frobtads-1.2.3/0000755000175000001440000000000012145614112012501 5ustar realncusersfrobtads-1.2.3/Testsuite.am0000644000175000001440000004125412016061405015015 0ustar realncusers## Rules for the TADS 3 test suite. ## Set up some env. variables needed by the test scripts. We must ## always use absolute paths here, as the scripts might 'cd'. ## TESTS_ENVIRONMENT = \ T3_DAT="@abs_srcdir@/tads3/test/data" \ T3_LOG="@abs_srcdir@/tads3/test/log" \ T3_OUT="@abs_builddir@/test/out" \ T3_RESDIR="@abs_srcdir@/tads3" \ T3_INCDIR="@abs_srcdir@/tads3/include" \ T3_LIBDIR="@abs_srcdir@/tads3/lib" \ SCRIPTS="@abs_srcdir@/testscripts" \ TESTPROGS="@abs_builddir@/test" \ t3make="@abs_builddir@/t3make" ## The test binaries. We'll also build the 't3pre' utility, since it's ## required by the test suite. ## check_PROGRAMS = \ test/t3pre \ test/test_pool \ test/test_utf8 \ test/test_chr \ test/test_gets \ test/test_err \ test/test_regex \ test/test_obj \ test/test_sort \ test/test_write \ test/test_exec \ test/test_tok \ test/test_prs \ test/test_sym \ test/test_prs_top \ test/test_comp_obj \ test/test_link ## Test programs to be executed (can be both scripts and binaries). ## TESTS = \ test/test_obj \ testscripts/test_pp.sh \ testscripts/test_ex.sh \ testscripts/all_make.sh \ testscripts/test_pre.sh # Extra compiler sources for dynamic code compilation in interpreter. # DYN_COMP_OBJS = \ tads3/vmrunsym.cpp \ tads3/tcprs.cpp \ tads3/tcprs_rt.cpp \ tads3/tcprsnf.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsnl.cpp \ tads3/tcgen.cpp \ tads3/tcglob.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tctok.cpp \ tads3/tcmain.cpp \ tads3/tcsrc.cpp \ tads3/tchostsi.cpp \ tads3/tclibprs.cpp \ tads3/tccmdutl.cpp REG_BUILTIN_CHAR = tads3/vmbifreg.cpp REG_BUILTIN_CHARNET = tads3/vmbifregn.cpp REG_BUILTIN_HTML = tads3/vmbifregx.cpp REG_BUILTIN_HTMLNET = tads3/vmbifregxn.cpp REG_METACLASS = tads3/vmmcreg.cpp TARGET_OBJS_BASE = \ tads3/tct3.cpp \ tads3/tct3stm.cpp \ tads3/tct3unas.cpp TARGET_OBJS_NO_RT = \ $(TARGET_OBJS_BASE) \ tads3/tct3nl.cpp \ tads3/tct3_d.cpp TARGET_OBJS_NO_LINK = \ $(TARGET_OBJS_BASE) \ tads3/tct3nl.cpp \ tads3/tct3_d.cpp TARGET_OBJS_SYM = \ $(TARGET_OBJS_BASE) \ tads3/tct3nl.cpp \ tads3/tct3prg.cpp TARGET_OBJS = \ $(TARGET_OBJS_BASE) \ tads3/tct3img.cpp \ tads3/tct3prg.cpp test_t3pre_SOURCES = tads3/test/test_pre.cpp \ src/osportable.cc \ src/missing.cc \ src/ost3comp.cc \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resldexe.cpp \ tads3/vmwrtimg.cpp \ tads3/vminit.cpp \ tads3/vmini_nd.cpp \ tads3/vminitim.cpp \ tads3/vmcfgmem.cpp \ tads3/vmobj.cpp \ tads3/vmundo.cpp \ tads3/vmtobj.cpp \ tads3/vmpat.cpp \ tads3/vmstrcmp.cpp \ tads3/vmdict.cpp \ tads3/vmgram.cpp \ tads3/vmstr.cpp \ tads3/vmcoll.cpp \ tads3/vmiter.cpp \ tads3/vmfref.cpp \ tads3/vmlst.cpp \ tads3/vmsort.cpp \ tads3/vmsortv.cpp \ tads3/vmbignum.cpp \ tads3/vmvec.cpp \ tads3/vmintcls.cpp \ tads3/vmanonfn.cpp \ tads3/vmlookup.cpp \ tads3/vmstrbuf.cpp \ tads3/vmdynfunc.cpp \ tads3/vmbytarr.cpp \ tads3/vmcset.cpp \ tads3/vmfilobj.cpp \ tads3/vmtmpfil.cpp \ tads3/vmpack.cpp \ tads3/vmhttpdum.cpp \ tads3/vmnetfillcl.cpp \ tads3/vmstack.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/vmpool.cpp \ tads3/vmpoolim.cpp \ tads3/vmtype.cpp \ tads3/vmtypedh.cpp \ tads3/utf8.cpp \ tads3/vmglob.cpp \ tads3/vmrun.cpp \ tads3/vmfunc.cpp \ tads3/vmmeta.cpp \ tads3/vmpreini.cpp \ tads3/vmimgrb.cpp \ tads3/vmbif.cpp \ tads3/vmbifc.cpp \ tads3/vmimage.cpp \ tads3/vmimg_nd.cpp \ tads3/vmsrcf.cpp \ tads3/vmfile.cpp \ tads3/vmbiftad.cpp \ tads3/vmisaac.cpp \ tads3/vmbiftio.cpp \ tads3/askf_tx3.cpp \ tads3/indlg_tx3.cpp \ tads3/vmsave.cpp \ tads3/vmcrc.cpp \ tads3/vmbift3.cpp \ tads3/vmbt3_nd.cpp \ tads3/vmregex.cpp \ tads3/vmconsol.cpp \ tads3/vmconmor.cpp \ tads3/vmconhmp.cpp \ tads3/os_stdio.cpp \ tads3/vmhosttx.cpp \ tads3/vmhostsi.cpp \ tads3/vmhash.cpp \ tads3/sha2.cpp \ tads3/md5.cpp \ tads3/vmlog.cpp \ tads3/vmbignumlib.cpp \ tads3/vmtz.cpp \ tads3/vmtzobj.cpp \ tads3/vmdate.cpp \ tads3/vmfilnam.cpp \ tads3/vmop.cpp \ $(DYN_COMP_OBJS) \ $(REG_METACLASS) \ $(REG_BUILTIN_CHAR) \ tads3/derived/vmuni_cs.cpp \ tads2/osifc.c \ tads2/osnoui.c \ tads2/osrestad.c \ tads2/osstzprs.c \ tads2/ostzposix.c \ $(TARGET_OBJS_NO_LINK) test_test_utf8_SOURCES = tads3/test/test_utf8.cpp \ tads3/utf8.cpp test_test_chr_SOURCES = tads3/test/test_chr.cpp \ src/osportable.cc \ src/missing.cc \ tads2/osnoui.c \ tads2/osstzprs.c \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/derived/vmuni_cs.cpp test_test_gets_SOURCES = tads3/test/test_gets.cpp \ src/osportable.cc \ src/missing.cc \ tads2/osnoui.c \ tads2/osstzprs.c \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcsrc.cpp \ tads3/derived/vmuni_cs.cpp test_test_pool_SOURCES = tads3/test/test_pool.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmpool.cpp \ tads3/vmpoolim.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/vmglob.cpp \ tads3/derived/vmuni_cs.cpp test_test_err_SOURCES = tads3/test/test_err.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/derived/vmuni_cs.cpp test_test_regex_SOURCES = tads3/test/test_regex.cpp \ src/missing.cc \ tads3/vmregex.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp test_test_obj_SOURCES = tads3/test/test_obj.cpp \ src/osportable.cc \ src/missing.cc \ src/ost3comp.cc \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/vmfile.cpp \ tads3/vmundo.cpp \ tads3/vmobj.cpp \ tads3/vmtobj.cpp \ tads3/vmpat.cpp \ tads3/vmstrcmp.cpp \ tads3/vmstr.cpp \ tads3/vmcoll.cpp \ tads3/vmiter.cpp \ tads3/vmfref.cpp \ tads3/vmlst.cpp \ tads3/vmsort.cpp \ tads3/vmsortv.cpp \ tads3/vmbignum.cpp \ tads3/vmsave.cpp \ tads3/vmcrc.cpp \ tads3/vmvec.cpp \ tads3/vmintcls.cpp \ tads3/vmanonfn.cpp \ tads3/vmlookup.cpp \ tads3/vmstrbuf.cpp \ tads3/vmdynfunc.cpp \ tads3/vmbytarr.cpp \ tads3/vmcset.cpp \ tads3/vmfilobj.cpp \ tads3/vmtmpfil.cpp \ tads3/vmpack.cpp \ tads3/vmstack.cpp \ tads3/vmdict.cpp \ tads3/vmgram.cpp \ tads3/vmhttpsrv.cpp \ tads3/vmhttpreq.cpp \ tads3/vmnet.cpp \ tads3/vmnetui.cpp \ tads3/vmnetcfg.cpp \ tads3/vmnetfil.cpp \ tads3/vmnetfillcl.cpp \ tads3/unix/osnetunix.cpp \ tads3/osifcnet.cpp \ tads3/vmrefcnt.cpp \ tads3/sha2.cpp \ tads3/md5.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/vmpool.cpp \ tads3/vmpoolim.cpp \ tads3/vmtype.cpp \ tads3/vmtypedh.cpp \ tads3/utf8.cpp \ tads3/vmglob.cpp \ tads3/vmrun.cpp \ tads3/vmsrcf.cpp \ tads3/vmfunc.cpp \ tads3/vmmeta.cpp \ tads3/vmbif.cpp \ tads3/vmsa.cpp \ tads3/vmbifl.cpp \ tads3/vmbiftad.cpp \ tads3/vmisaac.cpp \ tads3/vmbiftio.cpp \ tads3/vmbifnet.cpp \ tads3/askf_tx3.cpp \ tads3/indlg_tx3.cpp \ tads3/vmbift3.cpp \ tads3/vmbt3_nd.cpp \ tads3/vminit.cpp \ tads3/vmini_nd.cpp \ tads3/vmconsol.cpp \ tads3/vmconmor.cpp \ tads3/vmconhmp.cpp \ tads3/os_stdio.cpp \ tads3/vminitim.cpp \ tads3/vmcfgmem.cpp \ tads3/vmregex.cpp \ tads3/vmhosttx.cpp \ tads3/vmhostsi.cpp \ tads3/vmhash.cpp \ $(REG_METACLASS) \ $(REG_BUILTIN_CHARNET) \ tads3/derived/vmuni_cs.cpp \ tads3/tcprs.cpp \ tads3/tcprsnl.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ tads3/tcglob.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tctok.cpp \ tads3/tcmain.cpp \ tads3/tcsrc.cpp \ tads3/tchostsi.cpp \ tads3/tclibprs.cpp \ tads3/tccmdutl.cpp \ tads3/vmrunsym.cpp \ tads3/vmlog.cpp \ tads3/vmbignumlib.cpp \ tads3/vmfilnam.cpp \ tads3/vmdate.cpp \ tads3/vmtz.cpp \ tads3/vmtzobj.cpp \ tads3/vmop.cpp \ $(TARGET_OBJS_NO_LINK) \ tads3/tct3prg.cpp \ tads2/osifc.c \ tads2/osnoui.c \ tads2/osrestad.c \ tads2/osstzprs.c \ tads2/ostzposix.c test_test_sort_SOURCES = tads3/test/test_sort.cpp \ tads3/vmsort.cpp test_test_write_SOURCES = tads3/test/test_write.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmwrtimg.cpp \ tads3/vmfile.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/vmtypedh.cpp \ tads3/derived/vmuni_cs.cpp test_test_exec_SOURCES = tads3/test/test_exec.cpp \ src/missing.cc \ src/osportable.cc \ src/ost3comp.cc \ src/teststubs.c \ tads3/vmmain.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resldexe.cpp \ tads3/vminit.cpp \ tads3/vmini_nd.cpp \ tads3/vmconsol.cpp \ tads3/vmconmor.cpp \ tads3/vmconhmp.cpp \ tads3/os_stdio.cpp \ tads3/vminitim.cpp \ tads3/vmcfgmem.cpp \ tads3/vmobj.cpp \ tads3/vmundo.cpp \ tads3/vmtobj.cpp \ tads3/vmpat.cpp \ tads3/vmstrcmp.cpp \ tads3/vmstr.cpp \ tads3/vmcoll.cpp \ tads3/vmiter.cpp \ tads3/vmfref.cpp \ tads3/vmlst.cpp \ tads3/vmsort.cpp \ tads3/vmsortv.cpp \ tads3/vmbignum.cpp \ tads3/vmvec.cpp \ tads3/vmintcls.cpp \ tads3/vmanonfn.cpp \ tads3/vmdict.cpp \ tads3/vmgram.cpp \ tads3/vmlookup.cpp \ tads3/vmstrbuf.cpp \ tads3/vmdynfunc.cpp \ tads3/vmbytarr.cpp \ tads3/vmcset.cpp \ tads3/vmfilobj.cpp \ tads3/vmtmpfil.cpp \ tads3/vmpack.cpp \ tads3/vmhttpsrv.cpp \ tads3/vmhttpreq.cpp \ tads3/vmnet.cpp \ tads3/vmnetui.cpp \ tads3/vmnetcfg.cpp \ tads3/vmnetfil.cpp \ tads3/vmnetfillcl.cpp \ tads3/unix/osnetunix.cpp \ tads3/osifcnet.cpp \ tads3/vmrefcnt.cpp \ tads3/sha2.cpp \ tads3/md5.cpp \ tads3/vmstack.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/vmpool.cpp \ tads3/vmpoolim.cpp \ tads3/vmtype.cpp \ tads3/vmtypedh.cpp \ tads3/utf8.cpp \ tads3/vmglob.cpp \ tads3/vmrun.cpp \ tads3/vmfunc.cpp \ tads3/vmmeta.cpp \ tads3/vmsa.cpp \ tads3/vmbif.cpp \ tads3/vmbifl.cpp \ tads3/vmimage.cpp \ tads3/vmimg_nd.cpp \ tads3/vmsrcf.cpp \ tads3/vmfile.cpp \ tads3/vmbiftad.cpp \ tads3/vmisaac.cpp \ tads3/vmbiftio.cpp \ tads3/askf_tx3.cpp \ tads3/indlg_tx3.cpp \ tads3/vmsave.cpp \ tads3/vmcrc.cpp \ tads3/vmbift3.cpp \ tads3/vmbt3_nd.cpp \ tads3/vmbifnet.cpp \ tads3/vmregex.cpp \ tads3/vmhosttx.cpp \ tads3/vmhostsi.cpp \ tads3/vmhash.cpp \ tads3/vmbignumlib.cpp \ tads3/vmfilnam.cpp \ tads3/vmdate.cpp \ tads3/vmtz.cpp \ tads3/vmtzobj.cpp \ tads3/vmop.cpp \ $(REG_METACLASS) \ $(REG_BUILTIN_CHARNET) \ tads3/derived/vmuni_cs.cpp \ tads3/vmlog.cpp \ $(DYN_COMP_OBJS) \ tads2/osifc.c \ tads2/osnoui.c \ tads2/osrestad.c \ tads2/osstzprs.c \ tads2/ostzposix.c \ $(TARGET_OBJS_NO_LINK) test_test_tok_SOURCES = tads3/test/test_tok.cpp \ src/osportable.cc \ src/missing.cc \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/tcglob.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsnl.cpp \ tads3/tcprsnf.cpp \ tads3/tcgen.cpp \ tads3/tct3.cpp \ tads3/tct3unas.cpp \ tads3/tct3nl.cpp \ tads3/tct3_d.cpp \ tads3/vmhash.cpp \ tads3/vmtypedh.cpp \ tads3/vmbignumlib.cpp \ tads3/vmop.cpp \ tads3/derived/vmuni_cs.cpp \ tads2/osnoui.c \ tads2/osstzprs.c test_test_prs_SOURCES = tads3/test/test_prs.cpp \ src/osportable.cc \ src/missing.cc \ tads3/tcglob.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcprsimg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ $(TARGET_OBJS) \ tads3/vmhash.cpp \ tads3/vmwrtimg.cpp \ tads3/vmtypedh.cpp \ tads3/vmfile.cpp \ tads2/osnoui.c \ tads3/vmglob.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/vmbignumlib.cpp \ tads3/vmop.cpp \ tads2/osstzprs.c test_test_sym_SOURCES = tads3/test/test_sym.cpp \ src/osportable.cc \ src/missing.cc \ tads3/tcglob.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsnl.cpp \ $(TARGET_OBJS_SYM) \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ tads3/vmhash.cpp \ tads3/vmtypedh.cpp \ tads3/vmfile.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/vmbignumlib.cpp \ tads3/vmglob.cpp \ tads3/vmop.cpp \ tads2/osnoui.c \ tads2/osstzprs.c test_test_prs_top_SOURCES = tads3/test/test_prs_top.cpp \ src/osportable.cc \ src/missing.cc \ tads3/tcglob.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcprsimg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ $(TARGET_OBJS) \ tads3/vmhash.cpp \ tads3/vmwrtimg.cpp \ tads3/vmtypedh.cpp \ tads3/vmfile.cpp \ tads3/vmglob.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/vmbignumlib.cpp \ tads3/vmop.cpp \ tads2/osnoui.c \ tads2/osstzprs.c test_test_comp_obj_SOURCES = tads3/test/test_comp_obj.cpp \ src/osportable.cc \ src/missing.cc \ tads3/tcglob.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcprsimg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ $(TARGET_OBJS) \ tads3/vmhash.cpp \ tads3/vmwrtimg.cpp \ tads3/vmtypedh.cpp \ tads3/vmfile.cpp \ tads3/vmglob.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/vmbignumlib.cpp \ tads3/vmop.cpp \ tads2/osnoui.c \ tads2/osstzprs.c test_test_link_SOURCES = tads3/test/test_link.cpp \ src/osportable.cc \ src/missing.cc \ tads3/tcglob.cpp \ tads3/std.cpp \ tads3/std_dbg.cpp \ tads3/vmerr.cpp \ tads3/vmerrmsg.cpp \ tads3/utf8.cpp \ tads3/charmap.cpp \ tads3/resload.cpp \ tads3/resnoexe.cpp \ tads3/tcmain.cpp \ tads3/tcerr.cpp \ tads3/tcerrmsg.cpp \ tads3/tchostsi.cpp \ tads3/tcsrc.cpp \ tads3/tctok.cpp \ tads3/tcprs.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsstm.cpp \ tads3/tcprsprg.cpp \ tads3/tcprsimg.cpp \ tads3/tcgen.cpp \ tads3/tcgenfil.cpp \ $(TARGET_OBJS) \ tads3/vmhash.cpp \ tads3/vmwrtimg.cpp \ tads3/vmtypedh.cpp \ tads3/vmfile.cpp \ tads3/vmglob.cpp \ tads3/derived/vmuni_cs.cpp \ tads3/vmbignumlib.cpp \ tads3/vmop.cpp \ tads2/osnoui.c \ tads2/osstzprs.c frobtads-1.2.3/testscripts/0000755000175000001440000000000012145614112015070 5ustar realncusersfrobtads-1.2.3/testscripts/all_make.sh0000755000175000001440000000726312001064734017204 0ustar realncusers#! /bin/sh # "Make" tests. ret=0 cd "$T3_OUT" # We don't perform the "hashes" test due to the line terminator differences # between Unix/Windows. for i in asi anon anonfunc anonobj anonvarg badnest bignum bignum2 findreplace \ foreach funcparm htmlify ifnil inh_next isin join lclprop \ listprop lookup lookup2 lookup3 lookupdef multidyn nested newprop \ objloop opoverload propaddr propptr rexassert rexreplace setsc shr \ spec2html spec2text split sprintf strcomp2 strbuf substr \ unicode varmac vector vector2 vector3 testaddr2 testaddr3 testaddr4 \ strtpl listminmax packstr packarr do if $SCRIPTS/test_make.sh cp437 "$i" "$i"; then : else ret=1 fi done # These need to output latin1 encoded characters. for i in strcomp3 do if $SCRIPTS/test_make.sh latin1 "$i" "$i"; then : else ret=1 fi done for i in catch save html addlist listpar arith do if $SCRIPTS/test_make.sh cp437 -nodef "$i" "$i"; then : else ret=1 fi done for i in extfunc objrep funcrep conflict do if $SCRIPTS/test_make.sh cp437 -nodef "$i" ${i}1 ${i}2; then : else ret=1 fi done if $SCRIPTS/test_make.sh cp437 -pre vocext vocext1 vocext2 reflect; then : else ret=1 fi for i in extern objmod do if $SCRIPTS/test_make.sh cp437 -nodef "$i" ${i}1 ${i}2 ${i}3; then : else ret=1 fi done if $SCRIPTS/test_make.sh cp437 -nodef gram2 tok gram2; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -nodef rand rand; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -debug stack stack reflect; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre targprop targprop reflect; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre clone clone reflect; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre printexpr printexpr dynfunc; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre dynctx dynctx dynfunc; then : else ret=1 fi # These tests require running preinit (test_make normally suppresses it) for i in vec_pre symtab enumprop modtobj undef undef2 newembed newembederr \ triplequote optargs optargs_err optargs_err2 do if $SCRIPTS/test_make.sh cp437 -pre "$i" "$i"; then : else ret=1 fi done # Disable this for now; they can't work since they don't support building # in different directory than where the sources reside. #if $SCRIPTS/test_make.sh -pre multimethod_dynamic multimethod \ # multmethmultimethod_static -DMULTMETH_STATIC_INHERITED \ # multimethod multmeth #then # : #else # ret=1 #fi if $SCRIPTS/test_make.sh cp437 -pre multimethod_dynamic2 multimethod multimethod2 \ multmeth then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre multimethod_static2 -DMULTMETH_STATIC_INHERITED \ multimethod multimethod2 multmeth then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre namedparam namedparam multmeth; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre bifptr bifptr dynfunc; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre setmethod setmethod dynfunc; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre lclvars lclvars lclvars2 reflect dynfunc; then : else ret=1 fi if $SCRIPTS/test_make.sh cp437 -pre dynamicGrammar dynamicGrammar tok dynfunc gramprod then : else ret=1 fi # ITER does a save/restore test if $SCRIPTS/test_make.sh cp437 iter iter; then : else ret=1 fi if $SCRIPTS/test_restore.sh iter2 iter; then : else ret=1 fi exit $ret frobtads-1.2.3/testscripts/test_ex.sh0000755000175000001440000000204712001065271017102 0ustar realncusers#! /bin/sh # Execution tests ret=0 cd "$T3_OUT" for i in basic finally dstr fnredef undo gotofin do echo "Execution test: $i" $TESTPROGS/test_prs_top -I"$T3_INCDIR" "$T3_DAT/$i.t" "$T3_OUT/$i.t3" > "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" $TESTPROGS/test_exec -cs cp437 -norand "$T3_OUT/$i.t3" >> "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" rm "$T3_OUT/$i.err" if $SCRIPTS/test_diff.sh "$i"; then : else ret=1 fi done # These need to output latin1 encoded characters. for i in builtin do echo "Execution test: $i" $TESTPROGS/test_prs_top -I"$T3_INCDIR" "$T3_DAT/$i.t" "$T3_OUT/$i.t3" > "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" $TESTPROGS/test_exec -cs latin1 -norand "$T3_OUT/$i.t3" >> "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" rm "$T3_OUT/$i.err" if $SCRIPTS/test_diff.sh "$i"; then : else ret=1 fi done exit $ret frobtads-1.2.3/testscripts/test_pp.sh0000755000175000001440000000070311556744633017126 0ustar realncusers#! /bin/sh # Preprocessor tests rm -rf $T3_OUT mkdir $T3_OUT ret=0 cd "$T3_OUT" for i in ansi circ circ2 embed define ifdef concat varmacpp do echo "Preprocessor test: $i" $TESTPROGS/test_tok -I"$T3_DAT" -I"$T3_INCDIR" -P "$T3_DAT/$i.c" > "$T3_OUT/$i.log" 2> "$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" rm "$T3_OUT/$i.err" if $SCRIPTS/test_diff.sh "$i"; then : else ret=1 fi done exit $ret frobtads-1.2.3/testscripts/test_diff.sh0000755000175000001440000000123411556717705017417 0ustar realncusers#! /bin/sh # Diff a test result against the reference log # # The reference log is in $T3_LOG # The result file to compare is in $T3_OUT # # We'll store a .diff or a .succ file in $T3_OUT based on the result # clean up any old success/difference files we have hanging around rm -f "$T3_OUT/$1.succ" rm -f "$T3_OUT/$1.diff" if test -f "$T3_OUT/$1.log"; then : else echo "Output file '$T3_OUT/$1.log' not created - test failed" > "$T3_OUT/$1.diff" exit 1 fi diff -u "$T3_OUT/$1.log" "$T3_LOG/$1.log" > "$T3_OUT/$1.diff" if test $? = "0"; then echo "Success" > "$T3_OUT/$1.succ" rm "$T3_OUT/$1.log" rm "$T3_OUT/$1.diff" exit 0 fi exit 1 frobtads-1.2.3/testscripts/test_make.sh0000755000175000001440000000222312001064535017401 0ustar realncusers#! /bin/sh # Make and execute tests CHARSET=$1 shift case "$1" in -nodef) shift $t3make -test -I"$T3_DAT" -a -nodef -nobanner -nopre -Fs "$T3_DAT" -Fo "$T3_OUT" -Fy "$T3_OUT" -o "$T3_OUT/$1.t3" $2 $3 $4 $5 $6 $7 $8 $9 > "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" ;; -debug) shift $t3make -test -d -I"$T3_DAT" -a -nobanner -nopre -Fs "$T3_DAT" -Fo "$T3_OUT" -Fy "$T3_OUT" -o "$T3_OUT/$1.t3" $2 $3 $4 $5 $6 $7 $8 $9 > "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" ;; -pre) shift $t3make -test -I"$T3_DAT" -a -nobanner -Fs "$T3_DAT" -Fo "$T3_OUT" -Fy "$T3_OUT" -o "$T3_OUT/$1.t3" $2 $3 $4 $5 $6 $7 $8 $9 > "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" ;; *) $t3make -test -I"$T3_DAT" -a -nobanner -nopre -Fs "$T3_DAT" -Fo "$T3_OUT" -Fy "$T3_OUT" -o "$T3_OUT/$1.t3" $2 $3 $4 $5 $6 $7 $8 $9 > "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" ;; esac cat "$T3_OUT/$1.err" >> "$T3_OUT/$1.log" echo "'Make' test: $1" $TESTPROGS/test_exec -cs $CHARSET -norand "$T3_OUT/$1.t3" >> "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" cat "$T3_OUT/$1.err" >> "$T3_OUT/$1.log" rm "$T3_OUT/$1.err" $SCRIPTS/test_diff.sh "$1" exit $? frobtads-1.2.3/testscripts/test_restore.sh0000755000175000001440000000040311556734626020170 0ustar realncusers#! /bin/sh # Test restoring a saved state echo "'Restore' test: $1" $TESTPROGS/test_exec -cs cp437 "$T3_OUT/$2.t3" restore > "$T3_OUT/$1.log" 2>"$T3_OUT/$1.err" cat "$T3_OUT/$1.err" >> "$T3_OUT/$1.log" rm "$T3_OUT/$1.err" $SCRIPTS/test_diff.sh "$1" exit $? frobtads-1.2.3/testscripts/README0000644000175000001440000000112611371652512015756 0ustar realncusersThe scripts were created by using the existing scripts from the generic Unix port of TADS as a template. They have been modified in order to fix portability issues. So if you see a script that looks like this: if test -f "$T3_OUT/$1.log"; then : else foo fi don't bother changing it to: if [ ! -f $T3_OUT/$1.log ]; then foo fi and sending it to the maintainer. The second example may look much better, but it's not portable. ('!' is not portable, '[' is not portable... welcome to Unix!) Of course, it's unlikely that people use 4.3BSD or Solaris 8, but still... frobtads-1.2.3/testscripts/test_pre.sh0000755000175000001440000000120011556734613017263 0ustar realncusers#! /bin/sh # Preinit tests ret=0 cd "$T3_OUT" for i in preinit do echo "Preinit test: $i" $TESTPROGS/test_prs_top "$T3_DAT/$i.t" "$T3_OUT/$i.t3" > "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" $TESTPROGS/t3pre "$T3_OUT/$i.t3" "$T3_OUT/${i}_pre.t3" >> "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" $TESTPROGS/test_exec -cs cp437 "$T3_OUT/${i}_pre.t3" >> "$T3_OUT/$i.log" 2>"$T3_OUT/$i.err" cat "$T3_OUT/$i.err" >> "$T3_OUT/$i.log" rm "$T3_OUT/$i.err" if $SCRIPTS/test_diff.sh "$i"; then : else ret=1 fi done exit $ret frobtads-1.2.3/configure.ac0000644000175000001440000005240412145526452015005 0ustar realncusers# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # This file has been created by hand. Autoconf automation tools might # not be able to deal with it. # # The email address in the AC_INIT invocation is the address of the # current maintainer, not the original author. If the maintainer # changes, replace the email address. It is used to tell the user where # to send bug reports. # # AC_PREREQ(VERSION) should contain the version of Autoconf as used by the # maintainer. Everyone else should update to at least this version of # Autoconf. # AC_PREREQ(2.68) AC_INIT([FrobTADS],[1.2.3],[realnc@gmail.com],[frobtads]) AC_CONFIG_SRCDIR(src/osfrobtads.h) AC_CONFIG_AUX_DIR(config) AC_CONFIG_HEADERS(config.h) AM_SILENT_RULES([yes]) AM_INIT_AUTOMAKE([-Wall foreign]) # The current TADS OEM version. This must be set back to 0 each time # FrobTADS is synced with a new version of the base code, and increased # by 1 each time a new FrobTADS release is made that does not include a # new version of the base code. Note that this is a string, not a # number. # AC_DEFINE([TADS_OEM_VERSION], ["1"], [Current TADS OEM version.]) # The current maintainer of FrobTADS. Don't include an email address; # the email should be specified in the AC_INIT invocation above. # AC_DEFINE([PACKAGE_MAINTAINER], ["Nikos Chantziaras"], [The person who currently maintains FrobTADS.]) # Include custom macros we ship ourselves. # m4_include([m4/ax_func_mkdir.m4]) m4_include([m4/ax_tls.m4]) m4_include([m4/ax_append_flag.m4]) m4_include([m4/ax_check_compile_flag.m4]) m4_include([m4/ax_append_compile_flags.m4]) m4_include([m4/ax_pthread.m4]) # # Checks for programs. # Check for a C++ compiler. # AC_PROG_CXX # Check for a C compiler. # AC_PROG_CC # Check if the compiler accepts -c and -o at the same time. We need # this so that object files are placed in the same directory as the # corresponding source file, rather than in the root directory. # AM_PROG_CC_C_O AC_PROG_CXX_C_O # Disable strict-aliasing optimization since Tads does type punning on # purpose in quite a few places. AC_LANG_PUSH(C) AX_APPEND_COMPILE_FLAGS([-fno-strict-aliasing]) AC_LANG_PUSH(C++) AX_APPEND_COMPILE_FLAGS([-fno-strict-aliasing]) AC_LANG_POP(C++) AC_LANG_POP(C) # # Checks for libraries. # Check how to compile and link against the POSIX threads library. # AX_PTHREAD([], [AC_MSG_ERROR([POSIX threads seem to be missing; cannot continue])]) LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # Try to find a curses library. A curses library always provides the # initscr() function, so we check for that. We favor ncurses; if it # isn't found we fall back to plain curses. If that fails too, try # pdcurses (normally pdcurses is just curses on most systems, but it # doesn't hust to try pdcurses as a last resort.) # AC_SEARCH_LIBS([initscr], [ncurses curses pdcurses], [curseslibfound=true], [curseslibfound=false]) # Check for libcurl (network client library for http et al). # AC_SEARCH_LIBS([curl_global_init], [curl], [curllibfound=true], [curllibfound=false]) # # Checks for header files. AC_CHECK_HEADERS([limits.h stddef.h termios.h sys/ioctl.h]) # Prefer ncurses over curses. # AC_CHECK_HEADERS([ncurses.h curses.h], [break]) # Check for . This is related to AC_HEADER_TIME below. # AC_CHECK_HEADERS([sys/time.h]) # Check if we may include both and . On some older # systems, includes , but is not protected # against multiple inclusion. If it's safe to include both, # TIME_WITH_SYS_TIME will be defined. # AC_HEADER_TIME # Many systems lack . # AC_CHECK_HEADERS([wchar.h], [wcharheaderfound=true], [wcharheaderfound=false]) # (pathname-search using patterns) is only available in POSIX.2. # AC_CHECK_HEADERS([glob.h]) # Locale support headers. # AC_CHECK_HEADERS([langinfo.h locale.h]) # If the use of TIOCGWINSZ requires , then define # GWINSZ_IN_SYS_IOCTL. Otherwise TIOCGWINSZ can be found in . # AC_HEADER_TIOCGWINSZ # # Checks for typedefs, structures, and compiler characteristics. # This normally checks if 'struct tm' is defined in . We could # use this to include in case lacks this struct. # Unfortunately, the TADS base code includes on its own in some # places so it won't work. This is plain paranoia anyway; I guess most # systems define this struct in . # #AC_STRUCT_TM # Suggested by autoscan. # AC_C_CONST AC_TYPE_SIZE_T AC_C_VOLATILE # Some systems don't provide the SIGWINCH signal (like MS Windows). # AC_CACHE_CHECK([for SIGWINCH signal support], ac_cv_sigwinch_signal, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int foo = SIGWINCH;]])], [ac_cv_sigwinch_signal=yes], [ac_cv_sigwinch_signal=no] ) ]) if test x$ac_cv_sigwinch_signal = xyes; then AC_DEFINE([HAVE_SIGWINCH], [1], [Define to 1 if you have the SIGWINCH signal.]) fi # Some of the TADS base code tries to define "uchar", "ushort" and the # like. This will cause a compiler error if these types are already # defined, so it checks for the OS_U*_DEFINED macros first. We check # for the existence of these types in both C and C++; the "common.h" # header will then define the final OS_*_DEFINED macros according to # whether it's being compiled by C or C++. # AC_LANG_PUSH(C) AC_MSG_NOTICE([checking for presence of uchar, ushort, uint and ulong in C]) AC_CHECK_TYPE([uchar], [AC_DEFINE([C_UCHAR_DEFINED], [1], [Define to 1 if the uchar type exists in C.])]) AC_CHECK_TYPE([ushort], [AC_DEFINE([C_USHORT_DEFINED], [1], [Define to 1 if the ushort type exists in C.])]) AC_CHECK_TYPE([uint], [AC_DEFINE([C_UINT_DEFINED], [1], [Define to 1 if the uint type exists in C.])]) AC_CHECK_TYPE([ulong], [AC_DEFINE([C_ULONG_DEFINED], [1], [Define to 1 if the ulong type exists in C.])]) AC_LANG_POP(C) # We can't use the same check twice, since autoconf would simply pull # the results from the previous check from its cache. In order to # avoid that, we check each type with a space appended so it looks # different; sounds stupid, but works :P AC_LANG_PUSH(C++) AC_MSG_NOTICE([checking for presence of uchar, ushort, uint and ulong in C++]) AC_CHECK_TYPE([uchar ], [AC_DEFINE([CXX_UCHAR_DEFINED], [1], [Define to 1 if the uchar type exists in C++.])]) AC_CHECK_TYPE([ushort ], [AC_DEFINE([CXX_USHORT_DEFINED], [1], [Define to 1 if the ushort type exists in C++.])]) AC_CHECK_TYPE([uint ], [AC_DEFINE([CXX_UINT_DEFINED], [1], [Define to 1 if the uint type exists in C++.])]) AC_CHECK_TYPE([ulong ], [AC_DEFINE([CXX_ULONG_DEFINED], [1], [Define to 1 if the ulong type exists in C++.])]) AC_LANG_POP(C++) # Makefile.am needs to know if the system is big-endian (like Motorola # and SPARC CPUs) or little-endian (like Intel and VAX). We detect this # here and let Automake know. We'll report big-endian even if we can't # actually detect the endianess; that's because the big-endian routines # of TADS are actually generic and work for both big as well as # little-endian CPUs. # AC_C_BIGENDIAN([cpuisbigendian=true], [cpuisbigendian=false], [cpuisbigendian=true]) AM_CONDITIONAL([CPU_IS_BIGENDIAN], [test x$cpuisbigendian = xtrue]) # Also define a CPU_IS_BIGENDIAN macro to 0 or 1. if test x$cpuisbigendian = xtrue; then AC_DEFINE([CPU_IS_BIGENDIAN], [1], [Define to 1 if CPU is big endian.]) else AC_DEFINE([CPU_IS_BIGENDIAN], [0], [Define to 0 if CPU is little endian.]) fi # Check the sizes of 'long', 'int' and 'short'. # AC_CHECK_SIZEOF([long]) AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([short]) # Check how the compiler handles TLS. # #AX_TLS([], []) AX_TLS # Some systems have the wchar functions (wcslen, wcscpy, etc) in the C # library, but doesn't declare them. # AC_CHECK_DECLS([wcslen, wcscpy], [], [], [#include ]) # Check if we can ioctl() TIOCGWINSZ. This is the portable way of # getting the terminal size. # AC_CACHE_CHECK([for TIOCGWINSZ ioctl support], ac_cv_tiocgwinsz_ioctl, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#ifndef GWINSZ_IN_SYS_IOCTL #if HAVE_TERMIOS_H #include #endif #endif #if HAVE_SYS_IOCTL_H #include #endif]], [[struct winsize size; ioctl(0, TIOCGWINSZ, &size);]])], [ac_cv_tiocgwinsz_ioctl=yes], [ac_cv_tiocgwinsz_ioctl=no] ) ]) if test x$ac_cv_tiocgwinsz_ioctl = xyes; then AC_DEFINE([HAVE_TIOCGWINSZ], [1], [Define to 1 if TIOCGWINSZ is available for ioctl.]) fi # Check if we can ioctl() TIOCGSIZE. This is the BSD4.3 way of getting # the terminal size. # AC_CACHE_CHECK([for TIOCGSIZE ioctl support], ac_cv_tiocgsize_ioctl, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#if HAVE_TERMIOS_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif]], [[struct ttysize size; ioctl(0, TIOCGSIZE, &size);]])], [ac_cv_tiocgsize_ioctl=yes], [ac_cv_tiocgsize_ioctl=no] ) ]) if test x$ac_cv_tiocgsize_ioctl = xyes; then AC_DEFINE([HAVE_TIOCGSIZE], [1], [Define to 1 if TIOCGSIZE is available for ioctl.]) fi # # Checks for library functions. # Suggested by autoscan. # # Don't do the malloc() and realloc() checks because they result in a # link error during a cross compile and we don't provide the needed # fallback functions (rpl_malloc() and rpl_realloc()) anyway (that is, # if those checks would fail while not cross-compiling, we would still # get a link error.) #AC_FUNC_MALLOC #AC_FUNC_REALLOC AC_FUNC_MEMCMP AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([memmove memset strchr putenv]) # Check for mkdir()/_mkdir() and wether mkdir() only takes one argument. AX_FUNC_MKDIR # The TADS base code considers the memicmp() function to be "standard". # It actually isn't. If the system doesn't provide it, our own # implementation will be used. # AC_CHECK_FUNCS([memicmp]) # Tads uses the functions stricmp() and strnicmp() as if they were # standard. They aren't, but they have BSD 4.4 equivalents called # strcasecmp() and strncasecmp(). Most Unices provide these. If the # system provides them, and the original ones aren't provided, map # stricmp() to strcasecmp() and strnicmp() to strncasecmp(). If none of # them exist, our code will provide default implementations. # AC_CHECK_FUNC(stricmp, [AC_DEFINE([HAVE_STRICMP], [1], [Define to 1 if you have stricmp.])], [ AC_CHECK_FUNC( strcasecmp, [ AC_DEFINE( [stricmp], [strcasecmp], [Map stricmp to strcasecmp if you lack the former.] ) AC_DEFINE( [HAVE_STRCASECMP], [1], [Define to 1 if you have strcasecmp but not stricmp.] ) ] ) ] ) AC_CHECK_FUNC(strnicmp, [AC_DEFINE([HAVE_STRNICMP], [1], [Define to 1 if have strnicmp.])], [ AC_CHECK_FUNC( strncasecmp, [ AC_DEFINE( [strnicmp], [strncasecmp], [Map strnicmp to strncasecmp if you lack the former.] ) AC_DEFINE( [HAVE_STRNCASECMP], [1], [Define to 1 if you have strncasecmp but not strnicmp.] ) ] ) ] ) # Try to find a function that changes the current working directory. We # first try chdir(). If not found, we search for SetCurrentDirectory(). # # chdir() is listed in SVr4, SVID, POSIX, X/OPEN and 4.4BSD. # SetCurrentDirectory() is MS-Windows. Actually, chdir() should also be # available in Windows, but I'm not sure. # AC_CHECK_FUNC(chdir, [AC_DEFINE([HAVE_CHDIR], [1], [Define to 1 if you have chdir.])], [AC_CHECK_FUNC(SetCurrentDirectory, [AC_DEFINE([HAVE_SETCURRENTDIRECTORY], [1], [Define to 1 if you have SetCurrentDirectory.])])] ) # Tads wants a millisecond-precise timer. The standard C library lacks # a function that gets the current time with ms-precision. Therefore, # we search for one of 3 functions that can do that: clock_gettime(), # gettimeofday() and ftime(), in that order. # # clock_gettime() has nanosecond-precision and is listed in SUSv2 and # POSIX 1003.1-2001; gettimeofday() has microsecond-precision and is in # SVr4, BSD 4.3 and POSIX 1003.1-2001; ftime() has millisecond-precision # and is listed in the BSD 4.2 and POSIX 1003.1-2001 standards. # # clock_gettime() is the modern way of doing things, but is not widely # available on older systems. gettimeofday() should be available # almost everywhere, while ftime() is an obsolete function but still # does the job most of the time (some implementations are buggy and lack # millisecond precision, like in early glibc2 < 2.1.1 libraries, in # which case ftime() is no better than time(); no problem though since # most such systems provide gettimeofday()). # # Note that on some systems we must link with the "rt" library to be # able to use the clock_gettime() function. # # If clock_gettime() has been found, we also check if the system # supports a monotonic clock (CLOCK_MONOTONIC). # AC_SEARCH_LIBS(clock_gettime, rt, [ AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define to 1 if you have clock_gettime.]) AC_MSG_CHECKING([whether clock_gettime supports CLOCK_MONOTONIC]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[#include ]], [[clockid_t clockType = CLOCK_MONOTONIC;]] )], [ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_CLOCK_MONOTONIC], [1], [Define to 1 if clock_gettime supports CLOCK_MONOTONIC.]) ], AC_MSG_RESULT(no) ) ], [ AC_CHECK_FUNCS([gettimeofday ftime], [break]) ] ) # Various other functions not available everywhere. # AC_CHECK_FUNCS([wcslen wcscpy glob]) # Check for use_default_colors(), which is an extension to curses and # not available everywhere. # AC_CHECK_FUNCS([use_default_colors]) # Locale support functions. # AC_CACHE_CHECK([for nl_langinfo and CODESET], frob_cv_langinfo_codeset, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char* cs = nl_langinfo(CODESET);]])], [frob_cv_langinfo_codeset=yes], [frob_cv_langinfo_codeset=no] ) ]) if test x$frob_cv_langinfo_codeset = xyes; then AC_DEFINE([HAVE_LANGINFO_CODESET], [1], [Define to 1 if you have and nl_lan ginfo(CODESET).]) fi AC_CHECK_FUNCS([setlocale]) # # Checks for C++ features. # Switch to C++ before running the tests. # AC_LANG(C++) # Check if the compiler supports the 'and', 'or' and 'not' keywords. # Note that even some real compilers don't support them, although they # are in the standard since 1997. I'm not talking about VC++ 6; I said # *real* compilers :*) # # If they aren't supported, we define them in common.h. We don't define # them here in order to avoid problems with some C (not C++) compilers # that already define them as macros. # AC_CACHE_CHECK([whether the C++ compiler supports the and keyword], ac_cv_cxx_and_keyword, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int i;]], [[i = 0 and 1;]])], [ac_cv_cxx_and_keyword=yes], [ac_cv_cxx_and_keyword=no] ) ]) if test x$ac_cv_cxx_and_keyword = xyes; then AC_DEFINE([HAVE_AND_KEYWORD], [1], [Define to 1 if you have the and keyword.]) fi AC_CACHE_CHECK([whether the C++ compiler supports the or keyword], ac_cv_cxx_or_keyword, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int i;]], [[i = 0 or 1;]])], [ac_cv_cxx_or_keyword=yes], [ac_cv_cxx_or_keyword=no] ) ]) if test x$ac_cv_cxx_or_keyword = xyes; then AC_DEFINE([HAVE_OR_KEYWORD], [1], [Define to 1 if you have the or keyword.]) fi AC_CACHE_CHECK([whether the C++ compiler supports the not keyword], ac_cv_cxx_not_keyword, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int i;]], [[i = not 1;]])], [ac_cv_cxx_not_keyword=yes], [ac_cv_cxx_not_keyword=no] ) ]) if test x$ac_cv_cxx_not_keyword = xyes; then AC_DEFINE([HAVE_NOT_KEYWORD], [1], [Define to 1 if you have the not keyword.]) fi # Check if the compiler supports the 'bool' datatype. Some older ones # don't, so we'll have to typedef it in our common.h header. # AC_CHECK_TYPES(bool) # Check if the compiler supports modern casting syntax # (X_cast(value)). If yes, we'll define HAVE_X_CAST. We use this # to define the X_cast keywords as macros if the compiler lacks them. # # Original macros by Todd Veldhuizen and Luc Maisonobe . # Updated by and autoupdate. # AC_CACHE_CHECK(whether the compiler supports dynamic_cast<>, ac_cv_cxx_dynamic_cast, [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[#include class Base {public: Base(){} virtual void f() = 0;}; class Derived: public Base {public: Derived(){} virtual void f(){}}; ]], [[ Derived d; Base& b=d; return dynamic_cast(&b) ? 0 : 1; ]])], [ac_cv_cxx_dynamic_cast=yes], [ac_cv_cxx_dynamic_cast=no] ) ]) if test x$ac_cv_cxx_dynamic_cast = xyes; then AC_DEFINE(HAVE_DYNAMIC_CAST, 1, [define to 1 if the compiler supports dynamic_cast<>]) fi AC_CACHE_CHECK(whether the compiler supports static_cast<>, ac_cv_cxx_static_cast, [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[#include class Base {public: Base(){} virtual void f() = 0;}; class Derived: public Base {public: Derived(){} virtual void f(){}}; int g(Derived&) {return 0;} ]], [[ Derived d; Base& b = d; Derived& s = static_cast (b); return g(s); ]])], [ac_cv_cxx_static_cast=yes], [ac_cv_cxx_static_cast=no] ) ]) if test x$ac_cv_cxx_static_cast = xyes; then AC_DEFINE(HAVE_STATIC_CAST, 1, [define to 1 if the compiler supports static_cast<>]) fi AC_CACHE_CHECK(whether the compiler supports reinterpret_cast<>, ac_cv_cxx_reinterpret_cast, [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[#include class Base {public: Base(){} virtual void f() = 0;}; class Derived: public Base {public: Derived(){} virtual void f(){}}; class Unrelated {public: Unrelated(){}}; int g(Unrelated&) {return 0;} ]], [[ Derived d; Base& b=d; Unrelated& e=reinterpret_cast(b); return g(e); ]])], [ac_cv_cxx_reinterpret_cast=yes], [ac_cv_cxx_reinterpret_cast=no] ) ]) if test x$ac_cv_cxx_reinterpret_cast = xyes; then AC_DEFINE(HAVE_REINTERPRET_CAST, 1, [define to 1 if the compiler supports reinterpret_cast<>]) fi # # Additional 'configure' command-line options. # Add '--enable-t3debug', which builds the debug-version of TADS 3 and # also enables the TADS 3 test suite. # AC_ARG_ENABLE(t3debug, [ --enable-t3debug Build the debug version of TADS 3], [case "${enableval}" in yes) t3debugbuild=true break ;; no) t3debugbuild=false break ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-t3debug) break ;; esac], [t3debugbuild=false] ) AM_CONDITIONAL(T3_DEBUG_BUILD, test x$t3debugbuild = xtrue) # Add '--enable-static-link', which allows the user to build static # binaries on systems that default to dynamic linking. # AC_MSG_CHECKING(whether to create static or dynamic binaries) AC_ARG_ENABLE(static-link, [ --enable-static-link Create statically-linked binaries], [case "${enableval}" in yes) LDFLAGS="$LDFLAGS -static" AC_MSG_RESULT(statically linked) break ;; no) AC_MSG_RESULT(let the system decide) break ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-static-link) break ;; esac], AC_MSG_RESULT(let the system decide) ) # Add '--enable-error-checking', which allows the user to enable some # extra error-checking code in the TADS2 VM (stack overflows and such). # AC_ARG_ENABLE(error-checking, [ --enable-error-checking Enable TADS2 runtime error-checks], [case "${enableval}" in yes) t2runtimechecks=true break ;; no) t2runtimechecks=false break ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-error-checking) break ;; esac], [t2runtimechecks=false] ) AM_CONDITIONAL([T2_RUNTIME_CHECKING], [test x$t2runtimechecks = xtrue]) # Tell Automake to build the interpreter only if a curses library was found. # AM_CONDITIONAL([BUILD_INTERPRETER], [test x$curseslibfound = xtrue]) # Build the compilers only if the user didn't disable them. # AC_ARG_ENABLE(t2-compiler, [ --disable-t2-compiler Do not build the TADS 2 compiler], [case "${enableval}" in yes) buildt2compiler=true break ;; no) buildt2compiler=false break ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-t2-compiler) break ;; esac], [buildt2compiler=true] ) AC_ARG_ENABLE(t3-compiler, [ --disable-t3-compiler Do not build the TADS 3 compiler], [case "${enableval}" in yes) buildt3compiler=true break ;; no) buildt3compiler=false break ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-t3-compiler) break ;; esac], [buildt3compiler=true] ) AM_CONDITIONAL([BUILD_T2_COMPILER], [test x$buildt2compiler = xtrue]) AM_CONDITIONAL([BUILD_T3_COMPILER], [test x$buildt3compiler = xtrue]) # If the system lacks the header, let Automake know. # AM_CONDITIONAL([WCHAR_HEADER_MISSING], [test x$wcharheaderfound = xfalse]) # Tell Automake when the compiler doesn't supports TLS. # AM_CONDITIONAL([NO_TLS], [test x$ac_cv_tls = xnone]) # # Generate the output files. AC_CONFIG_FILES([Makefile]) AC_OUTPUT if test x$curseslibfound = xfalse; then AC_MSG_NOTICE([***]) AC_MSG_NOTICE([*** A curses library is missing. The interpreter cannot be built.]) fi if test x$curllibfound = xfalse; then AC_MSG_NOTICE([***]) AC_MSG_NOTICE([*** libcurl is missing. The interpreter cannot be built.]) fi frobtads-1.2.3/aclocal.m40000644000175000001440000011174512145604017014355 0ustar realncusers# generated automatically by aclocal 1.12.6 -*- Autoconf -*- # Copyright (C) 1996-2012 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.12' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.12.6], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.12.6])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly. AC_PREREQ([2.50])dnl # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated. For more info, see: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_INIT_AUTOMAKE-invocation]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl dnl Support for Objective C++ was only introduced in Autoconf 2.65, dnl but we still cater to Autoconf 2.62. m4_ifdef([AC_PROG_OBJCXX], [AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])])dnl ]) _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl dnl The 'parallel-tests' driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Copyright (C) 1999-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_CC_C_O # -------------- # Like AC_PROG_CC_C_O, but changed for automake. AC_DEFUN([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC_C_O])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl # FIXME: we rely on the cache variable name because # there is no other way. set dummy $CC am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o if test "$am_t" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi dnl Make sure AC_PROG_CC is never called again, or it will override our dnl setting of CC. m4_define([AC_PROG_CC], [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])]) ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2012 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar],, [pax],, [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' _am_tools=${am_cv_prog_tar_$1-$_am_tools} # Do not fold the above two line into one, because Tru64 sh and # Solaris sh will not grok spaces in the rhs of '-'. for _am_tool in $_am_tools do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR frobtads-1.2.3/doc/0000755000175000001440000000000012145614112013246 5ustar realncusersfrobtads-1.2.3/doc/NEWS0000644000175000001440000002434212145573762013771 0ustar realncusersv1.2.3 - 2013-05-17 =================== - Tads virtual machines and compilers updated to 2.5.17/3.1.3 v1.2.2 - 2012/08/29 =================== - Embedding resource directories with t3make was broken in version 1.2.1 if the files to be embedded belonged to a different user. This has been fixed. - The Tads 3 test suite could not be built in 1.2.1 due to linker errors, and in the case of GCC 4.7, also compiler errors. This should no longer occur. - The configure script will now automatically append -fno-strict-aliasing to the C and C++ compiler options (if the compiler recognizes it). It's not required anymore to do this manually. - A POSIX threads underlinking issue has been fixed. v1.2.1 - 2012/08/22 =================== - WebUI save/restore functionality was broken in 1.2. This has been fixed. - Tads virtual machines and compilers updated to 2.5.15/3.1.2. v1.2 - 2012/07/16 ================= - The interpreter should now work correctly when running inside an Emacs eterm. - The "--no-seed-rand" option should now be usable. - 8-bit characters (like German umlauts) should now display correctly. - Tads virtual machines and compilers updated to 2.5.15/3.1.1. v1.1 - 2011/12/29 ================= - Tads virtual machines and compilers updated to 2.5.15/3.1.0. Tads 3.1 introduces network capabilities and currently it is not possible to build the package without network support. pthreads, sockets and libcurl are required for a successful build. - Compiler and runtime sources have been unified. It is no longer necessary to download separate archives in order to build the T2 or T3 compilers. Because of this, two new configure options have been introduced in case you don't want to build the compilers: --disable-t2-compiler and: --disable-t3-compiler v1.0 ==== - Git is now used for version control. The repository is hosted at: http://www.assembla.com/spaces/frobtads - If the terminal size can't be detected, the interpreter will now assume 80x24. - The --undo-size option was wrongly documented as --t3undo. This was changed in the code a while back, but the --help output stil refered to the old name. - Games requesting 0ms input-timeouts would result in the input operation blocking; this was incorrect behavior and has been fixed. 0ms requests really mean "don't block and don't wait." - The flush() operation was not implemented correctly, which could in some cases lead to a game not displaying output. This would happen if the game paused using timing functionality without using any input routines to achieve the pause effect. - The ChangeLog file has been deprecated and changes will not be recorded there any longer, since now we have "git log". - A new option has been introduced: -R/--replay which can be used to load replay files at startup. Thanks to Marc Simpson for the patch. v0.13 ===== - Tads virtual machines and compilers updated to 2.5.14/3.0.18.1. v0.12 ===== - Tads 2 virtual machine and compiler updated to 2.5.11. v0.11 ===== - Tads 3 virtual machine and compiler updated to 3.0.16. - Embedding resources in Tads 3 game files (like a GameInfo.txt) was somewhat broken; the EOF identifier just before the resource wasn't removed. In the case of the GameInfo.txt resource, this resulted in third-party tools like Babel and Zoom not being able to read this resource. This has been fixed. Make sure to recompile your games with the now fixed t3make in order to have the resources correctly embedded in your game files. v0.10 ===== - Since Unicode doesn't work yet correctly in the curses interface, the interpreter will use regular ASCII by default to avoid display problems with apostrophes and quotes. - Compilation problems on Mac OS X 10.5 (Leopard), Intel C++, Solaris 10 and on the Linux version of the Sun Studio compilers have been fixed. - FrobTADS should now compile and run correctly as a 64-bit binary, at least under x86-64. Tested under x86-64 Linux, FreeBSD/AMD64 and Solaris 10. - It should be easier now to cross-compile FrobTADS without modifying any code. If your cross-compiler is called "i586-pc-mingw32-gcc", you can cross-compile with: ./configure --host=i586-pc-mingw32 make and it should build out-of-the-box. If you don't know what cross-compiling is, then you probably don't need it ;P - The PDCurses library is now correctly detected when building. It will be used if neither ncurses nor curses is found. v0.9 ==== - Tads 3 virtual machine and compiler updated to 3.0.15.3 (beta). - The default project file t3make looks for when invoked without an -f argument is now Makefile.t3m instead of makefile.t3m on every system. Capitalizing the first letter of the makefile is common practice on Unix. This means that if you use "Makefile.t3m" as the filename for your project file, you only need to type "t3make" with no arguments to build your project. - You can now choose between screen interfaces by using the new -i (or --interface) option. Available interfaces right now are curses (the default if not using the option) and plain (which only does stdio input/output). This new "plain" interface is still experimental and not complete at this point; screen size and text input are not handled correctly. Note that even in it's complete implementation, input will be very basic in plain mode and none of the more advanced features will ever be available (colors, banners, statusline, etc.) This mode is useful for screen readers that might get confused by the more complicated output of curses and for using FrobTADS as a back-end for other programs (a CGI script to play IF on the web, for example). v0.8 ==== - Tads 3 virtual machine and compiler updated to 3.0.15.1 (beta). v0.7 ==== - On most POSIX systems, the Tads 3 compiler (t3make) should now be able to include resources in the build when -res is used to specify a whole directory. The -recurse option should work also. (Patch was submitted by Ilya V. Goz.) - Tads 2 character mapping files are now supported and loaded by the interpreter when a game requests them. Charmap files are distributed with some games and the interpreter tries to find them in the current directory. The interpreter changes the current directory to the game's directory by default, so if you place the mapping files in the game's directory the interpreter should load them. (Patch was submitted by Ilya V. Goz.) Note that FrobTADS still has problems with 8-bit character sets, since curses deals only with 7-bit characters. This will hopefully be fixed in a feature release. v0.6 ==== - Tads 3 virtual machine and compiler updated to 3.0.12. v0.5 ==== - Tads 3 virtual machine and compiler updated to 3.0.11. - The compiler was unable to compile sourcefiles that were using a non-built-in character set. For example, a sourcefile beginning with #charset "koi8-r" would generate an error message. This has been fixed. - The Tads 3 "proto-documentation" has been wiped. More thourough documentation is now available at www.tads.org (it's a 10MB download so it has not been included in FrobTADS). v0.4 ==== - Tads virtual machines and compilers updated to 2.5.10/3.0.10. - Fixed a problem where it was not possible to compile TADS 3 games. Some required library files, although included in the package, did not get installed at the "make install" step. - Fixed a compilation problem with "src/missing.cc" ("unknown identifier: wc" and "unknown identifier: dst"). This only occured on some systems that lack the wchar C99 functions (like some Mac OS X systems). - Fixed possible color-curruption problems with old/broken curses libraries. v0.3 ==== - Tads virtual machines and compilers updated to 2.5.9/3.0.9 (the included Tads 2.5.9 version is more up-to-date than the official 2.5.9 release, but it's not yet 2.5.10). - Statusline now looks good even when colors are disabled ("--no-colors" option) or not available. - Fixed compilation problem on systems that don't support the SIGWINCH signal ("unknown identifier: SIGWINCH"), like MS Windows with MinGW and/or Dev-C++. - Fixed the fix of the compilation problem with the functions (wcslen and wcscpy); this time for good, I hope. - Hopefully improved terminal resize handling. v0.2 ==== Thanks to Mike Roberts, FrobTADS now has its own homepage and main distribution point: http://www.tads.org/frobtads.htm - Some command line options have been changed (type "frob --help" to list them). - Scrollback mode can now also be activated with the Escape (ESC) key. Fixed some possible problems with the ENTER key not being recognized on some systems. - White text on white background ("frob -t7 -b7") should work now. - Fixed a curses compilation problem (compilation failed with something like: "unknown identifier: KEY_RESIZE"). - Screen resizing (when running in an xterm) has been improved; a signal handler does the job now and some code has been improved for better resizing. - Fixed a compilation problem on systems that lack the wchar.h header and/or the wchar functions wcslen and wcscpy. - If you don't override the default colors, or use white on black (-b0 -t7), *and* your curses/ncurses version supports it, the interpreter will use the default colors of your terminal. This makes it possible to use bitmap pictures as background, or make the terminal transparent (if your terminal supports this, of course). Note that, by default, this won't work for the statusline, as it reverses the colors. If you want to use this feature for the entire screen, you'll have to start frob with "frob -g0 -l7". This feature can be disabled using the "-o" (or "--no-defcolors") option. - The Tads 3 documentation (HTML format) is now installed along with the compiler ([prefix]/share/frobtads/tads3/doc). - Documentation files have been moved into the new "doc" directory. v0.1 ==== Initial release. frobtads-1.2.3/doc/BUGS0000644000175000001440000000164012020175746013741 0ustar realncusersKnown bugs. Don't report them; fix them! :) - When a game has set a timeout for an input, and the terminal the interpreter runs in is resized, the timeout will be reset to its initial value rather than the remaining time. - The "--no-defcolors" option doesn't work with all curses versions. - On some systems it is not possible to bundle multi-media resources into the image file using the -recurse command line option of t3make. The systems affected are those that lack the system header. - An interpreter crash (a segfault for example) is likely to leave the terminal in a weird state. Entering "reset" (even if you can't see what you're typing) followed by a "unicode_start" (if applicable for your system) should bring the terminal back to normal. - Plain-mode (pure stdout output without curses) is only partially supported (frob --interface plain) and buggy. frobtads-1.2.3/doc/INSTALL0000644000175000001440000002713712020175746014320 0ustar realncusersFor impatient people who don't like to read through INSTALL files prior to installing software on their system: Enter the directory where you unpacked this package (for example, "cd ~/downloads/frobtads-1.0"). Type: "./configure" followed by "make", then "make install". Play TADS games: "frob ~/games/tads/arrival.gam". Get a list of command-line options: "frob --help". If you want some more information, read through the rest of this file. You can find generic and very detailed documentation about the configuration script in the file CONFIGURE_DOC. Requirements ============ Libraries ......... FrobTADS requires a curses compatible library along with its or header file (that means you have to install a curses development package, not only the runtime package). ncurses, plain old curses as well as PDCurses are supported. If no curses library is found, you can still build and use the compilers, since only the interpreter needs curses. In additon, you will need libcurl and its development files installed. This is needed for some of the network functionality of TADS 3. Compiler ........ To compile the package you'll need a C and a C++ compiler. It shouldn't matter what kind of compilers are installed on the system, or how recent they are; older compilers should work just as well, as long as they can compile a reasonable subset of ANSI C (like function prototypes). *Really* old K&R-only compilers will probably not work. As for C++, the code does not use the template library, nor any other "modern" C++ features like templates and exception handling, and should therefore compile on pre-standard C++ compilers. Among the compilers known to work are GCC 2.x, GCC 3.x, GCC 4.x and Intel C/C++. The quickie =========== Compile and install: ./configure [options] make make install You are not required to build in the same directory where you unpacked the source package. For example, you can do something like this: mkdir ~/build cd ~/build ../downloads/frobtads-0.10/configure make make install Not all versions of Make support this though; check if your version of Make supports the VPATH feature (GNU Make does, by the way). Some 'configure' options (cAsE sENsiTiVe): --help Lists every supported option. --help=short Lists only FrobTADS specific options. --prefix=PATH Top-level installation directory. Default is "/usr/local". --bindir=PATH Exact installion point of the binaries. Default is "PREFIX/bin". --datadir=PATH Installation directory for the various data-files. Default is "PREFIX/share". Note that a subdirectory called "frobtads" will be created automaticly. --enable-t3debug Will build the debug version of the T3VM. This results in slower code, but is useful for people who want to make changes to the source code. This will also enable the TADS 3 test suite. --disable-t2-compiler --disable-t3-compiler These options disable the building of the TADS 2 and TADS 3 compiler respectively. If you don't intent to hack around in the interpreter's source code, you can also specify this option: --disable-dependency-tracking This will result in a faster compile. But if you make changes to the source code, a "make" won't be able to always tell which source files changed. You may specify your own C and C++ compiler flags like so: ./configure CFLAGS="-O3 -pipe -march=pentium4" CXXFLAGS="-O3 -pipe -march=pentium4" If you used the "--enable-t3debug" configure option, you can run the test suite with: make check You are *not* required to do a "make install" prior to running the test suite. Play Tads games: frob [sometadsgame] The file extension (.gam or .t3) is optional. Detailed instructions ===================== To install FrobTADS, you'll have to compile it first, and to compile it you'll have to configure it. The procedure is very easy and is described in detail below. Configuration ............. First, the package has to find out some information about your system. Then, a "makefile" has to be created, since the "make" utility is used for building FrobTADS. Both steps are accomplished by entering the following command: ./configure If you want to build the debug version of TADS 3: ./configure --enable-t3debug This will result in slower code (as it performs additional checks in this mode) and is not recommended for production builds. This will also enable the TADS 3 test suite (see below). You can specify where FrobTADS will be installed by passing arguments to 'configure'. For example, if you want the executable(s) to be installed in "/usr/bin" and the data-files in "/usr/share", you can call 'configure' like this: ./configure --prefix=/usr If you don't specify a prefix-directory, "/usr/local" will be used. You can fine-tune the installation points of the various files. For example, if you want the executable(s) to be installed in "/usr/bin", but the various data-files in "/opt", you can do: ./configure --bindir=/usr/bin --datadir=/opt Note that a subdirectory called "frobtads" will be created automaticly, so don't use "--datadir=/opt/frobtads"; just "--datadir=/opt". You can also pass compiler-flags to 'configure', and they will be passed on to the compiler. For example, to compile the C sources with "-O3 -ansi" and the C++ sources with "-O2 -ansi -fno-exceptions", call 'configure' like this: ./configure CFLAGS="-O3 -ansi" CXXFLAGS="-O2 -ansi -fno-exceptions" (Don't forget to quote the options if they contain spaces, like in the above example.) Even if the shell allows it, please don't use this: CFLAGS="-O3 -ansi" ./configure *Always* specify these variables *after* the "./configure" part. If you're using GCC, you should add -fno-exceptions to the C++ options, since FrobTADS doesn't make use of C++ exceptions. You'll save some RAM and get a smaller executable, which is a Good Thing on low-end systems. You may want to check out the GCC docs to see what optimization options are available; typing "info gcc" on the console should get you started. With GCC, if you want FrobTADS to be compiled with optimized code that is tailored to your system's CPU, use the "-march=CPU" option. Recent GCC versions allow you to use "-march=native", in which case the compiler will detect your CPU and optimize the code accordingly. For GCC versions that do not support "native", you have to specify your CPU by name. A few examples for the CPU value on PCs: i386, i486, pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4, prescott, nocona (that's the 64bit P4; what, you have one?) core2, athlon, athlon-4, athlon-xp, athlon64 For example: "-march=pentium4" of you have a Pentium4 or Celeron D. Note that older GCC versions don't support all of the above. For the full list of CPUs, type: info gcc invoking submodel To find out the GCC version you have, type gcc --version Compilation ........... Now that the makefile has been created, you can compile the package with the following command: make The compilation should finish without errors (although there might be warning messages; you can usually ignore them). If compilation aborts with an error-message, send a bug report to the maintainer (include the error-message, the name and version of your compiler, and details about your system). If you've built the debug version of TADS 3, you can run the test suite by typing: make check This will build the test programs and then run some test scripts. If not all of the tests succeed, there's something wrong. Contact the maintainer in this case. You don't need to install the package prior to running the test suite. Since running the test suite requires you to build the debug version of the T3VM, you should do a make clean to delete the debug version and then configure and compile the package again without the "--enable-t3debug" option. Installing and using the debug version for regular work is not recommended. If you want to do a sanity-check on the TADS 3 compiler, you can type: make sample This will compile the TADS 3 library sample game. It will be placed in the "samples" subdirectory of the build directory. You can run it with: ./frob ./samples/sample.t3 "make sample" does not require the debug-version of T3, so it's always a good idea to try it out before installing the package. Installation ............ To install the package, type: make install You'll probably have to login as root first, if you're installing in a directory where you don't have write-access. If you're installing everything in your own home-directory, you don't have to be root. On most systems, you can use the "sudo" command to execute the above as root: sudo make install You will be prompted to enter root's password (or your own if you're on Ubuntu). This will install FrobTADS on your system and you're ready to play all those Tads games. The TADS compilers will be installed too, along with their data files and development libraries, as long as you didn't disable them during the ./configure step. If the above procedure doesn't work for you, contact the maintainer. See the README file for an email address. Or type: ./configure --help This will print the maintainer's email address as the last line. Usage ..... To play a Tads game, just pass the game's filename as an argument to 'frob', like this: frob /usr/local/games/tads/zebulon.gam The extension ".gam" (or ".t3" for Tads 3 games) is optional. Typing: frob --help will list the supported command-line options. If you also installed the compilers, look at their documentation on how to use them. For starters, take a look at the COMPILERS file. Operating systems ================= Mac OS X ........ OS X should work just like "regular" Unix. Nothing special is required to build the interpreter on OS X, and therefore the above instructions also apply for OS X. Don't forget to install the OS X curses/ncurses and libcurl development packages first. BeOS .... BeOS is supported and Like OS X, there should be no extra steps needed. Just make sure you have curses/ncurses and libcurl installed. Microsoft Windows ................. ** The below is outdated; it is currently NOT possible to build FrobTADS ** on Windows with MinGW/MSYS. The last known version to build on ** Windows is 1.0. It might be possible to build using Cygwin. Yes, you can compile FrobTADS even in Windows. If you're using MinGW/MSYS or cygwin, just configure and compile like in Unix. Of course, you must have a curses or ncurses library installed in a place where the compiler and linker can find it (PDCurses is known to work). Note that the TADS 3 test suite won't work in Windows, because of the difference in text line terminators. You can also cross-compile from Unix to Windows. If your cross-compiler is, for example "i586-pc-mingw32-gcc", configure with: ./configure --host=i586-pc-mingw32 and after "make" you should have your Windows *.exe files. This is in fact supported, tested and known to work. DOS ... MS-DOG and other brain damages (grin) aren't supported. A DOS interpreter for TADS games already exists, by the way. Other systems ............. No one tried yet to compile FrobTADS in systems like Amiga, Atari, OS2, etc. Or if someone did, we never heard of it. Any volunteers? Note that if you run Linux on those systems (there's a Linux for Amigas, I think), or some other Unix-like OS, then just follow the normal steps like for every other Unix system. It should build and run just fine. MorphOS on newer Amigas for example is known to work. (At least it did in the past.) frobtads-1.2.3/doc/THANKS0000644000175000001440000000112512001065531014154 0ustar realncusersThanks fly out to: Adam D. Ashworth Michael Baltes Ben Cressey James Cunningham M. Damian Dollahite Stephen Dranger Sophie Fruehling Ilya V. Goz Donavan Hall Andrew Huang Michael Martin Dave Picton Andrew Pontious Mike Roberts Andreas Sewe Marc Simpson Charles Srstka Regan Toews for the bug reports, patches, suggestions and everything else; privately and/or through the IF newsgroups. Thanks, folks! A special thanks to Dave Picton for his patches and more than thorough testing on Solaris and its broken curses lib :) frobtads-1.2.3/doc/COPYING0000644000175000001440000000071712001065531014302 0ustar realncusersThis software is distributed under the terms and conditions of the "TADS 3 FREEWARE SOURCE CODE LICENSE". The text of this license can be found in tads3/LICENSE.TXT, as included in the packaging of this file. Note to distributors and packagers: The license does not permit distribution of this software if a fee is collected for this service, nor inclusion of this software with other software for which a fee is collected without the maintainer's permission. frobtads-1.2.3/doc/AUTHORS0000644000175000001440000000027012001065531014311 0ustar realncusersTADS2/TADS3 base code: Michael J. Roberts FrobTADS code: Nikos Chantziaras Command-line options parser: Brad Appleton frobtads-1.2.3/doc/CONFIGURE_DOC0000644000175000001440000002203012001065531015170 0ustar realncusersCopyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. frobtads-1.2.3/doc/ChangeLog.old0000644000175000001440000003331312001065531015574 0ustar realncusersThis file is no longer used to record changes in the FrobTADS sources. We have the Git log for that now :) This file is kept for historical purposes. 2009-05-31 Nikos Chantziaras * configure.ac: AM_SILENT_RULES([yes]). 2009-05-07 Nikos Chantziaras * New upstream release: 0.13 * Basecode: Synced with 2.5.14/3.0.18.1 basecode release. * t3compiler/Testsuite.am: Replaced every occurance of askf_tx.cpp and indlg_tx.cpp with askf_tx3.cpp and indlg_tx3.cpp respectively. 2009-04-30 Nikos Chantziaras * Basecode: Synced with 2.5.12/3.0.18 basecode release. * Common.am: Replaced tads3/askf_tx.cpp with tads3/askf_tx3.cpp and tads3/indlg_tx.cpp with tads3/indlg_tx3.cpp in T2RCSOURCES. * tads3/askf_tx.cpp: File renamed to tads3/askf_tx3.cpp. * tads3/indlg_tx.cpp: File renamed to tads3/indlg_tx3.cpp. * src/osdos.h: Define OS_NEWLINE_SEQ as "\r\n". * src/osfrobtads.h: Define OS_NEWLINE_SEQ as "\n" if not already defined. * configure.ac: AC_PREREQ(2.63) (from 2.61). 2008-08-19 Nikos Chantziaras * New upstream release: 0.12.1 * Basecode: Synced with 3.0.17 basecode release. * t3compiler/Makefile.am: Added multmeth.t to t3lib_DATA. Added tips.t to t3libadv3_DATA. Added tcommand.htm to t3libextensionstcommanddoc_DATA. 2008-08-15 Nikos Chantziaras * New upstream release: 0.12 * Basecode: Synced with 2.5.11 basecode release. 2008-04-16 Nikos Chantziaras * src/frobtadsapp.cc (fRunTads2): construct argv using variables only, no string constants. Pass only variables to trdmain(), no string constants. * src/frobtadsappcurses.cc (FrobTadsApplicationCurses): Pass static variables to putenv() rather than string constants. 2008-04-14 Nikos Chantziaras * New upstream release: 0.11 * configure.ac: Removed AC_SYS_LONG_FILE_NAMES check. * src/osportable.cc (osfoprwt, osfoprwb): Don't open the file in append mode. * Basecode: Synced with 3.0.16 basecode release. * t3compiler/Makefile.am: Added CustomStatus.t, SimpleAttachable.t, combineReports.t, custmsg.t, showTranscript.t and smartAccompany.t to t3libextensions_DATA. Added t3libextensionstcommanddir. Added GiveToAskFor.t and TCommand.t to t3libextensionstcommand_DATA. Added t3libextensionstcommanddocdir. Added cicon9.gif, contpage.htm, givetoaskfor.htm, index.html, introduction.htm and telltoaction.htm to t3libextensionstcommanddoc_DATA. 2008-03-27 Nikos Chantziaras * New upstream release: 0.10 * configure.ac: Removed AX_CXXFLAGS_GCC_OPTION(-fno-strict-aliasing) * src/main.cc: Use "us-ascii" as default character set instead "\0" (auto-detection.) 2008-03-27 Nikos Chantziaras * configure.ac: Removed AC_HEADER_STDC check. Check for uchar, ushort, uint and ulong two times; one for C and one for C++. * src/common.h: #include . Define OS_UCHAR_DEFINE (and friends) if needed. * Common.am: Removed doc/README.64-bit from EXTRA_DIST. * doc/README.64-bit: File deleted. * src/osfrobtads.h: Wrap standard C header #includes inside an extern "C++" block. * configure.ac: Don't switch to C++ when checking for clock_gettime(), gettimeofday() and ftime(). 2008-03-26 Nikos Chantziaras * Common.am: Use -D_M_IX86_64 instead of -D_M_IX86. Added h_ix86_64.h to T2RCHEADERS. 2008-03-22 Nikos Chantziaras * configure.ac: Also look for pdcurses in AC_SEARCH_LIBS. * configure.ac: Removed AC_FUNC_MALLOC and AC_FUNC_REALLOC checks. * src/osunixt.h: New file. * Common.am: Added osunixt.h to COMMONSOURCES. * src/osscurses.cc (oss_eof_on_stdin): Use _eof() instead of select() on MS-Windows. * acinclude.m4: New file. * configure.ac: Use -fno-strict-aliasing when compiling with GNU C++. * configure.ac: AC_PREREQ(2.61) (previously 2.60). 2007-10-10 Nikos Chantziaras * New upstream release: 0.9 * Basecode: Synced with 3.0.15.2 basecode release. * Common.am: Added doc/README.64-bit to EXTRA_DIST. 2007-10-09 Nikos Chantziaras * Frob.am: Added src/frobtadsappcurses.cc, src/frobtadsappcurses.h and src/frobtadsappplain.h in FROBSOURCES. * src/frobtadsappcurses.cc src/frobtadsappcurses.h src/frobtadsappplain.h: New files. * src/frobtadsapp.cc: Removed #include for and "colors.h". (winResizeHandler): Call globalApp->resizeEvent() instead of globalApp->fCreateGameWindow(). Call globalApp->width(), globalApp->height() and globalApp->flush() instead of globalApp->fGameWindow->width(), globalApp->fGameWindow->height() and globalApp->fGameWindow->flush(). (FrobTadsApplication): Removed fGameWindow(0) and fDispBuf(0) from initialization list. Added fColorsEnabled(false) to initialization list. Removed detection of terminal size and all curses API calls. (getTermSize ~FrobTadsApplication fCreateGameWindow scrollRegionUp scrollRegionDown clear getRawChar): Implementations removed. (runTads): Call this->init(), this->width(), this->height() and this->flush() instead of this->fCreateGameWindow(), this->fGameWindow->width(), this->fGameWindow->height() and this->fGameWindow->flush(). * src/frobtadsapp.h: Don't #include and "tadswindow.h". (FrobTadsApplication): Removed fGameWindow, fDispBuff, fCreateGameWindow() and ~FrobTadsApplication(). Moved fColorsEnabled and fRemainingTimeout from private to protected. Added init() and resizeEvent() to protected section. Added width() and height() to public. Made moveCursor(), print(), clear(), scrollRegionUp(), scrollRegionDown(), getRawChar() and sleep() pure virtual. Renamed inclusion sentry from FROBTADSAPPLICATION_H to FROBTADSAPP_H. * src/main.cc: Don't #include "frobtadsapp.h". #include "frobtadsappcurses.h" and "frobtadsappplain.h". * src/main.cc: Added "i|interface" option and changed "-o|no-defcolors" to "o|no-defcolors" in optv[]. screenInterface enum and interface var added. Parsing of -i option added. Start the game by instanciating an interface-specific subclass of FrobTadsApplication. 2007-09-24 Nikos Chantziaras * src/osfrobtads.h: #define UNIX * t3compiler/Makefile.am: Added t3compiler/tads3/lib/extensions/newNames.t to t3libextensions_DATA. * Basecode: Synced with 3.0.15.2 basecode release. 2007-09-06 Nikos Chantziaras * src/osportable.cc: Added os_create_tempfile() and osfdel_temp(). * src/osfrobtads.h: #define OSNOUI_OMIT_TEMPFILE 2007-08-01 Nikos Chantziaras * New upstream release: 0.8 * src/osscurses.cc: Added oss_eof_on_stdin(). * src/osfrobtads.h: Added osfflush(). * src/frobtadsapp.cc (FrobTadsApplication::fRunTads3) updated for new vm_run_image() arguments. * Basecode: Synced with 3.0.15.1 basecode release. * Common.am: Add MacOSX in EXTRA_DIST. 2007-01-21 Nikos Chantziaras * New upstream release: 0.7 2006-12-29 Ilya V. Goz * configure.ac: Check for and glob(). * t3compiler/src/osportable3.cc: Implemented os_find_first_file(), os_find_next_file() and os_find_close(). (oss_build_outpathbuf): New function. * configure.ac: Check for , , nl_langinfo(CODESET) and setlocale(). * src/main.cc: #include . Initialize locale. New command-line option (-k, --character-set). * src/osportable.cc: #include . #include "frobtadsapp.h". Implemented os_gen_charmap_filename() and os_get_charmap(). (get_charset_alias): New function. * src/frobtadsapp.h: New field in FrobOptions struct (characterSet). 2006-09-16 Nikos Chantziaras * New upstream release: 0.6 * Basecode: Synced with 3.0.12 basecode release. * t3compiler/Makefile.am: Added t3libextensionsdir and t3libextensions_DATA. 2006-09-10 Nikos Chantziaras * New upstream release: 0.5 * Basecode: Synced with 3.0.11 basecode release. * t3compiler/Makefile.am: Updated t3doc_DATA. * Common.am: Replaced tads3/resnoexe.cpp with tads3/resldexe.cpp in T3RCSOURCES. * t3compiler/Makefile.am: Replaced t3compiler/tads3/resldexe.cpp with t3compiler/tads3/resnoexe.cpp in T3COMP_DISTFILES. * t3compiler/Testsuite.am: Replaced t3compiler/tads3/resldexe.cpp with tads3/resldexe.cpp in nodist_test_t3pre_SOURCES and nodist_test_test_exec_SOURCES. Replaced all occurances of tads3/resnoexe.cpp with t3compiler/tads3/resnoexe.cpp. 2006-08-27 Nikos Chantziaras * New upstream release: 0.4 * Basecode: Synced with 3.0.10 basecode release. * Common.am: Include tads2/bifgdum.c in T2RCSOURCES. 2005-09-24 Dave Picton * src/frobtadsapp.cc (fCreateGameWindow): Clear window with color. 2005-09-18 Nikos Chantziaras * t3compiler/Makefile.am: Install tadsiox.h, gramprod.t, and settings.t. 2005-09-10 Nikos Chantziaras * src/missing.cc (wcslen, wcscpy): Fixed syntax error. 2005-09-10 Nikos Chantziaras * New upstream release: 0.3 2005-09-07 Nikos Chantziaras * Basecode: Synced with 3.0.9 basecode release. * scr/oscurses.h: Renamed to osfrobtads.h. * Common.am: Changed -DCURSES to -DFROBTADS. * src/frobtadsapp.cc (fRunTads3): Pass 0 as logfile charset arg of vm_run_image(). 2005-09-07 Dave Picton * src/frobtadsapp.cc (fCreateGameWindow): Do a wclear(stdscr) after resetting curses. touch() the window after initialization. (runTads): Display one character in reverse video then one in normal colors prior to starting the VM. 2005-08-10 Nikos Chantziaras * src/frobtadsapp.h, src/frobtadsapp.cc (runTads): Removed third argument. * src/frobtadsapp.cc (getTermSize): New function. (fCreateGameWindow): Use getTermSize() to get terminal dimensions. (FrobTadsApplication): Use getTermSize(). Pass string constants to putenv() instead of static vars. Initialize LINES and COLUMNS with larger values than 200. 2005-08-09 Dave Picton * src/frobtadsapp.cc (FrobTadsApplication): Set LINES and COLUMNS env. variables prior to starting curses. 2005-08-05 Nikos Chantziaras * src/tadswindow.h: Removed everything related to stdscr. * src/frobtadsapp.h, src/frobtadsapp.cc: Removed everything related to fRootWindow. * src/osscurses.cc (ossgetcolor): Only examine requested background color in no-colors mode before using A_REVERSE. * configure.ac: Check for putenv(). Check for TIOCGSIZE. * src/frobtadsapp.cc (winResizeHandler): Removed endwin() and refresh(). (fCreateGameWindow): Added endwin() and refresh(). 2005-08-04 Dave Picton * src/frobtadsapp.cc (fCreateGameWindow): Added TIOCGSIZE ioctl. Update LINES and COLUMNS env vars. 2005-08-03 Nikos Chantziaras * configure.ac: Added AC_HEADER_TIOCGWINSZ. Check for . Check for TIOCGWINSZ. * src/frobtadsapp.cc: Include and only if needed. (fCreateGameWindow): Only get the terminal's size with an ioctl if the system supports this. * configure.ac: Removed checks for , , and . 2005-08-01 Dave Picton * src/frobtadsapp.cc: Include . (fCreateGameWindow): Get the terminal's size with an ioctl. 2005-07-31 Nikos Chantziaras * configure.ac: Check for wcslen() and wcscpy() declarations. * src/missing.h: Declare wcslen() and wcscpy() if system lacks these declarations. 2005-07-28 Nikos Chantziaras * configure.ac: Check for SIGWINCH signal. * src/frobtadsapp.cc (winResizeHandler, FrobTadsApplication): Install SIGWINCH handler only if system supports this signal. 2005-07-27 Nikos Chantziaras * New upstream release: 0.2 * src/tadswindow.h: New ctor for top-level windows without parent. * src/frobtadsapp.cc (winResizeHandler): Flush the game window after telling Tads to resize, don't blank it at all. (fCreateGameWindow): Create game window as a parentless fullscreen window. Initialize fDispBuf using the game window's width, not COLS. 2005-07-26 Dave Picton * src/frobtadsapp.cc (winResizeHandler): Reinstall signal handler. 2005-07-25 Nikos Chantziaras * src/colors.h (makeColorPair): Map color pair 0 to color pair 7 and vice versa. Changed color pair range from 1-64 to 0-63. * src/frobtadsapp.cc (FrobTadsApplication): Removed initialization of color pair 0. * configure.ac: Check for use_default_colors(). * src/main.cc: New command line option (o|nodefcolors). * src/frobtadsapp.h (FrobTadsApplication): New field in FrobOptions struct (defColors). * src/frobtadsapp.cc (FrobTadsApplication): Call use_default_colors(). * src/frobtadsapp.h, src/frobtadsapp.cc: Added SIGWINCH signal handler (winResizeHandler). * src/frobtadsapp.cc (getRawChar): Removed KEY_RESIZE resize handling. * configure.ac: Added AC_TYPE_SIGNAL. * configure.ac: Detect wchar.h, wcslen(), wcscpy(). Added WCHAR_HEADER_MISSING Automake conditional. * src/missing.h, src/missing.cc: Added wcslen() and wcscpy(). * src/wchar/wchar.h: New header. * Common.am: Check WCHAR_HEADER_MISSING conditional. Added src/wchar/wchar.h to COMMONSOURCES. * t3compiler/Makefile.am: Added t3docdir and t3doc_DATA. * src/main.cc (main): Changed some command line options. * doc/: New directory. Moved the documentation text files. * doc/THANKS: New file. * README: New file. * configure.ac: Added "foreign" to Automake options. * Common.am: Added doc/ files to EXTRA_DIST. * src/oscurses.cc (timedGetcRaw): Map \n, \r and KEY_ENTER to code 13. Minor structure improvents. * src/osscurses.cc (oss_raw_key_to_cmd): Also toggle scrollback with ESC. frobtads-1.2.3/doc/README0000644000175000001440000001504012001065531014122 0ustar realncusersFrobTADS - A portable TADS toolkit. Maintained by Nikos Chantziaras . For the newest version, visit: http://www.tads.org/frobtads.htm By default, the FrobTADS source-package only contains the interpreter. See the file COMPILERS on where to obtain and how to install the TADS 2 and TADS 3 development tools, and on where to look for documentation in order to get started with developing your own games. For more information about TADS, visit its home page: http://www.tads.org Platforms ========= FrobTADS is known to compile and run on various platforms, including: Linux (all flavors), Mac OS X, various BSDs, old and modern Solaris, BeOS and Microsoft Windows. If you manage to run FrobTADS on a system not mentioned above, please let the maintainer know about it. About TADS and FrobTADS ======================= TADS stands for "Text Adventure Development System". It's a set of tools that allow easy implementation of text adventures, or "Interactive Fiction". The tools include a compiler along with supporting libraries, a debugger and an interpreter. An interpreter is needed to run the compiler's output, as it generates "byte code" programs (much like Java). The primary target of TADS nowadays is Microsoft Windows (although MS-DOS is still supported). FrobTADS is a "portable port" of the TADS toolkit. The term "portable port" sure sounds funny, but it's a quite accurate description; although the main target of FrobTADS is Unix, it compiles and runs even in MS Windows. Therefore, it's a portable port :-) The main development takes place on a Linux PC. Not everything is included in FrobTADS yet; for now, there are no debuggers available. On the other hand, the compilers and interpreters are (I hope) feature-complete. FrobTADS is not written from scratch; it uses Mike Roberts' portable reference implementations of the two TADS virtual machines; the T2 VM (written in C) and T3 VM (written in C++). FrobTADS hooks-in into that code by providing a portable implementation of the TADS I/O API. Goals ===== FrobTADS has been written as a replacement for the "traditional" Unix-port of TADS, which has many problems and limitations, and is difficult to maintain and change. The FrobTADS interpreter also provides some features that the traditional Unix-port lacks, the most important of them being: - Automatic configuration prior to building; no need to edit makefiles. - TADS 3 color support and configurable default colors. - Correct timing with timed operations (millisecond-precision). - Input like in Frotz; cursor keys, insert, delete, etc., instead of Emacs-like input. - TADS 3 banners look as they should. - You are not required to install the package prior to running the TADS 3 test suite. - File I/O initiated by the game will happen in the game's directory. This means that you don't have to change to the game's directory prior to starting the interpreter. - Less source code (and also less complex), more comments. - More portable. The ultimate goal is to make FrobTADS compile and run out-of-the-box on every system that has a curses library and a Unix-like shell available. What is Multimedia TADS? ======================== Multimedia TADS (also known as "HTML TADS") is an extension of TADS that uses HTML to provide multimedia capabilities. Multimedia TADS is not an extension of the two TADS languages (TADS 2 and 3), but rather of their output system. Although FrobTADS, being a character-mode interpreter, doesn't support most of the HTML extensions, it *can* run games that use HTML; on the binary level, Multimedia TADS and "plain" TADS executables are actually the same thing. You won't see any graphics nor hear sounds and music, but the game will play just fine. In TADS 2 games, you also won't see any banners that the game would otherwise display (TADS 3 games don't use HTML for banners; FrobTADS provides full support for TADS 3 banners). The vast majority of games don't use the multimedia extensions though, or use only the subset supported by FrobTADS. What's Interactive Fiction? =========================== Well, this is just a poor README file and therefore not the appropriate place for an introduction to Interactive Fiction. For more information about Interactive Fiction (or "IF" for short), just go to the TADS page (http://www.tads.org) and follow some links. Or go to the Google search engine (http://www.google.com) and search for "interactive fiction"; you'll be amazed about how many results you'll get. And you'll be even more amazed about how active the IF community is. There are two Usenet newsgroups for IF related things. The first is RGIF, which is short for: rec.games.int-fiction where people are talking about IF games in general; things they like in games, things they don't like, things they hope to see in future games, requests for hints and solutions, reviews of games, announcements of new games/software or happenings... Stuff like that. The other newsgroup is RAIF, which stands for: rec.arts.int-fiction This newsgroup is for everyone who is interested in creating games. Most IF authors use to hang around there, so this newsgroup is an excellent place to post questions about IF theory, authorship and programming. Your ISP should usually provide a newsserver which you can use to access Usenet. If not, you can use Google's web-based Usenet interface: http://groups.google.com/group/rec.games.int-fiction http://groups.google.com/group/rec.arts.int-fiction Where do I find games for FrobTADS? =================================== There's a large repository for IF-related stuff (with *lots* of games!) called "The Interactive Fiction Archive"; people usually just refer to it as "the Archive". You can access it by HTTP: http://www.ifarchive.org (very slow) or by FTP: ftp://ftp.ifarchive.org (also very slow) The archive has mirrors that are usually much faster. You should always access it through: http://mirror.ifarchive.org Please use this mirror instead of the main archive! You'll save yourself (and others) quite a few headaches. (The full list of mirrors is displayed in the main archive's title-page.) Since the archive is actually just a (huge) bunch of data thrown together (more or less), a nice fellow has created a site that will guide you through the archive. The site is called "Baf's Guide to the IF Archive" and is located at: http://wurb.com/if It contains many cool things, like a "Genre Map" for the games located in the archive along with descriptions, reviews and ratings, as well as many useful links. frobtads-1.2.3/doc/MacOSX0000644000175000001440000003614612001065531014271 0ustar realncusersHow to install Frobtads on a Mac running OSX So that you too can write TADS games!!! (These are instructions for command-line novices, like me!) by Reg Corrections to this text should be sent to regan.toews@mail.mcgill.ca -------------------------------------------- 1) Find the program "Terminal". Get to know it. It is probably stored in the utilities folder which is inside your applications folder. Drag it to the dock as you'll be needing to open this often. Some info for Terminal Newbies: Using terminal, you are always located somewhere inside your computer. For instance, you could be inside your applications folder, or perhaps your itunes folder... but it's important to know where you are because this affects how commands could be interpreted. Some common commands: CHANGING DIRECTORY cd - change directory (this is how you navigate around in your computer) /Applications - the slash refers to a folder, the name after the slash is the name of a folder cd /Applications - this is how you would enter the Applications folder cd /Applications/Utilities - this is how you would enter the utilities folder . - a dot means "current folder" .. - two dots mean "one folder before" cd ../ - means move to the previous folder Try the cd command with different folders to get a feel for navigating. (If any folders have spaces in them, things are a little more complicated - you can drag a file or folder into the "terminal" window and it will show how you would type the location of the file or folder) FINDING A FILE (includes hidden files which spotlight won't show you) cd / - changes directories so you're located in the top-most directory find . -name adv3.h - searches for a file called "adv3.h" OTHER COMMANDS tar -xzf - expand a *.tar.gz compressed archive ls - list all files in a folder, except hidden files ls -a - list everything in a folder, including hidden files sudo - this command is added to the beginning of a command if it's something where you need to have administrative (root) access A list of more commands: http://www.ss64.com/bash A very good unix beginner page is here: http://forums.macosxhints.com/showthread.php?t=40648 By the way: The word "binary" generally means "compiled file" as opposed to the "source code" which is the building blocks of a file. -------------------------------------------- 2) Download a free text-editing application. You will use this program to write your game code files with. I use Smultron (available at: http://smultron.sourceforge.net/). -------------------------------------------- 3) Install xcode tools. This is on your install DVD that came with your mac but it's better to download the newest version via the internet. Goto: http://developer.apple.com/tools/xcode/ This is free when you make an ADC account (account is also free). Follow the installation instructions that come with it. -------------------------------------------- 4) Download and install fink Goto: http://fink.sourceforge.net/download/ IMPORTANT NOTE: the installer disk image of fink contains additional files, including an application called finkcommander. Create a new folder in Applications called "fink extras" and drag the extra info including finkcommander to this folder after you've installed fink itself. The instructions on how to download and install the program are on the site but I've included it here so you don't have to be reading instructions from more than one place. I've edited it a little too: ----- 1. Download the installer disk image: Fink 0.8.1 Binary Installer (PowerPC) - 17930 KB Fink 0.8.1 Binary Installer (Intel) - 17510 KB (10.3 users - use Fink 0.7.2) (10.2 users - use Fink 0.6.4) (10.1 users - use Fink 0.4.1) 2. Double-click "Fink-0.8.1-XYZ-Installer.dmg" (where XYZ is either PowerPC or Intel) to mount the disk image, then double-click the "Fink 0.8.1 XYZ Installer.pkg" package inside. Follow the instructions on screen. Then create a folder in your Applications folder called "fink extras" and drag the other information, including the application "finkcommander" contained in the disk image, into the fink extras folder. 3. At the end of the installation, the pathsetup utility will be launched. You will be asked for permission before your shell's configuration files are edited (use your administrative password). When the utility has finished, you are set to go! 4. If anything goes wrong during this process, you can try again by launching the pathsetup application which appears on the installer disk, or by running (from the command line in a Terminal.app window) /sw/bin/pathsetup.sh (This step should also be repeated by any other users on your system: each user must run pathsetup in his or her own account.) If pathsetup generates errors messages, consult the documentation, particularly section 2.3 "Setting Up Your Environment" of the User's Guide. 5. Open a new Terminal.app window and run the following: "fink scanpackages; fink index", or use the included Fink Commander GUI application (which must be placed in a real folder on your system, not run from the disk image) and run the following commands from its menu: Source->scanpackages followed by Source->Utilities->index. 6. Once those two commands are finished you should update the fink package, in case there have been significant changes since the last point release. After you do this you can install other packages. There are several ways to do this: * Use the included Fink Commander to select and install packages. Fink Commander provides an easy to use GUI for Fink. This is the recommended method for new users, or users who are not comfortable with the command line. Fink Commander has Binary and Source menus. You should install from binaries if you don't have the Developer Tools installed, or don't want to build packages yourself. o The Fink Commander sequence to update fink from binaries is as follows: 1. Binary->Update descriptions 2. Select the fink package. 3. Binary->Install o The recommended Fink Commander sequence to update fink from source is as follows: 1. Source->Selfupdate 2. Tools->Interact with Fink... 3. Make sure "Accept default response" is selected, and click "Submit". 4. fink and other base packages will be built and run automatically Now that you've updated fink, you can install other packages. o To install from binaries, select the package, and use Binary->Install. o To install from source, select the package, and use Source->Install * Use apt-get. Apt-get will fetch and install binary packages for you, saving compiling time. You should either use this method or the Fink Commander binary method (above) if you don't have the Developer Tools installed. To update fink open a Terminal.app window and type sudo apt-get update ; sudo apt-get install fink Once you've updated fink, you can install other packages, using the same syntax, e.g sudo apt-get install gimp to install the Gimp. Note, however, that not all fink packages are in binary form. * Install from source (requires the XCode Tools [Developer Tools on 10.2] to be installed). To update fink run fink selfupdate. When prompted, select option (1), "rsync". This will automatically update the fink package. Once fink is updated, you can use "fink install" to fetch and compile from source code. For example, to install the Gimp, run fink install gimp. -------------------------------------------- 5) Use finkcommander to install ncurses - start the finkcommander application (should be in the folder you made called "fink extras") - type "ncurses" in the search window - locate nucurses, ncurses-dev, and ncurses-shlibs and see if they say "current" on the left and have a date under "installed". If so, you're good to go to the next step. If not, click on those that are not installed and click on the "install binary" icon (the little icon with the blue crss on it) -------------------------------------------- 6) Download and install Frobtads and the TADS2 and/or TADS3 compiler(s) Go to: http://www.tads.org/frobtads.htm - Download "FrobTADS" and then one or both of the TADS 2 and TAD 3 compiler add-ons depending on your preference. - Your computer will probably automatically decompress these items. If it hasn't done so for the frobtads folder, double-click on the disk image so it decompresses. Throw out any expanded versions of the Tads 2 or 3 compilers as you must expand these using "terminal" - Drag your decompressed frobtads folder to your computer icon - start the "terminal" program - type "cd /frobtads-0.6" and press enter - Drag the disk image of the compiler(s) to the expanded frobtads folder - Expand the compiler disk images using the "terminal" application - Do this by changing the directory to frobtads folder where the disk images are now stored cd /Applications/frobtads-0.6 - expand the files depending on which ones you have by typing the following: tar -xzf t3comp.tar.gz tar -xzf t2comp.tar.gz To configure and compile the program, type the following one-at-a-time: export PATH=$PATH:/usr/local/bin ./configure make sudo make install (after sudo make install you will be probably asked for your admin password) (If, after "make" all it says is: make all-am, type "make clean" and then "make" again) (If, after "make" it says "no rule to make target" then you've written over the tad3compiler folder with the one you expanded and you need to go back to the beginning and re-expand the frobtads folder. Make sure you expand the tads3 compiler using the "terminal" application and not stuffit) ------------------------ 7) Make sure all is in working order Frobtads should now be installed on your computer. Check by: downloading a Tads3 game, use the terminal program to change directories to the folder containing the game and then type "frob" and the game name in order to see if the game will run. Example: Download a game from here: http://www.ifarchive.org/indexes/if-archiveXgamesXtads.html If you saved it in a folder called "textadventuers" inside another called "games" you would do the following: cd /games/textadventures frob abrokenman.t3 If it works, test the compilers: TADS 2 (I don't know how to test it other than write a game and see if it compiles since I only have TADS 3 installed myself). To write a game, read: TADS 2: http://www.tela.bc.ca/tads-manual/ Note: For Tads 3, you don't need to specify where the compiler's include files are installed. But the Tads 2 compiler works different. When invoking 'tadsc', you'll have to use the -i option: tadsc -i /usr/local/share/frobtads/tads2 somesource.t For Tads3 compiler: make sample ./frob ./samples/sample.t3 If a game starts, all is well! Write your game! TADS 2: http://www.tela.bc.ca/tads-manual/ TADS 3: http://www.tads.org/t3doc/doc/index.htm NOTE ABOUT INVOKING (STARTING) T3MAKE, FROB ETC: If when you type t3make it says command not found, try typing /usr/local/bin/t3make If this works, t3make has been installed, it's just that your "path" isn't set-up. To set up your PATH in bash, you edit the file ~/.profile (using any text editor that can save in plain text - e.g. 'pico') and add a line like the following: export PATH="$PATH:/usr/local/bin" For information on how to edit this file, go to: http://forums.macosxhints.com/showthread.php?t=40648 -------------------------------------------- 8) Tidy Up! Once all is in working order, you can trash the frobtads folders and disk images as they are no longer neccessary. (Everything is now stored in folders in your computer, namely /usr/local) -------------------------------------------- 9) Notes Once a compiler or run-time is installed, you can just type the command like "frob" or "t3make" for example and you don't have to tell the computer where the command is stored, nor do you have to be in the directory where it's stored - you have to tell it where the game file is stored though or be located in the correct folder where the game is located When compiling using the Tads3 compiler the "-f" is vital... don't forget it! t3make -f heidi.t3m (or specify location: t3make -f /Games/heidi.t3m) Usually, in your own game, you name your project file (actually a makefile) "Makefile.t3m". That way, you only need to type: t3make to compile the game. No "-f Makefile.t3m" required. Where to get games: http://www.ifarchive.org/indexes/if-archiveXgamesXtads.html How to start writing your own games: TADS 2: http://www.tela.bc.ca/tads-manual/ TADS 3: http://www.tads.org/t3doc/doc/index.htm To play games: In the "terminal" program, type "frob" followed by the location of the game file. For instance, if I create a folder called "games" on my harddrive and I want to play a game called greatgame.gam, I would type: frob /games/greatgame.gam To get a list of command-line options: "frob --help". -------------------------------------------- 10) Troubleshooting 1 If both curses and ncurses are installed, ncurses will be preferred. If neither is found, the 'configure' script will inform you that the interpreter cannot be built. You need to go back to the fink program and make sure they are installed. 2 If you're "denied permission" at any time, add the word "sudo" to the front of the command 3 For Tads 3, you don't need to specify where the compiler's include files are installed. But the Tads 2 compiler works different. When invoking 'tadsc', you'll have to use the -i option: tadsc -i /usr/local/share/frobtads/tads2 somesource.t (Remember to use the actual path if you installed somewhere else.) 4 If, when you type "make" it just says "make all-am" and nothing else, type "make clean" and then "./configure" again 5 If you decide later you want to install one or the other of the compilers but you have already installed the frobtads program, type "./config-status --recheck" and then recompile. 5 If you're trying to do "make install" and nothing seems to work, type export PATH=$PATH:/usr/local/bin If you're told certain folders are missing too, type the line above and start again from ./configure 6 If it says t3make not found, you probably forgot export PATH=$PATH:/usr/local/bin 7 If the game isn't compiling, don't forget the "-f" part of the command. Also make sure to create an obj folder in the same folder as the heidi game. 8 If you've installed frobtads (when you type "cd /usr/local/bin" and then "ls" it is listed), but when you type t3make, it says command not found, you need to set up your "path" To set up your PATH in bash, you edit the file ~/.profile (using any text editor that can save in plain text - e.g. 'pico') and add a line like the following: export PATH="$PATH:/usr/local/bin" For information on how to edit this file, go to: http://forums.macosxhints.com/showthread.php?t=40648 --------------------------- Most important websites: www.tads.org http://www.ifarchive.org/ http://forums.macosxhints.com/showthread.php?t=40648 frobtads-1.2.3/doc/COMPILERS0000644000175000001440000000262712001065531014531 0ustar realncusersThere is no documentation on the TADS programming languages included in any of the FrobTADS packages. Luckily, the TADS homepage offers various downloads and links: http://www.tads.org The TADS homepage; has the latest news, updates and documentation for everything TADS-related. http://www.tads.org/t3doc/doc The TADS 3 Bookshelf. Everything you need to know about writing IF in TADS 3 can be found there. A must-visit. It contains various books, browsable online (HTML) and downloadable (PDF). http://teladesign.com/tads-manual/tadsman.zip The TADS 2 Author's Manual (HTML format). http://teladesign.com/tads-manual This leads to the online-version of the TADS 2 Author's Manual. Using the compilers =================== The compilers can be invoked with: tadsc (TADS 2 compiler) t3make (TADS 3 compiler) Note that, unlike the interpreter, the compilers don't support GNU-like command line options, which means that "short options" aren't available. For example, use "-help" instead of "--help". This is to ensure compatibility of compiler command line syntax between different ports. For Tads 3, you don't need to specify where the compiler's include files are installed. But the Tads 2 compiler works different. When invoking 'tadsc', you'll have to use the -i option: tadsc -i /usr/local/share/frobtads/tads2 mygame.t (Remember to use the actual path if you installed somewhere else.) frobtads-1.2.3/doc/SRC_GUIDELINES0000644000175000001440000001145712001065531015334 0ustar realncusersTo keep the code as portable as possible, follow these rules. All these "don't"s may seem brain-damaged, but there's really no other way to keep the code compileable on old and/or brain-damaged compilers. These rules are not a panacea, mind you. The intention is to make the code as portable as *possible*, so that any compiler-incompatibilities are easier to fix. They don't give a "100% portability guarantee." Furthermore, I'm no portability-wizard. These rules are a combination of my own and (most importantly) other people's experiences. If you have ideas regarding portability, please contact the maintainer. If you spot any code that does not follow these rules, please report it as a bug and/or send a patch. An exception to this are the files in the tads2/ and tads3/ directories; they are read-only. Don't touch them. Any changes made to them will be lost next time FrobTADS gets in sync with the TADS base code. Problems in any of these files should only be reported, not fixed. Sidenote: If you're looking for object oriented programming guidelines, forget it. FrobTADS is object based, not object oriented. It uses C++ mostly as a "better C". Classes and objects are only used to make things simpler, not with OOP in mind. OK, here goes: The "common.h" header file ========================== In new source files, always, Always, ALWAYS, #include "common.h" as the first header. Everywhere; both in C and C++ sources, in headers and *.c/*.cc/*.cpp files. 'and', 'or' and 'not' ===================== If you prefer these keywords over the usual '&&', '||' and '!' operators (I do), just use them as if they were available everywhere. You must always #include "common.h" for this to work. This only applies to C++ code of course. Don't use these keywords in C (they *are* in the C++ standard, even if old compilers don't support them, but not in ANSI C). C++ template library ==================== Don't use the C++ standard library. That means no , no , no , no , no fun. #including standard C-headers in C++ ==================================== Don't use new-style C++ includes to include C-library headers. Do this: #include rather than this: #include Namespaces ========== Don't use namespaces. for-loops ========= Don't rely on the modern semantics of for-loops. Don't do this: for (int i = 0; i < frob; ++i) { // ... } // ... for (int i = 0; [...] as it will only compile on modern compilers. Do this instead: int i; for (i = 0; i < frob; ++i) [...] for (i = 0; [...] This compiles on both modern and old compilers. If you really need a variable that expires after the loop, use this: {for (int i = 0; i < frob; ++i) { // ... }} Attention! If you have an old compiler, don't do this: for (int i = 0; [...] for (i = 0; [...] as it will *only* work with *old* compilers. Modern compilers can't compile this code. C++ casts ========= Don't use static_cast<> and friends. Do this instead: static_cast(foo)(bar) dynamic_cast(frob*)(baz) reinterpret_cast(const xyzzy*)(plugh) This will work on every compiler (as long as you #include "common.h" in your sources). On modern compilers, this: static_cast(int)(bar) will be macro-transformed into: static_cast(bar) while on old compilers, it will become: (int)(bar) Exception handling ================== Don't use exception handling. There's a portable exception-framework in the TADS 3 base code (transparently utilizing labels and gotos through macros); you might want to use it if you really can't do without exception handling. Templates ========= Don't use templates. Don't try to work around this by emulating them with macros; you'll just shoot yourself in the foot (and since this is C++, this will blow away your whole leg). Non-standard compiler features ============================== Don't use compiler-specific non-ANSI features; not everyone has GCC installed. You should always compile with GCC's "-ansi -pedantic" switches (both C and C++). Note that "-ansi -pedantic" cannot check your code for full ANSI correctness; they are only meant to catch obvious blunders. Non-ANSI/ISO functions ====================== If you feel like using a non-ANSI function (for example a BSD one), use it, but provide a working ANSI-only fall-back, as is already done with some functions in src/missing.[h/cc]. Also look at the implementation of os_get_sys_clock_ms() in src/oscurses.cc on how to handle portability issues. Generally, if such a function has equivalent ones in other standards, just check for them in "configure.ac" and structure your code to use the information the configuration script detects. Filenames ========= Don't use filenames longer than 14 characters. I'm not talking about DOS (which has the 8+3 limit). There are some Unices that have this limitation. frobtads-1.2.3/Makefile.am0000644000175000001440000000166611555503330014551 0ustar realncusers## Makefile.am -- Process this file with automake to produce Makefile.in ## This is the Automake master-file. It includes the other "fragments" ## as necessary (based on what configure.ac suggests). ## This will be copied verbatim to the generated Makefile.in and ## Makefile. ## # This file has been generated automatically. Any changes made to this # file will be *lost*. ## Include the rules common to many of the executables. ## include $(srcdir)/Common.am ## Include compiler data file definitions. ## include $(srcdir)/T3CompData.am ## Because each individual makefile expands bin_PROGRAMS with '+=', it ## must have been previously set, even if it's empty. ## bin_PROGRAMS = ## If we should build the compilers, include their makefiles. ## if BUILD_T2_COMPILER include $(srcdir)/T2Compiler.am endif if BUILD_T3_COMPILER include $(srcdir)/T3Compiler.am endif ## Ditto for the interpreter. ## if BUILD_INTERPRETER include Frob.am endif frobtads-1.2.3/T3Compiler.am0000644000175000001440000000450711561061155015013 0ustar realncusers## This file contains Automake rules for the TADS 3 compiler. ## Include rules for the TADS 3 test suite if we're building the debug ## version. ## if T3_DEBUG_BUILD include $(srcdir)/Testsuite.am endif bin_PROGRAMS += t3make AM_CPPFLAGS += -I$(srcdir)/src -I$(srcdir)/tads3 -I$(srcdir)/tads3/test ## TADS 3 compiler sources. ## T3CSOURCES = \ src/ost3comp.cc \ tads3/os_stdio.cpp \ tads3/rcmain.cpp \ tads3/std_dbg.cpp \ tads3/tcgenfil.cpp \ tads3/tcmakecl.cpp \ tads3/tcmake.cpp \ tads3/tcprsfil.cpp \ tads3/tcprsimg.cpp \ tads3/tcprsprg.cpp \ tads3/tct3img.cpp \ tads3/tct3prg.cpp \ tads3/vmbifc.cpp \ tads3/vmbifreg.cpp \ tads3/vmhttpdum.cpp \ tads3/vmimgrb.cpp \ tads3/vmpreini.cpp \ tads3/vmwrtimg.cpp t3make_SOURCES = $(COMMONSOURCES) $(T3HEADERS) $(T3RCSOURCES) $(T3CSOURCES) ## Install compiler data files. ## t3libdir = $(T3_LIB_DIR) t3incdir = $(T3_INC_DIR) t3extdir = $(T3_EXT_DIR) t3exttcommanddir = $(T3_EXT_TCOMMAND_DIR) t3exttcommanddocdir = $(T3_EXT_TCOMMAND_DOC_DIR) t3adv3dir = $(T3_ADV3_DIR) t3adv3enusdir = $(T3_ADV3_EN_US_DIR) t3webuiresdir = $(T3_WEBUIRES_DIR) t3docdir = $(T3_DOC_DIR) t3lib_DATA = $(T3_LIB_FILES) t3inc_DATA = $(T3_INC_FILES) t3ext_DATA = $(T3_EXT_FILES) t3exttcommand_DATA = $(T3_EXT_TCOMMAND_FILES) t3exttcommanddoc_DATA = $(T3_EXT_TCOMMAND_DOC_FILES) t3adv3_DATA = $(T3_ADV3_FILES) t3adv3enus_DATA = $(T3_ADV3_EN_US_FILES) t3webuires_DATA = $(T3_WEBUIRES_FILES) t3doc_DATA = $(T3_DOC_FILES) ## Target to build the TADS 3 sample game. We'll always rebuild it, no ## matter if it already exists or not. ## ## Note: These are make rules, so use tabs to indent the commands, not spaces! ## sample: t3make test -z "@abs_builddir@/samples/obj" || $(mkinstalldirs) "@abs_builddir@/samples/obj" rm -f "@abs_builddir@/samples/sample.t3m" cp "$(srcdir)/tads3/samples/sample.t3m" "@abs_builddir@/samples/sample.t3m" cd "@abs_builddir@/samples" && "@abs_builddir@/t3make" -f sample.t3m -a -FL "@abs_srcdir@/tads3/lib" \ -FI "@abs_srcdir@/tads3/include" -I "@abs_srcdir@/tads3/samples" -Fs "@abs_srcdir@/tads3/samples" \ -Fy "@abs_builddir@/samples/obj" -Fo "@abs_builddir@/samples/obj" sample-clean: rm -f "@abs_builddir@/samples/sample.t3m" "@abs_builddir@/samples/sample.t3" "@abs_builddir@/samples/gameinfo.txt" rm -rf "@abs_builddir@/samples/obj" frobtads-1.2.3/tads3/0000755000175000001440000000000012145614112013517 5ustar realncusersfrobtads-1.2.3/tads3/tcgen.cpp0000644000175000001440000010023411774034240015330 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tcgen.cpp,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcgen.cpp - TADS 3 Compiler code generator support classes Function Notes Modified 05/09/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "tcglob.h" #include "tcgen.h" #include "vmerr.h" #include "tcerrnum.h" #include "tctok.h" #include "tcprs.h" #include "tcmain.h" #include "vmfile.h" #include "tctarg.h" /* ------------------------------------------------------------------------ */ /* * Data/Code Stream Parser-Allocated Object */ /* * allocate via a parser memory allocator */ void *CTcCSPrsAllocObj::operator new(size_t siz, CTcPrsMem *allocator) { /* allocate via the allocator */ return allocator->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * Data Stream */ /* * initialize */ CTcDataStream::CTcDataStream(char stream_id) { /* remember my ID */ stream_id_ = stream_id; /* nothing is allocated yet */ ofs_ = 0; obj_file_start_ofs_ = 0; pages_ = 0; page_slots_ = 0; page_cnt_ = 0; page_cur_ = 0; rem_ = 0; wp_ = 0; /* we have no anchors yet */ first_anchor_ = last_anchor_ = 0; /* create our parser memory allocator */ allocator_ = new CTcPrsMem(); } /* * delete */ CTcDataStream::~CTcDataStream() { size_t i; /* delete the page slots if we allocated any */ for (i = 0 ; i < page_cnt_ ; ++i) t3free(pages_[i]); /* delete the page slot array if we allocated it */ if (pages_ != 0) t3free(pages_); /* delete our label/fixup allocator */ delete allocator_; } /* * Reset */ void CTcDataStream::reset() { /* move the write pointer back to the start */ ofs_ = 0; obj_file_start_ofs_ = 0; /* back to the first page */ page_cur_ = 0; /* set up to write to the first page, if we have any pages at all */ if (pages_ != 0) { /* we have all of the first page available again */ wp_ = calc_addr(0); rem_ = TCCS_PAGE_SIZE; } /* reset the allocator */ allocator_->reset(); /* * forget all of the anchors (no need to delete them explicitly - * they were allocated from our allocator pool, which we've reset to * completely discard everything it contained) */ first_anchor_ = last_anchor_ = 0; } /* * Decrement the write offset */ void CTcDataStream::dec_ofs(int amount) { /* adjust the offset */ ofs_ -= amount; /* * calculate the new page we're on, since this may take us to a * different page */ page_cur_ = ofs_ / TCCS_PAGE_SIZE; /* calculate the remaining size in this page */ rem_ = TCCS_PAGE_SIZE - (ofs_ % TCCS_PAGE_SIZE); /* calculate the current write pointer */ wp_ = calc_addr(ofs_); } /* * Get a pointer to a block at a given offset and a given length. */ const char *CTcDataStream::get_block_ptr(ulong ofs, ulong requested_len, ulong *available_len) { size_t page_rem; /* * determine how much is left on the page containing the offset * after the given offset */ page_rem = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); /* * if the amount remaining on the page is greater than the request * length, the available length is the entire request; otherwise, * the available length is the amount remaining on the page */ if (page_rem >= requested_len) *available_len = requested_len; else *available_len = page_rem; /* return the address at this offset */ return calc_addr(ofs); } /* * Write bytes to the stream at an earlier offset */ void CTcDataStream::write_at(ulong ofs, const char *buf, size_t len) { /* if we're writing to the current offset, use the normal writer */ if (ofs == ofs_) write(buf, len); /* * log an internal error, and skip writing anything, if the desired * range of offsets has not been previously written */ if (ofs + len > ofs_) G_tok->throw_internal_error(TCERR_WRITEAT_PAST_END); /* write the data to each page it spans */ while (len != 0) { size_t cur; /* * determine how much is left on the page containing the current * starting offset */ cur = TCCS_PAGE_SIZE - (ofs % TCCS_PAGE_SIZE); /* * figure out how much we can copy - copy the whole remaining * size, but no more than the amount remaining on this page */ if (cur > len) cur = len; /* copy the data */ memcpy(calc_addr(ofs), buf, cur); /* advance past this chunk */ len -= cur; ofs += cur; buf += cur; } } /* * Copy a chunk of the stream to the given buffer */ void CTcDataStream::copy_to_buf(char *buf, ulong start_ofs, ulong len) { /* read the data from each page that the block spans */ while (len != 0) { size_t cur; /* * determine how much is left on the page containing the current * starting offset */ cur = TCCS_PAGE_SIZE - (start_ofs % TCCS_PAGE_SIZE); /* * figure out how much we can copy - copy the whole remaining * size, but no more than the amount remaining on this page */ if (cur > len) cur = (size_t)len; /* copy the data */ memcpy(buf, calc_addr(start_ofs), cur); /* advance past this chunk */ len -= cur; start_ofs += cur; buf += cur; } } /* * Reserve space */ ulong CTcDataStream::reserve(size_t len) { ulong ret; /* we'll always return the offset current before the call */ ret = ofs_; /* if we have space on the current page, it's easy */ if (len <= rem_) { /* advance the output pointers */ ofs_ += len; wp_ += len; rem_ -= len; } else { /* keep going until we satisfy the request */ do { size_t cur; /* if necessary, allocate more memory */ if (rem_ == 0) alloc_page(); /* limit this chunk to the space remaining on the current page */ cur = len; if (cur > rem_) cur = rem_; /* skip past this chunk */ ofs_ += cur; wp_ += cur; rem_ -= cur; len -= cur; } while (len != 0); } /* return the starting offset */ return ret; } /* * Append data from another stream. The source stream is permanently * moved to the new stream, destroying the original stream. */ void CTcDataStream::append_stream(CTcDataStream *stream) { ulong rem; ulong ofs; ulong start_ofs; CTcStreamAnchor *anchor; CTcStreamAnchor *nxt; /* remember the starting offset of the copy in my stream */ start_ofs = get_ofs(); /* copy all data from the other stream */ for (ofs = 0, rem = stream->get_ofs() ; rem != 0 ; ) { ulong request; const char *ptr; ulong actual; /* * request as much as possible from the other stream, up to the * remaining length or 64k, whichever is smaller */ request = 65535; if (rem < request) request = rem; /* get the chunk from the source stream */ ptr = stream->get_block_ptr(ofs, request, &actual); /* * write this chunk (which we know is less than 64k and can thus * be safely cast to size_t, even on 16-bit machines) */ write(ptr, (size_t)actual); /* advance our counters */ rem -= actual; ofs += actual; } /* * Now copy all of the anchors from the source stream to our stream. * This will ensure that fixups in the other stream have * corresponding fixups in this stream. Note that we must adjust * the offset of each copied anchor by the offset of the start of * the copied data in our stream. */ for (anchor = stream->get_first_anchor() ; anchor != 0 ; anchor = nxt) { /* * remember the old link to the next anchor, since we're going * to move the anchor to my list and thus forget about its * position in the old list */ nxt = anchor->nxt_; /* adjust the anchor's offset */ anchor->ofs_ += start_ofs; /* unlink the anchor from its old stream */ anchor->nxt_ = 0; /* link it in to my anchor list */ if (last_anchor_ != 0) last_anchor_->nxt_ = anchor; else first_anchor_ = anchor; last_anchor_ = anchor; } } /* * Write bytes to the stream */ void CTcDataStream::write(const char *buf, size_t len) { /* * if possible, write it in one go (this is for efficiency, so that * we can avoid making a few comparisons in the most common case) */ if (len <= rem_) { /* write the data */ memcpy(wp_, buf, len); /* advance the output pointers */ ofs_ += len; wp_ += len; rem_ -= len; } else { /* keep going until we satisfy the request */ do { size_t cur; /* if necessary, allocate more memory */ if (rem_ == 0) alloc_page(); /* limit this chunk to the space remaining on the current page */ cur = len; if (cur > rem_) cur = rem_; /* copy it to the page */ memcpy(wp_, buf, cur); /* skip past the space written in the destination */ ofs_ += cur; wp_ += cur; rem_ -= cur; /* advance past the space in the source */ buf += cur; len -= cur; } while (len != 0); } } /* * allocate a new page */ void CTcDataStream::alloc_page() { /* * if we're coming back to a page that was previously allocated, we * need merely re-establish the existing page */ if (page_cur_ + 1 < page_cnt_) { /* move to the next page */ ++page_cur_; /* start writing at the start of the page */ wp_ = pages_[page_cur_]; rem_ = TCCS_PAGE_SIZE; /* we're done */ return; } /* * if we don't have room for a new page in the page array, expand * the page array */ if (page_cnt_ >= page_slots_) { /* increase the page slot count */ page_slots_ += 100; /* allocate or reallocate the page array */ if (pages_ == 0) pages_ = (char **)t3malloc(page_slots_ * sizeof(pages_[0])); else pages_ = (char **)t3realloc(pages_, page_slots_ * sizeof(pages_[0])); /* if that failed, throw an error */ if (pages_ == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* allocate the new page */ pages_[page_cnt_] = (char *)t3malloc(TCCS_PAGE_SIZE); /* throw an error if we couldn't allocate the page */ if (pages_[page_cnt_] == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* start writing at the start of the new page */ wp_ = pages_[page_cnt_]; /* the entire page is free */ rem_ = TCCS_PAGE_SIZE; /* make the new page the current page */ page_cur_ = page_cnt_; /* count the new page */ ++page_cnt_; } /* * Add an absolute fixup for this stream at the current write offset. */ void CTcDataStream::add_abs_fixup(CTcAbsFixup **list_head) { /* add the fixup to the list at my current write location */ CTcAbsFixup::add_abs_fixup(list_head, this, get_ofs()); } /* * Add an anchor at the current offset. */ CTcStreamAnchor *CTcDataStream::add_anchor(CTcSymbol *owner_sym, CTcAbsFixup **fixup_list_head, ulong ofs) { CTcStreamAnchor *anchor; /* allocate the anchor, giving it our current offset */ anchor = new (allocator_) CTcStreamAnchor(owner_sym, fixup_list_head, ofs); /* append it to our list */ if (last_anchor_ != 0) last_anchor_->nxt_ = anchor; else first_anchor_ = anchor; last_anchor_ = anchor; /* return the new anchor */ return anchor; } /* * Find an anchor with the given stream offset */ CTcStreamAnchor *CTcDataStream::find_anchor(ulong ofs) const { CTcStreamAnchor *cur; /* scan the anchor list */ for (cur = first_anchor_ ; cur != 0 ; cur = cur->nxt_) { /* if this one has the desired offset, return it */ if (cur->get_ofs() == ofs) return cur; } /* didn't find it */ return 0; } /* * Write an object ID */ void CTcDataStream::write_obj_id(ulong obj_id) { /* * if there's an object ID fixup list, and this is a valid object * reference (not a 'nil' reference), add this reference */ if (G_keep_objfixups && obj_id != TCTARG_INVALID_OBJ) CTcIdFixup::add_fixup(&G_objfixup, this, get_ofs(), obj_id); /* write the ID */ write4(obj_id); } /* * Write an object ID self-reference */ void CTcDataStream::write_obj_id_selfref(CTcSymObj *obj_sym) { /* * Add a fixup list entry to the symbol. This type of reference * must be kept with the symbol rather than in the global list, * because we must apply this type of fixup each time we renumber * the symbol. */ obj_sym->add_self_ref_fixup(this, get_ofs()); /* write the ID to the stream */ write4(obj_sym->get_obj_id()); } /* * Write a property ID */ void CTcDataStream::write_prop_id(uint prop_id) { /* if there's an object ID fixup list, add this reference */ if (G_keep_propfixups) CTcIdFixup::add_fixup(&G_propfixup, this, get_ofs(), prop_id); /* write the ID */ write2(prop_id); } /* * Write an enumerator ID */ void CTcDataStream::write_enum_id(ulong enum_id) { /* if there's a fixup list, add this reference */ if (G_keep_enumfixups) CTcIdFixup::add_fixup(&G_enumfixup, this, get_ofs(), enum_id); /* write the ID */ write4(enum_id); } /* * Given a stream ID, get the stream */ CTcDataStream *CTcDataStream:: get_stream_from_id(char stream_id, const textchar_t *obj_fname) { switch(stream_id) { case TCGEN_DATA_STREAM: return G_ds; case TCGEN_CODE_STREAM: return G_cs_main; case TCGEN_STATIC_CODE_STREAM: return G_cs_static; case TCGEN_OBJECT_STREAM: return G_os; case TCGEN_ICMOD_STREAM: return G_icmod_stream; case TCGEN_BIGNUM_STREAM: return G_bignum_stream; case TCGEN_REXPAT_STREAM: return G_rexpat_stream; case TCGEN_STATIC_INIT_ID_STREAM: return G_static_init_id_stream; case TCGEN_LCL_VAR_STREAM: return G_lcl_stream; default: G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INVAL_STREAM_ID, obj_fname); return 0; } } /* ------------------------------------------------------------------------ */ /* * Code Stream */ /* * create the code stream */ CTcCodeStream::CTcCodeStream(char stream_id) : CTcDataStream(stream_id) { /* no switch yet */ cur_switch_ = 0; /* no enclosing statement yet */ enclosing_ = 0; /* no code body being generated yet */ code_body_ = 0; /* start writing at offset zero */ ofs_ = 0; /* no symbol tables yet */ symtab_ = 0; goto_symtab_ = 0; /* no labels yet */ active_lbl_ = 0; free_lbl_ = 0; /* no fixups yet */ free_fixup_ = 0; /* allocate an initial set of line record pages */ line_pages_alloc_ = 0; line_pages_ = 0; alloc_line_pages(5); /* no line records in use yet */ line_cnt_ = 0; /* no local frame yet */ cur_frame_ = 0; } /* * destroy the code stream */ CTcCodeStream::~CTcCodeStream() { size_t i; /* release all active labels */ release_labels(); /* delete the line records pages */ for (i = 0 ; i < line_pages_alloc_ ; ++i) t3free(line_pages_[i]); /* delete the master list of pages */ t3free(line_pages_); } /* * Set the current local frame */ CTcPrsSymtab *CTcCodeStream::set_local_frame(CTcPrsSymtab *symtab) { /* remember the original local frame, so we can return it later */ CTcPrsSymtab *old_frame = cur_frame_; /* add the current byte code location to the outgoing frame */ if (old_frame != 0) old_frame->add_to_range(ofs_ - method_ofs_); /* remember the current frame */ cur_frame_ = symtab; /* add it to the local frame list for the method if necessary */ add_local_frame(symtab); /* add the current byte code location to the incoming frame */ symtab->add_to_range(ofs_ - method_ofs_); /* return the original local frame */ return old_frame; } /* * Reset */ void CTcCodeStream::reset() { /* inherit default */ CTcDataStream::reset(); /* clear the line records */ clear_line_recs(); /* * forget all of the labels and fixups - they're allocated from the * allocator pool, which we've completely reset now */ free_lbl_ = active_lbl_ = 0; free_fixup_ = 0; /* forget all of the symbol tables */ symtab_ = 0; goto_symtab_ = 0; /* forget the frame list */ frame_head_ = frame_tail_ = 0; cur_frame_ = 0; frame_cnt_ = 0; /* forget all of the statement settings */ cur_switch_ = 0; enclosing_ = 0; code_body_ = 0; /* presume 'self' is not available */ self_available_ = FALSE; } /* * Allocate line record pages */ void CTcCodeStream::alloc_line_pages(size_t number_to_add) { size_t siz; size_t i; /* create or expand the master page array */ siz = (line_pages_alloc_ + number_to_add) * sizeof(tcgen_line_page_t *); if (line_pages_ == 0) line_pages_ = (tcgen_line_page_t **)t3malloc(siz); else line_pages_ = (tcgen_line_page_t **)t3realloc(line_pages_, siz); /* allocate the new pages */ for (i = line_pages_alloc_ ; i < line_pages_alloc_ + number_to_add ; ++i) { /* allocate this page */ line_pages_[i] = (tcgen_line_page_t *) t3malloc(sizeof(tcgen_line_page_t)); } /* remember the new allocation */ line_pages_alloc_ += number_to_add; } /* * Allocate a new label object */ CTcCodeLabel *CTcCodeStream::alloc_label() { CTcCodeLabel *ret; /* if there's anything in the free list, use it */ if (free_lbl_ != 0) { /* take the first one off the free list */ ret = free_lbl_; /* unlink it from the list */ free_lbl_ = free_lbl_->nxt; } else { /* allocate a new label */ ret = new (allocator_) CTcCodeLabel; /* throw an error if allocation failed */ if (ret == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* add the label to the active list */ ret->nxt = active_lbl_; active_lbl_ = ret; /* return the allocated label */ return ret; } /* * Allocate a new fixup object */ CTcLabelFixup *CTcCodeStream::alloc_fixup() { CTcLabelFixup *ret; /* if there's anything in the free list, use it */ if (free_fixup_ != 0) { /* take the first one off the free list */ ret = free_fixup_; /* unlink it from the list */ free_fixup_ = free_fixup_->nxt; } else { /* allocate a new fixup */ ret = new (allocator_) CTcLabelFixup; /* throw an error if allocation failed */ if (ret == 0) err_throw(TCERR_CODEGEN_NO_MEM); } /* return the allocated fixup */ return ret; } /* * Release all active labels. If any labels are undefined, log an * internal error. */ void CTcCodeStream::release_labels() { int err_cnt; /* we haven't found any errors yet */ err_cnt = 0; /* run through the list of active labels */ while (active_lbl_ != 0) { CTcCodeLabel *lbl; /* pull this label off of the active list */ lbl = active_lbl_; active_lbl_ = active_lbl_->nxt; /* put this label on the free list */ lbl->nxt = free_lbl_; free_lbl_ = lbl; /* check for unresolved fixups */ while (lbl->fhead != 0) { CTcLabelFixup *fixup; /* pull this fixup off of the active list */ fixup = lbl->fhead; lbl->fhead = lbl->fhead->nxt; /* put this fixup on the free list */ fixup->nxt = free_fixup_; free_fixup_ = fixup; /* count the unresolved label */ ++err_cnt; } } /* * if we found any unresolved fixups, log the error; there's not * much point in logging each error individually, since this is an * internal compiler error that the user can't do anything about, * but at least give the user a count for compiler diagnostic * purposes */ if (err_cnt != 0) G_tcmain->log_error(0, 0, TC_SEV_INTERNAL, TCERR_UNRES_TMP_FIXUP, err_cnt); } /* * Allocate a new label at the current code offset */ CTcCodeLabel *CTcCodeStream::new_label_here() { CTcCodeLabel *lbl; /* allocate a new label */ lbl = alloc_label(); /* set the label's location to the current write position */ lbl->ofs = ofs_; lbl->is_known = TRUE; /* return the new label */ return lbl; } /* * Allocate a new forward-reference label */ CTcCodeLabel *CTcCodeStream::new_label_fwd() { CTcCodeLabel *lbl; /* allocate a new label */ lbl = alloc_label(); /* the label's location is not yet known */ lbl->ofs = 0; lbl->is_known = FALSE; /* return the new label */ return lbl; } /* * Define the position of a label, resolving any fixups associated with * the label. */ void CTcCodeStream::def_label_pos(CTcCodeLabel *lbl) { /* set the label's position */ lbl->ofs = ofs_; lbl->is_known = TRUE; /* resolve each fixup */ while (lbl->fhead != 0) { CTcLabelFixup *fixup; long diff; char buf[4]; /* pull this fixup off of the active list */ fixup = lbl->fhead; lbl->fhead = lbl->fhead->nxt; /* * calculate the offset from the fixup position to the label * position, applying the bias to the fixup position */ diff = lbl->ofs - (fixup->ofs + fixup->bias); /* convert the offset to the correct format and write it out */ if (fixup->is_long) { /* write an INT4 offset value */ oswp4(buf, diff); write_at(fixup->ofs, buf, 4); } else { /* write an INT2 offset value */ oswp2s(buf, diff); write_at(fixup->ofs, buf, 2); } /* add this fixup to the free list, since we're finished with it */ fixup->nxt = free_fixup_; free_fixup_ = fixup; } } /* * Determine if a label has a fixup at a particular offset */ int CTcCodeStream::has_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) { CTcLabelFixup *fixup; /* scan for a match */ for (fixup = lbl->fhead ; fixup != 0 ; fixup = fixup->nxt) { /* if this is a match, indicate success */ if (fixup->ofs == ofs) return TRUE; } /* we didn't find a match */ return FALSE; } /* * Remove a label's fixup at a particular offset */ void CTcCodeStream::remove_fixup_at_ofs(CTcCodeLabel *lbl, ulong ofs) { CTcLabelFixup *fixup; CTcLabelFixup *prv; CTcLabelFixup *nxt; /* scan for a match */ for (prv = 0, fixup = lbl->fhead ; fixup != 0 ; prv = fixup, fixup = nxt) { /* remember the next one */ nxt = fixup->nxt; /* if this is a match, remove it */ if (fixup->ofs == ofs) { /* unlink this fixup from the list */ if (prv == 0) lbl->fhead = nxt; else prv->nxt = nxt; /* move it to the free list */ fixup->nxt = free_fixup_; free_fixup_ = fixup; } } } /* * Write an offset value to the given label */ void CTcCodeStream::write_ofs(CTcCodeLabel *lbl, int bias, int is_long) { /* if the label is known, write it; otherwise, generate a fixup */ if (lbl->is_known) { long diff; /* * calculate the branch offset from the current position, * applying the bias to the current position */ diff = lbl->ofs - (ofs_ + bias); /* convert the offset to the correct format and write it out */ if (is_long) write4(diff); else write2(diff); } else { CTcLabelFixup *fixup; /* allocate a fixup */ fixup = alloc_fixup(); /* set up the fixup data */ fixup->ofs = ofs_; fixup->bias = bias; fixup->is_long = is_long; /* link the fixup into the label's fixup list */ fixup->nxt = lbl->fhead; lbl->fhead = fixup; /* write a placeholder to the code stream */ if (is_long) write4(0); else write2(0); } } /* * Add a new line record at the current code offset */ void CTcCodeStream::add_line_rec(CTcTokFileDesc *file, long linenum) { /* if there's no file descriptor, there's nothing to add */ if (file == 0) return; /* compute the current offset, relative to the start of the method */ ulong cur_ofs = G_cs->get_ofs() - method_ofs_; /* presume we won't re-use the previous record */ int reuse = FALSE; /* * If we haven't added any code since the previous line record, * overwrite the previous record - it doesn't refer to any * executable code, so it's an unnecessary record. Similarly, if * the previous record is at the same source position, don't add a * separate line record for it, since the debugger won't be able to * treat the two lines separately. */ tcgen_line_t *rec; if (line_cnt_ > 0) { /* get the previous record */ rec = get_line_rec(line_cnt_ - 1); /* * if it refers to the same code offset, replace the old record * with one at this location */ if (rec->ofs == cur_ofs) reuse = TRUE; /* * if it has the identical source file location, don't bother * adding a new record at all */ if (rec->source_id == file->get_index() && rec->source_line == linenum) return; } /* if we're not re-using the previous record, allocate a new one */ if (!reuse) { /* * we need a new record - if we've used all the allocated space, * allocate more */ if (line_cnt_ >= line_pages_alloc_ * TCGEN_LINE_PAGE_SIZE) alloc_line_pages(5); /* get a pointer to the next available entry */ rec = get_line_rec(line_cnt_); /* count the new record */ ++line_cnt_; } /* store the code offset relative to the start of the current method */ rec->ofs = cur_ofs; /* store the file information */ rec->source_id = file->get_index(); rec->source_line = linenum; /* store the frame information */ rec->frame = cur_frame_; } /* * Get the nth line record */ tcgen_line_t *CTcCodeStream::get_line_rec(size_t n) { return &(line_pages_[n / TCGEN_LINE_PAGE_SIZE] ->lines[n % TCGEN_LINE_PAGE_SIZE]); } /* * Add a frame to the list of local frames in the method */ void CTcCodeStream::add_local_frame(CTcPrsSymtab *symtab) { /* * If this is the global symbol table, or it's null, or it's already * in a list, ignore it. Note that we can tell if the item is in a * list by checking its index value - a value of zero is never a * valid index and thus indicates that the item isn't in a list yet. */ if (symtab == G_prs->get_global_symtab() || symtab == 0 || symtab->get_list_index() != 0) return; /* link the frame in at the tail of our list */ symtab->set_list_next(0); if (frame_tail_ == 0) frame_head_ = symtab; else frame_tail_->set_list_next(symtab); frame_tail_ = symtab; /* count the new entry in the list */ ++frame_cnt_; /* * Set this frame's index in the list. Note that we've already * incremented the index value, so the first frame in the list will * have index 1, as is required. */ symtab->set_list_index(frame_cnt_); } /* ------------------------------------------------------------------------ */ /* * Data stream anchor */ /* * Get the length. We can deduce the length by subtracting our offset * from the next item's offset, or, if we're the last item, from the * length of the stream. */ ulong CTcStreamAnchor::get_len(CTcDataStream *ds) const { if (nxt_ != 0) { /* * there's another item after me - my length is the difference * between the next item's offset and my offset */ return (nxt_->ofs_ - ofs_); } else { /* I'm the last item - my length is whatever is left in the stream */ return (ds->get_ofs() - ofs_); } } /* * Set the finaly absoluate address, and apply fixups. The code * generator must invoke this during the link phase once this object's * final address is known. */ void CTcStreamAnchor::set_addr(ulong addr) { /* remember my address */ addr_ = addr; /* apply all outstanding fixups for this object */ CTcAbsFixup::fix_abs_fixup(*fixup_list_head_, addr); } /* ------------------------------------------------------------------------ */ /* * Absolute Fixup Object */ /* * Add an absolute fixup at the current stream location to a given fixup * list. */ void CTcAbsFixup::add_abs_fixup(CTcAbsFixup **list_head, CTcDataStream *ds, ulong ofs) { CTcAbsFixup *fixup; /* * create the fixup object - allocate it out of our fixup allocator * pool, since this fixup object has the same attributes (small and * long-lived) as other fixup objects */ fixup = (CTcAbsFixup *)G_prsmem->alloc(sizeof(CTcAbsFixup)); /* set the fixup location to the current offset */ fixup->ds = ds; fixup->ofs = ofs; /* link it in to the caller's list */ fixup->nxt = *list_head; *list_head = fixup; } /* * Fix up a fix-up list */ void CTcAbsFixup::fix_abs_fixup(CTcAbsFixup *list_head, ulong final_ofs) { CTcAbsFixup *cur; /* scan the list and fix up each entry */ for (cur = list_head ; cur != 0 ; cur = cur->nxt) { /* * fix this entry by writing the final offset in UINT4 format to * the target stream at the target offset */ cur->ds->write4_at(cur->ofs, final_ofs); } } /* ------------------------------------------------------------------------ */ /* * Object/property ID fixups */ /* * add a fixup to a list */ void CTcIdFixup::add_fixup(CTcIdFixup **list_head, class CTcDataStream *ds, ulong ofs, ulong id) { CTcIdFixup *fixup; /* create the new fixup object */ fixup = new (G_prsmem) CTcIdFixup(ds, ofs, id); /* link it in at the head of the list */ fixup->nxt_ = *list_head; *list_head = fixup; } /* * Apply this fixup */ void CTcIdFixup::apply_fixup(ulong final_id, size_t siz) { /* write the fixup */ if (siz == 2) ds_->write2_at(ofs_, (uint)final_id); else ds_->write4_at(ofs_, final_id); } frobtads-1.2.3/tads3/README.TXT0000644000175000001440000000656312000300161015051 0ustar realncusersText Adventure Development System - TADS 3 Source Code Distribution Copyright (c) 1999, 2003 by Michael J. Roberts. All Rights Reserved. This is the C++ source code for TADS 3. In order to help make TADS available on a wide range of computer systems, we're distributing this source code. TADS 3 and this source code are copyrighted works, so please read the license information below if you're going to use these files. We want to keep TADS consistent in features and functionality across all of the different computers it works on, so we ask that you do not make any changes to this code beyond what is necessary to make TADS work on your type of computer system. In addition, the author retains the rights to TADS and any derivative works based on this source code. If your main interest in TADS is for writing or playing a game, you probably don't need this source code, since executable versions of TADS are available for a number of platforms. This source code is meant primarily for people who want to port TADS to new systems, or track down and fix problems with the system code itself. ------------------------------------------------------------------------------ HOW TO PORT TADS 3 TO A NEW PLATFORM To use this code, you will need a C++ compiler, and you may have to do some C or C++ coding to customize TADS to your system, in order to build a working version of TADS from this source code. This code depends upon the TADS 2 Operating System Interface (osifc) layer, which is not included with this distribution. You can obtain the TADS 2 source code, including TADS 2 osifc implementations for many operating systems, via FTP from ftp://ftp.ifarchive.org/if-archive/programming/tads2/source (Note that ftp.ifarchive.org is the new home, effective August 2001, of the former ftp.gmd.de archive.) TADS 3 uses the identical osifc implementation that TADS 2 uses. This means that if TADS 2 has been ported to your operating system, you will probably not have to write any new code to port TADS 3 - all you'll have to do is create a makefile (or your local equivalent) and build TADS 3 using the same osifc implementation files that you used for TADS 2. If TADS 2 has not already been ported to your system, you must first write the code necessary to implement the osifc layer for your platform. You don't need to port all of TADS 2, but you must port at least the osifc code. Refer to the file PORTNOTE.TXT in the TADS 2 source code distribution for information on how to do this. ------------------------------------------------------------------------------ CONTACTING THE AUTHOR For contact information, please visit www.tads.org. If you port this software to a new platform, I'd appreciate hearing about any changes you had to make to the portable code to make TADS work on your system. My goal is for the portable part of the code to compile and run everywhere without any changes; all of the port-specific code should be isolated to the "osifc" layer. Any time the portable part of the code proves to be less than portable, I consider it a bug and will try to fix it. ------------------------------------------------------------------------------ COPYRIGHT INFORMATION TADS 3 is "freeware," which means that it's copyrighted software that the author makes available free of charge but subject to certain conditions. Please see the accompanying license file LICENSE.TXT for license information. frobtads-1.2.3/tads3/vmstack.h0000644000175000001440000002607511744244235015363 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMSTACK.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstack.h - VM stack manager Function Notes Modified 10/28/98 MJRoberts - Creation */ #ifndef VMSTACK_H #define VMSTACK_H #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * The stack pointer and a few other VM registers are critical to * performance, since they're accessed so frequently. If we're compiling * in VMGLOB_VARS or VMGLOB_STRUCT mode, pull out the key registers as * global static variables; this results in smaller and faster code on most * machines than when it's part of the CVmStack structure. Don't do this * when compiling in other global modes, since the local implementation * must have a reason to keep everything in parameters (probably a shared * memory situation, such as threads or a shared library). */ #if defined(VMGLOB_VARS) || defined(VMGLOB_STRUCT) /* global variable mode - define registers as separate globals */ # define VM_IF_REGS_IN_STRUCT(decl) # define VM_IF_REGS_IN_GLOBALS(decl) decl # define VM_REG_ACCESS static # define VM_REG_CONST #else /* structure mode - define registers as part of CVmStack/CVmRun */ # define VM_IF_REGS_IN_STRUCT(decl) decl # define VM_IF_REGS_IN_GLOBALS(decl) # define VM_REG_ACCESS # define VM_REG_CONST const #endif /* declare the stack pointer register as an extern global, if appropriate */ VM_IF_REGS_IN_GLOBALS(extern vm_val_t *sp_;) /* ------------------------------------------------------------------------ */ /* * VM stack interface */ class CVmStack { public: /* * Allocate the stack. The maximum depth that the stack can achieve * is fixed when the stack is created. */ CVmStack(size_t max_depth, size_t reserve_depth); /* initialize */ void init() { /* start the stack pointer at the first element */ sp_ = arr_; } /* delete the stack */ ~CVmStack(); /* * get the current stack depth - this gives the number of active * elements in the stack */ size_t get_depth() const { return sp_ - arr_; } /* * Translate between pointer and index values. An index value is * simply an integer giving the index in the stack of the given * pointer; this value can be used, for example, for saving a stack * location persistently. * * We return zero for a null stack pointer; we always return * non-zero for a non-null stack pointer. */ ulong ptr_to_index(vm_val_t *p) const { /* if it's null, return index 0; otherwise, return a non-zero index */ return (p == 0 ? 0 : p - arr_ + 1); } vm_val_t *index_to_ptr(ulong idx) const { /* if the index is zero, it's null; otherwise, get the pointer */ return (idx == 0 ? 0 : arr_ + idx - 1); } /* * Get the depth relative to a given frame pointer. This returns * the number of items on the stack beyond the given pointer. If * the given frame pointer is beyond the current stack pointer * (i.e., values have been popped since the frame pointer equalled * the stack pointer), the return value will be negative. */ VM_REG_ACCESS int get_depth_rel(vm_val_t *fp) VM_REG_CONST { return sp_ - fp; } /* get the current stack pointer */ VM_REG_ACCESS vm_val_t *get_sp() VM_REG_CONST { return sp_; } /* * Set the current stack pointer. The pointer must always be a * value previously returned by get_sp(). */ VM_REG_ACCESS void set_sp(vm_val_t *p) { sp_ = p; } /* * Get an element relative to a frame pointer (a frame pointer is a * stack position that was previously obtained via get_sp() and * stored by the caller). The offset is negative for a value pushed * prior to the frame pointer, zero for the value at the frame * pointer, or positive for values pushed after the frame pointer. */ static vm_val_t *get_from_frame(vm_val_t *fp, int i) { return (fp + i - 1); } /* push a value */ VM_REG_ACCESS void push(const vm_val_t *val) { *sp_++ = *val; } /* * Push an element, returning a pointer to the element; this can be * used to fill in a new stack element directly, without copying a * value. The new element is not filled in yet on return, so the * caller should immediately fill in the element with a valid value. */ VM_REG_ACCESS vm_val_t *push() { return sp_++; } /* * Push a number of elements: this allocates a block of contiguous * stack elements that the caller can fill in individually. The stack * elements are uninitialized, so the caller must set the values * immediately on return. A pointer to the first pushed element is * returned; subsequent elements are addressed at the return value * plus 1, plus 2, and so on. */ VM_REG_ACCESS vm_val_t *push(unsigned int n) { /* remember the current stack pointer, which is what we return */ vm_val_t *ret = sp_; /* allocate the elements */ sp_ += n; /* return the base of the allocated block */ return ret; } /* push, checking space */ void push_check(const vm_val_t *val) { *push_check() = *val; } vm_val_t *push_check() { check_throw(1); return push(); } vm_val_t *push_check(unsigned int n) { check_throw(n); return push(n); } /* * Insert space for 'num' slots at index 'idx'. If 'idx' is zero, this * is the same as pushing 'num' slots. Returns a pointer to the first * slot allocated. */ vm_val_t *insert(size_t idx, size_t num) { /* make sure there's room */ check_space(num); /* add the space */ sp_ += num; /* if idx is non-zero, move the idx slots by num to make room */ if (idx != 0) memmove(sp_ - idx, sp_ - idx - num, idx*sizeof(*sp_)); /* return the start of the inserted block */ return sp_ - idx - num; } /* * Get an element. Elements are numbered from zero to (depth - 1). * Element number zero is the item most recently pushed onto the * stack; element (depth-1) is the oldest element on the stack. */ VM_REG_ACCESS vm_val_t *get(size_t i) VM_REG_CONST { return (sp_ - i - 1); } /* pop the top element off the stack */ VM_REG_ACCESS void pop(vm_val_t *val) { *val = *--sp_; } /* discard the top element */ VM_REG_ACCESS void discard() { --sp_; } /* discard a given number of elements from the top of the stack */ VM_REG_ACCESS void discard(int n) { sp_ -= n; } /* * Probe the stack for a given allocation. Returns true if the given * number of slots are available, false if not. Does NOT actually * allocate the space; merely checks for availability. * * Compilers are expected to produce function headers that check for * the maximum amount of stack space needed locally in the function on * entry, which allows us to check once at the start of the function * for available stack space, relieving us of the need to check for * available space in every push operation. * * Returns true if the required amount of space is available, false if * not. * * (NB: 'slots' really should be a size_t, as it's not meaningful to * reserve negative space. However, a compiler bug in 3.0.17, * 3.0.17.1, and 3.0.18 caused the compiler to generate the wrong * reservation sizes in headers; affected code occasionally makes * OPC_MAKELSTPAR compute a negative value for its size request here. * Making 'slots' signed makes the result of (get_depth() + ) * less than the current depth, as it should be, whereas a size_t type * makes it yield a huge positive value on some systems, which looks * like a stack overflow. These faulty .t3 files are generally benign * despite the stack size miscalculation, because they're usually not * so far off that we'd actually blow past the real memory limits of * the stack, thanks to the exception-handling reserve. In other * words, we'd usually catch an actual stack overflow in a faulty .t3 * before it crashed the interpreter. So it's desirable to be * compatible with such files.) */ int check_space(int nslots) const { return (get_depth() + nslots <= max_depth_); } /* check space for 'nslots' new slots, throwing an error on overflow */ void check_throw(int nslots) const { if (!check_space(nslots)) err_throw(VMERR_STACK_OVERFLOW); } /* * Release the reserve. Debuggers can use this to allow manual * recovery from stack overflows, by making some extra stack * temporarily available for the debugger's use in handling the * overflow. This releases the reserve, if available, that was * specified when the stack was allocated. Returns true if reserve * space is available for release, false if not. */ int release_reserve() { /* if the reserve is already in use, we can't release it again */ if (reserve_in_use_) return FALSE; /* add the reserve space to the maximum stack depth */ max_depth_ += reserve_depth_; /* note that the reserve has been released */ reserve_in_use_ = TRUE; /* indicate that we successfully released the reserve */ return TRUE; } /* * Recover the reserve. If the debugger releases the reserve to handle * a stack overflow, it can call this once the situation has been dealt * with to take the reserve back out of play, so that the debugger can * deal with any future overflow in the same manner. */ void recover_reserve() { /* if the reserve is in use, put it back in reserve */ if (reserve_in_use_) { /* remove the reserve from the stack */ max_depth_ -= reserve_depth_; /* mark the reserve as available again */ reserve_in_use_ = FALSE; } } private: /* the array of value holders making up the stack */ vm_val_t *arr_; /* * Next available stack slot - the stack pointer starts out pointing at * arr_[0], and is incremented after storing each element. This is * defined as a member variable only if we're not defining it as a * separate static global. */ VM_IF_REGS_IN_STRUCT(vm_val_t *sp_;) /* maximum depth that the stack is capable of holding */ size_t max_depth_; /* extra reserve space */ size_t reserve_depth_; /* flag: the reserve has been released for VM use */ int reserve_in_use_; }; #endif /* VMSTACK_H */ frobtads-1.2.3/tads3/vmconmor.cpp0000644000175000001440000001007410535121333016064 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconmor.cpp - TADS 3 console input/output - MORE-enabled/text-only version Function There are three possible configurations for the output formatter. For each configuration, there is one extra file that must be linked into the system when building an application; the choice of this file determines the configuration of the system. The three choices are: Text only, MORE mode enabled - vmconmor.cpp Text only, OS-level MORE handling - vmconnom.cpp HTML mode - vmconhtm.cpp Notes Modified 09/06/99 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmconsol.h" /* ------------------------------------------------------------------------ */ /* * This is a MORE-enabled configuration, so indicate that we should handle * MORE mode in the formatter layer. */ int CVmFormatter::formatter_more_mode() const { return TRUE; } /* ------------------------------------------------------------------------ */ /* * This is a non-HTML version, so we turn off the HTML-target flag */ int CVmFormatter::get_init_html_target() const { return FALSE; } /* ------------------------------------------------------------------------ */ /* * This is a text-only (non-HTML) version, so the HTML start/end functions * do nothing */ void CVmFormatterMain::start_html_in_os() { } void CVmFormatterMain::end_html_in_os() { } /* * This is a MORE-enabled configuration, so indicate that we handle line * wrapping ourselves (not in the OS layer). */ int CVmFormatterMain::get_os_line_wrap() { return FALSE; } /* ------------------------------------------------------------------------ */ /* * This is a MORE-mode version, so show a MORE prompt ourselves */ void CVmConsole::show_con_more_prompt(VMG0_) { int done; int next_page; /* display the "MORE" prompt */ disp_str_->print_to_os("[More]"); disp_str_->flush_to_os(); /* wait for an acceptable keystroke */ for (done = FALSE ; !done ; ) { os_event_info_t evt; /* get an event */ switch(os_get_event(0, FALSE, &evt)) { case OS_EVT_KEY: switch(evt.key[0]) { case ' ': /* stop waiting, show one page */ done = TRUE; next_page = TRUE; break; case '\r': case '\n': case 0x2028: /* unicode line separator character */ /* stop waiting, show one line */ done = TRUE; next_page = FALSE; break; default: /* ignore any other keystrokes */ break; } break; case OS_EVT_EOF: /* end of file - there's nothing to wait for now */ done = TRUE; next_page = TRUE; /* stop showing [more] prompts, as the user can't respond */ G_os_moremode = FALSE; break; default: /* ignore other events */ break; } } /* * Remove the prompt from the screen by backing up and overwriting * it with spaces. (Note that this assumes that we're running in * some kind of terminal or character mode with a fixed-pitch font; * if that's not the case, the OS layer should be taking * responsibility for pagination anyway, so this code shouldn't be * in use in the first place.) */ disp_str_->print_to_os( "\r \r" ); /* * if they pressed the space key, it means that we should show an * entire new page, so reset the line count to zero; otherwise, * we'll want to display another MORE prompt at the very next line, * so leave the line count alone */ if (next_page) disp_str_->reset_line_count(FALSE); } frobtads-1.2.3/tads3/vmglob.h0000644000175000001440000005306212145504453015172 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmglob.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmglob.h - T3 VM globals Function The T3 VM requires a number of static variables that are shared by several subsystems. This file defines the globals and a scheme for accessing them. Important: note that some VM globals aren't part of the scheme described and defined below. In particular, the try-catch-throw exception handling subsystem doesn't use it; the dynamic compiler doesn't use it; read-only statics don't use it; and some global caches don't use it. The exception subsystem opts out because its globals constitute a critical performance bottleneck, plus they need to be per-thread for multi-threaded builds; so they use native thread-local storage if threads are used, otherwise they're just ordinary globals. The dynamic compiler doesn't use the VM globals scheme because the compiler wasn't originally designed to be used within the VM at all; by the time we adapted it, it was too late and would have required too much work to bring it into the scheme. Read-only statics can be excluded because they can be safely shared among threads and/or instances without any extra work. And caches can safely opt out as long as the information cached is truly global, not private to a VM instance; but note that caches might need to use per-thread storage, or else mutexes, if they can be accessed from multiple threads. The globals can be configured in four different ways, depending on how the T3 VM is to be used: 1. As individual external variables. This is the most efficient way to configure the globals, but this can't be used when multiple independent VM instances are needed in a single process, because each VM would need its own copy of the globals. This is the fastest way to lay out the globals, because it allows the compiler to generate code that accesses internal members of performance-critical global objects directly, without any pointer dereferences. 2. As members of a static global structure. This is the second most efficient way to configure the globals, but this can't be used when multiple independent VM instances are needed, because each VM would need its own copy of the globals, which isn't possible in this configuration. (Note that accessing members of a global static structure is identical to accessing global variables, since the compiler can compute the relative address of a structure member at compile time and hence does not need to perform any run-time pointer arithmetic or any other computation beyond what it would normally use for global variable access. However, in this configuration, it's not possible to allocate any of the global objects in-line within the master global structure, so each global object's internal members must be accessed through one pointer dereference.) 3. As members of an allocated structure whose address is contained in a global static variable. This is slightly less efficient than method 1, in that the global pointer must be dereferenced on each global variable access. This method allows multiple VM instances to be used, as long as the host application specifically stores the correct structure pointer in the global static variable before each call to the VM; this can be used when the host system uses the VM in only one thread. 4. As members of an allocated structure whose address is passed as a parameter to each function that needs global variable access. As with method 2, the host application creates a structure per VM instance it requires, and then passes a pointer to the correct structure on each call to a VM function; the VM internally passes this pointer in its own function calls where needed. This is the most flexible method, in that the VM can be used in multiple thread simultaneously (as long as each thread has a separate VM global structure instance), but is the least efficient, because the structure pointer must be passed around on all calls. To select a configuration, define one of the following preprocessor symbols in the makefile: VMGLOB_VARS - method 1 - individual global variables VMGLOB_STRUCT - method 2 - global static structure VMGLOB_POINTER - method 3 - global static pointer to allocated structure VMGLOB_PARAM - method 4 - pointer to structure passed as parameter We provide all of these different possible configurations because of the trade-offs involved in selecting one. The host application's needs should be determined, and the most efficient configuration that meets those needs should be chosen. Notes Modified 11/28/98 MJRoberts - Creation */ #ifndef VMGLOB_H #define VMGLOB_H #include #include "t3std.h" #include "vmpoolsl.h" /* ------------------------------------------------------------------------ */ /* * HOST SYSTEM GLOBAL INITIALIZATION * * The host system should declare a local variable as follows: * * vm_globals *vmg__; // two underscores * * The program must call vmglob_alloc() each time it wants to initialize a * global variable context. This routine must always be called at least * once. (In the VMGLOB_STRUCT or VMGLOB_VARS configuration, this routine * has no effect, but should still be called so that the code will work * unchanged in other configurations.) Calling this routine will overwrite * the current static global variable pointer in the VMGLOB_POINTER * configuration, so the caller must take care to keep track of each set of * globals it allocates for later restoration. * * When calling vmglob_alloc(), assign the return value to vmg__. */ /* * HOST SYSTEM GLOBAL TERMINATION * * The caller should invoke vmglob_delete() for each set of globals it * allocated. */ /* * HOST SYSTEM VM FUNCTION CALLS * * In each call to a VM function, the host system should include the * macro "vmg_" (one underscore) at the start of each parameter list; do * not put a comma after vmg_. Call functions that take no other * parameters with vmg0_ instead of vmg_. */ /* ------------------------------------------------------------------------ */ /* * INTERNAL FUNCTION DECLARATION AND CALLING * * Every function that requires global access, or which calls a function * that requires global access, must put "VMG_" at the very start of its * formal parameters declaration. Do not put a comma after VMG_, but * simply list the first parameter. If the function has no parameters * at all, use VMG0_ instead of VMG_. For example: * * void CVmClass::func1(VMG_ const textchar_t *str, size_t len) { ... } *. void CVmClass::func2(VMG0_) { ... } * * In each call to a function that uses global variables or calls * functions that use global variables, put "vmg_" at the very start of * the actual parmaeters list in the call; do not put a comma after the * vmg_. If the function takes no parameters at all, use vmg0_ instead * of vmg_. * * func1(vmg_ "test", 4); *. func2(vmg0_); */ /* ------------------------------------------------------------------------ */ /* * Functions that themselves can't participate in the prototype * mechanism, because they present an external API to other subsystems, * must keep track of the globals on their own. To do this, they must * stash the global pointer in their own context structure - use * VMGLOB_ADDR to get the address of the global structure. When passing * this stashed pointer back to function calls, use vmg_var(x) (for the * first argument to a function with multiple arguments) or vmg_var0(x) * (for a single-argument call), as appropriate, where x is the local * copy. * * To access globals from this type of routine, put the VMGLOB_PTR(x) * macro in with the local variable decalarations for the function, * passing as the argument the stashed address of the global structure. * This will set things up so that the G_xxx variables are accessible * through a local pointer, when necessary, and will also make the vmg_ * and vmg0_ argument macros work properly. See the examples below. */ #if 0 /* EXAMPLES ONLY - THIS CODE IS FOR DOCUMENTATION ONLY */ struct my_context_def { /* holder for VM globals structure pointer */ vm_globals *vmg; }; int func_calling_external_interfaces(VMG_ int x, int y, int z) { my_context_def ctx; /* stash the address of the VM globals in my private context */ ctx.vmg = VMGLOB_ADDR; /* call my external API function */ external_api_func(&ctx); } int func_called_from_external_interface(my_context_def *ctx, int x, int y) { /* set up access to the VM globals through my stashed context */ VMGLOB_PTR(ctx->vmg); /* access a global variable */ G_myvar->do_something(); /* call a function using a VMG_ prototype */ G_myvar->do_something_else(vmg_ x, y); } #endif /* 0 */ /* ------------------------------------------------------------------------ */ /* * Conditional access to globals. * * Some code that accesses global variables might need to be able to run * during startup or shutdown. On platforms where the globals are * dynamically allocated, such code might need to test to see not only * whether or not particular global variable has been allocated, but also * whether or not the memory containing the global variables themselves * exists. E.g., if you want to access the console object in code that * might be called during early startup or late termination, it's not good * enough to test if G_console constains a non-null object pointer, since * merely accessing G_console itself will dereference the global variable * structure pointer, and this pointer might be null on platforms where the * globals are allocated. * * For such situations, use VMGLOB_IF_AVAIL(varname) to cover the global * variable name. On platforms where the globals are allocated, this will * return null if the globals themselves aren't yet allocated or have * already been freed, otherwise it'll return the variable's value. */ #if 0 /* EXAMPLE ONLY - THIS CODE IS FOR DOCUMENTATION PURPOSES */ CVmConsole *con = VMGLOB_IF_AVAIL(G_console); #endif /* ------------------------------------------------------------------------ */ /* * Set up to define the global variables. For the POINTER and PARAM * configurations, put pointers to the global structures in a structure. * For the static STRUCT configuration, actually allocate all of the * objects statically. * * Use VM_GLOBAL_OBJDEF() to define the entry for an object (struct or * class) type. The global variable will be a pointer to this type. * * Use VM_GLOBAL_PREOBJDEF to define the entry for an object type that's to * be defined as a pre-allocated static object in the VARS configuration. * This can be used for objects that (1) have very frequent access and thus * should have their fields reachable directly as statics rather than via * static pointers, and (2) need no constructor parameters and thus can be * pre-allocated at compile-time. In the VARS configuration, these * variables can be allocated at compile-time, which allows the compiler to * generate code that accesses the objects' internal members without any * pointer dereferences. * * Any variable defined with VM_GLOBAL_PREOBJDEF MUST have its accessor * defined through VMGLOB_PREACCESS() rather than VMGLOB_ACCESS(). * * Use VM_GLOBAL_VARDEF to define a scalar variable. */ #if defined(VMGLOB_POINTER) || defined(VMGLOB_PARAM) || defined(VMGLOB_STRUCT) #define VM_GLOBALS_BEGIN struct vm_globals { #define VM_GLOBAL_OBJDEF(typ, var) typ *var; #define VM_GLOBAL_PREOBJDEF(typ, var) typ *var; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) typ *var; #define VM_GLOBAL_VARDEF(typ, var) typ var; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) typ var[eles]; #define VM_GLOBALS_END }; /* * we do allocate all global objects, including external objects; hence * external globals are non-static */ #define VM_IF_ALLOC_PRE_GLOBAL(x) x #define VM_IFELSE_ALLOC_PRE_GLOBAL(x, y) x #define VM_PRE_GLOBALS_ARE_STATIC FALSE #endif #if defined(VMGLOB_VARS) #define VM_GLOBALS_BEGIN #define VM_GLOBAL_OBJDEF(typ, var) extern typ *G_##var##_X; #define VM_GLOBAL_PREOBJDEF(typ, var) extern typ G_##var##_X; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) extern typ G_##var##_X; #define VM_GLOBAL_VARDEF(typ, var) extern typ G_##var##_X; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) extern typ G_##var##_X[eles]; #define VM_GLOBALS_END /* we don't actually need a structure for the globals; use a dummy */ struct vm_globals { int x; }; /* external global objects are statically allocated */ #define VM_IF_ALLOC_PRE_GLOBAL(x) #define VM_IFELSE_ALLOC_PRE_GLOBAL(x, y) y #define VM_PRE_GLOBALS_ARE_STATIC TRUE #endif /* define the globals */ #include "vmglobv.h" /* ------------------------------------------------------------------------ */ /* * If we're not including from the global-defining source file, merely * declare the globals. */ #ifndef VMGLOB_DECLARE #define VMGLOB_DECLARE extern #endif /* ------------------------------------------------------------------------ */ /* * INDIVIDUAL STATIC GLOBAL VARIABLES configuration. In this * configuration, the globals are defined as individual global variables. * This is the most efficient configuration, but it only allows one VM * instance in a given process. */ #ifdef VMGLOB_VARS /* initialization - this has no effect in this mode */ inline vm_globals *vmglob_alloc() { return 0; } /* termination - this has no effect in this mode */ inline void vmglob_delete(vm_globals *) { } /* * we don't require anything for the parameter declaration or usage, since * we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* "vmg0_" in parens, for constructor arguments */ /* * get the address of the globals - this doesn't do anything in this * configuration, as there's not really a global variables structure */ #define VMGLOB_ADDR 0 /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* global variables are statically declared so they're always available */ #define VMGLOB_IF_AVAIL(x) x /* we access globals directly as individual statics */ #define VMGLOB_ACCESS(var) (G_##var##_X) #define VMGLOB_PREACCESS(var) (&G_##var##_X) #endif /* VMGLOB_VARS */ /* ------------------------------------------------------------------------ */ /* * STATIC GLOBAL STRUCTURE configuration. In this configuration, the * globals are defined in a single static global structure. This is the * second most efficient configuration, but it only allows one VM instance * per process. */ #ifdef VMGLOB_STRUCT /* define the global variables structure */ VMGLOB_DECLARE vm_globals G_vmglobals; /* initialization - this has no effect in this mode */ inline vm_globals *vmglob_alloc() { return &G_vmglobals; } /* termination - this has no effect in this mode */ inline void vmglob_delete(vm_globals *) { } /* * we don't require anything for the parameter declaration or usage, * since we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* get the address of the globals */ #define VMGLOB_ADDR (&G_vmglobals) /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* global variables are statically declared so they're always available */ #define VMGLOB_IF_AVAIL(x) x /* we access globals directly as individual statics */ #define VMGLOB_ACCESS(var) (G_vmglobals.var) #define VMGLOB_PREACCESS(var) (G_vmglobals.var) #endif /* VMGLOB_STRUCT */ /* ------------------------------------------------------------------------ */ /* * STATIC GLOBAL POINTER configuration. In this configuration, the * globals are stored in an allocated structure whose address is stored * in a global static pointer variable. */ #ifdef VMGLOB_POINTER /* define our global static pointer to the global variables */ VMGLOB_DECLARE vm_globals *G_vmglobals; /* initialization - allocate a new set of globals */ inline vm_globals *vmglob_alloc() { G_vmglobals = new vm_globals(); return G_vmglobals; } /* termination - delete a set of globals */ inline void vmglob_delete(vm_globals *glob) { delete glob; } /* * we don't require anything for the parameter declaration or usage, * since we don't use the local parameter mechanism at all */ #define VMG_ #define VMG0_ #define vmg_ #define vmg0_ #define VMGNULL_ #define VMG0NULL_ #define Pvmg0_P /* get the address of the globals */ #define VMGLOB_ADDR G_vmglobals /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) #define vmg_var0(x) /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) /* test to see if a global variable is available */ #define VMGLOB_IF_AVAIL(x) (G_vmglobals != 0 ? x : 0) /* accessing a global requires dereferencing the global pointer */ #define VMGLOB_ACCESS(var) (G_vmglobals->var) #define VMGLOB_PREACCESS(var) (G_vmglobals_var) #endif /* VMGLOB_POINTER */ /* ------------------------------------------------------------------------ */ /* * PARAMETER configuration. In this configuration, the globals are * stored in an allocated structure whose address is passed to each VM * function as a parameter. */ #ifdef VMGLOB_PARAM /* initialization - allocate a new set of globals */ inline vm_globals *vmglob_alloc() { return new vm_globals(); } /* termination - delete a set of globals */ inline void vmglob_delete(vm_globals *glob) { delete glob; } /* function declaration for the global pointer parameter */ #define VMG_ vm_globals *vmg__, #define VMG0_ vm_globals *vmg__ /* parameter reference for passing to a function */ #define vmg_ vmg__, #define vmg0_ vmg__ #define VMGNULL_ ((vm_globals *)NULL), #define VMG0NULL_ ((vm_globals *)NULL) #define Pvmg0_P (vmg__) /* get the address of the globals */ #define VMGLOB_ADDR vmg__ /* pass a stashed copy of the global pointer, if necessary */ #define vmg_var(x) x, #define vmg_var0(x) x /* declare a local variable to access the globals */ #define VMGLOB_PTR(x) vm_globals *vmg__ = x /* test to see if a global variable is available */ #define VMGLOB_IF_AVAIL(x) (vmg__ != 0 ? x : 0) /* accessing a global variable requires dereferencing the parameter */ #define VMGLOB_ACCESS(var) (vmg__->var) #define VMGLOB_PREACCESS(var) (vmg__->var) #endif /* VMGLOB_PARAM */ /* ------------------------------------------------------------------------ */ /* * Global variable accessors. For convenience, we define these cover * macros that access the global variables in the appropriate manner for * our configuration. Code can use these G_xxx symbols syntactically as * though they were normal global variables. */ #define G_mem VMGLOB_ACCESS(mem) #define G_undo VMGLOB_ACCESS(undo) #define G_meta_table VMGLOB_ACCESS(meta_table) #define G_bif_table VMGLOB_ACCESS(bif_table) #define G_varheap VMGLOB_ACCESS(varheap) #define G_preinit_mode VMGLOB_ACCESS(preinit_mode) #define G_bif_tads_globals VMGLOB_ACCESS(bif_tads_globals) #define G_host_ifc VMGLOB_ACCESS(host_ifc) #define G_image_loader VMGLOB_ACCESS(image_loader) #define G_disp_cset_name VMGLOB_ACCESS(disp_cset_name) #define G_cmap_from_fname VMGLOB_ACCESS(cmap_from_fname) #define G_cmap_to_fname VMGLOB_ACCESS(cmap_to_fname) #define G_cmap_from_ui VMGLOB_ACCESS(cmap_from_ui) #define G_cmap_to_ui VMGLOB_ACCESS(cmap_to_ui) #define G_cmap_from_file VMGLOB_ACCESS(cmap_from_file) #define G_cmap_to_file VMGLOB_ACCESS(cmap_to_file) #define G_cmap_to_log VMGLOB_ACCESS(cmap_to_log) #define G_console VMGLOB_ACCESS(console) #define G_exc_entry_size VMGLOB_ACCESS(exc_entry_size) #define G_line_entry_size VMGLOB_ACCESS(line_entry_size) #define G_dbg_hdr_size VMGLOB_ACCESS(dbg_hdr_size) #define G_srcf_table VMGLOB_ACCESS(srcf_table) #define G_dbg_lclsym_hdr_size VMGLOB_ACCESS(dbg_lclsym_hdr_size) #define G_dbg_fmt_vsn VMGLOB_ACCESS(dbg_fmt_vsn) #define G_dbg_frame_size VMGLOB_ACCESS(dbg_frame_size) #define G_dyncomp VMGLOB_ACCESS(dyncomp) #define G_net_queue VMGLOB_ACCESS(net_queue) #define G_net_config VMGLOB_ACCESS(net_config) #define G_iter_get_next VMGLOB_ACCESS(iter_get_next) #define G_iter_next_avail VMGLOB_ACCESS(iter_next_avail) #define G_tadsobj_queue VMGLOB_PREACCESS(tadsobj_queue) #define G_predef VMGLOB_PREACCESS(predef) #define G_stk G_interpreter #define G_interpreter VMGLOB_PREACCESS(interpreter) #define G_const_pool VMGLOB_PREACCESS(const_pool) #define G_code_pool VMGLOB_PREACCESS(code_pool) #define G_obj_table VMGLOB_PREACCESS(obj_table) #define G_syslogfile VMGLOB_ACCESS(syslogfile) #define G_file_path VMGLOB_ACCESS(file_path) #define G_sandbox_path VMGLOB_ACCESS(sandbox_path) #define G_tzcache VMGLOB_ACCESS(tzcache) #define G_debugger VMGLOB_ACCESS(debugger) #endif /* VMGLOB_H */ frobtads-1.2.3/tads3/vmsortv.cpp0000644000175000001440000000363511714725642015766 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsortv.cpp - CVmSortVal implementation Function Notes Modified 05/14/00 MJRoberts - Creation */ #include #include "t3std.h" #include "vmglob.h" #include "vmsort.h" #include "vmstack.h" #include "vmrun.h" #include "vmerr.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * compare two vm_val_t values */ int CVmQSortVal::compare(VMG_ size_t a, size_t b) { int result; vm_val_t val_a; vm_val_t val_b; /* get the two values */ get_ele(vmg_ a, &val_a); get_ele(vmg_ b, &val_b); /* check for an explicit comparison function */ if (compare_fn_.typ != VM_NIL) { vm_val_t val; /* push the values (in reverse order) */ G_stk->push(&val_b); G_stk->push(&val_a); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ &compare_fn_, 2, &rc, 0); /* get the result */ val = *G_interpreter->get_r0(); /* get the result value */ result = val.num_to_int(vmg0_); } else { /* compare the values */ result = val_a.compare_to(vmg_ &val_b); } /* if we're sorting in descending order, reverse the result */ if (descending_) result = -result; /* return the result */ return result; } /* * exchange two vm_val_t elements */ void CVmQSortVal::exchange(VMG_ size_t a, size_t b) { vm_val_t val_a; vm_val_t val_b; /* get the two elements */ get_ele(vmg_ a, &val_a); get_ele(vmg_ b, &val_b); /* store the two elements, swapping the positions */ set_ele(vmg_ b, &val_a); set_ele(vmg_ a, &val_b); } frobtads-1.2.3/tads3/vmconsol.h0000644000175000001440000021774312145504453015554 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconsol.h - TADS 3 console input reader and output formatter Function Provides console input and output for the TADS 3 built-in function set for the T3 VM. T3 uses the UTF-8 character set to represent character strings. The OS functions use the local character set. We perform the mapping between UTF-8 and the local character set within this module, so that OS routines see local characters only, not UTF-8. This code is based on the TADS 2 output formatter, but has been substantially reworked for C++, Unicode, and the slightly different TADS 3 formatting model. Notes Modified 09/04/99 MJRoberts - Creation */ #ifndef VMCONSOL_H #define VMCONSOL_H #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "vmtype.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Synthetic console events. These are events we generate alongside * OS_EVT_xxx codes; we start these at 10000 to ensure that we don't * overlap any current or future OS_EVT_xxx event codes. */ #define VMCON_EVT_END_QUIET_SCRIPT 10000 /* end quiet script playback */ #define VMCON_EVT_DIALOG 10001 /* result from dialog */ #define VMCON_EVT_FILE 10002 /* result from file dialog */ /* * Event attributes. Some scriptable events have extra attributes * associated with them in addition to the main payload. These attributes * take the syntactic form of HTML tag attributes, so in principal we could * have arbitrarily many attributes, each of which have arbitrary string * values. However, at the moment, we only have a small number of * attributes, and each attribute's value is merely a boolean, so it's * adequate to represent these in the call interface with bit flags. */ /* OVERWRITE, as in */ #define VMCON_EVTATTR_OVERWRITE 0x00000001 /* ------------------------------------------------------------------------ */ /* * Newline type codes. These specify how we are to perform line * breaking after writing out text. */ enum vm_nl_type { /* * no line separation at all - write out this text and subsequent * text as part of the same line with no separators */ VM_NL_NONE, /* * flushing in preparation for input - don't show any line separation, * and make sure that we display everything in the buffer, including * trailing spaces */ VM_NL_INPUT, /* break the line at the end of this text and start a newline */ VM_NL_NEWLINE, /* OS line separation - add a space after the text */ VM_NL_OSNEWLINE, /* * flushing internal buffers only: no line separation, and do not * flush to underlying OS level yet */ VM_NL_NONE_INTERNAL }; /* ------------------------------------------------------------------------ */ /* * Handle manager. This is a simple class for mapping system objects to * integers, which we give as "handles" to byte code callers. We give only * the integer handles to byte code to ensure that handles given back to us * by the byte code are valid; if we handed back raw pointers to the byte * code, it could call us with random garbage, and we'd have no way to * protect against it. */ class CVmHandleManager { public: CVmHandleManager(); /* * delete the object (use this rather than calling the destructor * directly, since we need to call some virtuals in the course of * preparing for deletion */ void delete_obj(VMG0_); protected: /* delete */ virtual ~CVmHandleManager(); /* allocate a slot for a new item */ int alloc_handle(void *item); /* clear the given handle's slot */ void clear_handle(int handle) { /* if the handle is valid, clear its associated slot */ if (is_valid_handle(handle)) handles_[handle - 1] = 0; } /* is the given handle valid? */ int is_valid_handle(int handle) { /* * it's valid if it's within range for the allocated slots, and * there's a non-null object in the handle's slot */ return (handle >= 1 && (size_t)handle <= handles_max_ && handles_[handle - 1] != 0); } /* get the object for the given handle */ void *get_object(int handle) { /* * If the handle is valid, get the item from the slot; the handle * is indexed from 1, so decrement it to get the array index of the * object for the handle. If the handle is invalid, return null. */ return is_valid_handle(handle) ? handles_[handle - 1] : 0; } /* * Delete the item in a slot, in preparation for destroying the handle * manager itself - each subclass must override this to do the * appropriate work on termination. */ virtual void delete_handle_object(VMG_ int handle, void *obj) = 0; /* array of window banners */ void **handles_; size_t handles_max_; }; /* ------------------------------------------------------------------------ */ /* * Banner manager. This keeps track of banner windows outside of the main * console window. */ class CVmBannerManager: public CVmHandleManager { public: CVmBannerManager() { } /* * Create a banner window. This creates an OS-level banner window, and * creates a console object to format its output. Returns a banner * handle that can be used to refer to the window. Banner handle zero * is invalid and indicates failure. * * 'parent_id' is the banner ID of the parent of the new banner. The * new banner is created as a child of the given parent. If parent_id * is zero, then the new banner is created as a child of the main * window. The parent determines how the new window is laid out: the * new window's display area is carved out of the display area of the * parent. * * 'where' is OS_BANNER_FIRST, OS_BANNER_LAST, OS_BANNER_BEFORE, or * OS_BANNER_AFTER. 'other_id' is the banner ID of an existing child * of the given parent, for the relative insertion point; this is * ignored for OS_BANNER_FIRST and OS_BANNER_LAST. * * 'wintype' is an OS_BANNER_TYPE_xxx code giving the type of window to * be created. * * 'siz' is the size, in units specified by 'siz_units', an * OS_BANNER_SIZE_xxx value. * * 'style' is a combination of OS_BANNER_STYLE_xxx flags. */ int create_banner(VMG_ int parent_id, int where, int other_id, int wintype, int align, int siz, int siz_units, unsigned long style); /* delete a banner */ void delete_banner(VMG_ int banner) { delete_or_orphan_banner(vmg_ banner, FALSE); } /* * Get the OS-level handle for the banner - this handle can be used to * call the os_banner_xxx functions directly. */ void *get_os_handle(int banner); /* get the banner's console object */ class CVmConsoleBanner *get_console(int banner) { /* the object behind the handle is the console */ return (CVmConsoleBanner *)get_object(banner); } /* flush all banners */ void flush_all(VMG_ vm_nl_type nl); protected: /* delete the object in a slot, in preparation for deleting the manager */ virtual void delete_handle_object(VMG_ int handle, void *) { /* * delete the banner object, but orphan the system-level banner - * this will allow the system-level banner to remain visible even * after VM termination, in case the host application continues * running even after the VM exits */ delete_or_orphan_banner(vmg_ handle, TRUE); } /* delete or orphan a banner window */ void delete_or_orphan_banner(VMG_ int banner, int orphan); }; /* ------------------------------------------------------------------------ */ /* * Log console manager. This keeps track of log consoles, which are * consoles created specifically for capturing text to a log file. */ class CVmLogConsoleManager: public CVmHandleManager { public: CVmLogConsoleManager() { } /* create a log console - returns the console handle */ int create_log_console(VMG_ class CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width); /* delete a log console */ void delete_log_console(VMG_ int handle); /* get the log's console object */ class CVmConsoleLog *get_console(int banner) { /* the object behind the handle is the console */ return (CVmConsoleLog *)get_object(banner); } protected: /* delete the object associated with a handle */ virtual void delete_handle_object(VMG_ int handle, void *) { /* delete the console */ delete_log_console(vmg_ handle); } }; /* * Log console manager item. We use these to track individual log * consoles. */ class CVmLogConsoleItem { public: CVmLogConsoleItem(const char *fname, class CCharmapToLocal *cmap); ~CVmLogConsoleItem(); /* get the console */ class CVmConsoleLog *get_console() const { return console_; } protected: /* my console object */ class CVmConsoleLog *console_; }; /* ------------------------------------------------------------------------ */ /* * Script stack entry */ struct script_stack_entry { script_stack_entry(VMG_ script_stack_entry *encp, int old_more, class CVmNetFile *netfile, osfildef *outfp, const vm_val_t *filespec, int new_more, int is_quiet, int is_event_script); void delobj(VMG0_); /* the enclosing stack level */ script_stack_entry *enc; /* network file descriptor for our script file */ class CVmNetFile *netfile; /* the script file at this level */ osfildef *fp; /* the MORE mode that was in effect before this script file */ int old_more_mode; /* the MORE mode in effect during this script */ int more_mode; /* are we reading quietly from this script? */ int quiet; /* is this an event script? */ int event_script; /* VM global variable, for gc protection for our file spec */ struct vm_globalvar_t *filespec; private: ~script_stack_entry() { } }; /* ------------------------------------------------------------------------ */ /* * Console. A console corresponds to device that shows information to * and reads text from the user. On a text system, the console is * simply the terminal. On a graphical system, the console is usually * an application window. */ class CVmConsole { public: CVmConsole(); virtual void delete_obj(VMG0_); /* write out a null-terminated UTF-8 string */ int format_text(VMG_ const char *p) { /* get its length and write it out */ return format_text(vmg_ p, strlen(p)); } /* write out a UTF-8 string of a given byte length */ virtual int format_text(VMG_ const char *p, size_t len); /* format text explicitly to the log file, if any */ int format_text_to_log(VMG_ const char *p, size_t len); /* display a blank line */ void write_blank_line(VMG0_); /* set the whitespace mode (returns the old whitespace mode) */ int set_obey_whitespace(int f); /* set the text color */ void set_text_color(VMG_ os_color_t fg, os_color_t bg); /* set the body color */ void set_body_color(VMG_ os_color_t color); /* set the caps flag - capitalize the next character */ void caps(); /* set the nocaps flag - make the next letter miniscule */ void nocaps(); /* flush the output with the given newline type */ void flush(VMG_ vm_nl_type nl); /* flush the output, ending the current line and starting a new line */ void flush(VMG0_) { flush(vmg_ VM_NL_NEWLINE); } /* empty our buffers */ void empty_buffers(VMG0_); /* clear the window */ virtual void clear_window(VMG0_) = 0; /* * Flush all windows we control. By default, we just flush our own * window; consoles that manage multiple windows should flush their * managed windows here as well. */ virtual void flush_all(VMG_ vm_nl_type nl) { flush(vmg_ nl); } /* immediately update the display window */ void update_display(VMG0_); /* * Open a new log file. Closes any previous log file. If the file * already exists, we'll overwrite it with the new log information, * otherwise we'll create a new file. Returns zero on success, * non-zero on failure. */ int open_log_file(VMG_ const char *fname); int open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc); /* * close any existing log file - returns zero on success, non-zero * on failure */ int close_log_file(VMG0_); /* * Open a new command log file. We'll log commands (and only * commands) to the command log file. Returns zero on success, * non-zero on failure. */ int open_command_log(VMG_ const char *fname, int event_script); int open_command_log(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int event_script); /* close the command log file, if there is one */ int close_command_log(VMG0_); /* * Set the current MORE mode. Returns the old state. The state is * true if we show MORE prompts, false if not. The state will be * false if the underlying OS display layer handles prompting, so a * return of false doesn't necessarily mean that MORE prompts are * never shown, but merely that we don't handle MORE prompts in the * output formatter itself. */ virtual int set_more_state(int state) = 0; /* determine if we're in MORE mode */ virtual int is_more_mode() const = 0; /* get the line width of the display device */ virtual int get_line_width() const = 0; /* * Do we allow overrunning the line width when we can't find a natural * breaking point (at a whitespace character, for example) such that * we can fit some text within the line width? * * If this returns false, then we'll force a newline when we reach the * line width, even if doing so breaks up a single word that doesn't * have a natural breaking point within. */ virtual int allow_overrun() const = 0; /* get the page length of the display device */ virtual int get_page_length() const = 0; /* get/set the double-space flag (for periods and other punctuation) */ int get_doublespace() const { return doublespace_; } void set_doublespace(int f) { doublespace_ = f; } /* reset the MORE prompt line count */ void reset_line_count(int clearing); /* * check to see if we're reading from a script input file - returns * true if so, false if reading from the user (via the keyboard or * other input device) */ int is_reading_script() const { return (script_sp_ != 0); } /* * check to see if we're reading quietly from a script - if we're * reading from a script, and this flag is true, we suppress all * output */ int is_quiet_script() const { return (script_sp_ != 0 && script_sp_->quiet); } /* is the script in MORE mode? */ int is_moremode_script() const { return (script_sp_ != 0 && script_sp_->more_mode); } /* is the script an type? */ int is_event_script() const { return (script_sp_ != 0 && script_sp_->event_script); } /* * Open a script file. If 'quiet' is true, no output is displayed * while the script is being processed. If 'script_more_mode' is true, * MORE mode is in effect while processing the script, otherwise MORE * mode is turned off while processing the script (to leave things as * they are, simply pass in is_more_mode() for this argument). * * Returns 0 on success, non-zero on error. */ int open_script_file(VMG_ const char *fname, int quiet, int script_more_mode); int open_script_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int quiet, int script_more_mode); /* * Close the script file. Returns the original MORE mode that was * in effect before the script file was opened; this MORE mode * should be restored. */ int close_script_file(VMG0_); /* * Read a line of text from the keyboard. Fills in the buffer with * a null-terminated UTF-8 string. Returns zero on success, * non-zero on end-of-file reading the console (which usually * indicates that the user has closed the application, so we're in * the process of terminating; it might also indicate that the * user's terminal has been detached, in which case we also probably * can't do much except terminate). */ int read_line(VMG_ char *buf, size_t buflen, int bypass_script = FALSE); /* * Read a line of input with optional timeout. Fills in the buffer * with a null-terminated UTF-8 string. Returns an OS_EVT_xxx code, * according to how the input was terminated: * * OS_EVT_LINE - the user pressed Return to enter the text *. OS_EVT_TIMEOUT - the timeout expired before the user pressed Return *. OS_EVT_EOF - an error occurred reading the input * * This routine is a cover for the low-level os_gets_timeout(), and * behaves essentially the same way. Note in particular that if this * routine returns OS_EVT_TIMEOUT, then our read_line_cancel() routine * must be called before any output or other display changes can be * made, with the exception that another call to read_line_timeout() * is always allowed. */ int read_line_timeout(VMG_ char *buf, size_t buflen, unsigned long timeout, int use_timeout, int bypass_script = FALSE); /* * Cancel a line of input in progress, which was interrupted by a * timeout in read_line_timeout(). If 'reset' is true, we'll forget * any editing state from the prior line. */ void read_line_cancel(VMG_ int reset); /* * Display a file dialog. This routine works exactly the same way * as os_askfile(), but is implemented here to allow for a formatted * text interface on systems where no dialog is available. */ int askfile(VMG_ const char *prompt, size_t prompt_len, char *reply, size_t replen, int dialog_type, os_filetype_t file_type, int bypass_script = FALSE); /* * Display a system dialog. This routine works exactly the same way * as os_input_dialog(), but is implemented here to allow for a * formatted text interface on systems where no dialog is available. */ int input_dialog(VMG_ int icon_id, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index, int bypass_script = FALSE); /* show the MORE prompt and wait for the user to acknowledge it */ virtual void show_more_prompt(VMG0_) = 0; /* * Log an event. This saves the event to the current script log, if * there is one, in the proper format for the script. We return the * event code. * * 'evt' is the event type, as an OS_EVT_xxx or VMCON_EVT_xxx code. * * 'param' can be given in the local UI character set or in UTF-8 - * specify which it is via 'param_is_utf8'. We write the file in the * local UI character set, so if the parameter is given in UTF-8, we * have to translate it. */ int log_event(VMG_ int evt, const char *param, size_t paramlen, int param_is_utf8); int log_event(VMG_ int evt) { return log_event(vmg_ evt, 0, 0, FALSE); } /* log an event with the given event type tag */ void log_event(VMG_ const char *tag, const char *param, size_t paramlen, int param_is_utf8); /* read an event from an event script */ int read_event_script(VMG_ int *evt, char *buf, size_t buflen, const int *filter, int filter_cnt, unsigned long *attrs); protected: /* the destructor is protected - use delete_obj() to delete */ virtual ~CVmConsole() { } /* * Service routine - show MORE prompt on this console. This can be * called from show_more_prompt() when a MORE prompt is desired at all * in the subclassed console. */ void show_con_more_prompt(VMG0_); /* read a line from the script file */ int read_line_from_script(char *buf, size_t buflen, int *evt); /* read the type tag from the next script event */ int read_script_event_type(int *evt, unsigned long *attrs); /* skip to the next line of the script */ void skip_script_line(osfildef *fp); /* * read a script parameter - this reads the rest of the line into the * given buffer, and skips to the start of the next line in the script; * returns true on success, false if we reach EOF before reading * anything */ int read_script_param(char *buf, size_t buflen, osfildef *fp); /* internal routine to terminate line reading */ void read_line_done(VMG0_); /* write utf-8 text to a file, mapping to the given file character set */ void write_to_file(osfildef *fp, const char *txt, class CCharmapToLocal *map); /* open a comand log file - internal version */ int open_command_log(VMG_ class CVmNetFile *netfile, int event_script); /* open a script file - internal version */ int open_script_file(VMG_ class CVmNetFile *netfile, const vm_val_t *filspec, int quiet, int script_more_mode); /* our current display stream */ class CVmFormatter *disp_str_; /* our log stream - this stream is written to the log file, if any */ class CVmFormatterLog *log_str_; /* * Flag: the log stream is enabled. We can temporarily disable the * log stream, such as when writing to the statusline stream. */ unsigned int log_enabled_ : 1; /* * Flag: display two spaces after a period-like punctuation mark. * This should be true if the output should have two spaces after a * period, question mark, or exclamation point, false for a single * space. This should generally be true for fixed-width fonts, * false for proportional fonts, although some users might prefer to * use single-spacing even for fixed-width fonts. */ unsigned int doublespace_ : 1; /* * flag: the command log is an event script; if this is set, we log all * input events in the tagged file format, otherwise we * log just command lines in the old-style ">line" format */ unsigned int command_eventscript_ : 1; /* * Script-input stack. Each time we open a script, we create a new * stack entry object and link it at the head of the list. So, the * head of the list is the current state, the next element is the * enclosing state, and so on. */ script_stack_entry *script_sp_; /* command log file, if there is one */ class CVmDataSource *command_fp_; class CVmNetFile *command_nf_; struct vm_globalvar_t *command_glob_; }; /* ------------------------------------------------------------------------ */ /* * Main system console. This console is attached to the OS-level primary * console. */ class CVmConsoleMain: public CVmConsole { public: CVmConsoleMain(VMG0_); /* delete */ void delete_obj(VMG0_); /* get the system banner manager */ class CVmBannerManager *get_banner_manager() const { return banner_manager_; } /* get the system log console manager */ class CVmLogConsoleManager *get_log_console_manager() const { return log_console_manager_; } /* * Switch in or out of statusline mode. When we're running on the text * implementation of the OS layer, we must explicitly switch modes * between the main text stream and statusline stream. 'mode' is true * to switch to statusline mode, false to switch back to main text * mode. */ void set_statusline_mode(VMG_ int mode); /* clear the window */ virtual void clear_window(VMG0_); /* set MORE mode */ virtual int set_more_state(int state) { int old_state; /* remember the old state */ old_state = G_os_moremode; /* set the new mode */ G_os_moremode = state; /* return the previous state */ return old_state; } /* get the MORE mode */ virtual int is_more_mode() const { return G_os_moremode; } /* * Flush everything - this flushes not only the main console, but any * banner windows we're managing. This should be called before pausing * for input or for a timed delay, to make sure that buffered output in * all windows is shown. */ void flush_all(VMG_ vm_nl_type nl); /* get the line width of the display device */ virtual int get_line_width() const { return G_os_linewidth; } /* do not allow overrunning the line width on the main console */ virtual int allow_overrun() const { return FALSE; } /* get the page length of the display device */ virtual int get_page_length() const { return G_os_pagelength; } /* show the MORE prompt */ virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); } protected: /* main text area display stream */ class CVmFormatterMain *main_disp_str_; /* statusline display stream */ class CVmFormatterStatline *statline_str_; /* * The system banner window manager. Since the main console is * inherently a singleton (as there's only one OS-level primary * console), we keep track of the banner manager. */ class CVmBannerManager *banner_manager_; /* the system log console manager */ class CVmLogConsoleManager *log_console_manager_; }; /* ------------------------------------------------------------------------ */ /* * Banner-window console. */ class CVmConsoleBanner: public CVmConsole { public: /* create */ CVmConsoleBanner(void *banner_handle, int win_type, unsigned long style); /* delete */ void delete_obj(VMG0_); /* retrieve our OS-level banner handle */ void *get_os_handle() const { return banner_; } /* get banner information */ int get_banner_info(os_banner_info_t *info); /* clear the window */ virtual void clear_window(VMG0_); /* set MORE mode */ virtual int set_more_state(int state) { /* banners never change the global MORE mode state */ return is_more_mode(); } /* get the MORE mode - return the global mode flag */ virtual int is_more_mode() const { return G_os_moremode; } /* show the MORE prompt - does nothing for a banner window */ virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); } /* get the line width of the display device */ virtual int get_line_width() const { return os_banner_get_charwidth(banner_); } /* allow overrunning the line width in a banner */ virtual int allow_overrun() const { return TRUE; } /* get the page length of the display device, for MORE mode */ virtual int get_page_length() const { return os_banner_get_charheight(banner_); } protected: /* our underlying OS-level banner handle */ void *banner_; /* our window type (an OS_BANNER_TYPE_xxx code) */ int win_type_; }; /* ------------------------------------------------------------------------ */ /* * Common base class for log-only consoles */ class CVmConsoleLogBase: public CVmConsole { public: /* clear the window - do nothing on a log console */ virtual void clear_window(VMG0_) { } /* set MORE mode - doesn't apply to us */ virtual int set_more_state(int state) { return FALSE; } /* show the MORE prompt - does nothing */ virtual void show_more_prompt(VMG0_) { } /* get the MORE mode */ virtual int is_more_mode() const { return FALSE; } /* get the line width of the display device */ virtual int get_line_width() const { return G_os_linewidth; } /* allow overrunning the line width in a lot file */ virtual int allow_overrun() const { return TRUE; } /* * get the page length of the display device - this is arbitrary, since * we don't use MORE mode anyway */ virtual int get_page_length() const { return 55; } }; /* * Log console. This is used to create a console that has no display * presence, but simply captures its output directly to a log file. (This * is similar to the log file attached to a regular display console, but * this kind of console ONLY has the log file.) */ class CVmConsoleLog: public CVmConsoleLogBase { public: /* create */ CVmConsoleLog(VMG_ CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width); void delete_obj(VMG0_); protected: }; /* * A special console that sends output to the *main* console's log file. * This allows code to be written with a generic console pointer, rather * than a test checking for the main console or some other console; just * plug this in when there's not another console pointer and output will go * to the main console automatically. */ class CVmConsoleMainLog: public CVmConsoleLogBase { public: /* create */ CVmConsoleMainLog() { } /* we send text to the main console's log */ int format_text(VMG_ const char *p, size_t len) { /* send the text to the main console's log file */ return G_console->format_text_to_log(vmg_ p, len); } protected: ~CVmConsoleMainLog() { } }; /* ------------------------------------------------------------------------ */ /* * constants */ /* * HTML lexical analysis modes. We use these modes for two purposes: * * First, for tracking our state while doing our own HTML parsing, which we * do when the underlying renderer handles only plain text (in which case * we must interpret and remove any HTML tags from the stream before * sending the stream on to the underlying renderer). * * Second, for tracking the lexical state in the underlying renderer, when * the underlying renderer handles full HTML interpretation. In this case, * we simply pass HTML tags through to the underlying renderer; but even * though we don't interpret the tags, we do keep track of the lexical * structure of the text, so that we can tell when we're inside a tag and * when we're in ordinary text. Certain operations we apply (such as case * conversions with "\^" and "\v" sequences) apply only to ordinary text, * so we need to know what's what in the stream text. */ enum vmconsole_html_state { VMCON_HPS_NORMAL, /* normal text, not in a tag */ VMCON_HPS_TAG_START, /* parsing the start of a tag */ VMCON_HPS_TAG_NAME, /* parsing a tag name */ VMCON_HPS_TAG, /* parsing inside a tag */ VMCON_HPS_ATTR_NAME, /* parsing an attribute name */ VMCON_HPS_ATTR_VAL, /* parsing an attribute value */ VMCON_HPS_SQUOTE, /* in a single-quoted string in a tag */ VMCON_HPS_DQUOTE, /* in a double-quoted string in a tag */ VMCON_HPS_ENTITY_1ST, /* first character in an entity name */ VMCON_HPS_ENTITY_NUM_1ST, /* first character of a numeric entitiy */ VMCON_HPS_ENTITY_HEX, /* in a hexadecimal entitiy number */ VMCON_HPS_ENTITY_DEC, /* in a decimal entity number */ VMCON_HPS_ENTITY_NAME, /* in a named entity */ VMCON_HPS_MARKUP_END /* at last character of a markup */ }; /* * HTML parsing mode flag for
tags. We defer these until we've * read the full tag in order to obey an HEIGHT attribute we find. When * we encounter a
, we figure out whether we think we'll need a * flush or a blank line; if we find a HEIGHT attribute, we may change * this opinion. */ enum vmconsole_html_br_mode { HTML_DEFER_BR_NONE, /* no pending
*/ HTML_DEFER_BR_FLUSH, /* only need to flush output */ HTML_DEFER_BR_BLANK /* need a blank line */ }; /* * Color/attribute information. Each character is tagged with its current * color and attributes. */ struct vmcon_color_t { /* foreground color */ os_color_t fg; /* background color */ os_color_t bg; /* the current OS_ATTR_xxx attributes */ int attr; /* check for equality with another color structure */ int equals(const vmcon_color_t *other) const { return (fg == other->fg && bg == other->bg && attr == other->attr); } }; /* ------------------------------------------------------------------------ */ /* * Output Buffer Flags. Each character in the output buffer has an * associated flag value associated with it; the flag value is a * combination of the bit flags defined here. */ /* * Unbreakable character point. This indicates that a line break is not * allowed to follow this character, even if the text at this point would * ordinarily allow a soft line break. Note that this does NOT override a * hard line break (i.e., an explicit newline); it merely prevents a soft * line break from occurring immediately after this character. * * We apply this flag to the character immediately preceding an explicit * non-breaking space in the text stream. */ #define VMCON_OBF_NOBREAK 0x01 /* * Breakable character point. This indicates that a line break is allowed * to follow this character, even if the text at this point would not * ordinarily allow a soft line break. * * We apply this flag to the character immediately preceding an explicit * zero-width breakable space in the text stream. */ #define VMCON_OBF_OKBREAK 0x02 /* * Break-anywhere mode. This indicates that this character is part of a * run of break-anywhere text. In break-anywhere text, we're allowed to * break lines between any two characters, except adjacent to explicit * non-breaking spaces in the text stream. * * Note that the NOBREAK flag overrides this flag, because this flag * merely indicates the mode, while the NOBREAK flag indicates an explicit * non-breaking indicator. */ #define VMCON_OBF_BREAK_ANY 0x04 /* * Soft hyphenation point. This flag indicates that the text can be * hyphenated immediately following this character, which is to say that * we can insert a hyphen following this character and break the line * after the hyphen. If we do not hyphenate here, the soft hyphen has no * effect; in particular, no hyphen character appears in the output stream * when a soft hyphen is not used as a line break point. */ #define VMCON_OBF_SHY 0x08 /* * Quoted space. This indicates that this is a space character that * doesn't collapse in runs of contiguous whitespace. */ #define VMCON_OBF_QSPACE 0x10 /* ------------------------------------------------------------------------ */ /* * Output formatter stream interface. A formatter stream performs * formatting on a stream of displayed text. * * A given stream of display text might be fed into more than one * formatter stream. For example, if logging is turned on for a * console, we'll feed the same text to the console's main formatting * stream, which will end up being displayed to the user, and also to * the log file's formatting stream, which will end up written out to * the log file. * * Note that the formatter interface is internal to the console system. * Client code should never need to refer to a formatter object, but * should instead call the console object (CVmConsole). */ /* tag alignment types */ enum vmfmt_tab_align_t { VMFMT_TAB_NONE, VMFMT_TAB_LEFT, VMFMT_TAB_CENTER, VMFMT_TAB_RIGHT, VMFMT_TAB_DECIMAL }; /* maximum formatter state stack depth */ const size_t CVMFMT_STACK_MAX = 25; /* maximum depth of color stack */ const size_t CVFMT_CLRSTK_MAX = 25; /* maximum length of an attribute name/value */ const size_t CVFMT_MAX_ATTR_NAME = 40; const size_t CVFMT_MAX_ATTR_VAL = 256; /* output stream information structure (forward declaration) */ typedef struct out_stream_info out_stream_info; /* output formatter stream */ class CVmFormatter { public: CVmFormatter(class CVmConsole *console) { /* remember our display object */ console_ = console; /* there's no title buffer by default */ html_title_buf_ = 0; html_title_buf_size_ = 0; /* we have no horizontal tab table yet (for the HTML mini-parser) */ tabs_ = 0; /* no character mapper yet */ cmap_ = 0; } /* * delete the object (call this instead of calling the destructor * directly, since some subclasses need global access) */ virtual void delete_obj(VMG0_) { delete this; } /* initialize */ virtual void init() { /* start out at the first column */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; /* start out in normal text color */ cur_color_.fg = OS_COLOR_P_TEXT; cur_color_.bg = OS_COLOR_P_TRANSPARENT; cur_color_.attr = 0; os_color_ = cur_color_; /* no pending tab yet */ pending_tab_align_ = VMFMT_TAB_NONE; /* start out at the first line */ linecnt_ = 0; /* we're not in either "caps", "nocaps", or "allcaps" mode yet */ capsflag_ = nocapsflag_ = allcapsflag_ = FALSE; /* * set the initial buffer flags: start out in word-break (not * break-anywhere) more */ cur_flags_ = 0; /* * start in normal spacing mode - treat runs of whitespace as * equivalent to a single space */ obey_whitespace_ = FALSE; /* start out in HTML interpretation mode */ literal_mode_ = FALSE; /* presume we won't have OS-level line wrapping */ os_line_wrap_ = FALSE; /* we haven't flushed a new line yet */ just_did_nl_ = FALSE; /* assume that the underlying system is not HTML-enabled */ html_target_ = FALSE; /* presume this target accepts OS highlighting sequences */ plain_text_target_ = FALSE; /* * start out in "normal" lexical state in our parser and in the * underlying output stream */ html_parse_state_ = VMCON_HPS_NORMAL; html_passthru_state_ = VMCON_HPS_NORMAL; /* not in an ignored tag yet (-> nesting depth is zero) */ html_in_ignore_ = 0; /* not in title mode yet (-> nesting depth is zero) */ html_in_title_ = 0; /* not yet deferring line breaks */ html_defer_br_ = HTML_DEFER_BR_NONE; /* not yet in quotes (-> nesting depth is zero) */ html_quote_level_ = 0; /* not in a PRE section */ html_pre_level_ = 0; /* we're not parsing inside any tags yet */ html_allow_alt_ = FALSE; html_allow_color_ = FALSE; html_in_body_ = FALSE; html_in_tab_ = FALSE; html_in_wrap_ = FALSE; /* assume we're a normal display stream */ is_disp_stream_ = TRUE; /* no stacked colors yet */ color_sp_ = 0; } /* get/set obey-whitespace mode */ int get_obey_whitespace() const { return obey_whitespace_ != 0; } void set_obey_whitespace(int f) { obey_whitespace_ = (f != 0); } /* turn on CAPS mode */ void caps() { /* turn on CAPS mode */ capsflag_ = TRUE; /* turn off the NOCAPS and ALLCAPS modes */ nocapsflag_ = FALSE; allcapsflag_ = FALSE; } /* turn on NOCAPS mode */ void nocaps() { /* turn on NOCAPS mode */ nocapsflag_ = TRUE; /* turn off CAPS and ALLCAPS mode */ capsflag_ = FALSE; allcapsflag_ = FALSE; } /* turn on or off ALLCAPS mode */ void allcaps(int all_caps) { /* set the ALLCAPS flag */ allcapsflag_ = all_caps; /* clear the CAPS and NOCAPS flags */ capsflag_ = FALSE; nocapsflag_ = FALSE; } /* * Display a string of a given byte-length. The text is given in * UTF-8 format. We'll interpret embedded control codes (newlines, * blank lines, caps flags, etc); depending on the display mode, we * might also interpret HTML markup sequences. */ int format_text(VMG_ const char *s, size_t len); /* set the text attributes for subsequent format_text() displays */ void set_text_attr(VMG_ int attr) { /* remember the new text attributes */ cur_color_.attr = attr; } /* set the text color for subsequent format_text() displays */ void set_text_color(VMG_ os_color_t fg, os_color_t bg) { /* remember the new text colors */ cur_color_.fg = fg; cur_color_.bg = bg; } /* write a blank line to the stream */ void write_blank_line(VMG0_); /* * Flush the current line to the display, using the given type of * line termination. */ void flush(VMG_ vm_nl_type nl); /* clear our buffers */ void empty_buffers(VMG0_); /* immediately update the display window */ void update_display(VMG0_); /* * determine if I'm an HTML target - this returns true if the * underyling renderer accepts HTML, in which case we write HTML * markups directly to the os_xxx routines (os_printz, for example). */ int is_html_target() const { return html_target_; } /* reset the MORE prompt line counter */ virtual void reset_line_count(int clearing) { linecnt_ = 0; } /* * Note that we read a line from the keyboard with echo. Reading a * line displays a carriage return without our help, taking us back to * the first column - we need to know this so that we can deal * properly with output on the following line. */ void note_input_line() { /* * a CR/LF will have been echoed automatically by the input * reader, which takes us back to the first column */ linecol_ = 0; /* note that we effectively just wrote a newline */ just_did_nl_ = TRUE; } /* -------------------------------------------------------------------- */ /* * Virtual interface to underlying OS renderer */ /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os() = 0; virtual void end_html_in_os() = 0; /* set the text color */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) = 0; /* set the body color */ virtual void set_os_body_color(os_color_t) = 0; /* set text attributes */ virtual void set_os_text_attr(int attr) = 0; /* * display text to the OS window; the text is given in the local * character set */ virtual void print_to_os(const char *txt) = 0; /* flush the underlying OS-level rendere */ virtual void flush_to_os() = 0; /* set the window title in the OS layer */ virtual void set_title_in_os(const char *txt) = 0; /* set a new character mapper */ void set_charmap(class CCharmapToLocal *cmap); protected: virtual ~CVmFormatter(); /* * Write a line (or a partial line) of text to the stream, using the * indicated line breaking. The text is given as wide Unicode * characters. */ void write_text(VMG_ const wchar_t *txt, size_t cnt, const vmcon_color_t *colors, vm_nl_type nl); /* write a tab to the stream */ void write_tab(VMG_ int indent, int multiple); /* flush the current line, with or without padding */ void flush_line(VMG_ int padding); /* * Buffer a character of output. The character is presented to us as a * wide Unicode character. We'll expand this character with the local * character mapping's expansion rules, then add the expansion to our * output buffer, performing word-wrapping as needed. */ void buffer_char(VMG_ wchar_t c); /* * buffer an expanded character - we'll buffer the given unicode * character, with no further expansion */ void buffer_expchar(VMG_ wchar_t c); /* buffer a rendered expanded character */ void buffer_rendered(wchar_t c, unsigned char flags, int wid); /* * Buffer a string of output to the stream. The string is in UTF-8 * format. */ void buffer_string(VMG_ const char *txt); /* buffer a wide unicode character string */ void buffer_wstring(VMG_ const wchar_t *txt); /* get the next wide unicode character in a UTF8-encoded string */ static wchar_t next_wchar(const char **s, size_t *len); /* * Determine if we should use MORE mode in the formatter layer; if * this returns true, we handle MORE prompting ourselves, otherwise * we handle it through the OS layer. * * The default version of this function is implemented in the output * formatter configuration module. This function can also be * overridden in a subclass (for example, a log stream never uses * MORE mode, no matter what configuration we're using). */ virtual int formatter_more_mode() const; /* * Get the maximum column to store in our internal buffer. If we're * doing our own line breaking, we'll break off the buffer at the * actual line width of the underlying console. If we're doing OS * line wrapping, we'll simply fill up our internal buffer to its * maximum size, since the flushing points are irrelevant to the line * wrapping the underlying OS console will be doing and hence we might * as well buffer as much as we can for efficiency. */ virtual int get_buffer_maxcol() const { /* * if the OS is doing the line wrapping, use the full buffer; * otherwise, use the actual line width from the underyling * console */ if (os_line_wrap_) return OS_MAXWIDTH; else return console_->get_line_width(); } /* * Determine if the underlying stream interprets HTML markups. We * call this during initialization to set our html_target_ member * (we cache the result since we check it frequently). * * This is implemented in the formatter configuration module. */ virtual int get_init_html_target() const; /* -------------------------------------------------------------------- */ /* * HTML mini-parser. This only needs to be implemented when linking * with non-HTML osifc implementations; when osifc provides HTML * parsing, we don't need to do any HTML parsing here, so these can use * dummy implementations. */ /* * Resume text-only HTML mini-parser. This is called when we start * writing a new string and discover that we're parsing inside an HTML * tag in our mini-parser. * * Returns the next character after the run of text we parse. */ wchar_t resume_html_parsing(VMG_ wchar_t c, const char **sp, size_t *slenp); /* parse an HTML entity ('&') markup */ wchar_t parse_entity(VMG_ wchar_t *ent, const char **sp, size_t *slenp); /* * Parse the beginning HTML markup. This is called when we are * scanning a '<' or '&' character in output text, and we're in HTML * mode, and the underlying target doesn't support HTML parsing. * Returns the next character to process after we finish our initial * parsing. */ wchar_t parse_html_markup(VMG_ wchar_t c, const char **sp, size_t *slenp); /* * Expand any pending tab. This should be called when we're doing * HTML mini-parsing, and we see a tag or we reach the end of an * output line. We'll expand any pending RIGHT/CENTER tab by going * back to the tab's location and inserting the necessary number of * spaces now that we know the extent of the text affected. * * If 'allow_anon' is true, we will allow "anonymous" tabs, which is * to say tabs with no ID attribute. Anonymous tabs allow text to be * tabbed relative to the full width of the line, but these are * meaningful only with normal line endings; when a line is flushed * before the end of the line is reached (because the line will be * used for a prompt, for example), anonymous tabs should not be * expanded with the line. */ void expand_pending_tab(VMG_ int allow_anon); /* * find a tab definition object; if 'create' it true and the specified * tab doesn't exist, we'll create a new tab object */ class CVmFmtTabStop *find_tab(wchar_t *id, int create); /* service routine - translate a wide character string to an integer */ static int wtoi(const wchar_t *p); /* parse a COLOR attribute of a FONT tag */ int parse_color_attr(VMG_ const wchar_t *val, os_color_t *result); /* push the current color for later restoration */ void push_color() { /* * add a level to the color stack, if possible; if it's not * possible, assume we've lost an end tag somewhere, so rotate the * entire stack down a level */ if (color_sp_ == CVFMT_CLRSTK_MAX) { /* take everything down a level */ memmove(color_stack_, color_stack_ + 1, (CVFMT_CLRSTK_MAX - 1)*sizeof(color_stack_[0])); --color_sp_; } /* save the current color in the stack */ color_stack_[color_sp_++] = cur_color_; } /* pop the current color */ void pop_color() { /* * if we're at the bottom of the stack, we must have had too many * close tags; ignore the extra operation */ if (color_sp_ != 0) { /* restore the next color down */ cur_color_ = color_stack_[--color_sp_]; } } /* * Process a tag. By default, we ignore this tag entirely. * Log streams should hide the contents of this tag. */ virtual void process_nolog_tag(int /*is_end_tag*/) { /* by default, ignore this tag */ } /* * Process a tag. By default, we hide the contents of this tag; * this tag marks text that is to be shown only in a log stream. */ virtual void process_log_tag(int is_end_tag) { /* turn hiding on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } /* -------------------------------------------------------------------- */ /* * Member variables */ /* the console that owns this formatter stream */ class CVmConsole *console_; /* our character mapper */ class CCharmapToLocal *cmap_; /* current line position and output column */ int linepos_; int linecol_; /* * flag: the current buffer is a "continuation" line; that is, we've * already flushed a partial line to the display without moving to a * new line vertically, and the current buffer will be displayed on the * same line on the terminal, to the right of the previously-displayed * material */ int is_continuation_; /* number of lines on the screen (since last MORE prompt) */ int linecnt_; /* * Output buffer. We keep the output buffer as wide Unicode * characters, and translate to the local character set when we * flush the buffer and send the text to the os_xxx routines for * display. */ wchar_t linebuf_[OS_MAXWIDTH + 1]; /* * Output buffer character colors. We keep track of the display color * of each character in the buffer. */ vmcon_color_t colorbuf_[OS_MAXWIDTH + 1]; /* * output buffer flags - we keep a flag value for each character in * the output buffer */ unsigned char flagbuf_[OS_MAXWIDTH + 1]; /* current attribute name/value buffers */ wchar_t attrname_[CVFMT_MAX_ATTR_NAME]; wchar_t attrval_[CVFMT_MAX_ATTR_VAL]; wchar_t attr_qu_; /* current color of characters being added to our buffer */ vmcon_color_t cur_color_; /* color of last OS output (via write_text) */ vmcon_color_t os_color_; /* stack of color attributes, saved for nested tags */ vmcon_color_t color_stack_[CVFMT_CLRSTK_MAX]; size_t color_sp_; /* * Current output character flags. This is the base value to write to * flagbuf_ for each character we output, given the current mode * settings. */ unsigned char cur_flags_; /* obey-whitespace mode - treat whitespace as literal */ unsigned int obey_whitespace_ : 1; /* literal mode - ignore HTML markups, pass everything literally */ unsigned int literal_mode_ : 1; /* CAPS mode - next character output is converted to upper-case */ unsigned int capsflag_ : 1; /* NOCAPS mode - next character output is converted to lower-case */ unsigned int nocapsflag_ : 1; /* ALLCAPS mode - all characters output are converted to upper-case */ unsigned int allcapsflag_ : 1; /* flag indicating that we just flushed a new line */ unsigned int just_did_nl_ : 1; /* * Flag indicating that the underlying output system wants to * receive its output as HTML. * * If this is true, we'll pass through HTML to the underlying output * system, and in addition generate HTML sequences for certain * TADS-native escapes (for example, we'll convert the "\n" sequence * to a
sequence). * * If this is false, we'll do just the opposite: we'll remove HTML * from the output stream and convert it into normal text sequences. */ unsigned int html_target_ : 1; /* * Flag indicating that the target uses plain text. If this flag is * set, we won't add the OS escape codes for highlighted characters. */ unsigned int plain_text_target_ : 1; /* * flag: the underlying OS layer handles line wrapping, so we never * need to write newlines when flushing our line buffer except when * we want to indicate a hard line break */ unsigned int os_line_wrap_ : 1; /* * Current lexical analysis state for our own HTML parsing. This is * used to track our HTML state when we have an underlying plain text * renderer. */ vmconsole_html_state html_parse_state_; /* * Current lexical analysis mode for the text stream going to the * underlying renderer. This is used to track the lexical structure of * the stream when we're passing HTML tags through to the underlying * renderer. */ vmconsole_html_state html_passthru_state_; /* last tag name */ char html_passthru_tag_[32]; char *html_passthru_tagp_; /*
defer mode */ vmconsole_html_br_mode html_defer_br_; /* * HTML "ignore" mode - we suppress all output when parsing the * contents of a or <ABOUTBOX> tag. This is a counter that * keeps track of the nesting depth for ignored tags. */ int html_in_ignore_; /* * HTML <TITLE> mode - when we're in this mode, we're gathering the * title (i.e., we're inside a <TITLE> tag's contents). We'll copy * characters to the title buffer rather than the normal output * buffer, and then call os_set_title() when we reach the * tag. This is a counter that keeps track of the nesting depth of * tags. */ int html_in_title_; /* buffer for the title */ char *html_title_buf_; size_t html_title_buf_size_; /* pointer to next available character in title buffer */ char *html_title_ptr_; /* * quoting level - this is a counter that keeps track of the nesting * depth of <Q> tags */ int html_quote_level_; /* PRE nesting depth */ int html_pre_level_; /* * Parsing mode flag for ALT attributes. If we're parsing a tag * that allows ALT, such as IMG or SOUND, we'll set this flag, then * insert the ALT text if we encounter it during parsing. */ unsigned int html_allow_alt_ : 1; /* parsing mode flag for COLOR attributes */ unsigned int html_allow_color_ : 1; /* parsing a BODY tag's attributes */ unsigned int html_in_body_ : 1; /* parsing a TAB tag's attributes */ unsigned int html_in_tab_ : 1; /* parsing a WRAP tag's attributes */ unsigned int html_in_wrap_ : 1; /* hash table of <TAB> objects */ class CVmHashTable *tabs_; /* characteristics of TAB tag we're defining */ class CVmFmtTabStop *new_tab_entry_; vmfmt_tab_align_t new_tab_align_; wchar_t new_tab_dp_; /* * Characteristics of pending tab. We must handle this tab when we * reach the next <TAB> or the end of the current line. If * pending_tab_align_ is VMFMT_TAB_NONE, it indicates that there is no * pending tab (since a pending tab always requires alignment). */ vmfmt_tab_align_t pending_tab_align_; class CVmFmtTabStop *pending_tab_entry_; wchar_t pending_tab_dp_; /* * starting column of pending tab - this is the output column where we * were writing when the pending tab was encountered, so this is the * column where we insert spaces for the tab */ int pending_tab_start_; /* * color/attributes active at start of pending tab - this is the color * we'll use for the spaces we insert when we insert the tab */ vmcon_color_t pending_tab_color_; /* * Flag: this is a display stream. Other types of streams (such as * log file streams) should set this to false. */ unsigned int is_disp_stream_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Formatter for display windows */ class CVmFormatterDisp: public CVmFormatter { public: CVmFormatterDisp(class CVmConsole *console) : CVmFormatter(console) { } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatter::init(); /* * if we're compiled for HTML mode, set the standard output * stream so that it knows it has an HTML target - this will * ensure that HTML tags are passed through to the underlying * stream, and that we generate HTML equivalents for our own * control sequences */ html_target_ = get_init_html_target(); /* * since we always use HTML mode, turn on HTML mode in the * underlying OS window if our underlying OS renderer is HTML-aware */ if (html_target_) start_html_in_os(); } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the main display */ class CVmFormatterMain: public CVmFormatterDisp { public: CVmFormatterMain(class CVmConsole *console, size_t html_title_buf_size) : CVmFormatterDisp(console) { /* allocate a title buffer */ html_title_buf_size_ = html_title_buf_size; if (html_title_buf_size_ != 0) html_title_buf_ = (char *)t3malloc(html_title_buf_size_); } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatterDisp::init(); /* remember the OS line wrap setting from the console */ os_line_wrap_ = get_os_line_wrap(); } /* set the window title in the OS layer */ virtual void set_title_in_os(const char *txt) { /* set the window title */ os_set_title(txt); } protected: ~CVmFormatterMain() { /* delete our title buffer */ if (html_title_buf_ != 0) t3free(html_title_buf_); } /* * Determine if the main console uses OS-level line wrapping - if this * is returns true, then an output formatter on this console will not * insert a newline at the end of a line that it's flushing for word * wrapping, but will instead let the underlying OS display layer * handle the wrapping. * * The OS line wrapping status is a PERMANENT feature of the console, * so it is safe for the formatter to query this during initialization * and cache the value. */ static int get_os_line_wrap(); /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os(); virtual void end_html_in_os(); /* display text directly to the OS renderer */ virtual void print_to_os(const char *txt) { /* display the text on the primary OS console */ os_printz(txt); } /* flush the underlying OS-level renderer */ virtual void flush_to_os() { os_flush(); } /* * set text attributes for subsequent text directly in the underlying * OS window */ virtual void set_os_text_attr(int attr) { /* if the target isn't in 'plain' mode, set the attributes */ if (!plain_text_target_) os_set_text_attr(attr); } /* set the text color in the underlying OS window */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) { /* * if the target is in 'plain' mode, don't use colors; otherwise, * ask the console to do the work */ if (!plain_text_target_) os_set_text_color(fg, bg); } /* set the "body" color in the underlying OS window */ virtual void set_os_body_color(os_color_t color) { /* if not in "plain" mode, set the color */ if (!plain_text_target_) os_set_screen_color(color); } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the status line */ class CVmFormatterStatline: public CVmFormatterDisp { public: CVmFormatterStatline(class CVmConsole *console) : CVmFormatterDisp(console) { } /* we never use 'more' mode in the status line */ virtual int formatter_more_mode() const { return FALSE; } /* HTML mode has no effect on the status line */ virtual void start_html_in_os() { } virtual void end_html_in_os() { } /* text colors and attributes are not used in the status line */ virtual void set_os_text_color(os_color_t, os_color_t) { } virtual void set_os_body_color(os_color_t) { } virtual void set_os_text_attr(int) { } /* text displayed in the status line goes directly to the main console */ virtual void print_to_os(const char *txt) { os_printz(txt); } /* flushing the status line simply flushes the main text stream */ virtual void flush_to_os() { os_flush(); } /* titles have no effect in the status line */ virtual void set_title_in_os(const char *) { } }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for banner windows */ class CVmFormatterBanner: public CVmFormatterDisp { public: CVmFormatterBanner(void *banner, class CVmConsole *console, int win_type, unsigned long style) : CVmFormatterDisp(console) { /* remember my OS banner handle */ banner_ = banner; /* remember my window type */ win_type_ = win_type; /* remember the banner style */ style_ = style; } /* initialize */ void init_banner(int os_line_wrap, int obey_whitespace, int literal_mode) { /* inherit base class initialization */ CVmFormatterDisp::init(); /* remember the OS line wrapping mode in our underlying OS window */ os_line_wrap_ = os_line_wrap; /* remember the whitespace setting */ obey_whitespace_ = obey_whitespace; /* remember the literal-mode setting */ literal_mode_ = literal_mode; } /* set the window title in the OS layer (does nothing for a banner) */ virtual void set_title_in_os(const char *) { } /* reset the MORE prompt line counter */ virtual void reset_line_count(int clearing) { /* * To ensure we always keep a line of context when we page-forward * from a MORE prompt, start the line counter at 2. Note that we * do this in banner windows, but not in the main window, because - * for historical reasons - the OS layer tells us the *paging* size * for the main window, but the *actual* height for banner window. * Note that we don't want to handle this adjustment in our own * page length calculation, because doing so would cause the * *first* MORE prompt (from a cleared window) to show up too * early. * * When we're clearing the screen, reset to zero, since we have no * context to retain. */ linecnt_ = (clearing ? 0 : 1); } protected: /* * Use MORE mode in a banner window if we have the MORE-mode banner * window style, AND the base display banner would use MORE mode. The * latter check tests to see if we handle MORE mode at the OS level or * in the formatter. */ virtual int formatter_more_mode() const { return ((style_ & OS_BANNER_STYLE_MOREMODE) != 0 && CVmFormatterDisp::formatter_more_mode()); } /* display text directly to the OS renderer */ virtual void print_to_os(const char *txt) { /* display the text on our OS banner window */ os_banner_disp(banner_, txt, strlen(txt)); } /* turn HTML mode on/off in the underlying OS-level renderer */ virtual void start_html_in_os() { os_banner_start_html(banner_); } virtual void end_html_in_os() { os_banner_end_html(banner_); } /* set the text color */ virtual void set_os_text_color(os_color_t fg, os_color_t bg) { /* set the color in the banner */ os_banner_set_color(banner_, fg, bg); } /* set the body color */ virtual void set_os_body_color(os_color_t color) { /* set the color in the banner */ os_banner_set_screen_color(banner_, color); } /* set text attributes */ virtual void set_os_text_attr(int attr) { /* set the attributes on the underlying OS-level banner */ os_banner_set_attr(banner_, attr); } /* flush the underlying OS-level renderer */ virtual void flush_to_os() { /* flush the OS banner window */ os_banner_flush(banner_); } /* determine if the target supports HTML */ virtual int get_init_html_target() const { /* if we're a text grid, the underlying window does not use HTML */ if (win_type_ == OS_BANNER_TYPE_TEXTGRID) return FALSE; /* defer to the inherited determination in other cases */ return CVmFormatterDisp::get_init_html_target(); } /* my OS banner handle */ void *banner_; /* my OS window type (OS_BANNER_TYPE_xxx) */ int win_type_; /* banner style (a combination of OS_BANNER_STLE_xxx bit flags) */ unsigned long style_; }; /* ------------------------------------------------------------------------ */ /* * Formatter subclass for the log file */ class CVmFormatterLog: public CVmFormatter { friend class CVmConsole; friend class CVmConsoleLog; public: CVmFormatterLog(class CVmConsole *console, int width) : CVmFormatter(console) { /* we have no log file yet */ lognf_ = 0; logfp_ = 0; logglob_ = 0; /* remember our width */ width_ = width; } void delete_obj(VMG0_) { /* close the log file */ close_log_file(vmg0_); /* do the basic deletion */ CVmFormatter::delete_obj(vmg0_); } /* initialize */ void init() { /* inherit base class initialization */ CVmFormatter::init(); /* use plain text in the log file stream */ plain_text_target_ = TRUE; /* we're not a display stream */ is_disp_stream_ = FALSE; /* we're not an HTML formatter */ html_target_ = FALSE; /* * we use our own internal line wrapping, since our underlying * display layer is simply dumping to a text file */ os_line_wrap_ = FALSE; /* no log file yet */ logfp_ = 0; } /* don't use MORE mode in a log stream */ int formatter_more_mode() const { return FALSE; } /* get my width */ virtual int get_buffer_maxcol() const { return width_; } protected: /* log streams do not support HTML at the OS level */ virtual void start_html_in_os() { } virtual void end_html_in_os() { } /* set the attributes in the underlying stream */ virtual void set_os_text_attr(int) { /* log streams are plain text - they don't support attributes */ } /* set the color in the underlying stream */ virtual void set_os_text_color(os_color_t, os_color_t) { /* log streams are plain text - they don't support colors */ } /* set the body color */ virtual void set_os_body_color(os_color_t) { /* log streams are plain text - they don't support colors */ } /* display text to the underlying OS device */ virtual void print_to_os(const char *txt) { /* display the text through the log file */ if (logfp_ != 0) { os_fprintz(logfp_, txt); osfflush(logfp_); } } /* flush the underlying OS-level rendere */ virtual void flush_to_os() { } /* set the window title in the OS layer - no effect for log streams */ virtual void set_title_in_os(const char *) { } /* open a log file */ int open_log_file(VMG_ const char *fname); int open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc); int open_log_file(VMG_ class CVmNetFile *netfile); /* set the log file to a file previously opened */ int set_log_file(VMG_ CVmNetFile *nf, osfildef *fp); /* close the log file */ int close_log_file(VMG0_); /* * Process a <nolog> tag. Since we're a log stream, we hide the * contents of this tag. */ virtual void process_nolog_tag(int is_end_tag) { /* turn hiding on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } /* * Process a <log> tag. Since we're a log stream, we show the contents * of this tag, so we can simply parse and ignore it. */ virtual void process_log_tag(int /*is_end_tag*/) { } /* my log file handle and network file descriptor */ osfildef *logfp_; class CVmNetFile *lognf_; struct vm_globalvar_t *logglob_; /* the maximum width to use for our lines */ int width_; }; #endif /* VMCONSOL_H */ �����������������������������frobtads-1.2.3/tads3/resload.h����������������������������������������������������������������������0000644�0001750�0000144�00000007240�07627507700�015341� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/resload.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resload.h - resource loader Function Notes Modified 10/17/98 MJRoberts - Creation */ #ifndef RESLOAD_H #define RESLOAD_H #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * Resource loader. */ class CResLoader { public: /* create the loader */ CResLoader(); /* * create the loader with a root directory - we'll look for external * files under this directory */ CResLoader(const char *root_dir); /* set the executable file name */ void set_exe_filename(const char *exe_filename) { /* discard the old executable filename, if there is one */ lib_free_str(exe_filename_); /* remember the new path */ exe_filename_ = lib_copy_str(exe_filename); } /* delete */ ~CResLoader(); /* * Load a resource given a resource path. * * We'll start by looking for a file in the local file system matching * the name 'respath'. We interpret 'respath' as a relative URL; we'll * convert it to local system conventions before looking for the local * file. The path is relative to the root resource directory, which is * the directory specified when the resource loader was created. * * If we fail to find a local file matching the given name, we'll look * for a T3 resource library file with name 'deflib', if that argument * is non-null. 'deflib' is given as a URL to a file relative to the * root resource directory, and should be specified WITHOUT the default * ".t3r" suffix - we'll add that automatically using the appropriate * local filename conventions. If we can find this resource library, * we'll look for 'respath' within the library. Note that 'deflib' is * given in URL notation - we'll convert this to local path conventions * before attempting to locate the file. * * If we still can't find the file, and 'exerestype' is non-null, we'll * look in the executable file for a resource of the given type (this * should be a four-byte identifier, as used with MAKETRX). This * allows resources of different types to be bundled into the * executable file. Note that resources bundled into the executable * must use the standard T3 resource-image file format. */ osfildef *open_res_file(const char *respath, const char *deflib, const char *exerestype); protected: /* * Load a resource from the executable file. If we find the resource, * returns a file handle with its seek position set to the start of * the resource data. If we can't find the resource in the * executable, returns null. */ osfildef *open_exe_res(const char *respath, const char *restype); /* * Load a resource from a resource library. If we find the resource, * returns a file handle with its seek position set to the start of the * resource data. If we can't find the library or we can't find the * resource in the library, returns null. */ osfildef *open_lib_res(const char *libfile, const char *respath); /* root directory for external file searches */ char *root_dir_; /* path to executable file */ char *exe_filename_; }; #endif /* RESLOAD_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmnet.h������������������������������������������������������������������������0000644�0001750�0000144�00000141160�12145504453�015032� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnet.h - TADS 3 networking Function Defines the networking layer for TADS 3. This is used for the web server configurations. This is portable code implementing high-level network operations, such as an HTTP server, using the low-level socket and thread API defined in osifcnet.h. Notes Modified 04/11/10 MJRoberts - Creation */ #ifndef VMNET_H #define VMNET_H /* include this module only if the networking subsystem is enabled */ #ifdef TADSNET #include <time.h> #include <wchar.h> #include "osifcnet.h" #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmerr.h" #include "vmdbg.h" #include "vmglob.h" #include "vmstrref.h" /* ------------------------------------------------------------------------ */ /* * Web server configuration. We create a global singleton of this * structure when the interpreter reads a web configuration file at * startup. */ class TadsNetConfig { public: TadsNetConfig() { first_var = 0; last_var = 0; } ~TadsNetConfig(); /* read a config file */ void read(osfildef *fp, class CVmMainClientIfc *clientifc); /* look up a configuration variable */ const char *get(const char *name) const; /* look up a configuration variable, applying a default if not defined */ const char *get(const char *name, const char *defval) { const char *val = get(name); return (val != 0 ? val : defval); } /* does the given configuration variable equal the given string? */ const int match(const char *name, const char *strval) { const char *val = get(name, ""); return strcmp(val, strval) == 0; } /* * Does the given configuration variable equal the given integer? If * the configuration variable isn't defined, returns false. Otherwise, * converts the configuration string value to an integer with atoi(), * and compares the result to the given value. */ const int match(const char *name, int intval) { const char *val = get(name); return (val != 0 ? atoi(val) == intval : FALSE); } /* set a variable */ class TadsNetConfigVar *set(const char *name, const char *val); protected: /* look up a variable */ class TadsNetConfigVar *getvar(const char *name) const; /* head/tail of list of variables */ class TadsNetConfigVar *first_var; class TadsNetConfigVar *last_var; }; /* variable entry in a TadsNetConfig */ class TadsNetConfigVar { public: TadsNetConfigVar(const char *name, const char *val) { this->name = lib_copy_str(name); this->val = lib_copy_str(val); this->nxt = 0; } ~TadsNetConfigVar() { lib_free_str(name); lib_free_str(val); } /* get the name/value */ const char *getname() const { return name; } const char *getval() const { return val; } /* change the value */ void setval(const char *val) { lib_free_str(this->val); this->val = lib_copy_str(val); } /* does our name match the given name? */ int matchname(const char *name) { return stricmp(name, this->name) == 0; } /* get/set the next item pointer */ TadsNetConfigVar *getnext() const { return nxt; } void setnext(TadsNetConfigVar *nxt) { this->nxt = nxt; } protected: char *name; char *val; TadsNetConfigVar *nxt; }; /* ------------------------------------------------------------------------ */ /* * Check a storage server API reply, and throw an error if appropriate. * Storage server API replies conventionally are plain text buffers of the * form: * * Code Message * * where 'Code' is an alphanumeric token giving an error code, and Message * is a human-readable result message. On success, the Code token is "OK"; * other token strings indicate errors. * * Functions that also return regular body data (such as GETFILE) return * the status message in an X-IFDBStorage-Status header instead of the * body. If the status header is present, we'll use that rather than * checking the reply body. * * If the HTML status, headers, or reply indicates an error, we'll throw a * storage server run-time error, so this won't return. On success, we * simply return. */ void vmnet_check_storagesrv_reply(VMG_ int htmlstat, CVmDataSource *reply, const char *headers); /* * Retrieve the storage server status from the reply. This returns an * allocated string buffer containing the status code as the first * space-delimited token, with a printable error message following. * * The status code is "OK" on success. If the request failed due to a * storage server error, the first token is a non-numeric error ID. If the * request failed due to an HTTP, the first token is the numeric HTTP * status. If the request failed due to a lower-level network error, the * code is a negative numeric code giving the internal OS_HttpClient ErrXxx * code. * * The caller must delete the returned buffer with t3free(). */ char *vmnet_get_storagesrv_stat(VMG_ int htmlstat, CVmDataSource *reply, const char *headers); /* * Check the reply code returned by vmnet_get_storagesrv_reply(). On * success, frees the status code buffer and returns. On failure, frees * the status code buffer and throws a StorageServerError exception. Note * that we free the buffer in either case. */ void vmnet_check_storagesrv_stat(VMG_ char *stat); /* ------------------------------------------------------------------------ */ /* * Master thread list. This is a portable helper object for keeping a list * of threads. The OS implementation can use this to maintain a list of * all of the threads the program has started. This is useful mainly so * that the program can wait for background threads to exit on their own * before terminating the overall process. It could also be useful for * debugging purposes, such as to display a list of active threads on the * console at various times. * * If the OS layer uses this object, OS_Thread should register each new * thread in its constructor, and unregister each outgoing thread in its * destructor. Doing so is optional - it's purely for the OS layer's * benefit, so if the OS layer doesn't need the master thread list * information, it can ignore this object. */ /* * thread list link */ struct TadsThreadLink { TadsThreadLink(OS_Thread *thread, TadsThreadLink *nxt) { /* save a weak reference to the thread */ this->thread = thread->get_weak_ref(); this->nxt = nxt; } ~TadsThreadLink() { thread->release_ref(); } OS_Thread *get_thread() { return (OS_Thread *)thread->ref(); } /* weak reference to our thread */ CVmWeakRef *thread; /* next link in list */ TadsThreadLink *nxt; }; /* * thread list */ class TadsThreadList: public CVmRefCntObj { public: TadsThreadList() { mu = new OS_Mutex(); head = 0; } ~TadsThreadList() { mu->release_ref(); } /* * add a thread to the master list - the OS_Thread constructor must * call this */ void add(OS_Thread *thread) { /* lock the list */ mu->lock(); /* link the thread into our list */ head = new TadsThreadLink(thread, head); /* done with the list lock */ mu->unlock(); } /* clean the list - clear dead threads */ void clean() { /* lock the list */ mu->lock(); /* * Unlink the thread from the list. It's possible that it's not in * the list any longer, since wait_all() removes threads before * waiting for them to exit. */ TadsThreadLink *cur, *prv, *nxt; for (prv = 0, cur = head ; cur != 0 ; cur = nxt) { /* remember the next thread */ nxt = cur->nxt; /* check to see if this thread is still alive */ if (cur->thread->test()) { /* it's still alive - keep it in the list */ prv = cur; } else { /* this thread is gone - remove the link */ if (prv != 0) prv->nxt = nxt; else head = nxt; /* delete the link */ delete cur; } } /* done with the list lock */ mu->unlock(); } /* * Wait for all threads to exit. The 'timeout' (given in milliseconds) * is applied to each individual thread. Returns true if all threads * exited successfully, false if one or more threads timed out. */ int wait_all(long timeout) { /* presume success */ int ok = TRUE; /* keep going until we're out of threads */ for (;;) { /* lock the list and pull off the first thread */ mu->lock(); TadsThreadLink *link = head; /* if we got an element, remove it from the list */ if (link != 0) { /* * Unlink it from the list. Note that our local variable * 't' has now assumed the reference count that the list * previously held on the thread. */ head = link->nxt; } /* done with the list lock */ mu->unlock(); /* if there are no more threads, we're done */ if (link == 0) break; /* if this thread is still alive, wait for it to exit */ OS_Thread *t = link->get_thread(); if (t != 0) { /* wait for this thread to exit */ if (t->wait(timeout) != OSWAIT_EVENT) { /* * the wait timed out or failed - note that we have at * least one thread that didn't exit successfully * within the timeout */ ok = FALSE; } /* we're done with the thread */ t->release_ref(); } /* delete the link */ delete link; } /* return the status result */ return ok; } protected: /* head of thread list */ TadsThreadLink *head; /* list mutex */ OS_Mutex *mu; }; /* ------------------------------------------------------------------------ */ /* * Network service registration. * * The usual way that a client finds a server on the Web is via an Internet * address and a "well-known" port number. For example, any time you * connect to a Web server, you generally connect to port 80, which is the * standard port for HTTP servers. * * TADS games DON'T use these standard well-known ports. This is an * important part of the design, because it allows many TADS games to run * on a single shared server. If each game tried to take over the same * well-known port for its network listener, you could only run one game * per machine (or per network adapter, anyway). Instead of using * well-known ports, then, TADS game servers use port numbers assigned * dynamically by the operating system. A game can't know its port number * until it's actually running. Therefore, we need some mechanism to * convey the port number to the client so that the client knows where to * connect. * * Registration is the solution. When a game starts a server, it registers * that server, by sending its IP address and listening port number to a * registration server. The registration server makes an entry in its * database of active servers, and then makes this information available to * clients through its own well-known port. The registration server CAN * use a well-known port because we only need one registration server per * machine. */ /* ------------------------------------------------------------------------ */ /* * Server manager. This is the global object that manages all of the * servers in the process. */ class TadsServerManager: public CVmRefCntObj { public: TadsServerManager(); ~TadsServerManager(); /* * Generate a random ID string - this can be used for anything * requiring a universally unique identifier, such as a session ID. * * 'obj' is a pointer to the object on whose behalf we're constructing * the string. This is used to contribute to the randomness of the * result, but can be null if no object is involved. * * This returns an allocated buffer, which the caller must free with * lib_free_str() when done with it. */ static char *gen_rand_id(void *obj); /* get a random number */ ulong rand(); protected: /* our ISAAC random number generator */ struct isaacctx *ic; /* resource protection mutex */ OS_Mutex *mutex; }; /* ------------------------------------------------------------------------ */ /* * Network listener. This object creates a thread that binds to a network * port and listens for incoming connections. */ class TadsListener: public CVmRefCntObj { public: /* * Create a web server listener. This binds to the given network port, * then launches the given listener thread to listen for new * connections on the port. */ static TadsListener *launch(const char *hostname, ushort portno, class TadsListenerThread *thread); /* * Shut down the listener. This sends a control message to the * listener thread telling it to shut down. The listener will close * its network port and exit its thread. * * This returns immediately without waiting for the listener to finish * shutting down. If you want to wait until the thread has exited, * wait for the thread object. */ void shutdown(); /* the listener thread object */ class TadsListenerThread *get_thread() const { return thread; } protected: /* * construction - this is protected because callers create this object * via the static method launch() */ TadsListener(TadsListenerThread *thread); /* deletion - protected because we're managed by reference counting */ ~TadsListener(); /* my thread object */ class TadsListenerThread *thread; }; /* * Listener thread. This is the generic class for listener threads; it * must be subclassed for each specific type of network object. */ class TadsListenerThread: public OS_Thread { friend class TadsListener; friend class TadsServerThread; public: /* construction */ TadsListenerThread(OS_Event *quit_evt) { /* no port yet */ port = 0; /* no error message yet */ errmsg = 0; /* * use the caller's quit event, if they supplied one, otherwise * create our own; if we create one, it's sticky - once signaled, * it stays signaled, no matter how many threads are waiting on it */ if (quit_evt == 0) this->quit_evt = new OS_Event(TRUE); else (this->quit_evt = quit_evt)->add_ref(); /* create our private shutdown event */ shutdown_evt = new OS_Event(TRUE); /* create our resource protection mutex */ mutex = new OS_Mutex(); /* we haven't started any server threads yet */ servers = 0; /* note the time we started running */ os_time(&start_time); /* start with thread #1 */ next_thread_id = 1; /* generate a random password */ password = TadsServerManager::gen_rand_id(this); } /* destruction */ ~TadsListenerThread(); /* * Create the server thread object. This must be subclassed for each * type of server to create the appropriate thread subclass. */ virtual class TadsServerThread *create_server_thread(OS_Socket *s) = 0; /* get the IP address and port number */ int get_local_addr(char *&ip, int &portno) const { return port != 0 ? port->get_local_addr(ip, portno) : 0; } /* set the listener port - we assume the caller's reference */ void set_port(OS_Listener *p) { port = p; } /* main thread entrypoint */ void thread_main(); /* * Check for an error message. If the server encounters an error it * can't recover from, it'll store a status message and exit the * listener thread. */ const char *get_errmsg() const { return errmsg; } /* generate a human-readable status report listing my threads */ void list_threads(struct NetString *buf); /* get the control password */ const char *get_password() const { return password; } /* get the start time as an ASCII string */ const char *asc_start_time() const { return asctime(os_localtime(&start_time)); } /* get the application-wide 'quit' event object */ OS_Event *get_quit_evt() const { return quit_evt; } /* get the listener shutdown event */ OS_Event *get_shutdown_evt() const { return shutdown_evt; } protected: /* add a thread to our list */ void add_thread(class TadsServerThread *t); /* remove a thread from our list */ void remove_thread(class TadsServerThread *t); /* time the server started running */ os_time_t start_time; /* * Password: this is a random string generated at startup, to secure * network access to control functions. The idea is that the server * displays this on its console, so only someone with console access to * the server will be able to also gain remote access to the server * control functions. */ char *password; /* next thread serial number */ int next_thread_id; /* our listener port object */ OS_Listener *port; /* * Application-wide quit event. This is the global event object that * signals application termination. We'll abort any blocking operation * when this event is signaled so that we can promptly terminate the * listener thread when the application is being terminated. */ OS_Event *quit_evt; /* * Listener shutdown event. This is the local event object that lets * our owner tell us to shut down the listener thread. This only * applies to this thread, not to the rest of the application, so it * can be used to selectively terminate this single listener while * leaving other server threads running. */ OS_Event *shutdown_evt; /* * error message - if the server encounters an error that it can't * recover from, it will store status information here and shut down */ char *errmsg; /* head of our list of active server threads */ class TadsServerThread *servers; /* mutex protecting our shared resources against concurrent access */ OS_Mutex *mutex; }; /* * Base class for server threasds. */ class TadsServerThread: public OS_Thread { friend class TadsListenerThread; public: TadsServerThread(TadsListenerThread *l, OS_Socket *s) { /* we take over the socket reference from the caller */ socket = s; /* remember the client's IP address */ socket->get_peer_addr(client_ip, client_port); /* remember the listener thread */ listener = l; l->add_ref(); /* create our resource protection mutex */ mutex = new OS_Mutex(); /* set the initial state string */ state = new StringRef("Initializing"); } ~TadsServerThread() { /* release our resources */ socket->release_ref(); listener->release_ref(); mutex->release_ref(); state->release_ref(); if (client_ip != 0) t3free(client_ip); } /* get the client IP address */ const char *get_client_ip() const { return client_ip; } int get_client_port() const { return client_port; } /* main thread entrypoint */ void thread_main(); /* * Process a request. The main server loop simply calls this * repeatedly as long as it returns true. This should read a request * from the socket and send the response, then return a success or * failure indication to the caller. Returns true on success, false if * an unrecoverable error occurs. A false return tells the server loop * to close the socket and terminate the thread. */ virtual int process_request() = 0; /* * Read data from our peer. This blocks until at least one byte is * available, then returns as much data as can be read without * blocking. On success, returns the number of bytes read. On error, * returns -1. * * If 'buf' is null, we'll read and discard data until we've read * 'buflen' bytes. * * 'minlen' gives the minimum number of bytes desired. This can be * less than the buffer length, since we might not know in advance how * many bytes the client will be sending. We'll read up to the buffer * size, but we won't return until we read the minimum size (or * encounter an error or timeout). */ long read(char *buf, size_t buflen, long minlen, unsigned long timeout = OS_FOREVER); /* * Send data to our peer. This blocks until the request completes, but * we'll abort immediately if the 'quit' event in our listener fires. * Returns true on success, false on error or a 'quit' signal. */ int send(const char *buf, size_t len); int send(const char *buf) { return send(buf, strlen(buf)); } /* get the last send/receive error from the socket */ int last_error() const { return socket->last_error(); } /* * Get the state string. The caller must release the reference when * done with it. */ StringRef *get_state() const { /* make sure no one changes it under us while we're working */ mutex->lock(); /* make a copy for the caller */ StringRef *s = state; s->add_ref(); /* our copy is safe now */ mutex->unlock(); /* return the caller's copy */ return s; } /* set the current run state */ void set_run_state(const char *s) { /* set up a StringRef for the string */ StringRef *r = new StringRef(s); /* set it as the new state */ set_run_state(r); /* done with our reference to the new StringRef */ r->release_ref(); } void set_run_state(StringRef *s) { /* lock against concurrent access while working */ mutex->lock(); /* release the old state string, and save the new one */ if (state != 0) state->release_ref(); if ((state = s) != 0) s->add_ref(); /* done with the mutex */ mutex->unlock(); } /* close my socket */ void close_socket() { if (socket != 0) socket->close(); } protected: /* * Thread ID - this is a serial number set by the listener when we * start up, used for human-readable identification. It has no other * significance; in particular, it's NOT an operating system thread * handle or ID. */ int thread_id; /* * our connection socket - this is the two-way network channel we use * to communicate with our client */ OS_Socket *socket; /* our listener */ TadsListenerThread *listener; /* next thread in our listener's list of threads */ TadsServerThread *next_server; /* resource protection mutex */ OS_Mutex *mutex; /* current run state message */ StringRef *state; /* client IP address and port */ char *client_ip; int client_port; }; /* ------------------------------------------------------------------------ */ /* * HTTP server thread */ class TadsHttpServerThread: public TadsServerThread { public: /* construction */ TadsHttpServerThread(class TadsHttpListenerThread *l, TadsMessageQueue *q, long ulim, OS_Socket *s); /* deletion */ ~TadsHttpServerThread(); /* get my listener thread object */ TadsHttpListenerThread *get_listener() const { return (TadsHttpListenerThread *)listener; } /* process a request */ int process_request(); /* * Send a simple HTTP response. Returns true on success, false on * error. The status code and mime type strings are required. The * message body is optional; if it's null, we'll simply send the * headers with no body. If there is a message body, we'll send it * with a Content-Length header. The extra headers are optional; if * provided, this must be in standard header format, with each header * (INCLUDING the last one) terminated by a CR-LF (\r\n) sequence. If * this is null we'll only include a set of standard headers describing * the message body (content-type, content-length, cache-control, * connection). */ int send_simple(const char *status_code, const char *mime_type, const char *msg_body, size_t msg_len, const char *extra_headers); /* send a simple message body */ int send_simple(const char *status_code, const char *mime_type, const char *msg_body) { return send_simple(status_code, mime_type, msg_body, strlen(msg_body), 0); } /* send the contents of a file as the message body */ int send_file(const char *fname, const char *mime_type); protected: /* * Read until we reach the desired number of newlines. The states are * 0->base, 1->first CR, 2->first LF, 3->second CR, 4->second LF. To * stop after one contiguous newline, stop at state 2; to stop after * two newlines, stop at state 4. */ int read_to_nl(StringRef *dst, long startofs, int init_state, int end_state); /* the message queue */ TadsMessageQueue *queue; /* upload limit */ long upload_limit; }; /* ------------------------------------------------------------------------ */ /* * HTTP server listener */ class TadsHttpListenerThread: public TadsListenerThread { public: TadsHttpListenerThread(vm_obj_id_t srv_obj, class TadsMessageQueue *q, long upload_limit); /* our associated server thread class is TadsHttpServer */ virtual class TadsServerThread *create_server_thread(OS_Socket *s) { return new TadsHttpServerThread(this, queue, upload_limit, s); } /* * Get the HTTPServer object that created the listener. * * For thread safety, this routine should only be called from the main * VM thread. The garbage collector can delete the server object and * thus invalidate the value returned here. The GC runs in the main VM * thread, so it's always safe to interrogate this value from the main * thread. */ vm_obj_id_t get_server_obj() const { return srv_obj; } /* * Detach from the HTTPServer object. The HTTPServer calls this when * it's about to be deleted by the garbage collector. We have a * reference on the HTTPServer object, but we're not ourselves a * garbage-collected object, so our reference won't keep the HTTPServer * alive. It can thus be collected while we're still pointing to it. * To deal with this, the HTTPServer lets us know when it's about to be * deleted, so that we can clear our reference. * * This routine should only be called from the main VM thread. This is * designed to be called from the HTTPServer object's notify_delete() * method, which is called by the garbage collector, which always runs * in the main VM thread. */ void detach_server_obj() { srv_obj = VM_INVALID_OBJ; } protected: ~TadsHttpListenerThread(); /* the message queue we use to handle incoming requests */ class TadsMessageQueue *queue; /* upload limit for each incoming request */ long upload_limit; /* the HTTPServer object that owns the listener */ vm_obj_id_t srv_obj; }; /* ------------------------------------------------------------------------ */ /* * XML helper class */ class TadsXml { public: /* given an XML buffer, find the end of the <?XML?> prefix */ const char *strip_xml_header(const char *buf); }; /* ------------------------------------------------------------------------ */ /* * Message queue. Server threads handle requests by sending messages to * the main thread, via the message queue. This approach allows the * network connections to be handled by dedicated threads, while keeping * the byte-code program itself single-threaded. The queue handles the * communications between threads, and also serializes requests so that * they can be serviced by a single thread. */ /* * Base message object */ class TadsMessage: public CVmRefCntObj { public: TadsMessage(const char *typ, OS_Event *quit_evt) { /* remember the message type */ this->typ = typ; /* we're not in a queue yet */ nxt = 0; /* set up an event to signal completion of the message */ ev = new OS_Event(FALSE); completed = FALSE; /* remember the quit event */ if ((this->quit_evt = quit_evt) != 0) quit_evt->add_ref(); } virtual ~TadsMessage() { /* destroy our completion event */ ev->release_ref(); /* forget our quit event */ if (quit_evt != 0) quit_evt->release_ref(); } /* * wait for completion: returns true on success, false if the wait * failed (due to error, timeout, or Quit) */ int wait_for_completion(unsigned long timeout) { /* wait on the event, or the quit event */ OS_Waitable *w[] = { ev, quit_evt }; return OS_Waitable::multi_wait(quit_evt != 0 ? 2 : 1, w, timeout) == OSWAIT_EVENT + 0; } /* mark the message as completed */ void complete() { /* set the completion event */ ev->signal(); /* flag it internally */ completed = TRUE; } /* message type - use the name of the class */ const char *typ; /* next message in queue */ TadsMessage *nxt; /* * completion event - this is signaled when the message has been * processed by the recipient, and if a reply is expected, the reply is * available */ OS_Event *ev; /* * the 'quit' event - our owner signals this event to tell us to shut * down the server without doing any more processing */ OS_Event *quit_evt; /* flag: has completion been signaled yet? */ int completed; }; /* * Safely cast an event message */ #define cast_tads_message(cls, msg) \ (msg != 0 && strcmp((msg)->typ, #cls) == 0 ? (cls *)msg : 0) /* * Network Event message. This is the base class for messages used within * the TADS 3 getNetEvent() queue for sending messages to the byte-code * program. */ class TadsEventMessage: public TadsMessage { public: TadsEventMessage(OS_Event *quit_evt) : TadsMessage("TadsEventMessage", quit_evt) { } /* * Set up the event object for the byte-code program. This pushes the * constructor arguments onto the run-time stack, and returns the * appropriate NetEvent subclass object ID (from G_predef). * * '*argc' is to be filled in with the number of constructor arguments * pushed. Note that the caller will push one additional constructor * argument giving the request type code (which is common to all * NetEvent subclass constructors), so this routine doesn't have to * push that value. * * '*evt_code' is to be filled in with the event type code. This is an * integer giving the type of the event, as defined in the TADS header * include/tadsnet.h - see the NetEvXxx code list. * * Each server type must subclass this to generate the suitable * information for the request. */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_code) = 0; }; /* * Message queue. */ class TadsMessageQueue: public CVmRefCntObj { public: TadsMessageQueue(OS_Event *quit_evt) { /* create our mutex object */ mu = new OS_Mutex(); /* create our event - this signals when a message arrives */ ev = new OS_Event(FALSE); /* no messages in the queue yet */ head = tail = 0; /* remember my global quit event */ if ((this->quit_evt = quit_evt) != 0) quit_evt->add_ref(); } /* are we in the process of shutting down the server? */ int is_quitting() { return quit_evt != 0 && quit_evt->test(); } /* * Add a message to the queue, without waiting for completion. This is * a one-way send: posting effectively transfers ownership of the * reference on the message to the queue, and thence to the recipient. * The caller thus can't access the message after posting it - for * their purposes it's effectively gone after they send it. If the * caller does want to hang onto its own reference to the message after * sending it, just use add_ref() on the message (BEFORE posting, of * course, in keeping with the usual ref-count transaction rules). */ void post(TadsMessage *m) { /* synchronize on the mutex */ mu->lock(); /* link the message at the end of the queue */ if (tail != 0) tail->nxt = m; else head = m; tail = m; m->nxt = 0; /* * if the message doesn't have a 'quit' event, and we do, copy our * quit event to the message */ if (m->quit_evt == 0 && quit_evt != 0) (m->quit_evt = quit_evt)->add_ref(); /* signal that a message is available in the queue */ ev->signal(); /* done with the mutex */ mu->unlock(); } /* * Send a message: post it to the queue and wait for completion. * Returns true on success, false if the wait failed (due to error or * timeout). * * Unlike post(), the caller explicitly keeps its own reference to the * message with send(), because the message object also provides * storage for the reply (if any). */ int send(TadsMessage *m, unsigned long timeout) { /* * Add a reference to the message on behalf of our caller. The * caller's original reference is about to be transfered to the * queue (and later to the recipient) via post(), but the caller * also wants to hang on to the message, so it needs a second * reference for its own copy. */ m->add_ref(); /* post the message */ post(m); /* wait for completion */ return m->wait_for_completion(timeout); } /* * Wait for a message. Blocks until a message is available in the * queue, or the timeout expires. If there's a message, removes the * message from the queue and returns OSWAIT_EVENT. Otherwise, returns * another OSWAIT_xxx code indicating what happened. * * The message queue has a 'quit' event object that's used to signal a * general shutdown. If this event fires, we'll return OSWAIT_EVENT+1 * and fill in *msgp with null. * * The message queue also has a 'debug break' event that's used to * signal that the user wants to pause execution and break into the * debugger. If this event is signaled, we'll return OSWAIT_EVENT+2 * and fill in *msgp with null. * * If we return a message, the caller takes over our reference on it. * The caller is thus responsible for calling release_ref() when done * with the message object. */ int wait(VMG_ unsigned long timeout, TadsMessage **msgp); /* * Pull the next message out of the queue without waiting. The caller * assumes our reference on the message, so they must call Release() * when done with it. */ TadsMessage *get() { /* synchronize on the mutex */ mu->lock(); /* stash the first message for returning to the caller */ TadsMessage *m = head; /* pull it off the queue (if there's a message at all) */ if (m != 0) { head = head->nxt; if (head == 0) tail = 0; } /* done with the mutex */ mu->unlock(); /* return the message */ return m; } /* * Abandon a message: release any references the queue has on the given * message. This can be used if the sender times out waiting for a * response, to ensure that the queue won't keep the message in memory. */ void abandon(TadsMessage *msg) { /* lock the queue while working */ mu->lock(); /* scan the queue for this message */ TadsMessage *cur, *prv; for (cur = head, prv = 0 ; cur != 0 ; prv = cur, cur = cur->nxt) { /* if this is the message we're looking for, unlink it */ if (cur == msg) { /* unlink the message */ if (prv != 0) prv->nxt = cur->nxt; else head = cur->nxt; /* if it's the tail, move the tail back one */ if (cur == tail) tail = prv; /* release our reference on the message */ cur->release_ref(); /* no need to keep looking */ break; } } /* done with the queue lock */ mu->unlock(); } /* * Flush the queue: discard any messages in our list. */ void flush() { /* keep going until the queue is empty */ for (;;) { /* lock the queue, and pull the first element off the list */ mu->lock(); TadsMessage *m = head; /* if we got an element, unlink it */ if (m != 0) { head = m->nxt; if (head == 0) tail = 0; } /* unlock the queue */ mu->unlock(); /* if we're out of queue elements, we're done */ if (m == 0) break; /* release this message */ m->release_ref(); } } /* get my message arrival event */ OS_Event *get_event_obj() { return ev; } /* get my quit event object */ OS_Event *get_quit_evt() { return quit_evt; } protected: /* mutex for access to our statics */ OS_Mutex *mu; /* event for signaling a message arrival */ OS_Event *ev; /* * the server's general 'quit' event - the server signals this event to * tell message queues to shut down without further processing */ OS_Event *quit_evt; /* head/tail of message queue */ TadsMessage *head, *tail; ~TadsMessageQueue() { /* discard any messages still in the queue */ flush(); /* destroy our synchronization resources */ mu->release_ref(); ev->release_ref(); if (quit_evt != 0) quit_evt->release_ref(); } }; /* ------------------------------------------------------------------------ */ /* * HTTP request header. */ struct TadsHttpRequestHeader { /* * Initialize given the start of a header line; returns a pointer to * the start of the next header line. This modifies the buffer in * place: we add a null at the end of the header name, and we add a * null at the newline at the end of the header. */ TadsHttpRequestHeader(char *&p, int parse) { /* no next item in the list yet */ nxt = 0; /* the name is always at the start of the line */ name = p; /* presume we won't find a value - set it to blank */ value = ""; /* parse out the name and value, if desired */ if (parse) { /* * find the end of the header name - it ends at the first * whitespace or colon character */ for ( ; *p != '\0' && strchr(":\r\n \t", *p) == 0 ; ++p) ; /* check what we found */ if (*p == ':') { /* found the delimiting colon - null it out and skip spaces */ for (*p++ = '\0' ; isspace(*p) ; ++p) ; /* the value is the rest of the line */ value = p; } else if (isspace(*p)) { /* stopped at a space - null it out and skip any more spaces */ for (*p++ = '\0' ; isspace(*p) ; ++p) ; /* make sure we're at the colon */ if (*p == ':') { /* skip the colon and any subsequent spaces */ for (++p ; isspace(*p) ; ++p) ; /* the value starts here */ value = p; } } } /* find the end of the line */ for ( ; *p != '\0' && (*p != '\r' || *(p+1) != '\n') ; ++p) ; /* if we found the CR-LF, null-terminate and skip the pair */ if (*p == '\r' && *(p+1) == '\n') { *p = '\0'; p += 2; } } ~TadsHttpRequestHeader() { /* delete the rest of the list */ if (nxt != 0) delete nxt; } /* * Simplify multi-line headers into the single-line equivalents */ static void fix_multiline_headers(StringRef *hdrs, int start_ofs) { /* * Look for CR LF <space>+ <^space> sequences, and delete the line * breaks in each one. */ char *p, *dst, *endp = hdrs->getend(); for (dst = p = hdrs->get() + start_ofs ; p < endp ; ) { /* check for a line break */ if (*p == '\r' && *(p+1) == '\n') { /* * if the next line starts with whitespace, it's a * continuation line; otherwise it's a new header */ if (isspace(*(p+2) && *(p+2) != '\r' && *(p+2) != '\n')) { /* * it's a continuation line - replace the CR-LF with a * single space, then skip all of the whitespace at the * start of this line */ *dst++ = ' '; for (p += 2 ; isspace(*p) && *p != '\r' && *p != '\n' ; ++p) ; } else { /* it's not a continuation line, so copy it as-is */ *dst++ = *p++; *dst++ = *p++; } } else if (*p == '\r' || *p == '\n') { /* unpaired CR or LF - convert it to a space */ *dst++ = ' '; ++p; } else { /* ordinary character - copy it unchanged */ *dst++ = *p++; } } /* set the new size of the headers */ hdrs->truncate(dst - hdrs->get()); } /* parse headers from a StringRef starting at the given offset */ static void parse_headers(TadsHttpRequestHeader *&hdr_list, TadsHttpRequestHeader *&hdr_tail, int parse_first, StringRef *str, long ofs) { /* fix multiline headers */ fix_multiline_headers(str, ofs); /* set up at the starting offset */ char *p = str->get() + ofs; /* if we're already at a blank line, there are no headers */ if (*p == '\0' || *p == '\r' || *p == '\n') return; /* * if there aren't any headers yet, parse the first one; otherwise * we'll just add to the existing list */ if (hdr_list == 0) hdr_list = hdr_tail = new TadsHttpRequestHeader(p, parse_first); /* now parse until we find a blank line */ while (*p != '\r' && *p != '\n' && *p != '\0') hdr_tail = hdr_tail->nxt = new TadsHttpRequestHeader(p, TRUE); } /* find a header in the list following this item */ const char *find(const char *name) { /* scan the list, starting with this element */ for (TadsHttpRequestHeader *h = this ; h != 0 ; h = h->nxt) { /* if this name matches, return the value */ if (stricmp(h->name, name) == 0) return h->value; } /* didn't find a match */ return 0; } /* name - pointer into a buffer owned by the request object */ const char *name; /* value - pointer into a buffer owned by the request object */ const char *value; /* next header in list */ TadsHttpRequestHeader *nxt; }; /* ------------------------------------------------------------------------ */ /* * HTTP Request Message. */ class TadsHttpRequest: public TadsEventMessage { public: TadsHttpRequest(TadsHttpServerThread *t, const char *verb, size_t verb_len, StringRef *hdrs, TadsHttpRequestHeader *hdr_list, StringRef *body, int overflow, const char *resource_name, size_t res_name_len, OS_Event *quit_evt) : TadsEventMessage(quit_evt) { /* remember the server thread */ if ((this->thread = t) != 0) t->add_ref(); /* remember the verb and resource name */ this->verb = new StringRef(verb, verb_len); this->resource_name = new StringRef(resource_name, res_name_len); /* keep a copy of the headers */ this->headers = hdrs; hdrs->add_ref(); /* keep a copy of the body */ this->overflow = overflow; if ((this->body = body) != 0) body->add_ref(); /* take ownership of the parsed header list */ this->hdr_list = hdr_list; } ~TadsHttpRequest() { /* done with our thread and resource name string references */ resource_name->release_ref(); verb->release_ref(); thread->release_ref(); headers->release_ref(); delete hdr_list; if (body != 0) body->release_ref(); } /* mark the request as completed */ void complete(); /* * Prepare the event object. This creates an HTTPRequest object (the * intrinsic class representing an incoming HTTP request), and sets up * a NetRequestEvent (the byte-code object representing a request) to * wrap it. */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type); /* server thread */ TadsHttpServerThread *thread; /* the HTTP verb */ StringRef *verb; /* the request string */ StringRef *resource_name; /* buffer containing the headers, parsed into null-delimited strings */ StringRef *headers; /* list of starts of the headers in the buffer */ TadsHttpRequestHeader *hdr_list; /* message body, if any */ StringRef *body; /* * Flag: the message body exceeded the upload size limit. If this is * set, body is null, since we discard content that's too large. */ int overflow; }; /* ------------------------------------------------------------------------ */ /* * UI Close Event. In the stand-alone local interpreter configuration, * where the browser UI is an integrated part of the interpreter, this * event is sent when the user explicitly closes the UI window. In most * cases, the game will simply want to terminate when this happens, because * it indicates that the user has effectively dismissed the application. */ class TadsUICloseEvent: public TadsEventMessage { public: TadsUICloseEvent(OS_Event *quit_evt) : TadsEventMessage(quit_evt) { } virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type); }; #endif /* TADSNET */ #endif /* VMNET_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmcset.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000042545�11713617420�015543� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcset.cpp - T3 CharacterSet metaclass Function Notes Modified 06/06/01 MJRoberts - Creation */ #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmcset.h" #include "vmbif.h" #include "vmfile.h" #include "vmerrnum.h" #include "vmerr.h" #include "vmstack.h" #include "vmmeta.h" #include "vmrun.h" #include "charmap.h" #include "vmstr.h" #include "vmpredef.h" #include "vmrun.h" #include "vmhost.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassCharSet metaclass_reg_obj; CVmMetaclass *CVmObjCharSet::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjCharSet:: *CVmObjCharSet::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjCharSet::getp_undef, &CVmObjCharSet::getp_get_name, &CVmObjCharSet::getp_is_known, &CVmObjCharSet::getp_is_mappable, &CVmObjCharSet::getp_is_rt_mappable }; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjCharSet::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t *arg1; const char *charset_name; /* check our arguments */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the name of the character set */ arg1 = G_stk->get(0); charset_name = arg1->get_as_string(vmg0_); if (charset_name == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create the character set object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(vmg_ charset_name + VMB_LEN, vmb_get_len(charset_name)); /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjCharSet::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(); return id; } /* * Create with the given character set name */ vm_obj_id_t CVmObjCharSet::create(VMG_ int in_root_set, const char *charset_name, size_t charset_name_len) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjCharSet(vmg_ charset_name, charset_name_len); return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjCharSet::CVmObjCharSet(VMG_ const char *charset_name, size_t charset_name_len) { /* allocate and initialize our extension */ ext_ = 0; alloc_ext(vmg_ charset_name, charset_name_len); } /* * Allocate and initialize our extension */ void CVmObjCharSet::alloc_ext(VMG_ const char *charset_name, size_t charset_name_len) { size_t alloc_size; vmobj_charset_ext_t *extp; CResLoader *res_ldr; /* if we already have an extension, delete it */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* * compute the size we need - note that we use the one fixed byte of * the structure's name element as the extra byte we need for null * termination of the name */ alloc_size = sizeof(vmobj_charset_ext_t) + charset_name_len; /* allocate space for our extension structure */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alloc_size, this); /* cast the extension to our structure type */ extp = (vmobj_charset_ext_t *)ext_; /* store the character set name and length, null-terminating the name */ extp->charset_name_len = charset_name_len; memcpy(extp->charset_name, charset_name, charset_name_len); extp->charset_name[charset_name_len] = '\0'; /* presume we won't be able to load the character sets */ extp->to_local = 0; extp->to_uni = 0; /* get the resource loader */ res_ldr = G_host_ifc->get_sys_res_loader(); /* if we have a resource loader, load the mappings */ if (res_ldr != 0) { /* load the unicode-to-local mapping */ extp->to_local = CCharmapToLocal::load(res_ldr, extp->charset_name); /* load the local-to-unicode mapping */ extp->to_uni = CCharmapToUni::load(res_ldr, extp->charset_name); } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjCharSet::notify_delete(VMG_ int /*in_root_set*/) { /* release our mapper objects */ if (ext_ != 0) { /* release the to-local character mapper */ if (get_ext_ptr()->to_local != 0) get_ext_ptr()->to_local->release_ref(); /* release the to-unicode character mapper */ if (get_ext_ptr()->to_uni != 0) get_ext_ptr()->to_uni->release_ref(); /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjCharSet::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjCharSet::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjCharSet::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* initialize with the character set name from the image file */ alloc_ext(vmg_ ptr + VMB_LEN, vmb_get_len(ptr)); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjCharSet::save_to_file(VMG_ class CVmFile *fp) { /* write the name length */ fp->write_uint2(get_ext_ptr()->charset_name_len); /* write the bytes of the name */ fp->write_bytes(get_ext_ptr()->charset_name, get_ext_ptr()->charset_name_len); } /* * restore from a file */ void CVmObjCharSet::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { char buf[128]; size_t len; size_t read_len; /* read the length of the character set name */ len = fp->read_uint2(); /* limit the reading to the length of the buffer */ read_len = len; if (read_len > sizeof(buf)) read_len = sizeof(buf); /* read the name, up to the buffer length */ fp->read_bytes(buf, read_len); /* skip any bytes we couldn't fit in the buffer */ if (len > read_len) fp->set_pos(fp->get_pos() + len - read_len); /* initialize from the saved data */ alloc_ext(vmg_ buf, read_len); } /* ------------------------------------------------------------------------ */ /* * Compare for equality */ int CVmObjCharSet::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { CVmObjCharSet *other; const vmobj_charset_ext_t *ext; const vmobj_charset_ext_t *other_ext; /* if it's a self-reference, it's certainly equal */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* if it's not another character set, it's not equal */ if (val->typ != VM_OBJ || !is_charset(vmg_ val->val.obj)) return FALSE; /* we know it's another character set - cast it */ other = (CVmObjCharSet *)vm_objp(vmg_ val->val.obj); /* get my extension and the other extension */ ext = get_ext_ptr(); other_ext = other->get_ext_ptr(); /* it's equal if it has the same name (ignoring case) */ return (ext->charset_name_len == other_ext->charset_name_len && memicmp(ext->charset_name, other_ext->charset_name, ext->charset_name_len) == 0); } /* * Calculate a hash value */ uint CVmObjCharSet::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { uint hash; size_t rem; const char *p; /* add up the bytes in the array */ for (hash = 0, rem = get_ext_ptr()->charset_name_len, p = get_ext_ptr()->charset_name ; rem != 0 ; --rem, ++p) { /* add this character into the hash */ hash += *p; } /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the character set name */ int CVmObjCharSet::getp_get_name(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* create a new string for the name */ retval->set_obj(CVmObjString::create(vmg_ FALSE, get_ext_ptr()->charset_name, get_ext_ptr()->charset_name_len)); /* handled */ return TRUE; } /* * property evaluator - is known */ int CVmObjCharSet::getp_is_known(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * it's known if both of our character mappers are non-null; if either * is null, the character set is not known on this platform */ retval->set_logical(get_ext_ptr()->to_local != 0 && get_ext_ptr()->to_uni != 0); /* handled */ return TRUE; } /* * property evaluator - check a character or a string for mappability */ int CVmObjCharSet::getp_is_mappable(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t arg; const char *str; CCharmapToLocal *to_local; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the local mapping */ to_local = get_to_local(vmg0_); /* get the argument and check what type we have */ G_stk->pop(&arg); if (to_local == 0) { /* no mapping is available, so it's not mappable */ retval->set_nil(); } else if ((str = arg.get_as_string(vmg0_)) != 0) { size_t len; utf8_ptr p; /* get the length and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* presume every character will be mappable */ retval->set_true(); /* check each character for mappability */ for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* check to see if this character is mappable */ if (!to_local->is_mappable(p.getch())) { /* * The character isn't mappable - this is an * all-or-nothing check, so if one isn't mappable we * return false. Set the nil return and stop looking. */ retval->set_nil(); break; } } } else if (arg.typ == VM_INT) { /* * Check if the integer character value is mappable. If it's out * of the 16-bit unicode range (0..0xffff), it's not mappable; * otherwise, ask the character mapper. */ if (arg.val.intval < 0 || arg.val.intval > 0xffff) { /* it's out of the valid unicode range, so it's not mappable */ retval->set_nil(); } else { /* ask the character mapper */ retval->set_logical(to_local->is_mappable( (wchar_t)arg.val.intval)); } } /* handled */ return TRUE; } /* * property evaluator - check a character or a string to see if it has a * round-trip mapping. A round-trip mapping is one where the unicode * characters can be mapped to the local character set, then back to * unicode, yielding the exact original unicode string. */ int CVmObjCharSet::getp_is_rt_mappable(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t arg; const char *str; CCharmapToLocal *to_local; CCharmapToUni *to_uni; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the local and unicode mappings */ to_local = get_to_local(vmg0_); to_uni = get_to_uni(vmg0_); /* get the argument and check what type we have */ G_stk->pop(&arg); if (to_local == 0 || to_uni == 0) { /* no character sets, so it's not mappable */ retval->set_nil(); } else if ((str = arg.get_as_string(vmg0_)) != 0) { size_t len; utf8_ptr p; /* get the length and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* presume every character will be mappable */ retval->set_true(); /* check each character for mappability */ for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* check for round-trip mappability */ if (!is_rt_mappable(p.getch(), to_local, to_uni)) { /* nope - return false */ retval->set_nil(); break; } } } else if (arg.typ == VM_INT) { /* check the integer character for mappability */ if (arg.val.intval < 0 || arg.val.intval > 0xffff) { /* it's out of the valid unicode range, so it's not mappable */ retval->set_nil(); } else { /* ask the character mapper */ retval->set_logical(is_rt_mappable( (wchar_t)arg.val.intval, to_local, to_uni)); } } /* handled */ return TRUE; } /*------------------------------------------------------------------------ */ /* * Determine if a character has a round-trip mapping. */ int CVmObjCharSet::is_rt_mappable(wchar_t c, CCharmapToLocal *to_local, CCharmapToUni *to_uni) { char lclbuf[16]; char unibuf[16]; size_t lcllen; size_t unilen; char *p; /* if there's no local mapping, it's obviously not mappable */ if (!to_local->is_mappable(c)) return FALSE; /* * If there's an expansion in the mapping to the local set, then there * can't be a round-trip mapping. Expansions are inherently one-way * because they produce multiple local characters for a single unicode * character, and the reverse mapping has no way to group those * multiple local characters back into a single unicode character. */ if (to_local->get_expansion(c, &lcllen) != 0) return FALSE; /* get the local mapping */ lcllen = to_local->map_char(c, lclbuf, sizeof(lclbuf)); /* map it back to unicode */ p = unibuf; unilen = sizeof(unibuf); unilen = to_uni->map(&p, &unilen, lclbuf, lcllen); /* * if the unicode mapping is one character that exactly matches the * original input character, then we have a valid round-trip mapping */ return (unilen == utf8_ptr::s_wchar_size(c) && utf8_ptr::s_getch(unibuf) == c); } /*------------------------------------------------------------------------ */ /* * Get the unicode-to-local character set mapper */ CCharmapToLocal *CVmObjCharSet::get_to_local(VMG0_) const { /* if there's no mapper, throw an exception */ if (get_ext_ptr()->to_local == 0) { /* throw an UnknownCharacterSetException */ G_interpreter->throw_new_class(vmg_ G_predef->charset_unknown_exc, 0, "unknown character set"); } /* return the mapper */ return get_ext_ptr()->to_local; } /* * Get the local-to-unicode character set mapper */ CCharmapToUni *CVmObjCharSet::get_to_uni(VMG0_) const { /* if there's no mapper, throw an exception */ if (get_ext_ptr()->to_uni == 0) { /* throw an UnknownCharacterSetException */ G_interpreter->throw_new_class(vmg_ G_predef->charset_unknown_exc, 0, "unknown character set"); } /* return the mapper */ return get_ext_ptr()->to_uni; } �����������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmbytarr.h���������������������������������������������������������������������0000644�0001750�0000144�00000041611�11723015550�015543� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbytarr.h - T3 ByteArray metaclass Function Notes Modified 06/05/01 MJRoberts - Creation */ #ifndef VMBYTARR_H #define VMBYTARR_H #include <stdlib.h> #include "os.h" #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * A ByteArray is simply an array of byte values. This class provides a * simple, fast mechanism to store blocks of binary data, so it is not a * subclass of Array and is not a Collection. * * The image file data for a byte array is simple: * * UINT4 number of bytes *. BYTE bytes[1..number_of_bytes] * * Internally, we store the array data in chunks of 32k each. Our * extension is a first-level page table, pointing to the chunks: * * UINT4 number of elements *. unsigned char **page0 *. unsigned char **page1 *. ... * * Each pageN pointer points to a second-level page table, which consists * of (up to) 8192 pointers to the actual pages. Since a page is 32k, and * we can store 8k pointers per second-level table, each second-level * table is capable of referencing 256MB. By design, we can store up to * 4GB, so we need at most 16 second-level tables. * * The extension is allocated according to the actual number of * second-level tables we require for the element count. Each * second-level page is allocated to 8192*sizeof(char *), except the last * second-level page, which is allocated to N*sizeof(char *) where N is * the number of elements required in the last second-level table. Each * page is allocated to 32K bytes, except the last, which is allocated to * the actual size needed. * * To access an element at index i, we calculate s1 (the page table * selector) as i/(32k*8k) == i/256M; s2 (the page selector within the * selected page table) as (i%256M)/32k; and s3 (the byte selector within * the page) as i%32k. The byte is then accessed as * * page[s1][s2][s3] */ class CVmObjByteArray: public CVmObject { friend class CVmMetaclassByteArray; friend class bytearray_undo_rec; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other objects and cannot ourselves be converted * to constant data, so there's nothing to do here */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no data and cannot be converted to constant data, * so there's nothing to do */ } /* cast to string - assumes the bytes as Latin-1 characters */ virtual const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const; /* create an array with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create an array with a given number of elements */ static vm_obj_id_t create(VMG_ int in_root_set, unsigned long element_count); /* create an array by mapping a string through a character set */ static vm_obj_id_t create_from_string( VMG_ const vm_val_t *strval, const char *str, const vm_val_t *mapval); /* create from binary data */ static vm_obj_id_t create_from_bytes( VMG_ int in_root_set, const char *buf, size_t len); /* * determine if an object is a byte array - it is if the object's * virtual metaclass registration index matches the class's static * index */ static int is_byte_array(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *rec); void discard_undo(VMG_ struct CVmUndoRecord *); /* our data are just bytes - we reference nothing */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } void mark_refs(VMG_ uint /*state*/) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* reload from the image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* index the array */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set an indexed element of the array */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Check a value for equality. We will match another byte array with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the array */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * assume that we've been changed since loading, if we came from the * image file */ int is_changed_since_load() const { return TRUE; } /* get the number of elements in the array */ unsigned long get_element_count() const { return t3rp4u(get_ext_ptr()); } /* get the byte at a given 1-based index */ unsigned char get_element(unsigned long idx) const { size_t avail; return *get_ele_ptr(idx, &avail); } /* * construction: copy (without undo) bytes from a buffer into the byte * array */ void cons_copy_from_buf(const unsigned char *buf, unsigned long idx, size_t cnt) { /* copy the bytes into our array */ copy_from_buf(buf, idx, cnt); } /* * Write the specified region of the array to a file. Returns zero on * success, non-zero on failure. */ int write_to_file(class CVmDataSource *fp, unsigned long start_idx, unsigned long len) const; /* * Read bytes from a file into a region of the array, replacing * existing bytes in the array; saves undo for the change. Returns * the number of bytes actually read from the file, which will be less * than the number of bytes requested if we reach the end of the file * before satisfying the request. */ unsigned long read_from_file(VMG_ vm_obj_id_t self, class CVmDataSource *fp, unsigned long start_idx, unsigned long len, int save_undo); /* * write to a 'data' mode file - returns zero on success, non-zero on * failure */ int write_to_data_file(class CVmDataSource *fp); /* * read from a 'data' mode file, creating a new ByteArray object to * hold the bytes from the file */ static int read_from_data_file(VMG_ vm_val_t *retval, class CVmDataSource *fp); /* * Copy bytes into a buffer, starting at the given 1-based index. * Returns the number of bytes actually copied (this might be less than * the requested size, because the ByteArray might not have enough * contents to fill the request). */ size_t copy_to_buf(unsigned char *buf, unsigned long idx, size_t cnt) const; /* * Copy bytes from a buffer into the array at the given 1-based index, * saving undo. Returns the actual number of bytes copied, which might * be less than the requested size, since the ByteArray might not be * big enough to accommodate the write. */ size_t copy_from_buf_undo(VMG_ vm_obj_id_t self, const unsigned char *buf, unsigned long idx, size_t cnt); protected: /* load image data */ virtual void load_image_data(VMG_ const char *ptr, size_t siz); /* create a list with no initial contents */ CVmObjByteArray() { ext_ = 0; } /* * create a list with a given number of elements, for construction * of the list element-by-element */ CVmObjByteArray(VMG_ unsigned long byte_count); /* get a pointer to my extension */ const char *get_ext_ptr() const { return ext_; } /* * get my extension data pointer for construction purposes - this is * a writable pointer, so that a caller can fill our data buffer * during construction */ char *cons_get_ext_ptr() const { return ext_; } /* allocate space for the array, given the number of elements */ void alloc_array(VMG_ unsigned long element_count); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - length */ int getp_length(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - subarray */ int getp_subarray(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - copy from another byte array */ int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - fill with a value */ int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - convert to string */ int getp_to_string(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - read integer */ int getp_read_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - write integer */ int getp_write_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - packBytes */ int getp_packBytes(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - unpackBytes */ int getp_unpackBytes(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - static ByteArray.unpackBytes */ static int static_packBytes(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - sha256 */ int getp_sha256(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - digestMD5 */ int getp_digestMD5(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * Given a 1-based index, get a pointer to the byte at the index, and * the number of contiguous bytes available starting with that byte. * The available byte count doesn't take into account a short last * page, but simply returns the maximum number of bytes that would be * available on the page if it were allocated to full size; the caller * is responsible for ensuring that there is no reading or writing * past the end of the array. */ unsigned char *get_ele_ptr(unsigned long idx, size_t *bytes_avail) const { size_t s1; size_t s2; size_t s3; /* convert to a zero-based index */ --idx; /* * calculate the page table index - since each page holds 32k * bytes and each page table points to 8k pages, divide by 32k*8k * == 2^15*2^13 == 2^28 */ s1 = idx >> 28; /* * calculate the page index within the page table - each page * holds 32k, so calculate the excess from the page table selector * (i.e, idx % 32k*8k) and then divide by 32k == 2^15 */ s2 = (idx & 0x0FFFFFFF) >> 15; /* * calculate the page offset - this is simply the excess from the * page index */ s3 = idx & 0x7FFF; /* * Each page holds 32k, so the number of contiguous bytes starting * at this byte is 32k less the index. */ *bytes_avail = (32*1024) - s3; /* * dereference the extension to get the page table, deference the * page table to get the page, and index the page by the offset */ return get_page_table_ptr(s1)[s2] + s3; } /* * Given a page table selector, return a pointer to the selected page * table. */ unsigned char **get_page_table_ptr(size_t s) const { return get_page_table_array()[s]; } /* * Get a pointer to the page table array */ unsigned char ***get_page_table_array() const { /* the page table array starts after the element count */ return (unsigned char ***)(ext_ + 4); } /* fill the given (1-based index) range with the given byte value */ void fill_with(unsigned char val, unsigned long start_idx, unsigned long cnt); /* copy bytes from another byte array into this one */ void copy_from(unsigned long dst_idx, CVmObjByteArray *src_arr, unsigned long src_start_idx, unsigned long cnt); /* move bytes within this array */ void move_bytes(unsigned long dst_idx, unsigned long src_idx, unsigned long cnt); /* copy bytes from a buffer into the array */ void copy_from_buf(const unsigned char *buf, unsigned long idx, size_t cnt); /* map to a string */ size_t map_to_string(unsigned long idx, unsigned long len, class CVmObjString *str, size_t str_len, class CCharmapToUni *mapper); /* save undo for a change to a range of the array */ void save_undo(VMG_ vm_obj_id_t self, unsigned long start_idx, unsigned long cnt); /* set the number of bytes in the array */ void set_element_count(unsigned long cnt) { oswp4(cons_get_ext_ptr(), cnt); } /* property evaluation function table */ static int (CVmObjByteArray::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassByteArray: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "bytearray/030002"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjByteArray(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjByteArray(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjByteArray::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjByteArray:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMBYTARR_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjByteArray) �����������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/tct3prg.cpp��������������������������������������������������������������������0000644�0001750�0000144�00000017174�12010750337�015624� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCT3STM.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3stm.cpp - TADS 3 Compiler - T3 VM Code Generator - program-level classes Function Generate code for the T3 VM. This file contains program-level classes, in order to segregate the code generation classes required for the full compiler from those required for subsets that require only expression parsing (such as debuggers). The program-level classes are for statements at the outer level, outside of executable code: object definitions, dictionary declarations, etc. This also includes code to read and write object files. Notes Modified 05/08/99 MJRoberts - Creation */ #include <stdio.h> #include "t3std.h" #include "os.h" #include "tcprs.h" #include "tct3.h" #include "tcgen.h" #include "vmtype.h" #include "vmwrtimg.h" #include "vmfile.h" #include "tcmain.h" #include "tcerr.h" /* ------------------------------------------------------------------------ */ /* * Object Definition Statement */ /* * given the offset of the start of an object in the compiled object * stream, get the offset of the first property */ ulong CTPNStmObject::get_stream_first_prop_ofs(CTcDataStream *stream, ulong obj_ofs) { /* * the property table follows the superclass table, which follows * the tads-object header; the superclass table contains 4 bytes per * superclass (we can obtain the superclass count from the stream * data) */ return obj_ofs + TCT3_TADSOBJ_SC_OFS + (get_stream_sc_cnt(stream, obj_ofs) * 4); } /* * given the offset of the start of an object in the compiled object * stream, get the offset of the property at the given index */ ulong CTPNStmObject::get_stream_prop_ofs(CTcDataStream *stream, ulong obj_ofs, uint idx) { /* * calculate the offset to the selected property from the start of * the property table */ return get_stream_first_prop_ofs(stream, obj_ofs) + (TCT3_TADSOBJ_PROP_SIZE * idx); } /* * given the offset of the start of an object in the compiled object * stream, get the number of properties in the stream data */ uint CTPNStmObject::get_stream_prop_cnt(CTcDataStream *stream, ulong obj_ofs) { /* the property count is at offset 2 in the tads-object header */ return stream->readu2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 2); } /* * given the offset of the start of an object in the compiled object * stream, set the number of properties in the stream data, adjusting * the data size in the metaclass header to match */ size_t CTPNStmObject::set_stream_prop_cnt(CTcDataStream *stream, ulong obj_ofs, uint prop_cnt) { size_t data_size; /* the property count is at offset 2 in the tads-object header */ stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 2, prop_cnt); /* * calculate the new data size to store in the metaclass header -- * this is the size of the tads-object header, plus the size of the * superclass table (4 bytes per superclass), plus the size of the * property table */ data_size = TCT3_TADSOBJ_HEADER_SIZE + (get_stream_sc_cnt(stream, obj_ofs) * 4) + (prop_cnt * TCT3_TADSOBJ_PROP_SIZE); /* write the data size to the metaclass header (it's at offset 4) */ stream->write2_at(obj_ofs + TCT3_META_HEADER_OFS + 4, data_size); /* return the new data size */ return data_size; } /* * Get the object flags from an object in a compiled stream */ uint CTPNStmObject::get_stream_obj_flags(CTcDataStream *stream, ulong obj_ofs) { /* the flags are at offset 4 in the tads-object header */ return stream->read2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 4); } /* * Set the object flags in an object in a compiled stream */ void CTPNStmObject::set_stream_obj_flags(CTcDataStream *stream, ulong obj_ofs, uint flags) { /* * write the new flags - they're at offset 4 in the tads-object * header */ stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 4, flags); } /* * given the offset of the start of an object in the compiled object * stream, get the number of superclasses in the stream data */ uint CTPNStmObject::get_stream_sc_cnt(CTcDataStream *stream, ulong obj_ofs) { /* the superclass count is at offset 0 in the tads-object header */ return stream->readu2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 0); } /* * given the stream offset of the start of an object in the compiled * object stream, change a superclass object ID */ void CTPNStmObject::set_stream_sc(CTcDataStream *stream, ulong obj_ofs, uint sc_idx, vm_obj_id_t new_sc) { /* * set the superclass - it's at offset 6 in the object data, plus * four bytes (UINT4) per index slot */ stream->write2_at(obj_ofs + TCT3_TADSOBJ_HEADER_OFS + 6 + (sc_idx * 4), new_sc); } /* * given the offset of the start of an object in the compiled object * stream, get the property ID of the property at a given index in the * property table */ vm_prop_id_t CTPNStmObject::get_stream_prop_id(CTcDataStream *stream, ulong obj_ofs, uint prop_idx) { ulong prop_ofs; /* get the property's data offset */ prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx); /* read the property ID - it's at offset 0 in the property data */ return (vm_prop_id_t)stream->readu2_at(prop_ofs); } /* * given the offset of the start of an object in the compiled object * stream, get the datatype of the property at the given index in the * property table */ vm_datatype_t CTPNStmObject:: get_stream_prop_type(CTcDataStream *stream, ulong obj_ofs, uint prop_idx) { ulong dh_ofs; /* get the property's data holder offset */ dh_ofs = get_stream_prop_val_ofs(stream, obj_ofs, prop_idx); /* the type is the first byte of the serialized data holder */ return (vm_datatype_t)stream->get_byte_at(dh_ofs); } /* * given the offset of the start of an object in the compiled object * stream, get the stream offset of the serialized DATAHOLDER structure * for the property at the given index in the property table */ ulong CTPNStmObject::get_stream_prop_val_ofs(CTcDataStream *stream, ulong obj_ofs, uint prop_idx) { ulong prop_ofs; /* get the property's data offset */ prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx); /* the data holder immediately follows the (UINT2) property ID */ return prop_ofs + 2; } /* * given the offset of the start of an object in the compiled object * stream, get the property ID of the property at a given index in the * property table */ void CTPNStmObject::set_stream_prop_id(CTcDataStream *stream, ulong obj_ofs, uint prop_idx, vm_prop_id_t new_id) { ulong prop_ofs; /* get the property's data offset */ prop_ofs = get_stream_prop_ofs(stream, obj_ofs, prop_idx); /* set the data */ stream->write2_at(prop_ofs, (uint)new_id); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmop.h�������������������������������������������������������������������������0000644�0001750�0000144�00000034072�11730222544�014662� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmop.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmop.h - T3 VM Opcodes Function Notes Modified 11/14/98 MJRoberts - Creation */ #ifndef VMOP_H #define VMOP_H /* * T3 VM opcode definitions */ class CVmOpcodes { public: /* * Opcode size table. Index by opcode; each entry gives the size in * bytes of the instruction. A value of 0 is special - it means that * the instruction is varying-length. */ static const uchar op_siz[]; /* * Get the size in bytes of an opcode. This computes the actual size * of varying-length instructions. */ static size_t get_op_size(const uchar *op); }; #define OPC_PUSH_0 0x01 /* push constant integer 0 */ #define OPC_PUSH_1 0x02 /* push constant integer 1 */ #define OPC_PUSHINT8 0x03 /* push SBYTE operand as integer */ #define OPC_PUSHINT 0x04 /* push INT4 operand as integer */ #define OPC_PUSHSTR 0x05 /* push UINT4 operand as string constant */ #define OPC_PUSHLST 0x06 /* push UINT4 operand as list constant */ #define OPC_PUSHOBJ 0x07 /* push UINT4 operand as object ID */ #define OPC_PUSHNIL 0x08 /* push nil */ #define OPC_PUSHTRUE 0x09 /* push true */ #define OPC_PUSHPROPID 0x0A /* push UINT2 operand as property ID */ #define OPC_PUSHFNPTR 0x0B /* push UINT4 code offset */ #define OPC_PUSHSTRI 0x0C /* push inline string constant */ #define OPC_PUSHPARLST 0x0D /* push varargs parameter list */ #define OPC_MAKELSTPAR 0x0E /* push varargs parameter from list */ #define OPC_PUSHENUM 0x0F /* push an enum value */ #define OPC_PUSHBIFPTR 0x10 /* push a pointer to a built-in function */ #define OPC_NEG 0x20 /* negate */ #define OPC_BNOT 0x21 /* bitwise NOT */ #define OPC_ADD 0x22 /* add */ #define OPC_SUB 0x23 /* subtract */ #define OPC_MUL 0x24 /* multiply */ #define OPC_BAND 0x25 /* bitwise AND */ #define OPC_BOR 0x26 /* bitwise OR */ #define OPC_SHL 0x27 /* shift left */ #define OPC_ASHR 0x28 /* arithmetic shift right */ #define OPC_XOR 0x29 /* bitwise/logical XOR */ #define OPC_DIV 0x2A /* divide */ #define OPC_MOD 0x2B /* MOD (remainder) */ #define OPC_NOT 0x2C /* logical NOT */ #define OPC_BOOLIZE 0x2D /* convert top of stack to true/nil */ #define OPC_INC 0x2E /* increment value at top of stack */ #define OPC_DEC 0x2F /* decrement value at top of stack */ #define OPC_LSHR 0x30 /* logical shift right */ #define OPC_EQ 0x40 /* equals */ #define OPC_NE 0x41 /* not equals */ #define OPC_LT 0x42 /* less than */ #define OPC_LE 0x43 /* less than or equal to */ #define OPC_GT 0x44 /* greater than */ #define OPC_GE 0x45 /* greater than or equal to */ #define OPC_RETVAL 0x50 /* return with value at top of stack */ #define OPC_RETNIL 0x51 /* return nil */ #define OPC_RETTRUE 0x52 /* return true */ #define OPC_RET 0x54 /* return with no value */ #define OPC_NAMEDARGPTR 0x56 /* pointer to named argument table */ #define OPC_NAMEDARGTAB 0x57 /* named argument table */ #define OPC_CALL 0x58 /* function call */ #define OPC_PTRCALL 0x59 /* function call through pointer */ #define OPC_GETPROP 0x60 /* get property */ #define OPC_CALLPROP 0x61 /* call property with arguments */ #define OPC_PTRCALLPROP 0x62 /* call property through pointer with args */ #define OPC_GETPROPSELF 0x63 /* get property of 'self' */ #define OPC_CALLPROPSELF 0x64 /* call method of 'self' */ #define OPC_PTRCALLPROPSELF 0x65 /* call method of 'self' through pointer */ #define OPC_OBJGETPROP 0x66 /* get property of specific object */ #define OPC_OBJCALLPROP 0x67 /* call method of specific object */ #define OPC_GETPROPDATA 0x68 /* get property, disallowing side effects */ #define OPC_PTRGETPROPDATA 0x69 /* get prop through pointer, data only */ #define OPC_GETPROPLCL1 0x6A /* get property of local variable */ #define OPC_CALLPROPLCL1 0x6B /* call property of local variable */ #define OPC_GETPROPR0 0x6C /* get property of R0 */ #define OPC_CALLPROPR0 0x6D /* call property of R0 */ #define OPC_INHERIT 0x72 /* inherit from superclass */ #define OPC_PTRINHERIT 0x73 /* inherit through property pointer */ #define OPC_EXPINHERIT 0x74 /* inherit from an explicit superclass */ #define OPC_PTREXPINHERIT 0x75 /* inherit from explicit sc through prop ptr */ #define OPC_VARARGC 0x76 /* modifier: next call is var arg count */ #define OPC_DELEGATE 0x77 /* delegate to object on stack */ #define OPC_PTRDELEGATE 0x78 /* delegate through property pointer */ #define OPC_SWAP2 0x7A /* swap top two elements with next two */ #define OPC_SWAPN 0x7B /* swap elements at operand indices */ #define OPC_GETARGN0 0x7C /* get argument #0 */ #define OPC_GETARGN1 0x7D /* get argument #1 */ #define OPC_GETARGN2 0x7E /* get argument #2 */ #define OPC_GETARGN3 0x7F /* get argument #3 */ #define OPC_GETLCL1 0x80 /* push a local variable */ #define OPC_GETLCL2 0x81 /* push a local (2-byte index) */ #define OPC_GETARG1 0x82 /* push an argument */ #define OPC_GETARG2 0x83 /* push an argument (2-byte index) */ #define OPC_PUSHSELF 0x84 /* push 'self' */ #define OPC_GETDBLCL 0x85 /* push debug frame local */ #define OPC_GETDBARG 0x86 /* push debug frame argument */ #define OPC_GETARGC 0x87 /* get current argument count */ #define OPC_DUP 0x88 /* duplicate top of stack */ #define OPC_DISC 0x89 /* discard top of stack */ #define OPC_DISC1 0x8A /* discard n items from stack */ #define OPC_GETR0 0x8B /* push the R0 register onto the stack */ #define OPC_GETDBARGC 0x8C /* push debug frame argument count */ #define OPC_SWAP 0x8D /* swap top two stack elements */ #define OPC_PUSHCTXELE 0x8E /* push a method context value */ #define PUSHCTXELE_TARGPROP 0x01 /* push target property */ #define PUSHCTXELE_TARGOBJ 0x02 /* push target object */ #define PUSHCTXELE_DEFOBJ 0x03 /* push defining object */ #define PUSHCTXELE_INVOKEE 0x04 /* push the invokee */ #define OPC_DUP2 0x8F /* duplicate the top two stack elements */ #define OPC_SWITCH 0x90 /* jump through case table */ #define OPC_JMP 0x91 /* unconditional branch */ #define OPC_JT 0x92 /* jump if true */ #define OPC_JF 0x93 /* jump if false */ #define OPC_JE 0x94 /* jump if equal */ #define OPC_JNE 0x95 /* jump if not equal */ #define OPC_JGT 0x96 /* jump if greater than */ #define OPC_JGE 0x97 /* jump if greater or equal */ #define OPC_JLT 0x98 /* jump if less than */ #define OPC_JLE 0x99 /* jump if less than or equal */ #define OPC_JST 0x9A /* jump and save if true */ #define OPC_JSF 0x9B /* jump and save if false */ #define OPC_LJSR 0x9C /* local jump to subroutine */ #define OPC_LRET 0x9D /* local return from subroutine */ #define OPC_JNIL 0x9E /* jump if nil */ #define OPC_JNOTNIL 0x9F /* jump if not nil */ #define OPC_JR0T 0xA0 /* jump if R0 is true */ #define OPC_JR0F 0xA1 /* jump if R0 is false */ #define OPC_ITERNEXT 0xA2 /* iterator next */ #define OPC_GETSETLCL1R0 0xA3 /* set local from R0 and leave value on stack */ #define OPC_GETSETLCL1 0xA4 /* set local and leave value on stack */ #define OPC_DUPR0 0xA5 /* push R0 twice */ #define OPC_GETSPN 0xA6 /* get stack element at given index */ #define OPC_GETLCLN0 0xAA /* get local #0 */ #define OPC_GETLCLN1 0xAB /* get local #1 */ #define OPC_GETLCLN2 0xAC /* get local #2 */ #define OPC_GETLCLN3 0xAD /* get local #3 */ #define OPC_GETLCLN4 0xAE /* get local #4 */ #define OPC_GETLCLN5 0xAF /* get local #5 */ #define OPC_SAY 0xB0 /* display a constant string */ #define OPC_BUILTIN_A 0xB1 /* call built-in func from set 0 */ #define OPC_BUILTIN_B 0xB2 /* call built-in from set 1 */ #define OPC_BUILTIN_C 0xB3 /* call built-in from set 2 */ #define OPC_BUILTIN_D 0xB4 /* call built-in from set 3 */ #define OPC_BUILTIN1 0xB5 /* call built-in from any set, 8-bit index */ #define OPC_BUILTIN2 0xB6 /* call built-in from any set, 16-bit index */ #define OPC_CALLEXT 0xB7 /* call external function */ #define OPC_THROW 0xB8 /* throw an exception */ #define OPC_SAYVAL 0xB9 /* display the value at top of stack */ #define OPC_INDEX 0xBA /* index a list */ #define OPC_IDXLCL1INT8 0xBB /* index a local variable by an int8 value */ #define OPC_IDXINT8 0xBC /* index by an int8 value */ #define OPC_NEW1 0xC0 /* create new object instance */ #define OPC_NEW2 0xC1 /* create new object (2-byte operands) */ #define OPC_TRNEW1 0xC2 /* create new transient instance */ #define OPC_TRNEW2 0xC3 /* create transient object (2-byte operands) */ #define OPC_INCLCL 0xD0 /* increment local variable by 1 */ #define OPC_DECLCL 0xD1 /* decrement local variable by 1 */ #define OPC_ADDILCL1 0xD2 /* add immediate 1-byte int to local */ #define OPC_ADDILCL4 0xD3 /* add immediate 4-byte int to local */ #define OPC_ADDTOLCL 0xD4 /* add value to local variable */ #define OPC_SUBFROMLCL 0xD5 /* subtract value from local variable */ #define OPC_ZEROLCL1 0xD6 /* set local to zero */ #define OPC_ZEROLCL2 0xD7 /* set local to zero */ #define OPC_NILLCL1 0xD8 /* set local to nil */ #define OPC_NILLCL2 0xD9 /* set local to nil */ #define OPC_ONELCL1 0xDA /* set local to numeric value 1 */ #define OPC_ONELCL2 0xDB /* set local to numeric value 1 */ #define OPC_SETLCL1 0xE0 /* set local (1-byte local number) */ #define OPC_SETLCL2 0xE1 /* set local (2-byte local number) */ #define OPC_SETARG1 0xE2 /* set parameter (1-byte param number) */ #define OPC_SETARG2 0xE3 /* set parameter (2-byte param number) */ #define OPC_SETIND 0xE4 /* set value at index */ #define OPC_SETPROP 0xE5 /* set property in object */ #define OPC_PTRSETPROP 0xE6 /* set property through prop pointer */ #define OPC_SETPROPSELF 0xE7 /* set property in self */ #define OPC_OBJSETPROP 0xE8 /* set property in immediate object */ #define OPC_SETDBLCL 0xE9 /* set debugger local variable */ #define OPC_SETDBARG 0xEA /* set debugger parameter variable */ #define OPC_SETSELF 0xEB /* set 'self' */ #define OPC_LOADCTX 0xEC /* load method context from stack */ #define OPC_STORECTX 0xED /* store method context and push on stack */ #define OPC_SETLCL1R0 0xEE /* set local (1-byte local number) from R0 */ #define OPC_SETINDLCL1I8 0xEF /* set indexed local */ #define OPC_BP 0xF1 /* debugger breakpoint */ #define OPC_NOP 0xF2 /* no operation */ #endif /* VMOP_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmlst.h������������������������������������������������������������������������0000644�0001750�0000144�00000073614�11773413225�015060� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMLST.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlst.h - VM dynamic list implementation Function Notes Modified 10/29/98 MJRoberts - Creation */ #ifndef VMLST_H #define VMLST_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmstack.h" #include "vmrun.h" class CVmObjList: public CVmObjCollection { friend class CVmMetaclassList; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * Create dynamically from parameters in the stack; we do not remove * the elements from the stack, but simply create a list from the * parameters. 'idx' is the parameter index of the first parameter, * and 'cnt' is the number of parameters to use. */ static vm_obj_id_t create_from_params(VMG_ uint idx, uint cnt); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get my datatype when converted to constant data */ virtual vm_datatype_t get_convert_to_const_data_type() const { return VM_LIST; } /* create a list with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* * create a list with a given number of elements, for construction * of the list element-by-element */ static vm_obj_id_t create(VMG_ int in_root_set, size_t element_count); /* * create a list from a constant list, for construction of the list * as a modified copy of an original list */ static vm_obj_id_t create(VMG_ int in_root_set, const char *lst); /* get an element, given a zero-based index */ void get_element(size_t idx, vm_val_t *val) const { /* get the data from the data holder in our extension */ vmb_get_dh(get_element_ptr(idx), val); } /* * List construction: set an element. List contents are immutable, so * they cannot be changed after the list is constructed. However, it * is often convenient to construct a list one element at a time, so a * caller can create the list with the appropriate number of elements, * then use this routine to set each element of the list individually. * * idx is the index of the element in the list; the first element is at * index zero. Note that this routine does *not* allocate memory; the * list must be pre-allocated to its full number of elements. Use * cons_ensure_space to expand the list as needed. */ void cons_set_element(size_t idx, const vm_val_t *val); /* update the list in place so that each value is unique */ void cons_uniquify(VMG0_); /* * Copy an existing list into our list, starting at a given index. The * caller must ensure that our list buffer is large enough to * accommodate the new elements. The 'orig_list' value must point to a * standard list constant value: a UINT2 element count prefix followed * by DATAHOLDER elements. */ void cons_copy_elements(size_t start_idx, const char *orig_list); /* * Copy existing list elements into our list, starting at the given * index. The caller must ensure that our list buffer is large enough * to accommodate the new elements. The 'ele_array' is an array of * DATAHOLDER values. */ void cons_copy_data(size_t start_idx, const char *ele_array, size_t ele_count); /* * Set the length of the list. This can be used when constructing a * list, and the actual number of elements is unknown before * construction is complete (however, the maximum number of elements * must be known in advance, since this merely sets the length, and * does NOT reallocate the list -- hence, this call can only be used * to shrink the list below its allocated size, never to expand it). */ void cons_set_len(size_t len) { vmb_put_len(ext_, len); } /* * During construction, ensure there's enough space in the list to hold * an added item at the given index (0-based). If not, expands the * list to make it large enough to hold the given new elements plus the * margin. The margin is to ensure that we don't keep reallocating one * element at a time when the caller is adding items iteratively. The * added elements are automatically set to nil. */ void cons_ensure_space(VMG_ size_t idx, size_t margin); /* * During construction, clear a range of the list by setting each * element in the range (inclusive of the endpoints) to nil. If the * construction process could trigger garbage collection, it's * important for the caller to clear the list first. Uninitialized * elements could happen to look like pointers to invalid objects, so * if the gc runs, it could try to follow one of these invalid pointers * and cause a crash. The index values are 0-based. */ void cons_clear(size_t start_idx, size_t end_idx); /* clear the entire list (set the whole list to nil) */ void cons_clear() { if (get_ele_count() != 0) cons_clear(0, get_ele_count() - 1); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations - lists are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) { ext_ = (char *)ptr; } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * cast to string - returns a string consisting of the list elements * converted to strings and concatenated together with commas * separating elements; the result is the same as self.join(',') */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a 'self' value */ vm_val_t self_val; self_val.set_obj(self); /* join the list elements into a string, using commas as separators */ join(vmg_ new_str, &self_val, ",", 1); /* return the string result */ return new_str->get_as_string(vmg0_); } /* explicitly convert to string using toString() semantics */ virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* set up a 'self' value */ vm_val_t self_val; self_val.set_obj(self); /* convert to string */ return list_to_string(vmg_ new_str, &self_val, radix, flags); } /* * explicitly convert a list or list-like value to a string using * toString() semantics */ static const char *list_to_string( VMG_ vm_val_t *result, const vm_val_t *self, int radix, int flags); /* * Add a value to the list. If the value to add is a list (constant * or object), we'll append each element of the list to this list; * otherwise, we'll just append the value itself to the list. In * any case, we don't modify this list itself, but create a new list * object to hold the result. */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * Index the list */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* * Set an indexed element of the list. Since the contents of a list * object cannot be changed, we'll return in *new_container a new * list object that we create with the modified contents. */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Subtract a value from the list. This creates a new list with the * element matching the given value removed from the original list. * We do not modify the original list; instead, we create a new list * object with the new value. */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * get as a list - simply return our extension, which is in the * required portable list format */ const char *get_as_list() const { return ext_; } /* I'm the original list-like object */ int is_listlike(VMG_ vm_obj_id_t /*self*/) { return TRUE; } int ll_length(VMG_ vm_obj_id_t /*self*/) { return get_ele_count(); } /* * Check a value for equality. We will match any constant list that * contains the same data as our list, and any other list object * with the same underlying data. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* * Static list adder. This creates a new list object that results * from appending the given value to the given list constant. This * is defined statically so that this code can be shared for adding * to constant pool lists and adding to CVmObjList objects. * * 'lstval' must point to a constant list. The first two bytes of * the list are stored in portable UINT2 format and give the number * of elements in the list; this is immediately followed by a packed * array of data holders in portable format. */ static void add_to_list(VMG_ vm_val_t *result, vm_obj_id_t self, const char *lstval, const vm_val_t *val); /* * Static list subtraction routine. This creates a new list object * that results from removing the given value from the list * constant. This is defined statically so that this code can be * shared for subtracting from constant pool lists and subtracting * from CVmObjList objects. * * 'lstmem' must point to a constant list in the same format as * required for add_to_list. If 'lstmem' comes from the constant * pool, then 'lstval' must be provided to give us the constant pool * address; otherwise, 'lstval' should be null. */ static void sub_from_list(VMG_ vm_val_t *result, const vm_val_t *lstval, const char *lstmem, const vm_val_t *val); /* * Constant list comparison routine. Compares the given list * constant (in portable format, with leading UINT2 element count * prefix followed by the list's elements in portable data holder * format) to the other value. Returns true if the other value is a * list constant or object whose contents match the list constant, * false if not. * * If 'lstmem' comes from the constant pool, then 'lstval' must be * provided to give us the constant pool address; otherwise, * 'lstval' should be null. */ static int const_equals(VMG_ const vm_val_t *lstval, const char *lstmem, const vm_val_t *val, int depth); /* * Calculate a hash value for the list */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * Constant list hash value calculation */ static uint const_calc_hash(VMG_ const vm_val_t *self_val, const char *lst, int depth); /* * Constant list indexing routine. Indexes the given constant list * (which must be in portable format, with leading UINT2 element * count followed by the list's elements in portable data holder * format), looking up the value at the index number given by the * index value, and puts the result in *result. */ static void index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val); /* index a list, using a 1-based index */ static void index_list(VMG_ vm_val_t *result, const char *lst, uint idx); /* push the indexed element, using a 1-based index */ static void index_and_push(VMG_ const char *lst, uint idx) { vm_val_t *p; /* push a new stack element */ p = G_stk->push(); /* index the list and store the value directly in the stack */ index_list(vmg_ p, lst, idx); } /* * Constant list set-index routine. Creates a new list object as a * copy of this list, with the element at the given index set to the * given new value. */ static void set_index_list(VMG_ vm_val_t *result, const char *lst, const vm_val_t *index_val, const vm_val_t *new_val); /* * Find a value within a list. If we find the value, we'll set *idxp * to the index (starting at 1 for the first element) of the item we * found, and we'll return true; if we don't find the value, we'll * return false. */ static int find_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp); /* find the last match for a value */ static int find_last_in_list(VMG_ const vm_val_t *lst, const vm_val_t *val, size_t *idxp); /* * Evaluate a property of a constant list value. Returns true if we * successfully evaluated the property, false if the property is not * one of the properties that the list class defines. */ static int const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, vm_prop_id_t prop, vm_obj_id_t *srcobj, uint *argc); /* property evaluator - undefined property */ static int getp_undef(VMG_ vm_val_t *, const vm_val_t *, const char *, uint *) { return FALSE; } /* property evaluator - select a subset through a callback */ static int getp_subset(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - apply a callback to each element */ static int getp_map(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* get the length */ static int getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* sublist */ static int getp_sublist(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* intersect */ static int getp_intersect(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* indexOf */ static int getp_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* car */ static int getp_car(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* cdr */ static int getp_cdr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* indexWhich */ static int getp_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* forEach */ static int getp_for_each(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* forEachAssoc */ static int getp_for_each_assoc(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* valWhich */ static int getp_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastIndexOf */ static int getp_last_index_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastIndexWhich */ static int getp_last_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* lastValWhich */ static int getp_last_val_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* countOf */ static int getp_count_of(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* countWhich */ static int getp_count_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* general routine for indexWhich and lastIndexWhich */ static int gen_index_which(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int forward, vm_rcdesc *rc); /* getUnique */ static int getp_get_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* appendUnique */ static int getp_append_unique(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* appendUnique - internal interface */ static void append_unique(VMG_ vm_val_t *retval, const vm_val_t *self, const vm_val_t *other); /* append */ static int getp_append(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* sort */ static int getp_sort(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* insertAt */ static int getp_insert_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* prepend */ static int getp_prepend(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - remove a single element at a given index */ static int getp_remove_element_at(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - removeRange */ static int getp_remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - splice */ static int getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - join */ static int getp_join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* * join the elements of a list into a string; this is equivalent to the * bytecode-level join() method, but can be called more conveniently * from C++ code */ static void join(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *sep, size_t sep_len); /* property evaluator - generate */ static int getp_generate(VMG_ vm_val_t *retval, const vm_val_t * /*self*/, const char * /*lst*/, uint *argc) { return static_getp_generate(vmg_ retval, argc); } /* static property evaluator - generate */ static int static_getp_generate(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - index of minimum value */ static int getp_indexOfMin(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - minimum value in list */ static int getp_minVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - index of maximum value in list */ static int getp_indexOfMax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); /* property evaluator - maximum value in list */ static int getp_maxVal(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); protected: /* general processor for forEach and forEachAssoc */ static int for_each_gen(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc, int send_idx_to_cb, vm_rcdesc *rc); /* general min/max handler, for minIndex, minVal, maxIndex, maxVal */ static int get_minmax(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *in_argc, const vm_rcdesc *rc, int sense, int want_index); /* * Compute the intersection of two lists. Returns a new list with the * elements that occur in both lists. */ static vm_obj_id_t intersect(VMG_ const vm_val_t *l1, const vm_val_t *l2); /* insert the arguments into the list at the given index */ static void insert_elements(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint argc, int idx); /* remove elements */ static void remove_range(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, int start_idx, int del_cnt); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* * create a live iterator - for a list, there is no difference * between snapshot and live iterators, since a list is immutable */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { new_iterator(vmg_ retval, self_val); } /* get the number of elements in the list */ size_t get_ele_count() const { return vmb_get_len(ext_); } /* get the number of elements in a constant list */ static size_t get_ele_count_const(const char *lstval) { return vmb_get_len(lstval); } /* get an element from a constant list, given a zero-based index */ static void get_element_const(const char *lstval, size_t idx, vm_val_t *val) { /* get the data from the data holder in the constant list */ vmb_get_dh(get_element_ptr_const(lstval, idx), val); } /* given an index, get a pointer to the element's data in the list */ char *get_element_ptr(size_t idx) const { /* * figure out where this element's data holder is by skipping * the count prefix, then skipping past preceding data holders */ return ext_ + VMB_LEN + (idx * VMB_DATAHOLDER); } /* * given an index, and a pointer to a constant list, get a pointer * to the element's data in the list constant */ static const char *get_element_ptr_const(const char *lstval, size_t idx) { /* * figure out where this element's data holder is by skipping * the count prefix, then skipping past preceding data holders */ return lstval + VMB_LEN + (idx * VMB_DATAHOLDER); } /* * given a pointer to a list element, increment the pointer so that * it points to the next element */ static void inc_element_ptr(char **p) { /* add the size of a data holder to the current pointer */ *p += VMB_DATAHOLDER; } /* increment a constant element pointer */ static void inc_const_element_ptr(const char **p) { /* add the size of a data holder to the current pointer */ *p += VMB_DATAHOLDER; } /* create a list with no initial contents */ CVmObjList() { ext_ = 0; } /* * create a list with a given number of elements, for construction * of the list element-by-element */ CVmObjList(VMG_ size_t element_count); /* create a list from a constant list */ CVmObjList(VMG_ const char *lst); /* * Calculate the amount of space we need to store a list of a given * length. We require two bytes for the length prefix, plus the * space for each element. */ static size_t calc_alloc(size_t elecnt) { return (VMB_LEN + (elecnt * VMB_DATAHOLDER)); } /* allocate space for the list, given the number of elements */ void alloc_list(VMG_ size_t element_count); /* property evaluation function table */ static int (*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *lst, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * A constant list is exactly like an ordinary list, except that our * contents come from the constant pool. We store a pointer directly to * our constant pool data rather than making a separate copy. The only * thing we have to do differently from an ordinary list is that we don't * delete our extension when we're deleted, since our extension is really * just a pointer into the constant pool. */ class CVmObjListConst: public CVmObjList { public: /* notify of deletion */ void notify_delete(VMG_ int /*in_root_set*/) { /* * do nothing, since our extension is just a pointer into the * constant pool */ } /* create from constant pool data */ static vm_obj_id_t create(VMG_ const char *const_ptr); protected: /* construct from constant pool data */ CVmObjListConst(VMG_ const char *const_ptr) { /* point our extension directly to the constant pool data */ ext_ = (char *)const_ptr; } }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassList: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "list/030008"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjList(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjList(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjList::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjList::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; #endif /* VMLST_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjList) ��������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmcoll.h�����������������������������������������������������������������������0000644�0001750�0000144�00000011120�11047273254�015167� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcoll.h - T3 Collection base class Function A Collection is the base class for List, Array, and other objects providing a collection of objects that can be iterated via an Iterator. Collection is an abstract base class: it cannot be instantiated, and thus has no image-file or state-file representation. Notes Modified 04/22/00 MJRoberts - Creation */ #ifndef VMCOLL_H #define VMCOLL_H #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" class CVmObjCollection: public CVmObject { friend class CVmMetaclassCollection; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * constant value property evaluator - this allows us to evaluate a * property for an object value or for a constant value using the * same code */ int const_get_coll_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, const vm_val_t *self_val, vm_obj_id_t *src_obj, uint *argc); protected: /* * Create an iterator for this collection. Fills in *retval with a * reference to the new iterator object. This iterator must refer * to an immutable snapshot of the collection. */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) = 0; /* * Create a "live" iterator for this collection. Fills in *retval * with a reference to the new iterator object. This iterator must * refer to the original "live" collection. */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) = 0; /* property evaluator - undefined property */ int getp_undef(VMG_ vm_val_t *, const vm_val_t *, uint *) { return FALSE; } /* property evaluator - create iterator */ int getp_create_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); /* property evaluator - create live iterator */ int getp_create_live_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); /* property evaluation function table */ static int (CVmObjCollection::*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassCollection: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "collection/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCollection:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMCOLL_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjCollection) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmrun.h������������������������������������������������������������������������0000644�0001750�0000144�00000155574�11744245126�015071� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmrun.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrun.h - VM Execution Function Notes Modified 11/12/98 MJRoberts - Creation */ #ifndef VMRUN_H #define VMRUN_H #include "vmglob.h" #include "vmtype.h" #include "vmstack.h" #include "vmpool.h" #include "vmobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmprofty.h" #include "vmfunc.h" /* ------------------------------------------------------------------------ */ /* * for debugger use - interpreter context save structure */ struct vmrun_save_ctx { const uchar *entry_ptr_native_; vm_val_t *frame_ptr_; size_t old_stack_depth_; const uchar **pc_ptr_; }; /* ------------------------------------------------------------------------ */ /* * Recursive VM call descriptor. When making a recursive call into the VM, * a caller can set up one of these structures to describe the caller. * We'll store this in the stack in the recursive context slot. */ struct vm_rcdesc { /* no-op construction */ vm_rcdesc() { } /* construct for a system caller */ vm_rcdesc(const char *name) { init(name); } /* initialize for a system caller */ void init(const char *name) { this->name = name; bifptr.set_nil(); self.set_nil(); method_idx = 0; argc = 0; argp = 0; caller_addr = 0; } /* initialize for a system caller, including a return address */ void init_ret(VMG_ const char *name); /* construct for a built-in function */ vm_rcdesc(VMG_ const char *name, const struct vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc) { init(vmg_ name, funcset, idx, argp, argc); } /* initialize for a built-in function */ void init(VMG_ const char *name, const struct vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc) { init(vmg_ name, self, method_idx, argp, argc); } /* initialize for an intrinsic class method */ void init(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, uint *argc) { init(vmg_ name, self, method_idx, argp, argc != 0 ? *argc : 0); } /* initialize for an intrinsic class method */ void init(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc); /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc) { init(vmg_ name, self, method_idx, argp, argc); } /* construct for an intrinsic class method */ vm_rcdesc(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, uint *argc) { init(vmg_ name, self, method_idx, argp, argc != 0 ? *argc : 0); } /* is there a return address available in the calling frame? */ int has_return_addr() const { return caller_addr != 0; } /* * Compute the return address in the calling frame. This requires * figuring the size of the instruction at caller_addr (which is * relatively quick, but not quick enough that we want to precompute it * on every call). */ const uchar *get_return_addr() const; /* pointer to first argument in stack */ vm_val_t *argp; /* number of arguments */ int argc; /* descriptive name, for error messages */ const char *name; /* built-in function pointer, if the caller is a bif */ vm_val_t bifptr; /* 'self', if the caller is an intrinsic class */ vm_val_t self; /* for an intrinsic class caller, the method index of the caller */ unsigned short method_idx; /* * Byte-code address of the instruction in the calling frame that * triggered the call. The return address can be computed by adding * the length of that instruction to this address. */ const uchar *caller_addr; }; /* ------------------------------------------------------------------------ */ /* * Pseudo subroutine addresses. * * There are some situations where the bytecode engine needs to patch in * what's effectively a local subroutine to invoke on return from a * function or method call. (A local subroutine is one that doesn't * involve a method header change - it's just a matter of pushing a return * address and jumping, similar to the way LJSR and LRET work.) * * In a real machine, we'd just load the subroutine's instructions into * some reserved area of system memory, and use the address of the * subroutine code as the return address when making the call. We'd also * push the *real* return address before this, so that the subroutine would * be able to do the local return to the actual calling address upon * completion. * * Due to the way we manage memory, though, we can't do this. Return * addresses are always offsets from a method header, since the only * addresses we're allowed to resolve are method header addresses. (This * is largely because memory can be discontiguous outside of a single * method block.) * * So what we do instead is define a range of return addresses that are * impossible, and use those to signal these special internal subroutines. * A return address is always an offset from the start of a method header, * so, conveniently, any offset less than the size of a header is * inherently invalid as an actual return address. This gives us a range * of addresses that are safe to use to signal special meanings. The * original T3 method header is 10 bytes, so any return offset from 0 to 9 * is invalid and thus can have a special meaning. */ /* is the given offset a special return address? */ inline int vmrun_is_special_return(uint ofs) { return ofs < 10; } /* * Special return address: Recursive Call Return. This value for the * return offset indicates that this is a recursive call into the VM, so * the VM bytecode execution loop should simply return when this frame * exits. */ const uint VMRUN_RET_RECURSIVE = 0; /* * Special return address: Return from Operator Overload. This value for * the return offset indicates that this is a call to evaluate an * overloaded operator. On return from this frame, execute the following * local subroutine: * *. GETR0 ; push return value from R0 onto the stack *. SWAP ; arrange stack: [sp]=return offset, [sp+1]=return value *. LRET [sp+] ; pop stack -> X and return to offset X in current method * * The stack at entry is arranged as follows: [sp]=return offset */ const uint VMRUN_RET_OP = 1; /* * Special return address: Return from Operator Overload And Assign Local. * This value for the return address indicates that this is a call to * evaluate an overloaded operator, and on return, we're to assign the * result to a local variable. On return from this frame, execute the * following local subroutine: * *. SETLCLR0 [sp+] ; pop stack -> V and assign R0 -> local variable #V *. LRET [sp+] ; pop stack -> X and return to offset X in current method * * The stack is arranged at entry as follows: [sp]=local variable number to * assign, [sp+1]=return offset */ const uint VMRUN_RET_OP_ASILCL = 2; /* ------------------------------------------------------------------------ */ /* * Procedure activation frame. The activation frame is arranged as * follows (the stack index increases reading down the list): * *. argument N *. argument N-1 *. ... *. argument 2 *. argument 1 *. target property *. original target object *. defining object *. self *. offset in calling method of next instruction to execute *. caller's entry pointer (EP) register value *. actual parameter count *. caller's frame pointer <<<--- CURRENT FRAME POINTER *. local variable 1 *. local variable 2 *. local variable 3 * * So, local variable 1 is at (FP+1), local variable 2 is at (FP+2), and * so on; the argument count is at (FP-1); 'self' is at (FP-4); argument 1 * is at (FP-5), argument 2 is at (FP-6), and so on. */ /* offset from FP of first argument */ const int VMRUN_FPOFS_ARG1 = -11; /* offset from FP of target property */ const int VMRUN_FPOFS_PROP = -10; /* offset from FP of original target object */ const int VMRUN_FPOFS_ORIGTARG = -9; /* offset from FP of defining object (definer of current method) */ const int VMRUN_FPOFS_DEFOBJ = -8; /* offset from FP of 'self' */ const int VMRUN_FPOFS_SELF = -7; /* invokee (this is the FuncPtr, DynamicFunc, AnonFunc, etc being invoked) */ const int VMRUN_FPOFS_INVOKEE = -6; /* frame reference (for reflection access to the frame contents) */ const int VMRUN_FPOFS_FRAMEREF = -5; /* recursive VM invocation native caller context */ const int VMRUN_FPOFS_RCDESC = -4; /* offset from FP of return address */ const int VMRUN_FPOFS_RET = -3; /* offset from FP of enclosing entry pointer */ const int VMRUN_FPOFS_ENC_EP = -2; /* offset from FP of argument count */ const int VMRUN_FPOFS_ARGC = -1; /* offset from FP of enclosing frame pointer */ const int VMRUN_FPOFS_ENC_FP = 0; /* offset from FP of first local variable */ const int VMRUN_FPOFS_LCL1 = 1; /* ------------------------------------------------------------------------ */ /* * Define certain of the VM CPU registers in global variables, if * applicable. */ VM_IF_REGS_IN_GLOBALS(extern vm_val_t r0_;) VM_IF_REGS_IN_GLOBALS(extern const uchar *entry_ptr_native_;) VM_IF_REGS_IN_GLOBALS(extern vm_val_t *frame_ptr_;) VM_IF_REGS_IN_GLOBALS(extern const uchar **pc_ptr_;) /* ------------------------------------------------------------------------ */ /* * VM Execution Engine class. This class handles execution of byte * code. */ class CVmRun: public CVmStack { friend class CVmDebug; friend class vmrun_prop_eval; public: CVmRun(size_t max_depth, size_t reserve_depth); ~CVmRun(); /* initialize */ void init(); /* terminate */ void terminate(); /* * Get/set the method header size. This size is stored in the image * file; the image loader sets this at load time to the value * retrieved from the image file. All method headers in an image * file use the same size. */ void set_funchdr_size(size_t siz); size_t get_funchdr_size() const { return funchdr_size_; } /* * Call a function or method. 'target_ptr' is the byte pointer to the * code to invoke, and 'argc' is the number of arguments that the * caller has pushed onto the stack. * * Before calling this, the caller must push onto the stack targetprop, * targetobj, definingobj, and self, in that order. For a function, * targetprop is VM_INVALID_PROP and all of the others are nil. * * 'caller_ofs' is the method offset (the byte code offset from the * current entry pointer) in the caller. If 'caller_ofs' is non-zero, * we'll set up to begin execution in the target code and return the * new program counter. If 'caller_ofs' is zero, we'll invoke the VM * byte code interpreter recursively, so this function will return only * after the called code returns. When calling recursively, set * 'recurse_calling' to a descriptive string that can be used to show * the system code calling the recursive code in case of error. * * When calling a function, 'self' should be VM_INVALID_OBJ. * Otherwise, this value gives the object whose method is being * invoked. * * The return value is the new program counter. For recursive * invocations, this will simply return null. */ const uchar *do_call(VMG_ uint caller_ofs, const uchar *target_ptr, uint argc, const vm_rcdesc *recurse_ctx); /* call a function, non-recursively */ const uchar *do_call_func_nr(VMG_ uint caller_ofs, pool_ofs_t ofs, uint argc); /* * Call a function pointer value. If 'funcptr' contains a function * pointer, we'll simply call the function; if it contains an * anonymous function object, we'll call the anonymous function. */ const uchar *call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs); /* call a function pointer, with the invocation frame already set up */ const uchar *call_func_ptr_fr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs); /* * Get the descriptive message, if any, from an exception object. * The returned string will not be null-terminated, but the length * will be stored in *msg_len. The returned string might point to * constant pool data or data in an object, so it might not remain * valid after a constant pool translation or garbage collection * operation. If the exception has no message, we will return a * null pointer. */ static const char *get_exc_message(VMG_ const CVmException *exc, size_t *msg_len); static const char *get_exc_message(VMG_ vm_obj_id_t obj, size_t *msg_len); /* * Get the descriptive message from an exception. If the exception * has a program-generated exception object, we'll try to get the * message from that object; if it's a VM exception with no * underlying object, we'll retrieve the VM message. * * If add_unh_prefix is true, we'll add an "unhandled exception:" * prefix to the message if we retrieve the message from a * program-defined exception. Otherwise, if it's a program * exception, we won't add any prefix at all. */ static void get_exc_message(VMG_ const CVmException *exc, char *buf, size_t buflen, int add_unh_prefix); /* * Evaluate a property of an object. 'target_obj' is the object whose * property is to be evaluated, 'target_prop' is the ID of the * property to evaluate, and 'argc' is the number of arguments to the * method. * * 'caller_ofs' is the current method offset (the offset from the * current entry pointer to the current program counter) in the * caller. If this is zero, we'll make a recursive call to the * interpreter loop to execute any method code; thus, any method code * will have run to completion by the time we return in this case. * * 'self' is the object in whose context we're to perform the code * execution, if the property is a method. Note that 'self' may * differ from 'target_obj' in some cases, particularly when * inheriting. * * The return value is the new program counter from which execution * should resume. This will be null (and can be ignored) for * recursive invocations. */ const uchar *get_prop(VMG_ uint caller_ofs, const vm_val_t *target_obj, vm_prop_id_t target_prop, const vm_val_t *self, uint argc, const vm_rcdesc *rc); /* * Simplified get_prop, for cases where the caller just wants to * evaluate a property of a value (with recursive VM entry). */ void get_prop(VMG_ vm_val_t *result, const vm_val_t *obj, vm_prop_id_t prop, uint argc, const vm_rcdesc *rc); /* * Evaluate a property for operator overloading. */ const uchar *op_overload(VMG_ uint caller_ofs, int asi_lcl, const vm_val_t *obj, vm_prop_id_t prop, uint argc, int err); /* * Set a property of an object */ void set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop, const vm_val_t *new_val); /* get data register 0 (R0) */ VM_REG_ACCESS vm_val_t *get_r0() { return &r0_; } /* set the default "say" function */ void set_say_func(VMG_ const vm_val_t *val); /* get the current default "say" function */ void get_say_func(vm_val_t *val) const; /* set the default "say" method */ void set_say_method(vm_prop_id_t prop) { /* remember the property */ say_method_ = prop; } /* get the current "say" method */ vm_prop_id_t get_say_method() const { return say_method_; } /* pop an integer value; throws an error if the value is not an integer */ void pop_int(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); } /* pop an object value */ void pop_obj(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); } /* pop a property pointer value */ void pop_prop(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_PROP) err_throw(VMERR_PROPPTR_VAL_REQD); } /* pop a function pointer value */ void pop_funcptr(VMG_ vm_val_t *val) { pop(val); if (val->typ != VM_FUNCPTR) err_throw(VMERR_FUNCPTR_VAL_REQD); } /* * Pop two values from the stack. The values are popped in reverse * order, so val2 has the value at the top of the stack. If the * left operand was pushed first, this results in placing the left * operand in val1 and the right operand in val2. */ void popval_2(VMG_ vm_val_t *val1, vm_val_t *val2) { popval(vmg_ val2); popval(vmg_ val1); } /* * Pop the left-hand operand of a two-operand operator, leaving the * right-hand operand on the stack. For binary operators, the * left-hand value is pushed first, then the right-hand value; this * means that the right value is at top-of-stack. So we basically have * to pull the left-hand value out of the stack, then move the right * value up one slot to close the gap. */ void pop_left_op(VMG_ vm_val_t *left) { *left = *get(1); *get(1) = *get(0); discard(); } /* * Pop two integers, throwing an error if either value is not an * integer. Pops the item at the top of the stack into val2, and * the next value into val1. */ void pop_int_2(VMG_ vm_val_t *val1, vm_val_t *val2) { popval(vmg_ val2); popval(vmg_ val1); if (val1->typ != VM_INT || val2->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); } /* * get the active function's argument count - we read the value from * the first item below the frame pointer in the current frame */ VM_REG_ACCESS int get_cur_argc(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_ARGC)->val.intval; } /* * Get a parameter value; 0 is the first parameter, 1 is the second, * and so on. */ VM_REG_ACCESS vm_val_t *get_param(VMG_ int idx) VM_REG_CONST { return get_param_from_frame(vmg_ frame_ptr_, idx); } /* get a parameter from a given frame */ VM_REG_ACCESS vm_val_t *get_param_from_frame( VMG_ vm_val_t *fp, int idx) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_ARG1 - idx); } /* get a parameter at the given stack level */ VM_REG_ACCESS vm_val_t *get_param_at_level( VMG_ int idx, int level) VM_REG_CONST { return get_param_from_frame(vmg_ get_fp_at_level(vmg_ level), idx); } /* get the named parameter table, if any, from a frame */ static const uchar *get_named_args_from_frame( VMG_ vm_val_t *fp, vm_val_t **arg0); /* * get a local variable's value; 0 is the first local variable, 1 is * the second, and so on */ VM_REG_ACCESS vm_val_t *get_local(VMG_ int idx) VM_REG_CONST { return get_local_from_frame(vmg_ frame_ptr_, idx); } /* get a local from a given frame */ VM_REG_ACCESS vm_val_t *get_local_from_frame( VMG_ vm_val_t *fp, int idx) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_LCL1 + idx); } /* get a local at the given stack level */ VM_REG_ACCESS vm_val_t *get_local_at_level( VMG_ int idx, int level) VM_REG_CONST { return get_local_from_frame(vmg_ get_fp_at_level(vmg_ level), idx); } /* get a local, parameter, or context local at a given stack level */ VM_REG_ACCESS void get_local_from_frame( VMG_ vm_val_t *val, vm_val_t *fp, const class CVmDbgFrameSymPtr *symp); VM_REG_ACCESS void get_local_from_frame( VMG_ vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx); /* set a local, parameter, or context local at a given stack level */ VM_REG_ACCESS void set_local_in_frame( VMG_ const vm_val_t *val, vm_val_t *fp, const class CVmDbgFrameSymPtr *symp); VM_REG_ACCESS void set_local_in_frame( VMG_ const vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx); /* * Get the frame pointer at the given stack level. Level 0 is the * currently active frame, 1 is the first enclosing level, and so * on. Throws an error if the enclosing frame is invalid. */ VM_REG_ACCESS vm_val_t *get_fp_at_level(VMG_ int level) VM_REG_CONST; /* * Get the current frame depth. This is the stack depth of the * current frame pointer. This can be used to compare two frame * pointers to determine if one encloses the other - the pointer * with the smaller depth value encloses the larger one. */ size_t get_frame_depth(VMG0_) const { return ptr_to_index(frame_ptr_); } /* get the current frame pointer */ VM_REG_ACCESS vm_val_t *get_frame_ptr() VM_REG_CONST { return frame_ptr_; } /* given a frame pointer, get the enclosing frame pointer */ static vm_val_t *get_enclosing_frame_ptr(VMG_ vm_val_t *fp) { return (vm_val_t *)get_from_frame(fp, VMRUN_FPOFS_ENC_FP)->val.ptr; } /* get the number of arguments from a given frame */ VM_REG_ACCESS int get_argc_from_frame(VMG_ vm_val_t *fp) VM_REG_CONST { return get_from_frame(fp, VMRUN_FPOFS_ARGC)->val.intval; } /* get the argument counter from a given stack level */ VM_REG_ACCESS int get_argc_at_level(VMG_ int level) VM_REG_CONST { return get_argc_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the 'self' object for the frame */ static vm_obj_id_t get_self_from_frame(VMG_ vm_val_t *fp) { /* get the 'self' slot on the stack */ vm_val_t *self_val = get_from_frame(fp, VMRUN_FPOFS_SELF); /* return the appropriate value */ return (self_val->typ == VM_NIL ? VM_INVALID_OBJ : self_val->val.obj); } /* get the 'self' object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_self_at_level(VMG_ int level) VM_REG_CONST { return get_self_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the target property for the frame */ static vm_prop_id_t get_target_prop_from_frame(VMG_ vm_val_t *fp) { vm_val_t *val; /* get the 'self' slot on the stack */ val = get_from_frame(fp, VMRUN_FPOFS_PROP); /* return the appropriate value */ return (val->typ == VM_NIL ? VM_INVALID_PROP : val->val.prop); } /* get the target property at a given stack level */ VM_REG_ACCESS vm_prop_id_t get_target_prop_at_level( VMG_ int level) VM_REG_CONST { return get_target_prop_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the defining object from the frame */ VM_REG_ACCESS vm_obj_id_t get_defining_obj_from_frame(VMG_ vm_val_t *fp) VM_REG_CONST { /* get the defining object slot on the stack */ vm_val_t *val = get_from_frame(fp, VMRUN_FPOFS_DEFOBJ); /* return the appropriate value */ return (val->typ != VM_OBJ ? VM_INVALID_OBJ : val->val.obj); } /* get the defining object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_defining_obj_at_level(VMG_ int level) VM_REG_CONST { return get_defining_obj_from_frame(vmg_ get_fp_at_level(vmg_ level)); } /* given a frame pointer, get the original target object */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj_from_frame( VMG_ vm_val_t *fp) VM_REG_CONST { /* get the original target object slot on the stack */ vm_val_t *val = get_from_frame(fp, VMRUN_FPOFS_ORIGTARG); /* return the appropriate value */ return (val->typ == VM_NIL ? VM_INVALID_OBJ : val->val.obj); } /* get the current original target object at a given stack level */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj_at_level(VMG_ int level) VM_REG_CONST { return get_orig_target_obj_from_frame( vmg_ get_fp_at_level(vmg_ level)); } /* get the enclosing entry pointer from a given frame */ static const uchar *get_enclosing_entry_ptr_from_frame(VMG_ vm_val_t *fp) { return (const uchar *)get_from_frame(fp, VMRUN_FPOFS_ENC_EP)->val.ptr; } /* get the recursive native caller contxt from the frame */ static const vm_rcdesc *get_rcdesc_from_frame(VMG_ vm_val_t *fp) { return (vm_rcdesc *)get_from_frame(fp, VMRUN_FPOFS_RCDESC)->val.ptr; } /* get the frame reference object from a frame */ static vm_val_t *get_frameref_slot(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_FRAMEREF); } /* get the invokee from a frame */ static vm_val_t *get_invokee_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_INVOKEE); } /* get the invokee of the current function */ VM_REG_ACCESS vm_val_t *get_invokee(VMG0_) { return get_invokee_from_frame(vmg_ frame_ptr_); } /* * Get the return offset from a given frame. This is the offset of * the return address from the start of the method header for the * frame. */ static ulong get_return_ofs_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_RET)->val.ofs; } /* * Get the return address from a given frame. (The return address * is the third item pushed before the enclosing frame pointer, * hence it's at offset -3 from the frame pointer.) Returns zero if * we were called by recursive invocation of the VM - this is not * ambiguous with an actual return address of zero, since zero is * never a valid code address. */ static const uchar *get_return_addr_from_frame(VMG_ vm_val_t *fp) { pool_ofs_t ofs; /* get the return method offset from the stack */ ofs = get_return_ofs_from_frame(vmg_ fp); /* check for special offset values */ switch (ofs) { case VMRUN_RET_RECURSIVE: /* * recursive VM call - there's no byte-code return address in * this case since the return takes us back to native code, so * return 0 to indicate this */ return 0; case VMRUN_RET_OP: /* * return from operator overload; the true return address is in * the stack slot just above the last argument */ ofs = G_interpreter->get_param_from_frame( vmg_ fp, G_interpreter->get_argc_from_frame(vmg_ fp)) ->val.intval; break; case VMRUN_RET_OP_ASILCL: /* * return from operator overload and assign local: the true * return address is two slots above the last argument */ ofs = G_interpreter->get_param_from_frame( vmg_ fp, G_interpreter->get_argc_from_frame(vmg_ fp) + 1) ->val.intval; break; } /* * add the offset to the enclosing entry pointer to yield the * absolute pool address of the return point */ return ofs + get_enclosing_entry_ptr_from_frame(vmg_ fp); } /* * Given a frame pointer, set up a function pointer for the return * address from the frame. */ static void set_return_funcptr_from_frame(VMG_ class CVmFuncPtr *func_ptr, vm_val_t *frame_ptr); /* reset the machine registers to the initial conditions */ void reset(VMG0_); /* * Get the current "self" object. The "self" object is always the * implicit first parameter to any method. Note that this version of * the method *doesn't* check for nil - it assumes that the caller * knows for sure that there's a valid "self", so dispenses with any * checks to save time. */ VM_REG_ACCESS vm_obj_id_t get_self(VMG0_) VM_REG_CONST { /* get the object value of the 'self' slot in the current frame */ return get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->val.obj; } /* get the pointer to the current "self" value */ VM_REG_ACCESS vm_val_t *get_self_val(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF); } /* get the self value from a frame */ VM_REG_ACCESS vm_val_t *get_self_val_from_frame(VMG_ vm_val_t *fp) { return get_from_frame(fp, VMRUN_FPOFS_SELF); } /* * Get the current "self" object, checking for nil. If "self" is nil, * we'll return VM_INVALID_OBJ. This version (not get_self()) should * be used whenever it's not certain from context that there's a valid * "self". */ VM_REG_ACCESS vm_obj_id_t get_self_check(VMG0_) VM_REG_CONST { /* get the 'self' slot from the stack frame */ vm_val_t *valp = get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF); /* if it's an object, return the ID, otherwise invalid */ return (valp->typ == VM_OBJ ? valp->val.obj : VM_INVALID_OBJ); } /* set the current 'self' object */ VM_REG_ACCESS void set_self(VMG_ const vm_val_t *val) { /* store the given value in the 'self' slot in the current frame */ *get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF) = *val; } /* create a method context object suitable for LOADCTX */ static void create_loadctx_obj(VMG_ vm_val_t *result, vm_obj_id_t self, vm_obj_id_t defobj, vm_obj_id_t targobj, vm_prop_id_t targprop); /* * Set the current execution context: the 'self' value, the target * property, the original target object, and the defining object. */ VM_REG_ACCESS void set_method_ctx( VMG_ vm_obj_id_t new_self, vm_prop_id_t new_target_prop, vm_obj_id_t new_target_obj, vm_obj_id_t new_defining_obj) { /* set the "self" slot in the current stack frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_SELF)->set_obj(new_self); /* set the target property slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP) ->set_propid(new_target_prop); /* set the original target object slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG) ->set_obj(new_target_obj); /* set the defining object slot in the frame */ get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ) ->set_obj(new_defining_obj); } /* get the current target property value */ VM_REG_ACCESS vm_prop_id_t get_target_prop(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)->val.prop; } /* get the current defining object */ VM_REG_ACCESS vm_obj_id_t get_defining_obj(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)->get_as_obj(); } /* get the current original target object */ VM_REG_ACCESS vm_obj_id_t get_orig_target_obj(VMG0_) VM_REG_CONST { return get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)->val.obj; } /* push an object ID */ void push_obj(VMG_ vm_obj_id_t obj) { push()->set_obj(obj); } /* push an object ID, or nil if it's an invalid object */ void push_obj_or_nil(VMG_ vm_obj_id_t obj) { if (obj == VM_INVALID_OBJ) push()->set_nil(); else push()->set_obj(obj); } /* push a property ID */ void push_prop(VMG_ vm_prop_id_t prop) { push()->set_propid(prop); } /* push a property ID, or nil if it's invalid */ void push_prop_or_nil(VMG_ vm_prop_id_t prop) { if (prop == VM_INVALID_PROP) push()->set_nil(); else push()->set_propid(prop); } /* push a boolean value */ void push_bool(VMG_ int flag) { push()->set_logical(flag); } /* push nil */ void push_nil(VMG0_) { push()->set_nil(); } /* push a code offset value */ void push_codeofs(VMG_ pool_ofs_t ofs) { push()->set_codeofs(ofs); } /* push a code pointer value */ void push_codeptr(VMG_ const void *p) { push()->set_codeptr(p); } /* push a stack pointer value */ void push_stackptr(VMG_ vm_val_t *stack_ptr) { push()->set_stack((void *)stack_ptr); } /* push an integer value */ void push_int(VMG_ int32_t intval) { push()->set_int(intval); } /* push an enumerator value */ void push_enum(VMG_ uint32_t intval) { push()->set_enum(intval); } /* push a C string value */ void push_string(VMG_ const char *str, size_t len); void push_string(VMG_ const char *str) { push_string(vmg_ str, strlen(str)); } /* push a printf-formatted string */ void push_stringf(VMG_ const char *fmt, ...); void push_stringvf(VMG_ const char *fmt, va_list va); /* get the function entrypoint address */ VM_REG_ACCESS const uchar *get_entry_ptr() VM_REG_CONST { return entry_ptr_native_; } /* get the current program counter offset from the entry pointer */ VM_REG_ACCESS uint get_method_ofs() VM_REG_CONST { /* * Return the current program counter minus the current entry * pointer. If there is no current program counter, we're not * executing in byte code, so there's no method offset. */ if (pc_ptr_ != 0) return *pc_ptr_ - entry_ptr_native_; else return 0; } /* * Convert a pointer to the currently executing method into an offset * from the start of the current method. */ VM_REG_ACCESS ulong pc_to_method_ofs(const uchar *p) { /* * get the memory address of the current entry pointer, and * subtract that from the given memory pointer to get an offset * from the start of the current method */ return p - entry_ptr_native_; } /* * Create an exception of the given imported class and throw it. If * the class is not exported, we'll create a basic run-time exception; * if that's not defined, we'll create an arbitrary object. * * Arguments to the exception constructor are on the stack, with the * argument count in argc. If the imported class doesn't exist, we'll * instead throw an intrinsic-class-general-error exception with the * given fallback message as explanatory text. */ void throw_new_class(VMG_ vm_obj_id_t cls, uint argc, const char *fallback_msg); /* * Create an exception of the given RuntimeError subclass and throw it. * If the class isn't exported, we'll create a base RuntimeError. * * Push any constructor arguments *besides* the error number onto the * stack before calling this. A RuntimeError subclass constructor will * always take an additional first argument giving the error number, * but don't push that or include it in the 'argc' value. */ void throw_new_rtesub(VMG_ vm_obj_id_t cls, uint argc, int errnum); /* * Save/restore the interpreter context. This is for use by the * debugger when evaluating an expression in the course of execution, * to ensure that everything is reset properly to the enclosing * execution context when it's finished. */ void save_context(VMG_ vmrun_save_ctx *ctx); void restore_context(VMG_ vmrun_save_ctx *ctx); /* * Get the boundaries of the given source-code statement in the given * function. Fills in the line pointer and *stm_start and *stm_end * with information on the source line containing the given offset in * the given method. Returns true if source information is * successfully located for the given machine code address, false if * not. * * If no debugging information is available for the given code * location, this function cannot get the source-code statement * bounds, and returns false. */ static int get_stm_bounds(VMG_ const class CVmFuncPtr *func_ptr, ulong pc_ofs, class CVmDbgLinePtr *line_ptr, const uchar **stm_start, const uchar **stm_end); /* -------------------------------------------------------------------- */ /* * Set the HALT VM flag. This allows the debugger to terminate the * program immediately, without allowing any more byte-code * instructions to execute. */ void set_halt_vm(int f) { halt_vm_ = f; } /* -------------------------------------------------------------------- */ /* * Start profiling. This deletes any old profiling records and starts * a new profiling session. We'll capture profiling data until * end_profiling() is called. This function is only included in the * build if the profiler is included in the build. */ void start_profiling(); /* end profiling */ void end_profiling(); /* * get the profiling data - we'll invoke the callback once for each * function in our table of data */ void get_profiling_data(VMG_ void (*cb)(void *ctx, const char *func_name, unsigned long time_direct, unsigned long time_in_children, unsigned long call_cnt), void *cb_ctx); /* get the last program counter address */ VM_REG_ACCESS const uchar *get_last_pc() VM_REG_CONST { return pc_ptr_ != 0 ? *pc_ptr_ : 0; } protected: /* * Execute byte code starting at a given address. This function * retains control until the byte code function invoked returns or * throws an unhandled exception. * * If an exception occurs and is not handled by the byte code, we'll * throw VMERR_UNHANDLED_EXC with the exception object as the first * parameter. */ void run(VMG_ const uchar *p); /* * Display a dstring via the default string display function. This * function pushes a string value (with the given constant pool * offset), then does the same work as do_call() to invoke a function * with one argument. * * The string is identified by its offset in the constant pool. */ const uchar *disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs, vm_obj_id_t self); /* * Display the value at top of stack via the default string display * function. does the same work as do_call() to invoke the function * with one argument, which must already be on the stack. */ const uchar *disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self); /* * Set up a function header pointer for the current function */ void set_current_func_ptr(VMG_ class CVmFuncPtr *func_ptr); /* call a built-in function */ void call_bif(VMG_ uint set_index, uint func_index, uint argc); /* * Throw an exception. Returns a non-null program counter if a * handler was found, false if not. If a handler was found, byte-code * execution can proceed; if not, the byte-code execution loop must * pass the exception up to its caller. */ const uchar *do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj); /* * Inherit a property - this is essentially the same as get_prop, * but rather than getting the property from the given object, this * ignores any such property defined directly by the object and goes * directly to the inherited definition. However, the "self" object * is still the same as the current "self" object, since we want to * evaluate the inherited method in the context of the original * target "self" object. */ const uchar *inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc); /* * Look up a property value without evaluating it. Returns true if we * found the property, false if not. */ inline static int get_prop_no_eval(VMG_ const vm_val_t **target_obj, vm_prop_id_t target_prop, uint *argc, vm_obj_id_t *srcobj, vm_val_t *val, const vm_val_t **self, vm_val_t *new_self); /* * Evaluate a property value. If the value contains code, we'll * execute the code; if it contains a self-printing string, we'll * display the string; otherwise, we'll push the value onto the stack. * * 'found' indicates whether or not the property is defined by the * object. False indicates that the property is not defined, true * that it is defined. If the property isn't defined, we'll simply * discard arguments and push nil. * * If 'caller_ofs' is zero, we'll recursively invoke the interpreter * loop if it's necessary to run a method; otherwise, we'll set up at * the beginning of the method's code and let the caller proceed into * the code. */ const uchar *eval_prop_val(VMG_ int found, uint caller_ofs, const vm_val_t *val, vm_obj_id_t self, vm_prop_id_t target_prop, const vm_val_t *orig_target_obj, vm_obj_id_t defining_obj, uint argc, const vm_rcdesc *rc); /* * Check a property for validity in a speculative evaluation. If * evaulating the property would cause any side effects, we'll throw * an error (VMERR_BAD_SPEC_EXPR); otherwise, we won't do anything. * Side effects include displaying a dstring or invoking a method. */ void check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop); /* * Return from a function or method. Returns the new program counter * at which to continue execution, and restore machine registers to * the enclosing frame. * * Returns a non-null program counter if execution should proceed, * null if we're returning from the outermost stack level. When we * return null, the caller must return control to the host * environment, since the host environment called the function from * which we're returning. */ const uchar *do_return(VMG0_); /* * append a stack trace to the given string, returning a new string * object */ vm_obj_id_t append_stack_trace(VMG_ vm_obj_id_t str_obj); /* * Validate a local stack index. This verifies that an index used in a * GETSPN, GETSPX, SETSPN, or SETSPX instruction refers to the * temporary storage area for the current function. */ void validate_local_stack_index(VMG_ unsigned int idx) { /* get a pointer to the current function header */ CVmFuncPtr hdr_ptr; hdr_ptr.set(entry_ptr_native_); /* * Check that the index is below the index of the last local * variable. The locals start just below the frame pointer. */ if (idx >= get_depth_rel(frame_ptr_) - hdr_ptr.get_local_cnt()) err_throw(VMERR_STACK_OVERFLOW); } /* push a value onto the stack */ void pushval(VMG_ const vm_val_t *val) { push(val); } /* pop a value off the stack */ void popval(VMG_ vm_val_t *val) { pop(val); } /* add two values, leaving the result in *val1 */ int compute_sum(VMG_ vm_val_t *val1, const vm_val_t *val2); /* compute a sum, specialized for individual opcodes */ const uchar *compute_sum_inc(VMG_ const uchar *p); const uchar *compute_sum_add(VMG_ const uchar *p); const uchar *compute_sum_lcl_imm(VMG_ vm_val_t *lclp, const vm_val_t *ival, int lclidx, const uchar *p); /* subtract one value from another, leaving the result in *val1 */ int compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute a difference, specialized for individual opcodes */ const uchar *compute_diff_dec(VMG_ const uchar *p); const uchar *compute_diff_sub(VMG_ const uchar *p); /* compute the product, leaving the result in *val1 */ int compute_product(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute the quotient val1/val2, leaving the result in *val2 */ int compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2); /* compute the modulo val1%val2, leaving the result in *val2 */ int compute_mod(VMG_ vm_val_t *val1, vm_val_t *val2); /* XOR two values and push the result */ int xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2); /* process a MAKELSTPAR instruction */ void makelstpar(VMG0_); /* * index container_val by index_val (i.e., compute * container_val[index_val]), storing the result at *result */ VM_REG_ACCESS int apply_index(VMG_ vm_val_t *result, const vm_val_t *container_val, const vm_val_t *index_val); /* * Set the element at index index_val in container_val to new_val, * and push the new container value. The container may be a new * object, since some types (lists, for example) cannot have their * values changed but instead create new objects when an indexed * element is modified. */ VM_REG_ACCESS int set_index(VMG_ vm_val_t *container_val, const vm_val_t *index_val, const vm_val_t *new_val); /* * create a new object of the given index into the metaclass * dependency table for the load image file, using the given number * of parameters; removes the parameters from the stack, and leaves * the new object reference in register R0 */ const uchar *new_and_store_r0(VMG_ const uchar *pc, uint metaclass_idx, uint argc, int is_transient); /* * Compare the two values at top of stack for equality; returns true * if the values are equal, false if not. Removes the two values from * the stack. */ int pop2_equal(VMG0_) { /* compare the values and return the result */ int ret = get(1)->equals(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* * Compare the magnitude of the two values at the top of the stack. * Returns 1 if the value at (TOS-1) is greater than the value at TOS, * -1 if (TOS-1) is less than (TOS), and 0 if the two value are equal. * Removes the two values from the stack. */ int pop2_compare(VMG0_) { /* compare the values and return the result */ int ret = get(1)->compare_to(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 < TOS ? */ int pop2_compare_lt(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_lt(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 <= TOS ? */ int pop2_compare_le(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_le(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 > TOS ? */ int pop2_compare_gt(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_gt(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* is TOS-1 >= TOS ? */ int pop2_compare_ge(VMG0_) { /* compare the values and return the result */ int ret = get(1)->is_ge(vmg_ get(0)); /* discard the values */ discard(2); /* return the result */ return ret; } /* given a constant pool offset, get a pointer to the constant data */ static const char *get_const_ptr(VMG_ pool_ofs_t ofs) { return G_const_pool->get_ptr(ofs); } /* * get a signed 16-bit byte-code operand, incrementing the * instruction pointer past the operand */ int16_t get_op_int16(const uchar **p) { int16_t ret = (int16_t)osrp2s(*p); *p += 2; return ret; } /* get an unsigned 16-bit byte-code operand */ uint16_t get_op_uint16(const uchar **p) { uint16_t ret = (uint16_t)osrp2(*p); *p += 2; return ret; } /* get a signed 32-bit byte-code operand */ int32_t get_op_int32(const uchar **p) { int32_t ret = (int32_t)osrp4s(*p); *p += 4; return ret; } /* get an unsigned 32-bit byte-code operand */ uint32_t get_op_uint32(const uchar **p) { uint32_t ret = (uint32_t)t3rp4u(*p); *p += 4; return ret; } /* get a signed 8-bit byte-code operand */ int get_op_int8(const uchar **p) { int ret = (int)(signed char)**p; ++(*p); return ret; } /* get an unsigned 8-bit byte-code operand */ uint get_op_uint8(const uchar **p) { uint ret = (uint)**p; ++(*p); return ret; } /* record a function or method entry in the profiler data */ void prof_enter(VMG_ const uchar *fptr); /* record a function or method exit in the profiler data */ void prof_leave(); /* find or create a function entry in the master profiler table */ class CVmHashEntryProfiler *prof_find_master_rec(const struct vm_profiler_rec *p); /* calculate an elapsed time */ void prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a, const vm_prof_time *b); /* add an elapsed time value to a cumulative elapsed time value */ void prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val); /* hash table enumeration callback for dumping profiler data */ static void prof_enum_cb(void *ctx0, class CVmHashEntry *entry0); /* validate the built-in function pointer at top of stack */ void validate_bifptr(VMG0_); /* * Function header size - obtained from the image file upon loading */ size_t funchdr_size_; /* * A pointer to a global variable in the object table (CVmObjTable) * containing the function to invoke for the SAY and SAYVAL opcodes. * This can be a function pointer, a function object, or nil. If this * is nil, the SAY opcode will throw an error. */ struct vm_globalvar_t *say_func_; /* * The method to invoke for the SAY and SAYVAL opcodes when a valid * "self" object is available. If no method is defined, this will * be set to VM_INVALID_PROP. */ vm_prop_id_t say_method_; /* * R0 - data register 0. This register stores function return * values. */ VM_IF_REGS_IN_STRUCT(vm_val_t r0_;) /* * native entry pointer value - this is simply the translated value of * the entry pointer (i.e., G_code_pool->get_ptr(entry_ptr_)) */ VM_IF_REGS_IN_STRUCT(const uchar *entry_ptr_native_;) /* * FP - frame pointer register. This points to the base of the * current stack activation frame. Local variables and parameters * are reachable relative to this register. */ VM_IF_REGS_IN_STRUCT(vm_val_t *frame_ptr_;) /* * Pointer to program counter - we use this in the debugger to create * pseudo-stack frames for system code when we recursively invoke the * VM, and for finding the current PC from intrinsic function code. */ VM_IF_REGS_IN_STRUCT(const uchar **pc_ptr_;) /* * Flag: VM is halting. This is used by the debugger to force the * program to stop executing. This is not used except with the * debug-mode interpreter. */ int halt_vm_; /* flag: profiling is active */ int profiling_; /* in case we have a profiler, include the profiler stack */ struct vm_profiler_rec *prof_stack_; size_t prof_stack_max_; /* next available index in the profiler stack */ size_t prof_stack_idx_; /* * Start of execution in the currently active function, since the last * call or return. This uses the OS-specific high-precision timer * (defined by os_prof_curtime() in vmprof.h). * * Each time we call a function or return from a function, we measure * the delta from this value, then add that in to the cumulative time * for the function. */ vm_prof_time prof_start_; /* * profiler master hash table - this is a table with one entry for * every function and method called since we began profiling, keyed by * method or function ID (the object.property for a method, or the * entrypoint code offset for a function) */ class CVmHashTable *prof_master_table_; }; #endif /* VMRUN_H */ ������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/unix/��������������������������������������������������������������������������0000755�0001750�0000144�00000000000�12145614111�014501� 5����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/unix/osnetunix.cpp�������������������������������������������������������������0000644�0001750�0000144�00000103065�12145504453�017255� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name osnetunix.cpp - TADS OS networking and threading: Unix implementation Function Notes Modified 04/06/10 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include <poll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <net/if.h> #include <sys/ioctl.h> #include <sys/times.h> #include <unistd.h> #include <time.h> #include <stdio.h> #include <curl/curl.h> #include "os.h" #include "t3std.h" #include "osifcnet.h" #include "vmnet.h" #include "vmfile.h" #include "vmdatasrc.h" /* * To enable extra code for libcurl debugging, define this macro */ // #define OSNU_CURL_DEBUG /* * ignore a return value (to suppress superfluous gcc warn_unused_result * warnings) */ void IGNORE(int) { } /* * Thread attributes */ pthread_attr_t G_thread_attr; /* * Master thread list */ TadsThreadList *G_thread_list = 0; /* ------------------------------------------------------------------------ */ /* * Thread/event resource leak checking */ #ifdef LEAK_CHECK OS_Counter mutex_cnt(0), event_cnt(0), thread_cnt(0), spin_lock_cnt(0); # define IF_LEAK_CHECK(x) x #else # define IF_LEAK_CHECK(x) #endif /* ------------------------------------------------------------------------ */ /* * debug logging (for debugging purposes only) */ void oss_debug_log(const char *fmt, ...) { #if 0 va_list args; va_start(args, fmt); char *str = t3vsprintf_alloc(fmt, args); va_end(args); time_t timer = time(0); struct tm *tblk = localtime(&timer); char *tmsg = asctime(tblk); size_t tmsgl = strlen(tmsg); if (tmsg > 0 && tmsg[tmsgl-1] == '\n') tmsg[--tmsgl] = '\0'; FILE *fp = fopen("/var/log/frob/tadslog2.txt", "a"); fprintf(fp, "[%s] %s\n", tmsg, str); fclose(fp); t3free(str); #endif } /* ------------------------------------------------------------------------ */ /* * Watchdog thread */ /* the watchdog quit event, as a global static */ OS_Event *OSS_Watchdog::quit_evt = 0; /* ------------------------------------------------------------------------ */ /* * Package initialization and termination */ /* * Initialize the networking and threading package */ void os_net_init(TadsNetConfig *config) { /* initialize the CURL (http client) library */ curl_global_init(0); /* set up the default thread attributes */ pthread_attr_init(&G_thread_attr); pthread_attr_setdetachstate(&G_thread_attr, PTHREAD_CREATE_JOINABLE); /* create the master thread list */ G_thread_list = new TadsThreadList(); /* if desired, start the watchdog thread */ if (config != 0 && (config->match("watchdog", "yes") || config->match("watchdog", "on"))) { /* launch the watchdog thread */ OSS_Watchdog *wt = new OSS_Watchdog(); if (!wt->launch()) oss_debug_log("failed to launch watchdog thread"); /* we're done with the thread object */ wt->release_ref(); } } /* * Clean up the package in preparation for application exit */ void os_net_cleanup() { /* if there's a watchdog thread, tell it to terminate */ if (OSS_Watchdog::quit_evt != 0) OSS_Watchdog::quit_evt->signal(); /* * sleep for a short time to let any threads that are already almost * done finish up their work */ usleep(10000); /* wait for threads to exit */ G_thread_list->wait_all(500); /* done with the master thread list */ G_thread_list->release_ref(); /* check for system resource leaks */ IF_LEAK_CHECK(printf("os_net_cleanup: mutexes=%ld, events=%ld, " "threads=%ld, spin locks=%ld\n", mutex_cnt.get(), event_cnt.get(), thread_cnt.get(), spin_lock_cnt.get())); /* clean up the CURL library */ curl_global_cleanup(); /* delete our thread attributes */ pthread_attr_destroy(&G_thread_attr); } /* ------------------------------------------------------------------------ */ /* * Local file selector dialog. We don't currently implement a stand-alone * Web UI mode on Unix platforms, so this is a no-op. */ int osnet_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, int file_type) { return OS_AFE_FAILURE; } /* * Connect to the client UI. A Web-based game calls this after starting * its internal HTTP server, to send instructions back to the client on how * the client UI can connect to the game. * * This Unix implementation currently only supports client/server mode. */ int osnet_connect_webui(VMG_ const char *addr, int port, const char *path, char **errmsg) { /* * Web server mode: our parent process is the conventional Web server * running the php launch page. The php launch page has a pipe * connection to our stdout. Send the start page information back to * the php page simply by writing the information to stdout. */ printf("\nconnectWebUI:http://%s:%d%s\n", addr, port, path); fflush(stdout); /* success */ *errmsg = 0; return TRUE; } /* ------------------------------------------------------------------------ */ /* * Local host information */ int os_get_hostname(char *buf, size_t buflen) { /* ask the system for the host name */ return !gethostname(buf, buflen); } int os_get_local_ip(char *buf, size_t buflen, const char *host) { /* presume failure */ int ok = FALSE; /* if the caller didn't provide a host name, look up the default */ char hostbuf[128]; if (host == 0 && !gethostname(hostbuf, sizeof(hostbuf))) host = hostbuf; /* * Start by asking the system for the host name via gethostname(), then * getting the IP addresses for the name. The complication is that on * many linux systems, this will only return 127.0.0.1, which isn't * useful because that's only the local loopback address. But we can * at least try... */ if (host != 0) { /* get the address info for the host name */ struct hostent *local_host = gethostbyname(buf); if (local_host != 0) { /* scan the IP addresses for this host name */ for (char **al = local_host->h_addr_list ; *al != 0 ; ++al) { /* get the IP address */ const char *ip = inet_ntoa(*(struct in_addr *)*al); /* if it's not a 127.0... address, use it */ if (memcmp(ip, "127.0.", 6) != 0) { /* this is an external address - return it */ lib_strcpy(buf, buflen, ip); return TRUE; } } } } /* * If we get this far, it means that gethostbyname() won't give us * anything besides the useless 127.0.0.1 loopback address. The next * step is to open a socket and do some mucking around in its system * structures via an ioctl to get a list of the network interfaces in * the system. */ /* clear out the ioctl address structure */ struct ifconf ifc; memset(&ifc, 0, sizeof(ifc)); ifc.ifc_ifcu.ifcu_req = 0; ifc.ifc_len = 0; /* no socket yet */ int s = -1; /* * Open a socket (we don't need to bind it to anything; just open it), * then do the SIOCGICONF ioctl on the socket to get its network * interface information. We need to do this in two passes: first we * make a call with a null request (ifcu_req) buffer just to find out * how much space we need for the buffer, then we allocate the buffer * and make another call to fetch the data. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0 && ioctl(s, SIOCGIFCONF, &ifc) >= 0 && (ifc.ifc_ifcu.ifcu_req = (struct ifreq *)t3malloc(ifc.ifc_len)) != 0 && ioctl(s, SIOCGIFCONF, &ifc) >= 0) { /* figure the number of interfaces in the list */ int numif = ifc.ifc_len / sizeof(struct ifreq); /* iterate over the interfaces in the list */ struct ifreq *r = ifc.ifc_ifcu.ifcu_req; for (int i = 0 ; i < numif ; ++i, ++r) { /* get the network address for this interface */ struct sockaddr_in *sin = (struct sockaddr_in *)&r->ifr_addr; const char *ip = inet_ntoa(sin->sin_addr); /* if it's not a 127.0... address, return it */ if (memcmp(ip, "127.0.", 6) != 0) { /* copy the result */ lib_strcpy(buf, buflen, ip); /* success! - and no need to look any further */ ok = TRUE; break; } } } /* if we allocated a result buffer, free it */ if (ifc.ifc_ifcu.ifcu_req != 0) t3free(ifc.ifc_ifcu.ifcu_req); /* if we opened a socket, close it */ if (s >= 0) close(s); /* return the status information */ return ok; } /* ------------------------------------------------------------------------ */ /* * Waitable object base class */ /* * Wait for the object with a timeout */ int OS_Waitable::wait(unsigned long timeout) { return get_event()->evt_wait(timeout); } /* * Test the object state without blocking */ int OS_Waitable::test() { return get_event()->evt_test(); } /* * Wait for multiple waitable objects */ int OS_Waitable::multi_wait(int cnt, OS_Waitable **objs, unsigned long timeout) { int i; int ret; struct timespec tm; /* figure the timeout, if applicable */ if (timeout != OS_FOREVER) figure_timeout(&tm, timeout); /* create an event to represent the group wait */ OS_Event group_evt(FALSE); /* add the group event as a subscriber to each individual event */ for (i = 0 ; i < cnt ; ++i) objs[i]->get_event()->subscribe(&group_evt); /* now wait until we get an individual event */ for (;;) { /* check to see if any individual event is fired yet */ for (i = 0, ret = OSWAIT_ERROR ; i < cnt ; ++i) { /* if this event is fired, we have a winner */ if (objs[i]->test()) { /* this is the event that fired */ ret = OSWAIT_EVENT + i; /* we only need one event, so stop looking */ break; } } /* if we found a fired event, we're done */ if (ret != OSWAIT_ERROR) break; /* * Since we didn't find a signaled event, wait for the group event. * We subscribed to each individual event, so when any of the * subject events are signaled, they'll also signal our group event * object, waking us up from this wait, at which point we'll go * back and look for which event was fired. Note that if there's a * timeout, we use the wait-with-timeout with the ending time we * figured; otherwise we just use a simple indefinite wait. */ ret = (timeout != OS_FOREVER ? group_evt.evt_wait(&tm) : (group_evt.evt_wait(), OSWAIT_EVENT)); /* * If the group event fired, continue with the loop, so that we go * back and find which individual subject event fired. Otherwise, * we have a timeout or error, so in either case abort the wait and * return the result to our caller. */ if (ret != OSWAIT_EVENT) break; } /* remove each subscription */ for (i = 0 ; i < cnt ; ++i) objs[i]->get_event()->unsubscribe(&group_evt); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Socket monitor thread */ class OS_Socket_Mon_Thread: public OS_Thread { public: OS_Socket_Mon_Thread(OS_CoreSocket *s) { /* keep a reference on our socket */ this->s = s; s->add_ref(); } ~OS_Socket_Mon_Thread() { /* we're done with the socket */ s->release_ref(); } /* main thread entrypoint */ void thread_main() { /* create the quit notifier pipe */ if (pipe(qpipe) == -1) { s->close(); s->ready_evt->signal(); return; } /* * add a cleanup handler: on exit, we need to signal the 'ready' * event so that any other threads waiting for the socket will * unblock */ oss_debug_log("starting socket monitor thread %lx", (long)tid); pthread_cleanup_push(thread_cleanup, this); /* loop as long as the socket is open */ for (;;) { /* * Wait for the socket to go into blocking mode. Unix doesn't * have an "inverse select" function that blocks while the * handle is unblocked, so we have to roll our own mechanism * here, using a separate event set up for this purpose. The * socket signals this event each time it encounters an * EWOULDBLOCK error in a send/recv. */ s->blocked_evt->wait(); #if 1 /* * Wait for the socket to become ready. Also poll the quit * notification pipe, so that we'll wake up as soon as our * socket has been closed locally. */ pollfd fd[2]; fd[0].fd = s->s; fd[0].events = (s->wouldblock_sending ? POLLOUT : POLLIN | POLLPRI) | POLLHUP; fd[1].fd = qpipe[0]; fd[1].events = POLLIN; /* wait; if that fails, terminate the thread */ if (poll(fd, 2, -1) < 0) { if (errno != EINTR) break; } /* check to see if the socket is ready, or if it's been closed */ if (fd[1].revents != 0) { /* quit event - exit the loop */ break; } else if (fd[0].revents != 0) { /* ready - reset the 'blocked' event, and signal 'ready' */ s->blocked_evt->reset(); s->ready_evt->signal(); } else { /* not ready - reset 'ready' and signal 'blocked' */ s->ready_evt->reset(); s->blocked_evt->signal(); } #else /* initialize a selection set for the socket */ fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); /* add the socket to the appropriate sets */ FD_SET(s->s, s->wouldblock_sending ? &wfds : &rfds); /* wait for the socket to become ready */ if (select(s->s + 1, &rfds, &wfds, 0, 0) <= 0) break; /* * if the socket has an exception, give up - the socket must * have been closed or reset, in which case the monitor thread * is no longer useful */ if (FD_ISSET(s->s, &efds)) break; /* if the socket selector fired, the socket is ready */ if (FD_ISSET(s->s, &rfds) || FD_ISSET(s->s, &wfds)) { /* reset the 'blocked' event, and signal 'ready' */ s->blocked_evt->reset(); s->ready_evt->signal(); } else { /* reset 'ready' and signal 'blocked' */ s->ready_evt->reset(); s->blocked_evt->signal(); } #endif } /* pop and invoke the cleanup handler */ pthread_cleanup_pop(TRUE); } /* notify the thread to shut down */ void shutdown() { /* write the notification to the quit pipe */ (void)write(qpipe[1], "Q", 1); s->blocked_evt->signal(); } private: static void thread_cleanup(void *ctx) { /* get the thread object (it's the context) */ OS_Socket_Mon_Thread *t = (OS_Socket_Mon_Thread *)ctx; /* before exiting, let anyone waiting for us go */ t->s->blocked_evt->signal(); t->s->ready_evt->signal(); } /* our socket */ OS_CoreSocket *s; /* quit notification pipe, for sending a 'quit' signal to the thread */ int qpipe[2]; }; /* ------------------------------------------------------------------------ */ /* * Core Socket. This is the base class for ordinary data channel sockets * and listener sockets. */ /* last incoming network event time */ time_t OS_CoreSocket::last_incoming_time = 0; /* * Destruction */ OS_CoreSocket::~OS_CoreSocket() { /* close the socket */ close(); /* release resources */ if (ready_evt != 0) ready_evt->release_ref(); if (blocked_evt != 0) blocked_evt->release_ref(); if (mon_thread != 0) mon_thread->release_ref(); } /* * Close the socket */ void OS_CoreSocket::close() { /* close and forget the system socket */ if (s != -1) { ::close(s); s = -1; } /* release our non-blocking status events */ if (ready_evt != 0) ready_evt->signal(); /* kill the monitor thread, if applicable; wait for it to exit */ if (mon_thread != 0) { mon_thread->shutdown(); mon_thread->wait(); } } /* * Set the socket to non-blocking mode */ void OS_CoreSocket::set_non_blocking() { /* if it's already in non-blocking mode, there's nothing to do */ if (ready_evt != 0) return; /* set the system socket to non-blocking mode */ fcntl(s, F_SETFL, O_NONBLOCK); /* * Create the "ready" event for the socket. The monitor thread signals * this event whenever select() shows that the socket is ready. The * send() and recv() methods reset this event whenever the underlying * socket send/recv functions return EWOULDBLOCK. Callers can thus * wait on this event when they want to block until the socket is * ready. * * The reason that callers would want an event object rather than just * actually blocking on the socket call is that they can combine this * event with others in a multi-wait, so that they'll stop blocking * when the socket is ready OR another event fires, whichever comes * first. * * This is a manual reset event because it follows the socket's * readiness state - it doesn't reset on releasing a thread, but rather * when the thread actually exhausts the socket buffer and would cause * the socket to block again. */ ready_evt = new OS_Event(TRUE); /* * Create the "blocked" event. The send() and recv() methods signal * this event when the underlying socket calls return EWOULDBLOCK. The * monitor thread waits on this event before calling select() on the * socket, so that it doesn't loop endlessly when the socket is ready * for an extended period. Instead, the monitor thread blocks on this * until the main thread encounters an EWOULDBLOCK error, at which * point the monitor wakes up and performs a select(), which will then * block until the socket becomes ready. */ blocked_evt = new OS_Event(TRUE); /* * Assume the socket starts out ready. When the caller performs the * first socket operation, they'll determine the actual state - if the * send/recv succeed, it really was ready. If EWOULDBLOCK is returned, * we know that it wasn't ready, at which point we'll update both * events so that the monitor thread can watch for eventual readiness. */ ready_evt->signal(); /* * Launch the monitor thread. This thread synchronizes the state of * the sock_evt event with the underlying socket state. */ mon_thread = new OS_Socket_Mon_Thread(this); mon_thread->launch(); } /* ------------------------------------------------------------------------ */ /* * HTTP Client */ /* receive callback */ static size_t http_get_recv(void *ptr, size_t siz, size_t nmemb, void *stream) { /* copy the received data to the caller's stream, if we have one */ if (stream != 0) { /* cast the stream to our CVmDataSource type */ CVmDataSource *reply = (CVmDataSource *)stream; /* * Copy the data to the buffer; if write() returns zero, it means * we were successful; to indicate success to CURL, return the * number of bytes written, which equals the number of bytes we * were asked to write. If write() returns non-zero, it's an * error, which we indicate by returning 0 to CURL to say that we * didn't successfully write any bytes. */ size_t bytes = siz * nmemb; return reply->write(ptr, bytes) ? 0 : bytes; } else { /* there's no stream, so we didn't copy anything */ return 0; } } /* send callback */ static size_t http_get_send(void *ptr, size_t siz, size_t nmemb, void *stream) { /* copy the data from the stream to the buffer */ if (stream != 0) { /* cast the stream to our payload item object */ CVmDataSource *s = (CVmDataSource *)stream; /* read from the stream into the buffer */ return s->readc(ptr, siz * nmemb); } else { /* there's no stream, so we didn't copy anything */ return 0; } } /* header receive callback */ static size_t http_get_hdr(void *ptr, size_t siz, size_t nmemb, void *stream) { /* get our stream object, properly cast */ CVmMemorySource *hstream = (CVmMemorySource *)stream; /* write the header to the stream */ if (hstream->write(ptr, siz * nmemb)) return 0; /* success - we handled all of the bytes passed in */ return siz * nmemb; } /* * curl debug callback - curl invokes this to send us error information if * anything goes wrong in a curl_easy_perform() call */ #ifdef OSNU_CURL_DEBUG static int curl_debug(CURL *h, curl_infotype infotyp, char *info, size_t infolen, void *) { if (infotyp == CURLINFO_TEXT) fprintf(stderr, "%.*s\n", (int)infolen, info); return 0; } #endif /* * Send an HTTP request as a client */ int OS_HttpClient::request(int opts, const char *host, unsigned short portno, const char *verb, const char *resource, const char *send_headers, size_t send_headers_len, OS_HttpPayload *payload, CVmDataSource *reply, char **headers, char **location, const char *ua) { char *url = 0; /* full resource URL */ CURL *h = 0; /* libcurl transaction handle */ char *formbuf = 0; /* application/x-www-form-urlencoded buffer */ size_t formlen = 0; /* length of formbuf data */ int ret = ErrOther; /* result code */ curl_httppost *formhead = 0; /* multipart form field list head */ curl_httppost *formtail = 0; /* multipart form field list tail */ CVmMemorySource *hstream = 0; /* memory stream for capturing headers */ curl_slist *hdr_slist = 0; /* caller's headers, in curl slist format */ /* initially clear the location, if applicable */ if (location != 0) *location = 0; /* presume no headers */ if (headers != 0) *headers = 0; /* figure the scheme */ const char *scheme = ((opts & OptHTTPS) != 0 ? "https" : "http"); /* set up a handle for the communications */ h = curl_easy_init(); /* if that failed, return failure */ if (h == 0) { ret = ErrOther; goto done; } /* build the full URL */ url = t3sprintf_alloc("%s://%s:%d%s", scheme, host, portno, resource); /* set up the connection to the resource */ curl_easy_setopt(h, CURLOPT_URL, url); /* we don't want a progress meter or signals */ curl_easy_setopt(h, CURLOPT_NOPROGRESS, (long)1); curl_easy_setopt(h, CURLOPT_NOSIGNAL, (long)1); /* set our write callback */ curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, http_get_recv); curl_easy_setopt(h, CURLOPT_WRITEDATA, reply); /* set our header write callback, if the caller wants the headers */ if (headers != 0) { hstream = new CVmMemorySource(1024); curl_easy_setopt(h, CURLOPT_HEADERFUNCTION, http_get_hdr); curl_easy_setopt(h, CURLOPT_WRITEHEADER, hstream); } /* if there's a user agent string, send it along */ if (ua != 0 && ua[0] != '\0') curl_easy_setopt(h, CURLOPT_USERAGENT, ua); /* set the verb */ if (stricmp(verb, "GET") == 0) { /* this is the default verb - no libcurl option setting is needed */ } else if (stricmp(verb, "POST") == 0 && payload != 0) { /* check for a multipart/formdata upload */ if (payload->count_items() == 1 && payload->get(0)->name[0] == '\0') { /* * We have exactly one file-type item, and the item has an * empty name. In this case, the caller has pre-encoded the * content body, so we don't want to apply any further POST * encoding; simply use the content exactly as given. */ /* get the file to send - this is the payload item's stream */ CVmDataSource *stream = payload->get(0)->stream; /* set up the source data */ curl_easy_setopt(h, CURLOPT_READFUNCTION, http_get_send); curl_easy_setopt(h, CURLOPT_READDATA, stream); curl_easy_setopt(h, CURLOPT_INFILESIZE, stream->get_size()); } else if (payload->is_multipart()) { /* we have file attachments - build a curl_httppost list */ int cnt = payload->count_items(); for (int i = 0 ; i < cnt ; ++i) { /* get this item */ OS_HttpPayloadItem *item = payload->get(i); /* check the item type */ if (item->stream != 0) { /* this is a file upload field */ if (curl_formadd( &formhead, &formtail, CURLFORM_COPYNAME, item->name, CURLFORM_FILENAME, item->val, CURLFORM_CONTENTTYPE, item->mime_type, CURLFORM_CONTENTSLENGTH, item->stream->get_size(), CURLFORM_STREAM, item->stream, CURLFORM_END)) goto done; /* make sure we've set the read callback */ curl_easy_setopt(h, CURLOPT_READFUNCTION, http_get_send); } else { /* this is a simple name/value pair */ if (curl_formadd(&formhead, &formtail, CURLFORM_COPYNAME, item->name, CURLFORM_COPYCONTENTS, item->val, CURLFORM_END)) goto done; } } /* set up the post with the field list */ curl_easy_setopt(h, CURLOPT_POST, (long)1); curl_easy_setopt(h, CURLOPT_HTTPPOST, formhead); } else { /* it's a simple form - build the urlencoded form data */ formbuf = payload->urlencode(formlen); /* set the form data */ curl_easy_setopt(h, CURLOPT_POST, (long)1); curl_easy_setopt(h, CURLOPT_POSTFIELDS, formbuf); curl_easy_setopt(h, CURLOPT_POSTFIELDSIZE, (long)formlen); } } else if (stricmp(verb, "PUT") == 0 && payload != 0 && payload->count_items() == 1) { /* set the PUT verb */ curl_easy_setopt(h, CURLOPT_UPLOAD, (long)1); /* get the file to send - this is the single payload item's stream */ CVmDataSource *stream = payload->get(0)->stream; /* set up the source data */ curl_easy_setopt(h, CURLOPT_READFUNCTION, http_get_send); curl_easy_setopt(h, CURLOPT_READDATA, stream); curl_easy_setopt(h, CURLOPT_INFILESIZE, (long)stream->get_size()); } else { /* it's not GET, POST, or PUT - we don't accept other verbs */ curl_easy_cleanup(h); return ErrParams; } /* * if a location parameter was provided, the caller wants to get any * redirect location returned, rather than following the redirect; * otherwise they want us to follow redirects */ curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, (long)(location == 0)); /* add the caller's custom headers, if provided */ if (send_headers != 0) { /* add each header to the list */ const char *p = send_headers; size_t rem = send_headers_len; while (rem != 0) { /* skip leading spaces */ for ( ; rem != 0 && *p != '\r' && *p != '\n' && is_space(*p) ; ++p, --rem) ; /* scan to the CR-LF */ const char *h = p; for ( ; rem != 0 && !(rem >= 2 && memcmp(p, "\r\n", 2) == 0) ; ++p, --rem) ; /* if the line isn't empty, add the header */ if (p != h) { /* get a null-terminated copy of the header */ char *hn = lib_copy_str(h, p - h); /* add the header */ hdr_slist = curl_slist_append(hdr_slist, hn); /* done with the copy of the string */ lib_free_str(hn); } /* skip the CR-LF */ if (rem >= 2) p += 2, rem -= 2; } /* set the header list in curl */ curl_easy_setopt(h, CURLOPT_HTTPHEADER, hdr_slist); } /* * To debug problems with CURL, enable the next two lines, which tell * CURL to call curl_debug() with detailed status information as it * goes through the curl_easy_perform() call. The status information * can be very helpful in tracking down problems. You can look at the * status calls to curl_debug() using gdb or by modifying curl_debug() * (see above) to write to a log file or the like. */ #ifdef OSNU_CURL_DEBUG curl_easy_setopt(h, CURLOPT_VERBOSE, 1); curl_easy_setopt(h, CURLOPT_DEBUGFUNCTION, curl_debug); #endif /* do the transfer */ if (!curl_easy_perform(h)) { /* get the HTTP response code */ long http_stat; curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &http_stat); /* the HTTP response code is the return value */ ret = (int)http_stat; /* if they wanted redirect data, and we got redirect data, return it */ if (location != 0 && http_stat == 301) { char *l = 0; curl_easy_getinfo(h, CURLINFO_REDIRECT_URL, &l); /* copy it back to the caller */ *location = lib_copy_str(l); } /* if they wanted headers, return the headers */ if (headers != 0) { /* get the size of the headers */ size_t hlen = hstream->get_size(); /* allocate space for a copy for the caller (with null byte) */ *headers = new char[hlen + 1]; /* copy the headers into the new buffer */ hstream->seek(0, OSFSK_SET); hstream->read(*headers, hlen); /* add the null terminator */ (*headers)[hlen] = '\0'; } } done: /* close the handle */ if (h != 0) curl_easy_cleanup(h); /* delete the URL buffer */ if (url != 0) lib_free_str(url); /* free the form data buffer */ if (formbuf != 0) t3free(formbuf); /* delete the multipart form field list */ if (formhead != 0) curl_formfree(formhead); /* delete the header capture stream */ if (hstream != 0) delete hstream; /* free the header slist */ if (hdr_slist != 0) curl_slist_free_all(hdr_slist); /* return the result code */ return ret; } /* ------------------------------------------------------------------------ */ /* * Thread */ OS_Thread::OS_Thread() { /* count it in the leak tracker */ IF_LEAK_CHECK(thread_cnt.inc()); /* we don't have a thread ID yet */ tid_valid = FALSE; /* create the event to signal at thread completion */ done_evt = new OS_Event(TRUE); /* add myself to the master thread list */ G_thread_list->add(this); } OS_Thread::~OS_Thread() { /* * Detach the thread. We don't use 'join' to detect when the * thread terminates; instead, we use our thread event. This lets * the operating system delete the thread resources (in particular, * its stack memory) immediately when the thread exits, rather than * waiting for a 'join' that might never come. */ if (tid_valid) pthread_detach(tid); /* release our 'done' event */ done_evt->release_ref(); /* clean up the master thread list for our deletion */ if (G_thread_list != 0) G_thread_list->clean(); /* count it for leak tracking */ IF_LEAK_CHECK(thread_cnt.dec()); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/unix/osnetunix.h���������������������������������������������������������������0000644�0001750�0000144�00000147265�12145505173�016734� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name osnetunix.h - TADS networking and threading, Unix implementation Function Notes Modified 04/07/10 MJRoberts - Creation */ #ifndef OSNETUNIX_H #define OSNETUNIX_H /* system headers */ #include <pthread.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <sys/time.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> #include <math.h> #include <ctype.h> #include <fcntl.h> #include <signal.h> #include <stdarg.h> /* true/false */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* infinite timeout value */ #define OS_FOREVER ((ulong)-1) /* libcurl headers */ #include <curl/curl.h> /* OS X spinlocks */ #ifdef __APPLE__ #include <libkern/OSAtomic.h> typedef OSSpinLock pthread_spinlock_t; inline void pthread_spin_init(OSSpinLock* p, int) { *p=OS_SPINLOCK_INIT; } inline void pthread_spin_destroy(OSSpinLock*) {} #define pthread_spin_lock OSSpinLockLock #define pthread_spin_unlock OSSpinLockUnlock #endif /* * Most Unix variants have at least one of SO_NOSIGPIPE or MSG_NOSIGNAL, * and they seem to serve the same purpose everywhere, so if either is * missing just define it away (i.e., define as 0, as these are bit flags). * This doesn't handle the case where a system lacks *both*, but that seems * to be rare, and the code necessary to work around it is messy enough * that we're going to ignore the possibility. */ #ifdef SO_NOSIGPIPE # ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 # endif #else # ifdef MSG_NOSIGNAL # define SO_NOSIGPIPE 0 # endif #endif /* include the base layer headers */ #include "osifcnet.h" /* base TADS 3 header */ #include "t3std.h" /* debug logging */ void oss_debug_log(const char *fmt, ...); /* ------------------------------------------------------------------------ */ /* * Most compilers provide atomic operation intrinsics, and C++-11 actually * made them standard. If we detect C++-11, we use std::atomic. If not, * we check for GCC and Clang intrinsic support (both new and legacy.) * * Using intrinsics is preferable to using a spinlock, since they translate * to atomic machine instructions (if supported by the machine) and no * locking is happening. * * It might look a bit redundant to check for the new intrinsics * (__atomic_*) since they were introduced specifically for C++-11 support; * if they are available, one would think that std::atomic is too. But * that's usually not the case. In order to get to std::atomic, the * compiler has to be explicitly told to enable support for C++-11 (through * a command-line switch, usually "-std=c++11".) In the future, C++-11 * will be the default in most compilers. But as of now, it's not; neither * in GCC nor in Clang. */ #ifndef __has_builtin /* compatibility with compilers other than Clang */ #define __has_builtin(x) 0 #endif #if __cplusplus >= 201103L #include <atomic> #define ATOMIC_TYPE(typ) std::atomic<typ> #define ATOMIC_INC_FETCH(var) (var.fetch_add(1, std::memory_order_relaxed) + 1) #define ATOMIC_DEC_FETCH(var) (var.fetch_sub(1, std::memory_order_relaxed) - 1) #elif ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) \ || (defined(__clang__) && __has_builtin(__atomic_add_fetch)) #define ATOMIC_TYPE(typ) typ #define ATOMIC_INC_FETCH(var) (__atomic_add_fetch((&var), 1, __ATOMIC_RELAXED)) #define ATOMIC_DEC_FETCH(var) (__atomic_sub_fetch((&var), 1, __ATOMIC_RELAXED)) #elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) \ || (defined(__clang__) && __has_builtin(__sync_add_and_fetch)) #define ATOMIC_TYPE(typ) typ #define ATOMIC_INC_FETCH(var) (__sync_add_and_fetch((&var), 1)) #define ATOMIC_DEC_FETCH(var) (__sync_sub_and_fetch((&var), 1)) #endif /* ------------------------------------------------------------------------ */ /* * Reference counter */ class OS_Counter { public: OS_Counter(long c = 1) /* * Avoid having this default-constructed, as that would complicate * things in C++-11 (we would need to explicitly call an * initializer before we can use the atomic type.) */ : cnt(c) { #ifndef ATOMIC_TYPE /* initialize the system spin lock, for serializing access */ pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE); #endif } #ifndef ATOMIC_TYPE ~OS_Counter() { /* destroy the system resources for the lock */ pthread_spin_destroy(&lock); } #endif /* get the current counter value */ long get() const { return cnt; } /* * Increment/decrement. These return the post-update result; the * operation must be atomic, so that the return value is the result of * this increment or decrement only, even if another thread * concurrently makes another update. */ long inc() { #ifdef ATOMIC_TYPE return ATOMIC_INC_FETCH(cnt); #else /* do the inc-and-fetch with a spin lock to make it atomic */ pthread_spin_lock(&lock); long newval = ++cnt; pthread_spin_unlock(&lock); /* return the updated value */ return newval; #endif } long dec() { #ifdef ATOMIC_TYPE return ATOMIC_DEC_FETCH(cnt); #else /* do the dec-and-fetch with a spin lock to make it atomic */ pthread_spin_lock(&lock); long newval = --cnt; pthread_spin_unlock(&lock); /* return the updated value */ return newval; #endif } private: #ifdef ATOMIC_TYPE /* reference count */ ATOMIC_TYPE(long) cnt; #else /* reference count */ long cnt; /* spin lock to protect the counter against concurrent access */ pthread_spinlock_t lock; #endif }; /* ------------------------------------------------------------------------ */ /* * Remove the ATOMIC_TYPE macros - we only needed them to define the * OS_Counter class, so we can keep them local to this section of the * header to avoid any potential name conflicts. */ #ifdef ATOMIC_TYPE # undef ATOMIC_TYPE # undef ATOMIC_INC_FETCH # undef ATOMIC_DEC_FETCH #endif #ifndef __clang__ # if __has_builtin == 0 # undef __has_builtin # endif #endif /* ------------------------------------------------------------------------ */ /* * System resource leak tracking */ #ifdef LEAK_CHECK extern OS_Counter mutex_cnt, event_cnt, thread_cnt, spin_lock_cnt; # define IF_LEAK_CHECK(x) x #else # define IF_LEAK_CHECK(x) #endif /* ------------------------------------------------------------------------ */ /* * Include the reference-counted object header */ #include "vmrefcnt.h" /* ------------------------------------------------------------------------ */ /* * Waitable objects */ /* private structure: multi-wait subscriber link */ struct osu_waitsub { osu_waitsub(class OS_Event *e, osu_waitsub *nxt) { this->e = e; this->nxt = nxt; } /* this event */ class OS_Event *e; /* next subscriber in list */ osu_waitsub *nxt; }; /* * Waitable object. This is the base class for objects that can be used in * a multi-object wait. A waitable object has two states: unsignaled and * signaled. Waiting for the object blocks the waiting thread as long as * the object is in the unsignaled state, and releases the thread (allows * it to continue running) as soon as the object enters the signaled state. * Waiting for an object that's already in the signaled state simply allows * the calling thread to continue immediately without blocking. */ class OS_Waitable { public: OS_Waitable() { } virtual ~OS_Waitable() { } /* * Wait for the object, with a maximum wait of 'timeout' milliseconds. * Returns an OSWAIT_xxx code to indicate what happened. */ int wait(unsigned long timeout = OS_FOREVER); /* * Test to see if the object is ready, without blocking. Returns true * if the object is ready, false if not. If the object is ready, * waiting for the object would immediately release the thread. * * Note that testing some types of objects "consumes" the ready state * and resets the object to not-ready. This is true of auto-reset * event objects. */ int test(); /* * Wait for multiple objects. This blocks until at least one event in * the list is signaled, at which point it returns OSWAIT_OBJECT+N, * where N is the array index in 'events' of the event that fired. If * the timeout (given in milliseconds) expires before any events fire, * we return OSWAIT_TIMEOUT. If the timeout is omitted, we wait * indefinitely, blocking until one of the events fires. */ static int multi_wait(int cnt, OS_Waitable **objs, unsigned long timeout = OS_FOREVER); protected: /* * Figure a timeout end time. This adds the given timeout to the * current system time to get the system time at the expiration of the * timeout. */ static void figure_timeout(struct timespec *tm, unsigned long timeout) { /* figure the timeout interval in seconds */ long sec = timeout / 1000UL; /* add the timeout to the current time */ os_time_t cur_sec; long cur_nsec; os_time_ns(&cur_sec, &cur_nsec); cur_sec += sec; cur_nsec += (timeout % 1000UL) * 1000000L; if (cur_nsec > 999999999L) { cur_nsec -= 1000000000L; cur_sec++; } /* pass back the resulting timeout */ tm->tv_sec = cur_sec; tm->tv_nsec = cur_nsec; } /* * Get the associated event object. For the unix implementation, all * waitable objects are waitable by virtue of having associated event * objects. */ virtual class OS_Event *get_event() = 0; }; /* ------------------------------------------------------------------------ */ /* * Event */ class OS_Event: public CVmRefCntObj, public OS_Waitable { friend class OS_Waitable; public: OS_Event(int manual_reset) { /* remember the reset mode */ this->manual_reset = manual_reset; /* initial state is not signaled (zero 'set's) */ cnt = 0; /* initialize the system resources */ pthread_cond_init(&cond, 0); pthread_mutex_init(&mutex, 0); /* no multi-wait subscribers yet */ sub_head = sub_tail = 0; /* count it for leak tracking */ IF_LEAK_CHECK(event_cnt.inc()); } ~OS_Event() { /* release the system resources */ pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); /* count it for leak tracking */ IF_LEAK_CHECK(event_cnt.dec()); } /* * Signal the event. In the case of a manual-reset event, this * releases all threads waiting for the event, and leaves the event in * a signaled state until it's explicitly reset. For an auto-reset * event, this releases only one thread waiting for the event and then * automatically resets the event. */ void signal() { /* acquire the mutex so we can modify the counter */ pthread_mutex_lock(&mutex); /* increase the counter */ cnt += 1; /* * Notify subscribers. These are all ad hoc events created by * multi_wait(), so there's no danger of a deadlock: they can't be * subscribed in turn, so they'll never run through this code * themselves. Note that our subscriber list is protected by our * own mutex, so the fact that we've acquired our mutex means that * the list can't be updated while we're working. * * We'll simply notify all of our subscribers and let them sort out * amongst themselves who gets the event. For a manual-reset * event, they'll all get it, at least as long as no one resets it * before they all get CPU time. For an auto-reset event, only one * subscriber will get the event, since it will reset as soon as * the first waiter gets CPU time. But it still doesn't hurt to * wake everyone: they'll just look at their subscribed events and * see that none of them are in fact available, so they'll realize * that they missed their window and go back into another wait. */ for (osu_waitsub *s = sub_head ; s != 0 ; s = s->nxt) s->e->signal(); /* release the mutex */ pthread_mutex_unlock(&mutex); /* set the condition */ pthread_cond_signal(&cond); } /* * Reset the event. This has no effect for an auto-reset event. */ void reset() { /* this only applies to manual-reset events */ if (manual_reset) { /* acquire the mutex so we can modify the counter */ pthread_mutex_lock(&mutex); /* reset the counter to zero */ cnt = 0; /* release the mutex */ pthread_mutex_unlock(&mutex); } } protected: /* we're obviously our own waitable event object */ virtual class OS_Event *get_event() { return this; } private: /* * Wait for the event. If the event is already in the signaled state, * this returns immediately. Otherwise, this blocks until another * thread signals the event. For an auto-reset event, the system * immediately resets the event as soon as a thread is released. */ void evt_wait() { /* acquire the mutex */ pthread_mutex_lock(&mutex); /* be sure to unlock the mutex on the way out */ pthread_cleanup_push(evt_wait_cleanup, this); /* wait for the count to come up positive */ while (cnt <= 0) { /* wait for the condition */ if (pthread_cond_wait(&cond, &mutex) && errno != EINTR) break; } /* if it's an auto-reset event, reduce the counter */ if (!manual_reset) cnt -= 1; /* clean up (release the mutex) */ pthread_cleanup_pop(TRUE); } /* on the way out of an event wait, release our mutex */ static void evt_wait_cleanup(void *ctx) { pthread_mutex_unlock(&((OS_Event *)ctx)->mutex); } /* * Wait for the event with a timeout, given in milliseconds. This * works the same way as wait(), but if the timeout expires before the * event is signaled, this aborts the wait and returns OSWAIT_TIMEOUT. * If the event is signaled before the timeout expires, we return with * OSWAIT_EVENT. */ int evt_wait(unsigned long timeout) { /* if the timeout is "forever", use the untimed wait */ if (timeout == OS_FOREVER) { evt_wait(); return OSWAIT_EVENT; } /* figure the ending time for the wait */ struct timespec tm; figure_timeout(&tm, timeout); /* do the wait */ return evt_wait(&tm); } /* * Wait for the event with a timeout, given as an ending time in terms * of the system clock. */ int evt_wait(const struct timespec *tm) { int rc = 0; /* acquire the mutex */ pthread_mutex_lock(&mutex); /* be sure we unlock the mutex on the way out */ pthread_cleanup_push(evt_wait_cleanup, this); /* wait until we get a positive counter or the timeout expires */ while (cnt <= 0) { /* wait for it */ if ((rc = pthread_cond_timedwait(&cond, &mutex, tm)) != 0 && errno != EINTR) break; } /* if the wait succeeded, and it's an auto-reset event, update the count */ if (rc == 0 && !manual_reset) cnt -= 1; /* clean up (release the mutex) */ pthread_cleanup_pop(TRUE); /* interpret the results */ return (rc == 0 ? OSWAIT_EVENT : rc == ETIMEDOUT ? OSWAIT_TIMEOUT : OSWAIT_ERROR); } /* * Test the event, without blocking. This returns true if the event is * in the signaled state, false if not. */ int evt_test() { /* acquire the mutex */ pthread_mutex_lock(&mutex); /* if the counter is positive, the condition is signaled for us */ int signaled = (cnt > 0); /* if it's signaled, and it's auto-reset, consume one count */ if (signaled && !manual_reset) cnt -= 1; /* release the mutex */ pthread_mutex_unlock(&mutex); /* return the signal status */ return signaled; } /* * Subscribe a multi-wait event object. This adds the object to our * list of multi-wait events that will be notified when this event * fires. This will wake up the multi-waiters, so that they can * determine whether to wake up from their overall wait. */ void subscribe(OS_Event *e) { /* acquire the mutex while we're working */ pthread_mutex_lock(&mutex); /* create and link in the new subscriber link */ osu_waitsub *sub = new osu_waitsub(e, 0); /* * Link it at the end of our current list. This naturally causes * round-robin dispatch if we have multiple subscribers waiting for * an auto-reset event. The first member of the list will be the * first to get the signal and thus the first to wake up * (presumably - it could depend on the OS scheduling policy, but * in practice it seems to work this way on Linux). When it awakes * it will unsubscribe, leaving the next subscriber as the head of * the list. If the same caller that awoke does some work and then * turns around and multi-waits for the same event again, it'll go * to the end of the list, so it won't get the signal again until * the others that were already in the list ahead of it get their * chance. */ if (sub_tail != 0) sub_tail->nxt = sub; else sub_head = sub; sub_tail = sub; /* release the mutex */ pthread_mutex_unlock(&mutex); } /* * Unsubscribe a multi-wait event object. */ void unsubscribe(OS_Event *e) { /* acquire the mutex while we're working */ pthread_mutex_lock(&mutex); /* scan our list for the given event */ osu_waitsub *cur, *prv; for (prv = 0, cur = sub_head ; cur != 0 ; prv = cur, cur = cur->nxt) { /* if this is the one we're looking for, unlink it */ if (cur->e == e) { /* unlink this item */ if (prv != 0) prv->nxt = cur->nxt; else sub_head = cur->nxt; /* if it's the tail, move back the tail */ if (cur == sub_tail) sub_tail = prv; /* done searching */ break; } } /* release the mutex */ pthread_mutex_unlock(&mutex); } /* * Is this a manual-reset event? True means that this is a manual * event: signaling the event will release all threads waiting for the * event, and the event will remain signaled until it's explicitly * cleared. False means that this is an auto-reset event: signaling * the event releases only ONE thread waiting for the event, and as * soon as the thread is released, the system automatically clears the * event, so no more threads will be released until the next signal. */ int manual_reset; /* the signal count */ int cnt; /* the condition object */ pthread_cond_t cond; /* the system mutex object */ pthread_mutex_t mutex; /* head of our list of multi-wait subscribers */ osu_waitsub *sub_head, *sub_tail; }; /* ------------------------------------------------------------------------ */ /* * Mutex. */ class OS_Mutex: public CVmRefCntObj { public: OS_Mutex() { /* count it for leak tracking */ IF_LEAK_CHECK(mutex_cnt.inc()); /* initialize it */ pthread_mutex_init(&h, 0); } ~OS_Mutex() { /* destroy the system resources */ pthread_mutex_destroy(&h); /* count it for leak tracking */ IF_LEAK_CHECK(mutex_cnt.dec()); } /* * Lock the mutex. Blocks until the mutex is available. */ void lock() { pthread_mutex_lock(&h); } /* * Test the mutex: if the mutex is available, locks the mutex and * returns true; if not, returns false. Doesn't block in either case. * On a 'true' return, the mutex is locked, so the caller must unlock * it when done. */ int test() { return pthread_mutex_trylock(&h) == 0; } /* * Unlock the mutex. This can only be used after a successful lock() * or test(). This releases our lock and makes the mutex available to * other threads. */ void unlock() { pthread_mutex_unlock(&h); } private: /* the underlying system mutex data */ pthread_mutex_t h; }; /* ------------------------------------------------------------------------ */ /* * Socket error indicator - returned from OS_Socket::send() and recv() if * an error occurs, in lieu of a valid length value. */ #define OS_SOCKET_ERROR (-1) /* * Error values for OS_Socket::last_error(). */ #define OS_EWOULDBLOCK EWOULDBLOCK /* send/recv would block */ #define OS_ECONNRESET ECONNRESET /* connection reset by peer */ #define OS_ECONNABORTED ECONNABORTED /* connection aborted on this end */ /* ------------------------------------------------------------------------ */ /* * Socket. This is a thin layer implementing the same model as the Unix * socket API. Windows provides a socket library of its own, but it's * quite idiosyncratic, so we can't just write code directly to the * standard Unix API. */ class OS_CoreSocket: public CVmRefCntObj, public OS_Waitable { friend class OS_Socket_Mon_Thread; public: OS_CoreSocket() { /* no socket yet */ s = -1; /* no error yet */ err = 0; /* no non-blocking-mode thread yet */ ready_evt = 0; blocked_evt = 0; mon_thread = 0; wouldblock_sending = FALSE; } ~OS_CoreSocket(); /* * Set the socket to non-blocking mode. In this mode, a read/write on * the socket will immediately with an error code if the socket's data * buffer is empty on read or full on write. By default, these calls * "block": they don't return until at least one byte is available to * read or there's buffer space for all of the data being written. */ void set_non_blocking(); /* * In non-blocking mode, the caller must manually reset the socket's * event waiting status after each successful wait. */ void reset_event() { } /* * Get the last error for a send/receive operation. Returns an OS_Exxx * value. */ int last_error() const { return err; } /* close the socket */ void close(); /* * Get the IP address for the given host. The return value is an * allocated buffer that the caller must delete with 'delete[]'. If * the host IP address is unavailable, returns null. */ static char *get_host_ip(const char *hostname) { /* * if no host name was specified, get the default local host name * from the environment */ if (hostname == 0 || hostname[0] == '\0') { hostname = getenv("HOSTNAME"); if (hostname == 0) return 0; } /* get the host entry for our local host name */ struct hostent *local_host = gethostbyname(hostname); if (local_host == 0) return 0; /* get the IP address from the host entry structure */ const char *a = inet_ntoa(*(struct in_addr *)local_host->h_addr_list[0]); if (a == 0) return 0; /* make an allocated copy of the buffer for the caller */ char *buf = new char[strlen(a) + 1]; strcpy(buf, a); /* return the allocated copy */ return buf; } /* * Get the IP address for our side (the local side) of the socket. * 'ip' receives an allocated string with the IP address string in * decimal notation ("192.168.1.15"). We fill in 'port' with the local * port number of the socket. Returns true on success, false on * failure. If we return success, the caller is responsible for * freeing the allocated 'ip' string via t3free(). */ int get_local_addr(char *&ip, int &port) { socklen_t len; struct sockaddr_storage addr; /* get the local socket information */ len = sizeof(addr); getsockname(s, (struct sockaddr *)&addr, &len); /* parse the address */ return parse_addr(addr, len, ip, port); } /* * Get the IP address for the network peer to which we're connected. * 'ip' receives an allocated string with the IP address string in * decimal notation ("192.168.1.15"). We fill in 'port' with the port * number on the remote host. Returns true on success, false on * failure. If we return success, the caller is responsible for * freeing the allocated 'ip' string via t3free(). */ int get_peer_addr(char *&ip, int &port) { socklen_t len; struct sockaddr_storage addr; /* get the peer name */ len = sizeof(addr); getpeername(s, (struct sockaddr *)&addr, &len); /* parse the address */ return parse_addr(addr, len, ip, port); } /* * System time (as returned by time()) of last incoming network * activity. The watchdog thread (if enabled) uses this to make sure * that the bytecode program is actually serving a client, and * terminate the program if it appears to be running without a client * for an extended period. */ static time_t last_incoming_time; protected: /* create a socket object wrapping an existing system socket */ OS_CoreSocket(int s) { this->s = s; err = 0; ready_evt = 0; blocked_evt = 0; wouldblock_sending = FALSE; } /* * Get the waitable event. A caller who's waiting for the socket wants * to know when the socket is ready, so use the 'ready' event as the * socket wait event. */ virtual class OS_Event *get_event() { return ready_evt; } /* parse an address */ int parse_addr(struct sockaddr_storage &addr, int len, char *&ip, int &port) { char ipstr[INET6_ADDRSTRLEN]; /* presume failure */ ip = 0; port = 0; /* check the protocol family */ if (addr.ss_family == AF_INET) { /* get the address information */ struct sockaddr_in *s = (struct sockaddr_in *)&addr; port = ntohs(s->sin_port); inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); /* allocate the IP string and return success */ ip = lib_copy_str(ipstr); return TRUE; } else if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; port = ntohs(s->sin6_port); inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); /* allocate the IP string and return success */ ip = lib_copy_str(ipstr); return TRUE; } else { /* we don't handle other families */ return FALSE; } } /* our underlying system socket handle */ int s; /* last send/receive error */ int err; /* * for non-blocking sockets, the recv/send direction of the last * EWOULDBLOCK: true means send, false means recv */ int wouldblock_sending; /* * The non-blocking status events. The 'ready' event is signaled * whenever select() shows that the socket is ready, meaning that a * recv() or send() will be able to read/write at least one byte * without blocking. The 'blocked' event is the opposite: it's * signaled whenever a recv() or send() gets an EWOULDBLOCK error, * indicating that the socket is blocked until more data arrive in or * depart from the buffer. * * The 'ready' event is maintained by a separate monitor thread that we * create when the socket is placed into non-blocking mode. * * We need both events because we don't have a way of waiting for an * event to become unsignaled. So we need to signal these two states * independently via separate event objects. */ OS_Event *ready_evt; OS_Event *blocked_evt; /* non-blocking status monitor thread */ class OS_Socket_Mon_Thread *mon_thread; }; /* ------------------------------------------------------------------------ */ /* * Data socket */ class OS_Socket: public OS_CoreSocket { friend class OS_Listener; public: /* * Send bytes. Returns the number of bytes sent, or OS_SOCKET_ERROR if * an error occurs. */ int send(const char *buf, size_t len) { /* send the bytes and note the result */ int ret = ::send(s, buf, len, MSG_NOSIGNAL); /* * If an error occurred, note it. Treat EAGAIN as equivalent to * EWOULDBLOCK. */ err = (ret < 0 ? (errno == EAGAIN ? EWOULDBLOCK : errno) : 0); /* on EWOULDBLOCK, set the blocking status indicators */ if (err == EWOULDBLOCK) { /* the wouldblock direction is 'send' */ wouldblock_sending = TRUE; /* set the events for blocking mode */ ready_evt->reset(); blocked_evt->signal(); } /* return the result */ return ret; } /* * Receive bytes. Returns the number of bytes received, or * OS_SOCKET_ERROR if an error occurs. */ int recv(char *buf, size_t len) { /* read the bytes and note the result */ int ret = ::recv(s, buf, len, MSG_NOSIGNAL); /* * If an error occurred, note it. Treat EAGAIN as equivalent to * EWOULDBLOCK. */ err = (ret < 0 ? (errno == EAGAIN ? EWOULDBLOCK : errno) : 0); /* on EWOULDBLOCK, set the blocking status indicators */ if (err == EWOULDBLOCK) { /* the wouldblock direction is 'receive' */ wouldblock_sending = FALSE; /* set the events for blocking mode */ ready_evt->reset(); blocked_evt->signal(); } /* if we successfully received data, this is incoming activity */ if (err == 0) last_incoming_time = time(0); /* return the result */ return ret; } protected: /* create an OS_Socket object to wrap an existing system socket handle */ OS_Socket(int s) : OS_CoreSocket(s) { } ~OS_Socket() { } }; /* ------------------------------------------------------------------------ */ /* * Listener. This class represents a network listener socket, which is * used to wait for and accept new incoming connections from clients. */ class OS_Listener: public OS_CoreSocket { public: /* * Construction. This sets up the object, but doesn't open a listener * port. Call open() to actually open the port. */ OS_Listener() { } /* destructor - free resources */ ~OS_Listener() { } /* * Open the listener on the given port number. This can be used to * create a server on a well-known port, but it has the drawback that * ports can't be shared, so this will fail if another process is * already using the same port number. * * Returns true on success, false on failure. */ int open(const char *hostname, unsigned short port_num) { /* create our socket */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) return FALSE; /* bind to the given host address */ char *ip = get_host_ip(hostname); if (ip == 0) return FALSE; /* * set up the address request structure to bind to the requested * port on the local address we just figured */ struct sockaddr_in saddr; saddr.sin_family = PF_INET; saddr.sin_addr.s_addr = inet_addr(ip); saddr.sin_port = htons(port_num); memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero)); /* done with the IP address */ delete [] ip; /* try binding */ if (bind(s, (sockaddr *)&saddr, sizeof(saddr))) return FALSE; /* put the socket into the 'listening' state */ if (listen(s, SOMAXCONN)) return FALSE; /* success */ return TRUE; } /* * Open the listener port, with the port number assigned by the system. * The system will select an available port and assign it to this * listener, so this avoids contention for specific port numbers. * However, it requires some separate means of communicating the port * number to clients, since there's no way for them to know the port * number in advance. * * Returns true on success, false on failure. */ int open(const char *hostname) { return open(hostname, 0); } /* * Accept the next incoming connection. If the listener is in blocking * mode (the default), and there are no pending requests, this blocks * until the next pending request arrives. In non-blocking mode, this * returns immediately with a null socket if no requests are pending, * and last_error() indicates OS_EWOULDBLOCK. */ OS_Socket *accept() { /* accept a connection on the underlying system socket */ struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int snew = ::accept(s, (sockaddr *)&addr, &addrlen); /* check to see if we got a socket */ if (snew != -1) { /* success - clear the error memory */ err = 0; /* this counts as incoming network activity - note the time */ last_incoming_time = time(0); /* * Set the "linger" option on close. This helps ensure that we * transmit any error response to a request that causes the * server thread to abort. Without this option, winsock will * sometimes terminate the connection before transmitting the * final response. */ struct linger ls = { TRUE, 1 }; setsockopt(snew, SOL_SOCKET, SO_LINGER, (char *)&ls, sizeof(ls)); /* * Turn off SIGPIPE signals on this socket - we deal with * disconnections via error returns on send() rather than * signals. Note that this setting only applies to some Unix * varieties; we'll do it only if SO_NOSIGPIPE is defined * locally. */ #ifdef SO_NOSIGPIPE int nsp = 1; setsockopt(snew, SOL_SOCKET, SO_NOSIGPIPE, (char *)&nsp, sizeof(nsp)); #endif /* * Disable the Nagle algorithm for this socket to minimize * transmit latency. With the Nagle algorithm in effect, * whenever the program sends a small packet (via ::send(), * e.g.), the TCP layer queues the bytes but doesn't send them * out on the network immediately; instead, it waits a little * while to see if another ::send() quickly follows, in which * case the bytes from the first ::send() can be combined with * those from the second to form one larger packet. This * continues until the maximum packet size is reached or a * timeout expires, at which point the queued bytes are finally * sent out on the network. This automatic buffering and * coalescing behavior is beneficial to many appliations, * because there's some overhead associated with each packet; * coalescing multiple small ::send()'s into one large packet * reduces this packet overhead. However, it's detrimental to * programs like TADS that tend to send a series of small * messages that need to be acknowledged individually before * the next can be sent. The Nagle algorithm's delay in this * case manifests as pure latency, since the additional bytes * being waited for never arrive, so the original small packet * must be sent out after all - but not until the Nagle * algorithm delay expires, whereas with Nagle turned off, it's * sent out immediately. */ int tcpflag = 1; setsockopt(snew, IPPROTO_TCP, TCP_NODELAY, (char *)&tcpflag, sizeof(tcpflag)); /* wrap the socket in an OS_Socket and return the object */ return new OS_Socket(snew); } else { /* failed - remember the system error code */ err = (errno == EAGAIN ? EWOULDBLOCK : errno); /* on EWOULDBLOCK, set the blocking status indicators */ if (err == EWOULDBLOCK) { /* the wouldblock direction for listen is 'receive' */ wouldblock_sending = FALSE; /* set the events for blocking mode */ ready_evt->reset(); blocked_evt->signal(); } /* return failure */ return 0; } } protected: }; /* ------------------------------------------------------------------------ */ /* * Thread. Callers subclass this to define the thread entrypoint routine. */ class OS_Thread: public CVmWeakRefable, public OS_Waitable { friend class TadsThreadList; public: OS_Thread(); virtual ~OS_Thread(); /* * Launch the thread. Returns true on success, false on failure. */ int launch() { /* default thread attributes */ extern pthread_attr_t G_thread_attr; /* * The initial reference for our caller, which constructed this * object. Add a second reference on behalf of the new thread that * we're launching - it has a reference to 'this' as its thread * context object. */ add_ref(); /* create the thread */ int err = pthread_create(&tid, &G_thread_attr, &sys_thread_main, this); /* check for errors */ if (err != 0) { /* * The launch failed, so release the reference we created for * the thread - there actually will be no thread to take over * that reference. */ release_ref(); /* signal the 'done' event immediately */ done_evt->signal(); /* not launched */ return FALSE; } else { /* launched successfully - mark the thread ID as valid */ tid_valid = TRUE; /* indicate success */ return TRUE; } } #if 0 /* * Cancel the thread. * * [This method is currently disabled because of problems that appear * with some versions of Linux and/or gcc - it's not clear who's at * fault, but there appears to be a known bug where pthread_cancel() * fails to trigger cleanup handlers in the target thread. This method * was only used in one place, which was to kill the monitor thread * associated with a non-blocking socket when the socket was closed. * That's now handled instead via a message through a pipe, so no code * currently uses the routine, and we've #if'd it out to make sure that * no new code uses it without being aware of the cleanup handler bug. * If it becomes desirable to re-enable the routine at some point, we * need to first make sure that there's a solution to the cleanup * handler bug, since that will cause unexpected behavior on platforms * where the bug occurs.] */ void cancel_thread(int wait = FALSE) { /* request cancellation of the pthreads thread */ if (tid_valid) pthread_cancel(tid); /* if desired, wait for the thrad */ if (wait) done_evt->wait(); } #endif /* * Thread entrypoint routine. Callers must subclass this class and * provide a concrete definition for this method. This method is * called at thread entry. */ virtual void thread_main() = 0; protected: /* get the waitable event */ virtual class OS_Event *get_event() { return done_evt; } /* * Static entrypoint. This is the routine the system calls directly; * we then invoke the virtual method with the subclassed thread main. */ static void *sys_thread_main(void *ctx) { /* set up our cleanup routine */ pthread_cleanup_push(sys_thread_cleanup, ctx); /* the context is the thread structure */ OS_Thread *t = (OS_Thread *)ctx; /* launch the real thread and remember the result code */ t->thread_main(); /* pop and invoke the cleanup handler */ pthread_cleanup_pop(TRUE); /* return a dummy result code */ return 0; } /* * Cleanup entrypoint. The system pthreads package invokes this when * our thread exits, either by returning from the main thread * entrypoint routine or via a cancellation request. At exit, we * release the thread's reference on the OS_Thread object, and we * signal the thread's 'done' event to indicate that the thread has * terminated. */ static void sys_thread_cleanup(void *ctx) { /* the context is the thread structure */ OS_Thread *t = (OS_Thread *)ctx; /* before we release our ref on the thread, save its done event */ OS_Event *done_evt = t->done_evt; done_evt->add_ref(); /* release the thread's reference on the thread structure */ t->release_ref(); /* the thread has now truly exited - signal the termination event */ done_evt->signal(); done_evt->release_ref(); } /* system thread ID */ int tid_valid; pthread_t tid; /* end-of-thread event */ OS_Event *done_evt; }; /* master thread list global */ extern class TadsThreadList *G_thread_list; /* ------------------------------------------------------------------------ */ /* * Watchdog thread. This is an optional system thread that monitors the * program's activity, and terminates the program if it appears to be * either inactive or looping. When TADS is running as a network server * that's open to anyone's bytecode programs, we can't count on the * underlying bytecode program being well-behaved. The watchdog thread * handles some common problem areas: * * - If the bytecode program has a bug that gets it stuck in an infinite * loop, it will consume as much CPU time as it's given, which will bring * other processes on the server to a crawl. The watchdog checks * periodically to see if our process has been consuming a disproportionate * amount of CPU time over a recent interval, and will terminate the * process if so. This will also shut down programs that are intentionally * (not buggily) doing CPU-intensive computations for long stretches, but * this is a reasonable policy anyway: shared TADS game servers are meant * for interactive games, and if someone wants to run heavy-duty number * crunching, they really shouldn't be using a shared game server for it. * * - If the bytecode program has a long period without any network requests * from clients, it probably has a bug in its internal session management * code. On a public server, a given interpreter session is meant to run * only as long as it's actively being used by a client. The standard TADS * web server library has an internal watchdog that monitors for client * activity, and shuts itself down after a period of inactivity. However, * we can't count on every bytecode program using the standard library, and * even those that do could have bugs (in the library itself or in user * code) that prevent the internal inactivity watchdog from working * properly. So, our system watchdog thread serves as a backup here; if we * don't see any incoming client network requests for an extended period, * we'll assume that the program no longer has any job to do, so we'll shut * it down. Note that a bytecode program could be intentionally running as * a continuous service, e.g., acting as a web server; but that's really an * abuse of a public TADS server, since a continuously resident process * takes up a lot of memory that's meant to be shared with other users. So * we don't care whether the bytecode program is running without clients * due to bugs or by design; in either case we want to shut it down. * * The system administrator enables the watchdog thread by putting the * variable "watchdog = yes" in the tadsweb.config file. */ /* the statistics we collect each time the watchdog wakes up */ struct watchdog_stats { /* total user CPU time used as of this interval, in seconds */ clock_t cpu_time; /* wall clock time (time() value) as of this interval, in seconds */ time_t wall_time; }; /* * Watchdog thread object class */ class OSS_Watchdog: public OS_Thread { public: OSS_Watchdog() { /* create our 'quit' event object */ quit_evt = new OS_Event(TRUE); /* we haven't collected any statistics yet */ cnt = 0; idx = 0; /* * Initialize the last incoming network activity time to the * current time. We haven't actually seen any network activity * yet, of course, but what we're really interested in is the * interval, and it's not meaningful to talk about an interval * longer than we've been running. */ OS_CoreSocket::last_incoming_time = time(0); } ~OSS_Watchdog() { /* we're done with our 'quit' event */ quit_evt->release_ref(); } virtual void thread_main() { oss_debug_log("watchdog thread started"); /* loop until our 'quit' event has fired */ while (!quit_evt->test()) { /* wake up every 10 seconds, or when the 'quit' event fires */ if (quit_evt->wait(10000) == OSWAIT_TIMEOUT) { /* * That timed out, so it's time for another watchdog check. * First, update the timers. */ stats[idx].cpu_time = clock() / CLOCKS_PER_SEC; stats[idx].wall_time = time(0); /* bump the statistics counters */ ++cnt; if (++idx >= NSTATS) idx = 0; /* * If we've collected enough statistics, do our checks. We * need at least 60 seconds of trailing data, and we wake * up every 10 seconds, so we need at least seven samples * (#0 is 0 seconds ago, #1 is 10 seconds ago, ... #6 is 60 * seconds ago, hence we need 0-6 = 7 samples). */ if (cnt >= 7) { /* get the last and the 60-seconds-ago stats */ const watchdog_stats *cur = get_stats(0); const watchdog_stats *prv = get_stats(6); /* calculate the percentage of CPU time */ double cpu_pct = ((double)(cur->cpu_time - prv->cpu_time)) / (cur->wall_time - prv->wall_time); /* calculate the time since the last network event */ time_t time_since_net = cur->wall_time - OS_CoreSocket::last_incoming_time; /* * Check for termination conditions: * * 1. If we've consumed 40% or higher CPU over the * last minute, we're probably stuck in a loop; we * might also be running an intentionally * compute-intensive program, which isn't a bug but is * an abuse of a shared server, so in either case kill * the program. (We're not worried about short bursts * of high CPU usage, which is why we average over a * fairly long real-time period.) * * 2. If we haven't seen any incoming network activity * in 6 minutes, the program is probably idle and * should probably have noticed that and shut itself * down already, so it might have a bug in its internal * inactivity monitor. It could also be intentionally * running as a continuous service, but that's abusive * on a shared server because it uses memory that's * meant to be shared. In either case, shut it down. */ if (cpu_pct >= 0.4) { oss_debug_log("watchdog terminating process due to " "high CPU usage (cpu_pct = %0.2ld%%)", cpu_pct); kill(getpid(), 9); } if (time_since_net > 6*60) { oss_debug_log("watchdog terminating process due to " "network inactivity (time_since_net" " = %ld seconds)", (long)time_since_net); kill(getpid(), 9); } } } } } /* the watchdog thread 'quit' event */ static OS_Event *quit_evt; private: /* number of times we've collected statistics */ int cnt; /* next statistics write index */ int idx; /* * Statistics array. We keep records for the latest NSTATS iterations. * idx points to the next element to write, so idx-1 is the last one we * wrote, and so on. The array is circular, so the actual array index * of element i is i % NSTATS. */ static const int NSTATS = 10; watchdog_stats stats[NSTATS]; /* * get the nth most recent stats entry - 0 is the latest entry, 1 is * the second older entry, etc */ const watchdog_stats *get_stats(int i) const { /* * idx points to the next to write, so idx-1 is the latest entry, * idx-2 is the second entry, etc */ i = idx - 1 - i; /* the array is circular, so adjust for wrapping */ if (i < 0) i += NSTATS; /* return the element */ return &stats[i]; } }; #endif /* OSNETUNIX_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/md5.cpp������������������������������������������������������������������������0000644�0001750�0000144�00000034535�11461025056�014725� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch <ghost@aladdin.com>. Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include <string.h> in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include <stdio.h> in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include "t3std.h" // MJR addition #include "vmdatasrc.h" // MJR addition #include <string.h> #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } /* ------------------------------------------------------------------------ */ /* * E-Z MD5 [MJR addition] */ void md5_ez(char *hash, const char *msg, size_t len) { md5_state_t s; md5_byte_t digest[16]; /* calculate the binary md5 of the message */ md5_init(&s); md5_append(&s, (const md5_byte_t *)msg, len); md5_finish(&s, digest); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < 16 ; ++i, hash += 2) byte_to_xdigits(hash, digest[i]); /* null-terminate the output string */ *hash = '\0'; } /* * Hash a data source [MJR addition] */ void md5_datasrc(char *hash, class CVmDataSource *src, unsigned long len) { /* initialize the digest state */ md5_state_t s; md5_init(&s); /* read from the data source */ while (len != 0) { /* get a chunk up to one buffer-full, or the total remaining */ md5_byte_t buf[1024]; size_t cur = (len > sizeof(buf) ? sizeof(buf) : (size_t)len); /* read the chunk */ if (cur != 0) cur = src->readc(buf, cur); /* if there's nothing left, we're done */ if (cur == 0) break; /* feed this chunk into the hash */ md5_append(&s, buf, cur); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the digest value */ md5_byte_t digest[16]; md5_finish(&s, digest); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < 16 ; ++i, hash += 2) byte_to_xdigits(hash, digest[i]); /* null-terminate the output string */ *hash = '\0'; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmstrres.h���������������������������������������������������������������������0000644�0001750�0000144�00000003061�07627507750�015577� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/TADS2/RES.H,v 1.1 1999/07/11 00:46:30 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrres.h - string resource definitions for TADS 3 Function Defines the ID's for string resources. These strings must be loaded when needed with os_get_str_rsc(), since the specific mechanism by which these strings are loaded varies by operating system. We assign the resources sequential numbers starting at 1, to make it easier to map the resource loader to an operating system mechanism where such a mechanism exists. Notes Modified 06/25/99 MJRoberts - Creation */ #ifndef VMSTRRES_H #define VMSTRRES_H /* * Dialog buttons. These provide the text for standard buttons in * dialogs created with os_input_dialog(). * * These labels can use "&" to indicate a shortcut letter, per the * normal os_input_dialog() interface; for example, if the Yes button * label is "&Yes", the button has the shortcut letter "Y". * * The text of these buttons may vary by system, since these should * conform to local conventions where there are local conventions. In * addition, of course, these strings will vary by language. */ /* OK and Cancel buttons */ #define VMRESID_BTN_OK 1 #define VMRESID_BTN_CANCEL 2 /* "Yes" and "No" buttons */ #define VMRESID_BTN_YES 3 #define VMRESID_BTN_NO 4 #endif /* VMSTRRES_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmmccore.h���������������������������������������������������������������������0000644�0001750�0000144�00000004274�11724416463�015525� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMMCCORE.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmccore.h - Core Metaclass Registrations Function Notes Modified 12/01/98 MJRoberts - Creation */ /* * NOTE - this file is INTENTIONALLY not protected against multiple * inclusion. Because of the funny business involved in buildling the * registration tables, we must include this file more than once in * different configurations. Therefore, there's no #ifndef * VMMCCORE_INCLUDED test at all in this file. */ /* ------------------------------------------------------------------------ */ /* * Before we begin, if we're building the registration table, redefine * the CENTRAL REGISTRATION BUILDER version of the metaclass * registration macro. We do this here rather than in vmmcreg.cpp to * make it easier to create separate versions of vmmcreg.cpp for * separate subsystems. This core file is always included, even in * special configurations, before any other metaclass headers. */ #ifdef VMMCCORE_BUILD_TABLE #ifdef VM_REGISTER_METACLASS #undef VM_REGISTER_METACLASS #endif #define VM_REGISTER_METACLASS(meta_class) \ { &meta_class::metaclass_reg_ }, #endif /* VMMCCORE_BUILD_TABLE */ /* ------------------------------------------------------------------------ */ /* * Now include each header for file the core metaclasses */ #include "vmobj.h" #include "vmtobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmdict.h" #include "vmgram.h" #include "vmbignum.h" #include "vmintcls.h" #include "vmanonfn.h" #include "vmcoll.h" #include "vmiter.h" #include "vmvec.h" #include "vmlookup.h" #include "vmbytarr.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmtmpfil.h" #include "vmfilnam.h" #include "vmpat.h" #include "vmstrcmp.h" #include "vmstrbuf.h" #include "vmdynfunc.h" #include "vmfref.h" #include "vmdate.h" #include "vmtzobj.h" /* if networking is enabled, include the networking metaclasses */ #ifdef TADSNET #include "vmmcnet.h" #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmres.h������������������������������������������������������������������������0000644�0001750�0000144�00000003103�11677362237�015042� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmres.h,v 1.2 1999/05/17 02:52:30 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmres.h - resource object implementation Function A resource is a named binary byte stream stored within the image file. To the VM, resources are opaque; the VM merely maintains the resource name table, and provides access to the byte stream to the user program. Notes Modified 04/03/99 MJRoberts - Creation */ #ifndef VMRES_H #define VMRES_H #include <stdlib.h> #include "t3std.h" class CVmResource { friend class CVmImageLoader; public: CVmResource(long seek_pos, uint32_t len, size_t name_len); ~CVmResource(); /* get the seek position */ long get_seek_pos() const { return seek_pos_; } /* get the length of the byte stream */ uint32_t get_len() const { return len_; } /* get my name */ const char *get_name() const { return name_; } /* get/set next resource in list */ CVmResource *get_next() const { return nxt_; } void set_next(CVmResource *nxt) { nxt_ = nxt; } private: /* get my name buffer */ char *get_name_buf() const { return name_; } /* seek position in image file of my binary data */ long seek_pos_; /* length in bytes of my binary data stream */ uint32_t len_; /* name string */ char *name_; /* next resource in list */ CVmResource *nxt_; }; #endif /* VMRES_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmbiftad.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000405001�12145504453�016025� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMBIFTAD.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftad.cpp - TADS built-in function set for T3 VM Function Notes Modified 04/05/99 MJRoberts - Creation */ #include <stdio.h> #include <string.h> #include <time.h> #include "t3std.h" #include "os.h" #include "utf8.h" #include "vmuni.h" #include "vmbiftad.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmregex.h" #include "vmundo.h" #include "vmfile.h" #include "vmsave.h" #include "vmbignum.h" #include "vmfunc.h" #include "vmpat.h" #include "vmtobj.h" #include "vmimport.h" #include "vmpredef.h" #include "vmlookup.h" #include "vmfilobj.h" #include "vmnetfil.h" #include "vmbytarr.h" #include "vmcrc.h" #include "vmfindrep.h" /* ------------------------------------------------------------------------ */ /* * Initialize the TADS intrinsics global state */ CVmBifTADSGlobals::CVmBifTADSGlobals(VMG0_) { /* allocate our regular expression parser */ rex_parser = new CRegexParser(); rex_searcher = new CRegexSearcherSimple(rex_parser); /* * Allocate a global variable to hold the most recent regular * expression search string. We need this in a global so that the last * search string is always protected from garbage collection; we must * keep the string because we might need it to extract a group-match * substring. */ last_rex_str = G_obj_table->create_global_var(); /* ISAAC is the default RNG */ rng_id = VMBT_RNGID_ISAAC; /* * Set the random number seed to a fixed starting value (this value * is arbitrary; we chose it by throwing dice). If the program * wants another sequence, it can manually change this by calling * the randomize() intrinsic in our function set, which seeds the * generator with an OS-dependent starting value (usually based on * the system's real-time clock, to ensure that each run will use a * different starting value). */ lcg_rand_seed = 024136543305; /* create the ISAAC context structure */ isaac_ctx = (struct isaacctx *)t3malloc(sizeof(struct isaacctx)); /* initialize with a fixed seed vector */ isaac_init(isaac_ctx, FALSE); /* create the Mersenne Twister object */ mt_ctx = new CVmMT19937(); } /* * delete the TADS intrinsics global state */ CVmBifTADSGlobals::~CVmBifTADSGlobals() { /* delete our regular expression searcher and parser */ delete rex_searcher; delete rex_parser; /* * note that we leave our last_rex_str global variable undeleted here, * as we don't have access to G_obj_table (as there's no VMG_ to a * destructor); this is okay, since the object table will take care of * deleting the variable for us when the object table itself is deleted */ /* delete the ISAAC context */ t3free(isaac_ctx); /* delete the Mersenne Twister context */ delete mt_ctx; } /* ------------------------------------------------------------------------ */ /* * datatype - get the datatype of a given value */ void CVmBifTADS::datatype(VMG_ uint argc) { vm_val_t val; vm_val_t retval; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the value */ G_stk->pop(&val); /* return the appropriate value for this type */ retval.set_datatype(vmg_ &val); retval_int(vmg_ retval.val.intval); } /* ------------------------------------------------------------------------ */ /* * getarg - get the given argument to the current procedure */ void CVmBifTADS::getarg(VMG_ uint argc) { int idx; /* check arguments */ check_argc(vmg_ argc, 1); /* get the argument index value */ idx = pop_int_val(vmg0_); /* if the argument index is out of range, throw an error */ if (idx < 1 || idx > G_interpreter->get_cur_argc(vmg0_)) err_throw(VMERR_BAD_VAL_BIF); /* return the parameter value */ *G_interpreter->get_r0() = *G_interpreter->get_param(vmg_ idx - 1); } /* ------------------------------------------------------------------------ */ /* * firstobj - get the first object instance */ void CVmBifTADS::firstobj(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 2); /* enumerate objects starting with object 1 in the master object table */ enum_objects(vmg_ argc, (vm_obj_id_t)1); } /* * nextobj - get the next object instance after a given object */ void CVmBifTADS::nextobj(VMG_ uint argc) { vm_val_t val; vm_obj_id_t prv_obj; /* check arguments */ check_argc_range(vmg_ argc, 1, 3); /* get the previous object */ G_interpreter->pop_obj(vmg_ &val); prv_obj = val.val.obj; /* * Enumerate objects starting with the next object in the master * object table after the given object. Reduce the argument count by * one, since we've removed the preceding object. */ enum_objects(vmg_ argc - 1, prv_obj + 1); } /* enum_objects flags */ #define VMBIFTADS_ENUM_INSTANCES 0x0001 #define VMBIFTADS_ENUM_CLASSES 0x0002 /* * Common handler for firstobj/nextobj object iteration */ void CVmBifTADS::enum_objects(VMG_ uint argc, vm_obj_id_t start_obj) { vm_val_t val; vm_obj_id_t sc; vm_obj_id_t obj; unsigned long flags; /* presume no superclass filter will be specified */ sc = VM_INVALID_OBJ; /* presume we're enumerating instances only */ flags = VMBIFTADS_ENUM_INSTANCES; /* * check arguments - we can optionally have two more arguments: a * superclass whose instances/subclasses we are to enumerate, and an * integer giving flag bits */ if (argc == 2) { /* pop the object */ G_interpreter->pop_obj(vmg_ &val); sc = val.val.obj; /* pop the flags */ flags = pop_long_val(vmg0_); } else if (argc == 1) { /* check to see if it's an object or the flags integer */ switch (G_stk->get(0)->typ) { case VM_INT: /* it's the flags */ flags = pop_long_val(vmg0_); break; case VM_OBJ: /* it's the superclass filter */ G_interpreter->pop_obj(vmg_ &val); sc = val.val.obj; break; default: /* invalid argument type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* presume we won't find anything */ retval_nil(vmg0_); /* * starting with the given object, scan objects until we find one * that's valid and matches our superclass, if one was provided */ for (obj = start_obj ; obj < G_obj_table->get_max_used_obj_id() ; ++obj) { /* * If it's valid, and it's not an intrinsic class modifier object, * consider it further. Skip intrinsic class modifiers, since * they're not really separate objects; they're really part of the * intrinsic class they modify, and all of the properties and * methods of a modifier object are reachable through the base * intrinsic class. */ if (G_obj_table->is_obj_id_valid(obj) && !CVmObjIntClsMod::is_intcls_mod_obj(vmg_ obj)) { /* * if it's a class, skip it if the flags indicate classes are * not wanted; if it's an instance, skip it if the flags * indicate that instances are not wanted */ if (vm_objp(vmg_ obj)->is_class_object(vmg_ obj)) { /* it's a class - skip it if classes are not wanted */ if ((flags & VMBIFTADS_ENUM_CLASSES) == 0) continue; } else { /* it's an instance - skip it if instances are not wanted */ if ((flags & VMBIFTADS_ENUM_INSTANCES) == 0) continue; } /* * if a superclass was specified, and it matches, we have a * winner */ if (sc != VM_INVALID_OBJ) { /* if the object matches, return it */ if (vm_objp(vmg_ obj)->is_instance_of(vmg_ sc)) { retval_obj(vmg_ obj); break; } } else { /* * We're enumerating all objects - but skip List and String * object, as we expose these as special types. */ if (vm_objp(vmg_ obj)->get_as_list() == 0 && vm_objp(vmg_ obj)->get_as_string(vmg0_) == 0) { retval_obj(vmg_ obj); break; } } } } } /* ------------------------------------------------------------------------ */ /* * Abstract RNG algorithm interface */ class IVmBifTadsRNG { public: virtual ~IVmBifTadsRNG() { } /* get the next random number */ virtual uint32_t rand() = 0; /* seed the generator from random data */ virtual void seed_random() = 0; /* seed from an int */ virtual void seed_int(int32_t i) { /* by default, turn this into a string and seed with the string */ char buf[40]; oswp4(buf, i); t3sprintf(buf+4, sizeof(buf)-4, "%ld", (long)i); seed_str(buf, 4 + strlen(buf+4)); } /* seed from a string */ virtual void seed_str(const char *str, size_t len) { /* by default, hash the string to an int and seed with the int */ CVmCRC32 crc; crc.scan_bytes(str, len); seed_int((int32_t)crc.get_crc_val()); } /* get the state into a ByteArray object */ void get_state(VMG_ vm_val_t *val) { size_t len = get_state_size(); if (len == 0) { /* simple int32_t state */ val->set_int(get_state_int()); } else { /* allocate a buffer */ char *buf = new char[len]; /* make sure we delete the allocated buffer */ err_try { /* get the state into our buffer */ get_state_buf(buf); /* create a ByteArray from the state vector */ val->set_obj(CVmObjByteArray::create_from_bytes( vmg_ FALSE, buf, len)); } err_finally { /* done with the buffer */ delete [] buf; } err_end; } } /* put the state from a ByteArray object */ void put_state(VMG_ vm_val_t *val) { /* determine what to do based on the state length */ size_t len = get_state_size(); if (len == 0) { /* simple int32_t state */ put_state_int(val->num_to_int(vmg0_)); } else { /* retrieve the ByteArray object */ CVmObjByteArray *barr = vm_val_cast(CVmObjByteArray, val); /* check that it matches the expected state buffer size */ unsigned long cnt = barr->get_element_count(); if (cnt != len) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the bytes and restore the state */ char *buf = new char[len]; err_try { /* retrieve the bytes */ barr->copy_to_buf((unsigned char *)buf, 1, len); /* restore the state */ put_state_buf(buf); } err_finally { delete [] buf; } err_end; } } protected: /* * Get the size of the state vector, in bytes. Return 0 if we can * store the state in an int32_t. */ virtual size_t get_state_size() = 0; /* get/put state as an int32_t */ virtual int32_t get_state_int() = 0; virtual void put_state_int(int32_t i) = 0; /* save/restore current state vector to/from a buffer */ virtual void get_state_buf(char *buf) = 0; virtual void put_state_buf(const char *buf) = 0; }; /* ------------------------------------------------------------------------ */ /* * ISAAC random number generator */ class vmbt_isaac_ifc: public IVmBifTadsRNG { public: vmbt_isaac_ifc(VMG0_) { ctx = G_bif_tads_globals->isaac_ctx; } isaacctx *ctx; virtual uint32_t rand() { return isaac_rand(ctx); } /* seed from random data */ virtual void seed_random() { /* generate random bytes into the ISAAC rsl array */ os_gen_rand_bytes((unsigned char *)ctx->rsl, sizeof(ctx->rsl)); /* initialize from the rsl array */ isaac_init(ctx, TRUE); } /* seed from a string value */ virtual void seed_str(const char *str, size_t len) { /* * Copy the string value into the rsl array; copy as much as will * fit, and zero the rest. (The point here is to be deterministic, * so we want this to be the same every time with a given seed - we * don't want to include random data left behind from previous * iterations.) */ if (len > sizeof(ctx->rsl)) len = sizeof(ctx->rsl); memset(ctx->rsl, 0, sizeof(ctx->rsl)); memcpy(ctx->rsl, str, len); /* initialize with the rsl data */ isaac_init(ctx, TRUE); } virtual int32_t get_state_int() { return 0; } virtual void put_state_int(int32_t) { } virtual size_t get_state_size() { return isaac_get_state(ctx, 0); } virtual void get_state_buf(char *buf) { isaac_get_state(ctx, buf); } virtual void put_state_buf(const char *buf) { isaac_set_state(ctx, buf); } }; /* ------------------------------------------------------------------------ */ /* * Linear Congruential Random-Number Generator. This generator uses an * algorithm from Knuth, The Art of Computer Programming, Volume 2, p. * 170, with parameters chosen from the same book for their good * statistical properties and efficiency on 32-bit hardware. */ class vmbt_lcg_ifc: public IVmBifTadsRNG { public: vmbt_lcg_ifc(VMG0_) { seedp = &G_bif_tads_globals->lcg_rand_seed; } virtual uint32_t rand() { const uint32_t a = 1664525L; const uint32_t c = 1; /* * Generate the next random value using the linear congruential * method described in Knuth, The Art of Computer Programming, * volume 2, p170. * * Use 2^32 as m, hence (n mod m) == (n & 0xFFFFFFFF). This is * efficient and is well-suited to 32-bit machines, works fine on * larger machines, and will even work on 16-bit machines as long * as the compiler can provide us with 32-bit arithmetic (which we * assume extensively elsewhere anyway). * * We use a = 1664525, a multiplier which has very good results * with the Spectral Test (see Knuth p102) with our choice of m. * * Use c = 1, since this trivially satisfies Knuth's requirements * about common factors. * * Note that the result of the multiplication might overflow a * 32-bit ulong for values of lcg_rand_seed that are not small. * This doesn't matter, since if it does, the machine will * naturally truncate high-order bits to yield the result mod 2^32. * So, on a 32-bit machine, the (&0xFFFFFFFF) part is superfluous, * but it's harmless and is needed for machines with a larger word * size. */ *seedp = (int32_t)(((a * (uint32_t)*seedp) + c) & 0xFFFFFFFF); return (uint32_t)*seedp; } virtual void seed_random() { long l; os_rand(&l); *seedp = (int32_t)l; } virtual void seed_int(int32_t i) { *seedp = i; } virtual int32_t get_state_int() { return *seedp; } virtual void put_state_int(int32_t i) { *seedp = i; } virtual size_t get_state_size() { return 0; } virtual void get_state_buf(char *) { } virtual void put_state_buf(const char *) { } int32_t *seedp; }; /* ------------------------------------------------------------------------ */ /* * Bit-shift generator. This is from Knuth, The Art of Computer * Programming, volume 2. This generator is designed to produce random * strings of bits and isn't suitable for use as a general-purpose RNG. * * Linear congruential generators aren't ideal for generating random bits; * their statistical properties are better suited for generating values * over a full integer range. This generator is specially designed to * produce random bits, so it could be a useful complement to an LCG RNG. * * Since this isn't a good general RNG, we don't currently enable it. * We're keeping the code here, ifdef'd out, in case we decide to enable it * in the future - which seems really unlikely, since ISAAC seems to be * good at generating both full-range integers and random bits, leaving * little reason to have a dedicated random bit generator. */ #ifdef VMBIFTADS_RNG_BITSHIFT static uint32_t bits_rng_next(VMG0_) { int top_bit = (G_bif_tads_globals->bits_rand_seed & 0x8000000); G_bif_tads_globals->bits_rand_seed <<= 1; if (top_bit) G_bif_tads_globals->bits_rand_seed ^= 035604231625; return G_bif_tads_globals->bits_rand_seed & 1; } #endif /* VMBIFTADS_RNG_BITSHIFT */ /* ------------------------------------------------------------------------ */ /* * Mersenne Twister MT19937 RNG algorithm */ class vmbt_mt_ifc: public IVmBifTadsRNG { public: vmbt_mt_ifc(VMG0_) { mt = G_bif_tads_globals->mt_ctx; } CVmMT19937 *mt; virtual uint32_t rand() { return mt->rand(); } virtual void seed_random() { mt->seed_random(); } virtual void seed_int(int32_t i) { mt->seed(i); } virtual void seed_str(const char *str, size_t len) { mt->seed(str, len); } virtual int32_t get_state_int() { return 0; } virtual void put_state_int(int32_t i) { } virtual size_t get_state_size() { return mt->get_state_size(); } virtual void get_state_buf(char *buf) { mt->get_state(buf); } virtual void put_state_buf(const char *buf) { mt->put_state(buf); } }; /* ------------------------------------------------------------------------ */ /* * RNG interface selector. This sets up each type of RNG interface, and * returns the one for the given ID. This object can be constructed on the * stack for minimal memory management hassle. */ class CVmRNGSelector { public: CVmRNGSelector(VMG0_) : isaac(vmg0_), lcg(vmg0_), mt(vmg0_) { } IVmBifTadsRNG *get(int id) { switch (id) { case VMBT_RNGID_ISAAC: return &isaac; case VMBT_RNGID_LCG: return &lcg; case VMBT_RNGID_MT19937: return &mt; default: err_throw(VMERR_BAD_VAL_BIF); AFTER_ERR_THROW(return 0;) } } vmbt_isaac_ifc isaac; vmbt_lcg_ifc lcg; vmbt_mt_ifc mt; }; /* ------------------------------------------------------------------------ */ /* * randomize - seed the random number generator. This has several formats: * *. randomize() - select the default RNG (ISAAC) and initialize it *. with random seed data. * *. randomize(nil) - retrieve the current state of the random number *. generator. Returns a list: [id, state], where 'id' *. is the generator ID, and 'state' is an opaque value *. representing the generator's internal state. * *. randomize([state]) - restores the generator and internal state *. returned from a previous call to randomize(nil). * *. randomize(id) - select the RNG identified by 'id'. * *. randomize(id, nil) - select the given RNG, and initialize it with *. random seed data. * *. randomize(id, val) - select the given RNG, and initialize it with *. the given fixed seed data. The seed can be provided *. as an integer or string value. */ void CVmBifTADS::randomize(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 2); if (argc == 0) { /* * No arguments - select the default random number generator * (ISAAC) and initialize it with random seed data. * * Load the ISAAC initialization vector with some truly random data * from the operating system. */ G_bif_tads_globals->rng_id = VMBT_RNGID_ISAAC; vmbt_isaac_ifc s Pvmg0_P; s.seed_random(); /* no return value */ retval_nil(vmg0_); } else if (argc == 1 && G_stk->get(0)->typ == VM_NIL) { /* * randomize(nil) - retrieve the current RNG state. This returns a * list with two elements: [id, state]. Set up the return list. */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, 2); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); G_stk->push()->set_obj(lstid); /* retval[1] = the active RNG ID */ vm_val_t ele; ele.set_int(G_bif_tads_globals->rng_id); lst->cons_set_element(0, &ele); /* retval[2] = the active RNG's saved state vector */ CVmRNGSelector sel Pvmg0_P; sel.get(G_bif_tads_globals->rng_id)->get_state(vmg_ &ele); lst->cons_set_element(1, &ele); /* discard gc protection and return the new list */ G_stk->discard(1); retval_obj(vmg_ lstid); } else if (argc == 1 && G_stk->get(0)->is_listlike(vmg0_)) { /* * Randomize([state]) - restores a previous RNG state. The first * list element is the RNG ID; the second is the saved state data, * in an RNG-specific format. */ vm_val_t idele, state; G_stk->get(0)->ll_index(vmg_ &idele, 1); G_stk->get(0)->ll_index(vmg_ &state, 2); int id = idele.num_to_int(vmg0_); /* set the state for the selected generator */ CVmRNGSelector sel Pvmg0_P; sel.get(id)->put_state(vmg_ &state); /* valid ID - select the generator */ G_bif_tads_globals->rng_id = id; /* no return value */ retval_nil(vmg0_); } else if ((argc == 1 || argc == 2) && G_stk->get(0)->is_numeric(vmg0_)) { /* * randomize(id) - select the RNG identified by 'id' *. randomize(id, nil) - select and seed with random data *. randomize(id, val) - select and seed with fixed data */ /* get the ID, and get the generator interface for it */ int id = G_stk->get(0)->num_to_int(vmg0_); CVmRNGSelector sel Pvmg0_P; IVmBifTadsRNG *rng = sel.get(id); /* * Determine whether and how we're seeding the RNG. If there's * only the one argument, we're not seeding it. If there's a * second argument, and it's nil, we're seeding with OS random * data. Otherwise we're seeding with the fixed data in the second * argument, which must be an integer or string value. */ if (argc == 2) { /* get the seed, and sense its type */ vm_val_t *seed = G_stk->get(1); const char *str; if (seed->typ == VM_NIL) { /* nil - use random data from the OS */ rng->seed_random(); } else if (seed->is_numeric(vmg0_)) { /* fixed integer seed value */ rng->seed_int(seed->num_to_int(vmg0_)); } else if ((str = seed->get_as_string(vmg0_)) != 0) { /* string seed value */ rng->seed_str(str + VMB_LEN, vmb_get_len(str)); } else { /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } /* if we got this far, the ID was valid, so select the RNG */ G_bif_tads_globals->rng_id = id; /* no return value */ retval_nil(vmg0_); } else { /* invalid arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } } /* * LCG randomize - seed the random-number generator */ //void CVmBifTADS::randomize(VMG_ uint argc) //{ // /* check arguments */ // check_argc(vmg_ argc, 0); // // /* seed the generator */ // os_rand(&G_bif_tads_globals->lcg_rand_seed); //} /* ------------------------------------------------------------------------ */ /* * Get the next random number from the selected RNG */ static uint32_t rng_next(VMG0_) { CVmRNGSelector sel Pvmg0_P; return sel.get(G_bif_tads_globals->rng_id)->rand(); } /* ------------------------------------------------------------------------ */ /* * Map a random 32-bit number to a smaller range 0..range-1. * * Use the "multiplication" method Knuth describes in TAoCP Vol 2 section * 3.4.2. This treats the source value as a fractional value in the range * [0..1); we multiply this fractional value by the upper bound to get a * linearly distributed number [0..range). To turn a 32-bit integer into a * fractional value in the range [0..1), divide by 2^32. * * We do this calculation entirely with 32-bit integer arithmetic. The * nominal calculation we're performing is: * *. rand_val = (ulong)((((double)rand_val) / 4294967296.0) *. * (double)range); * * To do the arithmetic entirely with integers, we refactor this by first * calculating the 64-bit product of rand_val * range, then dividing the * product by 2^32. This isn't possible to do directly with 32-bit ints, * of course. But the division is particularly easy because it's the same * as a 32-bit right-shift. So the trick is to factor out the low-order * 32-bits of the product and the high-order 32-bits, which we can do with * some bit shifting. */ static inline ulong rand_range(ulong rand_val, ulong range) { /* calculate the high-order 32 bits of (rand_val / 2^32 * range) */ ulong hi = (((rand_val >> 16) & 0xffff) * ((range >> 16) & 0xffff)) + ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) >> 16) + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) >> 16); /* calculate the low-order 32 bits */ ulong lo = ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) & 0xffff) + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) & 0xffff) + ((((rand_val & 0xffff) * (range & 0xffff)) >> 16) & 0xffff); /* add the carry from the low part into the high part to get the result */ return hi + (lo >> 16); } /* calculate a random number in the range [lower, upper] */ static inline ulong rand_range(ulong rand_val, ulong lower, ulong upper) { return lower + rand_range(rand_val, upper - lower + 1); } /* random number with two ranges */ static inline ulong rand_range(ulong rand_val, ulong lo1, ulong hi1, ulong lo2, ulong hi2) { rand_val = rand_range(rand_val, (hi1 - lo1 + 1) + (hi2 - lo2 + 1)); return rand_val + (rand_val <= hi1 - lo1 ? lo1 : lo2 - (hi1 - lo1 + 1)); } /* random number with three ranges */ static inline ulong rand_range(ulong rand_val, ulong lo1, ulong hi1, ulong lo2, ulong hi2, ulong lo3, ulong hi3) { rand_val = rand_range(rand_val, (hi1 - lo1 + 1) + (hi2 - lo2 + 1) + (hi3 - lo3 + 1)); return rand_val + (rand_val <= hi1 - lo1 ? lo1 : rand_val <= hi1 - lo1 + hi2 - lo2 + 1 ? lo2 - (hi1 - lo1 + 1) : lo3 - (hi1 - lo1 + 1) - (hi2 - lo2 + 1)); } /* ------------------------------------------------------------------------ */ /* * Random string template parser */ class RandStrParser { public: /* initialize - parse the template string */ RandStrParser(const char *src, size_t len); /* delete */ ~RandStrParser(); /* execute the template and return a string object result */ void exec(VMG_ vm_val_t *result); /* get the current character */ wchar_t getch() { return rem > 0 ? p.getch() : 0; } /* parse an integer value */ int parseInt() { /* parse digits in the input */ int acc; for (acc = 0 ; more() && is_digit(getch()) ; skip()) { /* add this digit into the accumulator */ acc *= 10; acc += value_of_digit(getch()); } /* return the accumulator value */ return acc; } /* skip the current character */ void skip() { if (rem != 0) p.inc(&rem); } /* is there more input? */ int more() const { return rem != 0; } /* get the number of bytes remaining */ size_t getrem() const { return rem; } protected: /* source string pointer and remaining length */ utf8_ptr p; size_t rem; /* root of the parse tree */ class RandStrNode *tree; }; class RandStrNode { public: RandStrNode() { firstChild = lastChild = 0; nextSibling = 0; } virtual ~RandStrNode() { while (firstChild != 0) { RandStrNode *nxt = firstChild->nextSibling; delete firstChild; firstChild = nxt; } } /* calculate the maximum length for the generated string for this node */ virtual int maxlen() = 0; /* generate the string for this node */ virtual void generate(VMG_ wchar_t *&dst) = 0; /* add a child */ void addChild(RandStrNode *chi) { if (lastChild != 0) lastChild->nextSibling = chi; else firstChild = chi; lastChild = chi; } /* tree links */ RandStrNode *firstChild, *lastChild; RandStrNode *nextSibling; }; /* * range element - this covers a single character or single 'a-z' range * within a [character list] expression */ class RandStrRange: public RandStrNode { public: RandStrRange(wchar_t c1, wchar_t c2) { if (c2 > c1) this->c1 = c1, this->c2 = c2; else this->c1 = c2, this->c2 = c1; } virtual int maxlen() { return 1; } virtual void generate(VMG_ wchar_t *&dst) { } /* low and high end of the character range, inclusive */ wchar_t c1, c2; }; /* literal string - this is a string of characters enclosed in quotes */ class RandStrLit: public RandStrNode { public: RandStrLit(class RandStrParser *p) { /* * Allocate our buffer. To make this easy, we use the total byt * length of the string remaining. This is more than we'll * actually ever need, on two counts: we could have more bytes than * characters in the remaining source, since some characters could * be multi-byte; and the quoted section probably isn't the whole * rest of the string. So we'll waste a little memory. But this * object is short-lived and the wasted space is usually trivial, * so it's not worth the additional work to be more precise in * allocating the buffer. */ buf = new wchar_t[p->getrem()]; len = 0; /* skip the open quote, then parse the contents */ for (p->skip() ; p->more() ; p->skip()) { wchar_t ch; /* if we're at a close quote, we might be done */ if ((ch = p->getch()) == '"') { /* skip the quote */ p->skip(); /* if it's not stuttered, we're done */ if ((ch = p->getch()) != '"') break; } /* add this character to our buffer */ buf[len++] = ch; } } ~RandStrLit() { delete [] buf; } virtual int maxlen() { return len; } virtual void generate(VMG_ wchar_t *&dst) { /* copy our string */ memcpy(dst, buf, len*sizeof(*dst)); /* advance the pointer */ dst += len; } protected: /* our buffer */ wchar_t *buf; /* number of charactres in the buffer */ size_t len; }; /* atom - this is a single character specifier or a [character list] */ class RandStrAtom: public RandStrNode { public: RandStrAtom(class RandStrParser *p) { /* we don't have a character class or literal character yet */ ch = 0; isLit = FALSE; /* we have no character list items yet */ listChars = 0; /* check for a character list of the form [abcw-z] */ if (p->getch() == '[') { /* keep going until we reach the ']' or end of string */ for (p->skip() ; p->more() && p->getch() != ']' ; ) { /* get the next character */ wchar_t c1 = p->getch(); /* check for quoting */ if (c1 == '%') { /* skip the '%' and get the quoted character */ p->skip(); c1 = (p->more() ? p->getch() : '%'); } /* skip the character */ p->skip(); /* if the next character is '-', we have a range expression */ if (p->getch() == '-') { /* skip the '-' */ p->skip(); /* if there's a '%', skip it as well */ if (p->getch() == '%') p->skip(); /* get the upper bound character */ wchar_t c2 = (p->more() ? p->getch() : c1); /* skip the second character */ p->skip(); /* add the range */ addChild(new RandStrRange(c1, c2)); listChars += (c2 > c1 ? c2 - c1 : c1 - c2) + 1; } else { /* it's just this character in the range */ addChild(new RandStrRange(c1, c1)); listChars += 1; } } /* skip the ']' */ p->skip(); } else if (p->getch() == '%') { /* * quoted character expression - skip the '%' and store the * single character */ p->skip(); ch = p->more() ? p->getch() : '%'; /* note that this is a literal character expression */ isLit = TRUE; /* skip the character */ p->skip(); } else { /* character class expression - store it and skip it */ ch = p->getch(); p->skip(); } } /* an atom generates exactly one character */ virtual int maxlen() { return 1; } /* generate the atom */ virtual void generate(VMG_ wchar_t *&dst) { /* * If we have a character list expression, choose a character from * the list. Otherwise, generate a character according to our * character class code. */ wchar_t outc; if (listChars != 0) { /* pick a random index in our range */ wchar_t n = (wchar_t)rand_range(rng_next(vmg0_), listChars); /* find the range containing this character */ for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) { /* cast the child - we know it's a range node */ RandStrRange *r = (RandStrRange *)chi; /* if the index is in this range, we've found it */ if (n <= r->c2 - r->c1) { outc = r->c1 + n; break; } /* deduct the size of this range from the index */ n -= (r->c2 - r->c1 + 1); } } else if (isLit) { /* literal character expression */ outc = ch; } else { /* no list, so generate a character based on the class code */ ulong rand_val = rng_next(vmg0_); switch (ch) { case 'i': /* printable ASCII character 32-126 */ outc = (wchar_t)rand_range(rand_val, 32, 126); break; case 'l': /* printable Latin-1 character 32-126, 160-255 */ outc = (wchar_t)rand_range(rand_val, 32, 126, 160, 255); break; case 'u': /* * Printable Unicode character. This excludes undefined * character, the private use area, and the control * characters. */ for (;;) { /* exclude the control and private use ranges */ outc = (wchar_t)rand_range( rand_val, 32, 127, 160, 0xDFFF, 0xF900, 0xFFFE); /* if it's a unicode character, we're set */ if (t3_is_unichar(ch)) break; /* pick a new random number */ rand_val = rng_next(vmg0_); } break; case 'd': /* decimal digit 0-9 */ outc = (wchar_t)rand_range(rand_val, '0', '9'); break; case 'X': /* upper-case hex digit 0-9 A-F */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'A', 'F'); break; case 'x': /* lower-case hex digit 0-9 a-f */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'f'); break; case 'A': /* letter A-Z */ outc = (wchar_t)rand_range(rand_val, 'A', 'Z'); break; case 'a': /* letter a-z */ outc = (wchar_t)rand_range(rand_val, 'a', 'z'); break; case 'b': /* random byte value 0-255 */ outc = (wchar_t)rand_range(rand_val, 0, 255); break; case 'c': /* mixed-case letter A-Z a-z */ outc = (wchar_t)rand_range(rand_val, 'a', 'z', 'A', 'Z'); break; case 'z': /* mixed-case letter or number 0-9 a-z A-Z */ outc = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'z', 'A', 'Z'); break; default: /* no character */ return; } } /* store the output character */ *dst++ = outc; } protected: /* for a single-character expression, the character */ wchar_t ch; /* is 'ch' a literal character, or a character code expression? */ int isLit; /* number of characters included in all of [character list] elements */ int listChars; }; /* * repeat group - this is an item optionally followed by a {repeat count}, * a '*', or a '?' */ class RandStrRepeat: public RandStrNode { public: RandStrRepeat(RandStrParser *p); /* * If we have a finite upper bound, our maximum length is our child * item length times our maximum repeat count. If there's no upper * bound, our output is in principle infinite, but the probability of * the string being at least a given length N is (2/3)^N, so the odds * drop off very rapidly as N increases; at N=25, we're down to one in * 25,000. Return 50, and use this as a hard cap in the generator. */ static const int MAX_UNBOUNDED = 50; virtual int maxlen() { return firstChild->maxlen() * (hi < 0 ? MAX_UNBOUNDED : hi); } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { if (hi >= 0) { /* * Finite upper bound. Pick a random repeat count from lo to * hi, and generate our child that many times. */ for (ulong cnt = rand_range(rng_next(vmg0_), lo, hi) ; cnt != 0 ; --cnt) firstChild->generate(vmg_ dst); } else { /* no upper bound - start with the fixed lower bound */ int i; for (i = 0 ; i < lo ; ++i) firstChild->generate(vmg_ dst); /* * Now add additional items one at a time, with 67% probability * for each added item. Stop when we fail the roll. */ for (i = 0 ; rand_range(rng_next(vmg0_), 0, 99) < 67 && i < MAX_UNBOUNDED ; ++i) firstChild->generate(vmg_ dst); } } /* * Repeat range. If hi is negative, there's no upper bound. Rather * than picking a number from 0 to infinity uniformly (which is * obviously impractical, since the expectation value is infinite), we * have a 50% chance of adding each additional item. */ int lo, hi; }; /* concatenation list - this is a list of repeat groups */ class RandStrCatList: public RandStrNode { public: RandStrCatList(RandStrParser *p) { /* * parse repeat groups until we reach the end of the string, the * end of the alternative, or the end of the parenthesized group */ while (p->more() && p->getch() != ')' && p->getch() != '|') addChild(new RandStrRepeat(p)); } /* * we generate a concatenation of our children, so our length is the * sum of our child lengths */ virtual int maxlen() { int sum = 0; for (RandStrNode *chi = firstChild ; chi != 0 ; sum += chi->maxlen(), chi = chi->nextSibling) ; return sum; } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { /* generate each child sequentially */ for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) chi->generate(vmg_ dst); } }; /* alternative list - this is a list of CatList items separated by '|' */ class RandStrAltList: public RandStrNode { public: RandStrAltList(RandStrParser *p, int isOuterExpr) { /* no alternatives yet */ altCount = 0; /* * parse alternatives until we reach the end of the string or a * close paren (unless we're the outermost expression, in which * case we treat ')' as an ordinary character since there's no * enclosing group to close) */ while (p->more() && (isOuterExpr || p->getch() != ')')) { /* parse the next alternative and add it to our child list */ addChild(new RandStrCatList(p)); altCount += 1; /* if there's a '|', skip it */ if (p->getch() == '|') p->skip(); } } /* * when we generate, we choose one child to generate, so our maximum * length is the highest of our individual child lengths */ virtual int maxlen() { int lmax = 0; for (RandStrNode *chi = firstChild ; chi != 0 ; chi = chi->nextSibling) { int l = chi->maxlen(); if (l > lmax) lmax = l; } return lmax; } /* generate */ virtual void generate(VMG_ wchar_t *&dst) { /* pick one of our alternatives at random */ int n = rand_range(rng_next(vmg0_), altCount); /* find it and generate it */ RandStrNode *chi; for (chi = firstChild ; n != 0 && chi != 0 ; --n, chi = chi->nextSibling) ; /* generate the expression */ if (chi != 0) chi->generate(vmg_ dst); } /* number of alternatives */ int altCount; }; /* * repeat group - this is an atom or parenthesized expression, optionally * followed by a postfix repeat count */ RandStrRepeat::RandStrRepeat(RandStrParser *p) { /* if we have an open paren, parse the enclosed alt list */ if (p->getch() == '(') { /* skip the open paren */ p->skip(); /* parse the alt list */ addChild(new RandStrAltList(p, FALSE)); /* if there's a close paren, skip it */ if (p->getch() == ')') p->skip(); } else if (p->getch() == '"') { /* parse the literal string */ addChild(new RandStrLit(p)); } else { /* no parens or quotes, so this is a simple character atom */ addChild(new RandStrAtom(p)); } /* check for {n}, {n-m}, *, or ? */ if (p->getch() == '{') { /* skip the '{' */ p->skip(); /* parse the low end of the range */ lo = p->parseInt(); /* if we're at a ',', get the high end of the range */ if (p->getch() == ',') { /* there's a separate high part - get it */ for (p->skip() ; is_space(p->getch()) ; p->skip()) ; /* * if the high part is missing, there's no upper bound; * otherwise parse the finite upper bound */ if (p->getch() == '}' || p->getch() == '\0') { /* -1 means "infinity" */ hi = -1; } else { /* parse the upper bound */ hi = p->parseInt(); /* make sure hi > lo */ if (hi < lo) { int tmp = hi; hi = lo; lo = tmp; } } } else { /* there's only the one number, so it's the low and high */ hi = lo; } /* skip the '}' */ if (p->getch() == '}') p->skip(); } else if (p->getch() == '+') { /* one or more */ lo = 1; hi = -1; p->skip(); } else if (p->getch() == '*') { /* zero or more */ lo = 0; hi = -1; p->skip(); } else if (p->getch() == '?') { /* zero or one */ lo = 0; hi = 1; p->skip(); } else { /* no repeat count - generate exactly once */ lo = hi = 1; } } /* * main parser implementation */ RandStrParser::RandStrParser(const char *src, size_t len) { /* set up our string pointer */ this->p.set((char *)src); this->rem = len; /* parse the tree starting at the top of the recursive descent */ tree = new RandStrAltList(this, TRUE); } RandStrParser::~RandStrParser() { /* delete the parse tree */ delete tree; } void RandStrParser::exec(VMG_ vm_val_t *result) { /* calculate the maximum length of the result */ size_t len = tree->maxlen(); /* if the maximum length is zero, just return an empty string */ if (len == 0) { result->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return; } /* allocate a wchar_t buffer of the maximum length */ wchar_t *buf = new wchar_t[len]; /* generate the string */ wchar_t *dst = buf; tree->generate(vmg_ dst); /* build the return string */ result->set_obj(CVmObjString::create(vmg_ FALSE, buf, dst - buf)); /* done with the temporary buffer */ delete [] buf; } /* ------------------------------------------------------------------------ */ /* * rand - generate a random number, or choose an element randomly from a * list of values or from our list of arguments. * * With one integer argument N, we choose a random number from 0 to N-1. * * With one list argument, we choose a random element of the list. * * With multiple arguments, we choose one argument at random and return * its value. Note that, because this is an ordinary built-in function, * all of our arguments will be fully evaluated. */ void CVmBifTADS::rand(VMG_ uint argc) { int32_t range; int use_range; int choose_an_arg = FALSE; int choose_an_ele = FALSE; ulong rand_val; /* determine the desired range of values based on the arguments */ if (argc == 0) { /* * if no argument is given, produce a random number in our full * range - clear the 'use_range' flag to so indicate */ use_range = FALSE; } else if (argc == 1 && G_stk->get(0)->typ == VM_INT) { /* we're returning a number in the range 0..(arg-1) */ range = G_stk->get(0)->val.intval; use_range = TRUE; /* discard the argument */ G_stk->discard(); } else if (argc == 1 && G_stk->get(0)->is_listlike(vmg0_) && (range = G_stk->get(0)->ll_length(vmg0_)) >= 0) { /* use the range of 1..length */ use_range = TRUE; /* note that we're choosing from a list-like object */ choose_an_ele = TRUE; /* note - leave the object on the stack as gc protection */ } else if (argc == 1 && G_stk->get(0)->get_as_string(vmg0_) != 0) { /* * It's a string, giving a template for generating a new random * string. First, get the string argument. */ const char *tpl = G_stk->get(0)->get_as_string(vmg0_); size_t tplbytes = vmb_get_len(tpl); tpl += VMB_LEN; #if 1 /* parse it */ RandStrParser *rsp = new RandStrParser(tpl, tplbytes); err_try { /* generate the string */ vm_val_t ret; rsp->exec(vmg_ &ret); /* return it */ retval(vmg_ &ret); } err_finally { /* delete our parser on the way out */ delete rsp; } err_end; #else /* figure its character length */ utf8_ptr tplp((char *)tpl); size_t tplchars = tplp.len(tplbytes); /* allocate temporary space for the result, as wide characters */ wchar_t *buf = new wchar_t[tplchars], *dst; /* run through the template and generate random characters */ for (dst = buf ; tplbytes != 0 ; ) { /* generate a random number for this character */ rand_val = rng_next(vmg0_); /* get and skip the next template character */ wchar_t tch = tplp.getch(); tplp.inc(&tplbytes); /* translate the template character */ wchar_t ch; switch (tch) { case '.': /* printable ASCII character 32-126 */ ch = (wchar_t)rand_range(rand_val, 32, 126); break; case '?': /* printable Latin-1 character 32-126, 160-255 */ ch = (wchar_t)rand_range(rand_val, 32, 126, 160, 255); break; case '*': /* * Printable Unicode character. This excludes undefined * character, the private use area, and the control * characters. */ for (;;) { /* exclude the control and private use ranges */ ch = (wchar_t)rand_range( rand_val, 32, 127, 160, 0xDFFF, 0xF900, 0xFFFE); /* if it's a unicode character, we're set */ if (t3_is_unichar(ch)) break; /* pick a new random number */ rand_val = rng_next(vmg0_); } break; case '9': /* digit 0-9 */ ch = (wchar_t)rand_range(rand_val, '0', '9'); break; case 'X': /* upper-case hex digit 0-9 A-F */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'A', 'F'); break; case 'x': /* lower-case hex digit 0-9 a-f */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'f'); break; case 'A': /* letter A-Z */ ch = (wchar_t)rand_range(rand_val, 'A', 'Z'); break; case 'a': /* letter a-z */ ch = (wchar_t)rand_range(rand_val, 'a', 'z'); break; case 'b': /* random byte value 0-255 */ ch = (wchar_t)rand_range(rand_val, 0, 255); break; case 'c': /* mixed-case letter A-Z a-z */ ch = (wchar_t)rand_range(rand_val, 'a', 'z', 'A', 'Z'); break; case 'z': /* mixed-case letter or number 0-9 a-z A-Z */ ch = (wchar_t)rand_range(rand_val, '0', '9', 'a', 'z', 'A', 'Z'); break; case '[': /* * Character range. Scan the range to count the number of * characters included. */ { /* * allocate a list of range descriptors - use 'len2' as * the range count, since at the most we could have one * range per byte (but it'll usually be less) */ struct rdesc { int set(wchar_t c) { start = end = c; return 1; } int set(wchar_t a, wchar_t b) { if (b > a) start = a, end = b; else start = b, end = a; return end - start + 1; } wchar_t start; wchar_t end; }; rdesc *ranges = new rdesc[tplbytes]; int nranges = 0, nchars = 0; /* scan for the closing ']' */ for ( ; tplbytes != 0 && tplp.getch() != ']' ; tplp.inc(&tplbytes)) { /* check for escapes */ wchar_t rch = tplp.getch(); if (rch == '%') { tplp.inc(&tplbytes); rch = tplp.getch(); } /* if the next character is '-', it's a range */ if (tplbytes > 1 && tplp.getch_at(1) == '-') { /* skip the current character and the '-' */ tplp.inc(&tplbytes); tplp.inc(&tplbytes); /* if the next character is quoted, skip the % */ if (tplbytes > 1 && tplp.getch() == '%') tplp.inc(&tplbytes); /* * The range count includes everything from * 'rch' to the current character. */ if (tplbytes != 0) nchars += ranges[nranges++].set( rch, tplp.getch()); } else { /* it's a single character range */ nchars += ranges[nranges++].set(rch); } /* if we're out of characters, we're done */ if (tplbytes == 0) break; } /* pick a number from 0 to rcnt */ ch = (wchar_t)rand_range(rand_val, nchars); /* find the character */ for (int i = 0 ; i < nranges ; ++i) { /* if it's in the current range, apply it */ const rdesc *r = &ranges[i]; if (ch <= r->end - r->start) { ch += r->start; break; } /* * it's not in this range, so deduct the range * length from the remainder and keep going */ ch -= r->end - r->start + 1; } /* done with the range list */ delete [] ranges; /* skip the closing ']' */ if (tplbytes != 0 && tplp.getch() == ']') tplp.inc(&tplbytes); } break; case '%': /* copy the next character unchanged */ if (tplbytes != 0) { ch = tplp.getch(); tplp.inc(&tplbytes); } else ch = '%'; break; default: /* no character */ continue; } /* save it in our temp buffer, and note its utf8 size */ *dst++ = ch; } /* convert the temp buffer to a real string */ retval_obj(vmg_ CVmObjString::create(vmg_ FALSE, buf, dst - buf)); /* done with the temporary buffer */ delete [] buf; #endif /* discard the argument */ G_stk->discard(); /* we're done */ return; } else { /* * produce a random number in the range 0..(argc-1) so that we * can select one of our arguments */ range = argc; use_range = TRUE; /* note that we should choose an argument value */ choose_an_arg = TRUE; } /* get the next random number */ rand_val = rng_next(vmg0_); /* * Calculate our random value in the range 0..(range-1). If range * == 0, simply choose a value across our full range. */ if (use_range) rand_val = rand_range(rand_val, range); /* * Return the appropriate value, depending on our argument list */ if (choose_an_arg) { /* return the selected argument */ retval(vmg_ G_stk->get((int)rand_val)); /* discard all of the arguments */ G_stk->discard(argc); } else if (choose_an_ele) { vm_val_t val; /* get the selected element */ if (range == 0) { /* there are no elements to choose from, so return nil */ val.set_nil(); } else { /* get the selected list element */ G_stk->get(0)->ll_index(vmg_ &val, rand_val + 1); } /* set the result */ retval(vmg_ &val); /* discard our gc protection */ G_stk->discard(); } else { /* simply return the random number */ retval_int(vmg_ (long)rand_val); } } /* ------------------------------------------------------------------------ */ /* * toString - convert to string */ void CVmBifTADS::toString(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 3); /* get the argument */ vm_val_t *val = G_stk->get(0); /* if there's a radix specified, pop it as well */ int radix = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : 10); /* the radix must be from 2 to 36 */ if (radix < 2 || radix > 36) err_throw(VMERR_BAD_VAL_BIF); /* assume unsigned */ int flags = TOSTR_UNSIGNED;; /* * If the 'isSigned' argument is present, pop it as a bool; otherwise * treat the value as signed if the radix is decimal, otherwise * unsigned. */ if (argc >= 3 ? G_stk->get(2)->get_logical_only() : radix == 10) flags &= ~TOSTR_UNSIGNED; /* convert the value */ toString(vmg_ G_interpreter->get_r0(), val, radix, flags); /* discard arguments */ G_stk->discard(argc); } /* * explicitly convert to string */ void CVmBifTADS::toString(VMG_ vm_val_t *result, const vm_val_t *val, int radix, int flags) { /* do the basic string conversion */ char buf[50]; const char *p = CVmObjString::cvt_to_str( vmg_ result, buf, sizeof(buf), val, radix, flags); /* save the new string on the stack to protect from garbage collection */ G_stk->push(result); /* * if the return value wasn't already a new object, create a string * from the return value */ if (result->typ != VM_OBJ) { /* we just have a string in a buffer - create a new string from it */ result->set_obj(CVmObjString::create( vmg_ FALSE, p + VMB_LEN, vmb_get_len(p))); } /* discard gc protection */ G_stk->discard(1); } /* * toInteger - convert to an integer */ void CVmBifTADS::toInteger(VMG_ uint argc) { /* convert as an integer only */ toIntOrNum(vmg_ argc, TRUE); } /* * toNumber - convert to an integer or BigNumber */ void CVmBifTADS::toNumber(VMG_ uint argc) { /* convert as integer or BigNumber */ toIntOrNum(vmg_ argc, FALSE); } /* * Common handler for toInteger and toNumber */ void CVmBifTADS::toIntOrNum(VMG_ uint argc, int int_only) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* check for BigNumber values */ vm_val_t *valp = G_stk->get(0); if (valp->typ == VM_OBJ && CVmObjBigNum::is_bignum_obj(vmg_ valp->val.obj)) { /* * If we only want integer results, try converting the BigNumber to * an integer. Otherwise, simply return the BigNumber as-is. */ if (int_only) { /* we only want an integer result - convert to int */ long intval = ((CVmObjBigNum *)vm_objp(vmg_ valp->val.obj)) ->convert_to_int(); /* return the integer value */ retval_int(vmg_ intval); } else { /* BigNumber results are okay - just return the BigNumber */ retval(vmg_ valp); } /* discard arguments, and we're done */ G_stk->discard(argc); return; } /* if it's already an integer, just return the same value */ if (valp->typ == VM_INT) { /* just return the argument value */ retval_int(vmg_ valp->val.intval); /* discard arguments and return */ G_stk->discard(argc); return; } /* if it's true or nil, convert to 1 or 0 */ if (valp->typ == VM_TRUE || valp->typ == VM_NIL) { /* return 1 for true, 0 for nil */ retval_int(vmg_ valp->typ == VM_TRUE ? 1 : 0); G_stk->discard(argc); return; } /* the only other type of value we can convert is a string */ const char *strp = G_stk->get(0)->get_as_string(vmg0_); if (strp == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the string length and buffer pointer */ size_t len = vmb_get_len(strp); strp += VMB_LEN; /* if there's a radix specified, pop it as well; the default is 10 */ int radix = 10; if (argc >= 2) { /* get the radix from the stack */ radix = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's in the valid range */ if (radix < 2 || radix > 36) err_throw(VMERR_BAD_VAL_BIF); } /* get a version of the string stripped of leading and trailing spaces */ const char *p2 = strp; size_t len2 = len; for ( ; len2 != 0 && is_space(*p2) ; ++p2, --len2) ; for ( ; len2 != 0 && is_space(*(p2 + len2 - 1)) ; --len2) ; /* * Check for "nil" and "true", ignoring leading and trailing spaces; if * it matches either of those, return the corresponding boolean value. * Otherwise, parse the string as an integer value in the given radix. */ if (len2 == 3 && memcmp(p2, "nil", 3) == 0) { /* the value for "nil" is 0 */ retval_int(vmg_ 0); } else if (len2 == 4 && memcmp(p2, "true", 3) == 0) { /* the value for "true" is 1 */ retval_int(vmg_ 1); } else { /* parse the string as an integer (orBigNumber if it's too large) */ vm_val_t val; CVmObjString::parse_num_val(vmg_ &val, strp, len, radix, int_only); retval(vmg_ &val); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * put an integer value in a constant list, advancing the list write * pointer */ static void put_list_int(char **dstp, long intval) { /* set up the integer value */ vm_val_t val; val.set_int(intval); /* write it to the list */ vmb_put_dh(*dstp, &val); /* advance the output pointer */ *dstp += VMB_DATAHOLDER; } /* * put an object value in a constant list, advancing the list write * pointer */ static void put_list_obj(char **dstp, vm_obj_id_t objval) { vm_val_t val; /* set up the integer value */ val.set_obj(objval); /* write it to the list */ vmb_put_dh(*dstp, &val); /* advance the output pointer */ *dstp += VMB_DATAHOLDER; } /* * Given a time_t value, create a list of components giving the local time * corresponding to the time_t, using the getTime(GetDateAndTime) format. * Returns the object ID of the new list. * * The list format is [year, month, day, day-of-week, day-of-year, hour, * minute, second, seconds-since-1970]. */ vm_obj_id_t CVmBifTADS::format_datetime_list(VMG_ os_time_t timer) { /* note the starting stack pointer, so we can discard gc protection */ vm_val_t *gsp = G_stk->get_sp(); /* convert the time_t to the local time */ struct tm *tblock = os_localtime(&timer); /* start the list buffer - set the length (9 elements) */ char buf[80]; vmb_put_len(buf, 9); char *dst = buf + VMB_LEN; /* add the time elements to the list */ put_list_int(&dst, tblock->tm_year + 1900); put_list_int(&dst, tblock->tm_mon + 1); put_list_int(&dst, tblock->tm_mday); put_list_int(&dst, tblock->tm_wday + 1); put_list_int(&dst, tblock->tm_yday + 1); put_list_int(&dst, tblock->tm_hour); put_list_int(&dst, tblock->tm_min); put_list_int(&dst, tblock->tm_sec); /* * if the timer is over 0x7fffffff, we can't represent it as an * int32_t, so store it as a BigNumber value */ if (timer <= 0x7fffffffU) { /* it'll fit in an ordinary 32-bit signed int */ put_list_int(&dst, (uint32_t)timer); } else { /* it won't fit an int32, so convert to BigNumber */ vm_obj_id_t bn; if (sizeof(timer) <= 4) { /* os_time_t must be a uint32 */ bn = CVmObjBigNum::createu(vmg_ FALSE, (ulong)timer, 10); } else { /* os_time_t must be 64 bits (or larger) */ bn = CVmObjBigNum::create_int64( vmg_ FALSE, (uint32_t)(timer >> 32), (uint32_t)(timer & 0xFFFFFFFF)); } /* save it on the stack for gc protection */ G_stk->push()->set_obj(bn); /* add it to the list */ put_list_obj(&dst, bn); } /* create a list from the formatted buffer, and return its object ID */ vm_obj_id_t lst = CVmObjList::create(vmg_ FALSE, buf); /* discard gc protection */ G_stk->set_sp(gsp); /* return the list */ return lst; } /* * get the current time */ void CVmBifTADS::gettime(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's an argument, get the type of time value to return */ int typ; if (argc == 1) { /* get the time type code */ typ = pop_int_val(vmg0_); } else { /* use the default type */ typ = 1; } /* check the type */ switch(typ) { case 1: /* * GetTimeDateAndTime - return the current time and date as a list * of time elements */ /* get the current system time and format it into our list */ retval_obj(vmg_ format_datetime_list(vmg_ os_time(NULL))); /* done */ break; case 2: /* * They want the high-precision system timer value, which returns * the time in milliseconds from an arbitrary zero point. */ { unsigned long t; static unsigned long t_zero; static int t_zero_set = FALSE; /* retrieve the raw time from the operating system */ t = os_get_sys_clock_ms(); /* * We only have 31 bits of precision in our result (since we * must fit the value into a signed integer), so we can only * represent time differences of about 23 days. Now, the * value from the OS could be at any arbitrary point in our * 23-day range, so there's a nontrivial probability that the * raw OS value is near enough to the wrapping point that a * future call to this same function during the current * session could encounter the wrap condition. The caller is * likely to be confused by this, because the time difference * from this call to that future call would appear to be * negative. * * There's obviously no way we can eliminate the possibility * of a negative time difference if the current program * session lasts more than 23 days of continuous execution. * Fortunately, it seems unlikely that most sessions will be * so long, which gives us a way to reduce the likelihood that * the program will encounter a wrapped timer: we can adjust * the zero point of the timer to the time of the first call * to this function. That way, the timer will wrap only if * the program session runs continuously until the timer's * range is exhausted. */ if (!t_zero_set) { /* this is the first call - remember the zero point */ t_zero = t; t_zero_set = TRUE; } /* * Adjust the time by subtracting the zero point from the raw * OS timer. This will give us the number of milliseconds * from our zero point. * * If the system timer has wrapped since our zero point, we'll * get what looks like a negative number; but what we really * have is a large positive number with a borrow from an * unrepresented higher-precision portion, so the fact that * this value is negative doesn't matter - it will still be * sequential when treated as unsigned. */ t -= t_zero; /* * whatever we got, keep only the low-order 31 bits, since we * only have 31 bits in which to represent an unsigned value */ t &= 0x7fffffff; /* return the value we've calculated */ retval_int(vmg_ t); } break; default: err_throw(VMERR_BAD_VAL_BIF); } } /* ------------------------------------------------------------------------ */ /* * re_match - match a regular expression to a string */ void CVmBifTADS::re_match(VMG_ uint argc) { const char *str; utf8_ptr p; size_t len; int match_len; vm_val_t *v1, *v2, *v3; int start_idx; CVmObjPattern *pat_obj = 0; const char *pat_str = 0; /* check arguments */ check_argc_range(vmg_ argc, 2, 3); /* * make copies of the arguments, so we can pop the values without * actually removing them from the stack - leave the originals on the * stack for gc protection */ v1 = G_stk->get(0); v2 = G_stk->get(1); v3 = (argc >= 3 ? G_stk->get(2) : 0); G_stk->push(v2); G_stk->push(v1); /* note the starting index, if given */ start_idx = 1; if (v3 != 0) { /* check the type */ if (v3->typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* get the value */ start_idx = (int)v3->val.intval; } /* * remember the last search string (the second argument), and reset any * old group registers (since they'd point into the previous string) */ G_bif_tads_globals->last_rex_str->val = *v2; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* * check what we have for the pattern - we could have either a string * giving the regular expression, or a RexPattern object with the * compiled pattern */ if (G_stk->get(0)->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj)) { vm_val_t pat_val; /* get the pattern object */ G_stk->pop(&pat_val); pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj); } else { /* get the pattern string */ pat_str = pop_str_val(vmg0_); } /* get the string to match */ str = pop_str_val(vmg0_); len = vmb_get_len(str); p.set((char *)str + VMB_LEN); /* if the starting index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(len) : -1); /* skip to the starting index */ for ( ; start_idx > 0 && len != 0 ; --start_idx, p.inc(&len)) ; /* match the pattern */ if (pat_obj != 0) { /* match the compiled pattern object */ match_len = G_bif_tads_globals->rex_searcher-> match_pattern(pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len); } else { /* match the pattern to the regular expression string */ match_len = G_bif_tads_globals->rex_searcher-> compile_and_match(pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len); } /* check for a match */ if (match_len >= 0) { /* we got a match - calculate the character length of the match */ retval_int(vmg_ (long)p.len(match_len)); } else { /* no match - return nil */ retval_nil(vmg0_); } /* discard the arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * Common handler for re_search() and re_search_back() */ template<int dir> inline void CVmBifTADS::re_search_common(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 2, 3); /* * make copies of the arguments, so we can pop the values without * actually removing them from the stack - leave the originals on the * stack for gc protection */ vm_val_t *v1 = G_stk->get(0); vm_val_t *v2 = G_stk->get(1); vm_val_t *v3 = (argc >= 3 ? G_stk->get(2) : 0); G_stk->push(v2); G_stk->push(v1); /* note the starting index, if given */ int start_idx = (dir > 0 ? 1 : 0); if (v3 != 0) { /* check the type */ if (v3->typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* get the value */ start_idx = (int)v3->val.intval; } /* * remember the last search string (the second argument), and clear out * any old group registers (since they'd point into the old string) */ G_bif_tads_globals->last_rex_str->val = *v2; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* check to see if we have a RexPattern object or an uncompiled string */ const char *pat_str = 0; CVmObjPattern *pat_obj = 0; if (G_stk->get(0)->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj)) { vm_val_t pat_val; /* get the pattern object */ G_stk->pop(&pat_val); pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj); } else { /* get the pattern string */ pat_str = pop_str_val(vmg0_); } /* get the string to search for the pattern */ const char *str = pop_str_val(vmg0_); utf8_ptr p((char *)str + VMB_LEN); size_t len = vmb_get_len(str); /* if the starting index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(len) : start_idx == 0 && dir < 0 ? (int)p.len(len) : -1); /* skip to the starting index */ for (int i = start_idx ; i > 0 && len != 0 ; --i, p.inc(&len)) ; /* search for the pattern */ int match_idx; int match_len; if (pat_obj != 0) { /* try finding the compiled pattern */ match_idx = (dir > 0 ? G_bif_tads_globals->rex_searcher->search_for_pattern( pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len, &match_len) : G_bif_tads_globals->rex_searcher->search_back_for_pattern( pat_obj->get_pattern(vmg0_), str + VMB_LEN, p.getptr(), len, &match_len)); } else { /* try finding the regular expression string pattern */ match_idx = (dir > 0 ? G_bif_tads_globals->rex_searcher->compile_and_search( pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len, &match_len) : G_bif_tads_globals->rex_searcher->compile_and_search_back( pat_str + VMB_LEN, vmb_get_len(pat_str), str + VMB_LEN, p.getptr(), len, &match_len)); } /* check for a match */ if (match_idx >= 0) { /* * We found a match - calculate the character index of the match * offset, adjusted to a 1-base. The character index is simply the * number of characters in the part of the string up to the match * index. Note that we have to add the starting index to get the * actual index in the overall string, since 'p' points to the * character at the starting index. Also note that when searching * backwards, the match index is the number of characters *before* * the starting point. */ size_t char_idx; if (dir > 0) char_idx = p.len(match_idx) + start_idx + 1; else char_idx = utf8_ptr::s_len( str + VMB_LEN, start_idx - match_idx) + 1; /* calculate the character length of the match */ utf8_ptr matchp(p.getptr() + dir*match_idx); size_t char_len = matchp.len(match_len); /* allocate a string containing the match */ vm_obj_id_t match_str_obj = CVmObjString::create(vmg_ FALSE, matchp.getptr(), match_len); /* push it momentarily as protection against garbage collection */ G_stk->push()->set_obj(match_str_obj); /* * set up a 3-element list to contain the return value: * [match_start_index, match_length, match_string] */ char buf[VMB_LEN + VMB_DATAHOLDER * 3]; vmb_put_len(buf, 3); char *dst = buf + VMB_LEN; put_list_int(&dst, (long)char_idx); put_list_int(&dst, (long)char_len); put_list_obj(&dst, match_str_obj); /* allocate and return the list */ retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf)); /* we no longer need the garbage collection protection */ G_stk->discard(); } else { /* no match - return nil */ retval_nil(vmg0_); } /* discard the arguments */ G_stk->discard(argc); } /* * re_search - search for a substring matching a regular expression * within a string */ void CVmBifTADS::re_search(VMG_ uint argc) { re_search_common<1>(vmg_ argc); } /* * re_search_back - search backwards for a regular expression */ void CVmBifTADS::re_search_back(VMG_ uint argc) { re_search_common<-1>(vmg_ argc); } /* ------------------------------------------------------------------------ */ /* * re_group - get the string matching a group in the most recent regular * expression search or match */ void CVmBifTADS::re_group(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* get the group number to retrieve */ int groupno = pop_int_val(vmg0_); /* group 0 is the special group for the overall match */ const re_group_register *reg; if (groupno == 0) { /* get the special register for the overall match */ reg = G_bif_tads_globals->rex_searcher->get_last_match(); } else { /* make sure it's in range */ if (groupno < 1 || groupno > RE_GROUP_REG_CNT) err_throw(VMERR_BAD_VAL_BIF); /* adjust from a 1-base to a 0-base */ --groupno; /* if the group doesn't exist in the pattern, return nil */ if (groupno >= G_bif_tads_globals->rex_searcher->get_group_cnt()) { retval_nil(vmg0_); return; } /* get the group register */ reg = G_bif_tads_globals->rex_searcher->get_group_reg(groupno); } /* * get the previous search string - get a pointer directly to the * contents of the string */ const char *last_str = G_bif_tads_globals->last_rex_str->val.get_as_string(vmg0_); /* if the group wasn't set, or there's no last string, return nil */ if (last_str == 0 || reg->start_ofs == -1 || reg->end_ofs == -1) { retval_nil(vmg0_); return; } /* set up for a list with three elements */ char buf[VMB_LEN + 3*VMB_DATAHOLDER]; vmb_put_len(buf, 3); char *dst = buf + VMB_LEN; /* get the starting offset from the group register */ int start_byte_ofs = reg->start_ofs; /* * The first element is the character index of the group text in the * source string. Calculate the character index by adding 1 to the * character length of the text preceding the group; calculate the * character length from the byte length of that string. Note that the * starting in the group register is stored from the starting point of * the search, not the start of the string, so we need to add in the * starting point in the search. */ utf8_ptr p((char *)last_str + VMB_LEN); put_list_int(&dst, p.len(start_byte_ofs) + 1); /* * The second element is the character length of the group text. * Calculate the character length from the byte length. */ p.set(p.getptr() + start_byte_ofs); put_list_int(&dst, p.len(reg->end_ofs - reg->start_ofs)); /* * The third element is the string itself. Create a new string * containing the matching substring. */ vm_obj_id_t strobj = CVmObjString::create( vmg_ FALSE, p.getptr(), reg->end_ofs - reg->start_ofs); put_list_obj(&dst, strobj); /* save the string on the stack momentarily to protect against GC */ G_stk->push()->set_obj(strobj); /* create and return the list value */ retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf)); /* we no longer need the garbage collector protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * rexReplace() - replace one or all occurrences of a regular expression in * a given subject string with a given replacement string. This uses the * common find/replace handler defined in vmfindrep.h. */ void CVmBifTADS::re_replace(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 3, 6); /* * do the replacement - the subject string is the 2nd stack argument * (at stack offset 1), so the replacement string argument is at stack * offset 2 */ vm_find_replace<VMFINDREPLACE_rexReplace>( vmg_ G_interpreter->get_r0(), argc, G_stk->get(1), 0); /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * savepoint - establish an undo savepoint */ void CVmBifTADS::savepoint(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* establish the savepoint */ G_undo->create_savept(vmg0_); } /* * undo - undo changes to most recent savepoint */ void CVmBifTADS::undo(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* if no undo is available, return nil to indicate that we can't undo */ if (G_undo->get_savept_cnt() == 0) { /* we can't undo */ retval_nil(vmg0_); } else { /* undo to the savepoint */ G_undo->undo_to_savept(vmg0_); /* tell the caller that we succeeded */ retval_true(vmg0_); } } /* ------------------------------------------------------------------------ */ /* * save */ void CVmBifTADS::save(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the filename argument */ const vm_val_t *filespec = G_stk->get(0); /* * if there's a metadata table argument, fetch it (but leave the value * on the stack for gc protection) */ CVmObjLookupTable *metatab = 0; if (argc >= 2) { /* it must be a LookupTable object, or nil */ vm_val_t *v = G_stk->get(1); if (v->typ == VM_NIL) { /* nil, so there's no table - just discard the argument */ G_stk->discard(); } else { /* it's not nil, so it has to be a LookupTable object */ if (!CVmObjLookupTable::is_lookup_table_obj(vmg_ v->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the lookup table object */ metatab = (CVmObjLookupTable *)vm_objp(vmg_ v->val.obj); } } /* set up for network storage server access, if applicable */ vm_rcdesc rc(vmg_ "saveGame", bif_table, 15, G_stk->get(0), argc); CVmNetFile *netfile = CVmNetFile::open( vmg_ filespec, &rc, NETF_WRITE | NETF_CREATE | NETF_TRUNC, OSFTT3SAV, "application/x-t3vm-state"); /* open the file and save the game */ CVmFile *file = 0; err_try { /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ netfile, VMOBJFILE_ACCESS_WRITE); /* open the file */ osfildef *fp = osfoprwtb(netfile->lclfname, OSFTT3SAV); if (fp == 0) err_throw(VMERR_CREATE_FILE); /* set up the file writer */ file = new CVmFile(); file->set_file(fp, 0); /* save the state */ CVmSaveFile::save(vmg_ file, metatab); /* close the file */ delete file; file = 0; } err_catch_disc { /* close the file if it's still open */ if (file != 0) delete file; /* abandon the network file */ if (netfile != 0) netfile->abandon(vmg0_); /* rethrow the error */ err_rethrow(); } err_end; /* close out the network file */ netfile->close(vmg0_); /* discard arguments */ G_stk->discard(argc); /* no return value */ retval_nil(vmg0_); } /* * restore */ void CVmBifTADS::restore(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* get the filename or spec */ const vm_val_t *filespec = G_stk->get(0); /* set up for network storage server access, if applicable */ vm_rcdesc rc(vmg_ "restoreGame", bif_table, 16, G_stk->get(0), argc); CVmNetFile *netfile = CVmNetFile::open( vmg_ filespec, &rc, NETF_READ, OSFTT3SAV, "application/x-t3vm-state"); /* open the file and restore the game */ CVmFile *file = 0; int err = 0; err_try { /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ netfile, VMOBJFILE_ACCESS_READ); /* open the file */ osfildef *fp = osfoprb(netfile->lclfname, OSFTT3SAV); if (fp == 0) err_throw(VMERR_FILE_NOT_FOUND); /* set up the file reader */ file = new CVmFile(fp, 0); /* restore the state; throw an exception on error */ if ((err = CVmSaveFile::restore(vmg_ file)) != 0) err_throw(err); } err_finally { /* close our local file */ if (file != 0) delete file; /* close the network file */ if (netfile != 0) netfile->close(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* no return value */ retval_nil(vmg0_); } /* * restart */ void CVmBifTADS::restart(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* reset the VM to the image file's initial state */ CVmSaveFile::reset(vmg0_); /* no return value */ retval_nil(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Get the maximum value from a set of argument */ void CVmBifTADS::get_max(VMG_ uint argc) { uint i; vm_val_t cur_max; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* start with the first argument as the presumptive maximum */ cur_max = *G_stk->get(0); /* if there's one list-like argument, get the max of the list elements */ if (argc == 1 && cur_max.is_listlike(vmg0_)) { /* get the list length */ vm_val_t lst = cur_max; uint cnt = lst.ll_length(vmg0_); /* if it's a zero-element list, it's an error */ if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the first element as the tentative maximum */ lst.ll_index(vmg_ &cur_max, 1); /* compare each additional list element */ for (i = 2 ; i <= cnt ; ++i) { /* compare the current element and keep it if it's the highest */ vm_val_t ele; lst.ll_index(vmg_ &ele, i); if (ele.compare_to(vmg_ &cur_max) > 0) cur_max = ele; } } else { /* compare each argument in turn */ for (i = 1 ; i < argc ; ++i) { /* * compare this value to the maximum so far; if this value is * greater, it becomes the new maximum so far */ if (G_stk->get(i)->compare_to(vmg_ &cur_max) > 0) cur_max = *G_stk->get(i); } } /* discard the arguments */ G_stk->discard(argc); /* return the maximum value */ retval(vmg_ &cur_max); } /* * Get the minimum value from a set of argument */ void CVmBifTADS::get_min(VMG_ uint argc) { uint i; vm_val_t cur_min; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* start with the first argument as the presumptive minimum */ cur_min = *G_stk->get(0); /* if there's one list-like argument, get the max of the list elements */ if (argc == 1 && cur_min.is_listlike(vmg0_)) { /* get the list length */ vm_val_t lst = cur_min; uint cnt = lst.ll_length(vmg0_); /* if it's a zero-element list, it's an error */ if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the first element as the tentative maximum */ lst.ll_index(vmg_ &cur_min, 1); /* compare each additional list element */ for (i = 2 ; i <= cnt ; ++i) { /* compare the current element and keep it if it's the highest */ vm_val_t ele; lst.ll_index(vmg_ &ele, i); if (ele.compare_to(vmg_ &cur_min) < 0) cur_min = ele; } } else { /* compare each argument in turn */ for (i = 1 ; i < argc ; ++i) { /* * compare this value to the minimum so far; if this value is * less, it becomes the new minimum so far */ if (G_stk->get(i)->compare_to(vmg_ &cur_min) < 0) cur_min = *G_stk->get(i); } } /* discard the arguments */ G_stk->discard(argc); /* return the minimum value */ retval(vmg_ &cur_min); } /* ------------------------------------------------------------------------ */ /* * makeString - construct a string by repeating a character; by * converting a unicode code point to a string; or by converting a list * of unicode code points to a string */ void CVmBifTADS::make_string(VMG_ uint argc) { vm_val_t val; long rpt; vm_obj_id_t new_str_obj; CVmObjString *new_str; size_t new_str_len; char *new_strp; const char *strp = 0; int lst_len = -1; size_t i; utf8_ptr dst; /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the base value */ G_stk->pop(&val); /* if there's a repeat count, get it */ rpt = (argc >= 2 ? pop_long_val(vmg0_) : 1); /* if the repeat count is less than zero, it's an error */ if (rpt < 0) err_throw(VMERR_BAD_VAL_BIF); /* leave the original value on the stack to protect it from GC */ G_stk->push(&val); /* * see what we have, and calculate how much space we'll need for the * result string */ switch(val.typ) { case VM_LIST: /* it's a list of integers giving unicode character values */ lst_len = val.ll_length(vmg0_); do_list: /* * Run through the list and get the size of each character, so * we can determine how long the string will have to be. */ for (new_str_len = 0, i = 1 ; (int)i <= lst_len ; ++i) { /* get this element */ vm_val_t ele_val; val.ll_index(vmg_ &ele_val, i); /* if it's not an integer, it's an error */ if (ele_val.typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* add this character's byte size to the string size */ new_str_len += utf8_ptr::s_wchar_size((wchar_t)ele_val.val.intval); } break; case VM_SSTRING: /* get the string pointer */ strp = G_const_pool->get_ptr(val.val.ofs); do_string: /* * it's a string - the output length is the same as the input * length */ new_str_len = vmb_get_len(strp); break; case VM_INT: /* * it's an integer giving a unicode character value - we just * need enough space to store this particular character */ new_str_len = utf8_ptr::s_wchar_size((wchar_t)val.val.intval); break; case VM_OBJ: /* check to see if it's a string */ if ((strp = val.get_as_string(vmg0_)) != 0) goto do_string; /* check to see if it's a list */ if (val.is_listlike(vmg0_) && (lst_len = val.ll_length(vmg0_)) >= 0) goto do_list; /* it's invalid */ err_throw(VMERR_BAD_TYPE_BIF); break; default: /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); break; } /* * if the length times the repeat count would be over the maximum * 16-bit string length, it's an error */ if (new_str_len * rpt > 0xffffL - VMB_LEN) err_throw(VMERR_BAD_VAL_BIF); /* multiply the length by the repeat count */ new_str_len *= rpt; /* allocate the string and gets its buffer */ new_str_obj = CVmObjString::create(vmg_ FALSE, new_str_len); new_str = (CVmObjString *)vm_objp(vmg_ new_str_obj); new_strp = new_str->cons_get_buf(); /* set up the destination pointer */ dst.set(new_strp); /* run through the number of iterations requested */ for ( ; rpt != 0 ; --rpt) { /* build one iteration of the string, according to the type */ if (lst_len >= 0) { /* run through the list */ for (i = 1 ; i <= (size_t)lst_len ; ++i) { /* get this element */ vm_val_t ele_val; val.ll_index(vmg_ &ele_val, i); /* add this character to the string */ dst.setch((wchar_t)ele_val.val.intval); } } else if (strp != 0) { /* copy the string's contents into the output string */ memcpy(dst.getptr(), strp + VMB_LEN, vmb_get_len(strp)); /* advance past the bytes we copied */ dst.set(dst.getptr() + vmb_get_len(strp)); } else { /* set this int value */ dst.setch((wchar_t)val.val.intval); } } /* return the new string */ retval_obj(vmg_ new_str_obj); /* discard the GC protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * makeList - construct a list by repeating a given value a given number of * times */ void CVmBifTADS::make_list(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the value to use for each list element */ vm_val_t val; G_stk->pop(&val); /* if there's a repeat count, get it */ long rpt = (argc >= 2 ? pop_long_val(vmg0_) : 1); /* if the repeat count is less than zero, it's an error */ if (rpt < 0) err_throw(VMERR_BAD_VAL_BIF); /* leave the original value on the stack to protect it from GC */ G_stk->push(&val); /* allocate our return list */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, rpt); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* fill in the list with the repeated value */ for (int i = 0 ; i < rpt ; ++i) lst->cons_set_element(i, &val); /* return the new list */ retval_obj(vmg_ lst_obj); /* discard the GC protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * getFuncParams */ void CVmBifTADS::get_func_params(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* set up a method header pointer for the function pointer argument */ CVmFuncPtr hdr; if (!hdr.set(vmg_ G_stk->get(0))) err_throw(VMERR_FUNCPTR_VAL_REQD); /* * Allocate our return list. We need three elements: [minArgs, * optionalArgs, isVarargs]. */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, 3); /* get the list object, properly cast */ CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the minimum argument count */ vm_val_t val; val.set_int(hdr.get_min_argc()); lst->cons_set_element(0, &val); /* set the optional argument count */ val.set_int(hdr.get_opt_argc()); lst->cons_set_element(1, &val); /* set the varargs flag */ val.set_logical(hdr.is_varargs()); lst->cons_set_element(2, &val); /* return the list */ retval_obj(vmg_ lst_obj); } /* ------------------------------------------------------------------------ */ /* * sprintf */ /* bufprintf format string pointer */ struct bpptr { bpptr(const char *p, size_t len) { this->p = p; this->len = len; } bpptr(bpptr &src) { set(src); } /* set from another pointer */ void set(bpptr &src) { this->p = src.p; this->len = src.len; } /* do we have more in our buffer? */ int more() const { return len != 0; } /* get the current character */ char getch() const { return len != 0 ? *p : '\0'; } /* get and skip the current character */ char skipch() { if (len != 0) { char ch = *p; ++p, --len; return ch; } else return '\0'; } /* * match a character: if we match, return true and skip the character, * otherwise just return false */ int match_skip(char ch) { if (getch() == ch) { inc(); return TRUE; } else return FALSE; } /* skip the current character */ void inc() { if (len != 0) ++p, --len; } /* get the next wide character */ wchar_t getwch() const { return len != 0 ? utf8_ptr::s_getch(p) : 0; } /* get and skip the next wide character */ wchar_t skipwch() { wchar_t ch = getwch(); incwch(); return ch; } /* skip the next wide character */ void incwch() { if (len != 0) { size_t clen = utf8_ptr::s_charsize(*p); if (clen > len) clen = len; p += clen; len -= clen; } } /* parse an integer value */ int atoi() { int acc = 0; while (is_digit(getch())) { acc *= 10; acc += value_of_digit(skipch()); } return acc; } /* parse an integer value; return -1 if there's no integer here */ int check_atoi() { return is_digit(getch()) ? atoi() : -1; } /* current string pointer */ const char *p; /* remaining length in bytes */ size_t len; }; /* format_int() flags */ #define FI_UNSIGNED 0x0001 #define FI_CAPS 0x0002 /* '%' formatting options */ struct fmtopts { fmtopts() { sign = '\0'; group = 0; prec = -1; width = -1; left_align = FALSE; pad = ' '; pound = FALSE; } /* * sign character to show for positive numbers: '\0' to show nothing, ' * ' to show a space, '+' to show a plus */ char sign; /* group charater, or '\0' if not grouping */ wchar_t group; /* width (%10d); -1 means no width spec */ int width; /* precision (%.10s); -1 means no precision spec */ int prec; /* true -> left align the value in its field; false -> right align */ int left_align; /* padding character; default is space */ wchar_t pad; /* * '#' flag: e E f g G -> always use a decimal point; g G -> keep * trailing zeros; x X o -> use 0x/0X/0 prefix for non-zero values */ int pound; }; /* bufprintf output writer */ struct bpwriter { bpwriter(VMG_ CVmObjString *str) { this->vmg = VMGLOB_ADDR; this->str = str; this->dst = str->cons_get_buf(); } /* close - set the final string length */ void close() { VMGLOB_PTR(vmg); str->cons_shrink_buffer(vmg_ dst); } /* write a character */ void putch(char ch) { VMGLOB_PTR(vmg); dst = str->cons_append(vmg_ dst, &ch, 1, 64); } /* write a UTF8 character */ void putwch(wchar_t ch) { VMGLOB_PTR(vmg); dst = str->cons_append(vmg_ dst, ch, 64); } /* write a UTF8 character multiple times */ void putwch(wchar_t ch, int cnt) { while (cnt-- != 0) putwch(ch); } /* write a string */ void puts(const char *str) { puts(str, strlen(str)); } void puts(const char *str, size_t len) { VMGLOB_PTR(vmg); dst = this->str->cons_append(vmg_ dst, str, len, 64); } /* format a string value */ void format_string(VMG_ const vm_val_t *val, const fmtopts &opts) { /* if we don't have a string value, cast it to string */ vm_val_t strval; val->cast_to_string(vmg_ &strval); G_stk->push(&strval); /* get the string buffer and length */ const char *str = strval.get_as_string(vmg0_); size_t bytes = vmb_get_len(str); str += VMB_LEN; /* get the length of the string in characters */ utf8_ptr p((char *)str); size_t chars = p.len(bytes); /* If the string is longer than the precision, truncate it */ if (opts.prec >= 0 && (int)chars > opts.prec) { /* limit the length (in both chars and bytes) to the precision */ chars = opts.prec; bytes = p.bytelen(chars); } /* write it out with the appropriate padding and alignment */ format_with_padding(vmg_ str, bytes, chars, opts); /* discard our gc protection */ G_stk->discard(); } /* format a character value */ void format_char(VMG_ const vm_val_t *val) { /* check what we have */ const char *str = val->get_as_string(vmg0_); if (str != 0) { /* it's a string - show the first character */ size_t len = vmb_get_len(str); str += VMB_LEN; if (len != 0) { /* write the first character */ putwch(utf8_ptr::s_getch(str)); } else { /* empty string - write a null character */ putch((char)0); } } else { /* it's not a string - get the integer value */ int i = val->cast_to_int(vmg0_); /* make sure it's in range */ if (i < 0 || i > 65535) err_throw(VMERR_NUM_OVERFLOW); /* write the character value */ putwch((wchar_t)i); } } /* format an internal UTF-8 length+string, with padding and alignment */ void format_with_padding(VMG_ const char *str, const fmtopts &opts) { /* figure the byte length and get the buffer */ size_t bytes = vmb_get_len(str); str += VMB_LEN; /* figure the character length */ utf8_ptr p((char *)str); size_t chars = p.len(bytes); /* write it out */ format_with_padding(vmg_ str, bytes, chars, opts); } /* format a UTF-8 string value, adding padding and alignment */ void format_with_padding(VMG_ const char *str, size_t bytes, size_t chars, const fmtopts &opts) { /* * Figure the amount of padding: if the width is larger than the * string length (in characters), pad by the difference. */ int npad = (opts.width > (int)chars ? opts.width - chars : 0); /* if right-aligning, write the padding */ if (!opts.left_align) putwch(opts.pad, npad); /* write the string */ puts(str, bytes); /* if left-aligning, write the padding */ if (opts.left_align) putwch(opts.pad, npad); } /* format an integer value */ void format_int(VMG_ const vm_val_t *val, int radix, const char *type_prefix, const fmtopts &opts, int flags = 0) { /* a stack buffer for conversions that can use it */ char buf[256]; /* check the type */ switch (val->typ) { case VM_NIL: /* format nil as zero */ format_int(vmg_ "0", 1, type_prefix, opts, flags); break; case VM_TRUE: /* format true as one */ format_int(vmg_ "1", 1, type_prefix, opts, flags); break; case VM_INT: { /* set flags - round to integer, unsigned if applicable */ int cvtflags = TOSTR_ROUND; if ((flags & FI_UNSIGNED) != 0) cvtflags |= TOSTR_UNSIGNED; /* get the basic string representation of the integer */ const char *p = CVmObjString::cvt_int_to_str( buf, sizeof(buf), val->val.intval, radix, cvtflags); /* apply our extra formatting to the basic string rep */ format_int(vmg_ p + VMB_LEN, vmb_get_len(p), type_prefix, opts, flags); } break; default: { /* * figure the string conversion flags: round to integer, * and use an unsigned interpretation if the format is * unsigned */ int tsflags = TOSTR_ROUND; if (flags & FI_UNSIGNED) tsflags |= TOSTR_UNSIGNED; /* cast the value to a numeric type */ vm_val_t num; val->cast_to_num(vmg_ &num); G_stk->push(&num); /* ...thence to string, to get a printable representation */ vm_val_t str; const char *p = CVmObjString::cvt_to_str( vmg_ &str, buf, sizeof(buf), &num, radix, tsflags); G_stk->push(&str); /* apply our extra formatting to the basic string rep */ format_int(vmg_ p + VMB_LEN, vmb_get_len(p), type_prefix, opts, flags); /* discard the GC protection */ G_stk->discard(2); } break; } } /* * Format an integer value for which we've generated a basic string * buffer value. This applies our extra formatting - padding, plus * sign, alignment, case conversion. */ void format_int(VMG_ const char *p, size_t len, const char *type_prefix, const fmtopts &opts, int flags) { /* if they're trying to pawn off an empty string on us, use "0" */ if (p == 0 || len == 0) p = "0", len = 1; /* note if the value is all zeros */ int zero = TRUE; const char *p2 = p; for (size_t i = 0 ; i < len ; ++i) { if (*p2 != '0') { zero = FALSE; break; } } /* * get the number of digits: assume that the whole thing is digits * except for a leading minus sign */ int digits = len; if (*p == '-') --digits; /* * Figure the display width required. Start with the length of the * string. If we the "sign" option is set and we don't have a "-" * sign, add space for a "+" sign. If the "group" option is set, * add a comma for each group of three digits. If the '#' flag is * set, add the type prefix if the value is nonzero. */ int dispwid = len; if (opts.sign != '\0' && *p != '-') dispwid += 1; if (opts.group != 0) dispwid += ((len - (*p == '-' ? 1 : 0)) - 1)/3; if (opts.pound && type_prefix != 0 && !zero) dispwid += strlen(type_prefix); /* * If there's a precision setting, it means that we're to add * leading zeros to bring the number of digits up to the * precision. */ if (digits < opts.prec) dispwid += opts.prec - digits; /* * Figure the amount of padding. If there's an explicit width spec * in the options, and the display width is less than the width * spec, pad by the differene. */ int padcnt = (opts.width > dispwid ? opts.width - dispwid : 0); /* if they want right alignment, add padding characters before */ if (!opts.left_align) putwch(opts.pad, padcnt); /* add the + sign if needed */ if (opts.sign && *p != '-') putch(opts.sign); /* if there's a '-' sign, write it */ if (*p == '-') putch(*p++), --len; /* add the type prefix */ if (opts.pound && type_prefix != 0 && !zero) puts(type_prefix); /* add the leading zeros for the precision */ for (int i = digits ; i < opts.prec ; ++i) putch('0'); /* write the digits, adding grouping commas and converting case */ for (int dig = 0 ; len != 0 ; ++p, --len, ++dig) { /* * if this isn't the first digit, and we have a multiple of * three digits remaining, and we're using grouping, add the * group comma */ if (opts.group != 0 && dig != 0 && len % 3 == 0) putwch(opts.group); /* write this character, converting case as needed */ if (is_digit(*p)) putch(*p); else if ((flags & FI_CAPS) != 0) putch((char)toupper(*p)); else putch((char)tolower(*p)); } /* if they want left alignment, add padding characters after */ if (opts.left_align) putwch(opts.pad, padcnt); } /* format a Roman numeral */ void format_roman(VMG_ const vm_val_t *val, const fmtopts &opts, int flags) { char buf[40]; /* get the integer value */ int32_t i = val->cast_to_int(vmg0_); /* * use Roman numerals if it's in the range 1-4999, otherwise just * treat it like '%d' */ if (i >= 1 && i <= 4999) { /* Roman numeral conversion chart */ static const struct { const char *numeral; int val; } r[] = { { "m", 1000 }, { "cm", 900 }, { "d", 500 }, { "cd", 400 }, { "c", 100 }, { "xc", 90 }, { "l", 50 }, { "x", 10 }, { "ix", 9 }, { "v", 5 }, { "iv", 4 }, { "i", 1 } }; /* * convert by repeatedly appending the highest-value Roman * numeral less than or equal to the number, deducting each * Roman numeral value from the remaining number balance until * we reach zero */ for (int ri = 0 ; i != 0 && ri < countof(r) ; ) { /* if this one fits, append this Roman numeral */ if (r[ri].val <= i) { /* append this numeral, converting case if needed */ for (const char *p = r[ri].numeral ; *p != 0 ; ++p) { if ((flags & FI_CAPS) != 0) putch((char)toupper(*p)); else putch(*p); } /* deduct its value from the remaining balance */ i -= r[ri].val; } else { /* this numeral is too large - move on to the next one */ ++ri; } } } else { t3sprintf(buf, sizeof(buf), "%ld", (long)i); format_int(vmg_ buf, strlen(buf), 0, opts, 0); } } /* format a floating-point value */ void format_float(VMG_ const vm_val_t *val, char type_spec, const fmtopts &opts) { /* a stack buffer for conversions that can use it */ char buf[256]; /* * Use the precision specified, with a default of 6 digits; assume * that there's no maximum number of digits. */ int prec = (opts.prec >= 0 ? opts.prec : 6); int maxdigs = -1; /* figure the formatter flags */ ulong flags = VMBN_FORMAT_LEADING_ZERO; /* if the sign option is set, always show a sign */ if (opts.sign == '+') flags |= VMBN_FORMAT_SIGN; else if (opts.sign == ' ') flags |= VMBN_FORMAT_POS_SPACE; /* * if the '#' flag is set, always show a decimal point, and keep * trailing zeros with 'g' and 'G' */ if (opts.pound) { flags |= VMBN_FORMAT_POINT; if (type_spec == 'g' || type_spec == 'G') flags |= VMBN_FORMAT_TRAILING_ZEROS; } /* for 'E' or 'G', use capital 'E' for the exponent indicator */ if (type_spec == 'E' || type_spec == 'G') flags |= VMBN_FORMAT_EXP_CAP; /* * for 'e' or 'E', always use scientific notation, with a sign * symbol in the exponent value */ if (type_spec == 'e' || type_spec == 'E') flags |= VMBN_FORMAT_EXP | VMBN_FORMAT_EXP_SIGN; /* if the type code is 'g' or 'G', use "compact" notation */ if (type_spec == 'g' || type_spec == 'G') { /* set the compact notation flag */ flags |= VMBN_FORMAT_COMPACT | VMBN_FORMAT_EXP_SIGN; /* the precision is the number of significant digits to show */ flags |= VMBN_FORMAT_MAXSIG; maxdigs = prec; prec = -1; } /* cast the value to a numeric type */ vm_val_t num; val->cast_to_num(vmg_ &num); G_stk->push(&num); /* check the type */ switch (num.typ) { case VM_INT: { /* format the integer as though it were a float */ const char *p = CVmObjBigNum::cvt_int_to_string_buf( buf, sizeof(buf), num.val.intval, maxdigs, -1, prec, 3, flags); /* write it out, adding padding and alignment */ format_with_padding(vmg_ p, opts); } break; case VM_OBJ: /* if it's an object, it should be a BigNumber */ if (CVmObjBigNum::is_bignum_obj(vmg_ num.val.obj)) { /* get the BigNumber object */ CVmObjBigNum *bn = (CVmObjBigNum *)vm_objp(vmg_ num.val.obj); /* format the BigNumber */ vm_val_t str; const char *p = bn->cvt_to_string_buf( vmg_ &str, buf, sizeof(buf), maxdigs, -1, prec, 3, flags); G_stk->push(&str); /* write it out, adding padding and alignment */ format_with_padding(vmg_ p, opts); /* discard gc protection */ G_stk->discard(); } break; default: break; } /* discard our gc protection */ G_stk->discard(); } /* result string */ CVmObjString *str; /* current write pointer */ char *dst; /* VM globals */ vm_globals *vmg; }; /* * Internal sprintf formatter. The arguments are on the stack in the usual * function call order, with the first argument at top of stack. We'll * allocate a new String object to hold the result. */ static void tsprintf(VMG_ vm_val_t *retval, const char *fmtp, size_t fmtl, int arg0, int argc) { /* set up a format string reader */ bpptr fmt(fmtp, fmtl); /* * Create a string to hold the result. Use 150% the length of the * format string as a guess, with a minimum of 64 characters; we'll * expand this as needed as we go. */ size_t init_len = (fmtl < 43 ? 64 : fmtl*3/2); retval->set_obj(CVmObjString::create(vmg_ FALSE, init_len)); /* push it for gc protection */ G_stk->push(retval); /* adjust the argument base for our additions */ arg0 += 1; /* set up an output writer */ bpwriter dst(vmg_ (CVmObjString *)vm_objp(vmg_ retval->val.obj)); /* set up a nil value for missing arguments */ vm_val_t nil_val; nil_val.set_nil(); /* * Scan the format string. The string is in utf8 format as always, but * our format codes are all plain ASCII characters, so a simple byte * scan is perfectly adequate. */ for (int argpos = 1 ; fmt.more() ; ) { /* check for format codes */ char ch = fmt.skipch(); if (ch == '%') { /* format specifier - remember where the '%' was */ const char *pct = fmt.p - 1; /* set up our initial default options */ fmtopts opts; int argno = -1; /* parse flags until we're out of them */ for (int flags_done = FALSE ; !flags_done ; ) { /* check the next character */ switch (fmt.getch()) { case '[': /* argument number specifier - "[digits]" */ fmt.inc(); argno = fmt.atoi(); fmt.match_skip(']'); break; case '+': /* note the sign specifier */ opts.sign = '+'; fmt.inc(); break; case ' ': /* note the blank-for-plus specifier */ opts.sign = ' '; fmt.inc(); break; case ',': /* note the group specifier */ opts.group = ','; fmt.inc(); break; case '-': /* note the left-alignment specifier */ opts.left_align = TRUE; fmt.inc(); break; case '_': /* padding spec - the next charater is the pad char */ fmt.inc(); opts.pad = fmt.skipwch(); break; case '#': /* pound flag - special flag per type */ opts.pound = TRUE; fmt.inc(); break; default: /* it's not an option flag, so we're done with flags */ flags_done = TRUE; break; } } /* * Next comes the width, but there's one more flag character * that has a special position just before the width: '0', to * specify leading zero padding. */ if (fmt.match_skip('0')) { /* obey this only if we're in right-align mode */ if (!opts.left_align) opts.pad = '0'; } /* check for a width specifier */ opts.width = fmt.check_atoi(); /* check for a precision specifier */ if (fmt.match_skip('.')) opts.prec = fmt.atoi(); /* we're at the type specifier */ char type_spec = fmt.skipch(); /* presume we'll use an argument */ int used_arg = TRUE; /* retrieve the current argument value */ int argi = (argno > 0 ? argno : argpos); const vm_val_t *val = (argi <= argc ? G_stk->get(arg0 + argi - 1) : &nil_val); /* apply the substitution based on the type */ switch (type_spec) { case '%': /* literal % - copy it */ dst.putch('%'); /* this doesn't use an argument */ used_arg = FALSE; break; case 'b': /* number -> binary integer */ dst.format_int(vmg_ val, 2, 0, opts, FI_UNSIGNED); break; case 'c': /* number -> Unicode character */ dst.format_char(vmg_ val); break; case 'd': /* number -> decimal integer */ dst.format_int(vmg_ val, 10, 0, opts); break; case 'r': /* number -> roman numeral (lowercase) */ dst.format_roman(vmg_ val, opts, 0); break; case 'R': /* number -> roman numeral (uppercase) */ dst.format_roman(vmg_ val, opts, FI_CAPS); break; case 'u': /* number -> decimal integer, unsigned interpretation */ dst.format_int(vmg_ val, 10, 0, opts, FI_UNSIGNED); break; case 'e': case 'E': case 'f': case 'g': case 'G': /* number -> floating point */ dst.format_float(vmg_ val, type_spec, opts); break; case 'o': /* number -> octal integer */ dst.format_int(vmg_ val, 8, "0", opts, FI_UNSIGNED); break; case 's': /* string */ dst.format_string(vmg_ val, opts); break; case 'x': /* number -> hex integer, lowercase letters */ dst.format_int(vmg_ val, 16, "0x", opts, FI_UNSIGNED); break; case 'X': /* number -> hex integer, uppercase letters */ dst.format_int(vmg_ val, 16, "0X", opts, FI_UNSIGNED | FI_CAPS); break; default: /* anything else is invalid - just copy the source % string */ dst.puts(pct, fmt.p - pct); /* we didn't use an argument after all */ used_arg = FALSE; break; } /* if we used a positional argument, count it */ if (used_arg && argno < 0) ++argpos; } else { /* just copy this byte verbatim */ dst.putch(ch); } } /* set the final result string length */ dst.close(); /* done with our gc protection */ G_stk->discard(); } /* * sprintf */ void CVmBifTADS::sprintf(VMG_ uint argc) { /* we need at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the format string */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and string buffer */ size_t fmtl = vmb_get_len(fmt); fmt += VMB_LEN; /* do the sprintf */ vm_val_t retv; tsprintf(vmg_ &retv, fmt, fmtl, 1, argc - 1); /* return the new string */ retval(vmg_ &retv); /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * abs() - absolute value */ void CVmBifTADS::get_abs(VMG_ uint argc) { /* we need exactly one argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* check the argument type */ switch (G_stk->get(0)->typ) { case VM_INT: /* integer */ { /* get the integer */ int32_t i = G_stk->get(0)->val.intval; /* if it's negative, negate it */ if (i < 0) i = -i; /* return the result */ retval_int(vmg_ i); } break; case VM_OBJ: /* object */ { /* try a BigNumber cast */ CVmObjBigNum *b = vm_val_cast(CVmObjBigNum, G_stk->get(0)); if (b != 0) { /* get the absolute value of the BigNumber */ b->abs_val(vmg_ G_interpreter->get_r0(), G_stk->get(0)->val.obj); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * sgn() - sign */ void CVmBifTADS::get_sgn(VMG_ uint argc) { /* we need exactly one argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* check the argument type */ switch (G_stk->get(0)->typ) { case VM_INT: /* integer */ { /* get the integer */ int32_t i = G_stk->get(0)->val.intval; /* compute the sgn value */ retval_int(vmg_ i < 0 ? -1 : i == 0 ? 0 : 1); } break; case VM_OBJ: /* object */ { /* try a BigNumber cast */ CVmObjBigNum *b = vm_val_cast(CVmObjBigNum, G_stk->get(0)); if (b != 0) { /* compute the sgn value */ b->sgn_val(vmg_ G_interpreter->get_r0(), G_stk->get(0)->val.obj); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); } /* ------------------------------------------------------------------------ */ /* * concat() */ void CVmBifTADS::concat(VMG_ uint argc) { /* * First, run through the arguments. Convert each one that's not * already a string to a string, and count up the lengths. */ size_t len = 0; for (uint i = 0 ; i < argc ; ++i) { /* cast this value to a string */ vm_val_t newval; const char *str = G_stk->get(i)->cast_to_string(vmg_ &newval); /* add its length */ len += vmb_get_len(str); /* replace it in the stack, in case we did a conversion */ *G_stk->get(i) = newval; } /* allocate the result string */ vm_obj_id_t retobj = CVmObjString::create(vmg_ FALSE, len); CVmObjString *retstr = vm_objid_cast(CVmObjString, retobj); /* get its construction buffer */ char *dst = retstr->cons_get_buf(); /* run through the arguments again, copying them into the result buffer */ for (uint i = 0 ; i < argc ; ++i) { /* get this value as a string (it's already been cast) */ const char *src = G_stk->get(i)->get_as_string(vmg0_); /* parse and skip the length prefix */ size_t len = vmb_get_len(src); src += VMB_LEN; /* add the string to the output buffer */ memcpy(dst, src, len); dst += len; } /* return the result string */ retval_obj(vmg_ retobj); /* discard arguments */ G_stk->discard(argc); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/resload.cpp��������������������������������������������������������������������0000644�0001750�0000144�00000005747�11704410271�015671� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/resload.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resload.cpp - resource loader Function Notes Modified 10/17/98 MJRoberts - Creation */ #include <stddef.h> #include <string.h> #include "t3std.h" #include "resload.h" /* ------------------------------------------------------------------------ */ /* * create resource loader */ CResLoader::CResLoader() { /* we have no root directory */ root_dir_ = 0; /* no executable path yet */ exe_filename_ = 0; } /* * create a resource loader given a root directory */ CResLoader::CResLoader(const char *root_dir) { /* remember the root directory for file searches */ root_dir_ = lib_copy_str(root_dir); /* no executable path yet */ exe_filename_ = 0; } /* * delete resource loader */ CResLoader::~CResLoader() { /* free our root directory string and executable path string */ lib_free_str(root_dir_); lib_free_str(exe_filename_); } /* * Open a resource file given the resource path. */ osfildef *CResLoader::open_res_file(const char *respath, const char *deflib, const char *exerestype) { char filepath[OSFNMAX]; osfildef *fp; /* * Look for the resource as an external file. If we have a root * directory, look for the file under that directory; otherwise, * look for it in the current directory. */ if (root_dir_ != 0) { /* get the resource name as a file path */ char fname[OSFNMAX]; os_cvt_url_dir(fname, sizeof(fname), respath); /* build a full path from the root directory and the resource path */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); } else { /* get the resource name as a file path */ os_cvt_url_dir(filepath, sizeof(filepath), respath); } /* try opening the file */ fp = osfoprb(filepath, OSFTBIN); /* if we didn't find it, try looking in the default library */ if (fp == 0 && deflib != 0) { char fname[OSFNMAX]; /* convert from URL notation to local path conventions */ os_cvt_url_dir(fname, sizeof(fname), deflib); /* build the full path, starting in the root resource directory */ os_build_full_path(filepath, sizeof(filepath), root_dir_, fname); /* add the default resource library extension */ os_defext(filepath, "t3r"); /* try opening the file */ fp = open_lib_res(filepath, respath); } /* if we still didn't find it, try looking in the executable */ if (fp == 0 && exerestype != 0) fp = open_exe_res(respath, exerestype); /* return the result */ return fp; } �������������������������frobtads-1.2.3/tads3/t3std.h������������������������������������������������������������������������0000644�0001750�0000144�00000104277�12016417744�014755� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name t3std.h - standard definitions Function Various standard definitions Notes None Modified 10/17/98 MJRoberts - creation (from TADS 2 lib.h) */ #ifndef T3_STD_INCLUDED #define T3_STD_INCLUDED #include <stddef.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <ctype.h> #include <limits.h> #include "os.h" /* ------------------------------------------------------------------------ */ /* * err_throw() Return Handling * * Some compilers (such as MSVC 2007) are capable of doing global * optimizations that can detect functions that never return. err_throw() * is one such function: it uses longjmp() to jump out, so it never returns * to its caller. * * Most compilers can't detect this automatically, and C++ doesn't have a * standard way to declare a function that never returns. So on most * compilers, the compiler will assume that err_throw() returns, and thus * will generate a warning if err_throw() isn't followed by some proper * control flow statement. For example, in a function with a return value, * a code branch containing an err_throw() would still need a 'return * <val>' statement - without such a statement, the compiler would generate * an error about a branch without a value return. * * This creates a porting dilemma. On compilers that can detect that * err_throw() never returns, the presence of any statement in a code * branch after an err_throw() will cause an "unreachable code" error. For * all other compilers, the *absence* of such code will often cause a * different error ("missing return", etc). * * The only way I can see to deal with this is to use a compile-time * #define to select which type of compiler we're using, and use this to * insert or delete the proper dummy control flow statement after an * err_throw(). So: * * --- INSTRUCTIONS TO BASE CODE DEVELOPERS --- * * - after each err_throw() call, if the code branch needs some kind of * explicit termination (such as a "return val;" statement), code it with * the AFTER_ERR_THROW() macro. Since err_throw() never *actually* * returns, these will be dummy statements that will never be reached, but * the compiler might require their presence anyway because it doesn't know * better. * * --- INSTRUCTIONS TO PORTERS --- * * - if your compiler CAN detect that err_throw() never returns, define * COMPILER_DETECTS_THROW_NORETURN in your compiler command-line options; * * - otherwise, leave the symbol undefined. */ #ifdef COMPILER_DETECTS_THROW_NORETURN #define COMPILER_DETECTS_THROW_NORETURN_IN_VMERR #define AFTER_ERR_THROW(code) #else #define AFTER_ERR_THROW(code) code #endif /* * And the same idea as above, but for the special case where the compiler * detects this within the vmerr.cpp module (for static functions in the * same module), but not across modules. */ #ifdef COMPILER_DETECTS_THROW_NORETURN_IN_VMERR #define VMERR_AFTER_ERR_THROW(code) #else #define VMERR_AFTER_ERR_THROW(code) code #endif /* * os_term() return handling. This is similar to the longjmp() issue * above. Some compilers perform global optimizations that detect that * exit(), and functions that unconditionally call exit(), such as * os_term(), do not return. Since these compilers know that anything * following a call to exit() is unreachable, some will complain about if * anything follows an exit(). Compilers that *don't* do such * optimizations will complain if there *isn't* a 'return' after an exit() * if the containing function requires a return value, since as far as * they're concerned the function is falling off the end without returning * a value. So there's no solution at the C++ level; it's a compiler * variation that we have to address per compiler. If you get 'unreachable * code' errors in the generic code after calls to os_term(), define this * macro. Most platforms can leave it undefined. */ #ifdef COMPILER_DETECTS_OS_TERM_NORETURN #define AFTER_OS_TERM(code) #else #define AFTER_OS_TERM(code) code #endif /* * Some compilers (notably gcc >= 4.7.1) require that overloads for the * basic 'new' and 'delete' operators be declared with 'throw' clauses to * match the standard C++ library (that is, 'new' must be declared to throw * std::bad_alloc, and 'delete' must be declared to throw nothing). * Naturally, some other compilers (notably MSVC 2003) don't want the * 'throw' clauses. If your compiler wants the 'throw' declarations, * define NEW_DELETE_NEED_THROW in your makefile, otherwise omit it. Note * that some compilers (notably gcc < 4.7.1) don't care one way or the * other, so if your compiler doesn't complain, you probably don't need to * worry about this setting. */ #ifndef SYSTHROW #ifdef NEW_DELETE_NEED_THROW #define SYSTHROW(exc) exc #else #define SYSTHROW(exc) #endif #endif /* ------------------------------------------------------------------------ */ /* * T3 OS interface extensions. These are portable interfaces to * OS-dependent functionality, along the same lines as the functions * defined in tads2/osifc.h but specific to TADS 3. */ /* * Initialize the UI after loading the image file. The T3 image loader * calls this after it's finished loading a .t3 image file, but before * executing any code in the new game. This lets the local platform UI * perform any extra initialization that depends on inspecting the contents * of the loaded game. * * This isn't required to do anything; a valid implementation is an empty * stub routine. The purpose of this routine is to give the UI a chance to * customize the UI according to the details of the loaded game, before the * game starts running. In particular, the UI configuration might vary * according to which intrinsic classes or function sets are linked by the * game. For example, one set of windows might be used for a traditional * console game, while another is used for a Web UI game, the latter being * recognizable by linking the tads-net function set. */ void os_init_ui_after_load(class CVmBifTable *bif_table, class CVmMetaTable *meta_table); /* ------------------------------------------------------------------------ */ /* * Memory debugging */ /* in debug builds, we override operator new, which requires including <new> */ #ifdef T3_DEBUG #include <new> #endif /* for Windows debug builds, add stack trace info to allocation blocks */ #if defined(T3_DEBUG) && defined(T_WIN32) # define OS_MEM_PREFIX \ struct { \ DWORD return_addr; \ } stk[6]; void os_mem_prefix_set(struct mem_prefix_t *mem); # define OS_MEM_PREFIX_FMT ", return=(%lx, %lx, %lx, %lx, %lx, %lx)" # define OS_MEM_PREFIX_FMT_VARS(mem) \ , (mem)->stk[0].return_addr, \ (mem)->stk[1].return_addr, \ (mem)->stk[2].return_addr, \ (mem)->stk[3].return_addr, \ (mem)->stk[4].return_addr, \ (mem)->stk[5].return_addr #endif /* provide empty default definitions for system memory header add-ons */ #ifndef OS_MEM_PREFIX # define OS_MEM_PREFIX # define os_mem_prefix_set(mem) # define OS_MEM_PREFIX_FMT # define OS_MEM_PREFIX_FMT_VARS(mem) #endif /* ------------------------------------------------------------------------ */ /* * Types */ /* short-hand for various types */ #ifndef OS_UCHAR_DEFINED typedef unsigned char uchar; #endif #ifndef OS_USHORT_DEFINED typedef unsigned short ushort; #endif #ifndef OS_UINT_DEFINED typedef unsigned int uint; #endif #ifndef OS_ULONG_DEFINED typedef unsigned long ulong; #endif /* sizeof() extension macros */ #ifndef countof #define countof(array) (sizeof(array)/sizeof((array)[0])) #endif #define sizeof_field(struct_name, field) sizeof(((struct_name *)0)->field) /* * The types int16_t, uint16_t, int32_t, and uint32_t are defined by ANSI * C99 as EXACTLY the specified number of bits (e.g., int16 is a signed * 16-bit integer; uint32 is an unsigned 32-bit integer). * * Many modern compilers provide definitions for these types in <stdint.h>. * When <stdint.h> isn't available, ports must provide suitable typedefs in * the osxxx.h header - see tads2/osifc.h. * * Because these types have exact sizes, their limits are fixed, so we can * provide portable definitions here. */ #define INT16MAXVAL 32767 #define INT16MINVAL (-32768) #define UINT16MAXVAL 65535 #define INT32MAXVAL 2147483647L #define INT32MINVAL (-2147483647L-1) #define UINT32MAXVAL 4294967295U /* * Text character. We use ASCII and UTF-8 for most character string * representations, so our basic character type is 'char', which is defined * univerally as one byte. (UTF-8 uses varying numbers of bytes per * character, but its basic storage unit is bytes.) */ typedef char textchar_t; /* ------------------------------------------------------------------------ */ /* * Logical and Arithmetic right shifts. C99 deliberately leaves it up to * the implementation to define what happens to the high bits for a right * shift of a negative signed integer: they could be filled with 0s or 1s, * depending on the implementation. C99 is clear on the result for * unsigned values (the high bits are filled with 0s), however it's still * possible that we'll encounter an older implementation that doesn't * conform. * * There's one case where we care about ASHR vs RSHR being well defined, * and that's the VM's implementation of the OPC_LSHR and OPC_ASHR * instructions. We want these to be predictable on all platforms - we * insist on breaking the cycle of ambiguity; we don't want to pass along * this headache to our own users writing TADS programs. * * Nearly all modern compilers treat right shifts of signed operands as * arithmetic, and right shifts of unsigned operands as logical. So our * default implementation will take advantage of this to produce highly * efficient code. For platforms where the signed/unsigned behavior * doesn't work this way, we provide portable bit-twiddly versions that * will work, but are somewhat more overhead to implement. * * If you're on a platform that DOES provide the "modern" signed==ASHR / * unsigned==LSHR behavior, you don't have to do anything special - that's * the default. If your platform doesn't use the modern behavior, AND your * compiler's preprocessor does >> calculations the same way as generated * code, you also don't have to do anything, since our #if's below will * detect the situation and generate our portable explicit ASHR/LSHR code. * If your compiler doesn't use the "modern" behavior AND its preprocessor * uses different rules from generated code for >>, then you'll have to * define the preprocessor symbols OS_CUSTOM_ASHR and/or OS_CUSTOM_LSHR in * your makefile. * * You can test that your configuration is correct by compiling and running * the shr.t from the test suite (tads3/test/data). To further test the * detection conditions and custom macros, use test/test_shr.cpp. */ #if defined(OS_CUSTOM_ASHR) || ((-1 >> 1) != -1) /* signed a >> signed b != a ASHR b, so implement with bit masking */ inline int32_t t3_ashr(int32_t a, int32_t b) { int32_t mask = (~0 << (sizeof(int32_t)*CHAR_BIT - b)); return ((a >> b) | ((a & mask) ? mask : 0)); }; #else /* signed a >> signed b == a ASHR b, so we can use >> */ inline int32_t t3_ashr(int32_t a, int32_t b) { return a >> b; } #endif #if defined(OS_CUSTOM_LSHR) || ((ULONG_MAX >> 1UL) != ULONG_MAX/2) /* unsigned a >> unsigned b != a LSHR b, so implement with bit masking */ inline int32_t t3_lshr(int32_t a, int32_t b) { return ((a >> b) & ~(~0 << (sizeof(int32_t)*CHAR_BIT - b))); } #else /* * unsigned a >> unsigned b == a LSHR b, so we can use >>; note that we * explicit mask the left operand to 32 bits in case we're on a 64-bit or * larger platform, as the T3 VM itself is defined as a 32-bit machine */ inline int32_t t3_lshr(int32_t a, int32_t b) { return (int32_t)((uint32_t)a >> (uint32_t)b); } #endif /* ------------------------------------------------------------------------ */ /* * General portable utility macros */ /* clear a struture */ #define CLRSTRUCT(x) memset(&(x), 0, (size_t)sizeof(x)) #define CPSTRUCT(dst,src) memcpy(&(dst), &(src), (size_t)sizeof(dst)) /* TRUE and FALSE */ #ifndef TRUE # define TRUE 1 #endif /* TRUE */ #ifndef FALSE # define FALSE 0 #endif /* FALSE */ /* bitwise operations */ #define bit(va, bt) ((va) & (bt)) #define bis(va, bt) ((va) |= (bt)) #define bic(va, bt) ((va) &= ~(bt)) /* conditionally compile code if debugging is enabled */ #ifdef DEBUG # define IF_DEBUG(x) x #else /* DEBUG */ # define IF_DEBUG(x) #endif /* DEBUG */ /* offset within a structure of a member of the structure */ #ifndef offsetof # define offsetof(s_name, m_name) (size_t)&(((s_name *)0)->m_name) #endif /* offsetof */ /* * Read an unsigned 32-bit value from the portable external-file * representation. This is parallel to osrp4(), but explicitly reads an * unsigned value. The important thing is that we mask the result to 32 * bits, to prevent unwarranted sign extension on architectures with word * sizes greater than 32 bits (at the moment, this basically means 64-bit * machines, but it would apply to any >32-bit architecture). * * NB: as of TADS 3.1, osrp4() *should* be doing this automatically. It * should be reading a 32 bit value and interpreting it as unsigned, doing * any necessary zero extension to larger 'int' sizes. However, we're * keeping this for now to be sure. */ #define t3rp4u(p) ((ulong)(osrp4(p) & 0xFFFFFFFFU)) /* ------------------------------------------------------------------------ */ /* * Allocate space for a null-terminated string and save a copy of the * string */ char *lib_copy_str(const char *str); char *lib_copy_str(const char *str, size_t len); /* * allocate space for a string of a given length; we'll add in space for * a null terminator */ char *lib_alloc_str(size_t len); /* * Free a string previously allocated with lib_copy_str() or * lib_alloc_str() */ void lib_free_str(char *buf); /* ------------------------------------------------------------------------ */ /* * Safe strcpy with an explicit source length. Truncates the string to the * buffer size, and always null-terminates. Note that the source string is * NOT null-terminated - we copy the explicit length given, up to the * output size limit. */ inline void lib_strcpy(char *dst, size_t dstsiz, const char *src, size_t srclen) { if (dstsiz > 0) { size_t copylen = srclen; if (copylen > dstsiz - 1) copylen = dstsiz - 1; memcpy(dst, src, copylen); dst[copylen] = '\0'; } } /* * Safe strcpy - checks the output buffer size and truncates the string if * necessary; always null-terminates the result. */ inline void lib_strcpy(char *dst, size_t dstsiz, const char *src) { lib_strcpy(dst, dstsiz, src, strlen(src)); } /* * Compare two counted-length strings, with or without case sensitivity. */ inline int lib_strcmp(const char *str1, size_t len1, const char *str2, size_t len2) { int bylen = len1 - len2, bymem; if (bylen == 0) return memcmp(str1, str2, len1); else if (bylen < 0) bymem = memcmp(str1, str2, len1); else bymem = memcmp(str1, str2, len2); return (bymem != 0 ? bymem : bylen); } inline int lib_stricmp(const char *str1, size_t len1, const char *str2, size_t len2) { int bylen = len1 - len2, bymem; if (bylen == 0) return memicmp(str1, str2, len1); else if (bylen < 0) bymem = memicmp(str1, str2, len1); else bymem = memicmp(str1, str2, len2); return (bymem != 0 ? bymem : bylen); } /* * Compare a counted-length string to a regular C string, with or without * case sensitivity. */ inline int lib_strcmp(const char *str1, size_t len1, const char *str2) { return lib_strcmp(str1, len1, str2, strlen(str2)); } inline int lib_stricmp(const char *str1, size_t len1, const char *str2) { return lib_stricmp(str1, len1, str2, strlen(str2)); } /* ------------------------------------------------------------------------ */ /* * Limited-length strchr. Searches within the given string for the given * character; stops if we exhaust the length limit 'len' bytes or reach a * null character in the string. */ inline char *lib_strnchr(const char *src, size_t len, int ch) { /* search until we exhaust the length limit or reach a null byte */ for ( ; len != 0 && *src != '\0' ; --len, ++src) { /* if this is the character we're looking for, return the pointer */ if (*src == ch) return (char *)src; } /* didn't find it */ return 0; } /* ------------------------------------------------------------------------ */ /* * Limited-length atoi */ int lib_atoi(const char *str, size_t len); /* * Limited-length atoi, with auto-advance of the string */ int lib_atoi_adv(const char *&str, size_t &len); /* ------------------------------------------------------------------------ */ /* * Compare two strings, ignoring differences in whitespace between the * strings. Returns true if the strings are equal (other than * whitespace, false if not. * * Note that we do not ignore the *presence* of whitespace; we only * ignore differences in the amount of whitespace. For example, "login" * does not equal "log_in" (underscore = whitespace for these examples * only, to emphasize the spacing), because the first lacks whitespace * where the second has it; but "log_in" equals "log___in", because both * strings have whitespace, albeit in different amounts, in the same * place, and are otherwise the same. */ int lib_strequal_collapse_spaces(const char *a, size_t a_len, const char *b, size_t b_len); /* ------------------------------------------------------------------------ */ /* * Utility routine - compare UTF-8 strings with full Unicode case folding. * Returns <0 if a<b, 0 if a==b, >0 if a>b. * * If 'bmatchlen' is non-null, string 'a' can match as a leading substring * of string 'b'. If 'b' is identical to 'a' or contains 'a' as a leading * substring, we'll return 0 to indicate a match, and fill in '*bmatchlen' * with the number of bytes matched. * * If 'bmatchlen' is null, we'll do a straightforward string comparison, so * a 0 return means the two strings match exactly. */ int t3_compare_case_fold( const char *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen); /* compare a UTF-8 string against a wchar_t* string */ int t3_compare_case_fold( const wchar_t *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen); /* * Compare the minimum portions of two UTF-8 strings with case folding. * This compares the folded version of the first character of each string * to the other. If they match, we advance the pointers and lengths past * the matched text and return 0; otherwise we return < 0 if the first * string sorts before the second, > 0 if the first sorts after the second. * * In the simplest case, this matches one character from each string. * However, there are situations where the folded version of one character * can correspond to two characters of source text in the other string, * such as the German ess-zed: this will match "ss" in the source text in * the other string, consuming only one character (the ess-zed) in one * string but two ("ss") in the other. */ int t3_compare_case_fold_min(class utf8_ptr &a, size_t &alen, class utf8_ptr &b, size_t &blen); int t3_compare_case_fold_min(class utf8_ptr &a, size_t &alen, const wchar_t* &b, size_t &blen); int t3_compare_case_fold_min(const wchar_t* &a, size_t &alen, const wchar_t* &b, size_t &blen); /* ------------------------------------------------------------------------ */ /* * Find a version suffix in an identifier string. A version suffix * starts with the given character. If we don't find the character, * we'll return the default version suffix. In any case, we'll set * name_len to the length of the name portion, excluding the version * suffix and its leading separator. * * For example, with a '/' suffix, a versioned name string would look * like "tads-gen/030000" - the name is "tads_gen" and the version is * "030000". */ const char *lib_find_vsn_suffix(const char *name_string, char suffix_char, const char *default_vsn, size_t *name_len); /* ------------------------------------------------------------------------ */ /* * Unicode-compatible character classification functions. These * functions accept any Unicode character, but classify all non-ASCII * characters in the Unicode character set as unknown; hence, * is_digit(ch) will always return false for any non-ASCII character, * even if the character is considered a digit in the Unicode character * set, and to_upper(ch) will return ch for any non-ASCII character, * even if the character has a case conversion defined in the Unicode * set. * * Use the t3_is_xxx() and t3_to_xxx() functions defined vmuni.h for * classifications and conversions that operate over the entire Unicode * character set. */ /* determine if a character is an ASCII character */ inline int is_ascii(wchar_t c) { return (((unsigned int)c) <= 127); } /* determine if a character is an ASCII space */ inline int is_space(wchar_t c) { return (is_ascii(c) && isspace((char)c)); } /* determine if a character is an ASCII alphabetic character */ inline int is_alpha(wchar_t c) { return (is_ascii(c) && isalpha((char)c)); } /* determine if a character is an ASCII numeric character */ inline int is_digit(wchar_t c) { return (is_ascii(c) && isdigit((char)c)); } /* determine if a character is an ASCII octal numeric character */ inline int is_odigit(wchar_t c) { return (is_ascii(c) && isdigit((char)c) && c <= '7'); } /* determine if a character is an ASCII hex numeric character */ inline int is_xdigit(wchar_t c) { return (is_ascii(c) && (isdigit((char)c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))); } /* get the numeric value of a decimal digit character */ inline int value_of_digit(wchar_t c) { return (int)(c - '0'); } /* get the numeric value of an octal numeric character */ inline int value_of_odigit(wchar_t c) { return (int)(c - '0'); } /* get the numeric value of a hex numeric character */ inline int value_of_xdigit(wchar_t c) { /* * since our internal characters are always in unicode, we can take * advantage of the order of unicode characters to reduce the number * of comparisons we must make here */ return (int)(c >= 'a' ? c - 'a' + 10 : c >= 'A' ? c - 'A' + 10 : c - '0'); } /* convert a number 0-15 to a hex digit */ inline char int_to_xdigit(int i) { return ((i >= 0 && i < 10) ? '0' + i : (i >= 10 && i < 16 ) ? 'A' + i - 10 : '?'); } /* convert a byte to a pair of hex digits */ inline void byte_to_xdigits(char *buf, unsigned char b) { buf[0] = int_to_xdigit((b >> 4) & 0x0F); buf[1] = int_to_xdigit(b & 0x0F); } /* determine if a character is a symbol initial character */ inline int is_syminit(wchar_t c) { /* underscores and alphabetic characters can start symbols */ return (is_ascii(c) && (c == '_' || isalpha((char)c))); } /* determine if a character is a symbol non-initial character */ inline int is_sym(wchar_t c) { /* underscores, alphabetics, and digits can be in symbols */ return (is_ascii(c) && (c == '_' || isalpha((char)c) || isdigit((char)c))); } /* determine if a character is ASCII lower-case */ inline int is_lower(wchar_t c) { return (is_ascii(c) && islower((char)c)); } /* convert ASCII lower-case to upper-case */ inline wchar_t to_upper(wchar_t c) { return (is_ascii(c) ? toupper((char)c) : c); } inline wchar_t to_lower(wchar_t c) { return (is_ascii(c) ? tolower((char)c) : c); } /* convert a string to lower case */ void t3strlwr(char *p); /* ------------------------------------------------------------------------ */ /* * sprintf and vsprintf replacements. These versions provide subsets of * the full 'printf' format capabilities, but check for buffer overflow, * which the standard library's sprintf functions do not. * * NB: the 'args' parameter is effectively const, even though it's not * declared as such. That is, you can safely call t3vsprintf multiple * times with the same 'args' parameter without worrying that the contents * will be changed on platforms where va_list is a reference type. (It's * not declared const due to an implementation detail, specifically that * the routine internally needs to make a private copy with va_copy(), * which doesn't accept a const source value.) */ size_t t3sprintf(char *buf, size_t buflen, const char *fmt, ...); size_t t3vsprintf(char *buf, size_t buflen, const char *fmt, va_list args); /* * Automatic memory allocation versions of sprintf and vsprintf: we'll * measure the actual space needed, allocate a buffer, format the message * into the buffer, and return the allocated buffer pointer. The caller is * responsible for freeing the returned buffer via t3free(). */ char *t3sprintf_alloc(const char *fmt, ...); char *t3vsprintf_alloc(const char *fmt, va_list args); /* ------------------------------------------------------------------------ */ /* * Basic heap allocation functions. We don't call malloc and free * directly, but use our own cover functions; when we compile the system * for debugging, we use diagnostic memory allocators so that we can more * easily find memory mismanagement errors (such as leaks, multiple * deletes, and use after deletion). */ #define T3MALLOC_TYPE_MALLOC 1 #define T3MALLOC_TYPE_NEW 2 #define T3MALLOC_TYPE_NEWARR 3 #ifdef T3_DEBUG /* * Compiling in debug mode - use our diagnostic heap functions. * Override C++ operators new, new[], delete, and delete[] as well, so * that we can handle those allocations through our diagnostic heap * manager, too. */ void *t3malloc(size_t siz, int alloc_type); void *t3realloc(void *oldptr, size_t siz); void t3free(void *ptr, int alloc_type); inline void *t3malloc(size_t siz) { return t3malloc(siz, T3MALLOC_TYPE_MALLOC); } inline void *t3mallocnew(size_t siz) { return t3malloc(siz, T3MALLOC_TYPE_NEW); } inline void t3free(void *ptr) { t3free(ptr, T3MALLOC_TYPE_MALLOC); } void *operator new(size_t siz) SYSTHROW(throw (std::bad_alloc)); void *operator new[](size_t siz) SYSTHROW(throw (std::bad_alloc)); void operator delete(void *ptr) SYSTHROW(throw ()); void operator delete[](void *ptr) SYSTHROW(throw ()); /* * List all allocated memory blocks - displays heap information on stdout. * This can be called at program termination to detect un-freed memory * blocks, the existence of which could indicate a memory leak. * * If cb is provided, we'll display output through the given callback * function; otherwise we'll display the output directly on stderr. */ void t3_list_memory_blocks(void (*cb)(const char *msg)); #else /* T3_DEBUG */ /* * Compiling in production mode - use the system memory allocators * directly. Note that we go through the osmalloc() et. al. functions * rather than calling malloc() directly, so that individual ports can * use customized memory management where necessary or desirable. */ #define t3malloc(siz) (::osmalloc(siz)) #define t3mallocnew(siz) (::osmalloc(siz)) #define t3realloc(ptr, siz) (::osrealloc(ptr, siz)) #define t3free(ptr) (::osfree(ptr)) #define t3_list_memory_blocks(cb) #endif /* T3_DEBUG */ /* ------------------------------------------------------------------------ */ /* * A simple array list type. We keep an underlying array of elements, * automatically expanding the underlying array as needed to accomodate new * elements. */ /* array list element type codes */ #define ARRAY_LIST_ELE_INT 1 #define ARRAY_LIST_ELE_LONG 2 #define ARRAY_LIST_ELE_PTR 3 /* array list element - we can store various types here */ union array_list_ele_t { array_list_ele_t(int i) { intval = i; } array_list_ele_t(long l) { longval = l; } array_list_ele_t(void *p) { ptrval = p; } int intval; long longval; void *ptrval; /* compare to a given value for equality */ int equals(array_list_ele_t other, int typ) { return ((typ == ARRAY_LIST_ELE_INT && intval == other.intval) || (typ == ARRAY_LIST_ELE_LONG && longval == other.longval) || (typ == ARRAY_LIST_ELE_PTR && ptrval == other.ptrval)); } }; /* * The array list type */ class CArrayList { public: CArrayList() { /* we have nothing allocated yet */ arr_ = 0; cnt_ = 0; /* use default initial size and increment */ alloc_ = 16; inc_siz_ = 16; } CArrayList(size_t init_cnt, size_t inc_siz) { /* we have nothing allocated yet */ arr_ = 0; cnt_ = 0; /* remember the initial size and increment */ alloc_ = init_cnt; inc_siz_ = inc_siz; } virtual ~CArrayList() { /* delete our underlying array */ free_mem(arr_); } /* get the number of elements in the array */ size_t get_count() const { return cnt_; } /* get the element at the given index (no error checking) */ int get_ele_int(size_t idx) const { return arr_[idx].intval; } long get_ele_long(size_t idx) const { return arr_[idx].longval; } void *get_ele_ptr(size_t idx) const { return arr_[idx].ptrval; } /* find an element's index; returns -1 if not found */ int find_ele(int i) const { return find_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); } int find_ele(long l) const { return find_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); } int find_ele(void *p) const { return find_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); } /* find an element's index; returns -1 if not found */ int find_ele(array_list_ele_t ele, int typ) const { size_t i; array_list_ele_t *p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, return the index */ if (p->equals(ele, typ)) return (int)i; } /* didn't find it */ return -1; } /* add a new element */ void add_ele(int i) { add_ele(array_list_ele_t(i)); } void add_ele(long l) { add_ele(array_list_ele_t(l)); } void add_ele(void *p) { add_ele(array_list_ele_t(p)); } /* add a new element */ void add_ele(array_list_ele_t ele) { /* expand the array if necessary */ if (arr_ == 0) { /* we don't have an array yet, so allocate at the initial size */ init(); } if (cnt_ >= alloc_) { /* allocate at the new size */ arr_ = (array_list_ele_t *) realloc_mem(arr_, alloc_ * sizeof(arr_[0]), (alloc_ + inc_siz_) * sizeof(arr_[0])); /* remember the new size */ alloc_ += inc_siz_; } /* add the new element */ arr_[cnt_++] = ele; } /* remove one element by value; returns true if found, false if not */ void remove_ele(int i) { remove_ele(array_list_ele_t(i), ARRAY_LIST_ELE_INT); } void remove_ele(long l) { remove_ele(array_list_ele_t(l), ARRAY_LIST_ELE_LONG); } void remove_ele(void *p) { remove_ele(array_list_ele_t(p), ARRAY_LIST_ELE_PTR); } /* remove one element by value; returns true if found, false if not */ int remove_ele(array_list_ele_t ele, int typ) { size_t i; array_list_ele_t *p; /* scan for the element */ for (i = 0, p = arr_ ; i < cnt_ ; ++i, ++p) { /* if this is the element, remove it */ if (p->equals(ele, typ)) { /* remove the element at this index */ remove_ele(i); /* indicate that we found the element */ return TRUE; } } /* we didn't find the element */ return FALSE; } /* remove the element at the given index */ void remove_ele(size_t idx) { array_list_ele_t *p; /* move each following element down one slot */ for (p = arr_ + idx, ++idx ; idx < cnt_ ; ++idx, ++p) *p = *(p + 1); /* reduce the in-use count */ --cnt_; } /* clear the entire list */ void clear() { cnt_ = 0; } protected: /* * Initialize. This is called to set up the array at the initial size, * stored in alloc_, when we first need memory. Note that we defer * this until we actually need the memory for two reasons. First, we * can't call it from the constructor, because the vtable won't be * built at construction, and we need to call the virtual alloc_mem(). * Second, by waiting, we ensure that we won't allocate any memory if * our list is never actually needed. */ void init() { /* allocate the array */ arr_ = (array_list_ele_t *)alloc_mem(alloc_ * sizeof(arr_[0])); } /* memory management */ virtual void *alloc_mem(size_t siz) { return t3malloc(siz); } virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz) { return t3realloc(p, newsiz); } virtual void free_mem(void *p) { t3free(p); } /* our array of elements */ array_list_ele_t *arr_; /* number of elements allocated */ size_t alloc_; /* number of elements currently in use */ size_t cnt_; /* increment size */ size_t inc_siz_; }; #endif /* T3_STD_INCLUDED */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/md5.h��������������������������������������������������������������������������0000644�0001750�0000144�00000007542�11546610266�014377� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch <ghost@aladdin.com>. Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke <purschke@bnl.gov>. 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED #include <stdlib.h> /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); /* * E-Z MD5 - calculate the MD5 of a string, returning printable hex. The * 'hash' buffer must be at least 33 characters long. [MJR addition] */ void md5_ez(char *hash, const char *msg, size_t len); /* * DataSource MD5 - calculate the MD5 of bytes from a data source, * returning printable hex. The hash buffer must be at least 33 characters * long. [MJR addition] */ void md5_datasrc(char *hash, class CVmDataSource *src, unsigned long len); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/tcprs.h������������������������������������������������������������������������0000644�0001750�0000144�00000411601�12014212704�015022� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/tcprs.h,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.h - TADS 3 Compiler - parser Function Notes Modified 04/29/99 MJRoberts - Creation */ #ifndef TCPRS_H #define TCPRS_H #include <assert.h> #include "vmtype.h" #include "t3std.h" #include "tcglob.h" #include "tctok.h" #include "tctargty.h" #include "tcprstyp.h" /* ------------------------------------------------------------------------ */ /* * Object ID type */ typedef ulong tc_obj_id; /* * Property ID type */ typedef uint tc_prop_id; /* ------------------------------------------------------------------------ */ /* * scope data structure */ struct tcprs_scope_t { /* local symbol table */ class CTcPrsSymtab *local_symtab; /* enclosing scope's local symbol table */ class CTcPrsSymtab *enclosing_symtab; /* number of locals allocated in scope */ int local_cnt; }; /* ------------------------------------------------------------------------ */ /* * Code body parsing types. Each type of code body is essentially the * same with minor variations, so we use a common code body parser that * checks the parsing type to apply the variations. */ enum tcprs_codebodytype { /* a standard function or method code body */ TCPRS_CB_NORMAL, /* anonymous function */ TCPRS_CB_ANON_FN, /* anonymous method */ TCPRS_CB_ANON_METHOD, /* short-form anonymous function */ TCPRS_CB_SHORT_ANON_FN }; /* ------------------------------------------------------------------------ */ /* * the saved method context is always at index 1 in local variable context * arrays, when we're using local variable context arrays */ #define TCPRS_LOCAL_CTX_METHODCTX 1 /* ------------------------------------------------------------------------ */ /* * Parser */ class CTcParser { public: CTcParser(); ~CTcParser(); /* initialize - call this after the code generator is set up */ void init(); /* * reset - for dynamic code compilation in the interpreter, this resets * internal state for the start of a new compilation */ void reset() { /* forget any old local symbol table */ local_symtab_ = 0; /* clear out all of our lists */ nested_stm_head_ = nested_stm_tail_ = 0; anon_obj_head_ = anon_obj_tail_ = 0; nonsym_obj_head_ = nonsym_obj_tail_ = 0; exp_head_ = exp_tail_ = 0; enclosing_stm_ = 0; dict_cur_ = dict_head_ = dict_tail_ = 0; dict_prop_head_ = 0; gramprod_head_ = gramprod_tail_ = 0; template_head_ = template_tail_ = 0; cur_code_body_ = 0; /* clear out any symbol table references */ local_symtab_ = 0; enclosing_local_symtab_ = 0; goto_symtab_ = 0; } /* * define (def=TRUE) or look up (def=FALSE) a special compiler-defined * property */ class CTcSymProp *def_special_prop(int def, const char *name, tc_prop_id *idp = 0); /* * Set the module information. This tells us the module's name (as * it's given in the makefile (.t3m) or library (.tl) file) and its * sequence number (an ordinal giving its position in the list of * modules making up the overall program build). We use this * information in generating the sourceTextGroup object. */ void set_module_info(const char *name, int seqno); /* * Write an exported symbol file. An exported symbol file * facilitates separate compilation by providing a listing of the * symbols defined in another module. If module A depends on the * symbols from module B, the user can first create an exported * symbol file for module B, then can compile module A in the * presence of B's symbol file, without actually loading B, and * without manually entering a set of external definitions in module * A's source code. */ void write_symbol_file(class CVmFile *fp, class CTcMake *make_obj); /* * Seek to the start of the build configuration information in a symbol * file. The return value is the number of bytes stored in the build * configuration block; on return, the file object will have its seek * offset set to the first byte of the build configuration data. * Returns zero if the symbol file is invalid or does not contain any * configuration data. */ static ulong seek_sym_file_build_config_info(class CVmFile *fp); /* * Write the global table to an object file. */ void write_to_object_file(class CVmFile *fp); /* * Read an object file and load it into the global symbol table. We * will fill in the object and property ID translation tables * provided with the translated values for the object and property * symbols that we find in the object file. * * Returns zero on success; logs error messages and returns non-zero * on error. Note that a non-zero value should be returned only * when the file appears to be corrupted or an I/O error occurs; * errors involving conflicting symbols, or other problems that do * not prevent us from continuing to read the file in an orderly * fashion, should not return failure but should simply log the * error and continue; this way, we can detect any additional symbol * conflicts or other errors. This routine should return failure * only when it is not possible to continue reading the file. */ int load_object_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, tctarg_prop_id_t *prop_xlat, ulong *enum_xlat); /* * Apply internal object/property ID fixups. This traverses the * symbol table and calls each symbol's apply_internal_fixups() * method. This can be called once after loading all object files. */ void apply_internal_fixups(); /* * Read an exported symbol file. Reads the file and loads the * global symbol table with the symbols in the file, with each * symbol marked as external. * * This can be used for separate compilation. If module A depends * on symbols in module B, first create a symbol file for module B, * then module A can be compiled simply be pre-loading B's symbol * file. Any symbol files that a module depends upon must be loaded * before the module is compiled - symbol file loading must precede * parsing. * * If any errors occur, we'll log the errors and return non-zero. * We'll return zero on success. */ int read_symbol_file(class CVmFile *fp); /* get the global symbol table */ class CTcPrsSymtab *get_global_symtab() const { return global_symtab_; } /* set the global symbol table */ class CTcPrsSymtab *set_global_symtab(class CTcPrsSymtab *t) { /* remember the old table */ class CTcPrsSymtab *old = global_symtab_; /* set the new table */ global_symtab_ = t; /* refresh the cache of special internal symbols */ cache_special_props(FALSE); /* return the old symbol table */ return old; } /* get the current local symbol table */ class CTcPrsSymtab *get_local_symtab() const { return local_symtab_; } /* get the 'goto' symbol table */ class CTcPrsSymtab *get_goto_symtab() const { return goto_symtab_; } /* set the current pragma C mode */ void set_pragma_c(int mode); /* turn preprocess expression mode on or off */ void set_pp_expr_mode(int f) { pp_expr_mode_ = f; } /* get the current preprocess expression mode flag */ int get_pp_expr_mode() const { return pp_expr_mode_; } /* set/get the sourceTextGroup mode */ void set_source_text_group_mode(int f); int get_source_text_group_mode() const { return src_group_mode_; } /* get/set the syntax-only mode flag */ int get_syntax_only() const { return syntax_only_; } void set_syntax_only(int f) { syntax_only_ = f; } /* get/set the debugger expression flag */ int is_debug_expr() const { return debug_expr_; } void set_debug_expr(int f) { debug_expr_ = f; } /* * Get the constructor and finalize property ID's - all constructors * and finalizers have these property ID's respectively */ tc_prop_id get_constructor_prop() const { return constructor_prop_; } tc_prop_id get_finalize_prop() const { return finalize_prop_; } /* get the constructor property symbol */ class CTcSymProp *get_constructor_sym() const { return constructor_sym_; } /* get the exported GrammarProd exported */ tc_prop_id get_grammarTag_prop() const; tc_prop_id get_grammarInfo_prop() const; /* * Check for unresolved external symbols. Scans the global symbol * table and logs an error for each unresolved external. Returns * true if any unresolved externals exist, false if not. */ int check_unresolved_externs(); /* * build the dictionaries - scans the global symbol table, and * inserts each object symbol's dictionary words into its * corresponding dictionary */ void build_dictionaries(); /* build the grammar productions */ void build_grammar_productions(); /* * Top-level parser. Parse functions, objects, and other top-level * definitions and declarations. */ class CTPNStmProg *parse_top(); /* * Parse a required semicolon. If the semicolon is present, we'll * simply skip it. If it's missing, we'll log an error and try to * resynchronize. If we find something that looks like it should go * at the end of an expression, we'll try to skip up to the next * semicolon; otherwise, we'll simply stay put. * * Returns zero if the caller should proceed, non-zero if we're at * end of file, in which case there's nothing more for the caller to * parse. */ static int parse_req_sem(); /* * Skip to the next semicolon, ignoring any tokens up to that point. * This can be used when the caller encounters an error that makes * it impossible to process the current statement further, and wants * to find the next semicolon in the hope that it will be a good * place to start again with the next statement. * * Returns zero if the caller should proceed, non-zero if we reach * the end of the file. */ static int skip_to_sem(); /* * Parse an expression. This parses a top-level "comma" expression. */ class CTcPrsNode *parse_expr(); /* * Parse a condition expression. This parses a top-level "comma" * expression, but displays a warning if the outermost operator in * the expression is an assignment, because such expressions are * very frequently meant as comparisons, but the '=' operator was * inadvertantly used instead of '=='. */ class CTcPrsNode *parse_cond_expr(); /* * Parse a value expression or a double-quoted string expression * (including a double-quoted string with embedded expressions). If * allow_comma_expr is true, we'll parse a comma expression; * otherwise, we'll parse an assignment expression. (A comma * expression is broader than an assignment expression, since the * comma separates assignment expressions.) */ class CTcPrsNode *parse_expr_or_dstr(int allow_comma_expr); /* * Parse an assignment expression - this is the next precedence * level down from comma expressions. In certain contexts, a * top-level comma expression is not allowed because a comma has a * separate meaning (in the initializer clause of a 'for' statement, * for example, or in a list element). */ class CTcPrsNode *parse_asi_expr(); /* parse an 'enum' top-level statement */ void parse_enum(int *err); /* parse a 'dictionary' top-level statement */ class CTPNStmTop *parse_dict(int *err); /* parse a 'grammar' top-level statement */ class CTPNStmTop *parse_grammar(int *err, int replace, int modify); /* parse a grammar token list (an alternative list) */ void parse_gram_alts(int *err, class CTcSymObj *gram_obj, class CTcGramProdEntry *prod, struct CTcGramPropArrows *arrows, class CTcGramAltFuncs *funcs); /* parse and flatten a set of grammar rules */ class CTcPrsGramNode *flatten_gram_rule(int *err); /* parse a 'grammar' OR node */ class CTcPrsGramNode *parse_gram_or(int *err, int level); /* parse a 'grammar' CAT node */ class CTcPrsGramNode *parse_gram_cat(int *err, int level); /* parse a 'grammar' qualifier int value */ int parse_gram_qual_int(int *err, const char *qual_name, int *stm_end); /* skip to the end of a mal-formed grammar qualifier */ void parse_gram_qual_skip(int *err, int *stm_end); /* * Parse a 'function' top-level statement. If 'is_extern' is true, * the function is being defined externally, so it should have no * code body defined here (just the prototype). If 'replace' is * true, we're replacing an existing function. * * If 'func_kw_present' is true, the 'function' keyword is present * and must be skipped; otherwise, the function definition elides * the 'function' keyword and starts directly with the function name * symbol. */ class CTPNStmTop *parse_function(int *err, int is_extern, int replace, int modify, int func_kw_present); /* parse an 'intrinsic' top-level statement */ class CTPNStmTop *parse_intrinsic(int *err); /* parse an 'intrinsic class' top-level statement */ class CTPNStmTop *parse_intrinsic_class(int *err); /* parse an 'extern' top-level statement */ void parse_extern(int *err); /* * parse an object or function defintion (this is called when the * first thing in a statement is a symbol; we must check what * follows to determine what type of definition it is) */ class CTPNStmTop *parse_object_or_func(int *err, int replace, int suppress_error, int *suppress_next_error); /* parse a template definition statement */ class CTPNStmTop *parse_template_def(int *err, const class CTcToken *class_tok); /* parse a string template definition statement */ class CTPNStmTop *parse_string_template_def(int *err); /* add a template definition */ void add_template_def(class CTcSymObj *class_sym, class CTcObjTemplateItem *item_head, size_t item_cnt); /* add inherited template definitions */ void add_inherited_templates(class CTcSymObj *sc_sym, class CTcObjTemplateItem *item_head, size_t item_cnt); /* * expand the 'inherited' keyword in a template for the given * superclass template and add the result to the template list for the * class */ void expand_and_add_inherited_template(class CTcSymObj *sc_sym, class CTcObjTemplateItem *items, class CTcObjTemplate *sc_tpl); /* * build a list of superclass templates, for expanding an 'inherited' * token in a template definition */ void build_super_template_list(struct inh_tpl_entry **list_head, struct inh_tpl_entry **list_tail, class CTcSymObj *sc_sym); /* parse an 'object' statement */ class CTPNStmTop *parse_object_stm(int *err, int is_transient); /* * parse an object definition that starts with a '+' string; this * also parses '+ property' statements */ class CTPNStmTop *parse_plus_object(int *err); /* * Parse an object definition. If 'replace' is true, this * definition is to replace a previous definition of the same * object; if 'modify' is true, this definition is to modify a * previous definition. If 'is_class' is true, the definition is * for a class, otherwise it's for a static instance. * * If the definition uses the '+' notation to set the location, * plus_cnt gives the number of '+' signs preceding the object * definition. */ class CTPNStmTop *parse_object(int *err, int replace, int modify, int is_class, int plus_cnt, int is_transient); /* find or define an object symbol */ CTcSymObj *find_or_def_obj(const char *tok_txt, size_t tok_len, int replace, int modify, int *is_class, class CTcSymObj **mod_orig_sym, class CTcSymMetaclass **meta_sym, int *is_transient); /* parse an anonymous object */ class CTPNStmObject *parse_anon_object(int *err, int plus_cnt, int is_nested, struct tcprs_term_info *term_info, int is_transient); /* * Parse an object body. We start parsing from the colon that * introduces the class list, and parse the class list and the * property list for the object. * * If 'is_anon' is true, this is an anonymous object. 'obj_sym' * should be null in this case. * * If 'is_nested' is true, this is a nested object defined in-line in * an object's property list. Note that is_nested implies is_anon, * since nested objects are always anonymous. * * If this is a 'modify' definition, 'mod_orig_tok' should be set up * with the synthesized symbol for the modified base object; * otherwise, 'mod_orig_tok' should be null. * * If 'meta_sym' is non-null, we're modifying an intrinsic class. * This imposes certain restrictions; in particular, we cannot modify * a method defined in the native interface to the class. */ class CTPNStmObject *parse_object_body( int *err, class CTcSymObj *obj_sym, int is_class, int is_anon, int is_grammar, int is_nested, int modify, class CTcSymObj *mod_orig_sym, int plus_cnt, class CTcSymMetaclass *meta_sym, struct tcprs_term_info *term_info, int is_transient); /* parse an object definition's superclass list */ void parse_superclass_list( class CTcSymObj *obj_sym, class CTPNSuperclassList &sclist); /* * Add a generated object. This is used for objects created implicitly * rather than defined in the source code. For the static compiler, * we'll create an anonymous object and set up its definition * statements. For the dynamic compiler, we'll actually create the VM * object directly. */ class CTcSymObj *add_gen_obj(const char *clsname); class CTcSymObj *add_gen_obj(class CTcSymObj *cls); class CTcSymObj *add_gen_obj() { return add_gen_obj((class CTcSymObj *)0); } /* add constant property values to a generated object */ void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, int val); void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, const char *val); void add_gen_obj_prop(class CTcSymObj *obj, const char *prop, const CTcConstVal *val); /* parse an object template instance in an object body */ void parse_obj_template(int *err, class CTPNObjDef *objdef, int is_inline); /* search a superclass list for a template match */ const class CTcObjTemplate *find_class_template(const class CTPNSuperclass *first_sc, class CTcObjTemplateInst *src, size_t src_cnt, const CTPNSuperclass **def_sc, int *undescribed_class); /* find a match for a given template in the given list */ const class CTcObjTemplate *find_template_match(const class CTcObjTemplate *first_tpl, class CTcObjTemplateInst *src, size_t src_cnt); /* * Match a template to a given actual template parameter list. Returns * true if we match, false if not. We'll fill in the actual list with * the property symbols that we matched; these values are only * meaningful if we return true to indicate a match. */ int match_template(const class CTcObjTemplateItem *tpl_head, class CTcObjTemplateInst *src, size_t src_cnt); /* get the first string template */ class CTcStrTemplate *get_str_template_head() const { return str_template_head_; } /* parse an object's property list */ int parse_obj_prop_list( int *err, class CTPNObjDef *objdef, class CTcSymMetaclass *meta_sym, int modify, int is_nested, int braces, int is_inline, struct tcprs_term_info *outer_term_info, struct tcprs_term_info *term_info); /* parse property definition within an object */ void parse_obj_prop( int *err, class CTPNObjDef *objdef, int replace, class CTcSymMetaclass *meta_sym, struct tcprs_term_info *term_info, struct propset_def *propset_stack, int propset_depth, int enclosing_obj_is_nested, int is_inline); /* parse a class definition */ class CTPNStmTop *parse_class(int *err); /* parse a 'modify' definition */ class CTPNStmTop *parse_modify(int *err); /* parse a 'replace' definition */ class CTPNStmTop *parse_replace(int *err); /* parse a 'property' statement */ void parse_property(int *err); /* parse an 'export' statement */ void parse_export(int *err); /* add an export for the given symbol; returns the new export record */ class CTcPrsExport *add_export(const char *sym, size_t sym_len); /* add an export record to our list */ void add_export_to_list(class CTcPrsExport *exp); /* get the head of the export list */ class CTcPrsExport *get_exp_head() const { return exp_head_; } /* * Parse a function or method body, starting with the formal parameter * list. If 'eq_before_brace' is set, we expect an '=' before the * opening brace of the code body, and we allow the expression syntax, * where an expression enclosed in parentheses can be used. * 'self_valid' indicates whether or not 'self' is valid in the context * of the code being compiled; for an object method, 'self' is usually * valid, while for a stand-alone function it isn't. */ class CTPNCodeBody *parse_code_body(int eq_before_brace, int is_obj_prop, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, class CTcSymLocal ** p_varargs_list_local, int *has_retval, int *err, class CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type, struct propset_def *propset_stack, int propset_depth, struct CTcCodeBodyRef *enclosing, class CTcFormalTypeList **type_list); /* parse a nested code body (such as an anonymous function) */ class CTPNCodeBody *parse_nested_code_body( int eq_before_brace, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, class CTcSymLocal **p_varargs_list_local, int *has_retval, int *err, class CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type); /* insert a propertyset expansion */ void insert_propset_expansion(struct propset_def *propset_stack, int propset_depth); /* parse a formal parameter list */ void parse_formal_list(int count_only, int opt_allowed, int *argc, int *opt_argc, int *varargs, int *varargs_list, class CTcSymLocal **varargs_list_local, int *err, int base_formal_num, int for_short_anon_func, class CTcFormalTypeList **type_list); /* * Parse a compound statement. If 'skip_lbrace' is true, we'll skip * the opening '{', otherwise the caller must already have skipped it. * If 'need_rbrace' is true, we require the block to be closed by an * '}', which we'll skip before returning; otherwise, the block can end * at end-of-file on the token stream. * * 'enclosing_symtab' is the enclosing scope's symbol table, and * 'local_symtab' is the symbol table for the new scope within the * compound statement; if the caller has not already allocated a new * symbol table for the inner scope, it should simply pass the same * value for both symbol tables. * * 'enclosing_switch' is the immediately enclosing switch statement, if * any. This is only set when we're parsing the immediate body of a * switch statement. */ class CTPNStmComp *parse_compound(int *err, int skip_lbrace, int need_rbrace, class CTPNStmSwitch *enclosing_switch, int use_enclosing_scope); /* parse a local variable definition */ class CTPNStm *parse_local(int *err); /* parse a local initializer */ class CTcPrsNode *parse_local_initializer(class CTcSymLocal *lcl, int *err); /* * Parse an individual statement. * * If 'compound_use_enclosing_scope' is true, then if the statement * is a compound statement (i.e., the current token is a left * brace), the compound statement will use the current scope rather * than creating its own scope. Normally, a compound statement * establishes its own scope, so that local variables can hide * locals and parameters defined outside the braces. In certain * cases, however, locals defined within the braces should share the * enclosing scope: at the top level of a function or method, for * example, the formal parameters and the locals within the function * body go in the same scope, so the function body's compound * statement doesn't create its own scope. */ class CTPNStm *parse_stm(int *err, class CTPNStmSwitch *enclosing_switch, int compound_use_enclosing_scope); /* parse a 'case' label */ class CTPNStm *parse_case(int *err, class CTPNStmSwitch *enclosing_switch); /* parse a 'default' label */ class CTPNStm *parse_default(int *err, class CTPNStmSwitch *enclosing_switch); /* parse an 'if' statement */ class CTPNStm *parse_if(int *err); /* parse a 'return' statement */ class CTPNStm *parse_return(int *err); /* parse a 'for' statement */ class CTPNStm *parse_for(int *err); /* parse an 'in' clause in a 'for' statement */ class CTcPrsNode *parse_for_in_clause( class CTcPrsNode *lval, class CTPNForIn *&head, class CTPNForIn *&tail); /* parse a 'foreach' statement */ class CTPNStm *parse_foreach(int *err); /* parse a 'break' statement */ class CTPNStm *parse_break(int *err); /* parse a 'continue' statement */ class CTPNStm *parse_continue(int *err); /* parse a 'while' */ class CTPNStm *parse_while(int *err); /* parse a 'do-while' */ class CTPNStm *parse_do_while(int *err); /* parse a 'switch' */ class CTPNStm *parse_switch(int *err); /* parse a 'goto' */ class CTPNStm *parse_goto(int *err); /* parse a 'try' */ class CTPNStm *parse_try(int *err); /* parse a 'throw' */ class CTPNStm *parse_throw(int *err); /* parse an 'operator' property name */ int parse_op_name(class CTcToken *tok, int *op_argp = 0); /* * Create a symbol node. We'll look up the symbol in local scope. * If we find the symbol in local scope, we'll return a resolved * symbol node for the local scope item. If the symbol isn't * defined in local scope, we'll return an unresolved symbol node, * so that the symbol's resolution can be deferred until code * generation. */ class CTcPrsNode *create_sym_node(const textchar_t *sym, size_t sym_len); /* * Get the source file descriptor and line number for the current * source line. We note this at the start of each statement, so * that a statement node constructed when we finish parsing the * statement can record the location of the start of the statement. */ class CTcTokFileDesc *get_cur_desc() const { return cur_desc_; } long get_cur_linenum() const { return cur_linenum_; } /* * Get/set the current enclosing statement. An enclosing statement * is a 'try' or 'label:' container. At certain times, we need to * know the current enclosing statement, or one of its enclosing * statements; for example, a 'break' with a label must find the * label in the enclosing statement list to know where to jump to * after the 'break', and must also know about all of the enclosing * 'try' blocks our to that point so that it can invoke their * 'finally' blocks. */ class CTPNStmEnclosing *get_enclosing_stm() const { return enclosing_stm_; } class CTPNStmEnclosing *set_enclosing_stm(class CTPNStmEnclosing *stm) { class CTPNStmEnclosing *old_enclosing; /* remember the current enclosing statement for a moment */ old_enclosing = enclosing_stm_; /* set the new enclosing statement */ enclosing_stm_ = stm; /* * return the previous enclosing statement - this allows the * caller to restore the previous enclosing statement upon * leaving a nested block, if that's why the caller is setting a * new enclosing statement */ return old_enclosing; } /* get the current code body reference object */ struct CTcCodeBodyRef *get_cur_code_body() const { return cur_code_body_; } /* determine if 'self' is valid in the current context */ int is_self_valid() const { return self_valid_; } /* * get/set the 'self' reference status - this indicates whether or not * 'self' has been referenced, explicitly via the 'self' * pseudo-variable or implicitly (such as via a property reference or * method call), in the code body currently being parsed */ int self_referenced() const { return self_referenced_; } void set_self_referenced(int f) { self_referenced_ = f; } /* * get/set the full method context reference status - this indicates * whether or not any of the method context variables (self, * targetprop, targetobj, definingobj) have been referenced, explicitly * or implicitly, in the code body currently being parsed */ int full_method_ctx_referenced() const { return full_method_ctx_referenced_; } void set_full_method_ctx_referenced(int f) { full_method_ctx_referenced_ = f; } /* * Get/set the flag indicating whether or not the local context of the * outermost code body needs 'self'. The outer code body needs 'self' * in the local context if any lexically nested code body requires * access to 'self'. */ int local_ctx_needs_self() const { return local_ctx_needs_self_; } void set_local_ctx_needs_self(int f) { local_ctx_needs_self_ = f; } /* * Get/set the flag indicating whether or not the local context of the * outermost code body needs the full method context stored in its * local context. The outer code body needs the full context stored if * any lexically nested code body requires access to any of the method * context variables besides 'self' (targetprop, targetobj, * definingobj). */ int local_ctx_needs_full_method_ctx() const { return local_ctx_needs_full_method_ctx_; } void set_local_ctx_needs_full_method_ctx(int f) { local_ctx_needs_full_method_ctx_ = f; } /* * Add a code label. This creates a 'goto' symbol table for the * current code body if one doesn't already exist */ class CTcSymLabel *add_code_label(const class CTcToken *tok); /* * Set the debugger local symbol table. Returns the previous symbol * table so that it can be restored if desired. */ class CTcPrsDbgSymtab *set_debug_symtab(class CTcPrsDbgSymtab *tab) { class CTcPrsDbgSymtab *old_tab; /* remember the original for later use */ old_tab = debug_symtab_; /* set the new table */ debug_symtab_ = tab; /* return the original */ return old_tab; } /* * given a (1-based) object file symbol index, get the symbol */ class CTcSymbol *get_objfile_sym(uint idx) { return (idx == 0 ? 0 : obj_sym_list_[idx - 1]); } /* * given a 1-based object file symbol index, get an object symbol; * if the symbol does not refer to an object, we'll return null */ class CTcSymObj *get_objfile_objsym(uint idx); /* * given an object file (1-based) object file dictionary index, get * the dictionary entry */ class CTcDictEntry *get_obj_dict(uint idx) { return (idx == 0 ? 0 : obj_dict_list_[idx - 1]); } /* add a dictionary object loaded from the object file */ void add_dict_from_obj_file(class CTcSymObj *sym); /* add a symbol object loaded from the object file */ void add_sym_from_obj_file(uint idx, class CTcSymbol *sym); /* * Get the next object file symbol index. Object file symbol * indices are used to relate symbols stored in the object file to * the corresponding symbol object in memory when the object file is * reloaded. */ uint get_next_obj_file_sym_idx() { /* return the next index, consuming the index value */ return obj_file_sym_idx_++; } /* * Get the next object file dictionary index. */ uint get_next_obj_file_dict_idx() { /* return the next index, consuming the index value */ return obj_file_dict_idx_++; } /* * add an anonymous function or other anonymous top-level statement * to our list of nested top-level statements */ void add_nested_stm(class CTPNStmTop *stm); /* get the head of the nested statement list */ class CTPNStmTop *get_first_nested_stm() const { return nested_stm_head_; } /* add an anonymous object to our list */ void add_anon_obj(class CTcSymObj *obj); /* add a non-symbolic object ID */ void add_nonsym_obj(tctarg_obj_id_t id); /* determine if the current code body has a local context */ int has_local_ctx() const { return has_local_ctx_ != 0; } /* get the local context variable number */ int get_local_ctx_var() const { return local_ctx_var_num_; } /* set up a local context, in preparation for a nested code body */ void init_local_ctx(); /* finish the local context, after parsing a nested code body */ void finish_local_ctx(CTPNCodeBody *cb, class CTcPrsSymtab *local_symtab); /* * allocate a context variable index - this assigns an array index * for a context variable within the context object that contains * the shared locals for its scope */ int alloc_ctx_arr_idx(); /* allocate a local for use as a local context holder */ int alloc_ctx_holder_var() { return alloc_local(); } /* get the maximum number of locals required in the function */ int get_max_local_cnt() const { return max_local_cnt_; } /* get the lexicalParent property symbol */ class CTcSymProp *get_lexical_parent_sym() const { return lexical_parent_sym_; } /* * find a grammar production symbol, adding a new one if needed, * returning the grammar production list entry for the object */ class CTcGramProdEntry *declare_gramprod(const char *sym, size_t len); /* find a grammar production list entry for a given object */ class CTcGramProdEntry *get_gramprod_entry(class CTcSymObj *sym); /* find a grammar production symbol, adding a new one if needed */ class CTcSymObj *find_or_def_gramprod(const char *txt, size_t len, class CTcGramProdEntry **entryp); /* allocate a new enumerator ID */ ulong new_enum_id() { return next_enum_id_++; } /* get the number of enumerator ID's allocated */ ulong get_enum_count() const { return next_enum_id_; } /* * Look up a property symbol, adding it if not yet defined. If the * symbol is defined as another type, we'll show an error if * show_err is true, and return null. */ CTcSymProp *look_up_prop(const class CTcToken *tok, int show_err); /* get the '+' property for tracking the location graph */ CTcSymProp *get_plus_prop() const { return plus_prop_; } /* * Read a length-prefixed string from a file. Copies the string into * tokenizer space (which is guaranteed valid throughout compilation), * and returns a pointer to the tokenizer copy. If ret_len is null, * we'll return a null-terminated string; otherwise, we'll return a * non-null-terminated string and set *ret_len to the length of the * string. * * The string must fit in the temporary buffer to be read, but the * permanent tokenizer copy is returned rather than the temp buffer. * If the string doesn't fit in the temp buffer (with null * termination, if null termination is requested), we'll log the given * error. */ static const char *read_len_prefix_str (CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len, int err_if_too_long); /* * Read a length-prefixed string into the given buffer, null * terminating the result. If the string is too long for the buffer, * we'll flag the given error code and return non-zero. If * successful, we'll return zero. */ static int read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, int err_if_too_long); /* get the miscVocab property symbol */ tctarg_prop_id_t get_miscvocab_prop() const { return miscvocab_prop_; } /* property symbols for the operator overload properties */ class CTcSymProp *ov_op_add_; class CTcSymProp *ov_op_sub_; class CTcSymProp *ov_op_mul_; class CTcSymProp *ov_op_div_; class CTcSymProp *ov_op_mod_; class CTcSymProp *ov_op_xor_; class CTcSymProp *ov_op_shl_; class CTcSymProp *ov_op_ashr_; class CTcSymProp *ov_op_lshr_; class CTcSymProp *ov_op_bnot_; class CTcSymProp *ov_op_bor_; class CTcSymProp *ov_op_band_; class CTcSymProp *ov_op_neg_; class CTcSymProp *ov_op_idx_; class CTcSymProp *ov_op_setidx_; /* embedded expression token capture list */ class CTcEmbedTokenList *embed_toks_; private: /* cache the special properties, defining them if desired */ void cache_special_props(int def); /* * Static compiler handling for object generation. These are * implemented only in the static compiler; they're stubbed out for the * dynamic compiler, because the dynamic compiler generates objects in * the live VM through the G_vmifc interface instead. */ CTcSymObj *add_gen_obj_stat(class CTcSymObj *cls); void add_gen_obj_prop_stat(class CTcSymObj *obj, class CTcSymProp *prop, const CTcConstVal *val); /* clear the anonymous function local context information */ void clear_local_ctx(); /* * begin a property expression, saving parser state for later * restoration with finish_prop_expr */ void begin_prop_expr( class CTcPrsPropExprSave *save_info, int is_static, int is_inline); /* * Finish a property expression, wrapping it in a code body if * necessary to allow for an embedded anonymous function. Returns * null if no wrapping is required, in which case the original * expression should continue to be used, or the non-null code body * wrapper if needed, in which case the original expression should be * discarded in favor of the fully wrapped code body. */ void finish_prop_expr( class CTcPrsPropExprSave *save_info, class CTcPrsNode* &expr, class CTPNCodeBody* &code_body, class CTPNAnonFunc* &inline_method, int is_static, int is_inline, class CTcSymProp *prop_sym); /* * callback for symbol table enumeration for writing a symbol export * file */ static void write_sym_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for writing an object file */ static void write_obj_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for writing cross references */ static void write_obj_ref_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for named grammar rules */ static void write_obj_gram_cb(void *ctx, class CTcSymbol *sym); /* callback for symbol table enumeration for merging grammar rules */ static void build_grammar_cb(void *ctx, class CTcSymbol *sym); /* * Enter a scope. Upon entering, we'll remember the current local * variable data; on leaving, we'll restore the enclosing scope. */ void enter_scope(struct tcprs_scope_t *info) { /* remember the current scope information */ info->local_symtab = local_symtab_; info->enclosing_symtab = enclosing_local_symtab_; info->local_cnt = local_cnt_; /* * We haven't yet allocated a symbol table local to the new * scope -- we defer this until we actually need to insert a * symbol into the new scope. In order to detect when we need * to create our own local symbol table, we keep track of the * enclosing symbol table; when the local table is the same as * the enclosing table, and we need to insert a symbol, it means * that we must create a new table for the current scope. */ enclosing_local_symtab_ = local_symtab_; } /* leave a scope */ void leave_scope(struct tcprs_scope_t *info) { /* restore enclosing scope information */ local_symtab_ = info->local_symtab; enclosing_local_symtab_ = info->enclosing_symtab; /* return to the local count in the enclosing scope */ // $$$ we can't actually do this because variables could // be allocated after this scope ends, but need lifetimes // that overlap with the enclosed scope; what we actually // need to do, if we wanted to optimize things, would be // to allow this block of variables to be used in *disjoint* // scopes, but not again in enclosing scopes. We can easily, // though suboptimally, handle this by simply not allowing // the variables in the enclosed scope to be re-used at all // in the current code block. // local_cnt_ = info->local_cnt; } /* * Create a local symbol table in the current scope, if necessary. * If we've already created a local symbol table for the current * scope, this has no effect. */ void create_scope_local_symtab(); /* allocate a new local variable ID */ int alloc_local() { /* * if this exceeds the maximum depth in the block so far, note * the new maximum depth */ if (local_cnt_ + 1 > max_local_cnt_) max_local_cnt_ = local_cnt_ + 1; /* return the local number, and increment the counter */ return local_cnt_++; } /* find a dictionary symbol, adding a new one if needed */ class CTcDictEntry *declare_dict(const char *sym, size_t len); /* create a new dictionary list entry */ class CTcDictEntry *create_dict_entry(class CTcSymObj *sym); /* find a dictionary list entry for a given object */ class CTcDictEntry *get_dict_entry(class CTcSymObj *sym); /* create a new grammar production list entry */ class CTcGramProdEntry *create_gramprod_entry(class CTcSymObj *sym); /* symbol enumerator - look for unresolved external references */ static void enum_sym_extref(void *ctx, class CTcSymbol *sym); /* symbol enumerator - apply internal fixups */ static void enum_sym_internal_fixup(void *ctx, class CTcSymbol *sym); /* symbol enumerator - build dictionary */ static void enum_sym_dict(void *ctx, class CTcSymbol *sym); /* enumeration callback - context local conversion */ static void enum_for_ctx_locals(void *ctx, class CTcSymbol *sym); /* global symbol table */ class CTcPrsSymtab *global_symtab_; /* the constructor property ID and symbol */ tc_prop_id constructor_prop_; class CTcSymProp *constructor_sym_; /* the finalizer property ID */ tc_prop_id finalize_prop_; /* grammarInfo property symbol */ class CTcSymProp *graminfo_prop_; /* grammarTag property symbol */ class CTcSymProp *gramtag_prop_; /* miscVocab property ID */ tctarg_prop_id_t miscvocab_prop_; /* lexicalParent property symbol */ class CTcSymProp *lexical_parent_sym_; /* sourceTextOrder property symbol */ class CTcSymProp *src_order_sym_; /* sourceTextGroup property symbol */ class CTcSymProp *src_group_sym_; /* sourceTextGroupName, sourceTextGroupOrder */ class CTcSymProp *src_group_mod_sym_; class CTcSymProp *src_group_seq_sym_; /* * Source text order index. Each time we encounter an object * definition in the source code, we assign the current index value to * the object's 'sourceTextOrder' property, then we increment the * index. This provides the game program with information on the order * in which static objects appear in the source code, so that the * program can sort a collection of objects into their source file * order if desired. */ long src_order_idx_; /* * Source group object. If we're assigning source text group values, * we create an object for each source module to identify the module. */ tctarg_obj_id_t src_group_id_; /* * flag: in preprocessor constant expression mode; double-quoted * strings should be treated the same as single-quoted strings for * concatenation and comparisons */ uint pp_expr_mode_ : 1; /* * Is source text mode turned on? If this is true, we'll generate * sourceTextGroup properties for objects, otherwise we won't. */ uint src_group_mode_ : 1; /* * Flag: syntax-only mode. We use this mode to analyze the syntax * of the file without building the image; this is used, for * example, to build the exported symbol file for a source file. In * this mode, we'll suppress certain warnings and avoid doing work * that's not necessary for syntactic analysis; for example, we * won't show "unreachable code" errors. */ uint syntax_only_ : 1; /* * Flag: debugger expression mode. We accept some special internal * syntax in this mode that's not allowed in ordinary source code. */ uint debug_expr_ : 1; /* * Code block parsing state */ /* * 'goto' symbol table for the current code block - there's only one * 'goto' scope for an entire code block, so this never changes over * the course of a code block */ class CTcPrsSymtab *goto_symtab_; /* * Current local symbol table. Each inner scope that defines its * own local variables has its own local symbol table, nested within * the enclosing scope's. When leaving an inner scope, this should * always be restored to the local symbol table of the enclosing * scope. */ class CTcPrsSymtab *local_symtab_; /* * Enclosing local symbol table. If this is the same as * local_symtab_, it means that the current scope has not yet * created its own local symbol table. We defer this creation until * we find we actually need a local symbol table in a scope, since * most scopes don't define any of their own local variables. */ class CTcPrsSymtab *enclosing_local_symtab_; /* * Current debugger local symbol table. When we're compiling a * debugger expression, this will provide access to the current * local scope in the debug records. */ class CTcPrsDbgSymtab *debug_symtab_; /* * Number of local variables allocated so far in current code block * -- this reflects nesting to the current innermost scope, because * variables in inner scope are allocated in the same stack frame as * the enclosing scopes. When leaving an inner scope, this should * be restored */ int local_cnt_; /* * maximum local variable depth for the current code block -- this * reflects the maximum depth, including all inner scopes so far */ int max_local_cnt_; /* * Enclosing statement - this is the innermost 'try' or 'label:' * enclosing the current code. */ class CTPNStmEnclosing *enclosing_stm_; /* file descriptor and line number at start of current statement */ class CTcTokFileDesc *cur_desc_; long cur_linenum_; /* currently active dictionary */ class CTcDictEntry *dict_cur_; /* head and tail of dictionary list */ class CTcDictEntry *dict_head_; class CTcDictEntry *dict_tail_; /* head and tail of grammar production entry list */ class CTcGramProdEntry *gramprod_head_; class CTcGramProdEntry *gramprod_tail_; /* * array of symbols loaded from the object file - these are indexed * by the object file symbol index stored in symbol references in * the object file, allowing us to fix up references from one symbol * to another during loading */ class CTcSymbol **obj_sym_list_; /* * array of dictionary objects for the object file being loaded - * these are indexed by the dictionary index stored in symbol * references in the object file, allowing us to fix up references * from an object to its dictionary */ class CTcDictEntry **obj_dict_list_; /* next available object file dictionary index */ uint obj_file_dict_idx_; /* next available object file symbol index */ uint obj_file_sym_idx_; /* dictionary property list head */ class CTcDictPropEntry *dict_prop_head_; /* * Head and tail of list of nested top-level statements parsed for the * current top-level statement. This list includes anonymous * functions and nested objects, since these statements must * ultimately be linked into the top-level statement queue, but can't * be linked in while they're being parsed because of their nested * location in the recursive descent. We'll throw each new nested * top-level statement into this list as we parse them, then add this * list to the top-level statement list when we're done with the * entire program. */ class CTPNStmTop *nested_stm_head_; class CTPNStmTop *nested_stm_tail_; /* * Anonymous object list. This is a list of objects which are * defined without symbol names. */ class CTcSymObj *anon_obj_head_; class CTcSymObj *anon_obj_tail_; /* * Non-symbolic object list. This is a list of objects that are * defined without symbols at all. */ struct tcprs_nonsym_obj *nonsym_obj_head_; struct tcprs_nonsym_obj *nonsym_obj_tail_; /* * Object template list - this is the master list of templates for the * root object class. */ class CTcObjTemplate *template_head_; class CTcObjTemplate *template_tail_; /* * Object template instance parsing expression array. Each time we * define a new template, we'll make sure this array is long enough * for the longest defined template. We use this list when we're * parsing a template instance to keep track of the expressions in * the template instance - we can't know until we have the entire * list which template we're using, so we must keep track of the * entire list until we reach the end of the list. */ class CTcObjTemplateInst *template_expr_; size_t template_expr_max_; /* * String template list. String templates are unrelated to object * templates; these are for custom syntax in << >> expressions in * strings. */ class CTcStrTemplate *str_template_head_; class CTcStrTemplate *str_template_tail_; /* head and tail of exported symbol list */ class CTcPrsExport *exp_head_; class CTcPrsExport *exp_tail_; /* * Flag: current code body has a local variable context object. If * this is set, we must generate code that sets up the context * object on entry to the code body. */ unsigned int has_local_ctx_ : 1; /* local variable number of the code body's local variable context */ int local_ctx_var_num_; /* array of context variable property values */ tctarg_prop_id_t *ctx_var_props_; /* size of array */ size_t ctx_var_props_size_; /* number of context variable property values in the list */ size_t ctx_var_props_cnt_; /* * number of context variable property values assigned to the * current code body */ size_t ctx_var_props_used_; /* next available local variable context index */ int next_ctx_arr_idx_; /* reference to the current code body being parsed */ CTcCodeBodyRef *cur_code_body_; /* flag: 'self' is valid in current code body */ int self_valid_; /* * flag: 'self' is used (explicitly or implicitly, such as via a * property reference or method call) in the current code body */ int self_referenced_; /* * Flag: method context beyond 'self' (targetprop, targetobj, * definingobj) is referenced (explicitly or implicitly, such as via * 'inherited' or 'delegated') in the current code body. */ int full_method_ctx_referenced_; /* * Flags: the local context of the outermost code body requires * 'self'/the full method context to be stored. */ int local_ctx_needs_self_; int local_ctx_needs_full_method_ctx_; /* next available enumerator ID */ ulong next_enum_id_; /* * The '+' property - this is the property that defines the * containment graph for the purposes of the '+' syntax. */ class CTcSymProp *plus_prop_; /* * '+' property location stack. Each time the program defines an * object using the '+' notation to set the location, we'll update our * record here of the last object at that depth. Any time an object * is defined at depth N (i.e., using N '+' signs), its location is * set to the last object at depth N-1. An object with no '+' signs * is at depth zero. */ class CTPNStmObject **plus_stack_; size_t plus_stack_alloc_; /* * The module name and sequence number, if known. The module name is * the name as it appears on the command line, makefile (.t3m), or * library (.tl) file. The sequence number is an ordinal giving its * position in the list of modules making up the overall program build. * We use this information in generating the sourceTextGroup object. */ char *module_name_; int module_seqno_; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree parser - property arrow list */ struct CTcGramPropArrows { CTcGramPropArrows() { cnt = 0; } /* maximum number of arrows */ static const size_t max_arrows = 100; /* array of property arrows */ class CTcSymProp *prop[max_arrows]; /* number of arrows */ size_t cnt; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree parser - compiler interface functions. We abstract a * number of functions from the compiler interface to allow for differences * in behavior between regular static compilation and run-time construction * of grammar rules. */ class CTcGramAltFuncs { public: /* look up a property symbol, defining it if it's undefined */ virtual class CTcSymProp *look_up_prop( const class CTcToken *tok, int show_err) = 0; /* declare a grammar production symbol */ virtual class CTcGramProdEntry *declare_gramprod( const char *txt, size_t len) = 0; /* check the given enum for use as a production token */ virtual void check_enum_tok(class CTcSymEnum *enumsym) = 0; /* handle EOF in an alternative list */ virtual void on_eof(int *err) = 0; }; /* ------------------------------------------------------------------------ */ /* * Grammar tree node - base class */ class CTcPrsGramNode: public CTcTokenSource { public: CTcPrsGramNode() { /* we have no siblings yet */ next_ = 0; } /* get the next token - does nothing by default */ virtual const CTcToken *get_next_token() { return 0; } /* consolidate OR nodes at the top of the subtree */ virtual CTcPrsGramNode *consolidate_or() = 0; /* flatten CAT nodes together in the tree */ virtual void flatten_cat() { /* by default, do nothing */ } /* am I an "or" node? */ virtual int is_or() { return FALSE; } /* am I a "cat" node? */ virtual int is_cat() { return FALSE; } /* get my token - if I'm not a token node, returns null */ virtual const CTcToken *get_tok() const { return 0; } /* initialize expansion - by default, we do nothing */ virtual void init_expansion() { } /* * Advance to the next expansion state. Returns true if we 'carry' out * of the current item, which means that we were already at our last * state and hence are wrapping back to our first state. Returns false * if we advanced to a new state without wrapping back. * * By default, since normal items have only one alternative, we don't * do anything but return a 'carry, since each advance takes us back to * our single and initial state. */ virtual int advance_expansion() { return TRUE; } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const = 0; /* next sibling node */ CTcPrsGramNode *next_; }; /* ------------------------------------------------------------------------ */ /* * Statement termination information. This is used for certain nested * definition parsers, where a lack of termination in the nested * definition is to be interpreted as being actually caused by a lack of * termination of the enclosing definition. */ struct tcprs_term_info { /* initialize */ void init(class CTcTokFileDesc *desc, long linenum) { /* remember the current location */ desc_ = desc; linenum_ = linenum; /* no termination error yet */ unterm_ = FALSE; } /* * source location where original terminator might have been - this is * where we decided to go into a nested definition, so if it turns out * that the definintion shouldn't have been nested after all, there * was missing termination here */ class CTcTokFileDesc *desc_; long linenum_; /* * flag: termination was in fact missing in the nested definition; the * nested parser sets this to relay the problem to the caller */ int unterm_; }; /* ------------------------------------------------------------------------ */ /* * Object template list entry */ class CTcObjTemplate { public: CTcObjTemplate(class CTcObjTemplateItem *item_head, size_t item_cnt) { /* remember my item list */ items_ = item_head; item_cnt_ = item_cnt; /* not in a list yet */ nxt_ = 0; } /* head of list of template items */ class CTcObjTemplateItem *items_; /* number of items in the list */ size_t item_cnt_; /* next template in master list of templates */ CTcObjTemplate *nxt_; }; /* * Object template list item */ class CTcObjTemplateItem { public: CTcObjTemplateItem(class CTcSymProp *prop, tc_toktyp_t tok_type, int is_alt, int is_opt) { /* remember my defining information */ prop_ = prop; tok_type_ = tok_type; is_alt_ = is_alt; is_opt_ = is_opt; /* not in a list yet */ nxt_ = 0; } /* property that the item in this position defines */ class CTcSymProp *prop_; /* token type of item in this position */ tc_toktyp_t tok_type_; /* next item in this template's item list */ CTcObjTemplateItem *nxt_; /* flag: this item is an alternative to the previous item */ unsigned int is_alt_ : 1; /* flag: this item is optional */ unsigned int is_opt_ : 1; }; /* * Template item instance - we keep track of the actual parameters to a * template with these items. */ class CTcObjTemplateInst { public: /* * expression value for the actual parameter, as either a naked * expression (expr_) or as a code body (code_body_) - only one of * expr_ or code_body_ will be valid */ class CTcPrsNode *expr_; class CTPNCodeBody *code_body_; class CTPNAnonFunc *inline_method_; /* * the introductory token of the parameter - if the parameter is * introduced by an operator token, this will not be part of the * expression */ tc_toktyp_t def_tok_; /* the first token of the value */ CTcToken expr_tok_; /* * The property to which to assign this actual parameter value. This * isn't filled in until we match the full list to an actual template, * since we don't know the meanings of the parameters until we match * the actuals to an existing template in memory. */ class CTcSymProp *prop_; }; /* ------------------------------------------------------------------------ */ /* * 'propertyset' definition structure. Each property set defines a * property pattern and an optional argument list for the properties within * the propertyset group. */ struct propset_def { /* the property name pattern */ const char *prop_pattern; size_t prop_pattern_len; /* head of list of tokens in the parameter list */ struct propset_tok *param_tok_head; }; /* * propertyset token list entry */ struct propset_tok { propset_tok(const CTcToken *t) { /* copy the token */ this->tok = *t; /* we're not in a list yet */ nxt = 0; } /* the token */ CTcToken tok; /* next token in the list */ propset_tok *nxt; }; /* * Token source for parsing formal parameters using property set formal * lists. This retrieves tokens from a propertyset stack. */ class propset_token_source: public CTcTokenSource { public: propset_token_source() { /* nothing in our list yet */ nxt_tok = last_tok = 0; } /* get the next token */ virtual const CTcToken *get_next_token() { /* if we have another entry in our list, retrieve it */ if (nxt_tok != 0) { /* remember the token to return */ CTcToken *ret = &nxt_tok->tok; /* advance our internal position to the next token */ nxt_tok = nxt_tok->nxt; /* return the token */ return ret; } else { /* we have nothing more to return */ return 0; } } /* insert a token */ void insert_token(const CTcToken *tok); /* insert a token based on type */ void insert_token(tc_toktyp_t typ, const char *txt, size_t len); /* the next token we're to retrieve */ propset_tok *nxt_tok; /* tail of our list */ propset_tok *last_tok; }; /* maximum propertyset nesting depth */ const size_t MAX_PROPSET_DEPTH = 10; /* ------------------------------------------------------------------------ */ /* * String template entry. A string template defines custom syntax for * embedded << >> expressions in strings. */ class CTcStrTemplate { public: CTcStrTemplate() { /* initially clear the token list */ head = tail = 0; cnt = 0; star = FALSE; /* no function symbol yet */ func = 0; /* not in the global list yet */ nxt = 0; } /* append a token to the list */ void append_tok(const CTcToken *tok); /* the token list */ CTcTokenEle *head, *tail; /* number of tokens in the list */ int cnt; /* do we have a '*' token? */ int star; /* the processor function symbol */ class CTcSymFunc *func; /* next list element */ CTcStrTemplate *nxt; }; /* ------------------------------------------------------------------------ */ /* * Non-symbolic object list entry */ struct tcprs_nonsym_obj { tcprs_nonsym_obj(tctarg_obj_id_t id) { /* remember the ID */ id_ = id; /* not in a list yet */ nxt_ = 0; } /* ID of this object */ tctarg_obj_id_t id_; /* next entry in the list */ tcprs_nonsym_obj *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Dictionary property list entry. Each time the source code defines a * dictionary property, we'll make an entry in this list. */ class CTcDictPropEntry { public: CTcDictPropEntry(class CTcSymProp *prop) { /* remember the property */ prop_ = prop; /* not in a list yet */ nxt_ = 0; /* not defined for current object yet */ defined_ = FALSE; } /* my property */ class CTcSymProp *prop_; /* next entry in list */ CTcDictPropEntry *nxt_; /* flag: the current object definition includes this property */ unsigned int defined_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Dictionary list entry. Each dictionary object gets an entry in this * list. */ class CTcDictEntry { public: CTcDictEntry(class CTcSymObj *sym); /* get/set my object file index */ uint get_obj_idx() const { return obj_idx_; } void set_obj_idx(uint idx) { obj_idx_ = idx; } /* get my object symbol */ class CTcSymObj *get_sym() const { return sym_; } /* get/set the next item in the list */ CTcDictEntry *get_next() const { return nxt_; } void set_next(CTcDictEntry *nxt) { nxt_ = nxt; } /* add a word to the table */ void add_word(const char *txt, size_t len, int copy, tc_obj_id obj, tc_prop_id prop); /* write my symbol to the object file if I haven't already done so */ void write_sym_to_obj_file(CVmFile *fp); /* get the hash table */ class CVmHashTable *get_hash_table() const { return hashtab_; } protected: /* enumeration callback - write to object file */ static void enum_cb_writeobj(void *ctx, class CVmHashEntry *entry); /* associated object symbol */ class CTcSymObj *sym_; /* * object file index (we use this to match up the dictionary objects * when we re-load the object file) */ uint obj_idx_; /* next item in the dictionary list */ CTcDictEntry *nxt_; /* hash table containing the word entries */ class CVmHashTable *hashtab_; }; /* * entry in a dictionary list */ struct CTcPrsDictItem { CTcPrsDictItem(tc_obj_id obj, tc_prop_id prop) { obj_ = obj; prop_ = prop; nxt_ = 0; } /* object */ tc_obj_id obj_; /* property */ tc_prop_id prop_; /* next entry in list */ CTcPrsDictItem *nxt_; }; /* * Parser dictionary hash table entry */ class CVmHashEntryPrsDict: public CVmHashEntryCS { public: CVmHashEntryPrsDict(const char *txt, size_t len, int copy) : CVmHashEntryCS(txt, len, copy) { /* nothing in my list yet */ list_ = 0; } /* add an item to my list */ void add_item(tc_obj_id obj, tc_prop_id prop); /* get the list head */ struct CTcPrsDictItem *get_list() const { return list_; } protected: /* list of object/property associations with this word */ struct CTcPrsDictItem *list_; }; /* ------------------------------------------------------------------------ */ /* * Grammar production list entry */ class CTcGramProdEntry { public: CTcGramProdEntry(class CTcSymObj *prod_obj); /* get my production object symbol */ class CTcSymObj *get_prod_sym() const { return prod_sym_; } /* get/set the next item in the list */ CTcGramProdEntry *get_next() const { return nxt_; } void set_next(CTcGramProdEntry *nxt) { nxt_ = nxt; } /* add an alternative */ void add_alt(class CTcGramProdAlt *alt); /* get the alternative list head */ class CTcGramProdAlt *get_alt_head() const { return alt_head_; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static void load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat, class CTcSymObj *private_owner); /* move alternatives from my list to the given target list */ void move_alts_to(CTcGramProdEntry *new_entry); /* get/set explicitly-declared flag */ int is_declared() const { return is_declared_; } void set_declared(int f) { is_declared_ = f; } protected: /* associated production object symbol */ class CTcSymObj *prod_sym_; /* next item in the list */ CTcGramProdEntry *nxt_; /* head and tail of alternative list */ class CTcGramProdAlt *alt_head_; class CTcGramProdAlt *alt_tail_; /* * flag: this production was explicitly declared (this means that we * will consider it valid at link time even if it has no alternatives * defined) */ unsigned int is_declared_ : 1; }; /* * Grammar production alternative. Each grammar production has one or * more alternatives that, when matched, generate the production. */ class CTcGramProdAlt { public: CTcGramProdAlt(class CTcSymObj *obj_sym, class CTcDictEntry *dict); /* get/set my score */ int get_score() const { return score_; } void set_score(int score) { score_ = score; } /* get/set my badness */ int get_badness() const { return badness_; } void set_badness(int badness) { badness_ = badness; } /* get my processor object symbol */ class CTcSymObj *get_processor_obj() const { return obj_sym_; } /* get/set the next list element */ CTcGramProdAlt *get_next() const { return nxt_; } void set_next(CTcGramProdAlt *nxt) { nxt_ = nxt; } /* add a token to my list */ void add_tok(class CTcGramProdTok *tok); /* get the head of my token list */ class CTcGramProdTok *get_tok_head() const { return tok_head_; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static CTcGramProdAlt * load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat); /* get the dictionary in effect when the alternative was defined */ class CTcDictEntry *get_dict() const { return dict_; } protected: /* head and tail of our token list */ class CTcGramProdTok *tok_head_; class CTcGramProdTok *tok_tail_; /* dictionary in effect when alternative was defined */ class CTcDictEntry *dict_; /* the processor object associated with this alternative */ class CTcSymObj *obj_sym_; /* next alternative in our production */ CTcGramProdAlt *nxt_; /* score */ int score_; /* badness */ int badness_; }; /* grammar production token types */ enum tcgram_tok_type { /* unknown */ TCGRAM_UNKNOWN, /* match a production (given by the production object) */ TCGRAM_PROD, /* match a part of speech (given by the dictionary property) */ TCGRAM_PART_OF_SPEECH, /* match a literal string */ TCGRAM_LITERAL, /* token-type match */ TCGRAM_TOKEN_TYPE, /* free-floating end-of-string */ TCGRAM_STAR, /* match one of several parts of speech */ TCGRAM_PART_OF_SPEECH_LIST }; /* * Grammar production alternative token */ class CTcGramProdTok { public: CTcGramProdTok() { /* not in a list yet */ nxt_ = 0; /* no type yet */ typ_ = TCGRAM_UNKNOWN; /* no property association yte */ prop_assoc_ = TCTARG_INVALID_PROP; } /* get/set my next element */ CTcGramProdTok *get_next() const { return nxt_; } void set_next(CTcGramProdTok *nxt) { nxt_ = nxt; } /* set me to match a production object */ void set_match_prod(class CTcSymObj *obj) { /* remember the production object */ typ_ = TCGRAM_PROD; val_.obj_ = obj; } /* set me to match a token type */ void set_match_token_type(ulong enum_id) { /* remember the token enum ID */ typ_ = TCGRAM_TOKEN_TYPE; val_.enum_id_ = enum_id; } /* set me to match a dictionary property */ void set_match_part_of_speech(tctarg_prop_id_t prop) { /* remember the part of speech */ typ_ = TCGRAM_PART_OF_SPEECH; val_.prop_ = prop; } /* * set me to match a list of parts of speech; each part of speech must * be separately added via add_match_part_ele() */ void set_match_part_list(); /* add an element to the part-of-speech match list */ void add_match_part_ele(tctarg_prop_id_t prop); /* set me to match a literal string */ void set_match_literal(const char *txt, size_t len) { /* remember the string */ typ_ = TCGRAM_LITERAL; val_.str_.txt_ = txt; val_.str_.len_ = len; } /* set me to match a free-floating end-of-string */ void set_match_star() { /* set the type */ typ_ = TCGRAM_STAR; } /* get my type */ tcgram_tok_type get_type() const { return typ_; } /* get my value */ class CTcSymObj *getval_prod() const { return val_.obj_; } tctarg_prop_id_t getval_part_of_speech() const { return val_.prop_; } const char *getval_literal_txt() const { return val_.str_.txt_; } const size_t getval_literal_len() const { return val_.str_.len_; } ulong getval_token_type() const { return val_.enum_id_; } size_t getval_part_list_len() const { return val_.prop_list_.len_; } tctarg_prop_id_t getval_part_list_ele(size_t idx) const { return val_.prop_list_.arr_[idx]; } /* * get/set my property association - this is the property to which * the actual match to the rule is assigned when we match the rule */ tctarg_prop_id_t get_prop_assoc() const { return prop_assoc_; } void set_prop_assoc(tctarg_prop_id_t prop) { prop_assoc_ = prop; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* load from an object file */ static CTcGramProdTok * load_from_obj_file(class CVmFile *fp, const tctarg_prop_id_t *prop_xlat, const ulong *enum_xlat); protected: /* next token in my list */ CTcGramProdTok *nxt_; /* my type - this specifies how this token matches */ tcgram_tok_type typ_; /* match specification - varies according to my type */ union { /* object - for matching a production */ class CTcSymObj *obj_; /* property - for matching a part of speech */ tctarg_prop_id_t prop_; /* token enum id - for matching a token type */ ulong enum_id_; /* literal string */ struct { const char *txt_; size_t len_; } str_; /* list of vocabulary elements */ struct { /* number of array entries allocated */ size_t alo_; /* number of array entries actually used */ size_t len_; /* array of entries */ tctarg_prop_id_t *arr_; } prop_list_; } val_; /* property association */ tctarg_prop_id_t prop_assoc_; }; /* ------------------------------------------------------------------------ */ /* * Exported symbol record */ class CTcPrsExport { public: /* create with the given compiler symbol */ CTcPrsExport(const char *sym, size_t sym_len) { /* remember my name */ sym_ = sym; sym_len_ = sym_len; /* * we don't yet have an explicit external name, so export using * the internal name */ ext_name_ = sym; ext_len_ = sym_len; /* we're not in a list yet */ nxt_ = 0; } /* set the external name */ void set_extern_name(const char *txt, size_t len) { ext_name_ = txt; ext_len_ = len; } /* get the symbol name and length */ const char *get_sym() const { return sym_; } size_t get_sym_len() const { return sym_len_; } /* get the external name and length */ const char *get_ext_name() const { return ext_name_; } size_t get_ext_len() const { return ext_len_; } /* get/set the next entry in the list */ CTcPrsExport *get_next() const { return nxt_; } void set_next(CTcPrsExport *nxt) { nxt_ = nxt; } /* write to an object file */ void write_to_obj_file(class CVmFile *fp); /* read from an object file */ static CTcPrsExport *read_from_obj_file(class CVmFile *fp); /* determine if my external name matches the given export's */ int ext_name_matches(const CTcPrsExport *exp) const { return (exp->get_ext_len() == get_ext_len() && memcmp(exp->get_ext_name(), get_ext_name(), get_ext_len()) == 0); } /* determine if my name matches the given string */ int ext_name_matches(const char *txt) const { return (get_ext_len() == get_strlen(txt) && memcmp(get_ext_name(), txt, get_ext_len()) == 0); } /* determine if my name matches the leading substring */ int ext_name_starts_with(const char *txt) const { return (get_ext_len() >= get_strlen(txt) && memcmp(get_ext_name(), txt, get_strlen(txt)) == 0); } /* determine if my symbol name matches the given export's */ int sym_matches(const CTcPrsExport *exp) const { return (exp->get_sym_len() == get_sym_len() && memcmp(exp->get_sym(), get_sym(), get_sym_len()) == 0); } protected: /* symbol name - this is the internal compiler symbol being exported */ const char *sym_; size_t sym_len_; /* external name - this is the name visible to the VM loader */ const char *ext_name_; size_t ext_len_; /* next in list */ CTcPrsExport *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table. The parser maintains a hierarchy of symbol * tables; a local symbol table can be nested inside an enclosing * scope's symbol table, and so on up to the top-level block scope, * which is enclosed by the global scope. In addition, at function * scope there's a separate table for "goto" labels. */ /* find_or_def actions for undefined symbols */ enum tcprs_undef_action { /* if undefined, add an "undefined" entry unconditionally */ TCPRS_UNDEF_ADD_UNDEF, /* add a "property" entry unconditionally, but warn about it */ TCPRS_UNDEF_ADD_PROP, /* add a "property" entry unconditionally, with no warning */ TCPRS_UNDEF_ADD_PROP_NO_WARNING, /* add a "weak" property entry without warning */ TCPRS_UNDEF_ADD_PROP_WEAK }; /* parser symbol table */ class CTcPrsSymtab { public: CTcPrsSymtab(CTcPrsSymtab *parent_scope); virtual ~CTcPrsSymtab(); /* allocate parser symbol tables out of the parser memory pool */ void *operator new(size_t siz); /* * perform static initialization/termination - call once at program * startup and shutdown (respectively) */ static void s_init(); static void s_terminate(); /* get the enclosing scope's symbol table */ CTcPrsSymtab *get_parent() const { return parent_; } /* find a symbol; returns null if the symbol isn't defined */ class CTcSymbol *find(const textchar_t *sym, size_t len) { return find(sym, len, 0); } class CTcSymbol *find(const textchar_t *sym) { return find(sym, strlen(sym), 0); } /* * Find a symbol; returns null if the symbol isn't defined. If * symtab is not null, we'll fill it in with the actual symbol table * in which we found the symbol; this might be an enclosing symbol * table, since we search up the enclosing scope list. */ class CTcSymbol *find(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab); /* find a symbol without changing its referenced status */ class CTcSymbol *find_noref(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab); /* * Find a symbol; if the symbol isn't defined, log an error and add * the symbol as type "undefined". Because we add a symbol entry if * the symbol isn't defined, this *always* returns a valid symbol * object. */ class CTcSymbol *find_or_def_undef(const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_UNDEF); } /* * Find a symbol; if the symbol isn't defined, log a warning and * define the symbol as type property. Because we add an entry if * the symbol isn't defined, this *always* returns a valid symbol * object. */ class CTcSymbol *find_or_def_prop(const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP); } /* * Find a symbol; if the symbol isn't defined, define the symbol as * type property with no warning. This should be used when it is * unambiguous that a symbol is meant as a property name. Because we * add an entry if the symbol isn't defined, this *always* returns a * valid symbol object. */ class CTcSymbol *find_or_def_prop_explicit( const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP_NO_WARNING); } /* * Find a symbol; if the symbol isn't defined, define it as a property * without warning, but flag it as a "weak" definition. */ class CTcSymbol *find_or_def_prop_weak( const char *sym, size_t len, int copy_str) { return find_or_def(sym, len, copy_str, TCPRS_UNDEF_ADD_PROP_WEAK); } /* * Find a symbol. If the symbol isn't defined, and a "self" object * is available, define the symbol as a property. If the symbol * isn't defined an no "self" object is available, add an * "undefined" entry for the symbol. */ class CTcSymbol *find_or_def_prop_implied(const char *sym, size_t len, int copy_str, int is_self_avail) { return find_or_def(sym, len, copy_str, is_self_avail ? TCPRS_UNDEF_ADD_PROP : TCPRS_UNDEF_ADD_UNDEF); } /* * Find a symbol. If the symbol is already defined as a "weak" * property, delete it to make way for the new definition. */ class CTcSymbol *find_delete_weak(const char *sym, size_t len); /* add a formal parameter symbol */ class CTcSymLocal *add_formal(const textchar_t *sym, size_t len, int formal_num, int copy_str); /* add a local variable symbol */ class CTcSymLocal *add_local(const textchar_t *sym, size_t len, int local_num, int copy_str, int init_assigned, int init_referenced); /* * add the current token as a local symbol, initially unassigned and * unreferenced */ class CTcSymLocal *add_local(int local_num); /* add a 'goto' symbol */ class CTcSymLabel *add_code_label(const textchar_t *sym, size_t len, int copy_str); /* enumerate entries in the table through a callback */ void enum_entries(void (*func)(void *, class CTcSymbol *), void *ctx); /* * Scan the symbol table and check for unreferenced locals. Logs an * error for each unreferenced or unassigned local. */ void check_unreferenced_locals(); /* * Get/set my debugging list index - this is the index of this table * in the list for this function or method. The index values start * at 1 - a value of zero indicates that the symbol table isn't part * of any list. */ int get_list_index() const { return list_index_; } void set_list_index(int n) { list_index_ = n; } /* get/set the next entry in the linked list */ CTcPrsSymtab *get_list_next() const { return list_next_; } void set_list_next(CTcPrsSymtab *nxt) { list_next_ = nxt; } /* * The low-level virtual interface. All searching and manipulation of * the underlying hash table goes through these routines. This allows * subclasses to implement the symbol table as a view of another data * structure. * * The compiler itself just uses the underlying hash table directly. * The dynamic compiler for interpreter "eval()" functionality uses a * customized version that creates a view of an underlying user-code * LookupTable object. */ /* find a symbol directly in this table, without searching parents */ virtual class CTcSymbol *find_direct(const textchar_t *sym, size_t len); /* add an entry to the table */ virtual void add_entry(class CTcSymbol *sym); /* remove an entry */ virtual void remove_entry(class CTcSymbol *sym); /* expand my byte code range to include the given location */ void add_to_range(int ofs) { /* * if we don't have a starting offset yet, or this is before it, * this is the new start offset */ if (start_ofs_ == 0 || ofs < start_ofs_) start_ofs_ = ofs; /* * if we don't have an ending offset yet, or this is after it, this * is the ending offset */ if (ofs > end_ofs_) end_ofs_ = ofs; } /* get my bytecode offset range */ int get_start_ofs() const { return start_ofs_; } int get_end_ofs() const { return end_ofs_; } protected: /* add an entry to a global symbol table */ static void add_to_global_symtab(CTcPrsSymtab *tab, CTcSymbol *entry); /* get the underlying hash table */ class CVmHashTable *get_hashtab() const { return hashtab_; } /* enumeration callback - check for unreferenced locals */ static void unref_local_cb(void *ctx, class CTcSymbol *sym); /* * find a symbol, or define a new symbol, according to the given * action mode, if the symbol is undefined */ class CTcSymbol *find_or_def(const textchar_t *sym, size_t len, int copy_str, tcprs_undef_action action); /* enclosing scope (parent) symbol table */ CTcPrsSymtab *parent_; /* hash table */ class CVmHashTable *hashtab_; /* hash function */ static class CVmHashFunc *hash_func_; /* * Byte code range covered by this frame. This is the range of offsets * within the method where this frame is in effect. The range is * inclusive of the start offset and exclusive of the end offset. */ int start_ofs_; int end_ofs_; /* * Next symbol table in local scope chain. For each function or * method, we keep a simple linear list of the local scopes so that * they can be written to the debugging records. We also keep an * index value giving its position in the list, so that we can store * references to the table using the list index. */ CTcPrsSymtab *list_next_; int list_index_; }; /* ------------------------------------------------------------------------ */ /* * Debugger symbol table interface. This is an abstract interface that * debuggers can implement to allow us to search for symbols that are * obtained from a compiled program's debugger records. To keep the * compiler independent of the target architecture and the debugger's * own internal structures, we define this abstract interface that the * debugger must implement. * * Since this type of symbol table is provided by a debugger as a view * on the symbol information in a previously compiled program, the * parser naturally has no need to add symbols to the table; hence the * only required operations are symbol lookups. */ class CTcPrsDbgSymtab { public: /* * Get information on a symbol. Returns true if the symbol is * found, false if not. If we find the symbol, fills in the * information structure with the appropriate data. */ virtual int find_symbol(const textchar_t *sym, size_t len, struct tcprsdbg_sym_info *info) = 0; }; /* * Debugger local symbol information structure */ struct tcprsdbg_sym_info { /* symbol type */ enum tc_symtype_t sym_type; /* local/parameter number */ uint var_id; /* context variable index - 0 if it's not a context local */ int ctx_arr_idx; /* stack frame index */ uint frame_idx; }; /* ------------------------------------------------------------------------ */ /* * Parse Tree storage manager. * * The parse tree has some special characteristics that make it * desirable to use a special memory manager for it. First, the parse * tree consists of many small objects, so we would like to have as * little overhead per object for memory tracking as possible. Second, * parse tree objects all have a similar lifetime: we create the entire * parse tree as we scan the source, then use it to generate target * code, then discard the whole thing. * * To manage memory efficiently for the parse tree, we define our own * memory manager for parse tree objects. The memory manager is very * simple, fast, and has minimal per-object overhead. We simply * maintain a list of large blocks, then suballocate requests out of the * large blocks. Each time we run out of space in a block, we allocate * a new block. We do not keep track of any extra tracking information * per node, so a node cannot be individually freed; however, the entire * block list can be freed at once, which is exactly the behavior we * want. */ class CTcPrsMem { public: CTcPrsMem(); ~CTcPrsMem(); /* allocate storage */ void *alloc(size_t siz); /* save the current pool state, for later resetting */ void save_state(struct tcprsmem_state_t *state); /* * reset the pool to the given state - delete all objects allocated * in the pool since the state was saved */ void reset(const struct tcprsmem_state_t *state); /* reset to initial state */ void reset(); private: /* delete all parser memory */ void delete_all(); /* allocate a new block */ void alloc_block(); /* head of list of memory blocks */ struct tcprsmem_blk_t *head_; /* tail of list and current memory block */ struct tcprsmem_blk_t *tail_; /* current allocation offset in last block */ char *free_ptr_; /* remaining space available in last block */ size_t rem_; }; /* * state-saving structure */ struct tcprsmem_state_t { /* current tail of memory block list */ struct tcprsmem_blk_t *tail; /* current allocation offset in last block */ char *free_ptr; /* current remaining space in last block */ size_t rem; }; /* * Provide an overridden operator new for allocating objects explicitly * from the pool */ inline void *operator new(size_t siz, CTcPrsMem *pool) { return pool->alloc(siz); } /* * provide an array operator new as well */ inline void *operator new[](size_t siz, CTcPrsMem *pool) { return pool->alloc(siz); } /* * parse tree memory block */ struct tcprsmem_blk_t { /* next block in the list */ tcprsmem_blk_t *next_; /* * This block's byte array (the array extends off the end of the * structure). */ char buf_[1]; }; /* ------------------------------------------------------------------------ */ /* * Special array list subclass that uses parser memory */ class CPrsArrayList: public CArrayList { protected: /* * override the memory management functions to use parser memory */ virtual void *alloc_mem(size_t siz) { /* allocate from the parser pool */ return G_prsmem->alloc(siz); } virtual void *realloc_mem(void *p, size_t oldsiz, size_t newsiz) { void *pnew; /* allocate a new block from the parser pool */ pnew = G_prsmem->alloc(newsiz); /* copy from the old block to the new block */ memcpy(pnew, p, oldsiz); /* return the new block */ return pnew; } virtual void free_mem(void *p) { /* * do nothing - the parser pool automatically frees everything as a * block when terminating the parser */ } }; /* ------------------------------------------------------------------------ */ /* * Expression Constant Value object. This object is used to express the * value of a constant expression. */ class CTcConstVal { public: CTcConstVal() { /* the type is unknown */ typ_ = TC_CVT_UNK; /* assume it's a true constant, not just a compile-time constant */ ctc_ = FALSE; } /* * determine if this is a constant value - it is a constant if it * has any known value */ int is_const() const { return (typ_ != TC_CVT_UNK); } /* * set the type to unknown - this indicates that there is no valid * value, which generally means that the associated expression does * not have a constant value */ void set_unknown() { typ_ = TC_CVT_UNK; } /* set from another value */ void set(const CTcConstVal *val) { /* copy the type */ typ_ = val->typ_; /* copy the value */ val_ = val->val_; } /* set an integer value */ void set_int(long val) { typ_ = TC_CVT_INT; val_.intval_ = val; } /* set a floating-point value */ void set_float(const char *val, size_t len, int promoted); /* set a floating-point value from a vbignum_t */ void set_float(const class vbignum_t *val, int promoted); /* set a floating-point value promoted from an integer */ void set_float(ulong i); /* * Check to see if a promoted float value can be demoted back to int. * If the value is a float that was flagged as promoted from an int * constant, and the value fits in an int32, we'll demote the value * back to an int. This has no effect if the value isn't a float, * wasn't promoted, or doesn't fit in an int. This can be used after a * constant-folding operation is applied to a value that was at some * point promoted from int to see if the promotion is no longer * required. */ void demote_float(); /* set an enumerator value */ void set_enum(ulong val) { typ_ = TC_CVT_ENUM; val_.enumval_ = val; } /* set a single-quoted string value */ void set_sstr(const char *val, size_t len); void set_sstr(const CTcToken *tok); /* set a regex string value (R'...' or R"...") */ void set_restr(const CTcToken *tok); /* set a list value */ void set_list(class CTPNList *lst); /* for the debugger only: set a pre-resolved constant pool value */ void set_sstr(uint32_t ofs); void set_list(uint32_t ofs); /* set an object reference value */ void set_obj(ulong obj, enum tc_metaclass_t meta) { typ_ = TC_CVT_OBJ; val_.objval_.id_ = obj; val_.objval_.meta_ = meta; } /* set a property pointer value */ void set_prop(uint prop) { typ_ = TC_CVT_PROP; val_.propval_ = prop; } /* set a function pointer value */ void set_funcptr(class CTcSymFunc *sym) { typ_ = TC_CVT_FUNCPTR; val_.funcptrval_ = sym; } /* set an anonymous function pointer value */ void set_anon_funcptr(class CTPNCodeBody *code_body) { typ_ = TC_CVT_ANONFUNCPTR; val_.codebodyval_ = code_body; } /* set a built-in function pointer value */ void set_bifptr(class CTcSymBif *sym) { typ_ = TC_CVT_BIFPTR; val_.bifptrval_ = sym; } /* set a nil/true value */ void set_nil() { typ_ = TC_CVT_NIL; } void set_true() { typ_ = TC_CVT_TRUE; } /* * Set a vocabulary list placeholder. This has no actual value * during compilation; instead, this is just a placeholder. During * linking, we'll replace each of these with a list of strings * giving the actual vocabulary for the property. */ void set_vocab_list() { typ_ = TC_CVT_VOCAB_LIST; } /* set a nil/true value based on a boolean value */ void set_bool(int val) { typ_ = (val ? TC_CVT_TRUE : TC_CVT_NIL); } /* is this a boolean value? */ int is_bool() const { return typ_ == TC_CVT_NIL || typ_ == TC_CVT_TRUE; } /* get my type */ tc_constval_type_t get_type() const { return typ_; } /* get my int value (no type checking) */ long get_val_int() const { return val_.intval_; } /* get my floating point value (no type checking) */ const char *get_val_float() const { return val_.floatval_.txt_; } size_t get_val_float_len() const { return val_.floatval_.len_; } /* was the value promoted to float from int due to overflow? */ int is_promoted() const { return promoted_; } /* get my enumerator value (no type checking) */ ulong get_val_enum() const { return val_.enumval_; } /* get my string value (no type checking) */ const char *get_val_str() const { return val_.strval_.strval_; } size_t get_val_str_len() const { return val_.strval_.strval_len_; } /* get my list value (no type checking) */ class CTPNList *get_val_list() const { return val_.listval_.l_; } /* * for debugger expressions only: the string/list as a pre-resolved * constant pool address */ uint32_t get_val_str_ofs() const { return val_.strval_.pool_ofs_; } uint32_t get_val_list_ofs() const { return val_.listval_.pool_ofs_; } /* get my object reference value (no type checking) */ ulong get_val_obj() const { return val_.objval_.id_; } enum tc_metaclass_t get_val_obj_meta() const { return val_.objval_.meta_; } /* get my property pointer value (no type checking) */ uint get_val_prop() const { return val_.propval_; } /* get my function pointer symbol value (no type checking) */ class CTcSymFunc *get_val_funcptr_sym() const { return val_.funcptrval_; } /* get my anonymous function pointer value (no type checking) */ class CTPNCodeBody *get_val_anon_func_ptr() const { return val_.codebodyval_; } /* get my built-in function pointer symbol value (noi type checking) */ class CTcSymBif *get_val_bifptr_sym() const { return val_.bifptrval_; } /* * Determine if this value equals a given constant value. Returns * true if so, false if not. We'll set (*can_compare) to true if * the values are comparable, false if the comparison is not * meaningful. */ int is_equal_to(const CTcConstVal *val) const; /* * Convert an integer, nil, or true value to a string. Fills in the * buffer with the result of the conversion if the value wasn't * already a string. If the value is already a string, we'll simply * return a pointer to the original string without making a copy. * Returns null if the value is not convertible to a string. */ const char *cvt_to_str(char *buf, size_t bufl, size_t *result_len); /* * Get my true/nil value. Returns false if the value is nil or zero, * true if it's anything else. */ int get_val_bool() const { return !(typ_ == TC_CVT_NIL || equals_zero()); } /* is this is a numeric value equal to zero? */ int equals_zero() const; /* * Set/get the compile-time constant flag. A compile-time constant is * a value that's constant at compile-time, but which can vary from one * compilation to the next. The defined() and __objref() operators * have this property. */ void set_ctc(int f) { ctc_ = f; } int is_ctc() const { return ctc_; } private: /* my type */ tc_constval_type_t typ_; union { /* integer value (valid when typ_ == TC_CVT_INT) */ long intval_; /* floating-point value (valid when typ_ == TC_CVT_FLOAT) */ struct { const char *txt_; size_t len_; } floatval_; /* enumerator value (valid when typ_ == TC_CVT_ENUM) */ ulong enumval_; /* * String value (valid when typ_ == TC_CVT_TYPE_SSTR). We need * to know the length separately, because the underyling string * may not be null-terminated. */ struct { const char *strval_; size_t strval_len_; /* * For debugger expressions only: the pre-resolved constant * pool address in the live running program of a string or list * expression. This type is indicated by setting the value * data type to string or list, and setting the token value * pointer for that type to null. */ uint32_t pool_ofs_; } strval_; /* my list value */ struct { class CTPNList *l_; /* for debugger expressions only: the pre-resolved pool address */ uint32_t pool_ofs_; } listval_; /* property ID value */ uint propval_; /* object reference value */ struct { ulong id_; enum tc_metaclass_t meta_; } objval_; /* * function pointer value - we store the underlying symbol, * since function pointers are generally not resolved until late * in the compilation */ class CTcSymFunc *funcptrval_; /* built-in function pointer value */ class CTcSymBif *bifptrval_; /* * code body pointer value - we store the underlying code body * for anonymous functions */ class CTPNCodeBody *codebodyval_; } val_; /* * Is this a compile-time constant value? A compile-time constant is a * value that has a fixed constant value as of compile time, but could * vary from one compilation to another. The defined() operator * produces this type of constant, for example. * * The main distinction between a true constant and a compile-time * constant is that true constants generate warnings when they produce * invariant code, such as when a true constant is used as the * condition of an 'if'. Compile-time constants are specifically for * producing code that's invariant once compiled, but which can vary * across compilations, allowing for more sophisticated configuration * management. For example, defined() makes it possible to produce * code that only gets compiled when a particular symbol is included in * the build, so that code in module A can refer to symbols defined in * module B when module B is included in the build, but will * automatically omit the referring code when module B is omitted. */ uint ctc_ : 1; /* * Is this a promoted value? This is set to true for promotions from * int to BigNumber that are due to overflows. This isn't set for * values explicitly entered as floating point values or that were * constant-folded from expressions containing explicit floats. */ uint promoted_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Assignment Types. */ enum tc_asitype_t { /* simple assignment: x = 1 */ TC_ASI_SIMPLE, /* add to: x += 1 */ TC_ASI_ADD, /* subtract from: x -= 1 */ TC_ASI_SUB, /* multiply by: x *= 1 */ TC_ASI_MUL, /* divide by: x /= 1 */ TC_ASI_DIV, /* modulo: x %= 1 */ TC_ASI_MOD, /* bitwise-and with: x &= 1 */ TC_ASI_BAND, /* bitwise-or with: x |= 1 */ TC_ASI_BOR, /* bitwise-xor with: x ^= 1 */ TC_ASI_BXOR, /* shift left: x <<= 1 */ TC_ASI_SHL, /* arithmetic shift right: x >>= 1 */ TC_ASI_ASHR, /* logical shift right: x >>>= 1 */ TC_ASI_LSHR, /* pre-increment */ TC_ASI_PREINC, /* pre-decrement */ TC_ASI_PREDEC, /* post-increment */ TC_ASI_POSTINC, /* post-decrement */ TC_ASI_POSTDEC, /* * Subscript assignment: []= * * This isn't actually an operator in the language, but subscripted * assignments are handled specially because of the way subscript * assignment generates a new container value. So a[b] = c is actually * treated as a = a.operator[]=(b, c). */ TC_ASI_IDX }; /* ------------------------------------------------------------------------ */ /* * Formal parameter type list. For functions with declared formal * parameter types (such as multi-methods), we use this class to keep the * list of type names in the parameter list. */ class CTcFormalTypeList { public: CTcFormalTypeList() { /* no entries in our type list yet */ head_ = tail_ = 0; /* assume this isn't a varargs list */ varargs_ = FALSE; } ~CTcFormalTypeList() { } /* create the decorated name */ void decorate_name(CTcToken *decorated_name, const CTcToken *func_base_name); /* get the first parameter in the list */ class CTcFormalTypeEle *get_first() const { return head_; } /* add a typed variable to the list */ void add_typed_param(const CTcToken *tok); /* add an untyped variable to the list */ void add_untyped_param(); /* add 'n' untyped variables to the list */ void add_untyped_params(int n) { while (n-- > 0) add_untyped_param(); } /* add a trailing ellispsis (varargs indicator) */ void add_ellipsis() { varargs_ = TRUE; } protected: /* add a new list element */ void add(class CTcFormalTypeEle *ele); /* add/tail of parameter list */ class CTcFormalTypeEle *head_, *tail_; /* is this a varargs list? */ int varargs_; }; /* formal parameter type list entry */ class CTcFormalTypeEle { public: CTcFormalTypeEle() { name_ = 0; } CTcFormalTypeEle(const char *name, size_t len); ~CTcFormalTypeEle() { } /* next element in list */ CTcFormalTypeEle *nxt_; /* type name */ char *name_; size_t name_len_; }; /* ------------------------------------------------------------------------ */ /* * Expression Operator Parsers. We construct a tree of these operator * parsers so that we can express the expression grammar in a relatively * compact and declarative notation. */ /* * basic operator parser */ class CTcPrsOp { public: /* * Parse an expression with this operator. Logs an error and * returns non-zero if the expression is not valid; on success, * returns zero. * * Fills in *val with the constant value, if any, of the expression. * If the expression does not have a constant value, *val's type * will be set to TC_CVT_UNKNOWN to indicate this. * * Returns a parse node if successful, or null if an error occurs * and the operator parser is unable to make a guess about what was * intended. */ virtual class CTcPrsNode *parse() const = 0; }; /* * generic left-associative binary operator */ class CTcPrsOpBin: public CTcPrsOp { public: CTcPrsOpBin() { /* no left or right subexpression specified */ left_ = right_ = 0; /* as-yet unknown operator token */ op_tok_ = TOKT_INVALID; } CTcPrsOpBin(tc_toktyp_t typ) { /* remember my operator token */ op_tok_ = typ; } CTcPrsOpBin(const CTcPrsOp *left, const CTcPrsOp *right, tc_toktyp_t typ) { /* remember my left and right sub-operators */ left_ = left; right_ = right; /* remember my operator token */ op_tok_ = typ; } /* parse the binary expression */ class CTcPrsNode *parse() const; /* build a new tree out of our left-hand and right-hand subtrees */ virtual class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const = 0; /* * Try evaluating a constant result. If the two values can be * combined with the operator to yield a constant value result, * create a new parse node for the constant value (or update one of * the given subnodes) and return it. If we can't provide a * constant value, return null. * * By default, we'll indicate that the expression does not have a * valid constant value. */ virtual class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const { /* indicate that we cannot synthesize a constant value */ return 0; } /* get/set my token */ tc_toktyp_t get_op_tok() const { return op_tok_; } void set_op_tok(tc_toktyp_t tok) { op_tok_ = tok; } protected: /* operator that can be parsed for my left-hand side */ const CTcPrsOp *left_; /* operator that can be parsed for my right-hand side */ const CTcPrsOp *right_; /* my operator token */ tc_toktyp_t op_tok_; }; /* * Binary Operator Group. This is a group of operators at a common * precedence level. The group has an array of binary operators that * are all at the same level of precedence; we'll evaluate the left * suboperator, then check the token in the input stream against each of * our group's operators, applying the one that matches, if one matches. */ class CTcPrsOpBinGroup: public CTcPrsOp { public: CTcPrsOpBinGroup(const CTcPrsOp *left, const CTcPrsOp *right, const class CTcPrsOpBin *const *ops) { /* remember my left and right suboperators */ left_ = left; right_ = right; /* remember the operators in my group */ ops_ = ops; } /* parse the expression */ class CTcPrsNode *parse() const; protected: /* find and apply an operator to the parsed left-hand side */ int find_and_apply_op(CTcPrsNode **lhs) const; /* my left and right suboperators */ const CTcPrsOp *left_; const CTcPrsOp *right_; /* group of binary operators at this precedence level */ const class CTcPrsOpBin *const *ops_; }; /* * Binary operator group for comparison operators. This is a similar to * other binary groups, but also includes the special "is in" and "not * in" operators. */ class CTcPrsOpBinGroupCompare: public CTcPrsOpBinGroup { public: CTcPrsOpBinGroupCompare(const class CTcPrsOp *left, const class CTcPrsOp *right, const class CTcPrsOpBin *const *ops) : CTcPrsOpBinGroup(left, right, ops) { } class CTcPrsNode *parse() const; protected: /* parse the 'in' list portion of the expression */ class CTPNArglist *parse_inlist() const; }; /* comma operator */ class CTcPrsOpComma: public CTcPrsOpBin { public: CTcPrsOpComma(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_COMMA) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* logical OR */ class CTcPrsOpOr: public CTcPrsOpBin { public: CTcPrsOpOr(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_OROR) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* logical AND */ class CTcPrsOpAnd: public CTcPrsOpBin { public: CTcPrsOpAnd(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpBin(left, right, TOKT_ANDAND) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* general magnitude comparison operators */ class CTcPrsOpRel: public CTcPrsOpBin { public: CTcPrsOpRel(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* * Get the result true/false value, given the result of the * comparison. For example, if this is a greater-than operator, * this should return TRUE if comp > 0, FALSE otherwise. */ virtual int get_bool_val(int comparison_value) const = 0; }; /* comparison - greater than */ class CTcPrsOpGt: public CTcPrsOpRel { public: CTcPrsOpGt() : CTcPrsOpRel(TOKT_GT) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp > 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - greater than or equal to */ class CTcPrsOpGe: public CTcPrsOpRel { public: CTcPrsOpGe() : CTcPrsOpRel(TOKT_GE) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp >= 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - less than */ class CTcPrsOpLt: public CTcPrsOpRel { public: CTcPrsOpLt() : CTcPrsOpRel(TOKT_LT) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp < 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* comparison - less than or equal to */ class CTcPrsOpLe: public CTcPrsOpRel { public: CTcPrsOpLe() : CTcPrsOpRel(TOKT_LE) { } /* get the boolean value for a comparison sense */ int get_bool_val(int comp) const { return comp <= 0; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; }; /* * Equality/inequality comparison */ class CTcPrsOpEqComp: public CTcPrsOpBin { public: CTcPrsOpEqComp(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const = 0; }; /* * Equality comparison */ class CTcPrsOpEq: public CTcPrsOpEqComp { public: /* start out in C mode - use '==' operator by default */ CTcPrsOpEq() : CTcPrsOpEqComp(TOKT_EQEQ) { } /* set the current equality operator */ void set_eq_op(tc_toktyp_t op) { op_tok_ = op; } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const { return ops_equal; } }; /* * Inequality comparison */ class CTcPrsOpNe: public CTcPrsOpEqComp { public: CTcPrsOpNe() : CTcPrsOpEqComp(TOKT_NE) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* get the boolean value to use if the operands are equal */ virtual int get_bool_val(int ops_equal) const { return !ops_equal; } }; /* * binary arithmetic operators */ class CTcPrsOpArith: public CTcPrsOpBin { public: CTcPrsOpArith(tc_toktyp_t typ) : CTcPrsOpBin(typ) { } CTcPrsOpArith(const CTcPrsOp *left, const CTcPrsOp *right, tc_toktyp_t typ) : CTcPrsOpBin(left, right, typ) { } /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result of the operand applied to constant int values */ virtual long calc_result(long val1, long val2, int &ov) const = 0; /* calculate the result for constant float values */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const = 0; }; /* bitwise OR */ class CTcPrsOpBOr: public CTcPrsOpArith { public: CTcPrsOpBOr(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_OR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 | val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* bitwise XOR */ class CTcPrsOpBXor: public CTcPrsOpArith { public: CTcPrsOpBXor(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_XOR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 ^ val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* bitwise AND */ class CTcPrsOpBAnd: public CTcPrsOpArith { public: CTcPrsOpBAnd(const CTcPrsOp *left, const CTcPrsOp *right) : CTcPrsOpArith(left, right, TOKT_AND) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate the result */ virtual long calc_result(long val1, long val2, int &ov) const { ov = FALSE; return val1 & val2; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * shift left */ class CTcPrsOpShl: public CTcPrsOpArith { public: CTcPrsOpShl() : CTcPrsOpArith(TOKT_SHL) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: long calc_result(long a, long b, int &ov) const { ov = FALSE; return a << b; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * arithmetic shift right */ class CTcPrsOpAShr: public CTcPrsOpArith { public: CTcPrsOpAShr() : CTcPrsOpArith(TOKT_ASHR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: long calc_result(long a, long b, int &ov) const { ov = FALSE; return t3_ashr(a, b); } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * logical shift right */ class CTcPrsOpLShr: public CTcPrsOpArith { public: CTcPrsOpLShr() : CTcPrsOpArith(TOKT_LSHR) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { ov = FALSE; return t3_lshr(a, b); } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const { G_tok->log_error(TCERR_BAD_OP_FOR_FLOAT); return 0; } }; /* * multiply */ class CTcPrsOpMul: public CTcPrsOpArith { public: CTcPrsOpMul() : CTcPrsOpArith(TOKT_TIMES) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int64_t prod = a * b; ov = (prod > (int64_t)INT32MAXVAL || prod < (int64_t)INT32MINVAL); return (long)prod; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * divide */ class CTcPrsOpDiv: public CTcPrsOpArith { public: CTcPrsOpDiv() : CTcPrsOpArith(TOKT_DIV) { } CTcPrsOpDiv(tc_toktyp_t tok) : CTcPrsOpArith(tok) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const; /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * mod - inherit from divide operator to pick up divide-by-zero checking */ class CTcPrsOpMod: public CTcPrsOpDiv { public: CTcPrsOpMod() : CTcPrsOpDiv(TOKT_MOD) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const; /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * add */ class CTcPrsOpAdd: public CTcPrsOpArith { public: CTcPrsOpAdd() : CTcPrsOpArith(TOKT_PLUS) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int32_t sum = (int32_t)(a + b); ov = (a >= 0 ? b > 0 && sum < a : b < 0 && sum > a); return sum; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * subtract */ class CTcPrsOpSub: public CTcPrsOpArith { public: CTcPrsOpSub() : CTcPrsOpArith(TOKT_MINUS) { } /* build a new tree out of our left-hand and right-hand subtrees */ class CTcPrsNode *build_tree(class CTcPrsNode *left, class CTcPrsNode *right) const; /* evaluate constant result */ class CTcPrsNode *eval_constant(class CTcPrsNode *left, class CTcPrsNode *right) const; protected: /* calculate a constant integer result */ long calc_result(long a, long b, int &ov) const { int32_t diff = (int32_t)(a - b); ov = (a >= 0 ? b < 0 && diff < a : b > 0 && diff > a); return diff; } /* calculate a constant float result */ virtual class vbignum_t *calc_result( const class vbignum_t &a, const class vbignum_t &b) const; }; /* * Unary Operators */ class CTcPrsOpUnary: public CTcPrsOp { public: class CTcPrsNode *parse() const; /* * evaluate a constant subscript expression; returns a constant * parse node expression if the subscript can be evaluated to a * compile-time constant, or null if not */ static class CTcPrsNode *eval_const_subscript(class CTcPrsNode *lhs, class CTcPrsNode *subscript); /* * evaluate a constant NOT expression; returns a constant parse node * expression if the logical negation can be evaluated to a * compile-time constant, or null if not */ static class CTcPrsNode *eval_const_not(class CTcPrsNode *lhs); /* parse a string with embedded expressions */ static class CTcPrsNode *parse_embedding(struct CTcEmbedBuilder *builder); /* parse a list */ static class CTcPrsNode *parse_list(); /* parse a primary expression */ static class CTcPrsNode *parse_primary(); /* parse an anonymous function */ static class CTPNAnonFunc *parse_anon_func(int short_form, int is_method); /* parse an in-line object definition */ static class CTPNInlineObject *parse_inline_object(int has_colon); protected: /* embedded expression: parse an expression list */ static CTcPrsNode *parse_embedding_list( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* embedded expression: parse an <<if>> or <<unless>> embedding */ static CTcPrsNode *parse_embedded_if( struct CTcEmbedBuilder *b, int unless, int &eos, struct CTcEmbedLevel *parent); /* parse a single embedded expression */ static CTcPrsNode *parse_embedded_expr( struct CTcEmbedBuilder *b, class CTcEmbedTokenList *tl); /* embedded expression: parse an <<one of>> embedding */ static CTcPrsNode *parse_embedded_oneof( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* embedded expression: parse an <<first time>>...<<only>> embedding */ static CTcPrsNode *parse_embedded_firsttime( struct CTcEmbedBuilder *b, int &eos, struct CTcEmbedLevel *parent); /* create the parse node for a <<one of>> structure */ static CTcPrsNode *create_oneof_node( struct CTcEmbedBuilder *b, class CTPNList *lst, const char *attrs); /* capture an embedded expression to a saved token list */ static void capture_embedded(struct CTcEmbedBuilder *b, class CTcEmbedTokenList *tl); /* * Match an end token for an embedded expression construct - <<end>>, * <<else>>, etc. This version parses captured tokens. */ static int parse_embedded_end_tok(class CTcEmbedTokenList *tl, struct CTcEmbedLevel *parent, const char **open_kw); /* parse an end token directly from the token stream */ static int parse_embedded_end_tok(struct CTcEmbedBuilder *b, struct CTcEmbedLevel *parent, const char **open_kw); /* parse a logical NOT operator */ static class CTcPrsNode *parse_not(CTcPrsNode *sub); /* parse a bitwise NOT operator */ static class CTcPrsNode *parse_bnot(CTcPrsNode *sub); /* parse an address-of operator */ class CTcPrsNode *parse_addr() const; /* parse an arithmetic positive operator */ static class CTcPrsNode *parse_pos(CTcPrsNode *sub); /* parse an arithmetic negative operator */ static class CTcPrsNode *parse_neg(CTcPrsNode *sub); /* parse a pre- or post-increment operator */ static class CTcPrsNode *parse_inc(int pre, CTcPrsNode *sub); /* parse a pre- or post-decrement operator */ static class CTcPrsNode *parse_dec(int pre, CTcPrsNode *sub); /* parse a 'new' operator */ static class CTcPrsNode *parse_new(CTcPrsNode *sub, int is_transient); /* parse a 'delete' operator */ static class CTcPrsNode *parse_delete(CTcPrsNode *sub); /* parse a postfix expression */ static class CTcPrsNode *parse_postfix(int allow_member_expr, int allow_call_expr); /* parse a function or method call */ static class CTcPrsNode *parse_call(CTcPrsNode *lhs); /* parse an argument list */ static class CTPNArglist *parse_arg_list(); /* parse a subscript */ static class CTcPrsNode *parse_subscript(CTcPrsNode *lhs); /* parse a member selection ('.' operator) */ static class CTcPrsNode *parse_member(CTcPrsNode *lhs); /* parse an "inherited" expression */ static class CTcPrsNode *parse_inherited(); /* parse a "delegated" expression */ static class CTcPrsNode *parse_delegated(); }; /* * if-nil operator ?? */ class CTcPrsOpIfnil: public CTcPrsOp { public: CTcPrsOpIfnil() { } class CTcPrsNode *parse() const; }; /* * tertiary conditional operator */ class CTcPrsOpIf: public CTcPrsOp { public: CTcPrsOpIf() { } class CTcPrsNode *parse() const; }; /* * Assignment operators (including the regular assignment, "="/":=", * plus all calculate-and-assign operators: "+=", "-=", etc) */ class CTcPrsOpAsi: public CTcPrsOp { public: CTcPrsOpAsi() { /* start out with the C-mode simple assignment operator */ asi_op_ = TOKT_EQ; } /* parse an assignment */ class CTcPrsNode *parse() const; /* set the current simple assignment operator */ void set_asi_op(tc_toktyp_t tok) { asi_op_ = tok; } private: /* current simple assignment operator */ tc_toktyp_t asi_op_; }; #endif /* TCPRS_H */ �������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmtobj.h�����������������������������������������������������������������������0000644�0001750�0000144�00000106222�11746252437�015212� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMTOBJ.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtobj.h - VM TADS Object implementation Function Notes This implementation assumes a non-relocating memory manager, both for the fixed part (the CVmObject part) and the variable part (the "extension," located in the variable-part heap) of our objects. In the present implementation, the memory manager satisfies this requirement, and there are no plans to change this. The memory manager is designed *in principal* to allow for object relocation (specifically as a means to reduce heap fragmentation), so it's not a foregone conclusion that such a thing will never be implemented. However, given the large memories of modern machines (especially relative to the size of a typical tads application), and given that recent academic research has been calling into question the conventional wisdom that heap fragmentation is actually a problem in practice, we consider the probability that we will want to implement a relocation memory manager low, and thus we feel it's better to exploit the efficiencies of using and storing direct object pointers in some places in this code. Modified 10/30/98 MJRoberts - Creation */ #ifndef VMTOBJ_H #define VMTOBJ_H #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* forward-declare our main class */ class CVmObjTads; /* ------------------------------------------------------------------------ */ /* * TADS-Object image file data. The image file state is loaded into an * image object data block, and we set up our own internal data based on * it at load time. The image file data block is arranged as follows: * *. UINT2 superclass_count *. UINT2 load_image_property_count *. UINT2 flags *. UINT4 superclass_1 *. ... *. UINT4 superclass_N *. UINT2 load_image_property_ID_1 *. DATAHOLDER load_image_property_value_1 *. ... *. UINT2 load_image_property_ID_N *. DATAHOLDER load_image_property_value_N */ /* superclass structure for object extension */ struct vm_tadsobj_sc { vm_obj_id_t id; CVmObjTads *objp; }; /* * For our in-memory object extension, we use a structure that stores the * object data. We store the properties in a hash table keyed on property * ID. */ struct vm_tadsobj_hdr { /* allocate */ static vm_tadsobj_hdr *alloc(VMG_ class CVmObjTads *self, unsigned short sc_cnt, unsigned short prop_cnt); /* delete */ void free_mem(); /* reallocate an existing object to expand its property table */ static vm_tadsobj_hdr *expand(VMG_ class CVmObjTads *self, vm_tadsobj_hdr *obj); /* * reallocate an existing object to expand its property table to the * given minimum number of property entries */ static vm_tadsobj_hdr *expand_to(VMG_ class CVmObjTads *self, vm_tadsobj_hdr *obj, size_t new_sc_cnt, size_t min_prop_cnt); /* invalidate the cached inheritance path, if any */ void inval_inh_path() { /* if we have an inheritance path cached, forget it */ if (inh_path != 0) { /* forget the path, so that we recalculate it on demand */ t3free(inh_path); inh_path = 0; } } /* find a property entry */ struct vm_tadsobj_prop *find_prop_entry(uint prop); /* allocate a new hash entry */ vm_tadsobj_prop *alloc_prop_entry(vm_prop_id_t prop, const vm_val_t *val, unsigned int flags); /* calculate the hash code for a property */ unsigned int calc_hash(uint prop) const { /* simply take the property ID modulo the table size */ return (unsigned int)(prop & (hash_siz - 1)); } /* check to see if we have the required number of free entries */ int has_free_entries(size_t cnt) const { return cnt <= (size_t)(prop_entry_free - prop_entry_cnt); } /* cache and return the inheritance search path for this object */ struct tadsobj_objid_and_ptr *get_inh_search_path(VMG0_); /* * Inheritance search table. We build and save the search path for any * class with multiple superclasses, because the inheritance path for a * class with multiple base classes can be somewhat time-consuming to * determine. For objects with only one base class, we don't bother * caching a path, since the path is trivial to calculate in these * cases. */ struct tadsobj_objid_and_ptr *inh_path; /* load image object flags (a combination of VMTOBJ_OBJF_xxx values) */ unsigned short li_obj_flags; /* internal object flags (a combination of VMTO_OBJ_xxx values) */ unsigned short intern_obj_flags; /* * Number of hash buckets, and a pointer to the bucket array. (The * hash bucket array is allocated as part of the same memory block as * this structure - we suballocate it from the memory block when * allocating the structure.) 'hash_arr[hash]' points to the head of * a list of property entries with the given hash value. */ unsigned short hash_siz; struct vm_tadsobj_prop **hash_arr; /* * Pointer to our allocation array of hash buckets. We suballocate * this out of our allocation block. (Note that this isn't the hash * table; this is the pool of elements out of which hash table entries * - not buckets, but the entries in the lists pointed to by the * buckets - are allocated.) */ struct vm_tadsobj_prop *prop_entry_arr; /* total number of hash entries allocated */ unsigned short prop_entry_cnt; /* * Index of next available hash entry. Hash entries are never * deleted, so we don't have to worry about returning entries to the * free pool. So, the free pool simply consists of entries from this * index to the maximum index (prop_entry_cnt - 1). * * When we run out of entries, we must reallocate this entire * structure to make room for more. This means that reallocation is * fairly expensive, but this is acceptable because we will always * want to resize the hash table at the same time anyway. We always * resize the hash table on exhausting our current allocation size * because we pick the hash table size based on the expected maximum * number of entries; once we exceed that maximum, we must reconsider * the hash table size. */ unsigned short prop_entry_free; /* * Number of superclasses, and the array of superclasses. We * overallocate the structure to make room for enough superclasses. * (Note that this means the 'sc' field must be the last thing in the * structure.) */ unsigned short sc_cnt; vm_tadsobj_sc sc[1]; }; /* * Tads-object property entry. Each hash table entry points to a linked * list of these entries. */ struct vm_tadsobj_prop { /* my property ID */ vm_prop_id_t prop; /* pointer to the next entry at the same hash value */ vm_tadsobj_prop *nxt; /* flags */ unsigned char flags; /* my value */ vm_val_t val; }; /* * Internal object flags */ /* from load image - object originally came from image file */ #define VMTO_OBJ_IMAGE 0x0001 /* modified - object has been modified since being loaded from image */ #define VMTO_OBJ_MOD 0x0002 /* * Property entry flags */ /* modified - this property is not from the load image file */ #define VMTO_PROP_MOD 0x01 /* we've stored undo for this property since the last savepoint */ #define VMTO_PROP_UNDO 0x02 /* ------------------------------------------------------------------------ */ /* * Load Image Object Flag Values - these values are stored in the image * file object header. */ /* class - the object represents a class, not an instance */ #define VMTOBJ_OBJF_CLASS 0x0001 /* ------------------------------------------------------------------------ */ /* * Initial empty property table size. When we initially load an object, * we'll allocate this many empty slots for modifiable properties. */ const ushort VMTOBJ_PROP_INIT = 16; /* ------------------------------------------------------------------------ */ /* * TADS object interface. */ class CVmObjTads: public CVmObject { friend class CVmMetaclassTads; friend struct tadsobj_sc_search_ctx; friend struct vm_tadsobj_hdr; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object a TadsObject object? */ static int is_tadsobj_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return create_from_stack_intern(vmg_ pc_ptr, argc, FALSE); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* create an object with no initial extension */ static vm_obj_id_t create(VMG_ int in_root_set); /* * Create an object with a given number of superclasses, and a given * number of property slots. The property slots are all initially * allocated to modified properties. */ static vm_obj_id_t create(VMG_ int in_root_set, ushort superclass_count, ushort prop_slots); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* create an instance of this object */ void create_instance(VMG_ vm_obj_id_t self, const uchar **pc_ptr, uint argc); /* determine if the object has a finalizer method */ virtual int has_finalizer(VMG_ vm_obj_id_t /*self*/); /* invoke the object's finalizer */ virtual void invoke_finalizer(VMG_ vm_obj_id_t self); // $$$ testing virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const { if (((vm_tadsobj_hdr *)ext_)->hash_siz == 0) return TRUE; else return (val->typ == VM_OBJ && val->val.obj == self); } virtual uint calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { if (((vm_tadsobj_hdr *)ext_)->hash_siz == 0) return 0; else return (uint)(((ulong)self & 0xffff) ^ (((ulong)self & 0xffff0000) >> 16)); } // $$$ end testing /* get the number of superclasses of this object */ virtual int get_superclass_count(VMG_ vm_obj_id_t self) const { /* * if we have no superclass, inherit the default, since we * inherit from the system TadsObject class; if we have our own * superclasses, return them */ if (get_sc_count() == 0) return CVmObject::get_superclass_count(vmg_ self); else return get_sc_count(); } /* get the nth superclass of this object */ virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t self, int idx) const { /* * if we have no superclass, inherit the default, since we * inherit from the system TadsObject class; if we have our own * superclasses, return them */ if (get_sc_count() == 0) return CVmObject::get_superclass(vmg_ self, idx); else if (idx >= get_sc_count()) return VM_INVALID_OBJ; else return get_sc(idx); } /* determine if I'm a class object */ virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const { return (get_li_obj_flags() & VMTOBJ_OBJF_CLASS) != 0; } /* determine if I'm an instance of the given object */ int is_instance_of(VMG_ vm_obj_id_t obj); /* this object type provides properties */ int provides_props(VMG0_) const { return TRUE; } /* enumerate properties */ void enum_props(VMG_ vm_obj_id_t self, void (*cb)(VMG_ void *ctx, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val), void *cbctx); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* inherit a property */ int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* build my property list */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* * Receive notification of a new savepoint. We keep track of * whether or not we've saved undo information for each modifiable * property in the current savepoint, so that we can avoid saving * redundant undo information when repeatedly changing a property * value (since only the first change in a given savepoint needs to * be recorded). When we start a new savepoint, we obviously * haven't yet stored any undo information for the new savepoint, so * we can simply clear all of the undo records. */ void notify_new_savept() { clear_undo_flags(); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark a reference in an undo record */ void mark_undo_ref(VMG_ struct CVmUndoRecord *undo); /* * remove stale weak references from an undo record -- we keep only * normal strong references, so we don't need to do anything here */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* restore to image file state */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* perform post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); /* determine if the object has been changed since it was loaded */ int is_changed_since_load() const; /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get the nth superclass */ vm_obj_id_t get_sc(uint n) const { return get_hdr()->sc[n].id; } /* get a pointer to the object for the nth superclass */ CVmObjTads *get_sc_objp(VMG_ ushort n) const { return get_hdr()->sc[n].objp; } /* set the nth superclass to the given object */ void set_sc(VMG_ ushort n, vm_obj_id_t obj) { get_hdr()->sc[n].id = obj; get_hdr()->sc[n].objp = (CVmObjTads *)vm_objp(vmg_ obj); } /* static class initialization/termination */ static void class_init(VMG0_); static void class_term(VMG0_); protected: /* create an object with no initial extension */ CVmObjTads() { ext_ = 0; } /* * Create an object with a given number of superclasses, and a given * number of property slots. All property slots are initially * allocated to the modifiable property list. */ CVmObjTads(VMG_ ushort superclass_count, ushort prop_count); /* internal handler to create from stack arguments */ static vm_obj_id_t create_from_stack_intern(VMG_ const uchar **pc_ptr, uint argc, int is_transient); /* * internal handler to create with multiple inheritance from arguments * passed on the stack */ static vm_obj_id_t create_from_stack_multi(VMG_ uint argc, int is_transient); /* get the load image object flags */ uint get_li_obj_flags() const { return get_hdr()->li_obj_flags; } /* set the object flags */ void set_li_obj_flags(ushort flags) { get_hdr()->li_obj_flags = flags; } /* * mark the object as modified; sets the VMTO_OBJ_MOD flag, and adds an * undo record for the transition if the flag wasn't previously set */ void mark_modified(VMG_ class CVmUndo *undo, vm_obj_id_t self); /* * Allocate memory - this replaces any existing extension, so the * caller must take care to free the extension (if one has already * been allocated) before calling this routine. * * If 'from_image' is true, we're allocating memory for use with an * object loaded from an image file, so we'll ignore the superclass * count and leave the image_data pointer in the header unchanged. * If 'from_image' is false, we're allocating memory for a dynamic * object that does not have a presence in the image file, so we'll * allocate space for the superclass list as part of the extension * and set the image_data pointer in the header to refer the extra * space after the modifiable property array and undo bit array. */ void alloc_mem(VMG_ ushort sc_count, ushort mod_prop_count, int from_image); /* get a property from the intrinsic class */ int get_prop_intrinsic(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * Search for a property, continuing a previous search from the given * point. defining_obj is the starting point for the search: we start * searching in the target object's inheritance tree after * defining_obj. This is used to continue an inheritance search from * a given point, as needed for the 'inherited' operator, for example. */ static int search_for_prop_from(VMG_ uint prop, vm_val_t *val, vm_obj_id_t orig_target_obj, vm_obj_id_t *source_obj, vm_obj_id_t defining_obj); /* load the image file properties and superclasses */ void load_image_props_and_scs(VMG_ const char *ptr, size_t siz); /* get/set the superclass count */ ushort get_sc_count() const { return get_hdr()->sc_cnt; } void set_sc_count(ushort cnt) { get_hdr()->sc_cnt = cnt; } /* change the superclass list */ void change_superclass_list(VMG_ const vm_val_t *lst, int cnt); /* clear all undo flags */ void clear_undo_flags(); /* -------------------------------------------------------------------- */ /* * Low-level format management - these routines encapsulate the byte * layout of the object in memory. This is a bit nasty because we * keep the object's contents in the portable image format. */ /* get my header */ inline struct vm_tadsobj_hdr *get_hdr() const { return (vm_tadsobj_hdr *)ext_; } /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - createInstance */ int getp_create_instance(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - createClone */ int getp_create_clone(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluator - createTransientInstance */ int getp_create_trans_instance(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc); /* property evaluator - createInstanceOf */ int getp_create_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluator - createTransientInstanceOf */ int getp_create_trans_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* common handler for createInstance and createTransientInstance */ int getp_create_common(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc, int is_transient); /* common handler for createInstanceOf and createTransientInstanceOf */ int getp_create_multi_common(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc, int is_transient); /* property evaluator - setSuperclassList */ int getp_set_sc_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* object table iteration callback for setSuperclassList */ static void set_sc_cb(VMG_ vm_obj_id_t obj, void *ctx); /* property evaluator - getMethod */ int getp_get_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluator - setMethod */ int getp_set_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc); /* property evaluation function table */ static int (CVmObjTads::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassTads: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "tads-object/030005"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTads(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTads(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTads::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * Intrinsic class modifier object. This object is for use as a modifier * object for an intrinsic class. * * This is a simple subclass of the regular TADS-Object class. The only * difference is that we resolve properties a little differently: unlike * regular TADS Objects, this class is essentially a mix-in, and has no * intrinsic superclass at all. This means that the only place we look * for a property in get_prop is in our property list; we specifically do * not look for an intrinsic property, nor do we look for a superclass * that provides an intrinsic property. */ class CVmObjIntClsMod: public CVmObjTads { friend class CVmMetaclassIntClsMod; public: static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjTads::is_of_metaclass(meta)); } /* is the given object an intrinsic class modifier object? */ static int is_intcls_mod_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* can't create instances of intrinsic class modifiers */ err_throw(VMERR_ILLEGAL_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* inherit a property */ int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc); /* build my property list */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* create an object with no initial extension */ CVmObjIntClsMod() { ext_ = 0; } /* * Create an object with a given number of superclasses, and a given * number of property slots. All property slots are initially * allocated to the modifiable property list. */ CVmObjIntClsMod(VMG_ ushort superclass_count, ushort prop_count) : CVmObjTads(vmg_ superclass_count, prop_count) { } }; /* * Registration table object */ class CVmMetaclassIntClsMod: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "int-class-mod/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIntClsMod(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIntClsMod(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjIntClsMod::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIntClsMod:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * object ID + pointer structure */ struct tadsobj_objid_and_ptr { vm_obj_id_t id; CVmObjTads *objp; }; /* ------------------------------------------------------------------------ */ /* * Queue element for the inheritance path search queue */ struct pfq_ele { /* object ID of this element */ vm_obj_id_t obj; /* pointer to the object */ CVmObjTads *objp; /* next queue element */ pfq_ele *nxt; }; /* allocation page */ struct pfq_page { /* next page in the list */ pfq_page *nxt; /* the elements for this page */ pfq_ele eles[50]; }; /* ------------------------------------------------------------------------ */ /* * Queue for search_for_prop(). This implements a special-purpose work * queue that we use to keep track of the objects yet to be processed in * our depth-first search across the inheritance tree. */ class CVmObjTadsInhQueue { public: CVmObjTadsInhQueue() { init(); } void init() { /* there's nothing in the free list or the queue yet */ head_ = 0; free_ = 0; /* we have no elements yet */ alloc_ = 0; } ~CVmObjTadsInhQueue() { pfq_page *cur; pfq_page *nxt; /* delete all of the allocated pages */ for (cur = alloc_ ; cur != 0 ; cur = nxt) { /* remember the next page */ nxt = cur->nxt; /* free this page */ t3free(cur); } } /* get the head of the queue */ pfq_ele *get_head() const { return head_; } /* remove the head of the queue and return the object ID */ vm_obj_id_t remove_head() { /* if there's a head element, remove it */ if (head_ != 0) { pfq_ele *ele; /* note the element */ ele = head_; /* unlink it from the list */ head_ = head_->nxt; /* link the element into the free list */ ele->nxt = free_; free_ = ele; /* return the object ID from the element we removed */ return ele->obj; } else { /* there's nothing in the queue */ return VM_INVALID_OBJ; } } /* clear the queue */ void clear() { /* move everything from the queue to the free list */ while (head_ != 0) { pfq_ele *cur; /* unlink this element from the queue */ cur = head_; head_ = cur->nxt; /* link it into the free list */ cur->nxt = free_; free_ = cur; } } /* determine if the queue is empty */ int is_empty() const { /* we're empty if there's no head element in the list */ return (head_ == 0); } /* allocate a path from the contents of the queue */ tadsobj_objid_and_ptr *create_path() const { /* count the elements in the queue */ int cnt; pfq_ele *cur; for (cnt = 0, cur = head_ ; cur != 0 ; cur = cur->nxt) { /* only non-nil elements count */ if (cur->obj != VM_INVALID_OBJ) ++cnt; } /* allocate the path */ tadsobj_objid_and_ptr *path = (tadsobj_objid_and_ptr *)t3malloc( (cnt + 1) * sizeof(tadsobj_objid_and_ptr)); /* initialize the path */ tadsobj_objid_and_ptr *dst; for (dst = path, cur = head_ ; cur != 0 ; cur = cur->nxt) { /* only store non-nil elements */ if (cur->obj != VM_INVALID_OBJ) { dst->id = cur->obj; dst->objp = cur->objp; ++dst; } } /* set the null last entry */ dst->id = VM_INVALID_OBJ; dst->objp = 0; /* return the new path */ return path; } /* * Insert an object into the queue. We'll insert after the given * element (null indicates that we insert at the head of the queue). * Returns a pointer to the newly-inserted element. */ pfq_ele *insert_obj(VMG_ vm_obj_id_t obj, CVmObjTads *objp, pfq_ele *ins_pt) { pfq_ele *ele; /* * If the exact same element is already in the queue, delete the * old copy. This will happen in situations where we have multiple * superclasses that all inherit from a common base class: we want * the common base class to come in inheritance order after the * last superclass that inherits from the common base. By deleting * previous queue entries that match new queue entries, we ensure * that the common class will move to follow (in inheritance order) * the last class that derives from it. */ for (ele = head_ ; ele != 0 ; ele = ele->nxt) { /* if this is the same thing we're inserting, remove it */ if (ele->obj == obj) { /* * clear the element (don't unlink it, as this could cause * confusion for the caller, who's tracking an insertion * point and traversal point) */ ele->obj = VM_INVALID_OBJ; ele->objp = 0; /* * no need to look any further - we know we can never have * the same element appear twice in the queue, thanks to * this very code */ break; } } /* allocate our new element */ ele = alloc_ele(); ele->obj = obj; ele->objp = objp; /* insert it at the insertion point */ if (ins_pt == 0) { /* insert at the head */ ele->nxt = head_; head_ = ele; } else { /* insert after the selected item */ ele->nxt = ins_pt->nxt; ins_pt->nxt = ele; } /* return the new element */ return ele; } protected: /* allocate a new element */ pfq_ele *alloc_ele() { pfq_ele *ele; /* if we have nothing in the free list, allocate more elements */ if (free_ == 0) { pfq_page *pg; size_t i; /* allocate another page */ pg = (pfq_page *)t3malloc(sizeof(pfq_page)); /* link it into our master page list */ pg->nxt = alloc_; alloc_ = pg; /* link all of its elements into the free list */ for (ele = pg->eles, i = sizeof(pg->eles)/sizeof(pg->eles[0]) ; i != 0 ; --i, ++ele) { /* link this one into the free list */ ele->nxt = free_; free_ = ele; } } /* take the next element off the free list */ ele = free_; free_ = free_->nxt; /* return the element */ return ele; } /* head of the active queue */ pfq_ele *head_; /* head of the free element list */ pfq_ele *free_; /* * Linked list of element pages. We allocate memory for elements in * blocks, to reduce allocation overhead. */ pfq_page *alloc_; }; #endif /* VMTOBJ_H */ /* * Register the classes */ VM_REGISTER_METACLASS(CVmObjTads) VM_REGISTER_METACLASS(CVmObjIntClsMod) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmvsn.h������������������������������������������������������������������������0000644�0001750�0000144�00000002670�12145504453�015054� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2006 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvsn.h - VM Version Information Function Notes Modified 07/12/99 MJRoberts - Creation */ #ifndef VMVSN_H #define VMVSN_H /* * The VM version number. A VM program can obtain this value through * the get_vm_vsn() function in the T3VM intrinsic function set. * * The value is encoded as a 32-bit value with the major version number * in the high-order 16 bits, the minor version number in the next 8 * bits, and the patch release number in the low-order 8 bits. So, the * release 1.2.3 would be encoded as 0x00010203. */ #define MAKE_VERSION_NUMBER(major,minor,maint) \ (((major) << 16) | ((minor) << 8) | (maint)) #define T3VM_VSN_NUMBER MAKE_VERSION_NUMBER(3,1,3) /* * The VM identification string */ #define T3VM_IDENTIFICATION "mjr-T3" /* * The VM short version string. This contains merely the version number, * in display format. */ #define T3VM_VSN_STRING "3.1.3" /* * The VM banner string. A VM program can obtain this value through the * get_vm_banner() function in the T3VM intrinsic function set. */ /* copyright-date-string */ #define T3VM_BANNER_STRING \ "T3 VM " T3VM_VSN_STRING " - Copyright 1999, 2012 Michael J. Roberts" #endif /* VMVSN_H */ ������������������������������������������������������������������������frobtads-1.2.3/tads3/tcglob.h�����������������������������������������������������������������������0000644�0001750�0000144�00000011466�11774034513�015162� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/tcglob.h,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcglob.h - TADS 3 Compiler globals Function The TADS 3 Compiler uses a number of static variables that are shared by several subsystems. We define these variables as global variables for quick access, and to minimize the number of parameters that are passed around among subsystems. Notes Modified 05/01/99 MJRoberts - creation */ #ifndef TCGLOB_H #define TCGLOB_H /* * If we're not explicitly defining the storage for the globals, define * them as external - this lets everyone pick up external declarations * for all of the globals simply by including this file. */ #ifndef TC_GLOB_DECLARE #define TC_GLOB_DECLARE extern #endif /* host system interface */ TC_GLOB_DECLARE class CTcHostIfc *G_hostifc; /* main compiler driver */ TC_GLOB_DECLARE class CTcMain *G_tcmain; /* the parser */ TC_GLOB_DECLARE class CTcParser *G_prs; /* parse tree node list memory manager */ TC_GLOB_DECLARE class CTcPrsMem *G_prsmem; /* the tokenizer */ TC_GLOB_DECLARE class CTcTokenizer *G_tok; /* * Current code stream - this points to the currently active code stream * object. The active code stream can vary according to what kind of * code we're generating. */ TC_GLOB_DECLARE class CTcCodeStream *G_cs; /* primary generated code stream - for all normal code */ TC_GLOB_DECLARE class CTcCodeStream *G_cs_main; /* static initializer code stream */ TC_GLOB_DECLARE class CTcCodeStream *G_cs_static; /* local variable name stream */ TC_GLOB_DECLARE class CTcDataStream *G_lcl_stream; /* generated data (constant) stream */ TC_GLOB_DECLARE class CTcDataStream *G_ds; /* TADS-Object metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_os; /* Dictionary metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_dict_stream; /* GrammarProduction metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_gramprod_stream; /* BigNumber metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_bignum_stream; /* RexPattern metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_rexpat_stream; /* IntrinsicClass metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_int_class_stream; /* intrinsic class modifier metaclass data stream */ TC_GLOB_DECLARE class CTcDataStream *G_icmod_stream; /* static initializer obj.prop ID stream */ TC_GLOB_DECLARE class CTcDataStream *G_static_init_id_stream; /* target-specific code generator class */ TC_GLOB_DECLARE class CTcGenTarg *G_cg; /* * Run-time metaclass table. When we're doing dynamic compilation, the * interpreter will set this to its live metaclass table for the loaded * program. */ TC_GLOB_DECLARE class CVmMetaTable *G_metaclass_tab; /* * object ID fixup list head, and flag indicating whether to keep object * fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_objfixup; TC_GLOB_DECLARE int G_keep_objfixups; /* * property ID fixup list head, and flag indicating whether to keep * property ID fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_propfixup; TC_GLOB_DECLARE int G_keep_propfixups; /* * enumerator ID fixup list head, and flag indicating whether to keep * enumerator fixups */ TC_GLOB_DECLARE struct CTcIdFixup *G_enumfixup; TC_GLOB_DECLARE int G_keep_enumfixups; /* * Debug mode - if this is true, we're compiling for debugging, so we * must generate additional symbolic information for the debugger. */ TC_GLOB_DECLARE int G_debug; /* disassembly output stream, if disassembly display is desired */ TC_GLOB_DECLARE class CTcUnasOut *G_disasm_out; /* * Image file sizes. For the normal compiler, these are fixed quantities * based on the current VM version we're targeting. For the dynamic * compiler, these are adjusted to match the data from the loaded image * file; this is necessary to ensure that code we generate matches static * code loaded from the image. */ struct tc_image_info { /* size of the method header */ int mhdr; /* size of the exception table entry */ int exc_entry; /* debugger table header size */ int dbg_hdr; /* debugger line table entry size */ int dbg_line; /* debugger frame record size */ int dbg_frame; /* local symbol header size */ int lcl_hdr; /* debug record format version ID */ int dbg_fmt_vsn; }; TC_GLOB_DECLARE tc_image_info G_sizes; /* * Compiler interface to the live VM. In dynamic compilation mode, the VM * supplies the compiler with this object to provide the compiler with * access to VM resources. */ TC_GLOB_DECLARE class CTcVMIfc *G_vmifc; #endif /* VMGLOB_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmtz.h�������������������������������������������������������������������������0000644�0001750�0000144�00000042102�12017751642�014677� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtz.h - TADS time zone management Function TADS uses the IANA zoneinfo database (aka the Olson database) for converting time values between universal time (UTC) and local time representations. This is used in particular in the Date intrinsic class, which provides a bytecode API for time and date conversions, formatting, and arithmetic. This zoneinfo database lets us determine the correct local time, given a UTC time, in just about any regional time zone on just about any date since time zones were invented. It takes into account the ever-changing daylight savings rules as well as historical time zone realignments and redefinitions. We don't just project the current time zone rules back in time, as most operating systems do in their time managers; the zoneinfo database tells us the actual changes that occurred. For example, in the United States, we'll properly start daylight savings time from the last Sunday in April in 1986, but use the second Sunday in April in 2007 and later. Likewise, we'll properly display EWT (Eastern War Time) if asked for a local time in New York in 1943. We use a custom binary version of the zoneinfo database. The TADS source code distribution includes a compiler (written in TADS) that reads the zoneinfo sources files as distributed by IANA and generates our custom binary file. We don't compile the file merely to have it in binary form, but rather because the source form of the information requires quite a lot of work to interpret. It uses a considerable amount of abstraction, both to make the zone histories and rules more readily human-readable and to make them more compact and reusable (e.g., it allows for expressing rules at the national level that span multiple time zones, such as in the US). The main reason to compile the material is to boil down the abstraction to a form that can be used efficiently at run-time. While we're at it we generate a binary file format that's fast and easy to read. This package includes a cache manager that reads zone data from the binary file on demand. The zoneinfo database (and thus our custom binary version of it) contains quite a lot of information, since it has historical records on each of about 450 time zones around the world. It amounts to about 300k of compiled binary data in our format. Rather than read all of that into memory, we'll read it as needed per time zone. Most programs will use only the local time zone of the local system, so in most cases we'll never need to read more than one zone. Notes None Modified 02/05/12 MJRoberts - creation */ #ifndef VMTZ_H #define VMTZ_H #include "t3std.h" #include "vmglob.h" #include "vmobj.h" /* ------------------------------------------------------------------------ */ /* * Time zone cache. This loads time zone data on demand. */ class CVmTimeZoneCache { friend class CVmTimeZone; public: CVmTimeZoneCache(); ~CVmTimeZoneCache(); /* * Parse a timezone name, returning a CVmTimeZone object representing * the zone. This accepts names in the following formats: * *. :local - the local zone * *. UTC+offset - or GMT+ofs, Z+ofs, or simply +ofs: a zone at the *. given hh[:mm[:ss]] offset from UTC. This type of *. zone uses a fixed offset year round and for all *. dates, not tied to any location's history. * *. zoneinfoName - the name of a zoneinfo database entry, such as *. "America/Los_Angeles". This type uses the zone *. history from the database. */ class CVmTimeZone *parse_zone(VMG_ const char *name, size_t len); /* * Get a time zone object by name from the zoneinfo database; returns * the cached copy if present, otherwise loads the entry from disk. */ class CVmTimeZone *get_db_zone(VMG_ const char *name, size_t len); class CVmTimeZone *get_db_zone(VMG_ const char *name) { return get_db_zone(vmg_ name, strlen(name)); } /* * Get a time zone object by GMT offset, in seconds. Returns a zone * that represents the fixed GMT offset, not for any particular * location. */ class CVmTimeZone *get_gmtofs_zone(VMG_ int32_t gmtofs_secs); /* * Get a time zone object by abbreviation. This searches the * abbreviation list for a match, and returns the corresponding * timezone object if found. We also fill in the GMT offset for the * abbreviation - this is required when the zone is specified by * abbreviation because an abbreviation carries a standard time or * daylight time designation, which we'd normally infer from the zone * and date information. An explicit daylight or standard time * designation overrides the time that's normally in effect on the * given date. For example, "1/1/2012 12:00 PDT" corresopnds to * 1/1/2012 19:00 GMT, whereas "1/1/2012 12:00" America/Los_Angeles * corresponds to 20:00 GMT. The difference is that standard time is * normally in effect on that date: a time specified by location * (America/Los_Angeles) is interpreted as a wall clock time for that * date according to the prevailing standard/daylight rules for the * zone, whereas a time explicitly in PDT is interpreted as daylight * time regardless of whether daylight or standard time is actually in * effect in the zone on the given date. */ class CVmTimeZone *get_zone_by_abbr(VMG_ int32_t *gmtofs_ms, const char *name, size_t len); /* * Create a missing database zone. This creates a placeholder zone * representing a zone imported from a saved game using a different * version of the database that includes a zone that's not defined in * the version we're using. */ class CVmTimeZone *create_missing_zone( VMG_ const char *name, size_t namelen, const os_tzinfo_t *desc); /* open the zoneinfo binary file */ static osfildef *open_zoneinfo_file(VMG0_); /* * Get the system default local time zone. Returns a CVmTimeZone * object representing the local zone. */ class CVmTimeZone *get_local_zone(VMG0_); /* is the given zone the special local zone? */ int is_local_zone(class CVmTimeZone *zone) const { return zone == local_zone_; } protected: /* load the zoneinfo database index from the binary file */ int load_db_index(VMG0_); /* get or load the CVmTimeZone object for a hash entry */ CVmTimeZone *tz_from_hash(VMG_ class ZoneHashEntry *entry); /* parse a "UTC+-h[:mm[:ss]]" offset string into a zone */ CVmTimeZone *parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len); /* parse a zone spec in POSIX TZ format (EST5, EST5EDT, EST5EDT4) */ CVmTimeZone *parse_zone_posixTZ(VMG_ const char *name, size_t len); /* parse an [+-]h[:mm[:ss]] time/offset value */ int parse_hhmmss(int32_t &ofs, const char *&name, size_t &len, int sign_required); /* search for a set of zone specifications */ class ZoneHashEntry *search( VMG_ const struct ZoneSearchSpec *specs, int cnt); class ZoneHashEntry *search( VMG_ const struct ZoneSearchSpec *specs, class AbbrHashEntry **e, int cnt, class ZoneHashEntry *zone); /* have we attempted to load the index yet? */ int index_loaded_; /* did we successfully load the index? */ int index_loaded_ok_; /* * Cached local zone object. Determining the local time zone can be a * non-trivial amount of work, so we cache this after we figure it out * the first time. */ class CVmTimeZone *local_zone_; /* zone name hash table for zones loaded from the zoneinfo database */ class CVmHashTable *db_tab_; /* zone name hash table for synthesized zones - GMT+ofs, EST5EDT, etc */ class CVmHashTable *synth_tab_; /* abbreviation hash table */ class CVmHashTable *abbr_tab_; /* head of linked list of *loaded* zone objects */ class ZoneHashEntry *first_loaded_; /* zone index, loaded from the binary file */ char *zone_bytes_; /* link index, loaded from the binary file */ char *link_bytes_; /* abbreviation mapping index, loaded from the binary file */ char *abbr_bytes_; }; /* ------------------------------------------------------------------------ */ /* * Time type. This describes the settings for a time zone during the * period covered by a transition. */ struct vmtz_ttype { vmtz_ttype() { } vmtz_ttype(const struct vmtzquery *query); vmtz_ttype(int32_t gmtofs, int32_t save, const char *fmt) { this->gmtofs = gmtofs; this->save = save; this->fmt = fmt; } /* * standard time ofset, in milliseconds - add this to UTC to get local * standard time */ int32_t gmtofs; /* * daylight time offset, in milliseconds - add to local standard time * to get local wall clock time */ int32_t save; /* time zone format string abbreviation ("EDT", etc) */ const char *fmt; }; /* * Transition - this records a change in a time zone's rules. Each * transition covers the period from its FROM date (inclusive) to (but not * including) the next transition's FROM date. */ struct vmtz_trans { vmtz_trans() { } vmtz_trans(int32_t dayno, int32_t daytime, vmtz_ttype *ttype) { this->dayno = dayno; this->daytime = daytime; this->ttype = ttype; } /* * Date/time of the transition, relative to 3/1/0000 UTC; the time is * in milliseconds after midnight. This is starting time for the * transition - it's the moment that our ttype comes into effect. */ int32_t dayno; int32_t daytime; /* compare my date to the given date/time (time in milliseconds) */ int compare_to(int32_t dayno, int32_t daytime, int local) const; /* create a List object representing the transition */ vm_obj_id_t get_as_list(VMG0_) const; /* time zone settings in effect on and after this transition */ struct vmtz_ttype *ttype; }; /* * Time zone daylight savings rule. This describes a switch between * standard time and daylight time. Any given rule fires once a year. * Rules apply to the period after the last enumerated transition in the * time zone; this allows us to project the current rules that apply in the * zone forward in time indefinitely, without having to enumerate all of * the possible transitions (which would be impractical given that our time * type can in principle represent millions of years into the future). */ struct vmtz_rule { /* the format abbreviation to use when the rule is in effect */ const char *fmt; /* the gmt offset for standard time during this rule, in milliseconds */ int32_t gmtofs; /* daylight savings time offset from standard time, in milliseconds */ int32_t save; /* the month the rule takes effect (1=January) */ char mm; /* * When in the month the rule takes effect: * *. 0 -> takes effect on the fixed day of the month <dd> *. 1 -> last <weekday> of the month *. 2 -> first <weekday> on or after <dd> *. 3 -> first <weekday> on or before <dd> */ char when; /* * The day of the month the rule takes effect (1-366). (This can be * greater than 31, because it's also used to encode POSIX TZ-style * rules with "Julian day of year counting Feb 29". Our calendar * calculator can handle overflows in the day of month, carrying them * automatically into subsequent months, so we can encode the Nth day * of the year as simply January Nth, and we'll get the right result * even if N > 31. */ short dd; /* the day of the week the rule takes effect (1=Sunday) */ char weekday; /* * Time zone for the effective time/date: 0 -> local wall clock time * (with the zone settings for the period immediately before the rule * takes effect); 0x40 -> local standard time (for the period * immediately before the rule takes effect), 0x80 -> UTC */ uchar at_zone; /* time of day the rule takes effect, as milliseconds after midnight */ int32_t at; /* resolve this rule in the given year */ void resolve(int32_t &dayno, int32_t &daytime, int year, const vmtz_rule *prev_rule); /* set from an OS DST start/end date descriptor */ void set(const os_tzrule_t *desc, const char *abbr, int32_t gmtofs, int32_t save); }; /* ------------------------------------------------------------------------ */ /* * Time zone info query structure */ struct vmtzquery { /* starting date/time of the transition or rule that applies */ int32_t start_dayno; int32_t start_daytime; /* GMT offset for standard time, in milliseconds */ int32_t gmtofs; /* daylight savings offset from standard time, in milliseconds */ int32_t save; /* zone name abbreviation ("EST", etc) */ const char *abbr; /* set from a transition item */ void set(const vmtz_trans *trans) { set(trans->ttype, trans->dayno, trans->daytime); } /* set from a time type */ void set(const vmtz_ttype *tty, int32_t dayno, int32_t daytime) { start_dayno = dayno; start_daytime = daytime; gmtofs = tty->gmtofs; save = tty->save; abbr = tty->fmt; } /* set from a rule */ void set(const vmtz_rule *rule, int32_t dayno, int32_t daytime) { start_dayno = dayno; start_daytime = daytime; gmtofs = rule->gmtofs; save = rule->save; abbr = rule->fmt; } }; /* ------------------------------------------------------------------------ */ /* * Time zone object. */ class CVmTimeZone { public: /* create from a timezone descriptor */ CVmTimeZone(const os_tzinfo_t *desc); /* destruction */ ~CVmTimeZone(); /* load from the zoneinfo database file */ static CVmTimeZone *load(VMG_ class ZoneHashEntry *entry); /* get the primary name of the zone */ const char *get_name(size_t &len) const; /* create a List object with this zone's names and aliases */ vm_obj_id_t get_name_list(VMG0_) const; /* get the single history item that applies to the given date */ vm_obj_id_t get_history_item(VMG_ int32_t dayno, int32_t daytime) const; /* create a List object populated with the transition list */ vm_obj_id_t get_history_list(VMG0_) const; /* create a List object populated with the ongoing rules */ vm_obj_id_t get_rule_list(VMG0_) const; /* country code, if available (ISO3166 two-letter country code) */ const char *country() const { return country_; } /* coordinates, if available (+DDMM+DDDMM or +DDMMSS+DDDMMSS format) */ const char *coords() const { return coords_; } /* zone.tab description/comment */ const char *desc() const { return desc_; } /* * Convert a local wall clock time in this zone to UTC. 'daytime' is * in milliseconds after midnight. */ void local_to_utc(int32_t &dayno, int32_t &daytime); /* * Convert a UTC time to local time in this zone. Returns the format * abbreviation for the converted time (e.g., PDT for Pacific Daylight * Time). 'daytime' is in milliseconds after midnight. Fills in * 'tzofs' with the time zone's offset from UTC, in milliseconds. */ const char *utc_to_local(int32_t &dayno, int32_t &daytime, int32_t &tzofs); /* * Query the time zone information at the given time/right now. The * time is in milliseconds after midnight. */ void query(vmtzquery *result, int32_t dayno, int32_t daytime, int local) const; void query(vmtzquery *result) const; protected: /* create from binary data loaded from the file */ CVmTimeZone(class ZoneHashEntryDb *entry, const char *file_data, unsigned int trans_cnt, unsigned int type_cnt, unsigned int rule_cnt, unsigned int abbr_bytes); /* create a synthetic zone */ CVmTimeZone(class ZoneHashEntrySynth *entry); /* initialize from an OS timezone descriptor */ void init(const os_tzinfo_t *desc); /* my hash table entry */ class ZoneHashEntry *hashentry_; /* my name; used only if we don't have a hash entry */ char *name_; /* transition list */ int trans_cnt_; vmtz_trans *trans_; /* time type list */ int ttype_cnt_; vmtz_ttype *ttype_; /* rule list */ int rule_cnt_; vmtz_rule *rule_; /* abbreviations */ char *abbr_; /* country code */ char country_[3]; /* coordinates */ char coords_[16]; /* comments/description from zone.tab */ char *desc_; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmbt3_nd.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000004415�11665777423�015770� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbt3_nd.cpp - T3 VM system interface function set - non-debugger version Function Notes Modified 03/11/00 MJRoberts - Creation */ #include <time.h> #include "t3std.h" #include "vmtype.h" #include "vmbif.h" #include "vmbift3.h" #include "vmstack.h" #include "vmrun.h" #include "charmap.h" #include "vmdatasrc.h" #include "vmlog.h" /* * Debug Trace */ void CVmBifT3::debug_trace(VMG_ uint argc) { /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* pop the flags and see what we're being asked to do */ switch(pop_int_val(vmg0_)) { case T3DBG_CHECK: /* check arguments */ check_argc(vmg_ argc, 1); /* we're just being asked if the debugger is present - it's not */ retval_nil(vmg0_); break; case T3DBG_BREAK: /* check arguments */ check_argc(vmg_ argc, 1); /* tell the caller there's no debugger */ retval_nil(vmg0_); break; case T3DBG_LOG: { /* check arguments and retrieve the string */ check_argc(vmg_ argc, 2); const char *str = G_stk->get(0)->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_TYPE_BIF); /* decode and skip the length prefix */ int len = vmb_get_len(str); str += VMB_LEN; /* convert the string to the local file character set */ char *lstr; size_t llen = G_cmap_to_file->map_utf8_alo(&lstr, str, len); /* log the message */ vm_log(vmg_ lstr, llen); /* done with the mapped string */ t3free(lstr); /* discard the string */ G_stk->discard(); } /* no return value */ retval_nil(vmg0_); break; default: /* anything else just returns nil, to allow for future expansion */ G_stk->discard(argc - 1); retval_nil(vmg0_); break; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/tcprs_rt.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000004564�12014216026�016071� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs_rt.cpp - stubs for parser functions not needed in run-time Function Interpreters that include "eval()" functionality require a portion of the compiler, including code body and expression compilation. This module provides stubs for functions that aren't needed for interpreter configurations, but which are referenced from the parts of the compiler we do need, to resolve symbols at link time. Many of these stubs are simply never called in interpreter builds, because the conditions that would trigger their invocation can't occur; others just need to provide defaults or other simplified functionality when called; others generate errors, because the functionality they provide is specifically not supported in interpreter builds. Notes Modified 02/02/00 MJRoberts - Creation */ #include "tctarg.h" #include "tcprs.h" #include "vmerr.h" #include "vmerrnum.h" #include "tctok.h" /* ------------------------------------------------------------------------ */ /* * Simple stubs for functions not used in the dynamic compiler */ /* * Debug records aren't needed when we're compiling code in the debugger * itself - debugger-generated code can't itself be debugged */ void CTPNStmBase::add_debug_line_rec() { } void CTPNStmBase::add_debug_line_rec(CTcTokFileDesc *, long) { } /* * Add an entry to the global symbol table. We can't define new global * symbols in an interactive debug session, so this does nothing. */ void CTcPrsSymtab::add_to_global_symtab(CTcPrsSymtab *, CTcSymbol *) { } int CTPNStmObjectBase::parse_nested_obj_prop( CTPNObjProp* &, int *, tcprs_term_info *, const CTcToken *, int) { return FALSE; } /* * Set the sourceTextGroup mode. This doesn't apply in the debugger, since * #pragma isn't allowed. */ void CTcParser::set_source_text_group_mode(int) { } /* * we don't need static object generation, since we use G_vmifc interface * to generate live objects in the VM */ CTcSymObj *CTcParser::add_gen_obj_stat(CTcSymObj *) { return 0; } void CTcParser::add_gen_obj_prop_stat( CTcSymObj *, CTcSymProp *, const CTcConstVal *) { } ��������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmstr.h������������������������������������������������������������������������0000644�0001750�0000144�00000066643�12007551000�015053� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMSTR.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstr.h - VM dynamic string implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #ifndef VMSTR_H #define VMSTR_H #include "vmglob.h" #include "vmobj.h" /* * String object */ class CVmObjString: public CVmObject { friend class CVmMetaclassString; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object reference a string object? */ static int is_string_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create from stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get my datatype when converted to constant data */ virtual vm_datatype_t get_convert_to_const_data_type() const { return VM_SSTRING; } /* create a string with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create a string to hold a string of the given byte length */ static vm_obj_id_t create(VMG_ int in_root_set, size_t bytelen); /* create from a constant UTF-8 string */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t bytelen); /* create from a wide unicode string */ static vm_obj_id_t create(VMG_ int in_root_set, const wchar_t *str, size_t charlen); /* create from a byte stream, treating the bytes as Latin-1 characters */ static vm_obj_id_t create_latin1(VMG_ int in_root_set, class CVmDataSource *src); /* create from a string, mapping through the given character mapper */ static vm_obj_id_t create(VMG_ int in_root_set, const char *str, size_t bytelen, CCharmapToUni *cmap); /* * For construction: get a pointer to the string's underlying * buffer. Returns a pointer into which the caller can write. The * buffer starts after the length prefix. */ char *cons_get_buf() const { return ext_ + 2; } /* get the current construction length in bytes */ size_t cons_get_len() const { return vmb_get_len(ext_); } /* * Ensure space for a string under construction. This expands the * buffer if necessary. 'ptr' is a pointer into our buffer, giving the * current write position, and 'len' is the amount of data we want to * add. If we already have enough space, we'll simply return 'ptr'. * If not, we'll reallocate the buffer with enough space for the added * chunk plus the given overhead margin, and return a new pointer that * points to the same offset in the new buffer as 'ptr' did in the * original. */ char *cons_ensure_space(VMG_ char *ptr, size_t len, size_t margin); /* * Append a string to the buffer during construction, expanding the * buffer if necessary. 'ptr' is a pointer into our buffer to where * the string is to be appended. Returns the updated pointer to the * end of the appended data. */ char *cons_append(VMG_ char *ptr, const char *addstr, size_t addlen, size_t margin); /* append a character to the buffer during construction */ char *cons_append(VMG_ char *ptr, wchar_t ch, size_t margin); /* * Shrink the buffer to the given actual size to finalize construction. * 'ptr' is a pointer into the construction buffer; we'll reallocate * the buffer so that it's just big enough for the text up to the byte * before 'ptr', and set the string length accordingly. * * If the buffer is within a reasonable margin of the actual used size, * we'll ignore the reallocation request. Reallocation takes some work * (to find new memory and copy the string contents), so if the memory * savings won't be substantial, it's more efficient just to waste the * memory. In all likelihood the string will be collected as garbage * eventually anyway, so the extra space will probably only be tied up * temporarily. * * The caller must not call cons_set_len() prior to calling this, * because we take the current length from our internal length element. */ void cons_shrink_buffer(VMG_ char *ptr); /* * For construction: set my length. This can be used if the string * stored is smaller than the buffer allocated. This cannot be used to * expand the buffer, since this merely writes the length prefix and * does not reallocate the buffer. * * When used with a pointer, the pointer must point within our buffer; * we'll set the length according to the offset of the pointer from the * start of the buffer. This can be used for dynamic construction with * the final write pointer after filling in the buffer's contents. */ void cons_set_len(size_t len) { vmb_put_len(ext_, len); } void cons_set_len(char *ptr) { vmb_put_len(ext_, ptr - ext_ - 2); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* undo operations - strings are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { }; void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* reference operations - strings reference no other objects */ void mark_refs(VMG_ uint state) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t) { ext_ = (char *)ptr; } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * add a value to the string -- this creates a new string by * appending the value to this string */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * Get a string representation of the object. This is trivial for a * string object - we simply return our extension, which contains * the string in the required format. */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* we are the string object */ new_str->set_obj(self); /* return our extension directly */ return ext_; } /* convert a value to string via reflection services in the bytecode */ static const char *reflect_to_str( VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, const char *fmt, ...); /* get the underlying string */ const char *get_as_string(VMG0_) const { return ext_; } /* cast to integer */ virtual long cast_to_int(VMG0_) const; /* cast to number */ virtual void cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const; /* parse a string as an integer value or (optionally) a BigNumber */ static void parse_num_val(VMG_ vm_val_t *retval, const char *str, size_t len, int radix, int int_only); /* * Static routine to add a value to a string constant. Creates a * new string by appending the given value to the given string * constant. The string constant must be stored in portable format: * the first two bytes are the length prefix, in UINT2 format, * giving the length of the string's contents not counting the * prefix itself; immediately following the length prefix are the * bytes of the string's contents. */ static void add_to_str(VMG_ vm_val_t *result, const vm_val_t *self, const vm_val_t *val); /* * Check a value for equality. We will match any constant string * that contains the same text as our string, and any other string * object with the same text. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* * Compare the string to another value. If the other value is a * constant string or string object, we'll perform a lexical * comparison of the string; other types are not comparable to * strings, so we'll throw an error for any other type. */ int compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const; /* calculate a hash */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * Convert a value to a string. Throws an error if the value is not * convertible to a string. * * The result is stored in the given buffer, if possible, in portable * string format (with a portable UINT2 length prefix followed by the * string's bytes). If the buffer is not provided or is not large * enough to contain the result, we will allocate a new string object * and return its contents; since the string object will never be * referenced by anyone, it will be deleted in the next garbage * collection pass. In any case, we will return a pointer to a buffer * containing the result string. * * We'll fill in *new_obj with the new string object value, or nil if * we don't create a new string; this allows the caller to protect the * allocated object from garbage collection if necessary. * * 'flags' is a combination of TOSTR_xxx values (see vmobj.h). */ static const char *cvt_to_str(VMG_ vm_val_t *new_obj, char *result_buf, size_t result_buf_size, const vm_val_t *val, int radix, int flags); /* * Convert an integer to a string, storing the result in the given * buffer in portable string format (with length prefix). The radix * can be from 2 to 36. * * If is_signed is true, we'll show a hyphen as the first character. * Otherwise we'll treat the value as unsigned. * * For efficiency, we store the number at the end of the buffer (this * makes it easy to generate the number, since we need to generate * numerals in reverse order). We return a pointer to the result, * which may not start at the beginning of the buffer. * * 'flags' is a combination of TOSTR_xxx flags (see vmobj.h). */ static char *cvt_int_to_str(char *buf, size_t buflen, int32_t inval, int radix, int flags); /* * Allocate a string buffer large enough to hold a given value. * We'll use the provided buffer if possible. * * If the provided buffer is null or is not large enough, we'll * allocate a new string object with a large enough buffer to hold * the value, and return the object's extension as the buffer. * * The buffer size and requested size are in bytes. * * If we allocate a new object, we'll set new_obj to the object * value; otherwise we'll set new_obj to nil. */ static char *alloc_str_buf(VMG_ vm_val_t *new_obj, char *buf, size_t buf_size, size_t required_size); /* * Constant string equality test routine. Compares the given * constant string (in portable format, with leading UINT2 length * prefix followed by the string's text in UTF8 format) to the other * value. Returns true if the values are lexically identical, false * if not or if the other value is not a string of some kind. */ static int const_equals(VMG_ const char *str, const vm_val_t *val); /* * Constant string hash value calculation */ static uint const_calc_hash(const char *str); /* * Constant string magnitude comparison routine. Compares the given * constant string (in portable format) to the other value. Returns * a positive value if the constant string is lexically greater than * the other value, a negative value if the constant string is * lexically less than the other value, or zero if the two values * are identical. Throws an error for any other type of value. */ static int const_compare(VMG_ const char *str, const vm_val_t *val); /* * Find a substring within a string. Returns a pointer to to the start * of the substring within the string, or null if the substring isn't * found. If 'idxp' is non-null, we'll fill in *idxp with the * character index, starting at zero for the first character, of the * substring within the string. * * start_idx is the 1-based index where we start the search. If this * is negative, it's an index from the end of the string (-1 for the * last character). * * Both strings are in standard constant string format, with UINT2 * length prefixes. */ static const char *find_substr( VMG_ const char *str, int32_t start_idx, const char *substr, size_t *idxp); /* * Find a substring or RexPattern. Returns a pointer to the matching * substring, or null if no match is found. * * 'basestr' is the base string we're searching, and 'str' and 'len' * give the starting point and remaining length of the substring of * 'basestr' where the search actually begins. 'basestr' and 'str' * point directly to the bytes to search, not a VMB_LEN prefix (which * is why we have 'len' as a separate argument). We won't actually * search the portion of the base string before 'str', but we need to * know where the overall string starts to properly handle certain * regex assertions (e.g., '^'). * * 'substr' is the tads-style (VMB_LEN-prefixed) substring we're * looking for; if 'substr' is null, 'pat' is the regular expression * object to match, otherwise 'pat' is ignored. * * 'match_idx' returns with the CHARACTER index from 'str' (starting at * 0 for the first byte of 'str') of the substring we found, if * successful; otherwise this is undefined. * * 'match_len' returns with the BYTE length of the substring we * matched, if successful; otherwise this is undefined. */ static const char *find_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, class CVmObjPattern *pat, int *match_idx, int *match_len); /* find the last matching substring or pattern */ static const char *find_last_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, class CVmObjPattern *pat, int *match_idx, int *match_len); /* * Evaluate a property of a constant string value. Returns true if * we successfully evaluated the property, false if the property is * not one of the properties that the string class defines. */ static int const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, vm_prop_id_t prop, vm_obj_id_t *srcobj, uint *argc); /* evaluate a property */ virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* property evaluator - undefined property */ static int getp_undef(VMG_ vm_val_t *, const vm_val_t *, const char *, uint *) { return FALSE; } /* property evaluator - get the length */ static int getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - extract a substring */ static int getp_substr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toUpper */ static int getp_upper(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toLower */ static int getp_lower(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toTitleCase */ static int getp_toTitleCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - toFoldedCase */ static int getp_toFoldedCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* common handler for case conversions (toUpper, toLower, etc) */ static int gen_getp_case_conv(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, const wchar_t *(*conv)(wchar_t)); /* property evaluator - find substring */ static int getp_find(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* common handler for find() and findLast() */ template<int dir> static inline int find_common( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - convert to unicode */ static int getp_to_uni(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - htmlify */ static int getp_htmlify(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - startsWith */ static int getp_starts_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - endsWith */ static int getp_ends_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - mapToByteArray */ static int getp_to_byte_array(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findReplace() - replace substring */ static int getp_replace(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - splice */ static int getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - split */ static int getp_split(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - specialsToHtml */ static int getp_specialsToHtml(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - specialsToText */ static int getp_specialsToText(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - urlEncode */ static int getp_urlEncode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - urlDecode */ static int getp_urlDecode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - sha256 hash */ static int getp_sha256(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - md5 hash */ static int getp_md5(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - packBytes */ static int getp_packBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return static_getp_packBytes(vmg_ retval, argc); } /* static property - packBytes */ static int static_getp_packBytes(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - unpackBytes */ static int getp_unpackBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - compareTo */ static int getp_compareTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - compareIgnoreCase */ static int getp_compareIgnoreCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findLast */ static int getp_findLast(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - findAll */ static int getp_findAll(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); /* property evaluator - match */ static int getp_match(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); protected: /* create a string with no initial contents */ CVmObjString() { ext_ = 0; } /* create with a given buffer size in bytes */ CVmObjString(VMG_ size_t bytelen); /* create from a constant UTF-8 string */ CVmObjString(VMG_ const char *str, size_t bytelen); /* * Set the length of the string. This can be used after a string is * constructed to set the size of the actual stored string. */ void set_length(size_t bytelen) { vmb_put_len(ext_, bytelen); } /* copy bytes into the string buffer */ void copy_into_str(const char *str, size_t bytelen) { memcpy(ext_ + VMB_LEN, str, bytelen); } /* copy bytes into the string buffer starting at the given byte offset */ void copy_into_str(size_t ofs, const char *str, size_t bytelen) { memcpy(ext_ + VMB_LEN + ofs, str, bytelen); } /* * Copy bytes from the byte array into the string buffer, treating the * bytes as Latin-1 characters. Returns the number of bytes used in * the output buffer. If the output exceeds the available length in * 'len', we won't store any bytes past 'len', but we'll still * calculate the full needed length and return it. Call with len == 0 * to scan the string and get the required allocation length. */ static size_t copy_latin1_to_string(char *str, size_t len, class CVmDataSource *src); /* common handler for specialsToText and specialsToHtml */ static int specialsTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, int html); /* property evaluation function table */ static int (*func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * A constant string is exactly like an ordinary string, except that our * contents come from the constant pool. We store a pointer directly to * our constant pool data rather than making a separate copy. The only * thing we have to do differently from an ordinary string is that we don't * delete our extension when we're deleted, since our extension is really * just a pointer into the constant pool. */ class CVmObjStringConst: public CVmObjString { public: /* notify of deletion */ void notify_delete(VMG_ int /*in_root_set*/) { /* * do nothing, since our extension is just a pointer into the * constant pool */ } /* create from constant pool data */ static vm_obj_id_t create(VMG_ const char *const_ptr); protected: /* construct from constant pool data */ CVmObjStringConst(VMG_ const char *const_ptr) { /* point our extension directly to the constant pool data */ ext_ = (char *)const_ptr; } }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassString: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "string/030008"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjString(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjString(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjString::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjString::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMSTR_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjString) ���������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmundo.h�����������������������������������������������������������������������0000644�0001750�0000144�00000020730�11677362237�015223� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/VMUNDO.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmundo.h - VM undo manager Function Notes Modified 10/29/98 MJRoberts - Creation */ #ifndef VMUNDO_H #define VMUNDO_H #include "t3std.h" #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * Undo record. The VM undo manager maintains a list of these records; * each record contains the information necessary to undo one particular * data change in an object. Each object class (TADS object, array, * etc) keeps a linked list of undo records for its own actions. */ struct CVmUndoRecord { /* * Object ID. All undoable state is stored in objects, hence each * undo record is associated with an object. */ vm_obj_id_t obj; /* * Identifier - the meaning of this member is defined by the object * that created the record. For TADS objects, this is a property ID * specifying the property that was changed; for arrays, it's the * index of the array element that changed. Other object * metaclasses may assign different meanings. */ union { /* property value key */ vm_prop_id_t prop; /* integer value key */ uint32_t intval; /* pointer value key */ void *ptrval; } id; /* * Data value. This is the data value prior to the change. In some * cases, the type may be 'empty' to indicate that the original * value did not exist; for example, when a property is added to a * TADS object and the property was not present previously, we * create an undo record with type 'empty' to indicate that the * property must be deleted on undo. */ vm_val_t oldval; }; /* * Undo meta-record. Each slot in the undo array is one of these * meta-records, which is a union of the normal undo record and an undo * link pointer. The first undo record in any savepoint is always a * link pointer; all of the other records are normal undo records. */ union CVmUndoMeta { /* the first entry in each savepoint is a link entry */ struct { /* pointer to the first record in the previous savepoint */ CVmUndoMeta *prev_first; /* pointer to the first record in the next savepoint */ CVmUndoMeta *next_first; } link; /* every entry but the first in a savepoint is an ordinary undo record */ CVmUndoRecord rec; }; /* ------------------------------------------------------------------------ */ /* * Undo manager */ class CVmUndo { public: /* * create the undo manager, specifying the upper limit for memory * usage and retained savepoints */ CVmUndo(size_t undo_record_cnt, uint max_savepts); /* delete the undo manager */ ~CVmUndo(); /* * Create a savepoint. If doing so would exceed the maximum number * of savepoints, we'll discard the oldest savepoint. */ void create_savept(VMG0_); /* get the current savepoint ID */ vm_savept_t get_cur_savept() const { return cur_savept_; } /* determine how many savepoints exist */ uint get_savept_cnt() const { return savept_cnt_; } /* drop all undo information */ void drop_undo(VMG0_); /* * Allocate and initialize an undo record with a property key or * with an integer key. * * We check that the undo manager has an active savepoint. It may * not have an active savepoint if no savepoint has been created, * all undo records have been applied, or we've run out of space in * the current savepoint; in any of these cases, since there's no * savepoint, we don't need to keep any undo records. */ void add_new_record_prop_key(VMG_ vm_obj_id_t obj, vm_prop_id_t key, const vm_val_t *val); void add_new_record_int_key(VMG_ vm_obj_id_t obj, uint32_t key, const vm_val_t *val); /* * Add a new undo record with a pointer key; returns true if the * record was created, false if not. (This version of * add_new_record_xxx_key returns an indication of success because * the pointer value might have been allocated, in which case the * caller must deallocate the value if we didn't add an undo * record.) */ int add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key, const vm_val_t *val); /* add a new record with a pointer key and no separate value data */ int add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key) { /* set up a nil value to fill the value slot in the record */ vm_val_t nilval; nilval.set_nil(); /* save the record */ return add_new_record_ptr_key(vmg_ obj, key, &nilval); } /* * Apply undo to the latest savepoint. After applying the undo * records, we delete all undo records to the savepoint; this leaves * the previous savepoint as the new most recent savepoint, so * repeating this will undo to the previous savepoint. */ void undo_to_savept(VMG0_); /* * Garbage collection: mark referenced objects. This goes through * all of the undo records; for each undo record, we have the object * that owns the undo record mark as referenced any object to which * the record refers. */ void gc_mark_refs(VMG0_); /* * Garbage collection: delete obsolete weak references. This goes * through all of the undo records; for each undo record, if the * object that owns the record is unreachable, we'll delete the undo * record; otherwise, we'll tell the object that owns the record to * clean up the record if it contains an unreachable weak reference. */ void gc_remove_stale_weak_refs(VMG0_); private: /* * Allocate an undo record. Returns null if no records are * available, in which case the caller should simply skip saving * undo. */ CVmUndoMeta *alloc_rec(VMG0_); /* * increment a record pointer, wrapping back at the end of the array * to the first record */ void inc_rec_ptr(CVmUndoMeta **rec) { /* increment the record pointer */ ++(*rec); /* if it's at the end of the array, wrap back to the start */ if (*rec == rec_arr_ + rec_arr_size_) *rec = rec_arr_; } /* * Add a new record and return the new record. If we don't have an * active savepoint, this will return null, since there's no need to * keep undo. */ CVmUndoRecord *add_new_record(VMG_ vm_obj_id_t controlling_obj); /* discard the oldest savepoint */ void drop_oldest_savept(VMG0_); /* current savepoint ID */ vm_savept_t cur_savept_; /* * Number of active savepoint ID's. If this is zero, it means that * we have no undo information and we're not keeping any undo * information. If this is one, it means that we only have the * current savepoint stored. */ uint savept_cnt_; /* * The nominal maximum number of savepoints to keep. Any time that * we attempt to create a new savepoint, and doing so would exceed * this number of stored savepoints, we will discard the oldest * savepoint. Note that we may not always store this many * savepoints, since we will also discard old savepoints when we run * out of available undo records attempting to create a new * savepoint. */ vm_savept_t max_savepts_; /* * Pointer to the first record in the current savepoint. This isn't * a real undo record -- it's a link record. */ CVmUndoMeta *cur_first_; /* * Pointer to the first undo record in the oldest savepoint. This * is a link record. */ CVmUndoMeta *oldest_first_; /* pointer to the next free undo record */ CVmUndoMeta *next_free_; /* * Master array of undo records, and the number of records in this * list. We pre-allocate a fixed array of these records. If we run * out, we start discarding old undo. */ CVmUndoMeta *rec_arr_; size_t rec_arr_size_; }; #endif /* VMUNDO_H */ ����������������������������������������frobtads-1.2.3/tads3/vmtobj.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000234576�12145504453�015553� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMTOBJ.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtobj.cpp - TADS object implementation Function Notes Modified 10/30/98 MJRoberts - Creation */ #include <stdlib.h> #include <assert.h> #include <stdarg.h> #include "t3std.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmobj.h" #include "vmtobj.h" #include "vmundo.h" #include "vmtype.h" #include "vmfile.h" #include "vmstack.h" #include "vmrun.h" #include "vmpredef.h" #include "vmmeta.h" #include "vmlst.h" #include "vmintcls.h" #include "vmbif.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Allocate a new object header */ vm_tadsobj_hdr *vm_tadsobj_hdr::alloc(VMG_ CVmObjTads *self, unsigned short sc_cnt, unsigned short prop_cnt) { ushort hash_siz; size_t siz; size_t i; vm_tadsobj_hdr *hdr; char *mem; vm_tadsobj_prop **hashp; /* * Figure the size of the hash table to allocate. Empirically, a good * size seems to be about one hash slot per property. */ if (prop_cnt <= 2) hash_siz = 2; else if (prop_cnt <= 4) hash_siz = 4; else if (prop_cnt <= 8) hash_siz = 8; else if (prop_cnt <= 16) hash_siz = 16; else if (prop_cnt <= 32) hash_siz = 32; else if (prop_cnt <= 64) hash_siz = 64; else if (prop_cnt <= 128) hash_siz = 128; else hash_siz = 256; /* * increase the requested property count to the hash size at a minimum * - this will avoid the need to reallocate the object to make room * for more properties until we'd have to resize the hash table, at * which point we have to reallocate the object anyway */ if (prop_cnt < hash_siz) prop_cnt = hash_siz; /* figure the size of the structure we need */ siz = sizeof(vm_tadsobj_hdr) + (sc_cnt - 1) * sizeof(hdr->sc[0]) + (hash_siz) * sizeof(hdr->hash_arr[0]) + prop_cnt * sizeof(hdr->prop_entry_arr[0]); /* allocate the memory */ hdr = (vm_tadsobj_hdr *)G_mem->get_var_heap()->alloc_mem(siz, self); /* * Set up to suballocate out of this block. Free memory in the block * starts after our structure and the array of superclass entries. */ mem = (char *)&hdr->sc[sc_cnt]; /* clear our flags and load-image flags */ hdr->li_obj_flags = 0; hdr->intern_obj_flags = 0; /* the object has no precalculated inheritance path yet */ hdr->inh_path = 0; /* suballocate the hash buckets */ hdr->hash_siz = hash_siz; hdr->hash_arr = (vm_tadsobj_prop **)mem; /* clear out the hash buckets */ for (hashp = hdr->hash_arr, i = hash_siz ; i != 0 ; ++hashp, --i) *hashp = 0; /* move past the memory taken by the hash buckets */ mem = (char *)(hdr->hash_arr + hash_siz); /* suballocate the array of hash entries */ hdr->prop_entry_cnt = prop_cnt; hdr->prop_entry_arr = (vm_tadsobj_prop *)mem; /* all entries are currently free, so point to the first entry */ hdr->prop_entry_free = 0; /* remember the superclass count */ hdr->sc_cnt = sc_cnt; /* return the new object */ return hdr; } /* * Free */ void vm_tadsobj_hdr::free_mem() { /* if I have a precalculated inheritance path, delete it */ if (inh_path != 0) t3free(inh_path); } /* * Expand an existing object header to make room for more properties */ vm_tadsobj_hdr *vm_tadsobj_hdr::expand(VMG_ CVmObjTads *self, vm_tadsobj_hdr *hdr) { unsigned short prop_cnt; /* * Move up to the next property count increment. If we're not huge, * simply double the current size. If we're getting large, expand by * 50%. */ prop_cnt = hdr->prop_entry_cnt; if (prop_cnt <= 128) prop_cnt *= 2; else prop_cnt += prop_cnt/2; /* expand to the new size */ return expand_to(vmg_ self, hdr, hdr->sc_cnt, prop_cnt); } /* * Expand an existing header to the given minimum property table size */ vm_tadsobj_hdr *vm_tadsobj_hdr::expand_to(VMG_ CVmObjTads *self, vm_tadsobj_hdr *hdr, size_t new_sc_cnt, size_t new_prop_cnt) { vm_tadsobj_hdr *new_hdr; size_t i; vm_tadsobj_prop *entryp; /* allocate a new object at the expanded property table size */ new_hdr = alloc(vmg_ self, (ushort)new_sc_cnt, (ushort)new_prop_cnt); /* copy the superclasses from the original object */ memcpy(new_hdr->sc, hdr->sc, (hdr->sc_cnt < new_sc_cnt ? hdr->sc_cnt : new_sc_cnt) * sizeof(hdr->sc[0])); /* use the same flags from the original object */ new_hdr->li_obj_flags = hdr->li_obj_flags; new_hdr->intern_obj_flags = hdr->intern_obj_flags; /* * if the superclass count is changing, we're obviously changing the * inheritance structure, in which case the old cached inheritance path * is invalid - delete it if so */ if (new_sc_cnt != hdr->sc_cnt) hdr->inval_inh_path(); /* copy the old inheritance path (if we still have one) */ new_hdr->inh_path = hdr->inh_path; /* * Run through all of the existing properties and duplicate them in the * new object, to build the new object's hash table. Note that the * free index is inherently equivalent to the count of properties in * use. */ for (i = hdr->prop_entry_free, entryp = hdr->prop_entry_arr ; i != 0 ; --i, ++entryp) { /* add this property to the new table */ new_hdr->alloc_prop_entry(entryp->prop, &entryp->val, entryp->flags); } /* delete the old header */ G_mem->get_var_heap()->free_mem(hdr); /* return the new header */ return new_hdr; } /* * Allocate an entry for given property from the free pool. The caller is * responsible for checking that there's space in the free pool. We do * not check for an existing entry with the same caller ID, so the caller * is responsible for making sure the property doesn't already exist in * our table. */ vm_tadsobj_prop *vm_tadsobj_hdr::alloc_prop_entry( vm_prop_id_t prop, const vm_val_t *val, unsigned int flags) { /* get the hash code for the property */ unsigned int hash = calc_hash(prop); /* use the next free entry */ vm_tadsobj_prop *entry = &prop_entry_arr[prop_entry_free]; /* link this entry into the list for its hash bucket */ entry->nxt = hash_arr[hash]; hash_arr[hash] = entry; /* count our use of the free entry */ ++prop_entry_free; /* set the new entry's property ID */ entry->prop = prop; /* set the value and flags */ entry->val = *val; entry->flags = (unsigned char)flags; /* return the entry */ return entry; } /* * Find an entry */ vm_tadsobj_prop *vm_tadsobj_hdr::find_prop_entry(uint prop) { /* scan the list of entries in this bucket */ for (vm_tadsobj_prop *entry = hash_arr[calc_hash(prop)] ; entry != 0 ; entry = entry->nxt) { /* if this entry matches, return it */ if (entry->prop == prop) return entry; } /* didn't find it */ return 0; } /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassTads metaclass_reg_obj; CVmMetaclass *CVmObjTads::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjTads:: *CVmObjTads::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjTads::getp_undef, &CVmObjTads::getp_create_instance, &CVmObjTads::getp_create_clone, &CVmObjTads::getp_create_trans_instance, &CVmObjTads::getp_create_instance_of, &CVmObjTads::getp_create_trans_instance_of, &CVmObjTads::getp_set_sc_list, &CVmObjTads::getp_get_method, &CVmObjTads::getp_set_method }; /* * Function table indices. We only need constant definitions for these * for our static methods, since in other cases we translate through the * function table. */ const int PROPIDX_CREATE_INSTANCE = 1; const int PROPIDX_CREATE_CLONE = 2; const int PROPIDX_CREATE_TRANS_INSTANCE = 3; const int PROPIDX_CREATE_INSTANCE_OF = 4; const int PROPIDX_CREATE_TRANS_INSTANCE_OF = 5; /* ------------------------------------------------------------------------ */ /* * Static class initialization */ void CVmObjTads::class_init(VMG0_) { /* allocate the inheritance analysis object */ VM_IFELSE_ALLOC_PRE_GLOBAL( G_tadsobj_queue = new CVmObjTadsInhQueue(), G_tadsobj_queue->init()); } /* * Static class termination */ void CVmObjTads::class_term(VMG0_) { /* delete the inheritance analysis object */ VM_IF_ALLOC_PRE_GLOBAL( delete G_tadsobj_queue; G_tadsobj_queue = 0; ) } /* ------------------------------------------------------------------------ */ /* * Static creation methods */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjTads::create_from_stack_intern( VMG_ const uchar **pc_ptr, uint argc, int is_transient) { vm_obj_id_t id; CVmObjTads *obj; vm_val_t val; vm_obj_id_t srcobj; /* check arguments */ if (argc == 0) { /* no superclass argument - create a base object */ val.set_nil(); } else { /* * We have arguments. The first is the superclass argument, which * must be an object or nil. Retrieve it and make sure it's * valid. */ G_stk->pop(&val); if (val.typ != VM_OBJ && val.typ != VM_NIL) err_throw(VMERR_OBJ_VAL_REQD_SC); /* if it's the invalid object, treat it as nil */ if (val.typ == VM_OBJ && val.val.obj == VM_INVALID_OBJ) val.set_nil(); /* we cannot create an instance of a transient object */ if (val.typ != VM_NIL && G_obj_table->is_obj_transient(val.val.obj)) err_throw(VMERR_BAD_DYNAMIC_NEW); /* count the removal of the first argument */ --argc; } /* * create the object - this type of construction is never used for * root set objects */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* make the object transient if desired */ if (is_transient) G_obj_table->set_obj_transient(id); /* * create a TADS object with the appropriate number of superclasses * (0 if no superclass was specified, 1 if one was), and the default * number of initial mutable properties */ obj = new (vmg_ id) CVmObjTads(vmg_ (val.typ == VM_NIL ? 0 : 1), VMTOBJ_PROP_INIT); /* set the object's superclass */ if (val.typ != VM_NIL) obj->set_sc(vmg_ 0, val.val.obj); /* * Invoke the object's "construct" method, passing it the arguments * that are still on the stack. If the new object doesn't define or * inherit the "construct" method, simply push the new object * reference onto the stack directly. */ if (obj->get_prop(vmg_ G_predef->obj_construct, &val, id, &srcobj, 0)) { vm_val_t srcobj_val; vm_val_t id_val; const uchar *dummy_pc_ptr; uint caller_ofs; vm_rcdesc rc, *rcp; /* use the null PC pointer if the caller didn't supply one */ if (pc_ptr == 0) { /* there's no caller PC pointer - use a dummy value */ pc_ptr = &dummy_pc_ptr; caller_ofs = 0; /* set up the recursive context */ rcp = &rc; rc.init_ret(vmg_ "TadsObject.construct"); } else { /* get the caller's offset */ caller_ofs = G_interpreter->pc_to_method_ofs(*pc_ptr); rcp = 0; } /* * A "construct" method is defined - have the interpreter invoke * it, which will set up the interpreter to start executing its * byte-code. This is all we need to do, since we assume and * require that the constructor will return the new object as * its return value when it's done. */ srcobj_val.set_obj(srcobj); id_val.set_obj(id); *pc_ptr = G_interpreter->get_prop(vmg_ caller_ofs, &srcobj_val, G_predef->obj_construct, &id_val, argc, rcp); } else { /* * there's no "construct" method defined - if we have any * arguments, its an error */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* leave the new object value in R0 */ G_interpreter->get_r0()->set_obj(id); } /* return the new object */ return id; } /* create an object with no initial extension */ vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjTads(); return id; } /* * Create an object with a given number of superclasses, and a given * property table size. Each superclass must be set before the object * can be used, and the property table is initially empty. * * This form is used to create objects dynamically; this call is never * used to load an object from an image file. */ vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set, ushort superclass_count, ushort prop_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjTads(vmg_ superclass_count, prop_count); return id; } /* * Create an instance based on multiple superclasses, using the * createInstanceOf() interface. Arguments are passed on the stack. Each * argument gives a superclass, and optionally the arguments for its * inherited constructor. If an argument is a simple object/class, then we * won't inherit that object's constructor at all. If an argument is a * list, then the first element of the list gives the class, and the * remaining elements of the list give the arguments to pass to that * class's inherited constructor. */ vm_obj_id_t CVmObjTads::create_from_stack_multi( VMG_ uint argc, int is_transient) { /* allocate an object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, TRUE, FALSE); if (is_transient) G_obj_table->set_obj_transient(id); /* create the new object */ CVmObjTads *obj = new (vmg_ id) CVmObjTads( vmg_ (ushort)argc, VMTOBJ_PROP_INIT); /* push the new object, for garbage collector protection */ G_interpreter->push_obj(vmg_ id); /* set the superclasses */ for (ushort i = 0 ; i < argc ; ++i) { /* * get this argument (it's at i+1 because of the extra item we * pushed for gc protection) */ vm_val_t *arg = G_stk->get(i + 1); /* * if it's a list, the superclass is the first element; otherwise, * the argument is the superclass */ vm_val_t sc; const char *lstp = arg->get_as_list(vmg0_); if (lstp != 0) { /* it's a list - the first element is the superclass */ CVmObjList::index_list(vmg_ &sc, lstp, 1); } else { /* not a list - the argument is the superclass */ sc = *arg; } /* make sure it's a TadsObject */ if (sc.typ != VM_OBJ || !is_tadsobj_obj(vmg_ sc.val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* can't create an instance of a transient object */ if (G_obj_table->is_obj_transient(sc.val.obj)) err_throw(VMERR_BAD_DYNAMIC_NEW); /* set this superclass */ obj->set_sc(vmg_ i, sc.val.obj); } /* set up the resursive invocation context */ vm_rcdesc rc("TadsObject.contructMulti"); /* * The new object is ready to go. All that remains is invoking any * inherited construtors that the caller wants us to invoked. * Constructor invocation is indicated by passing a list argument for * the corresponding superclass, so run through the arguments and * invoke each indicated constructor. */ for (ushort i = 0 ; i < argc ; ++i) { /* get the next argument */ vm_val_t *arg = G_stk->get(i + 1); /* if it's not a list, we don't want to invoke this constructor */ const char *lstp = arg->get_as_list(vmg0_); if (lstp == 0) { /* no constructor call is wanted - just keep going */ continue; } /* get the superclass from the list */ vm_val_t sc; CVmObjList::index_list(vmg_ &sc, lstp, 1); /* get the number of list elements */ uint lst_cnt = vmb_get_len(lstp); /* make sure we have room to push the arguments */ if (!G_stk->check_space(lst_cnt - 1)) err_throw(VMERR_STACK_OVERFLOW); /* * push the list elements in reverse order; don't push the first * element, since it's the superclass itself rather than an * argument to the constructor */ for (uint j = lst_cnt ; j > 1 ; --j) CVmObjList::index_and_push(vmg_ lstp, j); /* * Invoke the constructor via a recursive call into the VM. Note * that we're inheriting the property, so 'self' is the new object, * but the 'target' object is the superclass whose constructor * we're invoking. */ vm_val_t new_obj_val; new_obj_val.set_obj(id); G_interpreter->get_prop(vmg_ 0, &sc, G_predef->obj_construct, &new_obj_val, lst_cnt - 1, &rc); } /* discard the arguments plus our own gc protection */ G_stk->discard(argc + 1); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Constructors */ /* * Create an object with a given number of superclasses, and a given * property table size. The superclasses must be individually set * before the object can be used, and the property table is initially * empty. * * This constructor is used only when creating a new object dynamically, * and is never used to load an object from an image file. */ CVmObjTads::CVmObjTads(VMG_ ushort superclass_count, ushort prop_count) { /* allocate our header */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, superclass_count, prop_count); } /* ------------------------------------------------------------------------ */ /* * receive notification of deletion */ void CVmObjTads::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0) { /* tell the header to delete its memory */ get_hdr()->free_mem(); /* delete the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Create an instance of this class */ void CVmObjTads::create_instance(VMG_ vm_obj_id_t self, const uchar **pc_ptr, uint argc) { /* push myself as the superclass */ G_stk->push()->set_obj(self); /* use the normal stack creation routine */ create_from_stack(vmg_ pc_ptr, argc+1); } /* ------------------------------------------------------------------------ */ /* * Determine if the object has a finalizer method */ int CVmObjTads::has_finalizer(VMG_ vm_obj_id_t self) { vm_val_t val; vm_obj_id_t srcobj; /* * look up the finalization method - if it's defined, and it's a * method, invoke it; otherwise do nothing */ return (G_predef->obj_destruct != VM_INVALID_PROP && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)); } /* ------------------------------------------------------------------------ */ /* * Invoke the object's finalizer */ void CVmObjTads::invoke_finalizer(VMG_ vm_obj_id_t self) { vm_val_t val; vm_obj_id_t srcobj; /* * look up the finalization method - if it's defined, and it's a * method, invoke it; otherwise do nothing */ if (G_predef->obj_destruct != VM_INVALID_PROP && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* * invoke the finalizer in a protected frame, to ensure that we * catch any exceptions that are thrown out of the finalizer */ err_try { vm_val_t srcobj_val; vm_val_t self_val; /* * Invoke the finalizer. Use a recursive VM invocation, * since the VM must return to the garbage collector, not to * what it was doing in the enclosing stack frame. */ srcobj_val.set_obj(srcobj); self_val.set_obj(self); vm_rcdesc rc("TadsObject.finalize"); G_interpreter->get_prop(vmg_ 0, &srcobj_val, G_predef->obj_destruct, &self_val, 0, &rc); } err_catch_disc { /* silently ignore the error */ } err_end; } } /* ------------------------------------------------------------------------ */ /* * Clear the undo flags for all properties */ void CVmObjTads::clear_undo_flags() { vm_tadsobj_prop *entry; uint i; vm_tadsobj_hdr *hdr = get_hdr(); /* scan all property entries and clear their undo flags */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* clear this entry's undo flag */ entry->flags &= ~VMTO_PROP_UNDO; } } /* ------------------------------------------------------------------------ */ /* * Set a property */ void CVmObjTads::set_prop(VMG_ CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* look for an existing property entry */ vm_tadsobj_prop *entry = hdr->find_prop_entry(prop); /* check for an existing entry for the property */ vm_val_t oldval; if (entry != 0) { /* found an existing entry - note the old value */ oldval = entry->val; /* store the new value in the existing entry */ entry->val = *val; } else { /* * We didn't find an existing entry for the property, so we have to * add a new one. If we don't have any free property slots left, * expand the object to create some more property slots. */ if (!hdr->has_free_entries(1)) { /* expand the extension to make room for more properties */ ext_ = (char *)vm_tadsobj_hdr::expand(vmg_ this, hdr); /* get the reallocated header */ hdr = get_hdr(); } /* allocate a new entry */ entry = hdr->alloc_prop_entry(prop, val, 0); /* * The old value didn't exist, so mark it emtpy, with an intval of * zero. The zero indicates that this is a newly created property * entry, and thus should be deleted on undo. */ oldval.set_empty(); oldval.val.intval = 0; } /* * If we already have undo for this property for the current * savepoint, as indicated by the undo flag for the property, we don't * need to save undo for this change, since we already have an undo * record in the current savepoint. Otherwise, we need to add an undo * record for this savepoint. */ if (undo != 0 && (entry->flags & VMTO_PROP_UNDO) == 0) { /* save the undo record */ undo->add_new_record_prop_key(vmg_ self, prop, &oldval); /* mark the property as now having undo in this savepoint */ entry->flags |= VMTO_PROP_UNDO; /* * If the entry wasn't previously marked as modified, remember this * by storing an extra 'empty' undo record with intval 1 after the * record we just saved. When we see an 'empty' undo value with an * intval of 1, we recognize it as this special marker that tells * us to remove the 'modified' flag from the property. Note that * we don't need to bother if the old value was already empty, * since that deletes the whole property on undo, making the * 'modified' flag irrelevant. */ if ((entry->flags & VMTO_PROP_MOD) == 0 && oldval.typ != VM_EMPTY) { /* store an empty undo record with intval 1 */ oldval.set_empty(); oldval.val.intval = 1; undo->add_new_record_prop_key(vmg_ self, prop, &oldval); } } /* mark the property entry as modified */ entry->flags |= VMTO_PROP_MOD; /* mark the overall object as modified */ mark_modified(vmg_ undo, self); } /* ------------------------------------------------------------------------ */ /* * Mark the object as modified */ void CVmObjTads::mark_modified(VMG_ CVmUndo *undo, vm_obj_id_t self) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* if we're not already marked as modified, set the flag */ if ((hdr->intern_obj_flags & VMTO_OBJ_MOD) == 0) { /* add the modified flag */ hdr->intern_obj_flags |= VMTO_OBJ_MOD; /* add an undo record for it */ if (undo != 0) { /* * property == 'invalid' and value == int(1) is our special * record indicating an unmodified-to-modified transition */ vm_val_t v; v.set_int(1); undo->add_new_record_prop_key(vmg_ self, VM_INVALID_PROP, &v); } } } /* ------------------------------------------------------------------------ */ /* * Build a list of my properties */ void CVmObjTads::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { size_t cnt; size_t idx; CVmObjList *lst; vm_tadsobj_prop *entry; vm_tadsobj_hdr *hdr = get_hdr(); /* the next free index is also the number of properties we have */ cnt = hdr->prop_entry_free; /* allocate a list big enough for all of our properties */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); /* get the list object, property cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* add our image file properties to the list */ for (idx = 0, entry = hdr->prop_entry_arr ; cnt != 0 ; --cnt, ++entry) { /* make a value for this property ID */ vm_val_t val; val.set_propid(entry->prop); /* add it to the list */ lst->cons_set_element(idx++, &val); } /* set the final length of the list */ lst->cons_set_len(idx); } /* ------------------------------------------------------------------------ */ /* * Call a static method. */ int CVmObjTads::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { int idx; /* convert the property to an index in our method vector */ idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* check what property they're evaluating */ switch(idx) { case PROPIDX_CREATE_INSTANCE: case PROPIDX_CREATE_TRANS_INSTANCE: { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(result, argc, &desc)) return TRUE; /* * They want to create an instance of TadsObject, which is * just a plain base object with no superclass. Push null as * the base class and call our from-stack constructor. */ result->set_obj(create_from_stack_intern( vmg_ pc_ptr, 0, idx == PROPIDX_CREATE_TRANS_INSTANCE)); } /* handled */ return TRUE; case PROPIDX_CREATE_INSTANCE_OF: case PROPIDX_CREATE_TRANS_INSTANCE_OF: { static CVmNativeCodeDesc desc(0, 0, TRUE); uint in_argc = (argc == 0 ? 0 : *argc); /* check arguments */ if (get_prop_check_argc(result, argc, &desc)) return TRUE; /* * They want to create an instance of TadsObject, which is just * a plain base object with no superclass. Push null as the * base class and call our from-stack constructor. */ result->set_obj(create_from_stack_multi( vmg_ in_argc, idx == PROPIDX_CREATE_TRANS_INSTANCE_OF)); } /* handled */ return TRUE; default: /* it's not one of ours; inherit the base class statics */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Superclass inheritance search context. This keeps track of our position * in searching the inheritance tree of a given class. */ struct tadsobj_sc_search_ctx { /* initialize at a given object */ tadsobj_sc_search_ctx(VMG_ vm_obj_id_t obj, CVmObjTads *objp) { /* start at the given object */ cur = obj; curhdr = objp->get_hdr(); /* we have no path yet */ path_sc = 0; } /* current object ID */ vm_obj_id_t cur; /* if we're using a multiple-inheritance search path, the path */ tadsobj_objid_and_ptr *path_sc; /* current object header */ vm_tadsobj_hdr *curhdr; /* * Find the given property, searching our superclass list until we find * an object providing the property. Returns true if found, and fills * in *val and *source. Returns false if not found. */ int find_prop(VMG_ uint prop, vm_val_t *val, vm_obj_id_t *source) { /* keep going until we find the property */ do { /* look for this property in the current object */ vm_tadsobj_prop *entry = curhdr->find_prop_entry(prop); if (entry != 0) { /* we found the property - return it */ *val = entry->val; *source = cur; return TRUE; } } while (to_next(vmg0_)); /* we've exhausted the search path - return failure */ return FALSE; } /* * Move to the next superclass. This updates 'cur' to refer to the * next object in inheritance order. Returns true if there is a next * element, false if not. * * It's legal to call this with 'cur' uninitialized, as we don't need * the old value of 'cur' to do our work. This is important because it * allows a search position to be initialized knowing only an object's * 'this' pointer, not its object ID. */ int to_next(VMG0_) { /* check for a path */ try_again: if (path_sc == 0) { /* * we're not working on a path at all - this means we're * working directly on a (so far) single-inheritance superclass * chain, so simply follow the chain up to the next superclass */ /* we have no path, so look at our object's superclasses */ switch(curhdr->sc_cnt) { case 1: /* we have exactly one superclass, so traverse to it */ { const vm_tadsobj_sc *sc = curhdr->sc; cur = sc->id; curhdr = sc->objp->get_hdr(); } return TRUE; case 0: /* we have no superclasses, so there's nowhere to go */ return FALSE; default: /* we have multiple superclasses, so set up a search path */ path_sc = curhdr->get_inh_search_path(vmg0_); /* start over with the search path */ goto try_again; } } else if ((cur = path_sc->id) != VM_INVALID_OBJ) { /* * we're working on a path, and we have elements remaining - * move on to the next element */ curhdr = path_sc->objp->get_hdr(); ++path_sc; /* got it */ return TRUE; } else { /* * we're working on a path, and we're out of elements - we have * nowhere else to go */ return FALSE; } } /* * Skip to the given object. If we find the object in the path, we'll * leave the current position set to the given object and return true; * if we fail to find the object, we'll return false. */ int skip_to(VMG_ vm_obj_id_t target) { /* keep going until the current object matches the target */ while (cur != target) { /* move to the next element */ if (!to_next(vmg0_)) { /* there's nothing left - return failure */ return FALSE; } } /* found it */ return TRUE; } }; /* * Get the inheritance search path for this object */ tadsobj_objid_and_ptr *vm_tadsobj_hdr::get_inh_search_path(VMG0_) { /* if we have a cached path, return it */ if (inh_path != 0) return inh_path; /* get the queue builder global */ CVmObjTadsInhQueue *q = G_tadsobj_queue; /* * We haven't already cached a search path for this object, so build * the search path now and save it for future searches. Start by * clearing the work queue. */ q->clear(); /* we're not yet processing the first element */ pfq_ele *q_ele = 0; /* start with self */ vm_tadsobj_hdr *curhdr = this; /* keep going until we run out of queue elements */ for (;;) { /* get the superclass count for this object */ uint cnt = curhdr->sc_cnt; /* insert my superclasses right after me */ pfq_ele *q_ins = q_ele; /* enqueue the current object's superclasses */ uint i; vm_tadsobj_sc *scp; for (i = 0, scp = curhdr->sc ; i < cnt ; ++i, ++scp) { /* get the current superclass */ vm_obj_id_t sc = scp->id; CVmObjTads *scobj = scp->objp; /* if it's not a TadsObject, skip it */ if (scobj->get_metaclass_reg() != CVmObjTads::metaclass_reg_) continue; /* enqueue this superclass */ q_ins = q->insert_obj(vmg_ sc, scobj, q_ins); } /* move to the next valid element */ do { /* get the next queue element */ q_ele = (q_ele == 0 ? q->get_head() : q_ele->nxt); /* if we're out of elements, we're done */ if (q_ele == 0) goto done; } while (q_ele->obj == VM_INVALID_OBJ); /* get the object header for the queue item */ curhdr = q_ele->objp->get_hdr(); } done: /* create and cache a linearized path for the queue, and return it */ return inh_path = q->create_path(); } /* * Search for a property via inheritance, starting after the given defining * object. */ int CVmObjTads::search_for_prop_from(VMG_ uint prop, vm_val_t *val, vm_obj_id_t orig_target_obj, vm_obj_id_t *source_obj, vm_obj_id_t defining_obj) { /* set up a search position */ tadsobj_sc_search_ctx curpos(vmg_ orig_target_obj, (CVmObjTads *)vm_objp(vmg_ orig_target_obj)); /* if we have a starting point, skip past it */ if (defining_obj != VM_INVALID_OBJ) { /* skip until we're at defining_obj */ if (!curpos.skip_to(vmg_ defining_obj)) return FALSE; /* skip defining_obj itself */ if (!curpos.to_next(vmg0_)) return FALSE; } /* find the property */ return curpos.find_prop(vmg_ prop, val, source_obj); } /* ------------------------------------------------------------------------ */ /* * Get a property. We first look in this object; if we can't find the * property here, we look for it in one of our superclasses. */ int CVmObjTads::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in my own direct property list or a * superclass property list */ tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, val, source_obj)) return TRUE; /* * we didn't find the property in a property list, so try the * intrinsic class methods */ if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc)) return TRUE; /* * we didn't find the property among our methods, so try inheriting it * from the base metaclass */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* * Inherit a property. */ int CVmObjTads::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { /* * check to see if we're already inheriting from an intrinsic class or * an intrinsic class modifier */ if (defining_obj == VM_INVALID_OBJ || (!CVmObjIntClsMod::is_intcls_mod_obj(vmg_ defining_obj) && !CVmObjClass::is_intcls_obj(vmg_ defining_obj))) { /* * The previous defining object wasn't itself an intrinsic class or * modifier object, so continue searching for TadsObject * superclasses. */ if (search_for_prop_from(vmg_ prop, val, orig_target_obj, source_obj, defining_obj)) return TRUE; /* * We didn't find the property in a property list. Since we were * inheriting, we must have originally found it in a property list, * but we've found no more inherited properties. Next, check the * intrinsic methods of the intrinsic class. */ if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc)) return TRUE; /* * We didn't find it among our TadsObject superclasses or as an * intrinsic method. There's still one possibility: it could be * defined in an intrinsic class modifier for TadsObject or one of * its intrinsic superclasses (aka supermetaclasses). * * This represents a new starting point in the search. No longer * are we looking for TadsObject overrides; we're now looking for * modifier objects. The modifier objects effectively form a * separate class hierarchy alongside the intrinsic class hierarchy * they modify. Since we're starting a new search in this new * context, forget the previous defining object - it has a * different meaning in the new context, and we want to start the * new search from the beginning. * * Note that if this search does turn up a modifier object, and * that modifier object further inherits, we'll come back through * this method again to find the base class method. At that point, * however we'll notice that the previous defining object was a * modifier, so we will not go through this branch again - we'll go * directly to the base metaclass and continue the inheritance * search there. */ defining_obj = VM_INVALID_OBJ; } /* continue searching via our base metaclass */ return CVmObject::inh_prop(vmg_ prop, val, self, orig_target_obj, defining_obj, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Get a property from the intrinsic class. */ int CVmObjTads::get_prop_intrinsic(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function in our function vector */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Enumerate properties */ void CVmObjTads::enum_props(VMG_ vm_obj_id_t self, void (*cb)(VMG_ void *ctx, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val), void *cbctx) { size_t i; size_t sc_cnt; vm_tadsobj_prop *entry; vm_tadsobj_hdr *hdr = get_hdr(); /* run through our properties */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* invoke the callback */ (*cb)(vmg_ cbctx, self, entry->prop, &entry->val); } /* enumerate properties in each superclass */ sc_cnt = get_sc_count(); for (i = 0 ; i < sc_cnt ; ++i) { vm_obj_id_t sc; /* get this superclass */ sc = get_sc(i); /* enumerate its properties */ vm_objp(vmg_ sc)->enum_props(vmg_ sc, cb, cbctx); } } /* ------------------------------------------------------------------------ */ /* * Determine if I'm an instance of the given object */ int CVmObjTads::is_instance_of(VMG_ vm_obj_id_t obj) { /* * Set up a superclass search position. Since the first thing we'll * do is call 'to_next', and since 'to_next' doesn't require a valid * current object ID (only a valid 'this' pointer), we don't need to * know our own object ID - simply set the initial object ID to the * invalid ID. */ tadsobj_sc_search_ctx curpos(vmg_ VM_INVALID_OBJ, this); /* * scan through the search list, comparing each superclass to the * object of interest; if we find it among our superclasses, we're an * instance of the given object */ for (;;) { /* skip to the next object */ if (!curpos.to_next(vmg0_)) { /* we've run out of superclasses without finding it */ break; } /* * if the current superclass is the object we're looking for, then * we're an instance of that object */ if (curpos.cur == obj) return TRUE; } /* * None of our superclasses match the given object, and none of the * superclasses derive from the given object, so we must not derive * from the given object. Our last recourse is to determine if the * object represents our metaclass; inherit the default handling to * make this check. */ return CVmObject::is_instance_of(vmg_ obj); } /* ------------------------------------------------------------------------ */ /* * Apply undo */ void CVmObjTads::apply_undo(VMG_ CVmUndoRecord *rec) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* * if the property is valid, it's a simple property change record; * otherwise it's some other object-level change */ if (rec->id.prop == VM_INVALID_PROP) { /* * Invalid property ID, so it's an object-level change: * * - int(1) -> object is newly modified (VMTO_OBJ_MOD flag was * newly set) * * - list -> superclass list change */ switch (rec->oldval.typ) { case VM_INT: switch (rec->oldval.val.intval) { case 1: /* VMTO_OBJ_MOD flag newly set - clear the flag */ hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; break; } break; default: /* check for a list, which indicates a superclass change */ if (rec->oldval.is_listlike(vmg0_)) { /* set the new superclass list */ change_superclass_list( vmg_ &rec->oldval, rec->oldval.ll_length(vmg0_)); } break; } } else { /* find the property entry for the property being undone */ vm_tadsobj_prop *entry = hdr->find_prop_entry(rec->id.prop); if (entry == 0) { /* can't find the property - something is out of whack */ assert(FALSE); return; } /* restore the value from the record */ entry->val = rec->oldval; /* if the old value is 'empty', it requires special handling */ if (rec->oldval.typ == VM_EMPTY) { vm_tadsobj_prop *cur, **prv; unsigned int hash = hdr->calc_hash(rec->id.prop); /* * We use 'empty' records for multiple purposes, with the * specific one indicated by the intval field. */ switch (rec->oldval.val.intval) { case 0: /* * Empty with intval 0 indicates a property addition, which * we undo by deleting the property. First, find it in the * hash chain. */ for (prv = &hdr->hash_arr[hash] ; (cur = *prv) != 0 && cur != entry ; prv = &cur->nxt) ; /* make sure we found it */ if (cur == entry) { /* unlink it */ *prv = entry->nxt; /* return it to the free list */ hdr->prop_entry_free -= 1; assert(entry == &hdr->prop_entry_arr[hdr->prop_entry_free]); } else { /* this should be impossible */ assert(FALSE); } break; case 1: /* * Empty with intval 1 indicates that we marked the slot as * newly modified, which we undo by clearing the modified * flag. */ entry->flags &= ~VMTO_PROP_MOD; break; } } } } /* ------------------------------------------------------------------------ */ /* * Mark as referenced all of the objects to which we refer */ void CVmObjTads::mark_refs(VMG_ uint state) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* * Go through all of our property slots and mark each object value. * Note that we only need to worry about the modified properties; * everything referenced in the load image list is necessarily part of * the root set, or it couldn't have been in the load image, so we * don't need to bother marking any of those objects, since they can * never be deleted by virtue of being in the root set. */ vm_tadsobj_prop *entry = hdr->prop_entry_arr; vm_tadsobj_prop *first_free = entry + hdr->prop_entry_free; for ( ; entry != first_free ; ++entry) { /* * if the slot is marked as modified and contains an object * reference, mark the reference */ if ((entry->val.typ == VM_OBJ || entry->val.typ == VM_OBJX) && (entry->flags & VMTO_PROP_MOD) != 0) { /* mark the reference */ G_obj_table->mark_all_refs(entry->val.val.obj, state); } } /* mark our superclasses as referenced */ vm_tadsobj_sc *scp = hdr->sc, *sclast = scp + hdr->sc_cnt; for ( ; scp != sclast ; ++scp) G_obj_table->mark_all_refs(scp->id, state); } /* ------------------------------------------------------------------------ */ /* * Mark a reference in an undo record */ void CVmObjTads::mark_undo_ref(VMG_ CVmUndoRecord *undo) { /* if the undo record refers to an object, mark the object */ if (undo->oldval.typ == VM_OBJ || undo->oldval.typ == VM_OBJX) G_obj_table->mark_all_refs(undo->oldval.val.obj, VMOBJ_REACHABLE); } /* ------------------------------------------------------------------------ */ /* * Determine if the object has been changed since it was loaded from the * image file. If the object has no properties stored in the modified * properties table, it is in exactly the same state as is stored in the * image file. */ int CVmObjTads::is_changed_since_load() const { /* return our 'modified' flag */ return ((get_hdr()->intern_obj_flags & VMTO_OBJ_MOD) != 0); } /* ------------------------------------------------------------------------ */ /* * Save the object's state to a file. We only need to save the modified * property list, because the load image list never changes. */ void CVmObjTads::save_to_file(VMG_ CVmFile *fp) { size_t i; vm_tadsobj_prop *entry; uint cnt; vm_tadsobj_hdr *hdr = get_hdr(); /* count the number of properties that have actually been modified */ for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* if the slot is modified, count it */ if ((entry->flags & VMTO_PROP_MOD) != 0) ++cnt; } /* write the number of modified properties */ fp->write_uint2(cnt); /* write the number of superclasses */ fp->write_uint2(get_sc_count()); /* write the superclasses */ for (i = 0 ; i < get_sc_count() ; ++i) fp->write_uint4(get_sc(i)); /* write each modified property */ for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* if the slot is modified, write it out */ if ((entry->flags & VMTO_PROP_MOD) != 0) { char slot[16]; /* prepare the slot data */ oswp2(slot, entry->prop); vmb_put_dh(slot + 2, &entry->val); /* write the slot */ fp->write_bytes(slot, 2 + VMB_DATAHOLDER); } } } /* ------------------------------------------------------------------------ */ /* * Restore the object from a file */ void CVmObjTads::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* read number of modified properties */ ushort mod_count = (ushort)fp->read_uint2(); /* read the number of superclasses */ ushort sc_cnt = (ushort)fp->read_uint2(); /* * If we don't have an extension yet, allocate one. The only way we * won't have an extension is if we weren't loaded from the image * file, since we always create the extension upon construction when * loading from an image file. */ if (ext_ == 0) { /* allocate our extension */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, mod_count); } else { /* * We already have an extension, so we must have come from the * image file. Make sure we have enough memory to hold this many * properties, and make sure we have space for the superclasses. */ vm_tadsobj_hdr *hdr = get_hdr(); if (!hdr->has_free_entries(mod_count) || sc_cnt > hdr->sc_cnt) { /* * we need to expand the header to accomodate the modified * properties and/or the modified superclass list */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt + mod_count); } } /* get the extension header */ vm_tadsobj_hdr *hdr = get_hdr(); /* read the superclass list */ hdr->sc_cnt = sc_cnt; for (ushort i = 0 ; i < sc_cnt ; ++i) { /* read the next superclass */ vm_obj_id_t sc = (vm_obj_id_t)fp->read_uint4(); /* fix it up to the new (post-restore) memory numbering system */ sc = fixups->get_new_id(vmg_ sc); /* * store it - as when loading from the image file, we can't count * on the superclass having been loaded yet, so we can only store * the superclass's ID, not its actual object pointer */ hdr->sc[i].id = sc; hdr->sc[i].objp = 0; } /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); /* * invalidate any existing inheritance path, in case the superclass * list changed */ hdr->inval_inh_path(); /* read the modified properties */ for (ushort i = 0 ; i < mod_count ; ++i) { /* read the next slot */ char buf[32]; fp->read_bytes(buf, 2 + VMB_DATAHOLDER); /* fix up this entry */ fixups->fix_dh(vmg_ buf + 2); /* decode the entry */ vm_prop_id_t prop = (vm_prop_id_t)osrp2(buf); vm_val_t val; vmb_get_dh(buf + 2, &val); /* * store the entry (don't save any undo for the operation, as we * can't undo a load) */ set_prop(vmg_ 0, self, prop, &val); } /* clear all undo information */ clear_undo_flags(); /* * If we were saved to a file in the first place, it's because we were * modified or newly created relative to the image file data. So on * restore, we're necessarily a modified object that will need to be * saved again, even if we're not modified between now and the next * save. */ get_hdr()->intern_obj_flags |= VMTO_OBJ_MOD; } /* ------------------------------------------------------------------------ */ /* * Post-load initialization: cache the object pointers for our * superclasses. */ void CVmObjTads::post_load_init(VMG_ vm_obj_id_t self) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* cache the superclass object pointers */ for (int i = 0 ; i < hdr->sc_cnt ; ++i) hdr->sc[i].objp = (CVmObjTads *)vm_objp(vmg_ hdr->sc[i].id); } /* ------------------------------------------------------------------------ */ /* * Load the object from an image file */ void CVmObjTads::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* save our image data pointer for reloading */ G_obj_table->save_image_pointer(self, ptr, siz); /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* get the number of load image properties */ ushort li_cnt = osrp2(ptr + 2); /* allocate our header */ ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, li_cnt); vm_tadsobj_hdr *hdr = get_hdr(); /* read the object flags from the image file and store them */ hdr->li_obj_flags = osrp2(ptr + 4); /* * set our internal flags - we come from the load image file, and we're * not yet modified from the load image data */ hdr->intern_obj_flags |= VMTO_OBJ_IMAGE; hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; /* load the image file properties */ load_image_props_and_scs(vmg_ ptr, siz); /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); } /* * Reset to image file state. Discards all modified properties, so that * we have only the image file properties. */ void CVmObjTads::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* * Clear the property table. We don't have to worry about the new * property table being larger than the existing property table, * because we can't have shrunk since we were originally loaded. So, * all we need to do is mark all property entries as free and clear * out the hash table. */ hdr->prop_entry_free = 0; memset(hdr->hash_arr, 0, hdr->hash_siz * sizeof(hdr->hash_arr[0])); /* if we need space for more superclasses, reallocate the header */ if (sc_cnt > hdr->sc_cnt) { /* allocate the new header */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt); } /* reload the image properties and superclasses */ load_image_props_and_scs(vmg_ ptr, siz); /* we're now unmodified from the image file state */ hdr->intern_obj_flags &= ~VMTO_OBJ_MOD; /* request post-load initialization, to set up the superclass list */ G_obj_table->request_post_load_init(self); } /* * Load the property list from the image data */ void CVmObjTads::load_image_props_and_scs(VMG_ const char *ptr, size_t siz) { /* get my header */ vm_tadsobj_hdr *hdr = get_hdr(); /* get the number of superclasses */ ushort sc_cnt = osrp2(ptr); /* get the number of load image properties */ ushort li_cnt = osrp2(ptr + 2); /* read the superclasses from the load image and store them */ ushort i; const char *p; for (i = 0, p = ptr + 6 ; i < sc_cnt ; ++i, p += 4) { /* store the object ID */ hdr->sc[i].id = (vm_obj_id_t)t3rp4u(p); /* * We can't store the superclass pointer yet, as the superclass * object might not be loaded yet. Store null for now; we'll fix * this up in post_load_init() after all the other objects have * been loaded. */ hdr->sc[i].objp = 0; } /* read the properties from the load image and store them */ for (i = 0 ; i < li_cnt ; ++i, p += 2 + VMB_DATAHOLDER) { /* decode the property data */ vm_prop_id_t prop = (vm_prop_id_t)osrp2(p); vm_val_t val; vmb_get_dh(p + 2, &val); /* store the property */ hdr->alloc_prop_entry(prop, &val, 0); } } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createInstance */ int CVmObjTads::getp_create_instance(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_common(vmg_ self, retval, in_argc, FALSE); } /* * Property evaluator - createTransientInstance */ int CVmObjTads::getp_create_trans_instance(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a transient instance */ return getp_create_common(vmg_ self, retval, in_argc, TRUE); } /* * Common handler for createInstance() and createTransientInstance() */ int CVmObjTads::getp_create_common(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, int is_transient) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); /* check arguments - any number are allowed */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* * push myself as the first argument - 'self' is the superclass of the * object to be created */ G_interpreter->push_obj(vmg_ self); /* * Create an instance - this will recursively execute the new object's * constructor, if it has one. Note that we have one more argument * than provided by the caller, because we've pushed the implicit * argument ('self') that create_from_stack uses to identify the * superclass. */ retval->set_obj(create_from_stack_intern(vmg_ 0, argc + 1, is_transient)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createClone */ int CVmObjTads::getp_create_clone(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); vm_obj_id_t new_obj; CVmObjTads *tobj; vm_tadsobj_prop *entry; ushort i; vm_tadsobj_hdr *hdr = get_hdr(); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * create a new object with the same number of superclasses as I have, * and with space for all of my properties */ new_obj = create(vmg_ FALSE, get_sc_count(), hdr->prop_entry_free); tobj = (CVmObjTads *)vm_objp(vmg_ new_obj); /* copy my superclass list to the new object */ for (i = 0 ; i < get_sc_count() ; ++i) tobj->set_sc(vmg_ i, get_sc(i)); /* copy my properties to the new object */ for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry) { /* * Store the property in the new object. We don't need to store * undo for the property, as the object is entirely new since the * last savepoint (as there can't have been a savepoint while we've * been working, obviously). */ tobj->set_prop(vmg_ 0, self, entry->prop, &entry->val); } /* the return value is the new object ID */ retval->set_obj(new_obj); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - createInstanceOf */ int CVmObjTads::getp_create_instance_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_multi_common(vmg_ self, retval, in_argc, FALSE); } /* * Property evaluator - createTransientInstanceOf */ int CVmObjTads::getp_create_trans_instance_of( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* create a persistent instance */ return getp_create_multi_common(vmg_ self, retval, in_argc, TRUE); } /* * Common handler for createInstanceOf() and createTransientInstanceOf() */ int CVmObjTads::getp_create_multi_common(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, int is_transient) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); /* check arguments - any number are allowed */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* create the new instance */ retval->set_obj(create_from_stack_multi(vmg_ argc, is_transient)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Iteration callback for getp_set_sc_list. On changing an object's * superclass list, we have to rebuild the inheritance path cache for any * object that has the modified object anywhere in its path. */ struct set_sc_cb_ctx { set_sc_cb_ctx(vm_obj_id_t obj) { this->obj = obj; } vm_obj_id_t obj; }; void CVmObjTads::set_sc_cb(VMG_ vm_obj_id_t obj, void *ctx0) { /* if this is a TadsObject instance, update it */ if (CVmObjTads::is_tadsobj_obj(vmg_ obj)) { /* cast the context to our private structure */ set_sc_cb_ctx *ctx = (set_sc_cb_ctx *)ctx0; /* get this object's header */ vm_tadsobj_hdr *hdr = ((CVmObjTads *)vm_objp(vmg_ obj))->get_hdr(); /* * if it has a cached inheritance path, check to see if it contains * the object being changed */ tadsobj_objid_and_ptr *path = hdr->inh_path; if (path != 0) { /* scan the path for 'obj' */ for ( ; path->id != VM_INVALID_OBJ ; ++path) { /* if this superclass is 'obj', we must delete the path */ if (path->id == ctx->obj) { /* we need to drop this path */ hdr->inval_inh_path(); /* no need to look any further */ break; } } } } } /* ------------------------------------------------------------------------ */ /* * Property evaluator - setSuperclassList */ int CVmObjTads::getp_set_sc_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1); int cnt; int i; vm_val_t ele; int sc_cnt; vm_tadsobj_hdr *hdr = get_hdr(); vm_val_t *lst; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the list argument (but leave it on the stack for now) */ lst = G_stk->get(0); if (!lst->is_listlike(vmg0_) || (cnt = lst->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* we need at least one argument - the minimal root is TadsObject */ if (cnt < 1) err_throw(VMERR_BAD_VAL_BIF); /* * Check for a special case: our entire superclass list consists of * [TadsObject]. In this case, we have nothing in our internal * superclass list, since our only superclass is our metaclass. */ lst->ll_index(vmg_ &ele, 1); if (cnt == 1 && ele.typ == VM_OBJ && ele.val.obj == metaclass_reg_->get_class_obj(vmg0_)) { /* use an empty internal superclass list */ sc_cnt = 0; } else { /* * Scan the superclasses. Each superclass must be a TadsObject, * with the one exception that if we have only one superclass, it * can be the TadsObject intrinsic class itself, signifying that we * have no superclasses. */ for (i = 1 ; i <= cnt ; ++i) { /* get this element from the list */ lst->ll_index(vmg_ &ele, i); /* it has to be an object of type TadsObject */ if (ele.typ != VM_OBJ || !is_tadsobj_obj(vmg_ ele.val.obj)) err_throw(VMERR_BAD_VAL_BIF); /* * make sure that this superclass doesn't inherit from 'self' - * if it does, that would create a circular inheritance * hierarchy, which is illegal */ if (vm_objp(vmg_ ele.val.obj)->is_instance_of(vmg_ self)) err_throw(VMERR_BAD_VAL_BIF); } /* the list is valid - we need one superclass per list element */ sc_cnt = cnt; } /* mark the overall object as modified if it's not already */ mark_modified(vmg_ G_undo, self); /* if there's a system undo object, add undo for the change */ if (G_undo != 0) { /* allocate a list for the results */ vm_val_t oldv; oldv.set_obj(CVmObjList::create(vmg_ FALSE, hdr->sc_cnt)); CVmObjList *oldp = (CVmObjList *)vm_objp(vmg_ oldv.val.obj); /* build the superclass list */ for (i = 0 ; i < hdr->sc_cnt ; ++i) { /* add this superclass to the list */ ele.set_obj(hdr->sc[i].id); oldp->cons_set_element(i, &ele); } /* * Add an undo record with the original superclass list as the old * value. Use the 'invalid' property as the property key; when * this property is used with a list value, it indicates a * superclass list change. */ G_undo->add_new_record_prop_key(vmg_ self, VM_INVALID_PROP, &oldv); } /* update the superclass list with the given list */ change_superclass_list(vmg_ lst, sc_cnt); /* discard arguments */ G_stk->discard(); /* no return value */ retval->set_nil(); /* we need to clear all cached superclass path lists involving 'self' */ set_sc_cb_ctx ctx(self); G_obj_table->for_each(vmg_ &set_sc_cb, &ctx); /* handled */ return TRUE; } /* * Change the superclass list to the given list. 'lstp' is the new * superclass list, in constant list format (i.e., a packed array of * dataholder values). */ void CVmObjTads::change_superclass_list(VMG_ const vm_val_t *lst, int cnt) { vm_tadsobj_hdr *hdr = get_hdr(); int i; /* keep the count within range */ cnt = (cnt > UINT16MAXVAL ? UINT16MAXVAL : cnt < 0 ? 0 : cnt); /* * if we're increasing the number of superclasses, expand our object * header to make room */ if (cnt > (int)hdr->sc_cnt) { /* expand the header to accomodate the new superclass list */ ext_ = (char *)vm_tadsobj_hdr::expand_to( vmg_ this, hdr, cnt, hdr->prop_entry_cnt); /* get the new header */ hdr = get_hdr(); } /* set the new superclass count */ hdr->sc_cnt = (ushort)cnt; /* set the new superclasses */ for (i = 0 ; i < cnt ; ++i) { vm_val_t ele; /* get this element from the list */ lst->ll_index(vmg_ &ele, i+1); /* set this superclass in the header */ hdr->sc[i].id = ele.val.obj; hdr->sc[i].objp = (CVmObjTads *)vm_objp(vmg_ ele.val.obj); } /* invalidate the cached inheritance path */ hdr->inval_inh_path(); } /* ------------------------------------------------------------------------ */ /* * Get a method pointer. This is almost like an ordinary getprop, but if * the property contains a method, returns a function pointer to the method * rather than evaluating it. */ int CVmObjTads::getp_get_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); /* check arguments: getMethod(&propid) */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the property ID */ vm_obj_id_t prop = CVmBif::pop_propid_val(vmg0_); /* find the property */ vm_obj_id_t source_obj; tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, retval, &source_obj)) { /* the property is definfed - check its type */ switch (retval->typ) { case VM_CODEOFS: /* it's a direct call to code - convert to a function pointer */ retval->typ = VM_FUNCPTR; break; case VM_DSTRING: /* it's a self-printing constant string - convert to string */ retval->typ = VM_SSTRING; break; case VM_OBJX: /* * It's an executable object, which is either a self-printing * non-constant string or an execute-on-invoke anonymous * function. In either case, simply convert it back to an * ordinary value. */ retval->typ = VM_OBJ; break; case VM_BIFPTRX: /* * executable built-in function pointer - convert back to a * regular bif pointer */ retval->typ = VM_BIFPTR; break; default: /* other types aren't executable methods, so return nil */ retval->set_nil(); break; } } else { /* this property isn't defined - return nil */ retval->set_nil(); } /* handled */ return TRUE; } /* * Set a method pointer. This is almost like an ordinary setprop, but if * the value to assign contains a function pointer, this sets the property * to a method rather than to a pointer-to-function value. */ int CVmObjTads::getp_set_method(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments: setMethod(&propid, val) */ static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * retrieve the property ID and the method value to set (leave the * value on the stack as gc protection) */ vm_prop_id_t prop = CVmBif::pop_propid_val(vmg0_); vm_val_t mval = *G_stk->get(0); /* check the type */ switch (mval.typ) { case VM_FUNCPTR: /* * a function pointer translates directly to a regular method, by * directly calling the code body */ mval.typ = VM_CODEOFS; break; case VM_OBJ: /* * We can accept string and invokable objects. For either one of * these, we store the object reference and mark it as an * execute-on-eval object rather than an ordinary object value. */ if (CVmObjString::is_string_obj(vmg_ mval.val.obj) || vm_objp(vmg_ mval.val.obj)->get_invoker(vmg_ 0)) { /* * We can handle this object by executing it on evaluation. * Mark it as a special execute-on-eval object rather than an * ordinary object. */ mval.typ = VM_OBJX; } else { /* it's not an acceptable type of object */ err_throw(VMERR_BAD_TYPE_BIF); } break; case VM_BIFPTR: /* built-in function pointer - mark it as an execute-on-eval bif */ mval.typ = VM_BIFPTRX; break; case VM_SSTRING: /* * A constant string translates into a self-printing string. * SSTRING and DSTRING have the same data representation, so we * merely need to switch the type to flag it as print-on-eval. */ mval.typ = VM_DSTRING; break; default: /* other types are not acceptable */ err_throw(VMERR_BAD_TYPE_BIF); } /* set the property value */ set_prop(vmg_ G_undo, self, prop, &mval); /* discard the method value */ G_stk->discard(); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Intrinsic Class Modifier object implementation */ /* metaclass registration object */ static CVmMetaclassIntClsMod metaclass_reg_obj_icm; CVmMetaclass *CVmObjIntClsMod::metaclass_reg_ = &metaclass_reg_obj_icm; /* * Get a property. Intrinsic class modifiers do not have intrinsic * superclasses, because they're effectively mix-in classes. Therefore, * do not look for intrinsic properties or intrinsic superclass properties * to resolve the property lookup. */ int CVmObjIntClsMod::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in our property list or a superclass * property list */ tadsobj_sc_search_ctx curpos(vmg_ self, this); if (curpos.find_prop(vmg_ prop, val, source_obj)) return TRUE; /* * We didn't find it in our list, so we don't have the property. * Because we're an intrinsic mix-in, we don't look for an intrinsic * implementation or an intrinsic superclass implementation. */ return FALSE; } /* * Inherit a property. As with get_prop(), we don't want to inherit from * any intrinsic superclass if we don't find the property in our property * list or an inherited property list. */ int CVmObjIntClsMod::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { /* * try finding the property in our property list or a superclass * property list */ if (search_for_prop_from(vmg_ prop, val, orig_target_obj, source_obj, defining_obj)) return TRUE; /* * we didn't find it in our list, and we don't want to inherit from any * intrinsic superclass, so we don't have the property */ return FALSE; } /* * Build my property list. We build the complete list of methods defined * in the intrinsic class modifier for all classes, including any modify * base classes that we further modify. */ void CVmObjIntClsMod::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval) { /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* build our own list */ CVmObjTads::build_prop_list(vmg_ self, retval); /* if we have a base class that we further modify, add its list */ if (get_sc_count() != 0) { vm_obj_id_t base_id; CVmObject *base_obj; /* get the base class */ base_id = get_sc(0); base_obj = vm_objp(vmg_ base_id); /* get its list only if it's of our same metaclass */ if (base_obj->get_metaclass_reg() == get_metaclass_reg()) { vm_val_t base_val; /* save our list for gc protection */ G_stk->push(retval); /* get our base class's list */ base_obj->build_prop_list(vmg_ base_id, &base_val); /* add this list to our result list */ vm_objp(vmg_ retval->val.obj)-> add_val(vmg_ retval, retval->val.obj, &base_val); /* discard our gc protection */ G_stk->discard(); } } /* discard gc protection */ G_stk->discard(); } ����������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmcfgmem.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000001137�11470775532�016044� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcfgmem.cpp - T3 VM Configuration - in-memory (non-swapping) memory manager Function Notes Modified 10/08/99 MJRoberts - Creation */ #include "t3std.h" #include "vminit.h" #include "vmglob.h" /* * initialize */ void vm_initialize(struct vm_globals **vmg, const vm_init_options *opts) { vm_init_in_mem(vmg, opts); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmisaac.cpp��������������������������������������������������������������������0000644�0001750�0000144�00000011575�11723015123�015655� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmisaac.cpp - T3 VM ISAAC random number generator implementation Function Notes Modified 04/11/10 MJRoberts - Creation */ #include "vmisaac.h" /* service macros for ISAAC random number generator */ #define isaac_ind(mm,x) ((mm)[(x>>2)&(ISAAC_RANDSIZ-1)]) #define isaac_step(mix,a,b,mm,m,m2,r,x) \ { \ x = *m; \ a = ((a^(mix)) + *(m2++)) & 0xffffffff; \ *(m++) = y = (isaac_ind(mm,x) + a + b) & 0xffffffff; \ *(r++) = b = (isaac_ind(mm,y>>ISAAC_RANDSIZL) + x) & 0xffffffff; \ } #define isaac_mix(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=c>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=e>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=g>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=a>>9; c+=h; a+=b; \ } /* generate the group of numbers */ void isaac_gen_group(isaacctx *ctx) { uint32_t a; uint32_t b; uint32_t x; uint32_t y; uint32_t *m; uint32_t *mm; uint32_t *m2; uint32_t *r; uint32_t *mend; mm = ctx->mem; r = ctx->rsl; a = ctx->a; b = (ctx->b + (++ctx->c)) & 0xffffffff; for (m = mm, mend = m2 = m + (ISAAC_RANDSIZ/2) ; m<mend ; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } for (m2 = mm; m2<mend; ) { isaac_step(a<<13, a, b, mm, m, m2, r, x); isaac_step(a>>6, a, b, mm, m, m2, r, x); isaac_step(a<<2, a, b, mm, m, m2, r, x); isaac_step(a>>16, a, b, mm, m, m2, r, x); } ctx->b = b; ctx->a = a; } /* * Initialize. If flag is true, then use the contents of ctx->rsl[] to * initialize ctx->mm[]; otherwise, we'll use a fixed starting * configuration. */ void isaac_init(isaacctx *ctx, int flag) { int i; uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t e; uint32_t f; uint32_t g; uint32_t h; uint32_t *m; uint32_t *r; ctx->a = ctx->b = ctx->c = 0; m = ctx->mem; r = ctx->rsl; a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */ /* scramble the initial settings */ for (i = 0 ; i < 4 ; ++i) { isaac_mix(a, b, c, d, e, f, g, h); } if (flag) { /* initialize using the contents of ctx->rsl[] as the seed */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += r[i]; b += r[i+1]; c += r[i+2]; d += r[i+3]; e += r[i+4]; f += r[i+5]; g += r[i+6]; h += r[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } /* do a second pass to make all of the seed affect all of m */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { a += m[i]; b += m[i+1]; c += m[i+2]; d += m[i+3]; e += m[i+4]; f += m[i+5]; g += m[i+6]; h += m[i+7]; isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } } else { /* initialize using fixed initial settings */ for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8) { isaac_mix(a, b, c, d, e, f, g, h); m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d; m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h; } } /* fill in the first set of results */ isaac_gen_group(ctx); /* prepare to use the first set of results */ ctx->cnt = ISAAC_RANDSIZ; } /* * Get the internal state */ size_t isaac_get_state(isaacctx *ctx, char *buf) { /* if the didn't provide a buffer, it's a size-needed query */ const size_t sz = 4 * (4 + ISAAC_RANDSIZ + ISAAC_RANDSIZ); if (buf == 0) return sz; /* copy the scalar members */ oswp4(buf, ctx->cnt); buf += 4; oswp4(buf, ctx->a); buf += 4; oswp4(buf, ctx->b); buf += 4; oswp4(buf, ctx->c); buf += 4; /* copy the array members */ for (int i = 0 ; i < ISAAC_RANDSIZ ; ++i, buf += 8) { oswp4(buf, ctx->rsl[i]); oswp4(buf+4, ctx->mem[i]); } return sz; } /* * Set the internal state */ void isaac_set_state(isaacctx *ctx, const char *buf) { /* decode the buffer into our context */ ctx->cnt = osrp4(buf); buf += 4; ctx->a = osrp4(buf); buf += 4; ctx->b = osrp4(buf); buf += 4; ctx->c = osrp4(buf); buf += 4; for (int i = 0 ; i < ISAAC_RANDSIZ ; ++i, buf += 8) { ctx->rsl[i] = osrp4(buf); ctx->mem[i] = osrp4(buf+4); } /* sanity check the result: make sure 'cnt' is in range */ if (ctx->cnt > ISAAC_RANDSIZ) ctx->cnt = 0; } �����������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/tct3_d.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000001020�11336555362�015410� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3_d.cpp - stubs for functions not needed in debugger builds Function Notes Modified 02/02/00 MJRoberts - Creation */ #include "tctarg.h" #include "tcprs.h" void CTcGenTarg::add_debug_line_table(ulong) { } void CTPNObjProp::check_locals() { } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/indlg_tx3.cpp������������������������������������������������������������������0000644�0001750�0000144�00000020100�11444673446�016127� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name indlg_tx.cpp - formatted text implementation of input_dialog Function Implements the input dialog using formatted text Notes Only one of indlg_tx.c or indlg_os.c should be included in a given executable. For a text-only version, include indlg_tx. For a version where os_input_dialog() provides a system dialog, use indlg_os instead. We provide a choice of input_dialog() implementations in the portable code (rather than only through the OS code) so that we can call the formatted text output routines in this version. An OS-layer implementation could not call the formatted output routines (it would have to call os_printf directly), which would result in poor prompt formatting any time a prompt exceeded a single line of text. Modified 09/27/99 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmglob.h" #include "vmconsol.h" #include "charmap.h" /* * formatted text-only file prompt */ int CVmConsole::input_dialog(VMG_ int /*icon_id*/, const char *prompt, int standard_button_set, const char **buttons, int button_count, int default_index, int cancel_index, int bypass_script) { /* keep going until we get a valid response */ for (;;) { int i; char buf[256]; const char *p; const char *cur; char *resp; int match_cnt; int last_found; int err; static const struct { const char *buttons[3]; int button_count; } std_btns[] = { { { "&OK" }, 1 }, { { "&OK", "&Cancel" }, 2 }, { { "&Yes", "&No" }, 2 }, { { "&Yes", "&No", "&Cancel" }, 3 } }; /* * if we have a standard button set selected, get our button * labels */ switch(standard_button_set) { case 0: /* use the explicit buttons provided */ break; case OS_INDLG_OK: i = 0; use_std_btns: /* use the selected standard button set */ buttons = (const char **)std_btns[i].buttons; button_count = std_btns[i].button_count; break; case OS_INDLG_OKCANCEL: i = 1; goto use_std_btns; case OS_INDLG_YESNO: i = 2; goto use_std_btns; case OS_INDLG_YESNOCANCEL: i = 3; goto use_std_btns; default: /* * we don't recognize other standard button sets - return an * error */ return 0; } /* * if there are no buttons defined, they'll never be able to * respond, so we'd just loop forever - rather than let that * happen, return failure */ if (button_count == 0) return 0; /* display a newline and the prompt string */ format_text(vmg_ "\n"); format_text(vmg_ prompt); format_text(vmg_ " "); /* display the response */ for (i = 0 ; i < button_count ; ++i) { /* * display a slash to separate responses, if this isn't the * first one */ if (i != 0) format_text(vmg_ "/"); /* get the current button */ cur = buttons[i]; /* * Look for a "&" in the response string. If we find it, * remove the "&" and enclose the shortcut key in parens. */ for (p = cur ; *p != '\0' && *p != '&' ; p = utf8_ptr::s_inc((char *)p)) ; /* if we found the "&", put the next character in parens */ if (*p != '\0') { size_t pre_len; size_t post_len; /* * note the length of the part up to the '&', but limit it * to avoid overflowing the buffer */ pre_len = p - cur; if (pre_len > sizeof(buf) - 5) pre_len = sizeof(buf) - 5; /* * note the length of the part after the '&', limiting it * as well */ post_len = strlen(p+2); if (post_len > sizeof(buf) - 5 - pre_len) post_len = sizeof(buf) - 5 - pre_len; /* reformat the response string */ sprintf(buf, "%.*s(%c)%.*s", (int)pre_len, cur, *(p + 1), (int)post_len, p + 2); /* display it */ format_text(vmg_ buf); } else { /* no '&' - just display the response string as-is */ format_text(vmg_ cur); } } /* switch to input font */ format_text(vmg_ "<font face='TADS-Input'>"); /* read the response */ format_text(vmg_ " >"); err = read_line(vmg_ buf, sizeof(buf), bypass_script); /* close the input font tag */ format_text(vmg_ "</font>"); /* on error, return 0 */ if (err) return 0; /* skip any leading spaces in the reply */ for (resp = buf ; isspace(*resp) ; ++resp) ; /* if it's one character, check it against the shortcut keys */ if (strlen(resp) == 1) { /* scan the responses */ for (i = 0 ; i < button_count ; ++i) { /* look for a '&' in this button */ for (p = buttons[i] ; *p != '&' && *p != '\0' ; ++p) ; /* if we found the '&', check the shortcut */ if (*p == '&' && toupper(*(p+1)) == toupper(*resp)) { /* * this is the one - return the current index * (bumping it by one to get a 1-based value) */ return i + 1; } } } /* * Either it's not a one-character reply, or it didn't match a * short-cut - check it against the leading substrings of the * responses. If it matches exactly one of the responses in its * leading substring, use that response. */ for (i = 0, match_cnt = 0 ; i < button_count ; ++i) { const char *p1; const char *p2; /* * compare this response to the user's response; skip any * '&' in the button label */ for (p1 = resp, p2 = buttons[i] ; *p1 != '\0' && *p2 != '\0' ; ++p1, ++p2) { /* if this is a '&' in the button label, skip it */ if (*p2 == '&') ++p2; /* if these characters don't match, it's no match */ if (toupper(*p1) != toupper(*p2)) break; } /* * if we reached the end of the user's response, we have a * match in the leading substring - count it and remember * this as the last one, but keep looking, since we need to * make sure we don't have any other matches */ if (*p1 == '\0') { ++match_cnt; last_found = i; } } /* * if we found exactly one match, return it (adjusting to a * 1-based index); if we found more or less than one match, it's * not a valid response, so start over with a new prompt */ if (match_cnt == 1) return last_found + 1; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmsave.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000035616�11744267311�015550� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsave.cpp - T3 save/restore state Function Notes Modified 08/02/99 MJRoberts - Creation */ /* * Saved game header structure: * *. <17 bytes> - signature, "T3-state-vXXX\015\012\032", where XXX is *. the hex version number *. UINT4 - stream size, counting from the timestamp on *. UINT4 - CRC32 checksum of stream, from the timestamp on *. <24 bytes> - timestamp of the game file, to ensure that this saved *. state file is only applied to the correct image file *. UINT2 - length of filename in bytes *. <? bytes> - image file name; length given by preceding UINT2 *. UINT2 - number of bytes in metadata table *. <? bytes> - metadata table *. <? bytes> - object stream data * * The metadata table contains any number of name/value string pairs. * These are arbitrary values that allow the game to store descriptive * information about the saved game that the interpreter and other tools * can display to the user, to jog the user's memory when reviewing a * collection of saved game files. The format of the table is: * *. UINT2 - number of string/value pairs *. <? bytes> - first pair *. <? bytes> - second pair *. ...etc... * * Each pair has this format: * *. UINT2 - length of the name string *. UINT2 - length of the value string *. <? bytes> - name string *. <? bytes> - value string */ #include "t3std.h" #include "os.h" #include "vmglob.h" #include "vmsave.h" #include "vmfile.h" #include "vmimage.h" #include "vmobj.h" #include "vmrun.h" #include "vmstack.h" #include "vmundo.h" #include "vmmeta.h" #include "vmcrc.h" #include "vmlookup.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Saved state signature. This signature is tied to a specific format * version; it should be changed whenever the format version is modified * so that it is incompatible with older versions. * * Note that incompatible changes to intrinsic class serialization formats * will require updating the version number. * * Incompatible changes to the format are not a particularly big deal. * Saved states tend to be local to a particular machine, since they're * mostly used to suspend sessions for later resumption and to "branch" * the state evolution (i.e., to allow playing a game from a particular * point, then returning later to that same point to play again, but doing * different things this time; this is used particularly to save "good" * positions as a precaution against later getting into unwinnable * states). */ #define VMSAVEFILE_SIG "T3-state-v000A\015\012\032" /* ------------------------------------------------------------------------ */ /* * Compute the checksum of the contents of a file stream. We'll compute * the checksum of the given file, starting at the current seek position * and running for the requested number of bytes. */ static unsigned long compute_checksum(CVmFile *fp, unsigned long len) { CVmCRC32 crc; /* read the file and compute the CRC value for its contents */ while (len != 0) { char buf[256]; size_t cur_len; /* figure out how much we can load from the file */ cur_len = sizeof(buf); if (cur_len > len) cur_len = (size_t)len; /* load the data from the file */ fp->read_bytes(buf, cur_len); /* deduct the amount we read from the overall file length remaining */ len -= cur_len; /* scan this block into the checksum */ crc.scan_bytes(buf, cur_len); } /* return the computed value */ return crc.get_crc_val(); } /* ------------------------------------------------------------------------ */ /* * Callback for enumerating the LookupTable entries for the metadata table * while saving a game */ struct metatab_saver { metatab_saver(CVmFile *fp) { this->fp = fp; this->cnt = 0; } /* number of name/value pairs we've written so far */ int cnt; /* file we're writing to */ CVmFile *fp; /* LookupTable::for_each callback */ static void cb(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx) { /* the context is our 'this' pointer */ metatab_saver *self = (metatab_saver *)ctx; CVmFile *fp = self->fp; /* both the key and value must be strings for us to save them */ const char *keystr = key->get_as_string(vmg0_); const char *valstr = val->get_as_string(vmg0_); if (keystr != 0 && valstr != 0) { size_t len; /* save the key string */ fp->write_uint2(len = vmb_get_len(keystr)); fp->write_bytes(keystr + VMB_LEN, len); /* save the value string */ fp->write_uint2(len = vmb_get_len(valstr)); fp->write_bytes(valstr + VMB_LEN, len); /* count the pair */ self->cnt += 1; } } }; /* ------------------------------------------------------------------------ */ /* * Save VM state to a file */ void CVmSaveFile::save(VMG_ CVmFile *fp, CVmObjLookupTable *metatab) { /* write the signature */ fp->write_bytes(VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1); /* note the seek position of the start of the file header */ long startpos = fp->get_pos(); /* write a placeholder for the stream size and checksum */ fp->write_uint4(0); fp->write_uint4(0); /* write the image file's timestamp */ fp->write_bytes(G_image_loader->get_timestamp(), 24); /* get the image filename */ const char *fname = G_image_loader->get_filename(); size_t fname_len = strlen(fname); /* * write the image filename, so we can figure out what image file to * load if we start the interpreter specifying only the saved state * to be restored */ fp->write_uint2(fname_len); fp->write_bytes(fname, fname_len); /* if there's a metadata table, write it out */ if (metatab != 0) { /* write a placeholder for the length and entry count */ long metapos = fp->get_pos(); fp->write_uint2(0); fp->write_uint2(0); /* run through the table and write out the entries */ metatab_saver ctx(fp); metatab->for_each(vmg_ &ctx.cb, &ctx); /* go back and fix up the table length and entry count */ long endpos = fp->get_pos(); fp->set_pos(metapos); fp->write_uint2((int)(endpos - metapos - 2)); fp->write_uint2(ctx.cnt); /* it's an error if this table exceeds 64k */ if (endpos - metapos > 0xffff) err_throw(VMERR_DESC_TAB_OVERFLOW); /* seek back to the end of the table */ fp->set_pos(endpos); } else { /* there's no metadata table - just write a zero-byte length */ fp->write_uint2(0); } /* save all modified object state */ G_obj_table->save(vmg_ fp); /* save the synthesized exports */ G_image_loader->save_synth_exports(vmg_ fp); /* remember where the file ends */ long endpos = fp->get_pos(); /* * compute the size of the data stream - this includes everything * after the size/checksum fields */ unsigned long datasize = endpos - startpos - 8; /* * seek back to just after the size/checksum header - this is the * start of the section of the file for which we must compute the * checksum */ fp->set_pos(startpos + 8); /* compute the checksum */ unsigned long crcval = compute_checksum(fp, datasize); /* * seek back to the size/checksum header, and fill in those fields now * that we know their values */ fp->set_pos(startpos); fp->write_uint4(datasize); fp->write_uint4(crcval); /* seek back to the end of the file */ fp->set_pos(endpos); } /* ------------------------------------------------------------------------ */ /* * Given a saved state file, get the name of the image file that was * loaded when the state file was created. */ int CVmSaveFile::restore_get_image(osfildef *fp, char *fname_buf, size_t fname_buf_len) { /* read the signature, size/checksum, and timestamp fields */ char buf[128]; if (osfrb(fp, buf, sizeof(VMSAVEFILE_SIG)-1 + 8 + 24)) return VMERR_READ_FILE; /* check the signature */ if (memcmp(buf, VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1) != 0) return VMERR_NOT_SAVED_STATE; /* read the length of the image file name */ if (osfrb(fp, buf, 2)) return VMERR_READ_FILE; /* get the length from the buffer */ size_t len = osrp2(buf); /* if it won't fit in the buffer, return an error */ if (len + 1 > fname_buf_len) return VMERR_READ_FILE; /* read the name into the caller's buffer */ if (osfrb(fp, fname_buf, len)) return VMERR_READ_FILE; /* null-terminate the name */ fname_buf[len] = '\0'; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Restore VM state from a file. Returns zero on success, non-zero on * error. */ int CVmSaveFile::restore(VMG_ CVmFile *fp) { /* we don't have a fixup table yet (the object loader will create one) */ CVmObjFixup *fixups = 0; /* read the file's signature */ char buf[128]; fp->read_bytes(buf, sizeof(VMSAVEFILE_SIG)-1); /* check the signature */ if (memcmp(buf, VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1) != 0) return VMERR_NOT_SAVED_STATE; /* read the size/checksum fields */ unsigned long datasize = fp->read_uint4(); unsigned long old_crcval = fp->read_uint4(); /* note the starting position of the datastream */ long startpos = fp->get_pos(); /* compute the checksum of the file data */ unsigned long new_crcval = compute_checksum(fp, datasize); /* * if the checksum we computed doesn't match the one stored in the * file, the file is corrupted */ if (new_crcval != old_crcval) return VMERR_BAD_SAVED_STATE; /* seek back to the starting position */ fp->set_pos(startpos); /* check the timestamp */ fp->read_bytes(buf, 24); if (memcmp(buf, G_image_loader->get_timestamp(), 24) != 0) return VMERR_WRONG_SAVED_STATE; /* * skip the image filename - since we already have an image file * loaded, this information is of no use to us here (it's only * useful when we want to restore a saved state before we know what * the image file is) */ fp->set_pos_from_cur(fp->read_int2()); /* * skip the metadata table - it's provided for browsing tools, and we * have no use for it ourselves */ fp->set_pos_from_cur(fp->read_int2()); /* * discard all undo information - any undo information we currently * have obviously can't be applied to the restored state */ G_undo->drop_undo(vmg0_); /* * Disable garbage collection while restoring. This is necessary * because there are possible intermediate states where we have * restored some of the objects but not all of them, so objects that * are reachable from the fully restored state won't necessarily appear * to be reachable from all possible intermediate states. */ int old_gc_enabled = G_obj_table->enable_gc(vmg_ FALSE); int err = 0; err_try { /* forget any IntrinsicClass instances we created at startup */ G_meta_table->forget_intrinsic_class_instances(vmg0_); /* load the object data from the file */ if ((err = G_obj_table->restore(vmg_ fp, &fixups)) != 0) goto read_done; /* load the synthesized exports from the file */ err = G_image_loader->restore_synth_exports(vmg_ fp, fixups); if (err != 0) goto read_done; /* * re-link to the exports and synthesized exports loaded from the * saved session */ G_image_loader->do_dynamic_link(vmg0_); /* create any missing IntrinsicClass instances */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); read_done: ; } err_catch(exc) { /* remember the error code */ err = exc->get_error_code(); } err_end; /* we're done with the fixup table, so delete it if we created one */ if (fixups != 0) delete fixups; /* restore the garbage collector's enabled state */ G_obj_table->enable_gc(vmg_ old_gc_enabled); /* if any error occurred, throw the error */ if (err != 0) err_throw(err); /* * explicitly run garbage collection, since any dynamic objects that * were reachable before the restore only through non-transient * references will no longer be reachable, all of the non-transient * references having been replaced now */ G_obj_table->gc_full(vmg0_); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Reset to initial image file state */ void CVmSaveFile::reset(VMG0_) { /* * discard undo information, since it applies only to the current VM * state and obviously is no longer relevant after we reset to the * initial state */ G_undo->drop_undo(vmg0_); /* * discard all synthesized exports, since we want to dynamically link * to the base image file state */ G_image_loader->discard_synth_exports(); /* forget any IntrinsicClass instances we created at startup */ G_meta_table->forget_intrinsic_class_instances(vmg0_); /* reset all objects to initial image file load state */ G_obj_table->reset_to_image(vmg0_); /* * forget the previous dynamic linking information and relink to the * image file again - this will ensure that any objects created after * load are properly re-created now */ G_image_loader->do_dynamic_link(vmg0_); /* create any missing IntrinsicClass instances */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); /* * explicitly run garbage collection to clean up dynamic objects that * are no longer reachable from the initial state */ G_obj_table->gc_full(vmg0_); /* run the static initializers */ G_image_loader->run_static_init(vmg0_); } ������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmuni.h������������������������������������������������������������������������0000644�0001750�0000144�00000014113�11732623526�015040� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmuni.h - T3 VM Unicode-specific functions Function Notes Modified 08/27/99 MJRoberts - Creation */ #ifndef VMUNI_H #define VMUNI_H #include <stdlib.h> #include "wchar.h" #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * Upper/lower case classification and conversion functions for Unicode * character values. These routines are implemented in a * machine-generated source file (the source file is mechanically * derived from the Unicode character database). */ int t3_is_lower(wchar_t ch); int t3_is_upper(wchar_t ch); int t3_is_title(wchar_t ch); const wchar_t *t3_to_lower(wchar_t ch); const wchar_t *t3_to_upper(wchar_t ch); const wchar_t *t3_to_title(wchar_t ch); const wchar_t *t3_to_fold(wchar_t ch); wchar_t t3_simple_case_fold(wchar_t ch); /* * Character types. Types are mutually exclusive, so a character has * exactly one type. */ #define T3_CTYPE_UNDEF 0 /* character isn't defined */ #define T3_CTYPE_ALPHA 1 /* alphabetic, with no case information */ #define T3_CTYPE_UPPER 2 /* upper-case alphabetic */ #define T3_CTYPE_TITLE 3 /* title-case alphabetic */ #define T3_CTYPE_LOWER 4 /* lower-case alphabetic */ #define T3_CTYPE_DIGIT 5 /* digit */ #define T3_CTYPE_SPACE 6 /* horizontal whitespace */ #define T3_CTYPE_VSPAC 7 /* vertical whitespace */ #define T3_CTYPE_PUNCT 8 /* punctuation */ #define T3_CTYPE_OTHER 9 /* character doesn't fit any other category */ /* macro name strings, in order of appearance */ #define T3_CTYPE_NAMES \ "T3_CTYPE_UNDEF", "T3_CTYPE_ALPHA", "T3_CTYPE_UPPER", "T3_CTYPE_TITLE", \ "T3_CTYPE_LOWER", "T3_CTYPE_DIGIT", "T3_CTYPE_SPACE", "T3_CTYPE_VSPAC", \ "T3_CTYPE_PUNCT", "T3_CTYPE_OTHER" /* get the character type */ int t3_get_chartype(wchar_t ch); /* * character classification functions */ /* alphabetic? */ inline int t3_is_alpha(wchar_t ch) { int ctype = t3_get_chartype(ch); return (ctype >= T3_CTYPE_ALPHA && ctype <= T3_CTYPE_LOWER); } /* uppercase? */ inline int t3_is_upper(wchar_t ch) { int ctype = t3_get_chartype(ch); return (ctype == T3_CTYPE_UPPER || ctype == T3_CTYPE_TITLE); } /* lowercase? */ inline int t3_is_lower(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_LOWER); } /* digit? */ inline int t3_is_digit(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_DIGIT); } /* horizontal whitespace? */ inline int t3_is_space(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_SPACE); } /* vertical whitespace? */ inline int t3_is_vspace(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_VSPAC); } /* any whitespace, horizontal or vertical? */ inline int t3_is_whitespace(wchar_t ch) { int t = t3_get_chartype(ch); return (t == T3_CTYPE_SPACE || t == T3_CTYPE_VSPAC); } /* punctuation? */ inline int t3_is_punct(wchar_t ch) { return (t3_get_chartype(ch) == T3_CTYPE_PUNCT); } /* is it a defined unicode character? */ inline int t3_is_unichar(wchar_t ch) { return (t3_get_chartype(ch) != T3_CTYPE_UNDEF); } /* ------------------------------------------------------------------------ */ /* * Case folding wchar_t string reader */ class CVmCaseFoldStr { public: CVmCaseFoldStr(const wchar_t *s) { init(s, wcslen(s)); } CVmCaseFoldStr(const wchar_t *s, size_t len) { init(s, len); } /* get the current pointer */ const wchar_t *getptr() const { return p; } /* is another character available? */ int more() const { return rem != 0 || *fp != 0 || fp == ie; } /* are we at a character boundary? */ int at_boundary() const { return fp != fpbase && *fp == 0; } /* get the next character */ wchar_t getch() { /* if the expansion is exhausted, expand the next source character */ if (fp != ie && *fp == 0) { /* if there's nothing left in the source string, we're done */ if (rem == 0) return 0; /* get the next source character */ wchar_t ch = *p++; --rem; /* get its case-folded expansion */ if ((fp = t3_to_fold(ch)) == 0) { ie[0] = ch; fpbase = fp = ie; } } /* return the next expansion character */ return *fp++; } /* * Case-insensitive wide-string comparison routine. Returns true if * the strings are equal, false if not. */ static int wstreq(const wchar_t *a, const wchar_t *b) { /* loop over characters until we find a mismatch */ for (CVmCaseFoldStr fa(a), fb(b) ; ; ) { /* get the next character of each folded string */ wchar_t ca = fa.getch(); wchar_t cb = fb.getch(); /* if they differ, return not equal */ if (ca != cb) return FALSE; /* if they both ended here, we have a match */ if (ca == 0) return TRUE; } } private: void init(const wchar_t *s, size_t len) { /* set up at the start of the string */ this->p = s; this->rem = len; /* null-terminate our special one-byte identity conversion buffer */ ie[1] = 0; /* start without anything loaded */ fpbase = ie; fp = &ie[1]; } /* current position in case folding expansion of current source char */ const wchar_t *fp; /* start of current folding expansion */ const wchar_t *fpbase; /* buffer for identity expansions */ wchar_t ie[2]; /* original source string and character length */ const wchar_t *p; size_t rem; }; #endif /* VMUNI_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/t3_os.h������������������������������������������������������������������������0000644�0001750�0000144�00000005512�11321105547�014724� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name t3_os.h - miscellaneous system-specific definitions for T3 Function Various standard definitions Notes None Modified 05/31/03 MJRoberts - creation */ /* ------------------------------------------------------------------------ */ /* * Generic definitions. We'll start with a set of default macro * definitions that should be usable on most platforms. System-specific * versions can override these by #undefing and re-#defining them later, * in #ifdef-protected sections for those specific platforms. */ /* * The name of the default project makefile. The t3make program will look * for a file with this name if no makefile is specifically identified * (with the t3make -f option) and no source files are specified on the * command line. * * Note that if this is overridden, it should NOT specify a directory * prefix; the default makefile should always be sought in the current * working directory. Also, note that gratuitous changes to the name are * discouraged; ports should only change the name as needed to conform to * local conventions, and then should only change it as much as needed, * and ideally in such a way that people accustomed to working with the * local system would typically map "makefile.t3m" to local conventions. * * For example, one good reason to change the name would be that the * platform only allows six-character filenames; in these cases we'd want * to choose a reasonable abbreviation, such as "mkfile". Another good * reason would be that periods are not valid filename characters. * Period-delimited suffixes are such a widespread convention that it's * likely that users of such a platform would have adopted a standard (or * at least typical) mapping for these suffixes; so that mapping should be * applied. A third good reason would be local upper/lower-case * conventions. */ #define T3_DEFAULT_PROJ_FILE "makefile.t3m" /* ------------------------------------------------------------------------ */ /* * Unix-specific definitions */ #ifdef UNIX /* * Redefine the default project makefile to conform to Unix conventions. * Unix makefiles are conventionally called "Makefile" - the "M" is * capitalized to take advantage of (1) ASCII sorting order and (2) the * fact that source files conventionally use all-lower-case names, so that * the makefile sorts ahead of its related source files in directory * listings. Cheesy but effective. We'll follow the convention by * looking for "Makefile.t3m" by default. */ #undef T3_DEFAULT_PROJ_FILE #define T3_DEFAULT_PROJ_FILE "Makefile.t3m" #endif /* UNIX */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/sha2.cpp�����������������������������������������������������������������������0000644�0001750�0000144�00000065676�11554660323�015114� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* --------------------------------------------------------------------------- Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. ALTERNATIVELY, provided that this notice is retained in full, this product may be distributed under the terms of the GNU General Public License (GPL), in which case the provisions of the GPL apply INSTEAD OF those given above. DISCLAIMER This software is provided 'as is' with no explicit or implied warranties in respect of its properties, including, but not limited to, correctness and/or fitness for purpose. --------------------------------------------------------------------------- Issue Date: 30/11/2002 This is a byte oriented version of SHA2 that operates on arrays of bytes stored in memory. This code implements sha256, sha384 and sha512 but the latter two functions rely on efficient 64-bit integer operations that may not be very efficient on 32-bit machines The sha256 functions use a type 'sha256_ctx' to hold details of the current hash state and uses the following three calls: void sha256_begin(sha256_ctx ctx[1]) void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]) void sha256_end(unsigned char hval[], sha256_ctx ctx[1]) The first subroutine initialises a hash computation by setting up the context in the sha256_ctx context. The second subroutine hashes 8-bit bytes from array data[] into the hash state withinh sha256_ctx context, the number of bytes to be hashed being given by the the unsigned long integer len. The third subroutine completes the hash calculation and places the resulting digest value in the array of 8-bit bytes hval[]. The sha384 and sha512 functions are similar and use the interfaces: void sha384_begin(sha384_ctx ctx[1]); void sha384_hash(const unsigned char data[], unsigned long len, sha384_ctx ctx[1]); void sha384_end(unsigned char hval[], sha384_ctx ctx[1]); void sha512_begin(sha512_ctx ctx[1]); void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]); void sha512_end(unsigned char hval[], sha512_ctx ctx[1]); In addition there is a function sha2 that can be used to call all these functions using a call with a hash length parameter as follows: int sha2_begin(unsigned long len, sha2_ctx ctx[1]); void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]); void sha2_end(unsigned char hval[], sha2_ctx ctx[1]); My thanks to Erik Andersen <andersen@codepoet.org> for testing this code on big-endian systems and for his assistance with corrections */ /* define the hash functions that you need */ //#define SHA_2 /* for dynamic hash length */ // $$$MJR - removed #define SHA_256 //#define SHA_384 // $$$MJR - removed //#define SHA_512 // $$$MJR - removed #include <string.h> /* for memcpy() etc. */ #include <stdlib.h> /* for _lrotr with VC++ */ #include <stdarg.h> #include "sha2.h" #include "t3std.h" #include "vmdatasrc.h" /* 1. PLATFORM SPECIFIC INCLUDES */ #if defined(__GNU_LIBRARY__) # include <byteswap.h> # include <endian.h> #elif defined(__CRYPTLIB__) # if defined( INC_ALL ) # include "crypt.h" # elif defined( INC_CHILD ) # include "../crypt.h" # else # include "crypt.h" # endif # if defined(DATA_LITTLEENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # else # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif defined(_MSC_VER) # include <stdlib.h> #elif !defined(WIN32) # include <stdlib.h> # if !defined (_ENDIAN_H) # include <sys/param.h> # else # include _ENDIAN_H # endif #endif /* 2. BYTE ORDER IN 32-BIT WORDS To obtain the highest speed on processors with 32-bit words, this code needs to determine the order in which bytes are packed into such words. The following block of code is an attempt to capture the most obvious ways in which various environemnts specify their endian definitions. It may well fail, in which case the definitions will need to be set by editing at the points marked **** EDIT HERE IF NECESSARY **** below. */ #define SHA_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ #define SHA_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ #if defined(CPU_IS_BIGENDIAN) # if (CPU_IS_BIGENDIAN == 1) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # elif (CPU_IS_BIGENDIAN == 0) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # endif #endif #if !defined(PLATFORM_BYTE_ORDER) #if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN) # if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # if defined(BYTE_ORDER) # if (BYTE_ORDER == LITTLE_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif (BYTE_ORDER == BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif # endif # elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN) # if defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) # if defined(_BYTE_ORDER) # if (_BYTE_ORDER == _LITTLE_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif (_BYTE_ORDER == _BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif # endif # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN # elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN # endif #elif 0 /* **** EDIT HERE IF NECESSARY **** */ #define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN #elif 0 /* **** EDIT HERE IF NECESSARY **** */ #define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN #elif (('1234' >> 24) == '1') # define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN #elif (('4321' >> 24) == '1') # define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN #endif #endif #if !defined(PLATFORM_BYTE_ORDER) # error Please set undetermined byte order (lines 159 or 161 of sha2.c). #endif #ifdef _MSC_VER #pragma intrinsic(memcpy) #endif #define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) #if !defined(bswap_32) #define bswap_32(x) (rotr32((x), 24) & 0x00ff00ff | rotr32((x), 8) & 0xff00ff00) #endif #if (PLATFORM_BYTE_ORDER == SHA_LITTLE_ENDIAN) #define SWAP_BYTES #else #undef SWAP_BYTES #endif #if defined(SHA_2) || defined(SHA_256) #define SHA256_MASK (SHA256_BLOCK_SIZE - 1) #if defined(SWAP_BYTES) #define bsw_32(p,n) { int _i = (n); while(_i--) p[_i] = bswap_32(p[_i]); } #else #define bsw_32(p,n) #endif /* SHA256 mixing function definitions */ #define ch(x,y,z) (((x) & (y)) ^ (~(x) & (z))) #define maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define s256_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) #define s256_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) #define g256_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) #define g256_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) /* rotated SHA256 round definition. Rather than swapping variables as in */ /* FIPS-180, different variables are 'rotated' on each round, returning */ /* to their starting positions every eight rounds */ #define h2(i) ctx->wbuf[i & 15] += \ g256_1(ctx->wbuf[(i + 14) & 15]) + ctx->wbuf[(i + 9) & 15] + g256_0(ctx->wbuf[(i + 1) & 15]) #define h2_cycle(i,j) \ v[(7 - i) & 7] += (j ? h2(i) : ctx->wbuf[i & 15]) + k256[i + j] \ + s256_1(v[(4 - i) & 7]) + ch(v[(4 - i) & 7], v[(5 - i) & 7], v[(6 - i) & 7]); \ v[(3 - i) & 7] += v[(7 - i) & 7]; \ v[(7 - i) & 7] += s256_0(v[(0 - i) & 7]) + maj(v[(0 - i) & 7], v[(1 - i) & 7], v[(2 - i) & 7]) /* SHA256 mixing data */ const sha2_32t k256[64] = { n_u32(428a2f98), n_u32(71374491), n_u32(b5c0fbcf), n_u32(e9b5dba5), n_u32(3956c25b), n_u32(59f111f1), n_u32(923f82a4), n_u32(ab1c5ed5), n_u32(d807aa98), n_u32(12835b01), n_u32(243185be), n_u32(550c7dc3), n_u32(72be5d74), n_u32(80deb1fe), n_u32(9bdc06a7), n_u32(c19bf174), n_u32(e49b69c1), n_u32(efbe4786), n_u32(0fc19dc6), n_u32(240ca1cc), n_u32(2de92c6f), n_u32(4a7484aa), n_u32(5cb0a9dc), n_u32(76f988da), n_u32(983e5152), n_u32(a831c66d), n_u32(b00327c8), n_u32(bf597fc7), n_u32(c6e00bf3), n_u32(d5a79147), n_u32(06ca6351), n_u32(14292967), n_u32(27b70a85), n_u32(2e1b2138), n_u32(4d2c6dfc), n_u32(53380d13), n_u32(650a7354), n_u32(766a0abb), n_u32(81c2c92e), n_u32(92722c85), n_u32(a2bfe8a1), n_u32(a81a664b), n_u32(c24b8b70), n_u32(c76c51a3), n_u32(d192e819), n_u32(d6990624), n_u32(f40e3585), n_u32(106aa070), n_u32(19a4c116), n_u32(1e376c08), n_u32(2748774c), n_u32(34b0bcb5), n_u32(391c0cb3), n_u32(4ed8aa4a), n_u32(5b9cca4f), n_u32(682e6ff3), n_u32(748f82ee), n_u32(78a5636f), n_u32(84c87814), n_u32(8cc70208), n_u32(90befffa), n_u32(a4506ceb), n_u32(bef9a3f7), n_u32(c67178f2), }; /* SHA256 initialisation data */ const sha2_32t i256[8] = { n_u32(6a09e667), n_u32(bb67ae85), n_u32(3c6ef372), n_u32(a54ff53a), n_u32(510e527f), n_u32(9b05688c), n_u32(1f83d9ab), n_u32(5be0cd19) }; void sha256_begin(sha256_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i256, 8 * sizeof(sha2_32t)); } /* Compile 64 bytes of hash data into SHA256 digest value */ /* NOTE: this routine assumes that the byte order in the */ /* ctx->wbuf[] at this point is in such an order that low */ /* address bytes in the ORIGINAL byte stream placed in this */ /* buffer will now go to the high end of words on BOTH big */ /* and little endian systems */ void sha256_compile(sha256_ctx ctx[1]) { sha2_32t v[8], j; memcpy(v, ctx->hash, 8 * sizeof(sha2_32t)); for(j = 0; j < 64; j += 16) { h2_cycle( 0, j); h2_cycle( 1, j); h2_cycle( 2, j); h2_cycle( 3, j); h2_cycle( 4, j); h2_cycle( 5, j); h2_cycle( 6, j); h2_cycle( 7, j); h2_cycle( 8, j); h2_cycle( 9, j); h2_cycle(10, j); h2_cycle(11, j); h2_cycle(12, j); h2_cycle(13, j); h2_cycle(14, j); h2_cycle(15, j); } ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; } /* SHA256 hash data in an array of bytes into hash buffer */ /* and call the hash_compile function as required. */ void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]) { sha2_32t pos = (sha2_32t)(ctx->count[0] & SHA256_MASK), space = SHA256_BLOCK_SIZE - pos; const unsigned char *sp = data; if((ctx->count[0] += len) < len) ++(ctx->count[1]); while(len >= space) /* tranfer whole blocks while possible */ { memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space); sp += space; len -= space; space = SHA256_BLOCK_SIZE; pos = 0; bsw_32(ctx->wbuf, SHA256_BLOCK_SIZE >> 2) sha256_compile(ctx); } memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len); } /* SHA256 Final padding and digest calculation */ static sha2_32t m1[4] = { n_u32(00000000), n_u32(ff000000), n_u32(ffff0000), n_u32(ffffff00) }; static sha2_32t b1[4] = { n_u32(80000000), n_u32(00800000), n_u32(00008000), n_u32(00000080) }; void sha256_end(unsigned char hval[], sha256_ctx ctx[1]) { sha2_32t i = (sha2_32t)(ctx->count[0] & SHA256_MASK); bsw_32(ctx->wbuf, (i + 3) >> 2) /* bytes in the buffer are now in an order in which references */ /* to 32-bit words will put bytes with lower addresses into the */ /* top of 32 bit words on BOTH big and little endian machines */ /* we now need to mask valid bytes and add the padding which is */ /* a single 1 bit and as many zero bits as necessary. */ ctx->wbuf[i >> 2] = (ctx->wbuf[i >> 2] & m1[i & 3]) | b1[i & 3]; /* we need 9 or more empty positions, one for the padding byte */ /* (above) and eight for the length count. If there is not */ /* enough space pad and empty the buffer */ if(i > SHA256_BLOCK_SIZE - 9) { if(i < 60) ctx->wbuf[15] = 0; sha256_compile(ctx); i = 0; } else /* compute a word index for the empty buffer positions */ i = (i >> 2) + 1; while(i < 14) /* and zero pad all but last two positions */ ctx->wbuf[i++] = 0; /* the following 32-bit length fields are assembled in the */ /* wrong byte order on little endian machines but this is */ /* corrected later since they are only ever used as 32-bit */ /* word values. */ ctx->wbuf[14] = (ctx->count[1] << 3) | (ctx->count[0] >> 29); ctx->wbuf[15] = ctx->count[0] << 3; sha256_compile(ctx); /* extract the hash value as bytes in case the hash buffer is */ /* mislaigned for 32-bit words */ for(i = 0; i < SHA256_DIGEST_SIZE; ++i) hval[i] = (unsigned char)(ctx->hash[i >> 2] >> 8 * (~i & 3)); } void sha256(unsigned char hval[], const unsigned char data[], unsigned long len) { sha256_ctx cx[1]; sha256_begin(cx); sha256_hash(data, len, cx); sha256_end(hval, cx); } #endif #if defined(SHA_2) || defined(SHA_384) || defined(SHA_512) #define SHA512_MASK (SHA512_BLOCK_SIZE - 1) #define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) #if !defined(bswap_64) #define bswap_64(x) (((sha2_64t)(bswap_32((sha2_32t)(x)))) << 32 | bswap_32((sha2_32t)((x) >> 32))) #endif #if defined(SWAP_BYTES) #define bsw_64(p,n) { int _i = (n); while(_i--) p[_i] = bswap_64(p[_i]); } #else #define bsw_64(p,n) #endif /* SHA512 mixing function definitions */ #define s512_0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) #define s512_1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) #define g512_0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) #define g512_1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) /* rotated SHA512 round definition. Rather than swapping variables as in */ /* FIPS-180, different variables are 'rotated' on each round, returning */ /* to their starting positions every eight rounds */ #define h5(i) ctx->wbuf[i & 15] += \ g512_1(ctx->wbuf[(i + 14) & 15]) + ctx->wbuf[(i + 9) & 15] + g512_0(ctx->wbuf[(i + 1) & 15]) #define h5_cycle(i,j) \ v[(7 - i) & 7] += (j ? h5(i) : ctx->wbuf[i & 15]) + k512[i + j] \ + s512_1(v[(4 - i) & 7]) + ch(v[(4 - i) & 7], v[(5 - i) & 7], v[(6 - i) & 7]); \ v[(3 - i) & 7] += v[(7 - i) & 7]; \ v[(7 - i) & 7] += s512_0(v[(0 - i) & 7]) + maj(v[(0 - i) & 7], v[(1 - i) & 7], v[(2 - i) & 7]) /* SHA384/SHA512 mixing data */ const sha2_64t k512[80] = { n_u64(428a2f98d728ae22), n_u64(7137449123ef65cd), n_u64(b5c0fbcfec4d3b2f), n_u64(e9b5dba58189dbbc), n_u64(3956c25bf348b538), n_u64(59f111f1b605d019), n_u64(923f82a4af194f9b), n_u64(ab1c5ed5da6d8118), n_u64(d807aa98a3030242), n_u64(12835b0145706fbe), n_u64(243185be4ee4b28c), n_u64(550c7dc3d5ffb4e2), n_u64(72be5d74f27b896f), n_u64(80deb1fe3b1696b1), n_u64(9bdc06a725c71235), n_u64(c19bf174cf692694), n_u64(e49b69c19ef14ad2), n_u64(efbe4786384f25e3), n_u64(0fc19dc68b8cd5b5), n_u64(240ca1cc77ac9c65), n_u64(2de92c6f592b0275), n_u64(4a7484aa6ea6e483), n_u64(5cb0a9dcbd41fbd4), n_u64(76f988da831153b5), n_u64(983e5152ee66dfab), n_u64(a831c66d2db43210), n_u64(b00327c898fb213f), n_u64(bf597fc7beef0ee4), n_u64(c6e00bf33da88fc2), n_u64(d5a79147930aa725), n_u64(06ca6351e003826f), n_u64(142929670a0e6e70), n_u64(27b70a8546d22ffc), n_u64(2e1b21385c26c926), n_u64(4d2c6dfc5ac42aed), n_u64(53380d139d95b3df), n_u64(650a73548baf63de), n_u64(766a0abb3c77b2a8), n_u64(81c2c92e47edaee6), n_u64(92722c851482353b), n_u64(a2bfe8a14cf10364), n_u64(a81a664bbc423001), n_u64(c24b8b70d0f89791), n_u64(c76c51a30654be30), n_u64(d192e819d6ef5218), n_u64(d69906245565a910), n_u64(f40e35855771202a), n_u64(106aa07032bbd1b8), n_u64(19a4c116b8d2d0c8), n_u64(1e376c085141ab53), n_u64(2748774cdf8eeb99), n_u64(34b0bcb5e19b48a8), n_u64(391c0cb3c5c95a63), n_u64(4ed8aa4ae3418acb), n_u64(5b9cca4f7763e373), n_u64(682e6ff3d6b2b8a3), n_u64(748f82ee5defb2fc), n_u64(78a5636f43172f60), n_u64(84c87814a1f0ab72), n_u64(8cc702081a6439ec), n_u64(90befffa23631e28), n_u64(a4506cebde82bde9), n_u64(bef9a3f7b2c67915), n_u64(c67178f2e372532b), n_u64(ca273eceea26619c), n_u64(d186b8c721c0c207), n_u64(eada7dd6cde0eb1e), n_u64(f57d4f7fee6ed178), n_u64(06f067aa72176fba), n_u64(0a637dc5a2c898a6), n_u64(113f9804bef90dae), n_u64(1b710b35131c471b), n_u64(28db77f523047d84), n_u64(32caab7b40c72493), n_u64(3c9ebe0a15c9bebc), n_u64(431d67c49c100d4c), n_u64(4cc5d4becb3e42b6), n_u64(597f299cfc657e2a), n_u64(5fcb6fab3ad6faec), n_u64(6c44198c4a475817) }; /* Compile 64 bytes of hash data into SHA384/SHA512 digest value */ void sha512_compile(sha512_ctx ctx[1]) { sha2_64t v[8]; sha2_32t j; memcpy(v, ctx->hash, 8 * sizeof(sha2_64t)); for(j = 0; j < 80; j += 16) { h5_cycle( 0, j); h5_cycle( 1, j); h5_cycle( 2, j); h5_cycle( 3, j); h5_cycle( 4, j); h5_cycle( 5, j); h5_cycle( 6, j); h5_cycle( 7, j); h5_cycle( 8, j); h5_cycle( 9, j); h5_cycle(10, j); h5_cycle(11, j); h5_cycle(12, j); h5_cycle(13, j); h5_cycle(14, j); h5_cycle(15, j); } ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; ctx->hash[6] += v[6]; ctx->hash[7] += v[7]; } /* Compile 128 bytes of hash data into SHA256 digest value */ /* NOTE: this routine assumes that the byte order in the */ /* ctx->wbuf[] at this point is in such an order that low */ /* address bytes in the ORIGINAL byte stream placed in this */ /* buffer will now go to the high end of words on BOTH big */ /* and little endian systems */ void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]) { sha2_32t pos = (sha2_32t)(ctx->count[0] & SHA512_MASK), space = SHA512_BLOCK_SIZE - pos; const unsigned char *sp = data; if((ctx->count[0] += len) < len) ++(ctx->count[1]); while(len >= space) /* tranfer whole blocks while possible */ { memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space); sp += space; len -= space; space = SHA512_BLOCK_SIZE; pos = 0; bsw_64(ctx->wbuf, SHA512_BLOCK_SIZE >> 3); sha512_compile(ctx); } memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len); } /* SHA384/512 Final padding and digest calculation */ static sha2_64t m2[8] = { n_u64(0000000000000000), n_u64(ff00000000000000), n_u64(ffff000000000000), n_u64(ffffff0000000000), n_u64(ffffffff00000000), n_u64(ffffffffff000000), n_u64(ffffffffffff0000), n_u64(ffffffffffffff00) }; static sha2_64t b2[8] = { n_u64(8000000000000000), n_u64(0080000000000000), n_u64(0000800000000000), n_u64(0000008000000000), n_u64(0000000080000000), n_u64(0000000000800000), n_u64(0000000000008000), n_u64(0000000000000080) }; static void sha_end(unsigned char hval[], sha512_ctx ctx[1], const unsigned int hlen) { sha2_32t i = (sha2_32t)(ctx->count[0] & SHA512_MASK); bsw_64(ctx->wbuf, (i + 7) >> 3); /* bytes in the buffer are now in an order in which references */ /* to 64-bit words will put bytes with lower addresses into the */ /* top of 64 bit words on BOTH big and little endian machines */ /* we now need to mask valid bytes and add the padding which is */ /* a single 1 bit and as many zero bits as necessary. */ ctx->wbuf[i >> 3] = (ctx->wbuf[i >> 3] & m2[i & 7]) | b2[i & 7]; /* we need 17 or more empty byte positions, one for the padding */ /* byte (above) and sixteen for the length count. If there is */ /* not enough space pad and empty the buffer */ if(i > SHA512_BLOCK_SIZE - 17) { if(i < 120) ctx->wbuf[15] = 0; sha512_compile(ctx); i = 0; } else i = (i >> 3) + 1; while(i < 14) ctx->wbuf[i++] = 0; /* the following 64-bit length fields are assembled in the */ /* wrong byte order on little endian machines but this is */ /* corrected later since they are only ever used as 64-bit */ /* word values. */ ctx->wbuf[14] = (ctx->count[1] << 3) | (ctx->count[0] >> 61); ctx->wbuf[15] = ctx->count[0] << 3; sha512_compile(ctx); /* extract the hash value as bytes in case the hash buffer is */ /* misaligned for 32-bit words */ for(i = 0; i < hlen; ++i) hval[i] = (unsigned char)(ctx->hash[i >> 3] >> 8 * (~i & 7)); } #endif #if defined(SHA_2) || defined(SHA_384) /* SHA384 initialisation data */ const sha2_64t i384[80] = { n_u64(cbbb9d5dc1059ed8), n_u64(629a292a367cd507), n_u64(9159015a3070dd17), n_u64(152fecd8f70e5939), n_u64(67332667ffc00b31), n_u64(8eb44a8768581511), n_u64(db0c2e0d64f98fa7), n_u64(47b5481dbefa4fa4) }; void sha384_begin(sha384_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i384, 8 * sizeof(sha2_64t)); } void sha384_end(unsigned char hval[], sha384_ctx ctx[1]) { sha_end(hval, ctx, SHA384_DIGEST_SIZE); } void sha384(unsigned char hval[], const unsigned char data[], unsigned long len) { sha384_ctx cx[1]; sha384_begin(cx); sha384_hash(data, len, cx); sha384_end(hval, cx); } #endif #if defined(SHA_2) || defined(SHA_512) /* SHA512 initialisation data */ const sha2_64t i512[80] = { n_u64(6a09e667f3bcc908), n_u64(bb67ae8584caa73b), n_u64(3c6ef372fe94f82b), n_u64(a54ff53a5f1d36f1), n_u64(510e527fade682d1), n_u64(9b05688c2b3e6c1f), n_u64(1f83d9abfb41bd6b), n_u64(5be0cd19137e2179) }; void sha512_begin(sha512_ctx ctx[1]) { ctx->count[0] = ctx->count[1] = 0; memcpy(ctx->hash, i512, 8 * sizeof(sha2_64t)); } void sha512_end(unsigned char hval[], sha512_ctx ctx[1]) { sha_end(hval, ctx, SHA512_DIGEST_SIZE); } void sha512(unsigned char hval[], const unsigned char data[], unsigned long len) { sha512_ctx cx[1]; sha512_begin(cx); sha512_hash(data, len, cx); sha512_end(hval, cx); } #endif #if defined(SHA_2) #define CTX_256(x) ((x)->uu->ctx256) #define CTX_384(x) ((x)->uu->ctx512) #define CTX_512(x) ((x)->uu->ctx512) /* SHA2 initialisation */ int sha2_begin(unsigned long len, sha2_ctx ctx[1]) { unsigned long l = len; switch(len) { case 256: l = len >> 3; case 32: CTX_256(ctx)->count[0] = CTX_256(ctx)->count[1] = 0; memcpy(CTX_256(ctx)->hash, i256, 32); break; case 384: l = len >> 3; case 48: CTX_384(ctx)->count[0] = CTX_384(ctx)->count[1] = 0; memcpy(CTX_384(ctx)->hash, i384, 64); break; case 512: l = len >> 3; case 64: CTX_512(ctx)->count[0] = CTX_512(ctx)->count[1] = 0; memcpy(CTX_512(ctx)->hash, i512, 64); break; default: return SHA2_BAD; } ctx->sha2_len = l; return SHA2_GOOD; } void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]) { switch(ctx->sha2_len) { case 32: sha256_hash(data, len, CTX_256(ctx)); return; case 48: sha384_hash(data, len, CTX_384(ctx)); return; case 64: sha512_hash(data, len, CTX_512(ctx)); return; } } void sha2_end(unsigned char hval[], sha2_ctx ctx[1]) { switch(ctx->sha2_len) { case 32: sha256_end(hval, CTX_256(ctx)); return; case 48: sha_end(hval, CTX_384(ctx), SHA384_DIGEST_SIZE); return; case 64: sha_end(hval, CTX_512(ctx), SHA512_DIGEST_SIZE); return; } } int sha2(unsigned char hval[], unsigned long size, const unsigned char data[], unsigned long len) { sha2_ctx cx[1]; if(sha2_begin(size, cx) == SHA2_GOOD) { sha2_hash(data, len, cx); sha2_end(hval, cx); return SHA2_GOOD; } else return SHA2_BAD; } #endif /* ------------------------------------------------------------------------ */ /* * MJR additions */ /* * E-Z sha256 - hash a buffer and generate a printable hash string [mjr] */ void sha256_ez(char *hash, const char *data, size_t len) { sha256_ctx ctx; const int HASH_BYTES = 32; unsigned char hval[HASH_BYTES]; /* calculate the hash in binary format */ sha256_begin(&ctx); sha256_hash((const unsigned char *)data, len, &ctx); sha256_end(hval, &ctx); /* convert the binary hash to printable hex digits */ for (int i = 0 ; i < HASH_BYTES ; ++i, hash += 2) byte_to_xdigits(hash, hval[i]); /* null-terminate the string */ *hash = '\0'; } /* * Printf-style sha256 hashing [mjr] */ void sha256_ezf(char *hash, const char *fmt, ...) { /* generate the hash source */ va_list args; va_start(args, fmt); char *src = t3vsprintf_alloc(fmt, args); va_end(args); /* hash the result */ sha256_ez(hash, src, strlen(src)); /* done with the source buffer */ t3free(src); } /* * E-Z sha256 - hash from a data source [mjr] */ void sha256_datasrc(char *buf, CVmDataSource *src, unsigned long len) { /* set up the hash accumulator */ sha256_ctx ctx; sha256_begin(&ctx); /* feed the selected range of bytes into the hash */ while (len != 0) { /* get a chunk up to one buffer-full, or the total remaining */ unsigned char buf[1024]; size_t cur = (len > sizeof(buf) ? sizeof(buf) : (size_t)len); /* read the chunk */ if (cur != 0) cur = src->readc(buf, cur); /* if there's nothing left, we're done */ if (cur == 0) break; /* feed this chunk into the hash */ sha256_hash(buf, cur, &ctx); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the hash result */ const int HASH_BYTES = 32; unsigned char hash[HASH_BYTES]; sha256_end(hash, &ctx); /* convert the binary hash to printable hex digits */ char *bufp = buf; for (int i = 0 ; i < HASH_BYTES ; ++i, bufp += 2) byte_to_xdigits(bufp, hash[i]); /* null-terminate the string */ *bufp = '\0'; } ������������������������������������������������������������������frobtads-1.2.3/tads3/vmbifl.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000004321�11364312563�015511� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifl.cpp - built-in function - Load-time resolution Function This is a version of the built-in function interface for resolving built-ins on loading the image file. This version makes no checks on the availability of a function when it's invoked. This version can be used in a normal stand-alone interpreter. A version of the interpreter that's used to complete compilation by running 'preinit' should use the call-time resolution version instead. Notes Modified 07/21/99 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include "t3std.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmbif.h" #include "vmbifreg.h" #include "vmstr.h" #include "vmobj.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Call the given function from the given function set. */ void CVmBifTable::call_func(VMG_ uint set_index, uint func_index, uint argc) { /* * find the function set in the registration table, get the function * descriptor from the set's function table, and invoke the function * pointer from the function descriptor */ (*table_[set_index]->func[func_index].func)(vmg_ argc); } /* * Get a function's descriptor */ const vm_bif_desc *CVmBifTable::get_desc(uint set_index, uint func_index) { /* * find the function in the registration table, and return the function * descriptor from the set's function table */ return &table_[set_index]->func[func_index]; } /* * Handle adding a function set entry that's unresolvable at load-time */ void CVmBifTable::add_entry_unresolved(VMG_ const char *func_set_id) { /* this is the static-link version, so an unresolved entry is an error */ err_throw_a(VMERR_UNKNOWN_FUNC_SET, 3, ERR_TYPE_TEXTCHAR, func_set_id, ERR_TYPE_FUNCSET, func_set_id, ERR_TYPE_VERSION_FLAG); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmsrcf.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000015320�07627507540�015543� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsrcf.cpp - T3 VM source file list Function Notes Modified 12/01/99 MJRoberts - Creation */ #include "t3std.h" #include "vmsrcf.h" /* ------------------------------------------------------------------------ */ /* * Source file table implementation */ /* * Initialize */ CVmSrcfTable::CVmSrcfTable() { /* no entries yet */ list_ = 0; list_used_ = 0; list_alloc_ = 0; } /* * Delete */ CVmSrcfTable::~CVmSrcfTable() { /* delete the list if we allocated one */ if (list_ != 0) { /* clear the table */ clear(); /* delete the list */ t3free(list_); } } /* * Clear the table */ void CVmSrcfTable::clear() { size_t i; /* delete each entry */ for (i = 0 ; i < list_used_ ; ++i) delete(list_[i]); /* * we no longer have any entries, but leave the list allocated in * case we add entries again */ list_used_ = 0; } /* * Add a new entry to the table */ CVmSrcfEntry *CVmSrcfTable::add_entry(int orig_index, size_t name_len) { CVmSrcfEntry *entry; /* allocate the new entry */ entry = new CVmSrcfEntry(orig_index, (uint)orig_index == list_used_, name_len); /* * make sure we have room in our list for a new entry; if not, * expand the list */ if (list_used_ >= list_alloc_) { size_t siz; /* calculate the expanded list size */ list_alloc_ += 10; siz = list_alloc_ * sizeof(list_[0]); /* allocate or reallocate the list */ if (list_ == 0) list_ = (CVmSrcfEntry **)t3malloc(siz); else list_ = (CVmSrcfEntry **)t3realloc(list_, siz); } /* add the new entry */ list_[list_used_] = entry; /* count the new entry */ ++list_used_; /* return the new entry */ return entry; } /* ------------------------------------------------------------------------ */ /* * Source file record implementation */ /* * Allocate or expand the line records array */ void CVmSrcfEntry::alloc_line_records(ulong cnt) { ulong siz; /* * if the current array size is already big enough for the given * count, there's nothing to do */ if (cnt <= lines_alo_) return; /* calculate the allocation size */ siz = cnt * sizeof(lines_[0]); /* * if the new size exceeds the maximum local system's architectural * limit for a single allocation, restrict the size to the maximum * allocation */ if (siz > OSMALMAX) { /* recalculate the size for the maximum architectural allocation */ cnt = OSMALMAX / sizeof(lines_[0]); siz = cnt * sizeof(lines_[0]); /* if we're already there, ignore the request */ if (cnt <= lines_alo_) return; } /* allocate or reallocate the line record array */ if (lines_ == 0) lines_ = (CVmSrcfLine *)t3malloc((size_t)siz); else lines_ = (CVmSrcfLine *)t3realloc(lines_, (size_t)siz); /* remember the new array size */ lines_alo_ = cnt; } /* * add a line record */ void CVmSrcfEntry::add_line_record(ulong linenum, ulong code_addr) { /* make sure we have enough space */ if (lines_cnt_ >= lines_alo_) { /* expand the line records array */ alloc_line_records(lines_alo_ + 1024); /* if that didn't create enough space, ignore the new record */ if (lines_cnt_ >= lines_alo_) return; } /* add the new record */ lines_[lines_cnt_].linenum = linenum; lines_[lines_cnt_].code_addr = code_addr; /* count the new record */ ++lines_cnt_; } /* * Find a source line */ ulong CVmSrcfEntry::find_src_addr(ulong *linenum, int exact) { long hi, lo, cur; /* if there are no line records, return failure */ if (lines_cnt_ == 0) return 0; /* perform a binary search of the line record array */ lo = 0; hi = (long)lines_cnt_ - 1; while (lo <= hi) { int match; /* split the difference */ cur = lo + (hi - lo)/2; /* * Check for a match. If they require an exact match, we must * find the line number exactly. Otherwise, check to see if * this is the next executable line after the given line. */ if (exact) { /* exact match required */ match = (lines_[cur].linenum == *linenum); } else { /* * exact match not required - match if this is the next * executable line after the requested line, or this is the * last executable line and the requested line is higher */ if (cur == 0) { /* * this is the first executable line - if the requested * line is before this one, this is our match */ match = (*linenum <= lines_[cur].linenum); } else if (cur == (long)lines_cnt_ - 1) { /* * this is the last executable line - if the requested * line is after this one, this is our match */ match = (*linenum >= lines_[cur].linenum); } else { /* * we're somewhere in the middle of the file - if the * requested line is before this one, but after the * previous executable line, this is our match */ match = (*linenum <= lines_[cur].linenum && *linenum > lines_[cur - 1].linenum); } } /* if we have a match, return it; otherwise, keep searching */ if (match) { /* * if this is a non-exact match, update the caller's line * number with the actual line number we found */ *linenum = lines_[cur].linenum; /* return our code address */ return lines_[cur].code_addr; } else if (*linenum > lines_[cur].linenum) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? hi - 1 : cur); } } /* failure */ return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmpool.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000016642�11360325015�015547� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpool.cpp - constant pool implementation Function Notes Modified 10/20/98 MJRoberts - Creation */ #include <stdlib.h> #include <memory.h> #include "t3std.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Basic pool implementation */ /* * Get the number of pages in the pool */ size_t CVmPool::get_page_count() const { /* get the page count from the backing store */ return backing_store_->vmpbs_get_page_count(); } /* * Attach to a backing store */ void CVmPool::attach_backing_store(CVmPoolBackingStore *backing_store) { /* remember the backing store */ backing_store_ = backing_store; /* get the page size from the backing store */ page_size_ = backing_store->vmpbs_get_common_page_size(); } /* ------------------------------------------------------------------------ */ /* * Base paged pool implementation */ /* * delete our page list */ void CVmPoolPaged::delete_page_list() { /* if there's a page list, delete it */ if (pages_ != 0) { /* free the page array */ t3free(pages_); /* forget it */ pages_ = 0; page_slots_ = 0; page_slots_max_ = 0; } /* we can no longer have a backing store */ backing_store_ = 0; } /* * Attach to a backing store */ void CVmPoolPaged::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t cur; size_t log2; /* delete any existing page list */ delete_page_list(); /* inherit default handling */ CVmPool::attach_backing_store(backing_store); /* * if the page size is zero, there must not be any pages at all - * use a dummy default page size */ if (page_size_ == 0) page_size_ = 1024; /* * Compute log2 of the page size. If the page size isn't a power of * two, throw an error. */ for (cur = page_size_, log2 = 0 ; (cur & 1) == 0 ; cur >>= 1, ++log2) ; if (cur != 1) err_throw(VMERR_BAD_POOL_PAGE_SIZE); /* store log2(page_size_) in log2_page_size_ */ log2_page_size_ = log2; /* allocate the pages */ alloc_page_slots(backing_store_->vmpbs_get_page_count()); } /* * Allocate a page slot */ void CVmPoolPaged::alloc_page_slots(size_t nslots) { size_t old_slots; size_t i; /* note the old slot count */ old_slots = page_slots_; /* if the new size isn't bigger than the old size, ignore the request */ if (nslots <= page_slots_) return; /* if necessary, expand the master page array */ if (nslots > page_slots_max_) { size_t siz; /* * Increase the maximum, leaving some room for dynamically added * pages. */ page_slots_max_ = nslots + 10; /* calculate the new allocation size */ siz = page_slots_max_ * sizeof(pages_[0]); /* allocate or reallocate at the new size */ if (pages_ == 0) pages_ = (CVmPool_pg *)t3malloc(siz); else pages_ = (CVmPool_pg *)t3realloc(pages_, siz); } /* set the new size */ page_slots_ = nslots; /* clear the new subarrays */ for (i = old_slots ; i < page_slots_ ; ++i) pages_[i].mem = 0; } /* ------------------------------------------------------------------------ */ /* * Two-level paged pool implementation. This is a variation of the regular * paged pool that uses a two-level page table. This implementation is not * currently used, because it is less efficient than the single-page pool * and is not needed for modern machines with large contiguous address * spaces; however, we retain it in the event it's needed for 16-bit * segmented architectures, where it might not be possible or convenient to * allocate a sufficiently large master page table and thus a two-level * table is needed. */ #if 0 /* * delete the pool - deletes all allocated pages */ CVmPoolPaged2::~CVmPoolPaged2() { /* free our page memory */ delete_page_list(); } /* * delete our page list */ void CVmPoolPaged2::delete_page_list() { /* if there's a page list, delete it */ if (pages_ != 0) { size_t i; size_t cnt; /* free each subarray */ cnt = get_subarray_count(); for (i = 0 ; i < cnt ; ++i) t3free(pages_[i]); /* free the master array */ t3free(pages_); /* forget about the page list */ pages_ = 0; } } /* * Attach to a backing store */ void CVmPoolPaged2::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t cur; size_t log2; /* delete any existing page list */ delete_page_list(); /* remember the backing store */ backing_store_ = backing_store; /* get the page size from the backing store */ page_size_ = backing_store_->vmpbs_get_common_page_size(); /* * if the page size is zero, there must not be any pages at all - use a * dummy default page size */ if (page_size_ == 0) page_size_ = 1024; /* * Compute log2 of the page size. If the page size isn't a power of * two, throw an error. */ for (cur = page_size_, log2 = 0 ; (cur & 1) == 0 ; cur >>= 1, ++log2) ; if (cur != 1) err_throw(VMERR_BAD_POOL_PAGE_SIZE); /* store log2(page_size_) in log2_page_size_ */ log2_page_size_ = log2; /* allocate the pages */ alloc_page_slots(backing_store_->vmpbs_get_page_count()); } /* * Allocate a page slot */ void CVmPoolPaged2::alloc_page_slots(size_t nslots) { size_t old_slots; size_t old_sub_cnt; size_t new_sub_cnt; /* note the old slot count */ old_slots = page_slots_; /* if the new size isn't bigger than the old size, ignore the request */ if (nslots <= page_slots_) return; /* note the original subarray count */ old_sub_cnt = get_subarray_count(); /* set the new size */ page_slots_ = nslots; /* note the new subarray count */ new_sub_cnt = get_subarray_count(); /* allocate or expand the master array */ if (new_sub_cnt > old_sub_cnt) { size_t siz; size_t i; /* figure the new size */ siz = new_sub_cnt * sizeof(pages_[0]); /* allocate or re-allocate the master array */ if (pages_ == 0) pages_ = (CVmPool_pg **)t3malloc(siz); else pages_ = (CVmPool_pg **)t3realloc(pages_, siz); /* throw an error if that failed */ if (pages_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the new slots */ memset(pages_ + old_sub_cnt, 0, (new_sub_cnt - old_sub_cnt) * sizeof(pages_[0])); /* allocate the subarrays */ for (i = old_sub_cnt ; i < new_sub_cnt ; ++i) { /* allocate this subarray */ pages_[i] = (CVmPool_pg *)t3malloc(VMPOOL_SUBARRAY_SIZE * sizeof(pages_[i][0])); /* make sure we got the space */ if (pages_[i] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the page */ memset(pages_[i], 0, VMPOOL_SUBARRAY_SIZE * sizeof(pages_[i][0])); } } } #endif /* removed 2-paged pool */ ����������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmhttpsrv.h��������������������������������������������������������������������0000644�0001750�0000144�00000016657�11546174133�015774� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpsrv.h - CVmObjHTTPServer object Function Notes Modified 04/22/10 MJRoberts - Creation */ #ifndef VMHTTPSRV_H #define VMHTTPSRV_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmnet.h" /* ------------------------------------------------------------------------ */ /* * HTTP server objects have no image file representation, because these * objects are inherently transient. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_httpsrv_ext { /* allocate the structure */ static vm_httpsrv_ext *alloc_ext(VMG_ class CVmObjHTTPServer *self); /* the listener object */ class TadsListener *l; /* original requested listener address */ char *addr; }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer intrinsic class definition */ class CVmObjHTTPServer: public CVmObject { friend class CVmMetaclassHTTPServer; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjHTTPServer object? */ static int is_httpsrv_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record - we don't keep any undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *) { } /* mark our undo record references - we don't have undo or references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references - we don't have any to mark */ void mark_refs(VMG_ uint) { } /* remove weak references - we don't have any weak refs */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } /* * Get my listening address and port number. 'host' is the host name * original specified in the constructor, and 'ip' is the listener IP * address in decimal 1.2.3.4 notation. 'port' is the actual listening * port number. The 'host' and 'ip' strings must be deleted by the * caller via delete[]. */ int get_listener_addr(char *&addr, char *&ip, int &port) const; protected: /* get my extension data */ vm_httpsrv_ext *get_ext() const { return (vm_httpsrv_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjHTTPServer() { ext_ = 0; } /* create with contents ('flag' is just for overloading resolution) */ CVmObjHTTPServer(VMG_ int flag); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* shut down the server */ int getp_shutdown(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the port number */ int getp_get_port(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the original listening address */ int getp_get_addr(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the listening address in IP format */ int getp_get_ip(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjHTTPServer::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer metaclass registration table object */ class CVmMetaclassHTTPServer: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "http-server/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPServer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPServer(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjHTTPServer::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjHTTPServer:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMHTTPSRV_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjHTTPServer) ���������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmop.cpp�����������������������������������������������������������������������0000644�0001750�0000144�00000051304�11730223015�015204� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmop.cpp - TADS 3 VM opcode definitions Function Notes Modified 03/14/12 MJRoberts - Creation */ #include "t3std.h" #include "vmop.h" #include "vmtype.h" /* * Get the size of an opcode */ size_t CVmOpcodes::get_op_size(const uchar *op) { switch (*op) { case OPC_PUSHSTRI: return 3 + osrp2(op+1); case OPC_SWITCH: return 1 + 2 + (VMB_DATAHOLDER + 2)*osrp2(op+1) + 2; case OPC_NAMEDARGTAB: return 1 + 2 + osrp2(op+1); default: /* all others have fixed sizes */ return op_siz[*op]; } } /* * Table of T3 VM opcode sizes */ const uchar CVmOpcodes::op_siz[] = { 0, /* 0x00 - unused */ 1, /* 0x01 - OPC_PUSH_0 */ 1, /* 0x02 - OPC_PUSH_1 */ 2, /* 0x03 - OPC_PUSHINT8 */ 5, /* 0x04 - OPC_PUSHINT */ 5, /* 0x05 - OPC_PUSHSTR */ 5, /* 0x06 - OPC_PUSHLST */ 5, /* 0x07 - OPC_PUSHOBJ */ 1, /* 0x08 - OPC_PUSHNIL */ 1, /* 0x09 - OPC_PUSHTRUE */ 3, /* 0x0A - OPC_PUSHPROPID */ 5, /* 0x0B - OPC_PUSHFNPTR */ 0, /* 0x0C - OPC_PUSHSTRI - variable-size instruction */ 2, /* 0x0D - OPC_PUSHPARLST */ 1, /* 0x0E - OPC_MAKELSTPAR */ 5, /* 0x0F - OPC_PUSHENUM */ 5, /* 0x10 - OPC_PUSHBIFPTR */ 1, /* 0x11 - unused */ 1, /* 0x12 - unused */ 1, /* 0x13 - unused */ 1, /* 0x14 - unused */ 1, /* 0x15 - unused */ 1, /* 0x16 - unused */ 1, /* 0x17 - unused */ 1, /* 0x18 - unused */ 1, /* 0x19 - unused */ 1, /* 0x1A - unused */ 1, /* 0x1B - unused */ 1, /* 0x1C - unused */ 1, /* 0x1D - unused */ 1, /* 0x1E - unused */ 1, /* 0x1F - unused */ 1, /* 0x20 - OPC_NEG */ 1, /* 0x21 - OPC_BNOT */ 1, /* 0x22 - OPC_ADD */ 1, /* 0x23 - OPC_SUB */ 1, /* 0x24 - OPC_MUL */ 1, /* 0x25 - OPC_BAND */ 1, /* 0x26 - OPC_BOR */ 1, /* 0x27 - OPC_SHL */ 1, /* 0x28 - OPC_ASHR */ 1, /* 0x29 - OPC_XOR */ 1, /* 0x2A - OPC_DIV */ 1, /* 0x2B - OPC_MOD */ 1, /* 0x2C - OPC_NOT */ 1, /* 0x2D - OPC_BOOLIZE */ 1, /* 0x2E - OPC_INC */ 1, /* 0x2F - OPC_DEC */ 1, /* 0x31 - OPC_LSHR */ 1, /* 0x31 - unused */ 1, /* 0x32 - unused */ 1, /* 0x33 - unused */ 1, /* 0x34 - unused */ 1, /* 0x35 - unused */ 1, /* 0x36 - unused */ 1, /* 0x37 - unused */ 1, /* 0x38 - unused */ 1, /* 0x39 - unused */ 1, /* 0x3A - unused */ 1, /* 0x3B - unused */ 1, /* 0x3C - unused */ 1, /* 0x3D - unused */ 1, /* 0x3E - unused */ 1, /* 0x3F - unused */ 1, /* 0x40 - OPC_EQ */ 1, /* 0x41 - OPC_NE */ 1, /* 0x42 - OPC_LT */ 1, /* 0x43 - OPC_LE */ 1, /* 0x44 - OPC_GT */ 1, /* 0x45 - OPC_GE */ 1, /* 0x46 - unused */ 1, /* 0x47 - unused */ 1, /* 0x48 - unused */ 1, /* 0x49 - unused */ 1, /* 0x4A - unused */ 1, /* 0x4B - unused */ 1, /* 0x4C - unused */ 1, /* 0x4D - unused */ 1, /* 0x4E - unused */ 1, /* 0x4F - unused */ 1, /* 0x50 - OPC_RETVAL */ 1, /* 0x51 - OPC_RETNIL */ 1, /* 0x52 - OPC_RETTRUE */ 1, /* 0x53 - unused */ 1, /* 0x54 - OPC_RET */ 1, /* 0x55 - unused */ 4, /* 0x56 - OPC_NAMEDARGPTR */ 0, /* 0x57 - OPC_NAMEDARGTAB - variable-size operands */ 6, /* 0x58 - OPC_CALL */ 2, /* 0x59 - OPC_PTRCALL */ 1, /* 0x5A - unused */ 1, /* 0x5B - unused */ 1, /* 0x5C - unused */ 1, /* 0x5D - unused */ 1, /* 0x5E - unused */ 1, /* 0x5F - unused */ 3, /* 0x60 - OPC_GETPROP */ 4, /* 0x61 - OPC_CALLPROP */ 2, /* 0x62 - OPC_PTRCALLPROP */ 3, /* 0x63 - OPC_GETPROPSELF */ 4, /* 0x64 - OPC_CALLPROPSELF */ 2, /* 0x65 - OPC_PTRCALLPROPSELF */ 7, /* 0x66 - OPC_OBJGETPROP */ 8, /* 0x67 - OPC_OBJCALLPROP */ 3, /* 0x68 - OPC_GETPROPDATA */ 1, /* 0x69 - OPC_PTRGETPROPDATA */ 4, /* 0x6A - OPC_GETPROPLCL1 */ 5, /* 0x6B - OPC_CALLPROPLCL1 */ 3, /* 0x6C - OPC_GETPROPR0 */ 4, /* 0x6D - OPC_CALLPROPR0 */ 1, /* 0x6E - unused */ 1, /* 0x6F - unused */ 1, /* 0x70 - unused */ 1, /* 0x71 - unused */ 4, /* 0x72 - OPC_INHERIT */ 2, /* 0x73 - OPC_PTRINHERIT */ 8, /* 0x74 - OPC_EXPINHERIT */ 6, /* 0x75 - OPC_PTREXPINHERIT */ 1, /* 0x76 - OPC_VARARGC */ 4, /* 0x77 - OPC_DELEGATE */ 2, /* 0x78 - OPC_PTRDELEGATE */ 1, /* 0x79 - unused */ 1, /* 0x7A - SWAP2 */ 3, /* 0x7B - SWAPN */ 1, /* 0x7C - OPC_GETARGN0 */ 1, /* 0x7D - OPC_GETARGN1 */ 1, /* 0x7E - OPC_GETARGN2 */ 1, /* 0x7F - OPC_GETARGN3 */ 2, /* 0x80 - OPC_GETLCL1 */ 3, /* 0x81 - OPC_GETLCL2 */ 2, /* 0x82 - OPC_GETARG1 */ 3, /* 0x83 - OPC_GETARG2 */ 1, /* 0x84 - OPC_PUSHSELF */ 5, /* 0x85 - OPC_GETDBLCL */ 5, /* 0x86 - OPC_GETDBARG */ 1, /* 0x87 - OPC_GETARGC */ 1, /* 0x88 - OPC_DUP */ 1, /* 0x89 - OPC_DISC */ 2, /* 0x8A - OPC_DISC1 */ 1, /* 0x8B - OPC_GETR0 */ 3, /* 0x8C - OPC_GETDBARGC */ 1, /* 0x8D - OPC_SWAP */ 2, /* 0x8E - OPC_PUSHCTXELE */ 1, /* 0x8F - DUP2 */ 0, /* 0x90 - OPC_SWITCH - variable-size instruction */ 3, /* 0x91 - OPC_JMP */ 3, /* 0x92 - OPC_JT */ 3, /* 0x93 - OPC_JF */ 3, /* 0x94 - OPC_JE */ 3, /* 0x95 - OPC_JNE */ 3, /* 0x96 - OPC_JGT */ 3, /* 0x97 - OPC_JGE */ 3, /* 0x98 - OPC_JLT */ 3, /* 0x99 - OPC_JLE */ 3, /* 0x9A - OPC_JST */ 3, /* 0x9B - OPC_JSF */ 3, /* 0x9C - OPC_LJSR */ 3, /* 0x9D - OPC_LRET */ 3, /* 0x9E - OPC_JNIL */ 3, /* 0x9F - OPC_JNOTNIL */ 3, /* 0xA0 - OPC_JR0T */ 3, /* 0xA1 - OPC_JR0F */ 5, /* 0xA2 - OPC_ITERNEXT */ 2, /* 0xA3 - GETSETLCL1R0 */ 2, /* 0xA4 - GETSETLCL1 */ 1, /* 0xA5 - DUPR0 */ 2, /* 0xA6 - GETSPN */ 1, /* 0xA7 - unused */ 1, /* 0xA8 - unused */ 1, /* 0xA9 - unused */ 1, /* 0xAA - GETLCLN0 */ 1, /* 0xAB - GETLCLN1 */ 1, /* 0xAC - GETLCLN2 */ 1, /* 0xAD - GETLCLN3 */ 1, /* 0xAE - GETLCLN4 */ 1, /* 0xAF - GETLCLN5 */ 5, /* 0xB0 - OPC_SAY */ 3, /* 0xB1 - OPC_BUILTIN_A */ 3, /* 0xB2 - OPC_BUILTIN_B */ 3, /* 0xB3 - OPC_BUILTIN_C */ 3, /* 0xB4 - OPC_BUILTIN_D */ 3, /* 0xB5 - OPC_BUILTIN1 */ 4, /* 0xB6 - OPC_BUILTIN2 */ 0, /* 0xB7 - OPC_CALLEXT (reserved; not currently implemented) */ 1, /* 0xB8 - OPC_THROW */ 1, /* 0xB9 - OPC_SAYVAL */ 1, /* 0xBA - OPC_INDEX */ 3, /* 0xBB - OPC_IDXLCL1INT8 */ 2, /* 0xBC - OPC_IDXLINT8 */ 1, /* 0xBD - unused */ 1, /* 0xBE - unused */ 1, /* 0xBF - unused */ 3, /* 0xC0 - OPC_NEW1 */ 5, /* 0xC1 - OPC_NEW2 */ 3, /* 0xC2 - OPC_TRNEW1 */ 5, /* 0xC3 - OPC_TRNEW2 */ 1, /* 0xC4 - unused */ 1, /* 0xC5 - unused */ 1, /* 0xC6 - unused */ 1, /* 0xC7 - unused */ 1, /* 0xC8 - unused */ 1, /* 0xC9 - unused */ 1, /* 0xCA - unused */ 1, /* 0xCB - unused */ 1, /* 0xCC - unused */ 1, /* 0xCD - unused */ 1, /* 0xCE - unused */ 1, /* 0xCF - unused */ 3, /* 0xD0 - OPC_INCLCL */ 3, /* 0xD1 - OPC_DECLCL */ 3, /* 0xD2 - OPC_ADDILCL1 */ 7, /* 0xD3 - OPC_ADDILCL4 */ 3, /* 0xD4 - OPC_ADDTOLCL */ 3, /* 0xD5 - OPC_SUBFROMLCL */ 2, /* 0xD6 - OPC_ZEROLCL1 */ 3, /* 0xD7 - OPC_ZEROLCL2 */ 2, /* 0xD8 - OPC_NILLCL1 */ 3, /* 0xD9 - OPC_NILLCL2 */ 2, /* 0xDA - OPC_ONELCL1 */ 3, /* 0xDB - OPC_ONELCL2 */ 1, /* 0xDC - unused */ 1, /* 0xDD - unused */ 1, /* 0xDE - unused */ 1, /* 0xDF - unused */ 2, /* 0xE0 - OPC_SETLCL1 */ 3, /* 0xE1 - OPC_SETLCL2 */ 2, /* 0xE2 - OPC_SETARG1 */ 3, /* 0xE3 - OPC_SETARG2 */ 1, /* 0xE4 - OPC_SETIND */ 3, /* 0xE5 - OPC_SETPROP */ 1, /* 0xE6 - OPC_PTRSETPROP */ 3, /* 0xE7 - OPC_SETPROPSELF */ 7, /* 0xE8 - OPC_OBJSETPROP */ 5, /* 0xE9 - OPC_SETDBLCL */ 5, /* 0xEA - OPC_SETDBARG */ 1, /* 0xEB - OPC_SETSELF */ 1, /* 0xEC - OPC_LOADCTX */ 1, /* 0xED - OPC_STORECTX */ 2, /* 0xEE - OPC_SETLCL1R0 */ 3, /* 0xEF - OPC_SETINDLCL1I8 */ 1, /* 0xF0 - unused */ 1, /* 0xF1 - OPC_BP */ 1, /* 0xF2 - OPC_NOP */ 1, /* 0xF3 - unused */ 1, /* 0xF4 - unused */ 1, /* 0xF5 - unused */ 1, /* 0xF6 - unused */ 1, /* 0xF7 - unused */ 1, /* 0xF8 - unused */ 1, /* 0xF9 - unused */ 1, /* 0xFA - unused */ 1, /* 0xFB - unused */ 1, /* 0xFC - unused */ 1, /* 0xFD - unused */ 1, /* 0xFE - unused */ 255 /* 0xFF - unused */ }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/osifcnet.h���������������������������������������������������������������������0000644�0001750�0000144�00000147427�11736301460�015525� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name osifcnet.h - TADS operating system interfaces for networking and threading Function Virtualizes OS APIs for networking and threading. The style of the interface defined here is different from the basic osifc.h function interfaces. Instead, the interfaces are defined via C++ classes. This is a more convenient way to represent the facilities in this package, because they all involve resources (network sockets, thread handles, event handles, etc) that have associated sets of operations - which maps very well onto objects and methods. We could have used C++'s abstract class mechanism to define a set of public interfaces here that are implemented by system .cpp files. However, that approach isn't very efficient, since it involves run-time virtual method dispatch that truly isn't necessary in this situation (since a given build will be for a single OS, so only that one OS's concrete classes can ever be invoked in that build). Furthermore, as we learned with HTML TADS, that's a somewhat confusing and high-overhead way to set things up for porting. So, instead, we define dummy skeletons for the classes, showing the public interfaces that must be implemented. These aren't the real definitions - we #ifdef them out so that they're not actually compiled. The real definitions must be provided by each OS implementation instead. Simply copy the skeleton from this file into your OS file, define the methods listed, and add any additional methods and/or variables that you need for your OS version. You can also add OS-specific base classes as needed. This approach gives you full flexibility in defining the concrete classes, while giving the portable code a predictable set of interfaces. Notes The network API defined here is for the TADS-in-a-browser setup. This includes both the client/server configuration, where the user interface is presented through a web browser on a client machine, but the TADS VM runs on a remote server, and the two talk across an internet connection via HTTP; and the stand-alone browser configuration, where both the browser and the TADS web server are running on the same machine. The latter is really a special case of the former: it's actually just a software bundle that launches the web server and the browser at once, and transparentl to the user, so that the game looks like a simple stand-alone interpreter app to the user. For traditional interpreter builds, the network API isn't used, so no implementation is required. Modified 04/04/10 MJRoberts - Creation */ #ifndef OSIFCNET_H #define OSIFCNET_H #include "vmglob.h" /* * Intialize the networking and threading package. This must be called * once at program startup. The OS isn't required to do anything here; * it's just a chance for the OS layer to do any required static/global * initialization. If the OS implementation uses the global thread list * (see TadsThreadList in vmnet.h), it should create the global list object * here. */ void os_net_init(class TadsNetConfig *config); /* * Clean up the networking and threading package. This must be called once * just before program termination. The OS isn't required to do anything * here; it's just a chance for the OS layer to clean up resources before * quitting. * * If the OS implementation uses the global thread list (see * CTadsThreadList in vmnet.h), it can use this opportunity to do a * wait_all() on the thread list to allow background threads to terminate * gracefully, then it should release its reference on the thread list * object. */ void os_net_cleanup(); /* * Get the local host name, if possible. This should return the EXTERNAL * network name of the local computer (i.e., it shouldn't return * "localhost", but rather the name that other machines on the network can * use to connect to this machine). Copies the host name to the buffer; * returns true if successful, false if not. */ int os_get_hostname(char *buf, size_t buflen); /* * Get the IP address for the given local host name, if possible. On * success, fills in 'buf' with the IP address in the standard decimal * format ("1.2.3.4") and returns true. On failure, returns false. * * 'host' is a host name for an adapter on the local machine. This * function resolves the external IP address for the host name, which is * the address that other machines on the network use to connect to this * host on the given adapter. Note that this should only be the external * address as far as this machine is concerned. For example, if the * machine is connected to a LAN, and the LAN contains a NAT router that's * connected to the public Internet, this routine does NOT go so far as to * resolve the external address outside the NAT router - it merely returns * the machine's local address on the LAN. * * If 'host' is null, this should return the default adapter's IP address, * if there is such a thing. On a machine with a single adapter, the * default is that single adapter; on a machine with multiple adapters, if * there's a local convention for designating which one is the default, use * the local convention, otherwise the implementation can choose the * default according to its own criteria, or simply pick one arbitrarily. */ int os_get_local_ip(char *buf, size_t buflen, const char *host); /* * Special timeout value for an infinite wait. When this is passed as a * timeout value to the wait() functions, the wait continues indefinitely, * until the desired event occurs. */ /* #define OS_FOREVER (define per OS) */ /* * Event wait results. */ #define OSWAIT_TIMEOUT -1 /* timeout expired before the event dired */ #define OSWAIT_ERROR -2 /* wait failed with a system error */ #define OSWAIT_EVENT 0 /* got the event we were waiting for */ /* for a multi-wait, OSWAIT_EVENT+n means the nth event fired */ #define OSWAIT_USER 1000 /* user-defined code can use this and above */ /* * Socket error code. This is the value returned by OS_Socket::send() and * recv() when an error occurs, in lieu of a valid length value. Define * per operating system. */ /* #define OS_SOCKET_ERROR (-1) */ /* * Error values for OS_Socket::last_error(). Define these per OS. * * OS_EWOULDBLOCK - the error code returned from accept(), send(), and * recv() when the socket is in non-blocking mode AND the call can't do any * work without blocking. This tells the caller that they should continue * with asynchronous processing for a while and poll again later. * * OS_ECONNRESET - returned from send(), recv(), and others when the * network peer (on the other side of the socket) has closed its end of the * connection. This means that further communications through the socket * are not possible, since the other side isn't paying attention any more. * * OS_ECONNABORTED - returned from send(), recv(), and others when the * local host has terminated the connection (i.e., some lower-level * software in the network stack - not the application - has decided on its * own to close the socket). This can happen in various situations, such * as when the peer fails to acknowledge a transmission from our side. * From the application's perspective, this is usually equivalent to * OS_ECONNRESET, since in either case we've lost the ability to * communicate with the client due to some kind of interruption outside of * the local machine's control. */ /* #define OS_EWOULDBLOCK EWOULDBLOCK */ /* #define OS_ECONNRESET ECONNRESET */ /* #define OS_ECONNABORTED ECONNABORTED */ /* * Protected counter. This object maintains an integer count that can be * incremented and decremented. The inc/dec operations are explicitly * atomic, meaning they're thread-safe: it's not necessary to use any * additional access serialization mechanism to perform an increment or * decrement. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Counter { public: /* initialize with a starting count value */ OS_Counter(long c = 1); /* get the current counter value */ long get() const; /* * Increment/decrement. These return the post-update result. * * The entire operation must be atomic. In machine language terms, the * fetch and set must be locked against concurrent access by other * threads or other CPUs. */ long inc(); long dec(); }; #endif /* ------------------------------------------------------------------------ */ /* * IMPORTANT: in your OS-specific header, #include "vmrefcnt.h" here, * immediately after defining class OS_Counter. * * vmrefcnt.h defines generic classes that are used as base classes by OS * interface classes below. */ /* ------------------------------------------------------------------------ */ /* * Waitable object. This is the base class for objects that can be used in * a multi-object wait. A waitable object has two states: ready and not * ready. Waiting for the object blocks the waiting thread as long as the * object is in the not-ready state, and releases the thread (allows it to * continue running) as soon as the object enters the rady state. Waiting * for an object that's already in the ready state simply allows the * calling thread to continue immediately without blocking. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Waitable { public: OS_Waitable(); virtual ~OS_Waitable(); /* * Wait for the object, with a maximum wait of 'timeout' milliseconds. * Blocks the calling thread until the object is in its ready state, OR * the timeout interval elapses, whichever comes first. At that point * the thread is released and this function returns. If the object is * already in the ready state when this is called, the method returns * immediately. * * If 'timeout' is OS_FOREVER, the wait is indefinite - it continues * until the event occurs, no matter how long that takes. * * Returns an OSWAIT_xxx code to indicate what happened. If the object * is already in the ready state, returns OSWAIT_EVENT immediately. A * timeout value of zero returns OSWAIT_TIMEOUT immediately (without * blocking) if the object isn't ready. */ int wait(unsigned long timeout = OS_FOREVER); /* * Test to see if the object is ready, without blocking. Returns true * if the object is ready, false if not. If the object is ready, * waiting for the object would immediately release the thread. * * Note that testing some types of objects "consumes" the ready state * and resets the object to not-ready. This is true of auto-reset * event objects. */ int test(); /* * Wait for multiple objects. This blocks until at least one object in * the list is in the ready state, at which point it returns * OSWAIT_OBJECT+N, where N is the array index in 'objs' of an object * that's ready. If the timeout (given in milliseconds) expires before * any objects are ready, we return OSWAIT_TIMEOUT. If the timeout is * omitted, we don't return until an object is ready. * * If multiple objects become ready at the same time, this picks one * and returns its index. * * If any of the objects are auto-reset event objects, the signaled * state of the *returned* object is affected the same way as it would * be for an ordinary wait(). The signaled states of any other objects * involved in the multi_wait are not affected. For example, if you * multi_wait for three auto-reset events, and all three start off in * the signaled state, multi_wait will arbitrarily choose one as the * returned event, and only that one chosen event will be reset by the * multi_wait. */ static int multi_wait(int cnt, OS_Waitable **objs, unsigned long timeout = OS_FOREVER); }; #endif /* * Event. This is a waitable object with two states: signaled and * unsignaled. In the unsignaled state, a thread waiting for the event * blocks until the event becomes signaled, at which point the thread is * released. In the signaled state, a thread waiting for the event simply * continues running without blocking. * * There are two types of event objects: auto-reset and manual-reset. * * An auto-reset event automatically reverts to the unsignaled state as * soon as ONE thread waiting for the event is released. This means that * if multiple threads are waiting for the event, signaling the event will * only release a single thread; the others will continue blocking until * the event is signaled again, at which point one more thread will be * released. An auto-reset event balances signals and waits: one signal * releases one waiting thread. If the event is signaled twice while no * threads are waiting, the next two waits will immediately be released, * and the third wait will block. * * A manual-reset event simply stays signaled once signaled, no matter how * many threads are waiting for it. The event reverts to unsignaled only * when someone manually calls reset(). */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Event: public OS_Waitable, public CVmRefCntObj { public: /* construct */ OS_Event(int manual_reset); /* * Signal the event. In the case of a manual-reset event, this * releases all threads waiting for the event, and leaves the event in * a signaled state until it's explicitly reset. For an auto-reset * event, this releases only one thread waiting for the event and then * automatically resets the event. */ void signal(); /* * Reset the event. For a manual-reset event, this returns the event * to the unsignaled state. This has no effect for an auto-reset * event. */ void reset(); }; #endif /* * Mutex - mutual exclusion object. This is used to protect shared * resources from concurrent access by multiple threads. * * You create a mutex to protect a designated set of shared resources; a * typical example is that you create a mutex for each instance of a class, * to protect the member variables of each instance. When a thread is * about to start a series of operation on the shared variables, and that * series of operations is required to be atomic, the thread first locks * the mutex associated with the variables (e.g., if you're about to make * some changes to an object's member variables, you lock the object's * mutex first). Only one thread can hold a mutex lock at a time, so once * you have the mutex lock, you know that any other thread attempting the * same operation would be blocked on its own lock attempt as long as * you're holding the lock, ensuring that no other thread will attempt to * modify the same variables. When you're done with the atomic operation, * you release the mutex to allow other threads to access the variables * again. * * The OS implementation for mutexes should be as lightweight as possible. * It's more efficient in terms of thread concurrency if callers can create * very granular mutexes, to protect specific resources. This means that * callers will want to create many mutexes in a typical system, so the * cost of creating lots of mutexes shouldn't outweigh the efficiency gain * of using lots of them. * * Care should be taken to avoid deadlocks when locking more than one mutex * at a time. The simplest technique for avoiding deadlocks is to use a * fixed order for acquiring any given set of mutexes. For example, if you * have two mutexes A and B, make sure all threads that acquire both will * always acquire A first, then B. This ensures that a thread that * successfully acquires A and then wants to acquire B will not deadlock * with another thread that already locked B and wants to acquire A. Since * the other thread will also always try to lock A before B, the first * thread can be assured that once it has A, no one else who wants both A * and B can already have B, since anyone else wanting both would * necessarily already have A, which the first thread knows can't be the * case because it acquired A. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Mutex: public CVmRefCntObj { public: OS_Mutex(); ~OS_Mutex(); /* * Lock the mutex. Blocks until the mutex is available. */ void lock(); /* * Test the mutex: if the mutex is available, returns true; if not, * returns false. Doesn't block in either case. If the return value * is true, the mutex is acquired as though lock() had been called, so * the caller must use release() when done. */ int test(); /* * Unlock the mutex. This can only be used after a successful lock() * or test(). This releases our lock and makes the mutex available to * other threads. */ void unlock(); }; #endif /* * "Core" socket. This is the common base class of data sockets and * listener sockets. * * In the standard Unix socket API, there's really just one kind of socket * object, and its usage as a data or listener socket depends on the way * it's set up. In OO terms, though, the data and listener sockets are * really separate subclasses, with a small part of their interfaces in * common. We've extracted the common parts into this base class. * * Our socket classes in aggregate are just a thin layer implementing the * same model as the Unix socket API. Most modern operating systems have * socket libraries modeled closely on the Unix socket API, so this should * be easily implementable on most systems. We can't use the Unix socket * API directly, because other systems do tend to have idiosyncracies, * despite their overall similarity to the Unix API. Windows is a * particularly good (?) example: a lot of the basic structure of the API * is the same as in Unix, but with a bunch of gratuitous name changes; and * some of the more advanced features, such as non-blocking mode, are * handled rather differently because of deeper differences in the OS. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_CoreSocket: public CVmRefCntObj, public OS_Waitable { public: OS_CoreSocket(); /* deleting the object closes the underlying system socket */ ~OS_CoreSocket() { close(); } /* * Close the socket. This releases any system resources associated * with the socket, including any network ports it's using. */ void close(); /* * Set the socket to non-blocking mode. * * In non-blocking mode, any network operation on the socket will * return immediately with OS_SOCKET_ERROR if the operation can't be * completed immediately, and thus would block pending the arrival of * data or completion of a transmission. Specifically: * * - reading a data socket (recv(), in the Unix API) would block if * there are zero bytes available to be read in the inbound buffer * * - writing a data socket (send() in Unix) would block if the outbound * buffer is too full to accommodate all of the bytes being sent * * - listening for a new incoming connection request (accept() in Unix) * would block if there are no pending connection requests * * If one of these functions returns OS_SOCKET_ERROR, the caller should * check the specific error code by calling last_error(). This will * return OS_EWOULDBLOCK when the cause of the error is one of the * above conditions that would normally cause the call to block. * * After an OS_EWOULDBLOCK occurs, the caller can do one of two things. * First, it can simply carry on with other operations; since the * thread isn't blocked waiting for the network operation to complete, * it can do whatever else it wants in the meantime. The thread can * "poll" the socket by attempting the operation again from time to * time to see if it's finally ready. Second, if the thread doesn't * have any other business to transact, it can wait for the socket to * become ready for the desired operation using the socket's * OS_Waitable interface. That blocks the thread the same way that the * same network operation functions would in the default blocking mode, * but provides two extra capabilities that the regular network * functions don't: you can set a timeout so that the thread will only * block for a certain maximum interval, and you can use the multi-wait * interface so that other events of interest will also wake up the * thread if they should occur before the socket is ready. * * Note that the default mode when you create a new socket is blocking * mode. In blocking mode, any of the above blocking conditions do not * cause an error: instead, they simply block the thread until the * network operation completes. */ void set_non_blocking(); /* * In non-blocking mode, the caller must manually reset the socket's * event waiting status after each successful wait. */ void reset_event(); /* * Get the last error for a network operation (for a data socket, send * and receive; for a listener socket, accept). Returns an OS_Exxx * value. */ int last_error() const; /* * Get my port number. For a listener socket, this is the port on * which we're listening; for a regular data socket, this is the port * number on our side (the local side) of the connection. Returns 0 if * the port hasn't been opened yet. */ unsigned short get_port_num(); /* * Get this socket's local IP address. This returns the IP address of * the network adapter on our side (the local side) of the connection. * For a listening socket, this is the IP address that clients use to * initiate a connection to this server. * * The returned string is allocated. The caller must free the string * when done with it, using lib_free_str(). */ char *get_ip_addr() const { return lib_copy_str(local_ip); } /* * Get the IP address for the given host. The return value is an * allocated buffer that the caller must delete with 'delete[]'. If * the host IP address is unavailable, returns null. * * A null or empty hostname gets the default external IP address for * the local machine. This does NOT return "localhost" or "127.0.0.1", * but rather the external IP that other machines on the same local * area network can use to connect to this machine. Note that this * address is valid on our LAN, but not necessarily on the broader * Internet, since we could be behind a NAT firewall that assigns local * IP addresses within the LAN. */ static char *get_host_ip(const char *hostname); }; #endif /* * Data socket. This is a subclass of the core socket that's used for * basic data transmission. A data socket is a full-duplex communication * channel between this process and another process, which might be running * on the same host or on a remote host connected via a network. * * On the server side, sockets are created by OS_Listener::accept(). * * Currently, there is no interface for creating a client-side socket. * Instead, we provide a request-level API for initiating HTTP requests * from the client side (see class OS_HttpRequest). */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Socket: public OS_CoreSocket { public: /* * Construction. Note that callers do not generally create socket * objects directly; instead, these objects are created by other * osifcnet system classes and returned to user code to access. For * example, server sockets are created by OS_Listener::accept(). */ OS_Socket(); /* * Send bytes. Returns the number of bytes sent, or OS_SOCKET_ERROR if * an error occurs. * * When the socket is in non-blocking mode, and the buffer is too full * to accept the complete write, this writes nothing and returns * immediately with an error; last_error() will return OS_EWOULDBLOCK. */ int send(const char *buf, size_t len); /* * Receive bytes. Returns the number of bytes received. If the return * value is 0, it means that the peer has closed its end of the socket * and all buffered data have already been received. If the return * value is negative, it indicates an error; the specific error code * can be obtained from last_error(). * * This doesn't necessarily fulfill the complete read request. If * there are fewer bytes immediately available than 'len', this reads * all of the bytes immediately available and returns, indicating the * number of bytes actually transfered. This is the case regardless of * the blocking mode of the socket. * * When the socket is in non-blocking mode, and there are no bytes * available, this returns immediately with an error, and last_error() * will return OS_EWOULDBLOCK. */ int recv(char *buf, size_t len); /* * Get the IP address and port for our side of the connection. 'ip' * receives an allocated string with the IP address string in decimal * notation ("192.168.115"). We fill in 'port' with our port number. * Returns true on success, false on failure. If we return success, * the caller is responsible for freeing the 'ip' string with * lib_free_str(). */ int get_local_addr(char *&ip, int &port); /* * Get the IP address and port for the network peer to which we're * connected. 'ip' receives an allocated string with the IP address * string in decimal notation ("192.168.1.15"). We fill in 'port' with * the port number on the remote host. Returns true on success, false * on failure. If we return success, the caller is responsible for * freeing the allocated 'ip' string via lib_free_str(). */ int get_peer_addr(char *&ip, int &port); }; #endif /* * Listener. This class represents a network listener socket, which is * used to wait for and accept new incoming connections from clients. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Listener: public OS_CoreSocket { public: /* * Construction. This sets up the object, but doesn't open a listener * port. Call open() to actually open the port. */ OS_Listener(); /* * Open the listener on the given port number. This can be used to * create a server on a well-known port, but it has the drawback that * ports can't be shared, so this will fail if another process is * already using the same port number. * * Returns true on success, false on failure. */ int open(const char *hostname, unsigned short port_num); /* * Open the listener port, with the port number assigned by the system. * The system will select an available port and assign it to this * listener, so this avoids contention for specific port numbers. * However, it requires some separate means of communicating the port * number to clients, since there's no way for them to know the port * number in advance. * * Returns true on success, false on failure. */ int open(const char *hostname); /* * Accept the next incoming connection. If the listener is in blocking * mode (the default), and there are no pending requests, this blocks * until the next pending request arrives. In non-blocking mode, this * returns immediately with a null socket if no requests are pending, * and last_error() indicates OS_EWOULDBLOCK. */ OS_Socket *accept(); }; #endif /* * Thread. * * To define the code to execute for a thread, you subclass this class and * define a concrete implementation for thread_main(). The thread * automatically terminates when thread_main() returns. * * When used as a Waitable object, a thread is in in the "ready" state when * the thread's thread_main() has returned. In other words, waiting for a * thread means that you're waiting for the thread to complete. */ #if 0 /* skeleton definition - make a copy for the actual OS definition */ class OS_Thread: public CVmWeakRefable, public OS_Waitable { /* the TadsThreadList class must be a friend, if we use it */ friend class TadsThreadList; public: /* * Construction. This doesn't actually launch the thread; that can't * happen in the base class constructor because the thread entrypoint * might be reached before the construction is completed. Instead, * call launch() to start the system-level thread running. * * If the OS uses the TadsThreadList master list of threads, each new * thread should be registered here through the ThreadList global. * This is optional; it's for the OS layer's benefit, so if the OS * layer doesn't need it, you can omit it. */ OS_Thread() { TadsThreadList::add(this); } /* * Destruction. * * If the OS uses the ThreadList master list of threads, you should * clean up the master thread list here. The thread list keeps a weak * reference to the thread, and the weak reference is automatically * cleared when we're deleted, so the thread list simply needs to scan * for dead weak refs and clean them up. */ ~OS_Thread() { TadsThreadList::clean(); } /* * Launch the system thread, and set it up to start running in * thread_main(). Returns true on success, false if the thread * couldn't be started. * * When this returns, the thread may or may not have actually started * executing thread_main(). On most operating systems it's up to the * OS scheduler to decide when to give CPU time to the thread, so the * thread's execution status depends on the OS and on current CPU * conditions in the system. If the caller needs to know that the * thread has started executing, or that the thread has reached some * particular point in its execution, it can easily coordinate this by * creating an event object that it shares with the thread: the thread * signals the event when the desired milestone has been reached, and * the caller waits for the event after creating the thread, ensuring * that the caller won't proceed until the thread has complete the * desired startup steps. */ int launch(); /* * Thread entrypoint routine. Callers must subclass this class and * provide a concrete definition for this method. This method is * called at thread entry. */ virtual void thread_main() = 0; }; #endif /* * HTTP client operations */ class OS_HttpClient { public: /* * Perform an HTTP request on a given resource, returning the resource * contents. * * 'opts' is a combination of OptXxx option flags (see below). * * 'verb' is the HTTP verb to perform; this is usually GET, HEAD, POST, * or PUT. 'resource' is the full URL to the resource to be retrieved. * 'portno' is the port number (usually 80). 'ua' is an optional User * Agent string; if this is null, we'll use a default UA string * identifying ourselves as the generic TADS HTTP client. * * 'send_headers' is an optional string of custom headers to send with * the request. The headers are appended to the headers that we * automatically generate. The string must be in the standard "Name: * Value" format, with headers separated by CR-LF sequences. The * overall string can end wtih one or more CR-LF sequences but isn't * required to. Pass null if no custom headers are to be sent. * 'send_headers_len' is the length in bytes of this string. * * If 'payload' is non-null, it contains the payload data to send with * the request. This is for use with POST and PUT to specify the data * to send with the request. For a POST, the payload can be a mix of * form fields and file items. If there are any file items, we'll send * the POST data in the multipart/form-data format; otherwise we'll * send as application/x-www-form-urlencoded. For any verb other than * POST, the payload (if present at all) is limited to a single file * item. * * Special exception: for POST, if the payload consists of a single * payload item with an empty string ("") as the name, we'll assume * that the caller did all of the necessary POST encoding already, and * we'll send that payload item as the entire content body of the * request WITHOUT any further encoding. This allows the caller to * hand-code an x-www-form-urlencoded or multipart/form-data body, or * to use an entirely different encoding for the body. In other words, * we treat a POST payload with a single unnamed payload item exactly * as we treat a payload for PUT or any other non-POST verb. * * Returns the HTTP status code from the server, or an ErrXxx code * defined below if an error occurred on the client side. The ErrXxx * codes are all negative, which distinguishes them from HTTP status * codes. * * If the server sends back a reply body, we'll store the contents of * the reply in the 'reply' object. If provided, this must be a * writable stream object. 'reply' can alternatively be null, in which * case we'll simply discard any reply body. If the request doesn't * return any reply body, but 'reply' is non-null, we'll simply leave * 'reply' unchanged. Note that we don't rewind or truncate the * 'reply' stream object, so the caller has to prepare the object in * the desired initial conditions before calling this method. * * 'headers' is optional. If this is non-null, we'll allocate a buffer * for the headers and store the pointer to the buffer in '*headers'. * This buffer will contain the raw CR-LF delimited header lines from * the reply, with a null terminator. The caller must delete this * memory with delete[]. The header buffer will be returned only if * the request makes it far enough to retrieve headers; if the call * fails at the socket connection level, for example, no headers are * returned. * * 'location' is optional. If this is non-null, AND the request result * is a 301 code ("resource moved"), we'll allocate a copy of the * redirected URL from the HTTP reply and hand it back via *location. * The caller is responsible for freeing this buffer when done with it * via lib_free_str(). * * If 'location' is null, and the resource has moved, we'll * automatically follow the redirection link and retrieve the resource * at the redirected location. Any redirection is thus transparent to * the caller when 'location' is null. */ static int request(int opts, const char *host, unsigned short portno, const char *verb, const char *resource, const char *send_headers, size_t send_headers_len, class OS_HttpPayload *payload, class CVmDataSource *reply, char **reply_headers, char **location, const char *ua); /* option flags for request() */ static const int OptHTTPS = 0x0001; /* use https scheme */ /* non-HTTP status codes for request() */ static const int ErrNoMem = -1; /* out of memory */ static const int ErrNoConn = -2; /* couldn't connect to host */ static const int ErrNet = -3; /* other network/socket error */ static const int ErrParams = -4; /* invalid parameters */ static const int ErrReadFile = -5; /* can't read payload file contents */ static const int ErrWriteFile = -6; /* error writing reply stream */ static const int ErrGetHdrs = -7; /* error retrieving reply headers */ static const int ErrThread = -8; /* error starting thread */ static const int ErrOther = -100; /* other error */ }; /* HTTP request payload item */ class OS_HttpPayloadItem { public: /* create as a name/value pair for a form field for a POST */ OS_HttpPayloadItem(const char *name, const char *val); OS_HttpPayloadItem(const char *name, size_t name_len, const char *val, size_t val_len); /* * Create as a file upload for PUT or POST. 'filename' is the nominal * name for the upload - it doesn't have to correspond to an actual * file system object. The actual payload contents will be taken from * the stream, NOT from a disk file. * * Since 'filename' is only used on the server side to identify the * upload, it's normally a root name only, stripped of any path prefix. * A local path on the client won't mean anything to the server, so * there's no value in sending it, and some people consider doing so a * security risk in that it unnecessarily exposes information about the * local system's directory structure across the network. */ OS_HttpPayloadItem(const char *name, const char *filename, const char *mime_type, class CVmDataSource *contents); OS_HttpPayloadItem(const char *name, size_t name_len, const char *filename, size_t filename_len, const char *mime_type, size_t mime_type_len, class CVmDataSource *contents); /* delete */ ~OS_HttpPayloadItem(); void init(); /* field name, for a POST form field or POST form file upload */ char *name; /* * Field value, for a simple POST form field; for a file, this is the * nominal filename to send to the server. */ char *val; /* data stream for a PUT file or a POST form file upload */ class CVmDataSource *stream; /* MIME type, for a PUT file or a POST form file upload */ char *mime_type; /* next item in the list */ OS_HttpPayloadItem *nxt; }; /* * HTTP request payload data. Use this to package the uploaded data for an * HTTP PUT or POST via OS_HttpClient::request(). */ class OS_HttpPayload { public: OS_HttpPayload(); ~OS_HttpPayload(); /* add a simple name/value form field */ void add(const char *name, const char *val); /* add a simple name/value field, in tads-string (VMB_LEN prefix) format */ void add_tstr(const char *name, const char *val); /* add a simple file upload */ void add(const char *name, const char *filename, const char *mime_type, class CVmDataSource *contents); void add(const char *name, size_t name_len, const char *filename, size_t filename_len, const char *mime_type, size_t mime_type_len, class CVmDataSource *contents); /* add an item */ void add(OS_HttpPayloadItem *item); /* get the number of payload items */ int count_items() const; /* * Is this a multipart payload? This returns true if any of the fields * contain file uploads, false if they're all simple form fields. A * POST consisting entirely of simple form fields is normally sent with * MIME type application/x-www-form-urlencoded, whereas a POST * containing uploaded file data requires multipart/formdata. */ int is_multipart() const; /* * Create an application/x-www-form-urlencoded buffer from the payload. * This is only applicable when there are no file upload fields. The * returned string is an allocated buffer that the caller is * responsible for freeing when done with it, via t3free(). */ char *urlencode(size_t &len) const; /* get the Nth item */ OS_HttpPayloadItem *get(int n) const; protected: /* list of payload items */ OS_HttpPayloadItem *first_item, *last_item; }; /* ------------------------------------------------------------------------ */ /* * Forward declarations */ class TadsThreadList; /* ------------------------------------------------------------------------ */ /* * Include the appropriate system-specific header */ #if defined(_WIN32) #include "osnetwin.h" #elif defined(UNIX) || defined(FROBTADS) #include "osnetunix.h" #else #include "osnetdum.h" #endif /* ------------------------------------------------------------------------ */ /* * Connect to the Web UI. The game calls this after it starts its HTTP * server through which it will present its Web UI. This function's job is * to get the client connected to the new HTTP server - in other words, to * navigate the client's browser to the game's HTTP server and load the * game's start page. * * 'message_queue' is an optional message queue to receive notifications * about events in the UI after it's connected. If this is null, no * notifications are sent. If provided, the UI will send the following * event messages to the queue as appropriate: * * - For a stand-alone local configuration (where the browser UI is an * integrated part of the interpreter application - see below), a * TadsUICloseEvent is posted when the user manually closes the UI window. * For the local configuration, the game will usually want to consider this * to be an "end of file" on the input, causing the game to terminate. * * 'addr' is the network address of the server (as a decimal IP string, * e.g. "192.168.1.25", or as a DNS host name), and 'port' is the port * number on which the server is listening. The address will be sent to * the client, and the client will use it to connect to the game, so the * address must be in a form that the client can use to reach our server. * For a client within the same LAN, the local IP address will work. This * won't work in cases where we're behind a NAT firewall and the client is * on the other side of the firewall, though: in those cases, you have to * be sure to use the external Internet IP address. In most cases this * isn't something the game will have to worry about, since the game * generally just takes its listener binding address from the Web server * that launched it - so assuming the Web server is configured correctly, * the game will usually have the right address. * * 'path' is the path to the starting UI page; this is an absolute path on * the server, such as "/webui.htm". 'errmsg' is a pointer to a pointer to * be filled in with an allocated error message string if the operation * fails. 'errmsg' must be set to a string allocated with lib_alloc_str() * or lib_copy_str(); the caller is responsible for freeing this string * with lib_free_str() when done with it. The function must set '*errmsg' * to null if not used. Return true on success, false on failure. * * This function could conceivably be implemented any number of ways, but * we foresee two main implementation strategies, for our two main expected * configurations. * * CONFIGURATION 1: STAND-ALONE PLAY * * Stand-alone play is where the whole game, client and server, runs on a * single PC and looks to the user like a single application. The fact * that there's a network server running is transparent to the user; they * see what looks like the traditional command-line or HTML TADS setup, * where there's just a "t3run <game>" command that opens a game window. * * In this configuration, connecting to the Web UI is easy. Since both * client and server are running under one roof, we have control over the * client UI. We simply need to open a Web browser window and navigate it * to the game server's start page. * * Opening the browser window could consist of actually launching a * separate Web browser application and passing in the starting URL. This * is the simplest approach. For a more integrated appearance, the * application could instead launch its own custom frame window with an * embedded HTML browser widget filling the interior of the window (the * "client area" in Windows parlance). In either case, the job is the * same: launch a browser and point it to the game's HTTP server. * * CONFIGURATION 2: CLIENT/SERVER WEB PLAY * * Starting a Web-based TADS game presents us with an interesting problem. * The client initiates the game by connecting to a Web server where the * game is hosted, and navigating to a "launch" page. The launch page is * actually a server-side program that starts the TADS 3 Interpreter in a * child process, passing the .t3 file name to the interpreter. The * interpreter loads the game and starts executing it. The game then sets * up an HTTP server which it will use to present its Web UI. * * That's where our "interesting problem" comes up. We have a client who * knows how to talk to the "launch" page, and separately we have the * game's HTTP server on an ad hoc port assigned by the operating system. * (For details on why this is an ad hoc port rather than a well-known * port, see the discussion of Network Service Registration below.) The * problem is that we now need to connect the client to the server. * * The way we solve this is to use that "launch" page as the conduit. When * the launch page loads, it doesn't immediately send anything back to the * client. Instead, it stays momentarily silent while it launches the TADS * Interpreter and lets the game start up. When the launch page starts the * interpreter, it establishes an interprocess communication channel of * some kind between itself and the interpreter - for example, a Unix pipe * would work nicely. When the game calls vmnet_connect_webui(), we use * this IPC to send our IP address and port number information back to the * launch page. When the launch page receives this information, it finally * replies to the client. But rather than sending back just any old HTML, * it sends back a REDIRECT reply, pointing the client to the game server * start page. When the client receives the redirect, it turns around and * loads the start page from the game server. * * --- * * The implementation of this function is specific to both the * CONFIGURATION and the SYSTEM. For example, there might be two separate * Unix implementations: one for stand-alone play that opens an integrated * frame window containing an embedded browser widget; and the other for * Web server use that writes the information to stdout for the "launch" * page script to read. * * --- * * This routine should reuse the same UI display resources if repeated * calls are made. For example, in the stand-alone configuration, if we've * already opened a local browser window in a previous call, and that * window is still open, we should simply navigate that existing window to * the new location rather than opening a second window. * * The caller should invoke osnet_disconnect_webui() before terminating the * program. However, calls to osnet_connect_webui() are NOT required to be * matched by an equal number of calls to the disconnect function. It's * sufficient to call disconnect once after calling connect several times. */ int osnet_connect_webui(VMG_ const char *addr, int port, const char *path, char **errmsg); /* * Disconnect from the Web UI. This is used at program termination to * notify the Web UI that the server program is no longer running, to allow * it to update or remove its UI, and to release system resources. * * If there's no Web UI currently active, this routine should simply return * without doing anything. It must be harmless to call this routine with * no previous calls to osnet_connect_webui(). * * For a stand-alone configuration, this should disconnect from the Web * UI's window. If the Web UI is running as a separate process, this * should clean up any IPC channel between the processes, and should update * the UI to indicate that the game has ended. For example, the UI window * could show a message to this effect in its title bar or status line, or * could add a message to the text display. * * For a client/server web-play configuration, this usually doesn't do * anything on the UI side, since the UI is a separate browser (on a * separate machine) that the user launched and which we're not responsible * for closing. However, this routine can still take the opportunity to * free up any local resources allocated is osnet_connect_webui(). * * If 'close' is true, the web UI should be visually removed. If it's * running in a separate process, the process can be terminated. If * 'close' is false, a separate process can be left running for the user to * close manually. Explicitly closing the UI is appropriate for debuggers * and other "container" environments where it's reasonable to expect that * closing the container environment would close the UI window as well. * Leaving the UI open is appropriate for a stand-alone intepreter in a GUI * environment; even though the game engine has terminated, we usually want * to leave the final state of the UI displayed until the user manually * closes it. This gives the user a chance to read any final messages the * game displays before terminating. */ void osnet_disconnect_webui(int close); /* * Run the file selector dialog in the *local standalone mode* Web UI. * * This is a substitute for the regular os_askfile() routine that the * interpreter calls when we're running in the local standalone mode. This * mode is a hybrid of the Web UI and the local interpreter console UI: it * uses a web browser as the UI, but it uses the local file system for * storage. The file selection dialog is at the intersection of these two * axes: the file dialog is a UI element, and our primary UI is the * browser, but in this configuration the dialog accesses the local file * system and should present the same UI as it would for the a local * application. That's why we need a custom function to handle this. * * Generally, the implementation should invoke a standard local file * selector dialog, but show it within the UI context of the Web UI window. * In the Windows implementation, for example, we run the Web UI in a child * process that's a customized browser window, so we handle this function * by sending a message to the child UI process asking it to display a file * dialog on our behalf. The child process then sends us back a reply * containing the result of the dialog. * * This can be stubbed out with an empty implementation on platforms that * don't have a standalone Web UI mode. */ int osnet_askfile(const char *prompt, char *fname_buf, int fname_buf_len, int prompt_type, int file_type); #endif /* OSIFCNET_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/sha2.h�������������������������������������������������������������������������0000644�0001750�0000144�00000011727�11461022351�014533� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* --------------------------------------------------------------------------- Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK. All rights reserved. LICENSE TERMS The free distribution and use of this software in both source and binary form is allowed (with or without changes) provided that: 1. distributions of this source code include the above copyright notice, this list of conditions and the following disclaimer; 2. distributions in binary form include the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other associated materials; 3. the copyright holder's name is not used to endorse products built using this software without specific written permission. ALTERNATIVELY, provided that this notice is retained in full, this product may be distributed under the terms of the GNU General Public License (GPL), in which case the provisions of the GPL apply INSTEAD OF those given above. DISCLAIMER This software is provided 'as is' with no explicit or implied warranties in respect of its properties, including, but not limited to, correctness and/or fitness for purpose. --------------------------------------------------------------------------- Issue Date: 30/11/2002 */ #ifndef _SHA2_H #define _SHA2_H #include <limits.h> /* Defines for suffixes to 32 and 64 bit unsigned numeric values */ #define sfx_lo(x,y) x##y #define sfx_hi(x,y) sfx_lo(x,y) #define n_u32(p) sfx_hi(0x##p,s_u32) #define n_u64(p) sfx_hi(0x##p,s_u64) /* define an unsigned 32-bit type */ #if UINT_MAX == 0xffffffff typedef unsigned int sha2_32t; #define s_u32 u #elif ULONG_MAX == 0xffffffff typedef unsigned long sha2_32t; #define s_u32 ul #else #error Please define sha2_32t as an unsigned 32 bit type in sha2.h #endif /* define an unsigned 64-bit type */ #if defined( _MSC_VER ) typedef unsigned __int64 sha2_64t; #define s_u64 ui64 #elif ULONG_MAX == 0xffffffffffffffff typedef unsigned long sha2_64t; #define s_u64 ul #elif ULONG_MAX == 0xffffffff typedef unsigned long long sha2_64t; /* a somewhat dangerous guess */ #define s_u64 ull #else #error Please define sha2_64t as an unsigned 64 bit type in sha2.h #endif #if defined(__cplusplus) extern "C" { #endif #define SHA256_DIGEST_SIZE 32 #define SHA384_DIGEST_SIZE 48 #define SHA512_DIGEST_SIZE 64 #define SHA256_BLOCK_SIZE 64 #define SHA384_BLOCK_SIZE 128 #define SHA512_BLOCK_SIZE 128 #define SHA2_DIGEST_SIZE SHA256_DIGEST_SIZE #define SHA2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE #define SHA2_GOOD 0 #define SHA2_BAD 1 /* type to hold the SHA256 context */ typedef struct { sha2_32t count[2]; sha2_32t hash[8]; sha2_32t wbuf[16]; } sha256_ctx; /* type to hold the SHA384/512 context */ typedef struct { sha2_64t count[2]; sha2_64t hash[8]; sha2_64t wbuf[16]; } sha512_ctx; typedef sha512_ctx sha384_ctx; /* type to hold a SHA2 context (256/384/512) */ typedef struct { union { sha256_ctx ctx256[1]; sha512_ctx ctx512[1]; } uu[1]; sha2_32t sha2_len; } sha2_ctx; void sha256_compile(sha256_ctx ctx[1]); void sha512_compile(sha512_ctx ctx[1]); void sha256_begin(sha256_ctx ctx[1]); void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]); void sha256_end(unsigned char hval[], sha256_ctx ctx[1]); void sha256(unsigned char hval[], const unsigned char data[], unsigned long len); /* * Generate a printable version of a hash for a given buffer. 'hash' is an * array of at least 65 characters to receive the hash string. It's fine * to pass in the same buffer for both 'hash' and 'data', as long as it's * big enough (>=65 characters). [mjr] */ void sha256_ez(char *hash, const char *data, size_t data_len); /* * Generate a printable version of a hash for a given data source. [mjr] */ void sha256_datasrc(char *hash, class CVmDataSource *src, unsigned long len); /* * printf-style hash construction: format the string given by 'fmt' and the * subsequent arguments, and hash the result */ void sha256_ezf(char *hash, const char *fmt, ...); void sha384_begin(sha384_ctx ctx[1]); #define sha384_hash sha512_hash void sha384_end(unsigned char hval[], sha384_ctx ctx[1]); void sha384(unsigned char hval[], const unsigned char data[], unsigned long len); void sha512_begin(sha512_ctx ctx[1]); void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]); void sha512_end(unsigned char hval[], sha512_ctx ctx[1]); void sha512(unsigned char hval[], const unsigned char data[], unsigned long len); int sha2_begin(unsigned long size, sha2_ctx ctx[1]); void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]); void sha2_end(unsigned char hval[], sha2_ctx ctx[1]); int sha2(unsigned char hval[], unsigned long size, const unsigned char data[], unsigned long len); #if defined(__cplusplus) } #endif #endif �����������������������������������������frobtads-1.2.3/tads3/vmhash.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000037405�10371422240�015520� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmhash.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhash.cpp - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #include <assert.h> #include <memory.h> #include <string.h> #ifndef STD_H #include "t3std.h" #endif #ifndef VMHASH_H #include "vmhash.h" #endif /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function implementation. */ unsigned int CVmHashFuncCI::compute_hash(const char *s, size_t l) const { uint acc; /* * Add up all the character values in the string, converting all * characters to upper-case. */ for (acc = 0 ; l != 0 ; ++s, --l) { uchar c; c = (uchar)(is_lower(*s) ? to_upper(*s) : *s); acc += c; } /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ unsigned int CVmHashFuncCS::compute_hash(const char *s, size_t l) const { uint acc; /* * add up all the character values in the string, treating case as * significant */ for (acc = 0 ; l != 0 ; ++s, --l) { uchar c; c = (uchar)*s; acc += c; } /* return the accumulated value */ return acc; } /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ CVmHashEntry::CVmHashEntry(const char *str, size_t len, int copy) { /* not linked into a list yet */ nxt_ = 0; /* see if we can use the original string or need to make a private copy */ if (copy) { char *buf; /* allocate space for a copy */ buf = new char[len]; /* copy it into our buffer */ memcpy(buf, str, len * sizeof(*buf)); /* remember it */ str_ = buf; } else { /* we can use the original */ str_ = str; } /* remember the length */ len_ = len; /* remember whether or not we own the string */ copy_ = copy; } CVmHashEntry::~CVmHashEntry() { /* if we made a private copy of the string, we own it, so delete it */ if (copy_) delete [] (char *)str_; } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-insensitive * symbol match implementation */ int CVmHashEntryCI::matches(const char *str, size_t len) const { /* * it's a match if the strings are the same length and all * characters match, ignoring case */ return (len == len_ && memicmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-sensitive symbol * match implementation */ int CVmHashEntryCS::matches(const char *str, size_t len) const { /* * it's a match if the strings are the same length and all * characters match, treating case as significant */ return (len == len_ && memcmp(str, str_, len * sizeof(*str)) == 0); } /* ------------------------------------------------------------------------ */ /* * Hash table */ void CVmHashTable::init(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array) { CVmHashEntry **entry; size_t i; /* make sure it's a power of two */ assert(is_power_of_two(hash_table_size)); /* make sure we got a hash function */ assert(hash_function != 0); /* save the hash function */ hash_function_ = hash_function; own_hash_func_ = own_hash_func; /* check to see if the caller provided the hash array */ if (hash_array != 0) { /* the caller allocated the table - store a reference to it */ table_ = hash_array; table_size_ = hash_table_size; /* note that the table belongs to the caller, so we don't delete it */ own_hash_table_ = FALSE; } else { /* allocate the table */ table_ = new CVmHashEntry *[hash_table_size]; table_size_ = hash_table_size; /* note that we own the table and must delete it */ own_hash_table_ = TRUE; } /* clear the table */ for (entry = table_, i = 0 ; i < table_size_ ; ++i, ++entry) *entry = 0; } CVmHashTable::~CVmHashTable() { /* delete the hash function object if I own it */ if (own_hash_func_) delete hash_function_; /* delete each entry in the hash table */ delete_all_entries(); /* delete the hash table */ if (own_hash_table_) delete [] table_; } /* * delete all entries in the hash table, but keep the table itself */ void CVmHashTable::delete_all_entries() { CVmHashEntry **tableptr; size_t i; for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* delete each entry in the list at this element */ for (entry = *tableptr ; entry ; entry = nxt) { /* remember the next entry */ nxt = entry->nxt_; /* delete this entry */ delete entry; } /* there's nothing at this table entry now */ *tableptr = 0; } } /* * Verify that a value is a power of two. Hash table sizes must be * powers of two. */ int CVmHashTable::is_power_of_two(int n) { /* divide by two until we have an odd number */ while ((n & 1) == 0) n >>= 1; /* make sure the result is 1 */ return (n == 1); } /* * Compute the hash value for an entry */ unsigned int CVmHashTable::compute_hash(CVmHashEntry *entry) const { return compute_hash(entry->getstr(), entry->getlen()); } /* * Compute the hash value for a string */ unsigned int CVmHashTable::compute_hash(const char *str, size_t len) const { return adjust_hash(hash_function_->compute_hash(str, len)); } /* * Add an object to the table */ void CVmHashTable::add(CVmHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* link it into the slot for this hash value */ entry->nxt_ = table_[hash]; table_[hash] = entry; } /* * Remove an object */ void CVmHashTable::remove(CVmHashEntry *entry) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(entry); /* * if it's the first item in the chain, advance the head over it; * otherwise, we'll need to find the previous item to unlink it */ if (table_[hash] == entry) { /* it's the first item - simply advance the head to the next item */ table_[hash] = entry->nxt_; } else { CVmHashEntry *prv; /* find the previous item in the list for this hash value */ for (prv = table_[hash] ; prv != 0 && prv->nxt_ != entry ; prv = prv->nxt_) ; /* if we found it, unlink this item */ if (prv != 0) prv->nxt_ = entry->nxt_; } } /* * Find an object in the table matching a given string. */ CVmHashEntry *CVmHashTable::find(const char *str, size_t len) const { unsigned int hash; CVmHashEntry *entry; /* compute the hash value for this entry */ hash = compute_hash(str, len); /* scan the list at this hash value looking for a match */ for (entry = table_[hash] ; entry ; entry = entry->nxt_) { /* if this one matches, return it */ if (entry->matches(str, len)) return entry; } /* didn't find anything */ return 0; } /* * Enumerate hash matches for a given string */ void CVmHashTable::enum_hash_matches(const char *str, size_t len, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx) { unsigned int hash; /* compute the hash value for this entry */ hash = compute_hash(str, len); /* enumerate matches at the hash value */ enum_hash_matches(hash, cb, cbctx); } /* * Enumerate hash matches for a given hash code */ void CVmHashTable::enum_hash_matches(unsigned int hash, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx) { CVmHashEntry *entry; /* adjust the hash value for the table size */ hash = adjust_hash(hash); /* enumerate the complete list of entries at this hash value */ for (entry = table_[hash] ; entry ; entry = entry->nxt_) { /* call the callback with this entry */ (*cb)(cbctx, entry); } } /* * Find an object in the table matching a given leading substring. * We'll return the longest-named entry that matches a leading substring * of the given string. For example, if there's are entires A, AB, ABC, * and ABCE, and this routine is called to find something matching * ABCDEFGH, we'll return ABC as the match (not ABCE, since it doesn't * match any leading substring of the given string, and not A or AB, * even though they match, since ABC also matches and it's longer). */ CVmHashEntry *CVmHashTable::find_leading_substr(const char *str, size_t len) { size_t sublen; CVmHashEntry *entry; /* * try to find each leading substring, starting with the longest, * decreasing by one character on each iteration, until we've used * the whole string */ for (sublen = len ; sublen > 0 ; --sublen) { /* if this substring matches, use it */ if ((entry = find(str, sublen)) != 0) return entry; } /* we didn't find it */ return 0; } /* * Enumerate all entries */ void CVmHashTable::enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* go through each entry at this hash value */ for (entry = *tableptr ; entry ; entry = nxt) { /* * remember the next entry, in case the callback deletes the * current entry */ nxt = entry->nxt_; /* invoke the callback on this entry */ (*func)(ctx, entry); } } } /* * Enumerate all entries - ultra-safe version. This version should be * used when the callback code might delete arbitrary entries from the * hash table. This version is slower than the standard enum_entries, but * will tolerate any changes to the table made in the callback. */ void CVmHashTable::safe_enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { size_t list_idx; /* * start at the first (0th) entry in the current hash chain, and * keep going until we run out of entries in the chain */ for (list_idx = 0 ; ; ++list_idx) { CVmHashEntry *entry; size_t j; /* * Scan the hash chain for the current entry index. * * This is the part that makes this version slower than the * standard version and safer than the standard version. It's * slower than enum_entries() because we must scan the chain * list on every iteration to find the next entry, whereas * enum_entries() simply keeps a pointer to the next entry. * It's safer because we don't keep any pointers - if next * element is deleted in the callback in enum_entries(), that * stored next pointer would be invalid, but we store no * pointers that could become stale. */ for (j = 0, entry = *tableptr ; j < list_idx && entry != 0 ; entry = entry->nxt_, ++j) ; /* if we failed to find the entry, we're done with this chain */ if (entry == 0) break; /* invoke the callback on this entry */ (*func)(ctx, entry); } } } /* * Move all entries in this table to a new table */ void CVmHashTable::move_entries_to(CVmHashTable *new_tab) { CVmHashEntry **tableptr; size_t i; /* go through each hash value */ for (tableptr = table_, i = 0 ; i < table_size_ ; ++i, ++tableptr) { CVmHashEntry *entry; CVmHashEntry *nxt; /* go through each entry at this hash value */ for (entry = *tableptr ; entry ; entry = nxt) { /* * remember the next entry, since we'll be unlinking it from * this table, which will render the nxt_ member unusable * for the purposes of completing this enumeration */ nxt = entry->nxt_; /* * clear the 'next' pointer in this entry, to unlink it from * our table - since everything is being removed, there's no * need to worry about what came before us */ entry->nxt_ = 0; /* add the entry to the new hash table */ new_tab->add(entry); } /* * clear this hash value chain head - we've now removed * everything from it */ *tableptr = 0; } } /* ------------------------------------------------------------------------ */ /* * Debugging Functions */ #ifdef T3_DEBUG /* * dump information the hash table to stderr */ void CVmHashTable::debug_dump() const { long total; long longest; long avg; int over_avg; int empty; size_t i; /* gather statistics on the hash table */ for (total = longest = 0, empty = 0, i = 0 ; i < table_size_ ; ++i) { CVmHashEntry *cur; int curlen; /* scan this chain */ for (curlen = 0, cur = table_[i] ; cur != 0 ; cur = cur->nxt_) { ++curlen; ++total; } /* if it's empty, so note */ if (curlen == 0) ++empty; /* if it's longer than the longest, so note */ if (curlen > longest) longest = curlen; } /* calculate the average length */ avg = total/table_size_; /* count chains over average length */ for (over_avg = 0, i = 0 ; i < table_size_ ; ++i) { CVmHashEntry *cur; int curlen; /* scan this chain */ for (curlen = 0, cur = table_[i] ; cur != 0 ; cur = cur->nxt_) ++curlen; /* if it's over average length, note it */ if (curlen > avg) ++over_avg; } /* display the statistics */ fprintf(stderr, "hash table: total %ld, longest %ld, average %ld\n" "number of buckets over average length: %d\n" "number of empty buckets: %d\n", total, longest, avg, over_avg, empty); } #else /* T3_DEBUG */ /* dummy functions for release builds */ void CVmHashTable::debug_dump() const { } #endif /* T3_DEBUG */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmimage.h����������������������������������������������������������������������0000644�0001750�0000144�00000074310�11736126125�015331� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header: d:/cvsroot/tads/tads3/vmimage.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimage.h - VM image file loader Function Loads an image file for execution. The VM loads and executes an image by creating an appropriate concrete CVmImageFile subclass, then creating a CVmImageLoader using the CVmImageFile object. CVmImageLoader parses the input file, sets up page mappings for constant pools, and initializes objects found in the file. The lifespan of the CVmImageLoader object is the lifespan of the loaded image. As long as the VM is executing the image, it must keep the CVmImageLoader object in existence. The CVmImageLoader object can be deleted after the VM terminates execution of the image. The loader comes in two varieties: an external file loader, and a memory-mapped file loader. The external file loader reads data from a disk file, allocating memory for the information read from the file. The memory-mapped file loader takes a chunk of memory that's already been loaded, and initializes the VM to use the pre-allocated memory, rather than allocating a separate copy of the image data. The memory-mapped loader is meant for systems with no external storage, such as hand-held devices. It can also be used on systems with large, flat address spaces to speed up loading by isolating all disk access into a single bulk load of the image into memory. The external file loader is useful for systems with smaller address spaces, and can be used with a swapping pool implementation to allow the VM to operate when available memory is smaller than the image file. Notes Modified 12/12/98 MJRoberts - Creation */ #ifndef VMIMAGE_H #define VMIMAGE_H #include <memory.h> #include "vmpool.h" #include "vmglob.h" #include "vmtype.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Image file constants */ /* * signature - this is at the beginning of every image file so that we * can easily detect a completely invalid file */ #define VMIMAGE_SIG "T3-image\015\012\032" /* ------------------------------------------------------------------------ */ /* * Data Block Flags */ /* * Mandatory: this block must be recognized and loaded. When new types * of blocks are added in future versions, the compiler can use this * flag to indicate whether or not an old VM can safely ignore the new * block type. An old VM will not be able to load a new block, since * that new block won't have been part of the spec the VM was designed * for; in some cases, an image file can be successfully loaded and used * even when a particular block is ignored (possibly with the loss of * the new feature enabled by the data in the block). When a block can * be ignored by an old VM without losing the ability to correctly load * the image for the old VM's functionality, the mandatory flag will be * set to 0; when a new block carries information without which the * image cannot be properly loaded, the mandatory flag will be set to 1. */ #define VMIMAGE_DBF_MANDATORY 0x0001 /* ------------------------------------------------------------------------ */ /* * Constant pool image tracking structure. For each constant pool, we * maintain information on the locations in the image file of the pages * in the pool. */ /* number of entries in each subarray */ const size_t VMIMAGE_POOL_SUBARRAY_SIZE = 4096; /* page information structure */ struct CVmImagePool_pg { /* seek offset of the page */ long seek_pos; /* size of the page */ size_t page_size; /* XOR mask for the page */ uchar xor_mask; }; class CVmImagePool: public CVmPoolBackingStore { public: CVmImagePool(); ~CVmImagePool(); /* * Initialize the pool with a given number of pages and page size. * This can only be called once, and must be called before any page * locations can be established. */ void init(class CVmImageFile *fp, ulong page_count, ulong page_size); /* set a page's information */ void set_page_info(ulong page_idx, long seek_pos, size_t page_size, uchar xor_mask); /* apply an XOR mask to a block of bytes */ static void apply_xor_mask(char *p, size_t len, uchar xor_mask) { /* * apply the mask only if it's non-zero - xor'ing zero with * anything yields the original value, so we can avoid a lot of * pointless memory traversal by checking this first */ if (xor_mask != 0) { /* xor each byte with the xor mask */ for ( ; len != 0 ; --len, ++p) *p ^= xor_mask; } } /* -------------------------------------------------------------------- */ /* * CVmPoolBackingStore implementation */ /* get the total number of pages */ size_t vmpbs_get_page_count() { return page_count_; } /* get the common page size */ size_t vmpbs_get_common_page_size() { return page_size_; } /* get the size of a given page */ size_t vmpbs_get_page_size(pool_ofs_t ofs, size_t page_size) { return get_page_info_ofs(ofs)->page_size; } /* allocate and load a given page */ const char *vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size); /* free a page */ void vmpbs_free_page(const char *mem, pool_ofs_t ofs, size_t page_size); /* load a page into a given memory block */ void vmpbs_load_page(pool_ofs_t ofs, size_t page_size, size_t load_size, char *mem); /* determine if the backing store pages are writable */ int vmpbs_is_writable(); /* -------------------------------------------------------------------- */ private: /* compute the number of subarray pages we have */ size_t get_subarray_count() const { return (size_t)((page_count_ + VMIMAGE_POOL_SUBARRAY_SIZE - 1) / VMIMAGE_POOL_SUBARRAY_SIZE); } /* given a page index, get the information structure at the index */ CVmImagePool_pg *get_page_info(ulong idx) const { return &(page_info_[idx / VMIMAGE_POOL_SUBARRAY_SIZE] [idx % VMIMAGE_POOL_SUBARRAY_SIZE]); } /* given a pool offset, get the information structure for the page */ CVmImagePool_pg *get_page_info_ofs(pool_ofs_t ofs) const { return get_page_info(ofs / page_size_); } /* * Given a pool offset, seek to the image file data for the page, in * preparation for loading the data from the image file into memory. */ void seek_page_ofs(pool_ofs_t ofs); /* number of pages in the pool */ ulong page_count_; /* page size - each page in the pool has a common size */ ulong page_size_; /* * Page seek array. To accommodate 16-bit platforms, we keep this as a * set of arrays, with each subarray smaller than 64k. */ CVmImagePool_pg **page_info_; /* underlying image file */ class CVmImageFile *fp_; }; /* ------------------------------------------------------------------------ */ /* * Image loader. This takes an image file interface object (see below), * and loads the underlying image data into memory. */ class CVmImageLoader { public: /* initialize with an image file interface */ CVmImageLoader(class CVmImageFile *fp, const char *fname, long base_ofs); /* destruction */ ~CVmImageLoader(); /* * Load the image. */ void load(VMG0_); /* * Load a resource-only image file. 'fileno' is the file number * assigned by the host application (via the add_resfile() * interface) to the resource file. */ void load_resource_file(class CVmImageLoaderMres *res_ifc); /* load resources from a file handle at the current seek location */ static void load_resources_from_fp(osfildef *fp, const char *fname, class CVmHostIfc *hostifc); /* load resources from a file handle at the current seek location */ static void load_resources_from_fp(osfildef *fp, const char *fname, class CVmImageLoaderMres *res_ifc); /* * Run the image. This transfers control to the entrypoint defined in * the image file. This function doesn't return until the program * defined in the image terminates its execution by returning from the * entrypoint function or throwing an unhandled exception. * * If 'saved_state' is not null, it gives a null-terminated character * string with the name of a saved state file to be restored * immediately. We'll pass this information to the program's * entrypoint so it can handle the restore appropriately. * * The caller must create the code and constant pools before invoking * this. We'll set up the pools with their backing stores as loaded * from the image file. * * The global_symtab argument optionally provides the global symbol * table; if this is null, we'll use our own global symbol table that * we loaded from the debug records, if we found any. * * If an unhandled exception is thrown, this function throws * VMERR_UNHANDLED_EXC, with the exception object as the first * parameter. */ void run(VMG_ const char *const *argv, int argc, class CVmRuntimeSymbols *global_symtab, class CVmRuntimeSymbols *macro_symtab, const char *saved_state); /* run all static initializers */ void run_static_init(VMG0_); /* * Unload the image. This should be called after execution is * finished to disactivate the pools, which must be done before the * image file is deleted. */ void unload(VMG0_); /* * Create a global LookupTable to hold the symbols in the global * symbol table. */ void create_global_symtab_lookup_table(VMG0_); /* create a global LookupTable to hold the symbols in the macro table */ void create_macro_symtab_lookup_table(VMG0_); /* determine if the given block type identifiers match */ static int block_type_is(const char *type1, const char *type2) { /* compare the four-byte identifiers to see if they're identical */ return (memcmp(type1, type2, 4) == 0); } /* * Get the image file's timestamp. This is a 24-byte array in the * format "Sun Aug 01 17:05:20 1999". The purpose of the image file * timestamp is to provide a reasonably unique identifier that can * be stored in a saved state file and then checked upon loading the * file to ensure that it was created by the identical version of * the image file. */ const char *get_timestamp() const { return ×tamp_[0]; } /* get the filename of the loaded image */ const char *get_filename() const { return fname_; } /* get the fully-qualified, absolute directory path of the loaded image */ const char *get_path() const { return path_; } /* * check to see if the image file has a global symbol table (GSYM * block) */ int has_gsym() const { return has_gsym_ != 0; } /* * get the object ID of the LookupTable with the global symbol table * for reflection purposes */ vm_obj_id_t get_reflection_symtab() const { return reflection_symtab_; } /* get the object ID of the LookupTable with the macro table */ vm_obj_id_t get_reflection_macros() const { return reflection_macros_; } /* * perform dynamic linking after loading, resetting, or restoring */ void do_dynamic_link(VMG0_); /* * delete all synthesized exports - this must be called just prior to * resetting to image file state or loading a saved state */ void discard_synth_exports(); /* allocate a new property ID */ vm_prop_id_t alloc_new_prop(VMG0_); /* get the last property ID currently in use */ vm_prop_id_t get_last_prop(VMG0_); /* save/restore the synthesized export table */ void save_synth_exports(VMG_ class CVmFile *fp); int restore_synth_exports(VMG_ class CVmFile *fp, class CVmObjFixup *fixups); /* get the starting offset of static initializers in the code pool */ ulong get_static_cs_ofs() const { return static_cs_ofs_; } /* get the entrypoint function's code pool offset */ uint32_t get_entrypt() const { return entrypt_; } private: /* load external resource files associated with an image file */ void load_ext_resfiles(VMG0_); /* read and verify an image file header */ void read_image_header(); /* load an Entrypoint block */ void load_entrypt(VMG_ ulong siz); /* load a Static Object (OBJS) block */ void load_static_objs(VMG_ ulong siz); /* load a Constant Pool Definition (CPDF) block */ void load_const_pool_def(ulong siz); /* load a Constant Pool Page (CPPG) block */ void load_const_pool_page(ulong siz); /* load a Multimedia Resource (MRES) block */ void load_mres(ulong siz, class CVmImageLoaderMres *res_ifc); /* load a Multimedia Resource Link (MREL) block */ void load_mres_link(ulong size, class CVmImageLoaderMres *res_ifc); /* load a Metaclass Dependency block */ void load_meta_dep(VMG_ ulong siz); /* load a Function Set Dependency block */ void load_funcset_dep(VMG_ ulong siz); /* load a Symbolic Names block */ void load_sym_names(VMG_ ulong siz); /* load a Source Filenames block */ void load_srcfiles(VMG_ ulong siz); /* load a Global Symbols block */ void load_gsym(VMG_ ulong siz); /* load a Global Symbols (GSYM) block into the runtime symbol table */ void load_runtime_symtab_from_gsym(VMG_ ulong siz); /* load a Macro Symbols (MACR) block into the runtime symbol table */ void load_runtime_symtab_from_macr(VMG_ ulong siz); /* load a Macro Symbols (MACR) block */ void load_macros(VMG_ ulong siz); /* load a Method Header List block */ void load_mhls(VMG_ ulong siz); /* load a Static Initializer List block */ void load_sini(VMG_ ulong siz); /* * Fix up the debugging global symbol table's object entries with the * correct metaclass IDs. This has to wait until the whole image file * is loaded so that we're sure we have all of the objects loaded * already. */ void fix_gsym_meta(VMG0_); /* * Copy data from the file into a buffer, decrementing a size * counter. We'll throw a BLOCK_TOO_SMALL error if the read length * exceeds the remaining size. */ void read_data(char *buf, size_t read_len, ulong *remaining_size); /* skip data */ void skip_data(size_t skipo_len, ulong *remaining_size); /* * Allocate memory for data and read the data from the file, * decrementing the amount read from a size counter. Throws * BLOCK_TOO_SMALL if the read length exceeds the remaining size. */ const char *alloc_and_read(size_t read_len, ulong *remaining_size); /* add a resource to our resource table */ void add_resource(class CVmResource *res); /* * Add a symbol to the list of synthesized exports. Each time we * synthesize a value because we didn't find the associated symbol * exported from the image file, we must add an entry to this table. * On saving state, we'll save these symbols to the saved state file * so they will be restored on load. */ void add_synth_export_obj(const char *nm, vm_obj_id_t val); void add_synth_export_prop(const char *nm, vm_prop_id_t val); /* set the last property ID value */ void set_last_prop(VMG_ vm_prop_id_t last_prop); /* callback for synthesized export enumeration: save to file */ static void save_synth_export_cb(void *ctx, class CVmHashEntry *entry); /* underlying image file interface */ class CVmImageFile *fp_; /* image filename */ char *fname_; /* * fully-qualified, absolute directory path to the file (this is just * the directory path, sans the filename) */ char *path_; /* the base seek offset of the image stream within the image file */ long base_seek_ofs_; /* image file version number */ uint ver_; /* image file timestamp */ char timestamp_[24]; /* code pool offset of entrypoint function */ uint32_t entrypt_; /* pool tracking objects */ class CVmImagePool *pools_[2]; /* * The image's exported symbols. These are the symbols that the * program explicitly exported for dynamic linking from the VM. */ class CVmHashTable *exports_; /* * List of exports synthesized after loading by the VM. These exports * are not in the image file, so they must be saved in the saved state * file so that we can reattach to the same objects and properties on * restore. */ class CVmHashTable *synth_exports_; /* * The runtime global symbol table, if we have one. We'll build this * from the debug records if we find any, or from the records passed * in from the compiler when we run preinitialization. * * Note that the runtime global symbols are not the same as the * exported symbols. The exports are the symbols explicitly exported * for dynamic linking, so that the VM can attach to particular * objects defined in the image file. The runtime globals are all of * the compile-time global symbols as reflected in the debugging * records, and are used for reflection-type operations. */ class CVmRuntimeSymbols *runtime_symtab_; /* * The runtime macro definitions table, if we have one. As with the * runtime global symbol table, we build this from the debug records, * or from the records passed in from the compiler during preinit. */ class CVmRuntimeSymbols *runtime_macros_; /* object ID of LookupTable containing the global symbol table */ vm_obj_id_t reflection_symtab_; /* object ID of LookupTable containing the macro symbol table */ vm_obj_id_t reflection_macros_; /* head/tail of list of static initializer pages */ class CVmStaticInitPage *static_head_; class CVmStaticInitPage *static_tail_; /* * starting offset in code pool of static initializer code - the * compiler groups all static initializer code, and only static * initializer code, above this point, so after preinit, we can omit * all code above this point from the rewritten image file */ ulong static_cs_ofs_; /* flag: metaclass dependency table loaded */ uint loaded_meta_dep_ : 1; /* flag: function set dependency table loaded */ uint loaded_funcset_dep_ : 1; /* flag: entrypoint loaded */ uint loaded_entrypt_ : 1; /* * flag: the image file has a GSYM (global symbol table), which * implies that it was compiled for debugging */ uint has_gsym_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Static initializer page. Each page contains a fixed number of * initializers. */ const size_t VM_STATIC_INIT_PAGE_MAX = 1000; class CVmStaticInitPage { public: CVmStaticInitPage(size_t cnt) { /* remember the number of records */ cnt_ = cnt; /* no data yet */ data_ = 0; /* not in a list yet */ nxt_ = 0; } /* get an object/property ID given the index of a record */ vm_obj_id_t get_obj_id(size_t idx) { return vmb_get_objid(get_rec(idx)); } vm_prop_id_t get_prop_id(size_t idx) { return vmb_get_propid(get_rec(idx) + VMB_OBJECT_ID); } /* get a raw record pointer given an index */ const char *get_rec(size_t idx) { return data_ + (idx * 6); } /* next page in list */ CVmStaticInitPage *nxt_; /* number of records in the page */ size_t cnt_; /* * The data of the page. Each record is six bytes long, in portable * format: a UINT4 for the object ID, and a UINT2 for the property * ID. */ const char *data_; }; /* ------------------------------------------------------------------------ */ /* * Image file resource loader interface. This is an abstract class * interface that must be provided to load_resource_file() to provide * per-resource loading. */ class CVmImageLoaderMres { public: virtual ~CVmImageLoaderMres() { } /* load a resource */ virtual void add_resource(uint32_t seek_ofs, uint32_t siz, const char *res_name, size_t res_name_len) = 0; /* load a resource link */ virtual void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) = 0; }; /* ------------------------------------------------------------------------ */ /* * Image file interface. This is an abstract interface that provides * access to the data in an image file independently of the location of * the data. */ class CVmImageFile { public: /* delete the loader */ virtual ~CVmImageFile() { } /* duplicate the image file interface, a la stdio freopen() */ virtual CVmImageFile *dup(const char *mode) = 0; /* * Copy data from the image file to the caller's buffer. Reads from * the current file position. */ virtual void copy_data(char *buf, size_t len) = 0; /* * Allocate memory for and load data from the image file. Reads from * the current file position. Returns a pointer to the allocated data. * * 'remaining_in_page' is the amount of space remaining in the current * block being read from the image, including the space being read * here; this can be used as an upper bound for a new allocation if * the concrete subclass wishes to allocate blocks for suballocation. */ virtual const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page) = 0; /* * Determine if memory read with alloc_and_read() is writable. * Returns true if so, false if not. If the memory that * alloc_and_read() returns is a copy of the external file (rather * than mapped to the original external data, as might be the case * on a small machine without external storage, such as a palm-top * computer), this should return true. */ virtual int allow_write_to_alloc() = 0; /* free memory previously allocated by alloc_and_read */ virtual void free_mem(const char *mem) = 0; /* seek to a new file position, as an offset from the start of the file */ virtual void seek(long pos) = 0; /* get the current seek position */ virtual long get_seek() const = 0; /* skip the given number of bytes */ virtual void skip_ahead(long len) = 0; }; /* * Implementation of the generic stream interface for an image file block. * This will limit reading to the data in the block. */ class CVmImageFileStream: public CVmStream { public: CVmImageFileStream(CVmImageFile *fp, size_t len) { /* * remember the underlying image file, and the amount of space in * our data block */ fp_ = fp; len_ = len; } CVmStream *clone(VMG_ const char *mode) { /* duplicate our file handle */ CVmImageFile *fpdup = fp_->dup(mode); if (fpdup == 0) return 0; /* create a new image file stream wrapper for the duplicate handle */ return new CVmImageFileStream(fpdup, len_); } /* read bytes into a buffer */ virtual void read_bytes(char *buf, size_t len); virtual size_t read_nbytes(char *buf, size_t len); /* read a line (not used for this object) */ virtual char *read_line(char *buf, size_t len) { return 0; } /* write bytes */ virtual void write_bytes(const char *, size_t); /* get/set the seek position */ virtual long get_seek_pos() const { return fp_->get_seek(); } virtual void set_seek_pos(long pos) { fp_->seek(pos); } virtual long get_len() { return len_; } private: /* our underlying image file reader */ CVmImageFile *fp_; /* remaining data length in the underlying block */ size_t len_; }; /* ------------------------------------------------------------------------ */ /* * Image file interface - external disk file */ class CVmImageFileExt: public CVmImageFile { public: /* delete the image file loader */ ~CVmImageFileExt(); /* initialize with an underlying file */ CVmImageFileExt(class CVmFile *fp) { /* remember our file */ fp_ = fp; /* we don't have any suballocation blocks yet */ mem_head_ = mem_tail_ = 0; } /* duplicate the file interface */ virtual CVmImageFile *dup(const char *mode) { /* duplicate our file handle */ CVmFile *fpdup = fp_->dup(mode); if (fpdup == 0) return 0; /* return a new wrapper object for the duplicate handle */ return new CVmImageFileExt(fpdup); } /* * CVmImageFile interface implementation */ /* copy data to the caller's buffer */ void copy_data(char *buf, size_t len); /* allocate memory for and read data */ const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page); /* allow writing to alloc_and_read blocks */ virtual int allow_write_to_alloc() { return TRUE; } /* * Free memory previously allocated with alloc_and_read. We don't * need to do anything here; once we allocate and load a block, we * keep it in memory until the entire load image is deleted, at * which time we free all of the associated memory. */ void free_mem(const char *) { } /* seek to a new file position */ void seek(long pos); /* get the current seek position */ long get_seek() const; /* skip the given number of bytes */ void skip_ahead(long len); private: /* allocate memory for loading data */ char *alloc_mem(size_t siz, ulong remaining_in_page); /* the underlying file */ class CVmFile *fp_; /* * Memory block list. We keep a set of memory blocks for loading * data via the alloc_and_read() method. Rather than allocating an * individual "malloc" block for each alloc_and_read() call, we * allocate a large block, and suballocate memory out of the large * block. Since we don't know exactly how much memory we'll need in * advance, we take a guess at how large a block we need, * suballocate from the block until we fill it up, then allocate * another block and fill it up, then allocate another, and so on. * We keep all of the blocks we allocate in a linked list. */ class CVmImageFileExt_blk *mem_head_; class CVmImageFileExt_blk *mem_tail_; }; /* * aggregate allocation block size - use a size that should be reasonably * safe for 16-bit platforms (not over 64k, and a bit less to allow for * some malloc overhead) */ const size_t VMIMAGE_EXT_BLK_SIZE = 65000; /* * Memory tracking structure for external file reader. For each large * memory block we allocate (for suballocation), we allocate one of * these structures. */ class CVmImageFileExt_blk { public: /* create the block, allocating the given number of bytes for the block */ CVmImageFileExt_blk(size_t siz); /* delete the block */ ~CVmImageFileExt_blk(); /* suballocate memory out of the current block */ char *suballoc(size_t siz); /* next block in the list */ CVmImageFileExt_blk *nxt_; /* previous block in the list */ CVmImageFileExt_blk *prv_; /* pointer to the start of the block */ char *block_ptr_; /* number of bytes remaining in the block for future suballocations */ size_t rem_; /* pointer to next free byte of the block */ char *free_ptr_; }; /* ------------------------------------------------------------------------ */ /* * Image file interface - memory-mapped implementation. This * implementation assumes that the file is loaded into memory in a * contiguous chunk, which can be addressed linearly. */ class CVmImageFileMem: public CVmImageFile { public: ~CVmImageFileMem() { } /* initialize with an underlying block of pre-loaded data */ CVmImageFileMem(const char *mem, long len) { /* remember where our data are */ mem_ = mem; len_ = len; /* start at the beginning of the data */ pos_ = 0; } /* duplicate the file interface */ CVmImageFile *dup(const char *mode) { return new CVmImageFileMem(mem_, len_); } /* * CVmImageFile interface implementation */ /* copy data to the caller's buffer */ void copy_data(char *buf, size_t len); /* allocate memory for and read data */ const char *alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page); /* * do not allow writing to alloc_and_read blocks, since we map these * blocks directly to the underlying in-memory data */ virtual int allow_write_to_alloc() { return FALSE; } /* * Free memory allocated by alloc_and_read. Since our underlying * file is entirely in memory to start with, we don't actually ever * allocate any memory; hence, we don't actually need to free any * memory here. */ void free_mem(const char *) { } /* seek to a new file position */ void seek(long pos) { pos_ = pos; } /* get the current seek position */ long get_seek() const { return pos_; } /* skip the given number of bytes */ void skip_ahead(long len) { pos_ += len; } private: /* the underlying memory block */ const char *mem_; /* size in bytes of the underlying memory block */ ulong len_; /* current offset within the memory block */ long pos_; }; #endif /* VMIMAGE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmnetcfg.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000020451�11736304461�016046� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnet.cpp - TADS 3 network configuration Function Implements the network configuration object for TADS 3. Notes Modified 04/11/10 MJRoberts - Creation */ #include <stdlib.h> #include <string.h> #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnet.h" #include "vmglob.h" #include "vmerr.h" #include "vmtype.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmisaac.h" #include "sha2.h" #include "vmpredef.h" #include "vmhttpreq.h" #include "vmglob.h" #include "vmmain.h" #include "vmpredef.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * Configuration file reader */ TadsNetConfig::~TadsNetConfig() { /* delete our variables */ TadsNetConfigVar *cur, *nxt; for (cur = first_var ; cur != 0 ; cur = nxt) { nxt = cur->getnext(); delete cur; } } /* * load a configuration file */ void TadsNetConfig::read(osfildef *fp, CVmMainClientIfc *clientifc) { /* read the file line by line */ for (int linenum = 1 ; ; ++linenum) { /* read the next line */ char buf[512]; if (osfgets(buf, sizeof(buf), fp) == 0) break; /* skip leading whitespace */ char *p; for (p = buf ; isspace(*p) ; ++p) ; /* skip blank lines and comments */ if (*p == '\0' || *p == '#') continue; /* the variable name is the first token on the line */ char *name = p; /* find the end of the name: the next space or '=' */ for ( ; *p != '\0' && !isspace(*p) && *p != '=' ; ++p) ; char *name_end = p; /* skip spaces */ for ( ; isspace(*p) ; ++p) ; /* make sure we stopped at a '=' */ if (*p != '=') { char *msg = t3sprintf_alloc( "Missing '=' in web config file at line %d", linenum); clientifc->display_error(0, 0, msg, FALSE); t3free(msg); continue; } /* null-terminate the name */ *name_end = '\0'; /* skip spaces after the '=' */ for (++p ; isspace(*p) ; ++p) ; /* the value starts here */ char *val = p; /* * the value is the rest of the line, minus trailing spaces and * newlines */ for (p += strlen(p) ; p > val && (isspace(*(p-1)) || *(p-1) == '\n' || *(p-1) == '\r') ; --p) ; /* null-terminate the value */ *p = '\0'; /* add the variable */ TadsNetConfigVar *var = set(name, val); /* check that this is a variable name we recognize */ static const char *known_vars[] = { "serverid", "storage.domain", "storage.rootpath", "storage.apikey", "watchdog" }; int found = FALSE; for (size_t i = 0 ; i < countof(known_vars) ; ++i) { if (var->matchname(known_vars[i])) { found = TRUE; break; } } /* warn if we didn't find it among the known variables */ if (!found) { char *msg = t3sprintf_alloc( "Warning: unknown variable name '%s' in web config file " "at line %d\n", name, linenum); clientifc->display_error(0, 0, msg, FALSE); t3free(msg); } } } /* * set a variable */ TadsNetConfigVar *TadsNetConfig::set(const char *name, const char *val) { /* check for an existing variabel */ TadsNetConfigVar *var = getvar(name); if (var != 0) { /* found it - set the new value */ var->setval(val); } else { /* didn't find it - add a new variable */ var = new TadsNetConfigVar(name, val); if (last_var != 0) last_var->setnext(var); else first_var = var; last_var = var; } /* return the variable */ return var; } /* * retrieve a variable's value */ const char *TadsNetConfig::get(const char *name) const { TadsNetConfigVar *var = getvar(name); return (var != 0 ? var->getval() : 0); } /* * look up a variable */ TadsNetConfigVar *TadsNetConfig::getvar(const char *name) const { /* scan the variable list for the given name */ for (TadsNetConfigVar *cur = first_var ; cur != 0 ; cur = cur->getnext()) { /* if this one's name matches, return it */ if (cur->matchname(name)) return cur; } /* didn't find it */ return 0; } /* ------------------------------------------------------------------------ */ /* * Check a storage server API reply */ void vmnet_check_storagesrv_reply(VMG_ int htmlstat, CVmDataSource *reply, const char *headers) { /* retrieve and check the status code */ vmnet_check_storagesrv_stat( vmg_ vmnet_get_storagesrv_stat(vmg_ htmlstat, reply, headers)); } /* * Check a storage server status code returned by * vmnet_get_storagesrv_reply(). On failure, throw an error; on success, * simply return. In either case, we'll free the status code buffer. */ void vmnet_check_storagesrv_stat(VMG_ char *stat) { /* if it's not "OK", throw an error */ if (memcmp(stat, "OK ", 3) != 0) { /* * Error. Push the error code/message text string as the argument * to the runtime error constructor, then discard our copy of the * buffer. */ G_interpreter->push_string(vmg_ stat); t3free(stat); /* throw a StorageServerError */ G_interpreter->throw_new_rtesub( vmg_ G_predef->storage_server_error, 1, VMERR_STORAGE_SERVER_ERR); } else { /* success - discard the message text */ t3free(stat); } } /* * Get the storage server API reply code and message. Returns an allocated * buffer that the caller must free with t3free(). The first * space-delimited token in the return buffer is the code, and the rest is * the human-readable error message. For HTTP or network errors, the code * is simply the numeric error code (positive for HTTP status codes, * negative for internal network errors), with no message text. */ char *vmnet_get_storagesrv_stat(VMG_ int htmlstat, CVmDataSource *reply, const char *headers) { /* check the HTML status */ if (htmlstat == 200) { /* * The HTML transaction succeeded - check the reply. Start with * the headers, if provided. */ if (headers != 0) { /* find the X-IFDBStorage-Status header */ for (const char *p = headers ; ; p += 2) { /* are we at our header? */ if (memcmp(p, "X-IFDBStorage-Status:", 21) == 0) { /* this is our header - skip spaces and get the value */ for (p += 21 ; isspace(*p) ; ++p) ; /* find the end of the line or end of the headers */ const char *nl = strstr(p, "\r\n"); if (nl == 0) nl = p + strlen(p); /* return the message text */ return lib_copy_str(p, nl - p); } /* not our header - skip to the end of the line */ p = strstr(p, "\r\n"); if (p == 0) break; } } /* * We didn't find the header, so check the reply body. Read the * first line of the reply, since this contains the result code. */ reply->seek(0, OSFSK_SET); char *txt = reply->read_line_alo(); /* remove the trailing newline */ size_t l; if ((l = strlen(txt)) != 0 && txt[l-1] == '\n') txt[--l] = '\0'; /* return the message text */ return txt; } else { /* * HTML or network error. Return a message containing the numeric * status as the error code, with no text message. */ return t3sprintf_alloc("%d ", htmlstat); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmcrc.h������������������������������������������������������������������������0000644�0001750�0000144�00000001240�07713015750�015007� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright (c) 2003 by Michael J. Roberts. All Rights Reserved. */ /* Name vmcrc.h - compute a CRC-32 checksum of a data stream Function Notes Modified 06/21/03 MJRoberts - Creation */ #ifndef VMCRC_H #define VMCRC_H #include <stdlib.h> class CVmCRC32 { public: CVmCRC32() { /* start with zero in the accumulator */ acc_ = 0; } /* add the given buffer into the checksum */ void scan_bytes(const void *ptr, size_t len); /* retrieve the current checksum value */ unsigned long get_crc_val() const { return acc_; } protected: /* the checksum accumulator */ unsigned long acc_; }; #endif /* VMCRC_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmpreini.h���������������������������������������������������������������������0000644�0001750�0000144�00000001543�11332377736�015543� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpreini.h - preinit Function Notes Modified 07/21/99 MJRoberts - Creation */ #ifndef VMPREINI_H #define VMPREINI_H /* * Run preinitialization. Loads the image file, invokes its main * entrypoint, and saves the new file. */ void vm_run_preinit(class CVmFile *origfp, const char *orig_image_fname, class CVmFile *newfp, class CVmHostIfc *hostifc, class CVmMainClientIfc *clientifc, const char *const *argv, int argc, class CVmRuntimeSymbols *global_symtab, class CVmRuntimeSymbols *macros); #endif /* VMPREINI_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmcoll.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000007450�11331374057�015534� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcoll.cpp - collection metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #include <stdlib.h> #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassCollection metaclass_reg_obj; CVmMetaclass *CVmObjCollection::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjCollection:: *CVmObjCollection::func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) = { &CVmObjCollection::getp_undef, &CVmObjCollection::getp_create_iter, &CVmObjCollection::getp_create_live_iter }; /* ------------------------------------------------------------------------ */ /* * Get a property */ int CVmObjCollection::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_val_t self_val; /* set up the 'self' value */ self_val.set_obj(self); /* use the constant collection version */ if (const_get_coll_prop(vmg_ prop, retval, &self_val, source_obj, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * Get a property of a constant value */ int CVmObjCollection::const_get_coll_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, const vm_val_t *self_val, vm_obj_id_t *src_obj, uint *argc) { uint func_idx; /* presume no source object */ *src_obj = VM_INVALID_OBJ; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ retval, self_val, argc)) return TRUE; /* not found */ return FALSE; } /* * Create an iterator */ int CVmObjCollection::getp_create_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push(self_val); /* create the iterator */ new_iterator(vmg_ retval, self_val); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Create a live iterator */ int CVmObjCollection::getp_create_live_iter(VMG_ vm_val_t *retval, const vm_val_t *self_val, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push(self_val); /* create the "live" iterator */ new_live_iterator(vmg_ retval, self_val); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmfile.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000002371�11736305162�015517� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMFILE.CPP,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfile.cpp - VM file implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include "t3std.h" #include "vmfile.h" #include "vmerr.h" #include "vmdatasrc.h" /* * delete the file object */ CVmFile::~CVmFile() { /* if we still have an underlying OS file, close it */ if (fp_ != 0) osfcls(fp_); } /* * open for reading */ void CVmFile::open_read(const char *fname, os_filetype_t typ) { /* try opening the underlying OS file for binary reading */ fp_ = osfoprb(fname, typ); /* if that failed, throw an error */ if (fp_ == 0) err_throw(VMERR_FILE_NOT_FOUND); } /* * open for writing */ void CVmFile::open_write(const char *fname, os_filetype_t typ) { /* try opening the underlying OS file for binary writing */ fp_ = osfopwb(fname, typ); /* if that failed, throw an error */ if (fp_ == 0) err_throw(VMERR_CREATE_FILE); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmhosttx.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000023044�11730470033�016123� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhosttx.cpp - text-only host interface implementation Function Provides a base class for the T3 VM Host Interface for implementing text-only applications. Notes Modified 06/16/02 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmhash.h" #include "vmhost.h" #include "vmhosttx.h" /* ------------------------------------------------------------------------ */ /* * Hash table entry for a resource descriptor */ class CResEntry: public CVmHashEntryCI { public: CResEntry(const char *resname, size_t resnamelen, int copy, unsigned long ofs, unsigned long siz, int fileno) : CVmHashEntryCI(resname, resnamelen, copy) { /* remember the file locator information */ fileno_ = fileno; ofs_ = ofs; siz_ = siz; link_ = 0; } CResEntry(const char *resname, size_t resnamelen, int copy, const char *fname, size_t fnamelen) : CVmHashEntryCI(resname, resnamelen, copy) { /* save the local filename */ link_ = lib_copy_str(fname, fnamelen); /* it's not in a resource file */ fileno_ = 0; ofs_ = 0; siz_ = 0; } ~CResEntry() { lib_free_str(link_); } /* file number (this is an index in the hostifc's ext_ array) */ int fileno_; /* byte offset of the start of the resource data in the file */ unsigned long ofs_; /* byte size of the resource data */ unsigned long siz_; /* local filename, for a resource link (rather than a stored resource) */ char *link_; }; /* ------------------------------------------------------------------------ */ /* * construction */ CVmHostIfcText::CVmHostIfcText() { /* create our hash table */ restab_ = new CVmHashTable(128, new CVmHashFuncCI(), TRUE); /* allocate an initial set of external resource filename entries */ ext_max_ = 10; ext_ = (char **)t3malloc(ext_max_ * sizeof(ext_[0])); /* we use slot zero for the image filename, which we don't know yet */ ext_cnt_ = 1; ext_[0] = 0; /* no resource directory specified yet */ res_dir_ = 0; } /* * deletion */ CVmHostIfcText::~CVmHostIfcText() { /* delete our hash table */ delete restab_; /* delete our external filenames */ for (size_t i = 0 ; i < ext_cnt_ ; ++i) lib_free_str(ext_[i]); /* delete our array of external filename entries */ t3free(ext_); /* delete our resource directory path */ lib_free_str(res_dir_); } /* * set the image file name - we always use resource file slot zero to * store the image file */ void CVmHostIfcText::set_image_name(const char *fname) { /* free any old name string */ lib_free_str(ext_[0]); /* remember the new name */ ext_[0] = lib_copy_str(fname); } /* * set the resource directory */ void CVmHostIfcText::set_res_dir(const char *dir) { /* forget any previous setting, and remember the new path */ lib_free_str(res_dir_); res_dir_ = lib_copy_str(dir); } /* * add a resource file */ int CVmHostIfcText::add_resfile(const char *fname) { /* expand the resource file list if necessary */ if (ext_cnt_ == ext_max_) { /* bump up the maximum a bit */ ext_max_ += 10; /* reallocate the entry pointer array */ ext_ = (char **)t3realloc(ext_, ext_max_ * sizeof(ext_[0])); } /* store the new entry */ ext_[ext_cnt_++] = lib_copy_str(fname); /* * return the new entry's file number (we've already bumped the index, * so it's the current count minus one) */ return ext_cnt_ - 1; } /* * add a resource */ void CVmHostIfcText::add_resource(unsigned long ofs, unsigned long siz, const char *resname, size_t resnamelen, int fileno) { CResEntry *entry; /* create a new entry desribing the resource */ entry = new CResEntry(resname, resnamelen, TRUE, ofs, siz, fileno); /* add it to the table */ restab_->add(entry); } /* * add a resource */ void CVmHostIfcText::add_resource(const char *fname, size_t fnamelen, const char *resname, size_t resnamelen) { /* create a new entry desribing the resource */ CResEntry *entry = new CResEntry( resname, resnamelen, TRUE, fname, fnamelen); /* add it to the table */ restab_->add(entry); } /* * find a resource */ osfildef *CVmHostIfcText::find_resource(const char *resname, size_t resnamelen, unsigned long *res_size) { osfildef *fp; char buf[OSFNMAX]; char *fname; char fname_buf[OSFNMAX]; char res_dir[OSFNMAX] = ""; /* * get the resource directory - if there's an explicit resource path * specified, use that, otherwise use the image file folder */ if (res_dir_ != 0) lib_strcpy(res_dir, sizeof(res_dir), res_dir_); else if (ext_[0] != 0) os_get_path_name(res_dir, sizeof(res_dir), ext_[0]); /* try finding an entry in the resource map */ CResEntry *entry = (CResEntry *)restab_->find(resname, resnamelen); if (entry != 0) { /* found it - check the type */ if (entry->link_ == 0) { /* it's a stored binary resource - load it */ fp = osfoprb(ext_[entry->fileno_], OSFTBIN); /* if that succeeded, seek to the start of the resource */ if (fp != 0) osfseek(fp, entry->ofs_, OSFSK_SET); /* tell the caller the size of the resource */ *res_size = entry->siz_; /* return the file handle */ return fp; } else { /* it's a link to a local file */ fname = entry->link_; } } else { /* * There's no entry in the resource map, so convert the resource * name from the URL notation to local file system conventions, and * look for a file with the given name. This is allowed only if * the file safety level setting is 3 (read only local directory * access) or lower. */ if (get_io_safety_read() > 3) return 0; /* * Make a null-terminated copy of the resource name, limiting the * copy to our buffer size. */ if (resnamelen > sizeof(buf) - 1) resnamelen = sizeof(buf) - 1; memcpy(buf, resname, resnamelen); buf[resnamelen] = '\0'; /* convert the resource name to a URL */ os_cvt_url_dir(fname_buf, sizeof(fname_buf), buf); fname = fname_buf; /* if that yields an absolute path, it's an error */ if (os_is_file_absolute(fname)) return 0; /* * If it's not in the resource file folder, it's also an error - we * don't allow paths to parent folders via "..", for example. If * we don't have an resource file folder to compare it to, fail, * since we can't properly sandbox it. Resource files are always * sandboxed to the resource directory, even if the file safety * settings are less restrictive. */ if (res_dir[0] == 0 || !os_is_file_in_dir(fname, res_dir, TRUE, FALSE)) return 0; } /* * If we get this far, it's because we have a local file name in * 'fname' that we need to look up. */ /* check the path for relativity */ if (os_is_file_absolute(fname)) { /* it's already an absolute, fully-qualified path - use it as-is */ lib_strcpy(buf, sizeof(buf), fname); } else { /* * it's a relative path - make sure we have a resource directory * for it to be relative to */ if (res_dir[0] == 0) return 0; /* * build the full path name by combining the image file path with * the relative path we got from the resource name URL, as * converted local file system conventions */ os_build_full_path(buf, sizeof(buf), res_dir, fname); } /* try opening the file */ fp = osfoprb(buf, OSFTBIN); /* return failure if we couldn't find the file */ if (fp == 0) return 0; /* * the entire file is the resource data, so figure out how big the file * is, and tell the caller that the resource size is the file size */ osfseek(fp, 0, OSFSK_END); *res_size = osfpos(fp); /* * seek back to the start of the resource data (which is simply the * start of the file, since the entire file is the resource data) */ osfseek(fp, 0, OSFSK_SET); /* return the file handle */ return fp; } /* * determine if a resource exists */ int CVmHostIfcText::resfile_exists(const char *resname, size_t resnamelen) { /* try opening the resource file */ unsigned long res_size; osfildef *fp = find_resource(resname, resnamelen, &res_size); /* check to see if we successfully opened the resource */ if (fp != 0) { /* found it - close the file and return success */ osfcls(fp); return TRUE; } else { /* couldn't find it - indicate failure */ return FALSE; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmpoolim.cpp�������������������������������������������������������������������0000644�0001750�0000144�00000005263�11333256604�016101� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpool.cpp - constant pool - in-memory (non-swapping) implementation Function Notes Modified 10/20/98 MJRoberts - Creation */ #include <stdlib.h> #include <memory.h> #include "t3std.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * In-memory pool implementation. This pool implementation pre-loads * all available pages in the pool and keeps the complete pool in memory * at all times. */ /* * delete the pool's resources - this is called from our destructor, and * can also be called explicitly to reset the pool */ void CVmPoolInMem::terminate_nv() { /* free any pages we allocated from the backing store */ free_backing_pages(); } /* * free pages that we allocated from the backing store */ void CVmPoolInMem::free_backing_pages() { size_t i; pool_ofs_t ofs; CVmPool_pg *info; /* if there's no backing store, there's nothing to do */ if (backing_store_ == 0) return; /* * Run through the page array and delete each allocated page. Since * we allocate pages through the backing store, delete pages through * the backing store. */ for (i = 0, info = pages_, ofs = 0 ; i < page_slots_ ; ++i, ++info, ofs += page_size_) { /* if this slot was allocated, delete it */ if (info->mem != 0) { /* delete the page */ backing_store_->vmpbs_free_page(info->mem, ofs, page_size_); /* forget it */ info->mem = 0; } } } /* * initialize */ void CVmPoolInMem::attach_backing_store(CVmPoolBackingStore *backing_store) { size_t ofs; size_t i; CVmPool_pg *info; /* do the normal initialization to allocate the page slots */ CVmPoolPaged::attach_backing_store(backing_store); /* load all of the pages */ for (i = 0, info = pages_, ofs = 0 ; i < page_slots_ ; ++i, ++info, ofs += page_size_) { /* determine how much memory we really need for this page */ info->siz = backing_store_->vmpbs_get_page_size(ofs, page_size_); /* allocate and load the page */ info->mem = backing_store_->vmpbs_alloc_and_load_page( ofs, page_size_, info->siz); } } /* * Detach from the backing store */ void CVmPoolInMem::detach_backing_store() { /* release the backing pages */ free_backing_pages(); /* inherit default */ CVmPoolPaged::detach_backing_store(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/tcprsnf.cpp��������������������������������������������������������������������0000644�0001750�0000144�00000005645�11653627446�015735� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprsnf.cpp - TADS 3 Compiler Parser - "no files" module Function This module can be linked with programs that don't require any symbol or object file read/write functionality. This module contains dummy entrypoints for functions and methods that would normally be linked from tcprsfil.cpp, which contains the actual implementations of these methods. This is needed for interpreter builds that link in the compiler for "eval()" functionality, which doesn't require any intermediate file creation or loading. Notes Modified 04/30/99 MJRoberts - Creation */ #include <assert.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include "os.h" #include "t3std.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tcmake.h" /* ------------------------------------------------------------------------ */ /* * Object file readers */ void CTcSymObjBase::load_refs_from_obj_file( CVmFile *, const char *, tctarg_obj_id_t *, tctarg_prop_id_t *) { assert(FALSE); } /* ------------------------------------------------------------------------ */ /* * Object file writers */ int CTcSymbolBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_refs_to_obj_file(class CVmFile *) { assert(FALSE); return FALSE; } int CTcSymPropBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymFuncBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymEnumBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymMetaclassBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymBifBase::write_to_obj_file(CVmFile *) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * Symbol file writers */ int CTcSymbolBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymObjBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymFuncBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymEnumBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymPropBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } int CTcSymMetaclassBase::write_to_sym_file(CVmFile *) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * build the dictionary */ void CTcSymObjBase::build_dictionary() { assert(FALSE); } �������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/vmglob.cpp���������������������������������������������������������������������0000644�0001750�0000144�00000003400�11745015123�015510� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmglob.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmglob.cpp - global definitions Function Defines the global variables. Notes Modified 11/28/98 MJRoberts - Creation */ /* actually define the variables (i.e., don't make them 'extern') */ #define VMGLOB_DECLARE /* include the globals header */ #include "t3std.h" #include "vmglob.h" /* and some other headers that have special global definitions */ #include "vmerr.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * In the VARS configuration, we need to provide storage for all of the * variables. */ #ifdef VMGLOB_VARS /* we need to include headers for objects we define in-line */ #include "vmrun.h" #include "vmstack.h" #include "vmpool.h" #include "vmparam.h" #include "vmpredef.h" #include "vminit.h" #include "vmtobj.h" /* remove the declaring macros for the globals */ #undef VM_GLOBAL_OBJDEF #undef VM_GLOBAL_PREOBJDEF #undef VM_GLOBAL_PRECOBJDEF #undef VM_GLOBAL_VARDEF #undef VM_GLOBAL_ARRAYDEF /* provide new defining macros for the globals */ #define VM_GLOBAL_OBJDEF(typ, var) typ *G_##var##_X; #define VM_GLOBAL_PREOBJDEF(typ, var) typ G_##var##_X; #define VM_GLOBAL_PRECOBJDEF(typ, var, ctor_args) typ G_##var##_X ctor_args; #define VM_GLOBAL_VARDEF(typ, var) typ G_##var##_X; #define VM_GLOBAL_ARRAYDEF(typ, var, eles) typ G_##var##_X[eles]; /* include the variable definitions */ #include "vmglobv.h" #endif /* VMGLOB_VARS */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/doc/���������������������������������������������������������������������������0000755�0001750�0000144�00000000000�12145614112�014264� 5����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/doc/libcover.jpg���������������������������������������������������������������0000644�0001750�0000144�00000021551�10476525716�016617� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿØÿà�JFIF��È�È��ÿÀ��´��ÿÛ�„�   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢���������� ���}�!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú������� ��w�!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ� ��?�ù¾¿R¹Á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¢á`¨¸Â‹€Qp5¼+­¿‡uë]V+;+׷݈/có"}ÊWæ\Œã9àV5é*ÔÜjý·vw=“âN¥‡¼ðûTÓ´]/u­:ãí² .26!ê6‘¹±ŒuúWƒƒ«^´%'hÉ[Wæi-g‚׿s#ß¼]ð÷Nƒö~²¼±µ„kúD‘Ë©H ÄØ“–Îù|Õë‘„¯ŸÃã¦ñî2~ì¶í§_¿gÈx’¬ÆAÏ#5ô1>‘Ö“JÓµ„Ÿh2Ûx‚ÊÔ“û634¢%.&UrÃi×ÍSu'CçwƒvÕô¿™³¶š=ñö^ø­èÚ[9±·‘ä’ªè®'“ØÉçŠörüD«áãR{¿ÑØÎqJV=oÂú&‰âèÏðþÓ÷šå†žËªèú•¢‹§Ú»¤I7Ý ó•Æ‘^µZ5åõ—%-z/+~f‰&´>s¯£¹ˆQp;o…'KM}e×îôkM69"ißP¶7 ê”6°Ët,GÊ9ö<8çQÓµ4Û×go½ÿ�W*¾¥Ï“Ãßu³¬´ëK¤K‹Hlœ4F"6†^2ÊÙ9ÆF Œ®º«†O™¶´wÞã©HóÚôn@Qp .š‹Œ3EÀ3EÀ3EÀë¼_ãËßxwBѯ4ý6ÞÛEŒÇhöÂPáHPCv;W¶x®L>4jN¤[n[ÞßäS•ÕŽkK»KBÞêKK{ʼnÃ.CäÇf A#ñ®™§(´½F´øÕâ(%×<ëM>òßXˆCqmv÷Æ©´© ¹\‚sÍyÒʨµ6œ^[üŠçg™»«LÎ#ERÙŒíÓ®qøæ½>– ô‰>0j7‡æ:çè6ßfÓ¤1ÎÆÚHfF ƒÏZóVYNÓ\îÒw{køÎÏ?Õõ+½cSºÔu;‡¸½¹É4¯Õ˜õ>ßAÀ¯Bœ#N*VH—©ÛZüXÖìî ¼³²Ò­õ8tѦ¥ôp¿˜"ƒ‚û7gÛsøq\2Ë©I8ɶœ¯o?ºÿ�‰\ìóìסr4\ ­ÿ�Cµ°¹‹VÑg¿ºvV†hïŒ"0:‚»à{ò:`óXÔYI8JËÒÿ�¨Õ‰üwâíCÆšû꺠‰F°Å K„Š5èª>¤Ÿ©?Jœ.z|ÝÎ{5ÑrC4\4\©¸Â‹€Qp .EÀ(¸{;›ûÈm,`’âæfÛQ©fcè�©”ÔW4ì@j®!UK($ã<R¸4Ý>óT¼ŽÓMµžîêC„Š˳}�©HÁsIÙÅcWqÖ™§Þj·±Ùé¶³ÝÝHp‘AvoÀTN¤`¹¤ì‡b©«¸‚‹€Qp .Qr‚‹€Qp .g¦?†¡ðÌMrÖϪ‹;²T¤…Œ®Ê‘ƒÆÜª†uÇ<ãòOÛ:šmuÛmßù2´°¾¹ðÕ­œM¯Ái=Ãjø™d!`Hܸm½˜²€�ê2x¥‰U¤ÿ�vôåòßþ�+u"ð^YžãÄ7VÊÔ1§ŽV-æHX� dàN À§‰•kZšèûoÑ [©'…u-%<{}¨Ü›{f/f :ÃŒ¬#(,w03òŠ+B~ÁAjô¿1­Æ‹Ÿ Iㆠ„hpÛP#ªÏ"AŒªçrïq“ÆìžôZ²£kû×ûµý´¹¦^iÒA¯O{¬Y„³Š4p›Ð`ÿ�p?Þ=NkIÆiÅGU}¯P:ïÞø[AðýÕÙ½õ¦ÓàÂMç%Û9P2�Pª§8çqÉ$ ¢¹+ƽJŠ6÷y¼­a«$Máoæx óX¾Ò¢½¹žÖYâi-Ĉ±í*‡{ˆUþbæ<.æ•jîu”#+$×õßË¢ê4´9ûK ÙøâÞÒï\¸óo=äó!9Âyjn�Ãgx$ä@ÁÞJ´«ïh¯OÇ_ÐZXÚð÷…tÝ]›ØZm1Úo5.ÙÊ€BªãÇ$6ŠÆ¼kÔ¨£ow›ÊÖd1¯Jä…�¢à\¢ã .EÀ(¸�¢à\‹€Qp .EÀp‘ÄF0íå“’¹ã>¸£KÜÑp .EÀ(¸�¢à7  …ð?IÑ<AãXôi†öÚæ]gxÚ#nüm#9ÛŽk‡0©R.znÍ~¥E+êpÚ”ð\ßM5¥¢YÛ»e Gg=2Ä“ø×dJÍÜG øjÃ÷üC¬^hK6¯¦O Ïö©U_Í-†*\vàñ\5gUbc-}B’V*üÑtøÂ=Ä6)<rÈÉg—9œíÇQU«R•.znÖòRoR߃4ß øæúÿ�IM]ñlæ¹¶»·»y#C–Ä«&~RPG?Zšõ+aÒŸ72ºM[¿k $ÎOÀ1X\øÃI´Õ¬VúÊîæ;y#2´d`»RF~•Ó‰rT¤âìÒ%-OJƒÃ~×>)ꞶЦÓYg¸¶µÔa½‘Ú7ˆ1Ý">AS°ç µÀë×§‡Ž!Êú&Õ»•e{,x$W­r,z·†þÁ«üÖüF²±×mH»‚Ù_þ]²³÷).ý3÷¯6®9Ã_eéóþ­÷–£¥Ï-¶tŠâ)%‰f3DÄ€àA ƒƒÓƒšô^«BO]ñ•§‚´-K¶ãÂRIo«é6·ó´Z„Þlm6r#É ãd×—BXŠ‘›çÖ2keЦ‘ÄøóAµð_ÄMKGãQ³°¹�,ŒWÍBb¸ àà‘ŽAé]˜zν=›BjÌí|oáÏ _ü$Ó¼[à#ìån<U^êI^Ñð0�'I=HÏ̽9®L=jÑĺ5¥}4Ñj6•®ŽWÆcAÓt]7K·ÐãƒÄ^Bɨ\ ™XBää Fb7mÁoBÄ`]IMÍËݾ›^‚v8šì¹6 .Qr¬\,zgìåËñgLHÔ³{ �ôï ® Íÿ�³¿Uù¡Çs…±Ñ/ïtÝOP‚ÝÍžšŠ÷2‘…MΨ£>¤°ãØžÕ×*±Œ”[ÕŠÇuáToøQ9m§oÛìyÇûMþ"¹*¿öº~Œkf;öoÞ>.iMoe‚è…+q'jY›ÿ�f~«óAÍ„^,»ÖüN¾›AÒ͆´­k{&d¶³ÇûÍæGŒ*õ ä0k<eŸµRwŽªîêÿ�1§Ðà¼+m¯ÄíÖÞaqZÄ1Ç*ô € >½k²´›¡&ÿ�•þD¥©îÚ~µ½ã¿‰~û%ŽŸ­]›ÅÓ/í-Ö äu,Lo mÀsÔõãÊ›§J•k·k§ªõ/«GÎ6“w«ëvzMœL×·S¬„`†'úc½{³«AÍì‘=ã῎<-¦üVŽÊÚPØÝÄš™fŒÛ´cj#ö7Éù¿«ÇÅa«KÌítù¼ËM\ò‰ž“Á¾9Õ´I¼¨&&o㉹Cïò‘ŸpkÓÂ×U©F~D5f{‚xºËÃ~3øgm­éºlšuφtô{¹­TÏl̬ÖR7(SŒàð3Þ¼—BUiUpnêoKèþEÞÍñ+H½Ð¼{®éÚœ³OuÛ“<Ç/2“•r{–ƽ|-XÔ£GkÖ§kû<êwvÚŸŠ,âÛ-œº%ÅÄHѤˆŠž ù×&e¸ÂO~eøîTO)¸š[›‰g¸‘åšV.îç%˜œ’OsšôU’²$ŽÂÁEÂÁQq…_Ãþ$Ö¼9$Òh:¥æžó�$kiJ8Î>¦³©JO\ z·üO«éÒØjzö£ue+’ gfG ä;ò©†”%ͤÇq¶3ñ.Ÿ£*Ë\Ô ÓJ²›hç`˜=F=òh–”¥Îâ®+x{ĺׇgÐuKÍ=æ�Hm¥(_Æqõ5U)S©ñ«ˆ»{ã¯^ÚÍosâRH'•>ÒÀH=ŸÆ¢8j1wQC¹“£jú†‰~·ºEäöWjY r¬ëÈ­gÍrÉ]¹}â­zÿ�X¶Õ¯5{Ùµ;ly7O12Gƒ‘†ëQ4ã´coâßÛk“ë6úÍüz´ë¶[µ˜‰qÁn§ ü¨t)8r8«v$ñ.´šëkIª]®¬ßzìH|ÃÆ>÷Ð ~ÆŸ'%´ì"mcÅÞ!Ö®m.5mfúò{Fßo$ÓhŽAÊ“Ó)B…(&£®15ÿ�øƒÄ6Ñ[ëšÅõü16äK‰K…8ÆF}‰¢ tÝá[[×µmtÛgQº¾6éåÂn%/±}{UBœ)ß‘Zâ'ðÿ�еïÇ2hZ½îž“È-å(û⦥u>8Üf]ÝÌ×—S\ÝJó\Låä‘ÎY˜œ’Mh’JÈD4îEÀ8©¸ÎûÃþðýÿ�Ãûïjæ¥nl'ŽÞâÚ9$%œ»ʹçø5ÇSR5U8Åj»ÿ�À‘Ÿâÿ�Û鑯èú™Ô´]M¤$’&XdB7#¦æ�óÁƒWGç9BJÍM[Á¾Ò¼1áÍvçÄ:ÃZëbcQi³Çå>ÇÝ›€:ôÆx늈b*Êr‚м|ûü‚ÈÌÕ4ZøG¶´ñ÷ºEýºJ÷QY¯›nÌ̦6ˆÉŒ‚£?7C‘ž3q­QÂMÆÍ>ÿ�­‚ÈÝ×¼àíÆóøkTñN­ÐL°Évt˜Ì(H$ùû¶ŒõÅeOZt½¤`¾ý ²)xwÁ:&«¢xŸP¸×ï!þÁ!¥X4ô˜OI±Yœ¼ç“œuàšª˜™ÆQŠÅçÿ��,ejº7†×­ªh¾ º¹½Žé ’ÂòÉmä*ÊÇÌ]²¾åq표dgHU«í9g-ºwýhoêž ð–™ákw^$Ö>Í® ¼•M&6h¼§ØåÇž?ˆñŒäVQÄÖ”åîù÷ù‘ƒã Åá?ÛXj7æçL¹‚;¸/­!Üe‚A•uFaÏ^ íÖµ£]Õƒ”V©ÚÞac{Å> ð§…¼Pš6­âX±Ž)x´d(‚E ÿ�HÝÀ#8Û5•,MZ°çŒßÿ��,Š^ð~©ø÷Rðõ߉^ Ho³êqZ¬È‘+»;~ðmS#¿­ULEHÒUuÓK÷ùŠ1ðlž ñ³èž#šdµV·vЇ2Â~숥€9ôÝÁgŠº8…Z—<7 j~ |@¹ð·†n¥Ôd·–Hd¸¹…mÕLdùŒ~fÂ�¤î$qÚ”q?ºUj+_æ%Ó¼7á+ÝZ=,xÂh®d-n¤Ó1h[ ýá“xRˆ ÷Å)V­órië¯åú…|=ðvŸâ‹ýZÆÿ�V¸Óîl-¥ºÌ6k::D2ã&E9ôíî(Äb%I)%{»omþ@‘_ûÂwÕnì<I|5+8ÖHí/tä„\åÕHFYŸ8Ç@} ÚÖSIÇGÙíø ²9+¦â5(õ¯�ϦÛüñdšÕ•Åí˜Õ,÷Cop bpÜî*ßËò¯>¿3ÄÕÙÙ¡/Çx­ìôÛøh"x2{&ºÓךÄyÞcw8;}�Î1ÅܦçñÞÏôO¬Þè6Ÿ~ÿ�ÂC¤^j*´ ýšø[í_´òQ·gNžüLGˆ«Èí·Kôõ‡žkw1ê>9»Ôí"’;íNI­Ë&Ü¡”‘íÀ#§Jì‚q¤¢÷Kôéÿ�µ¿ XüLñ¼Sè·£Vš Žóíã4@gÊØÈ8ÎãŒôôáÂB«£ KKío>ãfÂláð/ĉ5+Yn­…¾øb›ÊfýðÆkcœ‡¥k‹æuiò»;¿ÈHå|a¨èú­†ŒžÓ.ì °´d¹I¤óˆc3Æ@«w(è1œWEÎ.\î÷ —m/…?á]|$ƒÆ–7sØÎ÷èg‚ëÊXºÁf]¤°éÑ—�µÄý¯¶¬é=tü†qŸ×Q·ø©ÙjBšÇšÂ›cK` Ädÿ� äóšéÀ¸º)Ç®þ½DÎç⦷á3â,‡Tѯe¿<q‹¤¼XwµÚ„óœnïûûW.eGÝ–œÝ¼ûÿ�Àå>ŠHuû”š7ÿ�²uÃ. ”ġƻë4à¿Ä¿4#Ò|-"|[øv|+vê|a ÄeÒ&sƒu�ûГê�ÿ�€žÍ\UÙk{UðË'Ü{‹á¨ÍÇÄo‹Úu¿­åž§ ¢Œ&ÜѨîÅTþFŠŽÔhÉìœ <NÞ)n'Ž xÞY¥`ˆˆ2ÌÄà�;œ×¦ä’»ê?YxÏÄêVîˉ|·nØÄ*üËžpx#5ÁŽ|ÔãÊþÒs:î§¡ê°Ó¼;£ßYÝÁs5ÅÀšà\—M‰ó¸cdcŒg>›Â5#7)»«/ 9 ×EÀ*n3²Ò|{>›à˯ ¦‰¢ÍavÁç’hå2Èã;X°`Œñ€·Zæ–J¢©Ìî¿®ÀQ/½/…î­l®¬"§¶–dc5«67Ø0�r#“Wìcí=¢v˜—_f»Ð´ÍëÂÞ›OÓC HÞ óã–Ãy¹äòry¬–)9©;¿Oò7QñÌ÷úÞ6‹¢¤:T"k¡t·s>â¡òNæ$óÎsÎn4bâ¤õëÔ ~:ñ]ÇŒµÉ5kû [Ù×=š:‰O� ÍÎ8ÅU JŒySºGÂ?¹ð¾…¨éVº.‰wo¨€·my ŽÓ(9U8p�¦�¨«‡U$¤äÕ»©ãio<9s¢ÙèZ—is*K3ÙC ’B™Ú ;·$âœh%57&Úî¾$ñìú÷…´ímE¶³Ó·}í£”Iæ ø-!Îâ9È4©áÔ&æ¤î÷þ¬:×îµÄÑ?¶tÍ.òm*$'‘$qŒ*JUÆà=F¿Zp ¡ÍÊÚ¿õ ßÅ)õÝI5 [žº»DT=´ÜªŒ( Kƒ€1Íg "‚åŒÚ_/ò:×âä^(Õuû#G¼¾Ôãe¸ŠO.d1²¢«Œ·œñÓoÚH{IÖo4}z cHqeyo7•œFsÐdœŒqƒœŽ¹­¥Î<’Õ4þ#ÔßÅ“ø’ƒk«Kv÷žl.É‹{dž=)*qö~Íík¿ÄJßRmVÓKЭõ²KhÅbV Ÿ,7'™æ±xhµÊÛ·kÿ�L þ ñå÷„¯5 Ë];L¾¼¾GŽi¯£y£ýõÀp0{ñšªØxÕI6Ò]€‘üzé¦jVzw‡<=§øLÜZÁ'šHViÆ)}]s')7oë°etÜ¢ã .Yðÿ�Àz§/'³Ð§°§˜`¸›c2ñ’89#ó¬+â#E^[ŽjæÒâÚú[9¡uºŠCŘ88+\ñZ©&®ÿ�޼©ø&òÚÏ[’Ì^Í”ÛÃ.÷‰OMü`gêzÎxÕMÇ`±‰¤é÷:¶©g§XFe»»™a…ñ3�üÍi)¨ÅÉì€~»¥^hZÍî—©Åå^ÚJÑJ™Îp{zPšœT£³{À>Õ<u=Ŷ…=‡Û!]æÞyü·uàexÁ uïYÖÄFм¶ ºO†ïu'‡Ý ±Ôšco²ñŒae ÁÁÏZ¹UŒaϺ ^hÆ×Ä-¤É¨iìË ®’b`¹ßŽƒ¦qB©xóX /x.ÿ�ÁWÐYê×Z|·R “ʵ›Ì(¤¥¸È<TѯªñAbw³èÚ›ªK¨é—j�´ÛÎZB ’¸�‚½¬¥'=ÆV‹¦]k:½–™§ÇæÞ]̰ęÆYŽÐsÖ´œÔ"äö@.µ¦]躽晨Äa¼´•¡™3œ2œ{z!58©-˜¾ ðEÿ�ŒRøé—ºl/eO4wSùl"\n~‡å¬«b#JÜÉê9«¨„3B%ŽQ•ó#9WÁÆAô5²wW*w�¢à  ¿Ã ^çÿðëvG6pÉ8Ë}¶Øàû ±®|DTù`ú¿ÑŒôOŠú%¶›ã©> iê‹{a­hØá®ß ‹õÞD¤z®\5G*~Åî¾_Ö€qÿ�‰oélÄ–:-‘$÷ýЭð_ÿ�§Â{-JÜê¾$Òc‰¯´ÈÖ;#,ˆ‹ö‰3óØÄ‡ë¶ž&Qv„¶{úÙþÔ: ›QмikŽ rÕÊ«Ϊ8,8'nÜ5†_RÉÒ}kà[»>ÞYLð][éË$R¡Ã# «rük®²RqO¿èÀõùìíþ%?†~!h°¢kVZ…¬!´ˆw(YÀô síþé5¤èsQ–Í;>]ÇÌßïŸç^šzè¿´/ü”‰?ëÂÏÿ�D%ràŸî¾oó˜ñü‹ÿ�¯i¿ô|•´¿/_Ð ß…6z¯ö§‰4dˆßéȱYdDy |ä¶1'Е5ž&Qv„¶{Ø~Óúí[Eñ•¬";mvÕ Â«Ü*Œ‚GwŽêÕ†_SÝtŸGøÌü ÿ�‹ÿ�ìX¿ÿ�Ðk‹Ú?â@y­uÜV . .¦å­ð¾³áû ëz~­c©Íy¨ª"Ím:"ĨÁÇNrG>Þõ…HÍÍ8µ ç¯5_ x{Âú±‘´m&y$ýËbINFTýêʤw`Zø©â­#ÅÚ…î•a}e-½¤vŽ·,ŠËíR0:ÒÃÓ•4Ôõ¾¹®x~çÁ^¥Øêv÷v’¼òK,èÑÏ#… H �^xSNš¨äÚÔ“Nøááð”ø'VÒõ[¥ó¾ÓȹŒ}ž^>à+÷s»ƒÏÎÜÖR¡?míbП„5m MÓõ˜u›MFâkûo³+Z̈¨»ÑòC)É܃ÛÖÕ#94âö`ZøUãË)U³Cqlêbº´/µgŒöÏ8 ò?BjqUhr°9 _|®øÆæ&·¹Öü]¡ø¢ >oé:‡öµ¥¬v¯ucxˆ·(ƒ Y] ŽààúW<)N›jFÀåõÝQ5-£¶¶¶V‘y6ðïÞÊ»™‰fÀÜÅ™‰8p�� ÚåÝêÀÛ×µ¯\ø3KÒ4«NÞîÎY&’Y§FIžM¡‰Aà"ÏO©¬á©¹I­@鬾 øwþ ðN©¥j·AeûLW"æ0måêv ¿w;¸<áÏ5“¡?míbÐß |Y¢øJ]^m[N¿¾–þÊ[ A:Æ«€<©;¸ã·=*ñçRÊ.Öw®£¬ø^Ýiz‘~º…ÕÄrI}}:9X×?"Q€IúãéUÔsR“Ð:·¸�¨¸Â‹€Qp .EÀ(¸�¢à\‹€Qp .EÀ(¸�¢à\‹€T\aEÀ(¸�¢à\‹€Qp54¿êÚ¤&{ å· ´Ï·l`ú8P™TŒw`§‡õm*5ý„ñ[–Ú'Ûº6>ÆTþtF¤e³.ªà\‹€Qp .EÀ(¸�©¹AEÀ(¸�¢à\‹­á[8/u•K¤ó!Š î d%òâydsó Ç<ñQ94´ÖK¥C­Æž$¾Ôä¹Ñ­þYà™ /lH�+°° òtÏAYs¸û‰jsâ¦ðý¥¡Ò´­&Ö{ëFóâ—Q|Ä–Êõ ¸dg(s=^Ì,r~*³‚Ç[–+T1ÂñÅ2ÄI&/25r™<¥¶óϬ$ÚÔ š»Œ(¸�¢à\‹€Qp ‹Œ(¸�¢à\‹€Qp%´¹šÎê›Y^)âpñȇXr¤ìÕ˜ŽºïÄÚ~·smw«}ºÆöÜ©O²F“[1ÊÛ¹P½9¶Ÿ@8¬”t_×Ì,I¬ø²Îæh.¥zÕü 5b†ð�Ãw‘ÍŽ�!‡F i°Xã.®&»¹–âæW–y\¼’9Ë3’IõÍj¬•ÈéÜ‹€Qp .EÀ(¸*` ,‚€°P Á@X( ` ,‚€°P Á@X( ` ,%MÆ\‹kJGR¶£N¶&@'h1¼&y+ž3I·m�ê<_á­?úˆ¬ä{ÇkkˆãÓd »gF÷‚›Xc¦ážµœ*9$ÄË?€î^Ó ÕšÚ{V¾ŠÎú+[¥im]É\ àðÝ220H4:º;v ˜–^¼¼þÎòg´ÿ�O‚YáÝ!ê÷nSÇ òœùÕ:‰\.>ê’E7Ùby-õ#–áUšq¿“€�ùŽqÇ=ÑË6^Ö/µ£§Y‹{Œ[-ѹŠBñyM€2y;psž8¤ê¤®Âæ-ÞžÚF¿.Ÿ­Ã4mkpbºŽ27Œ6ÒxÏ\•JWWC-xÓJ·Ñ¼Q«iö-3ÚYÝËj3)v1¤œzžGÿ�Z”$ÜSb[¾xPø»VšÀ]-¬†û9aÄ×XDZb§ò4ªTäWìsp¬kr‹t$Xà Qóžpz»Œô|5] M3팑j_ÙäÜÉ ,‡,›‘¿v2 aÀë׃ŒiÖæß±*W<óS´{ JîÎUÛ%¼Ï ÙÁRAç¿JÙJêåiÜ‹€Qp ‹Œ(¸�¢àmkž$¿Ömôˆ®Ù�Òí–ÞEÃ0SÁcÜ…Ú¹ôE©ŒR½ºŠÇAiñK}a5¢i¢y.ÖòôÆÒ¯ÚåRJçç!WqÜU@û�:zZâå3ô¯:ÖÖ$Ñôùä´’f¶–f”´I Ã'Êà9 ‘žOàÜ/ÔvŠ£O#þ%È|­-ôÑûãÈmÙ~~vã§J|¾}BÅ?ÇcäÆšl2Z=le…ç‘|À®\8d*ÊÁ‰èqŽ1Í' õê9¯¶G&¬/.m’hŒÞcÛ™k ä®ìîÆ8ÎsïWÒÃ-x§W]{^Ô5O±Åi%íÃÜ:FìÀ3œdôÎOãô¥ʬ$¬nx[Ç·~±±‡F¶Ki¡»77 +»�ü¨Ã8� ŒO©©•5'¨8ܯâŸÉâ \Ï-Œ6úc]}©ôؤ(¹$¹;†â[¡ã<bœaÊ+§¡»Ôîïm|9§ÙËv·7å&™Æ$ù› ¥À'<qÅJ§ek‹”äõÝ@jºÕþ¡ä%¹»æ1#±$€O=ëHè¬REw�¢à\©P@��P@��P@��P@��PÿÙ�������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/doc/index.htm������������������������������������������������������������������0000644�0001750�0000144�00000017053�11756676456�016145� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <!--if !tads.org--> <title>TADS 3 Bookshelf

The Books


TADS 3 Quick Start Guide
by Eric Eve

This brief guide is probably the best place to start for newcomers to TADS 3. It contains instructions for installing the system and compiling a basic game, an explanation of the other documentation, including guidance on where to go next, and a sample game newcomers can experiment with if they want to dive straight it.


Getting Started in TADS 3
by Eric Eve

This tutorial introduction is a great starting point for anyone new to TADS 3 and new to programming. The book opens with the basics of getting TADS working on your system and setting up your first simple game, so even if you've never done any programming before, this will help you get going quickly. The tutorial then leads you step by step through the design of a full-scale example game, showing how to use TADS to implement the effects needed in the game. By the time you're done, you'll know your way around the system and you'll be able to write your own games.

This book is accompanied by a sample game, The Further Adventures of Heidi. The game's source files can be found here: Heidi.t Heidi.t3m

This book can also be viewed in a PDF version. This version is recommended if you want to make a printed copy.


Learning TADS 3
by Eric Eve

Part tutorial and part reference, Learning TADS 3 is a perfect place to start for confident beginners, and for people with a little more programming experience. This book is designed for learning TADS on your terms - it's organized by functional area, so once you've mastered the basics, you can skip around to focus on the areas you're most interested in. As you develop your game, you can return to the book as a reference, to review the details of particular TADS features.

This book refers to a collection of sample games, which are available as a separate download - click here to download.


TADS 3 Tour Guide
by Eric Eve

This is a comprehensive overview of the TADS 3 library, covering nearly all of the key classes and functions. It uses practical examples that illustrate how to use library features to accomplish common tasks.

This book can also be viewed in a PDF version. This version is recommended if you want to make a printed copy.


TADS 3 System Manual
by Michael Roberts

This book is a reference to the TADS 3 language and the T3 VM, including the standard intrinsic functions and classes. It covers the language and VM features in depth, so it's the place to go when you need to find the details on the system.


TADS 3 Technical Manual
edited by Michael Roberts

This is a collection of technical articles about key aspects of the TADS 3 system and the Adv3 library. These articles provide detailed, task-oriented information on topics of interest to most TADS 3 users.


TADS 3 Library Reference Manual

This is an extensively cross-referenced and hyperlinked compilation of information on the Adv3 and system libraries. The entire library is indexed here, with the latest information generated directly from the library source code.


Introduction to HTML TADS
by Michael Roberts

TADS 3 uses a variant of HTML as its formatting language. These notes explain how HTML fits into TADS, and how you can use HTML formatting codes for text effects, graphics, and sound, and how the TADS variant differs from standard HTML. (These are essentially just the original notes for HTML TADS 2, so they're a little rough, but they are updated with relevant changes for TADS 3. Note that this isn't an HTML tutorial - but the HTML in TADS is pretty close to standard HTML, so you can learn it from any of the many tutorials on the Web for ordinary HTML.)


Change Logs

TADS 3 System Change History. This is a chronological log of changes to the TADS 3 language, tools, and Virtual Machine.

Adv3 Library Change History. A chronological log of changes to Adv3, the standard TADS 3 Adventure Framework Library.

HTML TADS Interpreter and Workbench Change History. This is a log of changes to the HTML TADS user interface, which includes the HTML TADS Interpreter and the Workbench development environment on Windows.


Notes

These books are copyrighted by their respective authors. In general, they may be used and distributed without charge, subject to certain restrictions. Please see the individual books for full copyright and licensing information.

Some of these books are offered in the "PDF" format. To view PDF files, you need the Acrobat Reader software, which is available as a free download from Adobe.

frobtads-1.2.3/tads3/doc/nolibref.htm0000644000175000001440000000173010502532677016611 0ustar realncusers TADS 3 Library Reference Manual Not Installed

TADS 3 Library Reference Manual - Not Installed

If you're seeing this page, it probably means that you installed the version of the TADS 3 Author's Kit that doesn't include the Library Reference Manual. This page is currently attempting to redirect you to the on-line edition of the documentation on the tads.org Web site. If your browser doesn't automatically display the on-line documentation after a few moments, you might try navigating manually to this page:

http://www.tads.org/t3doc/doc/libref/index.htm frobtads-1.2.3/tads3/doc/title2.gif0000644000175000001440000001465310477040173016175 0ustar realncusersGIF89ajd‡@@BCDEEG H I JKLNNOQSTTUWW X!!Y""Z##Z$$[%%\&&]((_**`++`,,a--b..c//c00d11e22f44h66i77i88j99l<HÆ ß”ùS‰ˆ‚eBfBÌ@&4Ð(¬˜ÛRUZÕ‚¡ÅÙX«¸Ö+5!2e‘¡Ô·‚$7 0Y…2uÑ"+µH#0ð®x!ä]œÀ*ÖEÀ]kXÉôM‹~ £2 jFízÍî³­ÄôZ„ÉV âX…B`Ë*nê5™E+[M£ÿÜ”LBI`r[S­œÕh¹SÏRu¸a…ÈÄÕ¶BneZ,Ev[V¾k aÅÊ$Т(·°ìÞZ-±Pñ@”W1î´‚ËNöD½dEîC JR/Þ¡+ÊU|Ý{×úè,WßE…<Å¿-¢idùk:„EW°ÈÚ`ôØn €`ÒG&\á!\8Ã5ÚEE¢:@XIx°z’³ W!=r Dˆ+¢ NçÄÖpÅŠ+ßÏ2#^Ñâ@6v¿#ö&4@¦Ç"¤˜eJ@«ëBR’ N2C  ͯF¤DJ`”™ )/˜,#`s™Èl3¯øÿ½c%EĤPèZ™@' ‹ Y+±5¯(ÈR:M¨!x°\/ÐB¼9gOÚ$qÐ }ÊŒµ… ô:P¨¡›Àœ.¨]¸]Hâ!»}-SçæÈR$Õ¥þu ¢‡Ñ²HÁ)‘ ,%(œ™"ÂöõýæVdP\.]¤åÆJ7X´BÀÕ‰-3i. _D:†t³L¨B®’3³±h[µDä€_”– VnÑUÝã… ØG÷6$8p¦¢Ù-Éߢ  $»ÎÞ|è#ˆÛ!—@‚²õ4*+$ã'TÇâ<VpEÿ «)î(Ü ÞD •òˆH"ç>Sb¡…´[Vðþt¿‚Wæ:· U(Óã&qŠ –LNÈK[((¯&ªE$A†|»EXËöSU< ¢ûì¬V"ó@ÖÝö·Ç}î až?©ždའ:»×–*y„µ‘•z—î^‘à—8ô„œ¶¾²ÆÇtyCoBVÑR4a °…É l¹r–ilBn)Ò÷†¤½Ep‚Ç Â 4ÜžZ;¿„Ô|±à3„yz@íÌÀöìÅèÛö¸`xG!Ð Dõe5íè7ò˜|ÿ–/û¾’iß±$™€-b-w„"}B®J'k¡>&ï¤ß„{ €Ó *9çf-k€} ñ¿Ç"`A kPA`'×"C°{¡€fÒ€Ø7Hgr>ÇQ…G&PÍ× –U›Wvc\â#âW¼R-¢5æa >‡•wQ†5ùQ6p®Ç"°7xß}ýG~JÆd¿Dw!F]&M7=Õd¥•@ WM€c·ù×"@e8~½B:jz ÑENŠÓ ¢0A'˜‚!‡e"ux‡yHA²†‚Þç~aÛvTÿ†’yh²æ&y‰h‚30,¡ „àÝôW a ;P&$ÃØSc~ Ñ&VpA9Ã7~#8-2<3ñVy1ÝôrqBeâ‹ÇçyqVCÕIõF?;@m,q†,¢qÀh”—.ð€@…1 XE&-°€äD‹ç%tEÛè#Ýøsޝ3Žç^•Яw‡qT-⤠aàá—‰´ÕòØ  …‹ Aod’“ÍtrúÕY<öcd§‹1 ´˜Z a¿‡Ã(‘¹T¦€úX' ~8ÎÈ"-¹¸/ðUplYo`! G&.ÿ𭀑-2“5y“>’“í¨ˆQ úu´fqµ"à Q OüˆrHš×"…ªdÒ} ‘ HPMžsI”+i–q_˜§¢v],Ä‘d¹V• quÂÌg†ŽÂŽÇ†óò4:)FMió)c‚bn"˜(Ã+p˜LH”¤PŠ]ÉŒ ‘tÕ2–£ç SYsøó|,OOö{!äW¹"K—h9ðÅoùeåDÆA¶", #—q GÙ"l|0shÂ|€—Žâ“ÓH4ÐÒsÁ“B× — „-’jÁœ 眤ö˜P8¤àIdR{ÿY}3Ç%WvàO×"*0"á\„€Ùµ\-Ò^g?¶š‡›—7… A_£3U;†›Þ>’[Ñm_W&+€!1 Æz!Ç"Ýņ¶H9/¦#’Æ"—]­P¶ÙÖ9W¡]8¢ 1¢ã©š²mi’T ñô_”‰…cz Kª`W¾ôRKƒšÝÉ?$Tpi~d"Œéç™,"ŠyGŒ!Q¾…é§?ÉxhÖY':p¥€]lxGu`ˆ]yuÁ R›A‘d’•¡¦NÕ¦É"pJƒ‚dr t—¨‚Bd‚ÈšCª½Å"½iäå#‡ê‰ÿŠ•™š,F $¥ ±d&(G² 2Àp @O*©­ð£-r^‘*shŠÃù¦@ ¸"Q‘­)N´}p}!ª+¢EõÔ"6b¸ ºZ½Ê"¿ú¨P¸w&èòYÉxH”Ÿ ©©óšEZè#90c ±…£ <Ì£Ÿ6 —§F4db“ ªÅÊ"L€€sf‚R0Á y |,º«Ôz«ÛwìèNd¢B'‡ £°ÏI›£ÈZ¤É÷.I@¥a :p&!0%Ù°bŠŸfùN$© BXæÔFá ª« ˜æÚ" y²û:;z~XvÉÿHú«0õ˜<ÄØk'ZôÈ"i`8Ú" E»"G[IË"K«±kR‚dšŒx& @Y‹§(»3ëJ4ële9¨ë…² a++[M†å²h›Ã…a `Œìç[&G¦˜e\Ö"8[K ðjá­,Ò‘@¸®fŠ»"ŒKµ[ËÒêk&;W£ü¸±,Ê^àç(•¶Ô*®o©¶ 1?mëŸp{­Ÿd²­±·µ«0á`P`‘…ANdÒ®·Å˜KêeŒfi*zd24ÙOÿ„&z™®/© »ºÉ’VÁëºT[WÓËsk½lÿÚºžnJ&‡´¡C&P2Ѩ-R¶â…º!¥-òªÔÛ½Dº.êµ3È»›-2+«@ÀY&°k¶a»N¡K(,S²ò«S|–I[a@9ü7ipC{›¬Ö d´¬°Ýô³-Q¨†j»@‹\‘»"Z¸$Zº¹yØ+2ÀQÃ7xß{QFY&œiP­ú)|:”»½Á ÔÅ~“8Bª½U<¾-üÁø6[,"°ÑT¸7˜S‰ÅÑ +] Ë–ê#¦“e²¼kVµ;d£>2ºÑ´`ÇÇ-¢ÇÙ *AL&C\¬@fÿÊ\zàÁK|Ål“oª[gkÅgùÈQ®7øƒ1R3Å­ÙkÉ1›"|Ÿ$LŸìéž/³p ¥¬lW襤ŒZ^,b°±lî ð.…ì#‡\©@Àç–œ<È{¿qûNǚŘlºí‡É}zµk(cëUŽ Í1XhæÑ`i&ç; OÀ‹E-2×1ßõ?C&ú­Û»­¼¾L’ ¡ ‡€Þû½ ý#Ðí¤Þ kΊ0Üž¶d=žÜ"7 à¡É(¤ [Ù£({(ÓÌÿ.°Ñ Ñ ‰iÚ» ­0É–µ´äLâÞ¹{8¤…’âñŽ®½¤Œ D^ÛÕÝ?•[qI\¤°Mðµ©6ØÒ+¼¥½"Ž(6iŽ×:nÐc~`¾Q×+Òá”}ÔÙC j pö-;pÞáÂG£“-,B¢“ Çp0uÚáÂ¥aÿèØ™×Ølß¼à žÜ¾Bncµ^òäj0ÚV©8ÈàæXÙ×å– Ö6ƒ \º‰=Õ¥*Ý,BQÁ,Ò©mŽYª@ œÀ€E@9·"÷f¤t<0$ 1hHPòº»‚kS @ þ‡½"Zeèq ¤@ o0lf‚xéG5iò[Ý h$-‚hZî —èžErÀî,âî«J˜þ:šÞ €L:^©Àçã=êĽIïm‚CPã% CÙ½|Ín>ÒÖM~§ñ½õÈ=¼òGb"p „V³‘¢€ £À Y·u­c5"ÿ>Ö Ø%ÀC`@u?âÇ”¼iò"Á>*~‡‘i&°ˆ0 a‚>ˆ`ï%>Ï[’I&KßôO? Q?õ¯Sõb.¦û¾|vvÎ çj€ ÔþBzøÒ/ñª”Úf"ˆ€` ¤ð¬@ –Pfðíc¢ŽÐŸIÕ,bÍ¡º-âãm½ †q’Ð$À¬×$¯„(šÜϲvg½}¸’ Ú…/p9 `¯jgߣ& `ú¨¯ú,"žŸï 1öHl`fhpó<`<â㩟å‹í×c"pú€Þþc«pÀ@‚ ,8¤ÕB†­†LÀ¨áÄ… F¤˜QãFŽ -^”ØQc˜ Ü™Rã(?t¢øáLš%@yÄJ%EMOÔ:ðE¥E+¥™` )£MÊD ”€‹@:UV¢! *BfRŪu«Á®_EBŘP„ ̬ZHJÄX‚C9¢ ™¯HVØÒ5Y%ãÞ»ó:E|Ô°SQ&²•8¥š’ĉ ¸f€6¼ä”Üj4 r)tG2Ð 0bˆÜÕ*¡j8UNd£¢¬H<•íÞ¿ƒ»n{±Êµmß¶RUæCe¤b- r'a‘†xЬ›÷Dî˯Æ^ýzöíÝ¿‡_þ|úõíßÇŸ_ÿ~þýýÿ0@$°@D0Ad°A„0B '¤°B /Ä0C 7ä°C?1DG$‘¿€;frobtads-1.2.3/tads3/doc/techcover.jpg0000644000175000001440000004341010476357525016773 0ustar realncusersÿØÿàJFIFHHÿÀ´¢ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?×»r‘}­‹,+’ÙÉU{žAä8ŠòT»9–‹úþ´èS¶™–îYfÚ’¨ýØ$¨qŒöà7VïW%ud\µI/ëúÓ¡bÎ×lP³Û«6ò.$2m(¹íÈÂÝÏ_¥L¬ô É;«ÿ_ÕºJëoHÙKõr0CpöÈ=ÏÞ¡&(+«µý_‘FEòâOq€£pzzg?'cÒµI5ä>-^Ëúÿ2Å”Kav—*¬A+¹~÷`@àŽJuSS);Y99«_ÖýFÝ\>µªÁ (Š"v"1ÀœcÝGÝíM.XÝGØÁÉêÿ¯ø=M­qaÃd‘¨ ¡²G1¯n;}äw·5”[Zœô®Ÿ3þ¿­zœÕÌ¢âB@Ú ŸÝ†=²;v®ªqêwÓVª5à •ùcûÇÛž›è:Q7ÌùPTw—*aåÚ<Í0ÜÝÊ©è3Ï÷³Û§¥GÅ;ôFw¼¼‘>ƒwko$‹qbë´0Øsëýïnž•hËtN"jëúüˆõxm呦¶eò]¸@9_Ôãïã§á&•ºŽ„¤—,·þ½âg··8yrg <ßûÄu8£âzô %)z¼¿:V’õK)ÞÝöŽÙ™Inè;ã9%{žõfŽËúþ¼É­¢2³LÃaFÜ/NÞP8=ý)Îv\¨%$•»_æ>y¼«–ŽP¹“iÚFz8è Œ`ô©ŠºÔ˜Æêíÿ_ÕÈ<Èœ™­"S(ä&0A=Î:nsWm,Í--¤>EžEû,K#ÎT3îŽÃýåíÛšÎ)^ì„¢½ç·õýnfC—3¤ ¸vü‡¹ôëéÛšêæQÎ¥%ßúþ¾gŸxÊi—W½ì‹§‹†HWx!A$—·ÕúŸ ÒÈqôá‡T“ª©§-K'¯©ÓS[ÂxŠöínµK‰.mfƒz áÎ[qÁ5âqD²ZT¥‡ÁC–¤jYèÖ×O_[´UÌkZ¤úƧ‘s"ÚÚE™¯¡9Æ ôïd™[C‡ž:šs«-/æ›Kî_{Ûx&óûoG¶¸™‰d\LØÎÒ¼ïŽ =«óÞ'ËÖY˜T£dÝãèõü6ù RäZnp¶ú—Š5~ò=6öW’rŠì1†<z“_¤VËø/ÀÑ«Œ¤’”V©=]¯Ð9cØÕÑ|Q¬Ùk0èÞ% ‰PJÛC&IÁÜ8ÆXç¼lÓ†2Ì^Y†PþݵiÛ}©Û_ÓQY|H“Åþ%Õ×Ä«¤hs-¦ÕTªÌHÉË·O¼{ÔðÇåÏ,y–:.woM]’vÙnôû¾ñF «³wÂÏâ(Rá<@ë¢OÝÊ7?9$²à0ã××­|ï<ÎÊöq×{.ÊÏTûôبò5n†ç”¨§íLì0DêÞ…‰ýÏzù»ÞÌ.Ÿõýw<ËR½ñ&//C¥ÚEóE€.ñØ>c€+õl‡á*XXCÛÎ[µwo7®ŠåhÎËÁš­Þ¥¡G¨_…ûHo*"(} À8Âöë_Å^˜Ê†û¶NÛÚý/ø‘Ë­-÷)òÇØ8^ãþù¯ÈÖË©µtÒ-³:óÏB;o?Ãü<ö¨æv¹œdÒ¿Wý~Ï8ø©o,~µšA„–åJ‚ÜýÖ9Ær:žÃ¥}ï‡<Òõíþq:#$äÑ¥¦_®•àXoò»á²]€à†b¸äÿxŽƒ¥yؼ±ÜA<2ûUŸÝw¹ •Û<ßÚÌút7çû8^›ÀUäpN2=¿Ú?¥~«œå4q•(Þ·³öNé+o¥¾ëhT£~¦ßà Aâš÷I“åbPäÈaÓŽþ;_7â]´©cã¯.Ñê¿Ìi&Ó+xO[³Ð¼E«M¨Úû‘B.I;ùÜf»³ü—šå˜jXk]$ÝݾÉ5jÈ}Õìž4ñ}“[C(†"Yä(mÌÇ“nk,6 <3“Vx™§)'d¶½¬’ïç¦Þ€íÙþ,Õ|=uâí5û+Ð_´ÁÃîà°Æy9"¼~Êóª|1}hÚm¾Imn×½¬þdB-GÝ+ü1º˜^ê+Îú|kû“'T9ãØ»^µ¯áè¬= “IVo[uV×Í¥+ZåÉ^Èõ½7–Né$rãi‚UÚÀ÷ÁãÑûž¹ï_–IÝèsÕ|Îÿwõ÷t<†ûZ±×¡¾²ñÿeÉhÛ£Ž,’ÎR9NµúÆ$Ååib²‡í£QYÞÖIÙ§¹Ð®¼Ëß .o.t‰¢¸’F†' cœqÈôÎß^µåñþ G ÒIJQnVõÑ¿7¯­ŠÑjv©èP"‰¼¼|¼¿†¿>çVdç¯úûÍ).ÔÝϻ˒TC弘eÞϯdÀäu÷¨Œ-cMÙ.ÿ×ê´À¡·xDP#gaÔœõÚ;ÿ=ª£}E«Ùÿ_ÕûæXák.T”ãq“ß‚½ÏOZ»Ýš7wý_€¹št·h’LÌqŸÀã?|w#ZVM‰Å9Y±"ºIyR0À;¶Ž}wC¸qDâÐå–¥«½VÃC°Š}Zt†)ÛhÜ„$qÀÆ8br2ßZèÀåx¬ÆnžÍ+ÛM¾g3‹“÷uþ¾}¿TñLJ® 6ñj¡`oœ€ÁçƒÀï݇zõáÂÌ]ýƒûãþeÒ§(»Ûúþ‘fÖU:`t9%^!ê?½Ž?¾{vö¯µ9S¨éÍY§gꎉnh2JËqöŠÚIÃȾŸ.Xmö܇·µbû™6’O­¿¯êâMâ2ÿÚ¶Êö°cɶ/ÑSønc÷{sÒº0xìN_SÚagË&­/é"];(õþ¿­LO>¡éu®¯m+à  ª¸ê3Ç%»˜í^ïá³\ËSƒšU­¿ï_Éî8Jr“åþ¿­:—ôØa†-`µT´€0väòG~§wojñqØšõëJ¦"\Ó{¿M?ÈÑÙ+õ×ùði6ë-¨½¨ûc9yf\ƒÏ ë׿«ªYÎ5`þ¨ê?gk[OT[|±Ð­áÍ®™›NƒÌ,ZV#$óÜ÷Ýé]tø“5…5]Ù+-¿È!Zïúü‹è4ŸÁm–ÒÕ'| »Wst=3Óæî:Šá”3Ös¨ùª8+½Ý‘”Ó“×õе¨èún¦©&¡eæ1´;‘»¨OÍÔûÖ8ߺÃUqO¢Ûîûº›þ¿¯N„ún—o¥ÈÆ,mP.HcwÎO¯Õ¸®|^.¾*\õ¦ç'Õ»•)>[-_õÿ¡zõÌÒ“:#›æ3÷ñÛ>¿+uoâ®uu;Z:uþ¿Ë¡©YhF{›ÝkL†îhËI)_™¶ŒœWwÝ`2OZö²¬Ã3„¡…ÂVqç’I_DÛ··ÖÈ´¥Ëî½_õýhMá}bÇ[…¦Ó¬Yt«VX‚$I.b01œãÔÕgùn//Ärc%ÍRJ÷»{¶·~…8ÙZú³ ûUŒ"j2ª/ÿ~«Áö)ëý~dr7­¿¯¼«pÉ"Ê…íÄ^^"M¤28Ç|7]ž£ïzÕ­ ^î¶×úÿ2½•›Å.ö œvA9 Ùÿu¾ðª”î­Üª•.¬ºÿ_Ö‚^Í"Ê Î¶áÏúÃÃ6z’G8ù½{SŠÐTÖÿ¯øŠ*ñä—’wy?tõÏmÇ×¥^¦®÷гk¦ý®åq*à îq´¯Ôgï„ôü*eW¸§YEký~dž(Ò,õkh­üGiåóæG5¸ÚŽÇ=†ÞÎ}zW£•æøœ²£«„vmYÝ_Oé-ïëóñ&·<70j:i³…mË«Ižƒ¨ù¹Ž îx®¬ï%˨(ýGí$åÊÖŸ~žzmóŒ[¹Ïhþ0Ônng“NÑå¹²‹ýdˆÄ8së€xç­{˜¾ ÁahÅbñJe²{_ó·™U[W ³ñ-Æ·¢k°ËPÇ»S9l«u$ÿ³úÕã8j†QÁNœÜœªÆ÷·G´ó)G[›?î#µð­ÔË,°]-ãìté Ø‡iäw¿ÅÒ¸üEWÍ)®žÉéR1©)[tv us´gHŠC¿å·ÍïÒ¾ÑþbyaüÿŠ&o*y¦ 2MSƒ•|€ÎpÐzŽ­VÛ¡ëÑ(ÄPîgÈUÁläúàXûßÂi-Y“nRõþ¿NÆT uæ•ÎÕûÎßÝ^ýÿÚ=ûV²÷#c¦_»¿¯ëä[‘Ìr¾Ö1ø÷„¹‚Ür7ç¥Jؘ꿯ëð,X›nd‚d»Œå¥ŠBU×^:nn„ôÍ &EKIÚH¯:ÝC!Û$q.6– Ç’ã?yùç¥J4£¦ÿ×ü1æÞÒoì|M©Ï}o$Σ1ÆàÏŸ_LšýˆóL&+)ÃQ£5)G–ëµ£oÌêïxr[­m5; _>&P&¶óv1!pH<{þ]ê¸o‰hPÀ<yû6›å—/2ÕßUýoÒÄ'Ñ“xwÃLÚ´7:†%¼`:É-îù2N¾pÃzÔçœõ»µñmt ­C~èñ"—]ÁÈççû¤t}h›q ÉÅïQ:ÜÂÖ÷hKy‘|Êûp2O?Ý>Ÿ{Ö²Z™A½÷_×§2¼‡Ëc‚m§×¿M¾£–çšI·°Óæz_—r;UþÐÖDS() õ;¶ûóŒíõkYi Ÿîáîõ%ºÒã³ÕÛHgà•7Í“œ÷ Ÿ½ÐóRçufѸZ]¯×±Zì$Û1ç‡ÀRqÇ©'¦vŽ­Þ®>ê×b ùWõý~øŽáô/^Ko–º„01Šx›ä“ NsŽyUêOZìÊ0ðÄã¨Ò¨¯TŠkÉ´¿R"œå®ÝŽwáž­{ªxzyïeW™nZ4`6!äŒdç“üUíñ¶W…Ëq𣆢é¦õo[Éu¿`¬—7õý~]‰åŽâÐ"ð¡˜gÿ-|w%õkúûÌ}šz´ÿ¯˜ÙÝÜÊGÊËß8ûÌO# ­£vËW”¬¿¯êÆE«¤·ÁîTº³eˆìO¾}Ûø‡JÞZ+#ª^ìl¿¯ëÐн‘¢¶·ïeÎ[¸SÔçÜ—ïÐZšhΜS~Kúÿ.„Z}óXZ…¸e†Fæ6ÀÉÏâýø¢~ô¬‡´Ÿ»ºþ¿Ë °ºK,Ç-Mƒ±˜®9$Gû}ÏZKD;réý[ÞÚiõFkŒÏ.ãî;s÷ºýjÛ´t-Íri¿õÿŠíŒ“6pªÐ: ¾™þ/^´£e S²_×õØ–qö8£‹e<¹aŽ=0qèÇ¡ëN+ß¡0½FßOëþçѤ¾Ñ¬äcç͹ß# f 3Ç\†?~¡À¥†Äâº+/ü6ÿ4ZiÞÛ#S{­)µo D¥„÷‰°y¸÷ù;v¯¢ËãC>uQë .þ½~ï{ï´š™³á‹Ѿ%ÝinÀƪñœPãƒÔ)íÞ¼^!®ó†1ïtÿÔ%/vä0Gyâÿj^ß\'Ù˘áˆòœ|  p9$SZâjÑÈ2j50ôTÜÒ»kMUîýzÒW-x&&‡â]͹‘µXãI0.œ`óÆ:VE(Õášu#OÙÝÅÛµßåÛÈ™ëìPðn—>½¨ë+{q ©;œEË9¶Ž‡Ž¤þÙĹ•,³…ĪJsµ•öI¥žŠÏ¦£œ”U΋àî¥ylÚ½´Ò­Åµ®ÖJ §;x8ÉP;~uâxƒ„¡Ë‡ÅSœ¯=WôÔάSkÌÍøM+%þ²­6ðdà=Ç€3ÇZèñ“,¡¦ÞÞj64¬ 5Yt¹5«ØãóÌh¬¡Æy˜~ ¢µãÜ4*ÏFNד_úJH)+|g/Н®õ S^º‚ö6ÊË¿$`’X€@®ž%ÇRÉ¡KC S’Öë~–VëÖú²šQŽˆƒÁYm3ÅŽáî Ù8ÞÊ2¯ógò­¸ž)bòîUoß->qÐ$öGSð†amá;¹ÏÞŒ#ê2ÛñÐí=kä¼F‡>kMÓ¥ÿ¥HΤ9§o/ëõèu e¨HDz7*ØÎG¯JømMirýµ%£dºµÃÏ9É,Þ>ç¯<÷-Üt¥JDч*¿õýlIdèñ"¤[W÷ò`€9¹ëó÷䵓¿õýv!,×—M,‡j¹Êÿ ޏÿÇ»ÐÚŠ°åîFËúþ´è%±ŽïQCrÞ\åÊñµ\ûë¿z|¼±ó^Î[ÿ_ð:Wb+ˆîïgZƾLA]Ì9<ñèÿÄzÖ{;/vÑõýiÐl¯mcT¸Ág;±†RO ž3÷_¹ªm\™N éýZnÌvàÈU¤äe{¤gpÞ½h†¬Ö:«­Æû†’\§sç‚øíÛ“ƒØõ­dùcd\¥ËTgjþ²ÕuûmbâæD–Ø¡6Í´´“è3’AÜW·€âzW¡—ñÎcƒ¤¨Z3IYs'u÷5þ~cç|ÖGIáý ÓI†[ *ÌÏ>er2î0IEÆ09÷¯ 6ÎqYom‰–¶Ñ-ôþ›3›i^NÆUÏÃMïÒîÊK‰-äbE¹|(98Lmþ yí^Ü|AÍ#‡ö/–é[šÞ÷çkùØŸ¬5¤¿¯ëÐM?ÁZj^ÛY½ÔñêHaóŒãÎO'¸¬1ü]ŠÇÔ¡V¤bs-õjÛëäZªÞ¯K_çгáÿÅ¡ÙÏ ¬Ó\ØÇ+Iæ>0X…qŒò¡®ë;­›WzÑQ—/.—Ù6úß»-¶ôêÿ¯óèZ2ê wGpʇ•Ží^W²§Õû:Kt1äóœ—ù°rÄó__ö»Š½íÊ´lZIð c’äÁÝëÛz–´¹-Y_×by¤–[ré—j[nåLÇ«~ ü]êRßr,“óþ¿àt¢˜-+ívÏ@=ûtâ¦Ûná+·ëúÛ¡bÖU[Åt! µÀéæmÉ>HnýêdšVîL£îÛ«6muf”¾èî ‚dû ç!¸ô dž½ë&ºœîÿ¯ën…KË(®¯Í¼ecòP™çr@û¼íþéîzúUBN1»4§7ó?ëóîUx[Iµg$ç$+à®æûtÚ}yj¤ý£¹p~ÚH©op’J«z€zH¿+qê8|¾„óTÖ†’…•âY"8ä2 4Pac^Ì~‡åNNÓ×ÐÖJïæcµn¯úþµ*ZÜH·n–îYÓb/$cžŸðÛ½oRŒ£ÚÑíæk4¹mý_3¦†êÚKFU’;›T(N ÷åG#î¯!qóq\ªþ¿¯3’Qiݯëú}ÎjñI¸1ÇóŒþí·dvõô^ÝëxÅ¥{_Õþã²)\·j^9RÒªP}Ãv:}qÑ9Àõ¬§ =Zòþ¿‰=_õùy»ý¦áF¤¬eÏ@1_EèZQ„¹tß©IrÇúþ»”ïï"wRŽ¢0BD ÏÓד…ôë]4pÕì¶W©¥:n*ìÜÓ&ˆ.öv†õU™NðHPUHçŸõ}HëšÆµ*‘µÓ³W^šÿ“9ê½|¿¯O1f¿†U&o5%ûŽ¿0Á\©'œnÂs¸qY¬%e)Fq³ŽééÖÖûïÐÆ{?ëú× –ÚÓZ&þÜ”‘¾IdüÂOp~Oâ*áBR½º/M¥M5î¿ëú¿B•ô‘†0Z„HNKl Œžœû|ûUA?ŠF´ïkËëþB Žw£ÈFå~•®†—ŠÑ“i6Ÿi‘îÕ®¢„âD ƒÐä÷ôoJŠ“km^v\©Ù¿ëü‰.¢Ž ižÍã0£¯ÏäžxûïqS 7¹rnÒÿ‡4cHï ¿èrƒ&á÷Øg#¿]¯Ž{ûÔJëÕ™ZQMõoúý:ÛX³ûJ,Èû•Tã ¹è}r}îãÖ›¨–ƒT¥Ëý_"•­«ÛÝL÷HSfìí8à}ãœàô#¯ñU¹hjäœtþ¿¯BF¸+pHVBLpÞƒu‰Ã´coÞüW¿Ÿšî÷¹-É+ÿ_§™COð¬éúl’2[ɘU±’p£œá n˜äî¯cÖˆÿ3*œnÜäHº„¶V©e2¬êW;$w¦ßOî§cŒâ²³näYÉßúþ·êAmlÞDj~ú¶èÐðrÀ1Üp„ñÀ49ký]Å)ëý]ú“ˆKÏ ¥¨òaT@A#¶~C÷xâ©>¬/£“þ¿­zkSÙÝ´7Ç¿1ñÀçS°«ƒ¾…ÒŸ2³_×õ~£u?îÓše¶â%ÎF6ªŽçvB€ € fº0x'‰ÄÆ‚væ~¦íôó'ØòKW§õÿÛÅM¦5Ê(˜Â‘#•;~]áXpI FôçŽTã¥{”øVµG¦½ç%×춯¶»?K«ï¥òÆ[­M[èW6šº}˜Çö¼ÌC7 €‚>^ãOJâ©‘b(ªRW«++z¥øÞþž{CŒ®µþ¿«˜¿ð”ÚyÏrŒ·¦&™;ö.ÖeË çåPØã¥w¾Å9ÍS’””oµÝâ¶{jíwêW¼Ó] fñu‘H|íÑùûB[È<ãqõþøæ¸['/dÔ”lï}5Šm|®—Ïî˜Si—´R ˆîåƒÌYbr›™çH>ƒ?Þí^Vc—ÕÃ{>{ZJú?Áù—5v£ý_"ÚiW¨”\Ú¨nÚ×#<àó\WK@ö‘Ž…›†’âÕ­ìîd†^Yþéç”úuÅZÜPŽ·hÈN¼”<÷éùãîŸNµ³W5jäÊѾå!ŠŽã9Ççýß^õœ‘-´.`wŽ\àcòàzÓ•þ÷zH/ßúþ½ ÒáìÙ-“ÇΧï óùíúb•“½ÉiNüß×õ~Æ„”jˆ’”Š<°WPNð:ðyØ |ÝϵCƒlÆtœ¥§õý_±\±0Ë©8"{‚#´ @ž3÷W¹Î}éËWÉ÷”þ%O¢ßúÿ€G,ÙÝGmæIó$pHöÏÈ Üç4Û¾å)s{Ïúþµ"³Ú&…¢Ú9D'ŽOÜ=³È__N”4ö}G¾ÿ׿Cf{çcà¬{uÆy ž¾•R×CJ’ºQ_×õ¨¶Q´·8ë‘Âô$ôQŽ;ììjj>TMY(Gúþ»š7 þG•oó|¸QœdgŽIØ€k(-oý[œôÖ·×õ¯R¹’O¶y–ÌÖ—d€±È»TàŒN©Æ8Ål­ö¶7I[ÞÛúÿƒÔšïQ¹ÔbK7ADs)àÓ+Èe‡Œ #U̵&1ä\Ûÿ_ðýI,tùµky2}ÛaÆà#rGÞNݹ¡U7Ífº¢jMÅkýZõLÚQÅs ;\†Eùðü Êzº(ö•½Ìþÿ¿ï%«Bëúü†_¦Ÿy~MªFÛ—.¡sæ1û£øºn\ç óŠNïM¼½ã¯Ë°Æ´¶‘hae+ÉTjÝñ’àG éBÄÖNêo~ïév¿¯øn߉NTŠiŒ‚(÷êdäsÏMÞ£¥kµ#^g÷ÿ]â¹U„BˆÀ”£Ĩägܼ{ö©”§-ÝÅgoëúü ±Á?ïe¸A$Ÿ3À žN~jÏšKK ­2žY&™åwf”òOSÇç»íÖ´ÛcT­°Ž¼åy Ó¹ãóþï·ZÑ2®Oh€æV<É ?<´wk&ù¤fÝ݆AûɼÇ-"¦Ic“œtç®ÑÜuª’²°çGÔ¹h†M×,ͼŒ+uÚ£¸'è |Ýë)i¡”½ßëúß¡CQa#™T `ä: ÷è½ëjq²7¥·þ¿¯BK¹²1^D»ÕP±ã·¸ô^üÒŸ¼ì*‰MÙÿ_Ö¥”Ô4ÙRµ]7ï$'vã9Âw9äRq×Q({Ö×õ©Q€¶³‘$ä0£¡íœü¾µ)¹H/Í-6_×ù–e&KY–ÈRVXð1œá}»ìõéŠ/¨›W×úþµ,ÚÄa¶|„ç½#§#1Áæ°›»þ¿®ç<åÍ/ëúîEªË±VÞ<¡8,£Œtvè ‡k¢”4»:(Óêÿ¯ë^£,ÃÜH&_–%8688öÊ“ÇjšÒ¶ˆU¥oëúóêMp»U*eGpйèH=O̧îñŽk8I²)¶õþ¿­;Kû¨¼¯*çíø0ã!}0§8Æáü#¥+j*úÿ_רۋ‰nLÏsqÍ/ OUSÐrN1¸vJ¾@ååz_ש_OŽ'i rûWæ!I àyù±Û¦zQSDUVú_—b[¹7å[’H';O§~6;r(§iǯõúvgºI9Q׿^1žq÷qÒ´œ”tEÎJ:")Ë9ýõÊ® ÊGóc9àc#ø›ø»Rˆãý_ðšá~Ôç?x\`|n«å z}ßðB¥Ø¢œd½qÏÐviX±mÔ;6âY#=útõÇÝj$ú'a.$iX·R1´g8 Ï?Ý_Nµp‡©bâ1iÖ³¶ÛÐõç²§qÔÒr¿¼D¥wÌÿ¯ëR[·T@±ðH=J¯ðŒó×O<“íJ*ìPWzÿ_Ö½ ã™Xycs9ëéÏýóÞ¶z#vìµþ¿­In¥ÛHIòÔpÃø¯¾~^ýª"º²`º¿ëúסoN¶Ùjfòa¼†_¼;ã=¦z§óQRZ™ÔݯgýÁèCw7ZŽm¼†änv€vÏðþ”'ËO’:ÿ_Ö¦‹©ýÚFV%=ó´) Ït2zóÅaªZ˜]¥®¿×ÏÌ%u‚6•T¢mŒýÅíœRx<€(§'¨á÷þ»÷ó0Ài怆ŒœžœqýáØô®·.Tuó(ÇÐÙŒ$0‘(ùW¡ ÷ÁîKáè¼×›oúþ¿ŠMÉÿ_×NýH% upÐJík–m£Œ’;˜ÜÝþÑ{«CXûŠëúþ­Ü‹O·.X-ç¡ãœ}ì;QSKX*ͧ§õý[¹fòÆ[KS)ŠÆ0»|ÂÇ\çç?Â>ïÒœg­‡ §ý]€G8!V0Ù HÏÝÆ7Ð}Ú¼ô­²_×àR›‰@bqÐäçëëýãéÒ¶²‚5²‚4•c!˜€ î#’¤ðÞ½0Ž~•ÏvÝÎkëýÀò1”‰äù"³Á?1Ï?^ùºÖÿ ¹Ò×*4‰îü냷Œ¨RЙ+›}¯Èæå¾º_"ƒã$›o@9úzú/aÖºž‡[v,H‚(ÖœrÀsôýöëYE]ó2#®¬Š#±Œ£g޼ÿ¯¢žÕ£WЩmË÷ÿ_yfÙDq3‘¹°‰çýÑß©zŽ*[2–¯úþ»•n_{y‰cщÏÓžØïW à’Vþ¿­Â0aˆÌîq¶2zàŽ¹üS¿<ÐýçaKÞvéýÁè.ÚîTT2ñž=W¿jS•´ “åŽß×õsZãlX¸(ö¸ÊKù$ïÛûÉÜ犕÷9£+é¿õÿЫm4†A"HÈÙc¹= ?ËžJ÷=)»u.VÙÿ_Ÿ™a8@wË‚7|¤ƒ÷AèK)=xZÎZ½LÚ»×úüÿMÈoåm’6UË‚Pãi9þ#Ó©a޼.+Jf´ÕÝÿ¯Ôn—i¹†ì¯œ8ÛÁ Ð8çæpzâ”æEI­û_¡vïNid–v ̲”A,cã qü=¹¤µ"2³»_×ô»”nç“ÊHÄ(î ¼pžƒÁðöæˆé«.:{Ïúþ­ÜµöYu[+m‘¾Àì¬I*;.9?ÆOÝíÏJ í«/¼õþ¿àw½¼†Ú[{ü2Ä1´¶Nyàuþñϰi8ݤ‡È¥%Ëý^¦PˇyŽé$å‰äàõõõnç¥uB**æÉ[bí„;wôãvßSß /Øt¬jÉÞÆU¥Ñ_—‘¯9(°¨³–\îÛížqÕý9⪜zŽ„ù¿¯ÓÈv™o$»DJ Áb#=qÇ\óè¾j'+ÊÝ «4´è\ŽÛÄqF±À¼(£C:Z‡N›w¹“ö Ýßñÿ#&×ä/3c‹„_ï0éÇ>‹ž+I¶Õ»›ÎîË¿õþ`ÑÊ]Sfé¾P99íëáô¡1ó%«Ù_æL%‘pwAoœc'qíëŒüžž´Þ‹Íî–»¿ëüÉ/% f,0I,2rzzöÙÜtÍ.^ãŒlõþ¿­Lù>uܤŽ;ãÓž}W¿jw±£vþ¿¯2ÄËqqj—o@6«u Èÿy;ö©æKBy­§õýoÐÒ³€Ãf]ËGùŸåù‘ˆ¨çø—£s¶³”ï¡„åÌõßúþ¶#’tvMä¢?»VçRx7££””G[uý^D%C2–¶‘÷®f=Ž3÷‡sÒ­+"‹‰"4FBÁQòYÇËÁáˆû¿Þ 9?t÷¨qÔÍÇ[_©šQî®”ºùQIÈãjªûtÎ÷éëZ7Ê›ä™viäŽX­àýÛ9”ðc€xðzzÖJ7Õ˜Æ)¦ßOëϰèµ9CÜlaä²ÿªÆ É=¸þûv=9愚µþ¿­;’µÅ®¡<’Ü1†Þ˜““ÁÆ~ónjšmYMiëúõ+Åk<0ù®[íD’‹÷ž¸û›ø„úPÝÚìÍ7俯òêCtfR¨«–ti»3˜‘Ï<·ðŽ•pµF‘\«OëúÓ© Q›‰U$“ó(Àïëê݇JÚrIzÚŠ¿õýlhÉ"¤E™AE\ãï}}q“æv+•&ßõýv9,å/ëþ‘Šìò»»å¤c“ßž¾ýð{u®±Ü’JÈeží"ŽÎ/*cˆ÷ÇDôäÖn1[‘%ü_×õ©²š®¬ˆ¨š‹Q€>SÇ×4½œgK¯õø€·¼xí`|é¶H]ÎHÞÝŽ0qüÂ;Óm­YmÊ Éîÿ¯óêEs·æä6çö[ÀNæUíÎ:§a×4âú­gÓëï*È<  -ËÎôõé•ôïWk»‚ÕÝõþ¿ÌˆŸ7CÆ9ǧ¯ªút¢Z¨ò¢HúŸ+ËifÎù :ßÔwì*˜ž‹Röž\å%û Ör„“±‰8àöûÃø»qY¸³_Õ_Öĺ¤Æ2%û;ZÞžDпÈê{ä¼?‹øhŒSÜ!é¿õýt3ay%Í+$#’GaúxwíÅ\÷I-=ßëúù‘\2Çm ‡ Å œ÷ãûÞ¿ÃÇ4î¯a_^]Úþ¿NÃZcr‹e•d?<’£†8sÓÖ‰Y1Û—VkÚ«Ü ª¢ †sŸº s÷‹¼(ïX·wsîïý~¦{ÃVÞÉ_ægÏfÌ×j^{x_]½:uäôÜ=:zVŠjÚ›ª‰«2;iCC$>@‘Ÿî¸äÇøóýáÜtô¨žqMrësv&ß÷XÔ,Ç8eýäcê3ŒõwŽ+4ŽfÕû_ð;wnÓÌܸQÂî$•™öÝëÚ´‚åFô×"¿õý|‡ˆÃyp«.A‘Él9ù»P“+¤æþ_×ü;©üÆù2‘<}GûÄõ=+HFÊìÒœ9UÞÿ×õ±jÕÚÝ¥˜í.»›· í×qîx\Ö2—3ÐÆræv‰?Ú¤Šå<¹Œ[äd!wnoLýæúQÊÒ¹*:_qÐ_%ëÇop¢ <Ê¿·MÍëÓÖ§X¯2]>Uu«þ½|†É=ç–¡e}ÂÉryãŒõ~Ǧjù¬ìR•šõýl2áÞ9OÛÂyè1åÆ ää`z¹èik+$ËUýZàî ÈO9Ýî{ñßø»s[¤¯¡´RéýZu'³C$ÁeEûÙçŽü~ü<ÔU–„U•–Ÿ×õ§SJêO%Iàcg¿CÇ=0ãk q»9áf­ýZu0YØo,GÌ~¿¯u=«²ÖGw*CàI..<¸9`Ü2ûvíRÛ¶¢””cvhêÚ³Þ@¶Êœ#ãqÏáÇ8ê¼`c*vwf4(4ùŸõýjc8Œ£áONOûêº4;.½> ;½*8bºhïQƒ¨9à“ÆO¯Eþkží6ÞÇÜ£&ÚÓúÿƒÔ£Â\†I€ «‘Ïr§?Þà+Eµ5ä’Õ_רõÀþe›®$1‘½FzyÁù䔜SZ’ÒjÒ-ØÍÚÙØ¹û<(I›y9fÏãýáéÓÐVrRZ™Î.-Éoý‘VçPi5)n¬ÔBX’»Fpçýïnž•|Ž×f¼žæ¤·:‚ÎX¢Üäï‘?ž>ñî:Vj6fq§Ë§õýhCæ-y2±\ðÝ'¶G¦ãßµTµ´QO_u_× ¬Y fr|ûŒ—nê½ù÷Ë÷¥»Ód=ÓeýZZ"É!’S¶%åÏC¯âÝøª›iY9´¬·þ¿­‰ã˜¬SÜ\F?~p ü¿^xã–ïÚ§•]Et3qZEtþ¿­  MÓE‡Ë6YÝë– çoL·sÒ©÷}¯×ÈkBÏ…fbdppäûtÏ~™¬ÛטJNîKåýkä1odK´xcÞ‘¦0wqþÙè}j¹n¬]¹–»ÿ_ð Ùbk`·* õÓïveÆÕëžÝpýŽx¤“½ÖÄÙ§u²þ¿È䆼ç_¿±h,l²d˜îB(µõ¸Ö£°·‰¦LIæHAÀdÆ@ã¿AÞ¢®ARŽ_,egÊ×-–›Kfõºùš;ƒÄ‘Á¨ßXÜ£,‘ȉ*—Ýò‡8àá¾\=Oz¹pÝZ¸J8šüÑm¦Ò·½Ë}ïnï¡2¶æ½œÐj«s.•,—–öå|ɼ¼*’¤ñ׎ž˜éí^N?ˆÀJ0Ä®Y4ô룶¿ F­’¾ƒHX‘*€O'üz¹4:-ëëË/7Ά€\‘By9>ž¹pËü5ÍÔ›´Uì›ûµrWgŸi[}¿¯Ó¹SÄÙðóhþYäÈŒ~Tùÿ{Ð}ÚÚ‡=KE+·±®s³¿õýz–åÓà“FŒÙ]+¸;äB3¸àvçÞ{ŸJÍU×RUGÌù—õý"©[‰¡†ã ®vy)ž˜ë–öéU§CMÅDebÀ0m¤«Œä¡î;ãïN•¤¡8¥Ì­uuçæZb¼‘…S$Š»ŽcÜöy廎”£Ns¿*½•þ]Åmt.Ùn”¿šÍödùßøó¼Ã¨®ié¢Ý™Ír«-ßõþD«pêe½$‡j¨=|ÀÃïqJÖ´Q.6÷Vß×ü„L˜Ùl>V'tÍýß^}â­"윾áÅï?»úûº¹D¸VxÃF‹´qŒuçþû=O­‹°Fúþ½:1»M?Þ™Éïßã¯Îzž•ù¥Ë)>irÇúþ´*ÅpÐÈÒ°ù›;Ëq¸ Ž3ŸŸ×Ö´œ/RŒ•¿¯ëbušÞÞ¸5¨U-p:ÌÀ`gî¹Ç'¥(T¯V4i«ÊM%êô_§c9&žºÿ_ðÅK»Ý!¯m"±¾7][ •B%=rBõ!¸#<æ½9å8Ú§V´-O•íñvÝýëO1ÁÉé.…K;=&-|¤ÑÿÄÂæ&‘£Ã2”ä}ÓÐöïÅmS™O¥û¨I$ýÛÅî­ö¼ôVí¡Ÿs…-õÓ"…õ%Œ²ì.ô_”ç€I°1Ñ…zq©Ä0¿\”›¦á-oòÉ®m7µÒ»ò&šœµ{_ÖäW6:]€´ Te‘`·ÆæbÌx^9Á õæ¹éc3R“ áp¥AÎIÇL¼s]t3 }ZUNR§Ý¥ü·»»KEÝýæ®ï+/hVvÚvƒ§höŽIºIæpã’O¯t†2+“5Ì*ãñsÅTÝì»-’ù"$š“—õýju‰¬In‹ ¾ž’CØŽU~eÌž•äèÿ§þG'-õoóÿ#“Ö¬¬çºxnIuÕâP1µ†p0}7wøkÒÀâêᜥIë(¸¿G¹Õg?¯ëÔæt¿ é1^EöÉnÞ=Ü…òóÎ:‚‡óÀ=y¯¡­Å¸É'hGUýï?ïyíð謴6š—.Ÿ×õêk[x[GŽk=9'½kg¹<º–Ç¢ûŸáŽÕËW‰qUjÊ»„néòlöùËüד1期OëúÔ£©øSMZ²}>ñŒq°–_1U·1;³¸sIÏ;@Ǻâ|D°ÕhÎ?l¬ÚåV³Vê­·fÛêkI·)#CÄ^²–æãPr\ ¶2H#d2A<r2HäcŒu¬pÜSŠ¡JáÚ Ë~Ö溺Ÿ[­„í£*·…tûËIæ¹¾¸¦œÊêl0CÇÎýºã8—EÁF÷aÊ´{];îµ÷VÖêÖ­±¹´ô-iZ]½”wÚa¸œÜJg™Tr6ôoêk‡3Í+c¥U%ÉUkíçvõ-hùŸõýhhI¿ÉE‘ Ïi’Ǧ<úô;û×–‰º{uþ¿Ë¡*ÆwO*ò“Ždô å†ïÂOâôªòeý[t+iɸÿI`¡OÌzgžF~›ûÓ«'Ëd]IZ6õýiКúcæ•b¼½‡~xã†Ç'­*0²¹5ý_.…V;–!r9íŸ_OFîzÖ¦Ëȯwm5Þ—e¾BÜÅåË&ܹç3­ëÖºòÌZÁc)ây9¹í{~:ùìÚ3tï Gg­EwÁ&•„{1˜Øqäp œ`ç¥{˜î'ž+,5Ho뵬ž›Éhÿ0º±¨úwÙo±ï›Paœ’nò7&SwÞ]Ùç»b»*ñ¢•NeBÞ䣤µ÷­³åÑ+h¿JzÙõŸ I¾•jÚ¹½ŠÒ2±nߌn' +‚§ŒwàŠæ¥Å¿¼¯R4}£¾–ºÑ+>h´×W¢ÖäSwmµb«øFÝž2Ï’G|n[|YÜ™'Ê#'Ž»ôª\a^P‹KØr$¥kK¤öÝt_‰M÷5<; ¶‘5ÌÐÍâÍ sˆ˜$ã«1è9ô5ågyÚÌ£J<œ¼‘¶÷¾Ý,’ù 澌醬Š¢\2ŽØy÷Õ|ï±LÏêéêÿ¯ÀÁ´—rÄç*Xäþ$Zì²QÐíQJ*HÕдøoïî`ŸvÈðÃ_N랤œatrâ*Êœ‘V9[Ë¢Çæe|¶?>:w#¥ DŠZEþÒíuY/c»!‚:Œõëõ55$â“Fx™ÊšN%Í7N‚î'·0Q+)`ylFsþñ¨çiœõ*8.eÛúüŒ}.$¸–S(Ýå« ü ÷ÿxÓ©'êɤ­×úý ž“í÷/‰a×i1  ÈÁ<¼:*{º_ÜÑ_Õ†êL~ÆÓd™6ÃbrHd y<çæ#¯CR•§Ê8Å*œ«ÔŠÅØ­‚ç û¦ÀüÛsÏ_ânýêÛ³a7i5ëú•,N-Zä IµHÇl²½ˆ÷ïZ5} fºÙŠÅ£ï8\þ%{õî:k{–­îìâ‡IÓ.P·›rÀ?>¤ßçCê'»òÿ‚URaEÚOÏŒöþï§ãùÑ{’Ÿ6å¨ÛD^?½·¯Oîúb±~öäKÞІó÷Á$l·lãp8íúšÖž±¹TÝÓbK›O±Æ„´s έӯ ã·ó¥ºrÅvkê1 P‚e‰•Y•ŽUˆç‘Ó·§­eÍg¹Ï:‘r‘OQ¶‹dr*…f HQžzƒîŠÚ\é‡Ãs5yŸÊþÁüñØVŒ©mq`$ÈÈ8ØÜ¾¾¿îŠÊ{‘5eqN¹ 1$î>UùÛ üiû8½MI]ŸÿÙfrobtads-1.2.3/tads3/doc/htads_cover.jpg0000644000175000001440000001676710477633707017331 0ustar realncusersÿØÿàJFIFÈÈÿÀ´~ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?úN¶KFÔaÕô»mBÕd×)æB\^3÷_à0ÃppF@9ã)8' ï@ö‘âý2úÄ\Ý9ÒÃI",wî‘»ùg@ÜrÈü)ØW-Ûx—E¹»½µƒT³{‹0M FcIö=+1Üš=oJ’š=NÉ¡w«‰×k1 zàŽ(°n|W¥ZøŽ={„K‰°‘¤@²–yÈs¸1È4ì/G­éRG‰©Y2JÆÂu!öçv9ç9ôÅ+Jh·:PÔlµ k«c"Ä R®w±À^HÁX.hÞêº}‹²ÞßZÛ²§˜VYUH\ãw'§½Miwoy’Òx§Œ1RÑ8`ê2;Ð7Ä:F¦×ka©ZÎÖŒË8IcÁÁ'ÐpyéE€Ðµ¸†îÝ'µš9¡q•’6 ¬=ˆ  h 8øâ SH×í£Ó®Úͨb»T‚Kœº>œúš‰6˜nµeq«^[ØÉÝ!yxYc G;s†bp0áßmµ@À• 8>”ÅÚø ¶ ¨jRêZO<á&·Ò†Îp:ÌaNâ°Äð¦™á¨.o¤Õä²µ© ¯(Œ `‚1#9à/ÊNÜóŠ/p±…5‡‚ Ðt½'PñEž-c!$ó!F–9<Œc•Ûó zz†ƒ¯ô¯ý¨êøšÞ$ºi&·o:/‘ŒˆÅ•ˆÉÃÆ>ö{Š5 ŸI𞟢Jš€½•’$•¤iBtfgÉ;xÆöåq‘Ö•ÂÇ3k§øbïC¸Ò´¯Cå=ÌF3ÂþOÎJ Á$ñ“–8ÆiêÝ{JðÖ»­ÝÛ·ˆ¡W0˜¦‰^"Àª2–*FF›Œã¾8¤®Ká;K´ŠæçE¹Š{k“L%Jˆ¼dàCQ¼bÒjênnVÓQÞ^Ù6*£6 0!ry’O\V6|?¤C¢i«gnï"ïiÜY™‹…O@¤ÆiP@GñsþFKoúô_ý ë9n#×+A…P7Œl®µ/ j–:|pÉusB‚g*£pÆIôÎzv¡n àìü}g=©[ >xžñ&¹7o+Ÿˆ fRXeœ€p8¼UÅc"ß᮹§­Œ–Vºl“ÆŠ&ݶܬ±¾÷|å’}ä>”] Ǭé s¤´~ ‚Ð\J]d†/ÂN$ ü¸Ï%y¨xZÔ4««³iÖ+q}$Ï5¬ÿ8‹–1ö|qíÅUÅb{ øšúýåÕíôe2_»“¸àƒ“@xRú}FÎ#>;âã¸ëôË0YZÛ`¶†2 ¢‚zÐ5£9skrÛËyc;½~´ 3)—ì°y¤‚_Ë$tçÚ€´û&usinYNAòÆAΟ4ãcjmZØÛCöv91ìIÎz}h¿Ùö[Jý’ßiÏXî0h1¥X ­¤*@#@ ‘Ðð¢àHº}’ýÛKqÂŽ#ÃÓò [ÙZÛ>û{xbm»rˆÎ8÷&€<¯âçüŒ–ßõè¿úÖrÜG«ÏΨÌ\nÝÐ/sZ Ég×|‘(‚ÑfU#Éò1Ü0KuásøÓÐ 7­i9e‰n2ÞPÆF;gžJ@=Zé¡›(‹ »Èã8ïÏ­U/ª,€¬0HŒëÍ´ªànÆ3žsŠ.çÔ…ä©ko@ª ³ñ“Üuÿ?¥é{lòaH@`©·ž:¶:P™uw4F @åS·~¿ã@ÂÞ5Ò³[¼+((Ì9Që×­Auý©,Û~Çfñ)FFòN;qÓš`_¾7j²Ž8äHqžGôÍ +!Ô‘>h¢i|7ØÏ?_Ò€$±Šâ k†–8Vgf“)p=69µ" kh¶¥@<ŒõÏ=¿¥h€A@IñsþFKoúô_ý ë9n#ÓõP­ a®·ÏÃçúþ ÌG²…#ˬ^¼l…YX“¸3à|Šb/ê&(e†9/d„lU$°HÒ%ƒc$72Ì%bul†9è{ö¤+‘½ÒG&§s‡Uƒm$ƒž†˜„Ž8¤öpê3£ÎŒ¶ì¸ œûà­mý¾Ï$^iËU¹ù};Òiju†á ÌťɞPûPé³$¡¾Û#"¶À]¤uïÖ‹%¥ƒÁxóµÜÒŒ'–Oȸî­%žžö÷"VºšT ´#“×9ÏZ§pUÌ—ò02ÄÆ6“Èè)Ü,Z³±¸†Öâ)nžIvÇ!bHq“îNO€ªš,þg˜úÀ%J+ªÀuýzÓ¸éšKÙ\,­up2›e|ƒ–ÎO¸éJàyÇÅÏù-¿ëÑô7¬å¸PÔ¥·!P´Êò G¿ Øû}kA”Q´o² 6Bm–\¹ë¸5ÍúéÞb¥âFXF@Ü3…ƒß΋Á«Ø³I÷¬ß*“ÂŽ{Q`$: •#3.÷` `õ=+ßÛ%ÃÀòm‘ ‚ œsÓž”å¿?äd¶ÿ¯EÿÐÞ³–â='[f h¢Ø\#Ne l?7¢‡nûW̹ҢY@d¶åwg…ÆAÍ1"’t•L:|Wº$)‰‹ÏA÷»ŸSBXK£\éKöc+ÈÅ­ÜpG\~yâ3ZÙÚIo$b±šD É)èÇŽÜR¡1¶qoý”óÛ,ˆcò²pIåˆÆ¹4Ä>Á¢»„[M¦¬6ÒbŒŒç' ¨ç=h[RœHùm§Áx˜²7Üô8ïÿÖ ERé5Ä¢O:1!ä†m )ö¸튴÷BD¶µ—HvJ_jðz½ºs€ [ɤCvÚKÀŘ3½þNï@î®1lÒ`‰“c1Tî'=0Tv9ýhÏ=¨•mF’¾YWm»X€ùvûÇë@s(›hL«'ÎTÃnA^9ÁsŒwÅ\ÓçT—uΚиXùÈrOŒq^ô†yçÄïøÿoúû“ÿD[ÖrêWÑ\ÈÐYDe/ŸâéZ Ï–×[0¡ÈrÑd»'Ó·h¨mï–Öád¹ ;¾äqü#DZìzЭ­ÓZÝE=Ó3KŸ-”í(@ÛÞ€([éz”S×åâ઴•ÂàóŸ›=yïL M>)à·HçHT}ì’IÉëŸlRÕP@w € ò?‰ßñþßõ÷'þˆ·¬ä#Ôï®ÖÑa,¹ó%XÇÌ3ßšÐex5X¥ÒÒøÇ"ÄÇÁ=p:ÿÖ‹‹¬[;lˆHò•b‘€2ØÏŸj,›W´[ølË·+² ÆA*2yíE€[RÞ %÷.Áä€Fyã®(°WÄv sö}Ò ¶©Û·ûÝzf‹ÈâñN›2+BÓ>àH^8<ã¹ü©Ø.Mkâ »”‚ÙÙݦhOËŒ2Œž¿JVÇö­·Û ©lJ$òñ‘Áž}>ðÙ5‹XîÞÙˉ—Œ‘‘Í%׬Ø‚G؆m£½{}VÚy HË›;8ôçð¢ÀT“_e‘E»²Få÷¨Æ8äþì-iz¤:‹L±`4M‚»Á$`ðxëJÀhP‘üNÿöÿ¯¹?ôE½g!¡©yáakxüÜH7¨PN=FHÅh3%†©=”QËb‹*áŠá  G9íž”Äj]µÌwÁl²&ÂrÈlû‘ÔR“½ÄwÖkš¼3É g¼Ð[¹µ5»Å¶› ‘™@i¿ÞëÉé@‰œYÆæÎ?;w1®Ñ“ƒ@‘^êΈßÙ´2B9 Á‹r¥Iôç¯øSeæÔ¿|E†Ò<Í…6OO-Æ¥íkutü eÚ[h%WÔO¦!@ #ÜÁ¨Ì©e‚W]²IvÚrN:cdâš]XZîþÉ·yBŒD@qÈÎzã¦=鈿§y’”q /*Ä(ÉêOPÕÕ¾ëÆx4Ƹ…T³KQœ’Ìրæ&GμûÐcAuŽOZˆÌXF@µ "?Üun½¡Á ÌLgzãžôÑ<$%BAÁøÏò怮÷YOàö  ‹ªéí!Œ^Û X¯š2êhżñ\Ä%·•%Œ’#dqÁæ€% #øÿíÿ_rè‹zÎB;ÿ]Elb3Ç3'—)ýÛ ÏËÓ ÔúV¨e'¾Óu$°£gÌÆ2¹ ¯|túzŠ]OÒnÕL¬ª±.H*wçQ×ð–eŽ»pùŸnKRO7{qœÐ32{íF;‰.W.v2'-Û§ÍŸÂDIuc¢ÙF'›íAoå*[9Ê|ßCƒÛúQ¨ÙÉ gì± ‚ñ´IóƒvrÙÏ"@ŸC¸Ñìâ¬Vd‰cTW‘€3ã'9‡p,hßÙò_£Û5ËM† ¾0,{vù‰ü¨Rh4`·ÖrQæYW¹oÐѨ„–÷GŠ3¸ÜÀ’0¨£Íýß_l¯¨Ô ]ÞÀŸµX±æ=xÊ “Ó·OÒ†3 K¢Çª^é"1u` O$þ4j#XÜÙèP2I5ÄŠTÊ7ÄAFã/iÚ¾ ²5³q“ßÜ{p*@yÄïøÿoúû“ÿD[Öréš«Ý(_±Ú¥Äøp3œŒýáZ ÏŽ]e% 4øLlë”G–6|Äß1ݑۧãOAÙ¯^ÎÖQ kpY|äd…îÍŒƒîi y{³dÒù*·@ð»AÈÏûÞžÿá@¬¥Õ´"ŽãO„ZÛ§È ¼ | Ÿ§øS’Üjs”XE,¤E ¸$ ÁaÁõïŸÆgÔëTa½ „FÕÚr /Ù½;zÐör\Ës7Vˆ©ä—såàÝÀ$zŸÂÈê… ‹;xçUù[`=ÆF7úqרíLD–¯¨ËzΟp‰7J`m?Rzý) “S7ำ·†EãºôëÎ9ê1@ †]Ac#슲’¡ˆE z‚GÏŸN½=è^K˜t¢Éh‹8p‹ ‚ÞŠO¯­²êmsÝÚD",ÊθùF8?{¹Àÿ ÔDTûŠ«ô %øÿíÿ_rè‹zÎB=ZŠ9Jî¿–ÑÄl–ä‘×ÈúVˆe{5[ÂZ¹™–hÁŽCÀeã·_ëLCõiícÔ5ýÌSnRÑIJ7²AúzçfÝ ½¶–ÎÒê`Ñ0,à8Ç'Ääô#­!™±4I".¥~da Uòåɰ;àF?)ˆ·m 7¼k¨K.I^CÊœá³ïž==¨F[ð[GçH­;|ØsÏ={æÆ½ƒ½œP©Ã#dȬAaÏœ÷ ˦ù±q£½ªÝIö†Î%26G§=zPyô9åIÕoQ˜œ2¹ÊƒÛ®?NôîÈt¸'šúü´d©2\8UNêx?©¤Ø_ñd¸¹óck¹0$‘¿ÔÛõVŠÎB=fâÒ –Vš%fPB·B ŒÕ ÈÆj7–r Ÿ¾zŒ`õöO§ÚÏ)–X·9 çq1Žþ€ }>ÖÞᧆ-²°ÚÇqä}3@ɤØÉ!‘à÷ݸäœãž:š.ñÙÛÆÛ’%$ñî0hdPˆª£ £P¨ € ( kXñu…¢´“í,­ï@'pÁùÇÊßw€F—­K`rÒj·7“EwzÒGædÛµÀ쩎JT9_»"·Í†óA*àsž't{8Z8Ö%ûdùbòö. ®Ì ¸9ç­÷Œ±ëVZ½õ¬Z=‰Ž Þ5-$Ù 1?¼ö¡OûF×þ€Úý÷?ÿ¢àÚ6¿ôÓÿï¹ÿøåþѵÿ 6Ÿÿ}ÏÿÇ(¸ö¯ý´ÿûîþ9EÀ?´mè §ÿßsÿñÊ.ý£kÿ@m?þûŸÿŽQpí8“æ·ÒôøekÉø ³)üAüè¸öÕ×üòÓÿðþ"‹€m]Ï-?ÿ ÿâ(¸öÕ×üòÓÿðþ"‹€m]Ï-?ÿ ÿâ(¸öÕ×üòÓÿðþ"‹€m]Ï-?ÿ ÿâ(¸öÕ×üòÓÿðþ"‹€Ó©™%Vº³±TÈqÏî¶“Ó¹=MÿÙfrobtads-1.2.3/tads3/doc/learning_cover.jpg0000644000175000001440000001505510777174331020006 0ustar realncusersÿØÿàJFIFHHÿÀ´ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?úV +fÑão³B\À|±’vÕGâ@xoØo¿çÖçþýµ}î<¿°ßÏ­Ïýûj?qåø}†ûþ}nïÛQû/Àì7ßóësÿ~ÚÜy~a¾ÿŸ[ŸûöÔ~ãËðû ÷üúÜÿß¶£÷_€Øo¿çÖçþýµ¸òü>Ã}ÿ>·?÷í¨ýÇ—àöïùõ¹ÿ¿mGî<¿°ßÏ­Ïýûj?qåøÈ|Bð>©¯XùúgÛíu(WäÙ½VQýÖÇèkƒ…£Z7ƒJHž/.5ë;Ë‹K©õ(nmÉDò8hñ×#µ|ãM;0;…þ2Õ4Ñuý¤òÜh‘àÍ<²s´±ù³ýÑ“€HïÀâãJ\µâÿ=êÒ‹»h®-bškyTÔ:á6¤QË)<GÏ¡'§ð÷ÍY¶}SÎ&æ( "6 ' Íž\ Œ~ ó@­†»¢‘®ÔËJAmÝ¡zŽ´ºmƹ3(¹·‚5V ùR ù†qÏLgŸjå>+ü6µñU¼Ú……¼K¬„ ?(»@ØÌ9 À|:|¦ö—>Ô^É Fܳ[ÛµÌ^^ã·ç†LòÈ`ÆGÊÄpPÕ~øÚãÁ¥¼ës{áy%ss65²±É†¼¨ §'¾0Óv—0^ZÃsi4s[Ìã–6 ®¤dGPEpÞi¿òµÿ®Kü…X € ( € ( € (Î>'ü4°ñ<ë¬ÙÛFºÜ1”c÷~Õ>ãG>™8=>a’âjí Y)H'Žpcñ%Û o° #pA7ÂÏŒ–>ñ*èw—³ÝxZí‹‘"6ý.Vc•’Y;‘ÔgÔ@=›þ=þ‚Ö?÷ýÆ€=GMÿu¯ýr_ä(ÅP@P@P@yŸÆ†øÆÆ[½7m¾°#òÝ”ö˜¸Ý'¡ `7àxÁø²Ê=;Äšœ6wVIÌŸgº¼Aÿ'Žç­zå}µ¦ÿÈ:×þ¹/òb€ ( € ( € ( € ñ¿ÿ­>!ií©é ·‰íÓ÷réGð9õônÝ<‡þÏÿÐ.oÍÆ€>ºÓäkÿ\—ù ±@P@P@P@çtÞi¿òµÿ®Kü…X € ( € ( € ( € óºï4ßùZÿ×%þB€,P@P;*)g`ª:’p(6ï^Ómƒo»Èê±Çô K^ñð¶Ó®e†$€.Q%–è8Àç>ôÇh:ºþжëQß@Í÷\€ÇŒí?19úŠõ ;ź}Û¤s m%n12àõÿPC@P@w@æ›ÿ ë_úä¿ÈPŠ($’p(†ñ—Žmô¦û=¤ŠpW¹8(§œ¨õaŽ~´Ï¿o$·iáÔ`ã‡0àŒä’sZ¯ôû‚ïqª}¥ó¸,aœ#ôãÞ€9«ÿj7,‰¦Ã´/+|÷–ôl 8æèǧjɾ[Ý]‘%»žîé_™Ú7Žp…£Ÿ~”6ƒ¢éÚÔW&¹Å¿b[猃#”qÎ 'œPð»Òã-ü1c<·R•Ux]„x$g’Ø<}{f€>†ðüÛhZt˜ûLvè²àñ¸(Ïë@è € óºï4ßùZÿ×%þB€$¹™-íåžS¶8Ô»@My4¿¡Ôe‘4ä6Ñ+”Yf`7tôàro­rZߌõ ¡Õ.# IÚ°’äõääŒsÆhÎu­fòõšpè~ë.`ì:~¸Ï?‰ŽÍRæY$(ó°ÜYÙ‹}Hb2½{Œ9€6$¶ûNä† ~Ñ»,® ò$d`ä}qס ovÖ¯>¤-­î&Áµ·¶yŠ_v lu^¸Ý×=¨ù±×?³à3Þ,_i‚Û""J €T;Ž ÆBhÐþ|1Šhå¾Ô K{‚^(5ó ù„yŠ;ýîôêÚ?‡ttë!*0.[ïhV€ ( €<î€;Í7þAÖ¿õÉ  Þ$–<=©Ër¥àKYEJ…9•|¥§êVv61&•”ƒË\¥Ã–g#¾OÓÚ€,¯‰mÚè+ÙÛ¬ƒ•-n¼\Ž}y  ¨µ-&p®lUK¶U@À×u<öç­H4Ë ’±[e%âš6P÷ý1Û­C¤ê–:}ËiâÎ9/`bªöãpx¸  ¿³_ê7òO{e›=Ûa`7 ÇLqøP­øáÖ×´Õui§Mª Vg(êCg’z~têÀP@P@yÝwšoüƒ­ë’ÿ!@.µêÚk{‹û'†T(êg\2‘‚:ÖßW«ü¯î`|·ã¿?ƒ/&ºÒî-u- G$‘]á³§qÏ_åJTjE]ůå­¦§l®â·œr`»lÆÞž[žW¦0?L€ŠâÎêÎ%¸¸¶º›…¸·¡ãŒ·§ùö  :f©oç…’âYˆÆì=þ¼óùP}oZGÔ…õ±ÉÛ„ÎXdsŒsŸRy «Cñ ÛňqF'}ý¿) ç?ç½z%·Œ•†R­EJ¦·ÎuGqtÓé÷MjXäÆÛŸl+‚¾QKš›·Ûxk]²b-uX¶žNàyõÈ®W“Vé$çÒu9Ô-Õ¾›0îÁŠqÔ„—ö=nëñÿ 5l4¸"…Ò[ ì~ø†7oý*–MW¬ÿá° XC±9ùc =ø\ Þ9,zÏðFßO¶€D(਑ŸÃ5вš YÜ [,ÔÝؤrÑ0‡Ë8 'b}º×t¡4¢©»YþT­@( € ú'Mÿu¯ýr_ä+â*üoÔ óNÿu¯ýr_ä(çí?þ?í¿ëªÿ1_oSà~€kxëþFÝKþºä+›/ÿw‡ -´v>±¹[…‚æõäc!V,NRäŸÂ—;©ZQµÔmø€ËË>mÞ;‹™.¯£¸É!Ê#•ÜzóÓ>´á ª«qV‹_ˆ~šKýro.'KxeE€Ÿ“?ˆô$õï\ø¨ªt•Ýåu¯Ì ^ xÕµa$K¶ÂW×$1ÁíÔÖ¸äýË;{È ^Õ&Ô¼M¥Å¨Gé´iº0 € c¸ÅgŒ¡T&àÚ¸ ðž§=Þ¹›0C¦Ü–F¶Ú6ƒŒ{Ž9ëOF0¤ê¯‰u·„Ö/øE$»-„Ì’0Ë)vïZcýÛOí $Ò'H|©È- 3ÇœP©ÆŽ&*)'_³XxG{hþÐ$y¥Œá™Ãci#°ãÞ®šö•¦§­­o@$ñ|û­´”1@’Éj“M²%V,sÉ g‘ƒŠœm)»»)4µš®ð>‰Óäkÿ\—ù øŠ¿õƒ¨¼Ó¿äkÿ\—ù ð 0Göøi’ÕÃ`HÀ>À×ÛU¿#²¸ÚðÒõOÏv5XRÊg ÇË“xÛ׊ãÃûjT9=åéþ`÷MÔô×ÓnfûÙçy,æ(YB1ÉF‘õ£ÙÕ¥?k{¥uéÔ y`±¶ž$k¡t<Áæ4*B„ïÀÏ5Ô¥RI»[N t¶×ºU§ŠÍÑ¿Y,Ý^8Dq°)R ŽÙíŸZà:ÓÃòrë×Ì ß Éce6¦./âE–ÖKxÛËs¹›ð:~¾Õ¾)Tš‡,v’}‹ÂÚXxŠ«Ë¸ã‚ß0V;ò¤p÷lgR‹Œ#«ô|/-žŸâhnnobÐ1o0#üÀÆ{÷ŸΥÇWèº Ö6wZ¿ŸIíd‚&çql`ýÞF"5'Z;I>>`6Ê[(ü+¨Z=ü"æiQÑ6?!sßo|ñU5Qâ#5òëóº\Öiá}NÚkØ£¹¹xÙ#(ç ê@Ç9¢¬fñ’ŽŠýº{M1h6ñÝ_¾™,Žde–ßÏáuÃÜg½cWÚ:ÍÆ<Évv·—˜Þ"Ó PÅ©E©.¥opå ÛJ°`:zq[á«]ºN­t »ú'Mÿu¯ýr_ä+â*üoÔ óNÿu¯ýr_ä(À4ë FY#µ@Α™,ëɯ¶«V4ÒrîÿØ×ßf7\~@m†O96†ôÎzÔ}fŸ7-õô`C}§\XÅܪªÎ &× 3Å]:ѨÚ@"û4ßd7[‘¿ËßÛv3Ê«ž<ܽlÙZM{p°[(i ,õ4ªT8óK`.\h·V×Mm;ÛGp§6óÅeL%hÞÞŒ w–—S˜n¢x¤8aÔzQ[B¤f¯tౚX–BcŽ78V–@»¾™þu2«í¸j0I§M$W¡bhÀ-–Œƒž˜Á¡Vƒ‡=ô]RiNÍ2Æk¦'ÈØ‡èO_¸êc›þ ¼úÝGÂþ(¾²–yžHy– i×rVçóý+ÊÄUÅÖi=/µ´mZѵé´ûË™nôÍJÚíä’܉#” À;IN:ŽE*8 Z•×»óþµ¯ÚØØÁhîV4bëù¤‘Îl ‚½Š•©áß=Y{ÍÃꦷw!ò ÛFOʈœãÜú׉[3¯R^ë²Þþ7ŒNºoI,–_±›…O}½1ŸÂ¼÷{êÊ@wšwüƒ­ë’ÿ!@=Åq$QMmµe_À9Çæå_pà›Mô ‹þIÔßöú.¸Ÿûêÿê=$òI Q;‘g`ôÉɮ՛k¨¦™w~Ô40ƒí0À·ªqÉ~¤ß%Gç^]Y8W~òü¿áî#¥ÈRÏþ»'óéVþ½^7ÿ‘¯Sÿ®¿ÐV÷xz{OÚþ Ô"ŸæŸL+,z„o¼¿N3XÔýÎ*.;OGëÜ h§Ä&Ÿu£:Iqkn!ž×vIÈúäÔGõz²…UdÝÓ¿‰¤»Ý{7ShÀ=8Û4Ÿ¼•ﯸƒÌó£ò7y»†ÍsÛ´­g}€ë4Ý*ûKÒu á…nn¦¢xã‘XÀ‡ïç^wHGd±ü®ºÓäƒkp7´?<^#†æ[kDIeÄŒ±ÁXàó\uðIÐqMè»SRÓŽ—ãhnTÇþé ÐÖ”«{\7?÷@oŽ,ÔÁýîAOþï@/[gEðuá›ä»ÕJ¬qŸ¼"\’Øô9Åc/ßâcm¡ùöAu£_DÐM$Syi"º2†þ¸®´á^ 5uv¾à5”VÊNÞ€s A‚;Šî_Á×mkâ­7d›ZIv‘ž«ü_†2k‹0pö K®Þ rš­Î«}pÓMök‘Š[Ây+ž7§jçú¾"ºJ¤¹cm–ÿ0+¯†¬ÊÅ»ž*a“ÒNòm´Š¨Š¨ªŒ; õ£’Ø¢´ßùZÿ×%þB¾&¯Æý@àêï4ïùZÿ×%þB€<;þoþw_÷ìרýwüè °ñJÙ}ŒY]}—þyyi>½:ûÖ×ÍÏ̯êVG‰, ›K ¨ÙƈA$zr*ªb0µ>)'ó8ô/Gs禙p%Îsä ~XÅSÅaœy\Õ½@¹=¯‹'¹Žâ{K¹'îÈÐWñÅeà£%oPqeâ»™|ÙìîÞ_ùè`¿»‡þtÿÆ·ÿ@»¯ûöhúîùÐå`¬–6Èà†XÔ{WÈÔw›k¸@æ›ÿ ë_úä¿ÈPŠ( € ( € ( € ( ; óMÿu¯ýr_ä()<]¡I<ÐǨÆòCÊŠÍ» íÀùðÄ·8'š¨ÁÉÙYx·C½‚)¡¾)n…œm,où‰#`ÜNT¨"¤£ñ·‡dÎÝM7o¨cpÎI#*¤e†Aädq@$ñ‘0Ë%üKÈ6lãpN=~fQǨõwM¾¶Ôìb¼°™f¶”e$^Î?¥Y € (  —zŒ·1[È$idF‘U#-ò©POì¿H·–ìHYã;WsÀ…rOn¢€"¼ÔmíÜÈ]þÐHËRû°¥»{)ÿ&€&Py{ÌÑ…õ,9Æ?0Gá@PÐy¦ÿÈ:×þ¹/òÏèÞM:îÅ¥Ô'º³Ó‘’ÂÚHÐÀå€Ë=ºäóM6€©ª|?²Ôíâ†æþùRn§‡ÈÆc–iŒ»øêTœxëž´€¦ßžkmîuÛ—{hšYÞ50+:³àùD!³ÁQŽùÖÒü%öHô˜îoÒiwMqk˜Jƒ‘í'¿úÂsì(¦†(ác†4Ž5è¨0á@ € (  «½$Üjßmic%QQáÜc’H9àœŒñü"€"‡Ãñ@-þÏ3!†8£¨;¶näú“»?Pj›QÒ~Û{m;KØ#dTx·IS¸sÔmÀã¹ £Ð! l¯!DŽŽâMÄHÏRsíó7ÐÍyÝk[k÷Q[EÇÔ@£*{­Iÿ ßüóƒþù?ã@ü$WóÎûäÿð‘]ÿÏ8?ï“þ4ÂEwÿ<àÿ¾OøÐÿ ßüóƒþù?ã@ü$WóÎûäÿð‘]ÿÏ8?ï“þ4ÂEwÿ<àÿ¾OøÐÿ ßüóƒþù?ã@ü$WóÎûäÿð‘]ÿÏ8?ï“þ4ÂEwÿ<àÿ¾OøÐÿ ßüóƒþù?ã@ü$WóÎûäÿð‘]ÿÏ8?ï“þ4Îyíè´ÿÙfrobtads-1.2.3/tads3/doc/syscover.jpg0000644000175000001440000001465210476335112016660 0ustar realncusersÿØÿàJFIFÈÈÿÀ´~ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ƹ×üQi0K­sS Ýâº2 ~ Ö¾÷û?ãuI|Õ¿CôjTòúË÷p‹~–ý›I–}NØJŸ/ ||ÑN$W_ü{ð&¼éÑå—*Â'éoò81S§‡vž[ºµ¿!ú£M§[4²üJº”Äp‰YÛè7< Q¤Ü¬ð‰zÛü‰ÃU¥ˆ•¡…^º[ò9‹]wÄ÷“”µ×µ/+¿†ÿ eø¿a¼ÿ¢™/ýõ'ÿKÙËþ€¿/òö†þ—áþAöÏú)’ÿßRñT{9ÐåþAý¡†ÿ eø}†óþŠd¿÷ÔŸüUÎ_ôùha¿è~äa¼ÿ¢™/ýõ'ÿG³—ý~_äÚoú_‡ùØnÿè¦Kÿ}IÿÅQìåÿ@_—ù×ðßô ¿ò°ÝÿÑM—þú“ÿŠ£ÙËþ€¿/òí 7ý/Ãüƒì7ŸôS%ÿ¾¤ÿâ¨örÿ /ËüƒûC ÿ@Ëðÿ û çýÉï©?øª=œ¿è òÿ þÐÃÐ2ü?È>ÃyÿE6_ûêOþ*g/úü¿È?´0ßô ¿ò°ÞÑL—þú“ÿŠ£ÙËþ€¿/òí 7ý/ÃüŽ2Æ}Wíñ\YxnâÿNrp¯ ’®@éí]8ÜÚ¬kÇuîÓÕ}÷_yËŠ£ìqQÃóhíwÛñ+x†þÊñã›OÐ_I™[’veÁ‡_¡®Ú±êÔæù[ò>›I(ºrªª+y˜º ݵåÔÚÆ6«26Ò+D±B¡zðzŸÂ•xV­ocS—åvÃI{(•UN?ŸÎæÍüšžª©6‘àë«kB>S2Ë»þGòÅ:¸a¯Eniy´¿ó<•€ÃGâÄ+ü¿Ì¨š¥®‘0‡Zðåë\–Ž{–‡ÿØëW‡ý/ÃüÃíÿDÂÿþúŸÿˆ£ÛUÿ Õÿ’ÿ˜gÐÿ ¥ø˜}±ÿè˜ßÿßSÿñ{j¿ô¿ò_óìúô¿ó¶?ý ÿûêþ"mWþƒWþKþaýŸCþ‚—áþaöÇÿ¢aÿ}OÿÄQíªÿÐjÿÉÌ?³èÐRü?Ì>ØÿôL/ÿï©ÿøŠ=µ_ú _ù/ù‡ö}ú _‡ù‡Ûþ‰ÿýõ?ÿG¶«ÿA«ÿ%ÿ0þÏ¡ÿAKðÿ0ûcÿÑ1¿ÿ¾§ÿâ(öÕè5ä¿æÙô?è)~ælú&ÿ÷ÔÿüEÚ¯ý¯ü—üÃû>‡ý/ÃüÍgJ¿øe© cAyn»™'’âæPêpAàµu=¦:µ9l£ø7ú‘‘ÚS«µ—ê{WÿÇá¿ Û[lîUÜ·rätúŸ‡½|FeŒxªîWÑh½?àžgŒxªîWÑh½?à5py‘q¨N’I(I Pܼ•/'Éœ^Nxì+¦4bÒ]Z½ú-N˜Ò‹Iuj÷èµ mm¦(M¾‰Ÿî¬ˆžÿÄFxì9¢Xe¶Ÿõä9á¹~Úב4ú¼)Ér·ÆR3Œ2qÏ;â¦8y7fìDpònÍØ¡iz,[”¸¸-%È”?Îwgnr9ãŒVÓ§í;-_Ki§‘´éûNËWÒÚiåù•|G%ÍÅ‚êpx‰´M64"R`ŽL¶ì¸çŒñ×ŠÓ ¡û'KžOmZ4¨F~ÊT¹äöծРµ½W[°Ô$ÔåK«xn vׂ1Úg'héÛóö­sl5Ц¬Ú»[ØÛ7ÃQ¡8ªjÍ­VöùµyÀ(¤Ó(Á¬«þã‡ù~D:ÿü€þ×ÍÇþŽJÖïx¿Hÿé!”ü8ð¯Éžû_|¨R¦H˜€(¦@ `€)á?ò5üCÿ®SÿèÑ_oKà‰~GÕc¿Üpÿ/ȇ_ÿÃÏúù¸ÿÑÉZÏýïéý$2Ÿ‡þù3ßkà• ZJ)€P@ ˜ H€€€ `xEüÿë”ÿú4WÛÒø0Ÿâ_‘õXï÷?Ëò!×ÿäðóþ¾n?ôrV³ÿ{ÅúGÿI §áÄ…~L÷Úø#åE¦H € )€R €˜ € `xEüÿë”ÿú4WÛÒø0Ÿâ_‘õXï÷?Ëò!×ÿäðóþ¾n?ôrV³ÿ{ÅúGÿI §áÄ…~L÷êø#åD € Z`% `€(h(  ¦„XÿÈ×ñþ¹Oÿ£E}½/ƒ þ%ùUŽÿqÃü¿"þ@?ëæãÿG%k?÷¼_¤ôÊ~GøWäÏgÖÚöK›;[Ämåßh-Æ1Œôë_ žçÉÉ’Ùøvh¤6¥{)î ÍË5¥‘7f¨°cÍ“þú4YØ¿aóÖOûèÑdgÞ5Ñ¡¶–{åÕl¢Žñ,‰¸€®d ’'…U,ǰæŽT+Öüg¡è—·Öú”·ð­¤‹ NbcÊQF­ÓvÙóŒóÁ£•Ë× Òí´k Ry/ã³¼‘cWh÷d¶Ð_•sÆOŒfŽT;ØGüõ“þú4YØ}„ÏY?ï£E]Š,@?ë_þú4r »,~[Îx¬¦’e¦2¤aH€<"ÇþF¿ˆõÊý+î)|Oñ/Èú¬wûŽåùëÿòøyÿ_7ú9+Yÿ½âý#ÿ¤†Sðâ?¿&{u×ü†´ï¤Ÿû-|=3ääYñf¿má mRõáãL+"仄\³ª2Ã$­3­üs£»[E9¸¶»ž;wKibË‘<†4Á\©ù”ä‚@çÐ?ô(m¬nÒK™l.f–#t¶ï²/.#)c‘÷JŒ‚3r:æ|HÞմĻկ¡sg<—sVÞà²>ÕY™‘Wrí_QŒ1õ Ô¢ð¬º½â$:õðµ)æÛù’Á —ʉDFÙó4b?›¡ŽäPC\ð,z7ü#—+ªÁ¢YA¾m»JË0 ŒååˆØÛŽFáŒP#ÐÆZ”zZO/›µB2[Hbæ#"0.ÐJ@ÎN(_Uø…áÍ*Þ9ï¯%ŽŠ)·‹i*H¥•› vŒ’zPö›©Gq©C:µÏÙܶ0Íå£ä{b@?@]ÿ¬JʦåÇb Ì  7^𮩨xëJÖ­µ™-ìm@Z‚~lHpCƒŸÖ­5kÓ½Ï:±ÿ‘¯âýrŸÿFŠûZ_üKò>³þã‡ù~D:ÿü>×ÍÇþŽJÖïx¿Hÿé!”ü8ð¯ÉžÝuÿ!­;é'þË_Lù)µý!5›Dîï- >ñ%¬Ià‚ †Át=@­ 9)~YÂAáÉíÉ·Ó4[) c•„²<œHÆå˜r¸Æ(Ñøm¢6&™#Þ<ÜÉu+4$F'Î =€9ç9$,Z°ðc¥EaŸå$7–Ê©a6“µ@ÎcŽ”¡'†l$¶¿ŒÛ/Z&—æÌaBãDüh–“ᆤ´‚ܵöÈIÚ|Õ'e%x¶ÐF`ñšÆý§„´«q YåxZ)q$€³ypù*O ç>ôÏß|!ðýõ¸ŠòëUŸ}˜<“«2Å·hPJñÆG>üœc¸Ó4Øtù/d…¤g¼˜O)sœ¶ÄN?Žh÷ëÒ²©¹qØ‚³((¦„XÿÈ×ñþ¹Oÿ£E}½/ƒ þ%ùUŽÿqÃü¿"þ@Ÿ?ëæãÿG%k?÷¼_¤ôÊvÄ…~Löë¯ù ißI?öZøzgÉÈè+BwÄ':.­ki&‘©ÝA4lísk‘#Ç@qøßKŒó€?ÔeÒµÍuµÈ­|g¦kWv[¤¶° ¶ðýý̶ñt†Þ&†ü¦xÙE•”(]ªU¤!{98èßøgÅ—:ƬÖ:£`É»O"æ-Ë!M¡ðÎ2=@•O+¦´uWù~%Ë%¡ib}vÓ×ÞôûÅ´ø—.­giqák«hg¼ŠÊK‰&`±JøÊÆ>`ÝÈ´ÀJ@Poo Γ<1´Ñ‚ʂ˞¸=©á–?ò6|Cÿ®SÿèÑ_oKà‰~GÕc¿Üpÿ/ȇ^èŸ0 ÿIŸÿG%k?÷¼_øcÿ¤†Sðâ?¿&{í|ò¡@ž)µšûÃ:½¥ªo¸žÎX£L¹™'É­pòP«Ke%ùJ‘§ˆ„å²’‰åº^ñO°X ÒmÒcn‘›6=àÄA„ã~Ö#dkó)ŒrÞìñ)Jî]|úïÓ×úHúJµòÊ“æsv»ÒÎÚïÒëvôkðIG&…âé´›Ká=6 H©.K+'ÌʧtÄŒ¼ó@` ýk ¦åínÝ?àvù±˜Ô•OlÛvÖÞ‹¤WD´zu³i ‹Añ¥ºjO‡l¾Ós`l^ìÜ/™,EV0„y»  åIÉÉõ¬#å¼Ý”¯kußµÿáÆñ˜ 8Þ«²Ÿ5­¢w½þêüÞþ–ÐÓ<3­-—‡-.tIã“O¼·–I…Ò![qf>aʨg0œÎçelêbè¾v§º}ù~7ò¶‰˜VÇaܪÊ5/Í+Yõ_áë£nûY5¢kÖëÀ>\)S  L¤‹Y"|G!@" ;zžkìi·Éƒÿ>—ßÔ°ÿ×B¦±ÿ ‡?õù'þŽJè«þõŒÿ ÿÒJÊþOøF{¥|1ó@ @ @À)w í@ @-%ÿÙfrobtads-1.2.3/tads3/doc/title.jpg0000644000175000001440000003402410476530251016120 0ustar realncusersÿØÿàJFIFÈÈÿÀ=gÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ð¯x Äž8{Åð¾›öæ´g|qìÝ¿}†s´ôô¯°­‰§FÞÑÚç:Mìu_ð ¾%ÿеÿ“ößür°þÒÃ7àÿÈ|’øP_ÿèZÿÉûoþ9Gö–ù¿þAÉ ÿ…ñ/þ…¯üŸ¶ÿã”ia¿›ðä’øP_ÿèZÿÉûoþ9Gö–ù¿þAÉ ÿ…ñ/þ…¯üŸ¶ÿã”ia¿›ðä’8蚇‡5«­'Z·û6¡jÁf‹z¾Ò@#•$èk®HÔŠ”vdµc6¬€;ï|ñ׉4Km_FÐZãO¸Å)º†2à ÚÎP{s\•1´)ÉÆR׿R‹g* ð‡ˆ¼:3®hz‚v’{vT?FÆç]ëÓ©ðI14Ñ…Zˆ(  OøVñ&¢¶:ŸsvF|¸¶©=÷¯¯hŸeÓ *$—íp>ÝÌp®OR;W=,m ²å„®þe8´puÔHP@P@O‚<âoý·þm7íßcÙçþþ8öoÝ·ï°Îv·OJ¶&{Gk&ö)øËÂZ߃5TÓ|Keö+׈L±ù©&P’Ê:©ïÚªhV4Ð4Öæj  € ( € ï|1ð‡Ç(Ðíµ CûVs»Ê›íp&í¬TðÎåHäv®Z˜Úäá9Y¯&R‹g©ØÜišÝ…ô~Uݬ­ ɸ®¤†zWDd¤”–Ì’µPø¿ÇâëFÓ|»p.îŸÊû9å¿à ×%|m.Òz”¢Ùµã€¾4ð¶‹sªÞG§\ÙZÆeí®rcQÔá‚“øf³¥˜Ñ«%{°phòŠï$ôÿ‚?¿áXK¬?ö7ö§ö‚Ä1ö¯'ËÙ¿ý†Îwût®,fë6÷­b£+©ÿ cÿR_þUûMpcðÿ‚W´ò5<+ûMÿoø£GÑÿáû?ö…ä6¾wö–ï/{…Ý(gÎ2**åNýžfÅ-·v3ޏ5åÒ‡´š‡v[vGÎðÖ?õ%ÿåWÿ´×¯ýýÿÃþ Ÿ´òøkú’ÿò«ÿÚhþÆþÿáÿ=§‘à|Oÿ —5O}“ì_mu#Íó6aBýì ýÜôëaé{*j½ˆnîç7[×ðŽ…sâéš-ˆÿH¾bØ å°'ØVuj*ps}Ò»?H´m6ÛGÒ,´Ûü»KHR—ÑT?•|täç''»:‰¿jÂ/ñ&{ÛhöiúÈ7q`p$ÏïWþúù¿àb¾—-¯í(Ùî´ÿ#«3ÇëÐ (Ôf?ù.>ÿ·Ÿý&–¸s/÷i|¿4T>#쟊>/ÿ„Á7þ!ûÛþÊѳùÞVíΩ÷¶œcvzv¯ÃPöÕ;ÚæÒvW<#þÇþ¤¿üªÿöšõ?±¿¿øÁ3öžE›?ÚºÍÛý3ÂW/¬WâCú¢Òy;é?À~ÐôŸüoðGŒf[8ï[O¼”mÚŠ÷“Ø6JŸ¦r}+жµ-muäRšf/Åo€>ñUµÅ‹F×,¦!¶ ›ÑÐp3ýåúk\.eR“´õ_ˆ¥ÏŒ52÷EÕn´ÝRÝí¯md1ËŽU‡ùëÞ¾ŽŒâ¥™‰¥¼·wpÛ[¡yæqh:³€?:m¤®Àýðo‡t„ŸJñC ¤>~¡y·æ™Àù›Ô󠯓­V¦*¯®ÈÝ%xOˆÿjQ¯$Ð,¢´×ìÒ;R”¦OÖ½Jy¹‚,òÐ^¬¤¡EþuÑ,žV÷gø Úá¥ßxoâ_ƒâ8áÔô[å*ÑNà«ʰ?ЃÐ×™(ÔÃÔ¶Í£GÃÿ|~øêãK…ÞM:dr?,cbFÓî#ß÷¯¥Áâ=½.g¿S+3®²B€ ( € ( € ê~ÉPðý†-?ôrV¯àÏü/òw>Æý¨¿ä‰ë¿ïÛÿèô¯žËÞcóü§±ð…}A€P@P@QþÄ_ó:ÛŸþׯ9û?ÐÒŸS‘ý±¿äªYØ*/ý-te?Àâÿ!TÜðªõ ( € ( ½?f?ù!Þÿ·Ÿý)–¾[2ÿy—ËòFðøO‹¾%ÉFñWý…n¿ôkWÑáÿƒð¯ÈÅîiüð¤^4ø£è×@›'ËsŽ3Äg¶q·>õÊÎ5¸E]Ÿl|Mñ®™ð¿ÁK¨5šº!Kk;(HŒ3`áG*…RztóXjÄÔå¿«6nÈùwÅŸ´WˆüM êº5EcC˜–A"ßqbå^å,²9)¦îŒÜÛïø½ÿ$¯Åßö ¹ÿÑm_/„þÓªê~,º1Ú/Ù- òÑ€.Ãè¤ø¯7¯hªK®¦”×SÚ|eñ:Óß¼/áYLe55o´ÈO0–;aüØ}ˆ5æÑÂ:”eW·ôËr³±Gö•ðwü%Ÿ ï%·~£¥fò YT~ñÉÇr¢«.¯ì«+ìô «£á ú“ Qý˜ÿ䏸kþÞôšZá̿ݥòüÑPø§¿j/ù"zïûöÿú=+ÅËÞcóüg±ð…}A€P@Y~Êß.µg>ñËÏueôû‰,è£æˆž¤Èövàæx5ÞÁiÔÖèÇþמŽóF‡Æ:t]Ú†ü¨ÿY8G>êp>è)eX–¥ì¥³Ø*.§Ì^ ¿‡Kñ†…¨] ÛÚ_Á<ƒÕVEcú öëEÊœ¢º¦f·>ÿø·áɼ]ðã]ÑlÜ‹«˜3:©>„¨|¦ª¥Z3}¼•ÑùÙs¶·2ÛÜÆñO”’7d`pAˆ5õɦ®Žr:`=¥‘¡HZG1#T-ò©8ÉÔà~B••î)€P@sG¸¹´Õìn, ÈgG„§Pá\{ç3Iŧµ€ý,×4Ñuœf·¸=×iÍ|d/Ì­Üégæ=}©ÌP@eþÆòKõOû Kÿ¢a¯ÍÿŒ¿Ãú³j{SûcÉT²ÿ°T_ú2ZîÊ€ÿÅþDTÜðªõ>¯ý‰î.GñU³îû$sÀñäñ½•ÃcðTý+ÁÎæ‹ëfkLÄýµÞ#®xY~ü[LXÿ²Yvþ¡«\žü²õB¨|×^ɘP@P@P@O¯ù*ÿ°Å§þŽJÃüÿ…þCŽçØßµü‘=wýûý•óÙoûÌ~‘´ö>¯¨0 ( € (ê?Ø‹þgOûsÿÚõáç?cçúSêr?¶7ü•K/ûEÿ£%®Œ§øü_ä*›ž^¡@P@÷§ìÇÿ$;Ã_öóÿ¥2×Ëf_ï2ù~HÞ ñwįù(Þ*ÿ°­×þjúãu¿81ƼF=ˆP?úŒ=J’‡‘ƒww>ëøKâÈüoðûJÖ«\I•v¾“/ÊüvÉä{_/Š£ìj¸ÅÝ|jðyðOÄ]SK Ø»ý¢ÏŽ /’ ºr¿ðú\mIK¯S+3†®¢OQý˜ÿ䏸kþÞôšZá̿ݥòüÑPø§¿j/ù"zïûöÿú=+ÅËÞcóüg±ð…}A€P@~ÕäЃFÔ§Qâ 6!«n",ƒÔã½ùï_3˜a)óÇáfЕÐïŒôŸË&©¥JšV¾Ü¼¡3Éÿ¦€t?í}AâŒ&a:>쵈J>HñßÃÿøìCâ-6HbfÛÊ|ðËþëŽ3ìp}«ß¡‰§Y^ ÉÅ£”­ÄP@uŸ ¼ ñćFÒîmm§Xv’ä°]ª@8Ú?0®|N"4!Ï$4®Ï§>þΖ>×-µŸjKª]Ú¸’ÞÞ(¶DŽ:1$åˆ<ŽœŽõââsIU‹„“4Œ,IûKüUÓô? ßø[G¹Krù 6ȵ‰‡Í¸â#€:ŒäöÊ˰rœÕI-â9ÊÊÇÆuôf!@ö_ìgÿ$¿Tÿ°Ä¿ú&ùÜßøËü?«6§±ÎþÒ¿ üaã?Úê^ÑþÛdš|p´Ÿi†<8y w£Ýë\»F'»;ùŠqmèyÞ‡û8øúþåRþÖËK„ŸšK‹¤|alŸÊ»'šPŠÑÜ•}%á›? | ð,vZž±E™¦šip%º€ XÆIàÎ1õ5ãT•\e[ÅÀ4VŠ>=øÁã™~ øÞïY(ðÙ…ÚBç&8—¦}É%¹¯¡ÂaÕ jz˜ÉÝœUtˆú{à—ìõo{§[k¾€9ñ8˜Ð‡4†•Ùö'‡~ü=øq¤ ÍBÞÁž š–¬QŽïmß*ûú×ÏTÅâ1²$l¢ôøÅð¾Y‡öõ†ÜãkZÈ#ÿ¾Šmýi}G½îPæˆx§á?€|¤ «K;^u-¥¥l\Ÿ_—åqõÏÔQK^„¬ßɃŠgÇüª|<ñ#éz®%‰Æûk¤Rtõ„t#±õ'è°Ø˜×‡4LZ³!øUÿ%CÁÿö´ÿÑÉOüÿ…þAϱ¿j/ù"zïûöÿú=+ç²ß÷˜üÿ#iì|!_P`}cû;ü¶·ÓÓÄ^8Ó¡¹¸¹Oô]:î ëã‘[‚Ät ÷éàãóß³¤öêkugûIø—ÂWøSÁ¾Ð!º‰±o§Â­ùdŒ ÿxŽ=k£.¥ZKÚÔ“·Ev)µ²8ïÙ÷áÔ?übðêLë¤XF'ºpÒäáPÙ9Éô½tcñN…;ÇvLcv}yªëøYcmÛiš2ÝE ;Ôí@Xû“ëÖ¼¾%¶®Ín¢QGøgñZÙíÐèÚËã%@òîzŽ’/ÔU[†wÕ~_äì‰>#iº5ý'O,ìïe† í¸… @Éï_K‡›(ÉîÑ‹VgбüΟöçÿµëÉÎ~ÇÏô.ŸSÐþ!ü'Ò¼Yñ|QâÛÈâðýŽŸf'–$ewf2?PO¨Ç<”1’¥KÙÓ^óe8ÝÝ’h¿¾i— £é:†‹hØvl‘¬›6Ÿ©?)áqr\òMüÇÍø³ð_ÃÞ4Ñ..tk+M?^d¶¹¶P‰1ÆBÈïusŽ …ÇÔ£+IÝ¢™ðÍÄ2[\KèÑÍta‚¬5ô馮Œ—á·‚u/x¢ J7 óÜ8% Œucú;’+F"4!Ï!¥v}™á߇?¾èBîþ <4@yºž©±›ý’Ü.{ýkç*b±‰Y_Ñ(¤MÅ¿†WógLa÷vÍ ,\ÿ´Ê'‚ÄÇÞåaÍøÅðDñ&‘6¯à»h,5…ÍHm€X.Æ3€£…cÙ†ï×#£ ˜Îœ¹j»¯Å POcãYcx¤xåFI•ea‚ꯢNæ'Þ_³üï ÛÏþ”Ë_/™¼Ëåù#x|'Åß¿ä£x«þ·_ú5«èðÿÁøWäb÷>ððgü+ßíIá ÿ„OûGÉ>göGÙüß+rç>_;s·Û8ö¯—­õŽ_ÞÞÞwýMÕºHÿ…ñ/þ…¯üŸ¶ÿã•ïÿia¿›ðäeÉ#Ýf? øãÀ÷:¶™â}­t{µÅ'Ú¡$èäüËŽqü¼¼Êµ ÉJíz—Öã¿kßkø6ÛÄv‘æïH}³`rÐ9ÿß-´ûÔeUù*:ogùŽkKŸ×Ñž£û1ÿÉqð×ý¼ÿé4µÃ™»Kåù¢¡ñO~Ô_òDõß÷íÿôzW‹–ÿ¼ÇçùÏcá úƒ €-iVr꥔™®fH¥™€©©””bÛèégˆnVË@ÔîŸ µ’Cž˜ Oô¯¦¯$¼Î–~d×ÚÅ6þïK¿†÷M¹šÖòÝйWCê©”T•¤®€ú_ágí,È!Ó¾ DYxUÕ-Ó‘ï"¿Uÿ¾{׉ʾÕ¸Ò5;ŸHÝ[é+ðóE:Zêz=ü^¡ã•Býé^2s¥;­4ÑŸ|mø{7ÿK`…äÒîAšÆfêÉžTŸï)àþ½}N«Óæê·1”lÏ?®²B€ (¹ø;ãÑðëÅ­­6œuÖÏnaùXÜTç;[û½1Þ¹qxo¬Sä½µ]™õWÃ_Þñž©•sú>§;…'`ñÊÇ¢«Œ|ÞÄ öçŠðñmJ1æZ£U4ËŸ>éž6ðÍíí­¤qxŽÖ&–Þâ%ÃM´gË|}àqž‡àÆ *3I¿uŽQº>¯©0 ( ²ÿc?ù%ú§ý†%ÿÑ0×ÎæÿÆ_áýYµ=/Œá[øªþïí/2Ñ.|ï¶ù8ÜÌ»vùmýÞ¹ïQ„Ëþ±~kkÛþJvgíap%ðk*w+©î?—”+¥äϤÿø"öžG¡x+âo>-“£Þéñ‹öREާ?˜'ËnAÇ^Ǿ8ÍrVÂWÂûééÝ IHñ¿Ú'à·…,_ľYIî͉o³dà2“ÉL0yŽÇCu_³©¹3…µGû>øf|UÑí/#Y[³]·£,c P[h>Ä×^>«¥Aµ¾ÄÁ]ŸS~Ò~=ºð?Ut™ Z¶§!·‚QÖ%Æ]Ǹа=«ÃËðʵ_{di7d|+#´ŽÏ#v9fc’O©¯¨1m<Ö·Om,ÏI#b¬Œ:GCI¤Õ˜wþξ=¸ñ߀ĺ£‡Õ´ù>Írý<ÞWú‘Á÷¾_†TjÚ;3x;£äヌ¿5«8ÄvrºÜÀ£¢¬ƒqÐ,°¯{UÕ¡÷2’³>§ý—|3ðªÆïË÷Vcw3÷+’#M «ðó*®uÚè´4‚²>`øõñóÇ>7¼scØJðYBË€pd#»63ŸLÕí`pÊ5Ýîg)]žk]¤ž©û>|D¾ð_,¬ä¸vе–¨¾T,@؃ŒúŒLpcð±­M¾¨¨ÊÌúGö¦ðÔ:ï»Ëï,Í!Öæî!dM§?Uãå•\+¥ÑšM]!ü*ÿ’¡àÿû Zè䯠Åá‘”w>Æý¨¿ä‰ë¿ïÛÿèô¯žËÞcóü§±äÿ³?Á¿¶µ·Œ•͗ཫö“øWâTåmñ“1f,Ä–'$žõôf'бLj­ôßjº-˪>«n¦ŠH‹£ê¬Çþ^NmIÊššèÿ3JoSÒÿio„z—Þ×_ðë‰u;;"K7lyц,6À`Y¸=x²ìlhÞÙ²§ê|~Ë¢j„0º°Ômdÿj)aqù"¾‡Ýœ{¦c°ÝSPºÕu‹ýBvžòáÌ’Êý]R}èŒTRŠØ¦b/ù?íÏÿk׋œýŸèiO©“ûcø“Uÿ„«Oðâ]2iÍ.šàI!wo\¹­2šQäu-­ì¶>r¯`ÌûöQÖî5„°Eu!‘´Û©,Ñ]€+¨üãè|ÎgMB½×Ush=–þ>ئŸñ‹Å0D»U®üì{Ȫçõc^Þ\Øx¿#9n}ûx~+ßkLƒíZ•ÙPÿôÊ1€?ï¢ÿ¥yµG*ª‘¥5¡æ´‚xËÅÿîà´ÐuÙô]4ù‚+Z68ܸ9lŒú]¹±¥I7%w¾¤NížQÿ'‹¿èU׿ð]/ÿ]ÿX£üËïDÙŸ[~Ê“ø‚/^èÞ%°Ôm?³çdûm»ÆLN Ú»€È Ópö¯3TÝE84îºÂöÔùÇöŽÐÓAø¿®Å‚í–ñÿ¦Š ãûëØËêsáã~šÍYŸTþÌòC¼5ÿo?úS-xy—û̾_’5‡Â|]ñ+þJ7Š¿ì+uÿ£Z¾üÿ…~F/sÕÿc?ù*§ýåÿÑÐ×oüþ/Ñ•Os¤ý¶ÿãëÁÿî]8«›iü¿QÔ>a¯lÌ( €:Ÿ…_òT<ÿa‹Oý•†+ø3ÿ ü‡Ï»þ/É+ñwý‚®ô[WËá?ñ#yl~s×לá@}Ûû3xCþ_†Vs\G³PÕÛ&Èä)»_Ápqر¯—ÌkûZÍ-–†ðVGâoÚ†ÛJñ£§ØøcíöÖ³¼)uý£å‰‚œn 圎9é]4ò‡()9Zë·ü]C7þÇþ¤¿üªÿöš¿ìoïþðEí<ƒþÇþ¤¿üªÿöš?±¿¿øÁiä{¿…5­7âOÃÈ/Œì:µ«Eql[vÂr’!8ÁÈÎx5åÕ§,=^^©š't|ãÜøOꮇy“-”íb1½z«~*AükêèÕUi©®¦ YØíf?ù.>ÿ·Ÿý&–¹³/÷i|¿48|GÓßµü‘=wýûý•âå¿ï1ùþF³ØøB¾ À( oý”ü'ˆ¼tºõÜDéš)É'?qGû¿{Û ë^fgˆöt¹ïò. îç¼~Ô>-‹ÃŸ nìRL_ë?è¨<ìàÊßM¿/Õ…yymR²}¥ÍÙ I “_NbP¿þÉÿnt'„ï¦/¥jdýœ;qà6û60G®ß|ù9¦JÕn¿"á-lz퇢­÷Ã{MMcm6õI|r#aø·—ùWSS–³t]E¡ñ}ˆP@èÝã‘d™H*ÊpA£p?J¼}q©ø7A¿¾»º°‚i†1ó´j[õ&¾2´TjJ+dÙж?;|zåÏ?ו/Ü|É©¹õ?ÁÙc¸øMá6€…K2½™P)ýA¯ ­^wîÍc±ùåy¶·“ÛÜ)Y¡‘‘ÁêÖ¾¹4ÕÑÎCL Z\]jvvöÙóæ™ãåÿtWƒŠÊåz–«±¬gÜô‹ ´?‰zH¹ŒÅo¬¬yµÔb‡á_}æ;w“ ŒžV騩E3áè×ÞÖï4Zõ¤†9c'8> ÷`ƒÜúzu#R*QÙ˜5cé/Ø‹þgOûsÿÚõãg?cçúSêr?¶7ü•K/ûEÿ£%®Œ§øü_ä*›ž^¡Ù±Ÿü’ýSþÃÿè˜kçsã/ðþ¬ÚžÇ€~Òò[¼þå×óбɶŸËõCæöÌ€ (©øUÿ%CÁÿö´ÿÑÉXb¿ƒ?ð¿ÈqÜû¿â÷ü’¿Ø*çÿEµ|¾øðÿ7–Çç=}yÎÙüð“x×â“£²´i<Û¢;Bœ¿Ó?t{°®l]ocIϨâ®Ï´þ8x­|ðÇT¾·aÜ‘ý’Ì/‘ÆîÍÿ¯œÁÑöÕ”^Û³i;#óæ¾°À( ¦ÿc_yWš§„®äù&l³ÿÀ‘GÔm?ð¯7¡tª¯FiMô&ý²|‡Òü]i ‹;Â~LlñåϲŠYE}é?TSËÿf?ù.>ÿ·Ÿý&–»s/÷i|¿4L>#êŸÚ;M¾ÕþkVZU•ÍõäÈ-¢i±2…PIÀׇ—Î1ÄEÉÙkùÏcâïøW~5ÿ¡?Äø,›ÿ‰¯£úÕç_z1åe«…¾;½}°øKZSÿM­!ù¸2ÆP[Í+=3Àß³7ˆõ+ˆfñ]ÄEŽrñFâ[‚=2£êIÇ¥qVÍiÅZš»ü TßSé ü!ð*#>¾ø‡âɵK½ÑYǘìí³Ä1çøêO¯°ô¸\4hC•oÔÂNìÉðÇ~dJßÿF-i_øRô`·=÷ãoì÷yý¡q­øš ˜¼ÚZ­îbÏÙê;dp<¬f­É[ïÿ2åÇΚ†‰ªé×_eÔ4ÛÛ[Û|© dlú`ŠõãRWNæv=“övøQ¯j~4Ó5ýVÂãOÒ4Ù–åd¸Œ£O"œ¢ <‘œzq޵çcñ”ãMÂ.í—»Üõ¿ÚÿÄiÿ`Ñ·ƒwª].ÔÏ>\gs7ýõ°~5Á•Sr­ÏÑ"ê=Œ+èÌB€ õ_‚Ÿ âø›¥x™cÔÇQÓ¾Îm™—tm¿ÍÜuþÁ9à×3ðòŽ—Nÿ¡QÊ>$ø)ãíᣓÃ÷7у…›Oz¿¸ óÄ ºxú_½t‡_<[âmfÝ5].ïFÒ•Á¸¸¼ŒÄû{„Fä±8Ç©¨Äf©ÇÝw~@ Ùöo‹uÝ;Àþ ¼ÔîvÅg§Ûâ8ó÷ˆDäàWÎR§*ÕVí›7d~oÝO%ÕÔ×±y¦rîÇ»’kìRIYäTÀ( ²ÿc?ù%ú§ý†%ÿÑ0×ÎæÿÆ_áýYµ=Žö­ð§ˆµ¿‰6—Z.«j6˦D†[K9%@ÂI PFpGõÕ–V§ -JIkßЙ¦Ùãq|8ñ´Ž|!âO÷´éT~ekÑxª?ξôG+=‹à—À-lø’ÇZñ¥¢ØéÖr ’ÎF $î9\€pª Ï'Ç9¯;™C‘“»eÆúžýñ«ÅöÞ øy«_M*­äе½œyåå`@ÀïŒî>Àוƒ ëUQ[u.NÈøoá—ŠÁ¾;Ñõà¥ãµ›÷È:´l ¸ûXãßôØš>Ú“‡sìî}Åñ+šoÅ_‡fÒÖê"· ·Z}âòªøù[èA ÷Á=ëæpõ¥†«v¼™³\ÈøgÅþ ñ„/¤µ×ô«›b„.Âbz«Žükééb)ÕWƒ1i¡Þð/‰<]}¶…¤ÝO½€3ÊÅõg<J®"%y°I³îo‡>Ó>ü;[;›¸–;ek«ûÇùUŸ3}ø½|Î"´±5n–û%ʆ~$ø™ücãc^e(—s“7UŒ¨¾Õôøz^Ê’‡cîî}9û!øÚßQ𤾺•VÿMf–ÝIæHY²qî¬N}˜W‰šáÜgíVÏó4¦ô±Æ~Ñ_µXüCyâ ÙI}czÆk«XtH~ó²“Ï “Úºrü|9:ŽÍ qêž–Âñ®¾Ì¶—ç8ò„gv~kÖæ¯s3èŸÙÏඪ|AgâYÉcidÂ[KI×l“H9We<ª©äg;W‘˜cãÈéÓwosHG«:ßÚûưXxZ ÚÊ­}¨ºËr€ó(r3þóe5†UAÊ~Õì‡Qéc柅_òT<ÿa‹Oý•ì⿃?ð¿ÈÎ;Ÿc~Ô_òDõß÷íÿôzWÏe¿ï1ùþFÓØùáWÄM[á߈VÿMc-œ¤-Ý›6tþŒ9Ãv÷ƒïâ°°¯YoјÆV>ÖGð§Æ_‡ç…¼Òî‡ àKk(øë®~„àóó½ÂUì×âm¤‘ñgŇ:¯Ã¯+ðf±˜–´½UÂL¿Ñ‡qý5ôx\T1ºßª1”lj|,øSyñúíÖ‘y:ž$B8&Iƒ$nþòñÛéÖ£ŒXyÅIhÇ݇Š<#â ^µ®¿¤]ÙJ’3±ÿÝqò°÷×E:ôê+ÁÜM4}iû$EâX< {½ ÄZZΧM U¶Kí™ÛŽÙ-^j麩Ã~¦°½ý­ä¶‹Ó-¶<Ô²…n1ýü3ÿ)^ŽUa¯r'¹ÝþÄ_ó:ÛŸþ×®\çì|ÿAÓêr?¶7ü•K/ûEÿ£%®Œ§øü_ä*›ž^¡Ù±Ÿü’ýSþÃÿè˜kçsã/ðþ¬ÚžÇ€~Òò[P½Òõ Ÿ³ÞØÝ[ÜgTв6}0FkÞSŒ•Ó2±Öø3áOŒ¼]siº-Ì6ÍÖîí 0¨õÜÃæú.OµsÖÆQ¤½é E²§ÅOxÆ}Ýý±í቞m›C3 cè ÅV¿¶§Ïk•±¿f?ù!Þÿ·Ÿý)–¾{2ÿy—ËòFÐøO‹¾%ÉFñWý…n¿ôkWÑáÿƒð¯ÈÅîz¿ìgÿ%CTÿ°<¿ú:àÍÿ‚¿Åú2©îtŸ¶ßü}x?ý˯çc“m?—ê:‡Ì5í™…PSð«þJ‡ƒÿì1iÿ£’°Åáã¹÷Åïù%~.ÿ°UÏþ‹jù|'ñáþ$o-Îzúóœ(êïØ·Cµ]#_רn½yÖÉIq‡8>å†Ýàç4aÒ×5¦º˜?¶Ž·s'ˆô- ;láµ7„÷ÝÙ“Ÿ N?Þ5®OMrJ}oaTgÍõì™…»àMvëÃ^1Ñõ‹ûûK”p¹ÀuÎO±RGãYW¦ªSp}PÓ³?A~#hV¾%ð6·¤ßÜÜZ¾(ÀnVá€?…|ž£§V2]ÍÚº>.ý˜ÿ䏸kþÞôšZú<ËýÚ_/ÍÃâ>ô¯–7 Éñ&³ý‹eö#ÏëòïÛý iNîÂlùËâí)¬i×ÓéÚ…em,|‹™šn½Â€¸üs^½ ª\Ò‘››>xñ_еÏê&÷ÄZ•ÅôüíóåŒÊ£…À õéQ…%h+Û{˜•¨‰ì.çÓïí¯-$òîmäYb|µ”ä:ŠRŠ’iöGÀŒúŸoÛFÖ´ëu¼†-íy•}cÁÁã¨8öó¸ì h.x½;FW=Ú¼²ÎWâw‹Á>½×Ì^µ¾1 “ËŸ|å[á¨ûjа›²¹ð?|c«øçÄ3júìáça¶8Ðb8PtT‡êzšúª!F°0m³œ­„Pª|ø?ÃÍ^òôȯàÕž4¦6B¥‚pGü´9ì:W;«Å;ÚÅFV>ì³›í6Ï·o˜öç8ÈÍ|»Vv7+ë—ÿÙz=íÿ—æýš—fí»°3ŒóŠp4”{ðGÅ¿ŠzßÄ}A ø[M.&ÞÂ&Ê¡é¹ñ7¿äM}V :ÓWÜÂRlóêë$( €>ËýŒÿä—êŸö—ÿDÃ_;›ÿ‡õfÔö=꼢Ȯ¦ò-Þ]»¶Œã8Í4®ÀðïŠ?gð{ýšÏ@ŠâáÁ ,·GjŸR¡r1^ž.Uur"S±ò¯/øÒox&ë[¶³KÉÑ–4ŽG* ·œ@ôã>¢ºp”zŠ ØRvGçö½«ÞëÚÍæ«ªÌg¾»Ë,„c$úð…}\!EF;#»ŸJ~Ä_ó:ÛŸþׯ9û?ÐÒŸS‘ý±¿äªYØ*/ý-te?Àâÿ!TÜðªõ>ËýŒÿä—êŸö—ÿDÃ_;›ÿ‡õfÔö<ö“ÿ’Ùâo÷áÿÑ׫—»Gúêg=Ï;ÓïntÛè/l'’ÞîE,mµ‘‡B uÊ*JÏbOµ¿gŠÚ—Ä;›=jν±Eßu`MîS9ÁÇ óxüh4âôfÑ•Ïg¯8³Œø¯ãfð…eÕÒÀ_2¢#/–3ëœéÂÐöóä½…'d|ãÿ_xßÅ7zî«\Üm RU@PI=¯¨¡F4`¡îî}§û1ÿÉð×ý¼ÿéLµó™—û̾_’6‡Â|]ñ+þJ7Š¿ì+uÿ£Z¾üÿ…~F/sÕÿc?ù*§ýåÿÑÐ×oüþ/Ñ•Os¤ý¶ÿãëÁÿî]8«›iü¿QÔ>a¯lÌ(ÿÙfrobtads-1.2.3/tads3/doc/t3changes.htm0000644000175000001440000141334512145504453016676 0ustar realncusers TADS 3 Change History

TADS 3 Change History

This is a list of changes to the TADS 3 core system: the language, the built-in classes and functions, the compiler and related build tools, the debugger, and the interpreter virtual machine. The change history is organized by release, with the most recent release first.

(This page only covers changes to the core TADS language and Virtual Machine engine. There are separate release notes for most operating system install packages - HTML TADS, FrobTADS, CocoaTADS, etc. For changes to the Adv3 library, see Recent Library Changes.)

Changes for 3.1.3 (May 16, 2013)

  • The compiler now allows nested embedded "<< >>" expressions in strings. Nesting wasn't allowed in the past; the compiler now allows nesting up to a depth limit (currently 10 levels deep). For example, this is now valid:
       local x = 1;
       "This is the main string. <<
          "This is a nested string: x=<<x>>."
       >> Back to the main string.";
    
  • In the past, HTML <PRE> blocks were problematic if you wanted to use <PRE> for precise indentation. The complication was that the output formatter in the VM pre-filtered the text sent to the HTML parser to consolidate each run of whitespace into a single space. If you wanted to use spaces in a <PRE> block for alignment purposes, you had to use quoted spaces ("\ " sequences) to make sure that all of the spaces were sent through to the HTML parser.

    Now, the VM formatter layer watches for <PRE> tags. Within a <PRE> block, the VM formatter passes spaces and newlines through to the HTML parser without any filtering. This change addresses bug #0000178.

    Note that there's another slight complication if you're writing multi-line preformatted blocks with spacing for indentation. The compiler normally removes the spaces that follow a line break within a string. The new compiler behavior described below gives you more precise control over this, as does the new #pragma newline_spacing(preserve) described later.

  • In the #pragma newline(collapse) and #pragma newline(delete) modes (see below), you can now override the default line break handling for a particular line in a string by writing an explicit \n sequence at the very end of the line. When a line within a string ends in \n, followed by a line break and the continuation of the string on the next line, the compiler preserves the whitespace at the start of the next line exactly as written. For example, consider the following string definitions, assuming that we're in the normal "collapse" mode for newline spacing:
       f()
       {
         local a = 'a
              b';
         local x = 'x\n
              y';
       }
    

    For the first string, the compiler converts the line break after the letter "a" and all of the whitespace at the start of the next line into a single space, so the string's actual contents end up as though you had written local a = 'a b';, with just one space between the two letters. But in the second string, we've written an explicit \n at the end of the line, telling the compiler to put an explicit line break into the string and to preserve the subsequent whitespace. This string is equivalent to writing local x = 'x\n     y';, with the five spaces at the start of the second line kept intact.

  • The #pragma newline_spacing directive now has three modes:
    • collapse mode (equivalent to the old on mode) collapses each line break within a string and any subsequent run of whitespace (at the start of the next line) into a single space.
    • delete mode (equivalent to the old off mode) removes each line break and any subsequent run of whitespace, mashing the text of the two lines together without any spacing.
    • preserve mode (new in this version) preserves spacing exactly as written in the source code, with the line break replaced by a \n (newline) character, and subsequent whitespace at the start of the next line kept intact.

    Existing code that uses the pragma will continue to work without any changes, because the former "on" mode is equivalent to the new "collapse" mode, and the former "off" mode is equivalent to "delete" mode. The compiler continues to accept the old names, although new code should use the new names for better clarity.

  • The Web UI library code had a bug that prevented dialogs (such as the display preferences dialog) from displaying properly on Internet Explorer 9 and later. This has been corrected. (bugdb.tads.org #0000194)
  • The compiler error message for a symbol token containing an accented letter or other non-ASCII character has been improved. In the past, the compiler treated a non-ASCII character as though it were a symbol delimiter; for example, this had the effect of splitting naïve into three tokens: na, ï, ve. The compiler then reported an "invalid character" error for the ï character. The compiler now assumes that any non-ASCII character that's adjacent to a character that's valid in a symbol (i.e., not separated by any spaces or punctuation marks) is meant to be part of the symbol token, so rather than reporting the invalid character in isolation, the compiler now reports the error in terms of the entire token containing the non-ASCII character. This should make it easier to pinpoint the source location of this type of error. The compiler also displays the numeric Unicode value of the non-ASCII character, in case the character can't be displayed in the console character set (on many systems, the command line console uses a limited character set, such as Latin 1, that can't display the full range of Unicode characters that could occur in the source). (bugdb.tads.org #0000185)
  • The IANA time zone database included in the base TADS release has been updated to version 2013c (2013-04-20).
  • On Windows, the interpreter behaved unpredictably (sometimes crashing) when certain Date or TimeZone operations were attempted and the Windows date/time locale was set to a time zone that uses standard time year round (i.e., the time zone doesn't currently make annual transitions between standard time and daylight time). This is now fixed. (There were actually two separate problems contributing to the bug, one specific to Windows and one in the portable code; both are fixed.) (bugdb.tads.org #0000171)
  • In HTTPRequest.sendReply(), automatic content detection of the MIME type based on a file's contents didn't work properly for files shorter than 512 bytes. This has been fixed. (bugdb.tads.org #0000187)
  • For Web UI mode, a bug in the HTTPServer class caused the whole server to shut down if any individual HTTPServer was terminated, either explicitly by calling its shutdown() method, or automatically when the object was deleted by the garbage collector. This prevented any more messages from being delivered via getNetEvent(), even if other server objects were still listening for connections. This didn't actually affect most games in practice, because most Web UI games create one HTTPServer object that listens for connections throughout the server lifetime, but it was still incorrect behavior. This has been corrected.
  • The compiler crashed when presented with a "try" statement with an empty "finally" block. This is now fixed. (bugdb.tads.org #0000176)
  • Starting in version 3.1.0, TADS Workbench didn't run under Windows 2000 or earlier versions of Windows, due to a dependency on a newer Windows feature that didn't exist until Windows XP SP1. The dependency has been removed and replaced with code that should work on Win 2K as well as newer systems, so Workbench should once again be able to run on Win 2K. (bugdb.tads.org #0000186)
  • The macro preprocessor incorrectly expanded macros containing string embeddings in certain complex cases. In particular, if the last macro argument was used within an embedded expression within a string within the macro expansion, and another separate embedding followed, any subsequent mentions of a macro argument were not expanded correctly. The minimal case was something like this:
    #define M(a, b) '<<b>><<>>' a
    

    This has been corrected. (bugdb.tads.org #0000180)

Changes for 3.1.2 (August 20, 2012)

  • New syntax allows defining an object within an expression. This type of object is known as an inline object, and is essentially the object analog of an anonymous function. Methods of inline objects behave like anonymous functions in that they can access locals from the enclosing scope. Inline objects are useful for a lot of the same coding patterns where anonymous functions are useful, but allow for more flexible APIs, since an object is a more general way to express context information. The new syntax also has the side benefit of letting you define new objects at run-time with the dynamic compiler: if you compile and evaluate an expression containing an inline object definition, the result will be an instance of the object defined in the expression.

    For full details, see inline objects in the System Manual.

  • The new String method match() complements the find() method by checking for a match at a given position in the subject string rather than searching for a match.
  • FileName.getFileInfo() now returns additional information on the file. The new fileAttrs property of the FileInfo object provides information on file attributes used on some operating systems: the "hidden" and "system" attributes, and whether the program has read and/or write access to the file.
  • The documentation for FileName.removeDirectory() has been changed to specify that the method definitely fails if deleteContents is nil and the directory to be removed isn't already empty. In the past, the documentation stated that the behavior for a non-empty directory varied by platform. In fact, there was no variation in practice - all of the existing platforms already failed if a directory was non-empty and deleteContents was nil - so the warning about variable behavior was just hedging in case any future ports didn't work this way. However, the internal porting interface that implements directory removal now specifies this exact behavior, so there's no longer any need for the hedge.
  • In the past, when #pragma once checked to see if the same file had been included previously, it was sensitive to case differences in the filenames as written in the #include directives. The comparison now uses the local file system conventions; on Windows, for example, the comparison ignores case differences.
  • The IANA time zone database included in the release has been updated to version 2012e (2012-08-03).
  • In 3.1.1, the debugger crashed if a run-time error was thrown by a non-Adv3 program. This has been corrected. (bugdb.tads.org #0000157)
  • The UTF-8 character output mapper produced incorrect output when mapping a string that ended with a non-ASCII character. This is now fixed. (bugdb.tads.org #0000159)

Changes for 3.1.1 (July 14, 2012)

  • New syntax lets you create constant regular expression values: R"pattern" and R'pattern' are equivalent, and define static, pre-compiled RexPattern objects. The compiler converts a string of the form R"..." or R'...' into a static RexPattern object, which you can then use in any context where a pattern is required. For example:
      local str = 'test string';
      local title = str.findReplace(R'%<%w', {x: x.toTitleCase()});
    

    To define a regular expression literal, the R must be capitalized, and there must be no spaces between the R and the open quote. "R" strings can't use embedded expressions (the << >> syntax). The triple-quote syntax can be used with R strings, as in R"""...""".

    In the past, you could achieve a similar effect by defining a static property and setting it to a new RexPattern() expression. You can still use that approach, of course, but the new syntax is more compact and eliminates the need to define an extra property just to cache the RexPattern object. Using the new syntax also generates slightly more efficient code, since it doesn't require a property lookup to retrieve the RexPattern object. (There might be other reasons to use the property approach in specific cases, though; for example, in a library or extension, defining the pattern via a property provides a clean way for users of the module to override the pattern if they need to replace it with a customized version.)

  • The basic integer arithmetic operators (+, -, *, /, and the corresponding combination operators such as ++ and +=) now check for overflow, and automatically promote the result to BigNumber when an overflow occurs. In the past, overflows were simply truncated to fit the 32-bit integer type. It was very difficult to detect such cases or to do anything sensible with the results; you really had to just be careful to avoid overflows, which isn't easy when working with external or user-entered data. The new treatment ensures that the results of the arithmetic operators will always be arithmetically correct, even when they exceed the capacity of the basic integer type. This is more in keeping with the TADS philosophy of providing a high-level environment where you don't have to worry about hardware-level details such as how many bits can be stored in an integer.

    This change should be transparent to existing programs, since (a) BigNumbers can for the most part be used interchangeably with integers, and (b) any program that encountered an integer overflow in the past probably misbehaved when it did, since there was no good way to detect or handle overflows. The effect on most existing programs should thus be to allow them to correctly handle a wider range of inputs. In some cases, the effect will be to flag a run-time error for a case where a value really does have to fit in an integer, where in the past the code would have failed somewhat more mysteriously due to the truncated arithmetic result. The new handling should be an improvement even in error cases, since the source of the error will be more immediately apparent than in the past.

    There's a slight impact on execution speed because of the extra checks required for arithmetic operations, although typical TADS programs aren't arithmetically intensive enough to notice any difference. What's more, the VM-level checking eliminates the need for extra program code to do bounds checking, so this could end up being a performance enhancement in situations where overflows are a concern.

  • The compiler now automatically promotes any integer constant value that overflows the ordinary integer type to BigNumber. This applies to values that are explicitly stated (e.g., "x = 3000000000;") as well as to constant expressions (e.g., "x = 1000000000 * 3;"). The compiler shows a warning message each time it applies such a promotion; BigNumber values aren't necessarily allowed in all contexts where integers are normally used, such as for some built-in function and method arguments, so the compiler wants you to be aware when it substitutes a BigNumber for a value you stated as an integer. You can remove the warning on a case-by-case basis by explicitly stating the value as a BigNumber constant in the first place, by including a decimal point in the number (e.g., "x = 3000000000.;").

    Integers specified in hex or octal notation (e.g., 0x80000000 or 040000000000) aren't promoted if they can fit within a 32-bit unsigned integer. Hex and octal are frequently used to enter numbers with specific bit patterns, so the compiler assumes that's your intention with these formats. A hex or octal number that's over the 32-bit unsigned limit of 4294967295 will be promoted, though, since there's no way to store such a large value in a 32-bit integer regardless of its signedness.

  • The compiler now pre-calculates the results of arithmetic expressions involving BigNumber constant values. (This is known as "constant folding".) If an expression contains only constant numeric values, with any combination of integers and BigNumber values, the compiler will pre-calculate the results for the ordinary arithmetic operators (+ - * / %) and the comparison operators (== != < > <= >=). In the past, the compiler deferred calculations involving BigNumber values until run-time; constant folding improves the execution speed of affected expressions for obvious reasons.
  • You can now use sprintf format codes directly within embedded expressions. To do this, start the expression with a "%" code immediately after the angle brackets, with no intervening spaces. For example, "x in octal is <<%o x>>" will display x's contents in octal (base 8). The compiler simply converts this sort of expression into a sprintf() call with the given format code, so you can use the entire range of format code syntax that sprintf() accepts.
  • The new sprintf format codes %r and %R generate Roman numerals for integer values, in lower- and upper-case.
  • The String methods find() and findReplace() now accept a RexPattern object as the search target. An ordinary string can also still be used, of course. This essentially makes String.find() and String.findReplace() replicate the functionality of rexSearch() and rexReplace(), respectively; the main benefit is that the syntax for the String methods is a little cleaner and more intuitive, since the subject string is moved out of the parameter list.

    This is especially convenient with the new R'...' syntax for creating static RexPattern objects. For example, str.find(R'%w+') finds the first word in a string.

  • The String method findReplace() and the rexReplace() function each now take an optional additional argument, limit, specifying the maximum number of replacements to perform. If the limit argument is omitted, the ReplaceOnce and ReplaceAll flags determine the limit; if limit is included in the arguments, the ReplaceOnce and ReplaceAll flags are ignored, and the limit value takes precedence. limit can be nil to specify that all occurrences are to be replaced, or an integer to set a limit count. Zero means that no replacements are performed, in which case the original subject string is returned unchanged.
  • The new String function findAll() searches a string for all occurrences of a given substring or regular expression, and returns a list of the matches.
  • Two new functions implement reverse searches in strings: rexSearchLast() and String.findLast() These functions search a string from right to left, allowing you to find the last (rightmost) match for a substring or regular expression pattern.
  • The rexGroup() function can now be used to get information on the entire match, by passing 0 for the group number. rexGroup(0) returns the same format as the other groups, but contains the text and location of the entire match rather than of a parenthesized group within the match.
  • TADS now has more complete support for Unicode case conversions. Unicode defines two levels of case conversions; the older, simpler level provides one-to-one character mappings between upper- and lower-case letters, while the newer level allows for characters that expand into multiple replacement characters when converting case. The canonical example is the German sharp S character, ß, which changes to "SS" when capitalized - there's no such thing as a capital sharp S in standard German typography. For proper case conversion of a string containing an ß, then, each ß character expands to the two-character sequence "SS". There are similar examples in other languages, some involving other ligatures and some involving accented characters that don't have upper-case equivalents.

    In past versions of TADS, only the one-to-one conversions were supported. Characters such as ß that required more complex handling were generally left unchanged in case conversions. TADS now supports the full one-to-N mappings. This won't affect most text, since most characters have simple single-character replacements when converting in upper or lower case.

    TADS also now supports Unicode "case folding", which is a separate mapping for case-insensitive string comparisons. In the past, TADS generally approached case-insensitive comparisons by converting each character to be compared to a common case (upper or lower), according to which character was the "reference" character in the comparison. Now, TADS uses the Unicode case folding tables instead, and converts each character to its "folded" form for comparison. The folded form of each character is defined individually in the Unicode character database tables, but in nearly all cases it's the same as converting the character to upper case and then back to lower case.

    The new case conversion and case-folding support affects several areas:

    • Regular expressions: when the <nocase> flag is specified, the matcher uses case folding to match each contiguous string of literals. (In past versions, characters were compared by converting to the pattern character's case using the one-to-one conversions only.)
    • The String methods toUpper() and toLower() use the new case conversion tables. In the past, these used the older one-to-one case conversion tables.
    • The new String methods toTitleCase() and toFoldedCase() use the new tables.
    • The new String method compareIgnoreCase() uses the full case folding tables to perform a case-insensitive comparison.
    • The StringComparator class now uses full case folding when the comparator isn't case-sensitive. In the past, caseless comparisons were done by converting each input character to match the case of the corresponding dictionary character, using the one-to-one conversions only.

    The new support only includes the unconditional case mappings. The Unicode tables define a number of case mappings that are conditional, some on the language in use and some on string context. TADS doesn't currently support any of the conditional mappings.

  • The new String method toTitleCase() converts each character in the string to "title case". This is the same as upper case for most characters, but varies for some characters. For example, a character representing a ligature (e.g., the 'ffi' ligature character, U+FB03) is converted to the corresponding series of separate letters with only the first letter capitalized (so U+FB03 becomes the three separate letters F, f, i).
  • The new String method toFoldedCase() converts each character in the string to its case-folded equivalent, as defined in the Unicode standard. The point of case folding is to erase case differences between strings, to allow for case-insensitive comparisons. For most strings, the case-folded version is the same as the lower-case version, although not always; characters that don't have exact equivalents in the opposite case (e.g., the German sharp S, ß) are generally handled as though they were first mapped to upper case and then back to lower, so the result will sometimes expand one character to two or more characters in the folded version (e.g., ß turns into ss, so that 'WEISS' will match 'weiß' in a case-insensitive comparison).
  • The new String method compareTo() compares the target string to another string, returning a negative number if the target string sorts before the second string, 0 if they're identical, or a positive number i the target string sorts after the other string. C/C++ programmers will recognize this as the standard strcmp() behavior. You can get the same information using comparison operators, but compareTo() is more efficient for things like sorting callbacks because it determines the relative order in one operation. For example:

       lst = lst.sort(SortAsc, {a, b: a < b ? -1 : a > b ? 1 : 0});
       lst = lst.sort(SortAsc, {a, b: a.compareTo(b)});
    

    The two callbacks have the same effect, but the second is a little more efficient, since it always does just one string comparison per callback invocation.

  • The new String method compareIgnoreCase() compares the target string to another string, using the case-folded version of each string. It returns the same type of result as compareTo() - negative if the target string sorts before the other string, zero if they're equal, and positive if the target sorts after the other string, in all cases ignoring case differences. This is equivalent to calling compareTo() using the results of calling toFoldedCase() on each string, but compareIgnoreCase() is more efficient since it never constructs the full case-folded versions of the two strings (it does the case folding character by character as it compares the strings).
  • The new function concat() returns a string with the concatenation of the argument values. This is essentially the same as using the "+" operator to concatenate a series of strings, but it's more efficient when combining three or more values, since the "+" operator is applied successively in pairs and so has to build and copy an intermediate result string at each step.
  • The new function abs() returns the absolute value of an integer or BigNumber value.
  • The new function sgn() returns the SGN (sign) of an integer or BigNumber value. The SGN is 1 for a positive argument, 0 for zero, and -1 for a negative argument.
  • The new t3make option -FC automatically creates the project's output directories. If this option is specified, the compiler creates the directories specified in the -Fy, -Fs, and -o options, if they don't already exist. This makes it simpler to move a project to a new directory or onto a new machine, since -FC makes it unnecessary to create the output directories manually.
  • HTTPRequest now recognizes GIF image files when sending a reply body. When the caller lets HTTPRequest auto-detect the MIME type in sendReply() and related methods, the class will now use "image/gif" when it detects a GIF file. As with other binary file types, the class recognizes GIF files by looking for the format's standard signature near the start of the reply body data. (bugdb.tads.org #0000139)
  • The new HTTPRequest method sendReplyAsync() lets you send the reply to an HTTP request asynchronously, in a background thread, so that the main program thread can continue to service other requests while the reply is sent. This is useful when the reply contains a large content body, such as a large image or audio file. Most browsers use background threads on the client side to download large media objects, so that the UI remains responsive to user input while the objects are downloaded; with the TADS Web UI, this means that the browser can generate new XML requests while image or audio downloads are still in progress. The HTTPRequest sendReply() method is synchronous, meaning that it doesn't return until the entire data transfer has been completed. This means that the program can't service any new XML requests that the browser sends during the download until after the download has completed and sendReply() returns, which makes the UI unresponsive for the duration of the download. sendReplyAsync() addresses this by letting you initiate a reply and then immediately return to servicing other requests, without waiting for the reply data transfer to finish.

    The Web UI library uses the new method to send replies to requests for resource files, since these files are often images, sounds, and other media objects that can be large enough to take noticeable time to transfer across a network. Resource files are the mechanism that most games use to handle their HTML media objects, so most game authors shouldn't have to use sendReplyAsync() directly.

  • The new class FileName provides a portable way to manipulate file names and directory paths, and methods to operate on the corresponding file system objects named. Each operating system has its own file path syntax, so it's always been difficult to use ordinary strings to construct and parse filenames that involve directory paths. It's too easy to make assumptions that tie the program to a single operating system; the alternative has been to write a bunch of special-case code to handle the syntax for each OS that you want to support. The FileName class helps by providing methods for common filename construction and parsing operations, which are implemented appropriately for each operating system where TADS runs. TADS has always had many of these portability functions internally for its own use, mainly for the compiler and other tools; the FileName class makes them available to TADS programs. These new features will probably be of little direct interest to game authors, but could be useful to library and tool developers.

    In addition to building and parsing filenames, FileName provides access to a much more complete set of file system functions than was previously available. The new class lets you create and delete directories, list directory contents, retrieve file system metadata (file sizes, types, modification dates), and move and rename files. As with the previously existing file access functions, the new functions are subject to the file safety restrictions to reduce the risk of malicious use and give the user control over the scope of a program's file system access.

  • The inputFile() function now returns a FileName object to represent the file chosen by the user, rather than a string. Existing code shouldn't be affected unless it's unusually dependent upon the result being a string, since the FileName object can be passed to any of the functions that open files (including the File.openXxx methods, saveGame(), etc) in place of a string, and is automatically converted to a string containing the file name in most contexts where a string is required.

    A FileName returned by inputFile() has an internal attribute that marks it as a user selection, which grants special permission to use the file even if it wouldn't normally be accessible under the file safety settings. A manual selection via an inputFile() dialog overrides the safety settings because of the user's direct involvement; the user directly expresses an intention to use the file in the manner proposed by the dialog, which is an implicit grant of permission.

  • Several of the system-level functions that access files are now subject to file safety restrictions: saveGame(), restoreGame(), setLogFile(), setScriptFile(), and logConsoleCreate() now enforce the appropriate read or write permissions according to the file safety settings.

    In the past, these functions didn't enforce file safety settings, mostly for practical reasons: these functions are all used by the Adv3 library to operate on files that are normally selected by the user, so it would have been confusing to deny access in cases where the user happened to choose a file outside the sandbox. This was balanced against the lower inherent risk with these functions, as compared to the general-purpose File methods. The game program can't use these functions for arbitrary read/write operations; the actual data content they read/write is largely under the control of the system, so there's probably no way to use them to do something like planting a virus or stealing private data. However, since some of them create new files, they could still be used for certain types of mischief, such as overwriting system files or destroying user data.

    The thing that's changed - that allows us to bring these functions into the file safety mechanism - is the new ability of inputFile() to mark its filename result as coming from a manual user selection, and the corresponding file safety enhancement that grants access permissions to such manually selected files. This ensures that user file selections for Save, Restore, etc. will still work properly, even when they're outside the sandbox.

  • The new Date built-in class provides extensive functionality for parsing, formatting, and doing arithmetic with calendar dates and times; it works with the new TimeZone class to convert between universal time and local time anywhere in the world, correctly accounting for historical changes in time zone definitions and daylight savings time. This should be especially useful to authors writing games involving time travel, or set during the early morning hours on certain Sundays in March or November.
  • The new function makeList() constructs a list consisting of a repeated value.
  • The system library's main startup code (in lib/_main.t) now allows the main() function to omit the argument list parameter. The library now simply calls main() with as many arguments as it requires, providing the standard "args" parameter if needed and otherwise omitting it. For little stand-alone programs that don't use the Adv3 library, this simplifies the code a little by letting you omit the argument list parameter if you don't need it.
  • Lists and vectors can now be converted to strings, explicitly with toString() as well as in contexts where non-string values are automatically coerced to strings, such as on the right-hand side of a "+" operator where the left-hand side is a string value. The string representation of a list or vector is the concatenation of its elements, each first converted to a string itself if necessary, with commas separating elements. For example, toString([1, 2, 3]) is the string '1,2,3'.
  • In implicit string conversions, the value true is now acceptable, and is converted to the string 'true'. In the past, true worked this way with the toString function, but not in implicit string conversions (such as when a value is used on the right-hand side of a "+" operator when the left-hand side is a string: 'x=' + true caused a run-time error in the past, but now returns 'x=true').
  • The toString function and implicit string conversions now accept properties, function pointers, pointers to built-in functions, and enum values. If the reflection services module (reflect.t) is included in the build, these types will be passed to reflectionServices.valToSymbol() so that they can be translated to symbols when possible. If reflect.t isn't included in the build, or if valToSymbol() doesn't return a string value, these types will be represented using a default format that indicates the value's type and an internal numeric identifier for the value, such as "property#23" or "enum#17". Any object type that doesn't have a specialized string conversion defined by the built-in object type is now handled the same way, so an object without special formatting is represented either by its symbolic name (if it has one and reflect.t is included in the build) or a generic "object#" format.
  • The List method sublist() now accepts negative length values, for better consistency with similar methods (e.g., String.substr). A negative length essentially states the length relative to the end of the list, in that it gives the number of elements to omit from the end of the result list.
  • The byte packing language now lets you specify that a square-bracketed group is to be packed from/unpacked into subgroups per iteration for a repeated item, rather than using a single sublist for the whole group. The "!" modifier makes this change. For example, fp.unpackBytes('[L S C]5!') returns (when successful) a list containing five sublists, each of which contains the three unpacked elements from one group iteration (a long int, a short int, and an 8-bit int).
  • The byte packer now allows up-to counts (e.g., 'a10*') for packing (not just unpacking). When packing, for a group or a non-string item, an up-to count packs up to the numeric limit, or up to the actual number of arguments; for a string, an up-to count packs up to the actual string length or up to the limit, truncating the string at the limit if it's longer.
  • The regular expression language accepts several new shorthand character classes: %s for a space character, %S for a non-space character, %d for a digit, %D for a non-digit, %v for a vertical space, %V for a non vertical space. (These correspond to backslash sequences - \s, \D, etc - that are fairly standard these days in other languages with regex parsers, such as Javascript and php. There were already <xxx> character classes that do the same things as these new % codes, but these particular classes tend to be used often enough that it's nice to have shorthand versions.)
  • The new __objref() operator lets you test for the existence of a particular object symbol, optionally generating a warning or error message if the symbol isn't defined or is defined as something other than an object. This is similar to the defined() operator, but is specialized for object references.
  • The randomize() built-in function can do several new tricks. First, it allows you to select from three different RNG algorithms to use in rand(): the default ISAAC algorithm (the original TADS 3 RNG), a Linear Congruential Generator (or LCG, the long-time de facto standard for computer RNGs), and the Mersenne Twister (a newer algorithm that's become popular in other modern interpreted languages). ISAAC is still a good general-purpose choice, but the new options are there in case you have some reason to prefer the properties of one of the other generators. Second, you can now set a fixed seed value. This allows you to override the automatic startup randomization that was added in TADS 3.1, and further lets you start a new fixed sequence at any time. Third, you can now save and restore the state of the RNG, so that you can make the RNG repeat the same sequence of results it produced from the time of the saved state.
  • When the interpreter is launched, any command-line arguments that follow the .t3 file name are passed to the program as string arguments to the main() function. In the past, these arguments were passed as-is, without any character set translation, which caused unpredictable results if they contained any non-ASCII characters. The interpreter now translates these strings from the local character set to Unicode, ensuring that any accented letters or other non-ASCII characters are interpreted properly. (Related to bugdb.tads.org #0000109)
  • The new interpreter command-line option -d specifies the default directory for file input/output. This is the directory that the File object uses to open files whose names are specified with relative paths. If -d isn't specified, the default is the folder containing the .t3 file. (In past versions, there wasn't any way to set the working directory, which was always the .t3 file's folder. This means the behavior in the absence of a -d option is the same as in the past.)

    The new option -sd lets you separately specify the "sandbox" directory for the file safety feature. In the absence of an -sd setting, the sandbox directory is the same as -d setting, or simply the .t3 file's containing folder if there's no -d option.

    (The -d option was added in part to address bugdb.tads.org #0000120)

  • A bug in the dynamic compiler caused 'if' statements in dynamically compiled code (e.g., using new DynamicFunc()) to use the 'then' branch code for both true and false outcomes. This has been fixed. (bugdb.tads.org #0000117)
  • A bug in the dynamic compiler sometimes caused a run-time error when accessing a local variable when the enclosing function also defined an anonymous function. This is now fixed. (bugdb.tads.org #0000118)
  • A bug in the BigNumber class sporadically gave incorrect results for additions. (Specifically, results were sporadically off by one in the last digit.) This has been corrected.
  • toInteger() caused a crash when used with values below 0.1. This has been corrected. (bugdb.tads.org #0000127)
  • The compiler reported an unhelpful internal error message ("unsplicing invalid line") if a file ended in an unterminated string literal. The message is now the more explanatory "unterminated string literal".
  • Consider this macro definition and usage:

    #define ERROR(msg) tadsSay(#@msg)
    ERROR({)
    

    In the past, the compiler treated the ERROR({) line as have a missing close paren. This is because the compiler previously tried to balance open and close curly braces and square brackets within macro arguments, and treated any parentheses found nested within braces or brackets as being part of the macro argument, rather than terminating the macro argument. This no longer occurs; parentheses are now treated independently of braces and brackets within macro arguments, so a close paren within a macro argument that doesn't match an earlier open paren in the same argument now ends the argument. The example above thus now compiles without error, and expands to tadsSay('{'). The balancing act for braces and brackets does still apply to commas, though: a comma within a pair of braces or brackets is still considered part of the argument. This is important for macro arguments that contain things like statement blocks or anonymous function definitions.

  • The File unpackBytes() method incorrectly threw an error if an "up-to" format was used (e.g., 'H20*') and the file had zero bytes left to read. This has been corrected; unpackBytes() now succeeds and returns a zero-length result for the format item.
  • String.split() incorrectly returned a one-element list (consisting of an empty string) when used on an empty string. This now correctly returns an empty list.
  • A bug in String.split() caused sporadic crashes when splitting at a delimiter and the result list had more than 10 elements. The bug was related to garbage collection timing, so it was unpredictable. This is now fixed. (bugdb.tads.org #0000156)
  • A bug introduced in 3.1.0 caused exceptions to be caught in the wrong handlers under certain rare conditions. The problem happened with exceptions thrown from within method calls when the caller had a new "try" block starting immediately after the expression containing the method call, with no other VM instructions between the call and the start of the "try" block (this means, for example, that the return value from the method call was discarded and no other computations were performed as part of the same expression after the method call). When all of these conditions were met, the exception was incorrectly handled by the "catch" part of the "try" block that started just after the call; this was incorrect because the "try" block didn't contain the call and so its "catch" block shouldn't have been involved in handling an exception that occurred within the call. The correct behavior has been restored.
  • A bug in the regular expression parser randomly caused bad behavior, including crashes, if the last character of an expression string was outside of the ASCII range (Unicode code points 0 to 127). The bug was only triggered when certain byte values happened to follow the string in memory, so it only showed up rarely even for expression strings matching the description. (It was also possible to trigger the same bug with a non-ASCII character within five characters of the last position if the string ended with an incomplete <Xxx> character class name, lacking the final ">", but this might have been too improbable to have ever been observed in the wild.) This has been fixed.
  • A compiler bug introduced in 3.1.0 made it impossible to assign to an indexed "self" element in a modifier method for an intrinsic class such as List or Vector. This has been corrected. (bugdb.tads.org #0000128)
  • In the past, the compiler attempted to pre-evaluate any indexing expression it encountered ("a[b]") where the index value and the value being indexed were both constants. In such cases, it only recognized list indexing, which was the only valid constant indexing expression before operator overloading made it possible to define indexing on custom object classes. This made the compiler generate error messages for (potentially) valid code involving constant index values applied to object names. This has been corrected; the compiler now treats such expressions as valid, and defers their evaluation until run-time, so that any operator overloading can be properly applied. (bugdb.tads.org #0000142)
  • The standard main window layout code in the Web UI library now loads its Flash helper object (TADS.SWF) dynamically rather than statically, and only does so when it detects that Flash support is already present in the browser. In the past, the TADS.SWF object was embedded statically on the page, which means that the browser saw the object declaration whether or not Flash support was installed. Some browsers attempt to be helpful in this situation by popping up a dialog or prompt pointing out that the page depends on Flash and offering to download and install a Flash plug-in. For users who intentionally omit Flash from their browser configurations, though, this "helpful" prompt is an annoyance, since it comes up every time a page with a Flash embedding is loaded or reloaded and the answer is always No. The library's new approach avoids the superfluous prompt by creating the TADS.SWF object embedding only after determining that Flash is already installed.

    (There's a trade-off, of course, in that the browsers that display the prompt do so for good reason. Without it, users who unintentionally omitted Flash from their configurations will never know the page makes use of it. This seems like a small price to pay, though, in that (a) most modern browsers include Flash support out of the box anyway (excluding those on iOS, where Flash simply isn't available), so practically everyone who hasn't gone out of their way to remove Flash already has it (or is running on iOS, where there's no need for a prompt since there's no way to install Flash at all); and (b) even if there's anyone left over after considering (a) who could benefit from the prompt, the Web UI only uses Flash as a very minor enhancement (specifically, to obtain a list of installed fonts for the Preferences dialog), so these presumably rare users won't suffer any really significant loss of functionality from the lack of Flash that we're preventing the browser from alerting them to.) (bugdb.tads.org #0000145)

  • The compiler didn't generate the full string list properly when the -Os option was used; only strings for the first source module in the build were included, rather than strings for all modules being compiled. (What's more, when the build included many modules, the underlying bug sometimes caused internal memory corruptions within the compiler that generated spurious error messages or other unpredictable results.) This has been corrected. (bugdb.tads.org #0000150)

Changes for 3.1.0 (December 21, 2011)

This update has three main themes: dynamic coding, greater convenience, and network support. On the dynamic code side, it's now possible to compile new code at run-time, and new reflection features provide more thorough run-time access to the program's own internal structure. These dynamic features will be especially interesting to library and extension authors, as they open new opportunities for creating miniature languages within the language. The convenience enhancements involve numerous, mostly small changes that make common tasks easier and frequent coding patterns more compact. As for the new network features, the original motivation and initial application is to run TADS games in a client/server configuration, where the player uses an ordinary browser to access the game, with no need to install TADS or even download a game file. This is just one application of the new technology, though; the network features are actually much more generalized and extensive than this first use might suggest. In effect, TADS is now capable of acting as a fairly complete (if small scale) Web server programming environment. This opens many new possibilities for networked user interfaces and multi-player games. It also has an interesting bonus benefit, which is that it lets you tap into the full power of the browser to create your TADS user interfaces. Modern browsers provide a vastly more powerful user interface platform than HTML TADS (or any other existing IF runtime), and all of that power is now directly available to TADS games.

Note that before this version was released, it was sometimes referred to as 3.0.19 (e.g., in the TADS bug database and the T3 blog). If you're looking for 3.0.19 based on something you read elsewhere, this is it. We finally bumped the name up to 3.1 because of the substantial new functionality it contains.

  • Compatibility Alerts: The following changes might affect existing code that was originally created with an earlier version of TADS 3.
    • operator is now a reserved word, due to the new operator overloading feature. This means that the word operator can't used as a symbol name, such as for the name of an object, function, or local variable.
    • method is now a reserved word, due to the new "floating" method definition syntax.
    • invokee is now a reserved word.
    • defined is now a reserved word.
    • << >> sequences are now meaningful in single-quoted strings, since these strings can now contain expression embeddings. This means that formerly inert << sequences in single-quoted strings will now be interpreted as embeddings. This should be a compatibility issue, but as it happens a fortuitous compiler bug in older versions made it virtually impossible to use << in single-quoted strings anyway, so this shouldn't affect any existing code.
    • Complex expressions involving compound assignment operators with side effects in their lvalues are now compiled with different (better) behavior. This probably won't affect any existing code, because (a) it only applies to fairly unusual expressions, and (b) anyone who encountered the old behavior probably would have thought it was a bug and changed their code to avoid it. The new behavior is much more predictable and much more what you'd expect, but it's possible that there's existing code that accidentally relies on the old behavior. Details below.
    • The regular expression character class <space> now explicitly matches only horizontal whitespace characters, not vertical separators (\n, \r, \b, and a few others). This is actually more likely to fix problems than create new ones, since the old behavior was inconsistent with what most people expect from other regular expression implementations.
    • The rand() function now evaluates only one of its arguments when it's called with multiple arguments. In the past, rand() evaluated all of its argument values first, then randomly selected one of the values as the result. Now, rand() makes the random selection first, then evaluates only the selected item, and returns the result. This means that side effects are only triggered for the selected argument, not for all arguments as in the past. This could conceivably affect existing code that relied on all of the arguments' side effects being executed, although such code tends to be tricky enough that most people avoid it, so the practical impact should be minimal to non-existent. Also, importantly, this is a compiler change only, so it only affects newly compiled code; existing .t3 files already in distribution won't be affected.
  • It's now possible for a TADS game to be a Web server. This is accomplished with the new intrinsic classes HTTPServer, which handles the low-level networking required to receive and parse HTTP requests from Web clients, and HTTPRequest, which represents an incoming HTTP request from a client; and the new intrinsic function set tads-net, which contains additional support functions for networking operations.

    The new HTTP server support classes are designed to automatically handle all of the low-level necessities of a network service, while still giving the game program full control over how requests are actually processed. This makes it possible to create a wide variety of effects with the network server. Initially, it will be used to present the traditional single-player user interface in a networked configuration, where the game runs on a server and the client needs only an ordinary Web browser. This eliminates the need for clients to install the TADS software, while also greatly expanding the UI capabilities of TADS games by letting you use the full power of HTML DOM and Javascript. Over time, it opens up lots of other possibilities, such as collaborative gaming and multi-player games.

  • In addition to the new ability to act as a Web server, TADS games can now also make HTTP requests as clients, via the new function sendNetRequest(). This lets a game send information to and receive information from remote Internet servers during play.
  • The interpreter has a new command-line option, -ns##, for setting the "network safety level". This is analogous to the file safety level, and controls the program's ability to access the network functions. The "##" part is two digits, the first giving the safety level for client functions, and the second giving the level for server functions. The client level controls outgoing connections from the program to external network services; the server level controls the program's ability to accept connections from external client programs (such as Web browsers). There are three possible values for each component: 0 means "no safety", which allows all network access without restrictions; 1 sets local access only, which only allows connections to or from other applications running on the same computer; and 2 is "maximum safety", meaning no network access is allowed at all. For example, -ns02 gives the program full access as a client to any external network service anywhere on the network, but at the same time prohibits the program from setting up any network services of its own or accepting any connections from other processes or computers. The default safety level is -ns11, which allows the program to connect to and accept connections from other programs on the same machine only.
  • The compiler now accepts "triple-quoted" strings. This isn't a third type of string beyond single- and double-quoted; it's just a new way of writing those two existing string types. A few other C-like languages have adopted this as a nicer syntax for writing strings that contain quote marks as part of their literal text. This is a particularly common need in TADS, since so many strings are part of the story text.

    A triple-quoted string starts and ends with three copies of the quote mark. What you gain is that you can then freely use the quote mark character within the string without worrying about "escaping" it with a backslash. The traditional C backslash syntax for embedded quotes is awkward to type and hard to read. Triple quotes make strings more readable by letting you use quotes directly in a string without any escape characters.

       desc = """The sign reads "Beware of Backslash!""""
    

    There are a couple of subtleties you should be aware of, so take a look at the System Manual for details.

  • Single-quoted strings can now use << >> expression embeddings (including all of the new embedding features, such as <<if>>). The single-quoted version produces a result that's equivalent to concatenating the embedded expressions to the surrounding string fragments. For example, 'one <<two>> three <<four>> five' is equivalent to 'one ' + two + ' three ' + four + ' five'. See String Literals in the System Manual for more details.
  • A new string embedding template syntax lets you create custom keywords and phrases for use inside embedded expressions. The compiler translates each custom template invocation into a function call, so this is merely a syntactic convenience, but it can make embeddings in strings more readable by avoiding expression-like syntax. For example, you could create a template that lets you write a string like "You currently have <<score in words>>" points", rather than the more techy looking "You currently have <<spellNum(score)>> points".
  • There's a powerful new string embedding syntax for writing passages that vary according to run-time conditions. Traditionally, conditions were embedded in strings using the ?: operator, as in:

       desc = "The door is <<isOpen ? 'open' : 'closed'>>."
    

    That's fine for substituting a word or two based on a simple true/false condition, but for anything more complex it can be hard to read. The new syntax improves the situation by making the varying text part of the string, rather than part of the expression. The conditions become almost like markups interposed within the text:

       desc = "The door is <<if isOpen>>open<<else>>closed<<end>>. "
    

    The improved readability of the new syntax is more obvious with longer passages:

       desc = "A massive iron door, bristling with rivets and bolts
         across its surface. <<if isOpen>>It's open a crack, leaving
         enough room for a mouse or perhaps a small hare to slip
         through, but probably not quite enough for a burly
         adventurer. "
    

    Another benefit of the new syntax is that, because the varying text is part of the string rather than part of an embedding, you can freely use additional embeddings within the then/else parts. (That's not possible with ?: embeddings, since strings inside embedded expressions can't themselves contain any embeddings.) You can even nest <<if>> structures for more complex conditions.

    As with other embedding syntax, <<if>> can be used in single-quoted strings as well.

    Full details are in the System Manual.

  • Another new embedding syntax, <<one of>>, makes it easier to create lists of alternative messages to be chosen randomly or in a cycle (or a combination of the two). <<one of>> variations are defined for simple random selection, shuffling, cycling, "stop" lists, and for various combinations of these, such as going through a list once in sequence and then shuffling it.

    The traditional way to create random or sequential messages was via the Adv3 EventList class, which of course still works. <<one of>> is much more concise and readable for simple message variations, though, since it doesn't require a separate object declaration for the event list. The new syntax also has the advantage of being nestable inside other <<one of>> structures and <<if>> structures.

  • One more new special embedding: <<first time>> shows a message the first time the enclosing string is displayed, and omits it after that. This is really just a special case of <<one of>> (and, in fact, the compiler actually rewrites it that way), but it's such a common motif in IF authoring that the custom syntax seems justified.
  • Speaking of string embedding, the compiler now respects parentheses (and square brackets and curly braces) within an embedded expression when determining where it ends. The compiler previously assumed an embedding ended at the very first >>, regardless of context, but this was problematic if you wanted to use the >> bit-shift operator within an expression. Now, the compiler counts parentheses, brackets, and braces, and treats >> as a bit-shift operator if it appears within a bracketed group. This means you can use the >> operator in an embedded expression simply by parenthesizing the expression.
  • The new TadsObject methods getMethod() and setMethod() give you more dynamic control over objects and classes by letting you add new methods to an existing object. This makes it possible to perform almost any sort of transformation on an object. See the TadsObject documentation for details.
  • The new method keyword lets you define a "floating" method. This is a routine that's not associated with an object, but which nonetheless has access to the method context variables (targetprop, targetobj, definingobj, and self), as well as inherited. This is meant specifically for use with TadsObject.setMethod(): it lets you create a method that's not intially part of any object, and then plug it in as an actual method of selected objects at run-time. Syntactically, a floating method definition looks just like an ordinary function definition, except that whole thing is preceded by the keyword method instead of the optional function keyword. See the System Manual for more details.

    The method keyword can also be used to create anonymous methods. These look and act almost the same as anonymous functions. The difference is that an anonymous method doesn't share its method context variables (self, definingobj, targetobj, targetprop) with its enclosing lexical scope. Instead, an anonymous method takes on the "live" values for those variables each time it's called. As with named floating methods, anonymous methods are designed for attaching to objects via setMethod(). Details are in the System Manual.

  • The new intrinsic class DynamicFunc makes it possible for a running program to extend itself by creating new code on the fly. New code is created by compiling a string that contains source code text, just as you'd use in the main program source code. This type of facility is common in modern interpreted languages, especially scripting languages (e.g., Javascript, PHP), where people have found all sorts of uses for it. It's especially interesting in TADS because it opens the door to new string and message processing capabilities.

    DynamicFunc values behave very much like ordinary function pointers. You can call a DynamicFunc as though it were a function pointer, and you can use a DynamicFunc in TadsObject.setMethod() to create a new method for an object.

  • GrammarProd objects can now be dynamically created, and the grammar rules for an existing GrammarProd can be modified at run-time. The new methods deleteAlt() and clearAlts() remove existing alternatives from a GrammarProd, and the new method addAlt() adds new alternatives. New rules are specified using the same syntax as the regular "grammar" statement.
  • It's now possible to retrieve information on the preprocessor macros defined by the compiled program. This is handled through the existing function t3GetGlobalSymbols(), which now takes an optional argument value that selects which type of symbol information to retrieve. The argument is one of the following constant values: T3GlobalSymbols, to retrieve the global symbol table; or T3PreprocMacros, to retrieve the macro symbol table. If you don't include an argument, the global symbol table is retrieved as in the past, so existing code will work unchanged. As with the symbol table, the macro table is available only during preinit, or during normal run-time if the program is compiled with debugging information.
  • It's now possible to get a pointer to an intrinsic (built-in) function. Use the "&" operator, just like with a property name. For example, &tadsSay yields a pointer to the tadsSay() intrinsic. These pointers operate just like ordinary function pointers: you can use them to make calls, and you can even use them in TadsObject.setMethod().
  • For syntactic consistency, the "&" operator can now be used to get a pointer to an ordinary function.

    (In the past this wasn't allowed, but only for nit-picky reasons. It was felt that, because there wasn't a need for an explicit "pointer-to" operator for functions, "&" shouldn't even be allowed for functions, making its function clearer by virtue of being single-purpose. This was seen as worthwhile because "pointer to property" seems to be a particularly subtle idea for new users, as it's rather abstract and not common in other languages. However, now that we have pointers to built-in functions, we do need an explicit "pointer to" operator for those, and "&" is certainly the right choice: it's the right parallel to C++, on which our syntax was originally modeled, and it's the right analogy to the existing TADS usage of "&" with properties. With the addition of the built-in function pointer type, given that "&property" means "pointer to property", and "&built-in" means "pointer to built-in," it would be confusing if "&function" didn't mean "pointer to function" as well. The only thing left out, really is "&object", but that would be going too far. The analogy with C++ would make "&object" confusing for experienced C++ users because object and &object have quite different meanings in that language.)

  • The new "defined" operator tests at compile time to determine whether a symbol is defined or not. The syntax is defined(symbol); this yields the constant value true if symbol is defined at the global level within the program (as a function, object, or property name), nil if not. You can use defined() in any expression context, such as in the condition of an if statement. This operator is particularly useful in libraries, since it makes it possible to write code that conditionally references objects only when they're actually part of the program. See the System Manual for details.

    Note that "defined" still has its separate meaning within #if preprocessor expressions. There, the operator determines if the symbol is defined as a preprocessor (#define) symbol. When used outside of #if expressions, "defined" has the new meaning of testing the symbol's presence in the compiler global symbols rather than the preprocessor macro symbols.

  • The "new" keyword is no longer required (although it's still allowed) in the definition of a long-form anonymous function. For example, it's now valid to write code like lst.mapAll(function(x) { return x+1; }). The presence or absence of the "new" makes no difference to the meaning.

    The original rationale for requiring "new" was that anonymous functions are actually objects that are newly created on each evaluation, so it was felt that this should be made explicit in the syntax. The rationale for now relaxing the "new" requirement is that "closures" (as they're more technically known) have become common in other mainstream languages, and no one else seems to think the syntax should mimic object construction. (For that matter, TADS's own short-form syntax never had the "new".) There's also a good pragmatic reason: with the new Web UI, TADS programmers might find themselves switching back and forth between TADS and Javascript, since the Web UI incorporates a substantial Javascript front end. Javascript's anonymous function syntax is almost exactly like TADS's long-form syntax, except that Javascript doesn't have the "new". Such close-but-not-perfect similarities are particularly vexing when switching between languages, so it seemed best to eliminate the unnecessary difference.

  • Short-form anonymous functions can now define their own local variables. Use the local keyword as usual, at the beginning of the function's expression. The syntax is analogous to defining locals within a for statement's initializer clause. See Anonymous Functions in the System Manual for details and examples.

    This is convenient because it lets you use the short-form syntax even if the function requires a local variable or two. In the past, you had to switch to the long-form syntax to define a local, even if the rest of the function simply returned an expression value.

  • The syntax of the for loop has been extended with three new features:
    • for..in:: for (x in collection) is now synonymous with the existing foreach syntax. This makes the syntax more uniform, which makes the language easier to use by eliminating the need to remember to use distinct keywords for the two kinds of loops.
    • Hybrid for and for..in loops: The "in" syntax newly allowed in for loops can also be combined with the conventional three-part init/condition/update syntax. This allows you to add other looping variables to an "in" iteration. One of the drawbacks of the foreach syntax is that any other looping variables have to be initialized before the foreach and updated and/or tested within the loop body. for loops, in contrast, can manage multiple variables as part of explicit loop structure within the for statement itself, which is more compact and often clearer. For example, to add a counter to an "in" loop, and stop after 10 iterations even if more elements are in the list, you could write for (local i = 1, local ele in list ; i < 10 ; ++i) { }.
    • for..in range loops: By far the most common type of for loop is a simple iteration over a range of integers, such as over the index range for a list. There's now a custom syntax for this kind of loop: for (var in from .. to). This syntax steps the variable var from the from expression's value to the to expression's value, inclusive of the limits. For example, to step through the index values for a list, you can write for (local i in 1..list.length()). This new syntax makes simple integer loops easier to write and clearer. It also makes them more reliable, since a common coding error is using < instead of <= (or vice versa) for a loop condition. The range syntax is more intuitive because it explicitly states the endpoints rather than expressing the loop condition as a value comparison. The new syntax also allows specifying the increment: for (i in 0..20 step 2) steps through even numbers, and for (i in 10..1 step -1) steps down from 10 to 1. As with collection loops, you can mix this syntax with the full three-part for syntax: for (local i in 1..20, sum = 0 ; lst[i] != nil ; sum += lst[i]).

    The new syntax is described in more detail in the System Manual.

  • A new language feature lets you pass argument values to functions and methods using explicit argument names. This has several important benefits. First, callees can retrieve argument values from callers without regard to the order of the values in the calling expression. Second, callees are free to ignore named parameters entirely, which allows a caller to pass extra, optional context information without burdening every callee's method definition syntax with unneeded extra parameter declarations. Third, nested callees can retrieve named arguments passed indirectly from a caller several levels removed, so intermediate functions that don't care about context information can ignore it without preventing nested callees from accessing it. This doesn't replace the traditional positional argument system, but rather extends it. Some other languages use named arguments primarily for code clarity reasons, but the TADS version is designed to address a particular coding problem that comes up time and again in IF library design. The details take a little work to explain; the new System Manual chapter on Named Arguments has the full story.
  • New syntax makes it easier to define functions and methods that take optional arguments. It's always been possible to do something similar using the "..." syntax, but "..." is really intended for cases where the number of additional arguments is unpredictable and has no fixed upper limit. The new syntax, in contrast, is for cases where you have a specific number of arguments, but where you wish to make one or more of the arguments optional, so that callers can omit them for the most common case where a default value would apply. This makes the calling syntax more convenient for the common case, while still letting callers specify the full details when needed. "..." isn't ideal for this use because it doesn't provide error checking for too many parameters, and because it requires fairly tedious extra syntax in the callee to check for the presence of the extra arguments and retrieve their values. The new syntax is described in detail in the new System Manual chapter on optional parameters.
  • The new invokee pseudo-variable retrieves a pointer to the function currently executing. This is most useful for anonymous functions, since it provides a way for an anonymous function to invoke itself recursively.
  • The t3GetStackTrace() function can now retrieve information on the local variables in effect at each stack level. The locals are available via the locals_ property of the T3StackInfo object that represents a stack level.

    By default, locals are omitted, since they take additional time to retrieve. To include local variable information, supply the new flags argument with the value T3GetStackLocals. (This is a bit value; it's possible that future bit values will be added, in which case this will be combinable with other bit flags via the '|' operator.)

    The locals in each stack frame are provided as a LookupTable. Each element of the table is keyed by a string giving the name of the variable, and each corresponding table value is the current value of the local. The table is merely a copy of the locals in the stack, a value in the table won't have any effect on the local variables themselves.

  • The t3GetStackTrace() function now includes information on the named arguments passed to each stack frame. This is available via the namedArgs_ property of the T3StackInfo object that represents each stack level. This property is nil for a stack level that doesn't have named arguments.

    Each named argument list is provided as a LookupTable. Each element of the table is keyed by a string giving the name of the argument, and each corresponding table value is the argument's value.

  • The t3GetStackTrace() function now includes full information on "native" calls in the stack - that is, intrinsic functions and intrinsic class methods. In the past, native callers were recognizable by their complete lack of information, in that they had a nil value for both the calling function and object/property values.

    This change comes into play when a native routine calls bytecode via a function pointer you passed into the native routine, such as when List.forEach() invokes its callback. When the native caller is a built-in function, the function pointer element of the stack level object will contain a built-in function pointer value; when it's an intrinsic class method, it will contain suitable object and property values describing the native method. For obvious reasons, there's no source file information for a native routine in the stack trace.

  • The new intrinsic class StackFrameDesc provides read and write access to the local variables in a stack frame. You obtain a StackFrameDesc object via t3GetStackTrace() by including the T3GetStackDesc flag. The object provides methods to get and set the values of local variables, and to retrieve the method context variables (self, targetobj, targetprop, definingobj). See the System Manual chapter for more information.
  • A new language and VM feature make it possible to "overload" operators. This means that you can define a method on an object or class that's invoked using one of the algebraic operator symbols, such as "+" or "*", rather than via the normal method call syntax. Operator overloading has many potential uses, but the two main uses are (1) to create an especially compact syntax for common operations on specialized objects, and (2) to create a custom object that mimics the low-level interface of one of the built-in types or classes. The details are described more fully in a new chapter in the system manual.

    Operator overloading has three significant limitations. First, you can't override operators pre-defined by intrinsic classes. For example, you can't redefine the indexing operator "[]" for a List, or the concatenation operator "+" for a String. You can, however, add operators to intrinsic classes: it's legal to use "modify" to define an operator on an intrinsic class as long as that operator isn't already defined by the intrinsic class itself. Second, there's no way to overload operators at all for a primitive type like integer, whether or not the type defines it: you can't change the meaning of "+" when applied to integers, or add a meaning for "true + nil". Third, not all operators are overloadable; notably, none of the comparison operators (==, !=, <, <=, >, >=) are overloadable. All of these limitations are due to performance considerations; with these restrictions in place, this new feature has no performance cost to programs that don't use it.

  • Using operator overloading, it's now possible to create "list-like" objects. An object is considered list-like if it has an overload for operator[] and it provides a length() method that takes zero arguments. If the object provides this interface, the length() method must return an integer value.

    The following built-in functions that formerly only accepted regular lists and/or vectors will now also accept list-like objects: inputDialog(), makeString(), rand(), Dictionary.addWord(), Dictionary.removeWord(), List.intersect(), List.appendUnique(), new LookupTable(), GrammarProd.parseTokens(), new StringComparator(), TadsObject.setSuperclassList(), new Vector(), Vector.appendAll(), Vector.appendUnique(), Vector.copyFrom(). Similarly, the "..." varying argument expansion operator can be applied to a list-like object as though it were a true list; and List and Vector comparisons with "==" and "!=" will compare element-by-element against a list-like value on the right-hand side; and the List and Vector "+" and "-" operators will treat right-hand operands as lists if they're list-like objects.

  • New syntax lets you create a LookupTable directly from a set of Key/Value pairs. The syntax is similar to a list expression: write the list of Key->Value pairs in square brackets, with an arrow symbol '->' between each key and value. For example, x = ['one'->1, 'two'->2, 'three'->3] creates a LookupTable with keys 'one', 'two', and 'three', corresponding to values 1, 2, and 3, so x['one'] yields 1, and so on. This syntax is equivalent to calling new LookupTable() and then filling in the keyed values, so this kind of expression creates a new LookupTable object each time it's evaluated.
  • The LookupTable class now lets you specify the value to be returned when you index a table with a key that doesn't exist in the table. This is called the "default value" for the table; in the past, this was always nil. The new setDefaultValue() method lets you set a different default. You can retrieve the default value previously set for a table with the new getDefaultValue() method. The initial default value for a new table is nil, so the behavior is the same as in prior versions if you don't use the new method. You can also specify the default value when creating a table with the new shorthand syntax, by writing "*->value" as the last element of the list. (The asterisk is meant to suggest a "wildcard" matching any key not specifically entered in the table.)
  • The Dictionary class has new built-in infrastructure support for spelling correctors. The new function correctSpelling() retrieves a list of words in the dictionary that are within a specified "edit distance" of a given string. This new function isn't a full-fledged spelling corrector, but provides a very fast version of a key infrastructure element for building one. Refer to the Dictionary class documentation for details.
  • The new intrinsic class StringBuffer is a mutable version of the character string object. Unlike regular String objects, a StringBuffer's text contents can be modified in place, without creating new String objects. StringBuffer is designed especially for situations where it takes many incremental steps to build a string. It's much more efficient to use StringBuffer for these cases than it is to use regular string concatenation, because the latter makes a new copy of each concatenated combination. StringBuffer provides methods to edit the contents of the buffer: you can insert, append, delete, and replace parts of the text. When you've finished the build steps for a string buffer, you can convert it to a regular string using toString(). You can also extract a substring of the buffer using the substr() method, just like for a regular string.
  • The rexReplace() function has several new features that make it more powerful and more convenient to use:
    • You can now specify a function to determine the replacement text to use for each match. If you supply a function (regular or anonymous) in place of the regular replacement text argument, rexReplace() calls the function for each match, passing as arguments the matched text, the index of the match within the overall subject string, and the original subject string. Your callback function returns a string value giving the text to use as the replacement. This allows for much more complex string manipulations, since you can test conditions that are beyond what can be encoded in a regular expression, and you can apply arbitrary transformations to the match string to produce the replacement text.
    • You can now specify a list of patterns to match, instead of just a single pattern, and each pattern can have a separate replacement value (which can be a string or a callback function, per the new callback feature above). By default, when you supply a list of patterns, rexReplace() searches for all of the patterns at once. This is similar to combining the patterns with '|' to make a single pattern, but it's more powerful because it lets you specify a different replacement string for each pattern. If you include ReplaceSerial in the flags, rexReplace() instead searches "serially" for the patterns: it replaces each occurrence of the first pattern throughout the entire string, and then re-scans the updated string to replace all occurrences of the second pattern, and so on. The effect is the same as calling rexReplace() sequentially with each individual pattern, but it's more compact to write it this way.
    • The "flags" argument is now optional. If it's omitted, the default is ReplaceAll. This makes the function more convenient to use for the most common case. Note that if you need to specify the "index" argument, you must also include a "flags" value, since the arguments are positional.
    • The new flag ReplaceIgnoreCase makes the search insensitive to case, by default. A <case> or <case> directive in the regular expression overrides the ReplaceIgnoreCase setting. The main reason this flag is provided at all (given that it's largely redundant with the <case> or <case> directives) is for uniformity with String.findReplace(), but it can occasionally be useful in that lets you reuse an expression for both case-sensitive and case-insensitive searches.
    • The new flag ReplaceFollowCase makes the replacement follow the capitalization pattern of the matched text. Lower-case letters in the replacement text are capitalized (or not) as follows: if all of the alphabetic characters in the matched text are capitals, the entire replacement text is capitalized; if all of the letters in the match are lower-case, the replacement text is left in lower-case; if the match has both capitals and lower-case letters, the first alphabetic character of the replacement text is capitalized. This only applies to lower-case letters in a replacement string, and only to literal text: "%" group substitutions aren't affected, since they already copy text directly from the match anyway. The flag also has no effect when the replacement is a callback function rather than a string; we have to assume the function returns exactly what it wants, since it can perform similar case manipulations of its own.
  • The String method findReplace() has a few new features:
    • You can now specify a list of search strings, and a list of corresponding replacements. This makes it possible to perform a whole series of replacements with a single call.
    • You can now pass a function in place of a string as the replacement argument. For each match, findReplace() invokes the function, passing in the matched text and other information; the function returns a string giving the replacement text. This makes it possible to vary the replacement according to the actual matched text, its position in the subject string, or other factors.
    • The "flags" argument is now optional. If it's omitted, the default is ReplaceAll. This makes the most common usage more convenient. Note that if you need to specify an "index" value (for the starting position), you'll need to include a "flags" value, since the arguments are positional.
    • The new flag ReplaceIgnoreCase makes the search insensitive to capitalization.
    • The new flag ReplaceFollowCase makes the replacement text follow the case of the matched text, mimicking its capitalization pattern.
  • The functions rexMatch(), rexSearch(), and rexReplace(), and the String methods toUnicode(), find(), and findReplace() now accept a negative values for the "index" argument. This is taken as an index from the end of the string: -1 is the last character, -2 the second to last, and so on. For the search and replace functions, a negative index doesn't change the left-to-right order of the search; it's simply a convenience for specifying the starting point. Some other string methods already accepted this notation, so these additions make the API more consistent.
  • The regular expression matcher now matches <space> only to horizontal whitespace characters. In the past, <space> matched some vertical whitespace characters as well, but this was inconsistent with the usual matching rules in most other regular expression implementations, and was usually undesirable. The new character type <vspace> explicitly matches vertical whitespace: '\n', '\r', '\b', '\u2028', '\u2029', and a few ASCII control characters that the Unicode standard defines as line break characters ('\u0085', '\u001C', '\u001D', '\u001E'). To create a character class matcher that matches all whitespace, the way <space> did in the past, use <space|vspace>.
  • Look-back assertions are now supported in the regular expression language. A look-back assertion tests a sub-pattern against the characters preceding the current match point, which makes it possible to apply conditions to the preceding context where a potential match appears. TADS follows the widely-used Perl-style syntax for the new assertions.
  • In the past, the regular expression compiler explicitly ignored any closure operator (*, +, {}) applied to an assertion, because such constructs are essentially meaningless and are susceptible to infinite loops. The compiler no longer takes this approach; instead, it does a thorough check for meaningless loops in the regular expression, and deletes them. This is an improvement because it detects all loops, no matter how complex, whereas the old approach only caught this one superficial case. This change creates one behavior difference, which is that it corrects the effect of the * operator applied to an assertion: in the past, the * was simply ignored, whereas it now correctly requires that the assertion is true zero or more times. This is the same as removing the assertion entirely, since a condition that has to be true zero or more times is really no condition at all.
  • The new function sprintf() creates formatted text from data values according to a format template string. This is similar to the sprintf() function in many C-like languages. This style of string formatting is sometimes more compact and convenient than the alternatives. sprintf() is also particularly useful for formatting numbers, since it has several style options for integers and floating-point values that are tedious to code by hand.
  • The new String method splice() lets you delete a portion of a string, insert new text into a string, or both at the same time. splice(idx, del, ins) deletes del characters starting at index idx, and then inserts the string ins in their place. The ins string is optional, so you can omit it if you just want to delete a segment of the string.
  • The String method substr() now accepts a negative value for the length argument, which specifies a number of characters to discard from the end of the string.
  • The new String method split() divides a string into substrings at a given delimiter, which can be given as either a string or a RexPattern (regular expression pattern). It can alternatively split a string into substrings of a fixed length. This method comes in handy for surprisingly many simple string parsing jobs.
  • The new List method join() concatenates the elements of the list together into a string. Vector has this same new method.
  • The new String method specialsToHtml() converts special TADS characters (such as \n and \b sequences) to standard HTML equivalents. This is designed specifically for the Web UI, to make it easier to port games between the traditional console UI and the Web UI by providing support in the Web UI for the traditional (pre-HTML) TADS formatting codes.
  • Another new String method, specialsToText(), is similar to specialsToHtml(), but converts the string to plain text. Special TADS characters are converted to their plain text equivalents, the basic HTML "&" entities are converted to their character equivalents, a few basic tags (<BR>, <P>, and a few others) are converted to suitable plain-text equivalents, and most other tags are stripped out. This is designed for situations where you need a plain-text rendering of the way a TADS string would look as displayed on the regular output console.
  • The new string methods urlEncode() and urlDecode() simplify encoding and decoding URL parameter strings, for use in HTTP network requests. urlEncode() converts special characters to "%" encodings; urlDecode() reverses the effect, translating "%" encodings back to the character equivalents.
  • Two new String methods, sha256() and digestMD5(), calculate standard hash values for the string's contents. sha256() calculates the 256-bit SHA-2 (Secure Hash Algorithm 2) hash, and digestMD5() calculates the MD5 message digest. The same methods are available on ByteArray to hash the byte array's contents, and on File to hash bytes from the file.
  • It's now easier to convert between strings and ByteArray objects. First, a ByteArray can now be converted to a string via toString(), or via implicit conversions, such as a "<< >>" embedding in a string. The bytes in the array are simply treated as Unicode character codes. Second, ByteArray.mapToString() can now be called without a character set argument, or with nil for the character set; this performs the same conversion as toString(). Third, the ByteArray constructor has two new formats: new ByteArray('string') creates an array containing the string, treating each character as a Latin-1 character; and new ByteArray('string', charmap) is equivalent to 'string'.mapToByteArray(charmap), but is a little more intuitive syntactically. Similarly, String.mapToByteArray() can now be called without the character mapper argument, which is equivalent to new ByteArray('string').
  • ByteArray.mapToString(), the ByteArray constructor, and String.mapToByteArray() now accept a string giving the name of a character set in place of a CharacterSet object. The methods simply create a CharacterSet object for the given name automatically. This is more convenient for one-off conversions, but if you're using a character set repeatedly keep in mind that it's more efficient for you to create the object once and reuse it.
  • Concatenating nil to a string with the "+" operator now simply yields the original string. That is, nil is treated as equivalent to an empty string for this operator. In the past, the literal string "nil" was appended instead. This was almost never useful, whereas it's often a convenience to have nil treated as an empty string in this situation.
  • The compiler now recognizes the "\r" escape code in string literals. \r represents a Carriage Return character, ASCII 13, which is the same meaning this code has in C, C++, Java, and Javascript. \r wasn't part of the TADS escape set historically (although you could always code it numerically as \015 or \u000D), mostly because there was never much need for it in practice. TADS tries to smooth out newline differences among platforms by representing all newline sequences as \n characters, so it's rare for a \r to find its way into a TADS string in the first place. We've added \r mostly for the sake of completeness and familiarity for C/Java programmers.
  • The built-in function makeString() now returns an empty string if the repeat count argument is zero, and throws an error if the count is less than zero. In the past, all repeat counts less than 1 were treated as though 1 were specified.
  • The new List method splice() lets you delete a portion of a List, insert new elements into the list, or both at the same time. splice(idx, del, ins1, ins2, ...) deletes del elements of the list starting at index idx, and then inserts the new elements ins1, ins2, etc., in their place. The new elements are optional; if they're omitted, the method only does the deletion. If del is 0, the method only does the insertion. The equivalent method is also now available for Vector.
  • The new static List method generate() creates a list with a given number of elements by invoking a callback function to generate each element's value. This is similar to mapAll(), but rather than transforming an existing list, generate() constructs a new list from a formula. For example, for a list of the first ten even integers, we can write List.generate({i: i*2}, 10).

    Vector.generate() works the same way to generate a new Vector.

  • The Vector constructor now allows you to omit the initial allocation argument in most cases. You can call new Vector() without any arguments to use a default initial allocation (currently 10 elements). new Vector(source), where source is a list or another Vector, creates a Vector copy of the source object using the source object's length as the initial allocation size. (This change is meant to make the Vector programming interface more consistent with the spirit of the class as a high-level, automatic collection manager. The old requirement to specify what amounts to an optimization parameter for every Vector was rather out of character with this spirit.)
  • Most of the List and Vector methods that take an index value arguments now accept negative index values to count backwards from the last element: -1 is the last element, -2 is the second to last, and so on. For methods that insert elements, 0 generally counts as one past the last element, to insert after the last existing element. See the individual List and Vector method descriptions for details. Note that this works only with method calls, not with the [ ] subscript operator.
  • The built-in functions max() and min() now accept a single list, vector, or other list-like object value as the argument. The result is the highest or lowest value in the list.
  • List has four new methods for finding minimum and maximum elements, optionally applying a mapping function to the element values to be minimized or maximized. indexOfMin() returns the index of the element with the minimum value; minVal() returns the minimum element value; indexOfMax() returns the index of the element with the maximum value; maxVal() returns the maximum element value. With no arguments, these methods all simply compare the element values directly, and minVal() and maxVal() return the lowest/highest element value. These methods can all optionally take one argument giving a function pointer. If the function argument is supplied, the methods call the function for each element in the list, passing the element's value as the argument to the callback function, and the methods all use the return value of the function in place of the element value. For example, if lst is a list of string values, lst.maxVal({x: x.length()}) returns the length of the longest string in the list.

    Vector has the same four new methods.

  • The File object has a new pair of methods, packBytes() and unpackBytes(), that make it much easier to work with raw binary files, especially files in third-party or standard formats such as JPEG or MP3. The new methods convert between bytes in a file and TADS datatypes in your program, using a mini-language that can express complex data structures very compactly. This is based on the similar facility in Perl and php, so if you're familiar with one of those you'll already know the basics. ByteArray and String have their own versions of the methods as well, for times when you want to prepare byte structures in memory rather than in a file. For details, see Byte Packing in the System Manual.
  • A new static (class-level) File method, File.getRootName(), extracts the "root" portion of a filename string. This is the portion of the filename that excludes any directory or folder path prefix. For example, given a string 'a/b/c.txt' while running on a Unix machine, the function returns 'c.txt'. The function uses the correct local naming rules for the OS that the program is actually running on.
  • In File.openTextFile() and File.openTextResource(), if the character set isn't specified (because the parameter is missing or nil), the default is now the system's default local character set for file contents. This is the same character that getLocalCharSet(CharsetFileCont) returns. In the past, the default was always "us-ascii". Another small enhancement is that the character set parameter can now be given as nil, which explicitly selects the default (in the past, if you wanted the default, you had to omit the parameter entirely).
  • A new static (class-level) File method, File.deleteFile(), lets you delete files. The file safety level for write mode applies to deletions, so you can only delete a file that you could also overwrite.
  • File.writeBytes() now accepts a File object as the source of the data to write to the file. This makes it easy to copy a portion of one file to another file. When the source is a File object, the start parameter specifies a seek location in the source file; if start is omitted or nil, the default starting location is the current seek location in the file.
  • A new File method, setMode(), lets you change the data mode of an open file.
  • In the past, File.getPos() wasn't reliable when reading from text files. The File object internally buffers text read from the file so that it can perform character set conversions, and getPos() was incorrectly returning the position of the last byte read into the internal buffer, rather than the read position within the buffer. This made it impossible to reliably seek back to a starting location in the middle of a series of text reads. This is now fixed.
  • Reading from a file opened in one of the read/write access modes (FileAccessReadWriteKeep, FileAccessReadWriteTrunc) didn't work in past versions; it could cause a crash. This has been corrected.
  • The File methods that open resource files are now affected by the file safety level. If a resource isn't bundled into the .t3 file, the open-resource methods traditionally looked for the resource as a separate, unbundled file within the image file's folder. They now do this only if the file safety level would allow access to the same file through a regular open-file method. This makes it especially important for you to explicitly bundle any resources directly into the .t3 file, since bundled resources are always accessible, regardless of file safety settings.
  • The new intrinsic class TemporaryFile provides support for temporary files in the local file system. A temporary file is a file that only exists for the duration of the program's execution, and is automatically deleted when the program exits. You can manipulate temporary files even when the file safety level prohibits access to the local file system, because their inherent limitations prevent misuse by malicious programs.

    Use new TemporaryFile() to create a TemporaryFile object. The system automatically assigns the new object a unique filename in the local file system, typically in a special system directory designated by the operating system. You don't specify the name of a temporary file, since the system chooses the name for you to ensure uniqueness. Creating the TemporaryFile object doesn't actually create a file on disk; it merely generates a filename that you can use. You can then create, read, write, and otherwise manipulate the actual file using the File object. Pass the TemporaryFile object in place of the filename string to open the file. You can also use TemporaryFile objects in most other system functions that operate on files (saveGame(), restoreGame(), setLogFile(), scriptScriptFile(), and logConsoleCreate()).

  • The file safety level is now separated into two components, one for reading files and the other for writing files. The safety levels have the same meanings as in past versions, but you can now select the read and write levels separately by specifying two digits in the -s option. The first digit is the read level, and the second is the write level. For example, -s04 allows all read access, but blocks all write access. If you specify only one digit, the given level is applied to both read and write operations, so the option works the same way it used to if you use the old syntax. The default is still -s2, which allows read and write access to the directory containing the image file, and denies all other file access.
  • The file safety levels that restrict access to the image file directory now consider files within subdirectories of that directory to be within that directory. In the past this was ambiguous, although most systems considered only allowed access to files directly in the image file directory. It makes more sense to allow subdirectory access than to forbid it: subdirectories are conceptually contained within their parent directory, so access within subdirectories is still effectively sandboxed to the containing directory.
  • Saved game files can now include an optional "metadata" table, containing game-defined descriptive information about the state of the game at the time of creating the save file. This can include any information the game wishes to include, such as the current room location, score, number of turns, chapter number, etc. The interpreter and other tools can extract this information and display it to the user when browsing a collection of saved game files, to help jog the user's memory about the game position saved in the file. See saveGame() in the System Manual for details.
  • The setLogFile() and setScriptFile() built-in functions now return values, to indicate success or failure. On success, the functions return true; on failure, they return nil. A failure result from setLogFile() means that the specified file can't be created, and from setScriptFile() it means the file doesn't exist or can't be opened.
  • The new IntrinsicClass static method isIntrinsicClass() lets you determine if a given object is an IntrinsicClass instance. This isn't possible using ofKind() or getSuperclassList(), because those methods work within the inheritance class tree for the intrinsic types. This new method is required to make this determination. IntrinsicClass is only used for the representation of an intrinsic class, and isn't involved in the inheritance structure. For more information, see the IntrinsicClass documentation.
  • You can now enter expressions containing anonymous functions interactively in the debugger (such as in the "Watch" list, breakpoint conditions, or the expression evaluation dialog). This wasn't allowed in the past.
  • The debugger now shows the contents of a Vector in-line when you inspect its value, just like a list. This saves you the trouble of using the "+" box in the watch window every time you want to look at a small vector's contents. You can still use the "+" box as usual, but when a vector only has a few elements, the in-line display is usually all you'll need.
  • The debugger now makes it easy to view the contents of a LookupTable. First, when a LookupTable value is displayed in a tooltip or in a watch window, the list of keys and values is displayed using the new [key->value] list notation. Second, when inspecting a lookup table object in a watch window, you can now click the "+" symbol to inspect the list of key/value pairs stored in the table.
  • The debugger now shows the original pattern string in-line when inspecting a variable containing a RexPattern object.
  • When rand() is used with multiple arguments, it now only evaluates the one randomly chosen argument value. This means that side effects will only be triggered for the chosen argument, and not for any of the other arguments. This is useful because it means that you can use rand() to intentionally trigger a randomly selected side effect. For example: rand("one", "two", "three", "four", "five") prints out a randomly selected number from one to five, and rand(f(x), g(x), h(x)) randomly calls one of the functions f(), g(), or h().
  • The rand() function can now generate a random string based on a template string. When rand() is called with a single string argument, the function returns a string of random characters chosen according to the template. See the rand() documentation for details.
  • The interpreter now automatically seeds the random number generator at the start of the run. In the past, it was up to the program to do this by explicitly calling the randomize() function. The reason for changing the default is that defaults in general should be the settings most people would want most of the time, and in this case that's clearly automatic randomizing. The only time you'd want not to randomize is during regression testing, when you want to verify that the program runs exactly the same way every time. When you do want a repeatable random number sequence, specify the new -norand interpreter option: t3run -norand mygame.t3
  • A new BigNumber method, numType(), returns information on the type of value represented by the BigNumber. This allows you to identify the special distinguished values "Not a Number" (NaN) and positive and negative infinity.
  • A few new BigNumber.formatString() flags have been added: BignumCompact (use the more compact of the regular format or scientific notation); BignumMaxSigDigits (count only significant digits against the maxDigits limit, not leading zeros); BignumKeepTrailingZeros (keep trailing zeros after the decimal point to fill out the result to maxDigits in length).
  • toString() now respects the radix argument for BigNumber values that are whole integers.
  • toString() accepts a new "isSigned" argument that lets you control whether a signed or unsigned interpretation should be used for integer values. In the past, this was tied to radix - decimal treated values as signed, any other radix as unsigned. The default is the same as before, but you can now override it to get an unsigned decimal representation, or a signed hex representation, for example.
  • toInteger() now accepts any radix from 2 to 36. The letters A through Z represent digit values 10 through 35 for bases above 10, in analogy to hexadecimal notation. For better consistency with its name, the function also now converts the strings "true" and "nil" respectively to 1 and 0 (instead of the boolean true and nil values that it formerly returned), and converts the boolean values true and nil to 1 and 0. It's also a little more liberal about parsing strings containing the text "true" or "nil", in that it now ignores leading and trailing spaces.
  • The new function toNumber() is similar to toInteger(), but also parses strings representing BigNumber values. If the input is a string representing a floating point value (i.e., it has a decimal point or uses the 'E' exponential format), or an integer that's too large for an ordinary 32-bit TADS integer, the value is returned as a BigNumber. If it's a whole number that fits in a 32-bit integer, it's returned as an integer. This routine can also be used to parse a BigNumber integer value in a non-decimal radix.
  • The compiler now pays attention to resource file timestamps in determining whether it needs to rebuild the image file. In the past, merely updating a resource file didn't trigger a relink, so the image wouldn't be updated with a new resource until it was relinked for some other reason.
  • The new "if-nil" operator ?? checks an expression to see if the value is nil, and if so yields a default value. a ?? b yields a if a is not nil, otherwise it yields b. This is a concise and efficient (in terms of code size and execution time) way to write a default value substitution, which is a common situation when using argument values from callers, property values set elsewhere in the code, and function and method call results.
  • The new operator >>> performs a logical right shift. Furthermore, the existing >> operator is now explicitly defined as performing an arithmetic right shift. In the past, it wasn't specified whether >> performed an arithmetic or logical shift, although in practice all existing implementations (as far as we know) performed an arithmetic shift, so existing programs shouldn't see any change.

    The difference between the arithmetic and logical right shift is the treatment of the vacated high-order bits. A logical right shift fills all vacated bits with zeros, whereas an arithmetic right shift fills the vacated bits with the original high-order bit of the left operand. Arithmetic shifts are so named because copying the high-order bit preserves the sign of the original value.

    The >>> operator isn't purely a TADS invention. Java and Javascript define it the same way TADS does, and likewise specify that >> performs an arithmetic shift.

  • Many of the standard bundled character sets now recognize a number of name variations, based on naming conventions used in various other programming languages and applications. They're meant to make the character mapper a little easier to use by letting you use names that you might be accustomed to using from other systems, rather than having to remember a separate naming scheme for TADS. The new variations (which are all insensitive to upper/lower case) are:
    • Latin-X: In the past, these had to be called "isoN", as in "iso2" for Latin-2. The mapper now also accepts Latin2, Latin-2, ISO-2, 8859-2, ISO8859-2, ISO-8859-2, ISO_8859-2, ISO_8859_2, and L2. (Likewise for Latin-3, Latin-4, etc.)
    • Windows and DOS code pages: The old name was "cpXXX", where XXX is the code page number, as in "cp1252". The mapper now also accepts just plain 1252, as well as win1252, win-1252, windows1252, windows-1252, dos1252, and dos-1252.
    • UCS-2LE: The little-endian 16-bit Unicode character sets can also be called UCS2LE, UTF-16LE, UTF16LE, UTF_16LE, UnicodeL, Unicode-L, and Unicode-LE.
    • UCS-2BE: The big-endian 16-bit Unicode character sets can also be called UCS2BE, UTF-16BE, UTF16BE, UTF_16BE, UnicodeB, Unicode-B, and Unicode-BE.
  • BigNumber now uses the standard "round to nearest, ties to even" rule whenever rounding occurs, including calculation results and explicit rounding requests. This is the rounding method that most computer floating point systems use, because it's considered the least statistically biased.

    The new rule is to round to the nearest digit, or to round to the nearest even digit when exactly halfway between two digits. For example, rounding 104.5 to integer yields 104: the value is exactly halfway between 104 and 105, so we round to the nearest even digit for a result of 104. Similarly, rounding 1.2345 to four digits yields 1.234, since 1.2345 is exactly halfway between 1.234 and 1.235, and the nearest even digit in last place is 4.

    The old algorithm was almost identical: it was "round to nearest, ties away from zero", which rounded to the next higher absolute value when exactly halfway between digits. For example, rounding 104.5 to integer formerly yielded 105, since we formerly always rounded up from the halfway point. There's no change for values that aren't exactly halfway between digits: rounding 102.5001 or 103.4999 to integer both yield 103 under both the new and old rules.

  • The console output formatter now renders embedded null characters (Unicode value U+0000) as spaces. In the past, the formatter considered a null to be the end of a string, which isn't consistent with TADS string semantics.
  • The compiler is less conservative than it used to be about a certain optimization involving anonymous functions. In the past, every anonymous function expression triggered the creation of an AnonFuncPtr object. This object contains the anonymous function's context: the local variables and "self" from its enclosing code block. However, many anonymous functions are entirely self-contained, meaning they don't make any reference to their enclosing contexts. These don't actually need a context object, since they have no dependency on anything that would go in it. There's a slight performance cost to creating the context object, so the compiler now creates one only when it's actually needed.

    This change should be almost invisible to game code. It's possible to detect if you're looking for it by checking the dataType() of an anonymous function value: it was formerly always TypeObject, but now it can also be TypeFuncPtr, for cases where no context object is needed. Other than this (and a slight speed improvement), the change should be transparent.

  • It's now possible to break out of an infinite loop if you should happen to get stuck in one evaluating an expression within the debugger. The debugger already let you break into normal program execution that was stuck looping, using a platform-defined keystroke or other UI action (on Windows, for example, the Workbench debugger uses the key combination Ctrl+Break). The same thing now works if you evaluate a debugger expression that gets stuck in a loop. (In the past, the debugger ignored the "break" command while it already had control. It now interrupts the expression by throwing an error.) (bugdb.tads.org #0000093)
  • In past versions, the StringComparator object didn't save its list of character equivalence mappings properly to the image file or to saved game files. This caused character mappings to be lost (corrupted, actually, but for practical purposes lost) when they were made during preinit, or when restoring a game where a StringComparator with mappings had been created dynamically. This has been corrected. (bugdb.tads.org #0000085)
  • The multi-method inherited() operator didn't work properly if used within a function that was replaced by another function using the "modify" statement. This is now fixed.
  • A bug in the compiler sometimes caused a crash in a rare situation involving a link-time symbol conflict (in particular, the same symbol defined as a grammar rule in one module and a different type in another module). This has been corrected.
  • A bug in the regular expression search functions prevented matching zero-length expressions at the end of the subject string. For example, searching for 'x*$' correctly matched 'uvwx' (with a match length of 1), but not 'uvw' (which should match with a length of 0). This has been fixed.
  • A regular expression bug caused incorrect results to be returned for capturing groups for certain complex expression types. This was triggered by an expression with two wildcard closures preceding a capturing group preceding some fixed text. This is now fixed. (bugdb.tads.org #0000088)
  • In past versions of the text-only interpreters, if an <ABOUTBOX> section contained an <IMG> tag with an ALT attribute, the ALT text was displayed. It shouldn't have been, because the text-only systems aren't supposed to display anything that's within an <ABOUTBOX> section. This has been corrected. (bugdb.tads.org #0000063)
  • The text-only interpreters now parse and translate HTML entity markups ("&" sequences) within <TITLE> tags when setting the window title. (In past versions, the text-only interpreters simply displayed the literal text of the & sequences. The HTML interpreters didn't have this problem, so this change doesn't affect them.) (bugdb.tads.org #0000062)
  • The bug fix in 3.0.18 for newline translation in UTF-16 text files introduced another bug, which corrupted output text when calling writeFile() with a string containing embedded newline ('\n') characters. This has been corrected. (bugdb.tads.org #0000065)
  • The compiler formerly generated code that was arguably incorrect for a compound assignment expression containing side effects in the lvalue. In the past, the compiler effectively expanded an expression of the form lvalue op= rvalue into lvalue = lvalue op rvalue. For example, "a += b" became "a = a + b". In most cases this is fine, but when the lvalue contains side effects, it results in two evaluations of the side effects. For example, "lst[i++] += 1" incremented "i" twice. For a really complex expression like "lst[i++][j++][k++] += 2", the "i++" side effect was triggered even more times, because it contains multiple implied lvalues.

    The correct behavior is to evaluate each subexpression in the lvalue only once. The compiler now generates code that does just this.

    The old behavior was only arguably wrong, in that the correct behavior wasn't really specified anywhere. But to the extent that TADS doesn't fully specify its expression behavior, it's reasonable to expect that TADS should behave like C++. C++ does have well-defined semantics for this situation, which the new behavior matches. Plus, the new behavior is in line with the obvious reading of an op= expression: the lvalue is only mentioned once in such an expression, suggesting that it should only be evaluated once.

  • A bug in the debugger caused a crash under rare circumstances. The problem happened when a run-time error occurred within a callback function being invoked from certain built-in functions or methods, and you then terminated the program via the debugger UI while execution was still suspended at the error location. This is now fixed.
  • A bug in the Vector intrinsic class caused rare, random crashes under certain conditions when calling mapAll(). The crashes were most likely with very large vectors, and only when the callback function could trigger garbage collection (such as by creating a new object). This has been fixed.
  • A bug in the BigNumber class caused inaccurate results for expE(), raiseToPower(), sinh(), cosh(), and tanh() for certain values. The problem only affected a limited (but difficult to characterize) range of input values. For affected values, the results were still correct to about 17 decimal places; the inaccuracy appeared starting at about the 18th decimal place, so it would only have been noticeable in applications requiring relatively high precisions. For example, a program that would have been happy with the standard C "double" type wouldn't have noticed the bug, because the typical C double provides only about 15 decimal digits of precision. The problem is now fixed.
  • In past versions, the ByteArray object didn't save undo information for File.readBytes() or ByteArray.writeInt() operations. It now saves the undo.
  • The compiler incorrectly reported internal errors for assignments to objects, functions, and other global symbol names. The compiler now shows a regular error message instead for this type of error. (bugdb.tads.org #0000071)

Changes for 3.0.18.1 (May 5, 2009)

  • The behavior of the multi-method inherited() operator has changed. The operator now chooses the inherited function to call based on the actual argument values, rather than the formal (declared) parameters to the current function. This is more consistent with the inherited() operator for regular methods.
  • A bug in the compiler, versions 3.0.17, 3.0.17.1, and 3.0.18, caused incorrect (too small) stack size requests to be generated for some functions and methods. This generally had no noticeable effect on valid programs, but it made it possible for an errant program (with infinite recursion, for example) to cause a stack overflow that the interpreter doesn't detect immediately, which could conceivably crash the interpreter. An actual crash would require an unlikely combination of factors, even with an errant program compiled with this bug, so you'll probably never notice it. Even so, if you distributed any .t3 files compiled with t3make versions 3.0.17 through 3.0.18, we recommend recompiling with the current t3make version and replacing your previously distributed files, if it's easy for you to do so.

Changes for 3.0.18 (April 28, 2009)

  • The inherited operator can now be used within a multi-method to inherit an overridden version of the method. The new syntax inherited<type-list>(arg-list) lets you inherit a specific version of the multi-method, while the basic syntax inherited(arg-list) automatically selects the next more general version of the multi-method. See the System Manual section on multi-methods for documentation.
  • The compiler pre-defines the new preprocessor symbol (i.e., macro) __TADS3, which expands simply to the number 1. This provides an easy way to detect that the compiler is a TADS 3 compiler (as opposed to a TADS 2 compiler) for conditional compilation purposes. (This might seem redundant with the existing symbol __TADS_SYSTEM_MAJOR, but __TADS3 can be used in one way that __TADS_SYSTEM_MAJOR can't. If for some reason you wanted to create a single source file that could be compiled under TADS 2 or TADS 3, using conditional compilation to handle the syntax differences between the language versions, you couldn't do it with __TADS_SYSTEM_MAJOR. This is because TADS 2 doesn't support #if, which is the only way you could use __TADS_SYSTEM_MAJOR to conditionally compile based on compiler version (e.g., #if __TADS_SYSTEM_MAJOR == 3). Since TADS 2 and 3 both support #ifdef, though, you can use the presence or absence of the __TADS3 symbol to conditionally include or omit code based on language version. Of course, since older versions of the TADS 3 compiler also lack this symbol, this approach requires using 3.0.17.2 or later on the TADS 3 side.)
  • In past versions, writing to text files with a UTF-16 encoding generated incorrect newline sequences when running on Windows. The problem had to do with the two-character representation of a newline in Windows. This has been corrected.
  • In the past, multi-methods defined in one source file and called from another resulted in an "undefined symbol" error. This has been corrected.

Changes for 3.0.17.1 (8/12/2008)

  • A compiler bug introduced in 3.0.17 caused Workbench (and the stand-alone interpreter) to crash when loading certain games when compiled for debugging. The compiler was mis-calculating some internal data structure sizes, which effectively corrupted the .t3 file; this happened when the byte code for a function plus its debug symbols was over a certain threshold size. This has been corrected.

Changes for 3.0.17 (8/9/2008)

  • The compiler has a new feature called "multi-methods." This implements a relatively new object-oriented programming technique known as multiple dispatch, in which the types of multiple arguments can be used to determine which of several possible functions to call. The traditional TADS method call uses a single-dispatch system: when you write "x.foo(3)", you're invoking the method foo as defined on the object x, or as inherited from the nearest superclass of x that defines that method. This is known as single dispatch because a single value (x) controls the selection of which definition of foo will be invoked. With multiple dispatch, this notion is extended so that multiple values can be considered when selecting which method to invoke. For example, you could write one version of a function "putIn()" that operates on a Thing and a Container, and another version of the same function that operates on a Liquid and a Vessel, and the system will automatically choose the correct version at run-time based on the types of both arguments. This new system is described more fully in the System Manual.

  • The compiler can now generate information in each object about the source file where the object was defined. This new information is stored in a new property, sourceTextGroup; this supplements the existing sourceTextOrder property. Since the new information takes up extra space in the compiled file, it's not generated by default. To generate it, include the new compiler option "-Gstg" in your makefile or command line, or place the directive #pragma sourceTextGroup(on) directive in each source module where you wish to generate the information. The #pragma lets you selectively generate the information in specific source files, or even in specific portions of specific source files: the corresponding directive #pragma sourceTextGroup(off) lets you turn off the information in a section where you don't need it.

    When you turn on sourceTextGroup generation, the compiler automatically adds a sourceTextGroup property to each object, just as it automatically adds a sourceTextOrder value. The sourceTextGroup value is a reference to an anonymous object that the compiler automatically creates. One such object is generated per source module for which sourceTextGroup generation is activated. This object contains two properties: sourceTextGroupName is a string giving the name of the module, as it appeared in the compiler command line, makefile (.t3m), or library (.tl) file; sourceTextGroupOrder is an integer giving the relative order of the module among the list of modules comprising the overall program.

    sourceTextGroup is useful in cases where you want to establish the order of appearance in source files among objects in multiple modules. For a given object x, x.sourceTextGroup.sourceTextGroupOrder gives you the relative order within the overall build of the module where x was defined, and x.sourceTextOrder gives you the relative order of x among the objects defined within that module. Between the two values, you can establish a linear ordering for all of the statically defined objects in the entire program.

  • The compiler now stores "links" to resource files in the .t3 file when compiling in Debug mode. This makes it easier to test a game that uses multimedia resources, by making the Build environment match the Release environment more precisely.

    In the past, when building in Debug mode, the compiler completely ignored multimedia resource files listed in the "-res" section of the command line or makefile. This was by design; the reasoning was that when you're compiling in Debug mode, you'll only be running that copy on your development machine, within your build environment, where all of the resource files are available as local files; and since the files are all available as separate local files anyway, we can make the compilation go faster by skipping the step where we copy those files into the .t3 file.

    This worked fine most of the time, but it didn't work in the occasional case where the resource name is different from the corresponding local file's name in the file system. One example is the Cover Art file, which is always stored in the game under the resource name ".system/CoverArt.jpg", which is usually not the same name the file has locally. This meant that if you tried to refer to such a resource via HTML (such as with an <IMG> tag), the resource was reported as missing when running the debug build.

    The new arrangement fixes this problem without giving up the time savings. With the new scheme, the compiler stores a link for each resource in the .t3 file: this simply records the mapping from resource name to local file name. The HTML renderer can then look up the appropriate local file for each resource, including any resources that have special names. As before, the actual contents of the resources are not copied.

    Note that none of this affects Release builds. The compiler has always copied the full contents of all resources into the .t3 file when doing a Release build, and continues to do so. Release builds continue to be fully self-contained, so that you only have to distribute the .t3 file to players, not the individual graphics and sound files it refers to.

  • The compiler's status output didn't add a line break after each resource files being added to the project, resulting in poorly formatted displays. This has been corrected.

Changes for 3.0.16 (4/10/2008)

  • The Author's Kit installer now gives you an option to skip creating the Windows file type associations for Workbench (specifically, project (.t3m) files).
  • The main startup routine in the library now performs an explicit garbage collection pass just before performing pre-initialization. In most cases, this is unnecessary (and harmless, obviously, apart from the added execution time required to perform the GC pass). However, under a certain combination of conditions, it can be important. In particular, it's important when a program is compiled so that preinit is performed at run-time rather than compile-time (this is normally the case for a "debug mode" build, but can be explicitly selected for a release build as well), and the program has pre-initialization code that performs global object loops. In such a program, a RESTART would re-run preinit, which could then encounter garbage objects still around from before the RESTART. The explicit GC pass just before preinit ensures that preinit sees only reachable objects in any global object loops it performs.
  • A bug in the ByteArray intrinsic class caused an interpreter crash when copying exactly the full bounds of an array onto itself. This has been corrected.
  • The rexReplace() intrinsic function got stuck in an infinite loop when the pattern to be replaced matched zero characters of the subject string. For example, a match pattern of "x*" is able to match any number of "x" characters in a row, including zero of them. The problem was that the function simply spun its wheels, repeatedly finding the same zero-character match and replacing it over and over. This no longer occurs: the function will apply each zero-character replacement once only, and then move on to the next character of the subject string before attempting a new match.
  • The rexSearch() and rexReplace() functions didn't properly take into account "^" (start-of-string) pattern specifiers when a starting offset for the search was explicitly specified. The functions incorrectly considered "^" to match the start of the substring starting at the given starting offset, whereas it should have only matched the start of the entire string. In other words, "^" should never match when the starting offset is greater than 1. This has been corrected.
  • A bug in the t3DebugTrace() intrinsic function sometimes caused the debugger to stop execution at the second line after the t3DebugTrace() function, rather than at the next line. This happened if you used the "Go" command to resume execution from the point where t3DebugTrace() stopped, then encountered the same t3DebugTrace() call without any intervening debugger activity. This has been corrected.
  • Comparing another object value to "self" in a global breakpoint expression sometimes caused the debugger to crash in past versions. This has been corrected.
  • In the past, the compiler returned a "success" indication to the operating system shell as long as no error messages were displayed during the compilation. This was incorrect in cases where the "treat warnings as errors" option was enabled and one or more warning messages were displayed; in these cases, the compiler should have returned a "failure" indication to reflect the fact that warnings count as full-fledged errors under this option setting. This has been corrected.
  • Applying toInteger() to BigNumber values near the capacity limits of the 32-bit integer type, but not exceeding them, sometimes produced spurious "numeric overflow" errors. In particular, values over 2147483640 or under -2147483640 that needed to be rounded up in their fractional parts (e.g., 2147483640.9) produced this spurious error. This has been corrected.

Changes for 3.0.15.3 (11/2/2007)

  • A bug in the compiler made it impossible to refer to items in global scope (such as enum values or objects) in constant expressions within anonymous functions. For example, it was impossible to use an enum value or object name as a 'case' label in a 'switch' statement within an anonymous function. This has been corrected.
  • A bug in the GrammarProd intrinsic class caused incorrect results in some cases for an empty production (i.e., a grammar rule consisting of no tokens). The problem had to do with the way the empty production figured its position in the input token list; since enclosing levels in the match tree find their positions relative to child nodes, the problem typically manifested itself by giving incorrect token list positions for one or more match levels enclosing an empty match. This is now fixed.

Changes for 3.0.15.2 (9/13/2007)

  • The inputFile() function now makes some additional checks when the filename is supplied by an active event script file. If the dialog type is InFileOpen, the function checks to make sure the filename read from the script refers to an existing file. If the dialog type is InFileSave, and the named file doesn't already exist, the function tries to create a dummy copy of the file; if that succeeds, the function immediately deletes the dummy copy. The function also still tests, as it did before, to make sure the file doesn't already exist for InFileSave dialogs. If any of these tests fail, the function warns the user about the problem and offers the same options it did in the past for the save-overwrite case: use the filename as given in the script, choose a different file, or cancel the script playback.
  • A bug in the VM caused sporadic, unpredictable errors when finalizers were used. The problem affected one opcode (OPCGETPROPR0); if a finalizer invocation happened to interrupt this particular opcode, the result was unpredictable, but could include spurious run-time errors or corruptions of calculated results. This has been corrected.

Changes for 3.0.15.1 (7/19/2007)

  • The keyword replaced can now be used within anonymous functions that are nested in modifier functions (i.e., functions defined with modify funcname ...). In the past, the compiler only allowed replaced to be used directly within a modifier function - not in nested anonymous functions defined within a modifier function. There's no inherent reason for disallowing this construct; it was simply a limitation of the compiler, which has now been lifted.
  • In the system library, the several internal convenience macros defined with the Tokenizer class (tokRuleName, tokRulePat, tokRuleType, etc) have been moved from the tok.t class file to the tok.h header file. The macros are unchanged, so this change won't affect existing code. The macros were formerly defined in tok.t rather than in the header because they were meant as private macros for internal use within the Tokenizer class itself, not for client code that uses the Tokenizer class. However, they're also useful in subclasses of Tokenizer, so it's desirable to have them defined in a header - that way, user code implementing Tokenizer subclasses can access the macros simply by #including tok.h.
  • The compiler is now stricter about interpreting tokens as BigNumber constants. In the past, the compiler treated an "E" following a numeric constant as an exponent signifier, even if no digits followed. For example, "3E-a" was taken to be the BigNumber constant "3E-0", followed by a separate token "a". The compiler now treats an "E" as the start of a separate token if no digits follow it (or the optional + or - sign after the "E"), so "3E-a" would now be treated as four tokens: "3", "E", "-", and "a".
  • The compiler now displays a specific error if it finds a decimal digit within an octal constant value. In the past, the compiler simply assumed that a decimal digit terminated the octal constant token and started a new token. This typically led to a confusing parsing error, since the compiler saw two consecutive numeric constant tokens where the user almost always intended one. The compiler now flags a specific error about the ill-formed octal value, and skips any decimal digits following the octal constant, which makes the error much easier to pinpoint.
  • The compiler is now strict about disallowing the old TADS 2 "<>" (unequal-to) operator. This variation was not documented, but has up to now been allowed.
  • The compiler no longer treats delete as a reserved word. (It did so in the past as a hold-over from TADS 2, where delete is indeed reserved.)

Changes for 3.0.15 (3/8/2007)

  • When an input event script is being played back, the inputFile() function now prompts the user if the result would likely overwrite an existing file. inputFile() in this case prompts the user interactively, momentarily halting script playback until the user responds. This is described more fully in the Input Scripts section of the System Manual.
  • The compiler reported an internal error ("too much unsplicing") in certain complex expressions involving multi-line string literals as macro arguments in embedded << >> expressions. This has been corrected.

Changes for 3.0.14 (2/9/2007)

  • The raiseToPower() method of the BigNumber class incorrectly returned 1 as the result of raising 0 to a non-zero exponent. The correct result should, of course, be 0 for any positive exponent, and an exception for any non-positive exponent (as 0^0 is undefined, and 0^n with n<0 is effectively division by zero). The method now yields these results.

Changes for 3.0.13 (1/19/2007)

  • A new t3make option, -we, tells the compiler to treat warnings as though they were errors, for the purposes of determining whether or not to proceed with compilation after a warning occurs. When the compiler finds an error within a source file, it stops the build process after finishing with that compilation unit, requiring you to fix the error before continuing. In contrast, warnings do not, by default, interrupt the build. This is because a warning message indicates a situation that's technically correct, but which contains a common pitfall. Compiler warnings tend to be right often enough to warrant close inspection, so many programmers adhere to the discipline of insisting on "clean" builds every time - that is, with no warning messages. The -we option is a convenient way of enforcing that discipline. When you specify the -we option, the compiler will stop the build after finishing with any module that generates any warning messages, just as it does for error messages. If you want to explicitly set the default mode, where warnings do not interrupt the build, use the -we- option.
  • A new interpreter (t3run) option, -I, runs a command script in "echo" mode. This works almost the same as the existing -i option, but -I causes all output to be displayed to the console as the script is read; in contrast, -i reads the script in "quiet" mode, meaning that no output is displayed while the script is running.
  • The command script recorder and player can now handle virtually any input event type, not just command lines. The script mechanism can now handle keyboard input, hyperlinks, ad hoc dialogs, and file dialogs; the functions inputKey(), inputEvent(), inputFile(), and inputDialog() can now take their input from a script. (inputLine() and inputLineTimeout() have always been able to read from scripts.) To handle the greater range of event types, a new script file format has been added; files written in new format are called Event Scripts. Event scripts are identified by a special signature: the first line of the file must read <eventscript>, with nothing else on the line (not even spaces). If a script doesn't start with this signature, the script reader will interpret it as the original script format, which is now called the Command-line Script. This allows old scripts to work without changes; of course, Command-line Scripts can only contain line input events, as they always have. When you create a script with RECORD or the with the Interpreter's -o option, the script is now recorded in the new Event Script format, and the recorder will automatically include all event types in the script. (There's no option with RECORD or -o to record an old-style Command-line Script instead - there wouldn't seem to be much call for this, as the functionality in Event Scripts is a superset of that of Command-line Scripts.) The new scripting functionality is described in the "Input Scripts" chapter of the System Manual.
  • The setScriptFile() intrinsic function now accepts a new type code, LogTypeEvent. This type signifies that a new-style Event Script should be created. The code LogTypeCommand, which was present in previous versions, continues to signify that an old-style Command-line Script should be created.
  • Input scripts now nest during playback. In the past, reading from a new input script terminated reading from any input script already in effect. Now, the console suspends the original script, and resumes reading from it upon reaching the end of the new script. Scripts can be nested to any depth. This allows an input script to "include" another by using, for example, the REPLAY command within the script.
  • In the past, the getFuncParams() intrinsic function didn't accept an anonymous function as its argument. Anonymous functions are now accepted.
  • The inputLine() intrinsic function (in the 'tads-io' function set) didn't correctly map non-ASCII characters (such as accented letters) from the user's input to Unicode. This has been corrected.
  • A compiler bug caused a crash if a "modify" or "replace" statement in one source module referred to a symbol that was actually defined in a source module that was included in the build after the first source file. This is an error condition anyway - a symbol has to be defined in the build before it can be modified or replaced, so the original definition has to occur in a source module listed earlier than the modifying module. However, the compiler now handles such an error properly, by displaying the appropriate error message and halting the build.
  • A bug in File.writeBytes() caused unpredictable results, sometimes including an interpreter crash, if a zero or negative value was passed for the starting index. The starting index is now checked for range.
  • Some internal changes to the portable VM and compiler code have been made to improve portability to 64-bit architectures.

Changes for 3.0.12 (9/15/2006)

This is the TADS 3.0 General Release version.

  • A couple of compiler error messages were internally mis-numbered, which could have caused the compiler to report the wrong error message for the affected error codes. These have been corrected.

Changes for 3.0.11 (9/8/2006)

  • The % operator now accepts only integer operands. (In the past, it failed to throw an error if a BigNumber operand was used, and instead produced a meaningless result.)
  • A comparison of the form "a==b", where a is an integer value and b is a BigNumber value, now coerces the integer to a BigNumber before comparing it. In the past, this type of comparison coerced the integer to a BigNumber only when the BigNumber was the left-hand operand. This correction ensures that "a==b" and "b==a" yield the same results when comparing integers and BigNumber values. The same change applies to the != operator.
  • In the past, the compiler reserved the names void, int, string, list, boolean, and any. These names are no longer reserved and thus can now be used freely as symbol names (for local variables, object names, etc). (These names were originally reserved in anticipation of the eventual introduction of optional static type declarations. Reserving the keywords ensured that no one would write code that would be incompatible if the feature had ever been added. The static typing feature will definitely not appear in the official 3.0 release, and at this point it seems very unlikely to be in any future version, so there's no longer any good reason to reserve these names.)
  • A compiler bug caused invalid code to be generated in the following situation:

       x: object
         m(p) { }
         n = (p)  // bad code generated here
         p = nil
       ;
    

    The bad code resulted from the compiler mistakenly using the local symbol table from the method m(p) while generating the code for n; so the compiler incorrectly treated p as a parameter variable in n, even though n has no parameters. This has been corrected.

    The local-to-Unicode character set mapper now converts any unmappable character found in a source string to Unicode U+FFFD, the standard Unicode "replacement character." In the past, the mapper converted unmappable characters to question marks, "?". Converting to question marks was undesirable because of the ambiguity it created. When compiling source code, for example, it led the compiler to think that the source file literally contained a question mark where the unmappable character was found; the diagnostics the compiler reported in these cases were thus confusing and misleading. Unicode defines the character U+FFFD specifically for the purpose of replacing source characters that cannot be mapped to Unicode; this gives the compiler and other consumers of mapped characters an unambiguous indication that the original input character was unmappable. The compiler uses this new information to report a specific diagnostic message when it finds a U+FFFD in the input stream.

Changes for 3.0.10 (8/17/2006)

  • Libraries (".tl" files) can now easily include their own multimedia resources in a build. In the past, there was no direct support for multimedia resources in libraries; an author using a library that included multimedia resources would have had to copy the library resources into the game's directory tree and then explicitly add those resources to the build. This is no longer necessary. Two new features support library resources:
    • First, Workbench for Windows now automatically adds each top-level library's directory path to the search list for individual resource files (.jpg, .png, .ogg, etc). Note that a "top-level" library is a library included directly in the Project window; a library included from within another library isn't top-level, so it's directory won't be included in the search list. When running the game under the debugger, this allows individual resources to be found under any library's directory tree.
    • Second, the compiler accepts a new "resource:" keyword in library (.tl) files. After this keyword, list one individual resource file, or one directory containing resource files. You can use this keyword as many times as you like to include multiple files or directories. As usual for library files, the file or directory mentioned after the "resource:" keyword is given with a URL-style relative path, relative to the library containing the library file.
    These new features allow a library to include multimedia resources with complete transparency to game authors using the library - game authors won't have to do anything extra to use the library multimedia resources. Library authors only have to perform a few easy extra steps. First, create a subdirectory of your library directory to contain the resources. For example, if your library is called mylib.tl, create a "mylibres" subdirectory under the directory containing mylib.tl. (The name isn't important, but it's desirable to pick a unique enough name that there won't be any conflict with other libraries that game authors might also be including. Your resources will be named relative to the subdirectory name, so don't pick a generic name like "resources" - it's best to use a name that incorporates your library name.) Second, put all of your library's resources (.jpg files, .png files, etc.) in your new subdirectory. Third, add the line "resource: mylibres" to your .tl file; this will tell the compiler to bundle all of the files in the "mylibres" subdirectory into the compiled .t3 file when a game includes your library. Finally, in the HTML that your library generates to reference your resources, refer to them using your subdirectory path - for example, you'd write <IMG SRC="mylibres/compass.png"> to refer to an image resource file.
  • The t3GetStackTrace() function (in the 't3vm' function set) has a new optional argument that lets you get information on a single stack level. This provides more efficient access to stack trace information in cases where you only want information on a specific level, such as the immediate caller.
  • When a game is compiled into a stand-alone executable (on systems where this is implemented, such as Windows), it's now possible to pass command-line arguments to the embedded .t3 program. To do this, use a dash ("-") to separate the interpreter arguments from the program arguments. The stand-alone version of the interpreter is also now strict about not accepting an image file argument; this means that you can't use a bundled stand-alone game to run a different .t3 image file.
  • You can now write directly to the main game window's log file, if one is open. (This is the log file that's opened via the setLogFile() function - the one that the library creates when the player types the SCRIPT command.) There are two ways to write to the log file. The simple way is to use the new <LOG> tag - this tag is simply the complement of the <NOLOG> tag: any text enclosed between <LOG> and </LOG> will be included only in the log file, and hidden in the game window. This approach is usually the right one, because you can mix your log-only text directly with other output text, ensuring that it's processed through the usual stack of output filters and the like. However, on occasion it's useful to be able to bypass the usual output stream mechanism, which brings us to the second way: you can use logConsoleSay(), passing in the special handle value MainWindowLogHandle as the handle. Using this approach bypasses mainOutputStream and all of its filters, and goes directly to the log file.
  • The compiler now warns on nested block comment ("/* ... */" comments). If the compiler finds a "/*" sequence within a block comment, it generates a warning. (Nested block comments aren't allowed, so a "/*" within a block comment usually indicates that a previous comment wasn't terminated properly. The warning makes it easier to notice such cases.)
  • The compiler now automatically word-wraps error messages to 80 columns when the "verbose" error message option is selected. This makes the long error messages much easier to read, especially in Workbench, which has an essentially infinitely wide message window - the word-wrapping saves you the trouble of manually scrolling horizontally to see the full message text.
  • A bug in the compiler sometimes caused a crash if a 'foreach' statement was used in a source file which never #included the system headers. The compiler correctly flagged the error, but didn't always recover properly. The compiler now handles this situation gracefully.
  • A bug in the compiler caused the compiler to ignore the last line of a library (.tl) file when the last line didn't end in a newline character of some sort. The same bug affected Workbench on Windows. This has been corrected.
  • A bug in the compiler caused incorrect results when a 'grammar' statement's token list started with an empty alternative. For example, the token list "( | 'x')" was compiled incorrectly. This problem only occurred when the empty alternative was the first element of the entire token list. This has been corrected.
  • In the past, the TadsObject type didn't retain 'undo' information when the superclass list was changed with setSuperclassList(). This meant that if an object's superclass was changed, an 'undo' operation didn't properly restore the original superclass list. This has been corrected; this undo information is now properly retained and applied.
  • A bug introduced in 3.0.9 caused the interpreter to misinterpret certain Unicode characters outside of the plain ASCII range when entered on a command line. This has been fixed.
  • A bug in TadsObject.setSuperclassList() caused sporadic problems. The bug was most likely to manifest itself as a stack overflow error when a call to self.setSuperclassList() was the first (or nearly the first) executable code within a method. The bug is now fixed.
  • The regular expression parser didn't accept the special character pattern <tab>, which is documented as matching the character '\t' (or, equivalently, '\u0009'). This has been corrected.
  • The regular expression parser didn't formerly classify as whitespace several control characters that are conventionally considered whitespace. This was due to a strict interpretation of the character properties defined in the Unicode character database, which defines all of the ASCII control characters as class "Other" rather than "Separator." The Unicode database has some additional property information, though, that does mark the appropriate control characters as whitespace, and the character classifier now takes this extra information into account. This means that characters \u0009 (tab), \u000A (line feed), \u000B (line tab), \u000C (form feed), \u000D (carriage return), \u001c (IS4), \u001d (IS3), \u001e (IS2), and \u001f (IS1) are now considered whitespace, and thus match the <space> character class pattern in regular expressions.
  • In the past, the compiler always assumed it was running under a DOS box, so it used the DOS box code page for any messages it displayed. On most systems, depending on localization, the DOS box uses a different code page from the Windows code page used by native Windows applications; this is because the DOS box is designed for compatibility with old MS-DOS applications, which sometimes rely upon the DOS line-drawing characters and so forth. This occasionally caused odd character mapping problems in error messages when compiling from Workbench, because Workbench uses the standard Windows code page instead of the DOS code page. This has been corrected, as follows: the compiler now checks to see if it's associated with a DOS box window, and uses the DOS box code page for its error messages if so, or the regular Windows code page if not.

Changes for 3.0.9 (8/29/2005)

  • The banner API has a new pair of style flags: BannerStyleHStrut ("horizontal strut") and BannerStyleVStrut ("vertical strut"). These style flags let you specify that bannerSetSize() should take a child banner's width (horizontal strut) and/or height (vertical strut) into account when figuring the parent banner's content size. By default, only the contents of the parent banner are considered, but these new styles let you include a child's contents in the parent banner's size calculation.
  • The File intrinsic class has a new feature in its various "open" methods. The filename passed to an "open" method can now be one of a set of distinguished values (defined in file.h) that identify "special" files. These are files that are meant to be used for particular purposes. Rather than opening these files by name, you open them by specifying their special identifiers. The interpreter determines the actual name and location of a special file in the local file system. This allows the interpreter to use different names of paths on different systems, to better conform to local conventions. Currently, there's only one special file defined: LibraryDefaultsFile, which is a text file that's used to store the library's global settings.
  • The new GrammarProd method getGrammarInfo() provides programmatic access to information on GrammarProd objects, which allows the game to retrieve full information on the 'grammar' statements in the source code. Sample code demonstrating how to use the new method to display a production's internal information in human-readable format is included in the samples directory, in the file gramdisp.t.
  • The interpreter now uses the default file-contents character set rather than the display character set as the default for log file output. On most systems, this change won't have any noticeable effect, as the default display and file-contents character sets are usually the same anyway. However, on a few systems, the display and file-contents character sets can differ; it makes more sense on these systems to use the file-contents character set for log files than it does to use the display character set, since it's usually desirable to treat log files as ordinary text files.
  • The new interpreter option "-csl charset" lets you explicitly specify the character set for log files, overriding the default. (The default log file character set is the file-contents character set, as described above.)
  • The logConsoleCreate() function now accepts nil for the character set argument. This chooses the default log file character set.
  • In tadsio.h, the constant InEvtNotimeout has been renamed to InEvtNoTimeout (that is, the "T" in "Timeout" has been capitalized). This change is simply cosmetic, for better consistency with the naming convention that each word in a symbol name starts with a capital letter. (The "o" in "Timeout" is still a minuscule, and this is consistent with the naming convention: in computerese, "timeout" is usually considered a single word.) To avoid breaking compatibility with code based on previous versions, the old name is still defined as a synonym for the new name.
  • The Vector class's constructor now throws an error ("value out of range") if the maximum-length argument exceeds 65535, which is the maximum number of elements that a Vector can have. (In the past, attempting to create a vector with a maximum length of more than 65535 appeared to succeed, but didn't really; it actually created a vector with a maximum of n mod 65536, where n was the given length.)
  • The compiler incorrectly considered the statement following a 'do-while' loop reachable if the loop's condition wasn't a constant 'true' expression, even if the condition itself was never reachable and the loop contained no 'break' statements. This caused the compiler to issue spurious warnings in certain obscure situations, such as when such a 'do-while' was at the end of a function that had returned values elsewhere; it also caused the compiler to fail to generate an "unreachable statement" warning when a statement followed such a loop. The compiler now correctly realizes that the statement following such a loop is unreachable, and generates control flow warnings accordingly.
  • A compiler bug sometimes caused problems when generating code involving a nested anonymous function (that is, an anonymous function defined within another anonymous function), which was defined within an object method, and which made reference to local variables defined within two or more enclosing lexical contexts. The problem only affected code with all of these attributes, and then only showed up some of the time. The problem manifested itself as a spurious run-time error ("invalid index operation - this type of value cannot be indexed") when the nested anonymous function was invoked. This has been corrected.
  • A bug in the compiler caused sporadic problems that sometimes included crashing the compiler when source code contained a constant expression involving a list indexed by a value exactly one greater than the number of elements in the list, as in "[1,2][3]". The problem only occurred when everything in the expression - all of the list elements, as well as the index value - was a constant. This has been fixed.
  • Due to a bug, the compiler didn't recognize the "^=" (XOR-and-assign) operator. This has been corrected.
  • A bug in the rexGroup() function caused the interpreter to crash in a particular, rare situation: specifically, when rexGroup() was called immediately after a call to rexSearch() or rexMatch() that caused a run-time error. This has been corrected.
  • A bug in the interpreter sometimes caused unpredictable behavior, including crashes, when t3SetSay() was used to set a property pointer handler, and a double-quoted string was then displayed from within a function (not an object method). This has been fixed.
  • A bug in the interpreter's console-output subsystem's HTML mini-parser caused subtle problems in some rare cases with the full multimedia interpreters (but not in text-only interpreters). For the most part, in the full HTML interpreters, the console subsystem just passes HTML markups directly through to the HTML renderer. It does, however, keep track of HTML sequences for its own purposes. One of these is to apply the "\^" and "\v" (capitalize and un-capitalize) flags to actual text rather than to HTML markups. The bug caused the HTML tracker to lose track of where it was in an HTML sequence when a tag included quoted attribute values. The only known symptom of the bug was that a "\^" or "\v" flag that appeared immediately before an HTML tag that contained quoted attribute values was sometimes misapplied to text within the HTML tag, effectively losing the effect of the flag. This has now been fixed.
  • A bug in the Dictionary class caused problems when setting a string comparator object that wasn't a StringComparator object. The Dictionary allowed setting the new comparator, but would then generate a spurious error (usually "invalid type") on calling any of the methods that called comparator methods (addWord, findWord, etc). This has been corrected.
  • The Vector.copyFrom() method had a bug that caused the wrong number of elements to be copied in some cases. In particular, when the destination vector had to be expanded to hold the new elements, the method copied one fewer element than was requested. This has been fixed.
  • A compiler bug caused duplicate "unreachable statement" to be displayed when a 'switch' contained a case with an unreachable 'break' statement. The duplicate messages no longer occur.
  • Due to a bug, the compiler sometimes crashed if a 'switch' statement contained an unreachable 'break' statement in one of its cases, and the statement immediately after the 'switch' was also unreachable. This has been fixed.
  • The compiler is now more consistent about where double-quoted strings are allowed and not allowed. First, superficial parentheses are now accepted around double-quoted strings wherever the strings would otherwise be legal. Second, the comma operator can now be used to juxtapose two or more double-quoted strings; the effect is simply to display the strings in the order given, as though they had been concatenated into a single string. Third, the compiler formerly accepted double-quoted strings, incorrectly, if they were used within the 'true' or 'false' operands of a '?:' operator that was embedded within any larger expression. The compiler now correctly flags this as an error.
  • The compiler is now stricter about flagging errors for a few syntactically invalid constructs that were formerly accepted. It's probably counterintuitive that more error messages are a good thing, but, really, they are in cases like this - if a construct is invalid, you're better off if the compiler catches it and tells you about it, because otherwise the compiler is going to do something poorly defined with the input. Without an error message, it won't be obvious that anything is amiss until the program starts behaving unexpectedly at run-time. The compiler now properly flags errors when it detects these constructs. Specifically, the compiler now enforces the following grammar rules:
    • Each formal parameter in a 'propertyset' declaration, including the "*" parameter, must be separated by exactly one comma from adjacent parameters.
    • 'transient' cannot be used in the definition of a new template - that is, "transient class template ..." is illegal.
    • Empty templates ("class template;") are illegal.
    • Parentheses are required around the condition expression of a do-while statement.
  • The compiler now allows "local" declarations to be labeled (via regular labels as well as "case" and "default" labels). In the past, the compiler didn't allow labeled local declarations. There wasn't any good reason for this restriction; it was just a quirk of the way the compiler's grammar was defined.
  • The toString() function formerly failed on conversions to base 2 of a value over 262143, with a run-time exception. This has been corrected, so the function should now properly convert any 32-bit integer value to a base 2 string representation.
  • The toInteger() function now accepts base 2 as the input format.
  • The compiler is now more tolerant of different newline formats in library (.tl) files. In the past, the compiler generally expected .tl files to conform to local newline conventions; this made it more difficult to re-use libraries on different operating systems, since it was sometimes necessary to convert newlines first. Source code files (.t and .h files) were never affected by this, but the part of the compiler that read .tl files wasn't as flexible. The compiler's .tl file reader now accepts essentially any newline format: it will accept newlines consisting of CR-LF sequences, LF-CR sequences, simple CR newlines, and simple LF newlines. It doesn't matter what your computer's native newline conventions are - every port of the compiler will now accept any of these formats.
  • Due to a bug, the compiler's macro preprocessor didn't properly expand macros that included the #ifempty or #ifnempty operators. In particular, any text in the macro's definition prior to the #ifempty or #ifnempty was effectively discarded; the text wasn't included in the expansion. This is now fixed.

Changes for 3.0.8 (9/12/2004)

  • The compiler now enforces unique naming for all of the modules in a project. Using the same name for two (or more) modules can cause problems, mostly because both modules would have the same filename for their object files and thus one would overwrite the other. In the past, when a module name conflict caused this kind of problem, the compiler reported the errors caused by the name collision, but it wasn't always easy to figure out that the name collision was the root cause. To avoid this potential source of confusion, the compiler now checks the entire project for module name collisions, and flags an error if any collisions are found.
  • The "file safety" levels have changed slightly to provide better security against malicious game programs. First, level 2, which formerly provided global read access but no write access, now instead provides read/write access to the current working directory only (the game isn't allowed any access to files outside of the working directory). Second, the default safety level is now level 2. In the past, level 1 (read from any file/write to current directory only) was the default, which left open the possibility that a game could access data outside of the working directory. A malicious game could conceivably have exploited this by, for example, secretly copying sensitive data to the game directory for later use by a coordinating network worm, or by encoding the data in a hyperlink (displayed in an HTML-enabled game) that takes the player to a spyware site when clicked. The new default setting should effectively eliminate these risks by barring access to any files outside of the working directory.
  • Several of the system headers formerly lacked #include directives for other headers they depended upon. This didn't cause problems most of the time, since the necessary headers were usually included from somewhere else anyway, but it meant that you had to include system headers in the right order to avoid compiler errors. Each of the system headers now explicitly includes any other headers it depends upon, which ensures that each system header can be included in isolation, and in any order with respect to other headers.
  • The saveGame(), restoreGame(), and restartGame() intrinsic functions can now be called from recursive VM invocations (that is, from contexts where a call to an intrinsic function or method has invoked a byte-code callback function, such as an anonymous function passed to an iteration method on a collection object). This restriction was needed in the distant past, but it's been vestigial since version 3.0.5a (when the save/restore mechanism was overhauled to exlude the call stack and execution context from the saved state information). Since the restriction is no longer necessary, it's now been removed.
  • In the intrinsic function getLocalCharSet(), the new constant CharsetFileCont can be used to retrieve the name of the character set typically used for the contents of text files on the local system. (Note that this is only the typical character set for text files on the local system; there's no guarantee that any particular file uses this character set. The actual character set of a given text file is determined by the user and application that created the file.)
  • In tadsio.h, the constant CharsetFile (for use with the getLocalCharSet() function) has been renamed to CharsetFileName. The name change is to emphasize that the constnat refers to the character set used in the local file system for names of files, as opposed to the contents of text files.
  • The compiler sometimes incorrectly complained about a missing option argument when a "-x" option (with a valid argument supplied) was used at the very end of a command line. This has been corrected.
  • A bug in the compiler occasionally caused floating point constants that included exponential ("E" suffixes) to be mis-parsed. When this happened, one or more spurious, random extra digits of precision were added to the end of the number's mantissa. This has been corrected.

Changes for 3.0.7 (6/12/2004)

There are no changes to the compiler or interpreter core in this release. The only changes in the adv3 library and the Windows HTML TADS interpreter.

Changes for 3.0.6q (5/9/2004)

There are no changes to the compiler or interpreter core in this release. The only changes are in the adv3 library and the Windows HTML TADS interpreter.

Changes for 3.0.6p (4/25/2004)

  • The regular expression parser now accepts the special character codes <lbrace> and <rbrace> for left and right "curly" braces, "{" and "}", respectively. Since curly braces are special characters in regular expressions, it's convenient to have named character codes for them.
  • In the past, the regular expression parser accepted a closure modifier (a "*", for example) directly following an assertion expression. Since assertions don't consume any input text, repeating one with a closure doesn't change the meaning of an expression, so a closure following an assertion is meaningless. However, doing so had the undesirable effect of creating an infinite loop, as the regular expression matcher tried pointlessly to find out how many times a successful assertion could be successful (the answer is always: as many times as you try). The regular expression parser now avoids such infinite loops by simply ignoring any closure modifier that's applied to an assertion. (Note, however, that you can still trick the parser into building an infinite loop with a more complex expression. For example, you can enclose one or more assertions in parentheses, and then apply a closure to the parenthesized group. The regexp parser isn't sophisticated enough to detect these more subtle infinite loops; it can only detect the obvious kind where a closure is applied directly to an assertion.)
  • A bug in the compiler caused incorrect results when an anonymous function referred to a property of "self," when the function was defined within a list value of a property. The property evaluation within the anonymous function typically yielded nil in these cases, even when that property had a non-nil value in the object containing the property with the list value containing the anonymous function. The same problem showed up whether the property containing the list value was defined with the ordinary "prop = val" syntax or with a template. This has been corrected; properties of self will now yield correct results in these cases. To be more concrete, in the example below, evaluating testObj.prop1[1]() should display "hello!" on the console, and now does this correctly; but in the past, this example displayed nothing, because the evaluation of prop2 within the anonymous function yielded nil.

      testObj: object 
        prop1 = [ {: say(prop2) } ] 
        prop2 = 'hello!'  
      ;
    
  • A bug in the compiler caused the compiler to crash in some cases when presented with an anonymous function within a list value of an object property defined with a template. The problem has been corrected.
  • The compiler incorrectly reported a standard-level warning when a function or method "list parameter" was unused within the function or method. (A list parameter is a formal parameter defined in square brackets to indicate that it receives a list containing the actual parameters, which can vary in number, when the method is called.) The compiler also incorrectly reported the warning as an assigned but unused value; it should have reported it as an unused parameter instead. The compiler now reports the correct warning, and only reports it at "pedantic" level; this makes the handling of list parameters consistent with the treatment of ordinary formal parameters.

Changes for 3.0.6o (3/14/2004)

  • The MIME type for saved position files has been changed to "application/x-t3vm-state". The Windows installers have been updated to reflect the new MIME type.

Changes for 3.0.6n (3/6/2004)

  • The debugger now automatically includes "self" in the local variable list whenever examining a frame in which "self" is valid.
  • On Windows, the installers now add the MIME type for the compiled game file type (.t3) and the saved position file type (.t3v) to the system registry. This information can be helpful to applications, particularly those that perform file transfers. (The MIME type for a compiled game is "application/x-t3vm-image", and the type for saved position files is "application/x-t3vm-save".)
  • A compiler bug caused the compiler to crash when certain obscure kinds of syntax errors occurred inside nested object definitions. This has been corrected.
  • A bug in the compiler caused the compiler to crash when it was attempting to parse a one-line function or method that contained an unclosed anonymous function. The problem was most likely to be seen when the errant one-liner was the last thing in a source file. The compiler now generates appropriate error messages and terminates normally.
  • A bug in the ByteArray class caused the VM to crash when a ByteArray was used as a key in a LookupTable. This has been corrected.
  • A bug in the BigNumber class caused the VM to crash in some cases when a string was used as the source value to construct a new BigNumber. This should now work reliably.
  • A bug in the debugger caused a crash when execution stopped inside an anonymous function with no 'self' context. The most common place to encounter this problem was at a run-time error in an anonymous function defined inside a list, such as in an EventList object. The problem has been corrected.
  • On Windows, the Author's Kit installer failed to create the samples\obj subdirectory. That subdirectory is required to build the sample game (sample.t3m) included in the kit, so you had to manually create the directory before you could compile the game. The installer now automatically creates the directory.
  • The BigNumber class incorrectly returned a result of zero when dividing zero by zero. (This only happened when the dividend was zero; for other dividend values, a divide-by-zero exception was correctly thrown.) This now throws a divide-by-zero exception.

Changes for 3.0.6m (11/15/2003)

Note: 3.0.6l was a library-only release, so there was no system release called 3.0.6l.

  • The propNotDefined mechanism is now invoked when 'inherited' fails to find a base class implementation of the method being inherited. This works almost exactly the same way it does when propNotDefined is invoked during a normal method/property call, with only one difference: when 'inherited' fails to find a base class property, it searches for a 'propNotDefined' using the same search pattern it used to seek the original inherited property. For example, consider this code:

      export propNotDefined;
    
      class A: object
        propNotDefined(prop, [args]) { "This is A.propNotDefined\n"; }
      ;
    
      class B: A
        propNotDefined(prop, [args]) { "This is B.propNotDefined\n"; }
        p1() { inherited(); }
      ;
    
      main(args)
      {
        local x = new B();
        x.p1();
      }
    

    When this program is run, the result displayed will be "This is A.propNotDefined". B.p1() attempts to inherit the base class definition of p1(); when it doesn't find an inherited definition, it searches for propNotDefined() using the same search pattern it used to find the inherited p1(). That is, the search starts with B's superclass, because that's where the search for the inherited p1() started from.

  • The t3SetSay() intrinsic function now returns the previous default output function or method. (The return value corresponds to the argument value: if the argument sets a new function pointer, the return value is the old function pointer; if the argument is a new property ID, the return value is the old property ID.) This allows code to save the old value for later restoration, if desired. In addition, the new special argument values T3SetSayNoFunc and T3SetSayNoMethod can be specified to cancel any existing default output function or method, respectively, and the function returns these values when needed to indicate the absence of a previous setting.
  • The t3SetSay() function now accepts an anonymous function as the default output function. (In the past, only a regular named function was acceptable.)
  • When propNotDefined() is invoked, the interpreter now sets 'targetprop' to &propNotDefined. In the past, the interpreter incorrectly set 'targetprop' to the property originally invoked. This was incorrect; one important consequence was that it didn't allow propNotDefined() itself to use 'inherited' to invoke the base class's implementation.
  • The compiler now automatically adds a property called 'sourceTextOrder' to each non-class object defined in a source file. The value of the property is an integer giving the object's order in the source file in which it's defined. This value is defined to increase monotonically throughout a source file, so you can use it to sort a collection of objects into the same order in which they appear in the source text. This property is useful when you want to create an initialized structure of objects (such as a list or a tree), and you want objects within the structure to have the same order at run-time that they have in the source text. In the past, there was no straightforward way to guarantee source text ordering, because the compiler and linker assign object ID's in arbitrary order. This new property is guaranteed to reflect the original source text order, so you can use it to initialize the run-time order. Note that a 'modify' statement doesn't assign a new sourceTextOrder to the underlying object; the original sourceTextOrder is retained for a modified object. Note also that sourceTextOrder only tells you the relative order of objects within a single source file; the compiler resets the counter at the start of each new source file, so it's not meaningful to compare sourceTextOrder values for objects in different source modules.
  • The toInteger() function now accepts an integer value as its first argument. If the first argument is an integer, the function simply returns the same value; the optional second argument (the radix value) is ignored if present.
  • The BigNumber constructor now accepts another BigNumber as the source value. This creates a new BigNumber with the given source value at an optional new precision, rounding the original value if the new precision is less than that of the source value.
  • The BigNumber method formatString() incorrectly returned an empty string when formatting a fractional number if the maximum digit count for formatting was insufficient to reach the first non-zero digit of the fractional value. For example, 0.0001.formatString(3) returned an empty string. The method now returns '0' for these cases.
  • In most of the character-mode interpreters, if a line of highlighted or colored text appeared just before a "MORE" prompt, subsequent text was incorrectly highlighted or colored like the line before the "MORE" prompt. This has been fixed.
  • The character-mode interpreters very occasionally crashed on exit, due to a bug in the termination code. This has been corrected.
  • In most of the character-mode interpreters that support the "-plain" mode, the interpreter is now more thorough about flushing the standard output before stopping to read input. This makes the plain mode interoperate better with other programs using "pipes" and similar OS-specific input/output redirection. This change pertains to the MS-DOS and Unix versions, and to any other platforms based on the common character-mode layer (known technically as "osgen3").
  • The rand() function now accepts a Vector as its argument. A vector argument behaves the same as a list argument: the function randomly chooses one of the elements of the vector and returns it.
  • A bug in the TadsObject.createInstanceOf() method caused an incorrect value to be left on the VM stack during creation of the new object. This showed up as an incorrect argument value if a method was invoked on the new object as part of the createInstanceOf() expression. This has been corrected.
  • The text-only interpreters incorrectly paid attention to a <BODY> tag within an <ABOUTBOX> tag, setting any color scheme specified in the <BODY> tag. This improperly set the main text window to the color scheme intended only for use inside the about-box. This no longer occurs.
  • The forEachAssoc() method of the List and Vector intrinsic classes incorrectly passed a zero-based index for the 'key' parameter to the callback (that is, the first element was identified by index 0, the second by index 1, etc., C-style). This should have been a one-based index for consistency with all of the other indexing operations for List and Vector. This is now fixed.
  • Vector.setLength() will now throw an error if the given length is less than zero. (In the past, it accepted an invalid length, leading to unpredictable results.)
  • Under certain conditions, the debugger didn't stop in response to a "Break" key (Ctrl-C, Ctrl+Break, or whatever key is appropriate to local convention). In particular, if a single line of code contained an infinite loop, and the user ran the program, broke into the loop once, and then resumed execution, the debugger ignored subsequent break keys. This has been corrected.
  • The compiler incorrectly reported errors in the branches of an 'if' statement at the line of the 'if' itself if the branches were bare statements (not enclosed in braces). The same applied to 'while', 'for', and 'do while' statements when their bodies were bare statements. The compiler now reports these errors at the sub-statement locations.
  • The compiler generated an incorrect debugger line number record under certain obscure circumstances: if the source file contained an object or class based on multiple superclasses, and the class didn't define an explicit constructor, then the compiler generated an incorrect line record for the last executable line of code in the file. This is now fixed.
  • The compiler now shows Unicode escape sequences when displaying an error message containing characters from the source file that can't be represented in the compiler console character set. For example, if you're running the compiler in a terminal window on Unix, and your terminal window is set up to display Latin-1 (ISO-8859-1) characters, and you're compiling a source file prepared in Latin-2, your terminal window would be unable to correctly display some of the characters in the source file, because Latin-2 has characters that aren't part of Latin-1. In the past, if any of these "unmappable" characters were shown as part of a compiler error message, the compiler showed them as question marks. Now, the compiler shows them as "backslash" escape sequences. For exampe, Latin-2 character 171, "T with caron," would be displayed as "\u0164", since its Unicode code is 0x164. This change doesn't affect the display of compatible characters - all mappable characters will still be displayed in the local character set. This only affects characters that were formerly displayed as question marks.
  • The default "file safety level" is now 1, which allows reading from any file and writing only to the current working directory. This default setting provides games with reasonable flexibility, but helps prevent the possibility that a malicious author can modify important files on your hard disk by limiting writing operations to the game's working directory only. You can override this using the "-s" option if you want to set a more permissive or more restrictive safety level.
  • In Workbench for Windows, the "Build for Release" command didn't properly invoke the "t3res" command for building external resource files (.3r0, etc). This now works properly.
  • A problem in the debugger caused sporadic bad behavior when a finalizer method threw an exception (out of the finalize() method; throwing around exceptions within a finalizer was fine, as long as the exception was caught entirely within the finalizer). This manifested itself in numerous different ways, ranging from no visible effects to full Workbench crashes. The problem only occurred in the debugger; the regular non-debug VM didn't have the problem. This has now been corrected.
  • The VM now recovers more gracefully from stack overflow exceptions. In the past, stack overflows tended to be unrecoverable because the VM would usually encounter a second stack overflow in the course of trying to create an exception object to represent the original stack overflow, forcing the VM to terminate the game summarily to avoid an infinite loop of exceptions thrown while handling exceptions thrown while handling exceptions, ad infinitum. The VM now keeps a small emergency reserve of stack space for handling stack overflow errors, which should in most cases allow an exception object to be created normally, which in turn allows the game to catch and handle the exception just like any other run-time error.
  • The debugger now gives you more flexibility in recovering from stack overflow errors. In the past, if your program caused a stack overflow (due to infinite recursion, for example), the debugger itself was unable to evaluate expressions without itself encountering stack overflows, since the debugger shares the stack with the VM. The debugger now holds some of the stack in reserve specifically for handling stack overflows; when a stack overflow occurs, the debugger uses the reserve for its own purposes, allowing you to evaluate expressions and carry on normally within the debugger. This makes it easier to figure out why the stack overflow occurred. Note that you can often use the debugger to recover from a stack overflow by manually moving the execution pointer to a 'return' statement (or the end of the method), breaking off the infinite recursion that caused the overflow.
  • The debugger now shows anonymous functions more descriptively in stack traces. In the past, these showed a string like "[1234].prop#0(5,6)"; now, the debugger shows "{anonfn:1234}(5,6)" instead. The "1234" is an internal object identifier for the anonymous function; this conveys no specific human-readable meaning other than to distinguish one function from another. The corresponding change has also been made in reflect.t, in reflectionServices.formatStackFrame().

Changes for 3.0.6k (8/17/2003)

  • The debugger now supports stop-on-change conditional breakpoints. When you create a breakpoint with a condition expression, the debugger now offers a choice between stopping when the expression evaluates to true or stopping when the expression's value changes. In the past, there was no choice, and only stop-when-true conditions were supported. Stop-on-change is now the default when creating a new global breakpoint.
  • The system.tl library now includes the default system startup module, _main.t. In addition, it sets the new library flag, "nodef", to indicate that the library replaces the default system modules that the compiler would otherwise include in the build automatically. This change primarily benefits users of Workbench for Windows, in that it causes Workbench to include _main.t explicitly in the list of source files. This makes it more convenient to view the file within Workbench, set breakpoints inside it, and trace into it. (This change will have no effect on most existing games. The only games affected would be those that include system.tl and include a non-standard startup module, using the "-nodef" compiler flag to suppress the default inclusion of _main.t. This is an unlikely scenario, but if it applies, here's what to do: drop the "-nodef" compiler flag from your project file, and instead add "-x _main" immediately after the "-lib system" inclusion.)
  • The new directive "nodef" can appear within a library (.tl) file. This instructs the compiler that the library includes the same modules that the compiler would include by default, or replacements for the standard modules. If a library includes the "nodef" directive, then including that library in the build has the same effect as including the "-nodef" option on the compiler command line or in the project (.t3m) file.
  • In the past, the text-only interpreters didn't properly handle banner size settings that exceeded the available screen size. The text-only interpreters will now limit a banner's size to the available space on the screen when the requested size is too large.
  • The AnonFuncPtr intrinsic class now correctly identifies itself as a subclass of Vector (via ofKind and getSuperclassList).
  • The AnonFuncPtr intrinsic class now compares and hashes by reference, rather than by value as its Vector base class does.
  • The banner function bannerSetSize(), when applied to a text window (BannerTypeText), now takes into account the space used for margins and borders when sizing in "absolute" units. This ensures that if you ask for a height of five rows, for example, then the window will actually be large enough to display five rows in the default font, without any scrolling. In the past, the margins weren't taken into account, so the actual capacity of a banner wasn't necessarily the same as the requested nominal size in absolute units.
  • The run-time symbol table now omits "intrinsic class modifier" objects. These objects have been filtered out of firstObj/nextObj iterations since 3.0.6c; they're now filtered out of the symbol table for the same reasons, and for consistency with firstObj/nextObj. (In the past, these objects appeared as symbols of type object, with names of the form " 99", and with no superclasses. These are internal implementation objects that are meaningless to the game program, so there's no reason for the symbol table to include them.)
  • A problem in the List and String classes caused the VM to crash in certain very rare cases when evaluating properties of constant list or string values. This has been corrected.
  • A bug in the Dictionary intrinsic class's findWord() method sometimes caused strange behavior, most noticeably as corruptions in the values of local variables in the invoking code. The problem only occurred when the method was called in its single-argument form. The problem has been fixed.

Changes for 3.0.6j (8/2/2003)

  • The new TadsObject method setSuperclassList() enables the game program to change an object's superclass list dynamically. The method takes a list giving the new superclass list for the object.
  • It's now legal to explicitly define a 'location' property in an object definition that also uses the '+' notation to set the object's location. The compiler specifically treats an explicit 'location' property setting as overriding the value set with the '+' notation, rather than considering the redefinition a conflict as it did in the past. This allows logically related object definitions to be kept close together in the source code even when some of the objects are part of a location tree and others are not; this happens fairly frequently, because simulation objects sometimes have abstract helper objects that don't participate in the normal location hierarchy.
  • Templates can now include alternative groups that are optional. If any element of an alternative group is marked as optional, then the whole group of alternatives is optional.
  • The new macro perInstance(expr), defined in the system header file tads.h, can be used to define a property in a class such that the property will be set to the value of the expression 'expr' separately for each instance of the class. For each instance, the expression is evaluated the first time that instance's value of the property is accessed, and then stored with that instance.
  • The compiler now has the ability to bundle resource files into the image (.t3) file as part of the main compilation ("t3make") step, without running the "t3res" utility as a separate step. This lets you include resource files in your project's makefile (the .t3m file), which means the makefile can now specify the entire build procedure for most types of projects. To add resources with t3make, add the new "-res" option after all of your source and library modules in the .t3m file, and list your resource files after the "-res" option. The syntax for the resource list following the "-res" option is the same as the "t3res" utility, except that the "-add" option is not allowed (as adding resources is the only possibility).
  • Workbench for Windows now allows you to add a resource file to a project before the resource file exists. To add a resource file that you haven't yet created, use the usual menu command to add a resource file, then type the name of the file into the file selector dialog. The dialog will allow you to type the name of a file that doesn't yet exist. This is handy when you want to include a resource file that won't be created until your pre-initialization code runs, such as the "gameinfo.txt" file.
  • The compiler is now able to determine automatically if relinking is needed due to a change in the set of modules included in the build. This is accomplished by checking a small bit of additional data in the .t3 about the set of object modules involved in the build. (The new information is simply a 32-bit "checksum" of the module source file list, so it adds only four bytes to the image file. It's not enough information to allow a user to recover a list of your source files by reverse-engineering the image file, so you don't have to worry about any possibility that a user could obtain "hints" this way. Because only a checksum is stored, there's a very small possibility that a change to the source file list could result in an identical checksum, in which case the compiler would not notice that relinking is required. The probability of this happening is extremely small, but if you ever suspect it, you can always use the "-al" option to force a relink.) Note that this doesn't affect the compiler's use of file timestamps to determine build dependencies; this is simply an additional check the compiler makes to detect when a new module is added to the build or an old module is removed.
  • The ByteArray intrinsic class has two new methods, readInt() and writeInt(), that make it easy to translate between integer values and byte representations of those values. This is especially useful for reading and writing binary files. The new methods let you read and write integers in 8-bit, 16-bit, and 32-bit sizes, signed or unsigned, and in little-endian or big-endian format.
  • The File intrinsic class has a new method, getFileSize(), which returns the size in bytes of the file.
  • A bug in the File intrinsic class sometimes caused the VM to crash when a read operation was attempted after a seek operation. This has been corrected.
  • The starter game templates now include startup handling for startup-and-restore. When the player launches the interpreter using a saved-game file, the interpreter invokes the startup-and-restore code, which the game must provide in a routine called mainRestore(). The starter templates now include a mainRestore() routine, and handle main() and mainRestore() using a common subroutine called mainCommon(). This change will make it much simpler for authors to include proper startup-and-restore support in their games, by providing a ready-made framework with the necessary code.
  • Fixed a bug in Workbench for Windows: checkboxes in the Project window didn't work for nested libraries; clicking on the checkbox for a nested library simply had no effect. These checkboxes now work properly; clicking on a library checkbox checks or unchecks the library and all of the files within the library.
  • The Win32 character-mode interpreter (t3run.exe) now includes a desktop icon in the executable file. This allows you to use the "-icon" option with maketrx32 to add your own custom icons to games you bundle as executables using the console-mode interpreter.
  • A bug in the regular expression matcher failed to handle the shortest-match closure modifier (specified with a '?' suffix to a '*' or '?' operator) properly. This bug was introduced in the regular expression processor rework in 3.0.6h, and has now been corrected.
  • In certain unusual cases, the debugger incorrectly included a property in property enumerations when the property was actually a method. This happened when a method overrode a property defined as a simple data value in a superclass. The debugger isn't meant to show methods in property enumerations, since invoking a method will naturally invoke any side effects of the method. This has been corrected.
  • In Workbench for Windows, projects created in the root directory of a disk (C:\, D:\, etc) caused a number of problems, including crashes during compilation. These problems have been corrected, so creating a project in a root directory is now safe.
  • In Workbench for Windows, the compiler incorrectly wrote the object files to the working object directory when compiling for release, overwriting the object files for the debug build. This didn't do any harm apart from requiring a full recompile for debugging after any release build, which the compiler automatically sensed and performed, but it caused an unnecessary delay. The compiler now correctly sends release build object files to a temporary directory, so that the debug build object files are retained through a release build.
  • The new-project wizard in Workbench for Windows will no longer accept a source filename that ends in an extension reserved for makefiles or compiler-generated files (.t3m, .t3, .t3o, .t3s). This makes it more difficult to accidentally create a source file that will be overwritten by the compiler or by Workbench with generated files.
  • The compiler will now recompile an object file if the symbol file is more recent. Compilations that fail due to a syntax error can leave a project partially compiled, so that all of the symbol files but not all of the object files have been recompiled. In the past, correcting such an error and then recompiling did not always compile all of the necessary modules. This change ensures that all modules noted as requiring compilation on the first attempt will be noted again on subsequent attempts, even if the first attempt fails part-way through.
  • Due to a bug in the VM, throwing exceptions out of a native-code callback context (such as out of the callback to a Vector or LookupTable iteration method) sometimes caused the VM to lose track of the calling exception handlers; this caused symptoms such as sending the exception to a "catch" block other than the innermost eligible one. This problem tended to show up when an exception was thrown out of a callback context, and then the same exception was thrown again on a later invocation. This has been corrected.
  • If a Vector contains a reference to itself, or to a Vector containing a reference to the first Vector, indirectly to any depth, then an attempt to compare the Vector to another Vector, or an attempt to use the Vector as a key in a LookupTable, will fail with a run-time exception ("maximum equality test/hash recursion depth exceeded"). Since Vector equality comparisons and hash calculations are defined recursively by value, Vectors with direct or indirect self-references cause the equality and hash calculations to recurse infinitely; the Vector class avoids this infinite recursion by throwing the exception when the recursion becomes too deep. Note that this also effectively limits the depth of a Vector tree when used in an equality test or hash value calculation, even if the tree is acyclic; the limit is 256 levels.
  • When reading input from a script (using the REPLAY command, for example), the interpreter now ignores morePrompt() calls. This allows script replay to proceed unattended, without unnecessary interactive pauses.
  • In the debugger, if the game was interrupted by a real-time event while the player was editing a command line, and the debugger took control (via a breakpoint, for example) inside the real-time event, and the game was then terminated from within the debugger (while still stepping through the real-time event code) and then later restarted, a bug in the console manager caused strange behavior, sometimes including crashing the debugger. This has been corrected.

Changes for 3.0.6i (6/15/2003)

  • Due to a compiler bug, when a template was defined using the "|" symbol to list a set of alternatives for a token position, object definitions using the template failed to match the last alternative of a set. All alternatives in a set are now properly recognized.
  • The gameinfo.t module has been removed. In its place is a new mechanism in the library that writes the GameInfo file automatically during pre-initialization, based on the definitions in the game's GameID object.
  • The Tokenizer class now gives each rule a name, which is simply a string value by which the rule can be identified. This changes the format of the rule list entries - each rule list entry has the new name value as its first element. Any subclasses or modifications of Tokenizer must use the new rule format.
  • The Tokenizer class has some new methods that allow rules to be added or removed. insertRule(rule, curName, after) inserts the new rule given by 'rule' before or after the existing rule named 'curName'; the new rule is inserted before the existing rule if 'after' is nil, after the existing rule if 'after' is true. insertRuleAt(rule, idx) inserts the new rule given by 'rule' at the index given by 'idx' in the rule list. deleteRule(name) deletes the existing rule with the given name, and deleteRuleAt(idx) deletes the existing rule at the given index in the rule list.

Changes for 3.0.6h (6/7/2003)

  • The "modify" keyword can now be used to define functions. Modifying a function works just like replacing a function with the "replace" keyword, except that the new function can invoke the previous version of the function by using the "replaced" keyword. Refer to the Procedural Code section of the documentation for details.
  • Two new TadsObject methods, createInstanceOf() and createTransientInstanceOf(), enable a program to dynamically instantiate an object based on multiple superclasses. Refer to the TadsObject reference for details of the new methods.
  • The object template syntax has been extended to include optional elements and alternative elements. An element is marked optional by placing a question mark after it; alternative elements are separated by '|' tokens.
  • Several string search and regular expression intrinsics accept a new optional argument specifying the starting index in the string for the search. If the index argument is specified, then the search will start from the given character index; the index value 1 indicates that the search starts from the first character of the string, which is the default if the index parameter is omitted. This change allows algorithms that search for repeated occurrences of a substring or pattern to be written more efficiently, since they won't have to split up the source string to continue searching for additional occurrences.
    • String.find(str, index?)
    • String.findReplace(origStr, newStr, flags, index?)
    • rexMatch(pat, str, index?)
    • rexSearch(pat, str, index?)
    • rexReplace(pat, str, replacement, flags, index?)
  • A new banner style flag, BannerStyleMoreMode, causes a banner window to use "MORE" mode. This means that any time text displayed to the banner fills the available vertical space in the banner window, the interpreter will display some sort of prompt to the user, and then wait for the user to acknowledge the prompt. The exact nature of the prompt and acknowledgment varies by platform; usually, the interpreter displays a prompt like "[MORE]" at the bottom of the overflowing window, and waits for the user to press a key. The purpose of MORE mode is to ensure that the user always has a chance to read all of the displayed text before it scrolls off the screen. Note that BannerStyleMoreMode implies BannerStyleAutoVScroll, since it only makes sense to pause for a MORE prompt when new text would cause old text to scroll away, and this only happens when the auto-vscroll style is used.
  • The forEachInstance() function now allows the callback to break out of the iteration loop by throwing a BreakLoopSignal exception. Throwing this exception simply terminates the loop and returns to the caller of forEachInstance(). You can conveniently throw the exception using the "breakLoop" macro (defined in tads.h).
  • A new utility function has been added to the base library (in _main.t): instanceWhich(cls, func). This function iterates over instances of the given class cls, and calling func on each instance; func is a pointer to a function taking one parameter, which is the current instance in the iteration. instanceWhich() returns the first instance for which the callback function func returns a non-nil and non-zero value. Note that the function iterates instances in arbitrary order, so if there are multiple instances of cls for which func returns true, the return value will simple be the first of these visited in the arbitrary iteration order. If func doesn't return a non-nil/non-zero value for any instance of cls, the function returns nil.
  • The setLogFile() function now takes an optional second parameter specifying the type of log file to open or close. The possible values are LogTypeTranscript, to create a transcript of all input and output; and LogTypeCommand, to create a record of only the input commands. The default, if the parameter is omitted, is LogTypeTranscript.
  • The character mapper now accepts the IANA standard names for the various Unicode encodings supported in TADS 3: "UTF-8", "UTF-16BE" (for the 16-bit big-endian encoding), and "UTF-16LE" (for the 16-bit little-endian encoding). These names are case-insensitive, and can be used anywhere a character encoding name is required (#charset, new Charset(), etc). The old non-standard names for the same character sets ("utf8", "unicodeb", and "unicodel") are still accepted as well.
  • The compiler now accepts source files that start with a Unicode UTF-16 byte-order marker (i.e., the "FF FE" or "FE FF" byte sequences) and also specify a #charset directive. The #charset directive in such cases must (1) itself be encoded in the 16-bit Unicode encoding implied by the byte-order marker, and (2) designate the character set name that matches the byte-order marker (so a file encoded in big-endian UTF-16 must have a #charset "utf-16be" directive, and a file encoded in little-endian UTF-16 must have a #charset "utf-16le" directive). The #charset directive is optional in these cases, since the compiler can correctly infer the encoding based on the byte-order marker alone.
  • The compiler now accepts source files that start with a Unicode UTF-8 byte-order marker. The compiler infers that the file is encoded in UTF-8 if it sees this sequence. Note that there is only one valid UTF-8 encoding, so the UTF-8 byte order marker is always the same (i.e., there is no "big-endian" or "little-endian" flavor of UTF-8; there's only UTF-8). A #charset "utf-8" directive is allowed but not required immediately after the UTF-8 three-byte byte-order marker sequence. This change is required because some text editors that save in UTF-8 format write the byte-order marker character at the start of the file.
  • A new compiler option, -quotefname, tells the compiler to quote filenames when reporting the source file location of an error. If this option is specified, the compiler encloses these filenames in double quotes, and "stutters" any double-quote characters that are part of the filenames themselves (that is, each double-quote character that's actually part of a filename is shown as two double-quote characters in a row). This change is intended to make it easier for automated tools to parse the compiler's error message output, by ensuring that filenames can be predictably parsed regardless of any special characters that might appear within.
  • The compiler now displays an initial status message indicating the number of files it will need to build. This is designed for use by automated tools, to give them an estimate of the amount of work to be done; this can be used, for example, to show a progress bar indicator as the compilation proceeds.
  • In the compiler options file (the .t3m file), certain options (-I, -o, -Os, -FL, -FI, -Fs, -Fy, -Fo, -source, -lib) can be specified using URL-style notation, to ensure portability of the .t3m file across different operating systems. In the past, the compiler didn't properly translate these options to local conventions when they contained '/' characters to separate path elements. The compiler now properly converts URL-style paths to local conventions.
  • When an object is defined with an undefined or invalid superclass name, and the object definition uses a template to define property values, the compiler now reports an "undefined superclass" error rather than an error with the template format. In cases where a superclass name is merely misspelled, the compiler will naturally fail to find a matching template for the object, since the misspelled superclass name probably doesn't have any templates associated with it; thus, the template error is secondary to the true problem, which is the misspelled superclass name. This change should make the diagnostics more useful by reporting the true problem rather than the missing-template side effect.
  • Text Grid windows no longer consolidate runs of whitespace; instead, all whitespace written to a text grid window is treated literally.
  • Text Grid windows incorrectly interpreted HTML markups in the text-only interpreters. This has been corrected; text displayed to text grids is now treated literally on all interpreters.
  • Workbench now automatically saves the project (.t3m) file settings upon starting execution of the game in the debugger, and also saves the recent game order whenever a new game is loaded. These measures help reduce the chance of data loss in the event that Workbench crashes while the game is running (which it ideally should never do at all, but until the glorious day when it's perfectly bug-free, this extra saving will at least make crashes a little less painful).
  • Due to a bug, Workbench on Windows did not start up correctly when it was started by double-clicking on a .t3m file from the Windows desktop. (Workbench started with a blank interpreter window, but didn't show the main Workbench IDE window at all.) This has been corrected.
  • A bug in the debugger caused the debugger to crash under certain obscure circumstances. In particular, the crash occurred if the game caused a stack overflow, and the overflow happened to occur on a recursive VM invocation from an intrinsic method (such as when an intrinsic class method invokes an anonymous function callback). This has been corrected.
  • In the past, the compiler generated an output function call for the intial empty portion of a double-quoted string of the form "<<expr>>..." (in other words, a string with an embedded expression immediately after the open quote). The compiler no longer generates this superfluous empty string output operation, slightly reducing code size and execution time.
  • The String intrinsic class failed to enforce the 64k length limit for an individual string in some cases; exceeding the limit caused problems ranging from corrupted string text to crashes. This has been corrected; the interpreter now throws a run-time exception ("string is too long") if creating a string (via concatenation, for example) would exceed this limit. The List intrinsic class is likewise more assertive now about enforcing its 64k limit as well.
  • The regular expression functions (rexMatch, rexSearch, rexReplace) were capable of causing the interpreter to crash (with a system stack overflow) when asked to match a pattern to an extremely long string. The exact limits varied by platform according to the amount of system stack space available; on Windows, the problem started occurring with strings about 5,000 characters long, although the exact length was also a function of the complexity of the regular expression pattern. This has been corrected; the regular expression matcher no longer uses a recursive algorithm, so its consumption of system stack space is now fixed and small. The matcher still consumes memory that scales according to the length of the input string and the complexity of the regular expression, since such memory consumption is inherent in the task; but the memory consumed is no longer in the system stack, which means among other things that the matcher can now recover gracefully (by throwing an "out of memory" run-time exception) if it should exhaust available memory while working.
  • In the DOS/Windows version, the executable file bundler (maketrx32) didn't look for the character map library file (cmaplib.t3r) in the correct location; it looked in the directory containing the original interpreter executable rather than the "charmap" subdirectory of the interpreter directory; since the installer puts cmaplib.t3r in the subdirectory, the bundler failed to find the file and omitted it from the bundled executable. The program now looks in the "charmap" subdirectory.
  • A bug caused the compiler to crash on certain types of syntax errors. If a long string (around 1,000 bytes or longer) was misplaced in a few specific contexts, the error message generator overflowed an internal buffer trying to include the text of the string in the error message. This should no longer occur.
  • The compiler now flags an error if a grammar production defines more than 65,535 alternatives. This limit is imposed by the .t3 file format for grammar rules, but it's undesirable to get anywhere close to the limit because of performance cost. It's possible to exceed the limit with relatively concise grammar rule definitions, because the compiler expands the permutations of rules defined with '|' alternatives. Grammar rules that contain complex sets of permutations using '|' alternatives should generally be rewritten using sub-productions, which will allow equally complex rules without the overhead of expanding the permutations.
  • In reflect.t, reflectionServices.valToSymbol() can now correctly identify the reflectionServices object itself. (Due to a bug, this caused a run-time error in the past.)
  • In reflect.t, reflectionServices.valToSymbol(), looking up an unnamed object now returns a string giving the superclass list of the object, if the superclass names are available. The format of the return value is now '(obj:sc1,sc2)', where 'sc1' and 'sc2' are the names of the superclasses (naturally, more or fewer superclass names will appear according to the actual number of superclasses of the object). The string 'obj' is literally present. If none of the superclasses have names, then the traditional result, '(obj)', is returned. If some superclasses have names but others don't, then the full list will be returned, with '(anonymous)' used for any unnamed superclass.

Changes for 3.0.6g (4/12/2003)

  • The new interpreter option -R lets you specify the root folder for individual resources. By default, the root resource folder is the folder that contains the image (.t3) file. If -R is specified, then the interpreter will look for resources in the given folder when they can't be found in the image file itself or in a resource bundle file. Note that this isn't a search list; only one -R option can be in effect.
  • Added a new method, isRoundTripMappable(), to the CharacterSet intrinsic class. This new method provides an easy way to determine if a set of characters has an exact, one-to-one mapping in a given local character set.
  • The compiler now warns when a grammar rule ends with an empty alternative, or is completely empty. Empty rules at the top level of a grammar rule are uncommon, and it's easy to accidentally leave a '|' at the end of a rule, especially while actively defining new rules. To eliminate this new warning, you can use an empty pair of parentheses, "()", to explicitly indicate an empty rule. This warning only applies to the outermost grouping level of a rule; empty rules within parenthesized portions of rules are not flagged.
  • Fixed a problem in the console formatter that caused command lines read from a command input file to be copied into a script output file twice.
  • Fixed a problem in the Dictionary class that caused dynamic vocabulary to be forgotten if you saved a game to a file, ended the interpreter session, started a new interpreter session, restored the file saved earlier, saved again to a second file, and then later restored the second file. (The problem was that the Dictionary object was not being marked as being in need of being saved again after it was restored in the new session, so the second save didn't include the dynamic vocabulary entries. The Dictionary object is now properly marked as being in need of saving after being restored.)
  • Fixed a problem that caused the Object.propDefined() method to yield incorrect results when called on an intrinsic class object to retrieve information on methods added to the intrinsic class with 'modify'. The information returned was inconsistent with that returned from getPropList(). This has been corrected; the returned information should now be fully consistent.
  • Fixed a problem that caused sporadic crashes in the Windows debugger when an expression including the '&' operator applied to an undefined symbol was evaluated interactively. This crashiness should no longer occur.
  • Fixed two problems in the text-only interpreters relating to "&" entities. First, a few valid entity names were not recognized at all, causing the interpreter to show the entity source text (i.e., the "&" code itself). Second, entities whose names weren't properly terminated (with a semicolon, ";") had the last letter of their name incorrectly added to the display after the entity itself. These problems have been corrected.
  • Fixed a couple of bugs that caused interpreter crashes when running with "unicodeb" or "unicodel" character set mappings.
  • The versions of the interpreters that run in a Windows command prompt window now correctly remove the console window's scrollbars in all cases on Windows NT, 2000, and XP. In the past, the interpreters usually did this, but in some cases missed the scrollbars and left them intact. (Console scrollbars appear when the console properties are set so that the console has a "screen buffer" larger than the actual console window. This feature isn't available on the Windows 9x platforms, only on the NT branch, which includes 2000 and XP.) Leaving the scrollbars active created the confusing situation that the area of the console containing the interpreter window could be scrolled off the screen. The scrollbars are now correctly removed under all conditions. This affects t3run.exe and tr32.exe.

Changes for 3.0.6f (3/23/2003)

  • Fixed a problem in the regular expression parser that misinterpreted character class expressions involving single Unicode characters outside of the plain ASCII range (for example: '<\u2019|\u201D>'). The regular expression compiler treated such expressions as invalid; it now handles them correctly.
  • Added a couple of new compiler options to allow more control over special system paths:
    • -FI path: overrides the standard system include directory with the given path. The compiler normally uses a path that depends on the operating system and installation configuration. Note that this option doesn't set up a search list the way -I does; there's only a single system include directory in effect at any given time.
    • i>-FL path: overrides the standard system library source directory with the given path. The standard library path depends on the operating system and installation configuration. As with -FI, there's only a single system library directory in effect at one time.

Changes for 3.0.6e (3/16/2003)

  • A new HTML tag is now recognized: <NOLOG>. This is a container tag (so each <NOLOG> must have a corresponding </NOLOG> tag); it delimits text that is to be displayed only on the interactive console, not in any log file (transcript) being made. The tag has no effect on the display.
  • The getPropList() method previously only returned static methods when called on intrinsic class objects (such as TadsObject or BigNumber). This has been changed so that the method now returns both static and non-static methods. This change clarifies the meaning of getPropList() when called on an intrinsic class object: the method returns the full set of properties that the class defines for instances of the intrinsic class, which may not necessarily be methods that are meaningful when called on the intrinsic class object itself. This change is necessary for consistency throughout the class hierarchy for the reflection mechanism, so that a program can traverse a class tree to create an exhaustive list of properties on a class, including those inherited from intrinsic superclasses.
  • In the output formatter, the effects of the "\^" and "\v" sequences are now limited to the plain text outside of HTML markups. In the past, if an HTML tag or entity appeared after a "\^" or "\v" sequence and before any alphabetic character, the case conversion effect was applied to the first character of the tag or entity name. Entity names are case-sensitive, so this had the potential to invalidate an entity name; in addition, whether a tag or entity name interceded, the case conversion effect was effectively lost, since it was misapplied to the HTML markup. Now, with this output formatter change, the case conversion effect of "\^" and "\v" sequences skips past any HTML markups, so the case conversion is applied to the next plain text.
  • Fixed a problem in the console output formatter that caused explicit blank line sequences to be ignored in some cases immediately after reading input.
  • Fixed a compiler problem that caused spurious syntax errors in any object definition that used a template in which a list element followed a single-quoted string element.
  • Fixed a compiler bug that caused spurious syntax error reports in certain cases involving multi-line strings as macro arguments. If code involving a macro spanned multiple lines, and the macro arguments included a string spanning multiple lines, and the string was part of a larger expression that started with a non-string token, the compiler sometimes misinterpreted the string as ending before its actual terminating quote. This resulted in spurious syntax errors, as the compiler thought some of the string's continuation lines were non-string tokens. This has been corrected.
  • Fixed a bug in the console output formatter that allowed too many lines to be displayed between "MORE" prompts in some cases, which caused text to scroll off the top of the screen before the player could read it. (The problem was introduced with the typographical spaces in 3.0.6b.)

Changes for 3.0.6d (2/23/2003)

  • Fixed a problem that caused the debugger to crash when attempting to stop a running game when the game was suspended in the debugger at a run-time exception that was thrown from within a callback invoked from a system intrinsic method (for example, from a callback invoked by the List.forEach() method). This has been corrected.
  • The inputKey() intrinsic function didn't return the correct square-bracket code for special keys (arrow keys, function keys, and so on) due to a bug introduced in an earlier patch release (3.0.6a). This is now fixed.
  • Fixed a problem in several methods of the Dictionary intrinsic class (addWord, removeWord, forEachWord) that sometimes caused stack overflows when calling these methods. Fixed the same problem in some File methods (setCharacterSet, closeFile, setPos, setPosEnd).

Changes for 3.0.6c (2/1/2003)

  • Defining a template for an object class no longer creates an external reference to the object, so it's now legal to define templates for objects that are never defined in any of the modules linked into a program. In the past, merely defining a template for an object created a reference to the object. This change makes it easier to create more modular subsystems whose components can be linked a la carte, since templates included in a header for the subsystem won't by themselves drag in the modules where the template objects are defined. An object mentioned in a template will now be required at link time only if the template is actually used to create instances of the mentioned object.
  • The 'modify grammar' statement now accepts an empty grammar rule list to indicate that the rules defined in the base grammar definition are to be retained. This change makes it possible to modify the properties and/or methods of a grammar rule in isolation, without changing the syntax for the rule. In the past, to modify only properties or methods of a grammar rule, it was necessary to repeat the entire rule list from the base grammar definition, since the rule list in the 'modify grammar' statement always superseded the base object's rule list.
  • The firstObj/nextObj object iteration functions now filter out "intrinsic class modifier" objects. These are purely internal implementation objects in the VM; they serve as the repositories of properties and methods attached via the "modify" keyword to intrinsic classes, and are for all intents and purposes parts of the intrinsic classes they modify. There is no benefit in enumerating them as separate objects, and doing so created some confusing special cases for code using the reflection mechanisms, so they've been eliminated from the firstObj/nextObj results.
  • The "intrinsic class" declaration syntax now allows the superclass of the intrinsic class to be specified. This information is largely for documentary purposes; intrinsic classes are built into the VM, so the "intrinsic class" syntax doesn't actually define intrinsic classes but merely tells the compiler about the interfaces of the intrinsic classes being used. The system header files have been changed to take advantage of this new feature to provide superclass information for the intrinsic classes they declare. The one immediate effect of this change is that the compiler is now able to enforce the rule against using "modify" with intrinsic methods of intrinsic classes, and can do so for the entire inheritance tree; in the past, the compiler was unable to enforce this rule for inherited intrinsic methods, since it didn't have information on the inheritance relationships among intrinsic classes.
  • Workbench for Windows had a problem that, under certain circumstances, caused the same directory to be added to the "include" path in the build settings every time a project was opened. This happened when a file in the "include files" section of the project had a parent-relative path (a path starting with ".."). This should no longer occur.
  • Workbench for Windows had a problem that failed to recognize the library-relative path of a header file associated with a nested library (that is, a library referenced from within another library; for example, en_us.tl is nested within adv3.tl in the standard library configuration). This sometimes caused such header files to be listed in the "include files" section of the project window with absolute or parent-relative paths, when they should have been listed without any path prefix, since Workbench automatically looks for header files in the directories associated with libraries included in the build. The unnecessary path prefixes caused other undesirable side effects, such as including superfluous include paths in the build settings. Workbench should now correctly identify headers associated with nested libraries.
  • Workbench for Windows did not properly initialize the "recent projects" menu when configured to load the most recent game at startup. On the initial load at startup, the list of recent projects was always shown with full, absolute directory paths, rather than with paths relative to the initially loaded project's directory. (This was purely cosmetic, but was inconsistent, since the recent projects were always shown with paths relative to the active project after opening another project subsequently.) This has been corrected.
  • A problem in the command-line option parser prevented some combined TADS 2/3 interpreters from accepting the "-cs" (character set) option for TADS 3 games. This is now fixed.

Changes for 3.0.6b (12/15/2002)

  • This version substantially overhauls the output formatter's word-wrapping rules. The changes are designed to give the game much greater control over the way that text lines are wrapped to fit the available horizontal space. The changes should be relatively transparent to existing code, because the new features mostly add optional new controls, but use defaults that provide roughly the old TADS 2 behavior. The changes are implemented in both the text-only and HTML interpreters. The new text-wrapping rules are described in detail in the Formatting documentation, but here's a summary of the changes:
    • The game can switch at any between "word-wrap" and "character-wrap" modes. In word-wrap mode, text can be broken only at word boundaries; in character-wrap mode, text can be broken between almost any two characters. The modes are selected with the new tag <WRAP>: <WRAP WORD> sets word-wrap mode, and <WRAP CHAR> sets character-wrap mode.
    • The formatter recognizes the Unicode character \uFEFF (HTML entity "&zwnbsp;"), the zero-width non-breaking space. This character doesn't show up on the display, but it tells the formatter not to break the line between the two adjacent characters, even if the text-wrapping rules would otherwise permit it.
    • The formatter recognizes the Unicode character \u200B ("&zwsp;"), the zero-width space. This character doesn't show up on the display, but it tells the formatter that it can break the line between the two adjacent characters, even if the wrapping rules wouldn't ordinarily allow a line break there.
    • The formatter recognizes several of the Unicode special-width space characters (en space, em space, etc). All of these are treated as quoted space, in that they don't combine with adjacent runs of ordinary whitespace. These spaces allow finer control over visual spacing, which facilitates a more typographical appearance in the HTML interpreters.
    • The "soft hyphen" character, \u00AD (HTML "&shy;") is now accepted by the formatter. This is invisible in most cases, but it tells the formatter that it can break the line by hyphenating. If the formatter does break a line at a soft hyphen, it displays the soft hyphen as an ordinary visible hyphen.
  • Added the new "log file console." This is a special kind of console object that has no display, but simply captures all of its output to a log file. This can be used to capture game output to a file, using the standard output formatting (including HTML interpretation and word wrapping), without displaying anything to the player. Refer to the tads-io function set documentation for details.

Changes for 3.0.6a (11/23/2002)

  • The new #pragma newline_spacing(on|off) directive lets the program control the treatment of newlines in strings. By default, newline spacing is set to "on": when a newline character is found within a string, the compiler converts the newline plus any whitespace that immediately follows to a single space character. When newline spacing is set to "off", the compiler instead simply strips out the newline and any immediately following whitespace. Setting newline spacing to "off" is desirable in some languages, such as Chinese, where whitespace isn't conventionally used as a token separator in ordinary text; turning off newline spacing can make it easier to write code in these cases, because it allows strings to be split over several lines without introducing unwanted whitespace.
  • The compiler now allows macro substitutions in library (.tl) files. In a library file, the value of a preprocessor symbol defined with the -D option can be substituted into the text of the library file using the notation "$(VARNAME)": the variable name is enclosed in parentheses preceded by a dollar sign. The adv3 library (adv3.tl) uses this capability to parameterize the language-specific library and message file selection. See Compiling and Linking for more information.
  • The text-only interpreters now treat "&nbsp;" as a true non-breaking space. (In the past, "&nbsp;" was treated the same as "\ ", the quoted space: the formatter did not combine such a space with other adjacent whitespace, or remove it from the start of a line, but did allow a line break to occur at an "&nbsp;" character.)
  • On Workbench for Windows, the Build Settings dialog has a new page that lets you set the directory paths to use for object files and symbol files. This new page's tab is labeled "Temp Files," as the symbol and object files are intermediate files that the compiler produces and reads during compilation. (These settings correspond to the -Fy and -Fs compiler options.)
  • When Workbench for Windows creates a new project, it automatically creates a subfolder (of the folder containing the project file) to store the project's object and symbol files. The new subfolder is called "obj". Workbench initializes the new project's object file and source file path options to the new subfolder. The purpose of using the subfolder is to keep the compiler-generated intermediate files separate from the project's source files, to make it easier to manage the files associated with the project.
  • A portability bug that caused interpreter crashes on some platforms (notably Solaris) has been fixed. (This bug only occurred when the interpreter was compiled with certain C++ compilers, and prevented the interpreter from running any games.)
  • The 'inherited' operator did not work properly to inherit a modified intrinsic class method from the intrinsic base class of a modified intrinsic class. For example, if a method defined with "modify TadsObject" used 'inherited' to inherit, and a method of the same name was defined with "modify Object", the Object modifier method was not properly inherited. This has been corrected.
  • The new compiler option "-errnum" causes the compiler to display the numeric error code for each warning or error message. By default, the compiler does not display the numeric codes for errors, because the code is mostly for the compiler's own use; however, the new option allows the user to show these codes if desired, for purposes such as cross-referencing with documentation.
  • The new compiler option "-w-nnn" causes the compiler to suppress the warning message identified by nnn, which is the compiler's internal numeric code for the warning. When this option is used, the compiler will not display any warnings of the given type, and will not include any such warnings in its summary error count. This option can be repeated to suppress multiple types of warning messages. Only warnings (and pedantic warnings) can be disabled with this option; specifying codes for errors of severity greater than warnings will have no effect. The complementary option "-w+nnn" can be used to explicitly enable a warning; by default, all warnings are enabled, so the only reason this option would be needed is to enable a warning previously removed with "-w-nnn" on the same command line, which might sometimes be useful with shell aliases or command scripts. Note also that the "-w+nnn" option cannot be used to selectively enable certain pedantic warnings without enabling pedantic mode; pedantic warnings are an entirely separate class of messages from regular warning, and can only be enabled as a group using the "-w2" option. Once pedantic warnings are generally enabled, you can use "-w-nnn" to disable specific pedantic warnings you wish to suppress. +
  • When searching for an external resource file (.3r0, .3r1, etc.), the interpreter now looks for a file with both a lower-case and upper-case version of each suffix. This is intended to make the naming convention more flexible on case-sensitive file systems, such as Unix; on systems with case-insensitive file naming, such as Windows or Mac, this change has no effect.
  • Added support for mixed multi-byte character sets to the character mapper; this allows the compiler to read source files encoded in multi-byte character sets, and allows the interpreters to use local system character sets with multi-byte encodings, both for displaying text on the screen and reading text from the keyboard. A number of widely-used character sets for Asian languages use this type of character set (Windows systems localized for Chinese and Japanese use multi-byte character sets, for example). Such character sets have always been supported in the character map compiler (mkchrtab), but the character mapper used in the compiler and VM did not implement the mapping logic for these types of character sets. Double-byte character sets are now fully supported; the character mapper can now handle any character set that uses two-byte sequences for all of its characters (usually called double-byte character sets), or that uses a mixture of one-byte and two-byte sequences (usually known as multi-byte or mixed-multi-byte character sets).
  • In Workbench for Windows, the Debug Log window displayed its contents using the same color settings as in the main game window. The Debug Log window now uses the color settings from the Workbench "options" dialog, the same as source file windows.
  • In the Build Settings dialog in Workbench for Windows, on the Installer page, a new field allows overriding the default character mapping library (cmaplib.t3r) with a custom mapping library. This is especially useful for authors working in Asian languages; the default character map library includes mappings for the common character sets for most European languages, but does not include the Asian character sets because of the large size of these sets. Authors working in languages which require character sets not included in the default mapping library can create their own mapping file or files (see the character mapping documentation), then bundle the file(s) into a custom library (a .t3r file) using the t3res tool. Specify this .t3r file in the new Character Map Library field, and the installer will automatically bundle this file into your game's executable file when building the install set.
  • Fixed a problem in Workbench for Windows that caused Workbench to reload a library (.tl) file every time Workbench became the foreground application (i.e., its main window was activated after another application had been in the foreground), if the library was ever modified after being loaded in Workbench in that session.
  • Fixed a problem in the UTF-8 character mapper that could have caused problems in rare cases when reading from a text file encoded in UTF-8. The mapper did not correctly figure the byte length of the last character when reading a three-byte character at the end of the buffer and the first two of the three bytes was read into the buffer. (This problem was extremely unlikely to actually occur in practice, but in any case it's now fixed.)
  • Fixed a problem in the GrammarProd class that caused interpreter crashes under certain circumstances. In particular, if a grammar rule was matched with a '*' element in the rule, and the matched input string had exactly six tokens, an internal memory corruption led to an interpreter crash. This has been corrected.
  • Fixed the t3make error message "error creating image file" to properly show the image file name as part of the message. This message incorrectly showed the filename as "s", regardless of the actual name of the image.
  • Fixed a problem in the DOS character-mode interpreters (t3run and tr32) that caused start-up error messages (in particular, errors relating to loading the .t3 file, such as a "game file not found" error) to be cleared from the display before the user had a chance to see them.

Changes for 3.0.5 (09/22/2002)

  • The <BANNER> tag is no longer allowed for T3 HTML programs. T3 games should use the banner API instead. (The <BANNER> tag has been removed entirely because it interacts poorly with the banner API. The adv3 library uses the banner API for the status line, so this essentially forces programs using the library to do likewise. To reduce the potential for any confusion that could result from games inadvertantly mixing banner API calls and banner tags, we simply disallow banner tags entirely, to ensure that all T3 programs use the banner API uniformly.) For the moment, <BANNER> tags are actually disallowed only when the banner API is supported by an HTML interpreter; for HTML interpreters that don't yet support the banner API, the tags are still allowed. This support will be withdrawn soon, though; we're just waiting until we're sure that banner API support is available on all HTML interpreters.
  • The compiler now assumes that each filename and folder name specified in a ".t3m" file (a compiler makefile) is given relative to the folder location of the .t3m file itself. This makes the compiler essentially independent of the working directory when using a .t3m file, because the location of the .t3m file effectively becomes the working directory for any file and folder paths specified within. This treatment naturally does not apply to any filename given in a .t3m file with an absolute path (for example, a Unix path that starts with "/", an MS-DOS path that starts with "\" or a drive letter, or a Macintosh path that starts with a volume name), since an absolute path is already independent of the working directory.
  • The compiler now keeps information on the compiler version that was used to compile a module. The compiler checks this information and automatically rebuilds a module that was built by an older version of the compiler. This ensures that all components of a program are rebuilt whenever a new version of the compiler is installed. (In the past, the compiler did not consider itself to be a dependency of the derived files, which occasionally led to confusing link-time errors caused by mismatched object file information.)

Changes for 3.0.5h (09/14/2002)

  • Some slight additional performance optimizations in the VM should improve execution speed by another 10% or so (vs. release 3.0.5g).
  • Fixed a problem in the Dictionary class that caused spurious run-time error when restoring games (particularly when restoring after a RESTART operation had been previously performed).
  • Fixed a problem in the regular expression parser: the counted-repeat operator ({n,m}) did not nest properly with closures.
  • Fixed a debugger problem that caused the debugger to fail to take control (i.e., pause the game program and enter the debugger UI) when a run-time error occurred under certain conditions when using the "Step Out" or "Step Over" commands.
  • Fixed a problem in the console-based text-only interpreters that caused the [More] prompt to be displayed at the wrong place (at the end of a line of text, rather than at the beginning) under certain rare conditions.

Changes for 3.0.5g (09/08/2002)

  • The Dictionary intrinsic class has been substantially altered. The central change is that the caller can now specify a "comparator" object that defines how strings in the dictionary are compared to input strings; this allows the game to customize the rules for looking up dictionary words. Related to this, all methods related to truncation length have been removed; instead, truncation, if desired, is now handled through the comparator. Finally, the methods that look up words in the dictionary (findWord and isWordDefined) have changed slightly to provide additional information from the comparator about how an input word matches a dictionary word.
  • Added the StringComparator intrinsic class. This class works with the Dictionary intrinsic class to specify customized rules for matching input words to dictionary words. StringComparator provides options that specify whether or not upper- and lower-case letters are distinguished; whether words can be truncated (abbreviated) on input, and to what minimum length; and a set of equivalence mappings that allow alternative spellings on input, such as using unaccented equivalents of accented characters.
  • The GrammarProd intrinsic class method parseTokens() now uses the supplied Dictionary to perform comparisons to literal tokens. Each input token is compared to literal tokens in the grammar rules using the Dictionary's current comparator. Literal tokens in grammar rules thus are matched using the same rules as dictionary words. The matchValues() results are stored in the match tree objects in the new property tokenMatchList: this property is set in each object in the match tree, and contains a list of the matchValues() results corresponding to the token list elements.

Changes for 3.0.5f (09/02/2002)

  • Added the new method findReplace() to the String intrinsic class. This new method is similar to the rexReplace() function, but is slightly more efficient for situations where the text to be replaced is a simple constant string rather than a regular expression pattern. It's also slightly simpler to use, since it's not necessary to worry about any special regular expression interpretation of the search string.
  • Added the new intrinsic class RexPattern. This new class encapsulates a "compiled" regular expression pattern, which can be used in place of a pattern string in the regular expression functions. Compiling an expression converts it from a string into an internal representation. When a particular pattern is used repeatedly in many searches, it's much more efficient to use a RexPattern object to conduct searches with the pattern, because the regular expression functions have to compile their search patterns internally anyway; by using a RexPattern, you perform the compilation for a particular pattern only once, in advance, rather than each time you use the pattern. Compilation takes a non-trivial amount of time, so if a pattern is used frequently, pre-compiling it to a RexPattern can make a difference in execution performance.

Changes for 3.0.5e (08/25/2002)

  • This release has a number of VM, compiler, and intrinsic class changes intended to make programs run faster. The compiler now generates more optimized code (especially when compiling with no debug information), and VM executes code faster. The changes should be entirely transparent in terms of functionality, although a few new VM instructions have been added, so code compiled with this new version of the compiler will not run with older VM versions.
  • Added two new LookupTable methods: keysToList(), which returns a list consisting of all of the keys in the table; and valsToList(), which returns a list consisting of all of the values in the table. In both cases, the lists are in arbitrary order.
  • The compiler left out a final newline after the error/warning count summary at the end of a compilation that generated error or warning messages. This is now fixed.
  • Fixed a couple of dialog messages in Workbench for Windows that referred to ".t3x" files (the old version of the ".t3" extension).
  • Fixed a problem in Workbench for Windows that caused an unparseable compiler command line to be generated if an option argument (such as an Include path) ended with a backslash. The problem had to do with DOS's odd command-line quoting rules; the trailing backslash was treated as "quoting" the quote mark at the end of the option argument. This has been corrected.
  • The compiler's command line error diagnostics are now somewhat improved, to help pinpoint the source of an error. In the past, the compiler always simply showed its usage message if the command line had a problem, which didn't help much in identifying which argument was causing the problem. The compiler now shows the usage message only when the command line is empty (or when the argument "-help" is given); in case of error, the compiler now shows a message explaining the specific problem, and shows the particular option causing the problem if appropriate.
  • In Workbench for Windows, the folder selector dialog was started with an empty current folder, which made the entire dialog appear empty, in some cases (such as when adding a directory to the "Include" list in the Build Settings dialog). The dialog should now always start with a valid initial directory.

Changes for 3.0.5d (08/04/2002)

  • Workbench for Windows now has a primitive but usable "profiling" feature. Profiling is a technique in which the programmer collects data on where the running program is spending its time. The purpose is to identify any areas where the program spends a disproportionate amount of time; this sometimes reveals obvious bottlenecks where the code can be sped up with simple improvements, and in any case usually reveals the areas where optimization efforts will yield the greatest results. Workbench collects profiling data with function/method granularity: each time the program enters or exits a function or method, Workbench can track the time spent in that code. The user can turn profiling on and off using the new "Profiler" menu; when profiling is turned on, Workbench collects profiling data until the profiler is turned off, at which point the system asks for a file to which to write the collected data. Workbench writes the data as a tab-delimited text file: the first column is the name of the function or method; the second is the number of microseconds spent directly in that code; the third is the number of microseconds spent in functions or methods called by that code (which excludes the time spent directly in the code itself); and the fourth is the number of times the function or method was invoked. This format can be easily loaded into an external analysis tool (such as a spreadsheet application) for inspection. (The profiling facility is designed mostly for use by library authors, so we probably won't go to great lengths in the area of ease of use; and we don't expect to build any integrated analysis tools, as a spreadsheet is more than adequate to analyze the data.)
  • Fixed some problems in the DOS interpreter's display management (these are generic character-mode interpreter fixes that will also take care of the same problems on Unix and other character-mode systems). The problems affected resizing the terminal window, interaction with "More" prompts in some situations, and cursor placement under certain conditions. In addition, the "review mode" status bar is back (it was missing from the first versions with the banner API); note, though, that the mode bar is now displayed at the top of the main game window rather than taking over the status line, which is necessary because of the possibility the banner API creates of non-standard status line layouts.
  • Fixed a problem in the VM that could conceivably have caused crashes or other misbehavior under certain extremely improbable conditions involving stack overflows.

Changes for 3.0.5c (07/28/2002)

  • The banner creation function bannerCreate() has a slightly modified interface. The new interface allows the initial size of the banner to be set in terms of the natural sizing units of the window; in addition, percentage-based sizing is still possible as well.
  • Percentage-based banner sizing now behaves slightly differently than it used to. In the past, the percentage size was based on the size of the complete application window. The percentage size is now based instead on the "remaining space" at the point at which a banner is laid out during the screen layout process.
  • The new banner function bannerSetSize() complements bannerSizeToContents() by adding the ability to set a banner's size in terms of the natural sizing units of the window or a new percentage-based size.

Changes for 3.0.5b (07/20/2002)

  • Fixed a bug in the interpreter that caused sporadic crashes in situations involving adding lots of new properties to an object (of class TadsObject).

Changes for 3.0.5a (07/14/2002)

  • A new set of functions, collectively called the banner API, provides more direct programmatic control over the appearance of the user interface than was available in past versions of TADS. The new API allows the program to divide the display into multiple "banners," which are non-overlapping windows, each with its own independent output stream. The banner API provides functionality based on the HTML TADS <BANNER> tag, but offers more power, and is also supported on many text-only interpreters.
  • Any time a nested object is defined, the compiler now automatically defines the property lexicalParent in the nested object, setting the property to a reference to the lexically enclosing object.
  • The main text window's output stream is now always in HTML mode. The "\{" and "\}" sequences have likewise been removed, since these are no longer useful now that HTML mode cannot be switched on and off.
  • The "\H+" and "\H-" output control sequences have been removed. These are no longer useful now that HTML mode switching has been removed.
  • The "\(" and "\)" output control sequences have been removed. The HTML markups <B> and </B> should be used instead. The "\(" and "\)" sequences are now redundant with the HTML <B> markup, since text output streams are now always in HTML mode.
  • A new system information code, SysInfoInterpClass, retrieves information on the broad "class" of the interpreter. This returns one of the following:
    • SysInfoIClassText: a text-only character-mode interpreter, such as MS-DOS TADS or Unix TADS. This type of interpreter uses a single fixed-pitch font to show all text, cannot show any graphics, and uses the text-only HTML subset.
    • SysInfoIClassTextGUI: a text-only interpreter running on a graphical platform, such as MacTADS or WinTADS. This type of interpreter might use proportionally-spaced fonts, and might use multiple fonts. These interpreters cannot show graphics using HTML, but they might use some graphics of their own for things like window borders. This type of interpreter recognizes the text-only HTML subset. Note that this code isn't indicative of the type of OS, but rather of the type of interpreter: MS-DOS TADS always returns SysInfoIClassText, even when it's running in a DOS box under Windows, because it still acts like a character-mode interpreter even when running on a Windows machine.
    • SysInfoIClassHTML: a full HTML interpreter, such as HTML TADS on Windows, or HyperTADS on Macintosh.
  • The SysInfoHtml and SysInfoHtmlMode system information codes have been eliminated. The former is superseded by SysInfoInterpClass, and the latter has been rendered obsolete by the elimination of HTML mode switching.
  • The TADS 3 character-mode interpreters now support the <TAB> tag, if the underlying OS code allows. This tag is supported on all full HTML interpreters, plus most character-mode text-only platforms. The GUI text-only interpreters do not support this currently. The full complement of <TAB> features is supported; see the HTML TADS documentation (specifically, the TADS-specific markup extensions) for details.
  • The compiler and VM support "transient" objects, which are objects that do not participate in any of the VM's built-in persistence mechanisms (save, restore, undo, restart). A transient object is not saved to a saved state file, and isn't affected by restore, undo, or restart operations. This is described more fully in the Object Definitions section of the documentation.
  • The saveGame function works differently than it used to. The function no longer saves the execution context, so it no longer has the "setjmp" behavior.
  • The restoreGame function works differently as well. Because saveGame no longer saves the execution context, restoreGame no longer restores it, so the function simply returns on successful completion - it doesn't have the "longjmp" behavior any more. This new design is much more flexible, because it allows arbitrary session information to be retained across the Restore operation, via local variables on the stack as well as with transient objects, and gives the program complete control over execution flow throughout the operation.
  • The restartGame function also has a new design. The function no longer requires a function to jump to, but simply resets object state and returns. As with the restoreGame changes, this allows the program complete control over execution flow and allows any amount of session information to be retained across the Restart operation.
  • All instances of the File intrinsic class are now transient. The static creation methods of the File class always return transient objects.
  • It is now possible to open "resources" as though they were files, and read their data using the File intrinsic class. Two new static construction methods make this possible: openTextResource() and openRawResource().
  • The option flags for the String.htmlify() method have been renamed, to make the names of the flags more descriptive of their functions. The flags formerly were of the form HtmlifyKeepXxx, but now use names of the form HtmlifyTranslateXxx. The change of terms to "translate" is a better match for what the flags actually mean, since the flags specify that certain characters are to be translated into HTML markup equivalents.
  • In Workbench for Windows, when the status line is displayed, the messages from the compiler showing the build progress are not displayed to the debug log window, but rather to the status line. This makes it much easier to read the results in the log window, and especially makes it easier to see the error messages, since the error messages aren't mixed in with lots of build progress messages. When the status line is hidden, build progress messages are still shown in the log window so that the compiler's progress can be seen.
  • The internal character encoding that the T3 software was using in past versions was not in conformance with the T3 specification, which calls for the standard UTF-8 encoding. The software was using an older encoding that had been obsoleted in the spec but was never changed in the software. This has been corrected; note that this change requires recompiling any existing games.
  • Due to a bug, Workbench for Windows did not properly store directory paths (such as for the Include path list) in the project file when the paths included a colon (":") character. This has been fixed.
  • A bug in Workbench for Windows caused the Window menu to display incorrect entries for Windows 95 and NT 4 (actually, this is more due to a bug in Windows 95 and NT 4, but Workbench didn't work around the bug, so in a sense it was a Workbench bug). This has been corrected.
  • A compiler bug caused the compiler to crash (with an access violation or equivalent) when given a source file whose very first object definition started with a template containing a double-quoted string with an embedded expression. This problem is now fixed.

Changes for version 3.0.4 - 06/01/2002

  • Fixed a problem in the Vector intrinsic class that caused interpreter crashes under certain circumstances. (The exact conditions were not entirely predictable, because the problem had to do with an interaction between the Vector class, the garbage collector, and the undo mechanism, and only occurred when garbage collection was triggered after certain operations in the Vector class.)
  • Added a status line to Workbench for Windows. The status line is optional; it can be displayed or hidden, as desired, using the "View" menu. When displayed, the status line shows whether or not the program being debugged is running, and shows the line and column number of the current cursor position when a source file window is active.
  • Fixed a problem in Workbench that prevented parts of the window layout from being saved in the project configuration file when the main Workbench application window was closed while the program being debugged was still running.

Changes for 05/19/2002

  • The DOS text-mode interpreter now supports setting text colors. Text colors can be set through HTML, as follows:
    • The <FONT COLOR=xxx BGCOLOR=yyy> tag can be used to set the and background colors of the text. The basic set of HTML colors is recognized, although the effective set of available colors is limited to the eight ANSI colors that DOS character mode can display: white, black, red, green, blue, yellow, cyan, and magenta. Hex RGB values (for example, <FONT COLOR='#C080E0'> are accepted, but these are simply mapped to the nearest available color from the ANSI set. Note that if a FONT tag specifies both COLOR and BGCOLOR, the text will be displayed exactly as specified; if the FONT tag specifies only COLOR, then the background color will be inherited from the enclosing text, and ultimately from the <BODY> color if no enclosing text has a specific background color.
    • The <BODY> tag can be used to set the foreground and background color of the window. The TEXT attribute sets the foreground text color, and the BGCOLOR attribute sets the background color of the window. The background color affects the entire window, just as in the HTML interpreters, so the entire window will be redisplayed in its new color whenever a <BODY BGCOLOR=xxx> tag is displayed.
    • The parameterized color names (TEXT, BGCOLOR, STATUSTEXT, STATUSBG) are accepted and map to the current settings of those colors, as set by the TRCOLOR program.
  • The new system information code SysInfoTextColors can be used to determine the level of color support available on the platform. The return value is a code indicating the level of support:
    • SysInfoTxcNone - no color support
    • SysInfoTxcParam - parameterized color names only
    • SysInfoTxcAnsiFg - The eight ANSI colors are available for text, but there is no capability to set the background color
    • SysInfoTxcAnsiFgBg - the ANSI colors are available for text and background colors (this is the code the DOS interpreter returns)
    • SysInfoTxcRGB - full RGB text color support is available (this is the code the Windows HTML interpreter returns)
  • The new system information code SysInfoTextHilite can be used to determine whether or not text highlighting is available. This returns true if a distinctive appearance for <B> text is available, nil if not. The HTML interpreters return true for this; the text-only interpreters generally return true, unless running in "plain" ASCII mode or with a terminal or display device that cannot render anything special for highlighted text.

Changes for 05/05/2002

  • Fixed a problem in Workbench for Windows with libraries (.tl files) found via the library search path. In some cases, the files within a library could not be opened; in addition, the library itself was not always properly scanned on opening the project, so no files within the library were visible in the Project window. Libraries found via the search path should work properly now.
  • Workbench for Windows now uses small (8x8-pixel) icons in the left margin of debugger source windows when the selected source window font is smaller than the standard (16x16-pixel) icons. (These are the icons that show the location of the current line, breakpoints, and so on during a debugging session.) This allows more lines to be displayed vertically in a source window when a small font is selected.
  • When quitting out of Workbench, if a build is still running, the Workbench displays a dialog indicating that Workbench is waiting for the build to complete. In the past, there was no way to force Workbench to exit if the build process had become stuck. Now, the "waiting for build" dialog offers an "Abort" button; clicking this button will forcibly terminate the build, allowing Workbench to terminate immediately. This button is useful when the build process becomes stuck (which ideally should never happen, but has happened at times due to compiler bugs). The build process should not normally be aborted forcibly unless the build appears stuck, because forcibly terminating a process on Windows can occasionally cause Windows to become unstable.
  • Fixed a compiler bug that made it possible for the linking step to get stuck in an infinite loop if the data associated with a particular "grammar" production became too large. This should no longer occur.
  • Fixed a debugger bug that caused sporadic crashes when running games in Workbench.
  • Added new systemInfo() codes for MNG display:
    • SysInfoMng - can the system display MNG images?
    • SysInfoMngTrans - can the system display MNG's with transparency?
    • SysInfoMngAlpha - can the system display MNG's with alpha channels (i.e., partial transparency)?

Changes for 04/28/2002

  • Renamed numerous macros in the system and adv3 header files to achieve more consistency in the naming scheme. Here's a table of the changes. (Sorry this isn't a Perl script or something.)

    Old NameNew Name
    BIGNUM_SIGNBignumSign
    BIGNUM_EXPBignumExp
    BIGNUM_EXP_SIGNBignumExpSign
    BIGNUM_LEADING_ZEROBignumLeadingZero
    BIGNUM_POINTBignumPoint
    BIGNUM_COMMASBignumCommas
    BIGNUM_POS_SPACEBignumPosSpace
    BIGNUM_EUROSTYLEBignumEuroStyle
    TYPE_NILTypeNil
    TYPE_TRUETypeTrue
    TYPE_OBJECTTypeObject
    TYPE_PROPTypeProp
    TYPE_INTTypeInt
    TYPE_SSTRINGTypeSString
    TYPE_DSTRINGTypeDString
    TYPE_LISTTypeList
    TYPE_CODETypeCode
    TYPE_FUNCPTRTypeFuncPtr
    TYPE_NATIVE_CODETypeNativeCode
    TYPE_ENUMTypeEnum
    PROPDEF_ANYPropDefAny
    PROPDEF_DIRECTLYPropDefDirectly
    PROPDEF_INHERITSPropDefInherits
    PROPDEF_GET_CLASSPropDefGetClass
    STR_HTML_KEEP_SPACESHtmlifyKeepSpaces
    STR_HTML_KEEP_NEWLINESHtmlifyKeepNewlines
    STR_HTML_KEEP_TABSHtmlifyKeepTabs
    STR_HTML_KEEP_WHITESPACEHtmlifyKeepWhitespace
    T3DBG_CHECKT3DebugCheck
    T3DBG_BREAKT3DebugBreak
    OBJ_INSTANCESObjInstances
    OBJ_CLASSESObjClasses
    OBJ_ALLObjAll
    REPLACE_ONCEReplaceOnce
    REPLACE_ALLReplaceAll
    GETTIME_DATE_AND_TIMEGetTimeDateAndTime
    GETTIME_TICKSGetTimeTicks
    INEVT_KEYInEvtKey
    INEVT_TIMEOUTInEvtTimeout
    INEVT_HREFInEvtHref
    INEVT_NOTIMEOUTInEvtNotimeout
    INEVT_EOFInEvtEof
    INEVT_LINEInEvtLine
    INEVT_END_QUIET_SCRIPTInEvtEndQuietScript
    INDLG_OKInDlgOk
    INDLG_OKCANCELInDlgOkcancel
    INDLG_YESNOInDlgYesNo
    INDLG_YESNOCANCELInDlgYesNoCancel
    INDLG_ICON_NONEInDlgIconNone
    INDLG_ICON_WARNINGInDlgIconWarning
    INDLG_ICON_INFOInDlgIconInfo
    INDLG_ICON_QUESTIONInDlgIconQuestion
    INDLG_ICON_ERRORInDlgIconError
    INDLG_LBL_OKInDlgLblOk
    INDLG_LBL_CANCELInDlgLblCancel
    INDLG_LBL_YESInDlgLblYes
    INDLG_LBL_NOInDlgLblNo
    INFILE_OPENInFileOpen
    INFILE_SAVEInFileSave
    INFILE_SUCCESSInFileSuccess
    INFILE_FAILUREInFileFailure
    INFILE_CANCELInFileCancel
    FILETYPE_LOGFileTypeLog
    FILETYPE_DATAFileTypeData
    FILETYPE_CMDFileTypeCmd
    FILETYPE_TEXTFileTypeText
    FILETYPE_BINFileTypeBin
    FILETYPE_UNKNOWNFileTypeUnknown
    FILETYPE_T3IMAGEFileTypeT3Image
    FILETYPE_T3SAVEFileTypeT3Save
    SYSINFO_VERSIONSysInfoVersion
    SYSINFO_OS_NAMESysInfoOsName
    SYSINFO_HTMLSysInfoHtml
    SYSINFO_JPEGSysInfoJpeg
    SYSINFO_PNGSysInfoPng
    SYSINFO_WAVSysInfoWav
    SYSINFO_MIDISysInfoMidi
    SYSINFO_WAV_MIDI_OVLSysInfoWavMidiOvl
    SYSINFO_WAV_OVLSysInfoWavOvl
    SYSINFO_PREF_IMAGESSysInfoPrefImages
    SYSINFO_PREF_SOUNDSSysInfoPrefSounds
    SYSINFO_PREF_MUSICSysInfoPrefMusic
    SYSINFO_PREF_LINKSSysInfoPrefLinks
    SYSINFO_MPEGSysInfoMpeg
    SYSINFO_MPEG1SysInfoMpeg1
    SYSINFO_MPEG2SysInfoMpeg2
    SYSINFO_MPEG3SysInfoMpeg3
    SYSINFO_HTML_MODESysInfoHtmlMode
    SYSINFO_LINKS_HTTPSysInfoLinksHttp
    SYSINFO_LINKS_FTPSysInfoLinksFtp
    SYSINFO_LINKS_NEWSSysInfoLinksNews
    SYSINFO_LINKS_MAILTOSysInfoLinksMailto
    SYSINFO_LINKS_TELNETSysInfoLinksTelnet
    SYSINFO_PNG_TRANSSysInfoPngTrans
    SYSINFO_PNG_ALPHASysInfoPngAlpha
    STATMODE_NORMALStatModeNormal
    STATMODE_STATUSStatModeStatus
    SCRFILE_QUIETScriptFileQuiet
    SCRFILE_NONSTOPScriptFileNonstop
    CHARSET_DISPLAYCharsetDisplay
    CHARSET_FILECharsetFile
    firstPersonFirstPerson
    secondPersonSecondPerson
    thirdPersonThirdPerson
    spellIntTeenHundredsSpellIntTeenHundreds
    spellIntAndTensSpellIntAndTens
    spellIntCommasSpellIntCommas
    Logicallogical
    LogicalRanklogicalRank
    LogicalRankOrdlogicalRankOrd
    Dangerousdangerous
    NonObviousnonObvious
    IllogicalNowillogicalNow
    Illogicalillogical
    Inaccessibleinaccessible
    DefaultReportdefaultReport
    MainReportmainReport
    ReportBeforereportBefore
    ReportAfterreportAfter
    ReportFailurereportFailure
    IsNonDefaultReportgIsNonDefaultReport
    SingleDobjsingleDobj
    SingleIobjsingleIobj
    DobjListdobjList
    IobjListiobjList
    NumberPhrasesingleNumber
    TopicPhrasesingleTopic
    LiteralPhrasesingleLiteral
    DirPhrasesingleDir
  • Added Phil Lewis's new Adv3 Reference Help to Workbench for Windows. This new Windows Help file is a reference to the classes in the adv3 library; Phil created it for his own use but has graciously offered it for inclusion in the Author's Kit distribution. Phil says the reference isn't complete, but it has a wealth of information all the same. The Adv3 Help can be reached from the "TADS 3 Library" item on the Workbench "Help" menu.
  • The compiler can now search for source files using a user-specified list of directories. This allows makefiles (.t3m files) to specify source files using only relative filename paths, eliminating any dependency in a makefile on the local system's directory structure and thus allowing the project to be easily used on other systems. The compiler searches for its files by looking in the following locations, in order:
    • The current working directory (for Workbench users, this is the directory containing the .t3m file for the project).
    • The directories specified with "-Fs" compiler options, in the order of the options.
    • The directories specified with the new "user library" mechanism.
    • The "system library" directory. This is the special directory containing the compiler's own libraries. This directory varies by system, but it is normally set up automatically when TADS is installed and requires no user action to configure.
    The "user library" list is specified using a system-dependent mechanism:
    • For the DOS/Windows command-line compiler (i.e., run from a command prompt), set the environment variable TADSLIB to a list of directories, separated by semicolons (";").
    • For Workbench for Windows, enter the list of directories in the "Libraries" page of the Workbench preferences dialog, reachable from the "Options" item on the "View" menu.
    • The mechanisms for other systems are yet to be determined, but in all likelihood Unix will use the same TADSLIB environment variable as the DOS version.
  • Workbench for Windows has a new option to automatically load the most recent project when Workbench is started. To set the start-up mode, use the "Start-Up" page of the Workbench preferences dialog, reachable from the "Options" item on the "View" menu.
  • Added the new systemInfo() code SysInfoOgg, which senses whether or not the interpreter supports Ogg Vorbis audio playback.
  • Changed inputEvent() so that it accepts a nil timeout argument value, for consistency with inputLine(). A nil value for the timeout is equivalent to omitting the timeout argument entirely: it indicates that the input should be allowed to proceed indefinitely.
  • The compiler now stores preprocessor symbol (macro) definitions with the debugger records of the compiled program, when compiling for source-level debugging (i.e., using the "-d" compiler command-line option). The debugger uses the preprocessor symbols when parsing expressions during the debugging session, so it is now possible to interactively evaluate expressions involving macros. Note that the compiler keeps debugging records for a macro only if the macro (1) is never undefined (using #undef) or redefined with a different expansion within a given module, and (2) has the exact same expansion in every module in which it is defined; these restrictions mean that only macros with consistent "global" meanings across the entire program are visible in the debugger.
  • Fixed a problem in Workbench for Windows that caused an incorrect name to be stored for the game's initial source file when creating a new project.
  • Fixed a compiler problem that caused the compiler to crash when confronted with a constant index expression where the indexed value was not a list (e.g., 1[1]).
  • Fixed a problem in the Windows HTML interpreter that prevented interrupted command lines from continuing correctly. When a command line input was interrupted by a timeout, the command line that was under construction when the timeout occurred was not properly restored for further editing. This has been corrected.
  • Fixed a problem in the VM that caused the VM to crash under rare circumstances. The problem was particularly likely to occur when stepping through large amounts of code with the debugger.
  • Fixed a problem in the character map compiler (mkchrtab) that generated corrupted character map files when the source file had duplicated display mapping entries.
  • The distributions (including the installer kits and the source code distribution) now include a full set of character mapping files that should cover most users on most platforms. For a listing of the included codings, see charmap/README.txt. (The full set adds only about 25k to the compressed distribution files, which seems a small cost for the simplicity of having an essentially universal set of mappings available with the default install.)
  • The Windows Author's Kit installer now associates .t3m files with Workbench, so that double-clicking on a .t3m file from Windows Explorer will open the file in Workbench. (The 4/7/2002 version still set the association for .t3c files instead, despite Workbench's switch to .t3c files for storing project configuration information.)
  • Fixed a problem that caused the compiler to display the incorrect text for invalid tokens in certain error messages related to 'grammar' statement syntax.

Changes for 04/07/2002

  • Workbench for Windows now uses the compiler makefile (.t3m) file format to store its project information. This means that a Workbench project file can be used directly as a compiler makefile, using the compiler's "-f" option. For the moment, Workbench is still capable of loading
  • Workbench on Windows now monitors for Ctrl+Break, and breaks into the debugger when this key combination is detected. This allows easily breaking into the debugger if the .t3 program enters an infinite loop.
  • The 'replace' and 'modify' keywords can now be used to change 'grammar' definitions. Refer to the Grammar Rules documentation for details.
  • A grammar production object can now be declared without creating any rules associated with the production. The new syntax is simply "grammar production_name;" (that is, a "grammar" statement that simply ends after the name of the production). This can be useful for libraries that want to define rules that refer to productions for which the library itself provides no rules, so that games based on the library can fill in the grammar definitions for the productions. In the past, if a production was referenced as a subproduction from one or more rules but had no rules of its own defined, the linker generated an error, because it is likely in such cases that the rules referring to the undeclared production were misspelled and meant to refer to a declared production; this new syntax gives the author a way to tell the compiler explicitly that a production name was used intentionally, even if the production has no rules.
  • Added new syntax that allows multiple part-of-speech properties (such as "noun" or "adjective") to be used as a single token matcher in a grammar rule. The new syntax uses a list of properties in angle brackets: <noun adjective plural>. When this syntax is used, an input token matching any of the part-of-speech properties in the list matches the rule token position.
  • Moved the "build configuration" information that was formerly stored in object (.t3o) files into the symbol (.t3s) files instead. The compiler uses the build configuration data stored in these files to determine if recompilation of a given source file is necessitated by build configuration changes since the last time the file was compiled. In the past, when the information was stored in the object files, the compiler unnecessarily regenerated symbol files repeatedly when compilations failed due to syntax errors, because compilation didn't get far enough to store the build data. Moving the information to the symbol files allows the compiler to skip symbol table generation when a previous compilation generated a valid symbol file for a given module. This saves time in the build-edit-build cycle when a program is initially compiled and contains syntax errors.
  • Added two new pseudo-variable keywords: targetobj and definingobj. These pseudo-variables are similar to self and targetobj, in that they act like read-only variables (i.e., you cannot assign values to them), and they are automatically maintained by the interpreter to keep track of the current method context. targetobj returns the "original target object" of the current method; this is the object that was targeted by the method call that reached the current method. The target object is set each time a method is called normally or via delegated, and remains the same as methods are invoked via inherited. definingobj is the object that defines the currently executing method; this is normally simply the object where the method was actually defined in the program's source code.
  • Removed the getMethodDefiner() intrinsic function from the "tads-gen" function set. This function has been replaced by the new pseudo-variable definingobj.
  • Changed the interface of the Object.propInherited() intrinsic class method. The method now takes an additional argument (positionally the second argument in the new interface) giving the original target object of the call. In cases where the caller wishes to know if the currently active method can inherit a base class method, the value for the new argument should simply be targetobj.
  • Changed the way inherited behaves after delegated is used. In the past, the inheritance mechanism tried to find the defining object of the method with the inherited operator in the inheritance tree of "self". After delegated, the defining object is normally not related by inheritance to "self", so the old search pattern normally failed to find any inherited instance of the method. Now, the interpreter keeps track internally of the "original target" object in addition to the "self" object; the original target is always the target of the last explicit method call or the target of the most recent delegated operator. The inherited operator uses the original target object as the starting point of the inheritance tree search, rather than "self"; this allows inherited to find methods inherited from base classes of the delegatee when appropriate.
  • Fixed a compiler bug that caused incorrect code to be generated for statements like this: if (constant_expr && non_constant_expr) - that is, an "if" whose condition expression consists of a constant expression (i.e., an expression whose value can be determined at compile-time) and a non-constant expression joined by the "&&" operator. The code that the compiler generated caused unpredictable results at run-time, and was able to crash the interpreter in some cases. This has been corrected.
  • Increased the standard VM stack size to 4096 stack slots (from 1024).
  • Expanded the compiler's maximum symbol length to 80 characters, to accomodate the longer symbols generated by the new treatment of production match object tagged names as actual compiler symbols.
  • Fixed a problem that caused a sporadic interpreter crash when using restartGame(). This crash occurred only rarely, and was related to memory management during program re-initialization. This should no longer occur.
  • Fixed a problem with restoreGame() that caused modifications to intrinsic classes to be lost in some cases. This problem occurred rarely, and should no longer occur.
  • Changed the character mapping input file format slightly. The "unicode_to_local" keyword has been renamed to "display_mappings", and has a slightly different effect: in the "display_mappings" section, each character mapping gives the Unicode expansion of a Unicode code point; that is, each mapping specifies a set of Unicode characters to substitute for the Unicode character being mapped. The expansion is then further translated automatically by the character mapper to the local character set. Each expansion must map to characters defined in the one-to-one mapping section (i.e., the set of mappings before the "display_mappings" keyword). Note in particular that an expansion cannot map to characters that are themselves further expanded. The purpose of this change is to allow the interpreter to perform expansion mappings - that is, mappings that can translate one Unicode character to several display characters - while still in the Unicode domain, and hence to perform operations such as word-wrapping on the expansions. The console output formatter uses this to properly word-wrap lines with expanded display mappings.

Changes for 03/16/2002

  • Fixed script-reading mode in the interpreter. Past versions did not correctly read input scripts (using the command-line interpreter's "-i" option, for example).
  • Added the new input event code INEVT_END_QUIET_SCRIPT, which is described in the documentation for the inputLineTimeout intrinsic function.
  • Added a couple of small usabilty enhancements to Workbench for Windows:
    • Pushing the Return/Enter key when keyboard focus is in the Project window opens the selected module in a source file window, if a module is selected. This allows easier keyboard navigation of the Project file list.
    • Double-clicking on a module in the Project window now moves keyboard focus to the newly-opened source file window. In the past, focus stayed on the Project window, so context-sensitive commands (such as "Find") did not apply to the newly-opened source window until manually clicking on the window to activate it.
  • Fixed a bug in mkchrtab (the character map compiler) that caused incorrect mappings in some compiled mapping files having display (from_unicode_to_local) mappings that expanded to more than one byte.
  • Fixed a problem in the Windows interpreter that made it incompatible with Windows 95. The interpreter should once again be compatible with Windows 95.

Changes for 03/10/2002

  • Added the new method removeElement() to the Vector intrinsic class. This new method removes an element by value, modifying the vector in-place.
  • Added the new method forEachWord() to the Dictionary intrinsic class. This new method provides a way of enumerating all of the entries in a Dictionary object.
  • Added information to the LICENSE.TXT file making it clear that derived versions of the library are permitted with few restrictions.
  • The "!" operator can now take an object reference as its operand; when applied to a non-nil object reference, "!" yields nil. This means that constructs such as "if (!obj)" are now treated as equivalent to "if (obj == nil)"; such constructs formerly generated a run-time error. In addition, property ID's, single-quoted strings, lists, and function pointers can all be used as operands of "!", and "!" applied to any of these types yields nil.
  • Fixed a compiler bug that caused the compiler to crash in some cases when confronted with an empty pair of parentheses within a grammar rule, such as grammar myProd: ('mytoken' ()).
  • Fixed a compiler bug that caused the compiler to crash in some cases when given a method definition with no code body and the end of the object immediately following.
  • Fixed a compiler bug that caused incorrect code generation for an 'if' with a constant controlling expression that evaluated to true and no 'else' clause. In such cases, the true branch of the 'if' was not generated; this has been corrected.
  • The project file extension for Workbench for Windows is now ".t3c" (it was formerly ".tdc", the same as it was for the TADS 2 Workbench). The extension has been changed to allow the Windows desktop to launch the appropriate version of Workbench when the user double-clicks a project file from the desktop and both TADS 2 and ADS 3 Workbench versions are installed.
  • Fixed a problem in the VM that caused repeated "terminate game" dialogs to appear in Workbench for Windows when the user attempted to compile the game after the debugger intercepted a run-time error.
  • Fixed a problem in the character map compiler (mkchrtab) that caused invalid map files to be generated if the source file had more than one symmetric (bidirectional) mapping assigned to the same Unicode code point. The character map compiler now generates a warning when these collisions occur, and generates valid map files even when collisions are present by ignoring redundant mappings.

Changes for 02/17/2002

  • The compiler now shows a summary count of warnings and errors at the end of the compiler console output when either count is non-zero, to make it more obvious that messages were generated during the compilation. If many modules are included in a compilation, the status messages that the compiler generates as it works through the module list can make it easy to miss an error or warning mixed in among all the status messages. The new summary at the end of the compilation should make it easier to notice when something requires attention. No summary counts are displayed when there are no warnings or errors; this is intended to make it even easier to notice when something is wrong, since authors will not be accustomed to seeing a summary count at all when everything is well.
  • The Iterator intrinsic class has the new methods getCurVal() and getCurKey(), which return the value and key of the current element of the iteration, respectively. For indexed collections, the "key" is the index of the current element.
  • LookupTable.forEach() no longer passes the key argument to the callback. If the key is desired, use the new method forEachAssoc() instead. This allows LookupTable to be used more interchangeably with other Collection subclasses, since its forEach() method has the same interface as the corresponding method of other Collection subclasses.
  • LookupTable, List, and Vector now have the additional method forEachAssoc(func), which calls func(key,val) for each element of the collection. For List and Vector, the "key" is the index of the element; for LookupTable, it is the key for the entry.
  • The Array intrinsic class has been deleted; Vector should be used instead. The Array class was too similar to the Vector class to justify its existence as a separate type, so the functionality of Array and Vector are now combined into the single Vector class.
  • When a Vector is used as the left-hand side of a '+' or '-' operation, in the past, the VM modified the left-hand side Vector object directly. This has been changed so that a new Vector is created, and the original left-hand Vector is left unchanged. The old behavior was confusing in that it gave the '+' and '-' operators side effects, which is inconsistent with the behavior of the operators in other situations.
  • A new Vector method, appendAll(), allows appending the individual elements of a List or Vector to another Vector in-place (i.e., without creating a new Vector object, as the '+' operator does).
  • The Vector methods getUnique() and subset() now return a new Vector to store the result of the operation. In the past, these methods modified the original Vector in place.
  • In the past, when a Vector was used as the right-hand side of a '+' operator whose left-hand side was a List or Vector, the List or Vector on the right was the single new element concatenated to the left-hand collection. This was inconsistent with the handling of List values; a List value concatenates its elements individually when used as the right-hand side of a '+' operator. For consistency, Vector now concatenates its elements individually when used on the right side of a '+' operator. Similarly, Vector values now subtract their elements from the left-hand side collection when used as the right-hand side of a '-' operator, also for consistency with the List type's behavior.
  • Fixed a problem in the VM that caused the VM to lose track of intrinsic class modifiers after a RESTORE operation. Intrinsic class modifiers will now properly survive RESTORE operations.

Changes for 02/09/2002

  • Changed inputLineTimeout so that it accepts a nil timeout argument value. A nil value for the timeout is equivalent to omitting the timeout argument entirely: it indicates that the input should be allowed to proceed indefinitely.
  • Improved inputLineTimeout's interaction with the OS layer so that the portable code can handle complete non-implementation of input-with-timeout by the OS layer.
  • Changed getTime(GETTIME_TICKS) so that the function always chooses the zero point of the system clock as the time of the first call to the function. This reduces the likelihood that a game will encounter a roll-over of the system clock, which could lead to confusion in interpreting time differences and the ordering of events.
  • Changed the names of two methods in the LookupTable intrinsic class. The method formerly called countBuckets() is now getBucketCount(), and the method formerly called countEntries() is now getEntryCount(). These names match the names as given in the documentation, but the header file (lookup.h) had the old names.
  • Added the compiler option "-clean", which causes the compiler to skip ordinary compilation and instead simply remove all derived files that the compilation would have produced; in particular, this removes the symbol (.t3s), object (.t3o), and image (.t3) files that the compilation would have produced.
  • Added the compiler option "-Pi", which causes the compiler to skip ordinary compilation and instead write to the standard output a list of files included with #include directives throughout the program. The compiler runs the normal preprocessing, and shows only files included in conditional compilation sections that are selected in the compilation (in other words, the list does not include any files included from within the false branches of #if or other conditional compilation directives). In the generated list, the files are listed one file per line, and each line specifying a file consists of the characters "#include", followed by a single space, followed by the name of the included file, followed by a newline sequence. The name of the included file isn't enclosed in any quote marks or other delimiters. This format is intended to simplify automatic processing of the list by clearly marking each line in the output that names an included file. Note that the list of included files might include a given file more than once, because each file is listed as it's included, and it is possible to include a given file more than once.
  • When the compiler is run with the "-P" option (which writes the preprocessed version of the source to standard output), the compiler now prefixes the generated source with the directive #charset "utf8" to indicate the character set of the preprocessed source.
  • Added a compiler warning message when the preprocessor finds a line that ends with a backslash followed by whitespace. While this format is legal (because it could be useful in defining a macro that is later "stringized"), it is highly unusual. Since trailing spaces are not usually visible in a text editor, the programmer might not be aware of their presence; and since the presence of the trailing spaces materially changes the meaning of the program, the compiler warns about them. Note that the warning can be suppressed by adding a comment at the end of the line; this doesn't change the meaning of the program but does make the spaces visually evident in a text editor, so the compiler recognizes that the warning isn't necessary in this case.
  • Fixed a VM problem that caused the garbage collector to incorrectly delete intrinsic class objects under certain unusual circumstances. When an intrinsic class is used implicitly but never specifically declared in the compiled program, the VM creates an object to represent the intrinsic class during the initial program load. In the past, it was possible for the garbage collector to regard such an object as unreachable even though it is always reachable via the native code implementing its corresponding intrinsic class. This problem sometimes led to VM crashes. The problem should no longer occur.
  • Fixed a compiler problem that caused incorrect compilation for a grammar rule specified with an alternation (two or more sub-rules, each separated by an "|" symbol) and an empty final sub-rule. In such cases, the compiler incorrectly generated two copies of the empty sub-rule, leading to two match objects being created at run-time each time the empty sub-rule was matched. This has been corrected.
  • Changed inherited so that it searches for the next inherited definition of the property as though the currently active method (and any overriding definitions) had simply never been defined. This makes inherited work roughly the same way as the inheritNext method that was formerly defined in the MixIn class in the adventure game library (adv3).
  • Added a new Object method, propInherited, which determines if a method is inherited, using the new rules of the inherited operator. This provides functionality equivalent to the canInheritNext method formerly defined in the MixIn class in the adventure game library.

Changes for 01/13/2002

  • The compiler can now read "library" files, which are lists of source files to be included in the compilation. Library files can be used to simplify compiler command lines and makefiles when including sets of source files that usually act as a group, such as the standard adventure library sources.
  • Workbench can now show library files in the project window. You can add a library file to the project window's source file list in the same way as you can add a source file; the library shows its contained source files as a subtree. Each source file within a library is shown with a checkbox, indicating whether or not the file is included in compilations; if you want to exclude a file within a library from compilation, click on the checkbox to remove the checkmark. The checkbox settings are stored with the project and don't affect the library itself, so you can use the same library in a different project, and include and exclude different sets of files.
  • The debugger no longer uses a RuntimeError exception to terminate the program being debugged. In the past, when the user terminated the running program using the debugger "Stop" button, or by starting a new compilation of the program while the program was still running, the debugger attempted to terminate the program by throwing a RuntimeError with system error code 2390. This approach wasn't completely reliable, because it allowed the running program to catch the exception and keep running; programs sometimes inadvertantly caught the error as part of an inclusive "catch" handler, causing strange interactions with the debugger user interface. The debugger now halts the program by telling the VM not to execute any more program instructions, which guarantees that the program stops immediately. This new approach should be much more robust and should avoid the strange effects that sometimes occurred with the old mechanism.
  • Fixed a problem in the compiler that caused the compiler to hang when given a makefile (a .t3m file) that didn't end with a newline character.
  • Fixed a compiler problem that generated an incorrect set of grammar rule patterns for grammars involving nested alternatives (i.e., parenthesized "|" expressions). In the past, the compiler incorrectly generated multiple copies of the top-level expressions in such rules, leading to run-time generation of multiple redundant matches for these expressions. This has been corrected; the compiler now generates only one copy of each top-level rule in an alternation.
  • Fixed an interpreter problem that caused the interpreter to crash when an HTML <TITLE> tag was displayed.
  • Fixed a problem in the debugger that caused memory leaks and other incorrect behavior when the user attempted to run the program, but the program had not been compiled (or had not been compiled with debugging information).
  • When Workbench needs to find a source file during program tracing in the debugger, it now scans the project list to find the file. In the past, the debugger used only the source path to find files, leading to annoying interactive requests to locate files that were in plain view in the project window.

Changes for 01/05/2002

  • Added the new intrinsic functions inputLineTimeout(), which allows reading a line of text with an optional timeout. This function can be used to combine command-line input with real-time effects, because it allows a command line to be interrupted if the command isn't completed before a real-time interval elapses.
  • Fixed a problem in the Dictionary class: the isWordDefinedTrunc() method used the wrong truncation length in its comparison - it required the string to be matched to be at least one character longer than the dictionary's truncation length. It now uses the correct truncation length.

Changes for 12/30/2001

  • The compiler now parses a delegated expression so that the object expression is taken to exclude function or method calls. That is, if parentheses follow the expression giving the target object of the delegation, these are taken to give the argument list of the delegation call itself, rather than of the expression giving the target object value. This change makes delegated syntactically parallel to inherited in that the target property can be omitted from the expression, in which case the delegation target property is the same as the property being evaluated in the caller. (If a function or method call is desired in the object expression itself, this can be easily accomplished by enclosing the entire object expression in parentheses.)
  • Workbench now stores its project configuration files so that the absolute file system path to "system" files is not stored; instead, system files are flagged as system files, and their locations are stored relative to the system directory. A "system" file is simply a file that is stored in the Workbench install directory or any of its subdirectories; the system directory is the directory containing the Workbench program executable. This change makes project configurations more readily moveable from one system to another, because it eliminates any dependency in the project configuration on the particular directory in which Workbench is installed.
  • The compiler now treats the symbol and object path options as overriding all module source paths, even when the filenames are given with "absolute" (as opposed to relative) path names.

Changes for 12/23/2001

  • Added a checksum to the saved game format. This allows the interpreter to validate a saved state file, to ensure the file wasn't corrupted (such as by a text-mode ftp transfer), before attempting to load the file.
  • Fixed a problem in the Vector intrinsic class that prevented two equivalent vectors from comparing equal.
  • Fixed a compiler problem that caused the compiler to crash in some cases when a symbol was redefined from function to another type.
  • Fixed a compiler problem that caused incorrect initialization of Dictionary objects in the image file in certain cases when preinitialization ran (i.e., when compiling with the -d option).

Changes for 12/09/2001

  • The compiler now accepts URL-style portable path names in #include directives, and in fact prefers them. Refer to the preprocessor chapter for details.
  • Added a new method, createClone, to the TadsObject intrinsic class. This method returns a newly-created object that is an identical copy of the original object.

Changes for 12/02/2001

  • Changed the tokenizer interface (in tok.t) to return a list of token elements, rather than parallel value/type lists. Each token element in the token list is a sublist, consisting of the following values:
    • [1] = token value, as set by the matched rule
    • [2] = token type enum, as set by the matched rule
    • [3] = original source text matched by the rule that matched the token
    The tok.h header defines macros that extract these elements of a token; these should be used rather than direct list elements to make your code more readable. The macros are getTokVal(tok), getTokType(tok), and getTokOrig(tok). So, if lst is a variable containing the result of calling Tokenizer.tokenize(), and you want to retrieve the type enum for the third token, you'd write getTokType(lst[3]).
  • Changed the tokenizer's rule specifications for greater power and symmetry in creating rules:
    • The "flags" entry has been eliminated. The functionality of the only flag that was defined, TOKFLAG_SKIP, has been replaced with the improved behavior of the rule processor. Since the processor method's interface is now powerful enough to subsume the functionality of the "skip" flag, the special case embodied in the flag is no longer needed.
    • The processor method (element [3] in the sublist defining a rule) has a new interface. In the past, this method took a text match and returned a token value. This interface meant that a given rule was limited to generating exactly one token. The new interface takes three arguments, and returns no value:

          self.(processorProp)(txt, typ, toks)
        

      txt is the text that matched the rule (this is the same as the lone argument to the old interface). typ is the token type from the rule (i.e., element [2] of the sublist defining the rule). toks is a Vector containing the token list under construction. The method must append the appropriate set of tokens to the Vector in toks. The method is not required to append any tokens; the rule in the default tokenizer rule set that matches whitespace, for example, doesn't generate any tokens, since whitespace characters have no significance other than separating tokens in the default rule set. The rule is also allowed to generate more than one token; this is useful for rules that generate certain tokens types only if they immediately follow particular text patterns. Note that the processor method is not required to use the typ value given, since each new token the method adds to the result Vector can be of whatever type is appropriate; the type information is included so that a single processor method can be re-used for similar types of processing that produce different token types.

  • Changed the intrinsic GrammarProd.parseTokens() method to accept tokens in the new format returned by the tokenizer. The method now takes only two arguments, not three: the second argument that formerly gave the token type list has been eliminated, since each element of the token list now includes the type information along with the token value information.
  • Changed the Dictionary and GrammarProd intrinsic classes to use case-sensitive matching exclusively. Case sensitivity in the past was inconsistent; all functions are now case-sensitive for consistency.
  • The template inheritance mechanism is somewhat altered from its first implementation. The 'inherited' keyword can now go anywhere a template; the inherited token lists are treated as substitutions for 'inherited' at the point at which it appears in the list. Furthermore, the compiler now treats each template involving inheritance as a macro defining all possible inherited templates; when a template is used, it is matched based on the entire expansion of the template, not on the partial superclass template. The net effect is that a template definition involving an 'inherited' token is identical to the implied set of fully expanded template definitions.
  • The -Fs option can now be used to specify a search path for source files, rather than a single path for all sources as it did in the past. Multiple -Fs options can be specified; each one adds one directory to the source search path. When a source file name is specified in "relative" form (as defined by local filename conventions), the compiler first looks for the file in the working directory, then searches the directories specified with -Fs options, in the order in which the options were specified. The compiler uses the first instance of the file it finds.
  • The compiler automatically adds the system-dependent default system library directory to the source search path. This directory is always searched after all directories specified by the user with -Fs options. In effect, the compiler adds an extra -Fs option for the default system library directory after all user-specified -Fs options. This allows library sources (such as reflect.t or gameinfo.t) to be included in a build configuration (in a .t3m file, for example) without any path information; the compiler will automatically find these system files using the implied -Fs setting.
  • The compiler's default system library and include directories are now system-dependent. On DOS and Windows systems, the default system header and library directories are the same as the compiler executable's directory (this is the main directory where the TADS 3 tools are installed). Check your platform-specific release notes for details on the locations of these directories on other platforms.
  • Fixed a problem that allowed objects to be deleted by the garbage collector when they were referenced as superclasses of other objects. This caused unpredictable behavior, including interpreter crashes.
  • Fixed a problem that affected the results of t3GetStackTrace() in the non-debug interpreter when a run-time error occurred. In such cases, the stack trace incorrectly omitted the innermost level, where the error actually occurred. This has been corrected.
  • Fixed a compiler problem that sometimes caused the compiler to crash after attempting a link that involved unresolved external symbol references.
  • Fixed a GrammarProd parsing problem that caused incorrect token index information to be returned in certain cases involving productions with no tokens.

Changes for 11/17/2001

  • Extended the object template syntax to allow template inheritance. A template definition can now include the keyword "inherited" as its final token; this indicates that the template "inherits" the templates of its class's superclasses.
  • The compiler now detects and reports circular class definitions (that is, class definitions where the class being defined is a subclass of one or more its purported superclasses). In the past, the compiler did not detect such definitions and could get stuck in infinite loops or unbounded recursion when they were made.
  • Fixed a compiler problem that sometimes caused crashes when an actual parameter list in a macro expansion was not properly terminated (with a closing parenthesis) in the source file.
  • Fixed a problem that affected a few Vector methods by incorrectly returning an empty vector. This affected the appendUnique, getUnique, and mapAll methods.
  • Fixed a compiler problem that caused a modified intrinsic class to show a spurious extra property in its getPropList() result list. This property had an undefined type (type code 16).
  • Fixed a problem in the Vector class that caused unpredictable results, including crashes, when adding a large list to a vector.
  • Fixed a problem in the debugger that caused unpredictable results, including crashes, when interrupting the program in mid-statement in certain circumstances. The problem occurred sporadically in such cases when the garbage collector happened to run while the debugger had control, but didn't appear until after returning control to the program. This problem only manifested itself rarely, but could cause crashes when it did appear.
  • Fixed a compiler problem that caused incorrect code to be generated for calls to operator new where the actual parameters included one or more "..." expressions (i.e., expanded variable argument lists). This affected the calling end, not the receiving end. The problem caused the caller to send one fewer argument than it should have.
  • Fixed a problem in the output formatter that caused log files to be generated without proper line wrapping in the HTML version of the interpreter.
  • Changed the output formatter so that <BANNER> contents (everything from a <BANNER> tag to the corresponding </BANNER> close tag) are not included in the output log. The contents of a banner are not logically part of the main text stream so are not appropriate for inclusion in a log file. This ensures that HTML-based status lines and other special display effects do not interfere with the log file contents.

Changes for 10/21/2001

  • Fixed a problem that made it impossible to use anonymous functions that referenced "self" or properties of "self" within property values defined as simple value expressions (as opposed to methods).
  • Changed the compiler to allow using short-form anonymous functions (i.e., using the "{args: }" notation, rather than the "new function" notation) as list elements without enclosing the functions in parentheses. In the past, the compiler considered an open brace at the start of a list element expression as an error; this was an heuristic intended to catch unterminated list expressions, but its utility as a diagnostic tool was not sufficient to justify the superfluous syntax requirement it imposed for this case.

Changes for 10/07/2001

  • Added special character sequences '\{' and '\}'. These are used to group a set of output formatter setting changes: any changes to the formatter settings made after a '\{' sequence are undone at the corresponding '\}' sequence. In particular, the following state changes are affected:
    • \H+
    • \H-
    • \H
    These new sequences can be used to isolate a run of text from the enclosing text's formatter settings, so that the text within the '\{ \}' pair doesn't have to worry about the enclosing settings and has no effect on the enclosing settings. This can be useful, for example, if a run of text wants to output an HTML-significant character, but doesn't know if it will be called with HTML interpretation turned on or off. In such cases, the text can turn HTML interpretation off within the brackets without affecting its caller: "\{\H-&\}" would display an ampersand, regardless of whether HTML mode was in effect before the text was displayed, but will have no effect on subsequent text's HTML mode.
  • Fixed problems in several intrinsic classes related to saving and restoring. These bugs caused crashes in some cases when restoring saved positions.
  • Changes the restartGame() behavior slightly. The function invoked after the restart is now passed the original command line arguments to the program; the arguments are passed as a list in the second parameter to the function. This allows the function to invoke the normal main program entrypoint as though the program were being started normally.
  • If a saved position file is found to be corrupted in the course of being restored, the interpreter will now force an exit. There is no way for the program to retain control in such cases. If a saved position file is corrupted, the partially restored machine state at the point at which the corruption is detected must be considered invalid, so the VM cannot safely pass control back to the program. In such cases, the VM terminates the program to avoid the high likelihood that the program would crash the VM due to the program's partially corrupted internal state.
  • Fixed some problems in the "undo" mechanism that caused unpredictable behavior (including VM crashes) under some circumstances.
  • Fixed a problem in the debugger that caused, in some cases, the incorrect error code to be reported in the RuntimeError object representing a VM exception. This only occurred when the program was being run in the debugger, and only then when certain types of expressions were evaluated. This has been corrected.

Changes for 09/30/2001

  • Changed the symbol file format to allow inclusion of intrinsic function and intrinsic class registration lists. This is an internal change only; its purpose is to make the compiler more flexible in correlating the internal linkage identifiers of intrinsics across object modules to eliminate the possibility of certain confusing linking errors. In the past, it was necessary to keep intrinsic definitions in a consistent order across separately compiled modules, which was problematic for library developers and especially for library extension developers. Adding the information to the symbol files allows the compiler to build an intrinsic identifier list globally across modules to ensure a consistent ordering, even when the individual modules have conflicting orderings. This change should not be visible to users except to the extent that it eliminates certain errors relating to intrinsic class and intrinsic function inclusion order.

Changes for 09/09/2001

  • Added a new function to the "tads-io" function set: flushOutput(), which immediately flushes text output to the display and updates the window, if possible.
  • Fixed a compiler bug that caused incorrect code to be generated for implicit constructors for classes with multiple superclasses. This code generation error did not manifest itself frequently, but when it did show up caused problems ranging from data value corruption to interpreter crashes.
  • The debugger now includes an ampersand prefix when displaying property pointer values.

Changes for 08/26/2001

  • Added new syntax that provides a short-hand notation for defining a group of properties with similar names and parameters: the new "propertyset" syntax.
  • Added a new keyword, "targetprop", a pseudo-variable that evaluates to the current target property pointer. The target property is the property that was invoked to reach the current method; this new pseudo-variable complements "self" with information on the property being evaluated in the current "self" object. The "targetprop" keyword can only be used in contexts where "self" is valid.
  • The "inherited" and "delegated" keywords can be used with new syntax that omits the target property. If the target property is omitted, then the current target property is implied. The target property can be omitted from any of the previously-allowed forms of the "inherited" and "delegated" expressions, as shown here ("delegated" syntax is parallel to "inherited", so only "inherited" is shown):
    • inherited is equivalent to inherited.targetprop
    • inherited(arguments) is equivalent to inherited.targetprop(arguments)
    • inherited superclass_name is equivalent to inherited superclass_name.targetprop
    • inherited superclass_name(arguments) is equivalent to inherited superclass_name.targetprop(arguments)
  • Added a new library module, reflect.t, that provides higher-level reflection services. This module is optional, but if it is included, the _main.t module uses reflection to show stack trace listings for unhandled runtime exceptions.
  • Added t3GetStackTrace() to the 't3vm' intrinsic function set. This function returns information on the call stack.
  • The RuntimeException object defined in _main.t now stores a stack trace (in the form returned by t3GetStackTrace()) in its stack_ property. The VM no longer stores an explicit stringized form of the stack trace when setting the exception message, since the new stack trace format is more powerful.
  • Fixed a bug in the TadsObject intrinsic class that sometimes caused a given property to appear more than once in the getPropList() method's returned list. Any given property now appears at most once in the result list.
  • The TadsObject intrinsic class now supports createInstance() as a static method on the intrinsic class. The expression TadsObject.createInstance() returns a new instance of TadsObject, which is the same as defining an object statically like so: "myobj: object;" - that is, using the "object" keyword rather than a superclass list.
  • Fixed a bug in the getPropList() method when called on any intrinsic class object (e.g., Object, TadsObject, BigNumber). In the past, this method returned a list of all of the methods that were valid on instances of the intrinsic class, not the methods that were necessarily valid on the intrinsic class object itself. This method now returns a list consisting only of the "static" methods of the intrinsic class, which is to say the methods that can be called directly on the intrinsic class object itself.
  • Fixed a bug in the Object intrinsic class's propType() method. When the property in question was undefined, the method returned TYPE_NIL, whereas it should have returned nil. The method now returns nil for these cases, as documented.
  • Fixed a bug in the compiler that caused incorrect code generation when a variable argument list parameter variable was referenced from within an anonymous function within the function or method with the variable argumetn list parameter.
  • The compiler now specifially flags code using '=' to define a method. Since this was valid TADS 2 syntax, and has been gratuitously changed in TADS 3, users are likely to use the old syntax accidentally, especially while transitioning to the new language. The compiler catches these cases so that it can generate specific and clear error messages; this should help ease the transition to the new syntax by spelling out plainly the new syntax requirement.

Changes for 08/12/2001

  • Fixed a compiler problem that caused incorrect code to be generated for expressions of the form obj.(prop)(args), where prop is a property of self whose value is a property pointer. An expressions of this form is equivalent to the form obj.(self.prop)(args), but the compiler in the past incorrectly interpreted this as equivalent to obj.prop(args) - that is, rather than evaluating (self.prop) and then calling the property of obj given by the resulting property pointer value with the given argument list args, the compiler generated code that called the property prop of obj with arguments args. This has been corrected - the compiler now correctly interprets obj.(prop)(args) as equivalent to obj.(self.prop)(args).
  • Fixed a compiler problem that caused incorrect code to be generated for delegated expressions involving variable argument lists.
  • Fixed a compiler problem that caused a spurious warning under certain circumstances. If a property was used only in an address expression ("&prop") in a module, and no object ever defined a value for the property, a call to the property in a separate module incorrectly generated a warning indicating a call to a possibly undefined property name. This warning was spurious because the use of the property name in an address expression is enough to definitively establish the symbol as a property.
  • Fixed an output formatter problem that caused the text-mode interpreter to hang (in an infinite loop internally) in response to certain "&" entities in HTML mode.
  • Fixed a problem in the GrammarProd intrinsic class that caused productions that matched zero tokens to indicate invalid token index values for firstTokenIndex and lastTokenIndex. These now always indicate zero in such case, to indicate that the production is associated with no tokens.

Changes for 08/05/2001

  • Added file safety level checking to the File intrinsic class. The initial implementation of File did not check the safety level set in the user preferences; the File class now correctly checks to ensure each open-file operation is allowed by the user's safety level settings. On a safety level violation, the "open" functions throw the new exception FileSafetyException.
  • Text files can now be opened in FileAccessReadWriteKeep and FileAccessReadWriteTrunc modes. In the past, only "data" and "raw" files could be opened in read/write modes, which made it impossible to append to an existing text file.

Changes for 07/22/2001

  • Fixed a problem with using 'modify' with intrinsic classes. In the past, if more than one 'modify' statement was applied to an intrinsic class, only the last of the 'modify' statements was actually taken into account at run-time. This has been corrected.
  • Fixed a compiler bug: the compiler did not report an error if a 'replace' statement replaced an object but did not specify a superclass list for the new object. The compiler misinterpreted such a statement as a new anonymous object definition. This has been corrected; the compiler now flags the missing superclass list as an error.

Changes for 07/16/2001

  • Fixed a problem in the List class that could cause an object to be improperly deleted by the garbage collector under certain obscure circumstances. (In particular, if an object was being assigned to a list element, and the garbage collector ran in the course of the creation of the new list for the result of the assignment, the object on the right-hand side of the assignment was lost if not otherwise referenced.)
  • Fixed a problem that occurred with propDefined(prop, PROPDEF_GET_CLASS) when the property of interest was an intrinsic method (i.e., a TYPE_NATIVE_CODE value). In these cases, the return value of the method was an invalid value, which caused problems if used, including interpreter crashes. In such cases, propDefined() now properly returns the intrinsic class object where the intrinsic method is defined.
  • Extended the object template mechanism to allow class-specific templates to be defined.
  • Extended the firstObj() and nextObj() intrinsic functions to take a new flags argument, which allows the caller to specify whether to enumerate instances only, classes only, or both.
  • Changed the TadsObject class to allow use of the new operator with no arguments. In the past, at least one argument (giving the immediate superclass of the new object) was required; now a new TadsObject with no arguments is understood to create a base TadsObject.
  • Added the File intrinsic class, which provides a new interface to external file input/output. This replaces the file-related functions in the "tads-io" intrinsic function set, which have been removed. Specifically, the following functions have been removed:
    • fileClose
    • fileGetPos
    • fileOpen
    • fileRead
    • fileSeek
    • fileSeekEnd
    • fileWrite
    Important note: This change is not upwardly compatible. All existing code must be recompiled for this change, and any code using the fileXxx functions must be modified to use the new File intrinsic class. The necessary code changes are relatively straightforward, since the names of the new File methods are similar to those of the old functions.
  • The compiler now generates an implicit constructor for each object or class that inherits from multiple base classes and does not provide an explicit constructor. The implicit constructor simply invokes each base class's constructor, in the same order in which the base classes are listed in the superclass list.
  • Added grouped sub-alternatives to rules in the grammar statement. This allows portions of an alternative in a rule definition to be grouped for alternation; for example, a rule can be defined as "('take' | 'get' | 'pick' 'up') nounList->lst_", which is more concise than flattening out the grouped alternation manually.
  • Added the #ifempty and #ifnempty variable-arguments expansion operators, which allow conditional expansion in a varargs macro depending on whether or not the varargs part is empty.
  • Added the -P option to t3make: this preprocesses the source file, writing the expanded results on standard output. When -P is specified, no compilation or linking is performed, so no object or image files are created.
  • Added new String intrinsic class methods: startsWith(), endsWith(), and mapToByteArray.
  • Added a feature to the substr() method of the String intrinsic class: if the starting index (the first argument) is negative, it indicates an offset from the end of the string: -1 is the last character of the string, -2 the second-to-last, and so on.
  • Added a new function to the "tads-io" function set: getLocalCharSet, which returns the name of one of the active local character sets on the current system.
  • Added new functionality to the "tads-io" function set for files. Files can now be opened in "raw" mode, which allows reading and writing byte arrays. Bytes written to and read from a file in raw mode are not translated or modified in any way; this mode offers direct access to external files, for purposes such as manipulating files in formats defined by other applications. The fileRead() and fileWrite() functions in this mode take ByteArray arguments.
  • Added new functionality to the "tads-io" function set: the fileOpen() routine can now take an object of intrinsic class CharacterSet instead of a character set name. If you've already instantiated a CharacterSet object for other reasons, it is more efficient to re-use the same object by reference in this manner than to pass the name of the character set to fileOpen(), since fileOpen() has to create another mapping object when given only a character set name.
  • Added the new CharacterSet intrinsic class.
  • Added the new ByteArray intrinsic class.
  • In the Object intrinsic class, method formerly known as isClass() has been renamed to ofKind(). x.ofKind(y) returns true if x is an instance or subclass or y. In addition, this method has been changed so that x.ofKind(x) returns true for any x.
  • Added a built-in character set mapping for ISO 8859-1 (Latin-1). This character set is in widespread use because of its status as the default HTTP character set, and has an especially simple mapping to Unicode, so it seemed worthwhile to add as a built-in set not requiring an external mapping file. The assigned name of the set is "iso-8859-1".
  • Changed the compiler's former -r option to -a, and the former -rl to -al. (The -a option forces recompilation of all involved modules even when not out of date with respect to the source files, and -al forces relinking even when the image file isn't out of date with respect to the object files; these options effectively override the compiler's normal dependency analysis for cases when the automatic analysis is incorrect for some reason.) This change is gratuitous and cosmetic, but makes the compiler's option names more closely resemble that of traditional "make" programs.
  • Fixed a compiler bug: the compiler reported a spurious "symbol already defined" warning when a particular grammar production had rules defined in multiple modules. This has been corrected.
  • Fixed a preprocessor bug: if the argument of a #message was missing its closing right parenthesis, the preprocessor got stuck in an infinite loop. This has been corrected.
  • Fixed a bug in the Vector intrinsic class (and, by inheritance, in the Array intrinsic class): if the insertAt method was called on an empty vector, a crash occurred. This has been corrected.
  • Fixed a compiler bug that caused the compiler to crash if confronted with a foreach statement in the absence of the definition of the Collection and Iterator classes. This has been corrected; the compiler now generates the appropriate error message and recovers gracefully.
  • Fixed a problem with the grammar production object that caused new match tree objects to bypass constructors on creation. The match tree constructors are now invoked as for any other object instantiation.
  • Fixed a problem in the debugger that prevented the debugger from taking control on a run-time exception being thrown when another run-time exception had previously occurred at the same place twice in a row with no intervening debugger activity.
  • Fixed a problem that caused the debugger to crash under some unusual conditions when an entry in the local-variables window was selected and later automatically removed due to an execution context change.
  • Improved the robustness of the debugger when the program being debugged caused a stack-overflow exception.
  • Updated the calc.t example to the new GrammarProd.parseTokens() interface.
  • Fixed a bug that caused comparisons of any two function pointers to yield unequal, even for equivalent function pointers. This bug had numerous subtle manifestations; for example, a function pointer key in a LookupTable could never be found, since a test for equality to the key would never succeed.
  • Fixed a compiler bug that caused the incorrect error message to be reported when a replace/modify of an extern object was out of order in a link (the message reported that a function rather than object was involved).
  • Removed a spurious warning that was generated when a dictionary was imported from multiple symbol files.
  • Fixed a bug in the GrammarProd intrinsic class that caused "*" tokens in subproductions to be misinterpreted. In the past, the "*" token only worked properly in top-level productions (i.e., a production on which parseTokens() was called); this has been corrected so that "*" works properly at any level.

Changes for 05/05/2001

  • Incompatible intrinsic class API change: The return value of the parseTokens() method in the GrammarProd intrinsic class has been changed. In the past, this method returned a list, each of whose elements was a sublist of two elements. Each sublist described one successful match tree; the first element of each sublist was the number of tokens matched, and the second was the root object of the match tree. The 4/29/2001 version added the firstTokenIndex and lastTokenIndex properties to the match objects; since the number of tokens matched in a tree can be inferred from the firstTokenIndex and lastTokenIndex values, the token counts in the return sublists were redundant. Therefore, to simplify the API, the sublists have been eliminated, and parseTokens() now returns simply a list of match tree root objects. To obtain the token count for a match, simply calculate (match.lastTokenIndex - match.firstTokenIndex - 1), where match is the root object in the match tree. Since the root object in a tree encompasses the entire match, its token range necessarily encompasses the range of tokens for the entire match.
  • Added variable-argument macros.
  • Fixed a problem in the GrammarProd intrinsic class that prevented a rule from being matched when it ended with the special '*' token and was used as a subproduction in another rule. This now works correctly.
  • Fixed a problem in the compiler that caused a spurious warning message indicating that a dictionary symbol had been imported multiple times. This warning was generated whenever the dictionary was declared in more than one source file but not in all source files; each file that did not declare the dictionary displayed the warning. This has been corrected.
  • Added the delegated keyword, which allows delegating a method call to an unrelated object.
  • Added the getMethodDefiner() intrinsic function, which returns the object that defines the currently executing method.
  • Changed the default name of the ASCII character set mapping to "us-ascii" for better readability. The old name ("asc7dflt") is still accepted, but is now obsolescent and should not be used in new code. All of the #charset directives in the system headers provided with the compiler have been updated to use the new "us-ascii" name.

Changes for 04/29/2001

  • Fixed a problem in the GrammarProd intrinsic class that caused incorrect results to be returned with productions that matched exactly zero tokens.
  • Extended the grammar syntax to allow the "->" notation to be used with literal tokens. In the past, the arrow was only allowed with symbol-match tokens (subproductions and token classes), but it is often desirable to be able to retrieve the matching token even for a literal match. There are two cases in particular where a literal's matching token is needed to reconstruct the original input: when the literal is matched with truncation; and when a production has several alternatives with different literals.
  • Modified the GrammarProd intrinsic class so that each match object now includes more direct information on the tokens involved in the match. The parser now sets the firstTokenIndex and lastTokenIndex properties of each match object to indicate the index of the first token and of the last token, respectively, that are involved in matching the rule corresponding to the match object. This is a simple range, so all of the tokens from the first to the last, inclusive, constitute the match's original text. In addition, the parser sets the tokenList property of each match tree item to the original token list passed into parseTokens() (this doesn't take up nearly as much memory as it might at first appear, since all of the match objects reference a single shared copy of the token list). The exported properties firstTokenIndex, lastTokenIndex, and tokenList are defined in gramprod.h.
  • The compiler now creates a dictionary entry in the default dictionary, if one is defined, for each literal string that appears in a "grammar" statement's rule list. If no default dictionary is defined when a "grammar" statement is encountered, the statement's literals are not entered into any dictionary. Each literal string is associated with the production object and the property "miscVocab". Since these strings are now stored in the dictionary, it is simple to determine if a word is defined in any context; without the grammar literal dictionary entries, it was not possible to determine if a given string, when entered by a user at run-time, was known in the context of a grammar literal.
  • The "grammar" syntax has been extended to allow a "tag" to be associated with each "grammar" statement. To specify a tag, specify any symbol or number in parentheses after the production name. For example:

        grammar nounPhrase(1): adjective->adj_ : object ;
    

    The tag's only purpose is to provide an identifying name for the new "grammarInfo" method, described below.

  • The compiler now automatically creates a method for each grammar match object that makes it possible to traverse match trees without knowing their internal structure in advance. The new method is called "grammarInfo," and it returns a list. The list's first element is a string giving the name of the production, with the optional tag (see above) enclosed in parentheses after the production's symbolic name. Each subsequent element is the value of a property appearing after an arrow ("->") in the production's rule list.
  • Modified the grammar production matching algorithm so that [badness] matches are examined as a group rather than individually. In particular, when a number of [badness] alternatives arise simultaneously, the parser had in the past examined only one such alternative at a time once determining that no better matches were available. Now, all [badness] alternatives with the lowest badness value are examined in parallel. This is important in some cases for the same reasons that parallel consideration of regular alternatives is necessary.
  • Fixed a compiler problem that caused spurious "variable assigned but not used" warnings in certain situations. In particular, if a local variable was defined in a nested scope within a method, and the variable was used only within the lexical confines of an anonymous function within the nested scope, the compiler did not correctly mark the variable as having been used and thus incorrectly reported the warning. This problem did not affect code generation; its only effect was the unnecessary warning.
  • Enhanced the Dictionary intrinsic class's findWord() and findWordTrunc() methods so that the property argument in each can be omitted or specified as nil, in which case all objects with vocabulary matching given text, for any part-of-speech property, will be returned.
  • Fixed a debugger problem that caused a crash in some obscure cases involving opening a file during a debugger session when the debugger had never taken control since the beginning of program execution, and a similar problem that occurred when the VM threw a run-time error exception under similar circumstances.

Changes for 04/15/2001

  • A new mechanism allows capturing calls to undefined methods and properties. When a call is made to an undefined method, the VM now calls a new property, propNotDefined(), on the target object. The new method receives as arguments the the undefined property ID and the arguments to the original method call. If propNotDefined is itself not defined, the VM simply returns nil for the undefined method call, as it did in the past.
  • Added some new regular expression features:
    • Character classes can now be combined with '|', as in <upper|digit> (matches any upper-case letter or any digit).
    • Character classes can now be complemented with '^', as in <^upper> (matches anything except upper-case letters).
    • Added some named character expressions, which look similar to character-class expressions, and which can be combined with character-class expressions via '|': <upper|star> matches any upper-case letter or an asterisk.
  • Renamed the t23run.exe executable to simply tr32.exe, so the combined TADS 2+3 interpreter is now the default interpreter. The separate interpreters are now called t2r32.exe (for TADS 2) and t3run.exe (for TADS 3). For the most part, users will not need to run the version-specific interpreters at all; the only time these should be needed is when bundling a game into an executable, in which case it is desirable for the sake of minimizing file sizes to include only the version of the interpreter actually needed.

Changes for 3/28/2001

  • The object definition syntax has been extended to allow an object's property list to be enclosed in braces. Refer to the Object Definitions documentation for details.
  • The object definition syntax now provides "nested" objects. A nested object is an anonymous object defined in-line within another object, with a property of the enclosing object initialized with a reference to the nested object; this syntax is convenient for defining certain types of small helper objects.
  • The language now has a static property initialization syntax, which allows a property to be initialized with a non-constant value computed at compile-time.
  • Changed the behavior of the '+' property. The new behavior is much simpler than the previous behavior: if an object is defined with N plus signs, its '+' property is initialized to the most recent object defined with N-1 plus signs. Explicitly setting the '+' property in an object list is now irrelevant. Refer to the documentation for details.
  • Changed the default filename suffix for image files to ".t3" (it was formerly ".t3x") to make image filenames somewhat more friendly looking.
  • Added the new executable "t23run" - this is a 32-bit console-mode combined TADS 2 and TADS 3 interpreter. You can use this single application to run games compiled for TADS 2 or TADS 3. This version of the interpreter is not designed for bundling single-file games for distribution, since this would unnecessarily increase the size of bundled games; use the appropriate single-version interpreter executable for bundling.
  • Added the new executable "html23" - this is a combined TADS 2 and TADS 3 version of the HTML interpreter.
  • For anyone interested in porting the combined command-line interpreter on another platform, the code that implements this should be easily portable to other command-line operating systems. See the target 't23run.exe' in win32/makefile.vc5; you can find the source code for the main program entrypoint in vmcl23.cpp. For systems that don't use Unix-style command lines, the command line code itself won't be portable, but portable code for sensing the engine version needed to run a given game file can be found in vmmain.cpp.
  • Fixed a bug that caused problems with certain "reflection" methods when the program used modify to extend the TadsObject intrinsic class. If the program modified TadsObject, then traversed the object tree (using the getSuperclassList() method), or used the propDefined() method, the behavior was incorrect, and could lead to VM crashes. This has been corrected.
  • Added documentation on the #charset directive and using source files encoded in Unicode UCS-2.
  • Added a #charset "asc7dflt" directive to each system source and header file included with the compiler distribution. This directive ensures that the compiler interprets the contents of the system files correctly, in case the user wants to specify a default character set for the compilation with the -cs compiler option.
  • The compiler now accepts Unicode character 0x2028 ("line separator") as equivalent to a newline in source files that use of the Unicode encodings the compiler accepts (UCS-2 big-endian, UCS-2 little-endian, or UTF-8).
  • In text mode, the fileRead() function now properly translates input characters according to the character set selected when the file was opened. (In the past, the character set was ignored, and the characters from the file were not properly translated to Unicode.) In addition, the fileRead() function accepts Unicode character 0x2028 (the Unicode line separator character) as equivalent to a newline (but note that, as always, the text returned from readLine() represents each end-of-line with a single '\n' character, regardless of the type of line termination in the underlying file).

Changes for February 24, 2001

  • Added the t3AllocProp() intrinsic function, which allocates a new property ID.
  • Added the Object.getPropList() method, which returns a list of properties directly defined by an object.
  • Added the Object.getPropParams(prop) method, which returns information on the parameters taken by a property or method.
  • Added the getFuncParams(funcptr) intrinsic function, which returns information ont he parameters taken by a function.

Changes for February 10, 2001

  • The new property statement allows a program to explicitly declare property symbols. This is optional, but can be useful in certain cases in library code.
  • Added the LookupTable intrinsic class, which implements a general-purpose associative array type. A LookupTable is similar to a Vector, but whereas a Vector can use only integers as indices, a Hashtable can use any value as an index.
  • Added the new t3GetGlobalSymbols() intrinsic function, which provides programmatic access to the compile-time names of objects, properties, functions, intrinsic classes, and enumerators.
  • The preprocessor now treats square brackets ("[ ]") and braces ("{ }") as grouping symbols when parsing macro arguments. In the past, only parentheses were counted. This allows list constants and anonymous functions to be used more easily as macro arguments.
  • In _main.t, added a new function, initAfterLoad(). The main program entrypoint in _main.t (function _main()) calls this new routine to perform initialization. This routine sets up the default display function, performs pre-initialization if necessary, then performs regular load-time initialization. All of these operations were previously carried out in _main(); the reason they've been separated into a new function is to allow the same operations to be called after a restartGame operation. Since a restartGame effectively re-loads the image file and starts the game over from scratch, the same initialization that _main() performs at initial load must be performed immediately after a restart; this function makes this common code easy to re-use.
  • Added a new method to the TadsObject intrinsic class: createInstance(), which creates an instance of the object. This method passes its arguments to the new object's constructor, if any, and returns a reference to the new object.
  • Added execAfterMe to ModuleExecObject in _main.t. User code can initialize this property in a given ModuleExecObject instance to contain a list of other ModuleExecObject instances that are to be initialized after the given instance; this is analogous to the execBeforeMe list, but allows an object to specify things that are to come after it. It is sometimes desirable to be able to specify initialization order dependencies by specifying what must follow rather than what must precede initialization of a given object, and this new property provides the flexibility to specify order dependencies either way.
  • A makefile for DJGPP (the definitive port of Gnu C/C++ for MS-DOS) is now included in the source distribution. This makefile can be used to create a 32-bit version of the interpreter that will run on MS-DOS on PC's with 386 or later processors.
  • Fixed a VM problem that caused modification of the root intrinsic object (i.e., modify Object) to fail. In the past, the VM simply failed to execute code added to the root intrinsic object with modify. This has been corrected; code added to Object is now properly inherited from the other intrinsic classes.
  • The String and List intrinsic classes can now be extended with the modify mechanism. In the past, the VM did not correctly handle code extensions to the String and List intrinsic classes.
  • Fixed a compiler bug that caused incorrect code generation when adding a constant zero value to a local variable which, at run-time, ends up containing a vector, list, or other non-integer value.
  • The Vector intrinsic class can now be used with the foreach keyword to loop over the elements in a vector. (In previous versions, the Vector class incorrectly omitted support for foreach.)
  • The "Frames" version of the documentation should now work correctly with Netscape and other browsers that had trouble with past versions. (The frames page had some ill-formed HTML, which some browsers rejected, but this page has now been corrected.)
  • Changed the text of the VM error message that results from applying an index to a type that cannot be indexed (nil[3], for example). In the past, the message read "invalid type for indexing - value cannot be index," which incorrectly signified that there was something wrong with the index value, when in fact the value being indexed was the problem. The message has been changed to read "invalid index operation - this type of value cannot be indexed."
  • Fixed a problem in the Vector intrinsic class that caused a vector's data to be corrupted by certain operations that should have, but did not, increase the internal storage space allocated for the Vector object.
  • Fixed a compiler problem with object templates. The compiler did not properly parse object templates that included list tokens; this has been corrected.
  • Fixed a bug in the rand() intrinsic function. In the past, this function crashed the interpreter if the argument was a list with no elements. This has been corrected; the function now returns nil when the argument is a list with no elements.
  • Fixed a problem with the Vector intrinsic class that caused pre-initialized data to be loaded incorrectly.
  • Corrected the documentation (the expressions chapter) to reflect the correct precedence of the "[]" and "." operators, which should be above the unary operators.
  • Added four new methods to the List intrinsic class: prepend(val), which inserts a new value at the start of a list; insertAt(index, val1, val2, ...), which inserts one or more values into a list at a given position; removeElementAt(index), which deletes the element at the given index; and removeRange(startIndex, endIndex), which removes the elements in the given range of indices.
  • Added a new method to the Vector intrinsic class: prepend(val), which inserts a new value at the start of a vector.
  • It is no longer legal to apply the unary "&" operator to an object symbol. In the past, "&objectName" was the same as simply "objectName"; this is no longer legal. "&objectName" was never a meaningful construct, since an object symbol yields a reference to the object to begin with, but past versions of the compiler accepted it without complaint and treated it as equivalent to the unadorned object symbol; the compiler now rejects this construct.
  • It is no longer legal to apply the unary "&" operator to a function name symbol. In the past, "&functionName" yielded a reference to the function. This has been changed so that the function name used by itself yields a reference to the function, just as an object name does.
  • Because the "&" operator can no longer be used with any symbol types other than properties, there is no longer a warning when using the operator with an otherwise undefined symbol name. "&symbol" now unambiguously defines the symbol as a property name.
  • Fixed a compiler bug that caused a spurious warning message when compiling certain types of expressions involving property pointers. The warning did not affect code generation, but it was incorrect and has been removed.

Changes for version 3.0.2 (September 27, 2000)

  • Fixed a compiler problem that caused the compiler to go into an infinite loop (and therefore freeze up) when confronted with a 'switch' statement containing code before the first 'case' or 'default' label. This has been corrected; the compiler now reports an error (such code is unreachable) and continues with the compilation.
  • Fixed a minor compiler problem that generated a spurious warning message in a certain situation. Specifically, if none of a 'switch' statement's 'case' labels led to code paths that could reach the next statement after the 'switch' (for example, if every code path reachable from a 'case' label led to a 'return' statement), but the 'switch' had no 'default' label, the compiler generated the warning message "unreachable statement" at the next executable statement after the 'switch'. This warning was incorrect because, in most cases, it is not safe to assume that every possible value of a switch's controlling expression is included in the list of explicit cases, hence the next statement following a 'switch' with no 'default' is reachable any time the controlling expression's value is not in the list of explicit 'case' labels. Note that this problem merely caused the spurious warning and did not affect code generation.

Changes for June 24, 2000

  • Fixed a compiler problem with try/catch statements. The compiler did not correctly generate handlers for any catch block past the first for a given try statement; this has been corrected.

Changes for version 3.0.1 (June 17, 2000)

  • The meaning of the syntax x.y and &y have changed slightly, in a way that will be transparent to most code. In the past, if y was a local variable, then x.y evaluated the local, then called the method on x specified by the property pointer value in the local; &y simply generated a compilation error because a local's address cannot be evaluated. This behavior has changed. Now, x.y and &y both ignore the local definition of y and interpreter y as a property name. This change is important because it allows code to refer to a property explicitly, even when a local of the same name has been defined in the code block. For example:

       myObject: object
         obj = nil
         setObj(obj) { self.obj = obj; }
       ;
    

    In the past, the code above would have had to use a different name for the formal parameter variable obj, because it would not have been able to refer to the property of the same name. With the new interpretation, the code can distinguish between the local and the property by referring to the property explicitly using self.obj.

    Note that it is still simple to obtain the old behavior of evaluating the local variable and calling a method via the resulting property pointer. To do this, simply enclose the local in parentheses: x.(y).

Changes for 5/21/2000

  • Add a new Vector intrinsic class, a subclass of Array that combines the reference semantics of an Array with the dynamic sizing capabilities of a List. Vector is substantially more efficient than List when constructing a collection iteratively.
  • Added some new List methods: sort, append, appendUnique.
  • Added new Array methods: sort, appendUnique.
  • Added an error message translation mechanism, which allows non-English versions of the interpreter's and compiler's error messages to be substituted at run-time. Messages can be loaded from an external file, so there is no need to recompile the system to switch languages. (At the moment, of course, only an English set of messages actually comes with the system, but it is hoped that a few translated versions will be contributed so that more languages will be supported "out of the box" in the future.)
  • Fixed a bug that reversed the order of arguments in calls to anonymous functions while running in the debugger.

Changes for 5/11/2000

  • Changed the name of the say function in the "tads-io" intrinsic function set to tadsSay. This allows the library to define its own higher-level function called say; since user code should almost always call the library's function rather than the low-level direct console output function, it is desirable to give the library version the shorter, more convenient name.
  • Added the appendUnique() method to the List and Array intrinsic classes.

Changes for 4/24/2000

  • Fixed a bug in the compiler that caused lost properties on objects under certain very complicated circumstances (the problem occurred when dictionaries and other objects were defined in a particular order).

Changes for 4/22/2000

  • Added the foreach statement.
  • Added the Collection and Iterator classes. List and Array are now subclasses of Collection, so these classes can create Iterator objects to visit their elements.
  • The Windows HTML interpreter now supports PNG transparency (this is a generic change to the Windows HTML renderer that will also be in HTML TADS 2.5.4). Note that only simple transparency is supported; full alpha blending is still not supported. PNG transparency now has its own systemInfo() capability code, SYSINFO_PNG_TRANS.

Changes for 4/16/2000

  • Fixed a bug that caused a compiler crash when a source file was completely empty (or contained only comments).
  • Fixed a bug that caused a compiler crash in certain situations involving multiple initializers in a single 'local' statement.

Changes since 3.0.0

  • Added the htmlify() method to the String class. This method converts any HTML markup-significant characters in the string to their HTML equivalents; in particular, it converts an ampersand ("&") to "&amp;" and a less-than sign ("<") to "&lt;", and can also optionally quote spaces, tabs, and newlines to ensure display fidelity for these whitespace characters as well.
  • Added a shortest-match modifier to the regular expression closure operators ("*", "+", and "?"). This feature provides precise control over how matches are allocated in expressions involving multiple closures.
  • Added the non-capturing modifier to the regular expression group syntax; this allows a parenthesized group to be omitted from the counted groups.
  • Added look-ahead assertions to the regular expression syntax.
  • Added the regular expression interval operator, which provides a compact notation for specifying a specific number of repetitions of a sub-expression.
  • Renamed a few list methods and array methods, and expanded both sets for greater consistency and more complete functionality:
    • indexOf
    • indexWhich
    • valWhich
    • lastIndexOf
    • lastIndexWhich
    • lastValWhich
    • countOf
    • countWhich
    • mapAll
    • applyAll (arrays only)
    • forEach
    • getUnique
  • When the default start-up module (_main.t) is automatically included in a build, _main.t is linked in first. It was formerly linked in last, which didn't allow 'modify' or 'replace' to be used with objects defined in the module.
  • When running under the debugger, RuntimeError exceptions now include stack trace information (with source line information) in the exceptionMessage string associated with the exception object. This makes it easier to track down where a run-time error occurred. Note that this information is available only when running under the debugger, because this is the only time the system has access to the necessary source line information.
  • Added a "character map library" mechanism to the stand-alone interpreter. This new feature allows a set of character map (.tcm) files to be bundled with a stand-alone game, eliminating the need to distribute separate .tcm files with a stand-alone game. (Having to distribute .tcm files seemed to defeat the purpose of building a stand-alone executable. The new bundling feature still allows users with localizations that aren't included in the standard character map set to add their own .tcm files, but it makes character mappings entirely invisible for most users.)
  • Added CP1250.TCM (a character set mapping for the Windows Eastern/Central European code page), CP850.TCM (DOS OEM Latin 1), and CP852.TCM (DOS OEM Latin 2) to the default character map set for Windows.

Changes since 03/30/2000

  • Slightly changed the InitObject mechanism. The class formerly known as InitObject is now called PreinitObject, and a new class InitObject has been added. The new InitObject does the same thing that PreinitObject does, but does it at regular program start-up rather than pre-initialization. This change makes the pre-initialization and regular initialization mechanisms work the same way. In addition, to make this mechanism more generic, both InitObject and PreinitObject now descend from a base class called ModuleExecObject; this class should be usable as the base class for other types of modular execution objects, such as perhaps a post-restore initializer sequence, a command parsing initializer sequence, and so on. Refer to the library pre-initialization documentation for details.
  • Added the -r option to the interpreter, allowing games to be restored directly from the command. The saved state files now include information on the image file that created them, so if you're restoring a game, you don't have to specify the image file; this particular feature also allows double-clicking on a saved state from the desktop to start the interpreter, load the image file, and restore the saved state.
  • The GrammarProd intrinsic class's parseTokens() method now includes truncated matches to dictionary words (according to the dictionary's truncation length setting) in its structural analysis. The method also includes truncated matches (again, using the dictionary setting) for literal tokens in the grammar. If you don't want to allow truncated matches, simply change the dictionary's truncation length setting to zero.

Changes since 03/26/2000

  • Added a new library pre-initialization mechanism based on "initialization objects" rather than a simple preinit() method. This new mechanism should be especially useful for library developers, since new initialization objects can be plugged in without touching any library or user code
  • It is now possible to extend intrinsic classes with user-defined methods.

Changes since 03/21/2000

  • The method-based API to certain functions that were formerly implemented as intrinsics is now official. The trailing underscores on the interim method names have been removed, and the corresponding functions have been removed from the intrinsic function sets.
    • getSuperclassList() is now Object.getSuperclassList()
    • isClass() is now Object.isClass()
    • propDefined() is now Object.propDefined()
    • propType() is now Object.propType()
    • length() is now List.length() and String.length()
    • sublist() is now List.sublist()
    • intersect() is now List.intersect()
    • car() and cdr() are now List.car() and List.cdr()
    • substr() is now String.substr()
    • toUpper() is now String.toUpper()
    • toLower() is now String.toLower()
    • find() is now List.find() and String.find()
    • toUnicode() is now String.toUnicode()
    This change will, of course, necessitate recompiling all code, and will require changing code that calls any of the functions that have been migrated to methods. The sample code included in the distribution has been updated to reflect the changes, and the documentation has also been updated.
  • Renamed the getSize() method in the Array class to length(), for consistency with List and String.
  • Added the 'is in' and 'not in' operators. See the "is in" and "not in" expressions section.
  • The debugger now has "program arguments" settings. You can access these settings via a dialog, which you can open using the "Program Arguments" item on the "Debug" menu. You can use the program arguments to control script file reading, output logging, and the argument string that is passed to your program's "_main()" entrypoint procedure.
  • Added several new regular expression features: <Case> and <NoCase>, <Alpha>, <Upper>, <Lower>, <Digit>, <AlphaNum>, <Space>, <Punct>. Refer to the new regular expressions section of the documentation for details.
  • Enhanced the regular expression parser to detect and remove cycles in compiled patterns. It is possible (pretty easy, actually) to introduce "infinite loops" into a pattern; the easiest way to do this is to put a zero-or-more closure inside another closure, as in "(.*)+" (this has an infinite loop because the expression in parentheses can match zero characters, and that match can be repeated any number of times), but much more complex and subtle types of cycles are possible and can be inadvertantly constructed. Eliminating the infinitely repeating zero-length matches obviously doesn't change the meaning of the expression, since one repetition of a zero-length match is as good as a million, so the regular expression matcher simply detects and removes these loops and then carries on as normal.
  • Updated StartI3.t and StartA3.t (the starter games in the Workbench kit) to work with _main.t, so you don't have to compile them with -nodef. (This also has the benefit of making the files a whole lot simpler.) Also updated them to use method calls rather than intrinsic function calls where appropriate.
  • Updated the sample files to use the standard _main.t startup and new property functions.

Changes since 03/12/2000

  • Added default display methods, which allow string display to be routed through a method of the active "self" object in effect each time a string is displayed and each time an expression embedded in a string via the << >> syntax is displayed. This powerful new feature allows for per-object output filtering, and allows for "stateful" filtering (in that the filtering function is a method of the object that initiated the filtering, and can therefore look at properties of "self" to perform its work).
  • Added the "short form" anonymous function syntax, which is much more compact than the long form, but can only be used to define functions that consist solely of a single expression.
  • Added the subset() and applyAll() methods to the array and list intrinsic classes.
  • Added documentation for the List intrinsic class.
  • Added the -cs interpreter option, which allows the user to specify a character set to use for the display and keyboard. Refer to the new interpreter section in the documentation.
  • As mentioned above, there is a new section on the interpreter in the documentation.
  • The TADS 3 HTML interpreter and debugger for Windows now use a separate registry key for storing their preference settings. In addition, the TADS 3 debugger's global preferences file is now called HTMLTDB3.TDC. These changes should allow TADS 2 and TADS 3 installations to co-exist independently on a single system, without affecting one another's stored configuration settings.
  • Anonymous function objects are now represented by their own intrinsic class. This is a mostly internal change, but does have the benefit of enabling the debugger to display the type of an anonymous function object (before, the debugger only knew that they were objects).
  • Made some internal changes to the way anonymous function context objects are implemented to make them more efficient. (In particular, context objects are now arrays, not TADS objects; array index lookup is faster than TADS object property lookup.)
  • Moved the code to Visual C++ 6 (which involved no changes, not surprisingly, although the new C++ compiler caught a few dubious constructs and generated helpful warnings, all of which have been cleaned up).
frobtads-1.2.3/tads3/doc/tgcover.jpg0000644000175000001440000002722710476335120016455 0ustar realncusersÿØÿàJFIFÈÈÿÀ´~ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?-¼#¦K7‡,Íì¿nÔü—™C¯îQòÄíÇ÷psžý+ïeŽª•Yòû±½¼Úÿ‚}\±SJr¶‘¿àEàÈãœÃò¤ÒÜÙÛÅ¥IF]¾b¹TävaUOÚ»Z%&ÿíÛmëB¡‹m]®ýÖ1ާÞkZ~¤½ë™îE»ËpŠ»‰` ªŒã©à“Úº}­HS•J–Ñ_Coi8ÁÎvÑ\èÿáÑnnõ²Ô¥0i¬²\Ì]Xy?>ò80Ú€yjâúõxÆ.qÖ[zéo–ÿqÍõª©EÊ:Ëo],PÕ4=Jm%/.Å-ÊÆêZxÃŽ6öܾ¼gŠÚ–#WÅ-KÎÎÝÍ!Z­NnUµíçfZð÷c¹Öm¬µB(VóLÖ¾T€ `3¸}ìn$ ôö5ž#2q¦çJ7´ù_ùéÐŠØÆ å´¬ÿÌáv»/¡Åz©ÝèmP@  ± “ÐTT© ps›²JíŽ1rv[š–ºr… ?-ýÐxù^uÇU§7OîÇùšÕúv_¡ô\¦)sVÕö;K¯ iVÚlS¸ÞF¶i™nAÚ¥G¯Vð5áTÍ1é*;Úÿùi{ô«)ÔtÕ—2W·›¿àŽ6ãNÁ1|éÚ½L§Žqt&¡Œ÷áßí/Ñüþó³•Sš½=àe:29VaÔWë8lM,M(Ö¤ï+¦|ìá(IÆKTmÚëž ¼k[;K«ÉäBA@³(<.GÒ¢X|™­˜¢*c_²Éò•ÎÒ8퓊…_›’”nüÑ*®6ù—ÞŠ÷7þ"º×m…ÄÚŒº½´Ÿ¹G eóž¨9æ®4°Ñ¤ùRQk^Å(QŒ’åq³¨kiž ^[¸î1±L¥®C€ÃŽàʪ*ªTÒµú}çN•“…¬hjVþ/Ô,cKûbkT ˾Õö€œtÇ9àéɸJ)ú£8K ^-_ÔÊÕ5}OS¸€ê73M<aFa‡$ORIc’rNk¢• TÓäVMÜÚ©Á>U£"»Òu+(VkÍ>òÞ&èòÂʧñ"œ+Ò›´d›õjÂNÑi•ím§»˜Ci³Êz$HYà*å8Á^NÈ©IE]»Ý­Åœí ÜÁ2õŽT*Ãð4Bqš¼]ÐFJJñw!ªPý&0Ó3‘÷Zø>>ÇJŽx»{G¯¢·êÑë唪¹¾‹ó=E "ÔÔ›µs$„+e€< Ç€o¼yȯÌh¼7³^Ó}úýß×s«{¬Ý†Ö[vßåúm©4W>ì† £^– Èà¬8ÈŸËÜñT§†çµ•¹¼öþ´"Tñîîù¹<¾-¯÷jVÔŸCþËq·7% _(I¸IæqÉvìürj*:ÏK^Þ{ßîµh,g¶\×å¿[ZÜ¿7~oÀáuˆ€) êx5ú‡Ù„äª`äô^òüŸèaœÑK–¢ô)ÙÝ\Y\Çse<¶÷œ¤±9F^ÜȯÒ'Î<²WG…(ÆJÒWG¬]øƒY_‚6Wë«êùµS¸/æÚÿ.ìç+çá…£ý§(r+rmen‡‘¾ºãʭ˵ˆ~ Z\Ýjº×ŠïVâúm:ÝÚ=Û¤’i™ON¤ ø«ÎgÓ†6Š“ôI_‘Y”£F„t»ü|cÓ®5-@ñdÖ²ÛÝO·¾ã(Ë Î SÈä0úm¥“Õ:µ0©Ý'uéý[ñ]QB¤è'tÑcãˆ5ZГJÕ/m#:dNc†fTfÜÜ• ëQ“áhÖ§QÔ‚~ûÝz—P§RçŠ~ó,ø³QµÓu¯‡þ+Ôì—í×o‘ØUÃíõ‰AéYá)N¥üzWÑb)F¤”y­ÓÌö*Â3š¿‘è_žgŸÃ™auh,G‘¨®ø±Àã±ÿW‘¨¥S£æÕv<ì­+O£æÛ±å•ï¨P·†®#·Ô£i”¼aÕ™GñrGå_ǸYN…JWP–¾Žßåo™êå’ož’vrއ.¥uue}•3ÙD$ع̬Xu Ùvù«­:’ŒÔ4Z|ßü¦8jTa:N¢Rv%Ó½¬¬Ë­~Ït4;ö– †Ë*‚Ç99![=ý«_jÜ´¦î¿[–‡2Ã¥zѳ~Ÿ m.Õÿàþ·siwF$@ÿ2‘»¿VqÜqøSúÕ½÷oË}gß÷q¨®Ý¼Ý­£ôHóOÜ­ÅÛ²–;Ü¿ÍןZû^ÂNUkcÏÝ^zÝýÚf’P§ =Q‹_¦ž)¹/‰o$ð„>h­þÃÏÚC…>fìŒçäö®U„‚Ä…ƒtàè+:™u•ý½M]­gk}Ö"x:s«íg®›=‡ÿÂy­ËáËýQ˜jV·d1’õžI"#ØÅ¸äÎGæifÐU£Z •®ÖIúè/©RUH«5ØÔºø¡¨]›w»Ð¼;q=¼kSÍfÎ꣦ cõ®xäôáu’I½“ÿ€eººS’OÏþ‡uã=nïÄÐë×W+5üجëTÄàg N|ðnÎëb*N¶Q”¥Íì̽[I°‹à拪Gkêj“ó23þCò­éW¨óÒoÝQÛî5§Vo(7¢ù‹¤ØMðÄ:œ¶±¶¡äiä|ȤÇ?3ùÑZ½E˜S¦ŸºâôûÂ¥Y¬\ ž?Ôôx|¥5Þhž†çN¼²I.ïÄ®†+ÉÎ~‡ñ¯ãªòÔ“­i);-5<׊©iËÚY§¢êšþ¡£[jW–ú‡’žs0HŽûDס‹©ZxèP„ÜSôù˜‰Ô–*4£+'ó!}Bñ_‚µ}gDÓ›HÔ´¯=ºLÒE*ò7rò÷â•zø\L(Õ—4g³µšcUjЭu%Ì¥÷N­áè,4½ ôì ›åžlÉÕèॊ•IÔUkòZM%¦Ç-:îRŸ=^[IöÓvˆ‹¸äžŸú'‰X¹Åâ'»Ù_Ve*ê¼£í¦”S½•õ*øOšëÁ?ðŒx§FÕbŠ“qmuh¨Å ÎAû·çÛ¦#NŸ¬áêE¶¬Ó¹U«Â5ýµ)­Ušeoùø{>…¢éÚ—Øæ¸Y¯/¯UW' *…Ô/ÿ_4¨biÏõœEH«+$®kÂuýµY­’FÝ爵;oxN÷L¶¿6VöQÛÝÂFÑ çv88#Ü æ„pÒ¡V&¯)6ŸäaAÒ©É]»£ÏügáÙ¥ñ=ôº…Çö|òï‰Y•ÝÉ\g $íŠö0Y%F1«5Ì´;ðøú1¤•I«¤u±ÛÃâ izg‹4^ÚóLÉ·¼³á£ã”žÀžŸ…q¹¼=iÔÃN-Iݧ}üŽ'Œ¥F¤§F¢j[§sGÆšž¥y‡#Ò,u{éô›¡r×WÊŠe ä´þ¸¬pt©AÔu%©«Y_B0ÕpñssšJJÖWªèþ×|@uëý;Ä6óÈË$öIl®ÃÏCŽ¥:XŒE ^‹]¡ {¥OÙFq~z“èÆ ÿ¯|Iªi–öRÛ"DPì mÆyôi¬ pôæ›Nÿ™5jaþª¨Â¢n÷3|'§*ü7Ô|9®Á¨ÚIs}燆&ñßÕ o‹­þÙE£m]»ÿ™®#Kë*µ9'emýF^Àtoßè>Ò5IäÔ}ªöì"’£øUAþ~§ðpš­ˆ|LÒåÙ+ŽªU+*µª-6H××ííu7G7Úî˜ö) «)*9$îÍF«¡)´£$äÞ¯þ•,T)ÊVåwwÕÿÀ<²ÿº£ÞÎö6·76¬äÅ;€EÏ yêG5îÒÌ(8&䓱êG2Ã(®i¤ÏmÕ/âÒô»›ÉòàŒ¹¾JøJÊçÈ·dRðeš ..‘dº¿uË0ÎâÜãè¦*ÈpVDºN±q¤&E›Çö›E?òÌgƒØÿ{¨Ùؤt- :Óqu=ýü¶v Û¨3Ì;=À89ìG¯ â2¯Œz•Ž­ ëB×^Vbe]¦EÆU€=ÁãÐT·{ÅŽ/[. [x´DÎÒ°–lr˜ÉÅUía½Kz…’E H3($q[Qw©挪«A¿#!.œw5î:Hñ}«'O³Ž à}kž´¡M6ú#z\Ói.¬ŽÒòYmQä*²‘ó9Ò¡iÓM•Y¸M¤\Ëß§¨®•N';ªÊís*ƒ‚÷«öq'ÚÈ®óÊǯåUìÑÕ²gk‡²õ¿260›·N?ñ±•ùg(%Øõ°´¹¡·Üæ{2°Èª ߯O«#›Ygv0麎ÒxVp>£4þ±ˆ{/À^Æ‚Ýþ%«mD=¤Ft’¹‰Y«~[Gòåâ%'Q¹nzxuMrìZŽò7¸e„´aTn#é‚8íS7ªê$ìÉôxÁ»¹yÈg Îx,yoÃî ¦‰!ðâ*]2Fýî›3[‘þÈ?!üW”^–v°š“}»Äi$g+§€ öó_œ~ûê¥êÇÔ»¯êÖé¡ß1Êó°‘Ž}:Õ9h3È>ê:¥¨[‘.¥ªÏ"ÃÀ dÈ}1“×Ú³r娗-<Ï@xçµð¼J‹ ‚Ûk‘îiääôóß5KHè8%kZÔ‰.‡£Ü¢lÝæDcS0ÿÙj.ùÑ¥—6…&ñÜZdZPžÚY<÷Tܧ„ÆÜä~?¥t*Ž›‹f >Ò2KÐê-¼V$帶ò¡' (ÎLÙÇðšj¬_Q:r]šÜUs¢yYÏêY[©€uëÐŽœ ð±º×g¹ƒÒŠ*X5À»t`­MÁÈêsÈþUwï?B± r¯SÌü[ãMjÚg·Ñ¦_(Jei"L·\á³ôíÔW?;c±gñYÓï¥0ŠI¯"TÙ§ŽÿÐZ9šd½ÕøWÇWšpÓœH—ìâfrØóŽì±ÏcÆ>:U_K - Þ+ñ­½¿‡.4Ñ‘<Û÷ã( Ó§¨Sv±RjöC<o :U§Ï(%Ùœ– 2Bc;õêsG¨¢º³ZûV2ê—ªËnRdgÍÀàãÏž¸8¢îå$cM©¼Ÿ`¶IÇn\4GûÌXç?B%~áöŽgYµûO@uJd¸Ut,¹x¿Z»Fé¡F÷mž“u$p k¤VØØPð¼Ÿn¼UIÙê5¶ƒÛV·_³Iê펡Ќœµ­êÃZÈO†:áš/·ë]™•[j3µôôæº0íSž½Œ+§Rw=ÏW±¼RÖ·pÈ3µºg±®åZ/©ÄèÉt.´€.Y€¤Õs¢yRæúÞ($•î#òãåˆ;±Ž¼ =ªÜ=œ¶g1sky<²Ã)òÙˆÎÂ?‡ßåW’G$z¸x¸SQfV„ò?ˆo–b‹l¨<¢¤äôÎ{uª¢šw"¼“÷zžtöÓêR3ÎþGšpV6fÇ\sÀ85‚„’Ø·$ż½ ⊠J *œ¨Û€à``Rösæ¸sÅFÄ-²…–Þ™ÑÉcœñ»ü(³ì$jjiï¢ÜZ5µ“ $;¤9ÉÎs×ò§»DlùšïØ,æµÒ¯ZÏn|ÄŒ¾Äàöçñ­}ŒÚ½‰U#}Ì;»ñoªÞÅyzÖSÅ."PO=3‘Y¸wFŠ¥¶/ÙkGB;º£ß9©åbº¹™¬x‚ŧ€ItòGo7š¿geG*}xªI¢mµm ÝÆšCj&y5kÅŒ&<›¦à_¼A4ç)5b¢’g`~"x]íŒw:„±ÆoåYÙö4æG }㨒òD³¼€Û3¯æH¸Î?JÚ3v±“ŠlžÛÆA^êÒHÄ“ˆç!CPçS¡QPêoÅâ}-­Tf=˜á¦=7æ¥J¯ríO±Ÿô.¾¡u¼tß1)Ç©,ȯ3qÔÏ•'¡ÐZøÃèIªYsÎ|ñ‘XÙš©#Å~+´Ð5ë8\ŸÞl‘NE\%(‘8ÆZš‚ÓN†Lµœ`;+»:÷ê+ÜŒi³È”¦ŽGân­§_Ü[Øh6bvˆî§Q€ç<(Äž}ñ\x™EË–é ¤•ä>ÓÃi¼¤K²Hñ#ïõ²•.ShÔæØä¼?¢Ï!3¨I 2lt,W8õÿ=?âÞ£”‘íº7ˆO‡|<¤0{~vƱ‚Á»îÏçñ®¿kəӿ‘äßïtïji¨ØƒÌ£Áüp}ºs\U&¦îuÁ8«}q‰ÏµAdNA(Å b€„P”‘Ò€>´ñlzU—†®¯a‚ëíˆÄŽAÇœ}k¦RIhÌ“z£”²ðCiÒºµÇß‘úå'õ¬TžÆÞÊèºtÓ-çÊUNxã¯ÖúXÏêË{œÿÃ=î´ÛÐð“ܧg'lçð¬¹÷Eòšðü0¹¸¿f–壵Q½P6Ö?@N8äšÍî5ˆ×ôÜEq=¶4v[°ÓHq½ÀoÂ’³+S‚º³ž'eœ‘ÁƒøÕˆ­m§ÜO ÷ÅæGß —Бé@æ€-ÙØµÅ­ìÛÂ-¬BBûÙu\ûë?…D§ÊÒîÿBã ¦û"ªðjÈ;It)<\öÚ†‡gmn^2·v𠩈¿3ì@n8Îà:Wñ1£'üލa¥V*P^¦·áë­,U¢¼²?Òí›|a™Cm$}ÖÁ Öð­;lûNŒ¢¹·]ÌÑipmMȆCn NÐxïøÌUó+Úú‘Ë+s[A¢1ü’ÛCíã8Î3ëŠwW°¹]¯ÐHãyˆÑ˜ã ¡´·›Øû} ¯%°Uur`FxõÅTñ7&6í41™}碷ùÍa*®Ká65¦GŒ4y£Ñî ¶_ßʆ5el›‚{ôÎ ÉO]M%4 ømk§XéUž£©ÚAs (bfPr:óž?™Í·d$’ZžÏ׆ô¨ÕgÔ,UÔuy—?ζ$wz™6ÙãßüU¥>•)Òõèã fö"w°c;u\×zÐùBâv–rÓ’díBŒû‚´]™Q‚弆`HÉÿ= ¡í -õ5|k ÚhÏe.4“ZÝÄ%FqƧÌì+“ ˆ•W%5f™×‰ÃªqŒ¢î™é>•m¬ü»¿‚MRêX4ÝÏÖCBžÃ qÈ5æÕ­*xåýÔ›ûΚU(4–­~§˜&„°\ýR™,î¤1™‰UGÈù_Ž8Ï=3Šõ~±Ì¹ ®“þ¬r*¯–¦­.zÿƒ<1k ]ééçÚ´š™‰ìÃ3H^EØÄð0SøAë‡>¼xXŒ\«siðïÓOó=œ-жºKóÿ/3§Á6Úγga},CMÓ§–1¦²´it ‚’®Ñƒ %ÝÔ/Ö¥cTh¶›æ·ÏsEÏv½ÓÎ5ø´_øÖ_Ú—HÓ^ÈIšT‘ŽÜò£b'\ðOµz‰Tjõð8áìÕOfö­¿ˆ–zV¦êr]Ú^ù7“G¶c¤'‡E`Nñµ½8<×¥J’QM&¯ýz¸ŠtáMÎÚ;iú¯3£økð~ßPѬµ[V÷NŽXEÌ1ùrÜù‘²Ç|àq×çs¾*xZ΂‚“O[½žG4)Æ :m§Üç/|Sã[Äľ%½ÿžgËíáÅ~‹ì"Û3ŸÔ®ç;í¥y®‹¬Y[|HøsekJ4ë¨ìÏ%‰Ý`þLÇò®L´œßÚ×ñgN:V¥_XÝ~ú Ç6sxÏÓÝ]Ij¶·X£…yÆp:/ÌI>ƒÞ–.téWæ’û&Tœ§†p]î|áãÿ iº¿Å­Vùdxíg^E䑆àoÏÈþ~µáAr­/c¦w<ï'gdÏ¢<àÿ êú†ž÷±Å½ÌH^R¾{•Q“íÚ¾gˆsZx:ŽÞóZò§úþFTñ'O–¦ºèq ø‰.±-Þe¨Ï´‰.t•‡›Y#tGø£Êºž˜ds_;˜d‘¡ ’Š|ÎÓ¿Ä·´»KTד}Œ£UÉÜñ{ûˆ­‰ ;É'÷@­~ÐÏ%X­¢ÅqüMIBËw$ɵömÈ?tv9¥¡Z„š”êG–È1þÈæ¦ã)Üëw*20ÏP0£@9ËýRþvË*pN \ŸÖ‹Ý;F7¢†ïÀ¦+^W8!JÈ¥N:sJOA¥©ë>%Óìuí^MKV½®d%›c$ä‘€{“ù×ÍÓÇW‚j1[žål:“NÏEbö–t£MÔn –ÞMñ²uCÉÈ8ëŸjç•zqÔÒœcN ?¸›ÅÖºoŒµh5sQž{˜c†ÂŒÄó…É­c˜b šå0žœícSÂþѼeiâ nZå­¥iÖØÊí>ß§­I«ÃDНJ®ïfuž=ñ…‡‹šk4HÌhm¦Û"äòCŒ‘¯­,N2µj±œckwÿ†*P¥Þæ†|/áÛ[ë_ëŸb´p¶©ÕÉÀ}þèç=«çs<Óã,.<ÒZ»+òÿW)U'ͪÆ7ˆ'Õ¾#ë÷Mêi–> Ò>Ó%°vuXãÞªÛÏ,Ž1‹ á#ÖŒ:£•ÑXˆ¹NN[ìÛvoNéûÑ”^©ëØàw›åê‹ú®¯§YÍ ðÝÕ®Ÿy)VÔ/¬Ð+^JË>îKÇ5¦,«V âÓ’_ ^¼«·­ŽªjÝjx²Û]1-$’{”<×éwg‹bu‚è`-´ÿ„gü)Â4Wƒ?èÓ¬f‘D/Û.VÞàý#4€­5­ë K‚OLD€+>ë‘ök‚ÝÀŒÓZ[‘ÿ.ó¬f€)\Û\,©ºÚn'ä4švw;S{hÊB[Ü.Þ7ç‘éŠñ¾«Zú¯À÷>³E­ÿ¨¹‘bˆÄX•*AçéøVÑÃK™Þ&3ÄÇ‘(È[InL’Õp6ýìg“‘N¥ Kð&•}[rüIVâttgŽ^H9Ž+?«Éì~±¬ï|áƒýyâÅuýƒ§¡˜Ç%ÞÝìúžžü>o5ÌÜ*LJkÚÉÛ]£ëçÙÀ ÕI]3Õ´ýSÿ¼+fÖÖ0^ÚÇ5¸¶–Xä6Ä¡]êT‘· >aÇå_WŒÊqP©vã&Òk›[Ùß®›sF¢w<—ƾ0Ô.l#Ñm'ût©—{ªÇ®ÎsµJŒùc Ï\f¾ß*È•9¼MH8ëxÁÝòù»ý§×±P”RÞìâ|™¥ˆF@P÷cþô<’Nü¿·´‹VRüO«Ž²Ic‰É9 ´÷yOœ‘5˜\JWköÇ¥dô*íýª[ÆÙyzbŒŒœ!f>ÔY-®ßQöPa\$÷1&pŸ(‡©çëJR)&hÛÉsî’øùg€¥J†ÓÙ“êÌË+Ó§\Í$’‡ó ÷=*šæZž¦ª²Mu4â}«ÕKµ¤]•‰–®æ<°¤±H^Wl¡ïíV¥fŒä´~†MUW¥ÌyV4â¶qG0X··µÃå,]ýFÑçÖuëaÔ4¬H £êHé_ÄÞOáüÎCĆ«âãe6´usz5­ -`Öÿ(—Í+˜ÛÍWÜxÀÅ~y‡§IR‹«ÊÓRu¿‰Í¯-¯ïk£–·Ôö^›|»އ¦x~Ú[-°r€,¨Í»ÊVÆ|°zšû¾Èë7~c­K+G¢²·3[sµ÷zžV3¥ztöþ¿ ¸ô¯¸ç<ÞR¼Ö€ž”s‡)ÏMñ;V_°Ûï#’Y@ŸÿZ¼çJ/s¿Û5±^‰:Ž¢ñÁmcM#yaùbˆÐ„u¯)hŒ[¯ëpÌË+ÎO1ßÒµP‹2sšÜDñ¦³q*¤$»ŸáPÄŸÃ4ù"º ÚMõ:#V¿¸¶ygs,;¾`F•…GìoMJ[Ëâè’sQLÍœ~øìæ©A5vÄê4쑇¬x²êÞÑï¾ÌZÕxÜ€°ÏnsúÒ½5Ô_¼zØæaø›wu©ZÁoe qË"ÆÛرÁ qÓ—>¦¼º» j1[ûS“Ù¡höÁìû•°½Õn!¶³Þ#V• œúšù>"âIáSÂà×5f¯¦¼«»ýÏ׳ ƒS|ÓØç<ã{?ëºÇ‡õKI#±»²ÜA|½¤Ç4y8Üzž˜¯Îs\²¶œ1Ÿ4Ôõ’w»~ô_—oøsÕ§5'Ëm,tÚšh^´Ñ,ïêÖØŽWIUã®=ñšû|ƒ#R¯ý©…ªKUÏ»ó{Û§®Üš­¯e ‰ -}ß·<ÿbÈÙV—¶dÈ™4{tÅžw†4'I>ÌŒ.c’Fù½»W?·‘ÓìbGöSK*-¢tÈÏú:‚? ÎS}M# lU¹\º]· $«œüÖ ãô¡TaìѨ麬3K›<¼>ËÉɃ4ý¤žŒ—M%t.‘¦êbÕ%šÚX¤a’?™©rw)FêâÎ/YÕZÖù¶¢×¯éT›±-$Í[]7R[øžâÚâHŽ7& Œ¾Ùþ´\v3¼Kà«xµ+[T/eîóUÌÏË¢Ø:~íýFkÊu$÷g|a²8ɬ‹±ušPç¾âJöO,ER‰H‹P˜Àÿ:a©"jÎ¥(ÿ€ð¢à$ŸÚ›I]NLÿ¸¿áEÀ®Òjápu?ðÿ .Žj˜Ïöƒçþ¹¯øS­>¦Ç ¨I·ÙTJ†þk˜ôÛ­÷2H<¦ûø=Zl—˜ß |%{âYŽ£u©£[‚_jü÷ ?9ýÈùž$âxeÑö4Zö²û£æÿDVÎï-V¾±ðÇÄC§Ó‚¶ñ:…–Ödê6Œ¼äzg±¯ËãˆÇåy„«É¹I;·º’~}ŸFz|°œ9Qæ\éº$ï¢ør;K‹ˆUukëGeŽáÐDJÇîç9Ç_§_Ð2ìzíc1—|­ºq•¯õ—Ÿkíë¶‹÷WÌ©£ø²ëGÕ ³¹ŽØ)ز,`±Æyç=q_I[žQRê:* ¸ô#¼×N¿y}q}rÒLŽJœ`3Î;V*“ŠNÚ³_j›jãnüAj|4Ñ$I¨I‰K„ç ð¹ôÛú×Dc'5Í©Ï)EAòš7^%†a£¼“œÁç;z `¯ò¬½œ½ï6_<}ß#¡ÕxTŠrmj›ÚQ}lîœwòØèçw²Vhå¼qã [fé, °‹[¼Êê—–îDy?òÊ2O¦Ðî1¼¨í_E‘äÒœ#:íºqø#-ÿÅ/ÅÆ.ü·2©Q'¢×©Ë>±iÕ¹ÃzÈ»Œã¯ø×Ö¸;)«™—Š}¸Ê4$îU'8äÛ¯éO‘¸ØÒ•Ét»•âæáäH¥<ˆÇ#ŒzÓå²±<×w ¼¹Æ–c ¸•°ÊsÐÕYÜWÐÑ»¾‰€Š%]£…mÙÈ¥`¹ÐOv|ùFìüǾ{ÕX›ž„­ÇAùPˆsÆZC$LŒPO,GA@8äòhcÍ0'ÑmÒûW´¶˜·—,[iÁÅpfØ©ápUkÓÞ1m©I&{M´[@À‹H0ª½¯À«×©^£«UÞMêÏU$•‘äÿÛþÔšûGf¼ÖöÃ{2“¹Ö5;qèpØ'©WÚð…(ãj(W÷£KX¯6õü´0­î꺟?_é÷›žV”rB·¯ÔTÚ9ÔËKm®IMÌG9cšÖ.èÊJĶñ#K$¤rwñÛ€qZX„Ýì>0­(Rª¨#hÇ5VÔ]êÇlv¨!Ÿ$ã“øÒVö}’:$q® €r9úÒvOa©ÜÊ\ÌÂB;·ÿZ„7¡ÿÙfrobtads-1.2.3/tads3/doc/t3QuickStart.htm0000644000175000001440000017076411757403110017360 0ustar realncusers TADS 3 Quick Start Guide

TADS 3 Quick Start Guide

Welcome to TADS 3! This Quick Start Guide is designed to get you up and running as quickly as possible. It contains three sections:

  1. Instructions on installing the system and creating and compiling a minimal game (as a basis for further work).
  2. A guide to the other documentation to help you choose which manual to look at first and which you can leave till later.
  3. Instructions on creating a sample game for people who want to leap right in and try out something hands-on before starting to read the other manuals.

Enjoy!

1. Installing TADS 3 and Creating a Project

1a. Windows Users: Installing and Creating a Project in Windows Workbench

  1. If you're using Windows, there's almost nothing to installing TADS 3 — just download the TADS 3 Author's Kit, which consists of a single .EXE file that installs everything. Open the installer executable (by double-clicking on it from wherever you downloaded it to), and step through the install screens. Everything should be self-explanatory. When the install is finished, you're all set.
  2. If you're using Windows, run TADS 3 Workbench (by selecting it from the "Start" menu group you selected during the installation process).
  3. By default, Workbench will show you a "welcome" dialog asking you if you want to open an existing game or create a new one. Click on the button for creating a new game.
  4. If you've turned off the "welcome" dialog, then select "New Project" from the Workbench "File" menu.
  5. In either case, this will display the New Project Wizard. Just step through the wizard screens to tell Workbench the name and location for your new project files. Workbench will automatically create all of the necessary files for your project, and it'll even compile it for you right away.

The steps you’d typically follow once the wizard is launched would be:

  1. Click the ‘Browse’ button on the first page of the Wizard.
  2. Use the file dialog that appears to create a new folder (e.g. ‘MyNewGame’) in a TADS 3 folder in your My Documents folder.
  3. Navigate to the new directory you have just created and enter a filename for your new game (e.g. ‘MyNewGame’) into the File Name field of the dialog, and then click the ‘Save’ button.
  4. Click the ‘Next’ button on the wizard.
  5. Click the ‘Next’ button again. On the next page of the wizard select the ‘Advanced’ radio button (for the purposes of this Guide you don’t want the ‘Introductory’ option).
  6. Click the ‘Next’ button again. On the next page of the wizard leave the ‘Standard’ radio button selected (the WebUI is a topic beyond the scope of this Guide).
  7. Fill in the four fields on the next page of the Wizard. Under ‘Story Title’ put the full name of your game (‘My New Game’ or whatever you want to call it; later on in this Guide it will be ‘The Best Burglar’ for example). Then fill in the next two fields with your own name and email address. The final field can be used to give a brief description of the game (e.g. ‘This is simply a tutorial game I’m using to learn TADS 3 with’).
  8. Click ‘Next’ and then click ‘Finish’.
  9. Wait until TADS 3 Workbench has finished created and compiling the new skeleton game (you should see a message saying ‘Build successfully completed' followed by the date and time). In the left-hand pane of Workbench (headed ‘Project’) look for the section (near the top) that says ‘Source Files’ and double-click on the icon representing the file you asked the Wizard to create at Step 3 above (e.g. ‘MyNewGame.t’); it should be the third one down. You will then see your new game source file open in the Workbench editing window.
  10. To compile the project again when you’ve made changes to it, just press the F7 key. (You can also select the "Compile for Debugging" command on the "Build" menu, or click the equivalent toolbar button.)

1b. Installing the Compiler and Creating a Project Manually (for non-Windows Users)

If you're not using Workbench (which at this stage should only be because you're not using Windows), you'll have to create your project files manually. Fortunately, this isn't very hard - you just need to create two files and one subdirectory.

Jim Aikin suggests the following steps for setting up TADS 3 and creating a project on a Macintosh (these should also work for other non-Windows systems with a little adaptation):

  1. Download FrobTADS, double-click on the .dmg file, and run the installer.

  2. Create a directory to hold your projects, and a subdirectory within it to hold your first project. For example, in Documents, create TADS. In TADS, create a MyGame folder.

  3. In the folder for your first project, create a folder called obj. This will hold the object files created by the compiler while it's running. You won't need to be concerned about anything in this folder; it will take care of itself.

  4. Using a text editor (not a word processor), create a .t3m file. For convenience, give the .t3m file the same name as the project, perhaps MyGame.t3m. Copy the following text into your new .t3m file and save the file to the project folder:
    
         -D LANGUAGE=en_us
         -D MESSAGESTYLE=neu
         -Fy obj -Fo obj
         -o MyGame.t3
         -lib system
         -lib adv3/adv3
         -source MyGame
    
    Replace "MyGame" in the code above with the name of your actual game, if it's different.

  5. Open a Terminal window. The Terminal program is located in Applications > Utilities. You may want to make an alias for it and drag it into your Dock.

  6. Create a starter game file, again as a text file, and save it to the MyGame directory. Your starter game should look more or less like this:
    
         #include <adv3.h>
         #include <en_us.h>
    
         gameMain: GameMainDef
           initialPlayerChar = me
         ;
    
         versionInfo: GameID
           name = 'My First Game'
           byline = 'by Bob Author'
           authorEmail = 'Bob Author <bob@myisp.com>'
           desc = 'This is an example of how to start a new game project. '
           version = '1'
           IFID = 'b8563851-6257-77c3-04ee-278ceaeb48ac'
         ;
    
         firstRoom: Room 'Starting Room'
           "This is the boring starting room."
         ;
    
         +me: Actor
         ; 
    
    Fill in those quoted parts under the line reading "versionInfo:GameID" with your own information. Everything should beself-explanatory, except that last line that starts "IFID =". That long, random-looking string of letters and numbers is exactly what it appears to be - a long, random string of letters and numbers. Well, almost: it's actually composed of random "hexadecimal", or base-16, digits, i.e. 0 to 9 plus A to F. The purpose of this random number is to serve as a unique identifier for your game when you upload it to the IF Archive. The format is important, but the individual digits should simply be chosen randomly. For your convenience, tads.org provides an on-line IFID generator at
    http://www.tads.org/ifidgen/ifidgen.

  7. In the Terminal, use the cd (change directory) command to navigate to the folder where your game files are stored. For instance, you might type 'cd Documents/TADS/MyGame' and then hit Return.

  8. While the Terminal is logged into this directory, you can compile your game using this command:
         t3make -d -f MyGame
    
    If all goes well, you should see a string of messages in the Terminal window, and a new file (MyGame.t3) will appear in the MyGame directory. This is your compiled game file. If you've installed an interpreter program that can run TADS games, you'll be able to double-click the .t3 file and launch the game to test your work.

    Alternatively, you can run the game directly in the Terminal by typing 'frob MyGame.t3' and hitting Return.

  9. Instead of typing the t3make command every time you want to compile your game, you can create a .command file in your project folder and then double-click this file. Double-clicking the .command file will launch Terminal and pass the text in the .command file to Terminal.

    However, when you try this, the Macintosh is quite likely to object that you don't have permission to execute the .command file. There seems to be no way to fix this using the Info box (which is opened using Cmd-I). You'll have to do it from the Terminal. Navigate, as before, to the directory where your .command file is located, and type this into the Terminal:
         chmod +x MyGame.command
    
    Again, substitute the name of your actual .command file. The chmod instruction with a +x flag will make the .command file executable. Now you can double-click it to compile your game, but only if Terminal is already logged into the game's directory. If no Terminal window is open, that won't be the case. To remedy this problem, add a cd line to the beginning of the .command file, so that the .command file looks something like this (substituting the name of your directory and game file):
         cd Documents/TADS/MyGame
         t3make -d -f MyGame
    

1c. Running Your Game

If you're running Workbench, once again, this is easy - press the F5 key (or select the "Go" command on the "Debug" menu, or click the equivalent toolbar button).

If you're not running Workbench, at your system command prompt, type

   t3run mygame

But you should check the README file that came with your system's download package - the program name might not be the same everywhere.

2. Where to Go Next: Navigating the Documentation

TADS 3 comes with a number of different guides and manuals. Which of them should you start with?

The first thing to be aware of is that three of the manuals are intended as tutorials (for use when learning TADS 3) and three are intended for reference (for use when writing your own games once you've mastered the most common features of TADS 3). There is overlap between the contents of these two groups of manuals, since you may well want to use the reference material to remind you of material you first encountered in a tutorial, but they have quite different functions.

The three tutorial manuals are Getting Started in TADS 3, Learning TADS 3 , and TADS 3 Tour Guide. Only the first two of these are intended as introductory; the TADS 3 Tour Guide is primarily intended as a follow-on to Getting Started in TADS 3 for people who want further tutorial material. So your choice of an introductory tutorial is between Getting Started in TADS 3 and Learning TADS 3. So why are there two introductory tutorials and which of them should you choose?

There are two because people have different learning styles and different needs and come to a system like TADS 3 from different backgrounds and with different previous knowledge and experience. Different people will therefore benefit from different approaches.

For most new users the answer to the question "Where do I go next?" is either Getting Started in TADS 3 or Learning TADS 3. If you're still unsure which to start off with, take a quick look (by skimming) through the first couple of chapters of each and then choose the one you feel more comfortable with.

2a. The Getting Started Guide

Getting Started in TADS 3 walks you through the creation of two TADS 3 games (one very short, one quite a bit bigger), explaining features of the system as it goes along but not presenting them in a particularly systematic fashion (the order of presentation is dictated by the order in which the games are developed). Completing the Getting Started tutorial will introduce you to the process of writing Interactive Fiction in TADS 3 and in doing so will introduce you to many of the features of the system, but not in a very systematic fashion. It is the manual you should begin with if you prefer a hands-on, inductive learning style or if you feel more comfortable being walked through the creation of a couple of games before trying to branch out on your own.

2b. Learning TADS 3

Learning TADS 3, on the other hand, takes a far more systematic approach, and aims to introduce you to all the most commonly-used features of TADS 3. It makes no attempt to walk you through the creation of a game, although it does provide a number of exercises you can try out for yourself (in the form of descriptions of mini-games you can try to write together with heavily-commented sample code you can download to compare with your efforts). This might be the tutorial manual you would choose to begin with if you're reasonably confident about programming or writing Interactive Fiction in another system and you want to see how it's done in TADS 3, or if you prefer to be introduced to the material in an orderly, systematic fashion and you don't particularly need or want to be hand-held through the creation of a couple of tutorial games.

2c. The Tour Guide

The TADS 3 Tour Guide was originally intended as a sequel to Getting Started in TADS 3 for people who want a more systematic and comprehensive tour of the TADS 3 library than Getting Started in TADS 3 gives them, though it could also be used as a sequel to Learning TADS 3 for someone who wants to work through a substantial tutorial game after mastering (or at least, being introduced to) the principal features of TADS 3 introduced in Learning TADS 3. The TADS 3 Tour Guide is not intended as an introductory tutorial nor is it primarily intended as a reference manual (the TADS 3 Library Reference Manual provides the definitive reference to the TADS 3 Library). What the Tour Guide does is to walk you through the creation of a (fairly substantial) game, introducing nearly all the main TADS 3 library features in a reasonably systematic fashion. Many TADS 3 users have found the Tour Guide helpful, but you don't have to use it if you don't want to. You may prefer to try to strike out on your own and start working on your own game once you've completed either Getting Started in TADS 3 or Learning TADS 3. You could then come back to the Tour Guide only if and when you felt a further tutorial might be helpful.

2d. The Reference Manuals

The three reference manuals are the TADS 3 System Manual, the TADS 3 Technical Manual and the TADS 3 Library Reference Manual. These are the three manuals you will probably find yourself consulting most frequently once you have mastered the basics of TADS 3 from one or more of the tutorial manuals and are writing your own game(s), but for most people, they are not the best place to start.

That said, the TADS 3 System Manual does contain a lot of material that might be of interest to confident beginners with a strong programming background (whether professional, amateur or hobbyist) who want to see how things are done in TADS 3, and the earlier sections of the TADS 3 Technical Manual do contain a number of "how-to" articles that might be of interest to beginners. Nevertheless, for most new users of TADS 3 these two reference manuals will not be the best place to start; for one thing in addition to the more basic information they cover, they both contain quite a bit of more advanced and complex information that is likely to be not only unnecessary but also potentially quite confusing to new users of TADS 3; for another while the Technical Manual contains a lot of useful how-to information, it is anything but systematic or comprehensive in the topics it covers, and is much more useful as a reference for reasonably experienced users than as an introduction. You will also find the TADS 3 Library Reference Manual an absolutely essential tool once you start working on your own games, and Learning TADS 3 will introduce you to its use, but it's certainly not the place to begin. Please be assured that you absolutely do not have to master the material in these three reference manuals in order to start using (or even continue using) TADS 3, although you will eventually find them helpful when you want to achieve something beyond the basics.

2e. Using the Library Reference Manual

When you first gaze upon the TADS 3 Library Reference Manual (LRM), it may appear to be an impenetrable tangle, but in fact it's an extremely useful resource. To use it, you need to be aware of the following basic ideas:

  • The introductory page, which opens up when you click on the LRM in the Bookshelf page, contains a good description of how to use it.
  • The links across the top change the contents of the index pane on the lower left. The most useful links may be "Classes" and "all symbols."
  • Most TADS classes are derived from other classes, and inherit the properties and methods of those classes. For instance, in-game objects ultimately inherit from the Thing class. To see how the LRM works, choose the Classes pane (by clicking it in the top menu bar) and scroll down to the Door class in the lower left pane. Click on the word Door. You'll see a Superclass List, which shows which classes Door inherits from. A little lower down is an enormous list of Properties and Methods. All of these items are links. Clicking on any of them will take you to the place in the LRM where the property or method is shown.
  • At the bottom of the page for a given class are the properties and methods that are unique to that class (and its subclasses). These descriptions are drawn directly from the comments in the TADS library source code.
  • To the right of each property or method are two links: to the file in which the property or method is defined, and to the specific line on which it is defined. you will usually want to click on the line number, not on the filename.
  • When you click on the line number, you'll be taken to the spot in the library source where that property or method is defined. By inspecting the code, you can often learn a great deal about how the property or method works.

3. Jumping Right In: A Sample Game

The sensible and logical thing to do at this point would be to start reading one of the manuals suggested immediately above and, frankly, that's what we recommend. But maybe you're thinking that having got as far as installing TADS 3 and being shown how to compile a minimal game, you'd like to actually try doing something with the system before reading a whole lot of text. So, if you're the sort of person that wants to jump right in, you might want to try entering and compiling the sample game shown below. Few explanations are given — that's what the manuals are for, after all — mainly just the source code which you can either copy and paste into the Workbench editor (if you're using Windows) or whatever other text editor/programming editor you're using (if you're not on Windows) and then try compiling and running it.

If you do decide to type the code rather than copying and pasting, be very careful to copy it exactly. (However, feel free to ignore the comments in the code when retyping it. The comments are bracketed by the symbols /* and */, and are included here to give you a better idea what you're seeing.) TADS 3 isn't too fussy about the amount of white space you use or where you put line breaks, but it can be very fussy about other things, and even the tiniest error can confuse it. In particular:

  • TADS 3 is case sensitive. That means that orb, ORB, Orb and oRb will all be taken to mean something different. You need to be absolutely sure to copy the precise case (upper or lower) of all the letters and words you type.
  • Even the tiniest punctuation mark such as a bracket or a semicolon can be vital to how TADS 3 understands your source code. A single missing semicolon, for example, can confuse the compiler in ways that can be quite baffling to a new user. If you can't get your version to compile, check very closely for things like braces, commas, brackets and semicolons.

The sample game given below is given in three versions, each fuller and more sophisticated than the last, so you can start from something very simple and see how it might be built up into something more complex. Don't worry if you don't understand too much of what's going on — at this point you can hardly expect to, after all — just try copying and compiling the code and then playing the resulting game to see what happens. You may learn something from this approach, and you should at least start to get some kind of feel for what writing a TADS 3 game looks like. If you find at any stage that this approach is simply confusing and frustrating, then give it up and go and start reading Getting Started in TADS 3 or Learning TADS 3 instead. But if you feel you're beginning to deduce how at least some of it works, then feel free to experiment (but don't try anything too ambitious, you're probably not ready for it yet).

I repeat, there is absolutely no need to carry out any of this exercise at all; it's simply provided for people who are anxious to dive straight in and try something practical before turning to the manuals. For some people this approach may be helpful (as a kind of inductive learning which will then help make the manuals make more sense); for many others it probably won't help at all.

Version 1

The plot of the game (such as it is) consists of the player character entering a house, stealing the Orb of Ultimate Satisfaction, and then escaping with it. We start with an absolutely basic bare bones implementation.


#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

/* The header, shown above, tells TADS to include some essential files. */

versionInfo: GameID
    IFID = '558c20af-6559-477a-9f98-b7b4274cd304'
    name = 'The Best Burglar'
    byline = 'by Eric Eve'
    htmlByline = 'by <a href="mailto:eric.eve@hmc.ox.ac.uk">
                  Eric Eve</a>'
    version = '1'
    authorEmail = 'Eric Eve <eric.eve@hmc.ox.ac.uk>'
    desc = 'You are the world\'s best burglar faced with the greatest challenge
        of your felonious career.'
    htmlDesc = 'You are the world\'s best burglar faced with the greatest
        challenge of your felonious career.'
;

/* Notice that each object definition, including versionInfo, ends with a semicolon. */

gameMain: GameMainDef
    /* The initial player character is an object called 'me', which will be defined shortly. */
    initialPlayerChar = me
;

/* Objects in the game are created by giving the object a name that can be referred to in
   your game code, then by stating what class the object is (in the case below, it's an
   OutdoorRoom), and then giving it a name that will be displayed when the game is running. */

startRoom: OutdoorRoom 'Driveway'
    "The great house stands before you to the north. "
    north = hallway
    
    roomAfterAction()
    {
        if(orb.isIn(me))
        {
            "Congratulations! You have just got away with the Orb of Ultimate
            Satisfaction! ";
            finishGameMsg(ftVictory, [finishOptionUndo]);
        }
    }
;

/* The plus sign on the first line of an object declaration tells TADS that this object
   will be located inside of the previous object. */

+ me: Actor
;

/* Another room. Notice how the exits from the room are listed. The text in
   double-quotes is the description of the room, and will be displayed when the player
   enters the room or types 'look'. Notice also that text in TADS generally ends with
   a space after the final period and before the closing quotation mark. */

hallway: Room 'Hallway'
    "This hall is pretty bare, but there are exits to west and south. "
    south = startRoom
    west = study
;

study: Room 'Study'
    "This study is much as you would expect. A desk stands in the middle of the
    room. The way out is to the east. "
    east = hallway
;

/* The desk object has two single-quoted strings in its declaration. The first creates
   some vocabulary words that the player can use to refer to the desk. The second is how
   the game will refer to it when assembling text to show to the player. */

+ desk: Heavy, Platform 'plain wooden desk' 'desk'
    "It's a plain wooden desk; nothing fancy, just a horizontal surface on legs
    but no drawers or anything like that. "    
;

/* Notice that the orb is defined starting with TWO + signs. This will cause it to show
   up on the desk.
*/

++ orb: Thing 'ultimate orb/satisfaction' 'Orb of Ultimate Satisfaction'
    "It's -- well how can you describe such a thing? -- it's simply the most
    valuable and desirable object in the known universe!"
;

The particular points to note here are the way rooms and other objects are defined, and the way the + sign is used to locate some objects inside others (such as rooms). Note also how the direction properties of rooms (north, south, east, west) are used to provide the interconnections between rooms. If you feel you can deduce how it's done, you could try adding other basic objects and more rooms, with connections between them, but if you don't feel confident about experimenting, then by all means leave it and go on to the next version.

Version 2

The first version wasn't much of a game. In the second we'll complicate things a little by putting the orb in a locked safe to which the player has to find the combination, and we'll provide the house with a locked front door. While we're at it, we'll also define an object to represent the house from the outside. We'll put the combination in a notebook in a locked desk drawer, which means adding a drawer to the desk and providing a key for it somewhere; we'll hide it in a vase in the hall. We'll also make the hall look a bit more like it belongs in a big house by adding a couple of fake exits that don't go anywhere but look as if they do. And, of course, we need a key to the front door which for now we'll just leave lying around in the drive. Finally, we'll provide a proper introduction to the game and make it so that the game ends (either in success or failure) when the player tries to return to the road.

Once again, if you want to try this version out be very careful to copy everything exactly, or it probably won't work. If you already have some experienced with programming, especially in another IF authoring system, you may be able to work out at least some of what's going on in the code below, but if it totally baffles you it may be time to start reaching for the manuals!


#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

versionInfo: GameID
    IFID = '558c20af-6559-477a-9f98-b7b4274cd304'
    name = 'The Best Burglar'
    byline = 'by Eric Eve'
    htmlByline = 'by <a href="mailto:eric.eve@hmc.ox.ac.uk">
                  Eric Eve</a>'
    version = '2'
    authorEmail = 'Eric Eve <eric.eve@hmc.ox.ac.uk>'
    desc = 'You are the world\'s best burglar faced with the greatest challenge
        of your felonious career.'
    htmlDesc = 'You are the world\'s best burglar faced with the greatest
        challenge of your felonious career.'
;

gameMain: GameMainDef
    /* the initial player character is 'me' */
    initialPlayerChar = me
    
    showIntro()
    {
        "<b>The Best Burglar</b>\nWell, you've got this far. Now it's just a
        quick nip inside the house and out again carrying the Orb of Ultimate
        Satisfaction, an object that no burglar has ever managed to steal
        before. If you can pull it off you're sure to win the Burglar of the
        Year Award, putting you at the pinnacle of your profession.\b";
    }
;

/* The asExit line in the room below will cause TADS to interpet the command 'in' exactly
   as if the player had typed 'north'. */

startRoom: OutdoorRoom 'Driveway'
    "Here you are in the drive of Number 305 Erehwon Avenue, with the great 
    house you've come to burgle standing just before you to the north. The
    drive back to the road where you left your getaway vehicle runs off
    to the southwest. "
    north = frontDoor
    in asExit(north)
    southwest = drive
;

+ me: Actor
    pcDesc = "You're Alexis Lightfinger, burglar extraordinaire, the most
        professional thief in the known universe; but you're on a job now, so
        you don't have time for the narcissistic indulgence of admiring your own
        appearance. You're far too professional not to have come fully prepared,
        so there's no practical need to look yourself over again. "
;

++ Container 'large white swag bag*bags' 'swag bag'
    "It's a large white bag with <q>SWAG</q> printed on it in very large
    letters. Everyone knows that no real burglar would ever carry such a thing,
    so by carrying it you know no one will take you for a real burglar. Cunning,
    eh? "
;

/* The frontDoor object is in the same location as me and the brassKey. TADS understands
   that doors are usually scenery, so no special effort is needed to prevent the game from
   reporting, "You can see a front door here." */

+ frontDoor: LockableWithKey, Door 'front door*doors' 'front door'
    keyList = [brassKey]
;

+ brassKey: Key 'small brass key*keys' 'small brass key'
    "It's an ordinary enough small brass key. "
    initSpecialDesc = "A small brass key lies on the ground near the door. "
;

/* The next object is an anonymous object. That is, it has no in-code name of its own,
   because the game code never needs to refer to it. The arrow pointing to frontDoor
   tells TADS where to send the player if he should type 'enter the house'. As you can
   see from the description, this object is the exterior of the house. */
   
+ Enterable -> frontDoor 'large tudor house/mansion*houses*buildings' 'house'
    "It's a large Tudor house with mullioned windows. "
;

+ drive: PathPassage 'drive/path' 'drive'
    "The drive leading back to the road runs off to the southwest. "
    
    dobjFor(TravelVia)
    {
        action()
        {
            "You retrace your steps back to the road, where your trusty unmarked
            burglarmobile is still parked, ready for your quick getaway. ";
            
            if(orb.isIn(me))
            {
                "Congratulations! You have got away with the Orb of Ultimate
                Satisfaction, a feat never before performed. As you slip the orb
                onto the back seat of your car and climb into the driver's seat
                you tell yourself that you're now absolutely certain to win
                the Burglar of the Year Award!\b";
                
                finishGameMsg(ftVictory, [finishOptionUndo]);
            }
            else
            {
                "It's a shame you didn't manage to steal the orb, though.
                Without it you'll never win the Burglar of the Year Award
                now.\b";
                
                finishGameMsg(ftFailure, [finishOptionUndo]);
            }
        }
    }
;

hallway: Room 'Hallway'
    "This hall is or grand proportions but pretty bare. The front door lies to
    the south and other exits lead east, north and west. "
    
    south = hallDoor
    out asExit(south)
    west = study
    north: FakeConnector { "You're pretty sure that only leads to the kitchen,
        and you haven't come here to cook a meal. " }
    
    east: DeadEndConnector { 'the living room' "You <<one of>>walk through the
        doorway and find yourself in<<or>>return to<<stopping>> the living room
        where you take <<one of>> a <<or>>another<<stopping>> quick look around,
        but <<one of>><<or>> once again<<stopping>> failing to find anything of
        interest you quickly return to the hall. "}
;

+ hallDoor: Lockable, Door -> frontDoor 'front door*doors' 'front door'
;

+ table: Surface 'small wooden mahogany side table/legs*tables' 'small table'
    "It's a small mahogany table standing on four thin legs. "
    initSpecialDesc = "A small table rests by the east wall. "  
;

++ vase: Container 'cheap china floral vase/pattern' 'vase'
    "It's only a cheap thing, made of china but painted in a tasteless floral
    pattern using far too many primary colours. "    
;

/* An object of the Hidden class will show up only when the player thinks to search its
   container. */
   
+++ silverKey: Hidden, Key 'small silver key*keys' 'small silver key'
;

study: Room 'Study'
    "This study is much as you would expect: somewhat spartan. A desk stands in
    the middle of the room with a chair placed just behind it. The way out is to
    the east. "
    east = hallway
    out asExit(east)
;

+ desk: Heavy, Platform 'plain wooden desk' 'desk'
    "It's a plain wooden desk with a single drawer. "          
;

++ drawer: KeyedContainer, Component '(desk) drawer*drawers' 'drawer'
    "It's an ordinary desk drawer with a small silver lock. "
    keyList = [silverKey]
;

/* The notebook object needs some special code for the command 'open notebook'. The
   dobjFor macro creates some code for the Open action, and the asDobjFor(Read) code
   causes 'open notebook' to have the same result as 'read notebook. */

+++ notebook: Readable 'small bright red notebook/book/cover/pages' 
    'small red notebook'
    "It's a small notebook with a bright red cover. "
    
    readDesc = "You open the notebook and flick through its pages. The only
        thing you find of any interest is a page with 1589 scrawled across it.
        After satisfying yourself that the notebook contains nothing else of
        any potential relevance you snap it shut again. "
    
    dobjFor(Open) asDobjFor(Read)
    cannotCloseMsg = 'It\'s already closed. '
;

+ CustomImmovable, Chair 'red office swivel chair' 'chair'
    "It's a typical office swivel chair, covered in red fabric. "
    cannotTakeMsg = 'You see no reason to burden yourself with such a useless
        object; that would be quite unprofessional. '
;
    
+ safe: CustomFixture, IndirectLockable, OpenableContainer 
    'sturdy steel safe' 'safe'
    "It's a sturdy steel safe with a single dial on its door. "
    specialDesc = "A safe is built into one wall. "
    cannotTakeMsg = 'It's firmly built into the wall; you can't budge it. '
;

++ orb: Thing 'ultimate battered dull metal orb/sphere/ball/satisfaction' 
    'Orb of Ultimate Satisfaction'
    "It doesn't look much be honest, just a battered sphere made of some dull
    metal, but you've been told it's the most valuable and desirable object 
    in the known universe! "
    
    aName = (theName)
;

/* Notice how the double angle brackets are used to let the description of the safe
   refer to the properties of the object. This is an extremely common and useful
   technique in TADS. */

+ safeDial: NumberedDial, CustomFixture 'dial*dials' 'dial'
    "The dial can be turned to any number between <<minSetting>> and
    <<maxSetting>>. It's currently at <<curSetting>>. "
    
    minSetting = 0
    maxSetting = 99
    curSetting = '35'
    
    num1 = 0
    num2 = 0
    correctCombination = 1589
    
    makeSetting(val)
    {
        inherited(val);
        num2 = num1;
        num1 = toInteger(val);
        if(100 * num2 + num1 == correctCombination)
        {
            "You hear a slight <i>click</i> come from the safe door. ";
            safe.makeLocked(nil);
        }
        else if(!safe.isOpen)
            safe.makeLocked(true);
    }
    
    cannotTakeMsg = 'It's firmly attached to the safe. '
;

There are too many new features to discuss in detail here, but one or two of them are worth briefly pointing out. Note how the front door to the house is implemented as two objects, each representing one side of the door, and how they are linked by having one side point to the other (-> frontDoor). Note also how the north and south properties of the driveway and the hall now point to one or other side of the door. You may also have noticed how objects can be defined as belonging to more than one class (e.g. the safeDial immediately above is both a NumberedDial so we can turn it to a particular number and a CustomFixture so we can't pick it up and walk away with it; likewise making the silverKey a Hidden as well as a Key means the player actually has to look inside the vase to find it). You'll probably have worked out that a Surface is something you can put things on and a Container is something you can put things inside. A Platform is something you can stand, sit or lie on as well (we make the desk a Platform because it's presumably big enough and sturdy enough to get on).

At this point you may want to experiment with adding or changing a few things to see how they work, or you may think you've had enough of copying code you don't understand (in which case it's probably time to head for the manuals) or you may want to go on and try out the third and final version of "The Best Burglar".

Version 3

We'll make the third and final version of The Best Burglar just a little more challenging by hiding the front door key under a flowerpot, hiding the safe behind a picture, and making the clue to the combination in the notebook a bit more cryptic. We'll also make the Orb of Ulimate Satisfaction a tad more interesting by making it do something (albeit not that much) when it's rubbed, which means we'll also need to define a new RUB verb. We'll add a few decoration objects to field commands directed at things mentioned in room descriptions and the like, and we'll tidy up a couple of things, by, for example, defining the bulk and bulk capacity of various objects so the player can't put something obviously bigger inside something obviously smaller, and, for example, by preventing the player picking up the hall table when there's something still on it. Finally, we'll add some scoring and hints to the game.


#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

versionInfo: GameID
    IFID = '558c20af-6559-477a-9f98-b7b4274cd304'
    name = 'The Best Burglar'
    byline = 'by Eric Eve'
    htmlByline = 'by <a href="mailto:eric.eve@hmc.ox.ac.uk">
                  Eric Eve</a>'
    version = '3'
    authorEmail = 'Eric Eve <eric.eve@hmc.ox.ac.uk>'
    desc = 'You are the world\'s best burglar faced with the greatest challenge
        of your felonious career.'
    htmlDesc = 'You are the world\'s best burglar faced with the greatest
        challenge of your felonious career.'
;

gameMain: GameMainDef
    /* the initial player character is 'me' */
    initialPlayerChar = me
    
    showIntro()
    {
        "<b>The Best Burglar</b>\nWell, you've got this far. Now it's just a
        quick nip inside the house and out again carrying the Orb of Ultimate
        Satisfaction, an object that no burglar has ever managed to steal
        before. If you can pull it off you're sure to win the Burglar of the
        Year Award, putting you at the pinnacle of your profession.\b";
    }
    
    showGoodbye()
    {
        "Thanks for playing! ";
    }
;

startRoom: OutdoorRoom 'Driveway'
    "The large red-brick Tudor house stands immediately to the north of this end
    of the driveway, while the drive back to the road where you left your
    getaway vehicle runs off though a belt of trees to the southwest."
    
    roomFirstDesc = "Here you are in the drive of Number 305 Erehwon Avenue,
        with the great house you've come to burgle standing just before you to
        the north. The drive back to the road where you left your getaway
        vehicle runs off though a belt of trees to the southwest."
    
    north = frontDoor
    in asExit(north)
    southwest = drive    
;

+ me: Actor
    pcDesc = "You're Alexis Lightfinger, burglar extraordinaire, the most
        professional thief in the known universe; but you're on a job now, so
        you don't have time for the narcissistic indulgence of admiring your own
        appearance. You're far too professional not to have come fully prepared,
        so there's no practical need to look yourself over again. "
;

++ Container 'large white swag bag*bags' 'swag bag'
    "It's a large white bag with <q>SWAG</q> printed on it in very large
    letters. Everyone knows that no real burglar would ever carry such a thing,
    so by carrying it you know no one will take you for a real burglar. Cunning,
    eh? "
;

+ frontDoor: LockableWithKey, Door 'solid oak front door*doors' 'front door'
    "The lintel above the front door is carved with the date 1589, presumably
    the date the house was built. The door itself is made of solid oak. "
    keyList = [brassKey]
    
    makeOpen(stat)
    {
        inherited(stat);
        if(stat)
            achievement.awardPointsOnce();
    }
    
    achievement: Achievement { +10 "opening the front door" }
        
;

+ flowerPot: ComplexContainer 'terracotta small flower flowerpot/pot*pots' 
    'flower pot'
    "It's a perfectly ordinary small terracota pot, though it looks like no
    one's got round to putting a plant in it yet. "
    subContainer: ComplexComponent, Container { bulkCapacity = 3}
    subUnderside: ComplexComponent, Underside { }
    
    initSpecialDesc = "A small flower pot rests on the ground not far from the
        front door. "
    
    bulk = 3
    bulkCapacity = 3
;

++ brassKey: Hidden, Key 'small brass key*keys' 'small brass key'
    "It's an ordinary enough small brass key. " 
    subLocation = &subUnderside 
;

+ Enterable -> frontDoor 'large red red-brick tudor house/mansion/front
    *houses*buildings' 'house'
    "It's a large red-brick Tudor house with mullioned windows, climbing
    creepers and the date 1589 carved over the door. "
;

++ Component '(door) carved lintel' 'lintel'
    "Its most noteworthy feature is the date 1589 carved into it. "
;

+ Decoration 'mullioned windows' 'windows'
    "They're architecturally attractive, no doubt, but not especially helpful to
    burglars. "
    
    notImportantMsg = 'It\'s a matter of professional pride with you never to
        mess with windows. '
    isPlural = true
;

+ Decoration 'green climbing ivy/creepers/creeper' 'creepers'
    "The front of the house is festooned with green creepers -- ivy, perhaps,
    but botany was never your strong point since in the main plants aren't
    worth burgling. "
    
    notImportantMsg = 'The creepers can\'t help you burgle the house -- they\'re
        certainly not strong enough to climb and they\'re certainly not worth
        stealing -- so you may as well leave them alone. '
    isPlural = true
;

+ drive: PathPassage 'drive/path/avenue' 'drive'
    "The drive leading back to the road runs off through a belt of trees to the
    southwest. "
    
    dobjFor(TravelVia)
    {
        action()
        {
            "You retrace your steps back to the road, where your trusty unmarked
            burglarmobile is still parked, ready for your quick getaway. ";
            
            if(orb.isIn(me))
            {
                "Congratulations! You have got away with the Orb of Ultimate
                Satisfaction, a feat never before performed. As you slip the orb
                onto the back seat of your car and climb into the driver's seat
                you tell yourself that you're now absolutely certain to win
                the Burglar of the Year Award!\b";
                
                achievement.awardPointsOnce();
                
                finishGameMsg(ftVictory, [finishOptionUndo,
                    finishOptionFullScore]);
            }
            else
            {
                "It's a shame you didn't manage to steal the orb, though.
                Without it you'll never win the Burglar of the Year Award
                now.\b";
                
                finishGameMsg(ftFailure, [finishOptionUndo]);
            }
        }
    }
    
    okayRubMsg = 'What -- all of it? That may take a while! '
    achievement: Achievement { +10 "getting away with the orb" }
;

+ Decoration 'belt/trees' 'trees'
    "The trees are in full leaf, which is good, because they hide what you're
    doing from the road. "
    
    notImportantMsg = 'The trees are doing a good job of hiding you from the
        road, so you may as well leave them alone. It\'s not as if they\'re
        something you could steal, after all. '
;
    
hallway: Room 'Hallway'
    "This hall is or grand proportions but pretty bare. The front door lies to
    the south and other exits lead east, north and west. "
    
    south = hallDoor
    out asExit(south)
    west = study
    north: FakeConnector { "You're pretty sure that only leads to the kitchen,
        and you haven't come here to cook a meal. " }
    
    east: DeadEndConnector { 'the living room' "You <<one of>>walk through the
        doorway and find yourself in<<or>>return to<<stopping>> the living room
        where you take <<one of>> a <<or>>another<<stopping>> quick look around,
        but <<one of>><<or>> once again<<stopping>> failing to find anything of
        interest you quickly return to the hall. "}
;

+ hallDoor: Lockable, Door -> frontDoor 'front door*doors' 'front door'
;

+ table:Surface 'small wooden mahogany side table/legs*tables' 'small table'
    "It's a small mahogany table standing on four thin legs. "
    initSpecialDesc = "A small table rests by the east wall. "  
    bulk = 5
    
    dobjFor(Take)
    {
        check()
        {
            if(contents.length > 0)
                failCheck('It\'s probably not a very good idea to try picking
                    up the table while <<contents[1].nameIs>> still on it. ');
        }
    }
;

++ vase: Container 'cheap china floral vase/pattern' 'vase'
    "It's only a cheap thing, made of china but painted in a tasteless floral
    pattern using far too many primary colours. "    

    bulk = 3
    bulkCapacity = 3
;

+++ silverKey: Hidden, Key 'small silver key*keys' 'small silver key'
;

study: Room 'Study'
    "This study is much as you would expect: somewhat spartan. A desk stands in
    the middle of the room with a chair placed just behind it. A <<if
      picture.moved>>safe is built into <<else>> rather bland painting hangs on
    <<end>> the west wall. The way out is to the east. "
    east = hallway
    out asExit(east)
;

+ desk: Heavy, Platform 'plain wooden desk' 'desk'
    "It's a plain wooden desk with a single drawer. "    
    dobjFor(Open) remapTo(Open, drawer)
    dobjFor(Close) remapTo(Close, drawer)
    dobjFor(LookIn) remapTo(LookIn, drawer)
    dobjFor(UnlockWith) remapTo(UnlockWith, drawer, IndirectObject)
    dobjFor(LockWith) remapTo(LockWith, drawer, IndirectObject)
    dobjFor(Lock) remapTo(Lock, drawer)
    dobjFor(Unlock) remapTo(Unlock, drawer)
;

++ drawer: KeyedContainer, Component '(desk) drawer*drawers' 'drawer'
    "It's an ordinary desk drawer with a small silver lock. "
    keyList = [silverKey]
;

+++ notebook: Readable 'small bright red notebook/book/cover/pages' 
    'small red notebook'
    "It's a small notebook with a bright red cover. "
    
    readDesc = "You open the notebook and flick through its pages. The only
        thing you find of any interest is a page with <q>SAFE DATE</q> scrawled
        across it. After satisfying yourself that the notebook contains nothing
        else of any potential relevance you snap it shut again. <.reveal
        safe-date>"
    
    dobjFor(Open) asDobjFor(Read)
    dobjFor(LookIn) asDobjFor(Read)
    
    dobjFor(Read)
    {
        action()
        {
            inherited;
            achievement.awardPointsOnce();
        }
    }
    
    cannotCloseMsg = 'It\'s already closed. '
    achievement: Achievement { +5 "reading the notebook" }
;

+ CustomImmovable, Chair 'red office swivel chair' 'chair'
    "It's a typical office swivel chair, covered in red fabric. "
    
    cannotTakeMsg = 'You see no reason to burden yourself with such a useless
        object; that would be quite unprofessional. '

;
    
+ picture: RoomPartItem, Thing 'rather bland picture/painting/landscape' 
    'picture'
    "It's a landscape, pleasantly executed enough, but of no great distinction
    and definitely not worth the bother of stealing. "
    
    initNominalRoomPartLocation = defaultWestWall
    initSpecialDesc = "A rather bland painting hangs on the west wall. "
    isListed = (moved)
    
    bulk = 8
    
    dobjFor(LookBehind)
    {
        action()
        {
            if(moved)
                inherited;
            else
            {
                safe.discover();
                "Behind the picture is a safe built into the wall. ";
            }
        }
    }
    
    moveInto(newDest)
    {
        if(!safe.discovered)
        {
            "Removing the painting from the wall reveals a safe behind. ";
            safe.discover();
        }
        inherited(newDest);
    }
;

+ safe: RoomPartItem, Hidden, CustomFixture, ComplexContainer 
    'sturdy steel safe' 'safe'
    "It's a sturdy steel safe with a single dial on its door. "
    
    subContainer: ComplexComponent, IndirectLockable, OpenableContainer 
    { 
        bulkCapacity = 5 
        makeOpen(stat)
        {
            inherited(stat);
            if(stat)
                achievement.awardPointsOnce();
        }
        
        achievement: Achievement { +10 "opening the safe" }        
    }
    
    specialDesc = "A safe is built into the west wall. "
    specialNominalRoomPartLocation = defaultWestWall
    cannotTakeMsg = 'It's firmly built into the wall; you can't budge it. '
    
    discover()
    {
        if(!discovered)
        {
            foreach(local cur in allContents)
                cur.discover();
            
            achievement.awardPointsOnce();
        }
        inherited();
    }
    
    achievement: Achievement { +5 "finding the safe" }
;

++ safeDoor:  Hidden, ContainerDoor '(safe) door' 'safe door'
    "It has a circular dial attached to its centre. "         
;

+++ safeDial: Hidden, Component,  NumberedDial 'circular dial*dials' 'dial'
    "The dial can be turned to any number between <<minSetting>> and
    <<maxSetting>>. It's currently at <<curSetting>>. "
    
    minSetting = 0
    maxSetting = 99
    curSetting = '35'
    
    num1 = 0
    num2 = 0
    correctCombination = 1589
    
    makeSetting(val)
    {
        inherited(val);
        num2 = num1;
        num1 = toInteger(val);
        if(100 * num2 + num1 == correctCombination)
        {
            "You hear a slight <i>click</i> come from the safe door. ";
            safe.makeLocked(nil);
        }
        else if(!safe.isOpen)
            safe.makeLocked(true);
    }
;

++ orb: Thing 'ultimate battered dull metal orb/sphere/ball/satisfaction' 
    'Orb of Ultimate Satisfaction'
    "It doesn't look much be honest, just a battered sphere made of some dull
    metal, but you've been told it's the most valuable and desirable object 
    in the known universe! "
    
    aName = (theName)
    
    subLocation = &subContainer
    
    okayRubMsg = 'As {you/he} rub{s} {the dobj/him} a shimmering djiin suddenly
        appears in the air before you!\b 
        <q>Hello, you have reached the automated holographic answering service
        of Jeannie the Genie,</q> she announces. <q>I\'m sorry I\'m not
        available to respond to your rub in person right now, but my hours of
        activity have been heavily curtailed by the European Working Time
        Directive. Before making a wish, please make sure that you have
        conducted a full risk assessment in line with the latest Health and
        Safety Guidelines. Also, please note that before any wish can be granted
        you must sign a Form P45/PDQ/LOL indemnifying this wish-granting agency
        against any consequential loss or damage arising from the fulfilment of
        your desires. Thank you for rubbing. Have a nice day!</q>\b
        Her message complete, the holographic djiin fades away into
        non-existence. '
    
    moveInto(dest)
    {
        inherited(dest);
        if(dest.isOrIsIn(me))
            achievement.awardPointsOnce();
    }
    
    achievement: Achievement { +10 "taking the orb" }
;

//------------------------------------------------------------------------------

/* DEFINE A NEW VERB */

DefineTAction(Rub)
;

VerbRule(Rub)
    'rub' dobjList
    : RubAction
    verbPhrase = 'rub/rubbing (what)'
;

/* When creating a new verb, you'll want to modify the Thing class so as to provide
   default handling for the command. The defaults specified here will be used except
   on objects for which you define explicit handling of the command. */
   
modify Thing
    dobjFor(Rub)
    {
        preCond = [touchObj]
        action() { mainReport(okayRubMsg); }        
    }
    
    okayRubMsg = '{You/he} rub{s} {the dobj/him} but not much happens as a
        result. '
    
    shouldNotBreakMsg = 'Only amateurs go round breaking things unnecessarily. '    
;

//------------------------------------------------------------------------------

/* HINTS */

TopHintMenu;

+ Goal -> (frontDoor.achievement)
    'How do I get into the house?'
    [
        'Well, the windows don\'t seem a good way in. ',
        'So perhaps you\'d better try the front door. ',
        'Could someone have left a key around somewhere? ',
        'Is there anything lying around where someone could have hidden a key? ',
        'What about that flowerpot? ',
        'Try looking under the flowerpot. '
    ]
    
    goalState = OpenGoal
;

/* The closeWhenSeen property of the following Goal object is an example of how to
   make your hint menu respond dynamically to the player's current situation. */
    
+ Goal 'Where can I find the orb? '
    [
        'Something like that is bound to be kept safe. ',
        'So it\'s probably inside the house. '              
    ]
    
    goalState = OpenGoal
    closeWhenSeen = hallway
;

+ Goal 'Where can I find the orb?'
    [
        'It\'s sure to be kept somewhere safe. ',
        'You\'d better hunt around. ',
        'Somewhere in the study seems the most likely place. ',
        deskHint,
        'But it should be safely locked in a safe ',
        'Where might someone hide a safe in this study? ',
        'What could be behind that picture on the wall? ',
        'Try looking behind the picture (or simply taking the picture). '
    ]
    
    openWhenSeen = hallway
    closeWhenSeen = orb
;

++ deskHint: Hint 'Have you tried looking in the desk drawer? '
    [deskGoal]
;

+ deskGoal: Goal 'How do I get the desk drawer open?'
    [
        'Have you examined the drawer? ',
        'What might you need to unlock it? ',
        'Where might you find such a thing? ',
        'What have you seen that a small key might be hidden in? ',
        'How carefully have you searched the hall? ',
        'What is (or was) on the hall table? ',
        'What might that vase be for? ',
        'Try looking in the vase. '
    ]
    closeWhenSeen = notebook
;

+ Goal 'How do I get the safe open?'
    [
        'How carefully have you examined the safe? ',
        'Where might someone leave a clue to the combination? ',
        deskHint,
        'Make sure you read the notebook. ',
        'Once you\'ve found the combination you need to use the dial. ',
        'If the combination is a number larger than 99 you\'ll need to enter it
        in stages. ',
        'For example, if the combination were 1234 you\'d first need to turn the
        dial to 12 and then turn it to 34. '
    ]
    
    openWhenSeen = safe
    closeWhenAchieved = (safe.subContainer.achievement)
;

+ Goal 'What does the clue in the notebook mean?'
    [
        'Well, <q>SAFE</q> might refer to something you want to open. ',
        'Have you seen a date round here? ',
        'When was this house built? ',
        'Where might you find the year in which this house was built? ',
        'How carefully have you looked at the front of the house? ',
        'Did you examine the door? '
    ]
    
    openWhenRevealed = 'safe-date'
    closeWhenAchieved = (safe.subContainer.achievement)
;


+ Goal 'What do I do with the orb now I\'ve got it?'
    [
        'Well, you could try rubbing it. ',
        'But the main thing to do now is to escape with it. '
    ]
    openWhenSeen = orb
;

Doubtless there a great many more things that could be done to improve this game, but it has now served its purpose.

Once again, there are far too many features here to discuss in a Quick Start Guide. One thing in particular to note is the use of the ComplexContainer class. We use it for the flowerpot because the pot is something we put the key under but it would also be possible to put things inside. We also use it for the safe — and this is a more important case — because we have now made the safe door a Component of the safe and the combination dial a Component of the safe door. If we had left the safe as an OpenableContainer (as in version 2), the safe door and the combination dial would have been locked inside the safe along with the orb, and the safe would have been impossible to open (a mistake it's very easy to make, so this is worth noting). Another thing worth noting is the use of RemapTo() to redirect certain actions that the player might reasonably try on the desk to its drawer. Finally it's worth pointing out that there's more than one way we could have implemented many of the things shown above.

There are plenty of other feature of TADS 3 that haven't been introduced yet. In particular this example doesn't even begin to touch on the creation of NPCs (other characters in your game — the fleeting appearance of Jeannie the Genie doesn't really count). But the three versions of the game shown above should have illustrated quite a few of the most common features of TADS 3 used in developing TADS 3 games, and depending on your background and inclinations you may have learned something by studying the sample code and trying it out. If you feel confident enough to experiment a little more on your own, by all means do so, but at this stage it's getting pretty near the point when you will need to move on to Getting Started in TADS 3 or Learning TADS 3.

Eric Eve — May 2012

frobtads-1.2.3/tads3/doc/getacro.gif0000644000175000001440000000262110476527522016415 0ustar realncusersGIF89aXæTþ]\ôÿÿÝÝÝü?=þßßâââÈyyÿõõðþÏÎÿÿ55üOMÙ——ÿÖÖÿççÌGGÿ……þÚ¦¦þ¯®ËÒÒÿðÙÙÙ··Ë**îEEöööÊÂÂæþ¿¾îîîÿ¹¹ËËËËÏÏþ ý/-ý}÷øøÌÿááÿ””ýŸžýomÿBBÿýýÿ¤¤ÑÑÑÿêêêééËŠŠÆÆÆÿqqÿÃÃÖ×××ɘ˜ñÝÝÉÈÈÿË””ÿiiÊËRRÌ ÿûûÿËËýÿÿÌÊÿÿÿÌÌÌ™™™ìì숈ˆ»»»ýŽDDDfff"""wwwþþþUUUÿÿÿïïÊœœ333ªªªþÿÿíííÎÎÎÁiiÌÍÍÍÍÍÿ˜˜òòòÁffÎhhüüüÕiiî––ÖKKØÑÑãëëð@@ú»»î||ÌÊ££Ì¾¾Ì˜˜Û00ðâ""ðÔ__õààÿddùˆˆðµµÿOOð!!Ë66ÿÿJJ!ùT,Xÿ€TGƒ„…†‡ˆ‰Šˆ^ ‹“†T‚F˜™š›œžŸžlj¤f ˜:!/Z˜®˜CIG—§¶·¸˜ ¼ §a3G\°šZ³HG¹ÍΛ~½ §_G«œÉGËÏàμ#`§&7!!GRœÊÌᜠMÕ› %÷¸)¼,Þy"“*Û—ð¼ÉsF ½:Alv€¯=`€ú cЪ0›¶(äÄDÀ©&$:1ˆÈiå'û2è1BAŸ;B>™xÁ.Û ˆü–IÉ& 11ÙÔd@LMYB%€‰T–Fª4¡`¤ÉŠN"øS° EÌL;ºa"Ó‡‘˜ÿŽ<ÑtDÉ&Pd¢PÏ!¦ªˆ¨¯ ƒj šxÐ$«Cg9¹`чׂ|´È@„Ï"D ±ãD DDY(@I%F¦h*á%¦|U#2À¤Â¡K)›hÒD…­%ìèE@ .@¬™€~œQ3AÔNŒ0qÂäI”(wacª²ÂÃb®Äs{Ømd¾—F$hÁŸ‹@=$ŒÕ‹#ÐQ'$‚ PFjS˜$[x˜ ±&MPÕÕWзR`BAc¿}UÂWY9“+èÁÂ0’°@@Ád  c¤&—Q‘Űe¤*H`œÿ+ŒàÐÃ1V… í­PÅ}ífp+Ø3„f^„ð†m$xB]¤ö¥Õ\‰ XÕwVdoQ€ :ªym&õæ ƒ>€‡ >ø™ nârRÎ8¡¡ž8€(z4j K\‚‚²„z”jÒB ,ôÒŸDüW. .Å\¸œºÑ…δ9¾šà"ÌŠK­F8!›X\ÁLP`qdKL¡+³Î&LàõŒ¯À¨À«|TБ§§€ª„Kt»ÄJ„'À[KdÄž *¼á9qêà€+î€sèàÅ0è‚êXÈöF-aÄHXkî…§>q²W„į÷bipÁNŸÔ*@½S@¼®O\¡î+Ñòº°9Aa¯5„;ð`ÆÞ‚,Q 3K{Ä©ì‘Å©J3ìÎÍ GȽH€B&1ýi»0w¼­®J;q…]OT»¶jP誳3RTÄõE=hOª|ƒÒwó"†˜ Uj߈ ABà}­‰áˆ¿)Åä”W®…ÝÀã %áùç ‡.ú褗nzéÈÀꬷŽÀˆî ”ÔnûíŠpÀƒ¼÷î»nk–PÄñÈ'¯üòÌ7ïüóÐG¿¼%;frobtads-1.2.3/tads3/doc/bkg.jpg0000644000175000001440000000444210477040125015540 0ustar realncusersÿØÿàJFIFÈÈÿÀddÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?÷ fmœŒub?Ƥc–Ù¹ÝjÁsÛã@Xó•ȘëƒÖ€Ûì%°À¿.hÊàâYHô Ò€"^F?~WèhÞVHU·v¤Ð…³dæÕ¶úŒ9bÃmȧÔÔ¶ Ÿq@ R ãÍÔ*P ùÎÓ9çå4¾J7%&É  iø–&ÁìÀPo#î¦bÔŸo‹vh=Å*ߢç-×ÐPÉw% ûœb€ÝÄÞøj‰®cÉܱ0>a@]Æ €zn û|@ýÄÔP‹ä ’à@(Hï#cÃèØ¯uDDŸö¨­q/Q@kr ÎqíH¥‰W>L›~´,wPlâÙ¹îOJk\À¬A±ê(’\ÚD„»@ 7ÃþYOø-Ë=³dg'¯JS,[Kdǹ Ãuo·þ=œŸ®(âܸÀA@ ’âÏfG™¸õhŸhµ<ˆç#Ùh¸¹¶Çú«Ê€# tw¥}AбJXfó÷ã½8ûrþb€\…?é1î>Ôtˆ’?nh-׎@3ôý·[È7C8ê ˆæ8Ýy‚}Å<ï¶ÌP«r×1Ÿz\³å$Œ¯©QÍFÒ^ò<ô{@*ÞíiúP2ÄõêM>Y¬ˆù-¸¦€dµÀÍ®­4Ëiœln½…­6’PÞ¹ ²v玹àPŒ–# œz𑿲*[’G­5dµÛÿ¼zÐÒZdüŒ=€  dÌ3¤Æ 0ý”à”‘Ö€†Ë16sýêHîNcCJ1õô8 ´¬æÆ}Ðo,£Ƥ}(¹*@œ÷@'¸Grwå¡â€%YF3å®O± ¥o¼@°ÐM,¸Ï–¤cÒ€“;c¢Ð„Øá;@ 3ôÿÇh@´`ÝüÇÒ˜†P žI4É]@Ͻ í B{Ô`?2Þ™ ’hr[ãœrzÐ54xlv¦ˆÀ'ú° îM$®€iÁ8Uæ ÃØÐç[¸ù!*qëšx6d[屜æ€pÃ"Á÷  #§ÆTãµ0,¡éȸ û¾ÝÙqҀءÂãë@Ö1¡ÆÐrxç¥0X‚2»N=èa§¦ÐŸJh²„qÎ>”¿b€‚I{P}Š2>P¿Ò€éñªîÚ¸Ç 57ì*Í·èM8Xœ 6%û`ÐÏ"ÿÓë@¬íƒ‚Þ€]úyl rà›¹äІ\B@îøe–B@CÓ€í"õŸZ`iœ#°ï@.ãsÖ€#bw‚»ƒf€0ÉRÙô4†á”áT`{Рû0n-Ðî b¡Ì{z‚Ù Y·zs@Ê|È PPöé!y'§­H'‡j€hTû2©,-Ç Ò€( ÀÞÎhV~J„O΀K°±xêh¤¯o¿{ ¹€³C† €ï@ÞA»bsï@erÁCÆÛGû"€çÎcöý0»†ù•‡¦q@iØã¦hHéÜxçœR$‘–ǵ0%•ÛfÖ¶ƒýÑ@Ü@òc|}5Ý·|ÈÀ{â€ç0CŒØšE_1òNwr½( =óŒP±—#míÀ u|{á€@ÿ„C>„ßÅÿÅTó.æ¾Æ§ò¿¸sxïà >_è<Ú„_üU˸{ŸÊþâFñ×…xcâ?±ÿ°„_üU˸{ŸÊþâ3ã¿ c-¯èDú‹ø¸ÿǨæ]ÃØÔþW÷xßÂl~oè cø˜EÿÅQÌ»‡±©ü¯îÿ ¯„ã\'ˆü?¸wþЋÿŠ£™wcSù_ÜF<{á€Üx‹C<ÿÐB,èTs.áìj+û‡7Žü0h žxÔ"ÿâ¨æ]ÃØÔþW÷ÿ„ëÂÅoh$ŽßÚñTs.áìj+û†ŸøKnOˆ4":cûB/þ*ŽeÜ=OåpÁã ±Ïü$:@qÍüYÿШæ]ÃØÔþW÷øEwâM“Èÿ‰Œ_üU˸{ŸÊþâ7ñ߆cþM þ¨Eý ŽeÜ=OåqðåsøP@P@P@ÿÙfrobtads-1.2.3/tads3/doc/nodoc.htm0000644000175000001440000000162510502532704016105 0ustar realncusers TADS 3 Bookshelf Not Installed

TADS 3 Bookshelf - Not Installed

If you're seeing this page, it probably means that you installed the version of the TADS 3 Author's Kit that doesn't include documentation. This page is currently attempting to redirect you to the on-line edition of the documentation on the tads.org Web site. If your browser doesn't automatically display the on-line documentation after a few moments, you might try navigating manually to this page:

http://www.tads.org/t3doc/doc/index.htm frobtads-1.2.3/tads3/doc/gsgcover.jpg0000644000175000001440000002010310476335125016612 0ustar realncusersÿØÿàJFIFÈÈÿÀ´ÿÛ„   (!%"/#%)*,-,!140+4(+,+  ++++++++++++++++++++++++++++++++++++++++++++++++++ÿÄ¢ }!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùú w!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?Á¸‚`¸y®VFXÄlbA†Œ…ö ŽO~•椯d}D\ä×*²r¿Ë«ùö0ºV—Lô€ŽÔ'}A M'k!‰Ž}ê¹…aHõ©R}×qE+ ÓM½Å ` o@µ€ôæ…¾€Ã¥LŽÔ'}A M'k!‰Ž}ê¹…aHõ©R}×qE+ ÓM½Å ` o@µŽªÕ!)«4ì¦t¶ªß8éèqõ⹕ùW©áëÊ»s|ÎTŽ0Oå]W³ÐöÖ¨µ%g¸Àjap?Î…`gÖªVì$(ëÖ¥½(Î}ªtCÒ…{ƒx5qÕ ‡QÉ¢ü¯@Ý }©+=Àö¡¦üèV}j¥nÂB޽j[ÐbŒçÚ§D0=(W¸3¡e„Z\¥­¡ybj¬‡qU9'óŽÙ¨ç[žTeÍiÎz9kòÙÁ9ï¼@äú Ñ'Ðõ]Eš)mÛdѼo×k. ^’w&2MhÆŽµذ$œÕ(µ¸®˜:Òjûwâm¨¥gqè(<{PÓ¸&'~µMÙ{Äé;Æê¬Tàý*R}™7b1À«ø˜¶u¨ÖÅ ôæ©E­ÅtÀqÖ“WØ¿km@8Å+;AAãÚ†Á3¨Ó.á‚ßRÂHVæÑ’#_™H‘Y³žÀÖ°åi|ÏÑ’Jû©jºk·ô†FÐ,Ï÷w—åÙ®:2  lõ¨‡¿VIí}ï_ò;´«Qóm…›Í:ââM-fî9¢•ÌI,y’>ãÁÉ ýhU`”ýšÕoÓ[¤W;¦­ë°ÝOCÒ4»åŠòâò(ü§|¥˜†ÂãüÝ}9Å8UHsE'·_øRÄUœo·ÿFЬïRÞ{ËÈÏå¶ò¥ñ×!Gãù«Ôœ£»[övkW”n’þ¬WÖ´½"ËOŠX'¼3Ï Í Èƒ×5TªÎ¤šµ¬íù™tªÕ”¬Ò²v,I¡éCXÓlÒkÝ—p‰w¹û¿Èçð¬Õyºr—.©¾»ÛäJ¯W’RvÑjþ·ÓôÙ]¤¸’ä̱Á´.EnTúôñ«¡]ÎJËK?T×AÃ*’·Kj%χíí⸀Ít÷Öñïrf ØÎÍÝsŽý)Óªê%%³q2m;+7ß_S Á¡Këy.µºÈ¦Aê¹æ¶•ùZ‹Ôéšn.ÛØìuK Q »’nÎWoçþHªT'ìšoVTÖu›}JHHÓü¥YZI31bù9* (ëÀãœÖ”àéÇ{è]*…×7NßÕȼEªÅ«ÝG2Z}Õ6·ï7n§aŠœ=e_«üu.'I8·qúî¯o©Ch‘XýŸìÉå©óKeGAÓõ§J#–·»¸¨Ò•6Ûw¿‘rOÛ6§axš^Ói–«ö‚rÝíÛŸ®jVÑ”y·oñ2Xiò8óoäR½×$¹Ò’Ác) Sù°åŒkƒ…Ï|g­i1N~¶4…Ys÷Veé|Y+Ú](´TººM’̲°ã‚ôë%„§xë¤]×õØÍa,Öº#;I»±K+›=JÚ)Y]d€€êËŸ^ÃÒJ\ÊQfÕa7%(=PëíRî,P@ïcf6¤NøgÉ$Ž„ût¢Ôy¤·“ÿ€(R’O_y“Üë¶÷ MQôÞW¢óŽÇFÎ>œ{R§AS¤©©T>oÀ›Pñ1Ô´Ùm/­<æ23Å!\pœgñ¥ìTj*©ô·¨©á9&œ‰[Å ZÛÚÇ`>Ï~SÄóY1޽d°qrs¾·¿¦–Õ3nZúm5‹Km3P³vVí²OžFÀPtçó­¥EÊq•ö4•9F\Ûy}ãô]z ;M’Õ¬Zo1û­ÁBHéŒ ÎŠ”9ª)ÞÖ¿âMZ©.dÿlõÛh!Ô£—LY#¾“s*Ͱ*ƒ•Pì~”çGšq—7¡7ÊÔ¶]ŒÝ_Q›SºJ©DÇ *(èU:q‚²ïsjT½œlëf»µ´Ô£‰âϤÍ÷A`ÊG«1^?£“¡åƲŒ[ÒZ5øýÇ(}«uÝž¿¹ãŠ‹k©BgŠ|º…Àõ¢6°0Å;ˆ\ v»liÚ ×q34ÐÀÛ7¢9%ÜûFN=ñXʵžÇ=LDa¢WÔ‚]î5…‰€ùÒâÛ2Ÿ0žã þ¢©N.诬E¿O!÷ÚÕœ pÛ)‰\ç,8„Ы);u:ð“å[Ø‘4 Œ“µÍ²Hµ¢/’§ý¢8_ÄŠJ²¾Ä¼JæQIÿ_™OQÓçÓÌis嬬3å‡ Tv'½©ÆJFÔêÆ¢÷Jy⯗SKëDm`aŠw¸6ívRM¶0$ði¤¶g[gd—v:œÅ ¸¹hÕ‹08@Þã zW?3ÓÔñ¡ZQŒyw½¯ÒÝŽHgw®‡kÜöPu4¶Cêà|Ý`&º •à–6â‘I€TŒƒÐÑ«D©G£ñÈ…|ÄeÜ221‘ëEÒØiÜs\ÎeŽO9üÈÀ¶ã•¦=1O–6ØžUªîXžþú{ȯg–Gž2 9^˜ä{T¨+5DiSŒyÐIuKÉ"Ž7¸r±¾ô0Þ¿©üèTã{¤ ”Ú[Œ´¾ºµ¹7Né9ÎçÎwg®s×ñ§(Åï±R§Ç•­¦–Iæyfvy噎I4’å‘qŠZ"

Óž¥8¾‰¯+ßüŠŠ”e:P}4ãµÍ§4ésþ™$áá•x0u1éÏéÛ5Xj©Ë’JÒKUßÏÏú¸°Ê Vµ¥o¼ã1‚+¶÷G}Ÿ Éß2ËöÉz»•@<€:xü«:—彯èaˆO—Kü‡xÂÍm5é„0G ¼€IŒü¥Hê?k,5NzI·w³õA…“•5}ш3ƒï]:€Èô;=Ūç@ç=ê¢ì&uoå%µãÜJ-Ÿ–7y¬OȪ?„Œœž8®zq¾ýÏ"‚n1QW÷¯éÜå;t®‹jzý?ZI40ü*}FÎi¶––LÞðæ±e¥ÛÞGwg-ÃÜ©ŠË´l#¦1×Þ°¯EÔåq•¬ï±Í^ŒêI4íb=S±Ó5®d³šn¢%ÆGâ³ièîEHÍÛ‘Û価&§t&tXÒ4ÇtD½Œ)®T·¿â*T¹#k”{óZl´4õü ÔéÕŒ1ßµ;±XL€9§ÊÛÐ.‘×n‘-&‚ÚHRû“†Þ¬Ãs“Ø’éÇ­sJWi£Æ”” ¦Þ®[þ‡&ÊUŠžµÑtõ=”!¨Núƒ@:šNÖCûÕs; ‘ëR¤ú ®âŠV@4àzzš¦ÚÖá¹;$XK;†¬ ÏMØ\ÿßX®ybà´f®Œa¬æ—Ï_Âäóéwq[‰L^bóˆÎòŸ\UB¼%±”y&í&ýÎÅA‚=Åjšz qqv{ŠGjißQ4©¤íd11ϽW3°¬)µ*O Úî(¥d:b©·¸´ Bmè±ØHšT·1À.ìÅ \…( !ÜŽ¬Ú9å¾µ”JϹäA85 ;{×¢8[_•èzû¡Oµ%g¸>Ô4Âà À Ï­T­ØH 'sš–ôZž™ð÷ÃZrÁo¨kÉy0ß 88‰{?¼zûV*TÛ´™æã±•UéÑÑ-ß/OÌãþ0h°\x‚{´¾–‘Œ§'€:Öo¡S’1¹æ¬;>yJÅ×+mâ‰W^h–ݲ%à. oZRM]XÊ”SNÎçcãO ›¦—P±ò>Ñ´¼‘Â6‰ëÇ÷¿sR©(J÷¹íá±z*U6èûÀüŽX2†S•#"»™Þî´jb¸çB°3ëU+vuëRÞƒg>Õ:!éB½Án£»Ri¦–ÕZ£r€8nyæ±mÉ/&x‘\ÔÒKij¯øœ“íÜ@äzâ·Šgµ}58ÂŽµØ $œÕ(µ¸®˜:Òjû5Œ)qi ¿ê¤™ñÝw ʳªÚƒ—*mv=¾Í.Úí8­öpÄcù×iÕœ“KK ªSŒZo[ž=ñŽ+ËínXíà%•@a¸8«ÃÎ0ªÝGaVŒ§M(v9¯„^m‡Žm Ñ• •ù®ìMHÊ)§ÔãÃÓ”[Mt>ˆÔµ9 ›b˜@#FI?N+Í«ˆ—?*g}* “™£Äõ›_°ê×–ý–f Àœú×}¾EsÚ…Nx©w*µZØÐ §5J-n+¦Ž´š¾Àø£[jÆ)YÜz Ô4î ­ã²iÒDÎ-µ«dó±æsضå>Ø÷Åeðhx±ä‚Ox©iëßäqDÖ‰ÕÄsTÞ–)=F´M6€(ø'k[ˆnw˜dY6ÿ{kǨš¼AÇ™8¾¨÷í5ÒæÖÚH¯"hJ†]’™HÈþb¢ ÿhùŠ©ÂM8ž3ñ'Ux–æ/³ùÙî?ʸcCÚNRæ¶§k­ÉºÕžãĺbÜZùi çjÝÓ\ÊW#ÚºŽÍ÷©› mÃL<´_›Ì+·hyúVóös¶†ÕTùQà×·"öúæåA ,¬èP¤¿¦+¢ì>Ž0äJ=‘â›Ô¥ i´@…M'ªÐb7N´ãtÄàéIjDz;]VÆ14QQf³\«H@GË`äýíÀcJçQæWìx“”#w¯5–ŸÖÞgä;Fxé] cÚ·Aƒ<ñTíÜÆN)^Êá`=Ç\л€Qfž8¡Ç]BçYà_&„ZÚþ-›Ÿ’EhëúzÂPPmÅbp‹“NÒüù2¯‰t¨µ­BmJÆú&~è<ôë^JĺwR]LëeÕU¹“Z|¾ót[‹)íï..R!€í'€{VÑÅFIÆ(Â}i;Å\èü]â§ÖaVžjY ´œ4žØì?vS¤ß½#¶†45nòü¿Íœ¸ïÅv;w58¥{+…€÷sBîE˜xâ‡u ‰Î)ép4 NËU¹ˆÙ-„²›„H£3O‚¬dñÝU\ŒàçðŒ­n§Žœ£iÁZïEåÿãO ­—v{³Ç×Q‰ž)òêÖˆÚÀÃî p(mÚ줛lb0€~¢ŽH½ÑQ«8/uØ|’<¬Ww*0 6M.‚r“Z±‡ÐU.ì‘Ù㊋k¨ÄÏùu ëDm`aŠw¸6ívRM¶0$ði¤¶gg«¢Í5úI-­ª@ïc™ vD›@äŠÏ•=&1çJ*:9_^Ý_Ìã]ÚF,ç,NI­–§¬••‘Ñèš6Ÿ{¦Ü\ÜMsº(ÙÛfT€NÞFXàdãÔW5J³‹I#’µz¨¢’ÕivzDÚL×m{çÁƒ ˆ¨\ÀÆEi9ÏšÈugZ5¬ÄƒÃ“I™ï¬bŒÄ²“$Œ08íׂ):ªö·Q¼ZNÜ­ëaÍᛥâ6e•ü¡‰xi9ù{ühu•­a,\-{=¿äOá뵋O‘¥· zÁc;ÊOcÇáÆiª±ÖÈ¥‰‹rV~éÚ5ÜVÏråºÜs&NÜŽý>ï½TjÅÙ[¥ÊUâåÊ·µÇ&–-õø´ûæ,¦Q<¹ÇBGb}(çr‹q­z^Ò=º—¢ð¼÷bi­.-ÒÕe‘s4„2?ÅéÍC­µ×C'ŒŒ-'{/Äjx^æK¨m¢¼±’Ic2.ÉåF?Ùïž>†‡Y]»1ýr*.M=R=éíä›| ûc2|ÒûÅG|`Óö©hjñæ·§âX ]­Ì‹‹V26Íêçj¶ÝÀ3Èö¥õ…mŒÖ.®Vzà ›Ã·0Ù­Ó\ÚÞ=é‰[‚p2:àQíVÖ*8¨¹rÙÜxnS%º¦¡§¹œ›e'8“Ó§}hutÕõ¸êÜ^„qxvîD‰¼ëP\¦TÈrþé#?m²ÅE7£ëønA­h·:G“ö™!-.í¢6'½;ÕBqšÛb¨â#Vü¦g«Õìoêwž(%u8cŠ Ì,aY—îÆýyç§§~½k•^Ç…ýš»Ó›æpކ7dlg<×]î{ŠÖº4[ZÔ<ÂÑÏånB„D¡¯wîk%ØËêôöjú•m¯n-¡ž&dŠq¶UÄ=ÿ3U(§«4•8ÊIµ°¢úèE By<¨ti»…> RqV»êÎ7nÛ“ cRón%“‰.?ÖøÝÆ(pƒ±Â’åØUÖµ$HQof 5ÝÂã§Ô{87{ êôõÓqÇ]Õ ²ý¶\7gƒÎ™¥ìaØ>¯KùDmOR»Ôm§7R=Ú±9 “ÿ׫Œb“V£NjÚu.jSkvMqms5Ç–ŽáÙs°–ûÜã¾ïÖ³Œa$¤—B)Æ„í(¥ò(Ǭjº:]Ê®‘y AÁ ýÚ·N/tjèSjÖë˜×ÕuŠXšòs§2)s†úÔ¨BÛØSM>]‚ëV¿ºš)n.æwˆæ2[îŸQéMSŠZ …pM%¸NùÀ7s‘?úßœüÿZ\‘²¸Õ]Z;l,:¾¡Ùü»™ìàˆ¹û€õÅ76K¡{­÷ÕõHQogÛ nŒo8SO’ ÞÁì)ë¢Ô†òúæô©º™å+œ9ÆNOëMEGb¡N0øQY‡N{ÕÅØ¦vå´6%¬V.e†ÝRäl±V´½{“Àíž‚±ROSË„œ’”¥£–¿-—üíÒ¶¶§©ÐSõ¤“C§Ô`<æ›iia$Í[jפÑXÊñ¸ ¬1‚=k)W¥9–›˜KI;6e°eb¥NAÁö­4{³{öûS» ‚f‚h匀ñ°e8εKk™GëZ$ÑÐ…O¨ÀyÍ6ÒÒÂI‡~iì´PïÀÍNXÃûS»„Èš|­½é·ˆõ{ír9í&sj-#HåÌä"€Ü¸ õôë\Êß3ÅTe$Ö¼Ú®šíý#Œ•Ude· 'kcJèºg´¯mF‘Ú„ï¨4©¤íd11ϽW3°¬w:RBÞÒâÿìf+‰¡$gŒžã^coë²½â¿6yÕ¹•Y%éþE éÖú¥¶£%Í”·pº°T“amÇcÁ5¶"«§(«¤ï%~渊²¦â¢ìŸÌmŽ“§Âc.— ’â׿U`ø*Àdçq‚)νE‡ö«GaN¬ý‚šÑ•lôû7¶Ô59ã”YÁ HàGù˜“À,G@1Ú´•gÏKvŸáÿi9Í8ÁnÖæf¤-À6oÊÙJœr3ÆyïZÂýw6‡?/¿¹×DQ|=£Ü½÷‘uµÁ…{ÊsÂóÆ: wí\TÿU%§2ü‘Á$ý¤£ËtÚ¿‘›k¦YÞø~9í,å’ö9Ò)—ÎÇSÔ t==¿ ×ÛJ5¹e³Z|·6IÂ¥¥--tI¤Øéþ'žÍ-ä6Cçw^§8èiV©R.·_‹°ªT­ JMëèdø–ÒÚÏUh,Тª.á¿xÜFxnã‘]4æÜuÿ#\4¥8sHË#Öš“ètµÜQJÈtÅSoqh„ÛÐ-c·ñW/®\ÙÛÏóàˆG,CbÚ0ƒ¯ƒ‘×ô¬•M.x°œ#É­¥·[÷ÿ€pýG&¶¿+Ðö·BŸjJÏp}¨i…Àÿ:€êmõ+”Óí­M¾‡,0  ‘Á9ÇSóu5Å'OÚ9ÚI¿'þG¢œœ¹¥¯õØm¦¡_* ,½ä†I$i×p$ä`ïã‘ïNs¦ÜdÓ÷|ŸùT`Ú»z.ß𦥨&¤šH’éSas4yoöŽsŽ3èj¢éû+;zKüƒØÁÇ’î×þº »»³–ì„Óž ŸšKc*4}r07víÍmxT’vw]l×èi*p”V®ë®·üˆ5®õ“4†Æ= F¨—1*¨`Ý*£ZIYëå'ú~‡-5e¹› ªßí”Ùh%m¿ÔæhÎÎù¼õÏzQ”šæ÷·Ò_äaì ïïK_ë±§¾§`nͼöy¹B’y~ãæë×ó«J5rOG†_ämR4çk§§“ÿ!tf¼ÑîæÓd®ßÞ]FØÏ¦Sœ¡Yr;ýÍm¯aUPª¬î¾_ð :ÝÌ·3D%ŠÖlDµ`P “ØžrMtSk̺0QNÍ»¾¦pÏ­k+v5B޽j[ÐbŒçÚ§D0=(W¸3±Öå¹¹×®¯/l㌘’ÛË'Ê€ÇR@éןjŽM,x±„b£-¥d×^ÿqÈLÊò3"Ô’Bƒ£Ò´Š}Ïg¦¤cWñ1l(ëQ­ŠAéÍR‹[Šé€ã­&¯°~(ÖÚ€qŠVw‚ƒÇµ ;‚bwëTÝ€‘éR“è6Ä ¿‰“°£­F¶( §5J-n+¦Ž´š¾Àø£[jÆ)YÜz Ô4î ®¹go?‹/á¹ n[Ì’2Û¤VÇ=28Éé‘X)´¾g‹N¬Ô#˽íåoëæqDÖÉÕÄsTÞ–)=F´M6€(£©¤õZ FéÖœn˜˜t)-XöAß8§{+\B“Rî 뚦ô°€qIê5 i´@…M'ªÐb7N´ãtÄàéIjDz:K[뻽Iu .d[Ù¢–Wš<+PH<:•«SÊ«År%¢’üw9×fwgrK1É>¦¯cÔKK êŸ@@zP·ƒIé`B÷4º&>¢ŒQkêž*ÒÔ›‡ð憗5‚ú { I-XØT½½Sè$¥ pÈ4ž–/sK¢cê!8ž¡q â­-I¸hisX/¡ÿÙfrobtads-1.2.3/tads3/doc/qstart_cover.jpg0000644000175000001440000003025511755477644017536 0ustar realncusersÿØÿàJFIFÈÈÿÛC      ÿÛC  ÿÀ´~"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ý¸®gàßÅ]7ã§ÂýÆ47Ñèž$µû~–÷(‘Éyc'ú‹½›þHçËž4“dž\‘ïŽ9<Èãåþ4|>Ö~7xËGð­ÕŸ‘ðêßý?Å4É$~,O.xãÑ|”ýç‘ælžw“drGv¾]Üww~G§W¨hkh~þÛ´2™ÌaO–T.CðzûÕÅðkàN"$œ×,r9Æ`+Àþ0xÇÆž7ý£< ð‡Â¾5Ô¾Zkú&¯âÍSÄU…æ«$VRéöÑÙZ‹è.-£ %ð’I X,AT)“zäüý­üYà/ŠW “7úYËpO•ÉŸ\ì1ÀÑàÿLùpCéó`› @ÇN+Ä|?ûeIã¯xáàÕ4 6ïÄþ3Ð/m­ï-ç·¹MkËa<ÁíLÇÙ<ÕHdˆ£>Öi”`ù‡Æ¯Û_ÇÞ&ýƒí>0kµøyà½O]ð^·¡ÜøwÄ—Ú׈/´‹¯iÆX°ˆÅ$–rk{i®üÏ:H²Ãïä«ÊönÊñMô\ÎÉ¿Å÷v}ŠQ“Ÿ³ëwuºviw³²miªî¯“áø\æñ‰,IýÐÈÇ@rO#¨$zÒwûP-Æ3#¾r7sœŽ½01^c¦|eøƒûC~Ã:¯ü£hšŒ|S Üê^ ·›WƒT†D–6}>i¥Œ}œK"Ý‘XQ›o*‚Çç}7ö¸ø·¢ÛxÃ~ÕuŸxµ~#¯‡ü_¥üUK? ëºTré77QÙK&—¥Ícs ´/4w–I°„Ž1$¹•ÖJН±–ŽñZ÷“åü\Ý´OV‘w‡´NêÒzvŒ\·ÛTŸ/}^ɳícà¼bìcœþëæŽŠC £<ñÎ{Ñÿ/“v»s<œ}rXóŽýk毇ðSGŶ•¿ÂëÏYIáÝ_YÕt-+ÅzÖ¹{g%ÝŒSÊñÍ5΋k§o k/Ö¾ØÉá [ÇÞ·áý gÄ76ö3Ú@¶zm´ö×Q»³Üî“ýy ¬qÆå‹û-~×–Þ1¾ø}á;_k¿£ñ.“âA¼U®há½UFÕ,ì^ÖïM{[r—¯ r0ŠÝwÚ³@J¼œ=¥ô÷ŸÊ<ÜÏåÉ%­›¶—ÒãºvôûÚæKו§¥ô~¶öÛ¿´Ï"\´ŒŠNß$e€tc‘úÖöÒZ¸Ycx،ᔃŠðm;þ ?_4¿Šz¾¯Xx~ëà¯ü,ËÚÞZÜÉü”f¶Iž|?–$3$890ƒó¯ü[ý½>$þÏß~$k_| ž«„üe xKþ%Ô¤-Q´¿­æ“õIî‘2mèÿ­×â›OºmlÙç c¿‡tXø{Ãkciá;ÍOPÒã{뛳ϩ<²^»dc!•甑&à7 qŒ/ÿÁ>¾ü3ð¤þÒtÏÿÂ;-ö¨A£^øÇY¿Ó4¹tûĽ³V³Ý¼6QÅ„¶.‘nu¸|E&­?õɼ@÷ð Šoíg¼7ÿ,9ˆ'Ÿ°FÌ›v³ì¿ÛŸóõmÿüiŸÛÖ>f϶ÚoÆí¾rç½jcšimoÂÍ}Í&»YXªÏÏñÑý÷×½õ<‹Â_ðOŸ…øÉgãÍ7BÖcñ—{©éÉ7‰õ[3J¹¼YVî[M>K–³¶i¼éLžL)½œ±Ës[~ ý~øà熼&—ªØøoÁÓ[Üh‹¦kÚ†›{¥É²FÑ^[ÏÒŽèq/ÎŽêÛ•ˆ>†šíŒ’:-å£<yDÊJýyâ£Oé²Û‰—Q±hY¶¡RÙÆ3œg»c"µå¢´‡ Ê AÏ4½ž[F;½<¿¯Õýçœi_±WÂíÀv~‡Â6rxzÃÁÿð€Ã§Ü\OqöÕ_±2Èí½pŠ7¶_½^k¬Á8þxoJÖtˆ´-zõ©aa¦êž Ó¯­´ùìdƒ~‘þþ£Ì}‰çɲH䨛ä®ÏÃú ·ÄmŸxoM}WcÆÿiÓ¿w³Ïù>ÿûˆô›}D•ÎWÃv¾ñ¿†5¿iÞ$ñ=ÜÚÄ÷Wsßùù–ïæ|ñ¤îžZyrË:‡âgü"¾-¼°¶ŸÅZÄz®–“è®öpy’o’8÷Èé²½/Cø} ø^D}7DÒ¬^7yí­cËy>ý2ãáχ®¯åôK™'û[Íö(üÏ?þz×J\Ãå#ñL0ÛøÿYû~¨ØÈövIž|pyiè‰ÿ-#ÿž•ÓéZ7†ôo„þðÝ®±}im¡ÝZÉϤI’ϧùqùoó¼_ñ§ýtÿ¦•í2|9Ðeð¼Ú#hšWö=ÃùgöXþÏ#ùžgÜÿ®•ü*ï }™áÿ„{Còdßò}Š?ùi–ôs)åú_­.4Ù›Äú­õå¼ý–ñìžxäHí#ÌÿQåþózßÈ굿ü!šÏŽ,Ñúé³÷uæ7<+/Ã9´} 绳·DÔŸ}Ôr\Gþ—æËÞßÿ¦‘ì­Ò¸Û±í72Ò¢³¹¹mJÇɳG’wóÓ÷~\~cÔÑøŽÂ[„E¿´‘ä-ÏÌ’¼ÞóKðgÂ_[_ù7Ö3j]j_ñûåÛÉþ¯|n'ûõÎ:§âá×ü&¾ ×µKkéõ/jñßè“;Éû»¯"?àþþçý´§Ê ÜõI5ë¯Ùï-cš=‘º;ÿÏO¹PÙøËG¿´†æßUÓg¶¸O1&K¨äŽDÿž•åÛÆ «Ÿ“‚\¥Ô‘É"GwæGÎÿòÎD£”\DZÞx£J°·y§Ô¬`†=ûÝçHü¿/ïÑÿ F›ö„‡íöžt“ùž|þy×κˆþ øKPÿGÓu‹™-uÒoÞ£¤’Gçüûü´“î>ÊÛðůÂ/ĚƱa ÝŽ¥¥ësÝÞ¼Ú—æ_G˜ÿ}þݧþŒ£”9oÅlº¤Öoµûe»ùÿÞoòüÏ/þýÓä׬"¼x^òÕ&fôóÓÌÌû•ã÷2ø{¯xòÿ¶j7z–¡¨ØùMï™oçþïÈÙóÿ«û•sÆqx>Ãâž¶š¼:ÍÞ¥pö7¹O.;ùghéÿ=$Oü‰¡Ìz\ž=Ñ"ŸÉþØÓ|í‰&ϵ'™³ÌòüÏûùVcñ„·‰hï#¼h‰:~ñãûõà:§Œ¾h šòtñTö¯ ò}¢Dû—÷üÇòäþ?ùg•ÖüµðOˆüY¥ ßÝ%ýœ—R=µÌþd—û½òãÿùŽQ§s¶ñÆO ø^í!Ô¼C¥XÍ$i4þ_Ïߎµ|?ã+Ų^&—i}ýŸ?‘?’þg–ÿóμ¯ÇŸðƒizÅý¶¨úä%ìòN‰<‘Ç$òG&ÿãÿžoÿ¢ÿçox?þ/éâ}.òú}7\ºØˆú™o¾I?ü¿àÿ–tr Nç£×çgüÇþNCDÿ±jý+»¯¶þþÐ^ø¿r‰£M<$"yÉåÿ«“Ëzø“þ ãÿ'!¢صþ•Ý×>!>AHûÿÅ’ê°ÙÛeÃi<Ò]'Ÿç«Žã’¼öâÿâ¿ö:_¦•áˆ5+x?ìÔºÿC¸>=’<ÿë?wÿûi]‡ÅH¡ºÑìÒ]zMý+ä™?å£ùrlÿgÿ¶uåן´Û =,ï~$x¶îÎâÕàžžI>ГÝùi#ìÿ¦›Òº"6ìz´¾'ºðžªòæÁ¬oŸû96y‘ìþ3çùÿñʳou¯]hú–ûkH/#Oô-ñþï—ü?üô®Kâ$¶«¦ÙÝxžúÆhô‡O³CÉ%ÂG÷äDJ³ðYÒ¾Ç5µ†·©j©ªNñ£ÜÁ?™ñÇå¾ýÿrOãÿ–tš°É¤¿ñå® ›£_[\]A¿ÎŸÈ’Þ.??fÏ3ï7ùtx¯Ä~6‡ÆöÚ6¦Ï¥[Â×?»óøãûÿçÿ!×%âH´¯ ø¢ÚÎëÇ> Ó®cº‚I’³Èï›#ßþ­é–]†©á»ê²^[ÚÇ©'ö•“Ïö2î9GßÿM6$õÒŸ)<ÇaáûévfþÍÒ­4ØÒxà¶°µHþÏûÏ“ïÏåÿ«§É¬øÂ/X[K¥i²iW—³Ç;¢~òÞ×ø?ýgÿ­ïøEæÿ„~òÃíï¾áÞH&ýçú?÷?Ž«Yxæ×GÖ-›[Õd›Tß$w;ÿyfÿìT”ex‚×Ä—^(¶OìMëM·ÔRKY¦OÞ[§üôûÿë++Åðžk:Ç“ÿß…n¬-ÞÖ{Y¯÷‘ÏŸ¼“gð~ïý_ï+WOøK©Xj‰7ü$÷Ó¤w°\l¹G“äŽ!ãûÿòÓïÕÏ ü4¹ðçŒ.uY|Cªß%Å’Z}äÿC·òÿå¢'üô¦šçŽ%ñ ¬hþ³Ò®ßgΗåùŸ¼ÿiï¬K9|magûÛ 6{ÈÒëdß'ý3Ù¿çÿÿ!ÕÏü/¹ð¿‰þ]{UÔ­£µòÚåäûþg™æ}úæôÿ€ZĶ÷/uâÛèîo>ݺy—F“ù{<‡Þ'ÜJº†§Ià}Xðç†õ‰¯,ô¨5+Éç¿ßmeåÇpÿí¢IóýĨtý{ÆÒÆï.‰§y2$@ˆÿ¼Ìûûþùgÿ´èðÃcAð¾±ay¯]_^^AäY^<ÒI$i_˜ÿôÒI7¿îëÏö}Õ´>Ù?ŒõÈÞKX {hf“ìöóÇúÄùÿßÿYBHQŽ‹î%~wÁ\ää4Oû ÿÒ»ºûWáŸÁŸ‡¾ †þ_ø\K{'´òoî¼Èäó'ó<Çÿ¦‘ýÊø«þ ãÿ'!¢صþ•ÝÖ‹rhL¼>$k:>—g¦¦³¦ÏªÃyz‘À‰eö¿.à“ýúé\­×ÃAá4¼û6•i¥y w¾h<¸ãóç“ÿ"yèõ«ñ²êh­ü= 艮[\jñÇ>ûY.>Æž\Ÿ¿ù+Êü?ö[¶jÞÓ‘-ç‚4šÛA¾“̃Ïó>H6y‰${÷×DWQ·cÖ¼{kàÏímµëk&ŽÉãO9<Ï. ?wåÿÛO¹X—’ü1Ñ®?´¥‡Jô;§ÿIò$ÿGŸÌ’Oûùæoª´F©ªéú¤/aàý;Äÿñ.yÓ§’âGó##ßþ­?×ÿËI?礕‰³gâ‹kiµAý‰&£u;Í¢]Ç%šGŸ¼Øéþ³Ëÿ¿žgÉN+¨7c§Ö]Ognö²Z§‘wö{ÝÉ÷Èÿ–s÷ž_ý³£”\Çs§ünðÆ³¿ìº“Î‘À“»¥¬þ^É$òÓø?ç¢U™>-xz+ˆSûJ7ó?¸ŸôÓËÿÑ•Ãh÷Z>«ð‹Mñ ¿nôy¤ºHJ¹²“í–iö¿žM$’·Xþ(ñG•ṯ?áRǪÛ}–yÞÎí?™þ¯cÁü³}ÿøây”r æ="?Ž~º¸xmõ_=ü‰çýÌ'É?ðUÉ>*øz-RÚÍõ(>Óy:@‰±þüŸr:óÏè1jè+ðö±ÉÔþO‘zÇÐþ4Øk? í¼TÖwÐX\?îÓä’OõžZ}Çÿ–ŸûRºÊ£ø_ÅV¾Ö!ºÖÒ}Vòëϵ¹OùwO.?ݧÉþÃÿË9?ÖSíü¯]x_^°Ôuéç›Tßö)¡³Éf’}ÈÑÑ#ÿcþšUk%ýÇÙ¬’úîþH'’ÖÍ?2ãËó?vŸ?û5ÇÇ?ZøóMðÛ\Ïý¥ª]Oij›<Èäx#óçþ V`rºÁ¿hºÃ¼¾-’îÃ÷o3^Ýù–û òßçßûÿ3ïþóþZWyðÿGÕ|9áûk=Jñ/žÝ?×oy$‘üÉ>þÿúg²¨xƒã&áÝû6ãÏ{™.’Ñ’|ïr&ÿŸäûû+ßö¯ð•׉?±üëèõ/" <Ÿ#þ{ýÈ÷ÿ«ó)»°Ðôª+Êt¿Û7Á:õœ3XMªÝý¡DO±y$wqÚyŸ?—ÿ-þýÖ—…ÿjo xßÄ–ÚV‘s=ÝÍÆ©>’ÿ¹òü¹àÌ¿þå¬4=ŠäÏÆ­þÐZm—ñÞýƒfôýÛùpIóÿsýz%C¨|xÐt¿\蓽Ú^YÝ%£þã÷{äÌOž•»ÙQ^}oûKxnê4™<øí¤‚Öí.fÙr$óù üu«áÿ:'ˆõ *ÚÕç’mcÙ~Oùç™óÿÛ:,Àë¿å¥6¼ËPý¦ìíuKÈWG»žÛOº{K«ŸµA—åÉåüˆïæ?ý³­ï…ÿ4ߊ·”6^\sisùoÚ’I$O.9<Ï‘þO¿Mņ‡___ðTùfÿ±šóÿLÚ ~Š×çWüþG™¿ìf¼ÿÓ6ƒXWøI‘÷—ÄíX­ôÙ´»/í/.õ>Õ A’lÿž‰½ãÙ^{qçú¿.I>Oõòκ"=/Å·ZÞ—â};D‚úÛì¯&ôHüÈçó?ÛxÿÖGQë—úÆ—ã æøn ­+PyäÕï7¤riïåü’}ÿž¢Õ4»=/Ã÷:%ÿ‰/ïïÒgºxä<ÿ3ûÿêÿ‚™¥Ø[]x_Å^oЧº¶¼yãžçÏòÿ²ÿwå¾Çßòyu%^,×¼skâÍšO‚t«ë95Ž{Ë›¤·“Èÿžÿç“îVô’ê±x>Îå¼7cý¥çþòÎ.O³§™&É+’þÁ°þÕÓo ø‘=´šŒïm Ô~^¡û¿õo½äýÜ•«m-·üA6«£x·Ï¶Ôê¶ÛM4ž^ù#òÒ?¿ÿ=þZS¸%bâÚø£MÝàý‰ªj>CÍä$’[Áÿ=ÒI?¹¿þý¥?P¿Öü9ñR†ßÃ)¥j—Pyˆ’I%äþD›ä}ŸsËØ‰¾O.¡×.ô«ô›Êñœÿf¼yäáäòÒH$“ïïù<¸Ò™§x"m{Åu¯Ä]Ft³û Ûé°Ï—Gýüòçÿ–žfúm\fmƳñ / ùßð¯t;»øáe‚OqÆž|›ãß¿ýg—å¿ú¿/þšW[ðó횥åηá„Òµ‹xO9?.ßÌ’?ݦǓ÷’l£\ûÄmAHñWÙfò<Ïô ÓÌÙæ|ÿú§ÿ»®{OºÐt½ ߯ÿfÙéÓÁöïåÆždäó÷ü’}ÏûùKq%büž#Öïï5'³ð”7PÙÞÝ@Žðyhòãó<Ïßùë7ìó?ç§™ZW:ö— iWš_†í Ô¯6IªCû2ß÷êÿÖGÿ£™á}ÛAðž·ggâµ<ž|›þÕÿo$’ð~óÿEÖ&—}¦Ëfñ|E‚wŽx7Ãöß3íêäxþÿü´¤•Æmü*ñ?Œìø©-œQé_lÑ5-e$¾òÓì~gú;ùr~òMŸòμ»C—º—msaðö8ò×÷ïmkmÊyG5MAñÙ¯ü0÷Þ]Ò@l—WGýÉäòü¿¹ýÿõ•Oáý®•ªÞY¾ þƳýÇðÏ<<’yŸÜý‡¯Z¸ñF›knóKbÇ¿{¼ñþïË“Ëü‰Vµ-¼ÄÿIƒ÷ŸíÑ̦>—áÍSð›ØAm'öl“¿î_ÌçŽOöÿxŸ¼J¿'…ì%Ó¯-¥¶ŽKmAüÉÑÿyæTÒk6qI =̽Ãùi½ÿÖQ©mupÅ4nòGæ"#ÿIF ü7Â:i¾_—?ž‰çÉåïò<¹¿þyü•gBøg øsX¹¿³Ó`‚þò´šoõ’\$r7zÕ³Õ-¯ÿÔ\Á?ßûÿ<äòÞíKo/Ú`Ùóÿ`føáöá}Qï,,ãµ¹’!Ýÿxžd’èÇzÁ·ýœüýŽ–ZWöŒ1Àð?Úgy>Ð’y{÷ÿË7û‰ÿ~ëªÄzl²:-ý¬ùo²xþÿ—æ连¬ÛßÛ]ªš >O3äà¢ì  áV• øoUÒ IþǬ;É>÷ýä~d~_Éÿ\ãD¬­?önðN—$3.ƒjóGyÎï$’$s}i[üið}Ö öiâ}K˜à{‡‡ûF?24ïÉ[ñ‡‹t´¿Òïíu9ãŽkiüÈÿwû·ùè» øi ø"á&Ò4«[£ƒì›áOàó<Ï/þþWÀÿðTùfÿ±šóÿLÚ ~Š×çWüþG™¿ìf¼ÿÓ6ƒX×øI‘õÿí1ã+ I`úž«=·Øµ?Ðî­#ó?qþ¯dÿ~Oùç\ÅçÄoüZ³ð–•-œ÷vmK $ß™"<òÓgÜÿsþzG^ñRÿ^µŽìÓ\¼ŽÖyí>\qÆþ_ÉýþbyŸõÍÿíqú~³ñ.ÃTI—ÁúT–wPy–ÉäGýžŸdýüˆþïäó÷§ü³ÿWÿm+¦.á"Ìrø?à‹ ¶•5X&·‚ 6 î’[ÉçÉæy›?ë¢}ÿøS<ðÓÀß~:YÙØ}»þ«?ü>°MVÿþ4‡Æ]jO Ïæyže§ïÿÜ“Ëùgÿ´ë¶ð^³ãÿøX6z§ƒô¨ô<ÿ?Uó#Žâß÷qìýÂ<Ÿîñº«©øŸÇ:ìš”)á-;QÒ£žúÖš’H‰æFŸ$“Çû¹?¿ÿ-<ÏûiWrZ±ÃøCTøEæ`Ù¦¸÷:|úu§úJIæFÿd“gÎÿôï¿ç޵~x£á¿€ô½*oCªÚØ[ÙAiÍäñlj=ß—å¾÷ó<È䮪âÿÇ1^C3h6³§Ú­^¶²‚?³ÁäI¾7wºÿ–sÿÏ?ùgÿ=+WÁú¦·¬ø’ÏMÖ|=k·ö[ÝÝ?ؼ¸ãŸÏù#Gß$ßûgI½+œÇÁ¿øCõOÛ\éx‚}Kdò#Üù~\i$“ÉüòÏ÷îÿ÷î¹ícß "·ÖâbG³ºÕ ½¹†ã’?2ü†õÙÛËãɬÞå<7¡Øê¶ð~âo²¤ŸòÒ=ñìûWüóù?Ö¬þYÕÏ ßøÏXñ¢&¯àýÇMŽöê7¼óã’I-|¸ü‰?뤒¬ÿ®t\{ž{ª|Aøo£iïæ¿ˆ4{mQÒ=ÚÁö§úúÏö<È>Oùg]ÿÁ? øJY?·¼94Ÿ¼²û"Còy–iæI'Üþ¹ÿêÿĹ¼[—) èúUì?&Ç›ýgú¿õŸ?—ûϾž]VÑõZéîŸðAüs:ZÁ¼Ÿë#yý+ÌþçúÏõôÒ—6ƒJÇšG¬ü1Òô;øZçÄv¶iÏ=Ô3GqÈ’IóÈÿöÒ½ OøoömÐæ¶ºÔµËèdõ$óŸíFŸóÍ*åæ©­è? Þh<=i«ÒZAfö^dr$“ÿr“ûÿóÒ ë^9ºñ%„:燴԰’yàžæŸèéåü’¯ÿ–’lOþ7G7pJÆ÷ÿŠÚ?Äû{É´™¤,ßËÞÿòÓþš'ý3ùþý×Á¿ðTùfÿ±šóÿLÚ ~ˆYØCaÿðÁû‰å×çüþG™¿ìf¼ÿÓ6ƒ\ÕþHû_ã>g­I›âÝKÃ1ÙO}Žéã’O2?õ›?ŽHÿër¾ð6¾0t—âwˆ5Y­õKÞÎñÿwðGž_ü³ÿYÿµ+Õ{Ïçyòy‘¼žfýŸ?É÷Þµtÿèú]Ç„¿˜ò|ŸôÒ?-éó.‚å(jŸ~ߢh–ŸÚWÐM¡ÎŽ“%Ôÿé#’?Ÿçùþÿü´ßPÞ|4¹¿ðu†”Úö±Ösyy òG%Â~ó÷oóùŸÇ]=ªXYà I²tòÑ*Z’Ž7TøKý³¥ÜÙ˪ߤ'ú亻ŽHÿyŸ#ùÿ'Ü©¼?ðÒÿFðþ½gqâ}gQ}béç‚iŸ÷šzIÿ,Ógü³®²Š.Àå´‡×:6­ÂÚÅõôÚ¤)<×SÇöÜy'Ï&Ïïþî«^|3Ô®¼?aaˆn¬^Îx'{˜|É..<¿¿»¿˜ñÉ]•®Õaø?s Å¯_l\oÙ~÷³ù‘ÿsçÿYþ®±õÏÙÏUÖ¬ï|âÛI®öM ÔžeºIÿ,þÿ—ÿÿå¥zx‡ÅïÛ›Â~³{mðkš”–²\A5”k³Ž4I$óÓdŸéIû‰ÿÔ~î9#Ù<ð}úN| z‡aÁ½UÔ¼GâמÎI ‘ï5‹¤‚ÞÏ÷qÇ÷ß÷iæIÿ‘$¯„?à¢:ý¯‰¼Ký¡nº„Vóø’ûbÝXÍir?âQ ýø.#GJíoþ4ëž=Ö, #include #include #include #include #include "os.h" #include "t3std.h" #include "rcmain.h" #include "vmimage.h" /* * copy a block of bytes from the input file to the output file */ static int copy_file_bytes(osfildef *fpin, osfildef *fpout, ulong siz) { static char copybuf[16 * 1024]; size_t cursiz; /* copy bytes until we run out */ while (siz != 0) { /* we can copy up to one full buffer at a time */ cursiz = (siz > sizeof(copybuf) ? sizeof(copybuf) : (size_t)siz); /* deduct the amount we're copying from the total */ siz -= cursiz; /* read from input, copy to output */ if (osfrb(fpin, copybuf, cursiz) || osfwb(fpout, copybuf, cursiz)) return 1; } /* success */ return 0; } /* * Add resources */ int CResCompMain::add_resources(const char *image_fname, const class CRcResList *reslist, class CRcHostIfc *hostifc, int create_new, os_filetype_t file_type, int link_mode) { osfildef *fp = 0; osfildef *resfp = 0; char buf[256 + 256 + 128]; long mres_seek; long mres_size; CRcResEntry *entry; long ofs; long contents_siz; /* * if the file doesn't exist, and we're not creating a new file, * it's an error */ if (osfacc(image_fname) && !create_new) { /* we can't create a new file - display an error and give up */ disp_error(hostifc, "image file \"%.*s\" does not exist", (int)OSFNMAX, image_fname); goto ret_error; } /* if we're creating a new file, write the header */ if (create_new) { os_time_t timer; struct tm *tblock; /* create a new image file */ fp = osfopwb(image_fname, file_type); if (fp == 0) { disp_error(hostifc, "can't create file \"%.*s\"", (int)OSFNMAX, image_fname); goto ret_error; } /* set the version ID in the header */ oswp2(buf, 1); /* set the reserved bytes in the header */ memset(buf + 2, 0, 32); /* set the timestamp in the header */ timer = os_time(NULL); tblock = os_localtime(&timer); memcpy(buf + 2 + 32, asctime(tblock), 24); /* set up an EOF block */ memcpy(buf + 2 + 32 + 24, "EOF ", 4); memset(buf + 2 + 32 + 24 + 4, 0, 4); oswp2(buf + 2 + 32 + 24 + 4 + 4, VMIMAGE_DBF_MANDATORY); /* write the header and the EOF block */ if (osfwb(fp, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) || osfwb(fp, buf, 2 + 32 + 24 + 10)) { disp_error(hostifc, "%.*s: error writing new file header", (int)OSFNMAX, image_fname); goto ret_error; } /* done with the file - close it for now */ osfcls(fp); fp = 0; } /* * open the file for reading and writing, since we'll need to read * through the current contents then write our new data after the * end of the existing file */ fp = osfoprwb(image_fname, file_type); if (fp == 0) { /* display an error and give up */ disp_error(hostifc, "can't open file \"%.*s\"", (int)OSFNMAX, image_fname); goto ret_error; } /* read and verify the header */ if (osfrb(fp, buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24) || memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0) { disp_error(hostifc, "%.*s: invalid image file header", (int)OSFNMAX, image_fname); goto ret_error; } /* read and skip the blocks until we reach the "EOF" block */ for (;;) { long block_siz; /* read the next block */ if (osfrb(fp, buf, 10)) { disp_error(hostifc, "%.*s: unexpected end of file", (int)OSFNMAX, image_fname); goto ret_error; } /* if it's EOF, we're done */ if (memcmp(buf, "EOF ", 4) == 0) break; /* read the size of this block */ block_siz = t3rp4u(buf + 4); /* skip past this block */ osfseek(fp, block_siz, OSFSK_CUR); } /* * we've found the EOF block - make sure we're at the end of the * file */ mres_seek = osfpos(fp); osfseek(fp, 0, OSFSK_END); if (mres_seek != osfpos(fp)) { disp_error(hostifc, "%.*s: extra data after end of file", (int)OSFNMAX, image_fname); goto ret_error; } /* * seek back to the start of the EOF block, so that we can overwrite * it with a new MRES block */ mres_seek -= 10; osfseek(fp, mres_seek, OSFSK_SET); /* * Prepare and write the MRES block header, plus the resource entry * count. If we're in "link mode," write an MREL header instead. */ memcpy(buf, link_mode ? "MREL" : "MRES", 4); memset(buf + 4, 0, 6); oswp2(buf + 10, reslist->get_count()); if (osfwb(fp, buf, 10 + 2)) { disp_error(hostifc, "%.*s: error writing resource block header", (int)OSFNMAX, image_fname); goto ret_error; } /* * First, figure out how much space the table of contents itself * will take up. We need this information so we will know where the * first resource's binary data stream begins. Note that the * contents offset starts at 2, since the table entry count (a * UINT2) comes before the table's first entry. */ for (contents_siz = 2, entry = reslist->get_head() ; entry != 0 ; entry = entry->get_next()) { /* * each entry in the table of contents requires a UINT4 (offset * of the data), UINT4 (size of the data), UBYTE (name length), * and the bytes for the name itself */ contents_siz += 4 + 4 + 1 + strlen(entry->get_url()); } /* build the table of contents */ for (ofs = contents_siz, entry = reslist->get_head() ; entry != 0 ; entry = entry->get_next()) { long res_size; size_t url_len; char *p; size_t rem; /* if the entry name is too long, it's an error */ url_len = strlen(entry->get_url()); if (url_len > 255) { disp_error(hostifc, "%.*s: resource name \"%.*s\" for file \"%.*s\" " "is too long", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_url(), (int)OSFNMAX, entry->get_fname()); goto ret_error; } /* * if we're in "link mode", the table of contents entry consists of * simply the resource name plus the linked filename - we don't * care about the contents of the local file in this case */ if (link_mode) { size_t flen; /* make sure the local filename isn't too long, either */ flen = strlen(entry->get_fname()); if (flen > 255) { disp_error(hostifc, "%.*s: filename \"%.*s\" for resource link " "\"%.*s\" is too long", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_fname(), (int)OSFNMAX, entry->get_url()); goto ret_error; } /* * build the block: resource name len, resource name, filename * len, filenam */ buf[0] = (uchar)url_len; memcpy(buf + 1, entry->get_url(), url_len); buf[1 + url_len] = (uchar)flen; memcpy(buf + 1 + url_len + 1, entry->get_fname(), flen); /* write the block */ if (osfwb(fp, buf, 1 + url_len + 1 + flen)) { disp_error(hostifc, "%.*s: error writing contents entry for " "resource link \"%.*s\"", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_url()); goto ret_error; } /* that's all for a link mode entry */ continue; } /* open this resource file */ resfp = osfoprb(entry->get_fname(), OSFTBIN); if (resfp == 0) { disp_error(hostifc, "%.*s: cannot open resource file \"%.*s\"", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_fname()); goto ret_error; } /* * seek to the end of the resource file so we can determine its * size */ osfseek(resfp, 0, OSFSK_END); res_size = osfpos(resfp); /* build this table entry */ oswp4(buf, ofs); oswp4(buf + 4, res_size); buf[8] = (char)url_len; memcpy(buf + 9, entry->get_url(), url_len); /* mask the resource name by xor'ing each byte with 0xff */ for (p = buf + 9, rem = url_len ; rem != 0 ; --rem, ++p) *p ^= 0xFF; /* write the entry */ if (osfwb(fp, buf, 9 + url_len)) { disp_error(hostifc, "%.*s: error writing contents entry for " "resource \"%.*s\"", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_url()); goto ret_error; } /* add the resource's size into the offset so far */ ofs += res_size; /* we're done with this file for now */ osfcls(resfp); resfp = 0; } /* now copy the resources themselves, if we're not in link-only mode */ if (!link_mode) { /* run through our resource list */ for (entry = reslist->get_head() ; entry != 0 ; entry = entry->get_next()) { long res_size; char msg[OSFNMAX*2 + 20]; /* show what we're doing */ sprintf(msg, "+ %.*s (%.*s)", (int)OSFNMAX, entry->get_fname(), (int)OSFNMAX, entry->get_url()); hostifc->display_status(msg); /* open this resource file */ resfp = osfoprb(entry->get_fname(), OSFTBIN); if (resfp == 0) { disp_error(hostifc, "%.*s: cannot open resource file \"%.*s\"", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_fname()); goto ret_error; } /* get the size of the file */ osfseek(resfp, 0, OSFSK_END); res_size = osfpos(resfp); osfseek(resfp, 0, OSFSK_SET); /* copy the resource file's contents into the image file */ if (copy_file_bytes(resfp, fp, res_size)) { disp_error(hostifc, "%.*s: error copying resource file \"%.*s\"", (int)OSFNMAX, image_fname, (int)OSFNMAX, entry->get_fname()); goto ret_error; } /* we're done with this file for now */ osfcls(resfp); resfp = 0; } } /* * calculate the size of the MRES/MREL data (excluding the 10-byte * standard block header) */ mres_size = osfpos(fp) - mres_seek - 10; /* go back and fix up the MRES/MREL block header with the block size */ osfseek(fp, mres_seek + 4, OSFSK_SET); oswp4(buf, mres_size); if (osfwb(fp, buf, 4)) { disp_error(hostifc, "%.*s: error writing header size", (int)OSFNMAX, image_fname); goto ret_error; } /* seek back to the end of the file */ osfseek(fp, 0, OSFSK_END); /* set up an EOF block */ memcpy(buf, "EOF ", 4); memset(buf + 4, 0, 4); oswp2(buf + 8, VMIMAGE_DBF_MANDATORY); /* write the EOF block */ if (osfwb(fp, buf, 10)) { disp_error(hostifc, "%.*s: error writing end-of-file block", (int)OSFNMAX, image_fname); goto ret_error; } /* done with the file */ osfcls(fp); fp = 0; /* success */ return 0; ret_error: /* close any files we opened */ if (fp != 0) osfcls(fp); if (resfp != 0) osfcls(resfp); /* return an error indication */ return 1; } /* * Format and display an error message */ void CResCompMain::disp_error(class CRcHostIfc *hostifc, const char *msg, ...) { char buf[1024]; va_list argp; /* format the message into our buffer */ va_start(argp, msg); vsprintf(buf, msg, argp); va_end(argp); /* display the formatted message */ hostifc->display_error(buf); } /* ------------------------------------------------------------------------ */ /* * Add a file or directory to a resource list */ void CRcResList::add_file(const char *fname, const char *alias, int recurse) { /* * if no alias was specified, convert the filename to a URL and use * that as the resource name; otherwise, use the alias without * changes */ char url[OSFNMAX]; if (alias == 0) { os_cvt_dir_url(url, sizeof(url), fname); alias = url; } /* check to see if this is a regular file or a directory */ unsigned long fmode; unsigned long fattr; if (osfmode(fname, TRUE, &fmode, &fattr) && (fmode & OSFMODE_DIR) != 0) { /* * It's a directory, so add an entry for each file within the * directory. Start by getting the first file in the directory. */ osdirhdl_t dirhdl; if (os_open_dir(fname, &dirhdl)) { char search_file[OSFNMAX]; while (os_read_dir(dirhdl, search_file, sizeof(search_file))) { /* build the full filename */ char fullname[OSFNMAX]; os_build_full_path( fullname, sizeof(fullname), fname, search_file); /* * build the full alias for this file path -- start with * the the alias for the directory itself */ size_t len = strlen(alias); char full_url[OSFNMAX]; memcpy(full_url, alias, len); /* * add a slash to separate the filename from the directory * prefix, if the directory path alias doesn't already end * in a slash */ if (len != 0 && full_url[len - 1] != '/') full_url[len++] = '/'; /* add this file name */ strcpy(full_url + len, search_file); /* get the file mode */ if (!osfmode(fullname, TRUE, &fmode, &fattr)) continue; /* skip hidden/system files */ if ((fattr & (OSFATTR_HIDDEN | OSFATTR_SYSTEM)) != 0) continue; /* check whether we found a file or directory */ if ((fmode & OSFMODE_DIR) != 0) { /* it's a directory - check for special link files */ os_specfile_t spec_type = os_is_special_file(search_file); /* * It's a directory - if we're meant to recurse, add * all of the directory's contents; otherwise simply * ignore it. Ignore the special self and parent links * (Unix/Windows "." and ".."), since those would cause * infinite recursion. */ if (recurse && spec_type != OS_SPECFILE_SELF && spec_type != OS_SPECFILE_PARENT) { /* add the subdirectory with a recursive call */ add_file(fullname, full_url, TRUE); } } else { /* add a new entry for this file */ add_element(new CRcResEntry(fullname, full_url)); } } /* done with the directory scan */ os_close_dir(dirhdl); } } else { /* it's not a directory - simply add an entry for the file */ add_element(new CRcResEntry(fname, alias)); } } frobtads-1.2.3/tads3/utf8.h0000644000175000001440000005771012014201637014567 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/utf8.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name utf8.h - UTF-8 character string manipulation Function Notes Modified 10/16/98 MJRoberts - Creation */ #ifndef UTF8_H #define UTF8_H #include #include "vmuni.h" /* ------------------------------------------------------------------------ */ /* * UTF-8 character string pointer. * * Note that this class deviates from the normal naming convention where * each class begins with a capital 'C'. Since this class is so * low-level, and is used so much like the (char *) type, it seems more * proper to give it a name as though it were a typedef for a native type. * * If ever there was a time when operator overloading is indicated, this * would be it. We could overload increment and decrement operators, for * example, to step through the string. However, I just plain don't like * operator overloading, so I do not use it here. Instead, we use * explicit method names to avoid obfuscating the code as overloaded * operators would. It's a trade-off: it's less concise this way, but * less obscure. * * Note the important distinction between "byte" and "character": a byte * is the basic multi-bit unit of native storage, and a character * represents the basic lexical unit; a character may be composed of more * than one byte. */ class utf8_ptr { public: /* create a UTF-8 string pointer, with no initial underlying string */ utf8_ptr() { p_ = 0; } /* * Create a UTF-8 string pointer with an underlying string. The * pointer must point to the first byte of a valid character. */ utf8_ptr(char *str) { set(str); } /* create from another utf8 pointer */ utf8_ptr(const utf8_ptr *p) { set(p->p_); } /* * Set the pointer to a new underlying buffer. The pointer must * point to the first byte of a valid character if there are already * characters in the buffer. */ void set(char *str) { p_ = str; } void set(const utf8_ptr *p) { p_ = p->p_; } /* * Get the character at the current position */ wchar_t getch() const { return s_getch(p_); } /* * Get the character at a given character offset from the current * position. The offset must be positive. */ wchar_t getch_at(size_t ofs) const { return s_getch_at(p_, ofs); } /* * Get the character preceding the current character by the given * amount. The offset must be positive. getch_before(1) returns * the character preceding the current character, (2) returns the * character two positions before the current character, and so on. */ wchar_t getch_before(size_t ofs) const { return s_getch_before(p_, ofs); } /* * Encode a character into the buffer at the current position, and * increment the pointer past the character. */ void setch(wchar_t ch) { /* store the character and advance the buffer pointer */ p_ += s_putch(p_, ch); } /* * encode a character into the buffer, incrementing the pointer and * decrementing a remaining length count */ void setch(wchar_t ch, size_t *rem) { size_t len = s_putch(p_, ch); p_ += len; *rem -= len; } /* call setch() for each character in a null-terminated string */ void setch_str(const char *str) { for ( ; *str != '\0' ; ++str) p_ += s_putch(p_, *str); } /* * Encode a string of wide characters into the buffer. We'll * increment our pointer so that it points to the next available * character when we're done. Returns the number of bytes used for * the encoding. * * 'src_count' is the number of wide characters in the source string. * * 'bufsiz' gives the size remaining in the underlying buffer. If * we run out of space, we won't encode any more characters, but we * will still return the total number of bytes required to encode * the string. */ size_t setwchars(const wchar_t *src, size_t src_count, size_t bufsiz); /* * Encode a null-terminated string of wide-characters into our buffer. * Works like setwchars(), but stops at the null terminator in the * source rather than taking a character count. * * This routine doesn't store a trailing null in the result string. My * string pointer is left at the next character after the last copied * character. */ size_t setwcharsz(const wchar_t *src, size_t bufsiz); /* increment the pointer by one character */ void inc() { p_ = s_inc(p_); } /* * increment the pointer by one character, and decrement a remaining * length counter accordingly */ void inc(size_t *rem) { /* calculate the increment amount */ char *p = s_inc(p_); /* decrement the length counter by the change */ *rem -= (p - p_); /* save the new pointer value */ p_ = p; } /* increment by a give number of characters */ void inc_by(size_t cnt) { for ( ; cnt > 0 ; inc(), --cnt) ; } /* decrement the pointer by one character */ void dec() { p_ = s_dec(p_); } /* decrement poniter and increment the remaining size to compensate */ void dec(size_t *rem) { /* calculate the decrement amount */ char *p = s_dec(p_); /* decrement the length counter by the change */ *rem += (p_ - p); /* save the new pointer value */ p_ = p; } /* increment/decrement by a byte count */ void inc_bytes(size_t cnt) { p_ += cnt; } void dec_bytes(size_t cnt) { p_ -= cnt; } /* * Determine if the current character is a continuation character. * Returns 1 if so, 0 if not. */ int is_continuation() const { return s_is_continuation(p_); } /* * count the number of characters in the given number of bytes, * starting at the current byte */ size_t len(size_t bytecnt) const { return s_len(p_, bytecnt); } /* get the byte size of the current character */ size_t charsize() const { return s_charsize(*p_); } /* * Get the number of bytes in the given number of characters * starting at the current position. */ size_t bytelen(size_t charcnt) const { return s_bytelen(p_, charcnt); } /* * count the number of characters to the null terminator */ size_t lenz() const { char *p; size_t cnt; /* increment until we find a null byte */ for (cnt = 0, p = p_ ; *p != 0 ; p = s_inc(p), ++cnt) ; /* return the result */ return cnt; } /* get the current pointer position */ char *getptr() const { return p_; } /* -------------------------------------------------------------------- */ /* * Static methods */ /* * Compare two UTF-8 strings. Returns a value less than zero if the * first string is lexically less than the second string (i.e., the * first string sorts ahead of the second string), zero if the two * strings are identical, or a value greater than zero if the first * string is lexically greater than the second string. */ static int s_compare_to(const char *p1, size_t bytelen1, const char *p2, size_t bytelen2); /* get the character at the given byte pointer */ static wchar_t s_getch(const char *p) { /* * If the high bit is 0, it's a one-byte sequence encoding the * value in the low seven bits. */ if ((*p & 0x80) == 0) return (((unsigned char)*p) & 0x7f); /* * If the high two bytes are 110, it's a two-byte sequence, with * the high-order 5 bits in the low 5 bits of the first byte, and * the low-order six bits in the low 6 bits of the second byte. */ if ((*p & 0xE0) == 0xC0) return (((((unsigned char)*p) & 0x1F) << 6) + (((unsigned char)*(p + 1)) & 0x3F)); /* * Otherwise, we have a three-byte sequence: the high-order 4 bits * are in the low-order 5 bits of the first byte, the next 6 bits * are in the low-order 6 bits of the second byte, and the * low-order 6 bits are in the low-order 6 bits of the third byte. */ return (((((unsigned char)*p) & 0x0F) << 12) + ((((unsigned char)*(p + 1)) & 0x3F) << 6) + (((unsigned char)*(p + 2)) & 0x3F)); } /* * get the character at a given positive character offset from a * byte pointer */ static wchar_t s_getch_at(const char *p, size_t ofs) { /* skip the given number of characters */ for ( ; ofs != 0 ; --ofs, p += s_charsize(*p)) ; /* return the character at the current position */ return s_getch(p); } /* * get the character preceding the current character by the given * number of positions; the offset value must be positive */ static wchar_t s_getch_before(const char *p, size_t ofs) { /* skip backwards the given number of characters */ for ( ; ofs != 0 ; --ofs) { /* * back up by one to three bytes, until we find no more * continuation flags */ --p; p -= s_is_continuation(p); p -= s_is_continuation(p); } /* return the character at the current position */ return s_getch(p); } /* * Write a given wchar_t value to the given byte pointer. The * caller must already have checked (via s_wchar_size) that there's * enough room in the buffer for this character's UTF-8 * representation. * * Returns the number of bytes stored. */ static size_t s_putch(char *p, wchar_t ch) { /* check the range to determine how to encode it */ if (ch <= 0x7f) { /* * it's in the range 0x0000 to 0x007f - encode the low-order * 7 bits in one byte */ *p = (char)(ch & 0x7f); return 1; } else if (ch <= 0x07ff) { /* * It's in the range 0x0080 to 0x07ff - encode it in two * bytes. The high-order 5 bits go in the first byte after * the two-byte prefix of 110, and the low-order 6 bits go in * the second byte after the continuation prefix of 10. */ *p++ = (char)(0xC0 | ((ch >> 6) & 0x1F)); *p = (char)(0x80 | (ch & 0x3F)); return 2; } else { /* * It's in the range 0x0800 to 0xffff - encode it in three * bytes. The high-order 4 bits go in the first byte after * the 1110 prefix, the next 6 bits go in the second byte * after the 10 continuation prefix, and the low-order 6 bits * go in the third byte after another 10 continuation prefix. */ *p++ = (char)(0xE0 | ((ch >> 12) & 0x0F)); *p++ = (char)(0x80 | ((ch >> 6) & 0x3F)); *p = (char)(0x80 | (ch & 0x3F)); return 3; } } /* increment a pointer to a buffer, returning the result */ static char *s_inc(const char *p) { /* * increment the pointer by the size of the current character * and return the result */ return (char *)p + s_charsize(*p); } /* get the size of the character at the given byte pointer */ static size_t s_charsize(char c) { unsigned int ch; /* * Check the top three bits. If the pattern is 111xxxxx, we're * pointing to a three-byte sequence. If the pattern is * 110xxxxx, we're pointing to a two-byte sequence. If it's * 0xxxxxxx, it's a one-byte sequence. * * We're being somewhat clever (tricky, anyway) here at the * expense of clarity. To avoid conditionals, we're doing some * tricky bit masking and shifting, since these operations are * extremely fast on most machines. We figure out our increment * using the bit patterns above to generate masks, then shift * these around to produce 1's or 0's, then add up all of the * mask calculations to get our final increment. * * The size is always at least 1 byte, so we start out with an * increment of 1. * * Next, we note that character sizes other than 1 always * require the high bit to be set. So, the rest is all ANDed * with (byte & 80) shifted right by seven OR'ed to the same * thing shifted right by six, which will give us a bit mask of * 0 when the high bit is clear and 3 when it's set. * * Next, we'll pick out that third bit (xx1xxxxx or xx0xxxxx) by * AND'ing with 0x20. We'll shift this right by 5, to give us 1 * if we have a three-byte sequence. * * We'll then add 1 to this, so we'll have a result of 1 for a * two-byte sequence, 2 for a three-byte sequence. */ ch = (unsigned int)(unsigned char)c; return (1 + ((((ch & 0x80) >> 7) | ((ch & 0x80) >> 6)) & (1 + ((ch & 0x20) >> 5)))); } /* count the number of characters in the given number of bytes */ static size_t s_len(const char *p, size_t bytecnt) { /* get the ending pointer */ const char *end = p + bytecnt; /* increment until we run out of bytes */ size_t cnt; for (cnt = 0 ; p < end ; p = s_inc(p), ++cnt) ; /* return the result */ return cnt; } /* count the number of bytes in the given number of characters */ static size_t s_bytelen(const char *str, size_t charcnt) { /* skip the given number of characters */ const char *p; for (p = str ; charcnt != 0 ; p = s_inc(p), --charcnt) ; /* return the number of bytes we skipped */ return (p - str); } /* * get the number of bytes required to encode a given wchar_t in * UTF-8 format */ static size_t s_wchar_size(wchar_t ch) { /* * characters 0-0x7f take up one byte; characters 0x80-0x7ff * take up two bytes; all others take up three bytes */ return (ch < 0x80 ? 1 : (ch < 0x800 ? 2 : 3)); } /* * get the number of bytes needed for the utf-8 mapping of a * null-terminated wchar_t string */ static size_t s_wstr_size(const wchar_t *str) { /* add up the byte lengths of the characters in 'str' */ size_t bytes = 0; for ( ; *str != 0 ; bytes += s_wchar_size(*str++)) ; /* return the total */ return bytes; } /* decrement a pointer by one character, returning the result */ static char *s_dec(char *p) { /* * Going backwards, we can't tell that we're on a start byte * until we get there - there's no context to tell us which byte * of a multi-byte sequence we're on, except that we can tell * whether or not we're on the first byte or an extra byte. So, * decrement the pointer by a byte; if we're not on a start * byte, decrement by another byte; if we're still not on a * start byte, decrement it again. * * Since the longest possible sequence is three bytes, we'll * unroll the loop and simply check twice to see if we're done * yet. */ --p; p -= s_is_continuation(p); p -= s_is_continuation(p); /* return the result */ return p; } /* * Determine if the current byte is a continuation byte. Returns 1 * if this is a continuation byte, 0 if not. */ static int s_is_continuation(const char *p) { /* * Continuation bytes have the pattern 10xxxxxx. Initial bytes * never have this pattern. So, if a byte ANDed with 0xC0 is * 0x80 (i.e., the high two bits have the exact pattern '10'), * we're on a continuation byte. * * To avoid conditionals, which can be expensive because they * require branching, we'll play more bit mask tricks: we'll * compute a value that's 1 when the high two bits are '10', and * is zero otherwise, and then subtract that from the current * pointer. To figure this value, we'll mask the byte with 0x80 * to pick out the high bit, and shift this right seven bits. * This will give us 1 for 1xxxxxxx. Then, we'll mask the byte * with 0x40, which will pick out the second bit, invert the * resulting bit pattern, AND it again with 0x40, and shift it * right six bits. This will give us 1 for x0xxxxxx. We'll AND * this with the previous calculation, which will give us 1 for * 10xxxxxx and 0 for anything else. */ unsigned int ch = (unsigned int)(unsigned char)*p; return (((ch & 0x80) >> 7) & (((~(ch & 0x40)) & 0x40) >> 6)); } /* * Truncate a string to the given byte length, ensuring that only * whole characters are included in the result. Takes the proposed * truncated length, and returns the actual length to use. The * returned length will be less than or equal to the proposed * length; if the returned length is less than the proposed length, * it means that the proposed length would have cut off a multi-byte * character, so the actual length had to be shorter to ensure that * no bytes of the final character were included. */ static size_t s_trunc(const char *p, size_t len) { /* * if the length is zero, no adjustment is needed - you * obviously can't divide zero bytes */ if (len == 0) return 0; /* * Get a pointer to the start of the last byte within the * proposed truncated byte region. Note that the last byte in * the buffer is at index (len-1), since the byte at index (len) * is the next byte after the truncated region. */ const char *last_ch = p + len - 1; /* * Decrement this byte pointer until we get to the start of the * character that contains the final byte. Since a character * can never be more than three bytes long, we need decrement * our pointer a maximum of two times. */ last_ch -= s_is_continuation(last_ch); last_ch -= s_is_continuation(last_ch); /* * figure the number of bytes of the last character that are * actually in the truncated region - this is simply the number * of bytes from where we are now to the end of the region */ size_t last_ch_len = len - (last_ch - p); /* * Now compute the actual size of this last character. If the * last character's actual size is the same as the truncated * size, then the last character fits exactly and we can return * the proposed length unchanged. If the last character's * required length is more than the truncated length, it means * that the truncation has cut off the last character so that * not all of its bytes fit, and hence we cannot include ANY of * the last character's bytes in the result. */ if (last_ch_len >= s_charsize(*last_ch)) { /* the last character fits in the truncation - we're fine */ return len; } else { /* * the last character doesn't fit - truncate so that none of * the last character's bytes are included */ return (last_ch - p); } } /* * Convert a utf8 string to a wchar_t string, with null termination. * Returns the number of wchar_t characters in the string. If the * given buffer isn't sufficient, we'll only convert as many characters * as will fit, but we'll still return the full length of the string. */ static size_t to_wcharz(wchar_t *buf, size_t bufcnt, const char *str, size_t len) { /* we need at least one character for null termination */ if (bufcnt > 0) { /* convert the string, reserving space for a null */ size_t cnt = to_wchar(buf, bufcnt - 1, str, len); /* add the null termination */ buf[bufcnt-1] = 0; /* return the character count */ return cnt; } else { /* * the output buffer is zero length, so we can't convert * anything and can't null terminate; just calculate the length */ return to_wchar(buf, bufcnt, str, len); } } /* * Convert a utf8 string to a wchar_t string, without null termination. */ static size_t to_wchar(wchar_t *buf, size_t bufcnt, const char *str, size_t len) { /* scan the string */ int cnt = 0; for (utf8_ptr p((char *)str) ; len != 0 ; p.inc(&len), ++cnt) { /* if there's room, add this character to the output buffer */ if (bufcnt > 0) { *buf++ = p.getch(); --bufcnt; } } /* return the character count */ return cnt; } private: /* the buffer pointer */ char *p_; }; /* ------------------------------------------------------------------------ */ /* * Case folding utf8 string reader */ class Utf8FoldStr { public: Utf8FoldStr(const char *p, size_t bytelen) { /* set up at the start of the string */ this->p.set((char *)p); this->rem = bytelen; /* null-terminate our special one-byte identity conversion buffer */ ie[1] = 0; /* start without anything loaded */ fpbase = ie; fp = &ie[1]; } /* get the current byte offset */ const char *getptr() const { return p.getptr(); } /* is a character available? */ int more() const { return rem != 0 || *fp != 0 || fp == ie; } /* are we at a character boundary in the original string? */ int at_boundary() const { return fp != fpbase && *fp == 0; } /* get the next character */ wchar_t getch() { /* if the expansion is exhausted, expand the next source character */ if (fp != ie && *fp == 0) { /* if there's nothing left in the source string, we're done */ if (rem == 0) return 0; /* get the next source character */ wchar_t ch = p.getch(); p.inc(&rem); /* get its case-folded expansion */ if ((fp = t3_to_fold(ch)) == 0) { ie[0] = ch; fpbase = fp = ie; } } /* return the next expansion character */ return *fp++; } private: /* current position in case folding expansion of current 's' character */ const wchar_t *fp; /* start of current expansion */ const wchar_t *fpbase; /* buffer for identity expansions */ wchar_t ie[2]; /* pointer into original string source, and remaining length in bytes */ utf8_ptr p; size_t rem; }; #endif /* UTF8_H */ frobtads-1.2.3/tads3/vmfindrep.h0000644000175000001440000012261312000111553015656 0ustar realncusers/* $Header$ */ /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmfindrep.h - find/replace common code template Function Defines the common code template for String.findReplace() and rexReplace(). The two functions are very similar, but have some slight interface differences. Implementing as a common subroutine has a measurable negative impact on performance; typical TADS games make heavy use of these functions, so optimizing them for speed is beneficial. By using a common code template, we can avoid the extra calls and run-time condition checking that's necessary in a common subroutine; in the template version, we essentially unroll the code and apply the conditions and test the conditions statically. This makes for larger but faster code. Notes Modified 06/17/12 MJRoberts - Creation */ #ifndef VMFINDREP_H #define VMFINDREP_H /* ------------------------------------------------------------------------ */ /* * re_replace flags */ /* replace all matches (if omitted, replaces only the first match) */ #define VMBIFTADS_REPLACE_ALL 0x0001 /* ignore case in matching the search string */ #define VMBIFTADS_REPLACE_NOCASE 0x0002 /* * follow case: lower-case characters in the the replacement text are * converted to follow the case pattern of the matched text (all lower, * initial capital, or all capitals) */ #define VMBIFTADS_REPLACE_FOLLOW_CASE 0x0004 /* * serial replacement: when a list of search patterns is used, we replace * each occurrence of the first pattern over the whole string, then we * start over with the result and scan for occurrences of the second * pattern, replacing each of these, and so on. If this flag isn't * specified, the default is parallel scanning: we find the leftmost * occurrence of any of the patterns and replace it; then we scan the * remainder of the string for the leftmost occurrence of any pattern, and * replace that one; and so on. The serial case is equivalent to making a * series of calls to this function with the individual search patterns. */ #define VMBIFTADS_REPLACE_SERIAL 0x0008 /* * Replace only the first occurrence. */ #define VMBIFTADS_REPLACE_ONCE 0x0010 /* * re_replace pattern/replacement structure. This is used to handle arrays * of arguments: each element represents one pattern->replacement mapping. */ struct re_replace_arg { re_replace_arg() { s = 0; pat = 0; pat_str = 0; rpl_func.set_nil(); match_valid = FALSE; } ~re_replace_arg() { /* if we created the pattern object, delete it */ if (pat != 0 && our_pat) CRegexParser::free_pattern(pat); if (s != 0) delete s; } void set(VMG_ const vm_val_t *patv, const vm_val_t *rplv, int str_to_pat, int32_t flags) { /* save the flags */ this->flags = flags; /* retrieve the compiled RexPattern or uncompiled pattern string */ const char *str; if (patv->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ patv->val.obj)) { /* create the searcher, if we haven't already */ create_searcher(vmg0_); /* it's a pattern object - get its compiled pattern structure */ pat = ((CVmObjPattern *)vm_objp(vmg_ patv->val.obj)) ->get_pattern(vmg0_); /* we didn't create the pattern object */ our_pat = FALSE; } else if ((str = patv->get_as_string(vmg0_)) != 0) { /* it's a string - decide how to treat it */ if (str_to_pat) { /* create the searcher */ create_searcher(vmg0_); /* we treat strings as regular expressions - compile it */ re_status_t stat; stat = G_bif_tads_globals->rex_parser->compile_pattern( str + VMB_LEN, vmb_get_len(str), &pat); /* if that failed, we don't have a pattern */ if (stat != RE_STATUS_SUCCESS) pat = 0; /* make a note that we allocated the pattern */ our_pat = TRUE; } else { /* we treat strings as simple literal search strings */ pat_str = str; } } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } /* * Save the replacement value. This can be a string, nil (which we * treat like an empty string), or a callback function. */ if (rplv->typ == VM_NIL) { /* treat it as an empty string */ rpl_str = "\000\000"; } else if ((str = rplv->get_as_string(vmg0_)) != 0) { /* save the string value */ rpl_str = str; } else if (rplv->is_func_ptr(vmg0_)) { /* it's a function or invokable object */ rpl_str = 0; rpl_func = *rplv; /* get the number of arguments it expects */ CVmFuncPtr f(vmg_ rplv); rpl_argc = f.is_varargs() ? -1 : f.get_max_argc(); } else { /* invalid type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* create the regex searcher */ void create_searcher(VMG0_) { /* create our searcher if we haven't yet */ if (s == 0) { /* create the searcher object */ s = new CRegexSearcherSimple(G_bif_tads_globals->rex_parser); /* turn off case sensitivity in the searcher if desired */ if ((flags & VMBIFTADS_REPLACE_NOCASE) != 0) s->set_default_case_sensitive(FALSE); } } void search(VMG_ const char *str, int start_idx, const char *last_str) { /* if we have a pattern, search for it */ if (pat_str != 0) { /* * whether or not we find a match, the search will be valid * when we return */ match_valid = TRUE; match_len = 0; /* set up a pointer to the current position */ utf8_ptr p((char *)str + VMB_LEN + start_idx); /* figure the number of bytes left in the string */ size_t rem = vmb_get_len(str) - start_idx; /* note the length of the search string */ size_t pat_len = vmb_get_len(pat_str); /* scan for a match, according to case sensitivity */ if (!(flags & VMBIFTADS_REPLACE_NOCASE)) { /* case sensitive - scan for a match */ for ( ; rem >= pat_len ; p.inc(&rem)) { /* no case folding, so simply compare byte by byte */ if (memcmp(p.getptr(), pat_str + VMB_LEN, pat_len) == 0) { /* success - remember the match length and position */ match_len = pat_len; match_idx = p.getptr() - (str + VMB_LEN); /* we're done */ return; } } } else { /* case-insensitive - scan for a match */ for ( ; rem >= pat_len ; p.inc(&rem)) { /* compare with case folding */ size_t ml; if (t3_compare_case_fold( pat_str + VMB_LEN, pat_len, p.getptr(), rem, &ml) == 0) { /* success - set the match index, and we're done */ match_idx = p.getptr() - (str + VMB_LEN); match_len = ml; return; } } } /* we didn't a match - set the match index to so indicate */ match_idx = -1; } if (pat != 0) { /* search for the regular expression */ match_idx = s->search_for_pattern( pat, str + VMB_LEN, last_str, vmb_get_len(str) - start_idx, &match_len); /* if we found it, adjust to the absolute offset in the string */ if (match_idx >= 0) match_idx += start_idx; } else { /* no pattern -> no match */ match_idx = -1; } /* whether or not we matched, our result is now valid */ match_valid = TRUE; } /* our search pattern, or null if we're searching for a literal string */ re_compiled_pattern *pat; /* our search string, or null if we're searching for a pattern */ const char *pat_str; /* Did we create the pattern? If so, delete it on destruction. */ int our_pat; /* our replacement string, or null if it's a callback function */ const char *rpl_str; /* our replacement function, in lieu of a string */ vm_val_t rpl_func; /* the number of arguments rpl_func expects, or -1 for varargs */ int rpl_argc; /* search flags */ int32_t flags; /* the byte index and length in the source string of our last match */ int match_idx; int match_len; /* * Is this last match data valid? This is false if we haven't done the * first search yet, or if the last replacement overlapped our matching * text at all. */ int match_valid; /* searcher - this holds the group registers for the last match */ CRegexSearcherSimple *s; }; /* ------------------------------------------------------------------------ */ /* * values for vm_find_replace */ #define VMFINDREPLACE_StringFindReplace 0 #define VMFINDREPLACE_rexReplace 1 /* * Common find-and-replace handler for String.findReplace() and * rexReplace(). The two functions are essentially the same; the only * differences are: * * 1. The arguments are in a slightly different order. *. rexReplace(pattern, subject, replacement, flags?, index?) *. subject.findReplace(pattern, replacement, flags?, index?) * * Note the placement of the subject string argument; it moves outside the * argument list to the target object position in the String method call * version, so the subsequent arguments all move down one position. * * 2. In String.findReplace(), the 'pattern' argument is taken as literal * text if a string value is passed. In rexReplace(), a string value for * 'pattern' is taken as an uncompiled regular expression. For either * function, a RexPattern object can be passed, in which case it's treated * as a regular expression (obviously). * * 3. If 'pattern' is a literal string, 'replacement' is also treated as * literal text; if 'pattern' is a regular expression (either explicitly as * a RexPattern object, or as a string taken to be an uncompiled regular * expression in rexReplace()), '%' sequences are used for group * replacements; e.g., '%1' is replaced with the matching text for the * first parenthesized group in the regex, '%2' by the second group's * matching text, etc. */ template inline void vm_find_replace( VMG_ vm_val_t *result, int argc, const vm_val_t *subj, const char *subj_str) { int i; vm_rcdesc rc; const int new_str_ofs = (mode == VMFINDREPLACE_StringFindReplace ? 1 : 2); const int pat_str_is_regex = (mode == VMFINDREPLACE_rexReplace); /* remember the original subject string */ const vm_val_t *orig_subj = subj; /* get the flags; use ReplaceAll if omitted */ int32_t flags = VMBIFTADS_REPLACE_ALL; const vm_val_t *flagp; if (new_str_ofs + 1 < argc && (flagp = G_stk->get(new_str_ofs + 1))->typ != VM_NIL) { /* check the type */ if (flagp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* retrieve the value */ flags = flagp->val.intval; } /* * Check for old flags. Before 3.1, there was only one flag bit * defined: ALL=1. This means there were only two valid values for * 'flags': 0 for ONCE mode, 1 for ALL mode. * * In 3.1, we added a bunch of new flags. At the same time, we made * the default ALL mode, because this is the more common case. * Unfortunately, this creates a compatibility issue. A new program * that specifies one of the new flags might leave out the ONCE or ALL * bits, since ALL is the default. However, we can't just take the * absence of the ONCE bit as meaning ALL, because that would hose old * programs that explicitly specify ONCE as 0 (no bits set). * * Here's how we deal with this: we prohibit new programs from passing * 0 for the flags, requiring them to specify at least one bit. So if * the flags value is zero, we must have an old program that passed * ONCE. In this case, explicitly set the ONCE bit. If we have any * non-zero value, we must have either a new program OR an old program * that included the ALL bit. In either case, ALL is the default, so * if the ONCE bit ISN'T set, explicitly set the ALL bit. */ if (flags == 0) { /* old program with the old ONCE flag - set the new ONCE flag */ flags = VMBIFTADS_REPLACE_ONCE; } else if (!(flags & VMBIFTADS_REPLACE_ONCE)) { /* * new program without the ONCE flag, OR an old program with the * ALL flag - explicitly set the ALL flag */ flags |= VMBIFTADS_REPLACE_ALL; } /* assume that the limit is one or infinity, per the 'flags' value */ int limit = ((flags & VMBIFTADS_REPLACE_ONCE) != 0 ? 1 : -1); /* if there's an explicit 'limit' argument, fetch it */ if (new_str_ofs + 3 < argc) { /* nil means explicitly unlimited; otherwise it's an int */ vm_val_t *limitp = G_stk->get(new_str_ofs + 3); if (limitp->typ == VM_NIL) { /* unlimited - signify with -1 */ limit = -1; } else if (limitp->typ == VM_INT) { /* get the limit, and make sure it's non-negative */ if ((limit = limitp->val.intval) < 0) err_throw(VMERR_BAD_VAL_BIF); /* * if the limit is zero, the result string will obviously be * identical to the source string, so just return it now */ if (limit == 0) { *result = *subj; return; } } else err_throw(VMERR_INT_VAL_REQD); } /* get the search substring or pattern value */ const vm_val_t *pat = G_stk->get(0); /* assume we have a single pattern to search for */ int pat_cnt = 1; int pat_is_list = FALSE; /* * if we're treating strings as literals (rather than uncompiled * regular expressions), assume we won't have any REs to search for */ int have_pat = pat_str_is_regex; /* check whether the pattern is given as an array or as a single value */ if (pat->is_listlike(vmg0_) && (pat_cnt = pat->ll_length(vmg0_)) >= 0) { /* It's a list. Check first to see if it's empty. */ if (pat_cnt == 0) { /* * empty list, so there's no work to do - simply return the * original subject string unchanged */ *result = *subj; return; } /* flag it as a list */ pat_is_list = TRUE; } /* get the replacement value from the stack */ const vm_val_t *rpl = G_stk->get(new_str_ofs); /* assume we have a single replacement value */ int rpl_cnt = 1; int rpl_is_list = FALSE; /* check to see if the replacement is a list */ if (rpl->is_listlike(vmg0_) && (rpl_cnt = rpl->ll_length(vmg0_)) >= 0) { /* flag it as a list */ rpl_is_list = TRUE; } /* allocate the argument array */ re_replace_arg *pats = new re_replace_arg[pat_cnt]; /* catch any errors so that we can free our arg array on the way out */ err_try { /* assume we won't need a recursive callback context */ int need_rc = FALSE; /* set up the pattern/replacement array from the arguments */ for (i = 0 ; i < pat_cnt ; ++i) { /* get the next pattern from the list, or the single pattern */ vm_val_t patele; if (pat_is_list) { /* we have a list - get the next element */ pat->ll_index(vmg_ &patele, i + 1); } else { /* we have a single pattern item */ patele = *pat; } /* * Get the next replacement from the list, or the single * replacement. If we have a single value for the replacement, * every pattern has the same replacement. If we have a list, * the pattern has the replacement at the corresponding index. * If it's a list and we're past the last replacement index, * use an empty string. */ vm_val_t rplele; if (rpl_is_list) { /* * we have a list - if we have an item at the current * index, it's the corresponding replacement for the * current pattern; if we've exhausted the replacement * list, use an empty string as the replacement */ if (i < rpl_cnt) rpl->ll_index(vmg_ &rplele, i + 1); else rplele.set_nil(); } else { /* single replacement item - it applies to all patterns */ rplele = *rpl; } /* fill in this argument item */ pats[i].set(vmg_ &patele, &rplele, pat_str_is_regex, flags); /* note if we have a regex */ have_pat |= (pats[i].pat != 0); /* we need a callback context if the replacement is a function */ need_rc |= (pats[i].rpl_str == 0); } /* initialize the recursive callback context if needed */ if (need_rc) { if (mode == VMFINDREPLACE_StringFindReplace) rc.init(vmg_ "String.findReplace", orig_subj, 11, G_stk->get(0), argc); else rc.init(vmg_ "rexReplace", CVmBifTADS::bif_table, 12, G_stk->get(0), argc); } /* get the starting index, if present; use index 1 if not present */ int start_char_idx = 1; const vm_val_t *idxp; if (new_str_ofs + 2 < argc && (idxp = G_stk->get(new_str_ofs + 2))->typ != VM_NIL) { /* check the type */ if (idxp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* get the value */ start_char_idx = idxp->val.intval; } /* * push a nil placeholder for the result value (we keep it on the * stack for gc protection) */ G_stk->push()->set_nil(); /* if this is a serial search, we'll start at the first pattern */ int serial_idx = 0; /* make sure the search string is indeed a string */ const char *str = subj_str; if (str == 0 && (str = subj->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* set up a utf8 pointer to the search string */ utf8_ptr strp((char *)str + VMB_LEN); /* adjust the starting index */ start_char_idx += (start_char_idx < 0 ? (int)strp.len(vmb_get_len(str)) : -1); restart_search: /* * remember the last search string globally, for group extraction; * and forget any old group registers, since they'd point into the * old string we're superseding */ G_bif_tads_globals->last_rex_str->val = *subj; if (have_pat) G_bif_tads_globals->rex_searcher->clear_group_regs(); /* * don't allocate anything for the result yet - we'll wait to do * that until we actually find a match, so that we don't allocate * memory unnecessarily */ vm_obj_id_t ret_obj = VM_INVALID_OBJ; CVmObjString *ret_str = 0; /* set up a null output string pointer for now */ utf8_ptr dstp; dstp.set((char *)0); /* * figure out how many bytes at the start of the string to skip * before our first replacement */ utf8_ptr p; size_t rem; for (p.set((char *)str + VMB_LEN), rem = vmb_get_len(str), i = 0 ; i < start_char_idx && rem != 0 ; ++i, p.inc(&rem)) ; /* the current offset in the string is the byte skip offset */ int skip_bytes = p.getptr() - (str + VMB_LEN); /* * Start searching from the beginning of the string. Build the * result string as we go. */ int start_idx; for (start_idx = skip_bytes ; (size_t)start_idx < vmb_get_len(str) ; ) { /* figure out where the next search starts */ const char *last_str = str + VMB_LEN + start_idx; /* we haven't found a matching pattern yet */ int best_pat = -1; /* do the next serial or parallel search */ if ((flags & VMBIFTADS_REPLACE_SERIAL) != 0) { /* * Serial search: search for one item at a time. If we're * out of items, we're done. */ if (serial_idx >= pat_cnt) break; /* search for the next item */ pats[serial_idx].search(vmg_ str, start_idx, last_str); /* if we didn't get a match, we're done */ if (pats[serial_idx].match_idx < 0) break; /* this is our replacement match */ best_pat = serial_idx; } else { /* * Parallel search: search for all of the items, and * replace the leftmost match. We might still have valid * search results for some items on past iterations, but * others might have overlapped replacement text, in which * case we'll have to refresh them. So do a search for * each item that's marked as invalid. */ /* search for each item */ for (i = 0 ; i < pat_cnt ; ++i) { /* refresh this search, if it's invalid */ if (!pats[i].match_valid) pats[i].search(vmg_ str, start_idx, last_str); /* if this is the leftmost result so far, remember it */ if (pats[i].match_idx >= 0 && (best_pat < 0 || pats[i].match_idx < pats[best_pat].match_idx)) best_pat = i; } /* if we didn't find a match, we're done */ if (best_pat < 0) break; } /* * Keep the leftmost pattern we matched. Note that we want a * relative offset from last_str, so adjust from the absolute * offset in the string that the pats[] entry uses. */ int match_idx = pats[best_pat].match_idx - start_idx; int match_len = pats[best_pat].match_len; const char *rplstr = pats[best_pat].rpl_str; vm_val_t rplfunc = pats[best_pat].rpl_func; /* * if we have the 'follow case' flag, note the capitalization * pattern of the match */ int match_has_upper = FALSE, match_has_lower = FALSE; if ((flags & VMBIFTADS_REPLACE_FOLLOW_CASE) != 0) { /* scan the match text */ for (p.set((char *)last_str + match_idx), rem = match_len ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* note whether it's upper or lower case */ match_has_upper |= t3_is_upper(ch); match_has_lower |= t3_is_lower(ch); } } /* remember the group info, if we have a regex pattern */ int group_cnt = 0; if (pats[best_pat].pat != 0) { /* note the group count */ group_cnt = pats[best_pat].pat->group_cnt; /* copy the group registers to the global searcher registers */ G_bif_tads_globals->rex_searcher->copy_group_regs( pats[best_pat].s); } /* if we have a replacement limit, count this against the limit */ if (limit > 0) --limit; /* * If we haven't allocated a result string yet, do so now, * since we finally know we actually need one. */ if (ret_obj == VM_INVALID_OBJ) { /* * We don't know yet how much space we'll need for the * result, so this is only a temporary allocation. As a * rough guess, use three times the length of the input * string. We'll expand this as needed as we build the * string, and shrink it down to the real size when we're * done. */ ret_obj = CVmObjString::create(vmg_ FALSE, vmb_get_len(str)*3); /* save it in our stack slot, for gc protection */ G_stk->get(0)->set_obj(ret_obj); /* get the string object pointer */ ret_str = (CVmObjString *)vm_objp(vmg_ ret_obj); /* get a pointer to the result buffer */ dstp.set(ret_str->cons_get_buf()); /* copy the initial part that we skipped */ if (skip_bytes != 0) { memcpy(dstp.getptr(), str + VMB_LEN, skip_bytes); dstp.set(dstp.getptr() + skip_bytes); } } /* copy the part up to the start of the matched text, if any */ if (match_idx > 0) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), match_idx, 512)); /* copy the part from the last match to this match */ memcpy(dstp.getptr(), last_str, match_idx); /* advance the output pointer */ dstp.set(dstp.getptr() + match_idx); } /* apply the replacement text */ if (rplstr != 0) { /* get the length of the string */ size_t rpllen = vmb_get_len(rplstr); /* we haven't substituted any alphabetic character yet */ int alpha_rpl_cnt = 0; /* note whether we have a literal search string or a regex */ int pat_is_str = (pats[best_pat].pat == 0); /* * if we're replacing a string pattern (not a regex), and * there's no case following, we can plop in the whole * replacement string without examining it */ if (pat_is_str && !(flags & VMBIFTADS_REPLACE_FOLLOW_CASE)) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), rpllen, 512)); /* copy the replacement text */ memcpy(dstp.getptr(), rplstr + VMB_LEN, rpllen); /* advance the pointer */ dstp.set(dstp.getptr() + rpllen); } else { /* * copy the replacement string into the output string, * expanding group substitutions if we have a regex */ for (p.set((char *)rplstr + VMB_LEN), rem = rpllen ; rem != 0 ; p.inc(&rem)) { /* fetch the character */ wchar_t ch = p.getch(); /* if replacing a regex, check for '%' sequences */ if (pat_is_str || ch != '%' || rem == 1) { /* * ensure we have space for this character (one * UTF8 needs 3 bytes max) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 3, 512)); /* * if we're in 'follow case' mode, adjust the * case of lower-case literal letters in the * replacement text */ const wchar_t *u = 0; if ((flags & VMBIFTADS_REPLACE_FOLLOW_CASE) != 0 && t3_is_lower(ch)) { /* * Check the mode: if we have all * upper-case in the match, convert the * replacement to all caps; all lower-case * in the match -> all lower in the * replacement; mixed -> capitalize the * first letter only */ if (match_has_upper && !match_has_lower) { /* all upper -> convert to upper */ u = t3_to_upper(ch); } else if (match_has_upper && match_has_lower && alpha_rpl_cnt++ == 0) { /* mixed case -> use title case */ u = t3_to_title(ch); } } /* if we found a case conversion, apply it */ if (u != 0) { /* ensure we have room for the conversion */ size_t need = utf8_ptr::s_wstr_size(u); dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), need, 512)); /* store the converted characters */ dstp.setwcharsz(u, need); } else { /* no case conversion - copy it as-is */ dstp.setch(ch); } } else { /* skip the '%' and see what follows */ p.inc(&rem); switch(p.getch()) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* get the group number */ int groupno = value_of_digit(p.getch()) - 1; /* if the group is valid, add its length */ if (groupno < group_cnt) { /* get the register */ const re_group_register *reg = G_bif_tads_globals->rex_searcher ->get_group_reg(groupno); /* if it's been set, add its text */ if (reg->start_ofs != -1 && reg->end_ofs != -1) { /* get the group length */ size_t glen = reg->end_ofs - reg->start_ofs; /* ensure space */ dstp.set( ret_str->cons_ensure_space( vmg_ dstp.getptr(), glen, 512)); /* copy the data */ memcpy(dstp.getptr(), str + VMB_LEN + reg->start_ofs, glen); /* advance past it */ dstp.set(dstp.getptr() + glen); } } } break; case '*': /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), match_len, 512)); /* add the entire matched string */ memcpy(dstp.getptr(), last_str + match_idx, match_len); dstp.set(dstp.getptr() + match_len); break; case '%': /* ensure space (the '%' is just one byte) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 1, 512)); /* add a single '%' */ dstp.setch('%'); break; default: /* * ensure space (we need 1 byte for the * '%', up to 3 for the other character) */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 4, 512)); /* add the entire sequence unchanged */ dstp.setch('%'); dstp.setch(p.getch()); break; } } } } } else { /* push the callback args - matchStr, matchIdx, subject */ const int pushed_argc = 3; G_stk->push(orig_subj); G_interpreter->push_int( vmg_ strp.len(start_idx + match_idx) + 1); G_interpreter->push_obj(vmg_ CVmObjString::create( vmg_ FALSE, last_str + match_idx, match_len)); /* adjust argc for what the callback actually wants */ int fargc = pats[best_pat].rpl_argc; if (fargc < 0 || fargc > pushed_argc) fargc = pushed_argc; /* call the callback */ G_interpreter->call_func_ptr(vmg_ &rplfunc, fargc, &rc, 0); /* discard extra arguments */ G_stk->discard(pushed_argc - fargc); /* if the return value isn't nil, copy it into the result */ if (G_interpreter->get_r0()->typ != VM_NIL) { /* get the string */ const char *r = G_interpreter->get_r0()->get_as_string(vmg0_); if (r == 0) err_throw(VMERR_STRING_VAL_REQD); /* ensure space for it in the result */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), vmb_get_len(r), 512)); /* store it */ memcpy(dstp.getptr(), r + VMB_LEN, vmb_get_len(r)); dstp.set(dstp.getptr() + vmb_get_len(r)); } } /* advance past this matched string for the next search */ start_idx += match_idx + match_len; /* skip to the next character if it was a zero-length match */ if (match_len == 0 && (size_t)start_idx < vmb_get_len(str)) { /* ensure space */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), 3, 512)); /* copy the character we're skipping to the output */ p.set((char *)str + VMB_LEN + start_idx); dstp.setch(p.getch()); /* move on to the next character */ start_idx += 1; } /* * In a parallel search, discard any match that started before * the new starting point. Those are no longer valid because * they matched original text that was wholly or partially * replaced by the current iteration. */ for (i = 0 ; i < pat_cnt ; ++i) { /* invalidate this match if it's before the replacement */ if (pats[i].match_idx >= 0 && pats[i].match_idx < start_idx) pats[i].match_valid = FALSE; } /* if we've reached the replacement limit, stop now */ if (limit == 0) break; } /* if we did any replacements on this round, finish the string */ if (ret_obj != VM_INVALID_OBJ) { /* ensure space for the remainder after the last match */ dstp.set(ret_str->cons_ensure_space( vmg_ dstp.getptr(), vmb_get_len(str) - start_idx, 512)); /* add the part after the end of the matched text */ if ((size_t)start_idx < vmb_get_len(str)) { memcpy(dstp.getptr(), str + VMB_LEN + start_idx, vmb_get_len(str) - start_idx); dstp.set(dstp.getptr() + vmb_get_len(str) - start_idx); } /* set the actual length of the string */ ret_str->cons_shrink_buffer(vmg_ dstp.getptr()); /* return the string */ result->set_obj(ret_obj); } else { /* we didn't replace anything, so keep the original string */ *result = *subj; } /* * If this is a serial search, and we have another item in the * search list, go back and start over with the current result as * the new search string. Exception: if we've reached the * replacement count limit, we're done. */ if ((flags & VMBIFTADS_REPLACE_SERIAL) != 0 && limit != 0 && ++serial_idx < pat_cnt) { /* use the return value as the new search value */ subj = result; *G_stk->get(2) = *subj; /* get the string again in case we're doing a serial iteration */ str = subj->get_as_string(vmg0_); /* go back for a brand new round */ goto restart_search; } /* discard our gc protection */ G_stk->discard(1); } err_finally { /* if we allocated an argument array, delete it */ if (pats != 0) delete [] pats; } err_end; } #endif /* VMFINDREP_H */ frobtads-1.2.3/tads3/vmsrcf.h0000644000175000001440000001033511321456342015176 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsrcf.h - T3 VM source file table Function Maintains a list of the source file records found in the "SRCF" block in the image file Notes Modified 12/01/99 MJRoberts - Creation */ #ifndef VMSRCF_H #define VMSRCF_H #include "t3std.h" /* * Source file line record */ struct CVmSrcfLine { /* line number in source file */ ulong linenum; /* code address of generated code for source line */ ulong code_addr; }; /* * Source file entry */ class CVmSrcfEntry { public: CVmSrcfEntry(int orig_index, int is_orig, size_t name_len) { /* no source lines yet */ lines_ = 0; lines_cnt_ = lines_alo_ = 0; /* set the original index */ orig_index_ = orig_index; /* remember whether this is the master entry */ is_orig_ = is_orig; /* allocate space for our name buffer */ name_buf_ = (char *)t3malloc(name_len + 1); } ~CVmSrcfEntry() { /* delete our line records, if we have any */ if (lines_ != 0) t3free(lines_); /* delete our name buffer */ t3free(name_buf_); } /* determine if the entry is the master record */ int is_master() const { return is_orig_; } /* get my name */ const char *get_name() const { return name_buf_; } /* get a pointer to my name buffer */ char *get_name_buf() { return name_buf_; } /* * allocate space for source line entries - this can be used to * pre-allocate space if the number of line entries is known in * advance */ void alloc_line_records(ulong cnt); /* * Add a source line entry. Line entries must be added in ascending * order of line number. */ void add_line_record(ulong linenum, ulong code_addr); /* * Find the code address for a given source line. Returns zero if * we are unsuccessful, non-zero if successful (an executable line * can never have a source address equal to zero, since if a method * is at such a low address at all, its header would take up the * first few bytes, putting the first executable instruction at a * non-zero address). * * If 'exact' is true, we'll fail unless we find an exact match; * otherwise, we'll return the code address for the next executable * line after the given line, or the last executable line in the * file if there are no more executable lines after the given line. * * If we find a non-exact match, we'll update *linenum to give the * actual line number where we found the executable line. */ ulong find_src_addr(ulong *linenum, int exact); private: /* name buffer */ char *name_buf_; /* index of original entry for this filename */ int orig_index_; /* flag: I'm the original entry */ uint is_orig_ : 1; /* * Line records array. For simplicity, this is simply a single * array. This could be a little constraining for 16-bit machines, * but even on a 16-bit machine, this will accommodate 8000 records, * which should be enough for even fairly large source files. */ CVmSrcfLine *lines_; /* number of line records actually in use */ ulong lines_cnt_; /* number of lines allocated */ ulong lines_alo_; }; /* * Source file table */ class CVmSrcfTable { public: CVmSrcfTable(); ~CVmSrcfTable(); /* clear the table */ void clear(); /* add a new entry */ CVmSrcfEntry *add_entry(int orig_index, size_t name_len); /* get an entry given the 0-based index */ CVmSrcfEntry *get_entry(size_t idx) const { return (idx < list_used_ ? list_[idx] : 0); } /* get the number of entries */ size_t get_count() const { return list_used_; } private: /* array of entries */ CVmSrcfEntry **list_; /* number of entries currently in use in the list */ size_t list_used_; /* number of entries allocated in the list */ size_t list_alloc_; }; #endif /* VMSRCF_H */ frobtads-1.2.3/tads3/tcerr.h0000644000175000001440000000333307627507702015030 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCERR.H,v 1.3 1999/05/17 02:52:27 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerr.h - TADS 3 Compiler Error Management Function Notes Modified 04/22/99 MJRoberts - Creation */ #ifndef TCERR_H #define TCERR_H #include "vmerr.h" /* * Error Severity Levels. */ enum tc_severity_t { /* information only - not an error */ TC_SEV_INFO, /* * Pedantic warning - doesn't prevent compilation from succeeding, * and doesn't even necessarily indicate anything is wrong. We use * a separate severity level for these because some users will want * to be able to filter these out. */ TC_SEV_PEDANTIC, /* * warning - does not prevent compilation from succeeding, but * indicates a potential problem */ TC_SEV_WARNING, /* * error - compilation cannot be completed successfully, although it * may be possible to continue with compilation to the extent * necessary to check for any additional errors */ TC_SEV_ERROR, /* fatal - compilation must be immediately aborted */ TC_SEV_FATAL, /* internal error - compilation must be immediately aborted */ TC_SEV_INTERNAL }; /* English version of message array */ extern const err_msg_t tc_messages_english[]; extern size_t tc_message_count_english; /* error message array */ extern const err_msg_t *tc_messages; extern size_t tc_message_count; /* look up a message */ const char *tcerr_get_msg(int msgnum, int verbose); #endif /* TCERR_H */ frobtads-1.2.3/tads3/tct3nl.cpp0000644000175000001440000000467511322342617015452 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3nl.cpp - T3 NO LINKER module Function This module can be linked with programs that don't require any linker functionality. This module contains dummy entrypoints for functions and methods that would normally be linked from tct3img.cpp, which contains the actual implementations of these methods. Notes Modified 11/22/99 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "tct3.h" /* ------------------------------------------------------------------------ */ /* * Symbol table entry routines for writing a symbol to the global symbol * table in the debug records in the image file. When we don't include * linking functionality in a program, these entrypoints will never be * called (but they're needed to link anything that references a * CTcSymbol subclass, since they're virtual functions in those classes) */ /* * write the symbol to an image file's global symbol table */ int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymObj::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * build the dictionary */ void CTcSymObj::build_dictionary() { assert(FALSE); } /* * write the symbol to an image file's global symbol table */ int CTcSymProp::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymMetaclass::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymBif::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } /* * write the symbol to an image file's global symbol table */ int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *) { assert(FALSE); return FALSE; } frobtads-1.2.3/tads3/vmfilnam.cpp0000644000175000001440000016633612145504453016061 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilnam.cpp - CVmObjFileName object Function Notes Modified 03/03/12 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmfilnam.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmfilobj.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "utf8.h" #include "charmap.h" #include "vmnetfil.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_filnam_ext *vm_filnam_ext::alloc_ext( VMG_ CVmObjFileName *self, int32_t sfid, const char *str, size_t len) { /* * Calculate how much space we need - we need space for the name string * plus its length prefix; store the name string with a null terminator * for convenience in calling osifc routines. */ size_t siz = (sizeof(vm_filnam_ext)-1) + (VMB_LEN + len + 1); /* allocate the memory */ vm_filnam_ext *ext = (vm_filnam_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* store the special file ID; assume it's valid */ ext->sfid = sfid; ext->sfid_valid = TRUE; /* assume that the file didn't come from a manual user interaction */ ext->from_ui = 0; /* set the string length and null terminator */ vmb_put_len(ext->str, len); ext->str[VMB_LEN + len] = '\0'; /* copy the string, if provided */ if (str != 0) memcpy(ext->str + VMB_LEN, str, len); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjFileName object statics */ /* metaclass registration object */ static CVmMetaclassFileName metaclass_reg_obj; CVmMetaclass *CVmObjFileName::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjFileName::*CVmObjFileName::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjFileName::getp_undef, /* 0 */ &CVmObjFileName::getp_getName, /* 1 */ &CVmObjFileName::getp_getRootName, /* 2 */ &CVmObjFileName::getp_getPath, /* 3 */ &CVmObjFileName::getp_fromUniversal, /* 4 */ &CVmObjFileName::getp_toUniversal, /* 5 */ &CVmObjFileName::getp_addToPath, /* 6 */ &CVmObjFileName::getp_isAbsolute, /* 7 */ &CVmObjFileName::getp_getAbsolutePath, /* 8 */ &CVmObjFileName::getp_getRootDirs, /* 9 */ &CVmObjFileName::getp_getFileType, /* 10 */ &CVmObjFileName::getp_getFileInfo, /* 11 */ &CVmObjFileName::getp_deleteFile, /* 12 */ &CVmObjFileName::getp_renameFile, /* 13 */ &CVmObjFileName::getp_listDir, /* 14 */ &CVmObjFileName::getp_forEachFile, /* 15 */ &CVmObjFileName::getp_createDirectory, /* 16 */ &CVmObjFileName::getp_removeDirectory /* 17 */ }; /* property indices */ const int VMOFN_FROMUNIVERSAL = 4; const int VMOFN_GETROOTDIRS = 9; const int VMOFN_GETFILETYPE = 10; const int VMOFN_GETFILEINFO = 11; const int VMOFN_DELETEFILE = 12; const int VMOFN_RENAMEFILE = 13; const int VMOFN_LISTDIR = 14; const int VMOFN_FOREACHFILE = 15; const int VMOFN_CREATEDIR = 16; const int VMOFN_REMOVEDIR = 17; /* ------------------------------------------------------------------------ */ /* * Cover functions for the various OS path operations. These test the * network storage mode: if we're in network mode, we use the storage * server syntax, which follows Unix rules regardless of the local OS. * Otherwise we use the local OS rules, via the osifc functions. */ /* canonicalize a network path: resolve . and .. links */ static void nf_canonicalize(char *path) { /* note if it's an absolute path */ int abs = path[0] == '/'; /* make a list of the path elements */ char **ele = new char*[strlen(path)]; int cnt; for (cnt = 0, ele[0] = strtok(path, "/") ; ele[cnt] != 0 ; ele[++cnt] = strtok(0, "/")) ; /* remove '.' and empty elements, and cancel '..' against prior elements */ int src, dst; for (src = dst = 0 ; src < cnt ; ) { if (ele[src][0] == '\0' || strcmp(ele[src], ".") == 0) { /* empty or '.' - simply omit it */ ++src; } else if (strcmp(ele[src], "..") == 0) { /* * '..' - cancel against the previous element if there is one * and it's not '..', otherwise keep it */ if (dst > 0 && strcmp(ele[dst-1], "..") != 0) --dst, ++src; else ele[dst++] = ele[src++]; } else { /* ordinary element - copy it */ ele[dst++] = ele[src++]; } } /* reconstruct the string */ char *p = path; if (abs) *p++ = '/'; for (int i = 0 ; i < dst ; ++i) { size_t l = strlen(ele[i]); memcpy(p, ele[i], l); p += l; if (i + 1 < dst) *p++ = '/'; } *p = '\0'; /* if it's complete empty, make it '.' */ if (path[0] == '\0') path[0] = '.', path[1] = '\0'; /* done with the path list */ delete [] ele; } /* build a full path */ static void fn_build_full_path( VMG_ char *buf, size_t buflen, const char *dir, const char *fname, int literal) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * Network storage mode - use Unix rules. First, if the filename * is in absolute format already, use it unchanged. If not, append * the filename to the directory, adding a '/' between the two if * the directory doesn't already end with a slash. */ if (fname[0] == '/') { /* the file is absolute - return it unchanged */ lib_strcpy(buf, buflen, fname); } else { /* note the last character of the directory path */ size_t dirlen = strlen(dir); char dirlast = dirlen != 0 ? dir[dirlen-1] : 0; /* build the result */ t3sprintf(buf, buflen, "%s%s%s", dir, dirlast == '/' ? "" : "/", fname); } /* if not in literal mode, canonicalize the result */ if (!literal) nf_canonicalize(buf); } else { /* local storage mode - use local OS rules */ if (literal) os_combine_paths(buf, buflen, dir, fname); else os_build_full_path(buf, buflen, dir, fname); } } /* get the root name from a path string */ static const char *fn_get_root_name(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - use Unix rules, so simply find the * rightmost '/' and return the portion after it (or the whole * name, if there's no '/') */ const char *r = strrchr(str, '/'); return r != 0 ? r + 1 : str; } else { /* local storage mode - use local OS rules */ return os_get_root_name(str); } } /* get the path name portion from a filename string */ static void fn_get_path_name(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * Network storage mode - use Unix rules, so simply find the * rightmost '/' and return the portion before it (or an empty * string, if there's no '/'). If the right most '/' is the first * character, or is only preceded by '/' characters, it's a root * path, so return '/' as the path. */ const char *r = strrchr(str, '/'); if (r == 0) { /* there's no '/' at all, so there's no path */ buf[0] = '\0'; } else { /* skip any more '/'s immediately preceding the one we found */ for ( ; r > str && *(r-1) == '/' ; --r) ; /* if we're at the first character, it's a root path */ if (r == str) { /* return "/" for the root path */ lib_strcpy(buf, buflen, "/"); } else { /* return the portion up to but not including the '/' */ lib_strcpy(buf, buflen, str, r - str); } } } else { /* local storage mode - use local OS rules */ return os_get_path_name(buf, buflen, str); } } /* is the file in absolute path format? */ static int fn_is_file_absolute(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode - it's absolute if it starts with '/' */ return str[0] == '/'; } else { /* local storage mode - use local OS rules */ return os_is_file_absolute(str); } } /* get the absolute path */ static int fn_get_abs_filename(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - the storage server doesn't have such a * thing as an absolute path; all paths are relative to the game's * sandbox folder */ return FALSE; } else { /* local storage mode - use local OS rules */ return os_get_abs_filename(buf, buflen, str); } } /* check for a special filename */ static int fn_is_special_file(VMG_ const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode treat "." and ".." as special */ if (strcmp(str, ".") == 0) return OS_SPECFILE_SELF; else if (strcmp(str, "..") == 0) return OS_SPECFILE_PARENT; else return OS_SPECFILE_NONE; } else { /* local storage mode - use local OS rules */ return os_is_special_file(str); } } /* get the special directory path for the working directory */ static const char *fn_pwd_dir(VMG0_) { if (CVmNetFile::is_net_mode(vmg0_)) return "."; else return OSPATHPWD; } /* convert from universal notation to local notation */ static void fn_cvt_url_dir(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - the universal and local notation are the * same, since the storage server uses unix-style syntax */ lib_strcpy(buf, buflen, str); } else { /* local storage mode - use the local OS conversion */ os_cvt_url_dir(buf, buflen, str); } } /* convert from local notation to universal notation */ static void fn_cvt_dir_url(VMG_ char *buf, size_t buflen, const char *str) { if (CVmNetFile::is_net_mode(vmg0_)) { /* network storage mode - local and universal notation are the same */ lib_strcpy(buf, buflen, str); } else { /* local storage mode - use the local OS conversion */ os_cvt_dir_url(buf, buflen, str); } } /* compare filenames */ static int fn_file_names_equal(VMG_ const char *a, const char *b) { if (CVmNetFile::is_net_mode(vmg0_)) { /* * network storage mode - compare using Unix rules; this is a * simple case-sensitive string comparison */ return strcmp(a, b) == 0; } else { /* local storage mode - use the local OS routine */ return os_file_names_equal(a, b); } } /* ------------------------------------------------------------------------ */ /* * CVmObjFileName intrinsic class implementation */ /* * construction */ CVmObjFileName::CVmObjFileName(VMG_ int32_t sfid, const char *str, size_t len) { /* allocate our extension structure */ ext_ = (char *)vm_filnam_ext::alloc_ext(vmg_ this, sfid, str, len); } /* * Get the local path string for a given value. If the value is a string, * we'll treat it as a local path name. If it's a FileName, we'll return * its local file name. In either case, returns a pointer to the string in * our usual VMB_LEN prefix format. */ const char *CVmObjFileName::get_local_path(VMG_ const vm_val_t *val) { CVmObjFileName *fn; const char *str; if ((fn = vm_val_cast(CVmObjFileName, val)) != 0) { /* it's a FileName object - return a copy of its filename string */ return fn->get_ext()->str; } else if ((str = val->get_as_string(vmg0_)) != 0) { /* it's a string, so convert from URL notation to local path syntax */ return str; } else { /* it's not a string or FileName */ return 0; } } /* * Combine two local path elements to create a full local path. Returns a * new object representing the combined path. */ vm_obj_id_t CVmObjFileName::combine_path( VMG_ const char *path, size_t pathl, const char *file, size_t filel, int literal) { /* * Estimate the space we'll need for the combined result. This is just * the sum of the two sizes on most systems, plus a path separator. On * some older systems this might add a bit more overhead, such as VMS * bracket notation for the directory. Pad it out a bit to be safe. */ size_t buflen = pathl + filel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; char *buf = 0, *pathz = 0, *filez = 0; vm_obj_id_t id = VM_INVALID_OBJ; err_try { /* allocate null-terminated copies of the source strings */ pathz = lib_copy_str(path, pathl); filez = lib_copy_str(file, filel); /* allocate the result buffer */ buf = lib_alloc_str(buflen); /* build the full path */ fn_build_full_path(vmg_ buf, buflen, pathz, filez, literal); /* create the FileName for the combined path */ id = create_from_local(vmg_ buf, strlen(buf)); } err_finally { /* delete the allocated strings */ lib_free_str(buf); lib_free_str(pathz); lib_free_str(filez); } err_end; /* return the new object */ return id; } /* * Convert a string from URL to local filename notation. Allocates the * result string; the caller must free the string with lib_free_str(). */ char *CVmObjFileName::url_to_local( VMG_ const char *str, size_t len, int nullterm) { char *strz = 0, *buf = 0; err_try { /* * Estimate the length of the return string we'll need. Most * systems these days have Unix-like file naming, where the result * path will be the same length as the source path. There are * older systems with more complex conventions; for example, VMS * puts directories in brackets. To allow for this kind of * variation, allocate space for the combined string plus some * overhead. In any case, use the local maximum filename length as * the lower limit. */ size_t buflen = len + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* make a null-terminated copy of the string if necessary */ if (!nullterm) strz = lib_copy_str(str, len); /* allocate a conversion buffer */ buf = lib_alloc_str(buflen); /* convert the name */ fn_cvt_url_dir(vmg_ buf, buflen, nullterm ? str : strz); } err_catch_disc { /* delete the buffer and re-throw the error */ lib_free_str(buf); err_rethrow(); } err_finally { /* we're done with copy of the source string (if we made one) */ lib_free_str(strz); } err_end; /* return the buffer */ return buf; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjFileName::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* parse arguments */ const char *path, *file; vm_obj_id_t id = VM_INVALID_OBJ; if (argc == 0) { /* no arguments - create a file representing the working directory */ const char *pwd = fn_pwd_dir(vmg0_); id = create_from_local(vmg_ pwd, strlen(pwd)); } else if (argc == 1 && (file = G_stk->get(0)->get_as_string(vmg0_)) != 0) { /* it's a string in local filename notation */ id = create_from_local(vmg_ file + VMB_LEN, vmb_get_len(file)); } else if (argc == 1 && G_stk->get(0)->is_numeric(vmg0_)) { /* it's a special file ID - resolve to a path */ int32_t sfid = G_stk->get(0)->num_to_int(vmg0_); char buf[OSFNMAX]; if (!CVmObjFile::sfid_to_path(vmg_ buf, sizeof(buf), sfid)) err_throw(VMERR_BAD_VAL_BIF); /* create the file */ id = create_from_sfid(vmg_ sfid, buf, strlen(buf)); } else if (argc == 2 && (path = get_local_path(vmg_ G_stk->get(0))) != 0 && (file = get_local_path(vmg_ G_stk->get(1))) != 0) { /* create a FileName from the combination of the two path strings */ id = combine_path(vmg_ path + VMB_LEN, vmb_get_len(path), file + VMB_LEN, vmb_get_len(file), FALSE); } else if (argc >= 3) { /* invalid number of arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* wrong types */ err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* * create from URL notation */ vm_obj_id_t CVmObjFileName::create_from_url(VMG_ const char *str, size_t len) { char *lcl = 0; vm_obj_id_t fn = VM_INVALID_OBJ; err_try { /* convert to local notation */ lcl = url_to_local(vmg_ str, len, FALSE); /* create the FileName object */ fn = create_from_local(vmg_ lcl, strlen(lcl)); } err_finally { /* we're done with the conversion buffer and path */ lib_free_str(lcl); } err_end; /* return the filename object */ return fn; } /* * create from a local path string */ vm_obj_id_t CVmObjFileName::create_from_local(VMG_ const char *str, size_t len) { /* allocate the new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjFileName(vmg_ 0, str, len); /* return the new ID */ return id; } /* * create from a special file ID */ vm_obj_id_t CVmObjFileName::create_from_sfid(VMG_ int32_t sfid, const char *str, size_t len) { /* allocate the new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjFileName(vmg_ sfid, str, len); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjFileName::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjFileName::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjFileName::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* translate the property into a function vector index */ uint func_idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * call a static property */ int CVmObjFileName::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); switch (midx) { case VMOFN_FROMUNIVERSAL: return s_getp_fromUniversal(vmg_ result, argc); case VMOFN_GETROOTDIRS: return s_getp_getRootDirs(vmg_ result, argc); default: /* it's not ours - inherit the default handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjFileName::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjFileName::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjFileName::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* get the special file ID */ int32_t sfid = osrp4s(ptr); /* if there's a special file ID, look it up; otherwise load the name */ vm_filnam_ext *ext; if (sfid != 0) { /* translate the special file ID to a path */ char sfbuf[OSFNMAX]; int ok = CVmObjFile::sfid_to_path(vmg_ sfbuf, sizeof(sfbuf), sfid); /* allocate the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, sfid, sfbuf, strlen(sfbuf)); /* note whether the special file ID is valid */ ext->sfid_valid = (char)ok; } else { /* get the filename pointer and length */ const char *str = ptr + 4; size_t len = vmb_get_len(str); str += VMB_LEN; char *lcl = 0; err_try { /* convert the universal path string to local notation */ lcl = url_to_local(vmg_ str, len, FALSE); /* allocate the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, 0, lcl, strlen(lcl)); } err_finally { lib_free_str(lcl); } err_end; } /* store the extension */ ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjFileName::save_to_file(VMG_ class CVmFile *fp) { /* get our extension */ vm_filnam_ext *ext = get_ext(); /* write the special file ID */ fp->write_int4(ext->sfid); /* if it's an ordinary file, write the path name */ if (ext->sfid == 0) { char *u = 0; err_try { /* write the path in universal notation */ u = to_universal(vmg0_); fp->write_str_short_prefix(u, strlen(u)); } err_finally { lib_free_str(u); } err_end; } } /* * restore from a file */ void CVmObjFileName::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the special file ID */ int32_t sfid = fp->read_int4(); /* if there's a special file ID, look it up; otherwise read the name */ vm_filnam_ext *ext; if (sfid != 0) { /* get the special file path */ char sfbuf[OSFNMAX]; int ok = CVmObjFile::sfid_to_path(vmg_ sfbuf, sizeof(sfbuf), sfid); /* create the extension */ ext = vm_filnam_ext::alloc_ext(vmg_ this, sfid, sfbuf, strlen(sfbuf)); /* note whether the ID is valid on this platform */ ext->sfid_valid = (char)ok; } else { char *uni = 0, *lcl = 0; err_try { /* read the filename length */ size_t len = fp->read_uint2(); /* load the saved universal path */ uni = lib_alloc_str(len + 1); fp->read_bytes(uni, len); uni[len] = '\0'; /* convert the saved universal path to local notation */ lcl = url_to_local(vmg_ uni, len, TRUE); /* allocate the extension structure */ ext = vm_filnam_ext::alloc_ext(vmg_ this, 0, lcl, strlen(lcl)); } err_finally { /* we're done with the allocated buffers */ lib_free_str(lcl); lib_free_str(uni); } err_end; } /* remember the new extension */ ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * Get my path string. This validates the special file ID, and throws an * error if it's not valid. It's possible to create a FileName with an * invalid special file ID by loading a saved game that was created on a * platform with a newer version that includes new SFIDs that don't exist * in this interpreter version. In such a case we load the special file, * and it can be saved again, but it's not usable locally and has no path * information. */ const char *CVmObjFileName::get_path_string() const { /* make sure this isn't an invalid special file */ if (get_ext()->sfid && !get_ext()->sfid_valid) err_throw(VMERR_FILE_NOT_FOUND); /* return my path */ return get_ext()->str; } /* ------------------------------------------------------------------------ */ /* * Compare filenames by the path strings */ int CVmObjFileName::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* check the type of the other value */ CVmObjFileName *fn; const char *str; if ((fn = vm_val_cast(CVmObjFileName, val)) != 0) { /* it's another FileName object - compare its path */ return fn_file_names_equal( vmg_ get_ext()->get_str(), fn->get_ext()->get_str()); } else if ((str = val->get_as_string(vmg0_)) != 0) { char *strz = 0; int eq; err_try { /* make a null-terminated copy of the other name */ strz = lib_copy_str(str + VMB_LEN, vmb_get_len(str)); /* compare the names */ eq = fn_file_names_equal(vmg_ get_ext()->get_str(), strz); } err_finally { lib_free_str(strz); } err_end; /* return the result */ return eq; } else { /* other types are definitely not equal */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Add a value - this appends a path element */ int CVmObjFileName::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* * get the other value's path - this can come from a FileName object or * a string */ const char *other = get_local_path(vmg_ val); if (other == 0) err_throw(VMERR_BAD_TYPE_ADD); /* build a new object from the combined paths */ result->set_obj(combine_path( vmg_ get_ext()->get_str(), get_ext()->get_len(), other + VMB_LEN, vmb_get_len(other), FALSE)); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * fromUniversal static method */ int CVmObjFileName::s_getp_fromUniversal(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename argument as a string */ const char *str = G_stk->get(0)->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_VAL_BIF); /* create and return the new FileName from the url syntax */ retval->set_obj(create_from_url(vmg_ str + VMB_LEN, vmb_get_len(str))); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Get the filename string in univeral (URL) format. This allocates a * buffer, which the caller must free with lib_free_str() when done. */ char *CVmObjFileName::to_universal(VMG0_) const { /* get our local filename string */ const char *fname = get_ext()->get_str(); size_t fnamel = get_ext()->get_len(); /* * estimate the result size - the URL string should be about the same * length as the source string, but leave a little extra space just in * case we have to add some path separators that are implied in the * local format */ size_t buflen = fnamel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* allocate buffer */ char *buf = lib_alloc_str(buflen); /* do the conversion */ fn_cvt_dir_url(vmg_ buf, buflen, fname); /* return the new string */ return buf; } /* ------------------------------------------------------------------------ */ /* * toUniversal method */ int CVmObjFileName::getp_toUniversal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; char *buf = 0; err_try { /* allocate the temporary result buffer */ buf = to_universal(vmg0_); /* return a new String object from the result */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); } err_finally { /* free the temp buffer */ lib_free_str(buf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getName method */ int CVmObjFileName::getp_getName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our extension */ vm_filnam_ext *ext = get_ext(); /* return the path string */ retval->set_obj(CVmObjString::create( vmg_ FALSE, ext->get_str(), ext->get_len())); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootName method */ int CVmObjFileName::getp_getRootName(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* presume we won't find a root name */ retval->set_nil(); /* extract the root name from our filename string */ const char *r = fn_get_root_name(vmg_ get_ext()->get_str()); /* if we found a root name, return it as a string */ if (r != 0 && r[0] != '\0') retval->set_obj(CVmObjString::create(vmg_ FALSE, r, strlen(r))); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getPath method */ int CVmObjFileName::getp_getPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* presume we'll return nil */ retval->set_nil(); /* get our extension */ vm_filnam_ext *ext = get_ext(); /* get our filename string */ const char *fname = ext->get_str(); size_t fnamel = ext->get_len(); /* * Estimate how much space we'll need for the extracted path name. The * path portion should be shorter than the whole name, but add some * padding for overhead in case the local system needs to add any * syntax (e.g., VMS directory brackets). */ size_t buflen = fnamel + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; char *buf = 0; err_try { /* allocate a temporary result buffer */ buf = lib_alloc_str(buflen); /* extract the path name */ fn_get_path_name(vmg_ buf, buflen, fname); /* return the path as a FileName, or nil if there's no path */ if (buf[0] != '\0') retval->set_obj(create_from_local(vmg_ buf, strlen(buf))); } err_finally { /* free the temp buffer */ lib_free_str(buf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * addToPath method */ int CVmObjFileName::getp_addToPath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the target path - it can be a FileName or string */ const char *file = get_local_path(vmg_ G_stk->get(0)); if (file == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create a new object from the combined path */ retval->set_obj(combine_path( vmg_ get_ext()->get_str(), get_ext()->get_len(), file + VMB_LEN, vmb_get_len(file), FALSE)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * isAbsolute method */ int CVmObjFileName::getp_isAbsolute(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* ask the OS if we're absolute, and set the return value accordingly */ retval->set_logical(fn_is_file_absolute(vmg_ get_ext()->get_str())); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getAbsolutePath method */ int CVmObjFileName::getp_getAbsolutePath(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our local filename string */ const char *fname = get_ext()->get_str(); size_t fnamel = get_ext()->get_len(); /* we haven't allocated any strings yet */ char *buf = 0, *outbuf = 0; err_try { /* * if it's not absolute already, combine it with our internal * working directory to get the fully qualified path */ const char *src = fname; if (!fn_is_file_absolute(vmg_ fname)) { /* allocate space for the combined path */ size_t buflen = fnamel + strlen(G_file_path) + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* build the combined path */ buf = lib_alloc_str(buflen); fn_build_full_path(vmg_ buf, buflen, G_file_path, fname, TRUE); /* use this as the new source path */ src = buf; } /* * Even though the name should already be absolute, run it through * os_get_abs_filename() anyway, since that will convert the name * to a more canonical format on many systems. */ /* figure how much space we'll need, adding some padding */ size_t buflen = strlen(src) + 32; if (buflen < OSFNMAX) buflen = OSFNMAX; /* allocate the buffer and do the conversion */ outbuf = lib_alloc_str(buflen); if (fn_get_abs_filename(vmg_ outbuf, buflen, src)) { /* success - return the result string */ retval->set_obj(CVmObjString::create( vmg_ FALSE, outbuf, strlen(outbuf))); } else { /* failure - return nil */ retval->set_nil(); } } err_finally { /* free the allocated string */ lib_free_str(buf); lib_free_str(outbuf); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootDirs method */ int CVmObjFileName::s_getp_getRootDirs(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* * See how much space we need for the root list. If we're in network * storage mode, there's no concept of a root folder, since everything * is relative to the user+game folder. */ size_t buflen; if (!CVmNetFile::is_net_mode(vmg0_) && (buflen = os_get_root_dirs(0, 0)) != 0) { /* allocate space */ char *buf = new char[buflen]; char *lclstr = 0; err_try { /* get the root list for real this time */ os_get_root_dirs(buf, buflen); /* count the strings in the list, filtered for file safety */ int cnt = 0; char *p; for (p = buf ; *p != '\0' ; p += strlen(p) + 1) { /* if we're allowed to get metadata on this path, count it */ if (CVmObjFile::query_safety_for_open( vmg_ p, VMOBJFILE_ACCESS_GETINFO)) ++cnt; } /* create the list; clear it and push it for gc protection */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = vm_objid_cast(CVmObjList, retval->val.obj); lst->cons_clear(); G_stk->push(retval); /* build the list */ for (p = buf, cnt = 0 ; *p != '\0' ; p += strlen(p) + 1) { /* check to see if we're allowed to access this root */ if (CVmObjFile::query_safety_for_open( vmg_ p, VMOBJFILE_ACCESS_GETINFO)) { /* map the string to utf8 */ size_t len = G_cmap_from_fname->map_str_alo(&lclstr, p); /* create the FileName object */ vm_val_t ele; ele.set_obj(create_from_local(vmg_ lclstr, len)); /* done with the mapped string */ t3free(lclstr); lclstr = 0; /* add it to the list */ lst->cons_set_element(cnt++, &ele); } } /* done with our gc protection for the list */ G_stk->discard(); } err_finally { /* free the root list buffer */ delete [] buf; /* free the mapped filename string */ if (lclstr != 0) t3free(lclstr); } err_end; } else { /* no root list - return an empty list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, (size_t)0)); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteFile method */ int CVmObjFileName::getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the DELETE access mode */ vm_rcdesc rc(vmg_ "FileName.deleteFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_DELETEFILE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_DELETE, OSFTUNK, "application/octet-stream"); /* close the network file descriptor - this will delete the file */ netfile->close(vmg0_); /* no return value */ retval->set_nil(); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * renameFile method */ int CVmObjFileName::getp_renameFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up our recursive callback descriptor in case netfile needs it */ vm_rcdesc rc(vmg_ "FileName.renameFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_RENAMEFILE, G_stk->get(0), argc); CVmNetFile *oldfile = 0, *newfile = 0; err_try { /* get the old filename from self; use the RENAME FROM access mode */ oldfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_RENAME_FROM, OSFTUNK, "application/octet-stream"); /* get the new filename argument; use RENAME TO access mode */ newfile = CVmObjFile::get_filename_arg( vmg_ G_stk->get(0), &rc, VMOBJFILE_ACCESS_RENAME_TO, FALSE, OSFTUNK, "application/octet-stream"); /* rename the file */ oldfile->rename_to(vmg_ newfile); } err_finally { /* done with the netfile objects */ if (oldfile != 0) oldfile->abandon(vmg0_); if (newfile != 0) newfile->abandon(vmg0_); } err_end; /* no return value */ retval->set_nil(); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Translate an os_filemode_t value to a FileTypeXxx value */ static int32_t filemode_to_filetype(int osmode) { /* * mappings from OSFMODE_xxx values to FileTypeXxx values (see * include/filename.h in the system headers) */ static const struct { unsigned long osmode; int32_t tadsmode; } xlat[] = { { OSFMODE_FILE, 0x0001 }, // FileTypeFile { OSFMODE_DIR, 0x0002 }, // FileTypeDir { OSFMODE_CHAR, 0x0004 }, // FileTypeChar { OSFMODE_BLK, 0x0008 }, // FileTypeBlock { OSFMODE_PIPE, 0x0010 }, // FileTypePipe { OSFMODE_SOCKET, 0x0020 }, // FileTypeSocket { OSFMODE_LINK, 0x0040 } // FileTypeLink }; /* run through the bit flags, and set the translated bits */ int32_t tadsmode = 0; for (int i = 0 ; i < countof(xlat) ; ++i) { /* if this OS mode bit is set, set the corresponding TADS bit */ if ((osmode & xlat[i].osmode) != 0) tadsmode |= xlat[i].tadsmode; } /* return the result */ return tadsmode; } /* * Translate OSFATTR_xxx values to FileAttrXxx values */ static int32_t map_file_attrs(unsigned long osattrs) { /* * mappings from OSFATTR_xxx values to FileAttrXxx values (see * include/filename.h in the system headers) */ static const struct { unsigned long osattr; int32_t tadsattr; } xlat[] = { { OSFATTR_HIDDEN, 0x0001 }, // FileTypeHidden { OSFATTR_SYSTEM, 0x0002 }, // FileTypeSystem { OSFATTR_READ, 0x0004 }, // FileTypeRead { OSFATTR_WRITE, 0x0008 } // FileTypeWrite }; /* run through the bit flags, and set the translated bits */ int32_t tadsattrs = 0; for (int i = 0 ; i < countof(xlat) ; ++i) { /* if this OS mode bit is set, set the corresponding TADS bit */ if ((osattrs & xlat[i].osattr) != 0) tadsattrs |= xlat[i].tadsattr; } /* return the result */ return tadsattrs; } /* * Push a file timestamp value */ static void push_file_time(VMG_ os_time_t t) { /* * if the time value is zero, it means that the local system doesn't * support this type of timestamp; push it as nil */ if (t == 0) { /* no time value - push nil */ G_stk->push()->set_nil(); } else { /* we have a valid time, so create a Date object to represent it */ G_stk->push()->set_obj(CVmObjDate::create_from_time_t(vmg_ FALSE, t)); } } /* * Get my special file type flags, if applicable. This inspects the root * name to see if it's a special relative link, such as Unix "." or "..". */ int32_t CVmObjFileName::special_filetype_flags(VMG0_) { /* no special flags so far */ int32_t flags = 0; /* get my extension */ vm_filnam_ext *ext = get_ext(); /* get the root name from my filename string */ const char *root = fn_get_root_name(vmg_ (char *)ext->get_str()); /* check the root name to see if it's special */ switch (fn_is_special_file(vmg_ root)) { case OS_SPECFILE_SELF: flags |= 0x0080; // FileTypeSelfLink break; case OS_SPECFILE_PARENT: flags |= 0x0100; // FileTypeParentLink break; default: break; } /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * getFileType method */ int CVmObjFileName::getp_getFileType(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the GETINFO access mode */ vm_rcdesc rc(vmg_ "FileName.getFileType", metaclass_reg_->get_class_obj(vmg0_), VMOFN_GETFILETYPE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_GETINFO, OSFTUNK, "application/octet-stream"); /* check to see if we're following links (follow them by default) */ int follow_links = TRUE; if (argc >= 1) { /* * note that our 'asLink' parameter has the opposite sense from * 'follow_links', so invert the parameter value */ follow_links = !G_stk->get(0)->get_logical(); if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_VAL_BIF); } int ok; unsigned long osmode, osattr; err_try { /* get the file type from the netfile object */ ok = netfile->get_file_mode(vmg_ &osmode, &osattr, follow_links); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* figure the return value */ if (ok) { /* success - return the file mode, translated to FileTypeXxx bits */ retval->set_int( filemode_to_filetype(osmode) | special_filetype_flags(vmg0_)); } else { /* failed - simply return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getFileInfo method */ int CVmObjFileName::getp_getFileInfo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the GETINFO access mode */ vm_rcdesc rc(vmg_ "FileName.getFileInfo", metaclass_reg_->get_class_obj(vmg0_), VMOFN_GETFILETYPE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_GETINFO, OSFTUNK, "application/octet-stream"); /* check to see if we're following links (follow them by default) */ int follow_links = TRUE; if (argc >= 1) { /* * note that our 'asLink' parameter has the opposite sense from * 'follow_links', so invert the parameter value */ follow_links = G_stk->get(0)->get_logical(); if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_VAL_BIF); } int ok; os_file_stat_t stat; err_try { /* get the file type from the netfile object */ ok = netfile->get_file_stat(vmg_ &stat, follow_links); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* build the return value */ if (ok) { /* * Success - build a new FileInfo(mode, size, ctime, mtime, atime, * target, attrs). Start by pushing the constructor arguments * (last to first). */ /* push the file attributes */ G_stk->push()->set_int(map_file_attrs(stat.attrs)); /* if this is a symbolic link file, get the target path */ char target[OSFNMAX]; if ((stat.mode & OSFMODE_LINK) != 0 && netfile->resolve_symlink(vmg_ target, sizeof(target))) { /* push the name as a string in UTF8 */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, target, strlen(target), G_cmap_from_fname)); } else { /* no symbolic link name - push nil as the symlink argument */ G_stk->push()->set_nil(); } /* push the timestamps */ push_file_time(vmg_ stat.acc_time); push_file_time(vmg_ stat.mod_time); push_file_time(vmg_ stat.cre_time); /* * Push the file size. If the high part is non-zero, or the low * part is greater than 0x7fffffff, the value won't fit in an * int32_t, so we need to push this as a BigNumber value. */ if (stat.sizehi != 0 || stat.sizelo > 0x7FFFFFFFU) { /* push it as a BigNumber value */ G_interpreter->push_obj(vmg_ CVmObjBigNum::create_int64( vmg_ FALSE, stat.sizehi, stat.sizelo)); } else { /* it'll fit in a simple integer value */ G_interpreter->push_int(vmg_ (int32_t)stat.sizelo); } /* push the file mode, translated to FileTypeXxx bits */ G_interpreter->push_int( vmg_ filemode_to_filetype(stat.mode) | special_filetype_flags(vmg0_)); /* * Call the constructor. If there's a FileInfo object exported, * create one of those. Otherwise just create a list from the * values. */ const int argc = 7; vm_obj_id_t fi = G_predef->file_info; if (fi != VM_INVALID_OBJ) { /* create the FileInfo instance */ vm_objp(vmg_ fi)->create_instance(vmg_ fi, 0, argc); /* make sure we got an object back */ if (G_interpreter->get_r0()->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* set the object return value */ retval->set_obj(G_interpreter->get_r0()->val.obj); } else { /* return the information as a list */ retval->set_obj(CVmObjList::create_from_stack(vmg_ 0, argc)); } } else { /* failed - simply return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * listDir method */ int CVmObjFileName::getp_listDir(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the READ DIR access mode */ vm_rcdesc rc(vmg_ "FileName.listDir", metaclass_reg_->get_class_obj(vmg0_), VMOFN_LISTDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_READDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the directory listing */ if (!netfile->readdir(vmg_ get_ext()->get_str(), retval)) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error reading directory"); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * forEachFile method */ int CVmObjFileName::getp_forEachFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use the READ DIR access mode */ vm_rcdesc rc(vmg_ "FileName.forEachFile", metaclass_reg_->get_class_obj(vmg0_), VMOFN_FOREACHFILE, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_READDIR, OSFTUNK, "application/octet-stream"); /* get the 'recursive' flag, if present */ int recursive = FALSE; if (argc >= 2) { if (G_stk->get(1)->is_logical()) recursive = G_stk->get(1)->get_logical(); else err_throw(VMERR_BAD_TYPE_BIF); } err_try { /* do the directory enumeration through the callback */ if (!netfile->readdir_cb(vmg_ get_ext()->get_str(), &rc, G_stk->get(0), recursive)) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error reading directory"); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * createDirectory method */ int CVmObjFileName::getp_createDirectory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use MKDIR access mode */ vm_rcdesc rc(vmg_ "FileName.createDirectory", metaclass_reg_->get_class_obj(vmg0_), VMOFN_CREATEDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_MKDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the "create parents" parameter */ int create_parents = FALSE; if (argc >= 1) { /* check that it's a true/nil value */ if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the flag */ create_parents = G_stk->get(0)->get_logical(); } /* create the directory */ netfile->mkdir(vmg_ create_parents); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * removeDirectory method */ int CVmObjFileName::getp_removeDirectory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the filename; use RMDIR access mode */ vm_rcdesc rc(vmg_ "FileName.removeDirectory", metaclass_reg_->get_class_obj(vmg0_), VMOFN_REMOVEDIR, G_stk->get(0), argc); CVmNetFile *netfile = CVmObjFile::get_filename_from_obj( vmg_ self, &rc, VMOBJFILE_ACCESS_RMDIR, OSFTUNK, "application/octet-stream"); err_try { /* get the "remove contents" parameter */ int remove_contents = FALSE; if (argc >= 1) { /* check that it's a true/nil value */ if (!G_stk->get(0)->is_logical()) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the flag */ remove_contents = G_stk->get(0)->get_logical(); } /* remove the directory */ netfile->rmdir(vmg_ remove_contents); } err_finally { /* done with the netfile object */ netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmpreini.cpp0000644000175000001440000000547012000105676016062 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpreini.cpp - preinitializer Function Main entrypoint for compile-time preinitialization. Loads an image file, runs the main entrypoint in pre-init mode, then rewrites the image file in its new state after execution. Notes Modified 07/21/99 MJRoberts - Creation */ #include #include "t3std.h" #include "vminit.h" #include "vmerr.h" #include "vmfile.h" #include "vmimage.h" #include "vmrun.h" #include "vmimgrb.h" #include "vmpreini.h" #include "vmconsol.h" /* * Run pre-initialization */ void vm_run_preinit(CVmFile *origfp, const char *image_fname, CVmFile *newfp, class CVmHostIfc *hostifc, class CVmMainClientIfc *clientifc, const char *const *argv, int argc, class CVmRuntimeSymbols *runtime_symtab, class CVmRuntimeSymbols *runtime_macros) { vm_globals *vmg__; CVmImageLoader *volatile loader = 0; CVmImageFile *volatile imagefp = 0; /* initialize the VM */ vm_init_options opts(hostifc, clientifc); vm_initialize(&vmg__, &opts); /* * turn off "more" on the console - when running preinitialization, * any output is purely diagnostic information for the programmer * and thus should be formatted as simple stdio-style console output */ G_console->set_more_state(FALSE); err_try { /* note where the image file starts */ long start_pos = origfp->get_pos(); /* create the loader */ imagefp = new CVmImageFileExt(origfp); loader = new CVmImageLoader(imagefp, image_fname, 0); /* load the image */ loader->load(vmg0_); /* set pre-init mode */ G_preinit_mode = TRUE; /* run it, using the runtime symbols the caller sent us */ loader->run(vmg_ argv, argc, runtime_symtab, runtime_macros, 0); /* * seek back to the start of the image file, since we need to * copy parts of the original file to the new file */ origfp->set_pos(start_pos); /* save the new image file */ vm_rewrite_image(vmg_ origfp, newfp, loader->get_static_cs_ofs()); } err_finally { /* detach the pools from the image file */ if (loader != 0) loader->unload(vmg0_); /* delete the loader and the image file object */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; /* terminate the VM */ vm_terminate(vmg__, clientifc); } err_end; } frobtads-1.2.3/tads3/vmnetui.cpp0000644000175000001440000000261311541215613015716 0ustar realncusers /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetui.cpp - TADS 3 networking, UI extensions Function Implements UI-related routines for the TADS 3 networking subsystem. Notes These routines are segregated from vmnet.cpp to untangle vmnet.cpp from the VM globals. This allows that module to be used in build configurations that don't include the VM globals, such as for the front-end process for the stand-alone Web UI on Windows (tadsweb.exe). Modified 04/11/10 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnet.h" #include "vmglob.h" #include "vmerr.h" #include "vmtype.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmisaac.h" #include "sha2.h" #include "vmpredef.h" #include "vmhttpreq.h" #include "vmglob.h" #include "vmmain.h" #include "vmpredef.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * UI Close event - prepare the event object. */ vm_obj_id_t TadsUICloseEvent::prep_event_obj(VMG_ int *argc, int *evt_type) { /* we're not adding any arguments */ *argc = 0; /* the event type is NetEvUIClose (4 - see include/tadsnet.h) */ *evt_type = 4; /* use the basic NetEvent class */ return G_predef->net_event; } frobtads-1.2.3/tads3/vmerrmsg.cpp0000644000175000001440000010752411740653627016114 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerrmsg.cpp - T3 VM error message strings Function Defines the message text for VM errors. All error text is isolated into this module for easy replacement in translated versions. Notes Modified 05/13/00 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmerr.h" /* ------------------------------------------------------------------------ */ /* * Enable or disable verbose messages. To conserve memory, verbose * messages can be omitted. To omit verbose messages, the platform * makefile should define the preprocessor symbol VMERR_OMIT_VERBOSE. */ #ifdef VMERR_OMIT_VERBOSE # define VMsg(msg) "" #else # define VMsg(msg) msg #endif #ifdef VMERR_BOOK_MSG # define VBook(msg) , msg #else # define VBook(msg) #endif /* * To conserve even more memory, the messages can be omitted entirely. To * disable all compiled-in messages, define VMERR_OMIT_MESSAGES. */ /* ------------------------------------------------------------------------ */ /* * T3 VM Error Messages * * The messages must be sorted by message number, so that we can perform * a binary search to look up a message by number. */ const err_msg_t vm_messages_english[] = { #ifdef VMERR_OMIT_MESSAGES { 0, 0, 0 VBook(0) } #else /* VMERR_OMIT_MESSAGES */ { VMERR_READ_FILE, "error reading file", VMsg("Error reading file. The file might be corrupted or a media error " "might have occurred.") }, { VMERR_WRITE_FILE, "error writing file", VMsg("Error writing file. The media might be full, or another media " "error might have occurred.") }, { VMERR_FILE_NOT_FOUND, "file not found", VMsg("Error opening file. The specified file might not exist, you " "might not have sufficient privileges to open the file, or " "a sharing violation might have occurred.") }, { VMERR_CREATE_FILE, "error creating file", VMsg("Error creating file. You might not have sufficient privileges " "to open the file, or a sharing violation might have occurred.") }, { VMERR_CLOSE_FILE, "error closing file", VMsg("Error closing file. Some or all changes made to the file might " "not have been properly written to the physical disk/media.") }, { VMERR_DELETE_FILE, "error deleting file", VMsg("Error deleting file. This could because you don't have " "sufficient privileges, the file is marked as read-only, another " "program is using the file, or a physical media error occurred.") }, { VMERR_PACK_PARSE, "data packer format string parsing error at character index %d", VMsg("The format string for the data packing or unpacking has a syntax " "error at character index %d.") }, { VMERR_PACK_ARG_MISMATCH, "data packer argument type mismatch at format string index %d", VMsg("Data packer argument type mismatch. The type of the argument " "doesn't match the format code at character index %d in the " "format string.") }, { VMERR_PACK_ARGC_MISMATCH, "wrong number of data packer arguments at format string index %d", VMsg("Wrong number of arguments to the data packer. The number of " "argument values doesn't match the number of elements in the " "format string.") }, { VMERR_NET_FILE_NOIMPL, "this file operation isn't supported for storage server files", VMsg("This file operation isn't supported for storage server files. " "This operation can only be used with local disk files.") }, { VMERR_RENAME_FILE, "error renaming file", VMsg("An error occurred renaming the file. The new name might already " "be used by an existing file, you might not have the necessary " "permissions in the new or old directory locations, or the " "new name might be in an incompatible location, such as on " "a different device or volume." ) }, { VMERR_OBJ_IN_USE, "object ID in use - the image/save file might be corrupted", VMsg("An object ID requested by the image/save file is already in use " "and cannot be allocated to the file. This might indicate that " "the file is corrupted.") }, { VMERR_OUT_OF_MEMORY, "out of memory", VMsg("Out of memory. Try making more memory available by closing " "other applications if possible.") }, { VMERR_NO_MEM_FOR_PAGE, "out of memory allocating pool page", VMsg("Out of memory allocating pool page. Try making more memory " "available by closing other applications.") }, { VMERR_BAD_POOL_PAGE_SIZE, "invalid page size - file is not valid", VMsg("Invalid page size. The file being loaded is not valid.") }, { VMERR_OUT_OF_PROPIDS, "no more property ID's are available", VMsg("Out of property ID's. No more properties can be allocated.") }, { VMERR_CIRCULAR_INIT, "circular initialization dependency in intrinsic class (internal error)", VMsg("Circular initialization dependency detected in intrinsic class. " "This indicates an internal error in the interpreter. Please " "report this error to the interpreter's maintainer.") }, { VMERR_UNKNOWN_METACLASS, "this interpreter version cannot run this program (program requires " "intrinsic class %s, which is not available in this interpreter)", VMsg("This image file requires an intrinsic class with the identifier " "\"%s\", but the class is not available in this interpreter. This " "program cannot be executed with this interpreter.") }, { VMERR_UNKNOWN_FUNC_SET, "this interpreter version cannot run this program (program requires " "intrinsic function set %s, which is not available in this interpreter)", VMsg("This image file requires a function set with the identifier " "\"%s\", but the function set is not available in this " "intepreter. This program cannot be executed with this " "interpreter.") }, { VMERR_READ_PAST_IMG_END, "reading past end of image file - program might be corrupted", VMsg("Reading past end of image file. The image file might " "be corrupted.") }, { VMERR_NOT_AN_IMAGE_FILE, "this is not an image file (no valid signature found)", VMsg("This file is not a valid image file - the file has an invalid " "signature. The image file might be corrupted.") }, { VMERR_UNKNOWN_IMAGE_BLOCK, "this interpreter version cannot run this program (unknown block type " "in image file)", VMsg("Unknown block type. This image file is either incompatible with " "this version of the interpreter, or has been corrupted.") }, { VMERR_IMAGE_BLOCK_TOO_SMALL, "data block too small", VMsg("A data block in the image file is too small. The image file might " "be corrupted.") }, { VMERR_IMAGE_POOL_BEFORE_DEF, "invalid image file: pool page before pool definition", VMsg("This image file is invalid because it specifies a pool page " "before the pool's definition. The image file might be " "corrupted.") }, { VMERR_IMAGE_POOL_BAD_PAGE, "invalid image file: pool page out of range of definition", VMsg("This image file is invalid because it specifies a pool page " "outside of the range of the pool's definition. The image file " "might be corrupted.") }, { VMERR_IMAGE_BAD_POOL_ID, "invalid image file: invalid pool ID", VMsg("This image file is invalid because it specifies an invalid " "pool ID. The image file might be corrupted.") }, { VMERR_LOAD_BAD_PAGE_IDX, "invalid image file: bad page index", VMsg("This image file is invalid because it specifies an invalid " "page index. The image file might be corrupted.") }, { VMERR_LOAD_UNDEF_PAGE, "loading undefined pool page", VMsg("The program is attempting to load a pool page that is not present " "in the image file. The image file might be corrupted.") }, { VMERR_IMAGE_POOL_REDEF, "invalid image file: pool is defined more than once", VMsg("This image file is invalid because it defines a pool more than " "once. The image file might be corrupted.") }, { VMERR_IMAGE_METADEP_REDEF, "invalid image file: multiple intrinsic class dependency tables found", VMsg("This image file is invalid because it contains multiple " "intrinsic class tables. The image file might be corrupted.") }, { VMERR_IMAGE_NO_METADEP, "invalid image file: no intrinsic class dependency table found", VMsg("This image file is invalid because it contains no intrinsic class " "tables. The image file might be corrupted.") }, { VMERR_IMAGE_FUNCDEP_REDEF, "invalid image file: multiple function set dependency tables found", VMsg("This image file is invalid because it contains multiple " "function set tables. The image file might be corrupted.") }, { VMERR_IMAGE_NO_FUNCDEP, "invalid image file: no function set dependency table found", VMsg("This image file is invalid because it contains no function set " "tables. The image file might be corrupted.") }, { VMERR_IMAGE_ENTRYPT_REDEF, "invalid image file: multiple entrypoints found", VMsg("This image file is invalid because it contains multiple entrypoint " "definitions. The image file might be corrupted.") }, { VMERR_IMAGE_NO_ENTRYPT, "invalid image file: no entrypoint found", VMsg("This image file is invalid because it contains no entrypoint " "specification. The image file might be corrupted.") }, { VMERR_IMAGE_INCOMPAT_VSN, "incompatible image file format version", VMsg("This image file has an incompatible format version. You must " "obtain a newer version of the interpreter to execute this " "program.") }, { VMERR_IMAGE_NO_CODE, "image contains no code", VMsg("This image file contains no executable code. The file might " "be corrupted.") }, { VMERR_IMAGE_INCOMPAT_HDR_FMT, "incomptabile image file format: method header too old", VMsg("This image file has an incompatible method header format. " "This is an older image file version which this interpreter " "does not support.") }, { VMERR_UNAVAIL_INTRINSIC, "unavailable intrinsic function called (index %d in function set \"%s\")", VMsg("Unavailable intrinsic function called (the function is at " "index %d in function set \"%s\"). This function is not available " "in this version of the interpreter and cannot be called when " "running the program with this version. This normally indicates " "either (a) that the \"preinit\" function (or code invoked by " "preinit) called an intrinsic that isn't available during " "this phase, such as an advanced display function; or (b) that " "the program used '&' to refer to a function address, and" "the function isn't available in this interpreter version.") }, { VMERR_UNKNOWN_METACLASS_INTERNAL, "unknown internal intrinsic class ID %x", VMsg("Unknown internal intrinsic class ID %x. This indicates an " "internal error in the interpreter. Please report this error " "to the interpreter's maintainer.") }, { VMERR_XOR_MASK_BAD_IN_MEM, "page mask is not allowed for in-memory image file", VMsg("This image file cannot be loaded from memory because it contains " "masked data. Masked data is not valid with in-memory files. " "This probably indicates that the program file was not installed " "properly; you must convert this program file for in-memory use " "before you can load the program with this version of the " "interpreter.") }, { VMERR_NO_IMAGE_IN_EXE, "no embedded image file found in executable", VMsg("This executable does not contain an embedded image file. The " "application might not be configured properly or might need to " "be rebuilt. Re-install the application or obtain an updated " "version from the application's author.") }, { VMERR_OBJ_SIZE_OVERFLOW, "object size exceeds hardware limits of this computer", VMsg("An object defined in this program file exceeds the hardware limits " "of this computer. This program cannot be executed on this type " "of computer or operating system. Contact the program's author " "for assistance.") }, { VMERR_METACLASS_TOO_OLD, "this interpreter is too old to run this program (program requires " "intrinsic class version %s, interpreter provides version %s)", VMsg("This program needs the intrinsic class \"%s\". This VM " "implementation does not provide a sufficiently recent version " "of this intrinsic class; the latest version available in this " "VM is \"%s\". This program cannot run with this version of the " "VM; you must use a more recent version of the VM to execute this " "program.") }, { VMERR_INVAL_METACLASS_DATA, "invalid intrinsic class data - image file may be corrupted", VMsg("Invalid data were detected in an intrinsic class. This might " "indicate that the image file has been corrupted. You might need " "to re-install the program.") }, { VMERR_BAD_STATIC_NEW, "invalid object - class does not allow loading", VMsg("An object in the image file cannot be loaded because its class " "does not allow creation of objects of the class. This usually " "means that the class is abstract and cannot be instantiated " "as a concrete object.") }, { VMERR_FUNCSET_TOO_OLD, "this interpreter is too old to run this program (program requires " "function set version %s, interpreter provides version %s)", VMsg("This program needs the function set \"%s\". This VM " "implementation does not provide a sufficiently recent version " "of this function set; the latest version available in this VM " "is \"%s\". This program cannot run with this version of the " "VM; you must use a more recent version of the VM to execute " "this program.") }, { VMERR_INVAL_EXPORT_TYPE, "exported symbol \"%s\" is of incorrect datatype", VMsg("The exported symbol \"%s\" is of the incorrect datatype. Check " "the program and the library version.") }, { VMERR_INVAL_IMAGE_MACRO, "invalid data in macro definitions in image file (error code %d)", VMsg("The image file contains invalid data in the macro symbols " "in the debugging records: macro loader error code %d. This " "might indicate that the image file is corrupted.") }, { VMERR_NO_MAINRESTORE, "this program is not capable of restoring a saved state on startup", VMsg("This program is not capable of restoring a saved state on " "startup. To restore the saved state, you must run the program " "normally, then use the appropriate command or operation within " "the running program to restore the saved position file.") }, { VMERR_IMAGE_INCOMPAT_VSN_DBG, "image file is incompatible with debugger - recompile the program", VMsg("This image file was created with a version of the compiler " "that is incompatible with this debugger. Recompile the program " "with the compiler that's bundled with this debugger. If no " "compiler is bundled, check the debugger release notes for " "information on which compiler to use. ") }, { VMERR_NETWORK_SAFETY, "this operation is not allowed by the network safety level settings", VMsg("This operation is not allowed by the current network safety level " "settings. The program is attempting to access network features " "that you have disabled with the network safety level options. " "If you wish to allow this operation, you must restart the " "program with new network safety settings.") }, { VMERR_INVALID_SETPROP, "property cannot be set for object", VMsg("Invalid property change - this property cannot be set for this " "object. This normally indicates that the object is of a type " "that does not allow setting of properties at all, or at least " "of certain properties. For example, a string object does not " "allow setting properties at all.") }, { VMERR_NOT_SAVED_STATE, "file is not a valid saved state file", VMsg("This file is not a valid saved state file. Either the file was " "not created as a saved state file, or its contents have been " "corrupted.") }, { VMERR_WRONG_SAVED_STATE, "saved state is for a different program or a different version of " "this program", VMsg("This file does not contain saved state information for " "this program. The file was saved by another program, or " "by a different version of this program; in either case, it " "cannot be restored with this version of this program.") }, { VMERR_SAVED_META_TOO_LONG, "intrinsic class name in saved state file is too long", VMsg("An intrinsic class name in the saved state file is too long. " "The file might be corrupted, or might have been saved by an " "incompatible version of the interpreter.") }, { VMERR_SAVED_OBJ_ID_INVALID, "invalid object ID in saved state file", VMsg("The saved state file contains an invalid object ID. The saved " "state file might be corrupted.") }, { VMERR_BAD_SAVED_STATE, "saved state file is corrupted (incorrect checksum)", VMsg("The saved state file's checksum is invalid. This usually " "indicates that the file has been corrupted (which could be " "due to a media error, modification by another application, " "or a file transfer that lost or changed data).") }, { VMERR_STORAGE_SERVER_ERR, "storage server error", VMsg("An error occurred accessing the storage server. This could " "be due to a network problem, invalid user credentials, or " "a configuration problem on the game server.") }, { VMERR_DESC_TAB_OVERFLOW, "saved file metadata table exceeds 64k bytes", VMsg("The metadata table for the saved state file is too large. " "This table is limited to 64k bytes in length.") }, { VMERR_BAD_SAVED_META_DATA, "invalid intrinsic class data in saved state file", VMsg("The saved state file contains intrinsic class data that " "is not valid. This usually means that the file was saved " "with an incompatible version of the interpreter program.") }, { VMERR_NO_STR_CONV, "cannot convert value to string", VMsg("This value cannot be converted to a string.") }, { VMERR_CONV_BUF_OVF, "string conversion buffer overflow", VMsg("An internal buffer overflow occurred converting this value to " "a string.") }, { VMERR_BAD_TYPE_ADD, "invalid datatypes for addition operator", VMsg("Invalid datatypes for addition operator. The values being added " "cannot be combined in this manner.") }, { VMERR_NUM_VAL_REQD, "numeric value required", VMsg("Invalid value type - a numeric value is required.") }, { VMERR_INT_VAL_REQD, "integer value required", VMsg("Invalid value type - an integer value is required.") }, { VMERR_NO_LOG_CONV, "cannot convert value to logical (true/nil)", VMsg("This value cannot be converted to a logical (true/nil) value.") }, { VMERR_BAD_TYPE_SUB, "invalid datatypes for subtraction operator", VMsg("Invalid datatypes for subtraction operator. The values used " "cannot be combined in this manner.") }, { VMERR_DIVIDE_BY_ZERO, "division by zero", VMsg("Arithmetic error - Division by zero.") }, { VMERR_INVALID_COMPARISON, "invalid comparison", VMsg("Invalid comparison - these values cannot be compared " "to one another.") }, { VMERR_OBJ_VAL_REQD, "object value required", VMsg("An object value is required.") }, { VMERR_PROPPTR_VAL_REQD, "property pointer required", VMsg("A property pointer value is required.") }, { VMERR_LOG_VAL_REQD, "logical value required", VMsg("A logical (true/nil) value is required.") }, { VMERR_FUNCPTR_VAL_REQD, "function pointer required", VMsg("A function pointer value is required.") }, { VMERR_CANNOT_INDEX_TYPE, "invalid index operation - this type of value cannot be indexed", VMsg("This type of value cannot be indexed.") }, { VMERR_INDEX_OUT_OF_RANGE, "index out of range", VMsg("The index value is out of range for the value being indexed (it is " "too low or too high).") }, { VMERR_BAD_METACLASS_INDEX, "invalid intrinsic class index", VMsg("The intrinsic class index is out of range. This probably " "indicates that the image file is corrupted.") }, { VMERR_BAD_DYNAMIC_NEW, "invalid dynamic object creation (intrinsic class does not support NEW)", VMsg("This type of object cannot be dynamically created, because the " "intrinsic class does not support dynamic creation.") }, { VMERR_OBJ_VAL_REQD_SC, "object value required for base class", VMsg("An object value must be specified for the base class of a dynamic " "object creation operation. The superclass value is of a " "non-object type.") }, { VMERR_STRING_VAL_REQD, "string value required", VMsg("A string value is required.") }, { VMERR_LIST_VAL_REQD, "list value required", VMsg("A list value is required.") }, { VMERR_DICT_NO_CONST, "list or string reference found in dictionary (entry \"%s\") - this " "dictionary cannot be saved in the image file", VMsg("A dictionary entry (for the string \"%s\") referred to a string " "or list value for its associated value data. This dictionary " "cannot be stored in the image file, so the image file cannot " "be created. Check dictionary word additions and ensure that " "only objects are added to the dictionary.") }, { VMERR_INVAL_OBJ_TYPE, "invalid object type - cannot convert to required object type", VMsg("An object is not of the correct type. The object specified " "cannot be converted to the required object type.") }, { VMERR_NUM_OVERFLOW, "numeric overflow", VMsg("A numeric calculation overflowed the limits of the datatype.") }, { VMERR_BAD_TYPE_MUL, "invalid datatypes for multiplication operator", VMsg("Invalid datatypes for multiplication operator. The values " "being added cannot be combined in this manner.") }, { VMERR_BAD_TYPE_DIV, "invalid datatypes for division operator", VMsg("Invalid datatypes for division operator. The values being added " "cannot be combined in this manner.") }, { VMERR_BAD_TYPE_NEG, "invalid datatype for arithmetic negation operator", VMsg("Invalid datatype for arithmetic negation operator. The value " "cannot be negated.") }, { VMERR_OUT_OF_RANGE, "value is out of range", VMsg("A value that was outside of the legal range of inputs was " "specified for a calculation.") }, { VMERR_STR_TOO_LONG, "string is too long", VMsg("A string value is limited to 65535 bytes in length. This " "string exceeds the length limit.") }, { VMERR_LIST_TOO_LONG, "list too long", VMsg("A list value is limited to about 13100 elements. This list " "exceeds the limit.") }, { VMERR_TREE_TOO_DEEP_EQ, "maximum equality test/hash recursion depth exceeded", VMsg("This equality comparison or hash calculation is too complex and " "cannot be performed. This usually indicates that a value " "contains circular references, such as a Vector that contains " "a reference to itself, or to another Vector that contains a " "reference to the first one. This type of value cannot be " "compared for equality or used in a LookupTable.") }, { VMERR_NO_INT_CONV, "cannot convert value to integer", VMsg("This value cannot be converted to an integer.") }, { VMERR_BAD_TYPE_MOD, "invalid datatype for modulo operator", VMsg("Invalid datatype for the modulo operator. These values can't be " "combined with this operator.") }, { VMERR_BAD_TYPE_BIT_AND, "invalid datatype for bitwise AND operator", VMsg("Invalid datatype for the bitwise AND operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_BIT_OR, "invalid datatype for bitwise OR operator", VMsg("Invalid datatype for the bitwise OR operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_XOR, "invalid datatype for XOR operator", VMsg("Invalid datatype for the XOR operator. These values can't " "be combined with this operator.") }, { VMERR_BAD_TYPE_SHL, "invalid datatype for left-shift operator '<<'", VMsg("Invalid datatype for the left-shift operator '<<'. These values " "can't be combined with this operator.") }, { VMERR_BAD_TYPE_ASHR, "invalid datatype for arithmetic right-shift operator '>>'", VMsg("Invalid datatype for the arithmetic right-shift operator '>>'. " "These values can't be combined with this operator.") }, { VMERR_BAD_TYPE_BIT_NOT, "invalid datatype for bitwise NOT operator", VMsg("Invalid datatype for the bitwise NOT operator. These values can't " "be combined with this operator.") }, { VMERR_CODEPTR_VAL_REQD, "code pointer value required", VMsg("Invalid type - code pointer value required. (This probably " "indicates an internal problem in the interpreter.)") }, { VMERR_EXCEPTION_OBJ_REQD, "exception object required, but 'new' did not yield an object", VMsg("The VM tried to construct a new program-defined exception object " "to represent a run-time error that occurred, but 'new' did not " "yield an object. Note that another underlying run-time error " "occurred that triggered the throw in the first place, but " "information on that error is not available now because of the " "problem creating the exception object to represent that error.") }, { VMERR_NO_DOUBLE_CONV, "cannot convert value to native floating point", VMsg("The value cannot be converted to a floating-point type.") }, { VMERR_NO_NUM_CONV, "cannot convert value to a numeric type", VMsg("The value cannot be converted to a numeric type. Only values " "that can be converted to integer or BigNumber can be used in " "this context.") }, { VMERR_BAD_TYPE_LSHR, "invalid datatype for logical right-shift operator '>>>'", VMsg("Invalid datatype for the logical right-shift operator '>>>'. " "These values can't be combined with this operator.") }, { VMERR_WRONG_NUM_OF_ARGS, "wrong number of arguments", VMsg("The wrong number of arguments was passed to a function or method " "in the invocation of the function or method.") }, { VMERR_WRONG_NUM_OF_ARGS_CALLING, "argument mismatch calling %s - function definition is incorrect", VMsg("The number of arguments doesn't match the number expected " "calling %s. Check the function or method and correct the " "number of parameters that it is declared to receive.") }, { VMERR_NIL_DEREF, "nil object reference", VMsg("The value 'nil' was used to reference an object property. Only " "valid object references can be used in property evaluations.") }, { VMERR_MISSING_NAMED_ARG, "missing named argument '%s'", VMsg("The named argument '%s' was expected in a function or method " "call, but it wasn't provided by the caller.") }, { VMERR_BAD_TYPE_CALL, "invalid type for call", VMsg("The value cannot be invoked as a method or function.") }, { VMERR_NIL_SELF, "nil 'self' value is not allowed", VMsg("'self' cannot be nil. The function or method context has " "a nil value for 'self', which is not allowed.") }, { VMERR_CANNOT_CREATE_INST, "cannot create instance of object - object is not a class", VMsg("An instance of this object cannot be created, because this " "object is not a class.") }, { VMERR_ILLEGAL_NEW, "cannot create instance - class does not allow dynamic construction", VMsg("An instance of this class cannot be created, because this class " "does not allow dynamic construction.") }, { VMERR_INVALID_OPCODE, "invalid opcode - possible image file corruption", VMsg("Invalid instruction opcode - the image file might be corrupted.") }, { VMERR_UNHANDLED_EXC, "unhandled exception", VMsg("An exception was thrown but was not caught by the program. " "The interpreter is terminating execution of the program.") }, { VMERR_STACK_OVERFLOW, "stack overflow", VMsg("Stack overflow. This indicates that function or method calls " "were nested too deeply; this might have occurred because of " "unterminated recursion, which can happen when a function or method " "calls itself (either directly or indirectly).") }, { VMERR_BAD_TYPE_BIF, "invalid type for intrinsic function argument", VMsg("An invalid datatype was provided for an intrinsic " "function argument.") }, { VMERR_SAY_IS_NOT_DEFINED, "default output function is not defined", VMsg("The default output function is not defined. Implicit string " "display is not allowed until a default output function " "is specified.") }, { VMERR_BAD_VAL_BIF, "invalid value for intrinsic function argument", VMsg("An invalid value was specified for an intrinsic function argument. " "The value is out of range or is not an allowed value.") }, { VMERR_BREAKPOINT, "breakpoint encountered", VMsg("A breakpoint instruction was encountered, and no debugger is " "active. The compiler might have inserted this breakpoint to " "indicate an invalid or unreachable location in the code, so " "executing this instruction probably indicates an error in the " "program.") }, { VMERR_CALLEXT_NOT_IMPL, "external function calls are not implemented in this version", VMsg("This version of the interpreter does not implement external " "function calls. This program requires an interpreter that " "provides external function call capabilities, so this program " "is not compatible with this interpreter.") }, { VMERR_INVALID_OPCODE_MOD, "invalid opcode modifier - possible image file corruption", VMsg("Invalid instruction opcode modifier - the image file might " "be corrupted.") }, /* * Note - do NOT use the VMsg() macro on this message, since we always * want to have this verbose message available. */ { VMERR_NO_CHARMAP_FILE, "No mapping file available for local character set \"%.32s\"", "[Warning: no mapping file is available for the local character set " "\"%.32s\". The system will use a default ASCII character set " "mapping instead, so accented characters will be displayed without " "their accents.]" }, { VMERR_UNHANDLED_EXC_PARAM, "Unhandled exception: %s", VMsg("Unhandled exception: %s") }, { VMERR_VM_EXC_PARAM, "VM Error: %s", VMsg("VM Error: %s") VBook("This is used as a generic template for VM run-time " "exception messages. The interpreter uses this to " "display unhandled exceptions that terminate the program.") }, { VMERR_VM_EXC_CODE, "VM Error: code %d", VMsg("VM Error: code %d") VBook("This is used as a generic template for VM run-time " "exceptions. The interpreter uses this to report unhandled " "exceptions that terminate the program. When it can't find " "any message for the VM error code, the interpreter simply " "displays the error number using this template.") }, { VMERR_EXC_IN_STATIC_INIT, "Exception in static initializer for %s.%s: %s", VMsg("An exception occurred in the static initializer for " "%s.%s: %s") }, { VMERR_INTCLS_GENERAL_ERROR, "intrinsic class exception: %s", VMsg("Exception in intrinsic class method: %s") }, { VMERR_STACK_OUT_OF_BOUNDS, "stack access is out of bounds", VMsg("The program attempted to access a stack location that isn't " "part of the current expression storage area. This probably " "indicates a problem with the compiler that was used to create " "this program, or a corrupted program file.") }, { VMERR_DBG_ABORT, "'abort' signal", VMsg("'abort' signal") VBook("This exception is used internally by the debugger to " "signal program termination via the debugger UI.") }, { VMERR_DBG_RESTART, "'restart' signal", VMsg("'restart' signal") VBook("This exception is used internally by the debugger to " "signal program restart via the debugger UI.") }, { VMERR_DBG_HALT, "debugger VM halt", VMsg("debugger VM halt") VBook("This exception is used internally by the debugger to " "signal program termination via the debugger UI.") }, { VMERR_DBG_INTERRUPT, "interrupted by user", VMsg("The program was interrupted by a user interrupt key or " "other action.") }, { VMERR_NO_DEBUGGER, "no debugger available", VMsg("An instruction was encountered that requires the debugger, " "but this interpreter version doesn't include debugging " "capaabilities.") }, { VMERR_BAD_FRAME, "invalid frame in debugger local/parameter evaluation", VMsg("An invalid stack frame was specified in a debugger local/parameter " "evaluation. This probably indicates an internal problem in the " "debugger.") }, { VMERR_BAD_SPEC_EVAL, "invalid speculative expression", VMsg("This expression cannot be executed speculatively. (This does not " "indicate a problem; it's merely an internal condition in the " "debugger.)") }, { VMERR_INVAL_DBG_EXPR, "invalid debugger expression", VMsg("This expression cannot be evaluated in the debugger.") }, { VMERR_NO_IMAGE_DBG_INFO, "image file has no debugging information - recompile for debugging", VMsg("The image file has no debugging information. You must recompile " "the source code for this program with debugging information in " "order to run the program under the debugger.") }, { VMERR_BIGNUM_NO_REGS, "out of temporary floating point registers (calculation too complex)", VMsg("The interpreter is out of temporary floating point registers. " "This probably indicates that an excessively complex calculation " "has been attempted.") }, { VMERR_NO_BIGNUM_CONV, "cannot convert value to BigNumber", VMsg("This value cannot be converted to a BigNumber.") } #endif /* VMERR_OMIT_MESSAGES */ }; /* size of the english message array */ size_t vm_message_count_english = sizeof(vm_messages_english)/sizeof(vm_messages_english[0]); /* * the actual message array - we'll initialize this to the * english list that's linked in, but if we find an external message * file, we'll use the external file instead */ const err_msg_t *vm_messages = vm_messages_english; /* message count - initialize to the english message array count */ size_t vm_message_count = sizeof(vm_messages_english)/sizeof(vm_messages_english[0]); /* ------------------------------------------------------------------------ */ /* * we don't need the VMsg() (verbose message) cover macro any more */ #undef VMsg frobtads-1.2.3/tads3/vminit.cpp0000644000175000001440000002707312145504453015550 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMINIT.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminit.cpp - initialize and terminate VM processing Function Provides functions to create and destroy global objects for a VM session. Notes Modified 04/06/99 MJRoberts - Creation */ #include #include "t3std.h" #include "charmap.h" #include "vminit.h" #include "vmerr.h" #include "vmfile.h" #include "vmimage.h" #include "vmpool.h" #include "vmobj.h" #include "vmstack.h" #include "vmundo.h" #include "vmmeta.h" #include "vmbif.h" #include "vmrun.h" #include "vmpredef.h" #include "vmmcreg.h" #include "vmbiftad.h" #include "resload.h" #include "vmhost.h" #include "vmconsol.h" #include "vmbignum.h" #include "vmsrcf.h" #include "vmparam.h" #include "vmmain.h" #include "vmtobj.h" #include "osifcnet.h" #include "vmhash.h" #include "vmtz.h" /* ------------------------------------------------------------------------ */ /* * Perform base initialization. This is an internal routine called only * by higher-level initialization routines; we perform all of the * generic, configuration-independent initialization. */ void vm_init_base(vm_globals **vmg, const vm_init_options *opts) { /* * Allocate globals according to build-time configuration, then * assign the global pointer to a local named vmg__. This will * ensure that the globals are accessible for all of the different * build-time configurations. */ vm_globals *vmg__ = *vmg = vmglob_alloc(); /* * in configurations where globals aren't passed as parameters, vmg__ * will never be referenced; explicitly reference it so that the * compiler knows the assignment above is intentional even if it's not * ever used in this particular build configuration */ (void)vmg__; /* initialize the error stack */ err_init(VM_ERR_STACK_BYTES); /* get the system resource loader from the host interface */ CResLoader *map_loader = opts->hostifc->get_sys_res_loader(); /* if an external message set hasn't been loaded, try loading one */ if (!err_is_message_file_loaded() && map_loader != 0) { /* try finding a message file */ osfildef *fp = map_loader->open_res_file( VM_ERR_MSG_FNAME, 0, VM_ERR_MSG_RESTYPE); if (fp != 0) { /* * try loading it - if that fails, we'll just be left with * the built-in messages, so we won't have lost anything for * trying */ err_load_vm_message_file(fp); /* we're done with the file */ osfcls(fp); } } /* remember the host interface */ G_host_ifc = opts->hostifc; /* initialize the system debug log file name */ char path[OSFNMAX]; opts->hostifc->get_special_file_path(path, sizeof(path), OS_GSP_LOGFILE); os_build_full_path(G_syslogfile, sizeof(G_syslogfile), path, "tadslog.txt"); /* create the object table */ VM_IF_ALLOC_PRE_GLOBAL(G_obj_table = new CVmObjTable()); G_obj_table->init(vmg0_); /* * Create the memory manager. Empirically, our hybrid heap allocator * is faster than the standard C++ run-time library's allocator on many * platforms, so use it instead of hte basic 'malloc' allocator. */ G_varheap = new CVmVarHeapHybrid(G_obj_table); // G_varheap = new CVmVarHeapMalloc(); to use the system 'malloc' instead G_mem = new CVmMemory(vmg_ G_varheap); /* create the undo manager */ G_undo = new CVmUndo(VM_UNDO_MAX_RECORDS, VM_UNDO_MAX_SAVEPTS); /* create the metafile and function set tables */ G_meta_table = new CVmMetaTable(5); G_bif_table = new CVmBifTable(5); /* create the time zone cache */ G_tzcache = new CVmTimeZoneCache(); /* initialize the metaclass registration tables */ vm_register_metaclasses(); /* initialize the TadsObject class */ CVmObjTads::class_init(vmg0_); /* create the byte-code interpreter */ VM_IFELSE_ALLOC_PRE_GLOBAL( G_interpreter = new CVmRun(VM_STACK_SIZE, vm_init_stack_reserve()), G_interpreter->init()); /* presume we won't create debugger information */ G_debugger = 0; G_srcf_table = 0; /* presume we don't have a network configuration */ G_net_config = 0; /* initialize the debugger if present */ vm_init_debugger(vmg0_); /* create the source file table */ G_srcf_table = new CVmSrcfTable(); /* create the pre-defined object mapper */ VM_IFELSE_ALLOC_PRE_GLOBAL(G_predef = new CVmPredef, G_predef->reset()); /* presume we're in normal execution mode (not preinit) */ G_preinit_mode = FALSE; /* allocate the TADS intrinsic function set's globals */ G_bif_tads_globals = new CVmBifTADSGlobals(vmg0_); /* allocate the BigNumber register cache */ CVmObjBigNum::init_cache(); /* no image loader yet */ G_image_loader = 0; /* * If the caller explicitly specified a character set, use it. * Otherwise, ask the OS layer for the default character set we * should use. */ char disp_mapname[32]; const char *charset = opts->charset; if (charset == 0) { /* the user did not specify a mapping - ask the OS for the default */ os_get_charmap(disp_mapname, OS_CHARMAP_DISPLAY); /* use the name we got from the OS */ charset = disp_mapname; /* there's no explicit global character set name setting to store */ G_disp_cset_name = 0; } else { /* save the global character set name */ G_disp_cset_name = lib_copy_str(charset); } /* create the display character maps */ G_cmap_from_ui = CCharmapToUni::load(map_loader, charset); G_cmap_to_ui = CCharmapToLocal::load(map_loader, charset); /* create the filename character maps */ char filename_mapname[32]; os_get_charmap(filename_mapname, OS_CHARMAP_FILENAME); G_cmap_from_fname = CCharmapToUni::load(map_loader, filename_mapname); G_cmap_to_fname = CCharmapToLocal::load(map_loader, filename_mapname); /* create the file-contents character maps */ char filecont_mapname[32]; os_get_charmap(filecont_mapname, OS_CHARMAP_FILECONTENTS); G_cmap_from_file = CCharmapToUni::load(map_loader, filecont_mapname); G_cmap_to_file = CCharmapToLocal::load(map_loader, filecont_mapname); /* * If the caller specified a separate log-file character set, create * the mapping. Otherwise, just use the to-file mapper for log files. */ if (opts->log_charset != 0) { /* load the specified log file output mapping */ G_cmap_to_log = CCharmapToLocal::load(map_loader, opts->log_charset); } else { /* no log file mapping is specified, so use the generic file map */ if ((G_cmap_to_log = G_cmap_to_file) != 0) G_cmap_to_log->add_ref(); } /* make a note of whether we had any problems loading the maps */ int disp_map_err = (G_cmap_from_ui == 0 || G_cmap_to_ui == 0); /* if we failed to create any of the maps, load defaults */ if (G_cmap_from_ui == 0) G_cmap_from_ui = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_ui == 0) G_cmap_to_ui = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_fname == 0) G_cmap_from_fname = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_fname == 0) G_cmap_to_fname = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_from_file == 0) G_cmap_from_file = CCharmapToUni::load(map_loader, "us-ascii"); if (G_cmap_to_file == 0) G_cmap_to_file = CCharmapToLocal::load(map_loader, "us-ascii"); if (G_cmap_to_log == 0) G_cmap_to_log = CCharmapToLocal::load(map_loader, "us-ascii"); /* create the primary console */ G_console = opts->clientifc->create_console(VMGLOB_ADDR); /* * if we had any trouble opening the display character mapping file, * make a note that we are using a default mapping */ if (disp_map_err) { const char *msg; char buf[256]; /* get the message */ msg = err_get_msg(vm_messages, vm_message_count, VMERR_NO_CHARMAP_FILE, TRUE); /* format it */ sprintf(buf, msg, charset); /* display it */ opts->clientifc->display_error(VMGLOB_ADDR, 0, buf, TRUE); } } /* ------------------------------------------------------------------------ */ /* * Terminate the VM */ void vm_terminate(vm_globals *vmg__, CVmMainClientIfc *clientifc) { /* tell the debugger to shut down, if necessary */ vm_terminate_debug_shutdown(vmg0_); /* drop all undo information */ G_undo->drop_undo(vmg0_); /* delete the main console */ clientifc->delete_console(VMGLOB_ADDR, G_console); /* release references on the character mappers */ G_cmap_from_fname->release_ref(); G_cmap_to_fname->release_ref(); G_cmap_from_ui->release_ref(); G_cmap_to_ui->release_ref(); G_cmap_from_file->release_ref(); G_cmap_to_file->release_ref(); G_cmap_to_log->release_ref(); /* delete the saved UI character set name, if any */ lib_free_str(G_disp_cset_name); /* delete the BigNumber register cache */ CVmObjBigNum::term_cache(); /* delete the TADS intrinsic function set's globals */ delete G_bif_tads_globals; /* delete the predefined object table */ VM_IF_ALLOC_PRE_GLOBAL(delete G_predef); /* delete the interpreter */ VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_interpreter, G_interpreter->terminate()); /* terminate the TadsObject class */ CVmObjTads::class_term(vmg0_); /* delete the source file table */ delete G_srcf_table; /* delete debugger objects */ vm_terminate_debug_delete(vmg0_); /* delete dynamic compilation objects */ if (G_dyncomp != 0) { delete G_dyncomp; G_dyncomp = 0; } /* delete the constant pools */ VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_code_pool, G_code_pool->terminate()); VM_IFELSE_ALLOC_PRE_GLOBAL(delete G_const_pool, G_const_pool->terminate()); /* * Clear the object table. This deletes garbage-collected objects but * doesn't delete the object table itself; we'll do that after we've * cleared out the metaclass and function-set tables. We need to * remove the gc objects before the metaclasses and function tables * because the gc objects sometimes depend on their metaclasses. But * the metaclasses and function sets sometimes keep VM globals, so we * need to keep the object table around until after they're cleaned up. */ G_obj_table->clear_obj_table(vmg0_); /* delete the dependency tables */ G_bif_table->clear(vmg0_); delete G_meta_table; delete G_bif_table; /* delete the undo manager */ delete G_undo; /* delete the object table */ G_obj_table->delete_obj_table(vmg0_); VM_IF_ALLOC_PRE_GLOBAL(delete G_obj_table); /* delete the memory manager and heap manager */ delete G_mem; delete G_varheap; /* delete the time zone cache */ delete G_tzcache; /* delete the error context */ err_terminate(); /* delete the globals */ vmglob_delete(vmg__); } frobtads-1.2.3/tads3/charmap.cpp0000644000175000001440000024175112014201654015646 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/charmap.cpp,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name charmap.cpp - character mapper Function Notes Modified 10/17/98 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "resload.h" #include "charmap.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * Basic Mapper Class */ /* * match a name prefix (for the various Latin-X synonym names) */ static int prefixeq(const char *table_name, size_t digit_ofs, const char *prefix) { return (digit_ofs == strlen(prefix) && memicmp(table_name, prefix, digit_ofs) == 0); } /* * Open a mapping file, applying synonyms for character set names */ osfildef *CCharmap::open_map_file_syn(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type) { osfildef *fp; /* try opening the file/resource with the exact name given */ if ((fp = open_map_file(res_loader, table_name, map_type)) != 0) return fp; /* * We didn't find a file with the exact name given. Try various * synonyms for the Latin-X character sets. */ const char *p; for (p = table_name + strlen(table_name) ; p > table_name && is_digit(*(p-1)) ; --p) ; size_t ofs = p - table_name; int num = atoi(p); int found_suffix = (ofs != strlen(p) && num != 0); if (found_suffix) { if (prefixeq(table_name, ofs, "latin") || prefixeq(table_name, ofs, "latin-") || prefixeq(table_name, ofs, "iso") || prefixeq(table_name, ofs, "iso-") || prefixeq(table_name, ofs, "8859-") || prefixeq(table_name, ofs, "iso8859-") || prefixeq(table_name, ofs, "iso-8859-") || prefixeq(table_name, ofs, "iso_8859-") || prefixeq(table_name, ofs, "iso_8859_") || prefixeq(table_name, ofs, "l")) { /* rebuild the name as "isoX" */ char buf[25]; sprintf(buf, "iso%d", num); /* try loading it */ if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } } /* * if that didn't work, and the whole string looks like just a number, * try putting "cp" in front of it - i.e., allow loading cp1252 as just * "1252" */ if (found_suffix && p == table_name) { /* it's all digits - try making it "cpXXXX" */ char buf[25]; sprintf(buf, "cp%d", num); if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } /* * if we still don't have a file, check for "winXXXX" and "dosXXXX", as * synonyms for cpXXXX */ if (found_suffix && (prefixeq(table_name, ofs, "win") || prefixeq(table_name, ofs, "win-") || prefixeq(table_name, ofs, "windows") || prefixeq(table_name, ofs, "windows-") || prefixeq(table_name, ofs, "dos") || prefixeq(table_name, ofs, "dos-"))) { /* try making it "cpXXXX" */ char buf[25]; sprintf(buf, "cp%d", num); if ((fp = open_map_file(res_loader, buf, map_type)) != 0) return fp; } /* we couldn't find an alternative name; give up */ return 0; } /* * Open and characterize a mapping file */ osfildef *CCharmap::open_map_file(class CResLoader *res_loader, const char *table_name, charmap_type_t *map_type) { osfildef *fp; char respath[100]; ulong startpos; uchar buf[256]; uint entry_cnt; int found_single; int found_double; /* * Generate the full resource path - character mapping resource paths * always start with "charmap/" followed by the table name, plus the * ".tcm" extension. We use lower-case names for the mapping files, so * explicitly convert to lower, in case we're on a case-sensitive file * system like Unix. */ t3sprintf(respath, sizeof(respath), "charmap/%s.tcm", table_name); t3strlwr(respath); /* open the file for the character set */ fp = res_loader->open_res_file(respath, "charmap/cmaplib", "CLIB"); /* if we couldn't open the mapping file, return failure */ if (fp == 0) return 0; /* note the initial seek position */ startpos = osfpos(fp); /* read the header and the local-to-unicode header */ if (osfrb(fp, buf, 6)) goto fail; /* get the number of entries from the local-to-unicode header */ entry_cnt = osrp2(buf + 4); /* * Scan the entries to determine if we have single-byte, * double-byte, or both. */ found_single = found_double = FALSE; while (entry_cnt > 0) { size_t cur; const uchar *p; /* read up to a buffer-full or the remaining size */ cur = sizeof(buf)/4; if (cur > entry_cnt) cur = entry_cnt; /* read it */ if (osfrb(fp, buf, cur*4)) goto fail; /* deduct the amount we just read from the amount remaining */ entry_cnt -= cur; /* scan the entries */ for (p = buf ; cur > 0 ; --cur, p += 4) { /* * Note whether this is a single-byte or double-byte entry. * If the high-order byte is non-zero, it's a double-byte * entry; otherwise, it's a single-byte entry. * * Note that we read the UINT2 at (p+2), because that's the * local character-set code point in this tuple. */ if (((uint)osrp2(p + 2)) > 0xFF) found_double = TRUE; else found_single = TRUE; } /* * if we've found both single- and double-byte characters so * far, there's no need to look any further, since we know * everything about the file now */ if (found_single && found_double) break; } /* * create the appropriate mapper, depending on whether we found * single, double, or mixed characters */ if (found_single && found_double) { /* it's mixed */ *map_type = CHARMAP_TYPE_MB; } else if (found_double) { /* it's all double-byte */ *map_type = CHARMAP_TYPE_DB; } else if (found_single) { /* it's all single-byte */ *map_type = CHARMAP_TYPE_SB; } else { /* no mappings found at all - presume it's a single-byte mapper */ *map_type = CHARMAP_TYPE_SB; } /* seek back to the start of the table */ osfseek(fp, startpos, OSFSK_SET); /* return the file pointer */ return fp; fail: /* close the file and return failure */ osfcls(fp); return 0; } /* ------------------------------------------------------------------------ */ /* * Special built-in mapper to 7-bit ASCII. This is available as a last * resort when no external mapping file can be found. */ /* * create a plain ascii translator */ CCharmapToLocalASCII::CCharmapToLocalASCII() { unsigned char *dst; wchar_t *exp_dst; size_t siz; size_t exp_siz; struct ascii_map_t { wchar_t uni; char asc[5]; }; ascii_map_t *p; static ascii_map_t ascii_mapping[] = { /* regular ASCII characters */ { 1, { 1 } }, { 2, { 2 } }, { 3, { 3 } }, { 4, { 4 } }, { 5, { 5 } }, { 6, { 6 } }, { 7, { 7 } }, { 8, { 8 } }, { 9, { 9 } }, { 10, { 10 } }, { 11, { 11 } }, { 12, { 12 } }, { 13, { 13 } }, { 14, { 14 } }, { 15, { 15 } }, { 16, { 16 } }, { 17, { 17 } }, { 18, { 18 } }, { 19, { 19 } }, { 20, { 20 } }, { 21, { 21 } }, { 22, { 22 } }, { 23, { 23 } }, { 24, { 24 } }, { 25, { 25 } }, { 26, { 26 } }, { 27, { 27 } }, { 28, { 28 } }, { 29, { 29 } }, { 30, { 30 } }, { 31, { 31 } }, { 32, { 32 } }, { 33, { 33 } }, { 34, { 34 } }, { 35, { 35 } }, { 36, { 36 } }, { 37, { 37 } }, { 38, { 38 } }, { 39, { 39 } }, { 40, { 40 } }, { 41, { 41 } }, { 42, { 42 } }, { 43, { 43 } }, { 44, { 44 } }, { 45, { 45 } }, { 46, { 46 } }, { 47, { 47 } }, { 48, { 48 } }, { 49, { 49 } }, { 50, { 50 } }, { 51, { 51 } }, { 52, { 52 } }, { 53, { 53 } }, { 54, { 54 } }, { 55, { 55 } }, { 56, { 56 } }, { 57, { 57 } }, { 58, { 58 } }, { 59, { 59 } }, { 60, { 60 } }, { 61, { 61 } }, { 62, { 62 } }, { 63, { 63 } }, { 64, { 64 } }, { 65, { 65 } }, { 66, { 66 } }, { 67, { 67 } }, { 68, { 68 } }, { 69, { 69 } }, { 70, { 70 } }, { 71, { 71 } }, { 72, { 72 } }, { 73, { 73 } }, { 74, { 74 } }, { 75, { 75 } }, { 76, { 76 } }, { 77, { 77 } }, { 78, { 78 } }, { 79, { 79 } }, { 80, { 80 } }, { 81, { 81 } }, { 82, { 82 } }, { 83, { 83 } }, { 84, { 84 } }, { 85, { 85 } }, { 86, { 86 } }, { 87, { 87 } }, { 88, { 88 } }, { 89, { 89 } }, { 90, { 90 } }, { 91, { 91 } }, { 92, { 92 } }, { 93, { 93 } }, { 94, { 94 } }, { 95, { 95 } }, { 96, { 96 } }, { 97, { 97 } }, { 98, { 98 } }, { 99, { 99 } }, { 100, { 100 } }, { 101, { 101 } }, { 102, { 102 } }, { 103, { 103 } }, { 104, { 104 } }, { 105, { 105 } }, { 106, { 106 } }, { 107, { 107 } }, { 108, { 108 } }, { 109, { 109 } }, { 110, { 110 } }, { 111, { 111 } }, { 112, { 112 } }, { 113, { 113 } }, { 114, { 114 } }, { 115, { 115 } }, { 116, { 116 } }, { 117, { 117 } }, { 118, { 118 } }, { 119, { 119 } }, { 120, { 120 } }, { 121, { 121 } }, { 122, { 122 } }, { 123, { 123 } }, { 124, { 124 } }, { 125, { 125 } }, { 126, { 126 } }, { 127, { 127 } }, /* Latin-1 accented characters and symbols */ { 353, "s" }, { 352, "S" }, { 8218, "\'" }, { 8222, "\"" }, { 8249, "<" }, { 338, "OE" }, { 8216, "\'" }, { 8217, "\'" }, { 8220, "\"" }, { 8221, "\"" }, { 8211, "-" }, { 8212, "--" }, { 8482, "(tm)" }, { 8250, ">" }, { 339, "oe" }, { 376, "Y" }, { 162, "c" }, { 163, "L" }, { 165, "Y" }, { 166, "|" }, { 169, "(c)" }, { 170, "a" }, { 173, " " }, { 174, "(R)" }, { 175, "-" }, { 177, "+/-" }, { 178, "2" }, { 179, "3" }, { 180, "\'" }, { 181, "u" }, { 182, "P" }, { 183, "*" }, { 184, "," }, { 185, "1" }, { 186, "o" }, { 171, "<<" }, { 187, ">>" }, { 188, "1/4" }, { 189, "1/2" }, { 190, "3/4" }, { 192, "A" }, { 193, "A" }, { 194, "A" }, { 195, "A" }, { 196, "A" }, { 197, "A" }, { 198, "AE" }, { 199, "C" }, { 200, "E" }, { 201, "E" }, { 202, "E" }, { 203, "E" }, { 204, "I" }, { 205, "I" }, { 206, "I" }, { 207, "I" }, { 209, "N" }, { 210, "O" }, { 211, "O" }, { 212, "O" }, { 213, "O" }, { 214, "O" }, { 215, "x" }, { 216, "O" }, { 217, "U" }, { 218, "U" }, { 219, "U" }, { 220, "U" }, { 221, "Y" }, { 223, "ss" }, { 224, "a" }, { 225, "a" }, { 226, "a" }, { 227, "a" }, { 228, "a" }, { 229, "a" }, { 230, "ae" }, { 231, "c" }, { 232, "e" }, { 233, "e" }, { 234, "e" }, { 235, "e" }, { 236, "i" }, { 237, "i" }, { 238, "i" }, { 239, "i" }, { 241, "n" }, { 242, "o" }, { 243, "o" }, { 244, "o" }, { 245, "o" }, { 246, "o" }, { 247, "/" }, { 248, "o" }, { 249, "u" }, { 250, "u" }, { 251, "u" }, { 252, "u" }, { 253, "y" }, { 255, "y" }, { 710, "^" }, { 732, "~" }, /* math symbols */ { 402, "f" }, /* other symbols */ { 8226, "*" }, /* arrows */ { 8592, "<-" }, { 8594, "->" }, /* several capital Greek letters look a lot like Roman letters */ { 913, "A" }, { 914, "B" }, { 918, "Z" }, { 919, "H" }, { 921, "I" }, { 922, "K" }, { 924, "M" }, { 925, "N" }, { 927, "O" }, { 929, "P" }, { 932, "T" }, { 933, "Y" }, { 935, "X" }, /* Latin-2 accented characters */ { 260, "A" }, { 321, "L" }, { 317, "L" }, { 346, "S" }, { 350, "S" }, { 356, "T" }, { 377, "Z" }, { 381, "Z" }, { 379, "Z" }, { 261, "a" }, { 731, "o" }, { 322, "l" }, { 318, "l" }, { 347, "s" }, { 351, "s" }, { 357, "t" }, { 378, "z" }, { 733, "\"" }, { 382, "z" }, { 380, "z" }, { 340, "R" }, { 258, "A" }, { 313, "L" }, { 262, "C" }, { 268, "C" }, { 280, "E" }, { 282, "E" }, { 270, "D" }, { 272, "D" }, { 323, "N" }, { 327, "N" }, { 336, "O" }, { 344, "R" }, { 366, "U" }, { 368, "U" }, { 354, "T" }, { 341, "r" }, { 259, "a" }, { 314, "l" }, { 263, "c" }, { 269, "c" }, { 281, "e" }, { 283, "e" }, { 271, "d" }, { 273, "d" }, { 324, "n" }, { 328, "n" }, { 337, "o" }, { 345, "r" }, { 367, "u" }, { 369, "u" }, { 355, "t" }, { 0, { 0 } } }; /* determine how much space we'll need in the translation array */ for (p = ascii_mapping, siz = 0, exp_siz = 0 ; p->uni != 0 ; ++p) { /* we need space for this mapping string, plus a length prefix byte */ siz += strlen(p->asc) + 1; /* * if this is a multi-character expansion, count it in the * expansion array size */ if (strlen(p->asc) > 1) exp_siz += strlen(p->asc) + 1; } /* add in space for the default entry */ siz += 2; /* allocate the translation array */ xlat_array_ = (unsigned char *)t3malloc(siz); /* * allocate the expansion array; allocate one extra entry for the null * mapping at index zero */ exp_array_ = (wchar_t *)t3malloc((exp_siz + 1) * sizeof(wchar_t)); /* * start at element 1 of the expansion array (element zero is reserved * to indicate the null mapping) */ dst = xlat_array_; exp_dst = exp_array_ + 1; /* * Add the zeroeth entry, which serves as the default mapping for * characters that aren't otherwise mappable. */ set_mapping(0, 0); *dst++ = 1; *dst++ = '?'; /* set up the arrays */ for (p = ascii_mapping ; p->uni != 0 ; ++p) { size_t len; /* set the mapping's offset in the translation array */ set_mapping(p->uni, dst - xlat_array_); /* get the length of this mapping */ len = strlen(p->asc); /* set this mapping's length */ *dst++ = (unsigned char)len; /* copy the mapping */ memcpy(dst, p->asc, len); /* move past the mapping in the translation array */ dst += len; /* add the expansion mapping if necessary */ if (len > 1) { size_t i; /* add an expansion mapping */ set_exp_mapping(p->uni, exp_dst - exp_array_); /* set the length prefix */ *exp_dst++ = (wchar_t)len; /* add the mapping */ for (i = 0 ; i < len ; ++i) *exp_dst++ = (wchar_t)p->asc[i]; } } } /* ------------------------------------------------------------------------ */ /* * Special built-in mapper to ISO-8859-1. Because of the widespread use * of this character set, we make this mapping available even when no * external mapping file is available. */ /* * create an 8859-1 mapper */ CCharmapToLocal8859_1::CCharmapToLocal8859_1() { unsigned char *dst; size_t siz; wchar_t c; /* * Determine how much space we'll need in the translation array - we * need one byte for each character, plus one byte for the length of * each character. We also need two bytes for the default entry. */ siz = 256 + 256 + 2; /* allocate the mapping */ xlat_array_ = (unsigned char *)t3malloc(siz); /* start at the start of the array */ dst = xlat_array_; /* * Add the zeroeth entry, which serves as the default mapping for * characters that aren't otherwise mappable. */ set_mapping(0, 0); *dst++ = 1; *dst++ = '?'; /* * Set up the mappings - this is easy because each Unicode code point * from 0 to 255 maps to the same ISO 8859-1 code point. */ for (c = 0 ; c < 256 ; ++c) { /* set the mapping's offset in the translation array */ set_mapping(c, dst - xlat_array_); /* store the length (always 1) and translated character */ *dst++ = 1; *dst++ = (unsigned char)c; } } /* ------------------------------------------------------------------------ */ /* * Character mapping for Unicode to Local */ /* * create the translator */ CCharmapToLocal::CCharmapToLocal() { /* no mapping sub-tables yet */ memset(map_, 0, sizeof(map_)); memset(exp_map_, 0, sizeof(exp_map_)); /* no translation or expansion arrays yet */ xlat_array_ = 0; exp_array_ = 0; } /* * delete the translator */ CCharmapToLocal::~CCharmapToLocal() { size_t i; /* delete the translation array */ if (xlat_array_ != 0) t3free(xlat_array_); /* delete the expansion array */ if (exp_array_ != 0) t3free(exp_array_); /* delete any mapping tables we've allocated */ for (i = 0 ; i < sizeof(map_)/sizeof(map_[0]) ; ++i) { /* delete this mapping if allocated */ if (map_[i] != 0) t3free(map_[i]); } /* delete any expansion mapping tables */ for (i = 0 ; i < sizeof(exp_map_)/sizeof(exp_map_[0]) ; ++i) { /* delete this expansion mapping if allocated */ if (exp_map_[i] != 0) t3free(exp_map_[i]); } } /* * Set a mapping */ void CCharmapToLocal::set_mapping(wchar_t unicode_char, unsigned int xlat_offset) { int master_idx; /* get the master table index for this unicode character */ master_idx = (int)((unicode_char >> 8) & 0xff); /* if there's no sub-table here yet, create one */ if (map_[master_idx] == 0) { int i; /* allocate it */ map_[master_idx] = (unsigned int *)t3malloc(256 * sizeof(unsigned int)); /* * Set each entry to the default character, so that it will * produce valid results if no mapping is ever specified for the * character. The default character is always at offset zero in * the translation array. */ for (i = 0 ; i < 256 ; ++i) map_[master_idx][i] = 0; } /* set the mapping for the character's entry in the sub-table */ map_[master_idx][unicode_char & 0xff] = xlat_offset; } /* * Set an expansion mapping */ void CCharmapToLocal::set_exp_mapping(wchar_t unicode_char, unsigned int exp_offset) { int master_idx; /* get the master table index for this unicode character */ master_idx = (int)((unicode_char >> 8) & 0xff); /* if there's no sub-table here yet, create one */ if (exp_map_[master_idx] == 0) { int i; /* allocate it */ exp_map_[master_idx] = (unsigned int *)t3malloc(256 * sizeof(unsigned int)); /* * Set each entry to the default character, so that it will produce * valid results if no mapping is ever specified for the character. * The default character is always at offset zero in the expansion * array. */ for (i = 0 ; i < 256 ; ++i) exp_map_[master_idx][i] = 0; } /* set the mapping for the character's entry in the sub-table */ exp_map_[master_idx][unicode_char & 0xff] = exp_offset; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocal::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { char mapbuf[10]; size_t maplen = sizeof(mapbuf); char *mapp = mapbuf; /* map this character */ maplen = map(src.getch(), &mapp, &maplen); /* determine how to store the character */ if (dest == 0) { /* we're just counting */ } else if (dest_len >= maplen) { /* we have room for it - add it in */ memcpy(dest, mapbuf, maplen); /* advance past it */ dest += maplen; dest_len -= maplen; } else { /* there's no more room - stop now */ break; } /* add this into the total */ cur_total += maplen; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocal::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * map this character into the output, if it will fit, but in * any case count the space it needs in the output */ cur_total += map(src.getch(), &dest, &dest_len); } /* * add a null terminator if there's room, but don't count it in the * result length */ map(0, &dest, &dest_len); /* return the total length of the result */ return cur_total; } /* * Map a string, allocating a new buffer if the caller doesn't provide one. */ size_t CCharmapToLocal::map_utf8_alo( char **buf, const char *src, size_t srclen) const { /* figure out how much space we need */ size_t buflen = map_utf8(0, 0, src, srclen, 0); /* allocate the buffer, adding space for null termination */ *buf = (char *)t3malloc(buflen + 1); /* if that failed, return null */ if (buf == 0) return 0; /* do the mapping */ buflen = map_utf8(*buf, buflen, src, srclen, 0); /* fill in the null terminator */ (*buf)[buflen] = '\0'; /* return the mapped length */ return buflen; } /* * Map a null-terminated UTF-8 string to the local character set, escaping * characters that aren't part of the local character set. */ size_t CCharmapToLocal::map_utf8z_esc( char *dest, size_t dest_len, utf8_ptr src, size_t (*esc_fn)(wchar_t, char **, size_t *)) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { wchar_t ch = src.getch(); /* if this character is mappable, map it; otherwise, escape it */ if (is_mappable(src.getch())) { /* map the character */ cur_total += map(ch, &dest, &dest_len); } else { /* we can't map it, so let the escape callback handle it */ cur_total += (*esc_fn)(ch, &dest, &dest_len); } } /* * add a null terminator if there's room, but don't count it in the * result length */ map(0, &dest, &dest_len); /* return the total length of the result */ return cur_total; } /* * Escape callback for map_utf8z_esc() - prepares source-code-style * 'backslash' escape sequences for unmappable characters. */ size_t CCharmapToLocal::source_esc_cb(wchar_t ch, char **dest, size_t *len) { char buf[7]; size_t copylen; /* prepare our own representation */ sprintf(buf, "\\u%04x", (unsigned int)ch); /* copy the whole thing if possible, but limit to the available space */ copylen = 6; if (copylen > *len) copylen = *len; /* copy the bytes */ memcpy(*dest, buf, copylen); /* advance the buffer pointers */ *dest += copylen; *len -= copylen; /* return the full space needed */ return 6; } /* * Map to UTF8 */ size_t CCharmapToLocal::map_utf8(char *dest, size_t dest_len, const char *src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_ptr; /* set up the source UTF-8 pointer */ src_ptr.set((char *)src); /* map it and return the result */ return map_utf8(dest, dest_len, src_ptr, src_byte_len, src_bytes_used); } /* * Create a mapper and load a mapping file */ CCharmapToLocal *CCharmapToLocal::load(CResLoader *res_loader, const char *table_name) { osfildef *fp; CCharmapToLocal *mapper; charmap_type_t map_type; /* if they want a trivial UTF-8 translator, return one */ if (name_is_utf8_synonym(table_name)) return new CCharmapToLocalUTF8(); /* if they want a Unicode 16-bit encoding, return one */ if (name_is_ucs2le_synonym(table_name)) return new CCharmapToLocalUcs2Little(); if (name_is_ucs2be_synonym(table_name)) return new CCharmapToLocalUcs2Big(); /* presume failure */ mapper = 0; /* open and characterize the mapping file */ fp = open_map_file_syn(res_loader, table_name, &map_type); /* if we didn't find a map file, check for built-in mappings */ if (fp == 0) { /* if they want a plain ASCII translator, return a default one */ if (name_is_ascii_synonym(table_name)) return new CCharmapToLocalASCII(); /* if they want a plain ISO-8859-1 translator, return a default one */ if (name_is_8859_1_synonym(table_name)) return new CCharmapToLocal8859_1(); /* no map file - return failure */ return 0; } /* create an appropriate mapper */ switch(map_type) { case CHARMAP_TYPE_SB: /* create a single-byte mapper */ mapper = new CCharmapToLocalSB(); break; case CHARMAP_TYPE_DB: /* create a double-byte mapper */ mapper = new CCharmapToLocalDB(); break; case CHARMAP_TYPE_MB: /* create a mixed multi-byte mapper */ mapper = new CCharmapToLocalMB(); break; default: /* other mapper types are currently unknown */ break; } /* if we successfully created a mapper, tell it to load the table */ if (mapper != 0) { /* load the table */ mapper->load_table(fp); } /* close the file */ osfcls(fp); /* return the mapper, if any */ return mapper; } /* * Load the character set translation table */ void CCharmapToLocal::load_table(osfildef *fp) { ulong startpos; ulong ofs; uchar buf[256]; uint cnt; ulong xbytes; ulong xchars; uint next_ofs; /* note the initial seek position */ startpos = osfpos(fp); /* read the first entry, which gives the offset of the to-local table */ if (osfrb(fp, buf, 4)) return; ofs = t3rp4u(buf); /* seek to the to-local table */ osfseek(fp, startpos + ofs, OSFSK_SET); /* read the number of entries and number of bytes needed */ if (osfrb(fp, buf, 6)) return; cnt = osrp2(buf); xbytes = t3rp4u(buf + 2); /* * Allocate space for the translation table. Note that we cannot * handle translation tables bigger than the maximum allowed in a * single allocation unit on the operating system. */ if (xbytes > OSMALMAX) return; xlat_array_ = (unsigned char *)t3malloc(xbytes); if (xlat_array_ == 0) return; /* * Read each mapping */ for (next_ofs = 0 ; cnt > 0 ; --cnt) { wchar_t codept; uint xlen; /* read the code point and translation length */ if (osfrb(fp, buf, 3)) return; /* decode the code point and translation length */ codept = osrp2(buf); xlen = (unsigned int)buf[2]; /* assign the mapping */ set_mapping(codept, next_ofs); /* store the translation length */ xlat_array_[next_ofs++] = buf[2]; /* read the translation bytes */ if (osfrb(fp, xlat_array_ + next_ofs, xlen)) return; /* skip past the translation bytes we've read */ next_ofs += xlen; } /* * Next, read the expansions, if present. * * If we find the $EOF marker, it means it's an old-format file without * the separate expansion definitions. Otherwise, we'll have the * expansion entry count and the aggregate number of unicode characters * in all of the expansions. */ if (osfrb(fp, buf, 6) || memcmp(buf, "$EOF", 4) == 0) return; /* decode the expansion entry count and aggregate length */ cnt = osrp2(buf); xchars = t3rp4u(buf + 2); /* * add one entry so that we can leave index zero unused, to indicate * unmapped characters */ ++xchars; /* add one array slot per entry, for the length prefix slots */ xchars += cnt; /* allocate space for the expansions */ exp_array_ = (wchar_t *)t3malloc(xchars * sizeof(wchar_t)); if (exp_array_ == 0) return; /* * read the mappings; start loading them at index 1, since we want to * leave index 0 unused so that it can indicate unused mappings */ for (next_ofs = 1 ; cnt > 0 ; --cnt) { wchar_t codept; uint xlen; size_t i; /* read this entry's unicode value and expansion character length */ if (osfrb(fp, buf, 3)) return; /* decode the code point and expansion length */ codept = osrp2(buf); xlen = (uint)buf[2]; /* assign the expansion mapping */ set_exp_mapping(codept, next_ofs); /* set the length prefix */ exp_array_[next_ofs++] = (wchar_t)xlen; /* read and store the expansion characters */ for (i = 0 ; i < xlen ; ++i) { /* read this translation */ if (osfrb(fp, buf, 2)) return; /* decode and store this translation */ exp_array_[next_ofs++] = osrp2(buf); } } } /* * Write to a file */ int CCharmapToLocal::write_file(CVmDataSource *fp, const char *buf, size_t bufl) { utf8_ptr p; /* set up to read from the buffer */ p.set((char *)buf); /* map and write one buffer-full at a time */ while (bufl > 0) { char conv_buf[256]; size_t conv_len; size_t used_src_len; /* map as much as we can fit into our buffer */ conv_len = map_utf8(conv_buf, sizeof(conv_buf), p, bufl, &used_src_len); /* write out this chunk */ if (fp->write(conv_buf, conv_len)) return 1; /* advance past this chunk in the input */ p.set(p.getptr() + used_src_len); bufl -= used_src_len; } /* no errors */ return 0; } /* ------------------------------------------------------------------------ */ /* * Character mapper - trivial UTF8-to-UTF8 conversion */ /* * map a character */ size_t CCharmapToLocalUTF8::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* get the character size */ size_t map_len = utf8_ptr::s_wchar_size(unicode_char); /* if we don't have room for one more character, abort */ if (*output_len < map_len) { *output_len = 0; return map_len; } /* store the mapping */ utf8_ptr::s_putch(*output_ptr, unicode_char); /* increment the pointer by the number of characters we copied */ *output_ptr += map_len; /* adjust the remaining output length */ *output_len -= map_len; /* return the size of the result */ return map_len; } /* * Map a UTF-8 string of known byte length */ size_t CCharmapToLocalUTF8::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { /* * if they didn't give us a destination buffer, tell them how much * space is needed for the copy - this is identical to the length of * the source string since we make no changes to it */ if (dest == 0) { if (src_bytes_used != 0) *src_bytes_used = 0; return src_byte_len; } /* assume we'll be able to copy the whole string */ size_t copy_len = src_byte_len; /* if there's not enough space for the whole string, limit it */ if (copy_len > dest_len) { /* copy no more than the output buffer length */ copy_len = dest_len; /* * If this splits a character into two pieces, back up until we're * at a character boundary - that is, the first byte of the next * chunk is a non-continuation character. */ while (copy_len > 0 && utf8_ptr::s_is_continuation(src.getptr() + copy_len)) --copy_len; /* * If that left us with zero bytes, just do the partial copy to * ensure we don't get stuck in a zero-at-a-time infinite loop. */ if (copy_len == 0) copy_len = dest_len; } /* if we have an output buffer, copy the data */ if (dest != 0) memcpy(dest, src.getptr(), copy_len); /* set the amount we copied, if the caller is interested */ if (src_bytes_used != 0) *src_bytes_used = copy_len; /* return the number of bytes we put in the destination buffer */ return copy_len; } /* * Map a null-terminated UTF-8 string */ size_t CCharmapToLocalUTF8::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { /* get the source length */ size_t src_len = strlen(src.getptr()); /* copy the bytes */ map_utf8(dest, dest_len, src, src_len, 0); /* * if there's room for the null terminator (which takes up just one * byte in UTF-8), add it */ if (dest_len > src_len) *(dest + src_len) = '\0'; /* * return the amount of space needed to copy the whole string -- * this is identical to the source length, since we don't make any * changes to it */ return src_len; } /* ------------------------------------------------------------------------ */ /* * Character mapper - Unicode to Single-byte */ /* * map a character */ size_t CCharmapToLocalSB::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* get the mapping */ size_t map_len; const unsigned char *mapping = get_xlation(unicode_char, &map_len); /* if we don't have room for one more character, abort */ if (*output_len < map_len) { *output_len = 0; return map_len; } /* copy the mapping */ memcpy(*output_ptr, mapping, map_len); /* increment the pointer by the number of characters we copied */ *output_ptr += map_len; /* adjust the remaining output length */ *output_len -= map_len; /* return the size of the result */ return map_len; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalSB::map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { /* remember where we started */ utf8_ptr src_start = src; /* compute where the source buffer ends */ char *srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ size_t cur_total; for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { const unsigned char *mapping; size_t map_len; /* get the mapping for this character */ mapping = get_xlation(src.getch(), &map_len); /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting */ } else if (map_len <= dest_len) { /* add the sequence */ memcpy(dest, mapping, map_len); /* adjust the output pointer and length remaining */ dest += map_len; dest_len -= map_len; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += map_len; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalSB::map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { const unsigned char *mapping; size_t map_len; /* get the mapping for this character */ mapping = get_xlation(src.getch(), &map_len); /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (map_len <= dest_len) { /* add the sequence */ memcpy(dest, mapping, map_len); /* adjust the output pointer and length remaining */ dest += map_len; dest_len -= map_len; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += map_len; } /* * add a null terminator, if there's room, but don't count it in the * output length */ if (dest_len > 0) *dest = '\0'; /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper - Unicode to 16-bit Wide Unicode local character set */ /* * map a character */ size_t CCharmapToLocalWideUnicode::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* if we don't have room for another wchar_t, abort */ if (*output_len < sizeof(wchar_t)) { *output_len = 0; return sizeof(wchar_t); } /* * Set the wide character to the unicode value, with no translation * - unicode is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). */ **(wchar_t **)output_ptr = unicode_char; /* increment the pointer by the size of a wide character */ ++(*(wchar_t **)output_ptr); /* return the size of the result */ return sizeof(wchar_t); } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalWideUnicode:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; wchar_t *destw; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* set up a wchar_t output pointer for convenience */ destw = (wchar_t *)dest; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting - don't store anything */ } else if (dest_len >= sizeof(wchar_t)) { /* add the sequence */ *destw++ = src.getch(); /* adjust the length remaining */ dest_len -= sizeof(wchar_t); } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += sizeof(wchar_t); } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalWideUnicode:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; wchar_t *destw; /* set up a wchar_t output pointer for convenience */ destw = (wchar_t *)dest; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= sizeof(wchar_t)) { /* add the sequence */ *destw++ = src.getch(); /* adjust the length remaining */ dest_len -= sizeof(wchar_t); } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += sizeof(wchar_t); } /* * if there's room for a null terminator character (not byte - we need * to add an entire wide character), add it, but don't count it in the * return length */ if (dest_len >= sizeof(wchar_t)) *destw = '\0'; /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, big-endian. Stores the * characters in big-endian UCS-2 representation. */ size_t CCharmapToLocalUcs2Big::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* * If we don't have room for another byte pair, abort. Note that we * really do want to store exactly two bytes, not sizeof(anything), * since we're storing to the UCS-2 file format, which encodes each * character in two bytes. */ if (*output_len < 2) { *output_len = 0; return 2; } /* * Store the big-endian 16-bit value with no translation - unicode * is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). * * Store the high-order 8 bits in the first byte, and the low-order * 8 bits in the second byte. */ **output_ptr = ((unicode_char >> 8) & 0xff); *(*output_ptr + 1) = (unicode_char & 0xff); /* skip two bytes in the output */ *output_ptr += 2; *output_len -= 2; /* return the size of the result */ return 2; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalUcs2Big:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're not storing anything */ } else if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = ((unicode_char >> 8) & 0xff); *dest++ = (unicode_char & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += 2; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalUcs2Big:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = ((unicode_char >> 8) & 0xff); *dest++ = (unicode_char & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += 2; } /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper for 16-bit Wide Unicode, little-endian. Stores the * characters in little-endian UCS-2 representation. */ size_t CCharmapToLocalUcs2Little::map(wchar_t unicode_char, char **output_ptr, size_t *output_len) const { /* * If we don't have room for another byte pair, abort. Note that we * really do want to store exactly two bytes, not sizeof(anything), * since we're storing to the UCS-2 file format, which encodes each * character in two bytes. */ if (*output_len < 2) { *output_len = 0; return 2; } /* * Store the little-endian 16-bit value with no translation - * unicode is the same everywhere. * * Note that the need to perform this trivial translation for this * character set is a secondary reason that this routine is virtual * (the primary reason is to handle the default ASCII translation). * * Store the low-order 8 bits in the first byte, and the high-order * 8 bits in the second byte. */ **output_ptr = (unicode_char & 0xff); *(*output_ptr + 1) = ((unicode_char >> 8) & 0xff); /* skip two bytes in the output */ *output_ptr += 2; *output_len -= 2; /* return the size of the result */ return 2; } /* * Map a UTF-8 string of known byte length to the local character set */ size_t CCharmapToLocalUcs2Little:: map_utf8(char *dest, size_t dest_len, utf8_ptr src, size_t src_byte_len, size_t *src_bytes_used) const { utf8_ptr src_start; size_t cur_total; char *srcend; /* remember where we started */ src_start = src; /* compute where the source buffer ends */ srcend = src.getptr() + src_byte_len; /* copy characters until we reach the end of the source string */ for (cur_total = 0 ; src.getptr() < srcend ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest == 0) { /* we're just counting - don't store anything */ } else if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = (unicode_char & 0xff); *dest++ = ((unicode_char >> 8) & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - stop now */ break; } /* count the length in the total */ cur_total += 2; } /* if the caller wants to know how much space we used, tell them */ if (src_bytes_used != 0) *src_bytes_used = src.getptr() - src_start.getptr(); /* return the total length of the result */ return cur_total; } /* * Map a null-terminated UTF-8 string to the local character set */ size_t CCharmapToLocalUcs2Little:: map_utf8z(char *dest, size_t dest_len, utf8_ptr src) const { size_t cur_total; /* copy characters until we find the terminating null */ for (cur_total = 0 ; src.getch() != 0 ; src.inc()) { /* * if we have room, add it; otherwise, zero the output length * remaining so we don't try to add anything more */ if (dest_len >= 2) { wchar_t unicode_char; /* get the current character */ unicode_char = src.getch(); /* add the sequence */ *dest++ = (unicode_char & 0xff); *dest++ = ((unicode_char >> 8) & 0xff); /* adjust the length remaining */ dest_len -= 2; } else { /* it doesn't fit - zero the output length remaining */ dest_len = 0; } /* count the length in the total */ cur_total += 2; } /* * if there's room for a null terminator character (which takes two * bytes in UCS-2), add it, but don't count it in the return length */ if (dest_len >= 2) { *dest++ = '\0'; *dest++ = '\0'; } /* return the total length of the result */ return cur_total; } /* ------------------------------------------------------------------------ */ /* * Character mapper - local to UTF-8 */ /* * create an appropriate mapping object for the given mapping file */ CCharmapToUni *CCharmapToUni::load(class CResLoader *res_loader, const char *table_name) { osfildef *fp; CCharmapToUni *mapper; charmap_type_t map_type; /* if they want a trivial UTF-8 translator, return one */ if (name_is_utf8_synonym(table_name)) return new CCharmapToUniUTF8(); /* if they want a 16-bit Unicode mapping, return one */ if (name_is_ucs2le_synonym(table_name)) return new CCharmapToUniUcs2Little(); if (name_is_ucs2be_synonym(table_name)) return new CCharmapToUniUcs2Big(); /* presume failure */ mapper = 0; /* open and characterize the mapping file */ fp = open_map_file_syn(res_loader, table_name, &map_type); /* check to make sure we opened a file */ if (fp == 0) { /* * if there was no file, and they want a plain ASCII translator, * return a default ASCII translator */ if (name_is_ascii_synonym(table_name)) return new CCharmapToUniASCII(); /* if they want an ISO-8859-1 translator, return a default one */ if (name_is_8859_1_synonym(table_name)) return new CCharmapToUni8859_1(); /* return failure */ return 0; } /* create an appropriate mapper */ switch(map_type) { case CHARMAP_TYPE_SB: /* create a single-byte mapper */ mapper = new CCharmapToUniSB(); break; case CHARMAP_TYPE_DB: /* create a double-byte mapper */ mapper = new CCharmapToUniDB(); break; case CHARMAP_TYPE_MB: /* create a mixed multi-byte mapper */ mapper = new CCharmapToUniMB(); break; default: /* other mapper types are currently unknown */ break; } /* if we successfully created a mapper, tell it to load the table */ if (mapper != 0) { /* load the table */ mapper->load_table(fp); } /* close the file */ osfcls(fp); /* return the mapper, if any */ return mapper; } /* * load a mapping table */ void CCharmapToUni::load_table(osfildef *fp) { uchar buf[256]; uint entry_cnt; /* read the header and the local table header */ if (osfrb(fp, buf, 6)) return; /* get the local table size from the local table header */ entry_cnt = osrp2(buf + 4); /* read the mappings */ while (entry_cnt > 0) { size_t cur; const uchar *p; /* figure out how many entries we can read this time */ cur = sizeof(buf)/4; if (cur > entry_cnt) cur = entry_cnt; /* read the entries */ if (osfrb(fp, buf, cur*4)) return; /* deduct this number from the remaining count */ entry_cnt -= cur; /* scan the entries */ for (p = buf ; cur > 0 ; p += 4, --cur) { /* map this entry */ set_mapping(osrp2(p), osrp2(p+2)); } } } /* * Map a null-terminated string into a buffer */ size_t CCharmapToUni::map_str(char *outbuf, size_t outbuflen, const char *input_str) { /* map the string to the output buffer */ size_t output_len = map(&outbuf, &outbuflen, input_str, strlen(input_str)); /* if there's space remaining in the output buffer, add a null byte */ if (outbuflen != 0) *outbuf = '\0'; /* return the number of bytes needed for the conversion */ return output_len; } /* * Map a counted-length string into a buffer */ size_t CCharmapToUni::map_str(char *outbuf, size_t outbuflen, const char *input_str, size_t input_len) { /* map the string to the output buffer and return the result */ return map(&outbuf, &outbuflen, input_str, input_len); } /* * Map a null-terminated string, allocating space for the result */ size_t CCharmapToUni::map_str_alo(char **outbuf, const char *input_str) { /* figure the space needed */ size_t outlen = map_str(0, 0, input_str); /* allocate the buffer */ *outbuf = (char *)t3malloc(outlen + 1); /* map the sring */ return map_str(*outbuf, outlen, input_str); } /* * Validate a buffer of utf-8 characters */ void CCharmapToUni::validate(char *buf, size_t len) { for ( ; len != 0 ; ++buf, --len) { /* check the type of the character */ char c = *buf; if ((c & 0x80) == 0) { /* 0..127 are one-byte characters, so this is valid */ } else if ((c & 0xC0) == 0x80) { /* * This byte has the pattern 10xxxxxx, which makes it a * continuation byte. Since we didn't just come from a * multi-byte intro byte, this is invalid. Change this byte to * '?'. */ *buf = '?'; } else if ((c & 0xE0) == 0xC0) { /* * This byte has the pattern 110xxxxx, which makes it the first * byte of a two-byte character sequence. The next byte must * have the pattern 10xxxxxx - if not, mark the current * character as invalid, since it's not part of a valid * sequence, and deal with the next byte separately. */ if (len > 1 && (*(buf+1) & 0xC0) == 0x80) { /* we have a valid two-byte sequence - skip it */ ++buf; --len; } else { /* * the next byte isn't a continuation, so the current byte * is invalid */ *buf = '?'; } } else { /* * This byte has the pattern 111xxxxx, which makes it the first * byte of a three-byte sequence. The next two bytes must be * marked as continuation bytes. */ if (len > 2 && (*(buf+1) & 0xC0) == 0x80 && (*(buf+2) & 0xC0) == 0x80) { /* we have a valid three-byte sequence - skip it */ buf += 2; len -= 2; } else { /* this is not a valid three-byte sequence */ *buf = '?'; } } } } /* ------------------------------------------------------------------------ */ /* * Basic single-byte character set to UTF-8 mapper */ /* * read from a single-byte file and translate to UTF-8 */ size_t CCharmapToUniSB_basic::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; /* * Compute how much to read from the file. The input file is * composed of single-byte characters, so only read up to one third * of the buffer length; this will ensure that we can always fit * what we read into the caller's buffer. */ inlen = bufl / 3; /* in any case, we can't read more than our own buffer size */ if (inlen > sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer, and return the result. We're * certain that the data will fit in the caller's buffer: we're * mapping only a third as many characters as we have bytes * available, and each character can take up at most three bytes, * hence the worst case is that we fill the buffer completely. * * On the other hand, we may only fill the buffer to a third of its * capacity, but this is okay too, since we're not required to give * the caller everything they asked for. */ return map(&buf, &bufl, inbuf_, inlen); } /* ------------------------------------------------------------------------ */ /* * Plain ASCII local to UTF-8 mapper */ /* * map a string from the single-byte local character set to UTF-8 */ size_t CCharmapToUniASCII::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte) in the input string */ for ( ; inlen > 0 ; --inlen, ++inp) { wchar_t uni; size_t csiz; /* * map any character outside of the 7-bit range to U+FFFD, the * Unicode REPLACEMENT CHARACTER, which is the standard way to * represent characters that can't be mapped from an incoming * character set */ if (((unsigned char)*inp) > 127) uni = 0xfffd; else uni = ((wchar_t)(unsigned char)*inp); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output length */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Single-byte mapped local to UTF-8 mapper */ /* * map a string from the single-byte local character set to UTF-8 */ size_t CCharmapToUniSB::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte) in the input string */ for ( ; inlen > 0 ; --inlen, ++inp) { wchar_t uni; size_t csiz; /* get the unicode mapping for this character */ uni = map_[(unsigned char)*inp]; /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Trivial UTF8-to-UTF8 input mapper */ /* * map a string */ size_t CCharmapToUniUTF8::map2(char **outp, size_t *outlen, const char *inp, size_t inlen, size_t *partial_len) const { size_t copy_len; /* * Make sure we copy only whole characters, by truncating the string * to a length that includes only whole characters. */ copy_len = utf8_ptr::s_trunc(inp, inlen); /* * note the length of any partial characters at the end of the buffer * for the caller - this is simply the difference between the original * length and the truncated copy length, since the truncation length * is simply the length excluding the partial last character bytes */ *partial_len = inlen - copy_len; /* limit the copying to what will fit in the output buffer */ if (copy_len > *outlen) { /* don't copy more than will fit, and don't copy partial characters */ copy_len = utf8_ptr::s_trunc(inp, *outlen); /* we don't have enough room, so set the output size to zero */ *outlen = 0; } else { /* we have room, so decrement the output size by the copy size */ *outlen -= copy_len; } /* copy the data, if we have an output buffer */ if (outp != 0) { /* copy the bytes */ memcpy(*outp, inp, copy_len); /* validate that the bytes we copied are well-formed UTF-8 */ validate(*outp, copy_len); /* advance the output pointer past the copied data */ *outp += copy_len; } /* * return the total input length -- the total output length is * always identical to the input length, because we don't change * anything */ return inlen; } /* * read a file */ size_t CCharmapToUniUTF8::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t read_len; char *last_start; size_t last_got_len; size_t last_need_len; /* * Read directly from the file, up the buffer size minus two bytes. * We want to leave two extra bytes so that we can read any extra * continuation bytes for the last character, in order to ensure * that we always read whole characters; in the worst case, the last * character could be three bytes long, in which case we'd need to * read two extra bytes. * * If the available buffer size is less than three bytes, just read * the number of bytes they asked for and don't bother trying to * keep continuation sequences intact. */ if (bufl < 3) { read_len = fp->readc(buf, bufl); validate(buf, read_len); return read_len; } /* * read up to the buffer size, less two bytes for possible * continuation bytes */ read_len = fp->readc(buf, bufl - 2); /* * if we didn't satisfy the entire request, we're at the end of the * file, so there's no point in trying to finish off any * continuation sequences - in this case, just return what we have */ if (read_len < bufl - 2) { validate(buf, read_len); return read_len; } /* * Check the last byte we read to see if there's another byte or two * following. * * If the last byte is a continuation byte, this is a bit trickier. * We must back up to the preceding lead byte to figure out what we * have in this case. */ last_start = &buf[read_len - 1]; last_got_len = 1; if (utf8_ptr::s_is_continuation(last_start)) { /* * if we only read one byte, simply return the one byte - we * started in the middle of a sequence, so there's no way we can * read a complete sequence */ if (read_len == 1) { validate(buf, read_len); return read_len; } /* back up to the byte we're continuing from */ --last_start; ++last_got_len; /* * if this is another continuation byte, we've reached the maximum * byte length of three for a single character, so there's no way * we could need to read anything more */ if (utf8_ptr::s_is_continuation(last_start)) { validate(buf, read_len); return read_len; } } /* * Okay: we have last_start pointing to the start of the last * character, and last_got_len the number of bytes we actually have for * that last character. If the needed length differs from the length * we actually have, we need to read more. */ last_need_len = utf8_ptr::s_charsize(*last_start); if (last_need_len > last_got_len) { /* * we need more than we actually read, so read the remaining * characters */ read_len += fp->readc(buf + read_len, last_need_len - last_got_len); } /* validate the buffer - ensure that it's well-formed UTF-8 */ validate(buf, read_len); /* return the length we read */ return read_len; } /* ------------------------------------------------------------------------ */ /* * Basic UCS-2 to UTF-8 mapper */ /* * Read from a file, translating to UTF-8 encoding */ size_t CCharmapToUniUcs2::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; /* * Compute how much to read from the file. The input file is composed * of two-byte characters, so only read up to two thirds of the buffer * length; this will ensure that we can always fit what we read into * the caller's buffer. * * Note that we divide by three first, then double the result, to * ensure that we read an even number of bytes. Each UCS-2 character * is represented in exactly two bytes, so we must always read pairs of * bytes to be sure we're reading whole characters. */ inlen = bufl / 3; inlen *= 2; /* in any case, we can't read more than our own buffer size */ if (inlen > sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer, and return the result. We're * certain that the data will fit in the caller's buffer: we're * mapping only a third as many characters as we have bytes * available, and each character can take up at most three bytes, * hence the worst case is that we fill the buffer completely. * * On the other hand, we may only fill the buffer to a third of its * capacity, but this is okay too, since we're not required to give * the caller everything they asked for. */ return map(&buf, &bufl, inbuf_, inlen); } /* ------------------------------------------------------------------------ */ /* * UCS-2 little-endian to UTF-8 mapper */ /* * map a string */ size_t CCharmapToUniUcs2Little::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte pair) in the input string */ for ( ; inlen > 1 ; inlen -= 2, inp += 2) { wchar_t uni; size_t csiz; /* * read the little-endian two-byte value - no mapping is * required, since UCS-2 uses the same code point assignments as * UTF-8 */ uni = ((wchar_t)(unsigned char)*inp) + (((wchar_t)(unsigned char)*(inp + 1)) << 8); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * UCS-2 big-endian to UTF-8 mapper */ /* * map a string */ size_t CCharmapToUniUcs2Big::map(char **outp, size_t *outlen, const char *inp, size_t inlen) const { size_t tot_outlen; /* we haven't written any characters to the output buffer yet */ tot_outlen = 0; /* scan each character (character == byte pair) in the input string */ for ( ; inlen > 1 ; inlen -= 2, inp += 2) { wchar_t uni; size_t csiz; /* * read the big-endian two-byte value - no mapping is required, * since UCS-2 uses the same code point assignments as UTF-8 */ uni = (((wchar_t)(unsigned char)*inp) << 8) + ((wchar_t)(unsigned char)*(inp + 1)); /* get the size of this character */ csiz = utf8_ptr::s_wchar_size(uni); /* add it to the total output lenght */ tot_outlen += csiz; /* if there's room, add it to our output buffer */ if (*outlen >= csiz) { /* write it out */ *outp += utf8_ptr::s_putch(*outp, uni); /* deduct it from the remaining output length */ *outlen -= csiz; } else { /* there's no room - set the remaining output length to zero */ *outlen = 0; } } /* return the total output length */ return tot_outlen; } /* ------------------------------------------------------------------------ */ /* * Multi-byte character set translation to Unicode */ /* * construct the mapper */ CCharmapToUniMB::CCharmapToUniMB() { int i; cmap_mb_entry *p; /* clear out the mapping table */ for (i = 0, p = map_ ; i < 256 ; ++i, ++p) { /* assume this lead byte won't have a sub-table */ p->sub = 0; /* * we don't have a mapping for this lead byte yet, so use U+FFFD * (the Unicode REPLACEMENT CHARACTER) as the default mapping in * case we never assign it any other mapping */ p->ch = 0xFFFD; } } /* * delete the mapper */ CCharmapToUniMB::~CCharmapToUniMB() { int i; cmap_mb_entry *p; /* delete all of our sub-tables */ for (i = 0, p = map_ ; i < 256 ; ++i, ++p) { /* if this sub-table was allocated, delete it */ if (p->sub != 0) t3free(p->sub); } } /* * Set a mapping */ void CCharmapToUniMB::set_mapping(wchar_t uni_code_pt, wchar_t local_code_pt) { /* * Check to see if it's a one-byte or two-byte mapping. If the local * code point is in the range 0-255, it's a one-byte character; * otherwise, it's a two-byte character. */ if (local_code_pt <= 255) { /* it's a single-byte character, so simply set the mapping */ map_[(unsigned char)local_code_pt].ch = uni_code_pt; } else { cmap_mb_entry *entp; wchar_t *subp; /* * Get the mapping table entry for the lead byte. The lead byte of * the local code point is given by the high-order byte of the * local code point. (Note that this doesn't have anything to do * with the endian-ness of the local platform. The generic Unicode * mapping tables are specifically designed this way, independently * of endian-ness.) */ entp = &map_[(unsigned char)((local_code_pt >> 8) & 0xff)]; /* * It's a two-byte character. The high-order byte is the lead * byte, and the low-order byte is the trailing byte of the * two-byte sequence. * * If we haven't previously set up a sub-table for the lead byte, * do so now. */ if ((subp = entp->sub) == 0) { size_t i; wchar_t *p; /* allocate a new sub-mapping table for the lead byte */ subp = entp->sub = (wchar_t *)t3malloc(256 * sizeof(wchar_t)); /* initialize each entry to U+FFFD, in case we never map them */ for (i = 256, p = subp ; i != 0 ; --i, *p++ = 0xFFFD) ; } /* set the mapping in the sub-table for the second byte */ subp[(unsigned char)(local_code_pt & 0xff)] = uni_code_pt; } } /* * map a string, providing partial character info */ size_t CCharmapToUniMB::map2(char **output_ptr, size_t *output_buf_len, const char *input_ptr, size_t input_len, size_t *partial_len) const { size_t needed_out_len; /* presume we won't have a partial last character */ *partial_len = 0; /* we haven't found anything to store in the output yet */ needed_out_len = 0; /* keep going until we've mapped each character */ while (input_len != 0) { unsigned char c; const cmap_mb_entry *entp; wchar_t wc; size_t wlen; /* get the lead byte of the next input character */ c = *input_ptr; /* get the primary mapping table entry for the lead byte */ entp = &map_[c]; /* check for a one-byte or two-byte mapping */ if (entp->sub == 0) { /* it's a one-byte character - get the mapping */ wc = entp->ch; /* skip the single byte of input */ ++input_ptr; --input_len; } else { /* * it's a two-byte character lead byte - make sure we have a * complete input character */ if (input_len < 2) { /* we have an incomplete last character - tell the caller */ *partial_len = 1; /* we're done mapping it */ break; } /* get the second byte of the sequence */ c = input_ptr[1]; /* get the translation from the sub-table */ wc = entp->sub[c]; /* skip the two-byte sequence */ input_ptr += 2; input_len -= 2; } /* we have the translation - note its stored UTF-8 byte size */ wlen = utf8_ptr::s_wchar_size(wc); /* check for room to store the output character */ if (wlen > *output_buf_len) { /* * there's no room to store this character - zero out the * output buffer length so that we know not to try storing * anything else in the buffer */ *output_buf_len = 0; } else { /* there's room - store it */ wlen = utf8_ptr::s_putch(*output_ptr, wc); /* consume output buffer space */ *output_ptr += wlen; *output_buf_len -= wlen; } /* count the needed length, whether we stored it or not */ needed_out_len += wlen; } /* return the required output length */ return needed_out_len; } /* * read from a multi-byte input file, translating to UTF-8 */ size_t CCharmapToUniMB::read_file(CVmDataSource *fp, char *buf, size_t bufl) { size_t inlen; size_t outlen; size_t partial; /* * Compute how much to read from the file. The input file is composed * of one-byte or two-byte characters, so only read up to one-third of * the caller's buffer length; this will ensure that in the worst case * we can always fit what we read into the caller's buffer. (The worst * case is that the input is entirely single-byte local characters that * translate into three-byte UTF-8 characters.) */ inlen = bufl / 3; /* in any case, we can't read more than our own buffer size */ if (inlen >= sizeof(inbuf_)) inlen = sizeof(inbuf_); /* read raw bytes from the file */ inlen = fp->readc(inbuf_, inlen); /* * Map data to the caller's buffer. Note if we have a partial * character at the end of the buffer (i.e., the last byte of the * buffer is a lead byte that requires a second byte to make up a * complete two-byte local character), so that we can read an * additional byte to complete the two-byte final character if * necessary. */ outlen = map2(&buf, &bufl, inbuf_, inlen, &partial); /* * if we have a partial trailing character, read the other half of the * final character */ if (partial != 0) { /* move the lead byte to the start of our buffer */ inbuf_[0] = inbuf_[inlen - 1]; /* read the extra byte to form a complete character */ inlen = 1 + fp->readc(inbuf_ + 1, 1); /* if we got the second byte, map the complete final character */ if (inlen == 2) outlen += map(&buf, &bufl, inbuf_, inlen); } /* return the result length */ return outlen; } frobtads-1.2.3/tads3/vmpat.h0000644000175000001440000001626511774044132015037 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpat.h - regular-expression compiled pattern object Function Encapsulates a compiled regular expression as an object. This object allows a compiled regular expression to be saved for re-use; since regex compilation is time-consuming, it's much more efficient to re-use a compiled pattern in repeated searches than to recompile the expression each time it's needed. Notes Modified 08/27/02 MJRoberts - Creation */ #ifndef VMPAT_H #define VMPAT_H #include #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmregex.h" /* ------------------------------------------------------------------------ */ /* * Our serialized data stream, in both the image file and a saved file, * consists of: * * DATAHOLDER src_val * * 'src_val' is the source value - this is the string that was compiled to * create the pattern. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension consists of a simple structure with a pointer * to the compiled pattern data (the re_compiled_pattern structure) and * the original string value that was used to create the pattern (we hold * onto the original string mostly for debugging purposes). */ struct vmobj_pat_ext { /* the compiled pattern data */ re_compiled_pattern *pat; /* the original pattern source string */ vm_val_t str; }; /* ------------------------------------------------------------------------ */ /* * Pattern intrinsic class */ class CVmObjPattern: public CVmObject { friend class CVmMetaclassPattern; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a pattern object? */ static int is_pattern_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* defer to our base class */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to string - return the original pattern string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* return the original string value */ *new_str = *get_orig_str(); return new_str->get_as_string(vmg0_); } /* undo operations - we are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint); /* remove stale weak references - we have no weak references */ void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* perform post-load initialization */ void post_load_init(VMG_ vm_obj_id_t self); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* get my compiled pattern */ re_compiled_pattern *get_pattern(VMG0_) { return get_ext()->pat; } /* get my string object */ const vm_val_t *get_orig_str() const { return &get_ext()->str; } protected: /* create with no extension */ CVmObjPattern() { ext_ = 0; } /* create with a given pattern object and source string value */ CVmObjPattern(VMG_ re_compiled_pattern *pat, const vm_val_t *src_str); /* set my compiled pattern data structure */ void set_pattern(re_compiled_pattern *pat) { get_ext()->pat = pat; } /* set my original source string value */ void set_orig_str(const vm_val_t *val) { get_ext()->str = *val; } /* get my extension data */ vmobj_pat_ext *get_ext() const { return (vmobj_pat_ext *)ext_; } /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get my original string */ int getp_get_str(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjPattern::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassPattern: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "regex-pattern/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjPattern(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjPattern(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjPattern::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjPattern::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMPAT_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjPattern) frobtads-1.2.3/tads3/vmconhmp.cpp0000644000175000001440000017655412145504453016102 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconhmp.cpp - T3 VM Console - HTML mini-parser Function This is the console HTML "mini-parser," which provides some minimal interpretation of HTML markups for text-only underlying output streams. Note that the mini-parser is included even in full HTML-enabled versions of TADS (such as HTML TADS or HyperTADS), because we still need text-only interpretation of HTML markups for log-file targets. Notes Modified 06/08/02 MJRoberts - Creation */ #include #include #include #include #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "utf8.h" #include "vmuni.h" #include "vmconsol.h" #include "vmglob.h" #include "charmap.h" #include "vmhash.h" /* ------------------------------------------------------------------------ */ /* * Horizontal Tab object. We use these objects to record tab stops * defined with in our HTML mini-parser. * * This is a hash table entry subclass because we use a hash table of tab * entries, keyed by ID strings. Note that we use a case-sensitive hash * table entry - clients must convert ID's to lower-case if they don't * want case sensitivity. * * Note that this code looks a bit cavalier with unicode vs ASCII, but * this is actually safe. The underlying hash table code uses * counted-length strings, so it doesn't care about embedded null bytes. * We therefore can pass a wchar_t buffer to the underlying hash table, * even though the underlying hash table uses char's, because we multiply * our length by the size of a wchar_t to get the byte length. */ class CVmFmtTabStop: public CVmHashEntryCS { public: CVmFmtTabStop(const char *id, size_t idlen) : CVmHashEntryCS(id, idlen, TRUE) { /* we don't know our column yet */ column_ = 0; } /* the column where the tab is defined */ int column_; /* * Find a tabstop object with a given ID, optionally creating a new * object if an existing one isn't found. */ static CVmFmtTabStop *find(CVmHashTable *hashtab, wchar_t *id, int create) { /* * Get the length - in characters AND in bytes - of the new ID. * We mostly work with the new ID in bytes rather than wchar_t's, * because the underlying hash table does everything in bytes. */ /* * Convert the new ID to folded case. The hash table doesn't deal * with unicode case conversions, so we use a case-sensitive hash * table and simply make sure we only give it lower-case * characters. */ size_t flen = fold_case(0, id); wchar_t *fid = 0; if (flen != 0) fid = new wchar_t[flen+1]; CVmFmtTabStop *entry = 0; err_try { /* do the folding if applicable */ if (fid != 0) { fold_case(fid, id); id = fid; } /* get the folded ID length in characters and bytes */ size_t id_chars = wcslen(id); size_t id_bytes = id_chars * sizeof(wchar_t); /* look for an existing tab-stop entry with the same ID */ entry = (CVmFmtTabStop *)hashtab->find((char *)id, id_bytes); /* if we didn't find it, and they want to create it, do so */ if (entry == 0 && create) hashtab->add(entry = new CVmFmtTabStop((char *)id, id_bytes)); } err_finally { /* if we allocated the ID string, release it */ if (fid != 0) delete [] fid; } err_end; /* return what we found */ return entry; } /* get the case folding of a string */ static size_t fold_case(wchar_t *dst, const wchar_t *str) { size_t dstlen = 0; for ( ; *str != 0 ; ++str) { /* * get the case folding of the current character, defaulting to * an identity mapping if there's no folding defined */ const wchar_t *f = t3_to_fold(*str); wchar_t fi[2] = { *str, 0 }; if (f == 0) f = fi; /* copy it to the output if there's an output buffer */ for ( ; *f != 0 ; ++f, ++dstlen) { if (dst != 0) *dst++ = *f; } } /* add the null */ if (dst != 0) *dst = 0; /* return the length (excluding the null) */ return dstlen; } }; /* ------------------------------------------------------------------------ */ /* * HTML entity mapping table. When we're in non-HTML mode, we keep our own * expansion table so that we can map HTML entity names into the local * character set. * * The entries in this table must be in sorted order (by HTML entity name), * because we use a binary search to find an entity name in the table. */ /* * '&' character name definition structure */ struct amp_tbl_t { /* entity name */ const char *cname; /* HTML Unicode character value */ wchar_t html_cval; }; /* * table of '&' character name sequences */ static const struct amp_tbl_t amp_tbl[] = { { "AElig", 198 }, { "Aacute", 193 }, { "Abreve", 258 }, { "Acirc", 194 }, { "Agrave", 192 }, { "Alpha", 913 }, { "Aogon", 260 }, { "Aring", 197 }, { "Atilde", 195 }, { "Auml", 196 }, { "Beta", 914 }, { "Cacute", 262 }, { "Ccaron", 268 }, { "Ccedil", 199 }, { "Chi", 935 }, { "Dagger", 8225 }, { "Dcaron", 270 }, { "Delta", 916 }, { "Dstrok", 272 }, { "ETH", 208 }, { "Eacute", 201 }, { "Ecaron", 282 }, { "Ecirc", 202 }, { "Egrave", 200 }, { "Eogon", 280 }, { "Epsilon", 917 }, { "Eta", 919 }, { "Euml", 203 }, { "Gamma", 915 }, { "Iacute", 205 }, { "Icirc", 206 }, { "Igrave", 204 }, { "Iota", 921 }, { "Iuml", 207 }, { "Kappa", 922 }, { "Lacute", 313 }, { "Lambda", 923 }, { "Lcaron", 317 }, { "Lstrok", 321 }, { "Mu", 924 }, { "Nacute", 323 }, { "Ncaron", 327 }, { "Ntilde", 209 }, { "Nu", 925 }, { "OElig", 338 }, { "Oacute", 211 }, { "Ocirc", 212 }, { "Odblac", 336 }, { "Ograve", 210 }, { "Omega", 937 }, { "Omicron", 927 }, { "Oslash", 216 }, { "Otilde", 213 }, { "Ouml", 214 }, { "Phi", 934 }, { "Pi", 928 }, { "Prime", 8243 }, { "Psi", 936 }, { "Racute", 340 }, { "Rcaron", 344 }, { "Rho", 929 }, { "Sacute", 346 }, { "Scaron", 352 }, { "Scedil", 350 }, { "Sigma", 931 }, { "THORN", 222 }, { "Tau", 932 }, { "Tcaron", 356 }, { "Tcedil", 354 }, { "Theta", 920 }, { "Uacute", 218 }, { "Ucirc", 219 }, { "Udblac", 368 }, { "Ugrave", 217 }, { "Upsilon", 933 }, { "Uring", 366 }, { "Uuml", 220 }, { "Xi", 926 }, { "Yacute", 221 }, { "Yuml", 376 }, { "Zacute", 377 }, { "Zcaron", 381 }, { "Zdot", 379 }, { "Zeta", 918 }, { "aacute", 225 }, { "abreve", 259 }, { "acirc", 226 }, { "acute", 180 }, { "aelig", 230 }, { "agrave", 224 }, { "alefsym", 8501 }, { "alpha", 945 }, { "amp", '&' }, { "and", 8743 }, { "ang", 8736 }, { "aogon", 261 }, { "aring", 229 }, { "asymp", 8776 }, { "atilde", 227 }, { "auml", 228 }, { "bdquo", 8222 }, { "beta", 946 }, { "breve", 728 }, { "brvbar", 166 }, { "bull", 8226 }, { "cacute", 263 }, { "cap", 8745 }, { "caron", 711 }, { "ccaron", 269 }, { "ccedil", 231 }, { "cedil", 184 }, { "cent", 162 }, { "chi", 967 }, { "circ", 710 }, { "clubs", 9827 }, { "cong", 8773 }, { "copy", 169 }, { "crarr", 8629 }, { "cup", 8746 }, { "curren", 164 }, { "dArr", 8659 }, { "dagger", 8224 }, { "darr", 8595 }, { "dblac", 733 }, { "dcaron", 271 }, { "deg", 176 }, { "delta", 948 }, { "diams", 9830 }, { "divide", 247 }, { "dot", 729 }, { "dstrok", 273 }, { "eacute", 233 }, { "ecaron", 283 }, { "ecirc", 234 }, { "egrave", 232 }, { "emdash", 8212 }, { "empty", 8709 }, { "emsp", 0x2003 }, { "endash", 8211 }, { "ensp", 0x2002 }, { "eogon", 281 }, { "epsilon", 949 }, { "equiv", 8801 }, { "eta", 951 }, { "eth", 240 }, { "euml", 235 }, { "exist", 8707 }, { "figsp", 0x2007 }, { "fnof", 402 }, { "forall", 8704 }, { "fpmsp", 0x2005 }, { "frac12", 189 }, { "frac14", 188 }, { "frac34", 190 }, { "frasl", 8260 }, { "gamma", 947 }, { "ge", 8805 }, { "gt", '>' }, { "hArr", 8660 }, { "hairsp", 0x200a }, { "harr", 8596 }, { "hearts", 9829 }, { "hellip", 8230 }, { "iacute", 237 }, { "icirc", 238 }, { "iexcl", 161 }, { "igrave", 236 }, { "image", 8465 }, { "infin", 8734 }, { "int", 8747 }, { "iota", 953 }, { "iquest", 191 }, { "isin", 8712 }, { "iuml", 239 }, { "kappa", 954 }, { "lArr", 8656 }, { "lacute", 314 }, { "lambda", 955 }, { "lang", 9001 }, { "laquo", 171 }, { "larr", 8592 }, { "lcaron", 318 }, { "lceil", 8968 }, { "ldq", 8220 }, { "ldquo", 8220 }, { "le", 8804 }, { "lfloor", 8970 }, { "lowast", 8727 }, { "loz", 9674 }, { "lsaquo", 8249 }, { "lsq", 8216 }, { "lsquo", 8216 }, { "lstrok", 322 }, { "lt", '<' }, { "macr", 175 }, { "mdash", 8212 }, { "micro", 181 }, { "middot", 183 }, { "minus", 8722 }, { "mu", 956 }, { "nabla", 8711 }, { "nacute", 324 }, { "nbsp", 0x00A0 }, /* == 160 decimal */ { "ncaron", 328 }, { "ndash", 8211 }, { "ne", 8800 }, { "ni", 8715 }, { "not", 172 }, { "notin", 8713 }, { "nsub", 8836 }, { "ntilde", 241 }, { "nu", 957 }, { "oacute", 243 }, { "ocirc", 244 }, { "odblac", 337 }, { "oelig", 339 }, { "ogon", 731 }, { "ograve", 242 }, { "oline", 8254 }, { "omega", 969 }, { "omicron", 959 }, { "oplus", 8853 }, { "or", 8744 }, { "ordf", 170 }, { "ordm", 186 }, { "oslash", 248 }, { "otilde", 245 }, { "otimes", 8855 }, { "ouml", 246 }, { "para", 182 }, { "part", 8706 }, { "permil", 8240 }, { "perp", 8869 }, { "phi", 966 }, { "pi", 960 }, { "piv", 982 }, { "plusmn", 177 }, { "pound", 163 }, { "prime", 8242 }, { "prod", 8719 }, { "prop", 8733 }, { "psi", 968 }, { "puncsp", 0x2008 }, { "quot", '"' }, { "rArr", 8658 }, { "racute", 341 }, { "radic", 8730 }, { "rang", 9002 }, { "raquo", 187 }, { "rarr", 8594 }, { "rcaron", 345 }, { "rceil", 8969 }, { "rdq", 8221 }, { "rdquo", 8221 }, { "real", 8476 }, { "reg", 174 }, { "rfloor", 8971 }, { "rho", 961 }, { "rsaquo", 8250 }, { "rsq", 8217 }, { "rsquo", 8217 }, { "sacute", 347 }, { "sbquo", 8218 }, { "scaron", 353 }, { "scedil", 351 }, { "sdot", 8901 }, { "sect", 167 }, { "shy", 173 }, { "sigma", 963 }, { "sigmaf", 962 }, { "sim", 8764 }, { "spades", 9824 }, { "spmsp", 0x2006 }, { "sub", 8834 }, { "sube", 8838 }, { "sum", 8721 }, { "sup", 8835 }, { "sup1", 185 }, { "sup2", 178 }, { "sup3", 179 }, { "supe", 8839 }, { "szlig", 223 }, { "tau", 964 }, { "tcaron", 357 }, { "tcedil", 355 }, { "there4", 8756 }, { "theta", 952 }, { "thetasym", 977 }, { "thinsp", 0x2009 }, { "thorn", 254 }, { "tilde", 732 }, { "times", 215 }, { "tpmsp", 0x2004 }, { "trade", 8482 }, { "uArr", 8657 }, { "uacute", 250 }, { "uarr", 8593 }, { "ucirc", 251 }, { "udblac", 369 }, { "ugrave", 249 }, { "uml", 168 }, { "upsih", 978 }, { "upsilon", 965 }, { "uring", 367 }, { "uuml", 252 }, { "weierp", 8472 }, { "xi", 958 }, { "yacute", 253 }, { "yen", 165 }, { "yuml", 255 }, { "zacute", 378 }, { "zcaron", 382 }, { "zdot", 380 }, { "zeta", 950 }, { "zwnbsp", 0xfeff }, { "zwsp", 0x200b } }; /* * Color names. For text-mode interpreters, we parse certain tag * attributes that can specify colors, so we must recognize the basic set * of HTML color names for these attributes. */ struct color_tbl_t { /* name of the color */ const wchar_t *name; /* osifc color code */ os_color_t color_code; }; static const color_tbl_t color_tbl[] = { { L"black", os_rgb_color(0x00, 0x00, 0x00) }, { L"white", os_rgb_color(0xFF, 0xFF, 0xFF) }, { L"red", os_rgb_color(0xFF, 0x00, 0x00) }, { L"blue", os_rgb_color(0x00, 0x00, 0xFF) }, { L"green", os_rgb_color(0x00, 0x80, 0x00) }, { L"yellow", os_rgb_color(0xFF, 0xFF, 0x00) }, { L"cyan", os_rgb_color(0x00, 0xFF, 0xFF) }, { L"aqua", os_rgb_color(0x00, 0xFF, 0xFF) }, { L"magenta", os_rgb_color(0xFF, 0x00, 0xFF) }, { L"silver", os_rgb_color(0xC0, 0xC0, 0xC0) }, { L"gray", os_rgb_color(0x80, 0x80, 0x80) }, { L"maroon", os_rgb_color(0x80, 0x00, 0x00) }, { L"purple", os_rgb_color(0x80, 0x00, 0x80) }, { L"fuchsia", os_rgb_color(0xFF, 0x00, 0xFF) }, { L"lime", os_rgb_color(0x00, 0xFF, 0x00) }, { L"olive", os_rgb_color(0x80, 0x80, 0x00) }, { L"navy", os_rgb_color(0x00, 0x00, 0x80) }, { L"teal", os_rgb_color(0x00, 0x80, 0x80) }, /* the parameterized colors */ { L"text", OS_COLOR_P_TEXT }, { L"bgcolor", OS_COLOR_P_TEXTBG }, { L"statustext", OS_COLOR_P_STATUSLINE }, { L"statusbg", OS_COLOR_P_STATUSBG }, { L"input", OS_COLOR_P_INPUT }, /* end-of-table marker */ { 0, 0 } }; /* ------------------------------------------------------------------------ */ /* * Parse the COLOR attribute of a FONT tag. Sets *result to the parsed * color value. Returns true if we found a valid value, false if not. */ int CVmFormatter::parse_color_attr(VMG_ const wchar_t *val, os_color_t *result) { const color_tbl_t *p; /* * if the value starts with '#', it's a hex RGB value; otherwise, it's * a color name */ if (*val == '#') { unsigned long hexval; /* parse the value as a hex number */ for (hexval = 0, ++val ; *val != '\0' && is_xdigit(*val) ; ++val) { /* add this digit */ hexval <<= 4; hexval += (t3_is_digit(*val) ? *val - '0' : *val >= 'a' && *val <= 'f' ? *val - 'a' + 0x0A : *val - 'A' + 0x0A); } /* * os_color_t uses the same encoding as HTML, so just return the * value as given (ensuring that it doesn't overflow the 24 bits * assigned to our RGB encoding) */ *result = (os_color_t)(hexval & 0x00FFFFFF); return TRUE; } else { /* scan the color table for a match for the name */ for (p = color_tbl ; p->name != 0 ; ++p) { /* if the name matches, use it */ if (CVmCaseFoldStr::wstreq(val, p->name)) { /* this is the color - make it the current buffer color */ *result = p->color_code; /* indicate that we found a valid color value */ return TRUE; } } } /* indicate that we didn't find a valid color value */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Find a tab */ CVmFmtTabStop *CVmFormatter::find_tab(wchar_t *id, int create) { /* if we don't have a hash table yet, create one */ if (tabs_ == 0) tabs_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* find the tab */ return CVmFmtTabStop::find(tabs_, id, create); } /* ------------------------------------------------------------------------ */ /* * Expand the pending tab. */ void CVmFormatter::expand_pending_tab(VMG_ int allow_anon) { int txtlen; int spaces; int i; int col; /* check the alignment */ switch(pending_tab_align_) { case VMFMT_TAB_LEFT: /* * left-aligned tabs are never pending, so this should never * happen */ assert(FALSE); return; case VMFMT_TAB_NONE: /* there's no pending tab, so there's nothing to do */ return; case VMFMT_TAB_RIGHT: /* * It's a right-aligned tab. If we have a TO, expand the tab with * enough spaces to push the text up to the defined tab's column; * otherwise, insert spaces to push the text to the full line * width. */ if (pending_tab_entry_ != 0) { /* * if we're not past the tab, insert enough spaces to get us * there; if we're already past the tab, insert nothing */ if (pending_tab_entry_->column_ > linecol_) spaces = pending_tab_entry_->column_ - linecol_; else spaces = 0; } else if (allow_anon) { /* push the text to the full line width */ spaces = console_->get_line_width() - 1 - linecol_; } else { /* it's anonymous, but these aren't allowed - ignore it */ spaces = 0; } break; case VMFMT_TAB_CENTER: /* * It's a center-aligned tab. If we have a TO, expand the tab * with enough spaces to center the text on the defined tab's * column; otherwise, center the text between the tab's starting * column and the full line width. */ txtlen = linecol_ - pending_tab_start_; if (pending_tab_entry_ != 0) { int startcol; /* * find the column where the text would have to start for the * text to be centered over the tab's column */ startcol = pending_tab_entry_->column_ - (txtlen / 2); /* * if that's past the starting column, insert enough spaces to * get us there; otherwise, do nothing, as we'd have to start * to the left of the leftmost place we can put the text */ if (startcol > pending_tab_start_) spaces = startcol - pending_tab_start_; else spaces = 0; } else if (allow_anon) { /* center the text in the remaining space to the line width */ spaces = ((console_->get_line_width() - pending_tab_start_) - txtlen) / 2; } else { /* it's anonymous, but these aren't allowed - ignore it */ spaces = 0; } break; case VMFMT_TAB_DECIMAL: /* * Decimal tab. Scan the text after the tab, looking for the * first occurrence of the decimal point character. */ for (col = pending_tab_start_, i = linepos_ - (linecol_ - col) ; i < linepos_ ; ++i, ++col) { /* if this is the decimal point character, stop looking */ if (linebuf_[i] == pending_tab_dp_) break; } /* * insert enough spaces to put the decimal point character in the * defined tab's column; if the decimal point character is already * past the tab's column, do nothing */ if (pending_tab_entry_->column_ > col) spaces = pending_tab_entry_->column_ - col; else spaces = 0; break; } /* insert the spaces into the buffer where the appeared */ if (spaces > 0) { /* * Calculate the index in the buffer of the insertion point. To * do this, start with linepos_, which the current output index in * the buffer, and subtract the difference between linecol_ and * the tab starting column, since this will give us the number of * characters in the buffer since the tab insertion point. */ i = linepos_ - (linecol_ - pending_tab_start_); /* * open up the line buffer and line color buffer by the number of * spaces we're adding */ memmove(linebuf_ + i + spaces, linebuf_ + i, (linepos_ - i + 1)*sizeof(linebuf_[0])); memmove(colorbuf_ + i + spaces, colorbuf_ + i, (linepos_ - i + 1)*sizeof(colorbuf_[0])); /* advance the line and column positions for the insertion */ linecol_ += spaces; linepos_ += spaces; /* insert the spaces */ for ( ; spaces != 0 ; --spaces, ++i) { /* insert a space of the same color active at the */ linebuf_[i] = ' '; colorbuf_[i] = pending_tab_color_; } } /* we've handled the pending tab, so we can forget about it */ pending_tab_align_ = VMFMT_TAB_NONE; } /* ------------------------------------------------------------------------ */ /* * Resume text-only HTML mini-parser. This is called when we start writing * a new string and discover that we're parsing inside an HTML tag in our * mini-parser. * * Returns the next character after the run of text we parse. */ wchar_t CVmFormatter::resume_html_parsing(VMG_ wchar_t c, const char **sp, size_t *slenp) { /* keep going until we run out of characters */ while (c != L'\0') { /* * If we're parsing HTML here, and we're inside a tag, skip * characters until we reach the end of the tag. */ if (html_parse_state_ != VMCON_HPS_NORMAL) { wchar_t *dst; /* check our parsing mode */ switch(html_parse_state_) { case VMCON_HPS_TAG: /* * keep skipping up to the closing '>', but note when we * enter any quoted section */ switch(c) { case '>': /* we've reached the end of the tag */ html_parse_state_ = VMCON_HPS_NORMAL; /* if we have a deferred
, process it now */ switch(html_defer_br_) { case HTML_DEFER_BR_NONE: /* no deferred
*/ break; case HTML_DEFER_BR_FLUSH: flush(vmg_ VM_NL_NEWLINE); break; case HTML_DEFER_BR_BLANK: write_blank_line(vmg0_); break; } /* no more deferred
pending */ html_defer_br_ = HTML_DEFER_BR_NONE; /* if we're parsing a tag, finish it */ if (html_in_tab_) { /* check the attributes of the */ if (new_tab_entry_ != 0 || new_tab_align_ != VMFMT_TAB_NONE) { /* * We have either a , maybe with an * ALIGN attribute, or at least a . * * If there's no TO, then we use the default * treatment based on the alignment: for * ALIGN=RIGHT, the text up to the end of the * line is aligned at the right edge of the * display; for ALIGN=CENTER, it's centered * within the margins. For other alignments, * a TAB without a TO attribute has no effect. * * Check for ALIGN without TO, and ignore it * if it's not CENTER or RIGHT alignment. */ if (new_tab_entry_ == 0 && new_tab_align_ != VMFMT_TAB_RIGHT && new_tab_align_ != VMFMT_TAB_CENTER) { /* * meaningless alignment for without * a TO attribute - ignore the tab */ } else if (new_tab_align_ == VMFMT_TAB_LEFT || new_tab_align_ == VMFMT_TAB_NONE) { int delta; /* * This is a left-aligned tab (explicitly * or implicitly: if no alignment is * specified, default to left alignment). * This is easy: just insert spaces to the * desired column. */ delta = new_tab_entry_->column_ - linecol_; if (delta > 0) write_tab(vmg_ delta, 0); } else { /* * For other alignments, we won't know how * much space to insert until we find the * the next or the end of the line. * * Remember the starting column of the * pending tab. When we reach another * , or we reach the end of the line, * we'll insert spaces at this point as * needed. Remember the current color, so * we can use the color active at the * tag when we insert the spaces * expanding the tab. * * Also remember the characteristics * specified in the tag (alignment, * decimal-point alignment character), so * we'll be able to apply the desired * characteristics when we expand the tab. */ pending_tab_start_ = linecol_; pending_tab_color_ = cur_color_; pending_tab_entry_ = new_tab_entry_; pending_tab_align_ = new_tab_align_; pending_tab_dp_ = new_tab_dp_; /* default to '.' as the DP aligner */ if (pending_tab_align_ == VMFMT_TAB_DECIMAL && pending_tab_dp_ == L'\0') pending_tab_dp_ = L'.'; } } else { /* * it's just a with no attributes - * default to , so that it * behaves like a simple '\t' */ write_tab(vmg_ 0, 4); } } /* we're not parsing inside a tag any more */ html_allow_alt_ = FALSE; html_allow_color_ = FALSE; html_in_body_ = FALSE; html_in_tab_ = FALSE; html_in_wrap_ = FALSE; /* done */ break; case '"': /* enter a double-quoted string */ html_parse_state_ = VMCON_HPS_DQUOTE; break; case '\'': /* enter a single-quoted string */ html_parse_state_ = VMCON_HPS_SQUOTE; break; default: /* if it's alphabetic, start an attribute name */ if (t3_is_alpha(c)) { /* note that we're parsing an attribute name */ html_parse_state_ = VMCON_HPS_ATTR_NAME; /* empty the buffer */ attrname_[0] = L'\0'; /* go back and re-read it in the new mode */ continue; } break; } break; case VMCON_HPS_DQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '"') html_parse_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_SQUOTE: /* if we've reached the closing quote, return to tag state */ if (c == '\'') html_parse_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_ATTR_NAME: /* * parsing an attribute name; resume at the last character * of the buffer so far */ dst = attrname_ + wcslen(attrname_); /* add characters to the attribute name */ while (t3_is_alpha(c) || t3_is_digit(c)) { /* store this character if there's room */ if (dst + 1 < attrname_ + CVFMT_MAX_ATTR_NAME) *dst++ = c; /* get the next character */ c = next_wchar(sp, slenp); } /* null-terminate the result so far */ *dst++ = '\0'; /* * if we've reached the end of the string, stop here, * staying in the same mode */ if (c == '\0') break; /* skip any whitespace */ while (t3_is_whitespace(c)) c = next_wchar(sp, slenp); /* if we found an '=', switch to parsing the value */ if (c == '=') { /* switch to value mode */ html_parse_state_ = VMCON_HPS_ATTR_VAL; /* empty the value buffer */ attrval_[0] = L'\0'; attr_qu_ = L'\0'; /* proceed to the next character */ break; } else { /* * There's no value explicitly given, so the value is * implicitly the same as the attribute name. */ wcscpy(attrval_, attrname_); /* this takes us back to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* go process the implicit attribute value */ goto process_attr_val; } case VMCON_HPS_ATTR_VAL: /* reading attribute value - pick up where we left off */ dst = attrval_ + wcslen(attrval_); /* if we haven't started the name yet, skip whitespace */ if (attr_qu_ == 0 && attrval_[0] == L'\0') { while (t3_is_whitespace(c)) c = next_wchar(sp, slenp); } /* if we're out of characters, we're done */ if (c == 0) break; /* if this is the first character, check for a quote */ if (attrval_[0] == L'\0' && (c == L'"' || c == L'\'')) { /* note the quote */ attr_qu_ = c; /* skip it */ c = next_wchar(sp, slenp); if (c == 0) break; } /* keep going until we reach the end of the attribute */ while (c != 0) { /* if this is our quote character, we're done */ if (attr_qu_ != 0 && c == attr_qu_) { /* skip the quote */ c = next_wchar(sp, slenp); /* return to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* stop scanning */ break; } /* * if it's a space or a '>', and the attribute isn't * quoted, this marks the end of the attribute */ if (attr_qu_ == 0 && (t3_is_whitespace(c) || c == '>')) { /* return to tag mode */ html_parse_state_ = VMCON_HPS_TAG; /* stop scanning */ break; } /* store the character if we have room */ if (dst + 1 < attrval_ + CVFMT_MAX_ATTR_VAL) *dst++ = c; /* read the next character */ c = next_wchar(sp, slenp); } /* null-terminate the destination */ *dst = L'\0'; /* * if we're still in value-parsing mode, it means that we * reached the end of the string without reaching a value * delimiter - simply stop scanning and wait for more */ if (html_parse_state_ == VMCON_HPS_ATTR_VAL) break; process_attr_val: /* * We have our tag and value, so see if we recognize it, * and it's meaningful in the context of the current tag. */ if (html_defer_br_ != HTML_DEFER_BR_NONE && CVmCaseFoldStr::wstreq(attrname_, L"height")) { int ht; /* * If the height is zero, always treat this as a * non-blanking flush. If it's one, treat it as we * originally planned to. If it's greater than one, * add n blank lines. */ ht = wtoi(attrval_); if (ht == 0) { /* always use non-blanking flush */ html_defer_br_ = HTML_DEFER_BR_FLUSH; } else if (ht == 1) { /* keep original setting */ } else { /* * write out the desired number of blank lines */ for ( ; ht > 0 ; --ht) write_blank_line(vmg0_); } } else if (html_allow_alt_ && !html_in_ignore_ && CVmCaseFoldStr::wstreq(attrname_, L"alt")) { /* write out the ALT string */ buffer_wstring(vmg_ attrval_); } else if (html_allow_color_ && CVmCaseFoldStr::wstreq(attrname_, L"color")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.fg = new_color; } else if (html_allow_color_ && CVmCaseFoldStr::wstreq(attrname_, L"bgcolor")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.bg = new_color; } else if (html_in_body_ && CVmCaseFoldStr::wstreq(attrname_, L"text")) { os_color_t new_color; /* parse the color, and use it as the new foreground */ if (parse_color_attr(vmg_ attrval_, &new_color)) cur_color_.fg = new_color; } else if (html_in_body_ && CVmCaseFoldStr::wstreq(attrname_, L"bgcolor")) { os_color_t new_color; /* parse the color, and use it as the new background */ if (parse_color_attr(vmg_ attrval_, &new_color)) set_os_body_color(new_color); } else if (html_in_tab_) { /* check the attribute name */ if (CVmCaseFoldStr::wstreq(attrname_, L"id")) { /* * We're defining a new tab at the current output * position. Find or create a tab object with the * given ID, and set its position to the current * column position. (If we already have an entry * with the same name, the new definition replaces * the old definition, so just change the existing * entry.) */ CVmFmtTabStop *entry = find_tab(attrval_, TRUE); /* define the entry in the current column */ entry->column_ = linecol_; /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"to")) { /* * find the tab object with the given ID, and * remember it; when we're done parsing the tag, * we'll look at this to generate our tab output */ new_tab_entry_ = find_tab(attrval_, FALSE); /* if we didn't find it, ignore this */ if (new_tab_entry_ == 0) html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"indent")) { /* * it's a simple - this simply * inserts the given number of spaces */ write_tab(vmg_ wtoi(attrval_), 0); /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"multiple")) { /* * it's a simple - this simply * inserts spaces to the given column multiple */ write_tab(vmg_ wtoi(attrval_), 0); /* this finishes the */ html_in_tab_ = FALSE; } else if (CVmCaseFoldStr::wstreq(attrname_, L"align")) { /* note the alignment */ if (CVmCaseFoldStr::wstreq(attrval_, L"left")) new_tab_align_ = VMFMT_TAB_LEFT; else if (CVmCaseFoldStr::wstreq(attrval_, L"right")) new_tab_align_ = VMFMT_TAB_RIGHT; else if (CVmCaseFoldStr::wstreq(attrval_, L"center")) new_tab_align_ = VMFMT_TAB_CENTER; else if (CVmCaseFoldStr::wstreq(attrval_, L"decimal")) new_tab_align_ = VMFMT_TAB_DECIMAL; else { /* invalid - ignore the */ html_in_tab_ = FALSE; } } else if (CVmCaseFoldStr::wstreq(attrname_, L"dp")) { /* note the decimal point character */ new_tab_dp_ = attrval_[0]; } } else if (html_in_wrap_) { /* it's a WRAP tag - check the attribute */ if (CVmCaseFoldStr::wstreq(attrname_, L"word")) { /* word wrap mode - clear the 'any' wrap flag */ cur_flags_ &= ~VMCON_OBF_BREAK_ANY; } else if (CVmCaseFoldStr::wstreq(attrname_, L"char")) { /* character wrap mode - set the 'any' flag */ cur_flags_ |= VMCON_OBF_BREAK_ANY; } } /* * since we already read the next character, simply loop * back immediately */ continue; default: /* ignore other states */ break; } /* * move on to the next character, and start over with the * new character */ c = next_wchar(sp, slenp); continue; } /* * If we're in a title, and this isn't the start of a new tag, * skip the character - we suppress all regular text output * inside a ... sequence. */ if (html_in_ignore_) { /* check for the start of a new tag */ if (c == '<') { /* stop skipping - return to normal parsing */ return c; } else { wchar_t curCh; /* check for entities */ if (c == '&') { /* parse the entity (fetches the next character) */ c = parse_entity(vmg_ &curCh, sp, slenp); } else { /* use this character literally */ curCh = c; /* get the next character */ c = next_wchar(sp, slenp); } /* * if we're gathering a title, and there's room in the * title buffer for more (always leaving room for a null * terminator), add this to the title buffer */ if (html_in_title_ && html_title_ptr_ != 0) { size_t rem, orig_rem; /* * figure the remaining title buffer space, leaving * room for a terminating null byte */ orig_rem = rem = html_title_buf_size_ - 1; /* * map this character into the local character set and * add it to the buffer */ if (G_cmap_to_ui->map(curCh, &html_title_ptr_, &rem) > orig_rem) { /* * it didn't fit - add null termination and close * out the buffer (we might have to add more than * one byte, because a single character might take * up multiple bytes in the local character set; to * ensure that we don't skip a wide character then * later add a narrower character, close out the * entire buffer when first we encounter a * character that doesn't fit) */ while (html_title_ptr_ < html_title_buf_ + html_title_buf_size_) *html_title_ptr_++ = '\0'; } } } /* don't display anything in an ignore section */ continue; } /* * we didn't find any HTML parsing we need to do with this * character, so return the character */ return c; } /* return the character that made us stop parsing */ return c; } /* ------------------------------------------------------------------------ */ /* * Parse the beginning HTML markup. This is called when we are scanning a * '<' or '&' character in output text, and we're in HTML mode, and the * underlying target doesn't support HTML parsing. Returns the next * character to process after we finish our initial parsing. */ wchar_t CVmFormatter::parse_html_markup(VMG_ wchar_t c, const char **sp, size_t *slenp) { /* check what we have */ if (c == '<') { const size_t MAX_TAG_SIZE = 50; wchar_t tagbuf[MAX_TAG_SIZE]; wchar_t *dst; int is_end_tag; /* skip the opening '<' */ c = next_wchar(sp, slenp); /* note if this is a closing tag */ if (c == '/' || c == '\\') { /* it's an end tag - note it and skip the slash */ is_end_tag = TRUE; c = next_wchar(sp, slenp); } else is_end_tag = FALSE; /* * find the end of the tag name - the tag continues to the next * space, '>', or end of line */ for (dst = tagbuf ; c != '\0' && c != ' ' && c != '>' ; c = next_wchar(sp, slenp)) { /* add this to the tag buffer if it fits */ if (dst + 1 < tagbuf + MAX_TAG_SIZE) *dst++ = c; } /* null-terminate the tag name */ *dst = '\0'; /* * Check to see if we recognize the tag. We only recognize a few * simple tags that map easily to character mode. */ if (CVmCaseFoldStr::wstreq(tagbuf, L"br")) { /* * line break - if there's anything buffered up, just flush the * current line, otherwise write out a blank line */ if (html_in_ignore_) /* suppress in ignore mode */; else if (linepos_ != 0) html_defer_br_ = HTML_DEFER_BR_FLUSH; else html_defer_br_ = HTML_DEFER_BR_BLANK; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"b") || CVmCaseFoldStr::wstreq(tagbuf, L"i") || CVmCaseFoldStr::wstreq(tagbuf, L"em") || CVmCaseFoldStr::wstreq(tagbuf, L"strong")) { /* suppress in ignore mode */ if (!html_in_ignore_) { /* check to see if this is a start or end tag */ if (!is_end_tag) { int attr; /* it's a start tag - push the current attributes */ push_color(); /* figure the attribute we're adding */ switch(tagbuf[0]) { case 'b': case 'B': attr = OS_ATTR_BOLD; break; case 'i': case 'I': attr = OS_ATTR_ITALIC; break; case 'e': case 'E': attr = OS_ATTR_EM; break; case 's': case 'S': attr = OS_ATTR_STRONG; break; } /* add the attribute */ cur_color_.attr |= attr; } else { /* it's an end tag - just pop the saved attributes */ pop_color(); } } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"p")) { /* paragraph - send out a blank line */ if (!html_in_ignore_) write_blank_line(vmg0_); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"tab") && !html_in_ignore_) { if (!html_in_ignore_) { /* if we have a pending tab, handle it */ expand_pending_tab(vmg_ TRUE); /* start a tab definition */ html_in_tab_ = TRUE; /* the new tab has no known characteristics yet */ new_tab_entry_ = 0; new_tab_align_ = VMFMT_TAB_NONE; new_tab_dp_ = L'\0'; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"img") || CVmCaseFoldStr::wstreq(tagbuf, L"sound")) { /* IMG and SOUND - allow ALT attributes */ html_allow_alt_ = TRUE; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"font")) { /* FONT - look for COLOR attribute */ if (html_in_ignore_) { /* do nothing while ignoring tags */ } else if (!is_end_tag) { /* enable the COLOR attribute */ html_allow_color_ = TRUE; /* push the current color, in case we change it */ push_color(); } else { /* restore the color in the enclosing text */ pop_color(); } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"body")) { /* BODY - look for BGCOLOR and TEXT attributes */ if (!html_in_ignore_ && !is_end_tag) { /* note that we're parsing a BODY tag */ html_in_body_ = TRUE; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"hr")) { int rem; if (!html_in_ignore_) { /* start a new line */ flush(vmg_ VM_NL_NEWLINE); /* write out underscores to the display width */ for (rem = console_->get_line_width() - 1 ; rem > 0 ; ) { const size_t DASHBUFLEN = 100; wchar_t dashbuf[DASHBUFLEN]; wchar_t *p; size_t cur; /* do as much as we can on this pass */ cur = rem; if (cur > DASHBUFLEN - 1) cur = DASHBUFLEN - 1; /* deduct this from the total */ rem -= cur; /* fill the buffer with dashes */ for (p = dashbuf ; cur != 0 ; --cur) *p++ = '-'; *p = '\0'; /* write it out */ buffer_wstring(vmg_ dashbuf); } /* put a blank line after the underscores */ write_blank_line(vmg0_); } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"q")) { wchar_t htmlchar; if (!html_in_ignore_) { /* if it's an open quote, increment the level */ if (!is_end_tag) ++html_quote_level_; /* add the open quote */ htmlchar = (!is_end_tag ? ((html_quote_level_ & 1) == 1 ? 8220 : 8216) : ((html_quote_level_ & 1) == 1 ? 8221 : 8217)); /* write out the HTML quote character */ buffer_char(vmg_ htmlchar); /* if it's a close quote, decrement the level */ if (is_end_tag) --html_quote_level_; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"title")) { /* * Turn ignore mode on or off as appropriate, and turn on or * off title mode as well. */ if (is_end_tag) { /* * note that we're leaving an ignore section and a title * section */ --html_in_ignore_; --html_in_title_; /* * If we're no longer in a title, and we have a buffer * where we've gathered up the title string, call the OS * layer to tell it the title string, in case it wants to * change the window title or otherwise make use of the * title. (Note that some streams don't bother keeping * track of the title at all, so we might not have a title * buffer.) */ if (html_in_title_ == 0 && html_title_ptr_ != 0) { /* null-terminate the title string */ *html_title_ptr_ = '\0'; /* tell the OS about the title */ if (!html_in_ignore_) set_title_in_os(html_title_buf_); } } else { /* * if we aren't already in a title, set up to capture the * title into the title buffer */ if (!html_in_title_) html_title_ptr_ = html_title_buf_; /* * note that we're in a title and in an ignore section, * since nothing within gets displayed */ ++html_in_ignore_; ++html_in_title_; } } else if (CVmCaseFoldStr::wstreq(tagbuf, L"aboutbox") || CVmCaseFoldStr::wstreq(tagbuf, L"banner")) { /* turn ignore mode on or off as appropriate */ if (is_end_tag) --html_in_ignore_; else ++html_in_ignore_; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"log")) { /* handle the tag according to our stream subclass */ process_log_tag(is_end_tag); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"nolog")) { /* handle the tag according to our stream subclass */ process_nolog_tag(is_end_tag); } else if (CVmCaseFoldStr::wstreq(tagbuf, L"wrap")) { /* note that we have a 'wrap' tag to process */ if (!html_in_ignore_) html_in_wrap_ = TRUE; } else if (CVmCaseFoldStr::wstreq(tagbuf, L"pre")) { /* count the nesting level if starting PRE mode */ if (!is_end_tag) ++html_pre_level_; /* surround the PRE block with blank lines */ write_blank_line(vmg0_); /* count the nesting level if ending PRE mode */ if (is_end_tag && html_pre_level_ != 0) --html_pre_level_; } /* suppress everything up to the next '>' */ html_parse_state_ = VMCON_HPS_TAG; /* * return the next character - since we're now in TAG mode, we'll * resume the HTML mini-parser to parse the contents of the tag */ return c; } else if (c == '&') { /* parse the HTML markup */ wchar_t ent; c = parse_entity(vmg_ &ent, sp, slenp); /* translate it and write it out */ buffer_char(vmg_ ent); /* proceed with the next character */ return c; } else { /* * we have nothing special to do with this character - simply * buffer it and move on to the next character */ buffer_char(vmg_ c); return next_wchar(sp, slenp); } } /* ------------------------------------------------------------------------ */ /* * Parse an HTML entity markup. Call this with the current character * pointer at the '&'. We'll parse the entity name and return it in *ent. * The return value is the next character to be parsed after the entity. */ wchar_t CVmFormatter::parse_entity(VMG_ wchar_t *ent, const char **sp, size_t *slenp) { const size_t MAXAMPLEN = 10; char ampbuf[MAXAMPLEN]; char *dst; const char *orig_s; size_t orig_slen; const struct amp_tbl_t *ampptr; size_t lo, hi, cur; wchar_t c; /* * remember where the part after the '&' begins, so we can come back * here later if necessary */ orig_s = *sp; orig_slen = *slenp; /* get the character after the ampersand */ c = next_wchar(sp, slenp); /* if it's numeric, parse the number */ if (c == '#') { uint val; /* skip the '#' */ c = next_wchar(sp, slenp); /* check for hex */ if (c == 'x' || c == 'X') { /* skip the 'x' */ c = next_wchar(sp, slenp); /* read the hex number */ for (val = 0 ; is_xdigit(c) ; c = next_wchar(sp, slenp)) { /* accumulate this digit into the value */ val *= 16; if (is_digit(c)) val += c - '0'; else if (c >= 'a' && c <= 'f') val += c - 'a' + 10; else val += c - 'A' + 10; } } else { /* read the number */ for (val = 0 ; is_digit(c) ; c = next_wchar(sp, slenp)) { /* accumulate this digit into the value */ val *= 10; val += c - '0'; } } /* if we found a ';' at the end, skip it */ if (c == ';') c = next_wchar(sp, slenp); /* return the entity from the numeric character value */ *ent = (wchar_t)val; /* we're done with this character */ return c; } /* * Parse the sequence after the '&'. Parse up to the closing * semicolon, or any non-alphanumeric, or until we fill up the buffer. */ for (dst = ampbuf ; c != '\0' && (is_digit(c) || is_alpha(c)) && dst < ampbuf + MAXAMPLEN - 1 ; c = next_wchar(sp, slenp)) { /* copy this character to the name buffer */ *dst++ = (char)c; } /* null-terminate the name */ *dst = '\0'; /* do a binary search for the name */ lo = 0; hi = sizeof(amp_tbl)/sizeof(amp_tbl[0]) - 1; for (;;) { int diff; /* if we've converged, look no further */ if (lo > hi || lo >= sizeof(amp_tbl)/sizeof(amp_tbl[0])) { ampptr = 0; break; } /* split the difference */ cur = lo + (hi - lo)/2; ampptr = &_tbl[cur]; /* see where we are relative to the target item */ diff = strcmp(ampptr->cname, ampbuf); if (diff == 0) { /* this is it */ break; } else if (lo >= hi) { /* failed to find it */ ampptr = 0; break; } else if (diff > 0) { /* this one is too high - check the lower half */ hi = (cur == hi ? hi - 1 : cur); } else { /* this one is too low - check the upper half */ lo = (cur == lo ? lo + 1 : cur); } } /* skip to the appropriate next character */ if (c == ';') { /* name ended with semicolon - skip the semicolon */ c = next_wchar(sp, slenp); } else if (ampptr != 0) { int skipcnt; /* found the name - skip its exact length */ skipcnt = strlen(ampptr->cname); for (*sp = orig_s, *slenp = orig_slen ; skipcnt != 0 ; c = next_wchar(sp, slenp), --skipcnt) ; /* * that positions us on the last character of the entity name; skip * one more, so that we're on the character after the entity name */ c = next_wchar(sp, slenp); } /* if we found the entry, write out the character */ if (ampptr != 0) { /* we found it - return the character value */ *ent = ampptr->html_cval; } else { /* * we didn't find it - ignore the entire sequence, and just write * it out verbatim; for our caller's purposes, the result of the * parse is just the '&' itself */ *ent = '&'; /* now go back and scan from the next character after the ampersand */ *sp = orig_s; *slenp = orig_slen; c = next_wchar(sp, slenp); } /* return the next character */ return c; } /* ------------------------------------------------------------------------ */ /* * Service routine - read a number represented as a base-10 string of wide * characters */ int CVmFormatter::wtoi(const wchar_t *p) { long acc; int neg = FALSE; /* skip any leading whitespace */ while (t3_is_whitespace(*p)) ++p; /* check for a leading sign */ if (*p == '+') { /* positive sign - skip it */ ++p; } else if (*p == '-') { /* negative sign - note it and skip it */ neg = TRUE; ++p; } /* scan the digits */ for (acc = 0 ; is_digit(*p) ; ++p) { /* shift the accumulator and add this digit */ acc *= 10; acc += value_of_digit(*p); } /* apply the sign */ if (neg) acc = -acc; /* return the result */ return acc; } frobtads-1.2.3/tads3/vmrefcnt.h0000644000175000001440000001323411546172652015533 0ustar realncusers/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmrefcnt.h - thread-safe reference-counted objects Function Defines classes for reference counting, including weak references. Notes This header needs to be "interleaved" with the osifcnet.h class definitions. The classes we define here depend on the OS-specific thread-safe counter object already being defined in-line, while other OS-specific classes depend on our classes. This header must thus be #included within the OS-specific networking header after defining the counter class but before defining any of the other classes. Modified 05/24/10 MJRoberts - Creation */ #ifndef VMREFCNT_H #define VMREFCNT_H /* ------------------------------------------------------------------------ */ /* * Reference-counted object. This is the base class for objects with * memory management by reference count. Construction sets the initial * reference count to 1, on behalf of the caller that performed the 'new' * to create the object. Callers must manually release references as they * go out of scope. When the reference count reaches zero, the object is * destroyed. */ class CVmRefCntObj { public: /* * on construction, set the counter to 1, to count the initial * reference on behalf of the caller that performed the 'new' */ CVmRefCntObj() : cnt(1) { } /* add a reference */ void add_ref() { cnt.inc(); } /* * release a reference; automatically deletes the object when the * reference count reaches zero */ void release_ref() { if (cnt.dec() == 0) delete this; } /* * debugging add/remove ref - print a console message showing where the * add/remove came from */ void add_ref(void *from, const char *msg) { add_ref(); printf("add_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get(), (long)from, msg); } void release_ref(void *from, const char *msg) { printf("release_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get()-1, (long)from, msg); release_ref(); } /* get the current reference count */ long get_ref_count() const { return cnt.get(); } protected: /* * the destructor must be virtual because this class is designed for * subclassing, and we invoke the destructor from this base class */ virtual ~CVmRefCntObj() { } /* reference counter */ OS_Counter cnt; }; /* ------------------------------------------------------------------------ */ /* * Weak reference. This is a reference to a WeakRefable object that * doesn't count in the reference counting for the referenced object. */ class CVmWeakRef: public CVmRefCntObj { friend class CVmWeakRefable; public: /* * Get the referenced object. If the object is still alive, this * creates a new strong reference to the object and returns the strong * reference. The caller is responsible for releasing the returned * strong reference when it's done with the reference. If the object * has already been deleted, this returns null. */ class CVmWeakRefable *ref(); /* * Test the reference to see if the object is still alive. Returns * true if the object is alive, false if not. * * If this returns true, there's of course no guarantee that the object * won't be deleted at any time. If this returns false, however, you * can be certain that it will return false (and ref() will return nil) * from now on, because it means the underlying object has been * deleted. test() is thus useful for cases where you only need to * know for sure that an object has been deleted, such as when * periodically cleaning up a cache containing weak references. When * you need to use the object after determining if it's alive, you * should use ref() instead, since that atomically tests to see if the * object is alive and creates a strong reference to it if it is. */ int test() { return ref_ != 0; } protected: CVmWeakRef(class CVmWeakRefable *r); virtual ~CVmWeakRef(); /* * Clear our reference. The referenced object calls this when it's * about to be deleted, to let us know that we can no longer access it. * * The referenced object only calls this after locking the mutex. */ void clear_ref() { ref_ = 0; } /* the object we weakly reference */ class CVmWeakRefable *ref_; /* the mutex we share with the referenced object */ class OS_Mutex *mu; }; /* * Weak-referenceable object. This is a specialized reference-counted * object that can have weak references in addition to regular strong * references. A weak reference is a reference that doesn't prevent the * object from being deleted, but still allows access as long as the object * is alive. */ class CVmWeakRefable: public CVmRefCntObj { friend class CVmWeakRef; public: CVmWeakRefable(); /* release a reference */ void release_ref(); /* * Get the weak reference to this object */ CVmWeakRef *get_weak_ref(); /* debugging version */ void release_ref(void *from, const char *msg) { printf("release_ref(this=%lx, cnt=%ld, from=%lx, %s)\n", (long)this, cnt.get()-1, (long)from, msg); release_ref(); } protected: virtual ~CVmWeakRefable(); /* mutex protecting against concurrent access */ class OS_Mutex *mu; /* our weak reference object */ CVmWeakRef *weakref; }; #endif /* VMREFCNT_H */ frobtads-1.2.3/tads3/vmbignum.cpp0000644000175000001440000055122311773313061016064 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbignum.cpp - big number metaclass Function Notes Modified 02/18/00 MJRoberts - Creation */ #include #include #include #include #include #include #include #include "t3std.h" #include "vmtype.h" #include "vmmcreg.h" #include "vmbignum.h" #include "vmobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmpool.h" #include "vmstack.h" #include "utf8.h" #include "vmstr.h" #include "vmbif.h" #include "vmmeta.h" #include "vmlst.h" #include "vmdatasrc.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassBigNum metaclass_reg_obj; CVmMetaclass *CVmObjBigNum::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjBigNum:: *CVmObjBigNum::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjBigNum::getp_undef, &CVmObjBigNum::getp_format, &CVmObjBigNum::getp_equal_rnd, &CVmObjBigNum::getp_get_prec, &CVmObjBigNum::getp_set_prec, &CVmObjBigNum::getp_frac, &CVmObjBigNum::getp_whole, &CVmObjBigNum::getp_round_dec, &CVmObjBigNum::getp_abs, &CVmObjBigNum::getp_ceil, &CVmObjBigNum::getp_floor, &CVmObjBigNum::getp_get_scale, &CVmObjBigNum::getp_scale, &CVmObjBigNum::getp_negate, &CVmObjBigNum::getp_copy_sign, &CVmObjBigNum::getp_is_neg, &CVmObjBigNum::getp_remainder, &CVmObjBigNum::getp_sin, &CVmObjBigNum::getp_cos, &CVmObjBigNum::getp_tan, &CVmObjBigNum::getp_deg2rad, &CVmObjBigNum::getp_rad2deg, &CVmObjBigNum::getp_asin, &CVmObjBigNum::getp_acos, &CVmObjBigNum::getp_atan, &CVmObjBigNum::getp_sqrt, &CVmObjBigNum::getp_ln, &CVmObjBigNum::getp_exp, &CVmObjBigNum::getp_log10, &CVmObjBigNum::getp_pow, &CVmObjBigNum::getp_sinh, &CVmObjBigNum::getp_cosh, &CVmObjBigNum::getp_tanh, &CVmObjBigNum::getp_pi, &CVmObjBigNum::getp_e, &CVmObjBigNum::getp_numType }; /* constant value 1 */ const unsigned char CVmObjBigNum::one_[] = { /* number of digits - we just need one to represent the integer 1 */ 0x01, 0x00, /* exponent */ 0x01, 0x00, /* flags */ 0x00, /* mantissa - just one digit is needed, but pad out the byte with zero */ 0x10 }; #if 0 /* * Pi to 2048 digits */ const unsigned char CVmObjBigNum::pi_[] = { /* number of digits of precision */ 0x00, 0x08, /* base-10 exponent */ 0x01, 0x00, /* flags */ 0x00, /* * the first 2048 decimal digits of pi, packed two to a byte (typed * in from memory - I hope I got everything right :) */ /* 1-100 */ 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95, 0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37, 0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92, 0x30, 0x78, 0x16, 0x40, 0x62, 0x86, 0x20, 0x89, 0x98, 0x62, 0x80, 0x34, 0x82, 0x53, 0x42, 0x11, 0x70, 0x67, /* 101-200 */ 0x98, 0x21, 0x48, 0x08, 0x65, 0x13, 0x28, 0x23, 0x06, 0x64, 0x70, 0x93, 0x84, 0x46, 0x09, 0x55, 0x05, 0x82, 0x23, 0x17, 0x25, 0x35, 0x94, 0x08, 0x12, 0x84, 0x81, 0x11, 0x74, 0x50, 0x28, 0x41, 0x02, 0x70, 0x19, 0x38, 0x52, 0x11, 0x05, 0x55, 0x96, 0x44, 0x62, 0x29, 0x48, 0x95, 0x49, 0x30, 0x38, 0x19, /* 201-300 */ 0x64, 0x42, 0x88, 0x10, 0x97, 0x56, 0x65, 0x93, 0x34, 0x46, 0x12, 0x84, 0x75, 0x64, 0x82, 0x33, 0x78, 0x67, 0x83, 0x16, 0x52, 0x71, 0x20, 0x19, 0x09, 0x14, 0x56, 0x48, 0x56, 0x69, 0x23, 0x46, 0x03, 0x48, 0x61, 0x04, 0x54, 0x32, 0x66, 0x48, 0x21, 0x33, 0x93, 0x60, 0x72, 0x60, 0x24, 0x91, 0x41, 0x27, /* 301-400 */ 0x37, 0x24, 0x58, 0x70, 0x06, 0x60, 0x63, 0x15, 0x58, 0x81, 0x74, 0x88, 0x15, 0x20, 0x92, 0x09, 0x62, 0x82, 0x92, 0x54, 0x09, 0x17, 0x15, 0x36, 0x43, 0x67, 0x89, 0x25, 0x90, 0x36, 0x00, 0x11, 0x33, 0x05, 0x30, 0x54, 0x88, 0x20, 0x46, 0x65, 0x21, 0x38, 0x41, 0x46, 0x95, 0x19, 0x41, 0x51, 0x16, 0x09, /* 401-500 */ 0x43, 0x30, 0x57, 0x27, 0x03, 0x65, 0x75, 0x95, 0x91, 0x95, 0x30, 0x92, 0x18, 0x61, 0x17, 0x38, 0x19, 0x32, 0x61, 0x17, 0x93, 0x10, 0x51, 0x18, 0x54, 0x80, 0x74, 0x46, 0x23, 0x79, 0x96, 0x27, 0x49, 0x56, 0x73, 0x51, 0x88, 0x57, 0x52, 0x72, 0x48, 0x91, 0x22, 0x79, 0x38, 0x18, 0x30, 0x11, 0x94, 0x91, /* 501-600 */ 0x29, 0x83, 0x36, 0x73, 0x36, 0x24, 0x40, 0x65, 0x66, 0x43, 0x08, 0x60, 0x21, 0x39, 0x49, 0x46, 0x39, 0x52, 0x24, 0x73, 0x71, 0x90, 0x70, 0x21, 0x79, 0x86, 0x09, 0x43, 0x70, 0x27, 0x70, 0x53, 0x92, 0x17, 0x17, 0x62, 0x93, 0x17, 0x67, 0x52, 0x38, 0x46, 0x74, 0x81, 0x84, 0x67, 0x66, 0x94, 0x05, 0x13, /* 601-700 */ 0x20, 0x00, 0x56, 0x81, 0x27, 0x14, 0x52, 0x63, 0x56, 0x08, 0x27, 0x78, 0x57, 0x71, 0x34, 0x27, 0x57, 0x78, 0x96, 0x09, 0x17, 0x36, 0x37, 0x17, 0x87, 0x21, 0x46, 0x84, 0x40, 0x90, 0x12, 0x24, 0x95, 0x34, 0x30, 0x14, 0x65, 0x49, 0x58, 0x53, 0x71, 0x05, 0x07, 0x92, 0x27, 0x96, 0x89, 0x25, 0x89, 0x23, /* 701-800 */ 0x54, 0x20, 0x19, 0x95, 0x61, 0x12, 0x12, 0x90, 0x21, 0x96, 0x08, 0x64, 0x03, 0x44, 0x18, 0x15, 0x98, 0x13, 0x62, 0x97, 0x74, 0x77, 0x13, 0x09, 0x96, 0x05, 0x18, 0x70, 0x72, 0x11, 0x34, 0x99, 0x99, 0x99, 0x83, 0x72, 0x97, 0x80, 0x49, 0x95, 0x10, 0x59, 0x73, 0x17, 0x32, 0x81, 0x60, 0x96, 0x31, 0x85, /* 801-900 */ 0x95, 0x02, 0x44, 0x59, 0x45, 0x53, 0x46, 0x90, 0x83, 0x02, 0x64, 0x25, 0x22, 0x30, 0x82, 0x53, 0x34, 0x46, 0x85, 0x03, 0x52, 0x61, 0x93, 0x11, 0x88, 0x17, 0x10, 0x10, 0x00, 0x31, 0x37, 0x83, 0x87, 0x52, 0x88, 0x65, 0x87, 0x53, 0x32, 0x08, 0x38, 0x14, 0x20, 0x61, 0x71, 0x77, 0x66, 0x91, 0x47, 0x30, /* 901-1000 */ 0x35, 0x98, 0x25, 0x34, 0x90, 0x42, 0x87, 0x55, 0x46, 0x87, 0x31, 0x15, 0x95, 0x62, 0x86, 0x38, 0x82, 0x35, 0x37, 0x87, 0x59, 0x37, 0x51, 0x95, 0x77, 0x81, 0x85, 0x77, 0x80, 0x53, 0x21, 0x71, 0x22, 0x68, 0x06, 0x61, 0x30, 0x01, 0x92, 0x78, 0x76, 0x61, 0x11, 0x95, 0x90, 0x92, 0x16, 0x42, 0x01, 0x98, /* 1001-1100 */ 0x93, 0x80, 0x95, 0x25, 0x72, 0x01, 0x06, 0x54, 0x85, 0x86, 0x32, 0x78, 0x86, 0x59, 0x36, 0x15, 0x33, 0x81, 0x82, 0x79, 0x68, 0x23, 0x03, 0x01, 0x95, 0x20, 0x35, 0x30, 0x18, 0x52, 0x96, 0x89, 0x95, 0x77, 0x36, 0x22, 0x59, 0x94, 0x13, 0x89, 0x12, 0x49, 0x72, 0x17, 0x75, 0x28, 0x34, 0x79, 0x13, 0x15, /* 1101-1200 */ 0x15, 0x57, 0x48, 0x57, 0x24, 0x24, 0x54, 0x15, 0x06, 0x95, 0x95, 0x08, 0x29, 0x53, 0x31, 0x16, 0x86, 0x17, 0x27, 0x85, 0x58, 0x89, 0x07, 0x50, 0x98, 0x38, 0x17, 0x54, 0x63, 0x74, 0x64, 0x93, 0x93, 0x19, 0x25, 0x50, 0x60, 0x40, 0x09, 0x27, 0x70, 0x16, 0x71, 0x13, 0x90, 0x09, 0x84, 0x88, 0x24, 0x01, /* 1201-1300 */ 0x28, 0x58, 0x36, 0x16, 0x03, 0x56, 0x37, 0x07, 0x66, 0x01, 0x04, 0x71, 0x01, 0x81, 0x94, 0x29, 0x55, 0x59, 0x61, 0x98, 0x94, 0x67, 0x67, 0x83, 0x74, 0x49, 0x44, 0x82, 0x55, 0x37, 0x97, 0x74, 0x72, 0x68, 0x47, 0x10, 0x40, 0x47, 0x53, 0x46, 0x46, 0x20, 0x80, 0x46, 0x68, 0x42, 0x59, 0x06, 0x94, 0x91, /* 1301-1400 */ 0x29, 0x33, 0x13, 0x67, 0x70, 0x28, 0x98, 0x91, 0x52, 0x10, 0x47, 0x52, 0x16, 0x20, 0x56, 0x96, 0x60, 0x24, 0x05, 0x80, 0x38, 0x15, 0x01, 0x93, 0x51, 0x12, 0x53, 0x38, 0x24, 0x30, 0x03, 0x55, 0x87, 0x64, 0x02, 0x47, 0x49, 0x64, 0x73, 0x26, 0x39, 0x14, 0x19, 0x92, 0x72, 0x60, 0x42, 0x69, 0x92, 0x27, /* 1401-1500 */ 0x96, 0x78, 0x23, 0x54, 0x78, 0x16, 0x36, 0x00, 0x93, 0x41, 0x72, 0x16, 0x41, 0x21, 0x99, 0x24, 0x58, 0x63, 0x15, 0x03, 0x02, 0x86, 0x18, 0x29, 0x74, 0x55, 0x57, 0x06, 0x74, 0x98, 0x38, 0x50, 0x54, 0x94, 0x58, 0x85, 0x86, 0x92, 0x69, 0x95, 0x69, 0x09, 0x27, 0x21, 0x07, 0x97, 0x50, 0x93, 0x02, 0x95, /* 1501-1600 */ 0x53, 0x21, 0x16, 0x53, 0x44, 0x98, 0x72, 0x02, 0x75, 0x59, 0x60, 0x23, 0x64, 0x80, 0x66, 0x54, 0x99, 0x11, 0x98, 0x81, 0x83, 0x47, 0x97, 0x75, 0x35, 0x66, 0x36, 0x98, 0x07, 0x42, 0x65, 0x42, 0x52, 0x78, 0x62, 0x55, 0x18, 0x18, 0x41, 0x75, 0x74, 0x67, 0x28, 0x90, 0x97, 0x77, 0x72, 0x79, 0x38, 0x00, /* 1601-1700 */ 0x08, 0x16, 0x47, 0x06, 0x00, 0x16, 0x14, 0x52, 0x49, 0x19, 0x21, 0x73, 0x21, 0x72, 0x14, 0x77, 0x23, 0x50, 0x14, 0x14, 0x41, 0x97, 0x35, 0x68, 0x54, 0x81, 0x61, 0x36, 0x11, 0x57, 0x35, 0x25, 0x52, 0x13, 0x34, 0x75, 0x74, 0x18, 0x49, 0x46, 0x84, 0x38, 0x52, 0x33, 0x23, 0x90, 0x73, 0x94, 0x14, 0x33, /* 1701-1800 */ 0x34, 0x54, 0x77, 0x62, 0x41, 0x68, 0x62, 0x51, 0x89, 0x83, 0x56, 0x94, 0x85, 0x56, 0x20, 0x99, 0x21, 0x92, 0x22, 0x18, 0x42, 0x72, 0x55, 0x02, 0x54, 0x25, 0x68, 0x87, 0x67, 0x17, 0x90, 0x49, 0x46, 0x01, 0x65, 0x34, 0x66, 0x80, 0x49, 0x88, 0x62, 0x72, 0x32, 0x79, 0x17, 0x86, 0x08, 0x57, 0x84, 0x38, /* 1801-1900 */ 0x38, 0x27, 0x96, 0x79, 0x76, 0x68, 0x14, 0x54, 0x10, 0x09, 0x53, 0x88, 0x37, 0x86, 0x36, 0x09, 0x50, 0x68, 0x00, 0x64, 0x22, 0x51, 0x25, 0x20, 0x51, 0x17, 0x39, 0x29, 0x84, 0x89, 0x60, 0x84, 0x12, 0x84, 0x88, 0x62, 0x69, 0x45, 0x60, 0x42, 0x41, 0x96, 0x52, 0x85, 0x02, 0x22, 0x10, 0x66, 0x11, 0x86, /* 1901-2000 */ 0x30, 0x67, 0x44, 0x27, 0x86, 0x22, 0x03, 0x91, 0x94, 0x94, 0x50, 0x47, 0x12, 0x37, 0x13, 0x78, 0x69, 0x60, 0x95, 0x63, 0x64, 0x37, 0x19, 0x17, 0x28, 0x74, 0x67, 0x76, 0x46, 0x57, 0x57, 0x39, 0x62, 0x41, 0x38, 0x90, 0x86, 0x58, 0x32, 0x64, 0x59, 0x95, 0x81, 0x33, 0x90, 0x47, 0x80, 0x27, 0x59, 0x00, /* 2001-2048 */ 0x99, 0x46, 0x57, 0x64, 0x07, 0x89, 0x51, 0x26, 0x94, 0x68, 0x39, 0x83, 0x52, 0x59, 0x57, 0x09, 0x82, 0x58, 0x22, 0x62, 0x05, 0x22, 0x48, 0x94 }; #endif /* ------------------------------------------------------------------------ */ /* * The BigNumber internal cache. This is meant to be private to the * vmbignum module, but since the module is actually split between * vmbignum.cpp and vmbignumlib.cpp, we need to use a C++ global. We use * the S_ naming prefix to emphasize that it's conceptually a "static" * variable, private to this two-file module. */ extern CVmBigNumCache *S_bignum_cache; /* ------------------------------------------------------------------------ */ /* * Static creation methods. These routines allocate an object ID and * create a new object. */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjBigNum::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_val_t *val; size_t digits; const char *strval = 0; const CVmObjBigNum *objval = 0; /* check arguments */ if (argc < 1 || argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * check the first argument, which gives the source value - this can be * an integer, a string, or another BigNumber value */ val = G_stk->get(0); if (val->typ == VM_INT) { /* we'll use the integer value */ } else if (val->typ == VM_OBJ && is_bignum_obj(vmg_ val->val.obj)) { /* we'll use the BigNumber value as the source */ objval = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj); } else if ((strval = val->get_as_string(vmg0_)) != 0) { /* we'll parse the string value */ } else { /* it's not a source type we accept */ err_throw(VMERR_NUM_VAL_REQD); } /* * get the precision value, if provided; if not, infer it from the * source value */ if (argc > 1) { /* make sure it's an integer */ if (G_stk->get(1)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* retrieve the value */ digits = (size_t)G_stk->get(1)->val.intval; } else if (strval != 0) { /* parse the string to infer the precision */ digits = precision_from_string( strval + VMB_LEN, vmb_get_len(strval), 10); } else if (objval != 0) { /* use the same precision as the source BigNumber value */ digits = get_prec(objval->get_ext()); } else { /* use a default precision */ digits = 32; } /* create the value */ if (strval != 0) { /* create the value from the string */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ strval + VMB_LEN, vmb_get_len(strval), digits); } else if (objval != 0) { vm_val_t new_val; /* create the value based on the BigNumber value */ round_val(vmg_ &new_val, objval->get_ext(), digits, TRUE); /* return the new object ID */ id = new_val.val.obj; } else { /* create the value based on the integer value */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ (long)val->val.intval, digits); } /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* create with a given precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ digits); return id; } /* create from an integer value */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, long val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* create from an unsigned integer value */ vm_obj_id_t CVmObjBigNum::createu(VMG_ int in_root_set, ulong val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ digits); set_uint_val(((CVmObjBigNum *)vm_objp(vmg_ id))->get_ext(), val); return id; } /* create from a 64-bit unsigned int expressed as two 32-bit segments */ vm_obj_id_t CVmObjBigNum::create_int64(VMG_ int in_root_set, uint32_t hi, uint32_t lo) { /* * Store into our portable 8-byte format: the portable format is * little-endian, so simply store the low part in the first four bytes, * and the high part in the second four bytes. */ char buf[8]; oswp4(buf, lo); oswp4(buf+4, hi); /* * Now create the value from the buffer. (This could be implemented a * little more efficiently by working directly from the integers, but * this is certainly not on any critical paths, and the create_rp8 code * already exists and works.) */ return create_rp8(vmg_ in_root_set, buf); } /* create from a 64-bit unsigned int expressed in portable 8-byte format */ vm_obj_id_t CVmObjBigNum::create_rp8(VMG_ int in_root_set, const char *buf) { /* * create the BigNumber object big enough to hold a 64-bit integer * value, which can have up to 20 digits */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ 20); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* set the value */ n->set_rp8((const uchar *)buf); /* the source value is unsigned, so it's always non-negative */ set_neg(n->get_ext(), FALSE); /* return the object */ return id; } /* create from a 64-bit signed int */ vm_obj_id_t CVmObjBigNum::create_rp8s(VMG_ int in_root_set, const char *buf) { /* * create the BigNumber object big enough to hold a 64-bit integer * value, which can have up to 20 digits */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ 20); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* make a private copy of the source value */ uchar p[8]; memcpy(p, buf, 8); /* * If the value is negative, as indicated by the sign bit on the most * significant byte, get the absolute value by computing the two's * complement. This makes the arithmetic easy because we can do the * bit twiddling with unsigned values, and just set the BigNum sign bit * when we're done with all of that. * * Note that this 2's complement arithmetic is truly portable - it * doesn't matter whether the local machine's native int type is * big-endian or little-endian or some bizarre interleaved-endian, or * if it uses 2's complement notation or some other wacky bygone scheme * (1's complement, sign bit only, etc). The reason this code is * portable regardless of the local notation is that we're not * operating on local int types: we're operating on a buffer that's in * a universal notation that we define as 64-bit 2's complement * little-endian. We're doing our work at the byte level, so as long * as the local machine has 8+-bit bytes (which is a requirement for * the platform to have a C compiler), this code is portable. */ int neg = (p[7] & 0x80) != 0; if (neg) twos_complement_p8(p); /* set the 64-bit int value */ n->set_rp8(p); /* set the sign bit */ set_neg(n->get_ext(), neg); /* return the object */ return id; } /* set the value from an unsigned 64-bit little-endian buffer */ void CVmObjBigNum::set_rp8(const uchar *p) { /* * set up temporary registers with enough precision for 64-bit ints (we * need at most 20 digits for these values) */ char tmp1[5 + 20] = { 20, 0, 0, 0, 0 }; char tmp2[5 + 20] = { 20, 0, 0, 0, 0 }; /* * shift in bits 24 at a time - this makes the math fit in 32-bit ints * so that we don't have to worry about int overflows */ set_uint_val(get_ext(), (((ulong)p[7]) << 16) | (((ulong)p[6]) << 8) | p[5]); mul_by_long(get_ext(), 0x01000000); set_uint_val(tmp1, (((ulong)p[4]) << 16) | (((ulong)p[3]) << 8) | p[2]); compute_sum_into(tmp2, get_ext(), tmp1); mul_by_long(tmp2, 0x10000); set_uint_val(tmp1, (((ulong)p[1]) << 8) | p[0]); compute_sum_into(get_ext(), tmp2, tmp1); } /* create from a string value */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, const char *str, size_t len, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); return id; } /* create from a string value, inferring the required precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, const char *str, size_t len) { int digits = precision_from_string(str, len, 10); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); return id; } /* create from a string value in a given radix */ vm_obj_id_t CVmObjBigNum::create_radix( VMG_ int in_root_set, const char *str, size_t len, int radix) { int digits = precision_from_string(str, len, radix); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); if (radix == 10) new (vmg_ id) CVmObjBigNum(vmg_ str, len, digits); else { new (vmg_ id) CVmObjBigNum(vmg_ digits); ((CVmObjBigNum *)vm_objp(vmg_ id))->set_str_val(str, len, radix); } return id; } /* create from a double value, with enough precision for a native double */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, double val) { int digits = DBL_DIG + 2; vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* create from a double value, with specified precision */ vm_obj_id_t CVmObjBigNum::create(VMG_ int in_root_set, double val, size_t digits) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ val, digits); return id; } /* * Create from a BER-encoded compressed integer value */ vm_obj_id_t CVmObjBigNum::create_from_ber(VMG_ int in_root_set, const char *buf, size_t len) { /* * Create the object. The source value has 7 bits of precision per * byte. We need log(2)/log(10) digits of precision per bit of * precision, which is about 0.30103 digits per bit. */ int prec = (int)(len*7*0.30103 + 0.5); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); char *ext = n->get_ext(); /* allocate two temporary registers for intermediate sums */ char *t1, *t2; uint hdl1, hdl2; alloc_temp_regs((size_t)prec, 2, &t1, &hdl1, &t2, &hdl2); /* zero the accumulator (start with t1) */ set_zero(t1); /* set up a stack register for the base-128 digits */ char dig[5 + 3] = { 3, 0, 0, 0, 0 }; /* starting at the most significant byte, shift in the bits */ for (size_t i = 0 ; i < len ; ++i) { /* multiply the accumulator by 2^7 */ mul_by_long(t1, 128); /* add in the next base-128 digit */ set_uint_val(dig, buf[i] & 0x7f); compute_sum_into(t2, t1, dig); /* swap registers so that t2 becomes the accumulator */ char *tt = t1; t1 = t2; t2 = tt; } /* copy the accumulator into the result */ copy_val(ext, t1, FALSE); /* done with the temporary registers */ release_temp_regs(2, hdl1, hdl2); /* return the new ID */ return id; } /* * Create from a floating point value encoded in an IEEE 754-2008 binary * interchange format, little-endian order. We accept bit sizes of 16, 32, * 64, and 128 (half, single, double, and quad precision). * */ vm_obj_id_t CVmObjBigNum::create_from_ieee754( VMG_ int in_root_set, const char *buf, int bits) { /* * Create the object. Use the decimal equivalent precision of the IEEE * 754 object. Note that we use 17 digits for doubles; the binary type * stores 53 bits of mantissa == 15.95 decimal digits, but there's a * pathological case at 1+epsilon, which is 1 + 2e-16, which requires * 17 decimal digits to store. */ int prec = (bits == 16 ? 4 : bits == 32 ? 8 : bits == 64 ? 17 : bits == 128 ? 35 : 8); vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjBigNum(vmg_ prec); CVmObjBigNum *n = (CVmObjBigNum *)vm_objp(vmg_ id); /* decode the IEEE 754 value */ n->set_ieee754_value(vmg_ buf, bits); /* return the new object id */ return id; } /* ------------------------------------------------------------------------ */ /* * Cast a value to BigNumber */ void CVmObjBigNum::cast_to_bignum(VMG_ vm_val_t *bnval, const vm_val_t *srcval) { const char *str; if (srcval->typ == VM_INT) { /* create from the integer value */ bnval->set_obj(create( vmg_ FALSE, (long)srcval->val.intval, (size_t)10)); } else if ((str = srcval->get_as_string(vmg0_)) != 0) { /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* create from the string value */ bnval->set_obj(create(vmg_ FALSE, str, len)); } else if (srcval->typ == VM_OBJ && is_bignum_obj(vmg_ srcval->val.obj)) { /* it's already a BigNumber - just return the same value */ bnval->set_obj(srcval->val.obj); } else { /* can't cast to BigNumber */ err_throw(VMERR_NO_BIGNUM_CONV); } } /* * Promote an integer value to BigNumber */ void CVmObjBigNum::promote_int(VMG_ vm_val_t *val) const { val->set_obj(create(vmg_ FALSE, (long)val->val.intval, (size_t)10)); } /* ------------------------------------------------------------------------ */ /* * Constructors. These are called indirectly through our static * creation methods. */ /* * Create with no extension */ CVmObjBigNum::CVmObjBigNum() { /* no extension */ ext_ = 0; } /* * Create with a given precision */ CVmObjBigNum::CVmObjBigNum(VMG_ size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); } /* * Create with a given integer value */ CVmObjBigNum::CVmObjBigNum(VMG_ long val, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_int_val(ext_, val); } /* * Create with a given string as the source value */ CVmObjBigNum::CVmObjBigNum(VMG_ const char *str, size_t len, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_str_val(str, len); } /* * Create from a double value */ CVmObjBigNum::CVmObjBigNum(VMG_ double val, size_t digits) { /* allocate space */ alloc_bignum(vmg_ digits); /* set the value */ set_double_val(ext_, val); } /* ------------------------------------------------------------------------ */ /* * Delete */ void CVmObjBigNum::notify_delete(VMG_ int in_root_set) { /* * free our extension - do this only if it's not in the root set, * because extension will be directly in the image data for a root * set object */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Allocate space for a given precision */ void CVmObjBigNum::alloc_bignum(VMG_ size_t digits) { /* allocate space for the given number of elements */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(calc_alloc(digits), this); /* set the precision */ set_prec(ext_, digits); /* initialize the value to zero */ set_int_val(ext_, 0); /* clear the flags */ ext_[VMBN_FLAGS] = 0; } /* ------------------------------------------------------------------------ */ /* * Write to a 'data' mode file */ int CVmObjBigNum::write_to_data_file(CVmDataSource *fp) { char buf[16]; /* write the number of digits (i.e., the precision) */ oswp2(buf, get_prec(ext_)); if (fp->write(buf, 2)) return 1; /* write our entire extension */ if (fp->write(ext_, calc_alloc(get_prec(ext_)))) return 1; /* success */ return 0; } /* * Read from a 'data' mode file and instantiate a new BigNumber object to * hold the result */ int CVmObjBigNum::read_from_data_file(VMG_ vm_val_t *retval, CVmDataSource *fp) { char buf[16]; size_t prec; CVmObjBigNum *bignum; /* read the precision */ if (fp->read(buf, 2)) return 1; prec = osrp2(buf); /* create a BigNumber with the required precision */ retval->set_obj(create(vmg_ FALSE, prec)); bignum = (CVmObjBigNum *)vm_objp(vmg_ retval->val.obj); /* read the bytes into the new object's extension */ if (fp->read(bignum->get_ext(), calc_alloc(prec))) return 1; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set my value to a string */ void CVmObjBigNum::set_str_val(const char *str, size_t len) { /* parse the string into my extension */ parse_str_into(ext_, str, len); } /* * Set my string value with a given radix. If the radix is decimal, we'll * use the regular floating point parser. Otherwise we'll parse it as an * integer value in the given radix. */ void CVmObjBigNum::set_str_val(const char *str, size_t len, int radix) { /* parse the string into my extension using the given radix */ parse_str_into(ext_, str, len, radix); } /* ------------------------------------------------------------------------ */ /* * IEEE 754-2008 binary interchange format buffer manipulation. We arrange * our buffer in little-endian order to match the idiosyncratic TADS * pack/unpack formats, but otherwise we use the standard IEEE formats. */ /* IEEE 754-2008 buffer */ struct ieee754 { ieee754(char *buf, int bits) { /* remember the buffer and bit size */ this->buf = (unsigned char *)buf; this->bits = bits; this->bytes = bits/8; /* get the mantissa size (in bits) for the format size */ mbits = (bits == 16 ? 10 : bits == 32 ? 23 : bits == 64 ? 52 : bits == 128 ? 112 : 0); if (mbits == 0) err_throw(VMERR_NO_DOUBLE_CONV); /* the exponent uses the leftover bits, minus the sign bit */ ebits = bits - mbits - 1; /* figure the exponent bias - it's the halfway point for the range */ ebias = (1 << (ebits - 1)) - 1; /* figure the exponent range */ emin = 1 - ebias; emax = ebias; } /* * Get the number of decimal digits this type can represent. The * decimal precision is fractional, since the IEEE type is binary; we * round up to the next integer. There's a */ int decimal_digits() { return bits == 16 ? 4 : bits == 32 ? 8 : bits == 64 ? 17 : bits == 128 ? 35 : 0; } /* get the maximum number of decimal digits in the exponent */ int decimal_exp_digits() { return ebits <= 5 ? 2 : ebits <= 7 ? 3 : ebits <= 11 ? 4 : 5; } /* is the value infinity? */ int is_infinity() const { /* infinity has an exponent of emax+1 and a zero mantissa */ return get_exp() == emax + 1 && is_mantissa_zero(); } /* set Infinity */ void set_infinity(int neg) { /* * Infinity is represented with the maximum exponent plus one, and * the mantissa set to all zeros. Infinities can be positive or * negative, so set the sign bit. */ memset(buf, 0, bytes); set_exp(emax + 1); set_sign(neg); } /* is the value zero? */ int is_zero() const { /* * a zero is represented by all bits zero except possibly the sign * bit, which is 0 for +0 and 1 for -0 */ int sum = 0; for (int i = 0 ; i < bytes - 1 ; ++i) sum += buf[i]; return sum == 0 && (buf[bytes-1] & 0x7f) == 0; } /* set Zero */ void set_zero(int neg) { /* * Zero is represented with an exponent of zero and all zero bits * in the mantissa. Zeros can be positive or negative, so set the * sign bit. */ memset(buf, 0, bytes); set_sign(neg); } /* is this a NAN value? */ int is_nan() const { /* a NAN value has an exponent of emax+1 and a non-zero mantissa */ return get_exp() == emax + 1 && !is_mantissa_zero(); } /* is the mantissa zero? */ int is_mantissa_zero() const { /* add up the full bytes of the mantissa, plus the partial high byte */ int imsb = mantissa_msb_index(); int sum = buf[imsb] & mantissa_msb_mask(); for (int i = 0 ; i < imsb ; sum += buf[i++]) ; /* the mantissa is zero if the sum of the bytes is zero */ return (sum == 0); } /* get the bit mask for the partial high-order byte of the mantissa */ unsigned char mantissa_msb_mask() const { if (ebits <= 7) return (0x7f >> ebits); else if (ebits <= 15) return (0xff >> (ebits - 7)); else { assert(FALSE); return 0; } } /* get the index of the most significant byte of the mantissa */ int mantissa_msb_index() const { if (ebits <= 7) return bytes - 1; else if (ebits <= 15) return bytes - 2; else { assert(FALSE); return 0; } } /* set NAN (not a number) */ void set_nan() { /* * NAN (Not A Number) is represented with the maximum exponent plus * one, and a non-zero mantissa. */ set_exp(emax + 1); set_mbit(0, 1); } /* get the sign bit - false -> positive, true -> negative */ int get_sign() const { return (buf[bytes-1] & 0x80) != 0; } /* set the sign bit */ void set_sign(int neg) { if (neg) buf[bytes-1] |= 0x80; else buf[bytes-1] &= 0x7f; } /* get the byte index and bit mask for a mantissa bit */ inline void get_mbit_ptr(int &byteidx, unsigned char &mask, int n) const { /* figure the raw bit vector index, past the sign and exponent bits */ n += 1 + ebits; /* get the byte index (little-endian order) */ byteidx = bytes - 1 - n/8; /* get the bit index within the byte, and the corresponding bit mask */ n = 7 - (n % 8); mask = 1 << n; } /* set the nth bit of the mantissa (0 is the high-order bit) */ void set_mbit(int n, int bit) { /* get the mantissa byte index and mask */ int byteidx; unsigned char mask; get_mbit_ptr(byteidx, mask, n); /* set or clear the bit */ if (bit) buf[byteidx] |= mask; else buf[byteidx] &= ~mask; } /* * Round up - add 1 to the lowest bit. Returns true if we carried to * out of the highest order bit, false if not. * * Carrying means that we carried into the implied "1" above the first * stored bit. That bumps that bit to 0 and carries to the next bit, * so we now have an implied "10" before the first bit. The caller * must renormalize simply by incrementing the exponent. (The caller * doesn't have to worry about whether this is a normal or subnormal * number, because bumping the exponent works for both. For * subnormals, we carry into the implied "0" above the first bit, which * bumps it to 1, which puts us in the normalized range, which we * represent by bumping the exponent up one to emin.) */ int round_up() { /* add to the low byte, and carry as needed to whole bytes */ int imsb = mantissa_msb_index(); for (int i = 0 ; i < imsb ; ++i) { if (buf[i] == 0xff) { /* this overflows the byte - zero it and carry to the next */ buf[i] = 0; } else { /* no overflow - increment this byte and return "no carry" */ buf[i] += 1; return FALSE; } } /* * if we got this far, we've carried into the most significant * byte, which is a partial byte - we need to use the msb bit mask * for this arithmetic */ unsigned char mask = mantissa_msb_mask(); int b = (buf[imsb] + 1) & mask; buf[imsb] &= ~mask; buf[imsb] |= b; /* if we wrapped that to zero, we carried into the implied lead bit */ return (b == 0); } int get_mbit(int n) const { /* get the mantissa byte index and mask */ int byteidx; unsigned char mask; get_mbit_ptr(byteidx, mask, n); /* return the bit value */ return (buf[byteidx] & mask) != 0; } /* set the exponent, adjusting for the bias */ void set_exp(int e) { /* add the bias, and set the raw exponent */ set_raw_exp(ebias + e); } /* set the raw exponent, without the bias adjustment */ void set_raw_exp(int e) { int mask; if (ebits <= 7) { /* it fits entirely in the first byte */ mask = (0xff << (7 - ebits)) & 0x7f; buf[bytes-1] &= ~mask; buf[bytes-1] |= (e << (7 - ebits)) & mask; } else if (ebits <= 15) { /* it fits in the first two bytes */ buf[bytes-1] &= 0x80; buf[bytes-1] |= (e >> (ebits - 7)) & 0x7f; mask = (0xff << (15 - ebits)) & 0xff; buf[bytes-2] &= ~mask; buf[bytes-2] |= (e << (15 - ebits)) & mask; } else { /* we don't currently handle anything above quad precision */ assert(FALSE); } } /* get the exponent, adjusting for the bias */ int get_exp() const { return get_raw_exp() - ebias; } /* get the raw exponent */ int get_raw_exp() const { int mask; if (ebits <= 7) { /* it fits entirely in the first byte */ mask = (0xff << (7 - ebits)) & 0x7f; return (int)(buf[bytes-1] & mask) >> (7 - ebits); } else if (ebits <= 15) { /* it fits in the first two bytes */ int e = (int)(buf[bytes-1] & 0x7f) << (ebits - 7); mask = (0xff << (15 - ebits)) & 0xff; return e | (int)(buf[bytes-2] & mask) >> (15 - ebits); } else { /* we don't currently handle anything above quad precision */ assert(FALSE); return 0; } } /* overall bit size (16, 32, 64, or 128) */ int bits; /* overall byte length */ int bytes; /* mantissa size in bits */ int mbits; /* exponent size in bits */ int ebits; /* minimum and maximum exponent values, and exponent bias */ int emin, emax; int ebias; /* our buffer - the caller provides this */ unsigned char *buf; }; /* ------------------------------------------------------------------------ */ /* * Convert to IEEE 754-2008 binary interchange format. 'bits' is the * number of bits in the format; we accept all of the standard format sizes * (16, 32, 64, 128). Our output conforms to the IEEE standard for the * format, except that we generate the buffer in the reverse byte order; * this is for consistency with the idiosyncratic TADS pack/unpack formats, * which use little-endian ordering for consistency with the standard TADS * integer interchange formats. To get the fully standard format, simply * reverse the byte order of the buffer we return. */ void CVmObjBigNum::convert_to_ieee754(VMG_ char *buf, int bits, int &ov) { /* no overflow yet */ ov = FALSE; /* set up the buffer descriptor */ ieee754 val(buf, bits); /* * Check for zero, infinity, and NAN. These all have special * representations that don't follow from the arithmetic algorithm * below. */ if (is_zero(ext_)) { val.set_zero(get_neg(ext_)); return; } else if (is_infinity(ext_)) { val.set_infinity(get_neg(ext_)); return; } else if (is_nan(ext_)) { val.set_nan(); return; } /* * Figure the precision we need for our intermediate calculations. Our * most demanding calculation is the log2 of the value; we need to be * able to represent the full precision of the binary type in the * *fractional* part of the log2 result. The log2 result can have up * to the maximum exponent as the whole part, so the precision we need * for this calculation is (decimal digits of mantissa) + (decimal * digits of exponent). Add a couple of guard digits as usual so that * we don't lose significant precision in rounding in intermediate * calculations. */ int prec = val.decimal_digits() + val.decimal_exp_digits() + 2; /* * Set up some registers for intermediate calculations. We don't have * to allocate these since we know the upper bound to the precision * we'll need (namely 35+2 for the 128-bit type). */ char t1[5+37] = { (char)prec, 0, 0, 0, 0 }; char t2buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t2 = t2buf; char t3buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t3 = t3buf; /* * The target format represents the number as A*2^B. Call our value N. * Observe that N == 2^(log2(N)) for any N. Let's separate out log2(N) * into its whole and fractional parts - call them W and F: so log2(N) * == W+F, which makes N == 2^(W+F) == 2^W * 2^F == 2^F * 2^W. Recall * that the format we're looking for is A*2^B, which we now have: A is * 2^F, and B is W. * * That will give us *a* binary representation of the number, but not * necessarily the canonical representation: we have to normalize by * choosing the exponent such that the implied (but not stored) first * bit of the mantissa is 1. The ln2 calculation will get us close; we * then need to adjust the exponent once we start pulling out the bits * of the mantissa. */ /* get/cache ln2 to the required precision */ const char *ln2 = cache_ln2(prec); /* * Get the absolute value of the argument. Our format and the IEEE * format both represent the value as a combination of an absolute * value and a sign, so the only thing we need to do with the sign is * to set the same sign on the result as on the input. For the log * calculation, though, we need to be working with a positive value. */ copy_val(t2, ext_, TRUE); set_neg(t2, FALSE); /* calculate t1=ln(self), t2=t1/ln(2) == ln2(self) */ compute_ln_into(t1, t2); compute_quotient_into(t2, 0, t1, ln2); /* * Get the whole part as an integer - this is the "B" value as above. * Note that this will definitely fit in a 32-bit integer type: * BigNumber stores absolute values in the range 10^-32768 to 10^32767, * so log2(self) can range from about -108000 to +108000. */ copy_val(t1, t2, FALSE); compute_whole(t1); int32_t b = convert_to_int_base(t1, ov); if (get_neg(t1)) b = -b; /* * Calculate 2^fractional part - this is the "A" value as above. 2^y = * e^(y*ln2), so calculate t2*ln2 into t3, then e^t3 back into t2. */ compute_frac(t2); compute_prod_into(t1, t2, ln2); compute_exp_into(t2, t1); /* * If 'b' is less than emin-1 (the subnormal exponent value), we need * to raise it back up to emin-1. Do this by halving the mantissa and * incrementing 'b', repeating until 'b' is emin-1. * * If 'b' is less than emin-1 by more than the bit precision of the * target type, we won't have any bits to store, so store zero. */ if (b < val.emin - 1 - val.mbits) { val.set_zero(get_neg(ext_)); return; } while (b < val.emin - 1) { div_by_long(t2, 2); b += 1; } /* * Extract the bits of the mantissa. Do this by repeatedly shifting * and subtracting: starting with divisor = 1, if decimal mantissa >= * divisor, subtract divisor and set the current bit in the output to * 1, otherwise set the output bit to 0; set divisor = divisor/2, * repeat until we've filled the output mantissa. A is 2^fraction, * where -1 < fraction < 1, so 0.5 < A < 2. * * Normalization requires that the output mantissa start with an * implied 1. This means that we ignore leading zeros; for each * leading zero, we decrement b but don't set an output bit. When we * reach the first 1 bit, we don't store it, either, since it's implied * by normalization. However, if we reach emin-1 for 'b', we're in the * subnormalization range, which means we can't decrement the exponent * any further; instead, start storing leading zeros. (Subnormal * means that the number is stored with an implied *0* leading bit, and * the special stored exponent value of emin-1, which implies an actual * exponent of emin.) */ copy_val(t1, (const char *)one_, FALSE); int mbit, sig; for (mbit = 0, sig = FALSE ; mbit < val.mbits ; div_by_long(t1, 2)) { /* the current bit is 1 if t2 >= t1 */ if (compare_abs(t2, t1) >= 0) { /* * it's a 1 bit - if it's the first 1 bit, note that we've * found a significant bit, but don't store it, since the first * bit is implied in the output format */ if (sig || b == val.emin - 1) val.set_mbit(mbit++, 1); else sig = TRUE; /* subtract t2 from t1 */ compute_abs_diff_into(t3, t2, t1); /* swap t3 and t2 to keep the accumulator named t2 */ char *ttmp = t2; t2 = t3; t3 = ttmp; } else { /* * It's a zero bit - store it if we've found a 1 already, OR * the exponent is already at the subnormal value. Otherwise * just decrement the exponent. */ if (sig || b == val.emin - 1) val.set_mbit(mbit++, 0); else --b; } } /* * Round the result: if the remainder is less than half of the last * bit's value (i.e., t2 < t1), round down (i.e., do nothing). If the * remainder is greater than half of the last bit's value (i.e. t2 > * t1), round up (add 1 to the low-order bit). If the remainder is * exactly half of the last bit's value, round up if the last bit was a * 1, down if it was a 0. */ int r = compare_abs(t2, t1); if (r > 0 || (r == 0 && val.get_mbit(val.mbits-1))) { /* round up; if that carries out, increment the exponent */ if (val.round_up()) ++b; } /* if 'b' wound up above the maximum for the format, it's an overflow */ if (b > val.emax) { val.set_infinity(get_neg(ext_)); ov = TRUE; return; } /* store the final exponent and sign */ val.set_exp(b); val.set_sign(get_neg(ext_)); } /* ------------------------------------------------------------------------ */ /* * Convert from IEEE 754-2008 format to BigNumber format. This takes an * IEEE 754-2008 binary interchange buffer in the given bit size (16, 32, * 64, or 128) and loads the value into the BigNumber. The buffer is in * the standard IEEE format, except that it's little endian, for * consistency with the TADS pack/unpack formats. To convert a buffer in * the fully standard IEEE format, simply reverse the byte order before * converting. */ void CVmObjBigNum::set_ieee754_value(VMG_ const char *buf, int bits) { /* set up the buffer descriptor */ ieee754 val((char *)buf, bits); /* check for special values */ if (val.is_zero()) { set_zero(ext_); set_neg(ext_, val.get_sign()); return; } else if (val.is_nan()) { set_type(ext_, VMBN_T_NAN); return; } else if (val.is_infinity()) { set_type(ext_, VMBN_T_INF); set_neg(ext_, val.get_sign()); return; } /* * figure the precision for our intermediate calculations based on the * precision of the source format in decimal digits */ int prec = val.decimal_digits() + 2; /* * Set up some registers for intermediate calculations. We don't have * to allocate these since we know the upper bound to the precision * we'll need (namely 35+2 for the 128-bit type). */ char t1[5+37] = { (char)prec, 0, 0, 0, 0 }; char t2buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t2 = t2buf; char t3buf[5+37] = { (char)prec, 0, 0, 0, 0 }, *t3 = t3buf; /* * The IEEE value is represented as A * 2^B, for some non-zero value of * A. If the exponent is emin-1, A is represented as 0.bbbbb, where * the b's are bits of the mantissa. For any other exponent, A is * represented as 1.bbbbb. To compute our value, then, start with 2^B * in register t1 and zero in the accumulator. If the leading bit is 1 * (exponent >= emin), add r1 to the accumulator. Divide t1 by 2 and * loop: if the current bit is set, add t1 to the accumulator. Repeat * for each bit of the mantissa. */ /* calculate t1 = 2^B = e^(B*ln2) */ const char *ln2 = cache_ln2(prec); int e = val.get_exp(); set_int_val(t1, e); compute_prod_into(t2, t1, ln2); compute_exp_into(t1, t2); /* * If b > emin-1, there's an implied 1 bit for the whole part. If b == * emin-1, we have a subnormal number, meaning that the implied lead * bit is 0, and the actual exponent is val.emin. */ if (e > val.emin - 1) { /* normalized - add the implied leading "1" bit */ copy_val(t3, t1, FALSE); } else { /* * subnormal - the implied leading bit is "0", but the actual * exponent value is emin - one higher than it appears */ set_zero(t3); mul_by_long(t1, 2); } /* add each bit of the mantissa */ for (int i = 0 ; i < val.mbits ; ++i) { /* divide the power-of-two multiplier by 2 for the next lower place */ div_by_long(t1, 2); /* if this mantissa bit is 1, add t1 to the accumulator */ if (val.get_mbit(i)) { /* t2 <- acc + power of two */ compute_sum_into(t2, t3, t1); /* swap t2 and t3 so that t3 is the accumulator again */ char *ttmp = t3; t3 = t2; t2 = ttmp; } } /* set the sign and normalize the value */ set_neg(t3, val.get_sign()); normalize(t3); /* copy the result into our extension */ copy_val(ext_, t3, TRUE); } /* ------------------------------------------------------------------------ */ /* * Create a string representation of the number */ const char *CVmObjBigNum::cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* use my static method */ return cvt_to_string(vmg_ self, new_str, ext_, 100, -1, -1, -1, 0, 0); } /* * Convert to string with a given radix */ const char *CVmObjBigNum::explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* * If they want a non-decimal conversion, and we're a whole integer * without a fractional part, generate the integer representation in * the given radix. If we have a fractional part, or the requested * radix is decimal, use the floating-point formatter instead. * * Also use an integer conversion if we've been specfically asked for * an integer conversion. */ if ((radix != 10 && is_frac_zero(ext_)) || (flags & TOSTR_ROUND) != 0) { /* * it's all integer, and we have a non-decimal radix - format as an * integer */ return cvt_to_string_in_radix(vmg_ self, new_str, radix); } else { /* * no radix, or we have a fraction - use the basic floating-point * formatter with default options */ return cvt_to_string(vmg_ self, new_str, ext_, 100, -1, -1, -1, 0, 0); } } /* ------------------------------------------------------------------------ */ /* * String buffer allocator */ char *CBigNumStringBufAlo::get_buf(size_t need) { /* use the fixed buffer if it's big enough */ if (len >= need) return buf; /* the fixed buffer isn't big enough, so allocate a new string */ if (strval != 0) { VMGLOB_PTR(vmg); return CVmObjString::alloc_str_buf(vmg_ strval, 0, 0, need); } /* the buffer's too small, and we can't allocate, so fail */ return 0; } /* ------------------------------------------------------------------------ */ /* * convert to a string, storing the result in the given buffer if it'll * fit, otherwise in a new string */ const char *CVmObjBigNum::cvt_to_string_buf( VMG_ vm_val_t *new_str, char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* set up an allocator */ CBigNumStringBufAlo alo(vmg_ new_str, buf, buflen); /* convert to a string into our buffer */ return cvt_to_string_gen( &alo, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* ------------------------------------------------------------------------ */ /* * Convert to a string, creating a new string object to hold the result */ const char *CVmObjBigNum::cvt_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, vm_val_t *lead_fill) { /* push a self-reference for gc protection during allocation */ G_stk->push()->set_obj(self); /* set up an allocator */ CBigNumStringBufAlo alo(vmg_ new_str); /* if there's a lead fill parameter, get its string value */ const char *lead_fill_str = 0; size_t lead_fill_len = 0; if (lead_fill != 0 && lead_fill->typ != VM_NIL) { /* get the string value */ lead_fill_str = lead_fill->get_as_string(vmg0_); /* if it's not a string, it's an error */ if (lead_fill_str == 0) err_throw(VMERR_STRING_VAL_REQD); /* read and skip the length prefix */ lead_fill_len = vmb_get_len(lead_fill_str); lead_fill_str += VMB_LEN; /* if the length is zero, ignore the lead fill string entirely */ if (lead_fill_len == 0) lead_fill_str = 0; } /* * convert to a string - don't pass in a buffer, since we want to * create a new string to hold the result */ const char *ret = cvt_to_string_gen( &alo, ext, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill_str, lead_fill_len); /* discard our gc protection */ G_stk->discard(); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Convert an integer value to a string in the given radix (2-36) */ const char *CVmObjBigNum::cvt_to_string_in_radix( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix) const { /* check for special values: zeros, NaNs, and infinities */ const char *fixed = 0; int neg = get_neg(ext_); if (is_zero(ext_)) { /* show zero as "0" in all bases */ fixed = "0"; } else if (is_infinity(ext_)) { /* * show infinity as "1.#INF" for positive or unsigned, "-1.#INF" * for negative infinity */ fixed = (neg ? "-1.#INF" : "1.#INF"); } else if (is_nan(ext_)) { /* not a number */ fixed = "1.#NAN"; } else { /* make a temporary copy of the number */ uint thdl; char *tmp = alloc_temp_reg(get_prec(ext_), &thdl); err_try { /* copy the value into our temporary register */ copy_val(tmp, ext_, FALSE); /* if there's a fractional part, round to integer */ round_to_int(tmp); /* * It's an ordinary non-zero number. Figure how many digits * the integer portion will take in the given radix. This is * fairly simple: a D digit number in decimal requires * D/log10(R) digits in base R, rounding up. Our storage * format makes it easy to determine D - it's simply our * base-10 exponent. */ int len = (int)ceil(get_exp(ext_) / log10((double)radix)); /* if the value is zero, we need one digit for the zero */ if (len == 0) len = 1; /* if it's negative, add space for the '-' */ if (neg) len += 1; /* create the string */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, len)); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ new_str->val.obj); char *buf = str->cons_get_buf(), *p = buf; /* add the '-' sign if negative */ if (neg) { *p++ = '-'; ++buf; --len; } /* generate the digits */ while (!is_zero(tmp) && p - buf < len) { /* get the next digit */ unsigned long rem; div_by_long(tmp, radix, &rem); /* add it to the output */ *p++ = (char)(rem < 10 ? rem + '0' : rem - 10 + 'A'); } /* * We just generated the digits in reverse order. Reverse the * string so that they're in the right order. */ char *p1, *p2, tmp; for (p1 = buf, p2 = p - 1 ; p2 > p1 ; --p2, ++p1) tmp = *p1, *p1 = *p2, *p2 = tmp; /* if we generated no digits, generate a zero */ if (p == buf) *p++ = '0'; /* set the final length, in case rounding left us off by one */ str->cons_set_len(p - str->cons_get_buf()); } err_finally { /* release our temporary register */ release_temp_reg(thdl); } err_end; } /* if we came up with a fixed source string, create the string object */ if (fixed != 0) new_str->set_obj(CVmObjString::create( vmg_ FALSE, fixed, strlen(fixed))); /* return the new string buffer */ return new_str->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjBigNum::save_to_file(VMG_ class CVmFile *fp) { size_t prec; /* get our precision */ prec = get_prec(ext_); /* write the data */ fp->write_bytes(ext_, calc_alloc(prec)); } /* * restore from a file */ void CVmObjBigNum::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *) { size_t prec; /* read the precision */ prec = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate the space */ alloc_bignum(vmg_ prec); /* store our precision */ set_prec(ext_, prec); /* read the contents */ fp->read_bytes(ext_ + VMBN_EXP, calc_alloc(prec) - VMBN_EXP); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjBigNum::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no properties to set */ err_throw(VMERR_INVALID_SETPROP); } /* * get a static property */ int CVmObjBigNum::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* translate the property into a function vector index */ switch(G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop)) { case VMOBJBN_GET_PI: return s_getp_pi(vmg_ result, argc); case VMOBJBN_GET_E: return s_getp_e(vmg_ result, argc); default: /* * we don't define this one - inherit the default from the base * object metaclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* * get a property */ int CVmObjBigNum::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the function */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* * Property evaluator - formatString */ int CVmObjBigNum::getp_format(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int orig_argc = (argc != 0 ? *argc : 0); int max_digits = -1; uint flags = 0; int whole_places = -1; int frac_digits = -1; int exp_digits = -1; vm_val_t *lead_fill = 0; static CVmNativeCodeDesc desc(0, 6); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the maximum digit count */ if (orig_argc >= 1) max_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* get the flags */ if (orig_argc >= 2) flags = CVmBif::pop_int_or_nil(vmg_ 0); /* get the whole places */ if (orig_argc >= 3) whole_places = CVmBif::pop_int_or_nil(vmg_ -1); /* get the fraction digits */ if (orig_argc >= 4) frac_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* get the exponent digits */ if (orig_argc >= 5) exp_digits = CVmBif::pop_int_or_nil(vmg_ -1); /* * get the lead fill string if provided (leave it on the stack to * protect against garbage collection) */ if (orig_argc >= 6) lead_fill = G_stk->get(0); /* format the number, which will build the return string */ cvt_to_string(vmg_ self, retval, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill); /* discard the lead fill string, if provided */ if (lead_fill != 0) G_stk->discard(); /* handled */ return TRUE; } /* * Property eval - equal after rounding */ int CVmObjBigNum::getp_equal_rnd(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the value to compare */ G_stk->pop(&val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, &val2)) { /* it's not a BigNumber, so it's not equal */ retval->set_nil(); } else { int ret; /* test for equality */ ret = compute_eq_round(vmg_ ext_, get_objid_ext(vmg_ val2.val.obj)); /* set the return value */ retval->set_logical(ret); } /* handled */ return TRUE; } /* * property eval - get the precision */ int CVmObjBigNum::getp_get_prec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return an integer giving my precision */ retval->set_int(get_prec(ext_)); /* handled */ return TRUE; } /* * property eval - set the precision */ int CVmObjBigNum::getp_set_prec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { size_t digits; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the number of digits for rounding */ digits = CVmBif::pop_int_val(vmg0_); /* if I'm not a number, return myself unchanged */ if (is_nan(ext_)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* create the rounded value */ round_val(vmg_ retval, ext_, digits, TRUE); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * get pi (static method) */ int CVmObjBigNum::s_getp_pi(VMG_ vm_val_t *val, uint *argc) { size_t prec; char *new_ext; const char *pi; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the precision argument */ prec = CVmBif::pop_int_val(vmg0_); /* allocate the result */ val->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ val->val.obj); /* cache pi to the required precision */ pi = cache_pi(prec); /* return the value */ copy_val(new_ext, pi, TRUE); /* handled */ return TRUE; } /* * get e (static method) */ int CVmObjBigNum::s_getp_e(VMG_ vm_val_t *val, uint *argc) { size_t prec; char *new_ext; const char *e; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the precision argument */ prec = CVmBif::pop_int_val(vmg0_); /* allocate the result */ val->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ val->val.obj); /* cache e to the required precision */ e = cache_e(prec); /* return the value */ copy_val(new_ext, e, TRUE); /* handled */ return TRUE; } /* * Set up for a zero-argument operation that returns a BigNumber result. * Returns true if the argument check indicates that the caller should * simply return to its caller, false if the caller should proceed. * * After checking the argument count, we'll proceed to set up the return * value as per setup_getp_retval(). */ int CVmObjBigNum::setup_getp_0(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the return value */ return setup_getp_retval(vmg_ self, retval, new_ext, get_prec(ext_)); } /* * Set up for a one-argument operation that takes a BigNumber value as * the argument and returns a BigNumber result. Does the work of * setup_getp_0, but also pops the argument value and converts it to a * BigNumber (throwing an error if the value is not convertible). * * Fills in val2 with the argument value, and fills in *ext2 with val2's * extension buffer. * * The result value will have the larger of the precisions of self and * the other value, unless use_self_prec is set, in which case we'll use * self's precision unconditionally. * * If either argument is not a number, we'll set the return value to the * not-a-number argument unchanged, and return true. */ int CVmObjBigNum::setup_getp_1(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, char **new_ext, vm_val_t *val2, const char **ext2, int use_self_prec) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the argument value */ G_stk->pop(val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, val2)) { /* it's not a BigNumber - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the other value's extension */ *ext2 = get_objid_ext(vmg_ val2->val.obj); /* if the other value is not a number, return it as the result */ if (is_nan(*ext2)) { retval->set_obj(val2->val.obj); return TRUE; } /* * if val2's precision is higher than ours, use it, unless we've * been specifically told to use our own precision for the result */ size_t prec = get_prec(ext_); if (!use_self_prec && get_prec(*ext2) > prec) prec = get_prec(*ext2); /* push the other result to protect it from garbage collection */ G_stk->push(val2); /* allocate the return value */ if (setup_getp_retval(vmg_ self, retval, new_ext, prec)) { /* discard the val2 reference we pushed for gc protection */ G_stk->discard(); /* tell the caller we're done */ return TRUE; } /* tell the caller to proceed */ return FALSE; } /* * Set up for an operation that returns a BigNumber result. Returns * true if we finished the operation, in which case the caller should * simply return; returns false if the operation should still be carried * out, in which case the caller should proceed as normal. * * If the 'self' value is not-a-number, we'll return it as the result * (and return true to indicate that no further processing is required). * * If we return false, we'll have pushed a reference to 'self' onto the * stack for protection against garbage collection. The caller must * discard this reference before returning. We push no such * self-reference if we return true. * * In addition, if we return false, we'll fill in '*new_ext' with a * pointer to the extension buffer for the return value object that we * allocate. We'll allocate the return value with the same precision as * 'self'. * * Note that the caller should ensure that any argument sare removed * from the stack before calling this routine, since we might push the * self-reference onto the stack. */ int CVmObjBigNum::setup_getp_retval(VMG_ vm_obj_id_t self, vm_val_t *retval, char **new_ext, size_t prec) { /* if I'm not a number, return myself unchanged */ if (is_nan(ext_)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* create a new number with the same precision as the original */ retval->set_obj(create(vmg_ FALSE, prec)); *new_ext = get_objid_ext(vmg_ retval->val.obj); /* tell the caller to proceed */ return FALSE; } /* * property eval - get the fractional part */ int CVmObjBigNum::getp_frac(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object, and get the fractional portion */ memcpy(new_ext, ext_, calc_alloc(get_prec(ext_))); compute_frac(new_ext); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - get the whole part, with no rounding */ int CVmObjBigNum::getp_whole(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object, and get the whole part */ memcpy(new_ext, ext_, calc_alloc(get_prec(ext_))); compute_whole(new_ext); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - round to a given number of decimal places */ int CVmObjBigNum::getp_round_dec(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int places; char *new_ext; int post_idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the number of digits for rounding */ places = CVmBif::pop_int_val(vmg0_); /* set up the return value */ if (setup_getp_retval(vmg_ self, retval, &new_ext, prec)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* * Determine if the first digit we're lopping off is actually * represented in the number or not. This digit position is the * exponent plus the number of decimal places after the decimal to * keep - if this value is at least zero and less than the * precision, it's part of the number. */ post_idx = places + exp; if (post_idx < 0) { /* * the digit after the last digit to keep is actually before the * beginning of the number, so the result of the rounding is * simply zero */ set_zero(new_ext); } else if (post_idx >= (int)prec) { /* * the digit after the last digit to keep is past the end of the * represented digits, so rounding has no effect at all - we'll * simply return the same number */ } else { /* round it */ round_to(new_ext, post_idx); /* normalize the result */ normalize(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - get the absolute value */ int CVmObjBigNum::getp_abs(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* compute the absolute value */ abs_val(vmg_ retval, self); /* handled */ return TRUE; } /* * absolute value */ int CVmObjBigNum::abs_val(VMG_ vm_val_t *retval, vm_obj_id_t self) { /* * If I'm not an ordinary number or an infinity, or I'm already * non-negative, return myself unchanged. Note that we change negative * infinity to infinity, even though this might not make a great deal * of sense mathematically. */ if (!get_neg(ext_) || (get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF)) { retval->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* * if I'm negative infinity, we don't need any precision in the new * value; otherwise use my original precision */ size_t prec = (is_infinity(ext_) ? 1 : get_prec(ext_)); /* create a new number with the same precision as the original */ retval->set_obj(create(vmg_ FALSE, prec)); char *new_ext = get_objid_ext(vmg_ retval->val.obj); /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* set the sign to positive */ set_neg(new_ext, FALSE); /* remove my self-reference */ G_stk->discard(); /* success */ return TRUE; } /* * compute the sgn value */ int CVmObjBigNum::sgn_val(VMG_ vm_val_t *retval, vm_obj_id_t self) { /* figure the sgn value */ if (is_zero(ext_)) retval->set_int(0); else if (get_neg(ext_)) retval->set_int(-1); else retval->set_int(1); /* success */ return TRUE; } /* * property eval - ceiling (least integer greater than or equal to self) */ int CVmObjBigNum::getp_ceil(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); int frac_zero; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* determine if the fractional part is non-zero */ frac_zero = is_frac_zero(new_ext); /* check what we have */ if (exp <= 0) { /* * it's an entirely fractional number - the result is zero if * the number is negative or zero, one if the number is positive */ if (get_neg(new_ext) || frac_zero) { /* -1 < x <= 0 -> ceil(x) = 0 */ set_zero(new_ext); } else { /* 0 < x < 1 -> ceil(x) = 1 */ copy_val(new_ext, get_one(), FALSE); } } else { /* clear digits after the decimal point */ for (idx = (size_t)exp ; idx < prec ; ++idx) set_dig(new_ext, idx, 0); /* * if there's a fractional part and it's positive, increment the * number */ if (!frac_zero && !get_neg(new_ext)) increment_abs(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - floor (greatest integer <= self) */ int CVmObjBigNum::getp_floor(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t idx; int exp = get_exp(ext_); size_t prec = get_prec(ext_); int frac_zero; /* check arguments and allocate the result value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* determine if the fractional part is non-zero */ frac_zero = is_frac_zero(new_ext); /* check what we have */ if (exp <= 0) { /* * it's an entirely fractional number - the result is zero if * the number is positive or zero, -1 if the number is negative */ if (!get_neg(new_ext) || frac_zero) { /* 0 <= x < 1 -> floor(x) = 0 */ set_zero(new_ext); } else { /* -1 < x < 0 -> floor(x) = -1 */ copy_val(new_ext, get_one(), FALSE); set_neg(new_ext, TRUE); } } else { /* clear digits after the decimal point */ for (idx = (size_t)exp ; idx < prec ; ++idx) set_dig(new_ext, idx, 0); /* * if there's a fractional part and the number is negative, * increment the number's absolute value */ if (!frac_zero && get_neg(new_ext)) increment_abs(new_ext); } /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - getScale */ int CVmObjBigNum::getp_get_scale(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* check the type */ if (is_nan(ext_)) { /* it's not a number - return nil for the scale */ retval->set_nil(); } else { /* return an integer giving the number's scale */ retval->set_int(get_exp(ext_)); } /* handled */ return TRUE; } /* * property eval - scale */ int CVmObjBigNum::getp_scale(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); int scale; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the scaling argument */ scale = CVmBif::pop_int_val(vmg0_); /* set up the return value */ if (setup_getp_retval(vmg_ self, retval, &new_ext, prec)) return TRUE; /* copy the value */ memcpy(new_ext, ext_, calc_alloc(prec)); /* adjust the exponent by the scale factor */ set_exp(new_ext, get_exp(new_ext) + scale); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - negate */ int CVmObjBigNum::getp_negate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* negate the value */ neg_val(vmg_ retval, self); /* handled */ return TRUE; } /* * property eval - copy sign */ int CVmObjBigNum::getp_copy_sign(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; char *new_ext; const char *ext2; size_t prec = get_prec(ext_); if (setup_getp_1(vmg_ self, retval, argc, &new_ext, &val2, &ext2, TRUE)) return TRUE; /* make a copy of my value in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* set the sign from the other object */ set_neg(new_ext, get_neg(ext2)); /* * normalize it (this is important when the value was zero to start * with, since zero is always represented without a negative sign) */ normalize(new_ext); /* remove the GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * property eval - isNegative */ int CVmObjBigNum::getp_is_neg(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* if I'm not an ordinary number or an infinity, I'm not negative */ if (get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF) { /* I'm not negative, so return nil */ retval->set_nil(); } else { /* set the return value to true if I'm negative, nil if not */ retval->set_logical(get_neg(ext_)); } /* handled */ return TRUE; } /* * property eval - remainder */ int CVmObjBigNum::getp_remainder(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; const char *ext2; char *quo_ext; char *rem_ext; vm_val_t rem_val; vm_val_t quo_val; CVmObjList *lst; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the divisor */ G_stk->pop(&val2); /* convert it to BigNumber */ if (!cvt_to_bignum(vmg_ self, &val2)) { /* it's not a BigNumber - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the divisor's extension */ ext2 = get_objid_ext(vmg_ val2.val.obj); /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* create a quotient result value, and push it for safekeeping */ quo_ext = compute_init_2op(vmg_ &quo_val, ext_, ext2); G_stk->push(&quo_val); /* create a remainder result value, and push it for safekeeping */ rem_ext = compute_init_2op(vmg_ &rem_val, ext_, ext2); G_stk->push(&rem_val); /* * create a list for the return value - it will have two elements: * the quotient and the remainder */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 2)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the list elements */ lst->cons_set_element(0, &quo_val); lst->cons_set_element(1, &rem_val); /* calculate the quotient */ compute_quotient_into(quo_ext, rem_ext, ext_, ext2); /* remove the GC protection */ G_stk->discard(4); /* handled */ return TRUE; } /* * property eval - sine */ int CVmObjBigNum::getp_sin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7; int neg_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi */ pi = cache_pi(prec + 3); /* * Allocate our temporary registers. We'll use 1 and 2 to calculate * x^n - we'll start with x^(n-2) in one, and multiply by x^2 to put * the result in the other. 3 we'll use to store n!. 4 we'll use * to store the result of x^n/n!, and 5 and 6 we'll swap as the * master accumulator. 7 we'll use to store x^2. * * Allocate the temporary registers with more digits of precision * than we need in the result, to ensure that accumulated rounding * errors don't affect the result. */ alloc_temp_regs(prec + 3, 7, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* * Note that sin(-x) = -sin(x). If x < 0, note the negative * sign and then negate the value so that we calculate sin(x) * and return the negative of the result. */ neg_result = get_neg(ext1); set_neg(ext1, FALSE); /* calculate 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); /* * Because we'll use a Taylor series around 0 to calculate the * result, we want our value as close to the expansion point (0) * as possible to speed up convergence of the series. * Fortunately this is especially easy for sin() because of the * periodic nature of the function. * * First, note that sin(2*pi*i + x) = sin(x) for all integers i, * so we can reduce the argument mod 2*pi until it's in the * range 0 <= x < 2*pi (we might have to do this multiple times * if the number's scale exceeds its precision). Note that we * already made sure the number is positive. */ while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that sin(x+pi) = -sin(x). If x > pi, negate the * result (again if necessary) and reduce the argument by pi. * This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that sin(x + pi) = -sin(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* * Next, observe that sin(x+pi/2) = cos(x). If x > pi/4, * calculate using the cosine series instead of the sine series. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* * subtract pi/2 - this will give us a value in the range * -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* cos(-x) = cos(x), so we can ignore the sign */ set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* calculate the cosine series */ calc_cos_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } else { /* * We now have a value in the range 0 <= x <= pi/4, which * will converge quickly with our Taylor series */ calc_sin_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } /* negate the result if necessary */ if (neg_result) set_neg(new_ext, !get_neg(new_ext)); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(7, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - cosine. This works very much the same way as * getp_sin() - refer to the more extensive comments in that routine for * more detail on the algorithm. */ int CVmObjBigNum::getp_cos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7; int neg_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* allocate our temporary registers, as per sin() */ alloc_temp_regs(prec + 3, 7, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* presume the result sign will be correct */ neg_result = FALSE; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* note that cos(-x) = cos(x) - if x < 0, use -x */ set_neg(ext1, FALSE); /* reduce the argument modulo 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that cos(x+pi) = -cos(x). If x > pi, negate the * result (again if necessary) and reduce the argument by pi. * This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that cos(x + pi) = -cos(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ set_neg(ext1, FALSE); /* * Next, observe that cos(x+pi/2) = -sin(x). If x > pi/4, * calculate using the sine series instead of the cosine series. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* negate the result */ neg_result = !neg_result; /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* * subtract pi/2 - this will give us a value in the range * -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* sin(-x) = -sin(x) */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* calculate the sine series */ calc_sin_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } else { /* * We now have a value in the range 0 <= x <= pi/4, which * will converge quickly with our Taylor series */ calc_cos_series(vmg_ new_ext, ext1, ext2, ext3, ext4, ext5, ext6, ext7); } /* negate the result if necessary */ if (neg_result) set_neg(new_ext, !get_neg(new_ext)); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(7, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property eval - tangent. We calculate the sine and cosine, then * compute the quotient to determine the tangent. This routine works * very much like the sin() and cos() routines; refer to getp_sin() for * more thorough documentation. */ int CVmObjBigNum::getp_tan(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7, hdl8, hdl9; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6, *ext7, *ext8, *ext9; int neg_result; int invert_result; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to the working precision */ pi = cache_pi(prec + 3); /* * Allocate our temporary registers for sin() and cos() * calculations, plus two extra: one to hold the sine while we're * calculating the cosine, and the other to hold the cosine result. * * (We could make do with fewer registers by copying values around, * but if the numbers are of very high precision it's much cheaper * to allocate more registers, since the registers come out of the * register cache and probably won't require any actual memory * allocation.) */ alloc_temp_regs(prec + 3, 9, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6, &ext7, &hdl7, &ext8, &hdl8, &ext9, &hdl9); /* protect against errors so we definitely free the registers */ err_try { char *tmp; /* presume we won't have to invert our result */ invert_result = FALSE; /* copy our initial value to ext1 */ copy_val(ext1, ext_, FALSE); /* note that tan(-x) = -tan(x) - if x < 0, use -x */ neg_result = get_neg(ext1); set_neg(ext1, FALSE); /* reduce the argument modulo 2*pi */ copy_val(ext7, pi, TRUE); mul_by_long(ext7, 2); while (compare_abs(ext1, ext7) > 0) { /* divide by 2*pi, storing the remainder in r2 */ compute_quotient_into(ext6, ext2, ext1, ext7); /* swap r2 into r1 for the next round */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Next, note that tan(x+pi) = tan(x). So, if x > pi, the * argument by pi. This will reduce our range to 0 <= x <= pi. */ copy_val(ext7, pi, TRUE); if (compare_abs(ext1, ext7) > 0) { /* subtract pi from the argument */ compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * Use the fact that tan(x + pi) = tan(x) once again: if x > * pi/2, subtract pi from x to adjust the range to -pi/2 <= x <= * pi/2. */ div_by_long(ext7, 2); if (compare_abs(ext1, ext7) > 0) { /* subtract pi from the argument */ copy_val(ext7, pi, TRUE); compute_abs_diff_into(ext2, ext1, ext7); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * once again, reduce our range using the sign equivalence - * this will limit our range to 0 <= x <= pi/2 */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* * Next, observe that tan(x+pi/2) = 1/tan(x). If x > pi/4, * invert the result. */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 4); if (compare_abs(ext1, ext7) > 0) { /* calculate pi/2 */ copy_val(ext7, pi, TRUE); div_by_long(ext7, 2); /* subtract pi/2 to get into range -pi/4 <= x <= pi/4 */ compute_abs_diff_into(ext2, ext1, ext7); /* sin(-x) = -sin(x) */ if (get_neg(ext1)) neg_result = !neg_result; set_neg(ext1, FALSE); /* swap the result into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* note that we must invert the result */ invert_result = TRUE; } /* * make a copy of our argument value in ext9 for safekeeping * while we're calculating the sine */ copy_val(ext9, ext1, FALSE); /* * We now have a value in the range 0 <= x <= pi/4, which will * converge quickly with our Taylor series for sine and cosine. * This will also ensure that both sin and cos are non-negative, * so the sign we calculated for the tangent is all that matters * for the sign. * * First, Calculate the sine and store the result in r8. */ calc_sin_series(vmg_ ext8, ext1, ext2, ext3, ext4, ext5, ext6, ext7); /* * Calculate the cosine and store the result in r1. Note that * we saved the argument value in ext9 while we were working on * the sine, so we can now use that value as the argument here. * ext1 was trashed by the sine calculation, so just use it as * the output register here. */ calc_cos_series(vmg_ ext1, ext9, ext2, ext3, ext4, ext5, ext6, ext7); /* calculate the quotient sin/cos, or cos/sin if inverted */ if (invert_result) compute_quotient_into(new_ext, 0, ext1, ext8); else compute_quotient_into(new_ext, 0, ext8, ext1); /* negate the result if necessary */ set_neg(new_ext, neg_result); /* normalize the result */ normalize(new_ext); } err_finally { /* release our temporary registers */ release_temp_regs(9, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6, hdl7, hdl8, hdl9); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - convert degrees to radians */ int CVmObjBigNum::getp_deg2rad(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1; char *ext1; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our required precision */ pi = cache_pi(prec + 2); /* * allocate a temporary register for pi/180 - give it some extra * precision */ alloc_temp_regs(prec + 2, 1, &ext1, &hdl1); /* get pi to our precision */ copy_val(ext1, pi, TRUE); /* divide pi by 180 */ div_by_long(ext1, 180); /* multiply our value by pi/180 */ compute_prod_into(new_ext, ext_, ext1); /* release our temporary registers */ release_temp_regs(1, hdl1); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - convert radians to degrees */ int CVmObjBigNum::getp_rad2deg(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1; char *ext1; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 2); /* allocate a temporary register for pi/180 */ alloc_temp_regs(prec + 2, 1, &ext1, &hdl1); /* catch errors so we can be sure to free registers */ err_try { /* get pi to our precision */ copy_val(ext1, pi, TRUE); /* divide pi by 180 */ div_by_long(ext1, 180); /* divide by pi/180 */ compute_quotient_into(new_ext, 0, ext_, ext1); } err_finally { /* release our temporary registers */ release_temp_regs(1, hdl1); } err_end; /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - arcsine */ int CVmObjBigNum::getp_asin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate and return the arcsine */ return calc_asincos(vmg_ self, retval, argc, FALSE); } /* * property evaluator - arccosine */ int CVmObjBigNum::getp_acos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate and return the arcsine */ return calc_asincos(vmg_ self, retval, argc, TRUE); } /* * Common property evaluator routine - arcsine and arccosine. arcsin * and arccos are related by arccos(x) = pi/2 - arcsin(x). So, to * calculate an arccos, we can simply calculate the arcsin, then * subtract the result from pi/2. */ int CVmObjBigNum::calc_asincos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_acos) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* calculate the arcsin/arccos into the result */ calc_asincos_into(new_ext, ext_, is_acos); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * Calculate the arcsine or arccosine into the given result variable */ void CVmObjBigNum::calc_asincos_into(char *dst, const char *src, int is_acos) { size_t prec = get_prec(dst); uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; const char *pi; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* * allocate our temporary registers - use some extra precision over * what we need for the result, to reduce the effect of accumulated * rounding error */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we release our temp registers */ err_try { char *tmp; static const unsigned char one_over_sqrt2[] = { /* precision = 10, scale = 0, flags = 0 */ 10, 0, 0, 0, 0, 0x70, 0x71, 0x06, 0x78, 0x14 }; int use_sqrt; int sqrt_neg; /* get the initial value of x into our accumulator, r1 */ copy_val(ext1, src, FALSE); /* * check to see if the absolute value of the argument is greater * than 1 - if it is, it's not valid */ copy_val(ext2, get_one(), FALSE); if (compare_abs(ext1, ext2) > 0) err_throw(VMERR_OUT_OF_RANGE); /* presume we won't need to use the sqrt(1-x^2) method */ use_sqrt = FALSE; /* * Check to see if the absolute value of the argument is greater * than 1/sqrt(2). If it is, the series expansion converges too * slowly (as slowly as never, if the value is exactly 1). To * speed things up, in these cases calculate pi/2 - * asin(sqrt(1-x^2)) instead, which is equivalent but gives us a * smaller asin() argument for quicker series convergence. * * We don't need to compare to 1/sqrt(2) in great precision; * just use a few digits. */ copy_val(ext2, (const char *)one_over_sqrt2, TRUE); if (compare_abs(ext1, ext2) > 0) { /* flag that we're using the sqrt method */ use_sqrt = TRUE; /* note the sign - we'll need to apply this to the result */ sqrt_neg = get_neg(ext1); /* compute x^2 into r2 */ compute_prod_into(ext2, ext1, ext1); /* subtract r2 from 1 (by adding -r2 to 1), storing in r4 */ copy_val(ext3, get_one(), FALSE); make_negative(ext2); compute_sum_into(ext4, ext3, ext2); /* compute sqrt(1-x^2) (which is sqrt(r4)) into r1 */ compute_sqrt_into(ext1, ext4); } /* compute the arcsine */ ext1 = calc_asin_series(ext1, ext2, ext3, ext4, ext5); /* if we're using the sqrt method, finish the sqrt calculation */ if (use_sqrt) { /* calculate pi/2 */ copy_val(ext2, pi, TRUE); div_by_long(ext2, 2); /* compute pi/2 - r1 by negating r1 and adding it */ negate(ext1); compute_sum_into(ext3, ext2, ext1); /* negate the result if the original value was negative */ if (sqrt_neg) negate(ext3); /* swap the result back into r1 */ tmp = ext1; ext1 = ext3; ext3 = tmp; } /* * We now have the arcsine in r1. If we actually wanted the * arccosine, subtract the arcsine from pi/2 to yield the * arccosine. */ if (is_acos) { /* get pi/2 into r2 */ copy_val(ext2, pi, TRUE); div_by_long(ext2, 2); /* negate r1 to get -asin */ negate(ext1); /* add -asin to r2 to yield the arccosine in r3 */ compute_sum_into(ext3, ext2, ext1); /* swap the result back into ext1 */ tmp = ext3; ext3 = ext1; ext1 = tmp; } /* store the result, rounding if necessary */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; } /* * Calculate the asin series expansion. This should only be called with * argument values with absolute value less than 1/sqrt(2), because the * series converges very slowly for larger values. For operands above * 1/sqrt(2), the caller should instead compute the equivalent value * * +/- (pi/2 - asin(sqrt(1-x^2))). * * (+ if x > 0, - if x < 0). * * The argument value is in ext1, and we return a pointer to the * register that contains the result (which will be one of the input * registers). We use all of the input registers as scratchpads, so * their values are not retained. */ char *CVmObjBigNum::calc_asin_series(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5) { ulong n; /* get the current power of x (1) into the x power register, r2 */ copy_val(ext2, ext1, FALSE); /* * compute x^2 into r3 - we'll multiply the previous power by this * to get the next power (we need x^1, x^3, x^5, etc) */ compute_prod_into(ext3, ext1, ext1); /* start at the first term */ n = 1; /* keep going until we have enough precision in the result */ for (;;) { ulong i; char *tmp; /* move on to the next term */ n += 2; /* * compute the next weirdness factor into r4: * * 1*3*5*...*(n-2) *. ----------------- *. 2*4*6*...*(n-1)*n */ /* start out with 1 in r4 */ copy_val(ext4, get_one(), FALSE); /* multiply by odd numbers up to but not including 'n' */ for (i = 3 ; i < n ; i += 2) mul_by_long(ext4, i); /* * Divide by even numbers up to and including n-1. Note that we * use div_by_long() because it's faster than using a BigNumber * divisor. div_by_long() has a limit of ULONG_MAX/10 for the * divisor; we shouldn't have to worry about exceeding that, since * our maximum precision is 64k digits. The series will converge * to our maximum precision long before 'i' gets close to * ULONG_MAX/10. (Even if not, computing billions of series terms * would take so long that we'd never get there anyway.) */ for (i = 2 ; i < n ; i += 2) div_by_long(ext4, i); /* divide by n */ div_by_long(ext4, n); /* * compute the next power of x - multiply our current power of x * (r2) by x^2 (r3) */ compute_prod_into(ext5, ext2, ext3); /* swap r5 into r2 - this new power of x is now current */ tmp = ext5; ext5 = ext2; ext2 = tmp; /* * multiply the current x power by the current weirdness factor * - this will yield the current term into r5 */ compute_prod_into(ext5, ext2, ext4); /* * if this value is too small to show up in our accumulator, * we're done */ if (is_zero(ext5) || get_exp(ext1) - get_exp(ext5) > (int)get_prec(ext1)) break; /* * we can trash the weird factor now - use it as a scratch pad * for adding the accumulator so far (r1) to this term */ compute_sum_into(ext4, ext1, ext5); /* swap the result into r1, since it's the new accumulator */ tmp = ext4; ext4 = ext1; ext1 = tmp; } /* return the accumulator register */ return ext1; } /* * property evaluator - arctangent */ int CVmObjBigNum::getp_atan(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; const char *pi; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache pi to our working precision */ pi = cache_pi(prec + 3); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * If x has absolute value close to 1, either above or below, * our two series don't converge very rapidly. Happily, we can * fall back on an alternative in these case by using the * relation * * arctan(x) = (+/-)arccos(1/sqrt(x^2+1)) * * (The sign of the result is the same as the sign of x.) Since * we already have routines for arccosine and square root, this * calculation is easy. * * If x doesn't have absolute value close to 1, use the * appropriate series, since they converge rapidly. */ if (get_exp(ext_) < -1 || get_exp(ext_) > 2) { int term_neg; ulong n; /* * exponent less than -1 -> the number has a small absolute * value - use the small-x series expansion: * * x - x^3/3 + x^5/5 - x^7/7 ... * * OR * * exponent greater than 2 -> the number has a large * absolute value, so the large-x series expansion should * converge quickly: * * +/- pi/2 - 1/x + 1/3x^3 - 1/5x^5 ... * * (the sign of the leading pi/2 term is positive if x is * positive, negative if x is negative) * * We can commonize these expressions by defining x' = x for * small x and x' = 1/x for large x, defining C as 0 for * small x and +/-pi/2 for large X, defining N as +1 for * small x and -1 for large x, and rewriting the series as: * * C + Nx' - Nx'^3/3 + Nx'^5/5 + ... */ /* check for large or small value */ if (get_exp(ext_) < 0) { /* small number - start with zero in the accumulator (r1) */ set_zero(ext1); /* get the current power of x' into r2 - this is simply x */ copy_val(ext2, ext_, FALSE); /* the first term (x) is positive */ term_neg = FALSE; } else { /* large number - start with pi/2 in the accumulator (r1) */ copy_val(ext1, pi, TRUE); div_by_long(ext1, 2); /* if x is negative, make that -pi/2 */ set_neg(ext1, get_neg(ext_)); /* get 1/x into r2 - this is our x' term value */ compute_quotient_into(ext2, 0, get_one(), ext_); /* the first term (1/x) is negative */ term_neg = TRUE; } /* start at the first term */ n = 1; /* * get x'^2 into r3 - we'll use this to calculate each * subsequent term (we need x', x'^3, x'^5, etc) */ compute_prod_into(ext3, ext2, ext2); /* iterate until we reach the desired precision */ for (;;) { char *tmp; /* copy the current power term from r2 into r4 */ copy_val(ext4, ext2, FALSE); /* * divide by the current term constant (we'll never have * to compute billions of terms to reach our maximum * possible 64k digits of precision, so 'n' will always be * way less than the div_by_long limit of ULONG_MAX/10) */ div_by_long(ext4, n); /* negate this term if necessary */ if (term_neg) set_neg(ext4, !get_neg(ext4)); /* * if we're under the radar on precision, stop looping * (don't stop on the first term, since the accumulator * hasn't been fully primed yet) */ if (n != 1 && (is_zero(ext4) || (get_exp(ext1) - get_exp(ext4) > (int)get_prec(ext1)))) break; /* compute the sum of the accumulator and this term */ compute_sum_into(ext5, ext1, ext4); /* swap the result back into the accumulator */ tmp = ext1; ext1 = ext5; ext5 = tmp; /* * move on to the next term - advance n by 2 and swap * the term sign */ n += 2; term_neg = !term_neg; /* * compute the next power term - multiply r2 (the latest * x' power) by r3 (x'^2) */ compute_prod_into(ext4, ext2, ext3); /* swap r4 back into r2 - this is now the latest power */ tmp = ext2; ext2 = ext4; ext4 = tmp; } /* store the result, rounding as needed */ copy_val(new_ext, ext1, TRUE); } else { /* * We have a value of x from .01 to 10 - in this range, the * rewrite using arccosine will give us excellent precision. */ /* calculate x^2 into r1 */ compute_prod_into(ext1, ext_, ext_); /* add one (x^2 has to be positive, so don't worry about sign) */ increment_abs(ext1); /* take the square root and put the result in r2 */ compute_sqrt_into(ext2, ext1); /* divide that into 1, and put the result back in r1 */ compute_quotient_into(ext1, 0, get_one(), ext2); /* * Compute the arccosine of this value - the result is the * arctangent, so we can store it directly in the output * register. */ calc_asincos_into(new_ext, ext1, TRUE); /* if the input was negative, invert the sign of the result */ if (get_neg(ext_)) negate(new_ext); } } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - square root */ int CVmObjBigNum::getp_sqrt(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute the square root into the result */ compute_sqrt_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - natural logarithm */ int CVmObjBigNum::getp_ln(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* zero or negative values are not allowed */ if (is_zero(ext_) || get_neg(ext_)) err_throw(VMERR_OUT_OF_RANGE); /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute the natural logarithm */ compute_ln_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - exp (exponentiate e, the base of the natural * logarithm, to the power of this number) */ int CVmObjBigNum::getp_exp(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* compute exp(x) */ compute_exp_into(new_ext, ext_); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - log10 (base-10 logarithm) */ int CVmObjBigNum::getp_log10(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { char *new_ext; size_t prec = get_prec(ext_); uint hdl1, hdl2, hdl3; char *ext1, *ext2, *ext3; const char *ln10; /* check arguments and set up the result */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* cache ln(10) to the required precision */ ln10 = cache_ln10(prec + 3); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 3, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3); /* catch errors so we can be sure to free registers */ err_try { /* compute the natural logarithm of the number */ compute_ln_into(ext1, ext_); /* get ln(10), properly rounded */ copy_val(ext2, ln10, TRUE); /* compute ln(x)/ln(10) to yield log10(x) */ compute_quotient_into(ext3, 0, ext1, ext2); /* store the result, rounding as needed */ copy_val(new_ext, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(3, hdl1, hdl2, hdl3); } err_end; /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - pow(y) - calculate x^y */ int CVmObjBigNum::getp_pow(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t val2; const char *val2_ext; char *new_ext; size_t prec; uint hdl1, hdl2; char *ext1, *ext2; /* check arguments and allocate the result value */ if (setup_getp_1(vmg_ self, retval, argc, &new_ext, &val2, &val2_ext, FALSE)) return TRUE; /* use the precision of the result */ prec = get_prec(new_ext); /* * Check for a special case: if the number we're exponentiating is * zero, the result is 0 for any positive exponent, and an error for * any non-positive exponent (0^0 is undefined, and 0^n where n<0 is * equivalent to 1/0^n == 1/0, which is a divide-by-zero error). */ if (is_zero(ext_)) { /* 0^0 is undefined */ if (is_zero(val2_ext)) err_throw(VMERR_OUT_OF_RANGE); /* 0^negative is a divide by zero error */ if (get_neg(val2_ext)) err_throw(VMERR_DIVIDE_BY_ZERO); /* set the result to one, and we're done */ set_zero(new_ext); goto done; } /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 2, &ext1, &hdl1, &ext2, &hdl2); /* catch errors so we can be sure to free registers */ err_try { int result_neg; /* * If a = e^b, then b = ln(a). This means that x^y = e^ln(x^y) * = e^(y * ln(x)). So, we can compute the result in terms of * natural logarithm and exponentiation of 'e', for which we * have primitives we can call. */ /* * If x is negative, we can only exponentiate the value to * integer powers. In this case, we can substitute x' = -x * (hence x' will be positive), and rewrite the expression as * * (-1)^y * (x')^y * * We can only calculate (-1)^y for integer values of y, since * the result is complex if y is not an integer. */ if (get_neg(ext_)) { size_t idx; int units_dig; /* copy x into r2 */ copy_val(ext2, ext_, FALSE); /* * calculate x' = (-x) - since x is negative, this will * guarantee that x' is positive */ set_neg(ext2, FALSE); /* * make sure y is an integer - start at the first digit * after the decimal point and check for any non-zero digits */ idx = (get_exp(val2_ext) < 0 ? 0 : (size_t)get_exp(val2_ext)); for ( ; idx < get_prec(val2_ext) ; ++idx) { /* if this digit isn't a zero, it's not an integer */ if (get_dig(val2_ext, idx) != 0) { /* y isn't an integer, so we can't calculate (-1)^y */ err_throw(VMERR_OUT_OF_RANGE); } } /* get the first digit to the left of the decimal point */ if (get_exp(val2_ext) <= 0 || (size_t)get_exp(val2_ext) > get_prec(val2_ext)) { /* the units digit isn't represented - zero is implied */ units_dig = 0; } else { /* get the digit */ units_dig = get_dig(val2_ext, (size_t)get_exp(val2_ext) - 1); } /* * if the units digit is even, the result will be positive; * if it's odd, the result will be negative */ result_neg = ((units_dig & 1) != 0); /* calculate ln(x') into r1 */ compute_ln_into(ext1, ext2); } else { /* calculate ln(x) into r1 */ compute_ln_into(ext1, ext_); /* the result will be positive */ result_neg = FALSE; } /* calculate y * ln(x) into r2 */ compute_prod_into(ext2, val2_ext, ext1); /* calculate exp(r2) = exp(y*ln(x)) = x^y into r1 */ compute_exp_into(ext1, ext2); /* negate the result if we had a negative x and an odd power */ if (result_neg) negate(ext1); /* save the result, rounding as needed */ copy_val(new_ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(2, hdl1, hdl2); } err_end; done: /* discard the GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * property evaluator - sinh */ int CVmObjBigNum::getp_sinh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic sine using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, FALSE, FALSE); } /* * property evaluator - cosh */ int CVmObjBigNum::getp_cosh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic cosine using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, TRUE, FALSE); } int CVmObjBigNum::getp_tanh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* calculate the hyperbolic tangent using the common evaluator */ return calc_sinhcosh(vmg_ self, retval, argc, FALSE, TRUE); } /* * common property evaluator - compute a hyperbolic sine or cosine, or * do both to calculate a hyperbolic tangent */ int CVmObjBigNum::calc_sinhcosh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int is_cosh, int is_tanh) { char *new_ext; /* check arguments and allocate the return value */ if (setup_getp_0(vmg_ self, retval, argc, &new_ext)) return TRUE; /* calculate the result */ compute_sinhcosh_into(new_ext, ext_, is_cosh, is_tanh); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compute the fractional part of a number (replacing the value in place). * This effectively subtracts the integer portion of the number, leaving * only the fractional portion. */ void CVmObjBigNum::compute_frac(char *ext) { /* get the exponent and precision */ int exp = get_exp(ext); size_t prec = get_prec(ext); /* clear out the first n digits, where n is the exponent */ for (size_t idx = 0 ; idx < prec && (int)idx < exp ; ++idx) set_dig(ext, idx, 0); /* normalize the result */ normalize(ext); } /* * Get the whole part of a number (replacing the value in place). This * truncates the number to its integer portion, with no rounding. */ void CVmObjBigNum::compute_whole(char *ext) { /* get the exponent and precision */ int exp = get_exp(ext); size_t prec = get_prec(ext); /* check what we have */ if (exp <= 0) { /* it's an entirely fractional number - the result is zero */ set_zero(ext); } else { /* clear digits after the decimal point */ for (size_t idx = (size_t)exp ; idx < prec ; ++idx) set_dig(ext, idx, 0); /* normalize the result */ normalize(ext); } } /* ------------------------------------------------------------------------ */ /* * property evaluator - numType: get the number type information */ /* * number types defined in bignum.h - these are part of the public API, so * they can't be changed */ #define NumTypeNum 0x0001 #define NumTypeNAN 0x0002 #define NumTypePInf 0x0004 #define NumTypeNInf 0x0008 #define NumTypePZero 0x0010 #define NumTypeNZero 0x0020 int CVmObjBigNum::getp_numType(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set the appropriate flags */ switch (get_type(ext_)) { case VMBN_T_NUM: retval->set_int(NumTypeNum | (is_zero(ext_) ? (get_neg(ext_) ? NumTypeNZero : NumTypePZero) : 0)); break; case VMBN_T_INF: retval->set_int(get_neg(ext_) ? NumTypeNInf : NumTypePInf); break; case VMBN_T_NAN: default: retval->set_int(NumTypeNAN); break; } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compute a natural logarithm */ void CVmObjBigNum::compute_ln_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; size_t prec = get_prec(dst); const char *ln10; /* cache the value of ln(10) */ ln10 = cache_ln10(prec + 3); /* if the source value is zero, it's an error */ if (is_zero(src)) err_throw(VMERR_OUT_OF_RANGE); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { int src_exp; /* * Observe that we store our values as x = a*10^b. We can thus * rewrite ln(x) as ln(a*10^b) = ln(a)+ln(10^b) = * log(a)+b*ln(10). a is the mantissa, which due to * normalization is in the range 0.1 <= a < 1.0. Thus, a is an * ideal argument for the Taylor series. So, we can simply * compute ln(mantissa), then add it to ln(10)*exponent. */ /* copy our argument into r1 */ copy_val(ext1, src, FALSE); /* * remember the original exponent, and set the exponent of the * series argument to zero - we'll correct for this later by * adding the log of the exponent to the series result */ src_exp = get_exp(ext1); set_exp(ext1, 0); /* * The series expansion is especially efficient for values near * 1.0. We know the value is now in the range 0.1 <= a < 1.0. So * if the lead digit of the mantissa is 1, which means the value is * 0.1 and change, multiply by ten (thus making it 1.0 and change), * and adjust the exponent accordingly. */ if (get_dig(ext1, 0) == 1) { set_exp(ext1, 1); --src_exp; } /* compute the series expansion */ ext1 = compute_ln_series_into(ext1, ext2, ext3, ext4, ext5); /* add in the input exponent, properly adjusted */ if (src_exp != 0) { /* get ln10 into r2 */ copy_val(ext2, ln10, TRUE); /* apply the exponent's sign if it's negative */ if (src_exp < 0) { set_neg(ext2, TRUE); src_exp = -src_exp; } /* multiply by the exponent */ mul_by_long(ext2, src_exp); /* add this value to the series expansion value */ compute_sum_into(ext3, ext1, ext2); /* use this as the result */ ext1 = ext3; } /* copy the result, rounding if needed */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; } /* * Compute the natural log series. The argument value, initially in * ext1, should be adjusted to a small value before this is called to * ensure quick series convergence. */ char *CVmObjBigNum::compute_ln_series_into(char *ext1, char *ext2, char *ext3, char *ext4, char *ext5) { /* start at the first term of the series */ ulong n = 1; /* subtract one from r1 to yield (x-1) in r2 */ compute_abs_diff_into(ext2, ext1, get_one()); /* add one to r1 to yield (x+1) */ increment_abs(ext1); /* compute (x-1)/(x+1) into r3 - this will be our current power */ compute_quotient_into(ext3, 0, ext2, ext1); /* * compute ((x-1)/(x+1))^2 into r4 - we'll multiply r3 by this on * each iteration to produce the next required power, since we only * need the odd powers */ compute_prod_into(ext4, ext3, ext3); /* start out with the first power in our accumulator (r1) */ copy_val(ext1, ext3, FALSE); /* iterate until we have a good enough answer */ for (;;) { /* compute the next power into r2 */ compute_prod_into(ext2, ext3, ext4); /* copy the result into our current power register, r3 */ copy_val(ext3, ext2, FALSE); /* advance n */ n += 2; /* * divide the power by n (we'll never have to compute billions of * terms to get our maximum 64k digits of precision, so 'n' will * always be way less than the div_by_long limit of ULONG_MAX/10) */ div_by_long(ext2, n); /* if it's too small to notice, we're done */ if (is_zero(ext2) || get_exp(ext1) - get_exp(ext2) > (int)get_prec(ext1)) break; /* compute the sum with our accumulator into r5 */ compute_sum_into(ext5, ext1, ext2); /* swap r5 and r1 - the new sum is our new accumulator */ char *tmp = ext5; ext5 = ext1; ext1 = tmp; } /* multiply the result of the series by 2 */ mul_by_long(ext1, 2); /* return the register containing the result */ return ext1; } /* * Compute e^x, where e is the base of the natural logarithm (2.818...) */ void CVmObjBigNum::compute_exp_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4, hdl5, hdl6; char *ext1, *ext2, *ext3, *ext4, *ext5, *ext6; size_t prec = get_prec(dst); const char *ln10; /* get the constant value of ln10 to the required precision */ ln10 = cache_ln10(prec + 3); /* allocate temporary registers */ alloc_temp_regs(prec + 3, 6, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5, &ext6, &hdl6); /* catch errors so we can be sure to free registers */ err_try { char *tmp; ulong n; ulong n_fact; int use_int_fact; long new_exp; size_t idx; size_t decpt_idx; int twos; /* * We want to calculate e^x. Observe that a^x = e^(x*ln(x)), so * 10^x = e^(x*ln(10)). We can rewrite our desired expression * e^x as * * e^[(x/ln(10)) * ln(10)] * * which is thus * * 10^(x / ln(10)) * * We store our numbers as a mantissa times 10 raised to an * integer exponent. Clearly the exponent of 10 in the formula * above is not always an integer (in fact, it's extremely rare * that it would be an integer), so we still have more work to * do. What we must do is obtain an integer exponent. So, let * us define y = x/ln(10); we can now rewrite the above as * * 10^(int(y) + frac(y)) * * where int(y) is the integer portion of y and frac(y) is the * fractional portion (y - int(y)). We can rewrite the above as * * 10^frac(y) * 10^int(y) * * which we can further rewrite as * * e^(frac(y)*ln(10)) * 10^int(y) * * We haven't made the problem of finding an exponential * disappear, but we've reduced the argument to a very * manageable range, which is important because it makes the * Taylor series converge quickly. Furthermore, it's extremely * inexpensive to separate out the problem like this, since it * falls quite naturally out of the representation we use, so it * doesn't add much overhead to do this preparation work. */ /* first, calculate x/ln(10) into r1 */ compute_quotient_into(ext1, 0, src, ln10); /* * compute the integer portion of x/ln(10) - it has to fit in a * 16-bit integer, (-32768 to +32767), because this is going to * be the exponent of the result (or roughly so, anyway) */ decpt_idx = get_exp(ext1) >= 0 ? (size_t)get_exp(ext1) : 0; for (new_exp = 0, idx = 0 ; idx < decpt_idx ; ++idx) { int dig; /* * get this digit if it's represented; if not, it's an * implied trailing zero */ dig = (idx < get_prec(ext1) ? get_dig(ext1, idx) : 0); /* add this digit into the accumulator */ new_exp *= 10; new_exp += dig; /* * Make sure we're still in range. Note that, because our * representation is 0.dddd*10^x, we need one more factor of * ten than you'd think here, the adjust of the range from * the expected -32768..32767 */ if (new_exp > (get_neg(ext1) ? 32769L : 32766L)) err_throw(VMERR_NUM_OVERFLOW); /* * zero out this digit, so that when we're done r1 has the * fractional part only */ if (idx < get_prec(ext1)) set_dig(ext1, idx, 0); } /* negate the exponent value if the source value is negative */ if (get_neg(ext1)) new_exp = -new_exp; /* normalize the fractional part, which remains in ext1 */ normalize(ext1); /* * Multiply it by ln10, storing the result in r3. This is the * value we'll use with the Taylor series. */ compute_prod_into(ext3, ext1, ln10); /* * While our input value is greater than 0.5, divide it by two to * make it smaller than 0.5. This will speed up the series * convergence. When we're done, we'll correct for the divisions * by squaring the result the same number of times that we halved * 'x', because e^2x = (e^x)^2. */ copy_val(ext1, get_one(), FALSE); div_by_long(ext1, 2); for (twos = 0 ; compare_abs(ext3, ext1) > 0 ; ++twos) div_by_long(ext3, 2); /* * Start with 1+x in our accumulator (r1). This unrolls the * trivial first two steps of the loop, where n=0 (term=1) and n=1 * (term=x). */ copy_val(ext2, get_one(), FALSE); compute_sum_into(ext1, ext2, ext3); /* get term n=2 (x^2) into the current-power register (r2) */ compute_prod_into(ext2, ext3, ext3); /* start with 2 in our factorial register (r4) */ copy_val(ext4, get_one(), FALSE); mul_by_long(ext4, 2); /* start at term n=2, n! = 2 */ n = 2; n_fact = 2; use_int_fact = TRUE; /* go until we reach the required precision */ for (;;) { /* for efficiency, try integer division */ if (use_int_fact) { /* * we can still fit the factorial in an integer - divide * by the integer value of n!, since it's a lot faster */ copy_val(ext5, ext2, FALSE); div_by_long(ext5, n_fact); /* calculate the next n! integer, if it'll fit in a long */ if (n_fact > LONG_MAX/10/(n+1)) { /* * it'll be too big next time - we'll have to start * using the full quotient calculation */ use_int_fact = FALSE; } else { /* it'll still fit - calculate the next n! */ n_fact *= (n+1); } } else { /* compute x^n/n! (r2/r4) into r5 */ compute_quotient_into(ext5, 0, ext2, ext4); } /* if we're below the required precision, we're done */ if (is_zero(ext5) || get_exp(ext1) - get_exp(ext5) > (int)get_prec(ext1)) break; /* compute the sum of the accumulator and this term into r6 */ compute_sum_into(ext6, ext1, ext5); /* swap the result into the accumulator */ tmp = ext1; ext1 = ext6; ext6 = tmp; /* on to the next term */ ++n; /* compute the next factorial value */ mul_by_long(ext4, n); /* compute the next power of x' into r5 */ compute_prod_into(ext5, ext2, ext3); /* swap the result into our current-power register (r2) */ tmp = ext2; ext2 = ext5; ext5 = tmp; } /* square the result as many times as we halved the argument */ for ( ; twos != 0 ; --twos) { /* compute the square of r1 into r2 */ compute_prod_into(ext2, ext1, ext1); /* swap the result back into r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * set up our 10's exponent value - this is simply 1*10^new_exp, * which we calculated earlier (which we represent as * 0.1*10^(new_exp+1) */ copy_val(ext2, get_one(), FALSE); set_exp(ext2, (int)(new_exp + 1)); /* multiply by the 10's exponent value */ compute_prod_into(ext3, ext1, ext2); /* copy the result into the output register, rounding as needed */ copy_val(dst, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(6, hdl1, hdl2, hdl3, hdl4, hdl5, hdl6); } err_end; } /* ------------------------------------------------------------------------ */ /* * Compute a hyperbolic sine or cosine */ void CVmObjBigNum::compute_sinhcosh_into(char *dst, const char *src, int is_cosh, int is_tanh) { size_t prec = get_prec(dst); uint hdl1, hdl2, hdl3, hdl4; char *ext1, *ext2, *ext3, *ext4; /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 4, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4); /* catch errors so we can be sure to free registers */ err_try { /* * sinh(x) = (e^x - e^(-x))/2 * * cosh(x) = (e^x + e^(-x))/2 * * The two differ only in the sign of the e^(-x) term, so most * of the calculation is the same for both. */ /* compute e^x into r1 */ compute_exp_into(ext1, src); /* * rather than calculating e^-x separately, simply invert our * e^x value to yield e^-x (a simple division is much quicker * than calculating another exponent, which involves an entire * taylor series expansion) */ copy_val(ext3, get_one(), FALSE); compute_quotient_into(ext2, 0, ext3, ext1); /* * if we're calculating the tanh, we'll want both the sinh and * cosh values */ if (is_tanh) { /* add the terms to get the cosh */ compute_sum_into(ext4, ext1, ext2); /* subtract ext2 to get the sinh */ negate(ext2); compute_sum_into(ext3, ext1, ext2); /* tanh is the quotient of sinh/cosh */ compute_quotient_into(ext1, 0, ext3, ext4); /* our result is in ext1 - set ext3 to point there */ ext3 = ext1; } else { /* * if this is sinh, the e^-x term is subtracted; if it's * cosh, it's added */ if (!is_cosh) negate(ext2); /* compute r1 + r2 into r3 (e^x +/- e^(-x)) */ compute_sum_into(ext3, ext1, ext2); /* halve the result */ div_by_long(ext3, 2); } /* store the result */ copy_val(dst, ext3, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(4, hdl1, hdl2, hdl3, hdl4); } err_end; } /* ------------------------------------------------------------------------ */ /* * Cache the natural logarithm of 10 to the given precision and return * the value */ const char *CVmObjBigNum::cache_ln10(size_t prec) { char *ext; int expanded; uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; static const unsigned char ten[] = { /* number of digits */ 0x01, 0x00, /* exponent (0.1 * 10^2 = 10) */ 0x02, 0x00, /* flags */ 0x00, /* mantissa */ 0x10 }; /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the ln10 cache register */ ext = S_bignum_cache->get_ln10_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * Compute sqrt(10) - 10 is too large for the series to converge, * but sqrt(10) is good. We'll correct for this later by doubling * the result of the series expansion, which gives us the correct * result: ln(a^b) = b*ln(a), and sqrt(x) = x^(1/2), hence * ln(sqrt(x)) = ln(x)/2, which means that ln(x) = 2*ln(sqrt(x)). * * Note that we have to do this the hard way, by explicitly doing * the ln series rather than just calling compute_ln_into() to get * the value directly. Why? Because compute_ln_into() needs the * cached value of ln(10) to do its work. If we called * compute_ln_into() here, we'd get stuck in a recursion loop. */ /* compute sqrt(10), for quick series convergence */ copy_val(ext2, (const char *)ten, FALSE); compute_sqrt_into(ext1, ext2); /* compute the series expansion */ ext1 = compute_ln_series_into(ext1, ext2, ext3, ext4, ext5); /* double the result (to adjust for the sqrt) */ mul_by_long(ext1, 2); /* store the result in the cache */ copy_val(ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache the natural logarithm of 2 to the given precision and return the * value */ const char *CVmObjBigNum::cache_ln2(size_t prec) { char *ext; int expanded; static const unsigned char two[] = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x20 }; /* round up the precision to minimize recalculations */ prec = (prec + 7) & ~7; /* get the ln2 cache register */ ext = S_bignum_cache->get_ln2_reg(calc_alloc(prec), &expanded); if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we had a cached value with enough precision, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* reallocated - set the new precision and recalculate ln2 */ set_prec(ext, prec); compute_ln_into(ext, (const char *)two); /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache pi to the required precision */ const char *CVmObjBigNum::cache_pi(size_t prec) { char *ext; int expanded; uint hdl1, hdl2, hdl3, hdl4, hdl5; char *ext1, *ext2, *ext3, *ext4, *ext5; /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the pi cache register */ ext = S_bignum_cache->get_pi_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* allocate some temporary registers */ alloc_temp_regs(prec + 3, 5, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4, &ext5, &hdl5); /* catch errors so we can be sure to free registers */ err_try { /* * Compute the arctangent of 1. To do this, rewrite arctan(x) = * arccos(1/sqrt(1+x^2)), so x=1 gives us arccos(1/sqrt(2)). * * Now, arccos(x) = pi/2 - arcsin(x), but this would defeat the * purpose since we want to calculate pi, thus we don't want to * depend upon pi in our calculations. Fortunately, arcsin(x) = * pi/2 - arcsin(sqrt(1-x^2)), hence * * pi/2 - arcsin(x) = pi/2 - (pi/2 - arcsin(sqrt(1-x^2))) *. = arcsin(sqrt(1-x^2)) * * So, we can rewrite arccos(1/sqrt(2)) as arcsin(sqrt(1/2)). */ /* compute 1/2 into r2 */ copy_val(ext2, get_one(), FALSE); div_by_long(ext2, 2); /* compute sqrt(1/2) into r1 */ compute_sqrt_into(ext1, ext2); /* calculate the arcsin series for sqrt(1/2) */ ext1 = calc_asin_series(ext1, ext2, ext3, ext4, ext5); /* multiply the result by 4 */ mul_by_long(ext1, 4); /* store the result in the cache */ copy_val(ext, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(5, hdl1, hdl2, hdl3, hdl4, hdl5); } err_end; /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Cache e to the required precision */ const char *CVmObjBigNum::cache_e(size_t prec) { /* * round up the precision a bit to ensure that we don't have to * repeatedly recalculate this value if we're asked for a cluster of * similar precisions */ prec = (prec + 7) & ~7; /* get the e cache register */ int expanded; char *ext = S_bignum_cache->get_e_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * if we didn't have to allocate more memory, and the value in the * register has at least the required precision, return the cached * value */ if (!expanded && get_prec(ext) >= prec) return ext; /* * we have reallocated the register, or we just didn't have enough * precision in the old value - set the new precision */ set_prec(ext, prec); /* exponentiate the base of the natural logarithm to the power 1 */ compute_exp_into(ext, get_one()); /* return the register pointer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Compute a square root. We use Newton's method (a reliable old method * for extracting square roots, made better by the fact that, unlike the * method's inventor, we are fortunate to have an electronic computer to * carry out the tedious parts of the calculation for us). */ void CVmObjBigNum::compute_sqrt_into(char *dst, const char *src) { uint hdl1, hdl2, hdl3, hdl4; char *ext1, *ext2, *ext3, *ext4; size_t dst_prec = get_prec(dst); /* if the value is negative, it's an error */ if (get_neg(src)) err_throw(VMERR_OUT_OF_RANGE); /* allocate our scratchpad registers */ alloc_temp_regs(get_prec(dst) + 3, 4, &ext1, &hdl1, &ext2, &hdl2, &ext3, &hdl3, &ext4, &hdl4); /* catch errors so we can free our registers */ err_try { /* * Compute our initial guess. Since our number is represented as * * (n * 10^exp), * * the square root can be written as * * sqrt(n) * sqrt(10^exp) = sqrt(n) * 10^(exp/2) * * Approximate sqrt(n) as simply n for our initial guess. This * will get us to the right order of magnitude plus or minus * one, so we should converge pretty quickly. * * If we have an odd exponent, round up and divide the mantissa * by 2 - this will be something like 0.456e7, which can be * written as 4.56e6, whose square root is about 2e3, or .2e4. * * If we have an even exponent, multiply the mantissa by 2. * This will be something like .456e8, whose square root is * about .67e4. * * Note that it's well worth the trouble to make a good initial * approximation, even with the multiply/divide, because these * operations with longs are much more efficient than the full * BigNum divide we'll have to do on each iteration. */ copy_val(ext1, src, TRUE); if ((get_exp(ext1) & 1) != 0) { /* odd exponent - round up and divide the mantissa by two */ set_exp(ext1, (get_exp(ext1) + 1)/2); div_by_long(ext1, 2); } else { /* even exponent - multiply mantissa by two */ set_exp(ext1, get_exp(ext1)/2); mul_by_long(ext1, 2); } /* iterate until we get close enough to the solution */ for (;;) { char *tmp; size_t idx; /* * Calculate the next iteration's approximation, noting that r1 * contains the current iteration's value p: * * p' = p/2 + src/2p = (p + src/p)/2 * * Note that if p == 0, we can't compute src/p, so we can't * iterate any further. */ if (is_zero(ext1)) break; /* calculate src/p into r3 */ compute_quotient_into(ext3, 0, src, ext1); /* compute p + src/p into r4 */ compute_sum_into(ext4, ext1, ext3); /* compute (p + src/p)/2 into r4 */ div_by_long(ext4, 2); /* * check for convergence - if the new value equals the old * value to the precision requested for the result, we are * at the limit of our ability to distinguish differences in * future terms, so we can stop */ if (get_neg(ext1) == get_neg(ext4) && get_exp(ext1) == get_exp(ext4)) { /* * they're the same sign and magnitude - compare the * digits to see where they first differ */ for (idx = 0 ; idx < dst_prec + 1 ; ++idx) { /* if they differ here, stop scanning */ if (get_dig(ext1, idx) != get_dig(ext4, idx)) break; } /* * if we didn't find any difference up to the output * precision plus one digit (for rounding), further * iteration will be of no value */ if (idx >= dst_prec + 1) break; } /* swap the new value into r1 for the next round */ tmp = ext1; ext1 = ext4; ext4 = tmp; } /* * copy the last iteration's value into the destination, * rounding as needed */ copy_val(dst, ext1, TRUE); } err_finally { /* release our temporary registers */ release_temp_regs(4, hdl1, hdl2, hdl3, hdl4); } err_end; } /* ------------------------------------------------------------------------ */ /* * Calculate a Taylor expansion for sin(). The argument is in ext1, and * we'll store the result in new_ext. ext1 through ext7 are temporary * registers that we'll overwrite with intermediate values. * * Before calling this function, the caller should reduce the value in * ext1 to the range -pi/4 <= ext1 <= pi/4 to ensure quick convergence * of the series. */ void CVmObjBigNum::calc_sin_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7) { ulong n; int neg_term; char *tmp; /* start with 1! (i.e., 1) in r3 */ copy_val(ext3, get_one(), FALSE); /* * calculate x^2 into r7 - we need x, x^3, x^5, etc, so we can just * multiply the last value by x^2 to get the next power we need */ compute_prod_into(ext7, ext1, ext1); /* start with x (reduced mod 2pi) in r5 (our accumulator) */ copy_val(ext5, ext1, FALSE); /* start at term n=1 in our expansion */ n = 1; /* the first term is positive */ neg_term = FALSE; /* go until we have a precise enough value */ for (;;) { /* * move on to the next term: multiply r1 by x^2 into r2 to yield * the next power of x that we require */ compute_prod_into(ext2, ext1, ext7); /* swap r1 and r2 - r2 has the next power we need now */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* * multiply r3 by (n+1)*(n+2) - it currently has n!, so this * will yield (n+2)! */ mul_by_long(ext3, (n+1)*(n+2)); /* advance n to the next term */ n += 2; /* each term swaps signs */ neg_term = !neg_term; /* divide r1 by r3 to yield the next term in our series in r4 */ compute_quotient_into(ext4, 0, ext1, ext3); /* * if this value is too small to count in our accumulator, we're * done */ if (is_zero(ext4) || get_exp(ext5) - get_exp(ext4) > (int)get_prec(ext4)) break; /* * invert the sign of the term in r4 if this is a negative term */ if (neg_term) set_neg(ext4, !get_neg(ext4)); /* add r4 to our running accumulator in r5, yielding r6 */ compute_sum_into(ext6, ext5, ext4); /* swap r5 and r6 to put the accumulated value back into r5 */ tmp = ext5; ext5 = ext6; ext6 = tmp; } /* we're done - store the result, rounding if necessary */ copy_val(new_ext, ext5, TRUE); } /* * Calculate a Taylor expansion for cos(). The argument is in ext1, and * we'll store the result in new_ext. ext1 through ext7 are temporary * registers that we'll overwrite with intermediate values. * * Before calling this function, the caller should reduce the value in * ext1 to the range -pi/4 <= ext1 <= pi/4 to ensure quick convergence * of the series. */ void CVmObjBigNum::calc_cos_series(VMG_ char *new_ext, char *ext1, char *ext2, char *ext3, char *ext4, char *ext5, char *ext6, char *ext7) { ulong n; int neg_term; char *tmp; /* start with 2! (i.e., 2) in r3 */ copy_val(ext3, get_one(), FALSE); set_dig(ext3, 0, 2); /* * calculate x^2 into r7 - we need x^2, x^4, x^6, etc, so we can * just multiply the last value by x^2 to get the next power we need */ compute_prod_into(ext7, ext1, ext1); /* * the first power we need is x^2, so copy it into r1 (our current * power of x register) */ copy_val(ext1, ext7, FALSE); /* * start with 1 in r5, the accumulator (the first term of the series * is the constant value 1) */ copy_val(ext5, get_one(), FALSE); /* * start at term n=2 in our expansion (the first term is just the * constant value 1, so we can start at the second term) */ n = 2; /* * the first term we calculate (i.e., the second term in the actual * series) is negative */ neg_term = TRUE; /* go until we have a precise enough value */ for (;;) { /* divide r1 by r3 to yield the next term in our series in r4 */ compute_quotient_into(ext4, 0, ext1, ext3); /* * if this value is too small to count in our accumulator, we're * done */ if (is_zero(ext4) || get_exp(ext5) - get_exp(ext4) > (int)get_prec(ext4)) break; /* * invert the sign of the term in r4 if this is a negative term */ if (neg_term) set_neg(ext4, !get_neg(ext4)); /* add r4 to our running accumulator in r5, yielding r6 */ compute_sum_into(ext6, ext5, ext4); /* swap r5 and r6 to put the accumulated value back into r5 */ tmp = ext5; ext5 = ext6; ext6 = tmp; /* * move on to the next term: multiply r1 by x^2 into r2 to yield * the next power of x that we require */ compute_prod_into(ext2, ext1, ext7); /* swap r1 and r2 to put our next required power back in r1 */ tmp = ext1; ext1 = ext2; ext2 = tmp; /* * multiply r3 by (n+1)*(n+2) - it currently has n!, so this * will yield (n+2)! */ mul_by_long(ext3, (n+1)*(n+2)); /* advance n to the next term */ n += 2; /* each term swaps signs */ neg_term = !neg_term; } /* we're done - store the result, rounding if necessary */ copy_val(new_ext, ext5, TRUE); } /* ------------------------------------------------------------------------ */ /* * add a value */ int CVmObjBigNum::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the sum */ compute_sum(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * subtract a value */ int CVmObjBigNum::sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't use it */ err_throw(VMERR_BAD_TYPE_SUB); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the difference */ compute_diff(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * multiply a value */ int CVmObjBigNum::mul_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the product */ compute_prod(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * divide a value */ int CVmObjBigNum::div_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* convert it */ vm_val_t val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber, so we can't add it */ err_throw(VMERR_BAD_TYPE_ADD); } /* push 'self' and the other value to protect against GC */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* compute the quotient */ compute_quotient(vmg_ result, ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the GC protection items */ G_stk->discard(2); /* handled */ return TRUE; } /* * negate the number */ int CVmObjBigNum::neg_val(VMG_ vm_val_t *result, vm_obj_id_t self) { char *new_ext; size_t prec = get_prec(ext_); /* * If I'm not an ordinary number or an infinity, return myself * unchanged. Note that we change sign for an infinity, even though * this might not make a great deal of sense mathematically. * * If I'm zero, likewise return myself unchanged. Negative zero is * still zero. */ if ((get_type(ext_) != VMBN_T_NUM && get_type(ext_) != VMBN_T_INF) || is_zero(ext_)) { /* return myself unchanged */ result->set_obj(self); return TRUE; } /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* if I'm an infinity, we don't need any precision in the result */ if (is_infinity(ext_)) prec = 1; /* create a new number with the same precision as the original */ result->set_obj(create(vmg_ FALSE, prec)); new_ext = get_objid_ext(vmg_ result->val.obj); /* make a copy in the new object */ memcpy(new_ext, ext_, calc_alloc(prec)); /* reverse the sign */ set_neg(new_ext, !get_neg(new_ext)); /* remove my self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * check a value for equality */ int CVmObjBigNum::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { vm_val_t val2; int ret; /* if the other value is a reference to self, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber - it's not equal */ return FALSE; } /* push our values for safekeeping from the garbage collector */ G_stk->push()->set_obj(self); G_stk->push(&val2); /* check for equality and return the result */ ret = compute_eq_exact(ext_, get_objid_ext(vmg_ val2.val.obj)); /* discard the stacked values */ G_stk->discard(2); /* return the result */ return ret; } /* * Hash value calculation */ uint CVmObjBigNum::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { uint i; uint hash; /* add up the digits in the number */ for (hash = 0, i = 0 ; i < get_prec(ext_) ; ++i) { /* add this digit into the hash so far */ hash += get_dig(ext_, i); } /* add in the exponent as well */ hash += (uint)get_exp(ext_); /* return the combined hash */ return hash; } /* * compare to another value */ int CVmObjBigNum::compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const { vm_val_t val2; /* convert it */ val2 = *val; if (!cvt_to_bignum(vmg_ self, &val2)) { /* this type is not convertible to BigNumber - it's not comparable */ err_throw(VMERR_INVALID_COMPARISON); } /* compare the values */ return compare_ext(ext_, get_objid_ext(vmg_ val2.val.obj)); } /* ------------------------------------------------------------------------ */ /* * Initialize for a computation involving two operands. Checks the * operands for non-number values; if either is NAN or INF, allocates a * result value that is the same non-number type and returns null. If * both are valid numbers, we'll allocate a result value with precision * equal to the greater of the precisions of the operands, and we'll * return a pointer to the new object's extension buffer. */ char *CVmObjBigNum::compute_init_2op(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { size_t new_prec; char *new_ext; /* get the greater precision - this is the precision of the result */ new_prec = get_prec(ext1); if (get_prec(ext2) > new_prec) new_prec = get_prec(ext2); /* * if either operand is not an ordinary number, we need minimal * precision to represent the result, since we don't actually need * to store a number */ if (is_nan(ext1) || is_nan(ext2)) new_prec = 1; /* allocate a new object with the required precision */ result->set_obj(create(vmg_ FALSE, new_prec)); /* get the extension buffer */ new_ext = get_objid_ext(vmg_ result->val.obj); /* check the first value for NAN/INF conditions */ if (get_type(ext1) != VMBN_T_NUM) { /* set the result to the same non-number type and sign */ set_type(new_ext, get_type(ext1)); set_neg(new_ext, get_neg(ext1)); /* indicate that no further calculation is necessary */ return 0; } /* check the second number for NAN/INF conditions */ if (get_type(ext2) != VMBN_T_NUM) { /* set the result to the same non-number type and sign */ set_type(new_ext, get_type(ext2)); set_neg(new_ext, get_neg(ext2)); /* indicate that no further calculation is necessary */ return 0; } /* the operands are valid - return the new extension buffer */ return new_ext; } /* * Compute a sum */ void CVmObjBigNum::compute_sum(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the sum into the result */ compute_sum_into(new_ext, ext1, ext2); } /* * Compute a difference */ void CVmObjBigNum::compute_diff(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { /* allocate our result value */ char *new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the difference into the result object */ compute_diff_into(new_ext, ext1, ext2); } /* * Compute a product */ void CVmObjBigNum::compute_prod(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the product */ compute_prod_into(new_ext, ext1, ext2); } /* * Compute a quotient */ void CVmObjBigNum::compute_quotient(VMG_ vm_val_t *result, const char *ext1, const char *ext2) { char *new_ext; /* allocate our result value */ new_ext = compute_init_2op(vmg_ result, ext1, ext2); /* we're done if we had a non-number operand */ if (new_ext == 0) return; /* compute the quotient */ compute_quotient_into(new_ext, 0, ext1, ext2); } /* * Determine if two values are equal, rounding the value with greater * precision to the shorter precision if the two are not of equal * precision */ int CVmObjBigNum::compute_eq_round(VMG_ const char *ext1, const char *ext2) { size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); const char *shorter; const char *longer; char *tmp_ext; uint tmp_hdl; int ret; /* * allocate a temporary register with a rounded copy of the more * precise of the values */ if (prec1 > prec2) { /* the first one is longer */ longer = ext1; shorter = ext2; } else if (prec1 < prec2) { /* the second one is longer */ longer = ext2; shorter = ext1; } else { /* they're the same - do an exact comparison */ return compute_eq_exact(ext1, ext2); } /* get a temp register for rounding the longer value */ alloc_temp_regs(get_prec(shorter), 1, &tmp_ext, &tmp_hdl); /* make a rounded copy */ copy_val(tmp_ext, longer, TRUE); /* compare the rounded copy of the longer value to the shorter value */ ret = compute_eq_exact(shorter, tmp_ext); /* release the temporary register */ release_temp_regs(1, tmp_hdl); /* return the result */ return ret; } /* * Make an exact comparison for equality. If one value is more precise * than the other, we'll implicitly extend the shorter value to the * right with trailing zeros. */ int CVmObjBigNum::compute_eq_exact(const char *ext1, const char *ext2) { const char *longer; size_t min_prec; size_t max_prec; size_t prec1; size_t prec2; size_t idx; /* * if either is not an ordinary number, they are never equal to any * other value (note that this means INF != INF and NAN != NAN, * which is reasonable because these values cannot be meaningfully * compared; one NAN might mean something totally different from * another, and likewise various infinities are not comparable) */ if (is_nan(ext1) || is_nan(ext2)) return FALSE; /* figure out if one is more precise than the other */ prec1 = get_prec(ext1); prec2 = get_prec(ext2); if (prec1 > prec2) { /* ext1 is longer */ longer = ext1; max_prec = prec1; min_prec = prec2; } else if (prec2 > prec1) { /* ext2 is longer */ longer = ext2; max_prec = prec2; min_prec = prec1; } else { /* they're the same */ longer = 0; min_prec = max_prec = prec1; } /* if the signs aren't the same, the numbers are not equal */ if (get_neg(ext1) != get_neg(ext2)) return FALSE; /* if the exponents aren't equal, the numbers are not equal */ if (get_exp(ext1) != get_exp(ext2)) return FALSE; /* * compare digits up to the smaller precision, then make sure that * the larger-precision value's digits are all zeros from there out */ for (idx = 0 ; idx < min_prec ; ++idx) { /* if they don't match, return not-equal */ if (get_dig(ext1, idx) != get_dig(ext2, idx)) return FALSE; } /* check the longer one to make sure it's all zeros */ if (longer != 0) { /* scan the remainder of the longer one */ for ( ; idx < max_prec ; ++idx) { /* if this digit is non-zero, it's not a match */ if (get_dig(longer, idx) != 0) return FALSE; } } /* it's a match */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Round a value */ const char *CVmObjBigNum::round_val(VMG_ vm_val_t *new_val, const char *ext, size_t digits, int always_create) { /* presume we need rounding */ int need_round = TRUE; /* * if the value is already no longer than the requested precision, * return the original value; similarly, if we don't have to do any * rounding to truncate to the requested precision, do not change * the original object; likewise, don't bother changing anything if * it's not a number */ if (get_type(ext) != VMBN_T_NUM || !get_round_dir(ext, digits)) { if (always_create) { /* * we must create a new object regardless, but it won't need * rounding */ need_round = FALSE; } else { /* return the original value */ new_val->set_nil(); return ext; } } /* allocate a new object with the requested precision */ new_val->set_obj(create(vmg_ FALSE, digits)); char *new_ext = get_objid_ext(vmg_ new_val->val.obj); /* copy the sign, exponent, and type information */ set_prec(new_ext, digits); set_neg(new_ext, get_neg(ext)); set_exp(new_ext, get_exp(ext)); set_type(new_ext, get_type(ext)); /* * if we don't need rounding, just truncate the old mantissa and * return the result */ if (!need_round) { /* if the new size is smaller, truncate it */ if (digits <= get_prec(ext)) { /* copy the mantissa up to the requested new size */ memcpy(new_ext + VMBN_MANT, ext + VMBN_MANT, (digits + 1)/2); } else { /* it's growing - simply copy the old value */ copy_val(new_ext, ext, FALSE); } /* return the new value */ return new_ext; } /* copy the mantissa up to the requested new precision */ memcpy(new_ext + VMBN_MANT, ext + VMBN_MANT, (digits + 1)/2); /* round it up */ round_up_abs(new_ext, digits); /* return the new extension */ return new_ext; } /* * Convert a value to a big number value */ int CVmObjBigNum::cvt_to_bignum(VMG_ vm_obj_id_t self, vm_val_t *val) const { /* if it's an integer, convert it to a BigNum value */ if (val->typ == VM_INT) { /* * put my own value on the stack to ensure I'm not garbage * collected when creating the new object */ G_stk->push()->set_obj(self); /* it's an integer - convert it to a BigNum */ val->set_obj(create(vmg_ FALSE, (long)val->val.intval, 32)); /* done protecting my object reference */ G_stk->discard(); } /* if it's not a BigNumber object, we can't handle it */ if (val->typ != VM_OBJ || (vm_objp(vmg_ val->val.obj)->get_metaclass_reg() != metaclass_reg_)) { /* indicate that conversion was unsuccessful */ return FALSE; } /* successful conversion */ return TRUE; } frobtads-1.2.3/tads3/vmrefcnt.cpp0000644000175000001440000001262311376451450016064 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmrefcnt.cpp - thread-safe reference-counted objects Function Notes Modified 05/24/10 MJRoberts - Creation */ #include "t3std.h" #include "osifcnet.h" /* ------------------------------------------------------------------------ */ /* * Weakly Referenceable Object */ /* * Construction */ CVmWeakRef::CVmWeakRef(CVmWeakRefable *r) { /* remember the reference */ ref_ = r; /* * Remember the object's mutex. We share the mutex with the referenced * object, because our reference conversion (this->ref()) and its * reference counting (r->release_ref()) both access the same shared * data (namely our ref_ member). */ mu = r->mu; mu->add_ref(); } /* * destruction */ CVmWeakRef::~CVmWeakRef() { mu->release_ref(); } /* * Get the referenced object. This returns a strong reference to our * weakly referenced object. The caller is responsible for releasing the * reference that we return when it's done with it. * * The operation of checking our reference and incrementing the object's * reference count must be atomic. This ensures that we're safe even if * there's another thread that's about to release the last existing strong * reference on the object. That other thread's release will either * complete before we check our reference, in which case our reference will * be nulled out when we check it; OR we'll begin our atomic check/inc * operation first, in which case the other thread will be locked out until * after our inc, meaning we now have our own strong reference on the * object and it won't be deleted until our caller releases the new strong * reference. */ CVmWeakRefable *CVmWeakRef::ref() { /* lock the mutex while we're working */ mu->lock(); /* get the object */ CVmWeakRefable *r = ref_; /* * If we still have a reference to the object, it means it's still in * memory and still has at least one other referencer. As long as we * have the mutex locked, it's impossible for anyone else to release * their reference, so the object definitely will stay around at least * until we unlock the mutex. * * Add a strong reference on behalf of our caller. This ensures that * the object lives on for at least as long as the caller needs it, * because whatever else happens, they have a strong reference on it * starting now. Even if another thread releases the last pre-existing * reference on the object the moment we unlock the mutex, it doesn't * matter: before unlocking the mutex, we add our caller's strong * reference. */ if (r != 0) r->add_ref(); /* done with the mutex */ mu->unlock(); /* return the object */ return r; } /* ------------------------------------------------------------------------ */ /* * Weak-referencable object */ /* * construction */ CVmWeakRefable::CVmWeakRefable() { /* we don't have a weak ref yet */ weakref = 0; /* create our mutex */ mu = new OS_Mutex(); } /* * destruction */ CVmWeakRefable::~CVmWeakRefable() { /* done with our mutex */ mu->release_ref(); /* if we have a weak ref object, release it */ if (weakref != 0) weakref->release_ref(); } /* * Get a weak reference to this object. This returns a CVmWeakRef pointer * that can be used to obtain a strong reference to 'this' on demand, but * which doesn't by itself keep 'this' alive. If the WeakRef pointer is * dereferenced after 'this' is deleted, the dereference returns null. */ CVmWeakRef *CVmWeakRefable::get_weak_ref() { /* lock out other updaters */ mu->lock(); /* if we don't already have a weak reference object, create one */ if (weakref == 0) weakref = new CVmWeakRef(this); /* done with the lock */ mu->unlock(); /* add a reference on behalf of our caller */ weakref->add_ref(); /* return the weak reference object */ return weakref; } /* * Release a reference. * * The reference count update must be atomic with clearing any weak * reference pointing to us: this ensures that a weak referencer can't * convert its weak reference into a strong reference after the last * existing strong reference has performed a release, which initiates * deletion. Any weak referencer that gets in just before we begin the * atomic dec/clear operation will be safe, because it will obtain its * strong reference, which increments our reference count, BEFORE we do our * own decrement. Thus the reference count will not reach zero in that * case until the new strong reference created by the weak reference * conversion is itself released. */ void CVmWeakRefable::release_ref() { /* lock the object while we're working */ mu->lock(); /* decrement the reference counter */ int del = FALSE; if (cnt.dec() == 0) { /* if we have a weak referencer, clear its pointer to me */ if (weakref != 0) weakref->clear_ref(); /* note that it's time to delete the object */ del = TRUE; } /* done with our lock */ mu->unlock(); /* if we're unreferenced, delete myself */ if (del) delete this; } frobtads-1.2.3/tads3/vmrun.cpp0000644000175000001440000066677312145504453015431 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMRUN.CPP,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrun.cpp - VM Execution Function Notes Modified 11/12/98 MJRoberts - Creation */ #include #include "t3std.h" #include "os.h" #include "vmrun.h" #include "vmdbg.h" #include "vmop.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmobj.h" #include "vmlst.h" #include "vmstr.h" #include "vmtobj.h" #include "vmfunc.h" #include "vmmeta.h" #include "vmbif.h" #include "vmpredef.h" #include "vmfile.h" #include "vmsave.h" #include "vmprof.h" #include "vmhash.h" #include "vmlookup.h" #include "vmfref.h" #include "vmop.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * Define static global variables for certain VM registers, if we're * compiling the system in a static global configuration. * * Empirically, it makes a measurable difference (on the order of 2% * compared to the struct configuration) to keep these in globals and to * keep them together. These are the VM's equivalent of CPU registers, and * they get hit a lot during bytecode execution; keeping this small cluster * of hot memory locations together seems to enable the CPU to keep them in * cache. */ VM_IF_REGS_IN_GLOBALS(vm_val_t *sp_;) VM_IF_REGS_IN_GLOBALS(vm_val_t *frame_ptr_;) VM_IF_REGS_IN_GLOBALS(vm_val_t r0_;) VM_IF_REGS_IN_GLOBALS(const uchar *entry_ptr_native_;) VM_IF_REGS_IN_GLOBALS(const uchar **pc_ptr_;) /* ------------------------------------------------------------------------ */ /* * Initialize */ CVmRun::CVmRun(size_t max_depth, size_t reserve_depth) : CVmStack(max_depth, reserve_depth) { init(); } void CVmRun::init() { /* inherit base class handling */ CVmStack::init(); /* start out with 'nil' in R0 */ r0_.set_nil(); /* there's no frame yet */ frame_ptr_ = 0; /* there's no entry pointer yet */ entry_ptr_native_ = 0; /* function header size is not yet known */ funchdr_size_ = 0; /* we have no 'say' function yet */ say_func_ = 0; /* no default 'say' method */ say_method_ = VM_INVALID_PROP; /* no debugger halt requested yet */ halt_vm_ = FALSE; /* we have no program counter yet */ pc_ptr_ = 0; /* * If we're including the profiler in the build, allocate and * initialize its memory structures. */ #ifdef VM_PROFILER /* * Allocate the profiler stack. This stack will contain one record per * activation frame in the regular VM stack. */ prof_stack_max_ = 250; (prof_stack_ = (vm_profiler_rec *) t3malloc(prof_stack_max_ * sizeof(prof_stack_[0]))); /* we don't have anything on the profiler stack yet */ prof_stack_idx_ = 0; /* create the profiler master hash table */ prof_master_table_ = new CVmHashTable(512, new CVmHashFuncCI(), TRUE); /* we're not running the profiler yet */ profiling_ = FALSE; #endif /* VM_PROFILER */ } /* ------------------------------------------------------------------------ */ /* * Terminate */ CVmRun::~CVmRun() { terminate(); } void CVmRun::terminate() { /* * If we're including the profiler in the build, delete its memory * structures. */ #ifdef VM_PROFILER /* delete the profiler stack */ if (prof_stack_ != 0) { t3free(prof_stack_); prof_stack_ = 0; } /* delete the profiler master hash table */ if (prof_master_table_ != 0) { delete prof_master_table_; prof_master_table_ = 0; } #endif /* VM_PROFILER */ } /* ------------------------------------------------------------------------ */ /* * Set the function header size */ void CVmRun::set_funchdr_size(size_t siz) { /* remember the new size */ funchdr_size_ = siz; /* * Ensure that the size is at least as large as our required function * header block - if it's not, this version of the VM can't run this * image file. If we throw an error, flag it as a version mismatch * error. */ if (siz < VMFUNC_HDR_MIN_SIZE) err_throw_a(VMERR_IMAGE_INCOMPAT_HDR_FMT, 1, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Add two values, leaving the result in *val1 */ int CVmRun::compute_sum(VMG_ vm_val_t *val1, const vm_val_t *val2) { /* the meaning of "add" depends on the type of the first operand */ check_type: switch(val1->typ) { case VM_SSTRING: /* * string constant - add the second value to the string, using * the static string add method */ CVmObjString::add_to_str(vmg_ val1, val1, val2); return TRUE; case VM_LIST: /* * list constant - add the second value to the list, using the * static list add method */ CVmObjList::add_to_list(vmg_ val1, VM_INVALID_OBJ, get_const_ptr(vmg_ val1->val.ofs), val2); return TRUE; case VM_OBJ: /* * object - add the second value to the object, using the * object's virtual metaclass add method */ return vm_objp(vmg_ val1->val.obj)->add_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* * callers handle the int+int case inline, so we can skip that; we * just need to check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * the other value is numeric but not an integer; promote the * integer to the other type, then re-do the type check */ val2->promote_int(vmg_ val1); goto check_type; } else { /* can't add a non-numeric value to an integer */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types don't understand '+' as a native operator */ return FALSE; } } /* * Compute the sum for an INC instruction */ const uchar *CVmRun::compute_sum_inc(VMG_ const uchar *p) { /* add 1 to the value at TOS, leaving it on the stack */ vm_val_t val2; val2.set_int(1); if (compute_sum(vmg_ get(0), &val2)) { return p; } else { /* no native implementation - check for an overload */ vm_val_t val; pop(&val); push(&val2); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* * Compute the sum for an ADD instruction */ const uchar *CVmRun::compute_sum_add(VMG_ const uchar *p) { if (compute_sum(vmg_ get(1), get(0))) { /* success - the result is at TOS-1 */ discard(); return p; } else { /* try the operator overload */ vm_val_t val; pop_left_op(vmg_ &val); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* * Compute the sum of a local and an immediate value, assigning the result * to the local */ const uchar *CVmRun::compute_sum_lcl_imm(VMG_ vm_val_t *lclp, const vm_val_t *ival, int lclidx, const uchar *p) { /* compute the sum, leaving the result in the local */ if (compute_sum(vmg_ lclp, ival)) { /* success */ return p; } else { /* check for an overload */ push(ival); return op_overload(vmg_ p - entry_ptr_native_, lclidx, lclp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } /* ------------------------------------------------------------------------ */ /* * Compute the difference of two values, leaving the result in *val1 */ int CVmRun::compute_diff(VMG_ vm_val_t *val1, vm_val_t *val2) { /* the meaning of "subtract" depends on the type of the first operand */ check_type: switch(val1->typ) { case VM_LIST: /* * list constant - remove the second value from the list, using * the static list subtraction method */ CVmObjList::sub_from_list(vmg_ val1, val1, get_const_ptr(vmg_ val1->val.ofs), val2); return TRUE; case VM_OBJ: /* object - use the object's virtual subtraction method */ return vm_objp(vmg_ val1->val.obj)->sub_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* * callers handle the int-int case inline, so we can skip that; * check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * the other value is numeric but not an integer; promote the * integer to the other type, then re-do the type check */ val2->promote_int(vmg_ val1); goto check_type; } else { /* other values can't be subtracted */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types cannot be subtracted */ return FALSE; } } /* * Compute the difference for a DEC instruction */ const uchar *CVmRun::compute_diff_dec(VMG_ const uchar *p) { /* compute TOS - 1, leaving the result in TOS */ vm_val_t val2; val2.set_int(1); if (compute_diff(vmg_ get(0), &val2)) { return p; } else { /* no native implementation - check for an overload */ vm_val_t val; pop(&val); push(&val2); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } /* * Compute the difference for a SUB instruction */ const uchar *CVmRun::compute_diff_sub(VMG_ const uchar *p) { if (compute_diff(vmg_ get(1), get(0))) { /* the difference is at TOS-1 - discard the second value */ discard(); return p; } else { /* try for an operator overload */ vm_val_t val; pop_left_op(vmg_ &val); return op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } /* ------------------------------------------------------------------------ */ /* * Compute the product val1 * val2, leaving the result in val1 */ int CVmRun::compute_product(VMG_ vm_val_t *val1, vm_val_t *val2) { check_type: switch(val1->typ) { case VM_OBJ: /* use the object's virtual multiplication method */ return vm_objp(vmg_ val1->val.obj)->mul_val( vmg_ val1, val1->val.obj, val2); case VM_INT: /* callers handle int*int inline; check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* * int+other numeric - promote the integer to the other type, * then restart the calculation with the promoted value */ val2->promote_int(vmg_ val1); goto check_type; } else { /* other types are invalid */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types are invalid */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Compute the quotient val1/val2, leaving the result in val1. */ int CVmRun::compute_quotient(VMG_ vm_val_t *val1, vm_val_t *val2) { check_type: switch(val1->typ) { case VM_OBJ: /* use the object's virtual division method */ return vm_objp(vmg_ val1->val.obj)->div_val( vmg_ val1, val1->val.obj, val2); break; case VM_INT: /* callers handle int/int inline; check for other numeric types */ if (val2->is_numeric(vmg0_)) { /* promote to integer, then restart with the promoted value */ val2->promote_int(vmg_ val1); goto check_type; } else { err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } default: /* other types are invalid */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * XOR two values and push the result. The values can be numeric or * logical. If either value is logical, the result will be logical; * otherwise, the result will be a bitwise XOR of the integers. */ int CVmRun::xor_and_push(VMG_ vm_val_t *val1, vm_val_t *val2) { /* figure what to do based on the types */ if (val1->is_logical() && val2->is_logical()) { /* both values are logical - compute the logical XOR */ val1->set_logical(val1->get_logical() ^ val2->get_logical()); } else if (val1->is_logical() || val2->is_logical()) { /* * one value is logical, but not both - convert the other value * from a number to a logical and compute the result as a * logical value */ if (!val1->is_logical()) val1->num_to_logical(); else if (!val2->is_logical()) val2->num_to_logical(); /* compute the logical xor */ val1->set_logical(val1->get_logical() ^ val2->get_logical()); } else if (val1->typ == VM_INT && val2->typ == VM_INT) { /* compute and store the bitwise XOR */ val1->val.intval = val1->val.intval ^ val2->val.intval; } else { /* there's no logical conversion, so we can't compute it here */ return FALSE; } /* push the result */ pushval(vmg_ val1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Set an indexed value. Updates *container_val with the modified * container, if the operation requires this. (For example, setting an * indexed element of a list will create a new list, and return the new * list in *container_val. Setting an element of a vector simply modifies * the vector in place, hence the container reference is unchanged.) */ int CVmRun::set_index(VMG_ vm_val_t *container_val, const vm_val_t *index_val, const vm_val_t *new_val) { switch(container_val->typ) { case VM_LIST: /* list constant - use the static list set-index method */ CVmObjList::set_index_list(vmg_ container_val, get_const_ptr(vmg_ container_val->val.ofs), index_val, new_val); return TRUE; case VM_OBJ: /* object - use the object's virtual set-index method */ return vm_objp(vmg_ container_val->val.obj) ->set_index_val_q(vmg_ container_val, container_val->val.obj, index_val, new_val); default: /* other values cannot be indexed */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Create a new object and store it in R0 */ const uchar *CVmRun::new_and_store_r0(VMG_ const uchar *pc, uint metaclass_idx, uint argc, int is_transient) { vm_obj_id_t obj; /* create the object */ obj = G_meta_table->create_from_stack(vmg_ &pc, metaclass_idx, argc); /* if we got a valid object, store a reference to it in R0 */ if (obj != VM_INVALID_OBJ) { /* set the object return value */ r0_.set_obj(obj); /* make the object transient if desired */ if (is_transient) G_obj_table->set_obj_transient(obj); } else { /* failed - return nil */ r0_.set_nil(); } /* return the new instruction pointer */ return pc; } /* ------------------------------------------------------------------------ */ /* * Process a MAKELSTPAR instruction */ void CVmRun::makelstpar(VMG0_) { /* pop the value and the argument counter so far */ vm_val_t val, val2; popval(vmg_ &val); pop_int(vmg_ &val2); /* if it's not a list, just push it again unchanged */ int lstcnt; if (!val.is_listlike(vmg0_) || (lstcnt = val.ll_length(vmg0_)) < 0) { /* put it back on the stack */ pushval(vmg_ &val); /* increment the argument count and push it */ ++val2.val.intval; pushval(vmg_ &val2); /* our work here is done */ return; } /* set up a pointer to the current function header */ CVmFuncPtr hdr_ptr; hdr_ptr.set(entry_ptr_native_); /* get the depth required for the header */ uint hdr_depth = hdr_ptr.get_stack_depth(); /* * deduct the amount stack space we've already used from the amount * noted in the header, because that's the amount more that we could * need for the fixed stuff */ hdr_depth -= (get_depth_rel(frame_ptr_) - 1); /* make sure we have enough stack space available */ if (!check_space(lstcnt + hdr_depth)) err_throw(VMERR_STACK_OVERFLOW); /* push the elements of the list from last to first */ for (uint i = lstcnt ; i != 0 ; --i) { /* push this element's value */ val.ll_index(vmg_ push(), i); } /* increment and push the argument count */ val2.val.intval += lstcnt; pushval(vmg_ &val2); } /* ------------------------------------------------------------------------ */ /* * Index a value and push the result. */ int CVmRun::apply_index(VMG_ vm_val_t *result, const vm_val_t *container_val, const vm_val_t *index_val) { /* check the type of the value we're indexing */ switch(container_val->typ) { case VM_LIST: /* list constant - use the static list indexing method */ CVmObjList::index_list( vmg_ result, get_const_ptr(vmg_ container_val->val.ofs), index_val); return TRUE; case VM_OBJ: /* object - use the object's virtual indexing method */ { vm_obj_id_t obj = container_val->val.obj; return vm_objp(vmg_ obj)->index_val_q(vmg_ result, obj, index_val); } default: /* other values cannot be indexed */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Property evaluation invoker. This is a streamlined invoker for simple * evaluations where targetobj == self. */ class vmrun_prop_eval { public: /* caller offset */ uint caller_ofs; /* target property */ vm_prop_id_t target_prop; /* self and target object */ vm_val_t self; /* number of arguments */ uint argc; /* the property value */ vm_val_t val; /* defining object */ vm_obj_id_t defining_obj; const uchar *get_prop( VMG_ uint caller_ofs, vm_obj_id_t self, vm_prop_id_t target_prop) { this->self.set_obj(self); this->caller_ofs = caller_ofs; this->argc = 0; this->target_prop = target_prop; return get_prop(vmg0_); } const uchar *get_prop( VMG_ uint caller_ofs, vm_prop_id_t target_prop) { this->caller_ofs = caller_ofs; this->argc = 0; this->target_prop = target_prop; return get_prop(vmg0_); } const uchar *get_prop( VMG_ uint caller_ofs, vm_prop_id_t target_prop, uint argc) { this->caller_ofs = caller_ofs; this->argc = argc; this->target_prop = target_prop; return get_prop(vmg0_); } /* * Evaluate a property of an object. The caller must fill in * caller_ofs, target_obj, target_prop, self, and argc. */ const uchar *get_prop(VMG0_) { /* find the property without evaluating it */ if (get_prop_no_eval(vmg0_)) return eval_prop_val(vmg0_); /* try propNotDefined */ if (G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* save the original target property */ vm_prop_id_t real_target_prop = target_prop; /* look up propNotDefined */ target_prop = G_predef->prop_not_defined_prop; if (get_prop_no_eval(vmg0_)) { /* if we found a method, set up to call it */ if (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX) { /* * add the target property as the additional first * argument to propNotDefined (we push backwards, so * this will conveniently become the new first * argument) */ G_stk->push()->set_propid(real_target_prop); /* count the additional argument */ ++argc; } return eval_prop_val(vmg0_); } } /* * the property or method is not defined - discard arguments and * set R0 to nil */ G_stk->discard(argc); G_interpreter->get_r0()->set_nil(); /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; } /* * Look up a property without evaluating it. */ int get_prop_no_eval(VMG0_) { int found; const char *target_ptr; int (*f_const_get_prop)(VMG_ vm_val_t *, const vm_val_t *, const char *, vm_prop_id_t, vm_obj_id_t *, uint *); vm_obj_id_t (*f_const_create)(VMG_ const char *); /* * we can evaluate properties of regular objects, as well as string * and list constants - see what we have */ switch(self.typ) { case VM_OBJ: /* get the property value from the target object */ return vm_objp(vmg_ self.val.obj) ->get_prop(vmg_ target_prop, &val, self.val.obj, &defining_obj, &argc); case VM_LIST: f_const_get_prop = &CVmObjList::const_get_prop; f_const_create = &CVmObjListConst::create; goto const_common; case VM_SSTRING: f_const_get_prop = &CVmObjString::const_get_prop; f_const_create = &CVmObjStringConst::create; const_common: /* translate the list offset to a physical pointer */ target_ptr = G_const_pool->get_ptr(self.val.ofs); /* evaluate the constant list property */ found = f_const_get_prop( vmg_ &val, &self, target_ptr, target_prop, &defining_obj, &argc); /* * If the result is a method to run, we need an actual object * for 'self'. In this case, create a dynamic list object with * the same contents as the constant list value. */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* create the list */ self.set_obj(f_const_create(vmg_ target_ptr)); } /* go evaluate the result as normal */ return found; case VM_NIL: /* nil pointer dereferenced */ err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) default: /* we can't evaluate properties of anything else */ err_throw(VMERR_OBJ_VAL_REQD); AFTER_ERR_THROW(return FALSE;) } } /* * Given a value that has been retrieved from an object property, * evaluate the value. If the value contains code, we'll execute the * code; if it contains a self-printing string, we'll display the * string; otherwise, we'll just store the value in R0. * * If the value wasn't found, the caller sets the value type to 'empty' * to indicate that there's no value. */ const uchar *eval_prop_val(VMG0_) { /* take appropriate action based on the datatype of the result */ switch(val.typ) { case VM_CODEOFS: /* * It's a method - invoke the method. This will set us up to * start executing this new code, so there's nothing more we * need to do here. */ /* push targetprop, targetobj, definingobj, self, and invokee */ { vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self.val.obj); (fp++)->set_fnptr(val.val.ofs); } /* call the function */ return G_interpreter->do_call( vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(val.val.ofs), argc, 0); default: /* for any other value, no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* store the result in R0 */ *G_interpreter->get_r0() = val; /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; case VM_DSTRING: /* no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * it's a self-printing string - invoke the default string * output function (this is effectively a do_call()) */ return G_interpreter->disp_dstring( vmg_ val.val.ofs, caller_ofs, self.val.obj); case VM_OBJX: /* * Execute-on-eval object. If the value is an anonymous * function or other invokable object, call it. If it's a * string, print it. */ if (vm_objp(vmg_ val.val.obj)->get_invoker(vmg_ 0)) { /* convert to an ordinary anonymous function object */ vm_val_t funcptr; funcptr.set_obj(val.val.obj); /* prepare the invocation frame */ vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self.val.obj); (fp++)->set_obj(val.val.obj); /* invoke the function */ return G_interpreter->call_func_ptr_fr( vmg_ &funcptr, argc, 0, caller_ofs); } else if (CVmObjString::is_string_obj(vmg_ val.val.obj)) { /* print the string */ G_interpreter->push_obj(vmg_ val.val.obj); return G_interpreter->disp_string_val( vmg_ caller_ofs, self.val.obj); } err_throw(VMERR_BAD_TYPE_CALL); case VM_BIFPTRX: /* Execute-on-eval built-in function. Call the function. */ G_interpreter->call_bif( vmg_ val.val.bifptr.set_idx, val.val.bifptr.func_idx, argc); /* resume execution where we left off */ return G_interpreter->get_entry_ptr() + caller_ofs; } } }; /* ------------------------------------------------------------------------ */ /* * Integer arithmetic with promotions to BigNumber on overflow */ #define VMRUN_PROMOTE_INTS_ON_OVERFLOW #ifdef VMRUN_PROMOTE_INTS_ON_OVERFLOW static void promote_int_add(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> sum((long)aval->val.intval); sum += (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, sum)); } static inline void int_add(VMG_ vm_val_t *aval, int32_t b) { int32_t a = aval->val.intval, sum = a + b; if (a >= 0 ? b <= 0 || sum >= a : b >= 0 || sum <= a) aval->val.intval = sum; else promote_int_add(vmg_ aval, b); } static void promote_int_sub(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> diff((long)aval->val.intval); diff -= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, diff)); } static inline void int_sub(VMG_ vm_val_t *aval, int32_t b) { int32_t a = aval->val.intval, diff = a - b; if (a >= 0 ? b >= 0 || diff >= a : b < 0 || diff <= a) aval->val.intval = diff; else promote_int_sub(vmg_ aval, b); } static void promote_int_mul(VMG_ vm_val_t *aval, int32_t b) { bignum_t<20> prod((long)aval->val.intval); prod *= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, prod)); } static inline void int_mul(VMG_ vm_val_t *aval, int32_t b) { int64_t a = aval->val.intval, prod = a * b; if (prod <= (int64_t)INT32MAXVAL && prod >= (int64_t)INT32MINVAL) aval->val.intval = (int32_t)prod; else promote_int_mul(vmg_ aval, b); } static void promote_int_div(VMG_ vm_val_t *aval, int32_t b) { bignum_t<10> quo((long)aval->val.intval); quo /= (long)b; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, quo)); } static inline void int_div(VMG_ vm_val_t *aval, int32_t b) { /* check for division by zero */ if (b == 0) err_throw(VMERR_DIVIDE_BY_ZERO); /* * NB - assuming 2's complement notation; the only integer division * that can result in overflow is INT32MINVAL/-1 */ int32_t a = aval->val.intval; if (a != INT32MINVAL || b != -1) aval->val.intval = a / b; else promote_int_div(vmg_ aval, b); } static void promote_int_neg(VMG_ vm_val_t *aval) { bignum_t<10> neg((long)aval->val.intval); neg = -neg; aval->set_obj(CVmObjBigNum::create(vmg_ FALSE, neg)); } static inline void int_neg(VMG_ vm_val_t *aval) { /* * NB - assuming 2's complement representation; the only integer * negation that can result in overflow is -INT32MINVAL */ if (aval->val.intval != INT32MINVAL) aval->val.intval = -aval->val.intval; else promote_int_neg(vmg_ aval); } #define INT_ADD(aval, b) int_add(vmg_ aval, b) #define INT_SUB(aval, b) int_sub(vmg_ aval, b) #define INT_MUL(aval, b) int_mul(vmg_ aval, b) #define INT_DIV(aval, b) int_div(vmg_ aval, b) #define INT_NEG(aval) int_neg(vmg_ aval) #else #define INT_ADD(aval, b) ((aval)->val.intval += (b)) #define INT_SUB(aval, b) ((aval)->val.intval -= (b)) #define INT_MUL(aval, b) ((aval)->val.intval *= (b)) #define INT_DIV(aval, b) \ if (b == 0) err_throw(VMERR_DIVIDE_BY_ZERO); \ else ((aval)->val.intval /= (b)) #define INT_NEG(aval) ((aval)->val.intval = -(aval)->val.intval) #endif /* ------------------------------------------------------------------------ */ /* * Calculations for conditionals */ #if 1 # define false_for_cond(v) \ ((v)->typ == VM_NIL || ((v)->typ == VM_INT && (v)->val.intval == 0)) # define true_for_cond(v) \ ((v)->typ == VM_TRUE \ || (v)->typ == VM_ENUM \ || ((v)->typ == VM_INT && !(v)->val.intval == 0)) # define is_valid_for_jst(v) \ ((v)->typ == VM_NIL || (v)->typ == VM_INT) #else # define false_for_cond(v) \ ((v)->typ == VM_NIL \ || ((v)->is_numeric(vmg0_) && (v)->num_is_zero(vmg0_))) # define true_for_cond(v) \ ((v)->typ == VM_TRUE \ || (v)->typ == VM_ENUM \ || ((v)->is_numeric(vmg0_) && !(v)->num_is_zero(vmg0_))) # define is_valid_for_jst(v) \ ((v)->typ == VM_NIL || (v)->is_numeric(vmg0_)) #endif /* ------------------------------------------------------------------------ */ /* * Execute byte code */ void CVmRun::run(VMG_ const uchar *start_pc) { /* * If you're concerned about a compiler warning on the following * 'register' declaration, refer to the footnote at the bottom of this * file (search for [REGISTER_P_FOOTNOTE]). Executive summary: you can * safely ignore the warning, and I'm keeping the code as it is because * it causes better optimization on some platforms, and is harmless * when it doesn't help with optimization. */ register const uchar *p = start_pc; vmrun_prop_eval propev; vm_val_t *valp; vm_val_t *valp2; vm_val_t val; vm_val_t val2; vm_val_t val3; int done; vm_obj_id_t obj; vm_prop_id_t prop; uint argc; vm_obj_id_t unhandled_exc; const uchar *last_pc = start_pc; const uchar **old_pc_ptr; /* save the enclosing program counter pointer, and remember the new one */ old_pc_ptr = pc_ptr_; pc_ptr_ = &last_pc; /* we're not done yet */ done = FALSE; /* no unhandled exception yet */ unhandled_exc = VM_INVALID_OBJ; /* * Come back here whenever we catch a run-time exception and find a * byte-code error handler to process it in the stack. We'll * re-enter our exception handler and resume byte-code execution at * the handler. */ resume_execution: /* * Execute all code within an exception frame. If any routine we * call throws an exception, we'll catch the exception and process * it as a run-time error. */ err_try { /* execute code until something makes us stop */ for (;;) { #ifdef VM_DEBUGGER /* * check for user-requested break, and step into the debugger * if we find it */ static int brkchk = 0; if (++brkchk > 10000) { /* reset the break counter */ brkchk = 0; /* check for break, and step into debugger if found */ if (os_break()) { if (G_debugger->is_in_debugger()) err_throw(VMERR_DBG_INTERRUPT); else G_debugger->set_break_stop(); } } /* if we're single-stepping, break into the debugger */ if (G_debugger->is_single_step()) G_debugger->step(vmg_ &p, entry_ptr_native_, FALSE, 0); /* check for a halt request from the debugger */ if (halt_vm_) err_throw(VMERR_DBG_HALT); exec_instruction: #endif /* VM_DEBUGGER */ /* * Remember the location of this instruction in a non-register * variable, in case there's an exception. (We know that * last_pc is guaranteed to be a non-register variable because * we take its address and store it in our pc_ptr_ member.) * * We need to know the location of the last instruction when * an exception occurs so that we can find the exception * handler. We want to encourage the compiler to enregister * 'p', since we access it so frequently in this routine; but * if it's in a register, there's a risk we'd get the * setjmp-time value in our exception handler. To handle both * needs, simply copy the value to our non-register variable * last_pc; this will still let the vast majority of our * access to 'p' use fast register operations if the compiler * allows this, while ensuring we have a safe copy around in * case of exceptions. */ last_pc = p; /* * Execute the current instruction. * * The order of the case table is in descending order of * frequency of instruction usage for the first 30 or so * instructions, according to empirical testing. This is * intended to keep the code for the most common instructions * within a short jump of the 'switch', to increase the chances * that an instruction jump will go to a location in cache. * This seems to yield a slight speed improvement on Intel * machines, and even if it doesn't help on a given machine, it * shouldn't do any harm relative to a randomly ordered case * table. */ switch(*p++) { case OPC_GETARGN0: push(get_param(vmg_ 0)); continue; case OPC_GETPROPSELF: /* evaluate the property of 'self' */ prop = get_op_uint16(&p); p = propev.get_prop( vmg_ p - entry_ptr_native_, get_self(vmg0_), prop); continue; case OPC_GETR0: /* push the contents of R0 */ push(&r0_); continue; case OPC_DUPR0: /* push the contents of R0 twice */ push(&r0_); push(&r0_); continue; case OPC_GETSETLCL1R0: /* set local from R0 and leave value on stack */ push(&r0_); *get_local(vmg_ get_op_uint8(&p)) = r0_; continue; case OPC_GETSETLCL1: /* set local and leave value on stack */ *get_local(vmg_ get_op_uint8(&p)) = *get(0); continue; case OPC_SETPROPSELF: /* get the value to set */ pop(&val); /* set it */ set_prop(vmg_ get_self(vmg0_), get_op_uint16(&p), &val); continue; case OPC_SETLCL1R0: /* store R0 in the specific local */ *get_local(vmg_ get_op_uint8(&p)) = r0_; continue; case OPC_GETARGN1: push(get_param(vmg_ 1)); continue; case OPC_GETLCLN0: push(get_local(vmg_ 0)); continue; case OPC_SETLCL1: /* get a pointer to the local */ valp = get_local(vmg_ get_op_uint8(&p)); /* pop the value into the local */ pop(valp); continue; case OPC_PUSHSELF: /* push 'self' */ push(get_self_val(vmg0_)); continue; case OPC_RETNIL: /* store nil in R0 */ r0_.set_nil(); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_RETVAL: /* pop the return value into R0 */ pop(&r0_); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_GETPROPLCL1: /* get the local whose property we're evaluating */ propev.self = *get_local(vmg_ get_op_uint8(&p)); /* evaluate the property of the local variable */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_JNIL: /* jump if top of stack is nil */ valp = get(0); p += (valp->typ == VM_NIL ? osrp2s(p) : 2); /* discard the top value, regardless of what happened */ discard(); continue; case OPC_RET: /* return, leaving R0 unchanged */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_PUSHENUM: /* push a UINT4 operand value */ push()->set_enum(get_op_uint32(&p)); continue; case OPC_JMP: /* unconditionally jump to the given offset */ p += osrp2s(p); continue; case OPC_JNE: /* jump if the two values at top of stack are not equal */ p += (!pop2_equal(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JR0F: /* * if R0 is true, or it's a non-zero numeric value, or any * non-numeric and non-boolean value, stay put; otherwise, * jump */ if (false_for_cond(&r0_)) { /* it's zero or nil - jump */ p += osrp2s(p); } else { /* it's non-zero and non-nil - do not jump */ p += 2; } continue; case OPC_GETARGN2: push(get_param(vmg_ 2)); continue; case OPC_JGT: /* jump if greater */ p += (pop2_compare_gt(vmg0_) ? osrp2s(p) : 2); continue; case OPC_CALLPROPSELF: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callpropself: /* evaluate the property of 'self' */ propev.self.set_obj(get_self(vmg0_)); prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_INDEX: /* index TOS-1 by TOS, storing the result at TOS-1 */ valp = get(1); if (apply_index(vmg_ valp, valp, get(0))) { /* discard the index value */ discard(); } else { /* try an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_DUP: /* re-push the item at top of stack */ push(get(0)); continue; case OPC_IDXLCL1INT8: /* get the local */ valp = get_local(vmg_ get_op_uint8(&p)); /* get the index value */ val2.set_int(get_op_uint8(&p)); /* * look up the indexed value of the local, storing the * result in a newly-pushed stack element */ if (!apply_index(vmg_ push(), valp, &val2)) { /* * try an operator overload - push the index value as * the argument */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, valp, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_GETLCLN2: push(get_local(vmg_ 2)); continue; case OPC_CALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callprop: /* pop the object whose property we're fetching */ pop(&propev.self); /* evaluate the property given by the immediate data */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_GETLCLN1: push(get_local(vmg_ 1)); continue; case OPC_GETARGN3: push(get_param(vmg_ 3)); continue; case OPC_GETLCLN3: push(get_local(vmg_ 3)); continue; case OPC_JNOTNIL: /* jump if top of stack is not nil */ valp = get(0); p += (valp->typ != VM_NIL ? osrp2s(p) : 2); /* discard the top value, regardless of what happened */ discard(); continue; case OPC_ITERNEXT: /* get the iterator object from the local */ valp = get_local(vmg_ get_op_uint16(&p)); /* get the next value from the iterator */ if (valp->typ == VM_OBJ && vm_objp(vmg_ valp->val.obj)->iter_next( vmg_ valp->val.obj, &val)) { /* another value is available - push it and proceed */ push(&val); p += 2; } else { /* no more values available - exit the loop */ p += osrp2s(p); } break; case OPC_PUSH_0: /* push the constant value 0 */ push()->set_int(0); continue; case OPC_GETPROP: /* get the object whose property we're fetching */ pop(&propev.self); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_GETLCLN4: push(get_local(vmg_ 4)); continue; case OPC_JE: /* jump if the two values at top of stack are equal */ p += (pop2_equal(vmg0_) ? osrp2s(p) : 2); continue; /* * End of case table sorting by instruction execution * frequency. The order of the remainder is more or less * arbitrary; we get diminishing returns from the frequency * ordering past a certain point, because CPU caches are * only so large. */ case OPC_PUSHNIL: /* push nil */ push()->set_nil(); continue; case OPC_PUSHTRUE: /* push true */ push()->set_true(); continue; case OPC_PUSH_1: /* push the constant value 1 */ push()->set_int(1); continue; case OPC_PUSHINT8: /* push an SBYTE operand value */ push()->set_int(get_op_int8(&p)); continue; case OPC_PUSHINT: /* push a UINT4 operand value */ push()->set_int(get_op_int32(&p)); continue; case OPC_INC: /* * Increment the value at top of stack. We must perform * the same type conversions as the ADD instruction does. * As an optimization, check to see if we have an integer * on top of the stack, and if so simply increment its * value without popping and repushing. */ if ((valp = get(0))->typ == VM_INT) { /* it's an integer - increment it, and we're done */ ++(valp->val.intval); } else { /* for other types, use the general handler */ p = compute_sum_inc(vmg_ p); } continue; case OPC_ADD: /* if they're both integers, add them the quick way */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the sum */ INT_ADD(valp2, valp->val.intval); /* discard the second value */ discard(); } else { /* for other types, use the general handler */ p = compute_sum_add(vmg_ p); } continue; case OPC_DEC: /* * Decrement the value at top of stack. We must perform * the same type conversions as the SUB instruction does. * As an optimization, check to see if we have an integer * on top of the stack, and if so simply decrement its * value without popping and repushing. */ if ((valp = get(0))->typ == VM_INT) { /* it's an integer - decrement it, and we're done */ INT_SUB(valp, 1); } else { /* for other types, use the general handler */ p = compute_diff_dec(vmg_ p); } continue; case OPC_SUB: /* if they're both integers, subtract them the quick way */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the difference */ INT_SUB(valp2, valp->val.intval); /* discard the second value */ discard(); } else { /* for other types, use the general handler */ p = compute_diff_sub(vmg_ p); } continue; case OPC_PUSHSTR: /* push UINT4 offset operand as a string */ push()->set_sstring(get_op_uint32(&p)); continue; case OPC_DISC: /* discard the item at the top of the stack */ discard(); continue; case OPC_DISC1: /* discard n items */ discard(get_op_uint8(&p)); continue; case OPC_PUSHLST: /* push UINT4 offset operand as a list */ push()->set_list(get_op_uint32(&p)); continue; case OPC_PUSHOBJ: /* push UINT4 object ID operand */ push()->set_obj(get_op_uint32(&p)); continue; case OPC_PUSHPROPID: /* push UINT2 property ID operand */ push()->set_propid(get_op_uint16(&p)); continue; case OPC_PUSHFNPTR: /* push a function pointer operand */ push()->set_fnptr(get_op_uint32(&p)); continue; case OPC_PUSHPARLST: { /* get the number of fixed parameters */ uint cnt = *p++; /* allocate the list from the parameters */ obj = CVmObjList::create_from_params( vmg_ cnt, get_cur_argc(vmg0_) - cnt); /* push the new list */ push()->set_obj(obj); } continue; case OPC_MAKELSTPAR: makelstpar(vmg0_); continue; case OPC_NEG: /* if it's an integer value, do the calculation inline */ if ((valp = get(0))->typ == VM_INT) { /* negate number in place */ INT_NEG(valp); } else if (valp->typ == VM_OBJ && vm_objp(vmg_ valp->val.obj)->neg_val( vmg_ &val2, valp->val.obj)) { /* the object defined it natively - store the result */ *valp = val2; } else { /* try the overloaded operator */ pop(&val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_neg, 0, VMERR_BAD_TYPE_NEG); } continue; case OPC_BNOT: /* check the type */ if ((valp = get(0))->typ == VM_INT) { /* bitwise NOT the integer on top of stack */ valp->val.intval = ~valp->val.intval; } else { /* try the overloaded operator */ pop(&val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_not, 0, VMERR_BAD_TYPE_BIT_NOT); } continue; case OPC_MUL: /* if they're both integers, this is easy */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the product */ INT_MUL(valp2, valp->val.intval); /* discard the second value */ discard(); } else if (compute_product(vmg_ valp2, valp)) { /* success - discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_mul, 1, VMERR_BAD_TYPE_MUL); } continue; case OPC_DIV: /* if they're both integers, do the division inline */ valp = get(0); valp2 = get(1); if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result of the division */ INT_DIV(valp2, valp->val.intval); /* discard the second value */ discard(); } else if (compute_quotient(vmg_ valp2, valp)) { /* success - discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_div, 1, VMERR_BAD_TYPE_DIV); } continue; case OPC_MOD: /* remainder number at (TOS-1) by number at top of stack */ valp = get(0); valp2 = get(1); /* if they're both integers, do a quick local operation */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* check for division by zero */ if (valp->val.intval == 0) err_throw(VMERR_DIVIDE_BY_ZERO); /* * compute the remainder (TOS-1) % (TOS), leaving the * result at (TOS-1), and discard the second operand */ valp2->val.intval = os_remainder_long( valp2->val.intval, valp->val.intval); /* discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_mod, 1, VMERR_BAD_TYPE_MOD); } continue; case OPC_BAND: /* bitwise AND two integers on top of stack */ valp = get(0); valp2 = get(1); /* if we have two integers, do it the quick way */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result and discard the second operand */ valp2->val.intval &= valp->val.intval; /* discard the second value */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_and, 1, VMERR_BAD_TYPE_BIT_AND); } continue; case OPC_BOR: /* bitwise OR two integers on top of stack */ valp = get(0); valp2 = get(1); /* check the types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval |= valp->val.intval; /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_bit_or, 1, VMERR_BAD_TYPE_BIT_OR); } continue; case OPC_SHL: /* * bit-shift left integer at (TOS-1) by integer at top * of stack */ valp = get(0); valp2 = get(1); /* check the types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval <<= valp->val.intval; /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_shl, 1, VMERR_BAD_TYPE_SHL); } continue; case OPC_ASHR: /* * arithmetic shift right integer at (TOS-1) by integer at * top of stack */ valp = get(0); valp2 = get(1); /* check types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval = t3_ashr(valp2->val.intval, valp->val.intval); /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_ashr, 1, VMERR_BAD_TYPE_ASHR); } continue; case OPC_LSHR: /* * logical shift right integer at (TOS-1) by integer at * top of stack */ valp = get(0); valp2 = get(1); /* check types */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* compute the result */ valp2->val.intval = t3_lshr(valp2->val.intval, valp->val.intval); /* discard the second operand */ discard(); } else { /* try for an operator overload */ pop_left_op(vmg_ &val); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_lshr, 1, VMERR_BAD_TYPE_LSHR); } continue; case OPC_XOR: /* XOR two values at top of stack */ popval_2(vmg_ &val, &val2); if (!xor_and_push(vmg_ &val, &val2)) { /* try for an operator overload */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_xor, 1, VMERR_BAD_TYPE_XOR); } continue; case OPC_NOT: /* * invert the logic value; if the value is a number, * treat 0 as nil and non-zero as true */ valp = get(0); switch(valp->typ) { case VM_NIL: /* !nil -> true */ valp->set_true(); continue; case VM_OBJ: /* !obj -> true if obj is nil, nil otherwise */ valp->set_logical(valp->val.obj == VM_INVALID_OBJ); continue; case VM_TRUE: case VM_PROP: case VM_SSTRING: case VM_LIST: case VM_CODEOFS: case VM_FUNCPTR: case VM_ENUM: /* these are all considered true, so !them -> nil */ valp->set_nil(); continue; case VM_INT: /* !int -> true if int is 0, nil otherwise */ valp->set_logical(valp->val.intval == 0); continue; default: err_throw(VMERR_NO_LOG_CONV); } continue; case OPC_BOOLIZE: /* set to a boolean value */ valp = get(0); switch(valp->typ) { case VM_NIL: case VM_TRUE: /* it's already a logical value - leave it alone */ continue; case VM_INT: /* integer: 0 -> nil, non-zero -> true */ valp->set_logical(valp->val.intval); continue; case VM_ENUM: /* an enum is always non-nil */ valp->set_true(); continue; default: err_throw(VMERR_NO_LOG_CONV); } continue; case OPC_EQ: /* compare two values at top of stack for equality */ push_bool(vmg_ pop2_equal(vmg0_)); continue; case OPC_NE: /* compare two values at top of stack for inequality */ push_bool(vmg_ !pop2_equal(vmg0_)); continue; case OPC_LT: /* compare values at top of stack - true if (TOS-1) < TOS */ push_bool(vmg_ pop2_compare_lt(vmg0_)); continue; case OPC_LE: /* compare values at top of stack - true if (TOS-1) <= TOS */ push_bool(vmg_ pop2_compare_le(vmg0_)); continue; case OPC_GT: /* compare values at top of stack - true if (TOS-1) > TOS */ push_bool(vmg_ pop2_compare_gt(vmg0_)); continue; case OPC_GE: /* compare values at top of stack - true if (TOS-1) >= TOS */ push_bool(vmg_ pop2_compare_ge(vmg0_)); continue; case OPC_VARARGC: { /* get the modified opcode */ uchar opc = *p++; /* * skip the immediate data argument count - this is * superseded by our dynamic argument counter */ ++p; /* pop the argument counter */ pop_int(vmg_ &val); argc = val.val.intval; /* execute the appropriate next opcode */ switch(opc) { case OPC_CALL: goto do_opc_call; case OPC_PTRCALL: goto do_opc_ptrcall; case OPC_CALLPROP: goto do_opc_callprop; case OPC_PTRCALLPROP: goto do_opc_ptrcallprop; case OPC_CALLPROPSELF: goto do_opc_callpropself; case OPC_PTRCALLPROPSELF: goto do_opc_ptrcallpropself; case OPC_OBJCALLPROP: goto do_opc_objcallprop; case OPC_CALLPROPLCL1: goto do_opc_callproplcl1; case OPC_CALLPROPR0: goto do_opc_callpropr0; case OPC_INHERIT: goto do_opc_inherit; case OPC_PTRINHERIT: goto do_opc_ptrinherit; case OPC_EXPINHERIT: goto do_opc_expinherit; case OPC_PTREXPINHERIT: goto do_opc_ptrexpinherit; case OPC_DELEGATE: goto do_opc_delegate; case OPC_PTRDELEGATE: goto do_opc_ptrdelegate; case OPC_BUILTIN_A: goto do_opc_builtin_a; case OPC_BUILTIN_B: goto do_opc_builtin_b; case OPC_BUILTIN_C: goto do_opc_builtin_c; case OPC_BUILTIN_D: goto do_opc_builtin_d; case OPC_BUILTIN1: goto do_opc_builtin1; case OPC_BUILTIN2: goto do_opc_builtin2; case OPC_NEW1: goto do_opc_new1; case OPC_TRNEW1: goto do_opc_trnew1; case OPC_NEW2: goto do_opc_new2; case OPC_TRNEW2: goto do_opc_trnew2; default: err_throw(VMERR_INVALID_OPCODE_MOD); continue; } } continue; case OPC_NAMEDARGPTR: /* * Pointer to named argument table. Discard the named * arguments (the count is given by a one-byte operand), * then skip the table pointer (a two-byte operand). */ discard(get_op_uint8(&p)); p += 2; continue; case OPC_NAMEDARGTAB: /* * Named argument table. As with NAMEDARGPTR, we must * discard the named arguments. Then we simply skip the * table - the table is for the benefit of callees. */ { pool_ofs_t ofs = get_op_uint16(&p); discard(get_op_uint16(&p)); p += ofs - 2; } continue; case OPC_CALL: /* get the argument count */ argc = get_op_uint8(&p); do_opc_call: { /* get the code offset to invoke */ pool_ofs_t ofs = get_op_int32(&p); /* call it */ p = do_call_func_nr(vmg_ p - entry_ptr_native_, ofs, argc); } continue; case OPC_PTRCALL: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcall: /* retrieve the target of the call */ popval(vmg_ &val); /* * if it's a prop ID, and there's a valid "self" object, * treat it as a PTRCALLPROPSELF */ if (val.typ == VM_PROP && get_self_check(vmg0_) != VM_INVALID_OBJ) goto do_opc_ptrcallpropself_val; /* call the function */ p = call_func_ptr(vmg_ &val, argc, 0, p - entry_ptr_native_); continue; case OPC_RETTRUE: /* store true in R0 */ r0_.set_true(); /* return */ if ((p = do_return(vmg0_)) == 0) goto exit_loop; continue; case OPC_GETPROPR0: /* evaluate the property of R0 */ propev.self = r0_; prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_CALLPROPLCL1: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callproplcl1: /* get the local whose property we're calling */ propev.self = *get_local(vmg_ get_op_uint8(&p)); /* call the property of the local */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_CALLPROPR0: /* get the argument count */ argc = get_op_uint8(&p); do_opc_callpropr0: /* call the property of R0 */ propev.self = r0_; prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_PTRCALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcallprop: /* * pop the property to be evaluated, and the object whose * property we're evaluating */ pop_prop(vmg_ &val); pop(&propev.self); /* evaluate the property */ p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_PTRCALLPROPSELF: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrcallpropself: /* get the property to be evaluated */ pop_prop(vmg_ &val); do_opc_ptrcallpropself_val: /* evaluate the property of 'self' */ propev.self.set_obj(get_self(vmg0_)); p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_OBJGETPROP: /* get the object */ propev.self.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_OBJCALLPROP: /* get the argument count */ argc = get_op_uint8(&p); do_opc_objcallprop: /* get the object */ propev.self.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* evaluate the property */ prop = get_op_uint16(&p); p = propev.get_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_GETLCL1: /* push the local */ pushval(vmg_ get_local(vmg_ get_op_uint8(&p))); continue; case OPC_GETLCLN5: pushval(vmg_ get_local(vmg_ 5)); continue; case OPC_GETLCL2: /* push the local */ pushval(vmg_ get_local(vmg_ get_op_uint16(&p))); continue; case OPC_GETARG1: /* push the argument */ pushval(vmg_ get_param(vmg_ get_op_uint8(&p))); continue; case OPC_GETARG2: /* push the argument */ pushval(vmg_ get_param(vmg_ get_op_uint16(&p))); continue; case OPC_SETSELF: /* retrieve the 'self' object */ pop(&val); /* if it's nil, set the 'obj' field to null */ if (val.typ == VM_NIL) val.val.obj = VM_INVALID_OBJ; /* set 'self' */ set_self(vmg_ &val); continue; case OPC_STORECTX: /* create the context object */ create_loadctx_obj(vmg_ push(), get_self(vmg0_), get_defining_obj(vmg0_), get_orig_target_obj(vmg0_), get_target_prop(vmg0_)); continue; case OPC_LOADCTX: { /* * convert the context object (at top of stack) to a * list pointer */ const char *lstp = get(0)->get_as_list(vmg0_); /* throw an error if it's not what we're expecting */ if (lstp == 0 || vmb_get_len(lstp) < 4) err_throw(VMERR_LIST_VAL_REQD); /* retrieve and store the context elements */ set_method_ctx( vmg_ vmb_get_dh_obj(lstp + VMB_LEN), vmb_get_dh_prop(lstp + VMB_LEN + VMB_DATAHOLDER), vmb_get_dh_obj(lstp + VMB_LEN + 2*VMB_DATAHOLDER), vmb_get_dh_obj(lstp + VMB_LEN + 3*VMB_DATAHOLDER)); /* discard the context object at top of stack */ discard(); } continue; case OPC_PUSHCTXELE: /* check our context element type */ switch(*p++) { case PUSHCTXELE_TARGPROP: /* push the target property ID */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_PROP)); continue; case PUSHCTXELE_TARGOBJ: /* push the original target object ID */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_ORIGTARG)); continue; case PUSHCTXELE_DEFOBJ: /* push the defining object */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_DEFOBJ)); continue; case PUSHCTXELE_INVOKEE: /* push the invokee object */ push(get_from_frame(frame_ptr_, VMRUN_FPOFS_INVOKEE)); continue; default: /* the opcode is not valid in this VM version */ err_throw(VMERR_INVALID_OPCODE); } continue; case OPC_GETARGC: /* push the argument counter */ push_int(vmg_ get_cur_argc(vmg0_)); continue; case OPC_DUP2: /* * duplicate the top two elements: first push the * second-from-top, then push the old top (which will now * be the second-from-top, thanks to our first push) */ pushval(vmg_ get(1)); pushval(vmg_ get(1)); continue; case OPC_SWITCH: { /* get the control value */ valp = get(0); /* get the case count */ uint cnt = get_op_uint16(&p); /* iterate through the case table */ for ( ; cnt != 0 ; p += 7, --cnt) { /* get this value */ vmb_get_dh((const char *)p, &val2); /* check if the values match */ if (valp->equals(vmg_ &val2)) { /* it matches - jump to this offset */ p += VMB_DATAHOLDER; p += osrp2s(p); /* no need to look any further */ break; } } /* discard the control value */ discard(); /* if we didn't find it, jump to the default case */ if (cnt == 0) p += osrp2s(p); } continue; case OPC_JT: /* get the value */ valp = get(0); /* * if it's true, or a non-zero numeric value, or any * non-numeric and non-boolean value, jump */ if (false_for_cond(valp)) { /* it's zero or nil - do not jump */ p += 2; } else { /* it's non-zero and non-nil - jump */ p += osrp2s(p); } /* discard the value */ discard(); continue; case OPC_JR0T: /* * if R0 is true, or it's a non-zero numeric value, or any * non-numeric and non-boolean value, jump */ if (false_for_cond(&r0_)) { /* it's zero or nil - do not jump */ p += 2; } else { /* it's non-zero and non-nil - jump */ p += osrp2s(p); } continue; case OPC_JF: /* get the value */ valp = get(0); /* * if it's true, or a non-zero numeric value, or any * non-numeric and non-boolean value, do not jump; * otherwise, jump */ if (false_for_cond(valp)) { /* it's zero or nil - jump */ p += osrp2s(p); } else { /* it's non-zero and non-nil - do not jump */ p += 2; } /* discard the value */ discard(); continue; case OPC_JGE: /* jump if greater or equal */ p += (pop2_compare_ge(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JLT: /* jump if less */ p += (pop2_compare_lt(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JLE: /* jump if less or equal */ p += (pop2_compare_le(vmg0_) ? osrp2s(p) : 2); continue; case OPC_JST: /* get (do not remove) the element at top of stack */ valp = get(0); /* * if it's true, an enum value, or a non-zero number, jump, * saving the value; otherwise, require that it be a * logical value, pop it, and proceed */ if (true_for_cond(valp)) { /* it's true - save it and jump */ p += osrp2s(p); } else { /* * it's not true - discard the value, but require * that it be a valid logical value */ if (!is_valid_for_jst(valp)) err_throw(VMERR_LOG_VAL_REQD); discard(); /* skip to the next instruction */ p += 2; } continue; case OPC_JSF: /* get (do not remove) the element at top of stack */ valp = get(0); /* * if it's nil or zero, jump, saving the value; * otherwise, discard the value and proceed */ if (false_for_cond(valp)) { /* it's nil or zero - save it and jump */ p += osrp2s(p); } else { /* it's something non-false - discard it */ discard(); /* skip to the next instruction */ p += 2; } continue; case OPC_LJSR: /* * compute and push the offset of the next instruction * (at +2 because of the branch offset operand) from our * method header - this will be the return address, * which in this offset format will survive any code * swapping that might occur in subsequent execution */ push_int(vmg_ pc_to_method_ofs(p + 2)); /* jump to the target address */ p += osrp2s(p); continue; case OPC_LRET: /* get the indicated local variable */ valp = get_local(vmg_ get_op_uint16(&p)); /* the value must be an integer */ if (valp->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* * jump to the code address obtained from adding the * integer value in the given local variable to the * current method header pointer */ p = entry_ptr_native_ + valp->val.intval; continue; case OPC_SWAP: /* swap the top two elements on the stack */ valp = get(0); valp2 = get(1); /* make a working copy of TOS */ val = *valp; /* copy TOS-1 over TOS */ *valp = *valp2; /* copy the working copy of TOS over TOS-1 */ *valp2 = val; continue; case OPC_SWAP2: /* swap the top two elements with the next two */ valp = get(0); valp2 = get(1); /* make a working copy of elements 2 & 3 */ val = *get(2); val2 = *get(3); /* copy 0,1 over 2,3 */ *get(2) = *valp; *get(3) = *valp2; /* copy the saved 2,3 over 0,1 */ *valp = val; *valp2 = val2; continue; case OPC_SWAPN: /* swap elements at two given stack indices */ valp = get(get_op_uint8(&p)); valp2 = get(get_op_uint8(&p)); /* make a working copy of val1 */ val = *valp; /* write val2 over val1 */ *valp = *valp2; /* write the copy of val1 over val2 */ *valp2 = val; continue; case OPC_GETSPN: /* push stack element at index */ push(get(get_op_uint8(&p))); continue; case OPC_SAY: { /* get the string offset */ pool_ofs_t ofs = get_op_int32(&p); /* display it */ p = disp_dstring(vmg_ ofs, p - entry_ptr_native_, get_self_check(vmg0_)); } continue; case OPC_SAYVAL: /* invoke the default string display function */ p = disp_string_val(vmg_ p - entry_ptr_native_, get_self_check(vmg0_)); continue; case OPC_INHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_inherit: /* inherit the property */ prop = (vm_prop_id_t)get_op_uint16(&p); p = inh_prop(vmg_ p - entry_ptr_native_, prop, argc); continue; case OPC_PTRINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrinherit: /* pop the property to be inherited */ pop_prop(vmg_ &val); /* inherit it */ p = inh_prop(vmg_ p - entry_ptr_native_, val.val.prop, argc); continue; case OPC_EXPINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_expinherit: /* get the property to inherit */ prop = (vm_prop_id_t)get_op_uint16(&p); /* get the superclass to inherit it from */ val.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* * inherit it -- process this essentially the same way * as a normal CALLPROP, since we're going to evaluate * the given property of the given object, but retain * the current 'self' object */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val2, argc, 0); continue; case OPC_PTREXPINHERIT: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrexpinherit: /* pop the property to inherit */ pop_prop(vmg_ &val); /* get the superclass to inherit it from */ val3.set_obj((vm_obj_id_t)get_op_uint32(&p)); /* inherit it */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val3, val.val.prop, &val2, argc, 0); continue; case OPC_DELEGATE: /* get the argument count */ argc = get_op_uint8(&p); do_opc_delegate: /* get the property to inherit */ prop = (vm_prop_id_t)get_op_uint16(&p); /* get the object to delegate to */ pop(&val); /* delegate it */ val2.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val, prop, &val2, argc, 0); continue; case OPC_PTRDELEGATE: /* get the argument count */ argc = get_op_uint8(&p); do_opc_ptrdelegate: /* pop the property to delegate to */ pop_prop(vmg_ &val); /* pop the object to delegate to */ pop(&val2); /* delegate it */ val3.set_obj(get_self(vmg0_)); p = get_prop(vmg_ p - entry_ptr_native_, &val2, val.val.prop, &val3, argc, 0); continue; case OPC_BUILTIN_A: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_a: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #0 */ call_bif(vmg_ 0, idx, argc); } continue; case OPC_BUILTIN_B: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_b: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #1 */ call_bif(vmg_ 1, idx, argc); } continue; case OPC_BUILTIN_C: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_c: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #2 */ call_bif(vmg_ 2, idx, argc); } continue; case OPC_BUILTIN_D: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin_d: { /* get the function index */ uint idx = get_op_uint8(&p); /* call the function in set #3 */ call_bif(vmg_ 3, idx, argc); } continue; case OPC_BUILTIN1: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin1: { /* get the function index and function set ID */ uint idx = get_op_uint8(&p); uint set_idx = get_op_uint8(&p); /* call the function */ call_bif(vmg_ set_idx, idx, argc); } continue; case OPC_BUILTIN2: /* get the argument count */ argc = get_op_uint8(&p); do_opc_builtin2: { /* get the function index and function set ID */ uint idx = get_op_uint16(&p); uint set_idx = get_op_uint8(&p); /* call the function in set #0 */ call_bif(vmg_ set_idx, idx, argc); } continue; case OPC_IDXINT8: /* * make a copy of the value to index, so we can overwrite * the stack slot with the result */ val = *(valp = get(0)); /* set up the index value */ val2.set_int(get_op_uint8(&p)); /* apply the index, storing the result at TOS */ if (!apply_index(vmg_ valp, &val, &val2)) { /* * try an operator overload - push the index value as * the argument */ pop(&val); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_NEW1: /* get the argument count */ argc = get_op_uint8(&p); /* fall through to do_opc_new1 */ do_opc_new1: { /* get the metaclass ID and create the object */ uint idx = get_op_uint8(&p); p = new_and_store_r0(vmg_ p, idx, argc, FALSE); } continue; case OPC_TRNEW1: /* get the argument count */ argc = get_op_uint8(&p); /* fall through to do_opc_trnew1 */ do_opc_trnew1: { /* get the metaclass ID and create the object */ uint idx = get_op_uint8(&p); p = new_and_store_r0(vmg_ p, idx, argc, TRUE); } continue; case OPC_NEW2: /* get the argument count */ argc = get_op_uint16(&p); /* fall through to do_opc_new2 */ do_opc_new2: { /* get the metaclass ID */ uint idx = get_op_uint16(&p); /* create the new object */ p = new_and_store_r0(vmg_ p, idx, argc, FALSE); } continue; case OPC_TRNEW2: /* get the argument count */ argc = get_op_uint16(&p); /* fall through to do_opc_trnew2 */ do_opc_trnew2: { /* get the metaclass ID */ uint idx = get_op_uint16(&p); /* create the new object */ p = new_and_store_r0(vmg_ p, idx, argc, TRUE); } continue; case OPC_NOP: /* NO OP - no effect */ continue; case OPC_INCLCL: /* get the local */ { int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* do the increment inline if it's an integer */ if (valp->typ == VM_INT) { /* it's a number - just increment the value */ INT_ADD(valp, 1); } else { /* it's a non-numeric value - do the full addition */ val2.set_int(1); if (!compute_sum(vmg_ valp, &val2)) { /* * Check for an operator overload. We need to * do this with a recursive call to update the * local on return. */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } } continue; case OPC_DECLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* handle integers inline */ if (valp->typ == VM_INT) { /* it's a number - just decrement the value */ INT_SUB(valp, 1); } else { /* non-int - we must do the full subtraction work */ val2.set_int(1); if (!compute_diff(vmg_ valp, &val2)) { /* * Check for an operator overload. We need to * do this with a recursive call to update the * local on return. */ push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } } continue; case OPC_ADDILCL1: { /* get the local */ int itmp = get_op_uint8(&p); valp = get_local(vmg_ itmp); /* handle it inline if it's an integer */ if (valp->typ == VM_INT) { /* it's a number - just add the value */ INT_ADD(valp, get_op_int8(&p)); } else { /* get the number to add */ val2.set_int(get_op_int8(&p)); /* compute the sum */ p = compute_sum_lcl_imm(vmg_ valp, &val2, itmp, p); } } continue; case OPC_ADDILCL4: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* if it's an integer, handle it in-line */ if (valp->typ == VM_INT) { /* it's a number - just add the value */ INT_ADD(valp, get_op_int32(&p)); } else { /* get the number to add */ val2.set_int(get_op_int32(&p)); /* compute the sum */ p = compute_sum_lcl_imm(vmg_ valp, &val2, itmp, p); } } continue; case OPC_ADDTOLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* get the value to add */ valp2 = get(0); /* if they're both integers, handle in-line */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* add the value to the local */ INT_ADD(valp, valp2->val.intval); /* done with the addend */ discard(); } else { /* compute the sum, leaving the result in the local */ if (compute_sum(vmg_ valp, valp2)) { /* done - discard the addend */ discard(); } else { /* * check for an overload - do it recursively, * since we need to update the local on return */ push(valp2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_add, 1, VMERR_BAD_TYPE_ADD); } } } continue; case OPC_SUBFROMLCL: { /* get the local */ int itmp = get_op_uint16(&p); valp = get_local(vmg_ itmp); /* get the value to add */ valp2 = get(0); /* if they're both integers, handle in-line */ if (valp->typ == VM_INT && valp2->typ == VM_INT) { /* subtract the value from the local */ INT_SUB(valp, valp2->val.intval); /* discard the subtrahend */ discard(); } else if (compute_diff(vmg_ valp, valp2)) { /* done - discard the subtrahend */ discard(); } else { /* * no native implementation - check for an * overload; do this recursively since we need to * update the local on return */ push(valp2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_sub, 1, VMERR_BAD_TYPE_SUB); } } continue; case OPC_ZEROLCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_int(0); continue; case OPC_ZEROLCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_int(0); continue; case OPC_NILLCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_nil(); continue; case OPC_NILLCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_nil(); continue; case OPC_ONELCL1: /* get the local and set it to zero */ get_local(vmg_ get_op_uint8(&p))->set_int(1); continue; case OPC_ONELCL2: /* get the local and set it to zero */ get_local(vmg_ get_op_uint16(&p))->set_int(1); continue; case OPC_SETLCL2: /* get a pointer to the local */ valp = get_local(vmg_ get_op_uint16(&p)); /* pop the value into the local */ popval(vmg_ valp); continue; case OPC_SETARG1: /* get a pointer to the parameter */ valp = get_param(vmg_ get_op_uint8(&p)); /* pop the value into the parameter */ popval(vmg_ valp); continue; case OPC_SETARG2: /* get a pointer to the parameter */ valp = get_param(vmg_ get_op_uint16(&p)); /* pop the value into the parameter */ popval(vmg_ valp); continue; case OPC_SETIND: /* pop the index */ popval(vmg_ &val2); /* pop the value to be indexed */ popval(vmg_ &val); /* pop the value to assign */ popval(vmg_ &val3); /* assign into the index */ if (set_index(vmg_ &val, &val2, &val3)) { /* push the new container value */ pushval(vmg_ &val); } else { /* try operator overloading */ push(&val3); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, -1, &val, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); } continue; case OPC_SETINDLCL1I8: { /* get the local */ int itmp = get_op_uint8(&p); valp = get_local(vmg_ itmp); /* get the index value */ val2.set_int(get_op_uint8(&p)); /* pop the value to assign */ popval(vmg_ &val3); /* * set the index value - this will update the local * variable directly if the container value changes */ if (!set_index(vmg_ valp, &val2, &val3)) { /* try operator overloading */ push(&val3); push(&val2); p = op_overload(vmg_ p - entry_ptr_native_, itmp, valp, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); } } continue; case OPC_SETPROP: /* get the object whose property we're setting */ pop_obj(vmg_ &val); /* pop the value we're setting */ popval(vmg_ &val2); /* set the value */ set_prop(vmg_ val.val.obj, get_op_uint16(&p), &val2); continue; case OPC_PTRSETPROP: /* get the property and object to set */ pop_prop(vmg_ &val); pop_obj(vmg_ &val2); /* get the value to set */ popval(vmg_ &val3); /* set it */ set_prop(vmg_ val2.val.obj, val.val.prop, &val3); continue; case OPC_OBJSETPROP: /* get the object */ obj = (vm_obj_id_t)get_op_uint32(&p); /* get the new value */ popval(vmg_ &val); /* set the property */ set_prop(vmg_ obj, get_op_uint16(&p), &val); continue; case OPC_PUSHSTRI: /* push inline string */ { /* get the length prefix */ uint cnt = get_op_uint16(&p); /* create the new string from the inline data */ obj = CVmObjString::create( vmg_ FALSE, (const char *)p, cnt); /* skip past the string's bytes */ p += cnt; /* push the new string */ push()->set_obj(obj); } continue; case OPC_PUSHBIFPTR: { /* push pointer to built-in function */ uint idx = get_op_uint16(&p); push()->set_bifptr(get_op_uint16(&p), (ushort)idx); validate_bifptr(vmg0_); } continue; case OPC_THROW: /* pop the exception object */ pop_obj(vmg_ &val); /* * Throw it. Note that we pass the start of the current * instruction as the program counter, since we want to * find the exception handler (if any) for the current * instruction, not for the next instruction. */ if ((p = do_throw(vmg_ p - 1, val.val.obj)) == 0) { /* remember the unhandled exception for re-throwing */ unhandled_exc = val.val.obj; /* terminate execution */ goto exit_loop; } continue; #ifdef VM_DEBUGGER case OPC_GETPROPDATA: /* get the object whose property we're fetching */ pop(&propev.self); /* * if the object is not an object, it's one of the native * types, in which case we'll definitely run native code to * evaluate the property, in which case it's not valid for * speculative evaluation */ if (propev.self.typ != VM_OBJ) err_throw(VMERR_BAD_SPEC_EVAL); /* get the property */ prop = (vm_prop_id_t)get_op_uint16(&p); /* check validity for speculative evaluation */ check_prop_spec_eval(vmg_ propev.self.val.obj, prop); /* evaluate the property given by the immediate data */ p = propev.get_prop(vmg_ p - entry_ptr_native_, prop); continue; case OPC_PTRGETPROPDATA: /* get the property and object to evaluate */ pop_prop(vmg_ &val); pop(&propev.self); /* * if the object is not an object, it's one of the native * types, in which case we'll definitely run native code to * evaluate the property, in which case it's not valid for * speculative evaluation */ if (propev.self.typ != VM_OBJ) err_throw(VMERR_BAD_SPEC_EVAL); /* check validity for speculative evaluation */ check_prop_spec_eval(vmg_ propev.self.val.obj, val.val.prop); /* evaluate it */ p = propev.get_prop(vmg_ p - entry_ptr_native_, val.val.prop); continue; case OPC_GETDBARGC: /* push the argument count from the selected frame */ push_int(vmg_ get_argc_at_level(vmg_ get_op_uint16(&p) + 1)); continue; case OPC_GETDBLCL: { /* get the local variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* push the value */ pushval(vmg_ get_local_at_level(vmg_ idx, level + 1)); } continue; case OPC_GETDBARG: { /* get the parameter variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* push the value */ pushval(vmg_ get_param_at_level(vmg_ idx, level + 1)); } continue; case OPC_SETDBLCL: { /* get the local variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* get the local pointer */ vm_val_t *valp = get_local_at_level(vmg_ idx, level + 1); /* pop the value into the local */ popval(vmg_ valp); } continue; case OPC_SETDBARG: { /* get the parameter variable number and stack level */ uint idx = get_op_uint16(&p); int level = get_op_uint16(&p); /* get the parameter pointer */ vm_val_t *valp = get_param_at_level(vmg_ idx, level + 1); /* pop the value into the local */ popval(vmg_ valp); } continue; case OPC_BP: /* step back to the breakpoint location itself */ --p; /* let the debugger take control */ G_debugger->step(vmg_ &p, entry_ptr_native_, TRUE, 0); /* if we're halting, get out of here */ if (halt_vm_) err_throw(VMERR_DBG_HALT); /* * go back and execute the current instruction - bypass * single-step tracing into the debugger in this case, * since the debugger expects when it returns that one * instruction will always be traced before the debugger is * re-entered */ goto exec_instruction; #else /* VM_DEBUGGER */ case OPC_GETDBARGC: case OPC_GETDBLCL: case OPC_GETDBARG: case OPC_SETDBLCL: case OPC_SETDBARG: case OPC_GETPROPDATA: case OPC_PTRGETPROPDATA: err_throw(VMERR_NO_DEBUGGER); continue; case OPC_BP: /* if there's no debugger, it's an error */ err_throw(VMERR_BREAKPOINT); continue; #endif /* VM_DEBUGGER */ case OPC_CALLEXT: //$$$ err_throw(VMERR_CALLEXT_NOT_IMPL); continue; #ifdef OS_FILL_OUT_CASE_TABLES /* * Since we this switch is the innermost inner loop of the VM, * we go to some extra lengths to optimize it where possible. * See tads2/osifc.h for information on how to use * OS_FILL_OUT_CASE_TABLES and OS_IMPOSSIBLE_DEFAULT_CASE. * * Our controlling expression is an unsigned character value, * so we know the range of possible values will be limited to * 0-255. Therefore, we simply need to provide a "case" * alternative for every invalid opcode. To further encourage * the compiler to favor speed here, we specifically put * different code in every one of these case alternatives, to * force the compiler to generate a separate jump location for * each one; some compilers will generate a two-level jump * table if many cases point to shared code, to reduce the size * of the table, but we don't want that here because this * switch is critical to VM performance so we want it as fast * as possible. */ case 0x00: val.val.intval = 0x00; case 0x11: val.val.intval = 0x11; case 0x12: val.val.intval = 0x12; case 0x13: val.val.intval = 0x13; case 0x14: val.val.intval = 0x14; case 0x15: val.val.intval = 0x15; case 0x16: val.val.intval = 0x16; case 0x17: val.val.intval = 0x17; case 0x18: val.val.intval = 0x18; case 0x19: val.val.intval = 0x19; case 0x1A: val.val.intval = 0x1A; case 0x1B: val.val.intval = 0x1B; case 0x1C: val.val.intval = 0x1C; case 0x1D: val.val.intval = 0x1D; case 0x1E: val.val.intval = 0x1E; case 0x1F: val.val.intval = 0x1F; case 0x31: val.val.intval = 0x31; case 0x32: val.val.intval = 0x32; case 0x33: val.val.intval = 0x33; case 0x34: val.val.intval = 0x34; case 0x35: val.val.intval = 0x35; case 0x36: val.val.intval = 0x36; case 0x37: val.val.intval = 0x37; case 0x38: val.val.intval = 0x38; case 0x39: val.val.intval = 0x39; case 0x3A: val.val.intval = 0x3A; case 0x3B: val.val.intval = 0x3B; case 0x3C: val.val.intval = 0x3C; case 0x3D: val.val.intval = 0x3D; case 0x3E: val.val.intval = 0x3E; case 0x3F: val.val.intval = 0x3F; case 0x46: val.val.intval = 0x46; case 0x47: val.val.intval = 0x47; case 0x48: val.val.intval = 0x48; case 0x49: val.val.intval = 0x49; case 0x4A: val.val.intval = 0x4A; case 0x4B: val.val.intval = 0x4B; case 0x4C: val.val.intval = 0x4C; case 0x4D: val.val.intval = 0x4D; case 0x4E: val.val.intval = 0x4E; case 0x4F: val.val.intval = 0x4F; case 0x53: val.val.intval = 0x53; case 0x55: val.val.intval = 0x55; case 0x5A: val.val.intval = 0x5A; case 0x5B: val.val.intval = 0x5B; case 0x5C: val.val.intval = 0x5C; case 0x5D: val.val.intval = 0x5D; case 0x5E: val.val.intval = 0x5E; case 0x5F: val.val.intval = 0x5F; case 0x6E: val.val.intval = 0x6E; case 0x6F: val.val.intval = 0x6F; case 0x70: val.val.intval = 0x70; case 0x71: val.val.intval = 0x71; case 0x79: val.val.intval = 0x7D; // case 0xA2: val.val.intval = 0xA2; // case 0xA3: val.val.intval = 0xA3; // case 0xA4: val.val.intval = 0xA4; // case 0xA5: val.val.intval = 0xA5; case 0xA7: val.val.intval = 0xA7; case 0xA8: val.val.intval = 0xA8; case 0xA9: val.val.intval = 0xA9; case 0xBD: val.val.intval = 0xBD; case 0xBE: val.val.intval = 0xBE; case 0xBF: val.val.intval = 0xBF; case 0xC4: val.val.intval = 0xC4; case 0xC5: val.val.intval = 0xC5; case 0xC6: val.val.intval = 0xC6; case 0xC7: val.val.intval = 0xC7; case 0xC8: val.val.intval = 0xC8; case 0xC9: val.val.intval = 0xC9; case 0xCA: val.val.intval = 0xCA; case 0xCB: val.val.intval = 0xCB; case 0xCC: val.val.intval = 0xCC; case 0xCD: val.val.intval = 0xCD; case 0xCE: val.val.intval = 0xCE; case 0xCF: val.val.intval = 0xCF; case 0xDC: val.val.intval = 0xDC; case 0xDD: val.val.intval = 0xDD; case 0xDE: val.val.intval = 0xDE; case 0xDF: val.val.intval = 0xDF; case 0xF0: val.val.intval = 0xF0; case 0xF3: val.val.intval = 0xF3; case 0xF4: val.val.intval = 0xF4; case 0xF5: val.val.intval = 0xF5; case 0xF6: val.val.intval = 0xF6; case 0xF7: val.val.intval = 0xF7; case 0xF8: val.val.intval = 0xF8; case 0xF9: val.val.intval = 0xF9; case 0xFA: val.val.intval = 0xFA; case 0xFB: val.val.intval = 0xFB; case 0xFC: val.val.intval = 0xFC; case 0xFD: val.val.intval = 0xFD; case 0xFE: val.val.intval = 0xFE; case 0xFF: val.val.intval = 0xFF; err_throw(VMERR_INVALID_OPCODE); OS_IMPOSSIBLE_DEFAULT_CASE #else /* OS_FILL_OUT_CASE_TABLES */ case 0: /* * Explicitly call out this invalid instruction case so * that we can avoid extra work in computing the switch. * Some compilers will be smart enough to observe that we * populate the full range of possible values (0-255) for * the datatype of the switch control expression, and thus * will build jump tables that can be jumped through * without range-checking the value. (No range checking * is necessary, because a uchar simply cannot hold any * values outside of the 0-255 range.) This doesn't * guarantee that the compiler will be smart, but it does * help with some compilers and shouldn't hurt performance * with those that don't make any use of the situation. */ err_throw(VMERR_INVALID_OPCODE); case 0xFF: /* * explicitly call out this invalid instruction for the * same reasons we call out case 0 above */ err_throw(VMERR_INVALID_OPCODE); default: /* unrecognized opcode */ err_throw(VMERR_INVALID_OPCODE); continue; #endif /* OS_FILL_OUT_CASE_TABLES */ } } /* * We jump to this label when it's time to terminate execution * and return to the host environment which called us. */ exit_loop: /* note that we're ready to return */ done = TRUE; } err_catch(err) { int i; volatile int released_reserve = FALSE; /* if the debugger has halted the VM, re-throw the error */ VM_IF_DEBUGGER(if (halt_vm_) err_rethrow();) err_try { /* * Return to the start of the most recent instruction - we've * already at least partially decoded the instruction, so we * won't be pointing to its first byte. Note that last_pc is * a non-register variable (because we take its address to * store in pc_ptr_), so it will correctly indicate the * current instruction even though we've jumped here via * longjmp. */ p = last_pc; /* * Create a new exception object to describe the error. The * arguments to the constructor are the error number and the * error parameters. * * If the error code is "unhandled exception," it means that * an exception occurred in a recursive interpreter * invocation, and the exception wasn't handled within the * code called recursively; in this case, we can simply * re-throw the original error, and perhaps handle it in the * context of the current code. */ if (err->get_error_code() == VMERR_UNHANDLED_EXC) { /* get the original exception object from the error stack */ obj = (vm_obj_id_t)err->get_param_ulong(0); } else { /* step into the debugger, if it's present */ VM_IF_DEBUGGER( { const uchar *dbgp; /* * If we're in the process of halting the VM, don't * bother stepping into the debugger. We'll check the * same thing in a moment, after we get back from * stepping into the debugger, but this check isn't * redundant: we could already be halting even before * we enter the debugger here, because we could be * unwinding the native (C++) error stack on our way * out from such a halt. */ if (halt_vm_) { done = TRUE; goto skip_throw; } /* make a copy of the PC for the debugger's use */ dbgp = p; /* step into the debugger */ G_debugger->step(vmg_ &dbgp, entry_ptr_native_, FALSE, err->get_error_code()); /* * If the VM was halted while in the debugger, throw * the DEBUG HALT error. (We formerly just exited the * byte-code loop, but that has the effect of returning * to a native method or function caller, and allowing * the native caller to continue to run. We don't want * that to happen because we can't process the normal * unwinding on the return - that would require VM ops, * and we've halted the VM. So, to explicitly unwind * any native callers, we have to throw an exception.) */ if (halt_vm_) err_throw(VMERR_DBG_HALT); /* * if they moved the execution pointer, resume * execution at the new point, discarding the * exception */ if (dbgp != p) { /* resume execution at the new location */ p = dbgp; /* discard the exception and resume execution */ goto skip_throw; } } ); /* * If this is a stack overflow exception, there's probably * not enough stack left to create the exception object. * Fortunately, we have an emergency stack reserve just for * such conditions, so release it now, hopefully giving us * enough room to work with to construct the exception. */ if (err->get_error_code() == VMERR_STACK_OVERFLOW) released_reserve = release_reserve(); /* push the error parameters (in reverse order) */ for (i = err->get_param_count() ; i > 0 ; ) { /* go to the next parameter */ --i; /* see what we have and push an appropriate value */ switch(err->get_param_type(i-1)) { case ERR_TYPE_INT: /* push the integer value */ push_int(vmg_ err->get_param_int(i)); break; case ERR_TYPE_ULONG: /* push the value */ push_int(vmg_ (int32_t)err->get_param_ulong(i)); break; case ERR_TYPE_TEXTCHAR: /* push a new string with the text */ push_obj(vmg_ CVmObjString::create(vmg_ FALSE, err->get_param_text(i), get_strlen(err->get_param_text(i)))); break; case ERR_TYPE_CHAR: /* push a new string with the text */ push_obj(vmg_ CVmObjString::create(vmg_ FALSE, err->get_param_char(i), strlen(err->get_param_char(i)))); break; default: /* unrecognized type - push nil for now */ push_nil(vmg0_); break; } } /* * if there's a RuntimeError base class defined, create an * instance; otherwise, create a simple instance of the * basic object type to throw as a placeholder, since the * program hasn't made any provision to catch run-time * errors */ if (G_predef->rterr != VM_INVALID_OBJ) { /* push the error number */ push_int(vmg_ err->get_error_code()); /* * Create the new RuntimeException instance. Run the * constructor in a recursive invocation of the * interpreter (by passing a null PC pointer). */ vm_objp(vmg_ G_predef->rterr) ->create_instance(vmg_ G_predef->rterr, 0, err->get_param_count() + 1); /* get the object from R0 */ if (r0_.typ != VM_OBJ) err_throw(VMERR_EXCEPTION_OBJ_REQD); obj = r0_.val.obj; } else { /* * There's no RuntimeError object defined by the image * file, so create a basic object to throw. This * won't convey any information to the program except * that it's not one of the errors they're expecting; * this is fine, since they have made no provisions to * catch VM errors, as demonstrated by their lack of a * RuntimeError definition. */ obj = CVmObjTads::create(vmg_ FALSE, 0, 1); } /* * if possible, set the exceptionMessage property in the * new exception object to the default error message for * the run-time error we're processing */ if (G_predef->rterrmsg_prop != VM_INVALID_PROP) { const char *msg; char buf[256]; vm_obj_id_t str_obj; /* format the message text */ msg = err_get_msg(vm_messages, vm_message_count, err->get_error_code(), FALSE); err_format_msg(buf, sizeof(buf), msg, err); /* * momentarily push the new exception object, so we * don't lose track of it if we run garbage collection * here */ push_obj(vmg_ obj); /* create a string object with the message text */ str_obj = CVmObjString::create(vmg_ FALSE, buf, strlen(buf)); /* * before we can build a stack trace, let the debugger * synchronize its current position information */ VM_IF_DEBUGGER( G_debugger->sync_exec_pos(vmg_ p, entry_ptr_native_)); /* set the property in the new object */ val.set_obj(str_obj); vm_objp(vmg_ obj) ->set_prop(vmg_ G_undo, obj, G_predef->rterrmsg_prop, &val); /* we don't need gc protection any more */ discard(); } } /* * If we released the stack reserve, take it back. We've * finished creating the exception object, so we don't need the * emergency stack space any more. We want to put it back now * that we're done with it so that it'll be there for us if we * should run into another stack overflow in the future. */ if (released_reserve) recover_reserve(); /* throw the exception */ if ((p = do_throw(vmg_ p, obj)) == 0) { /* remember the unhandled exception for a moment */ unhandled_exc = obj; } /* come here to skip throwing the exception */ VM_IF_DEBUGGER(skip_throw: ); } err_catch_disc { /* * we got another exception trying to handle the first * exception - just throw the error again, but at least clean * up statics on the way out */ pc_ptr_ = old_pc_ptr; /* if we released the stack reserve, take it back */ if (released_reserve) recover_reserve(); /* re-throw the error */ err_rethrow(); } err_end; } err_end; /* * If an unhandled exception occurred, re-throw it. This will wrap our * exception object in a C++ object and throw it through our C++ * err_try/err_catch exception mechanism, so that the exception is * thrown out of the recursive native-code invoker. */ if (unhandled_exc != VM_INVALID_OBJ) { /* restore the enclosing PC pointer */ pc_ptr_ = old_pc_ptr; /* re-throw the unhandled exception */ err_throw_a(VMERR_UNHANDLED_EXC, 1, ERR_TYPE_ULONG, (unsigned long)unhandled_exc); } /* if we're not done, go back and resume execution */ if (!done) goto resume_execution; /* restore the enclosing PC pointer */ pc_ptr_ = old_pc_ptr; } /* ------------------------------------------------------------------------ */ /* * Validate the built-in function pointer at top of stack */ void CVmRun::validate_bifptr(VMG0_) { /* get the top of stack */ vm_val_t *bifp = G_stk->get(0); /* validate the entry in the bif table */ if (!G_bif_table->validate_entry( bifp->val.bifptr.set_idx, bifp->val.bifptr.func_idx)) { /* invalid entry - throw an error */ char buf[20]; sprintf(buf, "#%d", bifp->val.bifptr.set_idx); err_throw_a(VMERR_UNAVAIL_INTRINSIC, 2, ERR_TYPE_INT, bifp->val.bifptr.func_idx, ERR_TYPE_CHAR, buf); } } /* ------------------------------------------------------------------------ */ /* * Create a method context object suitable for use with the STORECTX and * LOADCTX instructions. */ void CVmRun::create_loadctx_obj(VMG_ vm_val_t *result, vm_obj_id_t self, vm_obj_id_t defobj, vm_obj_id_t targobj, vm_prop_id_t targprop) { char buf[VMB_LEN + 4*VMB_DATAHOLDER]; /* initialize a list template for a four-element list */ vmb_put_len(buf, 4); /* * put the list elements: 'self', targetprop, original target object, * and defining object */ vmb_put_dh_obj(buf + VMB_LEN, self); vmb_put_dh_prop(buf + VMB_LEN + VMB_DATAHOLDER, targprop); vmb_put_dh_obj(buf + VMB_LEN + 2*VMB_DATAHOLDER, targobj); vmb_put_dh_obj(buf + VMB_LEN + 3*VMB_DATAHOLDER, defobj); /* create and return a new list copied from our prepared buffer */ result->set_obj(CVmObjList::create(vmg_ FALSE, buf)); } /* ------------------------------------------------------------------------ */ /* * Get a local variable, parameter, or context local from a stack frame. */ void CVmRun::get_local_from_frame(VMG_ vm_val_t *val, vm_val_t *fp, const CVmDbgFrameSymPtr *symp) { get_local_from_frame(vmg_ val, fp, symp->get_var_num(), symp->is_param(), symp->is_ctx_local(), symp->get_ctx_arr_idx()); } void CVmRun::get_local_from_frame(VMG_ vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx) { /* check which kind of variable we have */ if (is_param) { /* parameter - get it from the frame */ *val = *get_param_from_frame(vmg_ fp, varnum); } else if (is_ctx_local) { /* context local - get the context local */ vm_val_t *cl = get_local_from_frame(vmg_ fp, varnum); /* index it to get the local */ vm_val_t idxval; idxval.set_int(ctx_arr_idx); apply_index(vmg_ val, cl, &idxval); } else { /* regular local - get it from the frame */ *val = *get_local_from_frame(vmg_ fp, varnum); } } /* ------------------------------------------------------------------------ */ /* * Set a local variable, parameter, or context local in a stack frame. */ void CVmRun::set_local_in_frame(VMG_ const vm_val_t *val, vm_val_t *fp, const CVmDbgFrameSymPtr *symp) { set_local_in_frame(vmg_ val, fp, symp->get_var_num(), symp->is_param(), symp->is_ctx_local(), symp->get_ctx_arr_idx()); } void CVmRun::set_local_in_frame(VMG_ const vm_val_t *val, vm_val_t *fp, int varnum, int is_param, int is_ctx_local, int ctx_arr_idx) { /* check which kind of variable we have */ if (is_param) { /* parameter - get it from the frame */ *get_param_from_frame(vmg_ fp, varnum) = *val; } else if (is_ctx_local) { /* context local - get the context local */ vm_val_t *cl = get_local_from_frame(vmg_ fp, varnum); /* index it to set the local */ vm_val_t idxval; idxval.set_int(ctx_arr_idx); set_index(vmg_ cl, &idxval, val); } else { /* regular local - set it in the frame */ *get_local_from_frame(vmg_ fp, varnum) = *val; } } /* ------------------------------------------------------------------------ */ /* * Get a named argument table from a stack frame. Returns a pointer to the * table if we find one, null if not. Fills in arg0 with the first named * argument in the stack frame; the nth named argument is at (arg0+n). */ const uchar *CVmRun::get_named_args_from_frame( VMG_ vm_val_t *fp, vm_val_t **arg0) { /* get the return address */ pool_ofs_t ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); const uchar *ep = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); const vm_rcdesc *rc = G_interpreter->get_rcdesc_from_frame(vmg_ fp); /* check for a recursive native code caller */ if (ofs == 0 && rc != 0 && rc->has_return_addr()) { /* get the return address from the descriptor */ ofs = rc->get_return_addr() - ep; } /* if there's no return address, there's no table */ if (ep == 0 || ofs == 0) return 0; /* get the return instruction pointer */ const uchar *ip = ep + ofs; /* if it's a NamedArgPtr instruction, follows the pointer */ if (*ip == OPC_NAMEDARGPTR) ip += 2 + osrp2(ip+2); /* make sure we're at a NamedArgTab instruction */ if (*ip == OPC_NAMEDARGTAB) { /* * We have a named argument table. Fill in *arg0 with a pointer to * the first named argument value in the stack frame. * * If there's a native caller at this level, get the arguments to * the native caller instead of the arguments to the bytecode * callee. Native callers never send named arguments, so we can * ignore the callee's arguments and look only at the native * caller's arguments. */ int argc; if (rc != 0) { /* native caller - get the native code's arguments */ argc = rc->argc; *arg0 = rc->argp - argc; } else { /* bytecode caller - get the current level's arguments */ argc = G_interpreter->get_argc_from_frame(vmg_ fp); *arg0 = G_interpreter->get_param_from_frame(vmg_ fp, argc); } /* skip the NamedArgTab opcode and table length prefix */ ip += 1 + 2; /* * Adjust arg0 for the number of arguments. The compiler generates * the names in the same order as the pushes, so the first name is * the first value pushed. */ *arg0 -= (osrp2(ip) - 1); /* * return the table pointer - it starts after the NamedArgTab * instruction and table byte length prefix */ return ip; } /* there's no named argument table in this return frame */ return 0; } /* ------------------------------------------------------------------------ */ /* * Throw an exception of the given class, with the constructor arguments * on the stack. */ void CVmRun::throw_new_class(VMG_ vm_obj_id_t cls, uint argc, const char *fallback_msg) { /* if the class is defined, create an instance and throw it */ if (cls != VM_INVALID_OBJ) { /* create the object */ vm_objp(vmg_ cls)->create_instance(vmg_ cls, 0, argc); /* make sure we created an object */ if (r0_.typ == VM_OBJ) { vm_obj_id_t exc_obj; /* get the object from R0 */ exc_obj = r0_.val.obj; /* * throw an 'unhandled exception' with this object as the * parameter; the execution loop will catch it and dispatch it * properly */ err_throw_a(VMERR_UNHANDLED_EXC, 1, ERR_TYPE_ULONG, (unsigned long)exc_obj); } else err_throw(VMERR_EXCEPTION_OBJ_REQD); } /* * the imported exception class isn't defined, or we failed to create * it; throw a generic intrinsic class exception with the fallback * message string */ err_throw_a(VMERR_INTCLS_GENERAL_ERROR, 1, ERR_TYPE_CHAR, fallback_msg); } /* * Throw a new RuntimeError subclass. */ void CVmRun::throw_new_rtesub(VMG_ vm_obj_id_t cls, uint argc, int errnum) { /* if the subclass is defined, create an instance and throw it */ if (cls != VM_INVALID_OBJ) { /* push the error number as the first argument */ push_int(vmg_ errnum); /* throw the subclass error */ throw_new_class(vmg_ cls, argc + 1, 0); } /* the subclass isn't defined, so throw a generic RuntimeError */ err_throw(errnum); } /* ------------------------------------------------------------------------ */ /* * Throw an exception. Returns true if an exception handler was found, * which means that execution can proceed; returns false if no handler * was found, in which case the execution loop must throw the exception * to its caller. */ const uchar *CVmRun::do_throw(VMG_ const uchar *pc, vm_obj_id_t exception_obj) { /* * Search the stack for a handler for this exception class. Start * at the current stack frame; if we find a handler here, use it; * otherwise, unwind the stack to the enclosing frame and search for * a handler there; repeat until we exhaust the stack. */ for (;;) { CVmExcTablePtr tab; const uchar *func_start; uint ofs; /* get a pointer to the start of the current function */ func_start = entry_ptr_native_; /* set up a pointer to the current exception table */ if (func_start != 0 && tab.set(func_start)) { size_t cnt; size_t i; CVmExcEntryPtr entry; /* calculate our offset in the current function */ ofs = pc - func_start; /* set up a pointer to the first table entry */ tab.set_entry_ptr(vmg_ &entry, 0); /* loop through the entries */ for (i = 0, cnt = tab.get_count() ; i < cnt ; ++i, entry.inc(vmg0_)) { /* * Check to see if we're in the range for this entry. * If this entry covers the appropriate range, and the * exception we're handling is of the class handled by * this exception (or derives from that class), this * handler handles this exception. */ if (ofs >= entry.get_start_ofs() && ofs <= entry.get_end_ofs() && (entry.get_exception() == VM_INVALID_OBJ || exception_obj == entry.get_exception() || (vm_objp(vmg_ exception_obj) ->is_instance_of(vmg_ entry.get_exception())))) { /* * this is it - move the program counter to the * first byte of the handler's code */ pc = func_start + entry.get_handler_ofs(); /* push the exception so that the handler can get at it */ push_obj(vmg_ exception_obj); /* return the new program counter at which to resume */ return pc; } } } /* * We didn't find a handler in the current function - unwind the * stack one level, using an ordinary RETURN operation (we're not * really returning, though, so we don't need to provide a return * value). First, though, check to make sure there is an enclosing * frame at all - if there's not, we can simply return immediately. */ if (frame_ptr_ == 0) { /* there's no enclosing frame, so there's nowhere to go */ return 0; } /* try unwinding the stack a level */ if ((pc = do_return(vmg0_)) == 0) { /* * The enclosing frame is a recursive invocation, so we cannot * unwind any further at this point. Return null to indicate * that the exception was not handled and should be thrown out * of the current recursive VM invocation. */ return 0; } } } /* ------------------------------------------------------------------------ */ /* * Call a built-in function */ void CVmRun::call_bif(VMG_ uint set_index, uint func_index, uint argc) { /* * Call the function -- presume the compiler has ensured that the * function set index is valid for the load image, and that the * function index is valid for the function set; all of this can be * determined at compile time, since function sets are statically * defined. */ G_bif_table->call_func(vmg_ set_index, func_index, argc); } /* ------------------------------------------------------------------------ */ /* * Call a function pointer */ const uchar *CVmRun::call_func_ptr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs) { /* * Prepare the invocation frame. Even though we're calling this as a * function, if the value is an object, pass it as the 'self' value, * for compatibility with the old anonymous function invocation * protocol. */ vm_val_t *fp = G_stk->push(5); (fp++)->set_propid(VM_INVALID_PROP); (fp++)->set_nil(); (fp++)->set_nil(); if (funcptr->typ == VM_OBJ) (fp++)->set_obj(funcptr->val.obj); else (fp++)->set_nil_obj(); *(fp++) = *funcptr; /* call the function */ return call_func_ptr_fr(vmg_ funcptr, argc, recurse_ctx, caller_ofs); } /* * Call a function pointer, with the invocation frame already set up */ const uchar *CVmRun::call_func_ptr_fr(VMG_ const vm_val_t *funcptr, uint argc, const vm_rcdesc *recurse_ctx, uint caller_ofs) { vm_val_t invoker; CVmObject *io; /* check what we have */ switch (funcptr->typ) { case VM_OBJ: /* it's an object - check to see if it's invokable */ if ((io = vm_objp(vmg_ funcptr->val.obj))->get_invoker(vmg_ &invoker)) { /* get the invocation address, according to the invoker type */ const void *invoke_addr; switch (invoker.typ) { case VM_FUNCPTR: invoke_addr = G_code_pool->get_ptr(invoker.val.ofs); break; case VM_CODEPTR: invoke_addr = invoker.val.ptr; break; default: invoke_addr = 0; break; } /* if we got a valid address, invoke the code */ if (invoke_addr != 0) { /* call the function and return the new program counter */ return do_call( vmg_ caller_ofs, (const uchar *)invoke_addr, argc, recurse_ctx); } } /* it's not invokable - throw an error */ err_throw(VMERR_FUNCPTR_VAL_REQD); AFTER_ERR_THROW(return 0;) case VM_FUNCPTR: /* call the function */ return do_call(vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(funcptr->val.ofs), argc, recurse_ctx); case VM_BIFPTR: /* * Built-in function. First, discard the invocation frame the * caller set up for a regular method/function call, as builtins * don't use those. Then, decode the SetIndex:FuncIndex value and * call the function. */ discard(5); call_bif(vmg_ funcptr->val.bifptr.set_idx, funcptr->val.bifptr.func_idx, argc); /* proceed from the caller's address */ return entry_ptr_native_ + caller_ofs; default: /* invalid type */ err_throw(VMERR_FUNCPTR_VAL_REQD); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Call a function, non-recursively. * * This is a separate form of do_call(), but simplified for cases where we * know in advance that we won't need to check for recursion and when we * know in advance that we're calling a function and thus have no 'self' * or other method context objects. These simplifications reduce the * amount of work we have to do, so that ordinary function calls run a * little faster than they would if we used the full do_call() routine. */ const uchar *CVmRun::do_call_func_nr(VMG_ uint caller_ofs, pool_ofs_t target_ofs, uint argc) { const uchar *target_ofs_ptr; CVmFuncPtr hdr_ptr; uint i; vm_val_t *fp; int lcl_cnt; /* store nil in R0 */ r0_.set_nil(); /* translate the target address */ target_ofs_ptr = (const uchar *)G_code_pool->get_ptr(target_ofs); /* set up a pointer to the new function header */ hdr_ptr.set(target_ofs_ptr); /* get the number of locals from the header */ lcl_cnt = hdr_ptr.get_local_cnt(); /* get the target's stack space needs and check for stack overflow */ if (!check_space(hdr_ptr.get_stack_depth() + 11)) err_throw(VMERR_STACK_OVERFLOW); /* allocate the stack frame */ fp = push(11 + lcl_cnt); /* there's no target property, target object, defining object, or self */ (fp++)->set_propid(VM_INVALID_PROP); (fp++)->set_nil(); (fp++)->set_nil(); (fp++)->set_nil_obj(); /* push the invokee */ (fp++)->set_fnptr(target_ofs); /* push the frame reference slot */ (fp++)->set_nil(); /* push the native code descriptor (none for a non-recursive call) */ (fp++)->set_codeptr(0); /* push the caller's code offset */ (fp++)->set_codeofs(caller_ofs); /* push the current entrypoint code offset */ (fp++)->set_codeptr(entry_ptr_native_); /* push the actual parameter count */ (fp++)->set_int((int32_t)argc); /* push the current frame pointer */ (fp++)->set_stack(frame_ptr_); /* verify the argument count */ if (!hdr_ptr.argc_ok(argc)) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* set up the new stack frame */ frame_ptr_ = fp; /* load EP with the new code offset */ entry_ptr_native_ = target_ofs_ptr; /* push nil for each local */ for (i = lcl_cnt ; i != 0 ; --i) (fp++)->set_nil(); /* create and activate the new function's profiler frame */ VM_IF_PROFILER(if (profiling_) prof_enter(vmg_ target_ofs_ptr)); /* return the new program counter */ return target_ofs_ptr + get_funchdr_size(); } /* ------------------------------------------------------------------------ */ /* * Recursive call descriptor */ /* set up a descriptor for a system caller */ void vm_rcdesc::init_ret(VMG_ const char *name) { this->name = name; bifptr.set_nil(); self.set_nil(); method_idx = 0; argc = 0; argp = 0; caller_addr = G_interpreter->get_last_pc(); } /* set up a descriptor for a built-in function */ void vm_rcdesc::init(VMG_ const char *name, const vm_bif_desc *funcset, int idx, vm_val_t *argp, int argc) { this->name = name; this->self.set_nil(); this->method_idx = 0; this->bifptr = funcset[idx].bifptr; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* initialize for an intrinsic class method */ void vm_rcdesc::init(VMG_ const char *name, vm_obj_id_t self, unsigned short method_idx, vm_val_t *argp, int argc) { this->name = name; this->bifptr.set_nil(); this->self.set_obj(self); this->method_idx = method_idx; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* initialize for an intrinsic class method */ void vm_rcdesc::init(VMG_ const char *name, const vm_val_t *self, unsigned short method_idx, vm_val_t *argp, int argc) { this->name = name; this->bifptr.set_nil(); this->self = *self; this->method_idx = method_idx; this->argp = argp; this->argc = argc; this->caller_addr = G_interpreter->get_last_pc(); } /* * Calculate the return address in the calling frame */ const uchar *vm_rcdesc::get_return_addr() const { /* if there's no caller address, there's no return address */ if (caller_addr == 0) return 0; /* * caller_addr is a pointer to the instruction that triggered the call * to the native code that in turn made the recursive call represented * by 'this'. The return address is the next instruction, so we just * need to calculate the size of the instruction at caller_addr and add * it to the caller address. Start with one byte for the opcode. */ const uchar *p = caller_addr; /* check for VARARGC, which can prefix many call instructions */ if (*p == OPC_VARARGC) ++p; /* advance past the actual call instruction */ p += CVmOpcodes::get_op_size(p); /* return the final address */ return p; } /* ------------------------------------------------------------------------ */ /* * Call a function or method */ const uchar *CVmRun::do_call(VMG_ uint caller_ofs, const uchar *target_ptr, uint argc, const vm_rcdesc *recurse_ctx) { CVmFuncPtr hdr_ptr; uint i; vm_val_t *fp; int lcl_cnt; /* store nil in R0 */ r0_.set_nil(); /* set up a pointer to the new function header */ hdr_ptr.set(target_ptr); /* get the number of locals from the header */ lcl_cnt = hdr_ptr.get_local_cnt(); /* * Get the space needs of the new function, and ensure we have enough * stack space available. Include the size of the frame that we store * (the original target object, the target property, the defining * object, the 'self' object, the caller's code offset, the caller's * entrypoint offset, the actual parameter count, and the enclosing * frame pointer) in our space needs. */ if (!check_space(hdr_ptr.get_stack_depth() + 11)) err_throw(VMERR_STACK_OVERFLOW); /* * Allocate the stack frame. Note that the caller has already pushed * targetprop, targetobj, definingobj, and self. */ fp = push(6 + lcl_cnt); /* push the frame reference slot */ (fp++)->set_nil(); /* push the native code descriptor */ (fp++)->set_codeptr(recurse_ctx); /* * Push the caller's code offset. Note that if the caller's offset is * zero, it indicates that the caller is not the byte-code interpreter * and that this is a recursive invocation; we represent recursive * frames using a zero caller offset, so we can just use the zero * value as given in this case. */ (fp++)->set_codeofs(caller_ofs); /* push the current entrypoint code offset */ (fp++)->set_codeptr(entry_ptr_native_); /* push the actual parameter count */ (fp++)->set_int((int32_t)argc); /* push the current frame pointer */ (fp++)->set_stack(frame_ptr_); /* * check the argument count - do this before establishing the new * frame and entry pointers, so that if we report a stack traceback in * the debugger, we'll report the error in the calling frame, which is * where it really belongs */ if (!hdr_ptr.argc_ok(argc)) { /* * if we're making a recursive call, throw an error indicating * what kind of recursive call we're making */ if (recurse_ctx != 0) { /* throw the named generic argument mismatch error */ err_throw_a(VMERR_WRONG_NUM_OF_ARGS_CALLING, 1, ERR_TYPE_CHAR, recurse_ctx->name); } else { /* throw the generic argument mismatch error */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } } /* * set up the new frame so that the frame pointer points to the old * frame pointer stored in the stack */ frame_ptr_ = fp; /* load EP with the new code offset */ entry_ptr_native_ = target_ptr; /* push nil for each local */ for (i = lcl_cnt ; i != 0 ; --i) (fp++)->set_nil(); /* create and activate the new function's profiler frame */ VM_IF_PROFILER(if (profiling_) prof_enter(vmg_ target_ptr)); /* if desired, make a recursive call into the byte code interpreter */ if (caller_ofs != 0) { /* * return the new program counter at the first byte of code in the * new function, which immediately follows the header */ return target_ptr + get_funchdr_size(); } else { /* recursively call the interpreter loop */ run(vmg_ target_ptr + get_funchdr_size()); /* * this was a recursive call, so there's no program counter to * return - just return null */ return 0; } } /* * Return from the current function. Returns true if execution can * proceed, false if this returns us out of the outermost function, in * which case the execution loop must terminate and return control to * the host environment. */ const uchar *CVmRun::do_return(VMG0_) { /* invalidate the frame reference object, if present */ vm_val_t *fro = get_frameref_slot(vmg_ frame_ptr_); if (fro->typ == VM_OBJ && CVmObjFrameRef::is_frameref_obj(vmg_ fro->val.obj)) ((CVmObjFrameRef *)vm_objp(vmg_ fro->val.obj))->inval_frame(vmg0_); /* * The frame pointer always points to the location on the stack * where we pushed the enclosing frame pointer. Reset the stack * pointer to the current frame pointer, then pop the enclosing * frame pointer. */ set_sp(frame_ptr_); frame_ptr_ = (vm_val_t *)get(0)->val.ptr; /* get the enclosing argument count */ int argc = get(1)->val.intval; /* restore the enclosing entry pointer */ entry_ptr_native_ = (const uchar *)get(2)->val.ptr; /* restore the enclosing code offset */ pool_ofs_t caller_ofs = get(3)->val.ofs; /* * Discard the actual parameters, plus the 'self', defining object, * original target object, and target property values. While we're at * it, also discard the enclosing frame pointer, enclosing argument * count, enclosing entry pointer, and enclosing code offset, which * we've already restored. */ discard(argc + 11); /* leave the profiler stack level */ VM_IF_PROFILER(if (profiling_) prof_leave()); /* if we're debugging, notify the debugger of the return */ VM_IF_DEBUGGER(G_debugger->step_return(vmg0_)); /* * if this is a special return address code, execute the appropraite * local subroutine */ if (vmrun_is_special_return(caller_ofs)) { vm_val_t v; switch (caller_ofs) { case VMRUN_RET_RECURSIVE: /* * return from recursive VM invocation - just return 0 as the * finishing offset to tell the caller to exit the bytecode * execution loop */ return 0; case VMRUN_RET_OP: /* * returning from operator evaluation - push R0 as the operand * result, and return to the address currently at TOS */ caller_ofs = get(0)->val.intval; *get(0) = r0_; break; case VMRUN_RET_OP_ASILCL: /* * Returning from an operator evaluation, with assignment to a * local. The local variable number is at TOS; assign it, then * return to the address next on the stack. */ pop(&v); *get_local(vmg_ v.val.intval) = r0_; pop(&v); caller_ofs = v.val.intval; break; default: /* * invalid address; we're going to dump into random code if we * continue, so nip it in the bud and throw an invalid op now */ err_throw(VMERR_INVALID_OPCODE); } } /* * Empirically, about half the time, the caller will push the return * value onto the stack. It's a little faster to test for that case * here and handle it specially. */ if (*(entry_ptr_native_ + caller_ofs) == OPC_GETR0) { push(&r0_); caller_ofs += 1; } /* * return the new program counter - calculate the PC offset by adding * the offset within the method to the entry pointer */ return entry_ptr_native_ + caller_ofs; } /* ------------------------------------------------------------------------ */ /* * Execution context save/restore - these are for the debugger's use, so * that it can save the machine registers while executing a debugger * expression. */ #ifdef VM_DEBUGGER /* * save the execution context */ void CVmRun::save_context(VMG_ vmrun_save_ctx *ctx) { /* save our registers */ ctx->entry_ptr_native_ = entry_ptr_native_; ctx->frame_ptr_ = frame_ptr_; ctx->pc_ptr_ = pc_ptr_; /* save the stack depth */ ctx->old_stack_depth_ = get_depth(); } /* * restore the execution context */ void CVmRun::restore_context(VMG_ vmrun_save_ctx *ctx) { /* restore our registers */ entry_ptr_native_ = ctx->entry_ptr_native_; frame_ptr_ = ctx->frame_ptr_; pc_ptr_ = ctx->pc_ptr_; /* if there's anything extra left on the stack, discard it */ if (get_depth() > ctx->old_stack_depth_) discard(get_depth() - ctx->old_stack_depth_); } #endif /* VM_DEBUGGER */ /* ------------------------------------------------------------------------ */ /* * Append a stack trace to a string. This is only meaningful in a * debugger-equipped version. */ #if VM_DEBUGGER /* * callback context for stack trace appender */ struct append_stack_ctx { /* the string so far */ vm_obj_id_t str_obj; /* globals */ vm_globals *vmg; /* frame pointer where we pushed our string for gc protection */ vm_val_t *gc_fp; }; /* * stack trace callback */ static void append_stack_cb(void *ctx0, const char *str, int strl) { append_stack_ctx *ctx = (append_stack_ctx *)ctx0; size_t new_len; size_t old_len; const char *old_str; char *new_str; /* set up access to globals */ VMGLOB_PTR(ctx->vmg); /* get the original string text */ old_str = vm_objp(vmg_ ctx->str_obj)->get_as_string(vmg0_); old_len = vmb_get_len(old_str); old_str += VMB_LEN; /* * allocate a new string, big enough for the old string plus the new * text, plus a newline */ new_len = old_len + strl + 1; ctx->str_obj = CVmObjString::create(vmg_ FALSE, new_len); /* get the new string buffer */ new_str = ((CVmObjString *)vm_objp(vmg_ ctx->str_obj))->cons_get_buf(); /* build the new string */ memcpy(new_str, old_str, old_len); new_str[old_len] = '\n'; memcpy(new_str + old_len + 1, str, strl); /* * replace our gc-protective stack reference to the old string with * the new string - we're done with the old string now, so it's okay * if it gets collected, but we obviously want to keep the new one * around */ G_interpreter->get_from_frame(ctx->gc_fp, 0)->set_obj(ctx->str_obj); } /* * append a stack trace to the given string */ vm_obj_id_t CVmRun::append_stack_trace(VMG_ vm_obj_id_t str_obj) { append_stack_ctx ctx; /* push the string for protection from gc */ push_obj(vmg_ str_obj); /* call the debugger to set up the stack traceback */ ctx.str_obj = str_obj; ctx.vmg = VMGLOB_ADDR; ctx.gc_fp = get_sp(); G_debugger->build_stack_listing(vmg_ &append_stack_cb, &ctx, TRUE); /* discard the gc protection */ discard(); /* return the result string */ return ctx.str_obj; } #endif /* VM_DEBUGGER */ /* ------------------------------------------------------------------------ */ /* * Set a property of an object */ void CVmRun::set_prop(VMG_ vm_obj_id_t obj, vm_prop_id_t prop, const vm_val_t *new_val) { /* set the property */ vm_objp(vmg_ obj)->set_prop(vmg_ G_undo, obj, prop, new_val); } /* ------------------------------------------------------------------------ */ /* * Evaluate an operator overload. 'obj' is the operand of a unary op, or * the left operand of a binary op; 'prop' is the operator overload * property ID; 'argc' is the number of additional operands (0 for a unary * op, 1 for a binary op); 'err' is the VMERR_xxx error number for applying * the operator to a value that doesn't implement it. * * If the object doesn't provide the property, we'll throw the error, since * we're attempting to apply an operator to a value that doesn't implement * it. This is in contrast to ordinary property evaluations, which just * return nil for an unimplemented property. * * The 'prop' usually comes from G_predef, since it's an imported name from * the image file. If this is undefined (VM_INVALID_PROP), we certainly * can't have an overload for the operator, so we'll simply throw the * error. * * The 'obj' value doesn't have to be an object, since the program could * attempt to apply the operator to any type. If the value is of a type * that doesn't support property evaluations, we'll throw the same error. * * If 'asi_lcl' is 0 or greater, it's the local variable number to assign * the value to on return from the operator overload. If this is negative, * the return value is to be pushed onto the stack on return. */ const uchar *CVmRun::op_overload(VMG_ uint caller_ofs, int asi_lcl, const vm_val_t *obj, vm_prop_id_t prop, uint argc, int err) { vm_val_t val; vm_obj_id_t srcobj; vm_val_t new_self; /* * if the program defines any overloads for the operator, it must * define the export property; so if the export property isn't defined, * we can't have any overloading for the operator */ if (prop == VM_INVALID_PROP) err_throw(err); /* * if the value isn't an object, list, or string, there's no property, * so throw the specified error */ switch (obj->typ) { case VM_OBJ: case VM_SSTRING: case VM_LIST: /* these can have property values - look it up */ if (get_prop_no_eval(vmg_ &obj, prop, &argc, &srcobj, &val, &obj, &new_self)) { /* presume this is not actually a procedure call */ int is_call = FALSE; /* * Got it. Set up the appropriate special local subroutine for * processing the return value. * * - For a recursive call, the caller will provide the special * handling on return, so there's nothing special to do. * * - If there's no local to assign, we simply push the value * onto the stack on return, then do a local return to the * actual caller. Push our return offset as the local return * address. * * - If there's a local, we assign the value to the local on * return. Push the return offset as the local return address, * and push the local number after that. */ if (val.typ == VM_CODEOFS || val.typ == VM_DSTRING || val.typ == VM_OBJX) { /* * These will all result in subroutine calls. Set up the * return frame patch accordingly. */ is_call = TRUE; if (caller_ofs == 0) { /* * this is a recursive call - no return processing * patch is required */ } else if (asi_lcl < 0) { /* open one stack slot above the arguments */ insert(argc, 1); /* on return, push the value onto the stack */ get(argc)->set_int(caller_ofs); caller_ofs = VMRUN_RET_OP; } else { /* open two stack slots above the arguments */ insert(argc, 2); /* on return, store the value in locl #asi_lcl */ get(argc + 1)->set_int(caller_ofs); get(argc)->set_int(asi_lcl); caller_ofs = VMRUN_RET_OP_ASILCL; } } /* evaluate the property */ const uchar *ret = eval_prop_val( vmg_ TRUE, caller_ofs, &val, obj->val.obj, prop, obj, srcobj, argc, 0); /* if this is a direct evaluation, do the return patch now */ if (!is_call) { if (caller_ofs == 0) { /* recursive call - the caller will take care of it */ } else if (asi_lcl < 0) { /* normal operator eval - push the result onto the stack */ push(get_r0()); } else { /* local assignment on return */ *get_local(vmg_ asi_lcl) = *get_r0(); } } /* return the result */ return ret; } /* the property isn't defined - throw the caller's error */ err_throw(err); AFTER_ERR_THROW(return 0;) default: /* other types don't have properties - throw the caller's error */ err_throw(err); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Evaluate a property of an object */ const uchar *CVmRun::get_prop(VMG_ uint caller_ofs, const vm_val_t *target_obj, vm_prop_id_t target_prop, const vm_val_t *self, uint argc, const vm_rcdesc *rc) { vm_val_t val; vm_obj_id_t srcobj; int found; vm_val_t new_self; /* find the property without evaluating it */ found = get_prop_no_eval(vmg_ &target_obj, target_prop, &argc, &srcobj, &val, &self, &new_self); /* if we didn't find it, try propNotDefined */ if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* look up propNotDefined */ found = get_prop_no_eval(vmg_ &target_obj, G_predef->prop_not_defined_prop, &argc, &srcobj, &val, &self, &new_self); /* if we found a method, set up to call it */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* * add the target property as the additional first argument to * propNotDefined (we push backwards, so this will conveniently * become the new first argument) */ push_prop(vmg_ target_prop); /* count the additional argument */ ++argc; /* the property we're actually calling now is propNotDefined */ target_prop = G_predef->prop_not_defined_prop; } } /* evaluate whatever we found or didn't find */ return eval_prop_val(vmg_ found, caller_ofs, &val, self->val.obj, target_prop, target_obj, srcobj, argc, rc); } /* * Simplified property evaluator */ void CVmRun::get_prop(VMG_ vm_val_t *result, const vm_val_t *obj, vm_prop_id_t prop, uint argc, const vm_rcdesc *rc) { /* use nil as the default in case we don't find a value */ result->set_nil(); /* if the property is invalid, we definitely can't evaluate it */ if (prop == VM_INVALID_PROP) return; /* evaluate the property into R0 */ get_prop(vmg_ 0, obj, prop, obj, argc, rc); /* copy the result to the caller's parameter */ *result = r0_; } /* * Look up a property without evaluating it. */ inline int CVmRun::get_prop_no_eval(VMG_ const vm_val_t **target_obj, vm_prop_id_t target_prop, uint *argc, vm_obj_id_t *srcobj, vm_val_t *val, const vm_val_t **self, vm_val_t *new_self) { int found; const char *target_ptr; /* * we can evaluate properties of regular objects, as well as string * and list constants - see what we have */ switch((*target_obj)->typ) { case VM_LIST: /* 'self' must be the same as the target for a constant list */ if ((*self)->typ != (*target_obj)->typ || (*self)->val.ofs != (*target_obj)->val.ofs) err_throw(VMERR_OBJ_VAL_REQD); /* translate the list offset to a physical pointer */ target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs); /* evaluate the constant list property */ found = CVmObjList::const_get_prop(vmg_ val, *target_obj, target_ptr, target_prop, srcobj, argc); /* * If the result is a method to run, we need an actual object for * 'self'. In this case, create a dynamic list object with the * same contents as the constant list value. */ if (found && (val->typ == VM_CODEOFS || val->typ == VM_OBJX || val->typ == VM_BIFPTRX)) { /* create the list */ new_self->set_obj(CVmObjListConst::create(vmg_ target_ptr)); /* use it as the new 'self' and the new effective target */ *self = new_self; *target_obj = new_self; } /* go evaluate the result as normal */ break; case VM_SSTRING: /* 'self' must be the same as the target for a constant string */ if ((*self)->typ != (*target_obj)->typ || (*self)->val.ofs != (*target_obj)->val.ofs) err_throw(VMERR_OBJ_VAL_REQD); /* translate the string offset to a physical pointer */ target_ptr = G_const_pool->get_ptr((*target_obj)->val.ofs); /* evaluate the constant string property */ found = CVmObjString::const_get_prop(vmg_ val, *target_obj, target_ptr, target_prop, srcobj, argc); /* * If the result is a method to run, we need an actual object for * 'self'. In this case, create a dynamic string object with the * same contents as the constant string value. */ if (found && (val->typ == VM_CODEOFS || val->typ == VM_OBJX || val->typ == VM_BIFPTRX)) { /* create the string */ new_self->set_obj(CVmObjStringConst::create(vmg_ target_ptr)); /* it's the new 'self' and the new effective target object */ *self = new_self; *target_obj = new_self; } /* go evaluate the result as normal */ break; case VM_OBJ: /* get the property value from the target object */ found = vm_objp(vmg_ (*target_obj)->val.obj) ->get_prop(vmg_ target_prop, val, (*target_obj)->val.obj, srcobj, argc); /* 'self' must be an object as well */ if ((*self)->typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); break; case VM_NIL: /* nil pointer dereferenced */ err_throw(VMERR_NIL_DEREF); default: /* we can't evaluate properties of anything else */ err_throw(VMERR_OBJ_VAL_REQD); } /* return the 'found' indication */ return found; } /* ------------------------------------------------------------------------ */ /* * Given a value that has been retrieved from an object property, * evaluate the value. If the value contains code, we'll execute the * code; if it contains a self-printing string, we'll display the * string; otherwise, we'll just store the value in R0. * * 'found' indicates whether or not the property value is defined. * False indicates that the property value is not defined by the object; * true indicates that it is. */ const uchar *CVmRun::eval_prop_val(VMG_ int found, uint caller_ofs, const vm_val_t *val, vm_obj_id_t self, vm_prop_id_t target_prop, const vm_val_t *orig_target_obj, vm_obj_id_t defining_obj, uint argc, const vm_rcdesc *rc) { /* check whether or not the property is defined */ if (found) { /* take appropriate action based on the datatype of the result */ switch(val->typ) { case VM_CODEOFS: /* * It's a method - invoke the method. This will set us up * to start executing this new code, so there's nothing more * we need to do here. */ /* push targetprop, targetobj, definingobj, self, and invokee */ { vm_val_t *fp = push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(orig_target_obj->val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self); (fp++)->set_fnptr(val->val.ofs); } /* call the function */ return do_call(vmg_ caller_ofs, (const uchar *)G_code_pool->get_ptr(val->val.ofs), argc, rc); case VM_DSTRING: /* no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* * it's a self-printing string - invoke the default string * output function (this is effectively a do_call()) */ return disp_dstring(vmg_ val->val.ofs, caller_ofs, self); case VM_OBJX: /* * Execute-on-eval object. If the value is an anonymous * function or other invokable object, call it. If it's a * string, print it. */ if (vm_objp(vmg_ val->val.obj)->get_invoker(vmg_ 0)) { /* push targetprop, targetobj, definingobj, self, invokee */ vm_val_t *fp = push(5); (fp++)->set_propid(target_prop); (fp++)->set_obj(orig_target_obj->val.obj); (fp++)->set_obj(defining_obj); (fp++)->set_obj(self); (fp++)->set_obj(val->val.obj); /* convert to an ordinary anonymous function object */ vm_val_t funcptr; funcptr.set_obj(val->val.obj); /* invoke it */ return call_func_ptr_fr(vmg_ &funcptr, argc, 0, caller_ofs); } else if (CVmObjString::is_string_obj(vmg_ val->val.obj)) { /* print the string */ push_obj(vmg_ val->val.obj); return disp_string_val(vmg_ caller_ofs, self); } err_throw(VMERR_BAD_TYPE_CALL); case VM_BIFPTRX: /* Execute-on-eval built-in function. Call the function. */ call_bif(vmg_ val->val.bifptr.set_idx, val->val.bifptr.func_idx, argc); /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; default: /* for any other value, no arguments are allowed */ if (argc != 0) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* store the result in R0 */ r0_ = *val; /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; } } else { /* * the property or method is not defined - discard arguments and * set R0 to nil */ discard(argc); r0_.set_nil(); /* resume execution where we left off */ return entry_ptr_native_ + caller_ofs; } } /* ------------------------------------------------------------------------ */ /* * Inherit a property or method from the appropriate superclass of the * object that defines currently executing code. */ const uchar *CVmRun::inh_prop(VMG_ uint caller_ofs, vm_prop_id_t prop, uint argc) { vm_val_t orig_target_obj; vm_obj_id_t defining_obj; vm_val_t val; vm_obj_id_t srcobj; int found; vm_obj_id_t self; /* get the defining object from the stack frame */ defining_obj = get_defining_obj(vmg0_); /* get the original target object from the stack frame */ orig_target_obj.set_obj(get_orig_target_obj(vmg0_)); /* get the 'self' object */ self = get_self(vmg0_); /* get the inherited property value */ found = vm_objp(vmg_ self)->inh_prop(vmg_ prop, &val, self, orig_target_obj.val.obj, defining_obj, &srcobj, &argc); /* if we didn't find it, try inheriting propNotDefined */ if (!found && G_predef->prop_not_defined_prop != VM_INVALID_PROP) { /* * Look up propNotDefined using the same search conditions we used * to find the original inherited property. This lets us look up * the "inherited" propNotDefined. */ found = vm_objp(vmg_ self)->inh_prop(vmg_ G_predef->prop_not_defined_prop, &val, self, orig_target_obj.val.obj, defining_obj, &srcobj, &argc); /* * if we found it, and it's code, push the original property ID we * were attempting to inherit - this becomes the new first * parameter to the propNotDefined method */ if (found && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* add the original property pointer argument */ push_prop(vmg_ prop); /* count the additional argument */ ++argc; /* the target property changes to propNotDefined */ prop = G_predef->prop_not_defined_prop; } } /* * evaluate and store the result - note that "self" remains the * current "self" object, since we're inheriting within the context * of the original method call */ return eval_prop_val(vmg_ found, caller_ofs, &val, self, prop, &orig_target_obj, srcobj, argc, 0); } /* ------------------------------------------------------------------------ */ /* * Display a dstring via the default string display mechanism */ const uchar *CVmRun::disp_dstring(VMG_ pool_ofs_t ofs, uint caller_ofs, vm_obj_id_t self) { /* push the string */ push()->set_sstring(ofs); /* invoke the default "say" function */ return disp_string_val(vmg_ caller_ofs, self); } /* * Display the value at top of stack via the default string display * mechanism */ const uchar *CVmRun::disp_string_val(VMG_ uint caller_ofs, vm_obj_id_t self) { /* * if there's a valid 'self' object, and there's a default display * method defined, and 'self' defines or inherits that method, * invoke the method */ if (say_method_ != VM_INVALID_PROP && self != VM_INVALID_OBJ) { vm_obj_id_t src_obj; vm_val_t val; /* * look up the property - if we find it, and it's a regular * method, invoke it */ if (vm_objp(vmg_ self)->get_prop(vmg_ say_method_, &val, self, &src_obj, 0) && (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX)) { /* set up a 'self' value - this is the target object */ vm_val_t self_val; self_val.set_obj(self); /* there's a default display method - invoke it */ return eval_prop_val(vmg_ TRUE, caller_ofs, &val, self, say_method_, &self_val, src_obj, 1, 0); } } /* if the "say" function isn't initialized, it's an error */ if (say_func_ == 0 || say_func_->val.typ == VM_NIL) err_throw(VMERR_SAY_IS_NOT_DEFINED); /* call the "say" function with the argument at top of stack */ return call_func_ptr(vmg_ &say_func_->val, 1, 0, caller_ofs); } /* * Set the "say" function. */ void CVmRun::set_say_func(VMG_ const vm_val_t *val) { /* * if we haven't yet allocated a global to hold the 'say' function, * allocate one now */ if (say_func_ == 0) say_func_ = G_obj_table->create_global_var(); /* remember the new function */ say_func_->val = *val; } /* * Get the current "say" function */ void CVmRun::get_say_func(vm_val_t *val) const { /* * if we ever allocated a global to hold the 'say' function, return its * value; otherwise, there's no 'say' function, so the result is nil */ if (say_func_ != 0) *val = say_func_->val; else val->set_nil(); } /* ------------------------------------------------------------------------ */ /* * Push a string value */ void CVmRun::push_string(VMG_ const char *str, size_t len) { if (str != 0) push()->set_obj(CVmObjString::create(vmg_ FALSE, str, len)); else push()->set_nil(); } /* * Push a printf-formatted string */ void CVmRun::push_stringf(VMG_ const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); push_stringvf(vmg_ fmt, args); va_end(args); } /* * Push a vprintf-formatted string */ void CVmRun::push_stringvf(VMG_ const char *fmt, va_list args) { /* allocate and format the string */ char *str = t3vsprintf_alloc(fmt, args); /* push it */ push_string(vmg_ str); /* free the allocated string buffer */ t3free(str); } /* ------------------------------------------------------------------------ */ /* * Check a property for speculative evaluation */ void CVmRun::check_prop_spec_eval(VMG_ vm_obj_id_t obj, vm_prop_id_t prop) { vm_val_t val; vm_obj_id_t srcobj; /* get the property value */ if (vm_objp(vmg_ obj)->get_prop(vmg_ prop, &val, obj, &srcobj, 0)) { /* check the type of the value */ switch(val.typ) { case VM_CODEOFS: case VM_OBJX: case VM_BIFPTRX: case VM_DSTRING: case VM_NATIVE_CODE: /* * evaulating these types could result in side effects, so * this property cannot be evaulated during a speculative * evaluation */ err_throw(VMERR_BAD_SPEC_EVAL); break; default: /* evaluating other types causes no side effects, so proceed */ break; } } } /* ------------------------------------------------------------------------ */ /* * Set up a function header pointer for the current function */ void CVmRun::set_current_func_ptr(VMG_ CVmFuncPtr *func_ptr) { /* set up the pointer based on the current Entry Pointer register */ func_ptr->set(entry_ptr_native_); } /* * Set up a function header pointer for the return address of the given * stack frame */ void CVmRun::set_return_funcptr_from_frame(VMG_ CVmFuncPtr *func_ptr, vm_val_t *frame_ptr) { /* set up the function pointer for the entry pointer */ func_ptr->set(get_enclosing_entry_ptr_from_frame(vmg_ frame_ptr)); } /* ------------------------------------------------------------------------ */ /* * Get the frame pointer at a given stack level */ vm_val_t *CVmRun::get_fp_at_level(VMG_ int level) VM_REG_CONST { vm_val_t *fp; const vm_rcdesc *rc; /* walk up the stack to the desired level */ for (fp = frame_ptr_, rc = 0 ; fp != 0 && level != 0 ; --level) { /* if this is a recursive level, count it */ if (rc != 0 && rc->has_return_addr()) { /* move to the bytecode caller at this level */ rc = 0; /* count the level */ if (--level == 0) break; } /* move to the enclosing level */ rc = get_rcdesc_from_frame(vmg_ fp); fp = get_enclosing_frame_ptr(vmg_ fp); } /* * if we ran out of frames before we reached the desired level, or we * stopped at a native caller, the requested frame doesn't exist */ if (fp == 0 || (rc != 0 && rc->has_return_addr())) err_throw(VMERR_BAD_FRAME); /* return the frame */ return fp; } /* ------------------------------------------------------------------------ */ /* * Get the message from an exception object */ void CVmRun::get_exc_message(VMG_ const CVmException *exc, char *buf, size_t buflen, int add_unh_prefix) { CVmException tmpexc; const char *tmpmsg; const char *msg; /* set up our temporary exception object with no parameters by default */ tmpexc.param_count_ = 0; /* check for unhandled program exceptions */ if (exc->get_error_code() == VMERR_UNHANDLED_EXC) { size_t msg_len; /* * This is not a VM error, but is simply an exception that the * program itself threw but did not handle. We might be able to * find an informational message in the exception object itself. */ /* get the exception's message, if available */ msg = get_exc_message(vmg_ exc, &msg_len); if (msg != 0) { /* * we got a message from the exception object - use it */ /* set up our parameters for the formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_str(0, msg, msg_len); /* * If they want an "unhandled exception" prefix, get the * message for the prefix; otherwise, just use the message * from the exception without further adornment. */ if (add_unh_prefix) { /* they want a prefix - get the prefix message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_UNHANDLED_EXC_PARAM, FALSE); } else { /* no prefix desired - just use the message as we got it */ tmpmsg = "%s"; } /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } else { /* no message - use a generic exception message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_UNHANDLED_EXC, FALSE); err_format_msg(buf, buflen, tmpmsg, &tmpexc); } } else { /* * It's a VM exception, so we can determine the error's meaning * from the error code. Look up the message for the error code * in our error message list. */ msg = err_get_msg(vm_messages, vm_message_count, exc->get_error_code(), FALSE); /* if that failed, just show the error number */ if (msg == 0) { /* no message - just show the error code */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_VM_EXC_CODE, FALSE); /* set up our parameters for formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_int(0, exc->get_error_code()); /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } else { char tmpbuf[256]; /* format the message from the exception parameters */ err_format_msg(tmpbuf, sizeof(tmpbuf), msg, exc); /* get the prefix message */ tmpmsg = err_get_msg(vm_messages, vm_message_count, VMERR_VM_EXC_PARAM, FALSE); /* set up our parameters for the formatting */ tmpexc.param_count_ = 1; tmpexc.set_param_str(0, tmpbuf); /* format the message */ err_format_msg(buf, buflen, tmpmsg, &tmpexc); } } } /* * Get the message from an "unhandled exception" error object */ const char *CVmRun::get_exc_message(VMG_ const CVmException *exc, size_t *msg_len) { vm_obj_id_t exc_obj; /* * if the error isn't "unhandled exception," there's not a stored * exception object; likewise, if there's no object parameter in the * exception, there's nothing to use to obtain the message */ if (exc->get_error_code() != VMERR_UNHANDLED_EXC || exc->get_param_count() < 1) return 0; /* get the exception object */ exc_obj = (vm_obj_id_t)exc->get_param_ulong(0); /* get the message from the object */ return get_exc_message(vmg_ exc_obj, msg_len); } /* * Get the message from an exception object */ const char *CVmRun::get_exc_message(VMG_ vm_obj_id_t exc_obj, size_t *msg_len) { vm_val_t val; vm_obj_id_t src_obj; const char *str; uint argc; /* if there's no object, there's no message */ if (exc_obj == VM_INVALID_OBJ) return 0; /* * get the exceptionMessage property value from the object; if * there's not a valid exceptionMessage property defined, or the * object doesn't have a value for the property, there's no message */ argc = 0; if (G_predef->rterrmsg_prop == VM_INVALID_PROP || (!vm_objp(vmg_ exc_obj)->get_prop(vmg_ G_predef->rterrmsg_prop, &val, exc_obj, &src_obj, &argc))) return 0; /* * We got the property. If it's a string or an object containing a * string, retrieve the string. */ switch(val.typ) { case VM_SSTRING: /* get the constant string */ str = G_const_pool->get_ptr(val.val.ofs); break; case VM_OBJ: /* get the string value of the object, if possible */ str = vm_objp(vmg_ val.val.obj)->get_as_string(vmg0_); break; default: /* it's not a string - we can't use it */ str = 0; break; } /* check to see if we got a string */ if (str != 0) { /* * The string is in the standard VM internal format, which means * it has a 2-byte length prefix followed by the bytes of the * string (with no null termination). Read the length prefix, * then skip past it so the caller doesn't have to. */ *msg_len = osrp2(str); str += VMB_LEN; } /* return the string pointer */ return str; } /* ------------------------------------------------------------------------ */ /* * Get the boundaries of the current statement, based on debugging * information. Returns true if valid debugging information was found for * the given code location, false if not. */ int CVmRun::get_stm_bounds(VMG_ const CVmFuncPtr *func_ptr, ulong method_ofs, CVmDbgLinePtr *caller_line_ptr, const uchar **stm_start, const uchar **stm_end) { CVmDbgTablePtr dbg_ptr; int lo; int hi; int cur; /* presume we won't find anything */ *stm_start = *stm_end = 0; /* * if the current method has no line records, we can't find the * boundaries */ if (!func_ptr->set_dbg_ptr(&dbg_ptr) || dbg_ptr.get_line_count(vmg0_) == 0) { /* indicate that we didn't find debug information */ return FALSE; } /* * We must perform a binary search of the line records for the line * that contains this program counter offset. */ lo = 0; hi = dbg_ptr.get_line_count(vmg0_) - 1; while (lo <= hi) { ulong start_ofs; ulong end_ofs; CVmDbgLinePtr line_ptr; /* split the difference and get the current entry */ cur = lo + (hi - lo)/2; dbg_ptr.set_line_ptr(vmg_ &line_ptr, cur); /* get the current statement's start relative to the method header */ start_ofs = line_ptr.get_start_ofs(); /* * Get the next statement's start offset, which gives us the end * of this statement. If this is the last statement in the table, * it runs to the end of the function; use the debug records table * offset as the upper bound in this case. */ if (cur == (int)dbg_ptr.get_line_count(vmg0_) - 1) { /* * it's the last record - use the debug table offset as an * upper bound, since we know the function can't have any * executable code past this point */ end_ofs = func_ptr->get_debug_ofs(); } else { CVmDbgLinePtr next_line_ptr; /* another record follows this one - use it */ next_line_ptr.copy_from(&line_ptr); next_line_ptr.inc(vmg0_); end_ofs = next_line_ptr.get_start_ofs(); } /* see where we are relative to this line record */ if (method_ofs >= end_ofs) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else if (method_ofs < start_ofs) { /* we need to go lower */ hi = (cur == hi ? hi - 1 : cur); } else { /* found it - set the bounds to this record's limits */ *stm_start = func_ptr->get() + start_ofs; *stm_end = func_ptr->get() + end_ofs; /* fill in the caller's line pointer if desired */ if (caller_line_ptr != 0) caller_line_ptr->copy_from(&line_ptr); /* indicate that we found the line boundaries successfully */ return TRUE; } } /* return failure */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Profiler functions */ #ifdef VM_PROFILER /* * Profiler master hash table entry */ class CVmHashEntryProfiler: public CVmHashEntryCI { public: CVmHashEntryProfiler(const char *str, size_t len, const vm_profiler_rec *rec) : CVmHashEntryCI(str, len, TRUE) { /* copy the profiler record's identifying portion */ rec_.func = rec->func; rec_.obj = rec->obj; rec_.prop = rec->prop; /* initialize the timers and counters to zero */ rec_.sum_direct.hi = rec_.sum_direct.lo = 0; rec_.sum_chi.hi = rec_.sum_chi.lo = 0; rec_.call_cnt = 0; } /* our profiler record */ vm_profiler_rec rec_; }; /* * Begin profiling */ void CVmRun::start_profiling() { /* clear any old profiler data from the master hash table */ prof_master_table_->delete_all_entries(); /* reset the profiler stack */ prof_stack_idx_ = 0; /* turn on profiling */ profiling_ = TRUE; } /* * End profiling */ void CVmRun::end_profiling() { /* turn off profiling */ profiling_ = FALSE; /* leave all active profiler stack levels */ while (prof_stack_idx_ != 0) prof_leave(); } /* context for our profiling callback */ struct vmrun_prof_enum { /* globals object */ vm_globals *globals; /* interpreter object */ CVmRun *terp; /* debugger object */ CVmDebug *dbg; /* client callback and its context */ void (*cb)(void *, const char *, unsigned long, unsigned long, unsigned long); void *cb_ctx; }; /* * Get the profiling data */ void CVmRun::get_profiling_data(VMG_ void (*cb)(void *, const char *, unsigned long, unsigned long, unsigned long), void *cb_ctx) { vmrun_prof_enum our_ctx; /* if there's no debugger, we can't get symbols, so we can't proceed */ if (G_debugger == 0) return; /* set up our callback context */ our_ctx.globals = VMGLOB_ADDR; our_ctx.terp = this; our_ctx.dbg = G_debugger; our_ctx.cb = cb; our_ctx.cb_ctx = cb_ctx; /* enumerate the master table entries through our callback */ prof_master_table_->enum_entries(&prof_enum_cb, &our_ctx); } /* * Callback for enumerating the profiling data */ void CVmRun::prof_enum_cb(void *ctx0, CVmHashEntry *entry0) { vmrun_prof_enum *ctx = (vmrun_prof_enum *)ctx0; VMGLOB_PTR(ctx->globals); CVmHashEntryProfiler *entry = (CVmHashEntryProfiler *)entry0; char namebuf[128]; const char *p; /* generate the name of the function or method */ if (entry->rec_.obj != VM_INVALID_OBJ) { char *dst; /* look up the object name */ p = ctx->dbg->objid_to_sym(entry->rec_.obj); /* get the original name, if this is a synthetic 'modify' object */ p = ctx->dbg->get_modifying_sym(p); /* * if we got an object name, use it; otherwise, synthesize a name * using the object number */ if (p != 0) strcpy(namebuf, p); else sprintf(namebuf, "obj#%lx", (long)entry->rec_.obj); /* add a period */ dst = namebuf + strlen(namebuf); *dst++ = '.'; /* look up the property name */ p = ctx->dbg->propid_to_sym(entry->rec_.prop); if (p != 0) strcpy(dst, p); else sprintf(dst, "prop#%x", (int)entry->rec_.prop); } else if (entry->rec_.func != 0) { /* look up the function at the code offset */ char buf[256]; p = ctx->dbg->funchdr_to_sym(vmg_ entry->rec_.func, buf); if (p != 0) strcpy(namebuf, p); else sprintf(namebuf, "func#%lx", (long)entry->rec_.func); } else { /* it must be system code */ strcpy(namebuf, ""); } /* invoke the callback with the data */ (*ctx->cb)(ctx->cb_ctx, namebuf, os_prof_time_to_ms(&entry->rec_.sum_direct), os_prof_time_to_ms(&entry->rec_.sum_chi), entry->rec_.call_cnt); } /* * Profile entry into a new function or method */ void CVmRun::prof_enter(VMG_ const uchar *fptr) { vm_prof_time cur; /* pull the target property and defining object from the frame */ vm_obj_id_t obj = get_defining_obj(vmg0_); vm_prop_id_t prop = get_target_prop(vmg0_); /* get the current time */ os_prof_curtime(&cur); /* if we have a valid previous entry, suspend it */ if (prof_stack_idx_ > 0 && prof_stack_idx_ - 1 < prof_stack_max_) { vm_profiler_rec *p; vm_prof_time delta; /* get a pointer to the outgoing entry */ p = &prof_stack_[prof_stack_idx_ - 1]; /* * add the time since the last start to the cumulative time spent * in this function */ prof_calc_elapsed(&delta, &cur, &prof_start_); prof_add_elapsed(&p->sum_direct, &delta); } /* if we have room on the profiler stack, add a new level */ if (prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; /* get a pointer to the new entry */ p = &prof_stack_[prof_stack_idx_]; /* remember the identifying data for the method or function */ p->func = fptr; p->obj = obj; p->prop = prop; /* we have no cumulative time yet */ p->sum_direct.hi = p->sum_direct.lo = 0; p->sum_chi.hi = p->sum_chi.lo = 0; } /* count the level */ ++prof_stack_idx_; /* remember the start time in the new current function */ os_prof_curtime(&prof_start_); } /* * Profile returning from a function or method */ void CVmRun::prof_leave() { vm_prof_time delta; vm_prof_time cur; vm_prof_time chi; /* get the current time */ os_prof_curtime(&cur); /* move to the last level */ --prof_stack_idx_; /* presume we won't know the child time */ chi.hi = chi.lo = 0; /* if we're on a valid level, finish the call */ if (prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; CVmHashEntryProfiler *entry; /* get a pointer to the outgoing entry */ p = &prof_stack_[prof_stack_idx_]; /* * add the time since the last start to the cumulative time spent * in this function */ prof_calc_elapsed(&delta, &cur, &prof_start_); prof_add_elapsed(&p->sum_direct, &delta); /* * Find or create the master record for the terminating function or * method, and add the cumulative times from this call to the * master record's cumulative times. Also count the invocation in * the master record. */ entry = prof_find_master_rec(p); prof_add_elapsed(&entry->rec_.sum_direct, &p->sum_direct); prof_add_elapsed(&entry->rec_.sum_chi, &p->sum_chi); ++(entry->rec_.call_cnt); /* * Calculate the cumulative time in the outgoing function - this is * the total time directly in the function plus the cumulative time * in all of its children. We must add this to the caller's * cumulative child time, since this function and all of its * children are children of the caller and thus must count in the * caller's total child time. */ chi = p->sum_direct; prof_add_elapsed(&chi, &p->sum_chi); } /* if we're leaving to a valid level, re-activate it */ if (prof_stack_idx_ > 0 && prof_stack_idx_ < prof_stack_max_) { vm_profiler_rec *p; /* get a pointer to the resuming entry */ p = &prof_stack_[prof_stack_idx_ - 1]; /* * add the time spent in the child and its children to our * cumulative child time */ prof_add_elapsed(&p->sum_chi, &chi); } /* * remember the new start time for the function we're resuming - we * must reset this to the current time, since we measure deltas from * the last call or return on each call or return */ os_prof_curtime(&prof_start_); } /* * Calculate an elapsed 64-bit time value */ void CVmRun::prof_calc_elapsed(vm_prof_time *diff, const vm_prof_time *a, const vm_prof_time *b) { /* calculate the differences of the low and high parts */ diff->lo = a->lo - b->lo; diff->hi = a->hi - b->hi; /* * if the low part ended up higher than it started, then we * underflowed, and hence must borrow from the high part */ if (diff->lo > a->lo) --(diff->hi); } /* * Add one elapsed time value to another */ void CVmRun::prof_add_elapsed(vm_prof_time *sum, const vm_prof_time *val) { unsigned long orig_lo; /* remember the original low part */ orig_lo = sum->lo; /* add the low parts and high parts */ sum->lo += val->lo; sum->hi += val->hi; /* * if the low part of the sum is less than where it started, then it * overflowed, and we must hence carry to the high part */ if (sum->lo < orig_lo) ++(sum->hi); } /* * Find or create a hash table entry for a profiler record */ CVmHashEntryProfiler *CVmRun::prof_find_master_rec(const vm_profiler_rec *p) { const size_t id_siz = sizeof(p->func) + sizeof(p->obj) + sizeof(p->prop); char id[id_siz]; CVmHashEntryProfiler *entry; /* * Build the ID string, which we'll use as our hash key. We never have * to serialize this, so it doesn't matter that it's dependent on byte * order and word size. */ memcpy(id, &p->func, sizeof(p->func)); memcpy(id + sizeof(p->func), &p->obj, sizeof(p->obj)); memcpy(id + sizeof(p->func) + sizeof(p->obj), &p->prop, sizeof(p->prop)); /* try to find an existing entry */ entry = (CVmHashEntryProfiler *)prof_master_table_->find(id, id_siz); /* if we didn't find an entry, create one */ if (entry == 0) { /* create a new entry */ entry = new CVmHashEntryProfiler(id, id_siz, p); /* add it to the table */ prof_master_table_->add(entry); } /* return the entry */ return entry; } #endif /* VM_PROFILER */ /* ------------------------------------------------------------------------ */ /* * Footnote - for the referring code, search the code above for * [REGISTER_P_FOOTNOTE]. * * This footnote pertains to a 'register' declaration that causes gcc (and * probably some other compilers) to generate a warning message. The * 'register' declaration is useful on some compilers and will be retained. * Here's a note I sent to Nikos Chantziaras (who asked about the warning) * explaining why I'm choosing to leave the 'register' declaration in, and * why I think this 'register' declaration is actually correct and useful * despite the warning it generates on some compilers. * * The basic issue is that the code takes the address of the variable in * question in expressions passed as parameters to certain function calls. * These function calls all happen to be in-linable functions, and it * happens that in each function, the address operator is always canceled * out by a '*' dereference operator - in other words, we have '*&p', which * the compiler can turn into just plain 'p' when the calls are in-lined, * eliminating the need to actually take the address of 'p'. * * Nikos: *. >I'm no expert, but I think GCC barks at this because it isn't possible *. >at all to store the variable in a register if the code wants its *. >address, therefore the 'register' in the declaration does nothing. * * That's correct, but a compiler is always free to ignore 'register' * declarations *anyway*, even if enregistration is possible. Therefore a * warning that it's not possible to obey 'register' is unnecessary, * because it's explicit in the language definition that 'register' is not * binding. It simply is not possible for an ignored 'register' attribute * to cause unexpected behavior. Warnings really should only be generated * for situations where it is likely that the programmer expects different * behavior than the compiler will deliver; in the case of an ignored * 'register' attribute, the programmer is *required* to expect that the * attribute might be ignored, so a warning to this effect is superfluous. * * Now, I understand why they generate the warning - it's because the * compiler believes that the program code itself makes enregistration * impossible, not because the compiler has chosen for optimization * purposes to ignore the 'register' request. However, as we'll see * shortly, the program code doesn't truly make enregistration impossible; * it is merely impossible in some interpretations of the code. Therefore * we really are back to the compiler choosing to ignore the 'register' * request due to its own optimization decisions; the 'register' request is * made impossible far downstream of the actual decisions that the compiler * makes (which have to do with in-line vs out-of-line calls), but it * really is compiler decisions that make it impossible, not the inherent * structure of the code. * *. >Furthermore, I'm not sure I understand the relationship *. >between 'register' and inlining; why should "*(&p)" do something *. >else "in calls to inlines" than its obvious meaning? * * When a function is in-lined, the compiler is not required to generate * the same code it would generate for the most general case of the same * function call, as long as the meaning is the same. * * For example, suppose we have some code that contains a call to a * function like so: * * a = myFunc(a + 7, 3); * * In the general out-of-line case, the compiler must generate some * machine-code instructions like this: * *. push #3 *. mov [a], d0 *. add #7, d0 *. push d0 *. call #myFunc *. mov d0, [a] * * The compiler doesn't have access to the inner workings of myFunc, so it * must generate the appropriate code for the generic interface to an * external function. * * Now, suppose the function is defined like so: * * int myFunc(int a, int b) { return a - 6; } * * and further suppose that the compiler decides to in-line this function. * In-lining means the compiler will generate the code that implements the * function directly in the caller; there will be no call to an external * linkage point. This means the compiler can implement the linkage to the * function with a custom one-off interface for this particular invocation * - every in-line invocation can be customized to the exact context where * it appears. So, for example, if we call myFunc right now and registers * d1 and d2 happens to be available, we can put the parameters in d1 and * d2, and the generated function will refer to those registers for the * parameters rather than having to look in the stack. Later on, if we * generate a separate call to the same function, but registers d3 and d7 * are the ones available, we can use those instead. Each generated copy * of the function can fit its exact context. * * Furthermore, looking at this function and at the arguments passed, we * can see that the formal parameter 'b' has no effect on the function's * results, and the actual parameter '3' passed for 'b' has no side * effects. Therefore, the compiler is free to completely ignore this * parameter - there's no need to generate any code for it at all, since we * have sufficient knowledge to see that it has no effect on the meaning of * the code. * * Further still, we can globally optimize the entire function. So, we can * see that myFunc(a+7, 3) is going to turn into the expression (a+7-6). * We can fold constants to arrive at (a+1) as the result of the function. * We can therefore generate the entire code for the function's invocation * like so: * * inc [a] * * Okay, now let's look at the &p case. In the specific examples in * vmrun.cpp, we have a bunch of function invocations like this: * * register const char *p; *. int x = myfunc(&p); * * In the most general case, we have to generate code like this: * *. lea [p], d0 ; load effective address *. push d0 *. call #myfunc *. mov d0, [x] * * So, in the most general case of a call with external linkage, we need * 'p' to have a main memory address so that we can push it on the stack as * the parameter to this call. Registers don't have main memory addresses, * so 'p' can't go in a register. * * However, we know what myfunc() looks like: * *. char myfunc(const char **p) *. { *. char c = **p; *. *p += 1; *. return c; *. } * * If the compiler chooses to in-line this function, it can globally * optimize its linkage and implementation as we saw earlier. So, the * compiler can rewrite the code like so: * * register const char *p; *. int x = **(&p); *. *(&p) += 1; * * which can be further rewritten to: * *. register const char *p; *. int x = *p; *. p += 1; * * Now we can generate the machine code for the final optimized form: * *. mov [p], a0 ; get the *value* of p into index register 0 *. mov.byte [a0+0], d0 ; get the value index register 0 points to *. mov.byte d0, [x] ; store it in x *. inc [p] ; inc the value of p * * Nowhere do we need a main memory address for p. This means the compiler * can keep p in a register, say d5: * *. mov d5, a0 *. mov.byte [a0+0], d0 *. mov.byte d0, [x] *. inc d5 * * And this is indeed exactly what the code that comes out of vc++ looks * like (changed from my abstract machine to 32-bit x86, of course). * * So: if the compiler chooses to in-line the functions that are called * with '&p' as a parameter, and the compiler performs the available * optimizations on those calls once they're in-lined, then a memory * address for 'p' is never needed. Thus there is a valid interpretation * of the code where 'register p' can be obeyed. If the compiler doesn't * choose to in-line the functions or make those optimizations, then the * compiler will be unable to satisfy the 'register p' request and will be * forced to put 'p' in addressable main memory. But it really is entirely * up to the compiler whether to obey the 'register p' request; the * program's structure does not make the request impossible to satisfy. * Therefore there is no reason for the compiler to warn about this, any * more than there would be if the compiler chose not to obey the 'register * p' simply because it thought it could make more optimal use of the * available registers. That gcc warns is understandable, in that a * superficial reading of the code would not reveal the optimization * opportunity; but the warning is nonetheless unnecessary, and the * 'register' does provide useful optimization hinting to at least vc++, so * I think it's best to leave it in and ignore the warning. */ frobtads-1.2.3/tads3/vmbifreg.h0000644000175000001440000000124707627507714015517 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmbifreg.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifreg.h - built-in function set registry definitions Function Notes Modified 12/05/98 MJRoberts - Creation */ #ifndef VMBIFREG_H #define VMBIFREG_H #include "vmbif.h" /* ------------------------------------------------------------------------ */ /* * Declare the global static table */ extern vm_bif_entry_t G_bif_reg_table[]; #endif /* VMBIFREG_H */ frobtads-1.2.3/tads3/vmtzobj.cpp0000644000175000001440000003534311727747676015761 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtzobj.cpp - CVmObjTimeZone object Function Notes Modified 02/06/12 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmtzobj.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmlst.h" #include "vmtz.h" #include "vmdate.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_tzobj_ext *vm_tzobj_ext::alloc_ext(VMG_ CVmObjTimeZone *self, CVmTimeZone *tz) { /* calculate how much space we need */ size_t siz = sizeof(vm_tzobj_ext); /* allocate the memory */ vm_tzobj_ext *ext = (vm_tzobj_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* save our TimeZone object */ ext->tz = tz; /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone object statics */ /* metaclass registration object */ static CVmMetaclassTimeZone metaclass_reg_obj; CVmMetaclass *CVmObjTimeZone::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjTimeZone::*CVmObjTimeZone::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjTimeZone::getp_undef, /* 0 */ &CVmObjTimeZone::getp_getNames, /* 1 */ &CVmObjTimeZone::getp_getHistory, /* 2 */ &CVmObjTimeZone::getp_getRules, /* 3 */ &CVmObjTimeZone::getp_getLocation /* 4 */ }; /* ------------------------------------------------------------------------ */ /* * construction */ CVmObjTimeZone::CVmObjTimeZone(VMG_ CVmTimeZone *tz) { /* allocate our extension structure */ ext_ = (char *)vm_tzobj_ext::alloc_ext(vmg_ this, tz); } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjTimeZone::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* parse the constructor arguments */ CVmTimeZone *tz = parse_ctor_args(vmg_ G_stk->get(0), argc); /* allocate the object ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create the object */ new (vmg_ id) CVmObjTimeZone(vmg_ tz); /* return the new ID */ return id; } /* * Parse stack arguments for a timezone constructor, returning a * CVmTimeZone object if the arguments specify a valid timezone. If not, * throws an error - we never return null. This doesn't create the garbage * collected TimeZone object; it just parses the arguments and creates the * underlying cache object. */ CVmTimeZone *CVmObjTimeZone::parse_ctor_args( VMG_ const vm_val_t *argp, int argc) { /* check arguments */ const char *str; CVmTimeZone *tz = 0; if (argc <= 0) { /* no arguments; create a local timezone object */ tz = G_tzcache->get_local_zone(vmg0_); } else if (argc == 1 && (str = argp->get_as_string(vmg0_)) != 0) { /* get the string buffer and length */ size_t len = vmb_get_len(str); str += VMB_LEN; /* parse the zone name */ tz = G_tzcache->parse_zone(vmg_ str, len); } else if (argc == 1 && argp->is_numeric(vmg0_)) { /* construct from a gmt offset */ tz = G_tzcache->get_gmtofs_zone(vmg_ argp->num_to_int(vmg0_)); } else if (argc > 1) { /* wrong number of arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* wrong type */ err_throw(VMERR_BAD_TYPE_BIF); } /* if we didn't find a zone, it's an error */ if (tz == 0) err_throw(VMERR_BAD_VAL_BIF); /* return the zone */ return tz; } /* ------------------------------------------------------------------------ */ /* * Create from a given CVmTimeZone object. */ vm_obj_id_t CVmObjTimeZone::create(VMG_ CVmTimeZone *tz) { /* allocate the object ID and create the object */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjTimeZone(vmg_ tz); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjTimeZone::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjTimeZone::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjTimeZone::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjTimeZone::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjTimeZone::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload data from the image */ void CVmObjTimeZone::load_image_data( VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* look up our timezone object */ CVmTimeZone *tz = G_tzcache->parse_zone(vmg_ ptr+9, osrp1(ptr+8)); /* if we didn't find it, create a dummy entry for it */ if (tz == 0) { /* * Read the default GMT offset and daylight savings offset. Note * that the saved values are in milliseconds, and os_tzinfo_t uses * seconds, so adjust accordingly. */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = osrp4s(ptr) / 1000; desc.dst_ofs = desc.std_ofs + osrp4s(ptr+4) / 1000; /* read the default abbreviation */ const char *abbr = ptr + 8 + osrp1(ptr+8) + 1; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), abbr+1, osrp1(abbr)); lib_strcpy(desc.dst_abbr, sizeof(desc.dst_abbr), abbr+1, osrp1(abbr)); /* create the dummy */ tz = G_tzcache->create_missing_zone(vmg_ ptr+9, osrp1(ptr+8), &desc); } /* allocate the extension */ vm_tzobj_ext *ext = vm_tzobj_ext::alloc_ext(vmg_ this, tz); ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjTimeZone::save_to_file(VMG_ class CVmFile *fp) { /* get our timezone name */ CVmTimeZone *tz = get_ext()->tz; size_t namelen; const char *name = tz->get_name(namelen); /* if this is the system's local zone, use ":local" as the name */ if (G_tzcache->is_local_zone(tz)) name = ":local", namelen = 6; /* query the zone data */ vmtzquery q; tz->query(&q); /* write our GMT offset, DST offset, abbreviation, and timezone name */ fp->write_int4(q.gmtofs); fp->write_int4(q.save); fp->write_str_byte_prefix(q.abbr); fp->write_str_byte_prefix(name, namelen); } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjTimeZone::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* * Read the GMT and DST offsets (in case we need a synthetic entry). * Note that the saved values are in milliseconds, but os_tzinfo_t * works in seconds, so adjust accordingly. */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = fp->read_int4() / 1000; desc.dst_ofs = desc.std_ofs + fp->read_int4() / 1000; /* read the abbreviation */ fp->read_str_byte_prefix(desc.std_abbr, sizeof(desc.std_abbr)); strcpy(desc.dst_abbr, desc.std_abbr); /* read the length and name */ char name[256]; size_t len = fp->read_str_byte_prefix(name, sizeof(name)); /* look up our timezone object */ CVmTimeZone *tz = G_tzcache->parse_zone(vmg_ name, len); /* if we didn't find it, create a dummy entry for it */ if (tz == 0) tz = G_tzcache->create_missing_zone(vmg_ name, len, &desc); /* allocate the extension structure */ vm_tzobj_ext *ext = vm_tzobj_ext::alloc_ext(vmg_ this, tz); ext_ = (char *)ext; } /* ------------------------------------------------------------------------ */ /* * cast to a string - returns the time zone name */ const char *CVmObjTimeZone::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *newstr) const { /* get the time zone name */ size_t len; const char *name = get_tz()->get_name(len); /* create the return string */ newstr->set_obj(CVmObjString::create(vmg_ FALSE, name, len)); return newstr->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * getNames method */ int CVmObjTimeZone::getp_getNames(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* create the name list */ retval->set_obj(get_tz()->get_name_list(vmg0_)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getHistory method */ int CVmObjTimeZone::getp_getHistory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check for a Date argument */ if (argc >= 1 && G_stk->get(0)->typ != VM_NIL) { /* there's a date - retrieve it */ CVmObjDate *date = vm_val_cast(CVmObjDate, G_stk->get(0)); if (date == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the history for the given date only */ retval->set_obj(get_tz()->get_history_item( vmg_ date->get_dayno(), date->get_daytime())); } else { /* no date argument - get the full history */ retval->set_obj(get_tz()->get_history_list(vmg0_)); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRules method */ int CVmObjTimeZone::getp_getRules(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the rule list */ retval->set_obj(get_tz()->get_rule_list(vmg0_)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getLocation method */ int CVmObjTimeZone::getp_getLocation(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get our underlying CVmTimeZone object */ CVmTimeZone *tz = get_ext()->tz; /* create the return list - [country, lat, long, desc] */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 4)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); G_stk->push(retval); lst->cons_clear(); /* set the country string */ vm_val_t ele; const char *str = tz->country(); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, strlen(str))); lst->cons_set_element(0, &ele); /* get the lat/long string */ str = tz->coords(); /* find the +/- that separates the lat/long portions */ const char *p = str; if (*p == '+' || *p == '-') ++p; for ( ; *p != '\0' && *p != '-' && *p != '+' ; ++p) ; /* set the two substrings */ ele.set_obj(CVmObjString::create(vmg_ FALSE, str, p - str)); lst->cons_set_element(1, &ele); ele.set_obj(CVmObjString::create(vmg_ FALSE, p, strlen(p))); lst->cons_set_element(2, &ele); /* set the description string */ str = tz->desc(); if (str != 0 && str[0] != '\0') ele.set_obj(CVmObjString::create(vmg_ FALSE, str, strlen(str))); else ele.set_nil(); lst->cons_set_element(3, &ele); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmmaincn.h0000644000175000001440000001163312145504453015512 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmaincn.h - VM main startup - console helper version Function Implements some helpers for the vmmain module and its users, for implementations using the standard system console (G_console and os_printz): - provides a CVmMainClientIfc implementation that writes errors to G_console (or os_printz, if no G_console is available), and ignores other notifications Notes Modified 04/05/02 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmmain.h" #include "vmconsol.h" /* ------------------------------------------------------------------------ */ /* * Client services interface for T3 VM - console-based version */ class CVmMainClientConsole: public CVmMainClientIfc { public: /* set plain ASCII mode */ void set_plain_mode() { /* set plain mode in the OS-level console */ os_plain(); } /* create the console */ CVmConsoleMain *create_console(struct vm_globals *vmg) { /* set up for global access */ VMGLOB_PTR(vmg); /* create a standard console and return it */ return new CVmConsoleMain(vmg0_); } /* delete the console */ void delete_console(struct vm_globals *vmg, CVmConsoleMain *con) { /* set up for global access */ VMGLOB_PTR(vmg); /* flush any pending buffered output */ con->flush(vmg_ VM_NL_NONE); /* delete the output formatter */ con->delete_obj(vmg0_); } /* initialize */ void client_init(struct vm_globals *vmg, const char *script_file, int script_quiet, const char *log_file, const char *cmd_log_file, const char *banner_str, int more_mode) { /* set up for global access */ VMGLOB_PTR(vmg); /* set the [More] mode as desired */ G_console->set_more_state(more_mode); /* if we have a script file, set up script input on the console */ if (script_file != 0) { G_console->open_script_file( vmg_ script_file, script_quiet, FALSE); } /* if we have a log file, set up logging on the console */ if (log_file != 0) G_console->open_log_file(vmg_ log_file); /* set up command logging on the console if desired */ if (cmd_log_file != 0) G_console->open_command_log(vmg_ cmd_log_file, TRUE); /* tell the HTML renderer that we're a T3 caller */ G_console->format_text(vmg_ ""); /* show the banner on the console, if desired */ if (banner_str != 0) { G_console->format_text(vmg_ banner_str); G_console->write_blank_line(vmg0_); } } /* terminate */ void client_terminate(struct vm_globals *) { } /* pre-execution initialization */ void pre_exec(struct vm_globals *) { } /* post-execution termination/error termination */ void post_exec(struct vm_globals *) { } void post_exec_err(struct vm_globals *) { } /* display an error */ void display_error(struct vm_globals *vmg, const struct CVmException *exc, const char *msg, int add_blank_line) { CVmConsole *con; size_t len; /* check for a newline at the end of the message */ int has_nl = ((len = strlen(msg)) != 0 && msg[len-1] == '\n'); /* set up for global access */ VMGLOB_PTR(vmg); /* if we have globals, get the console */ con = VMGLOB_IF_AVAIL(G_console); /* if we have a console, write to it */ if (con != 0) { int old_obey; /* flush any pending buffered output */ con->flush(vmg_ VM_NL_NONE); /* put the console in obey-whitespace mode for our message */ old_obey = con->set_obey_whitespace(TRUE); /* display the message on the console */ con->format_text(vmg_ msg); /* add a blank line if desired */ if (add_blank_line) con->write_blank_line(vmg0_); /* restore console mode */ con->set_obey_whitespace(old_obey); } else { /* display the error on the OS-level console */ os_printz(msg); /* add a blank line if desired */ if (add_blank_line) { /* add one newline */ os_printz("\n"); /* * if the message itself didn't end with a newline, add * another newline */ if (!has_nl) os_printz("\n"); } } } }; frobtads-1.2.3/tads3/vmsort.cpp0000644000175000001440000000460107627507540015575 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsort.cpp - T3 VM quicksort implementation Function Implements quicksort. We use our own implementation rather than the standard C library's qsort() routine for two reasons. First, we might want to throw an exception out of the comparison routine, and it is not clear that it is safe to longjmp() past qsort() on every type of machine and every C run-time implementation. Second, the standard C library's qsort() routine doesn't provide any means to pass a context to the comparison callback, and further insists that the data to be sorted be arranged as an array; we provide a higher-level abstraction for the comparison callback. Notes Modified 05/14/00 MJRoberts - Creation */ #include #include "t3std.h" #include "vmglob.h" #include "vmsort.h" /* ------------------------------------------------------------------------ */ /* * perform a quicksort */ void CVmQSortData::sort(VMG_ size_t l, size_t r) { /* proceed if we have a non-empty range */ if (r > l) { size_t i, j; size_t v_idx = r; /* start at the ends of the range */ i = l - 1; j = r; /* partition the range */ do { /* find the leftmost element >= the right element */ do { ++i; } while (i != r && compare(vmg_ i, v_idx) < 0); /* find the rightmost element <= the right element */ do { --j; } while (j != l && compare(vmg_ j, v_idx) > 0); /* exchange elements i and j */ exchange(vmg_ i, j); /* if we moved the 'v' element, follow that in the index */ if (v_idx == i) v_idx = j; else if (v_idx == j) v_idx = i; } while (j > i); /* undo the last exchange */ exchange(vmg_ i, j); /* exchange the right value into the pivot point */ exchange(vmg_ i, r); /* recursively sort the subranges */ if (i > l) sort(vmg_ l, i - 1); if (i < r) sort(vmg_ i + 1, r); } } frobtads-1.2.3/tads3/vmdynfunc.h0000644000175000001440000004320011463643776015724 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdynfunc.h - Code Object Function A Code Object is an intrinsic class object that contains a block of byte code. This can be used for things like debugger expressions and run-time evaluation and code creation. A Code Object is immutable, like a String or List. It contains the original source code text, as a UTF-8 string, and the corresponding compiled byte code. Notes Modified 12/13/09 MJRoberts - Creation */ #ifndef VMDYNFUNC_H #define VMDYNFUNC_H #include "t3std.h" #include "vmtype.h" #include "tcprstyp.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT2 code_length *. UINT2 obj_ref_cnt *. DATAHOLDER src_string *. BYTE[code_length] bytecode *. UINT2[ref_cnt] obj_refs * * code_length is the size of the bytecode stream, in bytes. * * obj_ref_cnt is the number of object references. These are stored * immediately after the bytecode stream. Each reference array entry is a * UINT2 giving the byte offset in the bytecode array of the reference, * which is a UINT4 stored in portable format giving the object ID. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure, which mimics the image file * structure but uses native types. */ /* prefix header size */ const size_t VMCO_PREFIX_LENGTH = VMB_OBJECT_ID; /* extension structure */ struct vm_dynfunc_ext { /* allocate the structure */ static vm_dynfunc_ext *alloc_ext(VMG_ class CVmDynamicFunc *self, size_t bytecode_len, int obj_ref_cnt); /* the source string object */ vm_val_t src; /* * Method context object. For a function that's compiled with a local * stack frame context, this is the 'self' object or the complete * method context object (suitable for the LOADCTX opcode), as * applicable, for the enclosing method. This allows the function to * establish the dynamic enclosing method context at entry. */ vm_val_t method_ctx; /* the size in bytes of the byte code data */ size_t bytecode_len; /* number of object references in the fixup list */ int obj_ref_cnt; /* * Object references. The structure is allocated with enough memory * for 'obj_ref_cnt' entries in this array. */ uint obj_refs[1]; /* * The structure is allocated with memory following the 'obj_refs' * array for the following dynamic elements: * *. char dynamic_code_header[VMB_OBJECT_ID] *. char bytecode[bytecode_len] * * Explanation of the dynamic object header: * * The VM internally refers to executing code by a pointer directly * into the code's physical memory. It always keeps an Entry Pointer * value giving a pointer to the first byte of the method; this makes * it easy to get metadata about any active code block by serving as an * identifier for the code. * * Regular code is stored at compile-time in the Code Pool, which is a * special block of read-only memory especially for code. The FUNCPTR * and CODEPTR primitive datatypes contain offsets into the Code Pool. * A function generated by the compiler is represented by a FUNCPTR * value. To get a FUNCPTR value given an Entry Pointer, we simply * translate the physical memory pointer back into a Code Pool offset, * and wrap the result in a FUNCPTR value. * * Dynamic code objects are not in the Code Pool, however, since that's * read-only memory that we can't add to at run-time. Dynamic code * objects are instead allocated in the garbage-collected heap like * other objects. So we need some way to translate from an Entry * Pointer that points into a dynamic object back into a vm_val_t. The * vm_val_t representation of a dynamic object is a reference to the * object - i.e., an OBJ value containing the code object's ID. * There's no generic way to look up an object ID given the extension * pointer, which is what the Entry Pointer for a dynamic code object * basically is. (The Entry Pointer doesn't actually point to the * extension - it points to the start of the 'buf' element of the * extension, because that's where our method header starts.) So, we * need our own way of working from the Entry Point back to our object * ID. To do this, we simply add our object ID just ahead of the * method header. To generate a vm_val_t from an Entry Pointer value, * then, we first look to see if the pointer is a Code Pool element; if * so, we use a FUNCPTR value; if not, it must be a dynamic code * object, so we retrieve the OBJECT_ID value at (Entry Pointer - * VMB_OBJECT_ID) as an object ID, and create an OBJ value with that * ID. */ /* get a pointer to the dynamic code object method header prefix */ char *get_prefix_ptr() { /* the prefix starts after the obj_ref array */ return (char *)&obj_refs[obj_ref_cnt]; } /* get a pointer to the start of the byte code */ char *get_bytecode_ptr() { /* the byte code starts after the dynamic code prefix */ return get_prefix_ptr() + VMCO_PREFIX_LENGTH; } }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc metaclass */ class CVmDynamicFunc: public CVmObject { friend class CVmMetaclassDynamicFunc; friend class CVmDynamicCompiler; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a DynamicFunc object? */ static int is_dynfunc_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Given a pointer to a method header, retrieve the object ID of the * DynamicFunc that owns the bytecode. Returns VM_INVALID_OBJ if we * can't find a valid owner. */ static vm_obj_id_t get_obj_from_prefix(VMG_ const uchar *p); /* create from a string, without saving the source text */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const char *src, size_t src_len) { vm_val_t v; v.set_nil(); return create(vmg_ in_root_set, globals, locals, macros, &v, src, src_len); } /* create from a string value */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src); /* create from a string, optionally saving the string value */ static vm_obj_id_t create(VMG_ int in_root_set, vm_obj_id_t symtab, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src_val, const char *src, size_t src_len); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* we cannot be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t) { /* we don't reference anything */ } /* index the object */ virtual int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no settable properties */ err_throw(VMERR_INVALID_SETPROP); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* invoke */ int get_invoker(VMG_ vm_val_t *val); /* undo operations - we're immutable, so we can ignore these */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references (we reference our source string) */ void mark_refs(VMG_ uint); /* we don't keep any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* we're immutable, so we're definitely not changed since loading */ int is_changed_since_load() const { return FALSE; } protected: /* get my extension data */ vm_dynfunc_ext *get_ext() const { return (vm_dynfunc_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* set the method context object */ void set_method_ctx(const vm_val_t *val) { get_ext()->method_ctx = *val; } /* create a with no initial contents */ CVmDynamicFunc() { ext_ = 0; } /* create with the given byte code array length */ CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src, size_t bytecode_len, int obj_ref_cnt); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get my source text */ int getp_get_source(VMG_ vm_obj_id_t, vm_val_t *, uint *); /* property evaluation function table */ static int (CVmDynamicFunc::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc registration table object */ class CVmMetaclassDynamicFunc: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "dynamic-func/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmDynamicFunc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmDynamicFunc(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmDynamicFunc::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmDynamicFunc::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler interface */ /* compilation modes */ enum CVmDynCompMode { /* compile an expression */ DCModeExpression, /* auto-sensing: 'function' syntax, 'method' syntax, or an expression */ DCModeAuto, /* compile a grammar production rule (alternative) list */ DCModeGramAlt }; /* debugger expression context */ struct CVmDynCompDebug { CVmDynCompDebug(class CTcPrsDbgSymtab *symtab, tcpn_dyncomp_info &di, int self_valid) { this->symtab = symtab; this->di = di; this->self_valid = self_valid; } /* IN: local symbol table for debugger expression evaluation */ class CTcPrsDbgSymtab *symtab; /* IN: debugger evaluation context settings */ tcpn_dyncomp_info di; /* IN: is there a valid 'self' for this expression? */ int self_valid; /* OUT: the parsed expression is an lvalue */ int is_lval; }; /* results structure */ struct CVmDynCompResults { CVmDynCompResults() { /* clear the error code */ err = 0; /* no error message */ msgbuf = 0; } virtual ~CVmDynCompResults() { free_msgbuf(); } /* Explicitly free the message buffer */ void free_msgbuf() { if (msgbuf != 0) { t3free(msgbuf); msgbuf = 0; } } /* throw a dynamic compilation error based on the results */ void throw_error(VMG0_); /* * In grammar mode (DCModeGramAlt), the compiler passes back the parsed * alt list to the caller by calling this method. Callers that don't * parse grammar rules can leave this as a no-op. 'alts' is the head * of the alternative list for the parsed grammar rule. */ virtual void save_grammar(VMG_ class CTcGramProdAlt * /*alts*/, struct CTcGramPropArrows * /*arrows*/) { } /* compiler error code */ int err; /* * Message buffer. The compiler allocates this via t3malloc() if an * error occurs. */ char *msgbuf; }; /* * dynamic compiler */ class CVmDynamicCompiler { public: /* create */ CVmDynamicCompiler(VMG0_); /* destroy */ ~CVmDynamicCompiler(); /* * Get or create the global singleton instance. This creates an * instance if it doesn't already exist, so that we don't instantiate * the compiler structures until they're actually needed. Once we * create the object, we keep it around in a global. */ static CVmDynamicCompiler *get(VMG0_); /* * Compile source code into byte code, returning a new DynamicFunc * instance containing the compiled function. The return value is the * ID of the new object if the compilation was successful, or * VM_INVALID_OBJ if the compilation failed. * * The source code is the string given by 'src' and 'srclen'. If * 'srcval' is non-null, it should be a VM_SSTR or VM_OBJ value with * the VM representation of the source code string; we'll store this * with the DynamicFunc for retrieval with getSource(). If 'srcval' is * null, the DynamicFunc won't store the source, so getSource() will * return nil. * * 'mode' gives the compilation mode, which controls how the source * string is parsed. * * If 'dbg' is non-null, we compile for the debugger, with the options * in 'dbg'. Debugger evaluation is slightly different from regular * evaluation because it can access local variables from an enclosing * scope. * * If 'errp' is non-null, we'll fill it in with the TCERR_xxx number * for the first compiler error, if any. * * If 'msgbuf' is non-null, we'll fill it in with an allocated string * buffer containing the compiler error message(s). Multiple messages * are separated by newline '\n' characters. The caller must free this * buffer with t3free() when done with it. */ vm_obj_id_t compile(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t srclen, CVmDynCompMode mode, CVmDynCompDebug *dbg, CVmDynCompResults *results); protected: /* generate code for a code body */ int gen_code_body( VMG_ class CTPNStmTop *node, const vm_val_t *srcval, CVmDynCompDebug *dbg); /* parser */ class CTcParser *prs_; /* compiler host interface */ class CTcHostIfcDynComp *hostifc_; }; #endif /* VMDYNFUNC_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmDynamicFunc) frobtads-1.2.3/tads3/vmvec.cpp0000644000175000001440000027712211773413241015364 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvec.cpp - T3 VM Vector metaclass Function Notes Modified 05/14/00 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmtype.h" #include "vmmcreg.h" #include "vmmeta.h" #include "vmglob.h" #include "vmobj.h" #include "vmvec.h" #include "vmlst.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmstack.h" #include "vmbif.h" #include "vmundo.h" #include "vmrun.h" #include "vmiter.h" #include "vmsort.h" /* ------------------------------------------------------------------------ */ /* * The largest number of items we can store is the smaller of the maximum * value for a UINT2 (unsigned 16-bit integer, so 2^16-1) or the maximum * allocation size (OSMALMAX) divided by the element size (DATAHOLDER) * divided minus overhead. */ const int32_t VEC_MAX_BY_ALO = (int32_t)(OSMALMAX/(VMB_DATAHOLDER + 1.0/8)) - 4; const int32_t VEC_MAX_ELEMENTS = (VEC_MAX_BY_ALO < 0xFFFF ? VEC_MAX_BY_ALO : 0xFFFF); /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassVector metaclass_reg_obj; CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjVector:: *CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjVector::getp_undef, /* 0 */ &CVmObjVector::getp_to_list, /* 1 */ &CVmObjVector::getp_get_size, /* 2 */ &CVmObjVector::getp_copy_from, /* 3 */ &CVmObjVector::getp_fill_val, /* 4 */ &CVmObjVector::getp_subset, /* 5 */ &CVmObjVector::getp_apply_all, /* 6 */ &CVmObjVector::getp_index_which, /* 7 */ &CVmObjVector::getp_for_each, /* 8 */ &CVmObjVector::getp_for_each_assoc, /* 9 */ &CVmObjVector::getp_map_all, /* 10 */ &CVmObjVector::getp_index_of, /* 11 */ &CVmObjVector::getp_val_which, /* 12 */ &CVmObjVector::getp_last_index_of, /* 13 */ &CVmObjVector::getp_last_index_which, /* 14 */ &CVmObjVector::getp_last_val_which, /* 15 */ &CVmObjVector::getp_count_of, /* 16 */ &CVmObjVector::getp_count_which, /* 17 */ &CVmObjVector::getp_get_unique, /* 18 */ &CVmObjVector::getp_append_unique, /* 19 */ &CVmObjVector::getp_sort, /* 20 */ &CVmObjVector::getp_set_length, /* 21 */ &CVmObjVector::getp_insert_at, /* 22 */ &CVmObjVector::getp_remove_element_at, /* 23 */ &CVmObjVector::getp_remove_range, /* 24 */ &CVmObjVector::getp_append, /* 25 */ &CVmObjVector::getp_prepend, /* 26 */ &CVmObjVector::getp_append_all, /* 27 */ &CVmObjVector::getp_remove_element, /* 28 */ &CVmObjVector::getp_splice, /* 29 */ &CVmObjVector::getp_join, /* 30 */ &CVmObjVector::getp_generate, /* 31 */ &CVmObjVector::getp_indexOfMin, /* 32 */ &CVmObjVector::getp_minVal, /* 33 */ &CVmObjVector::getp_indexOfMax, /* 34 */ &CVmObjVector::getp_maxVal /* 35 */ }; /* static method indices */ static const int PROPIDX_generate = 31; /* ------------------------------------------------------------------------ */ /* * create with a given number of elements */ CVmObjVector::CVmObjVector(VMG_ size_t element_count) { /* allocate space */ alloc_vector(vmg_ element_count); /* * a vector intially has no elements in use; the element_count * merely gives the number of slots to be allocated initially (this * won't affect the allocation count, which is stored separately) */ set_element_count(0); } /* ------------------------------------------------------------------------ */ /* * allocate space */ void CVmObjVector::alloc_vector(VMG_ size_t cnt) { /* allocate space for the given number of elements */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this); /* set the element count and allocated count */ set_allocated_count(cnt); set_element_count(cnt); /* clear the undo bits */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjVector::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Check an argument to see if it's a source list or vector */ static int get_src_obj(VMG_ const vm_val_t *arg, size_t *copy_cnt) { /* check for a list */ int c; if (arg->is_listlike(vmg0_) && (c = arg->ll_length(vmg0_)) >= 0) { /* it's a list-like object */ *copy_cnt = c; return TRUE; } /* it's not a list */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; CVmObjVector *vec; size_t cnt = 0; size_t copy_cnt = 0; vm_val_t *lstval = 0; size_t i; /* check arguments */ if (argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* if there are no arguments, use a default initial allocation size */ if (argc == 0) cnt = 10; /* parse the first argument, if present */ if (argc >= 1) { /* get the first argument */ vm_val_t *arg1 = G_stk->get(0); /* check for an initial allocation length or source object */ if (arg1->typ == VM_INT) { /* get the number of elements to allocate */ cnt = (size_t)arg1->val.intval; } else if (get_src_obj(vmg_ arg1, ©_cnt)) { /* it's a source object */ lstval = arg1; } else { /* anything else is invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } /* parse the second argument, if present */ if (argc >= 2) { /* get the second argument */ vm_val_t *arg2 = G_stk->get(1); /* see what we have */ if (get_src_obj(vmg_ arg2, ©_cnt)) { /* it's a source object */ lstval = arg2; } else if (arg2->typ == VM_INT) { /* * They want an explicit initial size, with the elements filled * in with nil values. Set the copy size; we'll know to fill * in nil values since there's no source object to copy from. */ copy_cnt = (size_t)arg2->val.intval; } else { /* invalid source type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* * make sure we allocate enough initial space to store the source * object we're copying */ if (copy_cnt > cnt) cnt = copy_cnt; /* create the vector with the given number of elements */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); vec = new (vmg_ id) CVmObjVector(vmg_ cnt); /* copy or initialize the elements */ for (i = 0 ; i < copy_cnt ; ++i) { vm_val_t ele; /* * get my element at this index: if we have a source list or * vector, take the current element from that list or vector; * otherwise, initialize the element to nil */ if (lstval != 0) lstval->ll_index(vmg_ &ele, i + 1); else ele.set_nil(); /* * set my element at this index - this is construction, so there's * no need to save undo information */ vec->set_element(i, &ele); } /* set the initial size */ vec->set_element_count(copy_cnt); /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjVector::save_to_file(VMG_ class CVmFile *fp) { size_t ele_cnt; size_t alloc_cnt; /* get our element count and full allocation count */ alloc_cnt = get_allocated_count(); ele_cnt = get_element_count(); /* write the counts and the elements */ fp->write_uint2(alloc_cnt); fp->write_uint2(ele_cnt); fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); } /* * restore from a file */ void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { size_t ele_cnt; size_t alloc_cnt; /* read the element count and the full allocation count */ alloc_cnt = fp->read_uint2(); ele_cnt = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate the space at the allocation count */ alloc_vector(vmg_ alloc_cnt); /* set the actual in-use element count */ set_element_count(ele_cnt); /* read the contents */ fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); /* fix up object references in the values */ fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt); } /* ------------------------------------------------------------------------ */ /* * create with no initial contents */ vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjVector(); return id; } /* * create with a given number of elements */ vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjVector(vmg_ element_count); return id; } /* ------------------------------------------------------------------------ */ /* * create with a given list of values, given as varargs vm_val_t values */ vm_obj_id_t CVmObjVector::create_fill(VMG_ int in_root_set, size_t element_count, ...) { /* create the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); CVmObjVector *vec = new (vmg_ id) CVmObjVector(vmg_ element_count); /* populate the elements */ va_list args; va_start(args, element_count); for (size_t i = 0 ; i < element_count ; ++i) { /* get the next value */ const vm_val_t *val = va_arg(args, const vm_val_t *); /* store it */ vec->set_element(i, val); } va_end(args); /* set the element count */ vec->set_element_count(element_count); /* return the new object's ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create a copy of this object */ vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val) { vm_obj_id_t new_id; CVmObjVector *new_vec; /* save the original object on the stack for gc protection */ G_stk->push(self_val); /* create a new vector with the same parameters as this one */ new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE); new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count()); new_vec->set_element_count(get_element_count()); /* copy the elements from the old vector to the new vector */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(get_element_count())); /* done with the gc protection - discard it */ G_stk->discard(1); /* return the new vector's object ID */ return new_id; } /* ------------------------------------------------------------------------ */ /* * Set the allocation size - this is the number of elements we've allocated * space for, which might exceed the number of elements we're actually * storing currently. */ void CVmObjVector::set_allocated_count(size_t cnt) { /* if the new size exceeds the maximum allowed size, throw an error */ if (cnt > 65535) err_throw(VMERR_OUT_OF_RANGE); /* set the new allocated size */ vmb_put_len(cons_get_vector_ext_ptr(), cnt); } /* ------------------------------------------------------------------------ */ /* * Create an iterator */ void CVmObjVector::new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { vm_val_t copy_val; /* * Create a copy of the vector - since a vector can change, we must * always create an iterator based on a copy, so that the iterator has * a consistent view of the original as of the time the iterator was * created. */ copy_val.set_obj(create_copy(vmg_ self_val)); /* push the copy for gc protection */ G_stk->push(©_val); /* * Set up a new indexed iterator object. The first valid index for a * vector is always 1, and the last valid index is the same as the * number of elements. */ retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ ©_val, 1, get_element_count())); /* discard gc protection */ G_stk->discard(); } /* * Create a live iterator */ void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { /* * Set up a new indexed iterator object. The first valid index for a * vector is always 1, and the last valid index is the same as the * number of elements. Since we want a "live" iterator, create the * iterator with a direct reference to the vector. */ retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val, 1, get_element_count())); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjVector::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * call a static property */ int CVmObjVector::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods */ switch (idx) { case PROPIDX_generate: return static_getp_generate(vmg_ result, argc); default: /* inherit the default handling */ return CVmObjCollection::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit our base class handling */ return CVmObjCollection:: get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Expand the vector to accommodate the given number of new elements. * We'll increase the element count, and we'll save undo for the change. * If necessary, we'll re-allocate our memory extension block at a * larger size. */ void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements) { /* calculate the element count */ size_t new_ele_cnt = get_element_count() + added_elements; /* remember the new size, saving undo */ set_element_count_undo(vmg_ self, new_ele_cnt); } /* ------------------------------------------------------------------------ */ /* * Append an element, with no undo */ void CVmObjVector::append_element(VMG_ vm_obj_id_t self, const vm_val_t *val) { /* expand the vector if necessary */ size_t cnt = get_element_count(); if (cnt >= get_allocated_count()) { /* expand by 50% of the current size, or at least 5 new elements */ int inc = cnt/2; if (inc < 5) inc = 5; /* do the expansion */ expand_by(vmg_ self, inc); } /* set the element */ set_element(cnt, val); /* count it in the vector */ set_element_count(cnt+1); } /* ------------------------------------------------------------------------ */ /* * Set the element count, keeping undo for the change */ void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self, size_t new_ele_cnt) { /* add undo if necessary */ if (G_undo != 0) { size_t old_ele_cnt; vm_val_t old_val; size_t idx; vm_val_t nil_val; /* get the old size */ old_ele_cnt = get_element_count(); /* * If we're shrinking the vector, save undo for each element we're * dropping off the end; to do this, simply set each element we're * losing to nil, since this will add an undo record with the * original value of the element. * * Note that we must save undo for dropped elements BEFORE we set * the vector's new sizd. We must perform the steps in this order * because undo is applied in reverse order: when we read back the * undo, we'll first change the vector size to its old size (since * that's the last saved undo instruction), then we'll set the * elements to their old values. */ nil_val.set_nil(); for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx) set_element_undo(vmg_ self, idx, &nil_val); /* set up an integer with the pre-modification element count */ old_val.set_int(old_ele_cnt); /* * add the undo record - use the special index value 0xffffffff * as the key; this will never be a valid index for a real * element, because we could never address an element with an * index this high in a 32-bit address space */ G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val); } /* * if the new size is larger than our current allocation size, * increase the allocated size */ if (new_ele_cnt > get_allocated_count()) { size_t new_alloc_cnt; size_t new_mem_size; /* bump up the allocated size */ new_alloc_cnt = get_allocated_count() + get_alloc_count_increment(); /* if that's still not big enough, go up to the requested size */ if (new_alloc_cnt < new_ele_cnt) new_alloc_cnt = new_ele_cnt; /* get the new size we need */ new_mem_size = calc_alloc(new_alloc_cnt); /* reallocate our memory at the new, larger size */ ext_ = (char *)G_mem->get_var_heap() ->realloc_mem(new_mem_size, ext_, this); /* set the new allocation size */ set_allocated_count(new_alloc_cnt); /* * clear the undo bits (it's easier than moving them, and the only * cost is that we might generate some redundant undo records) */ clear_undo_bits(); } /* * if we're expanding the in-use size of the vector, set each * newly-added element to nil */ if (new_ele_cnt > get_element_count()) { size_t i; vm_val_t nil_val; /* * set the old value for each new element to nil (we don't have to * save undo for this, since the undo operation for the change in * size will simply discard all of the new elements) */ nil_val.set_nil(); for (i = get_element_count() ; i < new_ele_cnt ; ++i) set_element(i, &nil_val); } /* set the new element count */ set_element_count(new_ele_cnt); } /* ------------------------------------------------------------------------ */ /* * notify of new undo savepoint */ void CVmObjVector::notify_new_savept() { /* * clear the undo bits - we obviously have no undo for the new * savepoint yet */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * apply undo */ void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec) { /* * if the record refers to index 0xffffffff, this is a size-change * record; otherwise, it's an ordinary element change record */ if ((size_t)rec->id.intval == 0xffffffffU) { /* * it's a size change - apply the new size, which is stored as * an integer in the old value record */ set_element_count((size_t)rec->oldval.val.intval); } else { /* it's an ordinary index record - set the element from undo data */ set_element((size_t)rec->id.intval, &rec->oldval); } } /* ------------------------------------------------------------------------ */ /* * mark references from an undo record */ void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec) { /* if the undo record refers to an object, mark the object */ if (rec->oldval.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE); } /* ------------------------------------------------------------------------ */ /* * mark references */ void CVmObjVector::mark_refs(VMG_ uint state) { /* get my element count */ size_t cnt = get_element_count(); /* mark as referenced each object in the vector */ for (char *p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) { /* * if this is an object, mark it as referenced, and mark its * references as referenced */ if (vmb_get_dh_type(p) == VM_OBJ) G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state); } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * load the data from an image file */ void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz) { size_t alloc_cnt; size_t ele_cnt; /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* get the allocation size and the actual in-use size */ alloc_cnt = vmb_get_len(ptr); ele_cnt = vmb_get_len(ptr + VMB_LEN); /* make sure we allocate at least as much as is actually in use */ if (alloc_cnt < ele_cnt) alloc_cnt = ele_cnt; /* make sure the size isn't larger than we'd expect */ if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt); /* * allocate memory at the new allocation size as indicated in the * image data */ alloc_vector(vmg_ alloc_cnt); /* set the in-use element count */ set_element_count(ele_cnt); /* * if the size is smaller than we'd expect from the initialized * element count, set extra elements to nil */ if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) { size_t i; vm_val_t nil_val; /* set all elements to nil */ nil_val.set_nil(); for (i = 0 ; i < ele_cnt ; ++i) set_element(i, &nil_val); } /* copy the initialized elements from the image data */ memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2); /* we're resetting to initial state, so forget all undo */ clear_undo_bits(); } /* ------------------------------------------------------------------------ */ /* * index the vector */ int CVmObjVector::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t, const vm_val_t *index_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * get the indexed element and store it in the result, adjusting the * index to the C-style 0-based range */ get_element(idx - 1, result); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * set an indexed element of the vector */ int CVmObjVector::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's at least 1 */ if (idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if it's higher than the current length, extend the vector with nil * entries to the requested size */ if ((uint32_t)idx > get_element_count()) { size_t i; vm_val_t nil_val; /* note the first new element index */ i = get_element_count(); /* extend the vector */ set_element_count_undo(vmg_ self, idx); /* * Fill in entries between the old length and the new length with * nil. Note that we don't have to fill in the very last element, * since we'll explicitly set it to the new value momentarily * anyway. Note also that we don't need to keep undo for the * initializations, since on undo we'll truncate the vector to * remove the newly-added elements and thus won't need to restore * any values for the slots. */ for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i) set_element(i, &nil_val); /* * set the new value - this doesn't require undo since we had to * expand the vector to make room for it */ set_element(idx - 1, new_val); } else { /* set the element and record undo, using a zero-based index */ set_element_undo(vmg_ self, (size_t)idx - 1, new_val); } /* the result is the original vector value */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Set an element and record undo for the change - takes a C-style 0-based * index. */ void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self, size_t idx, const vm_val_t *new_val) { /* if we don't have undo for this element already, save undo now */ if (G_undo != 0 && !get_undo_bit(idx)) { vm_val_t old_val; /* get the pre-modification value of this element */ get_element(idx, &old_val); /* add the undo record */ G_undo->add_new_record_int_key(vmg_ self, idx, &old_val); /* * set the undo bit to indicate that we now have undo for this * element */ set_undo_bit(idx, TRUE); } /* get the indexed element and store it in the result */ set_element(idx, new_val); } /* ------------------------------------------------------------------------ */ /* * Check a value for equality. We will match any list or vector with the * same number of elements and the same value for each element. */ int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const { int cnt; int cnt2; int idx; /* if the recursion depth is excessive, throw an error */ if (depth > VM_MAX_TREE_DEPTH_EQ) err_throw(VMERR_TREE_TOO_DEEP_EQ); /* if the other value is a reference to myself, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) { /* no need to look at the contents if this is a reference to me */ return TRUE; } /* if it's not list-like, it's not a match */ if (!val->is_listlike(vmg0_) || (cnt2 = val->ll_length(vmg0_)) < 0) return FALSE; /* if the sizes don't match, the values are not equal */ cnt = get_element_count(); if (cnt != cnt2) return FALSE; /* compare element by element */ for (idx = 0 ; idx < cnt ; ++idx) { vm_val_t val1; vm_val_t val2; /* get this element of self */ get_element(idx, &val1); /* get this element of the other value */ val->ll_index(vmg_ &val2, idx + 1); /* if these elements aren't equal, our values aren't equal */ if (!val1.equals(vmg_ &val2, depth + 1)) return FALSE; } /* we didn't find any differences, so the values are equal */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Hash value calculation */ uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const { uint hash; uint i; /* if the recursion depth is excessive, throw an error */ if (depth > VM_MAX_TREE_DEPTH_EQ) err_throw(VMERR_TREE_TOO_DEEP_EQ); /* combine the hash values of the members of the vector */ for (hash = 0, i = 0 ; i < get_element_count() ; ++i) { vm_val_t ele; /* get this element */ get_element(i, &ele); /* * Calculate its hash value and add it into the combined hash. * * Note that we have to increase the recursion depth, because we're * recursively calculating the hash value of our contents, and our * contents can refer (directly or indirectly) back to this object. * In other words, we can have cycles in the reference tree from * this object back to itself, so we need to keep track of the * recursion depth to ensure we don't loop forever. */ hash += ele.calc_hash(vmg_ depth + 1); } /* return the combined hash */ return hash; } /* ------------------------------------------------------------------------ */ /* * add a value to the vector */ int CVmObjVector::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { int idx; int rhs_cnt, alo_cnt; int i; CVmObjVector *new_vec; /* push the value to append, for gc protection */ G_stk->push(val); /* * remember the index of the first new element - this is simply one * higher than the last valid current index */ idx = get_element_count(); /* get the number of elements to add */ rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1); /* if it's not list-like, just add it directly as a single element */ alo_cnt = (rhs_cnt < 0 ? 1 : rhs_cnt); /* * allocate a copy of the vector for the result - make it big enough * for my elements plus the elements to be appended */ result->set_obj(create(vmg_ FALSE, idx + alo_cnt)); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); /* push the result for gc protection */ G_stk->push(result); /* copy my elements into the result */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(idx)); /* * set the element count to include the elements copied so far (it's a * new vector, so there's no need to save undo) */ new_vec->set_element_count(idx); /* add the new element or elements */ if (rhs_cnt < 0) { /* add a single element */ new_vec->set_element(idx, val); new_vec->set_element_count(idx + 1); } else { /* add the contents of the rhs */ for (i = 1 ; i <= rhs_cnt ; ++i, ++idx) { /* get this element from the right-hand side */ vm_val_t ele; val->ll_index(vmg_ &ele, i); /* store the element in the result */ new_vec->set_element(idx, &ele); new_vec->set_element_count(idx + 1); } } /* discard the gc protect */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * subtract a value from the vector */ int CVmObjVector::sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { int ele_cnt, rhs_cnt; int i; CVmObjVector *new_vec; size_t new_cnt; /* push the value to append, for gc protection */ G_stk->push(val); /* note the initial element count */ ele_cnt = get_element_count(); /* get the number of elements to remove */ rhs_cnt = (val->is_listlike(vmg0_) ? val->ll_length(vmg0_) : -1); /* * allocate a new vector for the result - the new object will be no * larger than the current object, so allocate at the same size */ result->set_obj(create(vmg_ FALSE, ele_cnt)); /* push the result for gc protection */ G_stk->push(result); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); /* * copy each element from me to the new vector, but don't copy * elements found in the right-hand side */ for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i) { vm_val_t ele; int found = FALSE; /* get this element of self */ get_element(i, &ele); /* check the right-hand side for a match */ if (rhs_cnt < 0) { /* single element - just check this element */ if (val->equals(vmg_ &ele)) found = TRUE; } else { /* scan the right side for the element */ for (int j = 1 ; j <= rhs_cnt ; ++j) { /* get this right-hand element */ vm_val_t rh_ele; val->ll_index(vmg_ &rh_ele, j); /* * if this right-hand element matches this left-hand * element, note that we have a match */ if (rh_ele.equals(vmg_ &ele)) { /* note that we found it */ found = TRUE; /* no need to look any further - one match is enough */ break; } } } /* * If we didn't find the value, include it in the result vector. * Note that we don't have to save undo, since we're still * constructing the new vector. */ if (!found) { /* store the element in the result vector */ new_vec->set_element(new_cnt, &ele); /* count it */ ++new_cnt; } } /* * update the new vector's size - we don't have to save undo for this * change, because we're still constructing the new vector */ new_vec->set_element_count(new_cnt); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - convert to a list */ int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int src_cnt; int dst_cnt; int start_idx; CVmObjList *lst; int idx; uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(0, 2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* note our size */ src_cnt = get_element_count(); /* * if there's a starting index, retrieve it; otherwise, start at our * first element */ if (orig_argc >= 1) start_idx = (size_t)CVmBif::pop_int_val(vmg0_); else start_idx = 1; /* negative index values count from the end of the list */ if (start_idx < 0) start_idx += src_cnt + 1; /* if the starting index is below 1, force it to 1 */ if (start_idx < 1) start_idx = 1; /* adjust the starting index to a 0-based index */ --start_idx; /* * if there's a size argument, retrieve it; if it's not specified, * use our actual size for the output size */ if (orig_argc >= 2) dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_); else dst_cnt = src_cnt; /* * in no case will the result list have more elements than we can * actually supply */ if (start_idx >= src_cnt) { /* we're starting past our last element - we can't supply anything */ dst_cnt = 0; } else if (src_cnt - start_idx < dst_cnt) { /* we can't supply as many values as requested - lower the size */ dst_cnt = src_cnt - start_idx; } /* push a self-reference for garbage collection protection */ G_stk->push()->set_obj(self); /* create the new list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the list elements */ for (idx = 0 ; idx < dst_cnt ; ++idx) { vm_val_t val; /* get my element at this index */ get_element(idx + start_idx, &val); /* store the element in the list */ lst->cons_set_element(idx, &val); } /* discard the self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - copy from another vector or list */ int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(4); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the source value */ vm_val_t src_val; G_stk->pop(&src_val); /* make sure the source argument is list-like, and get its length */ int src_cnt; if (!src_val.is_listlike(vmg0_) || (src_cnt = src_val.ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the source starting index, adjusting negatives to count from end */ int src_start = CVmBif::pop_int_val(vmg0_); if (src_start < 0) src_start += src_cnt + 1; /* note the destination length (i.e., my element count) */ int dst_cnt = get_element_count(); /* get the destination starting index, adjusting negative values */ int dst_start = CVmBif::pop_int_val(vmg0_); if (dst_start < 0) dst_start += dst_cnt + 1; /* get the number of elements to copy */ int copy_cnt = CVmBif::pop_int_val(vmg0_); /* if either starting index is below 1, force it to 1 */ if (src_start < 1) src_start = 1; if (dst_start < 1) dst_start = 1; /* adjust the starting indices to 0-based values */ --src_start; --dst_start; /* limit our copying to the remaining elements of the source */ if (src_start >= src_cnt) copy_cnt = 0; else if (src_start + copy_cnt >= src_cnt) copy_cnt = src_cnt - src_start; /* expand the vector if necessary to make room for added elements */ if (dst_start + copy_cnt > dst_cnt) set_element_count_undo(vmg_ self, dst_start + copy_cnt); /* set the list elements */ for (int i = 0 ; i < copy_cnt ; ++i) { /* get the source element at this index */ vm_val_t val; src_val.ll_index(vmg_ &val, i + src_start + 1); /* set my element at this index, recording undo for the change */ set_element_undo(vmg_ self, i + dst_start, &val); } /* the return value is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - fill with a value */ int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 2); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* note our size */ int cnt = get_element_count(); /* the return value is 'self' */ retval->set_obj(self); /* get the fill value */ vm_val_t fill_val; G_stk->pop(&fill_val); /* get the starting index; the default is 1 if it's not specified */ int start_idx = (orig_argc >= 2 ? CVmBif::pop_int_val(vmg0_) : 1); /* a negative index counts from the end of the vector */ if (start_idx < 0) start_idx += cnt + 1; /* if the starting index is below 1, force it to 1 */ if (start_idx < 1) start_idx = 1; /* adjust the starting index to a 0-based index */ --start_idx; /* * if there's a count argument, retrieve it; if it's not specified, * use our actual size for the count */ int end_idx = (orig_argc >= 3 ? start_idx + CVmBif::pop_int_val(vmg0_) : cnt); /* push self and the fill value for gc protection */ G_stk->push(retval); G_stk->push(&fill_val); /* expand the vector to the requested size */ if (end_idx > cnt) set_element_count_undo(vmg_ self, end_idx); /* set the elements to the fill value */ for (int idx = start_idx ; idx < end_idx ; ++idx) { /* set the element to the fill value, recording undo for the change */ set_element_undo(vmg_ self, idx, &fill_val); } /* discard GC protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the number of elements */ int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return my element count */ retval->set_int(get_element_count()); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - select a subset; returns a new vector consisting * of the subset of the original vector */ int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t src; size_t dst; size_t cnt; CVmObjVector *new_vec; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.subset", self, 5, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while allocating to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * allocate the new vector that we'll return - all we know at this * point is that the new vector won't be larger than the current * vector, so allocate at the current size */ cnt = get_element_count(); retval->set_obj(create(vmg_ FALSE, cnt)); /* get the return value as an vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our list, and invoke the callback on * each element. If the element passes, write it to the current * output location in the list; otherwise, just skip it. * * Note that we're using the same list as source and destination, * which is easy because the list will either shrink or stay the * same - we'll never need to insert new elements. */ for (src = dst = 0 ; src < cnt ; ++src) { vm_val_t ele; const vm_val_t *val; /* get this element from the source vector */ get_element(src, &ele); /* push the element as the callback's argument */ G_stk->push(&ele); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* get the result from R0 */ val = G_interpreter->get_r0(); /* * if the callback returned non-nil and non-zero, include this * element in the result */ if (val->typ == VM_NIL || (val->typ == VM_INT && val->val.intval == 0)) { /* it's nil or zero - don't include it in the result */ } else { /* * include this element in the result (there's no need to * save undo, since the whole vector is new) */ new_vec->set_element(dst, &ele); /* advance the output index */ ++dst; } } /* set the actual number of elements in the result */ new_vec->set_element_count(dst); /* discard our gc protection (self, return value) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - apply a callback to each element */ int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t idx; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.applyAll", self, 6, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * we're going to return the 'self' object, since we update the vector * in-place */ retval->set_obj(self); /* * Go through each element of our vector, invoking the callback on * each element. Replace each element with the result of the * callback. Note that we intentionally re-check the element count on * each iteration, in case the callback changes the number of * elements. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push this element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* replace this element with the result */ set_element_undo(vmg_ self, idx, G_interpreter->get_r0()); } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - find the first element matching a condition */ int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, working forwards */ vm_rcdesc rc(vmg_ "Vector.indexWhich", self, 7, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE, &rc); } /* * property evaluator - lastIndexWhich */ int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, working backwards */ vm_rcdesc rc(vmg_ "Vector.lastIndexWhich", self, 14, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE, &rc); } /* * general handler for indexWhich and lastIndexWhich */ int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int forward, int count_only, vm_rcdesc *rc) { const vm_val_t *func_val; size_t cnt; size_t idx; int val_cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* presume we won't find a matching element */ retval->set_nil(); val_cnt = 0; /* get the number of elements to visit */ cnt = get_element_count(); /* start at the first or last element, depending on direction */ idx = (forward ? 0 : cnt); /* * Go through each element of our vector, and invoke the callback on * each element, looking for the first one that returns true. */ for (;;) { /* if we've reached the last element, we can stop looking */ if (forward ? idx >= cnt : idx == 0) break; /* if we're going backwards, decrement the index */ if (!forward) --idx; /* push the element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, rc, 0); /* * if the callback returned true, we've found the element we're * looking for */ if (G_interpreter->get_r0()->typ == VM_NIL || (G_interpreter->get_r0()->typ == VM_INT && G_interpreter->get_r0()->val.intval == 0)) { /* nil or zero - this one failed the test, so keep looking */ } else { /* it passed the test - check what we're doing */ if (count_only) { /* we only want the count */ ++val_cnt; } else { /* we want the (1-based) index - return it */ retval->set_int(idx + 1); /* found it - no need to keep looking */ break; } } /* if we're going forwards, increment the index */ if (forward) ++idx; } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* return the count if desired */ if (count_only) retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - lastValWhich */ int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* get the index of the value using lastIndexWhich */ getp_last_index_which(vmg_ self, retval, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* get the element as the return value */ idx = (int)retval->val.intval; get_element(idx - 1, retval); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - valWhich */ int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* get the index of the value using indexWhich */ getp_index_which(vmg_ self, retval, argc); /* if the return value is a valid index, get the value at the index */ if (retval->typ == VM_INT) { int idx; /* get the element as the return value */ idx = (int)retval->val.intval; get_element(idx - 1, retval); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - call a callback for each element */ int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general forEach processor */ vm_rcdesc rc(vmg_ "Vector.forEach", self, 8, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, FALSE, &rc); } /* * property evaluator - call a callback for each element */ int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general forEach processor */ vm_rcdesc rc(vmg_ "Vector.forEachAssoc", self, 9, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, TRUE, &rc); } /* * General forEach/forEachAssoc processor */ int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int pass_key_to_cb, vm_rcdesc *rc) { const vm_val_t *func_val; size_t idx; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* no return value */ retval->set_nil(); /* * Invoke the callback on each element. Note that we intentionally do * not cache the element count, since it is possible that a program * could change the vector size in the course of an iteration; if we * cached the size, and the actual size were reduced during the * iteration, we would visit invalid elements past the new end of the * vector. To avoid this possibility, we re-check the current element * count on each iteration to make sure we haven't run off the end of * the vector. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push the element as the callback's argument */ push_element(vmg_ idx); /* push the index argument if desired */ if (pass_key_to_cb) G_stk->push()->set_int(idx + 1); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1, rc, 0); } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - mapAll */ int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const vm_val_t *func_val; size_t idx; CVmObjVector *new_vec; static CVmNativeCodeDesc desc(1); vm_rcdesc rc(vmg_ "Vector.mapAll", self, 10, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the function pointer argument, but leave it on the stack */ func_val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* * allocate a new vector for the return value - the new vector will * have the same size as the original, since we're mapping each * element of the old vector to the corresponding element of the new * vector */ retval->set_obj(create(vmg_ FALSE, get_allocated_count())); /* get the return value as an vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* * push a reference to the new list to protect it from the garbage * collector, which could be invoked in the course of executing the * user callback */ G_stk->push(retval); /* * Go through each element of our vector, and invoke the callback on * each element, storing the result in the corresponding element of * the new vector. Note that we re-check the element count on each * iteration, in case the callback changes it on us. */ for (idx = 0 ; idx < get_element_count() ; ++idx) { /* push the element as the callback's argument */ push_element(vmg_ idx); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ func_val, 1, &rc, 0); /* * replace this element with the result (there's no need to save * undo, since the whole vector is new) */ new_vec->set_element(idx, G_interpreter->get_r0()); new_vec->set_element_count(idx + 1); } /* discard our gc protection (self, new vector) and our arguments */ G_stk->discard(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - indexOf */ int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, going forwards */ return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE); } /* * property evaluator - lastIndexOf */ int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler, going backwards */ return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE); } /* * general handler for indexOf and lastIndexOf - searches the vector * either forwards of backwards for a given value */ int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int forward, int count_only) { const vm_val_t *val; size_t cnt; size_t idx; int val_cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value, but leave it on the stack */ val = G_stk->get(0); /* push a self-reference while working to protect from gc */ G_interpreter->push_obj(vmg_ self); /* presume we won't find a matching element */ retval->set_nil(); val_cnt = 0; /* get the number of elements to visit */ cnt = get_element_count(); /* start at the first or last element, depending on the direction */ idx = (forward ? 0 : cnt); /* scan the list, looking for the element */ for (;;) { vm_val_t ele; /* if we've reached the last element, stop looking */ if (forward ? idx >= cnt : idx == 0) break; /* if we're going backwards, move to the next element position */ if (!forward) --idx; /* get this element */ get_element(idx, &ele); /* if the element matches the search value, return its index */ if (ele.equals(vmg_ val)) { /* it matches - see what we're doing */ if (count_only) { /* they only want the count */ ++val_cnt; } else { /* this is the one - return its 1-based index */ retval->set_int(idx + 1); /* foind it - no need to keep searching */ break; } } /* if we're going forwards, move to the next element */ if (forward) ++idx; } /* discard our gc protection (self) and our arguments */ G_stk->discard(2); /* return the count if desired */ if (count_only) retval->set_int(val_cnt); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - countOf */ int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler to obtain the count */ return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE); } /* ------------------------------------------------------------------------ */ /* * property evaluator - countWhich */ int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use our general handler to obtain the count */ vm_rcdesc rc(vmg_ "Vector.countWhich", self, 17, G_stk->get(0), argc); return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE, &rc); } /* ------------------------------------------------------------------------ */ /* * property evaluator - getUnique; returns a new vector consisting of the * unique elements of the original vector */ int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { CVmObjVector *new_vec; size_t cnt; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* put myself on the stack for GC protection */ G_interpreter->push_obj(vmg_ self); /* * allocate a new vector for the return value - the new vector will * never be any larger than the original */ cnt = get_element_count(); retval->set_obj(create(vmg_ FALSE, cnt)); /* push a reference to the new list for gc protection */ G_stk->push(retval); /* get the return value as a vector */ new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* start out with the result element count the same as my own */ new_vec->set_element_count(cnt); /* copy my elements to the new vector */ memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), calc_alloc_ele(cnt)); /* uniquify the result */ new_vec->cons_uniquify(vmg0_); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * For construction, eliminate repeated elements of the vector, leaving * only the unique elements. Reduces the size of the vector to the size * required to accommodate the unique elements. */ void CVmObjVector::cons_uniquify(VMG0_) { size_t cnt; size_t src, dst; /* get the number of elements */ cnt = get_element_count(); /* loop through the list and look for repeated values */ for (src = dst = 0 ; src < cnt ; ++src) { size_t idx; vm_val_t src_val; int found; /* * look for a copy of this source value already in the output list */ get_element(src, &src_val); for (idx = 0, found = FALSE ; idx < dst ; ++idx) { vm_val_t idx_val; /* get this value */ get_element(idx, &idx_val); /* if it's equal to the current source value, note it */ if (src_val.equals(vmg_ &idx_val)) { /* note that we found it */ found = TRUE; /* no need to look any further */ break; } } /* if we didn't find the value, copy it to the output list */ if (!found) { /* * add it to the output vector - since this is a * construction-only method, there's no need to save undo (if * we apply undo, we'll undo the entire construction of the * vector, hence there's no need to track changes made since * creation) */ set_element(dst, &src_val); /* count it in the output */ ++dst; } } /* adjust the size of the result list */ set_element_count(dst); } /* ------------------------------------------------------------------------ */ /* * property evaluator - appendUnique */ int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *val2; int cnt2; int cnt; static CVmNativeCodeDesc desc(1); int i; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the other value, but leave it on the stack */ val2 = G_stk->get(0); /* the return value is 'self' */ retval->set_obj(self); /* put myself on the stack for GC protection */ G_stk->push(retval); /* get the number of elements in the original */ cnt = get_element_count(); /* get the number of elements to append */ cnt2 = (val2->is_listlike(vmg0_) ? val2->ll_length(vmg0_) : -1); /* expand myself to make room for the new elements */ expand_by(vmg_ self, cnt2 < 0 ? 1 : cnt2); /* append each element of the right-hand side */ if (cnt2 < 0) { /* the rhs isn't a list, so just append it directly */ set_element_undo(vmg_ self, cnt, val2); } else { /* append each element of the rhs list */ for (i = 1 ; i <= cnt2 ; ++i) { /* get this element */ vm_val_t ele; val2->ll_index(vmg_ &ele, i); /* store it */ set_element_undo(vmg_ self, cnt + i - 1, &ele); } } /* uniquify the result */ uniquify_for_append(vmg_ self); /* discard the gc protection and arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* * uniquify after an appendUnique operation */ void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self) { size_t cnt; size_t src, dst; /* get the number of elements */ cnt = get_element_count(); /* loop through the list and look for repeated values */ for (src = dst = 0 ; src < cnt ; ++src) { size_t idx; vm_val_t src_val; int found; /* look for a copy of this value elsewhere in the vector */ get_element(src, &src_val); for (idx = 0, found = FALSE ; idx < dst ; ++idx) { vm_val_t idx_val; /* get this value */ get_element(idx, &idx_val); /* if it's equal to the current source value, note it */ if (src_val.equals(vmg_ &idx_val)) { /* note that we found it */ found = TRUE; /* no need to look any further */ break; } } /* if we didn't find the value, copy it to the output list */ if (!found) { /* * Add it to the output vector if we're actually changing this * element (i.e., the destination and source indices are * unequal). */ if (dst != src) set_element_undo(vmg_ self, dst, &src_val); /* count it in the output */ ++dst; } } /* adjust the size of the result list */ set_element_count_undo(vmg_ self, dst); } /* ------------------------------------------------------------------------ */ /* * sorter for vector */ class CVmQSortVector: public CVmQSortVal { public: CVmQSortVector() { vec_ = 0; self_ = VM_INVALID_OBJ; } /* get an element from the vector */ void get_ele(VMG_ size_t idx, vm_val_t *val) { vec_->get_element(idx, val); } /* store an element */ void set_ele(VMG_ size_t idx, const vm_val_t *val) { vec_->set_element_undo(vmg_ self_, idx, val); } /* our vector object */ CVmObjVector *vec_; vm_obj_id_t self_; }; /* * property evaluator - sort */ int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { size_t len; uint argc = (in_argc == 0 ? 0 : *in_argc); CVmQSortVector sorter; static CVmNativeCodeDesc desc(0, 2); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* remember the length of my list */ len = get_element_count(); /* set the vector object in the sorter */ sorter.vec_ = this; sorter.self_ = self; /* if we have an 'descending' flag, note it */ if (argc >= 1) sorter.descending_ = (G_stk->get(0)->typ != VM_NIL); /* * if we have a comparison function, note it, but leave it on the * stack for gc protection */ if (argc >= 2) { /* get the function */ sorter.compare_fn_ = *G_stk->get(1); /* initialize the recursive caller descriptor for it */ sorter.rc.init(vmg_ "Vector.sort", self, 20, G_stk->get(0), argc); } /* put myself on the stack for GC protection */ G_interpreter->push_obj(vmg_ self); /* sort the vector, if we have any elements */ if (len != 0) sorter.sort(vmg_ 0, len - 1); /* discard the gc protection and arguments */ G_stk->discard(1 + argc); /* return myself */ retval->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - set the length */ int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { size_t old_len; int32_t new_len; int32_t idx; vm_val_t nil_val; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the return value is 'self' */ retval->set_obj(self); /* note the old length */ old_len = get_element_count(); /* get the new length */ new_len = CVmBif::pop_long_val(vmg0_); /* can't go less than zero */ if (new_len < 0 || new_len >= VEC_MAX_ELEMENTS) err_throw(VMERR_BAD_VAL_BIF); /* set the vector to its new size */ set_element_count_undo(vmg_ self, (size_t)new_len); /* * Set each newly added element to nil. Note that we don't bother * saving undo for these changes: to undo this change, the only thing * we'll have to do is reduce the vector size to its previous size, * and we've already saved undo for the size change. */ nil_val.set_nil(); for (idx = old_len ; idx < new_len ; ++idx) set_element((size_t)idx, &nil_val); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - insert one or more elements at a given index */ int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { /* we must have at least two arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* note the original size of the vector */ int old_cnt = get_element_count(); /* * get the starting insertion index; negatives count from the end, and * zero means to insert after the last existing element */ int ins_idx = CVmBif::pop_int_val(vmg0_); if (ins_idx <= 0) ins_idx += old_cnt + 1; /* the return value is 'self' */ retval->set_obj(self); /* * note the number of items we're adding - we're adding each * argument, other than the first */ int add_cnt = argc - 1; /* * if the starting index is out of range, it's an error - it can * range from 1 to one higher than the current element count (the * top end of the range allows us to insert elements after all of * the current elements) */ if (ins_idx < 1 || ins_idx > old_cnt + 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust the starting index to a zero-based value */ --ins_idx; /* do the insertion */ insert_elements_undo(vmg_ self, ins_idx, add_cnt); /* set the new element values */ for (int idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt) { /* pop the next argument value */ vm_val_t val; G_stk->pop(&val); /* store the item, keeping undo only if it's in the old size range */ if (idx < old_cnt) set_element_undo(vmg_ self, idx, &val); else set_element(idx, &val); } /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append an element */ int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* insert one element at the start of the vector */ insert_elements_undo(vmg_ self, 0, 1); /* * set the first element - note that there's no need to save undo, * since undoing this operation will simply discard this element (in * other words, there's no previous value for this element since it's * being created anew here) */ set_element(0, G_stk->get(0)); /* the return value is 'self' */ retval->set_obj(self); /* discard arguments */ G_stk->discard(1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove an element at the given position */ int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the deletion index; a negative index counts from the end */ int start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx < 0) start_idx += get_element_count() + 1; /* make sure the index is in range */ if (start_idx < 1 || start_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust to a zero-based index */ --start_idx; /* the return value is 'self' */ retval->set_obj(self); /* delete one element */ remove_elements_undo(vmg_ self, start_idx, 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove element(s) matching a given value */ int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *del_val; static CVmNativeCodeDesc desc(1); size_t src; size_t dst; size_t old_cnt; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to be removed */ del_val = G_stk->get(0); /* the return value is 'self'; leave it on the stack */ retval->set_obj(self); G_stk->push(retval); /* get the original number of elements */ old_cnt = get_element_count(); /* delete each element matching the given value */ for (src = dst = 0 ; src < old_cnt ; ++src) { vm_val_t ele_val; /* get this element */ get_element(src, &ele_val); /* * if this element doesn't match the value to be deleted, add it * to the result vector */ if (!ele_val.equals(vmg_ del_val)) { /* * This element is a keeper - if we're moving it to a new * position (i.e., we've deleted any elements before this * one), store it at its new position, keeping undo * information. If we're storing it at its same position, * there's no need to re-store the value or keep any undo, * since no change is involved. */ if (dst != src) set_element_undo(vmg_ self, dst, &ele_val); /* increment the write index past this element */ ++dst; } } /* if the element count changed, set the new element count */ if (dst != old_cnt) set_element_count_undo(vmg_ self, dst); /* discard gc protection and arguments */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - remove a range of elements */ int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { int start_idx; int end_idx; static CVmNativeCodeDesc desc(2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index; a negative value counts from the end */ start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx < 0) start_idx += get_element_count() + 1; /* * make sure the index is in range - it must refer to an existing * element */ if (start_idx < 1 || start_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* get the ending index, and adjust for negative values */ end_idx = CVmBif::pop_int_val(vmg0_); if (end_idx < 0) end_idx += get_element_count() + 1; /* * make sure it's in range - it must refer to an existing element, * and it must be greater than or equal to the starting index */ if (end_idx < start_idx || end_idx > (int)get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* adjust to zero-based indices */ --start_idx; --end_idx; /* the return value is 'self' */ retval->set_obj(self); /* * delete the elements - the number of elements we're deleting is * one higher than the difference of the starting and ending indices * (if the two indices are the same, we're deleting just the one * element) */ remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append an element */ int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *valp; size_t cnt; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to append, but leave it on the stack */ valp = G_stk->get(0); /* get the old size */ cnt = get_element_count(); /* the result object is 'self' */ retval->set_obj(self); /* push a self-reference for gc protection */ G_stk->push(retval); /* expand myself by one element to make room for the addition */ expand_by(vmg_ self, 1); /* add the new element, saving undo */ set_element_undo(vmg_ self, cnt, valp); /* discard the argument and gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - append elements from a collection, or append a * single non-collection element */ int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the value to append, but leave it on the stack */ vm_val_t *valp = G_stk->get(0); /* the result object is 'self' */ retval->set_obj(self); /* push a self-reference for gc protection */ G_stk->push(retval); /* get the old size */ int cnt = get_element_count(); /* get the number of items to add */ int add_cnt = valp->ll_length(vmg0_); /* expand myself to make room for the new elements */ expand_by(vmg_ self, add_cnt); /* add the new elements */ for (int i = 1 ; i <= add_cnt ; ++i) { /* get the next new element */ vm_val_t ele; valp->ll_index(vmg_ &ele, i); /* * add the new element - note that we don't need to keep undo * because we created this new element in this same operation, and * hence undoing the operation will truncate the vector to exclude * the element, and hence we don't need an old value for the * element */ set_element(cnt + i - 1, &ele); } /* discard the argument and gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - splice */ int CVmObjVector::getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the current vector size */ int old_cnt = get_element_count(); /* get the starting index, and adjust a zero or negative index */ int start_idx = CVmBif::pop_int_val(vmg0_); if (start_idx <= 0) start_idx += old_cnt + 1; /* adjust to a zero-based starting index */ --start_idx; /* make sure the starting index is in range */ if (start_idx < 0 || start_idx > old_cnt) err_throw(VMERR_INDEX_OUT_OF_RANGE);; /* get the deletion count */ int del_cnt = CVmBif::pop_int_val(vmg0_); /* adjust the deletion count to the number of elements remaining */ if (del_cnt > old_cnt - start_idx) del_cnt = old_cnt - start_idx; /* get a pointer to the first inserted item, and the number of items */ vm_val_t *ins = G_stk->get(0); int ins_cnt = argc - 2; /* the result object is 'self'; push self for gc protection */ retval->set_obj(self); G_stk->push(retval); /* * If we're doing a net deletion, contract the vector. If we're doing * a net expansion, expand it. */ if (del_cnt > ins_cnt) { /* net deletion - remove the excess of deletions over insertions */ remove_elements_undo( vmg_ self, start_idx + ins_cnt, del_cnt - ins_cnt); } else if (del_cnt < ins_cnt) { /* net insertion - add room for the excess inserted items */ insert_elements_undo( vmg_ self, start_idx + del_cnt, ins_cnt - del_cnt); } /* set the new elements */ for (int i = 0 ; i < ins_cnt ; ++i, --ins) { /* * If this element is within the old vector's size range, save * undo, since we're overwriting a pre-existing element. If it's * beyond the end of the old vector, there's no need to keep undo * since the overall undo operation will simply shrink the vector * to eliminate the slot. */ if (i < old_cnt) set_element_undo(vmg_ self, start_idx + i, ins); else set_element(start_idx + i, ins); } /* discard the arguments and gc protection */ G_stk->discard(ins_cnt + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Insert room for new elements. Inserts the given number of elements at * the given starting index. Leaves the old elements in the added slots * unchanged; the caller is responsible for setting the element values. */ void CVmObjVector::insert_elements_undo(VMG_ vm_obj_id_t self, int ins_idx, int add_cnt) { /* note the new and old sizes */ int old_cnt = get_element_count(); int new_cnt = old_cnt + add_cnt; /* create the new elements */ set_element_count_undo(vmg_ self, new_cnt); /* * Move the existing elements to their new slots. Start with the * starting index, and move it to its new position, since the starting * index is the first element that needs to be moved. Note that we * start at the top and work down, to avoid overwriting any overlapping * part of the new position block and the old position block. * * If we're inserting after the last element, there's no moving that * needs to be done. */ if (ins_idx < old_cnt) { for (int idx = old_cnt - 1 ; idx >= ins_idx ; --idx) { /* get the value at this slot */ vm_val_t ele; get_element(idx, &ele); /* calculate the new slot */ int new_idx = idx + add_cnt; /* * if the new index is within the old size range, keep undo for * the change; otherwise, no undo is necessary, since when we * undo we'll be completely dropping this new slot */ if (new_idx < old_cnt) set_element_undo(vmg_ self, new_idx, &ele); else set_element(new_idx, &ele); } } } /* * Delete a range of elements */ void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self, size_t start_idx, size_t del_cnt) { /* note the original size of the vector */ size_t cnt = get_element_count(); /* move all of the elements to their new slots, keeping undo */ for (size_t idx = start_idx ; idx < cnt ; ++idx) { /* * calculate the index of the element to replace this one - it's * past us by the number of items being deleted */ size_t move_idx = idx + del_cnt; /* * if we're moving from a valid index in the old vector, get the * element; otherwise, replace the value with nil (but still * replace the value, since we want to keep undo for its original * value) */ vm_val_t val; if (move_idx < cnt) get_element(move_idx, &val); else val.set_nil(); /* store the element at its new position */ set_element_undo(vmg_ self, idx, &val); } /* * Reduce the size, keeping undo. Note that it's important we do * this last: undo is applied in reverse order, so when applying * undo, we first want to increase the vector's size to its original * size, then we want to apply the element value restorations. */ set_element_count_undo(vmg_ self, cnt - del_cnt); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - join into a string */ int CVmObjVector::getp_join(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the generic list joiner */ vm_val_t vself; vself.set_obj(self); return CVmObjList::getp_join(vmg_ retval, &vself, 0, argc); } /* * C++ interface to join() */ void CVmObjVector::join(VMG_ vm_val_t *retval, vm_obj_id_t self, const char *sep, size_t sep_len) const { vm_val_t vself; vself.set_obj(self); return CVmObjList::join(vmg_ retval, &vself, sep, sep_len); } /* ------------------------------------------------------------------------ */ /* * Explicitly convert to a string */ const char *CVmObjVector::explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const { /* use the generic list converter */ vm_val_t vself; vself.set_obj(self); return CVmObjList::list_to_string(vmg_ new_str, &vself, radix, flags); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - generate a new vector */ int CVmObjVector::static_getp_generate(VMG_ vm_val_t *retval, uint *in_argc) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the callback function argument */ vm_val_t *func = G_stk->get(0); /* make sure it's a function; set up a header pointer if it is */ CVmFuncPtr funcdesc; if (!funcdesc.set(vmg_ func)) err_throw(VMERR_BAD_TYPE_BIF); /* get the count */ int32_t cnt = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's not negative */ if (cnt < 0) err_throw(VMERR_BAD_VAL_BIF); /* create the vector */ retval->set_obj(CVmObjVector::create(vmg_ FALSE, cnt)); CVmObjVector *vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); /* push the new vector for gc protection */ G_stk->push(retval); /* find out how many arguments the function wants */ int fargc = funcdesc.is_varargs() ? -1 : funcdesc.get_max_argc(); /* set up our recursive call descriptor */ vm_rcdesc rc(vmg_ "Vector.generate", CVmObjVector::metaclass_reg_->get_class_obj(vmg0_), PROPIDX_generate, func, argc); /* generate the elements */ for (int i = 0 ; i < cnt ; ++i) { /* push the callback arguments */ const int pushed_argc = 1; G_stk->push()->set_int(i+1); /* adjust the arguments for what the callback actually wants */ int sent_argc = fargc < 0 || fargc > pushed_argc ? pushed_argc : fargc; /* call the callback */ G_interpreter->call_func_ptr(vmg_ func, sent_argc, &rc, 0); /* discard excess arguments */ G_stk->discard(pushed_argc - sent_argc); /* add the result to the list */ vec->set_element(i, G_interpreter->get_r0()); vec->set_element_count(i+1); } /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator: get the index of the element with the minimum value */ int CVmObjVector::getp_indexOfMin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "Vector.indexOfMin", self, 32, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, -1, TRUE); } /* * Property evaluator: get the value of the element with the minimum value */ int CVmObjVector::getp_minVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the minimum value ("-1") */ vm_rcdesc rc(vmg_ "Vector.minVal", self, 33, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, -1, FALSE); } /* * Property evaluator: get the index of the element with the maximum value */ int CVmObjVector::getp_indexOfMax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "Vector.indexOfMax", self, 34, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, 1, TRUE); } /* * Property evaluator: get the value of the element with the maximum value */ int CVmObjVector::getp_maxVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* do the general calculation to select the maximum value ("+1") */ vm_rcdesc rc(vmg_ "Vector.maxVal", self, 35, G_stk->get(0), argc != 0 ? *argc : 0); return get_minmax(vmg_ self, retval, argc, &rc, 1, FALSE); } /* * General handling for minIndex, minVal, maxIndex, and maxVal. This finds * the highest/lowest value in the list, optionally mapped through a * callback function. Returns the winning element's index and value. * * 'sense' is the sense of the search: -1 for minimum, 1 for maximum. * 'want_index' is true if 'retval' should be set to the index of the * winning element, false if 'retval' should be set to the winning * element's value. */ int CVmObjVector::get_minmax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, const vm_rcdesc *rc, int sense, int want_index) { /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure the 'func' argument is valid */ const vm_val_t *cb = 0; if (argc >= 1) { cb = G_stk->get(0); if (!cb->is_func_ptr(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); } /* push 'self' for gc protection */ G_stk->push()->set_obj(self); /* if there are no elements, this is an error */ int cnt = get_element_count(); if (cnt == 0) err_throw(VMERR_BAD_VAL_BIF); /* we don't have a winner yet */ int winner_idx = 0; vm_val_t winner; /* run through the list */ for (int i = 0 ; i < cnt ; ++i) { /* get this element */ vm_val_t ele; get_element(i, &ele); /* if there's a callback, invoke it */ if (cb != 0) { /* invoke the callback with the element value as the argument */ G_stk->push(&ele); G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0); /* use the result as the comparison value */ ele = *G_interpreter->get_r0(); } /* compare it to the winner so far, and keep the lower value */ if (i == 0) { /* it's the first element, so it's the default winner so far */ winner_idx = i; winner = ele; } else { /* compare it to the winner so far */ int cmp = ele.compare_to(vmg_ &winner); /* keep it if it's the min/max so far */ if ((sense > 0 && cmp > 0) || (sense < 0 && cmp < 0)) { winner_idx = i; winner = ele; } } } /* remove gc protection and arguments */ G_stk->discard(argc + 1); /* return the winning index (note: 1-based) or value, as desired */ if (want_index) retval->set_int(winner_idx + 1); else *retval = winner; /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmcset.h0000644000175000001440000002104611370353156015203 0ustar realncusers/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmcset.h - T3 CharacterSet metaclass Function Notes Modified 06/06/01 MJRoberts - Creation */ #ifndef VMCSET_H #define VMCSET_H #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * A CharacterSet is a simple encapsulation of a pair of CCharmap * character mappings: one mapping from Unicode to a local character set, * and one mapping in the reverse direction. A CharacterSet is * parameterized on creation by the name of the mapping, using the * standard CCharmap names. * * In an image file, a CharacterSet contains simply the standard CCharmap * name of the mapping: * * UINT2 length-in-bytes *. BYTE name[] * * On creation, we will create the pair of CCharmap objects, if the name * of the mapping is valid. It is legal to create a CharacterSet with an * unknown mapping, but such a character set object cannot be used to * perform mappings. * * CharacterSet objects are constants at run-time. */ class CVmObjCharSet: public CVmObject { friend class CVmMetaclassCharSet; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* explicitly inherit our superclass handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* we can't be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *, vm_obj_id_t /*self*/) { /* * we don't reference any data and can't be converted to constant * data ourselves, so there's nothing to do here */ } /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create with the given character set name */ static vm_obj_id_t create(VMG_ int in_root_set, const char *charset_name, size_t charset_name_len); /* determine if an object is a CharacterSet */ static int is_charset(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } /* we reference nothing */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } void mark_refs(VMG_ uint /*state*/) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * Check a value for equality. We will match another byte array with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the array */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* our data are constant - we never change */ int is_changed_since_load() const { return FALSE; } /* * Get the to-local and to-unicode mappers. If the mapper isn't * available, we'll throw an UnknownCharacterSetException. */ class CCharmapToLocal *get_to_local(VMG0_) const; class CCharmapToUni *get_to_uni(VMG0_) const; protected: /* create with no initial contents */ CVmObjCharSet() { ext_ = 0; } /* create from the given character set name */ CVmObjCharSet(VMG_ const char *charset_name, size_t charset_name_len); /* allocate and initialize */ void alloc_ext(VMG_ const char *charset_name, size_t charset_name_len); /* get a pointer to my extension */ const struct vmobj_charset_ext_t *get_ext_ptr() const { return (vmobj_charset_ext_t *)ext_; } /* does the given unicode character have a round-trip mapping? */ static int is_rt_mappable(wchar_t c, class CCharmapToLocal *to_local, class CCharmapToUni *to_uni); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get the character set name */ int getp_get_name(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* determine if the mapping is known */ int getp_is_known(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * property evaluator - determine if the a character code (given as an * integer) or the characters in a string can be mapped from Unicode * to this local character set */ int getp_is_mappable(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * property evaluator - determine if the character code (given as an * integer) or the characters in a string have a round-trip mapping * from Unicode to local and back */ int getp_is_rt_mappable(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjCharSet::*func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* * Our extension structure */ struct vmobj_charset_ext_t { /* unicode-to-local mapping object */ class CCharmapToLocal *to_local; /* local-to-unicode mapping object */ class CCharmapToUni *to_uni; /* length of character set name */ size_t charset_name_len; /* name of the character set */ char charset_name[1]; }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassCharSet: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "character-set/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjCharSet(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjCharSet(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjCharSet::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCharSet::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMCSET_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjCharSet) frobtads-1.2.3/tads3/tct3unas.h0000644000175000001440000000526407627507712015463 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3UNAS.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3unas.h - TADS 3 Compiler - T3 Unassembler Function Takes a T3 byte-code stream and disassembles it to a printable display Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3UNAS_H #define TCT3UNAS_H #include "tcunas.h" /* ------------------------------------------------------------------------ */ /* * operand types, for instruction descriptions */ enum t3_oper_type_t { /* 8-bit signed integer */ T3OP_TYPE_8S, /* 8-bit unsigned integer */ T3OP_TYPE_8U, /* 16-bit signed integer */ T3OP_TYPE_16S, /* 16-bit unsigned integer */ T3OP_TYPE_16U, /* 32-bit signed integer */ T3OP_TYPE_32S, /* 32-bit unsigned integer */ T3OP_TYPE_32U, /* string offset */ T3OP_TYPE_STR, /* list offset */ T3OP_TYPE_LIST, /* absolute (32-bit) code address */ T3OP_TYPE_CODE_ABS, /* relative (16-bit) code address */ T3OP_TYPE_CODE_REL, /* object ID */ T3OP_TYPE_OBJ, /* property ID */ T3OP_TYPE_PROP, /* enum ID */ T3OP_TYPE_ENUM, /* context element type */ T3OP_TYPE_CTX_ELE, /* in-line string */ T3OP_TYPE_INLINE_STR }; /* ------------------------------------------------------------------------ */ /* * Instruction information - this structure describes one T3 byte-code * instruction. */ struct t3_instr_info_t { /* name of the instruction */ const char *nm; /* number of operands */ int op_cnt; /* * operand types (allow for a fixed upper limit to the number of * operands) */ t3_oper_type_t op_type[3]; }; /* ------------------------------------------------------------------------ */ /* * T3 Disassembler */ class CTcT3Unasm { public: /* disassemble a byte stream */ static void disasm(class CTcUnasSrc *src, class CTcUnasOut *out); /* show an exception table */ static void show_exc_table(class CTcUnasSrc *src, class CTcUnasOut *out, unsigned long base_ofs); /* get the instruction information array (read-only) */ static const t3_instr_info_t *get_instr_info() { return instr_info; } protected: /* disassemble an instruction */ static void disasm_instr(class CTcUnasSrc *src, class CTcUnasOut *out, char ch); /* instruction information array */ static t3_instr_info_t instr_info[]; }; #endif /* TCT3UNAS_H */ frobtads-1.2.3/tads3/vmnetfil.cpp0000644000175000001440000005117012006323444016054 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetfil.cpp - network file operations Function This module contains the network storage server implementation of CVmNetFile. Notes Modified 09/08/10 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnetfil.h" #include "vmnet.h" #include "vmfile.h" #include "vmdatasrc.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmglob.h" #include "vmpredef.h" #include "vmimport.h" #include "sha2.h" #include "vmhash.h" #include "vmbif.h" #include "vmtype.h" #include "vmobj.h" #include "vmtmpfil.h" #include "vmtobj.h" /* ------------------------------------------------------------------------ */ /* * Ticket generator. Generates the storage server "ticket" for a given * filename. The buffer must be at least 65 characters long. * * The ticket authenticates the game server to the storage server by * proving that the game server has a valid API key, which is the * equivalent of a password. */ class ServerTicket { public: ServerTicket(VMG_ const char *fname, int sfid) { /* * The ticket formula is as follows: * *. hash1 = SHA256(fname : gameServerID) *. ticket = SHA256(hash1 : apiKey) * * The filename must be the fully qualified filename with session * ID prefix, of the form ~SID/path. * * The ticket's purpose is to authenticate the game server by * proving that we know the API key, but without revealing the key. * A secure hash accomplishes this by generating a hash value that * can't be reversed (i.e., you can't figure the input given the * output). The double hash is for protection against chosen * plaintext attacks - these aren't a known vulnerability in * SHA256, but they've been used to attack older hash algorithms, * so we've tried to minimize the impact should SHA256 eventually * be found weaker in this area than presently thought. The idea * is that the filename is explicitly chosen by the user, and the * session ID *could* be chosen by an attacker who only wants to * get the server to generate hash codes for analysis (the storage * server issues true session IDs, so an attacker can't choose SIDs * that actually work against the server, but remember, the * attacker's goal is to analyze the hash output to discover the * secret API key). The game server ID *can't* be chosen by the * attacker, so we combine it with the two choosable plaintext * elements to come up with the first hash. The attacker *can't* * choose the output of this hash, since SHA256 is considered * secure against collision attacks (i.e., it's impossible to find * SHA256 input that produces a chosen output). The attacker will * still *know* the plaintext that's hashed with the API key, but * can't *choose* the plaintext. */ char hash1[65]; sha256_ezf(hash1, "%s:%s", fname, G_net_config->get("serverid")); sha256_ezf(ticket, "%s:%s", hash1, G_net_config->get("storage.apikey")); } /* get the ticket string */ const char *get() const { return ticket; } private: /* the ticket is an SHA256 hash (64 characters) */ char ticket[65]; }; /* * Are we in network storage server mode? */ int CVmNetFile::is_net_mode(VMG0_) { return (G_net_config != 0 && G_net_config->get("storage.domain") != 0 && G_net_config->get("storage.sessionid") != 0); } /* ------------------------------------------------------------------------ */ /* * Build the full server filename for a given path */ const char *CVmNetFile::build_server_filename( VMG_ char *dst, size_t dstsiz, const char *fname, int sfid) { /* if it's a special file, generate the server special file path */ if (sfid != 0) { t3sprintf(dst, dstsiz, "~%s/special/%d", G_net_config->get("storage.sessionid"), sfid); return dst; } /* if the filename doesn't have the session ID prefix, add it */ if (fname != 0 && fname[0] != '~') { /* build the full name - ~SID/filename */ t3sprintf(dst, dstsiz, "~%s/%s", G_net_config->get("storage.sessionid"), fname); return dst; } /* no changes are required */ return fname; } /* ------------------------------------------------------------------------ */ /* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const char *fname, int sfid, int mode, os_filetype_t typ, const char *mime_type) { /* check to see if we're in network mode */ if (is_net_mode(vmg0_)) { /* * We're in network mode, so the file is on the remote storage * server. All operations on the open file will actually be * performed on a local temporary copy. Generate a name for the * temp file. */ char tmp[OSFNMAX]; if (!os_gen_temp_filename(tmp, sizeof(tmp))) err_throw(VMERR_CREATE_FILE); /* build the full server filename */ char srvname[400]; fname = build_server_filename( vmg_ srvname, sizeof(srvname), fname, sfid); /* * If we're not truncating the file, and we're going to either read * or write the file (or both), download a copy of the file from * the server and save it into the temp file we just selected. The * truncate flag means that we're replacing any existing contents, * so there's no need to download the old file. * * If NEITHER read nor write modes are set, we don't have to fetch * the file: we're not going to access the contents, and we're not * going to re-upload the contents on close. * * It might seem strange at first glance that we have to fetch a * file in write-only mode. But we do: we need the old contents in * this case, even though we're not going to look at them locally, * because the caller might be patching a file somewhere in the * middle. This means that the rest of the file has to be kept * intact. To do this we have to download the old copy, so that * the updated copy we upload on close retains the rest of the * contents that we didn't modify. * * If we do decide to download the file, and the file doesn't * exist, this is an error unless the CREATE flag is set. The * CREATE flag means that we're meant to create a new file if * there's no existing file, so it's explicitly not an error if the * file doesn't exist. */ if ((mode & (NETF_READ | NETF_WRITE)) != 0 && (mode & NETF_TRUNC) == 0) { /* * Not in TRUNCATE mode, so we need to download the file. Open * the temp file for writing. */ osfildef *fp = osfoprwtb(tmp, typ); if (fp == 0) err_throw(VMERR_CREATE_FILE); /* get the storage server "ticket" for the file */ ServerTicket ticket(vmg_ fname, sfid); /* build the request URL */ char *url = t3sprintf_alloc( "%sgetfile?file=%P&ticket=%P", G_net_config->get("storage.rootpath", "/"), fname, ticket.get()); /* set up a file stream writer on the temp file */ CVmFileSource reply(fp); /* download the file from the server into the temp file */ char *headers = 0; int hstat = OS_HttpClient::request( 0, G_net_config->get("storage.domain"), 80, "GET", url, 0, 0, 0, &reply, &headers, 0, 0); /* done with the URL */ t3free(url); /* get the reply status */ char *stat = vmnet_get_storagesrv_stat( vmg_ hstat, &reply, headers); /* done with the headers */ delete headers; /* * If the reply is "FileNotFound", and the CREATE flag was set * in the request, this counts as success - the caller wishes * to create the file if it doesn't already exist, so it's not * an error if it doesn't exist. In this case simply replace * the status code with "OK". */ if (memcmp(stat, "FileNotFound ", 13) == 0) strcpy(stat, "OK "); /* on failure, delete the temp file */ if (memcmp(stat, "OK ", 3) != 0) osfdel(tmp); /* check the status and throw an error on failure */ vmnet_check_storagesrv_stat(vmg_ stat); } /* * Success. Return a file info structure with the temporary file * as the local file name, and the caller's filename as the server * filename. */ return new CVmNetFile(tmp, sfid, fname, mode, typ, mime_type); } else { /* * Local mode - the given file is our local file. Don't bother * storing the MIME type for these, since we don't use this with * the local file system APIs. */ return new CVmNetFile(fname, sfid, 0, mode, typ, 0); } } /* ------------------------------------------------------------------------ */ /* * Close a network file */ void CVmNetFile::close(VMG0_) { /* if there's no network file, it's a local file */ if (srvfname == 0 || !is_net_mode(vmg0_)) { int err = 0; /* if we opened the file in "create" mode, set the OS file type */ if ((mode & NETF_CREATE) != 0) os_settype(lclfname, typ); /* if we opened the file in "delete" mode, delete the file */ if ((mode & NETF_DELETE) != 0 && osfdel(lclfname)) err = VMERR_DELETE_FILE; /* if the file spec was a TadsObject object, call its closeFile */ if (filespec != VM_INVALID_OBJ && CVmObjTads::is_tadsobj_obj(vmg_ filespec) && G_predef->filespec_closeFile != VM_INVALID_PROP) { /* set up an object value for the file spec */ vm_val_t fs; fs.set_obj(filespec); /* call closeFile */ vm_rcdesc rc("System.closeFile"); G_interpreter->get_prop( vmg_ 0, &fs, G_predef->filespec_closeFile, &fs, 0, &rc); } /* delete self */ delete this; /* if an error occurred, throw the error */ if (err != 0) err_throw(err); /* done */ return; } /* * presume we won't perform a network operation, so there'll be no * server status code generated */ char *stat = 0; /* * We have a network file. * * If we opened the file in DELETE mode, delete the server file. Note * that we check this before checking WRITE mode, since there's no * point in sending the updated contents to the server if we're just * going to turn around and delete the file anyway. * * Otherwise, if we opened the file with WRITE access, send the updated * version of the temp file back to the server. */ if ((mode & NETF_DELETE) != 0) { /* get the ticket for deleting the file */ ServerTicket ticket(vmg_ srvfname, sfid); /* generate the URL */ char *url = t3sprintf_alloc( "%sdelfile?file=%P&ticket=%P", G_net_config->get("storage.rootpath", "/"), srvfname, ticket.get()); /* send the GET request */ char *headers = 0; CVmMemorySource rstr(1024); int hstat = OS_HttpClient::request( 0, G_net_config->get("storage.domain"), 80, "GET", url, 0, 0, 0, &rstr, &headers, 0, 0); /* done with the URL */ t3free(url); /* get the reply code from the server */ stat = vmnet_get_storagesrv_stat(vmg_ hstat, &rstr, headers); /* done with the headers */ if (headers != 0) delete [] headers; } else if ((mode & NETF_WRITE) != 0) { /* * open the local temp file for reading - if we can't, throw a * "close file" error, since this is equivalent to an error * flushing the write buffer to disk on closing a regular file */ osfildef *fp = osfoprb(lclfname, typ); if (fp == 0) err_throw(VMERR_CLOSE_FILE); /* set up a stream reader on the temp file */ CVmFileSource *st = new CVmFileSource(fp); /* get the ticket for writing this file */ ServerTicket ticket(vmg_ srvfname, sfid); /* build the POST data */ OS_HttpPayload *post = new OS_HttpPayload(); post->add("file", srvfname); post->add("sid", G_net_config->get("storage.sessionid")); post->add("ticket", ticket.get()); post->add("contents", "noname", mime_type, st); /* generate the URL */ char *url = t3sprintf_alloc( "%sputfile", G_net_config->get("storage.rootpath", "/")); /* send the POST request */ CVmMemorySource rstr(128); char *headers = 0; int hstat = OS_HttpClient::request( 0, G_net_config->get("storage.domain"), 80, "POST", url, 0, 0, post, &rstr, &headers, 0, 0); /* done with the URL and the POST parameters */ t3free(url); delete post; /* get the reply code from the server */ stat = vmnet_get_storagesrv_stat(vmg_ hstat, &rstr, headers); /* done with the headers */ if (headers != 0) delete [] headers; } /* delete the temporary file */ osfdel(lclfname); lclfname = 0; /* done with the file structure */ delete this; /* check the status code from the server, if any */ if (stat != 0) vmnet_check_storagesrv_stat(vmg_ stat); } /* ------------------------------------------------------------------------ */ /* * Do a Read or Write check on a server file. */ static int server_file_check(VMG_ const char *srvfname, const char *mode) { /* get the server ticket */ ServerTicket ticket(vmg_ srvfname, 0); /* build the request URL to test file existence */ char *url = t3sprintf_alloc( "%stestfile?file=%P&ticket=%P&mode=%s", G_net_config->get("storage.rootpath", "/"), srvfname, ticket.get(), mode); /* send the request */ CVmMemorySource rstr(128); int hstat = OS_HttpClient::request( 0, G_net_config->get("storage.domain"), 80, "GET", url, 0, 0, 0, &rstr, 0, 0, 0); /* done with the URL */ t3free(url); /* check the result */ char reply[128]; return (hstat == 200 && rstr.readc(reply, sizeof(reply)) >= 1 && reply[0] == 'Y'); } /* ------------------------------------------------------------------------ */ /* * Check to see if a file exists */ int CVmNetFile::exists(VMG_ const char *fname, int sfid) { /* create a no-op descriptor for the file */ CVmNetFile *nf = open(vmg_ fname, sfid, 0, OSFTUNK, 0); /* * if it's a network file, check with the server; otherwise check the * local file system */ int ret = (nf->is_net_file() ? server_file_check(vmg_ nf->srvfname, "R") : !osfacc(nf->lclfname)); /* done with the descriptor */ nf->abandon(vmg0_); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Check to see if a file is writable */ int CVmNetFile::can_write(VMG_ const char *fname, int sfid) { /* create a no-op descriptor for the file */ CVmNetFile *nf = open(vmg_ fname, sfid, 0, OSFTUNK, 0); /* * if it's a network file, check with the server; otherwise check the * local file system */ int ret = (nf->is_net_file() ? server_file_check(vmg_ nf->srvfname, "W") : can_write_local(nf->lclfname)); /* done with the descriptor */ nf->abandon(vmg0_); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Get the file mode */ int CVmNetFile::get_file_mode( VMG_ unsigned long *mode, unsigned long *attrs, int follow_links) { /* * if it's a network file, check with the server to see if the file * exists; otherwise check the local file system */ int ret; if (is_net_file()) { /* * network file - if the file exists, it's simply a regular file, * since that's all we support */ ret = server_file_check(vmg_ srvfname, "R"); *mode = (ret ? OSFMODE_FILE : 0); } else { /* local file - get the local file mode */ ret = osfmode(lclfname, follow_links, mode, attrs); } /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Get file status */ int CVmNetFile::get_file_stat(VMG_ os_file_stat_t *stat, int follow_links) { if (is_net_file()) { /* not supported for network files */ err_throw(VMERR_NET_FILE_NOIMPL);; AFTER_ERR_THROW(return FALSE); } else { /* local file - get the local file status */ return os_file_stat(lclfname, follow_links, stat); } } /* ------------------------------------------------------------------------ */ /* * Resolve a symbolic link */ int CVmNetFile::resolve_symlink(VMG_ char *target, size_t target_size) { if (is_net_file()) { /* * the storage server doesn't support symbolic links, so there's * never anything to resolve */ if (target_size != 0) target[0] = '\0'; return FALSE; } else { /* local file - get the local file status */ return os_resolve_symlink(lclfname, target, target_size); } } /* ------------------------------------------------------------------------ */ /* * Rename a file */ void CVmNetFile::rename_to(VMG_ CVmNetFile *newname) { if (is_net_file()) { /* the storage server doesn't support directory creation */ err_throw(VMERR_NET_FILE_NOIMPL);; } else { /* create the file locally */ rename_to_local(vmg_ newname); } } /* ------------------------------------------------------------------------ */ /* * Create a directory */ void CVmNetFile::mkdir(VMG_ int create_parents) { if (is_net_file()) { /* the storage server doesn't support directory creation */ err_throw(VMERR_NET_FILE_NOIMPL);; } else { /* create the file locally */ mkdir_local(vmg_ create_parents); } } /* ------------------------------------------------------------------------ */ /* * Remove a directory */ void CVmNetFile::rmdir(VMG_ int remove_contents) { if (is_net_file()) { /* the storage server doesn't support directory removal */ err_throw(VMERR_NET_FILE_NOIMPL);; } else { /* create the file locally */ rmdir_local(vmg_ remove_contents); } } /* ------------------------------------------------------------------------ */ /* * Read a directory */ int CVmNetFile::readdir(VMG_ const char *nominal_path, vm_val_t *retval) { if (is_net_file()) { /* the storage server doesn't support directory listings */ err_throw(VMERR_NET_FILE_NOIMPL);; AFTER_ERR_THROW(return FALSE); } else { /* read the local directory */ return readdir_local(vmg_ nominal_path, retval, 0, 0, FALSE); } } /* * Read a directory, enumerating contents through a callback */ int CVmNetFile::readdir_cb(VMG_ const char *nominal_path, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive) { if (is_net_file()) { /* the storage server doesn't support directory listings */ err_throw(VMERR_NET_FILE_NOIMPL);; AFTER_ERR_THROW(return FALSE); } else { /* read the local directory */ return readdir_local(vmg_ nominal_path, 0, rc, cb, recursive); } } frobtads-1.2.3/tads3/vmiter.h0000644000175000001440000002707011470777350015222 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmiter.h - Iterator metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #ifndef VMITER_H #define VMITER_H #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Base Iterator class */ class CVmObjIter: public CVmObject { friend class CVmMetaclassIter; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* set a property */ void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* cannot set iterator properties */ err_throw(VMERR_INVALID_SETPROP); } /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); protected: /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = 0; /* function table */ static int (CVmObjIter::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Indexed Iterator subclass. An indexed iterator works with arrays, * lists, and other collections that can be accessed via an integer * index. */ /* * The extension data for an indexed iterator consists of a reference to * the associated indexed collection, the index value of the next item * to be retrieved, and the first and last valid index values: Note that * the collection value can be an object ID or a constant VM_LIST value. * * DATAHOLDER collection_value *. UINT4 cur_index *. UINT4 first_valid *. UINT4 last_valid *. UINT4 flags * * The flag values are: * * VMOBJITERIDX_UNDO - we've saved undo for this savepoint. If this is * set, we won't save additional undo for the same savepoint. */ /* total extension size */ #define VMOBJITERIDX_EXT_SIZE (VMB_DATAHOLDER + 16) /* * flag bits */ /* we've saved undo for the current savepoint */ #define VMOBJITERIDX_UNDO 0x0001 /* * indexed iterator class */ class CVmObjIterIdx: public CVmObjIter { friend class CVmMetaclassIterIdx; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjIter::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* * Create an indexed iterator. This method is to be called by a * list, array, or other indexed collection object to create an * iterator for its value. */ static vm_obj_id_t create_for_coll(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * notify of a new savepoint - clear the 'undo' flag, since we * cannot have created any undo information yet for the new * savepoint */ void notify_new_savept() { set_flags(get_flags() & ~VMOBJITERIDX_UNDO); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint state); /* there are no references in our undo stream */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* restore to image file state */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* * determine if the object has been changed since it was loaded - * assume we have, since saving and reloading are very cheap */ int is_changed_since_load() const { return TRUE; } /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* get the next iteration item */ virtual int iter_next(VMG_ vm_obj_id_t self, vm_val_t *val); protected: /* create */ CVmObjIterIdx() { ext_ = 0; } /* create */ CVmObjIterIdx(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index); /* get the value from the collection for a given index */ void get_indexed_val(VMG_ long idx, vm_val_t *retval); /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get my collection value */ void get_coll_val(vm_val_t *val) { vmb_get_dh(ext_, val); } /* get/set the current index (without saving undo) */ long get_cur_index() const { return t3rp4u(ext_ + VMB_DATAHOLDER); } void set_cur_index_no_undo(long idx) { oswp4(ext_ + VMB_DATAHOLDER, idx); } /* set the index value, saving undo if necessary */ void set_cur_index(VMG_ vm_obj_id_t self, long idx); /* get my first/last valid index values */ long get_first_valid() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 4); } long get_last_valid() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 8); } /* set my first/last valid index values - for construction only */ void set_first_valid(long idx) const { oswp4(ext_ + VMB_DATAHOLDER + 4, idx); } void set_last_valid(long idx) const { oswp4(ext_ + VMB_DATAHOLDER + 8, idx); } /* get/set the flags */ unsigned long get_flags() const { return t3rp4u(ext_ + VMB_DATAHOLDER + 12); } void set_flags(unsigned long flags) const { oswp4(ext_ + VMB_DATAHOLDER + 12, flags); } }; /* ------------------------------------------------------------------------ */ /* * Registration table object for the base iterator class */ class CVmMetaclassIter: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "iterator/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { err_throw(VMERR_BAD_STATIC_NEW); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; /* * Registration table object for indexed iterators */ class CVmMetaclassIterIdx: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "indexed-iterator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterIdx(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterIdx(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIterIdx::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMITER_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjIter) VM_REGISTER_METACLASS(CVmObjIterIdx) frobtads-1.2.3/tads3/vmdict.cpp0000644000175000001440000023155112145504453015526 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdict.cpp - dictionary metaclass Function Notes Modified 01/24/00 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmdict.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmhash.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmstrcmp.h" #include "vmpredef.h" /* ------------------------------------------------------------------------ */ /* * Dictionary undo record. Each time we change the dictionary, we track * the change with one of these records - we attach this record to the * standard undo record via the 'ptrval' field. */ struct dict_undo_rec { /* action type */ enum dict_undo_action action; /* object value stored in dictionary record */ vm_obj_id_t obj; /* vocabulary property for word association */ vm_prop_id_t prop; /* length of the word */ size_t len; /* text of the word */ char txt[1]; }; /* ------------------------------------------------------------------------ */ /* * Generic comparator-based hash */ class CVmHashFuncComparator: public CVmHashFunc { public: CVmHashFuncComparator(VMG_ vm_obj_id_t obj) { globals_ = VMGLOB_ADDR; comparator_ = obj; } /* compute the hash value */ unsigned int compute_hash(const char *str, size_t len) const { vm_val_t val; vm_val_t cobj_val; VMGLOB_PTR(globals_); /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.calcHash"); /* create a string object to represent the argument, and push it */ G_stk->push()->set_obj(CVmObjString::create(vmg_ FALSE, str, len)); /* invoke the calcHash method */ cobj_val.set_obj(comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop, &cobj_val, 1, &rc); /* retrieve the result from R0 */ val = *G_interpreter->get_r0(); /* we need an integer value from the method */ if (val.typ == VM_INT) { /* return the hash value from the comparator */ return val.val.intval; } else { /* no or invalid hash value - throw an error */ err_throw(VMERR_BAD_TYPE_BIF); AFTER_ERR_THROW(return FALSE;) } } private: /* my comparator object */ vm_obj_id_t comparator_; /* system globals */ vm_globals *globals_; }; /* * StringComparator-based hash */ class CVmHashFuncStrComp: public CVmHashFunc { public: CVmHashFuncStrComp(CVmObjStrComp *comp) { comparator_ = comp; } /* compute the hash value */ unsigned int compute_hash(const char *str, size_t len) const { /* ask the StringComparator to do the work */ return comparator_->calc_str_hash(str, len); } private: /* my StringComparator object */ CVmObjStrComp *comparator_; }; /* ------------------------------------------------------------------------ */ /* * dictionary object statics */ /* metaclass registration object */ static CVmMetaclassDict metaclass_reg_obj; CVmMetaclass *CVmObjDict::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjDict:: *CVmObjDict::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjDict::getp_undef, /* 0 */ &CVmObjDict::getp_set_comparator, /* 1 */ &CVmObjDict::getp_find, /* 2 */ &CVmObjDict::getp_add, /* 3 */ &CVmObjDict::getp_del, /* 4 */ &CVmObjDict::getp_is_defined, /* 5 */ &CVmObjDict::getp_for_each_word, /* 6 */ &CVmObjDict::getp_correct /* 7 */ }; /* ------------------------------------------------------------------------ */ /* * Dictionary object implementation */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjDict::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; vm_obj_id_t comp; CVmObjDict *obj; /* check arguments */ if (argc != 0 && argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the Comparator, but leave it on the stack as gc protection */ if (argc == 0 || G_stk->get(0)->typ == VM_NIL) comp = VM_INVALID_OBJ; else if (G_stk->get(0)->typ == VM_OBJ) comp = G_stk->get(0)->val.obj; else err_throw(VMERR_BAD_TYPE_BIF); /* * allocate the object ID - this type of construction never creates * a root object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* create the object */ obj = new (vmg_ id) CVmObjDict(vmg0_); /* set the comparator */ obj->set_comparator(vmg_ comp); /* build an empty initial hash table */ obj->create_hash_table(vmg0_); /* * mark the object as modified since image load, since it doesn't * come from the image file at all */ obj->get_ext()->modified_ = TRUE; /* discard our gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * constructor */ CVmObjDict::CVmObjDict(VMG0_) { /* allocate our extension structure from the variable heap */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(sizeof(vm_dict_ext), this); /* we have no image data yet */ get_ext()->image_data_ = 0; get_ext()->image_data_size_ = 0; /* no hash table yet */ get_ext()->hashtab_ = 0; /* no Trie yet */ get_ext()->trie_ = 0; /* no non-image entries yet */ get_ext()->modified_ = FALSE; /* no comparator yet */ get_ext()->comparator_ = VM_INVALID_OBJ; set_comparator_type(vmg_ VM_INVALID_OBJ); } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjDict::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data */ if (ext_ != 0) { /* free our hash table */ if (get_ext()->hashtab_ != 0) delete get_ext()->hashtab_; /* free our Trie */ if (get_ext()->trie_ != 0) delete get_ext()->trie_; /* free the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Set the comparator object */ void CVmObjDict::set_comparator(VMG_ vm_obj_id_t obj) { /* if this is the same as the current comparator, there's nothing to do */ if (obj == get_ext()->comparator_) return; /* remember the new comparator */ get_ext()->comparator_ = obj; /* figure out what kind of compare functions to use */ set_comparator_type(vmg_ obj); /* rebuild the hash tale */ if (get_ext()->hashtab_ != 0) create_hash_table(vmg0_); } /* * Set the string-compare function based on the given comparator. */ void CVmObjDict::set_comparator_type(VMG_ vm_obj_id_t obj) { /* check what kind of object we have */ if (obj == VM_INVALID_OBJ) { /* there's no comparator at all, so simply use an exact comparison */ get_ext()->comparator_type_ = VMDICT_COMP_NONE; } else if (CVmObjStrComp::is_strcmp_obj(vmg_ obj)) { /* it's a StringComparator */ get_ext()->comparator_type_ = VMDICT_COMP_STRCOMP; } else { /* it's a generic comparator object */ get_ext()->comparator_type_ = VMDICT_COMP_GENERIC; } /* remember the object pointer, if there is one */ get_ext()->comparator_obj_ = (obj == VM_INVALID_OBJ ? 0 : vm_objp(vmg_ obj)); } /* ------------------------------------------------------------------------ */ /* * Rebuild the hash table. If there's an existing hash table, we'll move * the entries from the old table to the new table, and delete the old * table. */ void CVmObjDict::create_hash_table(VMG0_) { CVmHashTable *new_tab; CVmHashFunc *hash_func; /* * Create our hash function. If we have a comparator object, base the * hash function on the comparator; use a special hash function if we * specifically have a StringComparator, since we can call these * directly for better efficiency. If we have no comparator, create a * generic exact string match hash function. */ if (get_ext()->comparator_ != VM_INVALID_OBJ) { /* * use our special StringComparator hash function if possible; * otherwise, use a generic comparator hash function */ if (CVmObjStrComp::is_strcmp_obj(vmg_ get_ext()->comparator_)) { /* create a StringComparator hash function */ hash_func = new CVmHashFuncStrComp( (CVmObjStrComp *)vm_objp(vmg_ get_ext()->comparator_)); } else { /* create a generic comparator hash function */ hash_func = new CVmHashFuncComparator( vmg_ get_ext()->comparator_); } } else { /* create a simple exact-match hash */ hash_func = new CVmHashFuncCS(); } /* create the hash table */ new_tab = new CVmHashTable(256, hash_func, TRUE); /* if we had a previous hash table, move its contents to the new table */ if (get_ext()->hashtab_ != 0) { /* copy the old hash table to the new one */ get_ext()->hashtab_->move_entries_to(new_tab); /* delete the old hash table */ delete get_ext()->hashtab_; } /* if we had a previous trie, get rid of it */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* store the new hash table in the extension */ get_ext()->hashtab_ = new_tab; } /* ------------------------------------------------------------------------ */ /* * Check for a match between two strings, using the current comparator. * Returns true if the strings match, false if not; fills in *result_val * with the actual result value from the comparator's matchValues() * function. */ int CVmObjDict::match_strings(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen, const char *refstr, size_t reflen, vm_val_t *result_val) { vm_val_t cobj_val; /* check what kind of comparator we have */ switch(get_ext()->comparator_type_) { case VMDICT_COMP_NONE: /* no comparator - compare the strings byte-for-byte */ if (vallen == reflen && memcmp(valstr, refstr, vallen) == 0) { /* match - return true */ result_val->set_int(1); return TRUE; } else { /* no match - return false */ result_val->set_int(0); return FALSE; } case VMDICT_COMP_STRCOMP: /* match the value directly with the StringComparator */ result_val->set_int( ((CVmObjStrComp *)get_ext()->comparator_obj_)->match_strings( valstr, vallen, refstr, reflen)); /* we matched if the result was non-zero */ return result_val->val.intval; case VMDICT_COMP_GENERIC: /* 2nd param: push the reference string, as a string object */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, refstr, reflen)); /* 1st param: push the value string */ if (valstrval != 0) { /* push the value string as given */ G_stk->push(valstrval); } else { /* no value string was given - create one and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, valstr, vallen)); } { /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.matchValues"); /* call the comparator's matchValues method */ cobj_val.set_obj(get_ext()->comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->match_values_prop, &cobj_val, 2, &rc); } /* get the result from R0 */ *result_val = *G_interpreter->get_r0(); /* we matched if the result isn't 0 or nil */ return !(result_val->typ == VM_NIL || (result_val->typ == VM_INT && result_val->val.intval == 0)); } /* we should never get here, but just in case... */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Calculate a hash value with the current comparator */ unsigned int CVmObjDict::calc_str_hash(VMG_ const vm_val_t *valstrval, const char *valstr, size_t vallen) { vm_val_t result; vm_val_t cobj_val; /* check what kind of comparator we have */ switch(get_ext()->comparator_type_) { case VMDICT_COMP_NONE: /* no comparator - use the hash table's basic hash calculation */ return get_ext()->hashtab_->compute_hash(valstr, vallen); case VMDICT_COMP_STRCOMP: /* calculate the hash directly with the StringComparator */ return ((CVmObjStrComp *)get_ext()->comparator_obj_)->calc_str_hash( valstr, vallen); case VMDICT_COMP_GENERIC: /* push the value string */ if (valstrval != 0) { /* push the value string as given */ G_stk->push(valstrval); } else { /* no value string was given - create one and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, valstr, vallen)); } { /* set up a recursive call context */ vm_rcdesc rc; rc.init_ret(vmg_ "HashComparator.calcHash"); /* call the comparator object's calcHash method */ cobj_val.set_obj(get_ext()->comparator_); G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop, &cobj_val, 1, &rc); } /* get the result from R0 */ result = *G_interpreter->get_r0(); /* make sure it's an integer */ if (result.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); /* return the result */ return (unsigned int)result.val.intval; } /* we should never get here, but just in case... */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set a property. We have no settable properties, so simply signal an * error indicating that the set-prop call is invalid. */ void CVmObjDict::set_prop(VMG_ CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjDict::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * evaluate property: set the comparator */ int CVmObjDict::getp_set_comparator(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1); vm_obj_id_t comp; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* get the comparator object */ switch(G_stk->get(0)->typ) { case VM_NIL: comp = VM_INVALID_OBJ; break; case VM_OBJ: comp = G_stk->get(0)->val.obj; break; default: err_throw(VMERR_BAD_TYPE_BIF); } /* if there's a global undo object, add undo for the change */ if (G_undo != 0) { dict_undo_rec *undo_rec; /* create an undo record with the original comparator */ undo_rec = alloc_undo_rec(DICT_UNDO_COMPARATOR, 0, 0); undo_rec->obj = get_ext()->comparator_; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* set our new comparator object */ set_comparator(vmg_ comp); /* mark the object as modified since load */ get_ext()->modified_ = TRUE; /* discard arguments */ G_stk->discard(); /* no return value - just return nil */ val->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * result entry for find_ctx */ struct find_entry { /* matching object */ vm_obj_id_t obj; /* match result code */ vm_val_t match_result; }; /* * page of results for find_ctx */ const int FIND_ENTRIES_PER_PAGE = 32; struct find_entry_page { /* next in list of pages */ find_entry_page *nxt; /* array of entries */ find_entry entry[FIND_ENTRIES_PER_PAGE]; }; /* * enumeration callback context for getp_find */ struct find_ctx { /* set up a context that adds results to our internal list */ find_ctx(VMG_ CVmObjDict *dict_obj, const vm_val_t *val, const char *str, size_t len, vm_prop_id_t prop) { /* remember the dictionary object we're searching */ dict = dict_obj; /* remember the globals */ globals = VMGLOB_ADDR; /* keep a pointer to the search string value */ this->strval = val; this->strp = str; this->strl = len; /* remember the property */ voc_prop = prop; /* no results yet */ results_head = 0; results_tail = 0; result_cnt = 0; /* * set the free pointer to the end of the non-existent current * page, so we know we have to allocate a new page before adding * the first entry */ result_free = FIND_ENTRIES_PER_PAGE; } ~find_ctx() { /* free our list of results pages */ while (results_head != 0) { /* remember the current page */ find_entry_page *cur = results_head; /* move on to the next page */ results_head = results_head->nxt; /* delete the current page */ t3free(cur); } } /* add a result, expanding the result array if necessary */ void add_result(vm_obj_id_t obj, const vm_val_t *match_result) { /* * if we have no pages, or we're out of room on the current page, * allocate a new page */ if (result_free == FIND_ENTRIES_PER_PAGE) { find_entry_page *pg; /* allocate a new page */ pg = (find_entry_page *)t3malloc(sizeof(find_entry_page)); /* link it in at the end of the list */ pg->nxt = 0; if (results_tail != 0) results_tail->nxt = pg; else results_head = pg; results_tail = pg; /* reset the free pointer to the start of the new page */ result_free = 0; } /* add this result */ results_tail->entry[result_free].obj = obj; results_tail->entry[result_free].match_result = *match_result; ++result_free; ++result_cnt; } /* make our results into a list */ vm_obj_id_t results_to_list(VMG0_) { vm_obj_id_t lst_id; CVmObjList *lst; find_entry_page *pg; int idx; /* * Allocate a list of the appropriate size. We need two list * entries per result, since we need to store the object and the * match result code for each result. */ lst_id = CVmObjList::create(vmg_ FALSE, result_cnt * 2); lst = (CVmObjList *)vm_objp(vmg_ lst_id); /* add all entries */ for (idx = 0, pg = results_head ; idx < result_cnt ; pg = pg->nxt) { find_entry *ep; int pg_idx; /* add each entry on this page */ for (ep = pg->entry, pg_idx = 0 ; idx < result_cnt && pg_idx < FIND_ENTRIES_PER_PAGE ; ++idx, ++pg_idx, ++ep) { vm_val_t ele; /* add this entry to the list */ ele.set_obj(ep->obj); lst->cons_set_element(idx*2, &ele); lst->cons_set_element(idx*2 + 1, &ep->match_result); } } /* return the list */ return lst_id; } /* head/tail of list of result pages */ find_entry_page *results_head; find_entry_page *results_tail; /* next available entry on current results page */ int result_free; /* total number of result */ int result_cnt; /* the string to find */ const vm_val_t *strval; const char *strp; size_t strl; /* vocabulary property value to match */ vm_prop_id_t voc_prop; /* VM globals */ vm_globals *globals; /* dictionary object we're searching */ CVmObjDict *dict; }; /* * Callback for getp_find hash match enumeration */ void CVmObjDict::find_cb(void *ctx0, CVmHashEntry *entry0) { find_ctx *ctx = (find_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* if this entry matches the search string, process it */ if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { vm_dict_entry *cur; /* process the items under this entry */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* * If it matches the target property, it's a hit. If we're * searching for any property at all (indicated by the * context property being set to VM_INVALID_PROP), count * everything. */ if (cur->prop_ == ctx->voc_prop || ctx->voc_prop == VM_INVALID_PROP) { /* process the result */ ctx->add_result(cur->obj_, &match_val); } } } } /* ------------------------------------------------------------------------ */ /* * property evaluation - look up a word */ int CVmObjDict::getp_find(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1, 1); uint orig_argc = (argc != 0 ? *argc : 0); vm_val_t *arg0; vm_val_t *arg1; const char *strp; size_t strl; vm_prop_id_t voc_prop; unsigned int hash; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* * make sure the first argument is a string, but leave it on the stack * for gc protection */ arg0 = G_stk->get(0); if ((strp = arg0->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* scan and skip the string's length prefix */ strl = vmb_get_len(strp); strp += VMB_LEN; /* * get the property ID, which can be omitted or specified as nil to * indicate any property */ if (orig_argc < 2) voc_prop = VM_INVALID_PROP; else if ((arg1 = G_stk->get(1))->typ == VM_NIL) voc_prop = VM_INVALID_PROP; else if (arg1->typ == VM_PROP) voc_prop = arg1->val.prop; else err_throw(VMERR_PROPPTR_VAL_REQD); /* calculate the hash value */ hash = calc_str_hash(vmg_ arg0, strp, strl); /* enumerate everything that matches the string's hash code */ find_ctx ctx(vmg_ this, arg0, strp, strl, voc_prop); get_ext()->hashtab_->enum_hash_matches(hash, &find_cb, &ctx); /* build a list out of the results, and return the list */ val->set_obj(ctx.results_to_list(vmg0_)); /* discard arguments */ G_stk->discard(orig_argc); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluation - add a word */ int CVmObjDict::getp_add(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { vm_obj_id_t obj; vm_val_t str_val; vm_prop_id_t voc_prop; CVmObject *objp; int lstcnt; const char *str; static CVmNativeCodeDesc desc(3); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* pop the object, word string, and property ID arguments */ obj = CVmBif::pop_obj_val(vmg0_); G_stk->pop(&str_val); voc_prop = CVmBif::pop_propid_val(vmg0_); /* ensure that the object value isn't a string or list */ objp = vm_objp(vmg_ obj); if (objp->get_as_list() != 0 || objp->get_as_string(vmg0_) != 0) err_throw(VMERR_OBJ_VAL_REQD); /* if the string argument is a list, add each word from the list */ if (str_val.is_listlike(vmg0_) && (lstcnt = str_val.ll_length(vmg0_)) >= 0) { /* iterate over the list, using 1-based indices */ for (int idx = 1 ; idx <= lstcnt ; ++idx) { /* get the next value from the list */ vm_val_t ele_val; str_val.ll_index(vmg_ &ele_val, idx); /* get the next string value */ if ((str = ele_val.get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* set this string */ add_word(vmg_ self, str, obj, voc_prop); } } else if ((str = str_val.get_as_string(vmg0_)) != 0) { /* add the string */ add_word(vmg_ self, str, obj, voc_prop); } else { /* string value required */ err_throw(VMERR_STRING_VAL_REQD); } /* there's no return value */ val->set_nil(); /* success */ return TRUE; } /* * Service routine for getp_add() - add a single string */ void CVmObjDict::add_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { /* add the entry */ int added = add_hash_entry(vmg_ str, len, TRUE, obj, voc_prop, FALSE); /* * if there's a global undo object, and we actually added a new * entry, add undo for the change */ if (added && G_undo != 0) { dict_undo_rec *undo_rec; /* create the undo record */ undo_rec = alloc_undo_rec(DICT_UNDO_ADD, str, len); undo_rec->obj = obj; undo_rec->prop = voc_prop; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* mark the object as modified since load */ get_ext()->modified_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluation - delete a word */ int CVmObjDict::getp_del(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { vm_obj_id_t obj; vm_val_t str_val; vm_prop_id_t voc_prop; int lstcnt; const char *str; static CVmNativeCodeDesc desc(3); /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* pop the object, word string, and property ID arguments */ obj = CVmBif::pop_obj_val(vmg0_); G_stk->pop(&str_val); voc_prop = CVmBif::pop_propid_val(vmg0_); /* if the string argument is a list, add each word from the list */ if (str_val.is_listlike(vmg0_) && (lstcnt = str_val.ll_length(vmg0_)) >= 0) { /* iterate over the list, using 1-based indices */ for (int idx = 1 ; idx <= lstcnt ; ++idx) { /* get the next value from the list */ vm_val_t ele_val; str_val.ll_index(vmg_ &ele_val, idx); /* get the next string value */ if ((str = ele_val.get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* remove this string */ del_word(vmg_ self, str, obj, voc_prop); } } else if ((str = str_val.get_as_string(vmg0_)) != 0) { /* remove the string */ del_word(vmg_ self, str, obj, voc_prop); } else { /* string value required */ err_throw(VMERR_STRING_VAL_REQD); } /* there's no return value */ val->set_nil(); /* success */ return TRUE; } /* * Service routine for getp_del - delete a string value */ void CVmObjDict::del_word(VMG_ vm_obj_id_t self, const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { /* delete this entry */ int deleted = del_hash_entry(vmg_ str, len, obj, voc_prop); /* * if there's a global undo object, and we actually deleted an * entry, add undo for the change */ if (deleted && G_undo != 0) { dict_undo_rec *undo_rec; /* create the undo record */ undo_rec = alloc_undo_rec(DICT_UNDO_DEL, str, len); undo_rec->obj = obj; undo_rec->prop = voc_prop; /* add the undo record */ add_undo_rec(vmg_ self, undo_rec); } /* mark the object as modified since load */ get_ext()->modified_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * callback context for is_defined enumeration */ struct isdef_ctx { isdef_ctx(VMG_ CVmObjDict *dict_obj, vm_val_t *filter, const vm_val_t *val, const char *str, size_t len) { /* remember the VM globals and dictionary object */ globals = VMGLOB_ADDR; dict = dict_obj; /* we haven't yet found a match */ found = FALSE; /* remember the string */ this->strval = *val; this->strp = str; this->strl = len; /* remember the filter function */ filter_func = filter; } /* flag: we've found a match */ int found; /* string value we're matching */ vm_val_t strval; const char *strp; size_t strl; /* our filter callback function, if any */ const vm_val_t *filter_func; /* VM globals */ vm_globals *globals; /* dictionary we're searching */ CVmObjDict *dict; /* recursive native caller context */ vm_rcdesc rc; }; /* * Callback for getp_is_defined hash match enumeration */ void CVmObjDict::isdef_cb(void *ctx0, CVmHashEntry *entry0) { isdef_ctx *ctx = (isdef_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* * if we've already found a match, don't bother checking anything else * - it could be non-trivial to do the string match, so we can save * some work by skipping that if we've already found a match * previously */ if (ctx->found) return; /* * if this entry matches the search string, and it has at least one * defined object/property associated with it, count it as a match */ if (entry->get_head() != 0 && ctx->dict->match_strings(vmg_ &ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { /* if there's a filter function to invoke, check with it */ if (ctx->filter_func->typ != VM_NIL) { vm_val_t *valp; /* push the match value parameter */ G_stk->push(&match_val); /* call the filter callback */ G_interpreter->call_func_ptr(vmg_ ctx->filter_func, 1, &ctx->rc, 0); /* get the result */ valp = G_interpreter->get_r0(); /* if it's 0 or nil, it's a rejection of the match */ if (valp->typ == VM_NIL || (valp->typ == VM_INT && valp->val.intval == 0)) { /* it's a rejection of the value - don't count it */ } else { /* it's an acceptance of the value - count it as found */ ctx->found = TRUE; } } else { /* there's no filter, so everything matches */ ctx->found = TRUE; } } } /* * property evaluation - check to see if a word is defined */ int CVmObjDict::getp_is_defined(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc) { static CVmNativeCodeDesc desc(1, 1); uint orig_argc = (argc == 0 ? 0 : *argc); vm_val_t *arg0; const char *strp; size_t strl; vm_val_t filter; unsigned int hash; /* check arguments */ if (get_prop_check_argc(val, argc, &desc)) return TRUE; /* * make sure the argument is a string, but leave it on the stack for * gc protection */ arg0 = G_stk->get(0); if ((strp = arg0->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* read and skip the string's length prefix */ strl = vmb_get_len(strp); strp += VMB_LEN; /* if there's a second argument, it's the filter function */ if (orig_argc >= 2) { /* retrieve the filter */ filter = *G_stk->get(1); } else { /* there's no filter */ filter.set_nil(); } /* calculate the hash code */ hash = calc_str_hash(vmg_ arg0, strp, strl); /* enumerate matches for the string */ isdef_ctx ctx(vmg_ this, &filter, arg0, strp, strl); ctx.rc.init(vmg_ "Dict.isDefined", self, 5, arg0, orig_argc); get_ext()->hashtab_->enum_hash_matches(hash, &isdef_cb, &ctx); /* if we found any matches, return true; otherwise, return nil */ val->set_logical(ctx.found); /* discard arguments */ G_stk->discard(orig_argc); /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * callback context for forEachWord enumerator */ struct for_each_word_enum_cb_ctx { /* callback function value */ vm_val_t *cb_val; /* globals */ vm_globals *vmg; /* recursive native caller context */ vm_rcdesc rc; }; /* * callback for forEachWord enumerator */ static void for_each_word_enum_cb(void *ctx0, CVmHashEntry *entry0) { for_each_word_enum_cb_ctx *ctx = (for_each_word_enum_cb_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; size_t list_idx; vm_obj_id_t str_obj; /* set up access to the VM globals through my stashed context */ VMGLOB_PTR(ctx->vmg); /* create a string object for this entry's word string */ str_obj = CVmObjString::create(vmg_ FALSE, entry->getstr(), entry->getlen()); /* scan the entry's list of word associations */ for (list_idx = 0 ; ; ++list_idx) { vm_dict_entry *p; size_t i; /* * Find the list entry at the current list index. Note that we * scan the list on each iteration for the current index because * we don't want to keep track of any pointers between iterations; * this insulates us from any changes to the underlying list made * in the callback. */ for (i = 0, p = entry->get_head() ; i < list_idx && p != 0 ; p = p->nxt_, ++i) ; /* * if we didn't find a list entry at this index, we're done with * this hashtable entry */ if (p == 0) break; /* * Invoke the callback on this list entry. The arguments to the * callback are the object, the string, and the vocabulary * property of the association: * * func(obj, str, prop) * * Note that the order of the arguments is the same as for * addWord() and removeWord(). */ /* push the vocabulary property of the association */ G_stk->push()->set_propid(p->prop_); /* push the string of the entry */ G_stk->push()->set_obj(str_obj); /* push the object of the association */ G_stk->push()->set_obj(p->obj_); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ ctx->cb_val, 3, &ctx->rc, 0); } } /* * property evaluation - enumerate each word association */ int CVmObjDict::getp_for_each_word(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); vm_val_t *cb_val; int orig_argc = (argc != 0 ? *argc : 0); for_each_word_enum_cb_ctx ctx; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get a pointer to the callback value, but leave it on the stack */ cb_val = G_stk->get(0); /* push a self-reference for gc protection while we're working */ G_stk->push()->set_obj(self); /* * enumerate the entries in our underlying hashtable - use the "safe" * enumerator to ensure that we're insulated from any changes to the * hashtable that the callback wants to make */ ctx.vmg = VMGLOB_ADDR; ctx.cb_val = cb_val; ctx.rc.init(vmg_ "Dict.forEachWord", self, 6, cb_val, orig_argc); get_ext()->hashtab_->safe_enum_entries(for_each_word_enum_cb, &ctx); /* discard arguments and gc protection */ G_stk->discard(2); /* there's no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Correction list entry. Each time we match a word in the spelling * correction generator, we'll add one of these entries to our list of * matched words. This lets us check for words that we've already found * (since it's often possible to find a match via more than one editing * route). */ struct corr_word { void *operator new(size_t siz, size_t strl) { return t3mallocnew(siz + (strl-1)*sizeof(wchar_t)); } void operator delete(void *ptr) { t3free(ptr); } corr_word(wchar_t *str, size_t strl, int dist, int repl, corr_word *nxt) { memcpy(this->str, str, strl*sizeof(wchar_t)); this->strl = strl; this->dist = dist; this->repl = repl; this->nxt = nxt; } /* next in list */ corr_word *nxt; /* best edit distance found for this word */ int dist; /* number of character replacements */ int repl; /* text of the word (we overallocate the structure to make room) */ size_t strl; wchar_t str[1]; }; /* correction types */ enum corr_type { NoChange, Insertion, Deletion, Replacement, Transposition }; /* * Correction stack element. This represents a state in the correction * process. */ struct corr_state { /* * Allocate space. During the state processing, we need to add one * character to the last state to form certain subsequent states, so * always overallocate our string by one character. This allows us to * build the next-state strings in place in our buffer. */ void *operator new(size_t siz, size_t strl) { return t3mallocnew(siz + strl*sizeof(wchar_t)); } void operator delete(void *ptr) { t3free(ptr); } corr_state(vmdict_TrieNode *root) { init(0, 0, 0, 0, 0, root, NoChange, 0); } corr_state(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ, corr_state *nxt) { init(str, strl, dist, repl, ipos, node, typ, nxt); } void init(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ, corr_state *nxt) { memcpy(this->str, str, strl*sizeof(wchar_t)); this->strl = strl; this->dist = dist; this->repl = repl; this->ipos = ipos; this->node = node; this->typ = typ; this->nxt = nxt; } /* next state in the stack */ corr_state *nxt; /* edit distance so far */ int dist; /* number of replacement transitions */ int repl; /* current character index in the input string */ size_t ipos; /* current Trie node in the dictionary */ vmdict_TrieNode *node; /* type of last transition */ corr_type typ; /* the string (we overallocate the structure to make room) */ size_t strl; wchar_t str[1]; }; /* correction state stack */ struct corr_stack { corr_stack(vmdict_TrieNode *root) { /* start the stack with an empty initial state */ top = new (0) corr_state(root); } int empty() const { return top == 0; } void push(const wchar_t *str, size_t strl, int dist, int repl, size_t ipos, vmdict_TrieNode *node, corr_type typ) { top = new (strl) corr_state( str, strl, dist, repl, ipos, node, typ, top); } corr_state *pop() { /* retrieve and unlink the top state */ corr_state *ret = top; top = top->nxt; /* return the state we unlinked */ return ret; } /* top of the stack */ corr_state *top; }; /* * property evaluation - get a list of possible corrections for a * misspelled word */ int CVmObjDict::getp_correct(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { const char *str; size_t strl; int max_dist; static CVmNativeCodeDesc desc(2); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the word string and maximum edit distance */ str = CVmBif::pop_str_val(vmg0_); max_dist = CVmBif::pop_int_val(vmg0_); /* get the string length and character pointer */ strl = vmb_get_len(str); str += VMB_LEN; /* for easier processing, convert the string to a wchar_t array */ size_t wcnt = utf8_ptr::to_wcharz(0, 0, str, strl); wchar_t *wstr = new wchar_t[wcnt+1]; utf8_ptr::to_wcharz(wstr, wcnt+1, str, strl); /* if we have a StringComparator, fetch it */ CVmObjStrComp *cmp = 0; size_t trunc_len = 0; if (get_ext()->comparator_type_ == VMDICT_COMP_STRCOMP) { /* get the comparator */ cmp = (CVmObjStrComp *)get_ext()->comparator_obj_; /* note the truncation length */ trunc_len = cmp->trunc_len(); } /* if we don't have the Trie yet, build it */ if (get_ext()->trie_ == 0) build_trie(vmg0_); /* create a stack with the initial empty state */ corr_stack stk(get_ext()->trie_); /* start with an empty result list */ corr_word *results = 0; /* process states until the stack is empty */ while (!stk.empty()) { /* pop the next state */ corr_state *s = stk.pop(); /* * Check for an 'accept' state. This is a state where we've * exhausted the input word, and we're at a Trie node that has at * least one word defined. */ if (s->node->word_cnt != 0 && s->ipos == wcnt) { /* * We've found a matching word (i.e., a candidate dictionary * match for the misspelled input word). Scan the list of * candidates we've already found to see if we've already * matched this word. */ corr_word *w; for (w = results ; w != 0 ; w = w->nxt) { if (w->strl == s->strl && memcmp(w->str, s->str, s->strl*sizeof(wchar_t)) == 0) break; } /* if we didn't find an entry, add a new one */ if (w == 0) results = w = new (s->strl) corr_word( s->str, s->strl, s->dist, s->repl, results); /* keep the lowest edit distance we've found so far */ if (s->dist < w->dist || (s->dist == w->dist && s->repl < w->repl)) { w->dist = s->dist; w->repl = s->repl; } } /* * If we have any edit distance left, try an insertion (i.e., the * input word has an extra letter relative to the dictionary word). * Don't do insertions directly after deletions, though, since * they'd just cancel out. */ if (s->dist < max_dist && s->ipos < wcnt && s->typ != Deletion) stk.push(s->str, s->strl, s->dist + 1, s->repl, s->ipos + 1, s->node, Insertion); /* try each possible dictionary transition from here */ for (vmdict_TrieNode *chi = s->node->chi ; chi != 0 ; chi = chi->nxt) { /* * Build the next-state string. Note that we can just add the * character directly to the current state's string buffer, * since we always allocate state structures with one extra * character of padding for just this purpose. */ s->str[s->strl] = chi->ch; /* * Check for a match to the current character. If we have a * StringComparator, ask the comparator to check the match. * Otherwise just check for an exact character match. */ size_t matchlen = 0; if (s->ipos < wcnt) { if (cmp != 0) { /* we have a comparator - check it for the match */ matchlen = cmp->match_chars( wstr + s->ipos, wcnt - s->ipos, chi->ch); } else { /* no comparator - do a simple character comparison */ if (wstr[s->ipos] == chi->ch) matchlen = 1; } /* if we have a match, push a state for it */ if (matchlen != 0) stk.push(s->str, s->strl + 1, s->dist, s->repl, s->ipos + matchlen, chi, NoChange); } else { /* * we've reached the end of the input; but if the current * edit string is at least the truncation length in the * string comparator, consider this a character match as * well */ if (trunc_len != 0 && s->strl >= trunc_len && s->ipos >= trunc_len) stk.push(s->str, s->strl + 1, s->dist, s->repl, s->ipos, chi, NoChange); } /* if we have any edit distance remaining, try corrections */ if (s->dist < max_dist) { /* try a replaced letter */ if (s->ipos < wcnt && matchlen == 0) stk.push(s->str, s->strl + 1, s->dist + 1, s->repl + 1, s->ipos + 1, chi, Replacement); /* try a deletion (i.e., the input is missing a character) */ if (s->typ != Insertion) stk.push(s->str, s->strl + 1, s->dist + 1, s->repl, s->ipos, chi, Deletion); } /* * if we just did a replacement edit, check to see if it's * actually a transposition */ if (s->typ == Replacement && s->ipos > 0 && s->ipos < wcnt && wstr[s->ipos - 1] == chi->ch && wstr[s->ipos] == s->str[s->strl - 1]) stk.push(s->str, s->strl + 1, s->dist, s->repl - 1, s->ipos + 1, chi, Transposition); } /* we've finished processing the current state */ delete s; } /* done with the wchar_t version of the word */ delete [] wstr; /* * Count up the number of result entries. Only count words with a * non-zero edit distance - words with a zero edit distance are already * matches for the input word, so we can't consider them to be * corrections for it. */ corr_word *r; int i; for (r = results, i = 0 ; r != 0 ; r = r->nxt) { /* if this word has a non-zero edit distance, count it */ if (r->dist != 0) ++i; } /* allocate a List to hold the results */ vm_obj_id_t lst_id = CVmObjList::create(vmg_ FALSE, i); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_id); lst->cons_clear(); /* push it for gc protection */ G_stk->push()->set_obj(lst_id); /* create a List [word, distance] for each result */ for (r = results, i = 0 ; r != 0 ; r = r->nxt) { vm_val_t val; /* * If this word has a zero edit distance, skip it. A word with a * zero edit distance is already a match for the original, so it * can't be considered a correction. */ if (r->dist == 0) continue; /* create the sublist */ vm_obj_id_t sub_id = CVmObjList::create(vmg_ FALSE, 3); CVmObjList *sub = (CVmObjList *)vm_objp(vmg_ sub_id); /* add it to the main list */ val.set_obj(sub_id); lst->cons_set_element(i, &val); /* measure the utf8 space needed for the string */ utf8_ptr rp; size_t rplen = rp.setwchars(r->str, r->strl, 0); /* create the word string */ vm_obj_id_t lstr_id = CVmObjString::create(vmg_ FALSE, rplen); CVmObjString *lstr = (CVmObjString *)vm_objp(vmg_ lstr_id); /* encode it */ rp.set(lstr->cons_get_buf()); rp.setwchars(r->str, r->strl, rplen); /* add it to the sublist */ val.set_obj(lstr_id); sub->cons_set_element(0, &val); /* add the edit distance */ val.set_int(r->dist); sub->cons_set_element(1, &val); /* add the character replacement count */ val.set_int(r->repl); sub->cons_set_element(2, &val); /* count it */ ++i; } /* delete the result list */ while (results != 0) { /* unlink this element */ r = results; results = results->nxt; /* delete it */ delete r; } /* return the list value */ retval->set_obj(lst_id); /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Create an undo record */ dict_undo_rec *CVmObjDict::alloc_undo_rec(dict_undo_action action, const char *txt, size_t len) { size_t alloc_size; dict_undo_rec *rec; /* * compute the allocation size - start with the structure size, and * add in the length we need to store the string if one is present */ alloc_size = sizeof(dict_undo_rec) - 1; if (txt != 0) alloc_size += len; /* allocate the record */ rec = (dict_undo_rec *)t3malloc(alloc_size); /* set the action */ rec->action = action; /* if there's a word, copy the text */ if (txt != 0) memcpy(rec->txt, txt, len); /* save the word length */ rec->len = len; /* presume we won't store an object or property */ rec->obj = VM_INVALID_OBJ; rec->prop = VM_INVALID_PROP; /* return the new record */ return rec; } /* * add an undo record */ void CVmObjDict::add_undo_rec(VMG_ vm_obj_id_t self, dict_undo_rec *rec) { vm_val_t val; /* add the record with an empty value */ val.set_empty(); if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &val)) { /* * we didn't add an undo record, so our extra undo information * isn't actually going to be stored in the undo system - hence * we must delete our extra information */ t3free(rec); } } /* * apply undo */ void CVmObjDict::apply_undo(VMG_ CVmUndoRecord *undo_rec) { if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_ADD: /* we added the word, so we must now delete it */ del_hash_entry(vmg_ rec->txt, rec->len, rec->obj, rec->prop); break; case DICT_UNDO_DEL: /* we deleted the word, so now we must add it */ add_hash_entry(vmg_ rec->txt, rec->len, TRUE, rec->obj, rec->prop, FALSE); break; case DICT_UNDO_COMPARATOR: /* we changed the comparator object, so change it back */ set_comparator(vmg_ rec->obj); break; } /* discard my record */ t3free(rec); /* clear the pointer in the undo record so we know it's gone */ undo_rec->id.ptrval = 0; } } /* * discard extra undo information */ void CVmObjDict::discard_undo(VMG_ CVmUndoRecord *rec) { /* delete our extra information record */ if (rec->id.ptrval != 0) { /* free the record */ t3free((dict_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* * mark references in an undo record */ void CVmObjDict::mark_undo_ref(VMG_ CVmUndoRecord *undo_rec) { /* check our private record if we have one */ if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_COMPARATOR: /* the 'obj' entry is the old comparator object */ G_obj_table->mark_all_refs(rec->obj, VMOBJ_REACHABLE); break; case DICT_UNDO_ADD: case DICT_UNDO_DEL: /* * these actions use only weak references, so there's nothing * to do here */ break; } } } /* * remove stale weak references from an undo record */ void CVmObjDict::remove_stale_undo_weak_ref(VMG_ CVmUndoRecord *undo_rec) { /* check our private record if we have one */ if (undo_rec->id.ptrval != 0) { dict_undo_rec *rec; /* get my undo record */ rec = (dict_undo_rec *)undo_rec->id.ptrval; /* take the appropriate action */ switch(rec->action) { case DICT_UNDO_ADD: case DICT_UNDO_DEL: /* check to see if our object is being deleted */ if (rec->obj != VM_INVALID_OBJ && G_obj_table->is_obj_deletable(rec->obj)) { /* * the object is being deleted - since we only weakly * reference our objects, we must forget about this * object now, which means we can forget this entire * record */ t3free(rec); undo_rec->id.ptrval = 0; } break; case DICT_UNDO_COMPARATOR: /* this action does not use weak references */ break; } } } /* ------------------------------------------------------------------------ */ /* * callback context for weak reference function */ struct enum_hashtab_ctx { /* global context pointer */ vm_globals *vmg; /* dictionary object */ CVmObjDict *dict; }; /* * Mark references */ void CVmObjDict::mark_refs(VMG_ uint state) { /* if I have a comparator object, mark it as referenced */ if (get_ext()->comparator_ != VM_INVALID_OBJ) G_obj_table->mark_all_refs(get_ext()->comparator_, state); } /* * Remove weak references that have become stale. For each object for * which we have a weak reference, check to see if the object is marked * for deletion; if so, remove our reference to the object. */ void CVmObjDict::remove_stale_weak_refs(VMG0_) { enum_hashtab_ctx ctx; /* iterate over our hash table */ ctx.vmg = VMGLOB_ADDR; ctx.dict = this; get_ext()->hashtab_->enum_entries(&remove_weak_ref_cb, &ctx); } /* * enumeration callback to eliminate stale weak references */ void CVmObjDict::remove_weak_ref_cb(void *ctx0, CVmHashEntry *entry0) { enum_hashtab_ctx *ctx = (enum_hashtab_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; vm_dict_entry *nxt; /* set up for global access through the context */ VMGLOB_PTR(ctx->vmg); /* scan the entry's list */ for (cur = entry->get_head() ; cur != 0 ; cur = nxt) { /* remember the next entry in case we delete this one */ nxt = cur->nxt_; /* check to see if this object is free - if so, remove this record */ if (G_obj_table->is_obj_deletable(cur->obj_)) { /* if we have a trie, delete the trie entry */ if (ctx->dict->get_ext()->trie_ != 0) ctx->dict->get_ext()->trie_->del_word( entry->getstr(), entry->getlen()); /* delete the entry */ entry->del_entry(ctx->dict->get_ext()->hashtab_, cur->obj_, cur->prop_); } } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjDict::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* remember where our image data comes from */ get_ext()->image_data_ = ptr; get_ext()->image_data_size_ = siz; /* build the hash table from the image data */ build_hash_from_image(vmg0_); /* * register for post-load initialization, as we might need to rebuild * our hash table once we have access to the comparator object */ G_obj_table->request_post_load_init(self); } /* * Post-load initialization */ void CVmObjDict::post_load_init(VMG_ vm_obj_id_t self) { vm_obj_id_t comp; /* * Rebuild the hash table with the comparator, if we have one. When * we load, reset, or restore, we build a tentative hash table with no * comparator (i.e., the default exact literal comparator), because we * can't assume we have access to the comparator object (as it might * be loaded after us). So, we register for post-load initialization, * and then we go back and re-install the comparator for real, * rebuilding the hash table with the actual comparator. */ if ((comp = get_ext()->comparator_) != VM_INVALID_OBJ) { /* ensure the comparator object is initialized */ G_obj_table->ensure_post_load_init(vmg_ comp); /* set the comparator object type, now that it's loaded */ set_comparator_type(vmg_ comp); /* * force a rebuild the hash table, so that we build it with the * comparator properly installed */ create_hash_table(vmg0_); } } /* * Build the hash table from the image data */ void CVmObjDict::build_hash_from_image(VMG0_) { uint cnt; uint i; const char *p; const char *endp; vm_obj_id_t comp; /* start reading at the start of the image data */ p = get_ext()->image_data_; endp = p + get_ext()->image_data_size_; /* read the comparator object ID */ comp = (vm_obj_id_t)t3rp4u(p); p += 4; /* * Do NOT install the actual comparator object at this point, but * simply build the table tentatively with a nil comparator. We can't * assume that the comparator object has been loaded yet (as it might * be loaded after us), so we cannot use it to build the hash table. * We have to do something with the data, though, so build the hash * table tentatively with no comparator; we'll rebuild it again at * post-load-init time with the real comparator. * * (This isn't as inefficient as it sounds, as we retain all of the * original hash table entries when we rebuild the table. All we end * up doing is scanning the entries again to recompute their new hash * values, and reallocating the hash table object itself.) */ get_ext()->comparator_ = VM_INVALID_OBJ; set_comparator_type(vmg_ VM_INVALID_OBJ); /* create the new hash table */ create_hash_table(vmg0_); /* read the entry count */ cnt = osrp2(p); p += 2; /* scan the entries */ for (i = 0 ; p < endp && i < cnt ; ++i) { vm_obj_id_t obj; vm_prop_id_t prop; uint key_len; uint item_cnt; const char *key; CVmHashEntryDict *entry; char key_buf[256]; char *keyp; size_t key_rem; /* read the key length */ key_len = *(unsigned char *)p++; /* remember the key pointer */ key = p; p += key_len; /* copy the key to our buffer for decoding */ memcpy(key_buf, key, key_len); /* decode the key */ for (keyp = key_buf, key_rem = key_len ; key_rem != 0 ; --key_rem, ++keyp) *keyp ^= 0xBD; /* read the key from the decoded buffer now */ key = key_buf; /* read the item count */ item_cnt = osrp2(p); p += 2; /* find an existing entry for this key */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(key, key_len); /* if there's no existing entry, add one */ if (entry == 0) { /* create a new entry */ entry = new CVmHashEntryDict(key, key_len, TRUE, TRUE); /* add it to the table */ get_ext()->hashtab_->add(entry); } /* read the items */ for ( ; item_cnt != 0 ; --item_cnt) { /* read the object ID and property ID */ obj = (vm_obj_id_t)t3rp4u(p); prop = (vm_prop_id_t)osrp2(p + 4); /* * add the entry - this entry is from the image file, so * mark it as such */ entry->add_entry(obj, prop, TRUE); /* move on to the next item */ p += 6; } } /* * Now that we're done building the table, remember the comparator. We * can't set the type yet, because the object might not be loaded yet - * defer this until post-load initialization time. */ get_ext()->comparator_ = comp; } /* ------------------------------------------------------------------------ */ /* * Callback context for trie-building enumeration */ struct trie_cb_ctx { vmdict_TrieNode *t; }; /* hash table enumeration callback for building the trie */ static void trie_cb(void *ctx0, CVmHashEntry *entry0) { /* get the context and hash entries, properly cast */ trie_cb_ctx *ctx = (trie_cb_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; /* add this word to the trie */ ctx->t->add_word(entry->getstr(), entry->getlen()); } /* * Build the Trie from the hash table */ void CVmObjDict::build_trie(VMG0_) { trie_cb_ctx ctx; /* if we already have a trie, there's nothing to do */ if (get_ext()->trie_ != 0) return; /* create the root node */ ctx.t = get_ext()->trie_ = new vmdict_TrieNode(0, 0); /* enumerate the hash table to build the trie */ get_ext()->hashtab_->enum_entries(trie_cb, &ctx); } /* ------------------------------------------------------------------------ */ /* * Trie operations */ /* add a word to the Trie */ void vmdict_TrieNode::add_word(const char *str, size_t len) { vmdict_TrieNode *n; utf8_ptr p((char *)str); /* scan the string and walk the Trie */ for (n = this ; len != 0 ; p.inc(&len)) { /* get this character */ wchar_t ch = p.getch(); /* find the child node for this letter */ vmdict_TrieNode *chi; for (chi = n->chi ; chi != 0 && chi->ch != ch ; chi = chi->nxt) ; /* if there's no existing child for this letter, add one */ if (chi == 0) n->chi = chi = new vmdict_TrieNode(n->chi, ch); /* advance to this child node */ n = chi; } /* 'n' is the final node for this word, so count the word there */ n->word_cnt += 1; } /* find a node for a given word */ vmdict_TrieNode *vmdict_TrieNode::find_word(const char *str, size_t len) { vmdict_TrieNode *n; utf8_ptr p((char *)str); /* scan the string and walk the Trie */ for (n = this ; len != 0 ; p.inc(&len)) { /* get this character */ wchar_t ch = p.getch(); /* find the child node for this letter */ vmdict_TrieNode *chi; for (chi = n->chi ; chi != 0 && chi->ch != ch ; chi = chi->nxt) ; /* if we didn't find a child, the word isn't in the trie */ if (chi == 0) return 0; /* advance to this child node */ n = chi; } /* we've reached the final node for the word - return it */ return n; } /* delete a word from the Trie */ void vmdict_TrieNode::del_word(const char *str, size_t len) { /* find the final node for this word */ vmdict_TrieNode *n = find_word(str, len); /* if we found it, decrement its word count */ if (n != 0 && n->word_cnt != 0) n->word_cnt -= 1; } /* ------------------------------------------------------------------------ */ /* * Add an entry */ int CVmObjDict::add_hash_entry(VMG_ const char *p, size_t len, int copy, vm_obj_id_t obj, vm_prop_id_t prop, int from_image) { CVmHashEntryDict *entry; /* find an existing entry for this key */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(p, len); /* if we didn't find an entry, add one */ if (entry == 0) { /* create an entry */ entry = new CVmHashEntryDict(p, len, copy, from_image); /* add it to the table */ get_ext()->hashtab_->add(entry); /* if we have a trie, add it to the trie */ if (get_ext()->trie_ != 0) get_ext()->trie_->add_word(p, len); } /* add the obj/prop to the entry's item list */ return entry->add_entry(obj, prop, from_image); } /* ------------------------------------------------------------------------ */ /* * delete an entry */ int CVmObjDict::del_hash_entry(VMG_ const char *str, size_t len, vm_obj_id_t obj, vm_prop_id_t voc_prop) { CVmHashEntryDict *entry; /* find the hash table entry for the word */ entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(str, len); /* if we found it, delete the obj/prop entry */ if (entry != 0) { /* if we have a trie, delete the trie entry */ if (get_ext()->trie_ != 0) get_ext()->trie_->del_word(str, len); /* delete the hash table entry */ return entry->del_entry(get_ext()->hashtab_, obj, voc_prop); } /* we didn't find anything to delete */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * restore to image file state */ void CVmObjDict::reset_to_image(VMG_ vm_obj_id_t self) { /* delete the hash table */ if (get_ext()->hashtab_ != 0) { /* delete it */ delete get_ext()->hashtab_; /* forget it */ get_ext()->hashtab_ = 0; } /* delete the Trie */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* rebuild the hash table from the image file data */ build_hash_from_image(vmg0_); /* * register for post-load initialization, as we might need to rebuild * our hash table once we have access to the comparator object */ G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * determine if the object has been changed since it was loaded */ int CVmObjDict::is_changed_since_load() const { /* * return true if our 'modified' flag is true - if it is, we must have * been changed since we were loaded */ return (get_ext()->modified_ != 0); } /* ------------------------------------------------------------------------ */ /* * save-file enumeration context */ struct save_file_ctx { /* file object */ CVmFile *fp; /* entry count */ ulong cnt; /* globals */ vm_globals *vmg; }; /* * save to a file */ void CVmObjDict::save_to_file(VMG_ CVmFile *fp) { long cnt_pos; long end_pos; save_file_ctx ctx; /* write the current comparator object */ fp->write_uint4(get_ext()->comparator_); /* remember the starting seek position and write a placeholder count */ cnt_pos = fp->get_pos(); fp->write_uint4(0); /* enumerate the entries to write out each one */ ctx.fp = fp; ctx.cnt = 0; ctx.vmg = VMGLOB_ADDR; get_ext()->hashtab_->enum_entries(&save_file_cb, &ctx); /* remember our position for a moment */ end_pos = fp->get_pos(); /* go back and write out the symbol count prefix */ fp->set_pos(cnt_pos); fp->write_uint4(ctx.cnt); /* seek back to the end */ fp->set_pos(end_pos); } /* * save file enumeration callback */ void CVmObjDict::save_file_cb(void *ctx0, CVmHashEntry *entry0) { save_file_ctx *ctx = (save_file_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; uint cnt; /* set up global access */ VMGLOB_PTR(ctx->vmg); /* write out this entry's name */ ctx->fp->write_uint2(entry->getlen()); ctx->fp->write_bytes(entry->getstr(), entry->getlen()); /* count the items in this entry's list */ for (cnt = 0, cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* * if the referenced object is persistent, count this item; * don't count items that refer to non-persistent items, because * these objects will not be saved to the file and hence will * not be present when the saved state is restored */ if (G_obj_table->is_obj_persistent(cur->obj_)) ++cnt; } /* * if there are no saveable items for this entry, do not write out * the entry at all */ if (cnt == 0) return; /* count this entry */ ++ctx->cnt; /* write the item count */ ctx->fp->write_uint2(cnt); /* write out each item in this entry's list */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* if this item's object is persistent, write out the item */ if (G_obj_table->is_obj_persistent(cur->obj_)) { /* write out the object and property for this entry */ ctx->fp->write_uint4((long)cur->obj_); ctx->fp->write_uint2((int)cur->prop_); } } } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjDict::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { ulong cnt; vm_obj_id_t comp; /* delete the old hash table if we have one */ if (get_ext()->hashtab_ != 0) { delete get_ext()->hashtab_; get_ext()->hashtab_ = 0; } /* delete the Trie if we have one */ if (get_ext()->trie_ != 0) { delete get_ext()->trie_; get_ext()->trie_ = 0; } /* * Read the comparator and fix it up to the new object numbering * scheme, but do not install it (as it might not be loaded yet). */ comp = fixups->get_new_id(vmg_ (vm_obj_id_t)fp->read_uint4()); /* create the new, empty hash table */ create_hash_table(vmg0_); /* read the number of symbols */ cnt = fp->read_uint4(); /* read the symbols */ for ( ; cnt != 0 ; --cnt) { uint len; uint read_len; char buf[256]; uint item_cnt; /* read the symbol length */ len = fp->read_uint2(); /* limit the reading to our buffer size */ read_len = len; if (read_len > sizeof(buf)) read_len = sizeof(buf); /* read the string */ fp->read_bytes(buf, read_len); /* skip any extra data */ if (len > read_len) fp->set_pos(fp->get_pos() + len - read_len); /* read the item count */ item_cnt = fp->read_uint2(); /* read the items */ for ( ; item_cnt != 0 ; --item_cnt) { vm_obj_id_t obj; vm_prop_id_t prop; /* read the object and property ID's */ obj = (vm_obj_id_t)fp->read_uint4(); prop = (vm_prop_id_t)fp->read_uint2(); /* translate the object ID through the fixup table */ obj = fixups->get_new_id(vmg_ obj); /* add the entry, if it refers to a valid object */ if (obj != VM_INVALID_OBJ) add_hash_entry(vmg_ buf, len, TRUE, obj, prop, FALSE); } } /* * install the comparator (we can't set its type yet, though, because * the object might not be loaded yet) */ get_ext()->comparator_ = comp; set_comparator_type(vmg_ VM_INVALID_OBJ); /* * If we had to restore it, we'll have to save it if the current state * is later saved, even if we don't modify it again. The fact that * this version was saved before means this version has to be saved * from now on. */ get_ext()->modified_ = TRUE; /* * register for post-load initialization, so that we can rebuild the * hash table with the actual comparator if necessary */ G_obj_table->request_post_load_init(self); } /* ------------------------------------------------------------------------ */ /* * impedence-matcher callback context */ struct enum_word_props_ctx { /* client callback to invoke */ void (*cb_func)(VMG_ void *ctx, vm_prop_id_t prop, const vm_val_t *match_val); /* string value to match */ const vm_val_t *strval; const char *strp; size_t strl; /* client callback context */ void *cb_ctx; /* globals */ vm_globals *globals; /* the dictionary object we're searching */ CVmObjDict *dict; }; /* * enum_word_props hash table enumeration callback */ void CVmObjDict::enum_word_props_cb(void *ctx0, CVmHashEntry *entry0) { enum_word_props_ctx *ctx = (enum_word_props_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_val_t match_val; VMGLOB_PTR(ctx->globals); /* if this entry matches the search string, process it */ if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl, entry->getstr(), entry->getlen(), &match_val)) { vm_dict_entry *cur; /* process the items under this entry */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* invoke the callback */ (*ctx->cb_func)(vmg_ ctx->cb_ctx, cur->prop_, &match_val); } } } /* * Enumerate the properties for which a word is defined */ void CVmObjDict::enum_word_props(VMG_ void (*cb_func)(VMG_ void *, vm_prop_id_t, const vm_val_t *), void *cb_ctx, const vm_val_t *strval, const char *strp, size_t strl) { enum_word_props_ctx ctx; unsigned int hash; /* set up the enumeration callback context */ ctx.strval = strval; ctx.strp = strp; ctx.strl = strl; ctx.cb_func = cb_func; ctx.cb_ctx = cb_ctx; ctx.globals = VMGLOB_ADDR; ctx.dict = this; /* calculate the hash code */ hash = calc_str_hash(vmg_ strval, strp, strl); /* enumerate the matches */ get_ext()->hashtab_->enum_hash_matches(hash, &enum_word_props_cb, &ctx); } frobtads-1.2.3/tads3/vmdate.cpp0000644000175000001440000037462411770174043015532 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdate.cpp - CVmObjDate object Function Implements the Date intrinsic class Notes Modified 01/23/12 MJRoberts - Creation */ #include #include #include #include #include #include #include #include #include "t3std.h" #include "vmdate.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbignum.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmlst.h" #include "vmvec.h" #include "vmlookup.h" #include "utf8.h" #include "vmuni.h" #include "vmtz.h" #include "vmtzobj.h" /* ------------------------------------------------------------------------ */ /* * Round a double to int32 */ static int32_t round_int32(double d) { double i; return (int32_t)(modf(d, &i) >= 0.5 ? (d >= 0 ? ceil(d) : floor(d)) : (d < 0 ? ceil(d) : floor(d))); } /* ------------------------------------------------------------------------ */ /* * get the current date/time in UTC */ void caldate_t::now(int32_t &dayno, int32_t &daytime) { /* get the current date and time in Unix Epoch format */ os_time_t s; long ns; os_time_ns(&s, &ns); /* * 's' is the number of seconds since the Unix Epoch, 1/1/1970 UTC. * Get our day number: divide by seconds per day to get days past the * Unix Epoch, then adjust to our Epoch (3/1/0000 UTC) by adding the * number of days from our Epoch to the Unix Epoch. */ dayno = (int32_t)(s/(24*60*60) + caldate_t::UNIX_EPOCH_DAYNO); /* * convert 's' to the time of day, adding in the fractional portion * from 'ns' but only keeping millisecond precision */ daytime = (uint32_t)((s % (24*60*60))*1000 + ns/1000000); } /* * normalize a date/time value: if the daytime value is before 0000 hours * or after 2400 hours, bring it back within bounds and adjust the day * number */ void caldate_t::normalize(int32_t &dayno, int32_t &daytime) { /* if we're outside the 0000-2400 bounds, adjust the time and day */ ldiv_t q = ldiv(daytime, 24*60*60*1000); dayno += (int32_t)q.quot; if ((daytime = (int32_t)q.rem) < 0) { daytime += 24*60*60*1000; dayno -= 1; } } /* ------------------------------------------------------------------------ */ /* * Locale string retrieval */ class CVmDateLocale { public: CVmDateLocale(VMG0_) { /* get the state Vector from the Date static class state holder */ const vm_val_t *val = vm_classobj_for(CVmObjDate)->get_class_state(); vecid = val->typ == VM_OBJ ? val->val.obj : VM_INVALID_OBJ; vec = vm_objid_cast(CVmObjVector, vecid); } /* get a locale string */ const char *get(VMG_ size_t &len, int idx) { /* if it's negative, it's invalid */ if (idx < 0) { len = 0; return 0; } /* try getting it from the state vector */ if (vec != 0 && idx < (int)vec->get_element_count()) { /* get the element */ vm_val_t ele; vec->get_element(idx, &ele); /* if it's a string, return it */ const char *str = ele.get_as_string(vmg0_); if (str != 0) { len = vmb_get_len(str); return str + VMB_LEN; } } /* we didn't find it in the state vector; return the default */ if (idx < ndefaults) { len = strlen(defaults[idx]); return defaults[idx]; } /* invalid entry */ len = 0; return 0; } /* * Index a locale list. If 'sticky' is true, the last item actually * present in the list will be returned if the index is past the end of * the list. */ const char *index_list(VMG_ size_t &itemlen, int item, int idx, int sticky) { /* get the item */ size_t len; const char *str = get(vmg_ len, item); /* if we didn't find it, return failure */ if (str == 0) { itemlen = 0; return 0; } /* skip items (delimited by commas) until we reach the target index */ for ( ; idx > 0 && len != 0 ; --idx, ++str, --len) { /* find the next comma */ const char *comma = lib_strnchr(str, len, ','); if (comma == 0) { /* * No more commas, so the index is past the end of the * list. If 'sticky' is true, use the last item; otherwise * return "not found". */ if (sticky) break; else { itemlen = 0; return 0; } } /* advance to the comma */ len -= comma - str; str = comma; } /* we have the item; measure its length, up to the next '=' or ',' */ const char *p; for (p = str, itemlen = 0 ; len != 0 && *p != '=' && *p != ',' ; --len, ++p, ++itemlen) ; /* return the item pointer */ return str; } /* set a locale string */ void set(VMG_ CVmUndo *undo, int idx, const vm_val_t *val) { /* if we don't have a vector, create one */ if (vec == 0) { /* create the vector */ vecid = CVmObjVector::create(vmg_ FALSE, ndefaults); vec = vm_objid_cast(CVmObjVector, vecid); /* initialize all elements to nil */ vm_val_t nil; nil.set_nil(); for (int i = 0 ; i < ndefaults ; ++i) vec->append_element(vmg_ vecid, &nil); /* store the vector in the class object's state holder */ vm_val_t val; val.set_obj(vecid); vm_classobj_for(CVmObjDate)->set_class_state(&val); } /* if it's out of bounds, it's an error */ if (idx < 0 || idx >= ndefaults) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* set the specified element */ vec->set_element_undo(vmg_ vecid, idx, val); } /* defaults */ static const char *defaults[]; static const int ndefaults; /* our state Vector */ vm_obj_id_t vecid; CVmObjVector *vec; }; /* list of default locale settings */ const char *CVmDateLocale::defaults[] = { /* 0 - month full names */ "January,February,March,April,May,June,July,August," "September,October,November,December", /* 1 - month name abbreviations */ "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep=Sept,Oct,Nov,Dec", /* 2 - weekday full names */ "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", /* 3 - weekday abbreviations */ "Sun,Mon,Tue,Wed,Thu,Fri,Sat", /* 4 - AM/PM indicators (upper, lower) */ "AM=A.M.,PM=P.M.,am,pm", /* 5 - era (AD/BC) indicators */ "AD=A.D.=CE=C.E.,BC=B.C.=BCE=B.C.E.", /* 6 - parsing format filter - 'us' or 'eu' */ "us", /* 7 - ordinal suffixes */ "st,nd,rd,th,st,nd,rd", /* 8 - timestamp format string */ "%a %b %#d %T %Y", /* 9 - time format string */ "%H:%M:%S", /* 10 - default date format */ "%m/%d/%Y", /* 11 - short date format */ "%m/%d/%y", /* 12 - 12-hour clock format */ "%#I:%M:%S %P", /* 13 - 24-hour clock format */ "%H:%M", /* 14 - 24-hour clock with seconds */ "%H:%M:%S" }; const int CVmDateLocale::ndefaults = countof(CVmDateLocale::defaults); /* locale indices */ static const int LC_MONTH = 0; static const int LC_MON = 1; static const int LC_WEEKDAY = 2; static const int LC_WKDY = 3; static const int LC_AMPM = 4; static const int LC_ERA = 5; static const int LC_PARSE_FILTER = 6; static const int LC_ORDSUF = 7; static const int LC_FMT_TIMESTAMP = 8; static const int LC_FMT_TIME = 9; static const int LC_FMT_DATE = 10; static const int LC_FMT_SHORTDATE = 11; static const int LC_FMT_12HOUR = 12; static const int LC_FMT_24HOUR = 13; static const int LC_FMT_24HOURSECS = 14; /* * Figure the ordinal suffix index (in LC_ORDSUF) for a given number. The * suffix indices are: * *. 0 = 1st *. 1 = 2nd *. 2 = 3rd *. 3 = Nth *. 4 = X1st (e.g., 21st, 31st, 121st) *. 5 = X2nd *. 6 = X3rd */ static int ordinal_index(int n) { /* get the last digit */ int lastdig = n % 10; /* * if it's in the teens, or the last digit is 0 or 4-9, use the generic * Nth suffix */ if (lastdig == 0 || lastdig >= 4 || (n > 10 && n < 20)) return 3; /* if it's in the 1-3 range, use 1st/2nd/3rd; otherwise use X1st/etc */ return lastdig + (n < 10 ? -1 : 3); } /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_date_ext *vm_date_ext::alloc_ext(VMG_ CVmObjDate *self) { /* calculate how much space we need */ size_t siz = sizeof(vm_date_ext); /* allocate the memory */ vm_date_ext *ext = (vm_date_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjDate object statics */ /* metaclass registration object */ static CVmMetaclassDate metaclass_reg_obj; CVmMetaclass *CVmObjDate::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjDate::*CVmObjDate::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjDate::getp_undef, /* 0 */ &CVmObjDate::getp_parseDate, /* 1 */ &CVmObjDate::getp_parseJulianDate, /* 2 */ &CVmObjDate::getp_formatDate, /* 3 */ &CVmObjDate::getp_formatJulianDate, /* 4 */ &CVmObjDate::getp_compareTo, /* 5 */ &CVmObjDate::getp_getDate, /* 6 */ &CVmObjDate::getp_getJulianDay, /* 7 */ &CVmObjDate::getp_getJulianDate, /* 8 */ &CVmObjDate::getp_getISOWeekDate, /* 9 */ &CVmObjDate::getp_getTime, /* 10 */ &CVmObjDate::getp_addInterval, /* 11 */ &CVmObjDate::getp_findWeekday, /* 12 */ &CVmObjDate::getp_setLocaleInfo /* 13 */ }; /* static property indices */ const int VMDATE_PARSEDATE = 1; const int VMDATE_PARSEJULIANDATE = 2; const int VMDATE_SETLOCALINFO = 13; /* ------------------------------------------------------------------------ */ /* * Construction */ CVmObjDate::CVmObjDate(VMG_ int32_t dayno, uint32_t daytime) { /* allocate our extension structure */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* fill it in */ ext->dayno = dayno; ext->daytime = daytime; } /* ------------------------------------------------------------------------ */ /* * Get my date and time in the given local time zone */ const char *CVmObjDate::get_local_time( int32_t &dayno, int32_t &daytime, int32_t &ofs, CVmTimeZone *tz) const { /* get my UTC date and time */ dayno = get_ext()->dayno; daytime = get_ext()->daytime; /* convert to my local time zone */ return tz->utc_to_local(dayno, daytime, ofs); } /* ------------------------------------------------------------------------ */ /* * Date/time parser */ /* string capture item */ struct date_parse_string { date_parse_string() { p = 0; len = 0; } void set(const char *p, size_t len) { this->p = p; this->len = len; } const char *p; size_t len; }; /* * Parse results structure. This keeps track of which information we've * filled in so far. */ struct date_parse_result { date_parse_result(multicaldate_t *cal) { /* remember my calendar interface */ this->cal = cal; /* * Initialize most elements to invalid, out-of-bounds values to * indicate they haven't been set yet. INT_MIN satisfies this for * all of these, so for the sake of clarity use it for all of them. * For the month and day, only positive values are meaningful, so * any negative value will suffice as the "invalid" token. The * hour, minute, second, and milliseconds must be non-negative to * be valid, so any negative value is invalid. The time zone * offset must be between -24 hours (-24*60 minutes) and +24 hours * (+24*60 minutes), so a large negative value is invalid. The * year can mathematically be any integer value, but the ultimate * representation of the date can only hold a 32-bit day number, * which limits the year number to about +/-5 million; so INT_MIN, * which is about minus 2 billion on a 32-bit machine, is a * negative year that we can't represent. (This shouldn't be a big * loss; prehistoric dates aren't very meaningful anyway, as there * are by definition no historical records to correlate with * calculated dates.) The timezone pointer is different in that * it's a pointer, so null is the natural 'undefined' there. */ era = 0; yy = INT_MIN; yy_needs_century = FALSE; mm = INT_MIN; dd = INT_MIN; ampm = 0; hh = INT_MIN; mi = INT_MIN; ss = INT_MIN; ms = INT_MIN; tzofs = INT_MIN; w = INT_MIN; tz = 0; } date_parse_result(date_parse_result &src) { memcpy(this, &src, sizeof(*this)); } /* translate a time from local to UTC using our parsed timezone info */ void local_to_utc(int32_t &dayno, int32_t &daytime) { /* * If we have a specific timezone offset, use the offset; otherwise * translate via the time zone object. * * Note that it's possible for a parsed date to specify both an * offset and a zone. If a seasonal zone abbreviation like "PDT" * is used, it tells us both the timezone (America/Los_Angeles in * the case of PDT) and the offset (-7 hours for PDT). The offset * overrides the implied offset for the timezone because it * specifies a particular seasonal time setting; if only the * timezone is specified, we use the wall clock time for the zone. * For example, "12/1/2000 3:00 America/Los_Angeles" has a -8 hour * offset, since Pacific Standard Time is in effect in this zone in * December, whereas "12/1/2000 3:00 PDT" has a -7 hour offset, * since it's specfically a PDT time even though the wall clock * time in Los Angeles in December is Pacific Standard Time. */ if (has_tzofs()) { /* we have an offset - it overrides the time zone */ daytime -= tzofs; caldate_t::normalize(dayno, daytime); } else { /* no offset, so use the timezone */ tz->local_to_utc(dayno, daytime); } } /* translate from UTC to local using our parsed timezone info */ void utc_to_local(int32_t &dayno, int32_t &daytime) { if (has_tzofs()) { daytime += tzofs; caldate_t::normalize(dayno, daytime); } else { int32_t ofs; tz->utc_to_local(dayno, daytime, ofs); } } /* * set a default date - this sets any undefined date components * individually from the given date */ void def_date(int32_t dayno) { /* convert the day number to a calendar date */ cal->set_dayno(dayno); /* apply the era to the year, if both are present */ if (era != 0 && has_year()) { switch (era) { case 1: /* * astronomical '+' notation - the internal year number * matches the nominal year */ break; case 2: /* * AD - the internal year matches the nominal year, but an * explicit AD overrides the need for a century; e.g., AD * 87 is pretty clear that we're talking about an * historical date in the first century */ yy_needs_century = FALSE; break; case -1: /* * astronomical '-' notation - the internal year number * matches the nominal negative year number, since we use * the same notation internally */ yy = -yy; break; case -2: /* BC - 1 BC is internal year 0, 2 BC is year -1, etc */ yy = -yy + 1; yy_needs_century = FALSE; break; } } /* set the default year, month, and day from the given date */ def_year(cal->y()); def_month(cal->m()); def_day(cal->d()); /* * If the year was specified without a century, choose the century * such that it yields the year nearest to the current year. This * is equivalent to finding the year within 50 years of the current * year. For 2012, this makes the range 1962 to 2061 - so '62 is * taken to be 1962, '85 is 1985, but '15 is 2015, and 61 is 2061. */ if (yy_needs_century) { yy += ((cal->y() / 100) * 100); if (yy < cal->y() - 50) yy += 100; if (yy > cal->y() + 49) yy -= 100; } } /* * set a default time - this sets any undefined time components * individually from the given time */ void def_time(int m, int h, int s, int ms) { /* apply the PM indicator to the time, if present and in range */ if (ampm == 1 && hh == 12) hh = 0; else if (ampm == 2 && hh < 12) hh += 12; def_hour(h); def_minute(m); def_second(s); def_msec(ms); } /* compute the day number */ int32_t dayno() const { /* if there's an ISO weekday, convert it to a calendar date */ if (has_iso_week() && has_year()) { /* * Calculate the "correction factor" - this is the ISO weekday * number of January 4 of the year, plus 3. Note that this is * explicitly defined as Jan 4 *Gregorian* by the standard, so * this isn't relative to the calendar we're parsing for. */ caldate_t jan4(yy, 1, 4); int corr = jan4.iso_weekday() + 3; /* * Set the date to "January 'ord'", where 'ord' is the ISO * ordinal day number calculated from the weekday and * correction factor. This won't necessarily be a valid date * in January, but caldate_t handles overflows in the day by * carrying into the month and year as needed. */ caldate_t cd(yy, 1, w - corr); return cd.dayno(); } else { /* set the calendar date to our year, month, and day */ cal->set_date(yy, mm, dd); /* return the day number from the calendar date */ return cal->dayno(); } } /* compute the time of day */ int32_t daytime() const { return hh*60*60*1000 + mi*60*1000 + ss*1000 + ms; } /* * Set defaults for the various components. These will set the * component only if it hasn't been set to a valid value already. */ void def_year(int y) { if (!has_year()) yy = y; } void def_month(int m) { if (!has_month()) mm = m; } void def_day(int d) { if (!has_day()) dd = d; } void def_hour(int h) { if (!has_hour()) hh = h; } void def_minute(int m) { if (!has_minute()) mi = m; } void def_second(int s) { if (!has_second()) ss = s; } void def_msec(int m) { if (!has_msec()) ms = m; } void def_tzofs(int t) { if (!has_tzofs()) tzofs = t; } void def_tz(CVmTimeZone *z) { if (!has_tz()) tz = z; } /* default to the local time zone */ void def_local_tz(VMG0_) { if (!has_tz()) tz = G_tzcache->get_local_zone(vmg0_); } /* determine if the various elements are set */ int has_era() const { return era != 0; } int has_year() const { return yy != INT_MIN; } int has_month() const { return mm != INT_MIN; } int has_day() const { return dd != INT_MIN; } int has_iso_week() const { return w != INT_MIN; } int has_ampm() const { return ampm != 0; } int has_hour() const { return hh != INT_MIN; } int has_minute() const { return mi != INT_MIN; } int has_second() const { return ss != INT_MIN; } int has_msec() const { return ms != INT_MIN; } int has_tzofs() const { return tzofs != INT_MIN; } int has_tz() const { return tz != 0; } /* the calendar we're using to parse the date */ multicaldate_t *cal; /* era: 1 = '+', 2 = AD, -1 = '-', -2 = BC */ int era; /* date - year, month, day */ int yy; int mm; int dd; /* * ISO 8601 week+day - this is the value W*7+D from a date in ISO 8601 * format, where W is the week number (1-53) and D is the day of week * number (1-7). */ int w; /* flag: the year was specified without a century */ int yy_needs_century; /* time - hour, minute, second, milliseconds on 24-hour clock */ int ampm; int hh; int mi; int ss; int ms; /* timezone */ CVmTimeZone *tz; /* time zone by GMT offset, in milliseconds */ int tzofs; /* string capture items for the various components */ date_parse_string pera, pyy, pmm, pdd, pdoy, pw; date_parse_string pampm, phh, pmi, pss, pms, punix, ptz; }; /* match a format element in a template string */ static inline int match_fmt(const char *fmt, const char *fmtend, const char *ref) { size_t len = strlen(ref); return (len == (size_t)(fmtend - fmt) && memcmp(ref, fmt, len) == 0); } /* * match a digit string; the template gives pairs of digits with the bounds * to match - e.g., "09" matches one digit from 0 to 9, "12" matches one * digit from 1 to 2, "0209" matches 00 to 29, etc. We can also match ',' * to a period or comma. */ static int match_digits(int &acc, const char *&str, size_t &len, const char *tpl, date_parse_string &capture) { /* set up a private pointer to the string */ const char *p = str; size_t rem = len; /* match each template pair */ acc = 0; for ( ; *tpl != '\0' ; tpl += 2, ++p, --rem) { /* check for the special match to a period or comma */ if (*tpl == ',') { /* back up to make up for the normal double increment */ --tpl; /* allow it to match, but don't require it */ if (rem != 0 && (*p == ',' || *p == '.')) continue; /* it doesn't match; simply say on the same input character */ --p, ++rem; continue; } /* if this input char isn't a digit, we don't have a match */ if (rem == 0 || !is_digit(*p)) return FALSE; /* if this digit is out of range, it's not a match */ if (*p < tpl[0] || *p > tpl[1]) return FALSE; /* matched this digit - add it to the accumulator */ acc *= 10; acc += value_of_digit(*p); } /* set the capture data */ capture.p = str; capture.len = p - str; /* advance the caller's string pointers past the match */ str = p; len = rem; /* return success */ return TRUE; } /* * match literal text */ static int match_lit(const char *&str, size_t &len, const char *lit, size_t litlen, date_parse_string &capture) { /* if there's no literal, there's no match */ if (lit == 0) return 0; /* if the template starts with "^", it means do not fold case */ int fold_case = TRUE; if (litlen != 0 && *lit == '^') fold_case = FALSE, ++lit, --litlen; /* try matching the template text */ size_t matchlen; if (fold_case) { /* compare the leading substring of 'str' with case folding */ if (t3_compare_case_fold(lit, litlen, str, len, &matchlen) != 0) return FALSE; } else if (len >= litlen) { /* compare exactly as given with no case folding */ if (memcmp(lit, str, litlen) != 0) return FALSE; matchlen = litlen; } else { /* it's too short to match */ return FALSE; } /* get a pointer to the next character after the end of the match */ utf8_ptr p((char *)str + matchlen); size_t rem = len - matchlen; /* make sure we ended on a suitable boundary */ if (rem != 0 && litlen != 0) { /* get the last character of the matched text */ wchar_t last = p.getch_before(1); wchar_t next = p.getch(); /* if we ended on alphabetic, make sure the next is non-alpha */ if (isalpha(last) && isalpha(next)) return FALSE; /* if we ended on a number, maek sure the next is non-numeric */ if (isdigit(last) && isdigit(next)) return FALSE; } /* set the capture data */ capture.p = str; capture.len = p.getptr() - str; /* advance the caller's string pointers past the match */ str = p.getptr(); len = rem; /* return success */ return TRUE; } static int match_lit(const char *&str, size_t &len, const char *lit, date_parse_string &capture) { return match_lit(str, len, lit, lit != 0 ? strlen(lit) : 0, capture); } /* * Match literal text from a list. The list is given as a string of * comma-separated elements, ending with a semicolon. Elements can have * aliases specified with '=', as in "sep=sept, ...". Spaces after commas * are ignored. */ static int match_list(int &acc, const char *&str, size_t &len, const char *lst, size_t lstlen, date_parse_string &capture) { /* no matches yet */ int bestlen = 0; int bestidx = 0; /* skip leading spaces */ for ( ; lstlen != 0 && is_space(*lst) ; ++lst, --lstlen) ; /* keep going until we're out of list */ for (int idx = 1 ; lstlen != 0 ; ) { /* find the next delimiter */ const char *p = lst; size_t rem = lstlen; for ( ; rem != 0 && *p != '=' && *p != ',' && *p != ';' ; ++p, --rem) ; /* try matching this text */ const char *curstr = str; size_t curlen = len; date_parse_string part; if (p - lst != 0 && match_lit(curstr, curlen, lst, p - lst, part)) { /* if this is the best one so far, remember it */ if (curstr - str > bestlen) { bestlen = curstr - str; bestidx = idx; } } /* * If we're at an '=', an alias for this same item follows, so keep * going without upping the index. If we're at a comma, we're on * to the next item. If we're at a ';' or the end of the string, * it's the end of the list, so we've failed to find a match. */ if (rem == 0 || *p == ';') { /* end of the list */ break; } else if (*p == ',') { /* next list item follows - up the index */ ++idx; /* skip the comma and any spaces before the next item */ for (++p, --rem ; rem != 0 && is_space(*p) ; ++p, --rem) ; } else if (*p == '=') { /* alias - just skip the '=' */ ++p, --rem; } /* on to the next item */ lst = p; lstlen = rem; } /* if we found a match, return it */ if (bestlen != 0) { capture.p = str; capture.len = bestlen; acc = bestidx; str += bestlen; len -= bestlen; return TRUE; } else return FALSE; } /* * match a timezone offset - [GMT] +/- hh [:][mi] [:][ss] */ static int match_tzofs(VMG_ date_parse_result *res, const char *&str, size_t &len) { const char *p = str; size_t rem = len; /* first, skip "gmt" if present */ date_parse_string part; int gmtlit = match_lit(p, rem, "^GMT", part); /* get the sign */ if (rem == 0 || (*p != '+' && *p != '-')) return FALSE; int s = (*p == '+' ? 1 : -1); /* skip the sign */ ++p, --rem; /* * Get the "hh". If the "GMT" literal was present, allow a single * digit; otherwise require the two-digit format. */ int hh; if (!match_digits(hh, p, rem, "0509", part) && (!gmtlit || !match_digits(hh, p, rem, "09", part))) return FALSE; /* * check for ":" - if present, the minutes must be present, otherwise * they're optional */ int mi = 0; if (rem != 0 && *p == ':') { /* skip the ":", then require the minutes */ ++p, --rem; if (!match_digits(mi, p, rem, "0509", part)) return FALSE; } else { /* no ":", so the minuts are optional */ match_digits(mi, p, rem, "0509", part); } /* likewise, check for ":ss" */ int ss = 0; if (rem != 0 && *p == ':') { ++p, --rem; if (!match_digits(ss, p, rem, "0509", part)) return FALSE; } else { match_digits(ss, p, rem, "0509", part); } /* success - figure the offset in seconds */ int32_t ofs = s * ((hh*60*60) + (mi*60) + ss); /* look up the zone by offset */ res->tz = G_tzcache->get_gmtofs_zone(vmg_ ofs); /* also set the explicit offset value (in milliseconds) */ res->tzofs = ofs * 1000; /* set the capture data */ res->ptz.p = str; res->ptz.len = p - str; /* advance the caller's vars past the parsed tzofs and return success */ str = p; len = rem; return TRUE; } /* * Match a timezone by name */ static int match_tzname(VMG_ date_parse_result *res, const char *&str, size_t &len) { /* check for a zone name string ("America/Los_Angeles", etc) */ if (len != 0 && isalpha(*str)) { const char *p = str; size_t rem = len; for ( ; rem != 0 ; ++p, --rem) { /* allow letters, or / or _ if followed by a letter */ if (isalpha(*p) || ((*p == '_' || *p == '/') && rem > 1 && isalpha(p[1]))) continue; /* otherwise we're done */ break; } /* if it contains a slash, look up the zoneinfo database name */ CVmTimeZone *tz = 0; if (lib_strnchr(str, p - str, '/') != 0) { if ((tz = G_tzcache->get_db_zone(vmg_ str, p - str)) != 0) { /* success - this is a valid timezone name */ res->ptz.p = str; res->ptz.len = p - str; str = p; len = rem; res->tz = tz; return TRUE; } } /* didn't find a zone name; try an abbreviation */ int32_t tzofs; tz = G_tzcache->get_zone_by_abbr(vmg_ &tzofs, str, p - str); if (tz != 0) { /* * Success - we found an abbreviation; this usually gives us * both a timezone and a specific offset, since an abbreviation * is specific to standard or daylight time. However, some * abbreviations don't specify an offset, since they're used in * the same zone for both standard and daylight time (e.g., * Australian EST); these are flagged by tzofs coming back as * INT32MINVAL. */ res->ptz.p = str; res->ptz.len = p - str; str = p; len = rem; res->tz = tz; if (tzofs != INT32MINVAL) res->tzofs = tzofs; return TRUE; } } /* not a time zone */ return FALSE; } /* * Parse a date/time string against a given template */ int CVmObjDate::parse_string_fmt(VMG_ date_parse_result *res, const char *&str, size_t &len, const char *fmt, size_t fmtl, CVmDateLocale *lc) { /* skip leading spaces and tabs in the source string */ for ( ; len != 0 && is_space(*str) ; ++str, --len) ; /* run through the format string */ while (len != 0) { /* skip leading spaces in the format string */ for ( ; fmtl != 0 && *fmt == ' ' ; --fmtl, ++fmt) ; /* stop if we're at the end of the string */ if (fmtl == 0) break; /* find the next space or end of string */ const char *sp = lib_strnchr(fmt, fmtl, ' '); sp = (sp != 0 ? sp : fmt + fmtl); /* parse the type */ switch (fmt[0]) { case 'd': if (match_fmt(fmt, sp, "d")) { /* one- or two-digit day of month */ int acc; if (match_digits(acc, str, len, "0209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd) || match_digits(acc, str, len, "09", res->pdd)) res->dd = acc; else return FALSE; } else if (match_fmt(fmt, sp, "dd")) { /* two-digit day of month */ int acc; if (match_digits(acc, str, len, "0009", res->pdd) || match_digits(acc, str, len, "1209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd)) res->dd = acc; else return FALSE; } else if (match_fmt(fmt, sp, "ddth")) { /* one/two-digit day of month, plus optional ordinal suffix */ int acc; if (match_digits(acc, str, len, "0209", res->pdd) || match_digits(acc, str, len, "3301", res->pdd) || match_digits(acc, str, len, "09", res->pdd)) res->dd = acc; else return FALSE; /* get the locale ordinal for the digit */ size_t ordl; const char *ord = lc->index_list( vmg_ ordl, LC_ORDSUF, ordinal_index(acc), TRUE); /* compare it */ date_parse_string pdd; if (match_lit(str, len, ord, ordl, pdd)) { /* matched - add this into the capture data */ res->pdd.len += pdd.len; } } else if (match_fmt(fmt, sp, "doy")) { /* day of year */ int acc; if (match_digits(acc, str, len, "000019", res->pdoy) || match_digits(acc, str, len, "001909", res->pdoy) || match_digits(acc, str, len, "120909", res->pdoy) || match_digits(acc, str, len, "330509", res->pdoy) || match_digits(acc, str, len, "336606", res->pdoy)) { /* * set the date to January 'doy' - this isn't * necessarily a valid calendar date in January, but * caldate_t handles overflows in the day by carrying * to the month and year as needed */ res->mm = 1; res->dd = acc; } } else goto other; break; case 'm': if (match_fmt(fmt, sp, "month")) { /* * Month by long name, short name, or Roman numeral. Get * the locale strings for the month names; the Roman * numerals are fixed, so we can set up a static string for * those. */ size_t mm_long_len, mm_abbr_len; const char *mm_long = lc->get(vmg_ mm_long_len, LC_MONTH); const char *mm_abbr = lc->get(vmg_ mm_abbr_len, LC_MON); static const char *romans = "^I,^II,^III,^IV,^V,^VI,^VII,^VIII,^IX,^X,^XI,^XII;"; /* try each variation */ int acc; if (match_list(acc, str, len, mm_long, mm_long_len, res->pmm) || match_list( acc, str, len, mm_abbr, mm_abbr_len, res->pmm) || match_list( acc, str, len, romans, strlen(romans), res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mon")) { /* month by short name */ size_t mm_abbr_len; const char *mm_abbr = lc->get(vmg_ mm_abbr_len, LC_MON); int acc; if (match_list(acc, str, len, mm_abbr, mm_abbr_len, res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "m")) { /* one- or two-digit month */ int acc; if (match_digits(acc, str, len, "0009", res->pmm) || match_digits(acc, str, len, "1102", res->pmm) || match_digits(acc, str, len, "09", res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mm")) { /* two-digit month */ int acc; if (match_digits(acc, str, len, "0009", res->pmm) || match_digits(acc, str, len, "1102", res->pmm)) res->mm = acc; else return FALSE; /* specifying a month makes the default day the 1st */ res->def_day(1); } else if (match_fmt(fmt, sp, "mi")) { /* two-digit minutes */ int acc; if (match_digits(acc, str, len, "0509", res->pmi)) res->mi = acc; else return FALSE; /* specifying the minute makes the default ss.frac zero */ res->def_second(0); res->def_msec(0); } else goto other; break; case 'W': if (match_fmt(fmt, sp, "W")) { /* * ISO week - a two-digit number giving the week of the * year, optionally followed by a day number */ int w; if (match_digits(w, str, len, "0019", res->pw) || match_digits(w, str, len, "1409", res->pw) || match_digits(w, str, len, "5503", res->pw)) { /* check for a weekday following */ int d = 1; if (len >= 2 && *str == '-' && strchr("1234567", str[1]) != 0) { d = value_of_digit(str[1]); str += 2, len -= 2; res->pw.len += 2; } else if (len >= 1 && strchr("1234567", str[0]) != 0) { d = value_of_digit(str[0]); str += 1, len -= 1; res->pw.len += 1; } /* matched */ res->w = w*7 + d; return TRUE; } else return FALSE; } else goto other; break; case 'y': if (match_fmt(fmt, sp, "y")) { /* one- to seven-digit year */ int acc; if (match_digits(acc, str, len, "09,090909,090909", res->pyy) || match_digits(acc, str, len, "090909,090909", res->pyy) || match_digits(acc, str, len, "0909,090909", res->pyy) || match_digits(acc, str, len, "09090909", res->pyy) || match_digits(acc, str, len, "090909", res->pyy)) res->yy = acc; else if (match_digits(acc, str, len, "0909", res->pyy) || match_digits(acc, str, len, "09", res->pyy)) { res->yy = acc; res->yy_needs_century = TRUE; if (res->yy >= 5879650 || res->yy <= -5879650) err_throw(VMERR_NUM_OVERFLOW); } else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "yy")) { /* two-digit year; century comes from the reference year */ int acc; if (match_digits(acc, str, len, "0909", res->pyy)) { res->yy = acc; res->yy_needs_century = TRUE; } else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "yyyy")) { /* four-digit year */ int acc; if (match_digits(acc, str, len, "09090909", res->pyy)) res->yy = acc; else return FALSE; /* specifying the year makes the default date 1/1 */ res->def_month(1); res->def_day(1); } else if (match_fmt(fmt, sp, "ye")) { /* * Year with optional era designator (AD/BC). The era can * come before or after the year, with spaces in between. */ size_t era_len; const char *era_lst = lc->get(vmg_ era_len, LC_ERA); int idx; int era = 0; if (match_list(idx, str, len, era_lst, era_len, res->pera)) { /* note the era - 2 for AD, -2 for BC */ res->era = era = (idx == 1 ? 2 : -2); /* allow spaces */ for ( ; len != 0 && isspace(*str) ; ++str, --len) ; } /* match the year - this part is required */ int acc; if (match_digits(acc, str, len, "09,090909,090909", res->pyy) || match_digits(acc, str, len, "090909,090909", res->pyy) || match_digits(acc, str, len, "0909,090909", res->pyy) || match_digits(acc, str, len, "09090909", res->pyy) || match_digits(acc, str, len, "090909", res->pyy) || match_digits(acc, str, len, "0909", res->pyy) || match_digits(acc, str, len, "09", res->pyy)) res->yy = acc; else return FALSE; /* we accept large year numbers, so check range */ if (res->yy >= 5879650 || res->yy <= -5879650) err_throw(VMERR_NUM_OVERFLOW); /* if we didn't have a prefixed era, check for a postfix era */ if (era == 0) { /* skip optional spaces */ const char *str2 = str; size_t len2 = len; for ( ; len2 != 0 && isspace(*str2) ; ++str2, --len2) ; /* check for the era */ if (match_list( idx, str2, len2, era_lst, era_len, res->pera)) { /* found it - set the era identifier */ res->era = era = (idx == 1 ? 2 : -2); /* advance past it in the main string */ str = str2; len = len2; } } /* * if we matched one or two digits, with no era, we need a * default century */ if (era == 0 && res->pyy.len <= 2) res->yy_needs_century = TRUE; } else goto other; break; case 'e': if (match_fmt(fmt, sp, "era")) { /* AD/BC era designator */ size_t era_len; const char *era_lst = lc->get(vmg_ era_len, LC_ERA); int idx; if (match_list(idx, str, len, era_lst, era_len, res->pera)) { /* set the era to 2 for AD or -2 for BC */ res->era = (idx == 1 ? 2 : -2); } else return FALSE; } else goto other; break; case 'h': if (match_fmt(fmt, sp, "h")) { /* one- or two-digit hours */ int acc; if (match_digits(acc, str, len, "0019", res->phh) || match_digits(acc, str, len, "1102", res->phh) || match_digits(acc, str, len, "09", res->phh)) res->hh = acc; else return FALSE; /* specifying the hour makes the default mm:ss.frac zero */ res->def_minute(0); res->def_second(0); res->def_msec(0); } else if (match_fmt(fmt, sp, "hh")) { /* two-digit hours */ int acc; if (match_digits(acc, str, len, "0109", res->phh) || match_digits(acc, str, len, "2204", res->phh)) res->hh = acc; else return FALSE; /* specifying the hour makes the default minute zero */ res->def_minute(0); res->def_second(0); res->def_msec(0); } else goto other; break; case 'i': if (match_fmt(fmt, sp, "i")) { /* one- or two-digit minutes */ int acc; if (match_digits(acc, str, len, "0509", res->pmi) || match_digits(acc, str, len, "09", res->pmi)) res->mi = acc; else return FALSE; /* specifying the minute makes the default ss.frac zero */ res->def_second(0); res->def_msec(0); } else goto other; break; case 's': if (match_fmt(fmt, sp, "ss")) { /* two-digit seconds */ int acc; if (match_digits(acc, str, len, "0509", res->pss)) res->ss = acc; else return FALSE; /* specifying the second makes the default fraction zero */ res->def_msec(0); } else if (match_fmt(fmt, sp, "s")) { /* one- or two-digit seconds */ int acc; if (match_digits(acc, str, len, "0509", res->pss) || match_digits(acc, str, len, "09", res->pss)) res->ss = acc; else return FALSE; /* specifying the second makes the default fraction zero */ res->def_msec(0); } else if (match_fmt(fmt, sp, "ssfrac")) { /* fractional seconds */ if (len >= 1 && isdigit(str[1])) { /* * parse digits - accumulate up to four digits so that * we can round to milliseconds */ res->pms.p = str; res->pms.len = 0; int acc = 0, mul = 10000, trailing = 0; for ( ; len != 0 && isdigit(*str) ; ++str, --len, ++res->pms.len, mul /= 10) { /* * add the next digit only if significant, * otherwise note any non-zero trailing digits */ if (mul > 1) acc *= 10, acc += value_of_digit(*str); else if (*str != '0') trailing = 1; } /* apply the multiplier if non-zero */ if (mul != 0) acc *= mul; /* we have 10*ms - divide by 10 to get ms */ int lastdig = acc % 10; acc /= 10; /* * round up if the last digit is 6+, 5+ with a non-zero * digit following, or exactly 5 with an odd second * digit */ if (lastdig > 5 || (lastdig == 5 && (trailing || ((acc % 10) & 1) != 0))) ++acc; /* store as integer milliseconds */ res->ms = acc; } else return FALSE; } else goto other; break; case 'u': if (match_fmt(fmt, sp, "unix")) { /* * Unix timestamp value - any number of digits giving a * positive or negative offset in seconds from the Unix * Epoch (1/1/1970) */ int s = 1; if (len >= 2 && str[0] == '-' && is_digit(str[1])) { res->punix.p = str, res->punix.len = 1; s = -1, ++str, --len; } else if (len >= 1 && is_digit(str[0])) { res->punix.p = str, res->punix.len = 0; } else return FALSE; /* * Parse digits; use a double in case we have more than a * 32-bit int's worth. (We only work in whole integers * here, but we might need more precision than an int32. A * double is enough for any day we can represent: we have a * 32-bit day; 86400 seconds per day is about 17 additional * bits, for a total of 49. An IEEE double has a 52-bit * mantissa, so a double can exactly represent a Unix * timestamp for any day number we can represent.) */ double acc = 0.0; for ( ; len > 0 && is_digit(*str) ; ++str, --len, ++res->punix.len) { acc *= 10.0; acc += value_of_digit(*str); } /* apply the sign */ acc *= s; /* split into days and seconds */ double days = floor(acc / (24.*60.*60.)); int32_t secs = (int32_t)fmod(acc, 24.*60.*60.); /* make sure it fits an int32 */ if (days > INT32MAXVAL) return FALSE; /* * convert to a calendar date, adjusting from the Unix * Epoch to our Epoch */ res->cal->set_dayno( (int32_t)days + caldate_t::UNIX_EPOCH_DAYNO); /* set the time and day in the parse results */ res->yy = res->cal->y(); res->mm = res->cal->m(); res->dd = res->cal->d(); res->hh = secs/(60*60); res->mi = (secs/60) % 60; res->ss = secs % 60; res->ms = 0; /* Unix timestamps are relative to UTC */ res->tzofs = 0; } else goto other; break; case 'a': if (match_fmt(fmt, sp, "ampm")) { /* month by short name */ size_t ampm_len; const char *ampm_str = lc->get(vmg_ ampm_len, LC_AMPM); int acc; if (match_list(acc, str, len, ampm_str, ampm_len, res->pampm)) res->ampm = acc; else return FALSE; } else goto other; break; case 't': if (match_fmt(fmt, sp, "tz")) { /* timezone by name, abbreviation, or offset */ if (!match_tzofs(vmg_ res, str, len) && !match_tzname(vmg_ res, str, len)) return FALSE; } else goto other; break; case 'g': if (match_fmt(fmt, sp, "gmtofs")) { /* timezone offset from GMT */ if (!match_tzofs(vmg_ res, str, len)) return FALSE; } else goto other; break; default: other: if (match_fmt(fmt, sp, "+-")) { /* * Astronomical-notation era designator - this is the * system where the year before AD 1 is Year 0, the year * before that -1, etc. Positive years can optionally have * a plus sign. */ if (match_lit(str, len, "+", res->pera)) res->era = 1; else if (match_lit(str, len, "-", res->pera)) res->era = -1; else return FALSE; } else { /* * Anything else is a list of literals to match. If it * ends with '*', we can match zero or more; if it ends * with '+', we can match one or more; otherwise we match * exactly one. */ const char *fmtend = sp; char suffix = *(sp-1); int mincnt = (suffix == '*' || suffix == '?' ? 0 : 1); int maxcnt = (suffix == '*' || suffix == '+' ? 65535 : 1); fmtend -= (strchr("*?+", suffix) != 0 ? 1 : 0); /* scan the character list */ int cnt = 0; utf8_ptr strp((char *)str); while (cnt < maxcnt && len != 0) { int found = FALSE; utf8_ptr fmtp((char *)fmt); for ( ; fmtp.getptr() < fmtend ; fmtp.inc()) { /* get the current template and source characters */ wchar_t fc = fmtp.getch(); wchar_t sc = strp.getch(); /* * check for specials: \ quotes the next, _ is a * space */ if (fc == '\\' && fmtp.getptr() < fmtend) { fmtp.inc(); fc = fmtp.getch(); } else if (fc == '_') { fc = ' '; } /* check for a match */ if (fc == sc) { found = TRUE; ++cnt; strp.inc(&len); str = strp.getptr(); break; } } if (!found) break; } /* if we didn't match the minimum number, it's a mismatch */ if (cnt < mincnt) return FALSE; } break; } /* advance past this field */ fmtl -= sp - fmt; fmt = sp; } /* if we didn't exhaust the format, we have no match */ if (fmtl != 0) return FALSE; /* we matched the format - skip trailing spaces */ for ( ; len != 0 && is_space(*str) ; ++str, --len) ; /* return success */ return TRUE; } /* custom/builtin format string iterator */ struct format_iter { format_iter(VMG_ const char **builtin, size_t builtin_cnt, const vm_val_t *custom) { /* save the source lists */ this->builtin = builtin; this->builtin_cnt = builtin_cnt; this->custom = custom; /* presume we'll start in the custom list */ this->mode = 0; /* figure how many builtin items we have, if any */ if (custom == 0 || custom->typ == VM_NIL) { /* no custom items - just use the builtin list */ this->custom_cnt = 0; this->mode = 1; } else if (custom->get_as_string(vmg0_) != 0) this->custom_cnt = 1; else if (custom->is_listlike(vmg0_)) this->custom_cnt = custom->ll_length(vmg0_); else err_throw(VMERR_BAD_TYPE_BIF); /* start at the top of each list */ this->bidx = 0; this->cidx = 0; } /* get the next string */ const char *next(VMG_ size_t &len) { /* assume we won't find anythign */ len = 0; /* keep going until we find something */ for (;;) { if (mode == 0) { /* custom list - if exhausted, we're done */ if (cidx >= custom_cnt) return 0; /* get the next custom item, according to the source type */ vm_val_t ele; if (custom->is_listlike(vmg0_)) { /* get the next list element */ custom->ll_index(vmg_ &ele, ++cidx); /* if it's nil, switch to the builtin list */ if (ele.typ == VM_NIL) { mode = 1; continue; } } else { /* it must be a single string */ ele = *custom; } /* retrieve the string value */ const char *str = ele.get_as_string(vmg0_); if (str != 0) { len = vmb_get_len(str); str += VMB_LEN; } return str; } else { /* builtin list - if exhausted, return to the custom list */ if (bidx >= builtin_cnt) { mode = 0; continue; } /* return the next builtin item */ len = strlen(builtin[bidx]); return builtin[bidx++]; } } } /* current list we're reading from - 0=custom, 1=builtin */ int mode; /* builtin list, and current index in list */ const char **builtin; size_t builtin_cnt; size_t bidx; /* custom string or list, and current index in list */ const vm_val_t *custom; size_t custom_cnt; size_t cidx; }; /* * Parse a date string in a constructor argument. */ int CVmObjDate::parse_date_string( VMG_ int32_t &dayno, int32_t &daytime, const char *str, const vm_val_t *custom, CVmDateLocale *lc, multicaldate_t *cal, int32_t refday, int32_t reftime, CVmTimeZone *reftz, date_parse_result *resultp, date_parse_string *fmtlist, int *nfmtlist) { /* get the string length and buffer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* format list */ static const char *fmt[] = { /* US-style dates, with the month first */ "[us]m / d / y", "[us]m / d", "[us]m - d", "[us]m . d", "[us]m .\t- d .\t- y", /* European-style date formats, with day/month order */ "[eu]d / m / y", "[eu]d / m", "[eu]d - m", "[eu]d . m", "[eu]d .\t- m .\t- y", /* match some special computer formats first */ "yyyy mm dd", /* 8-digit year month day */ "yyyy .? doy", /* PostreSQL year with day-of-year */ /* universal date formats */ "m / y", "m - y", "month _.\t-* ddth _,.\t+ ye", "ddth _.\t-* month _,.\t-* ye", "month _.\t-* ddth", "ddth _.\t-* month", "month - dd - ye", "month _\t.-* y", "month", "+- y - mm - dd", "y / m / d", "y - m - d", "y / m", "y - m", "ye _\t.-* month _\t.-* d", "ye _\t.-* month", "ye - month - dd", "yyyy", "era _* y", "y _* era", /* time formats */ "h _? ampm", "h : mi", "h .: mi _? ampm", "h .: mi .: ss _? ampm", "h : mi : ss", "h : mi : ss .: ssfrac _? ampm", "h : mi : ss .: ssfrac", "tT? hh .: mi", "tT? hh mi", "tT? hh .: mi .: ss", "tT? hh mi SS", "tT? hh .: mi .: ss _? tz", "tT? hh .: mi .: ss . ssfrac", "tz", /* special formats (ISO, other software, etc) */ "d / mon / yyyy : hh : mi : ss _ gmtofs", /* Unix log files */ "yyyy : mm : dd _ hh : mi : ss", /* EXIF */ "yyyy -? \\W W", /* ISO year with ISO week */ "yyyy - mm - dd \\T hh : mi : ss . ssfrac", /* SOAP */ "yyyy - mm - dd \\T hh : mi : ss . ssfrac gmtofs", /* SOAP */ "@ unix", /* Unix timestamp */ "yyyy mm dd \\T hh : mi : ss", /* XMLRPC */ "yyyy mm dd \\t hh mi ss", /* XMLRPC - compact */ "yyyy - m - d \\T h : i : s" /* WDDX */ }; /* get the template filter in "[xx]" format */ size_t filterlen; const char *filter = lc->get(vmg_ filterlen, LC_PARSE_FILTER); /* keep separate results for each pass */ date_parse_result result(cal); /* * Run through the format list as many times as it takes to either * consume the whole source string, or fail to find a matching format. * We have multiple format parts that can be specified individually or * in combination (time, date, time + date, date + time, etc). */ int fcapi; for (fcapi = 0 ; len != 0 ; ++fcapi) { /* we haven't found a best result for this pass yet */ int bestlen = 0; date_parse_result bestres(cal); /* search the list for a match */ format_iter fi(vmg_ fmt, countof(fmt), custom); size_t curfmtl; const char *curfmt; while ((curfmt = fi.next(vmg_ curfmtl)) != 0) { /* set up at the current position */ const char *curstr = str; size_t curlen = len; /* remember the original format string */ const char *curfmt0 = curfmt; size_t curfmtl0 = curfmtl; /* if this is a "[us]" or "[eu]" local entry, filter it */ if (curfmtl >= 3 && curfmt[0] == '[') { /* get the ']' */ const char *rb = lib_strnchr(curfmt+1, curfmtl-1, ']'); /* * if we found it, and the contents of the brackets don't * match the locale filter string, filter out this format */ if (rb != 0 && (rb - curfmt - 1 != (int)filterlen || memicmp(curfmt + 1, filter, filterlen) != 0)) continue; /* skip the filter string */ curfmtl -= rb - curfmt + 1; curfmt = rb + 1; } /* start with the results from previous passes */ date_parse_result curres(result); /* try matching this format at the current position */ if (parse_string_fmt(vmg_ &curres, curstr, curlen, curfmt, curfmtl, lc)) { /* if this is the longest match of this pass, keep it */ int reslen = curstr - str; if (reslen > bestlen) { /* remember this as the best result so far */ bestlen = reslen; bestres = curres; /* remember the format string it matches, if desired */ if (nfmtlist != 0 && fcapi < *nfmtlist) fmtlist[fcapi].set(curfmt0, curfmtl0); } } } /* if we didn't find a match on this round, we've failed */ if (bestlen == 0) return FALSE; /* skip what we matched for the best result of this pass */ str += bestlen; len -= bestlen; /* keep the best result for this pass */ result = bestres; /* skip spaces and other separator characters */ for ( ; len != 0 && strchr(" \t;,:", *str) != 0 ; ++str, --len) ; } /* default to the reference time zone */ result.def_tz(reftz); /* adjust the reference time to the appropriate local time zone */ result.utc_to_local(refday, reftime); /* set missing date elements to the corresponding reference date values */ result.def_date(refday); /* set any missing time elements to midnight or zero past the hour/min */ result.def_time(0, 0, 0, 0); /* convert to our internal day and time representation */ dayno = result.dayno(); daytime = result.daytime(); caldate_t::normalize(dayno, daytime); /* translate the final time value from the specified local time to UTC */ result.local_to_utc(dayno, daytime); /* copy back the results if the caller's interested */ if (resultp != 0) memcpy(resultp, &result, sizeof(result)); /* tell the caller how many format strings we matched, if desired */ if (nfmtlist != 0) *nfmtlist = fcapi; /* success */ return TRUE; } /* * Parse constructor arguments for a (...Y,M,D) or (...Y,M,D,HH,MI,SS,MS) * argument list. This parses the arguments from the stack and fills in * the day number and time of day values accordingly. We don't make any * time zone adjustments; we just return the value as specified. */ static void parse_ymd_args(VMG_ int argofs, int argc, int32_t &dayno, int32_t &daytime) { /* get the year, month, and day */ int32_t y = G_stk->get(argofs + 0)->num_to_int(vmg0_); int32_t m = G_stk->get(argofs + 1)->num_to_int(vmg0_); int32_t d = G_stk->get(argofs + 2)->num_to_int(vmg0_); /* figure the day number for the given calendar date */ caldate_t cd(y, m, d); dayno = cd.dayno(); /* * if this is the 7-argument version, get the time; otherwise it's * implicitly midnight on the given date */ daytime = 0; if (argc == 7) { /* get the hour and minute as integers, and seconds */ int hh = G_stk->get(argofs + 3)->num_to_int(vmg0_); int mm = G_stk->get(argofs + 4)->num_to_int(vmg0_); int ss = G_stk->get(argofs + 5)->num_to_int(vmg0_); int ms = G_stk->get(argofs + 6)->num_to_int(vmg0_); /* * Combine the time components into 'daytime' in milliseconds. * This could be a very large number, so work in double until we * can normalize it. Note that we're working in whole numbers only * (no fractions), so a double will be exact - all we're worried * about here is the extra integer precision it can hold, not the * fractional part. */ double dt = hh*60.*60.*1000. + mm*60.*1000. + ss*1000. + ms; /* figure the overflow into the day number (and make sure it fits) */ double dd = floor(dt / (24.*60.*60.*1000.)); dt = fmod(dt, 24.*60.*60.*1000.); /* apply the overflow to the day number, and make sure it fits */ dd += dayno; if (dd < INT32MINVAL || dd > INT32MAXVAL) err_throw(VMERR_NUM_OVERFLOW); /* it's all normalized and in range, so convert back to int32 */ dayno = (int32_t)dd; daytime = (int32_t)dt; } /* normalize the result */ caldate_t::normalize(dayno, daytime); } /* ------------------------------------------------------------------------ */ /* * Create with a given timestamp */ vm_obj_id_t CVmObjDate::create(VMG_ int in_root_set, int32_t dayno, int32_t daytime) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjDate(vmg_ dayno, daytime); return id; } /* ------------------------------------------------------------------------ */ /* * Create from an os_time_t timestamp */ vm_obj_id_t CVmObjDate::create_from_time_t(VMG_ int in_root_set, os_time_t t) { /* * turn the time_t into days and milliseconds after the Unix Epoch (use * doubles to ensure we don't overflow an int) */ double d = (double)t; double dn = floor(d / (24*60*60)); double dms = (d - (dn * (24*60*60))) * 1000; /* adjust to our internal Epoch */ dn += caldate_t::UNIX_EPOCH_DAYNO; /* we have the time in our own format now, so go create the object */ return create(vmg_ in_root_set, (int32_t)dn, (int32_t)dms); } /* ------------------------------------------------------------------------ */ /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjDate::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { int32_t dayno; int32_t daytime; const char *str; /* check for the various argument lists */ if (argc == 0) { /* no arguments - get the current date and time in the local zone */ caldate_t::now(dayno, daytime); } else if ((argc >= 1 && argc <= 3) && (str = G_stk->get(0)->get_as_string(vmg0_)) != 0) { /* * The first argument is a date in string format. The optional * second argument is the reference time zone; the optional third * argument is the reference date. */ /* get the reference time zone */ CVmTimeZone *reftz; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) reftz = get_tz_arg(vmg_ 1, argc); else reftz = G_tzcache->get_local_zone(vmg0_); /* get the reference date */ int32_t refday, reftime; if (argc >= 3 && G_stk->get(2)->typ != VM_NIL) { /* get the reference Date object */ CVmObjDate *refdate = vm_val_cast(CVmObjDate, G_stk->get(2)); if (refdate == 0) err_throw(VMERR_BAD_TYPE_BIF); /* take the reference point from the Date object */ refday = refdate->get_ext()->dayno; reftime = refdate->get_ext()->daytime; } else { /* no reference date, so use the current time */ caldate_t::now(refday, reftime); } /* parse the format */ CVmDateLocale lc Pvmg0_P; gregcaldate_t cal; if (!parse_date_string(vmg_ dayno, daytime, str, 0, &lc, &cal, refday, reftime, reftz, 0, 0, 0)) err_throw(VMERR_BAD_VAL_BIF); } else if (argc == 2 && G_stk->get(0)->is_numeric(vmg0_) && (str = G_stk->get(1)->get_as_string(vmg0_)) != 0) { /* check the type code */ double dn, dms; if (vmb_get_len(str) == 1 && (str[2] == 'U' || str[2] == 'u')) { /* * 'U' - Unix time, as seconds past 1/1/1970, UTC. Get the * argument as a double, and convert it to our day numbering * scheme by dividing by seconds per day and adding the Unix * Epoch's day number. (An ordinary double is big enough to * hold the largest Unix seconds-since-Epoch value we can * represent: our range is limited by the 32-bit day number, so * in terms of seconds that's (2^31-1)*86400, which needs about * 49 bits. A standard double's mantissa is 52 bits, so it can * represent any integer in our range exactly.) */ double d = G_stk->get(0)->num_to_double(vmg0_); dn = floor(d / (24*60*60)); dms = (d - (dn * (24*60*60))) * 1000; dn += caldate_t::UNIX_EPOCH_DAYNO; } else if (vmb_get_len(str) == 1 && (str[2] == 'J' || str[2] == 'j')) { /* * 'J' - Julian day number; get the value, and adjust it to our * Epoch by subtracting the Julian day number of our Epoch. * The whole part is our day number; the fractional part is the * fraction of a day past midnight (the .5 in the Epoch * adjustment recalibrates from noon to midnight). */ bignum_t<32> bdayno(vmg_ G_stk->get(0)); bdayno -= 1721119.5; dn = floor((double)bdayno); dms = (double)((bdayno - dn) * (long)(24*60*60*1000)); } else { /* unrecognized code */ err_throw(VMERR_BAD_VAL_BIF); } /* make sure the day number fits an int32 */ if (dn > INT32MAXVAL || dn < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); dayno = (int32_t)dn; /* convert milliseconds to int32 */ daytime = round_int32(dms); } else if ((argc == 3 || argc == 7) && G_stk->get(0)->is_numeric(vmg0_) && G_stk->get(1)->is_numeric(vmg0_) && G_stk->get(2)->is_numeric(vmg0_) && (argc == 3 || (G_stk->get(3)->is_numeric(vmg0_) && G_stk->get(4)->is_numeric(vmg0_) && G_stk->get(5)->is_numeric(vmg0_) && G_stk->get(6)->is_numeric(vmg0_)))) { /* parse the Y/M/D or Y/M/D HH:MI:SS arguments */ parse_ymd_args(vmg_ 0, argc, dayno, daytime); /* convert from the local time zone to UTC */ G_tzcache->get_local_zone(vmg0_)->local_to_utc(dayno, daytime); } else if ((argc == 4 || argc == 8) && G_stk->get(0)->is_numeric(vmg0_) && G_stk->get(1)->is_numeric(vmg0_) && G_stk->get(2)->is_numeric(vmg0_) && (argc == 4 || (G_stk->get(3)->is_numeric(vmg0_) && G_stk->get(4)->is_numeric(vmg0_) && G_stk->get(5)->is_numeric(vmg0_) && G_stk->get(6)->is_numeric(vmg0_)))) { /* parse the Y/M/D or Y/M/D HH:MI:SS arguments */ parse_ymd_args(vmg_ 0, argc - 1, dayno, daytime); /* convert from the given time zone to UTC */ get_tz_arg(vmg_ argc - 1, argc)->local_to_utc(dayno, daytime); } else { /* wrong arguments */ err_throw(VMERR_BAD_TYPE_BIF); } /* create the object */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); new (vmg_ id) CVmObjDate(vmg_ dayno, daytime); /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjDate::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjDate::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjDate::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * get a static (class) property */ int CVmObjDate::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* check for our static properties */ switch (midx) { case VMDATE_PARSEDATE: return s_getp_parseDate(vmg_ result, argc); case VMDATE_PARSEJULIANDATE: return s_getp_parseJulianDate(vmg_ result, argc); case VMDATE_SETLOCALINFO: return s_getp_setLocaleInfo(vmg_ result, argc); } /* not one of hours; inherit the superclass handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* ------------------------------------------------------------------------ */ /* * cast to a string */ const char *CVmObjDate::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *newstr) const { /* format the date into a buffer */ char buf[64]; format_string_buf(vmg_ buf, sizeof(buf)); /* create the return string */ newstr->set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); return newstr->get_as_string(vmg0_); } /* * Format into a string buffer using the default representation - for * implied conversions, toString, and the debugger */ void CVmObjDate::format_string_buf(VMG_ char *buf, size_t buflen) const { /* get the day number and time of day in local time */ int32_t dayno, daytime, tzofs; const char *tzabbr = get_local_time( dayno, daytime, tzofs, G_tzcache->get_local_zone(vmg0_)); /* convert the day number to a calendar date */ gregcaldate_t date(dayno); /* format using a default template */ CVmDateLocale lc Pvmg0_P; format_date(vmg_ buf, buflen, "%Y-%m-%d %H:%M:%S", 17, &lc, &date, dayno, daytime, tzabbr, tzofs); } /* ------------------------------------------------------------------------ */ /* * Date arithmetic - add an integer or BigNumber: adds the given number of * days to the date. */ int CVmObjDate::add_val(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, const vm_val_t *val) { /* get my date and time */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; /* the other value must be something numeric */ if (val->typ == VM_INT) { /* it's a simple integer - add it as a number of days */ dayno += val->val.intval; } else { /* it's not an int; calculate in the bignum domain */ bignum_t<32>d(vmg_ val); /* make sure the integer portion won't overflow */ double dd = floor((double)d); if (dd + dayno > INT32MAXVAL || dd + dayno < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); /* add the whole part to my day number */ dayno += (int32_t)dd; /* * convert the fractional part to milliseconds (by multiplying it * by the number of milliseconds in a day), then add it to my time * value */ daytime += round_int32((double)((d - dd) * (long)(24L*60*60*1000))); /* normalize, to carry time overflow/underflow into the date */ caldate_t::normalize(dayno, daytime); } /* return a new Date object representing the result */ result->set_obj(create(vmg_ FALSE, dayno, daytime)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Date arithmetic - subtract an integer or BigNumber to subtract a number * of days from the date; or subtract another Date value to calculate the * number of days between the dates. */ int CVmObjDate::sub_val(VMG_ vm_val_t *result, vm_obj_id_t /*self*/, const vm_val_t *val) { /* get my date and time */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; /* the other value must be a Date or something numeric */ if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* get the other date's contents */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t vdayno = vdate->get_ext()->dayno; int32_t vdaytime = vdate->get_ext()->daytime; /* we need fractional days, so calculate as bignum_t */ bignum_t<32> d1((long)dayno), t1((long)daytime); bignum_t<32> d2((long)vdayno), t2((long)vdaytime); d1 += (t1 / (long)(24*60*60*1000)); d2 += (t2 / (long)(24*60*60*1000)); /* return a BigNumber containing the result */ result->set_obj(CVmObjBigNum::create(vmg_ FALSE, d1 - d2)); /* handled (we don't want to return a Date, as the other paths do) */ return TRUE; } else if (val->typ == VM_INT) { /* it's a simple integer - subtract the number of days */ dayno -= val->val.intval; } else { /* it's not an int; calculate in the bignum domain */ bignum_t<32> d(vmg_ val); /* make sure the integer portion won't overflow */ double dd = floor((double)d); if (dd - dayno > INT32MAXVAL || dd - dayno < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); /* add the whole part to my day number */ dayno -= (int32_t)dd; /* * convert the fractional part to milliseconds (by multiplying it * by the number of milliseconds in a day), then subtract it from * my time value */ daytime -= round_int32((double)((d - dd) * (long)(24L*60*60*1000))); /* normalize, to carry time overflow/underflow into the date */ caldate_t::normalize(dayno, daytime); } /* return a new Date object representing the result */ result->set_obj(create(vmg_ FALSE, dayno, daytime)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check two Date values for equality */ int CVmObjDate::equals(VMG_ vm_obj_id_t, const vm_val_t *val, int) const { if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* it's a Date - get the two day number and time values */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t day1 = get_ext()->dayno, day2 = vdate->get_ext()->dayno; int32_t time1 = get_ext()->daytime, time2 = vdate->get_ext()->daytime; /* we're equal if we represent the same date and time */ return day1 == day2 && time1 == time2; } else { /* we're not equal to any other type */ return FALSE; } } /* * Compare two Date values */ int CVmObjDate::compare_to(VMG_ vm_obj_id_t, const vm_val_t *val) const { /* check the other object's type */ if (val->typ == VM_OBJ && is_date_obj(vmg_ val->val.obj)) { /* it's a Date - get the two day number and time values */ CVmObjDate *vdate = (CVmObjDate *)vm_objp(vmg_ val->val.obj); int32_t day1 = get_ext()->dayno, day2 = vdate->get_ext()->dayno; int32_t time1 = get_ext()->daytime, time2 = vdate->get_ext()->daytime; /* compare by date and time */ return (day1 != day2 ? day1 - day2 : time1 - time2); } else { /* we can't compare to other types */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjDate::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjDate::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjDate::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* allocate the extension */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* read the image data */ ext->dayno = osrp4s(ptr); ext->daytime = osrp4(ptr+4); } /* * save to a file */ void CVmObjDate::save_to_file(VMG_ class CVmFile *fp) { /* get our extension */ vm_date_ext *ext = get_ext(); /* write the extension data */ fp->write_int4(ext->dayno); fp->write_uint4(ext->daytime); } /* * restore from a file */ void CVmObjDate::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* allocate the extension structure */ vm_date_ext *ext = vm_date_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; /* read the data */ ext->dayno = fp->read_int4(); ext->daytime = fp->read_uint4(); } /* ------------------------------------------------------------------------ */ /* * Construct a list of integer values for return from a method */ static void make_int_list(VMG_ vm_val_t *retval, int cnt, ...) { /* create the list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* start the varargs scan */ va_list args; va_start(args, cnt); /* add the elements */ for (int i = 0 ; i < cnt ; ++i) { vm_val_t ele; ele.set_int(va_arg(args, int)); lst->cons_set_element(i, &ele); } /* done with the varargs */ va_end(args); } /* * Add a string to a return list */ static void enlist_str(VMG_ CVmObjList *lst, int idx, const char *str, size_t len) { /* set up a new string, or nil if the string is null */ vm_val_t ele; if (str != 0) ele.set_obj(CVmObjString::create(vmg_ FALSE, str, len)); else ele.set_nil(); /* add the list element */ lst->cons_set_element(idx, &ele); } /* add a string from a date_parse_string */ static void enlist_str(VMG_ CVmObjList *lst, int idx, const date_parse_string &str) { enlist_str(vmg_ lst, idx, str.p, str.len); } /* ------------------------------------------------------------------------ */ /* * parseDate method (static) */ int CVmObjDate::s_getp_parseDate(VMG_ vm_val_t *retval, uint *oargc) { /* parse the date using the Gregorian calendar */ gregcaldate_t cal; return common_parseDate(vmg_ retval, oargc, &cal); } /* * parseJulianDate method (static) */ int CVmObjDate::s_getp_parseJulianDate(VMG_ vm_val_t *retval, uint *oargc) { /* parse the date using the Julian calendar */ julcaldate_t cal; return common_parseDate(vmg_ retval, oargc, &cal); } /* * common parseDate handler for parseDate, parseJulianDate */ int CVmObjDate::common_parseDate(VMG_ vm_val_t *retval, uint *oargc, multicaldate_t *cal) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 3, FALSE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up a locale state interface */ CVmDateLocale lc Pvmg0_P; /* retrieve the string to parse */ const char *src = G_stk->get(0)->get_as_string(vmg0_); if (src == 0) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the format string/list argument, if present */ const vm_val_t *custom = 0; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) custom = G_stk->get(1); /* retrieve the reference date, if present */ int32_t refday, reftime; if (argc >= 3 && G_stk->get(2)->typ != VM_NIL) { /* get the reference date */ CVmObjDate *refdate = vm_val_cast(CVmObjDate, G_stk->get(2)); if (refdate == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get its contents */ refday = refdate->get_ext()->dayno; reftime = refdate->get_ext()->daytime; } else { /* no reference date argument - get the current date and time */ caldate_t::now(refday, reftime); } /* retrieve the reference time zone, if present */ CVmTimeZone *reftz = get_tz_arg(vmg_ 3, argc); /* parse the date */ int32_t dayno, daytime; date_parse_result result(cal); date_parse_string fmt_match[20]; int fmt_match_cnt = countof(fmt_match); if (parse_date_string(vmg_ dayno, daytime, src, custom, &lc, cal, refday, reftime, reftz, &result, fmt_match, &fmt_match_cnt)) { /* create the return list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 5)); G_stk->push(retval); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* element [1] is the return date */ vm_val_t ele; ele.set_obj(CVmObjDate::create(vmg_ FALSE, dayno, daytime)); lst->cons_set_element(0, &ele); /* [2] is the TimeZone object, if applicable */ ele.set_nil(); if (result.tz != reftz) ele.set_obj(CVmObjTimeZone::create(vmg_ result.tz)); lst->cons_set_element(1, &ele); /* [3] is the fixed timezone offset in seconds, if applicable */ ele.set_nil(); if (result.has_tzofs()) ele.set_int(result.tzofs / 1000); lst->cons_set_element(2, &ele); /* [4] next is the format string matched */ ele.set_obj(CVmObjList::create(vmg_ FALSE, fmt_match_cnt)); lst->cons_set_element(3, &ele); CVmObjList *flst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); flst->cons_clear(); /* populate the format string list */ for (int fi = 0 ; fi < fmt_match_cnt ; ++fi) enlist_str(vmg_ flst, fi, fmt_match[fi]); /* [5] is a sublist with the source substrings */ ele.set_obj(CVmObjList::create(vmg_ FALSE, 13)); lst->cons_set_element(4, &ele); CVmObjList *slst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); slst->cons_clear(); /* now add the date component source strings */ enlist_str(vmg_ slst, 0, result.pera); enlist_str(vmg_ slst, 1, result.pyy); enlist_str(vmg_ slst, 2, result.pmm); enlist_str(vmg_ slst, 3, result.pdd); enlist_str(vmg_ slst, 4, result.pdoy); enlist_str(vmg_ slst, 5, result.pw); enlist_str(vmg_ slst, 6, result.pampm); enlist_str(vmg_ slst, 7, result.phh); enlist_str(vmg_ slst, 8, result.pmi); enlist_str(vmg_ slst, 9, result.pss); enlist_str(vmg_ slst, 10, result.pms); enlist_str(vmg_ slst, 11, result.punix); enlist_str(vmg_ slst, 12, result.ptz); /* done with the list gc protection */ G_stk->discard(); } else { /* return nil */ retval->set_nil(); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * formatDate method */ int CVmObjDate::getp_formatDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { gregcaldate_t cd; return formatDateGen(vmg_ self, retval, oargc, &cd); } /* * formatJulianDate method */ int CVmObjDate::getp_formatJulianDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { julcaldate_t cd; return formatDateGen(vmg_ self, retval, oargc, &cd); } /* * general date formatter */ int CVmObjDate::formatDateGen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc, multicaldate_t *date) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the format string */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the format string length and buffer pointer */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* get my date and time in local time */ CVmTimeZone *tz = get_tz_arg(vmg_ 1, argc); int32_t dayno, daytime, tzofs; const char *tzabbr = get_local_time(dayno, daytime, tzofs, tz); /* convert the day number to a calendar date */ date->set_dayno(dayno); /* do a formatting pass to determine how much space we need */ CVmDateLocale lc Pvmg0_P; size_t buflen = format_date( vmg_ 0, 0, fmt, fmtlen, &lc, date, dayno, daytime, tzabbr, tzofs); /* create a string for the result */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buflen)); CVmObjString *str = vm_objid_cast(CVmObjString, retval->val.obj); /* format the string into the buffer */ format_date(vmg_ str->cons_get_buf(), buflen, fmt, fmtlen, &lc, date, dayno, daytime, tzabbr, tzofs); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* macro: write a character to the output buffer if there's room */ #define wrtch(ch) (++outlen, buflen > 0 ? *buf++ = ch, --buflen : 0) /* macros: write a null-terminated/counted length string */ #define wrtstr(str) wrtstrl(str, strlen(str)) #define wrttstr(str) wrtstrl(str + VMB_LEN, vmb_get_len(str)) #define wrtstrl(str, len) _wrtstrl(buf, buflen, outlen, str, len) /* service routine: write a counted-length string */ static void _wrtstrl(char *&buf, size_t &buflen, size_t &outlen, const char *str, size_t len) { /* if it fits, copy the whole thing; otherwise copy as much as fits */ if (len <= buflen) { memcpy(buf, str, len); buf += len; buflen -= len; } else if (buflen != 0) { memcpy(buf, str, buflen); buf += buflen; buflen = 0; } /* count it all in the output */ outlen += len; } /* macro: write an n-digit number */ #define wrtnum(v, ndigits) \ _wrtnum(buf, buflen, outlen, v, ndigits, pound, leadsp, roman) /* service routine: write an n-digit number */ static void _wrtnum(char *&buf, size_t &buflen, size_t &outlen, int v, int ndigits, int pound, char leadsp, int roman) { char numbuf[20]; int i = 0; int neg = FALSE; /* note the sign */ if (v < 0) { wrtch('-'); v = -v; neg = TRUE; } /* if desired, generate a Roman numeral, if it's in range (1-4999) */ if (roman && !neg && v >= 1 && v <= 4999) { static const struct { const char *r; int val; } r[] = { { "M", 1000 }, { "CM", 900 }, { "D", 500 }, { "CD", 400 }, { "C", 100 }, { "XC", 90 }, { "L", 50 }, { "X", 10 }, { "IX", 9 }, { "V", 5 }, { "IV", 4 }, { "I", 1 } }; for (int i = 0 ; v != 0 && i < countof(r) ; ) { /* if this numeral fits, append it */ if (r[i].val <= v) { /* it fits - write it and deduct it from the balance */ wrtstr(r[i].r); v -= r[i].val; } else { /* doesn't fit - move on to the next lower numeral */ ++i; } } return; } /* generate digits */ do { div_t q = div(v, 10); v = q.quot; numbuf[i++] = '0' + q.rem; } while (v != 0); /* * if we didn't generate as many digits as the field requires, add * leading zeros or spaces, as desired */ if (i < ndigits && (leadsp != 0 || !pound)) { /* add leading digits or the specified spaces */ char lead = leadsp != 0 ? leadsp : '0'; for (int j = i ; j < ndigits ; ++j) wrtch(lead); } /* write the digits */ for ( ; i > 0 ; --i, wrtch(numbuf[i])) ; } /* write a locale string list item */ #define wrtlistitem(item, idx, sticky) \ _wrtlistitem(vmg_ buf, buflen, outlen, lc, item, idx, sticky) static void _wrtlistitem(VMG_ char *&buf, size_t &buflen, size_t &outlen, CVmDateLocale *lc, int item, int idx, int sticky) { /* get the list item */ size_t len; const char *str = lc->index_list(vmg_ len, item, idx, sticky); /* write the item */ for ( ; len != 0 ; ++str, --len) wrtch(*str); } /* * Internal date formatter. Formats into a buffer; if no buffer is * provided, counts the length required. Note that the date is specified * in the *local* time zone - the caller must adjust to local time before * calling. Null-terminates the result if there's room, but doesn't * include the null terminator in the size count. * * Most of our format codes are the same as for C/C++/php strftime, which * are mostly the same as MySQL DATE_FORMAT(). We support all of the * strftime formats, with the same letter codes, except for the php tab * (%t) and newline (%n), which I don't see any good reason to include when * we could just as well use \t and \n. We support all of the MySQL format * options, although several have different codes (MySQL mostly follows the * strftime codes, though). */ size_t CVmObjDate::format_date(VMG_ char *buf, size_t buflen, const char *fmt, size_t fmtlen, CVmDateLocale *lc, const multicaldate_t *date, int32_t dayno, int32_t daytime, const char *tzabbr, int32_t tzofs) const { const char *subfmt; size_t subfmtl; /* we haven't written anything to the buffer yet */ size_t outlen = 0; /* scan the format string */ for ( ; fmtlen != 0 ; ++fmt, --fmtlen) { if (*fmt == '%' && fmtlen >= 2) { /* skip the '%' */ ++fmt, --fmtlen; /* parse flags */ int pound = FALSE; int minus = FALSE; int roman = FALSE; char leadsp = 0; for (int found_flag = TRUE ; found_flag && fmtlen >= 2 ; ) { /* assume we won't find another flag on this pass */ found_flag = FALSE; /* check for the '#' flag (meaning varies by format code) */ if (fmtlen >= 2 && *fmt == '#') { pound = found_flag = TRUE; ++fmt, --fmtlen; } /* ' ' and '\ ' (replace leading zeros with spaces) */ if (fmtlen >= 2 && (*fmt == ' ' || *fmt == 0x15)) { leadsp = *fmt; found_flag = TRUE; ++fmt, --fmtlen; } /* check for '-' flag */ if (fmtlen >= 2 && *fmt == '-') { minus = found_flag = TRUE; ++fmt, --fmtlen; } /* check for '&' flag */ if (fmtlen >= 2 && *fmt == '&') { roman = found_flag = TRUE; ++fmt, --fmtlen; } } /* get the format code */ switch (*fmt) { case 'a': /* abbreviated day name */ wrtlistitem(LC_WKDY, date->weekday(), FALSE); break; case 'A': /* full day name */ wrtlistitem(LC_WEEKDAY, date->weekday(), FALSE); break; case 'd': /* two-digit day of month (or one digit with '#') */ wrtnum(date->d(), 2); break; case 'j': /* day of year 001-366, three digits with leading zeros */ { /* * we can calculate the day of the year by subtracting * the day number of Jan 1 from the given date (and * adding 1 to get into range 1-366) */ caldate_t jan1(date->y(), 1, 1); int j = dayno - jan1.dayno() + 1; wrtnum(j, 3); } break; case 'J': /* * Julian day number (the 4713 BC kind); '#' suppresses the * fractional part */ { /* get the day and time */ long dn = get_dayno(), dt = get_daytime(); /* if the time is past noon, it's on the next day */ if (dt > 12*60*60*1000) dn += 1, dt -= 12*60*60*1000; /* * figure the combined date/time value, adjusting the * day number to the Julian day Epoch */ bignum_t<32> bdn(dn), bdt(dt); bdn += 1721119L; bdn += (bdt / (long)(24*60*60*1000)); /* format it and write it */ char jbuf[64]; bdn.format(jbuf, sizeof(jbuf), -1, 10); wrttstr(jbuf); } break; case 'u': /* day of week 1-7 Monday-Sunday (ISO weekday) */ wrtnum(date->iso_weekday(), 1); break; case 'w': /* day of week 0-6 Sunday-Saturday */ wrtnum(date->weekday(), 1); break; case 't': /* day of month with ordinal suffix (non-strftime) */ wrtnum(date->d(), 1); wrtlistitem(LC_ORDSUF, ordinal_index(date->d()), TRUE); break; case 'U': /* week number, 00-53, week 1 starting with first Sunday */ wrtnum(date->weekno(0), 2); break; case 'W': /* week number, 00-53, week 1 starting with first Monday */ wrtnum(date->weekno(1), 2); break; case 'V': /* ISO-8601:1988 week number of the year */ wrtnum(date->iso_weekno(0), 2); break; case 'b': /* abbreviated month name */ wrtlistitem(LC_MON, date->m() - 1, FALSE); break; case 'B': /* full month name */ wrtlistitem(LC_MONTH, date->m() - 1, FALSE); break; case 'm': /* two-digit month 01-12 (one digit with #) */ wrtnum(date->m(), 2); break; case 'C': /* two-digit century (e.g., 19 for 1900-1999) */ wrtnum(date->y() / 100, 2); break; case 'g': case 'G': /* ISO-8601:1988 year ('g'=2 digits, 'G'=4 digits) */ { /* ISO-8601:1988 week number of the year */ int iy; date->iso_weekno(&iy); wrtnum(iy, *fmt == 'g' ? 2 : 4); } break; case 'y': /* two-digit year */ wrtnum(ldiv(date->y(), 100).rem, 2); break; case 'Y': /* four-digit year */ wrtnum(date->y(), 4); break; case 'e': case 'E': /* * Year with AD/BC era before/after the year number. For * 'e', the era is always after, or always before on '-'. * For 'E', the era is AD before/BC after, or reversed on * '-'. */ { /* figure the era: positive years are AD, <=0 are BC */ int idx = 0; // assume AD int yy = date->y(); if (yy <= 0) { idx = 1; // BC yy = -yy + 1; } /* * Figure the display order: "%-e" puts the era first * in all case; "%E" puts AD first; "%-E" puts BC first */ if ((*fmt == 'e' && minus) || (*fmt == 'E' && !minus && idx == 0) || (*fmt == 'E' && minus && idx == 1)) { /* era first */ wrtlistitem(LC_ERA, idx, FALSE); wrtch(' '); wrtnum(yy, 1); } else { /* year first */ wrtnum(yy, 1); wrtch(' '); wrtlistitem(LC_ERA, idx, FALSE); } } break; case 'H': /* two-digit hour, 24-hour clock */ wrtnum(daytime/(60*60*1000), 2); break; case 'I': /* two-digit hour, 12-hour clock */ { int hh = daytime/(60*60*1000); if (hh == 0) hh = 12; else if (hh >= 13) hh -= 12; wrtnum(hh, 2); } break; case 'M': /* two-digit minute */ wrtnum(daytime/(60*1000) % 60, 2); break; case 'P': /* upper-case AM or PM */ wrtlistitem(LC_AMPM, daytime/(60*60*1000) >= 12 ? 1 : 0, FALSE); break; case 'p': /* lower-case am or pm */ wrtlistitem(LC_AMPM, daytime/(60*60*1000) >= 12 ? 3 : 2, FALSE); break; case 'r': /* full 12-hour clock time: %I:%M:%S %P */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_12HOUR); goto do_subfmt; do_subfmt: { /* recursively format the new format string */ size_t l = format_date( vmg_ buf, buflen, subfmt, subfmtl, lc, date, dayno, daytime, tzabbr, tzofs); /* count it in our output length */ outlen += l; /* advance our buffer pointer past the copied text */ if (buflen >= l) buflen -= l, buf += l; else buf += buflen, buflen = 0; } break; case 'R': /* 24-hour clock time w/minutes: %H:%M */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_24HOUR); goto do_subfmt; case 'S': /* two-digit seconds */ wrtnum(daytime/1000 % 60, 2); break; case 'N': /* three-digit milliseconds (non-strftime) */ wrtnum(daytime % 1000, 3); break; case 'T': /* 24-hour clock time w/seconds: %H:%M:%S */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_24HOURSECS); goto do_subfmt; case 'X': /* locale time representation without the date */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_TIME); goto do_subfmt; case 'z': /* time zone abbreviation */ { for (const char *p = tzabbr ; *p != '\0' ; ++p) wrtch(*p); } break; case 'Z': /* time zone UTC offset, four digits (+0500) */ { int32_t o = tzofs; if (o < 0) wrtch('-'), o = -o; else wrtch('+'); wrtnum(o/(60*60*1000), 2); pound = FALSE; wrtnum(o/(60*1000) % 60, 2); } break; case 'c': /* preferred locale date and time stamp */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_TIMESTAMP); goto do_subfmt; case 'D': /* short date - %m/%d/%y */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_SHORTDATE); goto do_subfmt; case 'F': /* database-style date: %Y-%m-%d */ subfmt = "%Y-%m-%d"; subfmtl = 8; goto do_subfmt; case 's': /* Unix Epoch timestamp as an integer */ { /* * The Unix timestamp is the number of seconds after * (or before, if negative) 1/1/1970 00:00 UTC. First * calculate the number of days after (before) 1/1/1970 * by subtracting the Unix Epoch day number from our * internal day number; then multiply that by the * number of seconds in a day (24*60*60) to get the * Unix timestamp of midnight on that day, then add the * number of seconds into the day ('daytime' stores * milliseconds, so divide it by 1000). * * Note that we need to adjust back to UTC by * subtracting the time zone offset. * * As we've discussed elsewhere in this file, a double * is big enough for the Unix timestamp value for any * day number we can store, and represents it exactly * since we're working in whole numbers. */ double utime = (dayno - caldate_t::UNIX_EPOCH_DAYNO) * 24.*60.*60. + daytime/1000 - tzofs/1000; /* format it to a buffer and copy to the output */ char ubuf[60]; sprintf(ubuf, "%.0lf", utime); wrtstr(ubuf); } break; case 'x': /* preferred locale date representation */ subfmt = lc->get(vmg_ subfmtl, LC_FMT_DATE); goto do_subfmt; case '%': /* a literal % */ wrtch('%'); break; default: /* * anything else is an error; copy the whole %x literally * to make the unparsed format character apparent */ wrtch('%'); wrtch(*fmt); break; } } else { /* it's an ordinary character, so copy it literally */ wrtch(*fmt); } } /* add a nul if there's room (but don't count it in the result length) */ if (buflen > 0) *buf++ = '\0'; /* return the total space needed */ return outlen; } /* done with our local macros */ #undef wrtch #undef wrt2dig /* ------------------------------------------------------------------------ */ /* * compareTo method */ int CVmObjDate::getp_compareTo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the other date */ CVmObjDate *other = vm_val_cast(CVmObjDate, G_stk->get(0)); if (other == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the respective dates */ int32_t dayno = get_ext()->dayno; int32_t daytime = get_ext()->daytime; int32_t odayno = other->get_ext()->dayno; int32_t odaytime = other->get_ext()->daytime; /* do the comparison */ retval->set_int(dayno != odayno ? dayno - odayno : daytime - odaytime); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getDate method */ int CVmObjDate::getp_getDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* express it as a calendar date */ caldate_t cd(dayno); /* return [year, month, monthday, weekday] */ make_int_list(vmg_ retval, 4, cd.y, cd.m, cd.d, cd.weekday() + 1); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getJulianDay method */ int CVmObjDate::getp_getJulianDay(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the Julian day number for midnight UTC on my day number */ caldate_t cd(get_ext()->dayno); bignum_t<32> jday(cd.julian_dayno()); /* add my fraction of a day past midnight UTC */ bignum_t<32> jt((long)get_ext()->daytime); jday += jt / (long)(24*60*60*1000); /* return a BigNumber result */ retval->set_obj(CVmObjBigNum::create(vmg_ FALSE, &jday)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getJulianDate method */ int CVmObjDate::getp_getJulianDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* get the julian date */ caldate_t cd(dayno); int y, m, d; cd.julian_date(y, m, d); /* * Return [year, month, monthday, weekday]. Note that there's no * separate Julian weekday calculation, since the Julian calendar and * Gregorian agree on the day of the week for every day. */ make_int_list(vmg_ retval, 4, y, m, d, cd.weekday() + 1); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getISOWeekDate method */ int CVmObjDate::getp_getISOWeekDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date in local time */ int32_t dayno = get_local_date(get_tz_arg(vmg_ 0, argc)); /* express it as a calendar date */ caldate_t cd(dayno); /* return [iso year, iso week, iso day] */ int iy; int iw = cd.iso_weekno(&iy); make_int_list(vmg_ retval, 3, iy, iw, cd.iso_weekday()); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getTime method */ int CVmObjDate::getp_getTime(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get my date and time in local time */ int32_t dayno, daytime; get_local_time(dayno, daytime, get_tz_arg(vmg_ 0, argc)); /* return [hour, minute, second, msec] */ make_int_list(vmg_ retval, 4, daytime/(24*60*60*1000), daytime/(60*60*1000) % 60, daytime/(60*1000) % 60, daytime % 1000); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * addInterval method */ int CVmObjDate::getp_addInterval(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the interval list */ vm_val_t *lst = G_stk->get(0); if (!lst->is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve my date as a calendar object */ int32_t dayno = get_ext()->dayno; caldate_t cd(dayno); /* retrieve my time as a double, to allow for overflows from int32 */ double daytime = get_ext()->daytime; /* add each element from the list */ int cnt = lst->ll_length(vmg0_); for (int i = 1 ; i <= cnt ; ++i) { /* get this interval element */ vm_val_t ele; lst->ll_index(vmg_ &ele, i); /* treat nil as zero */ if (ele.typ == VM_NIL) continue; /* add it to the appropriate date or time component */ switch (i) { case 1: cd.y += ele.num_to_int(vmg0_); break; case 2: cd.m += ele.num_to_int(vmg0_); break; case 3: cd.d += ele.num_to_int(vmg0_); break; case 4: daytime += ele.num_to_double(vmg0_)*60.*60.*1000.; break; case 5: daytime += ele.num_to_double(vmg0_)*60.*1000.; break; case 6: daytime += ele.num_to_double(vmg0_)*1000.; break; } } /* carry overflows from the time into the day */ double day_carry = floor(daytime / (24.*60.*60.*1000.)); daytime -= day_carry * (24.*60.*60.*1000.); if (day_carry + cd.d > INT32MAXVAL || day_carry + cd.d < INT32MINVAL) err_throw(VMERR_NUM_OVERFLOW); cd.d += (int32_t)day_carry; /* return the new date, using our same local timezone */ retval->set_obj(create(vmg_ FALSE, cd.dayno(), (int32_t)daytime)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * findWeekday method */ int CVmObjDate::getp_findWeekday(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the day of week and 'which' arguments */ int32_t wday = G_stk->get(0)->num_to_int(vmg0_); int32_t which = G_stk->get(1)->num_to_int(vmg0_); /* check the weekday and 'which' ranges */ if (wday < 1 || wday > 7 || which == 0) err_throw(VMERR_BAD_VAL_BIF); /* adjust wday to 0=Sunday */ wday -= 1; /* get my date in local time */ CVmTimeZone *tz = get_tz_arg(vmg_ 2, argc); int32_t dayno = get_local_date(tz); /* figure my weekday */ caldate_t cd(dayno); int my_wday = cd.weekday(); /* figure the difference between the target day and my day, mod 7 */ int delta = (7 + wday - my_wday) % 7; /* * Go forward that many days, or backwards (7 - delta), to get the * first occurrence before/after my date. Then go forwards or * backwards by additional weeks as needed. */ if (which > 0) dayno += delta + (which-1)*7; else dayno -= ((7 - delta) % 7) + (-which-1)*7; /* return a new date at midnight on the given day */ int32_t daytime = 0; tz->local_to_utc(dayno, daytime); retval->set_obj(create(vmg_ FALSE, dayno, daytime)); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Get a TimeZone argument. If the argument is nil or omitted, we'll * return the default local system TimeZone object. If it's a TimeZone * object, we'll return that as given. If it's a string or integer, we'll * construct a TimeZone object using that value per the TimeZone * constructors. * * 'argn' is the argument number to fetch; 'argc' is the actual number of * arguments passed to the method. */ CVmTimeZone *CVmObjDate::get_tz_arg(VMG_ uint argn, uint argc) { /* if the argument is nil or missing, use the default local zone */ vm_val_t *argp = G_stk->get(argn); if (argn == argc || argp->typ == VM_NIL) return G_tzcache->get_local_zone(vmg0_); /* check for a TimeZone object */ CVmObjTimeZone *tzobj; if (argn < argc && (tzobj = vm_val_cast(CVmObjTimeZone, argp)) != 0) return tzobj->get_tz(); /* otherwise, accept anything that the TimeZone constructor accepts */ return CVmObjTimeZone::parse_ctor_args(vmg_ argp, argc - argn); } /* ------------------------------------------------------------------------ */ /* * setLocaleInfo method (static method) */ int CVmObjDate::s_getp_setLocaleInfo(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up a locale state interface */ CVmDateLocale lc Pvmg0_P; /* parse arguments */ vm_val_t *argp = G_stk->get(0); if (argc == 1 && argp->is_listlike(vmg0_)) { /* list of the first N elements */ int n = argp->ll_length(vmg0_); for (int i = 0 ; i < n ; ++i) { /* get the element */ vm_val_t ele; argp->ll_index(vmg_ &ele, i+1); /* set the next index */ lc.set(vmg_ G_undo, i, &ele); } } else if ((argc & 0x0001) == 0) { /* even number of arguments; treat them Type, String pairs */ for (uint i = 0 ; i < argc ; i += 2, argp -= 2) lc.set(vmg_ G_undo, argp->num_to_int(vmg0_), argp - 1); } else { /* unknown arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmbifc.cpp0000644000175000001440000000702611364312574015507 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifc.cpp - built-in function - Call-time resolution Function This is a version of the built-in function interface for resolving built-ins only when each built-in is invoked. This version allows loading an image file with unresolved function sets, then checks on each built-in function's invocation to make sure the function is available. This version can be used in a version of the interpreter used by the compiler for running 'preinit' or similar situations in which it is desirable to be able to load and run a program with unresolved function sets. This version is less efficient than the load-time resolver, so normal stand-alone interpreters should use the load-time version instead. Notes Modified 07/21/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmbif.h" #include "vmbifreg.h" #include "vmstr.h" #include "vmobj.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Call the given function from the given function set. */ void CVmBifTable::call_func(VMG_ uint set_index, uint func_index, uint argc) { vm_bif_entry_t *entry; vm_bif_desc *desc; /* get the function set */ entry = table_[set_index]; /* if the function set is null, we can't call the function */ if (entry == 0) err_throw_a(VMERR_UNKNOWN_FUNC_SET, 1, ERR_TYPE_TEXTCHAR, names_[set_index]); /* get the function pointer */ desc = &entry->func[func_index]; /* if the function is null, we can't call it */ if (desc->func == 0) err_throw_a(VMERR_UNAVAIL_INTRINSIC, 2, ERR_TYPE_TEXTCHAR, names_[set_index], ERR_TYPE_INT, func_index); /* call the function */ (*desc->func)(vmg_ argc); } /* * Handle adding a function set entry that's unresolvable at load-time */ void CVmBifTable::add_entry_unresolved(VMG_ const char *func_set_id) { /* * Since this is the call-time resolver, allow loading of the image * file even though this function set is unresolved. Store a null * entry in the function set table, and store the name of the * function set - we'll need this in case the program attempts to * invoke a function in this function set, so that we can generate * an error containing the unresolved function set name. */ table_[count_] = 0; names_[count_] = lib_copy_str(func_set_id); /* count the new entry */ ++count_; } /* * Get a function's descriptor */ const vm_bif_desc *CVmBifTable::get_desc(uint set_index, uint func_index) { vm_bif_entry_t *entry; vm_bif_desc *desc; /* validate that the set index is in range */ if (set_index >= count_) return 0; /* get the function set, and make sure it's valid */ entry = table_[set_index]; if (entry == 0) return 0; /* validate that the function index is in range */ if (func_index > entry->func_count) return 0; /* get the descriptor, and make sure there's a function pointer */ desc = &entry->func[func_index]; if (desc->func == 0) return 0; /* return the descriptor */ return desc; } frobtads-1.2.3/tads3/tct3.cpp0000644000175000001440000126565212012714151015116 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tct3.cpp,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3.cpp - TADS 3 Compiler - T3 VM Code Generator Function Generate code for the T3 VM Notes Modified 05/08/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "tcprs.h" #include "tct3.h" #include "tcgen.h" #include "vmtype.h" #include "vmwrtimg.h" #include "vmfile.h" #include "tcmain.h" #include "tcerr.h" #include "vmbignum.h" #include "vmrunsym.h" #include "vmhash.h" #include "vmmeta.h" #include "tct3unas.h" #include "vmop.h" /* ------------------------------------------------------------------------ */ /* * Named argument call table. The code body maintains a list of tables, * one per call with named arguments. At the end of the code body, we * generate the tables as part of the instruction stream. */ struct CTcNamedArgTab { CTcNamedArgTab(const CTcNamedArgs *args) { /* remember the argument list */ arglist = args->args; cnt = args->cnt; /* create a code label for the table's opcode stream location */ lbl = G_cs->new_label_fwd(); /* not in a list yet */ nxt = 0; } /* generate this table */ void generate() { /* define the label location and start the table */ CTcPrsNode::def_label_pos(lbl); G_cg->write_op(OPC_NAMEDARGTAB); /* write a placeholder for the table size */ ulong start = G_cs->get_ofs(); G_cs->write2(0); /* write the number of arguments in the list */ G_cs->write2(cnt); /* write placeholders for the index entries */ ulong idx = G_cs->get_ofs(), idx0 = idx; for (CTPNArg *arg = arglist->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if it's a named argument, write an index placeholder */ if (arg->is_named_param()) G_cs->write2(0); } /* write a final index marker, for computing the last item's length */ G_cs->write2(0); /* write the strings */ for (CTPNArg *arg = arglist->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if it's a named argument, write it out */ if (arg->is_named_param()) { /* write its index pointer */ G_cs->write2_at(idx, G_cs->get_ofs() - idx0); /* write the string */ G_cs->write(arg->get_name(), arg->get_name_len()); /* advance the index pointer */ idx += 2; } } /* write the final index marker */ G_cs->write2_at(idx, G_cs->get_ofs() - idx0); /* go back and fill in the table length prefix */ G_cs->write2_at(start, G_cs->get_ofs() - start - 2); } /* argument list */ const CTPNArglist *arglist; /* number of named arguments */ int cnt; /* code label for the table's location in the opcode stream */ CTcCodeLabel *lbl; /* next in list */ CTcNamedArgTab *nxt; }; /* ------------------------------------------------------------------------ */ /* * T3 Target Code Generator class */ /* * initialize the code generator */ CTcGenTarg::CTcGenTarg() { int i; /* * we haven't written any instructions yet - fill the pipe with * no-op instructions so that we don't think we can combine the * first instruction with anything previous */ last_op_ = OPC_NOP; second_last_op_ = OPC_NOP; /* * we haven't seen any strings or lists yet - set the initial * maximum lengths to zero */ max_str_len_ = 0; max_list_cnt_ = 0; /* we haven't generated any code yet */ max_bytecode_len_ = 0; /* there are no metaclasses defined yet */ meta_head_ = meta_tail_ = 0; meta_cnt_ = 0; /* no function sets defined yet */ fnset_head_ = fnset_tail_ = 0; fnset_cnt_ = 0; /* * Add the built-in metaclass entries. The order of these entries * is fixed to match the TCT3_METAID_xxx constants - if this order * changes, those constants must change to match. */ add_meta("tads-object"); add_meta("list"); add_meta("dictionary2/030000"); add_meta("grammar-production/030000"); add_meta("vector"); add_meta("anon-func-ptr"); add_meta("int-class-mod/030000"); add_meta("lookuptable/030003"); /* * Set up the initial translation table for translating from our * internal metaclass index values - TCT3_METAID_xxx - to the *actual* * image file dependency index. When we're compiling a program, these * are simply in our own internal order - index N in our scheme is * simply index N in the actual image file - because we get to lay out * exactly what the dependency table looks like. However, we need the * translation table for when we're being used as part of the debugger * - in those cases, we don't get to dictate the dependency table * layout, because the image file (and thus the dependency table) might * have been generated by a different version of the compiler. In * those cases, the image file loader will have to set up the * translation information for us. */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) predef_meta_idx_[i] = i; /* start at the first valid property ID */ next_prop_ = 1; /* start at the first valid object ID */ next_obj_ = 1; /* allocate an initial sort buffer */ sort_buf_size_ = 4096; sort_buf_ = (char *)t3malloc(sort_buf_size_); /* not in a constructor */ in_constructor_ = FALSE; /* not in an operator overload */ in_op_overload_ = FALSE; /* no debug line record pointers yet */ debug_line_cnt_ = 0; debug_line_head_ = debug_line_tail_ = 0; /* normal (static compiler, non-debug) evaluation mode */ eval_for_dyn_ = FALSE; eval_for_debug_ = FALSE; speculative_ = FALSE; debug_stack_level_ = 0; /* no multi-method initializer object yet */ mminit_obj_ = VM_INVALID_OBJ; /* create the string interning hash table */ strtab_ = new CVmHashTable(1024, new CVmHashFuncCS(), TRUE); } /* * delete the code generator */ CTcGenTarg::~CTcGenTarg() { /* delete all of the metaclass list entries */ while (meta_head_ != 0) { tc_meta_entry *nxt; /* remember the next item */ nxt = meta_head_->nxt; /* delete this item */ t3free(meta_head_); /* move on */ meta_head_ = nxt; } /* delete all of the function set list entries */ while (fnset_head_ != 0) { tc_fnset_entry *nxt; /* remember the next item */ nxt = fnset_head_->nxt; /* delete this item */ t3free(fnset_head_); /* move on */ fnset_head_ = nxt; } /* delete our sort buffer */ t3free(sort_buf_); /* delete the debug line record pointers */ while (debug_line_head_ != 0) { tct3_debug_line_page *nxt; /* remember the next one */ nxt = debug_line_head_->nxt; /* delete this one */ t3free(debug_line_head_); /* move on */ debug_line_head_ = nxt; } /* delete the string table */ delete strtab_; } /* start loading the image file metaclass dependency table */ void CTcGenTarg::start_image_file_meta_table() { int i; /* * clear out all of the entries - set them to -1 to indicate that * they're invalid */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) predef_meta_idx_[i] = -1; } /* * Load an image file metaclass dependency table. When we're being used in * a debugger, the image loader must call this with the run-time dependency * table to establish the translation from our internal metaclass * identifiers (TCT3_METAID_xxx) to the actual run-time index values. */ void CTcGenTarg::load_image_file_meta_table( const char *nm, size_t len, int idx) { /* * Check the name against our known names. If it matches one of the * known types, store the actual index in our translation table under * our internal index entry. If it's not a known name, ignore it - we * only care about the names we actually pre-define, because those are * the only ones we need to generate our own code for. */ if (len == 11 && memcmp(nm, "tads-object", 11) == 0) predef_meta_idx_[TCT3_METAID_TADSOBJ] = idx; else if (len == 4 && memcmp(nm, "list", 4) == 0) predef_meta_idx_[TCT3_METAID_LIST] = idx; else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0) predef_meta_idx_[TCT3_METAID_DICT] = idx; else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0) predef_meta_idx_[TCT3_METAID_GRAMPROD] = idx; else if (len == 6 && memcmp(nm, "vector", 6) == 0) predef_meta_idx_[TCT3_METAID_VECTOR] = idx; else if (len == 13 && memcmp(nm, "anon-func-ptr", 13) == 0) predef_meta_idx_[TCT3_METAID_ANONFN] = idx; else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0) predef_meta_idx_[TCT3_METAID_ICMOD] = idx; else if (len == 11 && memcmp(nm, "lookuptable", 11) == 0) predef_meta_idx_[TCT3_METAID_LOOKUP_TABLE] = idx; } /* * End the image file metaclass dependency table. */ void CTcGenTarg::end_image_file_meta_table() { int i; /* * Scan the metaclass translation table and make sure they've all been * set. If any are unset, it means that these metaclasses aren't * available; since we depend upon these metaclasses, this means that * we can't generate code interactively, so we can't act as a debugger. */ for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) { /* if this entry hasn't been set properly, abort */ if (predef_meta_idx_[i] == -1) err_throw(VMERR_IMAGE_INCOMPAT_VSN_DBG); } } /* * Add an entry to the metaclass dependency table */ int CTcGenTarg::add_meta(const char *nm, size_t len, CTcSymMetaclass *sym) { tc_meta_entry *ent; size_t extra_len; const char *extra_ptr; const char *p; size_t rem; /* * if the name string doesn't contain a slash, allocate enough space * to add an implied version suffix of "/000000" */ for (p = nm, rem = len ; rem != 0 && *p != '/' ; ++p, --rem) ; if (rem == 0) { /* we didn't find a version suffix - add space for one */ extra_len = 7; extra_ptr = "/000000"; } else { /* * there's already a version suffix - but make sure we have * space for a six-character string */ if (rem < 7) { /* add zeros to pad out to a six-place version string */ extra_len = 7 - rem; extra_ptr = "000000"; } else { /* we need nothing extra */ extra_len = 0; extra_ptr = 0; } } /* allocate a new entry for the item */ ent = (tc_meta_entry *)t3malloc(sizeof(tc_meta_entry) + len + extra_len); if (ent == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* copy the name into the entry */ memcpy(ent->nm, nm, len); /* add any extra version suffix information */ if (extra_len != 0) memcpy(ent->nm + len, extra_ptr, extra_len); /* null-terminate the name string in the entry */ ent->nm[len + extra_len] = '\0'; /* remember the symbol */ ent->sym = sym; /* link the entry in at the end of the list */ ent->nxt = 0; if (meta_tail_ != 0) meta_tail_->nxt = ent; else meta_head_ = ent; meta_tail_ = ent; /* count the entry, returning the index of the entry in the list */ return meta_cnt_++; } /* * Find a metaclass index given the global identifier. */ tc_meta_entry *CTcGenTarg::find_meta_entry(const char *nm, size_t len, int update_vsn, int *entry_idx) { tc_meta_entry *ent; int idx; size_t name_len; const char *p; const char *vsn; size_t vsn_len; size_t rem; /* find the version suffix, if any */ for (rem = len, p = nm ; rem != 0 && *p != '/' ; --rem, ++p) ; /* note the length of the name portion (up to the '/') */ name_len = len - rem; /* note the version string, if there is one */ if (rem != 0) { vsn = p + 1; vsn_len = rem - 1; } else { vsn = 0; vsn_len = 0; } /* search the existing entries */ for (idx = 0, ent = meta_head_ ; ent != 0 ; ent = ent->nxt, ++idx) { size_t ent_name_len; char *ent_vsn; /* find the version suffix in the entry */ for (ent_vsn = ent->nm ; *ent_vsn != '\0' && *ent_vsn != '/' ; ++ent_vsn) ; /* the name is the part up to the '/' */ ent_name_len = ent_vsn - ent->nm; /* note the length of the name and the version suffix */ if (*ent_vsn == '/') { /* the version is what follows the '/' */ ++ent_vsn; } else { /* there is no version suffix */ ent_vsn = 0; } /* if this is the one, return it */ if (ent_name_len == name_len && memcmp(ent->nm, nm, name_len) == 0) { /* * if this version number is higher than the version number * we previously recorded, remember the new, higher version * number */ if (update_vsn && ent_vsn != 0 && strlen(ent_vsn) == vsn_len && memcmp(vsn, ent_vsn, vsn_len) > 0) { /* store the new version string */ memcpy(ent_vsn, vsn, vsn_len); } /* tell the caller the index, and return the entry */ *entry_idx = idx; return ent; } } /* we didn't find it */ return 0; } /* * Get the property ID for the given method table index in the given * metaclass. The metaclass ID is given as the internal metaclass ID * (without the "/version" suffix), not as the external class name. * * The method table index is 0-based. */ CTcPrsNode *CTcGenTarg::get_metaclass_prop(const char *name, ushort idx) const { vm_prop_id_t propid; /* * If we have a loaded metaclass table from the running program, use * it. This will be the case if we're doing a dynamic or debugging * compilation. If we do, use it, since it has the direct information * on the loaded classes. * * If there's no metaclass table, this is an ordinary static * compilation, so get the metaclass information from the symbol table. */ if (G_metaclass_tab != 0) { /* look up the metaclass in the table */ vm_meta_entry_t *entry = G_metaclass_tab->get_entry_by_id(name); /* if we didn't find the entry, return failure */ if (entry == 0) return 0; /* * Get the property from the table. Note that xlat_func() uses a * 1-based index. */ if ((propid = entry->xlat_func(idx + 1)) == VM_INVALID_PROP) return 0; } else { /* static compilation - look up the metaclass in the symbol table */ CTcSymMetaclass *sym = G_cg->find_meta_sym(name, strlen(name)); if (sym == 0) return 0; /* get the metaclass method table entry at the given index */ CTcSymMetaProp *mprop = sym->get_nth_prop(idx); if (mprop == 0 || mprop->prop_ == 0) return 0; /* get the property ID from the method table entry */ propid = mprop->prop_->get_prop(); } /* set up a constant value for the property ID */ CTcConstVal pv; pv.set_prop(propid); /* return a constant parse node for the property ID */ return new CTPNConst(&pv); } /* * Find a metaclass symbol given the global identifier */ class CTcSymMetaclass *CTcGenTarg::find_meta_sym(const char *nm, size_t len) { tc_meta_entry *ent; int idx; /* find the entry */ ent = find_meta_entry(nm, len, TRUE, &idx); /* * if we found it, return the associated metaclass symbol; if * there's no entry, there's no symbol */ if (ent != 0) return ent->sym; else return 0; } /* * Find or add a metaclass entry */ int CTcGenTarg::find_or_add_meta(const char *nm, size_t len, CTcSymMetaclass *sym) { tc_meta_entry *ent; int idx; /* find the entry */ ent = find_meta_entry(nm, len, TRUE, &idx); /* if we found it, return the index */ if (ent != 0) { /* * found it - if it didn't already have a symbol mapping, use * the new symbol; if there is a symbol in the table entry * already, however, do not change it */ if (ent->sym == 0) ent->sym = sym; /* return the index */ return idx; } /* we didn't find an existing entry - add a new one */ return add_meta(nm, len, sym); } /* * Get the symbol for a given metaclass dependency table entry */ CTcSymMetaclass *CTcGenTarg::get_meta_sym(int meta_idx) { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return 0; /* return this entry's symbol */ return ent->sym; } /* * Get the external name for the given metaclass index */ const char *CTcGenTarg::get_meta_name(int meta_idx) const { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return 0; /* return this entry's external name */ return ent->nm; } /* * Set the symbol for a given metaclass dependency table entry */ void CTcGenTarg::set_meta_sym(int meta_idx, class CTcSymMetaclass *sym) { tc_meta_entry *ent; /* find the list entry at the given index */ for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; ent = ent->nxt, --meta_idx) ; /* if we didn't find the entry, do nothing */ if (ent == 0 || meta_idx != 0) return; /* set this entry's symbol */ ent->sym = sym; } /* * Add an entry to the function set dependency table */ ushort CTcGenTarg::add_fnset(const char *nm, size_t len) { tc_fnset_entry *ent; const char *sl; size_t sl_len; ushort idx; /* find the version part of the new name, if present */ for (sl = nm, sl_len = len ; sl_len != 0 && *sl != '/' ; ++sl, --sl_len) ; /* look for an existing entry with the same name prefix */ for (idx = 0, ent = fnset_head_ ; ent != 0 ; ent = ent->nxt, ++idx) { char *ent_sl; /* find the version part of this entry's name, if present */ for (ent_sl = ent->nm ; *ent_sl != '\0' && *ent_sl != '/' ; ++ent_sl) ; /* check to see if the prefixes match */ if (ent_sl - ent->nm == sl - nm && memcmp(ent->nm, nm, sl - nm) == 0) { /* * This one matches. Keep the one with the higher version * number. If one has a version number and the other doesn't, * keep the one with the version number. */ if (*ent_sl == '/' && sl_len != 0) { /* * Both have version numbers - keep the higher version. * Limit the version length to 6 characters plus the * slash. */ if (sl_len > 7) sl_len = 7; /* check if the new version number is higher */ if (memcmp(sl, ent_sl, sl_len) > 0) { /* the new one is higher - copy it over the old one */ memcpy(ent_sl, sl, sl_len); } /* * in any case, we're going to keep the existing entry, so * we're done - just return the existing entry's index */ return idx; } else if (*ent_sl == '/') { /* * only the old entry has a version number, so keep it and * ignore the new definition - this means we're done, so * just return the existing item's index */ return idx; } else { /* * Only the new entry has a version number, so store the * new version number. To do this, simply copy the new * entry over the old entry, but limit the version number * field to 7 characters including the slash. */ if (sl_len > 7) len -= (sl_len - 7); /* copy the new value */ memcpy(ent->nm, nm, len); /* done - return the existing item's index */ return idx; } } } /* * Allocate a new entry for the item. Always allocate space for a * version number, even if the entry doesn't have a version number - * if the part from the slash on is 7 characters or more, add nothing, * else add enough to pad it out to seven characters. */ ent = (tc_fnset_entry *)t3malloc(sizeof(tc_fnset_entry) + len + (sl_len < 7 ? 7 - sl_len : 0)); if (ent == 0) err_throw(TCERR_CODEGEN_NO_MEM); /* copy the name into the entry */ memcpy(ent->nm, nm, len); ent->nm[len] = '\0'; /* link the entry in at the end of the list */ ent->nxt = 0; if (fnset_tail_ != 0) fnset_tail_->nxt = ent; else fnset_head_ = ent; fnset_tail_ = ent; /* count the entry, returning the index of the entry in the list */ return fnset_cnt_++; } /* * get a function set's name given its index */ const char *CTcGenTarg::get_fnset_name(int idx) const { tc_fnset_entry *ent; /* scan the linked list to find the given index */ for (ent = fnset_head_ ; idx != 0 && ent != 0 ; ent = ent->nxt, --idx) ; /* return the one we found */ return ent->nm; } /* * Determine if we can skip an opcode because it is unreachable from the * previous instruction. */ int CTcGenTarg::can_skip_op() { /* * if the previous instruction was a return or throw of some kind, * we can skip any subsequent opcodes until a label is defined */ switch(last_op_) { case OPC_RET: case OPC_RETVAL: case OPC_RETTRUE: case OPC_RETNIL: case OPC_THROW: case OPC_JMP: case OPC_LRET: /* it's a return, throw, or jump - this new op is unreachable */ return TRUE; default: /* this new op is reachable */ return FALSE; } } /* * Remove the last JMP instruction */ void CTcGenTarg::remove_last_jmp() { /* a JMP instruction is three bytes long, so back up three bytes */ G_cs->dec_ofs(3); } /* * Add a line record */ void CTcGenTarg::add_line_rec(CTcTokFileDesc *file, long linenum) { /* include line records only in debug mode */ if (G_debug) { /* * clear the peephole, to ensure that the line boundary isn't * blurred by code optimization */ clear_peephole(); /* add the record to the code stream */ G_cs->add_line_rec(file, linenum); } } /* * Write an opcode to the output stream. We'll watch for certain * combinations of opcodes being generated, and apply peephole * optimization when we see sequences that can be collapsed to more * efficient single instructions. */ void CTcGenTarg::write_op(uchar opc) { int prv_len; int op_len; /* write the new opcode byte to the output stream */ G_cs->write((char)opc); /* we've only written one byte so far for the current instruction */ op_len = 1; /* presume the previous instruction length is just one byte */ prv_len = 1; /* * check for pairs of instructions that we can reduce to more * efficient single instructions */ try_combine: switch(opc) { case OPC_DUP: /* combine GETR0 + DUP -> DUPR0 */ if (last_op_ == OPC_GETR0) { opc = OPC_DUPR0; goto combine; } break; case OPC_DISC: /* combine DISC+DISC -> DISC1<2> */ if (last_op_ == OPC_DISC) { /* * Switch the old DISC to DISC1<2>. We're deleting a one-byte * op and replacing a one-byte op with a two-byte op, so we're * going from two bytes to two bytes total - no net deletion * (op_len = 0). The new DISC1 is a two-byte instruction * (prv_len). */ op_len = 0; prv_len = 2; opc = OPC_DISC1; /* write the <2> operand, overwriting the second DISC */ G_cs->write_at(G_cs->get_ofs() - 1, 2); /* go replace the op */ goto combine; } break; case OPC_DISC1: /* combine DISC1 + DISC -> DISC1 */ if (last_op_ == OPC_DISC && (uchar)G_cs->get_byte_at(G_cs->get_ofs() - 2) != 0xff) { /* up the DISC1 operand */ G_cs->write_at(G_cs->get_ofs() - 2, G_cs->get_byte_at(G_cs->get_ofs() - 2) + 1); /* keep the DISC1 */ opc = OPC_DISC1; prv_len = 2; /* apply the combination */ goto combine; } break; case OPC_JF: /* * if the last instruction was a comparison, we can use the * opposite compare-and-jump instruction */ switch(last_op_) { case OPC_NOT: /* invert the sense of the test */ opc = OPC_JT; goto combine; combine: /* * delete the new opcode we wrote, since we're going to combine * it with the preceding opcode */ G_cs->dec_ofs(op_len); /* overwrite the preceding opcode with the new combined opcode */ G_cs->write_at(G_cs->get_ofs() - prv_len, opc); /* roll back our internal peephole */ last_op_ = second_last_op_; second_last_op_ = OPC_NOP; /* * we've deleted our own opcode, so the current (most recent) * instruction in the output stream has the length of the * current opcode */ op_len = prv_len; /* presume the previous opcode is one byte again */ prv_len = 1; /* * go back for another try, since we may be able to do a * three-way combination (for example, GT/NOT/JT would * change to GT/JF, which would in turn change to JLE) */ goto try_combine; case OPC_EQ: opc = OPC_JNE; goto combine; case OPC_NE: opc = OPC_JE; goto combine; case OPC_LT: opc = OPC_JGE; goto combine; case OPC_LE: opc = OPC_JGT; goto combine; case OPC_GT: opc = OPC_JLE; goto combine; case OPC_GE: opc = OPC_JLT; goto combine; case OPC_GETR0: opc = OPC_JR0F; goto combine; } break; case OPC_JE: /* * if we just pushed nil, convert the PUSHNIL + JE to JNIL, since * we simply want to jump if a value is nil */ if (last_op_ == OPC_PUSHNIL) { /* convert it to a jump-if-nil */ opc = OPC_JNIL; goto combine; } break; case OPC_JNE: /* if we just pushed nil, convert to JNOTNIL */ if (last_op_ == OPC_PUSHNIL) { /* convert to jump-if-not-nil */ opc = OPC_JNOTNIL; goto combine; } break; case OPC_JT: /* * if the last instruction was a comparison, we can use a * compare-and-jump instruction */ switch(last_op_) { case OPC_NOT: /* invert the sense of the test */ opc = OPC_JF; goto combine; case OPC_EQ: opc = OPC_JE; goto combine; case OPC_NE: opc = OPC_JNE; goto combine; case OPC_LT: opc = OPC_JLT; goto combine; case OPC_LE: opc = OPC_JLE; goto combine; case OPC_GT: opc = OPC_JGT; goto combine; case OPC_GE: opc = OPC_JGE; goto combine; case OPC_GETR0: opc = OPC_JR0T; goto combine; } break; case OPC_NOT: /* * If the previous instruction was a comparison test of some * kind, we can invert the sense of the test. If the previous * instruction was a BOOLIZE op, we can eliminate it entirely, * because the NOT will perform the same conversion before * negating the value. If the previous was a NOT, we're * inverting an inversion; we can simply perform a single * BOOLIZE to get the same effect. */ switch(last_op_) { case OPC_EQ: opc = OPC_NE; goto combine; case OPC_NE: opc = OPC_EQ; goto combine; case OPC_GT: opc = OPC_LE; goto combine; case OPC_GE: opc = OPC_LT; goto combine; case OPC_LT: opc = OPC_GE; goto combine; case OPC_LE: opc = OPC_GT; goto combine; case OPC_BOOLIZE: opc = OPC_NOT; goto combine; case OPC_NOT: opc = OPC_BOOLIZE; goto combine; } break; case OPC_RET: /* * If we're writing a return instruction immediately after * another return instruction, we can skip the additional * instruction, since it will never be reached. This case * typically arises only when we generate the catch-all RET * instruction at the end of a function. */ switch(last_op_) { case OPC_RET: case OPC_RETVAL: case OPC_RETNIL: case OPC_RETTRUE: /* simply suppress this additional RET instruction */ return; } break; case OPC_RETNIL: /* we don't need to write two RETNIL's in a row */ if (last_op_ == OPC_RETNIL) return; break; case OPC_RETTRUE: /* we don't need to write two RETTRUE's in a row */ if (last_op_ == OPC_RETTRUE) return; break; case OPC_RETVAL: /* check the last opcode */ switch(last_op_) { case OPC_GETR0: /* * if we just pushed R0 onto the stack, we can compress the * GETR0 + RETVAL sequence into a simple RET, since RET leaves * the R0 value unchanged */ opc = OPC_RET; goto combine; case OPC_PUSHNIL: /* PUSHNIL + RET can be converted to RETNIL */ opc = OPC_RETNIL; goto combine; case OPC_PUSHTRUE: /* PUSHTRUE + RET can be converted to RETTRUE */ opc = OPC_RETTRUE; goto combine; } break; case OPC_SETLCL1: /* combine GETR0 + SETLCL1 -> SETLCL1R0 */ if (last_op_ == OPC_GETR0) { /* generate a combined SETLCL1R0 */ opc = OPC_SETLCL1R0; goto combine; } /* combine DUP + SETLCL1 -> GETSETLCL1 */ if (last_op_ == OPC_DUP) { opc = OPC_GETSETLCL1; goto combine; } /* combine DUPR0 + SETLCL1 -> GETSETLCL1R0 */ if (last_op_ == OPC_DUPR0) { opc = OPC_GETSETLCL1R0; goto combine; } break; break; case OPC_GETPROP: /* check the previous instruction for combination possibilities */ switch(last_op_) { case OPC_GETLCL1: /* get property of one-byte-addressable local */ opc = OPC_GETPROPLCL1; /* overwrite the preceding two-byte instruction */ prv_len = 2; goto combine; case OPC_GETLCLN0: case OPC_GETLCLN1: case OPC_GETLCLN2: case OPC_GETLCLN3: case OPC_GETLCLN4: case OPC_GETLCLN5: /* add a byte to pad things to the same length as a GETLCL1 */ G_cs->write(0); /* write the local number operand of the GETPROPLCL1 */ G_cs->write_at(G_cs->get_ofs() - 2, last_op_ - OPC_GETLCLN0); /* get property of one-byte-addressable local */ opc = OPC_GETPROPLCL1; /* overwrite the preceding now-two-byte instruction */ prv_len = 2; goto combine; case OPC_GETR0: /* get property of R0 */ opc = OPC_GETPROPR0; goto combine; } break; case OPC_CALLPROP: /* check the previous instruction */ switch(last_op_) { case OPC_GETR0: /* call property of R0 */ opc = OPC_CALLPROPR0; goto combine; } break; case OPC_INDEX: /* we can combine small integer constants with INDEX */ switch(last_op_) { case OPC_PUSH_0: case OPC_PUSH_1: /* * We can combine these into IDXINT8, but we must write an * extra byte for the index value. Go back and plug in the * extra index value byte, and add another byte at the end of * the stream to compensate for the insertion. (We're just * going to remove and overwrite everything after the inserted * byte, so don't bother actually fixing up that part with real * data; we merely need to make sure we have the right number * of bytes in the stream.) */ G_cs->write_at(G_cs->get_ofs() - 1, last_op_ == OPC_PUSH_0 ? 0 : 1); G_cs->write(0); /* combine the instructions */ opc = OPC_IDXINT8; prv_len = 2; goto combine; case OPC_PUSHINT8: /* combine the PUSHINT8 + INDEX into IDXINT8 */ opc = OPC_IDXINT8; prv_len = 2; goto combine; } break; case OPC_IDXINT8: /* we can replace GETLCL1 + IDXINT8 with IDXLCL1INT8 */ if (last_op_ == OPC_GETLCL1) { /* add the index operand at the second byte after the GETLCL1 */ uchar idx = G_cs->get_byte_at(G_cs->get_ofs() - op_len + 1); G_cs->write_at(G_cs->get_ofs() - op_len, idx); /* * we're going from GETLCL1 IDXINT8 (4 bytes) to * IDXLCL1INT8 (3 bytes), for a net deletion of one * byte - but that's only if we've already written the * operand, so the net deletion is actually op_len-1 */ op_len -= 1; /* go back and combine into what's now a three-byte opcode */ opc = OPC_IDXLCL1INT8; prv_len = 3; goto combine; } if (last_op_ >= OPC_GETLCLN0 && last_op_ <= OPC_GETLCLN5) { /* write the local# at the byte after the GETLCLNx */ G_cs->write_at(G_cs->get_ofs() - op_len, last_op_ - OPC_GETLCLN0); /* * if we haven't already written the IDXINT8 operand, write a * placeholder for it, since 'combine:' can't add bytes */ if (op_len == 1) G_cs->write(0); /* * We're going from GETLCLNx IDXINT8 (3 bytes) to * IDXLCL1INT8 (3 bytes), so there's no net deletion. */ op_len = 0; /* go back and combine into the three-byte index-by-local op */ opc = OPC_IDXLCL1INT8; prv_len = 3; goto combine; } break; case OPC_SETIND: /* we can replace GETLCL1 + + SETIND with SETINDLCL1I8 */ if (second_last_op_ == OPC_GETLCL1 || (second_last_op_ >= OPC_GETLCLN0 && second_last_op_ <= OPC_GETLCLN5)) { int idx, lcl; int getlcl_len, push_len; /* get the local number and the GETLCL instruction length */ if (second_last_op_ == OPC_GETLCL1) { /* it's GETLCL1 - two bytes */ getlcl_len = 2; } else { /* no operands - the local is encoded in the opcode itself */ lcl = second_last_op_ - OPC_GETLCLN0; getlcl_len = 1; } /* check the middle opcode for an int push */ switch(last_op_) { case OPC_PUSHINT8: /* get the index from the PUSHINT8 */ idx = (int)(uchar)G_cs->get_byte_at(G_cs->get_ofs() - 2); push_len = 2; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); /* * PUSHINT8 pushes a signed value, but SETINDLCL1I8 uses an * unsigned value, so make sure the PUSHINT8 value is in * range */ if (idx < 127) goto combine_setind; /* not combinable */ break; case OPC_PUSHINT: /* get the int value */ idx = G_cs->read4_at(G_cs->get_ofs() - 5); push_len = 5; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); /* if the index is 0-255, we can combine the instructions */ if (idx >= 0 && idx <= 255) goto combine_setind; /* not combinable */ break; case OPC_PUSH_0: case OPC_PUSH_1: /* it's a one-byte PUSH */ idx = last_op_ - OPC_PUSH_0; push_len = 1; /* get the local, if it's a GETLCL1 */ if (second_last_op_ == OPC_GETLCL1) lcl = G_cs->get_byte_at(G_cs->get_ofs() - push_len - 2); combine_setind: /* write the operands of the SETINDLCLI8 */ G_cs->write_at( G_cs->get_ofs() - getlcl_len - push_len, (uchar)lcl); G_cs->write_at( G_cs->get_ofs() - getlcl_len - push_len + 1, (uchar)idx); /* * Go back and do the combine. Remove GETLCL, * PUSH, and SETIND (getlcl_len + push_len + 1 bytes), * and replace them with SETINDLCL1I8 (3 bytes). * The net deletion goes in op_len, and the new length goes * in prv_len. Also, since we're combining three * instructions into one, we're losing our second-to-last * opcode. */ opc = OPC_SETINDLCL1I8; second_last_op_ = OPC_NOP; op_len = getlcl_len + push_len - 2; prv_len = 3; goto combine; } } break; default: /* write this instruction as-is */ break; } /* remember the last opcode we wrote */ second_last_op_ = last_op_; last_op_ = opc; } /* * Write a CALLPROP instruction, combining with preceding opcodes if * possible. */ void CTcGenTarg::write_callprop(int argc, int varargs, vm_prop_id_t prop) { /* * if the previous instruction was GETLCL1, combine it with the * CALLPROP to form a single CALLPROPLCL1 instruction */ if (last_op_ == OPC_GETLCL1 || (last_op_ >= OPC_GETLCLN0 && last_op_ <= OPC_GETLCLN5)) { uchar lcl; /* if it was a GETLCLNx, expand it out to a GETLCL1 */ if (last_op_ == OPC_GETLCL1) { /* it's already a GETLCL1 - just get the index */ lcl = G_cs->get_byte_at(G_cs->get_ofs() - 1); /* back up and delete the GETLCL1 instruction */ G_cs->dec_ofs(2); } else { /* it's a GETLCLNx - infer the local number from the opcode */ lcl = last_op_ - OPC_GETLCLN0; /* delete the instruction */ G_cs->dec_ofs(1); } /* roll back the peephole for the instruction deletion */ last_op_ = second_last_op_; second_last_op_ = OPC_NOP; /* write the varargs modifier if appropriate */ if (varargs) write_op(OPC_VARARGC); /* write the CALLPROPLCL1 */ write_op(OPC_CALLPROPLCL1); G_cs->write((char)argc); G_cs->write(lcl); G_cs->write_prop_id(prop); } else { /* generate the varargs modifier if appropriate */ if (varargs) write_op(OPC_VARARGC); /* we have arguments - generate a CALLPROP */ write_op(OPC_CALLPROP); G_cs->write((char)argc); G_cs->write_prop_id(prop); } /* callprop removes arguments and the object */ note_pop(argc + 1); } /* * Note a string's length */ void CTcGenTarg::note_str(size_t len) { /* if it's the longest so far, remember it */ if (len > max_str_len_) { /* * flag an warning the length plus overhead would exceed 32k * (only do this the first time we cross this limit) */ if (len > (32*1024 - VMB_LEN) && max_str_len_ <= (32*1024 - VMB_LEN)) G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); /* remember the length */ max_str_len_ = len; } } /* * note a list's length */ void CTcGenTarg::note_list(size_t element_count) { /* if it's the longest list so far, remember it */ if (element_count > max_list_cnt_) { /* flag a warning if the stored length would be over 32k */ if (element_count > ((32*1024 - VMB_LEN) / VMB_DATAHOLDER) && max_list_cnt_ <= ((32*1024 - VMB_LEN) / VMB_DATAHOLDER)) G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); /* remember the length */ max_list_cnt_ = element_count; } } /* * Note a bytecode block length */ void CTcGenTarg::note_bytecode(ulong len) { /* if it's the longest bytecode block yet, remember it */ if (len > max_bytecode_len_) { /* flag a warning the first time we go over 32k */ if (len >= 32*1024 && max_bytecode_len_ < 32*1024) G_tok->log_warning(TCERR_CODE_POOL_OVER_32K); /* remember the new length */ max_bytecode_len_ = len; } } /* * Hash table entry for an interned string. This keeps track of a string * that we've previously generated to the data segment, for reuse if we * need to generate another copy of the same string literal. */ class CInternEntry: public CVmHashEntryCS { public: CInternEntry(const char *str, size_t len, CTcStreamAnchor *anchor) : CVmHashEntryCS(str, len, FALSE) { anchor_ = anchor; } /* the string's fixup list anchor */ CTcStreamAnchor *anchor_; }; /* * Add a string to the constant pool */ void CTcGenTarg::add_const_str(const char *str, size_t len, CTcDataStream *ds, ulong ofs) { /* * If it's a short string, look to see if we've already written it. If * so, we can reuse the previous copy. The odds of a string being * duplicated generally decrease rapidly with the length of the string, * and the cost of checking for an old copy increases, so don't bother * if the string is over a threshold length. */ const size_t intern_threshold = 40; if (len < intern_threshold) { /* look for an old copy */ CInternEntry *e = (CInternEntry *)strtab_->find(str, len); /* * If we found it, simply add a fixup for the new use to the * existing fixup list. This will cause the caller to point the * new literal to the existing copy, without generating a duplicate * copy in the data segment. */ if (e != 0) { /* * add a fixup to the existing string's fixup list for the * caller's new reference */ CTcAbsFixup::add_abs_fixup(e->anchor_->fixup_list_head_, ds, ofs); /* we're done */ return; } } /* * Add an anchor for the item, and add a fixup for the reference * from ds@ofs to the item. */ CTcStreamAnchor *anchor = G_ds->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); /* * If this is a short string, add it to our list of interned strings. * This will let us reuse this copy of the string in the data segment * if we need to generate another literal later with identical * contents. */ if (len < intern_threshold) strtab_->add(new CInternEntry(str, len, anchor)); /* write the length prefix */ G_ds->write2(len); /* write the string bytes */ G_ds->write(str, len); /* note the length of the string stored */ note_str(len); } /* * Add a list to the constant pool */ void CTcGenTarg::add_const_list(CTPNList *lst, CTcDataStream *ds, ulong ofs) { int i; CTPNListEle *cur; ulong dst; CTcStreamAnchor *anchor; /* * Add an anchor for the item, and add a fixup for the reference * from ds@ofs to the item. */ anchor = G_ds->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); /* * Reserve space for the list. We need to do this first, because * the list might contain elements which themselves must be written * to the data stream; we must therefore reserve space for the * entire list before we start writing its elements. */ dst = G_ds->reserve(2 + lst->get_count()*VMB_DATAHOLDER); /* set the length prefix */ G_ds->write2_at(dst, lst->get_count()); dst += 2; /* store the elements */ for (i = 0, cur = lst->get_head() ; cur != 0 ; ++i, cur = cur->get_next(), dst += VMB_DATAHOLDER) { CTcConstVal *ele; /* get this element */ ele = cur->get_expr()->get_const_val(); /* write it to the element buffer */ write_const_as_dh(G_ds, dst, ele); } /* make sure the list wasn't corrupted */ if (i != lst->get_count()) G_tok->throw_internal_error(TCERR_CORRUPT_LIST); /* note the number of elements in the list */ note_list(lst->get_count()); } /* * Generate a BigNumber object */ vm_obj_id_t CTcGenTarg::gen_bignum_obj( const char *txt, size_t len, int promoted) { /* if the value was promoted from integer due to overflow, warn */ if (promoted) G_tok->log_warning(TCERR_INT_CONST_OV); /* write to BigNumber object stream */ CTcDataStream *str = G_bignum_stream; /* generate a new object ID for the BigNumber */ vm_obj_id_t id = new_obj_id(); /* * add the object ID to the non-symbol object list - this is * necessary to ensure that the object ID is fixed up during linking */ G_prs->add_nonsym_obj(id); /* * Get the BigNumber representation, inferring the radix. The * tokenizer can pass us integer strings with '0x' (hex) or '0' (octal) * prefixes when the numbers they contain are too big for the 32-bit * signed integer type. Floating-point values never have a radix * prefix. */ size_t bnlen; char *bn = CVmObjBigNum::parse_str_alo(bnlen, txt, len, 0); /* write the OBJS header - object ID plus byte count for metaclass data */ str->write_obj_id(id); str->write2(bnlen); /* write the BigNumber contents */ str->write(bn, bnlen); /* free the BigNumber value */ delete [] bn; /* return the new object ID */ return id; } /* * Generate a RexPattern object */ vm_obj_id_t CTcGenTarg::gen_rexpat_obj(const char *txt, size_t len) { /* write to RexPattern object stream */ CTcDataStream *rs = G_rexpat_stream; /* generate a new object ID for the RexPattern object */ vm_obj_id_t id = new_obj_id(); /* add the object ID to the non-symbol object list */ G_prs->add_nonsym_obj(id); /* * write the OBJS header - object ID plus byte count for metaclass data * (a DATAHOLDER pointing to the constant string) */ rs->write_obj_id(id); rs->write2(VMB_DATAHOLDER); /* * Add the source text as an ordinary constant string. Generate its * fixup in the RexPattern stream, in the DATAHOLDER we're about to * write. */ add_const_str(txt, len, rs, rs->get_ofs() + 1); /* * Set up the constant string value. Use zero as the pool offset, * since the fixup for the constant string will fill this in when the * pool layout is finalized. */ vm_val_t val; val.set_sstring(0); /* write the RexPattern data (a DATAHOLDER for the const string) */ char buf[VMB_DATAHOLDER]; vmb_put_dh(buf, &val); rs->write(buf, VMB_DATAHOLDER); /* return the new object ID */ return id; } /* * Convert a constant value from a CTcConstVal (compiler internal * representation) to a vm_val_t (interpreter representation). */ void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, ulong ofs, const CTcConstVal *src) { vm_val_t val; char buf[VMB_DATAHOLDER]; /* convert according to the value's type */ switch(src->get_type()) { case TC_CVT_NIL: val.set_nil(); break; case TC_CVT_TRUE: val.set_true(); break; case TC_CVT_INT: val.set_int(src->get_val_int()); break; case TC_CVT_FLOAT: /* generate the BigNumber object */ val.set_obj(gen_bignum_obj(src->get_val_float(), src->get_val_float_len(), src->is_promoted())); /* add a fixup for the object ID */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj); break; case TC_CVT_SSTR: /* * Store the string in the constant pool. Note that our fixup * is at the destination stream offset plus one, since the * DATAHOLDER has the type byte followed by the offset value. */ add_const_str(src->get_val_str(), src->get_val_str_len(), ds, ofs + 1); /* * set the offset to zero for now - the fixup that * add_const_str() generates will take care of supplying the * real value */ val.set_sstring(0); break; case TC_CVT_RESTR: /* generate the RexPattern object */ val.set_obj(gen_rexpat_obj(src->get_val_str(), src->get_val_str_len())); /* add a fixup for the object ID */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj); break; case TC_CVT_LIST: /* * Store the sublist in the constant pool. Our fixup is at the * destination stream offset plus one, since the DATAHOLDER has * the type byte followed by the offset value. */ add_const_list(src->get_val_list(), ds, ofs + 1); /* * set the offset to zero for now - the fixup that * add_const_list() generates will take care of supplying the * real value */ val.set_list(0); break; case TC_CVT_OBJ: /* set the object ID value */ val.set_obj((vm_obj_id_t)src->get_val_obj()); /* * add a fixup (at the current offset plus one, for the type * byte) if we're keeping object ID fixups */ if (G_keep_objfixups) CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, src->get_val_obj()); break; case TC_CVT_ENUM: /* set the enum value */ val.set_enum(src->get_val_enum()); /* add a fixup */ if (G_keep_enumfixups) CTcIdFixup::add_fixup(&G_enumfixup, ds, ofs + 1, src->get_val_enum()); break; case TC_CVT_PROP: /* set the property ID value */ val.set_propid((vm_prop_id_t)src->get_val_prop()); /* * add a fixup (at the current offset plus one, for the type * byte) if we're keeping property ID fixups */ if (G_keep_propfixups) CTcIdFixup::add_fixup(&G_propfixup, ds, ofs + 1, src->get_val_prop()); break; case TC_CVT_FUNCPTR: /* * use a placeholder value of zero for now - a function's final * address is never known until after all code generation has * been completed (the fixup will take care of supplying the * correct value when the time comes) */ val.set_fnptr(0); /* * Add a fixup. The fixup is at the destination stream offset * plus one, because the DATAHOLDER has a type byte followed by * the function pointer value. */ src->get_val_funcptr_sym()->add_abs_fixup(ds, ofs + 1); break; case TC_CVT_ANONFUNCPTR: /* use a placeholder of zero for now, until we fix up the pointer */ val.set_fnptr(0); /* add a fixup for the code body */ src->get_val_anon_func_ptr()->add_abs_fixup(ds, ofs + 1); break; case TC_CVT_VOCAB_LIST: /* * it's an internal vocabulary list type - this is used as a * placeholder only, and will be replaced during linking with an * actual vocabulary string list */ val.typ = VM_VOCAB_LIST; break; case TC_CVT_UNK: case TC_CVT_BIFPTR: /* unknown - ignore it */ break; } /* write the vm_val_t in DATA_HOLDER format into the stream */ vmb_put_dh(buf, &val); ds->write_at(ofs, buf, VMB_DATAHOLDER); } /* * Write a DATAHOLDER at the current offset in a stream */ void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, const CTcConstVal *src) { /* write to the current stream offset */ write_const_as_dh(ds, ds->get_ofs(), src); } /* * Notify that parsing is finished */ void CTcGenTarg::parsing_done() { /* nothing special to do */ } /* * notify the code generator that we're replacing an object */ void CTcGenTarg::notify_replace_object(ulong stream_ofs) { uint flags; /* set the 'replaced' flag in the flags prefix */ flags = G_os->read2_at(stream_ofs); flags |= TCT3_OBJ_REPLACED; G_os->write2_at(stream_ofs, flags); } /* * Set the starting offset of the current method */ void CTcGenTarg::set_method_ofs(ulong ofs) { /* tell the exception table object about it */ get_exc_table()->set_method_ofs(ofs); /* remember it in the code stream */ G_cs->set_method_ofs(ofs); } /* ------------------------------------------------------------------------ */ /* * Method header generator */ /* * Open a method */ void CTcGenTarg::open_method( CTcCodeStream *stream, CTcSymbol *fixup_owner_sym, CTcAbsFixup **fixup_list_head, CTPNCodeBody *code_body, CTcPrsSymtab *goto_tab, int argc, int opt_argc, int varargs, int is_constructor, int is_op_overload, int is_self_available, tct3_method_gen_ctx *ctx) { /* set the code stream as the current code generator output stream */ G_cs = ctx->stream = stream; /* set the method properties in the code generator */ set_in_constructor(is_constructor); set_in_op_overload(is_op_overload); stream->set_self_available(is_self_available); /* we obviously can't combine any past instructions */ clear_peephole(); /* clear the old line records */ stream->clear_line_recs(); /* clear the old frame list */ stream->clear_local_frames(); /* clear the old exception table */ get_exc_table()->clear_table(); /* reset the stack depth counters */ reset_sp_depth(); /* there are no enclosing 'switch' or block statements yet */ stream->set_switch(0); stream->set_enclosing(0); /* * remember where the method header starts - we'll need to come back * and fix up some placeholder entries in "Close" */ ctx->method_ofs = stream->get_ofs(); /* set the method start offset in the code generator */ set_method_ofs(ctx->method_ofs); /* set up a fixup anchor for the new method */ ctx->anchor = stream->add_anchor(fixup_owner_sym, fixup_list_head); /* set the anchor in the associated symbol, if applicable */ if (fixup_owner_sym != 0) fixup_owner_sym->set_anchor(ctx->anchor); /* * Generate the function header. At the moment, we don't know the * stack usage, exception table offset, or debug record offset, since * these all come after the byte code; we won't know how big the byte * code is until after we generate it. For now, write zero bytes as * placeholders for these slots; we'll come back and fix them up to * their real values after we've generated the byte code. */ stream->write(argc | (varargs ? 0x80 : 0)); /* argument count */ stream->write((char)opt_argc); /* additional optional argument count */ stream->write2(0); /* number of locals - won't know until after codegen */ stream->write2(0); /* total stack - won't know until after codegen */ stream->write2(0); /* exception table offset - presume none */ stream->write2(0); /* debug record offset - presume no debug records */ /* * If the header size in the globals is greater than the fixed size we * write, pad out the remainder with zero bytes. */ for (int i = G_sizes.mhdr - 10 ; i > 0 ; --i) stream->write(0); /* remember the starting offset of the code */ ctx->code_start_ofs = stream->get_ofs(); /* set the current code body being generated */ ctx->old_code_body = stream->set_code_body(code_body); /* get the 'goto' symbol table for this function */ stream->set_goto_symtab(goto_tab); } /* * Close a method */ void CTcGenTarg::close_method(int local_cnt, CTcPrsSymtab *local_symtab, CTcTokFileDesc *end_desc, long end_linenum, tct3_method_gen_ctx *ctx, CTcNamedArgTab *named_arg_tab_head) { /* get the output code stream from the context */ CTcCodeStream *stream = ctx->stream; /* * Generate a 'return' opcode with a default 'nil' return value - this * will ensure that code that reaches the end of the procedure returns * normally. If this is a constructor, return the 'self' object rather * than nil. * * We only need to generate this epilogue if the next instruction would * be reachable. If it's not reachable, then the code explicitly took * care of all types of exits. */ if (!can_skip_op()) { /* * add a line record for the implied return at the last source line * of the code body */ add_line_rec(end_desc, end_linenum); /* write the appropriate return */ if (is_in_constructor()) { /* we're in a constructor - return 'self' */ write_op(OPC_PUSHSELF); write_op(OPC_RETVAL); } else { /* * Normal method/function - return without a value (explicitly * set R0 to nil, though, so we don't return something returned * from a called function). */ write_op(OPC_RETNIL); } } /* generate any named argument tables */ for (CTcNamedArgTab *nt = named_arg_tab_head ; nt != 0 ; nt = nt->nxt) nt->generate(); /* * release labels allocated for the code block; this will log an error * if any labels are undefined */ stream->release_labels(); /* * Eliminate jump-to-jump sequences in the generated code. Don't * bother if we've found any errors, as the generated code will not * necessarily be valid if this is the case. */ if (G_tcmain->get_error_count() == 0) remove_jumps_to_jumps(stream, ctx->code_start_ofs); /* note the code block's end point */ ctx->code_end_ofs = stream->get_ofs(); /* * Fix up the local variable count in the function header. We might * allocate extra locals for internal use while generating code, so we * must wait until after generating our code before we know the final * local count. */ stream->write2_at(ctx->method_ofs + 2, local_cnt); /* * Fix up the total stack space indicator in the function header. The * total stack size must include the locals, as well as stack space * needed for intermediate computations. */ stream->write2_at(ctx->method_ofs + 4, get_max_sp_depth() + local_cnt); /* * Before writing the exception table, mark the endpoint of the range * of the main local scope and any enclosing scopes. */ for (CTcPrsSymtab *t = local_symtab ; t != 0 ; t = t->get_parent()) t->add_to_range(G_cs->get_ofs() - ctx->method_ofs); /* * Generate the exception table, if we have one. If we have no * exception records, leave the exception table offset set to zero to * indicate that there is no exception table for the method. */ if (get_exc_table()->get_entry_count() != 0) { /* * write the exception table offset - it's at the current offset in * the code */ stream->write2_at(ctx->method_ofs + 6, stream->get_ofs() - ctx->method_ofs); /* write the table */ get_exc_table()->write_to_code_stream(); } } /* * Clean up after generating a method */ void CTcGenTarg::close_method_cleanup(tct3_method_gen_ctx *ctx) { /* * Tell the code generator our code block byte length so that it can * keep track of the longest single byte-code block; it will use this * to choose the code pool page size when generating the image file. */ note_bytecode(ctx->anchor->get_len(ctx->stream)); /* we're no longer in a constructor, if we ever were */ set_in_constructor(FALSE); /* likewise an operator overload method */ set_in_op_overload(FALSE); /* clear the current code body */ ctx->stream->set_code_body(ctx->old_code_body); /* always leave the main code stream active by default */ G_cs = G_cs_main; } /* ------------------------------------------------------------------------ */ /* * Do post-CALL cleanup */ void CTcGenTarg::post_call_cleanup(const CTcNamedArgs *named_args) { /* write the named argument table pointer, if necessary */ if (named_args != 0 && named_args->cnt != 0) { /* * We need a named argument table. Add the table to the curren * code body, which will generate a label for us to use to * reference the table. */ CTcCodeLabel *tab_lbl = G_cs->get_code_body() ->add_named_arg_tab(named_args); /* write the op: NamedArgPtr , */ G_cg->write_op(OPC_NAMEDARGPTR); G_cs->write((char)named_args->cnt); G_cs->write_ofs2(tab_lbl, 0); /* NamedArgPtr pops the arguments when executed */ G_cg->note_pop(named_args->cnt); } } /* ------------------------------------------------------------------------ */ /* * Run through the generated code stream starting at the given offset (and * running up to the current offset), and eliminate jump-to-jump sequences: * * - whenever we find any jump instruction that points directly to an * unconditional jump, we'll change the first jump so that it points to * the target of the second jump, saving the unnecessary stop at the * intermediate jump * * - whenever we find an unconditional jump to any return or throw * instruction, we'll replace the jump with a copy of the target * return/throw instruction. */ void CTcGenTarg::remove_jumps_to_jumps(CTcCodeStream *str, ulong start_ofs) { ulong ofs; ulong end_ofs; uchar prv_op; /* * scan the code stream starting at the given offset, continuing * through the current offset */ prv_op = OPC_NOP; for (ofs = start_ofs, end_ofs = str->get_ofs() ; ofs < end_ofs ; ) { uchar op; ulong target_ofs; ulong orig_target_ofs; uchar target_op; int done; int chain_len; /* check the byte code instruction at the current location */ switch(op = str->get_byte_at(ofs)) { case OPC_RETVAL: /* * If our previous opcode was PUSHTRUE or PUSHNIL, we can * replace the previous opcode with RETTRUE or RETNIL. This * sequence can occur when we generate conditional code that * returns a value; in such cases, we sometimes can't elide * the PUSHx/RETVAL sequence during the original code * generation because the RETVAL itself is the target of a * label and thus must be retained as a separate instruction. * Converting the PUSHTRUE or PUSHNIL here won't do any harm, * as we'll still leave the RETVAL as a separate instruction. * Likewise, if the previous instruction was GET_R0, we can * change it to a simple RET. */ switch(prv_op) { case OPC_PUSHTRUE: /* convert the PUSHTRUE to a RETTRUE */ str->write_at(ofs - 1, OPC_RETTRUE); break; case OPC_PUSHNIL: /* convert the PUSHNIL to a RETNIL */ str->write_at(ofs - 1, OPC_RETNIL); break; case OPC_GETR0: /* convert the GETR0 to a RET */ str->write_at(ofs - 1, OPC_RET); break; } /* skip the RETVAL */ ofs += 1; break; case OPC_PUSHSTRI: /* * push in-line string: we have a UINT2 operand giving the * length in bytes of the string, followed by the bytes of the * string, so read the uint2 and then skip that amount plus * three additional bytes (one for the opcode, two for the * uint2 itself) */ ofs += 3 + str->readu2_at(ofs+1); break; case OPC_SWITCH: /* * Switch: we have a UINT2 giving the number of cases, * followed by the cases, followed by an INT2; each case * consists of a DATAHOLDER plus a UINT2, for a total of 7 * bytes. The total is thus 5 bytes (the opcode, the case * count UINT2, the final INT2) plus 7 bytes times the number * of cases. */ ofs += 1 + 2 + (VMB_DATAHOLDER + 2)*str->readu2_at(ofs+1) + 2; break; case OPC_NAMEDARGTAB: /* * NamedArgTab - the first operand is a UINT2 giving the length * of the table */ ofs += 1 + 2 + str->readu2_at(ofs+1); break; case OPC_JMP: /* * Unconditional jump: check for a jump to a RETURN of any * kind or a THROW. If the destination consists of either of * those, replace the JMP with the target instruction. If the * destination is an unconditional JMP, iteratively check its * destination. */ orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); /* * Iterate through any chain of JMP's we find. Abort if we * try following a chain longer than 20 jumps, in case we * should encounter any circular chains. */ for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; ++chain_len) { switch(target_op = str->get_byte_at(target_ofs)) { case OPC_RETVAL: /* * Check for a special sequence that we can optimize * even better than the usual. If we have a GETR0 * followed by a JMP to a RETVAL, then we can * eliminate the JMP *and* the GETR0, and just convert * the GETR0 to a RET. */ if (prv_op == OPC_GETR0) { /* * The GETR0 is the byte before the original JMP: * simply replace it with a RET. Note that we can * leave the original jump intact, in case anyone * else is pointing to it. */ str->write_at(ofs - 1, OPC_RET); /* we're done iterating the chain of jumps-to-jumps */ done = TRUE; } else { /* handle the same as any return instruction */ goto any_RET; } break; case OPC_RETNIL: case OPC_RETTRUE: case OPC_RET: case OPC_THROW: any_RET: /* * it's a THROW or RETURN of some kind - simply copy * it to the current slot; write NOP's over our jump * offset operand, to make sure the code continues to * be deterministically readable */ str->write_at(ofs, target_op); str->write_at(ofs + 1, (uchar)OPC_NOP); str->write_at(ofs + 2, (uchar)OPC_NOP); /* we're done iterating the chain of jumps-to-jumps */ done = TRUE; break; case OPC_JMP: /* * We're jumping to another jump - there's no reason * to stop at the intermediate jump instruction, since * we can simply jump directly to the destination * address. Calculate the new target address, and * continue iterating, in case this jumps to something * we can further optimize away. */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* * if it's a jump to the original location, it must be * some unreachable code that generated a circular * jump; ignore it in this case */ if (target_ofs == ofs) done = TRUE; /* proceed */ break; default: /* * For anything else, we're done with any chain of * jumps to jumps. If we indeed found a new target * address, rewrite the original JMP instruction so * that it jumps directly to the end of the chain * rather than going through the intermediate jumps. */ if (target_ofs != orig_target_ofs) str->write2_at(ofs + 1, target_ofs - (ofs + 1)); /* we're done iterating */ done = TRUE; break; } } /* whatever happened, skip past the jump */ ofs += 3; /* done */ break; case OPC_JT: case OPC_JF: case OPC_JE: case OPC_JNE: case OPC_JGT: case OPC_JGE: case OPC_JLT: case OPC_JLE: case OPC_JST: case OPC_JSF: case OPC_JNIL: case OPC_JNOTNIL: case OPC_JR0T: case OPC_JR0F: /* * We have a jump (conditional or otherwise). Check the * target instruction to see if it's an unconditional jump; if * so, then we can jump straight to the target of the second * jump, since there's no reason to stop at the intermediate * jump instruction on our way to the final destination. Make * this check iteratively, so that we eliminate any chain of * jumps to jumps and land at our final non-jump instruction * in one go. */ orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; ++chain_len) { uchar target_op; /* get the target opcode */ target_op = str->get_byte_at(target_ofs); /* * if the target is an unconditional JMP, we can retarget * the original instruction to jump directly to the target * of the target JMP, bypassing the target JMP entirely and * thus avoiding some unnecessary work at run-time */ if (target_op == OPC_JMP) { /* * retarget the original jump to go directly to the * target of the target JMP */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* * continue scanning for more opportunities, as the new * target could also point to something we can bypass */ continue; } /* * Certain combinations are special. If the original * opcode was a JST or JSF, and the target is a JT or JF, * we can recode the sequence so that the original opcode * turns into a more efficient JT or JF and jumps directly * past the JT or JF. If we have a JST or JSF jumping to a * JST or JSF, we can also recode that sequence to bypass * the second jump. In both cases, we can recode the * sequence because the original jump will unequivocally * determine the behavior at the target jump in such a way * that we can compact the sequence into a single jump. */ switch(op) { case OPC_JSF: /* * the original is a JSF: we can recode a jump to a * JSF, JST, JF, or JT */ switch(target_op) { case OPC_JSF: /* * We're jumping to another JSF. Since the * original jump will only reach the target jump if * the value on the top of the stack is false, and * will then leave this same value on the stack to * be tested again with the target JSF, we know the * target JSF will perform its jump and leave the * stack unchanged again. So, we can simply * retarget the original jump to the target of the * target JSF. */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning for additional opportunities */ break; case OPC_JST: case OPC_JT: /* * We're jumping to a JST or a JT. Since the JSF * will only reach the JST/JT on a false value, we * know the JST/JT will NOT jump - we know for a * fact it will pop the non-true stack element and * proceed without jumping. Therefore, we can * avoid saving the value from the original JSF, * which means we can recode the original as the * simpler JF (which doesn't bother saving the * false value), and jump on false directly to the * instruction after the target JST/JT. */ str->write_at(ofs, (uchar)OPC_JF); op = OPC_JF; /* jump to the instruction after the target JST/JT */ target_ofs += 3; /* keep looking for more jumps */ break; case OPC_JF: /* * We're jumping to a JF: we know the JF will jump, * because we had to have - and then save - a false * value for the JSF to reach the JF in the first * place. Since we know for a fact the target JF * will remove the false value and jump to its * target, we can bypass the target JF by recoding * the original instruction as a simpler JF and * jumping directly to the target of the target JF. */ str->write_at(ofs, (uchar)OPC_JF); op = OPC_JF; /* jump to the tartet of the target JF */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning for more jumps */ break; default: /* can't make any assumptions about other targets */ done = TRUE; break; } break; case OPC_JST: /* * the original is a JST: recode it if the target is a * JSF, JST, JF, or JT */ switch(target_op) { case OPC_JST: /* JST jumping to JST: jump to the target's target */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep looking */ break; case OPC_JSF: case OPC_JF: /* * JST jumping to JSF/JF: the JSF/JF will * definitely pop the stack and not jump (since the * original JST will have left a true value on the * stack), so we can recode the JST as a more * efficient JT and jump to the instruction after * the JSF/JF target */ str->write_at(ofs, (uchar)OPC_JT); op = OPC_JT; /* jump to the instruction after the target */ target_ofs += 3; /* keep looking */ break; case OPC_JT: /* * JST jumping to JT: the JT will definitely pop * and jump, so we can recode the original as a * simpler JT and jump to the target's target */ str->write_at(ofs, (uchar)OPC_JT); op = OPC_JT; /* jump to the target of the target */ target_ofs = target_ofs + 1 + str->read2_at(target_ofs + 1); /* keep scanning */ break; default: /* can't make any assumptions about other targets */ done = TRUE; break; } break; default: /* * we can't make assumptions about anything else, so * we've come to the end of the road - stop scanning */ done = TRUE; break; } } /* * if we found a chain of jumps, replace our original jump * target with the final jump target, bypassing the * intermediate jumps */ if (target_ofs != orig_target_ofs) str->write2_at(ofs + 1, target_ofs - (ofs + 1)); /* skip past the jump */ ofs += 3; /* done */ break; default: /* * everything else is a fixed-size instruction, so simply * consult our table of instruction lengths to determine the * offset of the next instruction */ ofs += CVmOpcodes::op_siz[op]; break; } /* remember the preceding opcode */ prv_op = op; } } /* ------------------------------------------------------------------------ */ /* * Generic T3 node */ /* * generate a jump-ahead instruction, returning a new label which serves * as the jump destination */ CTcCodeLabel *CTcPrsNode::gen_jump_ahead(uchar opc) { /* * check to see if we should suppress the jump for peephole * optimization */ if (G_cg->can_skip_op()) return 0; /* emit the opcode */ G_cg->write_op(opc); /* allocate a new label */ CTcCodeLabel *lbl = G_cs->new_label_fwd(); /* * write the forward offset to the label (this will generate a fixup * record attached to the label, so that we'll come back and fix it * up when the real offset is known) */ G_cs->write_ofs2(lbl, 0); /* return the forward label */ return lbl; } /* * Allocate a new label at the current write position */ CTcCodeLabel *CTcPrsNode::new_label_here() { /* * suppress any peephole optimizations at this point -- someone * could jump directly to this instruction, so we can't combine an * instruction destined for this point with anything previous */ G_cg->clear_peephole(); /* create and return a label at the current position */ return G_cs->new_label_here(); } /* * define the position of a code label */ void CTcPrsNode::def_label_pos(CTcCodeLabel *lbl) { /* if the label is null, ignore it */ if (lbl == 0) return; /* * try eliminating a jump-to-next-instruction sequence: if the last * opcode was a JMP to this label, remove the last instruction * entirely */ if (G_cg->get_last_op() == OPC_JMP && G_cs->has_fixup_at_ofs(lbl, G_cs->get_ofs() - 2)) { /* remove the fixup pointing to the preceding JMP */ G_cs->remove_fixup_at_ofs(lbl, G_cs->get_ofs() - 2); /* the JMP is unnecessary - remove it */ G_cg->remove_last_jmp(); } /* define the label position and apply the fixup */ G_cs->def_label_pos(lbl); /* * whenever we define a label, we must suppress any peephole * optimizations at this point - someone could jump directly to this * instruction, so we can't combine an instruction destined for this * point with anything previous */ G_cg->clear_peephole(); } /* * Generate code for an if-else conditional test. The default * implementation is to evaluate the expression, and jump to the false * branch if the expression is false (or jump to the true part if the * expression is true and there's no false part). */ void CTcPrsNode::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { /* generate our expression code */ gen_code(FALSE, TRUE); /* * if we have a 'then' part, jump to the 'then' part if the condition * is true; otherwise, jump to the 'else' part if the condition is * false */ if (then_label != 0) { /* we have a 'then' part, so jump if true to the 'then' part */ G_cg->write_op(OPC_JT); G_cs->write_ofs2(then_label, 0); } else { /* we have an 'else' part, so jump if false to the 'else' */ G_cg->write_op(OPC_JF); G_cs->write_ofs2(else_label, 0); } /* the JF or JT pops an element off the stack */ G_cg->note_pop(); } /* * generate code for assignment to this node */ int CTcPrsNode::gen_code_asi(int, int phase, tc_asitype_t, const char *, CTcPrsNode *, int ignore_error, int, void **) { /* * If ignoring errors, the caller is trying to assign if possible but * doesn't require it to be possible; simply return false to indicate * that nothing happened if this is the case. Also ignore errors on * phase 2, since we'll already have reported an error on phase 1. */ if (ignore_error || phase > 1) return FALSE; /* we should never get here - throw an internal error */ G_tok->throw_internal_error(TCERR_GEN_BAD_LVALUE); AFTER_ERR_THROW(return FALSE;) } /* * generate code for taking the address of this node */ void CTcPrsNode::gen_code_addr() { /* we should never get here - throw an internal error */ G_tok->throw_internal_error(TCERR_GEN_BAD_ADDR); } /* * Generate code to call the expression as a function or method. */ void CTcPrsNode::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* function/method calls are never valid in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* * For default nodes, assume that the result of evaluating the * expression contained in the node is a method or function pointer. * First, generate code to evaluate the expression, which should * yield an appropriate pointer value. */ gen_code(FALSE, FALSE); /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* generate an indirect function call through the pointer */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* PTRCALL pops the arguments plus the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if the caller isn't going to discard the return value, push the * result, which is sitting in R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code for operator 'new' applied to this node */ void CTcPrsNode::gen_code_new(int, int, int, CTcNamedArgs *, int, int) { /* operator 'new' cannot be applied to a default node */ G_tok->log_error(TCERR_INVAL_NEW_EXPR); } /* * Generate code for a member evaluation */ void CTcPrsNode::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* evaluate my own expression to yield the object value */ gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the generic code to generate the rest */ s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * Generic code to generate the rest of a member expression after the * left side of the '.' has been generated. This can be used for cases * where the left of the '.' is an arbitrary expression, and hence must * be evaluated at run-time. */ void CTcPrsNode::s_gen_member_rhs(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* we can't call methods with arguments in speculative mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* get or generate the property ID value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple GETPROP or CALLPROP * instruction; otherwise, generate a PTRCALLPROP instruction */ if (prop != VM_INVALID_PROP) { /* * we have a constant property ID - generate a GETPROP or * CALLPROP with the property ID as constant data */ if (argc == 0) { /* no arguments - generate a GETPROP */ G_cg->write_op(G_cg->is_speculative() ? OPC_GETPROPDATA : OPC_GETPROP); G_cs->write_prop_id(prop); /* this pops an object element */ G_cg->note_pop(); } else { /* write the CALLPROP instruction */ G_cg->write_callprop(argc, varargs, prop); } } else { if (G_cg->is_speculative()) { /* * speculative - use PTRGETPROPDATA to ensure we don't cause * any side effects */ G_cg->write_op(OPC_PTRGETPROPDATA); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRCALLPROP */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write((int)argc); } /* * ptrcallprop/ptrgetpropdata removes arguments, the object, and * the property */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the result, push it from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code to get the property ID of the expression. */ vm_prop_id_t CTcPrsNode::gen_code_propid(int check_only, int is_expr) { /* * simply evaluate the expression normally, anticipating that this * will yield a property ID value at run-time */ if (!check_only) gen_code(FALSE, FALSE); /* tell the caller that there's no constant ID available */ return VM_INVALID_PROP; } /* ------------------------------------------------------------------------ */ /* * "self" */ /* * generate code */ void CTPNSelf::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* if we're not discarding the result, push the "self" object */ if (!discard) { G_cg->write_op(OPC_PUSHSELF); G_cg->note_push(); } } /* * evaluate a property */ void CTPNSelf::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* make sure "self" is available */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow arguments in speculative eval mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple GETPROPSELF or * CALLPROPSELF; otherwise, generate a PTRCALLPROPSELF */ if (prop != VM_INVALID_PROP) { /* * we have a constant property ID - generate a GETPROPDATA, * GETPROPSELF, or CALLPROPSELF with the property ID as * immediate data */ if (G_cg->is_speculative()) { /* speculative - use GETPROPDATA */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop); /* we pushed one element (self) and popped it back off */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* no arguments - generate a GETPROPSELF */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop); } else { /* add a varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have arguments - generate a CALLPROPSELF */ G_cg->write_op(OPC_CALLPROPSELF); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments */ G_cg->note_pop(argc); } } else { /* * a property pointer is on the stack - use PTRGETPROPDATA or * PTRCALLPROPSELF, depending on the speculative mode */ if (G_cg->is_speculative()) { /* speculative - use PTRGETPROPDATA after pushing self */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_PTRGETPROPDATA); /* we pushed self then removed self and the property ID */ G_cg->note_push(); G_cg->note_pop(2); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a prop pointer is on the stack - write a PTRCALLPROPSELF */ G_cg->write_op(OPC_PTRCALLPROPSELF); G_cs->write((int)argc); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSelf::gen_code_obj_predot(int *is_self) { /* make sure "self" is available */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* tell the caller that this is "self" */ *is_self = TRUE; return VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * "replaced" */ /* * evaluate 'replaced' on its own - this simply yields a function pointer * to the modified base code */ void CTPNReplaced::gen_code(int discard, int for_condition) { /* get the modified base function symbol */ CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); /* make sure we're in a 'modify func()' context */ if (mod_base == 0) G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); /* this expression yields a pointer to the modified base function */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the current code location */ if (mod_base != 0) mod_base->add_abs_fixup(G_cs); /* write a placeholder offset - arbitrarily use zero */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } /* * 'replaced()' call - this invokes the modified base code */ void CTPNReplaced::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* get the modified base function symbol */ CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); /* make sure we're in a 'modify func()' context */ if (mod_base == 0) G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call instruction and argument count */ G_cg->write_op(OPC_CALL); G_cs->write((char)argc); /* generate a fixup for the call to the modified base code */ if (mod_base != 0) mod_base->add_abs_fixup(G_cs); /* add a placeholder for the function address */ G_cs->write4(0); /* call removes arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* make sure the argument count is correct */ if (mod_base != 0 && !mod_base->argc_ok(argc)) { char buf[128]; G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, (int)mod_base->get_sym_len(), mod_base->get_sym(), mod_base->get_argc_desc(buf), argc); } /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "targetprop" */ /* * generate code */ void CTPNTargetprop::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_TARGETPROP_NOT_AVAIL); /* if we're not discarding the result, push the target property ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_TARGPROP); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "targetobj" */ /* * generate code */ void CTPNTargetobj::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_TARGETOBJ_NOT_AVAIL); /* if we're not discarding the result, push the target object ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_TARGOBJ); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "definingobj" */ /* * generate code */ void CTPNDefiningobj::gen_code(int discard, int) { /* it's an error if we're not in a method context */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_DEFININGOBJ_NOT_AVAIL); /* if we're not discarding the result, push the defining object ID */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_DEFOBJ); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "invokee" */ /* * generate code */ void CTPNInvokee::gen_code(int discard, int) { /* if we're not discarding the result, push the invokee value */ if (!discard) { G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "inherited" */ void CTPNInh::gen_code(int, int) { /* * we should never be asked to generate an "inherited" node * directly; these nodes should always be generated as part of * member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_INH); } /* * evaluate a property */ void CTPNInh::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* don't allow 'inherited' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* * If we're in a multi-method context, inherited() has a different * meaning from the ordinary method context meaning. */ for (CTPNCodeBody *cb = G_cs->get_code_body() ; cb != 0 ; cb = cb->get_enclosing()) { /* check for a multi-method context */ CTcSymFunc *f = cb->get_func_sym(); if (f != 0 && f->is_multimethod()) { /* generate the multi-method inherited() code */ gen_code_mminh(f, discard, prop_expr, prop_is_expr, argc, varargs, named_args); /* we're done */ return; } } /* * make sure "self" is available - we obviously can't inherit * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* a type list ('inherited<>') is invalid for regular method inheritance */ if (typelist_ != 0) G_tok->log_error(TCERR_MMINH_BAD_CONTEXT); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple INHERIT; * otherwise, generate a PTRINHERIT */ if (prop != VM_INVALID_PROP) { /* generate a varargs modifier if necessary */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular INHERIT */ G_cg->write_op(OPC_INHERIT); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments */ G_cg->note_pop(argc); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRINHERIT */ G_cg->write_op(OPC_PTRINHERIT); G_cs->write((int)argc); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } void CTPNInh::gen_code_mminh(CTcSymFunc *func, int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* * If the function has an associated global symbol from a modification, * use the global symbol instead. The modified function doesn't have a * real symbol, so we can't use it; but fortunately, the global symbol * applies to all modified versions as well, since they all inherit * from the same base function. */ while (func->get_mod_global() != 0) func = func->get_mod_global(); /* * There are two forms of inherited() for multi-methods. * * inherited(args) - this form takes an explicit type list, to * invoke a specific overridden version. * * inherited(args) - this form invokes the next inherited version, * determined dynamically at run-time. */ if (typelist_ == 0) { /* * It's the inherited(args) format. * * Call _multiMethodCallInherited(fromFunc, args). We've already * pushed the args list, so just add the source function argument * (which is simply our defining function). */ func->gen_code(FALSE); /* look up _multiMethodCallInherited */ CTcSymFunc *mmci = (CTcSymFunc *)G_prs->get_global_symtab()->find( "_multiMethodCallInherited", 25); if (mmci == 0 || mmci->get_type() != TC_SYM_FUNC) { /* undefined or incorrectly defined - log an error */ G_tok->log_error(TCERR_MMINH_MISSING_SUPPORT_FUNC, 25, "_multiMethodCallInherited"); } else { /* generate the call */ mmci->gen_code_call(discard, argc + 1, varargs, named_args); } } else { /* * It's the inherited(args) format. */ /* * Get the base name for the function. 'func' is the decorated * name for the containing function, which is of the form * 'Base*type1;type2...'. The base name is the part up to the * asterisk. */ const char *nm = func->getstr(), *p; size_t rem = func->getlen(); for (p = nm ; rem != 0 && *p != '*' ; ++p, --rem) ; /* make a token for the base name */ CTcToken btok; btok.set_text(nm, p - nm); /* build the decorated name for the target function */ CTcToken dtok; typelist_->decorate_name(&dtok, &btok); /* look up the decorated name */ CTcSymFunc *ifunc = (CTcSymFunc *)G_prs->get_global_symtab()->find( dtok.get_text(), dtok.get_text_len()); /* if we found it, call it */ if (ifunc != 0 && ifunc->get_type() == TC_SYM_FUNC) { /* generate the call */ ifunc->gen_code_call(discard, argc, varargs, named_args); } else { /* function not found */ G_tok->log_error(TCERR_MMINH_UNDEF_FUNC, (int)(p - nm), nm); } } } /* ------------------------------------------------------------------------ */ /* * "inherited class" */ void CTPNInhClass::gen_code(int discard, int for_condition) { /* * we should never be asked to generate an "inherited" node * directly; these nodes should always be generated as part of * member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_INH); } /* * evaluate a property */ void CTPNInhClass::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; CTcSymbol *objsym; vm_obj_id_t obj; /* * make sure "self" is available - we obviously can't inherit * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow 'inherited' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* get the superclass name symbol */ objsym = G_cs->get_symtab()->find_or_def_undef(sym_, len_, FALSE); /* if it's not an object, we can't inherit from it */ obj = objsym->get_val_obj(); if (obj == VM_INVALID_OBJ) { G_tok->log_error(TCERR_INH_NOT_OBJ, (int)len_, sym_); return; } /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple EXPINHERIT; otherwise, * generate a PTREXPINHERIT */ if (prop != VM_INVALID_PROP) { /* add a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular EXPINHERIT */ G_cg->write_op(OPC_EXPINHERIT); G_cs->write((char)argc); G_cs->write_prop_id(prop); G_cs->write_obj_id(obj); /* this removes argumnts */ G_cg->note_pop(argc); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTREXPINHERIT */ G_cg->write_op(OPC_PTREXPINHERIT); G_cs->write((int)argc); G_cs->write_obj_id(obj); /* this removes arguments and the property pointer */ G_cg->note_pop(argc + 1); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "delegated" */ void CTPNDelegated::gen_code(int discard, int for_condition) { /* * we should never be asked to generate a "delegated" node directly; * these nodes should always be generated as part of member evaluation */ G_tok->throw_internal_error(TCERR_GEN_CODE_DELEGATED); } /* * evaluate a property */ void CTPNDelegated::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* * make sure "self" is available - we obviously can't delegate * anything if we're not in an object's method */ if (!G_cs->is_self_available()) G_tok->log_error(TCERR_SELF_NOT_AVAIL); /* don't allow 'delegated' in speculative evaluation mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* generate the delegatee expression */ delegatee_->gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* generate the property value */ prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); /* * if we got a property ID, generate a simple DELEGATE; otherwise, * generate a PTRDELEGATE */ if (prop != VM_INVALID_PROP) { /* add a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* we have a constant property ID - generate a regular DELEGATE */ G_cg->write_op(OPC_DELEGATE); G_cs->write((char)argc); G_cs->write_prop_id(prop); /* this removes arguments and the object value */ G_cg->note_pop(argc + 1); } else { /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* a property pointer is on the stack - write a PTRDELEGATE */ G_cg->write_op(OPC_PTRDELEGATE); G_cs->write((int)argc); /* this removes arguments, the object, and the property pointer */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if the result is needed, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * "argcount" */ void CTPNArgc::gen_code(int discard, int) { /* generate the argument count, if we're not discarding */ if (!discard) { if (G_cg->is_eval_for_debug()) { /* generate a debug argument count evaluation */ G_cg->write_op(OPC_GETDBARGC); G_cs->write2(G_cg->get_debug_stack_level()); } else { /* generate the normal argument count evaluation */ G_cg->write_op(OPC_GETARGC); } /* we push one element */ G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * constant */ void CTPNConst::gen_code(int discard, int) { /* if we're discarding the value, do nothing */ if (discard) return; /* generate the appropriate type of push for the value */ switch(val_.get_type()) { case TC_CVT_NIL: G_cg->write_op(OPC_PUSHNIL); break; case TC_CVT_TRUE: G_cg->write_op(OPC_PUSHTRUE); break; case TC_CVT_INT: /* write the push-integer instruction */ s_gen_code_int(val_.get_val_int()); /* s_gen_code_int notes a push, which we'll do also, so cancel it */ G_cg->note_pop(); break; case TC_CVT_FLOAT: /* write the push-object instruction for the BigNumber object */ G_cg->write_op(OPC_PUSHOBJ); /* generate the BigNumber object and write its ID */ G_cs->write_obj_id(G_cg->gen_bignum_obj( val_.get_val_float(), val_.get_val_float_len(), val_.is_promoted())); break; case TC_CVT_RESTR: /* write the push-object instruction for the RexPattern object */ G_cg->write_op(OPC_PUSHOBJ); /* generate the RexPattern object and write its ID */ G_cs->write_obj_id(G_cg->gen_rexpat_obj( val_.get_val_str(), val_.get_val_str_len())); break; case TC_CVT_SSTR: /* generate the string */ s_gen_code_str(&val_); /* s_gen_code_int notes a push, which we'll do also, so cancel it */ G_cg->note_pop(); break; case TC_CVT_LIST: /* write the instruction */ G_cg->write_op(OPC_PUSHLST); /* * add the list to the constant pool, creating a fixup at the * current code stream location */ G_cg->add_const_list(val_.get_val_list(), G_cs, G_cs->get_ofs()); /* * write a placeholder address - this will be corrected by the * fixup that add_const_list() created for us */ G_cs->write4(0); break; case TC_CVT_OBJ: /* generate the object ID */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(val_.get_val_obj()); break; case TC_CVT_PROP: /* generate the property address */ G_cg->write_op(OPC_PUSHPROPID); G_cs->write_prop_id(val_.get_val_prop()); break; case TC_CVT_ENUM: /* generate the enum value */ G_cg->write_op(OPC_PUSHENUM); G_cs->write_enum_id(val_.get_val_enum()); break; case TC_CVT_FUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the function address */ val_.get_val_funcptr_sym()->add_abs_fixup(G_cs); /* write out a placeholder - arbitrarily use zero */ G_cs->write4(0); break; case TC_CVT_ANONFUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the code body address */ val_.get_val_anon_func_ptr()->add_abs_fixup(G_cs); /* write our a placeholder */ G_cs->write4(0); break; default: /* anything else is an internal error */ G_tok->throw_internal_error(TCERR_GEN_UNK_CONST_TYPE); } /* all of these push a value */ G_cg->note_push(); } int CTPNConst::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *, int ignore_errors, int, void **) { /* * If ignoring errors, just indicate that we can't do the assignment. * Also ignore errors on phase 2, since we'll already have generated an * error during phase 1. */ if (ignore_errors || phase > 1) return FALSE; /* * We can't assign to a constant. The parser will catch most errors of * this type before we ever get here, but for expressions that resolve * to global symbols (such as objects or functions), it can't know * until code generation time that the expression won't be assignable. */ G_tok->log_error(typ == TC_ASI_PREINC || typ == TC_ASI_PREDEC || typ == TC_ASI_POSTINC || typ == TC_ASI_POSTDEC ? TCERR_INVALID_UNARY_LVALUE : TCERR_INVALID_LVALUE, op); return FALSE; } /* * generate code to push an integer value */ void CTPNConst::s_gen_code_int(long intval) { /* push the smallest format that will fit the value */ if (intval == 0) { /* write the special PUSH_0 instruction */ G_cg->write_op(OPC_PUSH_0); } else if (intval == 1) { /* write the special PUSH_1 instruction */ G_cg->write_op(OPC_PUSH_1); } else if (intval < 127 && intval >= -128) { /* it fits in eight bits */ G_cg->write_op(OPC_PUSHINT8); G_cs->write((char)intval); } else { /* it doesn't fit in 8 bits - use a full 32 bits */ G_cg->write_op(OPC_PUSHINT); G_cs->write4(intval); } /* however we did it, we left one value on the stack */ G_cg->note_push(); } /* * Generate code to push a symbol's name as a string constant value */ void CTPNConst::s_gen_code_str(const CTcSymbol *sym) { /* generate the string for the symbol name */ s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } /* * Generate code to push a symbol's name as a string constant value, * according to the code generation mode. */ void CTPNConst::s_gen_code_str_by_mode(const CTcSymbol *sym) { /* check the mode */ if (G_cg->is_eval_for_dyn()) { /* dynamic evaluation - use in-line strings */ CTPNDebugConst::s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } else { /* static compilation - use the constant pool */ s_gen_code_str(sym->get_sym(), sym->get_sym_len()); } } /* * Generate code to push a string constant value */ void CTPNConst::s_gen_code_str(const CTcConstVal *val) { s_gen_code_str(val->get_val_str(), val->get_val_str_len()); } /* * Generate code to push a string constant value */ void CTPNConst::s_gen_code_str(const char *str, size_t len) { /* write the instruction to push a constant pool string */ G_cg->write_op(OPC_PUSHSTR); /* note the push */ G_cg->note_push(); /* * add the string to the constant pool, creating a fixup at the current * code stream location */ G_cg->add_const_str(str, len, G_cs, G_cs->get_ofs()); /* * write a placeholder address - this will be corrected by the fixup * that add_const_str() created for us */ G_cs->write4(0); } /* * Generate code to apply operator 'new' to the constant. We can apply * 'new' only to constant object values. */ void CTPNConst::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* check the type */ switch(val_.get_type()) { case TC_CVT_OBJ: /* * Treat this the same as any other 'new' call. An object symbol * folded into a constant is guaranteed to be of metaclass * TadsObject - that's the only kind of symbol we'll ever fold this * way. */ CTcSymObj::s_gen_code_new(discard, val_.get_val_obj(), val_.get_val_obj_meta(), argc, varargs, named_args, is_transient); break; default: /* can't apply 'new' to other constant values */ G_tok->log_error(TCERR_INVAL_NEW_EXPR); break; } } /* * Generate code to make a function call to this expression. If we're * calling a function, we can generate this directly. */ void CTPNConst::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* check our type */ switch(val_.get_type()) { case TC_CVT_FUNCPTR: /* generate a call to our function symbol */ val_.get_val_funcptr_sym()->gen_code_call( discard, argc, varargs, named_args); break; default: /* other types cannot be called */ G_tok->log_error(TCERR_CANNOT_CALL_CONST); break; } } /* * generate a property ID expression */ vm_prop_id_t CTPNConst::gen_code_propid(int check_only, int is_expr) { /* check the type */ switch(val_.get_type()) { case TC_CVT_PROP: /* return the constant property ID */ return (vm_prop_id_t)val_.get_val_prop(); default: /* other values cannot be used as properties */ if (!check_only) G_tok->log_error(TCERR_INVAL_PROP_EXPR); return VM_INVALID_PROP; } } /* * Generate code for a member evaluation */ void CTPNConst::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* check our constant type */ switch(val_.get_type()) { case TC_CVT_OBJ: /* call the object symbol code to do the work */ CTcSymObj::s_gen_code_member(discard, prop_expr, prop_is_expr, argc, val_.get_val_obj(), varargs, named_args); break; case TC_CVT_LIST: case TC_CVT_SSTR: case TC_CVT_RESTR: case TC_CVT_FLOAT: /* * list/string/BigNumber constant - generate our value as * normal, then use the standard member generation */ gen_code(FALSE, FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); break; default: G_tok->log_error(TCERR_INVAL_OBJ_EXPR); break; } } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNConst::gen_code_obj_predot(int *is_self) { /* we're certainly not "self" */ *is_self = FALSE; /* if I don't have an object value, this is illegal */ if (val_.get_type() != TC_CVT_OBJ) { G_tok->log_error(TCERR_INVAL_OBJ_EXPR); return VM_INVALID_OBJ; } /* report our constant object value */ return val_.get_val_obj(); } /* ------------------------------------------------------------------------ */ /* * debugger constant */ void CTPNDebugConst::gen_code(int discard, int for_condition) { /* if we're discarding the value, do nothing */ if (discard) return; /* generate the appropriate type of push for the value */ switch(val_.get_type()) { case TC_CVT_SSTR: if (val_.get_val_str() == 0) { /* validate the string address */ if (G_vmifc && !G_vmifc->validate_pool_str(val_.get_val_str_ofs())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* we have a pre-resolved pool offset */ G_cg->write_op(OPC_PUSHSTR); G_cs->write4(val_.get_val_str_ofs()); G_cg->note_push(); } else { /* write the in-line string instruction */ s_gen_code_str(val_.get_val_str(), val_.get_val_str_len()); } break; case TC_CVT_LIST: if (val_.get_val_list() == 0) { /* validate the list address */ if (G_vmifc && !G_vmifc->validate_pool_list(val_.get_val_list_ofs())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* we have a pre-resolved pool offset */ G_cg->write_op(OPC_PUSHLST); G_cs->write4(val_.get_val_list_ofs()); G_cg->note_push(); } else { /* we should never have a regular constant list when debugging */ assert(FALSE); } break; case TC_CVT_FUNCPTR: /* generate the function pointer instruction */ G_cg->write_op(OPC_PUSHFNPTR); /* * write the actual function address - no need for fixups in the * debugger, since everything's fully resolved */ G_cs->write4(val_.get_val_funcptr_sym()->get_code_pool_addr()); /* note the value push */ G_cg->note_push(); break; case TC_CVT_BIFPTR: /* validate the property ID if possible */ if (G_vmifc && !G_vmifc->validate_bif( val_.get_val_bifptr_sym()->get_func_set_id(), val_.get_val_bifptr_sym()->get_func_idx())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* generate the built-in function pointer instruction */ G_cg->write_op(OPC_PUSHBIFPTR); G_cs->write2(val_.get_val_bifptr_sym()->get_func_idx()); G_cs->write2(val_.get_val_bifptr_sym()->get_func_set_id()); /* note the value push */ G_cg->note_push(); break; case TC_CVT_ANONFUNCPTR: /* * we should never see an anonymous function pointer constant in * the debugger - these will always be represented as objects * intead */ assert(FALSE); break; case TC_CVT_FLOAT: { /* * find the 'BigNumber' metaclass - if it's not defined, we * can't create BigNumber values */ CTcSymMetaclass *sym = (CTcSymMetaclass *)G_prs->get_global_symtab()->find( "BigNumber", 9); if (sym == 0 || sym->get_type() != TC_SYM_METACLASS) err_throw(VMERR_INVAL_DBG_EXPR); /* push the floating value as an immediate string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(val_.get_val_str_len()); G_cs->write(val_.get_val_str(), val_.get_val_str_len()); /* create the new BigNumber object from the string */ G_cg->write_op(OPC_NEW2); G_cs->write2(1); G_cs->write2(sym->get_meta_idx()); /* retrieve the value */ G_cg->write_op(OPC_GETR0); /* * note the net push of one value (we pushed the argument, * popped the argument, and pushed the new object) */ G_cg->note_push(); } break; case TC_CVT_RESTR: { /* find the RexPattern metaclass */ CTcSymMetaclass *sym = (CTcSymMetaclass *)G_prs->get_global_symtab()->find( "RexPattern", 10); if (sym == 0 || sym->get_type() != TC_SYM_METACLASS) err_throw(VMERR_INVAL_DBG_EXPR); /* push the pattern source string value as an immediate string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(val_.get_val_str_len()); G_cs->write(val_.get_val_str(), val_.get_val_str_len()); /* create the new RexPattern object from the string */ G_cg->write_op(OPC_NEW2); G_cs->write2(1); G_cs->write2(sym->get_meta_idx()); /* retrieve the value */ G_cg->write_op(OPC_GETR0); /* * note the net push of one value (we pushed the argument, * popped the argument, and pushed the new object) */ G_cg->note_push(); } break; case TC_CVT_OBJ: /* validate the object ID if possible */ if (G_vmifc && !G_vmifc->validate_obj(val_.get_val_obj())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* use the default handling */ goto use_default; case TC_CVT_PROP: /* validate the property ID if possible */ if (G_vmifc && !G_vmifc->validate_prop((tctarg_prop_id_t)val_.get_val_prop())) { /* treat it as nil */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); break; } /* use the default handling */ goto use_default; default: use_default: /* handle normally for anything else */ CTPNConst::gen_code(discard, for_condition); break; } } /* * Generate a string value in a debug/dynamic code context. This generates * an in-line string rather than a constant pool reference. */ void CTPNDebugConst::s_gen_code_str(const char *str, size_t len) { /* write the PUSHSTRI */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(len); G_cs->write(str, len); /* note the value push */ G_cg->note_push(); } /* ------------------------------------------------------------------------ */ /* * Generic Unary Operator */ /* * Generate a unary-operator opcode. We assume that the opcode has no * side effects other than to compute the result, so we do not generate * the opcode at all if 'discard' is true; we do, however, always * generate code for the subexpression to ensure that its side effects * are performed. * * In most cases, the caller simply should pass through its 'discard' * status, since the result of the subexpression is generally needed * only when the result of the enclosing expression is needed. * * In most cases, the caller should pass FALSE for 'for_condition', * because applying an operator to the result generally requires that * the result be properly converted for use as a temporary value. * However, when the caller knows that its own opcode will perform the * same conversions that a conditional opcode would, 'for_condition' * should be TRUE. In most cases, the caller's own 'for_condition' * status is not relevant and should thus not be passed through. */ void CTPNUnary::gen_unary(uchar opc, int discard, int for_condition) { /* * Generate the operand. Pass through the 'discard' status to the * operand - if the result of the parent operator is being * discarded, then so is the result of this subexpression. In * addition, pass through the caller's 'for_condition' disposition. */ sub_->gen_code(discard, for_condition); /* apply the operator if we're not discarding the result */ if (!discard) G_cg->write_op(opc); } /* ------------------------------------------------------------------------ */ /* * Generic Binary Operator */ /* * Generate a binary-operator opcode. * * In most cases, the caller's 'discard' status should be passed * through, since the results of the operands are usually needed if and * only if the results of the enclosing expression are needed. * * In most cases, the caller should pass FALSE for 'for_condition'. * Only when the caller knows that the opcode will perform the same * conversions as a BOOLIZE instruction should it pass TRUE for * 'for_condition'. */ void CTPNBin::gen_binary(uchar opc, int discard, int for_condition) { /* * generate the operands, passing through the discard and * conditional status */ left_->gen_code(discard, for_condition); right_->gen_code(discard, for_condition); /* generate our operand if we're not discarding the result */ if (!discard) { /* apply the operator */ G_cg->write_op(opc); /* * binary operators all remove two values and push one, so there's * a net pop */ G_cg->note_pop(); } } /* * Generate a binary-operator opcode for a compound assignment. This omits * generating the left operand, because the compound assignment operator * will already have done that. */ void CTPNBin::gen_binary_ca(uchar opc) { /* generate the right operand (the left is already on the stack) */ right_->gen_code(FALSE, FALSE); /* generate the operator */ G_cg->write_op(opc); /* a binary op removes two values and pushes one */ G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * logical NOT */ void CTPNNot::gen_code(int discard, int) { /* * Generate the subexpression and apply the NOT opcode. Note that * we can compute the subexpression as though we were applying a * condition, because the NOT opcode takes exactly the same kind of * input as any condition opcode; we can thus avoid an extra * conversion in some cases. */ gen_unary(OPC_NOT, discard, TRUE); } /* ------------------------------------------------------------------------ */ /* * Boolean-ize operator */ void CTPNBoolize::gen_code(int discard, int for_condition) { /* * If the result will be used for a conditional, there's no need to * generate an instruction to convert the value to boolean. The opcode * that will be used for the condition will perform exactly the same * conversions that this opcode would apply; avoid the redundant work * in this case, and simply generate the underlying expression * directly. */ if (for_condition) { /* generate the underlying expression without modification */ sub_->gen_code(discard, for_condition); /* done */ return; } /* * if the sub-expression is already guaranteed to be a boolean value, * there's nothing we need to do */ if (sub_->is_bool()) return; /* * Generate the subexpression and apply the BOOLIZE operator. Since * we're explicitly boolean-izing the value, there's no need for the * subexpression to do the same thing, so the subexpression can * pretend it's generating for a conditional. */ gen_unary(OPC_BOOLIZE, discard, TRUE); } /* ------------------------------------------------------------------------ */ /* * bitwise NOT */ void CTPNBNot::gen_code(int discard, int) { gen_unary(OPC_BNOT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * arithmetic positive */ void CTPNPos::gen_code(int discard, int) { /* * simply generate our operand, since the operator itself has no * effect */ sub_->gen_code(discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * unary arithmetic negative */ void CTPNNeg::gen_code(int discard, int) { gen_unary(OPC_NEG, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * pre-increment */ void CTPNPreInc::gen_code(int discard, int) { /* use the static handler */ CTPNPreInc_gen_code(sub_, discard); } /* static pre-inc code generator, for sharing with other nodes */ void CTPNPreInc_gen_code(CTcPrsNode *sub, int discard) { /* ask the subnode to generate it */ void *ctx; if (!sub->gen_code_asi(discard, 1, TC_ASI_PREINC, "++", 0, FALSE, TRUE, &ctx)) { /* increment the value at top of stack */ G_cg->write_op(OPC_INC); /* * generate a simple assignment back to the subexpression; if * we're using the value, let the simple assignment leave its * value on the stack, since the result is the value *after* the * increment */ sub->gen_code_asi(discard, 2, TC_ASI_PREINC, "++", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * pre-decrement */ void CTPNPreDec::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_PREDEC, "--", 0, FALSE, TRUE, &ctx)) { /* decrement the value at top of stack */ G_cg->write_op(OPC_DEC); /* * generate a simple assignment back to the subexpression; if * we're using the value, let the simple assignment leave its * value on the stack, since the result is the value *after* the * decrement */ sub_->gen_code_asi(discard, 2, TC_ASI_PREDEC, "--", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * post-increment */ void CTPNPostInc::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_POSTINC, "++", 0, FALSE, TRUE, &ctx)) { /* * if we're keeping the result, duplicate the value at top of * stack prior to the increment - since this is a * post-increment, the result is the value *before* the * increment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* increment the value at top of stack */ G_cg->write_op(OPC_INC); /* * Generate a simple assignment back to the subexpression. * Discard the result of this assignment, regardless of whether * the caller wants the result of the overall expression, * because we've already pushed the actual result, which is the * original value before the increment operation. */ sub_->gen_code_asi(TRUE, 2, TC_ASI_POSTINC, "++", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * post-decrement */ void CTPNPostDec::gen_code(int discard, int) { /* ask the subnode to generate it */ void *ctx; if (!sub_->gen_code_asi(discard, 1, TC_ASI_POSTDEC, "--", 0, FALSE, TRUE, &ctx)) { /* * if we're keeping the result, duplicate the value at top of * stack prior to the decrement - since this is a * post-decrement, the result is the value *before* the * decrement */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* decrement the value at top of stack */ G_cg->write_op(OPC_DEC); /* * Generate a simple assignment back to the subexpression. * Discard the result of this assignment, regardless of whether * the caller wants the result of the overall expression, * because we've already pushed the actual result, which is the * original value before the decrement operation. */ sub_->gen_code_asi(TRUE, 2, TC_ASI_POSTDEC, "--", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * operator 'new' */ void CTPNNew::gen_code(int discard, int /*for condition*/) { /* * ask my subexpression to generate the code - at this point we * don't know the number of arguments, so pass in zero for now */ sub_->gen_code_new(discard, 0, FALSE, 0, FALSE, transient_); } /* ------------------------------------------------------------------------ */ /* * operator 'delete' */ void CTPNDelete::gen_code(int, int) { /* 'delete' generates no code for T3 VM */ } /* ------------------------------------------------------------------------ */ /* * comma operator */ void CTPNComma::gen_code(int discard, int for_condition) { /* * Generate each side's code. Note that the left side is *always* * discarded, regardless of whether the result of the comma operator * will be discarded. After we generate our subexpressions, there's * nothing left to do, since the comma operator itself doesn't * change anything - we simply use the right operand result as our * result. * * Pass through the 'for_condition' status to the right operand, * since we pass through its result to the caller. For the left * operand, treat it as a condition - we don't care about the result * value, so don't bother performing any extra conversions on it. */ left_->gen_code(TRUE, TRUE); right_->gen_code(discard, for_condition); } /* ------------------------------------------------------------------------ */ /* * logical OR (short-circuit logic) */ void CTPNOr::gen_code(int discard, int for_condition) { CTcCodeLabel *lbl; /* * First, evaluate the left-hand side; we need the result even if * we're discarding the overall expression, since we will check the * result to see if we should even evaluate the right-hand side. * We're using the value for a condition, so don't bother * boolean-izing it. */ left_->gen_code(FALSE, TRUE); /* * If the left-hand side is true, there's no need to evaluate the * right-hand side (and, in fact, we're not even allowed to evaluate * the right-hand side because of the short-circuit logic rule). * So, if the lhs is true, we want to jump around the code to * evaluate the rhs, saving the 'true' result if we're not * discarding the overall result. */ lbl = gen_jump_ahead(discard ? OPC_JT : OPC_JST); /* * Evaluate the right-hand side. We don't need to save the result * unless we need the result of the overall expression. Generate * the value as though we were going to booleanize it ourselves, * since we'll do just that (hence pass for_condition = TRUE). */ right_->gen_code(discard, TRUE); /* * If we discarded the result, we generated a JT which explicitly * popped a value. If we didn't discard the result, we generated a * JST; this may or may not pop the value. However, if it doesn't * pop the value (save on true), it will bypass the right side * evaluation, and will thus "pop" that value in the sense that it * will never be pushed. So, note a pop either way. */ G_cg->note_pop(); /* define the label for the jump over the rhs */ def_label_pos(lbl); /* * If the result is not going to be used directly for a condition, we * must boolean-ize the value. This isn't necessary if both of the * operands are already guaranteed to be boolean values. */ if (!for_condition && !(left_->is_bool() && right_->is_bool())) G_cg->write_op(OPC_BOOLIZE); } /* * Generate code for the short-circuit OR when used in a condition. We can * use the fact that we're being used conditionally to avoid actually * pushing the result value onto the stack, instead simply branching to the * appropriate point in the enclosing control structure instead. */ void CTPNOr::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { CTcCodeLabel *internal_then; /* * First, generate the conditional code for our left operand. If the * condition is true, we can short-circuit the rest of the expression * by jumping directly to the 'then' label. If the caller provided a * 'then' label, we can jump directly to the caller's 'then' label; * otherwise, we must synthesize our own internal label, which we'll * define at the end of our generated code so that we'll fall through * on true to the enclosing code. In any case, we want to fall through * if the condition is false, so that control will flow to the code for * our right operand if the left operand is false. */ internal_then = (then_label == 0 ? G_cs->new_label_fwd() : then_label); left_->gen_code_cond(internal_then, 0); /* * Now, generate code for our right operand. We can generate this code * using the caller's destination labels directly: if we reach this * code at all, it's because the left operand was false, in which case * the result is simply the value of the right operand. */ right_->gen_code_cond(then_label, else_label); /* * If we created an internal 'then' label, it goes at the end of our * generated code: this ensures that we fall off the end of our code * if the left subexpression is true, which is what the caller told us * they wanted when they gave us a null 'then' label. If the caller * gave us an explicit 'then' label, we'll have jumped there directly * if the first subexpression was true. */ if (then_label == 0) def_label_pos(internal_then); } /* ------------------------------------------------------------------------ */ /* * logical AND (short-circuit logic) */ void CTPNAnd::gen_code(int discard, int for_condition) { /* * first, evaluate the left-hand side; we need the result even if * we're discarding the overall expression, since we will check the * result to see if we should even evaluate the right-hand side */ left_->gen_code(FALSE, TRUE); /* * If the left-hand side is false, there's no need to evaluate the * right-hand side (and, in fact, we're not even allowed to evaluate * the right-hand side because of the short-circuit logic rule). * So, if the lhs is false, we want to jump around the code to * evaluate the rhs, saving the false result if we're not discarding * the overall result. */ CTcCodeLabel *lbl = gen_jump_ahead(discard ? OPC_JF : OPC_JSF); /* * Evaluate the right-hand side. We don't need to save the result * unless we need the result of the overall expression. */ right_->gen_code(discard, TRUE); /* define the label for the jump over the rhs */ def_label_pos(lbl); /* * If we discarded the result, we generated a JF which explicitly * popped a value. If we didn't discard the result, we generated a * JSF; this may or may not pop the value. However, if it doesn't * pop the value (save on false), it will bypass the right side * evaluation, and will thus "pop" that value in the sense that it * will never be pushed. So, note a pop either way. */ G_cg->note_pop(); /* * If the result is not going to be used directly for a condition, we * must boolean-ize the value. This isn't necessary if both of the * sub-expressions are already guaranteed to be boolean values. */ if (!for_condition && !(left_->is_bool() && right_->is_bool())) G_cg->write_op(OPC_BOOLIZE); } /* * Generate code for the short-circuit AND when used in a condition. We * can use the fact that we're being used conditionally to avoid actually * pushing the result value onto the stack, instead simply branching to the * appropriate point in the enclosing control structure instead. */ void CTPNAnd::gen_code_cond(CTcCodeLabel *then_label, CTcCodeLabel *else_label) { CTcCodeLabel *internal_else; /* * First, generate the conditional code for our left operand. If the * condition is false, we can short-circuit the rest of the expression * by jumping directly to the 'else' label. If the caller provided an * 'else' label, we can jump directly to the caller's 'else' label; * otherwise, we must synthesize our own internal label, which we'll * define at the end of our generated code so that we'll fall through * on false to the enclosing code. In any case, we want to fall * through if the condition is true, so that control will flow to the * code for our right operand if the left operand is true. */ internal_else = (else_label == 0 ? G_cs->new_label_fwd() : else_label); left_->gen_code_cond(0, internal_else); /* * Now, generate code for our right operand. We can generate this code * using the caller's destination labels directly: if we reach this * code at all, it's because the left operand was true, in which case * the result is simply the value of the right operand. */ right_->gen_code_cond(then_label, else_label); /* * If we created an internal 'else' label, it goes at the end of our * generated code: this ensures that we fall off the end of our code * if the left subexpression is false, which is what the caller told * us they wanted when they gave us a null 'else' label. If the * caller gave us an explicit 'else' label, we'll have jumped there * directly if the first subexpression was false. */ if (else_label == 0) def_label_pos(internal_else); } /* ------------------------------------------------------------------------ */ /* * bitwise OR */ void CTPNBOr::gen_code(int discard, int) { gen_binary(OPC_BOR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * bitwise AND */ void CTPNBAnd::gen_code(int discard, int) { gen_binary(OPC_BAND, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * bitwise XOR */ void CTPNBXor::gen_code(int discard, int) { gen_binary(OPC_XOR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * greater-than */ void CTPNGt::gen_code(int discard, int) { gen_binary(OPC_GT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * greater-or-equal */ void CTPNGe::gen_code(int discard, int) { gen_binary(OPC_GE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * less-than */ void CTPNLt::gen_code(int discard, int) { gen_binary(OPC_LT, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * less-or-equal */ void CTPNLe::gen_code(int discard, int) { gen_binary(OPC_LE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * compare for equality */ void CTPNEq::gen_code(int discard, int) { gen_binary(OPC_EQ, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * compare for inequality */ void CTPNNe::gen_code(int discard, int) { gen_binary(OPC_NE, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * 'is in' */ void CTPNIsIn::gen_code(int discard, int) { CTPNArglist *lst; CTPNArg *arg; CTcCodeLabel *lbl_found; CTcCodeLabel *lbl_done; /* allocate our 'found' label */ lbl_found = G_cs->new_label_fwd(); /* * allocate our 'done' label - we only need to do this if we don't * have a constant true value and we're not discarding the result */ if (!const_true_ && !discard) lbl_done = G_cs->new_label_fwd(); /* generate my left-side expression */ left_->gen_code(FALSE, FALSE); /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* compare to each element in the list on the right */ for (arg = lst->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* * duplicate the left-side value, so we don't have to generate * it again for this comparison */ G_cg->write_op(OPC_DUP); /* generate this list element */ arg->gen_code(FALSE, FALSE); /* if they're equal, jump to the 'found' label */ G_cg->write_op(OPC_JE); G_cs->write_ofs2(lbl_found, 0); /* we pushed one more (DUP) and popped two (JE) */ G_cg->note_push(1); G_cg->note_pop(2); } /* * Generate the code that comes at the end of all of tests when we * fail to find any matches - we simply discard the left-side value * from the stack, push our 'nil' value, and jump to the end label. * * If we have a constant 'true' value, there's no need to do any of * this, because we know that, even after testing all of our * non-constant values, there's a constant value that makes the * entire expression true, and we can thus just fall through to the * 'found' code. * * If we're discarding the result, there's no need to push a * separate value for the result, so we can just fall through to the * common ending code in this case. */ if (!const_true_ && !discard) { G_cg->write_op(OPC_DISC); G_cg->write_op(OPC_PUSHNIL); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(lbl_done, 0); } /* * Generate the 'found' code - this discards the left-side value and * pushes our 'true' result. Note that there's no reason to push * our result if we're discarding it. */ def_label_pos(lbl_found); G_cg->write_op(OPC_DISC); /* * if we're discarding the result, just note the pop of the left * value; otherwise, push our result */ if (discard) G_cg->note_pop(); else G_cg->write_op(OPC_PUSHTRUE); /* our 'done' label is here, if we needed one */ if (!const_true_ && !discard) def_label_pos(lbl_done); } /* ------------------------------------------------------------------------ */ /* * 'not in' */ void CTPNNotIn::gen_code(int discard, int) { CTPNArglist *lst; CTPNArg *arg; CTcCodeLabel *lbl_found; CTcCodeLabel *lbl_done; /* allocate our 'found' label */ lbl_found = G_cs->new_label_fwd(); /* * allocate our 'done' label - we only need to do this if we don't * have a constant false value */ if (!const_false_ && !discard) lbl_done = G_cs->new_label_fwd(); /* generate my left-side expression */ left_->gen_code(FALSE, FALSE); /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* compare to each element in the list on the right */ for (arg = lst->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* * duplicate the left-side value, so we don't have to generate * it again for this comparison */ G_cg->write_op(OPC_DUP); /* generate this list element */ arg->gen_code(FALSE, FALSE); /* if they're equal, jump to the 'found' label */ G_cg->write_op(OPC_JE); G_cs->write_ofs2(lbl_found, 0); /* we pushed one more (DUP) and popped two (JE) */ G_cg->note_push(1); G_cg->note_pop(2); } /* * Generate the code that comes at the end of all of tests when we * fail to find any matches - we simply discard the left-side value * from the stack, push our 'true' value, and jump to the end label. * * If we have a constant 'nil' value, however, there's no need to do * any of this, because we know that, even after testing all of our * non-constant values, there's a matching constant value that makes * the entire expression false (because 'not in' is false if we find * a match), and we can thus just fall through to the 'found' code. */ if (!const_false_ && !discard) { G_cg->write_op(OPC_DISC); G_cg->write_op(OPC_PUSHTRUE); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(lbl_done, 0); } /* * generate the 'found' code - this discards the left-side value and * pushes our 'nil' result (because the result of 'not in' is false * if we found the value) */ def_label_pos(lbl_found); G_cg->write_op(OPC_DISC); /* push the result, or note the pop if we're just discarding it */ if (discard) G_cg->note_pop(); else G_cg->write_op(OPC_PUSHNIL); /* our 'done' label is here, if we needed one */ if (!const_false_ && !discard) def_label_pos(lbl_done); } /* ------------------------------------------------------------------------ */ /* * bit-shift left */ void CTPNShl::gen_code(int discard, int) { gen_binary(OPC_SHL, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right '>>' */ void CTPNAShr::gen_code(int discard, int) { gen_binary(OPC_ASHR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * logical shift right '>>>' */ void CTPNLShr::gen_code(int discard, int) { gen_binary(OPC_LSHR, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * multiply */ void CTPNMul::gen_code(int discard, int) { /* if either side is zero or one, we can apply special handling */ if (left_->is_const_int(0)) { /* evaluate the right for side effects and discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); /* done */ return; } else if (right_->is_const_int(0)) { /* evaluate the left for side effects and discard the result */ left_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); /* done */ return; } else if (left_->is_const_int(1)) { /* * evaluate the right side - it's the result; note that, because * of the explicit multiplication, we must compute logical * results using assignment (not 'for condition') rules */ right_->gen_code(discard, FALSE); /* done */ return; } else if (right_->is_const_int(1)) { /* evaluate the right side - it's the result */ left_->gen_code(discard, FALSE); /* done */ return; } /* apply generic handling */ gen_binary(OPC_MUL, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * divide */ void CTPNDiv::gen_code(int discard, int for_cond) { /* if dividing by 1, we can skip the whole thing (except side effects) */ if (right_->is_const_int(1)) { /* * simply generate the left side for side effects; actually * doing the arithmetic has no effect */ left_->gen_code(discard, for_cond); return; } /* if the left side is zero, the result is always zero */ if (left_->is_const_int(0)) { /* evaluate the right for side effects, but discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* use generic code generation */ gen_binary(OPC_DIV, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * modulo */ void CTPNMod::gen_code(int discard, int for_condition) { /* if dividing by 1, we can skip the whole thing (except side effects) */ if (right_->is_const_int(1)) { /* * simply generate the left side for side effects; actually * doing the arithmetic has no effect */ left_->gen_code(discard, for_condition); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* if the left side is zero, the result is always zero */ if (left_->is_const_int(0)) { /* evaluate the right for side effects, but discard the result */ right_->gen_code(TRUE, TRUE); /* the result is zero */ G_cg->write_op(OPC_PUSH_0); G_cg->note_push(); return; } /* use generic processing */ gen_binary(OPC_MOD, discard, FALSE); } /* ------------------------------------------------------------------------ */ /* * subtract */ void CTPNSub::gen_code(int discard, int for_cond) { /* check for subtracting 1, which we can accomplish more efficiently */ if (right_->is_const_int(1)) { /* * We're subtracting one - use decrement. The decrement * operator itself has no side effects, so we can pass through * the 'discard' status to the subnode. */ left_->gen_code(discard, FALSE); /* apply decrement if we're not discarding the result */ if (!discard) G_cg->write_op(OPC_DEC); } else { /* we can't do anything special - use the general-purpose code */ gen_binary(OPC_SUB, discard, FALSE); } } /* ------------------------------------------------------------------------ */ /* * add */ void CTPNAdd::gen_code(int discard, int) { /* check for adding 1, which we can accomplish more efficiently */ if (right_->is_const_int(1)) { /* * We're adding one - use increment. The increment operator * itself has no side effects, so we can pass through the * 'discard' status to the subnode. */ left_->gen_code(discard, FALSE); /* apply increment if we're not discarding the result */ if (!discard) G_cg->write_op(OPC_INC); } else { /* we can't do anything special - use the general-purpose code */ gen_binary(OPC_ADD, discard, FALSE); } } /* ------------------------------------------------------------------------ */ /* * simple assignment */ void CTPNAsi::gen_code(int discard, int) { /* * Ask the left subnode to generate a simple assignment to the value * on the right. Simple assignments cannot be refused, so we don't * need to try to do any assignment work ourselves. */ left_->gen_code_asi(discard, 1, TC_ASI_SIMPLE, "=", right_, FALSE, TRUE, 0); } /* ------------------------------------------------------------------------ */ /* * add and assign */ void CTPNAddAsi::gen_code(int discard, int) { /* use the static handler */ CTPNAddAsi_gen_code(left_, right_, discard); } /* static handler, for sharing with other node types */ void CTPNAddAsi_gen_code(CTcPrsNode *left, CTcPrsNode *right, int discard) { /* * ask the left subnode to generate an add-and-assign; if it can't, * handle it generically */ void *ctx; if (!left->gen_code_asi(discard, 1, TC_ASI_ADD, "+=", right, FALSE, TRUE, &ctx)) { /* * There's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused. We've already generated * the left side above, so generate the right side. */ right->gen_code(FALSE, FALSE); /* generate the add (which pops two and adds one -> net one pop) */ G_cg->write_op(OPC_ADD); G_cg->note_pop(); /* generate the simple assignment to the lhs */ left->gen_code_asi(discard, 2, TC_ASI_ADD, "+=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * subtract and assign */ void CTPNSubAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a subtract-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_SUB, "-=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_SUB); left_->gen_code_asi(discard, 2, TC_ASI_SUB, "-=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * multiply and assign */ void CTPNMulAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a multiply-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_MUL, "*=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_MUL); left_->gen_code_asi(discard, 2, TC_ASI_MUL, "*=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * divide and assign */ void CTPNDivAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a divide-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_DIV, "/=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_DIV); left_->gen_code_asi(discard, 2, TC_ASI_DIV, "/=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * modulo and assign */ void CTPNModAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a mod-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_MOD, "%=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_MOD); left_->gen_code_asi(discard, 2, TC_ASI_MOD, "%=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-AND and assign */ void CTPNBAndAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an AND-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BAND, "&=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_BAND); left_->gen_code_asi(discard, 2, TC_ASI_BAND, "&=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-OR and assign */ void CTPNBOrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an OR-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BOR, "|=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_BOR); left_->gen_code_asi(discard, 2, TC_ASI_BOR, "|=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bitwise-XOR and assign */ void CTPNBXorAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an XOR-and-assign; if it can't, * handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_BXOR, "^=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_XOR); left_->gen_code_asi(discard, 2, TC_ASI_BXOR, "^=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * bit-shift left and assign */ void CTPNShlAsi::gen_code(int discard, int) { /* * ask the left subnode to generate an shift-left-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_SHL, "<<=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_SHL); left_->gen_code_asi(discard, 2, TC_ASI_SHL, "<<=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right and assign '>>=' */ void CTPNAShrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a shift-right-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_ASHR, ">>=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_ASHR); left_->gen_code_asi(discard, 2, TC_ASI_ASHR, ">>=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * logical shift right and assign '>>>=' */ void CTPNLShrAsi::gen_code(int discard, int) { /* * ask the left subnode to generate a shift-right-and-assign; if it * can't, handle it generically */ void *ctx; if (!left_->gen_code_asi(discard, 1, TC_ASI_LSHR, ">>>=", right_, FALSE, TRUE, &ctx)) { /* * there's no special coding for this assignment type -- compute * the result generically, then assign the result as a simple * assignment, which cannot be refused */ gen_binary_ca(OPC_LSHR); left_->gen_code_asi(discard, 2, TC_ASI_LSHR, ">>>=", 0, FALSE, TRUE, &ctx); } } /* ------------------------------------------------------------------------ */ /* * subscript a list/array value */ void CTPNSubscript::gen_code(int discard, int) { gen_binary(OPC_INDEX, discard, FALSE); } /* * assign to a subscripted value */ int CTPNSubscript::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int, int xplicit, void **ctx) { void *sctx; /* * An assignment to a subscript expression is not what it appears. * It's actually a compound assignment that evaluates the subscript * expression, assigns the RHS, and then assigns the *result* of that * first assignment to the left operand of the index operator. In * other words, a[b]=c is effectively rewritten as this: * *. a = a.operator[]=(b, c) * * which, by analogy with the other compound assignment operators, we * can rewrite as * *. a []== (b, c) */ if (typ == TC_ASI_SIMPLE) { int rhs_depth; int extra_disc = 0; /* * Simple assignment. Generate the value to assign to the element * - that's the right side of the assignment operator. If rhs is * null, it means the caller has already done this. */ if (rhs != 0) { /* generate code for the RHS */ rhs->gen_code(FALSE, FALSE); } /* * if we're not discarding the result, duplicate the value to be * assigned, so that we leave a copy on the stack after we're * finished. This is necessary because the SETIND instruction will * consume one copy of the value. */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* note the stack depth where the RHS value resides */ rhs_depth = G_cg->get_sp_depth(); /* * Generate the value to be subscripted - that's my left-hand side. * Generate this as phase 1 of a two-phase assignment, with the * special pseudo-operator '[]='. Ignore the return value; no one * can fully handle this type of assignment in phase 1. Also, note * that we don't yet have the right-hand value to assign - it's the * result of the SETIND yet to come. */ left_->gen_code_asi(FALSE, 1, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx); /* * The phase 1 []= code could have left context information on the * stack that it will need in phase 2, in addition to the LHS value * that we need for the SETIND we're about to generate. For the * SETIND, we need the RHS value at index 1, just above the LHS * value. So if the new depth is more than 1 greater than the RHS * depth, we need to do some stack rearrangement. * * If phase 1 left *nothing* on the stack, it means that the value * being indexed can't handle assignments at all, as in * self[i]=val. This is acceptable: we can still perform the []= * portion to assign to the indexed element, but we won't be able * to do the second part, which is assigning the result of the []= * operator to the lhs of the [] operator. We'll simply ignore * that second part in this case. */ if (G_cg->get_sp_depth() == rhs_depth) { /* * the lhs of [] is not assignable; simply generate the lhs so * that we can index it */ left_->gen_code(FALSE, FALSE); } else if (G_cg->get_sp_depth() > rhs_depth + 1) { /* figure the distance to the RHS */ int dist = G_cg->get_sp_depth() - rhs_depth; /* the instruction can only accommodate a distance up to 255 */ if (dist > 255) G_tok->log_error(TCERR_EXPR_TOO_COMPLEX); /* re-push the RHS */ G_cg->write_op(OPC_GETSPN); G_cs->write((uchar)dist); G_cg->note_push(); /* * swap it with the left operand of the [] to get everything in * the proper order */ G_cg->write_op(OPC_SWAP); /* * the original RHS we pushed is now superfluous, so we'll need * to explicitly pop it when we're done */ extra_disc += 1; } /* generate the index value - that's my right-hand side */ right_->gen_code(FALSE, FALSE); /* generate the assign-to-indexed-value opcode */ G_cg->write_op(OPC_SETIND); /* setind pops three and pushes one - net of pop 2 */ G_cg->note_pop(2); /* * The top value now on the stack is the new container value. The * new container will be different from the old container in some * cases (with lists, for example, because we must create a new * list object to contain the modified list value). Therefore, if * my left-hand side is an lvalue, we must assign the new container * to the left-hand side - this makes something like "x[1] = 5" * actually change the value in "x" if "x" is a local variable. If * my left-hand side isn't an lvalue, don't bother with this step, * and simply discard the new container value. * * Handle the assignment to the new container as phase 2 of the * '[]=' pseudo-operator that we set up above. This ensures that * the left operand had a chance to save any sub-expression values, * which it can now reuse. * * Regardless of whether we're keeping the result of the overall * expression, we're definitely not keeping the result of assigning * the new container - the result of the assignment is the value * assigned, not the container. Thus, discard = true in this call. * * * There's a special case that's handled through the peep-hole * optimizer: if we are assigning to a local variable and indexing * with a constant integer value, we will have converted the whole * operation to a SETINDLCL1I8. That instruction takes care of * assigning the value back to the rvalue, so we don't need to * generate a separate rvalue assignment. */ if (G_cg->get_last_op() == OPC_SETINDLCL1I8) { /* * no assignment is necessary - we just need to account for the * difference in the stack arrangement with this form of the * assignment, which is that we don't leave the container value * on the stack */ G_cg->note_pop(); } else if (!left_->gen_code_asi(TRUE, 2, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx)) { /* no assignment is possible; discard the new container value */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* discard any extra stack elements */ while (extra_disc-- != 0) { G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* handled */ return TRUE; } else if (phase == 1) { /* * Compound assignment, phase 1. Generate the lvalue's value, and * also leave the indexee and index values on the stack separately * for use later in doing the assignment to indexee[index]. */ /* * Generate the value to be subscripted - that's my left-hand side. * Generate it as phase 1 of assignment through my pseudo-operator * []=. */ left_->gen_code_asi(FALSE, 1, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx); /* * save the context in the caller's context holder, so that it'll * get passed back to us on the second pass */ *ctx = sctx; /* generate the index value - that's my right-hand side */ right_->gen_code(FALSE, FALSE); /* duplicate them both so we have them later, for the assignment */ G_cg->write_op(OPC_DUP2); G_cg->note_push(2); /* generate the INDEX operator to compute indexee[index] */ G_cg->write_op(OPC_INDEX); G_cg->note_pop(); /* * If the caller pushed a RHS value for its own use in assigning, */ /* tell the caller to proceed (i.e., we didn't finish the job) */ return FALSE; } else if (phase == 2) { /* * Compound assignment, phase 2. We now have the combined value to * be assigned on the stack, followed by the indexee and index * values on the stack. */ /* restore the left-side phase 1 context from the caller */ sctx = *ctx; /* if the assigned value will be further assigned, save a copy */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * The stack is now (bottom to top) INDEXEE INDEX RHS RHS (if * !discard) or INDEXEE INDEX RHS (if discard). SETIND needs it to * be (RHS) RHS INDEXEE INDEX. Swap the top one or two elements * with the next two to rearrange into SETIDX order. */ if (discard) { /* INDEXEE INDEX RHS -> RHS INDEX INDEXEE */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); /* -> RHS INDEXEE INDEX */ G_cg->write_op(OPC_SWAP); } else { /* swap INDEXEE INDEX with RHS RHS */ G_cg->write_op(OPC_SWAP2); } /* assign the rhs to the indexed value */ G_cg->write_op(OPC_SETIND); G_cg->note_pop(2); /* * The new container value is now on the stack; assign it to our * left-hand side. Note that the SETINDLCL1I8 optimization (see * the simple assignment case above) is impossible here, because we * had to generate the code to carry out the combination operator * (the '+' in '+=', for example) before the SETIND. There's thus * nothing for the SETIND to combine with. * * Generate as phase 2 of the []= assignment we started in phase 1. */ if (!left_->gen_code_asi(TRUE, 2, TC_ASI_IDX, op, 0, TRUE, xplicit, &sctx)) { /* no assignment was necessary; discard the container value */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* handled */ return TRUE; } else { /* ignore any other phases */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * If-nil operator */ void CTPNIfnil::gen_code(int discard, int for_condition) { /* generate the first operand */ first_->gen_code(FALSE, FALSE); /* check to see if we're keeping the result or merely testing it */ if (!discard) { /* we're keep the result - make a copy for the JNOTNIL */ G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* if it's not nil, we're done - jump to the end of the operator */ CTcCodeLabel *lbl_end = gen_jump_ahead(OPC_JNOTNIL); G_cg->note_pop(); /* * We're on the 'nil' branch now, meaning we're not going to yield the * first value after all. If we're yielding anything, discard the * extra yielding copy we made of the first value. */ if (!discard) { G_cg->write_op(OPC_DISC); G_cg->note_pop(); } /* generate the 'nil' branch, yielding the second operand */ second_->gen_code(discard, for_condition); /* this is the end of the test - generate the end label */ def_label_pos(lbl_end); } /* ------------------------------------------------------------------------ */ /* * conditional operator */ void CTPNIf::gen_code(int discard, int for_condition) { /* * Generate the condition value - we need the value regardless of * whether the overall result is going to be used, because we need * it to determine which branch to take. Generate the subexpression * for a condition, so that we don't perform any extra unnecessary * conversions on it. */ first_->gen_code(FALSE, TRUE); /* if the condition is false, jump to the 'else' expression part */ CTcCodeLabel *lbl_else = gen_jump_ahead(OPC_JF); /* JF pops a value */ G_cg->note_pop(); /* * Generate the 'then' expression part. Only request a return value if * it has one AND we're not discarding it. If it doesn't return a * value, and we actually need one, we'll supply a default 'nil' value * next. This value will be our yielded value (in this branch, * anyway), so pass through the for-condition flag. */ second_->gen_code(discard || !second_->has_return_value(), for_condition); /* * If this expression has no return value, and we need the return * value, supply nil as the result. */ if (!discard && !second_->has_return_value()) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* unconditionally jump over the 'else' part */ CTcCodeLabel *lbl_end = gen_jump_ahead(OPC_JMP); /* set the label for the 'else' part */ def_label_pos(lbl_else); /* * Generate the 'else' part. Only request a return value if it has one * AND we're not discarding it. Pass through 'discard' and * 'for_condition', since this result is our result. */ third_->gen_code(discard || !third_->has_return_value(), for_condition); /* * If this expression has no return value, and we need the return * value, supply nil as the result. */ if (!discard && !third_->has_return_value()) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* * Because of the jump, we only evaluate one of the two expressions * we generated, so note an extra pop for the branch we didn't take. * Note that if either one pushes a value, both will, since we'll * explicitly have pushed nil for the one that doesn't generate a * value to keep the stack balanced on both branches. * * If neither of our expressions yields a value, don't pop anything * extra, since we won't think we've pushed two values in the course * of generating the two expressions. */ if (second_->has_return_value() || third_->has_return_value()) G_cg->note_pop(); /* set the label for the end of the expression */ def_label_pos(lbl_end); } /* ------------------------------------------------------------------------ */ /* * symbol */ void CTPNSym::gen_code(int discard, int) { /* * Look up the symbol; if it's undefined, add a default property * symbol entry if possible. Then ask the symbol to generate the * code. */ G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code(discard); } /* * assign to a symbol */ int CTPNSym::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx) { /* * Look up the symbol; if it's undefined and there's a "self" object * available, define it as a property by default, since a property * is the only kind of symbol that we could possibly assign to * without having defined anywhere in the program. Once we have the * symbol, tell it to generate the code for assigning to it. */ return G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code_asi(discard, phase, typ, op, rhs, ignore_errors, xplicit, ctx); } /* * take the address of the symbol */ void CTPNSym::gen_code_addr() { /* * Look up our symbol in the global symbol table, then ask the * resulting symbol to generate the appropriate code. If the symbol * isn't defined, assume it's a property. * * Note that we look only in the global symbol table, because local * symbols have no address value. So, even if the symbol is defined in * the local table, ignore the local definition and look at the global * definition. */ G_prs->get_global_symtab() ->find_or_def_prop_explicit(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_addr(); } /* * call the symbol */ void CTPNSym::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * Look up our symbol in the symbol table, then ask the resulting * symbol to generate the appropriate call. The symbol is * implicitly a property (if in a method context), since that's the * only kind of undefined symbol that we could be calling. */ G_cs->get_symtab() ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), FALSE, G_cs->is_self_available()) ->gen_code_call(discard, argc, varargs, named_args); } /* * generate code for 'new' */ void CTPNSym::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* * Look up our symbol, then ask the resulting symbol to generate the * 'new' code. If the symbol is undefined, add an 'undefined' entry * to the table; we can't implicitly create an object symbol. */ G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_new(discard, argc, varargs, named_args, is_transient); } /* * generate a property ID expression */ vm_prop_id_t CTPNSym::gen_code_propid(int check_only, int is_expr) { CTcSymbol *sym; CTcPrsSymtab *symtab; /* * Figure out where to look for the symbol. If the symbol was given * as an expression (in other words, it was explicitly enclosed in * parentheses), look it up in the local symbol table, since it * could refer to a local. Otherwise, it must refer to a property, * so look only in the global table. * * If the symbol isn't defined already, define it as a property now. * Because the symbol is explicitly on the right side of a member * evaluation, we can define it as a property whether or not there's * a valid "self" in this context. */ if (is_expr) { /* it's an expression - look it up in the local symbol table */ symtab = G_cs->get_symtab(); } else { /* it's a simple symbol - look only in the global symbol table */ symtab = G_prs->get_global_symtab(); } /* * look it up (note that this will always return a valid symbol, * since it will create one if we can't find an existing entry) */ sym = symtab->find_or_def_prop(get_sym_text(), get_sym_text_len(), FALSE); /* ask the symbol to generate the property reference */ return sym->gen_code_propid(check_only, is_expr); } /* * generate code for a member expression */ void CTPNSym::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* * Look up the symbol, and let it do the work. There's no * appropriate default for the symbol, so leave it undefined if we * can't find it. */ G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSym::gen_code_obj_predot(int *is_self) { /* * Look up the symbol, and let it do the work. There's no default * type for the symbol, so leave it undefined if we don't find it. */ return G_cs->get_symtab() ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) ->gen_code_obj_predot(is_self); } /* ------------------------------------------------------------------------ */ /* * resolved symbol */ void CTPNSymResolved::gen_code(int discard, int) { /* let the symbol handle it */ sym_->gen_code(discard); } /* * assign to a symbol */ int CTPNSymResolved::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx) { /* let the symbol handle it */ return sym_->gen_code_asi(discard, phase, typ, op, rhs, ignore_errors, xplicit, ctx); } /* * take the address of the symbol */ void CTPNSymResolved::gen_code_addr() { /* let the symbol handle it */ sym_->gen_code_addr(); } /* * call the symbol */ void CTPNSymResolved::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* let the symbol handle it */ sym_->gen_code_call(discard, argc, varargs, named_args); } /* * generate code for 'new' */ void CTPNSymResolved::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int /*from_call*/, int is_transient) { /* let the symbol handle it */ sym_->gen_code_new(discard, argc, varargs, named_args, is_transient); } /* * generate a property ID expression */ vm_prop_id_t CTPNSymResolved::gen_code_propid(int check_only, int is_expr) { /* let the symbol handle it */ return sym_->gen_code_propid(check_only, is_expr); } /* * generate code for a member expression */ void CTPNSymResolved::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* let the symbol handle it */ sym_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * generate code for an object before a '.' */ vm_obj_id_t CTPNSymResolved::gen_code_obj_predot(int *is_self) { /* let the symbol handle it */ return sym_->gen_code_obj_predot(is_self); } /* ------------------------------------------------------------------------ */ /* * Debugger local variable symbol */ /* * generate code to evaluate the variable */ void CTPNSymDebugLocal::gen_code(int discard, int for_condition) { /* if we're not discarding the value, push the local */ if (!discard) { /* generate the debugger local/parameter variable instruction */ G_cg->write_op(is_param_ ? OPC_GETDBARG : OPC_GETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); /* note that we pushed the value */ G_cg->note_push(); /* if it's a context local, get the value from the context array */ if (ctx_arr_idx_ != 0) { CTPNConst::s_gen_code_int(ctx_arr_idx_); G_cg->write_op(OPC_INDEX); /* * the 'index' operation pops two values and pushes one, for a * net of one pop */ G_cg->note_pop(); } } } /* * generate code for assigning to this variable */ int CTPNSymDebugLocal::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, CTcPrsNode *rhs, int, int, void **) { /* check what we're doing */ if (typ == TC_ASI_SIMPLE || phase == 2) { /* * Simple assignment, or phase 2 of a compound assignment. In * either case, just assign the rvalue to the variable. */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not discarding the result, duplicate the value so we'll * have a copy after the assignment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* check for a context property */ if (ctx_arr_idx_ == 0) { /* * generate the debug-local-set instruction - the operands are * the variable number and the stack frame index */ G_cg->write_op(is_param_ ? OPC_SETDBARG : OPC_SETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); } else { /* get the local containing our context object */ G_cg->write_op(OPC_GETDBLCL); G_cs->write2(var_id_); G_cs->write2(frame_idx_); /* set the actual variable value in the context object */ CTPNConst::s_gen_code_int(ctx_arr_idx_); G_cg->write_op(OPC_SETIND); G_cg->write_op(OPC_DISC); /* * we did three pops (SETIND), then a push (SETIND), then a pop * (DISC) - this is a net of three extra pops */ G_cg->note_pop(3); } /* the debug-local-set removes the rvalue from the stack */ G_cg->note_pop(); /* handled */ return TRUE; } else if (phase == 1) { /* * Compound assignment, phase 1. Simply generate our value and let * the caller proceed with the generic combination operator. */ gen_code(FALSE, FALSE); return FALSE; } else { /* ignore other phases */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Double-quoted string. The 'discard' status is irrelevant, because we * evaluate double-quoted strings for their side effects. */ void CTPNDstr::gen_code(int discard, int) { /* if we're not discarding the value, it's an error */ if (!discard) G_tok->log_error(TCERR_DQUOTE_IN_EXPR, (int)len_, str_); /* generate the instruction to display it */ G_cg->write_op(OPC_SAY); /* add the string to the constant pool, creating a fixup here */ G_cg->add_const_str(str_, len_, G_cs, G_cs->get_ofs()); /* write a placeholder value, which will be corrected by the fixup */ G_cs->write4(0); } /* ------------------------------------------------------------------------ */ /* * Double-quoted debug string */ void CTPNDebugDstr::gen_code(int, int) { /* generate code to push the in-line string */ G_cg->write_op(OPC_PUSHSTRI); G_cs->write2(len_); G_cs->write(str_, len_); /* write code to display the value */ G_cg->write_op(OPC_SAYVAL); /* note that we pushed the string and then popped it */ G_cg->note_push(); G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * Double-quoted string embedding */ /* * create an embedding */ CTPNDstrEmbed::CTPNDstrEmbed(CTcPrsNode *sub) : CTPNDstrEmbedBase(sub) { } /* * Generate code for a double-quoted string embedding */ void CTPNDstrEmbed::gen_code(int, int) { /* note the stack depth before generating the expression */ int orig_depth = G_cg->get_sp_depth(); /* * Generate code for the embedded expression. If the expression has a * return value, generate the value so that it can be displayed in the * string; but don't request a value if it doesn't have one, as a * return value is optional in this context. This is a normal value * invocation, not a conditional, so we need any applicable normal * value conversions. */ sub_->gen_code(!sub_->has_return_value(), FALSE); /* * If the code generation left anything on the stack, generate code * to display the value via the default display function. */ if (G_cg->get_sp_depth() > orig_depth) { /* add a SAYVAL instruction */ G_cg->write_op(OPC_SAYVAL); /* SAYVAL pops the argument value */ G_cg->note_pop(); } } /* ------------------------------------------------------------------------ */ /* * embedded <> list */ void CTPNStrOneOf::gen_code(int discard, int for_condition) { /* * There are two components to the <> list. First, there's the * list of strings. Second, there's an anonymous OneOfIndexGen object * that we create; this object holds our state and generates our index * values. * * In the simple case, the list is just a constant list of constant * strings. However, if the enclosing string is a dstring, each of * these will be a dstring; and even if the outer string is an sstring, * these could contain further embedded expressions, in which case they * won't be constant. * * If the list is constant, we generate list[generator.getNextIndex()]. * This selects a string from the list and leaves it on the stack, * which is just what our parent node wants. * * If the list isn't constant, we generate a switch on * generator.getNextIndex(), and then fill in the case table from the * list: each case label is the index, and each case's body is the code * generated for the list element, followed by a 'break' to the end of * the switch. The effect once again is to pick an item from the list * and push its value onto the stack. */ /* look up the getNextIndex property */ CTcSymProp *propsym = (CTcSymProp *)G_prs->get_global_symtab()-> find_or_def_prop_explicit("getNextIndex", 12, FALSE); if (propsym->get_type() != TC_SYM_PROP) { G_tok->log_error(TCERR_ONEOF_REQ_GETNXT); return; } /* synthesize a constant value for the property */ CTcConstVal propc; propc.set_prop(propsym->get_prop()); CTPNConst *getNextIndex = new CTPNConst(&propc); /* generate the list */ CTcConstVal *c = lst_->get_const_val(); if (c != 0) { /* the list is a constant - push the list value */ lst_->gen_code(FALSE, FALSE); /* call generator.getNextIndex() to get the next index */ state_obj_->gen_code_member(FALSE, getNextIndex, FALSE, 0, FALSE, 0); /* index the list (this pops two values and pushes one) */ G_cg->write_op(OPC_INDEX); G_cg->note_pop(); } else { /* the list is non-constant - start by generating the index */ state_obj_->gen_code_member(FALSE, getNextIndex, FALSE, 0, FALSE, 0); /* note the number of list elements */ int i, cnt = lst_->get_count(); /* generate the SWITCH */ G_cg->write_op(OPC_SWITCH); G_cs->write2(cnt); G_cg->note_pop(); /* * Build the case table. Each label is an index value; leave the * jump offset zero for now, since we'll have to fill it in as we * generate the code. */ ulong caseofs = G_cs->get_ofs(); for (i = 1 ; i <= cnt ; ++i) { /* write the case label as the 1-based index value */ char buf[VMB_DATAHOLDER]; vm_val_t val; val.set_int(i); vmb_put_dh(buf, &val); G_cs->write(buf, VMB_DATAHOLDER); /* write a placeholder for the jump offset */ G_cs->write2(0); } /* write a placeholder for the default case */ G_cs->write2(0); /* create a forward label for the 'break' to the end of the switch */ CTcCodeLabel *brklbl = G_cs->new_label_fwd(); /* * Now run through the table and generate the code for each list * element. */ CTPNListEle *ele; for (i = 1, ele = lst_->get_head() ; ele != 0 && i <= cnt ; ++i, caseofs += VMB_DATAHOLDER + 2, ele = ele->get_next()) { /* set the jump to this case in the case table */ G_cs->write2_at(caseofs + VMB_DATAHOLDER, G_cs->get_ofs() - (caseofs + VMB_DATAHOLDER)); /* generate the code for this list element */ ele->get_expr()->gen_code(discard, for_condition);; /* generate a jump to the end of the table */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(brklbl, 0); /* * since all of these branches proceed in parallel, only one * push counts, so don't count every iteration - pretend that * we've already popped the value that this case generated */ if (!discard) G_cg->note_pop(); } /* write the default case - just push nil */ G_cs->write2_at(caseofs, G_cs->get_ofs() - caseofs); if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* this is the end of the switch - set the 'break' label here */ def_label_pos(brklbl); } } /* ------------------------------------------------------------------------ */ /* * Create or define an internal compiler-defined function in the program. */ static CTcSymFunc *get_internal_func(const char *nm, int argc, int opt_argc, int varargs, int has_retval) { /* get the symbol length */ size_t len = strlen(nm); /* look up the function symbol */ CTcSymFunc *fn = (CTcSymFunc *)G_prs->get_global_symtab()->find_delete_weak(nm, len); if (fn == 0) { /* it's not defined - create an implied declaration for it */ fn = new CTcSymFunc(nm, len, FALSE, argc, opt_argc, varargs, has_retval, FALSE, FALSE, TRUE, TRUE); /* add it to the global symbol table */ G_prs->get_global_symtab()->add_entry(fn); } else if (fn->get_type() != TC_SYM_FUNC) { /* it's defined, but not as a function - this is an error */ G_tok->log_error(TCERR_REDEF_AS_FUNC, (int)len, nm); fn = 0; } /* return the function */ return fn; } /* * Get an internal built-in function by name */ static CTcSymBif *get_internal_bif(const char *nm, int err) { /* look up the symbol */ size_t len = strlen(nm); CTcSymBif *fn = (CTcSymBif *)G_prs->get_global_symtab()->find(nm, len); /* if it wasn't found, flag an error */ if (fn == 0) G_tok->log_error(err, (int)len, nm); /* return the function */ return fn; } /* * Call an internal built-in function by name. This is for simple calls * with fixed arguments and no named parameters, keeping the return value. */ static void call_internal_bif(const char *nm, int err, int argc) { /* look up the function */ CTcSymBif *fn = get_internal_bif(nm, err); /* if we got it, call it */ if (fn != 0) fn->gen_code_call(FALSE, argc, FALSE, 0); } /* ------------------------------------------------------------------------ */ /* * Argument list */ void CTPNArglist::gen_code_arglist(int *varargs, CTcNamedArgs &named_args) { CTPNArg *arg; int i; int fixed_cnt; int pushed_varargs_counter; /* * scan the argument list for varargs - if we have any, we must * treat all of them as varargs */ for (*varargs = FALSE, fixed_cnt = named_args.cnt = 0, named_args.args = this, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { /* if this is a varargs argument, we have varargs */ if (arg->is_varargs()) { /* note it */ *varargs = TRUE; } else if (arg->is_named_param()) { /* count the named argument */ named_args.cnt += 1; } else { /* count another fixed positional argument */ ++fixed_cnt; } } /* * If we have named arguments, push them ahead of the position * arguments. */ if (named_args.cnt != 0) { /* * Run through the argument list again, evaluating only named * arguments on this pass. */ for (i = argc_, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), --i) { /* if it's a named argument, push it */ if (arg->is_named_param()) arg->gen_code(FALSE, FALSE); } } /* * Push each positional argument in the list. The arguments on the * stack go in right-to-left order. The parser builds the internal * list of argument expressions in reverse order, so we must merely * follow the list from head to tail. * * We need each argument value to be pushed (hence discard = false), * and we need the assignable value of each argument expression (hence * for_condition = false). */ for (pushed_varargs_counter = FALSE, i = argc_, arg = get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), --i) { int depth; /* skip named arguments on this pass */ if (arg->is_named_param()) continue; /* note the stack depth before generating the value */ depth = G_cg->get_sp_depth(); /* * check for varargs - if this is first varargs argument, push * the counter placeholder */ if (arg->is_varargs() && !pushed_varargs_counter) { /* * write code to push the fixed argument count - we can use * this as a starting point, since we always know we have * this many argument to start with; we'll dynamically add * in the variable count at run-time */ CTPNConst::s_gen_code_int(fixed_cnt); /* note that we've pushed the counter */ pushed_varargs_counter = TRUE; /* * we will take the extra value off when we evaluate the * varargs counter, so simply count it as removed now */ G_cg->note_pop(); } /* generate the argument's code */ arg->gen_code(FALSE, FALSE); /* * if we've pushed the variable argument counter value onto the * stack, and this a fixed argument, swap the top two stack * elements to get the argument counter back to the top of the * stack; if this is a varargs argument there's no need, since * it will have taken care of this */ if (pushed_varargs_counter && !arg->is_varargs()) G_cg->write_op(OPC_SWAP); /* ensure that it generated something */ if (G_cg->get_sp_depth() <= depth) G_tok->log_error(TCERR_ARG_EXPR_HAS_NO_VAL, i); } } /* * ------------------------------------------------------------------------ */ /* * argument list entry */ void CTPNArg::gen_code(int, int) { /* * Generate the argument expression. We need the value (hence * discard = false), and we need the assignable value (hence * for_condition = false). */ get_arg_expr()->gen_code(FALSE, FALSE); /* * if this is a list-to-varargs conversion, generate the conversion * instruction */ if (is_varargs_) { /* write the opcode */ G_cg->write_op(OPC_MAKELSTPAR); /* note the extra push and pop for the argument count */ G_cg->note_push(); G_cg->note_pop(); } } /* ------------------------------------------------------------------------ */ /* * function/method call */ /* * create */ CTPNCall::CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist) : CTPNCallBase(func, arglist) { /* the T3 instruction set limits calls to 127 arguments */ if (arglist->get_argc() > 127) G_tok->log_error(TCERR_TOO_MANY_CALL_ARGS); } /* * generate code */ void CTPNCall::gen_code(int discard, int for_condition) { /* check for special functions */ if (get_func()->sym_text_matches("rand", 4) && gen_code_rand(discard, for_condition)) return; /* push the argument list */ int varargs; CTcNamedArgs named_args; get_arg_list()->gen_code_arglist(&varargs, named_args); /* generate an appropriate call instruction */ get_func()->gen_code_call(discard, get_arg_list()->get_argc(), varargs, &named_args); } /* * Generate code for a call to 'rand'. If we have multiple arguments, * rand() is special because it doesn't evaluate all of its arguments. * Instead, it picks an argument at random, and evaluates only that single * selected argument, yielding the result. */ int CTPNCall::gen_code_rand(int discard, int for_condition) { /* get our argument list, and the number of arguments */ CTPNArglist *args = get_arg_list(); int argc = args->get_argc(); /* if we have zero or one argument, use the normal call */ if (argc < 2) return FALSE; /* if we have variable arguments, use the normal call */ CTPNArg *arg; for (arg = args->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg()) { if (arg->is_varargs()) return FALSE; } /* * We have a fixed number of arguments greater than one, so this is the * special form of the rand() call. First, generate an ordinary call * to rand() to select a number from 0 to argc-1. */ CTcNamedArgs named_args; named_args.cnt = 0; CTPNConst::s_gen_code_int(argc); get_func()->gen_code_call(FALSE, 1, FALSE, &named_args); /* generate the SWITCH */ G_cg->write_op(OPC_SWITCH); G_cs->write2(argc); G_cg->note_pop(); /* * Build the case table. The rand() call gave us an integer from 0 to * argc-1 inclusive, so our case labels are simply the argument index * values, starting at zero. Note that the argument list is stored * right to left, so the index values of the list elements run from * argc-1 down to 0. Since we're making a random selection anyway the * order shouldn't really matter; however, for the sake of regression * tests for pre-3.1 games, we want to use the same index ordering that * rand() did in the old version, where rand() itself chose one of its * pre-evaluated arguments. When running in regression mode with a * fixed random number stream, using the same index ordering will * ensure that we get the same results when running existing tests. */ ulong caseofs = G_cs->get_ofs(); int i; for (i = argc - 1 ; i >= 0 ; --i) { /* write the case label as the 0-based argument index */ char buf[VMB_DATAHOLDER]; vm_val_t val; val.set_int(i); vmb_put_dh(buf, &val); G_cs->write(buf, VMB_DATAHOLDER); /* write a placeholder for the jump offset */ G_cs->write2(0); } /* write a placeholder for the default case */ G_cs->write2(0); /* create a forward label for the 'break' to the end of the switch */ CTcCodeLabel *brklbl = G_cs->new_label_fwd(); /* generate the argument expressions */ for (arg = args->get_arg_list_head() ; arg != 0 ; arg = arg->get_next_arg(), caseofs += VMB_DATAHOLDER + 2) { /* set the jump to this case in the case table */ G_cs->write2_at(caseofs + VMB_DATAHOLDER, G_cs->get_ofs() - (caseofs + VMB_DATAHOLDER)); /* generate the argument expression */ arg->get_arg_expr()->gen_code(discard, for_condition); /* generate a jump to the end of the table */ G_cg->write_op(OPC_JMP); G_cs->write_ofs2(brklbl, 0); /* we generate the branches in parallel, so only one push counts */ if (!discard) G_cg->note_pop(); } /* write the default case - just push nil if we need a value */ G_cs->write2_at(caseofs, G_cs->get_ofs() - caseofs); if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* this is the end of the switch - set the 'break' label here */ def_label_pos(brklbl); /* tell the caller that we handled the code generation */ return TRUE; } /* * Generate code for operator 'new'. A 'new' with an argument list * looks like a function call: NEW(CALL(object-contents, ARGLIST(...))). */ void CTPNCall::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int from_call, int is_transient) { /* * if this is a recursive call from another 'call' node, it's not * allowed - we'd be trying to use the result of a call as the base * class of the 'new', which is illegal */ if (from_call) { G_tok->log_error(TCERR_INVAL_NEW_EXPR); return; } /* use our own named_args structure if the caller didn't provide one */ CTcNamedArgs my_named_args; if (named_args == 0) named_args = &my_named_args; /* generate the argument list */ get_arg_list()->gen_code_arglist(&varargs, *named_args); /* generate the code for the 'new' call */ get_func()->gen_code_new(discard, get_arg_list()->get_argc(), varargs, named_args, TRUE, is_transient); } /* ------------------------------------------------------------------------ */ /* * member property evaluation */ void CTPNMember::gen_code(int discard, int) { /* ask the object expression to generate the code */ get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, 0, FALSE, 0); } /* context for two-phase compound assignment to a member expression */ struct member_ca_ctx { member_ca_ctx(int is_self, vm_obj_id_t obj, vm_prop_id_t prop) { this->is_self = is_self; this->obj = obj; this->prop = prop; } int is_self; vm_obj_id_t obj; vm_prop_id_t prop; }; /* * assign to member expression */ int CTPNMember::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int, void **ctx) { int is_self; vm_obj_id_t obj; vm_prop_id_t prop; /* if this is phase 2 of a compound assignment, retrieve the context */ if (phase == 2) { /* cast the generic context pointer to our structure */ member_ca_ctx *cctx = (member_ca_ctx *)*ctx; /* retrieve the context information */ is_self = cctx->is_self; obj = cctx->obj; prop = cctx->prop; /* we're done with the context object - delete it */ delete cctx; } /* * if this is a simple assignment, start by generating the right-hand * side, unless the caller has already done so */ if (typ == TC_ASI_SIMPLE && rhs != 0) rhs->gen_code(FALSE, FALSE); /* * If this is a simple assignment, or it's phase 2 of a two-phase * assignment, we now have the RHS at top of stack, and we're about to * perform the actual assignment. If the caller wants to further use * the assigned value, push a copy, since we'll consume the RHS value * currently on the stack with our SETPROP or related instruction. */ if (!discard && (typ == TC_ASI_SIMPLE || phase == 2)) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * If this is a simple assignment, or phase 1 of a two-phase compound * assignment, generate the object and property expressions. */ if (typ == TC_ASI_SIMPLE || phase == 1) { /* * Determine what we have on the left: we could have self, a * constant object value, or any other expression. */ obj = get_obj_expr()->gen_code_obj_predot(&is_self); /* * determine what kind of property expression we have - don't * generate any code for now, since we may need to generate some * more code ahead of the property generation */ prop = get_prop_expr()->gen_code_propid(TRUE, prop_is_expr_); /* determine what we need to do based on the operands */ if (prop == VM_INVALID_PROP) { /* * We're assigning through a property pointer -- we must * generate a PTRSETPROP instruction. * * Before we generate the property expression, we must generate * the object expression. If we got a constant object, we must * generate code to push that object value; otherwise, the code * to generate the object value is already generated. */ if (is_self) { /* self - generate code to push the "self" value */ G_cg->write_op(OPC_PUSHSELF); G_cg->note_push(); } else if (obj != VM_INVALID_OBJ) { /* constant object - generate code to push the value */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj); G_cg->note_push(); } /* generate the property value expression */ get_prop_expr()->gen_code_propid(FALSE, prop_is_expr_); /* * If this is phase 1 of a two-phase compound assignment, we'll * need the object and property values that we've just stacked * again in phase 2. Save separate copies via DUP2. */ if (typ != TC_ASI_SIMPLE) { G_cg->write_op(OPC_DUP2); G_cg->note_push(2); } } else { /* * We're assigning to a fixed property ID. If the object * expression was not 'self' or a fixed object ID, we have an * object value on the stack; otherwise we have nothing on the * stack. * * For a two-phase assignment, we'll need the object value * again during the second phase, so if we did indeed stack a * value, make a copy of it for our use in phase 2. */ if (typ != TC_ASI_SIMPLE && !is_self && obj == VM_INVALID_OBJ) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } } } /* * If this is phase 1 of a two-phase assignment, stop here: we've * stacked the necessary obj and prop information for the actual * assignment codegen in phase 2, and we've stacked the obj.prop value * for the combination operator to access. Save context information * and return, telling the caller to proceed with the unrolled * operation. */ if (typ != TC_ASI_SIMPLE && phase == 1) { /* save the object and property information in the phase context */ *ctx = new member_ca_ctx(is_self, obj, prop); /* evaluate the property */ if (prop == VM_INVALID_PROP) { /* property pointer - call with zero arguments */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write(0); /* this pops the object and property */ G_cg->note_pop(2); } else { /* constant property */ if (is_self) { /* get property of self */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop); } else if (obj != VM_INVALID_OBJ) { /* constant object */ G_cg->write_op(OPC_OBJGETPROP); G_cs->write_obj_id(obj); G_cs->write_prop_id(prop); } else { /* it's an object expression */ G_cg->write_op(OPC_GETPROP); G_cs->write_prop_id(prop); /* this pops the object value */ G_cg->note_pop(); } } /* all of these leave the value in R0 - retrieve it */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); /* tell the caller to proceed with the unrolled assignment */ return FALSE; } /* * If we're doing a simple assignment, or this is phase 2 of a compound * assignment, it's time to do the actual assignment. */ if (typ == TC_ASI_SIMPLE || phase == 2) { /* determine how to make the assignment based on the operands */ if (prop == VM_INVALID_PROP) { /* * If this is phase 2 of a compound assignment, get the stack * back in order. We currently have (from bottom to top) obj * prop RHS RHS (if !discard), or obj prop RHS (if discard). * Swap the top one or two elements for the next two. */ if (typ != TC_ASI_SIMPLE && phase == 2) { if (discard) { /* obj prop RHS -> RHS prop obj */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); /* RHS prop obj -> RHS obj prop */ G_cg->write_op(OPC_SWAP); } else { /* obj prop RHS RHS -> RHS RHS obj prop */ G_cg->write_op(OPC_SWAP2); } } /* generate the PTRSETPROP instruction */ G_cg->write_op(OPC_PTRSETPROP); /* ptrsetprop removes three elements */ G_cg->note_pop(3); } else { /* constant property value */ /* * If this is phase 2, and we have an object on the stack, we * need to rearrange the stack into the proper order: * * !discard: stack = (obj RHS RHS), and we need (RHS RHS obj). * * discard: stack = (obj RHS), and we need (obj RHS). Just do * a SWAP. * * If there's no object on the stack, there's no need to do any * rearrangement. */ if (phase == 2 && !is_self && obj == VM_INVALID_OBJ) { if (discard) { /* obj RHS -> RHS obj */ G_cg->write_op(OPC_SWAP); } else { /* obj RHS RHS -> RHS RHS obj */ G_cg->write_op(OPC_SWAPN); G_cs->write(0); G_cs->write(2); } } /* * We have several instructions to choose from. If we're * assigning to a property of "self", use SETPROPSELF. If * we're assigning to a constant object, use OBJSETPROP. * Otherwise, use the plain SETPROP. */ if (is_self) { /* write the SETPROPSELF */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop); /* setpropself removes the RHS value */ G_cg->note_pop(); } else if (obj != VM_INVALID_OBJ) { /* write the OBJSETPROP */ G_cg->write_op(OPC_OBJSETPROP); G_cs->write_obj_id(obj); G_cs->write_prop_id(prop); /* objsetprop removes the RHS value */ G_cg->note_pop(); } else { /* * write the normal SETPROP; we already generated the code * to push the object value, so it's where it should be */ G_cg->write_op(OPC_SETPROP); G_cs->write_prop_id(prop); /* setprop removes the value and the object */ G_cg->note_pop(2); } } } /* we're done with the operation */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * member with argument list */ void CTPNMemArg::gen_code(int discard, int) { /* push the argument list */ int varargs; CTcNamedArgs named_args; get_arg_list()->gen_code_arglist(&varargs, named_args); /* ask the object expression to generate the code */ get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, get_arg_list()->get_argc(), varargs, &named_args); } /* ------------------------------------------------------------------------ */ /* * construct a list */ void CTPNList::gen_code(int discard, int for_condition) { /* * Before we construct the list dynamically, check to see if the * list is constant. If it is, we need only built the list in the * constant pool, and push its offset. */ if (is_const()) { /* push the value only if we're not discarding it */ if (!discard) { /* write the instruction */ G_cg->write_op(OPC_PUSHLST); /* add the list to the constant pool */ G_cg->add_const_list(this, G_cs, G_cs->get_ofs()); /* * write a placeholder address, which will be corrected by * the fixup that add_const_list() created */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } } else { /* * It's not a constant list, so we must generate code to construct * a list dynamically. Push each element of the list. We need * each value (hence discard = false), and we require the * assignable value of each expression (hence for_condition = * false). Push the argument list in reverse order, since the * run-time metaclass requires this ordering. */ for (CTPNListEle *ele = get_tail() ; ele != 0 ; ele = ele->get_prev()) ele->gen_code(FALSE, FALSE); /* generate a NEW instruction for an object of metaclass LIST */ if (get_count() <= 255) { /* the count will fit in one byte - use the short form */ G_cg->write_op(OPC_NEW1); G_cs->write((char)get_count()); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); } else { /* count doesn't fit in one byte - use the long form */ G_cg->write_op(OPC_NEW2); G_cs->write2(get_count()); G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); } /* new1/new2 remove arguments */ G_cg->note_pop(get_count()); /* if we're not discarding the value, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * If this is a lookup table list, and we're not discarding the value, * create the actual LookupTable object from the list source. If we're * discarding the value there's no need to create the object, since * doing so will have no meaningful side effects. */ if (!discard && is_lookup_table_) { /* * To construct the lookup table, we simply pass the list of [key, * value, key, value...] elements to the LookupTable constructor. * The list is already on the stack, so simply call the constructor * with one argument. */ G_cg->write_op(OPC_NEW1); G_cs->write(1); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LOOKUP_TABLE)); /* NEW1 removes arguments */ G_cg->note_pop(1); /* push the result */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * list element */ void CTPNListEle::gen_code(int discard, int for_condition) { /* generate the subexpression */ expr_->gen_code(discard, for_condition); } /* ------------------------------------------------------------------------ */ /* * Basic T3-specific symbol class */ /* * generate code to take the address of a symbol - in general, we cannot * take the address of a symbol, so we'll just log an error */ void CTcSymbol::gen_code_addr() { G_tok->log_error(TCERR_NO_ADDR_SYM, (int)get_sym_len(), get_sym()); } /* * generate code to assign to the symbol - in general, we cannot assign * to a symbol, so we'll just log an error */ int CTcSymbol::gen_code_asi(int, int phase, tc_asitype_t, const char *, class CTcPrsNode *, int ignore_error, int, void **) { /* * If we're ignoring errors, simply return false to indicate that * nothing happened. Ignore errors on phase 2, since we'll already * have generated an error on phase 1. */ if (ignore_error || phase > 1) return FALSE; /* log the error */ G_tok->log_error(TCERR_CANNOT_ASSIGN_SYM, (int)get_sym_len(), get_sym()); /* * even though we didn't generate anything, this has been fully * handled - the caller shouldn't attempt to generate any additional * code for this */ return TRUE; } /* * Generate code for calling the symbol. By default, we can't call a * symbol. */ void CTcSymbol::gen_code_call(int, int, int, CTcNamedArgs *) { /* log an error */ G_tok->log_error(TCERR_CANNOT_CALL_SYM, (int)get_sym_len(), get_sym()); } /* * Generate code for operator 'new' */ void CTcSymbol::gen_code_new(int, int, int, CTcNamedArgs *, int) { G_tok->log_error(TCERR_INVAL_NEW_EXPR); } /* * evaluate a property ID */ vm_prop_id_t CTcSymbol::gen_code_propid(int check_only, int is_expr) { /* by default, a symbol cannot be used as a property ID */ if (!check_only) G_tok->log_error(TCERR_SYM_NOT_PROP, (int)get_sym_len(), get_sym()); /* we can't return a valid property ID */ return VM_INVALID_PROP; } /* * evaluate a member expression */ void CTcSymbol::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* by default, a symbol cannot be used as an object expression */ G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); } /* * generate code for an object expression before a '.' */ vm_obj_id_t CTcSymbol::gen_code_obj_predot(int *is_self) { /* by default, a symbol cannot be used as an object expression */ G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); /* indicate that we don't have a constant object */ *is_self = FALSE; return VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * T3-specific function symbol class */ /* * evaluate the symbol */ void CTcSymFunc::gen_code(int discard) { /* * function address are always unknown during code generation; * generate a placeholder instruction and add a fixup record for it */ G_cg->write_op(OPC_PUSHFNPTR); /* add a fixup for the current code location */ add_abs_fixup(G_cs); /* write a placeholder offset - arbitrarily use zero */ G_cs->write4(0); /* note the push */ G_cg->note_push(); } /* * take the address of the function */ void CTcSymFunc::gen_code_addr() { /* '&function' is the same as 'function' with no arguments */ gen_code(FALSE); } /* * call the symbol */ void CTcSymFunc::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* we can't call a function that doesn't have a prototype */ if (!has_proto_) { G_tok->log_error(TCERR_FUNC_CALL_NO_PROTO, (int)get_sym_len(), get_sym()); return; } /* * If this is a multi-method base function, a call to the function is * actually a call to _multiMethodCall('name', args). */ if (is_multimethod_base_) { /* make a list out of the arguments */ if (varargs) { G_cg->write_op(OPC_VARARGC); G_cs->write((char)argc); } else if (argc <= 255) { G_cg->write_op(OPC_NEW1); G_cs->write((char)argc); } else { G_cg->write_op(OPC_NEW2); G_cs->write2(argc); } G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); G_cg->note_pop(argc); G_cg->write_op(OPC_GETR0); G_cg->note_push(); /* add the base function pointer argument */ CTcConstVal funcval; funcval.set_funcptr(this); CTPNConst func(&funcval); func.gen_code(FALSE, FALSE); /* look up _multiMethodCall */ CTcSymFunc *mmc = get_internal_func( "_multiMethodCall", 1, 0, TRUE, TRUE); /* * Generate the call. Note that there are always two arguments at * this point: the base function pointer, and the argument list. * The argument list is just one argument because we've already * constructed a list out of it. */ if (mmc != 0) mmc->gen_code_call(discard, 2, FALSE, named_args); } else { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call instruction and argument count */ G_cg->write_op(OPC_CALL); G_cs->write((char)argc); /* check the mode */ if (G_cg->is_eval_for_dyn()) { /* * dynamic (run-time) compilation - we know the absolute * address already, since all symbols are pre-resolved in the * debugger */ G_cs->write4(get_code_pool_addr()); } else { /* * Normal compilation - we won't know the function's address * until after generation is completed, so add a fixup for the * current location, then write a placeholder for the offset * field. */ add_abs_fixup(G_cs); G_cs->write4(0); } /* call removes arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* make sure the argument count is correct */ if (!argc_ok(argc)) { char buf[128]; G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, (int)get_sym_len(), get_sym(), get_argc_desc(buf), argc); } /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } } /* * Get my code pool address. Valid only after linking. */ ulong CTcSymFunc::get_code_pool_addr() const { /* check for an absolute address */ if (abs_addr_valid_) { /* * we have an absolute address - this means the symbol was * loaded from a fully-linked image file (specifically, from the * debug records) */ return abs_addr_; } else { /* * we don't have an absolute address, so our address must have * been determined through a linking step - get the final * address from the anchor */ return anchor_->get_addr(); } } /* * add a runtime symbol table entry */ void CTcSymFunc::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add an entry for our absolute address */ val.set_fnptr(get_code_pool_addr()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * T3-specific object symbol class */ /* * evaluate the symbol */ void CTcSymObj::gen_code(int discard) { /* write code to push the object ID */ if (!discard) { /* push the object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id_); /* note the push */ G_cg->note_push(); } } /* * take the address of the object */ void CTcSymObj::gen_code_addr() { /* act as though we were pushing the object ID directly */ gen_code(FALSE); } /* * Generate a 'new' expression */ void CTcSymObj::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* use our static generator */ s_gen_code_new(discard, obj_id_, metaclass_, argc, varargs, named_args, is_transient); } /* * Generate a 'new' expression. (This is a static method so that this * code can be used by all of the possible expression types to which * 'new' can be applied.) * * This type of generation applies only to objects of metaclass TADS * Object. */ void CTcSymObj::s_gen_code_new(int discard, vm_obj_id_t obj_id, tc_metaclass_t meta, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* * push the base class object - this is always the first argument * (hence last pushed) to the metaclass constructor */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); /* note the push */ G_cg->note_push(); /* * note that we can only allow 126 arguments to a constructor, * because we must add the implicit superclass argument */ if (argc > 126) G_tok->log_error(TCERR_TOO_MANY_CTOR_ARGS); /* * if we have varargs, swap the top stack elements to get the * argument count back on top, and then generate the varargs * modifier opcode */ if (varargs) { /* swap the top stack elements to get argc back to the top */ G_cg->write_op(OPC_SWAP); /* * increment the argument count to account for the superclass * object argument */ G_cg->write_op(OPC_INC); /* write the varargs modifier opcode */ G_cg->write_op(OPC_VARARGC); } /* * write the NEW instruction - since we always add TADS Object to * our metaclass table before we start compiling any code, we know * it always has a small metaclass number and will always fit in the * short form of the instruction * * Note that the actual argument count we generate is one higher * than the source code argument list, because we add the implicit * first argument to the metaclass constructor */ G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); G_cs->write((char)(argc + 1)); /* write out the dependency table index for the metaclass */ switch (meta) { case TC_META_TADSOBJ: G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_TADSOBJ)); break; default: /* we can't use 'new' on symbols of other metaclasses */ G_tok->log_error(TCERR_BAD_META_FOR_NEW); G_cs->write(0); break; } /* new1 removes the arguments */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if they're not discarding the value, push the new object * reference, which will be in R0 when the constructor returns */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * Generate code for a member expression */ void CTcSymObj::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { s_gen_code_member(discard, prop_expr, prop_is_expr, argc, obj_id_, varargs, named_args); } /* * Static method to generate code for a member expression. This is * static so that constant object nodes can share it. */ void CTcSymObj::s_gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, vm_obj_id_t obj_id, int varargs, CTcNamedArgs *named_args) { vm_prop_id_t prop; /* * generate the property expression - don't generate the code right * now even if code generation is necessary, because this isn't the * right place for it; for now, simply check to determine if we're * going to need to generate any code for the property expression */ prop = prop_expr->gen_code_propid(TRUE, prop_is_expr); /* don't allow method calls with arguments in speculative mode */ if (argc != 0 && G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* check for a constant property value */ if (prop != VM_INVALID_PROP) { /* generate an OBJGETPROP or OBJCALLPROP as appropriate */ if (G_cg->is_speculative()) { /* speculative evaluation - use GETPROPDATA */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop); /* we pushed the object, then popped it */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* no arguments - use OBJGETPROP */ G_cg->write_op(OPC_OBJGETPROP); G_cs->write_obj_id(obj_id); G_cs->write_prop_id(prop); } else { /* generate a varargs modifier if needed */ if (varargs) G_cg->write_op(OPC_VARARGC); /* arguments - use OBJCALLPROP */ G_cg->write_op(OPC_OBJCALLPROP); G_cs->write((char)argc); G_cs->write_obj_id(obj_id); G_cs->write_prop_id(prop); /* objcallprop removes arguments */ G_cg->note_pop(argc); } } else { /* * non-constant property value - we must first push the object * value, then push the property value, then write a PTRCALLPROP * instruction */ /* generate the object push */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(obj_id); /* note the pushes */ G_cg->note_push(); /* keep the argument counter on top if necessary */ if (varargs) G_cg->write_op(OPC_SWAP); /* generate the property push */ prop_expr->gen_code_propid(FALSE, prop_is_expr); /* generate the PTRCALLPROP or PTRGETPROPDATA */ if (G_cg->is_speculative()) { /* speculative - use the data-only property evaluation */ G_cg->write_op(OPC_PTRGETPROPDATA); } else { /* * if we have a varargs list, modify the call instruction * that follows to make it a varargs call */ if (varargs) { /* swap to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* normal - call the property */ G_cg->write_op(OPC_PTRCALLPROP); G_cs->write((int)argc); } /* ptrcallprop removes the arguments, the object, and the property */ G_cg->note_pop(argc + 2); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if they want the result, push it onto the stack */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate code for an object before a '.' */ vm_obj_id_t CTcSymObj::gen_code_obj_predot(int *is_self) { /* return our constant object reference */ *is_self = FALSE; return obj_id_; } /* * add a runtime symbol table entry */ void CTcSymObj::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_obj(obj_id_); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Invokable object */ void CTcSymFuncObj::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* push my value as the function pointer */ gen_code(discard); /* generate the pointer-call instruction and argument count */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* ptrcall removes arguments and the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* ------------------------------------------------------------------------ */ /* * T3-specific property symbol class */ /* * evaluate the symbol */ void CTcSymProp::gen_code(int discard) { /* * Evaluating a property is equivalent to calling the property on * the "self" object with no arguments. If there's no "self" * object, an unqualified property evaluation is not possible, so * log an error if this is the case. */ if (!G_cs->is_self_available()) { G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); return; } if (G_cg->is_speculative()) { /* push 'self', then evaluate the property in data-only mode */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(prop_); /* we pushed the 'self' value then popped it again */ G_cg->note_push(); G_cg->note_pop(); } else { /* generate the call to 'self' */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(prop_); } /* if they're not discarding the value, push the result */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * evaluate a member expression */ void CTcSymProp::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* generate code to evaluate the property */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * take the address of the property */ void CTcSymProp::gen_code_addr() { /* write code to push the property ID */ G_cg->write_op(OPC_PUSHPROPID); G_cs->write_prop_id(prop_); /* note the push */ G_cg->note_push(); } /* * assign to a property, implicitly of the "self" object */ int CTcSymProp::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int /*ignore_errors*/, int /*explicit*/, void ** /*ctx*/) { /* if there's no "self" object, we can't make this assignment */ if (!G_cs->is_self_available()) { /* log an error if it's phase 1 */ if (phase == 1) G_tok->log_error(TCERR_SETPROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); /* * indicate that we're finished, since there's nothing more we * can do here */ return TRUE; } /* check what we're doing */ if (typ == TC_ASI_SIMPLE) { /* * Simple assignment. Generate the right-hand side's expression * for assignment, unless the caller has already done so. */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not discarding the value, make a copy - we'll consume a * copy in the SETPROP instruction, so we need one more copy to * return to the enclosing expression */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * write the SETPROP instruction - use the special form to assign * to "self" */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* setpropself removes the value */ G_cg->note_pop(); /* handled */ return TRUE; } else if (phase == 1) { /* * Phase 1 of a compound assignment. Generate the property value * so that the compound operator can combine it with the RHS. */ gen_code(FALSE); /* proceed with the unrolled assignment */ return FALSE; } else if (phase == 2) { /* * Phase 2 of a compound assignment. The combined value of * (self.prop RHS) is on the stack, so complete the * assignment. */ /* if the caller will want to use the RHS value, make a copy */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* write the SETPROP */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* setpropself removes the value */ G_cg->note_pop(); /* done */ return TRUE; } else { /* other phases aren't handled */ return FALSE; } } /* * call the symbol */ void CTcSymProp::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * if there's no "self", we can't invoke a property without an * explicit object reference */ if (!G_cs->is_self_available()) { G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); return; } /* don't allow calling with arguments in speculative mode */ if (G_cg->is_speculative() && (argc != 0 || (named_args != 0 && named_args->cnt != 0))) err_throw(VMERR_BAD_SPEC_EVAL); /* generate code to invoke the property of "self" */ if (G_cg->is_speculative()) { /* push 'self', then get the property in data-only mode */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_GETPROPDATA); G_cs->write_prop_id(get_prop()); /* we pushed 'self' then popped it again */ G_cg->note_push(); G_cg->note_pop(); } else if (argc == 0) { /* use the instruction with no arguments */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(get_prop()); } else { /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* use the instruction with arguments */ G_cg->write_op(OPC_CALLPROPSELF); G_cs->write((char)argc); G_cs->write_prop_id(get_prop()); /* callpropself removes arguments */ G_cg->note_pop(argc); } /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding, push the return value from R0 */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a property ID expression */ vm_prop_id_t CTcSymProp::gen_code_propid(int check_only, int is_expr) { /* * If I'm to be treated as an expression (which indicates that the * property symbol is explicitly enclosed in parentheses in the * original source code expression), then I must evaluate this * property of self. Otherwise, I yield literally the property ID. */ if (is_expr) { /* generate code unless we're only checking */ if (!check_only) { /* evaluate this property of self */ G_cg->write_op(OPC_GETPROPSELF); G_cs->write_prop_id(get_prop()); /* leave the result on the stack */ G_cg->write_op(OPC_GETR0); G_cg->note_push(); } /* tell the caller to use the stack value */ return VM_INVALID_PROP; } else { /* simple '.prop' - return my property ID as a constant value */ return get_prop(); } } /* * add a runtime symbol table entry */ void CTcSymProp::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_propid(get_prop()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Enumerator symbol */ /* * evaluate the symbol */ void CTcSymEnum::gen_code(int discard) { if (!discard) { /* generate code to push the enum value */ G_cg->write_op(OPC_PUSHENUM); G_cs->write_enum_id(get_enum_id()); /* note the push */ G_cg->note_push(); } } /* * add a runtime symbol table entry */ void CTcSymEnum::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add our entry */ val.set_enum(get_enum_id()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * T3-specific local variable/parameter symbol class */ /* * generate code to evaluate the symbol */ void CTcSymLocal::gen_code(int discard) { /* generate code to push the local, if we're not discarding it */ if (!discard) { /* * generate as a context local if required, otherwise as an * ordinary local variable */ if (is_ctx_local_) { /* generate the context array lookup */ if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) { /* we can do this whole operation with one instruction */ G_cg->write_op(OPC_IDXLCL1INT8); G_cs->write((uchar)ctx_var_num_); G_cs->write((uchar)get_ctx_arr_idx()); /* this pushes one value */ G_cg->note_push(); } else { /* get our context array */ s_gen_code_getlcl(ctx_var_num_, FALSE); /* get our value from the context array */ CTPNConst::s_gen_code_int(get_ctx_arr_idx()); G_cg->write_op(OPC_INDEX); /* the INDEX operation removes two values and pushes one */ G_cg->note_pop(); } } else { /* generate as an ordinary local */ s_gen_code_getlcl(get_var_num(), is_param()); } } /* * Mark the value as referenced, whether or not we're generating the * code - the value has been logically referenced in the program * even if the result of evaluating it isn't needed. */ set_val_used(TRUE); } /* * generate code to push a local onto the stack */ void CTcSymLocal::s_gen_code_getlcl(int var_num, int is_param) { /* use the shortest form of the instruction that we can */ if (is_param && var_num < 4) { /* arguments 0-3 have dedicated opcodes */ G_cg->write_op(OPC_GETARGN0 + var_num); } else if (!is_param && var_num < 6) { /* locals 0-5 have dedicated opcodes */ G_cg->write_op(OPC_GETLCLN0 + var_num); } else if (var_num <= 255) { /* 8-bit local number - use the one-byte form */ G_cg->write_op(is_param ? OPC_GETARG1 : OPC_GETLCL1); G_cs->write((char)var_num); } else { /* local number won't fit in 8 bits - use the two-byte form */ G_cg->write_op(is_param ? OPC_GETARG2 : OPC_GETLCL2); G_cs->write2(var_num); } /* note the push */ G_cg->note_push(); } /* * assign a value */ int CTcSymLocal::gen_code_asi( int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **) { /* * if this is an explicit assignment, mark the variable as having had a * value assigned to it */ if (xplicit) set_val_assigned(TRUE); /* * if the assignment is anything but simple, this references the * value as well */ if (typ != TC_ASI_SIMPLE) set_val_used(TRUE); /* * If this is a context variable, use standard assignment (i.e., * generate the result first, then generate a simple assignment to the * variable). Otherwise, we might be able to generate a fancy * combined calculate-and-assign sequence, depending on the type of * assignment calculation we're performing. */ if (is_ctx_local_ && typ != TC_ASI_SIMPLE && phase == 1) { /* * It's a context local, and it's not a simple assignment, so we * can't perform any special calculate-and-assign sequence. For * phase 1, generate the value and tell the caller to calculate the * full result first and then try again using simple assignment. */ gen_code(FALSE); return FALSE; } /* * If this is phase 2 of a compound assignment, treat it the same as a * simple assignment. The RHS is on the stack, and we simply need to * complete the assignment to the local. */ if (phase == 2) typ = TC_ASI_SIMPLE; /* * check the type of assignment - we can optimize the code * generation to use more compact instruction sequences for certain * types of assignments */ int adding; switch(typ) { case TC_ASI_SIMPLE: /* * Simple assignment to local/parameter. Check for some special * cases: when assigning a constant value of 0, 1, or nil to a * local, we can generate a short instruction */ if (!is_param() && !is_ctx_local_ && rhs != 0 && rhs->is_const()) { CTcConstVal *cval; /* get the constant value */ cval = rhs->get_const_val(); /* check for nil and 0 or 1 values */ if (cval->get_type() == TC_CVT_NIL) { /* it's nil - generate NILLCL1 or NILLCL2 */ if (get_var_num() <= 255) { G_cg->write_op(OPC_NILLCL1); G_cs->write((char)get_var_num()); } else { G_cg->write_op(OPC_NILLCL2); G_cs->write2(get_var_num()); } /* if not discarding, leave nil on the stack */ if (!discard) { G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* handled */ return TRUE; } else if (cval->get_type() == TC_CVT_INT && (cval->get_val_int() == 0 || cval->get_val_int() == 1)) { int ival; /* get the integer value */ ival = cval->get_val_int(); /* 0 or 1 - generate ZEROLCLn or ONELCLn */ if (get_var_num() <= 255) { G_cg->write_op(ival == 0 ? OPC_ZEROLCL1 : OPC_ONELCL1); G_cs->write((char)get_var_num()); } else { G_cg->write_op(ival == 0 ? OPC_ZEROLCL2 : OPC_ONELCL2); G_cs->write2(get_var_num()); } /* if not discarding, leave the value on the stack */ if (!discard) { G_cg->write_op(ival == 0 ? OPC_PUSH_0 : OPC_PUSH_1); G_cg->note_push(); } /* handled */ return TRUE; } } /* * If we got here, we can't generate a specialized constant * assignment - so, first, generate the right-hand side's value * normally. (If no 'rhs' is specified, the value is already on * the stack.) */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* leave an extra copy of the value on the stack if not discarding */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* now assign the value at top of stack to the variable */ gen_code_setlcl(); /* handled */ return TRUE; case TC_ASI_ADD: adding = TRUE; goto add_or_sub; case TC_ASI_SUB: adding = FALSE; add_or_sub: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * Add/subtract to a local/parameter. If the right-hand side is a * constant integer value, we might be able to generate a special * instruction to add/subtract it. */ if (rhs != 0 && adding && rhs->is_const() && rhs->get_const_val()->get_type() == TC_CVT_INT) { long ival; /* get the integer value to assign */ ival = rhs->get_const_val()->get_val_int(); /* * if the right-hand side's integer value fits in one byte, * generate the short (8-bit) instruction; otherwise, * generate the long (32-bit) format */ if (ival == 1) { /* adding one - increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); } else if (ival == -1) { /* subtracting one - decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); } else if (ival <= 127 && ival >= -128 && get_var_num() <= 255) { /* fits in 8 bits - use the 8-bit format */ G_cg->write_op(OPC_ADDILCL1); G_cs->write((char)get_var_num()); G_cs->write((char)ival); } else { /* * either the value or the variable number doesn't fit * in 8 bits - use the 32-bit format */ G_cg->write_op(OPC_ADDILCL4); G_cs->write2(get_var_num()); G_cs->write4(ival); } } else { /* * We don't have a special instruction for the right side, * so generate it normally and add/subtract the value. (If * there's no 'rhs' value specified, it means that the value * is already on the stack, so there's nothing extra for us * to generate.) */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* write the ADDTOLCL instruction */ G_cg->write_op(adding ? OPC_ADDTOLCL : OPC_SUBFROMLCL); G_cs->write2(get_var_num()); /* addtolcl/subfromlcl remove the rvalue */ G_cg->note_pop(); } /* * if not discarding, push the result onto the stack; do this by * simply evaluating the local, which is the simplest and most * efficient way to obtain the result of the computation */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_PREINC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* generate code to increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); /* if we're not discarding, push the local's new value */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_POSTINC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * if we're not discarding, push the local's value prior to * incrementing it - this will be the result we'll leave on the * stack */ if (!discard) gen_code(FALSE); /* generate code to increment the local */ G_cg->write_op(OPC_INCLCL); G_cs->write2(get_var_num()); /* handled */ return TRUE; case TC_ASI_PREDEC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* generate code to decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); /* if we're not discarding, push the local's new value */ if (!discard) gen_code(FALSE); /* handled */ return TRUE; case TC_ASI_POSTDEC: /* if this is a parameter, there's nothing special we can do */ if (is_param()) { if (phase == 1) gen_code(FALSE); return FALSE; } /* * if we're not discarding, push the local's value prior to * decrementing it - this will be the result we'll leave on the * stack */ if (!discard) gen_code(FALSE); /* generate code to decrement the local */ G_cg->write_op(OPC_DECLCL); G_cs->write2(get_var_num()); /* handled */ return TRUE; default: /* * For other special assignment types, simply generate the value of * the local, and tell the caller to proceed with the unrolled * assignment. */ gen_code(FALSE); return FALSE; } } /* * generate code to assigin the value at top of stack to the local * variable */ void CTcSymLocal::gen_code_setlcl() { /* check to see if we're a context local (as opposed to a stack local) */ if (is_ctx_local_) { /* generate the assignment using the appropriate sequence */ if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) { /* we can fit this in a single instruction */ G_cg->write_op(OPC_SETINDLCL1I8); G_cs->write((uchar)ctx_var_num_); G_cs->write((uchar)get_ctx_arr_idx()); /* this pops the value being assigned */ G_cg->note_pop(); } else { /* get our context array */ s_gen_code_getlcl(ctx_var_num_, FALSE); /* set our value in the context array */ CTPNConst::s_gen_code_int(get_ctx_arr_idx()); G_cg->write_op(OPC_SETIND); G_cg->write_op(OPC_DISC); /* * the SETIND pops three values and pushes one (for a net two * pops), and the DISC pops one more value, so our total is * three pops */ G_cg->note_pop(3); } } else { /* we're just a plain stack variable */ gen_code_setlcl_stk(); } } /* * Generate code to store the value at the top of the stack into the given * local stack slot. Note that this routine will not work with a context * local - it only works if the variable is known to be a stack variable. */ void CTcSymLocal::s_gen_code_setlcl_stk(int var_num, int is_param) { /* use the shortest form that will fit our variable index */ if (var_num <= 255) { /* use the one-byte instruction */ G_cg->write_op(is_param ? OPC_SETARG1 : OPC_SETLCL1); G_cs->write((char)var_num); } else { /* big number - use the two-byte instruction */ G_cg->write_op(is_param ? OPC_SETARG2 : OPC_SETLCL2); G_cs->write2(var_num); } /* the setarg/setlcl ops remove the rvalue */ G_cg->note_pop(); } /* * call the symbol */ void CTcSymLocal::gen_code_call( int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* * to call a local, we'll simply evaluate the local normally, then * call through the resulting (presumed) property or function * pointer value */ gen_code(FALSE); /* * if we have a varargs list, modify the call instruction that * follows to make it a varargs call */ if (varargs) { /* swap the top of the stack to get the arg counter back on top */ G_cg->write_op(OPC_SWAP); /* write the varargs modifier */ G_cg->write_op(OPC_VARARGC); } /* don't allow this at all in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* call the result as a function or method pointer */ G_cg->write_op(OPC_PTRCALL); G_cs->write((char)argc); /* ptrcall removes the arguments and the function pointer */ G_cg->note_pop(argc + 1); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the value, push the result */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a property ID expression */ vm_prop_id_t CTcSymLocal::gen_code_propid(int check_only, int /*is_expr*/) { /* * treat the local as a property-valued expression; generate the * code for the local, then tell the caller that no constant value * is available, since the local's property ID value should be on * the stack */ if (!check_only) gen_code(FALSE); /* tell the caller to use the stack value */ return VM_INVALID_PROP; } /* * evaluate a member expression */ void CTcSymLocal::gen_code_member( int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* generate code to evaluate the local */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * write to a debug record */ int CTcSymLocal::write_to_debug_frame(int test_only) { /* if this is test-only mode, just note that we will write it */ if (test_only) return TRUE; /* * For version 2+ static compilation, write the symbol name * out-of-line, to the separate stream for local variable names. This * allows us to consolidate the names so that each name is stored only * once in the file. This saves a lot of space since there are a few * common local names that appear over and over. Version 1 always * wrote names in-line, and we also have to write the names in-line * when generating dynamic code, since we can't add to the constant * pool in this case. * * Also, store all 1- and 2-character names in-line. 1-character names * are smaller if stored in-line since they'll take only three bytes * (two bytes for the length, one byte for the string) vs four bytes * for a shared pool pointer. 2-char names are a wash on the frame * storage, but it's still more efficient to store them in-line because * we avoid also creating the constant pool entry for them. */ int inl = (len_ <= 2 || G_sizes.dbg_fmt_vsn < 2 || G_cg->is_eval_for_dyn()); /* * write my ID - if we're a context variable, we want to write the * context variable ID; otherwise write our stack location as normal */ if (is_ctx_local_) G_cs->write2(ctx_var_num_); else G_cs->write2(var_num_); /* compute my flags */ int flags = 0; if (is_param_) flags |= 0x0001; if (is_ctx_local_) flags |= 0x0002; if (!inl) flags |= 0x0004; /* write my flags */ G_cs->write2(flags); /* write my local context array index */ G_cs->write2(get_ctx_arr_idx()); /* add zeros to pad any future version information for the target VM */ for (int i = G_sizes.lcl_hdr - 6 ; i > 0 ; --i) G_cs->write(0); /* * write the name - either to the separate stream for local names, or * inline if necessary */ if (inl) { /* write the symbol name to the table in-line */ G_cs->write2(len_); G_cs->write(str_, len_); } else { /* add a fixup from the table to the local stream */ CTcStreamAnchor *anchor = G_lcl_stream->add_anchor(0, 0); CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, G_cs, G_cs->get_ofs()); /* add the placeholder to the table (this is what we fix up) */ G_cs->write4(0); /* write the name to the local stream */ G_lcl_stream->write2(len_); G_lcl_stream->write(str_, len_); } /* we did write this symbol */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Dynamic code local. This is a symbol for a local variable in an active * stack frame during dynamic compilation. */ /* * generate code to evaluate the variable's value */ void CTcSymDynLocal::gen_code(int discard) { /* there are no side effects, so do nothing if discarding the value */ if (discard) return; /* * to retrieve a dynamic frame local, we simply need to evaluate * fref[index], where 'fref' is the StackFrameRef object and 'index' is * an integer giving the frame index of the variable */ /* push the frame object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(fref_); G_cg->note_push(); /* push the variable's frame index */ CTPNConst::s_gen_code_int(varnum_); /* index the frame object */ G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one for a net of one pop */ G_cg->note_pop(1); /* * if this is a context local, we now have the context object, so we * need to further index it by the variable's context index */ if (ctxidx_ != 0) { /* index the context object by the context index */ CTPNConst::s_gen_code_int(ctxidx_); G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one */ G_cg->note_pop(1); } } /* * generate code to assign to the variable */ int CTcSymDynLocal::gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *, class CTcPrsNode *rhs, int, int, void **) { /* if this is a complex assignment, use the default two-phase process */ if (typ != TC_ASI_SIMPLE && phase == 1) { /* * generate our value, and tell the caller to calculate the * compound expression result and assign the value with a separate * call */ gen_code(FALSE); return FALSE; } /* if the rhs isn't already on the stack, generate it */ if (rhs != 0) rhs->gen_code(FALSE, FALSE); /* * if we're not disarding the rhs value, make an extra copy, as we'll * consume our copy in the performing the assignment */ if (!discard) { G_cg->write_op(OPC_DUP); G_cg->note_push(); } /* * to assign to a local variable, perform a set-index operation through * the frame object - that is, evaluate "fref[index] = value" */ /* push the frame object */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(fref_); G_cg->note_push(); /* push the variable's frame index */ CTPNConst::s_gen_code_int(varnum_); /* * if this is a context local, frame[index] yields the context object, * so we need to retrieve that before proceeding */ if (ctxidx_ != 0) { /* get frame[varnum] - that gives us the context object */ G_cg->write_op(OPC_INDEX); /* INDEX pops two items and pushes one for a net 1 pop */ G_cg->note_pop(); /* * now push the context index - the actual assignment will be * ctxobj[ctxidx] = rhs */ CTPNConst::s_gen_code_int(ctxidx_); } /* perform the assignment */ G_cg->write_op(OPC_SETIND); /* * Discard the new container value. This is the result of doing an * index-assign through the frame object, which is simply the original * frame object; even if it were changing, it's not something we'd need * to store anywhere since this is an internal indexing operation. */ G_cg->write_op(OPC_DISC); /* * the SETIND pops the rhs, index, and indexee, and pushes the * container, which we then discard, for a net pop of 3 */ G_cg->note_pop(3); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Built-in function symbol */ /* * Evaluate the symbol. Invoking a built-in function without an * argument list is simply a call to the built-in function with no * arguments. */ void CTcSymBif::gen_code(int discard) { /* generate a call */ gen_code_call(discard, 0, FALSE, 0); } /* * Generate code to call the built-in function */ void CTcSymBif::gen_code_call(int discard, int argc, int varargs, CTcNamedArgs *named_args) { /* don't allow calling built-in functions in speculative mode */ if (G_cg->is_speculative()) err_throw(VMERR_BAD_SPEC_EVAL); /* check for minimum and maximum arguments */ if (argc < min_argc_) { G_tok->log_error(TCERR_TOO_FEW_FUNC_ARGS, (int)get_sym_len(), get_sym()); } else if (!varargs_ && argc > max_argc_) { G_tok->log_error(TCERR_TOO_MANY_FUNC_ARGS, (int)get_sym_len(), get_sym()); } /* write the varargs modifier if appropriate */ if (varargs) G_cg->write_op(OPC_VARARGC); /* generate the call */ if (get_func_set_id() < 4 && get_func_idx() < 256) { uchar short_ops[] = { OPC_BUILTIN_A, OPC_BUILTIN_B, OPC_BUILTIN_C, OPC_BUILTIN_D }; /* * it's one of the first 256 functions in one of the first four * function sets - we can generate a short instruction */ G_cg->write_op(short_ops[get_func_set_id()]); G_cs->write((char)argc); G_cs->write((char)get_func_idx()); } else { /* it's not in the default set - use the longer instruction */ if (get_func_idx() < 256) { /* low function index - write the short form */ G_cg->write_op(OPC_BUILTIN1); G_cs->write((char)argc); G_cs->write((char)get_func_idx()); } else { /* big function index - write the long form */ G_cg->write_op(OPC_BUILTIN2); G_cs->write((char)argc); G_cs->write2(get_func_idx()); } /* write the function set ID */ G_cs->write((char)get_func_set_id()); } /* the built-in functions always remove arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* * if they're not discarding the value, push it - the value is * sitting in R0 after the call returns */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * take the address of the built-in function */ void CTcSymBif::gen_code_addr() { /* generate PUSHBIFPTR */ G_cg->write_op(OPC_PUSHBIFPTR); G_cs->write2(func_idx_); G_cs->write2(func_set_id_); /* this pushes one element */ G_cg->note_push(); } /* * add a runtime symbol table entry */ void CTcSymBif::add_runtime_symbol(CVmRuntimeSymbols *symtab) { vm_val_t val; /* add an entry for our absolute address */ val.set_bifptr(func_set_id_, func_idx_); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * External function symbol */ /* * evaluate the symbol */ void CTcSymExtfn::gen_code(int /*discard*/) { //$$$ to be implemented assert(FALSE); } /* * generate a call to the symbol */ void CTcSymExtfn::gen_code_call(int /*discard*/, int /*argc*/, int /*varargs*/, CTcNamedArgs * /*named_args*/) { //$$$ to be implemented assert(FALSE); } /* ------------------------------------------------------------------------ */ /* * Code Label symbol */ /* * evaluate the symbol */ void CTcSymLabel::gen_code(int discard) { /* it's not legal to evaluate a code label; log an error */ G_tok->log_error(TCERR_CANNOT_EVAL_LABEL, (int)get_sym_len(), get_sym()); } /* ------------------------------------------------------------------------ */ /* * Metaclass symbol */ /* * generate code for evaluating the symbol */ void CTcSymMetaclass::gen_code(int discard) { /* * mark it as referenced - if the metaclass wasn't defined in this file * but was merely imported from a symbol file, we now need to write it * to the object file anyway, since its class object ID has been * referenced */ ref_ = TRUE; /* * the metaclass name refers to the IntrinsicClass instance * associated with the metaclass */ G_cg->write_op(OPC_PUSHOBJ); G_cs->write_obj_id(class_obj_); /* note the push */ G_cg->note_push(); } /* * generate code for operator 'new' applied to the metaclass */ void CTcSymMetaclass::gen_code_new(int discard, int argc, int varargs, CTcNamedArgs *named_args, int is_transient) { /* if this is an external metaclass, we can't generate code */ if (ext_) { G_tok->log_error(TCERR_EXT_METACLASS, (int)get_sym_len(), get_sym()); return; } /* if we have varargs, write the modifier */ if (varargs) G_cg->write_op(OPC_VARARGC); if (meta_idx_ <= 255 && argc <= 255) { G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); G_cs->write((char)argc); G_cs->write((char)meta_idx_); } else { G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW2); G_cs->write2(argc); G_cs->write2(meta_idx_); } /* new1/new2 remove arguments */ G_cg->note_pop(argc); /* do post-call cleanup: named arg removal, etc */ G_cg->post_call_cleanup(named_args); /* if we're not discarding the value, push it */ if (!discard) { G_cg->write_op(OPC_GETR0); G_cg->note_push(); } } /* * generate a member expression */ void CTcSymMetaclass::gen_code_member(int discard, CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, CTcNamedArgs *named_args) { /* if this is an external metaclass, we can't generate code */ if (ext_) { G_tok->log_error(TCERR_EXT_METACLASS, (int)get_sym_len(), get_sym()); return; } /* generate code to push our class object onto the stack */ gen_code(FALSE); /* if we have an argument counter, put it back on top */ if (varargs) G_cg->write_op(OPC_SWAP); /* use the standard member generation */ CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* * add a runtime symbol table entry */ void CTcSymMetaclass::add_runtime_symbol(CVmRuntimeSymbols *symtab) { /* don't do this for external metaclasses */ if (ext_) return; /* add our entry */ vm_val_t val; val.set_obj(get_class_obj()); symtab->add_sym(get_sym(), get_sym_len(), &val); } /* ------------------------------------------------------------------------ */ /* * Exception Table */ /* * create */ CTcT3ExcTable::CTcT3ExcTable() { /* allocate an initial table */ exc_alloced_ = 1024; table_ = (CTcT3ExcEntry *)t3malloc(exc_alloced_ * sizeof(table_[0])); /* no entries are in use yet */ exc_used_ = 0; /* method offset is not yet known */ method_ofs_ = 0; } /* * add an entry to our table */ void CTcT3ExcTable::add_catch(ulong protected_start_ofs, ulong protected_end_ofs, ulong exc_obj_id, ulong catch_block_ofs) { CTcT3ExcEntry *entry; /* if necessary, expand our table */ if (exc_used_ == exc_alloced_) { /* expand the table a bit */ exc_alloced_ += 1024; /* reallocate the table at the larger size */ table_ = (CTcT3ExcEntry *) t3realloc(table_, exc_alloced_ * sizeof(table_[0])); } /* * set up the new entry - store the offsets relative to the method * header start address */ entry = table_ + exc_used_; entry->start_ofs = protected_start_ofs - method_ofs_; entry->end_ofs = protected_end_ofs - method_ofs_; entry->exc_obj_id = exc_obj_id; entry->catch_ofs = catch_block_ofs - method_ofs_; /* consume the new entry */ ++exc_used_; } /* * write our exception table to the code stream */ void CTcT3ExcTable::write_to_code_stream() { CTcT3ExcEntry *entry; size_t i; /* write the number of entries as a UINT2 */ G_cs->write2(exc_used_); /* write the entries */ for (i = 0, entry = table_ ; i < exc_used_ ; ++i, ++entry) { /* write this entry */ G_cs->write2(entry->start_ofs); G_cs->write2(entry->end_ofs); G_cs->write_obj_id(entry->exc_obj_id); G_cs->write2(entry->catch_ofs); /* pad any excess beyond the v1 size with zero bytes */ for (int j = G_sizes.exc_entry - 10 ; j > 0 ; --j) G_cs->write(0); } } /* ------------------------------------------------------------------------ */ /* * Parameter default value enumeration context. */ struct defval_ctx { defval_ctx() { nv = 0; nvalo = 128; v = new CTcSymLocal *[nvalo]; } ~defval_ctx() { delete [] v; } void add(CTcSymLocal *l) { /* if there's not room, expand the array */ if (nv == nvalo) { CTcSymLocal **vnew = new CTcSymLocal*[nvalo + 128]; memcpy(vnew, v, nvalo * sizeof(*vnew)); delete [] v; v = vnew; nvalo += 128; } /* add it */ v[nv++] = l; } /* sort the list in ascending order of sequence number */ void sort() { qsort(v, nv, sizeof(*v), &compare); } static int compare(const void *a0, const void *b0) { const CTcSymLocal *a = *(const CTcSymLocal **)a0; const CTcSymLocal *b = *(const CTcSymLocal **)b0; return a->get_defval_seqno() - b->get_defval_seqno(); } /* generate code to initialize an optional positional parameter */ static void gen_opt_positional(CTcSymLocal *lcl) { /* * The code we want to generate is this, in pseudo code (where 'n' * is our position among the arguments): * *. if (n <= argcount) *. lcl = getArg(n) *. else *. lcl = default_expression; * * The 'else' applies only if there's a default expression with the * local; otherwise we simply leave it with the nil initial value. */ /* if n > argcount... */ CTPNConst::s_gen_code_int(lcl->get_param_index() + 1); G_cg->write_op(OPC_GETARGC); G_cg->note_push(); /* ...jump to the 'not found' branch... */ CTcCodeLabel *nf_lbl = G_cs->new_label_fwd(); G_cg->write_op(OPC_JGT); G_cs->write_ofs2(nf_lbl, 0); G_cg->note_pop(2); /* getArg(n) */ CTPNConst::s_gen_code_int(lcl->get_param_index() + 1); call_internal_bif("getArg", TCERR_OPT_PARAM_MISSING_FUNC, 1); /* if there's a default value, generate it */ if (lcl->get_defval_expr() != 0) { /* we're done with the 'then' branch - jump to the assignment */ CTcCodeLabel *asi_lbl = G_cs->new_label_fwd(); G_cg->write_op(OPC_JMP); G_cs->write_ofs2(asi_lbl, 0); /* define the 'not found' label - come here to set the default */ CTcPrsNode::def_label_pos(nf_lbl); /* generate the default expression */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); /* we're ready to do the assignment */ CTcPrsNode::def_label_pos(asi_lbl); /* * since we're sharing the assignment (coming up next) with the * main branch, we need to account for the extra push on this * branch */ G_cg->note_pop(); } /* * Assign the value we decided upon to the local. Don't count this * as an explicit assignment of a value for warning purposes; this * is effectively the same kind of assignment as the kind that * binds an actual argument to this formal parameter, so it * shouldn't count as any more explicit an assignment than that * does. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); /* * if there was no default value, the 'not found' branch jumps * here, past the assignment, since we have no value to assign */ if (lcl->get_defval_expr() == 0) CTcPrsNode::def_label_pos(nf_lbl); } /* array of local variables with default values */ CTcSymLocal **v; /* allocated size of 'v' array */ int nvalo; /* number of entries in 'v' array */ int nv; }; /* ------------------------------------------------------------------------ */ /* * Code body */ /* * generate code */ void CTPNCodeBody::gen_code(int, int) { /* if I've been replaced, don't bother generating any code */ if (replaced_) return; /* * Open the method header. * * Generate to the static stream if this is a static initializer * method, otherwise to the main stream. * * Anchor the fixups in the associated symbol table entry, if any. We * maintain our own fixup list if we don't have a symbol, otherwise we * use the one from our symbol table entry - in either case, we have to * keep track of it ourselves, because a code body might be reachable * through multiple references (a function, for example, has a global * symbol table entry - fixups referencing us might already have been * created by the time we generate our code). */ tct3_method_gen_ctx gen_ctx; G_cg->open_method( is_static_ ? G_cs_static : G_cs_main, fixup_owner_sym_, fixup_list_anchor_, this, gototab_, argc_, opt_argc_, varargs_, is_constructor_, op_overload_, self_valid_, &gen_ctx); /* * Add a line record at the start of the method for all of the * generated method prolog code. Some prolog code can throw errors, so * we want a line record at the start of the code body to indicate the * location of any such errors. */ if (start_desc_ != 0) G_cs->add_line_rec(start_desc_, start_linenum_); /* * Add each local symbol table enclosing the code body's primary * local symbol table to the frame list. The outermost code body * table can be outside the primary code body table for situations * such as anonymous functions. Since these tables are outside of * any statements, we must explicitly add them to ensure that they * are assigned debugging frame ID's and are written to the debug * data. */ if (lcltab_ != 0) { /* add each frame outside the primary frame to the code gen list */ for (CTcPrsSymtab *tab = lcltab_->get_parent() ; tab != 0 ; tab = tab->get_parent()) G_cs->set_local_frame(tab); } /* the method's local symbol table is now the active symbol table */ G_cs->set_local_frame(lcltab_); /* if we have a local context, initialize it */ if (has_local_ctx_) { /* write code to create the new Vector to store the context locals */ CTPNConst::s_gen_code_int(local_ctx_arr_size_); G_cg->write_op(OPC_DUP); G_cg->write_op(OPC_NEW1); G_cs->write(2); G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_VECTOR)); /* retrieve the object value */ G_cg->write_op(OPC_GETR0); /* * we duplicated the vector size argument, then we popped it and * pushed the object; so we have a maximum of one extra push and a * net of zero */ G_cg->note_push(); G_cg->note_pop(); /* store the new object in the context local variable */ CTcSymLocal::s_gen_code_setlcl_stk(local_ctx_var_, FALSE); /* * go through our symbol table, and copy each parameter that's * also a context local into its context local slot */ if (lcltab_ != 0) lcltab_->enum_entries(&enum_for_param_ctx, this); } /* * If we have a varargs-list parameter, generate the code to set up * the list value from the actual parameters. Note that we must do * this after we set up the local context, in case the varargs list * parameter variable is a context local, in which case it will need * to be stored in the context, in which case we need the context to * be initialized first. */ if (varargs_list_) { /* generate the PUSHPARLST instruction to create the list */ G_cg->write_op(OPC_PUSHPARLST); G_cs->write((uchar)argc_); /* * we pushed at least one value (the list); we don't know how many * others we might have pushed, but it doesn't matter because the * interpreter is responsible for checking for stack space */ G_cg->note_push(); /* store the list in our varargs parameter list local */ varargs_list_local_->gen_code_setlcl(); } /* * generate other special parameter setup code: named arguments and * default values */ if (lcltab_ != 0) { /* * generate bindings for named parameters and optional positional * parameters that don't have default expressions, and build the * array of optional parameters with default expressions */ defval_ctx dctx; lcltab_->enum_entries(&enum_for_named_params, &dctx); /* sort the default value list in left-to-right parameter order */ dctx.sort(); /* generate the default value assignments */ for (int i = 0 ; i < dctx.nv ; ++i) { /* get this local */ CTcSymLocal *lcl = dctx.v[i]; /* generate the proper code depending on the parameter type */ if (lcl->is_named_param()) { /* * Named parameter with a default. * * If the default is a constant, call t3GetNamedArg(name, * defVal). This returns the argument value, or the * default value if the argument isn't defined. * * If the default value expression isn't a constant, we * can't evaluate it until we know whether or not the * argument is defined, since the expression could have * side effects that we don't want to trigger unless we * really need to evaluate the expression. So in this * case, we have to look up the argument first, and set the * default only the argument doesn't exist. * * Note that we could handle both cases with the * non-constant handling, since it would make no difference * semantically to defer evaluating a constant until after * checking to see if the argument exists. However, the * separate version for constants yields faster and smaller * byte code, so we use it as an optimization for a common * case. */ if (lcl->get_defval_expr()->is_const()) { /* constant default - use t3GetNamedArg(name, defval) */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); CTPNConst::s_gen_code_str_by_mode(lcl); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, 2); } else { /* * Non-constant default value. We don't want to * evaluate the argument unless necessary in this case, * so start by calling t3GetNamedArg(name). This will * throw an error if the argument doesn't exist, so do * this in an implied try/catch block. If we return * without an error, simply proceed with the value * returned. If an error is thrown, catch the error * and *then* evaluate the default value. */ ulong start_ofs = G_cs->get_ofs(); CTPNConst::s_gen_code_str_by_mode(lcl); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, 1); /* that's the end of the implied 'try' section */ ulong end_ofs = G_cs->get_ofs() - 1; /* * On a successful return, the result value will be at * top of stack, so just jump past the evaluation of * the default value. Since we're done with this * branch, clear what it left on the stack, since the * 'catch' branch will leave the same thing on the * stack in parallel. */ CTcCodeLabel *asi_lbl = gen_jump_ahead(OPC_JMP); G_cg->note_pop(); /* this is the start of our implied 'catch' block */ G_cg->get_exc_table()->add_catch( start_ofs, end_ofs, VM_INVALID_OBJ, G_cs->get_ofs()); G_cg->note_push(); /* discard the exception */ G_cg->write_op(OPC_DISC); G_cg->note_pop(); /* generate the default value expression */ lcl->get_defval_expr()->gen_code(FALSE, FALSE); /* this is the common 'assign' branch point */ def_label_pos(asi_lbl); } /* assign the top-of-stack value to the local */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); } else { /* it's a positional parameter - generate it */ dctx.gen_opt_positional(lcl); } } } /* * If this is a dynamic function (a DynamicFunc object), and it's * defined with the 'function' keyword (not 'method'), and it has a * 'self', we need to set up the method context to point to the * enclosing frame object's method context. */ if (G_cg->is_eval_for_dyn() && is_dyn_func_ && self_valid_ && (self_referenced_ || full_method_ctx_referenced_)) { /* * We need the method context. For dyanmic code, the invokee is * the DynamicFunc, and the DynamicFunc makes the context available * via its indexed value [1]. */ G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); CTPNConst::s_gen_code_int(1); G_cg->write_op(OPC_INDEX); /* we pushed the invokee and popped it again with the INDEX */ G_cg->note_push(); G_cg->note_pop(); /* * The invokee will store the appropriate object in its [1] * element, according to which type of method context we need. * Generate the appropriate 'set' for it. */ if (full_method_ctx_referenced_) { /* load the full method context */ G_cg->write_op(OPC_LOADCTX); } else { /* load the 'self' object */ G_cg->write_op(OPC_SETSELF); } /* that pops the context object */ G_cg->note_pop(); } /* * Generate code to initialize each enclosing-context-pointer local - * these variables allow us to find the context objects while we're * running inside this function. * * Before 3.1, we had to generate context level 1 last, because this * level sets 'self' for an anonymous function to the saved 'self' from * the context. Starting with 3.1, the stack frame has a separate * entry for the invokee, so we no longer conflate the anonymous * function object with 'self'. */ CTcCodeBodyCtx *cur_ctx; int ctx_idx; for (ctx_idx = 0, cur_ctx = ctx_head_ ; cur_ctx != 0 ; cur_ctx = cur_ctx->nxt_, ++ctx_idx) { /* * Get this context value, stored in invokee[n+2]. Note that the * context object indices start at 2 because the FUNCPTR value for * the code itself is at index 1. */ G_cg->write_op(OPC_PUSHCTXELE); G_cs->write(PUSHCTXELE_INVOKEE); CTPNConst::s_gen_code_int(ctx_idx + 2); G_cg->write_op(OPC_INDEX); /* * we pushed the object, then popped the object and index and * pushed the indexed value - this is a net of no change with one * maximum push */ G_cg->note_push(); G_cg->note_pop(); /* * If this is context level 1, and this context has a 'self', and * we need either 'self' or the full method context from the * lexically enclosing scope, generate code to load the self or the * full method context (as appropriate) from our local context. * * The enclosing method context is always stored in the context at * level 1, because this is inherently shared context for all * enclosed lexical scopes. We thus only have to worry about this * for context level 1. */ if (cur_ctx->level_ == 1 && !is_anon_method_ && self_valid_ && (self_referenced_ || full_method_ctx_referenced_)) { CTPNCodeBody *outer; /* * we just put our context object on the stack in preparation * for storing it - make a duplicate copy of it for our own * purposes */ G_cg->write_op(OPC_DUP); G_cg->note_push(); /* get the saved method context from the context object */ CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); G_cg->write_op(OPC_INDEX); /* * Load the context. We must check the outermost context to * determine what it stored, because we must load whatever it * stored. */ if ((outer = get_outermost_enclosing()) != 0 && outer->local_ctx_needs_full_method_ctx()) { /* load the full method context */ G_cg->write_op(OPC_LOADCTX); } else { /* load the 'self' object */ G_cg->write_op(OPC_SETSELF); } /* * we popped two values and pushed one in the INDEX, then * popped a value in the LOADCTX or SETSELF: the net is removal * of two elements and no additional maximum depth */ G_cg->note_pop(2); } /* store the context value in the appropriate local variable */ CTcSymLocal::s_gen_code_setlcl_stk(cur_ctx->var_num_, FALSE); } /* * if we created our own local context, and we have a 'self' object, * and we need access to the 'self' object or the full method context * from anonymous functions that refer to the local context, generate * code to store the appropriate data in the local context */ if (has_local_ctx_ && self_valid_ && (local_ctx_needs_self_ || local_ctx_needs_full_method_ctx_)) { /* * Check to see what we need. If we're marked as needing the full * method context, OR our outermost enclosing context has the full * context, we need to generate the full context. Intermediate * nesting levels of anonymous functions can need less context than * inner levels, but the context we actually generate at the outer * level depends on the innermost function's needs. So we simply * need to follow the lead of the outermost level, since that's * what the inner levels will all do. */ CTPNCodeBody *outer = get_outermost_enclosing(); if (local_ctx_needs_full_method_ctx_ || (outer != 0 && outer->local_ctx_needs_full_method_ctx())) { /* * we need the full method context - generate code to store it * and push a reference to it onto the stack */ G_cg->write_op(OPC_STORECTX); } else { /* we only need 'self' - push it */ G_cg->write_op(OPC_PUSHSELF); } /* we just pushed one value */ G_cg->note_push(); /* assign the value to the context variable */ if (local_ctx_var_ <= 255 && TCPRS_LOCAL_CTX_METHODCTX <= 255) { /* we can make the assignment with a single instruction */ G_cg->write_op(OPC_SETINDLCL1I8); G_cs->write((uchar)local_ctx_var_); G_cs->write(TCPRS_LOCAL_CTX_METHODCTX); /* that pops one value */ G_cg->note_pop(); } else { /* get the context object */ CTcSymLocal::s_gen_code_getlcl(local_ctx_var_, FALSE); /* store the data in the local context object */ CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); G_cg->write_op(OPC_SETIND); /* discard the indexed result */ G_cg->write_op(OPC_DISC); /* * the SETIND pops three values and pushes one, then we pop one * more with the DISC - this is a net three pops with no extra * maximum depth */ G_cg->note_pop(3); } } /* generate the compound statement, if we have one */ if (stm_ != 0) stm_->gen_code(TRUE, TRUE); #ifdef T3_DEBUG if (G_cg->get_sp_depth() != 0) { printf("---> stack depth is %d after block codegen (line %ld)!\n", G_cg->get_sp_depth(), end_linenum_); if (fixup_owner_sym_ != 0) printf("---> code block for %.*s\n", (int)fixup_owner_sym_->get_sym_len(), fixup_owner_sym_->get_sym()); } #endif /* close the method */ G_cg->close_method(local_cnt_, lcltab_, end_desc_, end_linenum_, &gen_ctx, named_arg_tables_); /* remember the head of the nested symbol table list */ first_nested_symtab_ = G_cs->get_first_frame(); /* * Generate debug records. If we're in debug mode, include the full * symbolic debugging information, including source line locations. * For release builds, include only the local variable symbols and * frame information; we need those even in release builds, for * reflection purposes. * * If we're generating this code for a debugger evaluation, omit * symbols. We don't need reflection support for debugger expressions, * since there doesn't seem to be any practical reason you'd need it * there. */ if (!G_cg->is_eval_for_debug()) build_debug_table(gen_ctx.method_ofs, G_debug); /* check for unreferenced labels and issue warnings */ check_unreferenced_labels(); /* show the disassembly of the code block if desired */ if (G_disasm_out != 0) show_disassembly(gen_ctx.method_ofs, gen_ctx.code_start_ofs, gen_ctx.code_end_ofs); /* clean up globals for the end of the method */ G_cg->close_method_cleanup(&gen_ctx); } /* * disassembly stream source implementation */ class CTcUnasSrcCodeBody: public CTcUnasSrc { public: CTcUnasSrcCodeBody(CTcCodeStream *str, unsigned long code_start_ofs, unsigned long code_end_ofs) { /* remember the stream */ str_ = str; /* start at the starting offset */ cur_ofs_ = code_start_ofs; /* remember the ending offset */ end_ofs_ = code_end_ofs; } /* read the next byte */ int next_byte(char *ch) { /* if there's anything left, return it */ if (cur_ofs_ < end_ofs_) { /* return the next byte */ *ch = str_->get_byte_at(cur_ofs_); ++cur_ofs_; return 0; } else { /* indicate end of file */ return 1; } } /* get the current offset */ ulong get_ofs() const { return cur_ofs_; } protected: /* code stream */ CTcCodeStream *str_; /* current offset */ unsigned long cur_ofs_; /* offset of end of code stream */ unsigned long end_ofs_; }; /* * Show the disassembly of this code block */ void CTPNCodeBody::show_disassembly(unsigned long start_ofs, unsigned long code_start_ofs, unsigned long code_end_ofs) { int argc; int locals; int total_stk; unsigned exc_rel; unsigned dbg_rel; /* first, dump the header */ argc = (unsigned char)G_cs->get_byte_at(start_ofs); locals = G_cs->readu2_at(start_ofs + 2); total_stk = G_cs->readu2_at(start_ofs + 4); exc_rel = G_cs->readu2_at(start_ofs + 6); dbg_rel = G_cs->readu2_at(start_ofs + 8); G_disasm_out->print("%8lx .code\n", start_ofs); G_disasm_out->print(" .argcount %d%s\n", (argc & 0x7f), (argc & 0x80) != 0 ? "+" : ""); G_disasm_out->print(" .locals %d\n", locals); G_disasm_out->print(" .maxstack %d\n", total_stk); /* set up a code stream reader and dump the code stream */ CTcUnasSrcCodeBody src(G_cs, code_start_ofs, code_end_ofs); CTcT3Unasm::disasm(&src, G_disasm_out); /* show the exception table, if there is one */ if (exc_rel != 0) { unsigned long exc_ofs; unsigned long exc_end_ofs; /* get the starting address */ exc_ofs = start_ofs + exc_rel; /* * get the length - it's the part up to the debug records, or the * part up to the current code offset if there are no debug records */ exc_end_ofs = (dbg_rel != 0 ? start_ofs + dbg_rel : G_cs->get_ofs()); /* show the table */ G_disasm_out->print(".exceptions\n"); CTcUnasSrcCodeBody exc_src(G_cs, exc_ofs, exc_end_ofs); CTcT3Unasm::show_exc_table(&exc_src, G_disasm_out, start_ofs); } /* add a blank line at the end */ G_disasm_out->print("\n"); } /* * Check for unreferenced local variables */ void CTPNCodeBody::check_locals() { CTcPrsSymtab *tab; /* check for unreferenced locals in each nested scope */ for (tab = first_nested_symtab_ ; tab != 0 ; tab = tab->get_list_next()) { /* check this table */ tab->check_unreferenced_locals(); } } /* * local symbol table enumerator for checking for parameter symbols that * belong in the local context */ void CTPNCodeBody::enum_for_param_ctx(void *, class CTcSymbol *sym) { /* if this is a local, check it further */ if (sym->get_type() == TC_SYM_LOCAL || sym->get_type() == TC_SYM_PARAM) { CTcSymLocal *lcl = (CTcSymLocal *)sym; /* * if it's a parameter, and it's also a context variable, its * value needs to be moved into the context */ if (lcl->is_param() && lcl->is_ctx_local()) { /* get the actual parameter value from the stack */ CTcSymLocal::s_gen_code_getlcl(lcl->get_var_num(), TRUE); /* * Store the value in the context variable. This is an implied * assignment equivalent to binding a formal to its actual * value. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, TRUE, FALSE, 0); } } } /* * local symbol table enumerator for generating named argument bindings */ void CTPNCodeBody::enum_for_named_params(void *ctx, class CTcSymbol *sym) { /* * If this is a local, and it's a named parameter, generate code to * bind it. If it's optional and has a default value expression, skip * it on this pass, since we need to generate default expressions in * the order in which they appear in the argument list to ensure that * dependencies are resolved properly and side effects happen in the * proper order. */ if (sym->get_type() == TC_SYM_LOCAL) { /* get the symbol, properly cast */ CTcSymLocal *lcl = (CTcSymLocal *)sym; /* check what we have */ if (lcl->get_defval_expr() != 0) { /* * It's an optional parameter (positional or named) with a * default value. We can't generate the binding yet, since we * need to generate these in left-to-right order, and the table * enumeration is in hash-table order instead. So just add it * to the context list, which we'll sort into the proper * left-right sequence later. */ ((defval_ctx *)ctx)->add(lcl); } else if (lcl->is_named_param()) { /* * It's a named parameter - generate the binding code. If it's * optional, call t3GetNamedArg(name, nil) to use the default * nil value if it's not defined. If it's not optional, use * t3GetNamedArg(name) to throw an error on failure. */ int fargc = 1; if (lcl->is_opt_param()) { /* optional parameter - call t3GetNamedArg(argname, nil) */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); ++fargc; } /* push the parameter name and call the appropriate builtin */ CTPNConst::s_gen_code_str_by_mode(sym); call_internal_bif("t3GetNamedArg", TCERR_NAMED_PARAM_MISSING_FUNC, fargc); /* * Assign the result to the local. This is an implied * assignment equivalent to binding an actual argument value to * this formal. */ lcl->gen_code_asi(TRUE, 1, TC_ASI_SIMPLE, "=", 0, FALSE, FALSE, 0); } else if (lcl->is_opt_param()) { /* * It's an optional parameter without a default value (we know * there's no default, since we handled that case above). * Generate code to load it if present. */ defval_ctx::gen_opt_positional(lcl); } } } /* * Add a named argument call table to the list. This is invoked each time * we generate a call that uses named parameters. We defer generation of * the argument name tables until the end of the method, to avoid having to * jump past inline tables while executing opcodes. */ CTcCodeLabel *CTPNCodeBody::add_named_arg_tab(const CTcNamedArgs *named_args) { /* allocate the tracking object */ CTcNamedArgTab *tab = new (G_prsmem) CTcNamedArgTab(named_args); /* link it into our list */ tab->nxt = named_arg_tables_; named_arg_tables_ = tab; /* return the code label for the table */ return tab->lbl; } /* ------------------------------------------------------------------------ */ /* * Debugger records and local symbol table */ /* local symbol enumeration callback context */ struct write_local_to_debug_frame_ctx { /* are we just counting the entries? */ int count_only; /* number of symbols written so far */ int count; }; /* * Callback for symbol table enumeration - write a local variable entry to * the code stream for a debug frame record. */ void CTPNCodeBody::write_local_to_debug_frame(void *ctx0, CTcSymbol *sym) { write_local_to_debug_frame_ctx *ctx; /* cast our context */ ctx = (write_local_to_debug_frame_ctx *)ctx0; /* write it out */ if (sym->write_to_debug_frame(ctx->count_only)) { /* we wrote the symbol - count it */ ++(ctx->count); } } /* * Build the debug information table for a code body */ void CTPNCodeBody::build_debug_table(ulong start_ofs, int include_lines) { size_t i; /* * Count up the frames that contain local variables. Frames that * contain no locals can be elided to reduce the size of the file, * since they carry no information. */ CTcPrsSymtab *frame; int frame_cnt; for (frame_cnt = 0, frame = G_cs->get_first_frame() ; frame != 0 ; frame = frame->get_list_next()) { /* enumerate the entries through our counting callback */ write_local_to_debug_frame_ctx cbctx; cbctx.count = 0; cbctx.count_only = TRUE; frame->enum_entries(&write_local_to_debug_frame, &cbctx); /* check to see if it has any locals */ if (cbctx.count != 0) { /* it has locals - renumber it to the current list position */ ++frame_cnt; frame->set_list_index(frame_cnt); } else { /* no locals - flag it as empty by setting its list index to 0 */ frame->set_list_index(0); } } /* * If we're not writing line records, and we don't have any frames with * local variables, skip writing the debug table. The debug table in * this case is for local variable reflection information only, so if * we don't have any locals, we don't need the table at all. */ if (!include_lines && frame_cnt == 0) return; /* fix up the debug record offset in the prolog to point here */ G_cs->write2_at(start_ofs + 8, G_cs->get_ofs() - start_ofs); /* * Write the debug table header. In the current VM version, this is * empty, so we have nothing to write. However, if we're dynamically * targeting a newer version, pad out any expected header space with * zeros. */ for (int j = G_sizes.dbg_hdr ; j > 0 ; --j) G_cs->write(0); /* * Add this offset to our list of line records. If we're creating an * object file, upon re-loading the object file, we'll need to go * through all of the line record tables and fix up the file references * to the final file numbering system at link time, so we need this * memory of where the line record are. We'll also need to fix up the * local variable name records to point to the consolidated set of * strings in the constant pool. */ G_cg->add_debug_line_table(G_cs->get_ofs()); /* if desired, write the source code location ("line") records */ if (include_lines) { /* write the number of line records */ G_cs->write2(G_cs->get_line_rec_count()); /* write the line records themselves */ for (i = 0 ; i < G_cs->get_line_rec_count() ; ++i) { /* get this record */ tcgen_line_t *rec = G_cs->get_line_rec(i); /* write the offset of the statement's first opcode */ G_cs->write2(rec->ofs); /* write the source file ID and line number */ G_cs->write2(rec->source_id); G_cs->write4(rec->source_line); /* * Get the frame for the line record. If the frame is empty, * use the parent frame instead, since we omit empty frames. */ CTcPrsSymtab *fr = rec->frame; while (fr != 0 && fr->get_list_index() == 0) fr = fr->get_parent(); /* write the frame ID */ G_cs->write2(fr == 0 ? 0 : fr->get_list_index()); /* add any padding expected in the target VM version */ for (int j = G_sizes.dbg_line - 10 ; j > 0 ; --j) G_cs->write(0); } } else { /* no line records - write zero as the line count */ G_cs->write2(0); } /* * write a placeholder pointer to the next byte after the end of the * frame table */ ulong post_ptr_ofs = G_cs->get_ofs(); G_cs->write2(0); /* write the frame count */ G_cs->write2(frame_cnt); /* * Write a placeholder frame index table. We will come back and fix up * this table as we actually write out the frames, but we don't * actually know how big the individual frame records will be yet, so * we can only write placeholders for them for now. First, note where * the frame index table begins. */ ulong index_ofs = G_cs->get_ofs(); /* write the placeholder index entries */ for (i = 0 ; i < (size_t)frame_cnt ; ++i) G_cs->write2(0); /* write the individual frames */ for (frame = G_cs->get_first_frame() ; frame != 0 ; frame = frame->get_list_next()) { /* * if this frame has a zero list index, it means that it contains * no writable symbols, so we can omit it from the debug table */ if (frame->get_list_index() == 0) continue; /* * go back and fill in the correct offset (from the entry itself) * in the index table entry for this frame */ G_cs->write2_at(index_ofs, G_cs->get_ofs() - index_ofs); /* move on to the next index entry */ index_ofs += 2; /* * Get the effective parent. If the actual parent doesn't have a * list index, it's empty, so we're not writing it. Find the * nearest parent of the parent that we'll actually write, and use * that as our effective parent. */ CTcPrsSymtab *par; for (par = frame->get_parent() ; par != 0 && par->get_list_index() == 0 ; par = par->get_parent()) ; /* write the ID of the enclosing frame */ G_cs->write2(par != 0 ? par->get_list_index() : 0); /* * write a placeholder for the count of the number of entries in * the frame, and remember where the placeholder is so we can come * back and fix it up later */ ulong count_ofs = G_cs->get_ofs(); G_cs->write2(0); /* add the bytecode range covered, if there's room in the format */ if (G_sizes.dbg_frame >= 8) { G_cs->write2(frame->get_start_ofs()); G_cs->write2(frame->get_end_ofs()); } /* pad out any extra space */ for (int j = G_sizes.dbg_frame - 8 ; j > 0 ; --j) G_cs->write(0); /* initialize the enumeration callback context */ write_local_to_debug_frame_ctx cbctx; cbctx.count = 0; cbctx.count_only = FALSE; /* write this frame table's entries */ frame->enum_entries(&write_local_to_debug_frame, &cbctx); /* go back and fix up the symbol count */ G_cs->write2_at(count_ofs, cbctx.count); } /* * go back and fill in the post-pointer offset - this is a pointer to * the next byte after the end of the frame table; write the offset * from the post-pointer field to the current location */ G_cs->write2_at(post_ptr_ofs, G_cs->get_ofs() - post_ptr_ofs); /* * write the required UINT4 zero value after the frame table - this * is a placeholder for future expansion (if we add more information * to the debug table later, this value will be non-zero to indicate * the presence of the additional information) */ G_cs->write4(0); } /* ------------------------------------------------------------------------ */ /* * 'return' statement */ /* * generate code */ void CTPNStmReturn::gen_code(int, int) { int val_on_stack; int need_gen; /* add a line record */ add_debug_line_rec(); /* presume we'll generate a value */ need_gen = TRUE; val_on_stack = FALSE; /* generate the return value expression, if appropriate */ if (expr_ != 0) { /* * it's an error if we're in a constructor, because a * constructor implicitly always returns 'self' */ if (G_cg->is_in_constructor()) log_error(TCERR_CONSTRUCT_CANNOT_RET_VAL); /* check for a constant expression */ if (expr_->is_const()) { switch(expr_->get_const_val()->get_type()) { case TC_CVT_NIL: case TC_CVT_TRUE: /* * we can use special constant return instructions for * these, so there's no need to generate the value */ need_gen = FALSE; break; default: /* * other types don't have constant-return opcodes, so we * must generate the expression code */ need_gen = TRUE; break; } } /* if necessary, generate the value */ if (need_gen) { int depth; /* note the initial stack depth */ depth = G_cg->get_sp_depth(); /* * Generate the value. We are obviously not discarding the * value, and since returning a value is equivalent to * assigning the value, we must use the stricter assignment * (not 'for condition') rules for logical expressions */ expr_->gen_code(FALSE, FALSE); /* note whether we actually left a value on the stack */ val_on_stack = (G_cg->get_sp_depth() > depth); } else { /* * we obviously aren't leaving a value on the stack if we * don't generate anything */ val_on_stack = FALSE; } } /* * Before we return, let any enclosing statements generate any code * necessary to leave their scope (in particular, we must invoke * 'finally' handlers in any enclosing 'try' blocks). * * Note that we generated the expression BEFORE we call any * 'finally' handlers. This is necessary because something we call * in the course of evaluating the return value could have thrown an * exception; if we were to call the 'finally' clauses before * generating the return value, we could invoke the 'finally' clause * twice (once explicitly, once in the handling of the thrown * exception), which would be incorrect. By generating the * 'finally' calls after the return expression, we're sure that the * 'finally' blocks are invoked only once - either through the * throw, or else now, after there's no more possibility of a * 'throw' before the return. */ if (G_cs->get_enclosing() != 0) { int did_save_retval; uint fin_ret_lcl; /* * if we're going to generate any subroutine calls, and we have * a return value on the stack, we need to save the return value * in a local to make sure the calculated value isn't affected * by the subroutine call */ if (val_on_stack && G_cs->get_enclosing()->will_gen_code_unwind_for_return() && G_cs->get_code_body() != 0) { /* allocate a local variable to save the return value */ fin_ret_lcl = G_cs->get_code_body()->alloc_fin_ret_lcl(); /* save the return value in a stack temporary for a moment */ CTcSymLocal::s_gen_code_setlcl_stk(fin_ret_lcl, FALSE); /* * note that we saved the return value, so we can retrieve * it later */ did_save_retval = TRUE; } else { /* note that we didn't save the return value */ did_save_retval = FALSE; } /* generate the unwind */ G_cs->get_enclosing()->gen_code_unwind_for_return(); /* if we saved the return value, retrieve it */ if (did_save_retval) CTcSymLocal::s_gen_code_getlcl(fin_ret_lcl, FALSE); } /* check for an expression to return */ if (G_cg->is_in_constructor()) { /* we're in a constructor - return 'self' */ G_cg->write_op(OPC_PUSHSELF); G_cg->write_op(OPC_RETVAL); } else if (expr_ == 0) { /* * there's no expression - generate a simple void return (but * explicitly return nil, so we don't return something left in * R0 from a previous function call we made) */ G_cg->write_op(OPC_RETNIL); } else { /* check for a constant expression */ if (expr_->is_const()) { switch(expr_->get_const_val()->get_type()) { case TC_CVT_NIL: /* generate a RETNIL instruction */ G_cg->write_op(OPC_RETNIL); break; case TC_CVT_TRUE: /* generate a RETTRUE instruction */ G_cg->write_op(OPC_RETTRUE); break; default: break; } } /* * if we needed code generation to evaluate the return value, we * now need to return the value */ if (need_gen) { /* * Other types don't have constant-return opcodes. We * already generated the expression value (before invoking * the enclosing 'finally' handlers, if any), so the value * is on the stack, and all we need to do is return it. * * If we didn't actually leave a value on the stack, we'll * just return nil. */ if (val_on_stack) { /* generate the return-value opcode */ G_cg->write_op(OPC_RETVAL); /* RETVAL removes an element from the stack */ G_cg->note_pop(); } else { /* * The depth didn't change - they must have evaluated an * expression involving a dstring or void function. * Return nil instead of the non-existent value. */ G_cg->write_op(OPC_RETNIL); } } } } /* ------------------------------------------------------------------------ */ /* * Static property initializer statement */ void CTPNStmStaticPropInit::gen_code(int, int) { int depth; /* add a line record */ add_debug_line_rec(); /* note the initial stack depth */ depth = G_cg->get_sp_depth(); /* generate the expression, keeping the generated value */ expr_->gen_code(FALSE, FALSE); /* ensure that we generated a value; if we didn't, push nil by default */ if (G_cg->get_sp_depth() <= depth) { /* push a default nil value */ G_cg->write_op(OPC_PUSHNIL); G_cg->note_push(); } /* * duplicate the value on the stack, so we can assign it to * initialize the property and also return it */ G_cg->write_op(OPC_DUP); G_cg->note_push(); /* write the SETPROPSELF to initialize the property */ G_cg->write_op(OPC_SETPROPSELF); G_cs->write_prop_id(prop_); /* SETPROPSELF removes the value */ G_cg->note_pop(); /* return the value (which we duplicated on the stack) */ G_cg->write_op(OPC_RETVAL); /* RETVAL removes the value */ G_cg->note_pop(); } /* ------------------------------------------------------------------------ */ /* * Object Definition Statement */ /* * generate code */ void CTPNStmObject::gen_code(int, int) { /* if this object has been replaced, don't generate any code for it */ if (replaced_) return; /* add an implicit constructor if necessary */ add_implicit_constructor(); /* get the appropriate stream for generating the data */ CTcDataStream *str = obj_sym_->get_stream(); /* clear the internal flags */ uint internal_flags = 0; /* * if we're a modified object, set the 'modified' flag in the object * header */ if (modified_) internal_flags |= TCT3_OBJ_MODIFIED; /* set the 'transient' flag if appropriate */ if (transient_) internal_flags |= TCT3_OBJ_TRANSIENT; /* clear the object flags */ uint obj_flags = 0; /* * If we're specifically marked as a 'class' object, or we're a * modified object, set the 'class' flag in the object flags. */ if (is_class_ || modified_) obj_flags |= TCT3_OBJFLG_CLASS; /* remember our starting offset in the object stream */ ulong start_ofs = str->get_ofs(); /* * store our stream offset in our defining symbol, for storage in * the object file */ obj_sym_->set_stream_ofs(start_ofs); /* write our internal flags */ str->write2(internal_flags); /* * First, write the per-object image file "OBJS" header - each * object starts with its object ID and the number of bytes in the * object's metaclass-specific data. For now, write zero as a * placeholder for our data size. Note that this is a * self-reference: it must be modified if the object is renumbered. */ str->write_obj_id_selfref(obj_sym_); str->write2(0); /* write a placeholder for the superclass count */ str->write2(0); /* write the fixed property count */ str->write2(proplist_.cnt_); /* write the object flags */ str->write2(obj_flags); /* * First, go through the superclass list and verify that each * superclass is actually an object. */ int bad_sc = FALSE; int sc_cnt = 0; for (CTPNSuperclass *sc = get_first_sc() ; sc != 0 ; sc = sc->nxt_) { /* look up the superclass in the global symbol table */ CTcSymObj *sc_sym = (CTcSymObj *)sc->get_sym(); /* make sure it's defined, and that it's really an object */ if (sc_sym == 0) { /* not defined */ log_error(TCERR_UNDEF_SYM_SC, (int)sc->get_sym_len(), sc->get_sym_txt(), (int)obj_sym_->get_sym_len(), obj_sym_->get_sym()); /* note that we have an invalid superclass */ bad_sc = TRUE; } else if (sc_sym->get_type() != TC_SYM_OBJ) { /* log an error */ log_error(TCERR_SC_NOT_OBJECT, (int)sc_sym->get_sym_len(), sc_sym->get_sym()); /* note that we have an invalid superclass */ bad_sc = TRUE; } else { /* count the superclass */ ++sc_cnt; /* write the superclass to the object header */ str->write_obj_id(sc_sym->get_obj_id()); } } /* * If we detected a 'bad template' error when we were parsing the * object definition, and all of our superclasses are valid, report the * template error. * * Do not report this error if we have any undefined or invalid * superclasses, because (1) we've already reported one error for this * object definition (the bad superclass error), and (2) the missing * template is likely just a consequence of the bad superclass, since * we can't have scanned the proper superclass's list of templates if * they didn't tell us the correct superclass to start with. When they * fix the superclass list and re-compile the code, it's likely that * this will fix the template problem as well, since we'll probably be * able to find the template give the corrected superclass list. * * If we found an undescribed class anywhere in our hierarchy, a * template simply cannot be used with this object; otherwise, the * error is that we failed to find a suitable template */ if (has_bad_template() && !bad_sc) log_error(has_undesc_sc() ? TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE : TCERR_OBJ_DEF_NO_TEMPLATE); /* go back and write the superclass count to the header */ str->write2_at(start_ofs + TCT3_TADSOBJ_HEADER_OFS, sc_cnt); /* * Write the properties. We're required to write the properties in * sorted order of property ID, but we can't do that yet, because * the property ID's aren't finalized until after linking. For now, * just write them out in the order in which they were defined. */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) { /* make sure we have a valid property symbol */ if (prop->get_prop_sym() != 0) { /* write the property ID */ str->write_prop_id(prop->get_prop_sym()->get_prop()); /* generate code for the property */ prop->gen_code(FALSE, FALSE); } } /* * go back and write the size of our metaclass-specific data - this * goes at offset 4 in the T3 generic metaclass header */ str->write2_at(start_ofs + TCT3_META_HEADER_OFS + 4, str->get_ofs() - (start_ofs + TCT3_META_HEADER_OFS + 6)); } /* * Check for unreferenced local variables */ void CTPNStmObject::check_locals() { /* check for unreferenced locals for each property */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) prop->check_locals(); } frobtads-1.2.3/tads3/vmstr.cpp0000644000175000001440000041110012007550136015375 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMSTR.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstr.cpp - VM string metaclass implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include #include #include #include #include #include "t3std.h" #include "vmmcreg.h" #include "vmobj.h" #include "vmstr.h" #include "utf8.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmstack.h" #include "vmpool.h" #include "vmmeta.h" #include "vmrun.h" #include "vmbif.h" #include "vmpredef.h" #include "vmlst.h" #include "vmuni.h" #include "vmcset.h" #include "vmbytarr.h" #include "vmstrbuf.h" #include "charmap.h" #include "vmnet.h" #include "vmstrref.h" #include "sha2.h" #include "md5.h" #include "vmpack.h" #include "vmdatasrc.h" #include "vmpat.h" #include "vmregex.h" #include "vmbiftad.h" #include "vmfindrep.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassString metaclass_reg_obj; CVmMetaclass *CVmObjString::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (*CVmObjString::func_table_[])(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) = { &CVmObjString::getp_undef, /* 0 */ &CVmObjString::getp_len, /* 1 */ &CVmObjString::getp_substr, /* 2 */ &CVmObjString::getp_upper, /* 3 */ &CVmObjString::getp_lower, /* 4 */ &CVmObjString::getp_find, /* 5 */ &CVmObjString::getp_to_uni, /* 6 */ &CVmObjString::getp_htmlify, /* 7 */ &CVmObjString::getp_starts_with, /* 8 */ &CVmObjString::getp_ends_with, /* 9 */ &CVmObjString::getp_to_byte_array, /* 10 */ &CVmObjString::getp_replace, /* 11 */ &CVmObjString::getp_splice, /* 12 */ &CVmObjString::getp_split, /* 13 */ &CVmObjString::getp_specialsToHtml, /* 14 */ &CVmObjString::getp_specialsToText, /* 15 */ &CVmObjString::getp_urlEncode, /* 16 */ &CVmObjString::getp_urlDecode, /* 17 */ &CVmObjString::getp_sha256, /* 18 */ &CVmObjString::getp_md5, /* 19 */ &CVmObjString::getp_packBytes, /* 20 */ &CVmObjString::getp_unpackBytes, /* 21 */ &CVmObjString::getp_toTitleCase, /* 22 */ &CVmObjString::getp_toFoldedCase, /* 23 */ &CVmObjString::getp_compareTo, /* 24 */ &CVmObjString::getp_compareIgnoreCase, /* 25 */ &CVmObjString::getp_findLast, /* 26 */ &CVmObjString::getp_findAll, /* 27 */ &CVmObjString::getp_match /* 28 */ }; /* static property indices */ const int PROPIDX_packBytes = 20; /* ------------------------------------------------------------------------ */ /* * Static creation methods */ /* create dynamically using stack arguments */ vm_obj_id_t CVmObjString::create_from_stack(VMG_ const uchar **, uint) { /* dynamic string construction is not currently supported */ err_throw(VMERR_BAD_DYNAMIC_NEW); /* the compiler doesn't know we won't make it here */ AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* create a string with no initial contents */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(); return id; } /* create with a given buffer size */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, size_t byte_size) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ byte_size); return id; } /* create from a constant UTF-8 string */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const char *str, size_t bytelen) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ str, bytelen); return id; } /* create from a wide-character string */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const wchar_t *str, size_t charlen) { /* get the utf8 byte length of the string */ size_t i, bytelen; const wchar_t *src; for (src = str, i = 0, bytelen = 0 ; i < charlen ; ++src, ++i) bytelen += utf8_ptr::s_wchar_size(*src); /* allocate space */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjString(vmg_ bytelen); CVmObjString *s = (CVmObjString *)vm_objp(vmg_ id); /* build the string */ utf8_ptr dst(s->cons_get_buf()); for (src = str, i = 0 ; i < charlen ; ++src, ++i) dst.setch(*src); /* return the new string object ID */ return id; } /* create from the given character set */ vm_obj_id_t CVmObjString::create(VMG_ int in_root_set, const char *src, size_t srclen, CCharmapToUni *cmap) { /* figure the size needed for the conversion */ size_t ulen = cmap->map_str(0, 0, src, srclen); /* create a string of the mapped length */ vm_obj_id_t ret = create(vmg_ in_root_set, ulen); CVmObjString *retstr = (CVmObjString *)vm_objp(vmg_ ret); /* map into the string buffer */ cmap->map_str(retstr->cons_get_buf(), ulen, src, srclen); /* return the new string */ return ret; } /* create from a byte stream, interpreting the bytes as Latin-1 characters */ vm_obj_id_t CVmObjString::create_latin1(VMG_ int in_root_set, CVmDataSource *src) { /* scan the memory stream to determine the string byte length */ long len = copy_latin1_to_string(0, 0, src); /* make sure the length is within the acceptable range */ if (len > 65535) err_throw(VMERR_STR_TOO_LONG); /* allocate the string object */ vm_obj_id_t id = create(vmg_ FALSE, len); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ id); /* copy the stream to the string */ copy_latin1_to_string(str->cons_get_buf(), len, src); /* return the new object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Copy a byte stream to a string, treating each byte of the source stream * as a Latin-1 character. * * If the output buffer is null or is too short, we'll just count up the * length without storing anything. Returns the total number of bytes * needed to store the full source byte array. */ size_t CVmObjString::copy_latin1_to_string(char *str, size_t len, CVmDataSource *src) { /* we haven't copied any bytes out yet */ size_t tot = 0; /* seek to the start of the source */ src->seek(0, OSFSK_SET); /* keep going until we reach EOF */ for (;;) { /* read the next chunk of bytes from the source */ char buf[1024]; int cur = src->readc(buf, sizeof(buf)); /* if there's nothing left, we're done */ if (cur == 0) return tot; /* scan the buffer */ for (char *p = buf ; cur != 0 ; ++p, --cur) { /* * if this character is from 0 to 127, store one byte; * otherwise store it as two bytes in UTF-8 format */ unsigned char c = *(unsigned char *)p; if (c <= 127) { /* 0..127 -> single byte UTF-8 character */ tot += 1; if (str != 0 && tot <= len) *str++ = c; } else { /* 128.255 -> two-byte UTF-8 character */ tot += 2; if (str != 0 && tot <= len) { *str++ = (char)(0xC0 | ((c >> 6) & 0x1F)); *str++ = (char)(0x80 | (c & 0x3F)); } } } } } /* ------------------------------------------------------------------------ */ /* * Constructors */ /* * create a string object with a given buffer size */ CVmObjString::CVmObjString(VMG_ size_t len) { /* * the length is limited to an unsigned 16-bit value (NB: it really is * 65535 on ALL PLATFORMS - this is a portable limit imposed by the * portable storage format, not a local platform limit) */ if (len > 65535) { ext_ = 0; err_throw(VMERR_STR_TOO_LONG); } /* * allocate space for the buffer plus the length prefix in the * variable heap */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* set the length */ vmb_put_len(ext_, len); } /* * create a string object from a given UTF8 string constant */ CVmObjString::CVmObjString(VMG_ const char *str, size_t len) { /* check for the length limit */ if (len > 65535) { ext_ = 0; err_throw(VMERR_STR_TOO_LONG); } /* * allocate space for the string plus the length prefix in the * variable heap */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* * store the length prefix in portable format (so that we can easily * write our contents to a saved state file) */ vmb_put_len(ext_, len); /* copy the string's bytes */ memcpy(ext_ + VMB_LEN, str, len); } /* ------------------------------------------------------------------------ */ /* * Ensure space for construction */ char *CVmObjString::cons_ensure_space(VMG_ char *ptr, size_t len, size_t margin) { /* figure the offset of the pointer into the current buffer */ int ofs = ptr - (ext_ + VMB_LEN); /* calculate the needed size: this is the offset plus the added length */ size_t need = ofs + len; /* if we already have enough space, we're done */ if (vmb_get_len(ext_) >= need) return ptr; /* expand to the needed size plus the margin */ size_t newlen = need + margin; ext_ = (char *)G_mem->get_var_heap()->realloc_mem( newlen + VMB_LEN, ext_, this); /* set the new buffer size */ vmb_put_len(ext_, newlen); /* return the pointer to the new buffer at the original offset */ return ext_ + VMB_LEN + ofs; } /* * Append a string during construction */ char *CVmObjString::cons_append(VMG_ char *ptr, const char *addstr, size_t addlen, size_t margin) { /* ensure we have space for the added string */ ptr = cons_ensure_space(vmg_ ptr, addlen, margin); /* add the new string onto the end of the buffer */ memcpy(ptr, addstr, addlen); /* return a pointer to the end of the added data */ return ptr + addlen; } /* * Append a character during construction */ char *CVmObjString::cons_append(VMG_ char *ptr, wchar_t ch, size_t margin) { /* encode the character as utf8 */ char buf[3]; size_t len = utf8_ptr::s_putch(buf, ch); /* append the utf8 string for the character */ return cons_append(vmg_ ptr, buf, len, margin); } /* * Shrink the buffer to the actual final size */ void CVmObjString::cons_shrink_buffer(VMG_ char *ptr) { /* figure the offset of the pointer into the current buffer */ size_t siz = (size_t)(ptr - (ext_ + VMB_LEN)); /* * If the savings are substantial enough, reallocate. If the size is * unchanged, or it's within a reasonable margin of the target size, do * nothing. Reallocation isn't free (we have to find memory and make a * copy of the current buffer), so it's more efficient to waste a * little memory if the savings are substantial. */ if (vmb_get_len(ext_) - siz >= 256) { /* reallocate at the new size */ ext_ = (char *)G_mem->get_var_heap()->realloc_mem( siz + VMB_LEN, ext_, this); } /* write the final length */ vmb_put_len(ext_, siz); } /* ------------------------------------------------------------------------ */ /* * receive notification of deletion */ void CVmObjString::notify_delete(VMG_ int in_root_set) { /* free our extension */ if (ext_ != 0 && !in_root_set) G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * call a static property */ int CVmObjString::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods */ switch (idx) { case PROPIDX_packBytes: return static_getp_packBytes(vmg_ result, argc); default: /* inherit the default handling */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Set a property. Strings have no settable properties, so simply * signal an error indicating that the set-prop call is invalid. */ void CVmObjString::set_prop(VMG_ CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Save the object to a file */ void CVmObjString::save_to_file(VMG_ CVmFile *fp) { /* get our length */ size_t len = vmb_get_len(ext_); /* write the length prefix and the string */ fp->write_bytes(ext_, len + VMB_LEN); } /* * Restore the object from a file */ void CVmObjString::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *) { /* read the length prefix */ size_t len = fp->read_uint2(); /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * allocate our extension - make room for the length prefix plus the * bytes of the string */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(len + VMB_LEN, this); /* store our length prefix */ vmb_put_len(ext_, len); /* read the string */ fp->read_bytes(ext_ + VMB_LEN, len); } /* ------------------------------------------------------------------------ */ /* * Cast to integer. We parse the string as a numeric value, using the same * rules as toInteger(). */ long CVmObjString::cast_to_int(VMG0_) const { /* * parse the string as an integer in decimal format; this is an * explicit int cast, so don't allow BigNumber promotions */ vm_val_t val; parse_num_val(vmg_ &val, ext_ + VMB_LEN, vmb_get_len(ext_), 10, TRUE); /* return the integer value */ return val.val.intval; } /* * Cast to number. */ void CVmObjString::cast_to_num(VMG_ vm_val_t *val, vm_obj_id_t self) const { /* * Parse the string as a number in decimal format. We're allowed to * return whatever numeric type is needed to represent the value, so * allow BigNumber promotions if necessary. */ parse_num_val(vmg_ val, ext_ + VMB_LEN, vmb_get_len(ext_), 10, FALSE); } /* * Parse a string as an integer value */ void CVmObjString::parse_num_val(VMG_ vm_val_t *retval, const char *str, size_t len, int radix, int int_only) { utf8_ptr p; size_t rem; /* skip leading spaces */ for (p.set((char *)str), rem = len ; rem != 0 && t3_is_whitespace(p.getch()) ; p.inc(&rem)) ; /* presume it's positive */ int is_neg = FALSE; /* check for a leading + or - */ if (rem != 0) { if (p.getch() == '-') { /* note the sign and skip the character */ is_neg = TRUE; p.inc(&rem); } else if (p.getch() == '+') { /* skip the character */ p.inc(&rem); } } /* skip any additional spaces after the sign */ for ( ; rem != 0 && t3_is_whitespace(p.getch()) ; p.inc(&rem)) ; /* clear the accumulator */ unsigned long acc = 0; /* scan the digits */ for ( ; rem != 0 ; p.inc(&rem)) { /* get the next digit */ wchar_t ch = p.getch(); /* * If we're allowed to promote to BigNumber, and the radix is * decimal, check for '.' and 'E' (for exponent) notation. These * could indicate a floating point value. */ if (radix == 10 && !int_only && (ch == '.' || ch == 'e' || ch == 'E')) { /* presume we're going to promote to BigNumber */ int promote = TRUE; /* * If it's an exponent, parse further to make sure it looks * like a valid exponent: we can have an optional + or - sign, * then need at least one digit. */ if (ch == 'e' || ch == 'E') { /* set up a second pointer */ utf8_ptr p2(p); size_t rem2 = rem; /* skip the 'e' */ p2.inc(&rem); /* if there's a sign, skip it */ if (rem2 != 0 && (p2.getch() == '+' || p2.getch() == '-')) p2.inc(&rem2); /* we need a digit at this point, or it's not an exponent */ promote = (rem2 != 0 && is_digit(p2.getch())); } /* if we're promoting after all, go do the promotion */ if (promote) { /* re-parse it as a BigNumber value */ retval->set_obj(CVmObjBigNum::create_radix( vmg_ FALSE, str, len, radix)); /* return this value */ return; } } /* get the integer value of the digit, treating A-Z as digits 10-35 */ int dig; if (ch >= '0' && ch <= '9') dig = (int)(ch - '0'); else if (ch >= 'a' && ch <= 'z') dig = (int)(ch - 'a' + 10); else if (ch >= 'A' && ch <= 'Z') dig = (int)(ch - 'A' + 10); else break; /* if it's outside the radix limit, it's not a digit after all */ if (dig >= radix) break; /* * Make sure we're not going to overflow. If the base is 2, 8, or * 16, and there wasn't a "-" sign, allow the value to go up to the * unsigned 32-bit limit, 0xffffffff. Otherwise, limit it to * 32-bit signed, -0x8000000 to +0x7ffffff. */ unsigned long limit = (!is_neg && (radix == 2 || radix == 8 || radix == 16)) ? 0xffffffff : is_neg ? 0x80000000 : 0x7fffffff; /* shift the accumulator by the radix, checking for overflow */ if (acc > limit/radix) goto overflow; acc *= radix; /* add the digit, checking for overflow */ if (acc > limit - dig) goto overflow; acc += dig; } /* apply the sign, if appropriate, and set the return value */ retval->set_int(is_neg && acc <= 0x7FFFFFFF ? -(long)acc : (long)acc); return; overflow: /* * We overflowed the plain integer type. If we're allowed to promote * to a BigNumber, re-parse the value as a BigNumber. If not, it's an * error. */ if (int_only) { /* we can't promote to BigNumber - throw an overflow error */ err_throw(VMERR_NUM_OVERFLOW); } else { /* re-parse it as a BigNumber value */ retval->set_obj(CVmObjBigNum::create_radix( vmg_ FALSE, str, len, radix)); } } /* ------------------------------------------------------------------------ */ /* * Add a value to this string */ int CVmObjString::add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val) { /* set up a 'self' value holder */ vm_val_t selfval; selfval.set_obj(self); /* * Use the generic string adder, using my extension as the constant * string. We store our extension in the general string format * required by the static adder. */ add_to_str(vmg_ result, &selfval, val); /* handled */ return TRUE; } /* * Static string adder. This creates a new string object that results * from appending the given value to the given string constant. This is * defined statically so that this same code can be shared for adding to * constant pool strings and adding to CVmObjString objects. * * 'strval' must point to a constant string. The first two bytes of the * string are stored in portable UINT2 format and give the length in * bytes of the string, not including the length prefix; immediately * following the length prefix are the bytes of the string. * * Note that we *always* create a new object to hold the result, even if * the new string is identical to the first, so that we consistently * return a distinct reference from the original. */ void CVmObjString::add_to_str(VMG_ vm_val_t *result, const vm_val_t *self, const vm_val_t *val) { const char *strval1, *strval2; char buf[128]; vm_obj_id_t obj; size_t len1, len2; CVmObjString *objptr; vm_val_t new_obj2; /* * Get the string buffer pointers. The left value is already a string, * or we wouldn't be here. The right value can be anything, though, so * we need to apply an implicit string conversion if it's another type. */ strval1 = self->get_as_string(vmg0_); strval2 = cvt_to_str(vmg_ &new_obj2, buf, sizeof(buf), val, 10, 0); /* get the lengths of the two strings */ len1 = vmb_get_len(strval1); len2 = vmb_get_len(strval2); /* * If the right-hand value is zero length, or it's nil, simply return * the left-hand value. If the left-hand value is zero length AND the * right-hand value is already a string (constant or object), return * the right-hand value unchanged. Otherwise, we actually need to * build a whole new string with the concatenated texts. */ if (len2 == 0 || val->typ == VM_NIL) { /* we're appending nothing to the string; just return 'self' */ *result = *self; } else if (len1 == 0 && val->get_as_string(vmg0_) != 0) { /* * we're appending the right value to an empty string, AND the * right value is already a string itself, so we can simply return * the right value unchanged */ *result = *val; } else { /* * push the new string (if any) and self, to protect the two * strings from garbage collection */ G_stk->push(self); G_stk->push(&new_obj2); /* create a new string object to hold the result */ obj = create(vmg_ FALSE, len1 + len2); objptr = (CVmObjString *)vm_objp(vmg_ obj); /* copy the two strings into the new object's string buffer */ objptr->copy_into_str(0, strval1 + VMB_LEN, len1); objptr->copy_into_str(len1, strval2 + VMB_LEN, len2); /* we're done with the garbage collection protection */ G_stk->discard(2); /* return the new object in the result */ result->set_obj(obj); } } /* ------------------------------------------------------------------------ */ /* * Allocate a string buffer large enough to hold a given value. We'll * use the provided buffer if possible. * * If the provided buffer is null or is not large enough, we'll allocate * a new string object with a large enough buffer to hold the value, and * return the object's extension as the buffer. This object will never * be referenced by anyone, so it will be deleted at the next garbage * collection. * * The buffer size and requested size are in bytes. */ char *CVmObjString::alloc_str_buf(VMG_ vm_val_t *new_obj, char *buf, size_t buf_size, size_t required_size) { /* if the provided buffer is large enough, use it */ if (buf != 0 && buf_size >= required_size) { /* there's no new object */ new_obj->set_nil(); /* return the buffer */ return buf; } /* allocate a new string object */ new_obj->set_obj(CVmObjString::create(vmg_ FALSE, required_size)); /* return the new object's full string buffer from the length prefix */ return ((CVmObjString *)vm_objp(vmg_ new_obj->val.obj))->ext_; } /* ------------------------------------------------------------------------ */ /* * Try converting a value to a string via reflection services, imported * from the bytecode program. Failing that, use a boilerplate format. */ const char *CVmObjString::reflect_to_str( VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, const char *fmt, ...) { /* look for an exported reflectionServices object from the byteocde */ vm_val_t refl; refl.set_obj(G_predef->reflection_services); vm_prop_id_t valToSym = G_predef->reflection_valToSymbol; if (refl.val.obj != VM_INVALID_OBJ && valToSym != VM_INVALID_PROP) { /* invoke reflectionServices.valToSymbol(val) */ G_stk->push(val); G_interpreter->get_prop(vmg_ 0, &refl, valToSym, &refl, 1, 0); /* if we got a string, return it */ const char *str = G_interpreter->get_r0()->get_as_string(vmg0_); if (str != 0) { /* set the return value */ *new_str = *G_interpreter->get_r0(); /* return the string buffer */ return str; } } /* * No reflection services - fall back on the boilerplate format. Start * by calculating the amount of space we need for the formatted string, * adding a byte for the trailing null. */ va_list args; va_start(args, fmt); size_t need = t3vsprintf(0, 0, fmt, args) + 1; va_end(args); /* allocate space, including room for the length prefix */ result_buf = alloc_str_buf( vmg_ new_str, result_buf, result_buf_size, need + VMB_LEN); /* do the formatting */ va_start(args, fmt); t3vsprintf(result_buf + VMB_LEN, need, fmt, args); va_end(args); /* set the length prefix */ vmb_put_len(result_buf, need); /* return the buffer */ return result_buf; } /* ------------------------------------------------------------------------ */ /* * Convert a value to a string */ const char *CVmObjString::cvt_to_str(VMG_ vm_val_t *new_str, char *result_buf, size_t result_buf_size, const vm_val_t *val, int radix, int flags) { /* presume we won't need to create a new string object */ new_str->set_nil(); /* check the type of the value */ switch(val->typ) { case VM_SSTRING: /* it's a string constant - no conversion is necessary */ return G_const_pool->get_ptr(val->val.ofs); case VM_OBJ: /* it's an object - ask it for its string representation */ return vm_objp(vmg_ val->val.obj)->explicit_to_string( vmg_ val->val.obj, new_str, radix, flags); case VM_INT: /* * It's a number - convert it to a string. Use the provided result * buffer if possible, but make sure we have room for the number. * The unicode values we're storing are in the ascii range, so we * only need one byte per character. The longest buffer we'd need, * then, is 33 bytes, for a conversion to a bit string (i.e., base * 2: 32 1s or 0s, plus a sign). The conversion also needs two * bytes for the length prefix; add a few extra bytes as insurance * against future algorithm changes that might need more padding. */ result_buf = alloc_str_buf(vmg_ new_str, result_buf, result_buf_size, 40); /* generate the string */ return cvt_int_to_str(result_buf, 40, val->val.intval, radix, flags); case VM_NIL: /* nil - use the literal string "nil" */ return "\003\000nil"; case VM_TRUE: /* true - use the literal string "true" */ return "\004\000true"; case VM_LIST: return CVmObjList::list_to_string(vmg_ new_str, val, radix, flags); case VM_PROP: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "property#%d", (int)val->val.prop); case VM_FUNCPTR: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "function#%lx", (long)val->val.ofs); case VM_ENUM: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "enum#%ld", (long)val->val.enumval); case VM_BIFPTR: return reflect_to_str(vmg_ new_str, result_buf, result_buf_size, val, "builtin#%d.%d", (int)val->val.bifptr.set_idx, (int)val->val.bifptr.func_idx); default: /* other types cannot be added to a string */ err_throw(VMERR_NO_STR_CONV); /* we never really get here, but the compiler doesn't know that */ AFTER_ERR_THROW(return 0;) } } /* ------------------------------------------------------------------------ */ /* * Convert an integer to a string, storing the result in the given buffer * in portable string format (with length prefix). We accept any radix * from 2 to 36 (36 can be represented with "digits" 0-9A-Z). * * If the value is signed, a leading dash is included if the number is * negative. Otherwise we treat the original value as unsigned, which on * most hardware means we'll show the two's complement value. * * For efficiency, we store the number at the end of the buffer (this makes * it easy to generate the number, since we need to generate numerals in * reverse order). We return a pointer to the result, which may not start * at the beginning of the buffer. */ char *CVmObjString::cvt_int_to_str(char *buf, size_t buflen, int32_t inval, int radix, int flags) { int neg; uint32_t val; char *p; size_t len; /* start at the end of the buffer */ p = buf + buflen; /* * if it's negative, and we're converting to a signed representation, * use a leading minus sign plus the absolute value; otherwise, treat * the value as unsigned */ if (inval >= 0 || (flags & TOSTR_UNSIGNED) != 0) { /* * the value is non-negative, either because the signed * interpretation is non-negative or because we're using an * unsigned interpretation (in which case there's no such thing as * negative) */ neg = FALSE; /* use the value as-is */ val = (uint32_t)inval; } else { /* it's signed and negative - note that we need a minus sign */ neg = TRUE; /* use the positive value for the conversion */ val = (uint32_t)(-inval); } /* store numerals in reverse order */ do { char c; /* if we have no more room, throw an error */ if (p == buf) err_throw(VMERR_CONV_BUF_OVF); /* move on to the next available character in the buffer */ --p; /* figure the character representation of this numeral */ c = (char)(val % radix); if (c < 10) c += '0'; else c += 'A' - 10; /* store the numeral at the current location */ *p = c; /* divide the remaining number by the radix */ val /= radix; } while (val != 0); /* store the leading minus sign if necessary */ if (neg) { /* if we don't have room, throw an error */ if (p == buf) err_throw(VMERR_CONV_BUF_OVF); /* move to the next byte */ --p; /* store the minus sign */ *p = '-'; } /* calculate the length */ len = buflen - (p - buf); /* make sure we have room for the length prefix */ if (p < buf + 2) err_throw(VMERR_CONV_BUF_OVF); /* store the length prefix */ p -= 2; vmb_put_len(p, len); /* return the pointer to the start of the number */ return p; } /* ------------------------------------------------------------------------ */ /* * Check a value for equality */ int CVmObjString::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { /* if the other value is a reference to myself, we certainly match */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* * use the constant string comparison routine, using our underlying * string as the constant string data */ return const_equals(vmg_ ext_, val); } /* * Constant string equality test */ int CVmObjString::const_equals(VMG_ const char *str, const vm_val_t *val) { const char *str2; size_t len; /* if the other value is a string buffer, compare to its contents */ if (val->typ == VM_OBJ && CVmObjStringBuffer::is_string_buffer_obj(vmg_ val->val.obj)) return ((CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj)) ->equals_str(str); /* get the other value as a string */ str2 = val->get_as_string(vmg0_); /* if the object doesn't have an underlying string, we don't match */ if (str2 == 0) return FALSE; /* * if their lengths match, and the bytes match exactly, we have a * match; otherwise, they're not equal */ len = vmb_get_len(str); return (len == vmb_get_len(str2) && memcmp(str + VMB_LEN, str2 + VMB_LEN, len) == 0); } /* ------------------------------------------------------------------------ */ /* * Hash value */ uint CVmObjString::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { return const_calc_hash(ext_); } /* * Hash value calculation */ uint CVmObjString::const_calc_hash(const char *str) { size_t len; uint hash; utf8_ptr p; /* get and skip the length prefix */ len = vmb_get_len(str); str += VMB_LEN; /* scan the string and calculate the hash */ for (p.set((char *)str), hash = 0 ; len != 0 ; p.inc(&len)) hash += p.getch(); /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * Compare this string to another value */ int CVmObjString::compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *val) const { /* use the static string magnitude comparison routine */ return const_compare(vmg_ ext_, val); } /* * Compare a constant string value to another value. Returns a positive * number if the constant string is lexically greater than the other * value, a negative number if the constant string is lexically less * than the other value, or zero if the constant string is lexically * identical to the other value. * * The other value must be a string constant or an object with an * underlying string value. We'll throw an error for any other type of * value. */ int CVmObjString::const_compare(VMG_ const char *str1, const vm_val_t *val) { const char *str2; size_t len1, len2; /* if the other value is a string buffer, compare to its contents */ if (val->typ == VM_OBJ && CVmObjStringBuffer::is_string_buffer_obj(vmg_ val->val.obj)) { /* * ask the string buffer to do the comparison, but reverse the * sense of the comparison, since from its perspective the * comparison is in the opposite order */ return -((CVmObjStringBuffer *)vm_objp(vmg_ val->val.obj)) ->compare_str(str1); } /* get the other value as a string */ str2 = val->get_as_string(vmg0_); /* if it's not a string, we can't compare it */ if (str2 == 0) err_throw(VMERR_INVALID_COMPARISON); /* get the lengths of the two strings */ len1 = vmb_get_len(str1); len2 = vmb_get_len(str2); /* perform a lexical comparison and return the result */ return utf8_ptr::s_compare_to(str1 + VMB_LEN, len1, str2 + VMB_LEN, len2); } /* ------------------------------------------------------------------------ */ /* * Find a substring within a string */ const char *CVmObjString::find_substr(VMG_ const char *str, int32_t start_idx, const char *substr, size_t *idxp) { /* get the lengths */ size_t rem = vmb_get_len(str); size_t sublen = vmb_get_len(substr); /* set up utf8 pointer into the string */ utf8_ptr p((char *)str + 2); /* if the index is negative, it's from the end of the string */ start_idx += (start_idx < 0 ? (int)p.len(rem) : -1); /* skip to the starting index */ for (int32_t i = start_idx ; i > 0 && rem >= sublen ; --i, p.inc(&rem)) ; /* scan for the substring */ for (size_t char_ofs = 0 ; rem != 0 && rem >= sublen ; ++char_ofs, p.inc(&rem)) { /* check for a match */ if (memcmp(p.getptr(), substr + VMB_LEN, sublen) == 0) { /* it's a match - set the return index if they are interested */ if (idxp != 0) *idxp = char_ofs + start_idx; /* return the current pointer */ return p.getptr(); } } /* we didn't find it - so indicate by returning null */ return 0; } /* * Find a substring or pattern. 'basestr' is the entire string, and 'str' * is a pointer into the string giving the starting position for the * search; this can be the same as 'baseptr' to search from the very * beginning of the string, or to a character in the middle of the string. * * The returned 'match_idx' value is the character index relative to the * starting search position 'str'. If the match starts at the beginning of * 'str' then 'match_idx' is returned as 0. Note that this is relative to * the starting point 'str' rather than the overall string 'basestr'. */ const char *CVmObjString::find_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, CVmObjPattern *pat, int *match_idx, int *match_len) { /* search for the string or pattern */ if (substr != 0) { /* get the substring length and buffer pointer */ size_t sublen = vmb_get_len(substr); substr += VMB_LEN; /* search for the substring */ utf8_ptr p((char *)str); for (int i = 0 ; len >= sublen ; p.inc(&len), ++i) { /* check for a match */ if (memcmp(p.getptr(), substr, sublen) == 0) { /* got it - the match length is simply the substring length */ *match_idx = i; *match_len = sublen; return p.getptr(); } } } else if (pat != 0) { /* get the compiled pattern */ re_compiled_pattern *cpat = pat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *strval; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* search for the pattern */ int idx = G_bif_tads_globals->rex_searcher->search_for_pattern( cpat, basestr, str, len, match_len); /* if we found the match, return it */ if (idx >= 0) { /* set the match index and length */ *match_idx = utf8_ptr::s_len(str, idx); return str + idx; } } /* no match */ *match_idx = -1; *match_len = 0; return 0; } /* * Find a substring, searching from the end of the string toward the * beginning. This works like find_substr(), but searchs backwards. The * match must end before the starting position 'str' - it can't overlap * this character. To search the entire string from the end, set 'str' to * the byte just after the last character of the string. * * As with find_substr(), we return with 'match_idx' set to the character * index of the start of the match relative to 'str'. This means that * 'match_idx' will be zero or negative on a successful match. (And the * only way it returns with zero is when the matched text is zero length, * because of the requirement that the match can't overlap the starting * character.) */ const char *CVmObjString::find_last_substr( VMG_ const vm_val_t *strval, const char *basestr, const char *str, size_t len, const char *substr, CVmObjPattern *pat, int *match_idx, int *match_len) { /* search for the string or pattern */ if (substr != 0) { /* get the substring length and buffer pointer */ size_t sublen = vmb_get_len(substr); substr += VMB_LEN; /* get the character length of the search string */ size_t subclen = utf8_ptr::s_len(substr, sublen); /* set up at the starting position */ utf8_ptr p((char *)str); /* * back up by the number of characters 'c' in the search string - * the match can't include the from_idx position, so we can't match * anything later in the string than 'c' characters before from_idx */ size_t c; for (c = subclen ; c != 0 && p.getptr() != basestr ; --c, p.dec()) ; /* if we couldn't back up far enough, there's no chance of a match */ if (c > 0) { *match_idx = -1; *match_len = 0; return 0; } /* search backwards for the string */ for (int i = -(int)subclen ; ; p.dec(), --i) { /* check for a match at the current position */ if (memcmp(p.getptr(), substr, sublen) == 0) { /* * got it - the match index is the negative of the * character offset from the match position to the search * starting position, and the match length is simply * substring byte length */ *match_idx = -(int)utf8_ptr::s_len( p.getptr(), str - p.getptr()); *match_len = sublen; return p.getptr(); } /* if we're at the start of the base string, we're done */ if (p.getptr() == basestr) break; } } else if (pat != 0) { /* get the compiled pattern */ re_compiled_pattern *cpat = pat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *strval; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* search for the pattern */ int idx = G_bif_tads_globals->rex_searcher->search_back_for_pattern( cpat, basestr, str, len, match_len); /* if we found the match, return it */ if (idx >= 0) { /* set the match index and length */ *match_idx = -(int)utf8_ptr::s_len(str - idx, idx); return str + idx; } } /* no match */ *match_idx = -1; *match_len = 0; return 0; } /* ------------------------------------------------------------------------ */ /* * Evaluate a property */ int CVmObjString::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { vm_val_t self_val; /* use the constant evaluator */ self_val.set_obj(self); if (const_get_prop(vmg_ retval, &self_val, ext_, prop, source_obj, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Evaluate a property of a constant string value */ int CVmObjString::const_get_prop(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, vm_prop_id_t prop, vm_obj_id_t *src_obj, uint *argc) { /* presume no source object */ *src_obj = VM_INVALID_OBJ; /* translate the property index to an index into our function table */ uint func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((*func_table_[func_idx])(vmg_ retval, self_val, str, argc)) return TRUE; /* * If this is a constant string (which is indicated by an invalid * 'self' object ID), try inheriting the default object * interpretation, passing the constant string placeholder object * for its type information. */ if (self_val->typ != VM_OBJ) { /* try going to CVmObject directly */ if (vm_objp(vmg_ G_predef->const_str_obj) ->CVmObject::get_prop(vmg_ prop, retval, G_predef->const_str_obj, src_obj, argc)) return TRUE; } /* not handled */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - get the length */ int CVmObjString::getp_len(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up a utf-8 pointer to the string's contents */ utf8_ptr p; p.set((char *)str + VMB_LEN); /* return the character length of the string */ retval->set_int(p.len(vmb_get_len(str))); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - extract a substring */ int CVmObjString::getp_substr(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { long start; long len = 0; size_t rem; utf8_ptr p; utf8_ptr start_p; size_t start_rem; size_t new_len; vm_obj_id_t obj; /* check arguments */ uint argc = (in_argc == 0 ? 0 : *in_argc); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the starting index */ start = CVmBif::pop_long_val(vmg0_); /* pop the length, if present */ if (argc >= 2) len = CVmBif::pop_long_val(vmg0_); /* push a self-reference to protect against GC */ G_stk->push(self_val); /* set up a utf8 pointer to traverse the string */ p.set((char *)str + VMB_LEN); /* get the byte length of the string */ rem = vmb_get_len(str); /* * Skip ahead to the starting index. If the index is positive, it's * an index from the start of the string; if it's negative, it's an * offset from the end of the string. */ if (start > 0) { /* * it's an index from the start - skip ahead by start-1 characters * (since a start value of 1 tells us to start at the first * character) */ for ( ; start > 1 && rem != 0 ; --start) p.inc(&rem); } else if (start < 0) { /* * It's an index from the end of the string: -1 tells us to start * at the last character, -2 at the second to last, and so on. * Move to the first byte past the end of the string, and work * backwards by the given number of characters. */ for (p.set((char *)str + VMB_LEN + rem), rem = 0 ; start < 0 && p.getptr() != (char *)str + VMB_LEN ; ++start) { /* move back one character */ p.dec(&rem); } } /* this is the starting position */ start_p = p; start_rem = rem; /* * if a length was specified, calculate the number of bytes in the * given length; otherwise, use the entire remainder of the string */ if (argc >= 2) { /* figure the positive or negative length */ if (len >= 0) { /* * Positive length - this specifies the number of characters to * keep starting at the starting index. Skip ahead by the * desired length to figure the end pointer. */ for ( ; len > 0 && rem != 0 ; --len) p.inc(&rem); } else { /* * Negative length - this specifies the number of characters to * remove from the end of the string. Move to the end of the * string, then skip back by |len| characters to find the end * pointer. */ for (p.set(p.getptr() + rem), rem = 0 ; len < 0 && rem < start_rem ; ++len) p.dec(&rem); } /* use the difference in lengths from the starting point to here */ new_len = start_rem - rem; } else { /* use the entire remainder of the string */ new_len = start_rem; } /* create the new string */ obj = CVmObjString::create(vmg_ FALSE, start_p.getptr(), new_len); /* return the new object */ retval->set_obj(obj); /* discard the GC protection references */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - toUpper */ int CVmObjString::getp_upper(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_upper); } /* * property evaluator - toLower */ int CVmObjString::getp_lower(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_lower); } /* * property evaluator - toTitleCase */ int CVmObjString::getp_toTitleCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_title); } /* * property evaluator - toFoldedCase */ int CVmObjString::getp_toFoldedCase( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return gen_getp_case_conv(vmg_ retval, self_val, str, argc, &t3_to_fold); } /* * General case converter for toUpper, toLower, toTitleCase, toFoldedCase */ int CVmObjString::gen_getp_case_conv( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, const wchar_t *(*conv)(wchar_t)) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get my length */ size_t srclen = vmb_get_len(str); /* leave the string on the stack as GC protection */ G_stk->push(self_val); /* * Scan the string to determine how long the result will be. The * result won't necessarily be the same length as the original: some * case conversions are 1:N characters, and even when they're 1:1, a * two-byte character in the original could turn into a three-byte * character in the result, and vice versa. */ size_t dstlen; utf8_ptr srcp, dstp; size_t rem; for (dstlen = 0, srcp.set((char *)str + VMB_LEN), rem = srclen ; rem != 0 ; srcp.inc(&rem)) { /* get the mapping for this character */ wchar_t ch = srcp.getch(); const wchar_t *u = conv(ch); /* add up the byte length of the mapping */ dstlen += (u != 0 ? utf8_ptr::s_wstr_size(u) : utf8_ptr::s_wchar_size(ch)); } /* allocate the result string */ vm_obj_id_t result_obj = CVmObjString::create(vmg_ FALSE, dstlen); /* get a pointer to the result buffer */ dstp.set(((CVmObjString *)vm_objp(vmg_ result_obj))->cons_get_buf()); /* write the string */ for (srcp.set((char *)str + VMB_LEN), rem = srclen ; rem != 0 ; srcp.inc(&rem)) { /* get the mapping for this character */ wchar_t ch = srcp.getch(); const wchar_t *u = conv(ch); /* put the character(s) */ if (u != 0) dstlen -= dstp.setwcharsz(u, dstlen); else dstp.setch(ch, &dstlen); } /* return the value */ retval->set_obj(result_obj); /* discard GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Common handler for find() and findLast() methods. 'dir' specifies the * search direction: 1 for forward (left to right), -1 for reverse (right * to left). */ template inline int CVmObjString::find_common( VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember where the base string strings */ const char *basestr = str; /* retrieve the string or regex pattern to find */ vm_val_t *val2 = G_stk->get(0); const char *str2 = val2->get_as_string(vmg0_); CVmObjPattern *pat2 = 0; if (str2 != 0) { /* we have a simple string to find */ } else if (val2->typ == VM_OBJ && CVmObjPattern::is_pattern_obj(vmg_ val2->val.obj)) { /* it's a pattern */ pat2 = (CVmObjPattern *)vm_objp(vmg_ val2->val.obj); } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* if there's a starting index, skip that many characters */ int32_t start_idx = 0; if (orig_argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* get the starting index value */ start_idx = G_stk->get(1)->num_to_int(vmg0_); /* set up a UTF-8 pointer for traversing the string */ utf8_ptr strp((char *)str); /* * A positive index value is a 1-based index from the start of the * string. A negative index is from the end of the string, with -1 * pointing to the last character. For reverse searches, 0 is the * position past the last character. In any case, adjust to a * zero-based index. */ start_idx += (start_idx == 0 && dir < 0 ? (int)strp.len(len) : start_idx < 0 ? (int)strp.len(len) : -1); /* if there's a substring, we can limit the skip */ size_t min_len = (dir > 0 && str2 != 0 ? vmb_get_len(str2) : 0); /* skip that many characters */ int32_t i; for (i = 0 ; i < start_idx && len > min_len ; ++i, strp.inc(&len)) ; /* * if the start index was past the end of the string (or past the * point where the remaining subject string is too short for the * target string), we definitely can't match */ if (i < start_idx) { retval->set_nil(); goto done; } /* start the search here */ str = strp.getptr(); } else if (dir < 0) { /* * we're searching backwards, and there's no starting index, so the * starting point is the end of the string */ str = str + len; start_idx = utf8_ptr::s_len(basestr, len); len = 0; } /* find the substring */ int match_idx, match_len; if (dir > 0 ? find_substr(vmg_ self_val, basestr, str, len, str2, pat2, &match_idx, &match_len) != 0 : find_last_substr(vmg_ self_val, basestr, str, len, str2, pat2, &match_idx, &match_len) != 0) { /* found it - adjust to a 1-based value for return */ retval->set_int(start_idx + match_idx + 1); } else { /* didn't find it - return nil */ retval->set_nil(); } done: /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* * property evaluator - find */ int CVmObjString::getp_find(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return find_common<1>(vmg_ retval, self_val, str, argc); } /* * property evaluator - findLast */ int CVmObjString::getp_findLast(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return find_common<-1>(vmg_ retval, self_val, str, argc); } /* ------------------------------------------------------------------------ */ /* * property evaluator - findAll */ int CVmObjString::getp_findAll(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the base string */ const char *basestr = str; /* retrieve the string or regex pattern to find */ vm_val_t *targ = G_stk->get(0); const char *targstr = 0; CVmObjPattern *targpat = 0; if ((targstr = targ->get_as_string(vmg0_)) != 0) { /* we have a simple string to find */ } else if ((targpat = vm_val_cast(CVmObjPattern, targ)) != 0) { /* it's a pattern */ } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* retrieve the filter callback function, if present */ vm_val_t *func = 0; int func_argc_min = 0, func_argc_max = 0; vm_rcdesc rc; if (orig_argc >= 2 && (func = G_stk->get(1))->typ != VM_NIL) { /* make sure it's a function */ if (!func->is_func_ptr(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* note how many arguments the function accepts */ CVmFuncPtr fd(vmg_ func); func_argc_min = fd.get_min_argc(); func_argc_max = fd.is_varargs() ? -1 : fd.get_max_argc(); } /* * create a tentative list for the results (we'll resize this as needed * as we add elements) */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear it initially and push it for gc protection */ lst->cons_clear(); G_stk->push(retval); /* repeatedly search for the pattern */ int cnt; for (cnt = 0 ; len != 0 ; ++cnt) { /* search for the next element */ int match_cofs, match_len, match_bofs; const char *nxt = find_substr( vmg_ self_val, basestr, str, len, targstr, targpat, &match_cofs, &match_len); /* if we didn't find a match, we're done */ if (nxt == 0) break; /* figure the byte offset of the match */ match_bofs = nxt - str; /* call the callback, or just return the match text directly */ vm_val_t ele; if (func == 0) { /* no callback - just use the match text, as a new string */ ele.set_obj(CVmObjString::create(vmg_ FALSE, nxt, match_len)); } else { /* we have a callback - push arguments */ int fargc = 0; /* * Figure out how many groups are populated, and how many the * function expects. If the function takes varargs, it expects * all of the groups. If it takes fixed arguments, it expects * at most max_argc-2. In addition, the function expects a * minimum of min_argc-2 groups, and expects us to add nil * arguments for any unpopulated groups to fill out that * minimum. */ int gc_actual = G_bif_tads_globals->rex_searcher->get_group_cnt(); int gc_wanted = (func_argc_max == -1 ? gc_actual : func_argc_max - 2); if (gc_wanted < func_argc_min - 2) gc_wanted = func_argc_min - 2; /* push the groups, last to first */ for (int i = gc_wanted ; i > 0 ; ) { /* get the group, if it exists */ const re_group_register *reg = 0; if (--i < gc_actual) { /* valid group - get the group information */ reg = G_bif_tads_globals->rex_searcher->get_group_reg(i); } /* if the group is populated, push its text, otherwise nil */ if (reg != 0 && reg->start_ofs >= 0 && reg->end_ofs >= 0) { /* push the group text as a new string */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, basestr + reg->start_ofs, reg->end_ofs - reg->start_ofs)); } else { /* unpopulated group - push nil */ G_stk->push()->set_nil(); } /* count it */ ++fargc; } /* if the index value is desired, push it */ if (func_argc_max == -1 || func_argc_max >= 2) { /* push the index of the match relative to basestr */ G_stk->push()->set_int( utf8_ptr::s_len(basestr, nxt - basestr) + 1); ++fargc; } /* if the match string value is desired, push it */ if (func_argc_max == -1 || func_argc_max >= 1) { /* push the match text */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, nxt, match_len)); ++fargc; } /* call the callback */ G_interpreter->call_func_ptr(vmg_ func, fargc, &rc, 0); /* retrieve the return value */ ele = *G_interpreter->get_r0(); } /* add it to the list */ lst->cons_ensure_space(vmg_ cnt, 0); lst->cons_set_element(cnt, &ele); /* skip the matched text */ size_t skip = match_bofs + match_len; str += skip; len -= skip; /* * if we matched zero characters, skip one character to prevent an * infinite loop */ if (match_len == 0 && len != 0) str += utf8_ptr::s_bytelen(str, 1); } /* finalize the list length */ lst->cons_set_len(cnt); /* discard arguments and gc protection */ G_stk->discard(orig_argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - match */ int CVmObjString::getp_match(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(1, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the base string */ const char *basestr = str; /* retrieve the string or regex pattern to match */ vm_val_t *targ = G_stk->get(0); const char *targstr = 0; CVmObjPattern *targpat = 0; size_t targstrlen = 0; if ((targstr = targ->get_as_string(vmg0_)) != 0) { /* it's a literal string - get its length and text pointer */ targstrlen = vmb_get_len(targstr); targstr += VMB_LEN; } else if ((targpat = vm_val_cast(CVmObjPattern, targ)) != 0) { /* it's a pattern */ } else { /* we need a string or a pattern; other values are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } /* retrieve the starting index, if present */ int32_t start_idx = 0; if (orig_argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* get the starting index value */ start_idx = G_stk->get(1)->num_to_int(vmg0_); /* set up a UTF-8 pointer for traversing the string */ utf8_ptr strp((char *)str); /* * A positive index value is a 1-based index from the start of the * string. A negative index is from the end of the string, with -1 * pointing to the last character. */ start_idx += (start_idx < 0 ? (int)strp.len(len) : -1); /* skip that many characters */ int32_t i; for (i = 0 ; i < start_idx && len > targstrlen ; ++i, strp.inc(&len)) ; /* * if the start index was past the end of the string (or past the * point where the remaining subject string is too short for the * target string), we definitely can't match */ if (i < start_idx) { retval->set_nil(); goto done; } /* start the search here */ str = strp.getptr(); } /* match the string or pattern */ if (targstr != 0) { /* make sure it's long enough, then match the text literally */ if (len >= targstrlen && memcmp(targstr, str, targstrlen) == 0) { /* matched - return the match length */ retval->set_int(targstrlen); } else { /* no match */ retval->set_nil(); } } else { /* RexPattern - get the compiled pattern */ re_compiled_pattern *cpat = targpat->get_pattern(vmg0_); /* save the last search source string */ G_bif_tads_globals->last_rex_str->val = *self_val; G_bif_tads_globals->rex_searcher->clear_group_regs(); /* match the pattern */ int matchlen = G_bif_tads_globals->rex_searcher->match_pattern( cpat, basestr, str, len); /* if it matched (len >= 0), return the length, otherwise nil */ if (matchlen >= 0) retval->set_int(matchlen); else retval->set_nil(); } done: /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * findReplace() method - replace one or all occurrences of a given * substring or regular expression pattern within the subject string (self) * with a given replacement string. This uses the common find/replace * handler defined in vmfindrep.h. */ int CVmObjString::getp_replace(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ uint orig_argc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(2, 3); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* do the search-and-replace */ vm_find_replace( vmg_ retval, orig_argc, self_val, str); /* discard arguments */ G_stk->discard(orig_argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - convert to unicode */ int CVmObjString::getp_to_uni(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { size_t bytelen; long idx = 1; utf8_ptr p; /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* retrieve the index argument if present */ if (argc >= 1) idx = CVmBif::pop_long_val(vmg0_); /* push a self-reference as GC protection */ G_stk->push(self_val); /* get and skip the string's length prefix */ bytelen = vmb_get_len(str); str += VMB_LEN; /* set up a utf8 pointer to the string */ p.set((char *)str); /* if the index is negative, it's an index from the end of the string */ if (idx < 0) idx += p.len(bytelen) + 1; /* check for an index argument */ if (argc >= 1) { /* skip through the string until we get to the desired index */ for ( ; idx > 1 && bytelen != 0 ; --idx, p.inc(&bytelen)) ; /* check to see if we have a character available */ if (idx == 1 && bytelen != 0) { /* the index is valid - return the character here */ retval->set_int((long)p.getch()); } else { /* * the index is past the end of the string or is less than 1 * - return nil to indicate that there's no character here */ retval->set_nil(); } } else { size_t charlen; vm_obj_id_t lst_obj; CVmObjList *lst; size_t i; /* * There's no index argument - they want a list of all of the * code points in the string. First, get the number of * characters in the string. */ charlen = p.len(bytelen); /* create a list to hold the results */ lst_obj = CVmObjList::create(vmg_ FALSE, charlen); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the list's elements to the unicode characters values */ for (i = 0 ; i < charlen ; ++i, p.inc()) { wchar_t ch; vm_val_t ele_val; /* get this character */ ch = p.getch(); /* set this list element */ ele_val.set_int((long)ch); lst->cons_set_element(i, &ele_val); } /* return the list object */ retval->set_obj(lst_obj); } /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - htmlify */ /* * htmlify flags */ /* preserve spaces */ #define VMSTR_HTMLIFY_KEEP_SPACES 0x0001 /* preserve newlines */ #define VMSTR_HTMLIFY_KEEP_NEWLINES 0x0002 /* preserve tabs */ #define VMSTR_HTMLIFY_KEEP_TABS 0x0004 /* * htmlify implementation */ int CVmObjString::getp_htmlify(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *in_argc) { size_t bytelen; utf8_ptr p; utf8_ptr dstp; size_t rem; size_t extra; long flags; vm_obj_id_t result_obj; int prv_was_sp; /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* if they specified flags, pop them */ if (argc >= 1) { /* retrieve the flags */ flags = CVmBif::pop_long_val(vmg0_); } else { /* no flags */ flags = 0; } /* push a self-reference as GC protection */ G_stk->push(self_val); /* get and skip the string's length prefix */ bytelen = vmb_get_len(str); str += VMB_LEN; /* * scan the string to determine how much space we'll have to add to * generate the htmlified version */ for (prv_was_sp = FALSE, extra = 0, p.set((char *)str), rem = bytelen ; rem != 0 ; p.inc(&rem)) { int this_is_sp; /* presume it's not a space */ this_is_sp = FALSE; /* check what we have */ switch(p.getch()) { case '&': /* we must replace '&' with '&' - this adds four bytes */ extra += 4; break; case '<': /* we must replace '<' with '<' - this adds three bytes */ extra += 3; break; case '>': /* replace '>' with '>' */ extra += 3; break; case ' ': /* * If we're in preserve-spaces mode, and the previous space * was some kind of whitespace character, change this to * ' ' - this adds five bytes */ if (prv_was_sp && (flags & VMSTR_HTMLIFY_KEEP_SPACES) != 0) extra += 5; /* note that this was a whitespace character */ this_is_sp = TRUE; break; case '\t': /* if we're in preserve-tabs mode, change this to '' */ if ((flags & VMSTR_HTMLIFY_KEEP_TABS) != 0) extra += 4; /* note that this was a whitespace character */ this_is_sp = TRUE; break; case '\n': case 0x2028: /* if we're in preserve-newlines mode, change this to '
' */ if ((flags & VMSTR_HTMLIFY_KEEP_NEWLINES) != 0) extra += 3; /* note that this was a whitespace character */ this_is_sp = TRUE; break; } /* for next time, remember whether this is a space */ prv_was_sp = this_is_sp; } /* allocate space for the new string */ result_obj = create(vmg_ FALSE, bytelen + extra); /* get a pointer to the result buffer */ dstp.set(((CVmObjString *)vm_objp(vmg_ result_obj))->cons_get_buf()); /* translate the string and write the result */ for (prv_was_sp = FALSE, p.set((char *)str), rem = bytelen ; rem != 0 ; p.inc(&rem)) { wchar_t ch; int this_is_sp; /* get this character */ ch = p.getch(); /* presume it's not a space */ this_is_sp = FALSE; /* check what we have */ switch(ch) { case '&': /* replace '&' with '&' */ dstp.setch_str("&"); break; case '<': /* we must replace '<' with '<' - this adds three bytes */ dstp.setch_str("<"); break; case '>': dstp.setch_str(">"); break; case ' ': /* note that this was a whitespace character */ this_is_sp = TRUE; /* * ignore it if not in preserve-spaces mode, or if the * previous character wasn't whitespace of some kind */ if (!prv_was_sp || (flags & VMSTR_HTMLIFY_KEEP_SPACES) == 0) goto do_default; /* add the nbsp */ dstp.setch_str(" "); break; case '\t': /* note that this was a whitespace character */ this_is_sp = TRUE; /* ignore if not in preserve-tabs mode */ if ((flags & VMSTR_HTMLIFY_KEEP_TABS) == 0) goto do_default; /* add the */ dstp.setch_str(""); break; case '\n': case 0x2028: /* note that this was a whitespace character */ this_is_sp = TRUE; /* if we're not in preserve-newlines mode, ignore it */ if ((flags & VMSTR_HTMLIFY_KEEP_NEWLINES) == 0) goto do_default; /* add the
*/ dstp.setch_str("
"); break; default: do_default: /* copy this character unchanged */ dstp.setch(ch); break; } /* for next time, remember whether this is a space */ prv_was_sp = this_is_sp; } /* return the new string */ retval->set_obj(result_obj); /* discard the GC protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - startsWith */ int CVmObjString::getp_starts_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { const char *str2; size_t len; size_t len2; /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the other string */ str2 = CVmBif::pop_str_val(vmg0_); /* get the lengths of the two strings */ len = vmb_get_len(str); len2 = vmb_get_len(str2); /* move to the contents of each string */ str += VMB_LEN; str2 += VMB_LEN; /* * if the other string is no longer than our string, and the other * string matches our string exactly for the other string's entire * length, we start with the other string */ retval->set_logical(len2 <= len && memcmp(str, str2, len2) == 0); /* handled */ return TRUE; } /* * property evaluator - endsWith */ int CVmObjString::getp_ends_with(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { const char *str2; size_t len; size_t len2; /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the other string */ str2 = CVmBif::pop_str_val(vmg0_); /* get the lengths of the two strings */ len = vmb_get_len(str); len2 = vmb_get_len(str2); /* move to the contents of each string */ str += VMB_LEN; str2 += VMB_LEN; /* * If the other string is no longer than our string, and the other * string matches our string at the end exactly for the other string's * entire length, we start with the other string. Note we don't need * to worry about finding a valid character index in our string for * the ending offset, because all we care about is whether or not we * have an exact byte match between our suffix and the other string. */ retval->set_logical(len2 <= len && memcmp(str + len - len2, str2, len2) == 0); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - mapToByteArray */ int CVmObjString::getp_to_byte_array(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *oargc) { /* check arguments */ int argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* note the mapper argument */ const vm_val_t *cmap = (argc >= 1 ? G_stk->get(0) : 0); /* save the string for gc protection */ G_stk->push(self_val); /* create the byte array */ retval->set_obj(CVmObjByteArray::create_from_string( vmg_ self_val, str, cmap)); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - splice */ int CVmObjString::getp_splice(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { char ins_buf[128]; /* check arguments */ uint oargc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(2, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the starting index and deletion length */ int start_idx = CVmBif::pop_int_val(vmg0_); int del_len = CVmBif::pop_int_val(vmg0_); /* get and skip our length */ size_t len = vmb_get_len(str); str += VMB_LEN; utf8_ptr p((char *)str); int charlen = p.len(len); /* adjust the index to 0-based, and figure end-of-string offsets */ start_idx += (start_idx < 0 ? charlen : -1); /* make sure the starting index and deletion length are in bounds */ start_idx = (start_idx < 0 ? 0 : start_idx > (int)charlen ? charlen : start_idx); del_len = (del_len < 0 ? 0 : del_len > (int)(charlen - start_idx) ? charlen - start_idx : del_len); /* get the starting byte index */ size_t start_byte_idx = p.bytelen(start_idx); /* save 'self' as gc protection */ G_stk->push(self_val); /* * If there's an insertion string, get it as a string. Treat nil as an * empty insertion. */ size_t ins_len = 0; const char *ins = 0; vm_val_t new_ins_str; new_ins_str.set_nil(); if (oargc >= 3 && G_stk->get(1)->typ != VM_NIL) { /* * Leave the insertion string on the stack for gc protection, and * do an explicit conversion to string. */ ins = cvt_to_str(vmg_ &new_ins_str, ins_buf, sizeof(ins_buf), G_stk->get(1), 10, 0); ins_len = vmb_get_len(ins); ins += VMB_LEN; } /* push the new insertion string (if any) for gc protection */ G_stk->push(&new_ins_str); /* check to see if we're making any changes */ if (del_len != 0 || ins_len != 0) { /* allocate a new string at the proper length */ retval->set_obj(create(vmg_ FALSE, len + ins_len - del_len)); CVmObjString *new_str = (CVmObjString *)vm_objp(vmg_ retval->val.obj); char *newp = new_str->cons_get_buf(); /* copy the part of our string up to the starting index */ if (start_byte_idx > 0) { memcpy(newp, str, start_byte_idx); newp += start_byte_idx; } /* if we have an insertion string, copy it */ if (ins_len != 0) { memcpy(newp, ins, ins_len); newp += ins_len; } /* skip past the deleted material in the original string */ p.set((char *)str + start_byte_idx); for (len -= start_byte_idx ; del_len != 0 && len != 0 ; --del_len) p.inc(&len); /* if we have any more of the spliced string, copy it */ if (len != 0) memcpy(newp, p.getptr(), len); } else { /* we're making no changes - return the original string */ *retval = *self_val; } /* * discard the remaining arguments and the gc protection (original * argc, minus 2 arg pops, plus 2 gc protection pushes -> oargc) */ G_stk->discard(oargc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - split */ int CVmObjString::getp_split(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argcp) { /* check arguments */ static CVmNativeCodeDesc desc(0, 2); uint argc = (argcp != 0 ? *argcp : 0); if (get_prop_check_argc(retval, argcp, &desc)) return TRUE; /* get the string buffer and length */ size_t len = vmb_get_len(str); str += VMB_LEN; /* remember the start of the base string */ const char *basestr = str; /* * Get the delimiter or split size, leaving it on the stack for gc * protection. This can be a string, a RexPattern, or an integer. */ const vm_val_t *delim = G_stk->get(0); const char *delim_str = 0; CVmObjPattern *delim_pat = 0; int split_len = 0; if (argc < 1 || delim->typ == VM_NIL) { /* there's no delimiter at all, so use the default split length 1 */ split_len = 1; } else if (delim->typ == VM_INT) { /* it's a simple split length */ split_len = delim->val.intval; if (split_len <= 0) err_throw(VMERR_BAD_VAL_BIF); } else if ((delim_str = delim->get_as_string(vmg0_)) == 0) { /* if it's not a length or string, it has to be a RexPattern object */ if (delim->typ != VM_OBJ || !CVmObjPattern::is_pattern_obj(vmg_ delim->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the pattern object, properly cast */ delim_pat = (CVmObjPattern *)vm_objp(vmg_ delim->val.obj); } /* get the split count limit, if there is one */ int32_t limit = -1; if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* there's an explicit limit; fetch it and make sure it's at least 1 */ if ((limit = G_stk->get(1)->num_to_int(vmg0_)) < 1) err_throw(VMERR_BAD_VAL_BIF); } /* push 'self' for gc protection */ G_stk->push(self_val); /* * Set up a return list. If we have a limit, the list can't go over * that length, so just create the list at the limit. Otherwise, if we * have a split length, we can figure the required list length by * dividing the string length by the split length (rounding up). * Otherwise we have no idea how many list elements we'll need, so just * create the list at an arbitrary default length and expand later if * needed. */ int init_list_len; if (limit > 0) { /* there's a limit, so we won't need more than this many elements */ init_list_len = limit; } else if (split_len > 0) { /* split length -> divide the string length by the split length */ init_list_len = (len + split_len - 1)/split_len; /* set the length to at least one, unless it's an empty string */ if (init_list_len < 1 && len != 0) init_list_len = 1; } else { /* no limit or length, so start with an arbitrary guess */ init_list_len = 10; } /* create the list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, init_list_len)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear the list to nils, since we're building it incrementally */ lst->cons_clear(); /* push it for gc protection */ G_stk->push(retval); /* repeatedly search for the delimiter and split the string */ int cnt; vm_val_t ele; for (cnt = 0 ; (limit < 0 || cnt + 1 < limit) && len != 0 ; ++cnt) { /* search for the next delimiter or next 'split_len' characters */ int match_ofs, match_len; if (split_len > 0) { /* figure the number of bytes in split_len characters */ match_ofs = utf8_ptr::s_bytelen(str, split_len); /* if that uses the whole rest of the string, we're done */ if (match_ofs >= (int)len) break; /* there's no delimiter, so the delimiter match length is zero */ match_len = 0; } else { /* search for the substring or pattern */ const char *nxt = find_substr( vmg_ self_val, basestr, str, len, delim_str, delim_pat, &match_ofs, &match_len); /* if we didn't find it, we're done */ if (nxt == 0) break; /* figure the byte offset to the match */ match_ofs = nxt - str; } /* add the substring from 'str' to 'p' to the list */ lst->cons_ensure_space(vmg_ cnt, 0); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, match_ofs)); lst->cons_set_element(cnt, &ele); /* skip to the position after the match */ size_t skip = match_ofs + match_len; str += skip; len -= skip; /* * if we matched zero characters, skip one character to prevent an * infinite loop */ if (split_len == 0 && match_len == 0 && len != 0) str += utf8_ptr::s_bytelen(str, 1); } /* add a final element for the remainder of the string */ if (len != 0) { lst->cons_ensure_space(vmg_ cnt, 0); ele.set_obj(CVmObjString::create(vmg_ FALSE, str, len)); lst->cons_set_element(cnt, &ele); /* count it */ ++cnt; } /* set the final size of the list */ lst->cons_set_len(cnt); /* discard arguments plus gc protection */ G_stk->discard(argc + 2); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Helper routines for specialsToHtml */ /* find the tag name within a tag */ static void find_tag_name(StringRef *tag, const char *&name, size_t &len, int &is_end_tag) { const char *p; /* skip leading spaces */ for (p = tag->get() ; isspace(*p) ; ++p) ; /* if it's an end tag, skip the '/' and any intervening spaces */ if ((is_end_tag = (*p == '/')) != 0) for (++p ; isspace(*p) ; ++p) ; /* scan the name - it's from here to the next space */ for (name = p ; *p != '\0' && !isspace(*p) ; ++p) ; /* note the length */ len = p - name; } /* parse the name from a tag */ static void parse_tag_name(StringRef *tag, char *&name, int &is_end_tag) { /* get the tag name */ size_t len; const char *namec; find_tag_name(tag, namec, len, is_end_tag); /* allocate a copy of the tag name */ name = lib_copy_str(namec, len); } /* find an attribute value, given a pointer to the end of the attr name */ static int find_attr_val(const char *name, size_t name_len, const char *&val, size_t &val_len) { const char *p; /* go to the end of the attribute name */ p = name + name_len; /* if there's an '=', there's a value */ if (*p == '=') { /* the attribute value starts after the '=' */ val = ++p; /* skip the token */ for (char qu = 0 ; *p != 0 && (!isspace(*p) || qu != 0) ; ++p) { /* note if entering or leaving a quoted section */ if (qu == 0 && (*p == '"' || *p == '\'')) qu = *p; else if (*p == qu) qu = 0; } /* note the length of the value */ val_len = p - val; /* there's an explicit value for this attribute */ return TRUE; } else { /* the attribute name is its own implied value */ val = name; val_len = name_len; /* there's no explicit value */ return FALSE; } } /* get an attribute value in a tag */ static char *parse_attr_val(StringRef *tag, const char *attr_name) { const char *p; size_t len; int is_end_tag; size_t attr_len = strlen(attr_name); /* find and skip the tag name */ find_tag_name(tag, p, len, is_end_tag); p += len; /* keep going until we find the attribute or run out of text */ for (;;) { /* skip leading spaces */ for ( ; isspace(*p) ; ++p) ; /* if we're at the end of the text, we're done */ if (*p == '\0') break; /* scan the attribute name */ const char *an; size_t anlen; for (an = p ; *p != '\0' && *p != '=' && !isspace(*p) ; ++p) ; anlen = p - an; /* get the attribute value */ const char *av; size_t avlen; find_attr_val(an, anlen, av, avlen); /* check for a match */ if (anlen == attr_len && memicmp(attr_name, an, attr_len) == 0) { /* it's our attribute name - allocate a copy */ char *retval = lib_copy_str(av, avlen); /* remove quotes */ char *dst, qu; for (p = dst = retval, qu = 0 ; *p != '\0' ; ++p) { /* check for entering/leaving a quoted section */ if (qu == 0 && (*p == '"' || *p == '\'')) { /* entering quotes - note the quote, don't copy it */ qu = *p; } else if (qu == *p) { /* leaving quotes - don't copy the quote */ qu = 0; } else { /* copy anything else as given */ *dst++ = *p; } } /* null-terminate the updated string and return it */ *dst = '\0'; return retval; } /* skip the value and move on to the next attribute */ p = av + avlen; } /* didn't find the value */ return 0; } /* * property evaluator - specialsToHtml */ int CVmObjString::getp_specialsToHtml(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return specialsTo(vmg_ retval, self_val, str, argc, TRUE); } /* * property evaluator - specialsToText */ int CVmObjString::getp_specialsToText(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { return specialsTo(vmg_ retval, self_val, str, argc, FALSE); } /* * common property evaluator for specialsToText and specialsToHtml */ int CVmObjString::specialsTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc, int html) { vm_rcdesc rc; vm_prop_id_t flags_prop = VM_INVALID_OBJ, tag_prop = VM_INVALID_OBJ; /* check arguments */ uint oargc = (argc != 0 ? *argc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set up the default initial state */ int in_line = FALSE; int caps = FALSE, nocaps = FALSE; int space = FALSE, qspace = FALSE; int in_tag = FALSE, in_entity = FALSE; int qlevel = 0; char attrq = 0; int col = 0; StringRef *tag = new StringRef(128); /* get the string length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* set up a buffer for the output - anticipate a little expansion */ StringRef *buf = new StringRef(len*5/4); err_try { /* get the state object, if present */ vm_val_t stateobj; stateobj.set_nil(); if (oargc >= 1) { /* the argument is there - get the object or nil */ if (G_stk->get(0)->typ == VM_OBJ) { /* get the state object */ stateobj = *G_stk->get(0); /* set up the recursive evaluation descriptor */ rc.init(vmg_ "String.specialsToHtml", self_val, 13, G_stk->get(0), oargc); /* get the 'flags_' and 'tag_' property IDs */ flags_prop = G_predef->string_sth_flags; tag_prop = G_predef->string_sth_tag; /* get the flags */ int32_t flags = 0; if (flags_prop != VM_INVALID_OBJ) { /* get the property */ G_interpreter->get_prop(vmg_ 0, &stateobj, flags_prop, &stateobj, 0, &rc); /* if it's an integer, get its value */ if (G_interpreter->get_r0()->typ == VM_INT) flags = G_interpreter->get_r0()->val.intval; } /* decode the flags */ in_line = (flags & 0x0001) != 0; caps = (flags & 0x0002) != 0; nocaps = (flags & 0x0004) != 0; in_tag = (flags & 0x0008) != 0; attrq = (flags & 0x0010 ? '"' : flags & 0x0020 ? '\'' : 0); space = (flags & 0x0040) != 0; qspace = (flags & 0x0080) != 0; qlevel = (flags & 0x0100) != 0; in_entity = (flags & 0x200) != 0; col = (flags & 0x3000) >> 12; /* get the tag value */ const char *tagp; if (tag_prop != VM_INVALID_OBJ) { /* get the property */ G_interpreter->get_prop(vmg_ 0, &stateobj, tag_prop, &stateobj, 0, &rc); /* get the string value, if any */ tagp = G_interpreter->get_r0()->get_as_string(vmg0_); /* copy the tag string, if we found it */ if (tagp != 0) tag->append(tagp + VMB_LEN, vmb_get_len(tagp)); } } else if (G_stk->get(0)->typ != VM_NIL) { /* anything other than an object or nil is an error */ err_throw(VMERR_BAD_TYPE_BIF); } } /* parse the string */ utf8_ptr p; for (p.set((char *)str) ; len != 0 ; p.inc(&len)) { /* get the next character */ wchar_t ch = p.getch(); /* if we're in a tag, add this character to the tag */ if (in_tag) { /* convert specials in the tag */ switch (ch) { case '\n': case 0x0B: case 0x15: case '\t': /* convert newlines, quoted spaces, and tabs to spaces */ tag->append(" "); break; case 0x0E: case 0x0F: /* \^ and \v have no place in tags */ break; case '"': case '\'': /* note that we're entering or leaving quoted text */ if (attrq == 0) attrq = (char)ch; else if (attrq == ch) attrq = 0; /* append the character */ tag->append_utf8(ch); break; case '>': /* ignore it if we're in quoted text */ if (attrq != 0) { tag->append_utf8(ch); } else { /* end the tag */ in_tag = FALSE; /* parse the tag name */ char *tagname; int is_end_tag; parse_tag_name(tag, tagname, is_end_tag); /* assume we'll copy it verbatim */ int keep_tag = TRUE; /* check to see if it's one of our special tags */ if (stricmp(tagname, "q") == 0) { /* * or - translate to &ldquo..&rdquo or * &lsquo..&rsquo */ static const char *qentity[] = { "“", "”", "‘", "’" }; static const char *qtxt[] = { "\"", "\"", "'", "'" }; /* if it's , pre-decrement the level */ if (is_end_tag) qlevel = !qlevel; /* * figure which type of quote to use: double * quotes at even levels, single quotes at odd * levels; left quotes for open tags, right * quotes for close tags */ int idx = (qlevel ? 2 : 0) + (is_end_tag ? 1 : 0); /* add the quote */ buf->append(html ? qentity[idx] : qtxt[idx]); col++; /* if it's , post-increment the level */ if (!is_end_tag) qlevel = !qlevel; /* we've translated the tag */ keep_tag = FALSE; } else if (stricmp(tagname, "br") == 0) { /* * if there's a HEIGHT attribute, it's special; * otherwise it's just a regular
*/ char *htv = parse_attr_val(tag, "height"); if (htv != 0) { /* get the numeric value */ int ht = atoi(htv); /* * if we're at the beginning of a line, * translate this to 'ht'
tags; if * we're within a line, use 'ht+1'
* tags */ if (in_line) ht += 1; /* write out the
tags */ while (ht-- > 0) buf->append(html ? "
" : "\n"); /* done with the height value */ lib_free_str(htv); /* we've translated the tag */ keep_tag = FALSE; } else if (!html) { /* regular text mode - convert to \n */ buf->append("\n"); } /* we're no longer in a line */ in_line = FALSE; col = 0; } else if (stricmp(tagname, "p") == 0) { /* in text mode, add a newline or two */ if (!html) { buf->append("\n"); if (in_line) buf->append("\n"); } /* this counts as a line break */ in_line = FALSE; col = 0; } else if (stricmp(tagname, "div") == 0 || stricmp(tagname, "center") == 0 || stricmp(tagname, "table") == 0 || stricmp(tagname, "td") == 0 || stricmp(tagname, "th") == 0 || stricmp(tagname, "caption") == 0) { /* this counts as a line break */ in_line = FALSE; col = 0; /* in text mode, add a newline */ if (!html) buf->append("\n"); } /* * if we're keeping the original tag, copy it to * the output */ if (html && keep_tag) { buf->append("<"); buf->append(tag->get(), tag->getlen()); buf->append(">"); } /* done with the tag name */ lib_free_str(tagname); /* we're done with the tag - clear the buffer */ tag->truncate(0); } break; default: /* append anything else as-is */ tag->append_utf8(ch); break; } /* we're done processing this character */ continue; } /* continue gathering entity text if applicable */ if (in_entity) { /* * If the entity is more than 10 character long, or this is * a semicolon, end the entity. Also stop if we're * processing a &# entity and this isn't a digit. */ if (ch == ';' || tag->getlen() >= 10 || (tag->get()[0] == '#' && !is_digit(ch))) { /* check what we have in the tag buffer */ char *n = tag->get(); if (n[0] == '#') { /* get the decimal unicode value */ buf->append_utf8((wchar_t)atoi(n+1)); /* * if we stopped on something other than a ';', add * it as well */ if (ch != ';') buf->append_utf8(ch); } else if (stricmp(n, "nbsp") == 0) buf->append_utf8(' '); else if (stricmp(n, "gt") == 0) buf->append_utf8('>'); else if (stricmp(n, "lt") == 0) buf->append_utf8('<'); else if (stricmp(n, "amp") == 0) buf->append_utf8('&'); else if (stricmp(n, "ldquo") == 0 || stricmp(n, "rdquo") == 0 || stricmp(n, "quot") == 0) buf->append_utf8('\"'); else if (stricmp(n, "lsquo") == 0 || stricmp(n, "rsquo") == 0) buf->append_utf8('\''); else { /* unknown entity - copy it as-is */ buf->append(n); } /* we're no longer in an entity */ in_entity = FALSE; tag->truncate(0); } else { /* * we're still working on gathering this entity - * simply buffer the character for now */ tag->append_utf8(ch); } /* we've handled this character */ continue; } /* if we have a pending space, write it out */ if (space) { /* * We have a pending regular space. If the next character * is a quoted space, omit the regular space entirely, as * regular spaces combine with adjacent quoted spaces. * Otherwise, write it as a regular space. */ if (ch != 0x15) { buf->append(" "); col++; } /* we've now processed this pending space */ space = FALSE; } else if (qspace) { /* * We have a pending quoted space. * * If the next character is a regular space, skip the space * and keep the pending quoted space flag set - the regular * space combines with the quoted space, so we can remove * it, but we don't yet know how to handle the quoted space * itself since we have to see the next non-space character * to determine that. * * If the next character is another quoted space, write the * pending quoted space as  , since adjacent quoted * spaces don't combine. * * If the next character is anything else (not a space or * quoted space), write the pending quoted space as a * regular space, since the html renderer won't combine the * space with the following non-space character, and we do * want to allow a line break after a quoted space. */ if (ch == ' ') continue; else if (html) { buf->append(ch == 0x15 ? " " : " "); col++; } else { buf->append(" "); col++; } /* we've now processed this pending quoted space */ qspace = FALSE; } /* handle the character */ switch (ch) { case '\n': /* add a
if we're within a line */ if (in_line) buf->append(html ? "
" : "\n"); /* we're now at the start of a new line */ in_line = FALSE; col = 0; caps = nocaps = FALSE; break; case 0x0B: /* * '\b' - blank line: add two
's if we're within a * line, otherwise just add one */ buf->append(html ? "
" : "\n"); if (in_line) buf->append(html ? "
" : "\n"); /* we're now at the start of a new line */ in_line = FALSE; col = 0; caps = nocaps = FALSE; break; case 0x0F: /* * '\^' - this doesn't write anything to the output, but * simply sets a flag that tells us to capitalize the next * printing character */ caps = TRUE; nocaps = FALSE; break; case 0x0E: /* * '\v' - this doesn't write anything to the output, but * simply sets a flag that tells us to lower-case the next * printing character */ caps = FALSE; nocaps = TRUE; break; case 0x15: /* * '\ ' - quoted space: this is a non-combining space (it * can't be consolidated with adjacent spaces) that allows * line breaks. Don't write anything now, but set the * quoted space flag for the next character: if the next * character out is a regular or quoted space, we'll write * the quoted space as a non-breaking space; otherwise * we'll write the quoted space as a regular space, so that * we can break after it. */ qspace = TRUE; break; case ' ': /* * Regular space. Don't actually write anything yet, since * we'll have to turn this into a non-breaking space if the * next character turns out to be a quoted space. Just set * the space-pending flag for now. */ space = TRUE; break; case '\t': /* * Tab - append spaces to take us out to the next 4-column * tab stop. We want the renderer to obey the spaces * (rather than consolidating them), so use non-breaking * spaces ( ). However, we want to allow a line break * after the tab, so use an ordinary space for the last * one. The renderer won't combine an ordinary space with * an  , so it'll render the number of spaces we ask * for, but we'll be able to break the line after that * last, ordinary space. * * In regular text mode, just write out the tab character. */ if (html) { col = ((col + 1) & 3) - 1; for ( ; col + 1 < 3 ; ++col) buf->append(" "); buf->append(" "); ++col; } else buf->append("\t"); break; case '<': /* start a tag */ in_tag = TRUE; break; case '&': /* if in text mode, gather the entity information */ if (!html) in_entity = TRUE; else buf->append("&"); break; default: /* Ordinary character. Check for caps/nocaps conversions. */ { const wchar_t *u = 0; if (caps) { u = t3_to_upper(ch); caps = FALSE; } else if (nocaps) { u = t3_to_lower(ch); nocaps = FALSE; } /* we're now within a line */ in_line = TRUE; /* add this character or string to the output */ if (u != 0) { for ( ; *u != 0 ; buf->append_utf8(*u++)) ; } else { buf->append_utf8(ch); } /* advance the tab-stop column */ col++; } break; } } /* * If we have a state object, write the final state back to the * state object, so that the next call can pick up where we left * off. */ if (stateobj.typ == VM_OBJ) { /* encode the flags */ vm_val_t v; v.set_int((in_line ? 0x0001 : 0) | (caps ? 0x0002 : 0) | (nocaps ? 0x0004 : 0) | (in_tag ? 0x0008 : 0) | (attrq == '"' ? 0x0010 : attrq == '\'' ? 0x0020 : 0) | (space ? 0x0040 : 0) | (qspace ? 0x0080 : 0) | (qlevel ? 0x0100 : 0) | (in_entity ? 0x0200 : 0) | ((col & 3) << 12)); /* set the flags */ if (flags_prop != VM_INVALID_OBJ) { G_interpreter->set_prop( vmg_ stateobj.val.obj, flags_prop, &v); } /* set the tag */ if (tag_prop != VM_INVALID_OBJ) { if (in_tag || in_entity) { /* we have a tag - set stateobj.tags_ to the string */ G_interpreter->push_string( vmg_ tag->get(), tag->getlen()); G_interpreter->set_prop(vmg_ stateobj.val.obj, tag_prop, G_stk->get(0)); /* done with the stacked string */ G_stk->discard(); } else { /* no tag - set stateobj.tags_ to nil */ v.set_nil(); G_interpreter->set_prop( vmg_ stateobj.val.obj, tag_prop, &v); } } } /* return the result as a string value */ G_interpreter->push_string(vmg_ buf->get(), buf->getlen()); G_stk->pop(retval); } err_finally { /* release resources */ tag->release_ref(); buf->release_ref(); } err_end; /* discard arguments and gc protection items */ G_stk->discard(oargc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - urlEncode() */ int CVmObjString::getp_urlEncode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save my own value for gc protection */ G_stk->push(self_val); /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* scan the string and determine the size of the expansion */ size_t outlen; const char *p; size_t rem; for (outlen = 0, p = str, rem = len ; rem != 0 ; ++p, --rem) { /* * if this is a space, letter, digit, or '-' or '_', it requires * one byte in the output; otherwise it requires three bytes for * the "%xx" sequence */ char c = *p; if (c == ' ' || c == '-' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) outlen += 1; else outlen += 3; } /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, outlen)); char *dst = ((CVmObjString *)vm_objp(vmg_ retval->val.obj))->cons_get_buf(); /* build the return string */ for (p = str, rem = len ; rem != 0 ; ++p, --rem) { /* translate this byte */ char c = *p; if (c == ' ') { /* translate ' ' to '+' */ *dst++ = '+'; } else if (c == '-' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { /* letter, digit, '-', '_' - leave unchanged */ *dst++ = c; } else { /* translate anything else to a %xx sequence */ *dst++ = '%'; byte_to_xdigits(dst, (unsigned char)c); dst += 2; } } /* discard gc protection */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - urlDecode() */ int CVmObjString::getp_urlDecode(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save my own value for gc protection */ G_stk->push(self_val); /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* scan the string and determine the size of the result */ size_t outlen; const char *p; size_t rem; for (outlen = 0, p = str, rem = len ; rem != 0 ; ++p, --rem, ++outlen) { /* * if this is a '%xx' sequence, it translates to a single byte; * otherwise just copy the byte as-is */ if (*p == '%' && rem >= 3 && is_xdigit(*(p+1)) && is_xdigit(*(p+2))) { /* * it's a %xx sequence - skip the whole thing and count is as * just one byte of output */ p += 2; rem -= 2; } } /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, outlen)); char *outbuf = ((CVmObjString *)vm_objp(vmg_ retval->val.obj))->cons_get_buf(); char *dst = outbuf; /* build the return string */ for (p = str, rem = len ; rem != 0 ; ++p, --rem, ++dst) { /* * if it's a '+', translate to a space; otherwise, if this is a * '%xx' sequence, it translates to a single byte; otherwise just * copy the byte as-is */ if (*p == '+') { *dst = ' '; } else if (*p == '%' && rem >= 3 && is_xdigit(*(p+1)) && is_xdigit(*(p+2))) { /* it's a %xx sequence - translate to a byte */ *dst = (value_of_xdigit(*(p+1)) << 4) | value_of_xdigit(*(p+2)); /* skip the %xx sequence in the input */ p += 2; rem -= 2; } else { /* copy anything else as-is */ *dst = *p; } } /* validate the resulting UTF8 */ CCharmapToUni::validate(outbuf, outlen); /* discard gc protection */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - sha256() */ int CVmObjString::getp_sha256(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* calculate the hash */ char hash[65]; sha256_ez(hash, str, len); /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, hash, 64)); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - digestMD5() */ int CVmObjString::getp_md5(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* calculate the hash */ char hash[33]; md5_ez(hash, str, len); /* allocate the return string */ retval->set_obj(create(vmg_ FALSE, hash, 32)); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - packBytes() */ int CVmObjString::static_getp_packBytes(VMG_ vm_val_t *retval, uint *pargc) { /* check arguments */ uint argc = (pargc != 0 ? *pargc : 0); static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, pargc, &desc)) return TRUE; /* set up an in-memory data stream to receive the packed data */ CVmMemorySource *dst = new CVmMemorySource(0L); err_try { /* do the packing */ CVmPack::pack(vmg_ 0, argc, dst); /* create a string from the byte stream */ retval->set_obj(create_latin1(vmg_ FALSE, dst)); } err_finally { /* done with the in-memory data source */ delete dst; } err_end; /* discard arguments */ G_stk->discard(argc); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * UTF-8 String data source. This translates characters from the string to * bytes, treating the character code as the byte value. If we try to read * a character outside of the 0..255 range, we'll throw an error. */ class CVmUTF8Source: public CVmDataSource { public: CVmUTF8Source(const char *str, size_t len) { /* remember the string and its byte length */ this->str = str; this->bytelen = len; /* calculate the character length */ utf8_ptr p((char *)str); this->charlen = p.len(len); /* start at the beginning of the string */ this->charidx = 0; this->byteidx = 0; } virtual CVmDataSource *clone(VMG_ const char * /*mode*/) { return 0; } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* do the read; if the request isn't filled exactly, return failure */ return (size_t)readc(buf, len) != len; } /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf0, size_t len) { /* get the output buffer pointer as a character pointer */ char *buf = (char *)buf0; /* * limit the length to the remaining number of characters from the * current position */ size_t rem = charlen - charidx; if (len > rem) len = rem; /* do the copy */ utf8_ptr p((char *)str + byteidx); for (size_t i = 0 ; i < len ; ++i, p.inc(), ++charidx) { /* get this character */ wchar_t ch = p.getch(); /* if it's outside of the 0..255 range, it's an error */ if (ch > 255) err_throw(VMERR_NUM_OVERFLOW); /* store it */ *buf++ = (char)ch; } /* calculate the new byte index */ byteidx = (size_t)(p.getptr() - str); /* return the length we copied */ return len; } /* write bytes - we're read-only, so just return an error */ virtual int write(const void *, size_t) { return 1; } /* * Get the length of the file in bytes. We translate each character * from the string into into one byte in the stream, so our byte length * equals our character length. */ virtual long get_size() { return charlen; } /* get the current seek location */ virtual long get_pos() { return charidx; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { /* figure the absolute position based on the mode */ switch (mode) { case OSFSK_SET: /* set - the offset is already absolute */ break; case OSFSK_CUR: /* relative to current index */ ofs += charidx; break; case OSFSK_END: /* relative to the end of file */ ofs += charlen; break; default: /* invalid mode - return an error */ return 1; } /* limit the position to the bounds of the string */ if (ofs < 0) ofs = 0; else if (ofs > (long)charlen) ofs = (long)charlen; /* * The caller is working with the stream: the byte index in the * stream equals the character index in the string. */ charidx = (size_t)ofs; /* calculate the byte index for the new character index */ utf8_ptr p((char *)str); byteidx = p.bytelen(charidx); /* success */ return 0; } /* flush - no effect, since we don't buffer anything */ virtual int flush() { return 0; } /* close - we have no system resources, so this is a no-op */ virtual void close() { } protected: /* the string */ const char *str; /* byte and character length of the string */ size_t bytelen, charlen; /* current byte and character seek position in the string */ size_t byteidx, charidx; }; /* * property evaluator - unpackBytes() */ int CVmObjString::getp_unpackBytes(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the format string (leave it on the stack for gc protection) */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* unpack the string */ CVmUTF8Source src(str, len); CVmPack::unpack(vmg_ retval, fmt, fmtlen, &src); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - compareTo() */ int CVmObjString::getp_compareTo(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the other string (leave it on the stack for gc protection) */ const char *other = G_stk->get(0)->get_as_string(vmg0_); if (other == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t olen = vmb_get_len(other); other += VMB_LEN; /* no result yet */ retval->set_nil(); /* compare character by character */ utf8_ptr a((char *)str); utf8_ptr b((char *)other); for ( ; len != 0 && olen != 0 ; a.inc(&len), b.inc(&olen)) { int delta = (int)a.getch() - (int)b.getch(); if (delta != 0) { retval->set_int(delta); break; } } /* if we didn't find any differences, the shorter string sorts first */ if (retval->typ == VM_NIL) retval->set_int(len - olen); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* * property evaluator - compareIgnoreCase() */ int CVmObjString::getp_compareIgnoreCase(VMG_ vm_val_t *retval, const vm_val_t *self_val, const char *str, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the length and buffer pointer */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get the other string (leave it on the stack for gc protection) */ const char *other = G_stk->get(0)->get_as_string(vmg0_); if (other == 0) err_throw(VMERR_STRING_VAL_REQD); /* get its buffer and length */ size_t olen = vmb_get_len(other); other += VMB_LEN; /* compare the strings */ retval->set_int(t3_compare_case_fold(str, len, other, olen, 0)); /* discard arguments */ G_stk->discard(1); /* done */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Constant-pool string object */ /* * create */ vm_obj_id_t CVmObjStringConst::create(VMG_ const char *const_ptr) { /* create our new ID */ vm_obj_id_t id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* create our string object, pointing directly to the constant pool */ new (vmg_ id) CVmObjStringConst(vmg_ const_ptr); /* return the new ID */ return id; } frobtads-1.2.3/tads3/tct3base.h0000644000175000001440000004025111466010506015404 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3BASE.H,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3base.h - base parse node classes for T3 code generator Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3BASE_H #define TCT3BASE_H #include "tcprs.h" /* include the target-independent base classes */ #include "tcpnbase.h" /* ------------------------------------------------------------------------ */ /* * Base parse node class */ class CTcPrsNode: public CTcPrsNodeBase { public: /* * Generate code to assign into the expression as an lvalue. By * default, we'll throw an error; a default node should never be called * upon to generate code as an assignment target, because the * impossibility of this condition should be detected during parsing. * Each class that overrides check_lvalue() to return true must * override this to generate code for an assignment. * * 'typ' specifies the type of assignment - this specifies the * assignment operator being generated. * * 'phase' indicates what part of the assignment we're generating. For * a simple assignment, this routine will be called only once, with * phase set to 1. For a compound assignment, this routine will be * called twice. The first call is phase 1. This can do one of two * things: * * - Generate the entire assignment code, and return TRUE to indicate * that the assignment has been handled. The caller will generate no * more code. This should be used when the particular combination of * assignment type and lvalue can be specially optimized in the * bytecode. * * - Generate code to evaluate the lvalue's value, and prepare the * stack for eventual assignment to the lvalue, and return FALSE. The * caller in this case will then generate generic code to combine the * lvalue and rvalue. For example, with operator '+=', the caller will * generate the ADD instruction to compute 'lvalue + rvalue'. The * caller will finally call the routine again with phase set to 2. * * During phase 2, the routine must generate code to assign the rvalue * at top of stack to the lvalue. * * Returns true if the assignment type was handled, false if not. * Simple assignment must always be handled, but composite assignments * (such as "+=" or "*=") can refused. If the assignment isn't * handled, the caller must perform a generic calculation to compute * the result of the assignment (for example, for "+=", the caller must * generate code compute the sum of the right-hand side and the * original value of the left-hand side), then call gen_code_asi() * again to generate a simple assignment. * * In general, gen_code_asi() must generate code for complex * assignments itself only when it can take advantage of special * opcodes that would result in more efficient generated code. When no * special opcodes are available for the assignment, there's no need * for special coding here. * * If 'ignore_error' is true, this should not generate an error if this * node is not a suitable lvalue, but should simply return false. * * 'explicit' is true if this is an explicit assignment by user code, * false if it's an assignment generated automatically by the compiler. * Implicit assignments don't count for the purposes of unused value * warnings. These warnings are primarily to alert the user that they * could delete an unnecessary assignment from their code, or * conversely that they might have forgotten to do something with the * value that the assignment would suggest they intended to do, but * with a compiler-generated assignment there's nothing for the user to * delete to fix the warning. * * 'ctx' is the address of a 'void *' variable (a generic pointer). * This is for the callee's use during a two-phase compound operator * assignment, for passing itself context information between the * phases. On phase 1, the callee can allocate a structure and store * it at *ctx. On phase 2, the callee can then look at this structure * to retrieve information saved from the first phase. The callee is * responsible for deleting the structure on the second phase. For * single-phase TC_ASI_SIMPLE calls, the caller can simply pass a null * pointer for ctx, so the callee must not attempt to store anything * here during a simple assignment call. */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_error, int xplicit, void **ctx); /* * Generate code to take the address of the expression. By default, * we'll throw an internal error; a default node should never be * called upon to generate code to take its address, because we * shouldn't be able to parse such a thing. Each class that * overrides has_addr() to return true must override this to * generate address code. */ virtual void gen_code_addr(); /* * Generate an if-then-else condition test. This is called on the * controlling expression of an if-then-else, and can also be called on * subexpressions of short-circuit operators (|| and &&). The purpose * of this type of code generation is to avoid pushing the result of * the controlling expression onto the stack when this can be avoided, * and generate a jump to the appropriate if-else branch directly. * * Either then_label or else_label will be non-null; the one that's * null directly follows the condition test, so we are simply to fall * through to that code rather than jump to it explicitly. */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); /* * Generate code to call the expression as a function or method. * The caller will already have generated code to push the argument * list; this routine only needs to generate code to make the call. * * By default, we'll assume that the result of evaluating the * expression is a method or function pointer, so we'll generate a * call-indirect instruction to call the result of evaluating the * expression. */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Generate code to apply operator 'new' to the expression. By * default, 'new' cannot be applied to an expression; nodes that * allow operator 'new' must override this. */ virtual void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* * Generate code to evaluate a member expression on this object * expression, calling the property expression given. By default, * we'll evaluate our own expression to yield the object value, then * get the property expression (either as a constant or by * generating code to yield a property pointer), then we'll invoke * that code. Nodes should override this where more specific * instructions can be generated. * * If 'prop_is_expr' is true, the property expression (prop_expr) is * a parenthesized expression; otherwise, it's a simple property * symbol. (We need this information because a symbol enclosed in * parentheses would be otherwise indistinguishable from a plain * symbol, but the two syntax cases differ in their behavior: a * plain symbol is always a property name, whereas a symbol in * parentheses can be a local variable.) */ virtual void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Generate code for an object on the left side of a '.' expression. * If possible, return the constant object rather than generating * code. If the expression refers to "self", set (*is_self) to true * and return VM_INVALID_OBJ; otherwise, if the expression refers to * a constant object reference, set (*is_self) to false and return * the constant object value; otherwise, generate code for the * expression, set (*is_self) to false, and return VM_INVALID_OBJ to * indicate that the value must be obtained from the generated code * at run-time. * * By default, we'll use generated code to get the value. */ virtual vm_obj_id_t gen_code_obj_predot(int *is_self) { /* generate the expression */ gen_code(FALSE, FALSE); /* tell the caller that the value is not a compile-time constant */ *is_self = FALSE; return VM_INVALID_OBJ; } /* * generic generation for a member expression after the left side * has been generated */ static void s_gen_member_rhs(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Get the property ID of this expression. If the property ID is * available as a constant value, this doesn't generate any code and * simply returns the constant property ID value. If this * expression requires run-time evaluation, we'll generate code for * the expression and return VM_INVALID_PROP to indicate that a * constant property ID is not available. * * If 'check_only' is true, this routine should only check to see * whether a constant property value is available and return the * appropriate indication, but should not generate any code in any * case. Errors should also be suppressed in this case, because the * routine will always be called again to perform the actual * generation, at which point any errors can be logged. * * If 'is_expr' is true, it means that this property was given as an * expression by being explicitly enclosed in parentheses. If * 'is_expr' is false, it means that the property was given as a * simple symbol name. For cases where the property expression is a * symbol, this distinction is important because we must resolve an * unparenthesized symbol as a property name, even if it's hidden by * a local variable. */ virtual vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* * generate a jump-ahead instruction, returning a new label which * serves as the jump destination */ static struct CTcCodeLabel *gen_jump_ahead(uchar opc); /* define the position of a code label */ static void def_label_pos(struct CTcCodeLabel *lbl); /* allocate a new label at the current write position */ static struct CTcCodeLabel *new_label_here(); }; /* ------------------------------------------------------------------------ */ /* * Basic T3-specific symbol class */ class CTcSymbol: public CTcSymbolBase { public: CTcSymbol(const char *str, size_t len, int copy, tc_symtype_t typ) : CTcSymbolBase(str, len, copy, typ) { } /* * get the object value for the symbol, if the symbol has an object * value; returns VM_INVALID_OBJ if the symbol is not an object */ virtual vm_obj_id_t get_val_obj() const { return VM_INVALID_OBJ; } /* generate code to evaluate the symbol */ virtual void gen_code(int discard) = 0; /* * Generate code to assign to the symbol. By default, we'll generate * an error indicating that the symbol cannot be assigned into. As * with CTcPrsNode::gen_code_asi(), this returns true if the assignment * was generated, false if the caller must generate a generic * assignment; simple assignment must always return true. * * If 'rhs' is null, the caller has already generated the value to be * assigned, so this code doesn't need to do that. Otherwise, this * code must generate the rvalue code. The reason that 'rhs' is passed * down at all is that we can sometimes optimize the type of opcode we * generate according to the type of value being assigned, especially * when a constant value is being assigned. * * If 'ignore_error' is true, this should not log an error if the value * cannot be assigned. * * 'xplicit' is true if this is an explicit assignment by user code, * false if it's an assignment generated automatically by the compiler. * Implicit assignments don't count for the purposes of unused value * warnings. These warnings are primarily to alert the user that they * could delete an unnecessary assignment from their code, or * conversely that they might have forgotten to do something with the * value that the assignment would suggest they intended to do, but * with a compiler-generated assignment there's nothing for the user to * delete to fix the warning. */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_error, int xplicit, void **ctx); /* * Generate code for taking the address of the symbol. By default, * we'll generate an error indicating that the symbol's address * cannot be taken. */ virtual void gen_code_addr(); /* * Generate code to call the symbol. By default, we can't call a * symbol. */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * generate code for operator 'new' applied to the symbol, with the * given number of arguments; by default, we can't generate any such * code */ virtual void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* evaluate a property ID */ virtual vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate code for a member expression */ virtual void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ virtual vm_obj_id_t gen_code_obj_predot(int *is_self); /* * Write the symbol to a local frame debugging record in the code * stream. Returns true if we wrote the symbol, false if not. By * default, we'll write nothing and return false, since most symbols * aren't used in local symbol tables. */ virtual int write_to_debug_frame(int test_only) { return FALSE; } /* * Write the symbol to the global symbol table in the debugging * records in an image file. Returns true if we wrote the symbol, * false if not. By default, we'll write nothing and return false, * since by default symbols don't go in the image file's global * symbol table. */ virtual int write_to_image_file_global(class CVmImageWriter *) { return FALSE; } }; #endif /* TCT3BASE_H */ frobtads-1.2.3/tads3/vmdate.h0000644000175000001440000005520211723543631015164 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdate.h - CVmObjDate object Function Defines the Date intrinsic class Notes Modified 01/23/12 MJRoberts - Creation */ #ifndef VMDATE_H #define VMDATE_H #include #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Service class for calendar date conversions. This converts between * calendar dates and day number values, using our Epoch of March 1, year * 0. */ struct caldate_t { /* create at the Epoch (3/1/0000 midnight UTC) */ caldate_t() : y(0), m(3), d(1) { } /* create from a calendar date */ caldate_t(int y, int m, int d) : y(y), m(m), d(d) { } /* create from a day number */ caldate_t(int32_t dayno) { set_dayno(dayno); } /* set to the current date (UTC) */ void set_now() { int32_t dayno, daytime; now(dayno, daytime); set_dayno(dayno); } /* get the Julian day number */ double julian_dayno() const { return dayno() + 1721119.5; } /* figure the date on the Julian calendar */ void julian_date(int &jy, int &jm, int &jd) { int32_t z = (int32_t)floor(julian_dayno() + 0.5); int32_t a = z; int32_t b = a + 1524; int32_t c = (int32_t)floor((b - 122.1) / 365.25); int32_t d = (int32_t)floor(365.25 * c); int32_t e = (int32_t)floor((b - d) / 30.6001); jm = e < 14 ? e - 1 : e - 13; jy = jm > 2 ? c - 4716 : c - 4715; jd = b - d - (int32_t)floor(30.6001 * e); } /* set my internal (Gregorian) date from a Julian date */ void set_julian_date(int jy, int jm, int jd) { /* adjust the month to 1-12 range */ div_t mq = div(jm - 1, 12); if (mq.rem <= 0) mq.rem += 12, mq.quot -= 1; jm = mq.rem + 1; jy += mq.quot; /* * Calculate the Julian day number for the julian date, then * subtract our Epoch's Julian day number to get our day number. */ if (jm <= 2) { jy -= 1; jm += 12; } set_dayno((int32_t)(floor(365.25 * (jy + 4716)) + floor(30.6001 * (jm + 1)) + jd - 1524.5 - 1721119.5)); } /* set from a day number */ void set_dayno(int32_t dayno) { int32_t y = (int32_t)floor((10000*(double)dayno + 14780)/3652425.0); int32_t d = dayno - (365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400)); if (d < 0) { y -= 1; d = dayno - (365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400)); } int32_t m = (100*d + 52)/3060; this->y = y + (m+2)/12; this->m = (m + 2)%12 + 1; this->d = d - (m*306 + 5)/10 + 1; } /* * Normalize the date. This takes the current m/d/y setting, * calculates the day number it represents, and then figure the m/d/y * for that day number. This ensures that a date entered with an * overflow in one of the fields (e.g., "February 30") is translated * into a proper calendar date. */ void normalize_date() { set_dayno(dayno()); } /* * Integer divide-and-get-floor calculation - i.e., round towards * negative infinity. This is the same as ordinary C integer division * for positive numbers, but for negative numbers most C * implementations round towards zero. */ static inline int32_t divfl(int32_t a, int32_t b) { ldiv_t ld = ldiv(a, b); return (ld.rem < 0 ? ld.quot - 1 : ld.quot); } /* get the day number of this calendar date */ int32_t dayno() const { /* * Adjust the month to our odd range where the year starts in * March: 0 = March, 1 = April, ... 11 = Feb. (If this yields a * negative remainder, adjust to a positive index by going back a * year.) */ div_t mq = div(this->m - 3, 12); if (mq.rem < 0) mq.rem += 12, mq.quot -= 1; int m = mq.rem; int y = this->y + mq.quot; return 365*y + divfl(y, 4) - divfl(y, 100) + divfl(y, 400) + (m*306 + 5)/10 + (d-1); } /* day number of the Unix Epoch (1/1/1970 UTC) */ static const int32_t UNIX_EPOCH_DAYNO = 719468; /* get the weekday for this date (0=Sunday, 1=Monday, etc) */ int weekday() const { const static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; int yy = y - (m < 3 ? 1 : 0); return (yy + yy/4 - yy/100 + yy/400 + t[m-1] + d) % 7; } /* get the ISO weekday for this date (1=Monday, ... 7=Sunday) */ int iso_weekday() const { int w = weekday(); return w == 0 ? 7 : w; } /* * Figure the week number, where week 1 starts on the first * of the year (0=Sunday). */ int weekno(int weekday) const { /* * Figure days until the first of the year: that's the * start of week 1, so the day number of the start of week 1 is the * jan1 day number plus this delta. The start of week 0 is 7 days * before that. */ caldate_t jan1(y, 1, 1); int delta = (7 + weekday - jan1.weekday()) % 7; int week0_dayno = jan1.dayno() + delta - 7; /* figure which week we're in and return the result */ return (dayno() - week0_dayno)/7; } /* * Figure the ISO-8601 week number of this date. Week 1 is the week * containing the year's first Thursday, and the first day of each week * is Monday. Fills in 'year' (if provided) with the year the week * belongs to, which might be the previous or next calendar year. */ int iso_weekno(int *year) const { /* * Find the Thursday of the current ISO week. To do this, figure * our weekday, subtract the ISO weekday number minus 1 to get the * nearest preceding Monday (the start of the ISO week), then add 3 * to get the following Thursday. The year containing the Thursday * of a given week is by definition the year which that whole week * (starting on Monday) belongs to. */ int wday = iso_weekday(); int32_t this_thu_dayno = dayno() - (wday-1) + 3; caldate_t this_thu(this_thu_dayno); /* the year containing the Thursday is the week's calendar year */ if (year != 0) *year = this_thu.y; /* * Find the first Thursday of the calendar year we just calculated. * ISO week 1 of a given calendar year is the week that contains * the first Thursday of the year. So find the first Thursday on * or after Jan 1. */ caldate_t jan1(this_thu.y, 1, 1); int jan1_wday = jan1.weekday(); int32_t first_thu_dayno = jan1.dayno() + ((11 - jan1_wday) % 7); /* * Calculate the number of weeks (== the number of days divided by * seven) between the Thursday of the target week and the Thursday * of the first week. This gives us the difference in week * numbers; since the first week is week #1, adding 1 to the * difference gives us the target week number. */ return ((this_thu_dayno - first_thu_dayno)/7) + 1; } /* get the current system date/time (UTC) */ static void now(int32_t &dayno, int32_t &daytime); /* normalize a date/time to bring the time within 00:00-24:00 */ static void normalize(int32_t &dayno, int32_t &daytime); /* calendar year, month (1=January), and day of month */ int y; int m; int d; }; /* ------------------------------------------------------------------------ */ /* * Multi-calendar interface. This virtualizes the calculations in * caldate_t for different calendars. */ struct multicaldate_t { virtual ~multicaldate_t() { } virtual int32_t dayno() const = 0; virtual void set_dayno(int32_t d) = 0; virtual int weekday() const = 0; virtual int iso_weekday() const = 0; virtual int iso_weekno(int *year) const = 0; /* get the day number of January 1 of year 'y' */ virtual int32_t jan1_dayno() const = 0; /* * get the week number for this date, for the week starting on the * given weekday */ int weekno(int weekday) const { /* * Figure days until the first of the year: that's the * start of week 1, so the day number of the start of week 1 is the * jan1 day number plus this delta. The start of week 0 is 7 days * before that. */ caldate_t jan1(jan1_dayno()); int delta = (7 + weekday - jan1.weekday()) % 7; int week0_dayno = jan1.dayno() + delta - 7; /* figure which week we're in and return the result */ return (dayno() - week0_dayno)/7; } virtual void set_date(int y, int m, int d) = 0; int y() const { return y_; } int m() const { return m_; } int d() const { return d_; } protected: /* year, month, day on the subclass calendar */ int y_, m_, d_; }; struct gregcaldate_t: multicaldate_t { gregcaldate_t() { } gregcaldate_t(int32_t d) { gregcaldate_t::set_dayno(d); } virtual int32_t dayno() const { return cd.dayno(); } virtual void set_dayno(int32_t d) { cd.set_dayno(d); y_ = cd.y; d_ = cd.d; m_ = cd.m; } virtual void set_date(int y, int m, int d) { y_ = cd.y = y; m_ = cd.m = m; d_ = cd.d = d; } virtual int32_t jan1_dayno() const { caldate_t jan1(y_, 1, 1); return jan1.dayno(); } virtual int weekday() const { return cd.weekday(); } virtual int iso_weekday() const { return cd.iso_weekday(); } virtual int iso_weekno(int *year) const { return cd.iso_weekno(year); } caldate_t cd; }; struct julcaldate_t: multicaldate_t { virtual int32_t dayno() const { return cd.dayno(); } virtual void set_dayno(int32_t d) { cd.set_dayno(d); cd.julian_date(y_, m_, d_); } virtual void set_date(int y, int m, int d) { cd.set_julian_date(y, m, d); y_ = y; m_ = m; d_ = d; } virtual int32_t jan1_dayno() const { caldate_t jan1; jan1.set_julian_date(y_, 1, 1); return jan1.dayno(); } virtual int weekday() const { return cd.weekday(); } virtual int iso_weekday() const { return cd.iso_weekday(); } virtual int iso_weekno(int *year) const { return cd.iso_weekno(year); } caldate_t cd; }; /* ------------------------------------------------------------------------ */ /* * A Date value is stored internally as two 32-bit integers: the day number * of the date, which is the number of days since March 1, year 0, midnight * UTC; and the time of day as the number of milliseconds past midnight * (UTC) on that day. * * The image file data block is arranged as follows: * *. UINT4 day_number *. UINT2 time_of_day_ms */ /* ------------------------------------------------------------------------ */ /* * Forward declarations */ typedef struct date_parse_result date_parse_result; /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This contains the date (as a * Gregorian day number since March 1, 0000 UTC), time of day (as the * number of milliseconds past midnight), and the TimeZone object that we * use to determine the local time zone for formatting the date as a string * or extracting calendar or clock elements. */ struct vm_date_ext { /* allocate the structure */ static vm_date_ext *alloc_ext(VMG_ class CVmObjDate *self); /* day number - number of days since March 1, year 0000 UTC */ int32_t dayno; /* time of day - number of milliseconds past midnight UTC on dayno*/ int32_t daytime; }; /* ------------------------------------------------------------------------ */ /* * CVmObjDate intrinsic class definition */ class CVmObjDate: public CVmObject { friend class CVmMetaclassDate; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjDate object? */ static int is_date_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* get my date and time values */ int32_t get_dayno() const { return get_ext()->dayno; } int32_t get_daytime() const { return get_ext()->daytime; } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create with a given timestamp */ static vm_obj_id_t create(VMG_ int in_root_set, int32_t dayno, int32_t daytime); /* create from an os_time_t value */ static vm_obj_id_t create_from_time_t(VMG_ int in_root_set, os_time_t t); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to a string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *newstr) const; /* format into a string buffer */ void format_string_buf(VMG_ char *buf, size_t buflen) const; /* format using a template */ size_t format_date(VMG_ char *buf, size_t buflen, const char *fmt, size_t fmtlen, class CVmDateLocale *lc, const multicaldate_t *date, int32_t dayno, int32_t daytime, const char *tzabbr, int32_t tzofs) const; /* date arithmetic - add an integer or BigNumber to add days */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* * date arithmetic - subtract an integer or BigNumber to subtract days * from the date, or subtract another Date to calculate the number of * days between the dates */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* compare two dates */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; int compare_to(VMG_ vm_obj_id_t self, const vm_val_t *val) const; /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record - we're immutable, so there's no undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *) { } /* mark our undo record references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references (we have none) */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* Date objects are immutable, so they can't change after loading */ int is_changed_since_load() const { return FALSE; } protected: /* create a with no initial contents */ CVmObjDate() { ext_ = 0; } /* create with a given timestamp */ CVmObjDate(VMG_ int32_t dayno, uint32_t daytime); /* get my extension data */ vm_date_ext *get_ext() const { return (vm_date_ext *)ext_; } /* get my date/time in the given time zone; returns the format abbr */ const char *get_local_time( int32_t &dayno, int32_t &daytime, class CVmTimeZone *tz) const { int32_t ofs; return get_local_time(dayno, daytime, ofs, tz); } /* * get my date/time in the given time zone, filling in the time zone's * offset from UTC in milliseconds */ const char *get_local_time( int32_t &dayno, int32_t &daytime, int32_t &tzofs, class CVmTimeZone *tz) const; /* get my date (as a day number since 1/1/0000) in the local time zone */ int32_t get_local_date(class CVmTimeZone *tz) const { int32_t dayno, daytime; (void)get_local_time(dayno, daytime, tz); return dayno; } /* get a TimeZone argument to one of our methods */ static class CVmTimeZone *get_tz_arg(VMG_ uint argn, uint argc); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* internal parsing routines */ static int parse_string_fmt( VMG_ date_parse_result *res, const char *&str, size_t &len, const char *fmt, size_t fmtlen, class CVmDateLocale *lc); static int parse_date_string( VMG_ int32_t &dayno, int32_t &daytime, const char *str, const vm_val_t *custom, class CVmDateLocale *lc, multicaldate_t *cal, int32_t refday, int32_t reftime, CVmTimeZone *reftz, struct date_parse_result *resultp, struct date_parse_string *fmtlist, int *nfmtlist); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* parseDate method */ int getp_parseDate(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_parseDate(vmg_ retval, argc); } /* static property evaluator for parseDate */ static int s_getp_parseDate(VMG_ vm_val_t *retval, uint *argc); /* parseJulianDate method */ int getp_parseJulianDate(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_parseJulianDate(vmg_ retval, argc); } /* static property evaluator for parseDate */ static int s_getp_parseJulianDate(VMG_ vm_val_t *retval, uint *argc); /* common handler for parseDate, parseJulianDate */ static int common_parseDate(VMG_ vm_val_t *retval, uint *argc, multicaldate_t *cal); /* general handler for formatDate, formatJulianDate */ int formatDateGen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, multicaldate_t *date); /* formatDate method */ int getp_formatDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* formatJulianDate method */ int getp_formatJulianDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* compareTo */ int getp_compareTo(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getDate method */ int getp_getDate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getJulianDay method */ int getp_getJulianDay( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getJulianDate method */ int getp_getJulianDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getISOWeekDate */ int getp_getISOWeekDate( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getTime method */ int getp_getTime(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* addInterval method */ int getp_addInterval(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* findWeekday method */ int getp_findWeekday(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* setLocaleInfo method */ int getp_setLocaleInfo(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc) { return s_getp_setLocaleInfo(vmg_ retval, argc); } /* static property evaluator for setLocalInfo */ static int s_getp_setLocaleInfo(VMG_ vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjDate::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjDate metaclass registration table object */ class CVmMetaclassDate: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "date/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDate(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjDate(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjDate::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjDate::call_stat_prop( vmg_ result, pc_ptr, argc, prop); } int set_stat_prop(VMG_ class CVmUndo *, vm_obj_id_t /*self*/, vm_val_t * /* class_state */, vm_prop_id_t /*prop*/, const vm_val_t * /*val*/) { return TRUE; } }; #endif /* VMDATE_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjDate) frobtads-1.2.3/tads3/tcerrmsg.cpp0000644000175000001440000044561712145504453016100 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcerrmsg.cpp - TADS 3 Compiler error message text Function Defines the error message strings for the TADS 3 compiler. The message strings are all isolated in this module to allow for easy replacement for translated versions. Notes Modified 05/13/00 MJRoberts - Creation */ #include "tcerr.h" #include "tcerrnum.h" /* ------------------------------------------------------------------------ */ /* * Error Messages - fixed English version; these can be replaced at * run-time by messages loaded from an external file, but we compile in an * English set as a fallback in case there's no message file. * * The messages must be sorted by message number, so that we can perform a * binary search to look up a message by number. */ const err_msg_t tc_messages_english[] = { { TCERR_LINE_MEM, "out of memory for line source", "Out of memory for line source. The source line may be too long, " "or you may need to make more memory available to the compiler " "by closing other applications." }, { TCERR_INV_PP_DIR, "invalid preprocessor directive", "\"%~.*s\" is not a valid preprocessor '#' directive." }, { TCERR_CANT_LOAD_CHARSET, "unable to load #charset", "Unable to load the character set specified in the #charset directive. " "Check the spelling of the character set name, and make sure that " "a character mapping file (with a name ending in \".TCM\") is " "available. Refer to the compiler's installation notes for your " "type of computer for details." }, { TCERR_UNEXPECTED_CHARSET, "unexpected or invalid #charset", "Unexpected or invalid #charset directive. This directive must " "be at the very beginning of the file, and must specify a character " "set name enclosed in double quotes." }, { TCERR_BAD_INC_SYNTAX, "syntax error in #include", "Invalid #include syntax - the filename must be enclosed with " "'\"' or '< >' characters." }, { TCERR_INC_NOT_FOUND, "cannot open include file \"%.*s\"", "The compiler cannot access the #include file \"%.*s\". " "Check the filename to ensure that it's spelled correctly, and check " "the compiler's include path setting (command line \"-I\" options) " "to ensure that the compiler is searching in the directory or folder " "containing the file. If the file exists, make sure that it's not " "being used by another application and that you have permission " "to read the file." }, { TCERR_REDUNDANT_INCLUDE, "file \"%.*s\" previously included; ignored", "The #include file \"%.*s\" has already been included. " "This redundant inclusion will be ignored." }, { TCERR_BAD_DEFINE_SYM, "invalid symbol \"%~.*s\" for #define", "Invalid symbol \"%~.*s\" for #define. A #define symbol must start " "with an ASCII letter or underscore symbol '_', and must contain " "only ASCII letters, digits, and underscores." }, { TCERR_MACRO_NO_RPAR, "missing ')' in macro parameter list", "The macro parameter list is missing a right parenthesis ')' at " "the end of the list." }, { TCERR_BAD_MACRO_ARG_NAME, "invalid macro parameter name \"%~.*s\"", "Invalid macro parameter name \"%~.*s\". A macro parameter name must " "start with an ASCII letter or underscore symbol '_', and must contain " "only ASCII letters, digits, and underscores." }, { TCERR_MACRO_EXP_COMMA, "expected ',' or ')' in macro parameter list (found \"%~.*s\")", "Expected a comma ',' or right parenthesis ')' in the macro " "parameter list, but found \"%~.*s\"." }, { TCERR_MACRO_REDEF, "redefinition of macro \"%~.*s\"", "Macro \"%~.*s\" has been previously defined; this new definition " "replaces the previous definition." }, { TCERR_UNKNOWN_PRAGMA, "unrecognized #pragma \"%~.*s\"; ignored", "Unrecognized #pragma \"%~.*s\"; ignored. (The compiler ignores " "#pragma directives that it doesn't understand, in case you're " "using a #pragma meant for another compiler, but if you meant " "the #pragma for this compiler, you need to correct a problem " "with it.)" }, { TCERR_BAD_PRAGMA_SYNTAX, "invalid syntax for #pragma", "Invalid syntax for #pragma." }, { TCERR_PP_EXTRA, "extra characters on line", "Extra characters found after the end of the preprocessor directive. " "Check the syntax and remove the extraneous characters at the " "end of the line." }, { TCERR_IF_NESTING_OVERFLOW, "#if nesting too deep", "#if/#ifdef nesting is too deep - you have specified more " "#if's within other #if's than the compiler allows. (The compiler " "has an internal limit on this nesting; you must simplify the " "nested #if structure of your source code.)" }, { TCERR_PP_ELSE_WITHOUT_IF, "#else without #if", "#else without a matching #if or #ifdef." }, { TCERR_PP_ENDIF_WITHOUT_IF, "#endif without #if", "#endif without a matching #if or #ifdef." }, { TCERR_PP_ELIF_WITHOUT_IF, "#elif without #if", "#elif without a matching #if." }, { TCERR_PP_INT_REQUIRED, "integer value required in preprocessor constant expression", "Incorrect value in preprocessor constant expression - an integer " "value is required." }, { TCERR_PP_INCOMP_TYPES, "incompatible types for comparison operator", "Incompatible types for comparison in preprocessor constant " "expression." }, { TCERR_PP_EXPR_EXTRA, "extra characters at end of line", "Extra characters found after end of preprocessor constant " "expression. Check the syntax of the expression and remove any " "extraneous characters at the end of the line." }, { TCERR_PP_DIV_ZERO, "divide by zero in constant expression", "Division by zero in preprocessor constant expression." }, { TCERR_PP_INVALID_VALUE, "expected number, symbol, or string (found \"%~.*s\")", "Expected a number, symbol, or single-quoted string in preprocessor " "constant expression, but found \"%~.*s\"." }, { TCERR_PP_UNTERM_STRING, "unterminated string in preprocessor constant expression", "Unterminated string found in a preprocessor constant expression. " "(A string in a preprocessor expression must be contained entirely " "on a single line.)" }, { TCERR_PP_UNMATCHED_LPAR, "unmatched '('", "Unmatched left parenthesis '(' in preprocessor constant expression." }, { TCERR_PP_BAD_NOT_VAL, "integer value required for '!' operator", "Integer value is required for '!' operator in preprocessor " "expression." }, { TCERR_PP_MACRO_ARG_RPAR_1LINE, "missing ')' in argument list for macro \"%~.*s\"", "Missing right parenthesis ')' in argument list in invocation of " "macro \"%~.*s\". The entire argument list must be on a single " "logical line for a line with a preprocessor directive (but you can " "extend the logical line over several physical lines by ending each " "line but the last with a backslash '\\')." }, { TCERR_PP_NO_MACRO_ARGS, "missing argument list for macro \"%~.*s\"", "An argument list must be specified in invocation of macro \"%~.*s\"." }, { TCERR_PP_FEW_MACRO_ARGS, "not enough arguments for macro \"%~.*s\"", "Not enough arguments are specified in invocation of macro \"%~.*s\"." }, { TCERR_PP_MACRO_ARG_RPAR, "missing ')' in argument list for macro \"%~.*s\"", "Missing right parenthesis ')' in argument list in " "invocation of macro \"%~.*s\"." }, { TCERR_PP_MANY_MACRO_ARGS, "too many arguments for macro \"%~.*s\"", "Too many arguments found in invocation of macro \"%~.*s\". (The " "macro was defined to take fewer arguments. Check the definition " "of the macro for proper usage, and make sure that all of the " "parentheses in the macro invocation are properly matched.)" }, { TCERR_PP_DEFINED_NO_SYM, "symbol required for defined() (found \"%~.*s\")", "Symbol required for defined() preprocessor operator " "(found \"%~.*s\" instead.)" }, { TCERR_PP_DEFINED_RPAR, "missing ')' in defined()", "Missing right parenthesis ')' in defined() preprocess operator." }, { TCERR_SYMBOL_TRUNCATED, "symbol \"%~.*s\" truncated to \"%~.*s\"", "The symbol \"%~.*s\" is too long; it has been truncated to \"%~.*s\". " "(The compiler limits the length of each symbol name; you must make " "this symbol name shorter.)" }, { TCERR_TOO_MANY_MAC_PARMS, "too many parameters for macro \"%~.*s\" (maximum %d)", "Too many formal parameters are defined for macro " "\"%~.*s\". (The compiler imposes a limit of %d parameters per macro.)" }, { TCERR_NO_STRBUF_MEM, "out of memory for string buffer", "Out of memory for string buffer. You may need to " "close other applications to make more memory available for " "the compiler." }, { TCERR_CANT_OPEN_SRC, "unable to open source file \"%.*s\"", "Unable to open source file \"%.*s\" - check that the filename " "is spelled correctly, and check that the file exists and that you " "have permission to open the file for reading." }, { TCERR_ERROR_DIRECTIVE, "#error : %.*s", "#error : %.*s" }, { TCERR_OUT_OF_MEM_MAC_EXP, "out of memory for macro expansion", "Out of memory for macro expansion. You may need to " "close other applications to make more memory available for " "the compiler." }, { TCERR_LINE_REQ_INT, "integer value required for #line", "An integer value is required for the line number in the #line " "directive." }, { TCERR_LINE_FILE_REQ_STR, "string value required for #line", "A string value is required for the filename in the #line directive." }, { TCERR_IF_WITHOUT_ENDIF, "#if without #endif (line %ld, file %.*s)", "The #if directive at line %ld of file %.*s has no matching #endif " "(a matching #endif is required in the same file as the #if)." }, { TCERR_UNSPLICE_NOT_CUR, "unsplicing invalid line", "Unsplicing invalid line." }, { TCERR_MULTI_UNSPLICE, "too much unsplicing", "Too much unsplicing." }, { TCERR_PP_ELIF_NOT_IN_SAME_FILE, "#elif without #if", "#elif without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #elif appears to " "correspond to a #if in the including file." }, { TCERR_PP_ELSE_NOT_IN_SAME_FILE, "#else without #if", "#else without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #else appears to " "correspond to a #if in the including file." }, { TCERR_PP_ENDIF_NOT_IN_SAME_FILE, "#endif without #if", "#endif without #if - an entire #if-#elif-#else-#endif sequence must " "all be contained within a single file; this #endif appears to " "correspond to a #if in the including file." }, { TCERR_REDEF_OP_DEFINED, "cannot #define reserved preprocessor symbol \"defined\"", "You cannot #define \"defined\" as a macro - this symbol is reserved " "as a preprocessor keyword and cannot be defined as a macro name." }, { TCERR_POSSIBLE_UNTERM_STR, "string appears unterminated (';' or '}' appears alone on line %ld)", "This string appears unterminated, because ';' or '}' appears on a line " "with no other non-blank characters (at line %ld) within the string. " "Check the string for proper termination; if the string is " "properly terminated and the ';' or '}' is meant to be part of the " "string, move the ';' or '}' onto the next or previous line to group " "it with at least one other non-whitespace character, so that it " "doesn't confuse the compiler" }, { TCERR_SRCLINE_TOO_LONG, "source line too long (exceeds maximum line length %ld bytes)", "This source line is too long - it exceeds the internal compiler " "limit of %ld bytes per source line. (The compiler's idea of the " "length of the logical source line may be longer than the line appears " "to be in the source file, because the compiler limit applies to the " "line after expansion of macros, assembly of all parts of any string " "that runs across several lines into a single line, and splicing of " "any lines that end in backslash characters '\\'. You must reduce " "the length of the logical source line; check in particular for any " "quoted strings that run across several lines. " }, { TCERR_INVALID_CHAR, "invalid character in input \"%~.*s\"", "Invalid character in input: \"%~.*s\". This character is not valid " "in any symbol name or as punctuation in source code; the character " "will be ignored. Check for missing quotes around a string, a missing " "ending quote for a string just before this point, or for " "a quote mark embedded within an earlier string (if you want to use " "a quote mark within a string, you must precede the quote mark with " "a backslash '\\')." }, { TCERR_PP_QC_MISSING_COLON, "missing colon ':' after conditional operator '?' in preprocessor " "expression", "The preprocessor constant conditional expression on this line is " "missing the colon ':' part. A question-mark operator '?' must be " "followed by the true-part, then a colon ':', then the false-part. " "Check the expression to ensure proper placement of parentheses, " "and check for other errors in the expression syntax. " }, { TCERR_PP_EXPR_NOT_CONST, "preprocessor expression is not a constant value", "The preprocessor expression given does not have a constant value. " "A preprocessor expression (in a #if, #elif, or #line directive) " "must use only numbers and strings, or #define symbols defined as " "numbers or strings. Preprocessor expressions cannot include " "variables, function calls, property references, or other values " "that do not have a constant value during compilation." }, { TCERR_CANT_LOAD_DEFAULT_CHARSET, "can't load mapping file for default character set \"%s\"", "The compiler cannot open the mapping file for the default " "character set, \"%s\". Make sure that a mapping file for this " "character set (with the same name as the character set plus the " "suffix \".TCM\") is properly installed on your system. Refer " "to the compiler's installation notes for your type of " "computer for details. You might need to re-install the compiler." }, { TCERR_MACRO_ELLIPSIS_REQ_RPAR, "'...' in macro formal parameter list must be followed by ')' - " "found \"%~.*s\"", "An ellipsis '...' in a macro formal parameter list can only be " "used with the last parameter to the macro, so it must be immediately " "followed by a close parenthesis ')' - the compiler found \"%~.*s\" " "instead. Check the macro definition syntax and insert the missing " "parenthesis." }, { TCERR_PP_FOREACH_TOO_DEEP, "#foreach/#ifempty/#ifnempty nesting too deep", "This line uses a macro whose expansion has too many nested uses " "of #foreach, #ifempty, and #ifnempty for its variable arguments. " "You must simplify the expansion text to reduce the nesting (in " "other words, you must reduce the number of these constructs that " "are contained within the expansion areas of others of these " "same constructs)." }, { TCERR_TRAILING_SP_AFTER_BS, "line ends with whitespace following backslash", "This line ends with one or more whitespace characters (space, tab, " "etc.) following a backslash '\\'. A backslash at the end of a line " "is most frequently used to indicate a continuation line, where " "a long preprocessor directive is broken up over several lines " "in the source file. To indicate line continuation, though, the " "backslash must be the very last character on the line; because of " "the extra whitespace after the backslash on this line, the " "preprocessor must treat the backslash as quoting the whitespace " "character that follows. If you meant to indicate line continuation, " "remove the trailing whitspace. If you actually intended to escape " "the whitespace character, you can suppress this warning by adding " "a comment (a simple '//' is adequate) at the end of the line." }, { TCERR_EXTRA_INC_SYNTAX, "extraneous characters following #include filename", "This #include line has extra characters after the name of the file. " "The only thing allowed on a #include line is the filename, enclosed " "in quotes (\"filename\") or angle brackets (). This line " "has extra characters after the filename specification. Check the " "syntax and remove the extraneous text. If the extra text is " "supposed to be a comment, make sure the comment syntax is correct." }, { TCERR_NESTED_COMMENT, "\"/*\" found within \"/* ... */\" - nested comments are not allowed", "The compiler found \"/*\" within a block comment (a \"/* ... */\" " "sequence). This type of comment cannot be nested. If you didn't " "intend to create a nested comment, the problem could be that the " "previous block comment is missing its \"*/\" end marker - you might " "want to check the previous comment to make sure it ended properly. " "If you did want to create a nested comment, consider using \"//\" " "comments instead, or you can use #if 0 ... #endif to comment out " "a large block." }, { TCERR_UNMAPPABLE_CHAR, "unmappable character in input", "The compiler encountered an \"unmappable\" character in the input. " "This is a character that's not defined as part of the source file " "character set you're using, so the compiler doesn't know how to " "interpret it. This can happen if you've declared the file to be " "in plain ASCII, via a #charset \"us-ascii\" directive, but the file " "actually contains characters outside of the plain ASCII range. " "The same can happen with the ISO-8859 (Latin-1, etc) character sets, " "since these do not define all possible character codes. Check for " "any accented letters or special symbols. If you don't see any of " "these, there might be invisible control characters or spaces causing " "the problem. If you are intentionally using accented characters, " "add a #charset directive to the start of the file to indicate the " "correct character set that your text editor is using when saving " "the source file." }, { TCERR_DECIMAL_IN_OCTAL, "decimal digit found in octal constant \"%.*s\"", "The compiler found a decimal digit (an 8 or a 9) within the octal " "constant value \"%.*s\". When you start a numeric constant with the " "digit zero (0), it signifies an octal constant - that is, a number " "written in base-8 notation. Octal numbers can only contain the " "digits 0 through 7, so you can't use an 8 or a 9 in this type of " "constant. If you didn't mean this to be interpreted as an octal " "value, simply remove the leading zero. Otherwise, remove the " "invalid digits from the octal number." }, { TCERR_LTGT_OBSOLETE, "the '<>' operator is obsolete - use '!=' instead", "The '<>' operator is obsolete - use '!=' instead. TADS 2 treated " "'<>' as equivalent to '!=', but TADS 3 doesn't allow '<>', because " "the varying operator syntax was sometimes confusing to people " "reading source code." }, { TCERR_INT_CONST_OV, "constant value exceeds integer range; promoted to BigNumber", "The numeric value specified is outside of the range that can be " "stored in the TADS integer type (-2147483648 to +2147483647), so it " "has been automatically promoted to a BigNumber (floating point) " "value. BigNumber values can represent much larger numbers than " "TADS integers, but some functions that require numeric arguments " "only accept integer values. If you're using this value in such a " "context, it might cause an error at run-time." }, { TCERR_BACKSLASH_SEQ, "invalid backslash escape sequence \\%c in string", "The backslash sequence \\%c is not valid." }, { TCERR_NON_ASCII_SYMBOL, "symbol \"%~.*s\" contains non-ASCII character (U+%04x)", "The symbol \"%~.*s\" contains one or more non-ASCII characters (the " "first is [Unicode] character U+%04x). Only plain ASCII characters " "can be used in symbols; accented letters and other non-ASCII " "characters aren't allowed." }, { TCERR_EMBEDDING_TOO_DEEP, "embedded expressions in strings are nested too deeply", "The embedded \"<< >>\" expressions in this string are nested too " "deeply. A nested embedding is a \"<< >>\" expression within " "a string that iself contains another string that has its own " "\"<< >>\" embedding, which in turn has another string with its " "own embedding, and so on. This is allowed, but only to a limited " "nesting depth. You will need to simplify the strings to reduce " "the depth." }, { TCERR_INTERNAL_EXPLAN, "Please report this error to the compiler's maintainer.", "This indicates a problem within the compiler itself. Please report " "this problem to the compiler's maintainer -- refer to the README file " "or release notes for contact information." }, { TCERR_INTERNAL_ERROR, "general internal error", "general internal error" }, { TCERR_MAKE_CANNOT_CREATE_SYM, "error creating symbol file \"%s\"", "Error creating symbol file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_CREATE_OBJ, "error creating object file \"%s\"", "Error creating object file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_CREATE_IMG, "error creating image file \"%s\"", "Error creating image file \"%s\". Check to make sure the filename " "contains only valid characters, that the path is valid, and that " "you have the required permissions to create the file." }, { TCERR_MAKE_CANNOT_OPEN_SYM, "error opening symbol file \"%s\" for reading", "Error opening symbol file \"%s\" for reading. Check that the " "file exists and that its name and path are valid." }, { TCERR_MAKE_CANNOT_OPEN_OBJ, "error opening object file \"%s\" for reading", "Error opening object file \"%s\" for reading. Check that the " "file exists and that its name and path are valid." }, { TCERR_MAKE_CANNOT_OPEN_IMG, "error opening image file \"%s\" for reading", "Error opening image file \"%s\" for reading. Since this is an " "intermediate file created during the build process, this probably " "indicates a problem with the filename path, directory permissions, " "or disk space." }, { TCERR_TOO_MANY_ERRORS, "too many errors (try fixing the first couple of errors and recompiling)", "Too many errors - aborting compilation. Don't panic! Your source " "code probably doesn't have nearly as many errors as the compiler " "is reporting. In all likelihood, the compiler got tripped up after " "the first couple of errors and hasn't been able to get itself " "re-synchronized with your code - it thinks there are errors only " "because it's trying to interpret the code in the wrong context. You " "should simply fix the first few errors, then try compiling your " "program again - chances are the compiler will get a lot further " "if you fix just the first few errors. If you want, you can save a " "copy of the current source code and send it to TADS's author, who " "might be able to make the compiler a little smarter about dealing " "with whatever is confusing it so badly." }, { TCERR_MODULE_NAME_COLLISION, "module %s the has same name as an existing module", "The module \"%s\" has the same name as an existing module elsewhere " "in the project. The root filename of each module must be unique, " "because the two modules' object files might otherwise overwrite one " "another. You must change the name of one of the modules so that " "each module has a unique name." }, { TCERR_MODULE_NAME_COLLISION_WITH_LIB, "module %s has the same name as an existing module from library \"%s\"", "The module %s has the same name as an existing module included " "from the library \"%s\". The root filename of each module must be " "unique, even for files included from libraries. If more than one " "module has the same root name, the modules' object files would " "overwrite one another. You must change the name of this module " "to a name not used anywhere else in the project." }, { TCERR_SOURCE_FROM_LIB, "\"%s\" (from library \"%s\")", "\"%s\" (from library \"%s\")" }, { TCERR_CANNOT_CREATE_DIR, "unable to create directory \"%s\"", "An error occurred creating the directory/folder \"%s\". This might " "mean that the name contains invalid characters, the disk is " "full, or you don't have the necessary permissions or privileges " "to create a new file in the parent folder." }, { TCERR_CONST_DIV_ZERO, "divide by zero in constant expression", "Division by zero in constant expression. (The compiler was trying " "to calculate the value of this expression because it appears to be " "constant. Check for any macros that might expand to zero, and check " "for proper placement of parentheses.)" }, { TCERR_NO_MEM_PRS_TREE, "out of memory - cannot allocate parse tree block", "Out of memory. (The compiler was trying to allocate space for a " "\"parse tree block,\" which stores an intermediate form of your " "program source code during compilation. If possible, make more " "memory available to the compiler by closing other applications or " "reconfiguring any background tasks, services, or device drivers " "that can be changed to use less memory.)" }, { TCERR_PRS_BLK_TOO_BIG, "parse block too big (size=%ld)", "Parse block too big (size=%ld)." }, { TCERR_INVALID_LVALUE, "invalid lvalue - cannot assign to expression on left of \"%s\"", "Invalid \"lvalue\". The expression on the left-hand side of the " "operator \"%s\" cannot be used as the destination of an assignment. " "You can only assign values to expressions such as local variables, " "object properties, and list elements. " "Check for missing or extra parentheses and operators. " }, { TCERR_QUEST_WITHOUT_COLON, "missing ':' in '?' conditional expression", "The ':' part of a '?' conditional expression is missing. A '?' " "expression uses the syntax (condition ? true-part : false-part). " "Check for missing parentheses or other errors in the " "condition or true-part expression." }, { TCERR_INVALID_UNARY_LVALUE, "invalid lvalue - cannot apply \"%s\" operator to expression", "The \"%s\" operator cannot be applied to this expression. This " "operator can only be applied to an expression that can be used in " "an assignment, such as local variables, object properties, and " "list elements. Check for missing or extra parentheses." }, { TCERR_DELETE_OBSOLETE, "operator 'delete' is obsolete; expression has no effect", "The 'delete' operator is obsolete. The compiler still accepts " "'delete' expressions, but they have no effect at run-time. (The " "run-time system now provides automatic deletion of objects as " "they become unreachable, so explicit deletion is no longer " "necessary. You can simply remove all 'delete' expressions from " "your program.)" }, { TCERR_NO_ADDRESS, "invalid address expression - can't apply '&' operator", "The unary '&' operator cannot be applied to this expression. You " "can only apply the '&' prefix operator to function names and property " "names. Check the expression after the '&' to make sure it is a valid " "function or property name, and check for other expression errors, " "such as unbalanced parentheses." }, { TCERR_CONST_UNARY_REQ_NUM, "unary '%s' operator requires numeric value in constant expression", "The '%s' operator must be followed by a numeric value in a constant " "expression. The value after the operator is a constant value, but " "is not a number." }, { TCERR_CONST_BINARY_REQ_NUM, "binary '%s' operator requires numeric value in constant expression", "The '%s' operator must be applied only to numeric values in a constant " "expression. Both of the values are constants, but both are not " "numbers." }, { TCERR_CONST_BINPLUS_INCOMPAT, "incompatible constant types for two-operand '+' operator", "The constant types in this expression are not compatible for use " "with the two-operand '+' operator. You can add two numbers, or add a " "non-list value to a string, or add a value to a list; other " "combinations are not allowed." }, { TCERR_EXPR_MISSING_RPAR, "expected ')' but found \"%~.*s\"", "This expression is missing a right parenthesis ')' - \"%~.*s\" is " "where the ')' should go. The parenthesis is required to " "match an earlier left parenthesis '('. Check to make sure the " "parentheses are properly balanced, and check for unterminated " "strings and missing operators." }, { TCERR_BAD_PRIMARY_EXPR, "expected integer, string, symbol, '[', or '(', but found \"%~.*s\"", "Invalid expression; expected an integer value, a string value (in " "single or double quotes), a symbolic name (such as a function, " "object, or property name), a list constant enclosed in square " "brackets '[ ]', or an expression in parentheses '( )', but " "found \"%~.*s\"." }, { TCERR_CONST_BAD_COMPARE, "incompatible types for comparison in constant expression", "This constant expression contains a comparison operator ('<', '<=', " "'>', or '>=') that attempts to compare values of incompatible types " "(you can compare an integer to an integer, a string to a string, " "or a floating-point value to another floating point value). Check " "the expression and correct the invalid comparison." }, { TCERR_EXPECTED_SEMI, "expected ';', but found \"%~.*s\"", "Expected a semicolon ';' but found \"%~.*s\". Please add the " "required semicolon. If a semicolon is already present, check " "for unbalanced parentheses or other expression errors." }, { TCERR_EXPECTED_STR_CONT, "expected '>>' and the string continuation, but found \"%~.*s\"", "Expected '>>' after the embedded expression, followed by the " "continuation of the string, but found \"%~.*s\" instead. Check the " "embedded expression (between '<<' and '>>') for errors, such as " "unbalanced parentheses, and check that the string is properly " "continued after a '>>' sequence." }, { TCERR_EXPECTED_ARG_COMMA, "expected ',' or ')' in argument list, but found \"%~.*s\"", "Expected a comma ',' or right parenthesis ')' in an argument list, " "but found \"%~.*s\". Arguments must be separated by commas, and " "the entire list must be enclosed in parentheses '( )'. Check for " "errors and unbalanced parentheses in the argument expression." }, { TCERR_EXPECTED_SUB_RBRACK, "expected ']' at end of subscript, but found \"%~.*s\"", "Expected a right square bracket ']' at the end of the subscript " "(index) expression, but found \"%~.*s\" instead. A ']' is required " "to match the '[' at the start of the subscript. Check for errors " "and unbalanced parentheses in the subscript expression." }, { TCERR_INVALID_PROP_EXPR, "expected property name or parenthesized expression after '.', " "found \"%~.*s\"", "Expected a property name or a parenthesized expression (which must " "evaluate at run-time to a property address value) after '.', but " "found \"%~.*s\", which is not a valid property-valued expression." }, { TCERR_LIST_MISSING_RBRACK, "expected ']' or a list element, but found \"%~.*s\"", "Expected to find a list element expression or a right square " "bracket ']' ending the list, but found \"%~.*s\" instead. " "The compiler will assume that this is the end of the list. " "Please insert the missing ']', or check for errors in the list " "expressions." }, { TCERR_LIST_EXTRA_RPAR, "found extraneous ')' in list; ignored", "Found an extra right parenthesis ')' where a list element " "expression or a right square bracket ']' ending the list should be. " "The compiler will assume that the ')' is extraneous and will " "ignore it. Please remove the extra ')' or check the list " "for unbalanced parentheses or other expression errors." }, { TCERR_LIST_EXPECT_COMMA, "expected ',' separating list elements, but found \"%~.*s\"", "A comma ',' must be used to separate each element of a list. " "The compiler found \"%~.*s\" where a comma should be. Please insert " "the missing comma between the list elements, or check for an " "error in the preceding list element expression." }, { TCERR_CONST_IDX_NOT_INT, "index value must be an integer in constant list index expression", "The list index expression has a constant value, but the list index " "is not a number. A list index must have a numeric value. Check " "the expression in the square brackets '[ ]' and correct any errors." }, { TCERR_CONST_IDX_RANGE, "index value out of range in constant list index expression", "The list index expression is out of range for the list. The index " "value must be a number from 1 to the number of elements in the " "list. Check the expression in the square brackets '[ ]' and " "correct the value." }, { TCERR_UNTERM_STRING, "unterminated string literal: string started with %c%~.*s%c", "Unterminated string. The string starting with %c%~.*s%c does not have a " "matching close quote before the end of the file. Please insert the " "missing quote mark. If the string looks properly terminated, check " "the previous string (or the previous few strings), since an unbalanced " "quote mark in an earlier string can sometimes propagate to later " "strings." }, { TCERR_EXPECTED_ARG_RPAR, "expected ')' at end of argument list, but found \"%~.*s\"", "Expected a right parenthesis ')' at the end of an argument list, " "but found \"%~.*s\". The compiler is assuming that this is the end " "of the statement. Please insert the missing parenthesis, or check " "the argument list for unbalanced parentheses or other errors." }, { TCERR_EXTRA_RPAR, "unexpected ')' found - ignored", "The expression contains an unbalanced right parenthesis ')'. " "The compiler will ignore the extra ')'. Remove the extra ')', " "or check the expression for other errors." }, { TCERR_EXTRA_RBRACK, "unexpected ']' found - ignored", "The expression contains an unbalanced right square bracket ']'. " "The compiler will ignore the extra ']'. Remove the extra ']', " "or check the expression for other errors." }, { TCERR_EXPECTED_OPERAND, "expected an operand, but found \"%~.*s\"", "The expression is missing an operand value - the compiler found " "\"%~.*s\" instead of a valid operand. Please check the expression and " "correct the error." }, { TCERR_PROPSET_REQ_STR, "expected property name pattern string after 'propertyset' - " "found \"%~.*s\"", "A property name pattern string, enclosed in single quotes, is " "required after the 'propertyset' keyword, but the compiler found " "\"%~.*s\" instead. Add the missing pattern string." }, { TCERR_INH_CLASS_SYNTAX, "\"inherited superclass\" syntax - expected '.', found \"%~.*s\"", "Invalid syntax in \"inherited class\" expression. The class name " "must be followed by '.', but the compiler found \"%~.*s\" instead. " "Check and correct the syntax." }, { TCERR_UNDEF_SYM, "undefined symbol \"%~.*s\"", "The symbol \"%~.*s\" is not defined. Check the spelling of the " "symbol name, and make sure that the corresponding local variable, " "object, or function definition is entered correctly. This error " "could be the result of a syntax error in the original declaration " "of the symbol; if the declaration has an error, correct that error " "first, then try recompiling." }, { TCERR_ASSUME_SYM_PROP, "undefined symbol \"%~.*s\" - assuming this is a property name", "The symbol \"%~.*s\" is undefined, but appears from context to be " "a property name. The compiler is assuming that this is a property. " "Check the spelling of the symbol. If this assumption is correct, " "you can avoid this warning by explicitly declaring a value to the " "property in an object definition rather than in method code." }, { TCERR_CONST_BINMINUS_INCOMPAT, "incompatible constant types for two-operand '-' operator", "The constant types in this expression are not compatible for use " "with the two-operand '-' operator. You can subtract one number " "from another, or subtract a value from a list; other combinations " "are not allowed." }, { TCERR_REQ_SYM_FORMAL, "expected a symbol in formal parameter list, but found \"%~.*s\"", "A symbol name is required for each formal parameter. The compiler " "found \"%~.*s\" instead of a symbol for the parameter name." }, { TCERR_REQ_COMMA_FORMAL, "expected a comma in formal parameter list, but found \"%~.*s\"", "The formal parameter list is missing a comma between two parameter " "names - a comma should come before \"%~.*s\" in the parameter list." }, { TCERR_MISSING_LAST_FORMAL, "missing parameter name at end of formal parameter list", "The last parameter name in the formal parameter list is missing. " "Insert a parameter name before the ')', or remove the extra comma " "at the end of the list." }, { TCERR_MISSING_RPAR_FORMAL, "missing right parenthesis ')' at end of formal parameter list - " "found \"%~.*s\"", "The right parenthesis ')' at the end of the formal parameter list " "is missing. The parenthesis should come before \"%~.*s\". " }, { TCERR_FORMAL_REDEF, "formal parameter \"%~.*s\" defined more than once", "The formal parameter name \"%~.*s\" is defined more than once in the " "parameter list. Each parameter name can be used only once in the " "same list. Remove the redundant variable, or change its name." }, { TCERR_EQ_WITH_METHOD_OBSOLETE, "'=' is not allowed with a method definition", "An equals sign '=' is not allowed in a method definition. You can " "only use '=' when defining a simple value for a property, not to " "define method code. (TADS 2 used '=' in methods, but this syntax is " "now obsolete.) Remove the '='." }, { TCERR_REQ_LBRACE_CODE, "expected '{' at start of method code body, but found \"%~.*s\"", "An open brace '{' was expected before a method's program code body, " "but the compiler found \"%~.*s\" instead." }, { TCERR_EOF_IN_CODE, "unexpected end of file in code block - '}' missing", "The compiler reached the end of the file before the current function " "or method was finished. A close brace '}' is probably missing - " "insert the close brace at the end of the function or method." }, { TCERR_REQ_LPAR_IF, "expected '(' after \"if\", but found \"%~.*s\"", "An open parenthesis '(' is required after the keyword \"if\" - " "the compiler found \"%~.*s\" instead. The compiler will assume " "that a parenthesis was intended. Please correct the syntax " "by inserting a parenthesis." }, { TCERR_MISPLACED_ELSE, "misplaced \"else\" - no corresponding \"if\" statement", "This \"else\" clause is invalid because it is not properly associated " "with an \"if\" statement. Most likely, this is because the group of " "statements following the \"if\" is not properly enclosed in braces " "'{ }', or because the braces just before the \"else\" aren't properly " "balanced, or because there are too many or too few semicolons ';' after " "the statement following the \"if\" and before the \"else\". Check " "braces to make sure they're properly balanced, and check the statement " "or statements before the \"else\" for proper syntax, especially " "for the correct number of terminating semicolons." }, { TCERR_MISPLACED_CASE, "misplaced \"case\" keyword - not in a \"switch\" statement", "This \"case\" clause is invalid because it is not part of a \"switch\" " "statement. The most likely cause is that braces before this \"case\" " "keyword aren't properly balanced. \"case\" labels must be enclosed " "directly by the \"switch\" - they cannot be enclosed within statements " "or braces inside the \"switch\". Check code preceding the \"case\" " "clause for proper syntax and balanced braces." }, { TCERR_MISPLACED_DEFAULT, "misplaced \"default\" keyword - not in a \"switch\" statement", "This \"default\" clause is invalid because it is not part of a " "\"switch\" statement. The most likely cause is that braces before " "this \"default\" keyword aren't properly balanced. A \"default\" " "label must be enclosed directly by the \"switch\" - it cannot be " "enclosed within a statement or braces within the \"switch\" body. " "Check code preceding the \"default\" clause for proper syntax " "and balanced braces." }, { TCERR_ELLIPSIS_NOT_LAST, "'...' cannot be followed by additional formal parameters", "An ellipsis '...' cannot be followed by additional parameters " "in an argument list. Move the '...' to the end of the parameter " "list, or remove the extraneous parameters after the ellipsis." }, { TCERR_LOCAL_REQ_COMMA, "expected ',' or ';' after local variable, but found \"%~.*s\"", "The compiler expected a comma ',' or semicolon ';' after a local " "variable declaration, but found \"%~.*s\" instead. If you're " "defining an additional variable, add a comma before the additional " "variable; if not, check for a missing semicolon." }, { TCERR_LOCAL_REQ_SYM, "expected symbol name in local variable declaration, but found \"%~.*s\"", "A symbol name is required in the local variable declaration, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and correct the error." }, { TCERR_FUNC_REQ_SYM, "expected symbol after 'function', but found \"%~.*s\"", "A symbol name is required after the 'function' keyword, but the " "compiler found \"%~.*s\" instead. Check the function definition " "syntax." }, { TCERR_REQ_CODE_BODY, "expected ';', '(', or '{', but found \"%~.*s\"", "The compiler expected a left parenthesis '(' starting a formal " "parameter list, a left brace '{' starting a code body, or a semicolon " "';' terminating the statement, but found \"%~.*s\". Check the function " "definition syntax." }, { TCERR_REQ_FUNC_OR_OBJ, "expected function or object definition, but found \"%~.*s\"", "The compiler expected a function or object definition, but " "found \"%~.*s\". Check the syntax, and check for unbalanced " "braces '{ }' and other syntax errors preceding this line." }, { TCERR_RET_REQ_EXPR, "expected ';' or expression after \"return\", but found \"%~.*s\"", "The \"return\" keyword must be followed by an expression giving the " "value to return, or by a semicolon ';' if there is no value to " "return; the compiler found \"%~.*s\" instead. Check the syntax, and " "insert the missing semicolon or expression as appropriate." }, { TCERR_UNREACHABLE_CODE, "unreachable statement", "This statement cannot be reached, because the previous statement " "returns or throws an exception. This code will never be executed. " "Check the logic to determine if the code is necessary; if not, " "remove the code. If the code is necessary, you must determine " "why the code is unreachable and correct the program logic." }, { TCERR_RET_VAL_AND_VOID, "code has \"return\" statements both with and without values", "This code has \"return\" statements both with and without values. " "A function or method's \"return\" statements should consistently " "return values or not; these should not be mixed in a single " "function or method, because callers will not have predictable " "results when using the function's return value." }, { TCERR_RET_VAL_AND_IMP_VOID, "code has \"return\" with value but also falls off end", "This code has one or more \"return\" statements that explicitly " "return a value from the function, but also \"falls off\" the end " "of the function without a \"return\" statement, which will result " "in a return without a value. The last statement in the function " "or method should be a \"return\" with a value, for consistency " "with the other \"return\" statements." }, { TCERR_REQ_INTRINS_NAME, "expected function set name string after \"intrinsic\", " "but found \"%~.*s\"", "The \"intrinsic\" keyword must be followed by the global name of the " "function set, enclosed in single-quotes, but the compiler found " "\"%~.*s\" instead. Check the \"intrinsic\" statement syntax." }, { TCERR_REQ_INTRINS_LBRACE, "expected '{' after intrinsic name, but found \"%~.*s\"", "The function set listing for an \"intrinsic\" statement must be " "closed in braces '{ }'. The compiler found \"%~.*s\" after the " "function set name, where the open brace '{' should be. Check the " "syntax." }, { TCERR_EOF_IN_INTRINS, "end of file in \"intrinsic\" list - '}' is probably missing", "The compiler reached the end of the file while still scanning " "an \"intrinsic\" statement's function set listing. The closing " "brace '}' of the function set list is probably missing; check " "for the missing brace." }, { TCERR_REQ_INTRINS_LPAR, "expected '(' after function name in intrinsic list, but found \"%~.*s\"", "An open parenthesis '(' is required after the name of a function " "in an intrinsic function list, but the compiler found \"%~.*s\" " "instead. Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_INTRINS_SYM, "expected function name in intrinsic list, but found \"%~.*s\"", "The compiler expected the name of a function in the intrinsic " "function list, but found \"%~.*s\" instead. Check the syntax of " "the statement, and check for unbalanced parentheses and braces." }, { TCERR_REQ_FOR_LPAR, "expected '(' after \"for\", but found \"%~.*s\"", "An open parenthesis '(' is required after the \"for\" keyword, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and insert the missing parenthesis." }, { TCERR_LOCAL_REDEF, "local variable \"%~.*s\" defined more than once", "The local variable name \"%~.*s\" is defined more than once in this " "scope. Each local variable name can be used only once at the same " "level of braces '{ }'. Remove the redundant variable, or change its " "name." }, { TCERR_REQ_FOR_LOCAL_INIT, "initializer expected after local variable name in \"for\", but found " "\"%~.*s\"", "A local variable defined within a \"for\" statement's initialization " "clause requires an initializer expression. The compiler expected to " "find an assignment operator after the local variable name, but found " "\"%~.*s\" instead. Check the syntax, and add the missing initializer " "to the local variable definition." }, { TCERR_MISSING_FOR_INIT_EXPR, "missing expression after comma in \"for\" initializer", "An expression must follow a comma in a \"for\" initializer list. " "Check the expression, and remove the extra comma before the " "semicolon, or supply the missing expression." }, { TCERR_MISSING_FOR_PART, "missing expression in \"for\" statement - expected ';', found '%~.*s'", "A \"for\" statement requires three expressions, separated by " "semicolons ';'. This statement doesn't seem to have all of the required " "parts - it ends unexpectedly at \"%~.*s\". Add the missing " "parts or correct the syntax." }, { TCERR_REQ_FOR_INIT_COMMA, "expected ',' or ';' in \"for\" initializer, but found \"%~.*s\"", "A comma ',' or semicolon ';' was expected in the \"for\" statement's " "initializer list, but the compiler found \"%~.*s\" instead. Check " "the expression syntax." }, { TCERR_REQ_FOR_COND_SEM, "expected ';' after \"for\" condition, but found \"%~.*s\"", "A semicolon ';' was expected after the \"for\" statement's " "condition expression, but the compiler found \"%~.*s\" instead. " "Check the expression syntax." }, { TCERR_REQ_FOR_RPAR, "missing ')' at end of \"for\" expression list - found \"%~.*s\"", "A closing parenthesis ')' is required at the end of the \"for\" " "statement's expression list, but the compiler found \"%~.*s\" instead. " "Check the syntax, and insert the missing parenthesis, or correct " "unbalanced parentheses or other syntax errors earlier in the " "expression list." }, { TCERR_FOR_COND_FALSE, "\"for\" condition is always false - body and reinitializer are " "unreachable", "The condition of this \"for\" statement is always false, so the " "body and reinitialization expression will never be executed." }, { TCERR_REQ_WHILE_LPAR, "missing '(' after \"while\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"while\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_WHILE_RPAR, "missing ')' after \"while\" expression - found \"%~.*s\"", "A close parenthesis ')' is required after the expression condition " "in a \"while\" statement, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_WHILE_COND_FALSE, "\"while\" condition is always false - loop body is unreachable", "The condition of this \"while\" statement is always false, so the " "body of the loop will never be executed." }, { TCERR_REQ_DO_WHILE, "expected \"while\" in \"do\" statement, but found \"%~.*s\"", "This \"do\" statement is missing the required \"while\" keyword " "immediately after the loop body. The compiler found \"%~.*s\" where " "the \"while\" keyword shuld be. Check the syntax and insert the " "missing \"while\" keyword." }, { TCERR_MISPLACED_CATCH, "misplaced \"catch\" clause - must be associated with \"try\"", "This \"catch\" clause is invalid because it is not part of a \"try\"" " statement. The most likely cause is that braces before this \"catch\"" " keyword aren't properly balanced. Check braces preceding the " "\"catch\" clause for proper syntax." }, { TCERR_MISPLACED_FINALLY, "misplaced \"finally\" clause - must be associated with \"try\"", "This \"finally\" clause is invalid because it is not part of a \"try\"" " statement. The most likely cause is that braces before this " "\"finally\" keyword aren't properly balanced. Check braces " "preceding the \"finally\" clause for proper syntax." }, { TCERR_REQ_SWITCH_LPAR, "missing '(' after \"switch\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"switch\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_SWITCH_RPAR, "missing ')' after \"switch\" expression - found \"%~.*s\"", "A close parenthesis ')' is required after the controlling expression " "of a \"switch\" statement, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_SWITCH_LBRACE, "missing '{' after \"switch\" expression - found \"%~.*s\"", "An open brace '{' is required after the controlling expression " "of a \"switch\" statement, but the compiler found \"%~.*s\" instead. " "The body of the \"switch\" must be enclosed in braces '{ }'. " "Check the syntax and insert the missing parenthesis." }, { TCERR_UNREACHABLE_CODE_IN_SWITCH, "code before first \"case\" or \"default\" label in \"switch\" is " "not allowed", "This statement precedes the first \"case\" or \"default\" label " "in the \"switch\" body - all code within a \"switch\" body must " "be reachable from a \"case\" or \"default\" label. Even \"local\" " "declarations must be within a labelled section of the \"switch\" " "body. Check for a missing \"case\" label, or move the code so that " "it is outside the \"switch\" body or after a \"case\" label." }, { TCERR_EOF_IN_SWITCH, "end of file in \"switch\" body", "End of file found in \"switch\" body. The \"switch\" body's " "braces '{ }' are probably not properly balanced. Check the code " "within the \"switch\" body for unbalanced braces and other errors." }, { TCERR_CODE_LABEL_REDEF, "code label \"%~.*s\" already defined in this function or method", "The code label \"%~.*s\" is already defined in this function or " "method. A code label can be used only once within each function " "or method; code labels always have function- or method-level scope, " "even when they're nested within braces. Change the name of one of " "the conflicting labels so that the two labels have different names." }, { TCERR_REQ_CASE_COLON, "missing ':' after \"case\" expression - found \"%~.*s\"", "A colon ':' is required after the \"case\" expression, but the " "compiler found \"%~.*s\" instead. Check the expression for errors, " "and insert the missing colon after the expression." }, { TCERR_CASE_NOT_CONSTANT, "\"case\" expression has a non-constant value", "The expression in this \"case\" label does not have a constant " "value. \"case\" expressions must always be constants or expressions " "involving only constants. Check the expression and remove " "references to local variables, object properties, or any other " "non-constant values." }, { TCERR_REQ_DEFAULT_COLON, "missing ':' after \"default\" - found \"%~.*s\"", "A colon ':' is required after the \"default\" keyword, but the " "compiler found \"%~.*s\" instead. Insert the missing colon " "after the keyword." }, { TCERR_DEFAULT_REDEF, "this \"switch\" already has a \"default:\" label", "This \"switch\" statement already has a \"default:\" label. A " "\"switch\" statement can have at most one \"default:\" label. Remove " "the redundant label." }, { TCERR_TRY_WITHOUT_CATCH, "\"try\" statement has no \"catch\" or \"finally\" clauses", "This \"try\" statement has no \"catch\" or \"finally\" clauses. A " "\"try\" statement must have at least one such clause, since it is " "otherwise superfluous. Check for unbalanced braces in the body of " "the \"try\" block." }, { TCERR_REQ_CATCH_LPAR, "missing '(' after \"catch\" - found \"%~.*s\"", "An open parenthesis '(' is required after the \"catch\" keyword, but " "the compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing parenthesis." }, { TCERR_REQ_CATCH_RPAR, "missing ')' after \"catch\" variable name - found \"%~.*s\"", "A close parenthesis ')' is required after the variable name " "of a \"switch\" clause, but the compiler found \"%~.*s\" instead. " "Check the syntax and insert the missing parenthesis." }, { TCERR_REQ_CATCH_CLASS, "expected class name in \"catch\" clause - found \"%~.*s\"", "The class name of the exception to catch is required in the " "\"catch\" clause, but the compiler found \"%~.*s\" instead. Check " "and correct the syntax." }, { TCERR_REQ_CATCH_VAR, "expected variable name in \"catch\" clause - found \"%~.*s\"", "The name of a local variable (which can either be an existing " "variable or can be a new variable implicitly defined by this use) " "is required in the \"catch\" clause, but the compiler found \"%~.*s\" " "instead. Check and correct the syntax." }, { TCERR_CATCH_VAR_NOT_LOCAL, "\"%~.*s\" is not a local variable, so is not a valid \"catch\" target " "variable", "The symbol \"%~.*s\" is defined as something other than a local " "variable, so it cannot be used as the target of this \"catch\" clause. " "Check for a conflicting symbol, and either remove the conflicting " "symbol or rename the \"catch\" variable." }, { TCERR_BREAK_REQ_LABEL, "label name expected after \"break\", but found \"%~.*s\"", "A label name was expected after the \"break\" keyword, but the " "compiler found \"%~.*s\" instead. This might indicate that a " "semicolon after \"break\" is missing. Check the syntax." }, { TCERR_CONT_REQ_LABEL, "label name expected after \"continue\", but found \"%~.*s\"", "A label name was expected after the \"continue\" keyword, but the " "compiler found \"%~.*s\" instead. This might indicate that a " "semicolon after \"continue\" is missing. Check the syntax." }, { TCERR_GOTO_REQ_LABEL, "label name expected after \"goto\", but found \"%~.*s\"", "A label name was expected after the \"goto\" keyword, but the " "compiler found \"%~.*s\" instead. Check the syntax and insert " "the missing label name." }, { TCERR_REDEF_AS_FUNC, "symbol \"%~.*s\" is already defined - can't redefine as function", "The symbol \"%~.*s\" is already defined, so you cannot use it " "as the name of a function here. This symbol is already being " "used as the name of an object or property elsewhere " "in your program. Change the name to a unique symbol." }, { TCERR_INVAL_EXTERN, "invalid \"extern\" type specifier \"%~.*s\"", "Invalid keyword \"%~.*s\" following \"extern\". The \"extern\" " "keyword must be followed by \"function\" or \"object\" to indicate " "the type of the external symbol to declare." }, { TCERR_EXTERN_NO_CODE_BODY, "code body is not allowed in an \"extern function\" declaration", "This \"extern function\" declaration is not valid because it has " "a left brace introducing a code body after the function prototype. " "An extern function declaration can have only the prototype, because " "it specifies that the actual code body of the function is defined " "in another module. Either remove the \"extern\" keyword or remove " "the code body." }, { TCERR_FUNC_REDEF, "function \"%~.*s\" is already defined", "The function \"%~.*s\" is already defined earlier in the program. " "Each function must have a unique name. Remove the redundant " "definition or change the name of one of the functions." }, { TCERR_INCOMPAT_FUNC_REDEF, "function \"%~.*s\" has an incompatible previous declaration", "The function \"%~.*s\" has an incompatible declaration previously " "in the program. The earlier definition could be from an \"extern\" " "declaration in this source file, or it could come from a symbol " "file for another module in your program. Each definition of " "a function must declare the same parameter list for the function. " "Check for other declarations of this function and correct the " "inconsistency." }, { TCERR_OBJDEF_REQ_COLON, "expected ':' after object name in object definition, but found \"%~.*s\"", "A colon ':' is required after the object name in an object " "definition, but the compiler found \"%~.*s\" instead. Check the " "object syntax and insert the missing colon." }, { TCERR_OBJDEF_REQ_SC, "expected superclass name in object definition, but found \"%~.*s\"", "The name of a superclass was expected in the object definition, " "but the compiler found \"%~.*s\". Check the object syntax; add or " "correct the superclass name, or correct other syntax errors." }, { TCERR_OBJDEF_OBJ_NO_SC, "superclasses cannot be specified with \"object\" as the base class", "This object specifies \"object\" as the base class, but also lists " "named superclasses. This is not valid -- a basic \"object\" " "definition cannot also specify named superclasses." }, { TCERR_REDEF_AS_OBJ, "symbol \"%~.*s\" is already defined - can't redefine as object", "The symbol \"%~.*s\" is already defined, so you cannot use it " "as the name of an object here. This symbol is already being " "used as the name of a function or property elsewhere " "in your program. Change the name to a unique symbol." }, { TCERR_OBJ_REDEF, "object \"%~.*s\" is already defined - can't redefine as object", "The object \"%~.*s\" is already defined earlier in the program. " "Each object must have a unique name. Remove the redundant " "definition or change the name of one of the objects." }, { TCERR_OBJDEF_REQ_PROP, "expected property name in object definition, but found \"%~.*s\"", "A property name was expected in the object definition, but the " "compiler found \"%~.*s\" instead. Check for a missing semicolon at the " "end of the object definition, and check for unbalanced " "braces prior to this line." }, { TCERR_REDEF_AS_PROP, "symbol \"%~.*s\" is already defined - can't redefine as property", "The symbol \"%~.*s\" is already defined, so you cannot use it as " "the name of a property here. The symbol is already being " "used as the name of an object or function elsewhere in the program. " "Change the name to a unique symbol." }, { TCERR_OBJDEF_REQ_PROPVAL, "expected property value or method after property name \"%~.*s\", " "but found \"%~.*s\"", "A property value or method was expected after the property " "name \"%~.*s\", but the compiler found \"%~.*s\". Check the syntax " "and supply a property value expression or method code in braces '{ }'."}, { TCERR_REPLACE_REQ_OBJ_OR_FUNC, "expected \"function\" or object name after \"replace\", but found " "\"%~.*s\"", "The keyword \"replace\" must be followed by \"function\" or by an " "object name, but the compiler found \"%~.*s\" instead." }, { TCERR_REPMODOBJ_UNDEF, "replace/modify cannot be used with an object not previously defined", "The \"replace\" and \"modify\" keywords cannot be used with an object " "that is not previously defined. The object must at least be defined " "as an \"extern\" object before it can be replaced or modified." }, { TCERR_DQUOTE_IN_EXPR, "a double-quoted string (\"%~.*s\") is not valid within an expression", "The double-quoted string value (\"%~.*s\") cannot be used within an " "expression, because a double-quoted string has no value. A " "double-quoted string indicates that you simply want to display " "the text of the string. Change " "the string's quotes to single quotes (') if you meant to use the " "string as a value in an expression." }, { TCERR_ASI_IN_COND, "assignment in condition (possible use of '=' where '==' was intended)", "The condition expression contains an assignment. This frequently " "indicates that the '=' (assignment) operator was used where the " "'==' (equality comparison) operator was intended. Check the condition " "to ensure that an assignment was actually intended. If the assignment " "is intentional, you can remove this warning by modifying the " "condition expression from the current form (x = y) to the form " "((x = y) != nil) or ((x = y) != 0) as appropriate, which will not " "change the meaning but will make it clear that the assignment is " "intentional." }, { TCERR_REPFUNC_UNDEF, "\"replace\"/\"modify\" cannot be used with a function not " "previously defined", "The \"replace\" and \"modify\" keywords cannot be used with a function " "that is not previously defined. The function must at least be defined " "as an \"extern\" function before it can be replaced." }, { TCERR_REPLACE_PROP_REQ_MOD_OBJ, "\"replace\" can be used with a property only in a \"modify\" object", "The \"replace\" keyword can be used with a property only in an " "object defined with the \"modify\" keyword. This object is not " "defined with \"modify\", so \"replace\" is not allowed with its " "property definitions." }, { TCERR_EXTERN_OBJ_REQ_SYM, "expected object name symbol in \"extern\" statement but found \"%~.*s\"", "An object name symbol is required after \"extern object\" or " "\"extern class\", but the compiler found \"%~.*s\". Check the syntax " "and supply the missing object name." }, { TCERR_PROP_REDEF_IN_OBJ, "property \"%~.*s\" already defined in object", "The property \"%~.*s\" is already defined in this object. An " "object can have at most one definition for a given property. Remove " "the redundant property definition." }, { TCERR_PROP_REQ_EQ, "'=' required between property name and value - found \"%~.*s\"", "An equals sign '=' is required to separate the property name and " "its value; the parser found \"%~.*s\" where the '=' should go. " "Check the syntax and supply the missing '='." }, { TCERR_LIST_EXPECT_ELEMENT, "extra list element expected after comma, but found end of list", "An additional list element was expected after the last comma in the " "list, but the compiler found the end of the list (a closing bracket " "']') instead. Check the list and either remove the unnecessary " "extra comma or add the missing list element." }, { TCERR_REQ_INTRINS_CLASS_NAME, "expected class name string after class name symbol, but found \"%~.*s\"", "The \"intrinsic class\" name must be followed by the global name " "of the metaclass, enclosed in single-quotes, but the compiler found " "\"%~.*s\" instead. Check the statement syntax." }, { TCERR_REQ_INTRINS_CLASS_LBRACE, "expected '{' after intrinsic class name, but found \"%~.*s\"", "The property listing for an \"intrinsic class\" statement must be " "closed in braces '{ }'. The compiler found \"%~.*s\" after the " "metaclass name, where the open brace '{' should be. Check the " "syntax." }, { TCERR_EOF_IN_INTRINS_CLASS, "end of file in \"intrinsic class\" list - '}' is probably missing", "The compiler reached the end of the file while still scanning " "an \"intrinsic class\" statement's property listing. The closing " "brace '}' of the list is probably missing; check " "for the missing brace." }, { TCERR_REQ_INTRINS_CLASS_PROP, "expected property name in intrinsic class list, but found \"%~.*s\"", "The compiler expected the name of a property in the intrinsic " "class property list, but found \"%~.*s\" instead. Check the syntax of " "the statement, and check for unbalanced braces." }, { TCERR_REQ_INTRINS_CLASS_NAME_SYM, "expected class name symbol after \"intrinsic class\", " "but found \"%~.*s\"", "The \"intrinsic class\" keywords must be followed by the class " "name symbol, but the compiler found \"%~.*s\" instead. Check " "the statement syntax." }, { TCERR_REDEF_INTRINS_NAME, "symbol \"%~.*s\" is already defined - can't redefine as intrinsic class", "The symbol \"%~.*s\" is already defined, so you cannot use it as " "the name of this intrinsic class. The symbol is already being " "used as the name of an object, function, or property elsewhere " "in the program. Change the name to a unique symbol." }, { TCERR_CANNOT_EVAL_METACLASS, "\"%~.*s\" is an intrinsic class name and cannot be evaluated " "in an expression", "The symbol \"%~.*s\" is an intrinsic class name. This symbol cannot " "be evaluated in an expression, because it has no value. You can " "only use this symbol in specific contexts where class names " "are permitted, such as with 'new'." }, { TCERR_DICT_SYNTAX, "expected 'property' or object name after 'dictionary', " "but found \"%~.*s\"", "The keyword 'property' or an object name symbol must follow " "the 'dictionary' keyword, but the compiler found \"%~.*s\" instead. " "Check the 'dictionary' statement syntax." }, { TCERR_DICT_PROP_REQ_SYM, "expected property name in 'dictionary property' list, but " "found \"%~.*s\"", "The name of a property was expected in the 'dictionary property' " "list, but the compiler found \"%~.*s\" instead. Check the " "statement syntax." }, { TCERR_DICT_PROP_REQ_COMMA, "expected comma in 'dictionary property' list, but found \"%~.*s\"", "A comma was expected after a property name symbol in a " "'dictionary property' list, but the compiler found \"%~.*s\" " "instead. Check the syntax of the property list and ensure that " "each item is separated from the next by a comma, and that the " "list ends with a semicolon." }, { TCERR_REDEF_AS_DICT, "redefining symbol \"%~.*s\" as dictionary object", "The symbol \"%~.*s\" cannot be used as a dictionary object, because " "it is already defined as a different type of object. You must change " "the name of this dictionary object, or change the name of the " "conflicting object definition." }, { TCERR_UNDEF_SYM_SC, "undefined symbol \"%~.*s\" (used as superclass of \"%~.*s\")", "The symbol \"%~.*s\" is undefined. This symbol is used as a superclass " "in the definition of the object \"%~.*s\". Check the object definition " "to ensure that the superclass name is spelled correctly, and check " "that the superclass's object definition is correct." }, { TCERR_VOCAB_REQ_SSTR, "vocabulary property requires string value, but found \"%~.*s\"", "A vocabulary property value must be one or more single-quoted " "string values, but the compiler found \"%~.*s\" instead. Check the " "property definition and use a single-quoted string value." }, { TCERR_VOCAB_NO_DICT, "vocabulary property cannot be defined - no dictionary is active", "A vocabulary property cannot be defined for this object because " "no dictionary is active. Insert a 'dictionary' statement prior " "to this object definition to establish the dictionary object that " "will be used to store this object's vocabulary." }, { TCERR_LISTPAR_NOT_LAST, "variable-argument list parameter must be the last parameter", "A variable-argument list parameter (a parameter enclosed in square " "brackets '[ ]') is required to be the last parameter in the " "parameter list. Remove the parameters following the list parameter." }, { TCERR_LISTPAR_REQ_RBRACK, "expected ']' after variable-argument list parameter, but found \"%~.*s\"", "A closing square bracket ']' was expected following the " "variable-argument list parameter name, but the compiler found " "\"%~.*s\" instead. Insert the missing bracket." }, { TCERR_LISTPAR_REQ_SYM, "variable-argument list parameter name expected, but found \"%~.*s\"", "A parameter name symbol was expected after the left square bracket '[', " "but the compiler found \"%~.*s\" instead. Check the syntax." }, { TCERR_DBG_NO_ANON_FUNC, "anonymous functions are not allowed in the debugger", "Anonymous functions are not allowed in the debugger." }, { TCERR_ANON_FUNC_REQ_NEW, "anonymous function requires 'new' before 'function' keyword", "An anonymous function definition requires the keyword 'new' " "before the keyword 'function'. This definition does not contain " "the 'new' keyword. Insert 'new' before 'function' in the " "definition." }, { TCERR_GRAMMAR_REQ_SYM, "expected symbol name after 'grammar', but found \"%~.*s\"", "A symbol giving the name of a production is required after " "the 'grammar' keyword, but the compiler found \"%~.*s\" instead. " "Check the statement syntax." }, { TCERR_GRAMMAR_REQ_COLON, "expected ':' after production name, but found \"%~.*s\"", "A colon ':' is required after the name of the production in " "a 'grammar' statement, but the compiler found \"%~.*s\" instead. " "Check the statement syntax and insert the missing colon." }, { TCERR_GRAMMAR_REQ_OBJ_OR_PROP, "object or property symbol required in 'grammar' token list " "(found \"%~.*s\")", "Symbols used in a token list in a 'grammar' statement must " "be property or object names. The symbol \"%~.*s\" is not a " "property or object name, so it cannot be used in the token list. " "Remove this symbol from the list or replace it with a symbol " "of the appropriate type." }, { TCERR_GRAMMAR_ARROW_REQ_PROP, "'->' in 'grammar' token list must be followed by a property name " "(found \"%~.*s\")", "An arrow '->' in a 'grammar' statement's token list must be " "followed by a property name, but the compiler found \"%~.*s\" " "instead. Check the statement syntax." }, { TCERR_GRAMMAR_INVAL_TOK, "invalid token \"%~.*s\" in 'grammar' token list", "The token \"%~.*s\" is not valid in a 'grammar' statement's token " "list. The token list must consist of property names, object names, " "and literal strings (in single quotes). Check the statement syntax." }, { TCERR_REDEF_AS_GRAMPROD, "redefining symbol \"%~.*s\" as grammar production object", "The symbol \"%~.*s\" cannot be used as a grammar production, because " "it is already defined as a different type of object. You must change " "the name of this production object, or change the name of the " "conflicting object definition." }, { TCERR_GRAMMAR_REQ_PROD, "object \"%~.*s\" is not valid in a grammar rule - only production " "names are allowed", "The object \"%~.*s\" cannot be used in a grammar rule. Only " "production names are allowed. A production name is a symbol " "that is used immediately after the 'grammar' keyword in a " "grammar rule definition." }, { TCERR_ENUM_REQ_SYM, "symbol expected in 'enum' list - found \"%~.*s\"", "A symbol name was expected in the 'enum' statement's list of " "enumerator symbols to define, but the compiler found \"%~.*s\" " "instead. Check the statement syntax." }, { TCERR_REDEF_AS_ENUM, "symbol \"%~.*s\" is already defined - can't be used as an enum name", "The symbol \"%~.*s\" is already defined, so it can't be used as an " "'enum' name. An 'enum' name cannot be used for any other global " "symbol, such as an object, function, or property name. Change the " "enum name, or change the conflicting symbol definition." }, { TCERR_ENUM_REQ_COMMA, "comma expected in 'enum' symbol list, but found \"%~.*s\"", "A comma was expected after a symbol name in the 'enum' statement's " "list of enumerator symbols, but the compiler found \"%~.*s\" " "instead. Check the statement syntax and insert the missing " "comma." }, { TCERR_GRAMMAR_BAD_ENUM, "enumerator \"%~.*s\" in 'grammar' list is not declared with " "'enum token'", "The enumerator \"%~.*s\" in the 'grammar' list was not originally " "declared with 'enum token'. Only 'enum token' enumerators can be " "used in 'grammar' token lists. Check the original definition of " "the enumerator and change it to 'enum token', or remove the " "enumerator from this 'grammar' list." }, { TCERR_GRAMMAR_STAR_NOT_END, "'*' must be the last token in a 'grammar' alternative list " "(found \"%~.*s\")", "A '*' must be the last token in a 'grammar' alternative list, " "because this specifies a match for any remaining input tokens. " "The compiler found \"%~.*s\" after the '*'. Check the grammar " "definition, and end the alternative immediately after the '*'." }, { TCERR_GRAMMAR_QUAL_NOT_FIRST, "grammar qualifiers must precede all tokens", "Grammar qualifiers (sequences enclosed in square brackets '[ ]' " "within a 'grammar' statement's item list) must precede all " "token items in an alternative. This statement contains a qualifier " "that appears after one or more token items. Move the qualifier " "to the start of the alternative's token list." }, { TCERR_GRAMMAR_QUAL_REQ_SYM, "keyword required after '[' in grammar qualifier - found \"%~.*s\"", "A keyword is required after the open bracket '[' in a grammar " "qualifier, but the compiler found \"%~.*s\" instead. The keyword " "must specify a valid grammar qualifier." }, { TCERR_BAD_GRAMMAR_QUAL, "invalid grammar qualifier \"%~.*s\"", "The grammar qualifier \"%~.*s\" is not valid. Check the statement " "syntax." }, { TCERR_GRAMMAR_QUAL_REQ_INT, "grammar qualifier \"[%s]\" requires integer constant value", "The grammar qualifier \"[%s]\" requires an integer constant value. " "The expression is missing, invalid, or does not evaluate to a " "constant integer value. Check the qualifier syntax." }, { TCERR_GRAMMAR_QUAL_REQ_RBRACK, "']' required after grammar qualifier - found \"%~.*s\"", "A closing bracket ']' is required at the end of a grammar " "qualifier, but the compiler found \"%~.*s\" instead. Check the " "syntax, and add the missing ']' or remove extraneous extra text." }, { TCERR_PLUSPROP_REQ_SYM, "symbol expected after '+ property', but found \"%~.*s\" instead", "A property symbol is required for the '+ property' statement, but the " "compiler found \"%~.*s\" instead. Check the syntax." }, { TCERR_PLUSOBJ_TOO_MANY, "too many '+' signs in object definition - location not defined", "This object definition has too many '+' signs. The immediately " "preceding object is not at a deep enough containment level for " "this many '+' signs. Check the previous object's location " "definition. You might need to move the immediately preceding " "object definition so that it doesn't come between this object " "and the preceding container you wish to refer to." }, { TCERR_OBJ_TPL_OP_REQ_PROP, "property name required after \"%s\" in object template - found \"%~.*s\"", "A property name is required after the \"%s\" token in the 'object " "template' statement, but the compiler found \"%~.*s\" instead. Check " "the statement syntax." }, { TCERR_OBJ_TPL_STR_REQ_PROP, "property name required in object template string - found %~.*s", "The contents of each string in an 'object " "template' statement must be a property name symbol, but the compiler " "found %~.*s in a string instead. Check the statement syntax." }, { TCERR_OBJ_TPL_REQ_RBRACK, "expected ']' after object template property name - found \"%~.*s\"", "A matching right square bracket ']' is required after a list property " "name in an 'object template' statement, but the compiler found " "\"%~.*s\" instead. Check the statement syntax." }, { TCERR_OBJ_TPL_BAD_TOK, "unexpected token \"%~.*s\" in object template", "The token \"%~.*s\" is invalid in an 'object template' statement. " "Each entry in the statement must be a property name in single or " "double quotes or square brackets, or one of the allowed operators " "('@', '+', '-', etc) followed by a property name." }, { TCERR_OBJ_TPL_SYM_NOT_PROP, "symbol \"%~.*s\" in object template is not a property", "The symbol \"%~.*s\" is used in an 'object template' statement where " "a property is required, but the symbol is not a property. Check " "for conflicting usage of the symbol (as an object or function name, " "for example)." }, { TCERR_OBJ_DEF_NO_TEMPLATE, "object definition does not match any template", "This object definition appears to use template notation, but it doesn't " "match any defined object template. Check your 'object template' " "definitions to make sure this object syntax is defined, or correct " "this object definition to match one of the defined templates. If " "this object is not intended to use a template at all, the object's " "first property definition probably has a syntax error, so check " "the object's property list syntax." }, { TCERR_OBJ_TPL_NO_VOCAB, "property \"%~.*s\" is a dictionary property - not valid in templates", "Property \"%~.*s\" is a dictionary property, which cannot be used " "in an object template." }, { TCERR_OBJ_TPL_PROP_DUP, "property \"%~.*s\" duplicated in object template", "The property \"%~.*s\" appears more than once in this 'object template' " "list. A given property can be used only once in a template." }, { TCERR_META_ALREADY_DEF, "intrinsic class has been previously defined as \"%~.*s\"", "This same intrinsic class has been previously defined with " "the name \"%~.*s\". An intrinsic class may only be defined with " "one class symbol. Remove one of the conflicting definitions." }, { TCERR_OBJ_DEF_REQ_SEM, "missing semicolon at end of object definition - found \"%~.*s\"", "This object definition is missing its closing semicolon ';' - the " "compiler found \"%~.*s\", which the compiler must assume is the start " "of a new statement or object definition. Insert the missing " "semicolon. If this is actually meant to be part of the object " "definition, there is a syntax error here - check and correct the " "syntax." }, { TCERR_EXPECTED_STMT_START, "expected start of statement, but found \"%~.*s\"", "The compiler expected to find the beginning of a statement, " "but found \"%~.*s\" instead. Check the statement syntax; check " "for preceding strings that weren't properly terminated, or extra " "or missing braces." }, { TCERR_MISSING_COLON_FORMAL, "missing colon at end of anonymous function formal list - found \"%~.*s\"", "The short-form of the anonymous function definition requires a colon " "':' at the end of the formal parameter list, but the compiler " "found \"%~.*s\" instead. Even if the function has no arguments, it " "still requires a colon immediately following the open brace '{'. " }, { TCERR_SEM_IN_SHORT_ANON_FN, "semicolon is not allowed in a short anonymous function", "This short-form anonymous function contains a semicolon, which is " "not allowed. A short-form anonymous function must consist of " "a single expression, and is terminated with the closing brace '}' " "with no semicolon between the expression and the brace." }, { TCERR_SHORT_ANON_FN_REQ_RBRACE, "semicolon is not allowed in a short-form anonymous function", "This short-form anonymous function's expression is followed by a " "semicolon, which is not allowed. A short-form anonymous function " "can contain only an expression, and ends with a right brace '}'." }, { TCERR_SHORT_ANON_FN_REQ_RBRACE, "missing '}' at end of anonymous function expression - found \"%~.*s\"", "There is no right brace '}' at the end of this short-form " "anonymous function. A short-form anonymous function must contain " "only an expression and end with '}'. Check the syntax." }, { TCERR_IN_REQ_LPAR, "missing '(' after 'in' operator - found \"%~.*s\"", "An open parenthesis '(' is required after the 'in' operator, but " "the compiler found \"%~.*s\". Check the syntax and insert the " "missing parenthesis." }, { TCERR_EXPECTED_IN_COMMA, "expected ',' or ')' in 'in' list, but found \"%~.*s\"", "Expected a comma ',' or right parenthesis ')' in the 'in' list, " "but found \"%~.*s\". The expressions in an 'in' list must be " "separated by commas, and the entire list must be enclosed in " "parentheses '( )'. Check for errors and unbalanced parentheses " "in the argument expression." }, { TCERR_EXPECTED_IN_RPAR, "expected ')' at end of 'in' list, but found \"%~.*s\"", "Expected a right parenthesis ')' at the end of the 'in' list, " "but found \"%~.*s\". The compiler is assuming that this is the end " "of the statement. Please insert the missing parenthesis, or check " "the argument list for unbalanced parentheses or other errors." }, { TCERR_CANNOT_MOD_OR_REP_TYPE, "objects of this type cannot be modified or replaced", "You cannot use 'modify' or 'replace' with an object of this type. " "Only objects and classes originally defined as ordinary objects " "can be modified or replaced." }, { TCERR_REQ_FOREACH_LPAR, "expected '(' after \"foreach\", but found \"%~.*s\"", "An open parenthesis '(' is required after the \"foreach\" keyword, " "but the compiler found \"%~.*s\" instead. Check the syntax " "and insert the missing parenthesis." }, { TCERR_MISSING_FOREACH_EXPR, "missing expression in \"foreach\" - found \"%~.*s\"", "This \"foreach\" statement is missing its iteration expression; " "the compiler found \"%~.*s\" when it was expecting the iteration " "expression of the form 'x in '. Check the " "syntax." }, { TCERR_FOREACH_REQ_IN, "\"in\" required in \"foreach\" - found \"%~.*s\"", "The keyword \"in\" was expected after the iteration variable " "expression in the this \"foreach\" statement, but the compiler " "found \"%~.*s\" instead. Check the syntax." }, { TCERR_REQ_FOREACH_RPAR, "missing ')' at end of \"foreach\" expression - found \"%~.*s\"", "A closing parenthesis ')' is required at the end of the \"foreach\" " "statement's expression, but the compiler found \"%~.*s\" instead. " "Check the syntax, and insert the missing parenthesis, or correct " "unbalanced parentheses or other syntax errors earlier in the " "expression." }, { TCERR_PROPDECL_REQ_SYM, "expected property name in 'property' list, but found \"%~.*s\"", "The name of a property was expected in the 'property' " "list, but the compiler found \"%~.*s\" instead. Check the " "statement syntax." }, { TCERR_PROPDECL_REQ_COMMA, "expected comma in 'property' list, but found \"%~.*s\"", "A comma was expected after a property name symbol in a " "'property' list, but the compiler found \"%~.*s\" " "instead. Check the syntax of the list and ensure that " "each item is separated from the next by a comma, and that the " "list ends with a semicolon." }, { TCERR_EXPORT_REQ_SYM, "expected symbol in 'export' statement, but found \"%~.*s\"", "A symbol name must follow the 'export' keyword, but the compiler " "found \"%~.*s\" instead. Check the syntax of the statement." }, { TCERR_EXPORT_EXT_TOO_LONG, "external name \"%~.*s\" in 'export' is too long", "The external name \"%~.*s\" in this 'export' statement is too long. " "External names are limited to the same maximum length as regular " "symbol names." }, { TCERR_UNTERM_OBJ_DEF, "unterminated object definition", "This object definition is not properly terminated - a semicolon ';' or " "closing brace '}' should appear at the end of the definition. The " "compiler found a new object definition or a statement that cannot " "be part of an object definition, but did not find the end of the " "current object definition. Insert the appropriate terminator, or " "check the syntax of the property definition. Note that this error " "is normally reported at the END of the unterminated object, so if " "the line number of the error refers to the start of a new object, " "it's probably the preceding object that is not property terminated." }, { TCERR_OBJ_DEF_REQ_RBRACE, "missing right brace at end of object definition - found \"%~.*s\"", "This object definition is missing its closing brace '}' - the " "compiler found \"%~.*s\", which the compiler must assume is the start " "of a new statement or object definition. Insert the missing " "brace. If this is actually meant to be part of the object " "definition, there is a syntax error here - check and correct the " "syntax." }, { TCERR_GRAMMAR_REQ_NAME_RPAR, "missing right parenthesis after name in 'grammar' - found \"%~.*s\"", "A right parenthsis ')' is required after the name tag in a 'grammar' " "statement, but the compiler found \"%~.*s\". Check the syntax and " "insert the missing parenthesis." }, { TCERR_PROPSET_REQ_LBRACE, "missing open brace '{' in propertyset definition - found \"%~.*s\"", "An open brace '{' is required to group the properties in the " "propertyset definition. The compiler found \"%~.*s\" where the " "open brace should be. Check the syntax and remove any extraneous " "characters or insert the missing open brace, as appropriate." }, { TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP, "missing right parentheses after group in 'grammar' - found \"%~.*s\"", "A right parenthesis ')' is required at the end of a parenthesized " "token group in a 'grammar' statement, but the compiler found " "\"%~.*s\". Check the syntax and insert the missing parenthesis." }, { TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED, "'->' is not allowed after parenthesized group in 'grammar' statement", "The arrow operator '->' is not allowed after a parenthesized group " "in a 'grammar' statement. A group is not a true sub-production, so " "it has no match object to assign to a property. If you want to " "create a run-time match object for the group, make the group into " "a rule for a separate, named production, and use the name of the " "production instead of the group in this rule." }, { TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE, "cannot use template with an unimported extern class as superclass", "This object cannot be defined with template notation (property value " "constants immediately after the superclass name or names) because " "one or more of its superclasses are unimported 'extern' classes, or " "inherit from unimported extern classes. The compiler does not have " "any class relationship information about classes that are explicitly " "defined as external ('extern') and not imported from a symbol file " "that is part of the current build, so it cannot determine which " "class's template definition to use. You must define this object's " "properties using the normal 'name = value' notation rather than " "using a template." }, { TCERR_REPLACE_OBJ_REQ_SC, "base class list required for object 'replace' definition", "This 'replace' statement replaces an object with a new definition, " "but it does not have a superclass list. A superclass list is " "required for the replacement object definition." }, { TCERR_PROPSET_TOO_DEEP, "'propertyset' nesting is too deep", "This 'propertyset' definition is too deeply nested - it's inside " "too many other 'propertyset' definitions. You must reduce " "the nesting depth." }, { TCERR_PROPSET_TOK_TOO_LONG, "expanded property name in propertyset is too long", "This property name, expanded into its full name using the " "propertyset pattern string, is too long. You must shorten the " "fully expanded name by shortening the propertyset pattern, " "shortening this property's name, or reducing the propertyset " "nesting depth." }, { TCERR_PROPSET_INVAL_PAT, "propertyset pattern string \"%~.*s\" is invalid", "The propertyset pattern string \"%~.*s\" is not valid. A propertyset " "pattern string must consist of valid symbol characters plus " "exactly one asterisk '*' (which specifies where the property " "names within the propertyset are inserted into the pattern)." }, { TCERR_PROPSET_INVAL_FORMALS, "invalid formal parameters in propertyset definition", "The propertyset formal parameter list is invalid. The parameters " "must contain exactly one asterisk '*', specifying the argument " "position where additional per-property parameters are inserted " "into the common parameter list." }, { TCERR_CIRCULAR_CLASS_DEF, "circular class definition: %~.*s is a subclass of %~.*s", "This class is defined circularly: %~.*s is a subclass of %~.*s, " "so the latter cannot also be a subclass of the former. Check the " "definition of the base class. " }, { TCERR_GRAMMAR_LIST_REQ_PROP, "property name required in list in 'grammar', but found \"%~.*s\"", "A part-of-speech list within a 'grammar' rule definition is only " "allowed to contain property names, but the compiler found \"%~.*s\" " "instead. Check the syntax. " }, { TCERR_GRAMMAR_LIST_UNCLOSED, "missing '>' in 'grammar' property list - found \"%~.*s\"", "The grammar property list is missing the closing angle bracket '>'; " "the compiler found \"%~.*s\" in the list and will assume that the '>' " "was accidentally omitted. Insert the missing '>' or remove the " "extraneous text from the list. " }, { TCERR_CONST_IDX_INV_TYPE, "invalid indexed type in constant expression - list required", "Only a list value can be indexed in a constant expression. The " "constant value being indexed in this expression is not a list. " }, { TCERR_INVAL_TRANSIENT, "'transient' is not allowed here", "The 'transient' keyword is not allowed in this context. 'transient' " "can only be used to modify an object definition; it cannot be used " "with classes, functions, templates, or any other definitions. " }, { TCERR_GRAMMAR_MOD_REQ_TAG, "'modify/replace grammar' requires name-tag", "'modify grammar' and 'replace grammar' can only be used with a grammar " "rule that includes a name-tag (in parentheses after the production " "name). The name-tag must match a grammar rule defined previously. " }, { TCERR_REQ_INTRINS_SUPERCLASS_NAME, "expected intrinsic superclass name after colon, but found \"%~.*s\"", "The name of the intrinsic class's superclass was expected after " "the colon, but the compiler found \"%~.*s\" instead. Add the " "superclass name (or remove the colon). " }, { TCERR_INTRINS_SUPERCLASS_UNDEF, "intrinsic class superclass \"%~.*s\" is not defined", "The intrinsic class superclass \"%~.*s\" is not defined. You must " "define the superclass before you define any subclasses. " }, { TCERR_GRAMMAR_ENDS_WITH_OR, "grammar rule ends with '|' (add '()' if empty last rule is " "intentional)", "The grammar rule ends with '|'. This indicates that the rule matches " "a completely empty input. This is legal, but it's uncommon. " "If you really intend for this rule to match empty input, " "you can eliminate this warning by making the empty rule " "explicit, by using '()' for the empty match rule. " }, { TCERR_GRAMMAR_EMPTY, "grammar rule is empty (add '()' if this is intentional)", "The grammar rule is empty, which indicates that the rule matches " "a completely empty input. This is legal, but it's uncommon. " "If you really intend for this rule to match empty input, " "you can eliminate this warning by making the empty rule " "explicit, by using '()' for the empty match rule. " }, { TCERR_TEMPLATE_EMPTY, "empty template definition", "The template is empty. A template definition must include at " "least one property name." }, { TCERR_REQ_RPAR_IF, "expected ')' after the \"if\" condition, but found \"%~.*s\"", "A close parenthesis ')' is required after the condition expression " "in the \"if\" statement, to balance the required open parenthesis, " "but the compiler found \"%~.*s\" instead. The compiler will assume " "that a parenthesis was intended. Please correct the syntax " "by inserting a parenthesis." }, { TCERR_INTRINS_SUPERCLASS_NOT_INTRINS, "intrinsic class superclass \"%~.*s\" is not an intrinsic class", "The intrinsic class superclass \"%~.*s\" is not itself an intrinsic " "class. An intrinsic class can only be derived from another intrinsic " "class. " }, { TCERR_FUNC_REDEF_AS_MULTIMETHOD, "function \"%.*s\" is already defined - can't redefine as a multi-method", "The function \"%.*s\" is already defined as an ordinary function, " "so it can't be redefined here with typed parameters. If you intended " "for the original version to be a multi-method, add the 'multimethod' " "modifier to its definition, immediately after the closing parenthesis " "of the parameter list in the function definition, as in " "\"foo(x) multimethod { ... }\"." }, { TCERR_MULTIMETHOD_NOT_ALLOWED, "'multimethod' is not allowed here", "The 'multimethod' modifier is not allowed in this context. " "This modifier can only be used for top-level function definitions." }, { TCERR_MMPARAM_NOT_OBJECT, "parameter type \"%~.*s\" is not an object", "The declared parameter type \"%~.*s\" is not an object. Only object " "or class names can be used to declare parameter types. Check the " "definition of the function and make sure the type name is correct, " "and that the same name isn't used elsewhere as a non-object type." }, { TCERR_MMINH_MISSING_ARG_TYPE, "expected a type name in inherited<> type list, but found \"%~.*s\"", "An inherited<> type list requires a type name (a class or object " "name, or '*' or '...') in each position. The compiler found " "\"%~.*s\" where it expected to find a type. The type must be " "written as a simple name, with no parentheses or other punctuation." }, { TCERR_MMINH_MISSING_COMMA, "expected a comma in inherited<> type list, but found \"%~.*s\"", "The compiler found \"%~.*s\" where it expected to find a comma " "in an inherited<> type list. Each element in the list must be " "separated by a comma." }, { TCERR_MMINH_MISSING_GT, "missing '>' in inherited<> type list (found \"%~.*s\")", "An inherited<> type list must end with a '>'. The compiler " "found \"%~.*s\", and is assuming that the list ends here. If " "this isn't the end of the type list, check the syntax; otherwise, " "just add '>' at the end of the list." }, { TCERR_MMINH_MISSING_ARG_LIST, "argument list missing in inherited<> (expected '(', found \"%~.*s\")", "An inherited<> expression requires an argument list. The compiler " "found \"%~.*s\" it expected the opening '(' of the argument list. " "If the function doesn't take any arguments, use empty parentheses, " "'()', to indicate the empty argument list." }, { TCERR_NAMED_ARG_NO_ELLIPSIS, "a pass-by-name argument value cannot use an ellipsis '...'", "A named argument value (\"name: expression\") cannot be combined " "with an ellipsis '...'. The ellipsis expands a list value into " "a series of positional arguments. This isn't meaningful with a " "named argument, because a named argument is passed by name rather " "than by position." }, { TCERR_ARG_MUST_BE_OPT, "parameter '%.*s' must be optional because it follows an " "optional parameter", "The parameter variable '%.*s' in this function or method definition " "must be declared as optional, because it follows one or more other " "optional arguments in the list. Argument values are assigned to parameter " "names in left-to-right order at run-time, so once you have one optional " "parameter to the list, all of the parameters that follow it must be " "optional as well. You can make this parameter optional by adding " "a '?' suffix after the variable name, or by adding an '= expression' " "default value expression after the name." }, { TCERR_NAMED_ARG_NO_TYPE, "formal parameter '%.*s' is named - a type declaration isn't allowed", "The formal parameter '%.*s' is named (it uses a ':' suffix), so it " "can't have a type declaration. Multimethod selection is based " "strictly on the positional arguments; named parameters aren't " "used in the selection process, so they can't be declared with types." }, { TCERR_LOOKUP_LIST_EXPECTED_ARROW, "missing -> in %dth element in LookupTable list", "An arrow '->' was expected in the %dth element of the LookupTable " "list. This list appears to be a LookupTable list, because earlier " "elements use the Key->Value notation, so every element must given " "as a Key->Value pair." }, { TCERR_ARROW_IN_LIST, "unexpected -> found in list (after %dth element)", "An arrow '->' was found in a list expression, after the %dth element. " "The arrow isn't allowed here because the expression appears to be an " "ordinary list, not a LookupTable list. If you intended this to be " "a LookupTable list, every element must be given as a Key->Value pair." }, { TCERR_MISPLACED_ARROW_IN_LIST, "misplaced -> in LookupTable list after %dth element - expected comma", "A comma ',' should appear after the %dth element of the LookupTable " "list, but an arrow '->' was found instead. The arrow appears to be " "misplaced. Each Key->Value pair in a LookupTable list should be " "separated from the next by a comma." }, { TCERR_LOOKUP_LIST_EXPECTED_END_AT_DEF, "LookupTable list must end after default *-> value, but found \"%~.*s\"", "A LookupTable list must end after the default value (specified with " "\"*->Value\"), but the compiler found \"%~.*s\" where the closing " "bracket ']' should be. Rearrange the list so that the default " "value is in the final position in the list." }, { TCERR_OPER_IN_PROPSET, "an 'operator' property cannot be defined within a 'propertyset'", "An 'operator' property cannot be defined within a 'propertyset' " "group. You must move the 'operator' property definition outside " "of the group." }, { TCERR_EXPECTED_RBRACK_IN_OP, "expected ']' in 'operator []', found '%~.*s'", "The ']' in an 'operator []' property declaration was missing (the " "compiler found '%~.*s' where the ']' should go)." }, { TCERR_BAD_OP_OVERLOAD, "invalid overloaded operator '%~.*s'", "The 'operator' property type requires one of the overloadable " "operators, but the compiler found '%~.*s' where the operator " "should appear. You must specify one of the overloadable operator " "symbols: + - * / % ^ << >> ~ | & [] []=" }, { TCERR_OP_OVERLOAD_WRONG_FORMALS, "wrong number of parameters defined for overloaded operator (%d required)", "This 'operator' property has the wrong number of parameters. " "This overloaded operator requires %d parameter(s)." }, { TCERR_RT_CANNOT_DEFINE_GLOBALS, "new global symbols (\"%~.*s\") cannot be defined in " "run-time code compilation", "Code compiled during program execution cannot define new global " "symbols. The code being compiled attempted to define \"%~.*s\"." }, { TCERR_FOR_IN_NOT_LOCAL, "'for..in' variable %.*s is not defined as a local variable", "The variable \"%.*s\" is being used as the control variable " "of a 'for..in' statement, but this variable isn't defined as a " "local variable. Check that the variable name is correct. If so, " "you must add a 'local' statement defining this variable (or simply " "add the word 'local' before the variable name in the 'in' clause)." }, { TCERR_BAD_EMBED_END_TOK, "<<%.*s>> found in string without a matching %s", "The compiler found the embedded keyword <<%.*s>> in this string, " "but this wasn't preceded by a matching %s. Check that all " "keywords are present and spelled correctly. If you're using nested " "<>'s or the like, the problem might be that the begin/end " "aren't balanced properly for one of the nested levels." }, { TCERR_STRTPL_MISSING_LANGLE, "expected '<<' at start of string template, but found '%.*s'", "The compiler expected to find '<<' at the start of the string " "template definition (after the word 'template'), but found '%.*s'. " "Check the syntax, and insert the missing '<<'." }, { TCERR_STRTPL_MISSING_RANGLE, "expected '>>' at end of string template, but found '%.*s'", "The compiler expected to find '>>' at the end of the string " "template definition, but found '%.*s'. This symbol isn't allowed " "within a string template, so the compiler is guessing that this " "is really the end of the statement and that the template is missing " "its closing '>>'. Check the syntax. If you really meant to include " "this symbol in the template, you must change it to something else, " "since it's not an allowed template element. If you simply left off " "the closing '>>', add it at the end of the definition." }, { TCERR_STRTPL_MULTI_STAR, "only one '*' is allowed in a string template", "This string template definition has more than one '*' symbol. " "Only one '*' is allowed in a string template." }, { TCERR_STRTPL_FUNC_MISMATCH, "string template processor function %.*s() does not match template - " "must take %d parameter(s) and return a value", "The processor function %.*s() for this string template does not " "match the template usage. It must be defined as a function taking " "%d argument(s) and returning a value." }, { TCERR_DEFINED_SYNTAX, "invalid syntax for defined() operator - argument must be a symbol name", "The syntax for the defined() operator is invalid. This operator " "requires a single symbol name as the argument, in parentheses." }, { TCERR___OBJREF_SYNTAX, "invalid syntax for __objref() operator", "The syntax for the __objref() operator is invalid. This operator " "requires syntax of the form __objref(symbol) or __objref(symbol, mode), " "where the mode is 'warn' or 'error'." }, { TCERR_BAD_OP_FOR_FLOAT, "floating point values can't be used with this operator", "Floating point values can't be used with this operator. Only ordinary " "integer values can be used." }, { TCERR_INLINE_OBJ_REQ_LBRACE, "expected inline object property list starting with '{' (found '%.*s')", "An inline property definition must contain a property list enclosed " "in braces, '{ ... }'. The compiler found '%.*s' where it expected " "to see the left brace '{' at the start of the property list. Check " "the object definition syntax." }, { TCERR_CODEGEN_NO_MEM, "out of memory for code generation", "Out of memory. The compiler cannot allocate memory to generate " "the compiled code for the program. Make more memory available, " "if possible (by closing other applications, for example), then " "try running the compiler again." }, { TCERR_UNRES_TMP_FIXUP, "%d unresolved branches", "%d unresolved branches." }, { TCERR_WRITEAT_PAST_END, "attempting to write past end of code stream", "The compiler's code generator is attempting to " "write past the end of the code stream." }, { TCERR_SELF_NOT_AVAIL, "'self' is not valid in this context", "The 'self' object is not available in this context. You can only " "refer to 'self' within a method associated with an object; you cannot " "use 'self' in a function or in other code that is not part of an " "object's method." }, { TCERR_INH_NOT_AVAIL, "'inherited' is not valid in this context", "The 'inherited' construct is not available in this context. You " "can only use 'inherited' within a method associated with an object " "with a superclass." }, { TCERR_NO_ADDR_SYM, "cannot take address of \"%~.*s\"", "You cannot take the address of \"%~.*s\". You can only take the " "address of a function or a property." }, { TCERR_CANNOT_ASSIGN_SYM, "cannot assign to \"%~.*s\"", "You cannot assign a value to \"%~.*s\". You can only assign a value " "to an object property, to a local variable or parameter, or to a " "subscripted element of a list." }, { TCERR_CANNOT_EVAL_LABEL, "\"%~.*s\" is a label and cannot be evaluated in an expression", "The symbol \"%~.*s\" is a code label; this symbol cannot be " "evaluated in an expression. You can only use this symbol " "in a 'goto' statement." }, { TCERR_CANNOT_CALL_SYM, "cannot call \"%~.*s\" as a method or function", "The symbol \"%~.*s\" is not a valid method, function, or expression " "value. You cannot invoke this symbol as a method or function call. " "Check spelling, and check for missing operators or mismatched " "parentheses." }, { TCERR_PROP_NEEDS_OBJ, "cannot call property \"%~.*s\" without \"object.\" prefix - no \"self\"", "You cannot call the property \"%~.*s\" in this context without an " "explicit \"object.\" prefix, because no implied \"self\" object " "exists in this context. A \"self\" object exists only inside object " "method code. Check spelling and syntax to ensure you meant to call " "a property, and add the object qualifier if so." }, { TCERR_VOID_RETURN_IN_EXPR, "\"%~.*s\" has no return value - cannot use in an expression", "\"%~.*s\" has no return value, so it cannot be used in an expression " "(there's no value to use in further calculations). Check the " "definition of the function or method." }, { TCERR_INVAL_FUNC_ADDR, "'&' cannot be used with a function name (\"%~.*s\")", "The '&' operator cannot be used with function name \"%~.*s\". If you " "want a reference to the function, simply use the name by itself with " "no argument list. If you want to call the function, put the argument " "list in parentheses '( )' after the function name; if the function " "takes no arguments, use empty parentheses '()' after the function's " "name." }, { TCERR_INVAL_NEW_EXPR, "cannot apply operator 'new' to this expression", "You cannot apply operator 'new' to this expression. 'new' can " "only be applied to an object or metaclass name, followed by an " "optional argument list. Check for syntax errors." }, { TCERR_TOO_FEW_FUNC_ARGS, "too few arguments to function \"%~.*s\"", "Not enough arguments are specified in the call to the " "function \"%~.*s\". Check the definition of the function, " "and check for unbalanced parentheses or other syntax errors." }, { TCERR_TOO_MANY_FUNC_ARGS, "too many arguments to function \"%~.*s\"", "Too many arguments are specified in the call to the " "function \"%~.*s\". Check the definition of the function, " "and check for unbalanced parentheses or other syntax errors." }, { TCERR_SETPROP_NEEDS_OBJ, "cannot assign to property \"%~.*s\" without \"object.\" prefix - " "no \"self\"", "You cannot assign to the property \"%~.*s\" in this context without an " "explicit \"object.\" prefix, because no implied \"self\" object " "exists in this context. A \"self\" object exists only inside object " "method code. Check spelling and syntax to ensure you meant to assign " "to a property, and add the object qualifier if so." }, { TCERR_SYM_NOT_PROP, "invalid '.' expression - symbol \"%~.*s\" is not a property", "The symbol \"%~.*s\" cannot be used after a period '.' because this " "symbol is not a property. The period in a member evaluation " "expression must be followed by a property name, or by an expression " "that yields a property pointer value. Check spelling and check" "for syntax errors." }, { TCERR_INVAL_PROP_EXPR, "invalid '.' expression - right side is not a property", "This property evaluation expression is invalid. The period '.' " "in a property expression must be followed by a property name, " "or by an expression that yields a property pointer value. Check " "spelling and check for syntax errors." }, { TCERR_INH_NOT_OBJ, "illegal 'inherited' - \"%~.*s\" is not a class", "This 'inherited' expression is not valid, because \"%~.*s\" is not " "a class. 'inherited' must be followed by a period '.' or by the " "name of a superclass of this method's object. Check spelling and " "expression syntax." }, { TCERR_INVAL_OBJ_EXPR, "illegal expression - left of '.' is not an object", "The expression on the left of the period '.' is not an object value. " "The left of a '.' expression must be an object name, or an expression " "that evaluates to an object value. Check spelling and syntax." }, { TCERR_SYM_NOT_OBJ, "symbol \"%~.*s\" is not an object - illegal on left of '.'", "The symbol \"%~.*s\" is not an object, so you cannot use this " "symbol on the left side of a period '.' expression. The left side of a " "'.' expression must be an object name or an expression that evaluates " "to an object value. Check the spelling and syntax." }, { TCERR_IF_ALWAYS_TRUE, "\"if\" condition is always true - \"else\" part is unreachable", "The condition in this \"if\" statement is always true, which means " "that the code in the \"else\" clause will never be executed. If this " "is unintentional, check the condition to ensure it's correct." }, { TCERR_IF_ALWAYS_FALSE, "\"if\" condition is always false", "The condition in this \"if\" statement is always false, which means " "that the statement or statements following the \"if\" will never " "be executed. If this is unintentional, check the condition to " "ensure it's correct." }, { TCERR_INVALID_BREAK, "invalid \"break\" - not in a for/while/do/switch", "This \"break\" statement is invalid. A \"break\" can only appear " "in the body of a loop (\"for\", \"while\", or \"do\") or within a " "case in a \"switch\" statement." }, { TCERR_INVALID_CONTINUE, "invalid \"continue\" - not in a for/while/do", "This \"continue\" statement is invalid. A \"continue\" can only appear " "in the body of a loop (\"for\", \"while\", or \"do\")." }, { TCERR_MAIN_NOT_DEFINED, "function _main() is not defined", "No function named _main() is defined. This function must be defined, " "because _main() is the entrypoint to the program at run-time." }, { TCERR_MAIN_NOT_FUNC, "_main is not a function", "The global symbol \"_main\" does not refer to a function. This symbol " "must be defined as a function, because _main() is the entrypoint " "to the program at run-time." }, { TCERR_CATCH_EXC_NOT_OBJ, "exception class symbol \"%~.*s\" in \"catch\" clause is not an object", "The exception class symbol \"%~.*s\" used in this \"catch\" clause " "is not an object class. Only an object class can be used to " "specify the type of exception to catch." }, { TCERR_INVALID_BREAK_LBL, "invalid \"break\" - no label \"%~.*s\" on any enclosing statement", "This \"break\" is invalid because the label \"%~.*s\" does not " "refer to an enclosing statement. When a label is used with " "\"break\", the label must refer to an enclosing statement. " "Check for unbalanced braces, and check that the label is correct." }, { TCERR_INVALID_CONT_LBL, "invalid \"continue\" - no label \"%~.*s\" on any enclosing loop", "This \"continue\" is invalid because the label \"%~.*s\" does not " "refer to an enclosing statement. When a label is used with " "\"continue\", the label must refer directly to an enclosing loop " "(\"while\", \"for\", or \"do\") statement. Check for unbalanced " "braces, and check that the label is correct." }, { TCERR_INVALID_GOTO_LBL, "invalid \"goto\" - label \"%~.*s\" is not defined in this function " "or method", "This \"goto\" is invalid because the label \"%~.*s\" is not defined " "in this function or method. Check the label name and correct " "any misspelling, or insert the missing label definition." }, { TCERR_UNRESOLVED_EXTERN, "unresolved external reference \"%~.*s\"", "The symbol \"%~.*s\" was defined as an external reference but " "is not defined anywhere in the program. Determine where this " "symbol should be defined, and make sure that the source file " "that contains the definition is included in the compilation." }, { TCERR_UNREFERENCED_LABEL, "label \"%~.*s\" is not referenced", "The statement label \"%~.*s\" is not referenced in any \"goto\", " "\"break\", or \"continue\" statement. The label is unnecessary " "and can be removed." }, { TCERR_UNREFERENCED_LOCAL, "local variable \"%~.*s\" is never used", "The local variable \"%~.*s\" is never used. This local variable " "can be removed. Check to make sure that the local variable isn't " "hidden by another variable of the same name inside nested braces '{ }' " "within the code." }, { TCERR_UNASSIGNED_LOCAL, "no value is assigned to local variable \"%~.*s\"", "No value is assigned to the local variable \"%~.*s\". " "You should initialize the variable with a value before its first " "use to make the meaning of the code clear." }, { TCERR_UNUSED_LOCAL_ASSIGNMENT, "value is assigned to local variable \"%~.*s\" but the value is " "never used", "A value is assigned to the local variable \"%~.*s\", but the variable's " "value is never used. It is possible that the variable can be " "removed." }, { TCERR_SC_NOT_OBJECT, "invalid superclass \"%~.*s\" - not an object", "The superclass \"%~.*s\" is not valid, because the symbol does not " "refer to an object. Each superclass of an object must be a class " "or object." }, { TCERR_ARG_EXPR_HAS_NO_VAL, "argument expression %d yields no value", "The argument expression in position %d in the argument list yields no " "value. This usually indicates that the argument expression uses " "a double-quoted (self-printing) string where a single-quoted string " "was intended." }, { TCERR_ASI_EXPR_HAS_NO_VAL, "right side of assignment has no value", "The expression on the right side of the assignment operator yields " "no value. This usually indicates that the expression after the " "assignment operator uses a double-quoted (self-printing) string " "where a single-quoted string was intended." }, { TCERR_WRONG_ARGC_FOR_FUNC, "wrong number of arguments to function \"%~.*s\": %s required, %d actual", "The call to function \"%~.*s\" has the wrong number of arguments. " "The function requires %s argument(s), but this call has %d. Check " "the argument list." }, { TCERR_GOTO_INTO_FINALLY, "'goto' cannot transfer control into a 'finally' block", "This 'goto' statement transfers control to a label defined in a " "'finally' block, but the 'goto' statement is not within the same " "'finally' block. This type of transfer is illegal because it would " "leave the exit path from the 'finally' undefined." }, { TCERR_UNREFERENCED_PARAM, "formal parameter \"%~.*s\" is never used", "The formal parameter \"%~.*s\" is never used. In many cases this " "doesn't indicate a problem, since a method might receive parameters " "it doesn't actually need - the method interface might be inherited " "from a base class, or the extra parameters might be included " "for possible future use or for architectural reasons. You might " "want to check that the parameter really wasn't meant to be used; in " "particular, check to make sure that the parameter isn't hidden " "by another variable of the same name inside nested braces '{ }' " "within the code." }, { TCERR_GRAMPROD_HAS_NO_ALTS, "grammar production \"%~.*s\" has no alternatives defined", "The grammar production symbol \"%~.*s\" has no alternatives defined. " "This probably indicates that this symbol was accidentally " "misspelled when used as a sub-production in a grammar rule. Check " "for occurrences of this symbol in grammar rules, and correct the " "spelling if necessary. If the symbol is spelled correctly, add " "at least one grammar rule for this production." }, { TCERR_CANNOT_MOD_META_PROP, "intrinsic class property \"%~.*s\" cannot be modified", "The property \"%~.*s\" is defined in this intrinsic class, " "so it cannot be modified. You can only add new properties to " "an intrinsic class; you can never override, modify, or replace " "properties defined in the intrinsic class itself." }, { TCERR_FOREACH_NO_CREATEITER, "Container.createIterator is not defined - %s..in is not valid", "The intrinsic class Container's method createIterator is not defined, " "so the %s..in syntax cannot be used. This should be corrected if you " "#include or in this source file." }, { TCERR_FOREACH_NO_GETNEXT, "Iterator.getNext is not defined - %s..in cannot be used", "The intrinsic class Iterator's method getNext is not defined, " "so the '%s..in' statement cannot be used. This should be corrected if " "you #include or in this source file." }, { TCERR_FOREACH_NO_ISNEXTAVAIL, "Iterator.isNextAvailable is not defined - %s..in cannot be used", "The intrinsic class Iterator's method isNextAvailable is not defined, " "so the '%s..in' statement cannot be used. This should be corrected if " "you #include or in this source file." }, { TCERR_INVALID_TYPE_FOR_EXPORT, "symbol \"%~.*s\" is not a valid type for export", "The symbol \"%~.*s\" appears in an 'export' statement, but this " "symbol is not of a valid type for export. Only object and property " "names may be exported." }, { TCERR_DUP_EXPORT, "duplicate export for external name \"%~.*s\": \"%~.*s\" and \"%~.*s\"", "Invalid duplicate 'export' statements. The external name \"%~.*s\" " "has been given to the exported symbols \"%~.*s\" and \"%~.*s\". An " "external name can be given to only one symbol. You must remove " "one of the duplicate exports." }, { TCERR_DUP_EXPORT_AGAIN, "additional duplicate export for external name \"%~.*s\": \"%~.*s\"", "Additional duplicate export: the external name \"%~.*s\" has also " "been exported with symbol \"%~.*s\". Only one export per external " "name is allowed." }, { TCERR_TARGETPROP_NOT_AVAIL, "'targetprop' is not available in this context", "The 'targetprop' value is not available in this context. You can only " "refer to 'targetprop' within a method associated with an object; " "you cannot use 'targetprop' in a function or in other code that is " "not part of an object's method." }, { TCERR_TARGETOBJ_NOT_AVAIL, "'targetobj' is not available in this context", "The 'targetobj' value is not available in this context. You can only " "refer to 'targetobj' within a method associated with an object; " "you cannot use 'targetobj' in a function or in other code that is " "not part of an object's method." }, { TCERR_DEFININGOBJ_NOT_AVAIL, "'definingobj' is not available in this context", "The 'definingobj' value is not available in this context. You can only " "refer to 'definingobj' within a method associated with an object; " "you cannot use 'definingobj' in a function or in other code that is " "not part of an object's method." }, { TCERR_CANNOT_CALL_CONST, "this constant expression cannot be called as a function", "This constant expression cannot be called as a function. Check for " "a missing operator before an open parenthesis, since this can make " "an expression look like a function call. " }, { TCERR_REPLACED_NOT_AVAIL, "'replaced' is not available in this context", "The 'replaced' keyword is not available in this context. You can only " "use 'replaced' within a function you are defining with 'modify'. " }, { TCERR_GRAMPROD_TOO_MANY_ALTS, "grammar production \"%~.*s\" has too many (>65,535) rules", "The grammar production symbol \"%~.*s\" has too many rules - a " "single production is limited to 65,535 rules, taking into " "account all 'grammar' statements for the production. This probably " "indicate that the production has one or more grammar rules with " "deeply nested '|' alternatives. Check for very complex 'grammar' " "statements, and replace complex '|' structures with separate " "sub-productions." }, { TCERR_BAD_META_FOR_NEW, "invalid 'new' - cannot create an instance of this type", "The 'new' operator cannot be used to create an instance of this " "type of object. " }, { TCERR_MMINH_TYPE_NOT_OBJ, "'inherited<>' type list element \"%~.*s\" is not an object name", "Type names in an 'inherited<>' type list must be object or " "class names. \"%~.*s\" is not an object name, so it can't be used " "as a type name here." }, { TCERR_MMINH_BAD_CONTEXT, "'inherited<>' is not valid here; it can only be used in a multi-method", "'inherited<>' is not valid here. This syntax can only be used " "within a function defined as a multi-method." }, { TCERR_MMINH_MISSING_SUPPORT_FUNC, "'inherited<>' requires \"%~.*s\", which is not defined " "or not a function", "'inherited<>' requires the library function \"%~.*s\" to be included " "included in the build. This is not defined or is not a function. " "Check that the library file 'multmeth.t' is included in the build." }, { TCERR_MMINH_UNDEF_FUNC, "'inherited<>' function \"%~.*s\" undefined", "The function \"%~.*s\" with the specified inherited<> type list is " "not defined. The inherited<> type list must exactly match the " "definition of the function you wish to call." }, { TCERR_NAMED_PARAM_MISSING_FUNC, "named parameters require support function \"%~.*s\", which is undefined", "This program uses named parameters, so it depends on the built-in " "support function \"%~.*s\". This is not defined or is not a built-in " "function. Check that you're including the current version of the " "system header file 't3.h' in the build." }, { TCERR_OPT_PARAM_MISSING_FUNC, "optional parameters require support function \"%~.*s\", which is undefined", "This program uses optional parameters, so it depends on the built-in " "support function \"%~.*s\". This is not defined or is not a built-in " "function. Check that you're including the current version of the " "system header file 't3.h' in the build. (An optional parameter is " "a function or method argument declared with a '?' after the variable " "name, or with '=' and a default expression after the name." }, { TCERR_ONEOF_REQ_GENCLS, "<> requires class OneOfIndexGen to be defined in the program", "<> requires your program to define the class OneOfIndexGen. " "This class isn't defined. Make sure that you're using the current " "version of the system library file _main.t." }, { TCERR_ONEOF_REQ_GETNXT, "<> requires getNextIndex to be defined as a property", "<> requires your program to define the property getNextIndex, " "as part of class OneOfIndexGen. This symbol is used as a different " "type in the program. Make sure that you're using the current version " "of the sy stem library file _main.t" }, { TCERR_EXT_METACLASS, "intrinsic class '%.*s' is not defined in this module", "The intrinsic class '%.*s' is not defined in this source module. " "To refer to this class within this module, you must declare it. " "The usual way to declare an intrinsic class is simply to #include " "the system header file that defines the class, near the beginning " "of your source file." }, { TCERR_FUNC_CALL_NO_PROTO, "cannot call extern function %.*s without an argument list definition", "The function %.*s was declared 'extern' without an argument list " "definition. This function can't be called without declaring the " "argument list. You can declare the argument list in the 'extern' " "statement, but note that in most cases the problem is that the " "function itself is never defined within the program." }, { TCERR_UNDEF_METACLASS, "\"%.*s\" is not defined or is not an intrinsic class", "The compiler requires \"%.*s\" to be defined as an intrinsic class; " "the symbol is either undefined or is defined as a different type. " "Check that the system header that defines this class is #included " "in this source file, and that there are no conflicting definitions " "of the class name." }, { TCERR_SYMEXP_INV_TYPE, "invalid symbol type in symbol file", "Invalid symbol type in symbol file. The symbol file is either " "corrupted or was created by an incompatible version of the " "compiler. Regenerate the symbol file." }, { TCERR_SYMEXP_INV_SIG, "invalid symbol file signature", "Invalid symbol file signature. The symbol file is corrupted, " "was generated by an incompatible verison of the compiler, or is " "not a symbol file. Delete this symbol file and re-generate it " "from its source file. (If you do not have access to the source" "file, you must obtain an updated symbol file from the person or " "vendor who provided you with the original symbol file.)" }, { TCERR_SYMEXP_SYM_TOO_LONG, "symbol name too long in symbol file", "A symbol name stored in the symbol file is too long. The symbol " "file is either " "corrupted or was created by an incompatible version of the " "compiler. Regenerate the symbol file." }, { TCERR_SYMEXP_REDEF, "symbol \"%~.*s\" in imported symbol file is already defined; ignoring " "redefinition", "The symbol \"%~.*s\" is defined in the symbol file, but is already " "defined. This usually indicates that two symbol files that you're " "importing both define this symbol as a function, object, or property " "name (the two files might define the symbol as the same type, or as " "different types - it doesn't matter, because the symbol can only be " "used once for any of these types). You must resolve the conflict " "by renaming the symbol in one or more of the source files to make each " "name unique." }, { TCERR_OBJFILE_INV_SIG, "invalid object file signature", "Invalid object file signature. The object file is corrupted, " "was generated by an incompatible version of the compiler, or " "is not an object file. Delete this object file and re-compile " "it from its source file. (If you do not have access to the source " "file, you must obtain an updated object file from the person or " "vendor who provided you with the original object file." }, { TCERR_OBJFILE_TOO_MANY_IDS, "too many object/property ID's in object file", "This object file contains too many object or property ID's. The " "compiler cannot load this object file on this operating system. " "This usually happens only on 16-bit computers, and indicates that " "the object file exceeds the architectural limit of the compiler " "on this machine. You might be able to work around this by reducing " "the number of objects in this object file, which you can probably do " "by splitting the source file into multiple files and compiling each " "one into its own object file. If you're running on MS-DOS on a " "386 or higher processor, you can use a 32-bit version of the " "compiler, which does not have this limit." }, { TCERR_OBJFILE_OUT_OF_MEM, "out of memory loading object file", "Out of memory. The compiler cannot " "allocate memory necessary to load the object file. Make more " "memory available, if possible (by closing other applications, " "for example), then try running the compiler again." }, { TCERR_OBJFILE_INV_TYPE, "invalid symbol type in object file", "Invalid symbol type in object file. The object file is either " "corrupted or was created by an incompatible version of the " "compiler. Re-compile the object file." }, { TCERR_OBJFILE_REDEF_SYM_TYPE, "symbol \"%~.*s\" (type: %s) redefined (type: %s) in object file \"%s\"", "The symbol \"%~.*s\", which was originally defined of type %s, is " "redefined with type %s in object file \"%s\". A global symbol can " "be defined only once in the entire program. You must change one of " "the symbol's names in one of your source files to remove the " "conflict.\n\n" "If you recently changed the meaning of this symbol, you might " "simply need to do a full recompile - try building again with the -a " "option. " }, { TCERR_OBJFILE_REDEF_SYM, "symbol \"%~.*s\" (type: %s) redefined in object file \"%s\"", "The symbol \"%~.*s\" (of type %s) is redefined in object file \"%s\". " "A global symbol can be defined only once in the entire program. " "You must change one of names in one of your source files to " "remove the conflict." }, { TCERR_OBJFILE_BIF_INCOMPAT, "intrinsic function \"%~.*s\" has different definition in object file " "\"%s\"", "The intrinsic function \"%~.*s\" has a different definition in object " "file \"%s\" than in its previous definition. The intrinsic function " "sets used in your program must be identical in each object file. " "Check the \"intrinsic\" definition statements in each source file " "and ensure that they all match." }, { TCERR_OBJFILE_FUNC_INCOMPAT, "function \"%~.*s\" has different definition in object file " "\"%s\"", "The function \"%~.*s\" has a different definition in object " "file \"%s\" than in its previous definition. This probably indicates " "that one or more of your object or symbol files are out of date " "and must be recompiled." }, { TCERR_OBJFILE_INT_SYM_MISSING, "internal symbol reference \"%~.*s\" missing in object file \"%s\"", "The internal symbol reference \"%~.*s\" is missing in the object " "file \"%s\". This is an error in the object file and probably indicates" " that the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_STREAM_ID, "invalid stream ID in object file \"%s\"", "The object file \"%s\" contains an invalid internal stream ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_OBJ_ID, "invalid object ID in object file \"%s\"", "The object file \"%s\" contains an invalid object ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INVAL_PROP_ID, "invalid property ID in object file \"%s\"", "The object file \"%s\" contains an invalid property ID code. " "This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_INV_FN_OR_META, "invalid function set or metaclass data in object file \"%s\"", "The object file \"%s\" contains invalid function set or metaclass " "data. This is an error in the object file and probably indicates that " "the file is corrupted. Delete the object file and recompile its " "source file." }, { TCERR_OBJFILE_FNSET_CONFLICT, "conflicting intrinsic function set \"%s\" found in object file \"%s\"", "The intrinsic function set \"%s\" in object file \"%s\" conflicts with " "a previous intrinsic definition from other object files. Each " "object file must define an identical list of intrinsics, in " "the same order. Examine your source files and use the same " "intrinsic definitions in all files." }, { TCERR_OBJFILE_META_CONFLICT, "conflicting metaclass \"%s\" found in object file \"%s\"", "The metaclass \"%s\" in object file \"%s\" conflicts with " "a previous metaclass definition from other object files. Each " "object file must define an identical list of metaclasses, in " "the same order. Examine your source files and use the same " "metaclass definitions in all files." }, { TCERR_OBJFILE_MODREPOBJ_BEFORE_ORIG, "modified or replaced object \"%~.*s\" found in object file \"%s\" " "before original object definition was loaded", "The object \"%~.*s\" in object file \"%s\" is defined with \"replace\" " "or \"modify\", but this object file was loaded before the object file " "containing the original definition of the object. You must change " "the order in which you're loading the object files so that the " "object file containing the original definition of each object is " "loaded before the object's first modification or replacement." }, { TCERR_OBJFILE_REPFUNC_BEFORE_ORIG, "modified or replaced function \"%~.*s\" found in object file \"%s\" " "before original function definition was loaded", "The function \"%~.*s\" in object file \"%s\" is defined with " "\"modify\" or \"replace\", but this object file was loaded before the " "object file containing the original definition of the function. You " "must change the order in which you're loading the object files so " "that the object file containing the original definition of each " "function is loaded before the function's first replacement." }, { TCERR_CONSTRUCT_CANNOT_RET_VAL, "\"construct\" cannot return a value", "The \"construct\" method cannot return a value. Remove the " "return value expression." }, { TCERR_OBJFILE_NO_DBG, "object file \"%s\" has no debugger information " "(required to link for debug)", "The object file \"%s\" has no debugger information. In order to " "include debug information in the image file, all object files must be " "compiled with debugging information. Recompile the object file with " "debug information and then re-link the program." }, { TCERR_OBJFILE_METACLASS_PROP_CONFLICT, "intrinsic class property \"%~.*s\" does not match " "previous name (\"%~.*s\") in object file %s", "The intrinsic class property \"%~.*s\" does not match its previous " "name (\"%~.*s\") in object file %s. Each property defined in an " "intrinsic class must match in all object files where the " "intrinsic class is declared. You must recompile your source code " "using a single definition for the intrinsic class that is the same " "in each file." }, { TCERR_OBJFILE_METACLASS_IDX_CONFLICT, "intrinsic class \"%~.*s\" in object file %s out of order with " "previous definition", "The intrinsic class \"%~.*s\" is defined in object file %s in a " "different order than it appeared in previous object files. Each " "object file must define all of its intrinsic classes in the same " "order as all other object files. You must recompile your source " "code using a single set of intrinsic class definitions, all in the " "same order." }, { TCERR_OBJFILE_CANNOT_MOD_OR_REP_TYPE, "cannot modify or replace object of this type - \"%~.*s\" in " "object file %s", "You cannot use 'modify' or 'replace' to modify an object of this " "type. The symbol \"%~.*s\" is being modified or replaced in object " "file %s, but the symbol was previously defined using an incompatible " "type. You can only modify or replace ordinary objects." }, { TCERR_EXPORT_SYM_TOO_LONG, "exported symbol in object file is too long - object file may be " "corrupted", "An exported symbol in this object file is too long. This probably " "indicates a corrupted object file. Recompile the source code to " "create a new object file. If the object file was just compiled, " "this probably indicates an internal error in the compiler." }, { TCERR_OBJFILE_MACRO_SYM_TOO_LONG, "macro symbol too long in object file debug records - object file may " "be corrupted - object file %s", "A macro symbol name in the object file %s is too long. This probably " "indicates a corrupted object file. Recompile the source code to " "create a new object file. If the object file was just compiled, " "this probably indicates an internal error in the compiler. " }, { TCERR_OBJFILE_MMFUNC_INCOMPAT, "function \"%~.*s\" has conflicting definitions as multi-method and " "as regular function - object file %s", "The function \"%~*.s\" has a definition in object file \"%s\" that " "conflicts with one or more definitions in other object files. The " "function is defined both as an ordinary function and as a multi-method. " "The function must be defined exclusively as one or the other. If the " "ordinary function definition was meant to be a multi-method definition, " "add the \"multimethod\" modifier keyword immediately after the " "closing parenthesis of the parameter list in that definition." }, { TCERR_OBJFILE_MMPARAM_NOT_OBJECT, "parameter type \"%~.*s\" in multi-method \"%~.*s\" is not an object", "The declared parameter type \"%~.*s\" in the definition of the " "multi-method function \"%~.*s\" is not an object. Only object or " "class names can be used to declare parameter types. Check the " "definition of the function and make sure the type name is correct, " "and that the same name isn't used elsewhere as a non-object type." }, { TCERR_MISSING_MMREG, "library support function \"%s\" is missing or invalid - " "check that the system library is included in the build", "The system library function \"%s\" is missing or " "is not defined as a function. This function is part of the system " "run-time library, which is usually included by default in the build. " "Check that system.tl is included in the build and that the " "multi-method support module is not excluded." }, { TCERR_CONST_POOL_OVER_32K, "constant value exceeds 32k - program will not run on 16-bit machines", "This constant data item (string or list constant) exceeds 32k in " "length (as stored in the compiled program file). This program will " "not work on 16-bit computers. If you want the program to be able " "to run on 16-bit machines, break up the string or list into multiple " "values, and manipulate them separately." }, { TCERR_CONST_TOO_LARGE_FOR_PG, "string or list constant exceeds constant page size", "A string or list constant is too large to fit on " "a single constant page." }, { TCERR_CORRUPT_LIST, "corrupted internal list data", "Corrupted internal list data." }, { TCERR_GEN_CODE_INH, "attempting to generate 'inherited' node directly", "Attempting to generate 'inherited' node directly." }, { TCERR_GEN_UNK_CONST_TYPE, "unknown constant type in code generation", "Unknown constant type in code generation." }, { TCERR_GEN_BAD_LVALUE, "attempting to generate assignment for non-lvalue", "Attempting to generate assignment for non-lvalue." }, { TCERR_GEN_BAD_ADDR, "attempting to generate address for non-addressable", "Attempting to generate address for non-addressable." }, { TCERR_TOO_MANY_CALL_ARGS, "function call argument list exceeds machine limit (maximum 127)", "This function call has too many arguments; the T3 VM limits function " "and method calls to a maximum of 127 arguments. Check for missing " "parentheses if you didn't intend to pass this many arguments. If the " "function actually does take more than 127 arguments, you will have " "to modify your program to reduce the argument count; you could do " "this by grouping arguments into an object or list value, or by " "breaking the function into several simpler functions." }, { TCERR_TOO_MANY_CTOR_ARGS, "'new' argument list exceeds machine limit (maximum 126)", "This 'new' operator has too many arguments; the T3 VM limits " "constructor calls to a maximum of 126 arguments. Check for missing " "parentheses if you didn't intend to pass this many arguments. If the " "constructor actually does take more than 126 arguments, you will have " "to modify your program to reduce the argument count; you could do " "this by grouping arguments into an object or list value." }, { TCERR_GEN_CODE_DELEGATED, "attempting to generate 'delegated' node directly", "Attempting to generate 'delegated' node directly." }, { TCERR_CODE_POOL_OVER_32K, "code block size exceeds 32k - program will not run on 16-bit machines", "This code block (a function or method body) exceeds 32k in length " "(as stored in the compiled program file). This program will not work " "on 16-bit computers. If you want the program to be able to run on " "16-bit machines, break up this function or method into multiple " "pieces." }, { TCERR_GEN_BAD_CASE, "case/default without switch or non-constant case expression", "case/default without switch or non-constant case expression" }, { TCERR_CATCH_FINALLY_GEN_CODE, "catch/finally gen_code called directly", "catch/finally gen_code called directly" }, { TCERR_INVAL_PROP_CODE_GEN, "invalid property value type for code generation", "invalid property value type for code generation" }, { TCERR_RESERVED_EXPORT, "external name \"%~.*s\" illegal in 'export' - reserved for compiler use", "The external name \"%~.*s\" illegally appears in an 'export' statement. " "The compiler automatically provides an export for this symbol, " "so the program cannot explicitly export this name itself." }, { TCERR_EXPR_TOO_COMPLEX, "expression too complex", "The expression is too complex. If possible, simplify the " "expression by breaking it up into sub-expressions, storing " "intermediate values in local variables." }, { TCERR_CONSTRUCT_NOT_DEFINED, "property \"construct\" is not defined", "No property named \"construct\" is defined. Creating an object " "with operator 'new' invokes this property if defined." }, { TCERR_CONSTRUCT_NOT_PROP, "\"construct\" is not a property", "The symbol \"construct\" does not refer to a property. Creating " "an object with operator 'new' invokes this property if defined." }, { TCERR_FINALIZE_NOT_DEFINED, "property \"finalize\" is not defined", "No property named \"finalize\" is defined. This property is " "invoked when an object is about to be deleted during garbage " "collection." }, { TCERR_FINALIZE_NOT_PROP, "\"finalize\" is not a property", "The symbol \"finalize\" does not refer to a property. This property " "is invoked when an object is about to be deleted during garbage " "collection." }, { TCERR_BAD_JS_TPL, "invalid js template", "Invalid js template." }, { TCERR_JS_EXPR_EXPAN_OVF, "js template expansion overflow", "Overflow expanding js template." } }; /* english message count */ size_t tc_message_count_english = sizeof(tc_messages_english)/sizeof(tc_messages_english[0]); /* * the actual message array - we'll initialize this to the english list * that's linked in, but if we find an external message file, we'll use * the external file instead */ const err_msg_t *tc_messages = tc_messages_english; /* message count - initialize to the english message array count */ size_t tc_message_count = sizeof(tc_messages_english)/sizeof(tc_messages_english[0]); frobtads-1.2.3/tads3/vmisaac.h0000644000175000001440000000311411722727216015324 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmisaac.h - TADS 3 VM ISAAC random number generator implementation Function Notes Modified 04/11/10 MJRoberts - Creation */ #ifndef VMISAAC_H #define VMISAAC_H #include "t3std.h" /* data block size */ #define ISAAC_RANDSIZL (8) #define ISAAC_RANDSIZ (1<cnt-- == 0 ? \ (isaac_gen_group(r), (r)->cnt=ISAAC_RANDSIZ-1, (r)->rsl[(r)->cnt]) : \ (r)->rsl[(r)->cnt]) /* * Initialize. If flag is true, then use the contents of ctx->rsl[] to * initialize ctx->mm[]; otherwise, we'll use a fixed starting * configuration. */ void isaac_init(isaacctx *ctx, int flag); /* * Get/set the internal state. This allows saving and restoring the * internal state of the RNG. 'get' returns the size of the byte buffer * required; call with buf==0 to determine the size needed. * * 'get' and 'set' both expect the caller to know the correct size from a * size-query call to 'get'. The size passed to 'set' must always exactly * match the size returned from 'get', since anything else could corrupt * the internal state. */ size_t isaac_get_state(isaacctx *ctx, char *buf); void isaac_set_state(isaacctx *ctx, const char *buf); /* generate a group of random values */ void isaac_gen_group(isaacctx *ctx); #endif /* VMISAAC_H */ frobtads-1.2.3/tads3/vmimage.cpp0000644000175000001440000027135512145504453015673 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmimage.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimage.cpp - VM image file loader Function Notes Modified 12/12/98 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3std.h" #include "vmtype.h" #include "vminit.h" #include "vmimage.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmfile.h" #include "vmmeta.h" #include "vmbif.h" #include "vmpredef.h" #include "vmrun.h" #include "vmtobj.h" #include "vmhost.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmsave.h" #include "vmrunsym.h" #include "vmlookup.h" #include "vmstack.h" #include "tcprstyp.h" #include "vmhash.h" #include "vmsrcf.h" #include "vmvec.h" #include "charmap.h" /* ------------------------------------------------------------------------ */ /* * Hash table entry for the exported symbols table */ class CVmHashEntryExport: public CVmHashEntryCS { public: CVmHashEntryExport(const char *nm, size_t len, int copy_nm, const vm_val_t *val) : CVmHashEntryCS(nm, len, copy_nm) { /* remember the value */ val_ = *val; } /* the value of this symbol */ vm_val_t val_; }; /* ------------------------------------------------------------------------ */ /* * Implementation of CVmImageLoaderMres interface for regular image file * resource loading */ class CVmImageLoaderMres_std: public CVmImageLoaderMres { public: CVmImageLoaderMres_std(int fileno, CVmHostIfc *hostifc) { fileno_ = fileno; hostifc_ = hostifc; } /* add a resource */ void add_resource(uint32_t seek_ofs, uint32_t siz, const char *res_name, size_t res_name_len) { /* call the host system interface to add the resource */ hostifc_->add_resource(seek_ofs, siz, res_name, res_name_len, fileno_); } /* add a local file resource link */ void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) { /* call the host system inetrface to add it */ hostifc_->add_resource(fname, fnamelen, res_name, res_name_len); } private: /* host interface object */ CVmHostIfc *hostifc_; /* file number to pass to host interface */ int fileno_; }; /* ------------------------------------------------------------------------ */ /* * Image file loader implementation */ /* * instantiation */ CVmImageLoader::CVmImageLoader(CVmImageFile *fp, const char *fname, long base_ofs) { size_t i; char fname_abs[OSFNMAX], path[OSFNMAX]; /* remember the underlying image file interface */ fp_ = fp; /* remember the image filename and the base seek offset */ fname_ = lib_copy_str(fname); base_seek_ofs_ = base_ofs; /* save the file's fully-qualified, absolute directory path */ os_get_abs_filename(fname_abs, sizeof(fname_abs), fname); os_get_path_name(path, sizeof(path), fname_abs); path_ = lib_copy_str(path); /* we don't know the file version yet */ ver_ = 0; /* allocate the pool tracking objects */ for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i) pools_[i] = new CVmImagePool(); /* no metaclass dependency table loaded yet */ loaded_meta_dep_ = FALSE; /* no function set dependency table loaded yet */ loaded_funcset_dep_ = FALSE; /* no entrypoint loaded yet */ loaded_entrypt_ = FALSE; /* no GSYM block yet */ has_gsym_ = FALSE; /* no runtime symbols loaded yet */ runtime_symtab_ = 0; runtime_macros_ = 0; /* there's no reflection LookupTable yet */ reflection_symtab_ = VM_INVALID_OBJ; reflection_macros_ = VM_INVALID_OBJ; /* create the exported symbol hash table */ exports_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* create the synthesized exports hash table */ synth_exports_ = new CVmHashTable(16, new CVmHashFuncCS(), TRUE); /* no static initializer pages yet */ static_head_ = static_tail_ = 0; /* we don't have a static initializer code offset yet */ static_cs_ofs_ = 0; } /* * destruction */ CVmImageLoader::~CVmImageLoader() { size_t i; /* delete the pool tracking objects */ for (i = 0 ; i < sizeof(pools_)/sizeof(pools_[0]) ; ++i) delete pools_[i]; /* delete the filename string and path */ lib_free_str(fname_); lib_free_str(path_); /* if we have our own runtime symbol/macro tables, delete them */ if (runtime_symtab_ != 0) delete runtime_symtab_; if (runtime_macros_ != 0) delete runtime_macros_; /* delete the exports tables */ delete exports_; delete synth_exports_; /* delete the static initializer pages */ while (static_head_ != 0) { CVmStaticInitPage *nxt; /* remember the next one */ nxt = static_head_->nxt_; /* free the image-allocated data associated with the page */ fp_->free_mem(static_head_->data_); /* delete this one */ delete static_head_; /* move on to the next */ static_head_ = nxt; } } /* ------------------------------------------------------------------------ */ /* * load the image */ void CVmImageLoader::load(VMG0_) { char buf[128]; int done; /* set myself to be the global image loader */ G_image_loader = this; /* * perform additional initialization now that we're about to load * the image file */ vm_init_before_load(vmg_ fname_); /* tell the host application the name of the image file */ G_host_ifc->set_image_name(fname_); /* read and validate the header */ read_image_header(); /* load the data blocks */ for (done = FALSE ; !done ; ) { ulong siz; uint flags; /* read the next data block header */ fp_->copy_data(buf, 10); /* get the size */ siz = t3rp4u(buf + 4); /* get the flags */ flags = osrp2(buf + 8); /* load the block according to its type */ if (block_type_is(buf, "CPPG")) { /* load the constant pool page block */ load_const_pool_page(siz); } else if (block_type_is(buf, "ENTP")) { /* load the entrypoint */ load_entrypt(vmg_ siz); } else if (block_type_is(buf, "OBJS")) { /* load static object block */ load_static_objs(vmg_ siz); } else if (block_type_is(buf, "CPDF")) { /* load the constant pool definition block */ load_const_pool_def(siz); } else if (block_type_is(buf, "MRES")) { /* * load the multimedia resource block (the main image file * is always file number zero) */ CVmImageLoaderMres_std res_ifc(0, G_host_ifc); load_mres(siz, &res_ifc); } else if (block_type_is(buf, "MREL")) { /* load the multimedia resource link block */ CVmImageLoaderMres_std res_ifc(0, G_host_ifc); load_mres_link(siz, &res_ifc); } else if (block_type_is(buf, "MCLD")) { /* load metaclass dependency block */ load_meta_dep(vmg_ siz); } else if (block_type_is(buf, "FNSD")) { /* load function set dependency list block */ load_funcset_dep(vmg_ siz); } else if (block_type_is(buf, "SYMD")) { /* load symbolic names export block */ load_sym_names(vmg_ siz); } else if (block_type_is(buf, "SRCF")) { /* load the source file list */ load_srcfiles(vmg_ siz); } else if (block_type_is(buf, "GSYM")) { /* load the global symbols block */ load_gsym(vmg_ siz); } else if (block_type_is(buf, "MACR")) { /* load the macro symbols block */ load_macros(vmg_ siz); } else if (block_type_is(buf, "MHLS")) { /* load the method header list block */ load_mhls(vmg_ siz); } else if (block_type_is(buf, "SINI")) { /* load the static initializer block */ load_sini(vmg_ siz); } else if (block_type_is(buf, "EOF ")) { /* end of file - we can stop looking now */ done = TRUE; } else { /* * This block type is unknown, so ignore it. If a new block * type is added in the future, an older VM version won't * recognize the new block, but it can still load the image * file simply by omitting any unrecognized new blocks. Since * the image file will not be complete in this case, it may not * work properly. * * To allow for future block types which contain advisory data, * which can safely be ignored by older VM versions, while also * allowing for the possibility of changes that create * incompatibilities, we have a flag in the header that * indicates whether the block is required or not. If this * block is marked as mandatory, throw an error, since we don't * recognize the block. Note that this is a version-related * error (i.e., a VM update should fix it), so set the version * flag in the exception. */ if ((flags & VMIMAGE_DBF_MANDATORY) != 0) err_throw_a(VMERR_UNKNOWN_IMAGE_BLOCK, 1, ERR_TYPE_VERSION_FLAG); /* skip past the block */ fp_->skip_ahead(siz); } } /* the image file is required to contain an entrypoint definition */ if (!loaded_entrypt_) err_throw(VMERR_IMAGE_NO_ENTRYPT); /* the image file is required to contain a metaclass dependency table */ if (!loaded_meta_dep_) err_throw(VMERR_IMAGE_NO_METADEP); /* the image is required to have a function set dependency table */ if (!loaded_funcset_dep_) err_throw(VMERR_IMAGE_NO_FUNCDEP); /* complete the dynamic linking */ do_dynamic_link(vmg0_); /* fix up the global symbol table metaclasses, if applicable */ fix_gsym_meta(vmg0_); /* * create an IntrinsicClass instance for each metaclass that the * image file is using and for which the compiler didn't supply its * own IntrinsicClass object */ G_meta_table->create_intrinsic_class_instances(vmg0_); /* * Cache certain metaclass method table entries that are used by the * VM: Iterator.getNext [Iterator#1], Iterator.isNextAvailable * [Iterator#2]. */ vm_meta_entry_t *mcoll = G_meta_table->get_entry_by_id("iterator"); if (mcoll != 0) { G_iter_get_next = mcoll->xlat_func(1); G_iter_next_avail = mcoll->xlat_func(2); } /* * Attach the code pool and constant pool to their backing stores, * which are the pool objects we loaded from the image file. */ G_code_pool->attach_backing_store(pools_[0]); G_const_pool->attach_backing_store(pools_[1]); /* perform any requested post-load object initializations */ G_obj_table->do_all_post_load_init(vmg0_); /* load external resource files if possible */ if (G_host_ifc->can_add_resfiles()) load_ext_resfiles(vmg0_); /* * perform additional initialization now that we've finished * loading the image file */ vm_init_after_load(vmg0_); /* * Let the UI inspect the built-ins linked by the program. The UI * might use different initial display configurations depending on the * function sets and/or intrinsic classes the program uses. For * example, Workbench on Windows initially hides the HTML TADS window * if the network functions are linked, since these are normally used * to implement a Web UI, which uses a separate display window. */ os_init_ui_after_load(G_bif_table, G_meta_table); /* forget the image loader */ G_image_loader = 0; } /* ------------------------------------------------------------------------ */ /* * Load external resource files associated with the image file */ void CVmImageLoader::load_ext_resfiles(VMG0_) { int i; char suffix_lc[4]; char suffix_uc[4]; /* set up the templates for the resource file suffix */ strcpy(suffix_lc, "3r0"); strcpy(suffix_uc, "3R0"); /* * Search for resource files with the same name as the image file, but * with the extension replaced with .3r0 through .3r9. Try both * lower-case and upper-case names (in that order), in case the file * system is case-sensitive. */ for (i = 0 ; i < 10 ; ++i) { char resfile[OSFNMAX]; /* substitute the current suffix number */ suffix_lc[2] = suffix_uc[2] = i + '0'; /* * if there's an explicit resource path, use it, otherwise use * the same directory that contains the image file */ if (G_host_ifc->get_res_path() != 0) { /* * there's an explicit resource path - build the full path to * the resource file using the resource path and the root name * of the image file */ os_build_full_path(resfile, sizeof(resfile), G_host_ifc->get_res_path(), os_get_root_name(fname_)); } else { /* * there's no resoruce path - use the image file full name, * including any directory path information */ strcpy(resfile, fname_); } /* replace the old image file extension with the resource suffix */ os_remext(resfile); os_addext(resfile, suffix_lc); /* if this file doesn't exist, try the upper-case name */ if (osfacc(resfile)) { /* replace the suffix with the upper-case version */ os_remext(resfile); os_addext(resfile, suffix_uc); } /* check to see if this file exists */ if (!osfacc(resfile)) { int fileno; CVmFile *fp; CVmImageFile *volatile imagefp = 0; CVmImageLoader *volatile loader = 0; /* ask the host system to assign a file number */ fileno = G_host_ifc->add_resfile(resfile); /* create a file object for reading the file */ fp = new CVmFile(); err_try { CVmImageLoaderMres_std res_ifc(fileno, G_host_ifc); /* open the file */ fp->open_read(resfile, OSFTT3IMG); /* set up the loader */ imagefp = new CVmImageFileExt(fp); loader = new CVmImageLoader(imagefp, resfile, 0); /* load the resource-only file */ loader->load_resource_file(&res_ifc); } err_finally { /* delete the objects we created */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; delete fp; } err_end; } } } /* * Load a resource file from the current seek location in the given file * handle */ void CVmImageLoader::load_resources_from_fp(osfildef *fp, const char *fname, CVmHostIfc *hostifc) { int fileno; /* ask the host system to assign a number to the file */ fileno = hostifc->add_resfile(fname); /* set up a resource interface with the file number */ CVmImageLoaderMres_std res_ifc(fileno, hostifc); err_try { /* load the resources */ load_resources_from_fp(fp, fname, &res_ifc); } err_catch_disc { /* ignore the error */ } err_end; } /* * Load a resource file from the current seek location in the given file * handle */ void CVmImageLoader::load_resources_from_fp(osfildef *fp, const char *fname, CVmImageLoaderMres *res_ifc) { CVmFile *file; CVmImageFile *volatile imagefp = 0; CVmImageLoader *volatile loader = 0; /* create a file object for reading the file */ file = new CVmFile(); /* set up the file with our file handler */ file->set_file(fp, 0); err_try { /* set up the loader */ imagefp = new CVmImageFileExt(file); loader = new CVmImageLoader(imagefp, fname, 0); /* load the resource-only file */ loader->load_resource_file(res_ifc); } err_finally { /* * detach our CVmFile object from the caller's file handle, * since we want to leave the caller's file handle open */ file->detach_file(); /* delete the objects we created */ if (loader != 0) delete loader; if (imagefp != 0) delete imagefp; delete file; } err_end; } /* ------------------------------------------------------------------------ */ /* * Load a resource-only file. 'fileno' is the file number assigned by * the host application (via the add_resfile() interface). */ void CVmImageLoader::load_resource_file(CVmImageLoaderMres *res_ifc) { int done; /* read and validate the image header */ read_image_header(); /* load the blocks */ for (done = FALSE ; !done ; ) { ulong siz; uint flags; char buf[128]; /* read the next data block header */ fp_->copy_data(buf, 10); /* get the size */ siz = t3rp4u(buf + 4); /* get the flags */ flags = osrp2(buf + 8); /* check the block type */ if (block_type_is(buf, "EOF ")) { /* we're done */ done = TRUE; } else if (block_type_is(buf, "MRES")) { /* load the multimedia resource block */ load_mres(siz, res_ifc); } else if (block_type_is(buf, "MREL")) { /* load the multimedia resource link block */ load_mres_link(siz, res_ifc); } else { /* * Unknown block type - ignore it, unless it's mandatory, in * which case this is an error. This is a version-related * error (fixable by updating to the latest version), so set * the version flag in the exception, if we throw one. */ if ((flags & VMIMAGE_DBF_MANDATORY) != 0) err_throw_a(VMERR_UNKNOWN_IMAGE_BLOCK, 1, ERR_TYPE_VERSION_FLAG); /* skip past the block */ fp_->skip_ahead(siz); } } } /* ------------------------------------------------------------------------ */ /* * Read and validate an image file header. */ void CVmImageLoader::read_image_header() { char buf[128]; /* * Read the header. The header consists of the signature string, a * UINT2 with the file format version number, 32 reserved bytes, * then the compilation timestamp (24 bytes). */ fp_->copy_data(buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24); /* verify the signature */ if (memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0) err_throw(VMERR_NOT_AN_IMAGE_FILE); /* get the version number */ ver_ = osrp2(buf + sizeof(VMIMAGE_SIG)-1); /* store the timestamp */ memcpy(timestamp_, buf + sizeof(VMIMAGE_SIG)-1 + 2 + 32, 24); /* * Check the version to ensure that it's within the range that this * loader implementation supports. If not, throw an error, and mark is * as a version-related error. */ if (ver_ > 1) err_throw_a(VMERR_IMAGE_INCOMPAT_VSN, 1, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Execute the image */ void CVmImageLoader::run(VMG_ const char *const *argv, int argc, CVmRuntimeSymbols *global_symtab, CVmRuntimeSymbols *macro_symtab, const char *saved_state) { CVmRuntimeSymbols *orig_runtime_symtab, *orig_runtime_macros; pool_ofs_t entry_code_ofs; int entry_code_argc; CCharmapToUni *argmap = 0; char *argstr = 0; /* make sure we found a code pool definition */ if (pools_[0]->vmpbs_get_page_count() < 1) err_throw(VMERR_IMAGE_NO_CODE); /* * Find the entrypoint. If we have a saved state, call the function * given by the exported symbol "mainRestore". Otherwise, call the * exported main entrypoint function. */ if (saved_state != 0) { /* * We're restoring a saved state file immediately on startup - call * the exported "mainRestore" function. If there is no such * export, we can't run the program with an initial saved state. */ if (G_predef->main_restore_func == 0) err_throw(VMERR_NO_MAINRESTORE); /* use the mainRestore export */ entry_code_ofs = G_predef->main_restore_func; } else { /* ordinary startup - use the exported primary entrypoint */ entry_code_ofs = entrypt_; } /* we have no arguments to the entrypoint function yet */ entry_code_argc = 0; /* set myself as the global image loader */ G_image_loader = this; /* remember the original runtime symbol table */ orig_runtime_symtab = runtime_symtab_; orig_runtime_macros = runtime_macros_; /* we haven't set the file path */ int file_path_set = FALSE; /* catch any errors so we restore globals on the way out */ err_try { int i; /* if there's no file path, use the image file folder by default */ if (G_file_path == 0) { G_file_path = lib_copy_str(get_path()); file_path_set = TRUE; } /* * if the caller gave us a runtime symbol table, use it over any * we have already stored */ if (global_symtab != 0) runtime_symtab_ = global_symtab; if (macro_symtab != 0) runtime_macros_ = macro_symtab; /* create a LookupTable for the reflection symbols, if any */ create_global_symtab_lookup_table(vmg0_); /* * if we successfully created a reflection symbol table, push it * onto the stack - this will ensure that the object won't be * discarded as long as the program is running */ if (reflection_symtab_ != VM_INVALID_OBJ) { /* set up an object value for the table and push it */ G_stk->push()->set_obj(reflection_symtab_); } /* create and stack the reflection LookupTable for macros */ create_macro_symtab_lookup_table(vmg0_); if (reflection_macros_ != VM_INVALID_OBJ) G_stk->push()->set_obj(reflection_macros_); /* * run static initializers (do this after creating the symbol * table, in case any of the initializers want to access the * symbol table) */ run_static_init(vmg0_); /* if there's a saved state file to restore, push it */ if (saved_state != 0) { /* create a string object for the filename, and push it */ G_stk->push()->set_obj( CVmObjString::create(vmg_ FALSE, saved_state, strlen(saved_state))); /* count the extra startup argument */ ++entry_code_argc; } /* get a character mapper for the command-line arguments */ if (argc > 0) { /* * If we have a net configuration, assume that the arguments * are in UTF-8 format, since they're coming from a web server. * Otherwise, ask the OS which character set to use for * command-line parameters. */ char argcs[40]; if (G_net_config != 0) strcpy(argcs, "utf8"); else os_get_charmap(argcs, OS_CHARMAP_CMDLINE); /* load the character set */ argmap = CCharmapToUni::load( G_host_ifc->get_sys_res_loader(), argcs); } /* * push a string object for each argument - push them onto the * stack to ensure that they're referenced in case garbage * collection occurs while we're working */ for (i = 0 ; i < argc ; ++i) { /* if we have a character mapper, map the argument string */ size_t slen; if (argmap != 0) { /* map the argument to UTF8 */ slen = argmap->map_str_alo(&argstr, argv[i]); } else { /* make a copy of the string */ slen = strlen(argv[i]); argstr = (char *)t3malloc(slen + 1); memcpy(argstr, argv[i], slen + 1); /* make sure it's well-formed UTF-8 */ CCharmapToUni::validate(argstr, slen); } /* push the mapped/adjusted string argument */ G_interpreter->push_string(vmg_ argstr, slen); /* done with the mapped/adjusted string */ t3free(argstr); argstr = 0; } /* create a list to hold the strings */ vm_obj_id_t lst_obj = CVmObjList::create(vmg_ FALSE, argc); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* set the list elements to the strings */ for (i = 0 ; i < argc ; ++i) { vm_val_t *strp; /* * Get this item from the stack. Note that the most recently * pushed item is number 0, then number 1, and so on - the * last argument string (at index argc) is thus number 0, and * the first (at index 0) is number argc. */ strp = G_stk->get(argc - i - 1); /* set this list element */ lst->cons_set_element(i, strp); } /* * we don't need the strings themselves any more, as we can reach * them through the list, so drop the strings from the stack */ G_stk->discard(argc); /* push the list - it's the argument to the program entrypoint */ G_stk->push()->set_obj(lst_obj); ++entry_code_argc; /* * Invoke the entrypoint function - it takes two arguments (the * list of argument strings, and the name of the initial saved * state file to restore, if any). */ G_stk->push()->set_propid(VM_INVALID_PROP); G_stk->push()->set_nil(); G_stk->push()->set_nil(); G_stk->push()->set_nil_obj(); G_stk->push()->set_fnptr(entry_code_ofs); vm_rcdesc rc("Main entrypoint"); G_interpreter->do_call( vmg_ 0, (const uchar *)G_code_pool->get_ptr(entry_code_ofs), entry_code_argc, &rc); /* * if we pushed a global symbol table LookupTable object, pop it - * we left it on the stack to protect it from the garbage * collector, since it's always implicitly referenced from the * intrinsic function that retrieves the symbol table object */ if (reflection_macros_ != VM_INVALID_OBJ) G_stk->discard(); if (reflection_symtab_ != VM_INVALID_OBJ) G_stk->discard(); } err_finally { /* forget the image loader */ G_image_loader = 0; /* restore the original runtime symbol tables */ runtime_symtab_ = orig_runtime_symtab; runtime_macros_ = orig_runtime_macros; /* release the command-line character mapper */ if (argmap != 0) argmap->release_ref(); /* release any argument string we were working on */ if (argstr != 0) t3free(argstr); /* * if we set the file path, un-set it, so that callers don't think * they have to do so */ if (file_path_set) { lib_free_str(G_file_path); G_file_path = 0; } } err_end; } /* * Run static initializers */ void CVmImageLoader::run_static_init(VMG0_) { CVmStaticInitPage *pg; /* run through the list of static initializers */ for (pg = static_head_ ; pg != 0 ; pg = pg->nxt_) { size_t i; /* run through the initializers on this page */ for (i = 0 ; i < pg->cnt_ ; ++i) { vm_obj_id_t obj; vm_prop_id_t prop; vm_val_t target; /* get this initializer's object and property ID */ obj = pg->get_obj_id(i); prop = pg->get_prop_id(i); target.set_obj(obj); /* invoke this object.property */ err_try { /* evaluate the property */ G_interpreter->get_prop(vmg_ 0, &target, prop, &target, 0, 0); } err_catch(exc) { /* * If the "error" is a debugger terminate, pass it through * - this simply means that the user is manually ending the * program via the debugger UI. Likewise for restarts or * interrupts. */ if (exc->get_error_code() == VMERR_DBG_HALT || exc->get_error_code() == VMERR_DBG_RESTART || exc->get_error_code() == VMERR_DBG_INTERRUPT) err_rethrow(); /* get the message for the exception that occurred */ char errbuf[512]; CVmRun::get_exc_message(vmg_ exc, errbuf, sizeof(errbuf), FALSE); /* presume we won't find names */ size_t obj_len = 0, prop_len = 0; const char *obj_name = 0, *prop_name = 0; /* find the obj and prop names in the global symbols */ if (runtime_symtab_ != 0) { /* find the object name */ obj_name = runtime_symtab_ ->find_obj_name(vmg_ obj, &obj_len); /* find the property name */ prop_name = runtime_symtab_ ->find_prop_name(vmg_ prop, &prop_len); } /* if we didn't find the names, use placeholders */ if (obj_name == 0) { obj_name = "[object name not available]"; obj_len = strlen(obj_name); } if (prop_name == 0) { prop_name = "[property name not available]"; prop_len = strlen(prop_name); } /* throw a new error for the static initializer failure */ err_throw_a(VMERR_EXC_IN_STATIC_INIT, 3, ERR_TYPE_TEXTCHAR_LEN, obj_name, obj_len, ERR_TYPE_TEXTCHAR_LEN, prop_name, prop_len, ERR_TYPE_CHAR, errbuf); } err_end; } } } /* * Unload the image. This detaches the pools from the backing stores, * which must be done before the loaded copy of the image file can be * deleted. */ void CVmImageLoader::unload(VMG0_) { /* detach the code pool from its backing store */ G_code_pool->detach_backing_store(); /* detach the constant pool from its backing store */ G_const_pool->detach_backing_store(); } /* ------------------------------------------------------------------------ */ /* * Create a LookupTable to hold the symbols in the global symbol table. */ void CVmImageLoader::create_global_symtab_lookup_table(VMG0_) { CVmObjLookupTable *lookup; vm_runtime_sym *sym; /* * if we don't have a runtime symbol table, we can't create the * reflection LookupTable */ if (runtime_symtab_ == 0) return; /* * if we already have a reflection symbol table, there's no need to * create another */ if (reflection_symtab_ != VM_INVALID_OBJ) return; /* create a LookupTable to hold the symbols */ reflection_symtab_ = CVmObjLookupTable:: create(vmg_ FALSE, 256, runtime_symtab_->get_sym_count()); /* get the object, properly cast */ lookup = (CVmObjLookupTable *)vm_objp(vmg_ reflection_symtab_); /* push the lookup table onto the stack for gc protection */ G_stk->push()->set_obj(reflection_symtab_); /* run through the symbols and populate the LookupTable */ for (sym = runtime_symtab_->get_head() ; sym != 0 ; sym = sym->nxt) { vm_val_t str; /* * skip intrinsic class modifier objects (for the same reason we * filter these out of firstObj/nextObj iterations: they're not * meaningful objects as far as the program is concerned, so * there's no reason for the program to see them) */ if (sym->val.typ == VM_OBJ && CVmObjIntClsMod::is_intcls_mod_obj(vmg_ sym->val.val.obj)) { /* it's an intrinsic class modifier - skip it */ continue; } /* create a string object to hold this symbol */ str.set_obj(CVmObjString::create(vmg_ FALSE, sym->sym, sym->len)); /* * add it to the lookup table - the symbol string is the key, and * the symbol's value is the lookup entry value */ lookup->add_entry(vmg_ &str, &sym->val); } /* done working - discard our gc protection */ G_stk->discard(); } /* * Create a LookupTable to hold the symbols in the macro table */ void CVmImageLoader::create_macro_symtab_lookup_table(VMG0_) { CVmObjLookupTable *lookup; vm_runtime_sym *sym; /* * if we don't have a runtime macro table, we can't create the * reflection LookupTable */ if (runtime_macros_ == 0) return; /* if we already created the table, there's no need to create another */ if (reflection_macros_ != VM_INVALID_OBJ) return; /* create a LookupTable to hold the symbols */ reflection_macros_ = CVmObjLookupTable:: create(vmg_ FALSE, 256, runtime_macros_->get_sym_count()); /* get the object, properly cast */ lookup = (CVmObjLookupTable *)vm_objp(vmg_ reflection_macros_); /* push the lookup table onto the stack for gc protection */ G_stk->push()->set_obj(reflection_macros_); /* run through the symbols and populate the LookupTable */ for (sym = runtime_macros_->get_head() ; sym != 0 ; sym = sym->nxt) { vm_val_t str; vm_val_t lst; vm_val_t args; vm_val_t v; int i; /* create a string object to hold this symbol */ str.set_obj(CVmObjString::create(vmg_ FALSE, sym->sym, sym->len)); /* stack it for gc protection */ G_stk->push(&str); /* * Create a list to hold the definition. We represent this as * [expansion_string, [argument_list], flags]. */ lst.set_obj(CVmObjList::create(vmg_ FALSE, 3)); G_stk->push(&lst); CVmObjList *lstp = (CVmObjList *)vm_objp(vmg_ lst.val.obj); lstp->cons_clear(); /* create and store the expansion string */ v.set_obj(CVmObjString::create( vmg_ FALSE, sym->macro_expansion, sym->macro_exp_len)); lstp->cons_set_element(0, &v); /* create the argument list */ args.set_obj(CVmObjList::create(vmg_ FALSE, sym->macro_argc)); CVmObjList *argp = (CVmObjList *)vm_objp(vmg_ args.val.obj); argp->cons_clear(); /* store the argument list in the definition list */ lstp->cons_set_element(1, &args); /* store the arguments */ for (i = 0 ; i < sym->macro_argc ; ++i) { /* create the string */ char *p = sym->macro_args[i]; size_t len = strlen(p); v.set_obj(CVmObjString::create(vmg_ FALSE, p, len)); /* store it in the argument list */ argp->cons_set_element(i, &v); } /* store the flags */ v.set_int(sym->macro_flags); lstp->cons_set_element(2, &v); /* * add the macro to the lookup table - the symbol string is the * key, and the symbol's value is the definition list */ lookup->add_entry(vmg_ &str, &lst); /* done with the symbol and list gc protection */ G_stk->discard(2); } /* done working - discard our gc protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Load an Entrypoint (ENTP) block */ void CVmImageLoader::load_entrypt(VMG_ ulong siz) { char buf[32]; /* if we've already loaded an entrypoint, throw an error */ if (loaded_entrypt_) err_throw(VMERR_IMAGE_ENTRYPT_REDEF); /* read the entrypoint offset */ read_data(buf, 16, &siz); /* set the entrypoint */ entrypt_ = t3rp4u(buf); /* set the method header size in the interpreter */ G_interpreter->set_funchdr_size(osrp2(buf+4)); /* set the exception table entry size global */ G_exc_entry_size = osrp2(buf+6); /* set the debugger source line record size global */ G_line_entry_size = osrp2(buf+8); /* set the debug table header size */ G_dbg_hdr_size = osrp2(buf+10); /* set the debug local symbol header size */ G_dbg_lclsym_hdr_size = osrp2(buf+12); /* set the debug version ID */ G_dbg_fmt_vsn = osrp2(buf+14); /* set the frame size */ G_dbg_frame_size = 4; if (siz >= 2) { read_data(buf, 2, &siz); G_dbg_frame_size = osrp2(buf); } /* note that we've loaded it */ loaded_entrypt_ = TRUE; } /* ------------------------------------------------------------------------ */ /* * Load a Constant Pool Definition (CPDF) data block */ void CVmImageLoader::load_const_pool_def(ulong siz) { char buf[32]; uint pool_id; ulong page_count; ulong page_size; /* read the block data */ read_data(buf, 10, &siz); /* decode the pool identifier, page count, and page size */ pool_id = osrp2(buf); page_count = t3rp4u(buf + 2); page_size = t3rp4u(buf + 6); /* ensure that the pool ID is valid */ if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0])) err_throw(VMERR_IMAGE_BAD_POOL_ID); /* adjust the pool to 0-based range */ --pool_id; /* initialize the pool */ pools_[pool_id]->init(fp_, page_count, page_size); } /* ------------------------------------------------------------------------ */ /* * Load a Constant Pool Page (CPPG) data block */ void CVmImageLoader::load_const_pool_page(ulong siz) { char buf[32]; uint pool_id; ulong idx; uchar xor_mask; /* read the header */ read_data(buf, 7, &siz); /* decode the pool ID and page index */ pool_id = osrp2(buf); idx = t3rp4u(buf + 2); xor_mask = buf[6]; /* ensure that the pool ID is valid */ if (pool_id < 1 || pool_id > sizeof(pools_)/sizeof(pools_[0])) err_throw(VMERR_IMAGE_BAD_POOL_ID); /* adjust the pool to 0-based range */ --pool_id; /* set the page information */ pools_[pool_id]->set_page_info(idx, fp_->get_seek(), siz, xor_mask); /* * Skip past the page data, which follow the header - we don't need * to load the page data right now, but merely note where it is for * later loading. The caller may choose to load page data only as * required, rather than all at once, so we don't want to bring it * into memory right now. */ fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * Load a Static Object (OBJS) data block */ void CVmImageLoader::load_static_objs(VMG_ ulong siz) { char buf[32]; uint obj_count; uint meta_idx; int header_size; int large_objs; uint flags; int trans; /* read the number of objects, the metaclass index, and the flags */ read_data(buf, 6, &siz); /* decode the object count and metaclass index values */ obj_count = osrp2(buf); meta_idx = osrp2(buf + 2); /* decode the flags */ flags = osrp2(buf + 4); large_objs = ((flags & 1) != 0); trans = ((flags & 2) != 0); /* calculate the per-object header size */ header_size = 4 + (large_objs ? 4 : 2); /* read the objects */ for ( ; obj_count != 0 ; --obj_count) { vm_obj_id_t obj_id; size_t obj_size; const char *data_ptr; /* read the object ID and data size */ read_data(buf, header_size, &siz); /* get the object ID */ obj_id = (vm_obj_id_t)t3rp4u(buf); /* get the size */ if (large_objs) { /* * Make sure the object size doesn't overflow the local * hardware limits. (This test is only necessary if such an * overflow is possible given the datatypes. On most modern * architectures, it simply isn't possible because OSMALMAX is * at least as large as any 32-bit value we could read here. * Note that the value we read here is explicitly a 32-bit * value, so the biggest it could possibly be is 0xFFFFFFFF.) */ #if 0xFFFFFFFFU > OSMALMAX if (t3rp4u(buf + 4) > (unsigned long)OSMALMAX) err_throw(VMERR_OBJ_SIZE_OVERFLOW); #endif /* read the object size */ obj_size = (size_t)t3rp4u(buf + 4); } else { /* read the 16-bit object size */ obj_size = osrp2(buf + 4); } /* load the data, allocating space for it */ data_ptr = alloc_and_read(obj_size, &siz); /* create a new object of the required metaclass */ G_meta_table->create_from_image(vmg_ meta_idx, obj_id, data_ptr, obj_size); /* mark the object as transient, if appropriate */ if (trans) G_obj_table->set_obj_transient(obj_id); } } /* ------------------------------------------------------------------------ */ /* * Load a multi-media resource (MRES) block */ void CVmImageLoader::load_mres(ulong siz, CVmImageLoaderMres *res_ifc) { char buf[16]; uint entry_cnt; long base_ofs; uint i; /* * Note the current physical seek position - each entry's seek position * is stored in the table of contents as an offset from this location. * * Compute the physical base offset: this is the logical base offset in * our image stream, plus the offset of the image stream within the * image file. We need the physical base offset because the underlying * interpreter's resource loader works with the physical file, not the * logical image stream. */ base_ofs = fp_->get_seek() + base_seek_ofs_; /* read the entry count and size of the table of contents */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { uint32_t entry_ofs; uint32_t entry_size; uint entry_name_len; char name_buf[256]; char *p; size_t rem; /* read the fixed part of the table-of-contents entry */ read_data(buf, 9, &siz); entry_ofs = t3rp4u(buf); entry_size = t3rp4u(buf + 4); entry_name_len = (uchar)*(buf + 8); /* read the name */ read_data(name_buf, entry_name_len, &siz); /* null-terminate the name */ name_buf[entry_name_len] = '\0'; /* XOR the bytes of the name with 0xFF */ for (p = name_buf, rem = entry_name_len ; rem != 0 ; ++p, --rem) *p ^= 0xFF; /* add the resource to the resource interface */ res_ifc->add_resource(base_ofs + entry_ofs, entry_size, name_buf, entry_name_len); } /* * skip the data portion of the block, since we now have a map of * the data and can load individual resources on demand */ if (siz != 0) fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * Load a multi-media resource link (MREL) block */ void CVmImageLoader::load_mres_link(ulong siz, CVmImageLoaderMres *res_ifc) { char buf[16]; uint entry_cnt; uint i; /* read the entry count and size of the table of contents */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { char buf[1]; uint resname_len, fname_len; char resname_buf[256], fname_buf[256]; /* read the resource name */ read_data(buf, 1, &siz); resname_len = (uchar)buf[0]; read_data(resname_buf, resname_len, &siz); resname_buf[resname_len] = '\0'; /* read the local filename this resource is linked to */ read_data(buf, 1, &siz); fname_len = (uchar)buf[0]; read_data(fname_buf, fname_len, &siz); fname_buf[fname_len] = '\0'; /* add the resource to the resource interface */ res_ifc->add_resource(fname_buf, fname_len, resname_buf, resname_len); } /* * skip the data portion of the block, since we now have a map of * the data and can load individual resources on demand */ if (siz != 0) fp_->skip_ahead(siz); } /* ------------------------------------------------------------------------ */ /* * load a Metaclass Dependency block */ void CVmImageLoader::load_meta_dep(VMG_ ulong siz) { char buf[256 + 10]; uint entry_cnt; uint i; /* it's an error if we've already seen a dependency block */ if (loaded_meta_dep_) err_throw(VMERR_IMAGE_METADEP_REDEF); /* note that we've loaded a dependency block */ loaded_meta_dep_ = TRUE; /* * reset the global metaclass table, in case there was anything * defined by a previously-loaded image - since a metaclass's index * in the table is implicit in the load order, we need to make sure * we're starting with a completely clean configuration */ G_meta_table->clear(); /* read the number of entries in the table */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { ushort j; uint len; uint prop_cnt; uint prop_len; vm_meta_entry_t *entry; char *p; ulong done_size; long prop_start_ofs; ulong prop_start_siz; vm_prop_id_t max_prop; vm_prop_id_t min_prop; /* * read the record size, and calculate the value of 'siz' that * we expect when we've parsed this entire record - it's the * current size minus the size of the record (note that we have * to add back in the two bytes of the size record itself, since * 'siz' will have been moved past the size record once we've * read it) */ read_data(buf, 2, &siz); done_size = (siz + 2) - osrp2(buf); /* read the length of the entry */ read_data(buf, 1, &siz); len = (uchar)buf[0]; /* read the name and null-terminate it */ read_data(buf, len, &siz); buf[len] = '\0'; p = buf + len + 1; /* read the property table information */ read_data(p, 4, &siz); prop_cnt = osrp2(p); prop_len = osrp2(p + 2); /* * set min and max to 'invalid' to start with, in case there are * no properties at all in the table */ min_prop = max_prop = VM_INVALID_PROP; /* * Read the properties - pass one: find the minimum and maximum * property ID in the table. Before we begin, remember where * the properties start in the file, so we can go back and read * the table again on the second pass. */ prop_start_ofs = fp_->get_seek(); prop_start_siz = siz; for (j = 0 ; j < prop_cnt ; ++j) { vm_prop_id_t cur; /* read the next entry */ read_data(p, 2, &siz); /* skip any additional data in the record */ if (prop_len > 2) skip_data(prop_len - 2, &siz); /* get the property */ cur = (vm_prop_id_t)osrp2(p); /* * if this is the first property, it's the max and min so * far; otherwise, remember it if it's the highest or lowest * so far */ if (j == 0) { /* this is the first one, so note it unconditionally */ min_prop = max_prop = cur; } else { /* if it's below the current minimum, it's the new minimum */ if (cur < min_prop) min_prop = cur; /* if it's above the current maximum, it's the new maximum */ if (cur > max_prop) max_prop = cur; } } /* * Now that we know the minimum and maximum properties in the * table, we can create the dependency record. */ G_meta_table->add_entry(buf, prop_cnt, min_prop, max_prop); /* get a pointer to my new entry */ entry = G_meta_table->get_entry(i); /* * Pass two: read the actual properties into the translation * table. Seek back to the start of the properties in the file, * then read the ID's. * * Note that add_prop_xlat() expects a 1-based function table * index. We are in fact reading a function table, so run our * function index counter from 1 to the entry count to yield the * proper 1-based index values. */ fp_->seek(prop_start_ofs); siz = prop_start_siz; for (j = 1 ; j <= prop_cnt ; ++j) { /* read the next entry */ read_data(p, 2, &siz); /* skip any additional data in the record */ if (prop_len > 2) skip_data(prop_len - 2, &siz); /* add this entry */ entry->add_prop_xlat((vm_prop_id_t)osrp2(p), j); } /* skip any remaining data in the record */ if (siz > done_size) skip_data(siz - done_size, &siz); } /* * always add the root object metaclass to the table, whether the * image file defines it or not */ G_meta_table ->add_entry_if_new(CVmObject::metaclass_reg_->get_reg_idx(), 0, VM_INVALID_PROP, VM_INVALID_PROP); } /* ------------------------------------------------------------------------ */ /* * load a Function Set Dependency block */ void CVmImageLoader::load_funcset_dep(VMG_ ulong siz) { char buf[256]; uint entry_cnt; uint i; /* it's an error if we've already seen a dependency block */ if (loaded_funcset_dep_) err_throw(VMERR_IMAGE_FUNCDEP_REDEF); /* note that we've loaded a dependency block */ loaded_funcset_dep_ = TRUE; /* * reset the global function set dependency table, in case there was * anything defined by a previously-loaded image - since a function * set index in the table is implicit in the load order, we need to * make sure we're starting with a completely clean configuration */ G_bif_table->clear(vmg0_); /* read the number of entries in the table */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { uint len; /* read the length of the entry */ read_data(buf, 1, &siz); len = (uchar)buf[0]; /* read the name and null-terminate it */ read_data(buf, len, &siz); buf[len] = '\0'; /* add the dependency */ G_bif_table->add_entry(vmg_ buf); } } /* ------------------------------------------------------------------------ */ /* * load a Symbolic Names export block */ void CVmImageLoader::load_sym_names(VMG_ ulong siz) { char buf[256]; uint entry_cnt; uint i; /* read the number of entries */ read_data(buf, 2, &siz); entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { vm_val_t val; uint namelen; /* read the data holder and name length */ read_data(buf, VMB_DATAHOLDER + 1, &siz); vmb_get_dh(buf, &val); namelen = (uchar)*(buf + VMB_DATAHOLDER); /* read the name */ read_data(buf, namelen, &siz); /* add this as a new entry to our exports table */ exports_->add(new CVmHashEntryExport(buf, namelen, TRUE, &val)); } } /* ------------------------------------------------------------------------ */ /* * Load a Global Symbols (GSYM) block into the runtime symbol table */ void CVmImageLoader::load_runtime_symtab_from_gsym(VMG_ ulong siz) { const size_t TOK_SYM_MAX_LEN = 80; char buf[TOK_SYM_MAX_LEN + 128]; ulong cnt; /* allocate the symbol table if we haven't already done so */ if (runtime_symtab_ == 0) runtime_symtab_ = new CVmRuntimeSymbols(); /* read the symbol count */ read_data(buf, 4, &siz); cnt = t3rp4u(buf); /* read the symbols and populate the symbol table */ for ( ; cnt != 0 ; --cnt) { char *sym_name; size_t sym_len; size_t dat_len; tc_symtype_t sym_type; char *dat; vm_val_t val; /* read the symbol's length, extra data length, and type code */ read_data(buf, 6, &siz); sym_len = osrp2(buf); dat_len = osrp2(buf + 2); sym_type = (tc_symtype_t)osrp2(buf + 4); /* check the lengths to make sure they don't overflow our buffer */ if (sym_len > TOK_SYM_MAX_LEN) { /* * this symbol name is too long - skip the symbol and its * extra data entirely */ skip_data(sym_len + dat_len, &siz); /* go on to the next symbol */ continue; } else if (dat_len + sym_len > sizeof(buf)) { /* * The extra data block is too long for our fixed buffer. * Truncate the extra data so that we don't overflow our * buffer, but proceed anyway with the truncated extra data. * Note that we won't lose any data we care about, since any * extra data beyond our buffer size is additional future * format data that we don't know how to handle anyway. By * design, we're allowed to skip extra data that we don't know * how to read. */ read_data(buf, sizeof(buf), &siz); /* skip the remainder of the extra data */ skip_data(sym_len + dat_len - sizeof(buf), &siz); } else { /* read the symbol's name and its type-specific data */ read_data(buf, sym_len + dat_len, &siz); } /* the symbol name is at the start of the buffer */ sym_name = buf; /* get the pointer to the extra data (it comes after the name) */ dat = buf + sym_len; /* create the new symbol table entry, depending on its type */ switch(sym_type) { case TC_SYM_FUNC: /* set up the function pointer value */ val.set_fnptr(t3rp4u(dat)); break; case TC_SYM_OBJ: /* set up the object value */ val.set_obj(t3rp4u(dat)); break; case TC_SYM_PROP: /* set up the property value */ val.set_propid(osrp2(dat)); /* check the 'dictionary property' flag */ if (dat_len >= 3 && (dat[0] & 0x01) != 0) { // $$$ it's a dictionary property } break; case TC_SYM_ENUM: /* set up the enum value */ val.set_enum(t3rp4u(dat)); /* check the 'enum token' flag */ if (dat_len >= 5 && (dat[0] & 0x01) != 0) { // $$$ it's an 'enum token' } break; case TC_SYM_METACLASS: /* set up the metaclass object value */ val.set_obj(t3rp4u(dat + 2)); break; case TC_SYM_BIF: /* built-in function */ val.set_bifptr(osrp2(dat + 2), osrp2(dat)); break; default: /* * ignore other types; mark the value as 'empty' so we know we * don't have anything to add to the table */ val.set_empty(); break; } /* if we found a valid type, add it to the table */ if (val.typ != VM_EMPTY) runtime_symtab_->add_sym(sym_name, sym_len, &val); } } /* ------------------------------------------------------------------------ */ /* * Load a Macro Definitions (MACR) block into the runtime symbol table */ void CVmImageLoader::load_runtime_symtab_from_macr(VMG_ ulong siz) { const size_t TOK_SYM_MAX_LEN = 80; char buf[10]; char sym[TOK_SYM_MAX_LEN]; ulong cnt; /* allocate the macro table if we haven't already done so */ if (runtime_macros_ == 0) runtime_macros_ = new CVmRuntimeSymbols(); /* read the symbol count */ read_data(buf, 4, &siz); cnt = t3rp4u(buf); /* read the symbols and populate the symbol table */ for ( ; cnt != 0 ; --cnt) { size_t sym_len; size_t exp_len; int argc; unsigned int flags; int i; /* read the symbol's length */ read_data(buf, 2, &siz); sym_len = osrp2(buf); /* if it fits, read it */ if (sym_len <= TOK_SYM_MAX_LEN) { /* read the name */ read_data(sym, sym_len, &siz); } else { /* it's too long - skip it */ skip_data(sym_len, &siz); } /* read the flags and argument count */ read_data(buf, 4, &siz); flags = osrp2(buf); argc = osrp2(buf + 2); /* remember the current position */ long start_pos = fp_->get_seek(); ulong start_siz = siz; /* count up the argument sizes */ size_t arg_len = 0; for (i = 0 ; i < argc ; ++i) { /* read this argument length */ read_data(buf, 2, &siz); size_t l = osrp2(buf); /* add it to the total, plus a null byte */ arg_len += l + 1; /* skip it in the file */ skip_data(l, &siz); } /* read the expansion size */ read_data(buf, 4, &siz); exp_len = osrp4(buf); /* allocate the symbol entry */ vm_runtime_sym *entry = runtime_macros_->add_macro( sym, sym_len, exp_len, flags, argc, arg_len); /* seek back to the start of the argument list */ fp_->seek(start_pos); siz = start_siz; /* read the arguments */ for (i = 0 ; i < argc ; ++i) { /* read the argument length */ read_data(buf, 2, &siz); size_t l = osrp2(buf); /* read this argument, and null-terminate it */ read_data(entry->macro_args[i], l, &siz); entry->macro_args[i][l] = '\0'; /* commit the storage */ entry->commit_macro_arg(i, l + 1); } /* read the expansion */ skip_data(4, &siz); read_data(entry->macro_expansion, exp_len, &siz); } } /* ------------------------------------------------------------------------ */ /* * load a Source File List block */ void CVmImageLoader::load_srcfiles(VMG_ ulong siz) { size_t i; char buf[64]; size_t entry_cnt; size_t line_size; /* * if there's no source file table, we're not in debug mode and hence * have no use for this type of information, so skip the block */ if (G_srcf_table == 0) { /* skip the entire block in the file */ fp_->skip_ahead(siz); /* we're done */ return; } /* clear any existing information in the table */ G_srcf_table->clear(); /* read the number of entries in the table, and the line record size */ read_data(buf, 4, &siz); entry_cnt = osrp2(buf); line_size = osrp2(buf + 2); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { long start_pos; long next_pos; int orig_index; size_t len; CVmSrcfEntry *entry; ulong line_cnt; ulong skip_amt; /* note the starting file location of this record */ start_pos = fp_->get_seek(); /* * read the size of this file record, the original index, and the * length of the filename */ read_data(buf, 8, &siz); orig_index = osrp2(buf + 4); len = osrp2(buf + 6); /* * calculate the file location of the start of the next block by * adding the starting position and size of this block */ next_pos = start_pos + t3rp4u(buf); /* allocate the entry */ entry = G_srcf_table->add_entry(orig_index, len); /* read the data into this entry's buffer */ read_data(entry->get_name_buf(), len, &siz); /* null-terminate the buffer */ entry->get_name_buf()[len] = '\0'; /* read the number of line records */ read_data(buf, 4, &siz); line_cnt = t3rp4u(buf); /* * if the line records are too big for our buffer, or smaller than * our required minimum size, don't bother reading them - we'll * just skip them entirely */ if (line_size <= sizeof(buf) && line_size >= 8) { /* tell the source file table entry how many lines we have */ entry->alloc_line_records(line_cnt); /* read the line records */ for ( ; line_cnt != 0 ; --line_cnt) { ulong linenum; ulong code_addr; /* read the record */ read_data(buf, line_size, &siz); /* get the source line number and byte code offset */ linenum = t3rp4u(buf); code_addr = t3rp4u(buf + 4); /* add the line to the source file table entry */ entry->add_line_record(linenum, code_addr); } } /* skip ahead to the start of the next record */ skip_amt = next_pos - fp_->get_seek(); if (skip_amt != 0) skip_data(skip_amt, &siz); } } /* ------------------------------------------------------------------------ */ /* * Perform dynamic linking after loading. */ void CVmImageLoader::do_dynamic_link(VMG0_) { struct sym_assoc_t { /* the symbol name */ const char *sym; /* datatype */ vm_datatype_t typ; /* address of predef table entry for this value */ void *addr; /* * flag: this is a synthetic import, which means that we don't * look for it in the image file, but only in the restored state */ int is_synthetic; }; sym_assoc_t imports[] = { /* build the table by including the import list */ #define VM_IMPORT_OBJ(sym, mem) { sym, VM_OBJ, &G_predef->mem, FALSE }, #define VM_NOIMPORT_OBJ(sym, mem) { sym, VM_OBJ, &G_predef->mem, TRUE }, #define VM_IMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, FALSE }, #define VM_NOIMPORT_PROP(sym, mem) { sym, VM_PROP, &G_predef->mem, TRUE }, #define VM_IMPORT_FUNC(sym, mem) { sym, VM_FUNCPTR, &G_predef->mem, FALSE }, #include "vmimport.h" /* end the table */ { 0, VM_NIL, 0, FALSE } }; const sym_assoc_t *ip; /* reset all of the predefs to undefined */ G_predef->reset(); /* * Run through our list of symbols to import from the load file, and * load each one. */ for (ip = imports ; ip->sym != 0 ; ++ip) { CVmHashEntryExport *entry; /* presume we won't find it */ entry = 0; /* * if it's not synthetic, look up this symbol in the image file's * export table (don't look up synthetic symbols, because we * always want to create these ourselves, not load them from an * image file) */ if (!ip->is_synthetic) { /* it's a regular import - look for it in the image exports */ entry = (CVmHashEntryExport *)exports_ ->find(ip->sym, strlen(ip->sym)); } /* * if we didn't find the entry in the image file's exports, look * in the synthesized exports, in case we're restoring state from * a previous session */ if (entry == 0) entry = (CVmHashEntryExport *)synth_exports_ ->find(ip->sym, strlen(ip->sym)); /* if the symbol isn't exported, skip it and proceed to the next */ if (entry == 0) continue; /* * make sure the type of the symbol exported by the image file * matches the type of value we want to import for the symbol */ if (ip->typ != entry->val_.typ) { /* the exported symbol has the wrong type - throw an error */ err_throw_a(VMERR_INVAL_EXPORT_TYPE, 1, ERR_TYPE_CHAR, ip->sym); } /* set the value according to its type */ switch(ip->typ) { case VM_OBJ: /* store the object value */ *(vm_obj_id_t *)ip->addr = entry->val_.val.obj; break; case VM_PROP: *(vm_prop_id_t *)ip->addr = entry->val_.val.prop; break; case VM_FUNCPTR: *(pool_ofs_t *)ip->addr = entry->val_.val.ofs; break; default: /* we don't import any other types */ break; } } /* * If we don't already have one, create a vector to keep track of the * last property ID allocated. We store this in a vector object so * that the property allocation mechanism easily integrates with the * undo and save/restore mechanisms. */ if (G_predef->last_prop_obj == VM_INVALID_OBJ) { /* create the object */ G_predef->last_prop_obj = CVmObjVector::create(vmg_ FALSE, 1); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_LASTPROPOBJ, G_predef->last_prop_obj); /* * set up the object with the current last property, as indicated * in the image file */ set_last_prop(vmg_ G_predef->last_prop); } /* * if the image didn't define an exceptionMessage property, allocate a * new property ID for it */ if (G_predef->rterrmsg_prop == VM_INVALID_PROP) { /* allocate the property ID */ G_predef->rterrmsg_prop = alloc_new_prop(vmg0_); /* add it to the synthesized export list */ add_synth_export_prop(VM_IMPORT_NAME_RTERRMSG, G_predef->rterrmsg_prop); } /* * Create the constant string and list placeholder objects - these are * never imported from the image file, but must be dynamically created * after loading is complete. * * Create these objects without values, since the values are * irrelevant - they're needed only for their type information. */ if (G_predef->const_str_obj == VM_INVALID_OBJ) { /* allocate it */ G_predef->const_str_obj = CVmObjString::create(vmg_ FALSE, 0); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_CONSTSTR, G_predef->const_str_obj); } if (G_predef->const_lst_obj == VM_INVALID_OBJ) { /* allocate it */ G_predef->const_lst_obj = CVmObjList::create(vmg_ FALSE, (size_t)0); /* add it to the synthesized export list */ add_synth_export_obj(VM_IMPORT_NAME_CONSTLST, G_predef->const_lst_obj); } } /* ------------------------------------------------------------------------ */ /* * Load a static initializer list block */ void CVmImageLoader::load_sini(VMG_ ulong siz) { char hdr[12]; ulong siz_rem; ulong hdr_siz; ulong init_cnt; ulong init_rem; /* * read the header size, static code starting offset, and * initializer count */ fp_->copy_data(hdr, 12); hdr_siz = t3rp4u(hdr); static_cs_ofs_ = t3rp4u(hdr + 4); init_cnt = t3rp4u(hdr + 8); /* skip any extra header information */ if (hdr_siz > 12) fp_->skip_ahead(hdr_siz - 12); /* account for having read the header in our size remaining */ siz_rem = siz - hdr_siz; /* read in chunks of VM_STATIC_INIT_PAGE_MAX initializers */ for (init_rem = init_cnt ; init_rem != 0 ; ) { size_t cur; CVmStaticInitPage *pg; /* read the page max, or the actual number remaining if fewer */ cur = VM_STATIC_INIT_PAGE_MAX; if (init_rem < cur) cur = (size_t)init_rem; /* allocate the next page */ pg = new CVmStaticInitPage(cur); /* link in the page */ if (static_tail_ != 0) static_tail_->nxt_ = pg; else static_head_ = pg; static_tail_ = pg; /* read the page */ pg->data_ = fp_->alloc_and_read(cur * 6, 0, siz_rem); /* adjust our counters for the chunk */ init_rem -= cur; siz_rem -= cur * 6; } /* skip any data we don't use in this version */ if (siz_rem != 0) fp_->skip_ahead(siz_rem); } /* ------------------------------------------------------------------------ */ /* * Add a synthesized object export */ void CVmImageLoader::add_synth_export_obj(const char *nm, vm_obj_id_t obj) { vm_val_t val; /* add the new entry with an object value */ val.set_obj(obj); synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val)); } /* * Add a synthesized property export */ void CVmImageLoader::add_synth_export_prop(const char *nm, vm_prop_id_t prop) { vm_val_t val; /* add the new entry with a property value */ val.set_propid(prop); synth_exports_->add(new CVmHashEntryExport(nm, strlen(nm), TRUE, &val)); } /* * Discard all synthesized exports. When we're resetting to the image * file state, or when we're about to restore a saved state, we must * discard the synthesized exports from the prior load so that we start * from the base image file state again. */ void CVmImageLoader::discard_synth_exports() { /* delete all of the entries in the table */ synth_exports_->delete_all_entries(); } /* ------------------------------------------------------------------------ */ /* * Callback context for enumerating the synthesized export symbols for * saving the list to a saved state file */ struct synth_save_cb_ctx { /* the file to which we're writing */ CVmFile *fp; /* number of entries */ long cnt; }; /* * Save the synthesized exports to a saved state file */ void CVmImageLoader::save_synth_exports(VMG_ CVmFile *fp) { synth_save_cb_ctx ctx; long pos; long end_pos; /* set up our context */ ctx.fp = fp; ctx.cnt = 0; /* * write a placeholder count of zero, but remember where it is so we * can come back and fix it up later */ pos = fp->get_pos(); fp->write_uint4(0); /* enumerate all of the entries through our save callback */ synth_exports_->enum_entries(&save_synth_export_cb, &ctx); /* go back and fix up the count, then seek back to the end */ end_pos = fp->get_pos(); fp->set_pos(pos); fp->write_uint4(ctx.cnt); fp->set_pos(end_pos); } /* * Synthesized export enumeration callback - save to file */ void CVmImageLoader::save_synth_export_cb(void *ctx0, CVmHashEntry *entry0) { synth_save_cb_ctx *ctx = (synth_save_cb_ctx *)ctx0; CVmHashEntryExport *entry = (CVmHashEntryExport *)entry0; char buf[VMB_DATAHOLDER]; /* write this entry's length and name to the file */ ctx->fp->write_uint2(entry->getlen()); ctx->fp->write_bytes(entry->getstr(), entry->getlen()); /* write the value as a dataholder */ vmb_put_dh(buf, &entry->val_); ctx->fp->write_bytes(buf, VMB_DATAHOLDER); /* count the entry */ ++(ctx->cnt); } /* * Restore the synthesized exports from a saved state file */ int CVmImageLoader::restore_synth_exports(VMG_ CVmFile *fp, CVmObjFixup *fixups) { long cnt; /* * discard the old synthesized exports - we want to entirely replace * these with what we're about to load from the file */ G_image_loader->discard_synth_exports(); /* read the number of synthesized exports */ cnt = fp->read_uint4(); /* read the exports */ for ( ; cnt != 0 ; --cnt) { size_t len; char buf[256]; char dh[VMB_DATAHOLDER]; vm_val_t val; /* read the length of the name and the name */ len = fp->read_uint2(); fp->read_bytes(buf, len > sizeof(buf) ? sizeof(buf) : len); /* skip any part of the name that didn't fit in the buffer */ if (len > sizeof(buf)) { /* skip the extra bytes */ fp->set_pos(fp->get_pos() + (len - sizeof(buf))); /* limit the length we use to the buffer size */ len = sizeof(buf); } /* read the value */ fp->read_bytes(dh, VMB_DATAHOLDER); /* if the value is an object reference, fix it up */ fixups->fix_dh(vmg_ dh); /* get the value */ vmb_get_dh(dh, &val); /* add the export */ synth_exports_->add(new CVmHashEntryExport(buf, len, TRUE, &val)); } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Allocate a new property ID. */ vm_prop_id_t CVmImageLoader::alloc_new_prop(VMG0_) { CVmObjVector *vec; vm_val_t idx_val; vm_val_t val; vm_val_t new_cont; /* get the LastPropObj vector */ vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj); /* * get the element at index 1 - our last property value is always at * the first element */ idx_val.set_int(1); /* get the value from the vector */ vec->index_val(vmg_ &val, G_predef->last_prop_obj, &idx_val); /* if we've allocated every available property ID, throw an error */ if (val.val.prop == 65535) err_throw(VMERR_OUT_OF_PROPIDS); /* allocate the new property ID by incrementing the last used ID */ ++val.val.prop; /* * update the vector with the new value (this new property ID is now * the last property ID in use) */ vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj, &idx_val, &val); /* return the new last ID */ return val.val.prop; } /* * Get the last property ID. This returns the last valid property ID * currently in use in the program. */ vm_prop_id_t CVmImageLoader::get_last_prop(VMG0_) { /* get LastPropObj[1] */ vm_val_t vec, ele; vec.set_obj(G_predef->last_prop_obj); vec.ll_index(vmg_ &ele, 1); /* return the value */ return ele.val.prop; } /* * Set the last property ID. This updates the LastPropObj synthetic * export object, so that the last property ID is properly integrated with * the undo and save/restore mechanisms. */ void CVmImageLoader::set_last_prop(VMG_ vm_prop_id_t last_prop) { /* get the LastPropObj vector */ CVmObjVector *vec = (CVmObjVector *)vm_objp(vmg_ G_predef->last_prop_obj); /* * set up the values - the index is always 1, since the vector * contains only one element; and the value contains the new last * property ID */ vm_val_t idx_val, new_val; idx_val.set_int(1); new_val.set_propid(G_predef->last_prop); /* update the vector */ vm_val_t new_cont; vec->set_index_val(vmg_ &new_cont, G_predef->last_prop_obj, &idx_val, &new_val); } /* ------------------------------------------------------------------------ */ /* * Copy data from the file into a buffer, decrementing a size counter. * We'll throw a BLOCK_TOO_SMALL error if the read length exceeds the * remaining size. */ void CVmImageLoader::read_data(char *buf, size_t read_len, ulong *remaining_size) { /* ensure we have enough data left in our block */ if (read_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* decrement the remaining size counter for the data we just read */ *remaining_size -= read_len; /* read the data */ fp_->copy_data(buf, read_len); } /* * skip data */ void CVmImageLoader::skip_data(size_t skip_len, ulong *remaining_size) { /* ensure we have enough data left in our block */ if (skip_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* decrement the remaining size counter for the data we're skipping */ *remaining_size -= skip_len; /* skip ahead in the underlying file */ fp_->skip_ahead(skip_len); } /* * Allocate memory for data and read the data from the file, * decrementing the amount read from a size counter. Throws * BLOCK_TOO_SMALL if the read length exceeds the remaining size. */ const char *CVmImageLoader::alloc_and_read(size_t read_len, ulong *remaining_size) { const char *p; /* ensure we have enough data left in our block */ if (read_len > *remaining_size) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* allocate space, and read the data into the new space */ p = fp_->alloc_and_read(read_len, 0, *remaining_size); /* decrement the remaining size counter for the data we just read */ *remaining_size -= read_len; /* return the pointer to the new space */ return p; } /* ------------------------------------------------------------------------ */ /* * Image Pool tracker implementation */ CVmImagePool::CVmImagePool() { /* we don't have any information about the pool yet */ page_count_ = 0; page_size_ = 0; page_info_ = 0; fp_ = 0; } CVmImagePool::~CVmImagePool() { /* if we allocated a set of page offset arrays, delete them */ if (page_info_ != 0) { size_t i; size_t cnt; /* compute the number of subarrays we have */ cnt = get_subarray_count(); /* delete each subarray */ for (i = 0 ; i < cnt ; ++i) t3free(page_info_[i]); /* delete the master array */ t3free(page_info_); } } /* * initialize */ void CVmImagePool::init(CVmImageFile *fp, ulong page_count, ulong page_size) { size_t cnt; size_t i; /* remember the page count and page size */ page_count_ = page_count; page_size_ = page_size; /* remember the underlying image file */ fp_ = fp; /* if this pool has already been defined, throw an error */ if (page_info_ != 0) err_throw(VMERR_IMAGE_POOL_REDEF); /* * Allocate the main page array. We need one entry for each * subarray, so figure the subarray count and allocate the * appropriate amount of space. */ cnt = get_subarray_count(); page_info_ = (CVmImagePool_pg **)t3malloc(cnt * sizeof(page_info_[0])); /* throw an error if that failed */ if (page_info_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the array */ memset(page_info_, 0, cnt * sizeof(page_info_[0])); /* allocate the subarrays */ for (i = 0 ; i < cnt ; ++i) { /* allocate this page, which contains file offsets */ page_info_[i] = (CVmImagePool_pg *)t3malloc(VMIMAGE_POOL_SUBARRAY_SIZE * sizeof(page_info_[i][0])); /* throw an error if that failed */ if (page_info_[i] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the memory */ memset(page_info_[i], 0, VMIMAGE_POOL_SUBARRAY_SIZE * sizeof(page_info_[i][0])); } } /* * Set a page seek offset */ void CVmImagePool::set_page_info(ulong page_idx, long seek_pos, size_t page_size, uchar xor_mask) { CVmImagePool_pg *info; /* * make sure we've allocated the page array and that this index is * in range -- throw an error if not */ if (page_info_ == 0) err_throw(VMERR_IMAGE_POOL_BEFORE_DEF); if (page_idx >= page_count_) err_throw(VMERR_IMAGE_POOL_BAD_PAGE); /* get the entry for this page */ info = get_page_info(page_idx); /* set the information */ info->seek_pos = seek_pos; info->page_size = page_size; info->xor_mask = xor_mask; } /* * allocate and load a page */ const char *CVmImagePool:: vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t /*page_size*/, size_t load_size) { CVmImagePool_pg *info; /* get the page information */ info = get_page_info_ofs(ofs); /* seek to the correct location in the image file */ seek_page_ofs(ofs); /* ask the underlying file to load the data */ return fp_->alloc_and_read(load_size, info->xor_mask, load_size); } /* * load a page into the given memory block */ void CVmImagePool::vmpbs_load_page(pool_ofs_t ofs, size_t /*page_size*/, size_t load_size, char *mem) { CVmImagePool_pg *info; /* get the page information */ info = get_page_info_ofs(ofs); /* seek to the correct location in the image file */ seek_page_ofs(ofs); /* ask the underlying file to load the data */ fp_->copy_data(mem, load_size); /* apply the XOR mask to the loaded data */ apply_xor_mask(mem, load_size, info->xor_mask); } /* * free a page previously loaded */ void CVmImagePool::vmpbs_free_page(const char *mem, pool_ofs_t /*ofs*/, size_t /*page_size*/) { /* tell the file to free the memory */ fp_->free_mem(mem); } /* * Determine if the backing store pages are writable. Let the * underlying file decide. */ int CVmImagePool::vmpbs_is_writable() { /* * if the underlying file allows writing to its allocated pages, * allow writing */ return fp_->allow_write_to_alloc(); } /* * seek to the image file data for the page at the given pool offset */ void CVmImagePool::seek_page_ofs(pool_ofs_t ofs) { ulong page_idx; CVmImagePool_pg *info; /* * get the page index - pages are all of a fixed length, so we can * find the page index simply by dividing the offset by the page * size */ page_idx = ofs / page_size_; /* make sure the page is valid */ if (page_idx >= page_count_) err_throw(VMERR_LOAD_BAD_PAGE_IDX); /* make sure the page has been defined */ info = get_page_info(page_idx); /* make sure this page's information has been loaded */ if (info->seek_pos == 0) err_throw(VMERR_LOAD_UNDEF_PAGE); /* seek to this block's position in the file */ fp_->seek(info->seek_pos); } /* ------------------------------------------------------------------------ */ /* * Image file interface - external disk file implementation */ /* * Delete the image file loader. This deletes all of the memory * associated with loaded objects, so the image file loader must not be * deleted until all of the loaded root-set objects are deleted. */ CVmImageFileExt::~CVmImageFileExt() { CVmImageFileExt_blk *cur; CVmImageFileExt_blk *nxt; /* run through the list of allocated blocks and delete each one */ for (cur = mem_head_ ; cur != 0 ; cur = nxt) { /* remember the next block, since we're deleting this one */ nxt = cur->nxt_; /* delete this one */ delete cur; } } /* * copy data to the caller's buffer */ void CVmImageFileExt::copy_data(char *buf, size_t len) { /* read directly from the file into the caller's buffer */ fp_->read_bytes(buf, len); } /* * allocate memory for and read data */ const char *CVmImageFileExt::alloc_and_read(size_t len, uchar xor_mask, ulong remaining_in_page) { char *mem; /* allocate memory */ mem = alloc_mem(len, remaining_in_page); if (mem == 0) err_throw(VMERR_OUT_OF_MEMORY); /* read the data */ fp_->read_bytes(mem, len); /* if there's an XOR mask, apply it to each byte we read */ CVmImagePool::apply_xor_mask(mem, len, xor_mask); /* return the memory block */ return mem; } /* * seek */ void CVmImageFileExt::seek(long pos) { /* seek to the given position in the underlying file */ fp_->set_pos(pos); } /* * get current seek position */ long CVmImageFileExt::get_seek() const { /* get the position from the underlying file */ return fp_->get_pos(); } /* * skip bytes */ void CVmImageFileExt::skip_ahead(long len) { /* seek ahead in the underlying file */ fp_->set_pos_from_cur(len); } /* * Allocate data for loading */ char *CVmImageFileExt::alloc_mem(size_t siz, ulong remaining_in_page) { /* * If the requested size is more than an eigth of our block size, * give the new allocation its own block. Our blocks are intended * to reduce overhead for small blocks; large blocks not only won't * create a lot of overhead as individual "malloc" allocations, but * would probably leave our block underutilized by leaving a lot of * it free, defeating the purpose. */ if (siz > VMIMAGE_EXT_BLK_SIZE / 8) { CVmImageFileExt_blk *new_block; /* it's a big request, so give it its own block */ new_block = new CVmImageFileExt_blk(siz); /* * link it at the tail of the list (we don't want to suballocate * anything more out of this, obviously, since we're going to * consume the entire block, but we do want to keep it in our * list so that we can track and eventually delete the memory) */ new_block->nxt_ = 0; new_block->prv_ = mem_tail_; /* set the forward pointer of the previous entry */ if (mem_tail_ != 0) mem_tail_->nxt_ = new_block; else mem_head_ = new_block; /* it's the new tail */ mem_tail_ = new_block; /* "suballocate" out of the new block */ return mem_tail_->suballoc(siz); } else { /* * It's a small request, so suballocate it out of a block. * First, make sure we have space in the current block; if not, * allocate a new block. */ if (mem_head_ == 0 || siz > mem_head_->rem_) { CVmImageFileExt_blk *new_block; size_t new_block_size; /* * There's no space left in the current block - allocate a new * block. Leave space in the new block for further * allocations, so we don't have to allocate lots of little * blocks; however, as an upper bound, allocate only as much * space as remains in the current page being read out of the * image file, so that we don't leave a bunch of unused space * after reading the last objects from the image. */ new_block_size = VMIMAGE_EXT_BLK_SIZE; if (new_block_size > remaining_in_page) new_block_size = (size_t)remaining_in_page; /* allocate the new block */ new_block = new CVmImageFileExt_blk(new_block_size); /* link it in at the head of our list */ new_block->prv_ = 0; new_block->nxt_ = mem_head_; /* set the back pointer of the next entry */ if (mem_head_ != 0) mem_head_->prv_ = new_block; else mem_tail_ = new_block; /* it's the new head */ mem_head_ = new_block; } /* suballocate it */ return mem_head_->suballoc(siz); } } /* ------------------------------------------------------------------------ */ /* * Memory tracker for external file loader */ CVmImageFileExt_blk::CVmImageFileExt_blk(size_t siz) { /* allocate the associated memory block */ block_ptr_ = (char *)t3malloc(siz); /* remember the amount of space remaining */ rem_ = siz; /* the next byte free is the first byte */ free_ptr_ = block_ptr_; /* nothing else is in our list yet */ nxt_ = prv_ = 0; } CVmImageFileExt_blk::~CVmImageFileExt_blk() { /* free our memory block */ t3free(block_ptr_); } char *CVmImageFileExt_blk::suballoc(size_t siz) { char *ret; /* if we don't have enough memory available, fail the request */ if (rem_ < siz) return 0; /* our return value will be the current free pointer */ ret = free_ptr_; /* move the free pointer past the allocated block */ free_ptr_ += siz; /* deduct the amount we just allocated from the space available */ rem_ -= siz; /* return the memory */ return ret; } /* ------------------------------------------------------------------------ */ /* * Image file interface - memory-mapped file implementation */ /* * copy data to the caller's buffer */ void CVmImageFileMem::copy_data(char *buf, size_t len) { /* if we're past the end of the file, throw an error */ if (pos_ + len > len_) err_throw(VMERR_READ_PAST_IMG_END); /* copy data into the caller's buffer */ memcpy(buf, mem_ + pos_, len); /* seek past the data */ pos_ += len; } /* * allocate memory for and read data */ const char *CVmImageFileMem::alloc_and_read(size_t len, uchar xor_mask, ulong /*remaining_in_page*/) { const char *ret; /* if we're past the end of the file, throw an error */ if (pos_ + len > len_) err_throw(VMERR_READ_PAST_IMG_END); /* the data are already in memory - figure out where */ ret = mem_ + pos_; /* seek past the data */ pos_ += len; /* * we can't apply an xor mask to an in-memory page - if the mask is * non-zero, it's an error */ if (xor_mask != 0) err_throw(VMERR_XOR_MASK_BAD_IN_MEM); /* return the pointer */ return ret; } /* ------------------------------------------------------------------------ */ /* * Generic stream implementation for an image file block */ /* * read bytes from the stream */ void CVmImageFileStream::read_bytes(char *buf, size_t len) { /* if we don't have enough data to satisfy the request, it's an error */ if (len > len_) err_throw(VMERR_IMAGE_BLOCK_TOO_SMALL); /* deduct the space we're reading from the remaining data size */ len_ -= len; /* copy the data from the image file to the caller's buffer */ fp_->copy_data(buf, len); } /* * read bytes, allowing short reads without error */ size_t CVmImageFileStream::read_nbytes(char *buf, size_t len) { /* limit the read to the available size */ if (len > len_) len = len_; /* deduct the space we're reading from the remaining data size */ len_ -= len; /* copy the data from the image file to the caller's buffer */ if (len != 0) fp_->copy_data(buf, len); /* return the length actually read */ return len; } /* * write bytes to the stream */ void CVmImageFileStream::write_bytes(const char *, size_t) { /* this is a read-only stream, so writing isn't allowed */ err_throw(VMERR_WRITE_FILE); } frobtads-1.2.3/tads3/vmerr.h0000644000175000001440000004213212006720646015033 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmerr.h,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerr.h - VM exception handling Function Defines error macros for try/throw exception handling. To throw an exception, use err_throw(exception_object), where the exception_object is an object describing the exception. This object must be allocated with 'new'. Control will immediately transfer to the nearest enclosing err_catch() block. Protected code is defined as shown below. The blocks must occur in the order shown, but the err_catch and err_finally blocks are optional (although it would be pointless to have an err_try that omits both). err_try { // protected code } err_catch(exception_variable) // or just err_catch_disc { // Exception handler - this code is executed only if an // exception occurs in the protected block. // // If an exception is thrown here (with no nested exception // handler to catch it), the err_finally block will be executed // and then the exception will be thrown to the enclosing block. } err_finally { // Code that is executed regardless whether exception occurs // or not. If no exception occurs, this code is executed as // soon as the protected block finishes. If an exception // occurs, this code is executed, then the exception is // re-thrown. // // If an exception is thrown here, the finally block will // be aborted and the enclosing error handler will be activated. // Care should be taken to ensure that code within this block // is properly protected against exceptions if necessary. } err_end; err_catch automatically defines the given exception_variable as a local variable of type (CVmException *) in the scope of the err_catch block. The referenced CVmException object is valid *only* within the err_catch block; after the err_catch block exits, the CVmException object will no longer be present. If you don't need to access the CVmException information, use err_catch_disc instead of err_catch - this has the same effect as err_catch, but does not declare a local variable to point to the exception object. To re-throw the exception being handled from an err_catch() block, use err_rethrow(). IMPORTANT: control must NOT be transferred out of an err_try, err_catch, or err_finally block via break, goto, or return. Actual execution of the err_end is required in order to properly unwind the error stack. The easiest way to leave one of these blocks prematurely, if necessary, is with a goto: err_try { // some code if (done_with_this_block) goto done; // more code done: ; } err_catch_disc { // etc... } err_end; Notes Modified 10/20/98 MJRoberts - Creation */ #ifndef VMERR_H #define VMERR_H #include #include #include "t3std.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * Error Message Definition structure */ struct err_msg_t { /* message number */ int msgnum; /* concise message text */ const char *short_msgtxt; /* verbose message text */ const char *long_msgtxt; #ifdef VMERR_BOOK_MSG const char *book_msgtxt; #endif }; /* VM error message array */ extern const err_msg_t *vm_messages; extern size_t vm_message_count; /* * VM error message array - English version. This version of the * messages is linked directly into the VM; at run time, we can attempt * to replace this version with another language version obtained from * an external file. We link in the English version so that we will * always have a valid set of messages even if the user doesn't have a * message file installed. */ extern const err_msg_t vm_messages_english[]; extern size_t vm_message_count_english; /* external message file signature */ #define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032" /* * load an external message file - returns zero on success, non-zero on * failure */ int err_load_message_file(osfildef *fp, const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size); /* load default message file */ #define err_load_vm_message_file(fp) \ err_load_message_file((fp), &vm_messages, &vm_message_count, \ vm_messages_english, vm_message_count_english) /* * check to see if an external message file has been loaded for the * default VM message set */ int err_is_message_file_loaded(); /* * delete messages previously loaded with err_load_message_file (this is * called automatically by err_terminate, so clients generally will not * need to call this directly) */ void err_delete_message_array(const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size); /* * Search an array of messages for a given message number. The array * must be sorted by message ID. */ const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count, int msgnum, int verbose); /* * Format a message with the parameters contained in an exception object. * Returns the size in bytes of the formatted message. If the output * buffer is null or too small, we'll fill it up as much as possible and * return the actual length required for the full message text. * * Suports the following format codes: * * %s - String. Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or * ERR_TYPE_TEXTCHAR_LEN value. * * %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer. * Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value. Automatically * uses the correct size for the argument. * * %% - Formats as a single percent sign. */ size_t err_format_msg(char *outbuf, size_t outbuflen, const char *msg, const struct CVmException *exc); /* * Format a message, allocating a buffer to store the result. The caller * must free the buffer with t3free(). */ char *err_format_msg(const char *msg, const struct CVmException *exc); /* ------------------------------------------------------------------------ */ /* * exception ID - this identifies an error */ typedef uint err_id_t; /* * Error parameter type codes */ enum err_param_type { /* ---------------- Display Parameters ---------------- */ /* * The following parameter types are for display purposes. These are * substituted into the message string in err_format_msg(). */ /* parameter is a native 'int' value */ ERR_TYPE_INT, /* parameter is a native 'unsigned long' value */ ERR_TYPE_ULONG, /* parameter is a 'textchar_t *' value (null terminated) */ ERR_TYPE_TEXTCHAR, /* parameter is a 'char *' value (null terminated) */ ERR_TYPE_CHAR, /* * parameter is a 'textchar_t *' value followed by a 'size_t' value * giving the number of bytes in the string */ ERR_TYPE_TEXTCHAR_LEN, /* parameter is a 'char *' value with a separate length */ ERR_TYPE_CHAR_LEN, /* ---------------- Non-Display Parameters ---------------- */ /* * The following parameter types are stored in the exception but are * NOT used in formatting the error message. */ /* * a char* value giving the ID/Version string for the metaclass * involved in the error (for example, "metaclass missing" or * "metaclass version not available") */ ERR_TYPE_METACLASS, /* a char* value the ID/Version string for the function set involved */ ERR_TYPE_FUNCSET, /* * void (no parameter): this is a flag indicating that the error is a * VM version error. This type of error can usually be solved by * updating the interpreter to the latest version. */ ERR_TYPE_VERSION_FLAG }; /* * Exception parameter */ struct CVmExcParam { /* type of this parameter */ err_param_type type_; /* value of the parameter */ union { /* as an integer */ int intval_; /* as an unsigned long */ unsigned long ulong_; /* as a text string */ const textchar_t *strval_; /* as a char string */ const char *charval_; /* as char string with separate length counter */ struct { const char *str_; size_t len_; } charlenval_; } val_; }; /* * Exception object */ struct CVmException { /* get the error code */ int get_error_code() const { return error_code_; } /* get the number of parameters */ int get_param_count() const { return param_count_; } /* get the type of the nth parameter */ err_param_type get_param_type(int n) const { return params_[n].type_; } /* get the nth parameter as an integer */ int get_param_int(int n) const { return params_[n].val_.intval_; } /* get the nth parameter as an unsigned long */ unsigned long get_param_ulong(int n) const { return params_[n].val_.ulong_; } /* get the nth parameter as a string */ const textchar_t *get_param_text(int n) const { return params_[n].val_.strval_; } /* get the nth parameter as a char string */ const char *get_param_char(int n) const { return params_[n].val_.charval_; } /* get the nth parameter as a counted-length string */ const char *get_param_char_len(int n, size_t *len) const { /* set the length return */ *len = params_[n].val_.charlenval_.len_; /* return the string pointer */ return params_[n].val_.charlenval_.str_; } /* set a parameter - null-terminated string value */ void set_param_str(int n, const char *str) { params_[n].type_ = ERR_TYPE_CHAR; params_[n].val_.charval_ = str; } /* set a parameter - counted-length string value */ void set_param_str(int n, const char *str, size_t len) { params_[n].type_ = ERR_TYPE_CHAR_LEN; params_[n].val_.charlenval_.str_ = str; params_[n].val_.charlenval_.len_ = len; } /* set a parameter - integer value */ void set_param_int(int n, int val) { params_[n].type_ = ERR_TYPE_INT; params_[n].val_.intval_ = val; } /* the error code */ err_id_t error_code_; /* * Is this a version-related error? If this is true, the error is due * to an out-of-date interpreter, and can usually be solved by * upgrading to the latest version. */ int version_flag_; /* * Some version-related errors are due to component versions, namely * metaclasses or function sets. For errors due to component versions, * we set the version flag AND set the appropriate identifier here to * indicate the required component version. (This is the component * version that's set as a dependency in the byte-code program we're * trying to run.) */ const char *metaclass_; const char *funcset_; /* number of parameters stored in the exception */ int param_count_; /* parameters (actual array size is given by param_cnt_) */ CVmExcParam params_[1]; }; /* ------------------------------------------------------------------------ */ /* * Error states. The states are bit flags. The following combinations are * possible: * * ERR_STATE_TRYING - the initial call to setjmp() in the err_try has just * returned * * ERR_STATE_EXCEPTION - we've just longjmp()'d into the frame from a * throw, and we haven't handled the exception yet * * ERR_STATE_EXCEPTION | ERR_STATE_FINALLY - we've longjmp()'d into the * frame from a throw, and we've entered the err_finally without * encountering an err_catch * * ERR_STATE_CAUGHT - we've longjmp()'d into the frame from a throw, and * we've entered the err_catch clause * * ERR_STATE_CAUGHT | ERR_FINALLY - we've longjmp()'d into the frame from a * throw, gone through the err_catch, and entered the err_finally * * ERR_STATE_RETHROWN - a new throw occurred within our err_catch or * err_finally clause * * ERR_STATE_RETHROWN | ERR_STATE_FINALLY - a new error occurred within our * err_catch or err_finally clause, and we've entered the err_finally */ typedef int err_state_t; /* * Trying - initial state, before any exception has been thrown. Note that * this must have value 0, because we initialize to this state from the * setjmp return going into the error frame, and setjmp always returns 0 on * the initial call. */ const int ERR_STATE_TRYING = 0; /* exception in progress, and has not been caught */ const int ERR_STATE_EXCEPTION = 0x0001; /* exception has been caught */ const int ERR_STATE_CAUGHT = 0x0002; /* error thrown while an err_catch or err_finally is in progress */ const int ERR_STATE_RETHROWN = 0x4000; /* * 'finally' block entered. This is OR'd into the other states when we * first enter the 'finally', so that we'll know not to re-enter the block * if another exception occurs within. */ const int ERR_STATE_FINALLY = 0x8000; /* ------------------------------------------------------------------------ */ /* * Error frame - allocated by err_try. This object is not manipulated * directly by the client; this is handled automatically by the error * macros. */ struct err_frame_t { /* current state */ err_state_t state_; /* enclosing error frame */ err_frame_t *prv_; /* exception being handled in this frame */ CVmException *exc_; /* jmpbuf for this handler */ jmp_buf jmpbuf_; }; /* * The current error frame active in this thread. Each time we enter an * err_try, we set up a new frame on the stack and set this thread-global * to point to the new frame, and we store the old value (the enclosing * frame) within the new frame, forming a stack via a linked list of * frames. When we leave the stack at err_end or via a throw, we pop the * frame stack by setting G_err_frame to the enclosing element. */ extern OS_DECL_TLS(err_frame_t *, G_err_frame); /* * Initialize the global error context. Should be called at program * initialization. 'param_stack_size' is no longer used. */ void err_init(size_t param_stack_size); /* * Delete the global error context. Should be called at program * termination. */ void err_terminate(); /* * Throw an exception. The first form takes no parameters except the * error code; the second form takes a parameter count followed by that * number of parameters. Each parameter requires two arguments: the * first is a type code of type err_param_type, and the second the * value, whose interpretation depends on the type code. */ void err_throw(err_id_t error_code); void err_throw_a(err_id_t error_code, int param_count, ...); /* * Rethrow the current exception. This is valid only in 'catch' blocks. */ void err_rethrow(); /* * Get the current exception being handled in the nearest enclosing * err_catch block. This searches the error frame stack for a frame in the * 'caught' state, and returns the exception object from that frame. This * allows retrieving the current exception being handled even when we enter * a new err_try block within an err_catch handler. */ CVmException *err_get_cur_exc(); /* * Fatal error - abort program */ void err_abort(const char *message); #define err_try \ { \ err_frame_t err_cur__; \ err_cur__.prv_ = os_tls_get(err_frame_t *, G_err_frame); \ os_tls_set(G_err_frame, &err_cur__); \ if ((err_cur__.state_ = \ (err_state_t)setjmp(err_cur__.jmpbuf_)) == ERR_STATE_TRYING) \ { \ #define err_catch(exc) \ } \ if (err_cur__.state_ == ERR_STATE_EXCEPTION) \ { \ CVmException *exc; \ exc = err_cur__.exc_; \ err_cur__.state_ = ERR_STATE_CAUGHT; #define err_catch_disc \ } \ if (err_cur__.state_ == ERR_STATE_EXCEPTION) \ { \ err_cur__.state_ = ERR_STATE_CAUGHT; #define err_finally \ } \ if ((err_cur__.state_ & ERR_STATE_FINALLY) == 0) \ { \ err_cur__.state_ |= ERR_STATE_FINALLY; #define err_end \ } \ os_tls_set(G_err_frame, err_cur__.prv_); \ if (err_cur__.state_ & (ERR_STATE_EXCEPTION | ERR_STATE_RETHROWN)) \ { \ if (os_tls_get(err_frame_t *, G_err_frame)->state_ \ & ERR_STATE_CAUGHT) \ t3free(os_tls_get(err_frame_t *, G_err_frame)->exc_); \ os_tls_get(err_frame_t *, G_err_frame)->exc_ = err_cur__.exc_; \ err_rethrow(); \ } \ if (err_cur__.state_ & ERR_STATE_CAUGHT) \ t3free(err_cur__.exc_); \ } #endif /* VMERR_H */ frobtads-1.2.3/tads3/vmerr.cpp0000644000175000001440000006455212006720646015400 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmerr.cpp,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerr.cpp - VM error handling Function This module contains global variables required for the error handler. Notes Modified 10/20/98 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmtype.h" #include "vmerr.h" /* ------------------------------------------------------------------------ */ /* * Global error context pointer, and reference count for the error * subsystem. */ OS_DECL_TLS(err_frame_t *, G_err_frame); static int G_err_refs = 0; /* ------------------------------------------------------------------------ */ /* * Initialize the global error context */ void err_init(size_t /*param_stack_size*/) { /* increase the error system reference counter */ if (++G_err_refs == 1) { /* * first initialization - allocate the thread-local variable slot * for the error frame pointer */ os_tls_create(G_err_frame); } } /* ------------------------------------------------------------------------ */ /* * Delete the global error context */ void err_terminate() { /* decrease the error system reference counter */ --G_err_refs; /* if that leaves no references, delete the error context */ if (G_err_refs == 0) { /* delete external messages, if we loaded them */ err_delete_message_array(&vm_messages, &vm_message_count, vm_messages_english, vm_message_count_english); /* delete the thrad-local slot for the error frame pointer */ os_tls_delete(G_err_frame); } } /* ------------------------------------------------------------------------ */ /* * Throw the error currently on the stack */ static void err_throw_current() { err_state_t new_state; /* * Figure out what state the enclosing frame will be in after this * jump. If we're currently in a 'trying' block, we'll now be in an * 'exception' state. Otherwise, we must have been in a 'catch' or * 'finally' already - in these cases, the new state is 'rethrown', * because we have an exception within an exception handler. */ err_state_t old_state = os_tls_get(err_frame_t *, G_err_frame)->state_; err_state_t f = (old_state & ERR_STATE_FINALLY); old_state &= ~ERR_STATE_FINALLY; if (old_state == ERR_STATE_TRYING) new_state = ERR_STATE_EXCEPTION | f; else new_state = ERR_STATE_RETHROWN | f; /* jump to the enclosing frame's exception handler */ longjmp(os_tls_get(err_frame_t *, G_err_frame)->jmpbuf_, new_state); } /* ------------------------------------------------------------------------ */ /* * Throw an exception */ void err_throw(err_id_t error_code) { /* throw the error, with no parameters */ err_throw_a(error_code, 0); } /* ------------------------------------------------------------------------ */ /* * store a string parameter */ static char *err_store_str(char* &strspace, const char *str, size_t len) { /* remember the starting position of the stored string */ char *ret = strspace; /* copy it into the string space */ memcpy(strspace, str, len); /* advance the allocation pointer */ strspace += len; /* add a trailing null */ *strspace++ = '\0'; /* return the start of the string */ return ret; } /* ------------------------------------------------------------------------ */ /* * Throw an exception with parameters in va_list format */ static size_t err_throw_v(err_id_t error_code, int param_count, va_list va, CVmException *exc) { /* * Assert that the size of the err_param_type enum is no larger than * the size of the native 'int' type. Since this routine is called * with a varargs list, and since ANSI requires compilers to promote * any enum type smaller than int to int when passing to a varargs * function, we must retrieve our err_param_type arguments (via the * va_arg() macro) with type 'int' rather than type 'enum * err_param_type'. * * The promotion to int is mandatory, so retrieving our values as * 'int' values is the correct, portable thing to do; the only * potential problem is that our enum type could be defined to be * *larger* than int, in which case the compiler would pass it to us * as the larger type, not int, and our retrieval would be incorrect. * The only way a compiler would be allowed to do this is if our enum * type actually required a type larger than unsigned int (in other * words, we had enum values higher than the largest unsigned int * value for the local platform). This is extremely unlikely, but * we'll assert our assumption here to ensure that the problem is * readily apparent should it ever occur. */ assert(sizeof(err_param_type) <= sizeof(int)); /* * Figure out how much space we need. Start with the size of the base * CVmException class, since we always need a CVmException structure. * This structure has space for one argument descriptor built in, so * subtract that off from our base size, since we'll add in space for * the argument descriptors separately. */ size_t siz = sizeof(CVmException) - sizeof(CVmExcParam); /* * Add in space the parameter descriptors. We need one CVmExcParam * structure to describe each parameter. */ siz += param_count * sizeof(CVmExcParam); /* fill in our base structure, if allocated */ CVmExcParam *param = 0; char *strspace = 0; if (exc != 0) { /* set the error code and parameter count */ exc->error_code_ = error_code; exc->param_count_ = param_count; /* presume this is not an out-of-date version error */ exc->version_flag_ = FALSE; exc->metaclass_ = 0; exc->funcset_ = 0; /* * If the current stack frame is already handling an error, delete * the current frame's exception object, since the current frame * will no longer be accessible after this throw. */ if (os_tls_get(err_frame_t *, G_err_frame)->state_ & (ERR_STATE_EXCEPTION | ERR_STATE_CAUGHT | ERR_STATE_RETHROWN)) t3free(os_tls_get(err_frame_t *, G_err_frame)->exc_); /* this is now the current exception */ os_tls_get(err_frame_t *, G_err_frame)->exc_ = exc; /* start at the first parameter slot */ param = exc->params_; /* * store strings in the allocated space after the base struct and * (varying-size) parameter array */ strspace = (char *)&exc->params_[param_count]; } /* * We now have our base exception structure, with enough space for the * parameter descriptors, but we still need to store the parameter * values themselves. */ for (int i = 0 ; i < param_count ; ++i, ++param) { /* get the type indicator, and store it in the descriptor */ err_param_type typ = (err_param_type)va_arg(va, int); /* store the type */ if (exc != 0) param->type_ = typ; /* store the argument's value */ int ival; ulong ulval; const char *sptr; size_t slen; switch(typ) { case ERR_TYPE_INT: /* store the integer */ ival = va_arg(va, int); if (exc != 0) param->val_.intval_ = ival; break; case ERR_TYPE_ULONG: /* store the unsigned long */ ulval = va_arg(va, unsigned long); if (exc != 0) param->val_.ulong_ = ulval; break; case ERR_TYPE_TEXTCHAR: /* * It's a (textchar_t *) string, null-terminated. Get the * string pointer and calculate its length. */ sptr = va_arg(va, textchar_t *); slen = get_strlen(sptr); /* count the string space needed */ siz += slen + 1; /* store it in parameter memory */ if (exc != 0) param->val_.strval_ = err_store_str(strspace, sptr, slen); break; case ERR_TYPE_TEXTCHAR_LEN: /* * It's a (textchar_t *) string with an explicit length given * as a separate size_t parameter. */ sptr = va_arg(va, textchar_t *); slen = va_arg(va, size_t); /* count the string space */ siz += slen + 1; /* store it in parameter memory */ if (exc != 0) { param->type_ = ERR_TYPE_TEXTCHAR; param->val_.strval_ = err_store_str(strspace, sptr, slen); } break; case ERR_TYPE_CHAR: /* it's a (char *) string, null-terminated */ sptr = va_arg(va, char *); slen = strlen(sptr); /* count the string space */ siz += slen + 1; /* store it */ if (exc != 0) param->val_.charval_ = err_store_str(strspace, sptr, slen); break; case ERR_TYPE_CHAR_LEN: /* it's a (char *) string with an explicit size_t size */ sptr = va_arg(va, char *); slen = va_arg(va, size_t); /* count the string space */ siz += slen + 1; /* store it */ if (exc != 0) { param->val_.charval_ = err_store_str(strspace, sptr, slen); param->type_ = ERR_TYPE_CHAR; } break; case ERR_TYPE_FUNCSET: /* * It's a char* string with a function set ID. These are not * stored in the parameters, but go in the funcset_ slot in the * exception object. */ sptr = va_arg(va, char *); siz += strlen(sptr) + 1; if (exc != 0) exc->funcset_ = err_store_str(strspace, sptr, strlen(sptr)); break; case ERR_TYPE_METACLASS: /* * It's a char* string with a metaclass ID. These are not * stored in the parameters, but go in the metaclass_ slot in * the exception object. */ sptr = va_arg(va, char *); siz += strlen(sptr) + 1; if (exc != 0) exc->metaclass_ = err_store_str(strspace, sptr, strlen(sptr)); break; case ERR_TYPE_VERSION_FLAG: /* * This parameter is a flag indicating that the error is due to * an out-of-date interpreter build. This has no parameter * data; we simply set the flag in the exception to indicate * the version error type. */ if (exc != 0) exc->version_flag_ = TRUE; break; } } /* * if we have an exception, throw it; if not, we're doing a dry run to * compute the exception object size, so simply return the computed * size */ if (exc != 0) { /* throw the exception object that we just populated */ err_throw_current(); VMERR_AFTER_ERR_THROW(return 0;) } else { /* we're only computing the size on this pass */ return siz; } } /* ------------------------------------------------------------------------ */ #ifdef MICROSOFT /* * Microsoft Visual C++ optimizer workaround - not applicable to other * systems. * * For MSVC, we need to turn off warning 4702 ("unreachable code"). MSVC * is too clever by half for our implementation here of err_throw_a(), and * the only recourse (empirically) seems to be to turn off this warning for * the duration of this function. * * The issue is that err_throw_a() makes a call to err_throw_v() within a * va_start()...va_end() pair. The MSVC optimizer recognizes that * err_throw_v() never returns, so it marks any code following a call to * same as unreachable. But we *have to* include the va_end() call after * the err_throw_v() call. The va_end() is *required* for portability - * some compilers expand va_end() to structural C code (for example, to * insert a close brace to balance an open brace inserted by va_start()). * So we can't omit the va_end() call regardless of its run-time * reachability. * * So, we have code that's both unreachable and required. The only * apparent solution is to disable the unreachable-code warning for the * duration of this function. * * (Conceivably, we could trick the optimizer by moving the err_throw_a() * implementation to a separate module; the optimizer probably couldn't * track the does-not-return status of err_throw_v() across modules. But * that wouldn't be any cleaner in any sense - it would just trick the * compiler in a different way. Better to explicitly turn off the warning; * at least that way the workaround is plain and explicit.) */ #pragma warning(push) #pragma warning(disable: 4702) #endif /* * Throw an exception with parameters */ void err_throw_a(err_id_t error_code, int param_count, ...) { va_list marker; /* do a dry run to determine the exception object size */ va_start(marker, param_count); size_t siz = err_throw_v(error_code, param_count, marker, 0); va_end(marker); /* allocate the new exception object */ CVmException *exc = (CVmException *)t3malloc(siz); /* build the argument list and throw the error */ va_start(marker, param_count); err_throw_v(error_code, param_count, marker, exc); va_end(marker); } /* MSVC - restore previous warning state (see above) */ #ifdef MICROSOFT #pragma warning(pop) #endif /* ------------------------------------------------------------------------ */ /* * Re-throw the current exception. This is valid only from 'catch' * blocks. */ void err_rethrow() { /* throw the error currently on the stack */ err_throw_current(); } /* ------------------------------------------------------------------------ */ /* * Abort the program with a serious, unrecoverable error */ void err_abort(const char *message) { printf("%s\n", message); exit(2); } /* ------------------------------------------------------------------------ */ /* * Retrieve the current exception being handled in the nearest enclosing * err_catch frame. */ CVmException *err_get_cur_exc() { /* * search the error frame stack for a frame in the 'caught exception' * state, starting at the current (innermost) frame */ for (err_frame_t *fr = os_tls_get(err_frame_t *, G_err_frame) ; fr != 0 ; fr = fr->prv_) { /* if this frame is in the 'caught' state, return its exception */ if ((fr->state_ & ERR_STATE_CAUGHT) != 0) return fr->exc_; } /* didn't find an exception */ return 0; } /* ------------------------------------------------------------------------ */ /* * Try loading a message file. Returns zero on success, non-zero if an * error occurred. */ int err_load_message_file(osfildef *fp, const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size) { char buf[128]; size_t i; err_msg_t *msg; /* read the file signature */ if (osfrb(fp, buf, sizeof(VM_MESSAGE_FILE_SIGNATURE)) || memcmp(buf, VM_MESSAGE_FILE_SIGNATURE, sizeof(VM_MESSAGE_FILE_SIGNATURE)) != 0) goto fail; /* delete any previously-loaded message array */ err_delete_message_array(arr, arr_size, default_arr, default_arr_size); /* read the message count */ if (osfrb(fp, buf, 2)) goto fail; /* set the new message count */ *arr_size = osrp2(buf); /* allocate the message array */ *arr = (err_msg_t *)t3malloc(*arr_size * sizeof(err_msg_t)); if (*arr == 0) goto fail; /* clear the memory */ memset((err_msg_t *)*arr, 0, *arr_size * sizeof(err_msg_t)); /* read the individual messages */ for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg) { size_t len1, len2; /* read the current message ID and the length of the two messages */ if (osfrb(fp, buf, 8)) goto fail; /* set the message ID */ msg->msgnum = (int)t3rp4u(buf); /* get the short and long mesage lengths */ len1 = osrp2(buf + 4); len2 = osrp2(buf + 6); /* allocate buffers */ msg->short_msgtxt = (char *)t3malloc(len1 + 1); msg->long_msgtxt = (char *)t3malloc(len2 + 1); /* if either one failed, give up */ if (msg->short_msgtxt == 0 || msg->long_msgtxt == 0) goto fail; /* read the two messages */ if (osfrb(fp, (char *)msg->short_msgtxt, len1) || osfrb(fp, (char *)msg->long_msgtxt, len2)) goto fail; /* null-terminate the strings */ *(char *)(msg->short_msgtxt + len1) = '\0'; *(char *)(msg->long_msgtxt + len2) = '\0'; } /* success */ return 0; fail: /* revert back to the built-in array */ err_delete_message_array(arr, arr_size, default_arr, default_arr_size); /* indicate failure */ return 1; } /* * Determine if an external message file has been loaded for the default * VM message set */ int err_is_message_file_loaded() { /* * if we're not using the compiled-in message array, we must have * loaded an external message set */ return vm_messages != &vm_messages_english[0]; } /* * Delete the message array, if one is loaded */ void err_delete_message_array(const err_msg_t **arr, size_t *arr_size, const err_msg_t *default_arr, size_t default_arr_size) { /* * If the message array is valid, and it's not set to point to the * built-in array of English messages, we must have allocated it, so * we must now free it. We don't need to free it if it points to * the English array, because that's static data linked into the VM * executable. */ if (*arr != 0 && *arr != default_arr) { size_t i; err_msg_t *msg; /* delete each message in the array */ for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg) { /* delete the strings for this entry */ if (msg->short_msgtxt != 0) t3free((char *)msg->short_msgtxt); if (msg->long_msgtxt != 0) t3free((char *)msg->long_msgtxt); } /* delete the message array itself */ t3free((err_msg_t *)*arr); } /* set the messages array back to the built-in english messages */ *arr = default_arr; *arr_size = default_arr_size; } /* ------------------------------------------------------------------------ */ /* * Find an error message */ const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count, int msgnum, int verbose) { int hi, lo, cur; /* perform a binary search of the message list */ lo = 0; hi = msg_count - 1; while (lo <= hi) { /* split the difference */ cur = lo + (hi - lo)/2; /* is it a match? */ if (msg_array[cur].msgnum == msgnum) { /* it's the one - return the text */ return (verbose ? msg_array[cur].long_msgtxt : msg_array[cur].short_msgtxt); } else if (msgnum > msg_array[cur].msgnum) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? cur - 1 : cur); } } /* no such message */ return 0; } /* ------------------------------------------------------------------------ */ /* * Format a message, allocating a buffer */ char *err_format_msg(const char *msg, const CVmException *exc) { /* * format the message into a null buffer to calculate the length; add a * byte for null termination */ size_t len = err_format_msg(0, 0, msg, exc) + 1; /* allocate a buffer */ char *buf = (char *)t3malloc(len); /* format the message into the new buffer */ err_format_msg(buf, len, msg, exc); /* return the buffer */ return buf; } /* * Format a message with the parameters contained in an exception object. * Suports the following format codes: * * %s - String. Formats an ERR_TYPE_CHAR or ERR_TYPE_TEXTCHAR value, * including the counted-length versions. * * %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer. * Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value. * Automatically uses the correct size for the argument. * * %% - Formats as a single percent sign. */ size_t err_format_msg(char *outbuf, size_t outbuflen, const char *msg, const CVmException *exc) { int curarg; const char *p; char *dst; int exc_argc; size_t need = 0; size_t rem = outbuflen; /* get the number of parameters in the exception object */ exc_argc = (exc == 0 ? 0 : exc->get_param_count()); /* start with the first parameter */ curarg = 0; /* start at the beginning of the buffer */ dst = outbuf; /* if there's no message, there's nothing to return */ if (msg == 0) { ++need; if (outbuflen != 0) *dst = '\0'; return need; } /* scan the format string for formatting codes */ for (p = msg ; *p != '\0' ; ++p) { /* if it's a format specifier, translate it */ if (*p == '%') { const char *src; char srcbuf[30]; err_param_type typ; size_t len; int use_strlen; /* * if no more parameters are available, ignore the * formatting code entirely, and leave it in the string as * it is */ if (curarg >= exc_argc) { ++need; if (rem > 1) --rem, *dst++ = *p; continue; } /* get the type of the current parameter */ typ = exc->get_param_type(curarg); /* * presume we'll want to use strlen to get the length of the * source value */ use_strlen = TRUE; /* skip the '%' and determine what follows */ ++p; switch (*p) { case 's': /* get the string value using the appropriate type */ if (typ == ERR_TYPE_TEXTCHAR) src = exc->get_param_text(curarg); else if (typ == ERR_TYPE_CHAR) src = exc->get_param_char(curarg); else if (typ == ERR_TYPE_CHAR_LEN) { /* get the string value and its length */ src = exc->get_param_char_len(curarg, &len); /* * src isn't null terminated, so don't use strlen to * get its length - we already have it from the * parameter data */ use_strlen = FALSE; } else src = "s"; break; case 'd': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%d", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%ld", exc->get_param_ulong(curarg)); else src = "d"; break; case 'u': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%u", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%lu", exc->get_param_ulong(curarg)); else src = "u"; break; case 'x': src = srcbuf; if (typ == ERR_TYPE_INT) sprintf(srcbuf, "%x", exc->get_param_int(curarg)); else if (typ == ERR_TYPE_ULONG) sprintf(srcbuf, "%lx", exc->get_param_ulong(curarg)); else src = "x"; break; case '%': /* add a single percent sign */ src = "%"; break; default: /* invalid format character; leave the whole thing intact */ src = srcbuf; srcbuf[0] = '%'; srcbuf[1] = *p; srcbuf[2] = '\0'; break; } /* get the length, if it's null-terminated */ if (use_strlen) len = strlen(src); /* count the full 'len' in our space needs */ need +=len; /* copy as much as we can */ if (rem > 1) { /* limit it to the remaining space, minus a null byte */ if (len > rem - 1) len = rem - 1; /* copy the value and advance past it in the output buffer */ memcpy(dst, src, len); dst += len; rem -= len; } /* consume the argument */ ++curarg; } else { /* just copy the current character as it is */ ++need; if (rem > 1) --rem, *dst++ = *p; } } /* add the trailing null */ if (rem != 0) *dst++ = '\0'; /* return the space required */ return need; } frobtads-1.2.3/tads3/vmdatasrc.h0000644000175000001440000006474612145504453015703 0ustar realncusers/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmdatasrc.h - Data Source object Function CVmDataSource is an abstract class representing a read/write data source, such as a file, block of memory, etc. The data source object has an interface essentially identical to the osifc file interface, but can be implemented with a variety of underlying data sources. Notes Modified 05/05/10 MJRoberts - Creation */ #ifndef VMDATASRC_H #define VMDATASRC_H #include #include "t3std.h" #include "osifcnet.h" #include "vmerr.h" #include "vmfile.h" #include "vmerrnum.h" /* ------------------------------------------------------------------------ */ /* * Abstract file interface. This allows the File intrinsic class to * present a common interface on multiple underlying data sources. */ class CVmDataSource { public: virtual ~CVmDataSource() { } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) = 0; /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf, size_t len) = 0; /* write bytes - returns 0 on success, non-zero on error */ virtual int write(const void *buf, size_t len) = 0; /* write a null-terminated string */ virtual int writez(const char *str) { return write(str, strlen(str)); } /* get the length of the file in bytes */ virtual long get_size() = 0; /* get the current seek location */ virtual long get_pos() = 0; /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) = 0; /* flush - returns 0 on success, non-zero on error */ virtual int flush() = 0; /* close the underlying system resource */ virtual void close() = 0; /* * Read a line of text (fgets() semantics). This default * implementation reads a character at a time from the underlying * source; subclasses should override this if they can implement it * more efficiently. */ virtual char *read_line(char *buf, size_t len) { /* if we have no bytes left, return null for EOF */ if (get_pos() >= get_size()) return 0; /* read bytes until we encounter a CR, LF, or CR-LF */ char *dst = buf; while (len > 0 && get_pos() < get_size()) { /* read the next character */ char ch; if (read(&ch, 1)) return 0; /* check for a line terminator */ if (ch == 10 || ch == 13) { /* * add a newline to the result (it's always '\n' in the * result buffer regardless of the type of newline in the * source) */ *dst++ = '\n'; --len; /* * if this is a CR-LF sequence, skip both characters; * otherwise just skip the single CR or LF */ char ch2; if (ch == 13 && get_pos() < get_size() && !read(&ch2, 1) && ch2 != 10) { /* it's not a CR-LF - un-get the second character */ seek(get_pos() - 1, OSFSK_SET); } /* we're done */ break; } /* copy this byte */ *dst++ = ch; /* count it against the remaining buffer length */ --len; } /* add a null terminator if possible */ if (len != 0) *dst = '\0'; /* return the original buffer pointer */ return buf; } /* read a line of text into an allocated buffer */ char *read_line_alo() { char *ret = 0; size_t retlen = 0; /* keep going until we satisfy the request */ for (;;) { /* read a chunk; if at EOF, we're done */ char buf[1024]; if (read_line(buf, sizeof(buf)) == 0) break; /* get the length of this chunk */ size_t len = strlen(buf); /* allocate or expand the buffer to accommodate the new text */ if (ret == 0) ret = (char *)t3malloc(len + 1); else ret = (char *)t3realloc(ret, retlen + len + 1); /* fail if out of memory */ if (ret == 0) return 0; /* add the new chunk to the result (including the null) */ memcpy(ret + retlen, buf, len + 1); retlen += len; /* if the buffer ends with a newline, we're done */ if (retlen != 0 && ret[retlen-1] == '\n') break; } /* return our buffer */ return ret; } /* * Create a duplicate data source. * * 'mode' is a simplified version of the stdio fopen() mode string. * The first letter is 'r' for read access, 'w' for write access. This * can be followed by '+' for both read and write access; 'r+' and 'w+' * are identical, both indicating read/write access. This can * optionally be followed by 't' for text mode or 'b' for binary mode; * text mode is assumed if not specified. */ virtual CVmDataSource *clone(VMG_ const char *mode) = 0; }; /* ------------------------------------------------------------------------ */ /* * Basic OS file data source. */ class CVmFileSource: public CVmDataSource { public: CVmFileSource(osfildef *fp) { this->fp = fp; } ~CVmFileSource() { if (fp != 0) close(); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { return osfrb(fp, buf, len); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int readc(void *buf, size_t len) { return osfrbc(fp, buf, len); } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { return osfgets(buf, len, fp); } /* write bytes - returns 0 on success, non-zero on error*/ virtual int write(const void *buf, size_t len) { return osfwb(fp, buf, len); } /* get the length of the file in bytes */ virtual long get_size() { /* remember the current seek location */ long oldpos = osfpos(fp); /* seek to the end */ osfseek(fp, 0, OSFSK_END); /* the current position is the file size */ long siz = osfpos(fp); /* seek back to where we started */ osfseek(fp, oldpos, OSFSK_SET); /* return the size */ return siz; } /* get the current seek location */ virtual long get_pos() { return osfpos(fp); } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { return osfseek(fp, ofs, mode); } /* flush - returns 0 on success, non-zero on error */ virtual int flush() { return osfflush(fp); } /* close the underlying system resource */ virtual void close() { osfcls(fp); fp = 0; } /* clone the source */ virtual CVmDataSource *clone(VMG_ const char *mode) { /* duplicate our underlying file handle */ osfildef *fpdup = osfdup(fp, mode); if (fpdup == 0) return 0; /* return a new source wrapping the duplicated file handle */ return new CVmFileSource(fpdup); } protected: /* the underlying file */ osfildef *fp; }; /* ------------------------------------------------------------------------ */ /* * Resource file data source. A resource file is a contiguous byte range * within a larger file. */ class CVmResFileSource: public CVmDataSource { public: CVmResFileSource(osfildef *fp, unsigned long start, long end) { this->fp = fp; this->start = start; this->end = end; } ~CVmResFileSource() { if (fp != 0) close(); } /* clone the source */ virtual CVmDataSource *clone(VMG_ const char *mode) { /* duplicate our underlying file handle */ osfildef *fpdup = osfdup(fp, mode); if (fpdup == 0) return 0; /* return a new source wrapping the duplicated file handle */ return new CVmResFileSource(fpdup, start, end); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* if this would take us past the end, we can't fulfill the request */ unsigned long pos = osfpos(fp); if (pos + len > end) return 1; /* do the read */ return osfrb(fp, buf, len); } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { /* if we're already past the end, we can't fulfill the request */ unsigned long pos = osfpos(fp); if (pos >= end) return 0; /* read a line */ char *ret = osfgets(buf, len, fp); if (ret == 0) return 0; /* * if that moved us past the end of our segment, go back and * re-read the exact number of bytes remaining */ if ((unsigned long)osfpos(fp) > end) { /* seek back to where we started */ osfseek(fp, pos, OSFSK_SET); /* read the remaining bytes in the resource (up to buflen) */ size_t r = end - pos > len ? (long)len : (size_t)(end - pos); if (osfrb(fp, buf, r)) return 0; /* if that didn't fill the buffer, null-terminate it */ if (r < len) buf[r] = '\0'; } /* return the result */ return buf; } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int readc(void *buf, size_t len) { /* if we're already past the end, we can't read anything */ unsigned long pos = osfpos(fp); if (pos >= end) return 0; /* limit the read to the available file size */ unsigned long limit = end - pos; if (len > limit) len = (size_t)limit; /* do the read */ return osfrbc(fp, buf, len); } /* write bytes - resource files are read-only */ virtual int write(const void *buf, size_t len) { return 1; } /* get the length of the file in bytes */ virtual long get_size() { return end - start; } /* get the current seek location */ virtual long get_pos() { return osfpos(fp) - start; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: do_set: /* check that 'ofs' is in range */ if (ofs < 0 || (unsigned long)ofs > end - start) return 1; /* seek to the offset */ return osfseek(fp, start + ofs, OSFSK_SET); case OSFSK_CUR: /* set the offset relative to the current position */ ofs += get_pos(); goto do_set; case OSFSK_END: /* set the offset relative to the end of the file */ ofs += (end - start); goto do_set; default: /* invalid mode */ return 1; } } /* flush - we're read-only, so this just returns success */ virtual int flush() { return 0; } /* close the underlying system resource */ virtual void close() { osfcls(fp); fp = 0; } protected: /* the underlying file */ osfildef *fp; /* the byte range within the resource file */ unsigned long start, end; }; /* ------------------------------------------------------------------------ */ /* * Read-only string data source. This takes a buffer in memory and * presents a read-only file interface to it. */ class CVmStringSource: public CVmDataSource { public: CVmStringSource(const char *mem, long len) { this->mem = mem; this->memlen = len; this->pos = 0; } virtual ~CVmStringSource() { } virtual CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmStringSource(mem, memlen); } /* read bytes - returns 0 on success, non-zero on EOF or error */ virtual int read(void *buf, size_t len) { /* make sure there's enough data to satisfy the request */ if (pos + (long)len > memlen) return 1; /* copy the data */ memcpy(buf, mem + pos, len); /* advance the read pointer */ pos += len; /* success */ return 0; } /* read bytes - returns the number of bytes read; 0 means EOF or error */ virtual int readc(void *buf, size_t len) { /* limit the read to the available length */ long rem = memlen - pos; if ((long)len > rem) len = rem; /* copy the bytes */ memcpy(buf, mem + pos, len); /* advance the read pointer */ pos += len; /* return the amount read */ return (int)len; } /* read a line of text (fgets() semantics) */ virtual char *read_line(char *buf, size_t len) { /* if we have no bytes left, return null for EOF */ if (pos == memlen) return 0; /* read bytes until we encounter a CR, LF, or CR-LF */ char *dst = buf; while (pos < memlen && len > 0) { /* check for a line terminator */ if (mem[pos] == 10 || mem[pos] == 13) { /* * add a newline to the result (it's always '\n' in the * result buffer regardless of the type of newline in the * source) */ *dst++ = '\n'; --len; /* * if this is a CR-LF sequence, skip both characters; * otherwise just skip the single CR or LF */ ++pos; if (pos < memlen && mem[pos-1] == 13 && mem[pos] == 10) ++pos; /* we're done */ break; } /* copy this byte */ *dst++ = mem[pos++]; /* count it against the remaining buffer length */ --len; } /* add a null terminator if possible */ if (len != 0) *dst = '\0'; /* return the original buffer pointer */ return buf; } /* write bytes - we're read-only, so this is an error */ virtual int write(const void *buf, size_t len) { return 1; } /* get the length of the file in bytes */ virtual long get_size() { return memlen; } /* get the current seek location */ virtual long get_pos() { return pos; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: do_set: /* make sure it's in range */ if (ofs < 0 || ofs > memlen) return 1; /* set the new position */ pos = ofs; /* success */ return 0; case OSFSK_CUR: /* calculate the absolute position and go do the set */ ofs += pos; goto do_set; case OSFSK_END: /* calculate the absolute position and go do the set */ ofs += memlen; goto do_set; default: /* invalid mode */ return 1; } } /* flush - we're read-only, so this is a no-op; just return success */ virtual int flush() { return 0; } /* close the underlying system resource; there's nothing for us to do */ virtual void close() { } protected: /* our memory buffer, and the number of bytes in the buffer */ const char *mem; long memlen; /* current read position */ long pos; }; /* ------------------------------------------------------------------------ */ /* * Private string source - makes a private copy of a string buffer */ class CVmPrivateStringSource: public CVmStringSource { public: CVmPrivateStringSource(const char *mem, long len) : CVmStringSource(0, len) { this->bufobj = new CVmRefCntBuf(mem, len); this->mem = bufobj->buf_; } CVmPrivateStringSource(CVmRefCntBuf *bufobj, long len) : CVmStringSource(bufobj->buf_, len) { this->bufobj = bufobj; } CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmPrivateStringSource(bufobj, memlen); } ~CVmPrivateStringSource() { bufobj->release_ref(); } protected: CVmRefCntBuf *bufobj; }; /* ------------------------------------------------------------------------ */ /* * Memory data source. This automatically allocates additional buffer * space as needed. */ /* page block for a memory source */ struct CVmMemorySourceBlock { CVmMemorySourceBlock(long ofs) { this->ofs = ofs; nxt = 0; } /* block length */ static const int BlockLen = 4096; /* next block in the list */ CVmMemorySourceBlock *nxt; /* byte offset in the overall stream of the start of this block */ long ofs; /* block buffer */ char buf[BlockLen]; }; /* page list for a memory source */ class CVmMemorySourceList: public CVmRefCntObj { public: static const int BlockLen = CVmMemorySourceBlock::BlockLen; CVmMemorySourceList(long init_len) { /* no blocks yet */ first_block_ = last_block_ = 0; /* always allocate at least the initial block */ add_block(); init_len -= BlockLen; /* set the seek position to the start of the first block */ cur_block_ = first_block_; cur_block_ofs_ = 0; /* add more blocks until we've satisfied the length request */ for ( ; init_len > 0 ; init_len -= BlockLen) add_block(); /* the file is currently empty */ len_ = 0; } /* read bytes */ int read(void *buf, size_t len) { /* * if the request would take us past the current content length, * fail immediately, since we know we can't satisfy the request */ if (cur_block_->ofs + cur_block_ofs_ + (long)len > len_) return 1; /* * do the read; return success == 0 if the amount read exactly * matches the requested length */ return (size_t)readc(buf, len) != len; } /* read bytes */ int readc(void *buf0, size_t len) { /* get the buffer pointer as a character pointer */ char *buf = (char *)buf0; /* we haven't ready anything yet */ size_t bytes_read = 0; /* limit the length to the remaining bytes in the object */ long rem = len_ - get_pos(); if ((long)len > rem) len = (size_t)rem; /* keep going until we satisfy the request */ while (len != 0) { /* figure how much we can read from the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* read that much */ if (cur != 0) { /* copy bytes to the output buffer */ memcpy(buf, cur_block_->buf + cur_block_ofs_, cur); /* move past the bytes in the current block */ cur_block_ofs_ += cur; /* adjust our request counters past this chunk */ len -= cur; buf += cur; /* count it in the total read so far */ bytes_read += cur; } /* if that satisfies the request, we're done */ if (len == 0) break; /* if this is the last block, we're done */ if (cur_block_->nxt == 0) return bytes_read; /* advance to the start of the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } /* return the amount we managed to read */ return bytes_read; } /* write bytes */ int write(const void *buf0, size_t len) { /* get the buffer pointer as a character pointer */ const char *buf = (const char *)buf0; /* keep going until we satisfy the request */ for (;;) { /* figure how much we can write to the current block */ size_t rem = BlockLen - cur_block_ofs_; size_t cur = (len < rem ? len : rem); /* write that much */ if (cur != 0) { memcpy(cur_block_->buf + cur_block_ofs_, buf, cur); cur_block_ofs_ += cur; len -= cur; buf += cur; } /* if that satisfied the request, we're done */ if (len == 0) break; /* if this is the last block, add another */ if (cur_block_->nxt == 0) { /* add the block */ add_block(); /* if that didn't work, fail */ if (cur_block_->nxt == 0) return 1; } /* advance to the start of the next block */ cur_block_ = cur_block_->nxt; cur_block_ofs_ = 0; } /* * if the seek pointer is past the current content length, we've * written past the old end of the file and thus expanded the file * - note the new length */ if (cur_block_->ofs + cur_block_ofs_ > len_) len_ = cur_block_->ofs + cur_block_ofs_; /* success */ return 0; } /* get the length of the stream's contents */ long get_size() { return len_; } /* get the current seek offset */ long get_pos() { /* * figure the seek position as the current block's base offset plus * the current offset within that block */ return cur_block_->ofs + cur_block_ofs_; } /* set the seek offset */ int seek(long pos, int mode) { /* figure the absolute position based on the mode */ switch (mode) { case OSFSK_SET: /* from the beginning - use pos as given */ break; case OSFSK_CUR: /* from the current position */ pos += get_pos(); break; case OSFSK_END: /* from the end */ pos += get_size(); break; default: /* other modes are invalid */ return 1; } /* limit the position to the file's bounds */ if (pos < 0) { /* at start of file */ pos = 0; } else if (pos >= len_) { /* * At end of file. This is a special case for setting the * block pointer, because we could be positioned at the last * byte of the last block, which we won't find in our scan. * Explicitly set the position here. */ pos = len_; cur_block_ = last_block_; cur_block_ofs_ = pos - last_block_->ofs; /* we've set the block position, so skip the usual search */ return 0; } /* find the block containing the seek offset */ CVmMemorySourceBlock *b; for (b = first_block_ ; b != 0 ; b = b->nxt) { /* if the offset is within this block, we're done */ if (pos >= b->ofs && pos < b->ofs + BlockLen) { /* set the current pointers */ cur_block_ = b; cur_block_ofs_ = pos - b->ofs; /* stop scanning */ break; } } /* success */ return 0; } protected: ~CVmMemorySourceList() { /* delete our block list */ CVmMemorySourceBlock *cur, *nxt; for (cur = first_block_ ; cur != 0 ; cur = nxt) { nxt = cur->nxt; delete cur; } } /* add a block to the end of the list */ void add_block() { long ofs = last_block_ == 0 ? 0 : last_block_->ofs + BlockLen; CVmMemorySourceBlock *b = new CVmMemorySourceBlock(ofs); if (last_block_ != 0) last_block_->nxt = b; else first_block_ = b; last_block_ = b; } /* current seek block and offset */ CVmMemorySourceBlock *cur_block_; long cur_block_ofs_; /* total file length */ long len_; /* head/tail of block list */ CVmMemorySourceBlock *first_block_; CVmMemorySourceBlock *last_block_; }; /* * Paged memory source */ class CVmMemorySource: public CVmDataSource { public: CVmMemorySource(long init_len) { /* create our underlying page list */ pl = new CVmMemorySourceList(init_len); } /* clone the data source */ CVmDataSource *clone(VMG_ const char * /*mode*/) { /* return a new data source wrapper for our page list */ return new CVmMemorySource(pl); } ~CVmMemorySource() { /* release our underlying page list */ pl->release_ref(); } /* read bytes */ virtual int read(void *buf, size_t len) { return pl->read(buf, len); } /* read bytes */ virtual int readc(void *buf, size_t len) { return pl->readc(buf, len); } /* write bytes */ virtual int write(const void *buf, size_t len) { return pl->write(buf, len); } /* get the length of the stream's contents */ virtual long get_size() { return pl->get_size(); } /* get the current seek offset */ virtual long get_pos() { return pl->get_pos(); } /* set the seek offset */ virtual int seek(long pos, int mode) { return pl->seek(pos, mode); } /* flush - we don't buffer, so this does nothing */ virtual int flush() { return 0; } /* * close - we don't have underlying system resources (other than * our memory buffers), so there's nothing to do here */ virtual void close() { } protected: CVmMemorySource(CVmMemorySourceList *pl) { pl->add_ref(); this->pl = pl; } /* our underlying page list */ CVmMemorySourceList *pl; }; #endif /* VMDATASRC_H */ frobtads-1.2.3/tads3/vmwrtimg.h0000644000175000001440000002612211677362237015570 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmwrtimg.h,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmwrtimg.h - T3 Image File Writer utility functions Function Notes Modified 04/04/99 MJRoberts - Creation */ #ifndef VMWRTIMG_H #define VMWRTIMG_H #include "t3std.h" #include "vmtype.h" class CVmImageWriter { public: /* create an image writer with a given output file */ CVmImageWriter(class CVmFile *fp); /* delete the image writer */ ~CVmImageWriter(); /* get the current seek location in the underlying file */ long get_pos() const; /* * Prepare the file: write out the fixed header information. This * should be called once, before doing any other writing. 'vsn' is * the image file version number to store in the file. */ void prepare(uint vsn, const char tool_data[4]); /* * Begin a block with the specified ID (a four-byte string). Writes * the block header, and remembers where the block starts so that * the size prefix can be stored when the block is finished. Blocks * cannot be nested; starting a new block automatically ends the * previous block. */ void begin_block(const char *block_id, int mandatory); /* * End the current block. */ void end_block(); /* write raw bytes to the image file */ void write_bytes(const char *ptr, uint32_t siz); /* * Write a complete entrypoint block, given the code offset of the * entrypoint and the various table entry sizes. If a block is in * progress, this will terminate it. */ void write_entrypt(uint32_t entry_ofs, size_t method_header_size, size_t exc_entry_size, size_t line_entry_size, size_t dbg_hdr_size, size_t dbg_lclsym_hdr_size, size_t dbg_frame_hdr_size, int dbg_vsn_id); /* * Write a complete function set dependency block, given an array of * function set names. If a block is in progress, this will * terminate it. */ void write_func_dep(const char **funcset_names, int count); /* * Write a complete metaclass dependency block, given an array of * metaclass names. If a block is in progress, this will terminate * it. */ void write_meta_dep(const char **metaclass_names, int count); /* * Write a metaclass dependency block in pieces. Call * begin_meta_dep() with the number of metaclasses, then call * write_meta_dep_item() to write each item, and finally call * end_meta_dep() to finish the block. */ void begin_meta_dep(int count); void write_meta_dep_item(const char *metaclass_name); void write_meta_item_prop(uint prop_id); void end_meta_prop_list(); void end_meta_dep(); /* * Write a function set dependency block in pieces */ void begin_func_dep(int count) { begin_dep_block("FNSD", count); } void write_func_dep_item(const char *funcset_name) { write_dep_block_item(funcset_name); } void end_func_dep() { end_dep_block(); } /* * Write a constant pool definition block. */ void write_pool_def(uint pool_id, uint32_t page_count, uint32_t page_size, int mandatory); /* * Fix up a constant pool definition block with the actual number of * pages. This can be used, if desired, to wait to determine the * actual number of pages in a given constant pool until after * writing the pages. Since the pool definition block must precede * the pool's first page block in the image file, it's impossible to * wait until after writing all of the pages to write the definition * block. Instead, the caller must write the pool definition block * first, using a temporary placeholder value for the page_count (0 * will suffice), then write the pool pages, then use this to fix up * the pool definition block with the actual number of pages. The * caller must note the seek position prior to writing the pool * definition block, so that we can seek back to that position to * fix up the definition block. * * Callers need not use this function if they know the actual number * of pages in the pool when writing the original pool definition * block. */ void fix_pool_def(long def_seek_ofs, uint32_t page_count); /* * Write a constant/code pool page. This writes the entire page * with its header and data in a single operation; this is the * easiest way to write a page when the page has been fully * constructed in a single memory block in advance. */ void write_pool_page(uint pool_id, uint32_t page_index, const char *page_data, uint32_t page_data_size, int mandatory, uchar xor_mask); /* * Write a constant/code pool page in pieces. These routines can be * used when the data in the page are not contiguous in memory and * must be written in pieces. Start by calling begin_pool_page(), * then call write_pool_page_bytes() for each item; the items are * written contiguously to the page. Finish by calling * end_pool_page(). */ void begin_pool_page(uint pool_id, uint32_t page_index, int mandatory, uchar xor_mask); void write_pool_page_bytes(const char *buf, uint32_t siz, uchar xor_mask); void end_pool_page(); /* * Write items in the symbolic names block. Start with * begin_sym_block(), then call the write_sym_item_xxx() functions * to write the names. Finally, call end_sym_block() when done. */ void begin_sym_block(); void write_sym_item_objid(const char *nm, size_t len, ulong obj_id); void write_sym_item_propid(const char *nm, size_t len, uint prop_id); void write_sym_item_func(const char *nm, size_t len, ulong code_ofs); void write_sym_item(const char *nm, size_t nmlen, const struct vm_val_t *val); void write_sym_item_objid(const char *nm, ulong obj_id) { write_sym_item_objid(nm, get_strlen(nm), obj_id); } void write_sym_item_propid(const char *nm, uint prop_id) { write_sym_item_propid(nm, get_strlen(nm), prop_id); } void write_sym_item(const char *nm, const struct vm_val_t *val) { write_sym_item(nm, get_strlen(nm), val); } void end_sym_block(); /* * Write items in an OBJS (static object data) block. Start with * begin_objs_block(), then call write_objs_bytes() repeatedly to * write the bytes. Finally, call end_objs_block() when done. * * If the 'large_objects' field is set, the objects in the block use * 32-bit size fields; otherwise the objects use 16-bit size fields. * * If 'trans' is true, the objects in this block are transient; * otherwise, the objects are non-transient (i.e., persistent). */ void begin_objs_block(uint metaclass_idx, int large_objects, int trans); void write_objs_bytes(const char *buf, uint32_t siz); void end_objs_block(uint object_count); /* * Write the items in a SRCF (source file descriptor) block. Start * with begin_srcf_block(), then write the file entries. For each * entry, call begin_src_entry(), then call write_src_line_entry() * for each source line debug record, then call end_srcf_entry(). * Call end_srcf_block() when done with all file entries. */ void begin_srcf_block(int count); void begin_srcf_entry(int orig_index, const char *fname); void write_srcf_line_entry(ulong linenum, ulong addr); void end_srcf_entry(); void end_srcf_block(); /* * Write the items in a GSYM (global symbol table) block. Start * with begin_gsym_block(), then call write_gsym_entry() repeatedly * to write the entries. Call end_gsym_block() when done. */ void begin_gsym_block(); void write_gsym_entry(const char *sym, size_t sym_len, int type_id, const char *dat, size_t dat_len); void end_gsym_block(ulong count); /* * Begin MHLS block, write an item, and end the block */ void begin_mhls_block(); void write_mhls_entry(ulong code_addr); void end_mhls_block(); /* * Begin/end SINI block. static_cs_ofs is the offset in the code * segment of the first static initializer; this is useful in the * image file because we can delete all of the code pages starting * at this point after pre-initialization is complete. */ void begin_sini_block(ulong static_cs_ofs, ulong init_cnt); void end_sini_block(); /* begin/end a MACR (macro symbols) block */ void begin_macr_block(); void end_macr_block(); /* * Finish the file. Automatically ends the current block if a block * is open, and writes the end-of-file marker. */ void finish(); /* * get the underlying file object; for some types of blocks, it's * simplest for the caller to write the data directly to the underlying * file stream without any help from us */ class CVmFile *get_fp() const { return fp_; } private: /* write a generic dependency (function set, metaclass) block */ void write_dep_block(const char *block_id, const char **names, int count); /* write a dependency block in pieces */ void begin_dep_block(const char *block_id, int count); void write_dep_block_item(const char *nm); void end_dep_block(); /* XOR a block of bytes with a mask and write the results to the file */ void xor_and_write_bytes(const char *p, uint32_t len, uchar xor_mask); /* underlying file */ class CVmFile *fp_; /* * Seek position of start of current block. If this is zero, no * block is currently open. */ long block_start_; /* count of symbolic names written so far (for writing SYMD block) */ int symd_cnt_; /* location of metaclass entry next-record offset */ long mcld_ofs_pos_; /* location of metaclass property list count prefix */ long mcld_propcnt_pos_; /* count of properties writeen in a metaclass prop list so far */ int mcld_prop_cnt_; /* seek location of SYMD count prefix, for fixing up at end of block */ long symd_prefix_; /* seek location of OBJS count prefix, for fixup up at end of block */ long objs_prefix_; /* seek location of GSYM count prefix, for fixup at end of block */ long gsym_prefix_; /* start of current SRCF file entry */ long srcf_entry_pos_; /* count of SRCF line entries */ long srcf_line_cnt_; /* position of SRCF line entry for the current file */ long srcf_line_pos_; /* position of MHLS block count entry, and MHLS entry count so far */ long mhls_cnt_pos_; ulong mhls_cnt_; }; #endif /* VMWRTIMG_H */ frobtads-1.2.3/tads3/tcmakecl.cpp0000644000175000001440000016056012145504453016024 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmakecl.cpp - TADS Compiler "Make" command line tool Function Command-line interface to "make" program build utility Notes Modified 07/11/99 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3_os.h" #include "t3std.h" #include "tcmake.h" #include "tchostsi.h" #include "vmerr.h" #include "tcvsn.h" #include "resload.h" #include "tcmain.h" #include "tclibprs.h" #include "t3test.h" #include "tccmdutl.h" #include "rcmain.h" /* * Library parser. This is a specialized version of the library parser * that we use to expand library arguments into their corresponding source * file lists. */ class CTcLibParserCmdline: public CTcLibParser { public: /* * Process a command-line argument that refers to a library. We'll * read the library and add each file mentioned in the library to the * source module list. * * Returns zero on success, non-zero if any errors occur. Fills in * '*nodef' on return to indicate whether or not a "nodef" flag * appeared in the library. */ static int process_lib_arg(CTcHostIfc *hostifc, CTcMake *mk, CRcResList *res_list, const char *lib_name, const char *lib_url, int *nodef) { char path[OSFNMAX]; char full_name[OSFNMAX]; /* add a default "tl" extension to the library name */ strcpy(full_name, lib_name); os_defext(full_name, "tl"); /* extract the path name from the library's name */ os_get_path_name(path, sizeof(path), lib_name); /* * add the directory containing the library to the include path, * if it's not already there */ mk->maybe_add_include_path(path); /* set up our library parser object */ CTcLibParserCmdline parser(hostifc, mk, res_list, full_name, lib_url); /* scan the library and add its files to the command line */ parser.parse_lib(); /* set the 'nodef' indication for the caller */ *nodef = parser.nodef_; /* if there are any errors, return non-zero */ return parser.get_err_cnt(); } protected: /* instantiate */ CTcLibParserCmdline(CTcHostIfc *hostifc, CTcMake *mk, CRcResList *res_list, const char *lib_name, const char *lib_url) : CTcLibParser(lib_name) { /* remember our host interface */ hostifc_ = hostifc; /* remember our 'make' control object */ mk_ = mk; /* remember our resource list container */ res_list_ = res_list; /* remember the URL to the library */ lib_name_ = lib_copy_str(lib_name); lib_url_ = lib_copy_str(lib_url); /* no "nodef" flag yet */ nodef_ = FALSE; } ~CTcLibParserCmdline() { /* delete the library name and URL strings */ lib_free_str(lib_name_); lib_free_str(lib_url_); } /* process a source file entry in the library */ void scan_full_source(const char *val, const char *fname) { char url[OSFNMAX*2 + 20]; CTcMakeModule *mod; /* add the source module to our module list */ mod = mk_->add_module(fname, 0, 0); /* * build the full URL for the module - this is the library URL * prefix plus the "source:" value (not the full filename - simply * the original unretouched value of the "source:" variable) */ sprintf(url, "%.*s%.*s", (int)OSFNMAX, lib_url_, (int)OSFNMAX, val); /* set the module's URL */ mod->set_url(url); /* set the module's original name and library name */ mod->set_orig_name(val); mod->set_from_lib(lib_name_, lib_url_); } /* process a sub-library entry in the library */ void scan_full_library(const char *val, const char *fname) { char url[OSFNMAX*2 + 20]; int nodef; /* * build the library URL prefix - this is the parent library URL * prefix, plus our "library:" value (not the full filename - * simply the original unretouched "library:" variable value), * plus a slash */ sprintf(url, "%.*s%.*s/", (int)OSFNMAX, lib_url_, (int)OSFNMAX, val); /* parse the sub-library */ if (process_lib_arg(hostifc_, mk_, res_list_, fname, url, &nodef)) { /* * error parsing the sub-library - count it in our own error * count, so we know the parsing failed */ ++err_cnt_; } /* * if the sub-library had a 'nodef' flag, count it as a 'nodef' * flag in this library */ if (nodef) nodef_ = TRUE; } /* process a resource entry in the library */ void scan_full_resource(const char *val, const char *fname) { /* add the resource to the list */ res_list_->add_file(fname, val, TRUE); } /* display an error */ void err_msg(const char *msg, ...) { va_list args; /* display the error */ va_start(args, msg); hostifc_->v_print_err(msg, args); hostifc_->print_err("\n"); va_end(args); } /* look up a preprocessor symbol */ int get_pp_symbol(char *dst, size_t dst_len, const char *sym_name, size_t sym_len) { const char *val; size_t val_len; /* ask the 'make' control object for the expansion */ val = mk_->look_up_pp_sym(sym_name, sym_len); /* if we didn't find a value, return undefined */ if (val == 0) return -1; /* if it fits in the caller's result buffer, copy it */ val_len = strlen(val); if (val_len <= dst_len) memcpy(dst, val, val_len); /* return the value length */ return val_len; } /* scan a "nodef" flag */ void scan_nodef() { nodef_ = TRUE; } /* our 'make' control object */ CTcMake *mk_; /* our resource list container */ CRcResList *res_list_; /* host system interface */ CTcHostIfc *hostifc_; /* the library name */ char *lib_name_; /* * the library URL - this is the common prefix for the URL of every * member of the library */ char *lib_url_; /* flag: we've seen a "nodef" flag */ int nodef_; }; /* ------------------------------------------------------------------------ */ /* * Helper object for CTcCommandUtil::parse_opt_file */ class CMainOptHelper: public CTcOptFileHelper { public: /* allocate an option string */ char *alloc_opt_file_str(size_t len) { return lib_alloc_str(len); } /* free an option string previously allocated */ void free_opt_file_str(char *str) { lib_free_str(str); } /* process a comment (we ignore comments) */ void process_comment_line(const char *) { } /* process a non-comment line (ignore it) */ void process_non_comment_line(const char *) { } /* process a configuration section line (ignore it) */ void process_config_line(const char *, const char *, int) { } }; /* ------------------------------------------------------------------------ */ /* * Make a file path relative to the option file path, if we indeed have an * option file path. 'buf' is a buffer the caller provides where we can * build the full path, if necessary. We'll return a pointer either to the * buffer containing the combined path, or to the original filename if we * decided not to use the relative path. */ static char *make_opt_file_relative(char *buf, size_t buflen, int read_opt_file, const char *opt_file_path, char *fname) { /* * if we haven't read an option file, we don't have an option file path * to use as the relative root, so use the original filename unchanged */ if (!read_opt_file) return fname; /* convert the name to local conventions */ char lcl[OSFNMAX]; os_cvt_url_dir(buf, buflen < sizeof(lcl) ? buflen : sizeof(lcl), fname); /* if the filename is absolute, use it as given */ if (os_is_file_absolute(buf)) return buf; /* * we have a relative filename and an option file, so build the given * name relative to the option file path, using the version that we * converted to local conventions */ lib_strcpy(lcl, sizeof(lcl), buf); os_build_full_path(buf, buflen, opt_file_path, lcl); /* return the caller's buffer where we built the full path */ return buf; } /* ------------------------------------------------------------------------ */ /* * Program main entrypoint */ int main(int argc, char **argv) { CTcHostIfcStdio *hostifc = new CTcHostIfcStdio(); int curarg; char *p; int force_build = FALSE; int force_link = FALSE; CTcMake *mk; int err_cnt, warn_cnt; int image_specified = FALSE; char dirbuf[OSFNMAX + 4096]; int show_banner; const char *opt_file = 0; char opt_file_path[OSFNMAX]; int read_opt_file = FALSE; int usage_err = FALSE; int add_def_mod = TRUE; int compile_only = FALSE; int pp_only = FALSE; osfildef *string_fp = 0; osfildef *assembly_fp = 0; int sym_dir_set = FALSE; int obj_dir_set = FALSE; int lib_err = FALSE; CMainOptHelper opt_helper; int opt_file_path_warning = FALSE; int need_opt_file_path_warning; const size_t SUPPRESS_LIST_MAX = 100; int suppress_list[SUPPRESS_LIST_MAX]; size_t suppress_cnt; CTcMakePath *sys_inc_entry; CTcMakePath *sys_lib_entry; int verbose = FALSE; int pedantic = FALSE; int res_recurse = TRUE; CRcResList *res_list; int warnings_as_errors = FALSE; /* we don't have any warning codes to suppress yet */ suppress_cnt = 0; /* no errors or warnings yet */ err_cnt = warn_cnt = 0; /* create a resource list, in case we have resource options */ res_list = new CRcResList(); /* initialize the error subsystem */ { CResLoader *res_loader; char buf[OSFNMAX]; /* create a resource loader */ os_get_special_path(buf, sizeof(buf), argv[0], OS_GSP_T3_RES); res_loader = new CResLoader(buf); /* tell the resource loader the executable filename, if possible */ if (os_get_exe_filename(buf, sizeof(buf), argv[0])) res_loader->set_exe_filename(buf); /* initialize the error subsystem */ CTcMain::tc_err_init(1024, res_loader); /* done with the resource loader */ delete res_loader; } /* create the 'make' object */ mk = new CTcMake(); /* presume we'll show the banner and progress messages */ show_banner = TRUE; /* * Add the default system header directory to the system include path. * System include paths always follow any user-specified paths in the * search order. */ os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_INC); sys_inc_entry = mk->add_sys_include_path(dirbuf); /* * Add the user library search path list to the source search path. * This list comes from platform-specific global configuration data * (such as a environment variable on Unix), so we want it to come * after any search list specified in the command-line options; ensure * that we search these locations last by making them "system" paths, * since system paths follow all command-line paths. */ os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_USER_LIBS); for (p = dirbuf ; *p != '\0' ; ) { char *start; char *nxt; /* remember where this list element starts */ start = p; /* find the next path separator character */ for ( ; *p != '\0' && *p != OSPATHSEP ; ++p) ; /* if there's another path element, note its start */ nxt = p; if (*nxt == OSPATHSEP) ++nxt; /* null-terminate the path at the separator */ *p = '\0'; /* add this path */ mk->add_sys_source_path(start); /* continue scanning at the next list element */ p = nxt; } /* * Add the default system library directory to the source path. * System source paths always follow user-specified paths in the * search order. */ os_get_special_path(dirbuf, sizeof(dirbuf), argv[0], OS_GSP_T3_LIB); sys_lib_entry = mk->add_sys_source_path(dirbuf); parse_options: /* read the options */ for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg) { char subopt; char relbuf[OSFNMAX]; /* * if we have a "-source" or "-lib" argument, we're done with * options, and we're on to the module list */ if (strcmp(argv[curarg], "-source") == 0 || strcmp(argv[curarg], "-lib") == 0 || strcmp(argv[curarg], "-res") == 0) break; /* * figure out which option we have, starting with the first letter * after the '-' */ switch(argv[curarg][1]) { case 'f': /* read an options file - make sure we don't have one already */ if (opt_file != 0) { /* can't read a file from a file */ printf("error: only one option file (-f) is allowed\n"); err_cnt = 1; goto done; } /* * remember the options file name - we'll get around to the * actual reading of the file after we finish with the * command line options */ opt_file = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1); if (opt_file == 0) goto missing_option_arg; break; case 'd': /* set debug mode */ mk->set_debug(TRUE); break; case 'D': /* add preprocessor symbol definition */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1); if (p != 0) { char *eq; /* see if there's an '=' in the string */ for (eq = p ; *eq != '\0' && *eq != '=' ; ++eq) ; /* if the '=' is present, replace it with a null byte */ if (*eq == '=') { /* replace it */ *eq = '\0'; /* skip to the start of the replacement text */ ++eq; } /* add the symbol definition */ mk->def_pp_sym(p, eq); } else goto missing_option_arg; break; case 'e': /* check for longer option names */ if (strlen(argv[curarg]) >= 7 && memcmp(argv[curarg], "-errnum", 7) == 0) { /* check for "+" or "-" suffixes */ if (strcmp(argv[curarg] + 7, "+") == 0 || argv[curarg][7] == '\0') { /* turn on error number display */ mk->set_show_err_numbers(TRUE); } else if (strcmp(argv[curarg] + 7, "-") == 0) { /* turn off error number display */ mk->set_show_err_numbers(FALSE); } else { /* invalid option */ goto bad_option; } } else { /* invalid option */ goto bad_option; } break; case 'U': /* add preprocess symbol un-definition */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1); if (p != 0) mk->undef_pp_sym(p); else goto missing_option_arg; break; case 'c': /* see what follows */ switch (argv[curarg][2]) { case 's': /* it's a character set specification */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2); if (p != 0) mk->set_source_charset(p); else goto missing_option_arg; break; case 'l': /* check for "-clean" */ if (strcmp(argv[curarg], "-clean") == 0) { /* set "clean" mode */ mk->set_clean_mode(TRUE); } else { /* invalid option */ goto bad_option; } break; case '\0': /* set compile-only (no link) mode */ mk->set_do_link(FALSE); compile_only = TRUE; break; default: /* invalid option */ goto bad_option; } break; case 'a': /* see what follows */ switch(argv[curarg][2]) { case 'l': /* force only the link phase */ force_link = TRUE; break; case '\0': force_build = TRUE; break; default: /* invalid option */ goto bad_option; } break; case 'v': /* set verbose mode */ mk->set_verbose(TRUE); verbose = TRUE; break; case 'I': /* add a #include path */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1); if (p != 0) { /* make it relative to the option file path if appropriate */ p = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, p); /* add the path */ mk->add_include_path(p); } else goto missing_option_arg; break; case 'o': /* set the image file name */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 1); if (p != 0) { /* make it relative to the option file path if appropriate */ p = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, p); /* set the image file name */ mk->set_image_file(p); /* note that we have an explicit image file name */ image_specified = TRUE; } else goto missing_option_arg; break; case 'O': switch(argv[curarg][2]) { case 's': /* * if we already have a string capture file, close the * old one */ if (string_fp != 0) { osfcls(string_fp); string_fp = 0; } /* set the string file */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2); if (p == 0) goto missing_option_arg; /* make it relative to the option file path if appropriate */ p = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, p); /* open the string file */ string_fp = osfopwt(p, OSFTTEXT); if (string_fp == 0) { printf("error: unable to create string capture file " "\"%s\"\n", p); goto done; } /* set the string capture file in the build object */ mk->set_string_capture(string_fp); /* done */ break; default: /* invalid suboption */ goto bad_option; } break; case 'F': /* presume we won't need an option file path warning */ need_opt_file_path_warning = FALSE; /* note the sub-option letter */ subopt = argv[curarg][2]; /* * If applicable, get the filename/path argument. Most of the * -F suboptions take an argument, but some don't, so check for * the ones that don't. */ p = 0; if (strchr("C", subopt) == 0) { p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2); if (p == 0) goto missing_option_arg; /* make it relative to the option file path */ p = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, p); /* * if this is an absolute path, note it so we can warn * about it if this is in an option file */ need_opt_file_path_warning = os_is_file_absolute(p); } /* check the suboption */ switch(subopt) { case 'L': /* override the system library path */ sys_lib_entry->set_path(p); break; case 'I': /* override the system include path */ sys_inc_entry->set_path(p); break; case 's': /* add the source path */ mk->add_source_path(p); break; case 'y': /* set the symbol path */ mk->set_symbol_dir(p); /* remember that we have a symbol directory */ sym_dir_set = TRUE; break; case 'o': /* set the object file directory */ mk->set_object_dir(p); /* remember that we've set this path */ obj_dir_set = TRUE; break; case 'C': /* set the "create output directories" flag */ mk->set_create_dirs(TRUE); break; case 'a': /* * Set the assembly listing file. Note that this is an * undocumented option - at the moment, the assembly * listing is a bit rough, and is meant for internal TADS 3 * development use, to facilitate analysis of the * compiler's code generation. */ /* close any previous assembly listing file */ if (assembly_fp != 0) { osfcls(assembly_fp); assembly_fp = 0; } /* open the listing file */ assembly_fp = osfopwt(p, OSFTTEXT); if (assembly_fp == 0) { printf("error: unable to create assembly listing file " "\"%s\"\n", p); goto done; } /* set the listing file */ mk->set_assembly_listing(assembly_fp); /* done */ break; default: /* invalid option */ goto bad_option; } /* * if we're reading from a command file, and we haven't already * found reason to warn about absolute paths, note that we need * to warn now */ if (read_opt_file && need_opt_file_path_warning) opt_file_path_warning = TRUE; /* done */ break; case 'G': /* code generation options */ if (strcmp(argv[curarg], "-Gstg") == 0) { /* turn on sourceTextGroup mode */ mk->set_source_text_group_mode(TRUE); } else goto bad_option; break; case 'n': /* check what we have */ if (strcmp(argv[curarg], "-nopre") == 0) { /* explicitly turn off preinit mode */ mk->set_preinit(FALSE); } else if (strcmp(argv[curarg], "-nobanner") == 0) { /* turn off the banner */ show_banner = FALSE; } else if (strcmp(argv[curarg], "-nodef") == 0) { /* don't add the default modules */ add_def_mod = FALSE; } else goto bad_option; break; case 'p': /* check what we have */ if (strcmp(argv[curarg], "-pre") == 0) { /* explicitly turn on preinit mode */ mk->set_preinit(TRUE); } else goto bad_option; break; case 'P': /* * A preprocess-only mode. The plain -P generates * preprocessed source to standard output; -Pi generates only * a list of names of #included files to standard output. */ if (strcmp(argv[curarg], "-P") == 0) { /* set preprocess-only mode */ mk->set_pp_only(TRUE); pp_only = TRUE; } else if (strcmp(argv[curarg], "-Pi") == 0) { /* set list-includes mode */ mk->set_list_includes(TRUE); pp_only = TRUE; } break; case 'q': /* check the full option name */ if (strcmp(argv[curarg], "-quotefname") == 0) { /* they want quoted filenames in error messages */ mk->set_err_quoted_fnames(TRUE); } else if (strcmp(argv[curarg], "-q") == 0) { /* quiet mode - turn off banner and progress messages */ show_banner = FALSE; hostifc->set_show_progress(FALSE); } else goto bad_option; break; case 's': /* check the full option name */ if (strcmp(argv[curarg], "-statprefix") == 0) { /* get the argument */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 10); /* set the status message prefix text */ if (p != 0) hostifc->set_status_prefix(p); else goto missing_option_arg; } else if (strcmp(argv[curarg], "-statpct") == 0) { /* note that we want status percentage updates */ mk->set_status_pct_mode(TRUE); } else { /* unknown option - report usage error */ goto bad_option; } break; case 't': /* check the full option name */ if (strcmp(argv[curarg], "-test") == 0) { /* * it's the secret test-mode option - the test scripts use * this to set the reporting mode so that we suppress path * names in progress reports, so that the test logs can be * insensitive to local path name conventions */ mk->set_test_report_mode(TRUE); /* perform any necessary test initialization */ test_init(); } else { /* unknown option - report usage error */ goto bad_option; } break; case 'w': /* warning level/mode - see what level they're setting */ switch(argv[curarg][2]) { int num; int enable; case '0': /* no warnings */ mk->set_warnings(FALSE); mk->set_pedantic(FALSE); break; case '1': /* normal warnings, but not pedantic warnings */ mk->set_warnings(TRUE); mk->set_pedantic(FALSE); break; case '2': /* show all warnings, even pedantic ones */ mk->set_warnings(TRUE); mk->set_pedantic(TRUE); pedantic = TRUE; break; case '+': case '-': /* * Add/remove a warning to/from the list of warnings to * suppress. First, note whether we're enabling or * disabling the warning. */ enable = (argv[curarg][2] == '+'); /* get the warning number */ p = CTcCommandUtil::get_opt_arg(argc, argv, &curarg, 2); if (p == 0) goto missing_option_arg; /* convert it to a number */ num = atoi(p); /* * add it to the list if we're disabling it, otherwise * remove it from the list (since every warning is enabled * by default) */ if (!enable) { /* if we don't have room, we can't add it */ if (suppress_cnt >= SUPPRESS_LIST_MAX) { /* tell them what's wrong */ printf("Too many -w-# options - maximum %d.\n", (int)SUPPRESS_LIST_MAX); /* abort */ goto done; } /* add the new list entry */ suppress_list[suppress_cnt++] = num; } else { size_t rem; int *src; int *dst; /* * scan the list for an existing instance of this * number, and remove any we find */ for (rem = suppress_cnt, src = dst = suppress_list ; rem != 0 ; ++src, --rem) { /* if this one isn't to be suppressed, keep it */ if (*src != num) *dst++ = *src; } /* calculate the new count */ suppress_cnt = dst - suppress_list; } /* remember the updated list */ mk->set_suppress_list(suppress_list, suppress_cnt); /* done */ break; case 'e': /* turn "warnings as errors" mode on or off */ switch (argv[curarg][3]) { case '\0': case '+': warnings_as_errors = TRUE; mk->set_warnings_as_errors(TRUE); break; case '-': warnings_as_errors = FALSE; mk->set_warnings_as_errors(FALSE); break; default: goto bad_option; } break; default: /* invalid option */ goto bad_option; } break; case '?': /* take '?' as an explicit request to show the usage message */ goto show_usage; case 'h': /* check for "-help" */ if (strcmp(argv[curarg], "-help") == 0) goto show_usage; /* other '-h*' options are unrecognized */ goto bad_option; default: bad_option: /* invalid - describe the problem */ printf("Error: Invalid option: \"%s\"\n" "(Type \"t3make -help\" for a summary of the " "command syntax)\n", argv[curarg]); /* abort */ goto done; missing_option_arg: /* missing option argument */ printf("Error: Missing argument for option \"%s\"\n" "(Type \"t3make -help\" for a summary of the " "command syntax)\n", argv[curarg]); /* abort */ goto done; } } /* * if there are no module arguments, and no project file was * specified, look for a default project file */ if (curarg >= argc && !usage_err && opt_file == 0) { /* * if the default project file exists, try reading it; otherwise, * show a usage error */ if (!osfacc(T3_DEFAULT_PROJ_FILE)) opt_file = T3_DEFAULT_PROJ_FILE; else { /* * if there are no arguments at all, just show the usage * message; otherwise, explain that we need some source files */ if (argc == 1) { /* just fall through to the usage message */ usage_err = TRUE; } else { /* explain that we need source files */ printf("Error: No source file(s) specified.\n" "(Type \"t3make -help\" for help with the " "command syntax.\n"); goto done; } } } /* * Show the banner, unless they explicitly asked us not to (and even * then, show it anyway if we're going to show the "usage" message). */ if ((show_banner || usage_err) && (opt_file == 0 || read_opt_file)) { char patch_id[10]; /* * Generate the patch number or development build iteration number, * if appropriate. If there's a patch number, give it precedence, * appending it as an extra dot-number suffix; if there's a build * number, show it as an alphabetic suffix. If neither is defined, * add nothing, so we'll just have a three-part dotted version * number. */ if (TC_VSN_PATCH != 0) sprintf(patch_id, ".%d", TC_VSN_PATCH); else if (TC_VSN_DEVBUILD != 0) sprintf(patch_id, "%c", TC_VSN_DEVBUILD + 'a' - 1); else patch_id[0] = '\0'; /* show the banner */ /* copyright-date-string */ printf("TADS Compiler %d.%d.%d%s " "Copyright 1999, 2012 Michael J. Roberts\n", TC_VSN_MAJOR, TC_VSN_MINOR, TC_VSN_REV, patch_id); } /* * warn about absolute paths in the options file, if necessary; do this * only in pedantic mode, though, since it's only a usage suggestion */ if (opt_file_path_warning && pedantic) { /* show the warning */ printf("Warning: absolute path found in -Fs/-Fo/-Fy option " "in options (-f) file.\n"); /* add more explanation if in verbose mode */ if (verbose) printf("It's better not to use absolute paths in the " "options file, because this\n" "ties the options file to the particular " "directory layout of your machine.\n" "If possible, refer only to subfolders of the " "folder containing the options\n" "file, and refer to the subfolders using " "relative path notation.\n"); } /* if a usage error occurred, display the usage message */ if (usage_err) { show_usage: printf("usage: t3make [options] module ... [-res resources]\n" "Options:\n" " -a - rebuild all files, " "even if up to date\n" " -al - relink, even if image file is up to date\n" " -c - compile only (do not create image file)\n" " -clean - delete all derived (symbol, object, image) " "files\n" " -cs xxx - source file character set is 'xxx'\n" " -d - compile for debugging (include symbols)\n" " -D x=y - define preprocessor symbol 'x' with value 'y'\n" " -errnum - show numeric error codes with error messages\n" " -f file - read command line options from 'file'\n" " -I dir - add 'dir' to #include search path\n" " -FC - create output directories (for -Fy, -Fo) if they " "don't exist\n" " -Fs dir - add 'dir' to source file search path\n" " -Fy dir - put symbol files in directory 'dir'\n" " -Fo dir - put object files in directory 'dir'\n" " -FI dir - override the standard system include path\n" " -FL dir - override the standard system library path\n" " -Gstg - generate sourceTextGroup properties\n" " -nobanner - suppress version/copyright banner\n" " -nodef - do not include default library modules in build\n" " -nopre - do not run pre-initialization, regardless of " "debug mode\n" " -o img - set image file name to 'img'\n" " -Os str - write all strings found in compiled files to " "text file 'str'\n" " -P - preprocess only; write preprocessed source " "to console\n" " -Pi - preprocess only; write only list of #include " "files to console\n" " -pre - run pre-initialization, regardless of " "debug mode\n" " -q - quiet mode: suppress banner and " "progress reports\n" " -quotefname - quote filenames in error messages\n" " -statprefix txt - set status-message prefix text\n" " -U x - un-define preprocessor symbol 'x'\n" " -v - display verbose error messages\n" " -w# - warning level: 0=none, 1=standard " "(default), 2=pedantic\n" " -w-# - suppress the given warning number\n" " -w+# - enable the given warning number\n" " -we - treat warnings as errors (-we- to disable)\n" "\n" "Modules: Each entry in the module list can have one " "of these forms:\n" "\n" " filename - specify the name of a source or " "library file; the\n" " file's type is inferred from its " "filename suffix\n" " -source filename - specify the name of a source file\n" " -lib filename - specify the name of a library\n" "\n" "The default filename suffix for a source file is \".t\", " "and the default for a\n" "library is \".tl\". You can omit the \"-source\" or " "\"-lib\" specifier for any\n" "file if its name includes the correct default suffix for " "its type, and its\n" "name doesn't begin with a \"-\".\n" "Immediately following each library, you can add one or more " "\"-x filename\"\n" "options to exclude library members. Each \"-x\" excludes " "one library member.\n" "The filename specified in a \"-x\" option must exactly " "match the filename as it\n" "appears in the library \"source:\" line.\n" "\n" "If no modules are specified, the compiler will attempt to " "find '" T3_DEFAULT_PROJ_FILE "'\n" "in the current directory, and will read it to obtain build " "instructions.\n" "\n" "Resources: Multi-media resources can be bundled into the " "image file using the\n" "-res option. After the -res option, specify any desired " "resource options:\n" "\n" " -recurse - recurse into subdirectories for subsequent " "items (default)\n" " -norecurse - do not recurse into subdirectories for " "subsequent items\n" " file - add 'file' as a multimedia resource\n" " file=alias - add 'file', using 'alias' as the resource " "name\n" " dir - add all files within directory 'dir' (and all " "files in all\n" " subdirectories of 'dir', if -recurse option " "is in effect)\n" "\n" "When a file is specified without an '=alias' name, the " "stored resource name is\n" "the original filename converted to URL-style notation. If " "an '=alias' name is\n" "specified, then the stored resource will be named with the " "alias name.\n" "\n" "Note that resource options are ignored when compiling " "for debugging.\n"); /* terminate */ goto done; } /* add the modules */ for ( ; curarg < argc ; ++curarg) { int is_source; int is_lib; char relbuf[OSFNMAX]; /* we don't know the file type yet */ is_source = FALSE; is_lib = FALSE; /* check to see if we have a module type option */ if (argv[curarg][0] == '-') { if (strcmp(argv[curarg], "-source") == 0) { /* note that we have a source file, and skip the option */ is_source = TRUE; ++curarg; } else if (strcmp(argv[curarg], "-lib") == 0) { /* note that we have a library, and skip the option */ is_lib = TRUE; ++curarg; } else if (strcmp(argv[curarg], "-res") == 0) { /* * Resource arguments follow - we're done with modules. * Skip the "-res" argument and exit the module loop. */ ++curarg; break; } else { /* invalid option in the module list */ goto bad_option; } /* * if we have no filename argument following the type * specifier, it's an error */ if (curarg == argc) { --curarg; goto missing_option_arg; } } /* * if we didn't find an explicit type specifier, infer the type * from the filename suffix */ if (!is_source && !is_lib) { size_t len; /* * if we have a ".tl" suffix, assume it's a library file; * otherwise, assume it's a source file */ if ((len = strlen(argv[curarg])) > 3 && stricmp(argv[curarg] + len - 3, ".tl") == 0) { /* ".tl" - it's a library file */ is_lib = TRUE; } else { /* something else - assume it's a source file */ is_source = TRUE; } } /* make the filename relative to the option file path */ p = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, argv[curarg]); /* process the file according to its type */ if (is_source) { /* add this file to the module list */ CTcMakeModule *mod = mk->add_module(p, 0, 0); /* set the module's original name to the name as given */ mod->set_orig_name(argv[curarg]); /* * Also use the original name (with a default .t suffix) as the * search name, in case it's a file we're going to search for * using the source search path. When searching, we want to * search for the original name, not the project-relative name. */ char sbuf[OSFNMAX]; lib_strcpy(sbuf, sizeof(sbuf), argv[curarg]); os_defext(sbuf, "t"); mod->set_search_source_name(sbuf); /* * if no image has been specified yet already, use this * module's name as the image name, with the suffix ".t3" */ if (!image_specified) { char buf[OSFNMAX]; /* * build the default image filename by changing the * module's extension to "t3" (or adding the "t3" * extension if the module didn't have one to begin with) */ strcpy(buf, p); os_remext(buf); os_addext(buf, "t3"); /* set the filename */ mk->set_image_file(buf); /* note that we know the image file's name now */ image_specified = TRUE; } } else { CTcMakeModule lib_mod; CTcMakeModule *last_pre_lib_mod; char fname[OSFNMAX]; char orig_fname[OSFNMAX]; int nodef; /* add a default "tl" extension */ strcpy(fname, p); os_defext(fname, "tl"); /* likewise, add a default "tl" extension to the original name */ strcpy(orig_fname, argv[curarg]); os_defext(orig_fname, "tl"); /* * set up a module for the library, so we can easily search * the source path for the module */ lib_mod.set_module_name(fname); lib_mod.set_search_source_name(orig_fname); /* get the module name by searching the path if necessary */ mk->get_srcfile(fname, &lib_mod); /* remember the last module before this library's modules */ last_pre_lib_mod = mk->get_last_module(); /* * Process the library. Since this is a top-level library * (rather than a sub-library included from within another * library), there is no prefix to the URL's of the member * items. */ if (CTcLibParserCmdline::process_lib_arg( hostifc, mk, res_list, fname, "", &nodef)) lib_err = TRUE; /* if there was a 'nodef' flag, note it */ if (nodef) add_def_mod = FALSE; /* process any exclusion options */ while (curarg + 1 < argc && strcmp(argv[curarg+1], "-x") == 0) { /* skip to the "-x" */ ++curarg; /* if the filename is missing, report a usage error */ if (curarg + 1 >= argc) goto missing_option_arg; /* skip the -x and get its argument (the URL to exclude) */ const char *xurl = argv[++curarg]; /* * Start with the next module after the last module before * this library. If there was nothing before this library, * then this library's first module is the first module in * the entire program,. */ CTcMakeModule *mod = last_pre_lib_mod; if (mod != 0) mod = mod->get_next(); else mod = mk->get_first_module(); /* scan the list of library modules for a match */ for ( ; mod != 0 ; mod = mod->get_next()) { /* if this module has the excluded URL, exclude it */ if (stricmp(mod->get_url(), xurl) == 0) { /* mark this module as excluded */ mod->set_excluded(TRUE); /* * no need to look any further - assume each URL * is unique */ break; } } } } } /* if we have any more arguments, they're for resource files */ for ( ; curarg < argc ; ++curarg) { char relbuf[OSFNMAX]; /* check for a resource bundler option */ if (argv[curarg][0] == '-') { /* check the argument */ switch(argv[curarg][1]) { case 'n': /* check for '-norecurse' */ if (strcmp(argv[curarg], "-norecurse") == 0) res_recurse = FALSE; else goto bad_option; break; case 'r': /* check for '-recurse' */ if (strcmp(argv[curarg], "-recurse") == 0) res_recurse = TRUE; else goto bad_option; break; default: /* unknown option */ goto bad_option; } } else { char *p; char *fname; char *alias; /* * It's not an option, so it must be a file. Scan for an * alias, which is introduced with an '=' character. */ for (p = fname = argv[curarg] ; *p != '\0' && *p != '=' ; ++p) ; if (*p == '=') { /* * overwrite the '=' with a null byte, so that the filename * ends here */ *p = '\0'; /* the alias starts after the '=' */ alias = p + 1; } else { /* there's no alias */ alias = 0; } /* make the filename relative to the option file path */ fname = make_opt_file_relative(relbuf, sizeof(relbuf), read_opt_file, opt_file_path, fname); /* add this file to the resource list*/ res_list->add_file(fname, alias, res_recurse); } } /* * If an option file was specified or implied, and we haven't read * it yet, read it now. */ if (opt_file != 0 && !read_opt_file) { osfildef *fp; int new_argc; char **new_argv; char new_opt_file[OSFNMAX]; /* if the options file doesn't exist, try adding the suffix 't3m' */ if (osfacc(opt_file)) { strcpy(new_opt_file, opt_file); os_defext(new_opt_file, "t3m"); opt_file = new_opt_file; } /* open the options file */ fp = osfoprt(opt_file, OSFTTEXT); if (fp == 0) { printf("error: unable to read option file \"%s\"\n", opt_file); err_cnt = 1; goto done; } /* * First, parse the file simply to count the arguments. Add in one * extra argument to make room for making a copy of argv[0]. */ new_argc = CTcCommandUtil::parse_opt_file(fp, 0, &opt_helper) + 1; /* rewind the file to parse it again */ osfseek(fp, 0, OSFSK_SET); /* allocate a new argument list */ new_argv = (char **)t3malloc(new_argc * sizeof(new_argv[0])); /* copy the program name from argv[0] */ new_argv[0] = argv[0]; /* * Re-read the file, saving the arguments. Note that we want to * start saving the arguments from the file at index 1 in the new * argv array, because we reserve argv[0] to hold the original * program name argument. */ CTcCommandUtil::parse_opt_file(fp, new_argv + 1, &opt_helper); /* done with the file - close it */ osfcls(fp); /* start over with the new argument vector */ argv = new_argv; argc = new_argc; /* * note that we've read the options file - we can only do this * once per run */ read_opt_file = TRUE; /* * Note the option file's path prefix. We'll assume that any * relative filename paths mentioned in the option file are meant * to be relative to the folder containing the option file itself. */ os_get_path_name(opt_file_path, sizeof(opt_file_path), opt_file); /* go add the new command options */ goto parse_options; } /* * Add the default object modules, if appropriate. Don't add the * default modules unless we're linking. */ if (add_def_mod && !compile_only && !pp_only) { /* build the full path to the "_main" library module */ char srcname[OSFNMAX]; os_build_full_path(srcname, sizeof(srcname), sys_lib_entry->get_path(), "_main"); /* add this as the first module of our compilation */ CTcMakeModule *mod = mk->add_module_first(srcname, 0, 0); /* set its original name, and note it's from the system library */ mod->set_orig_name("_main"); mod->set_from_syslib(); } /* * If the symbol file and object file directory paths weren't * explicitly set, set these to the same path used by the image file. * This will put all of the output files in the same place, if they * didn't explicitly tell us where to put the different kinds of * generated files. */ os_get_path_name(dirbuf, sizeof(dirbuf), mk->get_image_file()); if (!sym_dir_set) mk->set_symbol_dir(dirbuf); if (!obj_dir_set) mk->set_object_dir(dirbuf); /* if we encountered any errors parsing a library, we can't proceed */ if (lib_err) goto done; /* build the program */ mk->build(hostifc, &err_cnt, &warn_cnt, force_build, force_link, res_list, argv[0]); done: /* terminate the error subsystem */ CTcMain::tc_err_term(); /* delete our 'make' object */ delete mk; /* show the error and warning counts if non-zero */ if (err_cnt != 0 || warn_cnt != 0) printf("Errors: %d\n" "Warnings: %d\n", err_cnt, warn_cnt); /* if we read an options file, delete the memory used for the options */ if (read_opt_file) { int i; /* * delete the argv strings (except for argv[0], which came from * the caller) */ for (i = 1 ; i < argc ; ++i) opt_helper.free_opt_file_str(argv[i]); /* delete the argv vector itself */ t3free(argv); } /* if we have a string capture file, close it */ if (string_fp != 0) osfcls(string_fp); /* if we have an aassembly listing file, close it */ if (assembly_fp != 0) osfcls(assembly_fp); /* delete the host interface */ delete hostifc; /* delete the resource list */ delete res_list; /* show any unfreed memory (if we're in a debug build) */ t3_list_memory_blocks(0); /* * exit with an appropriate exit code, depending on whether we had * any errors or not */ return (err_cnt != 0 || (warnings_as_errors && warn_cnt != 0) ? OSEXFAIL : OSEXSUCC); } frobtads-1.2.3/tads3/vmhostsi.cpp0000644000175000001440000000315711713617265016121 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhostsi.cpp - stdio-based VM host application environment Function Notes Modified 08/06/99 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "resload.h" #include "vmhost.h" #include "vmhostsi.h" /* * initialize */ CVmHostIfcStdio::CVmHostIfcStdio(const char *argv0) { char buf[OSFNMAX]; /* remember the program's argv[0], in case we need it later */ argv0_ = lib_copy_str(argv0); /* * Create the resource loader for system resources (character mapping * files, etc) in the same directory as the executable. */ os_get_special_path(buf, sizeof(buf), argv0, OS_GSP_T3_RES); sys_res_loader_ = new CResLoader(buf); /* set the executable filename in the loader, if available */ if (os_get_exe_filename(buf, sizeof(buf), argv0)) sys_res_loader_->set_exe_filename(buf); /* * the default safety level allows reading and writing to the current * directory only */ io_safety_read_ = VM_IO_SAFETY_READWRITE_CUR; io_safety_write_ = VM_IO_SAFETY_READWRITE_CUR; net_client_safety_ = VM_NET_SAFETY_LOCALHOST; net_server_safety_ = VM_NET_SAFETY_LOCALHOST; } /* * delete */ CVmHostIfcStdio::~CVmHostIfcStdio() { /* delete our system resource loader */ delete sys_res_loader_; /* delete our saved argv[0] */ lib_free_str(argv0_); } frobtads-1.2.3/tads3/vmimport.h0000644000175000001440000001742311773604501015563 0ustar realncusers/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimport.h - T3 VM imported symbol list Function Defines the list of symbols to import from the image file. Each imported symbol must have an entry in this file. Notes Note that this file is NOT protected against multiple inclusion, because it is included in several different ways to generate different tables. Each includer must define the generator macros (VM_IMPORT, VM_IMP_xxx, etc) just before including this file. The macros are to be defined as needed by the includer to generate the appropriate table entries for that inclusion. Modified 02/23/01 MJRoberts - Creation */ /* if any of our macros are undefined, provide empty definitions now */ #ifndef VM_IMPORT_OBJ #define VM_IMPORT_OBJ(sym, member_name) #endif #ifndef VM_IMPORT_PROP #define VM_IMPORT_PROP(sym, member_name) #endif #ifndef VM_NOIMPORT_OBJ #define VM_NOIMPORT_OBJ(sym, member_name) #endif #ifndef VM_NOIMPORT_PROP #define VM_NOIMPORT_PROP(sym, member_name) #endif #ifndef VM_IMPORT_FUNC #define VM_IMPORT_FUNC(symm, member_name) #endif /* ------------------------------------------------------------------------ */ /* * This is the imported symbol table. Add symbols here as needed. Define * each imported symbol as follows: * * VM_IMPORT_OBJ("object_symbol_name", predef_member_name) - import an * object * * VM_IMPORT_PROP("prop_symbol_name", predef_member_name) - import a * property * * VM_NOIMPORT_OBJ("object_symbol_name", predef_member_name) - define a * named object, but do not generate an import for it; objects defined in * this way are created after load. Any object that the VM creates on its * own after load must be defined in this manner, so that the object can * be identified in saved state files and restored properly. * * VM_NOIMPORT_PROP("prop_symbol_name", predef_member_name) - define a * named property, but do not generate an import for it. */ /* the base class for VM run-time exceptions */ VM_IMPORT_OBJ("RuntimeError", rterr) /* * the property in which we store the exception message text for VM * run-time exceptions */ VM_IMPORT_PROP(VM_IMPORT_NAME_RTERRMSG, rterrmsg_prop) /* the constructor and destructor (finalizer) properties */ VM_IMPORT_PROP("Constructor", obj_construct) VM_IMPORT_PROP("Destructor", obj_destruct) /* the last property ID allocated by the compiler */ VM_IMPORT_PROP("LastProp", last_prop) /* the property used to invoke the function in an anonymous function object */ VM_IMPORT_PROP("ObjectCallProp", obj_call_prop) /* property invoked when an undefined property is invoked */ VM_IMPORT_PROP("propNotDefined", prop_not_defined_prop) /* object representing a frame in a stack trace (t3GetStackTrace) */ VM_IMPORT_OBJ("T3StackInfo", stack_info_cls) /* entrypoint function for restoring a saved state on startup */ VM_IMPORT_FUNC("mainRestore", main_restore_func) /* "length" method property */ VM_IMPORT_PROP("length", length_prop) /* * the objects used to represent constant strings and lists, respectively, * in method calls to such values */ VM_NOIMPORT_OBJ(VM_IMPORT_NAME_CONSTSTR, const_str_obj) VM_NOIMPORT_OBJ(VM_IMPORT_NAME_CONSTLST, const_lst_obj) /* * the array object that we use to keep track of the last allocated * property ID (we keep this in an object so that property ID allocations * can easily participate in the undo mechanism and are easily saved and * restored) */ VM_NOIMPORT_OBJ(VM_IMPORT_NAME_LASTPROPOBJ, last_prop_obj) /* storage server error */ VM_IMPORT_OBJ("StorageServerError", storage_server_error) /* String class imports */ VM_IMPORT_PROP("String.specialsToHtml.flags", string_sth_flags) VM_IMPORT_PROP("String.specialsToHtml.tag", string_sth_tag) /* * the properties that the GrammarProd intrinsic class uses to indicate in * a match tree the first and last token index of each match */ VM_IMPORT_PROP("GrammarProd.firstTokenIndex", gramprod_first_tok) VM_IMPORT_PROP("GrammarProd.lastTokenIndex", gramprod_last_tok) VM_IMPORT_PROP("GrammarProd.tokenList", gramprod_token_list) VM_IMPORT_PROP("GrammarProd.tokenMatchList", gramprod_token_match_list) VM_IMPORT_PROP("GrammarProd.grammarTag", gramprod_tag) VM_IMPORT_PROP("GrammarProd.miscVocab", misc_vocab) VM_IMPORT_PROP("GrammarProd.altProps", gramprod_alt_props) /* * classes that GrammarProd.getGrammarInfo() instantiates to store the * production description */ VM_IMPORT_OBJ("GrammarProd.GrammarAltInfo", gramprod_gram_alt_info) VM_IMPORT_OBJ("GrammarProd.GrammarAltTokInfo", gramprod_gram_alt_tok_info) /* * for the CharacterSet intrinsic class, the exception class for unknown * local character mappings */ VM_IMPORT_OBJ("CharacterSet.UnknownCharSetException", charset_unknown_exc) /* * exceptions for the File intrinsic class */ VM_IMPORT_OBJ("File.FileNotFoundException", file_not_found_exc) VM_IMPORT_OBJ("File.FileCreationException", file_creation_exc) VM_IMPORT_OBJ("File.FileOpenException", file_open_exc) VM_IMPORT_OBJ("File.FileIOException", file_io_exc) VM_IMPORT_OBJ("File.FileSyncException", file_sync_exc) VM_IMPORT_OBJ("File.FileClosedException", file_closed_exc) VM_IMPORT_OBJ("File.FileModeException", file_mode_exc) VM_IMPORT_OBJ("File.FileSafetyException", file_safety_exc) VM_IMPORT_PROP("FileSpec.getFilename", filespec_getFilename) VM_IMPORT_PROP("FileSpec.closeFile", filespec_closeFile) VM_IMPORT_OBJ("File.FileInfo", file_info) /* exceptions for the DynamicFunc intrinsic class */ VM_IMPORT_OBJ("DynamicFunc.CompilerException", compiler_exc) /* properties for comparators */ VM_IMPORT_PROP("IfcComparator.calcHash", calc_hash_prop) VM_IMPORT_PROP("IfcComparator.matchValues", match_values_prop) /* properties for overloaded operators */ VM_IMPORT_PROP("operator +", operator_add) VM_IMPORT_PROP("operator -", operator_sub) VM_IMPORT_PROP("operator *", operator_mul) VM_IMPORT_PROP("operator /", operator_div) VM_IMPORT_PROP("operator %", operator_mod) VM_IMPORT_PROP("operator ^", operator_xor) VM_IMPORT_PROP("operator <<", operator_shl) VM_IMPORT_PROP("operator >>", operator_ashr) VM_IMPORT_PROP("operator >>>", operator_lshr) VM_IMPORT_PROP("operator ~", operator_bit_not) VM_IMPORT_PROP("operator &", operator_bit_and) VM_IMPORT_PROP("operator |", operator_bit_or) VM_IMPORT_PROP("operator negate", operator_neg) VM_IMPORT_PROP("operator []", operator_idx) VM_IMPORT_PROP("operator []=", operator_setidx) /* objects and properties for the TADS Networking package */ VM_IMPORT_OBJ("TadsNet.NetException", net_exception) VM_IMPORT_OBJ("TadsNet.SocketDisconnectException", net_reset_exception) VM_IMPORT_OBJ("TadsNet.NetSafetyException", net_safety_exception) VM_IMPORT_OBJ("TadsNet.NetEvent", net_event) VM_IMPORT_OBJ("TadsNet.NetRequestEvent", net_request_event) VM_IMPORT_OBJ("TadsNet.NetTimeoutEvent", net_timeout_event) VM_IMPORT_OBJ("TadsNet.NetReplyEvent", net_reply_event) VM_IMPORT_OBJ("TadsNet.NetReplyDoneEvent", net_reply_done_event) VM_IMPORT_OBJ("TadsNet.FileUpload", file_upload_cl) VM_IMPORT_PROP("TadsNet.FileUpload.file", file_upload_fileObj) VM_IMPORT_PROP("TadsNet.FileUpload.contentType", file_upload_contentType) VM_IMPORT_PROP("TadsNet.FileUpload.filename", file_upload_filename) /* reflection services */ VM_IMPORT_OBJ("reflection.reflectionServices", reflection_services) VM_IMPORT_PROP("reflection.valToSymbol", reflection_valToSymbol) /* general-purpose object-to-string formatting */ VM_IMPORT_PROP("objToString", objToString) /* * now that we've built the table, undefine the macros used to build it, * so that future includers can redefine the macros differently */ #undef VM_IMPORT_OBJ #undef VM_IMPORT_PROP #undef VM_NOIMPORT_OBJ #undef VM_NOIMPORT_PROP #undef VM_IMPORT_FUNC frobtads-1.2.3/tads3/resnoexe.cpp0000644000175000001440000000224607627507276016104 0ustar realncusers/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name resnoexe.cpp - null implementation of executable file resource loader Function Provides a null implementation of CResLoader::open_exe_res(). Programs that do not want to be able to load resources from the executable file can link this module (rather than resldexe.cpp); this avoids dragging in additional osifc routines that are superfluous when executable resource loading is not required. Notes Modified 11/03/01 MJRoberts - Creation */ #include "resload.h" /* * Try loading a resource from the executable file */ osfildef *CResLoader::open_exe_res(const char *respath, const char *restype) { /* this version is a null implementation - return failure */ return 0; } /* * load a resource from a library */ osfildef *CResLoader::open_lib_res(const char *libfile, const char *respath) { /* this version is a null implementation - return failure */ return 0; } frobtads-1.2.3/tads3/vmlog.h0000644000175000001440000000205711665777150015042 0ustar realncusers/* $Header$ */ /* Copyright (c) 2011 by Michael J. Roberts. All Rights Reserved. */ /* Name vmlog.h - system log file Function Functions for writing messages to the system log file Notes Strings passed to vm_log_xxx functions are expected to be in the local file system character set, unless otherwise noted per function. These functions generally just write the bytes of the strings as given, without any character set mapping, so it's up to the caller to map to the file character set if necessary. Plain ASCII strings don't need mapping, since virtually all modern machines use character sets that include ASCII as a subset - this includes UTF-8, ISO-8859-X, Win CP12xx, etc, but excludes 16-bit Unicode mappings (UCS-2) and EBCDIC. Modified 12/01/11 MJRoberts - Creation */ #ifndef VMLOG_H #define VMLOG_H #include "t3std.h" #include "vmglob.h" /* log a message with sprintf-style formatting */ void vm_log_fmt(VMG_ const char *fmt, ...); /* log a message string */ void vm_log(VMG_ const char *str, size_t len); #endif /* VMLOG_H */ frobtads-1.2.3/tads3/vmnetfil.h0000644000175000001440000003711012006323527015521 0ustar realncusers/* $Header$ */ /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetfil.h - network file interface Function This module was added in 3.1 to manage access to files stored on a network storage server when the game is running in web server mode. In this mode, files are not stored on the same machine as the game server, nor on the user's client PC, but on a third machine known as the storage server. The purpose of separating the game (execution) server and the storage server is that it allows execution servers to be interchangeable, as they don't store any state. This lets us launch a game on any available server to distribute execution load. The execution location is transparent to the user since all execution servers access the same storage pool. Access to remote files is handled by local caching. When we open a file for reading, we download a temporary copy from the storage server via HTTP, and perform the local file operations on the temp copy. If the file is opened for writing, we upload our temporary copy to the storage server on close, again via HTTP. The interface is implemented in two versions. The Network version senses at run-time whether or not we're in web server mode; if so, it does the HTTP GET/PUT operations to download and upload the temp files, and if not it just opens files in the local file system. The Local version can be used in builds that don't include the networking functionality at all; this version unconditionally maps to local files, and is a very thin layer over the regular file handling. Use the TADSNET macro to control the build configuration. If the macro is defined, the network run-time checking is included in the build. If not, the local-only version is selected, in which case the CVmNetFile object is basically a no-op that just passes through the local filename. Notes Modified 09/08/10 MJRoberts - Creation */ #ifndef VMNETFIL_H #define VMNETFIL_H #include "t3std.h" #include "vmtype.h" #include "os.h" /* ------------------------------------------------------------------------ */ /* * File modes for CVmNetFile::open() */ #define NETF_READ 0x0001 /* open with read access */ #define NETF_WRITE 0x0002 /* open with write access */ #define NETF_CREATE 0x0004 /* create the file if it doesn't exist */ #define NETF_TRUNC 0x0008 /* discard existing contents if file exists */ #define NETF_DELETE 0x0010 /* no content access; delete the file */ /* composite flags for generic new file creation (create or replace) */ #define NETF_NEW (NETF_WRITE | NETF_CREATE | NETF_TRUNC) /* ------------------------------------------------------------------------ */ /* * Network file object. * * The protocol for operating on a user file is relatively simple to use: * * 1. Prepare the network file by calling CVmNetFile::open(). This returns * a CVmNetFile object that you hang onto for the duration of the file * manipulation - call this 'nfile'. * * 2. Use any local file system API (e.g., any of the osfopxxx() functions) * to open 'nfile->lclfname'. * * 3. Use the local file system APIs (osfread(), osfwrite(), etc) to * read/write the local file. * * 4. Close the local file (with osfcls(), say). * * 5. Close the network file by calling 'nfile->close()'. This has the * side effect of destroying 'nfile', so you don't have to manually * 'delete' it or otherwise dispose it. * * To retrofit this into existing osifc file management code, all that's * required is to bracket the existing code with the CVmNetFile::open() and * close() calls. Note that open() and close() can throw errors, so * protect these calls with err_try if you have to do any resource cleanup * if an error is thrown. */ class CVmNetFile { public: /* * Are we in network storage server mode? This returns true if we have * a storage server, false if we're storing files in the local file * system. */ static int is_net_mode(VMG0_) #ifdef TADSNET ; #else /* local-only implementation - we definitely use local storage only */ { return FALSE; } #endif /* * Open a file. In local mode, this simply returns a CVmNetFile object * with the given name in the lclfname field, so the caller will * directly access the local file. For a network file, if the existing * contents are needed, this downloads a copy of the network file to a * local temporary file, otherwise it simply generates a name for a new * local temporary file; in any case, it returns the temp file name in * the lclfname field. * * If 'sfid' is non-zero, it's a CVmObjFile::SFID_xxx value specifying * the special file ID. Use zero for an ordinary file. * * This throws an error on any download failure. */ static CVmNetFile *open(VMG_ const char *fname, int sfid, int mode, os_filetype_t typ, const char *mime_type) #ifdef TADSNET ; #else { /* * Local implementation - access the local file directly. We don't * care about MIME types for the local implementation, since our * local file system interfaces don't use them. */ return new CVmNetFile(fname, sfid, 0, mode, typ, 0); } #endif /* * Open a file, based on an argument value from the byte-code program. * This accepts string filenames, TemporaryFile (CVmObjTemporaryFile) * objects, or TadsObject objects providing the file spec interface. */ static CVmNetFile *open( VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type); /* * Open a file in local mode. This is essentially a no-op that allows * a caller to explicitly manipulate a local file through code that's * written to support network files, without adding a special case to * that code. This routine creates a local file descriptor regardless * of the interpreter mode. */ static CVmNetFile *open_local(VMG_ const char *fname, int sfid, int mode, os_filetype_t typ) { /* return an explicitly local file descriptor */ return new CVmNetFile(fname, sfid, 0, mode, typ, 0); } /* rename a file */ void rename_to_local(VMG_ CVmNetFile *newname); void rename_to(VMG_ CVmNetFile *newname) #ifdef TADSNET ; #else { rename_to_local(vmg_ newname); } #endif /* create a directory as named in the file object */ void mkdir_local(VMG_ int create_parents); void mkdir(VMG_ int create_parents) #ifdef TADSNET ; #else { mkdir_local(vmg_ create_parents); } #endif /* remove the directory named in the file object */ void rmdir_local(VMG_ int remove_contents); void rmdir(VMG_ int remove_contents) #ifdef TADSNET ; #else { rmdir_local(vmg_ remove_contents); } #endif /* * Is this a network file? This returns true if the file is stored on * the network, false if it's in the local file system. */ int is_net_file() const { /* it's a network file if it has a server-side filename */ return srvfname != 0; } /* * Get the file mode, per osfmode(). For a network file, the only * possible mode is "file", since we don't support directories or other * non-file types. On success, fills in *mode with a bitwise * combination of OSFMODE_xxx flags and returns true; returns false on * failure. */ int get_file_mode(VMG_ unsigned long *mode, unsigned long *attrs, int follow_links) #ifdef TADSNET ; #else { return osfmode(lclfname, follow_links, mode, attrs); } #endif /* * Get the file stat() information, per os_file_stat(). This isn't * supported for network files. */ int get_file_stat(VMG_ os_file_stat_t *stat, int follow_links) #ifdef TADSNET ; #else { return os_file_stat(lclfname, follow_links, stat); } #endif /* * Resolve a symbolic link. If this file is a symbolic link, this * fills in 'target' with the target path and returns true. Otherwise * returns false. */ int resolve_symlink(VMG_ char *target, size_t target_size) #ifdef TADSNET ; #else { return os_resolve_symlink(lclfname, target, target_size); } #endif /* * Get a listing of files in the directory. Creates a list object, and * fills in the list of FileName (CVmObjFileName) objects giving the * names of the files in the directory. Returns true on success, false * on failure. * * 'nominal_path' is an optional string to use as the displayed * directory path name for the constructed FileName objects. If this * is null, we'll use our actual internal local file path. The nominal * path can be used to preserve the original relative path name, which * might be desirable when the netfile object is resolved to an * absolute path based on a working directory. */ int readdir(VMG_ const char *nominal_path, vm_val_t *retval) #ifdef TADSNET ; #else { return readdir_local(vmg_ nominal_path, retval, 0, 0, 0); } #endif /* * Enumerate files in the directory through a callback function. */ int readdir_cb(VMG_ const char *nominal_path, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive) #ifdef TADSNET ; #else { return readdir_local(vmg_ nominal_path, 0, rc, cb, recursive); } #endif /* read a local directory */ int readdir_local(VMG_ const char *nominal_path, vm_val_t *retval, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive); /* * Does the given file exist? Returns true if so, nil if not. */ static int exists(VMG_ const char *fname, int sfid) #ifdef TADSNET ; #else { /* * local-only mode: check the local file system; it exists if * osfacc() returns success (zero) */ return !osfacc(fname); } #endif /* * Test to see if we can write to the given file. We attempt to open * the file for writing (retaining any existing data in the file, or * creating a new file if it doesn't exist). If we succeed, we close * the file (and delete it if it didn't exist), and return TRUE. If we * fail, return FALSE. */ static int can_write(VMG_ const char *fname, int sfid) #ifdef TADSNET ; #else { return can_write_local(fname); } #endif /* * Close the file. Call this after closing the local file handle. For * a network file opened with write access, this will copy the local * temp copy back to the storage server, then delete the local temp * copy. For any network file, deletes the local temp file. For all * files, deletes the CVmNetFile object. * * Throws an error on upload failure. The CVmNetFile object is always * reliably deleted, whether or not an error is thrown. */ void close(VMG0_) #ifdef TADSNET ; #else { /* * Local file: * * - if we opened it in "delete" mode, delete it *. - if we opened it in "create" mode, set the file's OS type code */ int err = 0; if ((mode & NETF_DELETE) != 0) { if (osfdel(lclfname)) err = VMERR_DELETE_FILE; } else if ((mode & NETF_CREATE) != 0) os_settype(lclfname, typ); /* delete self */ delete this; /* if an error occurred, throw the error */ if (err != 0) err_throw(err); } #endif /* * Abandon the network file. Call this if the local file manipulation * fails, and the caller decides it doesn't want to save any changes * back to the network after all. This deletes any local temp file, * and frees the CVmNetFile object. Call this only after closing the * local file handle. */ void abandon(VMG0_) { #ifdef TADSNET /* delete the temp file */ if (srvfname != 0) osfdel(lclfname); #endif /* free self */ delete this; } /* * Mark references for garbage collection. This marks our reference to * the filespec object, if we have one. */ void mark_refs(VMG_ uint state); /* * The local filename. After obtaining this object from vmnet_fopen(), * the caller can use any osfopxxx() function to open the file. If * we're using the network storage server, this will name a temp file * in the system temp directory; if the file isn't on the storage * server, this is simply the local file name. */ char *lclfname; /* special file ID */ int sfid; /* * The server filename. When we're operating on a storage server file, * this is the name of the file on the server; otherwise it's null. */ char *srvfname; /* * The file spec object. This can be a TemporaryFile object, or a * TadsObject object with a file spec interface. If the file was * opened based on a string filename, this is nil. */ vm_obj_id_t filespec; /* the file mode */ int mode; /* is this a temporary file? */ int is_temp; /* the OS file type */ os_filetype_t typ; /* MIME type of the file */ char *mime_type; protected: CVmNetFile(const char *lclfname, int sfid, const char *srvfname, int mode, os_filetype_t typ, const char *mime_type) { /* save the filenames, mode, and type */ this->lclfname = lib_copy_str(lclfname); this->srvfname = lib_copy_str(srvfname); this->sfid = sfid; this->mode = mode; this->typ = typ; this->mime_type = lib_copy_str(mime_type); this->filespec = VM_INVALID_OBJ; this->is_temp = FALSE; } ~CVmNetFile() { lib_free_str(lclfname); lib_free_str(srvfname); lib_free_str(mime_type); } /* build the full server-side filename for a given file */ static const char *build_server_filename( VMG_ char *dst, size_t dstsiz, const char *fname, int sfid); /* check to see if we can create/write to a local file */ static int can_write_local(const char *fname) { /* note whether or not the file exists already */ int existed = !osfacc(fname); /* * try opening for read/write, keeping the existing contents or * creating a new file if it doesn't exist */ osfildef *fp = osfoprwb(fname, OSFTBIN); if (fp != 0) { /* * Successfully opened the file, so we evidently can write it. * We don't actually need to do anything with the file other * than check that we could open it, so close it. */ osfcls(fp); /* if the file didn't exist before, restore the status quo ante */ if (!existed) osfdel(fname); /* indicate success */ return TRUE; } else { /* couldn't open the file - return failure */ return FALSE; } } }; #endif /* VMNETFIL_H */ frobtads-1.2.3/tads3/vmmeta.h0000644000175000001440000003407711745521623015205 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMMETA.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmeta.h - metaclass dependency table Function The metaclass dependency table establishes the correspondence between metaclass index numbers and metaclasses. The load image file contains a list of the metaclasses that the program requires, identifying each required metaclass with its universally unique identifier string. At load time, we scan this list in the load image and create a dependency table. Notes Modified 12/01/98 MJRoberts - Creation */ #ifndef VMMETA_H #define VMMETA_H #include #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "tcprstyp.h" /* ------------------------------------------------------------------------ */ /* * Metaclass table entry. This contains information on the metaclass at * one index in the table. */ struct vm_meta_entry_t { /* the class descriptor object for the metaclass */ class CVmMetaclass *meta_; /* * name of metaclass as read from image file (this is important for * rewriting an image file during preinit, since it ensures that we * store only the version number required by the compiled image * file, not the version number actually implemented in the preinit * interpreter) */ char *image_meta_name_; /* * the IntrinsicClass object (a CVmObjClass object) that represents * this class */ vm_obj_id_t class_obj_; /* * Property translation list. The translation list is an array of * internal function numbers, indexed by property ID. However, not * all property ID's are necessarily represented in the index - * instead, the index contains entries only from a minimum property * ID, and only contains a specified number of entries. So, given * property ID 'prop_id', we find the internal function number for * the property at index (prop_id - min_prop_id) in the table. * * We use an unsigned short (16 bits) for each internal function * number - this limits the number of functions per metaclass to * 65535, which should be more than adequate while keeping memory * usage within reason. */ vm_prop_id_t min_prop_; size_t prop_xlat_cnt_; unsigned short *prop_xlat_; /* * Function table. This is the reverse of the property translation * list: this table gives the property ID for each of the metaclass * functions. The first entry is the property of function index 1, * the second entry is function index 2, and so on. */ vm_prop_id_t *func_xlat_; size_t func_xlat_cnt_; /* relese storage */ void release_mem() { /* if this entry has a property table, delete it */ if (prop_xlat_ != 0) { /* free it */ t3free(prop_xlat_); /* forget the pointer */ prop_xlat_ = 0; } /* if this entry has a function translation table, delete it */ if (func_xlat_ != 0) { /* free it */ t3free(func_xlat_); /* forget the pointer */ func_xlat_ = 0; } /* relesae our stored name */ lib_free_str(image_meta_name_); } /* store the metaclass name as loaded from the image file */ void set_meta_name(const char *nm) { /* allocate space and store the name */ image_meta_name_ = lib_copy_str(nm); } /* * Add a property to the table, given the property ID and the * 1-based index of the corresponding function in the metaclass's * internal function table. */ void add_prop_xlat(vm_prop_id_t prop, ushort func_idx) { /* * Add the translation entry from property ID to function index * - note that we store this as a 1-based function table index * value, because we reserve function index 0 to indicate that * the property is invalid and has no function translation */ prop_xlat_[prop - min_prop_] = func_idx; /* * Add the translation entry from function index to property ID. * Note that we must adjust the 1-based function index down one * to get the index into our internal array. */ func_xlat_[func_idx - 1] = prop; } /* * Get the translation for a given property ID. This returns zero * if the property does not map to a valid function index for the * metaclass, or the 1-based index of the function in the * metaclass's "vtable" if the property is defined. */ unsigned short xlat_prop(vm_prop_id_t prop) const { /* * Check to see if the property is represented in the table. If * it's not within the range of properties in the table, there * is no translation for the property. */ if (prop < min_prop_ || prop >= min_prop_ + prop_xlat_cnt_) { /* the property isn't in the table, so it has no translation */ return 0; } /* return the function index from the table */ return prop_xlat_[prop - min_prop_]; } /* * Translate a function index to a property ID. The function index * is given as a 1-based index, for consistency with the return * value from xlat_prop(). */ vm_prop_id_t xlat_func(unsigned short func_idx) const { /* * if it's zero, or it's greater than the number of functions in * our table, it has no property translation */ if (func_idx == 0 || func_idx > func_xlat_cnt_) return VM_INVALID_PROP; /* * return the entry from our table for the given index, adjusted * to a 1-based index value */ return func_xlat_[func_idx - 1]; } }; /* ------------------------------------------------------------------------ */ /* * The metaclass table. One global instance of this table is generally * created when the image file is loaded. */ class CVmMetaTable { public: /* * Create a table with a given number of initial entries. The table * may be expanded in the future if necessary, but if the caller can * predict the maximum number of entries required, we can * preallocate the table at its final size and thus avoid the * overhead and memory fragmentation of expanding the table. */ CVmMetaTable(size_t init_entries); /* delete the table */ ~CVmMetaTable(); /* clear all existing entries from the table */ void clear(); /* * Build a translation table from runtime metaclass index to the * compiler's internal TC_META_xxx scheme. The table is simply an * array of TC_META_xxx values indexed by metaclass index number. The * caller is responsible for freeing the returned memory via t3free() * when done with it. */ static tc_metaclass_t *build_runtime_to_compiler_id_table(VMG0_); /* * Add an entry to the table, given the metaclass identifier (a * string giving the universally unique name for the metaclass). * Fills in the next available slot. Throws an error if the * metaclass is not present in the system. * * If min_prop and max_prop are both VM_INVALID_PROP, the metaclass * has no properties in the translation table. */ void add_entry(const char *metaclass_id, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* add an entry to the table given the registration table index */ void add_entry(const char *metaclass_id, uint idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* * Add an entry to the table given the registration table index, but * only if the metaclass at this index isn't already defined in the * dependency table; if the metaclass is already in the dependency * table, this function has no effect. */ void add_entry_if_new(uint reg_table_idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop); /* * Get the dependency table index for a metaclass, given the * registration table index. If the metaclass is not present in the * metaclass table, we'll return -1. */ int get_dependency_index(uint reg_table_idx) const { return reverse_map_[reg_table_idx]; } /* * Get the entry for a given external metaclass ID. This returns the * table entry if the metaclass is loaded, null if not. If the name is * given with a "/version" suffix, we'll return null if the loaded * version is older than the specified version. */ vm_meta_entry_t *get_entry_by_id(const char *id) const; /* * get the depencency table entry for a metaclass, given the * registration table index */ vm_meta_entry_t *get_entry_from_reg(int reg_idx) { /* translate the registration table index into a dependency index */ int dep_idx = get_dependency_index(reg_idx); /* * if we have a valid dependency index, return it; otherwise, * return null */ if (dep_idx >= 0) return &table_[dep_idx]; else return 0; } /* * Given a registration table index for a metaclass and a property * ID, retrieve the metaclass function vector index for the property * according to the image file's property mapping for the metaclass. * Returns zero if the property is not in the mapping, 1 for the * first function in the vector, 2 for the second function, and so * on. * * Note that we use the registration table index, NOT the dependency * index, because this function is provided for use by the metaclass * itself, which only has convenient access to its registration * index. */ int prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop) { /* get the entry for the registration table index */ vm_meta_entry_t *entry = get_entry_from_reg(reg_table_idx); /* * if there's no entry for this registration index, there's * obviously no metaclass function mapping for the property */ if (entry == 0) return 0; /* return the property translation */ return entry->xlat_prop(prop); } /* get the entry at a given dependency table index */ vm_meta_entry_t *get_entry(int idx) { return &table_[idx]; } /* get the total number of entries in the table */ size_t get_count() const { return count_; } /* * Invoke the VM-stack-based constructor for the metaclass at the * given index to create a new instance of that metaclass. Throws * an error if no such entry is defined. Returns the object ID of * the constructed object. */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint idx, uint argc); /* invoke a static property of a metaclass */ int call_static_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint idx, uint *argc, vm_prop_id_t prop); /* * Create an object with the given ID and load the object from the * image file. */ void create_from_image(VMG_ uint idx, vm_obj_id_t id, const char *ptr, size_t siz); /* * create an object in preparation for loading the object from a * saved state file */ void create_for_restore(VMG_ uint idx, vm_obj_id_t id); /* * write the dependency table to a file, for later reloading with * read_from_file() */ void write_to_file(class CVmFile *fp); /* * Read the dependency table from a file, as previously written by * write_to_file(). Returns zero on success, or an error code on * failure. We will entirely clear the current table before * loading, so the new table will replace the existing table. */ int read_from_file(class CVmFile *fp); /* rebuild the image file (for use after preinitialization) */ void rebuild_image(class CVmImageWriter *writer); /* * Create an IntrinsicClass object for each metaclass that doesn't * already have an IntrinsicClass object. This ensures that every * intrinsic class in use has an associated IntrinsicClass instance, * even if the compiler didn't generate one. */ void create_intrinsic_class_instances(VMG0_); /* forget IntrinsicClass objects we created at startup */ void forget_intrinsic_class_instances(VMG0_); private: /* * Ensure we have space for a given number of entries, allocating * more if necessary. If we must allocate more space, we'll * increase the current allocation size by at least the given * increment (more if necessary to bring it up to the required * size). */ void ensure_space(size_t entries, size_t increment); /* the table array */ vm_meta_entry_t *table_; /* * reverse dependency map - each entry in this table is the * dependency table index of the metaclass at the given registration * table index: * * reserve_map[registration_table_index] = dependency_table_index * * This information lets us determine if a given metaclass is in the * current dependency table at all, and if so what it's index is. * We store -1 in each entry that doesn't appear in the dependency * table. */ int *reverse_map_; /* number of entries defined in the table */ size_t count_; /* number of entries allocated for the table */ size_t alloc_; }; #endif /* VMMETA_H */ frobtads-1.2.3/tads3/vmrunsym.h0000644000175000001440000001242211332562464015602 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmrunsym.h - run-time global symbol table Function Defines a symbol table structure that allows us to convey the global symbols from the compiler or image loader to the interpreter. When the compiler runs pre-initialization, or when the image loader finds debug symbols in a program, the interpreter builds a LookupTable object containing the global symbol table. This provides a "reflection" mechanism that lets the running program inspect its own symbol table. Because we have two very different ways of getting the symbol information -- from the compiler, or from the image file's debug records -- we define this class as an intermediary that provides a common mechanism for conveying the information to the interpreter. Note that, when the information is coming from the compiler, there's usually not a debug symbol table in the image file, because the compiler usually only runs pre-initialization on programs compiled for release (i.e., with no debug information), so we can't count on debug records in the image file as a common conveyance mechanism. This is a very simple linked list storage class, because we have no need to search this symbol table. The only things we do with this type of symbol table are to load it with all of the symbols from the compiler or image file's debug records, and then enumerate all of our symbols to build a run-time LookupTable. One final note: we could conceivably avoid having this other data representation by having the compiler build a LookupTable directly and storing it in the image file. However, this would put the LookupTable into the root set, so it could never be deleted. By building the LookupTable dynamically during pre-initialization, the table will be automatically garbage collected before the image file is finalized if the program doesn't retain a reference to it in a location accessible from the root set. This allows the program to control the presence of this extra information in a very natural way. Notes Modified 02/17/01 MJRoberts - Creation */ #ifndef VMRUNSYM_H #define VMRUNSYM_H #include "vmtype.h" class CVmRuntimeSymbols { public: /* construct */ CVmRuntimeSymbols() { /* nothing in the list yet - empty the list and clear the count */ head_ = tail_ = 0; cnt_ = 0; } /* delete */ ~CVmRuntimeSymbols(); /* add a symbol */ void add_sym(const char *sym, size_t len, const vm_val_t *val); /* add a macro definition */ struct vm_runtime_sym *add_macro(const char *sym, size_t len, size_t explen, unsigned int flags, int argc, size_t arglen); /* get the first symbol */ struct vm_runtime_sym *get_head() const { return head_; } /* get the number of symbols */ size_t get_sym_count() const { return cnt_; } /* find an object name - returns null if the object isn't found */ const char *find_obj_name(VMG_ vm_obj_id_t obj, size_t *name_len) const { vm_val_t val; /* set up an object value */ val.set_obj(obj); /* find the value */ return find_val_name(vmg_ &val, name_len); } /* find a property name - returns null if not found */ const char *find_prop_name(VMG_ vm_prop_id_t prop, size_t *name_len) const { vm_val_t val; /* set up a property ID value */ val.set_propid(prop); /* find the value */ return find_val_name(vmg_ &val, name_len); } /* * Find the name for a value. Fills in the length pointer with the * length of the return string, which is not null-terminated. * Returns null if no such value is present in the table. */ const char *find_val_name(VMG_ const vm_val_t *val, size_t *name_len) const; protected: /* head and tail of symbol list */ struct vm_runtime_sym *head_; struct vm_runtime_sym *tail_; /* number of symbols in the list */ size_t cnt_; }; /* * A Symbol */ struct vm_runtime_sym { /* next symbol in the list */ vm_runtime_sym *nxt; /* the symbol name */ char *sym; /* the length of the name */ size_t len; /* the value of the symbol */ vm_val_t val; /* for a macro, the flags from the MACR record */ unsigned int macro_flags; /* for a macro, the expansion of the macro */ char *macro_expansion; size_t macro_exp_len; /* for a macro, the number of formal parameters */ int macro_argc; /* for a macro, the argument list */ char **macro_args; /* * Commit storage for a macro argument. Arguments MUST be stored * sequentially, starting from number 0. The caller must * null-terminate each name, AND must include the null byte in the * 'len' value. */ void commit_macro_arg(int i, size_t len) { /* if this isn't the last argument, set the next allocation pointer */ if (i + 1 < macro_argc) macro_args[i + 1] = macro_args[i] + len; } }; #endif /* VMRUNSYM_H */ frobtads-1.2.3/tads3/vmmcreg.cpp0000644000175000001440000000537407627507522015713 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMMCREG.CPP,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmcreg.cpp - Metaclass Registry Function The registry provides a static constant global table of the metaclasses that are linked in to this VM implementation. This file is dependent on the host application environment configuration. This particular file builds a table for the base set of T3 VM metaclasses. Some host environments may define an extended set of metaclasses; if you're building a host system with added metaclasses, do the following: 1. Make a copy of this file (DO NOT MODIFY THE ORIGINAL). 2. Remove this file (and/or files mechanically derived from this file, such as the compiled object file) from your makefile, and add your modified version of this file (and/or attendant derived files) instead. 3. At EACH line marked with a "!!!" comment below, add a #include for each of your added metaclass header files. (You don't need to include the core metaclasses, such as list and string, since you should still include "vmmccore.h", which takes care of including the core files.) Notes Modified 12/01/98 MJRoberts - Creation */ #include "vmmcreg.h" // !!! INCLUDE HOST-SPECIFIC METACLASS HEADERS HERE (FIRST OF TWO) /* the global registry table */ vm_meta_reg_t G_meta_reg_table[] = { /* * enable table-building mode, and include the core metaclasses * common to all T3 VM implementations */ #define VMMCCORE_BUILD_TABLE #include "vmmccore.h" // !!! INCLUDE HOST-SPECIFIC METACLASS HEADERS HERE (SECOND OF TWO) /* * last entry in the table (this is only required because of the * trailing comma in the registration macro) */ { 0 } }; /* ------------------------------------------------------------------------ */ /* * Register the metaclasses */ void vm_register_metaclasses() { uint i; vm_meta_reg_t *entry; /* * run through the metaclass table and tell each metaclass its * registration table index */ for (i = 0, entry = G_meta_reg_table ; entry->meta != 0 ; ++i, ++entry) { /* * Call this entry's class method to set its registration index. * This is static information that never changes throughout the * program's execution - we simply establish the registration * table index for each metaclass once upon initialization. */ (*entry->meta)->set_metaclass_reg_index(i); } } frobtads-1.2.3/tads3/samples/0000755000175000001440000000000012145614112015163 5ustar realncusersfrobtads-1.2.3/tads3/samples/gramdisp.t0000644000175000001440000002112710272415376017173 0ustar realncusers#charset "us-ascii" /* * Grammar displayer. This demonstrates how to use * GrammarProd.getGrammarInfo() by displaying the information on a given * production object in human-readable format. * * You can add this into any adv3-based game (make sure you have the * basic library's reflection module - reflect.t - included in the * build). We'll add a new verb to the game - "gramprod " - that * lets you display a grammar production's internal information * interactively. */ #include #include #include "adv3.h" #include "en_us.h" /* * display the grammar tree */ DefineLiteralAction(GramProd) execAction() { /* look up the production by name */ local prod = reflectionServices.symtab_[getLiteral()]; /* check to see if we found it */ if (prod != nil && prod.ofKind(GrammarProd)) { /* found it - display the given production */ displayProd(prod); } else { /* didn't find it */ "Sorry, there's no such production as <>. "; } } /* * Display the given production in human-readable format. This * contains the same information as a 'grammar' statement, but adds * some annotations to indicate the types of the symbols displayed. */ displayProd(prod) { local lastSubName; /* show the name of the overall production */ "<>:\n"; /* get the grammar info - this is a list of GrammarAltInfo objects */ local alts = prod.getGrammarInfo(); /* we don't have a last-sub-name yet */ lastSubName = nil; /* display each alternative */ foreach (local alt in alts) { /* * parse the sub-name out of the match object - the name will * be in the format prod(subname), so pull out the part in * the parentheses */ rexMatch('.+(.+)', reflectionServices.valToSymbol(alt.gramMatchObj)); local subName = rexGroup(1)[3]; /* * if this matches the previous rule's sub-name, just show it * as another alternative for the same named rule; otherwise, * show the new named rule */ if (subName == lastSubName) "\t\t| "; else "\t(<>):\n\t\t"; /* remember the sub-name for next time */ lastSubName = subName; /* check for badness */ if (alt.gramBadness != 0) "[badness <>] "; /* show the token list */ foreach (local tok in alt.gramTokens) { local nm = reflectionServices.valToSymbol(tok.gramTokenInfo); /* show the token information */ switch (tok.gramTokenType) { case GramTokTypeProd: "(prod:<>)"; break; case GramTokTypeSpeech: "(pos:<>)"; break; case GramTokTypeNSpeech: "(pos:"; for (local i = 1 ; i <= tok.gramTokenInfo.length() ; ++i) { if (i != 1) ","; say(reflectionServices.valToSymbol( tok.gramTokenInfo[i])); } ")"; break; case GramTokTypeLiteral: "'<>'"; break; case GramTokTypeTokEnum: "(tok:<>)"; break; case GramTokTypeStar: "*"; break; default: "(unknown)"; break; } /* add the target property, if present */ if (tok.gramTargetProp != nil) "-><< reflectionServices.valToSymbol(tok.gramTargetProp)>>"; /* add a space before the next one */ " "; } /* finish the line */ "\n"; } } /* * Display the given production as a 'grammar' statement or * statements. This will generate 'grammar' syntax that could be * used in source code. Note that this won't necessarily generate * the identical syntax as the original 'grammar' statement that was * used to create the GrammarProd object; there are many ways to * write the same production, and the compiler doesn't keep * information on superficial variations in syntax. The syntax we * generate will have the identical meaning to the original, though. * Note also that we don't (and can't) display the source code of any * methods defined with the match object, nor do we show any * additional properties of the object; we only show the grammar * definitions. */ displayProdGrammar(prod) { local lastMatchObj = nil; /* get the grammar info - this is a list of GrammarAltInfo objects */ local alts = prod.getGrammarInfo(); /* display each alternative */ for (local ai = 1 ; ai <= alts.length() ; ++ai) { local alt = alts[ai]; /* * if this matches the previous rule's full name, just show * it as another alternative for the same named rule; * otherwise, show the new named rule */ if (lastMatchObj == alt.gramMatchObj) "\t| "; else "grammar <>:\n\t"; /* check for badness */ if (alt.gramBadness != 0) "[badness <>] "; /* show the token list */ foreach (local tok in alt.gramTokens) { local nm = reflectionServices.valToSymbol(tok.gramTokenInfo); /* show the token information */ switch (tok.gramTokenType) { case GramTokTypeProd: case GramTokTypeSpeech: case GramTokTypeTokEnum: say(nm); break; case GramTokTypeNSpeech: "<"; for (local i = 1 ; i <= tok.gramTokenInfo.length() ; ++i) { if (i != 1) " "; say(reflectionServices.valToSymbol( tok.gramTokenInfo[i])); } ">"; break; case GramTokTypeLiteral: "'<>'"; break; case GramTokTypeStar: "*"; break; default: "(???)"; break; } /* add the target property, if present */ if (tok.gramTargetProp != nil) "-><< reflectionServices.valToSymbol(tok.gramTargetProp)>>"; /* add a space before the next one */ " "; } /* finish the line */ "\n"; /* * if this is the last one with this match object, add the * superclass specifier */ if (ai == alts.length() || alt.gramMatchObj != alts[ai+1].gramMatchObj) { /* add the superclass list */ "\t: "; for (local scLst = alt.gramMatchObj.getSuperclassList(), local i = 1 ; i <= scLst.length() ; ++i) { if (i != 1) ", "; say(reflectionServices.valToSymbol(scLst[i])); } /* end the grammar definition */ "\n;\n"; } /* remember the match object for next time */ lastMatchObj = alt.gramMatchObj; } } ; VerbRule(GramProd) 'gramprod' singleLiteral : GramProdAction verbPhrase = 'show/showing the grammar for (what)' ; frobtads-1.2.3/tads3/samples/bantest.t0000644000175000001440000001157310272264436017030 0ustar realncusers#charset "us-ascii" /* Copyright 2002 Michael J. Roberts */ /* * This is an interactive test for the new persistent banner manager * module, banner.t. You can add this into any adv3-based game; we'll * simply add a few new verbs to the game that let the player exercise * banners interactively: * * addbanner left|right|top|bottom id - adds a new banner, aligned as * requested and with the given ID. * * writebanner id - writes the given text to the banner. The * new text is appended to any text currently in the banner, followed by * a newline. * * delbanner id - removes the banner with the given ID. */ #include #include /* * An object to track our interactively-created banners. */ addedBannerTracker: object /* a table of our added banners, indexed by ID */ bannerTab = static new LookupTable(16, 32) ; DefineIAction(AddBanner) execAction() { /* add the banner at the top level (i.e., no banner parent) */ doAddBanner(nil, id_, align_.bannerAlign, 10); } doAddBanner(parentID, id, align, pct) { local parent; /* make sure the name isn't already in use */ if (addedBannerTracker.bannerTab[id] != nil) { "That banner ID is already in use. "; return; } /* create a new banner window object */ local win = new BannerWindow('user:' + id); /* if a parent was specified, find it by ID */ if (parentID != nil) { /* look up the parent by ID */ parent = addedBannerTracker.bannerTab[parentID]; /* make sure we found it */ if (parent == nil) { "The specified parent doesn't exist. "; return; } } else { /* there's no parent */ parent = nil; } /* show the banner */ if (!win.showBanner(parent, BannerLast, nil, BannerTypeText, align, pct, BannerSizePercent, BannerStyleBorder | BannerStyleVScroll | BannerStyleAutoVScroll)) { "Unable to create new banner window. "; return; } /* write some initial text */ win.writeToBanner('This is banner ' + id + '\n'); win.flushBanner(); /* add it to the table of active banners */ addedBannerTracker.bannerTab[id] = win; /* indicate success */ "Banner created. "; } ; DefineIAction(AddSubBanner) execAction() { /* add the banner with the given parent */ AddBannerAction.doAddBanner(parid_, id_, align_.bannerAlign, 40); } ; VerbRule(AddBanner) 'addbanner' bannerAlignType->align_ tokWord->id_ : AddBannerAction verbPhrase = 'addbanner/adding banner' ; VerbRule(AddBannerEmpty) 'addbanner' : IAction execAction() { "usage: addbanner left|right|top|bottom id "; } ; VerbRule(AddSubBanner) 'addsub' tokWord->parid_ bannerAlignType->align_ tokWord->id_ : AddSubBannerAction verbPhrase = 'addsub/adding sub-banner' ; VerbRule(AddSubBannerEmpty) 'addsub' : IAction execAction() { "usage: addsub parent_id left|right|top|bottom id "; } ; grammar bannerAlignType(left): 'left' : BasicProd bannerAlign = BannerAlignLeft ; grammar bannerAlignType(right): 'right' : BasicProd bannerAlign = BannerAlignRight ; grammar bannerAlignType(top): 'top' : BasicProd bannerAlign = BannerAlignTop ; grammar bannerAlignType(bottom): 'bottom' : BasicProd bannerAlign = BannerAlignBottom ; DefineLiteralAction(WriteBanner) execAction() { local win; /* make sure the banner actually exists */ win = addedBannerTracker.bannerTab[id_]; if (win == nil) { "There is no such banner. "; return; } /* write the literal text to the banner */ win.writeToBanner(getLiteral() + '\n'); win.flushBanner(); /* indicate success */ "Done. "; } ; VerbRule(WriteBanner) 'writebanner' tokWord->id_ singleLiteral : WriteBannerAction verbPhrase = 'write/writing to banner (what)' ; DefineIAction(DelBanner) execAction() { local win; /* make sure the banner actually exists */ win = addedBannerTracker.bannerTab[id_]; if (win == nil) { "There is no such banner. "; return; } /* remove the banner window */ win.removeBanner(); /* stop tracking the banner window object */ addedBannerTracker.bannerTab.removeElement(id_); /* indicate success */ "Done. "; } ; VerbRule(DelBanner) 'delbanner' tokWord->id_ : DelBannerAction verbPhrase = 'delbanner/deleting banner' ; frobtads-1.2.3/tads3/samples/sample.t3m0000644000175000001440000000114212136317005017070 0ustar realncusers# tads 3 makefile for the library sample game # # To compile the library sample game, use this makefile with the # command-line compiler like this: # # t3make -f sample # # You can also use this makefile with Workbench on Windows. Start Workbench, # select "Load Project" from the "File" menu, and then choose this file from # the file selector dialog. # -o sample.t3 -pre -nodef -D LANGUAGE=en_us -D MESSAGESTYLE=neu -Fy obj -Fo obj -w1 ##sources # This is the list of source files making up the sample game. # -source reflect -lib system -lib adv3/adv3 -source sample -source bantest -res GameInfo.txt frobtads-1.2.3/tads3/samples/sample.t0000644000175000001440000024425211747343340016653 0ustar realncusers#charset "us-ascii" /* * Sample game using the TADS 3 library. * * This is an extremely contrived game, and is not intended to be the * least bit interesting to a player but rather serves as a test of * various library features during library development. */ #include "adv3.h" #include "tok.h" #include "en_us.h" /* * This is how we'd change a library grammar definition for a verb, if * we wanted to replace library grammar with our own. This doesn't * change the rest of the VerbRule definition; it merely changes the * grammar. If we wanted to change the rest of the VerbRule, we'd use * 'replace' rather than 'modify'. Note that 'replace' and' modify' * both replace the grammar rule itself, though; they differ in how they * handle the *rest* of the properties of the VerbRule object. * * To delete a library verb rule grammar, modify the rule and use an * unmatchable token as the replacement grammar rule (' ' will work, * since the standard tokenizer won't return a space character as a * token). */ //modify VerbRule(Examine) // ('ex' | 'lkat') DobjList : //; /* ------------------------------------------------------------------------ */ /* * Game credits and version information */ versionInfo: GameID IFID = 'fa555579-c107-f533-d2a8-d2c93ca87ea0' name = 'TADS 3 Library Sampler' version = '1.0' byline = 'by M.J.Roberts' htmlByline = 'by M. J. Roberts' authorEmail = 'M.J. Roberts ' desc = 'A random sample and test of features of the tads 3 library.' htmlDesc = 'A random sample and test of features of the TADS 3 library.' /* add some special text of our own for the credits */ showCredit() { "<>\n <>\b Thanks to everyone who has participated in the TADS 3 library design process for contributing so many great ideas. \b
* * * \n
\b"; } /* show "about" information */ showAbout() { "This is just a boring sample game to test features of the library under construction. Everything in this game is contrived to exercise a particular set of library features, and we make no attempt to make a coherent game out of this hodge-podge of tests. A more interesting demonstration game will have to wait until the library is further along. "; } ; /* ------------------------------------------------------------------------ */ /* * Set up a barrier object so that we can conveniently prevent the * tricycle from going past certain points. This is just a generic * vehicle barrier, but we customize the failure message. */ tricycleBarrier: VehicleBarrier construct(msg) { msg_ = msg; } explainTravelBarrier(traveler) { if (traveler == tricycle) reportFailure(msg_); else inherited(traveler); } msg_ = '{You/he}\'d best stay indoors with the tricycle; it would probably break if you rode it outside. ' ; /* set up a barrier against pushing the television out of the house */ tvBarrier: PushTravelBarrier construct(msg) { msg_ = msg; } canPushedObjectPass(obj) { return obj != television; } explainTravelBarrier(traveler) { reportFailure(msg_); } msg_ = 'The television\'s casters are too small to allow the TV to go beyond this point. ' ; /* ------------------------------------------------------------------------ */ /* * Trivial class for rooms in the house. We use this mostly for * identification of house rooms. */ class HouseRoom: Room ; class DarkHouseRoom: DarkRoom, HouseRoom ; /* ------------------------------------------------------------------------ */ /* * Basic Coin class */ class Coin: Thing vocabWords = 'coin*coins' listWith = [coinGroup] /* * The base name for the coin as it appears in groups. We leave off * the word "coin" because the coin group prefix uses it. */ coinGroupBaseName = '' /* use the coinCollective for selected actions on coins */ collectiveGroups = [coinCollective] /* * coin group name and counted name - we synthesize these from the * group base name */ coinGroupName = ('one ' + coinGroupBaseName) countedCoinGroupName(cnt) { return spellIntBelow(cnt, 100) + ' ' + coinGroupBaseName; } ; /* * copper coins */ class CopperCoin: Coin 'copper -' 'copper coin' @livingRoom isEquivalent = true coinValue = 1 coinGroupBaseName = 'copper' ; /* * silver coins */ class SilverCoin: Coin 'silver -' 'silver coin' @livingRoom isEquivalent = true coinValue = 5 coinGroupBaseName = 'silver' ; /* * gold coins */ class GoldCoin: Coin 'gold -' 'gold coin' @livingRoom isEquivalent = true coinValue = 10 coinGroupBaseName = 'gold' /* flag: I've awarded points for being taken by the player character */ takeAwarded = nil /* award points when the item is first taken */ afterAction() { /* inherit the default handling */ inherited(); /* * if I'm now being held directly or indirectly, and I haven't * awarded my points for being taken before, award points now */ if (!takeAwarded && isIn(libGlobal.playerChar)) { /* award my points */ goldCoinAchievement.awardPoints(); /* only award this achievement once per coin */ takeAwarded = true; } } ; /* * Coin group - this is a listing group we use to group coins in room * contents lists, object contents lists, and inventory lists. */ coinGroup: ListGroupParen showGroupCountName(lst) { "<> coins"; } /* * Just for the heck of it, order coins in a group in descending * order of monetary value. Since we want descending order, return * the negative of the value comparison. */ compareGroupItems(a, b) { return b.coinValue - a.coinValue; } /* * Customize the display of the individual coins listed in a coin * group, so that we leave off the word "coin" from the name. This * will make our group list look like so: * * five coins (two gold, two silver, one copper) */ showGroupItem(lister, obj, options, pov, info) { say(obj.coinGroupName); } showGroupItemCounted(lister, lst, options, pov, infoTab) { say(lst[1].countedCoinGroupName(lst.length())); } ; /* * Coin Collective - this is a collective group object that we use to * perform certain actions on coins collectively, rather than iteratively * on the individuals. This is an "itemizing" collective group, because * we want "examine coins" to show a message listing the individual * coins. */ coinCollective: ItemizingCollectiveGroup '*coins' 'coins' ; /* ------------------------------------------------------------------------ */ /* * Balloons */ class Balloon: Thing 'inflated uninflated popped balloon' /* balloons are by default equivalent to one another */ isEquivalent = true /* list balloons in one place as a group */ listWith = [balloonGroup] /* our state index - this is an index into our allStates list */ state = nil /* get our current state - translates our index to a state */ getState = (allStates[state]) /* our list of all of our possible states */ allStates = [balloonStateInflated, balloonStateUninflated, balloonStatePopped] ; class RedBalloon: Balloon 'red -' 'red balloon'; class BlueBalloon: Balloon 'blue -' 'blue balloon'; class GreenBalloon: Balloon 'green -' 'green balloon'; /* use a simple unadorned grouper to keep all the balloons together */ balloonGroup: ListGroupSorted; /* balloon state objects */ balloonStateInflated: ThingState 'inflated' +1 stateTokens = ['inflated'] ; balloonStateUninflated: ThingState 'uninflated' +2 stateTokens = ['uninflated'] ; balloonStatePopped: ThingState 'popped' +3 stateTokens = ['popped'] ; sun: MultiFaceted initialLocationClass = OutdoorRoom instanceObject: Distant { 'sun' 'sun' "It's the yellow star around which the Earth orbits. " } ; /* ------------------------------------------------------------------------ */ /* * Living Room */ redBook: Thing 'red book*books' 'red book' @livingRoom; blueBook: Thing 'blue test book/booklet*books booklets' 'blue test booklet' @livingRoom; bigRedBall: Thing 'big large red ball*balls' 'large red ball' @livingRoom; smallRedBall: Thing 'small little red ball*balls' 'small red ball' @livingRoom; greenBall: Thing 'green ball*balls' 'green ball' @livingRoom; box: Container 'brown cardboard box' 'brown box' @livingRoom; poBox: Thing 'p. o. p.o. po post office # box' 'PO Box' @livingRoom; eightball: Thing '8 ball/8-ball*balls' '8-ball' @livingRoom; greenJar: OpenableContainer 'green glass jar*jars' 'green jar' @livingRoom "It's made of transparent green glass. " material = glass initiallyOpen = nil ; clearJar: OpenableContainer 'clear glass jar*jars' 'clear jar' @livingRoom "It's made of clear glass. " material = glass initiallyOpen = true isEquivalent = true ; CopperCoin location=greenJar; CopperCoin location=greenJar; SilverCoin; SilverCoin; SilverCoin; GoldCoin; GoldCoin; GoldCoin; GoldCoin; GoldCoin; /* achievement for obtaining a gold coin */ goldCoinAchievement: Achievement "obtaining <> gold coin<< scoreCount > 1 ? 's' : ''>>" /* * we're worth two points, and we can be awarded up to five times * (one time per gold coin), for a total of ten points */ points = 2 maxPoints = 10 ; ironKey: Key 'iron key*keys' 'iron key' @livingRoom "It's an ordinary iron key. " ; brassKey: Key 'brass key*keys' 'brass key' @livingRoom "It's an ordinary brass key. " ; rustyKey: Key 'rusty key*keys' 'rusty key' @livingRoom "It's an ordinary rusty key. " ; /* * Note that we don't have to define any vocabulary for the player * character, since the library automatically defines the appropriate * pronouns for any actor being used as the player character. The * pronoun selection is automatic and dynamic, so if we change to * another player character later, the pronouns will all switch * automatically at the same time. */ me: Person location = livingRoom bulkCapacity = 5 // to force lots of bag-of-holding shuffling, for testing // referralPerson = firstPerson /* * When we issue a series of commands to another actor, we can wait * until the entire series finishes before we take another turn. * Set this flag to true to wait, or nil to allow us to take turns * while the other actor carries out our orders. */ issueCommandsSynchronously = true ; + duffel: BagOfHolding, Openable, Container 'duffel bag' 'duffel bag' "It's a really capacious bag. " ; + keyring: Keyring 'key ring/keyring' 'keyring' ; bob: Person 'bob' 'Bob' @livingRoom isProperName = true isHim = true /* obey any command from any other character */ obeyCommand(issuer, action) { return true; } /* * We want to be able to follow other actors if requested, so keep * track of departure data. NPC's don't track departure information * by default, because most NPC's never need it, and tracking it * consumes a little extra time and memory. */ wantsFollowInfo(obj) { return true; } ; /* * Respond to any ASK ABOUT topic with a generic message incorporating * the tokens from the topic. */ + DefaultAskTopic "Ah, yes, <>, very interesting... " ; + GiveTopic matchTopic(fromActor, obj) { /* we match any coin */ return obj.ofKind(Coin) ? matchScore : nil; } handleTopic(fromActor, obj) { /* accept the coin into my actor's inventory */ obj.moveInto(getActor()); /* add our special report */ gTranscript.addReport(new AcceptCoinReport(obj)); /* register for collective handling at the end of the command */ gAction.callAfterActionMain(self); } afterActionMain() { /* * adjust the transcript by summarizing consecutive coin * acceptance reports */ gTranscript.summarizeAction( {x: x.ofKind(AcceptCoinReport)}, {vec: '\^' + getActor().theName + ' accepts the ' + spellInt(vec.length()) + ' coins. ' }); } ; + AskForTopic [ironKey, brassKey, rustyKey] handleTopic(fromActor, topic) { "So, you want a key, do you? Let's see... "; SimpleLister.showSimpleList(topic.inScopeList + topic.likelyList); "..."; } ; class AcceptCoinReport: MainCommandReport construct(obj) { /* remember the coin we accepted */ coinObj = obj; /* inherit the default handling */ gMessageParams(obj); inherited('Bob accepts {the obj/him}. '); } /* my coin object */ coinObj = nil ; bill: Person 'bill' 'Bill' @livingRoom isProperName = true isHim = true obeyCommand(issuer, action) { return true; } wantsFollowInfo(obj) { return true; } /* * Make Bill go after Bob. This normally wouldn't be important, * since the relative order of actor turns is usually arbitrary, but * we want our test scripts to be stable so we establish a fixed * order. We accomplish the order adjustment simply by giving Bill a * slightly higher schedule order than the default. */ calcScheduleOrder() { inherited(); scheduleOrder += 1; } ; livingRoom: HouseRoom roomName = 'Living Room' destName = 'the living room' desc = "It's a nice, big room. A potted plant is the only attempt at decoration. Passages lead north, east, and west. To the south, the front door of the house leads outside. " north = diningRoom east = den south = frontDoor out asExit(south) west = platformRoom ; + tricycle: Vehicle, BasicChair 'tricycle/trike' 'tricycle' "It's a child's three-wheeled bike. " gaveInstructions = nil allowedPostures = [sitting] /* * Customize the travel arrival/departure messages for certain types * of connectors. The default message is of the form "The tricycle * (carrying whoever) arrives from the east." This isn't great for * the tricycle, which only carries one rider, and which only goes * when the rider makes it go; a better message would be "Bob rides * the tricycle in from the east," which better captures that the * rider is the one making the tricycle go somewhere. */ sayArrivingDir(dir, conn) { local actor = getTravelerActors()[1]; "<> ride<> in from the <> on a tricycle. "; } sayArrivingThroughPassage(conn) { local actor = getTravelerActors()[1]; "<> ride<> in through <> on a tricycle. "; } sayDepartingDir(dir, conn) { local actor = getTravelerActors()[1]; "<> ride<> the tricycle off to the <>. "; } sayDepartingThroughPassage(conn) { local actor = getTravelerActors()[1]; "<> ride<> off through <> on the tricycle. "; } dobjFor(Ride) asDobjFor(SitOn) dobjFor(SitOn) { check() { /* don't allow riding the tricycle except in the house */ if (!gActor.location.getOutermostRoom().ofKind(HouseRoom)) { reportFailure('{You/he} shouldn\'t ride {the dobj/him} except in the house; {it dobj/he} might break. '); exit; } } action() { /* inherit the default handling */ inherited(); /* if we haven't given instructions previously, do so now */ if (!gaveInstructions) { mainReport('Okay, {you\'re} now on the tricycle. {You/he} can probably manage to ride around on it if {it/he} tr{ies}; just say the direction you\'d like ' + (gActor.isPlayerChar ? '' : '{it/him}') + ' to go. '); gaveInstructions = true; } } } dobjFor(Eat) { verify() { } action() { "{You/he} eat{s} {your} tricycle. "; } } ; + frontDoor: Door 'front door' 'front door' "It leads outside to the south. " travelBarrier = [tricycleBarrier, tvBarrier] ; + Decoration 'potted plant small green clay plant/tree/pot/leaf/leaves/dirt/soil' 'potted plant' "It's a tree with small green leaves, about six feet tall and growing in a clay pot filled with soil. " ; + television: TravelPushable 'old huge boxy model/unit/television/tv' 'television' "It's an old, huge, boxy model, certainly black-and-white. The bulbous picture tube shows no sign of life. Two dials control the channel selection (it looks like it's currently <>), but the channel setting hardly matters when the set doesn't work in the first place. It seems to be on small casters, which is a good thing given how big it is and how heavy it looks. " specialDesc = "An old television, a huge, boxy, massive-looking thing, sits on the floor. " specialNominalRoomPartLocation = defaultFloor curChannel() { if (upperDial.curSetting == 'UHF') return lowerDial.curSetting; else return upperDial.curSetting; } ; ++ Decoration 'casters' 'casters' "Small wheels, intended to make it easier to move the unit's huge bulk around the house. " ; ++ Decoration 'bulbous picture tube/screen' 'picture tube' "It shows no pictures. "; ++ upperDial: NumberedDial, Component 'upper tv television channel dial*dials' 'upper dial' "This is the VHF selector dial; it can be set to a channel from 2 through 13, or to the additional stop marked \"UHF\". It's currently set to <>. " minSetting = 2 maxSetting = 13 curSetting = 8 isValidSetting(val) { /* allow the special 'uhf' setting in addition to the numbers */ if (val.toLower() == 'uhf') return true; /* inherit the default handling to check the numbered settings */ return inherited(val); } makeSetting(val) { /* if the setting is 'uhf', change it to all caps */ if (val.toLower() == 'uhf') val = 'UHF'; /* inherit default handling with the (possibly) adjusted value */ inherited(val); } ; ++ lowerDial: NumberedDial, Component 'lower tv television channel dial*dials' 'lower dial' "This is the UHF selector dial; it can be set to a channel from 14 through 82. It's currently set to <>. You probably have to turn the upper dial to \"UHF\" for this dial's setting to have any effect. " minSetting = 14 maxSetting = 82 curSetting = 44 ; + Consultable 'phone book' 'phone book' "You can probably look up people you know in here. " ; ++ ConsultTopic @bob "Bob's number is (415)555-1212. "; ++ ConsultTopic @bill "Bill's number is (650)555-1212. "; ++ ConsultTopic @salesman "Ron's number is (510)555-1212. "; ++ ConsultTopic @insCompany "Actuarial Life Insurance Company! Call for best rates! (800)555-1212! " ; ++ DefaultConsultTopic "You can't find any such listing. "; /* ------------------------------------------------------------------------ */ /* * Platform room */ platformRoom: HouseRoom 'Platform Room' 'the platform room' "This room is obviously highly contrived. Filling one end of the room is a platform, almost like a theater stage, raised a couple of feet above the floor. At opposite ends of the platform are two smaller platforms, one red and one blue; atop each smaller platform is a chair of the same color as its platform. In the center of the main platform is a raised dais, upon which <> a leather armchair. <.p>The only exit is east. " east = livingRoom down = (whiteBox.isOpen ? whiteBox.down : nil) ; + Fixture, Platform 'main theater theatre platform/stage*platforms' 'main platform' ; ++whiteBox: Openable, Booth 'large white cardboard box' 'white box' useInitSpecialDesc() { /* show our initial description only when the actor isn't in me */ return inherited() && !gActor.isIn(self); } initSpecialDesc = "On the main platform is a large white box, about waist high and quite roomy. " /* don't mention our contents if we're using our initial description */ contentsListed = (!useSpecialDesc()) interiorDesc = "The box is roomy, but not enough that you can stand up in it when it's closed. " down = whiteBoxTrapDoor /* make it paper, so we can hear through the box */ material = paper /* * we can't stand up in the box when it's closed, so make the * default posture sitting */ defaultPosture = (isOpen ? standing : sitting) /* we can't close the box when someone's standing in it */ makeOpen(stat) { /* if we're closing the box, check to make sure we're allowed to */ if (!stat) { /* * We're trying to close the box; if anyone's standing in * the box, don't allow it. */ foreach (local cur in allContents()) { /* if this is a standing actor, disallow closure */ if (cur.isActor && cur.posture == standing) { /* * we can't close - issue a failure report and * terminate the command */ reportFailure('{You/he} cannot close the box while anyone is standing in it. '); exit; } } } /* no problems - inherit default handling */ inherited(stat); } /* * if they try to stand within a nested room within us, mention that * they can only sit */ roomBeforeAction() { /* * If we're closed, and the action is 'stand', and they're * something nested within me, mention after the fact that * there's no room to stand up. We need to mention this, but we * do not need to enforce the constraint here because the nested * room will set our default sitting posture when moving an * actor from an interior location. */ if (!isOpen && gActionIs(Stand) && gActor.isIn(self) && !gActor.isDirectlyIn(self)) reportAfter('There isn\'t room to stand up in here, so {you/he} sit{s} in the box. '); } /* enforce the low headroom when the box is closed */ makeStandingUp() { if (isOpen) { /* we're open, so proceed as normal */ inherited(); } else { /* the box is closed, so they can't stand up */ reportFailure('There\'s not enough room to stand up in the box while it\'s closed. '); } } ; +++ whiteBoxTrapDoor: Fixture, Door -> tunnelTrapDoor 'small trap door' 'trap door' "It's set into the floor of the box; it looks just large enough for you to fit through. " /* * list me *after* the room's contents, since the box is sometimes * listed with the room's contents */ specialDescBeforeContents = nil initSpecialDesc = "A small trap door is set into the floor of the box. " /* don't require standing up before entering the trap door */ actorTravelPreCond(actor) { return []; } enteredBefore = nil dobjFor(TravelVia) { action() { /* mention the ladder below */ if (enteredBefore) "You find the ladder below and climb down. "; else { "You tentatively lower yourself through the door, and manage to find what feels like a ladder below, so you carefully climb down. "; enteredBefore = true; } /* inherit the default handling */ inherited(); } } ; +++ Switch 'small black plastic beeper pager box/beeper/pager/switch' 'beeper' "It's a small black plastic box with a single red light (which is currently <>) and a switch (currently <>). " /* * if they know about us, they'll recognize our beeping, so they'll * want to be able to refer to the beeper itself; thus, give the * beeper a sound presence once it's been seen */ soundPresence = (isOn && seen) isOn = true makeOn(val) { /* inherit the default handling */ inherited(val); /* add/remove our beeping sound, as appropriate */ beeperNoise.moveInto(val ? self : nil); } ; ++++ Component 'tiny small red beeper light' 'beeper light' "It's a tiny red light, currently <>. " ; ++++ beeperNoise: Noise 'piercing high-pitched high pitched beeping sound/noise/beep' 'beeping sound' sourceDesc = "The beeper is making a piercing, high-pitched beeping noise. " descWithSource = "The beeper is beeping, which is what they do. " descWithoutSource = "It's a piercing, high-pitched beeping noise. " hereWithSource = "The beeper is making a piercing beeping noise. " hereWithoutSource = "You can hear a high-pitched beeping noise. " displaySchedule = [2, 4] ; ++ Fixture, Platform 'small smaller red platform*platforms' 'red platform' ; class PlatformChair: Chair dobjFor(SitOn) { verify() { /* inherit default */ inherited(); /* * if they're directly in my location, and my location is a * nested room of some kind, boost likelihood that this is * where they want to sit */ if (gActor.isDirectlyIn(location) && location.ofKind(NestedRoom)) logicalRank(150, 'adjacent'); } } ; +++ Fixture, PlatformChair 'red chair' 'red chair' ; ++ Fixture, Platform 'small smaller blue platform*platforms' 'blue platform' ; +++ Fixture, PlatformChair 'blue chair' 'blue chair' ; ++ dais: Fixture, Platform 'raised dais' 'dais' ; +++ stool: PlatformChair 'wooden wood stool' 'stool' /* * since we're part of the main room description if we're in our * original location, don't list me if I'm on the dais */ isListed = (location == dais ? nil : inherited) ; +++ Fixture, PlatformChair 'leather arm chair/armchair' 'armchair' actorInPrep = 'in' ; #if 0 // disabled by default /* * enable this to test multi-location sense connections - this is a bit * too contrived an example, so we disable it by default */ whiteBoxHole: SenseConnector, Fixture 'small hole' 'small hole' "It's a small hole in the box. " locationList = [whiteBox, backYard] connectorMaterial = glass ; #endif /* ------------------------------------------------------------------------ */ /* * Front yard */ frontYard: OutdoorRoom 'Front Yard' 'the front yard' "This is a small yard in front of a modest house. The front door to the house leads in to the north. To the south is the street. " in asExit(north) north = frontDoorOutside south: NoTravelMessage { "You'd rather not venture beyond the immediate vicinity of the house right now. " } atmosphereList: RandomEventList { eventList = [ nil, 'A car drives past. ', 'Someone honks a horn in the distance. ', nil, 'A few birds pass overhead. ', 'A couple of bicyclists pedal past on the street. ', nil, nil ] } ; + frontDoorOutside: Door ->frontDoor 'front door' 'front door' "It leads north, into the house. " ; + Enterable, Decoration 'narrow residential street/road' 'street' "It's a narrow residential street. " connector = (frontYard.south) ; + Enterable, Decoration ->frontDoorOutside 'house' 'house' "It's a modest house. A door leads in to the north. " ; + RedBalloon state = 3; + RedBalloon state = 3; + RedBalloon state = 3; + RedBalloon state = 3; + RedBalloon state = 1; + RedBalloon state = 1; + RedBalloon state = 2; + BlueBalloon state = 2; + BlueBalloon state = 1; + BlueBalloon state = 1; + GreenBalloon state = 3; + GreenBalloon state = 3; + GreenBalloon state = 3; + GreenBalloon state = 3; + GreenBalloon state = 3; + salesman: Person 'salesman/man/ron' 'salesman' "He's a thin, youngish man in a garish suit that's rather comically oversized on him. " isHim = true ; ++ Wearable 'oversized garish suit' 'suit' "It's an oddly bright shade of blue, and is way too large for its present wearer. " wornBy = salesman ; ++ Thing 'huge black briefcase' 'briefcase' "It's a huge black briefcase. " ; /* the salesman's initial state */ ++ ActorState isInitState = true specialDesc = "A youngish man in a garish suit is standing in the yard near the front door. " takeTurn() { if (gPlayerChar.location == frontYard) salesman.initiateConversation(salesConv, 'sales-hello'); inherited(); } ; ++ salesConv: InConversationState attentionSpan = 10000 nextState = salesWaiting stateDesc = "He's smiling eagerly at you. " specialDesc = "The salesman is standing a little too close, smiling a bit too much. " ; ++ salesWaiting: ConversationReadyState specialDesc = "The salesman is standing in the yard. " inConvState = salesConv takeTurn() { /* re-arm for conversation only when the PC is gone */ if (gPlayerChar.location != frontYard) regreet = true; /* greet again randomly if we're re-armed and the PC is present */ if (regreet && gPlayerChar.location == frontYard && rand(100) < 33) salesman.initiateConversation(salesConv, 'sales-1'); inherited(); } /* flag: we're ready to re-greet the player */ regreet = nil /* on activation, set the re-greet flag to nil by default */ activateState(actor, oldState) { inherited(actor, oldState); regreet = nil; } ; +++ HelloTopic "You tap the salesman on the shoulder. Oh, hi! he says. I\'d sure like to talk to you about life insurance. <.convnode sales-1> " ; +++ ByeTopic, ShuffledEventList ['I have to go, you say. <.p>Thanks for your time! the salesman says. ', 'Thanks, but not right now, you say. <.p>I\'ll be here when you\'re ready! the salesman says. '] ; +++ ImpByeTopic, ShuffledEventList ['The salesman waves. Okay, I\'ll wait here! he says. ', 'Thanks for your time! the salesman says. ', 'The salesman calls after you, I\'ll be here if you need me! '] ; ++ ConvNode 'sales-hello' npcGreetingMsg = "<.p>The man in the bad suit walks up to you and extends his hand; by habit you shake his hand. Hello, friend! My name is Ron, and I represent the Acturial Life Insurance Company. He releases your hand after a vigorous shaking. You know, a lot of people I talk to don't know just how important life insurance is to proper financial planning. Let me ask you this: do you have all the life insurance coverage you and your family need? " npcContinueList: CyclicEventList { [ 'The salesman says eagerly, Really, do you have enough insurance? ', 'Ron says, The sad fact is, most people don\'t know how much insurance they really need. I\'d really like to tell you about our policies...<.convnode sales-1> ' ]} ; +++ YesTopic "I'm covered, you say. <.p>Ron smiles and nods. You know, a lot of people think they're covered. But have you really read your insurance policy? Most insurance policies have so many exclusions and limitations that you just can't rely on them. That's why Acturial Life created a new kind of insurance policy. Let me tell you about it...<.convnode sales-1> " ; +++ NoTopic "Why, no, you say. <.p>Ron smiles eagerly. Well, then it's a good thing I was in your neighborhood today! Let me tell you about our policies... <.convnode sales-1> " ; ++ ConvNode 'sales-1' npcGreetingMsg = "<.p>Ron walks up to you. Hello again, friend! It would be my pleasure to help you with your insurance needs. " npcContinueList: ShuffledEventList { ['The salesman looks serious for a moment. Most people don\'t know this, but death is our number one killer, he says earnestly. He goes back to smiling. Fortunately, death is covered under our policy\'s loss-of-life section. ', 'Ron says, You know, life insurance is a lot more interesting than most people think. ', 'The salesman says, I\'m really glad I was in the neighborhood today. Everyone needs more insurance than they think. ' ]} ; ++ AskTellTopic, StopEventList @insPolicy [ 'Okay, tell me about your policy, you say. <.p>Our policy is the best in the business, Ron says. For starters, it\'s guaranteed go pay, which most policies aren\'t. There\'s so much more I can tell you. ', 'Tell me more about your policy, you say. <.p>I could go on all day, the salesman says. ' ] ; ++ AskTellTopic, StopEventList @insCompany ['I\'ve never heard of your company, you say. <.p>Most people haven\'t, Ron says. We\'re the best-kept secret in the business.', 'Why would you want to keep your company a secret? <.p>One word: savings. We save on marketing expenses, and pass the low cost on to you. ', 'What else can you tell me about your company? <.p>I\'d rather tell you about the policy. '] ; /* some topics for the salesman to talk about */ insPolicy: Topic 'life insurance policy/policies'; insCompany: Topic 'acturial life insurance company'; /* ------------------------------------------------------------------------ */ /* * Den */ den: HouseRoom roomName = 'Den' destName = 'the den' desc = "This small room is dominated by a desk, a massive steel edifice painted a drab gray, something that would be more at home in a government office during the cold war. Behind the desk, built in to the north wall, is a bookcase. A passage leads west. " west = livingRoom north = bookcasePassage roomParts = static (inherited() - defaultNorthWall) ; + Decoration 'north wall*walls' 'north wall' "A floor-to ceiling bookcase<< bookcasePassage.isOpen ? " (which has swung aside to expose a passage to the north)" : "">> is built into the wall. " ; + bookcase: Fixture 'book case/bookcase' 'bookcase' desc { "It's a floor-to-ceiling bookcase built in to the north wall, behind the desk, filled with hundreds of dusty tomes. "; if (bookcasePassage.isOpen) "The bookcase is evidently a secret door, because it has moved sideways to expose a passage to the north. "; } ; + bookcasePassage: BasicOpenable, HiddenDoor 'passage' 'passage' "It leads north. " initSpecialDesc { if (isOpen) "The bookcase has moved aside to reveal a passage to the north. "; } ; + Decoration 'dusty book/books/tome/tomes' 'books' "There must be hundreds of books, all with incomprehensible titles and authors you've never heard of. " isPlural = true dobjFor(Read) { verify() { } action() { "Much as you enjoy reading, you can't find any book whose title even means anything to you, let alone holds any interest. "; } } ; + desk: Immovable, Surface 'massive big huge edifice drab gray grey steel metal desk' 'desk' desc { "It's a big desk with a single drawer (currently <>). A small button is set inconspicuously into the edge of the desktop"; if (hiddenPanel.isOpen) ", and in the side is a small compartment containing a lever"; ". "; } dobjFor(Open) remapTo(Open, deskDrawer) dobjFor(Close) remapTo(Close, deskDrawer) dobjFor(LookIn) remapTo(LookIn, deskDrawer) iobjFor(PutIn) remapTo(PutIn, DirectObject, deskDrawer) ; ++ Thing 'bob\'s brand premium fish cleaner jar/cleaner' 'jar of Bob\'s Brand Premium Fish Cleaner' "It presumably once held fish cleaner (whatever that is), but it's just an empty jar now. " ; ++penCup: Container 'pen cup' 'pen cup' "It's a uneven clay cup, currently employed as a pen holder. " /* just for fun, show my contents out-of-line in listings I'm in */ contentsListedSeparately = true ; class pen: Thing 'pen*pens' 'pen' "A simple Bic. " isEquivalent = true ; +++pen; +++pen; +++pen; +++pen; ++ deskDrawer: Component, OpenableContainer 'desk drawer' 'desk drawer' ; ++ Immovable 'old-fashioned rotary phone/telephone/dial/receiver/handset' 'phone' "It's an old-fashioned rotary phone, very sturdy-looking but rather scuffed from long use. " dobjFor(Take) { verify() { } check() { } action() { "You pick up the handset, but there's nothing but static on the line. At least it stops ringing. You put the handset back, and the phone starts ringing again. "; } } dobjFor(Answer) asDobjFor(Take) ; +++ Noise 'ring/ringing' 'ringing/bell' sourceDesc = "The phone is ringing loudly. " descWithSource = "It's an actual mechanical bell, not the ubiquitous electronic warble of modern phones. " hereWithSource() { switch (displayCount) { case 1: "The phone is ringing. "; break; default: "The phone is still ringing. "; break; } } displaySchedule = [2, 4, 8] ; ++ Button, Component 'small button' 'small button' dobjFor(Push) { action() { /* open/close the hidden panel */ hiddenPanel.makeOpen(!hiddenPanel.isOpen); /* show the appropriate message */ if (hiddenPanel.isOpen) "A previously hidden panel in the side of the desk opens, revealing a small compartment containing a lever. "; else "The panel in the desk closes, hiding the lever. Now that the panel is closed, it's completely seamless - you can't see any sign of it. "; } } ; ++ hiddenPanel: BasicOpenable, Component, Container 'hidden panel/compartment' 'compartment' "It's a small compartment, just large enough to contain the lever it enclosed. " bulkCapacity = 0 initiallyOpen = nil /* the panel is only visible when it's open */ sightPresence = (self.isOpen) ; +++ SpringLever, Component 'lever' 'lever' "It's mounted in a small compartment in the desk. " dobjFor(Pull) { action() { /* open/close the secret door */ bookcasePassage.makeOpen(!bookcasePassage.isOpen); /* show the appropriate message */ if (bookcasePassage.isOpen) "The lever is surprisingly heavy, but you manage to pull it all the way out. At the end of its travel, something under the floor clicks, and the bookcase behind the desk slides sideways far enough to open a passage to the north. As soon as you release the lever, it springs back to its original position. "; else "You pull the lever out all the way. Something under the floor clicks, and the bookcase slides sideways until it covers the passage, leaving no trace of a doorway. "; } } ; ++ typewriter: Immovable 'big old old-fashioned black manual typewriter' 'typewriter' initSpecialDesc = "A big, old-fashioned manual typewriter is sitting on the desk. " desc = "It's big, black, manual typewriter, like something out of an old black-and-white movie about newspapermen or private detectives. There's a piece of paper in it. " dobjFor(TypeOn) { verify() { } } dobjFor(TypeLiteralOn) { verify() { } action() { /* add the literal text to the paper */ typewriterPaper.addText(gAction.getLiteral()); mainReport('With some effort, {you/he} work{s} the keys of the ancient machine, noisily impressing ' + gAction.text_ + ' on the paper. '); } } ; +++ typewriterPaper: Thing 'piece/paper' 'piece of paper' isListedInContents = nil desc { if (textList.length() == 0) "The paper is blank. "; else { "The typewritten letters on the page have that uneven darkness and wandering alignment characteristic of the work a well-worn manual typewriter:\b"; foreach (local cur in textList) "\t<>\n"; ""; } } addText(txt) { textList.append(txt); } textList = static new Vector(10) moveInto(obj) { reportFailure('On second thought, {you/he}\'d rather leave the paper in the typewriter in case someone needs to do some typing. '); exit; } dobjFor(TypeOn) { verify() { } } dobjFor(TypeLiteralOn) { verify() { /* * allow it but reduce the likelihood - we want the * typewriter to win in a default case */ logicalRank(50, 'nondefault'); } action() { /* treat 'type on paper' as 'type on typewriter' */ replaceAction(TypeLiteralOn, typewriter, gAction.text_); } } ; ++ dagger: Thing 'dagger' 'dagger' initSpecialDesc = "Someone has jabbed a dagger into the top of the desk, leaving the dagger sticking up almost vertically. " initExamineDesc = "Its point is stuck into the top of the desk. " ; ++ watch: Wearable 'watch' 'watch' "It has a minute hand and an hour hand. "; +++ Component 'minute big hand' 'minute hand'; +++ Component 'hour little hand' 'hour hand'; ++ Container 'glass bottle' 'bottle' bulkCapacity = 2 canFitObjThruOpening(obj) { /* we can only fit small objects through the opening */ return obj.bulk < 2; } ; +++ Thing 'minature model sailing ship/boat' 'model ship' "It's a miniature model of a sailing ship. " bulk = 2 ; #if 0 // for testing distant viewing of sightSize=large objects viewScreen: SenseConnector, Fixture 'view screen/viewscreen' 'viewscreen' "It's a viewscreen, displaying a close-up shot of a floating sphere. " locationList = [den, backYard] connectorMaterial: Material { seeThru = distant hearThru = distant smellThru = opaque touchThru = opaque } ; #endif /* ------------------------------------------------------------------------ */ /* * Top of stairs */ class SpiralStairway: Fixture 'perforated central black metal/stair/stairs/stairway/pole' 'stairs' "The stairs are narrow triangles of perforated black metal arranged in a helix around a central pole. " isPlural = true ; topOfStairs: HouseRoom roomName = 'Top of Stairs' destName = 'the top of the stairs' desc = "This is the top of a narrow spiral staircase that leads down a dark shaft walled in rough brown bricks. A passage leads south. " south = stairPassage down = spiralStairsTop ; + Decoration 'dark rough brown shaft/brick/bricks' 'dark shaft' "The shaft is just large enough for the staircase. " ; + stairPassage: ThroughPassage ->bookcasePassage 'south passage' 'passage' ; + spiralStairsTop: StairwayDown, SpiralStairway moveActor(actor) { if (actor.isPlayerChar) "{You/he} carefully descend{s} the steep, narrow stairs. "; inherited(actor); } travelBarrier = static [ new tvBarrier('The television is far too heavy to take down the stairs. '), new tricycleBarrier('It would make a nice stunt, but it\'s far too dangerous to ride the tricycle down the stairs. ') ] ; /* ------------------------------------------------------------------------ */ /* * Bottom of stairs */ bottomOfStairs: DarkHouseRoom roomName = 'Bottom of Stairs' destName = 'the bottom of the stairs' desc = "This is the bottom of a narrow spiral staircase. Apart from the stairs, the only exit is the door to the south. " up = spiralStairsBottom south = stairDoor /* no illumination here */ brightness = 0 /* * even in the dark, keep the stairs in scope, because we're * standing on them */ getExtraScopeItems(actor) { return inherited(actor) + spiralStairsBottom; } ; class IronDoor: LockableWithKey, Door 'iron door/rivet/rivets/plate/plates' 'iron door' "It's a formidable-looking mass of iron plate and rivets. " keyList = [ironKey] ; + stairDoor: IronDoor; + spiralStairsBottom: StairwayUp, SpiralStairway -> spiralStairsTop travelBarrier: tricycleBarrier { msg_ = 'Riding the tricycle up the stairs is simply out of the question. ' } ; /* * make the washing machine a complex containe so that we can have * components as well as contents within */ + ComplexContainer 'washing machine/washer' 'washing machine' "It's a beat-up old washing machine. A big dial is the only obvious control. " subContainer: ComplexComponent, OpenableContainer { } ; ++ Component, LabeledDial 'big washer washing machine control dial' 'control dial' "The dial has stops labeled Wash, Rinse, Spin, and Stop. It's currently set to <>. " curSetting = 'Wash' validSettings = ['Wash', 'Rinse', 'Spin', 'Stop'] ; /* ------------------------------------------------------------------------ */ /* * Dining Room */ diningRoom: HouseRoom roomName = 'Dining Room' destName = 'the dining room' desc = "This medium-sized room<> has a dining table and a couple of chairs, one upholstered in dark fabric and other in light fabric. A door (<>) leads north. " south = livingRoom north = diningDoor out asExit(north) myNote: Footnote { "This is just a sample note for the dining room. " } ; + alcove: OutOfReach, Fixture, Container 'arched small alcove' 'small alcove' "It's a small, arched alcove, set high up on the wall, near the ceiling. " useSpecialDesc = (contents.length() != 1 || contents[1] != trophy) specialDesc = "A small alcove is set near the top of one wall, up near the ceiling. " canObjReachContents(obj) { /* if the actor is standing on a chair, allow it */ if (obj.posture == standing && obj.location == diningTable) return true; /* inherit the default */ return inherited(obj); } cannotReachFromOutsideMsg(obj) { return '{You/he} can\'t reach ' + obj.theNameObj + ' from here; the alcove is too high up. {You/he} might be able to reach the alcove if {you/he} were standing on something. '; } ; ++ trophy: Thing 'silver big trophy/award/cup' 'trophy' "It's shaped like a big cup, and looks to be made of silver. " useInitSpecialDesc = (location == alcove) initSpecialDesc = "A trophy is visible in a small alcove set into the top of one wall. " ; + diningDoor: Door 'door' 'door' "It leads north. " travelBarrier = [tricycleBarrier, tvBarrier] ; class DiningChair: Chair, Immovable 'chair*chairs' 'chair' "Its style matches that of the table. " ; + DiningChair 'light lighter fabric' 'lighter chair'; + DiningChair 'dark darker fabric' 'darker chair'; + diningTable: Heavy, Platform 'dining large oval table' 'table' "It's a large<> oval table. " myNote: Footnote { "Okay, it's not all that large. Medium-sized sounds so weak, though. " } ; class MyCandle: FireSource, Candle fuelLevel = 32 desc() { /* show a description based on how far we've burned down */ if (fuelLevel > 30) "The candle has barely been used. "; else if (fuelLevel > 24) "The candle looks only slightly burned down. "; else if (fuelLevel > 16) "The candle looks about half burned down. "; else if (fuelLevel > 8) "The candle is considerably burned down. "; else if (fuelLevel > 0) "The candle is mostly burned down. "; else "The candle is completely burned down. "; /* if we're burning, mention it */ if (isLit) "It's lit. "; } ; ++ MyCandle 'red candle' 'red candle'; ++ MyCandle 'white candle' 'white candle'; ++ vase: Container 'vase' 'vase' "It's a simple glass cylinder, open at the top. " bulkCapacity = 1 initSpecialDesc = "A bouquet of flowers is arranged in a vase in the center of the table. " useInitSpecialDesc() { /* don't use the initial description if the flowers have been moved */ return flowers.isIn(self) ? inherited() : nil; } verifyInsert(obj, newCont) { if (obj != flowers) illogical('The vase is only designed to hold flowers. '); } /* * in room and inventory lists, show the vase with its flowers, if * it has the flowers */ listName = (flowers.isIn(self) ? 'a bouquet of flowers in a vase' : inherited()) ; +++ flowers: Thing 'flower/flowers/arrangement/bouquet' 'bouquet of flowers' "It's a nice arrangement of flowers. " /* * don't separately list the flowers if they're in the vase, because * the vase shows the flowers as part of its list name; but don't * hide the flowers from an explicit "look in vase" */ isListed = (!isIn(vase)) isListedIn = true ; ++++ Odor 'floral flowery pleasant fragrance/odor/smell' 'floral fragrance' /* the description when they smell the flowers directly */ sourceDesc = "The flowers have a strong, well, flowery fragrance. " /* the description when they smell us and the flowers are visible */ descWithSource = "The pleasant fragrance is coming from the flowers. " /* the description when they smell us and the flowers aren't seen */ descWithoutSource = "It's a pleasant floral fragrance. " /* room description with and without being able to see the flowers */ hereWithSource = "The flowers have a pleasant fragrance. " hereWithoutSource = "A flowery fragrance is in the air. " ; ++ StretchyContainer 'stretchy bag' 'stretchy bag' "It's made of some very elastic material. It seems to take on the size and shape of whatever's inside. " /* * we have no bulk contribution of our own when we have any contents, * but we do have a minimum empty bulk of 1 */ bulk = 0 minBulk = 1 ; ++ Container 'cookie tin' 'cookie tin' "It's a round metal cylinder about three inches tall, with no lid, decorated with pictures of cookies. " bulkCapacity = 3 ; ++ Thing 'yellow pile inflatable rubber raft' 'inflatable rubber raft' desc { if (inflated) "It's fully inflated. "; else "In its current deflated state, it's just a pile of yellow rubber, bunched up messily. It's large, but you could probably inflate it if you had to. "; } inflated = nil bulk { return inflated ? 10 : 2; } makeInflated(flag) { /* check the effect of the inflation on my bulk */ whatIf({: checkBulkChange()}, &inflated, flag); /* set the new status */ inflated = flag; } dobjFor(Inflate) { verify() { if (inflated) illogicalAlready('It\'s already fully inflated. '); } action() { makeInflated(true); mainReport('It takes all your strength, but you manage to blow up the raft. You might feel light-headed for a bit. '); } } dobjFor(Deflate) { verify() { if (!inflated) illogicalAlready('It\'s already fully deflated. '); } action() { makeInflated(nil); mainReport('You let the air out of the raft, which flattens into a bunched-up pile of yellow rubber. '); } } ; /* ------------------------------------------------------------------------ */ /* * Back Yard */ backYard: OutdoorRoom roomName = 'Back Yard' destName = 'the back yard' vocabWords = 'back yard' desc = "This small yard has a well-kept lawn. The edges of the yard are defined by thick, tall hedges. An old garden shed occupies the northwest corner of the yard. A door into the house is to the south. " south = yardDoor in asExit(south) northwest = shedOuterDoor ; + yardDoor: Door ->diningDoor 'house back door' 'back door of the house' "It leads south, into the house. " ; + Decoration 'shrub/shrubs/hedge/hedges/plant/plants' 'hedges' "The hedges are tall and thick, and completely close in the boundaries of the yard. " isPlural = true dobjFor(LookOver) { verify() { } action() { "The hedges are too tall. "; } } ; /* * This object tests and demonstrates using '*' and matchName to parse * non-dictionary words as object names. * * This isn't the kind of place you'd really use '*' in practice; for * this case, you'd be much better off defining all of the possible * color names as adjectives for the object and then using matchName() * to ignore matches to anything but the current one. This works better * because the color names would then exist as dictionary words, and * hence the parser would not complain about them being unknown when * this object is not in scope. */ + Sphere: Immovable noun = '*' 'sphere' name = (colors[curColor] + ' sphere') desc = "The <> sphere floats unnervingly in mid-air, with no apparent mechanical means of suspension. It does not hover like a balloon, but seems absolutely still, as though firmly attached to an invisible pole. " cannotTakeMsg = 'The sphere is strangely fixed in place; it resists your attempts to move it. It does seem to be able to rotate freely on its vertical axis, though, so you could probably turn it if you wanted to. ' cannotMoveMsg = (cannotTakeMsg) cannotPutInMsg = (cannotTakeMsg) initSpecialDesc = "In the center of the yard, floating in mid-air, is a <> sphere. " matchNameCommon(origTokens, adjustedTokens) { /* check each token */ foreach (local cur in origTokens) { /* * if it's 'sphere' or our current color name, match it; * otherwise, we don't have a match */ if (getTokVal(cur) not in ('sphere', colors[curColor])) return nil; } /* all of our tokens match */ return self; } dobjFor(Turn) { verify() { } action() { ++curColor; if (curColor > colors.length()) curColor = 1; "Although the sphere is immovably fixed in space, it rotates so freely that it feels weightless. You give it the lightest touch, and it starts spinning wildly, so fast that it becomes a featureless, glowing blur. After a few moments, it abruptly comes to a stop, and you see that its color has changed to <>. "; } } /* current index in color list */ curColor = 1 /* list of possible colors */ colors = ['red', 'blue', 'green', 'orange', 'yellow', 'white'] ; + Enterable, Decoration ->yardDoor 'house' 'house' "It's a modest one-story house. A door leads in to the south. " ; + Enterable ->shedOuterDoor 'old garden shed' 'shed' "The shed's wood siding looks well weathered; only the thinnest layer of white paint remains. Its flimsy-looking door is <>. " ; + Decoration 'shed\'s wood siding' 'wood siding' "It looks weathered. " ; + shedOuterDoor: Door 'shed door' 'door of the shed' "It leads into the shed. " ; /* ------------------------------------------------------------------------ */ /* * Shed */ shed: Room 'Shed' 'the shed' "There's barely room to stand up or turn around in this tiny storage space. The north wall has a few deep shelves, but otherwise the structure is featureless. A door leads out to the southeast. " southeast = shedInnerDoor out asExit(southeast) ; + shedInnerDoor: Door ->shedOuterDoor 'shed door' 'door' "It leads out of the shed. " ; + shedShelves: Fixture, Surface 'deep shelf/shelves' 'shelf' "The shelves are about a yard deep and look sturdy. " ; ++ Flashlight 'flash light/flashlight' 'flashlight' "It's a big lantern-type flashlight. It's currently <>. " ; ++ Thing 'rat poison/box' 'box of rat poison' "It's a large box, missing its top, full of white powder. The box is labeled RAT POISON and is marked with a prominent skull-and-crossbones symbol, along with a legend in tiny type reading Use of this product in a manner inconsistent with its labeling is a violation of Adventure Game Law. " dobjFor(Eat) remapTo(Eat, ratPoison) lookInDesc = "It's full of white powder. " ; +++ ratPoison: Component, Food 'white powder' 'white powder' "The box is full of white powder. " cannotTakeMsg = 'You\'d best leave the poison in the box. ' cannotMoveMsg = 'You\'d best leave the poison in the box. ' cannotPutMsg = 'Best to leave the poison in the box. ' dobjFor(Pour) { verify() { } action() { "You'd best leave the poison in its box. "; } } dobjFor(PourInto) asDobjFor(Pour) dobjFor(PourOnto) asDobjFor(Pour) dobjFor(Eat) { preCond = [touchObj] verify() { dangerous; } action() { "It's not very tasty, but it sure is deadly. "; /* terminate the game */ finishGameMsg(ftDeath, [finishOptionUndo, finishOptionCredits]); } } ; class MyMatch: Matchstick 'match*matches' 'match' ; ++ Matchbook 'match book/matches/matchbook*matches' 'book of matches' matchName(origTokens, adjustedTokens) { /* * if the name is just 'match', ignore it - we want to allow * 'match' as an adjective because we want to match 'match * book', but just plain 'match' is very unlikely to mean us */ if (origTokens.length() == 1 && getTokVal(origTokens[1]) == 'match') return nil; /* inherit default handling */ return inherited(origTokens, adjustedTokens); } /* put this after the individual matches when we match a plural */ pluralOrder = 110 ; +++ MyMatch; +++ MyMatch; +++ MyMatch; +++ MyMatch; +++ MyMatch; /* ------------------------------------------------------------------------ */ /* * Additional verbs */ DefineTAction(Inflate); VerbRule(Inflate) ('inflate' | 'blow' 'up') dobjList | 'blow' dobjList 'up' : InflateAction verbPhrase = 'inflate/inflating (what)' ; DefineTAction(Deflate); VerbRule(Deflate) 'deflate' dobjList : DeflateAction verbPhrase = 'deflate/deflating (what)' ; DefineTAction(LookOver); VerbRule(LookOver) 'look' 'over' dobjList : LookOverAction verbPhrase = 'look/looking (over what)' ; DefineTAction(Ride); VerbRule(Ride) 'ride' singleDobj : RideAction verbPhrase = 'ride/riding (what)' ; DefineTAction(Answer); VerbRule(Answer) 'answer' dobjList : AnswerAction verbPhrase = 'answer/answering (what)' ; modify Thing dobjFor(Inflate) { preCond = [touchObj] verify() { illogical('{That dobj/he} {is}n\'t something {you/he} can inflate. '); } } dobjFor(Deflate) { preCond = [touchObj] verify() { illogical('{That dobj/he} {is}n\'t something {you/he} can deflate. '); } } dobjFor(LookOver) { preCond = [objVisible] verify() { illogical('{You/he} see{s} nothing unusual. '); } } dobjFor(Ride) { verify() { illogical('{That dobj/he} {is}n\'t something {you/he} can ride. '); } } dobjFor(Answer) { preCond = [objVisible] verify() { illogical('{The dobj/he} didn\'t ask {you/him} anything. '); } } ; DefineTopicAction(ThinkAbout) execAction() { "You think about <>. "; } ; VerbRule(ThinkAbout) 'think' 'about' singleTopic : ThinkAboutAction verbPhrase = 'think/thinking (about what)' ; /* ------------------------------------------------------------------------ */ /* * Basement tunnel */ tunnel: DarkRoom 'Tunnel' 'the tunnel' "This low, narrow tunnel is walled in old, dark gray concrete or something similar. The walls curve inward at shoulder level to form an arched ceiling just barely high enough at the center to allow walking without bending your neck. The passage ends to the north at an iron door, and ends at the south in an earthen wall. A narrow side passage leads west. " north = tunnelDoor west = sideTunnel /* * since we have special walls and ceiling that are specifically * included as normal decoration objects in this location, keep only * the default floor among our room parts */ roomParts = [defaultFloor] ; + tunnelCeiling: Decoration 'arched concrete cement ceiling roof' 'ceiling' "It's very low, and arches in from the walls. " ; + tunnelWalls: Decoration 'east west tunnel concrete cement wall/walls' 'tunnel wall' "The walls are made of some kind of dark gray concrete. " ; + earthenWall: Decoration 'earthen dirt south wall' 'earthen wall' "It's just a wall of dirt, as though construction on the tunnel had been abruptly halted here. " ; + tunnelDoor: IronDoor ->stairDoor; /* ------------------------------------------------------------------------ */ /* * Side tunnel */ sideTunnel: DarkRoom 'Jagged Tunnel' 'the jagged tunnel' "This is an extremely narrow tunnel; it looks like construction was abandoned before it was completed. The passage is jagged, but continues a little way to the east. A rough ladder has been improvised from wood scraps and fastened to the wall, leading up to a small trap door above. " up = tunnelTrapDoor east = tunnel roomParts = [defaultFloor] getExtraScopeItems(actor) { /* * if they arrived from above, make sure the trap door and * ladder are in scope */ if (lastArrivedVia[actor] == tunnelTrapDoor) return inherited(actor) + tunnelTrapDoor + tunnelLadder; else return inherited(actor); } /* table giving last arrival connector for each actor present */ lastArrivedVia = static new LookupTable(4, 4) travelerArriving(traveler, origin, connector, backConnector) { /* note the connector by which they're arriving */ foreach (local actor in traveler.getTravelerActors()) lastArrivedVia[actor] = backConnector; /* inherit default handling */ inherited(traveler, origin, connector, backConnector); } ; + tunnelTrapDoor: Fixture, Door 'small trap door' 'trap door' ; + tunnelLadder: StairwayUp 'rough improvised wood ladder/scraps' 'ladder' dobjFor(TravelVia) remapTo(TravelVia, tunnelTrapDoor) ; + Decoration 'west north south tunnel dirt jagged wall/walls' 'tunnel wall' "The walls are just bare dirt. " ; /* ------------------------------------------------------------------------ */ /* * A generic USE verb of two objects. We'll remap this to a more * specific verb when possible, based on the direct object, using the * direct object's remapDobjUse() method. */ DefineTIAction(UseWith) /* we want to remap on the direct object, so we must resolve it first */ resolveFirst = DirectObject ; VerbRule(UseWith) 'use' singleDobj ('on' | 'with') singleIobj: UseWithAction verbPhrase = 'use/using (what) (on what)' ; VerbRule(UseWithWhat) 'use' singleDobj: UseWithAction verbPhrase = 'use/using (what) (on what)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = withSingleNoun; } ; modify Thing dobjFor(UseWith) { verify() { } } iobjFor(UseWith) { verify() { illogical('Please be more specific. '); }} ; /* * USE WITH maps to LOCK/UNLOCK WITH */ modify Key dobjFor(UseWith) { verify() { verifyIobjLockWith(); } preCond() { return preCondIobjLockWith(); } remap() { /* * If we have a single tentative indirect object, check to * see if it's locked or unlocked, and use the appropriate * verb (LOCK or UNLOCK) to reverse its state. If we can't * yet identify the indirect object, guess that it's UNLOCK. */ if (gTentativeIobj.length() == 1) { /* * If the object is locked, unlock it; otherwise lock * it. Note that we must reverse the dobj/iobj roles in * the remapped verb, because the key is the direct * object of USE but the indirect object of LOCK/UNLOCK. */ if (gTentativeIobj[1].obj_.isLocked) return [UnlockWithAction, OtherObject, self]; else return [LockWithAction, OtherObject, self]; } else { /* guess that it's UNLOCK */ return [UnlockWithAction, OtherObject, self]; } } } ; /* ------------------------------------------------------------------------ */ /* * "fill x with y" - treat this as "put y in x". This verb isn't really * useful in this sample game; it's here only as a demonstration of * using the TIAction remapping macros. */ DefineTIAction(FillWith) resolveFirst = DirectObject ; VerbRule(FillWith) 'fill' singleDobj 'with' iobjList : FillWithAction verbPhrase = 'fill/filling (what) (with what)' ; modify Thing dobjFor(FillWith) remapTo(PutIn, IndirectObject, self); ; /* ------------------------------------------------------------------------ */ /* * Main startup object. This lets us customize the introductory text, * set the player character object, and control the game's startup * procedure. */ gameMain: GameMainDef /* the initial (and only) player character is 'me' */ initialPlayerChar = me /* show our introduction message */ showIntro() { /* show our simple introduction message */ "Welcome to the TADS 3 Library Sample Game!\b"; } /* * Create an "about box". * * The tag has to be re-displayed each time we clear the * screen, since the tag is part of the main text window's output * stream and thus only lasts as long as the rest of the text on the * main game screen. Fortunately, the library will take care of this * for us automatically, as long as we do two things. First, we have * to put our tag here, in this method, so that the * library knows how to re-display the tag when necessary. Second, * we must always use the cls() function (NOT the low-level * clearScreen() function) whenever we want to clear the game window. */ setAboutBox() { "
T3 Library Sampler

This is the T3 Library Sample Game. It's just an example of how to create a game using the T3 standard library (also known as adv3).
"; } /* show our end-of-game message */ showGoodbye() { "<.p>Thanks for playing the Library Sample Game!\b"; } ; /* ------------------------------------------------------------------------ */ /* * random test verbs */ VerbRule(rtfuse) 'test-rtfuse' : IAction execAction() { "Starting a real-time fuse for 10 seconds... "; new RealTimeFuse(testFuse, &execEvent, 10000); } ; VerbRule(rtdaemon) 'test-rtdaemon' : IAction execAction() { "Starting a real-time daemon for 10 second... "; new RealTimeDaemon(testDaemon, &execEvent, 10000); } ; VerbRule(moreprompt) 'moreprompt' : IAction execAction() { "Pausing for a MORE prompt, leaving the real-time clock running.\n"; inputManager.pauseForMore(nil); } ; VerbRule(morefreeze) 'morefreeze' : IAction execAction() { "Pausing for a MORE prompt, freezing the real-time clock.\n"; inputManager.pauseForMore(true); } ; VerbRule(cls) 'cls' : IAction execAction() { "Clearing the screen..."; cls(); } ; VerbRule(dialog) 'dialog' : IAction execAction() { local btn = inputManager.getInputDialog( InDlgIconQuestion, 'What did you have for breakfast this morning?', ['&Eggs', '&Cereal', '&Fruit', '&Other'], 1, 4); "You selected button <>! "; } ; VerbRule(dialog2) 'ldialog' : IAction execAction() { local btn = inputManager.getInputDialog( InDlgIconQuestion, 'Here\'s a much taller dialog. This one will go on for several lines, to make sure that the layout looks right with long text contents as well as a short one-liner. \n\n And this just goes on and on and on, doesn\'t it? Well, that\'s the whole idea. This dialog also uses standard buttons instead of the explicitly labeled buttons of the dialog you get with the "dialog" command, to test that the standard button setup is working properly. Well, that should probably be long enough!!!', InDlgOkCancel, 1, 2); "You selected button <>! "; } ; VerbRule(fuse) 'test-fuse' : IAction execAction() { "Starting a fuse for three turns... "; new Fuse(testFuse, &execEvent, 3); } ; testFuse: object execEvent() { "<.commandsep>This is the test fuse!<.commandsep>"; } ; VerbRule(daemon) 'test-daemon': IAction execAction() { "Starting daemon that will run three times, every other turn... "; new Daemon(testDaemon, &execEvent, 2); testDaemon.counter = 0; } ; VerbRule(getkey) 'getkey': IAction execAction() { local key; /* get a key */ key = inputManager.getKey(true, {: "<.p>Enter a key:\ " }); /* show the key */ "\nThanks! You typed '<>'!\n"; nestedActorAction(bob, Take, bigRedBall); } ; VerbRule(showlinks) 'showlinks': IAction execAction() { "Here are some links:

  • color << aHref('outside', 'outside')>> the link
  • color << aHref('inside', 'inside')>> the link
  • a mix of << aHref('mix', 'outside and inside the link') >> for extra completeness
"; } ; testDaemon: object execEvent() { "\bThis is the test daemon! "; #if 0 "<.p>*** You have died ***\n"; finishGame([finishOptionUndo, finishOptionCredits, finishOptionFullScore]); #endif /* if I've run three times now, cancel myself */ if (++counter == 3) eventManager.removeCurrentEvent(); } counter = 0 ; VerbRule(nbspTest) 'nbsptest' : IAction execAction() { "(This test is designed for 80-column displays.) \b This is a long line intended to wrap after a bit. The trick is that here to here (i.e., the parts between the first and second 'here') should be non-breaking: 'here to here' should all be on one line, even though there are spaces between the words. Anyway, so much for the test. "; } ; VerbRule(wrapTest) 'wraptest' : IAction execAction() { "(This test is designed for 80-column displays.) \b This is a long line intended to wrap after.\u2002First, let's test soft hy­phen­ation. Okay, we should have hyphenated somewhere in there. \b Next, let us make sure that regular hyphen wrapping still works. What we need is for this line to wrap after 'need'.\n Next, let us make sure that regular hyphen wrapping still works. What we need -- and this is key -- is to make sure this line wraps before 'need'.\n Next, let us make sure that regular hyphens still work. This time use hard-hyphen-wrapping, where we break at a hyphen in the middle of a word. \b Now we can test some new stuff. First, test the explicit break flag: We\u200bCan\u200bBreak\u200bAt\u200bAny\u200bOf\u200bThese"; "\u200bInter\u200bCaps\u200bCharacters\u200bAnd\u200bWe\u200bCan\u200bGo"; "\u200bOn\u200bLike\u200bThis\u200bFor\u200bQuite\u200bSome\u200bTime"; "\u200bEven\u200bTo\u200bThe\u200bExtent\u200bOf\u200bWrapping\u200bAnother"; "\u200bLine\u200bOf\u200bText! \b Next, let's test that explicit non-break points are working. We'll want to\ufeff \ufeffbreak just before the previous word 'break', but we have a non-breaking zero-width space after the 'to' and another before the 'break', which should prevent breaking there.\n Likewise, we'll want to break this line at a hyphen, coming up right here-\ufeffabsolutely. But we have a non-break right after the hyphen, which should tell us not to break there.\n For comparison, this line we do want to break at the hyphen, which is here-absolutely once again. This time we should break after the hyphen. \b The next test is for East Asian language wrapping.WeAreNowIn"; "CharacterWrapMode,WhichMeansThatWeCanWrapBetweenAnyTwoCharactersExcept"; "WhereOtherwiseNotedWithAnExplicitNonBreakingIndicator\ufeff.This is back in English-style text, which means we should wrap on word boundaries.AndNowWe'reBackInCharacterWrappingMode.TheTrickIsThat"; "WeWantToCheckTransitionsBetweenTheTwoModes.Such as here, where we are back in word mode.OrHereWithCharInTheMiddleOfALine."; "We're back in word-wrap mode here.\u2002The remaining test is to put word-wrap mode in the middle of a line ofCharacterWrapped"; "Text,SuchAsThis(here is some word-wrapped text)AndNow"; "WeAreBackToCharacterWrappedTextToTheEndOfTheLine.ATestWeHaveYetToTryWith"; "CharWrappedTextIs\ufeff.\ufeff.\ufeff.ExplicitNonBreakingIndicators. (That ellipsis should be all together, not broken across lines, and should furthermore stick to the letter just before it, so the ellipsis doesn't start a new line.)\n \b This is just a test of underlining some typographical spaces: let's try an en space:\u2002an em space:\u2003a thin space:\u2009a punctuation space:\u2008a figure space:\u2007a hair space:\u200a...and that should do it.\n And now let's try something similar:\u2002 ending the line with an en plus an em:\u2002\u2003This is to demonstrate that we trim trailing whitespace, even if the whitespace consists of typographical spaces.\n Another similar test:\u2002This time, some non-breaking spaces:\uFEFF\u2002\uFEFF\u2003\uFEFF\n That ended with an en plus em again, but these were quoted with FEFF's. \n \b

ThisIsSomeTextInATableInCharacterWrapMode.TheIdeaIsTo"; "SeeHowTablesDealWithThisSortOfThing.TablesUseTheBasicWrappingMechanism"; "LikeEverythingElse,ButHaveSomeSpecialCodeToMeasureTheSizeOfTheTextThat"; "WillGoInThem. This is a second column that uses character-based wrapping instead of word-based wrapping. The two columns should more or less balance out, although the word-based column will want a bigger share because of its larger minimum width.
\n"; } ; VerbRule(EventTest) 'inputevent' : IAction execAction() { local evt = inputManager.getEvent(true, {: "Waiting for event..." }); " Got it!\n"; local name = [InEvtKey -> 'key', InEvtHref -> 'href', InEvtEof -> 'eof'][evt[1]]; "Event type=<> (<>), param=<< evt.length() > 1 ? evt[2] : ''>> "; } ; VerbRule(logAbout) 'logabout' : IAction execAction() { "Logging the ABOUT command to About.txt...\n"; LogConsole.captureToFile('About.txt', getLocalCharSet(CharsetDisplay), 80, {: AboutAction.execSystemAction() }); } ; /* ------------------------------------------------------------------------ */ /* * A rather contrived preparser. * * This preparser allows the player to set "aliases" using syntax like * this: * * alias $xxx yyy * * where xxx is the name of an alias variable, and yyy is the * replacement text for the variable. Once an alias has been created, * it can be used in any command simply by naming it with the '$ prefix. */ StringPreParser /* pre-compile our search patterns */ patDefAlias = static new RexPattern( '*alias+(+)+(.+)') patUseAlias = static new RexPattern( '(<^dollar>*)(+)(.*)') doParsing(str, which) { local ret; local aliasName; local aliasText; /* check for the alias definition syntax */ if (rexMatch(patDefAlias, str) != nil) { /* extract the alias name and the replacement text */ aliasName = rexGroup(1)[3]; aliasText = rexGroup(2)[3]; /* add the alias to our table */ aliasTable[aliasName] = aliasText; /* mention that the alias has been defined */ "Okay, $<> has been defined. "; /* this command has been fully handled */ return nil; } /* we're not defining an alias, so look for aliases to replace */ ret = nil; for (;;) { /* look for a '$' in the balance of the input string */ if (rexMatch(patUseAlias, str) != nil) { local prv; local nxt; /* * extract the text before the alias, the name of the * alias, and the rest of the text after the alias */ prv = rexGroup(1)[3]; aliasName = rexGroup(2)[3]; nxt = rexGroup(3)[3]; /* add the previous text to the output string so far */ if (ret != nil) ret += prv; else ret = prv; /* * translate the alias; if it has no translation, then * use the original text of the alias, including the * '$', unchanged */ aliasText = aliasTable[aliasName]; if (aliasText == nil) aliasText = '$' + aliasName; /* add the translated alias to the output string so far */ ret += aliasText; /* continue parsing the balance of the input string */ str = nxt; } else { /* * We have no more aliases in the string - the balance * of the string is simply to be returned unchanged. If * we've already built some output text, add the balance * of the input to the output so far; otherwise, the * balance of the input is the entirety of the output. */ if (ret != nil) ret += str; else ret = str; /* we're done with the text */ break; } } /* return the output string */ return ret; } /* our lookup table of defined aliases */ aliasTable = static new LookupTable() ; frobtads-1.2.3/tads3/vmimgrb.h0000644000175000001440000001261207627507726015362 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimgrb.h - image rebuilder Function Re-builds an image file from the loaded program's state. Useful for writing an image file after pre-initialization has bee completed. Notes Modified 07/21/99 MJRoberts - Creation */ #ifndef VMIMGRB_H #define VMIMGRB_H #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * Rewrite the image file. Copies an original image file to the given * file. */ void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp, ulong static_cs_start_ofs); /* ------------------------------------------------------------------------ */ /* * Object-to-Constant Mapper. * * When we're rebuilding an image file, we might want to convert all * string and list objects (i.e., instances of metaclass String or List) * into constant string and list values, respectively, adding these * values to the image file's constant pool rather than storing them as * object instances. Using constant pool data in an image file * increases a program's efficiency by reducing the number of active * instances required at run-time. * * During the conversion process, we need to store the constant data * that will go in the additional constant pool pages. We also need to * maintain a translation table that gives us the constant pool address * of each converted string or list value given its object ID. This * object maintains these tables. */ class CVmConstMapper { public: CVmConstMapper(VMG0_); ~CVmConstMapper(); /* * Get an object's constant pool address. Returns zero if the * object does not have an address mapped yet. */ ulong get_pool_addr(vm_obj_id_t obj_id); /* * Reserve space for an object's data in the new constant pool. * Returns the constant pool address of the reserved space, and adds * a translation mapping for the object to our table, so that future * calls to get_pool_addr(obj_id) will return this address. * * If the object is too large to fit on a constant pool page, we'll * return zero to indicate that the object cannot be stored in the * constant pool; in this case, the object must remain an object * instance rather than a constant item. * * This doesn't actually copy any data, but merely reserves space. */ ulong alloc_pool_space(vm_obj_id_t obj_id, size_t len); /* * Prepare to begin storing data. This must be called once, after * all pool space has been reserved and before the first object's * data are actually stored. */ void prepare_to_store_data(); /* * Store the object's data in its pre-reserved space in the rebuilt * constant pool. The space must have previously been allocated * with alloc_pool_space(), and the size used must be exactly the * same as the space allocated originally. */ void store_data(vm_obj_id_t obj_id, const void *ptr, size_t len); /* * get the number of pages we've stored - this is valid only after * prepare_to_store_data() has been called */ size_t get_page_count() const { return pages_cnt_; } /* * Write our pages to an image file. This routine can be called * only after all of the objects have been stored in the constant * pool. */ void write_to_image_file(class CVmImageWriter *writer, uchar xor_mask); protected: /* * Determine how many extra pages we need to add to the original * image file's constant pool for our mapped data. */ size_t calc_page_count() const { /* * calculate the total number of pages by dividing the page size * into the total number of bytes we've allocated, rounded up to * the next page whole page */ return (size_t)((next_free_ + (page_size_ - 1) - base_addr_) / page_size_); } /* * index of our first page - this will be the next page after the * last page used in the original image file */ size_t first_page_idx_; /* constant pool page size */ size_t page_size_; /* first address that we allocated */ ulong base_addr_; /* next free address for the allocation pass */ ulong next_free_; /* amount of space remaining on current pool allocation page */ size_t rem_; /* * Translation page array - each element of this array points to a * page that contains 1024 object ID translations. */ ulong **obj_addr_; /* number of page slots in obj_addr_ array */ size_t obj_addr_cnt_; /* * constant pool page data - we allocate the pages in * prepare_to_store_data(), which is called after we know how many * pages we'll need */ struct vm_const_mapper_page **pages_; size_t pages_cnt_; }; /* * mapper constant pool page */ struct vm_const_mapper_page { /* * high-water mark for data stored in the page, as an offset from * the start of the page's data */ size_t max_ofs_used; /* the page's data */ char buf[1]; }; #endif /* VMIMGRB_H */ frobtads-1.2.3/tads3/vmfilobj.cpp0000644000175000001440000026463612145504453016062 0ustar realncusers/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfilobj.h - File object metaclass Function Implements an intrinsic class interface to operating system file I/O. Notes Modified 06/28/01 MJRoberts - Creation */ #include #include "t3std.h" #include "charmap.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmbif.h" #include "vmcset.h" #include "vmstack.h" #include "vmmeta.h" #include "vmrun.h" #include "vmglob.h" #include "vmfile.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmdate.h" #include "vmfilobj.h" #include "vmpredef.h" #include "vmundo.h" #include "vmbytarr.h" #include "vmbignum.h" #include "vmbiftad.h" #include "vmhost.h" #include "vmimage.h" #include "vmnetfil.h" #include "vmnet.h" #include "vmfilnam.h" #include "vmhash.h" #include "vmpack.h" #include "sha2.h" #include "md5.h" /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassFile metaclass_reg_obj; CVmMetaclass *CVmObjFile::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjFile:: *CVmObjFile::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjFile::getp_undef, /* index 0 */ &CVmObjFile::getp_open_text, /* 1 */ &CVmObjFile::getp_open_data, /* 2 */ &CVmObjFile::getp_open_raw, /* 3 */ &CVmObjFile::getp_get_charset, /* 4 */ &CVmObjFile::getp_set_charset, /* 5 */ &CVmObjFile::getp_close_file, /* 6 */ &CVmObjFile::getp_read_file, /* 7 */ &CVmObjFile::getp_write_file, /* 8 */ &CVmObjFile::getp_read_bytes, /* 9 */ &CVmObjFile::getp_write_bytes, /* 10 */ &CVmObjFile::getp_get_pos, /* 11 */ &CVmObjFile::getp_set_pos, /* 12 */ &CVmObjFile::getp_set_pos_end, /* 13 */ &CVmObjFile::getp_open_res_text, /* 14 */ &CVmObjFile::getp_open_res_raw, /* 15 */ &CVmObjFile::getp_get_size, /* 16 */ &CVmObjFile::getp_get_mode, /* 17 */ &CVmObjFile::getp_getRootName, /* 18 */ &CVmObjFile::getp_deleteFile, /* 19 */ &CVmObjFile::getp_setMode, /* 20 */ &CVmObjFile::getp_packBytes, /* 21 */ &CVmObjFile::getp_unpackBytes, /* 22 */ &CVmObjFile::getp_sha256, /* 23 */ &CVmObjFile::getp_digestMD5 /* 24 */ }; /* * Vector indices - we only need to define these for the static functions, * since we only need them to decode calls to call_stat_prop(). */ enum vmobjfil_meta_fnset { VMOBJFILE_OPEN_TEXT = 1, VMOBJFILE_OPEN_DATA = 2, VMOBJFILE_OPEN_RAW = 3, VMOBJFILE_OPEN_RES_TEXT = 14, VMOBJFILE_OPEN_RES_RAW = 15, VMOBJFILE_GET_ROOT_NAME = 18, VMOBJFILE_DELETE_FILE = 19, }; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjFile::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { /* * we can't be created with 'new' - we can only be created via our * static creator methods (openTextFile, openDataFile, openRawFile) */ err_throw(VMERR_BAD_DYNAMIC_NEW); /* not reached, but the compiler might not know that */ AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set) { /* instantiate the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjFile(); /* files are always transient */ G_obj_table->set_obj_transient(id); /* return the new ID */ return id; } /* * Create with the given character set object and file handle. */ vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set, CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf) { /* instantiate the object */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmObjFile( vmg_ netfile, charset, fp, mode, access, create_readbuf); /* files are always transient */ G_obj_table->set_obj_transient(id); /* return the ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjFile::CVmObjFile(VMG_ CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, int mode, int access, int create_readbuf) { /* allocate and initialize our extension */ ext_ = 0; alloc_ext(vmg_ netfile, charset, fp, 0, mode, access, create_readbuf); } /* * Allocate and initialize our extension */ void CVmObjFile::alloc_ext(VMG_ CVmNetFile *netfile, vm_obj_id_t charset, CVmDataSource *fp, unsigned long flags, int mode, int access, int create_readbuf) { /* * if we already have an extension, delete it (and release our * underlying system file, if any) */ notify_delete(vmg_ FALSE); /* * Figure the needed size. We need at least the standard extension; * if we also need a read buffer, figure in space for that as well. */ size_t siz = sizeof(vmobjfile_ext_t); if (create_readbuf) siz += sizeof(vmobjfile_readbuf_t); /* allocate space for our extension structure */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this); /* store the data we received from the caller */ get_ext()->netfile = netfile; get_ext()->fp = fp; get_ext()->charset = charset; get_ext()->flags = flags; get_ext()->mode = (unsigned char)mode; get_ext()->access = (unsigned char)access; /* * point to our read buffer, for which we allocated space contiguously * with and immediately following our extension, if we have one */ if (create_readbuf) { /* point to our read buffer object */ vmobjfile_readbuf_t *rb = (vmobjfile_readbuf_t *)(get_ext() + 1); get_ext()->readbuf = rb; /* initialize the read buffer with no initial data */ rb->rem = 0; rb->ptr = rb->buf; } else { /* there's no read buffer at all */ get_ext()->readbuf = 0; } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjFile::notify_delete(VMG_ int /*in_root_set*/) { /* if we have an extension, clean it up */ if (ext_ != 0) { /* close our file if we have one */ if (get_ext()->fp != 0) delete get_ext()->fp; /* close the network file */ err_try { if (get_ext()->netfile != 0) get_ext()->netfile->close(vmg0_); } err_catch_disc { /* * Since we're being deleted by the garbage collector, there's * no way to relay this error to the bytecode program - it * evidently lost track of the file, so it must not care about * the ending disposition. Just discard the error. */ } err_end; /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* ------------------------------------------------------------------------ */ /* * Get the data source */ CVmDataSource *CVmObjFile::get_datasource() const { return get_ext()->fp; } /* * Get the file spec from the netfile */ void CVmObjFile::get_filespec(vm_val_t *val) const { /* presume we won't find a value */ val->set_nil(); /* if we have a net file object, get its filespec */ if (get_ext()->netfile != 0) val->set_obj(get_ext()->netfile->filespec); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjFile::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjFile::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * call a static property */ int CVmObjFile::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* look up the function */ uint midx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* * set up a blank recursive call descriptor, to be filled in when we * know which function we're calling */ vm_rcdesc rc(vmg_ "", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), (ushort)midx, G_stk->get(0), argc != 0 ? *argc : 0); /* translate the property into a function vector index */ switch(midx) { case VMOBJFILE_OPEN_TEXT: rc.name = "File.openTextFile"; return s_getp_open_text(vmg_ result, argc, FALSE, &rc); case VMOBJFILE_OPEN_DATA: rc.name = "File.openDataFile"; return s_getp_open_data(vmg_ result, argc, &rc); case VMOBJFILE_OPEN_RAW: rc.name = "File.openRawFile"; return s_getp_open_raw(vmg_ result, argc, FALSE, &rc); case VMOBJFILE_OPEN_RES_TEXT: rc.name = "File.openTextResource"; return s_getp_open_text(vmg_ result, argc, TRUE, &rc); case VMOBJFILE_OPEN_RES_RAW: rc.name = "File.openRawResource"; return s_getp_open_raw(vmg_ result, argc, TRUE, &rc); case VMOBJFILE_GET_ROOT_NAME: return s_getp_getRootName(vmg_ result, argc); case VMOBJFILE_DELETE_FILE: return s_getp_deleteFile(vmg_ result, argc); default: /* it's not one of ours - inherit from the base object metaclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * Static "open" methods */ int CVmObjFile::getp_open_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openTextFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_TEXT, G_stk->get(0), argc); return s_getp_open_text(vmg_ retval, argc, FALSE, &rc); } int CVmObjFile::getp_open_data(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openDataFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_DATA, G_stk->get(0), argc); return s_getp_open_data(vmg_ retval, argc, &rc); } int CVmObjFile::getp_open_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openRawFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RAW, G_stk->get(0), argc); return s_getp_open_raw(vmg_ retval, argc, FALSE, &rc); } int CVmObjFile::getp_open_res_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openTextResource", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RES_TEXT, G_stk->get(0), argc); return s_getp_open_text(vmg_ retval, argc, TRUE, &rc); } int CVmObjFile::getp_open_res_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_rcdesc rc(vmg_ "File.openRawResource", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_OPEN_RES_RAW, G_stk->get(0), argc); return s_getp_open_raw(vmg_ retval, argc, TRUE, &rc); } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjFile::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjFile::reload_from_image(VMG_ vm_obj_id_t /*self*/, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * load or re-load image data */ void CVmObjFile::load_image_data(VMG_ const char *ptr, size_t siz) { /* read our character set */ vm_obj_id_t charset = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; /* get the mode and access values */ int mode = (unsigned char)*ptr++; int access = (unsigned char)*ptr++; /* get the flags */ unsigned long flags = t3rp4u(ptr); /* * add in the out-of-sync flag, since we've restored the state from a * past state and thus we're out of sync with the external file system * environment */ flags |= VMOBJFILE_OUT_OF_SYNC; /* * Initialize our extension - we have no underlying network file * descriptor or native file handle, since the file is out of sync by * virtue of being loaded from a previously saved image state. Note * that we don't need a read buffer because the file is inherently out * of sync and thus cannot be read. */ alloc_ext(vmg_ 0, charset, 0, flags, mode, access, FALSE); } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjFile::save_to_file(VMG_ class CVmFile *fp) { /* files are always transient, so should never be saved */ assert(FALSE); } /* * restore from a file */ void CVmObjFile::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { /* files are always transient, so should never be saved */ } /* ------------------------------------------------------------------------ */ /* * Mark as referenced all of the objects to which we refer */ void CVmObjFile::mark_refs(VMG_ uint state) { /* mark our character set object, if we have one */ if (get_ext()->charset != VM_INVALID_OBJ) G_obj_table->mark_all_refs(get_ext()->charset, state); /* mark our network file descriptor's references */ if (get_ext()->netfile != 0) get_ext()->netfile->mark_refs(vmg_ state); } /* ------------------------------------------------------------------------ */ /* * Note that we're seeking within the file. */ void CVmObjFile::note_file_seek(VMG_ int is_explicit) { /* * if it's an explicit seek, invalidate our internal read buffer and * note that the stdio buffers have been invalidated */ if (is_explicit) { /* the read buffer (if we have one) is invalid after a seek */ if (get_ext()->readbuf != 0) get_ext()->readbuf->rem = 0; /* * mark the last operation as clearing stdio buffering - when we * explicitly seek, stdio automatically invalidates its internal * buffers */ get_ext()->flags &= ~VMOBJFILE_STDIO_BUF_DIRTY; } } /* * Update stdio buffering for a switch between read and write mode, if * necessary. Any time we perform consecutive read and write operations, * stdio requires us to explicitly seek when performing consecutive * dissimilar operations. In other words, if we read then write, we must * seek before the write, and likewise if we write then read. */ void CVmObjFile::switch_read_write_mode(VMG_ int writing) { /* if we're writing, invalidate the read buffer */ if (writing && get_ext()->readbuf != 0) get_ext()->readbuf->rem = 0; /* * if we just performed a read or write operation, we must seek if * we're performing the opposite type of operation now */ if ((get_ext()->flags & VMOBJFILE_STDIO_BUF_DIRTY) != 0) { int was_writing; /* check what type of operation we did last */ was_writing = ((get_ext()->flags & VMOBJFILE_LAST_OP_WRITE) != 0); /* * if we're switching operations, explicitly seek to the current * location to flush the stdio buffers */ if ((writing && !was_writing) || (!writing && was_writing)) get_ext()->fp->seek(get_pos(vmg0_), OSFSK_SET); } /* * remember that this operation is stdio-buffered, so that we'll know * we need to seek if we perform the opposite operation after this one */ get_ext()->flags |= VMOBJFILE_STDIO_BUF_DIRTY; /* remember which type of operation we're performing */ if (writing) get_ext()->flags |= VMOBJFILE_LAST_OP_WRITE; else get_ext()->flags &= ~VMOBJFILE_LAST_OP_WRITE; } /* ------------------------------------------------------------------------ */ /* * Retrieve the filename argument, and optionally the access mode argument. * Sets up network storage server access to the file, if applicable. * Returns a network/local file descriptor that the caller can use to open * the local file system object. * * If '*access' is zero at entry, we'll retrieve the access argument from * the stack for a non-resource file. (Resource files always use access * mode READ, so there's no extra argument for those.) If '*access' is * non-zero, we'll simply use the given access mode. * * This leaves the argument values on the stack - the caller must discard * when done with them. */ CVmNetFile *CVmObjFile::get_filename_and_access( VMG_ const vm_rcdesc *rc, int *access, int is_resource_file, os_filetype_t file_type, const char *mime_type) { /* * If it's a resource file, the access mode is always READ, and there's * no access mode argument. If the caller provided a non-zero access * mode, there's also no argument - we just use the caller's mode. If * the caller specified zero as the mode, it means we have to read the * mode from an extra integer argument. */ if (is_resource_file) { /* resource file - the only access mode possible is READ */ *access = VMOBJFILE_ACCESS_READ; } else if (*access == 0) { /* get the access mode from the stack */ *access = G_stk->get(1)->num_to_int(vmg0_); /* make sure it's within the valid range for bytecode access codes */ if (*access > VMOBJFILE_ACCESS_USERMAX) err_throw(VMERR_BAD_VAL_BIF); } /* get the filename from the first argument */ return get_filename_arg( vmg_ G_stk->get(0), rc, *access, is_resource_file, file_type, mime_type); } /* * Get the filename for a given argument value and access mode */ CVmNetFile *CVmObjFile::get_filename_from_obj( VMG_ vm_obj_id_t obj, const vm_rcdesc *rc, int access, os_filetype_t file_type, const char *mime_type) { vm_val_t arg; arg.set_obj(obj); return get_filename_arg(vmg_ &arg, rc, access, FALSE, file_type, mime_type); } /* * Get the filename for a given argument value and access mode */ CVmNetFile *CVmObjFile::get_filename_arg( VMG_ const vm_val_t *arg, const vm_rcdesc *rc, int access, int is_resource_file, os_filetype_t file_type, const char *mime_type) { /* figure the network file access mode */ int nmode; switch (access) { case VMOBJFILE_ACCESS_READ: /* read access; file must exist */ nmode = NETF_READ; break; case VMOBJFILE_ACCESS_WRITE: /* write access; create or replace existing file */ nmode = NETF_WRITE | NETF_CREATE | NETF_TRUNC; break; case VMOBJFILE_ACCESS_RW_KEEP: /* read/write access; keep an existing file or create a new one */ nmode = NETF_READ | NETF_WRITE | NETF_CREATE; break; case VMOBJFILE_ACCESS_RW_TRUNC: /* read/write access; truncate an existing file or create a new one */ nmode = NETF_READ | NETF_WRITE | NETF_CREATE | NETF_TRUNC; break; case VMOBJFILE_ACCESS_DELETE: /* delete mode */ nmode = NETF_DELETE; break; case VMOBJFILE_ACCESS_GETINFO: case VMOBJFILE_ACCESS_MKDIR: case VMOBJFILE_ACCESS_RMDIR: case VMOBJFILE_ACCESS_READDIR: case VMOBJFILE_ACCESS_RENAME_TO: case VMOBJFILE_ACCESS_RENAME_FROM: /* * getFileType/getFileInfo/rename/directory manipulation - we don't * need to open the file at all for these operations */ nmode = 0; break; default: /* invalid mode */ err_throw(VMERR_BAD_VAL_BIF); } /* we don't have a network file descriptor yet */ CVmNetFile *netfile = 0; char fname[OSFNMAX]; /* check to see what kind of file spec we have */ if (is_resource_file) { /* a resource file must be given as a string name - retrieve it */ CVmBif::get_str_val_fname(vmg_ fname, sizeof(fname), CVmBif::get_str_val(vmg_ arg)); /* resources are always local, so create a local file descriptor */ netfile = CVmNetFile::open_local(vmg_ fname, 0, nmode, file_type); } else { /* regular file - create the network file descriptor */ netfile = CVmNetFile::open( vmg_ arg, rc, nmode, file_type, mime_type); } /* * If this isn't a resource file, check the file safety mode to see if * the operation is allowed. Reading resources is always allowed, * regardless of the safety mode, since resources are read-only and are * inherently constrained in the paths they can access. */ if (!is_resource_file) { err_try { check_safety_for_open(vmg_ netfile, access); } err_catch_disc { /* abandon the net file descriptor and bubble up the error */ netfile->abandon(vmg0_); err_rethrow(); } err_end; } /* return the network file descriptor */ return netfile; } /* * Resolve a special file ID to a local filename path */ int CVmObjFile::sfid_to_path(VMG_ char *buf, size_t buflen, int32_t sfid) { const char *fname = 0; char path[OSFNMAX]; switch (sfid) { case SFID_LIB_DEFAULTS: /* library defaults file - T3_APP_DATA/settings.txt */ fname = "settings.txt"; goto app_data_file; case SFID_WEBUI_PREFS: /* Web UI preferences - T3_APP_DATA/webprefs.txt */ fname = "webprefs.txt"; goto app_data_file; app_data_file: /* get the system application data path */ G_host_ifc->get_special_file_path( path, sizeof(path), OS_GSP_T3_APP_DATA); /* add the filename */ os_build_full_path(buf, buflen, path, fname); /* success */ return TRUE; default: /* invalid ID value */ buf[0] = '\0'; return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Pop a character set mapper argument. If there's no argument, we'll * create and return a mapper for the default character set. */ vm_obj_id_t CVmObjFile::get_charset_arg(VMG_ int argn, int argc) { /* presume we won't find a valid argument value */ vm_obj_id_t obj = VM_INVALID_OBJ; /* if there's an argument, pop it */ if (argn < argc) obj = CVmBif::get_charset_obj(vmg_ argn); /* if we don't have a character set yet, create the default */ if (obj == VM_INVALID_OBJ) { /* get the default local character set for file contents */ char csname[32]; os_get_charmap(csname, OS_CHARMAP_FILECONTENTS); /* create the mapper */ obj = CVmObjCharSet::create(vmg_ FALSE, csname, strlen(csname)); } /* return the character set */ return obj; } /* ------------------------------------------------------------------------ */ /* * Static property evaluator - open a text file */ int CVmObjFile::s_getp_open_text(VMG_ vm_val_t *retval, uint *in_argc, int is_resource_file, const vm_rcdesc *rc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc_file(2, 1); static CVmNativeCodeDesc desc_res(1, 1); if (get_prop_check_argc(retval, in_argc, is_resource_file ? &desc_res : &desc_file)) return TRUE; /* * retrieve the filename and access mode arguments, and set up network * storage server access if applicable */ int access = 0; CVmNetFile *netfile = get_filename_and_access( vmg_ rc, &access, is_resource_file, OSFTTEXT, "text/plain"); err_try { /* presume we won't need a read buffer */ int create_readbuf = FALSE; /* retrieve the character set object */ vm_obj_id_t cset_obj = get_charset_arg(vmg_ 2, argc); /* push the character map object onto the stack for gc protection */ G_stk->push()->set_obj(cset_obj); /* open the file for reading or writing, as appropriate */ CVmDataSource *ds = 0; osfildef *fp = 0; switch(access) { case VMOBJFILE_ACCESS_READ: /* open a resource file or file system file, as appropriate */ if (is_resource_file) { /* it's a resource - open it */ unsigned long res_len; fp = G_host_ifc->find_resource( netfile->lclfname, strlen(netfile->lclfname), &res_len); /* * if we found the resource, note the start and end seek * positions, so we can limit reading of the underlying * file to the section that contains the resource data */ if (fp != 0) { /* * find_resource() handed us back a file handle * positioned at the start of the resource data stream, * possibly within a larger file; note the current * offset as the starting offset in case we need to * seek within the resource later */ long res_start = osfpos(fp); /* * note where the resource ends - it might be embedded * within a larger file, so we need to limit reading to * the extent from the resource map */ long res_end = res_start + res_len; /* create the data source */ ds = new CVmResFileSource(fp, res_start, res_end); } } else { /* * It's not a resource - open an ordinary text file for * reading. Even though we're going to treat the file as a * text file, open it in binary mode, since we'll do our * own universal newline translations; this allows us to * work with files in any character set, and using almost * any newline conventions, so files copied from other * systems will be fully usable even if they haven't been * fixed up to local conventions. */ fp = osfoprb(netfile->lclfname, OSFTTEXT); /* if that worked, create the data source */ if (fp != 0) ds = new CVmFileSource(fp); } /* make sure we opened it successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_not_found_exc, 0, "file not found"); /* we need a read buffer */ create_readbuf = TRUE; break; case VMOBJFILE_ACCESS_WRITE: /* open for writing */ fp = osfopwb(netfile->lclfname, OSFTTEXT); /* make sure we created it successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_creation_exc, 0, "error creating file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_KEEP: /* open for read/write, keeping existing contents */ fp = osfoprwb(netfile->lclfname, OSFTTEXT); /* make sure we were able to find or create the file */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); /* we need a read buffer */ create_readbuf = TRUE; break; case VMOBJFILE_ACCESS_RW_TRUNC: /* open for read/write, truncating existing contents */ fp = osfoprwtb(netfile->lclfname, OSFTTEXT); /* make sure we were successful */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); /* we need a read buffer */ create_readbuf = TRUE; break; default: /* invalid access mode */ fp = 0; err_throw(VMERR_BAD_VAL_BIF); } /* create our file object */ retval->set_obj(create(vmg_ FALSE, netfile, cset_obj, ds, VMOBJFILE_MODE_TEXT, access, create_readbuf)); /* * the network file descriptor is stored in the File object now, so * we no longer need to delete it */ netfile = 0; /* discard arguments and gc protection */ G_stk->discard(argc + 1); } err_finally { /* * If we created a network file descriptor and didn't hand it off * to the File object, we're abandoning the object. */ if (netfile != 0) netfile->abandon(vmg0_); } err_end; /* handled */ return TRUE; } /* * Static property evaluator - open a data file */ int CVmObjFile::s_getp_open_data(VMG_ vm_val_t *retval, uint *argc, const vm_rcdesc *rc) { /* use the generic binary file opener in 'data' mode */ return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_DATA, FALSE, rc); } /* * Static property evaluator - open a raw file */ int CVmObjFile::s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc, int is_resource_file, const vm_rcdesc *rc) { /* use the generic binary file opener in 'raw' mode */ return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_RAW, is_resource_file, rc); } /* * Generic binary file opener - common to 'data' and 'raw' files */ int CVmObjFile::open_binary(VMG_ vm_val_t *retval, uint *in_argc, int mode, int is_resource_file, const vm_rcdesc *rc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc file_desc(2); static CVmNativeCodeDesc res_desc(1); if (get_prop_check_argc(retval, in_argc, is_resource_file ? &res_desc : &file_desc)) return TRUE; /* retrieve the filename and access mode */ int access = 0; CVmNetFile *netfile = get_filename_and_access( vmg_ rc, &access, is_resource_file, OSFTBIN, "application/octet-stream"); err_try { osfildef *fp; CVmDataSource *ds = 0; /* open the file in binary mode, with the desired access type */ switch(access) { case VMOBJFILE_ACCESS_READ: /* open the resource or ordinary file, as appropriate */ if (is_resource_file) { unsigned long res_len; /* it's a resource - open it */ fp = G_host_ifc->find_resource( netfile->lclfname, strlen(netfile->lclfname), &res_len); /* * if we found the resource, note the start and end seek * positions, so we can limit reading of the underlying * file to the section that contains the resource data */ if (fp != 0) { /* * find_resource() hands us a file handle positioned at * the start of the resource data - note the seek * offset in case we need to seek around within the * resource stream later on */ long res_start = osfpos(fp); /* * note the end of the resource, in case it's embedded * within a larger physical file */ long res_end = res_start + res_len; /* create the data source */ ds = new CVmResFileSource(fp, res_start, res_end); } } else { /* open the ordinary file in binary mode for reading only */ fp = osfoprb(netfile->lclfname, OSFTBIN); /* create the data source */ if (fp != 0) ds = new CVmFileSource(fp); } /* make sure we were able to find it and open it */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_not_found_exc, 0, "file not found"); break; case VMOBJFILE_ACCESS_WRITE: /* open in binary mode for writing only */ fp = osfopwb(netfile->lclfname, OSFTBIN); /* make sure we were able to create the file successfully */ if (fp == 0) G_interpreter->throw_new_class( vmg_ G_predef->file_creation_exc, 0, "error creating file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_KEEP: /* open for read/write, keeping existing contents */ fp = osfoprwb(netfile->lclfname, OSFTBIN); /* make sure we were able to find or create the file */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); break; case VMOBJFILE_ACCESS_RW_TRUNC: /* open for read/write, truncating existing contents */ fp = osfoprwtb(netfile->lclfname, OSFTBIN); /* make sure we were successful */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc, 0, "error opening file"); /* create the data source */ ds = new CVmFileSource(fp); break; default: fp = 0; err_throw(VMERR_BAD_VAL_BIF); } /* create our file object */ retval->set_obj(create( vmg_ FALSE, netfile, VM_INVALID_OBJ, ds, mode, access, FALSE)); /* * we've handed off the network file descriptor to the File object, * so we no longer have responsibility for it */ netfile = 0; } err_finally { /* * if we created a network file descriptor and didn't hand it off * to the new File object, we're abandoning it */ if (netfile != 0) netfile->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check the safety settings to determine if an open operation is allowed. * If the access is not allowed, we'll throw an error. */ void CVmObjFile::check_safety_for_open(VMG_ CVmNetFile *nf, int access) { /* * Special files are inherently sandboxed, since the system determines * their paths. These are always allowed for ordinary read/write. */ if (nf->sfid != 0 && (access == VMOBJFILE_ACCESS_READ || access == VMOBJFILE_ACCESS_WRITE || access == VMOBJFILE_ACCESS_RW_KEEP || access == VMOBJFILE_ACCESS_RW_TRUNC || access == VMOBJFILE_ACCESS_GETINFO || access == VMOBJFILE_ACCESS_READDIR)) return; /* * Temporary file paths can only be obtained from the VM. Ordinary * read/write operations are allowed, as is delete. */ if (nf->is_temp && (access == VMOBJFILE_ACCESS_READ || access == VMOBJFILE_ACCESS_WRITE || access == VMOBJFILE_ACCESS_RW_KEEP || access == VMOBJFILE_ACCESS_RW_TRUNC || access == VMOBJFILE_ACCESS_DELETE || access == VMOBJFILE_ACCESS_GETINFO || access == VMOBJFILE_ACCESS_READDIR)) return; /* * Network files are subject to separate permission rules enforced by * the storage server. Local file safety settings don't apply. */ if (nf->is_net_file()) return; /* * If the file was specifically selected by the user via a file dialog, * allow access of the type proposed by the dialog, even if it's * outside the sandbox. The user has implicitly granted us permission * on this individual file by manually selecting it via a UI * interaction. */ CVmObjFileName *ofn; if ((ofn = vm_objid_cast(CVmObjFileName, nf->filespec)) != 0 && ofn->get_from_ui() != 0) { /* grant permission based on the dialog mode */ switch (ofn->get_from_ui()) { case OS_AFP_OPEN: /* allow reading for a file selected with an Open dialog */ if (access == VMOBJFILE_ACCESS_READ) return; break; case OS_AFP_SAVE: /* allow writing for a file selected with a Save dialog */ if (access == VMOBJFILE_ACCESS_WRITE) return; break; } /* * For anything we didn't override above based on the dialog mode, * don't give up - the file safety permissions might still allow * it. All we've determined at this point is that the dialog * selection doesn't grant extraordinary permission for the * attempted access mode. */ } /* * The file wasn't specified by a type that confers any special * privileges, so determine if it's accessible according to the local * file name's handling under the file safety and sandbox rules. */ return check_safety_for_open(vmg_ nf->lclfname, access); } /* * Check file safety for opening the given local file path. */ void CVmObjFile::check_safety_for_open(VMG_ const char *lclfname, int access) { /* check the safety settings */ if (!query_safety_for_open(vmg_ lclfname, access)) { /* this operation is not allowed - throw an error */ G_interpreter->throw_new_class(vmg_ G_predef->file_safety_exc, 0, "prohibited file access"); } } /* * Query the safety settings. Returns true if the access is allowed, false * if it's prohibited. */ int CVmObjFile::query_safety_for_open(VMG_ const char *lclfname, int access) { /* get the current file safety level from the host application */ int read_level = G_host_ifc->get_io_safety_read(); int write_level = G_host_ifc->get_io_safety_write(); /* * First check to see if the safety level allows or disallows the * requested access without regard to location. If so, we can save a * little work by skipping the sandbox resolution. */ switch (access) { case VMOBJFILE_ACCESS_READ: case VMOBJFILE_ACCESS_GETINFO: case VMOBJFILE_ACCESS_READDIR: /* * Read a file or its metadata. If the safety level is READ_ANY or * below, we can read any file regardless of location. If it's * MAXIMUM, we can't read anything. */ if (read_level <= VM_IO_SAFETY_READ_ANY_WRITE_CUR) return TRUE; if (read_level >= VM_IO_SAFETY_MAXIMUM) return FALSE; break; case VMOBJFILE_ACCESS_RMDIR: case VMOBJFILE_ACCESS_WRITE: case VMOBJFILE_ACCESS_RW_KEEP: case VMOBJFILE_ACCESS_RW_TRUNC: case VMOBJFILE_ACCESS_DELETE: case VMOBJFILE_ACCESS_MKDIR: case VMOBJFILE_ACCESS_RENAME_FROM: case VMOBJFILE_ACCESS_RENAME_TO: /* * Writing, deleting, renaming, creating/removing a directory. If * the safety level is MINIMUM, we're allowed to write anywhere. * If it's READ_CUR or higher, writing isn't allowed anywhere. */ if (write_level <= VM_IO_SAFETY_MINIMUM) return TRUE; if (write_level >= VM_IO_SAFETY_READ_CUR) return FALSE; break; } /* * If we get this far, it means we're in sandbox mode, so the access is * conditional on the file's location relative to the sandbox folder. * Get the sandbox path, defaulting to the file base path. */ const char *sandbox = (G_sandbox_path != 0 ? G_sandbox_path : G_file_path); /* assume the file isn't in the sandbox */ int in_sandbox = FALSE; /* * Most operations in sandbox mode don't consider the sandbox directory * itself to be part of the sandbox - e.g., you can't delete the * sandbox folder. */ int match_self = FALSE; /* * The effective sandbox extends beyond the actual contents the sandbox * directory for a couple of modes: * * - For READDIR mode (reading the contents of a directory), the * sandbox directory itself is considered part of the sandbox, since * the information we're accessing in creating a directory listing is * really the directory's contents. * * - For GETINFO mode (read file metadata), extend the sandbox to * include the sandbox directory itself, its parent, its parent's * parent, etc. It's pretty obvious that we should have metadata read * access to the sandbox directory itself, given that we can list and * access its contents. The rationale for also including parents of * the sandbox folder is that they're effectively part of its metadata, * since they're visible in its full path name. */ switch (access) { case VMOBJFILE_ACCESS_GETINFO: /* * read metadata - the sandbox directory is part of the sandbox for * this operation, as is its parent, its parent's parent, etc * (which is to say, any folder that contains the sandbox) */ in_sandbox |= os_is_file_in_dir(sandbox, lclfname, TRUE, TRUE); break; case VMOBJFILE_ACCESS_READDIR: /* * read directory contents - the sandbox directory itself is part * of the sandbox for this mode */ match_self = TRUE; break; } /* * Check to see if the file is in the sandbox folder (or any subfolder * thereof). If not, we may have to disallow the operation based on * safety level settings, but for now just note the location status. */ in_sandbox |= os_is_file_in_dir(lclfname, sandbox, TRUE, match_self); /* if the file isn't in the sandbox, access isn't allowed */ if (!in_sandbox) return FALSE; /* didn't find any problems - allow the access */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the character set */ int CVmObjFile::getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* if we have a character set object, return it; otherwise return nil */ if (get_ext()->charset != VM_INVALID_OBJ) retval->set_obj(get_ext()->charset); else retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set the character set */ int CVmObjFile::getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve and save the character set argument */ get_ext()->charset = get_charset_arg(vmg_ 0, 1); /* discard arguments */ G_stk->discard(); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Check that we're in a valid state to perform file operations. */ void CVmObjFile::check_valid_file(VMG0_) { /* if we're 'out of sync', we can't perform any operations on it */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0) G_interpreter->throw_new_class(vmg_ G_predef->file_sync_exc, 0, "file out of sync"); /* if the file has been closed, we can't perform any operations on it */ if (get_ext()->fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_closed_exc, 0, "operation attempted on closed file"); } /* * Check that we have read access */ void CVmObjFile::check_read_access(VMG0_) { /* we have read access unless we're in write-only mode */ if (get_ext()->access == VMOBJFILE_ACCESS_WRITE) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we have write access */ void CVmObjFile::check_write_access(VMG0_) { /* we have write access unless we're in read-only mode */ if (get_ext()->access == VMOBJFILE_ACCESS_READ) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we're in raw mode */ void CVmObjFile::check_raw_mode(VMG0_) { /* if we're not in raw mode, throw an error */ if (get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); } /* * Check that we've a valid raw readable file */ void CVmObjFile::check_raw_read(VMG0_) { check_valid_file(vmg0_); check_read_access(vmg0_); check_raw_mode(vmg0_); } /* * Check that we've a valid raw writable file */ void CVmObjFile::check_raw_write(VMG0_) { check_valid_file(vmg0_); check_write_access(vmg0_); check_raw_mode(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - close the file */ int CVmObjFile::getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* * take over the network file descriptor from the File object - we're * going to either close it or abandon it, which deletes it, so we * don't need to delete it again when the File object is collected */ CVmNetFile *netfile = get_ext()->netfile; get_ext()->netfile = 0; /* * Explicitly flush the file, to detect any errors flushing our final * write buffers. The regular osifc close ignores errors, so we have * to check with an explicit flush if we want this information. */ err_try { /* flush buffers */ get_ext()->fp->flush(); } err_catch_disc { /* * The flush failed, so the close effectively failed. Close the * file anyway to release system resources. */ delete get_ext()->fp; /* * Abandon the network file object. Since we failed to close the * local copy properly, we don't want to update the server copy (if * it exists) with corrupt data. */ if (netfile != 0) netfile->abandon(vmg0_); /* rethrow the error */ err_rethrow(); } err_end; /* close and explicitly forget the data source */ delete get_ext()->fp; get_ext()->fp = 0; /* if there's a network file, close it */ if (netfile != 0) netfile->close(vmg0_); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - read from the file */ int CVmObjFile::getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have read access */ check_read_access(vmg0_); /* note the implicit seeking */ note_file_seek(vmg_ FALSE); /* flush stdio buffers as needed and note the read operation */ switch_read_write_mode(vmg_ FALSE); /* read according to our mode */ switch(get_ext()->mode) { case VMOBJFILE_MODE_TEXT: /* read a line of text */ read_text_mode(vmg_ retval); break; case VMOBJFILE_MODE_DATA: /* read in data mode */ read_data_mode(vmg_ retval); break; case VMOBJFILE_MODE_RAW: /* can't use this call on this type of file */ G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); break; } /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Read a value in text mode */ void CVmObjFile::read_text_mode(VMG_ vm_val_t *retval) { /* get our file data source and read buffer from the extension */ CVmDataSource *fp = get_ext()->fp; vmobjfile_readbuf_t *readbuf = get_ext()->readbuf; /* get our character mapper */ CCharmapToUni *charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_uni(vmg0_); /* replenish the buffer if it's empty */ if (readbuf->rem == 0 && !readbuf->refill(fp)) { /* end of file - return nil */ retval->set_nil(); return; } /* * Allocate a string object for the result. We have no idea how long * the line will be, so start with an arbitrary guess; we'll adjust the * buffer up or down as needed. */ vm_obj_id_t strid = CVmObjString::create(vmg_ FALSE, 128); CVmObjString *str = (CVmObjString *)vm_objp(vmg_ strid); char *dst = str->cons_get_buf(); /* this string will be the return value */ retval->set_obj(strid); /* push it for gc protection */ G_stk->push(retval); /* * Read a line of text from the file into our buffer. Keep going * until we read an entire line; we might have to read the line in * chunks, since the line might end up being longer than our buffer. */ for (;;) { /* read the next character; if we can't, we're at EOF */ wchar_t ch; size_t chlen; if (!readbuf->getch(ch, fp, charmap)) break; /* if this is a CR, LF, or U+2028, it's the end of the line */ if (ch == '\n' || ch == '\r' || ch == 0x2028) { /* newline - skip CR-LF and LR-CR pairs */ wchar_t nxt = (ch == '\n' ? '\r' : ch == '\r' ? '\n' : 0); size_t b; if (nxt != 0 && readbuf->peekch(ch, fp, charmap, b) && ch == nxt) readbuf->commit_peek(b); /* whichever type of newline we read, store it as LF */ ch = 10; chlen = 1; } else { /* ordinary character - figure the output character length */ chlen = utf8_ptr::s_wchar_size(ch); } /* ensure we have enough space for it in our string */ dst = str->cons_ensure_space(vmg_ dst, chlen, 128); /* add this character to the string */ dst += utf8_ptr::s_putch(dst, ch); /* if we just read a newline, we're done */ if (ch == '\n') break; } /* close the string */ str->cons_shrink_buffer(vmg_ dst); /* * we now can discard the string we've been keeping on the stack to * for garbage collection protection */ G_stk->discard(); } /* * Read a value in 'data' mode */ void CVmObjFile::read_data_mode(VMG_ vm_val_t *retval) { char buf[32]; CVmObjString *str_obj; vm_obj_id_t str_id; CVmDataSource *fp = get_ext()->fp; /* read the type flag */ if (fp->read(buf, 1)) { /* end of file - return nil */ retval->set_nil(); return; } /* see what we have */ switch(buf[0]) { case VMOBJFILE_TAG_INT: /* read the INT4 value */ if (fp->read(buf, 4)) goto io_error; /* set the integer value from the buffer */ retval->set_int(osrp4s(buf)); break; case VMOBJFILE_TAG_ENUM: /* read the UINT4 value */ if (fp->read(buf, 4)) goto io_error; /* set the 'enum' value */ retval->set_enum(t3rp4u(buf)); break; case VMOBJFILE_TAG_STRING: /* * read the string's length - note that this length is two * higher than the actual length of the string, because it * includes the length prefix bytes */ if (fp->read(buf, 2)) goto io_error; /* * allocate a new string of the required size (deducting two * bytes from the indicated size, since the string allocator * only wants to know about the bytes of the string we want to * store, not the length prefix part) */ str_id = CVmObjString::create(vmg_ FALSE, osrp2(buf) - 2); str_obj = (CVmObjString *)vm_objp(vmg_ str_id); /* read the bytes of the string into the object's buffer */ if (fp->read(str_obj->cons_get_buf(), osrp2(buf) - 2)) goto io_error; /* success - set the string return value, and we're done */ retval->set_obj(str_id); break; case VMOBJFILE_TAG_TRUE: /* it's a simple 'true' value */ retval->set_true(); break; case VMOBJFILE_TAG_BIGNUM: /* read the BigNumber value and return a new BigNumber object */ if (CVmObjBigNum::read_from_data_file(vmg_ retval, fp)) goto io_error; break; case VMOBJFILE_TAG_BYTEARRAY: /* read the ByteArray value and return a new ByteArray object */ if (CVmObjByteArray::read_from_data_file(vmg_ retval, fp)) goto io_error; break; default: /* invalid data - throw an error */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* done */ return; io_error: /* * we'll come here if we read the type tag correctly but encounter * an I/O error reading the value - this indicates a corrupted input * stream, so throw an I/O error */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* * Internal read routine */ int CVmObjFile::read_file(VMG_ char *buf, int32_t &len) { /* get the data source */ CVmDataSource *fp = get_ext()->fp; /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || fp == 0) return FALSE; /* deal with stdio buffering if we're changing modes */ switch_read_write_mode(vmg_ FALSE); /* read according to the mode */ switch (get_ext()->mode) { case VMOBJFILE_MODE_TEXT: { /* get the read buffer and character mapper */ vmobjfile_readbuf_t *readbuf = get_ext()->readbuf; CCharmapToUni *charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_uni(vmg0_); /* loop until we satisfy the request */ int32_t actual; for (actual = 0 ; ; ) { /* peek at the next character */ wchar_t ch; size_t b; if (!readbuf->peekch(ch, fp, charmap, b)) break; /* make sure it'll fit */ size_t csiz = utf8_ptr::s_wchar_size(ch); if (actual + (int32_t)csiz > len) break; /* store this character */ actual += utf8_ptr::s_putch(buf + actual, ch); /* advance to the next character */ readbuf->commit_peek(b); } /* set the actual return length */ len = actual; } break; case VMOBJFILE_MODE_RAW: /* do the direct read */ len = fp->readc(buf, (size_t)len); break; default: /* can't handle others */ return FALSE; } /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - write to the file */ int CVmObjFile::getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); const vm_val_t *argval; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * get a pointer to the argument value, but leave it on the stack * for now to protect against losing it in garbage collection */ argval = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have write access */ check_write_access(vmg0_); /* deal with stdio buffering if we're changing modes */ switch_read_write_mode(vmg_ TRUE); /* read according to our mode */ switch(get_ext()->mode) { case VMOBJFILE_MODE_TEXT: /* read a line of text */ write_text_mode(vmg_ argval); break; case VMOBJFILE_MODE_DATA: /* read in data mode */ write_data_mode(vmg_ argval); break; case VMOBJFILE_MODE_RAW: /* can't use this call on this type of file */ G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); break; } /* discard our gc protection and argument */ G_stk->discard(2); /* no return value - return nil by default */ retval->set_nil(); /* handled */ return TRUE; } /* * Write a value in text mode */ void CVmObjFile::write_text_mode(VMG_ const vm_val_t *val) { char conv_buf[128]; vm_val_t new_str; CCharmapToLocal *charmap; const char *constp; const char *p, *startp; size_t rem; /* get our character mapper */ charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset)) ->get_to_local(vmg0_); /* convert the value to a string */ constp = CVmObjString::cvt_to_str( vmg_ &new_str, conv_buf, sizeof(conv_buf), val, 10, 0); /* protect the new string (if any) from gc while working */ G_stk->push(&new_str); /* scan for newlines - we need to write newline sequences specially */ for (startp = constp + VMB_LEN, rem = vmb_get_len(constp) ; rem != 0 ; ) { /* scan to the next newline */ for (p = startp ; rem != 0 && *p != '\n' ; ++p, --rem) ; /* write this chunk through the character mapper */ if (p != startp && charmap->write_file(get_ext()->fp, startp, p - startp)) { /* the write failed - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* write the newline, if applicable */ if (rem != 0 && charmap->write_file(get_ext()->fp, OS_NEWLINE_SEQ, strlen(OS_NEWLINE_SEQ))) { /* the write failed - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* if we're at a newline, skip it */ if (rem != 0) ++p, --rem; /* note where the new chunk starts */ startp = p; } /* done with the gc protection */ G_stk->discard(); /* invalidate any read buffer */ note_file_seek(vmg_ FALSE); } /* * Write a value in 'data' mode */ void CVmObjFile::write_data_mode(VMG_ const vm_val_t *val) { char buf[32]; CVmDataSource *fp = get_ext()->fp; vm_val_t new_str; const char *constp; /* see what type of data we want to put */ switch(val->typ) { case VM_INT: /* put the type in the buffer */ buf[0] = VMOBJFILE_TAG_INT; /* add the value in INT4 format */ oswp4s(buf + 1, val->val.intval); /* write out the type prefix plus the value */ if (fp->write(buf, 5)) goto io_error; /* done */ break; case VM_ENUM: /* put the type in the buffer */ buf[0] = VMOBJFILE_TAG_ENUM; /* add the value in INT4 format */ oswp4(buf + 1, val->val.enumval); /* write out the type prefix plus the value */ if (fp->write(buf, 5)) goto io_error; /* done */ break; case VM_SSTRING: /* get the string value pointer */ constp = val->get_as_string(vmg0_); write_binary_string: /* write the type prefix byte */ buf[0] = VMOBJFILE_TAG_STRING; if (fp->write(buf, 1)) goto io_error; /* * write the length prefix - for TADS 2 compatibility, include * the bytes of the prefix itself in the length count */ oswp2(buf, vmb_get_len(constp) + 2); if (fp->write(buf, 2)) goto io_error; /* write the string's bytes */ if (fp->write(constp + VMB_LEN, vmb_get_len(constp))) goto io_error; /* done */ break; case VM_OBJ: /* * Write BigNumber and ByteArray types in special formats. For * other types, try converting to a string. */ if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj)) { CVmObjBigNum *bignum; /* we know it's a BigNumber - cast it properly */ bignum = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj); /* write the type tag */ buf[0] = VMOBJFILE_TAG_BIGNUM; if (fp->write(buf, 1)) goto io_error; /* write it out */ if (bignum->write_to_data_file(fp)) goto io_error; } else if (CVmObjByteArray::is_byte_array(vmg_ val->val.obj)) { CVmObjByteArray *bytarr; /* we know it's a ByteArray - cast it properly */ bytarr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); /* write the type tag */ buf[0] = VMOBJFILE_TAG_BYTEARRAY; if (fp->write(buf, 1)) goto io_error; /* write the array */ if (bytarr->write_to_data_file(fp)) goto io_error; } else { /* * Cast it to a string value and write that out. Note that * we can ignore garbage collection for any new string we've * created, since we're just calling the OS-level file * writer, which will never invoke garbage collection. */ constp = vm_objp(vmg_ val->val.obj) ->cast_to_string(vmg_ val->val.obj, &new_str); goto write_binary_string; } break; case VM_TRUE: /* * All we need for this is the type tag. Note that we can't * write nil because we'd have no way of reading it back in - a * nil return from file_read indicates that we've reached the * end of the file. So there's no point in writing nil to a * file. */ buf[0] = VMOBJFILE_TAG_TRUE; if (fp->write(buf, 1)) goto io_error; /* done */ break; default: /* other types are not acceptable */ err_throw(VMERR_BAD_TYPE_BIF); } /* done */ return; io_error: /* the write failed - throw an i/o exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - read raw bytes from the file */ int CVmObjFile::getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1, 2); uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t arr_val; CVmObjByteArray *arr; unsigned long idx; unsigned long len; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* check that we have read access */ check_read_access(vmg0_); /* we can only use this call on 'raw' files */ if (get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); /* retrieve the ByteArray destination */ G_stk->pop(&arr_val); /* make sure it's really a ByteArray object */ if (arr_val.typ != VM_OBJ || !CVmObjByteArray::is_byte_array(vmg_ arr_val.val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* we know it's a byte array object, so cast it */ arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj); /* presume we'll try to fill the entire array */ idx = 1; len = arr->get_element_count(); /* if we have a starting index argument, retrieve it */ if (argc >= 2) idx = (unsigned long)CVmBif::pop_int_val(vmg0_); /* if we have a length argument, retrieve it */ if (argc >= 3) len = (unsigned long)CVmBif::pop_int_val(vmg0_); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* note the implicit seeking */ note_file_seek(vmg_ FALSE); /* deal with stdio buffering issues if necessary */ switch_read_write_mode(vmg_ FALSE); /* * read the data into the array, and return the number of bytes we * actually manage to read */ retval->set_int(arr->read_from_file( vmg_ arr_val.val.obj, get_ext()->fp, idx, len, TRUE)); /* discard our gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - write raw bytes to the file */ int CVmObjFile::getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { static CVmNativeCodeDesc desc(1, 2); uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t srcval; CVmObjByteArray *arr = 0; CVmObjFile *file = 0; unsigned long idx, has_idx = FALSE; unsigned long len; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* make sure it's a valid file, with write access, in raw mode */ check_valid_file(vmg0_); check_write_access(vmg0_); check_raw_mode(vmg0_); /* pop the source object argument */ G_stk->pop(&srcval); if (srcval.typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* check the object type */ if (CVmObjByteArray::is_byte_array(vmg_ srcval.val.obj)) { /* get the ByteArray object, suitably cast */ arr = (CVmObjByteArray *)vm_objp(vmg_ srcval.val.obj); /* assume we'll write the entire byte array */ idx = 1; len = arr->get_element_count(); } else if (CVmObjFile::is_file_obj(vmg_ srcval.val.obj)) { /* get the file object, suitably cast */ file = (CVmObjFile *)vm_objp(vmg_ srcval.val.obj); /* make sure it's in raw mode */ if (file->get_ext()->mode != VMOBJFILE_MODE_RAW) G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0, "wrong file mode"); /* assume we'll copy the file from the current seek offset onward */ idx = file->get_pos(vmg0_); len = file->get_file_size(vmg0_); } else { /* invalid source type */ err_throw(VMERR_BAD_TYPE_BIF); } /* if we have a starting index, retrieve it */ if (argc >= 2) { /* if it's nil, just ignore it, otherwise pop the int value */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else { idx = (unsigned long)CVmBif::pop_int_val(vmg0_); has_idx = TRUE; } } /* if we have a length, retrieve it */ if (argc >= 3) { /* if it's nil, just ignore it, otherwise pop the int value */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else len = (unsigned long)CVmBif::pop_int_val(vmg0_); } /* push a self-reference and a source reference for gc protection */ G_stk->push()->set_obj(self); G_stk->push(&srcval); /* flush stdio buffers as needed and note the read operation */ switch_read_write_mode(vmg_ TRUE); /* do the copy according to the source type */ if (arr != 0) { /* * write the bytes to the file - on success (zero write_to_file * return), return nil, on failure (non-zero write_to_file return), * return true */ if (arr->write_to_file(get_ext()->fp, idx, len)) { /* we failed to write the bytes - throw an I/O exception */ G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); } } else if (file != 0) { /* * if there's an explicit starting seek position, go there; if it * wasn't specified, the default is the current seek location, so * we just leave well enough alone in that case */ if (has_idx) file->set_pos(vmg_ idx); /* copy the requested byte range */ while (len != 0) { /* read the next chunk of bytes */ char buf[1024]; int32_t cur = (len < (int32_t)sizeof(buf) ? len : sizeof(buf)); if (!file->read_file(vmg_ buf, cur)) G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); /* if we read zero bytes, we've reached EOF */ if (cur == 0) break; /* write out this chunk */ if (get_ext()->fp->write(buf, (size_t)cur)) G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc, 0, "file I/O error"); /* deduct this hunk from the remaining length */ len -= cur; } } /* discard our gc protection */ G_stk->discard(2); /* no return value - return nil by default */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the seek position */ int CVmObjFile::getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* get the position */ retval->set_int(get_pos(vmg0_)); /* handled */ return TRUE; } /* * Get the current file position */ long CVmObjFile::get_pos(VMG0_) { /* get the seek position in the underlying file */ long pos = get_ext()->fp->get_pos(); /* * if we have a read buffer, adjust for buffering: the underlying file * corresponds to the end of the read buffer, so subtract the number of * bytes remaining in the read buffer */ if (get_ext()->readbuf != 0) pos -= get_ext()->readbuf->rem; /* return the result */ return pos; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set the seek position */ int CVmObjFile::getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); unsigned long pos; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* retrieve the target seek position */ pos = CVmBif::pop_long_val(vmg0_); /* seek to the new position */ get_ext()->fp->seek(pos, OSFSK_SET); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set position - internal version */ int CVmObjFile::set_pos(VMG_ long pos) { /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || get_ext()->fp == 0) return FALSE; /* note the seek */ note_file_seek(vmg_ TRUE); /* set the seek position */ return !get_ext()->fp->seek(pos, OSFSK_SET); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - set position to end of file */ int CVmObjFile::getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* seek to the end position */ get_ext()->fp->seek(0, OSFSK_END); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get size */ int CVmObjFile::getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* get the size */ retval->set_int(get_file_size(vmg0_)); /* handled */ return TRUE; } /* * Get the size, internal version */ long CVmObjFile::get_file_size(VMG0_) { /* make sure it's valid */ if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0 || get_ext()->fp == 0) return -1; /* note the seeking operation */ note_file_seek(vmg_ TRUE); /* get the size */ return get_ext()->fp->get_size(); } /* ------------------------------------------------------------------------ */ /* * Property evaluator - get the file mode */ int CVmObjFile::getp_get_mode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* return the mode */ retval->set_int(get_ext()->mode); /* handled */ return TRUE; } /* * Get the mode, internal version */ int CVmObjFile::get_file_mode(VMG0_) { return get_ext()->mode; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - setMode - change the file mode */ int CVmObjFile::getp_setMode(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ static CVmNativeCodeDesc desc(1, 1); int argc = (oargc != 0 ? *oargc : 0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* make sure we are allowed to perform operations on the file */ check_valid_file(vmg0_); /* retrieve the mode argument */ int mode = CVmBif::pop_int_val(vmg0_); --argc; /* presume no character set or read buffer */ vm_obj_id_t cset = VM_INVALID_OBJ; int create_readbuf = FALSE; /* save the retained settings from the current extension */ vmobjfile_ext_t *ext = get_ext(); CVmDataSource *fp = ext->fp; CVmNetFile *netfile = ext->netfile; unsigned long flags = ext->flags; int access = ext->access; /* check the mode */ switch (mode) { case VMOBJFILE_MODE_TEXT: /* text mode - we need a character mapper in this mode */ cset = get_charset_arg(vmg_ 0, argc); /* * if we're open with read access, we need a read buffer in order * to do the character set translation */ if (access == VMOBJFILE_ACCESS_READ) create_readbuf = TRUE; break; case VMOBJFILE_MODE_DATA: case VMOBJFILE_MODE_RAW: /* * data mode or raw mode - we don't use a character mapper for this * mode, so if the argument is present, simply discard it */ if (argc >= 2) G_stk->discard(); break; default: /* invalid mode */ err_throw(VMERR_BAD_VAL_BIF); } /* push the character set object (if any) for gc protection */ G_stk->push_obj_or_nil(vmg_ cset); /* * remove the data source and network file descriptor from the * extension, so that they're not deleted when we reallocate it */ ext->fp = 0; ext->netfile = 0; /* * Reallocate the extension. This will set up the correct structures * for the new file mode. */ alloc_ext(vmg_ netfile, cset, fp, flags, mode, access, create_readbuf); /* discard arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * getRootName property evaluator - get the root filename of a given * string. This is a static (class) method. */ int CVmObjFile::s_getp_getRootName(VMG_ vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); char buf[OSFNMAX*2]; const char *ret; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the string argument into our buffer */ CVmBif::pop_fname_val(vmg_ buf, sizeof(buf)); /* set up a no-access file descriptor to determine where the file is */ CVmNetFile *nf = CVmNetFile::open(vmg_ buf, 0, 0, OSFTUNK, 0); /* * If the file is on the network storage server, we always use the Unix * convention, regardless of the local naming rules. Otherwise, use * the local OS API to get the root name. */ if (nf->is_net_file()) { /* * We're in network mode, so use the Unix path conventions. Simply * scan for the last '/' character; if we find it, the root name is * the part after the '/', otherwise it's the whole string. */ const char *sl = strrchr(buf, '/'); ret = (sl != 0 ? sl + 1 : buf); } else { /* it's not a network file - use the local filename rules */ ret = os_get_root_name(buf); } /* done with the net file desriptor */ nf->abandon(vmg0_); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, ret, strlen(ret))); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * deleteFile() property evaluator - deletes a file */ int CVmObjFile::s_getp_deleteFile(VMG_ vm_val_t *retval, uint *in_argc) { /* check arguments */ uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the filename; use the DELETE access mode */ vm_rcdesc rc(vmg_ "File.deleteFile", CVmObjFile::metaclass_reg_->get_class_obj(vmg0_), VMOBJFILE_DELETE_FILE, G_stk->get(0), argc); int access = VMOBJFILE_ACCESS_DELETE; CVmNetFile *netfile = get_filename_and_access( vmg_ &rc, &access, FALSE, OSFTUNK, "application/octet-stream"); /* close the network file descriptor - this will delete the file */ netfile->close(vmg0_); /* discard arguments */ G_stk->discard(1); /* no return */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - unpackBytes */ int CVmObjFile::getp_unpackBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* get the format string, but leave it on the stack */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); if (fmt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the format string pointer and length */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* unpack the data from our file stream into a list, returning the list */ CVmPack::unpack(vmg_ retval, fmt, fmtlen, get_ext()->fp); /* discard our arguments and gc protection */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* * property evaluator - packBytes */ int CVmObjFile::getp_packBytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with write access in "raw" mode */ check_raw_write(vmg0_); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* remember the starting seek position */ long start = get_pos(vmg0_); /* pack the arguments into our file data stream */ CVmPack::pack(vmg_ 1, argc, get_ext()->fp); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* return the number of bytes packed */ retval->set_int(get_pos(vmg0_) - start); /* handled */ return TRUE; } /* * property evaluator - sha256 */ int CVmObjFile::getp_sha256(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* retrieve the length value, if present; if not, use the file size */ long len = (argc >= 1 && G_stk->get(0)->typ != VM_NIL ? G_stk->get(0)->num_to_int(vmg0_) : get_file_size(vmg0_)); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* calculate the hash */ char hash[65]; sha256_datasrc(hash, get_ext()->fp, len); /* return the hash value */ retval->set_obj(CVmObjString::create(vmg_ FALSE, hash, 64)); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* * property evaluator - digestMD5 */ int CVmObjFile::getp_digestMD5(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check that the it's a valid file open with read access in "raw" mode */ check_raw_read(vmg0_); /* retrieve the length value, if present; if not, use the file size */ long len = (argc >= 1 && G_stk->get(0)->typ != VM_NIL ? G_stk->get(0)->num_to_int(vmg0_) : get_file_size(vmg0_)); /* save 'self' for gc protection */ G_stk->push_obj(vmg_ self); /* calculate the hash */ char hash[33]; md5_datasrc(hash, get_ext()->fp, len); /* return the hash value */ retval->set_obj(CVmObjString::create(vmg_ FALSE, hash, 32)); /* discard our gc protection and our arguments */ G_stk->discard(argc + 1); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Refill the read buffer. Returns true if the buffer contains any data * on return, false if we're at end of file. */ int vmobjfile_readbuf_t::refill(CVmDataSource *fp) { /* if the buffer is already full, do nothing */ if (rem == sizeof(buf)) return FALSE; /* * if there's anything left in the buffer, move it to the start of the * buffer */ if (rem != 0) { memmove(buf, ptr, rem); ptr = buf + rem; } else ptr = buf; /* fill up the rest of the buffer */ size_t cur = fp->readc(ptr, sizeof(buf) - rem); rem += cur; /* return success if we managed to read any bytes on this round */ return (cur != 0); } /* * Read a character */ int vmobjfile_readbuf_t::getch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap) { /* keep going until we map a character or run out of input */ for (;;) { /* try mapping a character - if we get one, return success */ if (charmap->mapchar(ch, (const char *&)ptr, rem)) return TRUE; /* * we don't have enough bytes to map a whole character, so refill * our buffer; if that fails, we're at EOF */ if (!refill(fp)) return FALSE; } } /* * Peek at the next character */ int vmobjfile_readbuf_t::peekch(wchar_t &ch, CVmDataSource *fp, class CCharmapToUni *charmap, size_t &in_bytes) { /* keep going until we map a character or run out of input */ for (;;) { /* try mapping a character - if we get one, return success */ char *ptr2 = ptr; size_t rem2 = rem; if (charmap->mapchar(ch, (const char *&)ptr2, rem2)) { in_bytes = rem - rem2; return TRUE; } /* * we don't have enough bytes to map a whole character, so refill * our buffer; if that fails, we're at EOF */ if (!refill(fp)) return FALSE; } } frobtads-1.2.3/tads3/vmdynfunc.cpp0000644000175000001440000017324011713617426016256 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmdynfunc.cpp - DynamicFunc implementation Function Notes Modified 12/13/09 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "charmap.h" #include "vmdynfunc.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmlst.h" #include "vmstr.h" #include "vmrun.h" #include "vmgram.h" #include "vmdict.h" #include "vmtobj.h" #include "vmpredef.h" #include "vmimport.h" #include "vmfunc.h" #include "vmfref.h" #include "utf8.h" #include "vmop.h" #include "tctarg.h" #include "tcglob.h" #include "tctok.h" #include "tcprs.h" #include "tcmain.h" #include "resload.h" #include "tchost.h" #include "vmhost.h" #include "tcgen.h" #include "vmimage.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_dynfunc_ext *vm_dynfunc_ext::alloc_ext( VMG_ CVmDynamicFunc *self, size_t bytecode_len, int obj_ref_cnt) { /* * Calculate how much space we need: we need the base structure, plus * space for the object references, plus the dynamic object header, and * the byte code array. */ size_t siz = sizeof(vm_dynfunc_ext) + (obj_ref_cnt - 1)*sizeof_field(vm_dynfunc_ext, obj_refs[0]) + VMCO_PREFIX_LENGTH + bytecode_len; /* allocate the memory */ vm_dynfunc_ext *ext = (vm_dynfunc_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* remember the sizes */ ext->obj_ref_cnt = obj_ref_cnt; ext->bytecode_len = bytecode_len; /* we don't have any source text yet */ ext->src.set_nil(); /* we don't have a method context object yet */ ext->method_ctx.set_nil(); /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * DynamicFunc statics */ /* metaclass registration object */ static CVmMetaclassDynamicFunc metaclass_reg_obj; CVmMetaclass *CVmDynamicFunc::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmDynamicFunc::*CVmDynamicFunc::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmDynamicFunc::getp_undef, &CVmDynamicFunc::getp_get_source }; /* ------------------------------------------------------------------------ */ /* * DynamicFunc metaclass implementation */ /* * construction */ CVmDynamicFunc::CVmDynamicFunc(VMG_ vm_obj_id_t self, const vm_val_t *src, size_t bytecode_len, int obj_ref_cnt) { /* allocate our extension structure */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); /* store it in the object */ ext_ = (char *)ext; /* save the source value */ if (src != 0) ext->src = *src; /* set up the code object prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); } /* * create */ vm_obj_id_t CVmDynamicFunc::create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t src_len) { /* get the compiler interface */ CVmDynamicCompiler *comp = CVmDynamicCompiler::get(vmg0_); /* compile the code and create a code object */ CVmDynCompResults results; vm_obj_id_t id = comp->compile( vmg_ in_root_set, globals, locals, macros, srcval, src, src_len, DCModeAuto, 0, &results); /* if an error occurred, throw the error */ if (id == VM_INVALID_OBJ) results.throw_error(vmg0_); /* return the new ID */ return id; } /* * create */ vm_obj_id_t CVmDynamicFunc::create(VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *src) { /* get the source value as a string */ const char *src_txt = src->get_as_string(vmg0_); /* if it's not a string value, it's an error */ if (src_txt == 0) err_throw(VMERR_BAD_TYPE_BIF); /* create the object */ return create(vmg_ in_root_set, globals, locals, macros, src, src_txt + VMB_LEN, vmb_get_len(src_txt)); } /* * Create dynamically using stack arguments. This takes the source code as * a string, compiles it, and creates a DynamicFunc encapsulating the byte * code. */ vm_obj_id_t CVmDynamicFunc::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* check arguments */ if (argc < 1 || argc > 4) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the source code argument (leave on stack for gc protection) */ const vm_val_t *src = G_stk->get(0); /* get the global symbol table object, if any */ vm_obj_id_t globals = VM_INVALID_OBJ; if (argc >= 2) { /* it has to be an object or nil */ if (G_stk->get(1)->typ == VM_OBJ) globals = G_stk->get(1)->val.obj; else if (G_stk->get(1)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* get the local symbol table object, if any */ vm_obj_id_t locals = VM_INVALID_OBJ; if (argc >= 3) { /* it has to be an object or nil */ if (G_stk->get(2)->typ == VM_OBJ) locals = G_stk->get(2)->val.obj; else if (G_stk->get(2)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* check for macros */ vm_obj_id_t macros = VM_INVALID_OBJ; if (argc >= 4) { /* it has to be an object or nil */ if (G_stk->get(3)->typ == VM_OBJ) macros = G_stk->get(3)->val.obj; else if (G_stk->get(3)->typ != VM_NIL) err_throw(VMERR_BAD_TYPE_BIF); } /* allocate the object ID and create the object */ vm_obj_id_t id = create(vmg_ FALSE, globals, locals, macros, src); /* discard arguments */ G_stk->discard(argc); /* return the new ID */ return id; } /* * notify of deletion */ void CVmDynamicFunc::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * Retrieve a DynamicFunc ID from a bytecode method prefix */ vm_obj_id_t CVmDynamicFunc::get_obj_from_prefix(VMG_ const uchar *p) { /* * A DynamicFunc stores a method header prefix just before the start of * the header, and the prefix stores the associated DynamicFunc ID. * Read the prefix header. * * (Technically, this has some risk of reading outside the bounds of * valid program memory, because we don't know the origin of 'p'. * Callers should never attempt calling this routine with arbitrary * pointers. Only pass in pointers that are known to refer to valid * method headers, AND that aren't part of the static Code Pool. The * only other kind of valid method header pointer is a DynamicFunc, so * as long as callers obey these rules this dereference should be * safe.) */ vm_obj_id_t id = vmb_get_objid((const char *)p - VMCO_PREFIX_LENGTH); /* verify with the memory manager that this is a valid object ID */ if (!G_obj_table->is_obj_id_valid(id)) return VM_INVALID_OBJ; /* it's a valid object; make sure it's really of type DynamicFunc */ if (!is_dynfunc_obj(vmg_ id)) return VM_INVALID_OBJ; /* * It's a valid DynamicFunc: verify that it's really the one it claims * to be. If it is, the object's bytecode pointer should match 'p'. */ CVmDynamicFunc *co = (CVmDynamicFunc *)vm_objp(vmg_ id); if (co->get_ext()->get_bytecode_ptr() == (const char *)p) { /* we have a winner! */ return id; } /* it's not our object */ return VM_INVALID_OBJ; } /* * get a property */ int CVmDynamicFunc::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * Invoke */ int CVmDynamicFunc::get_invoker(VMG_ vm_val_t *val) { /* * Return a pointer to the start of our byte code. The actual method * header starts after our prefix. */ if (val != 0) val->set_codeptr(get_ext()->get_bytecode_ptr()); /* we're invokable */ return TRUE; } /* * Index the object. * * A DynamicFunc is an invokee, and as such it can be referenced by * generated code for special internal operations. We use indexing to * retrieve special information: * * [1] is the 'self' object or method context object for the enclosing * stack frame. The compiler uses this to establish the method context for * a dynamic method that's compiled in the context of an enclosing frame, * in analogy to an anonymous function that accesses its enclosing lexical * scope's method context. */ int CVmDynamicFunc::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* check the type */ if (index_val->typ == VM_INT) { /* it's an integer index - check the value */ switch (index_val->val.intval) { case 1: /* return our method context */ *result = get_ext()->method_ctx; return TRUE; default: /* other values are not allowed */ break; } } /* if we didn't handle it already, it's an invalid index */ err_throw(VMERR_INDEX_OUT_OF_RANGE); AFTER_ERR_THROW(return TRUE;) } /* * load from an image file */ void CVmDynamicFunc::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmDynamicFunc::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ self, ptr, siz); } /* * load or reload data from the image */ void CVmDynamicFunc::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bytecode length */ size_t bytecode_len = osrp2(ptr); ptr += 2; /* read the object reference count */ int obj_ref_cnt = osrp2(ptr); ptr += 2; /* allocate the extension */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); ext_ = (char *)ext; /* save the method context object */ vmb_get_dh(ptr, &ext->method_ctx); ptr += VMB_DATAHOLDER; /* save the source string value */ vmb_get_dh(ptr, &ext->src); ptr += VMB_DATAHOLDER; /* set up the dynamic code prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); /* copy the byte code */ memcpy(ext->get_bytecode_ptr(), ptr, bytecode_len); ptr += bytecode_len; /* load the object reference offsets */ for (int i = 0 ; i < obj_ref_cnt ; ++i, ptr += 2) ext->obj_refs[i] = osrp2(ptr); } /* * save to a file */ void CVmDynamicFunc::save_to_file(VMG_ class CVmFile *fp) { char buf[VMB_DATAHOLDER]; vm_dynfunc_ext *ext = get_ext(); /* write our source code length */ fp->write_uint2(ext->bytecode_len); /* write our object reference count */ fp->write_uint2(ext->obj_ref_cnt); /* write the method context object */ vmb_put_dh(buf, &ext->method_ctx); fp->write_bytes(buf, VMB_DATAHOLDER); /* write our source object value */ vmb_put_dh(buf, &ext->src); fp->write_bytes(buf, VMB_DATAHOLDER); /* write the bytecode data */ fp->write_bytes(ext->get_bytecode_ptr(), ext->bytecode_len); /* write the object reference list */ for (int i = 0 ; i < ext->obj_ref_cnt ; ++i) fp->write_uint2(ext->obj_refs[i]); } /* * restore from a file */ void CVmDynamicFunc::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { char buf[VMB_DATAHOLDER]; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bytecode length and object reference count */ size_t bytecode_len = fp->read_uint2(); int obj_ref_cnt = fp->read_uint2(); /* allocate the extension */ vm_dynfunc_ext *ext = vm_dynfunc_ext::alloc_ext( vmg_ this, bytecode_len, obj_ref_cnt); ext_ = (char *)ext; /* read the method context object */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->method_ctx); /* read the source object */ fp->read_bytes(buf, VMB_DATAHOLDER); fixups->fix_dh(vmg_ buf); vmb_get_dh(buf, &ext->src); /* set up the dynamic object code prefix header */ vmb_put_objid(ext->get_prefix_ptr(), self); /* read the bytecode */ char *bc = ext->get_bytecode_ptr(); fp->read_bytes(bc, bytecode_len); /* read the object references, fixing each one up in the bytecode */ for (int i = 0 ; i < obj_ref_cnt ; ++i) { /* read and save the offset of this reference */ uint ofs = ext->obj_refs[i] = fp->read_uint2(); /* fix up the object ID in the bytecode data */ fixups->fix_vmb_obj(vmg_ bc + ofs); } } /* * mark references */ void CVmDynamicFunc::mark_refs(VMG_ uint state) { /* * Get my extension, and only proceed if it's non-empty. For most * object types this isn't an issue, because the only time we create * empty objects of most types is during loading, when gc is disabled. * But we do create long-lived empty code objects in the course of * compilation, because we need a placeholder for the object ID ahead * of the compilation. */ vm_dynfunc_ext *ext = get_ext(); if (ext != 0) { /* mark my context object as referenced */ if (ext->method_ctx.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->method_ctx.val.obj, state); /* mark my source code string as referenced */ if (ext->src.typ == VM_OBJ) G_obj_table->mark_all_refs(ext->src.val.obj, state); /* mark each object reference in the bytecode stream */ char *bc = ext->get_bytecode_ptr(); for (int i = 0 ; i < ext->obj_ref_cnt ; ++i) { /* * Read the object ID from the bytecode. The obj_refs[] array * entry isn't the actual reference, but rather the offset of * the reference in the bytecode stream. The reference itself * is a portable OBJECT_ID field. */ vm_obj_id_t ref = vmb_get_objid(bc + ext->obj_refs[i]); G_obj_table->mark_all_refs(ref, state); } } } /* * property evaluator - get the source code string */ int CVmDynamicFunc::getp_get_source(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the source code object */ *retval = get_ext()->src; /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Custom host interface. This host interface captures error messages * generated during compilation so that we can return them to the program. */ class CTcHostIfcDynComp: public CTcHostIfc { public: CTcHostIfcDynComp() { /* allocate an initial buffer */ erralo_ = 1024; errmsg_ = (char *)t3malloc(erralo_); /* initially clear the message buffer */ reset_messages(); } ~CTcHostIfcDynComp() { /* free our buffer */ t3free(errmsg_); } /* clear the message buffer */ void reset_messages() { errmsg_[0] = '\0'; p_ = errmsg_; } /* get the text of the first error message since we cleared the buffer */ const char *get_error_msg() const { return errmsg_; } /* * CTcHostIfc interface */ /* display an informational message */ virtual void v_print_msg(const char *, va_list) { /* ignore informational messages */ } /* display a process step message */ virtual void v_print_step(const char *, va_list) { /* ignore process step messages */ } /* display an error message */ virtual void v_print_err(const char *msg, va_list args) { /* format the message */ char *f = t3vsprintf_alloc(msg, args); if (f == 0) return; /* get the length */ size_t len = strlen(f); /* expand our buffer if necessary */ size_t curlen = p_ - errmsg_; size_t need = curlen + len + 1; if (need > erralo_) { erralo_ = need + 1024; char *newbuf = (char *)t3realloc(errmsg_, erralo_); if (newbuf == 0) return; /* switch to the new buffer */ errmsg_ = newbuf; p_ = errmsg_ + curlen; } /* append the message */ memcpy(p_, f, len + 1); /* bump the write pointer */ p_ += len; /* done with the allocated message text */ t3free(f); } private: /* our error message buffer */ char *errmsg_; /* allocated buffer size */ size_t erralo_; /* pointer to next available byte of message buffer */ char *p_; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler symbol table interface. This provides the compiler * with global symbols specified via an indexable object. This is usually * a LookupTable, but any object that can be indexed by symbol name string * will work. The actual compiler symbols retrieved from * t3GetGlobalSymbols() can be used, or the program can provide its own * custom symbol table. * * We implement the symbol table as a view of the user-code object, with * caching in our own hash table. To match the interface, we have to * create a CTcSymbol object for each symbol we successfully look up. * Rather than creating redundant copies of these objects, we cache them in * our own hash table. */ class CVmDynFuncSymtab: public CTcPrsSymtab { public: /* initialize with a source object */ CVmDynFuncSymtab(VMG_ vm_obj_id_t globals, vm_obj_id_t locals) : CTcPrsSymtab(0) { /* remember the globals */ gptr_ = VMGLOB_ADDR; /* remember my symbol table objects */ globals_ = globals; locals_ = locals; } /* find a symbol - implementation of CTcPrsDbgSymtab interface */ virtual CTcSymbol *find_direct(const textchar_t *sym, size_t len) { /* first, check the cache to see if we've already looked it up */ CTcSymbol *ret = CTcPrsSymtab::find_direct(sym, len); if (ret != 0) return ret; /* * Didn't find it - check the user-code tables. */ /* try our local symbol table */ ret = find_local(sym, len); /* if that didn't work, try our global symbol table */ if (ret == 0) ret = find_global(sym, len); /* if we found a symbol, add it to the cache */ if (ret != 0) CTcPrsSymtab::add_entry(ret); /* return the symbol */ return ret; } /* add an entry */ virtual void add_entry(CTcSymbol *sym) { /* flag an error */ G_tok->log_error(TCERR_RT_CANNOT_DEFINE_GLOBALS, (int)sym->get_sym_len(), sym->get_sym()); } private: /* look up a symbol in our local symbol table */ CTcSymbol *find_local(const textchar_t *sym, size_t len) { /* if there's no user-code table, there's nothing to search */ if (locals_ == VM_INVALID_OBJ) return 0; /* establish access to globals */ VMGLOB_PTR(gptr_); /* * if it's a StackFrameDesc object, look up the symbol; otherwise * try to treat it as a list-like object, and check each element of * the list */ if (CVmObjFrameDesc::is_framedesc_obj(vmg_ locals_)) { /* look up the value in our single table */ return find_local(sym, len, locals_); } else { /* assume it's a list - set up an object value */ vm_val_t lt; lt.set_obj(locals_); /* check each list element */ int n = lt.ll_length(vmg0_); for (int i = 1 ; i <= n ; ++i) { /* get this element */ vm_val_t ele; lt.ll_index(vmg_ &ele, i); /* if it's a StackFrameDesc object, check it */ if (ele.typ == VM_OBJ && CVmObjFrameDesc::is_framedesc_obj(vmg_ ele.val.obj)) { /* look up the value in this table */ CTcSymbol *ret = find_local(sym, len, ele.val.obj); /* if we found it, return the value */ if (ret != 0) return ret; } } /* we didn't find a value */ return 0; } } /* find a symbol in a given local table */ CTcSymbol *find_local(const textchar_t *sym, size_t len, vm_obj_id_t fref) { /* establish access to globals */ VMGLOB_PTR(gptr_); /* get the frame descriptor object */ CVmObjFrameDesc *fp = (CVmObjFrameDesc *)vm_objp(vmg_ fref); /* look up the symbol */ CVmDbgFrameSymPtr info; if (fp->find_local(vmg_ sym, len, &info)) { /* return a new dynamic local symbol */ return new CTcSymDynLocal( sym, len, FALSE, fp->get_frame_ref_id(), fp->get_frame_ref(vmg0_)->get_var_frame_index(&info), info.is_ctx_local() ? info.get_ctx_arr_idx() : 0); } /* didn't find it - return failure */ return 0; } /* look up a symbol in our global table */ CTcSymbol *find_global(const textchar_t *sym, size_t len) { /* if there's no user-code table, there's nothing to search */ if (globals_ == VM_INVALID_OBJ) return 0; /* presume we won't find a symbol */ CTcSymbol *ret = 0; /* establish access to globals */ VMGLOB_PTR(gptr_); /* create a string for the symbol name; stack it for gc protection */ vm_val_t symstr; symstr.set_obj(CVmObjString::create(vmg_ FALSE, sym, len)); G_stk->push(&symstr); /* look up the symbol string in the table */ vm_val_t symval; vm_objp(vmg_ globals_)->index_val_ov(vmg_ &symval, globals_, &symstr); G_stk->push(&symval); /* check what we found */ switch (symval.typ) { case VM_OBJ: /* it's an object - map it to CTcSymObj or CTcSymMetaclass */ if (CVmObjClass::is_intcls_obj(vmg_ symval.val.obj)) { /* * it's an IntrinsicClass object - this represents a * metaclass, so map it to a CTcSymMetaclass symbol */ CVmObjClass *cl = (CVmObjClass *)vm_objp(vmg_ symval.val.obj); ret = new CTcSymMetaclass(sym, len, FALSE, cl->get_meta_idx(), symval.val.obj); } else if (vm_objp(vmg_ symval.val.obj)->get_invoker(vmg_ 0)) { /* * it's an invokable object - create a function-like object * symbol for it */ ret = new CTcSymFuncObj(sym, len, FALSE, symval.val.obj, FALSE, TC_META_UNKNOWN, 0); } else { /* * It's not an IntrinsicClass object, so it's a regular * CTcSymObj symbol. Check for special metaclasses that * the compiler is aware of. */ tc_metaclass_t meta = TC_META_UNKNOWN; if (CVmObjTads::is_tadsobj_obj(vmg_ symval.val.obj)) meta = TC_META_TADSOBJ; else if (CVmObjGramProd::is_gramprod_obj(vmg_ symval.val.obj)) meta = TC_META_GRAMPROD; else if (CVmObjDict::is_dictionary_obj(vmg_ symval.val.obj)) meta = TC_META_DICT; else if (CVmObjIntClsMod::is_intcls_mod_obj(vmg_ symval.val.obj)) meta = TC_META_ICMOD; /* create the object symbol */ ret = new CTcSymObj(sym, len, FALSE, symval.val.obj, FALSE, meta, 0); } break; case VM_PROP: /* it's a property ID - map to a CTcSymProp */ ret = new CTcSymProp(sym, len, FALSE, symval.val.prop); break; case VM_FUNCPTR: /* it's a function pointer */ { /* get the method header, for the parameter information */ CVmFuncPtr fp(vmg_ symval.val.ofs); /* set up the resolved function symbol */ ret = new CTcSymFunc( sym, len, FALSE, fp.get_min_argc(), fp.get_opt_argc(), fp.is_varargs(), TRUE, FALSE, FALSE, FALSE, TRUE); /* we know the absolute code address - set it */ ((CTcSymFunc *)ret)->set_abs_addr(symval.val.ofs); } break; case VM_ENUM: /* enumerated constant */ ret = new CTcSymEnum(sym, len, FALSE, symval.val.enumval, FALSE); break; case VM_BIFPTR: /* built-in function pointer */ ret = 0; { /* decode the function pointer */ ushort set_idx = symval.val.bifptr.set_idx; ushort func_idx = symval.val.bifptr.func_idx; /* get the function descriptor */ const vm_bif_desc *desc = G_bif_table->get_desc(set_idx, func_idx); /* if there's a valid descriptor, set up the symbol */ if (desc != 0 && desc->func != 0) { /* found it - set up the symbol */ ret = new CTcSymBif(sym, len, FALSE, set_idx, func_idx, TRUE, desc->min_argc, desc->min_argc + desc->opt_argc, desc->varargs); } } break; default: /* * it's either not in the table or it's not a type we can map * to a compiler symbol - return 'not found' */ ret = 0; break; } /* done with the gc protection */ G_stk->discard(2); /* return the symbol */ return ret; } /* VM globals */ vm_globals *gptr_; /* the local and global symbol table objects */ vm_obj_id_t globals_; vm_obj_id_t locals_; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler macro table. This works a lot like our global symbol * table, to provide program-defined macros to the tokenizer during dynamic * compilation. */ class CVmDynFuncMacros: public CTcMacroTable { public: CVmDynFuncMacros(VMG_ vm_obj_id_t tab) : hash_(512, new CVmHashFuncCS(), TRUE) { /* remember the globals */ globals_ = VMGLOB_ADDR; /* remember my symbol table object */ tab_ = tab; } /* find an entry */ CVmHashEntry *find(const char *sym, size_t len) { CVmHashEntry *entry; /* first, check to see if it's our underlying table */ entry = hash_.find(sym, len); if (entry != 0) return entry; /* if there's no user-code table, fail */ if (tab_ == VM_INVALID_OBJ) return 0; /* establish access to globals */ VMGLOB_PTR(globals_); /* create a string for the symbol name; stack it for gc protection */ vm_val_t symstr; symstr.set_obj(CVmObjString::create(vmg_ FALSE, sym, len)); G_stk->push(&symstr); /* look up the symbol string in the table */ vm_val_t symval; vm_objp(vmg_ tab_)->index_val_ov(vmg_ &symval, tab_, &symstr); G_stk->push(&symval); /* it has to be a list, and it has to have at least three elements */ const char *lst = symval.get_as_list(vmg0_); if (lst != 0 && vmb_get_len(lst) >= 3) { vm_val_t ele, subele; const char *exp, *arglst; uint flags = 0; int argc; const char **argv = 0; size_t *argvlen = 0; int i; /* the first element is the expansion string */ CVmObjList::index_list(vmg_ &ele, lst, 1); if ((exp = ele.get_as_string(vmg0_)) == 0) goto done; /* the second element is the argument list */ CVmObjList::index_list(vmg_ &ele, lst, 2); if ((arglst = ele.get_as_list(vmg0_)) == 0) goto done; /* figure the argument count */ argc = vmb_get_len(arglst); /* build the argument vector */ if (argc != 0) { /* allocate the string and length arrays */ argv = new const char*[argc]; argvlen = new size_t[argc]; /* populate the arrays */ for (i = 0 ; i < argc ; ++i) { /* retrieve this argument name string */ CVmObjList::index_list(vmg_ &subele, arglst, i+1); const char *n = subele.get_as_string(vmg0_); if (n == 0) goto done; /* add it to the string and length arrays */ argv[i] = n + VMB_LEN; argvlen[i] = vmb_get_len(n); } } /* the third element is the flags */ CVmObjList::index_list(vmg_ &ele, lst, 3); if (!ele.is_numeric(vmg0_)) goto done; flags = ele.num_to_int(vmg0_); /* create the entry */ entry = new CTcHashEntryPpDefine( sym, len, TRUE, (flags & 1) != 0, argc, (flags & 2) != 0, argv, argvlen, exp + VMB_LEN, vmb_get_len(exp)); /* add the entry to our cache */ hash_.add(entry); done: /* free the argument list */ if (argv != 0) delete [] argv; if (argvlen != 0) delete [] argvlen; } /* done with our gc protection */ G_stk->discard(2); /* return what we found */ return entry; } /* add an entry - ignore */ void add(CVmHashEntry *) { } /* remove an entry - ignore */ void remove(CVmHashEntry *) { } /* enumerate entries - ignore */ void enum_entries(void (*)(void *, class CVmHashEntry *), void *) { } /* dump to a file - ignore */ void debug_dump() { } private: /* VM globals */ vm_globals *globals_; /* the user LookupTable object */ vm_obj_id_t tab_; /* our cache of symbols we've already translated */ CVmHashTable hash_; }; /* ------------------------------------------------------------------------ */ /* * Grammar production alternative parser - compiler interface for dynamic * compilation */ class CTcGramAltFuncsDyn: public CTcGramAltFuncs { public: /* initialize with the parser object */ CTcGramAltFuncsDyn(CTcPrsSymtab *symtab) { this->symtab = symtab; } /* look up a property - look up or add to the global symbols */ CTcSymProp *look_up_prop(const CTcToken *tok, int show_err) { /* look up the symbol */ CTcSymProp *prop = (CTcSymProp *)symtab->find( tok->get_text(), tok->get_text_len()); /* if it's already defined, make sure it's a property */ if (prop != 0) { /* make sure it's a property */ if (prop->get_type() != TC_SYM_PROP) { /* log an error if appropriate */ if (show_err) { G_tok->log_error( TCERR_REDEF_AS_PROP, (int)tok->get_text_len(), tok->get_text()); } /* we can't use the symbol, so forget it */ prop = 0; } } else { /* * undefined - we can't add it for dynamic compilation, so log * an error */ if (show_err) { G_tok->log_error( TCERR_UNDEF_SYM, (int)tok->get_text_len(), tok->get_text()); } } /* return the property */ return prop; } /* declare a grammar production symbol */ CTcGramProdEntry *declare_gramprod(const char *txt, size_t len) { /* * we can't create symbols in the dynamic compiler - log an error * and return failure */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len, txt); return 0; } /* * Check an enum for use as a production token. For dynamic * compilation, we don't enforce the 'enum token' requirement that the * static compiler applies, because we don't have any information on * the 'enum token' attribute in the basic run-time symbol table. */ void check_enum_tok(class CTcSymEnum *) { } /* * EOF in the middle of an alternative - when we're parsing a * stand-alone grammar rule list, the rule list is the whole input * text, so EOF just means we've reached the end */ void on_eof(int * /*err*/) { } /* the parser's global symbol table */ CTcPrsSymtab *symtab; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler interface to the VM. */ class CVmDynFuncVMIfc: public CTcVMIfc { public: CVmDynFuncVMIfc(VMG0_) { /* remember the globals */ vmg = VMGLOB_ADDR; /* we haven't saved any objects on the stack yet */ gc_cnt = 0; } /* create a live object in the VM */ virtual tctarg_obj_id_t new_obj(tctarg_obj_id_t cls) { /* establish access to globals */ VMGLOB_PTR(vmg); /* create a TadsObject instance */ int argc = 0; if (cls != VM_INVALID_OBJ) { G_stk->push_obj(vmg_ cls); ++argc; } vm_obj_id_t id = CVmObjTads::create_from_stack(vmg_ 0, argc); /* push it for gc protection */ G_stk->push_check()->set_obj(id); ++gc_cnt; /* return the new ID */ return id; } /* validate an object value */ virtual int validate_obj(tctarg_obj_id_t obj) { VMGLOB_PTR(vmg); return G_obj_table->is_obj_id_valid(obj); } /* add a property value to an object */ virtual void set_prop(tctarg_obj_id_t obj, tctarg_prop_id_t prop, const CTcConstVal *c) { /* establish access to globals */ VMGLOB_PTR(vmg); /* set up a value based on the constant value */ vm_val_t val; switch (c->get_type()) { case TC_CVT_TRUE: val.set_true(); break; case TC_CVT_INT: val.set_int(c->get_val_int()); break; case TC_CVT_SSTR: val.set_obj(CVmObjString::create( vmg_ FALSE, c->get_val_str(), c->get_val_str_len())); break; case TC_CVT_OBJ: val.set_obj(c->get_val_obj()); break; case TC_CVT_PROP: val.set_propid((vm_prop_id_t)c->get_val_prop()); break; case TC_CVT_ENUM: val.set_enum(c->get_val_enum()); break; case TC_CVT_FLOAT: val.set_obj(CVmObjBigNum::create( vmg_ FALSE, c->get_val_float(), c->get_val_float_len())); break; case TC_CVT_LIST: case TC_CVT_FUNCPTR: case TC_CVT_ANONFUNCPTR: // $$$ implement later if necessary val.set_nil(); break; case TC_CVT_NIL: case TC_CVT_UNK: default: val.set_nil(); break; } /* set the property in the object */ vm_objp(vmg_ obj)->set_prop(vmg_ G_undo, obj, prop, &val); } /* validate a property value */ virtual int validate_prop(tctarg_prop_id_t prop) { VMGLOB_PTR(vmg); return prop <= G_image_loader->get_last_prop(vmg0_); } /* validate a built-in function pointer */ virtual int validate_bif(uint set_index, uint func_index) { VMGLOB_PTR(vmg); return G_bif_table->validate_entry(set_index, func_index); } /* validate a constant pool list address */ virtual int validate_pool_str(uint32_t ofs) { /* establish globals */ VMGLOB_PTR(vmg); /* validate the starting offset and full length prefix */ if (!G_const_pool->validate_ofs(ofs) || !G_const_pool->validate_ofs(ofs+1)) return FALSE; /* get the length */ size_t len = vmb_get_len(G_const_pool->get_ptr(ofs)); /* make sure the end of the string is valid as well */ if (!G_const_pool->validate_ofs(ofs + VMB_LEN + len - 1)) return FALSE; /* success */ return TRUE; } /* validate a constant pool string address */ virtual int validate_pool_list(uint32_t ofs) { /* establish globals */ VMGLOB_PTR(vmg); /* validate the starting offset and full length prefix */ if (!G_const_pool->validate_ofs(ofs) || !G_const_pool->validate_ofs(ofs+1)) return FALSE; /* get the length */ const char *p = G_const_pool->get_ptr(ofs); size_t len = vmb_get_len(p); p += VMB_LEN; /* make sure the end of the string is valid as well */ if (!G_const_pool->validate_ofs( ofs + VMB_LEN + (len*VMB_DATAHOLDER) - 1)) return FALSE; /* validate that each element is valid */ for ( ; len != 0 ; p += VMB_DATAHOLDER, --len) { if (*p < 0 || *p >= (int)VM_FIRST_INVALID_TYPE) return FALSE; } /* success */ return TRUE; } /* discard items pushed on the stack for gc protection */ void discard() { VMGLOB_PTR(vmg); G_stk->discard(gc_cnt); } protected: /* number of objects we've pushed for gc protection */ int gc_cnt; /* VM globals */ vm_globals *vmg; }; /* ------------------------------------------------------------------------ */ /* * Dynamic compiler results object */ /* throw an error based on the results */ void CVmDynCompResults::throw_error(VMG0_) { /* push the message text as the exception constructor argument */ if (msgbuf != 0) { /* push the message as a string object */ G_stk->push()->set_obj(CVmObjString::create( vmg_ FALSE, msgbuf, strlen(msgbuf))); /* done with the message buffer */ free_msgbuf(); } else { /* no message - push a nil argument */ G_stk->push()->set_nil(); } /* throw the error */ G_interpreter->throw_new_class(vmg_ G_predef->compiler_exc, 1, "code compilation failed"); } /* ------------------------------------------------------------------------ */ /* * Dynamic Compiler Interface */ /* * Construction */ CVmDynamicCompiler::CVmDynamicCompiler(VMG0_) { /* create our host interface */ hostifc_ = new CTcHostIfcDynComp(); /* initialize the compiler */ CTcMain::init(hostifc_, G_host_ifc->get_sys_res_loader(), "utf8"); /* * set up the compiler size globals from the corresponding load image * size records, so that dynamically generated byte code conforms to * the image file settings */ G_sizes.mhdr = G_interpreter->get_funchdr_size(); G_sizes.exc_entry = G_exc_entry_size; G_sizes.dbg_hdr = G_dbg_hdr_size; G_sizes.dbg_line = G_line_entry_size; G_sizes.lcl_hdr = G_dbg_lclsym_hdr_size; G_sizes.dbg_fmt_vsn = G_dbg_fmt_vsn; G_sizes.dbg_frame = G_dbg_frame_size; /* point the compiler to the loaded metaclass table */ G_metaclass_tab = G_meta_table; } /* * Destruction */ CVmDynamicCompiler::~CVmDynamicCompiler() { /* terminate the compiler */ CTcMain::terminate(); /* delete our host interface */ delete hostifc_; } /* * Get or create the global singleton instance. */ CVmDynamicCompiler *CVmDynamicCompiler::get(VMG0_) { /* if there's no compiler object, create one */ if (G_dyncomp == 0) G_dyncomp = new CVmDynamicCompiler(vmg0_); /* return the instance */ return G_dyncomp; } /* * Compile source code */ vm_obj_id_t CVmDynamicCompiler::compile( VMG_ int in_root_set, vm_obj_id_t globals, vm_obj_id_t locals, vm_obj_id_t macros, const vm_val_t *srcval, const char *src, size_t srclen, CVmDynCompMode mode, CVmDynCompDebug *dbg, CVmDynCompResults *results) { vm_obj_id_t coid = 0; CTcPrsSymtab *old_global_symtab; CTcMacroTable *old_macros; CVmDynFuncMacros *new_macros = 0; int pushcnt = 0; CTcPrsNode *node = 0; int frame_has_self = FALSE; CVmObjFrameDesc *framedesc = 0; CVmObjFrameRef *frameref = 0; /* * save the parser memory pool state, so we can reset it when we're * done (this allows us to discard any parser memory we allocate while * we're working - we only need it while compiling, and can discard it * when we're done) */ tcprsmem_state_t prsmem_state; G_prsmem->save_state(&prsmem_state); /* * Set the local symbol table context. If the caller provided a * debugger context, use the debugger symbol table; otherwise there's * no local symbol context. */ CTcPrsDbgSymtab *old_symtab = G_prs->set_debug_symtab( dbg != 0 ? dbg->symtab : 0); /* don't allow #define or other directives for dynamic compilation */ G_tok->enable_pp(FALSE); /* enable the special debugger syntax if it's for the debugger */ G_prs->set_debug_expr(dbg != 0); /* set up a dynamic VM interface for the compiler */ CVmDynFuncVMIfc vmifc Pvmg0_P; G_vmifc = &vmifc; /* * If there's no debugger context, set up the global symbol table and * the new macro table. */ if (dbg == 0) { /* install a new symbol table; remember the old one to restore */ old_global_symtab = G_prs->set_global_symtab( new CVmDynFuncSymtab(vmg_ globals, locals)); /* install a new macro table; remember the old one to restore */ old_macros = G_tok->set_defines_table( new_macros = new CVmDynFuncMacros(vmg_ macros)); } /* presume no error will occur */ int errcode = 0, errflag = FALSE; results->err = 0; results->free_msgbuf(); /* presume we won't generate a statement node */ CTPNCodeBody *cb = 0; /* catch any errors that occur during parsing or code generation */ err_try { /* * If there's a local stack frame object, note if it has a method * context ('self', etc - it's sufficient to check that 'self' is * not nil). If so, code defined with 'function()' uses the method * context of the enclosing stack frame, in analogy to an anonymous * function using its enclosing lexical scope's method context. If * we have multiple stack frames, consider only the innermost. */ if (locals != VM_INVALID_OBJ) { /* presume we have just a single frame object */ vm_obj_id_t frame = locals; /* * if it's not in fact a stack frame object, and it's * list-like, it's a collection of frames ordered from * innermost to outermost: so retrieve the first element to get * the innermost frame */ CVmObject *localp = vm_objp(vmg_ locals); if (!CVmObjFrameDesc::is_framedesc_obj(vmg_ locals) && localp->is_listlike(vmg_ locals) && localp->ll_length(vmg_ locals) > 0) { /* * it's a list - retrieve its first element; if that's an * object, use it as the frame object */ vm_val_t ele, v1; v1.set_int(1); localp->index_val_ov(vmg_ &ele, locals, &v1); if (ele.typ == VM_OBJ) frame = ele.val.obj; } /* if we now have a frame object, check its 'self' */ if (CVmObjFrameDesc::is_framedesc_obj(vmg_ frame)) { /* get the frame object, suitably cast */ framedesc = (CVmObjFrameDesc *)vm_objp(vmg_ frame); frameref = framedesc->get_frame_ref(vmg0_); /* get 'self' from the frame */ vm_val_t self; frameref->get_self(vmg_ &self); /* if that's not nil, we have a method context */ frame_has_self = (self.typ != VM_NIL); } } /* reset the compiler error counters for the new parse */ G_tcmain->reset_error_counts(); /* clear any old error messages in our host interface */ hostifc_->reset_messages(); /* point the tokenizer to the source string */ G_tok->set_source_buf(src, srclen); /* read the first token */ G_tok->next(); /* parse the source code */ switch (mode) { case DCModeExpression: /* parse an expression */ if ((node = G_prs->parse_expr()) != 0) { /* check some extra information for the debugger */ // $$$ does this need to be deferred until after folding // constants and adjust_for_dyn??? if (dbg != 0 && node != 0) { /* the debugger wants to know if it's an lvalue */ dbg->is_lval = node->check_lvalue_resolved( G_prs->get_global_symtab()); } /* wrap it in a 'return' and a code body */ cb = new CTPNCodeBody(G_prs->get_global_symtab(), 0, new CTPNStmReturn(node), 0, 0, FALSE, FALSE, 0, 0, dbg != 0 ? dbg->self_valid : FALSE, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } break; case DCModeAuto: /* * Auto-sensing mode - parse a function or expression based on * what the string looks like. * * If the first token is 'function', we have a function * definition of the form "function(args) { body }". This is * compiled as an ordinary function with no name. * * If the first token is 'method', we have a method definition * of the form "method(args) { body }". This is compiled as a * floating method definition with no name. * * Otherwise, we have an expression. We compile this as an * expression, then wrap it in a "return" statement and a * function code body with zero arguments. */ if (G_tok->cur() == TOKT_FUNCTION) { /* skip the 'function' token */ G_tok->next(); /* parse the code body */ cb = G_prs->parse_code_body(FALSE, FALSE, frame_has_self, 0, 0, 0, 0, 0, 0, &errflag, 0, TCPRS_CB_NORMAL, 0, 0, 0, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } else if (G_tok->cur() == TOKT_METHOD) { /* skip the 'method' token */ G_tok->next(); /* parse the code body */ cb = G_prs->parse_code_body(FALSE, FALSE, TRUE, 0, 0, 0, 0, 0, 0, &errflag, 0, TCPRS_CB_NORMAL, 0, 0, 0, 0); /* mark it as a dynamic method */ cb->set_dyn_method(TRUE); } else { /* parse an expression */ if ((node = G_prs->parse_expr()) != 0) { /* wrap it in a 'return' an a zero-argument code body */ cb = new CTPNCodeBody(G_prs->get_global_symtab(), 0, new CTPNStmReturn(node), 0, 0, FALSE, FALSE, 0, 0, dbg != 0 ? dbg->self_valid : FALSE, 0); /* mark it as a dynamic function */ cb->set_dyn_func(TRUE); } } break; case DCModeGramAlt: /* parse a grammar alternative list */ { /* set up a dummy symbol to represent the match object */ CTcSymObj *gram_obj = new CTcSymObj( ".anon", 5, FALSE, VM_INVALID_OBJ, FALSE, TC_META_TADSOBJ, 0); /* set up a production to hold the list */ CTcGramProdEntry *prod = new (G_prsmem) CTcGramProdEntry(gram_obj); /* parse the alternative list */ CTcGramPropArrows arrows; CTcGramAltFuncsDyn funcs(G_prs->get_global_symtab()); G_prs->parse_gram_alts( &errflag, gram_obj, prod, &arrows, &funcs); /* * if the parse was successful, send the parsed data back * to the caller */ if (!errflag && G_tcmain->get_error_count() == 0) results->save_grammar(vmg_ prod->get_alt_head(), &arrows); } break; } /* * if we didn't get a parse node out of the deal, or any compiler * errors occurred, fail */ if (cb == 0 || errflag || G_tcmain->get_error_count() != 0) goto compilation_done; /* assign an object ID for CodeBody for the main code we parsed */ coid = vm_new_id(vmg_ in_root_set, TRUE, FALSE); cb->set_dyn_obj_id(coid); /* * create a dummy object for it - we'll replace it with a real * object when actually generating the code */ new (vmg_ coid) CVmDynamicFunc(); /* save it on the stack for GC protection */ G_stk->push_check()->set_obj(coid); ++pushcnt; /* * Allocate object IDs for the nested code bodies. Each nested * code body requires its own separate CodeBody object. Nested * code bodies are typically referenced from the main code body, * and might cross-reference one another, so in order to generate * code for any of the code bodies, we have to pre-assign an object * ID to *all* code bodies first. */ for (CTPNStmTop *ts = G_prs->get_first_nested_stm() ; ts != 0 ; ts = ts->get_next_stm_top()) { /* allocate and assign the object ID */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); new (vmg_ id) CVmDynamicFunc(); ts->set_dyn_obj_id(id); /* save it on the stack for GC protection */ G_stk->push_check()->set_obj(id); ++pushcnt; } /* generate the code for the main object */ if (!gen_code_body(vmg_ cb, srcval, dbg)) goto compilation_done; /* * If necessary, store the method context in the object. This is * required if this is defined with 'function' rather than * 'method', and it references 'self' or other method context * variables, and there's a local frame to use for the method * context. */ if (frame_has_self && cb->is_dyn_func() && (cb->self_referenced() || cb->full_method_ctx_referenced())) { /* * We need the method context. The context is simply the * 'self' object if the rest of the context wasn't referenced, * otherwise it's the context object suitable for the LOADCTX * opcode. */ vm_val_t mctx; if (cb->full_method_ctx_referenced()) { /* we need the full method context object for LOADCTX */ frameref->create_loadctx_obj(vmg_ &mctx); } else { /* the context is simply the 'self' object */ frameref->get_self(vmg_ &mctx); } /* save the method context in the DynamicFunc */ ((CVmDynamicFunc *)vm_objp(vmg_ coid))->set_method_ctx(&mctx); } /* generate each nested statement code body */ for (CTPNStmTop *ts = G_prs->get_first_nested_stm() ; ts != 0 ; ts = ts->get_next_stm_top()) { if (!gen_code_body(vmg_ ts, 0, dbg)) goto compilation_done; } compilation_done: ; } err_catch(exc) { /* note the error code */ errcode = exc->get_error_code(); /* if there were no logged errors, use the error from the exception */ if (G_tcmain->get_error_count() == 0) { /* delete any old message text */ results->free_msgbuf(); /* format the message into the result object */ const char *msg = tcerr_get_msg(errcode, FALSE); results->msgbuf = err_format_msg(msg, exc); } /* we don't have a valid object */ coid = VM_INVALID_OBJ; } err_end; /* if a compilation error occurred, return failure */ if (errcode != 0 || errflag) { /* parsing failed - stop now */ coid = VM_INVALID_OBJ; goto done; } else if (G_tcmain->get_error_count() != 0 || coid == VM_INVALID_OBJ) { /* parse errors were reported - retrieve the first and stop */ errcode = G_tcmain->get_first_error(); coid = VM_INVALID_OBJ; goto done; } done: /* restore the original symbol table and macros in the parser */ G_prs->set_debug_symtab(old_symtab); /* reset the code generator streams */ G_ds->reset(); G_cs->reset(); G_os->reset(); /* restore the old global symbol and macro tables */ if (dbg == 0) { G_prs->set_global_symtab(old_global_symtab); G_tok->set_defines_table(old_macros); } /* delete the macro table if we created one */ if (new_macros != 0) delete new_macros; /* reset the parser and tokenizer */ G_prs->reset(); G_tok->reset(); /* we're done with the compiler's working state */ G_prsmem->reset(&prsmem_state); /* * if there were logged compiler errors, and we haven't already * returned an exception message, return the captured compiler error * messages from the host interface */ if (G_tcmain->get_error_count() != 0 && results->msgbuf == 0) results->msgbuf = lib_copy_str(hostifc_->get_error_msg()); /* return the error code result */ results->err = errcode; /* discard our GC protection, plus the vmifc's */ G_stk->discard(pushcnt); vmifc.discard(); /* return the DynamicFunc we created, if any */ return coid; } /* * Generate code for a parsed code body. Parsing a single block of source * code might yield multiple parsed code bodies, because anonymous * functions and other nested structures require separate top-level code * bodies. */ int CVmDynamicCompiler::gen_code_body( VMG_ CTPNStmTop *node, const vm_val_t *srcval, CVmDynCompDebug *dbg) { int i; CTcIdFixup *fixup; /* clear the object fixup list - we want just this object's fixups */ G_objfixup = 0; /* our code will start at the current code stream write pointer */ ulong start_ofs = G_cs->get_ofs(); /* fold constants */ if (node->fold_constants(G_prs->get_global_symtab()) == 0) return FALSE; /* * Make adjustments for dynamic compilation. If the caller provided * debugger evaluation options, use the dynamic adjustment options * specified there; otherwise use defaults. */ tcpn_dyncomp_info di; if (node->adjust_for_dyn(dbg != 0 ? &dbg->di : &di) == 0) return FALSE; /* * Set the code generation mode. If we have debugger options, set * debug mode; otherwise set dynamic compilation mode. */ if (dbg != 0) G_cg->set_debug_eval(dbg->di.speculative, dbg->di.stack_level); else G_cg->set_dyn_eval(); /* * Keep object fixups - we need these to find object references in the * generated bytecode. We don't need property or enum fixups, since * we're building against a loaded image file with all of those types * already resolved. */ G_keep_objfixups = TRUE; /* make the global symbol table active for code generation */ G_cs->set_symtab(G_prs->get_global_symtab()); /* generate the code */ node->gen_code(FALSE, FALSE); /* if any errors occurred during code generation, return failure */ if (G_tcmain->get_error_count() != 0) return FALSE; /* * Go through the object fixup list and count up references to objects * that aren't in the root set. We need to keep a list of these * references in the generated code object, because the code object * needs to manage them like any other object manages its live * references: mark them during garbage collection, fix them up during * save/restore, etc. */ int refcnt = 0; for (fixup = G_objfixup ; fixup != 0 ; fixup = fixup->nxt_) { /* if it refers to a non-root-set object, count it */ if (!G_obj_table->is_obj_in_root_set((vm_obj_id_t)fixup->id_)) ++refcnt; } /* figure the bytecode length for allocating the DynamicFunc instance */ size_t bytecode_rem = (size_t)(G_cs->get_ofs() - start_ofs); /* the caller already allocated our object ID - retrieve it */ vm_obj_id_t coid = node->get_dyn_obj_id(); /* create the C++ object */ CVmDynamicFunc *co = new (vmg_ coid) CVmDynamicFunc( vmg_ coid, srcval, bytecode_rem, refcnt); /* save the object reference list */ for (i = 0, fixup = G_objfixup ; fixup != 0 ; fixup = fixup->nxt_) { /* if it refers to a non-root-set object, save it */ if (!G_obj_table->is_obj_in_root_set((vm_obj_id_t)fixup->id_)) co->get_ext()->obj_refs[i++] = fixup->ofs_ - start_ofs; } /* copy the code */ char *bc = co->get_ext()->get_bytecode_ptr(); for (size_t blkofs = 0 ; bytecode_rem != 0 ; ) { /* get the next block */ ulong blklen; const char *blkp = G_cs->get_block_ptr( start_ofs + blkofs, bytecode_rem, &blklen); /* copy the data */ memcpy(bc + blkofs, blkp, blklen); /* advance past the copied data */ blkofs += blklen; bytecode_rem -= blklen; } /* success */ return TRUE; } frobtads-1.2.3/tads3/vmimgrb.cpp0000644000175000001440000020033312145504453015675 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmimgrb.cpp - T3 Image File Re-Builder Function This module re-builds an image file from the contents of memory after the program has been "pre-initialized." This allows a program to run through its static state initialization during the compilation process, then store the result as a new image file with pre-initialized state. Any initialization that must always happen for every run of the program can be performed during this pre-initialization pass, saving the time of doing the work each time the program is run. Mostly, we just copy the old image file to the new image file; most parts of the image file are copied without changes. We update the object stream, replacing the original objects with the objects in their pre-initialized state, and we add any new strings dynamically created during pre-initialization to the constant pool. Notes Modified 07/21/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmfile.h" #include "vmimage.h" #include "vmpool.h" #include "vmglob.h" #include "vmwrtimg.h" #include "vmobj.h" #include "vmtobj.h" #include "vmdict.h" #include "vmhash.h" #include "vmlst.h" #include "vmbignum.h" #include "vmstr.h" #include "vmgram.h" #include "vmmeta.h" #include "vmimgrb.h" #include "vmintcls.h" #include "vmiter.h" #include "vmvec.h" #include "vmlookup.h" #include "vmstack.h" #include "vmbytarr.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmtmpfil.h" #include "vmfilnam.h" #include "vmpat.h" #include "vmstrcmp.h" #include "vmstrbuf.h" #include "vmdynfunc.h" #include "vmfref.h" #include "vmdate.h" #include "vmtzobj.h" #include "vmtz.h" #ifdef TADSNET #include "vmhttpsrv.h" #include "vmhttpreq.h" #endif /* ------------------------------------------------------------------------ */ /* * Write a string to a buffer with a one-byte prefix */ static char *write_str_byte_prefix(char *ptr, const char *str, size_t len) { /* make sure it fits the byte prefix */ if (len > 255) err_throw(VMERR_STR_TOO_LONG); /* write the prefix and the string */ oswp1(ptr, (char)len); memcpy(ptr + 1, str, len); /* return the pointer to the next byte after the copied data */ return ptr + len + 1; } #if 0 // not currently needed static char *write_str_byte_prefix(char *ptr, const char *str) { return write_str_byte_prefix(ptr, str, strlen(str)); } #endif /* ------------------------------------------------------------------------ */ /* * Rebuild the OBJS blocks and write the data to the file. This goes * through the objects in memory and constructs fixed image-file * versions of the objects, then writes them to OBJS blocks. */ static void vm_rewrite_objs_blocks(VMG_ CVmImageWriter *writer, class CVmConstMapper *mapper) { size_t i; /* rewrite the image block for each of our defined metaclasses */ for (i = 0 ; i < G_meta_table->get_count() ; ++i) { /* write this metaclass's objects */ G_obj_table->rebuild_image(vmg_ i, writer, mapper); } } /* ------------------------------------------------------------------------ */ /* * Re-write the image file */ void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp, ulong static_cs_ofs) { char buf[4096]; int done; CVmImageWriter *writer; CVmConstMapper *const_mapper; uchar xor_mask; ulong code_page_size; /* we don't know the code page size yet */ code_page_size = 0; /* choose an arbitrary XOR mask for our pages */ xor_mask = 0xEF; /* create an image writer to help us write the output file */ writer = new CVmImageWriter(newfp); /* create our constant mapper */ const_mapper = new CVmConstMapper(vmg0_); /* * clear all undo information - we don't save undo with the rebuilt * file, so there's no reason to keep any of the objects that are * referenced only in the undo records */ G_undo->drop_undo(vmg0_); /* discard everything on the stack */ G_stk->discard(G_stk->get_depth()); /* * perform a full garbage collection pass, to make sure we don't * include any unreachable objects in the new image file */ G_obj_table->gc_full(vmg0_); /* add any metaclasses that weren't in the dependency table originally */ G_obj_table->add_metadeps_for_instances(vmg0_); /* convert objects to constant data to the extent possible */ G_obj_table->rebuild_image_convert_const_data(vmg_ const_mapper); /* * copy the header (signature, UINT2 format version number, 32 * reserved bytes, 24-byte compilation timestamp) to the new file */ origfp->read_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24); newfp->write_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24); /* copy or replace the data blocks */ for (done = FALSE ; !done ; ) { ulong siz; /* read the next block header */ origfp->read_bytes(buf, 10); /* get the size */ siz = t3rp4u(buf + 4); /* check the block type */ if (CVmImageLoader::block_type_is(buf, "OBJS") || CVmImageLoader::block_type_is(buf, "MCLD") || CVmImageLoader::block_type_is(buf, "SINI")) { /* * Simply skip all of the original OBJS (object data) or * MCLD (metaclass dependency table) blocks -- we'll replace * them with our re-built blocks. * * Also skip SINI (static initializer) blocks, since these * are only needed for pre-initialization and can be * discarded from the final image file. */ origfp->set_pos(origfp->get_pos() + (long)siz); } else if (CVmImageLoader::block_type_is(buf, "CPDF")) { char cpdf_buf[20]; uint pool_id; ulong pgcnt; ulong pgsiz; /* read the pool definition */ origfp->read_bytes(cpdf_buf, 10); /* get the ID, page count, and page size from the definition */ pool_id = osrp2(cpdf_buf); pgcnt = t3rp4u(cpdf_buf + 2); pgsiz = t3rp4u(cpdf_buf + 6); /* * if this is the constant pool (pool ID = 2), increase the * page count by the extra constant pool pages we need for * converting new object data to constants */ if (pool_id == 2) { /* add in our new count */ pgcnt += const_mapper->get_page_count(); /* write the new count */ oswp4(cpdf_buf + 2, pgcnt); } /* * if this is the code pool (pool ID = 1), and we have * static initializers, decrease the page count to exclude * the static initializer pages (all of the static * initializers are grouped at the high end of the code * pool, so we can discard them and only them by truncating * the code pool before the page containing the first static * initializer) */ if (pool_id == 1 && static_cs_ofs != 0) { /* * calculate the new count - it's the offset to the * first static initializer divided by the size of each * code page */ pgcnt = static_cs_ofs / pgsiz; /* write the new count */ oswp4(cpdf_buf + 2, pgcnt); /* * remember the code page size for later, when we're * scanning the code pages themselves */ code_page_size = pgsiz; } /* update the constant block definition */ newfp->write_bytes(buf, 10); newfp->write_bytes(cpdf_buf, 10); } else if (CVmImageLoader::block_type_is(buf, "CPPG")) { char cppg_buf[20]; long start_pos; uint pool_id; ulong page_idx; int keep_block; /* presume we're going to keep this block */ keep_block = TRUE; /* * This is a constant page - if it's in pool 2 (constants), * use its XOR mask for any new pages we write. First, read * the pool header, then seek back so we can copy the block * unchanged. */ start_pos = origfp->get_pos(); origfp->read_bytes(cppg_buf, 7); origfp->set_pos(start_pos); /* get the pool ID and the page's index */ pool_id = osrp2(cppg_buf); page_idx = t3rp4u(cppg_buf + 2); /* if it's pool 2 (constants), read its XOR mask byte */ if (pool_id == 2) xor_mask = cppg_buf[6]; /* * if it's pool 1 (code), and it's above the start of the * static initializers, skip it - we don't want to copy * static initializer code to the final image file, since * they're only needed for static initialization, which we * necessarily have finished by the time we reach this point */ if (pool_id == 1 && static_cs_ofs != 0 && page_idx * code_page_size >= static_cs_ofs) { /* this is a static initializer block - skip it */ keep_block = FALSE; } /* keep or skip the block, as appropriate */ if (keep_block) { /* * we only wanted to get information from this block, so * go copy it as-is to the output */ goto copy_block; } else { /* skip past the block */ origfp->set_pos(origfp->get_pos() + (long)siz); } } else if (CVmImageLoader::block_type_is(buf, "EOF ")) { /* end of file - note that we're done after this block */ done = TRUE; /* re-write the metaclass dependency block */ G_meta_table->rebuild_image(writer); /* write our new OBJS blocks */ vm_rewrite_objs_blocks(vmg_ writer, const_mapper); /* write our new constant pool pages */ const_mapper->write_to_image_file(writer, xor_mask); /* copy the EOF block to the new file unchanged */ goto copy_block; } else { copy_block: /* * For anything else, we'll simply copy the original block * from the original image file unchanged. */ /* write the block header unchanged */ newfp->write_bytes(buf, 10); /* copy the block in chunks */ while (siz != 0) { size_t cur; /* * read as much as we can, up to the amount remaining or * the buffer size, whichever is smaller */ cur = sizeof(buf); if (cur > siz) cur = (size_t)siz; /* read the data and write it out */ origfp->read_bytes(buf, cur); newfp->write_bytes(buf, cur); /* deduct this chunk from the amount remaining */ siz -= cur; } } } /* we're done with the image writer */ delete writer; /* we're done with the constant mapper */ delete const_mapper; } /* ------------------------------------------------------------------------ */ /* * Object Table extension - rebuild the image file representations for * all objects of a particular metaclass. */ void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer, class CVmConstMapper *mapper) { int pass; /* write persistent and transient objects separately */ for (pass = 1 ; pass <= 2 ; ++pass) { int trans; /* write persistent on pass 1, transient on pass 2 */ trans = (pass == 2); /* write the objects for this pass */ rebuild_image(vmg_ meta_dep_idx, writer, mapper, trans); } } /* * Write all of the transient or persistent objects of a given metaclass. */ void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer, class CVmConstMapper *mapper, int trans) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; char *buf; ulong bufsiz; int block_cnt; ulong block_size; int large_objects; /* we haven't written anything to the file yet */ block_cnt = 0; block_size = 0; /* presume we'll use small objects */ large_objects = FALSE; /* * allocate an initial object buffer - we'll expand this as needed * if we find an object that doesn't fit */ bufsiz = 4096; buf = (char *)t3malloc((size_t)bufsiz); if (buf == 0) err_throw(VMERR_OUT_OF_MEMORY); /* go through each page in the object table */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { size_t j; CVmObjPageEntry *entry; /* start at the start of the page */ j = VM_OBJ_PAGE_CNT; entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* * if this entry is in use, and its transient/persistent type * matches the type we're writing, and its metaclass matches * the one we're writing, write it out */ if (!entry->free_ && (entry->transient_ != 0) == (trans != 0) && (G_meta_table->get_dependency_index( entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx()) == meta_dep_idx)) { ulong objsiz; /* * if this object has been mapped to a constant value, * there's no need to store it, since it is no longer * reachable */ if (mapper->get_pool_addr(id) != 0) continue; /* try building the object */ objsiz = entry->get_vm_obj()->rebuild_image(vmg_ buf, bufsiz); /* if they need more space, reallocate the buffer */ if (objsiz > bufsiz) { /* if the object is too large, throw an error */ if (objsiz > OSMALMAX) err_throw(VMERR_OBJ_SIZE_OVERFLOW); /* reallocate to next 4k increment */ bufsiz = (objsiz + 4095) & ~4095; buf = (char *)t3realloc(buf, (size_t)bufsiz); if (buf == 0) err_throw(VMERR_OUT_OF_MEMORY); /* try it again */ objsiz = entry->get_vm_obj()-> rebuild_image(vmg_ buf, bufsiz); } /* if the object is not empty, write it out */ if (objsiz != 0) { char prefix[20]; /* * if this object's size exceeds 64k, and the * current OBJS block is a small block, end this * OBJS block and begin a large OBJS block */ if (objsiz > 65535 && !large_objects) { /* if we have a block open, end it */ if (block_cnt != 0) writer->end_objs_block(block_cnt); /* reset the count and size for the new block */ block_cnt = 0; block_size = 0; /* make the next block a large block */ large_objects = TRUE; } /* * If this object plus its prefix would push this OBJS * block over 64k, and we have a small-object block * open, close it off and start a new block. Don't do * this if we're already in a large-objects block, * since that means we have a single object that we * can't fit in 64k, meaning that it would be * impossible to keep the block itself to within 64k. */ if (block_size + objsiz + 6 > 64000L && block_cnt != 0 && !large_objects) { /* close this block */ writer->end_objs_block(block_cnt); /* reset the count and size for the new block */ block_cnt = 0; block_size = 0; /* * go bak to the small-object block format if this * object doesn't itself require a large-objects * block */ if (objsiz <= 65535) large_objects = FALSE; } /* if this is the first object, write the header */ if (block_cnt == 0) writer->begin_objs_block(meta_dep_idx, large_objects, trans); /* write the prefix information */ oswp4(prefix, id); if (large_objects) { /* write the 32-bit object size */ oswp4(prefix + 4, objsiz); /* write the header */ writer->write_objs_bytes(prefix, 8); } else { /* write the 16-bit object size */ oswp2(prefix + 4, objsiz); /* write the header */ writer->write_objs_bytes(prefix, 6); } /* write the object data */ writer->write_objs_bytes(buf, objsiz); /* count the object */ ++block_cnt; /* count the size (including the prefix) */ block_size += objsiz + 6; } } } } /* if we wrote any objects, end the OBJS block */ if (block_cnt != 0) writer->end_objs_block(block_cnt); /* delete our buffer */ t3free(buf); } /* * Scan all active objects and convert objects to constant data where * possible. Certain object metaclasses, such as strings and lists, can * be represented in a rebuilt image file as constant data; this routine * makes all of these conversions. */ void CVmObjTable::rebuild_image_convert_const_data (VMG_ CVmConstMapper *const_mapper) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; /* * First pass: go through each page in the object table, and reserve * space for the constant conversion. This assigns each object that * can be converted its address in the new constant pool pages. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { size_t j; CVmObjPageEntry *entry; /* start at the start of the page, but skip object ID = 0 */ j = VM_OBJ_PAGE_CNT; entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* * if this entry is in use, tell it to reserve space for * conversion to constant data */ if (!entry->free_) entry->get_vm_obj() ->reserve_const_data(vmg_ const_mapper, id); } } /* prepare the constant mapper to begin storing */ const_mapper->prepare_to_store_data(); /* * Second pass: go through the objects once again and make the * conversions. We must do this on a separate second pass because * we must fix up all references to objects being converted, hence * we must know the conversion status of every object to be * converted before we can fix up anything. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { size_t j; CVmObjPageEntry *entry; /* start at the start of the page, but skip object ID = 0 */ j = VM_OBJ_PAGE_CNT; entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* if this entry is in use, convert it if possible */ if (!entry->free_) entry->get_vm_obj() ->convert_to_const_data(vmg_ const_mapper, id); } } } /* ------------------------------------------------------------------------ */ /* * Convert a value to constant data. If the value is an object that has * reserved constant data space for itself, we'll update the value to * reflect the constant data conversion for the value. */ static void convert_dh_to_const_data(VMG_ CVmConstMapper *mapper, char *p) { /* if the value is an object, check for conversion */ if (vmb_get_dh_type(p) == VM_OBJ) { vm_obj_id_t obj; ulong addr; /* get the object value */ obj = vmb_get_dh_obj(p); /* look up the object's converted constant pool address */ if ((addr = mapper->get_pool_addr(obj)) != 0) { vm_val_t val; /* this value has been converted - set the new value */ val.typ = vm_objp(vmg_ obj)->get_convert_to_const_data_type(); val.val.ofs = addr; vmb_put_dh(p, &val); } } } /* * Convert a vm_val_t value to constant data if appropriate. */ static void convert_val_to_const_data(VMG_ CVmConstMapper *mapper, vm_val_t *val) { /* if it's an object, check for conversion */ if (val->typ == VM_OBJ) { ulong addr; /* look up the object's converted constant pool address */ if ((addr = mapper->get_pool_addr(val->val.obj)) != 0) { /* this value has been converted - set the new value */ val->typ = vm_objp(vmg_ val->val.obj) ->get_convert_to_const_data_type(); val->val.ofs = addr; } } } /* ------------------------------------------------------------------------ */ /* * List Metaclass implementation - image rebuilding */ /* * Build an image file record for the list. We need this because we * can't always convert a list to a constant value when rebuilding an * image file; in particular, if the list exceeds the size of a constant * pool page (unlikely but possible), we will have to store the list as * object data. */ ulong CVmObjList::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; /* calculate how much space we need to store the data */ copy_size = calc_alloc(vmb_get_len(ext_)); /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ memcpy(buf, ext_, copy_size); /* return the size */ return copy_size; } /* * Reserve space in a rebuilt constant pool for converting our data to a * constant value. */ void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* reserve the space for our list data */ mapper->alloc_pool_space(self, calc_alloc(vmb_get_len(ext_))); } /* * Convert to constant data. We must check each object that we * reference via a modified property and convert it to a constant data * item if necessary. Then, we must store our data in the constant * pool, if we successfully reserved an address for it. */ void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { size_t cnt; char *p; /* get my element count */ cnt = vmb_get_len(ext_); /* mark as referenced each object in our list */ for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) { /* convert the value to constant data if possible */ convert_dh_to_const_data(vmg_ mapper, p); } /* * if we managed to convert our object to constant data, store our * value */ if (mapper->get_pool_addr(self)) mapper->store_data(self, ext_, calc_alloc(vmb_get_len(ext_))); } /* ------------------------------------------------------------------------ */ /* * String Metaclass implementation - image rebuilding */ /* * Build an image file record for the string. We need this because we * can't always convert a string to a constant value when rebuilding an * image file; in particular, if the string exceeds the size of a * constant pool page (unlikely but possible), we will have to store the * string as object data. */ ulong CVmObjString::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; /* calculate how much space we need to store the data */ copy_size = vmb_get_len(ext_) + VMB_LEN; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ memcpy(buf, ext_, copy_size); /* return the size */ return copy_size; } /* * Reserve space in a rebuilt constant pool for converting our data to a * constant value. */ void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* reserve the space for our string data */ mapper->alloc_pool_space(self, vmb_get_len(ext_) + VMB_LEN); } /* * Convert to constant data. We don't reference any objects, so we need * simply store our data in the constant pool, if we successfully * reserved an address for it. */ void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* * if we managed to convert our object to constant data, store our * value */ if (mapper->get_pool_addr(self)) mapper->store_data(self, ext_, vmb_get_len(ext_) + VMB_LEN); } /* ------------------------------------------------------------------------ */ /* * TADS Object implementation - image rebuilding */ /* property comparison callback for qsort() */ extern "C" { static int prop_compare(const void *p1, const void *p2) { uint id1, id2; /* get the ID's */ id1 = osrp2(p1); id2 = osrp2(p2); /* compare them and return the result */ return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1); } } /* * build an image file record for the object */ ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen) { /* * Make sure the buffer is big enough. Start out with worst-case * assumption that we'll need every allocated property slot; we might * actually need fewer, since some of the slots could be empty by * virtue of having been undone. We need space for our own header * (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a * UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property. */ size_t max_size = (2 + 2 + 2) + get_sc_count() * 4 + (get_hdr()->prop_entry_free * 7); /* if it's more than we have available, ask for more space */ if (max_size > buflen) return max_size; /* * set up our header - use a placeholder 0 for the property count * for now, until we calculate the real value */ oswp2(buf, get_sc_count()); oswp2(buf+2, 0); oswp2(buf+4, get_li_obj_flags()); char *p = buf + 6; /* copy the superclass list */ for (size_t i = 0 ; i < get_sc_count() ; ++i, p += 4) oswp4(p, get_sc(i)); /* remember where the properties start */ char *props = p; /* copy the non-empty property slots */ vm_tadsobj_prop *entry = get_hdr()->prop_entry_arr; size_t prop_cnt = 0; for (size_t i = get_hdr()->prop_entry_free ; i != 0 ; --i, ++entry) { /* if this slot is non-empty, store it */ if (entry->val.typ != VM_EMPTY) { /* set up this slot */ oswp2(p, entry->prop); vmb_put_dh(p + 2, &entry->val); /* count the additional property */ ++prop_cnt; /* move past it in the buffer */ p += 7; } } /* fill in actual the property count now that we know it */ oswp2(buf+2, prop_cnt); /* sort the new table */ qsort(props, prop_cnt, 7, &prop_compare); /* return the final size */ return p - buf; } /* * Convert to constant data. We must check each object that we * reference via a modified property and convert it to a constant data * item if necessary. */ void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t /*self*/) { size_t i; vm_tadsobj_prop *entry; /* * Scan the property entries. Note that we don't have to worry about * the original properties, since they can only refer to data that was * in the original image file, and hence never need to be converted -- * if it was good enough for the original image file, it's good enough * for us. */ for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ; i != 0 ; --i, ++entry) { /* if this slot is modified, convert it */ if ((entry->flags & VMTO_PROP_MOD) != 0 && entry->val.typ != VM_EMPTY) { /* convert the value */ convert_val_to_const_data(vmg_ mapper, &entry->val); } } } /* ------------------------------------------------------------------------ */ /* * Dictionary object implementation - image rebuilding */ /* * callback context for image rebuild */ struct rebuild_ctx { /* space needed so far */ size_t space_needed; /* number of entries */ size_t entry_cnt; /* next available output pointer */ char *dst; }; /* * rebuild for image file */ ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen) { rebuild_ctx ctx; /* * calculate the amount of space we need - start with the comparator * object, which needs four bytes, and the entry count, which needs two * bytes */ ctx.space_needed = 6; /* enumerate the entries to count space */ ctx.entry_cnt = 0; get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx); /* if we need more space than is available, ask for more */ if (ctx.space_needed > buflen) return ctx.space_needed; /* write the comparator object and the entry count */ oswp4(buf, get_ext()->comparator_); oswp2(buf + 4, ctx.entry_cnt); /* start writing after the count */ ctx.dst = buf + 6; /* enumerate the entries to write to the image buffer */ get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx); /* return the size */ return ctx.dst - buf; } /* * enumeration callback - rebuild phase 1: count the space needed for * this table entry */ void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0) { rebuild_ctx *ctx = (rebuild_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; /* * count the space needed for this string - one byte for the length * prefix, the bytes of the name string, and two bytes for the item * count */ ctx->space_needed += 1 + entry->getlen() + 2; /* count this entry */ ++ctx->entry_cnt; /* count the items */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* * add the space for this item - four byte for the object ID, * two bytes for the property ID */ ctx->space_needed += 6; } } /* * enumeration callback - rebuild phase 2: write the entries to the * image buffer */ void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0) { rebuild_ctx *ctx = (rebuild_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; size_t cnt; char *p; size_t rem; /* count the entries in our list */ for (cnt = 0, cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_, ++cnt) ; /* write the length of the key string followed by the key string */ *ctx->dst++ = (char)entry->getlen(); memcpy(ctx->dst, entry->getstr(), entry->getlen()); /* xor the key string's bytes */ for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ; --rem, ++p) *p ^= 0xBD; /* move past the key string */ ctx->dst += entry->getlen(); /* write the item count */ oswp2(ctx->dst, cnt); ctx->dst += 2; /* write the items */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* write the object ID and property ID for this item */ oswp4(ctx->dst, (ulong)cur->obj_); oswp2(ctx->dst + 4, (uint)cur->prop_); ctx->dst += 6; } } /* * callback context for constant data conversion */ struct cvt_const_ctx { /* constant mapper */ CVmConstMapper *mapper; }; /* * convert to constant data */ void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { cvt_const_ctx ctx; /* make sure the comparator object isn't mappable to a constant */ if (mapper->get_pool_addr(get_ext()->comparator_) != 0) err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN, "", 12); /* * Go through our dictionary and make sure we don't have any * references to constant data. We don't actually have to perform * any conversions, because we simply don't allow references to * anything but TADS-object objects in the dictionary. */ ctx.mapper = mapper; get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx); } /* * enumeration callback - convert to constant data */ void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0) { cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0; CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0; vm_dict_entry *cur; /* inspect the items in this entry's list */ for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_) { /* if this item refers to constant data, it's an error */ if (ctx->mapper->get_pool_addr(cur->obj_) != 0) err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN, entry->getstr(), entry->getlen()); } } /* ------------------------------------------------------------------------ */ /* * Grammar production object - image rebuilding operations */ /* * rebuild for image file */ ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen) { if (get_ext()->modified_) { /* * We've been modified at run-time, so we need to rebuild the image * data from the current alt list. First, save to a counting * stream just to determine how much space we need. */ CVmCountingStream cstr; save_to_stream(vmg_ &cstr); /* make sure we have enough space */ if ((ulong)cstr.get_len() > buflen) return cstr.get_len(); /* save to the buffer */ CVmMemoryStream mstr(buf, buflen); save_to_stream(vmg_ &mstr); /* return the stream length */ return cstr.get_len(); } else { /* * We weren't modified, so simply copy back our original image data * unchanged. */ /* make sure we have room */ if (get_ext()->image_data_size_ > buflen) return get_ext()->image_data_size_; /* copy the data */ memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_); /* return the size */ return get_ext()->image_data_size_; } } /* ------------------------------------------------------------------------ */ /* * BigNumber Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; /* calculate how much space we need to store the data */ copy_size = calc_alloc(get_prec(ext_)); /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ memcpy(buf, ext_, copy_size); /* return the size */ return copy_size; } /* * Reserve space in a rebuilt constant pool for converting our data to a * constant value. */ void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* BigNumber values cannot be converted to constants */ } /* * Convert to constant data. We don't reference any other objects, so * we simply need to store our data in the constant pool, if we * successfully reserved an address for it. */ void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* * if we managed to convert our object to constant data, store our * value */ if (mapper->get_pool_addr(self)) mapper->store_data(self, ext_, calc_alloc(get_prec(ext_))); } /* ------------------------------------------------------------------------ */ /* * Object-to-Constant mapper */ /* * initialize */ CVmConstMapper::CVmConstMapper(VMG0_) { vm_obj_id_t max_id; size_t i; /* get the maximum object ID we've allocated */ max_id = G_obj_table->get_max_used_obj_id(); /* * Allocate our master translation page list so that we have room * for enough pages to store the maximum object ID. We need one * slot for each page of 1024 translation elements. */ obj_addr_cnt_ = max_id / 1024; obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0])); if (obj_addr_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear out the page slots - we haven't allocated any pages yet */ for (i = 0 ; i < obj_addr_cnt_ ; ++i) obj_addr_[i] = 0; /* get the constant pool's page size */ page_size_ = G_const_pool->get_page_size(); /* * our first page is the next page after the last page in the * current pool */ first_page_idx_ = G_const_pool->get_page_count(); /* * get the starting address - we'll start writing our data at the * first page after all existing pages in the pool */ base_addr_ = page_size_ * G_const_pool->get_page_count(); /* allocate from our base address */ next_free_ = base_addr_; /* we have the entire first page available */ rem_ = page_size_; /* * we haven't allocated any page data yet (we'll do this after we've * reserved all space, when the client invokes * prepare_to_store_data()) */ pages_ = 0; pages_cnt_ = 0; } /* * delete */ CVmConstMapper::~CVmConstMapper() { size_t i; /* delete each page in our mapping table */ for (i = 0 ; i < obj_addr_cnt_ ; ++i) { /* free this page if we allocated it */ if (obj_addr_[i] != 0) t3free(obj_addr_[i]); } /* delete our mapping table */ t3free(obj_addr_); /* delete each constant pool page */ for (i = 0 ; i < pages_cnt_ ; ++i) t3free(pages_[i]); /* free the page list */ if (pages_ != 0) t3free(pages_); } /* * Get an object's pool address, if it has one */ ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id) { /* determine if the page is mapped - if not, the object isn't mapped */ if (obj_addr_[obj_id / 1024] == 0) return 0; /* return the mapping entry */ return obj_addr_[obj_id / 1024][obj_id % 1024]; } /* * Allocate space in the pool for an object's data */ ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len) { ulong ret; /* * if the data block is too large to fit on a constant pool page, we * can't store it */ if (len > page_size_) return 0; /* if the translation page isn't mapped yet, map it */ if (obj_addr_[obj_id / 1024] == 0) { /* allocate a new page */ obj_addr_[obj_id / 1024] = (ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0])); /* if that failed, throw an error */ if (obj_addr_[obj_id / 1024] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* clear the new page */ memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0])); } /* if the block doesn't fit on this page, skip to the next page */ if (len > rem_) { /* skip past the remainder of this page */ next_free_ += rem_; /* we have the whole next page available now */ rem_ = page_size_; } /* remember the object ID's address in the translation list */ ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_; /* skip past the data */ next_free_ += len; rem_ -= len; /* return the new address */ return ret; } /* * Prepare to begin storing data */ void CVmConstMapper::prepare_to_store_data() { size_t i; /* figure out how many pages we need */ pages_cnt_ = calc_page_count(); /* allocate space for the list of pages */ pages_ = (vm_const_mapper_page **) t3malloc(pages_cnt_ * sizeof(pages_[0])); if (pages_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* allocate each page */ for (i = 0 ; i < pages_cnt_ ; ++i) { /* * allocate this page - allocate the header structure plus space * for the page data */ pages_[i] = (vm_const_mapper_page *) t3malloc(sizeof(pages_[i]) + page_size_); if (pages_[i] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* the page has nothing stored yet */ pages_[i]->max_ofs_used = 0; } } /* * Store an object's data */ void CVmConstMapper::store_data(vm_obj_id_t obj_id, const void *ptr, size_t len) { ulong addr; size_t page_idx; size_t page_ofs; /* get the pool address that was reserved for the object */ addr = get_pool_addr(obj_id); /* if the object had no space reserved, ignore the request */ if (addr == 0) return; /* figure out which page this address is in, and the offset in the page */ page_idx = (size_t)((addr - base_addr_) / page_size_); page_ofs = (size_t)((addr - base_addr_) % page_size_); /* * if this address takes us above the high-water mark for the page, * move the page's marker accordingly */ if (page_ofs + len > pages_[page_idx]->max_ofs_used) pages_[page_idx]->max_ofs_used = page_ofs + len; /* copy the data */ memcpy(pages_[page_idx]->buf + page_ofs, ptr, len); } /* * Write the pool pages to an image file */ void CVmConstMapper::write_to_image_file(CVmImageWriter *writer, uchar xor_mask) { size_t i; /* go through each of our pages */ for (i = 0 ; i < pages_cnt_ ; ++i) { /* write this page - it's in pool 2 (the constant data pool) */ writer->write_pool_page(2, first_page_idx_ + i, pages_[i]->buf, pages_[i]->max_ofs_used, TRUE, xor_mask); } } /* ------------------------------------------------------------------------ */ /* * metaclass table - image rewriter implementation */ /* * write the new metaclass dependency table */ void CVmMetaTable::rebuild_image(CVmImageWriter *writer) { size_t i; /* begin the new metaclass dependency table */ writer->begin_meta_dep(get_count()); /* write the new metaclass dependency table items */ for (i = 0 ; i < get_count() ; ++i) { vm_meta_entry_t *entry; ushort j; /* get this entry */ entry = get_entry(i); /* write this metaclass name */ writer->write_meta_dep_item(entry->image_meta_name_); /* * Write the property translation list. Note that xlat_func() * requires a 1-based index, so we loop from 1 to the count, * rather than following the usual C-style 0-based conventions. */ for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j) writer->write_meta_item_prop(entry->xlat_func(j)); } /* end the metaclass dependency table */ writer->end_meta_dep(); } /* ------------------------------------------------------------------------ */ /* * Hashtable Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; vm_lookup_ext *ext = get_ext(); uint i; vm_lookup_val **bp; vm_lookup_val *val; char *dst; uint idx; /* * we need space for the fixed header (6 bytes), 2 bytes per bucket, * the entries themselves, and the default value */ copy_size = 6 + (ext->bucket_cnt * 2) + (ext->value_cnt * VMLOOKUP_VALUE_SIZE) + VMB_DATAHOLDER; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* write the fixed data */ oswp2(buf, ext->bucket_cnt); oswp2(buf + 2, ext->value_cnt); idx = ext->val_to_img_idx(ext->first_free); oswp2(buf + 4, idx); dst = buf + 6; /* write the buckets */ for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp) { /* write this bucket's index */ idx = ext->val_to_img_idx(*bp); oswp2(dst, idx); dst += 2; } /* write the values */ for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val) { /* store the key, value, and index */ vmb_put_dh(dst, &val->key); vmb_put_dh(dst + VMB_DATAHOLDER, &val->val); idx = ext->val_to_img_idx(val->nxt); oswp2(dst + VMB_DATAHOLDER*2, idx); /* skip the data in the output */ dst += VMLOOKUP_VALUE_SIZE; } /* write the default value */ vmb_put_dh(dst, &ext->default_value); dst += VMB_DATAHOLDER; /* return the size */ return copy_size; } /* * Convert to constant data. We must convert each object we reference in * a key or in a value to constant data if possible. */ void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { uint i; vm_lookup_val *entry; /* run through each entry */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* convert the key */ convert_val_to_const_data(vmg_ mapper, &entry->key); /* convert the value */ convert_val_to_const_data(vmg_ mapper, &entry->val); } } /* ------------------------------------------------------------------------ */ /* * StringBuffer metaclass - image rebuilding */ ulong CVmObjStringBuffer::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; vm_strbuf_ext *ext = get_ext(); /* get our size */ copy_size = 12 + ext->len*2; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* write the header data */ oswp4(buf, ext->alo); oswp4(buf+4, ext->inc); oswp4(buf+8, ext->len); /* write the string */ memcpy(buf+12, ext->buf, ext->len*2); /* return the size */ return copy_size; } /* ------------------------------------------------------------------------ */ /* * IntrinsicClass Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen) { /* figure our size requirement: meta idx, mod id, state value */ size_t copy_size = 2 + 2 + 4 + VMB_DATAHOLDER; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ vm_intcls_ext *ext = get_ext(); oswp2(buf, (short)copy_size); oswp2(buf + 2, ext->meta_idx); oswp4(buf + 4, ext->mod_obj); vmb_put_dh(buf + 8, &ext->class_state); /* return the size */ return copy_size; } /* ------------------------------------------------------------------------ */ /* * Indexed Iterator */ /* * Build an image file record */ ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen) { /* calculate our data size - just store our entire extension */ size_t copy_size = VMOBJITERIDX_EXT_SIZE; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ memcpy(buf, ext_, copy_size); /* return the size */ return copy_size; } /* * Convert to constant data. We must convert our collection object * reference to constant data if possible. */ void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* convert our collection reference to constant data if possible */ convert_dh_to_const_data(vmg_ mapper, ext_); } /* ------------------------------------------------------------------------ */ /* * Hashtable Iterator */ /* * Build an image file record */ ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; /* calculate our data size - just store our entire extension */ copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* copy the data */ memcpy(buf, ext_, copy_size); /* return the size */ return copy_size; } /* * Convert to constant data. We must convert our collection object * reference to constant data if possible. */ void CVmObjIterLookupTable::convert_to_const_data( VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* convert our collection reference to constant data if possible */ convert_dh_to_const_data(vmg_ mapper, ext_); } /* ------------------------------------------------------------------------ */ /* * Vector Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen) { size_t copy_size; size_t ele_cnt; size_t alloc_cnt; /* * calculate how much space we need to store the data - store only * the data, not the undo bits */ ele_cnt = get_element_count(); alloc_cnt = get_allocated_count(); copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt); /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* save the allocation count and in-use element count */ vmb_put_len(buf, alloc_cnt); vmb_put_len(buf + VMB_LEN, ele_cnt); /* copy the element data */ memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt)); /* return the size */ return copy_size; } /* * Reserve space in a rebuilt constant pool for converting our data to a * constant value. */ void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { /* Vector values cannot be converted to constants */ } /* * Convert to constant data. We must convert each object we reference to * constant data if possible; we use the same algorithm as CVmObjList. */ void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t self) { size_t cnt; char *p; /* get my element count */ cnt = get_element_count(); /* mark as referenced each object in our list */ for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) { /* convert the value to constant data if possible */ convert_dh_to_const_data(vmg_ mapper, p); } } /* ------------------------------------------------------------------------ */ /* * ByteArray Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen) { ulong copy_size; ulong rem; ulong idx; /* we need four bytes for our count plus space for our byte array */ copy_size = 4 + get_element_count(); /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* store our element count */ oswp4(buf, get_element_count()); buf += 4; /* copy our data in chunks */ for (idx = 1, rem = get_element_count() ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit copying to the remaining size */ chunk = avail; if (chunk > rem) chunk = rem; /* store this chunk */ memcpy(buf, p, chunk); /* skip this chunk */ buf += chunk; idx += chunk; rem -= chunk; } /* return the size */ return copy_size; } /* ------------------------------------------------------------------------ */ /* * CharacterSet Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen) { ulong copy_size; /* we need two bytes for the name length count plus the name's bytes */ copy_size = 2 + get_ext_ptr()->charset_name_len; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* store the length of the name, and the name itself */ oswp2(buf, get_ext_ptr()->charset_name_len); memcpy(buf + 2, get_ext_ptr()->charset_name, get_ext_ptr()->charset_name_len); /* return the size */ return copy_size; } /* ------------------------------------------------------------------------ */ /* * File Metaclass implementation - image rebuilding */ /* * Build an image file record */ ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen) { ulong copy_size; /* * we need the character set object ID, the mode byte, the access * byte, and the uint32 flags */ copy_size = VMB_OBJECT_ID + 1 + 1 + 4; /* make sure we have room for our data */ if (copy_size > buflen) return copy_size; /* store the character set object ID */ vmb_put_objid(buf, get_ext()->charset); buf += VMB_OBJECT_ID; /* store the mode and access values */ *buf++ = get_ext()->mode; *buf++ = get_ext()->access; /* store the flags */ oswp4(buf, get_ext()->flags); /* return the size */ return copy_size; } /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile intrinsic object */ ulong CVmObjTemporaryFile::rebuild_image(VMG_ char *buf, ulong buflen) { /* we don't store any extra data */ return 0; } /* ------------------------------------------------------------------------ */ /* * CVmObjFileName intrinsic object */ ulong CVmObjFileName::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my extension */ vm_filnam_ext *ext = get_ext(); /* * if we're going to store the filename, we'll store it in universal * notation; do the conversion first so we can figure the length */ char *uni = 0; ulong needlen; size_t unilen = 0; err_try { if (ext->sfid == 0) { uni = to_universal(vmg0_); unilen = strlen(uni); } /* * figure the required size: special file ID, plus the universal * name with length prefix if the sfid is zero */ needlen = 4 + (ext->sfid != 0 ? 0 : VMB_LEN + unilen); /* if we have room, store the data */ if (buflen >= needlen) { /* write the special file ID first */ oswp4s(buf, ext->sfid); buf += 4; /* if it's not a special file, store the universal name */ if (ext->sfid == 0) { vmb_put_len(buf, unilen); memcpy(buf + VMB_LEN, uni, unilen); } } } err_finally { lib_free_str(uni); } err_end; /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * Pattern metaclass */ /* * build an image file record for the object */ ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen) { size_t need_size; /* we need a DATAHOLDER to store the original pattern string reference */ need_size = VMB_DATAHOLDER; /* if we need more space, just return our size requirements */ if (need_size > buflen) return need_size; /* write our value */ vmb_put_dh(buf, get_orig_str()); /* return our size */ return need_size; } /* * Convert to constant data. */ void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper, vm_obj_id_t /*self*/) { /* check our original source string value */ convert_val_to_const_data(vmg_ mapper, &get_ext()->str); } /* ------------------------------------------------------------------------ */ /* * StringComparator intrinsic class */ /* * build the image data */ ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen) { /* set up a stream writer on the buffer, and write it out */ CVmMemoryStream str(buf, buflen); return write_to_stream(vmg_ &str, &buflen); } /* ------------------------------------------------------------------------ */ /* * DynamicFunc intrinsic class */ /* * build the image data */ ulong CVmDynamicFunc::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my extension */ vm_dynfunc_ext *ext = get_ext(); /* figure the required size */ ulong needlen = 2 + 2 + VMB_DATAHOLDER + VMB_DATAHOLDER + (ext->obj_ref_cnt * 2) + ext->bytecode_len; /* make sure there's room */ if (needlen > buflen) return needlen; /* save the sizes: bytecode length, object reference count */ oswp2(buf, ext->bytecode_len); oswp2(buf, ext->obj_ref_cnt); buf += 4; /* save the method context object */ vmb_put_dh(buf, &ext->method_ctx); buf += VMB_DATAHOLDER; /* save the source object value */ vmb_put_dh(buf, &ext->src); buf += VMB_DATAHOLDER; /* save the byte code */ memcpy(buf, ext->get_bytecode_ptr(), ext->bytecode_len); buf += ext->bytecode_len; /* save the object reference list */ for (int i = 0 ; i < ext->obj_ref_cnt ; ++i, buf += 2) oswp2(buf, ext->obj_refs[i]); /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * Stack Frame Descriptor intrinsic object */ ulong CVmObjFrameDesc::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my extension */ vm_framedesc_ext *ext = get_ext(); /* figure the required size */ ulong needlen = VMB_OBJECT_ID + 2 + 2; /* make sure there's room */ if (needlen > buflen) return needlen; /* save the underlying StackFrameRef reference */ vmb_put_objid(buf, ext->fref); buf += VMB_OBJECT_ID; /* save the frame index and return address */ oswp2(buf, ext->frame_idx); oswp2(buf + 2, ext->ret_ofs); buf += 4; /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * Stack Frame Reference intrinsic object */ ulong CVmObjFrameRef::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my extension */ vm_frameref_ext *ext = get_ext(); /* figure the required size */ ulong needlen = 2 + 2 + VMB_DATAHOLDER + VMB_DATAHOLDER + VMB_OBJECT_ID + VMB_OBJECT_ID + VMB_PROP_ID + (ext->nlocals + ext->nparams)*VMB_DATAHOLDER; /* make sure there's room */ if (needlen > buflen) return needlen; /* if our frame is still active, build the local snapshot */ if (ext->fp != 0) make_snapshot(vmg0_); /* save the variable counts */ oswp2(buf, ext->nlocals); oswp2(buf+2, ext->nparams); buf += 4; /* write the entry pointer */ vmb_put_dh(buf, &ext->entry); buf += VMB_DATAHOLDER; /* write the method context variables */ vmb_put_dh(buf, &ext->self); buf += VMB_DATAHOLDER; vmb_put_objid(buf, ext->defobj); buf += VMB_OBJECT_ID; vmb_put_objid(buf, ext->targobj); buf += VMB_OBJECT_ID; vmb_put_propid(buf, ext->targprop); buf += VMB_PROP_ID; /* write the variable snapshot values */ int i; const vm_val_t *v; for (i = ext->nlocals + ext->nparams, v = ext->vars ; i > 0 ; --i, ++v) { vmb_put_dh(buf, v); buf += VMB_DATAHOLDER; } /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * CVmObjDate intrinsic object */ ulong CVmObjDate::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my extension */ vm_date_ext *ext = get_ext(); /* figure the required size */ ulong needlen = 8; /* make sure there's room */ if (needlen > buflen) return needlen; /* write our data */ oswp4s(buf, ext->dayno); oswp4(buf+4, ext->daytime); /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone intrinsic object */ ulong CVmObjTimeZone::rebuild_image(VMG_ char *buf, ulong buflen) { /* get my timezone object and query its default settings */ CVmTimeZone *tz = get_ext()->tz; vmtzquery q; tz->query(&q); /* get the zone name */ size_t namelen; const char *name = tz->get_name(namelen); /* if this is the special local zone object, use ":local" instead */ if (G_tzcache->is_local_zone(tz)) name = ":local", namelen = 6; /* * figure the required size - int32 gmt offset, int32 dst offset, * abbreviation (with one-byte prefix), name (with one-byte prefix) */ size_t abbrlen = strlen(q.abbr); ulong needlen = 4 + 4 + 1 + namelen + 1 + abbrlen; /* make sure there's room */ if (needlen > buflen) return needlen; /* copy the data */ oswp4s(buf, q.gmtofs); oswp4s(buf+4, q.save); char *p = write_str_byte_prefix(buf+8, name, namelen); write_str_byte_prefix(p, q.abbr, abbrlen); /* return the length */ return needlen; } #ifdef TADSNET /* ------------------------------------------------------------------------ */ /* * HTTP Server intrinsic object */ ulong CVmObjHTTPServer::rebuild_image(VMG_ char *buf, ulong buflen) { /* we don't store anything in the image file */ ulong needlen = 0; /* make sure there's room */ if (needlen > buflen) return needlen; /* return the length */ return needlen; } /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest intrinsic object */ ulong CVmObjHTTPRequest::rebuild_image(VMG_ char *buf, ulong buflen) { /* we don't store anything in the image file */ ulong needlen = 0; /* make sure there's room */ if (needlen > buflen) return needlen; /* return the length */ return needlen; } #endif /* TADSNET */ frobtads-1.2.3/tads3/vmtmpfil.h0000644000175000001440000001773311472224627015553 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtmpfil.h - CVmObjTemporaryFile object Function The TemporaryFile intrinsic class describes a temp file on disk. This isn't an open instance of a temp file; rather, it basically represents a filename in the local file system. The reason to make this an object is that it provides a natural way of identifying and freeing temp files. The File class can validate that the program is supplying a valid temporary file generated by the system, because only the system can create a TemporaryFile object. This prevents a malicious program from trying to circumvent the file safety levels by faking a temp file name; it's essentially impossible to invent a fake name since the name is encapsulated in the system object. The system can tell when it's safe to delete the underlying file system object through the magic of garbage collection: when the TemporaryFile object is collected, the program is definitely done with it, since the program doesn't have any references to it. Notes Modified 11/20/10 MJRoberts - Creation */ #ifndef VMTMPFIL_H #define VMTMPFIL_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Image file data block: * * This object doesn't store anything in an image file. A temporary file * object is completely transient and doesn't have anything to store. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_tmpfil_ext { /* allocate the structure */ static vm_tmpfil_ext *alloc_ext( VMG_ class CVmObjTemporaryFile *self, const char *filename); /* * The filename, as a null-terminated string. We overallocate the * structure with enough space. If this is set to an empty string, it * means that the temporary file is no longer valid. This happens if * the object is loaded from an image file or explicitly released via * the deleteFile() method. */ char filename[1]; }; /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile intrinsic class definition */ class CVmObjTemporaryFile: public CVmObject { friend class CVmMetaclassTemporaryFile; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjTemporaryFile object? */ static int is_tmpfil_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Get the name of the temp file (as a null-terminated string). * Returns null if the temp file is invalid. */ const char *get_fname() const { const char *f = get_ext()->filename; return f[0] != 0 ? f : 0; } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *); /* mark our undo record references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark our references */ void mark_refs(VMG_ uint) { } /* remove weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* we're immutable, so we can't have changed since loading */ int is_changed_since_load() const { return FALSE; } protected: /* get my extension data */ vm_tmpfil_ext *get_ext() const { return (vm_tmpfil_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjTemporaryFile() { ext_ = 0; } /* create with contents */ CVmObjTemporaryFile(VMG_ const char *fname); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* getFilename() method */ int getp_getFilename(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* deleteFile() method */ int getp_deleteFile(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjTemporaryFile::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjTemporaryFile metaclass registration table object */ class CVmMetaclassTemporaryFile: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "tempfile/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTemporaryFile(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTemporaryFile(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTemporaryFile::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTemporaryFile:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMTMPFIL_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjTemporaryFile) frobtads-1.2.3/tads3/rcmain.h0000644000175000001440000001116111034440154015140 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name rcmain.h - T3 resource compiler main Function Notes Modified 01/03/00 MJRoberts - Creation */ #ifndef RCMAIN_H #define RCMAIN_H #include "os.h" #include "t3std.h" /* * Resource compiler main class */ class CResCompMain { public: /* * Add the given resources to an image file. Returns zero on * success, nonzero on failure. If anything goes wrong, we'll use * the given host interface to display error messages. */ static int add_resources(const char *image_fname, const class CRcResList *reslist, class CRcHostIfc *hostifc, int create_new, os_filetype_t file_type, int link_mode); private: /* format an error message and display it via the host interface */ static void disp_error(class CRcHostIfc *hostifc, const char *msg, ...); }; /* * Resource list element */ class CRcResEntry { public: CRcResEntry(const char *fname, const char *url) { /* make a copy of the filename and URL */ fname_ = lib_copy_str(fname); url_ = lib_copy_str(url); /* not in a list yet */ next_ = 0; } ~CRcResEntry() { /* delete the filename and url strings */ lib_free_str(fname_); lib_free_str(url_); } /* get/set the next entry in the list */ CRcResEntry *get_next() const { return next_; } void set_next(CRcResEntry *nxt) { next_ = nxt; } /* get the entry's filename */ const char *get_fname() const { return fname_; } /* get the entry's URL */ const char *get_url() const { return url_; } private: /* next entry in list */ CRcResEntry *next_; /* filename */ char *fname_; /* URL as stored in resource file */ char *url_; }; /* * Resource operation modes */ enum rcmain_res_op_mode_t { /* add resources */ RCMAIN_RES_OP_MODE_ADD, /* replace resources */ RCMAIN_RES_OP_MODE_REPLACE, /* delete resources */ RCMAIN_RES_OP_MODE_DELETE }; /* * Resource list object */ class CRcResList { public: CRcResList() { /* nothing in the list yet */ head_ = tail_ = 0; count_ = 0; } ~CRcResList() { /* delete the entire list */ while (head_ != 0) { CRcResEntry *nxt; /* remember the next entry */ nxt = head_->get_next(); /* delete this entry */ delete head_; /* move on */ head_ = nxt; } } /* get the head of the list */ class CRcResEntry *get_head() const { return head_; } /* get the number of elements in the list */ unsigned int get_count() const { return count_; } /* * Add a file or directory. If 'alias' is non-null, we'll store the * alias string as-is (with no URL conversions or any other changes) * as the name of the resource. If 'alias' is null, we'll convert * the filename to a URL using the normal rules and store the URL. * * If 'fname' refers to a directory rather than a file, we'll add * all of the files within the directory. 'alias' is ignored in * this case, since we construct a URL from the directory path. If * 'recurse' is true, we'll also add the contents of any * subdirectories found within the given directory; otherwise, we'll * only add the files contained directly within the given directory. */ void add_file(const char *fname, const char *alias, int recurse); private: /* add an item to the list */ void add_element(class CRcResEntry *entry) { /* add it to the end of my list */ if (tail_ == 0) head_ = entry; else tail_->set_next(entry); tail_ = entry; /* count the new element */ ++count_; } /* head of my list */ class CRcResEntry *head_; /* tail of my list */ class CRcResEntry *tail_; /* number of elements in the list */ unsigned int count_; }; /* * Resource compiler host interface. This provides access to * application operations during resource compilation. */ class CRcHostIfc { public: /* display an error message */ virtual void display_error(const char *msg) = 0; /* display a status message */ virtual void display_status(const char *msg) = 0; }; #endif /* RCMAIN_H */ frobtads-1.2.3/tads3/vmvec.h0000644000175000001440000005250211773412332015022 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmvec.h - T3 VM Vector class Function A Vector is a subclass of Collection. A Vector is a List whose elements can be changed in-place, and whose size can change. Notes Modified 05/14/00 MJRoberts - Creation */ #ifndef VMVEC_H #define VMVEC_H #include #include "vmtype.h" #include "vmobj.h" #include "vmcoll.h" #include "vmglob.h" #include "vmstack.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Vectors are very similar to lists, but a vector's contents can be * modified during execution. Because vectors are dynamic, a vector's * image file data are copied into the heap. * * A vector's image file data structure looks like this: * * UINT2 elements_allocated *. UINT2 number_of_elements_used *. DATAHOLDER element[1] *. DATAHOLDER element[2] *. etc, up to number_of_elements_used (NOT the number allocated) * * The object extension for a vector is almost identical, but adds a prefix * giving the "allocated" size of the vector, and a suffix with a pointer * to our original image data (if applicable) plus one bit per element * after the end of the element[] list for undo bits: * * UINT2 allocated_elements *. UINT2 number_of_elements *. DATAHOLDER element[1] *. DATAHOLDER element[2] *. etc *. undo_bits * * The allocated_elements counter gives the total number of elements * allocated in the vector; this can be greater than the number of elements * actually in use, in which case we have free space that we can use to add * elements without reallocating the vector's memory. * * The undo_bits are packed 8 bits per byte. Note that the undo_bits * follow all allocated slots, so these do not change location when we * change the number of elements actually in use. */ /* ------------------------------------------------------------------------ */ /* * Vector metaclass */ class CVmObjVector: public CVmObjCollection { friend class CVmMetaclassVector; friend class CVmQSortVector; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* create with a given number of elements */ static vm_obj_id_t create(VMG_ int in_root_set, size_t element_count); /* create with initial contents */ static vm_obj_id_t create_fill(VMG_ int in_root_set, size_t element_count, ...); /* * determine if an object is a vector - it is if the object's * virtual metaclass registration index matches the class's static * index */ static int is_vector_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* vectors are list-like for read access */ int is_listlike(VMG_ vm_obj_id_t) { return TRUE; } int ll_length(VMG_ vm_obj_id_t) { return get_element_count(); } /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* call a static property */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* cast to string */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* join the vector into a string, using commas as separators */ join(vmg_ new_str, self, ",", 1); /* return the new string */ return new_str->get_as_string(vmg0_); } virtual const char *explicit_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str, int radix, int flags) const; /* add a value to the vector, yielding a new vector */ int add_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* subtract a value from the vector, yielding a new vector */ int sub_val(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *val); /* undo operations */ void notify_new_savept(); void apply_undo(VMG_ struct CVmUndoRecord *rec); void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* we keep only strong references */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* * remove weak references - we keep only normal (strong) references, * so this routine doesn't need to do anything */ void remove_stale_weak_refs(VMG0_) { } /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from the image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* index the vector */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set an indexed element of the vector */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* * Check a value for equality. We will match any list or vector with * the same number of elements and the same value for each element. */ int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const; /* calculate a hash value for the vector */ uint calc_hash(VMG_ vm_obj_id_t self, int depth) const; /* * assume that we've been changed since loading, if we came from the * image file */ int is_changed_since_load() const { return TRUE; } /* * get the allocated number of elements of the vector - this will be * greater than or equal to get_element_count(), and reflects the * actual allocated size */ size_t get_allocated_count() const { return vmb_get_len(get_vector_ext_ptr()); } /* get the number of elements in the vector */ size_t get_element_count() const { return vmb_get_len(get_vector_ext_ptr() + 2); } /* get an element without any range checking; 0-based index */ void get_element(size_t idx, vm_val_t *val) const { /* get the data from the data holder in our extension */ vmb_get_dh(get_element_ptr(idx), val); } /* append an element, with no undo */ void append_element(VMG_ vm_obj_id_t self, const vm_val_t *val); /* set an element; 0-based index; no bounds checking or undo */ void set_element(size_t idx, const vm_val_t *val) { /* set the element's data holder from the value */ vmb_put_dh(get_element_ptr(idx), val); } /* set an element, recording undo for the change; 0-based index */ void set_element_undo(VMG_ vm_obj_id_t self, size_t idx, const vm_val_t *val); /* * join the list into a string - this is the C++ interface to the * self.join() method */ void join(VMG_ vm_val_t *retval, vm_obj_id_t self, const char *sep, size_t sep_len) const; protected: /* load image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* for construction, remove all redundant elements */ void cons_uniquify(VMG0_); /* uniquify the value for an appendUnique operation */ void uniquify_for_append(VMG_ vm_obj_id_t self); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a live iterator */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a copy of the object */ vm_obj_id_t create_copy(VMG_ const vm_val_t *self_val); /* create with no initial contents */ CVmObjVector() { ext_ = 0; } /* create with a given number of elements */ CVmObjVector(VMG_ size_t element_count); /* get a pointer to my vector data */ const char *get_vector_ext_ptr() const { return ext_; } /* * get my extension data pointer for construction purposes - this is a * writable pointer, so that a caller can fill our data buffer during * construction */ char *cons_get_vector_ext_ptr() const { return ext_; } /* * Calculate the amount of space we need to store a list of a given * length. We require two bytes for the length prefix, plus the space * for each element, plus the space for the undo bits (one bit per * element, but we pack eight bits per byte). */ static size_t calc_alloc(size_t elecnt) { return (calc_alloc_prefix_and_ele(elecnt) + ((elecnt + 7) & ~7)); } /* * calculate the allocation amount, including only the count prefix * and element data */ static size_t calc_alloc_prefix_and_ele(size_t elecnt) { return (2*VMB_LEN + calc_alloc_ele(elecnt)); } /* calculate the allocation amount, including only the element storage */ static size_t calc_alloc_ele(size_t elecnt) { return elecnt * VMB_DATAHOLDER; } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* allocate space for the vector, given the number of elements */ void alloc_vector(VMG_ size_t element_count); /* expand to accommodate the given number of new elements */ void expand_by(VMG_ vm_obj_id_t self, size_t added_elements); /* get the allocation count increment */ size_t get_alloc_count_increment() const { /* * we might want to make this parameterizable; for now use a * fixed increment size */ return 16; } /* push an element onto the stack */ void push_element(VMG_ size_t idx) const { /* push a stack element */ vm_val_t *p = G_stk->push(); /* * get the data from the data holder in our extension directly into * our new stack element */ vmb_get_dh(get_element_ptr(idx), p); } /* set the allocated size */ void set_allocated_count(size_t cnt); /* given an index, get a pointer to the element's data in the list */ char *get_element_ptr(size_t idx) const { /* * figure out where this element's data holder is by skipping the * count prefix, then skipping past preceding data holders */ return cons_get_vector_ext_ptr() + 2*VMB_LEN + (idx * VMB_DATAHOLDER); } /* get a constant pointer to an element */ static const char *get_element_ptr_const(const char *ext, size_t idx) { return ext + 2*VMB_LEN + (idx * VMB_DATAHOLDER); } /* increment an element pointer */ static void inc_element_ptr(char **ptr) { *ptr += VMB_DATAHOLDER; } /* get a given undo bit */ int get_undo_bit(size_t idx) const { char *p; int bitnum; /* get the byte containing the bit for this index */ p = get_undo_ptr() + (idx >> 3); /* get the bit number within the byte */ bitnum = (idx & 7); /* get this bit */ return ((*p & (1 << bitnum)) != 0); } /* set a given undo bit */ void set_undo_bit(size_t idx, int val) { char *p; int bitnum; /* get the byte containing the bit for this index */ p = get_undo_ptr() + (idx >> 3); /* get the bit number within the byte */ bitnum = (idx & 7); /* set or clear the bit */ if (val) *p |= (1 << bitnum); else *p &= ~(1 << bitnum); } /* clear all undo bits */ void clear_undo_bits() { memset(get_undo_ptr(), 0, ((get_allocated_count() + 7) & ~7)); } /* get a pointer to the first byte of the undo bits */ char *get_undo_ptr() const { /* the undo bits follow the last element data */ return (cons_get_vector_ext_ptr() + 2*VMB_LEN + get_allocated_count()*VMB_DATAHOLDER); } /* set the number of elements in the vector; no undo or size checking */ void set_element_count(size_t cnt) { vmb_put_len(cons_get_vector_ext_ptr() + 2, cnt); } /* set the element count, saving undo for the change */ void set_element_count_undo(VMG_ vm_obj_id_t self, size_t new_element_count); /* insert elements into the vector, keeping undo */ void insert_elements_undo(VMG_ vm_obj_id_t self, int ins_idx, int add_cnt); /* delete elements from the vector, keeping undo */ void remove_elements_undo(VMG_ vm_obj_id_t self, size_t start_idx, size_t del_cnt); /* general handler for indexOf, lastIndexOf, and countOf */ int gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int forward, int count_only); /* general handler for indexWhich, lastIndexWhich, and countWhich */ int gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int forward, int count_only, vm_rcdesc *rc); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - convert to a list */ int getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - get the number of elements */ int getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - copy from another vector or list */ int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - fill with a value */ int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - select a subset */ int getp_subset(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - apply a callback to each element */ int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - find the first element satisfying a condition */ int getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback(key,val) on each element */ int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general processor for forEach/forEachAssoc */ int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int pass_key_to_cb, vm_rcdesc *rc); /* property evaluator - mapAll */ int getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - indexOf */ int getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - valWhich */ int getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - lastIndexOf */ int getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - lastIndexWhich */ int getp_last_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - valWhich */ int getp_last_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - countOf */ int getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - countWhich */ int getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - getUnique */ int getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - appendUnique */ int getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - sort */ int getp_sort(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - set the length */ int getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - insertAt */ int getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - remove a single element at a given index */ int getp_remove_element_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - removeRange */ int getp_remove_range(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - append a value */ int getp_append(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - prepend a value */ int getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - append everything from a collection */ int getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - remove any elements with a given value */ int getp_remove_element(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - join into a string */ int getp_join(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - generate */ int getp_generate(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { return static_getp_generate(vmg_ retval, argc); } /* static property evaluator - generate */ static int static_getp_generate(VMG_ vm_val_t *retval, uint *argc); /* property evaluator - splice */ int getp_splice(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the index of the minimum element */ int getp_indexOfMin(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the minimum value in the list */ int getp_minVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the index of the maximum element */ int getp_indexOfMax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get the maximum value in the list */ int getp_maxVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* general handler for the various min/max methods */ int get_minmax(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, const vm_rcdesc *rc, int sense, int want_index); /* property evaluation function table */ static int (CVmObjVector::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassVector: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "vector/030005"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjVector(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjVector(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjVector::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjVector::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; #endif /* VMVEC_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjVector) frobtads-1.2.3/tads3/charmap/0000755000175000001440000000000012145614111015131 5ustar realncusersfrobtads-1.2.3/tads3/charmap/cmaplib.t3r0000644000175000001440000051147112145504453017212 0ustar realncusersT3-image Thu May 16 18:59:22 2013MRESà’+l´ œ—ž’žÐŠŒÒžŒœ––Ñ‹œ’ Μ—ž’žÐœÎÍÊÏÑ‹œ’î 2œ—ž’žÐœÎÍÊÎÑ‹œ’ 04œ—ž’žÐœÎÍÊÍÑ‹œ’T>—œ—ž’žÐœÎÍÊÌÑ‹œ’ëLlœ—ž’žÐœÎÍÊËÑ‹œ’W[“œ—ž’žÐœÎÍÊÊÑ‹œ’êj윗ž’žÐœÎÍÊÉÑ‹œ’Öz°œ—ž’žÐœÎÍÊÈÑ‹œ’†‰ œ—ž’žÐœÎÍÊÇÑ‹œ’&˜rœ—ž’žÐœËÌÈÑ‹œ’˜§Ïœ—ž’žÐœÈÌÈÑ‹œ’g·zœ—ž’žÐœÈÈÊÑ‹œ’áÆ*œ—ž’žÐœÇÊÏÑ‹œ’ Ö–œ—ž’žÐœÇÊÍÑ‹œ’¡å✗ž’žÐœÇÊÊÑ‹œ’ƒõFœ—ž’žÐœÇÊÈÑ‹œ’ÉŽœ—ž’žÐœÇÉÏÑ‹œ’Wbœ—ž’žÐœÇÉÎÑ‹œ’¹#œ—ž’žÐœÇÉÍÑ‹œ’Ç3aœ—ž’žÐœÇÉÌÑ‹œ’(CÅœ—ž’žÐœÇÉËÑ‹œ’íSoœ—ž’žÐœÇÉÊÑ‹œ’\cМ—ž’žÐœÇÉÉÑ‹œ’,s[œ—ž’žÐœÇÉÆÑ‹œ’‡‚‘œ—ž’žÐœÇÈËÑ‹œ’“Aœ—ž’žÐ–ŒÎÑ‹œ’Y¢ðœ—ž’žÐ–ŒÎÏÑ‹œ’I²Åœ—ž’žÐ–ŒÍÑ‹œ’ œ—ž’žÐ–ŒÌÑ‹œ’Òœ—ž’žÐ–ŒËÑ‹œ’✗ž’žÐ–ŒÊÑ‹œ’6òEœ—ž’žÐ–ŒÉÑ‹œ’{†œ—ž’žÐ–ŒÈÑ‹œ’¡œ—ž’žÐ–ŒÇÑ‹œ’¢!qœ—ž’žÐ–ŒÆÑ‹œ’1œœ—ž’žÐ”–ÇÒÑ‹œ’¯@ œ—ž’žÐ’žœÑ‹œ’.Nœ—ž’žÐ’žœœšÑ‹œ’H\œ—ž’žÐ’žœœ†Ñ‹œ’Xj¦ œ—ž’žÐ’žœ˜šš”Ñ‹œ’þw_ œ—ž’žÐ’žœ–œš“ž‘›Ñ‹œ’]…ƒ œ—ž’žÐ’žœ‹ŠÑ‹œ’  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~çx?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~¡!¢c£L¤CUR¥Y¦|§S:¨"©(c)ª-a«<<¬~®(R)¯-°o±+-²^2³^3´'µu¶P:·.¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe - -- ` ' , " " " +! ++" *& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å".#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]h¤CUR§S:©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFòû  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¤¤¦¦§§¨¨©©««¬¬­­®®°°±±´´µµ¶¶··¸¸»»ÁÁÂÂÄÄÇÇÉÉËËÍÍÎÎÓÓÔÔÖÖ××ÚÚÜÜÝÝßßááââääççééëëííîîóóôôöö÷÷úúüüýýÃã¥¹Ææ È èÏïÐðÊêÌì9Å:å=¼>¾A£B³CÑDñGÒHòPÕQõTÀUàXØYøZŒ[œ^ª_º`ŠašbÞcþdenÙoùpÛqûyzŸ{¯|¿}Ž~žÇ¡Ø¢Ùÿ۲ݽ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™ë]?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢c£L¤¤¥Y¦¦§§¨¨©©ª-a««¬¬­­®®¯-°°±±²^2³^3´´µµ¶¶··¸¸¹^1º-o»»¼ 1/4½ 1/2¾ 3/4¿?ÀAÁÁÂÂÃAÄÄÅAAÆAEÇÇÈEÉÉÊEËËÌIÍÍÎÎÏIÐÐÑNÒOÓÓÔÔÕOÖÖ×רOÙUÚÚÛUÜÜÝÝÞTHßßàaááââãaääåaaæaeççèeééêeëëìiííîîïiðdhñnòoóóôôõoöö÷÷øoùuúúûuüüýýþthÿyÃã¥¹Ææ È èÏïÐðÊêÌì9Å:å=¼>¾A£B³CÑDñGÒHòPÕQõROESoeTÀUàXØYøZŒ[œ^ª_º`ŠašbÞcþdenÙoùpÛqûxYyzŸ{¯|¿}Ž~ž’fÆ^ǡآÙÿÛ²Ü~ݽ‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEŽZIJKLMNOP R!S"T#U$F%KH&TS'È(Š)Š*"+Y,'-E.YU/Â0a1b2v3g4d5e6ž7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGèHšIšJ"KyL'MeNyuOâQë – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]OÖª-a²^2³^3¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÞTHåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi%KH&TS.YUEkhFtsNyu3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFÿ  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¤¤¦¦§§©©««¬¬­­®®°°±±µµ¶¶··»»¨€ª½²¯£ Š Œ Ž ¡ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ Ð!Ñ"Ò#Ó$Ô%Õ&Ö'×(Ø)Ù*Ú+Û,Ü-Ý.Þ/ß0à1á2â3ã4ä5å6æ7ç8è9é:ê;ë<ì=í>î?ï@ðAñBòCóDôEõFöG÷HøIùJúKûLüMýNþOÿQ¸RSƒTºU¾V³W¿X¼YšZœ[ž\^¢_Ÿ¥‘´ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ ˆ!¹"!™“?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢c£L¤¤¥Y¦¦§§¨"©©ª-a««¬¬­­®®¯-°°±±²^2³^3´'µµ¶¶··¸,¹^1º-o»»¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpi¨€ª½²¯£ Š Œ Ž ¡ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ Ð!Ñ"Ò#Ó$Ô%Õ&Ö'×(Ø)Ù*Ú+Û,Ü-Ý.Þ/ß0à1á2â3ã4ä5å6æ7ç8è9é:ê;ë<ì=í>î?ï@ðAñBòCóDôEõFöG÷HøIùJúKûLüMýNþOÿQ¸RSƒTºU¾V³W¿X¼YšZœ[ž\^¢_Ÿ¥‘´ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ ˆ!Im!¹!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]KΪ-a²^2³^3¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFòû  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââããääååææççèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿRŒSœ`ŠašxŸ}Ž~ž’ƒÆˆÜ˜ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™ëF?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââããääååææççèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿAaAaCc C cDdÐdEeEe9L:l=L>lALBlCNDnGNHnPOQoRŒSœTRUrXRYrZS[s^S_s`ŠašbTctdTetnUoupUquxŸyZzz{Z|z}Ž~ž’ƒÆˆÇ^Ø~Ù.Û,ܘÝ"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEŽZIJKLMNOP R!S"T#U$F%KH&TS'CH(Š)Š*"+Y,'-È.Û/Â0a1b2v3g4d5e6ž7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHšIšJ"KyL'MèNûOâQë – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]>®—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi%KH&TS'CHEkhFtsGch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFÂï  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ££¤¤¥¥¦¦§§¨¨©©««¬¬­­®®°°±±²²³³µµ¶¶··»»½½’ƒ„´…¡†¢ˆ¸‰¹ŠºŒ¼Ž¾¿À‘Á’“ÔĕŖƗǘșɚʛ˜ÌÍžΟÏ СÑ£Ó¤Ô¥Õ¦Ö§רة٪ګ۬ܭݮޯ߰à±á²â³ã´äµå¶æ·ç¸è¹éºê»ë¼ì½í¾î¿ïÀðÁñÂòÃóÄôÅõÆöÇ÷ÈøÉùÊúËûÌüÍýÎþ – — ¯ ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™y?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢c££¤¤¥¥¦¦§§¨¨©©ª-a««¬¬­­®®¯-°°±±²²³³´'µµ¶¶··¸,¹^1º-o»»¼ 1/4½½¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ƒÆ^Ç^Ø~Ù.Û,Ü~Ý"„´…¡†¢ˆ¸‰¹ŠºŒ¼Ž¾¿À‘Á’“ÔĕŖƗǘșɚʛ˜ÌÍžΟÏ СÑ£Ó¤Ô¥Õ¦Ö§רة٪ګ۬ܭݮޯ߰à±á²â³ã´äµå¶æ·ç¸è¹éºê»ë¼ì½í¾î¿ïÀðÁñÂòÃóÄôÅõÆöÇ÷ÈøÉùÊúËûÌüÍýÎþÑèÒõÖðEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe – — ¯ ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"å "!å "-)"Ð"Ó"-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]F»ª-a¹^1º-o¼ 1/4¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoeZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-)"SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFêù  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜßßààááââããääååææççèèééêêëëììííîîïïññòòóóôôõõöö÷÷øøùùúúûûüüÿÿÐð0Ý1ýRŒSœ^Þ_þ`ŠašxŸ’ƒÆˆÜ˜ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™ïT?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐDHÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝYÞTHßßààááââããääååææççèèééêêëëììííîîïïðdhññòòóóôôõõöö÷÷øøùùúúûûüüýyþthÿÿAaAaCc C cDdDdEeEeÐð0Ý1ý9L:l=L>lALBlCNDnGNHnPOQoRŒSœTRUrXRYrZS[s^Þ_þ`ŠašbTctdTetnUoupUquxŸyZzz{Z|z}Z~z’ƒÆˆÇ^Ø~Ù.Û,ܘÝ"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(Š)Š*"+Y,'-È.Û/Â0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHšIšJ"KyL'MèNûOâQë – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]DºÐDHÞTHðdhþth—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH6zhEkhFtsGch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFªé  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¥¥¦¦§§¨¨©©««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹»»¼¼½½¾¾¿¿×ª÷º’ƒÆˆÜ˜°À±Á²³ôĵŶÆ·ǸȹÉ»˼̽;οÏÀÐÁÑÂÒÃÓÐàÑáÒâÓãÔäÕåÖæ×çØèÙéÚêÛëÜìÝíÞîßïàðáñâòãóäôåõæöç÷èøéùêúðÔñÕòÖó×ôØ ý þ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›ª ¤¬ €"!™!Ï?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤CUR¥¥¦¦§§¨¨©©ª-a««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹º-o»»¼¼½½¾¾¿¿ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖOתØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷ºøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ƒÆˆÇ^Ø~Ù.Û,ܘÝ"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe°À±Á²³ôĵŶÆ·ǸȹÉ»˼̽;οÏÀÐÁÑÂÒÃÓÐàÑáÒâÓãÔäÕåÖæ×çØèÙéÚêÛëÜìÝíÞîßïàðáñâòãóäôåõæöç÷èøéùêúðÔñÕòÖó×ôØ ý þ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /ª ¤¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Vã¤CURª-aº-oÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¢¢££¤¤¥¥¦¦§§¨¨©©««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹»»¼¼½½¾¾××ààââççèèééêêëëîîïïôô÷÷ùùûûüüRŒSœ’ƒÆˆ ¡º¿!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7Ø8Ù9Ú:Û@ÜAÝBÞCßDáEãFäGåHæIìJíKðLñMòNóOõPöQøRúyŠ~†ˆ‘š˜Ž©˜¯ºŸ¾ªÁÀÒÿ ž ý þ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™+Ý?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢¢££¤¤¥¥¦¦§§¨¨©©ª-a««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹º-o»»¼¼½½¾¾¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×רOÙUÚUÛUÜUÝYÞTHßssààáaââãaäaåaaæaeççèèééêêëëìiíiîîïïðdhñnòoóoôôõoöo÷÷øoùùúuûûüüýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoRŒSœTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ƒÆˆÇ^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MèNûOâQë ¡º¿!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7Ø8Ù9Ú:Û@ÜAÝBÞCßDáEãFäGåHæIìJíKðLñMòNóOõPöQøRúyŠ~†ˆ‘š˜Ž©˜¯ºŸ¾ªÁÀÒÿ  ž ý þ – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Qت-aº-oÅAAÆAEÐDHÞTHßssåaaæaeðdhþth—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFÖô  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¢¢££¤¤¦¦§§¨©©««¬¬­­®®¯°°±±²²³³´´µµ¶¶··¸¹¹»»¼¼½½¾¾ÄÄÅÅÆ¯ÉÉÓÓÕÕÖÖ×ר¨ÜÜßßääååæ¿ééóóõõöö÷÷ø¸üüÂâÀàÃã È èÇçËëÆæ"Ì#ì*Î+î.Á/á6Í7í;Ï<ïAÙBùCÑDñEÒFòLÔMôVªWºZÚ[ú`ÐaðjÛkûrØsøyÊzê{Ý|ý}Þ~þÇŽÙÿÛž – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›¬ €"!™€?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢¢££¤¤¥Y¦¦§§¨©©ª-a««¬¬­­®®¯°°±±²²³³´´µµ¶¶··¸¹¹º-o»»¼¼½½¾¾¿?ÀAÁAÂAÃAÄÄÅÅÆ¯ÇCÈEÉÉÊEËEÌIÍIÎIÏIÐDHÑNÒOÓÓÔOÕÕÖÖ×ר¨ÙUÚUÛUÜÜÝYÞTHßßàaáaâaãaääååæ¿çcèeééêeëeìiíiîiïiðdhñnòoóóôoõõöö÷÷ø¸ùuúuûuüüýyþthÿyÂâAaÀàÃã È èDdDdÇçËëÆæEe"Ì#ì*Î+î.Á/á6Í7í9L:l;Ï<ï=L>lAÙBùCÑDñEÒFòGNHnLÔMôPOQoROESoeTRUrVªWºXRYrZÚ[ú^S_s`ÐaðbTctdTetjÛkûnUoupUqurØsøxYyÊzê{Ý|ý}Þ~þ’fÆ^ÇŽØ~ÙÿÛžÜ~Ý"‘A’B“G”D•E–Z—ǘTH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©Ô±a²b³g´dµe¶z·ç¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉôÑthetaÒupsiÖpiEABVGDEÞZIJKLMNOP R!S"T#U$F%KH&TS'È(Ð)Ð*"+Y,'-E.YU/YA0a1b2v3g4d5e6þ7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGèHðIðJ"KyL'MeNyuOyaQe – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "¨"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Dºª-aº-oÐDHÞTHðdhþthROESoe˜TH¦PH§CH¨PS¸thÆphÇchÈpsÑthetaÒupsiÖpi%KH&TS.YU/YAEkhFtsNyuOya3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFâ÷  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÍÍÎÎÏÏÑÑÓÓÔÔÖÖ×רØÙÙÚÚÛÛÜÜßßààááââääååææççèèééêêëëííîîïïññóóôôöö÷÷øøùùúúûûüüÿÿÃãÐðRŒSœxŸ’ƒ Õ¡õ¯ݰýƈܘÌìÞ Ò#ò – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰9 ‹: ›« þ¬ €"!™õg?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃAÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌIÍÍÎÎÏÏÐÐÑÑÒOÓÓÔÔÕOÖÖ×רØÙÙÚÚÛÛÜÜÝYÞTHßßààááââãaääååææççèèééêêëëìiííîîïïðdhññòoóóôôõoöö÷÷øøùùúúûûüüýyþthÿÿÃãAaCc C cDdÐðEeEe9L:l=L>lALBlCNDnGNHnPOQoRŒSœTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxŸyZzz{Z|z}Z~z’ƒ Õ¡õ¯ݰýƈÇ^Ø~Ù.Û,ܘÝ"ÌìÞ Ò#ò‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-È.Û/Â0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MèNûOâQë – — ‘ ’ ‚ “ ” „ †! ‡" •& …0 ‰2 '3 ''9 ‹: ›> -D /« þ¬ €!Im!p!Re"!™5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]GÄÞTHðdhþth—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œ¥ª¦«®¬ª°ø±ñ²ýµæ·úº§»¯¼¬½«¿¨ÄŽÅÆ’Ç€Éѥ֙ܚßáà…á âƒä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ñ¤ò•ó¢ô“ö”÷öù—ú£û–üÿ˜’Ÿ“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆí ü§ ž"ù"û"ì)"ïH"÷a"ðd"óe"ò#© #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ ¼?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œ¤CUR¥¦|§S:¨"©(c)ª¦«®¬ª®(R)¯-°ø±ñ²ý³^3´'µæ¶P:·ú¸,¹^1º§»¯¼¬½«¾ 3/4¿¨ÀAÁAÂAÃAÄŽÅÆ’Ç€ÈEÉÊEËEÌIÍIÎIÏIÐDHÑ¥ÒOÓOÔOÕOÖ™×xØOÙUÚUÛUÜšÝYÞTHßáà…á âƒãaä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ðdhñ¤ò•ó¢ô“õoö”÷öøoù—ú£û–üýyþthÿ˜AaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖãEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü§ ž!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ#© #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]HĤCUR§S:©(c)®(R)³^3¶P:¹^1¾ 3/4ÐDHÞTHðdhþthROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ°ø±ñ²ý·ú÷ö†êˆë‰ìŠíŒîŽïð‘€’“‚”ƒ•„–…—†˜‡™ˆš‰›Šœ‹ŒžŸŽ ¡£‘¤’¥“¦”§•¨–©—ªô«õ¬á­â®ã¯å±˜²™³š´›µœ¶·ž¸Ÿ¹ º¡»¢¼£½¤¾¥¿¦À§Á¨ÂªÃ©Ä«Å¬Æ­Ç®È¯ÉàÊäËèÌæÍçÎé ü"ù"ûH"÷d"óe"ò%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ.Ü?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢c£L¤CUR¥Y¦|§S:¨"©(c)ª-a«<<¬~®(R)¯-°ø±ñ²ý³^3´'µ£¶P:·ú¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷öøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"†êˆë‰ìŠíŒîŽïð‘€’“‚”ƒ•„–…—†˜‡™ˆš‰›Šœ‹ŒžŸŽ ¡£‘¤’¥“¦”§•¨–©—ªô«õ¬á­â®ã¯å±˜²™³š´›µœ¶·ž¸Ÿ¹ º¡»¢¼£½¤¾¥¿¦À§Á¨ÂªÃ©Ä«Å¬Æ­Ç®È¯ÉàÊäËèÌæÍçÎéÑŸÒ¬Ö§EABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"œ "!œ "-)""‘"-"*"ù"û"þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"÷`"Øa"==d"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]MͤCUR§S:©(c)ª-a«<<®(R)³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoeZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-)"oo "-V+"INT 4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¢–£œ¤Ÿ¦§§õ©¨«®¬ª­ð®©°ø±ñ²ý³üµæ¶ô·ú¹û»¯¼¬½«¾óÄŽÅÆ’ÉÓàÕå֙מØÜšßáä„冿‘é‚ó¢õäö”÷öø›ü ƒµÐ€‡ ¶ Ñ퉸ӷÒ"•#…*¡+Œ.½/Ô6è7é;ê<ëA­BˆCãDçEîFìLâM“VŠW‹Z—[˜`¾aÕjÇk×rÆsÖyz¥{£|¤}Ï~Ø ï ò ¦ ÷"ù%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ$Ã?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢–£œ¤Ÿ¥Y¦§§õ¨"©¨ª-a«®¬ª­ð®©¯-°ø±ñ²ý³ü´'µæ¶ô·ú¸,¹ûº-o»¯¼¬½«¾ó¿?ÀAÁAÂAÃAÄŽÅÆ’ÇCÈEÉÊEËEÌIÍIÎIÏIÐDHÑNÒOÓàÔOÕå֙מØÙUÚUÛUÜšÝYÞTHßáàaáaâaãaä„冿‘çcèeé‚êeëeìiíiîiïiðdhñnòoó¢ôoõäö”÷öø›ùuúuûuüýyþthÿy ƒAaµÐ€‡ ¶ ÑDdDd퉸ӷÒEe"•#…*¡+Œ.½/Ô6è7é9L:l;ê<ë=L>lA­BˆCãDçEîFìGNHnLâM“POQoROESoeTRUrVŠW‹XRYrZ—[˜^S_s`¾aÕbTctdTetjÇk×nUoupUqurÆsÖxYyz¥{£|¤}Ï~Ø’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—í˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©â±a²b³g´dµe¶z·‰¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉ“ÑthetaÒupsiÖpiEABVGDEÏZIJKLMNOP R!S"T#U$F%KH&TS'¶(¾)¾*"+Y,'-E.YU/YA0a1b2v3g4d5e6Ø7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGÑHÕIÕJ"KyL'MeNyuOyaQe - -- ` ï , ò ¦ ÷ Å! Î" ù& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE ""Nabla"(- "!(- "-)"PROD "SUM "-"*"ù"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Gª-aº-oÐDHÞTHðdhþthROESoe˜TH¦PH§CH¨PS¸thÆphÇchÈpsÑthetaÒupsiÖpi%KH&TS.YU/YAEkhFtsNyuOya --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢½£œ¤Ï¥¾¦Ý§õ¨ù©¸ª¦«®¬ª­ð®©¯î°ø±ñ²ý³ü´ïµæ¶ô·ú¸÷¹ûº§»¯¼¬½«¾ó¿¨À·ÁµÂ¶ÃÇÄŽÅÆ’Ç€ÈÔÉÊÒËÓÌÞÍÖÎ×ÏØÐÑÑ¥ÒãÓàÔâÕå֙מØÙëÚéÛêÜšÝíÞèßáà…á âƒãÆä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ðÐñ¤ò•ó¢ô“õäö”÷öø›ù—ú£û–üýìþçÿ˜1Õ’Ÿ ò%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þ “?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢½£œ¤Ï¥¾¦ݧõ¨ù©¸ª¦«®¬ª­ð®©¯î°ø±ñ²ý³ü´ïµæ¶ô·ú¸÷¹ûº§»¯¼¬½«¾ó¿¨À·ÁµÂ¶ÃÇÄŽÅÆ’Ç€ÈÔÉÊÒËÓÌÞÍÖÎ×ÏØÐÑÑ¥ÒãÓàÔâÕå֙מØÙëÚéÛêÜšÝíÞèßáà…á âƒãÆä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ðÐñ¤ò•ó¢ô“õäö”÷öø›ù—ú£û–üýìþçÿ˜AaAaCc C cDdÑdEeEe1Õ9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiÓABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-Ô.ê/¶0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - -- ò ` ' , " " " Å! Î" ú& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE ""Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]IÊROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¤Ï§õ¨ù«®¬ª­ð°ø´ï¸÷»¯ÁµÂ¶ÄŽÇ€ÉËÓÍÖÎ×ÓàÔâ֙מÚéÜšÝíßáá âƒä„ç‡é‚ë‰í¡îŒó¢ô“ö”÷öú£üýìÆÇ¤¥† ¬ ŸÒÔÑШ©·Ø9‘:’=•>–ABˆCãDäGÕHåPŠQ‹TèUêXüYýZ—[˜^¸_­`æaçbÝcîd›eœnÞo…pëqûyz«{½|¾}¦~§ÇóØôÙúÛòÝñ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þ ?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢c£L¤Ï¥Y¦|§õ¨ù©(c)ª-a«®¬ª­ð®(R)¯-°ø±+-²^2³^3´ïµu¶P:·.¸÷¹^1º-o»¯¼ 1/4½ 1/2¾ 3/4¿?ÀAÁµÂ¶ÃAÄŽÅAAÆAEÇ€ÈEÉÊEËÓÌIÍÖÎ×ÏIÐÑÑNÒOÓàÔâÕO֙מØOÙUÚéÛUÜšÝíÞTHßáàaá âƒãaä„åaaæaeç‡èeé‚êeë‰ìií¡îŒïiðdhñnòoó¢ô“õoö”÷öøoùuú£ûuüýìþthÿyÆÇ¤¥† ¬ ŸÒÔÑШ©·Ø9‘:’=•>–ABˆCãDäGÕHåPŠQ‹ROESoeTèUêXüYýZ—[˜^¸_­`æaçbÝcîd›eœnÞo…pëqûxYyz«{½|¾}¦~§’fÆ^ÇóØôÙúÛòÜ~Ýñ‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiÓABVGDE¦ZIJKLMNOP R!S"T#U$F%KH&TS'¬(æ)æ*"+Y,'-E.YU/¶0a1b2v3g4d5e6§7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGŸHçIçJ"KyL'MeNyuOƒQ‰ - -- ` ' , " " " Å! Î" *& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ".#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Vè©(c)ª-a®(R)±+-²^2³^3¶P:¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÞTHåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi%KH&TS.YUEkhFtsNyu --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¤Ï§ý«®­ð»¯…ƒ‡‰‹ ‘ “ • —™›¡£ì­§©êô¸¾ÇÑÓÕ×Ý â!ä"æ#è$«%¶&¥'ü(ö)ú*Ÿ+ò,î-ø./à0 1¢2ë3¬4¦5¨6é7ó8·9½:Æ;Ð<Ò=Ô>Ö?Ø@áAãBåCçDªEµF¤GûHõIùJžKñLíM÷NœOÞQ„R€S‚T†UˆVŠWŒXŽYZ’[”\–^˜_š!ï%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þ"Ò?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢c£L¤Ï¥Y¦|§ý¨"©(c)ª-a«®¬~­ð®(R)¯-°o±+-²^2³^3´'µu¶P:·.¸,¹^1º-o»¯¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpi…ƒ‡‰‹ ‘ “ • —™›¡£ì­§©êô¸¾ÇÑÓÕ×Ý â!ä"æ#è$«%¶&¥'ü(ö)ú*Ÿ+ò,î-ø./à0 1¢2ë3¬4¦5¨6é7ó8·9½:Æ;Ð<Ò=Ô>Ö?Ø@áAãBåCçDªEµF¤GûHõIùJžKñLíM÷NœOÞQ„R€S‚T†UˆVŠWŒXŽYZ’[”\–^˜_š - -- ` ' , " " " Å! Î" *& ...0 %%2 '3 ''9 <: >> -D /!Im!ï!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ".#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Rà©(c)ª-a®(R)±+-²^2³^3¶P:¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFúý  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢½£œ¤Ï¥¾¦Ý§õ¨ù©¸ªÑ«®¬ª­ð®©¯î°ø±ñ²ý³ü´ïµæ¶ô·ú¸÷¹ûºÐ»¯¼¬½«¾ó¿¨À·ÁµÂ¶ÃÇÄŽÅÆ’Ç€ÈÔÉÊÒËÓÌÞÍÖÎ×ÏØÑ¥ÒãÓàÔâÕåÖ™×èØÙëÚéÛêÜšßáà…á âƒãÆä„冿‘ç‡èŠé‚êˆë‰ììí¡îŒï‹ñ¤ò•ó¢ô“õäö”÷öø›ù—ú£û–üÿí¦§0˜1^ž_Ÿ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þ ›?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢½£œ¤Ï¥¾¦ݧõ¨ù©¸ªÑ«®¬ª­ð®©¯î°ø±ñ²ý³ü´ïµæ¶ô·ú¸÷¹ûºл¯¼¬½«¾ó¿¨À·ÁµÂ¶ÃÇÄŽÅÆ’Ç€ÈÔÉÊÒËÓÌÞÍÖÎ×ÏØÐDHÑ¥ÒãÓàÔâÕåÖ™×èØÙëÚéÛêÜšÝYÞTHßáà…á âƒãÆä„冿‘ç‡èŠé‚êˆë‰ììí¡îŒï‹ðdhñ¤ò•ó¢ô“õäö”÷öø›ù—ú£û–üýyþthÿíAaAaCc C cDdDdEeEe¦§0˜19L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^ž_Ÿ`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiÓABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-Ô.ê/¶0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - -- ` ' , " " " Å! Î" ú& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE ""Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]MÒÐDHÞTHðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œª¦«®¬ª°ø±ñ²ýµæ·úº§»¯¼¬½«¿¨À‘Á†ÂÎǀȒÉʉ̘͋ѥҩӟԌՙÙÚ–Üšßáà…á âƒã„ç‡èŠé‚êˆìí¡ñ¤ò•ó¢ô“õ”÷öù—ú£ü“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆí ü§ ž"ù"û"ì)"ïH"÷a"ðd"óe"ò #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ¾?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œ¤CUR¥Y¦|§S:¨"©(c)ª¦«®¬ª®(R)¯-°ø±ñ²ý³^3´'µæ¶P:·ú¸,¹^1º§»¯¼¬½«¾ 3/4¿¨À‘Á†ÂÃŽÄAÅAAÆAEǀȒÉʉËE̘͋ÎIÏIÐDHѥҩӟԌՙÖO×xØOÙÚ–ÛUÜšÝYÞTHßáà…á âƒã„äaåaaæaeç‡èŠé‚êˆëeìí¡îiïiðdhñ¤ò•ó¢ô“õ”öo÷öøoù—ú£ûuüýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖãEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-’.YU/0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠNyuOƒQe - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü§ ž!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]L̤CUR§S:©(c)®(R)³^3¶P:¹^1¾ 3/4ÅAAÆAEÐDHÞTHåaaæaeðdhþthROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH.YU6zhEkhFtsGchHshIshchNyu --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­£œ«®¬ª°ø±ñ²ýµæ·ú»¯¼¬½«¿¨Á¤ÄŽÅÆ’Ç€ÉͥЋӦ֙ØÚ§ÜšÝ—Þßáà…á âƒä„冿‘ç‡èŠé‚êˆë‰í¡ðŒó¢ô“ö”÷öø›ú£û–üý˜þ•’Ÿ“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆí ü§ ž"ù"û"ì)"ïH"÷a"ðd"óe"ò#© #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ º?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢c£œ¤CUR¥Y¦|§S:¨"©(c)ª-a«®¬ª®(R)¯-°ø±ñ²ý³^3´'µæ¶P:·ú¸,¹^1º-o»¯¼¬½«¾ 3/4¿¨ÀAÁ¤ÂAÃAÄŽÅÆ’Ç€ÈEÉÊEËEÌIÍ¥ÎIÏIЋÑNÒOÓ¦ÔOÕOÖ™×xØÙUÚ§ÛUܚݗÞßáà…á âƒãaä„冿‘ç‡èŠé‚êˆë‰ìií¡îiïiðŒñnòoó¢ô“õoö”÷öø›ùuú£û–üý˜þ•ÿyAaAaCc C cDd‹dEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖãEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü§ ž!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE ""Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ#© #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]FÀ¤CUR§S:©(c)ª-a®(R)³^3¶P:¹^1º-o¾ 3/4ROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œ¥ª¦«®¬ª°ø±ñ²ýµæ·úº§»¯¼¬½«¿¨Ñ¥ßáá í¡ñ¤ó¢÷öú£’Ÿ“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆíЀÑ҂ӃԄՅֆׇ؈ىڊۋ܌ÝÞŽßàá‘â’ã“ä”啿–ç—è˜é™êš ü§ ž"ù"û"ì)"ïH"÷a"ðd"óe"ò#© #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ;ø?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢›£œ¤CUR¥¦|§S:¨"©(c)ª¦«®¬ª®(R)¯-°ø±ñ²ý³^3´'µæ¶P:·ú¸,¹^1º§»¯¼¬½«¾ 3/4¿¨ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑ¥ÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßáàaá âaãaäaåaaæaeçcèeéeêeëeìií¡îiïiðdhñ¤òoó¢ôoõoöo÷öøoùuú£ûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖãEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQeЀÑ҂ӃԄՅֆׇ؈ىڊۋ܌ÝÞŽßàá‘â’ã“ä”啿–ç—è˜é™êš - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü§ ž!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ#© #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]NФCUR§S:©(c)®(R)³^3¶P:¹^1¾ 3/4ÅAAÆAEÐDHÞTHåaaæaeðdhþthROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¢›£œ¤˜¦ §¨¤«®¬ª¯§°ø±ñ²ý³¦´¡µæ¶†·ú¸¥»¯¼¬½«¾­ÀŽÂ„ǀȑÉʒ˔ΨϕԙÙ۞ܚßáà…âƒç‡èŠé‚êˆë‰îŒï‹ó¢ô“÷öù—ú£û–ü’Ÿ“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆí  ü"ù"û"ì)"ïH"÷a"ðd"óe"ò#© #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ ¸?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢›£œ¤˜¥Y¦ §¨¤©(c)ª-a«®¬ª®(R)¯§°ø±ñ²ý³¦´¡µæ¶†·ú¸¥¹^1º-o»¯¼¬½«¾­¿?ÀŽÁA„ÃAÄAÅAAÆAEǀȑÉÊ’Ë”ÌIÍIΨϕÐDHÑNÒOÓOÔ™ÕOÖO×xØOÙÚU۞ܚÝYÞTHßáà…áaâƒãaäaåaaæaeç‡èŠé‚êˆë‰ìiíiîŒï‹ðdhñnòoó¢ô“õoöo÷öøoù—ú£û–üýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖã”ABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-‘.ž/„0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - --  ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ#© #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]G¿©(c)ª-a®(R)¹^1º-oÅAAÆAEÐDHÞTHåaaæaeðdhþthROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFîú  !!""##$$&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¢À££¤¤¦Û«—¬Ü­¡°€±“·»˜¼•½”×Þ÷ݲÆ’ ¬»¿@àQñ`°a±b²c³d´eµf¶g·h¸i¹j%"‚"ƒ"‘H"–%…%† %%Œ%Ž%%Š$%ˆ,%‰4%‹<%‡’%„ %þ}þð€þÁþ‚þ¢ƒþÄþ¥…þÄ‹þÆþÇŽþ¨þ©‘þÈ“þÉ•þª—þÊ™þ«›þËþ­ŸþÌ¡þ®£þÍ¥þ¯§þΩþÏ«þЭþѯþÒ±þ¼³þÓµþ½·þÔ¹þ¾»þÕ½þë¿þÖÁþ×ÅþØÉþßÊþÅËþÙÌþìÍþîÎþíÏþÚÐþ÷ÑþºÓþáÕþø×þâÙþüÛþãÝþûßþäáþïãþååþòçþæéþóëþçìþôíþèïþéðþõñþýòþöóþêõþùöþú÷þ™øþšûþüþžO3?        !!""##$$&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢À££¤¤¥Y¦Û§S:¨"©(c)ª-a«—¬Ü­¡®(R)¯-°€±“²^2³^3´'µu¶P:·¸,¹^1º-o»˜¼•½”¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×ÞØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷ÝøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆ’ÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe ¬»¿@àQñ`°a±b²c³d´eµf¶g·h¸i¹j% - -- ` ' , " " " ‡! ‡" ‚& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"‚"ƒ"þ"‘ "-V'"&("|)"**"++"INT 4".:<"~E"~=H"–`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"‹Å"#þ #þ #þ #þ)#þ*#þ%…%† %%Œ%Ž%%Š$%ˆ,%‰4%‹<%‡’%„ %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]}þð€þÁþ‚þ¢ƒþÄþ¥…þÄ‹þÆþÇŽþ¨þ©‘þÈ“þÉ•þª—þÊ™þ«›þËþ­ŸþÌ¡þ®£þÍ¥þ¯§þΩþÏ«þЭþѯþÒ±þ¼³þÓµþ½·þÔ¹þ¾»þÕ½þë¿þÖÁþ×ÅþØÉþßÊþÅËþÙÌþìÍþîÎþíÏþÚÐþ÷ÑþºÓþáÕþø×þâÙþüÛþãÝþûßþäáþïãþååþòçþæéþóëþçìþôíþèïþéðþõñþýòþöóþêõþùöþú÷þ™øþšûþüþž\ñ§S:©(c)ª-a®(R)²^2³^3¶P:¹^1º-o¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --& ...0 jj3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "-V+"INT 4".:E"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­£œ¤¯ª¦«®¬ª°ø±ñ²ýµæ·úº§¼¬½«¿¨ÄŽÅÆ’Ç€ÉÑ¥Ö™ØÜšßáà…á âƒä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ñ¤ò•ó¢ô“ö”÷öø›ù—ú£û–üÿ˜’Ÿ“â˜é£ä¦è©ê±à´ëµîÀãÃåÄçÆí ü§ ž"ù"û"ì)"ïH"÷a"ðd"óe"ò#© #ô!#õ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ »?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡­¢c£œ¤¯¥Y¦|§S:¨"©(c)ª¦«®¬ª®(R)¯-°ø±ñ²ý³^3´'µæ¶P:·ú¸,¹^1º§»>>¼¬½«¾ 3/4¿¨ÀAÁAÂAÃAÄŽÅÆ’Ç€ÈEÉÊEËEÌIÍIÎIÏIÐDHÑ¥ÒOÓOÔOÕOÖ™×xØÙUÚUÛUÜšÝYÞTHßáà…á âƒãaä„冿‘ç‡èŠé‚êˆë‰ìí¡îŒï‹ðdhñ¤ò•ó¢ô“õoö”÷öø›ù—ú£û–üýyþthÿ˜AaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ŸÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“â”D•E–Z—EH˜é™IšK›LœMNžXŸO P¡R£ä¤T¥U¦è§CH¨PS©ê±à²b³g´ëµî¶z·eh¸th¹iºk»l¼m½n¾x¿oÀãÁrÂsÃåÄçÅuÆíÇchÈpsÉohÑthetaÒupsiÖãEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MŠN–OƒQ‰ - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D / ü§ ž!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE ""Nabla"î "!î "-)"Ò"ä"-"*"ù"û"þ"ì "-V'"&("|)"ï*"++"INT 4".:<"~E"~=H"÷`"Øa"ðd"óe"ò‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ#© #ô!#õ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]HçS:©(c)®(R)³^3¶P:¹^1»>>¾ 3/4ÐDHÞTHðdhþthROESoe—EH§CH¨PS·eh¸thÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshch --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-) "-V+"INT 4".:E"~=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¤ý°ø·úðòôö€‚ƒ„…†‡ˆ‰Š‹ŒŽ !‘"’#“$”%•&–'—(˜)™*š+›,œ-.ž/Ÿ0 1¡2¢3£4¤5¥6¦7§8¨9©:ª;«<¬=­>®?¯@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïQñTóWõ^÷!ü"ù"û%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þ"Ì?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢c£L¤ý¥Y¦|§S:¨"©(c)ª-a«<<¬~®(R)¯-°ø±+-²^2³^3´'µu¶P:·ú¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiðòôö€‚ƒ„…†‡ˆ‰Š‹ŒŽ !‘"’#“$”%•&–'—(˜)™*š+›,œ-.ž/Ÿ0 1¡2¢3£4¤5¥6¦7§8¨9©:ª;«<¬=­>®?¯@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïQñTóWõ^÷ - -- ` ' , " " " Å! Ø" ù& ...0 %%2 '3 ''9 <: >> -D /!Im!ü!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"Ò"SUM "-"*"ù"û"þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"Øa"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ú#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºR%ÕS%ÖT%ÉU%¸V%·W%»X%ÔY%ÓZ%È[%¾\%½]%¼^%Æ_%Ç`%Ìa%µb%¶c%¹d%Ñe%Òf%Ëg%Ïh%Ði%Êj%Øk%×l%΀%ß„%܈%ÛŒ%Ý%Þ‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]RÚ§S:©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"SUM "oo "-V+"INT 4".:E"~=H"~=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFâ÷  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ£œ¦Š§õ¨ù©—«®¬‰­ð°ø±ñ²™³š·ˆ»¯½«„ï…÷††ˆ‰ŠŒ’Ž•˜¡‘¤’¥“¦”§•¨–©—ª˜¬™­šµ›¶œ·¸ž½Ÿ¾ Æ¡Ç£ϤÐ¥ѦÒ§Ó¨Ô©Õª‘«–¬›­®ž¯Ÿ°ü±Ö²׳Ø´ݵÞ¶à·á¸â¹ãºä»å¼æ½ç¾è¿éÀêÁëÂíÃìÄîÅòÆóÇôÈöÉúÊ Ëû̢ͣÎý Ž ‹ Œ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þº?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ ÿ¡!¢c£œ¤CUR¥Y¦Чõ¨ù©—ª-a«®¬‰­ð®(R)¯-°ø±ñ²™³š´'µæ¶P:·ˆ¸,¹^1º-o»¯¼ 1/4½«¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"„ï…÷††ˆ‰ŠŒ’Ž•˜¡‘¤’¥“¦”§•¨–©—ª˜¬™­šµ›¶œ·¸ž½Ÿ¾ Æ¡Ç£ϤÐ¥ѦÒ§Ó¨Ô©Õª‘«–¬›­®ž¯Ÿ°ü±Ö²׳Ø´ݵÞ¶à·á¸â¹ãºä»å¼æ½ç¾è¿éÀêÁëÂíÃìÄîÅòÆóÇôÈöÉúÊ Ëû̢ͣÎýÑâÒòÖêEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe - Ž Ž ‹ Œ , " " " Å! Î" ˆ& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"Þ "!Þ "-)"Æ"Ï"-"*"SQRT "þ"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"ÁÅ"ˆ#þ #þ #þ #þ)#þ*#þ%Ä%³ %Ú%¿%À%Ù%Ã$%´,%Â4%Á<%ÅP%ÍQ%ºT%ÉW%»Z%È]%¼`%Ìc%¹f%Ëi%Êl%΀%ß„%܈%Û‘%°’%±“%² %þÊ%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]KɤCURª-a®(R)¶P:¹^1º-o¼ 1/4¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoeZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-)"SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFŠá  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡¢£¤¥¦§¨ © ª « ¬ ­®¯°±²³´µ¶·¸¹º»¼½¾¿ À!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú?ß@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïPðQñRòSóTôUõVöW÷XøYùZú[û – — ‘ ’ “ ”" •& …¬ €A)?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~  ¡!¢c£L¤CUR¥Y¦|§S:¨"©(c)ª-a«<<¬~®(R)¯-°o±+-²^2³^3´'µu¶P:·•¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe¡¢£¤¥¦§¨ © ª « ¬ ­®¯°±²³´µ¶·¸¹º»¼½¾¿ À!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú?ß@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïPðQñRòSóTôUõVöW÷XøYùZú[û – — ‘ ’ , “ ” “ +! ++" •& …0 %%2 '3 ''9 <: >> -D /¬ €!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"•#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]f ¤CUR§S:©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya! ++0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââããääååææççèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ ˜?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááââããääååææççèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿAaAaCc C cDdÐdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-È.Û/Â0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MèNûOâQë - -- ` ' , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]KÏROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  §§­­°°··ÁÁÂÂÃÃÄÄÅÅÆÆÉÉËËÍÍÎÎÏÏÐÐÓÓÔÔÕÕÖÖØØÚÚÛÛÜÜÝÝÞÞßßááââããääååææééëëííîîïïððóóôôõõööøøúúûûüüýýþþÀࡱ È è©¹¢²ÌìÊê"£#³(¥)µ*¤+´.Ç/ç6¦7¶8ÿ;¨<¸EÑFñJ¯K¿LÒMò`ªaºf«g»h×i÷j®k¾rÙsù}¬~¼ ½,ä?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c£L¤CUR¥Y¦|§§¨"©(c)ª-a«<<¬~­­®(R)¯-°°±+-²^2³^3´'µu¶P:··¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁÁÂÂÃÃÄÄÅÅÆÆÇCÈEÉÉÊEËËÌIÍÍÎÎÏÏÐÐÑNÒOÓÓÔÔÕÕÖÖ×xØØÙUÚÚÛÛÜÜÝÝÞÞßßàaááââããääååææçcèeééêeëëìiííîîïïððñnòoóóôôõõöö÷:øøùuúúûûüüýýþþÿyÀàAa¡±Cc È èDd©¹¢²ÌìÊêEe"£#³(¥)µ*¤+´.Ç/ç6¦7¶8ÿ9L:l;¨<¸=L>lALBlCNDnEÑFñGNHnJ¯K¿LÒMòPOQoROESoeTRUrXRYrZS[s^S_s`ªaºbTctdTetf«g»h×i÷j®k¾nUoupUqurÙsùxYyZzz{Z|z}¬~¼’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—¢˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©Ò±a²b³g´dµe¶z·²¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉòÑthetaÒupsiÖpiËABVGDE¬ZIJKLMNOP R!S"T#U$F%KH&TS'È(ª)ª*"+Y,'-E.Û/Â0a1b2v3g4d5e6¼7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGèHºIºJ"KyL'MeNûOâQë - -- ½ ` ' , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]NÚ¤CUR©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ROESoe˜TH¦PH§CH¨PS¸thÆphÇchÈpsÑthetaÒupsiÖpi%KH&TSEkhFts --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¤¤§§¨¨­­°°´´¸¸ÁÁÂÂÄÄÇÇÉÉËËÍÍÎÎÓÓÔÔÖÖ××ÚÚÜÜÝÝßßááââääççééëëííîîóóôôöö÷÷úúüüýýÃã¡±Ææ È èÏïÐðÊêÌì9Å:å=¥>µA£B³CÑDñGÒHòPÕQõTÀUàXØYøZ¦[¶^ª_º`©a¹bÞcþd«e»nÙoùpÛqûy¬z¼{¯|¿}®~¾Ç·Ø¢Ùÿ۲ݽ «?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c£L¤¤¥Y¦|§§¨¨©(c)ª-a«<<¬~­­®(R)¯-°°±+-²^2³^3´´µu¶P:·.¸¸¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁÁÂÂÃAÄÄÅAAÆAEÇÇÈEÉÉÊEËËÌIÍÍÎÎÏIÐÐÑNÒOÓÓÔÔÕOÖÖ×רOÙUÚÚÛUÜÜÝÝÞTHßßàaááââãaääåaaæaeççèeééêeëëìiííîîïiðdhñnòoóóôôõoöö÷÷øoùuúúûuüüýýþthÿyÃã¡±Ææ È èÏïÐðÊêÌì9Å:å=¥>µA£B³CÑDñGÒHòPÕQõROESoeTÀUàXØYøZ¦[¶^ª_º`©a¹bÞcþd«e»nÙoùpÛqûxYy¬z¼{¯|¿}®~¾’fÆ^Ç·Ø¢ÙÿÛ²Ü~ݽ‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDE®ZIJKLMNOP R!S"T#U$F%KH&TS'È(©)©*"+Y,'-E.YU/Â0a1b2v3g4d5e6¾7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGèH¹I¹J"KyL'MeNyuOâQë - -- ` ' , " " " +! ++" *& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å".#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Zñ©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÞTHåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi%KH&TS.YUEkhFtsNyu --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFêù  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ££¤¤§§¨¨­­°°²²³³´´µµ··¸¸½½ÀÀÁÁÂÂÄÄÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÑÑÒÒÓÓÔÔÖÖ××ÙÙÚÚÛÛÜÜßßààááââääççèèééêêëëììííîîïïññòòóóôôöö÷÷ùùúúûûüüÆ æ Š娸«» Õ!õ$¦%¶&¡'±0©1¹4¬5¼\Þ]þ^ª_ºlÝmý{¯|¿Ø¢Ùÿ ×?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c££¤¤¥Y¦|§§¨¨©(c)ª-a«<<¬~­­®(R)¯-°°±+-²²³³´´µµ¶P:··¸¸¹^1º-o»>>¼ 1/4½½¾ 3/4¿?ÀÀÁÁÂÂÃAÄÄÅAAÆAEÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐDHÑÑÒÒÓÓÔÔÕOÖÖ×רOÙÙÚÚÛÛÜÜÝYÞTHßßààááââãaääåaaæaeççèèééêêëëììííîîïïðdhññòòóóôôõoöö÷÷øoùùúúûûüüýyþthÿyAaAaCcÆ æ Å å C cDdDdEeEeØø«» Õ!õ$¦%¶&¡'±0©1¹4¬5¼9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s\Þ]þ^ª_º`SasbTctdTetlÝmýnUoupUquxYyZzz{¯|¿}Z~z’fÆ^Ç^Ø¢ÙÿÛ,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)Þ*"+Y,'-È.Û/Â0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIþJ"KyL'MèNûOâQë - -- ` ' , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]\ó©(c)ª-a«<<®(R)±+-¶P:¹^1º-o»>>¼ 1/4¾ 3/4ÅAAÆAEÐDHÞTHåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH6zhEkhFtsGchHsh --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¤¤§§¨¨­­¯¯°°´´¸¸ÁÁÂÂÃÃÄÄÅÅÆÆÉÉËËÍÍÎÎÔÔÕÕÖÖ×רØÚÚÛÛÜÜßßááââããääååææééëëííîîôôõõöö÷÷øøúúûûüüÀࡱ È èÐðªºÌìÊê"«#»(¥)µ*Ï+ï.Ç/ç6Ó7ó8¢;¦<¶EÑFñJ½K¿LÒMòV£W³`©a¹f¬g¼hÝiýjÞkþrÙsù}®~¾Ç·ÙÿÛ²-ç?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c£L¤¤¥Y¦|§§¨¨©(c)ª-a«<<¬~­­®(R)¯¯°°±+-²^2³^3´´µu¶P:·.¸¸¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁÁÂÂÃÃÄÄÅÅÆÆÇCÈEÉÉÊEËËÌIÍÍÎÎÏIÐÐÑNÒOÓOÔÔÕÕÖÖ×רØÙUÚÚÛÛÜÜÝYÞTHßßàaááââããääååææçcèeééêeëëìiííîîïiðdhñnòoóoôôõõöö÷÷øøùuúúûûüüýyþthÿyÀàAa¡±Cc È èDdÐðªºÌìÊêEe"«#»(¥)µ*Ï+ï.Ç/ç6Ó7ó8¢9L:l;¦<¶=L>lALBlCNDnEÑFñGNHnJ½K¿LÒMòPOQoROESoeTRUrV£W³XRYrZS[s^S_s`©a¹bTctdTetf¬g¼hÝiýjÞkþnUoupUqurÙsùxYyZzz{Z|z}®~¾’fÆ^Ç·Ø~ÙÿÛ²Ü~Ý"‘A’B“G”D•E–Z—ª˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©Ò±a²b³g´dµe¶z·º¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉòÑthetaÒupsiÖpiËABVGDE®ZIJKLMNOP R!S"T#U$F%KH&TS'È(©)©*"+Y,'-E.Û/Â0a1b2v3g4d5e6¾7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGèH¹I¹J"KyL'MeNûOâQë - -- ` ' , " " " +! ++" *& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å".#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]PÝ©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÞTHðdhþthROESoe˜TH¦PH§CH¨PS¸thÆphÇchÈpsÑthetaÒupsiÖpi%KH&TSEkhFts --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  §ý­­¡¢£¤¥¦§¨ © ª « ¬®¯°±²³´µ¶·¸¹º»¼½¾¿ À!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú;Û<Ü=Ý>Þ?ß@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïQñRòSóTôUõVöW÷XøYùZú[û\ü^þ_ÿ!ð%ß?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c£L¤CUR¥Y¦|§ý¨"©(c)ª-a«<<¬~­­®(R)¯-°o±+-²^2³^3´'µu¶P:·.¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpi¡¢£¤¥¦§¨ © ª « ¬®¯°±²³´µ¶·¸¹º»¼½¾¿ À!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú;Û<Ü=Ý>Þ?ß@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïQñRòSóTôUõVöW÷XøYùZú[û\ü^þ_ÿ - -- ` ' , " " " +! ++" *& ...0 %%2 '3 ''9 <: >> -D /!Im!ð!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å".#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Wì¤CUR©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFRÓ  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¤¤­­ ¬»¿!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïPðQñRò:?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c£L¤¤¥Y¦|§S:¨"©(c)ª-a«<<¬~­­®(R)¯-°o±+-²^2³^3´'µu¶P:·.¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe ¬»¿!Á"Â#Ã$Ä%Å&Æ'Ç(È)É*Ê+Ë,Ì-Í.Î/Ï0Ð1Ñ2Ò3Ó4Ô5Õ6Ö7×8Ø9Ù:Ú@àAáBâCãDäEåFæGçHèIéJêKëLìMíNîOïPðQñRò - -- ` ' , " " " +! ++" *& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å".#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]g§S:©(c)ª-a«<<®(R)±+-²^2³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFîú  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ££¦¦§§¨¨©©««¬¬­­°°±±²²³³··»»½½„´…µ†¶ˆ¸‰¹ŠºŒ¼Ž¾¿À‘Á’“ÔĕŖƗǘșɚʛ˜ÌÍžΟÏ СÑ£Ó¤Ô¥Õ¦Ö§רة٪ګ۬ܭݮޯ߰à±á²â³ã´äµå¶æ·ç¸è¹éºê»ë¼ì½í¾î¿ïÀðÁñÂòÃóÄôÅõÆöÇ÷ÈøÉùÊúËûÌüÍýÎþ ¯ ¡ ¢!Ã?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢c££¤CUR¥Y¦¦§§¨¨©©ª-a««¬¬­­®(R)¯-°°±±²²³³´'µì¶P:··¸,¹^1º-o»»¼ 1/4½½¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷:øoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"„´…µ†¶ˆ¸‰¹ŠºŒ¼Ž¾¿À‘Á’“ÔĕŖƗǘșɚʛ˜ÌÍžΟÏ СÑ£Ó¤Ô¥Õ¦Ö§רة٪ګ۬ܭݮޯ߰à±á²â³ã´äµå¶æ·ç¸è¹éºê»ë¼ì½í¾î¿ïÀðÁñÂòÃóÄôÅõÆöÇ÷ÈøÉùÊúËûÌüÍýÎþÑèÒõÖðEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQe - ¯ ¯ ¡ ¢ , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"å "!å "-)"Ð"Ó"-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]MΤCURª-a®(R)¶P:¹^1º-o¼ 1/4¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoeZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-)"SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFvÜ  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¢¢££¤¤¥¥¦¦§§¨¨©©««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹»»¼¼½½¾¾×ª÷ºÐàÑáÒâÓãÔäÕåÖæ×çØèÙéÚêÛëÜìÝíÞîßïàðáñâòãóäôåõæöç÷èøéùêú ý þ ß(ã?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡!¢¢££¤¤¥¥¦¦§§¨¨©©ª-a««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹º-o»»¼¼½½¾¾¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖOתØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷ºøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MeNyuOyaQeÐàÑáÒâÓãÔäÕåÖæ×çØèÙéÚêÛëÜìÝíÞîßïàðáñâòãóäôåõæöç÷èøéùêú ý þ - -- ß ` ' , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]Zíª-aº-oÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshchNyuOya --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜßßààááââããääååææççèèééêêëëììííîîïïññòòóóôôõõöö÷÷øøùùúúûûüüÿÿÐð0Ý1ý^Þ_þ¤?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~€€‚‚ƒƒ„„……††‡‡ˆˆ‰‰ŠŠ‹‹ŒŒŽŽ‘‘’’““””••––——˜˜™™šš››œœžžŸŸ  ¡¡¢¢££¤¤¥¥¦¦§§¨¨©©ªª««¬¬­­®®¯¯°°±±²²³³´´µµ¶¶··¸¸¹¹ºº»»¼¼½½¾¾¿¿ÀÀÁÁÂÂÃÃÄÄÅÅÆÆÇÇÈÈÉÉÊÊËËÌÌÍÍÎÎÏÏÐDHÑÑÒÒÓÓÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝYÞTHßßààááââããääååææççèèééêêëëììííîîïïðdhññòòóóôôõõöö÷÷øøùùúúûûüüýyþthÿÿAaAaCc C cDdDdEeEeÐð0Ý1ý9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^Þ_þ`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiËABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-È.Û/Â0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MèNûOâQë - -- ` ' , " " " +! ++" ·& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "Ø"Nabla"(- "!(- "-)"PROD "SUM "-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"!=a"==d"<=e">=‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"·#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]O×ÐDHÞTHðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch --! ++& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "SQRT "oo "-V+"INT 4".:E"~=H"~=`"!=a"==d"<=e">=„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ š©¿°œ²·ž÷Ÿ³áâ÷çäåöúéêëìíîïð ò!ó"ô#õ$æ%è&ã'þ(û)ý*ÿ+ù,ø-ü.à/ñ0Á1Â2×3Ç4Ä5Å6Ö7Ú8É9Ê:Ë;Ì<Í=Î>Ï?Ð@ÒAÓBÔCÕDÆEÈFÃGÞHÛIÝJßKÙLØMÜNÀOÑQ£"•"–H"—d"˜e"™ #“!#›%€% %‚%ƒ%„%…%†$%‡,%ˆ4%‰<%ŠP% Q%¡R%¢S%¤T%¥U%¦V%§W%¨X%©Y%ªZ%«[%¬\%­]%®^%¯_%°`%±a%²b%´c%µd%¶e%·f%¸g%¹h%ºi%»j%¼k%½l%¾€%‹„%Œˆ%Œ%Ž%‘%’%‘“%’ %”¾?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ š¡!¢c£L¤CUR¥Y¦|§S:¨"©¿ª-a«<<¬~®(R)¯-°œ±+-²³^3´'µu¶P:·ž¸,¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷ŸøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpi³áâ÷çäåöúéêëìíîïð ò!ó"ô#õ$æ%è&ã'þ(û)ý*ÿ+ù,ø-ü.à/ñ0Á1Â2×3Ç4Ä5Å6Ö7Ú8É9Ê:Ë;Ì<Í=Î>Ï?Ð@ÒAÓBÔCÕDÆEÈFÃGÞHÛIÝJßKÙLØMÜNÀOÑQ£ - -- ` ' , " " " Š! ¼" •& ...0 %%2 '3 ''9 <: >> -D /!Im!p!Re"!(TM)5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"(- "!(- "-)"·"SUM "-"*"•"–"”"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"—`"¼a"==d"˜e"™‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"‰Å"ž#” #” #” #” #“!#›)#”*#”%€% %‚%ƒ%„%…%†$%‡,%ˆ4%‰<%ŠP% Q%¡R%¢S%¤T%¥U%¦V%§W%¨X%©Y%ªZ%«[%¬\%­]%®^%¯_%°`%±a%²b%´c%µd%¶e%·f%¸g%¹h%ºi%»j%¼k%½l%¾€%‹„%Œˆ%Œ%Ž%‘%’%‘“%’ %”Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]NÒ¤CUR§S:ª-a«<<®(R)±+-³^3¶P:¹^1º-o»>>¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi --& ...0 %%3 ''!Im!Re"!(TM)5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"SUM "oo "-V+"INT 4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¥´§¤¨¬©©ª»«Ç¬Â®¨¯ø°¡±±´«µµ¶¦·á¸üº¼»È¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÑ„ÒñÓîÔïÕÍօدÙôÚòÛó܆ߧàˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸÿØ1õRÎSÏxÙ’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý©½À¹ Ð Ñ Ô Õ â Ò Ó ã  ! à" ¥& É0 ä9 Ü: ÝD Ú¬ Û"!ª"¶"Æ"¸"·"Ã"°+"ºH"Å`"­d"²e"³Ê%×ÿøðûÞûßÏ?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¤CUR¥´¦|§¤¨¬©©ª»«Ǭ®¨¯ø°¡±±²^2³^3´«µµ¶¦·á¸ü¹^1º¼»ȼ 1/4½ 1/2¾ 3/4¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÐDHÑ„ÒñÓîÔïÕÍÖ…×xدÙôÚòÛó܆ÝYÞTHß§àˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ðdhñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸýyþthÿØAaAaCc C cDdDdEeEe1õ9L:l=L>lALBlCNDnGNHnPOQoRÎSÏTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxÙyZzz{Z|z}Z~z’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©½±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀ¹ÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖ¹èABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-é.ó/å0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MNžO‰Q‘ Ð Ñ Ô Õ â Ò Ó ã  ! à" ¥& É0 ä2 '3 ''9 Ü: Ý> -D Ú¬ Û!Im!p!Re"!ª5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "¶"TE "¯"Æ"Nabla"(- "!(- "-)"¸"·"-"*"Ã"?"° "-V'"&("|)"**"++"º4".:<"~E"~=H"Å`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"á#? #? #? #?)#?*#?Ê%×`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]ÿøðûÞûßC¹¤CUR²^2³^3¹^1¼ 1/4½ 1/2¾ 3/4ÐDHÞTHðdhþth—EH˜TH¦PH§CH¨PS·eh¸thÆphÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-) "-V4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê££§¤¨¬©©«Ç¬Â®¨°¡¶¦»ÈÁçĀɃÍêÓîÔïÕÍÖ…ÚòÜ†Ýøß§á‡äŠéŽí’ó—ô™õ›öš÷ÖúœüŸýù‚„ˆŒ ‰ ‹‘“”•–˜¢«ž"þ#®*±+´.¯/°6µ7ú9½:¾;¹<º=»>¼AüB¸CÁDÄE¿FÀGÅHËLÏMØPÌQÎTÙUÚVßWàXÛYÞZå[æ`áaädèeéjíkðnñoópôqõrös÷yz{û|ý}ë~ìÇÿ Ð Ñ Ô Õ â Ò Ó ã  " ¥& É9 Ü: Ý"!ª"¶"Æ"·"Ã`"­d"²e"³Ê%×ãF?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡!¢c££¤CUR¥Y¦|§¤¨¬©©ª-a«Ǭ®¨¯-°¡±+-²^2³^3´'µu¶¦·¥¸,¹^1º-o»ȼ 1/4½ 1/2¾ 3/4¿?ÀAÁçÂAÃAÄ€ÅAAÆAEÇCÈEɃÊEËEÌIÍêÎIÏIÐDHÑNÒOÓîÔïÕÍÖ…×xØOÙUÚòÛUÜ†ÝøÞTHß§àaá‡âaãaäŠåaaæaeçcèeéŽêeëeìií’îiïiðdhñnòoó—ô™õ›öš÷ÖøoùuúœûuüŸýùþthÿy‚Aa„ˆŒ ‰ ‹‘“Dd”•–˜¢«ž"þ#®*±+´.¯/°6µ7ú9½:¾;¹<º=»>¼AüB¸CÁDÄE¿FÀGÅHËLÏMØPÌQÎROESoeTÙUÚVßWàXÛYÞZå[æ^S_s`áaäbTctdèeéjíkðnñoópôqõrös÷xYyz{û|ý}ë~ì’fÆ^ÇÿØ~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—”˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©ϱa²b³g´dµe¶z·•¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉØÑthetaÒupsiÖpiEABVGDEëZIJKLMNOP R!S"T#U$F%KH&TS'‰(á)á*"+Y,'-E.YU/YA0a1b2v3g4d5e6ì7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsG‹HäIäJ"KyL'MeNyuOyaQe Ð Ñ Ô Õ â Ò Ó ã  ! ++" ¥& É0 %%2 '3 ''9 Ü: Ý> -D /!Im!p!Re"!ª5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "¶"TE "0"Æ"Nabla"(- "!(- "-)"PROD "·"-"*"Ã"?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"~=`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"¥#? #? #? #?)#?*#?Ê%×`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]L̤CURª-a±+-²^2³^3¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHåaaæaeðdhþthROESoe˜TH¦PH§CH¨PS¸thÆphÇchÈpsÑthetaÒupsiÖpi%KH&TS.YU/YAEkhFtsNyuOya! ++0 %%3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "oo "-V+"INT 4".:E"~=H"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê££§¤©©«Ç¬Â®¨°¡±±µµ¶¦»È÷Ö’ÄÝ«®¸Á§º· ¼ ¾ Ë ÍØÚ€‚ƒ„…†‡ˆ‰Š‹ŒŽ !‘"’#“$”%•&–'—(˜)™*š+›,œ-.ž/Ÿ0à1á2â3ã4ä5å6æ7ç8è9é:ê;ë<ì=í>î?ï@ðAñBòCóDôEõFöG÷HøIùJúKûLüMýNþOßQÞR¬S¯T¹UÏV´W»XÀY½Z¿[Ì\Î^Ù_Û¢‘¶ Ð Ñ Ô Õ Ò Ó ×  " ¥& ɬ ÿ!Ü"!ª"Æ"Ã"°H"Å`"­d"²e"³çL?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡!¢c££¤CUR¥Y¦|§¤¨"©©ª-a«Ǭ®¨¯-°¡±±²^2³^3´'µµ¶¦·¥¸,¹^1º-o»ȼ 1/4½ 1/2¾ 3/4¿?ÀAÁAÂAÃAÄAÅAAÆAEÇCÈEÉEÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖO×xØOÙUÚUÛUÜUÝYÞTHßssàaáaâaãaäaåaaæaeçcèeéeêeëeìiíiîiïiðdhñnòoóoôoõoöo÷ÖøoùuúuûuüuýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESoeTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’ÄÆ^Ç^Ø~Ù.Û,Ü~Ý"‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©OH±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀpÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖpiÝ«®¸Á§º· ¼ ¾ Ë ÍØÚ€‚ƒ„…†‡ˆ‰Š‹ŒŽ !‘"’#“$”%•&–'—(˜)™*š+›,œ-.ž/Ÿ0à1á2â3ã4ä5å6æ7ç8è9é:ê;ë<ì=í>î?ï@ðAñBòCóDôEõFöG÷HøIùJúKûLüMýNþOßQÞR¬S¯T¹UÏV´W»XÀY½Z¿[Ì\Î^Ù_Û¢‘¶ Ð Ñ Ô Õ , Ò Ó ×  ! ++" ¥& É0 %%2 '3 ''9 <: >> -D /¬ ÿ!Im!Ü!p!Re"!ª5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Æ"Nabla"(- "!(- "-)"PROD "SUM "-"*"Ã"?"° "-V'"&("|)"**"++"INT 4".:<"~E"~=H"Å`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"¥#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]HƤCURª-a²^2³^3¹^1º-o¼ 1/4½ 1/2¾ 3/4ÅAAÆAEÐDHÞTHßssåaaæaeðdhþthROESoe—EH˜TH¦PH§CH¨PS©OH·eh¸thÆphÇchÈpsÉohÑthetaÒupsiÖpi! ++0 %%3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-)"PROD "SUM "-V+"INT 4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê£’¥´¦›§¬¨Œ©©«Ç¬Â­œ®¨°®±±²‚³„·¯¹»È½—ĀɃօ܆ߧàˆâ‰äŠçèéŽêë‘î”ï•ô™öš÷ÖùûžüŸSÏ„‹…‡†͈Ή׊،َÚßý‘°’µ“¡”¢•¶–·—¸˜£™¹šº›¤œ»Áž¥Ÿà¦¡Ä£ª¤Æ¥˦¼§̨¾©¿ª««½¬À­Û®ܯݰþ±á²â³ç´äµå¶ú·è¸õ¹éºë»ì¼í½î¾ê¿ïÀðÁòÂ÷ÃóÄôÅùÆæÇøÈãÉöÊûËüÌÞÍàÎñ Ð Ñ Ô Õ Ò Ó  " –& É0 ˜"!“H"Å`"­d"²e"³ øÿá2?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡!¢c£’¤CUR¥´¦›§¬¨Œ©©ª-a«Ǭ­œ®¨¯-°®±±²‚³„´'µí¶P:·¯¸,¹º-o»ȼ 1/4½—¾ 3/4¿?ÀAÁAÂAÃAÄ€ÅAAÆAEÇCÈEɃÊEËEÌIÍIÎIÏIÐDHÑNÒOÓOÔOÕOÖ…×xØOÙUÚUÛU܆ÝYÞTHß§àˆáaâ‰ãaäŠåaaæaeçèéŽêë‘ìiíiî”ï•ðdhñnòoóoô™õoöš÷ÖøoùúuûžüŸýyþthÿyAaAaCc C cDdDdEeEe9L:l=L>lALBlCNDnGNHnPOQoROESÏTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxYyZzz{Z|z}Z~z’fÆ^Ç^Ø~Ù.Û,Ü~Ý"„‹…‡†͈Ή׊،َÚßý‘°’µ“¡”¢•¶–·—¸˜£™¹šº›¤œ»Áž¥Ÿà¦¡Ä£ª¤Æ¥˦¼§̨¾©¿ª««½¬À­Û®ܯݰþ±á²â³ç´äµå¶ú·è¸õ¹éºë»ì¼í½î¾ê¿ïÀðÁòÂ÷ÃóÄôÅùÆæÇøÈãÉöÊûËüÌÞÍàÎñÑõÒùÖðEABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-E.YU/YA0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MNžO‰Q‘ Ð Ñ Ñ Ô Õ , Ò Ó Ò  ! ++" –& É0 ˜2 '3 ''9 <: >> -D /!Im!p!Re"!“5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "d"TE "0"Nabla"å "!å "-)"¦"ª"-"*"SQRT "?"oo "-V'"&("|)"**"++"INT 4".:<"~E"~=H"Å`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"–#? #? #? #?)#?*#?Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND] øÿ@°¤CURª-a¶P:º-o¼ 1/4¾ 3/4ÅAAÆAEÐDHÞTHåaaæaeðdhþthROEZH%KH&TS'CH(SH)SHCH.YU/YA6zhEkhFtsGchHshIshch! ++3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla "!µ "-)"SQRT "oo "-V+"INT 4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_Ê%<>`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¥´§¤¨¬©©ª»«Ç¬Â®¨¯ø°¡±±´«µµ¶¦·á¸üº¼»È¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÐÜÑ„ÒñÓîÔïÕÍօدÙôÚòÛó܆ݠÞÞß§àˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ðÝñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸýàþßÿØ1õRÎSÏxÙ’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý©½À¹ Ð Ñ Ô Õ â Ò Ó ã" ¥& É0 äD Ú¬ Û"!ª"¶"Æ"¸"·"Ã"°+"ºH"Å`"­d"²e"³Ê%×ÿøðÍ ?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¤CUR¥´¦|§¤¨¬©©ª»«Ǭ®¨¯ø°¡±±²^2³^3´«µµ¶¦·á¸ü¹^1º¼»ȼ 1/4½ 1/2¾ 3/4¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÐÜÑ„ÒñÓîÔïÕÍÖ…×xدÙôÚòÛó܆ݠÞÞß§àˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ðÝñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸýàþßÿØAaAaCc C cDdÜdEeEe1õ9L:l=L>lALBlCNDnGNHnPOQoRÎSÏTRUrXRYrZS[s^S_s`SasbTctdTetnUoupUquxÙyZzz{Z|z}Z~z’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©½±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀ¹ÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖ¹èABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-é.ó/å0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MNžO‰Q‘ Ð Ñ Ô Õ â Ò Ó ã +! ++" ¥& É0 ä2 '3 ''9 <: >> -D Ú¬ Û!Im!p!Re"!ª5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "¶"TE "¯"Æ"Nabla"(- "!(- "-)"¸"·"-"*"Ã"?"° "-V'"&("|)"**"++"º4".:<"~E"~=H"Å`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"á#? #? #? #?)#?*#?Ê%×`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]ÿøð@³¤CUR²^2³^3¹^1¼ 1/4½ 1/2¾ 3/4—EH˜TH¦PH§CH¨PS·eh¸thÆphÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch! ++3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-) "-V4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOF‚ß !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¥´§¤¨¬©©ª»«Ç¬Â®¨¯ø°¡±±´«µµ¶¦·á¸üº¼»È¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÑ„ÒñÓîÔïÕÍօدÙôÚòÛó܆ߧàˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸÿØÚÛ0Ü1ÝRÎSÏ^Þ_ßxÙ’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý©½À¹ Ð Ñ Ô Õ â Ò Ó ã  ! à" ¥& É0 ä"!ª"¶"Æ"¸"·"Ã"°+"ºH"Å`"­d"²e"³Ê%× øõÿøðÐ?  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~ Ê¡Á¢¢££¤CUR¥´¦|§¤¨¬©©ª»«Ǭ®¨¯ø°¡±±²^2³^3´«µµ¶¦·á¸ü¹^1º¼»ȼ 1/4½ 1/2¾ 3/4¿ÀÀËÁçÂåÃÌĀů®Ç‚ÈéÉƒÊæËèÌíÍêÎëÏìÐDHÑ„ÒñÓîÔïÕÍÖ…×xدÙôÚòÛó܆ÝYÞTHß§àˆá‡â‰ã‹äŠåŒæ¾çèéŽêë‘ì“í’î”ï•ðdhñ–ò˜ó—ô™õ›öš÷Öø¿ùúœûžüŸýyþthÿØAaAaCc C cDdDdEeEeÚÛ0Ü1Ý9L:l=L>lALBlCNDnGNHnPOQoRÎSÏTRUrXRYrZS[s^Þ_ß`SasbTctdTetnUoupUquxÙyZzz{Z|z}Z~z’ÄÆöÇÿØùÙúÚûÛþÜ÷Ýý‘A’B“G”D•E–Z—EH˜TH™IšK›LœMNžXŸO P¡R£S¤T¥U¦PH§CH¨PS©½±a²b³g´dµe¶z·eh¸th¹iºk»l¼m½n¾x¿oÀ¹ÁrÂsÃsÄtÅuÆphÇchÈpsÉohÑthetaÒupsiÖ¹èABVGDEZHZIJKLMNOP R!S"T#U$F%KH&TS'CH(SH)SHCH*"+Y,'-é.ó/å0a1b2v3g4d5e6zh7z8i9j:k;l<m=n>o?p@rAsBtCuDfEkhFtsGchHshIshchJ"KyL'MNžO‰Q‘ Ð Ñ Ô Õ â Ò Ó ã  ! à" ¥& É0 ä2 '3 ''9 <: >> -D /!Im!p!Re"!ª5!alef!<-‘!^’!->“!v”!<->µ!RETÐ!<=Ñ!^Ò!=>Ó!vÔ!<=>"FA "¶"TE "¯"Æ"Nabla"(- "!(- "-)"¸"·"-"*"Ã"?"° "-V'"&("|)"**"++"º4".:<"~E"~=H"Å`"­a"==d"²e"³‚"(ƒ")„"!(†"(=‡")=•"(+)—"(x)¥"_|_Å"á#? #? #? #?)#?*#?Ê%×`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND] øõÿøðC¹¤CUR²^2³^3¹^1¼ 1/4½ 1/2¾ 3/4ÐDHÞTHðdhþth—EH˜TH¦PH§CH¨PS·eh¸thÆphÇchÈpsÉohÑthetaÒupsiZH%KH&TS'CH(SH)SHCH6zhEkhFtsGchHshIshch3 ''!Im!Re5!alef!<-’!->”!<->µ!RETÐ!<=Ò!=>Ô!<=>"FA "TE "Nabla"(- "!(- "-) "-V4".:E"~=a"==„"!(†"(=‡")=•"(+)—"(x)¥"_|_`&[SPADE]c&[CLUB]e&[HEART]f& [DIAMOND]$EOFEOF frobtads-1.2.3/tads3/vmstack.cpp0000644000175000001440000000273411744264705015716 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMSTACK.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstack.cpp - VM stack implementation Function Notes Modified 10/28/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmstack.h" #include "vmfile.h" /* * allocate the stack */ CVmStack::CVmStack(size_t max_depth, size_t reserve) { /* * Allocate the array of stack elements. Allocate the requested * maximum depth plus the requested reserve space. Overallocate by a * few elements to leave ourselves a little buffer against mild * overages - for the most part, we count on the compiler to check for * proper stack usage at entry to each function, but intrinsics * sometimes push a few elements without checking. */ arr_ = (vm_val_t *)t3malloc((max_depth + reserve + 25) * sizeof(arr_[0])); /* remember the maximum depth and the reserve depth */ max_depth_ = max_depth; reserve_depth_ = reserve; /* the reserve is not yet in use */ reserve_in_use_ = FALSE; /* initialize the stack pointer */ init(); } /* * delete the stack */ CVmStack::~CVmStack() { /* delete the stack element array */ t3free(arr_); } frobtads-1.2.3/tads3/vmnetfillcl.cpp0000644000175000001440000003317412006322350016546 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnetfil.cpp - network file operations, local mode Function This module contains network file functions that are needed in both server and local modes. This file should be linked into all builds, whether or not the server mode is used. Notes Modified 09/08/10 MJRoberts - Creation */ #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnetfil.h" #include "vmnet.h" #include "vmfile.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmglob.h" #include "vmpredef.h" #include "vmimport.h" #include "sha2.h" #include "vmhash.h" #include "vmbif.h" #include "vmtype.h" #include "vmobj.h" #include "vmtmpfil.h" #include "vmtobj.h" #include "vmimage.h" #include "vmstack.h" #include "charmap.h" #include "vmlst.h" #include "vmfilnam.h" #include "vmfilobj.h" #include "vmstr.h" /* ------------------------------------------------------------------------ */ /* * Open a network file */ CVmNetFile *CVmNetFile::open(VMG_ const vm_val_t *val, const vm_rcdesc *rc, int mode, os_filetype_t typ, const char *mime_type) { vm_val_t filespec; /* check for a TadsObject implementing getFilename */ if (G_predef->filespec_getFilename != VM_INVALID_PROP && vm_val_cast_ok(CVmObjTads, val)) { /* call getFilename - the return value is the file spec */ G_interpreter->get_prop( vmg_ 0, val, G_predef->filespec_getFilename, val, 0, rc); /* the result is the real file spec */ filespec = *G_interpreter->get_r0(); } else { /* it's not a TadsObject, so it must directly have the file name */ filespec = *val; } /* check the file spec argument type */ CVmNetFile *nf = 0; CVmObjTemporaryFile *tmp = 0; CVmObjFileName *ofn = 0; if ((tmp = vm_val_cast(CVmObjTemporaryFile, &filespec)) != 0) { /* if the temporary file object is invalid, it's an error */ if (tmp->get_fname() == 0) err_throw(VMERR_CREATE_FILE); /* create the local file descriptor for the temp file path */ nf = open_local(vmg_ tmp->get_fname(), 0, mode, typ); /* mark it as a temp file */ nf->is_temp = TRUE; } else if (filespec.is_numeric(vmg0_) || ((ofn = vm_val_cast(CVmObjFileName, &filespec)) != 0 && ofn->is_special_file())) { /* * It's a special file ID, either as an integer or as a FileName * wrapping a special file int. Get the value. */ int32_t sfid = (ofn != 0 ? ofn->get_sfid() : filespec.num_to_int(vmg0_)); /* resolve the file system path for the given special file ID */ char fname[OSFNMAX] = { '\0' }; if (!CVmObjFile::sfid_to_path(vmg_ fname, sizeof(fname), sfid)) err_throw(VMERR_BAD_VAL_BIF); /* create the special file descriptor */ nf = open(vmg_ fname, sfid, mode, typ, mime_type); } else { /* anything else has to be a string */ char fname[OSFNMAX]; CVmBif::get_fname_val(vmg_ fname, sizeof(fname), &filespec); /* * if it's a local file, and it has a relative path, explicitly * apply the image file path as the default working directory */ if (!os_is_file_absolute(fname) && !is_net_mode(vmg0_)) { /* build the full, absolute name based on the file base path */ char fnabs[OSFNMAX]; os_build_full_path(fnabs, sizeof(fnabs), G_file_path, fname); /* replace the relative path with the new absolute path */ lib_strcpy(fname, sizeof(fname), fnabs); } /* create the regular network file descriptor */ nf = open(vmg_ fname, 0, mode, typ, mime_type); } /* if they gave us an object as our file spec, remember it */ if (nf != 0 && val->typ == VM_OBJ) nf->filespec = val->val.obj; /* return the network file descriptor */ return nf; } /* ------------------------------------------------------------------------ */ /* * Rename a file */ void CVmNetFile::rename_to_local(VMG_ CVmNetFile *newname) { /* if the new name isn't local, this isn't supported */ if (newname->is_net_file()) err_throw(VMERR_RENAME_FILE); /* if the destination file already exists, it's an error */ if (!osfacc(newname->lclfname)) err_throw(VMERR_RENAME_FILE); /* do the rename */ if (!os_rename_file(lclfname, newname->lclfname)) err_throw(VMERR_RENAME_FILE); } /* ------------------------------------------------------------------------ */ /* * Create a local directory */ void CVmNetFile::mkdir_local(VMG_ int create_parents) { /* try creating the directory */ if (!os_mkdir(lclfname, create_parents)) err_throw(VMERR_CREATE_FILE); } /* ------------------------------------------------------------------------ */ /* * Empty a local directory */ static void empty_dir(VMG_ const char *dir) { /* open the directory search */ osdirhdl_t dirhdl; if (os_open_dir(dir, &dirhdl)) { err_try { /* keep going until we're out of files */ char fname[OSFNMAX]; while (os_read_dir(dirhdl, fname, sizeof(fname))) { /* get the full path */ char path[OSFNMAX]; os_build_full_path(path, sizeof(path), dir, fname); /* get the mode */ unsigned long fmode; unsigned long fattr; if (osfmode(path, FALSE, &fmode, &fattr)) { /* check whether it's a directory or an ordinary file */ if ((fmode & OSFMODE_DIR) != 0) { /* * directory - skip the special '.' and '..' links, * since they'd get us stuck in a loop */ os_specfile_t st = os_is_special_file(fname); if (st != OS_SPECFILE_SELF && st != OS_SPECFILE_PARENT) { /* recursively empty the directory */ empty_dir(vmg_ path); /* remove this directory */ if (!os_rmdir(path)) err_throw(VMERR_DELETE_FILE); } } else { /* ordinary file - delete it */ if (osfdel(path)) err_throw(VMERR_DELETE_FILE); } } } } err_finally { /* close the directory search handle */ os_close_dir(dirhdl); } err_end; } } /* * Remove a local directory */ void CVmNetFile::rmdir_local(VMG_ int remove_contents) { /* if desired, recursively remove the directory's contents */ if (remove_contents) empty_dir(vmg_ lclfname); /* try removing the directory */ if (!os_rmdir(lclfname)) err_throw(VMERR_DELETE_FILE); } /* ------------------------------------------------------------------------ */ /* * static directory lister */ static int s_readdir_local(VMG_ const char *lclfname, const char *nominal_path, vm_val_t *retval, const vm_rcdesc *rc, const vm_val_t *cb, int recursive) { /* note the nominal path string length */ size_t nominal_path_len = strlen(nominal_path); /* * If the caller wants a result list, create a list for the return * value. We don't know how many elements the list will need, so * create it with an arbitrary initial size, and expand it later as * needed. */ CVmObjList *lst = 0; if (retval != 0) { /* create the list */ const size_t initlen = 32; retval->set_obj(CVmObjList::create(vmg_ FALSE, initlen)); lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* clear it */ lst->cons_clear(0, initlen - 1); /* save the new list on the stack for gc protection */ G_stk->push(retval); } /* presume success */ int ok = TRUE; /* open the directory */ osdirhdl_t dirhdl; if (os_open_dir(lclfname, &dirhdl)) { err_try { /* iterate over the directory's contents */ int idx = 0; char curname[OSFNMAX]; while (os_read_dir(dirhdl, curname, sizeof(curname))) { char *unm = 0; vm_obj_id_t fnobj = VM_INVALID_OBJ; err_try { /* map the filename to UTF8 */ size_t unmlen = G_cmap_from_fname->map_str_alo( &unm, curname); /* create the FileName object */ fnobj = CVmObjFileName::combine_path( vmg_ nominal_path, nominal_path_len, unm, unmlen, TRUE); /* push it for gc protection */ G_stk->push()->set_obj(fnobj); } err_finally { if (unm != 0) t3free(unm); } err_end; /* if we're building a list, add the file to the list */ if (retval != 0) { vm_val_t ele; ele.set_obj(fnobj); lst->cons_ensure_space(vmg_ idx, 16); lst->cons_set_element(idx, &ele); lst->cons_set_len(++idx); } /* if there's a callback, invoke it */ if (cb != 0) { G_stk->push()->set_obj(fnobj); G_interpreter->call_func_ptr(vmg_ cb, 1, rc, 0); } /* * If we're doing a recursive listing, and this is a * directory, list its ocntents. Skip self and parent * directory links ('.' and '..' on Unix), since those * would cause infinite recursion. */ os_specfile_t st; if (recursive && (st = os_is_special_file(curname)) != OS_SPECFILE_SELF && st != OS_SPECFILE_PARENT) { /* build the full path name */ char fullname[OSFNMAX]; os_build_full_path(fullname, sizeof(fullname), lclfname, curname); /* check to see if it's a directory */ unsigned long fmode; unsigned long fattr; if (osfmode(fullname, FALSE, &fmode, &fattr) && (fmode & OSFMODE_DIR) != 0) { /* get the combined path from the FileName object */ const char *path = vm_objid_cast(CVmObjFileName, fnobj) ->get_path_string(); /* build the actual combined path */ char subfname[OSFNMAX]; os_build_full_path( subfname, sizeof(subfname), lclfname, curname); /* do the recursive listing */ ok |= s_readdir_local(vmg_ subfname, path + VMB_LEN, retval, rc, cb, TRUE); } } /* we're done with the FileName object for this iteration */ G_stk->discard(1); } } err_finally { /* close the directory handle */ os_close_dir(dirhdl); } err_end; } /* discard the gc protection for the list, if applicable */ if (retval != 0) G_stk->discard(1); /* return the result */ return ok; } /* * Get a local directory listing, returning a list of FileName objects * and/or invoking a callback for each file found. */ int CVmNetFile::readdir_local(VMG_ const char *nominal_path, vm_val_t *retval, const struct vm_rcdesc *rc, const vm_val_t *cb, int recursive) { /* verify that the path exists and refers to a directory */ unsigned long mode; unsigned long attr; if (!osfmode(lclfname, TRUE, &mode, &attr) || (mode & OSFMODE_DIR) == 0) return FALSE; /* * if the caller didn't specify a nominal path to use in constructed * FileName objects, use the actual local path */ if (nominal_path == 0) nominal_path = lclfname; /* call our static implementation with our local filename path */ return s_readdir_local(vmg_ lclfname, nominal_path, retval, rc, cb, recursive); } /* ------------------------------------------------------------------------ */ /* * Mark references for the garbage collector */ void CVmNetFile::mark_refs(VMG_ uint state) { /* if we have a filespec object, mark it */ if (filespec != VM_INVALID_OBJ) G_obj_table->mark_all_refs(filespec, state); } frobtads-1.2.3/tads3/vmconsol.cpp0000644000175000001440000036653512145504453016113 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1987, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmconsol.cpp - TADS 3 console input reader and output formatter Function Provides console input and output for the TADS 3 built-in function set for the T3 VM, including the output formatter. T3 uses the UTF-8 character set to represent character strings. The OS functions use the local character set. We perform the mapping between UTF-8 and the local character set within this module, so that OS routines see local characters only, not UTF-8. This code is based on the TADS 2 output formatter, but has been substantially reworked for C++, Unicode, and the slightly different TADS 3 formatting model. Notes Returns None Modified 08/25/99 MJRoberts - created from TADS 2 output formatter */ #include #include #include #include #include #include "wchar.h" #include "os.h" #include "t3std.h" #include "utf8.h" #include "charmap.h" #include "vmuni.h" #include "vmconsol.h" #include "vmglob.h" #include "vmhash.h" #include "vmdatasrc.h" #include "vmnetfil.h" #include "vmfilobj.h" #include "vmerr.h" #include "vmobj.h" /* ------------------------------------------------------------------------ */ /* * Log-file formatter subclass implementation */ /* * Open a new log file */ int CVmFormatterLog::open_log_file(VMG_ const char *fname) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ fname, 0, NETF_NEW, OSFTLOG, "text/plain"); } err_catch_disc { nf = 0; } err_end; /* open the log file using the network file descriptor */ return open_log_file(vmg_ nf); } int CVmFormatterLog::open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ filespec, rc, NETF_NEW, OSFTLOG, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* open the log file using the network file descriptor */ int err = open_log_file(vmg_ nf); /* if that succeeded, remember the file spec in our global variable */ if (!err) { /* create our VM global if we don't have one already */ if (logglob_ == 0) logglob_ = G_obj_table->create_global_var(); /* remember the file spec */ logglob_->val = *filespec; } /* return the result */ return err; } int CVmFormatterLog::open_log_file(VMG_ CVmNetFile *nf) { /* close any existing log file */ if (close_log_file(vmg0_)) { if (nf != 0) nf->abandon(vmg0_); return 1; } /* if there's no network file spec, return failure */ if (nf == 0) return 1; /* reinitialize */ init(); /* remember the network file descriptor */ lognf_ = nf; /* open the local file */ logfp_ = osfopwt(lognf_->lclfname, OSFTLOG); /* if we couldn't open the file, abandon the network file descriptor */ if (logfp_ == 0) { lognf_->abandon(vmg0_); lognf_ = 0; } /* return success if we successfully opened the file, failure otherwise */ return (logfp_ == 0); } /* * Set the log file to a file opened by the caller */ int CVmFormatterLog::set_log_file(VMG_ CVmNetFile *nf, osfildef *fp) { /* close any existing log file */ if (close_log_file(vmg0_)) return 1; /* reinitialize */ init(); /* remember the file */ logfp_ = fp; lognf_ = nf; /* success */ return 0; } /* * Close the log file */ int CVmFormatterLog::close_log_file(VMG0_) { /* presume success */ int err = FALSE; /* if we have a file, close it */ if (logfp_ != 0) { /* close and forget the handle */ osfcls(logfp_); logfp_ = 0; } /* forget the log network file descriptor, if we have one */ if (lognf_ != 0) { err_try { lognf_->close(vmg0_); } err_catch_disc { /* flag the error, but otherwise discard the exception */ err = TRUE; } err_end; /* forget the descriptor, as we've just deleted it */ lognf_ = 0; } /* clear our global for the file spec */ if (logglob_ != 0) logglob_->val.set_nil(); /* success */ return err; } /* ------------------------------------------------------------------------ */ /* * Base Formatter */ /* * deletion */ CVmFormatter::~CVmFormatter() { /* if we have a table of horizontal tabs, delete it */ if (tabs_ != 0) delete tabs_; /* forget the character mapper */ set_charmap(0); } /* * set a new character mapper */ void CVmFormatter::set_charmap(CCharmapToLocal *cmap) { /* add a reference to the new mapper, if we have one */ if (cmap != 0) cmap->add_ref(); /* release our reference on any old mapper */ if (cmap_ != 0) cmap_->release_ref(); /* remember the new mapper */ cmap_ = cmap; } /* * Write out a line. Text we receive is in the UTF-8 character set. */ void CVmFormatter::write_text(VMG_ const wchar_t *txt, size_t cnt, const vmcon_color_t *colors, vm_nl_type nl) { /* * Check the "script quiet" mode - this indicates that we're reading * a script and not echoing output to the display. If this mode is * on, and we're writing to the display, suppress this write. If * the mode is off, or we're writing to a non-display stream (such * as a log file stream), show the output as normal. */ if (!console_->is_quiet_script() || !is_disp_stream_) { char local_buf[128]; char *dst; size_t rem; /* * Check to see if we've reached the end of the screen, and if so * run the MORE prompt. Note that we don't show a MORE prompt * unless we're in "formatter more mode," since if we're not, then * the OS layer code is taking responsibility for pagination * issues. * * Note that we suppress the MORE prompt if we're showing a * continuation of a line already partially shown. We only want to * show a MORE prompt at the start of a new line. * * Skip the MORE prompt if this stream doesn't use it. */ if (formatter_more_mode() && console_->is_more_mode() && !is_continuation_ && linecnt_ + 1 >= console_->get_page_length()) { /* set the standard text color */ set_os_text_color(OS_COLOR_P_TEXT, OS_COLOR_P_TEXTBG); set_os_text_attr(0); /* display the MORE prompt */ console_->show_more_prompt(vmg0_); /* restore the current color scheme */ set_os_text_color(os_color_.fg, os_color_.bg); set_os_text_attr(os_color_.attr); } /* count the line if a newline follows */ if (nl != VM_NL_NONE && nl != VM_NL_NONE_INTERNAL) ++linecnt_; /* convert and display the text */ for (dst = local_buf, rem = sizeof(local_buf) - 1 ; cnt != 0 ; ) { size_t cur; size_t old_rem; wchar_t c; /* * if this character is in a new color, write out the OS-level * color switch code */ if (colors != 0 && !colors->equals(&os_color_)) { /* * null-terminate and display what's in the buffer so far, * so that we close out all of the remaining text in the * old color and attributes */ *dst = '\0'; print_to_os(local_buf); /* reset to the start of the local output buffer */ dst = local_buf; rem = sizeof(local_buf) - 1; /* set the text attributes, if they changed */ if (colors->attr != os_color_.attr) set_os_text_attr(colors->attr); /* set the color, if it changed */ if (colors->fg != os_color_.fg || colors->bg != os_color_.bg) set_os_text_color(colors->fg, colors->bg); /* * Whatever happened, set our new color internally as the * last color we sent to the OS. Even if we didn't * actually do anything, we'll at least know we won't have * to do anything more until we find another new color. */ os_color_ = *colors; } /* get this character */ c = *txt; /* * translate non-breaking spaces into ordinary spaces if the * underlying target isn't HTML-based */ if (!html_target_ && c == 0x00A0) c = ' '; /* try storing another character */ old_rem = rem; cur = (cmap_ != 0 ? cmap_ : G_cmap_to_ui)->map(c, &dst, &rem); /* if that failed, flush the buffer and try again */ if (cur > old_rem) { /* null-terminate the buffer */ *dst = '\0'; /* display the text */ print_to_os(local_buf); /* reset to the start of the local output buffer */ dst = local_buf; rem = sizeof(local_buf) - 1; } else { /* we've now consumed this character of input */ ++txt; --cnt; if (colors != 0) ++colors; } } /* if we have a partially-filled buffer, display it */ if (dst > local_buf) { /* null-terminate and display the buffer */ *dst = '\0'; print_to_os(local_buf); } /* write the appropriate type of line termination */ switch(nl) { case VM_NL_NONE: case VM_NL_INPUT: case VM_NL_NONE_INTERNAL: /* no line termination is needed */ break; case VM_NL_NEWLINE: /* write a newline */ print_to_os(html_target_ && html_pre_level_ == 0 ? "
\n" : "\n"); break; case VM_NL_OSNEWLINE: /* * the OS will provide a newline, but add a space to make it * explicit that we can break the line here */ print_to_os(" "); break; } } } /* ------------------------------------------------------------------------ */ /* * Flush the current line to the display, using the given type of line * termination. * * VM_NL_NONE: flush the current line but do not start a new line; more * text will follow on the current line. This is used, for example, to * flush text after displaying a prompt and before waiting for user * input. * * VM_NL_INPUT: acts like VM_NL_NONE, except that we flush everything, * including trailing spaces. * * VM_NL_NONE_INTERNAL: same as VM_NL_NONE, but doesn't flush at the OS * level. This is used when we're only flushing our buffers in order to * clear out space internally, not because we want the underlying OS * renderer to display things immediately. This distinction is * important in HTML mode, since it ensures that the HTML parser only * sees well-formed strings when flushing. * * VM_NL_NEWLINE: flush the line and start a new line by writing out a * newline character. * * VM_NL_OSNEWLINE: flush the line as though starting a new line, but * don't add an actual newline character to the output, since the * underlying OS display code will handle this. Instead, add a space * after the line to indicate to the OS code that a line break is * possible there. (This differs from VM_NL_NONE in that VM_NL_NONE * doesn't add anything at all after the line.) */ void CVmFormatter::flush(VMG_ vm_nl_type nl) { int cnt; vm_nl_type write_nl; /* null-terminate the current output line buffer */ linebuf_[linepos_] = '\0'; /* * Expand any pending tab. Allow "anonymous" tabs only if we're * flushing because we're ending the line normally; if we're not * ending the line, we can't handle tabs that depend on the line * ending. */ expand_pending_tab(vmg_ nl == VM_NL_NEWLINE); /* * note number of characters to display - assume we'll display all of * the characters in the buffer */ cnt = wcslen(linebuf_); /* * Trim trailing spaces, unless we're about to read input or are doing * an internal flush. (Show trailing spaces when reading input, since * we won't be able to revise the layout after this point. Don't trim * on an internal flush either, as this kind of flushing simply empties * out our buffer exactly as it is.) */ if (nl != VM_NL_INPUT && nl != VM_NL_NONE_INTERNAL) { /* * look for last non-space character, but keep any spaces that come * before an explicit non-breaking flag */ for ( ; cnt > 0 && linebuf_[cnt-1] == ' ' ; --cnt) { /* don't remove this character if it's marked as non-breaking */ if ((flagbuf_[cnt-1] & VMCON_OBF_NOBREAK) != 0) break; } /* * if we're actually doing a newline, discard the trailing spaces * for good - we don't want them at the start of the next line */ if (nl == VM_NL_NEWLINE) linepos_ = cnt; } /* check the newline mode */ switch(nl) { case VM_NL_NONE: case VM_NL_NONE_INTERNAL: /* no newline - just flush out what we have */ write_nl = VM_NL_NONE; break; case VM_NL_INPUT: /* no newline - flush out what we have */ write_nl = VM_NL_NONE; /* on input, reset the HTML parsing state */ html_passthru_state_ = VMCON_HPS_NORMAL; break; case VM_NL_NEWLINE: /* * We're adding a newline. We want to suppress redundant * newlines -- we reduce any run of consecutive vertical * whitespace to a single newline. So, if we have anything in * this line, or we didn't already just write a newline, write * out a newline now; otherwise, write nothing. */ if (linecol_ != 0 || !just_did_nl_ || html_pre_level_ > 0) { /* add the newline */ write_nl = VM_NL_NEWLINE; } else { /* * Don't write out a newline after all - the line buffer is * empty, and we just wrote a newline, so this is a * redundant newline that we wish to suppress (so that we * collapse a run of vertical whitespace down to a single * newline). */ write_nl = VM_NL_NONE; } break; case VM_NL_OSNEWLINE: /* * we're going to depend on the underlying OS output layer to do * line breaking, so we won't add a newline, but we will add a * space, so that the underlying OS layer knows we have a word * break here */ write_nl = VM_NL_OSNEWLINE; break; } /* * display the line, as long as we have something buffered to * display; even if we don't, display it if our column is non-zero * and we didn't just do a newline, since this must mean that we've * flushed a partial line and are just now doing the newline */ if (cnt != 0 || (linecol_ != 0 && !just_did_nl_) || html_pre_level_ > 0) { /* write it out */ write_text(vmg_ linebuf_, cnt, colorbuf_, write_nl); } /* check the line ending */ switch (nl) { case VM_NL_NONE: case VM_NL_INPUT: /* we're not displaying a newline, so flush what we have */ flush_to_os(); /* * the subsequent buffer will be a continuation of the current * text, if we've displayed anything at all here */ is_continuation_ = (linecol_ != 0); break; case VM_NL_NONE_INTERNAL: /* * internal buffer flush only - subsequent text will be a * continuation of the current line, if there's anything on the * current line */ is_continuation_ = (linecol_ != 0); break; default: /* we displayed a newline, so reset the column position */ linecol_ = 0; /* the next buffer starts a new line on the display */ is_continuation_ = FALSE; break; } /* * Move any trailing characters we didn't write in this go to the start * of the buffer. */ if (cnt < linepos_) { size_t movecnt; /* calculate how many trailing characters we didn't write */ movecnt = linepos_ - cnt; /* move the characters, colors, and flags */ memmove(linebuf_, linebuf_ + cnt, movecnt * sizeof(linebuf_[0])); memmove(colorbuf_, colorbuf_ + cnt, movecnt * sizeof(colorbuf_[0])); memmove(flagbuf_, flagbuf_ + cnt, movecnt * sizeof(flagbuf_[0])); } /* move the line output position to follow the preserved characters */ linepos_ -= cnt; /* * If we just output a newline, note it. If we didn't just output a * newline, but we did write out anything else, note that we're no * longer at the start of a line on the underlying output device. */ if (nl == VM_NL_NEWLINE) just_did_nl_ = TRUE; else if (cnt != 0) just_did_nl_ = FALSE; /* * if the current buffering color doesn't match the current osifc-layer * color, then we must need to flush just the new color/attribute * settings (this can happen when we have changed the attributes in * preparation for reading input, since we won't have any actual text * to write after the color change) */ if (!cur_color_.equals(&os_color_)) { /* set the text attributes in the OS window, if they changed */ if (cur_color_.attr != os_color_.attr) set_os_text_attr(cur_color_.attr); /* set the color in the OS window, if it changed */ if (cur_color_.fg != os_color_.fg || cur_color_.bg != os_color_.bg) set_os_text_color(cur_color_.fg, cur_color_.bg); /* set the new osifc color */ os_color_ = cur_color_; } } /* ------------------------------------------------------------------------ */ /* * Clear out our buffers */ void CVmFormatter::empty_buffers(VMG0_) { /* reset our buffer pointers */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; just_did_nl_ = FALSE; is_continuation_ = FALSE; /* there's no pending tab now */ pending_tab_align_ = VMFMT_TAB_NONE; /* start out at the first line */ linecnt_ = 0; /* reset the HTML lexical state */ html_passthru_state_ = VMCON_HPS_NORMAL; } /* ------------------------------------------------------------------------ */ /* * Immediately update the display window */ void CVmFormatter::update_display(VMG0_) { /* update the display window at the OS layer */ os_update_display(); } /* ------------------------------------------------------------------------ */ /* * Display a blank line to the stream */ void CVmFormatter::write_blank_line(VMG0_) { /* flush the stream */ flush(vmg_ VM_NL_NEWLINE); /* if generating for an HTML display target, add an HTML line break */ if (html_target_) write_text(vmg_ L"
", 4, 0, VM_NL_NONE); /* write out a blank line */ write_text(vmg_ L"", 0, 0, VM_NL_NEWLINE); } /* ------------------------------------------------------------------------ */ /* * Generate a tab for a "\t" sequence in the game text, or a or sequence parsed in our mini-parser. * * Standard (non-HTML) version: we'll generate enough spaces to take us to * the next tab stop. * * HTML version: if we're in native HTML mode, we'll just generate the * equivalent HTML; if we're not in HTML mode, we'll generate a hard tab * character, which the HTML formatter will interpret as a . */ void CVmFormatter::write_tab(VMG_ int indent, int multiple) { int maxcol; /* check to see what the underlying system is expecting */ if (html_target_) { char buf[40]; /* * the underlying system is HTML - generate an appropriate * sequence to produce the desired effect */ sprintf(buf, "", indent != 0 ? "INDENT" : "MULTIPLE", indent != 0 ? indent : multiple); /* write it out */ buffer_string(vmg_ buf); } else if (multiple != 0) { /* get the maximum column */ maxcol = get_buffer_maxcol(); /* * We don't have an HTML target, and we have a tab to an every-N * stop: expand the tab with spaces. Keep going until we reach * the next tab stop of the given multiple. */ do { /* stop if we've reached the maximum column */ if (linecol_ >= maxcol) break; /* add another space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cur_flags_; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer */ ++linepos_; /* advance the column counter */ ++linecol_; } while ((linecol_ + 1) % multiple != 0); } else if (indent != 0) { /* * We don't have an HTML target, and we just want to add a given * number of spaces. Simply write out the given number of spaces, * up to our maximum column limit. */ for (maxcol = get_buffer_maxcol() ; indent != 0 && linecol_ < maxcol ; --indent) { /* add another space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cur_flags_; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer and one column */ ++linepos_; ++linecol_; } } } /* ------------------------------------------------------------------------ */ /* * Flush a line */ void CVmFormatter::flush_line(VMG_ int padding) { /* * check to see if we're using the underlying display layer's line * wrapping */ if (os_line_wrap_) { /* * In the HTML version, we don't need the normal *MORE* * processing, since the HTML layer will handle that. * Furthermore, we don't need to provide actual newline breaks * -- that happens after the HTML is parsed, so we don't have * enough information here to figure out actual line breaks. * So, we'll just flush out our buffer whenever it fills up, and * suppress newlines. * * Similarly, if we have OS-level line wrapping, don't try to * figure out where the line breaks go -- just flush our buffer * without a trailing newline whenever the buffer is full, and * let the OS layer worry about formatting lines and paragraphs. * * If we're using padding, use newline mode VM_NL_OSNEWLINE. If * we don't want padding (which is the case if we completely * fill up the buffer without finding any word breaks), write * out in mode VM_NL_NONE, which just flushes the buffer exactly * like it is. */ flush(vmg_ padding ? VM_NL_OSNEWLINE : VM_NL_NONE_INTERNAL); } else { /* * Normal mode - we process the *MORE* prompt ourselves, and we * are responsible for figuring out where the actual line breaks * go. Use flush() to generate an actual newline whenever we * flush out our buffer. */ flush(vmg_ VM_NL_NEWLINE); } } /* ------------------------------------------------------------------------ */ /* * Write a character to an output stream. The character is provided to us * as a wide Unicode character. */ void CVmFormatter::buffer_char(VMG_ wchar_t c) { const wchar_t *exp; size_t exp_len; /* check for a display expansion */ exp = (cmap_ != 0 ? cmap_ : G_cmap_to_ui)->get_expansion(c, &exp_len); if (exp != 0) { /* write each character of the expansion */ for ( ; exp_len != 0 ; ++exp, --exp_len) buffer_expchar(vmg_ *exp); } else { /* there's no expansion - buffer the character as-is */ buffer_expchar(vmg_ c); } } /* * Write an expanded character to an output stream. */ void CVmFormatter::buffer_expchar(VMG_ wchar_t c) { /* presume the character takes up only one column */ int cwid = 1; /* presume we'll use the current flags for the new character */ unsigned char cflags = cur_flags_; /* assume it's not a quoted space */ int qspace = FALSE; /* * Check for some special characters. * * If we have an underlying HTML renderer, keep track of the HTML * lexical state, so we know if we're in a tag or in ordinary text. We * can pass through all of the special line-breaking and spacing * characters to the underlying HTML renderer. * * If our underlying renderer is a plain text renderer, we actually * parse the HTML ourselves, so HTML tags will never make it this far - * the caller will already have interpreted any HTML tags and removed * them from the text stream, passing only the final plain text to us. * However, with a plain text renderer, we have to do all of the work * of line breaking, so we must look at the special spacing and * line-break control characters. */ if (html_target_) { /* * track the lexical state of the HTML stream going to the * underlying renderer */ switch (html_passthru_state_) { case VMCON_HPS_MARKUP_END: case VMCON_HPS_NORMAL: /* check to see if we're starting a markup */ if (c == '&') html_passthru_state_ = VMCON_HPS_ENTITY_1ST; else if (c == '<') { html_passthru_tagp_ = html_passthru_tag_; html_passthru_state_ = VMCON_HPS_TAG_START; } else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_1ST: /* check to see what kind of entity we have */ if (c == '#') html_passthru_state_ = VMCON_HPS_ENTITY_NUM_1ST; else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) html_passthru_state_ = VMCON_HPS_ENTITY_NAME; else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_NUM_1ST: /* check to see what kind of number we have */ if (c == 'x' || c == 'X') html_passthru_state_ = VMCON_HPS_ENTITY_HEX; else if (c >= '0' && c <= '9') html_passthru_state_ = VMCON_HPS_ENTITY_DEC; else html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_HEX: /* see if we're done with hex digits */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_DEC: /* see if we're done with decimal digits */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if (c < '0' || c > '9') html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_ENTITY_NAME: /* see if we're done with alphanumerics */ if (c == ';') html_passthru_state_ = VMCON_HPS_MARKUP_END; else if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) html_passthru_state_ = VMCON_HPS_NORMAL; break; case VMCON_HPS_TAG_START: /* start of a tag, before the name - check for the name start */ if (c == '/' && html_passthru_tagp_ == html_passthru_tag_) { /* * note the initial '/', but stay in TAG_START state, so * that we skip any spaces between the '/' and the tag name */ *html_passthru_tagp_++ = '/'; } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { /* start of the tag name */ html_passthru_state_ = VMCON_HPS_TAG_NAME; *html_passthru_tagp_++ = (char)c; } else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { /* * ignore whitespace between '<' and the tag name - simply * stay in TAG_START mode */ } else { /* anything else is invalid - must not be a tag after all */ html_passthru_state_ = VMCON_HPS_NORMAL; } break; case VMCON_HPS_TAG_NAME: /* tag name - check for continuation */ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { /* gather the tag name if it fits */ if (html_passthru_tagp_ - html_passthru_tag_ + 1 < sizeof(html_passthru_tag_)) *html_passthru_tagp_++ = (char)c; } else { /* end of the tag name */ *html_passthru_tagp_ = '\0'; html_passthru_state_ = VMCON_HPS_TAG; goto do_tag; } break; case VMCON_HPS_TAG: do_tag: /* see if we're done with the tag, or entering quoted material */ if (c == '>') { /* switch to end of markup mode */ html_passthru_state_ = VMCON_HPS_MARKUP_END; /* note if entering or exiting a PRE tag */ if (stricmp(html_passthru_tag_, "pre") == 0) ++html_pre_level_; else if (stricmp(html_passthru_tag_, "/pre") == 0 && html_pre_level_ != 0) --html_pre_level_; } else if (c == '/' && html_passthru_tagp_ != html_passthru_tag_ && *(html_passthru_tagp_ - 1) != '/') { /* add the '/' to the end of the tag name */ if (html_passthru_tagp_ - html_passthru_tag_ + 1 < sizeof(html_passthru_tag_)) { *html_passthru_tagp_++ = (char)c; *html_passthru_tagp_ = '\0'; } } else if (c == '"') html_passthru_state_ = VMCON_HPS_DQUOTE; else if (c == '\'') html_passthru_state_ = VMCON_HPS_SQUOTE; break; case VMCON_HPS_SQUOTE: /* see if we're done with the quoted material */ if (c == '\'') html_passthru_state_ = VMCON_HPS_TAG; break; case VMCON_HPS_DQUOTE: /* see if we're done with the quoted material */ if (c == '"') html_passthru_state_ = VMCON_HPS_TAG; break; default: /* ignore other states */ break; } } else { /* check for special characters */ switch(c) { case 0x00AD: /* * The Unicode "soft hyphen" character. This indicates a * point at which we can insert a hyphen followed by a soft * line break, if it's a convenient point to break the line; * if we don't choose to break the line here, the soft hyphen * is invisible. * * Don't buffer anything at all; instead, just flag the * preceding character as being a soft hyphenation point, so * that we can insert a hyphen there when we get around to * breaking the line. */ if (linepos_ != 0) flagbuf_[linepos_ - 1] |= VMCON_OBF_SHY; /* we don't need to buffer anything, so we're done */ return; case 0xFEFF: /* * The Unicode zero-width non-breaking space. This indicates * a point at which we cannot break the line, even if we could * normally break here. Flag the preceding character as a * non-breaking point. Don't buffer anything for this * character, as it's not rendered; it merely controls line * breaking. */ if (linepos_ != 0) flagbuf_[linepos_ - 1] |= VMCON_OBF_NOBREAK; /* we don't buffer anything, so we're done */ return; case 0x200B: /* zero-width space */ case 0x200a: /* hair space */ case 0x2008: /* punctuation space */ /* * Zero-width space: This indicates an explicitly allowed * line-breaking point, but is rendered as invisible. Flag the * preceding character as an OK-to-break point, but don't * buffer anything, as the zero-width space isn't rendered. * * Hair and punctuation spaces: Treat these very thin spaces as * invisible in a fixed-width font. These are normally used * for subtle typographical effects in proportionally-spaced * fonts; for example, for separating a right single quote from * an immediately following right double quote (as in a * quotation within a quotation: I said, "type 'quote'"). When * translating to fixed-pitch type, these special spacing * effects aren't usually necessary or desirable because of the * built-in space in every character cell. * * These spaces cancel any explicit non-breaking flag that * precedes them, since they cause the flag to act on the * space's left edge, while leaving the right edge open for * breaking. Since we don't actually take up any buffer space, * push our right edge's breakability back to the preceding * character. */ if (linepos_ != 0) { flagbuf_[linepos_ - 1] &= ~VMCON_OBF_NOBREAK; flagbuf_[linepos_ - 1] |= VMCON_OBF_OKBREAK; } /* we don't buffer anything, so we're done */ return; case 0x00A0: /* non-breaking space - buffer it as given */ break; case 0x0015: /* special internal quoted space character */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2009: /* thin space */ /* * Treat all of these as non-combining spaces, and render them * all as single ordinary spaces. In text mode, we are * limited to a monospaced font, so we can't render any * differences among these various thinner-than-normal spaces. */ qspace = TRUE; c = ' '; break; case 0x2002: /* en space */ case 0x2004: /* three-per-em space */ /* * En space, three-per-em space - mark these as non-combining, * and render them as a two ordinary spaces. In the case of * an en space, we really do want to take up the space of two * ordinary spaces; for a three-per-em space, we want about a * space and a half, but since we're dealing with a monospaced * font, we have to round up to a full two spaces. */ qspace = TRUE; cwid = 2; c = ' '; break; case 0x2003: /* em space - mark it as non-combining */ qspace = TRUE; /* render this as three ordinary spaces */ cwid = 3; c = ' '; break; default: /* * Translate any horizontal whitespace character to a regular * space character. Note that, once this is done, we don't * need to worry about calling t3_is_space() any more - we can * just check that we have a regular ' ' character. */ if (t3_is_space(c)) { /* convert it to an ordinary space */ c = ' '; /* if we're in obey-whitespace mode, quote the space */ qspace = obey_whitespace_ || html_pre_level_ > 0; } break; } } /* if it's a quoted space, mark it as such in the buffer flags */ if (qspace) cflags |= VMCON_OBF_QSPACE; /* * Check for the caps/nocaps flags - but only if our HTML lexical state * in the underlying text stream is plain text, because we don't want * to apply these flags to alphabetic characters that are inside tag or * entity text. */ if (html_passthru_state_ == VMCON_HPS_NORMAL) { if ((capsflag_ || allcapsflag_) && t3_is_alpha(c)) { /* * capsflag or allcapsflag is set, so render this character in * title case or upper case, respectively. For the ordinary * capsflag, use title case rather than upper case, since * capsflag conceptually only applies to a single letter, and * some Unicode characters represent ligatures of multiple * letters. In such cases we only want to capitalize the first * letter in the ligature, which is exactly what we get when we * convert it to the title case. * * Start by consuming the capsflag. */ capsflag_ = FALSE; /* get the appropriate expansion */ const wchar_t *u = allcapsflag_ ? t3_to_upper(c) : t3_to_title(c); /* * If there's no expansion, continue with the original * character. If it's a single-character expansion, continue * with the replacement character. If it's a 1:N expansion, * recursively buffer the expansion. */ if (u != 0 && u[0] != 0) { if (u[1] != 0) { /* 1:N expansion - handle it recursively */ buffer_wstring(vmg_ u); return; } else { /* 1:1 expansion - continue with the new character */ c = u[0]; } } } else if (nocapsflag_ && t3_is_alpha(c)) { /* lower-casing the character - consume the flag */ nocapsflag_ = FALSE; /* get the expansion */ const wchar_t *l = t3_to_lower(c); /* * recursively handle 1:N expansions; otherwise continue with * the replacement character, if there is one */ if (l != 0 && l[0] != 0) { if (l[1] != 0) { /* 1:N expansion - handle it recursively */ buffer_wstring(vmg_ l); return; } else { /* 1:1 expansion - continue with the new character */ c = l[0]; } } } } /* * If this is a space of some kind, we might be able to consolidate it * with a preceding character. If the display layer is an HTML * renderer, pass spaces through intact, and let the HTML parser handle * whitespace compression. */ if (c == ' ' && html_pre_level_ == 0) { /* ignore ordinary whitespace at the start of a line */ if (linecol_ == 0 && !qspace) return; /* * Consolidate runs of whitespace. Ordinary whitespace is * subsumed into any type of quoted spaces, but quoted spaces do * not combine. */ if (linepos_ > 0) { wchar_t prv; /* get the previous character */ prv = linebuf_[linepos_ - 1]; /* * if the new character is an ordinary (combining) whitespace * character, subsume it into any preceding space character */ if (!qspace && prv == ' ') return; /* * if the new character is a quoted space, and the preceding * character is a non-quoted space, subsume the preceding * space into the new character */ if (qspace && prv == ' ' && !(flagbuf_[linepos_ - 1] & VMCON_OBF_QSPACE)) { /* remove the preceding ordinary whitespace */ --linepos_; --linecol_; } } } /* if the new character fits in the line, add it */ if (linecol_ + cwid < get_buffer_maxcol()) { /* buffer this character */ buffer_rendered(c, cflags, cwid); /* we're finished processing the character */ return; } /* * The line would overflow if this character were added. * * If we're trying to output any kind of breakable space, just add it * to the line buffer for now; we'll come back later and figure out * where to break upon buffering the next non-space character. This * ensures that we don't carry trailing space (even trailing en or em * spaces) to the start of the next line if we have an explicit * newline before the next non-space character. */ if (c == ' ') { /* * We're adding a space, so we'll figure out the breaking later, * when we output the next non-space character. If the preceding * character is any kind of space, don't bother adding the new * one, since any contiguous whitespace at the end of the line has * no effect on the line's appearance. */ if (linebuf_[linepos_ - 1] == ' ') { /* * We're adding a space to a line that already ends in a * space, so we don't really need to add the character. * However, reflect the virtual addition in the output column * position, since the space does affect our column position. * We know that we're adding the new space even though we have * a space preceding, since we wouldn't have gotten this far * if we were going to collapse the space with a run of * whitespace. */ } else { /* the line doesn't already end in space, so add the space */ linebuf_[linepos_] = ' '; flagbuf_[linepos_] = cflags; colorbuf_[linepos_] = cur_color_; /* advance one character in the buffer */ ++linepos_; } /* * Adjust the column position for the added space. Note that we * adjust by the rendered width of the new character even though * we actually added only one character; we only add one character * to the buffer to avoid buffer overflow, but the column position * needs adjustment by the full rendered width. The fact that the * actual buffer size and rendered width no longer match isn't * important because the difference is entirely in invisible * whitespace at the right end of the line. */ linecol_ += cwid; /* done for now */ return; } /* * We're adding something other than an ordinary space to the line, * and the new character won't fit, so we must find an appropriate * point to break the line. * * First, add the new character to the buffer - it could be * significant in how we calculate the break position. (Note that we * allocate the buffer with space for one extra character after * reaching the maximum line width, so we know we have room for this.) */ linebuf_[linepos_] = c; flagbuf_[linepos_] = cur_flags_; /* * if the underlying OS layer is doing the line wrapping, just flush * out the buffer; don't bother trying to do any line wrapping * ourselves, since this work would just be redundant with what the OS * layer has to do anyway */ if (os_line_wrap_) { /* flush the line, adding no padding after it */ flush_line(vmg_ FALSE); /* * we've completely cleared out the line buffer, so reset all of * the line buffer counters */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; /* we're done */ goto done_with_wrapping; } /* * Scan backwards, looking for a break position. Start at the current * column: we know we can fit everything up to this point on a line on * the underlying display, so this is the rightmost possible position * at which we could break the line. Keep going until we find a * breaking point or reach the left edge of the line. */ int shy, i; for (i = linepos_, shy = FALSE ; i >= 0 ; --i) { unsigned char f; unsigned char prvf; /* * There are two break modes: word-break mode and break-anywhere * mode. The modes are applied to each character, via the buffer * flags. * * In word-break mode, we can break at any ordinary space, at a * soft hyphen, just after a regular hyphen, or at any explicit * ok-to-break point; but we can't break after any character * marked as a no-break point. * * In break-anywhere mode, we can break between any two * characters, except that we can't break after any character * marked as a no-break point. */ /* get the current character's flags */ f = flagbuf_[i]; /* get the preceding character's flags */ prvf = (i > 0 ? flagbuf_[i-1] : 0); /* * if the preceding character is marked as a no-break point, we * definitely can't break here, so keep looking */ if ((prvf & VMCON_OBF_NOBREAK) != 0) continue; /* * if the preceding character is marked as an explicit ok-to-break * point, we definitely can break here */ if ((prvf & VMCON_OBF_OKBREAK) != 0) break; /* * If the current character is in a run of break-anywhere text, * then we can insert a break just before the current character. * Likewise, if the preceding character is in a run of * break-anywhere text, we can break just after the preceding * character, which is the same as breaking just before the * current character. * * Note that we must test for both cases to properly handle * boundaries between break-anywhere and word-break text. If * we're switching from word-break to break-anywhere text, the * current character will be marked as break-anywhere, so if we * only tested the previous character, we'd miss this transition. * If we're switching from break-anywhere to word-break text, the * previous character will be marked as break-anywhere, so we'd * miss the fact that we could break right here (rather than * before the previous character) if we didn't test it explicitly. */ if ((f & VMCON_OBF_BREAK_ANY) != 0 || (i > 0 && (prvf & VMCON_OBF_BREAK_ANY) != 0)) break; /* * If the preceding character is marked as a soft hyphenation * point, and we're not at the rightmost position, we can break * here with hyphenation. We can't break with hyphenation at the * last position because hyphenation requires us to actually * insert a hyphen character, and we know that at the last * position we don't have room for inserting another character. */ if (i > 0 && i < linepos_ && (prvf & VMCON_OBF_SHY) != 0) { /* note that we're breaking at a soft hyphen */ shy = TRUE; /* we can break here */ break; } /* * we can break to the left of a space (i.e., we can break before * the current character if the current character is a space) */ if (linebuf_[i] == ' ') break; /* * We can also break to the right of a space. We need to check * for this case separately from checking that the current * charatcer is a space (which breaks to the left of the space), * because we could have a no-break marker on one side of the * space but not on the other side. */ if (i > 0 && linebuf_[i-1] == ' ') break; /* * If we're to the right of a hyphen, we can break here. However, * don't break in the middle of a set of consecutive hyphens * (i.e., we don't want to break up "--" sequences). */ if (i > 0 && linebuf_[i-1] == '-' && linebuf_[i] != '-') break; } /* check to see if we found a good place to break */ if (i < 0) { /* * We didn't find a good place to break. If the underlying * console allows overrunning the line width, simply add the * character, even though it overflows; otherwise, force a break * at the line width, even though it doesn't occur at a natural * breaking point. * * In any case, don't let our buffer fill up beyond its maximum * size. */ if (!console_->allow_overrun() || linepos_ + 1 >= OS_MAXWIDTH) { /* * we didn't find any good place to break, and the console * doesn't allow us to overrun the terminal width - flush the * entire line as-is, breaking arbitrarily in the middle of a * word */ flush_line(vmg_ FALSE); /* * we've completely cleared out the line buffer, so reset all * of the line buffer counters */ linepos_ = 0; linecol_ = 0; linebuf_[0] = '\0'; is_continuation_ = FALSE; } } else { wchar_t tmpbuf[OS_MAXWIDTH]; vmcon_color_t tmpcolor[OS_MAXWIDTH]; unsigned char tmpflags[OS_MAXWIDTH]; size_t tmpchars; int nxti; /* null-terminate the line buffer */ linebuf_[linepos_] = '\0'; /* trim off leading spaces on the next line after the break */ for (nxti = i ; linebuf_[nxti] == ' ' ; ++nxti) ; /* * The next line starts after the break - save a copy. We actually * have to save a copy of the trailing material outside the buffer, * since we might have to overwrite the trailing part of the buffer * to expand tabs. */ tmpchars = wcslen(&linebuf_[nxti]); memcpy(tmpbuf, &linebuf_[nxti], tmpchars*sizeof(tmpbuf[0])); memcpy(tmpcolor, &colorbuf_[nxti], tmpchars*sizeof(tmpcolor[0])); memcpy(tmpflags, &flagbuf_[nxti], tmpchars*sizeof(tmpflags[0])); /* if we're breaking at a soft hyphen, insert a real hyphen */ if (shy) linebuf_[i++] = '-'; /* trim off trailing spaces */ for ( ; i > 0 && linebuf_[i-1] == ' ' ; --i) { /* stop if we've reached a non-breaking point */ if ((flagbuf_[i-1] & VMCON_OBF_NOBREAK) != 0) break; } /* terminate the buffer after the break point */ linebuf_[i] = '\0'; /* write out everything up to the break point */ flush_line(vmg_ TRUE); /* move the saved start of the next line into the line buffer */ memcpy(linebuf_, tmpbuf, tmpchars*sizeof(tmpbuf[0])); memcpy(colorbuf_, tmpcolor, tmpchars*sizeof(tmpcolor[0])); memcpy(flagbuf_, tmpflags, tmpchars*sizeof(tmpflags[0])); linecol_ = linepos_ = tmpchars; } done_with_wrapping: /* add the new character to buffer */ buffer_rendered(c, cflags, cwid); } /* * Write a rendered character to an output stream buffer. This is a * low-level internal routine that we call from buffer_expchar() to put * the final rendition of a character into a buffer. * * Some characters render as multiple copies of a single character; 'wid' * gives the number of copies to store. The caller is responsible for * ensuring that the rendered representation fits in the buffer and in the * available line width. */ void CVmFormatter::buffer_rendered(wchar_t c, unsigned char flags, int wid) { unsigned char flags_before; /* note whether or not we have a break before us */ flags_before = (linepos_ > 0 ? flagbuf_[linepos_-1] & VMCON_OBF_NOBREAK : 0); /* add the character the given number of times */ for ( ; wid != 0 ; --wid) { /* buffer the character */ linebuf_[linepos_] = c; flagbuf_[linepos_] = flags; colorbuf_[linepos_] = cur_color_; /* * if this isn't the last part of the character, carry forward any * no-break flag from the previous part of the character; this will * ensure that a no-break to the left of the sequence applies to * the entire sequence */ if (wid > 1) flagbuf_[linepos_] |= flags_before; /* advance one character in the buffer */ ++linepos_; /* adjust our column counter */ ++linecol_; } } /* ------------------------------------------------------------------------ */ /* * write out a UTF-8 string */ void CVmFormatter::buffer_string(VMG_ const char *txt) { /* write out each character in the string */ for ( ; utf8_ptr::s_getch(txt) != 0 ; txt += utf8_ptr::s_charsize(*txt)) buffer_char(vmg_ utf8_ptr::s_getch(txt)); } /* * write out a wide unicode string */ void CVmFormatter::buffer_wstring(VMG_ const wchar_t *txt) { /* write out each wide character */ for ( ; *txt != L'\0' ; ++txt) buffer_char(vmg_ *txt); } /* ------------------------------------------------------------------------ */ /* * Get the next wide unicode character in a UTF8-encoded string, and * update the string pointer and remaining length. Returns zero if no * more characters are available in the string. */ wchar_t CVmFormatter::next_wchar(const char **s, size_t *len) { wchar_t ret; size_t charsize; /* if there's nothing left, return a null terminator */ if (*len == 0) return 0; /* get this character */ ret = utf8_ptr::s_getch(*s); /* advance the string pointer and length counter */ charsize = utf8_ptr::s_charsize(**s); *len -= charsize; *s += charsize; /* render embedded null bytes as spaces */ if (ret == 0) ret = ' '; /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Display a string of a given length. The text is encoded as UTF-8 * characters. */ int CVmFormatter::format_text(VMG_ const char *s, size_t slen) { /* get the first character */ wchar_t c = next_wchar(&s, &slen); /* if we have anything to show, show it */ while (c != '\0') { /* * first, process the character through our built-in text-only HTML * mini-parser, if our HTML mini-parser state indicates that we're * in the midst of parsing a tag */ if (html_parse_state_ != VMCON_HPS_NORMAL || (html_in_ignore_ && c != '&' && c != '<')) { /* run our HTML parsing until we finish the tag */ c = resume_html_parsing(vmg_ c, &s, &slen); /* proceed with the next character */ continue; } /* check for special characters */ switch(c) { case 10: /* newline */ flush(vmg_ VM_NL_NEWLINE); break; case 9: /* tab - write an ordinary every-4-columns tab */ write_tab(vmg_ 0, 4); break; case 0x000B: /* \b - blank line */ write_blank_line(vmg0_); break; case 0x000F: /* capitalize next character */ capsflag_ = TRUE; nocapsflag_ = FALSE; break; case 0x000E: /* un-capitalize next character */ nocapsflag_ = TRUE; capsflag_ = FALSE; break; case '<': case '&': /* HTML markup-start character - process it */ if (html_target_ || literal_mode_) { /* * The underlying OS renderer interprets HTML mark-up * sequences, OR we're processing all text literally; in * either case, we don't need to perform any * interpretation. Simply pass through the character as * though it were any other. */ goto normal_char; } else { /* * The underlying target does not accept HTML sequences. * It appears we're at the start of an "&" entity or a tag * sequence, so parse it, remove it, and replace it (if * possible) with a text-only equivalent. */ c = parse_html_markup(vmg_ c, &s, &slen); /* go back and process the next character */ continue; } break; case 0x0015: /* our own quoted space character */ case 0x00A0: /* non-breaking space */ case 0x00AD: /* soft hyphen */ case 0xFEFF: /* non-breaking zero-width space */ case 0x2002: /* en space */ case 0x2003: /* em space */ case 0x2004: /* three-per-em space */ case 0x2005: /* four-per-em space */ case 0x2006: /* six-per-em space */ case 0x2007: /* figure space */ case 0x2008: /* punctuation space */ case 0x2009: /* thin space */ case 0x200a: /* hair space */ case 0x200b: /* zero-width space */ /* * Special Unicode characters. For HTML targets, write these * as &# sequences - this bypasses character set translation * and ensures that the HTML parser will see them as intended. */ if (html_target_) { char buf[15]; char *p; /* * it's an HTML target - render these as &# sequences; * generate the decimal representation of 'c' (in reverse * order, hence start with the terminating null byte and * the semicolon) */ p = buf + sizeof(buf) - 1; *p-- = '\0'; *p-- = ';'; /* generate the decimal representation of 'c' */ for ( ; c != 0 ; c /= 10) *p-- = (c % 10) + '0'; /* add the '&#' sequence */ *p-- = '#'; *p = '&'; /* write out the sequence */ buffer_string(vmg_ p); } else { /* for non-HTML targets, treat these as normal */ goto normal_char; } break; default: normal_char: /* normal character - write it out */ buffer_char(vmg_ c); break; } /* move on to the next character */ c = next_wchar(&s, &slen); } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Initialize the display object */ CVmConsole::CVmConsole() { /* no script file yet */ script_sp_ = 0; /* no command log file yet */ command_fp_ = 0; command_nf_ = 0; command_glob_ = 0; /* assume we'll double-space after each period */ doublespace_ = TRUE; /* presume we'll have no log stream */ log_str_ = 0; log_enabled_ = FALSE; } /* * Delete the display object */ void CVmConsole::delete_obj(VMG0_) { /* close any active script file(s) */ while (script_sp_ != 0) close_script_file(vmg0_); /* close any active command log file */ close_command_log(vmg0_); /* delete the log stream if we have one */ if (log_str_ != 0) log_str_->delete_obj(vmg0_); /* delete this object */ delete this; } /* ------------------------------------------------------------------------ */ /* * Display a string of a given byte length */ int CVmConsole::format_text(VMG_ const char *p, size_t len) { /* display the string */ disp_str_->format_text(vmg_ p, len); /* if there's a log file, write to the log file as well */ if (log_enabled_) log_str_->format_text(vmg_ p, len); /* indicate success */ return 0; } /* * Display a string on the log stream only */ int CVmConsole::format_text_to_log(VMG_ const char *p, size_t len) { /* if there's a log file, write to it; otherwise ignore the whole thing */ if (log_enabled_) log_str_->format_text(vmg_ p, len); /* indicate success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Set the text color */ void CVmConsole::set_text_color(VMG_ os_color_t fg, os_color_t bg) { /* set the color in our main display stream */ disp_str_->set_text_color(vmg_ fg, bg); } /* * Set the body color */ void CVmConsole::set_body_color(VMG_ os_color_t color) { /* set the color in the main display stream */ disp_str_->set_os_body_color(color); } /* ------------------------------------------------------------------------ */ /* * Display a blank line */ void CVmConsole::write_blank_line(VMG0_) { /* generate the newline to the standard display */ disp_str_->write_blank_line(vmg0_); /* if we're logging, generate the newline to the log file as well */ if (log_enabled_) log_str_->write_blank_line(vmg0_); } /* ------------------------------------------------------------------------ */ /* * outcaps() - sets an internal flag which makes the next letter output * a capital, whether it came in that way or not. Set the same state in * both formatters (standard and log). */ void CVmConsole::caps() { disp_str_->caps(); if (log_enabled_) log_str_->caps(); } /* * outnocaps() - sets the next letter to a miniscule, whether it came in * that way or not. */ void CVmConsole::nocaps() { disp_str_->nocaps(); if (log_enabled_) log_str_->nocaps(); } /* * obey_whitespace() - sets the obey-whitespace mode */ int CVmConsole::set_obey_whitespace(int f) { int ret; /* note the original display stream status */ ret = disp_str_->get_obey_whitespace(); /* set the stream status */ disp_str_->set_obey_whitespace(f); if (log_enabled_) log_str_->set_obey_whitespace(f); /* return the original status of the display stream */ return ret; } /* ------------------------------------------------------------------------ */ /* * Open a log file */ int CVmConsole::open_log_file(VMG_ const char *fname) { /* pass the file to the log stream, if we have one */ return log_str_ != 0 ? log_str_->open_log_file(vmg_ fname) : 1; } int CVmConsole::open_log_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc) { /* pass the file to the log stream, if we have one */ return log_str_ != 0 ? log_str_->open_log_file(vmg_ filespec, rc) : 1; } /* * Close the log file */ int CVmConsole::close_log_file(VMG0_) { /* if there's no log stream, there's obviously no file open */ if (log_str_ == 0) return 1; /* tell the log stream to close its file */ return log_str_->close_log_file(vmg0_); } #if 0 //$$$ /* * This code is currently unused. However, I'm leaving it in for now - * the algorithm takes a little thought, so it would be nicer to be able * to uncomment the existing code should we ever need it in the future. */ /* ------------------------------------------------------------------------ */ /* * Write UTF-8 text explicitly to the log file. This can be used to add * special text (such as prompt text) that would normally be suppressed * from the log file. When more mode is turned off, we don't * automatically copy text to the log file; any text that the caller * knows should be in the log file during times when more mode is turned * off can be explicitly added with this function. * * If nl is true, we'll add a newline at the end of this text. The * caller should not include any newlines in the text being displayed * here. */ void CVmConsole::write_to_logfile(VMG_ const char *txt, int nl) { /* if there's no log file, there's nothing to do */ if (logfp_ == 0) return; /* write the text in the log file character set */ write_to_file(logfp_, txt, G_cmap_to_log); /* add a newline if desired */ if (nl) { /* add a normal newline */ os_fprintz(logfp_, "\n"); /* if the logfile is an html target, write an HTML line break */ if (log_str_ != 0 && log_str_->is_html_target()) os_fprintz(logfp_, "
\n"); } /* flush the output */ osfflush(logfp_); } /* * Write text to a file in the given character set */ void CVmConsole::write_to_file(osfildef *fp, const char *txt, CCharmapToLocal *map) { size_t txtlen = strlen(txt); /* * convert the text from UTF-8 to the local character set and write the * converted text to the log file */ while (txtlen != 0) { char local_buf[128]; size_t src_bytes_used; size_t out_bytes; /* convert as much as we can (leaving room for a null terminator) */ out_bytes = map->map_utf8(local_buf, sizeof(local_buf), txt, txtlen, &src_bytes_used); /* null-terminate the result */ local_buf[out_bytes] = '\0'; /* write the converted text */ os_fprintz(fp, local_buf); /* skip the text we were able to convert */ txt += src_bytes_used; txtlen -= src_bytes_used; } } #endif /* 0 */ /* ------------------------------------------------------------------------ */ /* * Reset the MORE line counter. This should be called whenever user * input is read, since stopping to read user input makes it unnecessary * to show another MORE prompt until the point at which input was * solicited scrolls off the screen. */ void CVmConsole::reset_line_count(int clearing) { /* reset the MORE counter in the display stream */ disp_str_->reset_line_count(clearing); } /* ------------------------------------------------------------------------ */ /* * Flush the output line. We'll write to both the standard display and * the log file, as needed. */ void CVmConsole::flush(VMG_ vm_nl_type nl) { /* flush the display stream */ disp_str_->flush(vmg_ nl); /* flush the log stream, if we have an open log file */ if (log_enabled_) log_str_->flush(vmg_ nl); } /* ------------------------------------------------------------------------ */ /* * Clear our buffers */ void CVmConsole::empty_buffers(VMG0_) { /* tell the formatter to clear its buffer */ disp_str_->empty_buffers(vmg0_); /* same with the log stream, if applicable */ if (log_enabled_) log_str_->empty_buffers(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Immediately update the display */ void CVmConsole::update_display(VMG0_) { /* update the display for the main display stream */ disp_str_->update_display(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Open a script file */ int CVmConsole::open_script_file(VMG_ const char *fname, int quiet, int script_more_mode) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ fname, 0, NETF_READ, OSFTCMD, "text/plain"); } err_catch_disc { /* failed - no network file */ nf = 0; } err_end; /* open the script on the network file */ return open_script_file(vmg_ nf, 0, quiet, script_more_mode); } int CVmConsole::open_script_file(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int quiet, int script_more_mode) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open( vmg_ filespec, rc, NETF_READ, OSFTCMD, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_READ); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* open the script on the network file */ return open_script_file(vmg_ nf, filespec, quiet, script_more_mode); } int CVmConsole::open_script_file(VMG_ CVmNetFile *nf, const vm_val_t *filespec, int quiet, int script_more_mode) { int evt; char buf[50]; /* if the network file open failed, return an error */ if (nf == 0) return 1; /* open the local file */ osfildef *fp = osfoprt(nf->lclfname, OSFTCMD); /* if that failed, silently ignore the request */ if (fp == 0) { /* abandon the network file descriptor and return failure */ nf->abandon(vmg0_); return 1; } /* read the first line to see if it looks like an event script */ if (osfgets(buf, sizeof(buf), fp) != 0 && strcmp(buf, "\n") == 0) { /* remember that it's an event script */ evt = TRUE; } else { /* * it's not an event script, so it must be a regular command-line * script - rewind it so we read the first line again as a regular * input line */ evt = FALSE; osfseek(fp, 0, OSFSK_SET); } /* if there's an enclosing script, inherit its modes */ if (script_sp_ != 0) { /* * if the enclosing script is quiet, force the nested script to be * quiet as well */ if (script_sp_->quiet) quiet = TRUE; /* * if the enclosing script is nonstop, force the nested script to * be nonstop as well */ if (!script_sp_->more_mode) script_more_mode = FALSE; } /* push the new script file onto the stack */ script_sp_ = new script_stack_entry( vmg_ script_sp_, set_more_state(script_more_mode), nf, fp, filespec, script_more_mode, quiet, evt); /* turn on NONSTOP mode in the OS layer if applicable */ if (!script_more_mode) os_nonstop_mode(TRUE); /* success */ return 0; } /* * Close the current script file */ int CVmConsole::close_script_file(VMG0_) { script_stack_entry *e; /* if we have a file, close it */ if ((e = script_sp_) != 0) { int ret; /* close the file */ osfcls(e->fp); err_try { /* close the network file */ e->netfile->close(vmg0_); } err_catch_disc { /* * Ignore any error - since we're reading the file, the chances * of anything going wrong are pretty minimal anyway; but even * if an error did occur, our interface just isn't set up to do * anything meaningful with it. */ } err_end; /* pop the stack */ script_sp_ = e->enc; /* restore the enclosing level's MORE mode */ os_nonstop_mode(!e->old_more_mode); /* * return the MORE mode in effect before we started reading the * script file */ ret = e->old_more_mode; /* delete the stack level */ e->delobj(vmg0_); /* return the result */ return ret; } else { /* * there's no script file - just return the current MORE mode, * since we're not making any changes */ return is_more_mode(); } } /* ------------------------------------------------------------------------ */ /* * Script stack element */ script_stack_entry::script_stack_entry( VMG_ script_stack_entry *encp, int old_more, class CVmNetFile *netfile, osfildef *outfp, const vm_val_t *filespec, int new_more, int is_quiet, int is_event_script) { /* remember the stack level settings */ this->enc = encp; this->old_more_mode = old_more; this->netfile = netfile; this->fp = outfp; this->more_mode = new_more; this->quiet = is_quiet; this->event_script = is_event_script; /* if we have a file spec, create a global for it for gc protection */ this->filespec = 0; if (filespec != 0) { this->filespec = G_obj_table->create_global_var(); this->filespec->val = *filespec; } } void script_stack_entry::delobj(VMG0_) { /* we're done with our file spec global, if we had one */ if (filespec != 0) G_obj_table->delete_global_var(filespec); /* delete myself */ delete this; } /* ------------------------------------------------------------------------ */ /* * Open a command log file */ int CVmConsole::open_command_log(VMG_ const char *fname, int event_script) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ fname, 0, NETF_NEW, OSFTCMD, "text/plain"); } err_catch_disc { /* failed - no network file */ nf = 0; } err_end; /* create the command log */ return open_command_log(vmg_ nf, event_script); } int CVmConsole::open_command_log(VMG_ const vm_val_t *filespec, const struct vm_rcdesc *rc, int event_script) { CVmNetFile *nf = 0; err_try { /* create the network file descriptor */ nf = CVmNetFile::open(vmg_ filespec, rc, NETF_NEW, OSFTCMD, "text/plain"); /* validate file safety */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); } err_catch_disc { /* if we got a file, it must be a safety exception - rethrow it */ if (nf != 0) { nf->abandon(vmg0_); err_rethrow(); } } err_end; /* create the command log */ int err = open_command_log(vmg_ nf, event_script); /* if that succeeded, save the filespec in our VM global */ if (!err) { /* create our VM global if we don't have one already */ if (command_glob_ == 0) command_glob_ = G_obj_table->create_global_var(); /* remember the file spec */ command_glob_->val = *filespec; } /* return the result */ return err; } int CVmConsole::open_command_log(VMG_ CVmNetFile *nf, int event_script) { /* close any existing command log file */ close_command_log(vmg0_); /* if the network file open failed, return failure */ if (nf == 0) return 1; /* open the file */ osfildef *fp = osfopwt(nf->lclfname, OSFTCMD); if (fp != 0) { /* success - remember the file handle and net descriptor */ command_nf_ = nf; command_fp_ = new CVmFileSource(fp); } else { /* failed - abandon the network file */ nf->abandon(vmg0_); } /* note the type */ command_eventscript_ = event_script; /* if it's an event script, write the file type tag */ if (event_script && command_fp_ != 0) { command_fp_->writez("\n"); command_fp_->flush(); } /* return success if we successfully opened the file */ return (command_fp_ == 0); } /* * close the active command log file */ int CVmConsole::close_command_log(VMG0_) { /* if there's a command log file, close it */ if (command_fp_ != 0) { /* close the file */ delete command_fp_; /* close the network file */ err_try { command_nf_->close(vmg0_); } err_catch_disc { /* * ignore any errors - our interface doesn't give us any * meaningful way to return error information */ } err_end; /* forget the file */ command_fp_ = 0; } /* clear out our file spec global, if we have one */ if (command_glob_ != 0) command_glob_->val.set_nil(); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Read a line of input from the console. Fills in the buffer with a * null-terminated string in the UTF-8 character set. Returns zero on * success, non-zero on end-of-file. */ int CVmConsole::read_line(VMG_ char *buf, size_t buflen, int bypass_script) { /* cancel any previous interrupted input */ read_line_cancel(vmg_ TRUE); try_again: /* use the timeout version, with no timeout specified */ switch(read_line_timeout(vmg_ buf, buflen, 0, FALSE, bypass_script)) { case OS_EVT_LINE: /* success */ return 0; case VMCON_EVT_END_QUIET_SCRIPT: /* * end of script - we have no way to communicate this result back * to our caller, so simply ignore the result and ask for another * line */ goto try_again; default: /* anything else is an error */ return 1; } } /* ------------------------------------------------------------------------ */ /* * translate an OS_EVT_XXX code to an event script file tag */ static const char *evt_to_tag(int evt) { switch (evt) { case OS_EVT_KEY: return "key"; case OS_EVT_TIMEOUT: return "timeout"; case OS_EVT_HREF: return "href"; case OS_EVT_NOTIMEOUT: return "notimeout"; case OS_EVT_EOF: return "eof"; case OS_EVT_LINE: return "line"; case OS_EVT_COMMAND: return "command"; case VMCON_EVT_END_QUIET_SCRIPT: return "endqs"; case VMCON_EVT_DIALOG: return "dialog"; case VMCON_EVT_FILE: return "file"; default: return ""; } } /* ------------------------------------------------------------------------ */ /* * Log an event to the output script. The parameter is in the UI character * set. */ int CVmConsole::log_event(VMG_ int evt, const char *param, size_t paramlen, int param_is_utf8) { /* translate the event code to a tag, and log the event */ log_event(vmg_ evt_to_tag(evt), param, paramlen, param_is_utf8); /* return the event type code */ return evt; } void CVmConsole::log_event(VMG_ const char *tag, const char *param, size_t paramlen, int param_is_utf8) { /* if there's a script file, log the event */ if (command_fp_ != 0) { /* if the tag has < > delimiters, remove them */ size_t taglen = strlen(tag); if (tag[0] == '<') ++tag, --taglen; if (taglen != 0 && tag[taglen-1] == '>') --taglen; /* write the event in the proper format for the script type */ if (command_eventscript_) { /* * It's an event script, so we write all event types. Check * for certain special parameter translations. */ if (taglen == 3 && memicmp(tag, "key", 3) == 0) { /* * key event - for characters that would look like * whitespace characters if we wrote them as-is, translate * to [xxx] key names */ if (param != 0 && paramlen == 1) { switch (param[0]) { case '\n': param = "[enter]"; paramlen = 7; break; case '\t': param = "[tab]"; paramlen = 5; break; case ' ': param = "[space]"; paramlen = 7; break; } } } /* * if the event doesn't have parameters, ignore any parameter * provided by the caller */ if ((taglen == 9 && memicmp(tag, "notimeout", 9) == 0) || (taglen == 3 && memicmp(tag, "eof", 3) == 0)) param = 0; /* if we have a non-empty tag, write the event */ if (taglen != 0) { /* write the tag, in the local character set */ G_cmap_to_ui->write_file(command_fp_, "<", 1); G_cmap_to_ui->write_file(command_fp_, tag, strlen(tag)); G_cmap_to_ui->write_file(command_fp_, ">", 1); /* add the parameter, if present */ if (param != 0) { if (param_is_utf8) G_cmap_to_ui->write_file(command_fp_, param, paramlen); else command_fp_->write(param, paramlen); } /* add the newline */ G_cmap_to_ui->write_file(command_fp_, "\n", 1); /* flush the output */ command_fp_->flush(); } } else { /* * It's a plain old command-line script. If the event is an * input-line event, record it; otherwise leave it out, as this * script file format can't represent any other event types. */ if (taglen == 4 && memicmp(tag, "line", 4) == 0 && param != 0) { /* write the ">" prefix */ G_cmap_to_ui->write_file(command_fp_, ">", 1); /* add the command line */ if (param_is_utf8) G_cmap_to_ui->write_file(command_fp_, param, paramlen); else command_fp_->write(param, paramlen); /* add the newline */ G_cmap_to_ui->write_file(command_fp_, "\n", 1); /* flush the output */ command_fp_->flush(); } } } } /* ------------------------------------------------------------------------ */ /* * Static variables for input state. We keep these statically, because we * might need to use the values across a series of read_line_timeout calls * if timeouts occur. */ /* original 'more' mode, before input began */ static int S_old_more_mode; /* flag: input is pending from an interrupted read_line_timeout invocation */ static int S_read_in_progress; /* local buffer for reading input lines */ static char S_read_buf[256]; /* * Read a line of input from the console, with an optional timeout value. */ int CVmConsole::read_line_timeout(VMG_ char *buf, size_t buflen, unsigned long timeout, int use_timeout, int bypass_script) { /* no event yet */ int evt = OS_EVT_NONE; /* we haven't received any script input yet */ int got_script_input = FALSE; /* * presume we won't echo the text to the display; in most cases, it * will be echoed to the display in the course of reading it from * the keyboard */ int echo_text = FALSE; /* remember the initial MORE mode */ S_old_more_mode = is_more_mode(); /* * If we're not resuming an interrupted read already in progress, * initialize some display settings. */ if (!S_read_in_progress) { /* * Turn off MORE mode if it's on - we don't want a MORE prompt * showing up in the midst of user input. */ S_old_more_mode = set_more_state(FALSE); /* * flush the output; don't start a new line, since we might have * displayed a prompt that is to be on the same line with the user * input */ flush_all(vmg_ VM_NL_INPUT); } /* * if there's a script file, read from it, unless the caller has * specifically asked us to bypass it */ if (script_sp_ != 0 && !bypass_script) { read_script: /* note whether we're in quiet mode */ int was_quiet = script_sp_->quiet; /* try reading a line from the script file */ if (read_line_from_script(S_read_buf, sizeof(S_read_buf), &evt)) { /* we successfully read input from the script */ got_script_input = TRUE; /* * if we're not in quiet mode, make a note to echo the text to * the display */ if (!script_sp_->quiet) echo_text = TRUE; } else { /* * End of script file - return to reading from the enclosing * level (i.e., the enclosing script, or the keyboard if this * is the outermost script). The return value from * close_script_file() is the MORE mode that was in effect * before we started reading the script file; we'll use this * when we restore the enclosing MORE mode so that we restore * the pre-script MORE mode when we return. */ S_old_more_mode = close_script_file(vmg0_); /* note the new 'quiet' mode */ int is_quiet = (script_sp_ != 0 && script_sp_->quiet); /* * if we're still reading from a script (which means we closed * the old script and popped out to an enclosing script), and * the 'quiet' mode hasn't changed, simply go back for another * read */ if (script_sp_ != 0 && is_quiet == was_quiet) goto read_script; /* * temporarily turn off MORE mode, in case we read from the * keyboard */ set_more_state(FALSE); /* flush any output we generated while reading the script */ flush(vmg_ VM_NL_NONE); /* * If we were in quiet mode but no longer are, let the caller * know we've finished reading a script, so that the caller can * set up the display properly for reading from the keyboard. * * If we weren't in quiet mode, we'll simply proceed to the * normal keyboard reading; when not in quiet mode, no special * display fixup is needed. */ if (was_quiet && !is_quiet) { /* return to the old MORE mode */ set_more_state(S_old_more_mode); /* add a blank line to the log file, if necessary */ if (log_enabled_) log_str_->print_to_os("\n"); /* note in the streams that we've read an input line */ disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); /* * generate a synthetic "end of script" event to let the * caller know we're switching back to regular keyboard * reading */ return log_event(vmg_ VMCON_EVT_END_QUIET_SCRIPT); } /* * Note that we do not have an event yet - we've merely closed * the script file, and now we're going to continue by reading * a line from the keyboard instead. The call to * close_script_file() above will have left script_sp_ == 0, so * we'll shortly read an event from the keyboard. Thus 'evt' * is still not set to any value, because we do not yet have an * event - this is intentional. */ } } /* * if we're not reading from a scripot, reset the MORE line counter, * since we're reading user input at the current point and shouldn't * pause for a MORE prompt until the text we're reading has scrolled * off the screen */ if (script_sp_ == 0) reset_line_count(FALSE); /* reading is now in progress */ S_read_in_progress = TRUE; /* if we didn't get input from a script, read from the keyboard */ if (!got_script_input) { /* * If we're in network mode, return EOF to indicate that no console * input is available. Network programs can only call this routine * to read script input, and aren't allowed to read from the * regular keyboard. */ if (G_net_config != 0) { read_line_done(vmg0_); return OS_EVT_EOF; } /* read a line from the keyboard */ evt = os_gets_timeout((uchar *)S_read_buf, sizeof(S_read_buf), timeout, use_timeout); /* * If that failed because timeout is not supported on this * platform, and the caller didn't actually want to use a timeout, * try again with an ordinary os_gets(). If they wanted to use a * timeout, simply return the NOTIMEOUT indication to our caller. */ if (evt == OS_EVT_NOTIMEOUT && !use_timeout) { /* perform an ordinary untimed input */ if (os_gets((uchar *)S_read_buf, sizeof(S_read_buf)) != 0) { /* success */ evt = OS_EVT_LINE; } else { /* error reading input */ evt = OS_EVT_EOF; } } /* * If we actually read a line, notify the display stream that we * read text from the console - it might need to make some * internal bookkeeping adjustments to account for the fact that * we moved the write position around on the display. * * Don't note the input if we timed out, since we haven't finished * reading the line yet in this case. */ if (evt == OS_EVT_LINE) { disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); } } /* if we got an error, return it */ if (evt == OS_EVT_EOF) { set_more_state(S_old_more_mode); read_line_done(vmg0_); return log_event(vmg_ evt); } /* * Convert the text from the local UI character set to UTF-8. Reserve * space in the output buffer for the null terminator. */ char *outp = buf; size_t outlen = buflen - 1; G_cmap_from_ui->map(&outp, &outlen, S_read_buf, strlen(S_read_buf)); /* add the null terminator */ *outp = '\0'; /* * If we need to echo the text (because we read it from a script file), * do so now. Never echo text in the network configuration, since we * don't use the local console UI in this mode. */ if (echo_text && G_net_config == 0) { /* show the text */ format_text(vmg_ buf); /* add a newline */ format_text(vmg_ "\n"); } /* if we finished reading the line, do our line-finishing work */ if (evt == OS_EVT_LINE) read_line_done(vmg0_); /* * Log and return the event. Note that we log events in the UI * character set, so we want to simply use the original, untranslated * input buffer. */ return log_event(vmg_ evt, S_read_buf, strlen(S_read_buf), FALSE); } /* * Cancel an interrupted input. */ void CVmConsole::read_line_cancel(VMG_ int reset) { /* reset the underling OS layer */ os_gets_cancel(reset); /* do our line-ending work */ read_line_done(vmg0_); } /* * Perform line-ending work. This is used when we finish reading a line * in read_line_timeout(), or when we cancel an interrupted line, thus * finishing the line, in read_line_cancel(). */ void CVmConsole::read_line_done(VMG0_) { /* if we have a line in progress, finish it off */ if (S_read_in_progress) { /* set the original 'more' mode */ set_more_state(S_old_more_mode); /* * Write the input line, followed by a newline, to the log file. * Note that the text is still in the local character set, so we * can write it directly to the log file. * * If we're reading from a script file in "echo" mode, skip this. * When reading from a script file in "echo" mode, we will manually * copy the input commands to the main console, which will * automatically copy to the main log file. If we're in quiet * scripting mode, though, we won't do that, so we do need to * capture the input explicitly here. */ if (log_enabled_ && (script_sp_ == 0 || script_sp_->quiet)) { log_str_->print_to_os(S_read_buf); log_str_->print_to_os("\n"); } /* note in the streams that we've read an input line */ disp_str_->note_input_line(); if (log_str_ != 0) log_str_->note_input_line(); /* clear the in-progress flag */ S_read_in_progress = FALSE; } } /* ------------------------------------------------------------------------ */ /* * Read an input event from the script file. If we're reading an event * script file, we'll read the next event and return TRUE; if we're not * reading a script file, or the script file is a command-line script * rather than an event script, we'll simply return FALSE. * * If the event takes a parameter, we'll read the parameter into 'buf'. * The value is returned in the local character set, so the caller will * need to translate it to UTF-8. * * If 'filter' is non-null, we'll only return events of the types in the * filter list. */ int CVmConsole::read_event_script(VMG_ int *evt, char *buf, size_t buflen, const int *filter, int filter_cnt, unsigned long *attrs) { /* * if we're not reading a script, or it's not an event script, skip * this */ if (script_sp_ == 0 || !script_sp_->event_script) return FALSE; /* get the script file */ osfildef *fp = script_sp_->fp; /* keep going until we find something */ for (;;) { /* read the next event */ if (!read_script_event_type(evt, attrs)) { /* end of the script - close it */ set_more_state(close_script_file(vmg0_)); /* if there's no more script file, there's no event */ if (script_sp_ == 0) return FALSE; /* go back for the next event */ fp = script_sp_->fp; continue; } /* if it's not in the filter list, skip it */ if (filter != 0) { int i, found; /* look for a match in our filter list */ for (i = 0, found = FALSE ; i < filter_cnt ; ++i) { if (filter[i] == *evt) { found = TRUE; break; } } /* if we didn't find it, skip this line */ if (!found) { skip_script_line(fp); continue; } } /* if there's a buffer, read the rest of the line */ if (buf != 0) { /* read the parameter into the buffer, and return the result */ if (!read_script_param(buf, buflen, fp)) return FALSE; /* if this is an OS_EVT_KEY event, translate special keys */ if (*evt == OS_EVT_KEY) { if (strcmp(buf, "[enter]") == 0) strcpy(buf, "\n"); else if (strcmp(buf, "[tab]") == 0) strcpy(buf, "\t"); else if (strcmp(buf, "[space]") == 0) strcpy(buf, " "); } } else { /* no result buffer - just skip anything left on the line */ skip_script_line(fp); } /* success */ return TRUE; } } /* * Read a or attribute token from a script file. Returns the * character after the end of the token. */ static int read_script_token(char *buf, size_t buflen, osfildef *fp) { char *p; int c; /* skip leading whitespace */ for (c = osfgetc(fp) ; isspace(c) ; c = osfgetc(fp)) ; /* read from the file until we reach the end of the token */ for (p = buf ; p < buf + buflen - 1 && c != '>' && !isspace(c) && c != '\n' && c != '\r' && c != EOF ; ) { /* store this character */ *p++ = (char)c; /* get the next one */ c = osfgetc(fp); } /* null-terminate the token */ *p = '\0'; /* return the character that ended the token */ return c; } /* * Read the next event type from current event script file. This leaves * the file positioned at the parameter data for the event, if any. * Returns FALSE if we reach end of file without finding an event. */ int CVmConsole::read_script_event_type(int *evt, unsigned long *attrs) { /* clear the caller's attribute flags, if provided */ if (attrs != 0) *attrs = 0; /* if there's no script, there's no event */ if (script_sp_ == 0) return FALSE; /* get the file */ osfildef *fp = script_sp_->fp; /* if it's a command-line script, there are only line input events */ if (!script_sp_->event_script) { /* keep going until we find an input line */ for (;;) { /* read the first character of the line */ int c = osfgetc(fp); if (c == '>') { /* we found a line input event */ *evt = OS_EVT_LINE; return TRUE; } else if (c == EOF) { /* end of file - give up */ return FALSE; } else { /* * anything else is just a comment line - just skip it and * keep looking */ skip_script_line(fp); } } } /* keep going until we find an event tag */ for (;;) { int c; char tag[32]; static const struct { const char *tag; int evt; } *tp, tags[] = { { "key", OS_EVT_KEY }, { "timeout", OS_EVT_TIMEOUT }, { "notimeout", OS_EVT_NOTIMEOUT }, { "eof", OS_EVT_EOF }, { "line", OS_EVT_LINE }, { "command", OS_EVT_COMMAND }, { "endqs", VMCON_EVT_END_QUIET_SCRIPT }, { "dialog", VMCON_EVT_DIALOG }, { "file", VMCON_EVT_FILE }, { 0, 0 } }; /* check the start of the line to make sure it's an event */ c = osfgetc(fp); /* if at EOF, return failure */ if (c == EOF) return FALSE; /* if it's not an event code line, skip the line */ if (c != '<') { skip_script_line(fp); continue; } /* read the event type (up to the '>') */ c = read_script_token(tag, sizeof(tag), fp); /* check for attributes */ while (isspace(c)) { char attr[32]; static const struct { const char *name; unsigned long flag; } *ap, attrlist[] = { { "overwrite", VMCON_EVTATTR_OVERWRITE }, { 0, 0 } }; /* read the attribute name */ c = read_script_token(attr, sizeof(attr), fp); /* if the name is empty, stop */ if (attr[0] == '\0') break; /* look up the token */ for (ap = attrlist ; ap->name != 0 ; ++ap) { /* check for a match */ if (stricmp(attr, ap->name) == 0) { /* if the caller wants the flag, set it */ if (attrs != 0) *attrs |= ap->flag; /* no need to look any further */ break; } } /* if we're at the '>' or at end of line or file, stop */ if (c == '>' || c == '\n' || c == '\r' || c == EOF) break; } /* if it's not a well-formed tag, ignore it */ if (c != '>') { skip_script_line(fp); continue; } /* look up the tag */ for (tp = tags ; tp->tag != 0 ; ++tp) { /* check for a match to this tag name */ if (stricmp(tp->tag, tag) == 0) { /* got it - return the event type */ *evt = tp->evt; return TRUE; } } /* we don't recognize the tag name; skip the line and keep looking */ skip_script_line(fp); } } /* * Skip to the next script line */ void CVmConsole::skip_script_line(osfildef *fp) { int c; /* read until we find the end of the current line */ for (c = osfgetc(fp) ; c != EOF && c != '\n' && c != '\r' ; c = osfgetc(fp)) ; } /* * read the rest of the current script line into the given buffer */ int CVmConsole::read_script_param(char *buf, size_t buflen, osfildef *fp) { /* ignore zero-size buffer requests */ if (buflen == 0) return FALSE; /* read characters until we run out of buffer or reach a newline */ for (;;) { /* get the next character */ int c = osfgetc(fp); /* if it's a newline or end of file, we're done */ if (c == '\n' || c == EOF) { /* null-terminate the buffer */ *buf = '\0'; /* indicate success */ return TRUE; } /* * if there's room in the buffer, add the character - always leave * one byte for the null terminator */ if (buflen > 1) { *buf++ = (char)c; --buflen; } } } /* * Read a line of text from the script file, if there is one. Returns TRUE * on success, FALSE if we reach the end of the script file or encounter * any other error. */ int CVmConsole::read_line_from_script(char *buf, size_t buflen, int *evt) { /* if there's no script file, return failure */ if (script_sp_ == 0) return FALSE; /* get the file from the script stack */ osfildef *fp = script_sp_->fp; /* keep going until we find a line that we like */ for (;;) { /* read the script according to its type ('event' or 'line input') */ if (script_sp_->event_script) { /* read to the next event */ if (!read_script_event_type(evt, 0)) return FALSE; /* check the event code */ switch (*evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* * it's one of our line input events - read the line (or * partial line, in the case of TIMEOUT) */ return read_script_param(buf, buflen, fp); default: /* * it's not our type of event - skip the rest of the line * and keep looking */ skip_script_line(fp); break; } } else { /* * We have a basic line-input script rather than an event * script. Each input line starts with a '>'; everything else * is a comment. * * Read the first character on the line. If it's not a * newline, there's more text on the same line, so read the * rest and determine what to do. */ int c = osfgetc(fp); if (c == '>') { /* it's a command line - read it */ *evt = OS_EVT_LINE; return read_script_param(buf, buflen, fp); } else if (c == EOF) { /* end of file */ return FALSE; } else if (c == '\n' || c == '\r') { /* blank line - continue on to the next line */ } else { /* it's not a command line - just skip it and keep looking */ skip_script_line(fp); } } } } /* ------------------------------------------------------------------------ */ /* * Main System Console */ /* * create */ CVmConsoleMain::CVmConsoleMain(VMG0_) { /* create the system banner manager */ banner_manager_ = new CVmBannerManager(); /* create the log console manager */ log_console_manager_ = new CVmLogConsoleManager(); /* create and initialize our display stream */ main_disp_str_ = new CVmFormatterMain(this, 256); main_disp_str_->init(); /* initially send text to the main display stream */ disp_str_ = main_disp_str_; /* * Create our log stream. The main console always has a log stream, * even when it's not in use, so that we can keep the log stream's * state synchronized with the display stream in preparation for * activation. */ log_str_ = new CVmFormatterLog(this, 80); /* * use the default log file character mapper - on some systems, files * don't use the same character set as the display */ log_str_->set_charmap(G_cmap_to_log); /* initialize the log stream */ log_str_->init(); /* * the log stream is initially enabled (this is separate from the log * file being opened; it merely indicates that we send output * operations to the log stream for processing) */ log_enabled_ = TRUE; /* we don't have a statusline formatter until asked for one */ statline_str_ = 0; /* reset statics */ S_read_in_progress = FALSE; S_read_buf[0] = '\0'; } /* * delete */ void CVmConsoleMain::delete_obj(VMG0_) { /* delete the system banner manager */ banner_manager_->delete_obj(vmg0_); /* delete the system log console manager */ log_console_manager_->delete_obj(vmg0_); /* delete the display stream */ main_disp_str_->delete_obj(vmg0_); /* delete the statusline stream, if we have one */ if (statline_str_ != 0) statline_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } /* * Clear the window */ void CVmConsoleMain::clear_window(VMG0_) { /* flush and empty our output buffer */ flush(vmg_ VM_NL_NONE); empty_buffers(vmg0_); /* clear the main window */ oscls(); /* reset the MORE line counter in the display stream */ disp_str_->reset_line_count(TRUE); } /* * Set statusline mode */ void CVmConsoleMain::set_statusline_mode(VMG_ int mode) { CVmFormatterDisp *str; /* * if we're switching into statusline mode, and we don't have a * statusline stream yet, create one */ if (mode && statline_str_ == 0) { /* create and initialize the statusline stream */ statline_str_ = new CVmFormatterStatline(this); statline_str_->init(); } /* get the stream selected by the new mode */ if (mode) str = statline_str_; else str = main_disp_str_; /* if this is already the active stream, we have nothing more to do */ if (str == disp_str_) return; /* make the new stream current */ disp_str_ = str; /* * check which mode we're switching to, so we can do some extra work * specific to each mode */ if (mode) { /* * we're switching to the status line, so disable the log stream - * statusline text is never sent to the log, since the log reflects * only what was displayed in the main text area */ log_enabled_ = FALSE; } else { /* * we're switching back to the main stream, so flush the statusline * so we're sure the statusline text is displayed */ /* end the line */ statline_str_->format_text(vmg_ "\n", 1); /* flush output */ statline_str_->flush(vmg_ VM_NL_NONE); /* re-enable the log stream, if we have one */ if (log_str_ != 0) log_enabled_ = TRUE; } /* switch at the OS layer */ os_status(mode); } /* * Flush everything */ void CVmConsoleMain::flush_all(VMG_ vm_nl_type nl) { /* flush our primary console */ flush(vmg_ nl); /* * Flush each banner we're controlling. Note that we explicitly flush * the banners with newline mode 'NONE', regardless of the newline mode * passed in by the caller: the caller's mode is for the primary * console, but for the banners we just want to make sure they're * flushed out normally, since whatever we're doing in the primary * console that requires flushing doesn't concern the banners. */ banner_manager_->flush_all(vmg_ VM_NL_NONE); } /* ------------------------------------------------------------------------ */ /* * Handle manager */ /* initialize */ CVmHandleManager::CVmHandleManager() { size_t i; /* allocate an initial array of handle slots */ handles_max_ = 32; handles_ = (void **)t3malloc(handles_max_ * sizeof(*handles_)); /* all slots are initially empty */ for (i = 0 ; i < handles_max_ ; ++i) handles_[i] = 0; } /* delete the object - this is the public destructor interface */ void CVmHandleManager::delete_obj(VMG0_) { size_t i; /* * Delete each remaining object. Note that we need to call the virtual * delete_handle_object routine, so we must do this before reaching the * destructor (once in the base class destructor, we no longer have * access to the subclass virtuals). */ for (i = 0 ; i < handles_max_ ; ++i) { /* if this banner is still valid, delete it */ if (handles_[i] != 0) delete_handle_object(vmg_ i + 1, handles_[i]); } /* delete the object */ delete this; } /* destructor */ CVmHandleManager::~CVmHandleManager() { /* delete the handle pointer array */ t3free(handles_); } /* * Allocate a new handle */ int CVmHandleManager::alloc_handle(void *item) { size_t slot; /* scan for a free slot */ for (slot = 0 ; slot < handles_max_ ; ++slot) { /* if this one is free, use it */ if (handles_[slot] == 0) break; } /* if we didn't find a free slot, extend the array */ if (slot == handles_max_) { size_t i; /* allocate a larger array */ handles_max_ += 32; handles_ = (void **) t3realloc(handles_, handles_max_ * sizeof(*handles_)); /* clear out the newly-allocated slots */ for (i = slot ; i < handles_max_ ; ++i) handles_[i] = 0; } /* store the new item in our pointer array */ handles_[slot] = item; /* * convert the slot number to a handle by adjusting it to a 1-based * index, and return the result */ return slot + 1; } /* ------------------------------------------------------------------------ */ /* * Banner manager */ /* * Create a banner */ int CVmBannerManager::create_banner(VMG_ int parent_id, int where, int other_id, int wintype, int align, int siz, int siz_units, unsigned long style) { void *handle; void *parent_handle; void *other_handle; CVmConsoleBanner *item; /* get the parent handle, if provided */ parent_handle = get_os_handle(parent_id); /* get the 'other' handle, if we need it for the 'where' */ switch(where) { case OS_BANNER_BEFORE: case OS_BANNER_AFTER: /* retrieve the handle for the other_id */ other_handle = get_os_handle(other_id); break; default: /* we don't need 'other' for other 'where' modes */ other_handle = 0; break; } /* try creating the OS-level banner window */ handle = os_banner_create(parent_handle, where, other_handle, wintype, align, siz, siz_units, style); /* if we couldn't create the OS-level window, return failure */ if (handle == 0) return 0; /* create the new console */ item = new CVmConsoleBanner(handle, wintype, style); /* allocate a handle for the new banner, and return the handle */ return alloc_handle(item); } /* * Delete or orphan a banner window. Deleting and orphaning both sever * all ties from the banner manager (and thus from the T3 program) to the * banner. Deleting a banner actually gets deletes it at the OS level; * orphaning the banner severs our ties, but hands the banner over to the * OS to do with as it pleases. On some implementations, the OS will * continue to display the banner after it's orphaned to allow the final * display configuration to remain visible even after the program has * terminated. */ void CVmBannerManager::delete_or_orphan_banner(VMG_ int banner, int orphan) { CVmConsoleBanner *item; void *handle; /* if the banner is invalid, ignore the request */ if ((item = (CVmConsoleBanner *)get_object(banner)) == 0) return; /* get the OS-level banner handle */ handle = item->get_os_handle(); /* delete the banner item */ item->delete_obj(vmg0_); /* clear the slot */ clear_handle(banner); /* delete the OS-level banner */ if (orphan) os_banner_orphan(handle); else os_banner_delete(handle); } /* * Get the OS-level handle for the given banner */ void *CVmBannerManager::get_os_handle(int banner) { CVmConsoleBanner *item; /* if the banner is invalid, return failure */ if ((item = (CVmConsoleBanner *)get_object(banner)) == 0) return 0; /* return the handle from the slot */ return item->get_os_handle(); } /* * Flush all banners */ void CVmBannerManager::flush_all(VMG_ vm_nl_type nl) { size_t slot; /* flush each banner */ for (slot = 0 ; slot < handles_max_ ; ++slot) { /* if this slot has a valid banner, flush it */ if (handles_[slot] != 0) ((CVmConsoleBanner *)handles_[slot])->flush(vmg_ nl); } } /* ------------------------------------------------------------------------ */ /* * Banner Window Console */ CVmConsoleBanner::CVmConsoleBanner(void *banner_handle, int win_type, unsigned long style) { CVmFormatterBanner *str; os_banner_info_t info; int obey_whitespace = FALSE; int literal_mode = FALSE; /* remember our OS-level banner handle */ banner_ = banner_handle; /* get osifc-level information on the banner */ if (!os_banner_getinfo(banner_, &info)) info.os_line_wrap = FALSE; /* * If it's a text grid window, don't do any line wrapping. Text grids * simply don't have any line wrapping, so we don't want to impose any * at the formatter level. Set the formatter to "os line wrap" mode, * to indicate that the formatter doesn't do wrapping - even though * the underlying OS banner window won't do any wrapping either, the * lack of line wrapping counts as OS handling of line wrapping. */ if (win_type == OS_BANNER_TYPE_TEXTGRID) { /* do not wrap lines in the formatter */ info.os_line_wrap = TRUE; /* use literal mode, and obey whitespace literally */ literal_mode = TRUE; obey_whitespace = TRUE; } /* create and initialize our display stream */ disp_str_ = str = new CVmFormatterBanner(banner_handle, this, win_type, style); str->init_banner(info.os_line_wrap, obey_whitespace, literal_mode); /* remember our window type */ win_type_ = win_type; } /* * Deletion */ void CVmConsoleBanner::delete_obj(VMG0_) { /* delete our display stream */ disp_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } /* * Clear the banner window */ void CVmConsoleBanner::clear_window(VMG0_) { /* flush and empty our output buffer */ flush(vmg_ VM_NL_NONE); empty_buffers(vmg0_); /* clear our underlying system banner */ os_banner_clear(banner_); /* tell our display stream to zero its line counter */ disp_str_->reset_line_count(TRUE); } /* * Get banner information */ int CVmConsoleBanner::get_banner_info(os_banner_info_t *info) { int ret; /* get the OS-level information */ ret = os_banner_getinfo(banner_, info); /* make some adjustments if we got valid information back */ if (ret) { /* * check the window type for further adjustments we might need to * make to the data returned from the OS layer */ switch(win_type_) { case OS_BANNER_TYPE_TEXTGRID: /* * text grids don't support alignment, even if the * underlying OS banner says we do, because we simply don't * support (or any other HTML markups) in a text grid * window */ info->style &= ~OS_BANNER_STYLE_TAB_ALIGN; break; default: /* other types don't require any adjustments */ break; } } /* return the success indication */ return ret; } /* ------------------------------------------------------------------------ */ /* * Log file console manager */ /* * create a log console */ int CVmLogConsoleManager::create_log_console( VMG_ CVmNetFile *nf, osfildef *fp, CCharmapToLocal *cmap, int width) { CVmConsoleLog *con; /* create the new console */ con = new CVmConsoleLog(vmg_ nf, fp, cmap, width); /* allocate a handle for the new console and return the handle */ return alloc_handle(con); } /* * delete log a console */ void CVmLogConsoleManager::delete_log_console(VMG_ int handle) { CVmConsoleLog *con; /* if the handle is invalid, ignore the request */ if ((con = (CVmConsoleLog *)get_object(handle)) == 0) return; /* delete the console */ con->delete_obj(vmg0_); /* clear the slot */ clear_handle(handle); } /* ------------------------------------------------------------------------ */ /* * Log file console */ CVmConsoleLog::CVmConsoleLog(VMG_ CVmNetFile *nf, osfildef *fp, class CCharmapToLocal *cmap, int width) { CVmFormatterLog *str; /* create our display stream */ disp_str_ = str = new CVmFormatterLog(this, width); /* set the file */ str->set_log_file(vmg_ nf, fp); /* set the character mapper */ str->set_charmap(cmap); } /* * destroy */ void CVmConsoleLog::delete_obj(VMG0_) { /* delete our display stream */ disp_str_->delete_obj(vmg0_); /* do the inherited work */ CVmConsole::delete_obj(vmg0_); } frobtads-1.2.3/tads3/vmbignumlib.cpp0000644000175000001440000037166612145504453016567 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2012 by Michael J. Roberts. All Rights Reserved. */ /* Name vmbignumlib.cpp - BigNumber numerics library Function This module contains the low-level numerics library for the BigNumber object. The routines here operate on the internal byte-array representation of a BigNumber value, without the CVmObjBigNum wrapper object, so this module can be used as a library for high-precision arithmetic without dragging in the whole TADS 3 VM. Notes Modified 06/20/12 MJRoberts - Creation */ #include #include #include #include #include #include #include #include "t3std.h" #include "utf8.h" #include "vmtype.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * convert to a string, storing the result in the given buffer */ const char *CVmObjBigNum::cvt_to_string_buf( char *buf, size_t buflen, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, ext_, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* * Convert to a string, storign the result in the given buffer */ const char *CVmObjBigNum::cvt_to_string_buf( char *buf, size_t buflen, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len) { /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, ext, max_digits, whole_places, frac_digits, exp_digits, flags, lead_fill, lead_fill_len); } /* * Convert an integer value to a string, storing the result in the buffer. * This can be used to apply the whole set of floating-point formatting * options to an ordinary integer value, without going to the trouble of * creating a BigNumber object. */ const char *CVmObjBigNum::cvt_int_to_string_buf( char *buf, size_t buflen, int32_t intval, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags) { /* * Set up a stack register with the number. Allow 20 digits of * precision so that this can extend to 64-bit ints in the future. */ char tmp[5 + 20] = { 20, 0, 0, 0, 0 }; set_int_val(tmp, intval); /* * set the actual precision to equal the exponent, since zeros after * the decimal point aren't significant for an integer source */ set_prec(tmp, get_exp(tmp)); /* convert to a string into our buffer */ CBigNumStringBufFixed alo(buf, buflen); return cvt_to_string_gen(&alo, tmp, max_digits, whole_places, frac_digits, exp_digits, flags, 0, 0); } /* * Common routine to create a string representation. If buf is null, * we'll allocate a new string object, filling in new_str with the * object reference; otherwise, we'll format into the given buffer. */ const char *CVmObjBigNum::cvt_to_string_gen( IBigNumStringBuf *bufalo, const char *ext, int max_digits, int whole_places, int frac_digits, int exp_digits, ulong flags, const char *lead_fill, size_t lead_fill_len) { char plus_sign = '\0'; int always_sign_exp = ((flags & VMBN_FORMAT_EXP_SIGN) != 0); int always_exp = ((flags & VMBN_FORMAT_EXP) != 0); int lead_zero = ((flags & VMBN_FORMAT_LEADING_ZERO) != 0); int always_show_pt = ((flags & VMBN_FORMAT_POINT) != 0); int exp_caps = ((flags & VMBN_FORMAT_EXP_CAP) != 0); int pos_lead_space = ((flags & VMBN_FORMAT_POS_SPACE) != 0); int commas = ((flags & VMBN_FORMAT_COMMAS) != 0); int euro = ((flags & VMBN_FORMAT_EUROSTYLE) != 0); size_t req_chars; int prec = (int)get_prec(ext); int exp = get_exp(ext); int dig_before_pt; int dig_after_pt; int idx; unsigned int dig; char *p; int exp_carry; int show_pt; int whole_padding; int non_sci_digs; char decpt_char = (euro ? ',' : '.'); char comma_char = (euro ? '.' : ','); char *tmp_ext = 0; uint tmp_hdl; int max_sig = ((flags & VMBN_FORMAT_MAXSIG) != 0); int all_out_cnt, sig_out_cnt; /* we haven't written out any digits yet */ all_out_cnt = sig_out_cnt = 0; /* note the sign character to use for positive numbers, if any */ if (pos_lead_space) plus_sign = ' '; else if ((flags & VMBN_FORMAT_SIGN) != 0) plus_sign = '+'; /* * If we're not required to use exponential notation, but we don't * have enough max_digits places for the part before the decimal * point, use exponential anyway. (The number of digits before the * decimal point is simply the exponent if it's greater than zero, * or zero otherwise.) */ if (max_digits >= 0 && exp > max_digits) always_exp = TRUE; /* * If we're not required to use exponential notation, but our * absolute value is so small that we wouldn't show anything * "0.00000..." (i.e., we'd have too many zeros after the decimal * point to show any actual digits of our number), use exponential * notation. If our exponent is negative, its absolute value is the * number of zeros we'd show after the decimal point before the * first non-zero digit. */ if (max_digits >= 0 && exp < 0 && (-exp > max_digits || (frac_digits != -1 && -exp > frac_digits))) always_exp = TRUE; /* * If we're the "compact" mode flag is set, use scientific notation if * the displayed decimal exponent is less than -4 or greater than the * number of digits after the decimal point. (The displayed exponent * is one lower than the internal exponent, since the normalized format * has no digits before the decimal point but the displayed scientific * notation format shows one whole-part digit.) */ if ((flags & VMBN_FORMAT_COMPACT) != 0 && (exp < -3 || (max_digits >= 0 && exp - 1 > max_digits))) always_exp = TRUE; /* calculate how many digits we'd need in non-scientific notation */ if (exp < 0) { /* we have leading zeros before the first significant digit */ non_sci_digs = -exp + prec; } else if (exp > prec) { /* we have trailing zeros after the last significant digit */ non_sci_digs = exp + prec; } else { /* * we have no leading or trailing zeros to represent - only the * digits actually stored need to be displayed */ non_sci_digs = prec; } /* * Figure out how much space we need for our string. Start with the * length of the basic digit string, which is the smaller of max_digits * or the actual space we need for non-scientific notation. */ if (max_digits >= 0 && max_digits < non_sci_digs) { /* max_digits limits the number of characters we can show */ req_chars = max_digits; /* *. ..but if it only counts significant digits, and the exponent * is negative, we might also have to show all those leading zeros * over and above the max_digits limit */ if (max_sig && exp < 0) req_chars += -exp; } else { /* there's no cap on digits, so we could have to show them all */ req_chars = non_sci_digs; } /* * Now add overhead space for the sign symbol, a leading zero, a * decimal point, an 'E' for the exponent, an exponent sign, and up to * five digits for the exponent (16-bit integer -> -32768 to 32767). * Also allow an extra character in case we need to add a digit due to * rounding. */ req_chars += 11; /* * Make sure we leave enough room for the lead fill string - if they * specified a number of whole places, and we're not using * exponential format, we'll insert lead fill characters before the * first non-zero whole digit. */ if (!always_exp && whole_places != -1 && exp < whole_places) { int extra; int char_size; /* * if the exponent is negative, we'll pad by the full amount; * otherwise, we'll just pad by the difference between the * number of places needed and the exponent */ extra = whole_places; if (exp > 0) extra -= exp; /* * Add the extra bytes: one byte per character if we're using * the default space padding, or up to three bytes per character * if a lead string was specified (each unicode character can * take up to three bytes) */ char_size = (lead_fill != 0 ? 3 : 1); req_chars += extra * char_size; /* * add space for each padding character we could insert into a * comma position (there's at most one comma per three fill * characters) */ if (commas) req_chars += ((extra + 2)/3) * char_size; } /* * If we're using commas, and we're not using scientific notation, * add space for a comma for each three digits before the decimal * point */ if (commas && !always_exp) { /* add space for the commas */ req_chars += ((exp + 2) / 3); } /* * if they requested a specific minimum number of exponent digits, * and that number is greater than the allowance of 5 we already * provided, add the extra space needed */ if (exp_digits > 5) req_chars += (exp_digits - 5); /* * If they requested a specific number of digits after the decimal * point, make sure we have room for those digits. */ if (frac_digits != -1) req_chars += frac_digits; /* now that we know how much space we need, get the output buffer */ char *buf = bufalo->get_buf(req_chars + VMB_LEN); /* if that failed, return failure */ if (buf == 0) return 0; /* check for special values */ switch(get_type(ext)) { case VMBN_T_NUM: /* normal number - proceed */ break; case VMBN_T_NAN: /* not a number - show "1.#NAN" */ strcpy(buf + VMB_LEN, "1.#NAN"); vmb_put_len(buf, 6); return buf; case VMBN_T_INF: /* positive or negative infinity */ if (get_neg(ext)) { memcpy(buf + VMB_LEN, "-1.#INF", 7); vmb_put_len(buf, 7); } else { memcpy(buf + VMB_LEN, "1.#INF", 6); vmb_put_len(buf, 6); } return buf; default: /* other values are not valid */ memcpy(buf + VMB_LEN, "1.#???", 6); vmb_put_len(buf, 6); return buf; } /* * Allocate a temporary buffer to contain a copy of the number. At * most, we'll have to show max_digits of the number, or the current * precision, whichever is lower. */ { int new_prec; /* * limit the new precision to the maximum digits to be shown, or * our existing precision, whichever is lower */ if (max_digits < 0) new_prec = prec; else { new_prec = max_digits; if (prec < new_prec) new_prec = prec; } /* allocate the space */ alloc_temp_regs((size_t)new_prec, 1, &tmp_ext, &tmp_hdl); /* copy the value to the temp register, rounding the value */ copy_val(tmp_ext, ext, TRUE); /* note the new precision */ prec = new_prec; /* forget the original number and use the rounded version */ ext = tmp_ext; } start_over: /* * note the exponent, in case we rounded or otherwise adjusted the * temporary number */ exp = get_exp(ext); /* presume we won't carry into the exponent */ exp_carry = FALSE; /* no whole-part padding yet */ whole_padding = 0; /* * Figure out where the decimal point goes in the display. If we're * displaying in exponential format, we'll always display exactly * one digit before the decimal point. Otherwise, we'll display the * number given by our exponent if it's positive, or zero or one * (depending on lead_zero) if it's negative or zero. */ if (always_exp) dig_before_pt = 1; else if (exp > 0) dig_before_pt = exp; else dig_before_pt = 0; /* * if the digits before the decimal point exceed our maximum number * of digits allowed, we'll need to use exponential format */ if (max_digits >= 0 && dig_before_pt > max_digits) { always_exp = TRUE; goto start_over; } /* * Limit digits after the decimal point according to the maximum digits * allowed and the number we'll show before the decimal point. If we * have an explicit number of fractional digits to show, that overrides * the limit. */ if (max_digits >= 0) dig_after_pt = max_digits - dig_before_pt; else if (frac_digits >= 0) dig_after_pt = frac_digits; else if (prec > dig_before_pt) dig_after_pt = prec - dig_before_pt; else dig_after_pt = 0; /* start writing after the buffer length prefix */ p = buf + VMB_LEN; /* * if we're not in exponential mode, add leading spaces for the * unused whole places */ if (!always_exp && dig_before_pt < whole_places) { int cnt; size_t src_rem; utf8_ptr src; utf8_ptr dst; int idx; /* start with the excess whole places */ cnt = whole_places - dig_before_pt; /* if we're going to add a leading zero, that's one less space */ if (dig_before_pt == 0 && lead_zero) --cnt; /* * Increase the count by the number of comma positions in the * padding area. */ if (commas) { /* scan over the positions to fill and count commas */ for (idx = dig_before_pt ; idx < whole_places ; ++idx) { /* if this is a comma position, note it */ if ((idx % 3) == 0) ++cnt; } } /* set up our read and write pointers */ src.set((char *)lead_fill); src_rem = lead_fill_len; dst.set(p); /* add (and count) the leading spaces */ for ( ; cnt != 0 ; --cnt, ++whole_padding) { wchar_t ch; /* * if we have a lead fill string, read from it; otherwise, * just use a space */ if (lead_fill != 0) { /* if we've exhausted the string, start over */ if (src_rem == 0) { src.set((char *)lead_fill); src_rem = lead_fill_len; } /* get the next character */ ch = src.getch(); /* skip this character */ src.inc(&src_rem); } else { /* no lead fill string - insert a space by default */ ch = ' '; } /* write this character */ dst.setch(ch); } /* resynchronize from our utf8 pointer */ p = dst.getptr(); } /* * if the number is negative, or we're always showing a sign, add * the sign; if we're not adding a sign, but we're showing a leading * space for positive numbers, add the leading space */ if (get_neg(ext)) *p++ = '-'; else if (plus_sign != '\0') *p++ = plus_sign; /* * if we have no digits before the decimal, and we're adding a * leading zero, add it now */ if (dig_before_pt == 0 && lead_zero) { /* add the leading zero */ *p++ = '0'; /* * If we have a total digit limit, reduce the limit on the digits * after the decimal point, since this leading zero comes out of * our overall digit budget. This doesn't apply if max_digits is * the number of significant digits. */ if (max_digits >= 0 && !max_sig) --dig_after_pt; /* count it in the output counter for all digits */ all_out_cnt++; } /* * If we have limited the number of digits that we'll allow after the * decimal point, due to the limit on the total number of digits and * the number of digits we need to display before the decimal, start * over in exponential format to ensure we get the after-decimal * display we want. * * Note that we won't bother going into exponential format if the * number of digits before the decimal point is zero or one, because * exponential format won't give us any more room - in such cases we * simply have an impossible request. */ if (max_digits >=0 && !always_exp && frac_digits != -1 && dig_after_pt < frac_digits && dig_before_pt > 1) { /* switch to exponential format and start over */ always_exp = TRUE; goto start_over; } /* display the digits before the decimal point */ for (idx = 0 ; idx < dig_before_pt && idx < prec ; ++idx) { /* * if this isn't the first digit, and we're adding commas, and * an even multiple of three more digits follow, insert a comma */ if (idx != 0 && commas && !always_exp && ((dig_before_pt - idx) % 3) == 0) *p++ = comma_char; /* get this digit */ dig = get_dig(ext, idx); /* add it to the string */ *p++ = (dig + '0'); /* count it */ all_out_cnt++; if (dig != 0 || sig_out_cnt != 0) sig_out_cnt++; } /* if we ran out of precision, add zeros */ for ( ; idx < dig_before_pt ; ++idx) { /* add the zero */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } /* * Add the decimal point. Show the decimal point unless * always_show_pt is false, and either frac_digits is zero, or * frac_digits is -1 and we have no fractional part. */ if (always_show_pt) { /* always showing the point */ show_pt = TRUE; } else { if (frac_digits > 0) { /* we're showing fractional digits - always show a point */ show_pt = TRUE; } else if (frac_digits == 0) { /* we're showing no fractional digits, so suppress the point */ show_pt = FALSE; } else { /* * for now assume we'll show the point; we'll take it back * out if we don't encounter anything but zeros */ show_pt = TRUE; } } /* if we're showing the fractional part, show it */ if (show_pt) { int frac_len; int frac_lim; char *last_non_zero; /* * remember the current position as the last trailing non-zero - * if we don't find anything but zeros and decide to remove the * trailing zeros, we'll also remove the decimal point by * coming back here */ last_non_zero = p; /* add the point */ *p++ = decpt_char; /* if we're always showing a decimal point, we can't remove it */ if (always_show_pt) last_non_zero = p; /* if frac_digits is -1, there's no limit */ if (frac_digits == -1) frac_lim = dig_after_pt; else frac_lim = frac_digits; /* * further limit the fractional digits according to the maximum * digit allowance */ if (frac_lim > dig_after_pt) frac_lim = dig_after_pt; /* no fractional digits output yet */ frac_len = 0; /* * if we haven't yet reached the first non-zero digit, display * as many zeros as necessary */ if (idx == 0 && exp < 0) { int cnt; /* * display leading zeros based no the exponent: if exp is * zero, we don't need any; if exp is -1, we need one; if * exp is -2, we need two, and so on */ for (cnt = exp ; cnt != 0 && frac_len < frac_lim ; ++cnt) { /* add a zero */ *p++ = '0'; /* * if we're counting all digits (not just significant * digits), count this digit against the total */ if (!max_sig) ++frac_len; /* count it in the total digits out */ all_out_cnt++; } } /* add the fractional digits */ for ( ; idx < prec && frac_len < frac_lim ; ++idx, ++frac_len) { /* get this digit */ dig = get_dig(ext, idx); /* add it */ *p++ = (dig + '0'); /* * if it's not a zero, note the location - if we decide to * trim trailing zeros, we'll want to keep at least this * much, since this is a significant trailing digit */ if (dig != 0) last_non_zero = p; /* count it */ all_out_cnt++; if (dig != 0 || sig_out_cnt != 0) sig_out_cnt++; } /* * If the "keep trailing zeros" flag is set, and there's a * max_digits quota that we haven't filled, add trailing zeros as * needed. */ if ((flags & VMBN_FORMAT_TRAILING_ZEROS) != 0 && max_digits >= 0) { /* * fill out the request: if max_digits is stated in significant * digits, count the number of significant digits we've written * so far, otherwise count all digits written so far */ for (int i = max_sig ? sig_out_cnt : all_out_cnt ; i < max_digits ; ++i) { /* add a digit */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } } /* * add the trailing zeros if we ran out of precision before we * filled the requested number of places */ if (frac_digits != -1) { /* fill out the remaining request length with zeros */ for ( ; frac_len < frac_lim ; ++frac_len) { /* add a zero */ *p++ = '0'; /* count it */ all_out_cnt++; if (sig_out_cnt != 0) sig_out_cnt++; } } else if (!(flags & VMBN_FORMAT_TRAILING_ZEROS)) { char *p1; /* * if we're removing the decimal point, we're not showing a * fractional part after all - so note */ if (last_non_zero < p && *last_non_zero == decpt_char) show_pt = FALSE; /* * We can use whatever length we like, so remove meaningless * trailing zeros. Before we do this, though, make sure we * aren't rounding up the last trailing zero - if we need to * round the last digit up, the final zero is really a 1. */ if (p > last_non_zero && get_round_dir(ext, idx)) { /* * That last zero is significant after all, since we're * going to round it up to a 1 for display. Don't actually * do the rounding now; simply note that the last zero is * significant so that we don't drop the digits leading up * to it. */ last_non_zero = p; } /* * We've checked for rounding in the last digit, so we can now * safely remove meaningless trailing zeros. If this leaves a * completely empty buffer, not counting a sign and/or a * decimal point, it means that we have a fractional number * that we're showing without an exponent, and the number of * digits we had for display was insufficient to reach the * first non-zero fractional digit. In this case, simply show * '0' (or '0.', if a decimal point is required) as the result. * To find out, scan for digits. */ p = last_non_zero; for (p1 = buf + VMB_LEN ; p1 < p && !is_digit(*p1) ; ++p1) ; /* if we didn't find any digits, add/insert a '0' */ if (p1 == p) { /* * if there's a decimal point, insert the '0' before it; * otherwise, just append the zero */ if (p > buf + VMB_LEN && *(p-1) == decpt_char) { *(p-1) = '0'; *p++ = decpt_char; } else *p++ = '0'; } } } /* if necessary, round up at the last digit */ if (get_round_dir(ext, idx)) { char *rp; int need_carry; int found_pt; int dig_cnt; /* * go back through the number and add one to the last digit, * carrying as needed */ for (dig_cnt = 0, found_pt = FALSE, need_carry = TRUE, rp = p - 1 ; rp >= buf + VMB_LEN ; rp = utf8_ptr::s_dec(rp)) { /* if this is a digit, bump it up */ if (is_digit(*rp)) { /* count it */ ++dig_cnt; /* if it's 9, we'll have to carry; otherwise it's easy */ if (*rp == '9') { /* set it to zero and keep going to do the carry */ *rp = '0'; /* * if we haven't found the decimal point yet, and * we're not required to show a certain number of * fractional digits, we can simply remove this * trailing zero */ if (show_pt && !found_pt && frac_digits == -1) { /* remove the trailing zero */ p = utf8_ptr::s_dec(p); /* remove it from the digit count */ --dig_cnt; } } else { /* bump it up one */ ++(*rp); /* no more carrying is needed */ need_carry = FALSE; /* we don't need to look any further */ break; } } else if (*rp == decpt_char) { /* note that we found the decimal point */ found_pt = TRUE; } } /* * If we got to the start of the number and we still need a * carry, we must have had all 9's. In this case, we need to * redo the entire number, since all of the layout (commas, * leading spaces, etc) can change - it's way too much work to * try to back-patch all of this stuff, so we'll just bump the * number up and reformat it from scratch. */ if (need_carry) { /* truncate at this digit, and round up what we're keeping */ set_dig(tmp_ext, idx, 0); round_up_abs(tmp_ext, idx); /* * if this pushes us over the maximum digit range, switch to * scientific notation */ if (max_digits >= 0 && dig_cnt + 1 > max_digits) always_exp = TRUE; /* go back and start over */ goto start_over; } } /* add the exponent */ if (always_exp) { int disp_exp; /* add the 'E' */ *p++ = (exp_caps ? 'E' : 'e'); /* * calculate the display exponent - it's one less than the * actual exponent, because we put the point after one digit */ disp_exp = exp - 1; /* * if we carried into the exponent due to rounding, increase the * exponent by one */ if (exp_carry) ++disp_exp; /* add the sign */ if (disp_exp < 0) { *p++ = '-'; disp_exp = -disp_exp; } else if (always_sign_exp) *p++ = '+'; /* * if the exponent is zero, just put zero (unless a more * specific format was requested) */ if (disp_exp == 0 && exp_digits == -1) { /* add the zero exponent */ *p++ = '0'; } else { char expbuf[20]; char *ep; int dig_cnt; /* build the exponent in reverse */ for (dig_cnt = 0, ep = expbuf + sizeof(expbuf) ; disp_exp != 0 ; disp_exp /= 10, ++dig_cnt) { /* move back one character */ --ep; /* add one digit */ *ep = (disp_exp % 10) + '0'; } /* if necessary, add leading zeros to the exponent */ if (exp_digits != -1 && exp_digits > dig_cnt) { for ( ; dig_cnt < exp_digits ; ++dig_cnt) *p++ = '0'; } /* copy the exponent into the output */ for ( ; ep < expbuf + sizeof(expbuf) ; ++ep) *p++ = *ep; } } /* if we allocated a temporary register, free it */ if (tmp_ext != 0) release_temp_regs(1, tmp_hdl); /* set the string length and return the buffer */ vmb_put_len(buf, p - buf - VMB_LEN); return buf; } /* ------------------------------------------------------------------------ */ /* * Parse a string into an allocated buffer. */ char *CVmObjBigNum::parse_str_alo(size_t &buflen, const char *str, size_t len, int radix) { /* figure the precision */ int prec = precision_from_string(str, len, radix); /* allocate a buffer */ char *ext = new char[buflen = calc_alloc(prec)]; set_prec(ext, prec); /* parse the number */ parse_str_into(ext, str, len, radix); /* return the new buffer */ return ext; } /* ------------------------------------------------------------------------ */ /* * Parse a string with the given radix. */ void CVmObjBigNum::parse_str_into(char *ext, const char *str, size_t len, int radix) { /* if the radix is 0, infer the radix from the string */ int inferred_radix = 0; if (radix == 0) radix = inferred_radix = radix_from_string(str, len); /* if it's decimal, parse as a floating point value */ if (radix == 10) { parse_str_into(ext, str, len); return; } /* get the precision */ size_t prec = get_prec(ext); /* set the initial value to zero */ set_type(ext, VMBN_T_NUM); memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a sign */ int neg = FALSE; if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* note the sign and skip it */ neg = TRUE; p.inc(&rem); } /* skip spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* if we inferred the radix as hax, skip any '0x' marker */ if (inferred_radix == 16 && rem != 0 && p.getch() == '0' && (p.getch_at(1) == 'x' || p.getch_at(1) == 'X')) { /* skip the '0x' prefix */ p.inc(&rem); p.inc(&rem); } /* allocate a temporary register for swapping with the accumulator */ uint thdl; char *tmp = alloc_temp_reg(prec, &thdl); char *t1 = ext, *t2 = tmp; /* parse the digits */ int significant = FALSE; for ( ; rem != 0 ; p.inc(&rem)) { /* get the next character */ wchar_t ch = p.getch(); /* get the digit value of this character */ int d; if (ch >= '0' && ch <= '9') d = ch - '0'; else if (ch >= 'A' && ch <= 'Z') d = ch - 'A' + 10; else if (ch >= 'a' && ch <= 'z') d = ch - 'a' + 10; else break; /* if the digit isn't within the radix, it's not a digit after all */ if (d >= radix) break; /* if it's not a leading zero, it's significant */ if (ch != '0') significant = TRUE; /* if it's a significant digit, add it to the accumulator */ if (significant) { /* * Set up a stack register with the digit value. The radix can * be up to 36, so the digit value can be up to 35, so we need * up to two decimal digits to represent the digit value. */ char dreg[5 + 2] = { 2, 0, 0, 0, 0 }; set_uint_val(dreg, d); /* shift the accumulator and add the digit */ mul_by_long(t1, radix); compute_abs_sum_into(t2, t1, dreg); /* swap the accumulators */ char *tt = t1; t1 = t2; t2 = tt; } } /* if we ended up with the value in 'tmp', copy it into our result */ if (t1 != ext) copy_val(ext, t1, FALSE); /* free our temporary register */ release_temp_reg(thdl); /* set the sign */ set_neg(ext, neg); } /* ------------------------------------------------------------------------ */ /* * Parse a string value into an extension */ void CVmObjBigNum::parse_str_into(char *ext, const char *str, size_t len) { /* get the precision */ size_t prec = get_prec(ext); /* set the type to number */ set_type(ext, VMBN_T_NUM); /* initially zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* initialize the exponent to zero */ int exp = 0; /* presume it will be positive */ int neg = FALSE; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a sign */ if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* note the sign and skip it */ neg = TRUE; p.inc(&rem); } /* set the sign */ set_neg(ext, neg); /* skip spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* we haven't yet found a significant digit */ int significant = FALSE; /* shift the digits into the value */ int pt = FALSE; for (size_t idx = 0 ; rem != 0 ; p.inc(&rem)) { wchar_t ch; /* get this character */ ch = p.getch(); /* check for a digit */ if (is_digit(ch)) { /* * if it's not a zero, we're definitely into the significant * part of the number */ if (ch != '0') significant = TRUE; /* * if it's significant, add it to the number - skip leading * zeros, since they add no information to the number */ if (significant) { /* if we're not out of precision, add the digit */ if (idx < prec) { /* set the next digit */ set_dig(ext, idx, value_of_digit(ch)); /* move on to the next digit position */ ++idx; } /* * that's another factor of 10 if we haven't found the * decimal point (whether or not we're out of precision to * actually store the digit, count the increase in the * exponent) */ if (!pt) ++exp; } else if (pt) { /* * we haven't yet found a significant digit, so this is a * leading zero, but we have found the decimal point - this * reduces the exponent by one */ --exp; } } else if (ch == '.' && !pt) { /* * this is the decimal point - note it; from now on, we won't * increase the exponent as we add digits */ pt = TRUE; } else if (ch == 'e' || ch == 'E') { int acc; int exp_neg = FALSE; /* exponent - skip the 'e' */ p.inc(&rem); /* check for a sign */ if (rem != 0 && p.getch() == '+') { /* skip the sign */ p.inc(&rem); } else if (rem != 0 && p.getch() == '-') { /* skip it and note it */ p.inc(&rem); exp_neg = TRUE; } /* parse the exponent */ for (acc = 0 ; rem != 0 ; p.inc(&rem)) { wchar_t ch; /* if this is a digit, add it to the exponent */ ch = p.getch(); if (is_digit(ch)) { /* accumulate the digit */ acc *= 10; acc += value_of_digit(ch); } else { /* that's it for the exponent */ break; } } /* if it's negative, apply the sign */ if (exp_neg) acc = -acc; /* add the exponent to the one we've calculated */ exp += acc; /* after the exponent, we're done with the whole number */ break; } else { /* it looks like we've reached the end of the number */ break; } } /* set the exponent */ set_exp(ext, exp); /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Parse a string to determine the radix. If it has a '0x' prefix, it's in * hex. If it has a '0' prefix and no decimal point, 'E', or '8' or '9', * it's octal. Otherwise it's decimal. */ int CVmObjBigNum::radix_from_string(const char *str, size_t len) { /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* check for a radix indicator */ if (rem != 0 && p.getch() == '0') { /* * There's a leading zero, so it could be hex or octal. If there's * an 'x', it's definitely hex. Otherwise, it's octal as long as * it's in purely integer format - that is, no decimal point or 'E' * exponent marker. */ p.inc(&rem); if (rem != 0 && (p.getch() == 'x' || p.getch() == 'X')) { /* 0x prefix, so it's definitely hex */ return 16; } /* * There's a leading 0 but no 'x', so it could be octal or decimal, * depending on what's in the rest of the string. */ for ( ; rem != 0 ; p.inc(&rem)) { switch (p.getch()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* octal digit, so we can't rule anything out yet */ continue; case '.': case 'e': case 'E': /* * decimal point or exponent marker, so it's a floating * point value, which can only be decimal */ return 10; case '8': case '9': /* an 8 or a 9 can only appear in a decimal number */ return 10; default: /* * Anything else is invalid within a number, whether octal * integer or floating point, so we've reached the end of * the number. Since we didn't encounter anything that * makes the number a floating point value or decimal * integer, we have an octal integer. */ return 8; } } /* * we reached the end of the number without finding anything that * would rule out treating it as an octal integer, so that's what * it is */ return 8; } else { /* no leading '0', so it's decimal */ return 10; } } /* ------------------------------------------------------------------------ */ /* * Parse a string representation of a number in the given radix to * determine the precision required to hold the value. If 'radix' is 0, we * infer the radix from the string: if it has a '0' prefix and no decimal * point or 'E', it's an octal integer; if it has a '0x' prefix, it's a hex * integer; otherwise it's decimal and can be an integer or a floating * point value. */ int CVmObjBigNum::precision_from_string(const char *str, size_t len, int radix) { /* if the radix is zero, infer the radix from the string */ int inferred_radix = 0; if (radix == 0) radix = inferred_radix = radix_from_string(str, len); /* for decimal values, parse the full floating point format */ if (radix == 10) return precision_from_string(str, len); /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* if we inferred the radix as hax, skip any '0x' marker */ if (inferred_radix == 16 && rem != 0 && p.getch() == '0' && (p.getch_at(1) == 'x' || p.getch_at(1) == 'X')) { /* skip the '0x' prefix */ p.inc(&rem); p.inc(&rem); } /* scan digits */ int prec, significant = FALSE; for (prec = 0 ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* get the digit value of this character */ int d; if (ch >= '0' && ch <= '9') d = ch - '0'; else if (ch >= 'A' && ch <= 'Z') d = ch - 'A' + 10; else if (ch >= 'a' && ch <= 'z') d = ch - 'a' + 10; else break; /* if the digit isn't within the radix, it's not a digit after all */ if (d >= radix) break; /* it's significant if it's not a leading zero */ if (ch != '0') significant = TRUE; /* * if we have found a significant digit so far, count this one - do * not count leading zeros, whether they occur before or after the * decimal point */ if (significant) ++prec; } /* * Figure the decimal precision required to hold a number in the given * radix. This is a simple linear conversion: N base-R digits require * log(R)/log(10) decimal digits. Using log10 as our canonical log * base means that log(R)/log(10) == log(R)/1 == log(R). We of course * have to round up for any fractional digits. */ return (int)ceil(prec * log10((double)radix)); } /* * Parse a string representation of a number to determine the precision * required to hold the value. */ int CVmObjBigNum::precision_from_string(const char *str, size_t len) { /* set up to scan the string */ utf8_ptr p((char *)str); size_t rem = len; /* skip leading spaces */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* skip the sign if present */ if (rem != 0 && (p.getch() == '-' || p.getch() == '+')) p.inc(&rem); /* skip more spaces after the sign */ for ( ; rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ; /* we haven't yet found a significant leading digit */ int significant = FALSE; /* scan digits */ size_t prec; int pt; int digcnt; for (prec = 0, digcnt = 0, pt = FALSE ; rem != 0 ; p.inc(&rem)) { /* get this character */ wchar_t ch = p.getch(); /* see what we have */ if (is_digit(ch)) { /* * if it's not a zero, note that we've found a significant * digit */ if (ch != '0') significant = TRUE; /* * if we have found a significant digit so far, count this one * - do not count leading zeros, whether they occur before or * after the decimal point */ if (significant) ++prec; /* count the digit in any case */ ++digcnt; } else if (ch == '.' && !pt) { /* decimal point - note it and keep going */ pt = TRUE; } else if (ch == 'e' || ch == 'E') { /* exponent - that's the end of the mantissa */ break; } else { /* we've reached the end of the number */ break; } } /* * if the precision is zero, the number must be zero - use the actual * number of digits for the default precision, so that a value * specified as "0.0000000" has eight digits of precision */ if (prec == 0) prec = digcnt; /* return the precision necessary to store the entire string */ return prec; } /* ------------------------------------------------------------------------ */ /* * Set my value to the given double */ void CVmObjBigNum::set_double_val(char *ext, double val) { /* get the precision */ size_t prec = get_prec(ext); /* set the type to number */ set_type(ext, VMBN_T_NUM); /* set the sign bit */ if (val < 0) { set_neg(ext, TRUE); val = -val; } else set_neg(ext, FALSE); /* zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* * Initialize our exponent 'exp' to the integer portion of log10(val). * If val is a power of 10, exp == log10(val) is an integer, and val is * exactly one times 10^exp (e.g., if val is 1000, log10(val) == 3.0, * and val == 1*10^3). If val isn't a power of 10, 10^exp is the * nearest power of 10 below val. For example, log10(1500) == 3.176, * so exp == int(log10(1500)) == 3, so val = 1.5*10^3. In other words, * the value can be represented as some number between 1 and 10 times * 10^exp. Our storage format is close to this: we store the mantissa * as a value between 0 and 1. So what we *really* want for the * exponent is log10(val)+1 - this will give us an exponent that we can * multiply by some number between 0 and 1 to recreate 'val'. */ int exp = (int)log10(val) + 1; set_exp(ext, exp); /* * We know that val divided by 10^exp is between 0 and 1. So the most * significant digit of val is val/10^(exp-1). We can then divide the * remainder of that calculation by 10^(exp-2) to get the next digit, * and the remainder of that by 10^(exp-3) for the next digit, and so * on. Keep going until we've filled out our digits. */ double base = pow(10.0, exp - 1); for (size_t i = 0 ; i < prec ; ++i, base /= 10.0) { /* * get the most significant remaining digit by dividing by the * log10 exponent of the current value */ int dig = (int)(val / base); set_dig(ext, i, dig); /* get the remainder */ val -= dig*base; } /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Set my value to a given integer value */ void CVmObjBigNum::set_int_val(char *ext, long val) { if (val < 0) { set_uint_val(ext, -val); set_neg(ext, TRUE); } else { set_uint_val(ext, val); } } void CVmObjBigNum::set_uint_val(char *ext, ulong val) { size_t prec; int exp; /* get the precision */ prec = get_prec(ext); /* set the type to number */ ext[VMBN_FLAGS] = 0; set_type(ext, VMBN_T_NUM); /* the value is unsigned, so it's non-negative */ set_neg(ext, FALSE); /* initially zero the mantissa */ memset(ext + VMBN_MANT, 0, (prec + 1)/2); /* initialize the exponent to zero */ exp = 0; /* shift the integer value into the value */ while (val != 0) { unsigned int dig; /* get the low-order digit of the value */ dig = (unsigned int)(val % 10); /* shift the value one place */ val /= 10; /* * shift our number one place right to accommodate this next * higher-order digit */ shift_right(ext, 1); /* set the new most significant digit */ set_dig(ext, 0, dig); /* that's another factor of 10 */ ++exp; } /* set the exponent */ set_exp(ext, exp); /* normalize the number */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * compare extensions */ int CVmObjBigNum::compare_ext(const char *a, const char *b) { /* if either one is not a number, they can't be compared */ if (is_nan(a) || is_nan(b)) err_throw(VMERR_INVALID_COMPARISON); /* if the signs differ, the positive one is greater */ if (get_neg(a) != get_neg(b)) { /* * if a is negative, it's the lesser number; otherwise it's the * greater number */ return (get_neg(a) ? -1 : 1); } /* the signs are the same - compare the absolute values */ int ret = compare_abs(a, b); /* * If the numbers are negative, and abs(a) > abs(b), a is actually the * lesser number; if they're negative and abs(a) < abs(b), a is the * greater number. So, if a is negative, invert the sense of the * result. Otherwise, both numbers are positive, so the sense of the * absolute value comparison is the same as the sense of the actual * comparison, so just return the result directly. */ return (get_neg(a) ? -ret : ret); } /* ------------------------------------------------------------------------ */ /* * Compare the absolute values of two numbers */ int CVmObjBigNum::compare_abs(const char *ext1, const char *ext2) { size_t idx; int zero1 = is_zero(ext1); int zero2 = is_zero(ext2); size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); size_t max_prec; size_t min_prec; const char *max_ext; int result; /* * if one is zero and the other is not, the one that's not zero has a * larger absolute value */ if (zero1) return (zero2 ? 0 : -1); if (zero2) return (zero1 ? 0 : 1); /* * if the exponents differ, the one with the larger exponent is the * larger number (this is necessarily true because we keep all numbers * normalized) */ if (get_exp(ext1) > get_exp(ext2)) return 1; else if (get_exp(ext1) < get_exp(ext2)) return -1; /* * The exponents are equal, so we must compare the mantissas digit by * digit */ /* get the larger of the two precisions */ min_prec = prec2; max_prec = prec1; max_ext = ext1; if (prec2 > max_prec) { min_prec = prec1; max_prec = prec2; max_ext = ext2; } /* * The digits are in order from most significant to least significant, * which means we can use memcmp to compare the common parts. However, * we can only compare an even number of digits this way, so round down * the common precision if it's odd. */ if (min_prec > 1 && (result = memcmp(ext1 + VMBN_MANT, ext2 + VMBN_MANT, min_prec/2)) != 0) { /* * they're different in the common memcmp-able parts, so return the * memcmp result */ return result; } /* if the common precision is odd, compare the final common digit */ if ((min_prec & 1) != 0 && (result = ((int)get_dig(ext1, min_prec-1) - (int)get_dig(ext2, min_prec-1))) != 0) return result; /* * the common parts of the mantissas are identical; check each * remaining digit of the longer one to see if any are non-zero */ for (idx = min_prec ; idx < max_prec ; ++idx) { /* * if this digit is non-zero, the longer one is greater, because * the shorter one has an implied zero in this position */ if (get_dig(max_ext, idx) != 0) return (int)prec1 - (int)prec2; } /* they're identical */ return 0; } /* ------------------------------------------------------------------------ */ /* * Compute a sum into the given buffer */ void CVmObjBigNum::compute_sum_into(char *new_ext, const char *ext1, const char *ext2) { /* check to see if the numbers have the same sign */ if (get_neg(ext1) == get_neg(ext2)) { /* * the two numbers have the same sign, so the sum has the same sign * as the two values, and the magnitude is the sum of the absolute * values of the operands */ /* compute the sum of the absolute values */ compute_abs_sum_into(new_ext, ext1, ext2); /* set the sign to match that of the operand */ set_neg(new_ext, get_neg(ext1)); } else { /* * one is positive and the other is negative - subtract the * absolute value of the negative one from the absolute value of * the positive one; the sign will be set correctly by the * differencing */ if (get_neg(ext1)) compute_abs_diff_into(new_ext, ext2, ext1); else compute_abs_diff_into(new_ext, ext1, ext2); } } /* ------------------------------------------------------------------------ */ /* * Compute the sum of two absolute values into the given buffer. */ void CVmObjBigNum::compute_abs_sum_into(char *new_ext, const char *ext1, const char *ext2) { int exp1 = get_exp(ext1), exp2 = get_exp(ext2), new_exp; int prec1 = get_prec(ext1), prec2 = get_prec(ext2); int new_prec = get_prec(new_ext); /* if one or the other is identically zero, return the other value */ if (is_zero(ext1)) { /* ext1 is zero - return ext2 */ copy_val(new_ext, ext2, TRUE); return; } else if (is_zero(ext2)) { /* ext2 is zero - return ext1 */ copy_val(new_ext, ext1, TRUE); return; } /* * start the new value with the larger of the two exponents - this * will have the desired effect of dropping the least significant * digits if any digits must be dropped */ new_exp = exp1; if (exp2 > new_exp) new_exp = exp2; set_exp(new_ext, new_exp); /* compute the digit positions at the bounds of each of our values */ int hi1 = exp1 - 1; int lo1 = exp1 - prec1; int hi2 = exp2 - 1; int lo2 = exp2 - prec2; int hi3 = new_exp - 1; int lo3 = new_exp - new_prec; /* * If one of the values provides a digit one past the end of our * result, remember that as the trailing digit that we're going to * drop. We'll check this when we're done to see if we need to * round the number. Since the result has precision at least as * large as the larger of the two inputs, we can only be dropping * significant digits from one of the two inputs - we can't be * cutting off both inputs. */ int trail_dig = 0, trail_val = 0; if (lo3 - 1 >= lo1 && lo3 - 1 <= hi1) { /* remember the digit */ trail_dig = get_dig(ext1, exp1 - (lo3-1) - 1); trail_val = get_ORdigs(ext1, exp1 - (lo3-1)); } else if (lo3 - 1 >= lo2 && lo3 - 1 <= hi2) { /* remember the digit */ trail_dig = get_dig(ext2, exp2 - (lo3-1) - 1); trail_val = get_ORdigs(ext2, exp2 - (lo3-1)); } /* no carry yet */ int carry = 0; /* add the digits */ for (int pos = lo3 ; pos <= hi3 ; ++pos) { /* start with the carry */ int acc = carry; /* add the first value digit if it's in range */ if (pos >= lo1 && pos <= hi1) acc += get_dig(ext1, exp1 - pos - 1); /* add the second value digit if it's in range */ if (pos >= lo2 && pos <= hi2) acc += get_dig(ext2, exp2 - pos - 1); /* check for carry */ if (acc > 9) { /* reduce the accumulator and set the carry */ acc -= 10; carry = 1; } else { /* no carry */ carry = 0; } /* set the digit in the result */ set_dig(new_ext, new_exp - pos - 1, acc); } /* * If we have a carry at the end, we must carry it out to a new * digit. In order to do this, we must shift the whole number right * one place, then insert the one. */ if (carry) { /* * remember the last digit of the result, which we won't have * space to store after the shift */ trail_val |= trail_dig; trail_dig = get_dig(new_ext, new_prec - 1); /* shift the result right */ shift_right(new_ext, 1); /* increase the exponent to compensate for the shift */ set_exp(new_ext, get_exp(new_ext) + 1); /* set the leading 1 */ set_dig(new_ext, 0, 1); } /* the sum of two absolute values is always positive */ set_neg(new_ext, FALSE); /* * round up if the trailing digits are >5000..., or exactly 5000... * and the last digit is odd */ round_for_dropped_digits(new_ext, trail_dig, trail_val); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute a difference into a buffer */ void CVmObjBigNum::compute_diff_into(char *result, const char *ext1, const char *ext2) { /* check to see if the numbers have the same sign */ if (get_neg(ext1) == get_neg(ext2)) { /* * The two numbers have the same sign, so the difference is the * difference in the magnitudes, and has a sign depending on the * order of the difference and the signs of the original values. * If both values are positive, the difference is negative if the * second value is larger than the first. If both values are * negative, the difference is negative if the second value has * larger absolute value than the first. */ /* compute the difference in magnitudes */ compute_abs_diff_into(result, ext1, ext2); /* * if the original values were negative, then the sign of the * result is the opposite of the sign of the difference of the * absolute values; otherwise, it's the same as the sign of the * difference of the absolute values */ if (get_neg(ext1)) negate(result); } else { /* * one is positive and the other is negative - the result has the * sign of the first operand, and has magnitude equal to the sum of * the absolute values */ /* compute the sum of the absolute values */ compute_abs_sum_into(result, ext1, ext2); /* set the sign of the result to that of the first operand */ if (!is_zero(result)) set_neg(result, get_neg(ext1)); } } /* ------------------------------------------------------------------------ */ /* * Compute the difference of two absolute values into the given buffer */ void CVmObjBigNum::compute_abs_diff_into(char *new_ext, const char *ext1, const char *ext2) { int max_exp; int lo1, hi1; int lo2, hi2; int lo3, hi3; int pos; int result_neg = FALSE; int borrow; /* if we're subtracting zero or from zero, it's easy */ if (is_zero(ext2)) { /* * we're subtracting zero from another value - the result is * simply the first value */ copy_val(new_ext, ext1, TRUE); return; } else if (is_zero(ext1)) { /* * We're subtracting a value from zero - we know the value we're * subtracting is non-zero (we already checked for that case * above), and we're only considering the absolute values, so * simply return the negative of the absolute value of the * second operand. */ copy_val(new_ext, ext2, TRUE); set_neg(new_ext, TRUE); return; } /* * Compare the absolute values of the two operands. If the first * value is larger than the second, subtract them in the given * order. Otherwise, reverse the order and note that the result is * negative. */ if (compare_abs(ext1, ext2) < 0) { const char *tmp; /* the result will be negative */ result_neg = TRUE; /* swap the order of the subtraction */ tmp = ext1; ext1 = ext2; ext2 = tmp; } /* * start the new value with the larger of the two exponents - this * will have the desired effect of dropping the least significant * digits if any digits must be dropped */ max_exp = get_exp(ext1); if (get_exp(ext2) > max_exp) max_exp = get_exp(ext2); set_exp(new_ext, max_exp); /* compute the digit positions at the bounds of each of our values */ hi1 = get_exp(ext1) - 1; lo1 = get_exp(ext1) - get_prec(ext1); hi2 = get_exp(ext2) - 1; lo2 = get_exp(ext2) - get_prec(ext2); hi3 = get_exp(new_ext) - 1; lo3 = get_exp(new_ext) - get_prec(new_ext); /* start off with no borrow */ borrow = 0; /* * Check for borrowing from before the least significant digit * position in common to both numbers */ if (lo3-1 >= lo1 && lo3-1 <= hi1) { /* * In this case, we would be dropping precision from the end of * the top number. This case should never happen - the only way * it could happen is for the bottom number to extend to the * left of the top number at the most significant end. This * cannot happen because the bottom number always has small * magnitude by this point (we guarantee this above). So, we * should never get here. */ assert(FALSE); } else if (lo3-1 >= lo2 && lo3-1 <= hi2) { size_t idx; /* * We're dropping precision from the bottom number, so we want * to borrow into the subtraction if the rest of the number is * greater than 5xxxx. If it's exactly 5000..., do not borrow, * since the result would end in 5 and we'd round up. * * If the next digit is 6 or greater, we know for a fact we'll * have to borrow. If the next digit is 4 or less, we know for * a fact we won't have to borrow. If the next digit is 5, * though, we must look at the rest of the number to see if * there's anything but trailing zeros. */ idx = (size_t)(get_exp(ext2) - (lo3-1) - 1); if (get_dig(ext2, idx) >= 6) { /* definitely borrow */ borrow = 1; } else if (get_dig(ext2, idx) == 5) { /* borrow only if we have something non-zero following */ for (++idx ; idx < get_prec(ext2) ; ++idx) { /* if it's non-zero, we must borrow */ if (get_dig(ext2, idx) != 0) { /* note the borrow */ borrow = 1; /* no need to keep scanning */ break; } } } } /* subtract the digits from least to most significant */ for (pos = lo3 ; pos <= hi3 ; ++pos) { int acc; /* start with zero in the accumulator */ acc = 0; /* start with the top-line value if it's represented here */ if (pos >= lo1 && pos <= hi1) acc = get_dig(ext1, get_exp(ext1) - pos - 1); /* subtract the second value digit if it's represented here */ if (pos >= lo2 && pos <= hi2) acc -= get_dig(ext2, get_exp(ext2) - pos - 1); /* subtract the borrow */ acc -= borrow; /* check for borrow */ if (acc < 0) { /* increase the accumulator */ acc += 10; /* we must borrow from the next digit up */ borrow = 1; } else { /* we're in range - no need to borrow */ borrow = 0; } /* set the digit in the result */ set_dig(new_ext, get_exp(new_ext) - pos - 1, acc); } /* set the sign of the result as calculated earlier */ set_neg(new_ext, result_neg); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute the product of the values into the given buffer */ void CVmObjBigNum::compute_prod_into(char *new_ext, const char *ext1, const char *ext2) { size_t prec1 = get_prec(ext1); size_t prec2 = get_prec(ext2); size_t new_prec = get_prec(new_ext); size_t idx1; size_t idx2; size_t out_idx; size_t start_idx; int out_exp; /* start out with zero in the accumulator */ memset(new_ext + VMBN_MANT, 0, (new_prec + 1)/2); /* * Initially write output in the same 'column' where the top number * ends, so we start out with the same scale as the top number. */ start_idx = get_prec(ext1); /* * initial result exponent is the sum of the exponents, minus the * number of digits in the bottom number (effectively, this lets us * treat the bottom number as a whole number by scaling it enough to * make it whole, soaking up the factors of ten into decimal point * relocation) */ out_exp = get_exp(ext1) + get_exp(ext2) - prec2; /* there's no trailing accumulator digit yet */ int trail_dig = 0, trail_val = 0; /* * Loop over digits in the bottom number, from least significant to * most significant - we'll multiply each digit of the bottom number * by the top number and add the result into the accumulator. */ for (idx2 = prec2 ; idx2 != 0 ; ) { int carry; int dig; int ext2_dig; /* no carry yet on this round */ carry = 0; /* start writing this round at the output start index */ out_idx = start_idx; /* move to the next digit */ --idx2; /* get this digit of ext2 */ ext2_dig = get_dig(ext2, idx2); /* * if this digit of ext2 is non-zero, multiply the digits of * ext1 by the digit (obviously if the digit is zero, there's no * need to iterate over the digits of ext1) */ if (ext2_dig != 0) { /* * loop over digits in the top number, from least to most * significant */ for (idx1 = prec1 ; idx1 != 0 ; ) { /* move to the next digit of the top number */ --idx1; /* move to the next digit of the accumulator */ --out_idx; /* * compute the product of the current digits, and add in * the carry from the last digit, then add in the * current accumulator digit in this position */ dig = (get_dig(ext1, idx1) * ext2_dig) + carry + get_dig(new_ext, out_idx); /* figure the carry to the next digit */ carry = (dig / 10); dig = dig % 10; /* store the new digit */ set_dig(new_ext, out_idx, dig); } } /* * Shift the result accumulator right in preparation for the * next round. One exception: if this is the last (most * significant) digit of the bottom number, and there's no * carry, there's no need to shift the number, since we'd just * normalize away the leading zero anyway */ if (idx2 != 0 || carry != 0) { /* remember the trailing digit that we're going to drop */ trail_val |= trail_dig; trail_dig = get_dig(new_ext, new_prec - 1); /* shift the accumulator */ shift_right(new_ext, 1); /* increase the output exponent */ ++out_exp; /* insert the carry as the lead digit */ set_dig(new_ext, 0, carry); } } /* set the result exponent */ set_exp(new_ext, out_exp); /* * set the sign - if both inputs have the same sign, the output is * positive, otherwise it's negative */ set_neg(new_ext, get_neg(ext1) != get_neg(ext2)); /* if the trailing digit is 5 or greater, round up */ round_for_dropped_digits(new_ext, trail_dig, trail_val); /* normalize the number */ normalize(new_ext); } /* ------------------------------------------------------------------------ */ /* * Compute a quotient into the given buffer. If new_rem_ext is * non-null, we'll save the remainder into this buffer. We calculate * the remainder only to the precision of the quotient. */ void CVmObjBigNum::compute_quotient_into(char *new_ext, char *new_rem_ext, const char *ext1, const char *ext2) { char *rem_ext; uint rem_hdl; char *rem_ext2; uint rem_hdl2; int quo_exp; size_t quo_idx; size_t quo_prec = get_prec(new_ext); size_t dvd_prec = get_prec(ext1); size_t dvs_prec = get_prec(ext2); char *dvs_ext; uint dvs_hdl; char *dvs_ext2; uint dvs_hdl2; int lead_dig_set; int zero_remainder; int acc; size_t rem_prec; /* if the divisor is zero, throw an error */ if (is_zero(ext2)) err_throw(VMERR_DIVIDE_BY_ZERO); /* if the dividend is zero, the result is zero */ if (is_zero(ext1)) { /* set the result to zero */ set_zero(new_ext); /* if they want the remainder, it's zero also */ if (new_rem_ext != 0) set_zero(new_rem_ext); /* we're done */ return; } /* * Calculate the precision we need for the running remainder. We * must retain enough precision in the remainder to calculate exact * differences, so we need the greater of the precisions of the * dividend and the divisor, plus enough extra digits for the * maximum relative shifting. We'll have to shift at most one * extra digit, but use two to be extra safe. */ rem_prec = dvd_prec; if (rem_prec < dvs_prec) rem_prec = dvs_prec; rem_prec += 2; /* * Make sure the precision is at least three, since it simplifies * our digit approximation calculation. */ if (rem_prec < 3) rem_prec = 3; /* * Allocate two temporary registers for the running remainder, and * one more for the multiplied divisor. Note that we allocate the * multiplied divisor at our higher precision so that we don't lose * digits in our multiplier calculations. */ alloc_temp_regs(rem_prec, 3, &rem_ext, &rem_hdl, &rem_ext2, &rem_hdl2, &dvs_ext2, &dvs_hdl2); /* * Allocate another temp register for the shifted divisor. Note * that we need a different precision here, which is why we must * make a separate allocation call */ err_try { /* make the additional allocation */ alloc_temp_regs(dvs_prec, 1, &dvs_ext, &dvs_hdl); } err_catch_disc { /* delete the first group of registers we allocated */ release_temp_regs(3, rem_hdl, rem_hdl2, dvs_hdl2); /* re-throw the exception */ err_rethrow(); } err_end; /* the dividend is the initial value of the running remainder */ copy_val(rem_ext, ext1, TRUE); /* copy the initial divisor into our temp register */ copy_val(dvs_ext, ext2, TRUE); /* we haven't set a non-zero leading digit yet */ lead_dig_set = FALSE; /* * scale the divisor so that the divisor and dividend have the same * exponent, and absorb the multiplier in the quotient */ quo_exp = get_exp(ext1) - get_exp(ext2) + 1; set_exp(dvs_ext, get_exp(ext1)); /* we don't have a zero remainder yet */ zero_remainder = FALSE; /* * if the result of the floating point division is entirely fractional, * the dividend is the remainder, and the quotient is zero */ if (new_rem_ext != 0 && quo_exp <= 0) { /* copy the initial remainder into the output remainder */ copy_val(new_rem_ext, rem_ext, TRUE); /* set the quotient to zero */ set_zero(new_ext); /* we have the result - no need to do any more work */ goto done; } /* * Loop over each digit of precision of the quotient. */ for (quo_idx = 0 ; ; ) { int rem_approx, dvs_approx; int dig_approx; char *tmp; int exp_diff; /* start out with 0 in our digit accumulator */ acc = 0; /* * Get the first few digits of the remainder, and the first few * digits of the divisor, rounding up the divisor and rounding * down the remainder. Compute the quotient - this will give us * a rough guess and a lower bound for the current digit's * value. */ rem_approx = (get_dig(rem_ext, 0)*100 + get_dig(rem_ext, 1)*10 + get_dig(rem_ext, 2)); dvs_approx = (get_dig(dvs_ext, 0)*100 + (dvs_prec >= 2 ? get_dig(dvs_ext, 1) : 0)*10 + (dvs_prec >= 3 ? get_dig(dvs_ext, 2) : 0) + 1); /* adjust for differences in the scale */ exp_diff = get_exp(rem_ext) - get_exp(dvs_ext); if (exp_diff > 0) { /* the remainder is larger - scale it up */ for ( ; exp_diff > 0 ; --exp_diff) rem_approx *= 10; } else if (exp_diff <= -3) { /* * The divisor is larger by more than 10^3, which means that * the result of our integer division is definitely going to * be zero, so there's no point in actually doing the * calculation. This is only a special case because, for * sufficiently large negative differences, we'd have to * multiply our divisor approximation by 10 so many times * that we'd overflow a native int type, at which point we'd * get 0 in the divisor, which would result in a * divide-by-zero. To avoid this, just put 1000 in our * divisor, which is definitely larger than anything we can * have in rem_ext at this point (since it was just three * decimal digits, after all). */ dvs_approx = 1000; } else if (exp_diff < 0) { /* the divisor is larger - scale it up */ for ( ; exp_diff < 0 ; ++exp_diff) dvs_approx *= 10; } /* calculate our initial guess for this digit */ dig_approx = rem_approx / dvs_approx; /* * If this digit is above 2, it'll save us a lot of work to * subtract digit*divisor once, rather than iteratively * deducting the divisor one time per iteration. (It costs us a * little to calculate the digit*divisor product, so we don't * want to do this for very small digit values.) */ if (dig_approx > 2) { /* put the approximate digit in the accumulator */ acc = dig_approx; /* make a copy of the divisor */ copy_val(dvs_ext2, dvs_ext, FALSE); /* * multiply it by the guess for the digit - we know this * will always be less than or equal to the real value * because of the way we did the rounding */ mul_by_long(dvs_ext2, (ulong)dig_approx); /* subtract it from the running remainder */ compute_abs_diff_into(rem_ext2, rem_ext, dvs_ext2); /* if that leaves zero in the remainder, note it */ if (is_zero(rem_ext2)) { zero_remainder = TRUE; break; } /* * swap the remainder registers, since rem_ext2 is now the * new running remainder value */ tmp = rem_ext; rem_ext = rem_ext2; rem_ext2 = tmp; /* * Now we'll finish up the job by subtracting the divisor * from the remainder as many more times as necessary for * the remainder to fall below the divisor. We can't be * exact at this step because we're not considering all of * the digits, but we should only have one more subtraction * remaining at this point. */ } /* * subtract the divisor from the running remainder as many times * as we can */ for ( ; ; ++acc) { int comp_res; /* compare the running remainder to the divisor */ comp_res = compare_abs(rem_ext, dvs_ext); if (comp_res < 0) { /* * the remainder is smaller than the divisor - we have * our result for this digit */ break; } else if (comp_res == 0) { /* note that we have a zero remainder */ zero_remainder = TRUE; /* count one more subtraction */ ++acc; /* we have our result for this digit */ break; } /* subtract it */ compute_abs_diff_into(rem_ext2, rem_ext, dvs_ext); /* * swap the remainder registers, since rem_ext2 is now the * new running remainder value */ tmp = rem_ext; rem_ext = rem_ext2; rem_ext2 = tmp; } /* store this digit of the quotient */ if (quo_idx < quo_prec) { /* store the digit */ set_dig(new_ext, quo_idx, acc); } else if (quo_idx == quo_prec) { /* set the quotient's exponent */ set_exp(new_ext, quo_exp); /* * This is the last digit, which we calculated for rounding * purposes only. If it's greater than 5, round up. If it's * exactly 5 and we have a non-zero remainder, round up. If * it's exactly 5 and we have a zero remainder, round up if the * last digit is odd. */ round_for_dropped_digits(new_ext, acc, !zero_remainder); /* we've reached the rounding digit - we can stop now */ break; } /* * if this is a non-zero digit, we've found a significant * leading digit */ if (acc != 0) lead_dig_set = TRUE; /* * if we've found a significant leading digit, move to the next * digit of the quotient; if not, adjust the quotient exponent * down one, and keep preparing to write the first digit at the * first "column" of the quotient */ if (lead_dig_set) ++quo_idx; else --quo_exp; /* * If we have an exact result (a zero remainder), we're done. * * Similarly, if we've reached the units digit, and the caller * wants the remainder, stop now - the caller wants an integer * result for the quotient, which we now have. * * Similarly, if we've reached the rounding digit, and the * caller wants the remainder, skip the rounding step - the * caller wants an unrounded result for the quotient so that the * quotient times the divisor plus the remainder equals the * dividend. */ if (zero_remainder || (new_rem_ext != 0 && ((int)quo_idx == quo_exp || quo_idx == quo_prec))) { /* zero any remaining digits of the quotient */ for ( ; quo_idx < quo_prec ; ++quo_idx) set_dig(new_ext, quo_idx, 0); /* set the quotient's exponent */ set_exp(new_ext, quo_exp); /* that's it */ break; } /* divide the divisor by ten */ set_exp(dvs_ext, get_exp(dvs_ext) - 1); } /* store the remainder if the caller wants the value */ if (new_rem_ext != 0) { /* save the remainder into the caller's buffer */ if (zero_remainder) { /* the remainder is exactly zero */ set_zero(new_rem_ext); } else { /* copy the running remainder */ copy_val(new_rem_ext, rem_ext, TRUE); /* the remainder has the same sign as the dividend */ set_neg(new_rem_ext, get_neg(ext1)); /* normalize the remainder */ normalize(new_rem_ext); } } /* * the quotient is positive if both the divisor and dividend have * the same sign, negative if they're different */ set_neg(new_ext, get_neg(ext1) != get_neg(ext2)); /* normalize the quotient */ normalize(new_ext); done: /* release the temporary registers */ release_temp_regs(4, rem_hdl, rem_hdl2, dvs_hdl, dvs_hdl2); } /* ------------------------------------------------------------------------ */ /* * The BigNumber cache is implemented as an ordinary static. This can be * shared among VM instances (since it stores mathematical constants and * provides some simple memory management facilities), so it's not * necessary to use the VM global scheme (which is only needed for data * that must be isolated per VM instance). This simplifies interfaces to * many of the low-level routines by letting us omit the VMG_ parameter, * and allows many BigNumber features to be accessed without having to link * in the whole VM global subsystem. This last bit lets us use BigNumber * calculations in the compiler, for example, which is useful for constant * folding. */ CVmBigNumCache *S_bignum_cache = 0; /* number of references to the cache */ static int S_bignum_cache_refs = 0; /* ------------------------------------------------------------------------ */ /* * Cache creation/deletion */ void CVmObjBigNum::init_cache() { if (S_bignum_cache_refs++ == 0) S_bignum_cache = new CVmBigNumCache(32); } void CVmObjBigNum::term_cache() { if (--S_bignum_cache_refs == 0) { delete S_bignum_cache; S_bignum_cache = 0; } } /* ------------------------------------------------------------------------ */ /* * Allocate a temporary register */ char *CVmObjBigNum::alloc_temp_reg(size_t prec, uint *hdl) { /* allocate a register with enough space for the desired precision */ char *p = S_bignum_cache->alloc_reg(calc_alloc(prec), hdl); /* if that succeeded, initialize the memory */ if (p != 0) { /* set the desired precision */ set_prec(p, prec); /* initialize the flags */ p[VMBN_FLAGS] = 0; } /* return the register memory */ return p; } /* ------------------------------------------------------------------------ */ /* * release a temporary register */ void CVmObjBigNum::release_temp_reg(uint hdl) { /* release the register to the cache */ S_bignum_cache->release_reg(hdl); } /* ------------------------------------------------------------------------ */ /* * Allocate a group of temporary registers */ void CVmObjBigNum::alloc_temp_regs(size_t prec, size_t cnt, ...) { va_list marker; size_t i; int failed; char **ext_ptr; uint *hdl_ptr; /* set up to read varargs */ va_start(marker, cnt); /* no failures yet */ failed = FALSE; /* scan the varargs list */ for (i = 0 ; i < cnt ; ++i) { /* get the next argument */ ext_ptr = va_arg(marker, char **); hdl_ptr = va_arg(marker, uint *); /* allocate a register */ *ext_ptr = alloc_temp_reg(prec, hdl_ptr); /* if this allocation failed, note it, but keep going for now */ if (*ext_ptr == 0) failed = TRUE; } /* done reading argument */ va_end(marker); /* if we had any failures, free all of the registers we allocated */ if (failed) { /* restart reading the varargs */ va_start(marker, cnt); /* scan the varargs and free the successfully allocated registers */ for (i = 0 ; i < cnt ; ++i) { /* get the next argument */ ext_ptr = va_arg(marker, char **); hdl_ptr = va_arg(marker, uint *); /* free this register if we successfully allocated it */ if (*ext_ptr != 0) release_temp_reg(*hdl_ptr); } /* done reading varargs */ va_end(marker); /* throw the error */ err_throw(VMERR_BIGNUM_NO_REGS); } } /* ------------------------------------------------------------------------ */ /* * Release a block of temporary registers */ void CVmObjBigNum::release_temp_regs(size_t cnt, ...) { /* start reading the varargs */ va_list marker; va_start(marker, cnt); /* scan the varargs and free the listed registers */ for (size_t i = 0 ; i < cnt ; ++i) { uint hdl; /* get the next handle */ hdl = va_arg(marker, uint); /* free this register */ release_temp_reg(hdl); } /* done reading varargs */ va_end(marker); } /* ------------------------------------------------------------------------ */ /* * Round the extension to the nearest integer */ void CVmObjBigNum::round_to_int(char *ext) { /* * the exponent is equal to the number of digits before the decimal * point, so that's the number of digits we want to keep */ round_to(ext, get_exp(ext)); } /* * Round the extension to the given number of digits */ void CVmObjBigNum::round_to(char *ext, int digits) { /* if we're dropping digits, and we need to round up, round up */ int prec = get_prec(ext); if (digits < prec) { /* note if we need to round up */ int r = get_round_dir(ext, digits); /* zero the digits beyond the last one we're keeping */ for (int i = digits ; i < prec ; ++i) set_dig(ext, i, 0); /* round up if necessary */ if (r) round_up_abs(ext, digits); } } /* * get the OR sum of digits from the given starting place to least * significant */ int CVmObjBigNum::get_ORdigs(const char *ext, int d) { int sum, prec = get_prec(ext); for (sum = 0 ; d < prec ; sum |= get_dig(ext, d++)) ; return sum; } /* * Determine the direction to round a value to the given number of digits. * Returns 1 to round up, 0 to round down - this is effectively the value * to add at the last digit we're keeping. */ int CVmObjBigNum::get_round_dir(const char *ext, int digits) { /* if the dropped digit is beyond the precision, there's no rounding */ int prec = get_prec(ext); if (digits >= prec) return 0; /* we can't round to negative digits */ if (digits < 0) digits = 0; /* * if the first dropped digit is greater than 5, round up; if it's less * than 5, round down; otherwise we have to break the tie */ int d = get_dig(ext, digits); if (d > 5) return 1; if (d < 5) return 0; /* * it's a 5 - if there are any non-zero digits after this point, the * value is greater than 5, so round up */ for (int i = digits + 1 ; i < prec ; ++i) { /* if it's a non-zero digit, round up */ if (get_dig(ext, i) != 0) return 1; } /* * It's exactly 5000...., so we need to break the tie by rounding to * the nearest even digit in the last digit we're keeping. Get that * last digit. */ d = (digits > 0 ? get_dig(ext, digits - 1) : 0); /* * If the digit is already even, round down (return 0) to stay at that * even value. If it's odd, round up (return 1) to get to the next * even digit. (d & 1) happens to be 1 if it's odd, 0 if it's even. */ return d & 1; } /* * Round a number based on dropped digits lost during a calculation. * 'trail_dig' is the most significant dropped digit, and 'trail_val' is * the "|" sum of all of the less significant dropped digits. */ void CVmObjBigNum::round_for_dropped_digits( char *ext, int trail_dig, int trail_val) { if (trail_dig > 5 || (trail_dig == 5 && trail_val > 0) || (trail_dig == 5 && trail_val == 0 && (get_dig(ext, get_prec(ext)-1) & 1) != 0)) round_up_abs(ext); } /* * Round a number up. This adds 1 starting at the least significant digit * of the number. */ void CVmObjBigNum::round_up_abs(char *ext) { round_up_abs(ext, get_prec(ext)); } /* * Round a number up. This adds 1 starting at the least significant digit * we're keeping. */ void CVmObjBigNum::round_up_abs(char *ext, int keep_digits) { /* start with a "carry" into the least significant digit */ int carry = TRUE; /* propagate the carry through the number's digits */ for (int idx = keep_digits ; idx != 0 ; ) { /* move to the next more significant digit */ --idx; /* add the carry */ int d = get_dig(ext, idx); if (d < 9) { /* bump it up */ set_dig(ext, idx, d + 1); /* no carrying required, so we can stop now */ carry = FALSE; break; } else { /* it's a '9', so bump it up to '0' and carry to the next digit */ set_dig(ext, idx, 0); } } /* * If we carried out of the top digit, we must have had all nines, in * which case we now have all zeros. Put a 1 in the first digit and * increase the exponent to renormalize. */ if (carry) { /* shift the mantissa */ shift_right(ext, 1); /* increase the exponent */ set_exp(ext, get_exp(ext) + 1); /* set the lead digit to 1 */ set_dig(ext, 0, 1); } /* we know the value is non-zero now */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; } /* ------------------------------------------------------------------------ */ /* * Copy a value, extending with zeros if expanding, or truncating or * rounding, as desired, if the precision changes */ void CVmObjBigNum::copy_val(char *dst, const char *src, int round) { size_t src_prec = get_prec(src); size_t dst_prec = get_prec(dst); /* check to see if we're growing or shrinking */ if (dst_prec > src_prec) { /* * it's growing - copy the entire old mantissa, plus the flags, * sign, and exponent */ memcpy(dst + VMBN_EXP, src + VMBN_EXP, (VMBN_MANT - VMBN_EXP) + (src_prec + 1)/2); /* clear the balance of the mantissa */ memset(dst + VMBN_MANT + (src_prec + 1)/2, 0, (dst_prec + 1)/2 - (src_prec + 1)/2); /* * clear the digit just after the last digit we copied - we might * have missed this with the memset, since it only deals with * even-numbered pairs of digits */ set_dig(dst, src_prec, 0); } else { /* it's shrinking - truncate the mantissa to the new length */ memcpy(dst + VMBN_EXP, src + VMBN_EXP, (VMBN_MANT - VMBN_EXP) + (dst_prec + 1)/2); /* check for rounding */ if (round && get_round_dir(src, dst_prec)) round_up_abs(dst); } } /* ------------------------------------------------------------------------ */ /* * Multiply by an integer constant value */ void CVmObjBigNum::mul_by_long(char *ext, unsigned long val) { size_t idx; size_t prec = get_prec(ext); unsigned long carry = 0; /* * start at the least significant digit and work up through the digits */ for (idx = prec ; idx != 0 ; ) { unsigned long prod; /* move to the next digit */ --idx; /* * compute the product of this digit and the given value, and add * in the carry from the last digit */ prod = (val * get_dig(ext, idx)) + carry; /* set this digit to the residue mod 10 */ set_dig(ext, idx, prod % 10); /* carry the rest */ carry = prod / 10; } /* if we have a carry left over, shift it in */ int dropped_dig = 0, dropped_val = 0; while (carry != 0) { /* * Note if the previous dropped digit is non-zero, then note the * newly dropped digit. dropped_val tells us if there's anything * non-zero after the last digit, in case the last digit turns out * to be a 5. */ dropped_val |= dropped_dig; dropped_dig = get_dig(ext, prec - 1); /* shift the number and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); /* shift in this digit of the carry */ set_dig(ext, 0, carry % 10); /* take it out of the carry */ carry /= 10; } /* * round up if the dropped digits are >5000..., or they're exactly * 5000... and the last digit we're keeping is odd */ round_for_dropped_digits(ext, dropped_dig, dropped_val); /* normalize the result */ normalize(ext); } /* * Divide the magnitude of the number by 2^32. This is a special case of * div_by_long() for splitting a number into 32-bit chunks. Returns with * the quotient in 'ext', and the remainder in 'remp'. */ void CVmObjBigNum::div_by_2e32(char *ext, uint32_t *remp) { size_t in_idx; size_t out_idx; int sig; size_t prec = get_prec(ext); unsigned long rem = 0; int exp = get_exp(ext); /* if it's entirely fractional, the result is 0 with remainder 0 */ if (exp <= 0) { *remp = 0; set_zero(ext); return; } /* start at the most significant digit and work down */ for (rem = 0, sig = FALSE, in_idx = out_idx = 0 ; out_idx < prec ; ++in_idx) { /* * Multiply the remainder by 10. 10 == (2+8), so this is the sum * of rem<<1 and rem<<3. This is handy because it makes it * relatively easy to calculate the overflow from the 32-bit * accumulator on 32-bit platforms: the overflow is the sum of the * high bit and the high three bits, plus the carry from the low * part. The low part has a carry of 1 if rem<<1 + rem<<3 exceeds * 0xffffffff, which we can calculate in 32 bits as rem<<1 > * 0xffffffff - rem<<3. */ ulong rem1 = (rem << 1) & 0xffffffff; ulong rem3 = (rem << 3) & 0xffffffff; ulong hi = ((rem >> 31) & 0x00000001) + ((rem >> 29) & 0x00000007) + (rem1 > 0xffffffff - rem3 ? 1 : 0); rem = (rem1 + rem3) & 0xffffffff; /* * Add the current digit into the remainder. We again need to * carry the overflow into the high part. */ ulong dig = (in_idx < prec ? get_dig(ext, in_idx) : 0); hi += dig > 0xffffffff - rem ? 1 : 0; rem = (rem + dig) & 0xffffffff; /* * We now have the quotient of rem/2^32 in 'hi', and the remainder * in 'rem'. */ int quo = (int)hi; /* if the quotient is non-zero, we've found a significant digit */ if (quo != 0) sig = TRUE; /* * if we've found a significant digit, store it; otherwise, just * reduce the exponent to account for an implied leading zero that * we won't actually store */ if (sig) { /* store the digit */ set_dig(ext, out_idx++, quo); /* * if we've reached the decimal place, stop here, since we're * doing integer division */ if ((int)out_idx == exp) break; } else { /* all leading zeros so far - adjust the exponent */ set_exp(ext, --exp); /* if this leaves us with a fractional result, we're done */ if (exp == 0) break; } /* * If we've reached the last input digit and the remainder is zero, * we're done - fill out the rest of the number with trailing zeros * and stop looping. If we've reached the decimal point in the * output, stop here, since we're doing an integer division. */ if (rem == 0 && in_idx >= prec) { /* if we don't have any significant digits, we have a zero */ if (!sig) { set_zero(ext); out_idx = prec; } /* we have our result */ break; } } /* fill out the rest of the number with zeros */ for ( ; out_idx < prec ; ++out_idx) set_dig(ext, out_idx, 0); /* normalize the result */ normalize(ext); /* pass back the remainder */ *remp = rem; } /* * Divide by an integer constant value. Note that 'val' must not exceed * ULONG_MAX/10, because we have to be able to compute intermediate integer * dividend values up to 10*val. */ void CVmObjBigNum::div_by_long(char *ext, unsigned long val, unsigned long *remp) { size_t in_idx; size_t out_idx; int sig; size_t prec = get_prec(ext); unsigned long rem = 0; int exp = get_exp(ext); /* * if we're doing integer division, and the dividend is entirely * fractional, the result is 0 with remainder 0 */ if (remp != 0 && exp <= 0) { *remp = 0; set_zero(ext); return; } /* start at the most significant digit and work down */ for (rem = 0, sig = FALSE, in_idx = out_idx = 0 ; out_idx < prec ; ++in_idx) { /* * shift this digit into the remainder - if we're past the end of * the number, shift in an implied trailing zero */ rem *= 10; rem += (in_idx < prec ? get_dig(ext, in_idx) : 0); /* calculate the quotient and the next remainder */ long quo = rem / val; rem %= val; /* if the quotient is non-zero, we've found a significant digit */ if (quo != 0) sig = TRUE; /* * if we've found a significant digit, store it; otherwise, just * reduce the exponent to account for an implied leading zero that * we won't actually store */ if (sig) { /* store the digit */ set_dig(ext, out_idx++, (int)quo); /* * if we've reached the decimal place, and the caller wants the * remainder, stop here - the caller wants the integer quotient * and remainder rather than the full quotient */ if (remp != 0 && (int)out_idx == exp) break; } else { /* all leading zeros so far - adjust the exponent */ set_exp(ext, --exp); /* * if we're doing integer division, and this leaves us with a * fractional result, we're done */ if (remp != 0 && exp == 0) break; } /* * if we've reached the last input digit and the remainder is zero, * we're done - fill out the rest of the number with trailing zeros * and stop looping */ if (rem == 0 && in_idx >= prec) { /* check to see if we have any significant digits */ if (!sig) { /* no significant digits - the result is zero */ set_zero(ext); out_idx = prec; } /* we have our result */ break; } } /* fill out any remaining output digits with zeros */ for ( ; out_idx < prec ; ++out_idx) set_dig(ext, out_idx, 0); /* pass back the remainder if desired */ if (remp != 0) { /* hand back the integer remainder */ *remp = rem; } else { /* * We're computing the full-precision quotient, not the quotient * and remainder. Round up if the remainder is more than half the * divisor, or it's exactly half the divisor and the last digit is * odd. * * (This is effectively testing to see if the next digits are * 5000... or higher. The next digit is the whole part of * remainder*10/val, so it's >=5 if remainder/val >= 0.5, which is * to say remainder >= val/2, or remainder*2 >= val.) */ if (rem*2 > val || (rem*2 == val && (get_dig(ext, prec-1) & 1))) round_up_abs(ext); } /* normalize the result */ normalize(ext); } /* ------------------------------------------------------------------------ */ /* * Normalize a number - shift it so that the first digit is non-zero. If * the number is zero, set the exponent to zero and clear the sign bit. */ void CVmObjBigNum::normalize(char *ext) { int idx; int prec = get_prec(ext); int all_zero; int nonzero_idx = 0; /* no work is necessary for anything but ordinary numbers */ if (is_nan(ext)) return; /* check for an all-zero mantissa */ for (all_zero = TRUE, idx = 0 ; idx < prec ; ++idx) { /* check this digit */ if (get_dig(ext, idx) != 0) { /* note the location of the first non-zero digit */ nonzero_idx = idx; /* note that the number isn't all zeros */ all_zero = FALSE; /* no need to keep searching */ break; } } /* if it's zero or underflows, set the canonical zero format */ if (all_zero || get_exp(ext) - nonzero_idx < -32768) { /* set the value to zero */ set_zero(ext); } else { /* clear the zero flag */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; /* shift the mantissa left to make the first digit non-zero */ if (nonzero_idx != 0) shift_left(ext, nonzero_idx); /* decrease the exponent to account for the mantissa shift */ set_exp(ext, get_exp(ext) - nonzero_idx); } } /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ /* * Shift the value left (multiply by 10^shift) */ void CVmObjBigNum::shift_left(char *ext, unsigned int shift) { size_t prec = get_prec(ext); size_t i; /* do nothing with a zero shift */ if (shift == 0) return; /* if it's an even shift, it's especially easy */ if ((shift & 1) == 0) { /* simply move the bytes left by the required amount */ for (i = 0 ; i + shift/2 < (prec+1)/2 ; ++i) ext[VMBN_MANT + i] = ext[VMBN_MANT + i + shift/2]; /* zero the remaining digits */ for ( ; i < (prec+1)/2 ; ++i) ext[VMBN_MANT + i] = 0; /* * be sure to zero the last digit - if we have an odd precision, we * will have copied the garbage digit from the final half-byte */ set_dig(ext, prec - shift, 0); } else { /* apply the shift to each digit */ for (i = 0 ; i + shift < prec ; ++i) { unsigned int dig; /* get this source digit */ dig = get_dig(ext, i + shift); /* set this destination digit */ set_dig(ext, i, dig); } /* zero the remaining digits */ for ( ; i < prec ; ++i) set_dig(ext, i, 0); } } /* * Shift the value right (divide by 10^shift) */ void CVmObjBigNum::shift_right(char *ext, unsigned int shift) { size_t prec = get_prec(ext); size_t i; /* if it's an even shift, it's especially easy */ if ((shift & 1) == 0) { /* simply move the bytes left by the required amount */ for (i = (prec+1)/2 ; i > shift/2 ; --i) ext[VMBN_MANT + i-1] = ext[VMBN_MANT + i-1 - shift/2]; /* zero the leading digits */ for ( ; i > 0 ; --i) ext[VMBN_MANT + i-1] = 0; } else { /* apply the shift to each digit */ for (i = prec ; i > shift ; --i) { unsigned int dig; /* get this source digit */ dig = get_dig(ext, i-1 - shift); /* set this destination digit */ set_dig(ext, i-1, dig); } /* zero the remaining digits */ for ( ; i >0 ; --i) set_dig(ext, i-1, 0); } } /* * Increment a number */ void CVmObjBigNum::increment_abs(char *ext) { size_t idx; int exp = get_exp(ext); int carry; /* start at the one's place, if represented */ idx = (exp <= 0 ? 0 : (size_t)exp); /* * if the units digit is to the right of the number (i.e., the number's * scale is large), there's nothing to do */ if (idx > get_prec(ext)) return; /* increment digits */ for (carry = TRUE ; idx != 0 ; ) { int dig; /* move to the next digit */ --idx; /* get the digit value */ dig = get_dig(ext, idx); /* increment it, checking for carry */ if (dig == 9) { /* increment it to zero and keep going to carry */ set_dig(ext, idx, 0); } else { /* increment this digit */ set_dig(ext, idx, dig + 1); /* there's no carry out */ carry = FALSE; /* done */ break; } } /* if we carried past the end of the number, insert the leading 1 */ if (carry) { /* * if we still haven't reached the units position, shift right * until we do */ while (get_exp(ext) < 0) { /* shift it right and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); } /* shift the number right and adjust the exponent */ shift_right(ext, 1); set_exp(ext, get_exp(ext) + 1); /* insert the leading 1 */ set_dig(ext, 0, 1); } /* we know the value is now non-zero */ ext[VMBN_FLAGS] &= ~VMBN_F_ZERO; } /* * Determine if the fractional part is zero */ int CVmObjBigNum::is_frac_zero(const char *ext) { size_t idx; int exp = get_exp(ext); size_t prec = get_prec(ext); /* start at the first fractional digit, if represented */ idx = (exp <= 0 ? 0 : (size_t)exp); /* scan the digits for a non-zero digit */ for ( ; idx < prec ; ++idx) { /* if this digit is non-zero, the fraction is non-zero */ if (get_dig(ext, idx) != 0) return FALSE; } /* we didn't find any non-zero fractional digits */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Convert to an integer value */ int32_t CVmObjBigNum::ext_to_int(const char *ext, int &ov) { /* get the magnitude */ ov = FALSE; uint32_t m = convert_to_int_base(ext, ov); /* apply the sign and check limits */ if (get_neg(ext)) { /* * a T3 VM int is a 32-bit signed value (irrespective of the local * platform int size), so the magnitude of a negative value can't * exceed 2147483648 */ if (m > 2147483648U) ov = TRUE; /* negate the number and return it */ return -(long)m; } else { /* * a T3 VM positive int value can't exceed 2147483647L (regardless * of the local platform int size) */ if (m > 2147483647U) ov = TRUE; /* return the value */ return (long)m; } } /* * Convert to unsigned int */ uint32_t CVmObjBigNum::convert_to_uint(int &ov) const { /* negative numbers can't be converted to unsigned */ ov = get_neg(ext_); /* return the magnitude */ return convert_to_int_base(ext_, ov); } /* * Get the magnitude of an integer value, ignoring the sign */ uint32_t CVmObjBigNum::convert_to_int_base(const char *ext, int &ov) { size_t prec = get_prec(ext); int exp = get_exp(ext); size_t idx; size_t stop_idx; int round_inc; /* start the accumulator at zero */ uint32_t acc = 0; /* * if the exponent is negative, it means that the first non-zero digit * is in the second position after the decimal point, so even after * rounding, the result will always be zero */ if (exp < 0) return 0; /* get the rounding direction for truncating at the decimal point */ round_inc = get_round_dir(ext, exp); /* stop at the first fractional digit */ if (exp <= 0) { /* all digits are fractional */ stop_idx = 0; } else if ((size_t)exp < prec) { /* stop at the first fractional digit */ stop_idx = (size_t)exp; } else { /* all of the digits are in the whole part */ stop_idx = prec; } /* convert the integer part digit by digit */ if (stop_idx > 0) { /* loop over digits */ for (idx = 0 ; idx < stop_idx ; ++idx) { /* get this digit */ int dig = get_dig(ext, idx); /* make sure that shifting the accumulator won't overflow */ ov |= (acc > UINT32MAXVAL/10); /* shift the accumulator */ acc *= 10; /* make sure this digit won't overflow the 32-bit VM int type */ ov |= (acc > (UINT32MAXVAL - dig)); /* add the digit */ acc += dig; } } /* make sure rounding won't overflow */ ov |= (acc > UINT32MAXVAL - round_inc); /* return the result adjusted for rounding */ return acc + round_inc; } /* * Convert to a 64-bit portable representation, signed */ void CVmObjBigNum::wp8s(char *buf, int &ov) const { /* note if the value is negative */ int neg = get_neg(ext_); /* generate the absolute value */ wp8abs(buf, ov); /* if it's negative, compute the 2's complement of the buffer */ if (neg) { /* take the 2's complement */ twos_complement_p8((unsigned char *)buf); /* if it didn't come out negative, we have an overflow */ ov |= ((buf[7] & 0x80) == 0); } else { /* positive - the sign bit is set, we overflowed */ ov |= (buf[7] & 0x80); } } /* * Convert to a 64-bit portable representation, unsigned */ void CVmObjBigNum::wp8(char *buf, int &ov) const { /* generate the absolute value */ wp8abs(buf, ov); /* * if the value is negative, we can't represent it as unsigned; take * the two's complement in case they want the truncated version of the * value, and set the overflow flag */ if (get_neg(ext_)) { ov = TRUE; twos_complement_p8((unsigned char *)buf); } } /* * generate the portable 64-bit integer representation of the absolute * value of the number */ void CVmObjBigNum::wp8abs(char *buf, int &ov) const { /* * as a rough cut, if the number is greater than 10^30, it's definitely * too big for a 64-bit integer buffer */ ov = (get_exp(ext_) > 30); /* * Make a local copy of the value - we need at most 20 digits of * precision, since we only want the integer part. */ char tmp[5 + 20] = { 20, 0, 0, 0, 0 }; copy_val(tmp, ext_, TRUE); /* * Divide the value by 2^32. This splits the value into two 32-bit * halves for us, which are then easy to arrange into the buffer with * integer arithmetic. */ uint32_t lo, hi; div_by_2e32(tmp, &lo); hi = convert_to_int_base(tmp, ov); /* * store the low part in the first four bytes, the high part in the * next four bytes */ oswp4(buf, lo); oswp4(buf + 4, hi); } /* * Encode the integer portion of the number as a BER compressed integer. */ void CVmObjBigNum::encode_ber(char *buf, size_t buflen, size_t &outlen, int &ov) { /* BER can only store unsigned integers */ ov = get_neg(ext_); /* clear the output buffer */ outlen = 0; /* make a temporary copy of the value in a temp register */ uint thdl; char *tmp = alloc_temp_reg(get_prec(ext_), &thdl); copy_val(tmp, ext_, FALSE); /* keep going until the number is zero or we run out of space */ int z; while (!(z = is_zero(tmp)) && outlen < buflen) { /* get the low-order 7 bits by getting the remainder mod 128 */ ulong rem; div_by_long(tmp, 128, &rem); /* store it */ buf[outlen++] = (char)rem; } /* release the temporary register */ release_temp_reg(thdl); /* if we ran out of buffer before we ran out of digits, we overflowed */ ov |= (!z); /* if we wrote nothing, write a trivial zero value */ if (outlen == 0) buf[outlen++] = 0; /* * We stored the bytes from least significant to most significant, but * the standard format is the other way around. Reverse the bytes. */ int i, j; for (i = 0, j = (int)outlen - 1 ; i < j ; ++i, --j) { char tmpc = buf[i]; buf[i] = buf[j]; buf[j] = tmpc; } /* set the high bit in every byte except the last one */ for (i = 0 ; i < (int)outlen - 1 ; ++i) buf[i] |= 0x80; } /* * Calculate the 2's complement of a portable 8-byte integer buffer */ void CVmObjBigNum::twos_complement_p8(unsigned char *p) { size_t i; int carry; for (i = 0, carry = 1 ; i < 8 ; ++i) { p[i] = (~p[i] + carry) & 0xff; carry &= (p[i] == 0); } } /* * Convert to double */ double CVmObjBigNum::ext_to_double(const char *ext) { /* note the precision and sign */ size_t prec = get_prec(ext); int is_neg = get_neg(ext); /* if we're not a number (INF, NAN), it's an error */ if (get_type(ext) != VMBN_T_NUM) err_throw(VMERR_NO_DOUBLE_CONV); /* * if our absolute value is too large to store in a double, throw an * overflow error */ if (compare_abs(ext, cache_dbl_max()) > 1) err_throw(VMERR_NUM_OVERFLOW); /* * Our value is represented as .abcdef * 10^exp, where a is our first * digit, b is our second digit, etc. Start off the accumulator with * our most significant digit, a. The actual numeric value of this * digit is a*10^(exp-1) - one less than the exponent, because of the * implied decimal point at the start of the mantissa. */ double base = pow(10.0, get_exp(ext) - 1); double acc = get_dig(ext, 0) * base; /* * Now add in the remaining digits, starting from the second most * significant. * * We might have more precision than a double can hold. Digits added * beyond the precision of the double will have no effect on the double * value, since a double will naturally drop digits at the less * significant end when we exceed its precision. There's no point in * adding digits beyond that point. The maximum number of digits a * native double can hold is given by the float.h constant DBL_DIG; go * a couple of digits beyond this anyway, since many float packages * perform intermediate calculations at slightly higher precision and * can thus benefit from an extra digit or two for rounding purposes. */ size_t max_digits = (prec > DBL_DIG + 2 ? DBL_DIG + 2 : prec); for (size_t idx = 1 ; idx < max_digits ; ++idx) { /* get this digit */ int dig = get_dig(ext, idx); /* adjust the exponent base for this digit */ base /= 10.0; /* add the digit */ acc += dig * base; } /* apply the sign */ if (is_neg) acc = -acc; /* return the result */ return acc; } /* ------------------------------------------------------------------------ */ /* * Cache DBL_MAX */ const char *CVmObjBigNum::cache_dbl_max() { /* get the cache register; use the platform precision for a double */ size_t prec = DBL_DIG; int expanded; char *ext = S_bignum_cache->get_dbl_max_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we got a previously cached value, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* we allocated or reallocated the register, so set the new precision */ set_prec(ext, prec); /* store DBL_MAX in the register */ set_double_val(ext, DBL_MAX); /* return the register */ return ext; } /* * Cache DBL_MIN */ const char *CVmObjBigNum::cache_dbl_min() { /* get the cache register; use the platform precision for a double */ size_t prec = DBL_DIG; int expanded; char *ext = S_bignum_cache->get_dbl_min_reg(calc_alloc(prec), &expanded); /* if that failed, throw an error */ if (ext == 0) err_throw(VMERR_OUT_OF_MEMORY); /* if we got a previously cached value, return it */ if (!expanded && get_prec(ext) >= prec) return ext; /* we allocated or reallocated the register, so set the new precision */ set_prec(ext, prec); /* store DBL_MIN in the register */ set_double_val(ext, DBL_MIN); /* return the register */ return ext; } /* ------------------------------------------------------------------------ */ /* * Internal cache. The cache stores frequently used constants (pi, e, * DBL_MAX), and also provides memory for temporary registers for * intermediate values in calculations. */ /* * initialize */ CVmBigNumCache::CVmBigNumCache(size_t max_regs) { CVmBigNumCacheReg *p; size_t i; /* allocate our register array */ reg_ = (CVmBigNumCacheReg *)t3malloc(max_regs * sizeof(reg_[0])); /* remember the number of registers */ max_regs_ = max_regs; /* clear the list heads */ free_reg_ = 0; unalloc_reg_ = 0; /* we haven't actually allocated any registers yet - clear them out */ for (p = reg_, i = max_regs ; i != 0 ; ++p, --i) { /* clear this register descriptor */ p->clear(); /* link it into the unallocated list */ p->nxt_ = unalloc_reg_; unalloc_reg_ = p; } /* we haven't allocated the constants registers yet */ ln10_.clear(); ln2_.clear(); pi_.clear(); e_.clear(); dbl_max_.clear(); dbl_min_.clear(); } /* * delete */ CVmBigNumCache::~CVmBigNumCache() { CVmBigNumCacheReg *p; size_t i; /* delete each of our allocated registers */ for (p = reg_, i = max_regs_ ; i != 0 ; ++p, --i) p->free_mem(); /* free the register list array */ t3free(reg_); /* free the constant value registers */ ln10_.free_mem(); ln2_.free_mem(); pi_.free_mem(); e_.free_mem(); dbl_max_.free_mem(); dbl_min_.free_mem(); } /* * Allocate a register */ char *CVmBigNumCache::alloc_reg(size_t siz, uint *hdl) { CVmBigNumCacheReg *p; CVmBigNumCacheReg *prv; /* * search the free list for an available register satisfying the size * requirements */ for (p = free_reg_, prv = 0 ; p != 0 ; prv = p, p = p->nxt_) { /* if it satisfies the size requirements, return it */ if (p->siz_ >= siz) { /* unlink it from the free list */ if (prv == 0) free_reg_ = p->nxt_; else prv->nxt_ = p->nxt_; /* it's no longer in the free list */ p->nxt_ = 0; /* return it */ *hdl = (uint)(p - reg_); return p->buf_; } } /* * if there's an unallocated register, allocate it and use it; * otherwise, reallocate the smallest free register */ if (unalloc_reg_ != 0) { /* use the first unallocated register */ p = unalloc_reg_; /* unlink it from the list */ unalloc_reg_ = unalloc_reg_->nxt_; } else if (free_reg_ != 0) { CVmBigNumCacheReg *min_free_p; CVmBigNumCacheReg *min_free_prv = 0; /* search for the smallest free register */ for (min_free_p = 0, p = free_reg_, prv = 0 ; p != 0 ; prv = p, p = p->nxt_) { /* if it's the smallest so far, remember it */ if (min_free_p == 0 || p->siz_ < min_free_p->siz_) { /* remember it */ min_free_p = p; min_free_prv = prv; } } /* use the smallest register we found */ p = min_free_p; /* unlink it from the list */ if (min_free_prv == 0) free_reg_ = p->nxt_; else min_free_prv->nxt_ = p->nxt_; } else { /* there are no free registers - return failure */ return 0; } /* * we found a register that was either previously unallocated, or was * previously allocated but was too small - allocate or reallocate the * register at the new size */ p->alloc_mem(siz); /* return the new register */ *hdl = (uint)(p - reg_); return p->buf_; } /* * Release a register */ void CVmBigNumCache::release_reg(uint hdl) { CVmBigNumCacheReg *p = ®_[hdl]; /* add the register to the free list */ p->nxt_ = free_reg_; free_reg_ = p; } /* * Release all registers */ void CVmBigNumCache::release_all() { size_t i; /* mark each of our registers as not in use */ for (i = 0 ; i < max_regs_ ; ++i) release_reg(i); } frobtads-1.2.3/tads3/vmpoolsl.h0000644000175000001440000000517111321357670015557 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmpoolsl.h - pool manager selector definitions Function This header makes some definitions that vary according to which type of memory pool manager is selected. This can be included without including the entire memory pool subsystem header. Notes As of this writing, only the all-in-memory pool manager is supported. The swapping pool manager is no longer supported and is not planned to be supported in the future. However, we are retaining the selection mechanism for now, in the event that we want to restore the swapping pool, or add other pool configuration options, in the future (which seems unlikely, but still). Modified 08/17/02 MJRoberts - Creation */ #ifndef VMPOOLSL_H #define VMPOOLSL_H /* ------------------------------------------------------------------------ */ /* * Conditionally include code needed for the swapping pool manager. When * the swapping pool manager is used, code that translates memory addresses * must be mindful of the fact that translating one memory address can * render previously-translated addresses invalid. Such code is * unnecessary with non-swapping pool managers. * * To include the swapping pool manager in the build, you must #define * VM_SWAPPING_POOL globally for all modules - normally, this should be * done in the CFLAGS in the makefile, or with the equivalent local * convention, so that every module has the symbol defined. */ /* ------------------------------------------------------------------------ */ #ifdef VM_SWAPPING_POOL /* * swapping mode - include swapping-specific code */ #define VM_IF_SWAPPING_POOL(x) x /* the final pool manager subclass - use the swapping pool class */ #define CVmPool_CLASS CVmPoolSwap /* ------------------------------------------------------------------------ */ #else /* VM_SWAPPING_POOL */ /* * non-swapping mode - omit swapping-specific code */ #define VM_IF_SWAPPING_POOL(x) /* * The non-swapping pool comes in two varieties. Select the FLAT or PAGED * pool, as desired. The FLAT pool is slightly faster, but it doesn't have * any dynamic memory capabilities, which are required for the debugger. */ #ifdef VM_FLAT_POOL /* select the non-swapped FLAT pool */ #define CVmPool_CLASS CVmPoolFlat #else /* VM_FLAT_POOL */ /* select the non-swapped PAGED pool */ #define CVmPool_CLASS CVmPoolInMem #endif /* VM_FLAT_POOL */ #endif /* VM_SWAPPING_POOL */ #endif /* VMPOOLSL_H */ frobtads-1.2.3/tads3/vmbytarr.cpp0000644000175000001440000022271211736121455016107 0ustar realncusers/* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbytarr.cpp - TADS 3 ByteArray intrinsic class Function Notes Modified 06/05/01 MJRoberts - Creation */ #include #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" #include "vmbytarr.h" #include "vmbif.h" #include "vmfile.h" #include "vmerrnum.h" #include "vmerr.h" #include "vmstack.h" #include "vmmeta.h" #include "vmundo.h" #include "vmrun.h" #include "charmap.h" #include "utf8.h" #include "vmstr.h" #include "vmcset.h" #include "vmdatasrc.h" #include "vmpack.h" #include "sha2.h" #include "md5.h" /* ------------------------------------------------------------------------ */ /* * Byte Array data source */ class CVmByteArraySource: public CVmDataSource { public: /* set up a data source on the entire range of the array */ CVmByteArraySource(VMG_ vm_obj_id_t arr) { /* remember the array and globals */ this->vmg = VMGLOB_ADDR; this->arr_id = arr; this->arr = (CVmObjByteArray *)vm_objp(vmg_ arr); /* we provide access to the entire range of the array */ this->idx = 0; this->start_idx = 0; this->end_idx = this->arr->get_element_count(); } /* set up a data source on a slice of the array */ CVmByteArraySource(VMG_ vm_obj_id_t arr, long start_idx, long len) { /* remember the array and globals */ this->vmg = VMGLOB_ADDR; this->arr_id = arr; this->arr = (CVmObjByteArray *)vm_objp(vmg_ arr); /* remember our window */ this->idx = start_idx; this->start_idx = start_idx; this->end_idx = start_idx + len; /* limit the window to the actual available size */ if ((unsigned long)end_idx > this->arr->get_element_count()) end_idx = this->arr->get_element_count(); } CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmByteArraySource( vmg_ arr_id, start_idx, end_idx - start_idx); } /* read bytes; returns 0 on success, non-zero on error */ virtual int read(void *buf, size_t len) { /* limit the read to the available length */ size_t rdlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes starting at the current index */ size_t copied = arr->copy_to_buf((unsigned char *)buf, idx + 1, rdlen); /* move past the bytes copied */ idx += copied; /* we were successful if we copied exactly the requested length */ return copied != len; } /* read bytes; returns the number of bytes read */ virtual int readc(void *buf, size_t len) { /* limit the read to the available length */ size_t rdlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes */ size_t copied = arr->copy_to_buf((unsigned char *)buf, idx + 1, rdlen); /* move past the bytes copied */ idx += copied; /* return the number of bytes actually read */ return copied; } /* write bytes; returns 0 on success, non-zero on error */ virtual int write(const void *buf, size_t len) { /* establish global access */ VMGLOB_PTR(vmg); /* limit the write to the available length */ size_t wrtlen = (idx + (long)len > end_idx ? (size_t)(end_idx - idx) : len); /* copy the requested bytes (saving undo) */ size_t copied = arr->copy_from_buf_undo( vmg_ arr_id, (const unsigned char *)buf, idx + 1, wrtlen); /* move past the bytes copied */ idx += copied; /* we were successful if we copied exactly the requested length */ return copied != len; } /* get the length of our slice of the array in bytes */ virtual long get_size() { return end_idx - start_idx; } /* get the current seek location */ virtual long get_pos() { return idx - start_idx; } /* set the current seek location - 'mode' is an OSFSK_xxx mode */ virtual int seek(long ofs, int mode) { switch (mode) { case OSFSK_SET: idx = start_idx + ofs; break; case OSFSK_CUR: idx += ofs; break; case OSFSK_END: idx = end_idx + ofs; break; default: return 1; } /* make sure it's in range */ if (idx < start_idx) idx = start_idx; else if (idx > end_idx) idx = end_idx; /* success */ return 0; } /* flush - returns 0 on success, non-zero on error */ virtual int flush() { return 0; } /* close the underlying system resource */ virtual void close() { } protected: /* our underlying array */ vm_obj_id_t arr_id; CVmObjByteArray *arr; /* current read/write index in the array */ long idx; /* the bounds of the slice of the array we present as the file */ long start_idx; long end_idx; /* we need globals in some interface routines without the VMG_ param */ vm_globals *vmg; }; /* ------------------------------------------------------------------------ */ /* * Integer format codes. A full format code is given by a bitwise * combination of size, byte order, and signedness. */ /* integer sizes */ #define FmtSizeMask 0x000F #define FmtInt8 0x0000 #define FmtInt16 0x0001 #define FmtInt32 0x0002 /* integer byte orders */ #define FmtOrderMask 0x00F0 #define FmtLittleEndian 0x0000 #define FmtBigEndian 0x0010 /* integer signedness */ #define FmtSignedMask 0x0F00 #define FmtSigned 0x0000 #define FmtUnsigned 0x0100 /* ------------------------------------------------------------------------ */ /* * statics */ /* metaclass registration object */ static CVmMetaclassByteArray metaclass_reg_obj; CVmMetaclass *CVmObjByteArray::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjByteArray:: *CVmObjByteArray::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjByteArray::getp_undef, /* 0 */ &CVmObjByteArray::getp_length, /* 1 */ &CVmObjByteArray::getp_subarray, /* 2 */ &CVmObjByteArray::getp_copy_from, /* 3 */ &CVmObjByteArray::getp_fill_val, /* 4 */ &CVmObjByteArray::getp_to_string, /* 5 */ &CVmObjByteArray::getp_read_int, /* 6 */ &CVmObjByteArray::getp_write_int, /* 7 */ &CVmObjByteArray::getp_packBytes, /* 8 */ &CVmObjByteArray::getp_unpackBytes, /* 9 */ &CVmObjByteArray::getp_sha256, /* 10 */ &CVmObjByteArray::getp_digestMD5 /* 11 */ }; /* static property indices */ const int PROPIDX_packBytes = 8; /* ------------------------------------------------------------------------ */ /* * Create from stack */ vm_obj_id_t CVmObjByteArray::create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id = VM_INVALID_OBJ; CVmObjByteArray *arr; unsigned long cnt; vm_val_t *arg1; const char *str; /* check our arguments */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* see what we have for the first argument */ arg1 = G_stk->get(0); if (arg1->typ == VM_INT) { /* * it's a simple count argument - make sure we only have one * argument */ if (argc != 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the number of elements */ cnt = (unsigned long)arg1->val.intval; /* create the array with the given number of elements */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt); /* set each element to zero */ arr->fill_with(0, 1, cnt); } else if ((str = arg1->get_as_string(vmg0_)) != 0) { /* * String argument - we're creating a byte array from the string. * We can map it from a character set, or we can just stuff the * character values into bytes. Check arguments - we must have one * or two of them (the string, and the optional character set) */ if (argc < 1 || argc > 2) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* fetch the character mapper, if there is one */ const vm_val_t *cmap = (argc >= 2 ? G_stk->get(1) : 0); /* create the byte array */ id = create_from_string(vmg_ G_stk->get(0), str, cmap); } else if (arg1->typ == VM_OBJ && is_byte_array(vmg_ arg1->val.obj)) { unsigned long src_idx; unsigned long src_cnt; /* remember the source array */ CVmObjByteArray *src_arr = (CVmObjByteArray *)vm_objp(vmg_ arg1->val.obj); /* get the count from the array */ src_cnt = src_arr->get_element_count(); /* * check for the optional actual element count, and the optional * starting index */ if (argc == 1) { /* no size specified - use the same size as the original */ cnt = src_cnt; /* start at the first element */ src_idx = 1; } else { /* make sure it's an integer */ if (G_stk->get(1)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* use the specified starting index */ src_idx = (unsigned long)G_stk->get(1)->val.intval; /* if the index is below 1, force it to 1 */ if (src_idx < 1) src_idx = 1; /* check for the starting element argument */ if (argc >= 3) { /* make sure it's an integer */ if (G_stk->get(2)->typ != VM_INT) err_throw(VMERR_INT_VAL_REQD); /* use the specified element count */ cnt = (unsigned long)G_stk->get(2)->val.intval; /* make sure we don't have any extra arguments */ if (argc > 3) err_throw(VMERR_WRONG_NUM_OF_ARGS); } else { /* * no count specified - use the number of elements in the * original remaining after the starting index */ if (src_idx > src_cnt) cnt = 0; else cnt = src_cnt + 1 - src_idx; } } /* create the new array */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt); /* copy the source array */ arr->copy_from(1, src_arr, src_idx, cnt); } else { /* invalid argument */ err_throw(VMERR_BAD_TYPE_BIF); } /* discard arguments */ G_stk->discard(argc); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Create a new byte array from a string, mapping through the given * character set object if present, or just stuffing the character values * into bytes if not. */ vm_obj_id_t CVmObjByteArray::create_from_string( VMG_ const vm_val_t *strval, const char *str, const vm_val_t *mapval) { /* we can't allocate our object until we know the mapped byte count */ vm_obj_id_t id = VM_INVALID_OBJ; CVmObjByteArray *arr = 0; /* get the string's length and skip the length prefix */ size_t len = vmb_get_len(str); str += VMB_LEN; /* * We have two modes: map to bytes via a character set, or stuff * unmapped character values into bytes. Check which mode we're using. */ if (mapval == 0 || mapval->typ == VM_NIL) { /* * No character mapper - just stuff each character into a byte. If * a character is outside the 0..255 range, it's an error. */ /* count the number of characters */ utf8_ptr p((char *)str); size_t clen = p.len(len); /* allocate the array - each string character is one array byte */ id = create(vmg_ FALSE, clen); arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* copy bytes into the array */ for (int idx = 1 ; len != 0 ; ) { /* translate a buffer-full */ unsigned char buf[256]; size_t cur = 0; for ( ; len != 0 && cur < sizeof(buf) ; p.inc(&len)) { /* get the next character; make sure it fits in a byte */ wchar_t ch = p.getch(); if (ch > 255) err_throw(VMERR_NUM_OVERFLOW); /* save it */ buf[cur++] = (unsigned char)ch; } /* copy this chunk to the array */ arr->cons_copy_from_buf(buf, idx, cur); idx += cur; } } else { /* interpret the character set argument */ vm_obj_id_t mapid = CVmBif::get_charset_obj(vmg_ mapval); /* get the to-local mapping from the character set */ CCharmapToLocal *mapper = ((CVmObjCharSet *)vm_objp(vmg_ mapid))->get_to_local(vmg0_); /* * first, do a mapping with a null output buffer to determine how * many bytes we need for the mapping */ size_t src_bytes_used; size_t byte_len = mapper->map_utf8(0, 0, str, len, &src_bytes_used); /* allocate a new ByteArray with the required number of bytes */ id = CVmObjByteArray::create(vmg_ FALSE, byte_len); arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* convert it again, this time storing the bytes */ for (size_t out_idx = 1 ; len != 0 ; ) { char buf[128]; /* convert a buffer-full */ byte_len = mapper->map_utf8(buf, sizeof(buf), str, len, &src_bytes_used); /* store the bytes in the byte array */ arr->cons_copy_from_buf((unsigned char *)buf, out_idx, byte_len); /* advance past the output bytes we used */ out_idx += byte_len; /* advance past the source bytes we used */ str += src_bytes_used; len -= src_bytes_used; } } /* return the new array's object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create from binary data */ vm_obj_id_t CVmObjByteArray::create_from_bytes( VMG_ int in_root_set, const char *buf, size_t len) { /* allocate the array */ vm_obj_id_t id = create(vmg_ in_root_set, len); CVmObjByteArray *arr = (CVmObjByteArray *)vm_objp(vmg_ id); /* copy the bytes into the array */ arr->cons_copy_from_buf((const unsigned char *)buf, 1, len); /* return the new array's object ID */ return id; } /* ------------------------------------------------------------------------ */ /* * Create with no contents */ vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjByteArray(); return id; } /* * Create with the given element count */ vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set, unsigned long ele_count) { vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); new (vmg_ id) CVmObjByteArray(vmg_ ele_count); return id; } /* ------------------------------------------------------------------------ */ /* * Instantiate */ CVmObjByteArray::CVmObjByteArray(VMG_ unsigned long ele_count) { /* allocate space */ alloc_array(vmg_ ele_count); } /* ------------------------------------------------------------------------ */ /* * Allocate space */ void CVmObjByteArray::alloc_array(VMG_ unsigned long ele_count) { unsigned long ele_rem; size_t slot_cnt; size_t alloc_size; size_t i; /* * figure out how many first-level page tables we need - each * first-level page table refers to 256M bytes (8k pages per * second-level page table times 32k bytes per page), so we need one * page table pointer per 256 megabytes == 2^15*2^32 == 2^28. */ slot_cnt = (ele_count == 0 ? 0 : ((size_t)((ele_count - 1) >> 28) + 1)); /* * allocate the extension - 4 bytes for the element count plus one * (char **) per slot */ alloc_size = 4 + slot_cnt*sizeof(char **); ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alloc_size, this); /* set the element count */ set_element_count(ele_count); /* allocate the second-level page tables */ for (ele_rem = ele_count, i = 0 ; i < slot_cnt ; ++i) { unsigned char **pgtab; unsigned long pgcnt; size_t pg; /* * Determine how many pages we need in this page table. Each page * holds 32k bytes, so we need one page per 32k bytes remaining to * allocate. However, each page table can hold only up to 8k page * pointers, so limit this table to 8k pages. */ pgcnt = ((ele_rem - 1) >> 15) + 1; if (pgcnt > 8*1024) pgcnt = 8*1024; /* allocate this second-level page table */ pgtab = (unsigned char **)t3malloc( (size_t)(pgcnt * sizeof(unsigned char *))); /* set this page table pointer */ get_page_table_array()[i] = pgtab; /* allocate the pages in this table */ for (pg = 0 ; pg < pgcnt ; ++pg) { size_t pgsiz; /* * calculate the size for this page - the maximum size of a * page is 32k, but allocate only the amount remaining if we * have less than 32k left to allocate */ pgsiz = 32*1024; if (pgsiz > ele_rem) pgsiz = (size_t)ele_rem; /* deduct this page's size from the remaining element count */ ele_rem -= pgsiz; /* allocate and store this page */ pgtab[pg] = (unsigned char *)t3malloc(pgsiz); } } } /* ------------------------------------------------------------------------ */ /* * Notify of deletion */ void CVmObjByteArray::notify_delete(VMG_ int /*in_root_set*/) { size_t slot; unsigned char ***slotp; size_t slot_cnt; size_t bytes_rem; /* if we have no extension, there's nothing to do */ if (ext_ == 0) return; /* calculate the number of second-level page table slots */ slot_cnt = (size_t)(get_element_count() == 0 ? 0 : ((get_element_count() >> 28) + 1)); /* we have all of the bytes in the array left do delete */ bytes_rem = get_element_count(); /* traverse the list of second-level page tables and delete each one */ for (slot = 0, slotp = get_page_table_array() ; slot < slot_cnt ; ++slot, ++slotp) { size_t pg; unsigned char **pgp; /* traverse each page in this page table */ for (pg = 0, pgp = *slotp ; pg < 8*1024 && bytes_rem != 0 ; ++pg, ++pgp) { /* delete this page */ t3free(*pgp); /* * deduct this page's size from the remaining bytes to delete * - the page size is up to 32k, but no more than the * remaining size */ if (bytes_rem > 32*1024) bytes_rem -= 32*1024; else bytes_rem = 0; } /* delete this page table */ t3free(*slotp); } /* free our extension */ G_mem->get_var_heap()->free_mem(ext_); } /* ------------------------------------------------------------------------ */ /* * Cast to string. Maps to string treating the bytes as Latin-1 * characters. */ const char *CVmObjByteArray::cast_to_string( VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a source stream to read from the byte array into the string */ CVmByteArraySource src(vmg_ self); /* create the string */ new_str->set_obj(CVmObjString::create_latin1(vmg_ FALSE, &src)); /* return the new string's byte buffer */ return new_str->get_as_string(vmg0_); } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjByteArray::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Get a static property */ int CVmObjByteArray::call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* get the function table index */ int idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* check for static methods we define in ByteArray */ switch (idx) { case PROPIDX_packBytes: /* the static version of packBytes() */ return static_packBytes(vmg_ result, argc); default: /* not one of ours - defer to the superclass */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } } /* ------------------------------------------------------------------------ */ /* * get a property */ int CVmObjByteArray::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * ByteArray private undo record - saved byte block */ struct bytearray_undo_bytes { /* next block in the list */ bytearray_undo_bytes *nxt_; /* the bytes - overallocated to the required size */ unsigned char buf_[1]; }; /* * ByteArray private undo record */ class bytearray_undo_rec { public: bytearray_undo_rec(CVmObjByteArray *arr, unsigned long start_idx, unsigned long cnt) { unsigned long idx; unsigned long rem; bytearray_undo_bytes *tail; /* remember the starting index and length of the saved data */ idx_ = start_idx; cnt_ = cnt; /* store the original data in a linked list of byte blocks */ for (idx = start_idx, rem = cnt, save_head_ = tail = 0 ; rem != 0 ; ) { bytearray_undo_bytes *p; size_t cur; /* allocate up to 32k, or the amount remaining if lower */ cur = 32768; if (cur > rem) cur = (size_t)rem; /* allocate a new block */ p = (bytearray_undo_bytes *)t3malloc( sizeof(bytearray_undo_bytes) + cur - 1); /* link this block into our list */ if (tail != 0) tail->nxt_ = p; else save_head_ = p; tail = p; p->nxt_ = 0; /* copy bytes into this block */ arr->copy_to_buf(p->buf_, idx, cur); /* move past the copied bytes */ idx += cur; rem -= cur; } } ~bytearray_undo_rec() { bytearray_undo_bytes *cur; bytearray_undo_bytes *nxt; /* delete our list of saved byte blocks */ for (cur = save_head_ ; cur != 0 ; cur = nxt) { /* * remember the next one, as we're about to lose the current * one's memory */ nxt = cur->nxt_; /* delete this one */ t3free(cur); } } /* copy our data back into an array */ void apply_undo(CVmObjByteArray *arr) { bytearray_undo_bytes *cur; unsigned long idx; unsigned long rem; /* delete our list of saved byte blocks */ for (cur = save_head_, idx = idx_, rem = cnt_ ; rem != 0 ; ) { size_t copy_len; /* limit the copy to our block size (32k) */ copy_len = 32768; if (copy_len > rem) copy_len = (size_t)rem; /* copy this block into the array */ arr->copy_from_buf(cur->buf_, idx, copy_len); /* move past the copied data */ cur = cur->nxt_; idx += copy_len; rem -= copy_len; } } /* starting index */ unsigned long idx_; /* length */ unsigned long cnt_; /* head of list of blocks of saved data */ bytearray_undo_bytes *save_head_; }; /* * Save undo for a change to a range of the array */ void CVmObjByteArray::save_undo(VMG_ vm_obj_id_t self, unsigned long start_idx, unsigned long cnt) { bytearray_undo_rec *rec; vm_val_t oldval; /* create our key record - this contains the entire original value */ rec = new bytearray_undo_rec(this, start_idx, cnt); /* we don't use the old value for anything; use nil as a dummy */ oldval.set_nil(); /* add the undo record */ if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &oldval)) { /* failed to save the undo - discard our private key record */ delete rec; } } /* * Apply undo */ void CVmObjByteArray::apply_undo(VMG_ struct CVmUndoRecord *gen_rec) { /* apply our private undo record, if present */ if (gen_rec->id.ptrval != 0) { bytearray_undo_rec *rec; /* get my private record */ rec = (bytearray_undo_rec *)gen_rec->id.ptrval; /* apply the undo in the record to self */ rec->apply_undo(this); /* delete the record now that it's been applied */ delete rec; /* clear the pointer so we know it's gone */ gen_rec->id.ptrval = 0; } } /* * Discard undo */ void CVmObjByteArray::discard_undo(VMG_ struct CVmUndoRecord *rec) { /* delete our extra information record if present */ if (rec->id.ptrval != 0) { /* free the record */ t3free((bytearray_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* ------------------------------------------------------------------------ */ /* * load from an image file */ void CVmObjByteArray::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load from the image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload the object from image data */ void CVmObjByteArray::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* load the image data */ load_image_data(vmg_ ptr, siz); } /* * Load from image data */ void CVmObjByteArray::load_image_data(VMG_ const char *ptr, size_t siz) { unsigned long cnt; unsigned long idx; /* if we already have memory allocated, free it */ notify_delete(vmg_ FALSE); /* get the new array size */ cnt = t3rp4u(ptr); /* make sure the size isn't larger than we'd expect */ if (siz > 4 + cnt) siz = 4 + cnt; /* allocate memory at the new size as indicated in the image data */ alloc_array(vmg_ cnt); /* if the size is smaller than we'd expect, set extra elements to nil */ if (siz < VMB_LEN + (VMB_DATAHOLDER * cnt)) { /* fill everything with zeros to start with */ fill_with(0, 1, cnt); } /* copy the bytes */ for (ptr += 4, siz -= 4, idx = 1 ; siz != 0 ; ) { unsigned char *dstp; size_t chunk_size; size_t avail; /* get the next chunk */ dstp = get_ele_ptr(idx, &avail); /* limit this chunk size to the remaining copy size */ chunk_size = avail; if (chunk_size > siz) chunk_size = siz; /* copy this chunk */ memcpy(dstp, ptr, chunk_size); /* advance past this chunk */ idx += chunk_size; ptr += chunk_size; siz -= chunk_size; } } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjByteArray::save_to_file(VMG_ class CVmFile *fp) { unsigned long rem; unsigned long idx; /* write the element count */ fp->write_int4(get_element_count()); /* write the bytes in chunks */ for (idx = 1, rem = get_element_count() ; rem != 0 ; ) { size_t avail; size_t chunk; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this copy to the remaining bytes */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* write this chunk */ fp->write_bytes((char *)p, chunk); /* advance past this chunk */ idx += chunk; rem -= chunk; } } /* * restore from a file */ void CVmObjByteArray::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *) { unsigned long ele_cnt; unsigned long idx; unsigned long rem; /* read the element count */ ele_cnt = fp->read_uint4(); /* allocate or reallocate as needed */ if (ext_ == 0) { /* we're not yet allocated - allocate now */ alloc_array(vmg_ ele_cnt); } else { /* already allocated - if it's a different size, reallocate */ if (get_element_count() != ele_cnt) { /* delete the old array */ notify_delete(vmg_ FALSE); /* allocate a new one */ alloc_array(vmg_ ele_cnt); } } /* read the data */ for (idx = 1, rem = ele_cnt ; rem != 0 ; ) { size_t avail; size_t chunk; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this copy to the remaining bytes */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* read this chunk */ fp->read_bytes((char *)p, chunk); /* advance past this chunk */ idx += chunk; rem -= chunk; } } /* ------------------------------------------------------------------------ */ /* * Retrieve the value at the given index */ int CVmObjByteArray::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* get the index */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* get a pointer to the desired element */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* return the value as an integer */ result->set_int((int)(unsigned short)*p); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * set an indexed element of the array */ int CVmObjByteArray::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* get the index value as an integer */ int32_t idx = index_val->num_to_int(vmg0_); /* make sure it's in range - 1 to our element count, inclusive */ if (idx < 1 || (uint32_t)idx > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* save undo for the change */ save_undo(vmg_ self, idx, 1); /* get the new value as an integer */ int32_t new_byte = new_val->num_to_int(vmg0_); /* make sure it's in range */ if (new_byte < 0 || new_byte > 255) err_throw(VMERR_OUT_OF_RANGE); /* get a pointer to the desired element */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* set the value */ *p = (unsigned char)new_byte; /* the result is the original array value */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Compare for equality */ int CVmObjByteArray::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int /*depth*/) const { CVmObjByteArray *other; unsigned long idx; unsigned long rem; /* if it's a self-reference, it's certainly equal */ if (val->typ == VM_OBJ && val->val.obj == self) return TRUE; /* if it's not another byte array, it's not equal */ if (val->typ != VM_OBJ || !is_byte_array(vmg_ val->val.obj)) return FALSE; /* we know it's another byte array - cast it */ other = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); /* if it's not of the same length, it's not equal */ if (other->get_element_count() != get_element_count()) return FALSE; /* compare the arrays */ for (idx = 1, rem = get_element_count() ; rem != 0 ; ) { unsigned char *p1; unsigned char *p2; size_t avail1; size_t avail2; size_t chunk; /* get the next chunk of each array */ p1 = get_ele_ptr(idx, &avail1); p2 = other->get_ele_ptr(idx, &avail2); /* if the chunk sizes aren't the same, there's some problem */ assert(avail1 == avail2); /* limit the chunk size to the remaining size */ chunk = avail1; if (chunk > rem) chunk = (size_t)rem; /* if the chunks differ, the arrays differ */ if (memcmp(p1, p2, chunk) != 0) return FALSE; /* advance past the chunk */ idx += avail1; rem -= avail1; } /* we found no differences */ return TRUE; } /* * Calculate a hash value */ uint CVmObjByteArray::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const { unsigned long idx; unsigned long rem; uint hash; /* add up the bytes in the array */ for (hash = 0, idx = 1, rem = get_element_count() ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit the chunk size to the remaining size */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* advance our counters for this chunk */ idx += chunk; rem -= chunk; /* add up the bytes in this part */ for ( ; chunk != 0 ; --chunk, ++p) hash += *p; } /* return the result */ return hash; } /* ------------------------------------------------------------------------ */ /* * Copy bytes from an array */ void CVmObjByteArray::copy_from(unsigned long dst_idx, CVmObjByteArray *src_arr, unsigned long src_idx, unsigned long cnt) { /* if we're moving zero bytes, there's nothing to do */ if (cnt == 0) return; /* make sure we don't overrun our array */ if (dst_idx > get_element_count()) cnt = 0; else if (dst_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - dst_idx; /* * If the source and destination objects are the same, and the source * and destination regions overlap, we must take care to move the * bytes in such a way that we don't overwrite parts of the source in * the course of moving the bytes. */ if (src_arr == this && src_idx + cnt - 1 >= dst_idx && src_idx <= dst_idx + cnt - 1) { /* the regions overlap - use the overlap-safe move routine */ move_bytes(dst_idx, src_idx, cnt); /* done */ return; } /* continue until we exhaust the count */ while (cnt != 0) { size_t src_avail; size_t src_chunk_size; unsigned char *srcp; /* if we're past the end of the source array, stop copying */ if (src_idx > src_arr->get_element_count()) break; /* get the next source chunk */ srcp = src_arr->get_ele_ptr(src_idx, &src_avail); /* limit this chunk size to the remaining copy size */ src_chunk_size = src_avail; if (src_chunk_size > cnt) src_chunk_size = (size_t)cnt; /* limit this chunk to the remaining source array size */ if (src_idx + src_chunk_size - 1 > src_arr->get_element_count()) src_chunk_size = src_arr->get_element_count() + 1 - src_idx; /* copy this chunk into the destination */ copy_from_buf(srcp, dst_idx, src_chunk_size); /* move past this chunk in the source */ cnt -= src_chunk_size; src_idx += src_chunk_size; /* move past this chunk in the destination */ dst_idx += src_chunk_size; } /* * if there's any copying size left, we ran out of source array bytes * - fill the balance of the destination array with zeros */ if (cnt != 0) fill_with(0, dst_idx, cnt); } /* * Move bytes within our array. This is safe even if the source and * destination regions overlap. */ void CVmObjByteArray::move_bytes(unsigned long dst_idx, unsigned long src_idx, unsigned long cnt) { size_t src_avail; size_t dst_avail; unsigned char *srcp; unsigned char *dstp; /* * If the destination is before the source, we're moving bytes down, * so we must start at the low end and work forwards through the * array. If the destination is after the source, we're moving bytes * up, so we must start at the high end and work backwards through the * array. */ if (dst_idx < src_idx) { /* * Moving bytes down in the array - start at the low end and work * forwards through the array. Get the starting pointers and * available lengths. */ srcp = get_ele_ptr(src_idx, &src_avail); dstp = get_ele_ptr(dst_idx, &dst_avail); /* keep going until we've moved all of the bytes requested */ while (cnt != 0) { size_t move_len; /* * figure the largest amount we can move - we can move the * smallest of the remaining requested move size, the source * chunk, and the destination chunk */ move_len = cnt; if (move_len > src_avail) move_len = src_avail; if (move_len > dst_avail) move_len = dst_avail; /* move the data */ memmove(dstp, srcp, move_len); /* advance all of the counters by the move size */ srcp += move_len; dstp += move_len; cnt -= move_len; src_avail -= move_len; dst_avail -= move_len; src_idx += move_len; dst_idx += move_len; /* stop if we're done */ if (cnt == 0) break; /* if the source chunk is at an end, get the next one */ if (src_avail == 0) srcp = get_ele_ptr(src_idx, &src_avail); /* if the destination chunk is at an end, get the next one */ if (dst_avail == 0) dstp = get_ele_ptr(dst_idx, &dst_avail); } } else { /* * We're to move bytes up in the array - start at the high end and * work backwards through the array. Advance each index to one * past the last byte of its range. */ src_idx += cnt; dst_idx += cnt; /* get the chunk pointers */ srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1; dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1; /* * since we're working backwards, we actually want to know the * number of bytes on the page *before* the current pointers, so * subtract the available spaces from the size of the page to get * the available space preceding each */ src_avail = 32*1024 - src_avail + 1; dst_avail = 32*1024 - dst_avail + 1; /* keep going until we've moved all of the requested bytes */ while (cnt != 0) { size_t move_len; /* * figure the largest amount we can move - we can move the * smallest of the remaining requested move size, the source * chunk, and the destination chunk */ move_len = cnt; if (move_len > src_avail) move_len = src_avail; if (move_len > dst_avail) move_len = dst_avail; /* move the data */ memmove(dstp - move_len, srcp - move_len, move_len); /* advance all of the counters by the move size */ srcp -= move_len; dstp -= move_len; cnt -= move_len; src_avail -= move_len; dst_avail -= move_len; src_idx -= move_len; dst_idx -= move_len; /* stop if we're done */ if (cnt == 0) break; /* if we've exhausted the source chunk, get the next one */ if (src_avail == 0) { srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1; src_avail = 32*1024 - src_avail + 1; } /* if we've exhausted the destination chunk, get the next one */ if (dst_avail == 0) { dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1; dst_avail = 32*1024 - dst_avail + 1; } } } } /* ------------------------------------------------------------------------ */ /* * Fill a 1-based index range with the given value */ void CVmObjByteArray::fill_with(unsigned char val, unsigned long start_idx, unsigned long cnt) { unsigned long idx; unsigned long rem; /* ensure we don't overrun the array */ if (start_idx > get_element_count()) cnt = 0; else if (start_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - start_idx; /* continue until we exhaust the count */ for (idx = start_idx, rem = cnt ; rem != 0 ; ) { size_t avail; size_t chunk_size; unsigned char *p; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit this chunk size to the remaining size to fill */ chunk_size = avail; if (chunk_size > rem) chunk_size = (size_t)rem; /* fill this chunk */ memset(p, val, chunk_size); /* skip this chunk */ rem -= chunk_size; idx += chunk_size; } } /* ------------------------------------------------------------------------ */ /* * property evaluator - length */ int CVmObjByteArray::getp_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the length */ retval->set_int(get_element_count()); /* handled */ return TRUE; } /* * property evaluator - subarray */ int CVmObjByteArray::getp_subarray(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1, 1); unsigned long idx; unsigned long cnt; CVmObjByteArray *arr; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the starting index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; /* if there's a count, get it */ if (argc >= 2) { /* get the explicit count */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* use the entire rest of the array */ cnt = get_element_count(); } /* limit the count to the available size */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* push a self-reference while we're working for gc protection */ G_stk->push()->set_obj(self); /* allocate a new array to hold the result */ retval->set_obj(create(vmg_ FALSE, cnt)); arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* copy the data from our array into the new one */ arr->copy_from(1, this, idx, cnt); /* discard our self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * property evaluator - copy from another byte array */ int CVmObjByteArray::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(4); unsigned long dst_idx; unsigned long src_idx; unsigned long cnt; vm_obj_id_t src_arr_id; CVmObjByteArray *src_arr; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the source array */ src_arr_id = CVmBif::pop_obj_val(vmg0_); /* make sure it is indeed an array */ if (!is_byte_array(vmg_ src_arr_id)) err_throw(VMERR_BAD_TYPE_BIF); /* we know it's the right type, so cast it */ src_arr = (CVmObjByteArray *)vm_objp(vmg_ src_arr_id); /* get the starting source index */ src_idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (src_idx < 1) src_idx = 1; else if (src_idx > src_arr->get_element_count() + 1) src_idx = src_arr->get_element_count() + 1; /* get the destination index */ dst_idx = CVmBif::pop_int_val(vmg0_); /* force it to be within range */ if (dst_idx < 1) dst_idx = 1; else if (dst_idx > get_element_count() + 1) dst_idx = get_element_count() + 1; /* get the count */ cnt = CVmBif::pop_int_val(vmg0_); /* limit the copying to the available destination space */ if (dst_idx > get_element_count()) cnt = 0; else if (dst_idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - dst_idx; /* save undo for the change */ save_undo(vmg_ self, dst_idx, cnt); /* copy the data from the source array into our array */ copy_from(dst_idx, src_arr, src_idx, cnt); /* the result is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* * property evaluator - fill with a value */ int CVmObjByteArray::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(1, 2); unsigned long idx; unsigned long cnt; long fill_val; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the value with which to fill elements */ fill_val = CVmBif::pop_int_val(vmg0_); if (fill_val < 0 || fill_val > 255) err_throw(VMERR_OUT_OF_RANGE); /* get the starting index, if provided */ if (argc >= 2) { /* get the index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; } else { /* fill from the first element */ idx = 1; } /* get the count, if provided */ if (argc >= 3) { /* get the count */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* fill the entire array */ cnt = get_element_count(); } /* force the length to be in range */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* save undo for the change */ save_undo(vmg_ self, idx, cnt); /* fill with the given value */ fill_with((unsigned char)fill_val, idx, cnt); /* the result is 'self' */ retval->set_obj(self); /* handled */ return TRUE; } /* * property evaluator - convert to string */ int CVmObjByteArray::getp_to_string(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc) { uint argc = (in_argc != 0 ? *in_argc : 0); static CVmNativeCodeDesc desc(0, 3); unsigned long idx; unsigned long cnt; vm_val_t charset; CCharmapToUni *mapper = 0; size_t str_len; CVmObjString *str; /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* get the character set object */ charset.set_nil(); if (argc >= 1) charset.set_obj_or_nil(CVmBif::pop_charset_obj(vmg0_)); /* if we got a CharacterSet, get its to-unicode mapper */ if (charset.typ != VM_NIL) mapper = ((CVmObjCharSet *)vm_objp(vmg_ charset.val.obj)) ->get_to_uni(vmg0_); /* if there's a starting index, retrieve it */ if (argc >= 2) { /* retrieve the starting index */ idx = CVmBif::pop_int_val(vmg0_); /* force it to be in range */ if (idx < 1) idx = 1; else if (idx > get_element_count() + 1) idx = get_element_count() + 1; } else { /* start at the first byte */ idx = 1; } /* if there's a length, retrieve it */ if (argc >= 3) { /* retrieve the length */ cnt = CVmBif::pop_int_val(vmg0_); } else { /* use all remaining characters */ cnt = get_element_count(); } /* force the length to be in range */ if (idx > get_element_count()) cnt = 0; else if (idx + cnt - 1 > get_element_count()) cnt = get_element_count() + 1 - idx; /* push a self-ref for gc protection, and a ref to the character set */ G_stk->push()->set_obj(self); G_stk->push(&charset); /* map via the character mapper, or as raw unicode character values */ if (mapper != 0) { /* * We have a character set mapper. First, measure the required * buffer length by mapping to a null buffer. */ str_len = map_to_string(idx, cnt, 0, 0, mapper); /* allocate a string of the required length */ retval->set_obj(CVmObjString::create(vmg_ FALSE, str_len)); str = (CVmObjString *)vm_objp(vmg_ retval->val.obj); /* map the string, actually storing the bytes this time */ map_to_string(idx, cnt, str, str_len, mapper); } else { /* there's no mapper - treat our bytes as unicode character codes */ CVmByteArraySource src(vmg_ self, idx - 1, cnt); retval->set_obj(CVmObjString::create_latin1(vmg_ FALSE, &src)); } /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * Run a range of bytes through a character mapper to produce string data, * optionally storing the string data in a string object. Returns the * number of bytes in the resulting string. */ size_t CVmObjByteArray::map_to_string(unsigned long idx, unsigned long len, CVmObjString *str, size_t str_len, CCharmapToUni *mapper) { size_t str_total; size_t buf_len; unsigned char buf[128]; char *dst; /* get the string's buffer pointer */ dst = (str != 0 ? str->cons_get_buf() : 0); /* go through the source bytes a bit at a time */ for (str_total = 0, buf_len = 0 ; len != 0 ; ) { size_t copy_len; size_t str_cur; size_t partial_len; /* * fill up the buffer, up to the remaining source length or the * buffer's capacity, whichever is lower */ copy_len = sizeof(buf) - buf_len; if (copy_len > len) copy_len = (size_t)len; /* copy the bytes to our staging buffer */ copy_to_buf(buf, idx, copy_len); /* add the copied bytes into the buffer length */ buf_len += copy_len; /* advance past the copied bytes in the source */ idx += copy_len; len -= copy_len; /* translate the bytes through the character set mapping */ str_cur = mapper->map2(&dst, &str_len, (char *)buf, buf_len, &partial_len); /* * if this would push us over the maximum string size, we can't * convert the data */ if (str_cur > OSMALMAX - str_total - VMB_LEN) err_throw(VMERR_OUT_OF_MEMORY); /* add the current length into the total string length */ str_total += str_cur; /* copy the partial last character bytes to the start of the buffer */ if (partial_len != 0) memmove(buf, buf + buf_len - partial_len, partial_len); /* the buffer now contains only the partial character bytes */ buf_len = partial_len; } /* return the total string length */ return str_total; } /* * Copy bytes from the array to a buffer */ size_t CVmObjByteArray::copy_to_buf(unsigned char *buf, unsigned long idx, size_t len) const { /* we haven't copied any bytes so far */ size_t actual = 0; /* limit it to the actual available space */ unsigned long contlen = get_element_count(); if (idx > contlen) len = 0; else if (idx + len > contlen) len = contlen - idx + 1; /* keep going until we satisfy the request */ while (len != 0) { size_t avail; unsigned char *p; size_t copy_len; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* if there's nothing left, stop */ if (avail == 0) break; /* copy the available bytes or the reamining desired bytes */ copy_len = avail; if (copy_len > len) copy_len = len; /* copy the bytes */ memcpy(buf, p, copy_len); /* advance past the copied bytes */ buf += copy_len; idx += copy_len; len -= copy_len; actual += copy_len; } /* return the actual size copied */ return actual; } /* * Copy bytes from a buffer into the array, saving undo */ size_t CVmObjByteArray::copy_from_buf_undo(VMG_ vm_obj_id_t self, const unsigned char *buf, unsigned long idx, size_t len) { /* limit the size to the remaining length from 'idx' */ unsigned long avail_len = get_element_count() - idx + 1; if (len > avail_len) len = (size_t)avail_len; /* save undo */ save_undo(vmg_ self, idx, len); /* copy the bytes */ copy_from_buf(buf, idx, len); /* return the copy length */ return len; } /* * Copy bytes from a buffer into the array */ void CVmObjByteArray::copy_from_buf(const unsigned char *buf, unsigned long idx, size_t len) { /* keep going until we satisfy the request */ while (len != 0) { size_t avail; unsigned char *p; size_t copy_len; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* copy the available bytes or the remaining desired bytes */ copy_len = avail; if (copy_len > len) copy_len = len; /* copy the bytes */ memcpy(p, buf, copy_len); /* advance past the copied bytes */ buf += copy_len; idx += copy_len; len -= copy_len; } } /* ------------------------------------------------------------------------ */ /* * Swap byte order */ static void swap_bytes(unsigned char *buf, int len) { int i, j; for (i = 0, j = len - 1 ; i < j ; ++i, --j) { unsigned char tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp; } } /* ------------------------------------------------------------------------ */ /* * property evaluator - read an integer */ int CVmObjByteArray::getp_read_int(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(2); unsigned int idx; unsigned int fmt; long result = 0; size_t siz; unsigned char cbuf[4]; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index and format code */ idx = CVmBif::pop_int_val(vmg0_); fmt = CVmBif::pop_int_val(vmg0_); /* get the size from the format */ switch (fmt & FmtSizeMask) { case FmtInt8: default: siz = 1; break; case FmtInt16: siz = 2; break; case FmtInt32: siz = 4; break; } /* check that the index is in range */ if (idx < 1 || idx + siz - 1 > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* make a copy of the bytes in cbuf[] for easy access */ copy_to_buf(cbuf, idx, siz); /* read the value */ switch (siz) { case 1: /* 8-bit integer - all that matters is the signedness */ if ((fmt & FmtSignedMask) == FmtSigned) result = (long)(int)(char)cbuf[0]; else result = cbuf[0]; break; case 2: /* * 16-bit integer. Our standard portable order is little-endian, * so swap bytes if it's big-endian. */ if ((fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, 2); /* it's little-endian now, so pull out the signed or unsigned value */ if ((fmt & FmtSignedMask) == FmtSigned) result = osrp2s(cbuf); else result = osrp2(cbuf); break; case 4: /* * 32-bit integer. Convert to our standard little-endian ordering * if it's currently big-endian, by swapping the byte order. */ if ((fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, 4); /* it's little-endian now, so pull out the signed or unsigned value */ if ((fmt & FmtSignedMask) == FmtSigned) result = osrp4s(cbuf); else result = osrp4(cbuf); break; } /* return the result */ retval->set_int(result); /* handled */ return TRUE; } /* * property evaluator - write an integer */ int CVmObjByteArray::getp_write_int(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(3); unsigned int idx; unsigned int fmt; long val; size_t siz; unsigned char cbuf[4]; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the starting index, format code, and value to write */ idx = CVmBif::pop_int_val(vmg0_); fmt = CVmBif::pop_int_val(vmg0_); val = CVmBif::pop_long_val(vmg0_); /* get the size from the format */ switch (fmt & FmtSizeMask) { case FmtInt8: default: siz = 1; break; case FmtInt16: siz = 2; break; case FmtInt32: siz = 4; break; } /* check that the index is in range */ if (idx < 1 || idx + siz - 1 > get_element_count()) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* write the value to cbuf[] */ switch (siz) { case 1: /* 8-bit integer */ cbuf[0] = (char)(val & 0xFF); break; case 2: /* 16-bit integer - start with our standard little-endian format */ if ((fmt & FmtSignedMask) == FmtSigned) oswp2s(cbuf, (int)val); else oswp2(cbuf, (int)val); break; case 4: /* 32-bit integer - start with our standard little-endian format */ if ((fmt & FmtSignedMask) == FmtSigned) oswp4s(cbuf, val); else oswp4(cbuf, val); break; } /* swap bytes if they want big-endian */ if (siz > 1 && (fmt & FmtOrderMask) == FmtBigEndian) swap_bytes(cbuf, siz); /* save undo for the change */ save_undo(vmg_ self, idx, siz); /* store the byte representation we've constructed */ copy_from_buf(cbuf, idx, siz); /* there's no return value */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Write bytes from the specified region of the array to a file. Returns * zero on success, non-zero on failure. */ int CVmObjByteArray::write_to_file(CVmDataSource *fp, unsigned long start_idx, unsigned long len) const { unsigned long rem; unsigned long idx; /* make sure the starting index is valid */ if (start_idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if the starting index is past the end of the array, there's nothing * to do - just return success */ if (start_idx > get_element_count()) return 0; /* * limit the request to the number of bytes available after the * starting index */ if (start_idx + len - 1 > get_element_count()) len = get_element_count() - start_idx + 1; /* keep going until we satisfy the request or run into a problem */ for (idx = start_idx, rem = len ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit writing to the amount remaining of the requested size */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* * write out this chunk - if an error occurs, abort with a failure * indication */ if (fp->write(p, chunk)) return 1; /* move our counters past this chunk */ idx += chunk; rem -= chunk; } /* we satisfied the request without problems - indicate success */ return 0; } /* * Read bytes from the file into the specified region of the array. * Returns the number of bytes actually read. */ unsigned long CVmObjByteArray::read_from_file( VMG_ vm_obj_id_t self, CVmDataSource *fp, unsigned long start_idx, unsigned long len, int undo) { unsigned long rem; unsigned long idx; unsigned long total; /* make sure the starting index is at least 1 */ if (start_idx < 1) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* * if the starting index is past the end of the array, there's nothing * to do - just return success */ if (start_idx > get_element_count()) return 0; /* if the requested length is zero, consider it a success */ if (len == 0) return 0; /* * limit the request to the number of bytes available after the * starting index */ if (start_idx + len - 1 > get_element_count()) len = get_element_count() - start_idx + 1; /* save undo */ if (undo) save_undo(vmg_ self, start_idx, len); /* keep going until we satisfy the request or run into a problem */ for (idx = start_idx, rem = len, total = 0 ; rem != 0 ; ) { unsigned char *p; size_t avail; size_t chunk; size_t cur_read; /* get the next chunk */ p = get_ele_ptr(idx, &avail); /* limit reading to the amount of the request remaining */ chunk = avail; if (chunk > rem) chunk = (size_t)rem; /* read as much as we can of this chunk */ cur_read = fp->readc(p, chunk); /* add this amount into the total so far */ total += cur_read; /* * if we didn't get as much as we asked for, we must have reached * the end of the file before satisfying the request - there's * nothing more to be read in this case, so we can stop looping */ if (cur_read != chunk) break; /* move our counters past this chunk */ idx += chunk; rem -= chunk; } /* return the total amount we read */ return total; } /* ------------------------------------------------------------------------ */ /* * Write to a 'data' mode file */ int CVmObjByteArray::write_to_data_file(CVmDataSource *fp) { char buf[16]; /* write the number of bytes in our array */ oswp4(buf, get_element_count()); if (fp->write(buf, 4)) return 1; /* write the bytes */ return write_to_file(fp, 1, get_element_count()); } /* * Read from a 'data' mode file */ int CVmObjByteArray::read_from_data_file(VMG_ vm_val_t *retval, CVmDataSource *fp) { char buf[16]; CVmObjByteArray *arr; unsigned long len; /* read the number of bytes in the array */ if (fp->read(buf, 4)) return 1; len = t3rp4u(buf); /* create a new ByteArray to hold the result */ retval->set_obj(create(vmg_ FALSE, len)); arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* read the bytes (it's a new object, so don't save undo) */ if (arr->read_from_file(vmg_ retval->val.obj, fp, 1, len, FALSE) != len) { /* * we didn't manage to read all of the bytes - since the value was * tagged with the correct number of bytes, end-of-file in the * middle of the bytes indicates a corrupted file, so return * failure */ return 1; } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * property evaluator - packBytes */ int CVmObjByteArray::getp_packBytes( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(2, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the starting index from the first argument */ long idx = CVmBif::pop_long_val(vmg0_); --argc; /* limit the index to 1..length */ if (idx < 1) idx = 1; else if ((ulong)idx > get_element_count()) idx = get_element_count(); /* adjust to a zero-based index */ --idx; /* set up a data source for the byte array */ CVmByteArraySource dst(vmg_ self); /* seek to the starting index */ dst.seek(idx, OSFSK_SET); /* do the packing */ CVmPack::pack(vmg_ 0, argc, &dst); /* discard the arguments */ G_stk->discard(argc); /* return the number of bytes written */ retval->set_int(dst.get_pos() - idx); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - static ByteArray.packBytes(). This version of the * method creates a new ByteArray object containing the packed bytes. */ int CVmObjByteArray::static_packBytes(VMG_ vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(1, 0, TRUE); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* set up an in-memory data stream to receive the packed data */ CVmMemorySource *dst = new CVmMemorySource(0L); err_try { /* do the packing */ CVmPack::pack(vmg_ 0, argc, dst); /* create a byte array to hold the packed data */ long len = dst->get_size(); retval->set_obj(create(vmg_ FALSE, len)); CVmObjByteArray *arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj); /* * copy the bytes from the stream to the byte array; it's a new * object, so there's no undo to save */ dst->seek(0, OSFSK_SET); arr->read_from_file(vmg_ retval->val.obj, dst, 1, len, FALSE); } err_finally { /* done with the data stream */ delete dst; } err_end; /* discard the arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - unpackBytes */ int CVmObjByteArray::getp_unpackBytes( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the starting index from the first argument */ long idx = CVmBif::pop_long_val(vmg0_); --argc; /* limit the index to 1..length */ if (idx < 1) idx = 1; else if ((ulong)idx > get_element_count()) idx = get_element_count(); /* get the format string, but leave it on the stack for gc protection */ const char *fmt = G_stk->get(0)->get_as_string(vmg0_); /* get the format string length and buffer pointer */ size_t fmtlen = vmb_get_len(fmt); fmt += VMB_LEN; /* set up a data source for the byte array */ CVmByteArraySource src(vmg_ self); /* seek to the starting index */ src.seek(idx - 1, OSFSK_SET); /* do the unpacking */ CVmPack::unpack(vmg_ retval, fmt, fmtlen, &src); /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - sha256 */ int CVmObjByteArray::getp_sha256( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(0, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the starting index and length */ long cnt = get_element_count(); long idx = argc >= 1 ? CVmBif::pop_int_val(vmg0_) : 1; long len = argc >= 2 ? CVmBif::pop_int_val(vmg0_) : cnt; /* set up a data source on the array, and seek to the starting index */ CVmByteArraySource src(vmg_ self); src.seek(idx - 1, OSFSK_SET); /* calculate the hash */ char buf[65]; sha256_datasrc(buf, &src, len); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, 64)); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * property evaluator - digestMD5 */ int CVmObjByteArray::getp_digestMD5( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { /* check arguments */ uint argc = oargc != 0 ? *oargc : 0; static CVmNativeCodeDesc desc(0, 2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the starting index and length */ long cnt = get_element_count(); long idx = argc >= 1 ? CVmBif::pop_int_val(vmg0_) : 1; long len = argc >= 2 ? CVmBif::pop_int_val(vmg0_) : cnt; /* limit the starting index to the available range */ if (idx < 1) idx = 1; else if (idx > cnt + 1) idx = cnt + 1; /* limit the length to the range remaining */ if (len < 0) len = 0; else if (len > cnt - idx + 1) len = cnt - idx + 1; /* set up the hash accumulator */ md5_state_t ctx; md5_init(&ctx); /* feed the selected range of bytes into the hash */ while (len > 0) { /* get the next chunk */ size_t avail; unsigned char *p = get_ele_ptr(idx, &avail); /* use the whole chunk, up to the amount remaining in the request */ size_t cur = (len < (long)avail ? (size_t)len : avail); /* feed this chunk into the hash */ md5_append(&ctx, p, cur); /* deduct this chunk from the remaining length */ len -= cur; } /* calculate the hash result */ const int HASH_BYTES = 16; unsigned char hash[HASH_BYTES]; md5_finish(&ctx, hash); /* convert the binary hash to printable hex digits */ char buf[HASH_BYTES*2], *bufp = buf; for (int i = 0 ; i < HASH_BYTES ; ++i, bufp += 2) byte_to_xdigits(bufp, hash[i]); /* return the string */ retval->set_obj(CVmObjString::create(vmg_ FALSE, buf, HASH_BYTES*2)); /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmcrc.cpp0000644000175000001440000001030707675131034015347 0ustar realncusers/* Copyright (c) 2003 by Michael J. Roberts. All Rights Reserved. */ /* Name vmcrc.cpp - CRC-32 calculator Function Notes Modified 06/21/03 MJRoberts - Creation */ #include "vmcrc.h" /* * add the given buffer into the checksum */ void CVmCRC32::scan_bytes(const void *ptr, size_t len) { static unsigned long tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; const unsigned char *p; /* add the bytes into the CRC accumulator */ for (p = (const unsigned char *)ptr ; len != 0 ; ++p, --len) acc_ = tab[(acc_ ^ *p) & 0xff] ^ (acc_ >> 8); } frobtads-1.2.3/tads3/vmerrnum.h0000644000175000001440000003577111740653326015572 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMERRNUM.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmerrnum.h - VM error numbers Function Notes VM Error Numbers are in the range 1 to 9999. Modified 10/28/98 MJRoberts - Creation */ #ifndef VMERRNUM_H #define VMERRNUM_H /* ------------------------------------------------------------------------ */ /* * File errors */ /* error reading file */ #define VMERR_READ_FILE 101 /* error writing file */ #define VMERR_WRITE_FILE 102 /* file not found */ #define VMERR_FILE_NOT_FOUND 103 /* error creating file */ #define VMERR_CREATE_FILE 104 /* error closing/committing file */ #define VMERR_CLOSE_FILE 105 /* error deleting file */ #define VMERR_DELETE_FILE 106 /* data packer parsing error at format string index %d */ #define VMERR_PACK_PARSE 107 /* data packer argument mismatch at format string index %d */ #define VMERR_PACK_ARG_MISMATCH 108 /* data packer argument count mismatch at format string index %d */ #define VMERR_PACK_ARGC_MISMATCH 109 /* operation not supported for network server files */ #define VMERR_NET_FILE_NOIMPL 110 /* error renaming file */ #define VMERR_RENAME_FILE 111 /* ------------------------------------------------------------------------ */ /* * Memory manager errors */ /* * requested object ID is already allocated to another object (this * generally indicates that an image file or a saved state file contains * invalid data) */ #define VMERR_OBJ_IN_USE 201 /* out of memory */ #define VMERR_OUT_OF_MEMORY 202 /* out of memory allocating pool page (use a smaller in-memory cache) */ #define VMERR_NO_MEM_FOR_PAGE 203 /* invalid page size - must be a power of two */ #define VMERR_BAD_POOL_PAGE_SIZE 204 /* no more property ID's */ #define VMERR_OUT_OF_PROPIDS 205 /* circular initialization dependency in intrinsic class (internal error) */ #define VMERR_CIRCULAR_INIT 206 /* ------------------------------------------------------------------------ */ /* * Image file errors */ /* * Unsupported metaclass -- the image file requires a metaclass that * this VM implementation does not provide. This may indicate a version * mismatch (a newer version of the VM is required), or that the image * file is designed for a different host application environment. */ #define VMERR_UNKNOWN_METACLASS 301 /* * Unsupported function set "%s" -- the image file requires a function * set that this VM implementation does not provide. This may indicate * a version mismatch (a newer version of the VM is required), or that * the image file is designed for a different host application * environment. */ #define VMERR_UNKNOWN_FUNC_SET 302 /* attempted to read past end of image file */ #define VMERR_READ_PAST_IMG_END 303 /* not an image file (invalid signature) */ #define VMERR_NOT_AN_IMAGE_FILE 304 /* unrecognized block type in image file */ #define VMERR_UNKNOWN_IMAGE_BLOCK 305 /* data block too small (does not contain required data) */ #define VMERR_IMAGE_BLOCK_TOO_SMALL 306 /* ill-formed image file: pool page before pool definition */ #define VMERR_IMAGE_POOL_BEFORE_DEF 307 /* ill-formed image file: pool page out of range of definition */ #define VMERR_IMAGE_POOL_BAD_PAGE 308 /* invalid pool ID in image file */ #define VMERR_IMAGE_BAD_POOL_ID 309 /* attempted to load a pool page at an invalid offset */ #define VMERR_LOAD_BAD_PAGE_IDX 310 /* attempted to load a pool page that hasn't been defined in the image */ #define VMERR_LOAD_UNDEF_PAGE 311 /* ill-formed image file: pool is defined more than once */ #define VMERR_IMAGE_POOL_REDEF 312 /* file contains more than one metaclass dependency table */ #define VMERR_IMAGE_METADEP_REDEF 313 /* file does not contain a metaclass dependency table */ #define VMERR_IMAGE_NO_METADEP 314 /* file contains more than one than one function set dependency table */ #define VMERR_IMAGE_FUNCDEP_REDEF 315 /* file does not contain a function set dependency table */ #define VMERR_IMAGE_NO_FUNCDEP 316 /* file contains multiple entrypoint definitions */ #define VMERR_IMAGE_ENTRYPT_REDEF 317 /* file does not contain an entrypoint definition */ #define VMERR_IMAGE_NO_ENTRYPT 318 /* incompatible image file format */ #define VMERR_IMAGE_INCOMPAT_VSN 319 /* cannot execute - image contains no code */ #define VMERR_IMAGE_NO_CODE 320 /* image file has incompatible format: method header too old */ #define VMERR_IMAGE_INCOMPAT_HDR_FMT 321 /* * Unavailable intrinsic function called (index %d in function set "%s") * -- this indicates that a function was invoked in a function set that * provides only a subset of its functions. This normally can occur * only when running 'preinit' or similar special situations when * intrinsics are resolved on each call rather than during the initial * load. */ #define VMERR_UNAVAIL_INTRINSIC 322 /* unknown internal metaclass ID %d */ #define VMERR_UNKNOWN_METACLASS_INTERNAL 323 /* page mask not allowed for in-memory image file */ #define VMERR_XOR_MASK_BAD_IN_MEM 324 /* no image file found in executable */ #define VMERR_NO_IMAGE_IN_EXE 325 /* object size exceeds hardware limits of this computer */ #define VMERR_OBJ_SIZE_OVERFLOW 326 /* metaclass "%s" version is not available (latest available is "%s") */ #define VMERR_METACLASS_TOO_OLD 327 /* incorrect metaclass data (corrupted image file) */ #define VMERR_INVAL_METACLASS_DATA 328 /* object cannot be created */ #define VMERR_BAD_STATIC_NEW 329 /* function set "%s" version is not available (latest available is "%s") */ #define VMERR_FUNCSET_TOO_OLD 330 /* invalid type for exported symbol %s */ #define VMERR_INVAL_EXPORT_TYPE 331 /* invalid data in image file macro definitions (code %d) */ #define VMERR_INVAL_IMAGE_MACRO 332 /* no "mainRestore" export - cannot restore saved state on startup */ #define VMERR_NO_MAINRESTORE 333 /* incompatible debugger - image file must be recompiled for this debugger */ #define VMERR_IMAGE_INCOMPAT_VSN_DBG 334 /* ------------------------------------------------------------------------ */ /* * Network errors */ /* network safety level error (operation prohibited by safety settings) */ #define VMERR_NETWORK_SAFETY 400 /* ------------------------------------------------------------------------ */ /* * Property-related errors */ /* this property cannot be set for this object */ #define VMERR_INVALID_SETPROP 1001 /* ------------------------------------------------------------------------ */ /* * Saved state file errors */ /* file is not a valid saved state file */ #define VMERR_NOT_SAVED_STATE 1201 /* file was not saved by the same image or image version */ #define VMERR_WRONG_SAVED_STATE 1202 /* metaclass name is too long in saved file */ #define VMERR_SAVED_META_TOO_LONG 1203 /* object ID in saved state is invalid */ #define VMERR_SAVED_OBJ_ID_INVALID 1206 /* saved state file is corrupted (checksum does not match) */ #define VMERR_BAD_SAVED_STATE 1207 /* invalid data in saved metaclass */ #define VMERR_BAD_SAVED_META_DATA 1208 /* storage server error */ #define VMERR_STORAGE_SERVER_ERR 1209 /* save file description table too large (64k format limit) */ #define VMERR_DESC_TAB_OVERFLOW 1210 /* ------------------------------------------------------------------------ */ /* * Data manipulation and conversion errors */ /* cannot convert value to a string */ #define VMERR_NO_STR_CONV 2001 /* conversion buffer overflow */ #define VMERR_CONV_BUF_OVF 2002 /* invalid datatype for "add" operator */ #define VMERR_BAD_TYPE_ADD 2003 /* numeric value required */ #define VMERR_NUM_VAL_REQD 2004 /* integer value required */ #define VMERR_INT_VAL_REQD 2005 /* cannot convert to logical value */ #define VMERR_NO_LOG_CONV 2006 /* invalid datatype for "subtract" operator */ #define VMERR_BAD_TYPE_SUB 2007 /* division by zero */ #define VMERR_DIVIDE_BY_ZERO 2008 /* invalid comparison */ #define VMERR_INVALID_COMPARISON 2009 /* object value required */ #define VMERR_OBJ_VAL_REQD 2010 /* property pointer value required */ #define VMERR_PROPPTR_VAL_REQD 2011 /* logical value required */ #define VMERR_LOG_VAL_REQD 2012 /* function pointer value required */ #define VMERR_FUNCPTR_VAL_REQD 2013 /* indexing operation cannot be applied to this datatype */ #define VMERR_CANNOT_INDEX_TYPE 2014 /* index value out of range */ #define VMERR_INDEX_OUT_OF_RANGE 2015 /* metaclass index out of range (probable image file error) */ #define VMERR_BAD_METACLASS_INDEX 2016 /* invalid dynamic object creation (metaclass does not support NEW) */ #define VMERR_BAD_DYNAMIC_NEW 2017 /* object value required for superclass */ #define VMERR_OBJ_VAL_REQD_SC 2018 /* string value required */ #define VMERR_STRING_VAL_REQD 2019 /* list value required */ #define VMERR_LIST_VAL_REQD 2020 /* list or string reference found in dictionary - can't convert to const */ #define VMERR_DICT_NO_CONST 2021 /* invalid object type */ #define VMERR_INVAL_OBJ_TYPE 2022 /* numeric overflow */ #define VMERR_NUM_OVERFLOW 2023 /* invalid datatype for "multiply" operator */ #define VMERR_BAD_TYPE_MUL 2024 /* invalid datatype for "divide" operator */ #define VMERR_BAD_TYPE_DIV 2025 /* invalid datatype for "negate" operator */ #define VMERR_BAD_TYPE_NEG 2026 /* value out of range */ #define VMERR_OUT_OF_RANGE 2027 /* string too large */ #define VMERR_STR_TOO_LONG 2028 /* list too large */ #define VMERR_LIST_TOO_LONG 2029 /* tree depth too large for equality test/hash calculation */ #define VMERR_TREE_TOO_DEEP_EQ 2030 /* cannot convert value to integer */ #define VMERR_NO_INT_CONV 2031 /* bad type for modulo */ #define VMERR_BAD_TYPE_MOD 2032 /* bad type for bitwise AND */ #define VMERR_BAD_TYPE_BIT_AND 2033 /* bad type for bitwise OR */ #define VMERR_BAD_TYPE_BIT_OR 2034 /* bad type for XOR */ #define VMERR_BAD_TYPE_XOR 2035 /* bad type for left shift */ #define VMERR_BAD_TYPE_SHL 2036 /* bad type for arithmetic right shift */ #define VMERR_BAD_TYPE_ASHR 2037 /* bad type for bitwise NOT */ #define VMERR_BAD_TYPE_BIT_NOT 2038 /* code pointer value required */ #define VMERR_CODEPTR_VAL_REQD 2039 /* * exception object required (the VM threw an error based on an exception * class defined in the bytecode program, but 'new X' didn't yield an * object) */ #define VMERR_EXCEPTION_OBJ_REQD 2040 /* cannot convert value to double */ #define VMERR_NO_DOUBLE_CONV 2041 /* cannot convert value to number */ #define VMERR_NO_NUM_CONV 2042 /* bad type for logical right shift */ #define VMERR_BAD_TYPE_LSHR 2043 /* ------------------------------------------------------------------------ */ /* * Method and function invocation errors */ /* wrong number of arguments */ #define VMERR_WRONG_NUM_OF_ARGS 2201 /* wrong number of arguments calling %s */ #define VMERR_WRONG_NUM_OF_ARGS_CALLING 2202 /* nil object reference */ #define VMERR_NIL_DEREF 2203 /* missing named argument */ #define VMERR_MISSING_NAMED_ARG 2204 /* invalid type for call */ #define VMERR_BAD_TYPE_CALL 2205 /* 'self' cannot be nil */ #define VMERR_NIL_SELF 2206 /* ------------------------------------------------------------------------ */ /* * Object creation errors */ /* * cannot create an instance of the object, because the object is not a * class */ #define VMERR_CANNOT_CREATE_INST 2270 /* class cannot be created */ #define VMERR_ILLEGAL_NEW 2271 /* ------------------------------------------------------------------------ */ /* * General execution errors */ /* invalid opcode */ #define VMERR_INVALID_OPCODE 2301 /* unhandled exception */ #define VMERR_UNHANDLED_EXC 2302 /* stack overflow */ #define VMERR_STACK_OVERFLOW 2303 /* invalid type for built-in function */ #define VMERR_BAD_TYPE_BIF 2304 /* default string output function not defined */ #define VMERR_SAY_IS_NOT_DEFINED 2305 /* invalid value for built-in function (value out of range) */ #define VMERR_BAD_VAL_BIF 2306 /* breakpoint encountered */ #define VMERR_BREAKPOINT 2307 /* external function calls not implemented */ #define VMERR_CALLEXT_NOT_IMPL 2308 /* invalid opcode modifier */ #define VMERR_INVALID_OPCODE_MOD 2309 /* no character mapping file available */ #define VMERR_NO_CHARMAP_FILE 2310 /* unhandled exception: %s */ #define VMERR_UNHANDLED_EXC_PARAM 2311 /* VM Error: %s */ #define VMERR_VM_EXC_PARAM 2312 /* VM Error: code %d */ #define VMERR_VM_EXC_CODE 2313 /* exception executing static initializer for %*s.%*s: %s */ #define VMERR_EXC_IN_STATIC_INIT 2314 /* * intrinsic class error (this is used as a fallback when an intrinsic * class wants to throw an imported exception class, but the import * doesn't exist; this doesn't provide any real information, but if the * program doesn't export the required class then it's obviously not set * up to receive any information anyway) * * this exception takes a string describing the error */ #define VMERR_INTCLS_GENERAL_ERROR 2315 /* stack access out of bounds */ #define VMERR_STACK_OUT_OF_BOUNDS 2316 /* ------------------------------------------------------------------------ */ /* * Debugger interface errors. These are errors that the debugger throws * in response to the corresponding UI commands, to allow the user some * rough interactive control over execution in the debugger environment. */ /* abort - abort current command */ #define VMERR_DBG_ABORT 2391 /* restart - restart program execution */ #define VMERR_DBG_RESTART 2392 /* halt - terminate the program */ #define VMERR_DBG_HALT 2394 /* interrupt - user interrupted current operation (Ctrl+Break, etc) */ #define VMERR_DBG_INTERRUPT 2395 /* debugger not available */ #define VMERR_NO_DEBUGGER 2396 /* ------------------------------------------------------------------------ */ /* * Debugger-related errors */ /* invalid frame (in debug local/parameter operation) */ #define VMERR_BAD_FRAME 2500 /* invalid speculative evaluation */ #define VMERR_BAD_SPEC_EVAL 2501 /* invalid in debugger expression */ #define VMERR_INVAL_DBG_EXPR 2502 /* image file has no debugging information (must be recompiled) */ #define VMERR_NO_IMAGE_DBG_INFO 2503 /* ------------------------------------------------------------------------ */ /* * BigNumber package errors */ /* out of temporary registers - BigNumber calculation too complex */ #define VMERR_BIGNUM_NO_REGS 2600 /* value can't be converted to BigNumber */ #define VMERR_NO_BIGNUM_CONV 2601 #endif /* VMERRNUM_H */ frobtads-1.2.3/tads3/vmlookup.h0000644000175000001440000007575311745030653015574 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlookup.h - LookupTable metaclass Function Notes Modified 02/06/01 MJRoberts - Creation */ #ifndef VMLOOKUP_H #define VMLOOKUP_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmcoll.h" #include "vmiter.h" /* ------------------------------------------------------------------------ */ /* * The image file data block is arranged as follows: * *. UINT2 bucket_count *. UINT2 value_count *. UINT2 first_free_index *. UINT2 bucket_index[1] *. UINT2 bucket_index[2] *. ... *. UINT2 bucket_index[N] *. value[1] *. value[2] *. ... *. value[N] *. --- the following are only used in version 030003 and later --- *. DATAHOLDER default_value * * value_count gives the number of value slots allocated. Free value slots * are kept in a linked list, the head of which is at the 1-based index * given by first_free_index. If first_free_index is zero, there are no * free value slots. * * Each bucket_index[i] is the 1-based index of the first value in the * chain for that hash bucket. If the value in a bucket_index[i] is zero, * there are values for that bucket. * * Each free entry has a VM_EMPTY value stored in its key to indicate that * it's empty. * * Each value[i] looks like this: * *. DATAHOLDER key *. DATAHOLDER value *. UINT2 next_index * * next_index gives the 1-based index of the next value in the chain for * that bucket; a value of zero indicates that this is the last value in * the chain. * * For version 030003 and later, additional data MAY be supplied. This can * be detected by checking the data block size: if more bytes follow the * last value slot, the extended version 030003 elements are present. * * default_value is the default value to return when the table is indexed * by a key that doesn't exist in the table. This is nil by default. */ /* value entry size */ #define VMLOOKUP_VALUE_SIZE (VMB_DATAHOLDER + VMB_DATAHOLDER + VMB_UINT2) /* ------------------------------------------------------------------------ */ /* * in-memory value entry structure */ struct vm_lookup_val { /* the key */ vm_val_t key; /* the value */ vm_val_t val; /* next entry in same hash bucket */ vm_lookup_val *nxt; }; /* * Our in-memory extension data structure, which mimics the image file * structure but uses native types. */ struct vm_lookup_ext { /* allocate the structure, given the number of buckets and values */ static vm_lookup_ext *alloc_ext(VMG_ class CVmObjLookupTable *self, uint bucket_cnt, uint value_cnt); /* * Initialize the extension - puts all values into the free list and * clears all buckets. We don't do this automatically as part of * allocation, because some types of allocation set up the buckets and * free list from a known data set and thus are more efficient if they * skip the initialization step. */ void init_ext(); /* * Reallocate the structure with a larger number of values. Copies * all of the data from the original hash table into the new hash * table, and deletes the old structure. */ static vm_lookup_ext *expand_ext(VMG_ class CVmObjLookupTable *self, vm_lookup_ext *old_ext, uint new_value_cnt); /* * Copy the given extension's data into myself. This can only be used * when we have the same bucket count as the original (the entry count * need not be the same, but it must be large enough to hold all of * the data from the original). * * This loses any data previously in the table. */ void copy_ext_from(vm_lookup_ext *old_ext); /* allocate a value entry out of my free list */ vm_lookup_val *alloc_val_entry() { /* if the free list is empty, return failure */ if (first_free == 0) return 0; /* take the first item off the free list */ vm_lookup_val *entry = first_free; /* unlink it from the free list */ first_free = first_free->nxt; /* return the allocated item */ return entry; } /* * Add a value into the given hash bucket. The caller is responsible * for ensuring there's enough room. */ void add_val(uint hash, const vm_val_t *key, const vm_val_t *val) { /* allocate a new entry */ vm_lookup_val *entry = alloc_val_entry(); /* set it up with the new data */ entry->key = *key; entry->val = *val; /* link it into the given bucket */ entry->nxt = buckets[hash]; buckets[hash] = entry; } /* * Given a pool index, retrieve a value entry from our pool of value * entries. This has nothing to do with the hash bucket lists or the * free list - this is simply the nth entry in the master pool of all * values. */ vm_lookup_val *idx_to_val(uint idx) const { vm_lookup_val *pool; /* the pool of values starts immediately after the buckets */ pool = (vm_lookup_val *)(void *)&buckets[bucket_cnt]; /* return the nth element of the pool */ return &pool[idx]; } /* given a value entry, get the pool index */ uint val_to_idx(vm_lookup_val *val) const { return (val - idx_to_val(0)); } /* * Convert an image-file or save-file index to a value pointer. These * are given as 1-based pointers, with the special value zero used to * indicate a null pointer. */ vm_lookup_val *img_idx_to_val(uint idx) const { if (idx == 0) return 0; else return idx_to_val(idx - 1); } /* convert a value pointer to an image file index */ uint val_to_img_idx(vm_lookup_val *val) { /* * use zero for a null pointer; otherwise, use a 1-based index in * our master value pool */ if (val == 0) return 0; else return (val - idx_to_val(0)) + 1; } /* number of buckets and number of allocated value entries */ uint bucket_cnt; uint value_cnt; /* pointer to the first free value */ vm_lookup_val *first_free; /* * default value - this is returned when we index the table by a key * that doesn't exist in the table */ vm_val_t default_value; /* * buckets (we overallocate the structure to make room): each bucket * points to the first entry in the list of entries at this hash value */ vm_lookup_val *buckets[1]; }; /* ------------------------------------------------------------------------ */ /* * undo action codes */ enum lookuptab_undo_action { /* * null record - we use this to mark a record that has become * irrelevant because of a stale weak reference */ LOOKUPTAB_UNDO_NULL, /* we added this word to the dictionary (undo by deleting it) */ LOOKUPTAB_UNDO_ADD, /* we deleted this word from the dictionary (undo by adding it back) */ LOOKUPTAB_UNDO_DEL, /* we modified the value for a given key */ LOOKUPTAB_UNDO_MOD, /* we changed the default value for the table */ LOOKUPTAB_UNDO_DEFVAL }; /* ------------------------------------------------------------------------ */ /* * LookupTable metaclass */ class CVmObjLookupTable: public CVmObjCollection { friend class CVmObjIterLookupTable; friend class CVmMetaclassLookupTable; friend class CVmObjWeakRefLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjCollection::is_of_metaclass(meta)); } /* is this a lookup table object? */ static int is_lookup_table_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create */ static vm_obj_id_t create(VMG_ int in_root_set, uint bucket_count, uint init_capacity); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjCollection:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* we cannot be converted to constant data */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* apply an undo record */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* discard an undo record */ void discard_undo(VMG_ struct CVmUndoRecord *); /* mark undo references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint); /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } /* get an entry; returns true if the entry exists, false if not */ int index_check(VMG_ vm_val_t *result, const vm_val_t *index_val); /* get a value by index */ int index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val); /* set a value by index */ int set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val); /* add an entry - does not generate undo */ void add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* set or add an entry - does not generate undo */ void set_or_add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* make a list of keys/values in the table */ void keys_to_list(VMG_ vm_val_t *lst, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *) = 0) { make_list(vmg_ lst, TRUE, filter); } void vals_to_list(VMG_ vm_val_t *lst, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *) = 0) { make_list(vmg_ lst, FALSE, filter); } /* iterate over the table's contents through a callback */ void for_each(VMG_ void (*cb)(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx), void *ctx); /* get the default value */ void get_default_val(vm_val_t *val); protected: /* get and range-check the constructor arguments */ static void get_constructor_args(VMG_ uint argc, size_t *bucket_count, size_t *init_capacity, vm_val_t *src_obj); /* populate the table from a Key->Value list or vector */ void populate_from_list(VMG_ const vm_val_t *src_obj); /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a new object as a copy of this object */ vm_obj_id_t create_copy(VMG0_); /* add an entry, generating undo */ void add_entry_undo(VMG_ vm_obj_id_t self, const vm_val_t *key, const vm_val_t *val); /* delete an entry - does not generate undo */ void del_entry(VMG_ const vm_val_t *key); /* * Unlink an entry - does not generate undo. 'prv_entry' is the * previous entry in the hash chain containing this entry, and * 'hashval' is the bucket containing the entry. Pass null for * 'prv_entry' when the entry to unlink is the first entry in its hash * chain. */ void unlink_entry(VMG_ vm_lookup_val *entry, uint hashval, vm_lookup_val *prv_entry); /* * modify an entry - changes the value associated with the given key; * does not generate undo */ void mod_entry(VMG_ const vm_val_t *key, const vm_val_t *val); /* find an entry */ vm_lookup_val *find_entry(VMG_ const vm_val_t *key, uint *hashval_p, vm_lookup_val **prv_entry_p); /* * Check the table to make sure there's enough free space to add one * new item, and expand the table if necessary. */ void expand_if_needed(VMG0_); /* allocate a new entry, expanding the table if necessary */ vm_lookup_val *alloc_new_entry(VMG0_); /* calculate a value's hash code */ uint calc_key_hash(VMG_ const vm_val_t *key); /* get my extension data */ vm_lookup_ext *get_ext() const { return (vm_lookup_ext *)ext_; } /* get the hash bucket count */ uint get_bucket_count() const { return get_ext()->bucket_cnt; } /* get the value entry count */ uint get_entry_count() const { return get_ext()->value_cnt; } /* get/set the first-free item */ vm_lookup_val *get_first_free() const { return get_ext()->first_free; } void set_first_free(vm_lookup_val *p) { get_ext()->first_free = p; } /* get a bucket's first entry given a hash code */ vm_lookup_val **get_bucket(uint hash) const { return &get_ext()->buckets[hash]; } /* set a bucket's contents given a hash code */ void set_bucket(uint hash, vm_lookup_val *p) { get_ext()->buckets[hash] = p; } /* set an entry, keeping undo for the change */ void set_entry_val_undo(VMG_ vm_obj_id_t self, vm_lookup_val *entry, const vm_val_t *val); /* create an iterator */ virtual void new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a live iterator */ virtual void new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val); /* create a lookup table with no initial contents */ CVmObjLookupTable() { ext_ = 0; } /* * Create a lookup table with a given number of hash table buckets, * and the given number of entry slots. The hash table bucket count * is fixed for the life of the object. The entry slot count is * merely advisory: the table size will be increased as necessary to * accommodate new elements. */ CVmObjLookupTable(VMG_ size_t hash_count, size_t entry_count); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - remove an entry given the key */ int getp_remove_entry(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - determine if a given key is in the table */ int getp_key_present(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - apply a callback to each element */ int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* property evaluator - call a callback on each element */ int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general forEach/forEachAssoc processor */ int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, int pass_key_to_cb, struct vm_rcdesc *rc); /* get the number of buckets in the table */ int getp_count_buckets(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the number of entries in the table */ int getp_count_entries(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* make a list of all of the keys in the table */ int getp_keys_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* make a list of all of the values in the table */ int getp_vals_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the default value */ int getp_get_def_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* set the default value */ int getp_set_def_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* general handler for making a list of keys or values */ int getp_make_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int store_keys); /* get the nth key */ int getp_nthKey(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* get the nth value */ int getp_nthVal(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); /* * Get the nth element's key and/or value. This only counts in-use * buckets. If idx is zero, we return the default value. If idx is * out of range, we throw an error. */ void get_nth_ele(VMG_ vm_val_t *key, vm_val_t *val, vm_obj_id_t self, long idx); /* make a list of the keys or values in the table */ void make_list(VMG_ vm_val_t *retval, int store_keys, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *)); /* add a record to the global undo stream */ void add_undo_rec(VMG_ vm_obj_id_t self, enum lookuptab_undo_action action, const vm_val_t *key, const vm_val_t *old_entry_val); /* property evaluation function table */ static int (CVmObjLookupTable::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * WeakRefLookupTable - a subclass of LookupTable that places weak * references on its values. The keys are still strong references. */ class CVmObjWeakRefLookupTable: public CVmObjLookupTable { friend class CVmMetaclassWeakRefLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjLookupTable::is_of_metaclass(meta)); } /* create */ static vm_obj_id_t create(VMG_ int in_root_set, uint bucket_count, uint init_capacity); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* mark references */ void mark_refs(VMG_ uint); /* mark undo references */ void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references */ void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec); /* remove stale weak references */ void remove_stale_weak_refs(VMG0_); protected: /* create a lookup table with no initial contents */ CVmObjWeakRefLookupTable() { ext_ = 0; } /* construct */ CVmObjWeakRefLookupTable(VMG_ size_t hash_count, size_t entry_count) : CVmObjLookupTable(vmg_ hash_count, entry_count) { } }; /* ------------------------------------------------------------------------ */ /* * LookupTable Registration table object */ class CVmMetaclassLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "lookuptable/030003"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjLookupTable::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a Collection object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjCollection::metaclass_reg_; } }; /* * WeakRefLookupTable registration object */ class CVmMetaclassWeakRefLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "weakreflookuptable/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjWeakRefLookupTable(); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjWeakRefLookupTable(); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjWeakRefLookupTable:: create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjWeakRefLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* I'm a LookupTable object */ CVmMetaclass *get_supermeta_reg() const { return CVmObjLookupTable::metaclass_reg_; } }; /* ------------------------------------------------------------------------ */ /* * LookupTable Iterator subclass. This iterator is tightly coupled with * the LookupTable class, since we must look into the internal * implementation of the LookupTable to implement an iterator on it. */ /* * The extension data for an lookup table iterator consists of a reference * to the associated LookupTable object, the current bucket index, and the * index of the current entry. * * DATAHOLDER lookuptable_value *. UINT2 cur_entry_index (1-based; zero is invalid) *. UINT2 flags * * The flag values are: * * VMOBJITERLOOKUPTABLE_UNDO - we've saved undo for this savepoint. If * this is set, we won't save additional undo for the same savepoint. */ /* total extension size */ #define VMOBJITERLOOKUPTABLE_EXT_SIZE (VMB_DATAHOLDER + 2 + 2) /* * flag bits */ /* we've saved undo for the current savepoint */ #define VMOBJITERLOOKUPTABLE_UNDO 0x0001 /* * LookupTable iterator class */ class CVmObjIterLookupTable: public CVmObjIter { friend class CVmMetaclassIterLookupTable; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObjIter::is_of_metaclass(meta)); } /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* * Create a lookup table iterator. This method is to be called by the * lookup table to create an iterator for its value. */ static vm_obj_id_t create_for_coll(VMG_ const vm_val_t *coll); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* * notify of a new savepoint - clear the 'undo' flag, since we cannot * have created any undo information yet for the new savepoint */ void notify_new_savept() { set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* apply undo */ void apply_undo(VMG_ struct CVmUndoRecord *rec); /* mark references */ void mark_refs(VMG_ uint state); /* there are no references in our undo stream */ void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we keep only strong references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* * determine if the object has been changed since it was loaded - * assume we have */ int is_changed_since_load() const { return TRUE; } /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self); protected: /* create */ CVmObjIterLookupTable() { ext_ = 0; } /* create */ CVmObjIterLookupTable(VMG_ const vm_val_t *coll); /* find the first valid entry, starting at the given index */ uint find_first_valid_entry(VMG_ uint entry) const; /* property evaluator - get next value */ virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - is next value available? */ virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - reset to first item */ virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current key */ virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluator - get current value */ virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get information on the current entry */ void get_cur_entry(VMG_ vm_val_t *valp, vm_val_t *keyp) const; /* get my collection value */ void get_coll_val(vm_val_t *val) const { vmb_get_dh(ext_, val); } /* get/set the current entry index (without saving undo) */ long get_entry_index() const { return osrp2(ext_ + VMB_DATAHOLDER); } void set_entry_index(uint idx) { oswp2(ext_ + VMB_DATAHOLDER, idx); } /* update the entry index value, saving undo if necessary */ void set_entry_index_undo(VMG_ vm_obj_id_t self, uint entry); /* get/set the flags */ uint get_flags() const { return osrp2(ext_ + VMB_DATAHOLDER + 2); } void set_flags(uint flags) const { oswp2(ext_ + VMB_DATAHOLDER + 2, flags); } }; /* * Registration table object for lookup table iterators */ class CVmMetaclassIterLookupTable: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "lookuptable-iterator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjIterLookupTable(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { err_throw(VMERR_BAD_DYNAMIC_NEW); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjIterLookupTable:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMLOOKUP_H */ /* * Register the classes */ VM_REGISTER_METACLASS(CVmObjLookupTable) VM_REGISTER_METACLASS(CVmObjWeakRefLookupTable) VM_REGISTER_METACLASS(CVmObjIterLookupTable) frobtads-1.2.3/tads3/vmhttpreq.h0000644000175000001440000002635211736326622015745 0ustar realncusers/* $Header$ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpreq.h - CVmObjHTTPRequest object Function CVmObjHTTPRequest is the TADS intrinsic class wrapper for our native (C++) object TadsHttpRequest. The native object is part of the vmnet layer, which uses reference counting to manage memory. This VM object wrapper brings the object into the VM's garbage collection system, and also provides method access to the byte-code program. The native network layer's HTTP server creates the C++ TadsHttpRequest object when a request comes in from a network client. The request object goes into the message queue, which the main game thread reads via the getNetEvent() intrinsic function. When that function pulls a request object off the queue, it synthesizes the appropriate wrapper to return to the byte-code program. That's when CVmObjHTTPRequest is instatiated. The wrapper then keeps track of the VM's reference on the underlying request object, so that the request object stays in memory as long as the VM has a reference. Once the VM wrapper goes out of scope and is collected by the garbage collector, the wrapper releases its reference on the underlying C++ object. Notes Modified MJRoberts - Creation */ #ifndef VMHTTPREQ_H #define VMHTTPREQ_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * Image file data: this intrinsic class is inherently transient, so it's * not stored in an image file. We thus don't need any image file data * structure. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. This is usually a 'struct' * that contains the same information as in the image file, but using * native types. */ struct vm_httpreq_ext { /* allocate the structure */ static vm_httpreq_ext *alloc_ext(VMG_ class CVmObjHTTPRequest *self); /* the network message object */ class TadsHttpRequest *req; /* list of cookies to be sent with the reply */ struct vm_httpreq_cookie *cookies; /* the HTTPServer object for the server that created the request */ vm_obj_id_t server; }; /* * Cookie tracker */ struct vm_httpreq_cookie { vm_httpreq_cookie(const char *name, size_t namelen, const char *val, size_t vallen) { this->name = lib_copy_str(name, namelen); this->val = lib_copy_str(val, vallen); nxt = 0; } ~vm_httpreq_cookie() { lib_free_str(name); lib_free_str(val); if (nxt != 0) delete nxt; } /* clone this cookie and our list tail */ vm_httpreq_cookie *clone() { /* clone myself */ vm_httpreq_cookie *c = new vm_httpreq_cookie( name, strlen(name), val, val != 0 ? strlen(val) : 0); /* clone my list tail */ if (nxt != 0) c->nxt = nxt->clone(); /* return the clone */ return c; } /* send the cookie; returns true on success, false on failure */ int send(class TadsServerThread *t); /* name of the cookie */ char *name; /* value (including expiration, path, domain, etc) */ char *val; /* next in list */ vm_httpreq_cookie *nxt; }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest intrinsic class definition */ class CVmObjHTTPRequest: public CVmObject { friend class CVmMetaclassHTTPRequest; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is this a CVmObjHTTPRequest object? */ static int is_vmhttpreq_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Create the request object. 'srv_obj' is the HTTPServer object for * the listener that received the request. */ static vm_obj_id_t create(VMG_ int in_root_set, class TadsHttpRequest *req, vm_obj_id_t srv_obj); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* we don't participate in undo */ void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* we don't have any weak references */ void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* * determine if we've been changed since loading - assume we have (if * we haven't, the only harm is the cost of unnecessarily reloading or * saving) */ int is_changed_since_load() const { return TRUE; } protected: /* get my extension data */ vm_httpreq_ext *get_ext() const { return (vm_httpreq_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjHTTPRequest() { ext_ = 0; } /* create with contents ('flag' is just for overloading resolution) */ CVmObjHTTPRequest(VMG_ int flag); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* get the server object */ int getp_getServer(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the HTTP verb */ int getp_getVerb(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the query string */ int getp_getQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* parse the query string into the resource name and parameters */ int getp_parseQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* parse the query string and find a given parameter by name */ int getp_getQueryParam(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the request headers */ int getp_getHeaders(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get a single cookie */ int getp_getCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the cookies */ int getp_getCookies(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* set a cookie for the reply */ int getp_setCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the cookies */ int getp_getFormFields(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the request message body */ int getp_getBody(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* get the client network address */ int getp_getClientAddress( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a reply */ int getp_sendReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* common handler for sendReply and sendReplyAsync */ int common_sendReply( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc, int wait); /* start a chunked reply */ int getp_startChunkedReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a piece of a chunked reply */ int getp_sendReplyChunk(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* finish a chunked reply */ int getp_endChunkedReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* send a reply asynchronously */ int getp_sendReplyAsync( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjHTTPRequest::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest metaclass registration table object */ class CVmMetaclassHTTPRequest: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "http-request/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPRequest(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjHTTPRequest(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); G_obj_table->set_obj_transient(id); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjHTTPRequest::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjHTTPRequest:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMHTTPREQ_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjHTTPRequest) frobtads-1.2.3/tads3/vmundo.cpp0000644000175000001440000003717312145504453015554 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMUNDO.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmundo.cpp - undo manager Function Notes Modified 10/29/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmundo.h" /* * create the undo manager */ CVmUndo::CVmUndo(size_t undo_record_cnt, uint max_savepts) { /* remember the maximum number of savepoints */ max_savepts_ = (vm_savept_t)max_savepts; /* * initialize the savepoint to number 0 (this is arbitrary, but we * might as well start here) */ cur_savept_ = 0; /* no savepoints have been created yet */ savept_cnt_ = 0; /* create the undo record array */ rec_arr_size_ = undo_record_cnt; rec_arr_ = (CVmUndoMeta *)t3malloc(rec_arr_size_ * sizeof(rec_arr_[0])); /* we have no records at all yet, so we have no "firsts" */ cur_first_ = 0; oldest_first_ = 0; /* start allocating from the first entry in the array */ next_free_ = rec_arr_; } /* * delete the undo manager */ CVmUndo::~CVmUndo() { /* delete the array of undo records */ t3free(rec_arr_); } /* * create a savepoint */ void CVmUndo::create_savept(VMG0_) { CVmUndoMeta *rec; /* * Allocate an undo record to serve as the link pointer for this * savepoint. If this fails, we cannot create a savepoint; we won't * need to do anything else in this case, since we will have deleted * all existing savepoints if we ran out of records. */ rec = alloc_rec(vmg0_); if (rec == 0) return; /* if we have an old savepoint, the new record starts the next one */ if (cur_first_ != 0) cur_first_->link.next_first = rec; /* the old savepoint (if any) is our previous savepoint */ rec->link.prev_first = cur_first_; /* there's nothing after us yet */ rec->link.next_first = 0; /* this record is now the current savepoint's first record */ cur_first_ = rec; /* * if we didn't have any savepoints recorded yet, this is now the * first record in the oldest savepoint */ if (oldest_first_ == 0) oldest_first_ = rec; /* * increment the savepoint number, wrapping to zero if we reach the * highest value we can store */ if (cur_savept_ == VM_SAVEPT_MAX) cur_savept_ = 0; else ++cur_savept_; /* * if we're already storing the maximum number of savepoints we're * allowed to store simultaneously, drop the oldest savepoint; * otherwise, simply increment the savepoint counter */ if (savept_cnt_ == max_savepts_) { /* we have the maximum number of savepoints; discard the oldest */ drop_oldest_savept(vmg0_); } /* count the new savepoint */ ++savept_cnt_; /* notify the objects that we're starting a new savepoint */ G_obj_table->notify_new_savept(); } /* * Discard the oldest savepoint */ void CVmUndo::drop_oldest_savept(VMG0_) { /* if there are no savepoints, there's nothing to do */ if (savept_cnt_ == 0) return; /* decrement the savepoint count, now that this one is gone */ --savept_cnt_; /* forget the oldest lead pointer */ if (oldest_first_ != 0) { /* * discard records from the oldest lead pointer to the next * oldest lead pointer */ CVmUndoMeta *meta; for (meta = oldest_first_, inc_rec_ptr(&meta) ; meta != oldest_first_->link.next_first && meta != next_free_ ; ) { /* discard this entry, if it still exists */ if (meta->rec.obj != VM_INVALID_OBJ) vm_objp(vmg_ meta->rec.obj)->discard_undo(vmg_ &meta->rec); /* advance to the next record */ inc_rec_ptr(&meta); } /* advance the oldest pointer to the next savepoint's first record */ oldest_first_ = oldest_first_->link.next_first; /* there's now nothing before this one */ if (oldest_first_ != 0) oldest_first_->link.prev_first = 0; } /* if we don't have an oldest, we also don't have a current */ if (oldest_first_ == 0) cur_first_ = 0; } /* * Drop all undo information */ void CVmUndo::drop_undo(VMG0_) { /* delete each savepoint until we have none left */ while (savept_cnt_ != 0) drop_oldest_savept(vmg0_); /* reset the savepoint number */ cur_savept_ = 0; /* we have no "firsts" */ cur_first_ = 0; oldest_first_ = 0; /* start allocating from the start of the array */ next_free_ = rec_arr_; } /* * Allocate an undo record. If we run out of free records, delete * savepoints, starting with the oldest savepoint, until we have a free * record. */ CVmUndoMeta *CVmUndo::alloc_rec(VMG0_) { /* * keep trying until we find an entry or run out of savepoints to * delete */ for (;;) { /* * If we have no savepoints at all, all records are free. * Otherwise, if the next free pointer hasn't bumped up against * the oldest allocated record yet, we can allocate another * record. */ if (savept_cnt_ == 0 || next_free_ != oldest_first_) { CVmUndoMeta *ret; /* remember the current free record */ ret = next_free_; /* * advance to the next record, wrapping if we reach the end * of the array (since we treat the array as circular) */ inc_rec_ptr(&next_free_); /* return the record */ return ret; } /* * If we have at least one old savepoint, discard the oldest * savepoint, which may free up some records, and try again; * otherwise, give up. */ if (savept_cnt_ > 1) { /* * we have at least one old savepoint we can discard - get * rid of it and try again */ drop_oldest_savept(vmg0_); } else { /* * we are down to our last savepoint (or none at all) - it's * no longer valid (since we can't keep it complete due to * the lack of available records), so delete it and return * failure */ drop_oldest_savept(vmg0_); return 0; } } } /* * Add a new record with a property ID key */ void CVmUndo::add_new_record_prop_key(VMG_ vm_obj_id_t obj, vm_prop_id_t key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.prop = key; /* set the value */ rec->oldval = *val; } } /* * Add a new record with an integer key */ void CVmUndo::add_new_record_int_key(VMG_ vm_obj_id_t obj, uint32_t key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.intval = key; /* set the value */ rec->oldval = *val; } } /* * Add a new record with a pointer key */ int CVmUndo::add_new_record_ptr_key(VMG_ vm_obj_id_t obj, void *key, const vm_val_t *val) { CVmUndoRecord *rec; /* allocate the new record */ rec = add_new_record(vmg_ obj); /* if we successfully allocated a record, set it up */ if (rec != 0) { /* set the object */ rec->obj = obj; /* set the key */ rec->id.ptrval = key; /* set the value */ rec->oldval = *val; } /* return an indication as to whether the record was created */ return (rec != 0); } /* * Add a new undo record */ CVmUndoRecord *CVmUndo::add_new_record(VMG_ vm_obj_id_t controlling_obj) { CVmUndoMeta *meta; /* if there is not an active savepoint, do not keep undo */ if (savept_cnt_ == 0) return 0; /* * if the object was created after the current savepoint was created, * there is no need to keep undo for it during this savepoint, since * rolling back to the savepoint will roll back to a point at which * the object didn't even exist */ if (!G_obj_table->is_obj_in_undo(controlling_obj)) return 0; /* allocate a new record */ meta = alloc_rec(vmg0_); /* return the undo record, if we succeeded */ return (meta == 0 ? 0 : &meta->rec); } /* * Apply undo to the most recent savepoint. */ void CVmUndo::undo_to_savept(VMG0_) { CVmUndoMeta *meta; /* if we don't have any savepoints, there's nothing to do */ if (savept_cnt_ == 0) return; /* * Starting with the most recently-added record, apply each record * in sequence until we reach the first savepoint in the undo list. */ meta = next_free_; for (;;) { /* * move to the previous record, wrapping to the end of the array * at the first record (since we treat the array as circular) */ if (meta == rec_arr_) meta = rec_arr_ + rec_arr_size_; --meta; /* * if we're at the first record in the current savepoint, we're * done */ if (meta == cur_first_) break; /* apply this undo record */ G_obj_table->apply_undo(vmg_ &meta->rec); } /* * Unwind the undo stack -- get the first record in the previous * savepoint from the link pointer. */ cur_first_ = cur_first_->link.prev_first; /* * there's nothing following the current savepoint any more -- the * savepoint we just applied is gone now */ if (cur_first_ != 0) cur_first_->link.next_first = 0; else oldest_first_ = 0; /* that's one less savepoint */ --savept_cnt_; /* make the previous savepoint current */ --cur_savept_; if (cur_savept_ == 0) cur_savept_ = VM_SAVEPT_MAX; /* the savepoint link we just removed is now the next free record */ next_free_ = meta; /* * notify objects that a new savepoint is in effect - the savepoint * isn't new in the sense of being newly created, but in the sense of * being a different savepoint than was previously in effect */ G_obj_table->notify_new_savept(); } /* * Garbage collection: mark referenced objects. This goes through all * of the undo records; for each undo record, we have the object that * owns the undo record mark as referenced any object to which the * record refers. */ void CVmUndo::gc_mark_refs(VMG0_) { CVmUndoMeta *cur; CVmUndoMeta *next_link; /* if we don't have any records, there's nothing to do */ if (oldest_first_ == 0) return; /* the first record is a linking record */ next_link = oldest_first_; /* start at the first record */ cur = oldest_first_; /* * Go through all undo records, from the oldest to the newest. Note * that we must keep track of each "link" record, because these are * not ordinary undo records and must be handled differently. */ for (;;) { /* check to see if this is a linking record or an ordinary record */ if (cur == next_link) { /* * this is a linking record - simply note the next linking * record and otherwise ignore this record */ next_link = cur->link.next_first; } else if (cur->rec.obj != VM_INVALID_OBJ) { /* * It's an ordinary undo record -- mark its references. * Note that the fact that an object has undo records does * not mean that the object is reachable -- this is * effectively a weak link to the object, because if it's * not reachable through other means, there's no need to * keep any undo information for it; so, we don't mark the * owner object itself as reachable at this point. */ G_obj_table->mark_obj_undo_rec(vmg_ cur->rec.obj, &cur->rec); } /* advance to the next record, wrapping at the end of the array */ ++cur; if (cur == rec_arr_ + rec_arr_size_) cur = rec_arr_; /* stop if we've reached the last record */ if (cur == next_free_) break; } } /* * Garbage collection: delete obsolete weak references. This goes * through all of the undo records; for each undo record, if the object * that owns the record is unreachable, we'll delete the undo record; * otherwise, we'll tell the object that owns the record to clean up the * record if it contains an unreachable weak reference. */ void CVmUndo::gc_remove_stale_weak_refs(VMG0_) { CVmUndoMeta *cur; CVmUndoMeta *next_link; /* if we don't have any records, there's nothing to do */ if (oldest_first_ == 0) return; /* the first record is a linking record */ next_link = oldest_first_; /* start at the first record */ cur = oldest_first_; /* * Go through all undo records, from the oldest to the newest. Note * that we must keep track of each "link" record, because these are * not ordinary undo records and must be handled differently. */ for (;;) { /* check to see if this is a linking record or an ordinary record */ if (cur == next_link) { /* * this is a linking record - simply note the next linking * record and otherwise ignore this record */ next_link = cur->link.next_first; } else if (cur->rec.obj != VM_INVALID_OBJ) { /* * It's an ordinary undo record -- delete its stale * references. If the record owner is itself about to be * deleted, don't bother with the object, but rather delete * the entire undo record -- undo records themselves only * weakly reference their owning objects. */ if (!G_obj_table->is_obj_deletable(cur->rec.obj)) { /* it's not ready for deletion - clean up its weak refs */ G_obj_table->remove_obj_stale_undo_weak_ref( vmg_ cur->rec.obj, &cur->rec); } else { /* * it's no longer reachable - delete the undo record by * setting the owning object to 'invalid' */ cur->rec.obj = VM_INVALID_OBJ; } } /* advance to the next record, wrapping at the end of the array */ ++cur; if (cur == rec_arr_ + rec_arr_size_) cur = rec_arr_; /* stop if we've reached the last record */ if (cur == next_free_) break; } } frobtads-1.2.3/tads3/vmtype.h0000644000175000001440000006453311773414263015242 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMTYPE.H,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtype.h - VM types Function Notes Modified 10/21/98 MJRoberts - Creation */ #ifndef VMTYPE_H #define VMTYPE_H #include #include #include "t3std.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" /* * Constant pool/code offset. This is an address of an object in the * pool. Pool offsets are 32-bit values. */ typedef uint32_t pool_ofs_t; /* * Savepoint ID's are stored in a single byte; since we store many * copies of savepoint ID's (since they need to be stored with each undo * list head), we want to save some space on this type. This limits us * to 256 simultaneous savepoints, but this should be more than we * actually want to keep around anyway, because of the amount of memory * it would consume to try to keep more than that around. */ typedef uchar vm_savept_t; const vm_savept_t VM_SAVEPT_MAX = 255; /* * Object ID type. VM_INVALID_OBJ is a distinguished value that serves * as an invalid object ID (a null pointer, effectively); no object can * ever have this ID. */ typedef uint32_t vm_obj_id_t; const vm_obj_id_t VM_INVALID_OBJ = 0; /* * Property ID. Property ID's are 16-bit values. VM_INVALID_PROP is a * distinguished value that serves as an invalid property ID, which can * be used to indicate the absence of a property value. */ typedef uint16_t vm_prop_id_t; const vm_prop_id_t VM_INVALID_PROP = 0; /* * Maximum recursion depth for recursive equality tests and hash * calculations. * * When we're comparing or hashing a tree of references by value, such as * when we're comparing two vectors or hashing a vector, we'll keep track * of the recursion depth of our tree traversal. If we reach this depth, * we'll throw an error on the assumption that the tree contains cycles and * thus cannot be hashed or compared by value. This depth is chosen to be * large enough that it's unlikely we'll exceed it with acyclical trees, * but small enough that we probably won't blow the C++ stack before we * reach this depth. */ const int VM_MAX_TREE_DEPTH_EQ = 256; /* * Datatypes */ enum vm_datatype_t { /* nil - doubles as a null pointer and a boolean false */ VM_NIL = 1, /* true - boolean true */ VM_TRUE, /* * Stack pointer (this is used to store a pointer to the enclosing * frame in a stack frame). This is a native machine pointer. */ VM_STACK, /* * Code pointer (this is used to store a pointer to the return * address in a stack frame, for example). This is a native machine * pointer. This differs from VM_CODEOFS in that this is a native * machine pointer. */ VM_CODEPTR, /* object reference */ VM_OBJ, /* property ID */ VM_PROP, /* 32-bit signed integer */ VM_INT, /* * string constant value - the value is an offset into the constant * pool of the string descriptor */ VM_SSTRING, /* * self-printing string value - the value is an offset into the * constant pool of the string descriptor */ VM_DSTRING, /* * list constant - the value is an offset into the constant pool of * the list descriptor */ VM_LIST, /* * byte-code constant offset - this is an offset into the byte-code * pool. This differs from VM_CODEPTR in that this is an offset in * the byte-code constant pool rather than a native machine pointer. * */ VM_CODEOFS, /* * function pointer - this is represented as an offset into the * byte-code pool. This differs from VM_CODEOFS in that the code * referenced by a VM_CODEOFS value is generally invoked directly * whenever the value is evaluated, whereas VM_FUNCPTR values are * used to convey function pointers, so the underlying code is not * executed implicitly on evaluation of such a value but must be * explicitly invoked. */ VM_FUNCPTR, /* * This is a special pseudo-type used to indicate that a value is * not present. This differs from nil, in that nil is a null * reference or false value, whereas this indicates that there's no * specified value at all. This is used, for example, to indicate * in an undo record that a property did not previously exist. */ VM_EMPTY, /* * This is a special pseudo-type used to indicate that evaluating an * expression requires executing system code. The value stored is a * pointer to a constant CVmNativeCodeDesc object, which describes a * native code method. */ VM_NATIVE_CODE, /* * Enumerated constant */ VM_ENUM, /* * Built-in function pointer. This is represented as an integer value * with the BIF table entry index (the function set ID) in the high 16 * bits, and the function index (the index of the function within its * function set) in the low 16 bits. */ VM_BIFPTR, /* * Special internal pseudo-type for execute-on-evaluation objects. * This is used when an anonymous object or dynamically created string * is stored in an object property *as a method*, meaning that the * object is executed upon evaluation rather than simply returned as an * object value. This is never stored in an image file, but it can be * stored in a saved game file. It's never used in a value that's * visible to user code; it's only for internal storage within * TadsObject property tables and the like. */ VM_OBJX, /* the equivalent of VM_OBJX for built-in function pointers */ VM_BIFPTRX, /* * First invalid type ID. Tools (such as compilers and debuggers) * can use this ID and any higher ID values to flag their own * internal types. */ VM_FIRST_INVALID_TYPE }; /* * Macro to create a private type constant for internal use in a tool. The * compiler uses this to create special non-VM values for its own use * during compilation. Types created with this macro must never appear in * an image file, and must never be exposed to user bytecode via any APIs. */ #define VM_MAKE_INTERNAL_TYPE(idx) \ ((vm_datatype_t)(((int)VM_FIRST_INVALID_TYPE) + (idx))) /* * Value container. Local variables, stack locations, and other value * holders use this structure to store a value and its type. */ struct vm_val_t { vm_datatype_t typ; union { /* stack/code pointer */ const void *ptr; /* object reference */ vm_obj_id_t obj; /* property ID */ vm_prop_id_t prop; /* 32-bit integer */ int32_t intval; /* enumerated constant */ uint32_t enumval; /* sstring/dstring/list constant pool offset/pcode pool offset */ pool_ofs_t ofs; /* native code descriptor */ const class CVmNativeCodeDesc *native_desc; /* pointer to built-in function */ struct { unsigned short set_idx; unsigned short func_idx; } bifptr; } val; /* set various types of values */ void set_empty() { typ = VM_EMPTY; } void set_nil() { typ = VM_NIL; } void set_true() { typ = VM_TRUE; } void set_stack(void *ptr) { typ = VM_STACK; val.ptr = ptr; } void set_codeptr(const void *ptr) { typ = VM_CODEPTR; val.ptr = ptr; } void set_obj(vm_obj_id_t obj) { typ = VM_OBJ; val.obj = obj; } void set_propid(vm_prop_id_t prop) { typ = VM_PROP; val.prop = prop; } void set_int(int32_t intval) { typ = VM_INT; val.intval = intval; } void set_enum(uint32_t enumval) { typ = VM_ENUM; val.enumval = enumval; } void set_sstring(pool_ofs_t ofs) { typ = VM_SSTRING; val.ofs = ofs; } void set_dstring(pool_ofs_t ofs) { typ = VM_DSTRING; val.ofs = ofs; } void set_list(pool_ofs_t ofs) { typ = VM_LIST; val.ofs = ofs; } void set_codeofs(pool_ofs_t ofs) { typ = VM_CODEOFS; val.ofs = ofs; } void set_fnptr(pool_ofs_t ofs) { typ = VM_FUNCPTR; val.ofs = ofs; } void set_bifptr(ushort set_idx, ushort func_idx) { typ = VM_BIFPTR; val.bifptr.set_idx = set_idx; val.bifptr.func_idx = func_idx; } void set_native(const class CVmNativeCodeDesc *desc) { typ = VM_NATIVE_CODE; val.native_desc = desc; } /* * Set nil, specifically for a value that might be interpreted as an * object ID. This sets the object ID field to invalid in addition to * setting the value type to nil, so that callers that just look at the * object ID will see an explicitly invalid object value rather than * something random. */ void set_nil_obj() { typ = VM_NIL; val.obj = VM_INVALID_OBJ; } /* * set an object or nil value: if the object ID is VM_INVALID_OBJ, * we'll set the type to nil */ void set_obj_or_nil(vm_obj_id_t obj) { /* set the object value initially */ typ = VM_OBJ; val.obj = obj; /* if the object is invalid, set the type to nil */ if (obj == VM_INVALID_OBJ) typ = VM_NIL; } /* get an object, or a null pointer if it's not an object */ vm_obj_id_t get_as_obj() const { return typ == VM_OBJ ? val.obj : 0; } /* is this an object of the given class? */ int is_instance_of(VMG_ vm_obj_id_t cls) const; /* set to an integer giving the datatype of the given value */ void set_datatype(VMG_ const vm_val_t *val); /* set to nil if 'val' is zero, true if 'val' is non-zero */ void set_logical(int v) { typ = (v != 0 ? VM_TRUE : VM_NIL); } /* determine if the value is logical (nil or true) */ int is_logical() const { return (typ == VM_NIL || typ == VM_TRUE); } /* * Get a logical as numeric TRUE or FALSE. This doesn't perform * any type checking; the caller must ensure that the value is * either true or nil, or this may return meaningless results. */ int get_logical() const { return (typ == VM_TRUE); } /* get as logical, checking type */ int get_logical_only() const { if (typ == VM_TRUE) return TRUE; else if (typ == VM_NIL) return FALSE; else { err_throw(VMERR_BAD_TYPE_BIF); AFTER_ERR_THROW(return FALSE;) } } /* * Get the underlying string constant value. If the value does not * have an underlying string constant (because it is of a type that * does not store a string value), this will return null. */ const char *get_as_string(VMG0_) const; /* * Cast the value to a string. This attempts to reinterpret the value * as a string. */ const char *cast_to_string(VMG_ vm_val_t *new_str) const; /* * Get the underlying list constant value. If the value does not * have an underlying list constant (because it is of a type that * does not store list data), this returns null. */ const char *get_as_list(VMG0_) const; /* * Is the value indexable as a list? This returns true for types that * have contiguous integer indexes from 1..number of elements. */ int is_listlike(VMG0_) const; /* * For a list-like object, get the number of elements. If this isn't a * list-like object, returns -1. */ int ll_length(VMG0_) const; /* * For a list-like object, get an indexed element. The index is * 1-based, per our usual conventions. */ void ll_index(VMG_ vm_val_t *ret, const vm_val_t *idx) const; /* index with a 1-based integer */ void ll_index(VMG_ vm_val_t *ret, int idx) const { vm_val_t vidx; vidx.set_int(idx); ll_index(vmg_ ret, &vidx); } /* get as a native code pointer value */ const uchar *get_as_codeptr(VMG0_) const; /* * Is this a function pointer of some kind? This returns true if it's * a plain function pointer, a built-in function pointer, or an object * with an invoker (which covers anonymous and dynamic functions). */ int is_func_ptr(VMG0_) const; /* * Convert a numeric value to an integer value. If the value isn't * numeric, throws an error. */ void num_to_logical() { /* check the type */ if (typ == VM_INT) { /* it's an integer - treat 0 as nil, all else as true */ typ = (val.intval == 0 ? VM_NIL : VM_TRUE); } else { /* it's not a number - throw an error */ err_throw(VMERR_NO_LOG_CONV); } } /* determine if the value is an integer */ int is_int() const { return (typ == VM_INT); } /* get the value as an integer, throwing an error if it's any other type */ int32_t get_as_int() const { /* check the type */ if (typ == VM_INT) { /* it's an integer already - return the value directly */ return val.intval; } else { /* not an integer - throw an error */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0;) } } /* * determine if the type is numeric - this returns true for integer and * BigNumber values, and could match future types that are numeric */ int is_numeric(VMG0_) const { return typ == VM_INT || nonint_is_numeric(vmg0_); } /* * Promote an integer to this numeric type. For example, if 'this' is * a BigNumber, this creates a BigNumber representation of val. This * converts the value in place, replacing '*val' with the promoted * value. */ void promote_int(VMG_ vm_val_t *val) const; /* * Convert a numeric value to an integer. This converts any type for * which is_numeric() returns true, but doesn't convert non-numeric * types. */ int32_t num_to_int(VMG0_) const { return typ == VM_INT ? val.intval : nonint_num_to_int(vmg0_); } /* * Convert a numeric value to a double. This converts any type for * which is_numeric() returns true, but doesn't convert non-numeric * types. */ double num_to_double(VMG0_) const { return typ == VM_INT ? (double)val.intval : nonint_num_to_double(vmg0_); } /* * Cast to an integer. This is more aggressive than num_to_int(), in * that we actively try to convert non-numeric types. */ int32_t cast_to_int(VMG0_) const { return typ == VM_INT ? val.intval : nonint_cast_to_int(vmg0_); } /* * Cast to a numeric type - integer or BigNumber. If the value is * already numeric, we'll return the value unchanged. Otherwise, if an * automatic conversion to a numeric type is possible, we'll return the * converted value. We'll return the "simplest" type that can * precisely represent the value, meaning the type that uses the least * memory and that's fastest for computations. */ void cast_to_num(VMG_ vm_val_t *val) const; /* * determine if the numeric value is zero; throws an error if the * value is not numeric */ int num_is_zero() const { /* check the type */ if (typ == VM_INT) { /* check the integer value to see if it's zero */ return (val.intval == 0); } else { /* it's not a number */ err_throw(VMERR_NUM_VAL_REQD); /* in case the compiler doesn't know we'll never get here */ AFTER_ERR_THROW(return 0;) } } /* * Determine if this value equals a given value. The nature of the * match depends on the type of this value: * * integers, property ID's, code offsets: the types and values must * match exactly. * * string and list constants: the other value must either be the same * type of constant, or an object that has an underlying value of the * same type; and the contents of the strings or lists must match. * * objects: the match depends on the type of the object. We invoke the * object's virtual equals() routine to make this determination. * * 'depth' has the same meaning as in calc_hash(). */ int equals(VMG_ const vm_val_t *val, int depth = 0) const; /* * Calculate a hash for the value. The meaning of the hash varies by * type, but is stable for a given value. 'depth' is a recursion depth * counter, with the same meaning as in CVmObject::calc_hash(). */ uint calc_hash(VMG_ int depth = 0) const; /* * Compare this value to the given value. Returns a value greater than * zero if this value is greater than 'val', a value less than zero if * this value is less than 'val', or 0 if the two values are equal. * Throws an error if the two values are not comparable. * * By far the most common type of comparison is between integers, so we * test in-line to see if we have two integer values, and if so, use a * fast in-line comparison. If we don't have two integers, we'll use * our full out-of-line test, which will look at other more interesting * type combinations. */ int compare_to(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return (val.intval > b->val.intval ? 1 : val.intval < b->val.intval ? -1 : 0); else return gen_compare_to(vmg_ b); } /* * relative value comparisons */ /* self > b */ int is_gt(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval > b->val.intval; else return gen_compare_to(vmg_ b) > 0; } /* self >= b */ int is_ge(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval >= b->val.intval; else return gen_compare_to(vmg_ b) >= 0; } /* self < b */ int is_lt(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval < b->val.intval; else return gen_compare_to(vmg_ b) < 0; } /* self <= b */ int is_le(VMG_ const vm_val_t *b) const { if (typ == VM_INT && b->typ == VM_INT) return val.intval <= b->val.intval; else return gen_compare_to(vmg_ b) <= 0; } private: /* out-of-line comparison, used when we don't have two integers */ int gen_compare_to(VMG_ const vm_val_t *val) const; /* * internal int conversion and casting - we separate these from * num_to_int and cast_to_int so that cast_to_int can be inlined for * the common case where the value is already an int */ int nonint_is_numeric(VMG0_) const; int32_t nonint_num_to_int(VMG0_) const; double nonint_num_to_double(VMG0_) const; int32_t nonint_cast_to_int(VMG0_) const; }; /* ------------------------------------------------------------------------ */ /* * Native code descriptor. This describes a native method call of an * intrinsic class object. */ class CVmNativeCodeDesc { public: /* create a descriptor with an exact number of arguments */ CVmNativeCodeDesc(int argc) { /* remember the parameters - there are no optional arguments */ min_argc_ = argc; opt_argc_ = 0; varargs_ = FALSE; } /* create a descriptor with optional arguments (but not varargs) */ CVmNativeCodeDesc(int min_argc, int opt_argc) { /* remember the parameters */ min_argc_ = min_argc; opt_argc_ = opt_argc; varargs_ = FALSE; } /* create a descriptor with optional arguments and/or varargs */ CVmNativeCodeDesc(int min_argc, int opt_argc, int varargs) { /* remember the parameters */ min_argc_ = min_argc; opt_argc_ = opt_argc; varargs_ = varargs; } /* check the given number of arguments for validity */ int args_ok(int argc) const { /* * the actual parameters must number at least the minimum, and * cannot exceed the maximum (i.e., the minimum plus the * optionals) unless we have varargs, in which case there is no * maximum */ return (argc >= min_argc_ && (varargs_ || argc <= min_argc_ + opt_argc_)); } /* minimum argument count */ int min_argc_; /* number of optional named arguments beyond the minimum */ int opt_argc_; /* * true -> varargs: any number of arguments greater than or equal to * min_argc_ are valid */ int varargs_; }; /* ------------------------------------------------------------------------ */ /* * String handling - these routines are provided as covers to allow for * easier adjustment for Unicode or other encodings. Don't include these * if we're compiling interface routines for the HTML TADS environment, * since HTML TADS has its own similar definitions for these, and we don't * need these for interface code. * * NOTE: The purpose of this macro is to indicate that we're being * #included by an HTML TADS source file - that is, the current compile run * is for an htmltads/xxx.cpp file. Do NOT #define this macro when * compiling tads3/xxx.cpp files. The tads3/xxx.cpp files have absolutely * no explicit dependencies on the htmltads subsystem and thus don't * #include the htmltads headers that would provide the conflicting * interface definitions for these functions. The conflict is in the other * direction: some of the htmltads source files explicitly depend on tads3 * headers, so they indirectly #include this header. So, this macro needs * to be defined only when compiling htmltads/xxx.cpp files. */ #ifdef T3_COMPILING_FOR_HTML #include "tadshtml.h" #else inline size_t get_strlen(const textchar_t *str) { return strlen(str); } inline void do_strcpy(textchar_t *dst, const textchar_t *src) { strcpy(dst, src); } #endif /* T3_COMPILING_FOR_HTML */ /* ------------------------------------------------------------------------ */ /* * Portable Binary Representations. When we store certain types of * information in memory, we store it in a format that is identical to * the format we use in portable binary files; using this format allows * us to read and write binary files as byte images, without any * interpretation, which greatly improves I/O performance in many cases. */ /* * Portable binary LENGTH indicator. This is used to store length * prefixes for strings, lists, and similar objects. We use a UINT2 * (16-bit unsigned integer) for this type of value. */ const size_t VMB_LEN = 2; inline void vmb_put_len(char *buf, size_t len) { oswp2(buf, len); } inline size_t vmb_get_len(const char *buf) { return osrp2(buf); } /* * Portable binary unsigned 2-byte integer */ const size_t VMB_UINT2 = 2; inline void vmb_put_uint2(char *buf, uint16_t i) { oswp2(buf, i); } inline uint16_t vmb_get_uint2(const char *buf) { return osrp2(buf); } /* * Portable binary unsigned 4-byte integer */ const size_t VMB_UINT4 = 4; inline void vmb_put_uint4(char *buf, uint32_t i) { oswp4(buf, i); } inline uint32_t vmb_get_uint4(const char *buf) { return osrp4(buf); } /* * Portable binary signed 4-byte integer */ const size_t VMB_INT4 = 4; inline void vmb_put_int4(char *buf, int32_t i) { oswp4s(buf, i); } inline int32_t vmb_get_int4(const char *buf) { return osrp4s(buf); } /* * Portable binary object ID. */ const size_t VMB_OBJECT_ID = 4; inline void vmb_put_objid(char *buf, vm_obj_id_t obj) { oswp4(buf, obj); } inline vm_obj_id_t vmb_get_objid(const char *buf) { return t3rp4u(buf); } /* * Portable binary property ID */ const size_t VMB_PROP_ID = 2; inline void vmb_put_propid(char *buf, vm_obj_id_t prop) { oswp2(buf, prop); } inline vm_prop_id_t vmb_get_propid(const char *buf) { return osrp2(buf); } /* * Portable data holder. This is used to store varying-type data items; * for example, this is used to store an element in a list, or the value * of a property in an object. This type of value stores a one-byte * prefix indicating the type of the value, and a four-byte area in * which the value is stored. The actual use of the four-byte value * area depends on the type. */ const size_t VMB_DATAHOLDER = 5; /* offset from a portable data holder pointer to the data value */ const size_t VMB_DH_DATAOFS = 1; /* store a portable dataholder from a vm_val_t */ void vmb_put_dh(char *buf, const vm_val_t *val); /* store a nil value in a portable dataholder */ inline void vmb_put_dh_nil(char *buf) { buf[0] = VM_NIL; } /* store an object value in a portable dataholder */ inline void vmb_put_dh_obj(char *buf, vm_obj_id_t obj) { buf[0] = VM_OBJ; vmb_put_objid(buf + 1, obj); } /* store a property value in a portable dataholder */ inline void vmb_put_dh_prop(char *buf, vm_prop_id_t prop) { buf[0] = VM_PROP; vmb_put_propid(buf + 1, prop); } /* get the value portion of a vm_val_t from a portable dataholder */ void vmb_get_dh_val(const char *buf, vm_val_t *val); /* get the type from a portable dataholder */ inline vm_datatype_t vmb_get_dh_type(const char *buf) { return (vm_datatype_t)buf[0]; } /* get a vm_val_t from a portable dataholder */ inline void vmb_get_dh(const char *buf, vm_val_t *val) { val->typ = vmb_get_dh_type(buf); vmb_get_dh_val(buf, val); } /* get an object value from a portable dataholder */ inline vm_obj_id_t vmb_get_dh_obj(const char *buf) { return (vm_obj_id_t)t3rp4u(buf+1); } /* get an integer value from a portable dataholder */ inline int32_t vmb_get_dh_int(const char *buf) { return (int32_t)osrp4s(buf+1); } /* get a property ID value from a portable dataholder */ inline vm_prop_id_t vmb_get_dh_prop(const char *buf) { return (vm_prop_id_t)osrp2(buf+1); } /* get a constant offset value from a portable dataholder */ inline pool_ofs_t vmb_get_dh_ofs(const char *buf) { return (pool_ofs_t)t3rp4u(buf+1); } #endif /* VMTYPE_H */ frobtads-1.2.3/tads3/vmobj.cpp0000644000175000001440000034355412145504453015364 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMOBJ.CPP,v 1.4 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmobj.cpp - VM object manager Function Notes Modified 10/28/98 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmstack.h" #include "vmundo.h" #include "vmrun.h" #include "vmfile.h" #include "vmmeta.h" #include "vmlst.h" #include "vmstr.h" #include "vmintcls.h" #include "vmpool.h" #include "vmfunc.h" #include "vmpredef.h" #include "vmhash.h" #include "vmtobj.h" #include "vmanonfn.h" #include "vmdynfunc.h" /* ------------------------------------------------------------------------ */ /* * Statistics gathering */ #ifdef VMOBJ_GC_STATS # define IF_GC_STATS(x) x struct { void gc_stats() { runs = 0; tot_freed = 0; cur_freed = 0; max_freed = 0; t = 0; } void begin_pass() { t0 = os_get_sys_clock_ms(); runs++; pass_start_bytes = cur_bytes; cur_freed = 0; } void end_pass() { t += os_get_sys_clock_ms() - t0; if (cur_freed > max_freed) max_freed = cur_freed; long garbage_bytes = pass_start_bytes - cur_bytes; if (garbage_bytes > max_garbage_bytes) max_garbage_bytes = garbage_bytes; } void count_free() { ++cur_freed; ++tot_freed; } void count_alloc_bytes(size_t siz) { cur_bytes += siz; if (cur_bytes > max_bytes) max_bytes = cur_bytes; } void count_realloc_bytes(size_t oldsiz, size_t newsiz) { count_alloc_bytes(newsiz); count_free_bytes(oldsiz); } void count_free_bytes(size_t siz) { cur_bytes -= siz; } void display() { printf("Garbage collection statistics:\n" " collection runs: %ld\n" " objects freed: %ld\n" " average freed per run: %ld\n" " max freed in one run: %ld\n" " peak heap bytes: %ld\n" " peak garbage bytes: %ld\n" " total gc time (ms): %ld\n" " average gc time (ms): %ld\n", runs, tot_freed, runs != 0 ? tot_freed/runs : 0, max_freed, max_bytes, max_garbage_bytes, t, runs != 0 ? t/runs : 0); } /* number of times the gc has run */ long runs; /* total number of objects collected */ long tot_freed; /* number of objects collected on this pass */ long cur_freed; /* maximum number collected on any one pass */ long max_freed; /* current total heap manager allocated bytes */ long cur_bytes; /* allocated bytes at start of last gc pass */ long pass_start_bytes; /* peak allocated bytes */ long max_bytes; /* peak garbage bytes */ long max_garbage_bytes; /* elapsed time in garbage collector */ long t; /* starting time in ticks of current run */ long t0; } gc_stats; #else /* VMOBJ_GC_STATS */ # define IF_GC_STATS(x) #endif /* VMOBJ_GC_STATS */ /* ------------------------------------------------------------------------ */ /* * Base fixed-size object entry implementation */ /* * Metaclass registration object for the root object class. Note that a * root object can never be instantiated; this entry is purely for the * use of the type system. */ static CVmMetaclassRoot metaclass_reg_obj; CVmMetaclass *CVmObject::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObject:: *CVmObject::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj) = { &CVmObject::getp_undef, &CVmObject::getp_of_kind, &CVmObject::getp_sclist, &CVmObject::getp_propdef, &CVmObject::getp_proptype, &CVmObject::getp_get_prop_list, &CVmObject::getp_get_prop_params, &CVmObject::getp_is_class, &CVmObject::getp_propinh, &CVmObject::getp_is_transient }; /* * Allocate space for an object from a page table, given the object ID. * The caller must allocate the object ID prior to new'ing the object; * operator new will store the memory for the new object in the object * slot the caller allocated. */ void *CVmObject::operator new(size_t siz, VMG_ vm_obj_id_t obj_id) { /* * The size must be the size of an object entry. This size never * changes, even for subclasses of the object type, since all * variable-size data must be stored in the variable-size portion of * the object. Here we are only concerned with allocating the * fixed-size object descriptor. */ assert(siz == sizeof(CVmObject)); /* return the memory contained in the object entry */ return G_obj_table->get_obj(obj_id); } /* * Determine if this object is an instance of the given object. By * default, we will simply check to see if the given object is the * IntrinsicClass instance that represents our metaclass or one of its * superclasses. */ int CVmObject::is_instance_of(VMG_ vm_obj_id_t obj) { vm_meta_entry_t *entry; /* * we can only be an instance of the object if the object is an * IntrinsicClass instance, since by default we have only intrinsic * classes among our superclasses */ if (!CVmObjClass::is_intcls_obj(vmg_ obj)) return FALSE; /* * look up my metaclass in the metaclass dependency table, and * determine if my dependency table entry's record of the * IntrinsicClass object for the metaclass matches the given object */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if we have an entry, ask our superclass object if it is an * instance of the given object; otherwise, we must not be an * instance */ if (entry != 0) { /* if this is our direct superclass, we're an instance */ if (entry->class_obj_ == obj) return TRUE; /* if there's no intrinsic class object, return false */ if (entry->class_obj_ == VM_INVALID_OBJ) return FALSE; /* ask the superclass if it inherits from the given object */ return vm_objp(vmg_ entry->class_obj_)->is_instance_of(vmg_ obj); } else { /* * no metaclass table entry - we can't really make any * determination, so indicate that we're not an instance */ return FALSE; } } /* * Get the nth superclass. By default, an object's superclass is * represented by the intrinsic class object for the metaclass. */ vm_obj_id_t CVmObject::get_superclass(VMG_ vm_obj_id_t /*self*/, int sc_idx) const { vm_meta_entry_t *entry; /* we only have one superclass */ if (sc_idx != 0) return VM_INVALID_OBJ; /* look up the metaclass entry */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* return the IntrinsicClass object that represents this metaclass */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* * Get a property */ int CVmObject::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property index to an index into our function table */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* if we find it, the source object is the 'Object' intrinsic class */ *source_obj = metaclass_reg_->get_class_obj(vmg0_); /* call the appropriate function */ return (this->*func_table_[func_idx])(vmg_ self, retval, argc, prop, source_obj); } /* * Inherit a property */ int CVmObject::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* * We're inheriting. This is never called from native code, as native * code does its inheriting directly through C++ calls to base class * native code; hence, we can only be called from a byte-code modifier * object. * * First, try looking for a native implementation. We can reach this * point if a byte-code object overrides an intrinsic method, then * inherits from the byte-code override. */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); if (func_idx != 0) { /* the source object is the 'Object' intrinsic class */ *source_obj = metaclass_reg_->get_class_obj(vmg0_); /* call the native implementation */ return (this->*func_table_[func_idx])(vmg_ self, retval, argc, prop, source_obj); } /* * We didn't find it among the intrinsic methods, so look at the * modifier objects. */ return find_modifier_prop(vmg_ prop, retval, self, orig_target_obj, defining_obj, source_obj, argc); } /* * Cast the object to a string. If we got here, it means that there's no * metaclass override, so use reflection services if available, or the * basic "object#xxx" template. */ const char *CVmObject::cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *new_str) const { /* set up a vm_val_t for 'self' */ vm_val_t val; val.set_obj(self); /* try calling self.objToString */ vm_prop_id_t objToString = G_predef->objToString; if (objToString != VM_INVALID_PROP) { /* call self.objToString */ G_interpreter->get_prop(vmg_ 0, &val, objToString, &val, 0, 0); /* if that yielded a string, return it */ const char *str = G_interpreter->get_r0()->get_as_string(vmg0_); if (str != 0) { /* set the return value */ *new_str = *G_interpreter->get_r0(); /* return the string buffer */ return str; } } /* try getting a symbolic name, otherwise use the object#xxx template */ return CVmObjString::reflect_to_str( vmg_ new_str, 0, 0, &val, "object#%ld", (long)self); } /* * Any object can be listlike, even if it doesn't natively implement * indexing, if it defines operator[] and 'length' as user-code methods. */ int CVmObject::is_listlike(VMG_ vm_obj_id_t self) { vm_val_t v; vm_obj_id_t o; int minargs, optargs, varargs; /* check for the required imports */ if (G_predef->operator_idx == VM_INVALID_PROP || G_predef->length_prop == VM_INVALID_PROP) return FALSE; /* check for operator [] */ if (!get_prop(vmg_ G_predef->operator_idx, &v, self, &o, 0)) return FALSE; /* check for a 'length' method taking no arguments */ if (!get_prop_interface(vmg_ self, G_predef->length_prop, minargs, optargs, varargs) || minargs > 0) return FALSE; /* it's list-like */ return TRUE; } /* * An object that doesn't have a native list-like interface can still * provide list-like operations by defining operator[] and 'length' in user * code. */ int CVmObject::ll_length(VMG_ vm_obj_id_t self) { /* make a recursive call to 'length' */ vm_val_t vself; vself.set_obj(self); vm_rcdesc rc("Object.ll_length"); G_interpreter->get_prop(vmg_ 0, &vself, G_predef->length_prop, &vself, 0, &rc); /* the return value in R0 is the length */ vm_val_t *r0 = G_interpreter->get_r0(); if (r0->typ == VM_INT) return r0->val.intval; else return -1; } /* * Index the object, with overloading if there's no native implementation. */ void CVmObject::index_val_ov(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { /* try the native operation first */ if (!index_val_q(vmg_ result, self, index_val)) { /* no native indexing - try the operator[] overload */ vm_val_t vself; vself.set_obj(self); G_stk->push(index_val); G_interpreter->op_overload(vmg_ 0, -1, &vself, G_predef->operator_idx, 1, VMERR_CANNOT_INDEX_TYPE); /* return the result from R0 */ *result = *G_interpreter->get_r0(); } } /* * Index the object, with overloading if there's no native implementation. */ void CVmObject::set_index_val_ov(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { /* try the native indexing first */ if (!set_index_val_q(vmg_ new_container, self, index_val, new_val)) { /* no native indexing - try the operator[]= overload */ vm_val_t vself; vself.set_obj(self); G_stk->push(new_val); G_stk->push(index_val); G_interpreter->op_overload(vmg_ 0, -1, &vself, G_predef->operator_setidx, 2, VMERR_CANNOT_INDEX_TYPE); /* return the new container result from R0 */ *new_container = *G_interpreter->get_r0(); } } /* * Get the next value from an iteration */ int CVmObject::iter_next(VMG_ vm_obj_id_t selfobj, vm_val_t *val) { /* check that the iterator properties are defined by the program */ if (G_iter_next_avail == VM_INVALID_PROP || G_iter_get_next == VM_INVALID_PROP) return FALSE; /* call isNextAvailable */ vm_rcdesc rc("for..in"); vm_val_t self; self.set_obj(selfobj); G_interpreter->get_prop(vmg_ 0, &self, G_iter_next_avail, &self, 0, &rc); /* if that returned false (nil or 0), return false */ vm_val_t *ret = G_interpreter->get_r0(); if (ret->typ == VM_NIL || (ret->typ == VM_INT && ret->val.intval == 0)) return FALSE; /* call getNext and return the result */ G_interpreter->get_prop(vmg_ 0, &self, G_iter_get_next, &self, 0, &rc); *val = *G_interpreter->get_r0(); return TRUE; } /* * Get a property that isn't defined in our property table */ int CVmObject::getp_undef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t prop, vm_obj_id_t *source_obj) { /* * We didn't find a native implementation of the method, but there's * still one more place to look: the "modifier" object for our class * tree. Modifier objects are byte-code objects that can provide * implementations of methods that add to intrinsic classes (modifiers * can't override intrinsic methods, but they can add new methods). * * Since we're looking for a property on an initial get-property call * (not an inheritance call), we don't yet have a defining object to * find and skip in the inheritance tree, so use VM_INVALID_OBJ as the * defining object. In addition, we're directly calling the method, so * the target object is the same as the 'self' object. */ return find_modifier_prop(vmg_ prop, retval, self, self, VM_INVALID_OBJ, source_obj, argc); } /* * Find a modifier property. */ int CVmObject::find_modifier_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { vm_meta_entry_t *entry; int found_def_obj; /* we haven't yet found the defining superclass */ found_def_obj = FALSE; /* get my metaclass from the dependency table */ entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if there's an associated intrinsic class object, check to see if * it provides a user modifier object for this intrinsic class */ while (entry != 0 && entry->class_obj_ != VM_INVALID_OBJ) { vm_obj_id_t mod_obj; /* ask the intrinsic class object for the user modifier object */ mod_obj = ((CVmObjClass *)vm_objp(vmg_ entry->class_obj_)) ->get_mod_obj(); /* * If we have a defining object, we must ignore objects in the * superclass tree until we find the defining object. Therefore, * scan up the superclass tree for mod_obj and see if we can find * the defining object; when we find it, we can start looking at * objects for real at the defining object's superclass. * * (Superclasses in modifier objects aren't real superclasses, * because modifier objects are classless. Instead, the superclass * list simply implements the 'modify' chain.) */ if (mod_obj != VM_INVALID_OBJ && defining_obj != VM_INVALID_OBJ && !found_def_obj) { /* * if the defining object isn't among the byte-code * superclasses of the modifier object, we must skip this * entire intrinsic class and move to the intrinsic superclass */ if (mod_obj == defining_obj || vm_objp(vmg_ mod_obj)->is_instance_of(vmg_ defining_obj)) { /* * the defining object is among my modifier family - this * means that this is the intrinsic superclass where we * found the modifier method */ found_def_obj = TRUE; } else { /* * The current defining object is not part of the modifier * chain for this intrinsic class, so we've already skipped * past this point in the intrinsic superclass tree on past * inheritances. Simply move to the next intrinsic class * and look at its modifier. */ goto next_intrinsic_sc; } } /* * If there's a modifier object, send the property request to it. * We are effectively delegating the method call to the modifier * object, so we must use the "inherited property" call, not the * plain get_prop() call: 'self' is the original self, but the * target object is the intrinsic class modifier object. */ if (mod_obj != VM_INVALID_OBJ && vm_objp(vmg_ mod_obj)->inh_prop( vmg_ prop, retval, self, mod_obj, defining_obj, source_obj, argc)) return TRUE; /* we didn't find it in this object, so look at its super-metaclass */ next_intrinsic_sc: if (entry->meta_->get_supermeta_reg() != 0) { /* get the super-metaclass ID */ entry = (G_meta_table ->get_entry_from_reg(entry->meta_ ->get_supermeta_reg() ->get_reg_idx())); /* * if we've already found the previous defining intrinsic * class, we can forget about the previous defining modifier * object now: since we're moving to a new intrinsic * superclass, we will have no superclass relation to the * previous defining object in the new modifier family, so we * can simply use the next definition of the property we find */ if (found_def_obj) defining_obj = VM_INVALID_OBJ; } else { /* no super-metaclass - give up */ break; } } /* we don't have a modifier object, so the property is undefined */ return FALSE; } /* * property evaluator - ofKind */ int CVmObject::getp_of_kind(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t sc; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the superclass, and make sure it's an object */ G_stk->pop(&sc); if (sc.typ != VM_OBJ) err_throw(VMERR_OBJ_VAL_REQD); /* check for an identity test */ if (sc.val.obj == self) { /* x.ofKind(x) == true */ retval->set_true(); } else { /* check to see if the object is a superclass of ours */ retval->set_logical(is_instance_of(vmg_ sc.val.obj)); } /* handled */ return TRUE; } /* * property evaluator - isClass */ int CVmObject::getp_is_class(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* indicate whether or not we're a class object */ retval->set_logical(is_class_object(vmg_ self)); /* handled */ return TRUE; } /* * property evaluator - isTransient */ int CVmObject::getp_is_transient(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* indicate whether or not we're transient */ retval->set_logical(G_obj_table->is_obj_transient(self)); /* handled */ return TRUE; } /* * property evaluator - getSuperclassList */ int CVmObject::getp_sclist(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { size_t sc_cnt; vm_obj_id_t lst_obj; CVmObjList *lstp; size_t i; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for GC protection */ G_interpreter->push_obj(vmg_ self); /* get the number of superclasses */ sc_cnt = get_superclass_count(vmg_ self); /* allocate a list for the results */ lst_obj = CVmObjList::create(vmg_ FALSE, sc_cnt); lstp = (CVmObjList *)vm_objp(vmg_ lst_obj); /* build the superclass list */ for (i = 0 ; i < sc_cnt ; ++i) { vm_val_t ele_val; /* get this superclass */ ele_val.set_obj(get_superclass(vmg_ self, i)); /* set the list element */ lstp->cons_set_element(i, &ele_val); } /* discard our GC protection */ G_stk->discard(); /* return the list */ retval->set_obj(lst_obj); /* handled */ return TRUE; } /* * property evaluator - propDefined */ int CVmObject::getp_propdef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, vm_prop_id_t, vm_obj_id_t *) { uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t val; int flags; int found; vm_prop_id_t prop; vm_obj_id_t source_obj; static CVmNativeCodeDesc desc(1, 1); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* if we have the flag argument, get it; otherwise, use the default */ if (argc >= 2) { /* get the flag value */ G_interpreter->pop_int(vmg_ &val); flags = (int)val.val.intval; } else { /* use the default flags */ flags = VMOBJ_PROPDEF_ANY; } /* presume we won't find a valid source object */ source_obj = VM_INVALID_OBJ; /* look up the property */ found = get_prop(vmg_ prop, &val, self, &source_obj, 0); /* * If we found a result, check to see if it's an intrinsic class * modifier object. If it is, replace it with its intrinsic class: * modifier objects are invisible through the reflection mechanism, and * appear to be the actual intrinsic classes they modify. */ if (found && CVmObjIntClsMod::is_intcls_mod_obj(vmg_ source_obj)) source_obj = find_intcls_for_mod(vmg_ self, source_obj); /* check the flags */ switch(flags) { case VMOBJ_PROPDEF_ANY: /* return true if the property is defined */ retval->set_logical(found); break; case VMOBJ_PROPDEF_DIRECTLY: /* return true if the property is defined directly */ retval->set_logical(found && source_obj == self); break; case VMOBJ_PROPDEF_INHERITS: /* return true if the property is inherited only */ retval->set_logical(found && source_obj != self); break; case VMOBJ_PROPDEF_GET_CLASS: /* return the defining class, or nil if it's not defined */ if (found) { /* * If we got a valid source object, return it. If we didn't * get a valid source object, but we found the property, * return 'self' as the result; this isn't exactly right, but * this should only be possible when the source object is an * intrinsic class for which no intrinsic class object is * defined, in which case the best we can do is provide 'self' * as the answer. */ retval->set_obj(source_obj != VM_INVALID_OBJ ? source_obj : self); } else { /* didn't find it - the return value is nil */ retval->set_nil(); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); break; } /* handled */ return TRUE; } /* * Find the intrinsic class which the given modifier object modifies. This * can only be used with a modifier that modifies my intrinsic class or one * of its intrinsic superclasses. */ vm_obj_id_t CVmObject::find_intcls_for_mod(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { /* get my metaclass from the dependency table */ vm_meta_entry_t *entry = (G_meta_table ->get_entry_from_reg(get_metaclass_reg()->get_reg_idx())); /* * if there's an intrinsic class object for the metaclass, ask it to do * the work */ if (entry != 0 && entry->class_obj_ != VM_INVALID_OBJ) return (((CVmObjClass *)vm_objp(vmg_ entry->class_obj_)) ->find_mod_src_obj(vmg_ entry->class_obj_, mod_obj)); /* there's no intrinsic metaclass, so we can't find what we need */ return VM_INVALID_OBJ; } /* * property evaluator - propInherited */ int CVmObject::getp_propinh(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc, vm_prop_id_t, vm_obj_id_t *) { uint argc = (in_argc != 0 ? *in_argc : 0); vm_val_t val; int flags; int found; vm_prop_id_t prop; vm_obj_id_t source_obj; vm_obj_id_t orig_target_obj; vm_obj_id_t defining_obj; static CVmNativeCodeDesc desc(3, 1); /* check arguments */ if (get_prop_check_argc(retval, in_argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* get the original target object */ G_interpreter->pop_obj(vmg_ &val); orig_target_obj = val.val.obj; /* get the defining object */ G_interpreter->pop_obj(vmg_ &val); defining_obj = val.val.obj; /* if we have the flag argument, get it; otherwise, use the default */ if (argc >= 4) { /* get the flag value */ G_interpreter->pop_int(vmg_ &val); flags = (int)val.val.intval; } else { /* use the default flags */ flags = VMOBJ_PROPDEF_ANY; } /* presume we won't find a valid source object */ source_obj = VM_INVALID_OBJ; /* look up the property */ found = inh_prop(vmg_ prop, &val, self, orig_target_obj, defining_obj, &source_obj, 0); /* check the flags */ switch(flags) { case VMOBJ_PROPDEF_ANY: /* return true if the property is defined */ retval->set_logical(found); break; case VMOBJ_PROPDEF_GET_CLASS: /* return the defining class, or nil if it's not defined */ if (found) { /* return the source object, or 'self' if we didn't find one */ retval->set_obj(source_obj != VM_INVALID_OBJ ? source_obj : self); } else { /* didn't find it - the return value is nil */ retval->set_nil(); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); break; } /* handled */ return TRUE; } /* * property evaluator - propType */ int CVmObject::getp_proptype(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t val; vm_prop_id_t prop; vm_obj_id_t source_obj; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* get the property value */ if (!get_prop(vmg_ prop, &val, self, &source_obj, 0)) { /* the property isn't defined on the object - the result is nil */ retval->set_nil(); } else { /* * Fix up OBJX (execute-on-eval object reference) and BIFPTRX * (execute-on-eval built-in function pointer) values to reflect * the underlying datatype. Treat an anonymous function or dynamic * function as a CODEOFS value; treat a string object as a DSTRING * value. */ if (val.typ == VM_OBJX) { if (CVmObjAnonFn::is_anonfn_obj(vmg_ val.val.obj) || CVmDynamicFunc::is_dynfunc_obj(vmg_ val.val.obj)) val.typ = VM_CODEOFS; else if (CVmObjString::is_string_obj(vmg_ val.val.obj)) val.typ = VM_DSTRING; else val.typ = VM_OBJ; } else if (val.typ == VM_BIFPTRX) val.typ = VM_BIFPTR; /* set the return value to the property's datatype value */ retval->set_datatype(vmg_ &val); } /* handled */ return TRUE; } /* * property evaluator - getPropList */ int CVmObject::getp_get_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* build my property list */ build_prop_list(vmg_ self, retval); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Build a list of properties directly defined by this instance */ void CVmObject::build_prop_list(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval) { /* * by default, object instances have no directly defined properties, * so create and return an empty list */ retval->set_obj(CVmObjList::create(vmg_ FALSE, (size_t)0)); } /* * property evaluator - getPropParams */ int CVmObject::getp_get_prop_params(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, vm_prop_id_t, vm_obj_id_t *) { vm_val_t val; vm_prop_id_t prop; int min_args, opt_args, varargs; static CVmNativeCodeDesc desc(1); CVmObjList *lst; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* pop the property address to test */ G_interpreter->pop_prop(vmg_ &val); prop = val.val.prop; /* push a self-reference while we're working */ G_stk->push()->set_obj(self); /* get the property information */ get_prop_interface(vmg_ self, prop, min_args, opt_args, varargs); /* * Allocate our return list. We need three elements: [minArgs, * optionalArgs, isVarargs]. */ retval->set_obj(CVmObjList::create(vmg_ FALSE, 3)); /* get the list object, properly cast */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); /* set the minimum argument count */ val.set_int(min_args); lst->cons_set_element(0, &val); /* set the optional argument count */ val.set_int(opt_args); lst->cons_set_element(1, &val); /* set the varargs flag */ val.set_logical(varargs); lst->cons_set_element(2, &val); /* discard our self-reference */ G_stk->discard(); /* handled */ return TRUE; } /* * Get the method interface to a given object property */ int CVmObject::get_prop_interface(VMG_ vm_obj_id_t self, vm_prop_id_t prop, int &min_args, int &opt_args, int &varargs) { vm_val_t val; vm_obj_id_t source_obj; /* presume we won't find an argument interface */ min_args = opt_args = 0; varargs = FALSE; /* get the property value */ if (!get_prop(vmg_ prop, &val, self, &source_obj, 0)) { /* we didn't find the property at all */ return FALSE; } else if (val.typ == VM_CODEOFS || val.typ == VM_OBJX || val.typ == VM_BIFPTRX) { /* * It's a direct pointer to static code, an object that we execute * on evaluation, or a built-in function pointer. Try getting a * pointer to the method header. CODEOFS and BIFPTRX values always * have method headers; OBJX values do if the underlying object is * invokable. If the object *isn't* invokable, it won't have a * method header; the only way this can happen is that we have a * String object as an OBJX property, which acts like a DSTRING * when evaluated. That has the default zero-argument interface, * so we can simply leave the return value with the defaults we've * already set up in this case. */ CVmFuncPtr func; if (func.set(vmg_ &val)) { /* it's an invokable - get the interface info from the header */ min_args = func.get_min_argc(); opt_args = func.get_opt_argc(); varargs = func.is_varargs(); } /* got it */ return TRUE; } else if (val.typ == VM_NATIVE_CODE) { /* get the arguments from the native code descriptor */ min_args = val.val.native_desc->min_argc_; opt_args = val.val.native_desc->opt_argc_; varargs = val.val.native_desc->varargs_; /* we found the property */ return TRUE; } else { /* * it's not a function, so there are no arguments, but we did find * the property */ return TRUE; } } /* * Call a static property */ int CVmObject::call_stat_prop(VMG_ vm_val_t *retval, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* not handled */ return FALSE; } /* * Get my image file version string */ const char *CVmObject::get_image_file_version(VMG0_) const { /* get the metaclass table entry */ vm_meta_entry_t *entry = G_meta_table->get_entry_from_reg( get_metaclass_reg()->get_reg_idx()); /* if we found it, get the image metaclass string from the entry */ if (entry != 0) { /* get the image metaclass name - "name/version" */ const char *n = entry->image_meta_name_; /* scan for the version slash */ n = strchr(n, '/'); /* if we found it, the version follows the slash */ if (n != 0) return n + 1; } /* we couldn't find the version data, so return an empty string */ return ""; } /* ------------------------------------------------------------------------ */ /* * CVmMetaclass implementation */ /* * Get a metaclass's super-metaclass. We'll look up our super-metaclass * in the metaclass registration table and return the IntrinsicClass * object we find referenced there. */ vm_obj_id_t CVmMetaclass::get_supermeta(VMG_ int idx) const { vm_meta_entry_t *entry; /* we only have one supermetaclass */ if (idx != 0) return VM_INVALID_OBJ; /* if I don't have a supermetaclass at all, return nil */ if (get_supermeta_reg() == 0) return VM_INVALID_OBJ; /* look up my supermetaclass entry */ entry = (G_meta_table->get_entry_from_reg( get_supermeta_reg()->get_reg_idx())); /* return the IntrinsicClass object that represents this metaclass */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* * Determine if I'm an instance of the given object. Most metaclasses * inherit directly from CVmObject, so we'll return true only if the * object is the CVmObject IntrinsicClass object */ int CVmMetaclass::is_meta_instance_of(VMG_ vm_obj_id_t obj) const { /* iterate over my supermetaclasses */ for (CVmMetaclass *sc = get_supermeta_reg() ; sc != 0 ; sc = sc->get_supermeta_reg()) { /* look up the metaclass entry for this supermetaclass */ vm_meta_entry_t *entry = (G_meta_table->get_entry_from_reg(sc->get_reg_idx())); /* * if the object matches the current superclass's IntrinsicClass * object, we're a subclass of that object; otherwise we're not */ if (entry != 0 && entry->class_obj_ == obj) return TRUE; } /* it's not one of my superclasses */ return FALSE; } /* * Get my intrinsic class object */ vm_obj_id_t CVmMetaclass::get_class_obj(VMG0_) const { /* get my metacalss entry */ vm_meta_entry_t *entry = G_meta_table->get_entry_from_reg(get_reg_idx()); /* if we found our entry, return the class from the entry */ return (entry != 0 ? entry->class_obj_ : VM_INVALID_OBJ); } /* ------------------------------------------------------------------------ */ /* * The nil object. This is a special pseudo-object we create for object * #0, to provide graceful error handling if byte code tries to dereference * nil in any way. */ class CVmObjNil: public CVmObject { public: virtual class CVmMetaclass *get_metaclass_reg() const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return 0;) } virtual void set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { err_throw(VMERR_NIL_DEREF); } virtual int get_prop(VMG_ vm_prop_id_t, vm_val_t *, vm_obj_id_t, vm_obj_id_t *, uint *) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int is_instance_of(VMG_ vm_obj_id_t) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int get_superclass_count(VMG_ vm_obj_id_t) const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return 0;) } virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t, int) const { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return VM_INVALID_OBJ;) } virtual void enum_props(VMG_ vm_obj_id_t, void (*cb)(VMG_ void *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *), void *) { err_throw(VMERR_NIL_DEREF); } virtual int get_prop_interface(VMG_ vm_obj_id_t, vm_prop_id_t, int &, int &, int &) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t orig_target_obj, vm_obj_id_t defining_obj, vm_obj_id_t *source_obj, uint *argc) { err_throw(VMERR_NIL_DEREF); AFTER_ERR_THROW(return FALSE;) } virtual void build_prop_list(VMG_ vm_obj_id_t, vm_val_t *) { err_throw(VMERR_NIL_DEREF); } virtual void notify_delete(VMG_ int) { } virtual void mark_refs(VMG_ uint) { } virtual void remove_stale_weak_refs(VMG0_) { } virtual void notify_new_savept() { } virtual void apply_undo(VMG_ struct CVmUndoRecord *) { } virtual void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } virtual void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } virtual void load_from_image(VMG_ vm_obj_id_t, const char *, size_t) { } virtual void save_to_file(VMG_ class CVmFile *) { } virtual void restore_from_file(VMG_ vm_obj_id_t, class CVmFile *, class CVmObjFixup *) { } }; /* ------------------------------------------------------------------------ */ /* * object table implementation */ /* * construction - create an empty table */ CVmObjTable::CVmObjTable() { pages_ = 0; page_slots_ = 0; pages_used_ = 0; image_ptr_head_ = 0; globals_ = 0; global_var_head_ = 0; post_load_init_table_ = 0; } /* * allocate object table */ void CVmObjTable::init(VMG0_) { /* allocate the initial set of page slots */ page_slots_ = 10; pages_ = (CVmObjPageEntry **)t3malloc(page_slots_ * sizeof(*pages_)); /* if that failed, throw an error */ if (pages_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* no pages are in use yet */ pages_used_ = 0; /* there are no free objects yet */ first_free_ = 0; /* there's nothing in the GC work queue yet */ gc_queue_head_ = VM_INVALID_OBJ; /* nothing in the finalizer work queue yet */ finalize_queue_head_ = VM_INVALID_OBJ; /* we haven't allocated anything yet */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* * Set the upper limit for new objects and allocated bytes between * garbage collection passes. * * In choosing these parameters, there's a trade-off. Running more * frequently reduces GC "stall time" of each pass (the time that the * system is unresponsive while scanning for unreachable objects), * because the total scanning on each pass will be smaller. More * frequent GC'ing also minimizes the working memory size by freeing * garbage objects sooner after they become unreachable. But running * more frequently reduces *overall* program speed, since we spend more * time in the collector in aggregate. We have to scan the reachable * objects on every pass, so the more frequently we run, the more * unproductive time we'll spend scanning the same reachable objects * over and over. * * TADS is specialized for highly interactive programs, so minimizing * stall time is the overriding priority. In practical testing, the * stall time is imperceptible up to at least 30,000 objects between * passes. (It stays below 5 ms per run on an average 2007 desktop. * The usual threshold of perceptibility is about 50-100 ms.) Overall * throughput seems to level out at about 20,000 objects between * passes. Beyond that, decreasing the frequency of gc runs yields * diminishing returns. The knee is the point where we're waiting long * enough between passes that unreachable objects dominate. Once * you're at that frequency, going longer between passes won't improve * speed much because you're typically just adding more garbage for the * next pass. The time you save by not doing a pass now gets canceled * out by making the next pass longer by giving it more garbage to * collect. This is in contrast to running too frequently, where * scanning reachable objects dominates: this time typically plateaus * the longer you go between passes, since the reachable set itself * tends to plateau in size over time, so while we're still in this * zone, the longer you go between passes the better.) * * Although 20,000 allocations seems to be the best frequency for * throughput for our test set, we currently set a somewhat more * conservative limit (i.e., collect garbage more frequently). This is * because the assumptions in our test set don't apply to all programs. * Some programs might allocate larger objects more frequently than our * tests, and in those cases we don't want the working set to grow too * quickly. More frequenty GC will help avoid this. * * In addition to the simple object allocation count, we also count * bytes allocated between passes, and collect garbage if we go above a * byte threshold independently of the object count. This is to ensure * that we collect garbage more frequently when very large objects are * being used, to keep the working set from growing too fast. Very * large working sets can hurt performance by reducing locality of * reference (for CPU caching) and even triggering OS-level disk * swapping. */ // max_allocs_between_gc_ = 17500; max_allocs_between_gc_ = 10000; max_bytes_between_gc_ = 6*1024*1024; /* enable the garbage collector */ gc_enabled_ = TRUE; /* there are no saved image data pointers yet */ image_ptr_head_ = 0; image_ptr_tail_ = 0; image_ptr_last_cnt_ = 0; /* no global pages yet */ globals_ = 0; /* no global variables yet */ global_var_head_ = 0; /* create the post_load_init() request table */ post_load_init_table_ = new CVmHashTable(128, new CVmHashFuncCS(), TRUE); /* allocate the first object page */ alloc_new_page(); /* * Initialize object #0 with the nil pseudo-object. This isn't an * actual object, but it provides graceful error handling if the byte * code attempts to invoke any methods on nil. */ new (vmg_ VM_INVALID_OBJ) CVmObjNil; CVmObjPageEntry *obj0 = get_entry(0); obj0->free_ = TRUE; obj0->in_root_set_ = TRUE; obj0->reachable_ = VMOBJ_REACHABLE; obj0->finalize_state_ = VMOBJ_UNFINALIZABLE; obj0->in_undo_ = FALSE; obj0->transient_ = FALSE; obj0->requested_post_load_init_ = FALSE; obj0->can_have_refs_ = FALSE; obj0->can_have_weak_refs_ = FALSE; } /* * delete object table */ CVmObjTable::~CVmObjTable() { /* show statistics if applicable */ IF_GC_STATS(gc_stats.display()); } /* * Clear the object table. This deletes all garbage-collected objects in * preparation for VM shutdown. */ void CVmObjTable::clear_obj_table(VMG0_) { /* delete all entries in the post-load initialization table */ post_load_init_table_->delete_all_entries(); /* go through the pages and delete the entries */ for (size_t i = 0 ; i < pages_used_ ; ++i) { /* delete all of the objects on the page */ int j; CVmObjPageEntry *entry; for (j = 0, entry = pages_[i] ; j < VM_OBJ_PAGE_CNT ; ++j, ++entry) { /* if this entry is still in use, delete it */ if (!entry->free_) entry->get_vm_obj()->notify_delete(vmg_ entry->in_root_set_); } } } /* * Delete the table. (We need to separate this out into a method so * that we can get access to the global variables.) */ void CVmObjTable::delete_obj_table(VMG0_) { /* delete each page we've allocated */ for (size_t i = 0 ; i < pages_used_ ; ++i) t3free(pages_[i]); /* free the master page table */ t3free(pages_); /* we no longer have any pages */ pages_ = 0; page_slots_ = 0; pages_used_ = 0; /* empty out the object lists */ first_free_ = VM_INVALID_OBJ; gc_queue_head_ = VM_INVALID_OBJ; finalize_queue_head_ = VM_INVALID_OBJ; /* clear the gc statistics */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* delete each object image data pointer page */ for (vm_image_ptr_page *ip_page = image_ptr_head_, *ip_next = 0 ; ip_page != 0 ; ip_page = ip_next) { /* remember the next page (before we delete this one) */ ip_next = ip_page->next_; /* delete this page */ t3free(ip_page); } /* there's nothing left in the image data list */ image_ptr_head_ = image_ptr_tail_ = 0; /* delete the linked list of globals */ if (globals_ != 0) { delete globals_; globals_ = 0; } /* * delete any left-over global variables (these should always be * deleted by subsystems before we get here, but this is the last * chance, so clean them up manually) */ while (global_var_head_ != 0) delete_global_var(global_var_head_); /* delete the post-load initialization table */ delete post_load_init_table_; post_load_init_table_ = 0; } /* * Enable or disable garbage collection */ int CVmObjTable::enable_gc(VMG_ int enable) { /* remember the old status for returning */ int old_enable = gc_enabled_; /* set the new status */ gc_enabled_ = enable; /* * if we're enabling GC after it was previously disabled, check to * see if we should perform a GC pass now (but don't count this as a * separate allocation) */ if (!old_enable && enable) alloc_check_gc(vmg_ FALSE); /* return the previous status */ return old_enable; } /* * allocate a new object ID */ vm_obj_id_t CVmObjTable::alloc_obj(VMG_ int in_root_set, int can_have_refs, int can_have_weak_refs) { /* count the allocation and maybe perform garbage collection */ alloc_check_gc(vmg_ TRUE); /* if the free list is empty, allocate a new page of object entries */ if (first_free_ == VM_INVALID_OBJ) alloc_new_page(); /* remember the first item in the free list - this is our result */ vm_obj_id_t ret = first_free_; /* get the object table entry for the ID */ CVmObjPageEntry *entry = get_entry(ret); /* unlink new entry from the free list */ first_free_ = entry->next_obj_; if (entry->next_obj_ != VM_INVALID_OBJ) get_entry(entry->next_obj_)->ptr_.prev_free_ = entry->ptr_.prev_free_; /* initialize the entry */ init_entry_for_alloc(ret, entry, in_root_set, can_have_refs, can_have_weak_refs); /* return the free object */ return ret; } /* * Run garbage collection before allocating an object */ void CVmObjTable::gc_before_alloc(VMG0_) { /* count it if in statistics mode */ IF_GC_STATS(gc_stats.begin_pass()); /* run a full garbage collection pass */ gc_pass_init(vmg0_); gc_pass_finish(vmg0_); /* count it if in statistics mode */ IF_GC_STATS(gc_stats.end_pass()); } /* * Allocate an object with a particular ID */ void CVmObjTable::alloc_obj_with_id(vm_obj_id_t id, int in_root_set, int can_have_refs, int can_have_weak_refs) { CVmObjPageEntry *entry; /* * if the page containing the given object ID hasn't been allocated, * allocate pages until it's available */ while (id >= pages_used_ * VM_OBJ_PAGE_CNT) alloc_new_page(); /* get the object table entry for the ID */ entry = get_entry(id); /* if the desired object ID is already taken, it's an error */ if (!entry->free_) err_throw(VMERR_OBJ_IN_USE); /* unlink the item - set the previous item's forward pointer... */ if (entry->ptr_.prev_free_ == VM_INVALID_OBJ) first_free_ = entry->next_obj_; else get_entry(entry->ptr_.prev_free_)->next_obj_ = entry->next_obj_; /* ...and the next items back pointer */ if (entry->next_obj_ != VM_INVALID_OBJ) get_entry(entry->next_obj_)->ptr_.prev_free_ = entry->ptr_.prev_free_; /* initialize the entry for allocation */ init_entry_for_alloc(id, entry, in_root_set, can_have_refs, can_have_weak_refs); } /* * Initialize an object table entry that we've just allocated */ void CVmObjTable::init_entry_for_alloc(vm_obj_id_t id, CVmObjPageEntry *entry, int in_root_set, int can_have_refs, int can_have_weak_refs) { /* mark the entry as in use */ entry->free_ = FALSE; /* no undo savepoint has been created since the object was created */ entry->in_undo_ = FALSE; /* mark the object as being in the root set if appropriate */ entry->in_root_set_ = in_root_set; /* presume it's an ordinary persistent object */ entry->transient_ = FALSE; /* presume it won't need post-load initialization */ entry->requested_post_load_init_ = FALSE; /* set the GC characteristics as requested */ entry->can_have_refs_ = can_have_refs; entry->can_have_weak_refs_ = can_have_weak_refs; /* * Mark the object as initially unreachable and unfinalizable. It's * not necessarily really unreachable at this point, but we mark it * as such because the garbage collector hasn't explicitly traced * the object to be reachable. The initial conditions for garbage * collection are that all objects not in the root set and not * finalizable are marked as unreachable; since we're not in a gc * pass right now (we can't be - memory cannot be allocated during a * gc pass), we know that we must establish initial gc conditions * for the next time we start a gc pass. */ entry->reachable_ = VMOBJ_UNREACHABLE; entry->finalize_state_ = VMOBJ_UNFINALIZABLE; /* add it to the GC work queue for the next GC pass */ if (in_root_set) add_to_gc_queue(id, entry, VMOBJ_REACHABLE); } #if 0 // moved to in-line in header, since it's called *a lot* /* * get the page entry for a given ID */ CVmObjPageEntry *CVmObjTable::get_entry(vm_obj_id_t id) const { size_t main_idx; size_t page_idx; /* get the index of the page in the main array */ main_idx = (size_t)(id >> VM_OBJ_PAGE_CNT_LOG2); /* get the index within the page */ page_idx = (size_t)(id & (VM_OBJ_PAGE_CNT - 1)); /* get the object */ return &pages_[main_idx][page_idx]; } #endif /* * Delete an object, given the object table entry. */ void CVmObjTable::delete_entry(VMG_ vm_obj_id_t id, CVmObjPageEntry *entry) { /* mark the object table entry as free */ entry->free_ = TRUE; /* it's not in the root set if it's free */ entry->in_root_set_ = FALSE; /* * notify the object that it's being deleted - this will let the * object release any additional resources (such as variable-size * heap space) that it's holding */ entry->get_vm_obj()->notify_delete(vmg_ FALSE); /* * remove any post-load initialization request for the object, if it * ever requested post-load initialization */ if (entry->requested_post_load_init_) remove_post_load_init(id); /* link this object into the head of the free list */ entry->next_obj_ = first_free_; /* link the previous head back to this object */ if (first_free_ != VM_INVALID_OBJ) get_entry(first_free_)->ptr_.prev_free_ = id; /* this object doesn't have a previous entry */ entry->ptr_.prev_free_ = VM_INVALID_OBJ; /* it's now the first entry in the list */ first_free_ = id; } /* * allocate a new page of objects */ void CVmObjTable::alloc_new_page() { size_t i; vm_obj_id_t id; CVmObjPageEntry *entry; /* first, make sure we have room in the master page list */ if (pages_used_ == page_slots_) { /* increase the number of page slots */ page_slots_ += 10; /* allocate space for the increased number of slots */ pages_ = (CVmObjPageEntry **)t3realloc( pages_, page_slots_ * sizeof(*pages_)); } /* allocate a new page */ pages_[pages_used_] = (CVmObjPageEntry *)t3malloc(VM_OBJ_PAGE_CNT * sizeof(*pages_[0])); /* if that failed, throw an error */ if (pages_[pages_used_] == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * initialize the new page to be entirely free - add each element of * the page to the free list */ entry = pages_[pages_used_]; i = 0; id = pages_used_ * VM_OBJ_PAGE_CNT; /* * if this is the start of the very first page, leave off the first * object, since its ID is invalid */ if (id == VM_INVALID_OBJ) { /* mark the object as free so we don't try to free it later */ entry->free_ = TRUE; /* ...and it's certainly not a collectable object */ entry->in_root_set_ = TRUE; /* skip it for the impending free list construction */ ++i; ++entry; ++id; } /* loop over each object in this page */ for ( ; i < VM_OBJ_PAGE_CNT ; ++i, ++entry, ++id) { /* set the forward pointer in this object */ entry->next_obj_ = first_free_; /* set the back pointer in the previous object in the list */ if (first_free_ != VM_INVALID_OBJ) get_entry(first_free_)->ptr_.prev_free_ = id; /* there's nothing before this entry yet */ entry->ptr_.prev_free_ = VM_INVALID_OBJ; /* this is now the head of the list */ first_free_ = id; /* mark it free */ entry->free_ = TRUE; /* presume it's not part of the root set */ entry->in_root_set_ = FALSE; /* * mark it initially unreachable and finalized, since it's not * even allocated yet */ entry->reachable_ = VMOBJ_UNREACHABLE; entry->finalize_state_ = VMOBJ_FINALIZED; } /* count the new page we've allocated */ ++pages_used_; } /* * Add an object to the list of machine globals. */ void CVmObjTable::add_to_globals(vm_obj_id_t obj) { CVmObjGlobPage *pg; /* * if this is a root set object, there is no need to mark it as * global, since it is inherently uncollectable as an image file * object to begin with */ if (get_entry(obj)->in_root_set_) return; /* if we have any global pages allocated, try adding to the head page */ if (globals_ != 0 && globals_->add_entry(obj)) { /* we successfully added it to the head page - we're done */ return; } /* * either the head page is full, or we haven't allocated any global * pages at all yet; in either case, allocate a new page and link it * at the head of our list */ pg = new CVmObjGlobPage(); pg->nxt_ = globals_; globals_ = pg; /* * add the object to the new page - it must fit, since the new page is * empty */ globals_->add_entry(obj); } /* * Collect all garbage. This does a complete garbage collection pass, * returning only after all unreachable objects have been collected. If * incremental garbage collection is not required, the caller can simply * invoke this routine to do the entire operation in a single call. */ void CVmObjTable::gc_full(VMG0_) { /* count it if in statistics mode */ IF_GC_STATS(gc_stats.begin_pass()); /* * run the initial pass to mark globally-reachable objects, then run * the garbage collector to completion */ gc_pass_init(vmg0_); gc_pass_finish(vmg0_); /* count it if in statistics mode */ IF_GC_STATS(gc_stats.end_pass()); } /* * Garbage collector - initialize. Add all globally-reachable objects * to the work queue. * * We assume that the following initial conditions hold: all objects * except root set objects are marked as unreferenced, and all root set * objects are marked as referenced; all root set objects are in the GC * work queue. So, we don't need to worry about finding root objects or * initializing the other objects at this point. */ void CVmObjTable::gc_pass_init(VMG0_) { /* * reset the allocations-since-gc counter, since this is now the * last gc pass, and we obviously haven't performed any allocations * since this gc pass yet */ allocs_since_gc_ = 0; bytes_since_gc_ = 0; /* trace objects reachable from the stack */ gc_trace_stack(vmg0_); /* trace objects reachable from imports */ gc_trace_imports(vmg0_); /* trace objects reachable from machine globals */ gc_trace_globals(vmg0_); /* * Process undo records - for each undo record, mark any referenced * objects as reachable. Undo records are part of the root set. */ G_undo->gc_mark_refs(vmg0_); } /* * Garbage collection - continue processing the work queue. This * processes a set of entries from the work queue. This routine can be * used for incremental garbage collection: after calling * gc_pass_init(), the caller can repeatedly invoke this routine until * it returns false. Since this routine will return after a short time * even if there's more work left to do, other operations (such as * processing user input) can be interleaved in a single thread with * garbage collection. * * The actual number of entries that we process is configurable at VM * compile-time via VM_GC_WORK_INCREMENT. The point of running the GC * incrementally is to allow GC work to be interleaved with long-running * user I/O operations (such as reading a line of text from the * keyboard) in the foreground thread, so the work increment should be * chosen so that each call to this routine completes quickly enough * that the user will perceive no delay. * * Returns true if more work remains to be done, false if not. The * caller should invoke this routine repeatedly until it returns false. */ int CVmObjTable::gc_pass_continue(VMG_ int trace_transient) { int cnt; /* * keep going until we exhaust the queue or run for our maximum * number of iterations */ for (cnt = VM_GC_WORK_INCREMENT ; cnt != 0 && gc_queue_head_ != VM_INVALID_OBJ ; --cnt) { vm_obj_id_t cur; CVmObjPageEntry *entry; /* get the next item from the work queue */ cur = gc_queue_head_; /* get this object's entry */ entry = get_entry(cur); /* remove this entry from the work queue */ gc_queue_head_ = entry->next_obj_; /* * Tell this object to mark its references. Mark the referenced * objects with the same state as this object, if they're not * already marked with a stronger state. * * If we're not tracing transients, do not trace this object if * it's transient. */ if (trace_transient || !entry->transient_) entry->get_vm_obj()->mark_refs(vmg_ entry->reachable_); } /* * return true if there's more work to do; there's more work to do * if we have any objects left in the gc work queue */ return gc_queue_head_ != VM_INVALID_OBJ; } /* * Finish garbage collection. We'll finish any work remaining in the * work queue, so this is safe to call at any time after gc_pass_init(), * after any number of calls (even zero) to gc_pass_continue(). */ void CVmObjTable::gc_pass_finish(VMG0_) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; /* * Make sure we're done processing the work queue -- keep calling * gc_pass_continue() until it indicates that it's finished. If * we're skipping finalizers, stop as soon as the state structure * indicates that we've started running finalizers. */ gc_trace_work_queue(vmg_ TRUE); /* * We've now marked everything that's reachable from the root set as * VMOBJ_REACHABLE. We can therefore determine the set of objects * that are newly 'finalizable' - an object becomes finalizable when * it was previously 'unfinalizable' and is not reachable, because we * can finalize an object any time after it first becomes unreachable. * So, scan all objects for eligibility for the 'finalizable' * transition, and make the transition in those objects. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* * if this entry is not free and is not in the root set, check * to see if its finalization status is changing */ if (!entry->free_ && !entry->in_root_set_) { /* * If the entry is not reachable, and was previously * unfinalizable, it is now finalizable. * * Note that an object must actually be reachable to avoid * become finalizable at this point. Not only do * unreachable objects become finalizable, but * 'f-reachable' objects do, too, since these are only * reachable from other finalizable objects. */ if (entry->reachable_ != VMOBJ_REACHABLE && entry->finalize_state_ == VMOBJ_UNFINALIZABLE) { /* * This object is newly finalizable. If it has no * finalizer, the object can go directly to the * 'finalized' state; otherwise, add it to the queue * of objects with pending finalizers. */ if (entry->get_vm_obj()->has_finalizer(vmg_ id)) { /* * This object is not reachable from the root set * and was previously unfinalizable. Make the * object finalizable. */ entry->finalize_state_ = VMOBJ_FINALIZABLE; } else { /* * the entry has no finalizer, so we can make this * object 'finalized' immediately */ entry->finalize_state_ = VMOBJ_FINALIZED; } /* count it as a collected object */ IF_GC_STATS(gc_stats.count_free()); } /* * If this object is finalizable, add it to the work queue * in state "f-reachable." We must mark everything this * object references, directly or indirectly, as * f-reachable, which we'll do with another pass through * the gc queue momentarily. */ if (entry->finalize_state_ == VMOBJ_FINALIZABLE) { /* * this object and all of the objects it references * are "f-reachable" */ add_to_gc_queue(id, entry, VMOBJ_F_REACHABLE); } } } } /* * During the scan above, we put all of the finalizable objects in the * work queue in reachability state 'f-reachable'. (Actually, * finalizable objects that were fully reachable were not put in the * work queue, because they are in a stronger reachability state that * we've already fully scanned.) Trace the work queue so that we mark * everything reachable indirectly from an f-reachable object as also * being at least f-reachable. */ gc_trace_work_queue(vmg_ TRUE); /* * We have now marked everything that's fully reachable as being in * state 'reachable', and everything that's reachable from a * finalizable object as being in state 'f-reachable'. Anything that * is still in state 'unreachable' is garbage and can be collected. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* if it's not already free, process it */ if (!entry->free_) { /* if the object is deletable, delete it */ if (entry->is_deletable()) { /* * This object is completely unreachable, and it has * already been finalized. This means there is no * possibility that the object could ever become * reachable again, hence we can discard the object. */ delete_entry(vmg_ id, entry); } else { /* * The object is reachable, so keep it around. Since * it's staying around, and since we know which * objects we're deleting and which are staying, we * can ask this object to remove all of its "stale * weak references" - that is, weak references to * objects that we're about to delete. Don't bother * notifying the object if it's incapable of keeping * weak references. */ if (entry->can_have_weak_refs_) entry->get_vm_obj()->remove_stale_weak_refs(vmg0_); /* * If this object is finalizable, put it in the * finalizer queue, so that we can run its finalizer * when we're done scanning the table. */ if (entry->finalize_state_ == VMOBJ_FINALIZABLE) add_to_finalize_queue(id, entry); /* * restore initial conditions for this object, so that * we're properly set up for the next GC pass */ gc_set_init_conditions(id, entry); } } } } /* * Go through the undo records and clear any stale weak references * contained in the undo list. */ G_undo->gc_remove_stale_weak_refs(vmg0_); /* * All of the finalizable objects are now in the finalizer queue. Run * through the finalizer queue and run each such object's finalizer. */ run_finalizers(vmg0_); } /* * Trace all objects reachable from the work queue. */ void CVmObjTable::gc_trace_work_queue(VMG_ int trace_transient) { /* * trace everything reachable directly from the work queue, until we * exhaust the queue */ while (gc_pass_continue(vmg_ trace_transient)) ; } /* * Garbage collection: trace objects reachable from the stack */ void CVmObjTable::gc_trace_stack(VMG0_) { size_t i; size_t depth; vm_val_t *val; /* * Process the stack. For each stack element that refers to an * object, mark the object as referenced and add it to the work * queue. * * Note that it makes no difference in what order we process the * stack elements; we go from depth down to 0 merely as a trivial * micro-optimization to avoid evaluating the stack depth on every * iteration of the loop. */ for (i = 0, depth = G_stk->get_depth() ; i < depth ; ++i) { /* * If this element refers to an object, and the object hasn't * already been marked as referenced, mark it as reachable and * add it to the work queue. * * Note that we only have to worry about objects here. We don't * have to worry about constant lists, even though they can * contain object references, because any object reference in a * constant list must be a root set object, and we've already * processed all root set objects. */ val = G_stk->get(i); if (val->typ == VM_OBJ && val->val.obj != VM_INVALID_OBJ) add_to_gc_queue(val->val.obj, VMOBJ_REACHABLE); } } /* * Trace objects reachable from imports */ void CVmObjTable::gc_trace_imports(VMG0_) { /* * generate the list of object imports; for each one, if we have a * valid object for the import, mark it as reachable */ #define VM_IMPORT_OBJ(sym, mem) \ if (G_predef->mem != VM_INVALID_OBJ) \ add_to_gc_queue(G_predef->mem, VMOBJ_REACHABLE); #define VM_NOIMPORT_OBJ(sym, mem) VM_IMPORT_OBJ(sym, mem) #include "vmimport.h" } /* * Garbage collection: trace objects reachable from the machine globals */ void CVmObjTable::gc_trace_globals(VMG0_) { CVmObjGlobPage *pg; vm_val_t *val; vm_globalvar_t *var; /* trace each page of globals */ for (pg = globals_ ; pg != 0 ; pg = pg->nxt_) { size_t i; vm_obj_id_t *objp; /* trace each item on this page */ for (objp = pg->objs_, i = pg->used_ ; i != 0 ; ++objp, --i) { /* trace this global */ add_to_gc_queue(*objp, VMOBJ_REACHABLE); } } /* the return value register (R0) is a machine global */ val = G_interpreter->get_r0(); if (val->typ == VM_OBJ && val->val.obj != VM_INVALID_OBJ) add_to_gc_queue(val->val.obj, VMOBJ_REACHABLE); /* trace the global variables defined by other subsystems */ for (var = global_var_head_ ; var != 0 ; var = var->nxt) { /* if this global variable contains an object, trace it */ if (var->val.typ == VM_OBJ && var->val.val.obj != VM_INVALID_OBJ) add_to_gc_queue(var->val.val.obj, VMOBJ_REACHABLE); } } #if 0 // moved inline, as it's small and is called a fair amount /* * Set the initial conditions for an object, in preparation for the next * GC pass. */ void CVmObjTable::gc_set_init_conditions(vm_obj_id_t id, CVmObjPageEntry *entry) { /* * Mark the object as unreachable -- at the start of each GC pass, * all non-root-set objects must be marked unreachable. */ entry->reachable_ = VMOBJ_UNREACHABLE; /* * If it's in the root set, add it to the GC work queue -- all * root-set objects must be in the work queue and marked as reachable * at the start of each GC pass. * * If the object is not in the root set, check to see if it's * finalizable. If so, add it to the finalizer queue, so that we * eventually run its finalizer. */ if (entry->in_root_set_) add_to_gc_queue(id, entry, VMOBJ_REACHABLE); } #endif /* * Run finalizers */ void CVmObjTable::run_finalizers(VMG0_) { /* keep going until we run out of work or reach our work limit */ while (finalize_queue_head_ != VM_INVALID_OBJ) { CVmObjPageEntry *entry; vm_obj_id_t id; /* get the next object from the queue */ id = finalize_queue_head_; /* get the entry */ entry = get_entry(id); /* remove the entry form the queue */ finalize_queue_head_ = entry->next_obj_; /* mark the object as finalized */ entry->finalize_state_ = VMOBJ_FINALIZED; /* * the entry is no longer in any queue, so we must mark it as * unreachable -- this ensures that the initial conditions are * correct for the next garbage collection pass, since all * objects not in the work queue must be marked as unreachable * (it doesn't matter whether the object is actually reachable; * the garbage collector will make that determination when it * next runs) */ entry->reachable_ = VMOBJ_UNREACHABLE; /* invoke its finalizer */ entry->get_vm_obj()->invoke_finalizer(vmg_ id); } } /* * Receive notification that we're creating a new undo savepoint */ void CVmObjTable::notify_new_savept() { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; /* go through each page of objects */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* go through each entry on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* if this entry is active, tell it about the new savepoint */ if (!entry->free_) { /* * the object existed at the start of this savepoint, so * it must keep undo information throughout the savepoint */ entry->in_undo_ = TRUE; /* notify the object of the new savepoint */ entry->get_vm_obj()->notify_new_savept(); } } } } /* * Call a callback for each object in the table */ void CVmObjTable::for_each(VMG_ void (*func)(VMG_ vm_obj_id_t, void *), void *ctx) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; /* go through each page in the object table */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* start at the start of the page */ size_t j = VM_OBJ_PAGE_CNT; CVmObjPageEntry *entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* if this entry is in use, invoke the callback */ if (!entry->free_) (*func)(vmg_ id, ctx); } } } /* * Apply undo */ void CVmObjTable::apply_undo(VMG_ CVmUndoRecord *rec) { /* tell the object to apply the undo */ if (rec->obj != VM_INVALID_OBJ) get_obj(rec->obj)->apply_undo(vmg_ rec); } /* * Scan all objects and add metaclass entries to the metaclass * dependency table for any metaclasses of which there are existing * instances. */ void CVmObjTable::add_metadeps_for_instances(VMG0_) { CVmObjPageEntry **pg; size_t i; vm_obj_id_t id; /* go through each page in the object table */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { size_t j; CVmObjPageEntry *entry; /* start at the start of the page, but skip object ID = 0 */ j = VM_OBJ_PAGE_CNT; entry = *pg; /* go through each entry on this page */ for ( ; j > 0 ; --j, ++entry, ++id) { /* if this entry is in use, add its metaclass if necessary */ if (!entry->free_) G_meta_table->add_entry_if_new( entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx(), 0, VM_INVALID_PROP, VM_INVALID_PROP); } } } /* ------------------------------------------------------------------------ */ /* * creation */ CVmObjFixup::CVmObjFixup(ulong entry_cnt) { uint i; /* remember the number of entries */ cnt_ = entry_cnt; /* no entries are used yet */ used_ = 0; /* if we have no entries, there's nothing to do */ if (cnt_ == 0) { arr_ = 0; pages_ = 0; return; } /* calculate the number of subarrays we need */ pages_ = (entry_cnt + VMOBJFIXUP_SUB_SIZE - 1) / VMOBJFIXUP_SUB_SIZE; /* allocate the necessary number of subarrays */ arr_ = (obj_fixup_entry **)t3malloc(pages_ * sizeof(arr_[0])); /* allocate the subarrays */ for (i = 0 ; i < pages_ ; ++i) { size_t cur_cnt; /* * allocate a full page, except for the last page, which might be * only partially used */ cur_cnt = VMOBJFIXUP_SUB_SIZE; if (i + 1 == pages_) cur_cnt = ((entry_cnt - 1) % VMOBJFIXUP_SUB_SIZE) + 1; /* allocate it */ arr_[i] = (obj_fixup_entry *)t3malloc(cur_cnt * sizeof(arr_[i][i])); } } /* * deletion */ CVmObjFixup::~CVmObjFixup() { uint i; /* if we never allocated an array, there's nothing to do */ if (arr_ == 0) return; /* delete each subarray */ for (i = 0 ; i < pages_ ; ++i) t3free(arr_[i]); /* delete the main array */ t3free(arr_); } /* * add a fixup */ void CVmObjFixup::add_fixup(vm_obj_id_t old_id, vm_obj_id_t new_id) { obj_fixup_entry *entry; /* allocate the next available entry */ entry = get_entry(used_++); /* store it */ entry->old_id = old_id; entry->new_id = new_id; } /* * translate an ID */ vm_obj_id_t CVmObjFixup::get_new_id(VMG_ vm_obj_id_t old_id) { /* * if it's a root-set object, don't bother even trying to translate * it, because root-set objects have stable ID's that never change on * saving or restoring */ if (G_obj_table->is_obj_id_valid(old_id) && G_obj_table->is_obj_in_root_set(old_id)) return old_id; /* find the entry by the object ID */ obj_fixup_entry *entry = find_entry(old_id); /* * if we found it, return the new ID; otherwise, return the old ID * unchanged, since the ID evidently doesn't require mapping */ return (entry != 0 ? entry->new_id : old_id); } /* * find an entry given the old object ID */ obj_fixup_entry *CVmObjFixup::find_entry(vm_obj_id_t old_id) { /* do a binary search for the entry */ ulong cur; ulong lo = 0; ulong hi = cnt_ - 1; while (lo <= hi) { /* split the difference */ cur = lo + (hi - lo)/2; obj_fixup_entry *entry = get_entry(cur); /* is it the one we're looking for? */ if (entry->old_id == old_id) { /* it's the one - return the entry */ return entry; } else if (old_id > entry->old_id) { /* we need to go higher */ lo = (cur == lo ? cur + 1 : cur); } else { /* we need to go lower */ hi = (cur == hi ? cur - 1 : cur); } } /* didn't find it */ return 0; } /* * Fix a DATAHOLDER value */ void CVmObjFixup::fix_dh(VMG_ char *dh) { /* if it's an object, translate the ID */ if (vmb_get_dh_type(dh) == VM_OBJ) { vm_obj_id_t id; /* get the object value */ id = vmb_get_dh_obj(dh); /* translate it */ id = get_new_id(vmg_ id); /* * if it's invalid, set the dataholder value to nil; otherwise, * set it to the new object ID */ if (id == VM_INVALID_OBJ) vmb_put_dh_nil(dh); else vmb_put_dh_obj(dh, id); } } /* * Fix up an array of DATAHOLDER values. */ void CVmObjFixup::fix_dh_array(VMG_ char *arr, size_t cnt) { /* scan the array of dataholders */ for ( ; cnt != 0 ; --cnt, arr += VMB_DATAHOLDER) fix_dh(vmg_ arr); } /* * Fix a portable VMB_OBJECT_ID field */ void CVmObjFixup::fix_vmb_obj(VMG_ char *p) { vm_obj_id_t id; /* get the old ID */ id = vmb_get_objid(p); /* fix it up */ id = get_new_id(vmg_ id); /* store it back */ vmb_put_objid(p, id); } /* * Fix an array of portable VMB_OBJECT_ID fields */ void CVmObjFixup::fix_vmb_obj_array(VMG_ char *p, size_t cnt) { /* scan the array */ for ( ; cnt != 0 ; --cnt, p += VMB_OBJECT_ID) fix_vmb_obj(vmg_ p); } /* ------------------------------------------------------------------------ */ /* * Save/restore */ /* * table-of-contents flags */ /* object is transient (so the object isn't saved to the state file) */ #define VMOBJ_TOC_TRANSIENT 0x0001 /* * Save state to a file */ void CVmObjTable::save(VMG_ CVmFile *fp) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; size_t toc_cnt; size_t save_cnt; long toc_cnt_pos; long end_pos; /* * Before we save, perform a full GC pass. This will ensure that we * have removed all objects that are referenced only weakly, and * cleaned up the weak references to them; this is important because * we don't trace weak references for the purposes of calculating the * set of objects that must be saved, and hence won't save any objects * that are only weakly referenced, which would leave dangling * references in the saved state if those weak references weren't * cleaned up before the objects containing them are saved. */ gc_full(vmg0_); /* * Make sure that all of the metaclasses that we are actually using * are in the metaclass dependency table. We store the table in the * file, because the table provides the mapping from file-local * metaclass ID's to actual metaclasses; we must make sure that the * table is complete (i.e., contains an entry for each metaclass of * which there is an instance) before storing the table. */ add_metadeps_for_instances(vmg0_); /* save the metaclass table */ G_meta_table->write_to_file(fp); /* * Figure out what objects we need to save. We only need to save * objects that are directly reachable from the root object set, from * the imports, or from the globals. * * We don't need to save objects that are only accessible from the * undo, because we don't save any undo information in the file. We * also don't need to save any objects that are reachable only from * the stack, since the stack is inherently transient. * * Note that we don't need to trace from transient objects, since we * won't be saving the transient objects and thus won't need to save * anything referenced only from transient objects. * * So, we merely trace objects reachable from the imports, globals, * and work queue. At any time between GC passes, the work queue * contains the complete list of root-set objects, hence we can simply * trace from the current work queue. */ gc_trace_imports(vmg0_); gc_trace_globals(vmg0_); gc_trace_work_queue(vmg_ FALSE); /* * Before we save the objects themselves, save a table of contents of * the dynamically-allocated objects to be saved. This table of * contents will allow us to fix up references to objects on reloading * the file with the new object numbers we assign them at that time. * First, write a placeholder for the table of contents entry count. * * Note that we must store the table of contents in ascending order of * object ID. This happens naturally, since we scan the table in * order of object ID. */ toc_cnt = 0; save_cnt = 0; toc_cnt_pos = fp->get_pos(); fp->write_uint4(0); /* now scan the object pages and save the table of contents */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; -- j, ++entry, ++id) { /* * If the entry is currently reachable, and it was dynamically * allocated (which means it's not in the root set), then add * it to the table of contents. Note that we won't * necessarily be saving the object, because the object could * be transient - but if so, then we still want the record of * the transient object, so we'll know on reloading that the * object is no longer available. */ if (!entry->free_ && entry->reachable_ == VMOBJ_REACHABLE && !entry->in_root_set_) { ulong flags; /* set up the flags */ flags = 0; if (entry->transient_) flags |= VMOBJ_TOC_TRANSIENT; /* write the object ID and flags */ fp->write_uint4(id); fp->write_uint4(flags); /* count it */ ++toc_cnt; } /* if it's saveable, count it among the saveable objects */ if (entry->is_saveable()) ++save_cnt; } } /* go back and fix up the size prefix for the table of contents */ end_pos = fp->get_pos(); fp->set_pos(toc_cnt_pos); fp->write_uint4(toc_cnt); fp->set_pos(end_pos); /* write the saveable object count, which we calculated above */ fp->write_uint4(save_cnt); /* scan all object pages, and save each reachable object */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; -- j, ++entry, ++id) { /* if this object is saveable, save it */ if (entry->is_saveable()) { uint idx; char buf[2]; /* write the object ID */ fp->write_uint4(id); /* store the root-set flag */ buf[0] = (entry->in_root_set_ != 0); /* store the dependency table index */ idx = entry->get_vm_obj()-> get_metaclass_reg()->get_reg_idx(); buf[1] = (char)G_meta_table->get_dependency_index(idx); /* write the data */ fp->write_bytes(buf, 2); /* save the metaclass-specific state */ entry->get_vm_obj()->save_to_file(vmg_ fp); } /* * restore this object to the appropriate conditions in * preparation for the next GC pass, so that we leave things * as we found them -- saving the VM's state thus has no * effect on the VM's state */ gc_set_init_conditions(id, entry); } } } /* * Restore state from a file */ int CVmObjTable::restore(VMG_ CVmFile *fp, CVmObjFixup **fixups) { int err; ulong cnt; /* presume we won't create a fixup table */ *fixups = 0; /* load the metaclass table */ if ((err = G_meta_table->read_from_file(fp)) != 0) return err; /* * Reset all objects to the initial image file load state. Note that * we wait until after we've read the metaclass table to reset the * objects, because any intrinsic class objects in the root set will * need to re-initialize their presence in the metaclass table, which * they can't do until after the metaclass table has itself been * reloaded. */ G_obj_table->reset_to_image(vmg0_); /* read the size of the table of contents */ cnt = fp->read_uint4(); /* allocate the fixup table */ *fixups = new CVmObjFixup(cnt); /* read the fixup table */ for ( ; cnt != 0 ; --cnt) { vm_obj_id_t old_id; vm_obj_id_t new_id; ulong flags; /* read the next entry */ old_id = (vm_obj_id_t)fp->read_uint4(); flags = fp->read_uint4(); /* * Allocate a new object ID for this entry. If the object was * transient, then it won't actually have been saved, so we must * fix up references to the object to nil. */ if (!(flags & VMOBJ_TOC_TRANSIENT)) new_id = vm_new_id(vmg_ FALSE); else new_id = VM_INVALID_OBJ; /* * Add the entry. Note that the table of contents is stored in * ascending order of old ID (i.e., the ID's in the saved state * file's numbering system); this is the same ordering required by * the fixup table, so we can simply read entries from the file * and add them directly to the fixup table. */ (*fixups)->add_fixup(old_id, new_id); } /* read the number of saved objects */ cnt = fp->read_uint4(); /* read the objects */ for ( ; cnt != 0 ; --cnt) { char buf[2]; vm_obj_id_t id; int in_root_set; uint meta_idx; CVmObject *obj; /* read the original object ID */ id = (vm_obj_id_t)fp->read_uint4(); /* read the root-set flag and dependency table index */ fp->read_bytes(buf, 2); in_root_set = buf[0]; meta_idx = (uchar)buf[1]; /* * if it's not in the root set, we need to create a new object; * otherwise, the object must already exist, since it came from * the object file */ if (in_root_set) { /* * make sure the object is valid - since it's supposedly in * the root set, it must already exist */ if (!is_obj_id_valid(id) || get_entry(id)->free_) return VMERR_SAVED_OBJ_ID_INVALID; } else { /* * the object was dynamically allocated, so it will have a new * object number - fix up the object ID to the new numbering * system */ id = (*fixups)->get_new_id(vmg_ id); /* create the object */ G_meta_table->create_for_restore(vmg_ meta_idx, id); } /* read the object's data */ obj = get_obj(id); obj->restore_from_file(vmg_ id, fp, *fixups); } /* success */ return 0; } /* * Save an image data pointer */ void CVmObjTable::save_image_pointer(vm_obj_id_t obj_id, const char *ptr, size_t siz) { vm_image_ptr *slot; /* allocate a new page if we're out of slots on the current page */ if (image_ptr_head_ == 0) { /* no pages yet - allocate the first page */ image_ptr_head_ = (vm_image_ptr_page *) t3malloc(sizeof(vm_image_ptr_page)); if (image_ptr_head_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* it's also the last page */ image_ptr_tail_ = image_ptr_head_; image_ptr_tail_->next_ = 0; /* no slots used on this page yet */ image_ptr_last_cnt_ = 0; } else if (image_ptr_last_cnt_ == VM_IMAGE_PTRS_PER_PAGE) { /* the last page is full - allocate another one */ image_ptr_tail_->next_ = (vm_image_ptr_page *) t3malloc(sizeof(vm_image_ptr_page)); if (image_ptr_tail_->next_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* it's the new last page */ image_ptr_tail_ = image_ptr_tail_->next_; image_ptr_tail_->next_ = 0; /* no slots used on this page yet */ image_ptr_last_cnt_ = 0; } /* get the next available slot */ slot = &image_ptr_tail_->ptrs_[image_ptr_last_cnt_]; /* save the data */ slot->obj_id_ = obj_id; slot->image_data_ptr_ = ptr; slot->image_data_len_ = siz; /* count the new record */ ++image_ptr_last_cnt_; } /* * Reset to initial image file state */ void CVmObjTable::reset_to_image(VMG0_) { CVmObjPageEntry **pg; CVmObjPageEntry *entry; size_t i; size_t j; vm_obj_id_t id; vm_image_ptr_page *ip_page; /* * Drop all undo information. Since we're resetting to the initial * state, the undo for our outgoing state will no longer be * relevant. */ G_undo->drop_undo(vmg0_); /* delete all of the globals */ if (globals_ != 0) { delete globals_; globals_ = 0; } /* * Go through the object table and reset each non-transient object in * the root set to its initial conditions. */ for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i) { /* scan all objects on this page */ for (j = VM_OBJ_PAGE_CNT, entry = *pg ; j > 0 ; --j, ++entry, ++id) { /* * if it's not free, and it's in the root set, and it's not * transient, reset it */ if (!entry->free_ && entry->in_root_set_ && !entry->transient_) { /* * This object is part of the root set, so it's part of * the state immediately after loading the image. Reset * the object to its load file conditions. */ entry->get_vm_obj()->reset_to_image(vmg_ id); } } } /* * Go through all of the objects for which we've explicitly saved the * original image file location, and ask them to reset using the image * data. */ for (ip_page = image_ptr_head_ ; ip_page != 0 ; ip_page = ip_page->next_) { size_t cnt; vm_image_ptr *slot; /* * get the count for this page - if this is the last page, it's * the last page counter; otherwise, it's a full page, since we * fill up each page before creating a new one */ if (ip_page->next_ == 0) cnt = image_ptr_last_cnt_; else cnt = VM_IMAGE_PTRS_PER_PAGE; /* go through the records on the page */ for (slot = ip_page->ptrs_ ; cnt != 0 ; --cnt, ++slot) { /* it this object is non-transient, reload it */ if (!get_entry(slot->obj_id_)->transient_) { /* reload it using the saved image data */ vm_objp(vmg_ slot->obj_id_) ->reload_from_image(vmg_ slot->obj_id_, slot->image_data_ptr_, slot->image_data_len_); } } } } /* * Create a global variable */ vm_globalvar_t *CVmObjTable::create_global_var() { vm_globalvar_t *var; /* create the new variable */ var = new vm_globalvar_t(); /* initialize the variable's value to nil */ var->val.set_nil(); /* link it into our list of globals */ var->nxt = global_var_head_; var->prv = 0; if (global_var_head_ != 0) global_var_head_->prv = var; global_var_head_ = var; /* return the variable */ return var; } /* * Delete a global variable */ void CVmObjTable::delete_global_var(vm_globalvar_t *var) { /* unlink it from the list of globals */ if (var->nxt != 0) var->nxt->prv = var->prv; if (var->prv != 0) var->prv->nxt = var->nxt; else global_var_head_ = var->nxt; /* delete the memory */ delete var; } /* ------------------------------------------------------------------------ */ /* * post-load initialization status */ enum pli_stat_t { PLI_UNINITED, /* not yet initialized */ PLI_IN_PROGRESS, /* initialization in progress */ PLI_INITED /* initialization completed */ }; /* * Post-load initialization hash table entry. We use the object ID, * treating the binary representation as a string of bytes, as the hash * key. */ class CVmHashEntryPLI: public CVmHashEntryCS { public: CVmHashEntryPLI(vm_obj_id_t id) : CVmHashEntryCS((char *)&id, sizeof(id), TRUE) { /* * remember our object ID for easy access (technically, it's stored * as our key value as well, so this is redundant; but it's * transformed into a block of bytes for the key, so it's easier to * keep a separate copy of the true type) */ id_ = id; /* initialize our status */ status = PLI_UNINITED; } /* our object ID */ vm_obj_id_t id_; /* status for current operation */ pli_stat_t status; }; /* * Request post-load initialization. */ void CVmObjTable::request_post_load_init(vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* check for an existing entry - if there's not one already, add one */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); if (entry == 0) { /* it's not there yet - add a new entry */ post_load_init_table_->add(new CVmHashEntryPLI(obj)); /* mark the object as having requested post-load initialization */ get_entry(obj)->requested_post_load_init_ = TRUE; } } /* * Explicitly invoke post-load initialization on a given object */ void CVmObjTable::ensure_post_load_init(VMG_ vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* find the entry */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); /* if we found it, invoke its initialization method */ if (entry != 0) call_post_load_init(vmg_ entry); } /* * Invoke post-load initialization on the given object, if appropriate */ void CVmObjTable::call_post_load_init(VMG_ CVmHashEntryPLI *entry) { /* check the status */ switch (entry->status) { case PLI_UNINITED: /* * It's not yet initialized, so we need to initialize it now. Mark * it as having its initialization in progress. */ entry->status = PLI_IN_PROGRESS; /* * push the entry on the stack to protect it from gc while we're * initializing it */ G_stk->push()->set_obj(entry->id_); /* invoke its initialization */ vm_objp(vmg_ entry->id_)->post_load_init(vmg_ entry->id_); /* mark the object as having completed initialization */ entry->status = PLI_INITED; /* discard our GC protection */ G_stk->discard(); /* done */ break; case PLI_IN_PROGRESS: /* * The object is in the process of being initialized. This must * mean that the object's initializer is calling us indirectly, * probably through another object's initializer that it invoked * explicitly as a dependency, which in turn means that we have a * circular dependency. This is illegal, so abort with an error. */ err_throw(VMERR_CIRCULAR_INIT); break; case PLI_INITED: /* it's already been initialized, so there's nothing to do */ break; } } /* * Remove a post-load initialization record */ void CVmObjTable::remove_post_load_init(vm_obj_id_t obj) { CVmHashEntryPLI *entry; /* find the entry */ entry = (CVmHashEntryPLI *) post_load_init_table_->find((char *)&obj, sizeof(obj)); /* if we found the entry, remove it from the table and delete it */ if (entry != 0) { /* remove it */ post_load_init_table_->remove(entry); /* delete it */ delete entry; /* mark the entry as no longer being registered for post-load init */ get_entry(obj)->requested_post_load_init_ = FALSE; } } /* * post-load initialization enumeration context */ struct pli_enum_ctx { vm_globals *globals; }; /* * Invoke post-load initialization on all objects that have requested it. * This must be called after initial program load, restarts, and restore * operations. */ void CVmObjTable::do_all_post_load_init(VMG0_) { pli_enum_ctx ctx; /* set up our context */ ctx.globals = VMGLOB_ADDR; /* first, mark all entries as having status 'uninitialized' */ post_load_init_table_->enum_entries(&pli_status_cb, &ctx); /* next, invoke the initializer method for each entry */ post_load_init_table_->enum_entries(&pli_invoke_cb, &ctx); } /* * post-load initialization enumeration callback: mark entries as having * status 'uninitialized' */ void CVmObjTable::pli_status_cb(void *ctx0, CVmHashEntry *entry0) { CVmHashEntryPLI *entry = (CVmHashEntryPLI *)entry0; /* mark the entry as having status 'uninitialized' */ entry->status = PLI_UNINITED; } /* * post-load initialization enumeration callback: mark entries as having * status 'uninitialized' */ void CVmObjTable::pli_invoke_cb(void *ctx0, CVmHashEntry *entry0) { CVmHashEntryPLI *entry = (CVmHashEntryPLI *)entry0; VMGLOB_PTR(((pli_enum_ctx *)ctx0)->globals); /* invoke post-load initialization on the object */ call_post_load_init(vmg_ entry); } /* ------------------------------------------------------------------------ */ /* * Memory manager implementation */ CVmMemory::CVmMemory(VMG_ CVmVarHeap *varheap) { /* remember our variable-size heap */ varheap_ = varheap; /* initialize the variable-size heap */ varheap_->init(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Hybrid cell/malloc memory manager - cell sub-block manager */ /* * Allocate an object. Since we always allocate blocks of a fixed size, * we can ignore the size parameter; the heap manager will only route us * requests that fit in our blocks. */ CVmVarHeapHybrid_hdr *CVmVarHeapHybrid_head::alloc(size_t) { /* if there isn't an entry, allocate another array */ if (first_free_ == 0) { CVmVarHeapHybrid_array *arr; size_t real_cell_size; char *p; size_t cnt; /* * Allocate another page. We need space for the array header * itself, plus space for all of the cells. We want page_count_ * cells, each of size cell_size_ plus the size of the per-cell * header, rounded to the worst-case alignment size for the * platform. */ real_cell_size = osrndsz(cell_size_ + osrndsz(sizeof(CVmVarHeapHybrid_hdr))); arr = (CVmVarHeapHybrid_array *) t3malloc(sizeof(CVmVarHeapHybrid_array) + (page_count_ * real_cell_size)); /* if that failed, throw an error */ if (arr == 0) err_throw(VMERR_OUT_OF_MEMORY); /* link the array into the master list */ arr->next_array = mem_mgr_->first_array_; mem_mgr_->first_array_ = arr; /* * Build the free list. Each cell goes into the free list; the * 'next' pointer is stored in the data area of the cell. */ for (p = arr->mem, cnt = page_count_ ; cnt > 0 ; p += real_cell_size, --cnt) { /* link this one into the free list */ *(void **)p = first_free_; first_free_ = (void *)p; } } /* remember the return value */ CVmVarHeapHybrid_hdr *ret = (CVmVarHeapHybrid_hdr *)first_free_; /* * when we initialized or last freed this entry, we stored a pointer * to the next item in the free list in the object's data - retrieve * this pointer now, and update our free list head to point to the * next item */ first_free_ = *(void **)first_free_; /* fill in the block's pointer to the allocating heap (i.e., this) */ ret->block = this; /* return the item */ return ret; } /* * Reallocate */ void *CVmVarHeapHybrid_head::realloc(CVmVarHeapHybrid_hdr *mem, size_t siz, CVmObject *obj) { /* * if the new block fits in our cell size, return the original * memory unchanged; note that we must adjust the pointer so that we * return the client-visible portion */ if (siz <= cell_size_) return (void *)(mem + 1); /* * The memory won't fit in our cell size, so not only can't we * re-use the existing cell, but we can't allocate the memory from * our own sub-block at all. Allocate an entirely new block from * the heap manager. */ void *new_mem = mem_mgr_->alloc_mem(siz, obj); /* * Copy the old cell's contents to the new memory. Note that the * user-visible portion of the old cell starts immediately after the * header; don't copy the old header, since it's not applicable to * the new object. Note also that we got a pointer directly to the * user-visible portion of the new object, so we don't need to make * any adjustments to the new pointer. */ memcpy(new_mem, (void *)(mem + 1), cell_size_); /* free the old memory */ free(mem); /* return the new memory */ return new_mem; } /* * Release memory */ void CVmVarHeapHybrid_head::free(CVmVarHeapHybrid_hdr *mem) { /* link the block into our free list */ *(void **)mem = first_free_; first_free_ = (void *)mem; } /* ------------------------------------------------------------------------ */ /* * Hybrid cell/malloc heap manager */ /* * construct */ CVmVarHeapHybrid::CVmVarHeapHybrid(CVmObjTable *objtab) { /* remember my object table */ objtab_ = objtab; /* set the cell heap count */ cell_heap_cnt_ = 5; /* allocate our cell heap pointer array */ cell_heaps_ = (CVmVarHeapHybrid_head **) t3malloc(cell_heap_cnt_ * sizeof(CVmVarHeapHybrid_head *)); /* if that failed, throw an error */ if (cell_heaps_ == 0) err_throw(VMERR_OUT_OF_MEMORY); /* * Allocate our cell heaps. Set up the heaps so that the pages run * about 32k each. */ cell_heaps_[0] = new CVmVarHeapHybrid_head(this, 32, 850); cell_heaps_[1] = new CVmVarHeapHybrid_head(this, 64, 400); cell_heaps_[2] = new CVmVarHeapHybrid_head(this, 128, 200); cell_heaps_[3] = new CVmVarHeapHybrid_head(this, 256, 100); cell_heaps_[4] = new CVmVarHeapHybrid_head(this, 512, 50); /* allocate our malloc heap manager */ malloc_heap_ = new CVmVarHeapHybrid_malloc(); /* we haven't allocated any cell array pages yet */ first_array_ = 0; } /* * delete */ CVmVarHeapHybrid::~CVmVarHeapHybrid() { size_t i; /* delete our cell heaps */ for (i = 0 ; i < cell_heap_cnt_ ; ++i) delete cell_heaps_[i]; /* delete the cell heap pointer array */ t3free(cell_heaps_); /* delete all of the arrays */ while (first_array_ != 0) { CVmVarHeapHybrid_array *nxt; /* remember the next one */ nxt = first_array_->next_array; /* delete this one */ t3free(first_array_); /* move on to the next one */ first_array_ = nxt; } /* delete the malloc-based subheap manager */ delete malloc_heap_; } /* * allocate memory */ void *CVmVarHeapHybrid::alloc_mem(size_t siz, CVmObject *) { CVmVarHeapHybrid_head **subheap; size_t i; /* count the gc statistics if desired */ IF_GC_STATS(gc_stats.count_alloc_bytes(siz)); /* count the allocation */ objtab_->count_alloc(siz); /* scan for a cell-based subheap that can handle the request */ for (i = 0, subheap = cell_heaps_ ; i < cell_heap_cnt_ ; ++i, ++subheap) { /* * If it will fit in this one's cell size, allocate it from this * subheap. Note that we must adjust the return pointer so that * it points to the caller-visible portion of the block returned * from the subheap, which immediately follows the internal * header. */ if (siz <= (*subheap)->get_cell_size()) { CVmVarHeapHybrid_hdr *hdr = (*subheap)->alloc(siz); IF_GC_STATS(hdr->siz = siz); return (void *)(hdr + 1); } } /* * We couldn't find a cell-based manager that can handle a block * this large. Allocate the block from the default malloc heap. * Note that the caller-visible block is the part that immediately * follows our internal header, so we must adjust the return pointer * accordingly. */ CVmVarHeapHybrid_hdr *hdr = malloc_heap_->alloc(siz); IF_GC_STATS(hdr->siz = siz); return (void *)(hdr + 1); } /* * reallocate memory */ void *CVmVarHeapHybrid::realloc_mem(size_t siz, void *mem, CVmObject *obj) { CVmVarHeapHybrid_hdr *hdr; /* * Count the allocation. This isn't really a new allocation of 'siz' * bytes, since we're only expanding an object that already has a * non-zero size. But in many cases this will simply allocate new * memory anyway, copying the old object into the new memory, so it * could actually count against our OS-level working set. That makes * it worthwhile to count it against the GC quota. */ objtab_->count_alloc(siz); /* * get the block header, which immediately precedes the * caller-visible block */ hdr = ((CVmVarHeapHybrid_hdr *)mem) - 1; /* count the gc statistics if desired */ IF_GC_STATS((gc_stats.count_realloc_bytes(hdr->siz, siz), hdr->siz = siz)); /* * read the header to get the block manager that originally * allocated the memory, and ask it to reallocate the memory */ return hdr->block->realloc(hdr, siz, obj); } /* * free memory */ void CVmVarHeapHybrid::free_mem(void *mem) { CVmVarHeapHybrid_hdr *hdr; /* * get the block header, which immediately precedes the * caller-visible block */ hdr = ((CVmVarHeapHybrid_hdr *)mem) - 1; /* count the gc statistics if desired */ IF_GC_STATS(gc_stats.count_free_bytes(hdr->siz)); /* * read the header to get the block manager that originally * allocated the memory, and ask it to free the memory */ hdr->block->free(hdr); } frobtads-1.2.3/tads3/vmsave.h0000644000175000001440000000360711714021560015177 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsave.h - save/restore VM state Function Saves/restores VM state to/from a file. Notes Modified 08/02/99 MJRoberts - Creation */ #ifndef VMSAVE_H #define VMSAVE_H #include "vmglob.h" /* * Save/restore API. (This class is all static methods, so it's never * instatiated; it's really just a namespace.) */ class CVmSaveFile { public: /* * Save state to a file. Writes the state information to the given * open file stream. * * 'metadata' is an optional LookupTable object containing * string->string associations. We'll write each key/value pair to the * save file in the metadata header section. This section allows the * interpreter and external tools to display user-readable information * on the saved game. For example, you could store things like the * current location, chapter number, score, etc - things that would * help the user identify the context of the saved game when looking * for the one he/she wishes to restore. */ static void save(VMG_ class CVmFile *fp, class CVmObjLookupTable *metadata); /* * given a saved state file, read the name of the image file that * created it */ static int restore_get_image(osfildef *fp, char *fname_buf, size_t fname_buf_len); /* * Restore state from a file. Returns zero on success, or a * VMERR_xxx code indicating the problem on failure. */ static int restore(VMG_ class CVmFile *fp); /* reset the VM to the initial image file state */ static void reset(VMG0_); protected: }; #endif /* VMSAVE_H */ frobtads-1.2.3/tads3/utf8.cpp0000644000175000001440000001100111732613013015102 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/utf8.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name utf8.cpp - UTF-8 implementation Function Notes Modified 10/17/98 MJRoberts - Creation */ #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * encode a string of wide characters into the buffer */ size_t utf8_ptr::setwchars(const wchar_t *src, size_t src_count, size_t bufsiz) { /* loop through the source and store the characters */ size_t outbytes; for (outbytes = 0 ; src_count > 0 ; --src_count, ++src) { /* figure out how many bytes we need for this character */ size_t curbytes = s_wchar_size(*src); /* add it to the total output size */ outbytes += curbytes; /* if we have room, add it to the buffer */ if (bufsiz >= curbytes) { /* store it */ setch(*src); /* deduct this space from the remaining buffer size */ bufsiz -= curbytes; } else { /* * there's no room for this - make sure we don't store * anything more (since we might have room for a shorter * character later, but that would put a gap in the output * string - better to just truncate here) */ bufsiz = 0; } } /* return the total output size used (or needed) */ return outbytes; } /* ------------------------------------------------------------------------ */ /* * encode a null-terminated string of wide characters into the buffer */ size_t utf8_ptr::setwcharsz(const wchar_t *src, size_t bufsiz) { /* loop through the source and store the characters */ size_t outbytes; for (outbytes = 0 ; *src != 0 ; ++src) { /* figure out how many bytes we need for this character */ size_t curbytes = s_wchar_size(*src); /* add it to the total output size */ outbytes += curbytes; /* if we have room, add it to the buffer */ if (bufsiz >= curbytes) { /* store it */ setch(*src); /* deduct this space from the remaining buffer size */ bufsiz -= outbytes; } else { /* * there's no room for this - make sure we don't store * anything more (since we might have room for a shorter * character later, but that would put a gap in the output * string - better to just truncate here) */ bufsiz = 0; } } /* return the total output size used (or needed) */ return outbytes; } /* ------------------------------------------------------------------------ */ /* * Compare this string to the given string */ int utf8_ptr::s_compare_to(const char *p1, size_t bytelen1, const char *p2, size_t bytelen2) { /* keep going until one or the other string runs out of bytes */ while (bytelen1 != 0 && bytelen2 != 0) { wchar_t c1, c2; size_t siz1, siz2; /* get the current character from each string */ c1 = s_getch(p1); c2 = s_getch(p2); /* compare them */ if (c1 > c2) return 1; else if (c1 < c2) return -1; /* get the size of each character */ siz1 = s_charsize(*p1); siz2 = s_charsize(*p2); /* decrement each counter by the byte size of this character */ bytelen1 -= siz1; bytelen2 -= siz2; /* advance to the next character in each string */ p1 += siz1; p2 += siz2; } /* * we didn't find any character differences, but one string is * longer than the other -- if they ran out at the same time, * they're identical; otherwise, the one that ran out first is the * lesser one */ if (bytelen2 != 0) { /* the first one ran out first, so the first one sorts earlier */ return -1; } else if (bytelen1 != 0) { /* the second one ran out first, so the first one sort later */ return 1; } else { /* they both ran out at the same time */ return 0; } } frobtads-1.2.3/tads3/vmstrcmp.cpp0000644000175000001440000010531411756714647016127 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrcmp.cpp - T3 String Comparator intrinsic class Function Notes Modified 09/05/02 MJRoberts - Creation */ #include #include #include "utf8.h" #include "vmuni.h" #include "vmtype.h" #include "vmobj.h" #include "vmmeta.h" #include "vmglob.h" #include "vmstrcmp.h" #include "vmstack.h" #include "vmbif.h" #include "vmfile.h" #include "vmlst.h" #include "vmstr.h" #include "vmrun.h" /* ------------------------------------------------------------------------ */ /* * Statics */ /* metaclass registration */ static CVmMetaclassStrComp metaclass_reg_obj; CVmMetaclass *CVmObjStrComp::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjStrComp:: *CVmObjStrComp::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjStrComp::getp_undef, &CVmObjStrComp::getp_calc_hash, &CVmObjStrComp::getp_match_values }; /* ------------------------------------------------------------------------ */ /* * notify of deletion */ void CVmObjStrComp::notify_delete(VMG_ int in_root_set) { /* delete my extension data */ delete_ext(vmg0_); } /* * Delete my extension data */ void CVmObjStrComp::delete_ext(VMG0_) { vmobj_strcmp_ext *ext = get_ext(); /* if I have an extension, delete it */ if (ext != 0) { size_t i; /* delete each first-level mapping table */ for (i = 0 ; i < countof(ext->equiv) ; ++i) { /* if this table is present, delete it */ if (ext->equiv[i] != 0) t3free(ext->equiv[i]); } /* delete and forget our extension */ G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } } /* ------------------------------------------------------------------------ */ /* * set a property */ void CVmObjStrComp::set_prop(VMG_ class CVmUndo *, vm_obj_id_t, vm_prop_id_t, const vm_val_t *) { /* we have no properties to set */ err_throw(VMERR_INVALID_SETPROP); } /* ------------------------------------------------------------------------ */ /* * Get a property */ int CVmObjStrComp::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the function, if we found it */ if ((this->*func_table_[func_idx])(vmg_ self, val, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* not found - inherit default handling */ return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Abstract equivalence mapping reader. This can be implemented on * different underlying data sources. */ class CVmObjStrCompMapReader { public: /* * Read the next equivalence mapping. We fill in *ref_ch with the * reference character, *uc_result_flags with the upper-case result * flags, and *lc_result_flags with the lower-case result flags. On * input, *value_ch_cnt is the maximum number of value characters we * can store in the buffer at val_buf; on return, this is the number * of characters in the mapping, which might be higher than the number * originally in the buffer. In any case, we will write no more than * the allowed buffer size as given by *value_ch_cnt on input (so we * might indicate a higher number than we actually wrote: we always * return the actual value mapping size, even if we couldn't store the * whole thing because of a lack of buffer space). * * This routine should retrieve one mapping each time it's called, and * then move on to the next mapping. The caller is responsible for * making sure that this routine is called the correct number of * times, so we don't have to worry about running out of mappings. */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) = 0; }; /* * Stream-based mapping reader. This reads mappings from a stream that * uses our serialization format for image and saved-state files. */ class CVmObjStrCompMapReaderStream: public CVmObjStrCompMapReader { public: CVmObjStrCompMapReaderStream(CVmStream *str) { str_ = str; } /* read a mapping */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) { size_t copy_limit; size_t copy_size; size_t i; /* limit our value character copying to the actual buffer size */ copy_limit = *value_ch_cnt; /* read the header values */ *ref_ch = (wchar_t)str_->read_uint2(); copy_size = *value_ch_cnt = str_->read_byte(); *uc_result_flags = str_->read_uint4(); *lc_result_flags = str_->read_uint4(); /* limit copying to the actual buffer size */ if (copy_size > copy_limit) copy_size = copy_limit; /* read the values */ for (i = 0 ; i < copy_size ; ++i) *val_buf++ = (wchar_t)str_->read_uint2(); /* skip any values from the input that we weren't able to store */ for ( ; i < *value_ch_cnt ; ++i) str_->read_uint2(); } protected: /* my stream */ CVmStream *str_; }; /* * Constructor-list mapping reader. This reads mappings from list data in * the format that we use in our constructor. */ class CVmObjStrCompMapReaderList: public CVmObjStrCompMapReader { public: CVmObjStrCompMapReaderList(const vm_val_t *lst) { /* remember the list */ lst_ = lst; /* start at the first element */ idx_ = 1; } /* read a mapping */ virtual void read_mapping(VMG_ wchar_t *ref_ch, unsigned long *uc_result_flags, unsigned long *lc_result_flags, wchar_t *val_buf, size_t *value_ch_cnt) { vm_val_t val; vm_val_t sublst; const char *refstr; const char *valstr; size_t copy_rem; size_t rem; utf8_ptr p; /* note the size limit of the caller's value string buffer */ copy_rem = *value_ch_cnt; /* retrieve the next element of the list */ lst_->ll_index(vmg_ &sublst, idx_); /* get the value as a sublist */ if (!sublst.is_listlike(vmg0_) || sublst.ll_length(vmg0_) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* retrieve the reference character string (sublst's 1st element) */ sublst.ll_index(vmg_ &val, 1); refstr = val.get_as_string(vmg0_); /* retrieve the value string (sublst's 2nd element) */ sublst.ll_index(vmg_ &val, 2); valstr = val.get_as_string(vmg0_); /* make sure the reference and value strings are indeed strings */ if (refstr == 0 || valstr == 0) err_throw(VMERR_BAD_TYPE_BIF); /* we need at least one character in each string */ if (vmb_get_len(refstr) == 0 || vmb_get_len(valstr) == 0) err_throw(VMERR_BAD_VAL_BIF); /* fill in the caller's reference character from the ref string */ *ref_ch = utf8_ptr::s_getch(refstr + VMB_LEN); /* store the characters of the value string, up to the buffer limit */ p.set((char *)valstr + VMB_LEN); rem = vmb_get_len(valstr); for (*value_ch_cnt = 0 ; rem != 0 ; p.inc(&rem)) { /* if we have room, copy this character */ if (copy_rem != 0) { /* copy the character */ *val_buf++ = p.getch(); /* count it */ --copy_rem; } /* count it in the actual length */ ++(*value_ch_cnt); } /* get the upper-case flags (sublst's 3rd element) */ sublst.ll_index(vmg_ &val, 3); if (val.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); *uc_result_flags = val.val.intval; /* get the lower-case flags (sublst's 4th element) */ sublst.ll_index(vmg_ &val, 4); if (val.typ != VM_INT) err_throw(VMERR_BAD_TYPE_BIF); *lc_result_flags = val.val.intval; /* we're done with this mapping, so advance to the next one */ ++idx_; } protected: /* my list data */ const vm_val_t *lst_; /* the list index of the next mapping to retrieve */ size_t idx_; }; /* ------------------------------------------------------------------------ */ /* * Create from stack. We take the following constructor arguments: * * trunc_len (int: truncation length) *. case_sensitive (bool: case sensitivity flag) *. mappings (list: equivalence mappings) * * Our equivalence mappings are given as a list of lists; the main list * consists of one sublist per mapping. Each mapping sublist looks like * this: * * ['ref_char', 'val_string', uc_flags, lc_flag] * * The 'ref_char' is a one-character string giving the reference character * of the mapping, and 'val_string' is a string of one or more characters * that can match the reference character in a value (input) string. * uc_flags and lc_flags are integers giving the upper-case and lower-case * flags (respectively) that are to be added to the match result code when * the mapping is used to match a pair of strings. */ vm_obj_id_t CVmObjStrComp::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { size_t trunc_len; int case_sensitive; const vm_val_t *lst; vm_obj_id_t id; CVmObjStrComp *obj; size_t equiv_cnt; size_t total_chars; /* check arguments */ if (argc != 3) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* pop the truncation length parameter */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil, so truncation is not allowed */ trunc_len = 0; G_stk->discard(); } else { /* retrieve the truncation length as an integer */ trunc_len = CVmBif::pop_int_val(vmg0_); } /* get the case sensitivity flag */ case_sensitive = CVmBif::pop_bool_val(vmg0_); /* * retrieve the mapping list, but leave it on the stack (for gc * protection) */ if (G_stk->get(0)->typ == VM_NIL) { /* there are no mappings */ lst = 0; equiv_cnt = 0; total_chars = 0; } else { size_t i; int ec; /* get the list value from the argument */ lst = G_stk->get(0); if (!lst->is_listlike(vmg0_) || (ec = lst->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_TYPE_BIF); /* run through the list and count the value string characters */ for (i = 1, total_chars = 0, equiv_cnt = ec ; i <= equiv_cnt ; ++i) { vm_val_t sublst; vm_val_t val; const char *strp; utf8_ptr ustrp; /* get this mapping from the list */ lst->ll_index(vmg_ &sublst, i); /* make sure it's a sublist */ if (!sublst.is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* * get the second element of the mapping sublist - this is the * value string */ sublst.ll_index(vmg_ &val, 2); if ((strp = val.get_as_string(vmg0_)) == 0) err_throw(VMERR_BAD_TYPE_BIF); /* add the character length of the string to the total */ ustrp.set((char *)strp + VMB_LEN); total_chars += ustrp.len(vmb_get_len(strp)); } } /* create the new object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); obj = new (vmg_ id) CVmObjStrComp(); /* set up a list-based mapping reader */ CVmObjStrCompMapReaderList reader(lst); /* allocate and initialize the new object's extension */ obj->alloc_ext(vmg_ trunc_len, case_sensitive, equiv_cnt, total_chars, &reader); /* discard the gc protection */ G_stk->discard(); /* return the new object */ return id; } /* ------------------------------------------------------------------------ */ /* * Load from an image file */ void CVmObjStrComp::load_from_image(VMG_ vm_obj_id_t /*self*/, const char *ptr, size_t len) { /* load my image data */ CVmReadOnlyMemoryStream str(ptr, len); load_from_stream(vmg_ &str); } /* * Load from an abstract stream */ void CVmObjStrComp::load_from_stream(VMG_ CVmStream *str) { unsigned int trunc_len; unsigned int flags; unsigned int equiv_cnt; unsigned int total_chars; /* load the fixed header */ trunc_len = str->read_uint2(); flags = str->read_uint2(); equiv_cnt = str->read_uint2(); total_chars = str->read_uint2(); /* set up a stream-based mapping reader */ CVmObjStrCompMapReaderStream reader(str); /* allocate and initialize our extension */ alloc_ext(vmg_ trunc_len, (flags & 0x0001) != 0, equiv_cnt, total_chars, &reader); } /* * Allocate and initialize our extension */ void CVmObjStrComp::alloc_ext(VMG_ size_t trunc_len, int case_sensitive, size_t equiv_cnt, size_t total_chars, CVmObjStrCompMapReader *reader) { size_t siz; vmobj_strcmp_ext *ext; vmobj_strcmp_equiv *nxt_equiv; wchar_t *nxt_ch; size_t ch_rem; size_t i; size_t idx1, idx2; /* delete my extension, if I have one already */ delete_ext(vmg0_); /* * Calculate how much space we need for our extension. In addition to * the fixed part, allocate space for one vmobj_strcmp_equiv structure * per mapping, plus the wchar_t's for the value mappings. */ siz = sizeof(vmobj_strcmp_ext) + (equiv_cnt * sizeof(vmobj_strcmp_equiv)) + (total_chars * sizeof(wchar_t)); /* allocate our new extension */ ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this); ext = get_ext(); /* * set up our suballocation pool pointers: put the equivalence mapping * structures after the fixed part of the extension, and put the * wchar_t's after the equivalence mappings */ nxt_equiv = (vmobj_strcmp_equiv *)(ext + 1); nxt_ch = (wchar_t *)(&nxt_equiv[equiv_cnt]); ch_rem = total_chars; /* initialize the extension structure */ ext->trunc_len = trunc_len; ext->case_sensitive = case_sensitive; /* * we have no equivalence mappings installed yet, so clear out the * first tier of the mapping array */ for (i = 0 ; i < countof(ext->equiv) ; ++i) ext->equiv[i] = 0; /* load the mappings */ for (i = 0 ; i < equiv_cnt ; ++i, ++nxt_equiv) { wchar_t ref_ch; /* * set up our equivalent's value buffer with the remainder of our * main buffer */ nxt_equiv->val_ch = nxt_ch; nxt_equiv->val_ch_cnt = ch_rem; /* read the mapping */ reader->read_mapping(vmg_ &ref_ch, &nxt_equiv->uc_result_flags, &nxt_equiv->lc_result_flags, nxt_equiv->val_ch, &nxt_equiv->val_ch_cnt); /* deduct this mapping from our main value character buffer */ nxt_ch += nxt_equiv->val_ch_cnt; ch_rem -= nxt_equiv->val_ch_cnt; /* if we don't have a first-tier table for this character, add one */ idx1 = (ref_ch >> 8) & 0xFF; idx2 = (ref_ch & 0xFF); if (ext->equiv[idx1] == 0) { vmobj_strcmp_equiv **p; size_t j; /* allocate a first-tier table for this index */ ext->equiv[idx1] = p = (vmobj_strcmp_equiv **)t3malloc( 256 * sizeof(vmobj_strcmp_equiv *)); /* clear out the first-tier table */ for (j = 0 ; j < 256 ; ++j, ++p) *p = 0; } /* set the mapping for this character */ ext->equiv[idx1][idx2] = nxt_equiv; } } /* ------------------------------------------------------------------------ */ /* * save to a file */ void CVmObjStrComp::save_to_file(VMG_ class CVmFile *fp) { /* write our data to the file */ CVmFileStream str(fp); write_to_stream(vmg_ &str, 0); } /* * Serialize to a stream */ ulong CVmObjStrComp::write_to_stream(VMG_ CVmStream *str, ulong *bytes_avail) { wchar_t ref_ch_base; vmobj_strcmp_ext *ext = get_ext(); size_t i; vmobj_strcmp_equiv ***p; size_t total_value_ch; size_t equiv_cnt; size_t need_size; /* get the mapping totals */ count_equiv_mappings(&equiv_cnt, &total_value_ch); /* * Calculate our space needs. We need 8 bytes for the fixed header, * 11 bytes per equivalent mapping, and 2 bytes per value string * character. */ need_size = 8 + (11 * equiv_cnt) + (2 * total_value_ch); /* if we have a size limit, check to make sure we can abide by it */ if (bytes_avail != 0 && need_size > *bytes_avail) { /* * there's not enough space in the output stream for us, so don't * write anything at all; simply return the amount of space we * need */ return need_size; } /* write out the serialization structure header */ str->write_uint2(ext->trunc_len); str->write_uint2(ext->case_sensitive ? 0x0001 : 0x0000); str->write_uint2(equiv_cnt); str->write_uint2(total_value_ch); /* run through our equivalence table again and write the mappings */ for (ref_ch_base = 0, i = 0, p = ext->equiv ; i < countof(ext->equiv) ; ++i, ++p, ref_ch_base += 256) { vmobj_strcmp_equiv **ep; size_t j; /* if this first-tier mapping is unused, skip it */ if (*p == 0) continue; /* run through our second-level table */ for (j = 0, ep = *p ; j < 256 ; ++j, ++ep) { /* if this mapping is used, write it out */ if (*ep != 0) { size_t k; wchar_t *vp; /* write the fixed part of the mapping */ str->write_uint2(ref_ch_base + j); str->write_byte((uchar)(*ep)->val_ch_cnt); str->write_uint4((*ep)->uc_result_flags); str->write_uint4((*ep)->lc_result_flags); /* write the value mapping characters */ for (k = (*ep)->val_ch_cnt, vp = (*ep)->val_ch ; k != 0 ; --k, ++vp) { /* write this character */ str->write_uint2(*vp); } } } } /* return our space needs */ return need_size; } /* * Count the equivalence mappings. */ void CVmObjStrComp::count_equiv_mappings(size_t *equiv_cnt, size_t *total_value_ch) { vmobj_strcmp_ext *ext = get_ext(); size_t i; vmobj_strcmp_equiv ***p; /* run through our table and count up the mappings */ for (*total_value_ch = 0, *equiv_cnt = 0, i = 0, p = ext->equiv ; i < countof(ext->equiv) ; ++i, ++p) { vmobj_strcmp_equiv **ep; size_t j; /* if this first-tier mapping is unused, skip it */ if (*p == 0) continue; /* run through our second-level table */ for (j = 0, ep = *p ; j < 256 ; ++j, ++ep) { /* if this mapping is used, count it */ if (*ep != 0) { /* count this equivalent mapping */ ++(*equiv_cnt); /* count its value mapping characters in the total */ *total_value_ch += (*ep)->val_ch_cnt; } } } } /* ------------------------------------------------------------------------ */ /* * restore from a file */ void CVmObjStrComp::restore_from_file(VMG_ vm_obj_id_t /*self*/, class CVmFile *fp, CVmObjFixup *) { /* load from the file */ CVmFileStream str(fp); load_from_stream(vmg_ &str); } /* ------------------------------------------------------------------------ */ /* * property evaluator - calculate a hash value */ int CVmObjStrComp::getp_calc_hash(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(1); const char *strp; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * retrieve the string argument (it must be a string), but leave it on * the stack for now, for gc protection */ strp = G_stk->get(0)->get_as_string(vmg0_); if (strp == 0) err_throw(VMERR_BAD_TYPE_BIF); /* calculate the hash value and return it */ retval->set_int(calc_str_hash(strp + VMB_LEN, vmb_get_len(strp))); /* discard gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Hash adder */ struct StrCompHashAdder { StrCompHashAdder(size_t trunc_len) { this->trunc_len = trunc_len; this->has_trunc = (trunc_len != 0); hash = 0; } int add(wchar_t ch) { /* add the character to the hash */ hash += ch; hash &= 0xFFFF; /* if there's a truncation limit, count this against the limit */ if (has_trunc && --trunc_len == 0) { /* we've reached the limit */ return FALSE; } else { /* haven't reached the limit yet */ return TRUE; } } int done() const { return has_trunc && trunc_len == 0; } /* truncation limit */ size_t trunc_len; /* is there a truncation limit? */ int has_trunc; /* the hash code */ unsigned int hash; }; /* * Calculate a hash value */ unsigned int CVmObjStrComp::calc_str_hash(const char *strp, size_t len) { /* get my extension */ vmobj_strcmp_ext *ext = get_ext(); /* set up to scan the string */ utf8_ptr p((char *)strp); /* * Scan the string. Limit the scan to our truncation length, counting * substitution expansions and case folding expansions, because we * don't want to distinguish hash buckets beyond the truncation point. * If we did, a truncated string wouldn't hash into the same bucket as * a longer string it matches; all matching strings are required to go * into the same bucket, so we can't have such a hash mismatch. */ StrCompHashAdder hash(ext->trunc_len); for ( ; len != 0 ; p.inc(&len)) { /* get the current character */ wchar_t ch = p.getch(); /* check for a substitution mapping for this character */ vmobj_strcmp_equiv **t1, *eq; if ((t1 = ext->equiv[(ch >> 8) & 0xFF]) != 0 && (eq = t1[ch & 0xFF]) != 0) { /* * This character has a mapping, so add the contribution from * the canonical form of the character, which is the value * side of the mapping. */ wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vlen != 0 ; ++vp, --vlen) { /* get this character */ ch = *vp; /* * use case folding if we're insensitive to case and the * character has a case folding */ const wchar_t *f; if (!ext->case_sensitive && (f = t3_to_fold(ch)) != 0) { /* add the folded expansion to the hash */ for (const wchar_t *f = t3_to_fold(ch) ; *f != 0 ; ++f) { if (!hash.add(*f)) return hash.hash; } } else { if (!hash.add(ch)) return hash.hash; } } } else { /* * if we're not sensitive to case, and there's a case folding * available, use the case-folded representation of a character * for its hash value */ const wchar_t *f; if (!ext->case_sensitive && (f = t3_to_fold(ch)) != 0) { /* add the folded expansion to the hash */ for (const wchar_t *f = t3_to_fold(ch) ; *f != 0 ; ++f) { if (!hash.add(*f)) return hash.hash; } } else { /* exact case only - add the character as-is */ if (!hash.add(ch)) return hash.hash; } } } /* return the hash code */ return hash.hash; } /* ------------------------------------------------------------------------ */ /* * Pre-defined return flag values. */ #define RF_MATCH 0x0001 /* the string matched */ #define RF_CASEFOLD 0x0002 /* matched with case folding */ #define RF_TRUNC 0x0004 /* matched with truncation */ /* * property evaluator - calculate a hash value */ int CVmObjStrComp::getp_match_values(VMG_ vm_obj_id_t /*self*/, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(2); const char *valstr; const char *refstr; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* retrieve the strings, but leave them on the stack for gc protection */ valstr = G_stk->get(0)->get_as_string(vmg0_); refstr = G_stk->get(1)->get_as_string(vmg0_); /* make sure they're valid strings */ if (valstr == 0 || refstr == 0) err_throw(VMERR_BAD_TYPE_BIF); /* compare the strings and return the result */ retval->set_int(match_strings(valstr + VMB_LEN, vmb_get_len(valstr), refstr + VMB_LEN, vmb_get_len(refstr))); /* discard the gc protection */ G_stk->discard(2); /* handled */ return TRUE; } /* * Match two strings */ unsigned long CVmObjStrComp::match_strings(const char *valstr, size_t vallen, const char *refstr, size_t reflen) { vmobj_strcmp_ext *ext = get_ext(); utf8_ptr valp; utf8_ptr refp; unsigned long ret; int fold_case = !(ext->case_sensitive); /* set up to scan the strings */ valp.set((char *)valstr); refp.set((char *)refstr); /* start with no return flags */ ret = 0; /* scan the strings */ while (vallen != 0 && reflen != 0) { /* get each character */ wchar_t valch = valp.getch(); wchar_t refch = refp.getch(); /* check for an exact match first */ if (refch == valch) { /* it's an exact match - skip this character in each string */ valp.inc(&vallen); refp.inc(&reflen); continue; } /* check for a case-folded match if we're insensitive to case */ if (fold_case && t3_compare_case_fold_min(valp, vallen, refp, reflen) == 0) { /* note in the flags that we have differing cases in the match */ ret |= RF_CASEFOLD; /* keep going */ continue; } /* check for a reference equivalence mapping */ vmobj_strcmp_equiv **t1; vmobj_strcmp_equiv *eq; if ((t1 = ext->equiv[(refch >> 8) & 0xFF]) != 0 && (eq = t1[refch & 0xFF]) != 0) { /* * In case we match, apply the appropriate flags added for the * equivalence mapping, based on the case of the first value * character we're testing. (If we don't match, we'll simply * return failure, so it won't matter that we messed with the * flags.) */ ret |= (t3_is_upper(valch) ? eq->uc_result_flags : eq->lc_result_flags); /* match each character from the mapping string */ const wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vallen != 0 && vlen != 0 ; ) { /* get this character */ refch = *vp; /* if we have an exact match, keep going */ if (refch == valch) { /* matched - skip this character in each string */ valp.inc(&vallen); ++vp, --vlen; /* keep going */ continue; } /* check for a case-folded match if appropriate */ if (fold_case && t3_compare_case_fold_min(valp, vallen, vp, vlen) == 0) { /* note the case-folded match and keep going */ ret |= RF_CASEFOLD; continue; } /* no match */ return 0; } /* * if we didn't make it to the end of the equivalence string, * we must have run out of source before we matched the whole * string, so we don'to have a match */ if (vlen != 0) return 0; /* * If we make it here, we matched the equivalence mapping. * We've already skipped the input we matched, so skip the * reference character and keep going. */ refp.inc(&reflen); continue; } /* we don't have anything else to try, so we don't have a match */ return 0; } /* * If we ran out of reference string before we ran out of value * string, we definitely do not have a match. If we ran out of value * string before we ran out reference string, we have a match as long * as we matched at least the truncation length. */ if (reflen == 0 && vallen == 0) { /* * We ran out of both at the same time - it's a match. Return the * result code up to this point OR'd with RF_MATCH, which is our * pre-defined bit that we set for every match. */ return (ret | RF_MATCH); } else if (vallen != 0) { /* we ran out of reference string first - it's not a match */ return 0; } else { /* * We ran out of value string first, so it's a truncated match if * we matched at least up to the truncation length (assuming we * allow truncation at all). If we didn't make it to the * truncation length, or we don't allow truncation, it's not a * match. */ size_t valcharlen = utf8_ptr::s_len(valstr, valp.getptr() - valstr); if (ext->trunc_len != 0 && valcharlen >= ext->trunc_len) { /* * it's a truncated match - return the result code up to this * point, OR'd with RF_MATCH (our pre-defined bit we set for * every match) and RF_TRUNC (our pre-defined bit we set for * truncated matches) */ return (ret | RF_MATCH | RF_TRUNC); } else { /* didn't make it to the truncation length, so it's not a match */ return 0; } } } /* * Check for an approximation match to a given character. This checks the * given input string for a match to the approximation for a given * reference character. Returns the number of characters in the match, or * zero if there's no match. */ size_t CVmObjStrComp::match_chars(const wchar_t *valstr, size_t vallen, wchar_t refch) { vmobj_strcmp_ext *ext = get_ext(); int fold_case = !(ext->case_sensitive); vmobj_strcmp_equiv **t1; vmobj_strcmp_equiv *eq; wchar_t valch = *valstr; const wchar_t *start = valstr; /* check for an exact match first */ if (refch == valch) return 1; /* check for a case-folded match if we're insensitive to case */ if (fold_case) { /* try a minimum case-folded match to the one-character ref string */ size_t ovallen = vallen, reflen = 1; const wchar_t *refstr = &refch; if (t3_compare_case_fold_min(valstr, vallen, refstr, reflen) == 0) { /* got it - return the length we consumed from the value string */ return ovallen - vallen; } } /* check for a reference equivalence mapping */ if ((t1 = ext->equiv[(refch >> 8) & 0xFF]) != 0 && (eq = t1[refch & 0xFF]) != 0) { /* match each character from the mapping string */ const wchar_t *vp; size_t vlen; for (vp = eq->val_ch, vlen = eq->val_ch_cnt ; vallen != 0 && vlen != 0 ; ) { /* get this mapping character */ refch = *vp; /* if we have an exact match, keep going */ if (refch == valch) { /* matched exactly - skip this character and keep going */ ++valstr, --vallen; ++vp, --vlen; continue; } /* check for a case-folded match if appropriate */ if (fold_case && t3_compare_case_fold_min(valstr, vallen, vp, vlen) == 0) { /* matched - keep going */ continue; } /* no match */ return 0; } /* we've matched the mapping - return the match length */ return valstr - start; } /* it doesn't match any of the possibilities */ return 0; } /* * Get the truncation length */ size_t CVmObjStrComp::trunc_len() const { return get_ext()->trunc_len; } frobtads-1.2.3/tads3/tcprsstm.cpp0000644000175000001440000112467012145504453016123 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCPRSSTM.CPP,v 1.1 1999/07/11 00:46:54 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.cpp - TADS 3 Compiler Parser - statement node classes Function This parser module includes statement node classes that are needed for parsing executable code bodies - the insides of functions and methods. This doesn't include the outer program-level structures, such as object definitions, or the inner expression structures. This code is needed for interpreters that include "eval()" functionality, for dynamic construction of new executable code. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tcmake.h" /* ------------------------------------------------------------------------ */ /* * Clear the local anonymous function shared context information */ void CTcParser::clear_local_ctx() { /* we don't have a local context */ has_local_ctx_ = FALSE; /* there is no local context local yet */ local_ctx_var_num_ = 0; /* no variable properties are used */ ctx_var_props_used_ = 0; /* * start the context array index at the next entry after the slot we * always use to store the method context of the enclosing lexical * scope */ next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1; /* we haven't yet referenced 'self' or any other method context yet */ self_referenced_ = FALSE; full_method_ctx_referenced_ = FALSE; /* we don't yet need the method context in the local context */ local_ctx_needs_self_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; } /* * Set up the current code body with a local variable context */ void CTcParser::init_local_ctx() { /* if I already have a local variable context, there's nothing to do */ if (has_local_ctx_) return; /* note that we now require a local variable context object */ has_local_ctx_ = TRUE; /* create the local context variable */ local_ctx_var_num_ = alloc_local(); } /* * Allocate a context variable index */ int CTcParser::alloc_ctx_arr_idx() { return next_ctx_arr_idx_++; } /* * enumeration callback context */ struct enum_locals_ctx { /* symbol table containing context locals */ CTcPrsSymtab *symtab; /* code body */ CTPNCodeBody *code_body; }; /* * Enumeration callback - find local variables inherited from enclosing * scopes (for anonymous functions) */ void CTcParser::enum_for_ctx_locals(void *ctx0, CTcSymbol *sym) { /* cast the context */ enum_locals_ctx *ctx = (enum_locals_ctx *)ctx0; /* tell this symbol to apply its local variable conversion */ sym->apply_ctx_var_conv(ctx->symtab, ctx->code_body); } /* * Set up the local context for access to enclosing scope locals. Call * this after parsing a code body to set up the hooks between the new code * body's proxy symbol table for enclosing scopes and the actual enclosing * local scopes. */ void CTcParser::finish_local_ctx(CTPNCodeBody *cb, CTcPrsSymtab *local_symtab) { /* if we have a local context, mark the code body accordingly */ if (has_local_ctx_) cb->set_local_ctx(local_ctx_var_num_, next_ctx_arr_idx_ - 1); /* * If the caller passed in a local symbol table, check the table for * context variables from enclosing scopes, and assign the local holder * for each such variable. */ if (local_symtab != 0) { /* * consider only the outermost local table, since that's where the * shared locals reside */ CTcPrsSymtab *tab, *par; for (tab = local_symtab_, par = tab->get_parent() ; par != 0 && par != global_symtab_ ; tab = par, par = par->get_parent()) ; /* enumerate the variables */ enum_locals_ctx ctx; ctx.symtab = tab; ctx.code_body = cb; tab->enum_entries(&enum_for_ctx_locals, &ctx); } } /* ------------------------------------------------------------------------ */ /* * Parse a formal parameter list. If 'count_only' is true, we're only * interested in counting the formals, and we don't bother adding them * to any symbol table. If 'opt_allowed' is true, a parameter name can * be followed by a question mark token to mark the parameter as * optional. * * '*argc' returns with the number of parameters. 'opt_argc' can be * null; if it's not null, '*opt_argc' returns with the number of * parameters marked as optional. * * 'base_formal_num' is the starting formal parameter number to use in * creating symbol table entries. This is meaningful only if * 'count_only' is false. * * The caller must already have checked for an open paren and skipped * it. */ void CTcParser::parse_formal_list( int count_only, int opt_allowed, int *argc, int *opt_argc, int *varargs, int *varargs_list, CTcSymLocal **varargs_list_local, int *err, int base_formal_num, int for_short_anon_func, CTcFormalTypeList **type_list) { CTcSymLocal *lcl = 0; CTcToken varname; int named_param = FALSE, opt_param = FALSE; int is_typed = FALSE; int defval_cnt = 0; /* * choose the end token - if this is a normal argument list, the * ending token is a right parenthesis; for a short-form anonymous * function, it's a colon */ tc_toktyp_t end_tok = (for_short_anon_func ? TOKT_COLON : TOKT_RPAR); int missing_end_tok_err = (for_short_anon_func ? TCERR_MISSING_COLON_FORMAL : TCERR_MISSING_RPAR_FORMAL); /* no arguments yet */ *argc = 0; if (opt_argc != 0) *opt_argc = 0; *varargs = FALSE; *varargs_list = FALSE; /* no error yet */ *err = FALSE; /* we've only just begun */ int done = FALSE; /* check for an empty list */ if (G_tok->cur() == end_tok) { /* the list is empty - we're already done */ done = TRUE; /* skip the closing token */ G_tok->next(); } /* keep going until done */ while (!done) { tc_toktyp_t suffix_tok = TOKT_INVALID; CTcPrsNode *defval_expr = 0; /* see what comes next */ switch(G_tok->cur()) { case TOKT_ELLIPSIS: /* it's an ellipsis - note that we have varargs */ *varargs = TRUE; parse_ellipsis: /* add the varargs indicator to the formal type list */ if (type_list != 0 && *type_list != 0) (*type_list)->add_ellipsis(); /* the next token must be the close paren */ if (G_tok->next() == end_tok) { /* we've reached the end of the list */ goto handle_end_tok; } else { /* this is an error - guess about the problem */ switch(G_tok->cur()) { case TOKT_COMMA: /* * we seem to have more in the list - log an error, * but continue parsing the list */ G_tok->next(); G_tok->log_error(TCERR_ELLIPSIS_NOT_LAST); break; default: /* * anything else is probably a missing right paren - * provide it and exit the formal list */ done = TRUE; G_tok->log_error_curtok(missing_end_tok_err); break; } } break; case TOKT_LBRACK: /* * varargs with named list variable for last parameter - * this generates setup code that stores the arguments from * this one forward in a list to be stored in this variable */ /* note that we have varargs */ *varargs = TRUE; /* note it in the type list as well */ if (type_list != 0 && *type_list != 0) (*type_list)->add_ellipsis(); /* skip the bracket and check that a symbol follows */ switch(G_tok->next()) { case TOKT_RBRACK: /* * empty brackets - treat this as identical to an * ellipsis; since they didn't name the varargs list * parameter, it's just a varargs indication */ goto parse_ellipsis; case TOKT_SYM: /* if we're creating a symbol table, add the symbol */ if (!count_only) { int local_id; /* create a local scope if we haven't already */ create_scope_local_symtab(); /* note that we have a varargs list parameter */ *varargs_list = TRUE; /* * insert the new variable as a local - it's not a * formal, since we're going to take the actuals, * make a list, and store them in this local; the * formal in this position will not be named, since * this is a varargs function */ local_id = alloc_local(); *varargs_list_local = local_symtab_->add_local( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), local_id, FALSE, TRUE, FALSE); /* mark it as a list parameter */ (*varargs_list_local)->set_list_param(TRUE); } /* a close bracket must follow */ switch (G_tok->next()) { case TOKT_RBRACK: /* skip the close bracket, and check for the end token */ if (G_tok->next() == end_tok) { /* that's it - the list is done */ goto handle_end_tok; } /* we didn't find the end token - guess about the error */ switch (G_tok->cur()) { case TOKT_COMMA: /* * they seem to want another parameter - this is * an error, but keep parsing anyway */ G_tok->log_error(TCERR_LISTPAR_NOT_LAST); break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_LPAR: case TOKT_EOF: /* the ending token was probably just forgotten */ G_tok->log_error_curtok(missing_end_tok_err); /* presume that the argument list is done */ done = TRUE; break; default: /* end token is missing - log an error */ G_tok->log_error_curtok(missing_end_tok_err); /* * skip the errant token; if the ending token * follows this one, skip it as well, since * there's probably just a stray extra token * before the ending token */ if (G_tok->next() == end_tok) G_tok->next(); /* presume they simply left out the paren */ done = TRUE; break; } break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_EOF: /* they must have left out the bracket and paren */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK); done = TRUE; break; default: /* note the missing bracket */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_RBRACK); /* skip the errant token and assume we're done */ G_tok->next(); done = TRUE; break; } break; default: /* log an error */ G_tok->log_error_curtok(TCERR_LISTPAR_REQ_SYM); /* if this is the ending token, we're done */ if (G_tok->cur() == end_tok) goto handle_end_tok; /* * if we're not on something that looks like the end of * the parameter list, skip the errant token */ switch(G_tok->cur()) { case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EOF: /* looks like they left out the closing markers */ done = TRUE; break; default: /* * skip the errant token and continue - they * probably have more argument list following */ G_tok->next(); break; } break; } break; case TOKT_SYM: /* * If a formal type list is allowed, check to see if the next * token is a symbol - if it is, the current token is the type, * and the next token is the variable name. */ is_typed = FALSE; if (type_list != 0) { /* save the initial token */ CTcToken init_tok = *G_tok->copycur(); /* check the next one */ if (G_tok->next() == TOKT_SYM) { /* * We have another symbol, so the first token is the * type name, and the current token is the param name. * Add the type to the formal type list. */ /* first, make sure we've created the type list */ if (*type_list == 0) { /* * We haven't created one yet - create it now. * Since we're just getting around to creating the * list, add untyped elements for the parameters * we've seen so far - they all must have been * untyped, since we create the list on * encountering the first typed parameter. */ *type_list = new (G_prsmem) CTcFormalTypeList(); (*type_list)->add_untyped_params(*argc); } /* add this typed parameter */ (*type_list)->add_typed_param(&init_tok); /* flag it as typed */ is_typed = TRUE; /* * make sure the type symbol is defined as a class or * object */ CTcSymbol *clsym = global_symtab_->find( init_tok.get_text(), init_tok.get_text_len()); if (clsym == 0) { /* it's not defined - add it as an external object */ clsym = new CTcSymObj( init_tok.get_text(), init_tok.get_text_len(), FALSE, G_cg->new_obj_id(), TRUE, TC_META_TADSOBJ, 0); /* mark it as referenced */ clsym->mark_referenced(); /* add it to the table */ global_symtab_->add_entry(clsym); } else if (clsym->get_type() != TC_SYM_OBJ && clsym->get_type() != TC_SYM_METACLASS) { /* it's something other than an object */ G_tok->log_error(TCERR_MMPARAM_NOT_OBJECT, (int)init_tok.get_text_len(), init_tok.get_text()); } } else { /* there's no type name - back up to the param name */ G_tok->unget(); /* * We need to add an untyped element to the type list, * but don't do so quite yet - we need to make sure * that this isn't a named argument parameter. Only * positional elements go in the type list. For now, * simply flag it as non-typed. */ is_typed = FALSE; } } /* remember and skip the symbol name */ varname = *G_tok->getcur(); G_tok->next(); /* check for a colon, which might indicate a named parameter */ named_param = FALSE; if (G_tok->cur() == TOKT_COLON) { /* it looks like a named param so far */ named_param = TRUE; /* * There's one case where a colon *doesn't* indicate a * named parameter, which is when it's the closing colon of * a short-form anonymous function parameter list. For * those, the colon is a name marker only if it's followed * by a continuation of the formal list: specifically, * another parameter (so, a comma), or ':' to indicate the * actual end of the list. If anything else follows, the * colon is the terminator rather than part of the formal * list. */ if (for_short_anon_func) { /* check the next token */ switch (G_tok->next()) { case TOKT_COMMA: case TOKT_COLON: /* * More list follows, so this is indeed a named * parameter marker. */ break; default: /* * We don't have more list, so this colon is the * end marker for the formal list. Put it back and * continue. */ named_param = FALSE; G_tok->unget(); break; } } else { /* * for regular argument lists, this can only be a named * parameter indicator; skip it */ G_tok->next(); } /* named parameters can't be typed */ if (named_param && is_typed) { G_tok->log_error(TCERR_NAMED_ARG_NO_TYPE, (int)varname.get_text_len(), varname.get_text()); } } /* * check for an optionality flag (a '?' suffix) or a default * value ('= ') */ opt_param = FALSE; suffix_tok = G_tok->cur(); defval_expr = 0; if (opt_allowed && (suffix_tok == TOKT_QUESTION || suffix_tok == TOKT_EQ)) { /* it's optional */ opt_param = TRUE; /* count the optional argument */ if (opt_argc != 0 && !named_param) ++(*opt_argc); /* skip the suffix token */ G_tok->next(); /* if it was an '=', parse the default value expression */ if (suffix_tok == TOKT_EQ) { /* * parse the expression - comma has precedence in this * context as an argument separator, so we need to * parse this as an assignment expression (this is for * precedence reasons, NOT because it's syntactically * like an assignment - the '=' postfix to the argument * name isn't part of the expression we're parsing) */ defval_expr = parse_asi_expr(); } } /* * if any previous arguments are optional, all subsequent * arguments must be optional as well */ if (opt_allowed && opt_argc != 0 && *opt_argc != 0 && !opt_param && !named_param) { /* flag it as an error */ G_tok->log_error(TCERR_ARG_MUST_BE_OPT, (int)varname.get_text_len(), varname.get_text()); } /* if we're creating symbol table entries, add this symbol */ lcl = 0; if (!count_only) { /* * create a new local symbol table if we don't already have * one */ create_scope_local_symtab(); /* * Insert the new variable for the formal. If it's a named * parameter or an optional parameter, it's not a real * formal, since it doesn't point directly to a parameter * slot in the stack frame - it points instead to a regular * local variable slot that we have to set up with * generated code in the function prolog. */ if (named_param || opt_param) { /* it's a pseudo-formal - create a local for it */ lcl = local_symtab_->add_local( varname.get_text(), varname.get_text_len(), alloc_local(), FALSE, FALSE, FALSE); /* set the special flags */ lcl->set_named_param(named_param); lcl->set_opt_param(opt_param); /* remember the default value expression, if any */ if (defval_expr != 0) lcl->set_defval_expr(defval_expr, defval_cnt++); } else { /* insert the new local variable for the formal */ lcl = local_symtab_->add_formal( varname.get_text(), varname.get_text_len(), base_formal_num + *argc, FALSE); } /* * If it's a position parameter, set its parameter index. * Since optional parameters are actually stored as local * variables, we need to separately track the parameter * list index so that we can load the value into the local * on function entry. */ if (!named_param && lcl != 0) lcl->set_param_index(*argc); } /* * If it's not a named parameter, count it. Named parameters * don't count as formals since they're not part of the * positional list and aren't in the parameter area of the * stack frame. */ if (!named_param) { /* count it in the positional list */ ++(*argc); /* if it was untyped, add it to the type list */ if (type_list != 0 && *type_list != 0 && !is_typed) (*type_list)->add_untyped_param(); } /* check for the closing token */ if (G_tok->cur() == end_tok) { /* it's the closing token - skip it and stop scanning */ goto handle_end_tok; } /* check what follows */ switch (G_tok->cur()) { case TOKT_COMMA: /* skip the comma and continue */ G_tok->next(); break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EQ: case TOKT_EOF: /* * We've obviously left the list - the problem is * probably that we're missing the right paren. Catch * it in the main loop. */ break; case TOKT_SYM: /* * they seem to have left out a comma - keep parsing * from the symbol token */ G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL); break; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_REQ_COMMA_FORMAL); /* skip the errant token and continue */ G_tok->next(); break; } /* done with the formal */ break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_EOF: /* * We've obviously left the list - the problem is probably * that we're missing the right paren. Log an error and * stop scanning. */ G_tok->log_error_curtok(missing_end_tok_err); done = TRUE; break; default: /* check to see if it's the ending token */ if (G_tok->cur() == end_tok) { /* * they seem to have put in a comma followed by the * ending token - it's probably just a stray extra comma */ G_tok->log_error(TCERR_MISSING_LAST_FORMAL); /* skip the paren and stop scanning */ G_tok->next(); done = TRUE; break; } /* * If this is a short-form anonymous function's parameter * list, they probably forgot the colon - generate a more * specific error for this case, and assume the list ends * here. */ if (for_short_anon_func) { /* tell them they left out the colon */ G_tok->log_error_curtok(TCERR_MISSING_COLON_FORMAL); /* presume the argument list was meant to end here */ done = TRUE; break; } /* * anything else is probably just an extraneous token; skip * it and go on */ G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL); G_tok->next(); break; handle_end_tok: /* we've reached the end token - skip it, and we're done */ G_tok->next(); done = TRUE; break; } } /* * Check for the "multimethod" modifier, if allowed. It's allowed if a * formal type list is allowed and this is a normal full parameter list * (i.e., specified in parentheses rather than as an anonymous function * formal list). */ if (G_tok->cur() == TOKT_SYM && G_tok->cur_tok_matches("multimethod", 11)) { /* make sure it's allowed */ if (type_list != 0 && end_tok == TOKT_RPAR) { /* it's allowed - create the type list if we haven't already */ if (*type_list == 0) { /* create the type list */ *type_list = new (G_prsmem) CTcFormalTypeList(); /* add untyped parameters for the ones we've defined */ (*type_list)->add_untyped_params(*argc); /* mark it as varargs if applicable */ if (*varargs) (*type_list)->add_ellipsis(); } } else { /* it's not allowed - flag it as an error */ G_tok->log_error(TCERR_MULTIMETHOD_NOT_ALLOWED); } /* skip the token */ G_tok->next(); } /* deduct the optional arguments from the fixed arguments */ if (opt_argc != 0) *argc -= *opt_argc; } /* ------------------------------------------------------------------------ */ /* * Parse a nested code body (such as an anonymous function code body) */ CTPNCodeBody *CTcParser::parse_nested_code_body( int eq_before_brace, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, CTcSymLocal **p_varargs_list_local, int *p_has_retval, int *err, CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type) { /* remember the original parser state */ CTcPrsSymtab *old_local_symtab = local_symtab_; CTcPrsSymtab *old_enclosing_local_symtab = enclosing_local_symtab_; CTPNStmEnclosing *old_enclosing_stm = enclosing_stm_; CTcPrsSymtab *old_goto_symtab = goto_symtab_; int old_local_cnt = local_cnt_; int old_max_local_cnt = max_local_cnt_; int old_has_local_ctx = has_local_ctx_; int old_local_ctx_var_num = local_ctx_var_num_; int old_ctx_var_props_used = ctx_var_props_used_; int old_next_ctx_arr_idx = next_ctx_arr_idx_; int old_self_valid = self_valid_; int old_self_referenced = self_referenced_; int old_full_method_ctx_referenced = full_method_ctx_referenced_; int old_local_ctx_needs_self = local_ctx_needs_self_; int old_local_ctx_needs_full_method_ctx = local_ctx_needs_full_method_ctx_; CTcCodeBodyRef *old_cur_code_body = cur_code_body_; /* parse the code body */ CTPNCodeBody *code_body = parse_code_body( eq_before_brace, FALSE, self_valid, p_argc, p_opt_argc, p_varargs, p_varargs_list, p_varargs_list_local, p_has_retval, err, local_symtab, cb_type, 0, 0, cur_code_body_, 0); /* restore the parser state */ cur_code_body_ = old_cur_code_body; local_symtab_ = old_local_symtab; enclosing_local_symtab_ = old_enclosing_local_symtab; enclosing_stm_ = old_enclosing_stm; goto_symtab_ = old_goto_symtab; local_cnt_ = old_local_cnt; max_local_cnt_ = old_max_local_cnt; has_local_ctx_ = old_has_local_ctx; local_ctx_var_num_ = old_local_ctx_var_num; ctx_var_props_used_ = old_ctx_var_props_used; next_ctx_arr_idx_ = old_next_ctx_arr_idx; self_valid_ = old_self_valid; self_referenced_ = old_self_referenced; full_method_ctx_referenced_= old_full_method_ctx_referenced; local_ctx_needs_self_ = old_local_ctx_needs_self; local_ctx_needs_full_method_ctx_ = old_local_ctx_needs_full_method_ctx; /* return the code body we parsed */ return code_body; } /* ------------------------------------------------------------------------ */ /* * Parse a function or method body * * op_args is non-zero if this is an operator overload property. This * encodes the possible usages of the operator: bit 0x01 is set if this can * be a unary operator, bit 0x02 if it can be binary, and bit 0x4 is set if * it can be trinary. Most operators have only one bit set, but some have * multiple uses, such as "-" which can be unary (-3) or binary (5-2). The * number of arguments is always one less than the number of operands, * because 'self' is always the primary operand (the sole operand of a * unary, the left operand of a binary, or the leftmost operand of a * trinary). */ CTPNCodeBody *CTcParser::parse_code_body( int eq_before_brace, int is_obj_prop, int self_valid, int *p_argc, int *p_opt_argc, int *p_varargs, int *p_varargs_list, CTcSymLocal **p_varargs_list_local, int *p_has_retval, int *err, CTcPrsSymtab *local_symtab, tcprs_codebodytype cb_type, struct propset_def *propset_stack, int propset_depth, CTcCodeBodyRef *enclosing_code_body, CTcFormalTypeList **type_list) { /* * create a new code body reference - this will let nested code bodies * refer back to the code body object we're about to parse, even though * we won't create the actual code body object until we're done parsing * the entire code body */ cur_code_body_ = new (G_prsmem) CTcCodeBodyRef(); /* note if we're parsing some kind of anonymous function */ int parsing_anon_fn = (cb_type == TCPRS_CB_ANON_FN || cb_type == TCPRS_CB_SHORT_ANON_FN); /* remember the 'self' validity */ self_valid_ = self_valid; /* presume we will not need a local variable context object */ clear_local_ctx(); /* * Set the outer local symbol table. If the caller has provided us * with an explicit pre-constructed local symbol table, use that; * otherwise, use the global symbol table, since we have no locals * of our own yet. */ local_symtab_ = (local_symtab == 0 ? global_symtab_ : local_symtab); enclosing_local_symtab_ = (local_symtab_->get_parent() == 0 ? global_symtab_ : local_symtab_->get_parent()); /* there's no enclosing statement yet */ enclosing_stm_ = 0; /* * defer creating a 'goto' symbol table until we encounter a label * or a 'goto' */ goto_symtab_ = 0; /* no locals yet */ local_cnt_ = 0; max_local_cnt_ = 0; /* no formals yet */ int formal_num = 0; int opt_formal_num = 0; int varargs = FALSE; int varargs_list = FALSE; CTcSymLocal *varargs_list_local = 0; /* we haven't built our statement yet */ CTPNStmComp *stm = 0; long start_line = 0; CTcTokFileDesc *start_desc = 0; /* check for a short anonymous function, which uses unusual syntax */ if (cb_type == TCPRS_CB_SHORT_ANON_FN) { /* we're at the opening brace now */ G_tok->get_last_pos(&start_desc, &start_line); /* * a short-form anonymous function always has an argument list, * but it uses special notation: the argument list is simply the * first thing after the function's open brace, and ends with a * colon */ parse_formal_list(FALSE, TRUE, &formal_num, &opt_formal_num, &varargs, &varargs_list, &varargs_list_local, err, 0, TRUE, 0); if (*err) return 0; /* * The contents of a short-form anonymous function are simply an * expression, whose value is implicitly returned by the function. * Alternatively, it can start with a list of "local" clauses, to * define local variables. */ CTcPrsNode *expr = 0; while (G_tok->cur() == TOKT_LOCAL) { /* we need a variable name symbol */ if (G_tok->next() == TOKT_SYM) { /* add the symbol */ CTcSymLocal *lcl = local_symtab_->add_local(alloc_local()); /* check for an initializer */ CTcPrsNode *subexpr = 0; if (G_tok->next() == TOKT_ASI || G_tok->cur() == TOKT_EQ) { /* parse the initializer */ subexpr = parse_local_initializer(lcl, err); /* combine it with the expression we had so far */ expr = (expr == 0 ? subexpr : new CTPNComma(expr, subexpr)); } /* if there's a comma, skip it */ if (G_tok->cur() == TOKT_COMMA) G_tok->next(); } else { /* invalid 'local' syntax */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); } } /* if there's anything left, parse the simple expression */ if (expr == 0 || G_tok->cur() != TOKT_RBRACE) { /* parse the rest of the expression */ CTcPrsNode *subexpr = parse_expr_or_dstr(TRUE); /* combine it with the local initializers, if any */ expr = (expr == 0 ? subexpr : new CTPNComma(expr, subexpr)); } /* * The next token must be the closing brace ('}') of the function. * If the next token is a semicolon, it's an error, but it's * probably just a superfluous semicolon that we can ignore. */ if (G_tok->cur() == TOKT_SEM) { /* log an error explaining the problem */ G_tok->log_error(TCERR_SEM_IN_SHORT_ANON_FN); /* skip the semicolon */ G_tok->next(); } /* check for the brace */ switch (G_tok->cur()) { case TOKT_RBRACE: /* this is what we want - skip it and continue */ G_tok->next(); break; case TOKT_EOF: /* log an error and give up */ G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE); *err = 1; return 0; default: /* log an error, assuming they simply forgot the '}' */ G_tok->log_error_curtok(TCERR_SHORT_ANON_FN_REQ_RBRACE); break; } /* * This anonymous function syntax implicitly returns the value of * the expression, so generate a 'return' statement node that * returns the expression. If the expression has no return value, * we're simply evaluating it for side-effects, so wrap it in a * simple 'expression' statement. */ CTPNStm *ret_stm; if (expr->has_return_value()) ret_stm = new CTPNStmReturn(expr); else ret_stm = new CTPNStmExpr(expr); /* put the 'return' statement inside a compound statement */ stm = new CTPNStmComp(ret_stm, local_symtab_); } else { /* * If we have a propertyset stack, set up an inserted token stream * with the expanded token list for the formals, combining the * formals from the enclosing propertyset definitions with the * formals defined here. */ if (propset_depth != 0) insert_propset_expansion(propset_stack, propset_depth); /* * if we have an explicit left parenthesis, or an implied formal * list from an enclosing propertyset, parse the list */ if (G_tok->cur() == TOKT_LPAR) { /* skip the open paren */ G_tok->next(); /* * Parse the formal list. Add the symbols to the local * table (hence 'count_only' = false), and don't allow * optional arguments. */ parse_formal_list(FALSE, TRUE, &formal_num, &opt_formal_num, &varargs, &varargs_list, &varargs_list_local, err, 0, FALSE, type_list); if (*err) return 0; } /* parse an equals sign, if present */ if (G_tok->cur() == TOKT_EQ) { /* * An equals sign after a formal parameter list can be used if * the 'eq_before_brace' flag is set. Otherwise, if we're * defining an object property, this is an error, since it's * obsolete TADS 2 syntax that we no longer allow - because * this is a change in syntax, we want to catch it * specifically so we can provide good diagnostic information * for it. */ if (eq_before_brace && G_tok->cur() == TOKT_EQ) { /* it's allowed - skip the '=' */ G_tok->next(); } else if (is_obj_prop) { /* obsolete tads 2 syntax - flag the error */ G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE); /* * skip the '=' so we can continue parsing the rest of the * code body without cascading errors */ G_tok->next(); } else { /* * it's not a situation where we allow '=' specifically, * or where we know why it might be present erroneously - * let it go for now, as we'll flag the error in the * normal compound statement parsing */ } } /* check for '(' syntax */ //$$$ /* require the '{' */ switch (G_tok->cur()) { case TOKT_LBRACE: parse_body: /* note the location of the opening brace */ G_tok->get_last_pos(&start_desc, &start_line); /* parse the compound statement */ stm = parse_compound(err, TRUE, TRUE, 0, TRUE); break; case TOKT_SEM: case TOKT_RBRACE: /* * we seem to have found the end of the object definition, or * the end of a code body - treat it as an empty code body */ G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE); stm = new CTPNStmComp(0, 0); break; default: /* * the '{' was missing - log an error, but proceed from the * current token on the assumption that they merely left out * the open brace */ G_tok->log_error_curtok(TCERR_REQ_LBRACE_CODE); goto parse_body; } } /* if that failed, return the error */ if (*err || stm == 0) return 0; /* * determine how the statement exits, and generate any internal flow * warnings within the body code */ unsigned long flow_flags = stm->get_control_flow(TRUE); /* * Warn if the function has both explicit void and value returns. * If not, check to see if it continues; if so, it implicitly * returns a void value by falling off the end, so warn if it both * falls off the end and returns a value somewhere else. Suppress * this warning if this is a syntax check only. */ if (!G_prs->get_syntax_only()) { if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0 && (flow_flags & TCPRS_FLOW_RET_VOID) != 0) { /* it has explicit void and value returns */ stm->log_warning(TCERR_RET_VAL_AND_VOID); } else if ((flow_flags & TCPRS_FLOW_RET_VAL) != 0 && (flow_flags & TCPRS_FLOW_NEXT) != 0) { /* it has explicit value returns, and implicit void return */ stm->log_warning(TCERR_RET_VAL_AND_IMP_VOID); } } /* if the caller is interested, return the interface details */ if (p_argc != 0) *p_argc = formal_num; if (p_opt_argc != 0) *p_opt_argc = opt_formal_num; if (p_varargs != 0) *p_varargs = varargs; if (p_varargs_list != 0) *p_varargs_list = varargs_list; if (p_varargs_list_local != 0) *p_varargs_list_local = varargs_list_local; if (p_has_retval) *p_has_retval = ((flow_flags & TCPRS_FLOW_RET_VAL) != 0); /* create a code body node for the result */ CTPNCodeBody *body_stm = new CTPNCodeBody( local_symtab_, goto_symtab_, stm, formal_num, opt_formal_num, varargs, varargs_list, varargs_list_local, max_local_cnt_, self_valid, enclosing_code_body); /* store this new statement in the current code body reference object */ cur_code_body_->ptr = body_stm; /* save the starting location */ body_stm->set_start_location(start_desc, start_line); /* * set the end location in the new code body to the end location in * the underlying compound statement */ body_stm->set_end_location(stm->get_end_desc(), stm->get_end_linenum()); /* set up the local context for access to enclosing scope locals */ finish_local_ctx(body_stm, local_symtab); /* * if 'self' is valid, and we're parsing an anonymous function, and we * have any references in this code body to any method context * variables (self, targetprop, targetobj, definingobj), make certain * that the code body has a context at level 1, so that it can pick up * our method context */ if (self_valid && parsing_anon_fn && (self_referenced_ || full_method_ctx_referenced_)) body_stm->get_or_add_ctx_var_for_level(1); /* mark the code body for references to the method context */ body_stm->set_self_referenced(self_referenced_); body_stm->set_full_method_ctx_referenced(full_method_ctx_referenced_); /* * mark the code body for inclusion in any local context of the method * context */ body_stm->set_local_ctx_needs_self(local_ctx_needs_self_); body_stm->set_local_ctx_needs_full_method_ctx( local_ctx_needs_full_method_ctx_); /* return the new body statement */ return body_stm; } /* * Parse a compound statement */ CTPNStmComp *CTcParser::parse_compound( int *err, int skip_lbrace, int need_rbrace, CTPNStmSwitch *enclosing_switch, int use_enclosing_scope) { /* save the current line information for later */ CTcTokFileDesc *file; long linenum; G_tok->get_last_pos(&file, &linenum); /* skip the '{' if we're on one and the caller wants us to */ if (skip_lbrace && G_tok->cur() == TOKT_LBRACE) G_tok->next(); /* enter a scope */ tcprs_scope_t scope_data; if (!use_enclosing_scope) enter_scope(&scope_data); /* we don't have any statements in our sublist yet */ CTPNStm *first_stm = 0; CTPNStm *last_stm = 0; CTPNStm *cur_stm = 0; /* presume we won't find the closing brace */ int skip_rbrace = FALSE; /* keep going until we reach the closing '}' */ for (int done = FALSE ; !done ; ) { /* check what we've found */ switch (G_tok->cur()) { case TOKT_RBRACE: /* it's our closing brace - we're done */ done = TRUE; cur_stm = 0; /* note that we must still skip the closing brace */ skip_rbrace = TRUE; /* stop scanning statements */ break; case TOKT_EOF: /* * if we're at end of file, and we don't need a right brace to * end the block, consider this the end of the block */ if (!need_rbrace) { done = TRUE; cur_stm = 0; skip_rbrace = FALSE; break; } else { /* it's an error */ G_tok->log_error(TCERR_EOF_IN_CODE); cur_stm = 0; done = TRUE; break; } break; default: /* parse a statement */ cur_stm = parse_stm(err, enclosing_switch, FALSE); /* if an error occurred, stop parsing */ if (*err) done = TRUE; break; } /* if we parsed a statement, add it to our list */ if (cur_stm != 0) { /* link the statement at the end of our list */ if (last_stm != 0) last_stm->set_next_stm(cur_stm); else first_stm = cur_stm; last_stm = cur_stm; } } /* if there's no statement, make the body a null statement */ if (first_stm == 0) first_stm = new CTPNStmNull(); /* build the compound statement node */ CTPNStmComp *comp_stm = new CTPNStmComp(first_stm, local_symtab_); /* set some additional information if we created a statement */ if (comp_stm != 0) { /* set the statement's line to the start of the compound */ comp_stm->set_source_pos(file, linenum); /* note whether or not we have our own private scope */ comp_stm->set_has_own_scope(!use_enclosing_scope && (local_symtab_ != scope_data.local_symtab)); } /* if necessary, skip the closing brace */ if (skip_rbrace) G_tok->next(); /* leave the local scope */ if (!use_enclosing_scope) leave_scope(&scope_data); /* return the compound statement object */ return comp_stm; } /* * Create a local symbol table for the current scope, if necessary */ void CTcParser::create_scope_local_symtab() { /* * if our symbol table is the same as the enclosing symbol table, we * must create our own table */ if (local_symtab_ == enclosing_local_symtab_) { /* * Create our own local symbol table, replacing the current one * - we saved the enclosing one already when we entered the * scope, so we'll restore it on our way out. The new local * symbol table has the enclosing symbol table as its parent * scope. */ local_symtab_ = new CTcPrsSymtab(local_symtab_); } } /* * Parse a local variable definition */ CTPNStm *CTcParser::parse_local(int *err) { int done; CTPNStm *first_stm; CTPNStm *last_stm; /* we have no initializer statements yet */ first_stm = last_stm = 0; /* skip the 'local' keyword */ G_tok->next(); /* keep going until we reach the closing semicolon */ for (done = FALSE ; !done ; ) { /* we need a symbol name */ if (G_tok->cur() == TOKT_SYM) { const char *sym; size_t symlen; CTcSymLocal *lcl; CTPNStm *stm; CTcPrsNode *expr; /* get the symbol string from the token */ sym = G_tok->getcur()->get_text(); symlen = G_tok->getcur()->get_text_len(); /* add the new local variable to our symbol table */ lcl = local_symtab_->add_local(sym, symlen, alloc_local(), FALSE, FALSE, FALSE); /* skip the symbol and check for an initial value assignment */ switch (G_tok->next()) { case TOKT_EQ: case TOKT_ASI: /* parse the initializer */ expr = parse_local_initializer(lcl, err); /* if we didn't get a statement, we can't proceed */ if (expr == 0) { done = TRUE; break; } /* create a statement for the assignment */ stm = new CTPNStmExpr(expr); /* * set the statement's source location according to the * current source location - if we have multiple * initializers over several lines, this will allow the * debugger to step through the individual * initializations */ stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* add the statement to our list */ if (last_stm != 0) last_stm->set_next_stm(stm); else first_stm = stm; last_stm = stm; /* done */ break; default: /* there's nothing more to do with this variable */ break; } /* * check what follows - we can have a comma to introduce * another local variable, or a semicolon to end the * statement */ switch(G_tok->cur()) { case TOKT_COMMA: /* skip the comma and go on to the next variable */ G_tok->next(); break; case TOKT_SEM: /* skip the semicolon, and stop scanning */ G_tok->next(); done = TRUE; break; case TOKT_SYM: /* * they probably just left out a comma - assume the * comma is there and keep going */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA); break; default: /* * these almost certainly indicate that they left out a * semicolon - report the error and continue from here */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_COMMA); done = TRUE; break; } } else { /* symbol required - log the error */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); /* determine how to proceed based on what we have */ switch(G_tok->cur()) { case TOKT_COMMA: /* * they probably just put in an extra comma - skip it * and keep trying to parse the local list */ G_tok->next(); break; case TOKT_SEM: /* that's the end of the statement */ G_tok->next(); done = TRUE; break; case TOKT_EOF: /* set the error flag and stop scanning */ *err = TRUE; done = TRUE; break; default: /* try skipping this token and trying again */ G_tok->next(); break; } } } /* * if we have one statement, return it; if we have more than one, * return a compound statement to contain the list; if we have * nothing, return nothing */ if (first_stm == 0) return 0; else if (first_stm == last_stm) return first_stm; else return new CTPNStmComp(first_stm, local_symtab_); } /* * Parse a local variable initializer */ CTcPrsNode *CTcParser::parse_local_initializer(CTcSymLocal *lcl, int *err) { /* * skip the assignment operator and parse the expression (which * cannot use the comma operator) */ G_tok->next(); CTcPrsNode *expr = parse_asi_expr(); /* if that failed, return failure */ if (expr == 0) return 0; /* * if we have a valid local, return a new expression node for the * assignment; otherwise just return the expression, since we have * nothing to assign to */ return (lcl != 0 ? new CTPNAsi(new CTPNSymResolved(lcl), expr) : expr); } /* * Parse a statement */ CTPNStm *CTcParser::parse_stm(int *err, CTPNStmSwitch *enclosing_switch, int compound_use_enclosing_scope) { CTcToken tok; /* * remember where the statement starts - when we create the * statement object, it will refer to these values to set its * internal memory of the statement's source file location */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* see what we have */ try_again: switch(G_tok->cur()) { case TOKT_EOF: /* unexpected end of file - log an error */ G_tok->log_error(TCERR_EOF_IN_CODE); /* set the caller's error flag */ *err = TRUE; /* there's no statement to return, obviously */ return 0; case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_RBRACE: /* * we shouldn't be looking at any of these at the start of a * statement */ G_tok->log_error_curtok(TCERR_EXPECTED_STMT_START); G_tok->next(); return 0; case TOKT_SEM: /* * null statement - this doesn't generate any code; simply skip * the semicolon and keep going */ G_tok->next(); /* this doesn't generate any code */ return 0; case TOKT_LOCAL: /* if we don't have our own local symbol table, create one */ create_scope_local_symtab(); /* parse the local variable definition and return the result */ return parse_local(err); case TOKT_LBRACE: /* it's a compound statement */ return parse_compound(err, TRUE, TRUE, 0, compound_use_enclosing_scope); case TOKT_IF: /* parse an if statement */ return parse_if(err); case TOKT_RETURN: /* parse a return statement */ return parse_return(err); case TOKT_FOR: /* parse a for statement */ return parse_for(err); case TOKT_FOREACH: /* parse a foreach statement */ return parse_foreach(err); case TOKT_WHILE: /* parse a while statement */ return parse_while(err); case TOKT_DO: /* parse a do-while */ return parse_do_while(err); case TOKT_SWITCH: /* parse a switch */ return parse_switch(err); case TOKT_GOTO: /* parse a 'goto' */ return parse_goto(err); case TOKT_BREAK: return parse_break(err); case TOKT_CONTINUE: return parse_continue(err); case TOKT_TRY: return parse_try(err); case TOKT_THROW: return parse_throw(err); case TOKT_CATCH: /* misplaced 'catch' clause - log an error */ G_tok->log_error(TCERR_MISPLACED_CATCH); /* * skip the following open paren, class name, variable name, and * closing paren, as long as we find all of these */ if (G_tok->next() == TOKT_LPAR && G_tok->next() == TOKT_SYM && G_tok->next() == TOKT_SYM && G_tok->next() == TOKT_RPAR) G_tok->next(); /* there's no valid statement to return */ return 0; case TOKT_FINALLY: /* misplaced 'finally' clause - log an error */ G_tok->log_error(TCERR_MISPLACED_FINALLY); /* skip the 'finally' keyword, and return failure */ G_tok->next(); return 0; case TOKT_ELSE: /* * misplaced 'else' clause - log an error, skip the 'else' * keyword, and proceed with what follows */ G_tok->log_error(TCERR_MISPLACED_ELSE); G_tok->next(); return 0; case TOKT_CASE: /* * if we're in a 'switch', it's a valid 'case' label; otherwise, * it's misplaced */ if (enclosing_switch != 0) { /* parse the 'case' label */ return parse_case(err, enclosing_switch); } else { /* * not directly within a 'switch', so this is a misplaced * 'case' keyword - log an error */ G_tok->log_error(TCERR_MISPLACED_CASE); /* skip the 'case' keyword */ G_tok->next(); /* assume there's an expression here, and skip that as well */ parse_expr(); /* if there's a colon, skip it, too */ if (G_tok->cur() == TOKT_COLON) G_tok->next(); /* proceed from here */ return 0; } case TOKT_DEFAULT: /* allow this only if we're directly in a 'switch' body */ if (enclosing_switch != 0) { /* parse the 'default' label */ return parse_default(err, enclosing_switch); } else { /* misplaced 'default' keyword - log an error */ G_tok->log_error(TCERR_MISPLACED_DEFAULT); /* skip the 'default' keyword; if there's a colon, skip it, too */ if (G_tok->next() == TOKT_COLON) G_tok->next(); /* proceed from here */ return 0; } case TOKT_SYM: /* * It's a symbol. First, check for a label. This requires that * we look ahead one token, because we have to look at the next * token to see if it's a colon; if it's not, we have to back up * and parse the symbol as the start of an expression. So, * remember the current symbol token, then look at what follows. */ tok = *G_tok->copycur(); if (G_tok->next() == TOKT_COLON) { CTPNStmEnclosing *old_enclosing; CTPNStmLabel *label_stm; CTcSymLabel *lbl; CTPNStm *stm; /* it's a label - create a symbol table entry for it */ lbl = add_code_label(&tok); /* create the labeled statement node */ label_stm = new CTPNStmLabel(lbl, enclosing_stm_); /* skip the colon */ G_tok->next(); /* * set our new label to be the enclosing label for * everything contained within its statement */ old_enclosing = set_enclosing_stm(label_stm); /* parse the labeled statement */ stm = parse_stm(err, enclosing_switch, FALSE); /* restore our enclosing statement */ set_enclosing_stm(old_enclosing); /* if parsing the labeled statement failed, give up */ if (*err) return 0; /* connect to the label to the statement it labels */ label_stm->set_stm(stm); /* point the label symbol to its statement node */ if (lbl != 0) lbl->set_stm(label_stm); /* return the labeled statement node */ return label_stm; } /* * it's not a label - push the colon back into the input stream * so that we read it again, then parse this as an ordinary * expression */ G_tok->unget(); goto do_parse_expr; case TOKT_RPAR: /* * they probably had too many close parens in something like a * 'for' or 'if' statement - flag the error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* skip the extra paren and go back for another try */ G_tok->next(); goto try_again; default: do_parse_expr: /* anything else must be the start of an expression */ { /* parse the expression */ CTcPrsNode *expr = parse_expr_or_dstr(TRUE); /* the statement must be terminated with a semicolon */ if (parse_req_sem()) { /* set the error flag */ *err = TRUE; /* there's no statement to return */ return 0; } /* * if we successfully parsed an expression, create a * statement node for the expression; if expr is null, the * expression parser will already have issued an error, so * we can simply ignore the failed expression and continue * to the next statement */ if (expr != 0) return new CTPNStmExpr(expr); else return 0; } } } /* * Add a 'goto' label symbol to the current code body */ CTcSymLabel *CTcParser::add_code_label(const CTcToken *tok) { /* if there's no 'goto' symbol table, create one */ if (goto_symtab_ == 0) goto_symtab_ = new CTcPrsSymtab(0); /* create the label and return it */ return goto_symtab_->add_code_label(tok->get_text(), tok->get_text_len(), FALSE); } /* * Parse an 'if' statement */ CTPNStm *CTcParser::parse_if(int *err) { CTcPrsNode *cond_expr; CTPNStm *if_stm; CTPNStm *then_stm; CTPNStm *else_stm; CTcTokFileDesc *file; long linenum; /* save the starting line information for later */ G_tok->get_last_pos(&file, &linenum); /* skip the 'if' keyword, and require the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the left paren */ G_tok->next(); } else { /* * log an error, but proceed on the assumption that they simply * left out the paren */ G_tok->log_error_curtok(TCERR_REQ_LPAR_IF); } /* parse the expression */ cond_expr = parse_cond_expr(); /* if that failed, return failure */ if (cond_expr == 0) { *err = TRUE; return 0; } /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip it */ G_tok->next(); } else { /* * log an error, then proceed assuming that the paren was merely * omitted */ G_tok->log_error_curtok(TCERR_REQ_RPAR_IF); } /* parse the true-part statement */ then_stm = parse_stm(err, 0, FALSE); /* if an error occurred, return failure */ if (*err) return 0; /* check for 'else' */ if (G_tok->cur() == TOKT_ELSE) { /* skip the 'else' keyword */ G_tok->next(); /* parse the false-part statement */ else_stm = parse_stm(err, 0, FALSE); /* if an error occurred, return failure */ if (*err) return 0; } else { /* there's no 'else' part */ else_stm = 0; } /* create and return the 'if' statement node */ if_stm = new CTPNStmIf(cond_expr, then_stm, else_stm); /* set the original statement position in the node */ if_stm->set_source_pos(file, linenum); /* return the 'if' statement node */ return if_stm; } /* * Parse a 'return' statement */ CTPNStm *CTcParser::parse_return(int *err) { CTPNStm *stm; /* skip the 'return' keyword and see what we have */ switch(G_tok->next()) { case TOKT_SEM: /* * end of the statement - this is a void return; skip the * semicolon, and return a void return statement node */ G_tok->next(); stm = new CTPNStmReturn(0); break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they probably just left out a semicolon - flag the error, and * continue parsing from this token */ G_tok->log_error_curtok(TCERR_RET_REQ_EXPR); return 0; default: /* it's a return with an expression - parse the expression */ stm = new CTPNStmReturn(parse_expr()); /* make sure we're on a semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* done */ break; } /* return the statement node we created */ return stm; } /* * Parse a 'for' statement */ CTPNStm *CTcParser::parse_for(int *err) { /* save the current line information for later */ CTcTokFileDesc *file; long linenum; G_tok->get_last_pos(&file, &linenum); /* * enter a scope, in case we create a local symbol table for local * variables defined within the 'for' statement */ tcprs_scope_t scope_data; enter_scope(&scope_data); /* parse the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip it */ G_tok->next(); } else { /* log an error, and proceed, assuming it was simply left out */ G_tok->log_error_curtok(TCERR_REQ_FOR_LPAR); } /* we don't have any of the expressions yet */ CTcPrsNode *init_expr = 0; CTcPrsNode *cond_expr = 0; CTcPrsNode *reinit_expr = 0; int found_in = FALSE; /* "in" expression list */ CTPNForIn *in_head = 0, *in_tail = 0; /* parse the initializer list */ for (int done = FALSE ; !done ; ) { /* presume we won't find an expression on this round */ CTcPrsNode *expr = 0; /* check what we have */ switch(G_tok->cur()) { case TOKT_LOCAL: /* * if we haven't created our own symbol table local to the * 'for' loop, do so now */ create_scope_local_symtab(); /* skip the 'local' keyword and get the local name */ if (G_tok->next() == TOKT_SYM) { /* add the local symbol */ CTcSymLocal *lcl = local_symtab_->add_local(alloc_local()); /* check for the required initializer */ switch(G_tok->next()) { case TOKT_ASI: case TOKT_EQ: /* parse the initializer */ expr = parse_local_initializer(lcl, err); break; case TOKT_SYM: /* check for an 'in' expression */ if (G_tok->getcur()->text_matches("in", 2)) { /* parse the 'var in expr' or 'var in from..to' */ G_tok->next(); expr = parse_for_in_clause( new CTPNSymResolved(lcl), in_head, in_tail); /* note the "in" */ found_in = TRUE; /* if that failed, stop scanning the initializer */ if (expr == 0) done = TRUE; } else { /* anything else is an error */ goto req_asi; } break; default: req_asi: /* log an error - an initializer is required */ G_tok->log_error_curtok(TCERR_REQ_FOR_LOCAL_INIT); break; } } else { /* * the 'local' statement isn't constructed properly - * this is difficult to recover from intelligently, so * just log an error and keep going from here */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); break; } break; case TOKT_SEM: /* it's a semicolon - we're done with the initializer list */ done = TRUE; /* * if we have an expression already, it means that the * previous token was a comma - this is an error, since we * have a missing expression; log the error but continue * anyway */ if (init_expr != 0) G_tok->log_error(TCERR_MISSING_FOR_INIT_EXPR); break; case TOKT_RPAR: /* if we found an "in", we can end early */ if (found_in) { done = TRUE; break; } /* otherwise, fall through to the missing part error... */ /* FALL THROUGH */ case TOKT_LBRACE: case TOKT_RBRACE: /* premature end of the list - log an error and stop */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); done = TRUE; break; default: /* * This must be an expression - parse it. Parse an * assignment expression, not a comma expression, because we * must check for a "local" clause after each comma. */ expr = parse_asi_expr(); /* if that failed, stop scanning the "for" */ if (expr == 0) done = TRUE; /* check for an 'in' expression */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* skip the "in" and parse the collection/range */ G_tok->next(); expr = parse_for_in_clause(expr, in_head, in_tail); /* note the "in" */ found_in = TRUE; /* if that failed, stop parsing the initializer */ if (expr == 0) done = TRUE; } /* done with this clause */ break; } /* * if we got an expression, add it into the initializer * expression under construction by adding it under a "comma" * node */ if (expr != 0) { /* * if there's an expression, build a comma expression for * the expression so far plus the new expression; otherwise, * the new expression becomes the entire expression so far */ if (init_expr != 0) init_expr = new CTPNComma(init_expr, expr); else init_expr = expr; } /* if we're done, we can stop now */ if (done) break; /* * we must have a semicolon or comma after each initializer * expression */ switch(G_tok->cur()) { case TOKT_SEM: /* that's the end of the statement - stop now */ done = TRUE; break; case TOKT_COMMA: /* skip the comma and parse the next initializer */ G_tok->next(); break; case TOKT_RPAR: /* if we found an "in" clause, we only need an initializer */ if (found_in) { done = TRUE; break; } /* otherwise, fall through to the error case... */ /* FALL THROUGH */ case TOKT_LBRACE: case TOKT_RBRACE: /* log an error, and stop parsing the expression list */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); done = TRUE; break; default: /* log an error */ G_tok->log_error_curtok(TCERR_REQ_FOR_INIT_COMMA); /* skip the errant token and keep going */ G_tok->next(); break; } } /* * if we successfully found the ';' at the end of the initializer * list, parse the condition expression */ if (G_tok->cur() == TOKT_SEM) { int cont_to_reinit; /* presume we'll want to continue to the reinit expression */ cont_to_reinit = TRUE; /* skip the ';' */ G_tok->next(); /* if the condition isn't empty, parse it */ if (G_tok->cur() != TOKT_SEM) cond_expr = parse_cond_expr(); /* require the ';' after the expression */ switch(G_tok->cur()) { case TOKT_SEM: /* it's fine - keep going from here */ G_tok->next(); break; case TOKT_RPAR: case TOKT_LBRACE: case TOKT_RBRACE: /* missing part */ G_tok->log_error_curtok(TCERR_MISSING_FOR_PART); /* don't bother trying to find a reinitialization expression */ cont_to_reinit = FALSE; break; default: /* * we seem to be missing the semicolon; keep going from * here, assuming that they simply left out the semicolon * between the condition and reinitializer expressions */ G_tok->log_error_curtok(TCERR_REQ_FOR_COND_SEM); break; } /* * if we're to continue to the reinitializer, parse it; there is * no reinitialization expression if the next token is a right * paren */ if (cont_to_reinit && G_tok->cur() != TOKT_RPAR) { /* parse the expression */ reinit_expr = parse_expr(); } /* make sure we have the right paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and try parsing the body from here, on the * assumption that they simply forgot about the right paren * and jumped right into the body */ G_tok->log_error_curtok(TCERR_REQ_FOR_RPAR); } } else if (G_tok->cur() == TOKT_RPAR) { /* * We already found the right paren - early, so we logged an error * if necessary (it's an error in most cases, but *not* if there's * an 'in' clause in the initializer list). Simply skip it now so * that we can proceed to the body of the 'for'. */ G_tok->next(); } /* create the "for" node */ CTPNStmFor *for_stm = new CTPNStmFor( init_expr, cond_expr, reinit_expr, in_head, local_symtab_, enclosing_stm_); /* set the 'for' to enclose its body */ CTPNStmEnclosing *old_enclosing = set_enclosing_stm(for_stm); /* parse the body of the "for" loop */ CTPNStm *body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* if that failed, return failure */ if (*err) return 0; /* set the body of the 'for' */ for_stm->set_body(body_stm); /* set the original statement position in the node */ for_stm->set_source_pos(file, linenum); /* set the own-scope flag */ for_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab); /* exit any local scope we created */ leave_scope(&scope_data); /* return the "for" node */ return for_stm; } /* * Parse a for..in clause. This parses the part after the "in"; we accept * either a simple expression, or a range (expr .. expr). We return the * expression node representing the clause. It's up to the caller to look * up the local variable before the "in" and pass it to us. */ CTcPrsNode *CTcParser::parse_for_in_clause( CTcPrsNode *lval, CTPNForIn *&head, CTPNForIn *&tail) { /* * Parse an assignment expression. We parse from this point in the * grammar because the entire 'in' clause can be part of a comma * expression, so we want to stop if we reach a comma rather than * treating the comma as part of our own expression. */ CTcPrsNode *expr = parse_asi_expr(); /* if that failed, return failure */ if (expr == 0) return 0; /* if the next token is "..", we have a "for var in from..to" range */ CTPNForIn *in_expr; if (G_tok->cur() == TOKT_DOTDOT) { /* skip the ".." and parse the "to" expression */ G_tok->next(); CTcPrsNode *toExpr = parse_asi_expr(); /* if that failed, return failure */ if (toExpr == 0) return 0; /* check for the optional "step" expression */ CTcPrsNode *stepExpr = 0; if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("step", 4)) { /* skip the "step" and parse the expression */ G_tok->next(); stepExpr = parse_asi_expr(); } /* * allocate variables for the "to" and "step" expressions, but only * if they're non-constant */ int to_local = (toExpr->is_const() ? -1 : alloc_local()); int step_local = (stepExpr == 0 || stepExpr->is_const() ? -1 : alloc_local()); /* build the "for x in range" node */ in_expr = new CTPNVarInRange(lval, expr, toExpr, stepExpr, to_local, step_local); } else { /* it's a plain old "for x in collection" */ in_expr = new CTPNVarIn(lval, expr, alloc_local()); } /* link it into the list */ if (tail != 0) tail->setnxt(in_expr); else head = in_expr; tail = in_expr; /* return the new expression */ return in_expr; } /* * 'for var in expr' - fold constants */ CTcPrsNode *CTPNVarInBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each element */ lval_ = lval_->fold_constants(symtab); expr_ = expr_->fold_constants(symtab); /* we can't fold the overall 'in' expression itself */ return this; } /* * 'for var in from .. to' - fold constants */ CTcPrsNode *CTPNVarInRangeBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each element */ lval_ = lval_->fold_constants(symtab); from_expr_ = from_expr_->fold_constants(symtab); to_expr_ = to_expr_->fold_constants(symtab); if (step_expr_ != 0) step_expr_ = step_expr_->fold_constants(symtab); /* we can't fold the overall 'in' expression itself */ return this; } /* * Parse a 'foreach' statement */ CTPNStm *CTcParser::parse_foreach(int *err) { tcprs_scope_t scope_data; CTcPrsNode *iter_expr; CTcPrsNode *coll_expr; CTPNStm *body_stm; CTPNStmForeach *foreach_stm; CTcTokFileDesc *file; long linenum; CTPNStmEnclosing *old_enclosing; /* save the current line information for later */ G_tok->get_last_pos(&file, &linenum); /* * enter a scope, in case we create a local symbol table for local * variables defined within the 'for' statement */ enter_scope(&scope_data); /* parse the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip it */ G_tok->next(); } else { /* log an error, and proceed, assuming it was simply left out */ G_tok->log_error_curtok(TCERR_REQ_FOREACH_LPAR); } /* we don't have the iterator lvalue or collection expression yet */ iter_expr = 0; coll_expr = 0; /* check for 'local' before the iteration variable */ switch (G_tok->cur()) { case TOKT_LOCAL: /* * if we haven't created our own symbol table local to the 'for' * loop, do so now */ create_scope_local_symtab(); /* skip the 'local' keyword and get the local name */ if (G_tok->next() == TOKT_SYM) { /* add the local symbol */ local_symtab_->add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), alloc_local(), FALSE, FALSE, FALSE); } else { /* log the error */ G_tok->log_error_curtok(TCERR_LOCAL_REQ_SYM); } /* go handle the local as the iteration expression */ goto do_expr; case TOKT_LPAR: case TOKT_SYM: do_expr: /* parse the iterator lvalue expression */ iter_expr = parse_expr(); if (iter_expr == 0) { *err = TRUE; return 0; } break; default: /* premature end of the list - log an error and stop */ G_tok->log_error_curtok(TCERR_MISSING_FOREACH_EXPR); return 0; } /* require the 'in' keyword */ if (G_tok->cur() != TOKT_SYM || !G_tok->getcur()->text_matches("in", 2)) { /* log an error */ G_tok->log_error_curtok(TCERR_FOREACH_REQ_IN); /* see what we have */ switch(G_tok->cur()) { case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: case TOKT_EOF: /* probably end of statement */ return 0; case TOKT_RPAR: /* * probably an extra paren in the variable expression - skip * the paren and continue */ G_tok->next(); break; default: /* probably just left out 'in' - continue from here */ break; } } else { /* skip the 'in' */ G_tok->next(); } /* parse the collection expression */ coll_expr = parse_expr(); if (coll_expr == 0) { *err = TRUE; return 0; } /* make sure we have the close paren */ if (G_tok->cur() != TOKT_RPAR) { /* * log the error, but continue from here on the assumption that * they simply left out the paren */ G_tok->log_error_curtok(TCERR_REQ_FOREACH_RPAR); } else { /* skip the paren */ G_tok->next(); } /* * create the "foreach" node, allocating a private local variable * for holding the iterator object */ foreach_stm = new CTPNStmForeach(iter_expr, coll_expr, local_symtab_, enclosing_stm_, alloc_local()); /* set the "foreach" node to enclose its body */ old_enclosing = set_enclosing_stm(foreach_stm); /* parse the body of the loop */ body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* if that failed, return failure */ if (*err) return 0; /* set the body of the 'for' */ foreach_stm->set_body(body_stm); /* set the original statement position in the node */ foreach_stm->set_source_pos(file, linenum); /* set the own-scope flag */ foreach_stm->set_has_own_scope(local_symtab_ != scope_data.local_symtab); /* exit any local scope we created */ leave_scope(&scope_data); /* return the new statement node */ return foreach_stm; } /* * Parse a 'break' statement */ CTPNStm *CTcParser::parse_break(int *err) { CTPNStmBreak *brk_stm; /* create the 'break' statement */ brk_stm = new CTPNStmBreak(); /* skip the 'break' keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* set the label in the statement */ brk_stm->set_label(G_tok->getcur()); /* skip the label token */ G_tok->next(); break; case TOKT_SEM: /* keep going - we'll skip it in a moment */ break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they almost certainly simply left off the semicolon - don't * bother with a "label expected" error, since the real error is * most likely just "missing semicolon" */ break; default: /* log the error */ G_tok->log_error_curtok(TCERR_BREAK_REQ_LABEL); break; } /* parse the required terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the 'break' node */ return brk_stm; } /* * Parse a 'continue' statement */ CTPNStm *CTcParser::parse_continue(int *err) { CTPNStmContinue *cont_stm; /* create the 'break' statement */ cont_stm = new CTPNStmContinue(); /* skip the 'continue' keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* set the label in the statement */ cont_stm->set_label(G_tok->getcur()); /* skip the label token */ G_tok->next(); break; case TOKT_SEM: /* keep going - we'll skip it in a moment */ break; case TOKT_LBRACE: case TOKT_RBRACE: /* * they almost certainly simply left off the semicolon - don't * bother with a "label expected" error, since the real error is * most likely just "missing semicolon" */ break; default: /* log the error */ G_tok->log_error_curtok(TCERR_CONT_REQ_LABEL); break; } /* parse the required terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the 'continue' node */ return cont_stm; } /* * Parse a 'while' statement */ CTPNStm *CTcParser::parse_while(int *err) { CTcPrsNode *expr; CTPNStm *body_stm; CTPNStmWhile *while_stm; CTPNStmEnclosing *old_enclosing; /* skip the 'while' and check for the open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and proceed on the assumption that the paren * was simply left out and the statement is otherwise * well-formed */ G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR); } /* parse the condition expression */ expr = parse_cond_expr(); if (expr == 0) { *err = TRUE; return 0; } /* create the 'while' statement node */ while_stm = new CTPNStmWhile(expr, enclosing_stm_); /* check for the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* log an error, and continue from here */ G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR); } /* set the 'while' to enclose its body */ old_enclosing = set_enclosing_stm(while_stm); /* parse the loop body */ body_stm = parse_stm(err, 0, FALSE); /* restore the old enclosing statement */ set_enclosing_stm(old_enclosing); /* give up on error */ if (*err) return 0; /* set the body */ while_stm->set_body(body_stm); /* that's it - build and return the 'while' node */ return while_stm; } /* * Parse a 'do-while' statement */ CTPNStm *CTcParser::parse_do_while(int *err) { CTPNStm *body_stm; CTcPrsNode *expr; CTPNStmDoWhile *do_stm; CTPNStmEnclosing *old_enclosing; /* create the statement object */ do_stm = new CTPNStmDoWhile(enclosing_stm_); /* skip the 'do' keyword */ G_tok->next(); /* set the 'do' to be the enclosing statement */ old_enclosing = set_enclosing_stm(do_stm); /* parse the loop body */ body_stm = parse_stm(err, 0, FALSE); /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* return on failure */ if (*err) return 0; /* require the 'while' keyword */ if (G_tok->cur() == TOKT_WHILE) { /* skip the 'while' */ G_tok->next(); } else { /* * no 'while' keyword - there's no obvious way to correct this, * so simply ignore the 'do' statement and keep going from here, * on the assumption that they inadvertantly started a new * statement without finishing the 'do' */ G_tok->log_error_curtok(TCERR_REQ_DO_WHILE); } /* require the open paren */ if (G_tok->cur() == TOKT_LPAR) G_tok->next(); else G_tok->log_error_curtok(TCERR_REQ_WHILE_LPAR); /* parse the expression */ expr = parse_cond_expr(); if (expr == 0) { *err = TRUE; return 0; } /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) G_tok->next(); else G_tok->log_error_curtok(TCERR_REQ_WHILE_RPAR); /* set the condition expression and body in the 'do' node */ do_stm->set_cond(expr); do_stm->set_body(body_stm); /* * remember the location of the 'while' part, since this part * generates code */ do_stm->set_while_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* parse the required closing semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the new 'do-while' node */ return do_stm; } /* * Parse a 'switch' statement */ CTPNStm *CTcParser::parse_switch(int *err) { CTcPrsNode *expr; CTPNStmSwitch *switch_stm; CTPNStm *body_stm; int skip; int unreachable_error_shown; CTPNStmEnclosing *old_enclosing; /* create the switch statement object */ switch_stm = new CTPNStmSwitch(enclosing_stm_); /* skip the 'switch' and check for the left paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the left paren */ G_tok->next(); } else { /* log an error, and assume the paren is simply missing */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_LPAR); } /* parse the controlling expression */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* set expression in the switch statement node */ switch_stm->set_expr(expr); /* check for and skip the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* the right paren is present - skip it */ G_tok->next(); } else { /* log an error, and keep going from here */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_RPAR); } /* check for and skip the brace */ if (G_tok->cur() == TOKT_LBRACE) { /* it's there - skip it */ G_tok->next(); } else { /* * log an error, and keep going on the assumption that the brace * is simply missing but the switch body is otherwise correct */ G_tok->log_error_curtok(TCERR_REQ_SWITCH_LBRACE); } /* * The first thing in the switch body must be a 'case', 'default', * or closing brace. Other statements preceding the first 'case' or * 'default' label within the switch body are not allowed, because * they would be unreachable. Keep skipping statements until we get * to one of these. */ for (skip = TRUE, unreachable_error_shown = FALSE ; skip ; ) { /* see what we have */ switch(G_tok->cur()) { case TOKT_CASE: case TOKT_DEFAULT: case TOKT_RBRACE: /* this is what we're looking for */ skip = FALSE; break; case TOKT_EOF: /* end of file within the switch - log an error */ G_tok->log_error(TCERR_EOF_IN_SWITCH); /* return failure */ *err = TRUE; return 0; default: /* * for anything else, log an error explaining that the code * is unreachable - do this only once, no matter how many * unreachable statements precede the first case label */ if (!unreachable_error_shown && !G_prs->get_syntax_only()) { /* show the error */ G_tok->log_error(TCERR_UNREACHABLE_CODE_IN_SWITCH); /* * note that we've shown the error, so we don't show it * again if more unreachable statements follow */ unreachable_error_shown = TRUE; } /* parse (and ignore) this statement */ parse_stm(err, switch_stm, FALSE); if (*err != 0) return 0; /* keep looking for the first label */ break; } } /* the 'switch' is the enclosing statement for children */ old_enclosing = set_enclosing_stm(switch_stm); /* parse the switch body */ body_stm = parse_compound(err, FALSE, TRUE, switch_stm, FALSE); /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* if we failed to parse the compound statement, give up */ if (*err) return 0; /* connect the switch to its body */ switch_stm->set_body(body_stm); /* return the switch statement node */ return switch_stm; } /* * Parse a 'case' label */ CTPNStm *CTcParser::parse_case(int *err, CTPNStmSwitch *enclosing_switch) { CTcPrsNode *expr; CTPNStm *stm; CTPNStmCase *case_stm; /* skip the 'case' keyword */ G_tok->next(); /* create the 'case' statement node */ case_stm = new CTPNStmCase(); /* parse the expression */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* store the expression in the case statement node */ case_stm->set_expr(expr); /* require the colon */ if (G_tok->cur() == TOKT_COLON) { /* skip the colon */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_REQ_CASE_COLON); } /* * parse the labeled statement - it's directly within this same * enclosing switch, because a case label doesn't create a new * expression nesting level (hence another 'case' label immediately * following without an intervening statement is perfectly valid) */ stm = parse_stm(err, enclosing_switch, FALSE); /* set the statement in the case node */ case_stm->set_stm(stm); /* count the 'case' label in the 'switch' node */ enclosing_switch->inc_case_cnt(); /* return the case node */ return case_stm; } /* * Parse a 'default' label */ CTPNStm *CTcParser::parse_default(int *err, CTPNStmSwitch *enclosing_switch) { CTPNStm *stm; CTPNStmDefault *default_stm; /* create the 'default' statement node */ default_stm = new CTPNStmDefault(); /* * if the enclosing 'switch' already has a 'default' label, it's an * error; continue anyway, since we still want to finish parsing the * syntax */ if (enclosing_switch->get_has_default()) G_tok->log_error(TCERR_DEFAULT_REDEF); /* mark the switch as having a 'default' case */ enclosing_switch->set_has_default(); /* skip the 'default' node, and require the colon */ if (G_tok->next() == TOKT_COLON) { /* skip the colon */ G_tok->next(); } else { /* * log an error, and keep going, assuming that the token was * accidentally omitted */ G_tok->log_error_curtok(TCERR_REQ_DEFAULT_COLON); } /* * parse the labeled statement - it's directly within this same * enclosing switch, because a 'default' label doesn't create a new * expression nesting level (hence another 'case' label immediately * following without an intervening statement is perfectly valid) */ stm = parse_stm(err, enclosing_switch, FALSE); /* set the statement in the 'default' node */ default_stm->set_stm(stm); /* return the 'default' node */ return default_stm; } /* * Parse a 'goto' statement */ CTPNStm *CTcParser::parse_goto(int *err) { CTPNStmGoto *goto_stm; /* skip the 'goto' keyword, and demand that a symbol follows */ if (G_tok->next() == TOKT_SYM) { /* create the parse node for the 'goto' statement */ goto_stm = new CTPNStmGoto(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol */ G_tok->next(); } else { /* log the error */ G_tok->log_error(TCERR_GOTO_REQ_LABEL); /* no statement */ goto_stm = 0; } /* parse the required semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the statement node */ return goto_stm; } /* * Parse a 'try-catch-finally' statement */ CTPNStm *CTcParser::parse_try(int *err) { CTPNStmTry *try_stm; CTPNStmEnclosing *old_enclosing; CTPNStm *body_stm; /* create the 'try' statement node */ try_stm = new CTPNStmTry(enclosing_stm_); /* * the 'try' is the enclosing statement for the duration of its * protected code block */ old_enclosing = set_enclosing_stm(try_stm); /* skip the 'try' */ G_tok->next(); /* parse the body of the 'try' block */ body_stm = parse_stm(err, 0, FALSE); /* restore the previous enclosing statement */ set_enclosing_stm(old_enclosing); /* if parsing the body failed, stop now */ if (*err) return 0; /* add the body to the 'try' */ try_stm->set_body_stm(body_stm); /* * check for 'catch' clauses - there could be several, so keep going * until we stop seeing 'catch' keywords */ while (G_tok->cur() == TOKT_CATCH) { int catch_has_err; CTPNStm *catch_body; CTPNStmCatch *catch_stm; tcprs_scope_t scope_data; /* create a local scope for the 'catch' clause */ enter_scope(&scope_data); create_scope_local_symtab(); /* create the 'catch' statement node */ catch_stm = new CTPNStmCatch(); /* * set the 'catch' clause's source position independently of the * overall 'try' statement, so that the debugger can track entry * into this clause */ catch_stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* presume we'll parse this successfully */ catch_has_err = FALSE; /* skip the 'catch' keyword and check for the left paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_REQ_CATCH_LPAR); } /* get the exception class token */ if (G_tok->cur() == TOKT_SYM) { /* set the class name in the 'catch' clause */ catch_stm->set_exc_class(G_tok->getcur()); /* move on */ G_tok->next(); } else { /* flag the problem */ G_tok->log_error_curtok(TCERR_REQ_CATCH_CLASS); /* unless this is a close paren, skip the errant token */ if (G_tok->cur() != TOKT_RPAR) G_tok->next(); /* note the error */ catch_has_err = TRUE; } /* get the variable name token */ if (G_tok->cur() == TOKT_SYM) { CTcSymLocal *var; /* * create the local variable - note that this variable is * implicitly assigned when the 'catch' clause is entered, so * mark it as initially assigned; we don't care if the * variable is ever used, so also mark it as used */ var = local_symtab_->add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), alloc_local(), FALSE, TRUE, TRUE); /* set the variable in the 'catch' clause */ if (!catch_has_err) catch_stm->set_exc_var(var); /* move on */ G_tok->next(); } else { /* flag the problem */ G_tok->log_error_curtok(TCERR_REQ_CATCH_VAR); /* unless this is a close paren, skip the errant token */ if (G_tok->cur() != TOKT_RPAR) G_tok->next(); /* note the error */ catch_has_err = TRUE; } /* check for the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the paren */ G_tok->next(); } else { /* * log the error and continue, assuming that the paren is * simply missing and things are otherwise okay */ G_tok->log_error_curtok(TCERR_REQ_CATCH_RPAR); } /* * parse the 'catch' statement block - we've already established * a special scope for the 'catch' block, so don't start a new * scope if the block contains a compound statement */ catch_body = parse_stm(err, 0, TRUE); /* leave the special 'catch' scope */ leave_scope(&scope_data); /* if the statement block failed, give up now */ if (*err) return 0; /* add the 'catch' clause to the 'try' if we were successful */ if (!catch_has_err) { /* set the 'catch' node's body */ catch_stm->set_body(catch_body); /* set the local scope in the 'catch' */ catch_stm->set_symtab(local_symtab_); /* add the 'catch' to the 'try' */ try_stm->add_catch(catch_stm); } } /* check for a 'finally' clause */ if (G_tok->cur() == TOKT_FINALLY) { CTPNStmFinally *fin_stm; CTPNStm *fin_body; tcprs_scope_t scope_data; CTPNStmEnclosing *old_enclosing; /* * the locals we allocate for the 'finally' are in the finally's * own scope - the slots can be reused later */ enter_scope(&scope_data); /* create the 'finally' node */ fin_stm = new CTPNStmFinally(enclosing_stm_, alloc_local(), alloc_local()); /* * set the 'finally' clause's source position - we want this * clause to have its own source position independent of the * 'try' statement of which it is a part, so that the debugger * can keep track of entry into this clause's generated code */ fin_stm->set_source_pos(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* skip the 'finally' keyword */ G_tok->next(); /* set the 'finally' to enclose its body */ old_enclosing = set_enclosing_stm(fin_stm); /* parse the 'finally' statement block */ fin_body = parse_stm(err, 0, FALSE); /* set the 'finally' block's closing position, if present */ if (fin_body != 0) { fin_stm->set_end_pos( fin_body->get_end_desc(), fin_body->get_end_linenum()); } /* restore the enclosing statement */ set_enclosing_stm(old_enclosing); /* we're done with the special scope */ leave_scope(&scope_data); /* if that failed, give up now */ if (*err) return 0; /* set the 'finally' node's body */ fin_stm->set_body(fin_body); /* add the 'finally' to the 'try' */ try_stm->set_finally(fin_stm); } /* make sure we have at least one 'catch' or 'finally' clause */ if (!try_stm->has_catch_or_finally()) try_stm->log_error(TCERR_TRY_WITHOUT_CATCH); /* return the 'try' statement node */ return try_stm; } /* * Parse a 'throw' statement */ CTPNStm *CTcParser::parse_throw(int *err) { CTcPrsNode *expr; CTPNStmThrow *throw_stm; /* skip the 'throw' keyword */ G_tok->next(); /* parse the expression to be thrown */ expr = parse_expr(); if (expr == 0) { *err = TRUE; return 0; } /* create the statement node */ throw_stm = new CTPNStmThrow(expr); /* require a terminating semicolon */ if (parse_req_sem()) { *err = TRUE; return 0; } /* return the statement node */ return throw_stm; } /* ------------------------------------------------------------------------ */ /* * Unary operator parsing */ /* * Anonymous function symbol table preparer. This class creates the lcoal * symbol table for an anonymous function or method. */ class CAnonFuncSymtabPrep { public: /* * Create a local symbol table for an anonymous function or method. */ CTcPrsSymtab *create_symtab() { /* * Create a new local symbol table to represent the enclosing scope * of the new nested scope. Unlike most nested scopes, we can't * simply plug in the current scope's symbol table - instead, we * have to build a special representation of the enclosing scope to * handle the "closure" behavior. The enclosing scope's local * variable set effectively becomes an object rather than a simple * stack frame. The enclosing lexical scope and the anonymous * function then both reference the shared locals object. * * This synthesized enclosing scope will directly represent all of * the nested scopes up but not including to the root global scope. * The global scope doesn't need the special closure * representation, since it's already shared among all lexical * scopes anyway. Since we're not linking in to the enclosing * scope list the way we normally would, we need to explicitly link * in the global scope. To do this, we can simply make the global * scope the enclosing scope of our synthesized outer scope table. * */ enc_symtab = new CTcPrsSymtab(G_prs->get_global_symtab()); /* * fill the new local symbol table with the inherited local symbols * from the current local scope and any enclosing scopes - but stop * when we reach the global scope, since this is already shared by * all scopes and doesn't need any special closure representation */ for (CTcPrsSymtab *tab = G_prs->get_local_symtab() ; tab != 0 && tab != G_prs->get_global_symtab() ; tab = tab->get_parent()) { /* enumerate entries in this table */ tab->enum_entries(&enum_for_anon, this); } /* * Create the local symbol table for *within* the new anonymous * fucntion scope. This is another local symbol table, this time * nested within the table containing the locals shared from the * enclosing scope. This one will contain any formals defined for * the anonymous function, which hide inherited locals from the * enclosing scope. */ return new CTcPrsSymtab(enc_symtab); } /* * Finish the symbol table. Call this after parsing the code body, to * convert referenced locals in enclosing scopes to context locals. */ void finish() { /* * Enumerate all of the entries in our scope once again - this * time, we want to determine if there are any variables that were * not previously referenced from anonymous functions but have been * now; we need to convert all such variables to context locals. */ for (CTcPrsSymtab *tab = G_prs->get_local_symtab() ; tab != 0 && tab != G_prs->get_global_symtab() ; tab = tab->get_parent()) { /* enumerate entries in this table */ tab->enum_entries(&enum_for_anon2, this); } } private: /* * enumeration callback, phase 1: add a proxy context local to our * "enclosing" symbol table for each actual local defined in a parent * scope */ static void enum_for_anon(void *ctx, CTcSymbol *sym) { /* get 'this' from the context */ CAnonFuncSymtabPrep *self = (CAnonFuncSymtabPrep *)ctx; /* * If this symbol is already in our table, another symbol from an * enclosed scope hides it, so ignore this one. Note that we're * only interested in the symbols defined directly in our table - * we hide symbols defined in the enclosing global scope, so we * don't care if they're already defined there. */ if (self->enc_symtab->find_direct( sym->get_sym(), sym->get_sym_len()) != 0) return; /* create a context-variable copy */ CTcSymbol *new_sym = sym->new_ctx_var(); /* if we got a new symbol, add it to the new symbol table */ if (new_sym != 0) self->enc_symtab->add_entry(new_sym); } /* * enumeration callback, phase 2: convert locals in enclosing scopes * that are actually referenced in the anonymous function to context * locals */ static void enum_for_anon2(void *ctx, CTcSymbol *sym) { /* get 'this' from the context */ CAnonFuncSymtabPrep *self = (CAnonFuncSymtabPrep *)ctx; /* * If this symbol isn't in the anonymous function's local symbol * table, the anonymous function didn't end up using it. The fact * that it's in the enclosing table in this case simply means that * it's a context variable in the enclosing context. */ if (self->enc_symtab->find_direct( sym->get_sym(), sym->get_sym_len()) == 0) return; /* ask the symbol to apply the necessary conversion */ sym->finish_ctx_var_conv(); } /* enclosing symbol table */ CTcPrsSymtab *enc_symtab; }; /* * Parse an anonymous function */ CTPNAnonFunc *CTcPrsOpUnary::parse_anon_func(int short_form, int is_method) { /* * our code body type can be an anonymous function, a short anonymous * function, or an anonymous method */ tcprs_codebodytype cb_type = (short_form ? TCPRS_CB_SHORT_ANON_FN : G_tok->cur() == TOKT_METHOD ? TCPRS_CB_ANON_METHOD : TCPRS_CB_ANON_FN); /* skip the initial token */ G_tok->next(); /* prepare the enclosing local symbol table for the new nested scope */ CAnonFuncSymtabPrep symprep; CTcPrsSymtab *lcltab = symprep.create_symtab(); /* * Parse the code body. * * If it's a function (not a method), it shares the method context from * the lexically enclosing scope, so it has access to 'self' and other * method context variables if and only if the enclosing scope does. * If it's a method, it has access to the live method context, so the * enclosing scope's status is irrelevant. */ int err = 0; int has_retval; CTPNCodeBody *code_body = G_prs->parse_nested_code_body( FALSE, is_method || G_prs->is_self_valid(), 0, 0, 0, 0, 0, &has_retval, &err, lcltab, cb_type); /* if that failed, return failure */ if (code_body == 0 || err != 0) return 0; /* * If this is an anonymous function (not an anonymous method), and the * nested code body references 'self' or the full method context, then * so does the enclosing code body, and we need these variables in the * local context for the topmost enclosing code body. * * If this is an anonymous method, references to the method context are * to the live frame at the time of invocation, not to the saved * lexical frame at the time of creation. So references from within * the method's code body don't create references on the enclosing * scope, and they don't require any shared context information. */ if (!is_method) { if (code_body->self_referenced()) { G_prs->set_self_referenced(TRUE); G_prs->set_local_ctx_needs_self(TRUE); } if (code_body->full_method_ctx_referenced()) { G_prs->set_full_method_ctx_referenced(TRUE); G_prs->set_local_ctx_needs_full_method_ctx(TRUE); } } else { /* mark it as a method */ code_body->set_anon_method(TRUE); } /* finish the symbol table */ symprep.finish(); /* * if this is a function (not a method), and there's a 'self' object in * the enclosing context, and we referenced 'self' or the full method * context in the nested code body, we'll definitely need a local * context, so make sure we have one initialized even if we don't have * any local variables shared */ if (!is_method && G_prs->is_self_valid() && (code_body->self_referenced() || code_body->full_method_ctx_referenced())) { /* initialize a local context here, in the enclosing level */ G_prs->init_local_ctx(); } /* * add the code body to the parser's master list of nested top-level * statements for the current program */ G_prs->add_nested_stm(code_body); /* return a new anonymous function node */ return new CTPNAnonFunc(code_body, has_retval, is_method); } /* * Parse an in-line object definition. */ class CTPNInlineObject *CTcPrsOpUnary::parse_inline_object(int has_colon) { /* create the inline object node */ CTPNInlineObject *obj = new CTPNInlineObject(); /* if the caller didn't already check for a colon, do so now */ if (!has_colon && G_tok->next() == TOKT_COLON) { /* note and skip the colon */ has_colon = TRUE; G_tok->next(); } /* if there's a colon, parse the superclass list */ if (has_colon) G_prs->parse_superclass_list(0, obj->get_superclass_list()); /* next we need a left brace '{' */ if (G_tok->cur() != TOKT_LBRACE) { G_tok->log_error_curtok(TCERR_INLINE_OBJ_REQ_LBRACE); return 0; } /* skip the brace */ G_tok->next(); /* parse the property list */ int err = FALSE; tcprs_term_info outer_term; if (!G_prs->parse_obj_prop_list( &err, obj, 0, FALSE, FALSE, TRUE, TRUE, &outer_term, &outer_term)) return 0; /* return the object node */ return obj; } /* ------------------------------------------------------------------------ */ /* * State save structure for parsing property expressions */ class CTcPrsPropExprSave { public: unsigned int has_local_ctx_ : 1; int local_ctx_var_num_; size_t ctx_var_props_used_; int next_ctx_arr_idx_; int local_cnt_; int max_local_cnt_; int self_referenced_; int self_valid_; int full_method_ctx_referenced_; int local_ctx_needs_self_; int local_ctx_needs_full_method_ctx_; struct CTcCodeBodyRef *cur_code_body_; CTPNStmEnclosing *enclosing_stm_; class CTcPrsSymtab *local_symtab_; class CTcPrsSymtab *enclosing_local_symtab_; class CTcPrsSymtab *goto_symtab_; CAnonFuncSymtabPrep inline_prep_; }; /* ------------------------------------------------------------------------ */ /* * Parse an object superclass list */ void CTcParser::parse_superclass_list( CTcSymObj *obj_sym, CTPNSuperclassList &sclist) { /* scan the list */ for (int done = FALSE ; !done ; ) { /* we need a symbol */ switch(G_tok->cur()) { case TOKT_SYM: /* a symbol must be a superclass name - look it up */ { const CTcToken *tok = G_tok->getcur(); CTcSymObj *sc_sym = (CTcSymObj *)get_global_symtab()->find( tok->get_text(), tok->get_text_len()); /* * If this symbol is defined, and it's an object, check to * make sure this won't set up a circular class definition * - so, make sure the base class isn't the same as the * object being defined, and that it doesn't inherit from * the object being defined. */ if (sc_sym != 0 && obj_sym != 0 && sc_sym->get_type() == TC_SYM_OBJ && (sc_sym == obj_sym || sc_sym->has_superclass(obj_sym))) { /* * this is a circular class definition - complain about * it and don't add it to my superclass list */ G_tok->log_error(TCERR_CIRCULAR_CLASS_DEF, (int)sc_sym->get_sym_len(), sc_sym->get_sym(), (int)obj_sym->get_sym_len(), obj_sym->get_sym()); } else { /* it's good - add the new superclass to our list */ sclist.append(new CTPNSuperclass( tok->get_text(), tok->get_text_len())); /* * add it to the symbol's superclass name list as well * - we use this for keeping track of the hierarchy in * the symbol file for compile-time access */ if (obj_sym != 0) obj_sym->add_sc_name_entry( tok->get_text(), tok->get_text_len()); } /* skip the symbol and see what follows */ switch (G_tok->next()) { case TOKT_COMMA: /* we have another superclass following */ G_tok->next(); break; default: /* no more superclasses */ done = TRUE; break; } } break; case TOKT_OBJECT: /* * it's a basic object definition - make sure other * superclasses weren't specified */ if (sclist.head_ != 0) G_tok->log_error(TCERR_OBJDEF_OBJ_NO_SC); /* * mark the object as having an explicit superclass of the root * object class */ if (obj_sym != 0) obj_sym->set_sc_is_root(TRUE); /* * skip the 'object' keyword and we're done - there's no * superclass list */ G_tok->next(); done = TRUE; break; default: /* premature end of the object list */ G_tok->log_error_curtok(TCERR_OBJDEF_REQ_SC); /* stop here */ done = TRUE; break; } } } /* * Parse an object template instance at the beginning of an object body */ void CTcParser::parse_obj_template(int *err, CTPNObjDef *objdef, int is_inline) { /* check the current token for a template use */ switch(G_tok->cur()) { case TOKT_SSTR: case TOKT_SSTR_START: case TOKT_DSTR: case TOKT_DSTR_START: case TOKT_LBRACK: case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: /* we have an object template */ break; default: /* it's not a template - simply return without parsing anything */ return; } /* parse the expressions until we reach the end of the template */ size_t cnt; CTcObjTemplateInst *p; int done; CTcPrsPropExprSave save_info; for (cnt = 0, p = template_expr_, done = FALSE ; !done ; ++cnt) { /* * remember the statment start location, in case we have a * template element that generates code (such as a double-quoted * string with an embedded expression) */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* * note the token, so that we can figure out which template we * are using */ p->def_tok_ = G_tok->cur(); /* assume this will also be the first token of the value expression */ p->expr_tok_ = *G_tok->copycur(); /* we don't have any expression yet */ p->expr_ = 0; p->code_body_ = 0; p->inline_method_ = 0; /* prepare to parse a property value expression */ begin_prop_expr(&save_info, FALSE, is_inline); /* check to see if this is another template item */ switch(G_tok->cur()) { case TOKT_SSTR: /* single-quoted string - parse just the string */ p->expr_ = CTcPrsOpUnary::parse_primary(); break; case TOKT_SSTR_START: /* start of single-quoted embedded expression string - parse it */ p->expr_ = CTcPrsOpUnary::parse_primary(); /* treat it as a regular string for template matching */ p->def_tok_ = TOKT_SSTR; break; case TOKT_DSTR: /* string - parse it */ p->expr_ = parse_expr_or_dstr(TRUE); break; case TOKT_DSTR_START: /* start of a double-quoted embedded expression string */ p->expr_ = parse_expr_or_dstr(TRUE); /* * treat it as a regular double-quoted string for the * purposes of matching the template */ p->def_tok_ = TOKT_DSTR; break; case TOKT_LBRACK: /* it's a list */ p->expr_ = CTcPrsOpUnary::parse_list(); break; case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: /* skip the operator token */ G_tok->next(); /* the value expression starts with this token */ p->expr_tok_ = *G_tok->copycur(); /* a primary expression must follow */ p->expr_ = CTcPrsOpUnary::parse_primary(); break; case TOKT_EOF: /* end of file - return and let the caller deal with it */ return; default: /* anything else ends the template list */ done = TRUE; /* don't count this item after all */ --cnt; break; } /* * check for embedded anonymous functions, and wrap the expression * in a code body if necessary */ finish_prop_expr( &save_info, p->expr_, p->code_body_, p->inline_method_, FALSE, is_inline, 0); /* * move on to the next expression slot if we have room (if we * don't, we won't match anything anyway; just keep writing over * the last slot so that we can at least keep parsing entries) */ if (cnt + 1 < template_expr_max_) ++p; } /* we have no matching template yet */ const CTcObjTemplate *tpl = 0; const CTPNSuperclass *def_sc = 0; /* presume we don't have any undescribed classes in our hierarchy */ int undesc_class = FALSE; /* * Search for the template, using the normal inheritance rules that we * use at run-time: start with the first superclass and look for a * match; if we find a match, look at subsequent superclasses to look * for one that overrides the match. */ if (objdef != 0) { /* search our superclasses for a match */ tpl = find_class_template( objdef->get_first_sc(), template_expr_, cnt, &def_sc, &undesc_class); /* remember the 'undescribed class' status */ objdef->set_undesc_sc(undesc_class); } /* if we didn't find a match, look for a root object match */ if (tpl == 0 && !undesc_class) tpl = find_template_match(template_head_, template_expr_, cnt); /* if we didn't find a match, it's an error */ if (tpl == 0) { /* * Note the error, but don't report it yet. It might be that we * failed to find a template match simply because one of our * superclass names was misspelled. If that's the case, then the * missing template is the least of our problems, and it's not * worth reporting since it's probably just a side effect of the * missing superclass (that is, once the superclass misspelling is * corrected and the code is re-compiled, we might find that the * template is correct after all, since we'll know which class to * scan for the needed template.) At code generation time, we'll * be able to resolve the superclasses and find out what's really * going on, so that we can flag the appropriate error. */ if (objdef != 0) objdef->note_bad_template(TRUE); /* ignore the template instance */ return; } /* if there's no object statement, there's nothing left to do */ if (objdef == 0) return; /* * we know we have a matching template, so populate our actual * parameter list with the property identifiers for the matching * template */ match_template(tpl->items_, template_expr_, cnt); /* define the property values according to the template */ for (p = template_expr_ ; cnt != 0 ; ++p, --cnt) { /* add this property */ if (p->code_body_ != 0) objdef->add_method(p->prop_, p->code_body_, p->expr_, FALSE); else if (p->inline_method_ != 0) objdef->add_inline_method( p->prop_, p->inline_method_, p->expr_, FALSE); else if (p->expr_ != 0) objdef->add_prop(p->prop_, p->expr_, FALSE, FALSE); } } /* ------------------------------------------------------------------------ */ /* * search a class for a template match */ const CTcObjTemplate *CTcParser:: find_class_template(const CTPNSuperclass *first_sc, CTcObjTemplateInst *src, size_t src_cnt, const CTPNSuperclass **def_sc, int *undesc_class) { /* scan each superclass in the list for a match */ const CTPNSuperclass *sc; const CTcObjTemplate *tpl; for (tpl = 0, sc = first_sc ; sc != 0 ; sc = sc->nxt_) { /* find the symbol for this superclass */ CTcSymObj *sc_sym = (CTcSymObj *)get_global_symtab()->find( sc->get_sym_txt(), sc->get_sym_len()); /* if there's no symbol, or it's not a tads-object, give up */ if (sc_sym == 0 || sc_sym->get_type() != TC_SYM_OBJ || sc_sym->get_metaclass() != TC_META_TADSOBJ) { /* * this class has an invalid superclass - just give up without * issuing any errors now, since we'll have plenty to say * about this when building the object file data */ return 0; } /* find a match in this superclass hierarchy */ const CTcObjTemplate *cur_tpl = find_template_match( sc_sym->get_first_template(), src, src_cnt); /* see what we found */ const CTPNSuperclass *cur_def_sc; if (cur_tpl != 0) { /* we found it - note the current defining superclass */ cur_def_sc = sc; } else { /* * If this one has no superclass list, and it's not explicitly * a subclass of the root class, then this is an undescribed * class and cannot be used with templates at all. A class is * undescribed when it is explicitly declared as 'extern', and * does not have a definition in any imported symbol file in * the current compilation. If this is the case, flag it so * the caller will know we have an undescribed class. * * Note that we only set this flag if we failed to find a * template. A template can still be used if a matching * template is explicitly defined on the class in this * compilation unit, since in that case we don't need to look * up the inheritance hierarchy for the class. That's why we * set the flag here, only after we have failed to find a * template for the object. */ if (sc_sym->get_sc_name_head() == 0 && !sc_sym->sc_is_root()) { /* tell the caller we have an undescribed class */ *undesc_class = TRUE; /* * there's no need to look any further, since any matches * we might find among our other superclasses would be in * doubt because of the lack of information about this * earlier class, which might override later superclasses * if we knew more about it */ return 0; } /* we didn't find it - search superclasses of this class */ cur_tpl = find_class_template(sc_sym->get_sc_name_head(), src, src_cnt, &cur_def_sc, undesc_class); /* * if we have an undescribed class among our superclasses, * we're implicitly undescribed as well - if that's the case, * there's no need to look any further, so return failure */ if (*undesc_class) return 0; } /* if we found a match, see if we want to keep it */ if (cur_tpl != 0) { /* * if this is our first match, note it; if it's not, see if it * overrides the previous match */ if (tpl == 0) { /* this is the first match - definitely keep it */ tpl = cur_tpl; *def_sc = cur_def_sc; } else { /* * if the current source object descends from the previous * source object, this definition overrides the previous * definition, so keep it rather than the last one */ if (cur_def_sc->is_subclass_of(*def_sc)) { /* it overrides the previous one - keep the new one */ tpl = cur_tpl; *def_sc = cur_def_sc; } } } } /* return the best match we found */ return tpl; } /* ------------------------------------------------------------------------ */ /* * Find a matching template in the given template list */ const CTcObjTemplate *CTcParser:: find_template_match(const CTcObjTemplate *first_tpl, CTcObjTemplateInst *src, size_t src_cnt) { /* find the matching template */ for (const CTcObjTemplate *tpl = first_tpl ; tpl != 0 ; tpl = tpl->nxt_) { /* check for a match */ if (match_template(tpl->items_, src, src_cnt)) { /* it's a match - return this template */ return tpl; } } /* we didn't find a match */ return 0; } /* ------------------------------------------------------------------------ */ /* * Match a template to an actual template parameter list. */ int CTcParser::match_template(const CTcObjTemplateItem *tpl_head, CTcObjTemplateInst *src, size_t src_cnt) { /* check each element of the list */ CTcObjTemplateInst *p; const CTcObjTemplateItem *item; size_t rem; for (p = src, rem = src_cnt, item = tpl_head ; item != 0 && rem != 0 ; item = item->nxt_) { /* * Note whether or not this item is optional. Every element of an * alternative group must have the same optional status, so we need * only note the status of the first item if this is a group. */ int is_opt = item->is_opt_; /* * Scan each alternative in the current group. Note that if we're * not in an alternative group, the logic is the same: we won't * have any 'alt' flags, so we'll just scan a single item. */ int match; for (match = FALSE ; ; item = item->nxt_) { /* if this one matches, note the match */ if (item->tok_type_ == p->def_tok_) { /* note the match */ match = TRUE; /* this is the property to assign for the actual */ p->prop_ = item->prop_; } /* * If this one is not marked as an alternative, we're done. * The last item of an alternative group is identified by * having its 'alt' flag cleared. Also, if we somehow have an * ill-formed list, where we don't have a terminating * non-flagged item, we can stop now as well. */ if (!item->is_alt_ || item->nxt_ == 0) break; } /* check to see if the current item is optional */ if (is_opt) { /* * The item is optional. If it matches, try it both ways: * first try matching the item, then try skipping it. If we * can match this item and still match the rest of the string, * take that interpretation; otherwise, if we can skip this * item and match the rest of the string, take *that* * interpretation. If we can't match it either way, we don't * have a match. * * First, check to see if we can match the item and still match * the rest of the string. */ if (match && match_template(item->nxt_, p + 1, rem - 1)) { /* we have a match */ return TRUE; } /* * Matching this optional item doesn't let us match the rest of * the string, so try it with this optional item omitted - in * other words, just match the rest of the string, including * the current source item, to the rest of the template, * *excluding* the current optional item. * * There's no need to recurse to do this; simply continue * iterating, but do NOT skip the current source item. */ } else { /* * It's not optional, so if it doesn't match, the whole * template fails to match; if it does match, simply proceed * through the rest of the template. */ if (!match) return FALSE; /* we matched, so consume this source item */ ++p; --rem; } } /* skip any trailing optional items in the template list */ while (item != 0 && item->is_opt_) item = item->nxt_; /* * it's a match if and only if we reached the end of both lists at the * same time */ return (item == 0 && rem == 0); } /* ------------------------------------------------------------------------ */ /* * Begin a property expression */ void CTcParser::begin_prop_expr( CTcPrsPropExprSave *save_info, int is_static, int is_inline) { /* save the current parser state */ save_info->has_local_ctx_ = has_local_ctx_; save_info->local_ctx_var_num_ = local_ctx_var_num_; save_info->ctx_var_props_used_ = ctx_var_props_used_; save_info->next_ctx_arr_idx_ = next_ctx_arr_idx_; save_info->local_cnt_ = local_cnt_; save_info->max_local_cnt_ = max_local_cnt_; save_info->self_valid_ = self_valid_; save_info->self_referenced_ = self_referenced_; save_info->cur_code_body_ = cur_code_body_; save_info->full_method_ctx_referenced_ = full_method_ctx_referenced_; save_info->local_ctx_needs_self_ = local_ctx_needs_self_; save_info->local_ctx_needs_full_method_ctx_ = local_ctx_needs_full_method_ctx_; save_info->enclosing_stm_ = enclosing_stm_; save_info->enclosing_local_symtab_ = enclosing_local_symtab_; save_info->local_symtab_ = local_symtab_; save_info->goto_symtab_ = goto_symtab_; /* * we've saved the local context information, so clear it out for the * next parse job */ clear_local_ctx(); /* set up the local symbol table for the property expression */ if (is_inline) { /* * It's an inline object definition. For a normal (non-static) * property, the expression is treated as though it were wrapped in * an anonymous method, so that it can access the enclosing scope's * locals. We need to set up the usual anonymous function proxy * symbol table to allow access to the enclosing scope. For a * static property, the property expression is evaluated * immediately when we evaluate the overall inline object * expression. Static evaluation is also done in the context of * the enclosing scope, but since it happens during the enclosing * scope's lifetime, we don't have to worry about setting up a * proxy symbol table; we can simply access the locals directly * while they're still alive, so for the static case we just leave * the stack context exactly like it already is. */ if (!is_static) { /* * ordinary non-static expression - we need an anonymous * function context so that the expression can access the * enclosing scope's locals even after the enclosing scope * returns to its caller */ local_symtab_ = save_info->inline_prep_.create_symtab(); if ((enclosing_local_symtab_ = local_symtab_->get_parent()) == 0) enclosing_local_symtab_ = global_symtab_; self_valid_ = TRUE; } } else { /* * This is a regular top-level object definition, so there's no * enclosing scope. Clear out the code body and the local symbol * table so that we parse in global context. */ cur_code_body_ = 0; local_symtab_ = global_symtab_; enclosing_local_symtab_ = global_symtab_; } /* start a new parsing context */ enclosing_stm_ = 0; goto_symtab_ = 0; /* no locals yet */ local_cnt_ = 0; max_local_cnt_ = 0; } /* * Finish a property expression, checking for anonymous functions and * wrapping the expression in a code body if necesssary. If the expression * contains an anonymous function which needs to share context with its * enclosing scope, we need to build the code body wrapper immediately so * that we can capture the context information, which is stored in the * parser object itself (i.e,. 'this'). */ void CTcParser::finish_prop_expr( CTcPrsPropExprSave *save_info, CTcPrsNode* &expr, CTPNCodeBody* &cb, CTPNAnonFunc* &inline_method, int is_static, int is_inline, CTcSymProp *prop_sym) { /* presume we won't need to create a code body or inline method */ cb = 0; inline_method = 0; /* * If we have a local context, we have to set up a code body in order * to initialize the local context at run-time. If this is an inline * object definition, and it's not a static property, we need to turn * the expression into an anonymous method, so we also need to wrap it * in a code body. */ if (expr != 0 && (has_local_ctx_ || (is_inline && !is_static))) { /* * We need to wrap the expression in a code body to make the * property into a method. First, wrap the expression in an * appropriate statement so that we can put it into a code body. */ CTPNStm *stm; if (is_static) { /* * it's a static initializer - wrap it in a static initializer * statement node */ stm = new CTPNStmStaticPropInit(expr, prop_sym->get_prop()); } else if (expr->has_return_value()) { /* normal property value - wrap it in a 'return' */ stm = new CTPNStmReturn(expr); } else { /* * it's an expression that yields no value, such as a * double-quoted string expression or a call to a void * function; just use the expression itself */ stm = new CTPNStmExpr(expr); } /* wrap the expression statement in a code body */ cb = new CTPNCodeBody( local_symtab_, goto_symtab_, stm, 0, 0, FALSE, FALSE, 0, local_cnt_, self_valid_, save_info->cur_code_body_); /* set up the local context for access to enclosing scope locals */ finish_local_ctx(cb, local_symtab_); /* mark the code body for references to the method context */ cb->set_self_referenced(self_referenced_); cb->set_full_method_ctx_referenced(full_method_ctx_referenced_); /* mark the code body for inclusion in any local context */ cb->set_local_ctx_needs_self(local_ctx_needs_self_); cb->set_local_ctx_needs_full_method_ctx( local_ctx_needs_full_method_ctx_); /* * if this is a non-static property in an inline object * definition, turn the expression into an anonymous method */ if (is_inline && !is_static) { /* mark it as an anonymous method */ cb->set_anon_method(TRUE); /* add the code body to the master list of nested functions */ add_nested_stm(cb); /* wrap the code body in an anonymous method */ inline_method = new CTPNAnonFunc( cb, expr->has_return_value(), TRUE); /* the anonymous method supersedes the code body */ cb = 0; } } /* restore the saved parser state */ has_local_ctx_ = save_info->has_local_ctx_; local_ctx_var_num_ = save_info->local_ctx_var_num_; ctx_var_props_used_ = save_info->ctx_var_props_used_; next_ctx_arr_idx_ = save_info->next_ctx_arr_idx_; local_cnt_ = save_info->local_cnt_; max_local_cnt_ = save_info->max_local_cnt_; cur_code_body_ = save_info->cur_code_body_; self_valid_ = save_info->self_valid_; self_referenced_ = save_info->self_referenced_; full_method_ctx_referenced_ = save_info->full_method_ctx_referenced_; local_ctx_needs_self_ = save_info->local_ctx_needs_self_; local_ctx_needs_full_method_ctx_ = save_info->local_ctx_needs_full_method_ctx_; enclosing_stm_ = save_info->enclosing_stm_; enclosing_local_symtab_ = save_info->enclosing_local_symtab_; local_symtab_ = save_info->local_symtab_; goto_symtab_ = save_info->goto_symtab_; /* * if this is a non-static property of an inline object, finish the * anonymous method proxy symbol table for enclosing scopes */ if (is_inline && !is_static) save_info->inline_prep_.finish(); } /* ------------------------------------------------------------------------ */ /* * property set token source */ void propset_token_source::insert_token(const CTcToken *tok) { /* create a new link entry and initialize it */ propset_tok *cur = new (G_prsmem) propset_tok(tok); /* link it into our list */ if (last_tok != 0) last_tok->nxt = cur; else nxt_tok = cur; last_tok = cur; } void propset_token_source::insert_token( tc_toktyp_t typ, const char *txt, size_t len) { /* set up the token object */ CTcToken tok; tok.settyp(typ); tok.set_text(txt, len); /* insert it */ insert_token(&tok); } /* ------------------------------------------------------------------------ */ /* * Set up the inserted token stream for a propertyset invocation */ void CTcParser::insert_propset_expansion(struct propset_def *propset_stack, int propset_depth) { /* * First, determine if we have any added formals from propertyset * definitions. */ int i; int formals_found; for (formals_found = FALSE, i = 0 ; i < propset_depth ; ++i) { /* if this one has formals, so note */ if (propset_stack[i].param_tok_head != 0) { /* note it, and we need not look further */ formals_found = TRUE; break; } } /* * If we found formals from property sets, we must expand them into the * token stream. */ if (formals_found) { /* insert an open paren at the start of the expansion list */ propset_token_source tok_src; tok_src.insert_token(TOKT_LPAR, "(", 1); /* we don't yet need a leading comma */ int need_comma = FALSE; /* * Add the tokens from each propertyset in the stack, from the * outside in, until we reach an asterisk in each stack. */ for (i = 0 ; i < propset_depth ; ++i) { propset_tok *cur; /* add the tokens from the stack element */ for (cur = propset_stack[i].param_tok_head ; cur != 0 ; cur = cur->nxt) { /* * If we need a comma before the next real element, add it * now. */ if (need_comma) { tok_src.insert_token(TOKT_COMMA, ",", 1); need_comma = FALSE; } /* * If this is a comma and the next item is the '*', omit * the comma - if we have nothing more following, we want * to suppress the comma. */ if (cur->tok.gettyp() == TOKT_COMMA && cur->nxt != 0 && cur->nxt->tok.gettyp() == TOKT_TIMES) { /* * it's the comma before the star - simply stop here * for this list, but note that we need a comma before * any additional formals that we add in the future */ need_comma = TRUE; break; } /* * if it's the '*' for this list, stop here, since we want * to insert the next level in before we add these tokens */ if (cur->tok.gettyp() == TOKT_TIMES) break; /* insert it into our expansion list */ tok_src.insert_token(&cur->tok); } } /* * If we have explicit formals in the true input stream, add them, * up to but not including the close paren. */ if (G_tok->cur() == TOKT_LPAR) { /* skip the open paren */ G_tok->next(); /* check for a non-empty list */ if (G_tok->cur() != TOKT_RPAR) { /* * the list is non-empty - if we need a comma, add it now */ if (need_comma) tok_src.insert_token(TOKT_COMMA, ",", 1); /* we will need a comma at the end of this list */ need_comma = TRUE; } /* * copy everything up to but not including the close paren to * the expansion list */ while (G_tok->cur() != TOKT_RPAR && G_tok->cur() != TOKT_EOF) { /* insert this token into our expansion list */ tok_src.insert_token(G_tok->getcur()); /* skip it */ G_tok->next(); } /* skip the closing paren */ if (G_tok->cur() == TOKT_RPAR) G_tok->next(); } /* * Finish the expansion by adding the parts of each propertyset * list after the '*' to the expansion list. Copy from the inside * out, since we want to unwind the nesting from outside in that we * did to start with. */ for (i = propset_depth ; i != 0 ; ) { /* move down to the next level */ --i; /* find the '*' in this list */ propset_tok *cur; for (cur = propset_stack[i].param_tok_head ; cur != 0 && cur->tok.gettyp() != TOKT_TIMES ; cur = cur->nxt) ; /* if we found the '*', skip it */ if (cur != 0) cur = cur->nxt; /* * also skip the comma following the '*', if present - we'll * explicitly insert the needed extra comma if we actually find * we need one */ if (cur != 0 && cur->tok.gettyp() == TOKT_COMMA) cur = cur->nxt; /* insert the remainder of the list into the expansion list */ for ( ; cur != 0 ; cur = cur->nxt) { /* if we need a comma, add it now */ if (need_comma) { tok_src.insert_token(TOKT_COMMA, ",", 1); need_comma = FALSE; } /* insert this token */ tok_src.insert_token(&cur->tok); } } /* add the closing paren at the end of the expansion list */ tok_src.insert_token(TOKT_RPAR, ")", 1); /* * We've fully expanded the formal list. Now all that remains is * to insert the expanded token list into the token input stream, * so that we read from the expanded list instead of from the * original token stream. */ G_tok->set_external_source(&tok_src); } } /* ------------------------------------------------------------------------ */ /* * Parse an object definition's property list */ int CTcParser::parse_obj_prop_list( int *err, CTPNObjDef *objdef, CTcSymMetaclass *meta_sym, int modify, int is_nested, int braces, int is_inline, tcprs_term_info *outer_term_info, tcprs_term_info *term_info) { /* we aren't in a propertyset definition yet */ propset_def propset_stack[MAX_PROPSET_DEPTH]; int propset_depth = 0; /* * Parse the property list. Keep going until we get to the closing * semicolon. */ for (int done = FALSE ; !done ; ) { /* * Remember the statement start location. Some types of * property definitions result in implicit statements being * created, so we must record the statement start position for * those cases. */ cur_desc_ = G_tok->get_last_desc(); cur_linenum_ = G_tok->get_last_linenum(); /* presume no 'replace' on this property */ int replace = FALSE; /* parse the property definition */ switch(G_tok->cur()) { case TOKT_PROPERTYSET: { propset_def *cur_def; propset_def dummy_def; /* * It's a property set definition. These definitions * nest; make sure we have space left in our stack. */ if (propset_depth >= MAX_PROPSET_DEPTH) { /* * nested too deeply - flag the error if this is the * one that pushes us over the edge */ if (propset_depth == MAX_PROPSET_DEPTH) G_tok->log_error(TCERR_PROPSET_TOO_DEEP); /* * keep going with a syntactic parse despite the * problems - increment the current depth, but use a * dummy definition object, since we have no more * stack entries to store the real data */ ++propset_depth; cur_def = &dummy_def; } else { /* set up to use the next available stack entry */ cur_def = &propset_stack[propset_depth]; /* consume this stack entry */ ++propset_depth; } /* skip the keyword and get the property name pattern */ if (G_tok->next() == TOKT_SSTR) { /* note the pattern string */ cur_def->prop_pattern = G_tok->getcur()->get_text(); cur_def->prop_pattern_len = G_tok->getcur()->get_text_len(); /* * Validate the pattern. It must consist of valid * token characters, except for a single asterisk, * which it must have. */ int star_cnt = 0, inval_cnt = 0; utf8_ptr p((char *)cur_def->prop_pattern); size_t rem = cur_def->prop_pattern_len; for ( ; rem != 0 ; p.inc(&rem)) { /* check this character */ if (p.getch() == '*') ++star_cnt; else if (!is_sym(p.getch())) ++inval_cnt; } /* * if the first character isn't an asterisk, it must * be a valid initial symbol character */ if (cur_def->prop_pattern_len != 0) { /* get the first character */ wchar_t firstch = utf8_ptr::s_getch( cur_def->prop_pattern); /* * if it's not a '*', it must be a valid initial * token character */ if (firstch != '*' && !is_syminit(firstch)) ++inval_cnt; } /* * if it has no '*' or more than one '*', or it has * invalid characters, flag it as invalid */ if (star_cnt != 1 || inval_cnt != 0) G_tok->log_error_curtok(TCERR_PROPSET_INVAL_PAT); } else { /* not a string; flag an error, but try to keep going */ G_tok->log_error_curtok(TCERR_PROPSET_REQ_STR); /* we have no pattern string */ cur_def->prop_pattern = ""; cur_def->prop_pattern_len = 0; } /* we have no parameter token list yet */ cur_def->param_tok_head = 0; /* * skip the pattern string, and see if we have an argument * list */ if (G_tok->next() == TOKT_LPAR) { /* * Current parsing state: 0=start, 1=after symbol or * '*', 2=after comma, 3=done */ int state = 0; /* number of '*' tokens */ int star_cnt = 0; /* start with an empty token list */ propset_tok *last = 0; /* * We have a formal parameter list that is to be * applied to each property in the set. Parse tokens * up to the closing paren, stashing them in our list. */ for (G_tok->next() ; state != 3 ; ) { /* check the token */ switch (G_tok->cur()) { case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: /* * A brace or semicolon - assume that the right * paren was accidentally omitted. Flag the * error and consider the list to be finished. */ G_tok->log_error_curtok( TCERR_MISSING_RPAR_FORMAL); /* switch to 'done' state */ state = 3; break; case TOKT_RPAR: /* * Right paren. This ends the list. If we're * in state 2 ('after comma'), the paren is out * of place, so flag it as an error - but still * count it as ending the list. */ if (state == 2) G_tok->log_error(TCERR_MISSING_LAST_FORMAL); /* skip the paren */ G_tok->next(); /* switch to state 'done' */ state = 3; break; case TOKT_SYM: case TOKT_TIMES: /* * Formal parameter name or '*'. We can accept * these in states 'start' and 'after comma' - * in those states, just go add the token to * the list we're gathering. In state 'after * formal', this is an error - a comma should * have come between the two parameters. */ if (state == 1) { /* * 'after formal' - a comma must be * missing. Flag the error, but then * proceed as though the comma had been * there. */ G_tok->log_error_curtok( TCERR_REQ_COMMA_FORMAL); } /* switch to state 'after formal' */ state = 1; add_to_list: { /* create a list entry for the current token */ propset_tok *cur = new (G_prsmem) propset_tok( G_tok->getcur()); /* add it to the token list */ if (last != 0) last->nxt = cur; else cur_def->param_tok_head = cur; last = cur; /* if it's a '*', count it */ if (G_tok->cur() == TOKT_TIMES) ++star_cnt; /* skip it */ G_tok->next(); } break; case TOKT_COMMA: /* * In state 'after formal', a comma just goes * into our token list. A comma is invalid in * any other state. */ if (state == 1) { /* switch to state 'after comma' */ state = 2; /* go add the token to our list */ goto add_to_list; } else { /* otherwise, go handle as a bad token */ goto bad_token; } default: bad_token: /* generate an error according to our state */ switch (state) { case 0: case 2: /* * 'start' or 'after comma' - we expected a * formal name */ G_tok->log_error_curtok(TCERR_REQ_SYM_FORMAL); break; case 1: /* 'after formal' - expected a comma */ G_tok->log_error_curtok( TCERR_REQ_COMMA_FORMAL); break; } /* skip the token and keep going */ G_tok->next(); break; } } /* make sure we have exactly one star */ if (star_cnt != 1) G_tok->log_error(TCERR_PROPSET_INVAL_FORMALS); } /* require the open brace for the set */ if (G_tok->cur() == TOKT_LBRACE) G_tok->next(); else G_tok->log_error_curtok(TCERR_PROPSET_REQ_LBRACE); } /* proceed to parse the properties within */ break; case TOKT_SEM: /* * If we're using brace notation (i.e., the property list is * surrounded by braces), a semicolon inside the property list * is ignored. If we're using regular notation, a semicolon * ends the property list. */ if (braces || propset_depth != 0) { /* * we're using brace notation, or we're inside the braces * of a propertyset, so semicolons are ignored */ } else { /* * we're using regular notation, so this semicolon * terminates the property list and the object body - note * that we're done parsing the object body */ done = TRUE; } /* in any case, skip the semicolon and carry on */ G_tok->next(); break; case TOKT_CLASS: case TOKT_EXTERN: case TOKT_MODIFY: case TOKT_DICTIONARY: case TOKT_PROPERTY: case TOKT_PLUS: case TOKT_INC: case TOKT_INTRINSIC: case TOKT_OBJECT: case TOKT_GRAMMAR: case TOKT_ENUM: /* * All of these probably indicate the start of a new * statement - they probably just left off the closing * semicolon of the current object. Flag the error and * return, on the assumption that we'll find a new statement * starting here. */ G_tok->log_error_curtok(braces || propset_depth != 0 ? TCERR_OBJ_DEF_REQ_RBRACE : TCERR_OBJ_DEF_REQ_SEM); done = TRUE; break; case TOKT_SYM: /* * Property definition. For better recovery from certain * common errors, check a couple of scenarios. * * Look ahead one token. If the next token is a colon, then * we have a nested object definition (the property is * assigned to a reference to an anonymous object about to be * defined in-line). This could also indicate a missing * semicolon (or close brace) immediately before the symbol * we're taking as a property name - the symbol we just parsed * could actually be intended as the next object's name, and * the colon introduces its superclass list. If the new * object's property list isn't enclosed in braces, this is * probably what happened. * * If the next token is '(', '{', or '=', it's almost * certainly a property definition, so proceed on that * assumption. * * If the next token is a symbol, we have one of two probable * errors. Either we have a missing '=' in a property * definition, or this is a new anonymous object definition, * and the current object definition is missing its * terminating semicolon. If the current symbol is a class * name, it's almost certainly a new object definition; * otherwise, assume it's really a property definition, and * we're just missing the '='. */ /* look ahead at the next token */ switch(G_tok->next()) { case TOKT_LPAR: case TOKT_LBRACE: case TOKT_EQ: /* * It's almost certainly a property definition, no * matter what the current symbol looks like. Put back * the token and proceed. */ G_tok->unget(); break; case TOKT_COLON: /* * "symbol: " - a nested object definition, or what's * meant to be a new object definition, with the * terminating semicolon or brace of the current object * missing. Assume for now it's correct, in which case * it's a nested object definition; put back the token and * proceed. */ G_tok->unget(); break; case TOKT_SYM: case TOKT_AT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_TIMES: case TOKT_DIV: case TOKT_MOD: case TOKT_ARROW: case TOKT_AND: case TOKT_NOT: case TOKT_BNOT: case TOKT_COMMA: { /* * Two symbols in a row, or a symbol followed by * what might be a template operator - either an * equals sign was left out, or this is a new object * definition. Put back the token and check what * kind of symbol we had in the first place. */ G_tok->unget(); CTcSymbol *sym = global_symtab_->find( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* * if it's an object symbol, we almost certainly * have a new object definition */ if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* log the error */ G_tok->log_error_curtok( braces || propset_depth != 0 ? TCERR_OBJ_DEF_REQ_RBRACE : TCERR_OBJ_DEF_REQ_SEM); /* assume the object is finished */ done = TRUE; } } break; default: /* * anything else is probably an error, but it's hard to * guess what the error is; proceed on the assumption * that it's going to be a property definition, and let * the property definition parser take care of analyzing * the problem */ G_tok->unget(); break; } /* * if we decided that the object is finished, cancel the * property definition parsing */ if (done) break; parse_normal_prop: /* * initialize my termination information object with the * current location (note that we don't necessarily initialize * the current termination info, since only the outermost one * matters; if ours happens to be active, it's because it's * the outermost) */ outer_term_info->init(G_tok->get_last_desc(), G_tok->get_last_linenum()); /* go parse the property definition */ parse_obj_prop( err, objdef, replace, meta_sym, term_info, propset_stack, propset_depth, is_nested, is_inline); /* if we ran into an unrecoverable error, give up */ if (*err) return FALSE; /* * if we encountered a termination error, assume the current * object was intended to be terminated, so stop here */ if (term_info->unterm_) { /* * we found what looks like the end of the object within * the property parser - assume we're done with this * object */ done = TRUE; } /* done */ break; case TOKT_OPERATOR: /* * Operator overload property - parse this as a normal property * (the property parser takes care of the 'operator' syntax * specialization). */ goto parse_normal_prop; case TOKT_REPLACE: /* 'replace' is only allowed with 'modify' objects */ if (!modify) G_tok->log_error(TCERR_REPLACE_PROP_REQ_MOD_OBJ); /* skip the 'replace' keyword */ G_tok->next(); /* set the 'replace' flag for the property parser */ replace = TRUE; /* go parse the normal property definition */ goto parse_normal_prop; case TOKT_RBRACE: /* * If we're in a 'propertyset' definition, this ends the * current propertyset. If the property list is enclosed in * braces, this brace terminates the property list and the * object body. Otherwise, it's an error. */ if (propset_depth != 0) { /* pop a propertyset level */ --propset_depth; /* * skip the brace, and proceed with parsing the rest of * the object */ G_tok->next(); } else if (braces) { /* we're done with the object body */ done = TRUE; /* skip the brace */ G_tok->next(); } else { /* * this property list isn't enclosed in braces, so this is * completely unexpected - treat it the same as any other * invalid token */ goto inval_token; } break; case TOKT_EOF: default: inval_token: /* anything else is invalid */ G_tok->log_error(TCERR_OBJDEF_REQ_PROP, (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text()); /* skip the errant token and continue */ G_tok->next(); /* if we're at EOF, abort */ if (G_tok->cur() == TOKT_EOF) { *err = TRUE; return FALSE; } break; } } /* success */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Parse a property definition within an object definition. * * 'obj_is_nested' indicates that the enclosing object is a nested object. * This tells us that we can't allow the lexicalParent property to be * manually defined, since we will automatically define it explicitly for * the enclosing object by virtue of its being nested. */ void CTcParser::parse_obj_prop( int *err, CTPNObjDef *objdef, int replace, CTcSymMetaclass *meta_sym, tcprs_term_info *term_info, propset_def *propset_stack, int propset_depth, int obj_is_nested, int is_inline) { /* we haven't created the new property yet */ CTPNObjProp *new_prop = 0; /* presume it's not a static property definition */ int is_static = FALSE; /* remember the property token */ CTcToken prop_tok = *G_tok->copycur(); /* * if we're in a property set, apply the property set pattern to the * property name */ if (propset_depth != 0) { /* we can't define operator overloads here */ if (prop_tok.gettyp() == TOKT_OPERATOR) G_tok->log_error(TCERR_OPER_IN_PROPSET); /* * Build the real property name based on all enclosing propertyset * pattern strings. Start with the current (innermost) pattern, * then apply the next pattern out, and so on. * * Note that if the current nesting depth is greater than the * maximum nesting depth, ignore the illegally deep levels and * just start with the deepest legal level. */ int i = propset_depth; if (i > MAX_PROPSET_DEPTH) i = MAX_PROPSET_DEPTH; /* start with the current token */ size_t explen = G_tok->getcur()->get_text_len(); char expbuf[TOK_SYM_MAX_LEN]; memcpy(expbuf, G_tok->getcur()->get_text(), explen); /* iterate through the propertyset stack */ for (propset_def *cur = &propset_stack[i-1] ; i > 0 ; --i, --cur) { char tmpbuf[TOK_SYM_MAX_LEN]; utf8_ptr src; utf8_ptr dst; size_t rem; /* if we'd exceed the maximum token length, stop here */ if (explen + cur->prop_pattern_len > TOK_SYM_MAX_LEN) { /* complain about it */ G_tok->log_error(TCERR_PROPSET_TOK_TOO_LONG); /* stop the expansion */ break; } /* copy the pattern up to the '*' */ for (src.set((char *)cur->prop_pattern), dst.set(tmpbuf), rem = cur->prop_pattern_len ; rem != 0 && src.getch() != '*' ; src.inc(&rem)) { /* copy this character */ dst.setch(src.getch()); } /* if we found a '*', skip it */ if (rem != 0 && src.getch() == '*') src.inc(&rem); /* insert the expansion from the last round here */ memcpy(dst.getptr(), expbuf, explen); /* advance our output pointer */ dst.set(dst.getptr() + explen); /* copy the remainder of the pattern string */ if (rem != 0) { /* copy the remaining bytes */ memcpy(dst.getptr(), src.getptr(), rem); /* advance the output pointer past the copied bytes */ dst.set(dst.getptr() + rem); } /* copy the result back to the expansion buffer */ explen = dst.getptr() - tmpbuf; memcpy(expbuf, tmpbuf, explen); } /* store the new token in tokenizer memory */ prop_tok.set_text(G_tok->store_source(expbuf, explen), explen); } /* presume we won't find a valid symbol for the property token */ CTcSymProp *prop_sym = 0; /* presume it's not an operator overload property */ int op_args = 0; /* if this is an 'operator' property, the syntax is special */ if (prop_tok.gettyp() == TOKT_OPERATOR) parse_op_name(&prop_tok, &op_args); /* * look up the property token as a symbol, to see if it's already * defined - we don't want to presume it's a property quite yet, but * we can at least look to see if it's defined as something else */ CTcSymbol *sym = global_symtab_->find( prop_tok.get_text(), prop_tok.get_text_len()); /* skip it and see what comes next */ switch (G_tok->next()) { case TOKT_LPAR: case TOKT_LBRACE: parse_method: /* look up the symbol */ prop_sym = look_up_prop(&prop_tok, TRUE); /* vocabulary properties can't be code */ if (prop_sym != 0 && prop_sym->is_vocab()) G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR); /* * A left paren starts a formal parameter list, a left brace starts * a code body - in either case, we have a method, so parse the * code body. If we're parsing an inline object definition, parse * it as an anonymous method definition instead. */ if (is_inline) { /* inline object - parse an anonymous method */ G_tok->unget(); CTPNAnonFunc *m = CTcPrsOpUnary::parse_anon_func(FALSE, TRUE); /* add this as a property expression */ if (m != 0) new_prop = objdef->add_inline_method(prop_sym, m, 0, replace); } else { /* top-level object - parse a code body */ int argc; int opt_argc; int varargs; int varargs_list; CTcSymLocal *varargs_list_local; int has_retval; CTPNCodeBody *code_body = parse_code_body( FALSE, TRUE, TRUE, &argc, &opt_argc, &varargs, &varargs_list, &varargs_list_local, &has_retval, err, 0, TCPRS_CB_NORMAL, propset_stack, propset_depth, 0, 0); /* check for errors in the code body */ if (*err) return; /* * if this is an operator, check arguments, and pick the * correct operator for the number of operands for operators * with multiple operand numbers */ if (op_args != 0) { /* mark the code body as an operator overload method */ if (code_body != 0) code_body->set_operator_overload(TRUE); /* varargs aren't allowed */ if (varargs) { G_tok->log_error( TCERR_OP_OVERLOAD_WRONG_FORMALS, op_args - 1); } else if (argc != op_args - 1) { /* not a valid combination of operator and arguments */ G_tok->log_error( TCERR_OP_OVERLOAD_WRONG_FORMALS, op_args - 1); } } /* add the method definition */ if (prop_sym != 0) new_prop = objdef->add_method(prop_sym, code_body, 0, replace); } break; case TOKT_SEM: case TOKT_RBRACE: case TOKT_RPAR: case TOKT_RBRACK: case TOKT_COMMA: /* log the error */ G_tok->log_error(TCERR_OBJDEF_REQ_PROPVAL, (int)prop_tok.get_text_len(), prop_tok.get_text(), (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text()); /* if it's anything other than a semicolon, skip it */ if (G_tok->cur() != TOKT_SEM) G_tok->next(); break; case TOKT_COLON: /* * It's a nested object definition. The value of the property is a * reference to an anonymous object defined in-line after the * colon. The in-line object definition uses the "braces" version * of the standard syntax: after the colon we have the class list, * an open brace, the property list, and a close brace. * * Note that we hold off defining the property symbol for as long * as possible, since we want to make sure that we don't have a * syntax error where this was intended to be a whole new top-level * object definition, but the terminator was accidentally left off * the previous object. In this case, the new object definition * would look like a nested object rather than a new top-level * object. This is probably the case if the symbol before the * colon is already known as an object name. */ { /* * If the symbol before the colon is already known as an object * name, it must be intended as a new object rather than a * nested object, meaning that the terminator is missing from * the previous object definition. If this is the case, flag * the termination error and proceed to parse the new object * definition. */ if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* * flag the unterminated object error - note that the * location where termination should have occurred is * where the caller tells us */ G_tcmain->log_error(term_info->desc_, term_info->linenum_, TC_SEV_ERROR, TCERR_UNTERM_OBJ_DEF); /* unget the colon so we can parse the new object def */ G_tok->unget(); /* flag the termination error for the caller */ term_info->unterm_ = TRUE; /* we're done */ return; } /* skip the colon */ G_tok->next(); /* parse the nested object property */ if (!objdef->parse_nested_obj_prop( new_prop, err, term_info, &prop_tok, replace)) return; } /* done */ break; case TOKT_EQ: /* skip the '=' */ G_tok->next(); /* * if a brace follows, this must be using the obsolete TADS 2 * notation, in which an equals sign followed a property name even * if a method was being defined; if this is the case, generate an * error, but then proceed to treat what follows as a method */ if (G_tok->cur() == TOKT_LBRACE) { /* log the error... */ G_tok->log_error(TCERR_EQ_WITH_METHOD_OBSOLETE); /* ...but then go ahead and parse it as a method anyway */ goto parse_method; } /* go parse the value */ goto parse_value; default: /* * an '=' is required after a value property name; log the error * and proceed, assuming that the '=' was left out but that the * property value is otherwise well-formed */ G_tok->log_error_curtok(TCERR_PROP_REQ_EQ); parse_value: { /* look up the property symbol */ prop_sym = look_up_prop(&prop_tok, TRUE); /* note if it's a vocabulary property */ int is_vocab_prop = (prop_sym != 0 && prop_sym->is_vocab()); /* we don't know whether we have a constant or method yet */ CTPNCodeBody *code_body = 0; CTcPrsNode *expr = 0; CTPNAnonFunc *inline_method = 0; /* * if this is a vocabulary property, perform special vocabulary * list parsing - a vocabulary property can have one or more * single-quoted strings that we store in a list, but the list * does not have to be enclosed in brackets */ if (is_vocab_prop && G_tok->cur() == TOKT_SSTR) { /* * set the property to a vocab list placeholder for now in * the object - we'll replace it during linking with the * actual vocabulary list */ CTcConstVal cval; cval.set_vocab_list(); expr = new CTPNConst(&cval); /* if I have no dictionary, it's an error */ if (dict_cur_ == 0) G_tok->log_error(TCERR_VOCAB_NO_DICT); /* get the object symbol */ CTcSymObj *obj_sym = (objdef != 0 ? objdef->get_obj_sym() : 0); /* parse the list entries */ for (;;) { /* add the word to our vocabulary list */ if (obj_sym != 0) obj_sym->add_vocab_word( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), prop_sym->get_prop()); /* * skip the string; if another string doesn't * immediately follow, we're done with the implied list * */ if (G_tok->next() != TOKT_SSTR) break; } } else { /* if it's a vocabulary property, other types aren't allowed */ if (is_vocab_prop) G_tok->log_error_curtok(TCERR_VOCAB_REQ_SSTR); /* check for the 'static' keyword */ if (G_tok->cur() == TOKT_STATIC) { /* note that it's static */ is_static = TRUE; /* skip the 'static' keyword */ G_tok->next(); } /* prepare to parse the property value expression */ CTcPrsPropExprSave save_info; begin_prop_expr(&save_info, is_static, is_inline); /* * parse the expression (which can be a double-quoted * string instead of a value expression) */ expr = parse_expr_or_dstr(TRUE); if (expr == 0) { *err = TRUE; return; } /* * finish the expression, and check to see if it needs * dynamic evaluation (if so, it will yield a code body) */ finish_prop_expr( &save_info, expr, code_body, inline_method, is_static, is_inline, prop_sym); } /* if we have a valid property and expression, add it */ if (prop_sym != 0) { /* add the expression or code body, as appropriate */ if (code_body != 0) { /* method */ new_prop = objdef->add_method( prop_sym, code_body, expr, replace); } else if (inline_method != 0) { /* inline method */ new_prop = objdef->add_inline_method( prop_sym, inline_method, expr, replace); } else if (expr != 0) { /* constant expression */ new_prop = objdef->add_prop( prop_sym, expr, replace, is_static); } } } /* done with the property */ break; } /* make sure that the object doesn't already define this propery */ if (sym != 0) { /* presume it's not a duplicate */ int is_dup = FALSE; /* * if the enclosing object is nested, and this is the lexicalParent * property, then it's defined automatically by the compiler and * thus can't be redefined explicitly */ if (obj_is_nested && sym == lexical_parent_sym_) is_dup = TRUE; /* if we didn't already find a duplicate, scan the existing list */ if (!is_dup) { /* scan the property list for this object */ for (CTPNObjProp *obj_prop = objdef->get_first_prop() ; obj_prop != 0 ; obj_prop = obj_prop->get_next_prop()) { /* * if it matches (and it's not the one we just added), log * an error */ if (obj_prop != new_prop && obj_prop->get_prop_sym() == sym) { /* * if this property is overwritable, we're allowed to * replace it with a new value; otherwise, it's an * illegal duplicate */ if (obj_prop->is_overwritable()) { /* * we're allowed to overwrite it with an explicit * redefinition; simply remove the old property * from the list so we can add the new one */ objdef->delete_property(obj_prop->get_prop_sym()); } else { /* note that we found a duplicate property */ is_dup = TRUE; } /* no need to continue looking */ break; } } } /* if we found a duplicate, log an error */ if (is_dup) G_tok->log_error(TCERR_PROP_REDEF_IN_OBJ, (int)sym->get_sym_len(), sym->get_sym()); } /* * if we're modifying an intrinsic class, make sure the property isn't * defined in the class's native interface */ if (meta_sym != 0 && prop_sym != 0) { /* check this intrinsic class and all of its superclasses */ for (CTcSymMetaclass *cur_meta = meta_sym ; cur_meta != 0 ; cur_meta = cur_meta->get_super_meta()) { /* scan the list of native methods */ for (CTcSymMetaProp *mprop = cur_meta->get_prop_head() ; mprop != 0 ; mprop = mprop->nxt_) { /* if it matches, flag an error */ if (mprop->prop_ == prop_sym) { /* log the error */ G_tok->log_error(TCERR_CANNOT_MOD_META_PROP, prop_sym->get_sym_len(), prop_sym->get_sym()); /* no need to look any further */ break; } } } } /* * if this is a dictionary property, note that it's been defined for * this object */ if (prop_sym != 0 && prop_sym->is_vocab()) { /* scan the list of dictionary properties for this one */ for (CTcDictPropEntry *entry = dict_prop_head_ ; entry != 0 ; entry = entry->nxt_) { /* check this one for a match */ if (entry->prop_ == prop_sym) { /* mark this entry as defined for this object */ entry->defined_ = TRUE; /* no need to look any further */ break; } } } } /* ------------------------------------------------------------------------ */ /* * Look up a property symbol. If the symbol isn't defined, add it. If * it's defined as some other kind of symbol, we'll log an error and return * null. */ CTcSymProp *CTcParser::look_up_prop(const CTcToken *tok, int show_err) { /* look up the symbol */ CTcSymProp *prop = (CTcSymProp *)global_symtab_->find( tok->get_text(), tok->get_text_len()); /* if it's already defined, make sure it's a property */ if (prop != 0) { /* make sure it's a property */ if (prop->get_type() != TC_SYM_PROP) { /* log an error if appropriate */ if (show_err) G_tok->log_error(TCERR_REDEF_AS_PROP, (int)tok->get_text_len(), tok->get_text()); /* we can't use the symbol, so forget it */ prop = 0; } } else { /* create the new property symbol */ prop = new CTcSymProp(tok->get_text(), tok->get_text_len(), FALSE, G_cg->new_prop_id()); /* add it to the global symbol table */ global_symtab_->add_entry(prop); /* mark it as referenced */ prop->mark_referenced(); } /* return the property symbol */ return prop; } /* ------------------------------------------------------------------------ */ /* * Mix-in base class for object definitions (statement type or in-line * expression type) */ /* * Add a property value */ CTPNObjProp *CTPNObjDef::add_prop( CTcSymProp *prop_sym, CTcPrsNode *expr, int replace, int is_static) { /* create the new property item */ CTPNObjProp *prop = new CTPNObjProp(this, prop_sym, expr, 0, 0, is_static); /* link it into my list */ add_prop_entry(prop, replace); /* return the new property to our caller */ return prop; } /* * Add a method */ CTPNObjProp *CTPNObjDef::add_method( CTcSymProp *prop, CTPNCodeBody *code_body, CTcPrsNode *expr, int replace) { /* create a new property */ CTPNObjProp *objprop = new CTPNObjProp(this, prop, 0, code_body, 0, FALSE); /* link it into my list */ add_prop_entry(objprop, replace); /* return it */ return objprop; } /* * Add a method to an in-line object */ CTPNObjProp *CTPNObjDef::add_inline_method( CTcSymProp *prop, CTPNAnonFunc *inline_method, CTcPrsNode *expr, int replace) { /* create a new property */ CTPNObjProp *objprop = new CTPNObjProp( this, prop, expr, 0, inline_method, FALSE); /* link it into my list */ add_prop_entry(objprop, replace); /* return it */ return objprop; } /* * fold constants */ void CTPNObjDef::fold_proplist(CTcPrsSymtab *symtab) { /* fold constants in each of our property list entries */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->nxt_) prop->fold_constants(symtab); } /* ------------------------------------------------------------------------ */ /* * Object Definition Statement */ /* * add a superclass given the name */ void CTPNStmObjectBase::add_superclass(const CTcToken *tok) { /* add a new superclass list entry to my list */ sclist_.append(new CTPNSuperclass(tok->get_text(), tok->get_text_len())); } /* * add a superclass given the object symbol */ void CTPNStmObjectBase::add_superclass(CTcSymbol *sym) { /* add a new superclass entry to my list */ sclist_.append(new CTPNSuperclass(sym)); } /* * Add an implicit constructor, if one is required. */ void CTPNStmObjectBase::add_implicit_constructor() { /* count the superclasses */ int sc_cnt; CTPNSuperclass *sc; for (sc_cnt = 0, sc = get_first_sc() ; sc != 0 ; sc = sc->nxt_, ++sc_cnt) ; /* * If the object has multiple superclasses and doesn't have an explicit * constructor, add an implicit constructor that simply invokes each * superclass constructor using the same argument list sent to our * constructor. */ if (sc_cnt > 1) { /* scan the property list for a constructor property */ CTPNObjProp *prop; int has_constructor; for (prop = proplist_.first_, has_constructor = FALSE ; prop != 0 ; prop = prop->nxt_) { /* * if this is a constructor, note that the object has an * explicit constructor */ if (prop->is_constructor()) { /* we have a consructor */ has_constructor = TRUE; /* that's all we needed to know - no need to keep looking */ break; } } /* if we don't have a constructor, add one */ if (!has_constructor) { /* * Create a placeholder symbol for the varargs list variable - * we don't really need a symbol for this, but we do need the * symbol object so we can generate code for it properly. Note * that a varargs list variable is a local, not a formal. */ CTcSymLocal *varargs_local = new CTcSymLocal( "*", 1, FALSE, FALSE, 0); /* create the pseudo-statement for the implicit constructor */ CTPNStmImplicitCtor *stm = new CTPNStmImplicitCtor( (CTPNStmObject *)this); /* * Create a code body to hold the statement. The code body has * one local symbol (the vararg list formal parameter), and is * defined by the 'constructor' property. */ CTPNCodeBody *cb = new CTPNCodeBody( G_prs->get_global_symtab(), G_prs->get_goto_symtab(), stm, 0, 0, TRUE, TRUE, varargs_local, 1, TRUE, 0); /* * set the implicit constructor's source file location to the * source file location of the start of the object definition */ cb->set_source_pos(file_, linenum_); /* add it to the object's property list */ add_method(G_prs->get_constructor_sym(), cb, 0, FALSE); } } } /* * Add an entry to my property list */ void CTPNStmObjectBase::add_prop_entry(CTPNObjProp *prop, int replace) { /* inherit the default handling */ CTPNObjDef::add_prop_entry(prop, replace); /* check for replacement */ if (replace && !G_prs->get_syntax_only()) { /* start at the current object */ CTPNStmObject *stm = (CTPNStmObject *)this; /* iterate through all previous versions of the object */ for (;;) { /* * Get this object's superclass - since this is a 'modify' * object, its only superclass is object it directly modifies. * If there's no superclass, the modified base object must have * been undefined, so we have errors to begin with - ignore the * replacement in this case. */ CTPNSuperclass *sc = stm->get_first_sc(); if (sc == 0) break; /* get the symbol for the superclass */ CTcSymObj *sym = (CTcSymObj *)sc->get_sym(); /* * if this object is external, we must mark the property for * replacement so that we replace it at link time, after we've * loaded and resolved the external original object */ if (sym->is_extern()) { /* * Add an entry to my list of properties to delete in my * base objects at link time. */ obj_sym_->add_del_prop_entry(prop->get_prop_sym()); /* * no need to look any further for modified originals -- we * won't find any more of them in this translation unit, * since they must all be external from here on up the * chain */ break; } /* * Get this symbol's object parse tree - since the symbol isn't * external, and because we can only modify objects that we've * already seen, the parse tree must be present. If the parse * tree isn't here, stop now; we might be parsing for syntax * only in this case. */ stm = sym->get_obj_stm(); if (stm == 0) break; /* if this object isn't modified, we're done */ if (!sym->get_modified()) break; /* delete the property in the original object */ stm->delete_property(prop->get_prop_sym()); } } } /* * Delete a property */ void CTPNStmObjectBase::delete_property(CTcSymProp *prop_sym) { /* delete the symbol from the property list */ if (proplist_.del(prop_sym)) { /* found it - delete the property from our vocabulary list as well */ if (obj_sym_ != 0) obj_sym_->delete_vocab_prop(prop_sym->get_prop()); } } /* ------------------------------------------------------------------------ */ /* * Inline object */ /* * Parse a property defining a nested object. In an inline object, a * nested object is treated as an expression containing an inline object: * we evaluate the property expression at the time of instantiating the * enclosing inline object, so this has the effect of creating the nested * object at the same time we create the enclosing inline object. */ int CTPNInlineObjectBase::parse_nested_obj_prop( class CTPNObjProp* &new_prop, int *err, tcprs_term_info *term_info, const class CTcToken *prop_tok, int replace) { /* * Parse the inline object. The caller has already parsed and skipped * a colon, so tell the inline object parser that we're already past * the colon. */ CTPNInlineObject *objdef = CTcPrsOpUnary::parse_inline_object(TRUE); if (objdef == 0) { *err = TRUE; return FALSE; } /* look up the property symbol */ CTcSymProp *prop_sym = G_prs->look_up_prop(prop_tok, TRUE); if (prop_sym != 0 && prop_sym->is_vocab()) G_tok->log_error(TCERR_VOCAB_REQ_SSTR, 1, ":"); /* if we have a valid property, add it */ if (prop_sym != 0) new_prop = add_prop(prop_sym, objdef, replace, FALSE); /* * Define the lexicalParent property in the nested object with a * reference back to the parent object. */ CTcConstVal cval; cval.set_obj(get_obj_sym() != 0 ? get_obj_sym()->get_obj_id() : TCTARG_INVALID_OBJ, get_obj_sym() != 0 ? get_obj_sym()->get_metaclass() : TC_META_UNKNOWN); CTcPrsNode *cexpr = new CTPNConst(&cval); objdef->add_prop(G_prs->get_lexical_parent_sym(), cexpr, FALSE, FALSE); /* success */ return TRUE; } /* * adjust for dynamic compilation */ CTcPrsNode *CTPNInlineObjectBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each property expression */ for (CTPNObjProp *prop = proplist_.first_ ; prop != 0 ; prop = prop->get_next_prop()) prop->adjust_for_dyn(info); /* we did our updates in-place */ return this; } /* ------------------------------------------------------------------------ */ /* * Object property list */ /* * append a property */ void CTPNPropList::append(CTPNObjProp *prop) { *dst_ = prop; *(dst_ = &prop->nxt_) = 0; last_ = prop; ++cnt_; } /* * delete a property */ int CTPNPropList::del(CTcSymProp *prop_sym) { /* scan our and look for a property matching the given symbol */ for (CTPNObjProp **prvp = &first_, *cur = *prvp ; cur != 0 ; prvp = &cur->nxt_, cur = *prvp) { /* check for a match */ if (cur->get_prop_sym() == prop_sym) { /* this is the one to delete - unlink it from the list */ *prvp = cur->nxt_; /* if that left the list empty, clear the tail pointer */ if (first_ == 0) last_ = 0; /* make sure the append pointer points to the tail's nxt_ */ dst_ = (last_ != 0 ? &last_->nxt_ : &first_); /* decrement the property count */ --cnt_; /* indicate that we found it */ return TRUE; } } /* didn't find it */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Object Property node */ /* * fold constants */ CTcPrsNode *CTPNObjPropBase::fold_constants(CTcPrsSymtab *symtab) { /* * if we have a code body or inline method, check for an underlying * expression that the method wraps */ if ((code_body_ != 0 || inline_method_ != 0) && expr_ != 0) { /* fold the expression */ expr_ = expr_->fold_constants(symtab); /* if it's now a constant, drop the code body/method */ if (expr_->is_const() || expr_->is_dstring()) { if (code_body_ != 0) { code_body_->set_replaced(TRUE); code_body_ = 0; } if (inline_method_ != 0) { inline_method_->set_replaced(TRUE); inline_method_ = 0; } } else { /* * the expression is non-constant, so we need to generate the * code body for run-time evaluation; we can now drop the * expression, since the code wrapper supersedes it */ expr_ = 0; } } /* if we have an inline method or regular method, fold its constants */ if (inline_method_ != 0) inline_method_->fold_constants(symtab); else if (code_body_ != 0) code_body_->fold_constants(symtab); /* if we have an expression, fold it */ if (expr_ != 0) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* * If the resulting expression isn't a constant value, we need to * convert the expression to a method, since we need to evaluate * the value at run-time with code. Create an expression statement * to hold the expression, then create a code body to hold the * expression statement. Don't do this for inline objects, though; * expression properties in inline objects are evaluated at the * time the object is created, so they don't get made into methods. */ if (!objdef_->is_inline_object() && !expr_->is_const() && !expr_->is_dstring()) { /* * Generate an implicit 'return' for a regular expression, or * an implicit static initializer for a static property. */ CTPNStm *stm; if (is_static_) { /* * it's a static initializer - generate a static * initializer expression */ stm = new CTPNStmStaticPropInit(expr_, prop_sym_->get_prop()); } else if (expr_->has_return_value()) { /* * create a 'return' statement to return the value of the * expression */ stm = new CTPNStmReturn(expr_); } else { /* * It has no return value, so it must be something like a * double-quoted string or a call to a void function. Wrap * this in an 'expression statement' so that we evaluate * the expression for its side effects but discard any * return value. */ stm = new CTPNStmExpr(expr_); } /* create the code body */ code_body_ = new CTPNCodeBody( G_prs->get_global_symtab(), 0, stm, 0, 0, FALSE, FALSE, 0, 0, TRUE, 0); /* * forget about our expression, since it's now incorporated * into the code body */ expr_ = 0; /* * set the source file location in both the code body and the * expression statement nodes to use our own source file * location */ stm->set_source_pos(file_, linenum_); code_body_->set_source_pos(file_, linenum_); } } /* we're not changed directly by this */ return this; } /* * adjust for dynamic execution */ CTcPrsNode *CTPNObjPropBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust my various elements */ if (code_body_ != 0) code_body_->adjust_for_dyn(info); if (inline_method_ != 0) inline_method_->adjust_for_dyn(info); if (expr_ != 0) expr_ = expr_->adjust_for_dyn(info); /* we did our adjustments in-place */ return this; } /* * Am I a constructor? */ int CTPNObjPropBase::is_constructor() const { /* * I'm a constructor if I have a code body, and my property is the * constructor property. */ return (code_body_ != 0 && prop_sym_ != 0 && prop_sym_->get_prop() == G_prs->get_constructor_prop()); } /* ------------------------------------------------------------------------ */ /* * compound statement base class */ /* * instantiate */ CTPNStmCompBase::CTPNStmCompBase(CTPNStm *first_stm, CTcPrsSymtab *symtab) { /* remember the first statement in our statement list */ first_stm_ = first_stm; /* remember our local symbol table */ symtab_ = symtab; /* * presume we don't have our own private symbol table, but will * simply use our parent's */ has_own_scope_ = FALSE; /* * remember the source location of the closing brace, which should * be the current location when we're instantiated */ end_desc_ = G_tok->get_last_desc(); end_linenum_ = G_tok->get_last_linenum(); } /* * fold constants - we simply fold constants in each of our statements */ CTcPrsNode *CTPNStmCompBase::fold_constants(class CTcPrsSymtab *symtab) { /* iterate through our statements and fold constants in each one */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(cur->get_source_desc(), cur->get_source_linenum()); /* * Fold constants, using the enclosing symbol table (not the * local symbol table - during code generation we can resolve * only to the global scope, since local scope symbols must * always be resolved during parsing). We assume that statement * nodes are never replaced during constant folding, so we * ignore the result of this call and keep our original * statement list entry. */ cur->fold_constants(symtab); } /* * although nodes within our subtree might have been changed, this * compound statement itself is unchanged by constant folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNStmCompBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each statement in our list */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * adjust this statement - we assume that statements are never * replaced by this operation, so we just keep our existing list */ cur->adjust_for_dyn(info); } /* return myself */ return this; } /* * Generate code */ void CTPNStmCompBase::gen_code(int, int) { CTPNStm *cur; CTcPrsSymtab *old_frame; /* set my local scope symbol frame, if necessary */ old_frame = G_cs->set_local_frame(symtab_); /* set the code location for the start of the group */ add_debug_line_rec(); /* * iterate through our statements and generate code for each in * sequence */ for (cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* * set this statement's source line location to be the current * location for error reporting */ G_tok->set_line_info(cur->get_source_desc(), cur->get_source_linenum()); /* * Generate code for the statement. Note that we generate in * the scope of the enclosing symbol table, because we never * resolve symbols to local scope during code generation - local * scope symbols must be resolved during parsing, because these * symbols must always be declared before first being used * * We have no use for the results of any expressions, so discard * = true; we don't care about the form of any logical operator * results, so use the looser "for condition" rules */ cur->gen_code(TRUE, TRUE); } /* set the source location for the end of the group */ add_debug_line_rec(end_desc_, end_linenum_); /* check for unreferenced local variables */ if (symtab_ != 0 && has_own_scope_) { /* * set our line information to be current again, so that any * unreferenced local errors are reported at the start of the * compound statement */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); } /* restore the enclosing local frame */ old_frame = G_cs->set_local_frame(old_frame); } /* * Chart the control flow through the statement list */ unsigned long CTPNStmCompBase::get_control_flow(int warn) const { /* * presume we will not find a 'throw' or 'return', and that we'll be * able to reach the next statement after the list */ unsigned long flags = TCPRS_FLOW_NEXT; /* * iterate through the statements in our list, since they'll be * executed in the same sequence at run-time */ for (CTPNStm *cur = first_stm_ ; cur != 0 ; cur = cur->get_next_stm()) { /* get this statement's control flow characteristics */ unsigned long flags1 = cur->get_control_flow(warn); /* * OR each of 'throws', 'returns void', and 'returns value', * since anything a reachable statement does to return or throw, * the overall list can do to leave. Likewise, the 'break', * 'goto', and 'continue' flags push up into the enclosing * statement. */ flags |= (flags1 & (TCPRS_FLOW_THROW | TCPRS_FLOW_RET_VOID | TCPRS_FLOW_RET_VAL | TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT)); /* * if this statement can't continue to the next statement, the * remainder of this compound statement is unreachable, unless * it has a code label */ if (!(flags1 & TCPRS_FLOW_NEXT)) { /* * we can't reach the next statement; unless we find a label * at some point after this, we won't be able to reach any * of the remaining statements, hence we won't be able to * reach the statement after our list */ flags &= ~TCPRS_FLOW_NEXT; /* * If another statement follows, check to see if it's * reachable by label, because it's not reachable by any * other means */ CTPNStm *nxt = cur->get_next_stm(); if (nxt != 0) { /* * check to see if this statement has a label; if not, * warn that it is unreachable */ if (warn && !nxt->has_code_label() && !G_prs->get_syntax_only()) nxt->log_warning(TCERR_UNREACHABLE_CODE); /* skip ahead until we find something with a label */ while (nxt != 0 && !nxt->has_code_label()) { /* remember the previous statement */ cur = nxt; /* move on to the next statement */ nxt = nxt->get_next_stm(); } /* * if we found a reachable statement again, we might be * able to continue from the end of the list after all */ if (nxt != 0) flags |= TCPRS_FLOW_NEXT; } } } /* return the result */ return flags; } /* ------------------------------------------------------------------------ */ /* * expression statement base class */ /* * fold constants */ CTcPrsNode *CTPNStmExprBase::fold_constants(class CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our expression, replacing our expression with * the folded version */ expr_ = expr_->fold_constants(symtab); /* * although our expression subtree might have been changed, this * compound statement itself is unchanged by constant folding */ return this; } /* * Generate code */ void CTPNStmExprBase::gen_code(int, int) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* add a debug line record for the statement */ add_debug_line_rec(); /* * generate code in our expression - the result will not be used in any * further calculation, so discard it; and since we don't care about * the return value, use the looser "for condition" rules for any * top-level logical operators in the expression so that we don't * bother applying any extra conversions to a value we're just * discarding anyway */ expr_->gen_code(TRUE, TRUE); } /* ------------------------------------------------------------------------ */ /* * 'if' statement */ /* * fold constants */ CTcPrsNode *CTPNStmIfBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in the condition, replacing the expression with * the folded expression */ cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the substatements */ if (then_part_ != 0) then_part_ = (CTPNStm *)then_part_->fold_constants(symtab); if (else_part_ != 0) else_part_ = (CTPNStm *)else_part_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the conditional */ unsigned long CTPNStmIfBase::get_control_flow(int warn) const { unsigned long flags1; unsigned long flags2; /* * if my condition is constant, our control flow is determined * entirely by the branch we'll take */ if (cond_expr_ != 0 && cond_expr_->is_const()) { CTPNStm *part; /* * We have a constant expression - if it's true, our control * flow is determined by the 'then' part, otherwise it's * determines by the 'else' part. */ part = (cond_expr_->get_const_val()->get_val_bool() ? then_part_ : else_part_); /* * if the part determining the control flow is a null statement, * we'll simply continue to the next statement; otherwise, the * result is the control flow of the determining statement */ if (part == 0) { /* it's a null statement, which merely continues */ return TCPRS_FLOW_NEXT; } else { /* our control flow is this statement's control flow */ return part->get_control_flow(warn); } } /* * The control flow through the 'if' is a combination of the control * flow through the two parts. If one of the parts is a void * statement, it continues to the next statement after the 'if'. */ /* check the 'then' part if present */ flags1 = (then_part_ != 0 ? then_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* check the 'else' part if present */ flags2 = (else_part_ != 0 ? else_part_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* combine the parts to get the possible control flows */ return flags1 | flags2; } /* ------------------------------------------------------------------------ */ /* * "for" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmForBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in each of our expressions, replacing the * expressions with the folded versions */ if (init_expr_ != 0) init_expr_ = init_expr_->fold_constants(symtab); if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); if (reinit_expr_ != 0) reinit_expr_ = reinit_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the loop */ unsigned long CTPNStmForBase::get_control_flow(int warn) const { unsigned long flags; /* * If we have a condition with a constant false value, and there's * either a reinitialization expression or a loop body, warn about the * unreachable code. If the body is directly reachable via a code * label, we don't need this warning. */ if (cond_expr_ != 0 && cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool() && (reinit_expr_ != 0 || body_stm_ != 0) && (body_stm_ != 0 && !body_stm_->has_code_label()) && !G_prs->get_syntax_only()) { /* log a warning if desired */ if (warn && !cond_expr_->get_const_val()->is_ctc()) log_warning(TCERR_FOR_COND_FALSE); /* this will simply continue to the next statement */ return TCPRS_FLOW_NEXT; } /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * ignore any 'next' flag that comes out of the body, because 'next' * from the last statement in the body actually takes us back to the * top of the loop */ flags &= ~TCPRS_FLOW_NEXT; /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'for' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * If we have a condition, and either the condition is a constant * false value or has a non-constant value, assume that the * condition can possibly become false and hence that the loop can * exit, and hence that the next statement is reachable. */ if (cond_expr_ != 0 && (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool()))) flags |= TCPRS_FLOW_NEXT; /* * If we have any "in" clauses, we implicitly have a condition that * allows the loop to terminate. */ if (in_exprs_ != 0) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the "for" loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "foreach" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmForeachBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in each of our expressions, replacing the * expressions with the folded versions */ if (iter_expr_ != 0) iter_expr_ = iter_expr_->fold_constants(symtab); if (coll_expr_ != 0) coll_expr_ = coll_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the loop */ unsigned long CTPNStmForeachBase::get_control_flow(int warn) const { unsigned long flags; /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); /* * add in the 'next' flag, because collection iterations always * terminate */ flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them * in our own scope - if our child breaks or continues, it won't * affect the container of the 'foreach' itself */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); } else { /* * we have no body, so it's entirely up to the loop, which * simply iterates over the list and goes to the next statement */ flags = TCPRS_FLOW_NEXT; } /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "while" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmWhileBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our condition expression, replacing the * expression with the folded version */ if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmWhileBase::get_control_flow(int warn) const { unsigned long flags; /* * If we have a condition with a constant false value, and we have a * non-empty body, and the body isn't reachable via a label, warn * about the unreachable code */ if (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool() && body_stm_ != 0 && !body_stm_->has_code_label() && G_prs->get_syntax_only()) { /* log a warning if desired */ if (warn && !cond_expr_->get_const_val()->is_ctc()) log_warning(TCERR_WHILE_COND_FALSE); /* this will simply continue to the next statement */ return TCPRS_FLOW_NEXT; } /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * ignore any 'next' flag that comes out of the body, because 'next' * from the last statement in the body actually takes us back to the * top of the loop */ flags &= ~TCPRS_FLOW_NEXT; /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'for' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * if the condition is a constant false value, or has a non-constant * value, assume that the condition can possibly become false and * hence that the loop can exit, and hence that the next statement * is reachable */ if (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool())) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "do-while" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmDoWhileBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* * fold constants in our condition expression, replacing the * expression with the folded version */ if (cond_expr_ != 0) cond_expr_ = cond_expr_->fold_constants(symtab); /* fold constants in the body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmDoWhileBase::get_control_flow(int warn) const { unsigned long flags; /* check for a body */ if (body_stm_ != 0) { /* determine how our body can exit */ flags = body_stm_->get_control_flow(warn); } else { /* * We have no body, so it's entirely up to the loop - we'll * evaluate those parameters below, but for now clear all flags */ flags = 0; } /* * If we have a 'next' flag, it means that control can flow out from * the end of the last statement in the loop. That in turn means that * we can reach the code where we evaluate the condition. So, in order * for flow to proceed out of the loop, we have to be able to reach the * condition, and then the condition has to be capable of being false * (because if it's always true, we'll simply go back to the start of * the loop every time we hit the condition). */ if ((flags & TCPRS_FLOW_NEXT) != 0) { /* * The 'next' flag up to this point only means that control can * reach the condition at the bottom of the loop. Since we want to * tell our caller whether or not control can flow *out* of the * loop, this is now irrelevant, so clear the 'next' flag for now. */ flags &= ~TCPRS_FLOW_NEXT; /* * Okay, we now know we can reach the condition at the bottom of * the loop. If the condition is a constant false value, we know * that we can flow out of the loop, since we know we'll hit the * condition, find that it's false, and exit the loop. Likewise, * if the condition hasa non-constant value, assume that it can * possibly become false and hence that the loop can exit, and * hence that the next statement is reachable. */ if (!cond_expr_->is_const() || (cond_expr_->is_const() && !cond_expr_->get_const_val()->get_val_bool())) flags |= TCPRS_FLOW_NEXT; } /* * if there's a 'break' in the body, we can continue to the next * statement, since a break in the body takes us to the statement * after the 'while' */ if ((flags & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* * clear the 'break' and 'continue' flags, since we capture them in * our own scope - if our child breaks or continues, it won't affect * the loop's container */ flags &= ~(TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT); /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * "switch" statement node */ /* * fold constants */ CTcPrsNode *CTPNStmSwitchBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression and body */ expr_ = expr_->fold_constants(symtab); if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmSwitchBase::get_control_flow(int warn) const { unsigned long flags; /* * Control flow through a switch is controlled by control flow * through the compound statement. If the compound statement can * break, then we flow to the next statement. If the compound * statement can reach the next statement, then so can we. */ flags = (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * check for a reachable break in the body - a break means the next * statement is reachable */ if ((flags & TCPRS_FLOW_BREAK) != 0) { /* * break takes us to the next statement, and doesn't propagate * up to a break in any enclosing statement */ flags |= TCPRS_FLOW_NEXT; flags &= ~TCPRS_FLOW_BREAK; } /* * If the switch has no 'default' case, it is almost certain that we * can reach the next statement after the switch, because we can * probably assume that not every case is accounted for and hence we * have the equivalent of a null 'default' case. * * Note that we can't know for sure that every case hasn't been * accounted for, because we do not have datatype information for the * controlling expression; even if we did know the controlling * expression's datatype, it is impossible to enumerate every possible * value for expressions of certain datatypes (such as lists and * strings, which have effectively infinite ranges). However, it is a * reasonable assumption that not every possible case has been * accounted for, */ if (!get_has_default()) flags |= TCPRS_FLOW_NEXT; /* return the flags */ return flags; } /* ------------------------------------------------------------------------ */ /* * code label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmLabelBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmLabelBase::get_control_flow(int warn) const { ulong flags; /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ flags = (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * if we have an explicit 'break' flag in our control flow flags, it * means that we were targeted specifically with a 'break' statement * contained within our body - this transfers control to the next * statement after me, therefore my next statement is reachable in * this case */ if ((control_flow_flags_ & TCPRS_FLOW_BREAK) != 0) flags |= TCPRS_FLOW_NEXT; /* return the result */ return flags; } /* * add control flow flags */ void CTPNStmLabelBase::add_control_flow_flags(ulong flags) { /* add the flags to any we already have */ control_flow_flags_ |= flags; } /* ------------------------------------------------------------------------ */ /* * 'goto' statement node */ /* * Chart the control flow through the statement */ unsigned long CTPNStmGotoBase::get_control_flow(int warn) const { CTcSymbol *sym; /* find our target statement */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a 'goto' * targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_GOTO); } /* indicate that we jump somewhere */ return TCPRS_FLOW_GOTO; } /* ------------------------------------------------------------------------ */ /* * 'case' label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmCaseBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* if the expression isn't a constant, it's an error */ if (!expr_->is_const()) log_error(TCERR_CASE_NOT_CONSTANT); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmCaseBase::get_control_flow(int warn) const { /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'default' label statement node */ /* * fold constants */ CTcPrsNode *CTPNStmDefaultBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our labeled statement */ if (stm_ != 0) stm_ = (CTPNStm *)stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmDefaultBase::get_control_flow(int warn) const { /* * if we have an underlying statement, control flow is the same as * the statement's control flow; if we don't have a statement (i.e., * our labeled statement is an empty statement), we flow to the next * statement */ return (stm_ != 0 ? stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'try' statement */ /* * add a 'catch' clause */ void CTPNStmTryBase::add_catch(CTPNStmCatch *catch_stm) { /* link it in at the end of our list */ if (last_catch_stm_ != 0) last_catch_stm_->set_next_catch(catch_stm); else first_catch_stm_ = catch_stm; /* it's now the last catch statement */ last_catch_stm_ = catch_stm; catch_stm->set_next_catch(0); /* it's the last statement in its group */ catch_stm->set_next_stm(0); } /* * fold constants */ CTcPrsNode *CTPNStmTryBase::fold_constants(CTcPrsSymtab *symtab) { CTPNStmCatch *cur_catch; /* fold constants in our protected code */ if (body_stm_ != 0) body_stm_->fold_constants(symtab); /* fold constants in our catch list */ for (cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* fold constants; assume this will not change the statement */ cur_catch->fold_constants(symtab); } /* fold constants in our 'finally' clause */ if (finally_stm_ != 0) finally_stm_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNStmTryBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust our body statement */ if (body_stm_ != 0) body_stm_ = (CTPNStm *)body_stm_->adjust_for_dyn(info); /* adjust each item in our catch list */ for (CTPNStmCatch *cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* adjust this item; assume it won't replace the node */ cur_catch->adjust_for_dyn(info); } /* adjust our 'finally' clause */ if (finally_stm_ != 0) finally_stm_ = (CTPNStmFinally *)finally_stm_->adjust_for_dyn(info); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmTryBase::get_control_flow(int warn) const { unsigned long flags; unsigned long fin_flags; CTPNStmCatch *cur_catch; /* * First, determine the control flow through the protected code. If * it exits, we either exit or proceed to the 'finally' clause. */ flags = (body_stm_ != 0 ? body_stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * Next, figure out what the various 'catch' clauses can do. We * assume that each 'catch' clause is enterable, since any exception * could occur within the protected code (whether it explicitly * throws or not - it could call a function or method that throws). * So, we can simply OR together with the main block's flags all of * the ways the different 'catch' clauses can exit. */ for (cur_catch = first_catch_stm_ ; cur_catch != 0 ; cur_catch = cur_catch->get_next_catch()) { /* OR in the flags for this 'catch' block */ flags |= cur_catch->get_control_flow(warn); } /* * If we have a 'finally' clause, it is definitely enterable, since * every other way of leaving the protected code goes through it. * Find how it can exit. */ fin_flags = (finally_stm_ != 0 ? finally_stm_->get_control_flow(warn) : TCPRS_FLOW_NEXT); /* * If the 'finally' block can't flow to next, then neither can the * main/catch blocks. In addition, the main/catch blocks can't * 'break', 'continue', 'goto', or 'return' either, because the * 'finally' block will not allow the other blocks to proceed with * whatever transfers they attempt. So, if 'next' isn't set in the * 'finally' flags, clear out all of these flags from the main/catch * flags. */ if (!(fin_flags & TCPRS_FLOW_NEXT)) flags &= ~(TCPRS_FLOW_NEXT | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT | TCPRS_FLOW_RET_VAL | TCPRS_FLOW_RET_VOID | TCPRS_FLOW_GOTO); /* * Add in the remaining flags from the 'finally' block, since * whatever it does, the overall statement does. However, ignore * 'next' flags in the 'finally' block, because if the 'finally' * code goes to next, it just means that we keep doing whatever we * were doing in the main block. */ fin_flags &= ~TCPRS_FLOW_NEXT; flags |= fin_flags; /* return the result */ return flags; } /* ------------------------------------------------------------------------ */ /* * 'catch' clause */ /* * set our exception class name */ void CTPNStmCatchBase::set_exc_class(const CTcToken *tok) { /* * remember the token's contents - the tokenizer keeps token symbol * strings in memory throughout the compilation, so we can simply * store a direct reference to the string */ exc_class_ = tok->get_text(); exc_class_len_ = tok->get_text_len(); } /* * fold constants */ CTcPrsNode *CTPNStmCatchBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in our body */ if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmCatchBase::get_control_flow(int warn) const { /* * our control flow is that of the body */ return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'finally' clause */ /* * fold constants */ CTcPrsNode *CTPNStmFinallyBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in our body */ if (body_ != 0) body_ = (CTPNStm *)body_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* * Chart the control flow through the statement */ unsigned long CTPNStmFinallyBase::get_control_flow(int warn) const { /* our control flow is that of the body */ return (body_ != 0 ? body_->get_control_flow(warn) : TCPRS_FLOW_NEXT); } /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ /* * fold constants */ CTcPrsNode *CTPNStmThrowBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in our expression */ expr_ = expr_->fold_constants(symtab); /* we're unchanged by constant folding */ return this; } /* ------------------------------------------------------------------------ */ /* * 'break' statement */ /* * set the label */ void CTPNStmBreakBase::set_label(const CTcToken *tok) { /* * token text is kept in memory throughout compilation, so we can * simply store a reference to the text without copying it */ lbl_ = tok->get_text(); lbl_len_ = tok->get_text_len(); } /* * chart control flow */ ulong CTPNStmBreakBase::get_control_flow(int warn) const { /* * If we don't have a label, we break out of our enclosing control * structure. If we have a label, we act like a 'goto' statement. */ if (lbl_ == 0) { /* no label - we simply break out of the enclosing loop or switch */ return TCPRS_FLOW_BREAK; } else { CTcSymbol *sym; /* * We have a label - we act like a 'goto' statement. Find our * target statement and mark it as having a targeted 'break'. */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a * 'break' targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_BREAK); } /* indicate that we jump somewhere else */ return TCPRS_FLOW_GOTO; } } /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ /* * set the label */ void CTPNStmContinueBase::set_label(const CTcToken *tok) { /* * token text is kept in memory throughout compilation, so we can * simply store a reference to the text without copying it */ lbl_ = tok->get_text(); lbl_len_ = tok->get_text_len(); } /* * chart control flow */ ulong CTPNStmContinueBase::get_control_flow(int warn) const { /* * If we don't have a label, we continue in our enclosing control * structure. If we have a label, we act like a 'goto' statement. */ if (lbl_ == 0) { /* no label - we simply continue with the enclosing loop */ return TCPRS_FLOW_CONT; } else { CTcSymbol *sym; /* * We have a label - we act like a 'goto' statement. Find our * target statement and mark it as having a targeted 'continue'. */ if (G_prs->get_goto_symtab() != 0 && (sym = G_prs->get_goto_symtab()->find(lbl_, lbl_len_)) != 0 && sym->get_type() == TC_SYM_LABEL) { /* * we found our label - mark its statement as having a * 'continue' targeted at it */ ((CTcSymLabel *)sym)->add_control_flow_flags(TCPRS_FLOW_CONT); } /* indicate that we jump somewhere else */ return TCPRS_FLOW_GOTO; } } /* ------------------------------------------------------------------------ */ /* * code ('goto') label base */ /* * Add control flow flags to my statement */ void CTcSymLabelBase::add_control_flow_flags(ulong flags) { if (stm_ != 0) stm_->add_control_flow_flags(flags); } /* ------------------------------------------------------------------------ */ /* * Grammar tree node subclasses */ /* * Grammar tree node - base class for nodes with children (OR, cat) */ class CTcPrsGramNodeWithChildren: public CTcPrsGramNode { public: CTcPrsGramNodeWithChildren() { /* no subitems yet */ sub_head_ = sub_tail_ = 0; } /* add a sub-item */ void add_sub_item(CTcPrsGramNode *sub) { /* add it to the end of the list */ if (sub_tail_ != 0) sub_tail_->next_ = sub; else sub_head_ = sub; /* it's now the last item */ sub_tail_ = sub; sub->next_ = 0; } virtual void flatten_cat() { CTcPrsGramNode *cur; /* flatten CAT nodes in our subnodes */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->flatten_cat(); } /* initialize expansion */ virtual void init_expansion() { CTcPrsGramNode *cur; /* initialize subnodes */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->init_expansion(); } /* head and tail of list of subnodes */ CTcPrsGramNode *sub_head_; CTcPrsGramNode *sub_tail_; }; /* * Grammar tree node - CAT node. This represents a string of * concatenated tokens. */ class CTcPrsGramNodeCat: public CTcPrsGramNodeWithChildren { public: /* I'm a CAT node */ virtual int is_cat() { return TRUE; } /* * Consolidate all of the OR's at the top of the tree. If we have any * OR subnodes, we'll rewrite ourselves as an OR of the concatenations * of all of the sub-OR's in all combinations. */ virtual CTcPrsGramNode *consolidate_or(); /* * Flatten CAT nodes together. If we have any CAT nodes below us, * move their contents into our own list directly. */ virtual void flatten_cat() { CTcPrsGramNode *prv; CTcPrsGramNode *cur; CTcPrsGramNode *nxt; /* * Check each child. For each child that's a CAT node, move its * contents directly into our list, removing the redundant * intervening CAT node. */ for (prv = 0, cur = sub_head_ ; cur != 0 ; cur = nxt) { /* note the next item, in case we fiddle with the list */ nxt = cur->next_; /* flatten the CAT items below this one */ cur->flatten_cat(); /* if this item is a CAT, move its contents directly into us */ if (cur->is_cat()) { CTcPrsGramNodeCat *cur_cat; /* cast it to a CAT node */ cur_cat = (CTcPrsGramNodeCat *)cur; /* if it's empty, just remove it; otherwise, link it in */ if (cur_cat->sub_head_ != 0) { /* link from the previous item to the sublist head */ if (prv == 0) { /* * this is the first item - put it at the start of * our list */ sub_head_ = cur_cat->sub_head_; } else { /* link it after the previous item */ prv->next_ = cur_cat->sub_head_; } /* link from the tail of the sublist to the next item */ cur_cat->sub_tail_->next_ = nxt; /* if it's the last item, set our tail pointer */ if (sub_tail_ == cur) sub_tail_ = cur_cat->sub_tail_; /* * the tail of the sublist is now the previous item in * the list for the purposes of the next iteration */ prv = cur_cat->sub_tail_; } else { /* unlink it from our list */ if (prv != 0) prv->next_ = nxt; else sub_head_ = nxt; /* update the tail pointer if removing the last item */ if (sub_tail_ == cur_cat) sub_tail_ = prv; /* note that the previous item is unchanged */ } } else { /* * it's not a CAT, so we're not changing it; this is the * previous item for the next iteration */ prv = cur; } } } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* create a new 'cat' node */ CTcPrsGramNodeCat *new_cat = new (G_prsmem) CTcPrsGramNodeCat(); /* add a clone of each of my children and add it to my replica */ for (CTcPrsGramNode *cur = sub_head_ ; cur != 0 ; cur = cur->next_) new_cat->add_sub_item(cur->clone_expansion()); /* return my replica */ return new_cat; } /* clone this node (without any children) */ virtual CTcPrsGramNodeWithChildren *clone_this_node() { return new (G_prsmem) CTcPrsGramNodeCat(); } /* initialize expansion */ virtual void init_expansion() { /* set to expand from our first child */ cur_sub_ = sub_head_; /* inherit default to initialize subnodes */ CTcPrsGramNodeWithChildren::init_expansion(); } /* get the next token in a token expansion */ const CTcToken *get_next_token() { /* advance to the next subitem */ cur_sub_ = cur_sub_->next_; /* * If there's anything left, return it; otherwise, return null. * During final token expansion, a 'cat' node never has anything * but tokens beneath it, since we move all the 'or' nodes to a * single 'or' at the top of the tree and flatten out the 'cat' * nodes to a single level under that; so, we can simply return * the token directly from the next underlying node. */ return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0); } /* * get my current token - we'll just return the token from the current * subitem */ const CTcToken *get_tok() const { return (cur_sub_ != 0 ? cur_sub_->get_tok() : 0); } /* current subitem in expansion */ CTcPrsGramNode *cur_sub_; }; /* * Grammar tree node - OR node. This represents a set of alternatives. */ class CTcPrsGramNodeOr: public CTcPrsGramNodeWithChildren { public: /* I'm an "or" node */ virtual int is_or() { return TRUE; } /* * Consolidate all of the OR's at the top of the tree. */ virtual CTcPrsGramNode *consolidate_or() { CTcPrsGramNode *cur; CTcPrsGramNode *nxt; CTcPrsGramNode *old_head; /* * Consolidate OR's in all subtrees. If any of our immediate * children turn into OR's, simply pull their children into our OR * list directly - OR(a, OR(b, c)) is equivalent to OR(a, b, c). * * Before we build the new list, stash away our entire subtree, * since we'll rebuild the tree from the updated versions. */ old_head = sub_head_; sub_head_ = sub_tail_ = 0; /* run through our list and build the new, consolidated list */ for (cur = old_head ; cur != 0 ; cur = nxt) { CTcPrsGramNode *new_sub; /* remember the next item in the old list */ nxt = cur->next_; /* consolidate OR's in the subtree */ new_sub = cur->consolidate_or(); /* * if this is an OR node, add its children directly to us; * otherwise, add the node itself */ if (new_sub->is_or()) { CTcPrsGramNodeOr *new_or; CTcPrsGramNode *chi; CTcPrsGramNode *next_chi; /* cast it */ new_or = (CTcPrsGramNodeOr *)new_sub; /* add each of the sub-OR's children as our direct children */ for (chi = new_or->sub_head_ ; chi != 0 ; chi = next_chi) { /* remember the next child in the old sub-list */ next_chi = chi->next_; /* move it directly to our list */ add_sub_item(chi); } /* the old OR item is now empty of children */ new_or->sub_head_ = new_or->sub_tail_ = 0; } else { /* it's not an OR - add it directly to our list */ add_sub_item(new_sub); } } /* return myself with no further changes */ return this; } /* initialize expansion - set up at the first alternative */ virtual void init_expansion() { /* start at the first alternative */ cur_alt_ = sub_head_; /* initialize the first alternative for expansion */ cur_alt_->init_expansion(); /* we didn't just do an 'or' */ just_did_or_ = FALSE; /* we haven't yet returned the first token */ before_first_ = TRUE; } /* advance to the next alternative for expansion */ virtual int advance_expansion() { /* advance to the next state */ cur_alt_ = cur_alt_->next_; /* * if that was the last state, wrap back to the first state and * indicate a 'carry'; otherwise, indicate no carry */ if (cur_alt_ == 0) { /* we ran out of states - wrap back to the first one */ cur_alt_ = sub_head_; /* initialize expansion in the new subitem */ cur_alt_->init_expansion(); /* indicate that we've wrapped and should carry forward */ return TRUE; } else { /* initialize expansion in the new subitem */ cur_alt_->init_expansion(); /* this is another valid state - no carry */ return FALSE; } } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* return a replica of the current alternative being expanded */ return cur_alt_->clone_expansion(); } /* get the next token in a token expansion */ const CTcToken *get_next_token() { const CTcToken *tok; /* * If we returned the synthesized '|' token between alternatives * last time, advance to the next alternative and return its first * token. */ if (just_did_or_) { /* we no longer just did an '|' */ just_did_or_ = FALSE; /* advance to the next alternative */ cur_alt_ = cur_alt_->next_; /* if there is no next alternative, there's nothing to return */ if (cur_alt_ == 0) return 0; /* initialize the new alternative */ cur_alt_->init_expansion(); /* if this alternative has no subitems, return another '|' */ if (cur_alt_->get_tok() == 0) { /* flag the '|' */ just_did_or_ = TRUE; /* if this was the last alternative, we're done */ if (cur_alt_->next_ == 0) return 0; /* * return the '|' again (we know it's already set up, * because we only get here if we did an '|' previously) */ return &or_tok_; } /* return its current token */ return cur_alt_->get_tok(); } /* * Get the first or next token in the current alternative, * depending on where we are. */ if (before_first_) { /* we're before the first token - get the current token */ tok = cur_alt_->get_tok(); /* we're now past the first token */ before_first_ = FALSE; } else { /* we're past the first token, so get the next one */ tok = cur_alt_->get_next_token(); } /* if we got a token from the current alternative, return it */ if (tok != 0) return tok; /* * We've run out of tokens in this alternative - synthesize an OR * token ('|') and return it, so the caller will know a new * top-level alternative follows. * * Do NOT synthesize an OR token after our last alternative. */ if (cur_alt_->next_ == 0) { /* this was our last alternative - we're done */ return 0; } /* we have another alternative - synthesize an OR */ or_tok_.settyp(TOKT_OR); or_tok_.set_text("|", 1); /* flag that we just did an '|' */ just_did_or_ = TRUE; /* return the synthesized '|' token */ return &or_tok_; } /* current alternative being expanded */ CTcPrsGramNode *cur_alt_; /* * my synthesized token for returning the '|' token between * alternatives during expansion */ CTcToken or_tok_; /* flag: we just returned the 'or' between two alternatives */ unsigned int just_did_or_ : 1; /* flag: we haven't yet returned the first token */ unsigned int before_first_ : 1; }; /* * Consolidate all of the OR's at the top of the tree. If we have any OR * subnodes, we'll rewrite ourselves as an OR of the concatenations of all * of the sub-OR's in all combinations. */ CTcPrsGramNode *CTcPrsGramNodeCat::consolidate_or() { CTcPrsGramNode *old_head; CTcPrsGramNodeOr *new_or; CTcPrsGramNode *cur; CTcPrsGramNode *nxt; int or_cnt; /* * Consolidate OR's in all subtrees. Before we do, stash away our * entire subtree, since we'll rebuild the tree from the updated * versions. */ old_head = sub_head_; sub_head_ = sub_tail_ = 0; /* run through our old list and build the new, consolidated list */ for (cur = old_head ; cur != 0 ; cur = nxt) { /* remember the next item in the old list */ nxt = cur->next_; /* consolidate the subtree and add it to our new list */ add_sub_item(cur->consolidate_or()); } /* * Count of OR nodes - if we don't have any, we don't have to make any * changes. Since we've already consolidated all OR's out of * sub-nodes into a single node at the top of each subtree, we don't * have to worry about OR's below our immediate children. */ for (or_cnt = 0, cur = sub_head_ ; cur != 0 ; cur = cur->next_) { /* if it's an OR node, count it */ if (cur->is_or()) ++or_cnt; } /* if we have no OR nodes, we need no changes */ if (or_cnt == 0) return this; /* create a new OR node to contain the list of expansions */ new_or = new (G_prsmem) CTcPrsGramNodeOr(); /* set up each OR node with the next alternative */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) cur->init_expansion(); /* * Iterate through all of the possibilities. For each iteration, * we'll build the currently selected alternative, then we'll advance * by one alternative. We'll keep doing this until we've advanced * through all of the alternatives. */ for (;;) { CTcPrsGramNodeCat *new_cat; /* create a new CAT node for the current selected alternative */ new_cat = new (G_prsmem) CTcPrsGramNodeCat(); /* clone and add each current alternative to the new CAT node */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) new_cat->add_sub_item(cur->clone_expansion()); /* * we've finished this entire expansion, so add it to the new main * OR we're building */ new_or->add_sub_item(new_cat); /* * Go through the list of subitems and advance to the next OR * state. Stop when we reach the first item that advances without * a 'carry'. If we're still carrying when we reach the last * item, we know we've wrapped around back to the first * alternative and hence are done. */ for (cur = sub_head_ ; cur != 0 ; cur = cur->next_) { /* advance this one; if it doesn't carry, we're done */ if (!cur->advance_expansion()) break; } /* * if we carried past the last item, we've wrapped around back to * the first alternative, so we're done */ if (cur == 0) break; } /* return the new main OR we built */ return new_or; } /* * Grammar tree node - token node. This is a terminal node representing a * single token. */ class CTcPrsGramNodeTok: public CTcPrsGramNode { public: CTcPrsGramNodeTok(const CTcToken *tok) { /* remember the token */ G_tok->copytok(&tok_, tok); } /* consolidate OR's */ virtual CTcPrsGramNode *consolidate_or() { /* since I'm a terminal node, there's nothing to do here */ return this; } /* clone the current expansion subtree */ virtual CTcPrsGramNode *clone_expansion() const { /* return a new token node with my same token value */ return new (G_prsmem) CTcPrsGramNodeTok(&tok_); } /* get my token */ virtual const CTcToken *get_tok() const { return &tok_; } /* my token */ CTcToken tok_; }; /* * Parse a 'grammar' statement's alternative list. */ void CTcParser::parse_gram_alts(int *err, CTcSymObj *gram_obj, CTcGramProdEntry *prod, CTcGramPropArrows *arrows, CTcGramAltFuncs *funcs) { /* set up the first alternative and add it to the production */ CTcGramProdAlt *alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_); if (prod != 0) prod->add_alt(alt); /* flatten the grammar rules */ CTcPrsGramNode *tree = flatten_gram_rule(err); if (tree == 0 || *err != 0) return; /* * install the tree as the nested token source, so that we read tokens * from our expanded set of grammar rules rather than from the original * input stream */ G_tok->set_external_source(tree); /* parse the token specification list */ for (int done = FALSE ; !done ; ) { CTcGramProdTok *tok; /* see what we have */ switch(G_tok->cur()) { case TOKT_LBRACK: { int skip_to_close; /* presume we won't need to skip anything */ skip_to_close = FALSE; /* make sure we're at the start of the alternative */ if (alt->get_tok_head() != 0) G_tok->log_warning(TCERR_GRAMMAR_QUAL_NOT_FIRST); /* skip the open bracket and make sure a symbol follows */ if (G_tok->next() == TOKT_SYM) { /* check what qualifier we have */ if (G_tok->cur_tok_matches("badness", 7)) { int val; /* parse the integer-valued qualifier */ val = parse_gram_qual_int(err, "badness", &done); if (*err != 0) return; /* set the badness for the alternative */ alt->set_badness(val); } else { /* not a recognized qualifier */ G_tok->log_error_curtok(TCERR_BAD_GRAMMAR_QUAL); /* skip remaining tokens to the ']' */ skip_to_close = TRUE; } /* check for proper close if we're is okay so far */ if (!skip_to_close) { /* make sure we're at the close bracket */ if (G_tok->cur() != TOKT_RBRACK) { /* log an error */ G_tok->log_error_curtok( TCERR_GRAMMAR_QUAL_REQ_RBRACK); /* skip to the bracket */ skip_to_close = TRUE; } else { /* skip the bracket */ G_tok->next(); } } } else { /* log the error */ G_tok->log_error_curtok(TCERR_GRAMMAR_QUAL_REQ_SYM); /* skip to the end */ skip_to_close = TRUE; } /* * If we encountered an error, skip ahead until we find * a ']' or something that looks like the end of the * statement. */ if (skip_to_close) { /* skip to the end of the qualifier */ parse_gram_qual_skip(err, &done); if (*err) return; } } /* done */ break; case TOKT_COLON: /* this is the end of the list - we're done */ G_tok->next(); done = TRUE; break; case TOKT_OR: /* * alternator - create a new alternative and add it to the * production's list */ alt = new (G_prsmem) CTcGramProdAlt(gram_obj, dict_cur_); if (prod != 0) prod->add_alt(alt); /* skip the '|' token and proceed to the next token list */ G_tok->next(); break; case TOKT_SSTR: case TOKT_DSTR: /* * quoted string (single or double) - this gives a literal * string to match */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_literal(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* add it to the current alternative's list */ alt->add_tok(tok); /* * go check for an arrow (to assign the matching token to a * property) */ goto check_for_arrow; case TOKT_LT: /* * part-of-speech list - this gives a list, enclosed in angle * brackets, of part-of-speech properties that can be matched * by the token */ /* skip the open angle bracket */ G_tok->next(); /* set up a new token with a part-of-speech list */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_part_list(); /* add it to the current alternative's list */ alt->add_tok(tok); /* keep going until we reach the closing bracket */ for (;;) { CTcSymbol *sym; /* see what we have */ switch(G_tok->cur()) { case TOKT_GT: /* * closing angle bracket - we're done, so go check for * an arrow to assign the match to a property */ goto check_for_arrow; case TOKT_EOF: /* log the error */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); /* give up */ *err = TRUE; return; case TOKT_SYM: /* * symbol - look it up, defining it as a property if * it's not already defined as a property */ sym = global_symtab_->find_or_def_prop_explicit( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE); /* make sure it's a property */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { CTcSymProp *propsym = (CTcSymProp *)sym; /* add the property to the token's list */ tok->add_match_part_ele(propsym->get_prop()); } else { /* flag an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_REQ_PROP); } /* we're done with the symbol, so skip it */ G_tok->next(); /* back for more */ break; default: /* * anything else is an error; assume the '>' was * missing, so just flag the error and then act as * though we reached the end of the list */ G_tok->log_error_curtok(TCERR_GRAMMAR_LIST_UNCLOSED); goto check_for_arrow; } } /* NOT REACHED */ case TOKT_SYM: /* * symbol token - this gives a part-of-speech or * sub-production match, depending on whether the symbol * refers to a property (in which case we have a * part-of-speech match) or an object (in which case we have a * sub-production match). */ { CTcSymbol *sym; /* look it up in the global symbol table */ sym = global_symtab_->find(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* * if it's not defined, and we're not in syntax-only mode, * provide a default definition of the symbol as a * production object */ if (sym == 0 && !syntax_only_) { /* it's undefined - presume it's a production */ CTcGramProdEntry *sub_prod = funcs->declare_gramprod( G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* if the symbol if still undefined it's an error */ if (sub_prod == 0) return; /* get the production's symbol */ sym = sub_prod->get_prod_sym(); } /* check what kind of symbol we have */ if (sym == 0) { /* * we're in parse-only mode and we have no symbol - * we can just ignore this until we're really * compiling */ tok = 0; } else if (sym->get_type() == TC_SYM_OBJ) { /* we know it's an object symbol - cast it */ CTcSymObj *objsym = (CTcSymObj *)sym; /* make sure it's a production */ if (objsym->get_metaclass() != TC_META_GRAMPROD) G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_PROD); /* create the production token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_prod(objsym); } else if (sym->get_type() == TC_SYM_PROP) { CTcSymProp *propsym = (CTcSymProp *)sym; /* create the part-of-speech token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_part_of_speech(propsym->get_prop()); } else if (sym->get_type() == TC_SYM_ENUM) { /* get the enum symbol properly cast */ CTcSymEnum *enumsym = (CTcSymEnum *)sym; /* enforce the 'enum token' rule if applicable */ funcs->check_enum_tok(enumsym); /* create the token-type token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_token_type(enumsym->get_enum_id()); } else { /* it's an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_OBJ_OR_PROP); /* no token */ tok = 0; } } /* if we have a token, add it to the alternative's list */ if (tok != 0) alt->add_tok(tok); check_for_arrow: /* skip the symbol and check for a '->' specification */ if (G_tok->next() == TOKT_ARROW) { /* skip the arrow and make sure a symbol follows */ if (G_tok->next() == TOKT_SYM) { CTcSymbol *sym; /* look it up */ sym = funcs->look_up_prop(G_tok->getcur(), FALSE); /* set the association if we got a symbol */ if (sym != 0) { CTcSymProp *propsym = (CTcSymProp *)sym; size_t i; /* set the property association for the token */ if (tok != 0) tok->set_prop_assoc(propsym->get_prop()); /* * Add the property to our list of assigned * properties - we'll use this to build the * grammar info list for the match object. Only * add the property if it's not already in the * list. */ for (i = 0 ; i < arrows->cnt ; ++i) { /* if we found it, stop looking */ if (arrows->prop[i] == propsym) break; } /* * if we didn't find it, and we have room for * another, add it */ if (i == arrows->cnt && arrows->cnt < arrows->max_arrows) { /* it's not there - add it */ arrows->prop[arrows->cnt++] = propsym; } } else { /* log an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP); } /* skip the symbol */ G_tok->next(); } else { /* it's an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_ARROW_REQ_PROP); } } break; case TOKT_TIMES: /* free-floating end token */ tok = new (G_prsmem) CTcGramProdTok(); tok->set_match_star(); /* add it to the current alternative's list */ alt->add_tok(tok); /* skip the star */ G_tok->next(); /* * this must be the last token in the alternative, so we * must have a ':' or '|' following */ if (G_tok->cur() != TOKT_OR && G_tok->cur() != TOKT_COLON) G_tok->log_error_curtok(TCERR_GRAMMAR_STAR_NOT_END); break; case TOKT_EOF: /* let the compiler interface decide whether it's an error */ funcs->on_eof(err); if (*err) return; /* in any case, it's the end of the object */ done = TRUE; break; case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: /* * they probably meant to end the statement, even though * this isn't the time or place to do so - log an error and * stop scanning */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); done = TRUE; break; default: /* log an error */ G_tok->log_error_curtok(TCERR_GRAMMAR_INVAL_TOK); /* * skip the offending token, and hope that they merely * inserted something invalid */ G_tok->next(); break; } } } /* * Flatten a set of grammar rules. Each time we find a parenthesized * alternator, we'll expand the alternatives, until we have no * parenthesized alternators. */ CTcPrsGramNode *CTcParser::flatten_gram_rule(int *err) { /* first, build the top-level 'or' tree */ CTcPrsGramNode *tree = parse_gram_or(err, 0); if (tree == 0 || *err != 0) return 0; /* move all of the OR's to a single OR at the top of the tree */ tree = tree->consolidate_or(); /* flatten CAT nodes in the resulting tree */ tree->flatten_cat(); /* * if the top-level node isn't an OR, insert an OR at the top - this * makes the tree always follow the same shape, which makes it easier * to step through the tokens in the expansion */ if (!tree->is_or()) { CTcPrsGramNodeOr *new_or; /* create an OR node for the root of the tree */ new_or = new (G_prsmem) CTcPrsGramNodeOr(); /* insert our old root as the single alternative under the OR */ new_or->add_sub_item(tree); /* the OR is now the root of the tree */ tree = new_or; } /* start reading from the first token in the tree */ tree->init_expansion(); /* return the tree */ return tree; } /* * Parse an OR expression in a grammar rule. Returns an OR node * representing the expression. */ CTcPrsGramNode *CTcParser::parse_gram_or(int *err, int level) { CTcPrsGramNodeOr *or_node; CTcPrsGramNode *sub; /* build our 'or' node */ or_node = new (G_prsmem) CTcPrsGramNodeOr(); /* if the rule is completely empty, warn about it */ if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_) G_tok->log_warning(TCERR_GRAMMAR_EMPTY); /* parse our initial 'cat' subnode */ sub = parse_gram_cat(err, level + 1); /* abort on error */ if (sub == 0 || *err != 0) return 0; /* add the subnode to our 'or' list */ or_node->add_sub_item(sub); /* keep going as long as we have more rules appended with '|' */ while (G_tok->cur() == TOKT_OR) { /* skip the '|' */ G_tok->next(); /* * if we're at the top level, and the next token is the closing * ':', warn about the empty last rule */ if (level == 0 && G_tok->cur() == TOKT_COLON && !syntax_only_) G_tok->log_warning(TCERR_GRAMMAR_ENDS_WITH_OR); /* parse the 'cat' subnode */ sub = parse_gram_cat(err, level + 1); /* abort on error */ if (sub == 0 || *err != 0) return 0; /* add the subnode to our 'or' list */ or_node->add_sub_item(sub); } /* return the 'or' list */ return or_node; } /* * Parse a concatenation expression in a grammar rule. Returns a CAT node * representing the expression. */ CTcPrsGramNode *CTcParser::parse_gram_cat(int *err, int level) { CTcPrsGramNodeCat *cat_node; CTcPrsGramNode *sub; /* build our concatenation node */ cat_node = new (G_prsmem) CTcPrsGramNodeCat(); /* add tokens to the cat list until we find the end of the list */ for (;;) { /* see what we have */ switch(G_tok->cur()) { case TOKT_LPAR: /* skip the paren */ G_tok->next(); /* parse the OR expression */ sub = parse_gram_or(err, level + 1); /* if that failed, abort */ if (sub == 0 || *err != 0) return 0; /* add the 'OR' to our concatenation expression */ cat_node->add_sub_item(sub); /* make sure we're at the close paren */ if (G_tok->cur() != TOKT_RPAR) G_tok->log_error_curtok(TCERR_GRAMMAR_REQ_RPAR_AFTER_GROUP); else G_tok->next(); /* * make sure we don't have an arrow immediately following; an * arrow isn't allowed after a close paren, because a group * isn't a true subproduction */ if (G_tok->cur() == TOKT_ARROW) G_tok->log_error(TCERR_GRAMMAR_GROUP_ARROW_NOT_ALLOWED); /* done with the 'or' expression */ break; case TOKT_OR: /* * the 'or' has lower precedence than concatenation, so we've * reached the end of the concatenation expression - simply * return what we have so far and let the caller figure out * where to go next */ return cat_node; case TOKT_COLON: case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_EOF: case TOKT_RPAR: /* we've reached the end of the rule */ return cat_node; default: /* anything else is simply concatenated to the list so far */ cat_node->add_sub_item( new (G_prsmem) CTcPrsGramNodeTok(G_tok->getcur())); /* skip the token and keep scanning */ G_tok->next(); break; } } } /* * Parse a grammar qualifier integer value */ int CTcParser::parse_gram_qual_int(int *err, const char *qual_name, int *stm_end) { CTcPrsNode *expr; CTcConstVal *cval; /* skip the qualifier name */ G_tok->next(); /* check for a missing expression */ if (G_tok->cur() == TOKT_RBRACK) { /* don't bother looking for an expression - it's not there */ expr = 0; } else { /* parse an expression */ expr = parse_expr(); } /* * if we didn't get an expression, or it doesn't have a constant * value, or the constant value is something other than an integer, * it's an error */ if (expr == 0 || (cval = expr->get_const_val()) == 0 || cval->get_type() != TC_CVT_INT) { /* log an error */ G_tok->log_error(TCERR_GRAMMAR_QUAL_REQ_INT, qual_name); /* skip to the closing bracket */ parse_gram_qual_skip(err, stm_end); /* we don't have a value to return; use zero by default */ return 0; } else { /* return the constant expression value */ return cval->get_val_int(); } } /* * Skip to the end of a grammar qualifier. This is used when a syntax * error occurs and we abandon parsing the rest of the qualifier. */ void CTcParser::parse_gram_qual_skip(int *err, int *stm_end) { /* scan until we find the end */ for (;;) { switch(G_tok->next()) { case TOKT_RBRACK: /* * that's the end of the mal-formed qualifier; skip the * bracket and keep going from here */ G_tok->next(); break; case TOKT_EOF: case TOKT_RBRACE: case TOKT_LBRACE: case TOKT_SEM: /* * probably the end of the statement - stop scanning, and * set the 'stm_end' flag to tell the caller that we're done * parsing the entire statement */ *stm_end = TRUE; return; default: /* skip everything else and just keep going */ break; } } } /* ------------------------------------------------------------------------ */ /* * Grammar production list entry */ CTcGramProdEntry::CTcGramProdEntry(CTcSymObj *prod_sym) { /* remember my object symbol */ prod_sym_ = prod_sym; /* not in a list yet */ nxt_ = 0; /* no alternatives yet */ alt_head_ = alt_tail_ = 0; /* not explicitly declared yet */ is_declared_ = FALSE; } /* * Add an alternative */ void CTcGramProdEntry::add_alt(CTcGramProdAlt *alt) { /* link it at the end of my list */ if (alt_tail_ != 0) alt_tail_->set_next(alt); else alt_head_ = alt; alt_tail_ = alt; /* this is now the last element in our list */ alt->set_next(0); } /* * Move my alternatives to a new owner */ void CTcGramProdEntry::move_alts_to(CTcGramProdEntry *new_entry) { CTcGramProdAlt *alt; CTcGramProdAlt *nxt; /* move each of my alternatives */ for (alt = alt_head_ ; alt != 0 ; alt = nxt) { /* remember the next alternative, since we're unlinking this one */ nxt = alt->get_next(); /* unlink this one from the list */ alt->set_next(0); /* link this one into the new owner's list */ new_entry->add_alt(alt); } /* there's nothing left in our list */ alt_head_ = alt_tail_ = 0; } /* ------------------------------------------------------------------------ */ /* * Grammar production alternative */ CTcGramProdAlt::CTcGramProdAlt(CTcSymObj *obj_sym, CTcDictEntry *dict) { /* remember the associated processor object */ obj_sym_ = obj_sym; /* remember the default dictionary currently in effect */ dict_ = dict; /* nothing in our token list yet */ tok_head_ = tok_tail_ = 0; /* we don't have a score or badness yet */ score_ = 0; badness_ = 0; /* we're not in a list yet */ nxt_ = 0; } void CTcGramProdAlt::add_tok(CTcGramProdTok *tok) { /* link the token at the end of my list */ if (tok_tail_ != 0) tok_tail_->set_next(tok); else tok_head_ = tok; tok_tail_ = tok; /* there's nothing after this token */ tok->set_next(0); } /* ------------------------------------------------------------------------ */ /* * Grammar production token object */ /* * Initialize with a part-of-speech list */ void CTcGramProdTok::set_match_part_list() { const size_t init_alo = 10; /* remember the type */ typ_ = TCGRAM_PART_OF_SPEECH_LIST; /* we have nothing in the list yet */ val_.prop_list_.len_ = 0; /* set the initial allocation size */ val_.prop_list_.alo_ = init_alo; /* allocate the initial list */ val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc( init_alo * sizeof(val_.prop_list_.arr_[0])); } /* * Add a property to our part-of-speech match list */ void CTcGramProdTok::add_match_part_ele(tctarg_prop_id_t prop) { /* if necessary, re-allocate the array at a larger size */ if (val_.prop_list_.len_ == val_.prop_list_.alo_) { tctarg_prop_id_t *oldp; /* bump up the size a bit */ val_.prop_list_.alo_ += 10; /* remember the current list long enough to copy it */ oldp = val_.prop_list_.arr_; /* reallocate it */ val_.prop_list_.arr_ = (tctarg_prop_id_t *)G_prsmem->alloc( val_.prop_list_.alo_ * sizeof(val_.prop_list_.arr_[0])); /* copy the old list into the new one */ memcpy(val_.prop_list_.arr_, oldp, val_.prop_list_.len_ * sizeof(val_.prop_list_.arr_[0])); } /* * we now know we have space for the new element, so add it, bumping up * the length counter to account for the addition */ val_.prop_list_.arr_[val_.prop_list_.len_++] = prop; } frobtads-1.2.3/tads3/vmhost.h0000644000175000001440000002404111713617316015222 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhost.h - T3 VM host interface Function The host interface defines a set of services that the VM obtains from its host application environment. The VM uses the host interface to obtain these services so that the VM doesn't have to make too many assumptions about the larger application of which it is a subsystem. Every application containing the VM must provide the VM with an implementation of this interface. Notes Modified 07/29/99 MJRoberts - Creation */ #ifndef VMHOST_H #define VMHOST_H #include "os.h" /* ------------------------------------------------------------------------ */ /* * I/O Safety Levels. These are defined as integers, not an enum, because * they form a hierarchy; a higher value imposes all of the restrictions of * all lower values, plus additional restrictions of its own. * * In the past, there was a single safety level that controlled both read * and write. In 3.1 we separated the read and write levels. Because * of the history, each level specifies separate read and write permissions * that were formerly combined in that level. The descriptions still apply * even though we track the levels separately - some levels must be * interpreted differently for reading vs writing. For example, if the * read level is 3, it allows current-directory reading, but if the write * level is 3, it denies all write access. Separating the levels allows * for combinations that weren't formerly possible: for exmaple, -s04 (read * 0, write 4) allows reading anywhere but writing nowhere. */ /* level 0: minimum safety; read/write any file */ const int VM_IO_SAFETY_MINIMUM = 0; /* level 1: read any file, write only to files in the current directory */ const int VM_IO_SAFETY_READ_ANY_WRITE_CUR = 1; /* level 2: read/write in current directory only */ const int VM_IO_SAFETY_READWRITE_CUR = 2; /* level 3: read from current directory only, no writing allowed */ const int VM_IO_SAFETY_READ_CUR = 3; /* level 4: maximum safety; no file reading or writing allowed */ const int VM_IO_SAFETY_MAXIMUM = 4; /* ------------------------------------------------------------------------ */ /* * Network safety levels. */ /* level 0: minimum safety; all network access */ const int VM_NET_SAFETY_MINIMUM = 0; /* level 1: localhost access only */ const int VM_NET_SAFETY_LOCALHOST = 1; /* level 2: no network access */ const int VM_NET_SAFETY_MAXIMUM = 2; /* ------------------------------------------------------------------------ */ /* * get_image_name return codes */ enum vmhost_gin_t { /* * get_image_name() call ignored - this is returned if the host * system doesn't have a way of asking the user for an image name, * so the caller must rely on whatever other way it has of finding * the image name (which usually means that it can't find a image * name at all, given that it would only call get_image_name() when * it can't otherwise find the name) */ VMHOST_GIN_IGNORED, /* * user chose not to supply an image name - this is returned when, * for example, the user cancelled out of a file selector dialog */ VMHOST_GIN_CANCEL, /* * error - the host system can't prompt for a filename because of * some kind of error (not enough memory to load a dialog, for * example) */ VMHOST_GIN_ERROR, /* success */ VMHOST_GIN_SUCCESS }; /* ------------------------------------------------------------------------ */ /* * T3 VM Host Application Interface. This is an abstract class; it must * be implemented by each application that embeds the VM. */ class CVmHostIfc { public: virtual ~CVmHostIfc() { } /* * Get the file I/O safety read and write levels. These allow the host * application to control the file operations that a program running * under the VM may perform. See the VM_IO_SAFETY_xxx values above. */ virtual int get_io_safety_read() = 0; virtual int get_io_safety_write() = 0; /* * set the I/O safety level - this should only be done in response * to a user preference setting (such as via a command-line option), * never as a result of some programmatic operation by the executing * image */ virtual void set_io_safety(int read_level, int write_level) = 0; /* * Get the network safety level settings. This works like the I/O * safety level, but applies to network access. The client level * controls the game's access to network services as a client, such as * the game making http requests of a web server. The server level * controls the game's ability to create network services that accept * incoming connections from other processes and machines. */ virtual void get_net_safety(int *client_level, int *server_level) = 0; /* * Set the network safety level. */ virtual void set_net_safety(int client_level, int server_level) = 0; /* * Get the resource loader for system resources (character mapping * tables, the timezone database, etc). This resource loader should be * set up to load resources out of the VM executable or out of the * directory containing the VM executable, since these resources are * associated with the VM itself, not the T3 program executing under * the VM. */ virtual class CResLoader *get_sys_res_loader() = 0; /* * Set the image file name. The VM calls this after it learns the * name of the image file so that the host system can access the * file if necessary. */ virtual void set_image_name(const char *fname) = 0; /* * Set the root directory path for individual resources (such as * individual image and sound resources) that we don't find in the * image file or any attached resource collection file. If this is * never called, the directory containing the image file should be used * as the resource root directory. */ virtual void set_res_dir(const char *fname) = 0; /* * Add a resource collection file. The return value is a non-zero file * number assigned by the host system; the VM uses this number in * subsequent calls to add_resource() to add resources from this file. * The VM cannot add any resources for a file until it first adds the * file with this routine. */ virtual int add_resfile(const char *fname) = 0; /* * Determine if additional resource files are supported - if this * returns true, add_resfile() can be used, otherwise add_resfile() * will have no effect. */ virtual int can_add_resfiles() = 0; /* * Add a resource map entry. 'ofs' is the byte offset of the start * of the resource within the file, and 'siz' is the size in bytes * of the resource data. The resource is stored as contiguous bytes * starting at the given file offset and running for the given size. * The 'fileno' is zero for the image file, or the number assigned * by the host system in add_resfile() for other resource files. */ virtual void add_resource(unsigned long ofs, unsigned long siz, const char *res_name, size_t res_name_len, int fileno) = 0; /* * Add a resource link map entry. This creates an association between * a compiled resource name and a filename in the local file system. * This is used primarily for debugging purposes, so that the compiler * can avoid copying the resource data, instead simply placing a link * for each resource to its local file copy. */ virtual void add_resource(const char *fname, size_t fnamelen, const char *res_name, size_t res_name_len) = 0; /* * Find a resource. Returns an osfildef* handle to the open resource * file if the resource can be found; the file on return will have its * seek position set to the first byte of the resource in the file, * and *res_size will be set to the size in bytes of the resource * data. If there is no such resource, returns null. */ virtual osfildef *find_resource(const char *resname, size_t resname_len, unsigned long *res_size) = 0; /* * Get the external resource file path. If we should look for * resource files in a different location than the image file, the * host system can set this to a path that we should use to look for * resource files. If this is null, the VM should simply look in * the same directory that contains the image file. If the host * system provides a path via this routine, the path string must * have a trailing separator character if required, so that we can * directly append a name to this path to form a valid * fully-qualified filename. */ virtual const char *get_res_path() = 0; /* * Determine if a resource exists. Returns true if so, false if * not. */ virtual int resfile_exists(const char *res_name, size_t res_name_len) = 0; /* * Ask the user to supply an image file name. We'll call this * routine only if we can't obtain an image file from command line * arguments or from an attachment to the executable. */ virtual vmhost_gin_t get_image_name(char *buf, size_t buflen) = 0; /* * Get a special system filename path. This is essentially a wrapper * for os_get_special_path() that encapsulates the information needed * for that function. The 'id' value has the same meaning as in * os_get_special_path() (see tads2/osifc.h). */ virtual void get_special_file_path(char *buf, size_t buflen, int id) = 0; }; #endif /* VMHOST_H */ frobtads-1.2.3/tads3/vmbiftio.cpp0000644000175000001440000021754512145504453016066 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftio.cpp - TADS Input/Output functions Function Notes Modified 02/08/00 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "charmap.h" #include "vmbiftio.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmfile.h" #include "vmconsol.h" #include "vmstrres.h" #include "vmvsn.h" #include "vmhost.h" #include "vmpredef.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmfilnam.h" #include "vmnetfil.h" #include "vmnet.h" /* ------------------------------------------------------------------------ */ /* * Display a value */ void CVmBifTIO::say(VMG_ uint argc) { /* write the value to the main console */ say_to_console(vmg_ G_console, argc); } /* * Display the value or values at top of stack to the given console */ void CVmBifTIO::say_to_console(VMG_ CVmConsole *console, uint argc) { vm_val_t val; const char *str; char buf[30]; size_t len; vm_val_t new_str; /* presume we won't need to create a new string value */ new_str.set_nil(); /* display each argument */ for ( ; argc != 0 ; --argc) { /* get our argument */ G_stk->pop(&val); /* see what we have */ switch(val.typ) { case VM_SSTRING: /* get the string */ str = G_const_pool->get_ptr(val.val.ofs); /* the original value is our string */ new_str = val; disp_str: /* push the string to protect from garbage collection */ G_stk->push(&new_str); /* display the string through the output formatter */ console->format_text(vmg_ str + 2, osrp2(str)); /* discard the saved string now that we no longer need it */ G_stk->discard(); /* done */ break; case VM_OBJ: /* convert it to a string */ str = vm_objp(vmg_ val.val.obj) ->cast_to_string(vmg_ val.val.obj, &new_str); /* go display it */ goto disp_str; case VM_LIST: /* convert to a string */ str = val.cast_to_string(vmg_ &new_str); goto disp_str; case VM_INT: /* convert it to a string */ sprintf(buf + 2, "%ld", (long)val.val.intval); /* set its length */ len = strlen(buf + 2); oswp2(buf, len); /* display it */ str = buf; goto disp_str; case VM_NIL: /* display nothing */ break; default: /* other types are invalid */ err_throw(VMERR_BAD_TYPE_BIF); } } } /* ------------------------------------------------------------------------ */ /* * Logging - turn on or off output text capture */ #define LOG_SCRIPT 1 #define LOG_CMD 2 #define LOG_EVENT 3 void CVmBifTIO::logging(VMG_ uint argc) { /* presume success */ int ok = TRUE; /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the arguments: filename, log type */ const vm_val_t *filespec = G_stk->get(0); int log_type = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : LOG_SCRIPT); /* * if they passed us nil, turn off logging; otherwise, start logging * to the filename given by the string */ if (filespec->typ == VM_NIL) { /* turn off the appropriate type of logging */ switch(log_type) { case LOG_SCRIPT: G_console->close_log_file(vmg0_); break; case LOG_CMD: case LOG_EVENT: G_console->close_command_log(vmg0_); break; default: err_throw(VMERR_BAD_VAL_BIF); } } else { /* set up the recursive call context */ vm_rcdesc rc(vmg_ "setLogFile", bif_table, 1, G_stk->get(0), argc); /* open the appropriate log file */ switch(log_type) { case LOG_SCRIPT: ok = !G_console->open_log_file(vmg_ filespec, &rc); break; case LOG_CMD: case LOG_EVENT: ok = !G_console->open_command_log( vmg_ filespec, &rc, log_type == LOG_EVENT); break; default: err_throw(VMERR_BAD_VAL_BIF); } } /* discard arguments */ G_stk->discard(argc); /* return true on success, nil on error */ retval_bool(vmg_ ok); } /* ------------------------------------------------------------------------ */ /* * clearscreen - clear the main display screen */ void CVmBifTIO::clearscreen(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* ask the console to clear the screen */ G_console->clear_window(vmg0_); } /* ------------------------------------------------------------------------ */ /* * more - show MORE prompt */ void CVmBifTIO::more(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* ignore in web host mode */ if (G_net_config != 0) return; /* * if we're reading from a script, ignore this request - these types of * interactive pauses are irrelevant when reading a script, since we're * getting our input non-interactively */ if (G_console->is_reading_script()) return; /* flush the display output */ G_console->flush_all(vmg_ VM_NL_NONE); /* show the MORE prompt */ G_console->show_more_prompt(vmg0_); } /* ------------------------------------------------------------------------ */ /* * input - get a line of input from the keyboard */ void CVmBifTIO::input(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* read a line of text from the keyboard */ char buf[256]; if (G_console->read_line(vmg_ buf, sizeof(buf))) { /* end of file - return nil */ retval_nil(vmg0_); } else { /* return the string */ retval_str(vmg_ buf); } } /* ------------------------------------------------------------------------ */ /* * inputkey - read a keystroke */ void CVmBifTIO::inputkey(VMG_ uint argc) { char buf[32]; char c[10]; size_t len; int evt; static const int filter[] = { OS_EVT_KEY }; /* check arguments */ check_argc(vmg_ argc, 0); /* check for script input */ if (G_console->read_event_script( vmg_ &evt, buf, sizeof(buf), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a key from the script */ retval_ui_str(vmg_ buf); /* log the event */ G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), FALSE); /* done */ return; } /* flush any output */ G_console->flush_all(vmg_ VM_NL_INPUT); /* there's no console in web host mode - simply return eof */ if (G_net_config != 0) { retval_ui_str(vmg_ "[eof]"); return; } /* get a keystroke */ c[0] = (char)os_getc_raw(); len = 1; /* if it's an extended key, map it specially */ if (c[0] == 0) { char extc; /* get the second part of the sequence */ extc = (char)os_getc_raw(); /* map the extended key */ map_ext_key(vmg_ buf, (unsigned char)extc); } else { /* continue fetching bytes until we have a full character */ while (!raw_key_complete(vmg_ c, len) && len + 1 < sizeof(c)) { /* * We don't yet have enough bytes for a complete character, so * read another raw byte. The keyboard driver should already * have queued up all of the bytes we need to complete the * character sequence, so there should never be a delay from * os_getc_raw() here - it should simply return the next byte * of the sequence immediately. */ c[len++] = (char)os_getc_raw(); } c[len] = '\0'; /* * translate the key from the local character set to UTF-8, and map * the extended key code to the portable representation */ map_raw_key(vmg_ buf, c, len); } /* reset the [MORE] counter */ G_console->reset_line_count(FALSE); /* log the event */ G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), TRUE); /* return the string */ retval_str(vmg_ buf); } /* ------------------------------------------------------------------------ */ /* * inputevent - read an event */ void CVmBifTIO::inputevent(VMG_ uint argc) { int use_timeout; unsigned long timeout; os_event_info_t info; int evt; int ele_count; vm_obj_id_t lst_obj; CVmObjList *lst; char keyname[32]; vm_val_t val; int from_script = FALSE; static const int filter[] = { OS_EVT_KEY, OS_EVT_TIMEOUT, OS_EVT_NOTIMEOUT, OS_EVT_HREF, OS_EVT_EOF, OS_EVT_COMMAND }; /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's a timeout argument, get it */ if (argc == 0) { /* there's no timeout */ use_timeout = FALSE; timeout = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* the timeout is nil, which is the same as no timeout */ use_timeout = FALSE; timeout = 0; /* discard the nil timeout value */ G_stk->discard(); } else { /* pop the timeout value */ timeout = pop_long_val(vmg0_); /* note that we have a timeout to use */ use_timeout = TRUE; } /* check for script input */ if (G_console->read_event_script( vmg_ &evt, info.href, sizeof(info.href), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a script event - note it */ from_script = TRUE; /* translate certain events */ switch (evt) { case OS_EVT_COMMAND: /* read the numeric parameter */ info.cmd_id = atoi(info.href); break; } } else if (G_net_config != 0) { /* there's no console in web host mode - return eof */ evt = OS_EVT_EOF; } else { /* flush any buffered output */ G_console->flush_all(vmg_ VM_NL_INPUT); /* reset the [MORE] counter */ G_console->reset_line_count(FALSE); /* read an event from the OS layer */ evt = os_get_event(timeout, use_timeout, &info); } /* figure out how big a list we need to allocate */ switch(evt) { case OS_EVT_KEY: /* * we need two elements - one for the event type code, one for the * keystroke string */ ele_count = 2; break; case OS_EVT_COMMAND: /* we need a second element for the command ID */ ele_count = 2; break; case OS_EVT_HREF: /* * we need two elements - one for the event type code, one for the * HREF string */ ele_count = 2; break; default: /* for anything else, we need only the event type code element */ ele_count = 1; break; } /* create the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, ele_count); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* fill in the first element with the event type code */ val.set_int(evt); lst->cons_set_element(0, &val); /* set additional elements, according to the event type */ switch(evt) { case OS_EVT_KEY: /* map the extended or ordinary key, as appropriate */ if (from_script) { /* we got a key from the script - it's in the 'href' field */ val.set_obj(str_from_ui_str(vmg_ info.href)); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, info.href, strlen(info.href), FALSE); } else if (info.key[0] == 0) { /* it's an extended key */ map_ext_key(vmg_ keyname, info.key[1]); /* create a string for the key name */ val.set_obj(CVmObjString::create( vmg_ FALSE, keyname, strlen(keyname))); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE); } else { char c[4]; size_t len; /* fetch more bytes until we have a complete character */ for (c[0] = (char)info.key[0], len = 1 ; !raw_key_complete(vmg_ c, len) && len < sizeof(c) ; ) { /* * Read another input event. The keyboard driver should * already have queued up all of the bytes needed to * complete this character sequence, so there should never * be a delay from os_get_event() here - it should simply * return immediately with another OS_EVT_KEY event with * the next byte of the sequence. */ evt = os_get_event(0, FALSE, &info); /* * if it's not a keystroke event, something's wrong - * ignore the event and stop trying to read the remaining * bytes of the character sequence */ if (evt != OS_EVT_KEY) break; /* store the next byte of the sequence */ c[len++] = (char)info.key[0]; } /* it's an ordinary key - map it */ map_raw_key(vmg_ keyname, c, len); /* create a string for the key name */ val.set_obj(CVmObjString::create( vmg_ FALSE, keyname, strlen(keyname))); /* log the event */ G_console->log_event( vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE); } /* add it to the list */ lst->cons_set_element(1, &val); break; case OS_EVT_HREF: /* create the string for the href text */ val.set_obj(str_from_ui_str(vmg_ info.href)); /* add it to the list */ lst->cons_set_element(1, &val); /* log it */ G_console->log_event(vmg_ OS_EVT_HREF, info.href, strlen(info.href), FALSE); break; case OS_EVT_COMMAND: /* the second element is the command ID code */ val.set_int(info.cmd_id); lst->cons_set_element(1, &val); /* log it */ { char buf[20]; sprintf(buf, "%d", info.cmd_id); G_console->log_event(vmg_ OS_EVT_COMMAND, buf, strlen(buf), TRUE); } break; default: /* other event types have no extra data */ G_console->log_event(vmg_ evt); break; } /* return the list */ retval_obj(vmg_ lst_obj); /* we can drop the garbage collection protection now */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Service routine: Map an "extended" keystroke from raw os_getc_raw() * notation to a UTF-8 key name. The caller should pass the second byte of * the extended two-byte raw sequence. */ int CVmBifTIO::map_ext_key(VMG_ char *namebuf, int extc) { /* * Portable key names for the extended keystrokes. We map the extended * key codes to these strings, so that the TADS code can access arrow * keys and the like. */ static const char *ext_key_names[] = { "[up]", /* CMD_UP - 1 */ "[down]", /* CMD_DOWN - 2 */ "[right]", /* CMD_RIGHT - 3 */ "[left]", /* CMD_LEFT - 4 */ "[end]", /* CMD_END - 5 */ "[home]", /* CMD_HOME - 6 */ "[del-eol]", /* CMD_DEOL - 7 */ "[del-line]", /* CMD_KILL - 8 */ "[del]", /* CMD_DEL - 9 */ "[scroll]", /* CMD_SCR - 10 */ "[page up]", /* CMD_PGUP - 11 */ "[page down]", /* CMD_PGDN - 12 */ "[top]", /* CMD_TOP - 13 */ "[bottom]", /* CMD_BOT - 14 */ "[f1]", /* CMD_F1 - 15 */ "[f2]", /* CMD_F2 - 16 */ "[f3]", /* CMD_F3 - 17 */ "[f4]", /* CMD_F4 - 18 */ "[f5]", /* CMD_F5 - 19 */ "[f6]", /* CMD_F6 - 20 */ "[f7]", /* CMD_F7 - 21 */ "[f8]", /* CMD_F8 - 22 */ "[f9]", /* CMD_F9 - 23 */ "[f10]", /* CMD_F10 - 24 */ "[?]", /* invalid key - CMD_CHOME - 25 */ "[tab]", /* CMD_TAB - 26 */ "[?]", /* invalid key - shift-F2 - 27 */ "[?]", /* not used (obsoleted) - 28 */ "[word-left]", /* CMD_WORD_LEFT - 29 */ "[word-right]", /* CMD_WORD_RIGHT - 30 */ "[del-word]", /* CMD_WORDKILL - 31 */ "[eof]", /* CMD_EOF - 32 */ "[break]", /* CMD_BREAK - 33 */ "[insert]" /* CMD_INS - 34 */ }; /* if it's in the key name array, use the array entry */ if (extc >= 1 && extc <= (int)sizeof(ext_key_names)/sizeof(ext_key_names[0])) { /* use the array name */ strcpy(namebuf, ext_key_names[extc - 1]); return TRUE; } /* if it's in the ALT key range, generate an ALT key name */ if ((unsigned char)extc >= CMD_ALT && (unsigned char)extc <= CMD_ALT + 25) { /* generate an ALT key name */ strcpy(namebuf, "[alt-?]"); namebuf[5] = (char)(extc - CMD_ALT + 'a'); return TRUE; } /* it's not a valid key - use '[?]' as the name */ strcpy(namebuf, "[?]"); return FALSE; } /* * Service routine: Map a keystroke from the raw notation, consisting of a * normal keystroke in the local character set or an extended command key * using a CMD_xxx code, to UTF-8. If the keystroke is a control character * or any CMD_xxx code, we'll map the key to a high-level keystroke name * enclosed in square brackets. */ int CVmBifTIO::map_raw_key(VMG_ char *namebuf, const char *c, size_t len) { size_t outlen; /* if it's a control character, give it a portable key name */ if (len == 1 && ((c[0] >= 1 && c[0] <= 27) || c[0] == 127)) { switch(c[0]) { case 10: case 13: /* * return an ASCII 10 (regardless of local newline conventions * - this is the internal string representation, which we * define to use ASCII 10 to represent a newline everywhere) */ namebuf[0] = 10; namebuf[1] = '\0'; return TRUE; case 9: /* return ASCII 9 for TAB characters */ namebuf[0] = 9; namebuf[1] = '\0'; return TRUE; case 8: case 127: /* return '[bksp]' for backspace/del characters */ strcpy(namebuf, "[bksp]"); return TRUE; case 27: /* return '[esc]' for the escape key */ strcpy(namebuf, "[esc]"); return TRUE; default: /* return '[ctrl-X]' for other control characters */ strcpy(namebuf, "[ctrl-?]"); namebuf[6] = (char)(c[0] + 'a' - 1); return TRUE; } } /* map the character to wide Unicode */ outlen = 32; G_cmap_from_ui->map(&namebuf, &outlen, c, len); /* null-terminate the result */ *namebuf = '\0'; /* successfully mapped */ return TRUE; } /* * Service routine: determine if a raw byte sequence forms a complete * character in the local character set. */ int CVmBifTIO::raw_key_complete(VMG_ const char *c, size_t len) { /* ask the local character mapper if it's a complete character */ return G_cmap_from_ui->is_complete_char(c, len); } /* ------------------------------------------------------------------------ */ /* * Standard system button labels for bifinpdlg() */ #define BIFINPDLG_LBL_OK 1 #define BIFINPDLG_LBL_CANCEL 2 #define BIFINPDLG_LBL_YES 3 #define BIFINPDLG_LBL_NO 4 /* * inputdialog - run a dialog */ void CVmBifTIO::inputdialog(VMG_ uint argc) { int icon_id; char prompt[256]; char label_buf[256]; vm_val_t label_val[10]; const char *labels[10]; int lst_cnt; int std_btns; int btn_cnt; char *dst; size_t dstrem; int default_resp; int cancel_resp; int resp; char numbuf[32]; /* check arguments */ check_argc(vmg_ argc, 5); /* get the icon number */ icon_id = pop_int_val(vmg0_); /* get the prompt string */ pop_str_val_ui(vmg_ prompt, sizeof(prompt)); /* there aren't any buttons yet */ btn_cnt = 0; /* check for the button type */ if (G_stk->get(0)->typ == VM_INT) { /* get the standard button set ID */ std_btns = pop_int_val(vmg0_); } else if (G_stk->get(0)->is_listlike(vmg0_) && (lst_cnt = G_stk->get(0)->ll_length(vmg0_)) >= 0) { int i; vm_val_t *valp; /* we're not using any standard button set */ std_btns = 0; /* * run through the list and get the button items into our array * (we do this rather than traversing the list directly so that * we don't have to worry about a constant list's data being * paged out) */ vm_val_t *lst = G_stk->get(0); /* limit the number of elements to our private array size */ if (lst_cnt > (int)sizeof(label_val)/sizeof(label_val[0])) lst_cnt = sizeof(label_val)/sizeof(label_val[0]); /* copy the list */ for (i = 1, valp = label_val ; i <= lst_cnt ; ++i, ++valp) lst->ll_index(vmg_ valp, i); /* done with the list - discard it */ G_stk->discard(); /* set up to write into our label buffer */ dst = label_buf; dstrem = sizeof(label_buf); /* now build our internal button list from the array elements */ for (i = 0, valp = label_val ; i < lst_cnt ; ++i, ++valp) { const char *p; /* * We could have a number or a string in each element. If * the element is a number, it refers to a standard label. * If it's a string, use the string directly. */ if ((p = valp->get_as_string(vmg0_)) != 0) { size_t copy_len; /* * it's a string - make a copy in the label buffer, * making sure to leave space for null termination */ copy_len = vmb_get_len(p); if (copy_len > dstrem - 1) copy_len = utf8_ptr::s_trunc(p + VMB_LEN, dstrem - 1); memcpy(dst, p + VMB_LEN, copy_len); /* null-terminate the buffer */ dst[copy_len++] = '\0'; /* set this button to point to the converted text */ labels[btn_cnt++] = dst; /* skip past this label */ dst += copy_len; dstrem -= copy_len; } else if (valp->typ == VM_INT) { int id; int resid; char rscbuf[128]; /* it's a standard system label ID - get the ID */ id = (int)valp->val.intval; /* translate it to the appropriate string resource */ switch(id) { case BIFINPDLG_LBL_OK: resid = VMRESID_BTN_OK; break; case BIFINPDLG_LBL_CANCEL: resid = VMRESID_BTN_CANCEL; break; case BIFINPDLG_LBL_YES: resid = VMRESID_BTN_YES; break; case BIFINPDLG_LBL_NO: resid = VMRESID_BTN_NO; break; default: resid = 0; break; } /* * if we got a valid resource ID, load the resource; * otherwise, skip this button */ if (resid != 0 && !os_get_str_rsc(resid, rscbuf, sizeof(rscbuf))) { /* set this button to point to the converted text */ labels[btn_cnt++] = dst; /* convert the resource text to UTF-8 */ G_cmap_from_ui->map(&dst, &dstrem, rscbuf, strlen(rscbuf)); /* null-terminate the converted text */ *dst++ = '\0'; --dstrem; } } } } else { /* invalid button type */ err_throw(VMERR_BAD_TYPE_BIF); } /* get the default response */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil argument */ G_stk->discard(); /* there's no default response */ default_resp = 0; } else { /* get the default response index */ default_resp = pop_int_val(vmg0_); } /* get the cancel response */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil argument */ G_stk->discard(); /* there's no cancel response */ cancel_resp = 0; } else { /* get the cancel response index */ cancel_resp = pop_int_val(vmg0_); } /* check for script input */ static int filter[] = { VMCON_EVT_DIALOG }; int evt; if (G_console->read_event_script( vmg_ &evt, numbuf, sizeof(numbuf), filter, sizeof(filter)/sizeof(filter[0]), 0)) { /* we got a script response - no need to show the dialog */ resp = atoi(numbuf); /* log the event */ G_console->log_event(vmg_ VMCON_EVT_DIALOG, numbuf, strlen(numbuf), FALSE); } else if (G_net_config != 0) { /* there's no console in web host mode - return eof */ resp = 0; } else { /* flush output before showing the dialog */ G_console->flush_all(vmg_ VM_NL_INPUT); /* show the dialog */ resp = G_console->input_dialog(vmg_ icon_id, prompt, std_btns, labels, btn_cnt, default_resp, cancel_resp); /* log the event */ sprintf(numbuf, "%d", resp); G_console->log_event(vmg_ VMCON_EVT_DIALOG, numbuf, strlen(numbuf), TRUE); } /* return the result */ retval_int(vmg_ resp); } /* ------------------------------------------------------------------------ */ /* * askfile - ask for a filename via a standard file dialog */ void CVmBifTIO::askfile(VMG_ uint argc) { char prompt[256]; int dialog_type; os_filetype_t file_type; int result; char fname[OSFNMAX*3 + 1]; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; int from_script = FALSE; char warning[OSFNMAX + 255] = ""; int from_ui = FALSE; /* check arguments */ check_argc(vmg_ argc, 4); /* get the prompt string */ pop_str_val_ui(vmg_ prompt, sizeof(prompt)); /* get the dialog type and file type */ dialog_type = pop_int_val(vmg0_); file_type = (os_filetype_t)pop_int_val(vmg0_); /* * Pop and discard the flags. (This argument isn't used currently; * it's just there in case we need some option flags in the future. * Pop it as an integer to ensure the caller specified the correct * type, but discard the value.) */ (void)pop_long_val(vmg0_); /* check for a script response */ static int filter[] = { VMCON_EVT_FILE }; int evt; unsigned long attrs; if (G_console->read_event_script( vmg_ &evt, fname, sizeof(fname), filter, countof(filter), &attrs)) { int ok = TRUE; /* we got a response from the script */ from_script = TRUE; result = (fname[0] != '\0' ? OS_AFE_SUCCESS : OS_AFE_CANCEL); /* * If this is a "save" prompt, and the OVERWRITE flag isn't set, * and the file already exists, show an interactive warning that * we're about to ovewrite the file. */ if (ok && dialog_type == OS_AFP_SAVE && result == OS_AFE_SUCCESS && (attrs & VMCON_EVTATTR_OVERWRITE) == 0 && CVmNetFile::exists(vmg_ fname, 0)) { /* the file exists - warn about the overwrite */ ok = FALSE; t3sprintf(warning, sizeof(warning), "OV The script might overwrite the file %s. ", fname); } /* * If this is a "save" prompt, check to see if we can write the * file, and warn if not. */ if (ok && dialog_type == OS_AFP_SAVE && result == OS_AFE_SUCCESS && !CVmNetFile::can_write(vmg_ fname, 0)) { /* try creating the file */ osfildef *fp = osfopwb(fname, file_type); /* if that succeeded, undo the creation; otherwise warn */ if (fp != 0) { /* it worked - close and delete the test file */ osfcls(fp); osfdel(fname); } else { /* didn't work - warn about it */ ok = FALSE; t3sprintf(warning, sizeof(warning), "WR The script is attempting to write file %s, " "but that file cannot be written.", fname); } } /* * If this is an "open" prompt, and the file isn't readable, warn * about it. */ if (ok && dialog_type == OS_AFP_OPEN && result == OS_AFE_SUCCESS && !CVmNetFile::exists(vmg_ fname, 0)) { ok = FALSE; t3sprintf(warning, sizeof(warning), "RD The script is attempting to open file %s, " "but this file doesn't exist or isn't readable.", fname); } /* * If we generated a warning, and we're not in Web UI mode, display * the warning as a Yes/No/Cancel console input dialog. We can't * do this in Web UI mode, since we can't use the regular console * in Web mode. Instead, we'll return the warning information to * the caller for display. */ if (!ok && G_net_config == 0) { char fullprompt[OSFNMAX + 255 + 150]; /* build the full prompt */ t3sprintf(fullprompt, sizeof(fullprompt), "%s Do you wish to proceed? Select Yes to " "proceed with this file, No to choose a different " "file, or Cancel to stop replaying this script.", warning + 3); /* * display a dialog - note that this goes directly to user, * bypassing the active script, since this is a question about * how to handle a problem in the script */ show_warning: switch (G_console->input_dialog( vmg_ OS_INDLG_ICON_WARNING, fullprompt, OS_INDLG_YESNOCANCEL, 0, 0, 2, 3, TRUE)) { case 1: /* yes - proceed with fname */ break; case 2: /* no - ask for a new file */ result = G_console->askfile( vmg_ prompt, strlen(prompt), fname, sizeof(fname), dialog_type, file_type, TRUE); /* this didn't come from the script after all */ from_script = FALSE; /* * If they canceled the file selection, go back to the * warning dialog; if the dialog itself failed, cancel the * script entirely. If they selected a file, proceed with * their new file replacing the script input. Note that we * don't have to repeat the tests above on the new file, * since the user explicitly entered it and thus presumably * knows that it's the one they really want to use. */ if (result == OS_AFE_CANCEL) goto show_warning; else if (result == OS_AFE_FAILURE) close_script_file(vmg0_); /* handled */ break; case 3: /* cancel - stop the script playback */ close_script_file(vmg0_); /* indicate cancellation */ result = OS_AFE_CANCEL; break; } /* we've displayed the warning, so don't return it */ warning[0] = '\0'; } } else if (G_net_config != 0) { #ifdef TADSNET /* * If we're running in the local stand-alone configuration, we have * a bit of a special case: we're accessing files on the local file * system, but we're presenting the rest of the program's UI * through a browser window, which might be in a separate child * process; we have to present the file dialog UI in the same * manner. We have a special OS function for this situation. * * This only applies in the stand-alone configuration, where we * have no "hostname" parameter in the net config. If we have a * hostname, it means that we're operating in full client/server * mode, in which case we go through the Web UI directly. */ if (G_net_config->get("hostname") == 0) { result = osnet_askfile(prompt, fname, sizeof(fname), dialog_type, file_type); } else #endif { /* there's no console in web host mode - return eof */ result = OS_AFE_FAILURE; } } else { /* ask for a file via the console UI */ result = G_console->askfile( vmg_ prompt, strlen(prompt), fname, sizeof(fname), dialog_type, file_type); /* this file came from the console UI */ from_ui = TRUE; } /* * Create the return list. In all cases, we need one element for the * status code. On success, we need three addition elements (the * filename string, the descriptive text [future expansion], and the * warning message text). */ lst_obj = CVmObjList::create(vmg_ FALSE, result == OS_AFE_SUCCESS ? 4 : 1); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save it on the stack as protection against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* set the first element to the result code */ val.set_int(result); lst->cons_set_element(0, &val); /* add the extra elements for the success case */ if (result == OS_AFE_SUCCESS) { char *fnamep = fname, *fname2 = 0; err_try { /* if the name came from a script, map from the local char set */ if (from_script) { G_cmap_from_ui->map_str_alo(&fname2, fname); fnamep = fname2; } /* create a FileName object to represent the filename */ val.set_obj(CVmObjFileName::create_from_local( vmg_ fnamep, strlen(fnamep))); /* * if the name came from the console UI, flag the FileName as * being user-selected, which allows it to override the file * safety settings; since the user manually selected the name, * they implicitly granted permission to use the file for the * type of operation proposed by the dialog */ if (from_ui) { vm_objid_cast(CVmObjFileName, val.val.obj)->set_from_ui( dialog_type); } } err_finally { lib_free_str(fname2); } err_end; /* store the string as the second list element */ lst->cons_set_element(1, &val); /* * Add nil for the descriptive text, since we don't currently * implement this feature. (This is a future enhancement that some * system file selector dialogs might provide, allowing the user to * enter descriptive text in addition to the filename. The Web UI * storage server already uses this to save user descriptive text * with saved game files.) */ val.set_nil(); lst->cons_set_element(2, &val); /* * Add the script warning message, or nil if there's no warning. * The warning message contains the filename text from the script, * so translate it to the local character set. */ if (warning[0] != '\0') val.set_obj(str_from_ui_str(vmg_ warning)); else val.set_nil(); lst->cons_set_element(3, &val); } /* log the event */ if (result == OS_AFE_SUCCESS) G_console->log_event(vmg_ VMCON_EVT_FILE, fname, strlen(fname), FALSE); else G_console->log_event(vmg_ VMCON_EVT_FILE); /* return the list */ retval_obj(vmg_ lst_obj); /* we no longer need the garbage collector protection */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * timeDelay - pause for a specified interval */ void CVmBifTIO::timedelay(VMG_ uint argc) { long delay_ms; /* check arguments */ check_argc(vmg_ argc, 1); /* get the delay time in milliseconds */ delay_ms = pop_long_val(vmg0_); /* flush any pending output */ G_console->flush_all(vmg_ VM_NL_NONE); /* ask the system code to pause */ os_sleep_ms(delay_ms); } /* ------------------------------------------------------------------------ */ /* * systemInfo */ void CVmBifTIO::sysinfo(VMG_ uint argc) { int info; long result; /* make sure we have at least one argument */ if (argc < 1) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* get the information type code */ info = pop_int_val(vmg0_); /* see what we have */ switch(info) { case SYSINFO_SYSINFO: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* system information is supported in this version - return true */ retval_true(vmg0_); break; case SYSINFO_VERSION: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* return the VM version string, formatted as a string */ { char buf[30]; sprintf(buf, "%d.%d.%d", (int)((T3VM_VSN_NUMBER >> 16) & 0xffff), (int)((T3VM_VSN_NUMBER >> 8) & 0xff), (int)(T3VM_VSN_NUMBER & 0xff)); retval_str(vmg_ buf); } break; case SYSINFO_OS_NAME: /* there are no additional arguments for this information type */ check_argc(vmg_ argc, 1); /* return the OS name as a string */ retval_str(vmg_ OS_SYSTEM_NAME); break; case SYSINFO_HTML: case SYSINFO_JPEG: case SYSINFO_PNG: case SYSINFO_WAV: case SYSINFO_MIDI: case SYSINFO_WAV_MIDI_OVL: case SYSINFO_WAV_OVL: case SYSINFO_PREF_IMAGES: case SYSINFO_PREF_SOUNDS: case SYSINFO_PREF_MUSIC: case SYSINFO_PREF_LINKS: case SYSINFO_MPEG: case SYSINFO_MPEG1: case SYSINFO_MPEG2: case SYSINFO_MPEG3: case SYSINFO_LINKS_HTTP: case SYSINFO_LINKS_FTP: case SYSINFO_LINKS_NEWS: case SYSINFO_LINKS_MAILTO: case SYSINFO_LINKS_TELNET: case SYSINFO_PNG_TRANS: case SYSINFO_PNG_ALPHA: case SYSINFO_OGG: case SYSINFO_MNG: case SYSINFO_MNG_TRANS: case SYSINFO_MNG_ALPHA: case SYSINFO_TEXT_COLORS: case SYSINFO_TEXT_HILITE: case SYSINFO_BANNERS: case SYSINFO_INTERP_CLASS: /* there are no additional arguments for these information types */ check_argc(vmg_ argc, 1); /* ask the OS layer for the information */ if (os_get_sysinfo(info, 0, &result)) { /* we got a valid result - return it */ retval_int(vmg_ result); } else { /* * the information type is not known to the OS layer - * return nil to indicate that the information isn't * available */ retval_nil(vmg0_); } break; case SYSINFO_HTML_MODE: /* * This sysinfo flag is explicitly not used in TADS 3, since we're * always in HTML mode. (We make this case explicit to call * attention to the fact that it was not accidentally omitted, but * is intentionally not used.) */ /* fall through to default case */ default: /* * Other codes fail harmlessly with a nil return value. Pop all * remaining arguments and return nil. (Note that we discard * only (argc-1) arguments because we've already popped the * first argument.) */ G_stk->discard(argc - 1); retval_nil(vmg0_); break; } } /* ------------------------------------------------------------------------ */ /* * status_mode - set the status line mode */ void CVmBifTIO::status_mode(VMG_ uint argc) { int mode; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the mode value */ mode = pop_int_val(vmg0_); /* set the new status mode in the console */ G_console->set_statusline_mode(vmg_ mode); } /* ------------------------------------------------------------------------ */ /* * status_right - set the string in the right half of the status line */ void CVmBifTIO::status_right(VMG_ uint argc) { char msg[256]; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the status string */ pop_str_val_ui(vmg_ msg, sizeof(msg)); /* set the string */ os_strsc(msg); } /* ------------------------------------------------------------------------ */ /* * res_exists - check to see if an external resource can be loaded * through the host application */ void CVmBifTIO::res_exists(VMG_ uint argc) { const char *res_name; int result; /* check arguments */ check_argc(vmg_ argc, 1); /* pop the resource name */ res_name = pop_str_val(vmg0_); /* ask the host application if the resource can be loaded */ result = G_host_ifc->resfile_exists(res_name + VMB_LEN, osrp2(res_name)); /* return the result */ retval_bool(vmg_ result); } /* ------------------------------------------------------------------------ */ /* * set_script_file flags */ /* read in 'quiet' mode - do not dipslay output while reading the script */ #define VMBIFTADS_SCRIPT_QUIET 0x0001 /* turn off 'more' mode while reading the script */ #define VMBIFTADS_SCRIPT_NONSTOP 0x0002 /* the script is an "event" script - this is a query-only flag */ #define VMBIFTADS_SCRIPT_EVENT 0x0004 /* * set_script_file special request codes */ #define VMBIFTADS_SCRIPTREQ_GET_STATUS 0x7000 /* * set_script_file - open a command input scripting file */ void CVmBifTIO::set_script_file(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* * If the filename is nil, close the current script file. If the * "filename" is a number, it's a special request. Otherwise, the * filename must be a string giving the name of the file to open. */ if (G_stk->get(0)->typ == VM_NIL) { /* discard the nil filename */ G_stk->discard(); /* pop the flags if present - they're superfluous in this case */ if (argc >= 2) G_stk->discard(); /* close the script file */ close_script_file(vmg0_); /* this form always succeeds */ retval_true(vmg0_); } else if (G_stk->get(0)->typ == VM_INT) { /* get the request code */ int flags = pop_int_val(vmg0_); /* any additional argument is superfluous in this case */ if (argc >= 2) G_stk->discard(); /* check the request */ switch (flags) { case VMBIFTADS_SCRIPTREQ_GET_STATUS: /* get the current script reading status */ if (G_console->is_reading_script()) { /* a script is in progress - return the script flags */ retval_int(vmg_ (G_console->is_quiet_script() ? VMBIFTADS_SCRIPT_QUIET : 0) | (G_console->is_moremode_script() ? 0 : VMBIFTADS_SCRIPT_NONSTOP) | (G_console->is_event_script() ? VMBIFTADS_SCRIPT_EVENT : 0)); } else { /* not reading a script - return nil */ retval_nil(vmg0_); } break; default: /* other flags are invalid */ err_throw(VMERR_BAD_VAL_BIF); } } else { /* get the filename argument */ const vm_val_t *filespec = G_stk->get(0); /* if they provided flags, get the flags */ int flags = (argc >= 2 ? G_stk->get(1)->num_to_int(vmg0_) : 0); /* set up our recursive call descriptor */ vm_rcdesc rc(vmg_ "setScriptFile", bif_table, 15, G_stk->get(0), argc); /* open the script file */ int ok = !G_console->open_script_file( vmg_ filespec, &rc, (flags & VMBIFTADS_SCRIPT_QUIET) != 0, !(flags & VMBIFTADS_SCRIPT_NONSTOP)); /* discard arguments */ G_stk->discard(argc); /* return true on success, nil on failure */ retval_bool(vmg_ ok); } } /* * close the script file */ void CVmBifTIO::close_script_file(VMG0_) { int old_more_mode; /* close the script */ old_more_mode = G_console->close_script_file(vmg0_); /* restore the MORE mode in effect when the script was opened */ G_console->set_more_state(old_more_mode); } /* ------------------------------------------------------------------------ */ /* * get_charset selectors */ /* display character set */ #define VMBIFTADS_CHARSET_DISPLAY 0x0001 /* file system character set for filenames */ #define VMBIFTADS_CHARSET_FILENAME 0x0002 /* typical character set for text file contents */ #define VMBIFTADS_CHARSET_FILECONTENTS 0x0003 /* * get_charset - get a local character set name */ void CVmBifTIO::get_charset(VMG_ uint argc) { char csname[32]; int which; /* check arguments */ check_argc(vmg_ argc, 1); /* get the character set selector */ which = pop_int_val(vmg0_); /* map the selector to the appropriate value */ switch(which) { case VMBIFTADS_CHARSET_DISPLAY: /* * if there was an explicit character set parameter specified at * start-up time, use that */ if (G_disp_cset_name != 0) { /* there's an explicit parameter - return it */ retval_str(vmg_ G_disp_cset_name); /* we're done */ return; } /* no explicit setting - use the OS default character set */ which = OS_CHARMAP_DISPLAY; break; case VMBIFTADS_CHARSET_FILENAME: which = OS_CHARMAP_FILENAME; break; case VMBIFTADS_CHARSET_FILECONTENTS: which = OS_CHARMAP_FILECONTENTS; break; default: /* others are unrecognized; simply return nil for these */ retval_nil(vmg0_); return; } /* get the character set */ os_get_charmap(csname, which); /* return a string with the name */ retval_str(vmg_ csname); } /* ------------------------------------------------------------------------ */ /* * flush_output - flush the display output */ void CVmBifTIO::flush_output(VMG_ uint argc) { /* we take no arguments */ check_argc(vmg_ argc, 0); /* flush output */ G_console->flush(vmg_ VM_NL_NONE); /* immediately update the display */ G_console->update_display(vmg0_); } /* ------------------------------------------------------------------------ */ /* * input_timeout - get a line of input from the keyboard, with an optional * timeout */ void CVmBifTIO::input_timeout(VMG_ uint argc) { char buf[256]; long timeout; int use_timeout; int evt; int ele_count; vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* check arguments */ check_argc_range(vmg_ argc, 0, 1); /* if there's a timeout argument, retrieve it */ if (argc == 0) { /* no arguments - there's no timeout */ use_timeout = FALSE; timeout = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* * there's a timeout argument, but it's nil, so this means there's * no timeout */ use_timeout = FALSE; timeout = 0; /* discard the argument */ G_stk->discard(); } else { /* we have a timeout specified */ use_timeout = TRUE; timeout = pop_long_val(vmg0_); } /* read a line of text from the keyboard */ evt = G_console->read_line_timeout(vmg_ buf, sizeof(buf), timeout, use_timeout); /* figure out how big a list we'll return */ switch(evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* two elements - the event type, and the line of text */ ele_count = 2; break; default: /* for anything else, we need only the event type code */ ele_count = 1; break; } /* create the return list */ lst_obj = CVmObjList::create(vmg_ FALSE, ele_count); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); lst->cons_clear(); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* fill in the first element with the event type code */ val.set_int(evt); lst->cons_set_element(0, &val); /* set additional elements, according to the event type */ switch(evt) { case OS_EVT_LINE: case OS_EVT_TIMEOUT: /* the second element is the line of text we read */ val.set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); lst->cons_set_element(1, &val); break; default: /* other event types have no extra data */ break; } /* return the list */ retval_obj(vmg_ lst_obj); /* we can drop the garbage collection protection now */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * input_cancel - cancel input previously interrupted by timeout */ void CVmBifTIO::input_cancel(VMG_ uint argc) { vm_val_t val; int reset; /* check arguments */ check_argc(vmg_ argc, 1); /* get the 'reset' flag */ G_stk->pop(&val); reset = (val.typ == VM_TRUE); /* cancel the line */ G_console->read_line_cancel(vmg_ reset); } /* ------------------------------------------------------------------------ */ /* * Banner Window Functions */ /* * create a banner */ void CVmBifTIO::banner_create(VMG_ uint argc) { int parent_id; int other_id; int where; int wintype; int align; int siz; int siz_units; unsigned long style; int hdl; /* check arguments */ check_argc_range(vmg_ argc, 7, 8); /* retrieve the 'parent' parameter */ if (argc == 7) { /* * there's no parent argument - this is an obsolete format for the * arguments, but accept it for now, and simply treat it as * equivalent to a nil parent */ parent_id = 0; } else if (G_stk->get(0)->typ == VM_NIL) { /* parent is nil - use zero as the ID and discard the nil */ parent_id = 0; G_stk->discard(); } else { /* retrieve the parent ID */ parent_id = pop_int_val(vmg0_); } /* retrieve the 'where' parameter */ where = pop_int_val(vmg0_); /* retrieve the 'other' parameter, if it's needed for the 'where' */ switch(where) { case OS_BANNER_BEFORE: case OS_BANNER_AFTER: /* we need another banner ID for the relative insertion point */ other_id = pop_int_val(vmg0_); break; default: /* we don't need 'other' for this insertion point */ other_id = 0; G_stk->discard(); break; } /* retrieve the window type argument */ wintype = pop_int_val(vmg0_); /* retrieve the alignment argument */ align = pop_int_val(vmg0_); /* retrieve the size (as a percentage of the full screen size) */ if (G_stk->get(0)->typ == VM_NIL) { /* nil size - use zero for the size */ siz = 0; siz_units = OS_BANNER_SIZE_ABS; /* discard the size and size units */ G_stk->discard(); G_stk->discard(); } else { /* retrieve the size and size units as integer values */ siz = pop_int_val(vmg0_); siz_units = pop_int_val(vmg0_); } /* retrieve the flags */ style = pop_long_val(vmg0_); if (G_net_config != 0) { /* there's no console in web host mode - return failure */ retval_nil(vmg0_); return; } /* try creating the banner */ hdl = G_console->get_banner_manager()->create_banner( vmg_ parent_id, where, other_id, wintype, align, siz, siz_units, style); /* * If we succeeded, return the handle; otherwise, return nil. A banner * handle of zero indicates failure. */ if (hdl != 0) retval_int(vmg_ hdl); else retval_nil(vmg0_); } /* * delete a banner */ void CVmBifTIO::banner_delete(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* delete the banner */ G_console->get_banner_manager()->delete_banner(vmg_ pop_int_val(vmg0_)); } /* * clear a window */ void CVmBifTIO::banner_clear(VMG_ uint argc) { int id; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(id); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* tell the console that we're clearing it */ console->clear_window(vmg0_); } /* * write values to a banner */ void CVmBifTIO::banner_say(VMG_ uint argc) { CVmConsole *console; /* check arguments */ check_argc_range(vmg_ argc, 1, 32767); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(pop_int_val(vmg0_)); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* * write the argument(s) to the console (note that the first argument, * which we've already retrieved, is the console handle, so don't count * it among the arguments to display) */ say_to_console(vmg_ console, argc - 1); } /* * flush text to a banner */ void CVmBifTIO::banner_flush(VMG_ uint argc) { CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner - if it's invalid, throw an error */ console = G_console->get_banner_manager()->get_console(pop_int_val(vmg0_)); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* flush the console */ console->flush(vmg_ VM_NL_NONE); /* immediately update the display */ console->update_display(vmg0_); } /* * set the banner size */ void CVmBifTIO::banner_set_size(VMG_ uint argc) { int id; void *hdl; int siz; int siz_units; int is_advisory; /* check arguments */ check_argc(vmg_ argc, 4); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the size and size units */ siz = pop_int_val(vmg0_); siz_units = pop_int_val(vmg0_); /* get the is-advisory flag */ is_advisory = pop_bool_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); if (hdl == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the size */ os_banner_set_size(hdl, siz, siz_units, is_advisory); } /* * size a banner to its contents in one or both dimensions */ void CVmBifTIO::banner_size_to_contents(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* make sure we've flushed any pending output to the banner */ console->flush(vmg_ VM_NL_NONE); /* set the size */ os_banner_size_to_contents(hdl); } /* * move the output position in a text grid banner */ void CVmBifTIO::banner_goto(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; int row, col; /* check arguments */ check_argc(vmg_ argc, 3); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the coordinates */ row = pop_int_val(vmg0_); col = pop_int_val(vmg0_); /* make sure the values are valid */ if (row < 1 || col < 1) err_throw(VMERR_BAD_VAL_BIF); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* * make sure we've flushed and discarded any pending output (since we * don't want any pending output to show up at the new cursor position) */ console->flush(vmg_ VM_NL_NONE); console->empty_buffers(vmg0_); /* move the cursor, adjusting from 1-based to 0-based coordinates */ os_banner_goto(hdl, row - 1, col - 1); } /* * set the text color in a banner */ void CVmBifTIO::banner_set_text_color(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; os_color_t fg, bg; /* check arguments */ check_argc(vmg_ argc, 3); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the foreground and background color values */ fg = (os_color_t)pop_long_val(vmg0_); bg = (os_color_t)pop_long_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the text output color in the console's formatter */ console->set_text_color(vmg_ fg, bg); } /* * set the screen color in a banner */ void CVmBifTIO::banner_set_screen_color(VMG_ uint argc) { int id; void *hdl; CVmConsole *console; os_color_t color; /* check arguments */ check_argc(vmg_ argc, 2); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the body color */ color = (os_color_t)pop_long_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* set the body color in the console */ console->set_body_color(vmg_ color); } /* service routine: store an integer in a list under construction */ static void set_list_int(CVmObjList *lst, size_t idx, long intval) { vm_val_t val; /* set the value */ val.set_int(intval); /* store it in the list */ lst->cons_set_element(idx, &val); } /* * get information on a banner */ void CVmBifTIO::banner_get_info(VMG_ uint argc) { int id; void *hdl; os_banner_info_t info; CVmConsoleBanner *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the banner ID */ id = pop_int_val(vmg0_); /* get the banner - if it's invalid, throw an error */ hdl = G_console->get_banner_manager()->get_os_handle(id); console = G_console->get_banner_manager()->get_console(id); if (hdl == 0 || console == 0) err_throw(VMERR_BAD_VAL_BIF); /* get information on the banner */ if (console->get_banner_info(&info)) { vm_obj_id_t lst_obj; CVmObjList *lst; vm_val_t val; /* set up a return list with space for six entries */ lst_obj = CVmObjList::create(vmg_ FALSE, 6); lst = (CVmObjList *)vm_objp(vmg_ lst_obj); /* save the list on the stack to protect against garbage collection */ val.set_obj(lst_obj); G_stk->push(&val); /* * return the values: [align, style, rows, columns, pix_height, * pix_width] */ set_list_int(lst, 0, info.align); set_list_int(lst, 1, info.style); set_list_int(lst, 2, info.rows); set_list_int(lst, 3, info.columns); set_list_int(lst, 4, info.pix_height); set_list_int(lst, 5, info.pix_width); /* return the list */ retval_obj(vmg_ lst_obj); /* discard our gc protection */ G_stk->discard(); } else { /* no information available - return nil */ retval_nil(vmg0_); } } /* ------------------------------------------------------------------------ */ /* * Log Console Functions */ /* * create a log console */ void CVmBifTIO::log_console_create(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 3); /* get the arguments: log file name, character set, and width */ const vm_val_t *filespec = G_stk->get(0); const vm_val_t *charset = G_stk->get(1); int width = G_stk->get(2)->num_to_int(vmg0_); /* * Retrieve the character mapper, which can be given as either a * CharacterSet object or a string giving the character set name. */ CCharmapToLocal *cmap = 0; if (charset->typ == VM_NIL) { /* nil - use the default log file character set */ cmap = G_cmap_to_log; cmap->add_ref(); } else if (charset->typ == VM_OBJ && CVmObjCharSet::is_charset(vmg_ charset->val.obj)) { /* it's a CharacterSet object - pop the reference */ vm_obj_id_t obj = charset->val.obj; /* retrieve the character mapper from the character set */ cmap = ((CVmObjCharSet *)vm_objp(vmg_ obj))->get_to_local(vmg0_); cmap->add_ref(); } else { /* it's not a CharacterSet, so it must be a character set name */ const char *str = charset->get_as_string(vmg0_); if (str == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the length and skip the length prefix */ size_t len = vmb_get_len(str); str += VMB_LEN; /* get a null-terminated version of the name */ char *nm = lib_copy_str(str, len); /* * Create a character mapping for the given name. Note that this * will automatically add a reference to the mapper on our behalf, * so we don't have to add our own extra reference. */ cmap = CCharmapToLocal::load(G_host_ifc->get_sys_res_loader(), nm); /* done with the null-terminated version of the name string */ lib_free_str(nm); } /* if we didn't get a character map, use us-ascii by default */ if (cmap == 0) cmap = CCharmapToLocal::load(G_host_ifc->get_sys_res_loader(), "us-ascii"); /* open the file and start logging */ osfildef *fp = 0; CVmNetFile *nf = 0; int hdl = 0; err_try { /* create the network file descriptor */ vm_rcdesc rc(vmg_ "logConsoleCreate", bif_table, 30, G_stk->get(0), argc); nf = CVmNetFile::open(vmg_ filespec, &rc, NETF_NEW, OSFTLOG, "text/plain"); /* make sure the file safety level allows the operation */ CVmObjFile::check_safety_for_open(vmg_ nf, VMOBJFILE_ACCESS_WRITE); /* open the file for writing (in text mode) */ fp = osfopwt(nf->lclfname, OSFTLOG); /* if that failed, we can't contine */ if (fp == 0) G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc, 0, "error creating log file"); /* create the log console */ hdl = G_console->get_log_console_manager()->create_log_console( vmg_ nf, fp, cmap, width); /* we've handed off the file information to the console */ nf = 0; fp = 0; } err_finally { /* * release our character map reference - if we succeeded in * creating the log console, it will have added its own reference * by now */ cmap->release_ref(); /* close the file if we opened it and didn't hand it off */ if (fp != 0) osfcls(fp); /* if we have a network file object, abandon it */ if (nf != 0) nf->abandon(vmg0_); } err_end; /* discard arguments */ G_stk->discard(argc); /* * If we succeeded, return the handle; otherwise, return nil. A handle * of zero indicates failure. */ if (hdl != 0) retval_int(vmg_ hdl); else retval_nil(vmg0_); } /* * close (delete) a log console */ void CVmBifTIO::log_console_close(VMG_ uint argc) { int handle; CVmConsole *console; /* check arguments */ check_argc(vmg_ argc, 1); /* get the handle */ handle = pop_int_val(vmg0_); /* get the console based on the handle */ console = G_console->get_log_console_manager()->get_console(handle); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); /* flush the console */ console->flush(vmg_ VM_NL_NONE); /* delete the console */ G_console->get_log_console_manager()->delete_log_console(vmg_ handle); /* no return value */ retval_nil(vmg0_); } /* * write values to a log console */ void CVmBifTIO::log_console_say(VMG_ uint argc) { int hdl; CVmConsole *console; /* check arguments */ check_argc_range(vmg_ argc, 1, 32767); /* get the console handle */ hdl = pop_int_val(vmg0_); /* * if it's the special value -1, it means that we want to write to the * main console's log file; otherwise, it's a log console that we * previously created explicitly via log_console_create() */ if (hdl == -1) { /* use the main log */ console = new CVmConsoleMainLog(); } else { /* get the console by handle - if it's invalid, throw an error */ console = G_console->get_log_console_manager()->get_console(hdl); if (console == 0) err_throw(VMERR_BAD_VAL_BIF); } /* * write the argument(s) to the console (note that the first argument, * which we've already retrieved, is the console handle, so don't count * it among the arguments to display) */ err_try { say_to_console(vmg_ console, argc - 1); } err_finally { /* if we created a "main console" object, we're done with it */ if (hdl == -1) console->delete_obj(vmg0_); } err_end; } /* ------------------------------------------------------------------------ */ /* * Log an input event obtained from an external source, such as the Web UI */ void CVmBifTIO::log_input_event(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 1); /* retrieve the list */ const vm_val_t *lst = G_stk->get(0); if (!lst->is_listlike(vmg0_)) err_throw(VMERR_BAD_TYPE_BIF); /* make sure we have at least one element */ int len = lst->ll_length(vmg0_); if (len < 1) err_throw(VMERR_BAD_VAL_BIF); /* retrieve the event type code or tag name from the first element */ vm_val_t ele1; lst->ll_index(vmg_ &ele1, 1); /* if there's a second element, retrieve the parameter string */ const char *param = 0; size_t paramlen = 0; if (len >= 2) { /* there's a parameter - retrieve it */ vm_val_t ele2; lst->ll_index(vmg_ &ele2, 2); /* * Get its string value. If it's a filename object, use the * filename path string. */ CVmObjFileName *fname_param = vm_val_cast(CVmObjFileName, &ele2); if (fname_param != 0) param = fname_param->get_path_string(); else param = ele2.get_as_string(vmg0_); /* if we didn't get a parameter string value, it's an error */ if (param == 0) err_throw(VMERR_BAD_VAL_BIF); /* get the parameter length and buffer pointer */ paramlen = vmb_get_len(param); param += VMB_LEN; } /* log the event, according to the type of the event code */ if (ele1.is_numeric(vmg0_)) { /* it's an integer event type code - log it */ G_console->log_event( vmg_ ele1.num_to_int(vmg0_), param, paramlen, TRUE); } else if (ele1.get_as_string(vmg0_) != 0) { /* it's a string event tag - retrieve it in the UI character set */ char tag[128]; G_stk->push(&ele1); pop_str_val_ui(vmg_ tag, sizeof(tag)); /* log the event with the given tag */ G_console->log_event(vmg_ tag, param, paramlen, TRUE); } else { /* other types are invalid */ err_throw(VMERR_BAD_VAL_BIF); } /* discard arguments */ G_stk->discard(1); /* no return value */ retval_nil(vmg0_); } frobtads-1.2.3/tads3/vmnet.cpp0000644000175000001440000013146612145504453015375 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* Name vmnet.cpp - TADS 3 networking Function Implements the networking layer for TADS 3. This is used for the web server configurations. This is portable code implementing high-level network operations, such as an HTTP server, using the low-level socket and thread API defined in osifcnet.h. Notes Modified 04/11/10 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "osifcnet.h" #include "vmnet.h" #include "vmglob.h" #include "vmtype.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmisaac.h" #include "sha2.h" #include "vmpredef.h" #include "vmhttpreq.h" #include "vmglob.h" #include "vmmain.h" #include "vmpredef.h" #include "vmerrnum.h" #include "vmrun.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Server manager */ TadsServerManager::TadsServerManager() { /* initialize our ISAAC context */ ic = new isaacctx(); isaac_init(ic, TRUE); /* create our mutex */ mutex = new OS_Mutex(); } TadsServerManager::~TadsServerManager() { /* done with our ISAAC context */ delete ic; /* done with our mutex */ mutex->release_ref(); } /* * generate a random number */ ulong TadsServerManager::rand() { /* ISAAC needs protection against multi-threaded access */ mutex->lock(); /* get a random number */ ulong r = isaac_rand(ic); /* done with the mutex */ mutex->unlock(); /* return the number */ return r; } /* * Generate a random ID string */ static inline char nybble2hex(unsigned char c) { c &= 0x0f; return (c < 10 ? c + '0' : c - 10 + 'a'); } char *TadsServerManager::gen_rand_id(void *obj) { /* set up a hashing buffer */ sha256_ctx s; sha256_begin(&s); /* add the current date/time to the hash */ os_time_t timer = os_time(0); struct tm *tblock = os_localtime(&timer); sha256_hash((unsigned char *)tblock, sizeof(*tblock), &s); /* add the system timer to the hash */ long systime = os_get_sys_clock_ms(); sha256_hash((unsigned char *)&systime, sizeof(systime), &s); /* add the object address to the hash */ sha256_hash((unsigned char *)obj, sizeof(obj), &s); /* add the current stack location to the hash */ sha256_hash((unsigned char *)&obj, sizeof(void *), &s); /* add some random bytes from the operating system */ unsigned char rbuf[128]; os_gen_rand_bytes(rbuf, sizeof(rbuf)); sha256_hash(rbuf, sizeof(rbuf), &s); /* compute the hash */ unsigned char hval[32]; sha256_end(hval, &s); /* convert it to hex, but just keep the low nybbles, for 32 digits */ char *ret = lib_alloc_str(32); int i; for (i = 0 ; i < 32 ; ++i) ret[i] = nybble2hex(hval[i]); /* null-terminate the string */ ret[i] = '\0'; /* return the allocated string */ return ret; } /* ------------------------------------------------------------------------ */ /* * Message queue */ /* * wait for a message */ int TadsMessageQueue::wait(VMG_ unsigned long timeout, TadsMessage **msgp) { /* assume we won't return a message */ *msgp = 0; /* * Wait for a message to arrive in the queue. The sender will signal * our event upon posting a message. Note that our assumption that * there's only one reader thread per queue means that we don't have to * worry about another reader swiping the signaled message between the * time the signal went off and the time we read the queue. * * Also stop waiting if the quit event has been signaled, or the debug * event is signaled. */ OS_Waitable *w[3]; int cnt = 0; w[cnt++] = ev; /* add the quit event, if present */ if (quit_evt != 0) w[cnt++] = quit_evt; /* add the debug break event, if present */ OS_Event *brkevt = 0; VM_IF_DEBUGGER(if (G_debugger != 0) w[cnt++] = brkevt = G_debugger->get_break_event()); /* wait for an event */ int ret = OS_Waitable::multi_wait(cnt, w, timeout); /* if the message event fired, retrieve the message */ if (ret == OSWAIT_EVENT + 0) *msgp = get(); /* * always return +2 for the debug event (we'll have +1 from the * multi_wait if the list doesn't have a quit event) */ if (w[ret - OSWAIT_EVENT] == brkevt) ret = OSWAIT_EVENT + 2; /* release the debugger break event, if we got it */ if (brkevt != 0) brkevt->release_ref(); /* return the result */ return ret; } /* ------------------------------------------------------------------------ */ /* * Custom string object. This is a convenience class for complex string * construction, which the network servers do a lot of. */ struct NetString { NetString(size_t initsize) { init(initsize); } NetString() { init(50); } void init(size_t initsize) { buf = (char *)t3malloc(initsize); buf[0] = '\0'; len = initsize; inc = (initsize > 128 ? initsize / 2 : 128); wrtidx = 0; } ~NetString() { if (buf != 0) t3free(buf); } /* clear the buffer */ void clear() { buf[0] = '\0'; wrtidx = 0; } /* the buffer, its total size, and the size increment */ char *buf; size_t len; size_t inc; /* the current buffer index */ size_t wrtidx; /* reserve space for an addition 'b' bytes */ void reserve(size_t b) { if (wrtidx + b + 1 > len) { /* increase by the increment until big enough */ while (wrtidx + b + 1 > len) len += inc; /* reallocate at the increased size */ buf = (char *)t3realloc(buf, len); } } /* append text */ void append(const char *txt, size_t txtlen) { /* make sure there's room */ reserve(txtlen); /* copy the text */ memcpy(buf + wrtidx, txt, txtlen); /* adjust the counters */ wrtidx += txtlen; /* * null terminate (but don't count it - we can overwrite it with a * subsequent append) */ buf[wrtidx] = '\0'; } void append(const char *txt) { append(txt, strlen(txt)); } /* append text to a buffer, with sprintf-style formatting */ void vappendf(const char *fmt, va_list argp) { /* calculate the amount of space we need */ int plen = t3vsprintf(0, 0, fmt, argp); /* if that worked, format the data */ if (plen >= 0) { /* reserve the required space */ reserve(plen); /* format the text */ int plen = t3vsprintf(buf + wrtidx, len - wrtidx, fmt, argp); /* if that worked, advance the buffer pointers */ if (plen >= 0) wrtidx += plen; /* null-terminate at the new length in any case */ buf[wrtidx] = '\0'; } } void appendf(const char *fmt, ...) { va_list argp; /* process through our va_list version */ va_start(argp, fmt); vappendf(fmt, argp); va_end(argp); } /* * append text, converting plain text to valid XML by turning markup * characters into entities */ void append_xml(const char *txt) { const char *p, *start; /* append in chunks as we find special characters */ for (p = start = txt ; *p != '\0' ; ++p) { static const char *xlat[] = { "<", ">", "&" }; static const char *specials = "<>&"; const char *psp = strchr(specials, *p); int sp_index = (psp == 0 ? -1 : psp - specials); /* if we're at a special character, quote it */ if (sp_index >= 0) { /* append the plain chunk up to this point */ if (p != start) append(start, p - start); /* append the entity version of the special character */ append(xlat[sp_index]); /* the next chunk starts at the next character */ start = p + 1; } } /* append the final chunk */ if (p != start) append(start, p - start); } /* format then append as XML */ void vappendf_xml(const char *fmt, va_list argp) { NetString tmp(512); /* format the message */ tmp.vappendf(fmt, argp); /* append as XML */ append_xml(tmp.buf); } /* append text, converting plain text to URL encoding */ void append_urlencode(const char *txt) { const char *p, *start; /* append in chunks as we find special characters */ for (p = start = txt ; *p != '\0' ; ++p) { char c = *p; if (isalpha(c) || isdigit(c) || strchr("-_.", c) != 0) { /* it's an ordinary character - keep it as-is */ } else { /* special character - quote it */ /* copy the part up to the special character */ if (p != start) append(start, p - start); /* append '+' for a space or a '%' encoding for others */ if (c == ' ') append("+"); else appendf("%%%02x", (int)(unsigned char)c); /* the next chunk starts at the next character */ start = p + 1; } } /* append the final chunk */ if (p != start) append(start, p - start); } }; /* ------------------------------------------------------------------------ */ /* * Given an XML buffer, find the end of the header and the start of * the regular XML contents */ const char *TadsXml::strip_xml_header(const char *buf) { const char *p; /* skip any initial newlines or whitespace */ for (p = buf ; isspace(*p) || *p == '\n' || *p == '\r' ; ++p) ; /* if it starts with "' */ for (p += 6, qu = 0 ; *p != '\0' ; ++p) { /* check for quotes */ if (*p == '"' || *p == '\'') { /* * if we're in a quoted section, and this is the matching * close quote, leave the quoted section; if we're not in a * quoted section, this is the opening quote of a quoted * section */ if (qu == 0) { /* we weren't in a quoted section, so now we are */ qu = *p; } else if (*p == qu) { /* we were in a quoted section, and it's our close quote */ qu = 0; } } else if (*p == '?' && *(p+1) == '>') { /* * It's the end of the directive. Skip the "?>" sequence * and any subsequent newline characters. */ for (p += 2 ; *p == '\n' || *p == '\r' ; ++p) ; /* * we're now at the start of the XML contents - return the * current pointer */ return p; } } } /* * we either didn't find the start of the directive, or we * couldn't find the end of it - in either case, we don't have a * well-formed directive, so there's nothing to strip: just return the * original buffer */ return buf; } /* ------------------------------------------------------------------------ */ /* * HTTP Server */ /* * construct */ TadsListener::TadsListener(TadsListenerThread *t) { /* remember the thread, and note our reference */ thread = t; t->add_ref(); } TadsListener::~TadsListener() { /* release our referenced objects */ thread->release_ref(); } /* * launch a server */ TadsListener *TadsListener::launch(const char *hostname, ushort portno, TadsListenerThread *thread) { /* open the network port */ OS_Listener *l = new OS_Listener(); if (!l->open(hostname, portno)) { /* couldn't open the port - delete the listener and return failure */ l->release_ref(); return 0; } /* put the port into non-blocking mode */ l->set_non_blocking(); /* hand the network port over to the thread - it assumes our reference */ thread->set_port(l); /* launch the listener thread */ if (!thread->launch()) { /* couldn't launch the thread - delete it and return failure */ thread->release_ref(); return 0; } /* return the new listener object */ return new TadsListener(thread); } /* * notify the server that it's time to shut down */ void TadsListener::shutdown() { /* set the 'quit' event in our thread */ thread->shutdown_evt->signal(); } /* ------------------------------------------------------------------------ */ /* * Generic listener thread base class. */ /* * Deletion */ TadsListenerThread::~TadsListenerThread() { /* release our port, if we have one */ if (port != 0) port->release_ref(); /* release our quit event and shutdown event */ quit_evt->release_ref(); shutdown_evt->release_ref(); /* release our mutex */ mutex->release_ref(); /* free the error message string */ lib_free_str(errmsg); /* free the password string */ lib_free_str(password); /* * Release all of our server threads. Note that we don't need to * protect against concurrent access here because we know that no one * has a reference to this object any longer - it's the only way we can * be deleted. */ TadsServerThread *tcur, *tnxt; for (tcur = servers ; tcur != 0 ; tcur = tnxt) { /* remember the next one, and release this one */ tnxt = tcur->next_server; tcur->release_ref(); } } /* * Thread main */ void TadsListenerThread::thread_main() { /* * keep going until we get the general application-wide 'quit' signal * or our own private listener shutdown event */ while (!quit_evt->test() && !shutdown_evt->test()) { /* * wait for a new connection request OR the quit signal, whichever * comes first */ OS_Waitable *w[] = { port, quit_evt, shutdown_evt }; switch (OS_Waitable::multi_wait(3, w)) { case OSWAIT_EVENT + 0: /* the port is ready - reset the event */ port->reset_event(); /* read connections as long as they're available */ for (;;) { /* check for a connection request */ OS_Socket *s = port->accept(); /* if we're out of requests, go back to waiting */ if (s == 0 && port->last_error() == OS_EWOULDBLOCK) break; /* check for other errors */ if (s == 0) { /* failed - flag the error and shut down */ errmsg = t3sprintf_alloc( "Listener thread failed: error %d from accept()", port->last_error()); shutdown_evt->signal(); break; } /* put the socket into non-blocking mode */ s->set_non_blocking(); /* create the server thread (it takes over the socket ref) */ TadsServerThread *st = create_server_thread(s); st->thread_id = next_thread_id++; /* launch the server thread */ if (!st->launch()) { /* failed - flag the error and shut down */ errmsg = lib_copy_str("Listener thread failed: " "couldn't launch server thread"); shutdown_evt->signal(); /* release our reference on the thread */ st->release_ref(); /* done */ break; } /* we're done with our reference to the thread */ st->release_ref(); } break; case OSWAIT_EVENT + 1: /* * The quit signal fired - the whole app is terminating. * Signal our internal shutdown event and abort. */ shutdown_evt->signal(); break; case OSWAIT_EVENT + 2: /* shutdown signal fired - the listener is terminating; abort */ break; } } /* wait for our server threads to exit */ for (;;) { TadsServerThread *st; /* get the first thread from the list */ mutex->lock(); if ((st = servers) != 0) st->add_ref(); mutex->unlock(); /* if we're out of threads, we're done */ if (st == 0) break; /* wait for this thread */ st->wait(); /* we're done with this thread */ st->release_ref(); } } /* * Add a thread to our list */ void TadsListenerThread::add_thread(TadsServerThread *t) { /* protect against concurrent access */ mutex->lock(); /* link the thread into our list */ t->next_server = servers; servers = t; /* keep a reference on it */ t->add_ref(); /* done with concurrent access protection */ mutex->unlock(); } /* * Remove a thread from our list */ void TadsListenerThread::remove_thread(TadsServerThread *t) { /* protect against concurrent access */ mutex->lock(); /* find the thread */ TadsServerThread *cur, *prv; for (cur = servers, prv = 0 ; cur != 0 ; prv = cur, cur = cur->next_server) { /* if this is the one we're looking for, unlink it */ if (cur == t) { /* unlink it from the list */ if (prv != 0) prv->next_server = cur->next_server; else servers = cur->next_server; /* release our reference on it */ t->release_ref(); /* we're done searching */ break; } } /* done with concurrent access protection */ mutex->unlock(); } /* * generate a human-readable status report of my threads */ void TadsListenerThread::list_threads(NetString *buf) { /* protect against concurrent access */ mutex->lock(); /* scan threads */ for (TadsServerThread *cur = servers ; cur != 0 ; cur = cur->next_server) { /* report on this thread */ StringRef *state = cur->get_state(); buf->appendf("Thread ID=%d (thread object=%lx): %s
", cur->thread_id, (unsigned long)cur, state->get()); /* done with the state string */ state->release_ref(); } /* done with concurrent access protection */ mutex->unlock(); } /* ------------------------------------------------------------------------ */ /* * Generic server thread base class */ void TadsServerThread::thread_main() { /* add this thread to the listener's active server list */ listener->add_thread(this); /* process requests until we encounter an error or Quit signal */ while (!listener->quit_evt->test() && !listener->shutdown_evt->test() && process_request()) ; /* terminating - close the socket */ set_run_state("Closing"); socket->close(); /* remove myself from the listener's active server list */ listener->remove_thread(this); /* set my final run state */ set_run_state("Terminated"); } /* * Read bytes from the other side. Blocks until there's at least one byte * to read, then reads as many bytes into the buffer as we can without * blocking further. Aborts if either the listener's "quit" or "shutdown" * event is triggered. */ long TadsServerThread::read(char *buf, size_t buflen, long minlen, unsigned long timeout) { /* figure the ending time for the wait */ unsigned long t = os_get_sys_clock_ms(), t_end = t + timeout; /* we haven't read any bytes yet */ long totlen = 0; /* if the caller provided a buffer, we can't read past the buffer */ if (buf != 0 && minlen > (long)buflen) minlen = buflen; /* keep going until we read some data */ for (;;) { int len; char ibuf[4096], *dst; size_t dstlen; /* figure the buffer destination and size to read on this round */ if (buf == 0) { /* * There's no buffer, so read into our internal buffer. Tead * up to the remaining minimum size, or to our available * internal space, whichever is less. */ dst = ibuf; dstlen = (minlen < sizeof(ibuf) ? minlen : sizeof(ibuf)); } else { /* * Read into the caller's buffer, after any data we've read so * far, up to the remaining buffer length. */ dst = buf + totlen; dstlen = buflen - totlen; } /* read the data */ set_run_state("Receiving"); len = socket->recv(dst, dstlen); /* if an error occurred, check what happened */ if (len == OS_SOCKET_ERROR) { /* presume failure */ int ok = FALSE; /* if this is a would-block error, wait for data to arrive */ if (socket->last_error() == OS_EWOULDBLOCK) { /* * No data available - wait until we receive at least one * byte, or until the 'quit' event is signaled or a timeout * occurs. Figure the next timeout expiration, if we have * a timeout at all. */ if (timeout != OS_FOREVER) { /* if we're already past the timeout expiration, fail */ t = os_get_sys_clock_ms(); if (t > t_end) return -1; /* figure the remaining timeout interval */ timeout = t_end - t; } /* wait */ set_run_state("Waiting(receive)"); OS_Waitable *w[] = { socket, listener->quit_evt, listener->shutdown_evt }; if (OS_Waitable::multi_wait(3, w, timeout) == OSWAIT_EVENT + 0) { /* the socket is now ready - reset it and keep going */ socket->reset_event(); ok = TRUE; } } /* if we didn't correct the error, give up */ if (!ok) return -1; } else if (len == 0) { /* the socket has been closed - return failure */ set_run_state("Error(receiving)"); return -1; } else if (len >= minlen) { /* we've satisfied the request - return the bytes */ set_run_state("Receive completed"); return totlen + len; } else { /* * We've read some data, but not enough to satisfy the minimum * length request. Add the current chunk to the total read so * far, and deduct it from the remaining minimum. */ totlen += len; minlen -= len; } } } /* * Send data to the other side. Blocks until we send all of the data, or * until the Quit event is signaled or we encounter another error. Returns * true on success, false on failure. */ int TadsServerThread::send(const char *buf, size_t len) { /* keep going until we satisfy the write request or run into trouble */ while (len != 0) { /* try sending */ set_run_state("Sending"); size_t cur = socket->send(buf, len); /* check what happened */ if (cur == OS_SOCKET_ERROR) { /* error reading socket - presume failure */ int ok = FALSE; /* if this is a 'would block' error, wait for the socket */ if (socket->last_error() == OS_EWOULDBLOCK) { /* wait for the socket to unblock, or for the Quit event */ set_run_state("Waiting(send)"); OS_Waitable *w[] = { socket, listener->quit_evt, listener->shutdown_evt }; if (OS_Waitable::multi_wait(3, w) == OSWAIT_EVENT + 0) { /* socket is ready - reset it and carry on */ socket->reset_event(); ok = TRUE; } } /* if we didn't solve the problem, fail */ if (!ok) return FALSE; } else { /* adjust the counters for the write */ buf += cur; len -= cur; } } /* we successfully sent all of the requested data */ set_run_state("Send completed"); return TRUE; } /* ------------------------------------------------------------------------ */ /* * argument structure for URL parsing */ struct url_param { /* argument name */ const char *name; /* argument value */ const char *val; }; struct url_param_alo: url_param { url_param_alo() { name = 0; val = 0; } url_param_alo * operator =(const url_param &src) { name = lib_copy_str(src.name); val = lib_copy_str(src.val); return this; } ~url_param_alo() { lib_free_str((char *)name); lib_free_str((char *)val); } }; #if 0 // not currently used /* get the value of an argument */ static const char *get_arg_val(const char *name, const char *dflt, const url_param *args, int argc) { int i; /* scan for the argument name */ for (i = 0 ; i < argc ; ++i, ++args) { if (stricmp(args->name, name) == 0) return args->val; } /* didn't find it */ return dflt; } #endif /* ------------------------------------------------------------------------ */ /* * HTTP Listener Thread */ /* * construction */ TadsHttpListenerThread::TadsHttpListenerThread( vm_obj_id_t srv_obj, TadsMessageQueue *q, long ulim) : TadsListenerThread(q->get_quit_evt()) { /* remember the HTTPServer object that created the listener */ this->srv_obj = srv_obj; /* save a reference to the queue */ (queue = q)->add_ref(); /* save the upload limit */ upload_limit = ulim; } /* * deletion */ TadsHttpListenerThread::~TadsHttpListenerThread() { /* release our queue */ queue->release_ref(); } /* ------------------------------------------------------------------------ */ /* * HTTP Server Thread */ /* * Constant strings for some common HTTP status codes we report */ static const char *S_http_400 = "400 Bad Request"; static const char *S_http_500 = "500 Internal Server Error"; static const char *S_http_500_body = "" "500 Internal Server Error" "" "

Internal Server Error

" "" ""; static const char *S_http_503 = "503 Service Unavailable (Shutting Down)"; static const char *S_http_503_quitting_body = "" "503 Service Unavailable" "" "

Service Unavailable

" "The server is shutting down and cannot process any more requests." "" ""; /* * Parse a space-delimited token. */ static void parse_tok(char *&p, char *&tok, size_t &tok_len) { /* skip leading spaces */ for ( ; isspace(*p) ; ++p) ; /* the token starts here */ tok = p; /* * find the end of the token - it's the end of the string, or the next * whitespace or newline (CR or LF) character */ for ( ; *p != '\0' && !isspace(*p) && *p != '\n' && *p != '\r' ; ++p) ; /* note the length of the token */ tok_len = p - tok; } /* * HTTP server thread - Construction */ TadsHttpServerThread::TadsHttpServerThread( TadsHttpListenerThread *l, TadsMessageQueue *q, long ulim, OS_Socket *s) : TadsServerThread(l, s) { /* keep a reference to the message queue */ (queue = q)->add_ref(); /* remember the upload limit */ upload_limit = ulim; } TadsHttpServerThread::~TadsHttpServerThread() { /* done with the message queue */ queue->release_ref(); } /* * Send a simple response, with the given status code and message body. If * the mesage body is null, we'll just send the response header. Returns * true if successful, false on error. */ int TadsHttpServerThread::send_simple( const char *status_code, const char *mime_type, const char *msg_body, size_t msg_len, const char *extra_headers) { char hdr[100]; /* send the response */ t3sprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n", status_code); if (!send(hdr, strlen(hdr))) return FALSE; /* send the extra headers */ if (extra_headers != 0) send(extra_headers, strlen(extra_headers)); /* if there's a message body, send it, with a content-length header */ if (msg_body != 0) { char buf[200]; /* build the cache-control and content-length header */ t3sprintf(buf, sizeof(buf), "Content-Type: %s\r\n" "Content-Length: %lu\r\n" "Cache-control: no-cache\r\n" "Connection: Keep-Alive\r\n" "\r\n", mime_type, (ulong)msg_len); /* send the headers, followed by the message body */ if (!send(buf, strlen(buf)) || !send(msg_body, msg_len)) return FALSE; } else { /* * there's no body, so just send a blank line to mark the end of * the header */ if (!send("\r\n", 2)) return FALSE; } /* success */ return TRUE; } /* * Read to one or two newline sequences. */ int TadsHttpServerThread::read_to_nl(StringRef *dst, long ofs, int init_state, int end_state) { /* newline state: 0 \r -> 1 \n -> 2 \r -> 3 \n -> 4 */ int nlstate = init_state; /* keep going until we find the newline or newline pair */ for (;;) { char buf[8192]; /* scan up to the ending offset */ const char *p = dst->get() + ofs; long endofs = dst->getlen(); for ( ; ofs < endofs && nlstate != end_state ; ++ofs, ++p) { if (nlstate == 2 && *p == '\r') nlstate = 3; else if (*p == '\r') nlstate = 1; else if ((nlstate == 1 || nlstate == 3) && *p == '\n') nlstate += 1; else nlstate = 0; } /* if we're in the end state, we're done */ if (nlstate == end_state) break; /* * We didn't find the end sequence, so we need more input. Read at * least one byte with no timeout, so that we block until * something's available and then read all available bytes. */ long len = read(buf, sizeof(buf), 1, OS_FOREVER); set_run_state("Processing request"); /* if we got the 'quit' signal, stop now */ if (len < 0) return -1; /* append the text to the buffer */ dst->append(buf, len); } /* return the buffer offset of the end of the terminating newline */ return ofs; } /* * Process a request from our HTTP client. The main server loop calls this * when the socket has data ready to read. */ int TadsHttpServerThread::process_request() { StringRef *hdrs = new StringRef(1024); TadsHttpRequestHeader *hdr_list = 0, *hdr_tail = 0; TadsHttpRequest *req = 0; char *verb; size_t verb_len; char *resource_name; size_t res_name_len; int ok = FALSE; long ofs; char *p, *hbody; StringRef *body = 0; int overflow = FALSE; long hbodylen; const char *hcl, *hte; /* content-length, transfer-encoding */ /* * Read the header. We read data into our buffer until we find a * double CR-LF sequence, indicating the end of the header. */ if ((ofs = read_to_nl(hdrs, 0, 0, 4)) < 0) goto done; /* the body, if any, starts after the double line break */ hbody = hdrs->get() + ofs; hbodylen = hdrs->getlen() - ofs; /* truncate the headers to the CR-LF-CR-LF sequence */ hdrs->truncate(ofs - 2); /* * Parse the main verb in the header - get the method and the resource * ID. The format is: * *. * VERB + RESOURCE + HTTP-VERSION */ p = hdrs->get(); parse_tok(p, verb, verb_len); parse_tok(p, resource_name, res_name_len); /* now parse the remaining headers */ TadsHttpRequestHeader::parse_headers(hdr_list, hdr_tail, FALSE, hdrs, 0); /* * Check to see if there's a message body. There is if there's a * content-length or transfer-encoding header. */ hcl = hdr_list->find("content-length"); hte = hdr_list->find("transfer-encoding"); if (hcl != 0 || hte != 0) { /* * There's a content body. If there's a content-length field, * pre-allocate a chunk of memory, then read the number of bytes * indicated. If it's a chunked transfer, read it in pieces. */ if (hcl != 0) { /* get the length */ long hclval = atol(hcl); /* if it's non-zero, read the content */ if (hclval != 0) { /* if this exceeds the size limit, abort */ if (upload_limit != 0 && hclval > upload_limit) { /* set the overflow flag, discard the input, and abort */ overflow = TRUE; read(0, 0, hclval, 5000); goto done_with_upload; } /* allocate the buffer; it's initially empty */ body = new StringRef(hclval); /* copy any portion of the body we've already read */ if (hbodylen != 0) { /* limit this to the declared size */ if (hbodylen > hclval) hbodylen = hclval; /* copy the data */ body->append(hbody, hbodylen); /* deduct the remaining size */ hclval -= hbodylen; } /* read the body */ if (hclval != 0 && read(body->getend(), hclval, hclval, 5000) < 0) { send_simple(S_http_400, "text/html", "Error receiving request message body"); goto done; } /* set the body length */ body->addlen(hclval); } } else if (stricmp(hte, "chunked") == 0) { /* set up a string buffer for the content */ const long initlen = 32000; body = new StringRef(hbodylen > initlen ? hbodylen : initlen); /* if we've already read some body text, copy it to the buffer */ if (hbodylen != 0) body->append(hbody, hbodylen); /* keep going until we reach the end marker */ for (ofs = 0 ; ; ) { /* read to the first newline */ long nlofs = read_to_nl(body, ofs, 0, 2); if (nlofs < 0) goto done; /* get the chunk length */ long chunklen = strtol(body->get() + ofs, 0, 16); /* * We're done with the chunk length. Move any read-ahead * content down in memory so that it directly abuts the * preceding chunk, so that when we're done we'll have the * content assembled into one contiguous piece. */ long ralen = body->getlen() - nlofs; if (ralen > 0) memmove(body->get() + ofs, body->get() + nlofs, body->getlen() - nlofs); /* stop at the end of the read-ahead portion */ body->truncate(ofs + ralen); /* if the chunk length is zero, we're done */ if (chunklen == 0) break; /* check if this would overflow our upload size limit */ if (upload_limit != 0 && !overflow && body->getlen() + chunklen > upload_limit) { /* flag the overflow, but keep reading the content */ overflow = TRUE; } /* * figure the remaining read size for this chunk, after any * read-ahead portion */ long chunkrem = chunklen - ralen; /* * if we've already overflowed, read and discard the chunk; * otherwise read it into our buffer */ if (chunkrem + 2 <= 0) { /* * We've already read ahead by enough to cover the next * chunk and the newline at the end. Go in and delete * the newline (or as much of it as exists). Start by * getting the offset of the newline: it's just after * the current chunk, which starts at 'ofs' and runs * for 'chunklen'. */ nlofs = ofs + chunklen; /* * if we haven't yet read the full newline sequence, do * so now */ if (ralen - chunklen < 2) { long nllen = 2 - (ralen - chunklen); body->ensure(nllen); if (read(body->getend(), nllen, nllen, 30000) < 0) { send_simple(S_http_400, "text/html", "Error receiving request message chunk"); goto done; } } /* * if there's anything after the newline, move it down * to overwrite the newline */ if (ralen - chunklen > 2) { /* move the bytes */ memmove(body->get() + nlofs, body->get() + nlofs + 2, ralen - chunklen - 2); /* adjust the size for the closed gap */ body->truncate(body->getlen() - 2); } /* resume from after the newline */ ofs = nlofs; } else if (overflow) { /* overflow - discard the chunk size (plus the CR-LF) */ if (read(0, 0, chunkrem+2, 30000) < 0) goto done; /* clear the buffer */ body->truncate(0); ofs = 0; } else { /* ensure the buffer is big enough */ body->ensure(chunkrem + 2); /* read the chunk, plus the CR-LF that follows */ if (read(body->getend(), chunkrem + 2, chunkrem + 2, 30000) < 0) { send_simple(S_http_400, "text/html", "Error receiving request message chunk"); goto done; } /* count the amount we just read in the buffer length */ body->addlen(chunkrem + 2); /* verify that the last two bytes are indeed CR-LF */ if (memcmp(body->getend() - 2, "\r\n", 2) != 0) { send_simple(S_http_400, "text/html", "Error in request message chunk"); goto done; } /* * move past the chunk for the next read, minus the * CR-LF that follows the chunk - that isn't part of * the data, but just part of the protocol */ ofs = body->getlen() - 2; body->truncate(ofs); } } /* * Read to the closing blank line. We've just passed one * newline, following the '0' end length marker, so we're in * state 2. We could have either another newline following, or * "trailers" (more headers, following the content body). */ long nlofs = read_to_nl(body, ofs, 2, 4); if (nlofs < 0) goto done; /* * if we have more than just the closing blank line, we have * trailers - append them to the headers */ if (nlofs > ofs + 2) { TadsHttpRequestHeader::parse_headers( hdr_list, hdr_tail, TRUE, body, ofs); } /* * the trailers (if any) and closing newline aren't part of the * content - clip them out of the body */ body->truncate(ofs); } else { /* * Other combinations of these headers are illegal. Send an * error and abort. */ send_simple(S_http_400, "text/html", "Bad Request" "

Bad Request

" "This server does not accept the specified " "transfer-encoding."); goto done; } done_with_upload: /* done with the upload - check for overflow */ if (overflow) { /* release the body, if present */ if (body != 0) { body->release_ref(); body = 0; } } } /* create a message object for the request */ req = new TadsHttpRequest( this, verb, verb_len, hdrs, hdr_list, body, overflow, resource_name, res_name_len, listener->get_shutdown_evt()); /* we've handed over the header list to the request */ hdr_list = 0; /* send it to the main thread, and wait for the reply */ if (queue->send(req, OS_FOREVER)) { /* * success - the server will already have sent the reply, so we're * done */ } else if (queue->is_quitting()) { /* failed due to server shutdown */ send_simple(S_http_503, "text/html", S_http_503_quitting_body); } else { /* 'send' failed - return an internal server error */ send_simple(S_http_500, "text/html", S_http_500_body); } /* we're done with the request */ req->release_ref(); /* success */ ok = TRUE; done: /* release the message body buffer */ if (body != 0) body->release_ref(); /* release the header buffer */ hdrs->release_ref(); /* delete the header list */ if (hdr_list != 0) delete hdr_list; /* return the success/failure indication */ return ok; } /* ------------------------------------------------------------------------ */ /* * Complete an HTTP request */ void TadsHttpRequest::complete() { /* * if the client included a "connection" header with value "close", * close the socket */ const char *conn = (hdr_list != 0 ? hdr_list->find("connection") : 0); if (conn != 0 && stricmp(conn, "close") == 0 && thread != 0) thread->close_socket(); /* mark the message queue object as completed */ TadsEventMessage::complete(); } frobtads-1.2.3/tads3/vmtzobj.h0000644000175000001440000001660011723501740015371 0ustar realncusers/* * Copyright (c) 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtzobj.h - CVmObjTimeZone object Function Notes Modified 02/06/12 MJRoberts - Creation */ #ifndef VMTZOBJ_H #define VMTZOBJ_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" /* ------------------------------------------------------------------------ */ /* * The image file data simply contains the name of the time zone, as an * ASCII string, with a one-byte length prefix. The block is arranged as * follows: * *. UBYTE name_length *. ASCII name */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension data structure. */ struct vm_tzobj_ext { /* allocate the structure */ static vm_tzobj_ext *alloc_ext(VMG_ class CVmObjTimeZone *self, class CVmTimeZone *tz); /* our CVmTimeZone object */ class CVmTimeZone *tz; }; /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone intrinsic class definition */ class CVmObjTimeZone: public CVmObject { friend class CVmMetaclassTimeZone; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* get my CVmTimeZone object */ class CVmTimeZone *get_tz() const { return get_ext()->tz; } /* is this a CVmObjTimeZone object? */ static int is_CVmObjTimeZone_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * Parse TimeZone constructor arguments, returning a CVmTimeZone cache * object if the arguments match, throwing an error if not. Doesn't * actually create a TimeZone object; simply finds the underlying cache * object. */ static class CVmTimeZone *parse_ctor_args( VMG_ const vm_val_t *argp, int argc); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create from a given CVmTimeZone object */ static vm_obj_id_t create(VMG_ class CVmTimeZone *tz); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObject:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* * receive savepoint notification - we don't keep any * savepoint-relative records, so we don't need to do anything here */ void notify_new_savept() { } /* we're immutable, so there's no undo to mess with */ void apply_undo(VMG_ struct CVmUndoRecord *) { } void discard_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } /* we have no outgoing references */ void mark_refs(VMG_ uint) { } void remove_stale_weak_refs(VMG0_) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* reload from an image file */ void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* time zone objects are immutable */ int is_changed_since_load() const { return FALSE; } /* cast to a string - returns the time zone name */ const char *cast_to_string(VMG_ vm_obj_id_t self, vm_val_t *newstr) const; protected: /* get my extension data */ vm_tzobj_ext *get_ext() const { return (vm_tzobj_ext *)ext_; } /* load or reload image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* create a with no initial contents */ CVmObjTimeZone() { ext_ = 0; } /* create with contents */ CVmObjTimeZone(VMG_ class CVmTimeZone *tz); /* * parse a zone name by GMT offset, specified as a string in +-hhmm or * +-hh[:mm[:ss]] format, with the given prefix */ static class CVmTimeZone *parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len); /* property evaluator - undefined function */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* getNames method */ int getp_getNames(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getHistory method */ int getp_getHistory(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getRules method */ int getp_getRules(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* getLocation method */ int getp_getLocation(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); /* property evaluation function table */ static int (CVmObjTimeZone::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * CVmObjTimeZone metaclass registration table object */ class CVmMetaclassTimeZone: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "timezone/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTimeZone(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjTimeZone(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjTimeZone::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjTimeZone:: call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMTZOBJ_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjTimeZone) frobtads-1.2.3/tads3/vmtz.cpp0000644000175000001440000022031712145504453015236 0ustar realncusers/* * Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtz.cpp - TADS time zone management Function Implements the time zone cache manager and time zone objects. Notes None Modified 02/05/12 MJRoberts - creation */ #include #include #include #include #include #include "t3std.h" #include "osstzprs.h" #include "vmtz.h" #include "vmhash.h" #include "vmglob.h" #include "resload.h" #include "vmhost.h" #include "vmdate.h" #include "vmlst.h" #include "vmstr.h" #include "vmtzobj.h" /* ------------------------------------------------------------------------ */ /* * zone table hash entry */ /* hash table entry types */ #define ZONE_HASH_DB 1 /* defined in the zoneinfo db file */ #define ZONE_HASH_LINK 2 /* link (alias) to another zone */ #define ZONE_HASH_SYNTH 3 /* synthesized from an on os_tzinfo_t */ class ZoneHashEntry: public CVmHashEntryCI { public: ZoneHashEntry(const char *str, size_t len, int copy, int typ) : CVmHashEntryCI(str, len, copy) { this->tz = 0; this->typ = typ; this->nxt = 0; this->link_from = 0; } virtual ~ZoneHashEntry() { /* if we loaded a timezone object, delete it */ if (tz != 0) delete tz; } /* resolve links - by default, we don't have any links */ virtual ZoneHashEntry *resolve_links() { return this; } /* get my country code, if I have one */ virtual const char *get_country() { return 0; } /* match my country code to that of another zone */ int match_country(const char *country) { const char *this_country = get_country(); return (this_country != 0 && country[0] != '\0' && memcmp(this_country, country, 2) == 0); } /* entry type (ZONE_HASH_xxx) */ int typ; /* the cached zone object, if loaded */ CVmTimeZone *tz; /* the head of the list of zones that link to us */ class ZoneHashEntryLink *link_from; /* * In addition to the hash table, we keep a simple linked list of all * of the loaded zone objects. This lets us do iteraitons over the * loaded list more easily (and quickly) than using the hash table. */ ZoneHashEntry *nxt; }; class ZoneHashEntryDb: public ZoneHashEntry { public: ZoneHashEntryDb(const char *str, size_t len, int copy, uint32_t seekpos, const char *country) : ZoneHashEntry(str, len, copy, ZONE_HASH_DB), seekpos(seekpos) { memcpy(this->country, country, 2); } /* seek position in the database file */ uint32_t seekpos; /* country code */ virtual const char *get_country() { return country; } char country[2]; }; class ZoneHashEntryLink: public ZoneHashEntry { public: ZoneHashEntryLink(const char *str, size_t len, int copy, ZoneHashEntry *link_to) : ZoneHashEntry(str, len, copy, ZONE_HASH_LINK) { /* add myself to the target's 'from' list */ this->link_to = link_to->resolve_links(); link_nxt = link_to->link_from; this->link_to->link_from = this; } /* resolve links - get the target hash entry for a linked hash entry */ ZoneHashEntry *resolve_links() { return link_to->resolve_links(); } /* get the country from my resolved link */ const char *get_country() { return resolve_links()->get_country(); } /* the zone we link to */ ZoneHashEntry *link_to; /* the next link that links to our link_to */ ZoneHashEntryLink *link_nxt; }; class ZoneHashEntrySynth: public ZoneHashEntry { public: ZoneHashEntrySynth(const char *str, size_t len, int copy, const os_tzinfo_t *desc) : ZoneHashEntry(str, len, copy, ZONE_HASH_SYNTH) { /* copy the description */ memcpy(&synth, desc, sizeof(synth)); } /* synthetic zone description */ os_tzinfo_t synth; }; /* ------------------------------------------------------------------------ */ /* * zone search spec */ struct ZoneSearchSpec { ZoneSearchSpec() { abbr[0] = '\0'; gmtofs = INT32MAXVAL; dst = 'S'; } ZoneSearchSpec(const char *abbr, int32_t gmtofs, char dst) : gmtofs(gmtofs), dst(dst) { set_abbr(abbr); } void set(const char *abbr, int32_t gmtofs, char dst) { set_abbr(abbr); this->gmtofs = gmtofs; this->dst = dst; } void set(const char *abbr, size_t abbrlen, int32_t gmtofs, char dst) { set_abbr(abbr, abbrlen); this->gmtofs = gmtofs; this->dst = dst; } /* abbreviation string */ char abbr[16]; void set_abbr(const char *str, size_t len) { lib_strcpy(abbr, sizeof(abbr), str, len); } void set_abbr(const char *str) { set_abbr(str, str != 0 ? strlen(str) : 0); } /* GMT offset in milliseconds */ int32_t gmtofs; /* 'D' for daylight time, 'S' for standard time */ char dst; }; /* ------------------------------------------------------------------------ */ /* * Abbreviation hash entry. This records a mapping from a zone name * abbreviation (e.g., "EST") to a list of associated zone objects. */ /* individual mapping from an abbreviation to a zone */ struct AbbrToZone { /* the zone we link to */ ZoneHashEntry *zone; /* the GMT offset for this abbreviation in this zone, in milliseconds */ int32_t gmtofs; /* * 'D' if this is daylight savings time, 'S' for standard time, 'B' if * it the abbreviation can refer to both types (Australian zones work * this way: EST is Australian Eastern Standard Time AND Eastern Summer * Time). When we have a 'B' zone, we'll also have the regular 'D' and * 'S' entries for it, so that searches for the specific types will * succeed; the 'B' entry is for get_zone_by_abbr(), to let it know * that the zone isn't to be treated as a single fixed offset the way, * say, PDT would be. */ char dst; }; /* hash entry */ class AbbrHashEntry: public CVmHashEntryCI { public: AbbrHashEntry(const char *str, size_t len, int copy, int entry_cnt) : CVmHashEntryCI(str, len, copy) { this->entry_cnt = entry_cnt; this->entries = new AbbrToZone[entry_cnt]; } ~AbbrHashEntry() { delete [] entries; } void set_zone(int i, ZoneHashEntry *zone, int32_t gmtofs, char dst) { entries[i].zone = zone; entries[i].gmtofs = gmtofs; entries[i].dst = dst; } /* search for a match to a search spec */ ZoneHashEntry *search(int start_idx, const ZoneSearchSpec *spec) { for (int i = start_idx ; i < entry_cnt ; ++i) { if (entries[i].gmtofs == spec->gmtofs && entries[i].dst == spec->dst) return entries[i].zone; } return 0; } /* search for a match to a search spec and a zone object */ ZoneHashEntry *search(int start_idx, const ZoneSearchSpec *spec, const ZoneHashEntry *zone) { for (int i = start_idx ; i < entry_cnt ; ++i) { if (entries[i].zone == zone && entries[i].gmtofs == spec->gmtofs && entries[i].dst == spec->dst) return entries[i].zone; } return 0; } int entry_cnt; AbbrToZone *entries; }; /* ------------------------------------------------------------------------ */ /* * Time zone cache manager */ /* * construction */ CVmTimeZoneCache::CVmTimeZoneCache() { /* create the zone tables */ db_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); synth_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); /* we haven't loaded our zone database yet */ index_loaded_ = index_loaded_ok_ = FALSE; zone_bytes_ = 0; link_bytes_ = 0; abbr_tab_ = 0; abbr_bytes_ = 0; first_loaded_ = 0; local_zone_ = 0; } /* * destruction */ CVmTimeZoneCache::~CVmTimeZoneCache() { /* delete our zone table, zone index, and abbreviation table */ if (db_tab_ != 0) delete db_tab_; if (synth_tab_ != 0) delete synth_tab_; if (abbr_tab_ != 0) delete abbr_tab_; /* if we have a local zone object, delete it */ if (local_zone_ != 0) delete local_zone_; /* if we loaded the file data sections, delete them */ if (zone_bytes_ != 0) delete [] zone_bytes_; if (link_bytes_ != 0) delete [] link_bytes_; if (abbr_bytes_ != 0) delete [] abbr_bytes_; } /* ------------------------------------------------------------------------ */ /* * Translate a ZoneHashEntry to a CVmTimeZone object */ CVmTimeZone *CVmTimeZoneCache::tz_from_hash(VMG_ ZoneHashEntry *entry) { /* if there's no table entry, there's nothing to load */ if (entry == 0) return 0; /* resolve links */ entry = entry->resolve_links(); /* if we haven't loaded the timezone object yet, do so now */ if (entry->tz == 0) { /* load it */ entry->tz = CVmTimeZone::load(vmg_ entry); /* if successful, add it to the loaded zone list */ if (entry->tz != 0) { entry->nxt = first_loaded_; first_loaded_ = entry; } } /* return the cached object */ return entry->tz; } /* ------------------------------------------------------------------------ */ /* * Parse a zone name in various formats */ CVmTimeZone *CVmTimeZoneCache::parse_zone(VMG_ const char *name, size_t len) { /* treat a null, empty string, or ":local" as the local zone */ if (name == 0 || len == 0 || (len == 6 && memcmp(name, ":local", 6) == 0)) return get_local_zone(vmg0_); /* check for special zones: "Z", "UTC", "GMT" all mean UTC+0 */ if (lib_stricmp(name, len, "z") == 0 || lib_stricmp(name, len, "utc") == 0 || lib_stricmp(name, len, "gmt") == 0) return get_gmtofs_zone(vmg_ 0); /* try +8, -830, +8:30, -8:00:00 */ CVmTimeZone *tz; if ((tz = parse_zone_hhmmss(vmg_ "", name, len)) != 0) return tz; /* try UTC+8, UTC-830, UTC+8:30, UTC-8:00:00 */ if ((tz = parse_zone_hhmmss(vmg_ "utc", name, len)) != 0) return tz; /* try parsing as a POSIX-style EST5EDT string */ if ((tz = parse_zone_posixTZ(vmg_ name, len)) != 0) return tz; /* it's not one of our special formats; look it up in the database */ if ((tz = G_tzcache->get_db_zone(vmg_ name, len)) != 0) return tz; /* return failure */ return 0; } /* * Parse a time zone spec in simplified POSIX TZ notation: EST-5, EST-5EDT, * EST-5EDT-4. */ CVmTimeZone *CVmTimeZoneCache::parse_zone_posixTZ( VMG_ const char *name, size_t len) { /* * Run it through the POSIX TZ parser. Note that the zoneinfo format * uses negative offsets going west (e.g., EST-5), in contrast to the * original TZ format. */ os_tzinfo_t desc; if (oss_parse_posix_tz(&desc, name, len, FALSE)) { /* got it - check to see if it's already defined */ ZoneHashEntry *entry = (ZoneHashEntry *)synth_tab_->find(name, len); if (entry == 0) { /* it's not there yet - create a new entry for it */ entry = new ZoneHashEntrySynth(name, len, TRUE, &desc); synth_tab_->add(entry); } /* find or create its timezone object */ return tz_from_hash(vmg_ entry); } /* it's not ours */ return 0; } /* * get a zone specified in a +hh, +hmm, +hhmm, +hh:mm or +hh:mm:ss format */ CVmTimeZone *CVmTimeZoneCache::parse_zone_hhmmss( VMG_ const char *prefix, const char *name, size_t len) { /* check for the prefix - if it doesn't match, return failure */ size_t plen = strlen(prefix); if (plen != 0 && (len < plen || memicmp(name, prefix, plen) != 0)) return 0; /* skip the prefix */ name += plen; len -= plen; /* parse the offset */ int32_t ofs; if (!parse_hhmmss(ofs, name, len, TRUE) || len != 0) return 0; /* convert the hh:mm:ss value to seconds and look up the zone */ return get_gmtofs_zone(vmg_ ofs); } /* * parse a [+-]h[:mm[:ss]] time/offset value */ int CVmTimeZoneCache::parse_hhmmss( int32_t &ofs, const char *&name, size_t &len, int sign_required) { /* check for the sign */ int s = 1; if (len > 0 && name[0] == '+') ++name, --len; else if (len > 0 && name[0] == '-') ++name, --len, s = -1; else if (sign_required) return FALSE; /* parse the hours */ const char *hhp = name; int hh = lib_atoi_adv(name, len); /* if we had no digits, or more than four in a row, fail */ size_t hhlen = name - hhp; if (hhlen < 1 || hhlen > 4) return FALSE; /* if we had three or more consecutive digits, it's hmm or hhmm format */ int mm = 0, ss = 0; if (hhlen >= 3) { /* the hour is actually hours/minutes - divide out the components */ mm = hh % 100; hh /= 100; } else { /* not hmm/hhmm format, so it's hh[:mm[:ss]] - check for minutes */ if (len >= 3 && *name == ':' && isdigit(name[1]) && isdigit(name[2])) { /* parse the minutes */ ++name, --len; mm = lib_atoi_adv(name, len); /* check for seconds */ if (len >= 3 && *name == ':' && isdigit(name[1]) && isdigit(name[2])) { ++name, --len; ss = lib_atoi_adv(name, len); } } } /* success - compute the offset value in seconds */ ofs = s*(hh*60*60 + mm*60 + ss); return TRUE; } /* ------------------------------------------------------------------------ */ /* * Look up or load a time zone from the database */ CVmTimeZone *CVmTimeZoneCache::get_db_zone(VMG_ const char *name, size_t len) { /* if we haven't loaded the zone index data yet, do so now */ load_db_index(vmg0_); /* look up the hash table entry */ return tz_from_hash(vmg_ (ZoneHashEntry *)db_tab_->find(name, len)); } /* * Generate an offset string in the minimal format: h, h:mm, h:mm:ss. */ #define F_HIDE_ZERO 0x0001 /* suppress a zero offset */ #define F_PLUS_SIGN 0x0002 /* use a plus sign for positive offsets */ static void gen_ofs_string(char *buf, size_t buflen, const char *prefix, int32_t ofs, int flags) { /* figure the sign */ const char *signch = ""; if (ofs < 0) ofs = -ofs, signch = "-"; else if ((flags & F_PLUS_SIGN) != 0) signch = "+"; /* decompose the offset into hours, minutes, and seconds */ int hh = ofs / (60*60); int mm = ofs/60 % 60; int ss = ofs % 60; /* * figure the format: prefix, sign character, then h:mm:ss if we have * non-zero seconds, h:mm if we have non-zero minutes but no seconds, * or just the hours if the minutes and seconds are both zero */ const char *fmt = (ss != 0 ? "%s%s%d:%02d:%02d" : mm != 0 ? "%s%s%d:%02d" : hh != 0 || (flags & F_HIDE_ZERO) == 0 ? "%s%s%d" : "%s"); /* generate the string */ t3sprintf(buf, buflen, fmt, prefix, signch, hh, mm, ss); } /* * Look up or load a time zone for a GMT offset, in seconds */ CVmTimeZone *CVmTimeZoneCache::get_gmtofs_zone(VMG_ int32_t gmtofs_secs) { /* build our special name string */ char name[50]; gen_ofs_string(name, sizeof(name), "UTC", gmtofs_secs, F_HIDE_ZERO | F_PLUS_SIGN); /* if we already have an entry cached, return it */ ZoneHashEntry *entry = (ZoneHashEntry *)synth_tab_->find(name, strlen(name)); if (entry != 0) return tz_from_hash(vmg_ entry); /* create a new synthetic entry for the zone */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = gmtofs_secs; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), name); entry = new ZoneHashEntrySynth(name, strlen(name), TRUE, &desc); /* add it to the hash table */ synth_tab_->add(entry); /* translate it to a CVmTimeZone object */ return tz_from_hash(vmg_ entry); } /* * Look up a zone by abbreviation. * * Zone abbreviations are inherently ambiguous, and there's no reliable way * to figure out which zone a user might mean. The ideal solution would be * for users to stop using zone abbreviations entirely and switch to * zoneinfo location keys instead. But that's a tough sell, especially * since most users are blissfully unaware that the problem even exists - * who in the US knows that "CST" is also the name for time zones in Cuba, * China, and Australia? It's unreasonable not to expect users to plug in * abbreviations when entering date strings - especially given that the * Date parser is otherwise very liberal about accepting free-form, * colloquial formats. So we feel it's best to give it the college try at * understanding zone abbreviations, but recognizing that it's ultimately a * "best effort" sort of task, in that it's not something we can cleanly * and definitively solve. * * We fill in *gmtofs_ms with the GMT offset implied by the zone * abbreviation. If this is one of the funny Australian zones that uses * the same abbreviation for both standard and daylight time, we return * INT32MINVAL in *gmtofs_ms instead, to indicate that the abbreviation * doesn't specify an offset by itself, but only specifies the zone; the * offset in this case must be looked up via a date-sensitive query as * though the zone had been specified by zoneinfo name instead of by * abbreviation. */ CVmTimeZone *CVmTimeZoneCache::get_zone_by_abbr( VMG_ int32_t *gmtofs_ms, const char *name, size_t len) { vmtzquery lresult; /* if we haven't loaded the zone index data yet, do so now */ load_db_index(vmg0_); /* make sure we have an abbreviation table */ if (abbr_tab_ == 0) return 0; /* look up the abbrevation */ AbbrHashEntry *ah = (AbbrHashEntry *)abbr_tab_->find(name, len); /* if there's no table entry, or it's empty, there's nothing to find */ if (ah == 0 || ah->entry_cnt == 0) return 0; /* get the local zone information */ CVmTimeZone *ltz = get_local_zone(vmg0_); const char *country = ltz->country(); /* we don't have our selection yet */ AbbrToZone *atoz = 0, *ep; /* * First, look to see if this is an abbreviation used in our system's * local time zone. If so use that, since the most likely case is that * the user is referring to her own local time. */ int i; for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (ep->zone->tz == ltz) { atoz = ep; goto done; } } /* * If that didn't work, look for a zone in the same country as the * local time zone. This seems like the best bet for matching the * user's expectations. */ for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (ep->zone->match_country(country)) { atoz = ep; goto done; } } /* * If that failed, look for something within +- 3 hours of the local * zone. That will at least get something in the same part of the * world, and probably in the same country or a nearby country. Most * zones that use the same abbreviation probably aren't right next door * to each other, either, so this should be pretty selective. (There * are exceptions. CST in North America is Central Standard Time at * -6, but it's also Cuba Standard Time at -5. Hopefully most nearby * zone authorities aren't as mutually antagonistic as the US and Cuba * and do a better job of coordinating their zone naming.) */ ltz->query(&lresult); for (i = 0, ep = ah->entries ; i < ah->entry_cnt ; ++i, ++ep) { if (abs(lresult.gmtofs - ep->gmtofs) < 3*60*60*1000) { atoz = ep; goto done; } } /* * If all else fails, match the first zone in the list. Our zoneinfo * compiler places a specially hand-chosen primary zone for each * abbreviation at the head of the abbreviation's zone list. The hand * picks favor US and European zones, so this isn't the ideal way to * handle this globally, but in the absence of any other selection * criteria there's not much else we can do. */ atoz = &ah->entries[0]; done: /* * Pass the abbreviation's offset back to the caller. Most zone * abbreviations refer to a fixed offset; e.g., PST is always -8, even * in the summer when the US Pacific Time zone is observing daylight * time for wall clock time. Writing "PST" explicitly means that we're * talking about the -8 setting, rather than the current wall clock * setting. * * However, some zones (all in Australia, in the current zoneinfo * database) use a single abbreviation for both standard and daylight * time, so these zones *don't* carry the special fixed offset * information with the abbreviation. For example, Australian EST is * Eastern Standard Time in the winter and Eastern Summer Time in the * summer - same abbreviation, two different clock settings. So if we * determine that we're talking about Australian EST, we need to * determine the offset from the time of year, as we would for a spec * like America/Los_Angeles (as opposed to PST). Abbreviations with * multiple offsets in one zone are marked with 'B' as in the DST * field. */ *gmtofs_ms = (atoz->dst != 'B' ? atoz->gmtofs : INT32MINVAL); /* return the timezone object */ return tz_from_hash(vmg_ atoz->zone); } /* ------------------------------------------------------------------------ */ /* * Get the local system time zone. * * The local time zone is special, in that it's always represented as a * distinct CVmTimeZone object (and a distinct entry in our hash table) * from the object for the zone it represents. This allows callers to * distinguish a date that's implicitly in the local zone from one that's * explicitly in the zone that happens to be the local zone. For example, * in the CVmObjDate package, a date specified as "1 pm" is implicitly in * the local time zone, whereas a date specified as "1 pm PST" is * explicitly in Pacific Standard Time. If the local zone happens to be * PST, both dates will be in PST now; but if the objects are saved and * later restored on another machine that's in New York, the first date ("1 * pm") will be rendered in EST, whereas the second ("1 pm PST") will * continue to be rendered in PST. To make this possible, we have to know * that the first date's CVmTimeZone object is the special "local" object, * separate from the second date's CVmTimeZone object - even though both * represent the same underlying database entry. * * To implement this, if we resolve the local zone to a database entry, we * load a separate copy of the database entry so that the local zone is a * distinct object. */ CVmTimeZone *CVmTimeZoneCache::get_local_zone(VMG0_) { /* if we have a cached local zone, return it */ if (local_zone_ != 0) return local_zone_; /* we haven't found our zone entry yet */ ZoneHashEntry *entry = 0; /* see if we can get a zoneinfo database key from the operating system */ char name[128]; if (os_get_zoneinfo_key(name, sizeof(name)) && load_db_index(vmg0_) && (entry = (ZoneHashEntry *)db_tab_->find(name, strlen(name))) != 0) { /* we found a database entry - load a separate copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } /* if we didn't find it, try the lower-level timezone description */ os_tzinfo_t info; if (local_zone_ == 0 && os_get_timezone_info(&info)) { /* * If they were able to give us separate standard and daylight * offsets and abbreviations, search for the pair. Otherwise * search for the current one. * * Note that the os_tzinfo_t specifies offsets in seconds, whereas * the ZoneSearchSpec uses milliseconds, so multiply accordingly. */ if (info.std_ofs != info.dst_ofs && info.std_abbr[0] != '\0' && info.dst_abbr[0] != '\0') { ZoneSearchSpec spec[2]; spec[0].set(info.std_abbr, info.std_ofs*1000, 'S'); spec[1].set(info.dst_abbr, info.dst_ofs*1000, 'D'); entry = search(vmg_ spec, 2); } else { /* * they couldn't distinguish standard and daylight, so just use * the one that's currently in effect */ ZoneSearchSpec spec; if (info.is_dst) spec.set(info.dst_abbr, info.dst_ofs*1000, 'D'); else spec.set(info.std_abbr, info.std_ofs*1000, 'S'); entry = search(vmg_ &spec, 1); } /* check to see if we found a matching entry in the database */ if (entry != 0) { /* we found an entry - load a separate copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } else { /* * we didn't find an entry, so synthesize a new one based on * the zone description we got from the OS */ local_zone_ = new CVmTimeZone(&info); } } /* * If we still don't have anything, try the the standard C++ library's * time functions. These won't give us as much information as we were * hoping to get from the OS, and probably won't work at all given that * the OS routines probably went to the same primary sources that the * CRTL will look at, but it's worth trying as a last resort. */ if (local_zone_ == 0) { /* strftime(%Z) format gives us the local time zone */ os_time_t t = os_time(0); struct tm *tm = os_localtime(&t); strftime(name, sizeof(name), "%Z", tm); /* if we got a zone, look it up */ if (name[0] != '\0') { /* * Get the current minutes in the day in local time, and the * local time day number. For the day number, use the year * times 366 plus the day of the year; this doesn't correspond * to a serial day number in our caldate_t system or really any * other system, but it does produce monotonic values, which is * all we need: our only use for this day number if to compare * the UTC and local day numbers to see if the respective * clocks are on different days, and if so in which direction. */ int ss_local = tm->tm_hour*60*60 + tm->tm_min*60 + tm->tm_sec; long dayno_local = tm->tm_year*366L + tm->tm_yday; char dst = tm->tm_isdst ? 'D' : 'S'; /* repeat the exercise in local time */ tm = os_gmtime(&t); int ss_utc = tm->tm_hour*60*60 + tm->tm_min*60 + tm->tm_sec; long dayno_utc = tm->tm_year*366L + tm->tm_yday; /* if the clocks are on different days, adjust accordingly */ if (dayno_utc > dayno_local) ss_utc += 24*60*60; else if (dayno_utc < dayno_local) ss_utc -= 24*60*60; /* * Search for the zone. For the GMT offset, figure the * difference in milliseconds between local and UTC. */ ZoneSearchSpec spec(name, (ss_local - ss_utc)*1000, dst); if ((entry = search(vmg_ &spec, 1)) != 0) { /* we found a database entry - load a private copy */ local_zone_ = CVmTimeZone::load(vmg_ entry); } else { /* * no database match; synthesize an entry based on the time * zone info we got from the C time functions */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); desc.std_ofs = desc.dst_ofs = ss_local - ss_utc; lib_strcpy(desc.std_abbr, sizeof(desc.std_abbr), name); lib_strcpy(desc.dst_abbr, sizeof(desc.dst_abbr), name); desc.is_dst = tm->tm_isdst; local_zone_ = new CVmTimeZone(&desc); } } } /* if after all that we still have nothing, use UTC */ if (local_zone_ == 0) { /* try looking up the basic UTC zone */ os_tzinfo_t desc; memset(&desc, 0, sizeof(desc)); strcpy(desc.std_abbr, "UTC"); local_zone_ = new CVmTimeZone(&desc); } /* return the local zone we came up with */ return local_zone_; } /* ------------------------------------------------------------------------ */ /* * Create a missing database entry. This represents a database zone that's * not defined in the version of the database we're using, but was imported * from a saved game created with another database version. */ CVmTimeZone *CVmTimeZoneCache::create_missing_zone( VMG_ const char *name, size_t namelen, const os_tzinfo_t *desc) { /* create the entry */ ZoneHashEntry *e = new ZoneHashEntrySynth(name, namelen, TRUE, desc); /* add it to our database table */ db_tab_->add(e); /* return the new entry */ return tz_from_hash(vmg_ e); } /* ------------------------------------------------------------------------ */ /* * search for a zone that matches all of the given search specs */ ZoneHashEntry *CVmTimeZoneCache::search( VMG_ const ZoneSearchSpec *specs, int cnt) { /* if there are no abbreviations, fail */ if (!load_db_index(vmg0_) || abbr_tab_ == 0) return 0; /* if we're searching just one list, this is easy... */ if (cnt == 1) { /* look up this abbreviation */ AbbrHashEntry *e = (AbbrHashEntry *)abbr_tab_->find( specs[0].abbr, strlen(specs[0].abbr)); if (e == 0) return 0; /* return the first entry that matches the spec */ return e->search(0, specs); } else if (cnt == 2) { /* look up the two abbreviations */ AbbrHashEntry *e0 = (AbbrHashEntry *)abbr_tab_->find( specs[0].abbr, strlen(specs[0].abbr)); AbbrHashEntry *e1 = (AbbrHashEntry *)abbr_tab_->find( specs[1].abbr, strlen(specs[1].abbr)); /* if either abbreviation is missing, fail */ if (e0 == 0 || e0->entry_cnt == 0 || e1 == 0 || e1->entry_cnt == 0) return 0; /* find the earliest intersection */ for (int i = 0, j = 0 ; i < e0->entry_cnt && j < e1->entry_cnt ; ++i, ++j) { /* find the next first-list element matching the first spec */ ZoneHashEntry *z0 = e0->search(i, &specs[0]); if (z0 == 0) return 0; /* * look for a second-list element that matches the second spec * AND the first-list zone we just matched */ ZoneHashEntry *z1 = e1->search(j, &specs[1], z0); if (z1 != 0) return z1; /* * no luck; find the next second-list element matching just the * second spec, then find a first-list item matching z1 and the * first spec */ z1 = e1->search(j, &specs[1]); if ((z0 = e0->search(i+1, &specs[0], z1)) != 0) return z0; } /* didn't find it */ return 0; } /* * we're really only used for STD+DST searches; if we're called upon * for anything else, it's unexpected */ return 0; } /* ------------------------------------------------------------------------ */ /* * Load the zoneinfo database index. Returns true on success, false on * failure. */ int CVmTimeZoneCache::load_db_index(VMG0_) { /* if we've already loaded the index (or tried to), we're done */ if (index_loaded_) return index_loaded_ok_; /* we've now tried loading the index */ index_loaded_ = TRUE; /* open the zone file */ osfildef *fp = open_zoneinfo_file(vmg0_); if (fp == 0) return FALSE; /* read the signature */ char buf[24]; int ok = !osfrb(fp, buf, 24) && memcmp(buf, "T3TZ", 4) == 0; /* if the signature checked out, read the zone table */ uint32_t zone_table_len = 0, zone_cnt = 0; if (ok) { /* decode the zone name table length */ zone_table_len = osrp4(buf + 16) - 4; zone_cnt = osrp4(buf + 20); /* allocate space to load the table */ zone_bytes_ = new char[zone_table_len]; /* read the table */ ok = zone_bytes_ != 0 && !osfrb(fp, zone_bytes_, zone_table_len); } /* if everything is working so far, read the link table */ uint32_t link_table_len = 0; if (ok && !osfrb(fp, buf, 8)) { /* load the link table */ link_table_len = osrp4(buf) - 4; /* uint32_t link_cnt = osrp4(buf+4) - not currently needed */ link_bytes_ = new char[link_table_len]; ok = link_bytes_ != 0 && !osfrb(fp, link_bytes_, link_table_len); } /* if we're still good, read the abbreviation mappings */ uint32_t abbr_table_len = 0; if (ok && !osfrb(fp, buf, 8)) { /* load the abbreviation table */ abbr_table_len = osrp4(buf) - 4; /* uint32_t abbr_cnt = osrp4(buf + 4) - not currently needed */ abbr_bytes_ = new char[abbr_table_len]; ok = abbr_bytes_ != 0 && !osfrb(fp, abbr_bytes_, abbr_table_len); } /* we're done with the file */ osfcls(fp); /* if we read the zone table successfully, create a hash table from it */ ZoneHashEntry **zone_index = 0; if (ok) { /* create the numerical index */ zone_index = new ZoneHashEntry *[zone_cnt]; /* populate it */ const char *zone_end = zone_bytes_ + zone_table_len; int zi = 0; for (const char *p = zone_bytes_ ; p < zone_end ; ) { /* read and skip the name length prefix */ size_t len = osrp1(p); ++p; /* create this entry */ ZoneHashEntry *e = new ZoneHashEntryDb( p, len, FALSE, osrp4(p+len+1), p+len+1+4); /* add it to the hash table and zone index */ db_tab_->add(e); zone_index[zi++] = e; /* skip the name, seek pointer, and country code */ p += len + 1 + 4 + 2; } /* read the links */ const char *link_end = link_bytes_ + link_table_len; for (const char *p = link_bytes_ ; p < link_end ; ) { /* read and skip the name length */ size_t len = osrp1(p); ++p; /* read the TO index */ unsigned int to = osrp4(p + len + 1); /* look up the TO field */ ZoneHashEntry *e_to = (to < zone_cnt ? zone_index[to] : 0); /* if we found it, create the new entry for the FROM */ if (e_to != 0) db_tab_->add(new ZoneHashEntryLink(p, len, FALSE, e_to)); /* skip the entry */ p += len + 1 + 4; } } /* if there are no errors so far, read the abbreviation table */ if (ok) { /* create the abbreviation hash table */ abbr_tab_ = new CVmHashTable(64, new CVmHashFuncCI(), TRUE); /* populate it */ const char *abbr_end = abbr_bytes_ + abbr_table_len; for (const char *p = abbr_bytes_ ; p < abbr_end ; ) { /* read and skip the abbreviation length prefix */ size_t len = osrp1(p); ++p; /* read the number of zone entries */ int entry_cnt = osrp1(p + len + 1); /* create and add the hash table entry */ AbbrHashEntry *e = new AbbrHashEntry(p, len, FALSE, entry_cnt); abbr_tab_->add(e); /* populate the entry */ int ei; for (p += len + 1 + 1, ei = 0 ; ei < entry_cnt ; p += 9, ++ei) { /* look up the zone */ uint32_t zi = osrp4(p); ZoneHashEntry *zone = (zi < zone_cnt ? zone_index[zi] : 0); /* add the entry */ e->set_zone(ei, zone, osrp4s(p+4), p[8]); } } } /* we're done with the numerical zone index */ if (zone_index != 0) delete [] zone_index; /* return the status */ index_loaded_ok_ = ok; return ok; } /* * Open the zoneinfo file */ osfildef *CVmTimeZoneCache::open_zoneinfo_file(VMG0_) { /* open the file through the system resource loader */ return G_host_ifc->get_sys_res_loader()->open_res_file( "timezones.t3tz", 0, "TMZN"); } /* ------------------------------------------------------------------------ */ /* * Time zone object */ /* file data block sizes */ const int TRANS_SIZE = 9; const int TYPE_SIZE = 9; const int RULE_SIZE = 17; /* * construction */ CVmTimeZone::CVmTimeZone(ZoneHashEntryDb *entry, const char *file_data, unsigned int trans_cnt, unsigned int type_cnt, unsigned int rule_cnt, unsigned int abbr_bytes) { /* keep a pointer to our hash table entry */ hashentry_ = entry; /* since we have a hash entry, we don't need a separate name */ name_ = 0; /* allocate our arrays (do this first, as they have cross references) */ trans_ = (trans_cnt_ = trans_cnt) != 0 ? new vmtz_trans[trans_cnt] : 0; rule_ = (rule_cnt_ = rule_cnt) != 0 ? new vmtz_rule[rule_cnt] : 0; ttype_ = (ttype_cnt_ = type_cnt) != 0 ? new vmtz_ttype[type_cnt] : 0; abbr_ = (abbr_bytes != 0 ? new char[abbr_bytes] : 0); /* assume we have no descriptive data */ country_[0] = '\0'; coords_[0] = '\0'; desc_ = 0; /* set up to read from the file data block */ const char *p = file_data; unsigned int i; /* decode the transition list */ vmtz_trans *t; for (t = trans_, i = 0 ; i < trans_cnt ; ++i, ++t, p += TRANS_SIZE) { t->dayno = osrp4s(p); t->daytime = osrp4s(p+4); unsigned int idx = osrp1(p+8); t->ttype = idx < type_cnt ? &ttype_[idx] : 0; } /* decode the rule list */ vmtz_rule *r; for (r = rule_, i = 0 ; i < rule_cnt ; ++i, ++r, p += RULE_SIZE) { unsigned int idx = osrp1(p); r->fmt = idx < abbr_bytes ? abbr_ + idx : 0; r->mm = osrp1(p+1); r->when = osrp1(p+2); r->dd = osrp1(p+3); r->weekday = osrp1(p+4); r->at = osrp4s(p+5); r->gmtofs = osrp4s(p+9); r->save = osrp4s(p+13); /* decode the time zone from the AT data */ r->at_zone = (r->at >> 24) & 0xff; r->at &= 0x00ffffff; } /* decode the type list */ vmtz_ttype *tt; for (tt = ttype_, i = 0 ; i < type_cnt ; ++i, ++tt, p += TYPE_SIZE) { tt->gmtofs = osrp4s(p); tt->save = osrp4s(p+4); unsigned int idx = osrp1(p+8); tt->fmt = idx < abbr_bytes ? abbr_ + idx : 0; } /* copy the format abbreviation strings */ if (abbr_bytes != 0) { memcpy(abbr_, p, abbr_bytes); p += abbr_bytes; } /* save the descriptive data, if available */ if (*p != '\0') { /* save the country code */ lib_strcpy(country_, sizeof(country_), p, 2); p += 2; /* save the coordinate string */ lib_strcpy(coords_, sizeof(coords_), p, 15); p += 16; /* save the description */ size_t dlen = osrp1(p); desc_ = new char[dlen + 1]; lib_strcpy(desc_, dlen + 1, p+1, dlen); p += dlen + 2; } else { /* null byte for country - no descriptive information */ } #if 0// DEBUG $$$ for (t = trans_, i = 0 ; i < trans_cnt ; ++i, ++t) { caldate_t cd(t->dayno); printf("%04d-%02d-%02d %02d:%02d -> GMT%+d (%+d) %s\n", cd.y, cd.m, cd.d, (int)t->daytime/(60*60), (int)->daytime/60 % 60, t->ttype->gmtofs, t->ttype->save, t->ttype->fmt); } #endif } /* * Create a synthesized zone from an os descriptor */ CVmTimeZone::CVmTimeZone(ZoneHashEntrySynth *entry) { /* keep a pointer to our zone entry */ hashentry_ = entry; /* since we have a hash entry, we don't need a separate name */ name_ = 0; /* initialize from the descriptor */ init(&entry->synth); } /* * Create a synthesized zone from an os descriptor */ CVmTimeZone::CVmTimeZone(const os_tzinfo_t *desc) { /* no hash entry */ hashentry_ = 0; /* * Synthesize a POSIX-style name from the abbreviation and offset. If * we have both standard and daylight abbreviations and distinct * offsets, generate a POSIX-style "EST5DST" string. Otherwise just * use the active abbreviation and offset. * * Note that the offset in POSIX strings uses the opposite sign from * what we use - EST is +5 in POSIX strings vs -5 in the zoneinfo * scheme (which is what we use). */ char buf[128]; if (desc->std_abbr[0] != 0 && desc->dst_abbr[0] != 0 && desc->std_ofs != desc->dst_ofs) { /* * Use both abbreviations, as in EST5DST. If the daylight offset * is one hour higher than the standard offset, omit the offset * from the daylight part, since that's the default; otherwise * include it. */ char buf1[128], buf2[128]; gen_ofs_string(buf1, sizeof(buf1), desc->std_abbr, -desc->std_ofs, 0); if (desc->dst_ofs == desc->std_ofs + 3600) lib_strcpy(buf2, sizeof(buf2), desc->dst_abbr); else gen_ofs_string(buf2, sizeof(buf2), desc->dst_abbr, -desc->dst_ofs, 0); /* build the combined string */ t3sprintf(buf, sizeof(buf), "%s%s", buf1, buf2); } else if (desc->is_dst) { /* use the daylight abbreviation only */ gen_ofs_string(buf, sizeof(buf), desc->dst_abbr, -desc->dst_ofs, 0); } else { /* use the standard time abbreviation only */ gen_ofs_string(buf, sizeof(buf), desc->std_abbr, -desc->std_ofs, 0); } /* save the name */ name_ = lib_copy_str(buf); /* initialize from the descriptor */ init(desc); } /* * Initialize from an OS timezone descriptor */ void CVmTimeZone::init(const os_tzinfo_t *desc) { /* we don't have any transitions or rules */ trans_ = 0; trans_cnt_ = 0; rule_ = 0; rule_cnt_ = 0; /* make a copy of our one abbreviation(s) */ size_t std_abbr_len = strlen(desc->std_abbr); size_t dst_abbr_len = strlen(desc->dst_abbr); abbr_ = new char[std_abbr_len + dst_abbr_len + 2]; char *std_abbr = abbr_; strcpy(std_abbr, desc->std_abbr); char *dst_abbr = abbr_ + std_abbr_len + 1; strcpy(dst_abbr, desc->dst_abbr); /* if rules for the start/end dates are specified, use them */ if ((desc->dst_start.jday != 0 || desc->dst_start.yday != 0 || desc->dst_start.month != 0) && (desc->dst_end.jday != 0 || desc->dst_end.yday != 0 || desc->dst_end.month != 0)) { /* we have start/end rules - create our copies */ rule_cnt_ = 2; rule_ = new vmtz_rule[2]; /* * Set the rules from the OS description. Note that the OS * description gives offsets in seconds, whereas we store * everything in milliseconds. */ rule_[0].set(&desc->dst_start, dst_abbr, desc->std_ofs*1000, (desc->dst_ofs - desc->std_ofs)*1000); rule_[1].set(&desc->dst_end, std_abbr, desc->std_ofs*1000, 0); /* * allocate space for two types - one for standard time, one for * daylight time */ ttype_ = new vmtz_ttype[2]; ttype_cnt_ = 2; /* set up the standard time information */ ttype_[0].gmtofs = desc->std_ofs * 1000; ttype_[0].save = 0; ttype_[0].fmt = std_abbr; /* set up the daylight time information */ ttype_[1].gmtofs = desc->dst_ofs * 1000; ttype_[1].save = (desc->dst_ofs - desc->std_ofs) * 1000; ttype_[1].fmt = dst_abbr; } else { /* * There are no transition rules, so we only have use for whichever * setting is currently in effect. */ ttype_ = new vmtz_ttype[1]; ttype_cnt_ = 1; /* use the current setting only */ ttype_[0].save = 0; if (desc->is_dst) { ttype_[0].gmtofs = desc->dst_ofs * 1000; ttype_[0].fmt = dst_abbr; } else { ttype_[0].gmtofs = desc->std_ofs * 1000; ttype_[0].fmt = std_abbr; } } /* we have no descriptive information */ country_[0] = '\0'; coords_[0] = '\0'; desc_ = 0; } /* * destruction */ CVmTimeZone::~CVmTimeZone() { /* delete allocated items */ lib_free_str(name_); if (trans_ != 0) delete [] trans_; if (ttype_ != 0) delete [] ttype_; if (rule_ != 0) delete [] rule_; if (abbr_ != 0) delete [] abbr_; if (desc_ != 0) delete [] desc_; } /* * Load or create a CVmTimeZone object from a hash table entry. This * creates a new object, even if the hash table entry already has an * associated object. */ CVmTimeZone *CVmTimeZone::load(VMG_ ZoneHashEntry *entry) { /* load according to the data source in the hash table entry */ if (entry->typ == ZONE_HASH_DB) { /* it's in the database file - open the file */ osfildef *fp = CVmTimeZoneCache::open_zoneinfo_file(vmg0_); if (fp == 0) return 0; /* seek to our data */ osfseek(fp, ((ZoneHashEntryDb *)entry)->seekpos, OSFSK_SET); /* read the length prefix */ char pfx[2]; int ok = !osfrb(fp, pfx, 2); /* allocate space for the file data */ unsigned int alo = osrp2(pfx); char *buf = ok ? new char[alo] : 0; /* read the file data */ ok = buf != 0 && !osfrb(fp, buf, alo); /* decode the header */ unsigned int trans_cnt = osrp2(buf); unsigned int type_cnt = osrp2(buf+2); unsigned int rule_cnt = osrp1(buf+4); unsigned int abbr_bytes = osrp1(buf+5); /* create the time zone object */ CVmTimeZone *tz = 0; if (ok) { tz = new CVmTimeZone( (ZoneHashEntryDb *)entry, buf+6, trans_cnt, type_cnt, rule_cnt, abbr_bytes); } /* release resources */ if (buf != 0) delete [] buf; osfcls(fp); /* return the loaded object */ return tz; } else if (entry->typ == ZONE_HASH_LINK) { /* load from the linked object */ return load(vmg_ entry->resolve_links()); } else if (entry->typ == ZONE_HASH_SYNTH) { /* create from the zone descriptor */ return new CVmTimeZone((ZoneHashEntrySynth *)entry); } else { /* unknown type */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get the zone name */ const char *CVmTimeZone::get_name(size_t &len) const { /* use the name, or the hash table name, or the primary abbreviation */ if (name_ != 0) { /* we have an explicit name field - use it */ len = strlen(name_); return name_; } else if (hashentry_ != 0) { /* get the name from our hash table entry */ len = hashentry_->getlen(); return hashentry_->getstr(); } else { /* no name or hash entry - use our primary abbreviation */ len = strlen(abbr_); return abbr_; } } /* * Populate a List object with our alias list. This sets the first element * to our primary name, and remaining elements to aliases. */ vm_obj_id_t CVmTimeZone::get_name_list(VMG0_) const { /* no names found so far */ int cnt = 0; /* count names, depending on what information we have */ if (hashentry_ != 0) { /* the hash entry gives us our primary name */ cnt += 1; /* count incoming alias links */ for (ZoneHashEntryLink *e = hashentry_->link_from ; e != 0 ; e = e->link_nxt, ++cnt) ; } else if (name_ != 0) { /* we have only the one primary name */ cnt += 1; } else if (abbr_ != 0 && abbr_[0] != '\0') { /* no name, but we at least have an abbreviation we can use in lieu */ cnt += 1; } /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, cnt); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* start at index 0 */ int idx = 0; vm_val_t ele; /* * add our primary name - this is our explicit name_ string, our hash * entry name, or our primary abbreviation (in order of priority) */ if (hashentry_ != 0) { /* set the name from our hash entry */ ele.set_obj(CVmObjString::create( vmg_ FALSE, hashentry_->getstr(), hashentry_->getlen())); lst->cons_set_element(idx++, &ele); /* add our aliases */ for (ZoneHashEntryLink *link = hashentry_->link_from ; link != 0 ; link = link->link_nxt) { ele.set_obj(CVmObjString::create( vmg_ FALSE, link->getstr(), link->getlen())); lst->cons_set_element(idx++, &ele); } } else if (name_ != 0) { /* the first item is our primary name */ ele.set_obj(CVmObjString::create(vmg_ FALSE, name_, strlen(name_))); lst->cons_set_element(idx++, &ele); } else if (abbr_ != 0 && abbr_[0] != '\0') { /* use our primary abbreviation as our name */ ele.set_obj(CVmObjString::create(vmg_ FALSE, abbr_, strlen(abbr_))); lst->cons_set_element(idx++, &ele); } /* discard the gc protection */ G_stk->discard(); /* return the new list object */ return lstid; } /* * Get a history item for a given date */ vm_obj_id_t CVmTimeZone::get_history_item( VMG_ int32_t dayno, int32_t daytime) const { /* query the transition for the given date/time */ vmtzquery result; query(&result, dayno, daytime, FALSE); /* synthesize a transition description */ vmtz_ttype tty(&result); vmtz_trans t(result.start_dayno, result.start_daytime, &tty); /* build and return the List representation */ return t.get_as_list(vmg0_); } /* * Get a list of history items */ vm_obj_id_t CVmTimeZone::get_history_list(VMG0_) const { /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, trans_cnt_ + 1); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* set the special first item for the pre-establishment settings */ vmtz_trans t_pre(INT32MINVAL, INT32MINVAL, &ttype_[0]); vm_val_t ele; ele.set_obj(t_pre.get_as_list(vmg0_)); lst->cons_set_element(0, &ele); /* run through the transitions */ vmtz_trans *t = trans_; for (int i = 1 ; i <= trans_cnt_ ; ++i, ++t) { /* add this transition to the list */ ele.set_obj(t->get_as_list(vmg0_)); lst->cons_set_element(i, &ele); } /* discard the gc protection and return the result list */ G_stk->discard(1); return lstid; } /* * Get a list of the ongoing rules */ vm_obj_id_t CVmTimeZone::get_rule_list(VMG0_) const { /* allocate the result list; stack it for gc protection */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, rule_cnt_); G_stk->push()->set_obj(lstid); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* run through the rules */ vmtz_rule *r = rule_; for (int i = 0 ; i < rule_cnt_ ; ++i, ++r) { /* create a sublist to describe this rule */ vm_val_t ele; ele.set_obj(CVmObjList::create(vmg_ FALSE, 10)); lst->cons_set_element(i, &ele); /* get the sublist and clear it out */ CVmObjList *elst = (CVmObjList *)vm_objp(vmg_ ele.val.obj); elst->cons_clear(); /* [1] = abbreviation */ ele.set_obj(CVmObjString::create(vmg_ FALSE, r->fmt, strlen(r->fmt))); elst->cons_set_element(0, &ele); /* [2] = standard time offset in milliseconds */ ele.set_int(r->gmtofs); elst->cons_set_element(1, &ele); /* [3] = daylight savings delta in milliseconds */ ele.set_int(r->save); elst->cons_set_element(2, &ele); /* [4] = encoded 'when' rule: "Mar lastSun", "Mar Sun>=7", "Mar 7" */ char buf[128]; static const char *mon = "JanFebMarAprMayJunJulAugSepOctNov"; static const char *wkdy = "SunMonTueWedThuFriSat"; static const char *tpl[] = { "%.0s%02d", "last %.3s", "%.3s>=%d", "%.3s<=d" }; if (r->when == 0 && r->dd > 31) { /* day of month past 31 -> it's actually a day of the year */ t3sprintf(buf, sizeof(buf), "DOY %d", r->dd); } else if (r->when >= 0 && r->when < countof(tpl) && r->weekday >= 1 && r->weekday <= 7) { /* valid 'when' code */ memcpy(buf, mon + (r->mm - 1)*3, 3); buf[3] = ' '; t3sprintf(buf+4, sizeof(buf)-4, tpl[(unsigned char)r->when], wkdy + (r->weekday-1)*3, r->dd); } else { /* invalid 'when' code */ t3sprintf(buf, sizeof(buf), "INVAL(when=%d, weekday=%d, dd=%d)", r->when, r->weekday, r->dd); } ele.set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); elst->cons_set_element(3, &ele); /* [5] = month */ ele.set_int(r->mm); elst->cons_set_element(4, &ele); /* [6] = when */ ele.set_int(r->when); elst->cons_set_element(5, &ele); /* [7] = day of the month */ ele.set_int(r->dd); elst->cons_set_element(6, &ele); /* [8] = weekday */ ele.set_int(r->weekday); elst->cons_set_element(7, &ele); /* [9] = at time */ ele.set_int(r->at); elst->cons_set_element(8, &ele); /* [10] = at zone */ char u = (r->at_zone == 0x80 ? 'u' : r->at_zone == 0x40 ? 's' : 'w'); ele.set_obj(CVmObjString::create(vmg_ FALSE, &u, 1)); elst->cons_set_element(9, &ele); } /* discard the gc protection and return the result list */ G_stk->discard(1); return lstid; } /* ------------------------------------------------------------------------ */ /* * Query the time zone information at the current moment */ void CVmTimeZone::query(vmtzquery *result) const { /* get the current system time */ int32_t dayno, daytime; caldate_t::now(dayno, daytime); /* query the time zone information */ query(result, dayno, daytime, FALSE); } /* * Query the time zone information at the given time (UTC); daytime is * milliseconds after midnight. */ void CVmTimeZone::query(vmtzquery *result, int32_t dayno, int32_t daytime, int local) const { /* * if we're after the last transition, or we have no transitions, check * for ongoing rules */ if (trans_cnt_ == 0 || trans_[trans_cnt_-1].compare_to(dayno, daytime, local) <= 0) { /* * We won't find our answer in the transition list, so check for * ongoing rules that cover out-of-bounds dates. */ if (rule_cnt_ != 0) { /* * We have ongoing rules. * * Starting in the year AFTER the target date, search for the * last (in time) rule that fires on or before the target date. * This is the rule that defines the time zone for the target * date. If we don't find a rule on or before the target date, * back up one year and repeat. * * The reason we have to start next year is that there's a * possibility that the target date will be in the following * year in local time; rules are generally stated in local * time, so this difference could matter. For example, if the * target date is Dec 31 2010 23:59 UTC, that could be Jan 1 * 2011 07:59 local time. If we then had a rule that fired at * 01:00 on Jan 1, that rule in 2011 would be the correct * result of our search. This is a pathological case that's * unlikely to arise in the wild, but it's not impossible. * Likewise, a target date on Jan 1 could end up preceding a * rule that fires on Dec 31 the previous year; looping * backwards on year until we find a rule that fires on or * before the target will catch these cases. * * Note that our zoneinfo compiler guarantees that it will * produce at least one complete cycle fixed transitions (in * our trans_ list) for the open-ended rules. Since we're * after our last transition, we know that the period before a * rule's trigger date is controlled by the previous rule (in * time order, treating the list as circular) and not by any * other zone history change. This makes it possible to work * backwards through the list, since we don't have to look for * other possible zone definitions in the period before each * rule. */ caldate_t cd(dayno); for (int yy = cd.y + 1 ; yy >= cd.y - 2 ; --yy) { /* look for the last rule firing on or after the target */ int latest = -1; int32_t latest_rday = 0, latest_rtime = 0; for (int i = 0 ; i < rule_cnt_ ; ++i) { /* * we might need the previous rule to resolve the * current rule; the list is circular because it forms * an annual cycle */ int iprv = (i == 0 ? rule_cnt_ : i) - 1; /* get the concrete date for this rule in this year */ int32_t rday, rtime; rule_[i].resolve(rday, rtime, yy, &rule_[iprv]); /* adjust to local time if that's what we're looking for */ if (local) { rtime += rule_[i].gmtofs + rule_[i].save; caldate_t::normalize(rday, rtime); } /* * If the target date is after the resolved rule firing * time, and this rule is later than the last one we * matched, this is the best match so far. (Assume * that we never have two rules on the same day, so we * don't need to bother checking rtime against the last * matching rule.) Note that we don't match exactly at * the rule time - our policy is to put the moment of a * transition in the previous period. */ if ((dayno > rday || (dayno == rday && daytime > rtime)) && (latest < 0 || rday > latest_rday)) { latest = i; latest_rday = rday; latest_rtime = rtime; } } /* if we found a match, return its settings */ if (latest >= 0) { result->set(&rule_[latest], latest_rday, latest_rtime); return; } } /* * We should never get here - it should be *almost* impossible * not to find a match in the prior year, but not quite * impossible: if a zone only has rules that fire late on Dec * 31, we could have a target early on Jan 1 UTC which, when * translated to local time, is before all of the Dec 31 rules. * But it should be truly impossible not to find a match in * target year minus 2 - no UTC-to-local difference could take * us back a whole year. So there's something wrong in our * logic if we get here... */ assert(FALSE); } else if (trans_cnt_ != 0) { /* * There are no rules, and we're after the last transition, so * the last transition is permanent. */ result->set(&trans_[trans_cnt_-1]); } else { /* * There are no rules and no transitions. This means that the * zone is defined entirely by its first time type entry. */ result->set(&ttype_[0], INT32MINVAL, INT32MINVAL); return; } } /* * If we're at or before the first transition, use the * pre-establishment settings. This is usually the local mean time * settings from before a standard time zone was first established at * this location. */ if (trans_[0].compare_to(dayno, daytime, local) >= 0) { /* the pre-establishment settings are in ttype[0] */ result->set(&ttype_[0], INT32MINVAL, INT32MINVAL); return; } /* * The date is within our transition list, so search for the nearest * transition on or before the target date. We're looking for a * date/time after our first transition and before our last, so it's a * date covered by a history item. Search our transition list for the * target date/time. */ int lo = 0, hi = trans_cnt_ - 1, cur; while (lo < hi) { /* split the difference */ cur = (hi + lo)/2; /* check this entry */ vmtz_trans *tcur = &trans_[cur]; int dir = tcur->compare_to(dayno, daytime, local); if (dir < 0) { /* tcur < target - search the upper half */ lo = cur + 1; } else if (dir > 0) { /* tcur > target - search the lower half */ hi = cur - 1; } else { /* * exact match - treat the moment of the transition as being in * the previous interval, since we usually define the moment of * the transition in terms of the local time that was in effect * up until that moment */ result->set(tcur > 0 ? tcur - 1 : tcur); return; } } /* check the final 'lo' value; it might have ended up one too high */ cur = trans_[lo].compare_to(dayno, daytime, local) >= 0 && lo > 0 && trans_[lo-1].compare_to(dayno, daytime, local) < 0 ? lo - 1 : lo; /* set the result */ result->set(&trans_[cur]); } /* ------------------------------------------------------------------------ */ /* * Compare my date to the given date, which can be expressed in either UTC * or local wall clock time. */ int vmtz_trans::compare_to(int32_t dayno, int32_t daytime, int local) const { /* get my value, converting to local time if required */ int32_t dd = this->dayno, tt = this->daytime; if (local) { tt += ttype->gmtofs + ttype->save; caldate_t::normalize(dd, tt); } /* compare by day, then by time if on the same day */ return (dd != dayno ? dd - dayno : tt - daytime); } /* * Create a List object representing the transition */ vm_obj_id_t vmtz_trans::get_as_list(VMG0_) const { /* create a result list for this item - [date, ofs, save, fmt] */ vm_obj_id_t lstid = CVmObjList::create(vmg_ FALSE, 4); G_stk->push()->set_obj(lstid); /* get the list and clear it out, since we're building it in pieces */ CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ lstid); lst->cons_clear(); /* * Element [1] is the transition time as a Date object, or nil if the * date is INT32MINVALs (which represents the beginning of time, for * the pre-establishment settings). */ vm_val_t ele; if (dayno == INT32MINVAL && daytime == INT32MINVAL) ele.set_nil(); else ele.set_obj(CVmObjDate::create(vmg_ FALSE, dayno, daytime)); lst->cons_set_element(0, &ele); /* element [2] is the UTC offset */ ele.set_int(ttype->gmtofs); lst->cons_set_element(1, &ele); /* element [3] is the daylight savings delta */ ele.set_int(ttype->save); lst->cons_set_element(2, &ele); /* element [4] is the abbreviation string */ ele.set_obj(CVmObjString::create( vmg_ FALSE, ttype->fmt, strlen(ttype->fmt))); lst->cons_set_element(3, &ele); /* discard gc protection and return the list object */ G_stk->discard(1); return lstid; } /* ------------------------------------------------------------------------ */ /* * synthesize a ttype from a query result */ vmtz_ttype::vmtz_ttype(const vmtzquery *query) { gmtofs = query->gmtofs; save = query->save; fmt = query->abbr; } /* ------------------------------------------------------------------------ */ /* * Resolve a rule in the given year. Returns the firing time in UTC. */ void vmtz_rule::resolve(int32_t &dayno, int32_t &daytime, int year, const vmtz_rule *prev_rule) { /* set up our year and month; we have yet to work out the day */ int w, delta; caldate_t cd(year, mm, 0); /* figure out the day according to our type */ switch (when) { case 0: /* the rule takes effect on a the fixed day 'dd' */ cd.d = dd; break; case 1: /* * The rule takes effect on the last of the month. Get * the weekday of the last day of this month (which we can figure * using day 0 of next month). */ cd.m += 1; w = cd.weekday() + 1; /* figure the days from mm/last to the previous target weekday */ delta = w - weekday; if (delta < 0) delta += 7; /* go back that many days */ cd.d -= delta; break; case 2: /* first on or after
- get the weekday of
*/ cd.d = dd; w = cd.weekday() + 1; /* figure days between
and the next */ delta = weekday - w; if (delta < 0) delta += 7; /* go ahead that many days */ cd.d += delta; break; case 3: /* first on or before
- get the weekday of
*/ cd.d = dd; w = cd.weekday() + 1; /* figure the days from
to the next */ delta = weekday - w; if (delta < 0) delta += 7; /* go ahead that many days and back a week */ cd.d += delta - 7; break; } /* we now have our effective date and time */ dayno = cd.dayno(); daytime = at; /* adjust the time zone */ switch (at_zone) { case 0x80: /* the rule was stated in UTC, which is what we're after */ break; case 0x00: /* * The rule was stated in local wall clock time, in the period in * effect just before the rule. That means we use the zone * settings of the *previous* rule, which defines the period * leading up to this rule's firing time. Convert to UTC by * subtracting the standard time offset plus the daylight offset * for the prior period. */ daytime -= prev_rule->gmtofs + prev_rule->save; break; case 0x40: /* * the rule was stated in local standard time, so subtract the * standard time offset for the prior period */ daytime -= prev_rule->gmtofs; break; } /* normalize the time to keep it within 0-23:59:59 */ caldate_t::normalize(dayno, daytime); } /* ------------------------------------------------------------------------ */ /* * Set from an OS descriptor */ void vmtz_rule::set(const os_tzrule_t *desc, const char *abbr, int32_t gmtofs, int32_t save) { /* set the basic description */ this->fmt = abbr; this->gmtofs = gmtofs; this->save = save; /* the OS description always uses local wall clock time */ this->at = desc->time * 1000; this->at_zone = 0; /* transcode the date to our format */ if (desc->jday != 0) { /* * "Jn" = nth day of the year, NOT counting Feb 29. Since the date * mapping is the same for leap years and non-leap years, this is * just an obtuse way of specifying a month and day. So, figure * the mm/dd date that the Jn date represents, and map it to our * mode 0 (day
of ). We can figure the month/day by * setting up a calendar with "January N" in any non-leap year - * since it's a non-leap year, it won't have a Feb 29, so we'll get * the right mapping for dates after J59. */ caldate_t cd(2011, 1, desc->jday); cd.normalize_date(); this->when = 0; this->mm = (char)cd.m; this->dd = (short)cd.d; } else if (desc->yday != 0) { /* * "n" = nth day of the year, counting Feb 29. We can encode this * as "January n", even if n > 31, since our calendar calculator * handle overflows in the day of the month by carrying them into * subsequent months. */ this->when = 0; this->mm = 1; this->dd = (short)desc->yday; } else if (desc->month != 0) { /* Month/week/day spec */ this->mm = (char)desc->month; this->weekday = (char)desc->day; if (desc->week == 5) { /* week 5 means "last ", which is our mode 1 */ this->when = 1; } else { /* * week 1-4 means "nth "; we can encode this as mode * 2, "weekday>=n", where n is 1 for the first week, 8 for the * second week, 15 for the third week, 22 for the fourth week */ this->when = 2; this->dd = (char)(desc->week - 1)*7 + 1; } } } /* ------------------------------------------------------------------------ */ /* * Convert a UTC time to local */ const char *CVmTimeZone::utc_to_local( int32_t &dayno, int32_t &daytime, int32_t &tzofs) { /* look up the time zone information at the given time */ vmtzquery result; query(&result, dayno, daytime, FALSE); /* figure the full offset */ tzofs = result.gmtofs + result.save; /* adjust the time to local by adding the GMT offset */ daytime += tzofs; caldate_t::normalize(dayno, daytime); /* return the abbreviation */ return result.abbr; } /* * Convert a local time to UTC */ void CVmTimeZone::local_to_utc(int32_t &dayno, int32_t &daytime) { /* look up the time zone information at the given local time */ vmtzquery result; query(&result, dayno, daytime, TRUE); /* adjust the time to local by subtracting the GMT offset */ daytime -= result.gmtofs + result.save; caldate_t::normalize(dayno, daytime); } frobtads-1.2.3/tads3/tctok.cpp0000644000175000001440000110255212145504453015363 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/tctok.cpp,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctok.cpp - TADS3 compiler tokenizer Function Notes The tokenizer features an integrated C-style preprocessor. The preprocessor is integrated into the tokenizer for efficiency; since the preprocessor uses the same lexical structure as the the TADS language, we need only tokenize the input stream once, and the result can be used both for preprocessing and for parsing. Modified 04/12/99 MJRoberts - Creation */ #include #include #include #include #include "os.h" #include "t3std.h" #include "vmerr.h" #include "vmhash.h" #include "tcerr.h" #include "tcerrnum.h" #include "tctok.h" #include "tcsrc.h" #include "tcmain.h" #include "tchost.h" #include "tcprs.h" #include "tctarg.h" #include "charmap.h" #include "vmdatasrc.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Standard macro table. This implements the interface using a standard * hash table object. */ class CTcBasicMacroTable: public CTcMacroTable { public: CTcBasicMacroTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func) : tab(hash_table_size, hash_function, own_hash_func) { } virtual void add(CVmHashEntry *entry) { tab.add(entry); } virtual void remove(CVmHashEntry *entry) { tab.remove(entry); } virtual CVmHashEntry *find(const char *str, size_t len) { return tab.find(str, len); } virtual void enum_entries(void (*func)(void *, CVmHashEntry *), void *ctx) { tab.enum_entries(func, ctx); } virtual void debug_dump() { tab.debug_dump(); } private: /* our hash table */ CVmHashTable tab; }; /* ------------------------------------------------------------------------ */ /* * string embedded expression context */ void tok_embed_ctx::start_expr(wchar_t qu, int triple, int report) { if (level < countof(stk)) { s = stk + level; s->enter(qu, triple); } else if (report) { G_tok->log_error(TCERR_EMBEDDING_TOO_DEEP); } ++level; } /* ------------------------------------------------------------------------ */ /* * Initialize the tokenizer */ CTcTokenizer::CTcTokenizer(CResLoader *res_loader, const char *default_charset) { int i; os_time_t timer; struct tm *tblk; const char *tstr; char timebuf[50]; struct kwdef { const char *kw_text; tc_toktyp_t kw_tok_id; }; static const kwdef kwlist[] = { { "self", TOKT_SELF }, { "targetprop", TOKT_TARGETPROP }, { "targetobj", TOKT_TARGETOBJ }, { "definingobj", TOKT_DEFININGOBJ }, { "inherited", TOKT_INHERITED }, { "delegated", TOKT_DELEGATED }, { "argcount", TOKT_ARGCOUNT }, { "if", TOKT_IF }, { "else", TOKT_ELSE }, { "for", TOKT_FOR }, { "while", TOKT_WHILE }, { "do", TOKT_DO }, { "switch", TOKT_SWITCH }, { "case", TOKT_CASE }, { "default", TOKT_DEFAULT }, { "goto", TOKT_GOTO }, { "break", TOKT_BREAK }, { "continue", TOKT_CONTINUE }, // { "and", TOKT_AND }, // { "or", TOKT_OR }, // { "not", TOKT_NOT }, { "function", TOKT_FUNCTION }, { "return", TOKT_RETURN }, { "local", TOKT_LOCAL }, { "object", TOKT_OBJECT }, { "nil", TOKT_NIL }, { "true", TOKT_TRUE }, { "pass", TOKT_PASS }, { "external", TOKT_EXTERNAL }, { "extern", TOKT_EXTERN }, { "formatstring", TOKT_FORMATSTRING }, { "class", TOKT_CLASS }, { "replace", TOKT_REPLACE }, { "modify", TOKT_MODIFY }, { "new", TOKT_NEW }, // { "delete", TOKT_DELETE }, { "throw", TOKT_THROW }, { "try", TOKT_TRY }, { "catch", TOKT_CATCH }, { "finally", TOKT_FINALLY }, { "intrinsic", TOKT_INTRINSIC }, { "dictionary", TOKT_DICTIONARY }, { "grammar", TOKT_GRAMMAR }, { "enum", TOKT_ENUM }, { "template", TOKT_TEMPLATE }, { "static", TOKT_STATIC }, { "foreach", TOKT_FOREACH }, { "export", TOKT_EXPORT }, { "propertyset", TOKT_PROPERTYSET }, { "transient", TOKT_TRANSIENT }, { "replaced", TOKT_REPLACED }, { "property", TOKT_PROPERTY }, { "operator", TOKT_OPERATOR }, { "method", TOKT_METHOD }, { "invokee", TOKT_INVOKEE }, // { "void", TOKT_VOID }, // { "int", TOKT_INT }, // { "string", TOKT_STRING }, // { "list", TOKT_LIST }, // { "boolean", TOKT_BOOLEAN }, // { "any", TOKT_ANY }, /* end-of-table marker */ { 0, TOKT_INVALID } }; const kwdef *kwp; /* remember my resource loader */ res_loader_ = res_loader; /* there's no stream yet */ str_ = 0; /* no external source yet */ ext_src_ = 0; /* start numbering the file descriptors at zero */ next_filedesc_id_ = 0; /* there are no file descriptors yet */ desc_head_ = 0; desc_tail_ = 0; desc_list_ = 0; desc_list_cnt_ = desc_list_alo_ = 0; /* empty out the input line buffer */ clear_linebuf(); /* start out with a minimal line buffer size */ linebuf_.ensure_space(4096); expbuf_.ensure_space(4096); /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* remember the default character set */ default_charset_ = lib_copy_str(default_charset); /* we don't have a default character mapper yet */ default_mapper_ = 0; /* create an input mapper for the default character set, if specified */ if (default_charset != 0) default_mapper_ = CCharmapToUni::load(res_loader, default_charset); /* * if the default character set wasn't specified, or we failed to * load a mapper for the specified character set, use a plain ASCII * mapper */ if (default_mapper_ == 0) default_mapper_ = new CCharmapToUniASCII(); /* presume we're not in preprocessor-only mode */ pp_only_mode_ = FALSE; /* presume we're not in list-includes mode */ list_includes_mode_ = FALSE; /* presume we're not in test report mode */ test_report_mode_ = FALSE; /* allow preprocessing directives */ allow_pp_ = TRUE; /* there are no previously-included files yet */ prev_includes_ = 0; /* by default, use the "collapse" mode for newlines in strings */ string_newline_spacing_ = NEWLINE_SPACING_COLLAPSE; /* start out with ALL_ONCE mode off */ all_once_ = FALSE; /* by default, ignore redundant includes without warning */ warn_on_ignore_incl_ = FALSE; /* there are no include path entries yet */ incpath_head_ = incpath_tail_ = 0; /* not in a quoted string yet */ in_quote_ = '\0'; in_triple_ = FALSE; /* not in a #if block yet */ if_sp_ = 0; if_false_level_ = 0; /* not processing a preprocessor constant expression */ in_pp_expr_ = FALSE; /* we don't have a current or appended line yet */ last_desc_ = 0; last_linenum_ = 0; appended_desc_ = 0; appended_linenum_ = 0; /* allocate the first token-list block */ init_src_block_list(); /* create the #define and #undef symbol tables */ defines_ = new CTcBasicMacroTable(512, new CVmHashFuncCS(), TRUE); undefs_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* create the special __LINE__ and __FILE__ macros */ defines_->add(new CTcHashEntryPpLINE(this)); defines_->add(new CTcHashEntryPpFILE(this)); /* get the current time and date */ timer = os_time(0); tblk = os_localtime(&timer); tstr = asctime(tblk); /* * add the __DATE__ macro - the format is "Mmm dd yyyy", where "Mmm" * is the three-letter month name generated by asctime(), "dd" is * the day of the month, with a leading space for numbers less than * ten, and "yyyy" is the year. */ sprintf(timebuf, "'%.3s %2d %4d'", tstr + 4, tblk->tm_mday, tblk->tm_year + 1900); add_define("__DATE__", timebuf); /* add the __TIME__ macro - 24-hour "hh:mm:ss" format */ sprintf(timebuf, "'%.8s'", tstr + 11); add_define("__TIME__", timebuf); /* * Allocate a pool of macro resources. The number we start with is * arbitrary, since we'll add more as needed, but we want to try to * allocate enough up front that we avoid time-consuming memory * allocations later. On the other hand, we don't want to * pre-allocate a huge number of objects that we'll never use. */ for (macro_res_avail_ = 0, macro_res_head_ = 0, i = 0 ; i < 7 ; ++i) { CTcMacroRsc *rsc; /* allocate a new object */ rsc = new CTcMacroRsc(); /* add it onto the master list */ rsc->next_ = macro_res_head_; macro_res_head_ = rsc; /* add it onto the available list */ rsc->next_avail_ = macro_res_avail_; macro_res_avail_ = rsc; } /* create the keyword hash table */ kw_ = new CVmHashTable(64, new CVmHashFuncCS(), TRUE); /* populate the keyword table */ for (kwp = kwlist ; kwp->kw_text != 0 ; ++kwp) kw_->add(new CTcHashEntryKw(kwp->kw_text, kwp->kw_tok_id)); /* no ungot tokens yet */ unget_head_ = unget_cur_ = 0; /* no string capture file */ string_fp_ = 0; string_fp_map_ = 0; /* there's no current token yet */ curtok_.settyp(TOKT_NULLTOK); curtok_.set_text("", 16); } /* * Initialize the source save block list */ void CTcTokenizer::init_src_block_list() { /* allocate the first source block */ src_cur_ = src_head_ = new CTcTokSrcBlock(); /* set up to write into the first block */ src_ptr_ = src_head_->get_buf(); src_rem_ = TCTOK_SRC_BLOCK_SIZE; } /* ------------------------------------------------------------------------ */ /* * Delete the tokenizer */ CTcTokenizer::~CTcTokenizer() { /* delete all streams */ delete_source(); /* delete the string capture file */ if (string_fp_ != 0) delete string_fp_; /* delete all file descriptors */ while (desc_head_ != 0) { /* remember the next descriptor */ CTcTokFileDesc *nxt = desc_head_->get_next(); /* delete this one */ delete desc_head_; /* move on to the next one */ desc_head_ = nxt; } /* delete the unget list */ unget_cur_ = 0; while (unget_head_ != 0) { /* remember the next element */ CTcTokenEle *nxt = unget_head_->getnxt(); /* delete this element */ delete unget_head_; /* advance to the next element */ unget_head_ = nxt; } /* delete the file descriptor index array */ if (desc_list_ != 0) t3free(desc_list_); /* delete our default character set string copy */ lib_free_str(default_charset_); /* release our reference on our default character mapper */ default_mapper_->release_ref(); /* forget about all of our previous include files */ while (prev_includes_ != 0) { tctok_incfile_t *nxt; /* remember the next file */ nxt = prev_includes_->nxt; /* delete this one */ t3free(prev_includes_); /* move on to the next one */ prev_includes_ = nxt; } /* delete the include path list */ while (incpath_head_ != 0) { tctok_incpath_t *nxt; /* remember the next entry in the path */ nxt = incpath_head_->nxt; /* delete this entry */ t3free(incpath_head_); /* move on to the next one */ incpath_head_ = nxt; } /* delete the macro resources */ while (macro_res_head_ != 0) { CTcMacroRsc *nxt; /* remember the next one */ nxt = macro_res_head_->next_; /* delete this one */ delete macro_res_head_; /* move on to the next one */ macro_res_head_ = nxt; } /* delete the token list */ delete src_head_; /* delete the #define and #undef symbol tables */ delete defines_; delete undefs_; /* delete the keyword hash table */ delete kw_; /* if we created a mapping for the string capture file, release it */ if (string_fp_map_ != 0) string_fp_map_->release_ref(); } /* ------------------------------------------------------------------------ */ /* * Clear the line buffer */ void CTcTokenizer::clear_linebuf() { /* clear the buffer */ linebuf_.clear_text(); /* reset our read point to the start of the line buffer */ p_.set(linebuf_.get_buf()); } /* ------------------------------------------------------------------------ */ /* * Get a textual representation of an operator token */ const char *CTcTokenizer::get_op_text(tc_toktyp_t op) { struct tokname_t { tc_toktyp_t typ; const char *nm; }; static const tokname_t toknames[] = { { TOKT_EOF, "" }, { TOKT_SYM, "" }, { TOKT_INT, "" }, { TOKT_SSTR, "" }, { TOKT_DSTR, "" }, { TOKT_DSTR_START, "" }, { TOKT_DSTR_MID, "" }, { TOKT_DSTR_END, "" }, { TOKT_RESTR, "" }, { TOKT_LPAR, "(" }, { TOKT_RPAR, ")" }, { TOKT_COMMA, "," }, { TOKT_DOT, "." }, { TOKT_LBRACE, "{" }, { TOKT_RBRACE, "}", }, { TOKT_LBRACK, "[", }, { TOKT_RBRACK, "]", }, { TOKT_EQ, "=", }, { TOKT_EQEQ, "==", }, { TOKT_ASI, ":=" }, { TOKT_PLUS, "+" }, { TOKT_MINUS, "-" }, { TOKT_TIMES, "*" }, { TOKT_DIV, "/", }, { TOKT_MOD, "%" }, { TOKT_GT, ">" }, { TOKT_LT, "<" }, { TOKT_GE, ">=" }, { TOKT_LE, "<=" }, { TOKT_NE, "!=" }, { TOKT_ARROW, "->" }, { TOKT_COLON, ":" }, { TOKT_SEM, ";" }, { TOKT_AND, "&" }, { TOKT_ANDAND, "&&" }, { TOKT_OR, "|" }, { TOKT_OROR, "||" }, { TOKT_XOR, "^" }, { TOKT_SHL, "<<" }, { TOKT_ASHR, ">>" }, { TOKT_LSHR, ">>>" }, { TOKT_INC, "++" }, { TOKT_DEC, "--" }, { TOKT_PLUSEQ, "+=" }, { TOKT_MINEQ, "-=" }, { TOKT_TIMESEQ, "*=" }, { TOKT_DIVEQ, "/=" }, { TOKT_MODEQ, "%=" }, { TOKT_ANDEQ, "&=" }, { TOKT_OREQ, "|=" }, { TOKT_XOREQ, "^=" }, { TOKT_SHLEQ, "<<=" }, { TOKT_ASHREQ, ">>=" }, { TOKT_LSHREQ, ">>>=" }, { TOKT_NOT, "! (not)" }, { TOKT_BNOT, "~" }, { TOKT_POUND, "#" }, { TOKT_POUNDPOUND, "##" }, { TOKT_POUNDAT, "#@" }, { TOKT_ELLIPSIS, "..." }, { TOKT_QUESTION, "?" }, { TOKT_QQ, "??" }, { TOKT_COLONCOLON, "::" }, { TOKT_FLOAT, "" }, { TOKT_BIGINT, "" }, { TOKT_AT, "@" }, { TOKT_DOTDOT, ".." }, { TOKT_SELF, "self" }, { TOKT_TARGETPROP, "targetprop" }, { TOKT_TARGETOBJ, "targetobj" }, { TOKT_DEFININGOBJ, "definingobj" }, { TOKT_INHERITED, "inherited" }, { TOKT_DELEGATED, "delegated" }, { TOKT_IF, "if" }, { TOKT_ELSE, "else" }, { TOKT_FOR, "for" }, { TOKT_WHILE, "while" }, { TOKT_DO, "do" }, { TOKT_SWITCH, "switch" }, { TOKT_CASE, "case" }, { TOKT_DEFAULT, "default" }, { TOKT_GOTO, "goto" }, { TOKT_BREAK, "break" }, { TOKT_CONTINUE, "continue" }, { TOKT_FUNCTION, "function" }, { TOKT_RETURN, "return" }, { TOKT_LOCAL, "local" }, { TOKT_OBJECT, "object" }, { TOKT_NIL, "nil" }, { TOKT_TRUE, "true" }, { TOKT_PASS, "pass" }, { TOKT_EXTERNAL, "external" }, { TOKT_EXTERN, "extern" }, { TOKT_FORMATSTRING, "formatstring" }, { TOKT_CLASS, "class" }, { TOKT_REPLACE, "replace" }, { TOKT_MODIFY, "modify" }, { TOKT_NEW, "new" }, // { TOKT_DELETE, "delete" }, { TOKT_THROW, "throw" }, { TOKT_TRY, "try" }, { TOKT_CATCH, "catch" }, { TOKT_FINALLY, "finally" }, { TOKT_INTRINSIC, "intrinsic" }, { TOKT_DICTIONARY, "dictionary" }, { TOKT_GRAMMAR, "grammar" }, { TOKT_ENUM, "enum" }, { TOKT_TEMPLATE, "template" }, { TOKT_STATIC, "static" }, { TOKT_FOREACH, "foreach" }, { TOKT_EXPORT, "export" }, { TOKT_PROPERTYSET, "propertyset" }, { TOKT_TRANSIENT, "transient" }, { TOKT_REPLACED, "replaced" }, { TOKT_PROPERTY, "property" }, { TOKT_OPERATOR, "operator" }, { TOKT_METHOD, "method" }, { TOKT_INVOKEE, "invokee" }, // { TOKT_VOID, "void" }, // { TOKT_INTKW, "int" }, // { TOKT_STRING, "string" }, // { TOKT_LIST, "list" }, // { TOKT_BOOLEAN, "boolean" }, // { TOKT_ANY, "any"}, { TOKT_INVALID, 0 } }; const tokname_t *p; /* search for the token */ for (p = toknames ; p->nm != 0 ; ++p) { /* if this is our token, return the associated name string */ if (p->typ == op) return p->nm; } /* we didn't find it */ return ""; } /* ------------------------------------------------------------------------ */ /* * Reset the tokenizer. Delete the current source object and all of the * saved source text. This can be used after compilation of a unit * (such as a debugger expression) is completed and the intermediate * parser state is no longer needed. */ void CTcTokenizer::reset() { /* delete the source object */ delete_source(); /* delete saved token text */ if (src_head_ != 0) { /* delete the list */ delete src_head_; /* re-initialize the source block list */ init_src_block_list(); } } /* ------------------------------------------------------------------------ */ /* * Delete the source file, if any, including any parent include files. */ void CTcTokenizer::delete_source() { /* delete the current stream and all enclosing parents */ while (str_ != 0) { CTcTokStream *nxt; /* remember the next stream in the list */ nxt = str_->get_parent(); /* delete this stream */ delete str_; /* move up to the next one */ str_ = nxt; } /* there are no more streams */ str_ = 0; } /* ------------------------------------------------------------------------ */ /* * Set up to read a source file. Returns zero on success, or a non-zero * error code on failure. */ int CTcTokenizer::set_source(const char *src_filename, const char *orig_name) { CTcTokFileDesc *desc; CTcSrcFile *src; int charset_error; int default_charset_error; /* empty out the input line buffer */ clear_linebuf(); /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* create a reader for the source file */ src = CTcSrcFile::open_source(src_filename, res_loader_, default_charset_, &charset_error, &default_charset_error); if (src == 0) { /* if we had a problem loading the default character set, log it */ if (default_charset_error) log_error(TCERR_CANT_LOAD_DEFAULT_CHARSET, default_charset_); /* return failure */ return TCERR_CANT_OPEN_SRC; } /* find or create a file descriptor for this filename */ desc = get_file_desc(src_filename, strlen(src_filename), FALSE, orig_name, strlen(orig_name)); /* * Create a stream to read the source file. The new stream has no * parent, because this is the top-level source file, and was not * included from any other file. */ str_ = new CTcTokStream(desc, src, 0, charset_error, if_sp_); /* success */ return 0; } /* * Set up to read source code from a memory buffer */ void CTcTokenizer::set_source_buf(const char *buf, size_t len) { CTcSrcMemory *src; /* empty out the input line buffer */ clear_linebuf(); /* reset the scanning state to the start of a brand new stream */ in_pp_expr_ = FALSE; last_linenum_ = 0; unsplicebuf_.clear_text(); in_quote_ = '\0'; in_triple_ = FALSE; comment_in_embedding_.reset(); macro_in_embedding_.reset(); main_in_embedding_.reset(); if_sp_ = 0; if_false_level_ = 0; unget_cur_ = 0; /* set up at the beginning of the input line buffer */ start_new_line(&linebuf_, 0); /* create a reader for the memory buffer */ src = new CTcSrcMemory(buf, len, default_mapper_); /* * Create a stream to read the source file. The new stream has no * parent, because this is the top-level source file, and was not * included from any other file. */ str_ = new CTcTokStream(0, src, 0, 0, if_sp_); } /* ------------------------------------------------------------------------ */ /* * Stuff text into the source stream. */ void CTcTokenizer::stuff_text(const char *txt, size_t len, int expand) { CTcTokString expbuf; int p_ofs; /* if desired, expand macros */ if (expand) { /* expand macros in the text, storing the result in 'expbuf' */ expand_macros(&expbuf, txt, len); /* use the expanded version as the stuffed text now */ txt = expbuf.get_text(); len = expbuf.get_text_len(); } /* get the current p_ offset */ p_ofs = p_.getptr() - curbuf_->get_text(); /* insert the text into the buffer */ curbuf_->insert(p_ofs, txt, len); /* reset p_ in case the curbuf_ buffer was reallocated for expansion */ start_new_line(curbuf_, p_ofs); } /* ------------------------------------------------------------------------ */ /* * Find or create a file descriptor for a given filename */ CTcTokFileDesc *CTcTokenizer::get_file_desc(const char *fname, size_t fname_len, int always_create, const char *orig_fname, size_t orig_fname_len) { CTcTokFileDesc *orig_desc; CTcTokFileDesc *desc; /* presume we won't find an original descriptor in the list */ orig_desc = 0; /* * Search the list of existing descriptors to find one that matches. * Do this regardless of whether we're allowed to re-use an existing * one or not - even if we're creating a new one unconditionaly, we * need to know if there's an earlier copy that already exists so we * can associate the new one with the original. */ for (desc = desc_head_ ; desc != 0 ; desc = desc->get_next()) { /* check for a name match */ if (strlen(desc->get_fname()) == fname_len && memcmp(desc->get_fname(), fname, fname_len) == 0) { /* * if we're allowed to return an existing descriptor, return * this one, since it's for the same filename */ if (!always_create) return desc; /* * we have to create a new descriptor even though we have an * existing one - remember the original so we can point the * new one back to the original */ orig_desc = desc; /* * no need to look any further - we've found the first * instance of this filename in our list */ break; } } /* we didn't find a match - create a new descriptor */ desc = new CTcTokFileDesc(fname, fname_len, next_filedesc_id_++, orig_desc, orig_fname, orig_fname_len); /* link it in at the end of the master list */ desc->set_next(0); if (desc_tail_ == 0) desc_head_ = desc; else desc_tail_->set_next(desc); desc_tail_ = desc; /* expand our array index if necessary */ if (desc_list_cnt_ >= desc_list_alo_) { size_t siz; /* allocate or expand the array */ desc_list_alo_ += 10; siz = desc_list_alo_ * sizeof(desc_list_[0]); if (desc_list_ == 0) desc_list_ = (CTcTokFileDesc **)t3malloc(siz); else desc_list_ = (CTcTokFileDesc **)t3realloc(desc_list_, siz); } /* add the new array entry */ desc_list_[desc_list_cnt_++] = desc; /* return it */ return desc; } /* ------------------------------------------------------------------------ */ /* * Add an include path entry. Each new entry goes at the end of the * list, after all previous entries. */ void CTcTokenizer::add_inc_path(const char *path) { tctok_incpath_t *entry; /* create a new path list entry */ entry = (tctok_incpath_t *)t3malloc(sizeof(tctok_incpath_t) + strlen(path)); /* store the path in the entry */ strcpy(entry->path, path); /* link this entry at the end of our list */ if (incpath_tail_ != 0) incpath_tail_->nxt = entry; else incpath_head_ = entry; incpath_tail_ = entry; entry->nxt = 0; } /* ------------------------------------------------------------------------ */ /* * Set the string capture file. */ void CTcTokenizer::set_string_capture(osfildef *fp) { /* delete any old capture file */ if (string_fp_ != 0) delete string_fp_; /* * Remember the new capture file. Use a duplicate handle, since we * pass ownership of the handle to the CVmFileSource object (i.e., * it'll close the handle when done). */ string_fp_ = new CVmFileSource(osfdup(fp, "w")); /* * if we don't already have a character mapping to translate from * our internal unicode characters back into the source file * character set, create one now */ if (string_fp_map_ == 0) { /* try creating a mapping for the default character set */ if (default_charset_ != 0) string_fp_map_ = CCharmapToLocal::load(res_loader_, default_charset_); /* if we couldn't create the mapping, use a default ASCII mapping */ if (string_fp_map_ == 0) string_fp_map_ = CCharmapToLocal::load(res_loader_, "us-ascii"); } } /* ------------------------------------------------------------------------ */ /* * Get the next token in the input stream, reading additional lines from * the source file as needed. */ tc_toktyp_t CTcTokenizer::next() { /* the current token is about to become the previous token */ prvtok_ = curtok_; /* if there's an un-got token, return it */ if (unget_cur_ != 0) { /* get the current unget token */ curtok_ = *unget_cur_; /* we've now consumed this ungotten token */ unget_cur_ = unget_cur_->getprv(); /* return the new token's type */ return curtok_.gettyp(); } /* if there's an external source, get its next token */ if (ext_src_ != 0) { const CTcToken *ext_tok; /* get the next token from the external source */ ext_tok = ext_src_->get_next_token(); /* check to see if we got a token */ if (ext_tok == 0) { /* * restore the current token in effect before this source was * active */ curtok_ = *ext_src_->get_enclosing_curtok(); /* * this source has no more tokens - restore the enclosing * source, and keep going so we try getting a token from it */ ext_src_ = ext_src_->get_enclosing_source(); /* return the token type */ return curtok_.gettyp(); } else { /* we got a token - copy it to our internal token buffer */ curtok_ = *ext_tok; /* return its type */ return curtok_.gettyp(); } } /* keep going until we get a valid token */ for (;;) { /* * read the next token from the current line, applying * appropriate string translations and storing strings and * symbols in the source block list */ tc_toktyp_t typ = next_on_line_xlat_keep(); /* if it's the "null" token, skip it and read another token */ if (typ == TOKT_NULLTOK) continue; /* if we found a valid token, we're done - return the token */ if (typ != TOKT_EOF) return typ; /* * if we're at the end of a preprocess line, don't read another * line - just return end of file */ if (p_.getch() == TOK_END_PP_LINE) return TOKT_EOF; /* * we've reached the end of the line - read another line, * applying preprocessing directives and expanding macros as * needed */ if (read_line_pp()) { /* no more lines are available - return end of file */ return TOKT_EOF; } } } /* ------------------------------------------------------------------------ */ /* * clear external token sources, returning to the true input stream */ void CTcTokenizer::clear_external_sources() { /* * restore the current token as it was before the outermost external * source was first established */ if (ext_src_ != 0) { CTcTokenSource *outer; /* find the outermost source */ for (outer = ext_src_ ; outer->get_enclosing_source() != 0 ; outer = ext_src_->get_enclosing_source()) ; /* restore its original next token */ curtok_ = *ext_src_->get_enclosing_curtok(); } /* there's no external source now */ ext_src_ = 0; } /* ------------------------------------------------------------------------ */ /* * Make a safely storable copy of the current token. */ const CTcToken *CTcTokenizer::copycur() { /* if it's already a type that we store safely, return the current token */ if (is_tok_safe(curtok_.gettyp())) return getcur(); /* save the current token's text in permanent tokenizer memory */ curtok_.set_text(store_source(curtok_.get_text(), curtok_.get_text_len()), curtok_.get_text_len()); /* return the current token, now that we've made it safe */ return &curtok_; } /* * Make a safely storable copy of a given token. */ void CTcTokenizer::copytok(CTcToken *dst, const CTcToken *src) { /* start with an exact copy of the token */ *dst = *src; /* if the token is a symbol, it already has a safe copy */ if (is_tok_safe(src->gettyp())) return; /* save the token's text in permanent tokenizer memory */ dst->set_text(store_source(dst->get_text(), dst->get_text_len()), dst->get_text_len()); } /* * Is the given token type "safe"? A safe token is one whose text is * always saved in the tokenizer source list, which means that the text * pointer is safe as long as the tokenizer object exists. A non-safe * token is one whose text pointer points directly into the current line * buffer, meaning that its text buffer is only guaranteed to last until * the next token fetch. */ int CTcTokenizer::is_tok_safe(tc_toktyp_t typ) { switch (typ) { case TOKT_SYM: case TOKT_SSTR: case TOKT_SSTR_START: case TOKT_SSTR_MID: case TOKT_SSTR_END: case TOKT_DSTR: case TOKT_DSTR_START: case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_RESTR: case TOKT_FLOAT: case TOKT_BIGINT: /* these types are always stored in the source list */ return TRUE; default: /* other types are not always safely stored */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Check to see if the current token matches the given text */ int CTcTokenizer::cur_tok_matches(const char *txt, size_t len) { /* if the length matches, and the text matches exactly, it matches */ return (getcur()->get_text_len() == len && memcmp(getcur()->get_text(), txt, len) == 0); } /* ------------------------------------------------------------------------ */ /* * Look ahead to see if we match a pair of symbol tokens */ int CTcTokenizer::look_ahead(const char *s1, const char *s2) { /* if the first token doesn't match, the sequence doesn't match */ if (cur() != TOKT_SYM || !cur_tok_matches(s1, strlen(s1))) return FALSE; /* check the next token - if it matches, we have a sequence match */ if (next() == TOKT_SYM && cur_tok_matches(s2, strlen(s2))) { /* got it - skip the second token and return success */ next(); return TRUE; } /* * no match - but we've already read the next token, so put it back * before we return failure */ unget(); return FALSE; } /* * Peek ahead - same as look_ahead, but doesn't skip anything on a * successful match. */ int CTcTokenizer::peek_ahead(const char *s1, const char *s2) { /* if the first token doesn't match, the sequence doesn't match */ if (cur() != TOKT_SYM || !cur_tok_matches(s1, strlen(s1))) return FALSE; /* check the next token - if it matches, we have a sequence match */ int match = (next() == TOKT_SYM && cur_tok_matches(s2, strlen(s2))); /* whatever happened, un-get the second token, since we're just peeking */ unget(); /* return the match indication */ return match; } /* ------------------------------------------------------------------------ */ /* * Un-get the current token */ void CTcTokenizer::unget() { /* unget, backing up to the internally saved previous token */ unget(&prvtok_); } /* * Un-get the current token and back up to the specified previous token. */ void CTcTokenizer::unget(const CTcToken *prv) { /* push the current token onto the unget stack */ push(&curtok_); /* go back to the previous token */ curtok_ = *prv; /* the internally saved previous token is no longer valid */ prvtok_.settyp(TOKT_INVALID); } /* * Push a token into the stream */ void CTcTokenizer::push(const CTcToken *tok) { /* if the unget list is empty, create the initial entry */ if (unget_head_ == 0) unget_head_ = new CTcTokenEle(); /* advance to the next slot in the list */ if (unget_cur_ == 0) { /* no last ungot token - set up at the head of the list */ unget_cur_ = unget_head_; } else if (unget_cur_->getnxt() != 0) { /* there's another slot available - advance to it */ unget_cur_ = unget_cur_->getnxt(); } else { /* the list is full - we need to allocate a new slot */ CTcTokenEle *newele = new CTcTokenEle(); /* link it at the end of the list */ unget_cur_->setnxt(newele); newele->setprv(unget_cur_); /* advance to it */ unget_cur_ = newele; } /* save the pushed token */ unget_cur_->set(*tok); } /* ------------------------------------------------------------------------ */ /* * Assume that we should have just found a '>>' terminating an embedded * expression in a string. If possible, back out the previous token and * re-scan it as though it had started with '>>'. * * This is to be called by a higher-level parser when it determines that, * syntactically, we should have found the '>>' leaving an embedded * expression. */ void CTcTokenizer::assume_missing_str_cont() { xlat_string_to_src(&main_in_embedding_, TRUE); } /* ------------------------------------------------------------------------ */ /* * Skip whitespace and macro expansion markers */ void CTcTokenizer::skip_ws_and_markers(utf8_ptr *p) { /* keep going until we find something interesting */ for (;;) { wchar_t cur; /* get the current character */ cur = p->getch(); /* * if it's a macro expansion end marker, skip it as though it * were whitespace; otherwise, if it's whitespace, skip it; * otherwise, we're done skipping leading whitespace */ if (cur == TOK_MACRO_EXP_END) { /* skip the embedded pointer value that follows */ p->set(p->getptr() + 1 + sizeof(CTcHashEntryPp *)); } else if (is_space(cur)) { /* skip the space */ p->inc(); } else { /* it's not whitespace or equivalent - we're done */ return; } } } /* ------------------------------------------------------------------------ */ /* * Get the next token from the input stream, operating on the current * line only. */ tc_toktyp_t CTcTokenizer::next_on_line(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { tc_toktyp_t typ; /* skip whitespace */ skip_ws_and_markers(p); /* remember where the token starts */ utf8_ptr start = *p; /* get the initial character */ wchar_t cur = p->getch(); /* if there's nothing left in the current line, return EOF */ if (cur == '\0') { /* indicate end of file */ typ = TOKT_EOF; goto done; } /* skip the initial character */ p->inc(); /* presume the token will not be marked as fully macro-expanded */ tok->set_fully_expanded(FALSE); /* see what we have */ switch(cur) { case TOK_MACRO_FORMAL_FLAG: /* * this is a two-byte formal parameter sequence in a macro * expansion - skip the second byte of the two-byte sequence, * and return the special token type for this sequence */ typ = TOKT_MACRO_FORMAL; /* * skip the second byte - note that we want to skip exactly one * byte, regardless of what the byte looks like as a utf-8 * partial character, since it's not a utf-8 character at all */ p->set(p->getptr() + 1); break; case TOK_MACRO_FOREACH_FLAG: /* * this is the special macro '#foreach' flag - return it as a * special pseudo-token */ typ = TOKT_MACRO_FOREACH; break; case TOK_MACRO_IFEMPTY_FLAG: /* #ifempty macro flag */ typ = TOKT_MACRO_IFEMPTY; break; case TOK_MACRO_IFNEMPTY_FLAG: /* #ifnempty macro flag */ typ = TOKT_MACRO_IFNEMPTY; break; case TOK_MACRO_ARGCOUNT_FLAG: /* it's the special macro '#argcount' flag */ typ = TOKT_MACRO_ARGCOUNT; break; case TOK_FULLY_EXPANDED_FLAG: /* set the token flag indicating that it has been fully expanded */ tok->set_fully_expanded(TRUE); /* the token symbol starts at the byte after the flag byte */ start = p->getptr(); /* read the first character of the symbol */ cur = p->getch(); p->inc(); /* tokenize the symbol that follows */ goto tokenize_symbol; case TOK_END_PP_LINE: /* * Preprocess line-ending marker - when we reach the end of a * preprocessor line, we can't read another source line, because * a preprocessor directive consists of only a single logical * source line. Once we see this, return end-of-file until the * caller explicitly reads a new source line. * * Keep the read pointer stuck on this flag byte, so that we * return end-of-file on a subsequent attempt to get the next * token. */ *p = start; typ = TOKT_EOF; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* * Start out with the leading digit in the accumulator. Note * that the character set internally is always UTF-8. */ ulong acc = value_of_digit(cur); /* * The overflow limit differs for positive and negative values, * but we can't be sure at this point which we have, since even * if there's a '-' before the number, it could be a * subtraction operator rather than a negation operator. We * can't know until we've folded constants whether the number * is positive or negative. Use the lower limit for now; the * compiler will un-promote values after constant folding if * they end up fitting after all, which will handle the case of * INT32MINVAL, whose absolute value is higher than this limit. */ const ulong ovlimit = 2147483647U; /* * If it's a leading zero, treat as octal or hex. '0x' means * hex; otherwise, '0' means octal. */ if (cur == '0') { /* check for hex - if it's not hex, it's octal */ if (p->getch() == 'x' || p->getch() == 'X') { /* skip the 'x' */ p->inc(); /* * scan the hex number - keep going until we find * something that's not a hex digit */ for (;;) { /* get this character */ cur = p->getch(); /* if it's not a hex digit, stop scanning */ if (!is_xdigit(cur)) break; /* check for 32-bit integer overflow */ if ((acc & 0xF0000000) != 0) goto do_int_overflow; /* * Shift the accumulator and add this digit's value. * Note that we can save a test - if the character is * >= lower-case 'a', we know it's not an upper-case * letter because the lower-case letters all have * values above the upper-case letters in UTF-8 * encoding (which we always use as the internal * character set). Since we already know it's a * valid hex digit (we wouldn't be here if it * weren't), we can just check to see if it's at * least lower-case 'a', and we automatically know * then whether it's in the 'a'-'f' range or the * 'A'-'F' range. */ acc *= 16; acc += value_of_xdigit(cur); /* move on */ p->inc(); } } else { /* scan octal digits */ for ( ; is_odigit(p->getch()) ; p->inc()) { /* check for overflow */ if ((acc & 0xE0000000) != 0) goto do_int_overflow; /* add the digit */ acc = 8*acc + value_of_odigit(p->getch()); } /* * If we stopped on a digit outside of the octal range, * consume any remaining digits, and flag it as an * error. Leaving subsequent decimal digits as a * separate token tends to be confusing, since in most * cases the inclusion of decimal digits means that the * user didn't really intend this to be an octal number * after all. For instance, the leading zero might be * there for formatting reasons, and the user simply * forgot to take into account that it triggers octal * interpretation. */ if (is_digit(p->getch())) { /* skip subsequent digits */ for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; /* flag the error */ if (!expanding) log_error(TCERR_DECIMAL_IN_OCTAL, p->getptr() - start.getptr(), start.getptr()); } } } else { /* scan decimal digits */ for ( ; is_digit(p->getch()) ; p->inc()) { /* check for integer overflow in the shift */ if (acc > 214748364) goto do_int_overflow; /* shift the accumulator */ acc *= 10; /* get the digit */ int d = value_of_digit(p->getch()); /* check for overflow adding the digit */ if ((unsigned)acc > ovlimit - d) goto do_int_overflow; /* add the digit */ acc += d; } } /* * If we stopped at a decimal point or an exponent, it's a * floating point number. This doesn't count if we have two * periods in a row - ".." - because that's either a range * marker operator or an ellipsis operator. */ if (p->getch() == '.' && p->getch_at(1) != '.') { typ = TOKT_FLOAT; goto do_float; } else if (p->getch() == 'e' || p->getch() == 'E') { typ = TOKT_FLOAT; goto do_float; } /* it's an integer value */ typ = TOKT_INT; /* set the integer value */ tok->set_int_val(acc); } break; do_int_overflow: /* mark it as an integer that overflowed into a float type */ typ = TOKT_BIGINT; do_float: { /* haven't found a decimal point yet */ int found_decpt = FALSE; int is_hex = FALSE; /* start over at the beginning of the number */ *p = start; /* skip any leading sign */ if (p->getch() == '-') p->inc(); /* skip any leading "0x" */ if (typ == TOKT_BIGINT && p->getch() == '0') { wchar_t c1 = p->getch_at(1); if (c1 == 'x' || c1 == 'X') { is_hex = TRUE; p->inc_by(2); } } /* parse the rest of the float */ for ( ; ; p->inc()) { /* get this character and move on */ cur = p->getch(); /* see what we have */ if (is_digit(cur)) { /* we have another digit; just keep going */ } else if (is_hex && is_xdigit(cur)) { /* we have another hex digit, so keep going */ } else if (!found_decpt && !is_hex && cur == '.') { /* it's the decimal point - note it and keep going */ found_decpt = TRUE; /* if it started as a promoted int, it's really a float */ if (typ == TOKT_BIGINT) typ = TOKT_FLOAT; } else if (cur == 'e' || cur == 'E') { /* it might not be an exponent - look ahead to find out */ utf8_ptr p2 = *p; p2.inc(); /* if we have a sign, skip it */ if ((cur = p2.getch()) == '-' || cur == '+') p2.inc(); /* we need at least one digit to make an exponent */ if (!is_digit(p2.getch())) break; /* skip digits */ while (is_digit(p2.getch())) p2.inc(); /* advance to the end of the exponent */ *p = p2; /* if it started as a promoted int, it's now a float */ if (typ == TOKT_BIGINT) typ = TOKT_FLOAT; /* the end of the exponent is the end of the number */ break; } else { /* everything else ends the number */ break; } } } break; case '"': case '\'': *p = start; return tokenize_string(p, tok, ec, expanding); case '(': typ = TOKT_LPAR; if (ec != 0) ec->parens(1); break; case ')': typ = TOKT_RPAR; if (ec != 0) ec->parens(-1); break; case ',': typ = TOKT_COMMA; break; case '.': /* check for '..', '...', and floating-point numbers */ if (p->getch() == '.') { /* it's either '..' or '...' */ p->inc(); if (p->getch() == '.') { p->inc(); typ = TOKT_ELLIPSIS; } else typ = TOKT_DOTDOT; } else if (is_digit(p->getch())) { typ = TOKT_FLOAT; goto do_float; } else typ = TOKT_DOT; break; case '{': typ = TOKT_LBRACE; if (ec != 0) ec->parens(1); break; case '}': typ = TOKT_RBRACE; if (ec != 0) ec->parens(-1); break; case '[': typ = TOKT_LBRACK; if (ec != 0) ec->parens(1); break; case ']': typ = TOKT_RBRACK; if (ec != 0) ec->parens(-1); break; case '=': /* check for '==' */ if (p->getch() == '=') { p->inc(); typ = TOKT_EQEQ; } else typ = TOKT_EQ; break; case ':': /* check for '::' */ if (p->getch() == ':') { p->inc(); typ = TOKT_COLONCOLON; } else typ = TOKT_COLON; break; case '?': /* check for '??' */ if (p->getch() == '?') { p->inc(); typ = TOKT_QQ; } else typ = TOKT_QUESTION; break; case '+': /* check for '++' and '+=' */ if (p->getch() == '+') { p->inc(); typ = TOKT_INC; } else if (p->getch() == '=') { p->inc(); typ = TOKT_PLUSEQ; } else typ = TOKT_PLUS; break; case '-': /* check for '--', '->' and '-=' */ if (p->getch() == '-') { p->inc(); typ = TOKT_DEC; } else if (p->getch() == '=') { p->inc(); typ = TOKT_MINEQ; } else if (p->getch() == '>') { p->inc(); typ = TOKT_ARROW; } else typ = TOKT_MINUS; break; case '*': /* check for '*=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_TIMESEQ; } else typ = TOKT_TIMES; break; case '/': /* check for '/=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_DIVEQ; } else typ = TOKT_DIV; break; case '%': /* check for '%=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_MODEQ; } else typ = TOKT_MOD; break; case '>': /* check for '>>=', '>>' and '>=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_GE; } else if (p->getch() == '>') { /* check for the end of an embedded expression */ if (ec != 0 && ec->in_expr() && ec->parens() == 0) { *p = start; return tokenize_string(p, tok, ec, expanding); } /* check for '>>>' and '>>=' */ p->inc(); if (p->getch() == '>') { /* skip the '>' and check for '>>>=' */ p->inc(); if (p->getch() == '=') { p->inc(); typ = TOKT_LSHREQ; } else typ = TOKT_LSHR; } else if (p->getch() == '=') { p->inc(); typ = TOKT_ASHREQ; } else typ = TOKT_ASHR; } else typ = TOKT_GT; break; case '<': /* check for '<<=', '<<', '<>', and '<=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_LE; } else if (p->getch() == '<') { /* check for '<<=' */ p->inc(); if (p->getch() == '=') { p->inc(); typ = TOKT_SHLEQ; } else typ = TOKT_SHL; } #if 0 else if (p->getch() == '>') { /* '<>' is obsolete */ if (!expanding) log_error(TCERR_LTGT_OBSOLETE); /* ... but for now proceed as though it's != */ p->inc(); typ = TOKT_NE; } #endif else typ = TOKT_LT; break; case ';': typ = TOKT_SEM; break; case '&': /* check for '&&' and '&=' */ if (p->getch() == '&') { p->inc(); typ = TOKT_ANDAND; } else if (p->getch() == '=') { p->inc(); typ = TOKT_ANDEQ; } else typ = TOKT_AND; break; case '|': /* check for '||' and '|=' */ if (p->getch() == '|') { p->inc(); typ = TOKT_OROR; } else if (p->getch() == '=') { p->inc(); typ = TOKT_OREQ; } else typ = TOKT_OR; break; case '^': /* check for '^=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_XOREQ; } else typ = TOKT_XOR; break; case '!': /* check for '!=' */ if (p->getch() == '=') { p->inc(); typ = TOKT_NE; } else typ = TOKT_NOT; break; case '~': typ = TOKT_BNOT; break; case '@': typ = TOKT_AT; break; case '#': /* check for '##' and '#@' */ if (p->getch() == '#') { p->inc(); typ = TOKT_POUNDPOUND; } else if (p->getch() == '@') { p->inc(); typ = TOKT_POUNDAT; } else typ = TOKT_POUND; break; case 'R': /* check for regular expression string syntax */ if (p->getch() == '"' || p->getch() == '\'') { /* * It's a regular expression string. Tokenize the string * portion as normal, but mark it as a regex string rather than * a regular string. */ *p = start; return tokenize_string(p, tok, ec, expanding); } /* not a regex string - fall through to default case */ default: /* check to see if it's a symbol */ if (is_syminit(cur) || !is_ascii(cur)) { tokenize_symbol: wchar_t non_ascii = 0; const char *stop = 0; /* note if we're starting with a non-ASCII character */ if (!is_ascii(cur)) non_ascii = cur; /* * scan the identifier (note that we've already skipped the * first character, so we start out at length = 1) */ for (;;) { /* * If it's a non-ASCII character, assume it's an accented * character that's meant (erroneously) to be part of the * symbol name; note the error, but keep the character in * the symbol even though it's illegal, since this is more * likely to keep us synchronized with the intended token * structure of the source. * * Otherwise, stop on any non-symbol character. */ wchar_t ch = p->getch(); if (!is_ascii(ch)) { if (non_ascii == 0) non_ascii = ch; } else if (!is_sym(ch)) break; /* skip this character */ const char *prv = p->getptr(); p->inc(); /* if this character would push us over the limit, end here */ if (stop == 0 && p->getptr() - start.getptr() >= TOK_SYM_MAX_LEN) stop = prv; } /* if we found any non-ASCII characters, flag an error */ if (non_ascii != 0) log_error(TCERR_NON_ASCII_SYMBOL, (int)(p->getptr() - start.getptr()), start.getptr(), (int)non_ascii); /* if we truncated the symbol, issue a warning */ if (stop != 0 && !expanding) log_warning(TCERR_SYMBOL_TRUNCATED, (int)(p->getptr() - start.getptr()), start.getptr(), (int)(stop - start.getptr()), start.getptr()); /* it's a symbol */ typ = TOKT_SYM; } else { /* invalid token */ typ = TOKT_INVALID; } break; } done: /* set the type */ tok->settyp(typ); /* set the text */ tok->set_text(start.getptr(), p->getptr() - start.getptr()); /* return the type */ return typ; } /* * get the next token, limiting to the length of the source buffer */ tc_toktyp_t CTcTokenizer::next_on_line(const CTcTokString *srcbuf, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { /* * save the embedding context, in case the next token isn't part of the * narrowed section of the buffer we're scanning */ tok_embed_ctx oldec = *ec; /* get the next token */ next_on_line(p, tok, ec, expanding); /* if the token is past the end of the line, return EOF */ if (tok->get_text() >= srcbuf->get_text_end()) { /* set the token to indicate end of line */ tok->settyp(TOKT_EOF); /* set the token to point to the end of the buffer */ tok->set_text(srcbuf->get_text_end(), 0); /* restore the embedding context */ *ec = oldec; } /* return the token type */ return tok->gettyp(); } /* * Get the next token on the line, translating escapes in strings. This * updates the line buffer in-place to incorporate the translated string * text. */ tc_toktyp_t CTcTokenizer::next_on_line_xlat(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec) { /* skip whitespace */ skip_ws_and_markers(p); /* if this is a string, translate escapes */ switch(p->getch()) { case '"': case '\'': /* translate the string */ return xlat_string(p, tok, ec); case '>': /* if we're in an embedding, check for '>>' */ if (ec != 0 && ec->in_expr() && ec->parens() == 0 && p->getch_at(1) == '>') return tokenize_string(p, tok, ec, FALSE); /* use the default case */ goto do_normal; case 'R': /* check for a regex string */ if (p->getch_at(1) == '"' || p->getch_at(1) == '\'') return tokenize_string(p, tok, ec, FALSE); /* not a regex string - fall through to the default handling */ default: do_normal: /* for anything else, use the default tokenizer */ return next_on_line(p, tok, ec, FALSE); } } /* * Look up a keyword */ int CTcTokenizer::look_up_keyword(const CTcToken *tok, tc_toktyp_t *kwtok) { CTcHashEntryKw *kw; /* look it up in the keyword table */ kw = (CTcHashEntryKw *)kw_->find(tok->get_text(), tok->get_text_len()); if (kw != 0) { /* we found the keyword - set 'kw' to the keyword token id */ *kwtok = kw->get_tok_id(); /* tell the caller we found it */ return TRUE; } else { /* tell the caller it's not a keyword */ return FALSE; } } /* * Get the next token on the line, translating escape sequences in * strings, and storing strings and symbols in the source block list. * This routine also translates keywords for token types. */ tc_toktyp_t CTcTokenizer::next_on_line_xlat_keep() { tc_toktyp_t typ; /* keep going until we find a valid symbol */ for (;;) { /* skip whitespace and macro expansion flags */ skip_ws_and_markers(&p_); /* see what we have */ switch(p_.getch()) { case '"': case '\'': /* it's a string - translate and save it */ return xlat_string_to_src(&main_in_embedding_, FALSE); case '>': /* if we're in an embedding, this is the end of it */ if (main_in_embedding_.in_expr() && main_in_embedding_.parens() == 0 && p_.getch_at(1) == '>') return xlat_string_to_src(&main_in_embedding_, FALSE); /* use the normal parsing */ goto do_normal; case 'R': /* check for a regex string */ if (p_.getch_at(1) == '"' || p_.getch_at(1) == '\'') return xlat_string_to_src(&main_in_embedding_, FALSE); /* not a regex string - fall through to the default case */ default: do_normal: /* for anything else, use the default tokenizer */ typ = next_on_line(&p_, &curtok_, &main_in_embedding_, FALSE); /* check the token type */ switch(typ) { case TOKT_SYM: /* symbol */ { const char *p; CTcHashEntryKw *kw; /* look it up in the keyword table */ kw = (CTcHashEntryKw *)kw_->find(curtok_.get_text(), curtok_.get_text_len()); if (kw != 0) { /* replace the token with the keyword token type */ typ = kw->get_tok_id(); curtok_.settyp(typ); } else { /* ordinary symbol - save the text */ p = store_source(curtok_.get_text(), curtok_.get_text_len()); /* * change the token's text to point to the * source block, so that this token's text * pointer will remain permanently valid (the * original copy, in the source line buffer, * will be overwritten as soon as we read * another source line; we don't want the caller * to have to worry about this, so we return the * permanent copy) */ curtok_.set_text(p, curtok_.get_text_len()); } } break; case TOKT_FLOAT: case TOKT_BIGINT: /* floating-point number (or promoted large integer) */ { const char *p; /* * save the text so that it remains permanently * valid - we keep track of floats by the original * text, and let the code generator produce the * appropriate object file representation */ p = store_source(curtok_.get_text(), curtok_.get_text_len()); curtok_.set_text(p, curtok_.get_text_len()); } break; case TOKT_INVALID: /* * check for unmappable characters - these will show up as * Unicode U+FFFD, the "replacement character"; log it as * 'unmappable' if applicable, otherwise as an invalid * character */ if (utf8_ptr::s_getch(curtok_.get_text()) == 0xfffd) log_error_curtok(TCERR_UNMAPPABLE_CHAR); else log_error_curtok(TCERR_INVALID_CHAR); /* skip this character */ p_.inc(); /* keep going */ continue; default: break; } } /* return the type */ return typ; } } /* * Translate the string at the current token position in the input * stream to the source block list. */ tc_toktyp_t CTcTokenizer::xlat_string_to_src( tok_embed_ctx *ec, int force_embed_end) { /* * Reserve space for the entire rest of the line. This is * conservative, in that we will definitely need less space than * this. This might cause us to waste a little space here and * there, since we will over-allocate when we have a short string * early in a long line, but this will save us the time of scanning * the string twice just to see how long it is. */ reserve_source(curbuf_->get_text_len() - (p_.getptr() - curbuf_->get_text())); /* translate into the source block */ tc_toktyp_t typ = xlat_string_to( src_ptr_, &p_, &curtok_, ec, force_embed_end); /* commit the space in the source block */ commit_source(curtok_.get_text_len() + 1); /* return the string token */ return typ; } /* * Translate a string, setting up the token structure for the string, * and writing the translated version of the string directly over the * original source buffer of the string. * * Since a translated string can only shrink (because a translated * escape sequence is always shorter than the original source version), * we don't need a separate buffer, but can simply translate into the * source buffer, overwriting the original string as we go. */ tc_toktyp_t CTcTokenizer::xlat_string(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec) { /* * write the translated string over the original string's text, * starting at the character after the quote */ char *dst = p->getptr() + 1; /* translate the string into our destination buffer */ return xlat_string_to(dst, p, tok, ec, FALSE); } /* * Count a run of consecutive quotes */ static int count_quotes(const utf8_ptr *p, wchar_t qu) { /* count quotes */ int cnt = 0; for (utf8_ptr qp((utf8_ptr *)p) ; qp.getch() == qu ; qp.inc(), ++cnt); /* return the count */ return cnt; } /* * Translate a string, setting up the token structure for the string. * We'll update the line buffer in-place to incorporate the translated * string text. */ tc_toktyp_t CTcTokenizer::xlat_string_to( char *dstp, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int force_embed_end) { /* set up our output utf8 pointer */ utf8_ptr dst(dstp); /* note the open quote character */ wchar_t qu = p->getch(); /* set the appropriate string token type */ tok->settyp(qu == '"' ? TOKT_DSTR : qu == '\'' ? TOKT_SSTR : qu == 'R' ? TOKT_RESTR : ec != 0 ? ec->endtok() : TOKT_INVALID); /* if it's a regex string, skip the 'R' and note the actual quote type */ if (qu == 'R') { p->inc(); qu = p->getch(); } /* * If we're at a quote (rather than at '>>' for continuing from an * embedded expression), count consecutive open quotes, to determine if * we're in a triple-quoted string. */ int triple = FALSE; if (qu == '"' || qu == '\'') { /* count the consecutive open quotes */ if (count_quotes(p, qu) >= 3) { /* skip past the additional two open quotes */ p->inc(); p->inc(); /* note that we're in a triple-quoted string */ triple = TRUE; } } /* skip the open quote */ p->inc(); /* check for the end of an embedded expression (forced or actual) */ if (force_embed_end) { /* * they want us to assume the embedding ends here, regardless of * what we're looking at - act the same as though we had * actually seen '>>', but don't skip any input (in fact, back * up one, since we already skipped one character for what we * had thought was the open quote */ p->dec(); /* restore the enclosing string context */ qu = ec->qu(); triple = ec->triple(); tok->settyp(ec->endtok()); /* clear the caller's in-embedding status */ ec->end_expr(); } else if (qu == '>' && ec->parens() == 0) { /* skip the second '>' */ p->inc(); /* restore the enclosing string context */ qu = ec->qu(); triple = ec->triple(); /* end the expression */ ec->end_expr(); } /* scan the string and translate quotes */ for (;;) { /* get this character */ wchar_t cur = p->getch(); /* if this is the matching quote, we're done */ if (cur == qu) { /* * If we're in a triple-quote string, count consecutive quotes. * Triple quotes are greedy: if we have N>3 quotes in a row, * the first N-3 are inside the string, and the last 3 are the * terminating quotes. */ if (triple) { /* we need at least three quotes to end the string */ int qcnt = count_quotes(p, qu); if (qcnt >= 3) { /* copy all but the last three quotes to the output */ for ( ; qcnt > 3 ; --qcnt, p->inc()) dst.setch(qu); /* skip the three quotes */ p->inc_by(3); /* done with the string */ break; } } else { /* * It's an ordinary string, which ends with just one * matching quote, so we're done no matter what follows. * Skip the quote and stop scanning. */ p->inc(); break; } } /* * if we find an end-of-line within the string, it's an error - * we should always splice strings together onto a single line * before starting to tokenize the line */ if (cur == '\0') { /* set the token's text pointer */ size_t bytelen = dst.getptr() - dstp; tok->set_text(dstp, bytelen); /* null-terminate the result string */ dst.setch('\0'); /* * get the length of the unterminated string so far, but for * error logging, limit the length to twenty characters -- * we just want to give the user enough information to find * the string in error, without making the error message * huge */ utf8_ptr dp(dstp); size_t charlen = dp.len(bytelen); if (charlen > 20) bytelen = dp.bytelen(20); /* * Check for a special heuristic case. If the string was of * zero length, and we have something sitting in our * unsplice buffer, here's what probably happened: the input * was missing a ">>" sequence at the end of an embedded * expression, and the parser told us to put it back in. We * had earlier decided we needed to splice up to a quote to * end what looked to us like an unterminated string. If * this is the case, we and the parser are working at cross * purposes; the parser is smarter than we are, so we should * synchronize with it. */ if (tok->get_text_len() == 0 && qu == '"' && unsplicebuf_.get_text_len() != 0) { /* * we must have spliced a line to finish a string - * insert the quote into the splice buffer, and ignore * it here */ /* * make sure there's room for one more character (plus a * null byte) */ unsplicebuf_.ensure_space(unsplicebuf_.get_text_len() + 2); /* get the buffer pointer */ char *buf = unsplicebuf_.get_buf(); /* make room for the '"' */ memmove(buf + 1, buf, unsplicebuf_.get_text_len()); unsplicebuf_.set_text_len(unsplicebuf_.get_text_len() + 1); /* add the '"' */ *buf = '"'; /* * return the 'null token' to tell the caller to try * again - do not log an error at this point */ return TOKT_NULLTOK; } /* log the error */ log_error(TCERR_UNTERM_STRING, (char)qu, (int)bytelen, dstp, (char)qu); /* return the string type */ return tok->gettyp(); } /* if this is an escape, translate it */ if (cur == '\\') { /* translate the escape */ xlat_escape(&dst, p, qu, triple); continue; } else if (ec != 0 && tok->gettyp() != TOKT_RESTR && cur == '<' && p->getch_at(1) == '<') { /* * It's the start of an embedded expression - change the type * to so indicate. If we think we have a regular SSTR or DSTR, * switch to the appropriate START type, since the part up to * here is actually the starting fragment of a string with an * embedded expression. If we think we're in an END section, * switch to the corresponding MID section, since we're parsing * a fragment that already followed an embedding. The only * other possibility is that we're in a MID section, in which * case just stay in the same MID section. */ tc_toktyp_t tt = tok->gettyp(); tok->settyp(tt == TOKT_DSTR ? TOKT_DSTR_START : tt == TOKT_DSTR_END ? TOKT_DSTR_MID : tt == TOKT_SSTR ? TOKT_SSTR_START : tt == TOKT_SSTR_END ? TOKT_SSTR_MID : tt); /* skip the << */ p->inc_by(2); /* * Check for a '%' sprintf-style formatting sequence. If we * have a '%' immediately after the second '<', it's a sprintf * format code. */ if (p->getch() == '%') { /* remember the starting point of the format string */ utf8_ptr fmt(p); /* scan the format spec */ scan_sprintf_spec(p); /* translate escapes */ utf8_ptr dst(&fmt); xlat_escapes(&dst, &fmt, p); /* push the format spec into the token stream */ CTcToken ftok; ftok.set_text(fmt.getptr(), dst.getptr() - fmt.getptr()); ftok.settyp(TOKT_FMTSPEC); push(&ftok); } /* tell the caller we're in an embedding */ ec->start_expr(qu, triple, TRUE); /* stop scanning */ break; } /* copy this character to the output position */ dst.setch(cur); /* get the next character */ p->inc(); } /* set the token's text pointer */ tok->set_text(dstp, dst.getptr() - dstp); /* null-terminate the result string */ dst.setch('\0'); /* return the string type */ return tok->gettyp(); } /* * Translate all escapes in a string */ void CTcTokenizer::xlat_escapes(utf8_ptr *dst, const utf8_ptr *srcp, const utf8_ptr *endp) { /* set up writable copy of the source */ utf8_ptr p(srcp); /* scan the string */ while (p.getptr() < endp->getptr()) { /* check for an escape */ wchar_t ch = p.getch(); if (ch == '\\') { /* escape - translate it */ xlat_escape(dst, &p, 0, FALSE); } else { /* ordinary character - copy it as is */ dst->setch(ch); p.inc(); } } } /* * Skip a \ escape sequence */ void CTcTokenizer::skip_escape(utf8_ptr *p) { if (p->getch() == '\\') { /* translate an escape into an empty buffer */ char buf[10]; utf8_ptr dst(buf); xlat_escape(&dst, p, 0, FALSE); } else { /* just skip the character */ p->inc(); } } /* * Translate a \ escape sequence */ void CTcTokenizer::xlat_escape(utf8_ptr *dst, utf8_ptr *p, wchar_t qu, int triple) { int i, acc; /* get the character after the escape */ p->inc(); wchar_t cur = p->getch(); /* see what we have */ switch(cur) { case '^': /* caps - 0x000F */ cur = 0x000F; break; case '"': case '\'': /* * If we're in triple-quote mode, and this is the matching quote, a * single backslash escapes a whole run of consecutive quotes, so * skip the whole run. */ if (triple && cur == qu) { /* copy and skip all consecutive quotes */ for ( ; p->getch() == qu ; dst->setch(cur), p->inc()) ; /* we're done */ return; } break; case 'v': /* miniscules - 0x000E */ cur = 0x000E; break; case 'b': /* blank line - 0x000B */ cur = 0x000B; break; case ' ': /* quoted space - 0x0015 */ cur = 0x0015; break; case 'n': /* newline - explicitly use Unicode 10 character */ cur = 10; break; case 'r': /* return - explicitly use Unicode 13 character */ cur = 13; break; case 't': /* tab - explicitly use Unicode 9 character */ cur = 9; break; case 'u': /* * Hex unicode character number. Read up to 4 hex digits that * follow the 'u', and use that as a Unicode character ID. */ for (i = 0, acc = 0, p->inc() ; i < 4 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another hex digit, add it into the accumulator; * otherwise, we're done */ if (is_xdigit(cur)) acc = 16*acc + value_of_xdigit(cur); else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* we've already skipped ahead to the next character, so we're done */ return; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* * Octal ASCII character number. Accumulate up to three octal * numbers, and use the result as a character ID. */ for (i = 0, acc = 0 ; i < 3 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another digit, and it would leave our result in the * 0-255 range, count it; if not, we're done */ if (is_odigit(cur)) { /* compute the new value */ long new_acc = 8*acc + value_of_odigit(cur); /* if this would be too high, don't count it */ if (new_acc > 255) break; else acc = new_acc; } else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* * continue with the current character, since we've already skipped * ahead to the next one */ return; case 'x': /* * Hex ASCII character number. Read up to two hex digits as a * character number. */ for (i = 0, acc = 0, p->inc() ; i < 2 ; ++i, p->inc()) { /* get the next character */ cur = p->getch(); /* * if it's another hex digit, add it into the accumulator; * otherwise, we're done */ if (is_xdigit(cur)) acc = 16*acc + value_of_xdigit(cur); else break; } /* use the accumulated value as the character number */ dst->setch((wchar_t)acc); /* * continue with the current character, since we've already skipped * ahead to the next one */ return; case '<': case '>': case '\\': /* just copy these literally */ break; default: /* log a pedantic error */ log_pedantic(TCERR_BACKSLASH_SEQ, cur); /* copy anything else as-is, including the backslash */ dst->setch('\\'); break; } /* set the output character */ dst->setch(cur); /* skip the current character */ p->inc(); } /* * Scan a sprintf format spec */ void CTcTokenizer::scan_sprintf_spec(utf8_ptr *p) { /* skip the '%' */ p->inc(); /* scan the flags section */ for (int done = FALSE ; !done ; ) { switch (p->getch()) { case '[': /* skip digits and the closing ']' */ for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; if (p->getch() == ']') p->inc(); break; case '_': /* padding spec - skip this and the next character */ p->inc(); if (p->getch() != 0) skip_escape(p); break; case '-': case '+': case ' ': case ',': case '#': /* format spec - just skip it */ p->inc(); break; default: /* anything else means we're done with the flags */ done = TRUE; break; } } /* scan the width */ for ( ; is_digit(p->getch()) ; p->inc()) ; /* scan the precision */ if (p->getch() == '.') for (p->inc() ; is_digit(p->getch()) ; p->inc()) ; /* the next character is the format spec - skip it */ if (p->getch() != 0) skip_escape(p); } /* * Skip a string, setting up the token structure for the string. This * routine only parses to the end of the line; if the line ends with the * string unterminated, we'll flag an error. */ tc_toktyp_t CTcTokenizer::tokenize_string( utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding) { /* remember where the text starts */ const char *start = p->getptr(); /* check for a regex string - R'...' or R"..." */ int regex = (p->getch() == 'R'); if (regex) p->inc(); /* note the quote type, for matching later */ wchar_t qu = p->getch(); /* skip the quote in the input */ p->inc(); /* check for triple quotes */ int triple = FALSE; if ((qu == '"' || qu == '\'') && count_quotes(p, qu) >= 2) { /* note the triple quotes */ triple = TRUE; /* skip the open quotes */ p->inc(); p->inc(); } /* determine the token type based on the quote type */ tc_toktyp_t typ; int allow_embedding; switch(qu) { case '\'': /* single-quoted string */ typ = TOKT_SSTR; allow_embedding = (ec != 0); break; case '"': /* regular double-quoted string */ typ = TOKT_DSTR; allow_embedding = (ec != 0); break; case '>': /* get the ending type for the embedding */ if (ec != 0) { /* return to the enclosing string context */ typ = ec->endtok(); qu = ec->qu(); triple = ec->triple(); /* allow more embeddings */ allow_embedding = TRUE; /* exit the expression context */ ec->end_expr(); } else { /* we can only finish an embedding if we were already in one */ typ = TOKT_INVALID; qu = '"'; allow_embedding = FALSE; } /* skip the extra '>' character */ p->inc(); break; default: /* anything else is invalid */ typ = TOKT_INVALID; qu = '"'; allow_embedding = FALSE; break; } /* if it's a regex string, it has special handling */ if (regex) { /* embeddings aren't allowed in regex strings */ allow_embedding = FALSE; /* the token type is 'regex string' */ typ = TOKT_RESTR; } /* this is where the string's contents start */ const char *contents_start = p->getptr(); /* we don't know where it ends yet */ const char *contents_end; /* scan the string */ for (;;) { /* get the current character */ wchar_t cur = p->getch(); /* see what we have */ if (cur == '\\') { /* escape sequence - skip an extra character */ p->inc(); /* * if we're in a triple-quoted string, and this matches the * quote type, a single '\' escapes all consecutive quotes */ if (triple && p->getch() == qu) { /* skip the whole run of quotes */ for ( ; p->getch() == qu ; p->inc()) ; /* take it from the top, as we've already skipped them all */ continue; } } else if (cur == '<' && allow_embedding && p->getch_at(1) == '<') { /* * it's the start of an embedded expression - return the * appropriate embedded string part type */ typ = (typ == TOKT_DSTR ? TOKT_DSTR_START : typ == TOKT_DSTR_END ? TOKT_DSTR_MID : typ == TOKT_SSTR ? TOKT_SSTR_START : typ == TOKT_SSTR_END ? TOKT_SSTR_MID : typ); /* remember that we're in an embedding in the token stream */ ec->start_expr(qu, triple, !expanding); /* this is where the contents end */ contents_end = p->getptr(); /* skip the two embedding characters */ p->inc(); p->inc(); /* we're done - set the text in the token */ tok->set_text(start, p->getptr() - start); /* done */ break; } else if (cur == qu) { /* * if we're in a triple-quoted string, it ends at a triple * quote, except that any additional consecutive quotes go * inside the string rather than outside */ if (triple) { /* we need at least three quotes in a row to end the string */ int qcnt = count_quotes(p, qu); if (qcnt >= 3) { /* the contents include any quotes before the last 3 */ p->inc_by(qcnt - 3); contents_end = p->getptr(); /* skip the three close quotes */ p->inc_by(3); } else { /* it's not ending; skip the quotes and carry on */ p->inc_by(qcnt); continue; } } else { /* note where the string ends */ contents_end = p->getptr(); /* skip the closing quote */ p->inc(); } /* we're done - set the text in the token */ tok->set_text(start, p->getptr() - start); /* done */ break; } else if (cur == '\0') { /* this is where the contents end */ contents_end = p->getptr(); /* * We have an unterminated string. If we're evaluating a * preprocessor constant expression, log an error; otherwise * let it go for now, since we'll catch the error during the * normal tokenizing pass for parsing. */ if (G_tok->in_pp_expr_) log_error(TCERR_PP_UNTERM_STRING); /* set the partial text */ tok->set_text(start, p->getptr() - start); /* end of line - return with the string unfinished */ break; } /* skip this character of input */ p->inc(); } /* * if we're not in preprocessor mode, and we're saving string text, * write the string to the string text output file */ if (!G_tok->in_pp_expr_ && G_tok->string_fp_ != 0 && contents_start != contents_end) { /* write the line, translating back to the source character set */ G_tok->string_fp_map_ ->write_file(G_tok->string_fp_, contents_start, (size_t)(contents_end - contents_start)); /* add a newline */ G_tok->string_fp_->write("\n", 1); } /* set the type in the token */ tok->settyp(typ); /* return the token type */ return tok->gettyp(); } /* ------------------------------------------------------------------------ */ /* * Read a source line and handle preprocessor directives. This routine * will transparently handle #include, #define, and other directives; * when this routine returns, the input buffer will have a line of text * that contains no # directive. * * Returns zero on success, non-zero upon reaching the end of the input. */ int CTcTokenizer::read_line_pp() { /* * Read the next line from the input. If that fails, return an end * of file indication. */ int ofs = read_line(FALSE); if (ofs == -1) return 1; /* * before we process comments, note whether or not the line started * out within a character string */ int started_in_string = (in_quote_ != '\0'); /* set up our source pointer to the start of the new line */ start_new_line(&linebuf_, ofs); /* skip leading whitespace */ while (is_space(p_.getch())) p_.inc(); /* * If this line begins with a '#', process the directive. Ignore * any initial '#' if the line started off in a string. */ if (!started_in_string && p_.getch() == '#' && allow_pp_) { struct pp_kw_def { const char *kw; int process_in_false_if; void (CTcTokenizer::*func)(); }; static pp_kw_def kwlist[] = { { "charset", FALSE, &CTcTokenizer::pp_charset }, { "pragma", FALSE, &CTcTokenizer::pp_pragma }, { "include", FALSE, &CTcTokenizer::pp_include }, { "define", FALSE, &CTcTokenizer::pp_define }, { "if", TRUE, &CTcTokenizer::pp_if }, { "ifdef", TRUE, &CTcTokenizer::pp_ifdef }, { "ifndef", TRUE, &CTcTokenizer::pp_ifndef }, { "else", TRUE, &CTcTokenizer::pp_else }, { "elif", TRUE, &CTcTokenizer::pp_elif }, { "endif", TRUE, &CTcTokenizer::pp_endif }, { "error", FALSE, &CTcTokenizer::pp_error }, { "undef", FALSE, &CTcTokenizer::pp_undef }, { "line", FALSE, &CTcTokenizer::pp_line }, { 0, 0, 0 } }; pp_kw_def *kwp; const char *kwtxt; size_t kwlen; /* skip the '#' */ p_.inc(); /* * If the line ended inside a comment, read the next line until * we're no longer in a comment. The ANSI C preprocessor rules * say that a newline in a comment should not be treated as a * lexical newline, so pretend that the next line is part of the * preprocessor line in such a case. */ while (str_->is_in_comment()) { size_t p_ofs; /* remember the current offset in the line buffer */ p_ofs = p_.getptr() - linebuf_.get_buf(); /* append another line - stop at the end of the stream */ if (read_line(TRUE) == -1) break; /* restore the line pointer, in case the buffer moved */ start_new_line(&linebuf_, p_ofs); } /* read the directive */ next_on_line(); /* * if we've reached the end of the line, it's a null directive; * simply return an empty line */ if (curtok_.gettyp() == TOKT_EOF) { clear_linebuf(); return 0; } /* get the text and length of the keyword */ kwtxt = curtok_.get_text(); kwlen = curtok_.get_text_len(); /* if it's not a symbol, it's not a valid directive */ if (curtok_.gettyp() != TOKT_SYM) { /* log the error and return an empty line */ log_error(TCERR_INV_PP_DIR, (int)kwlen, kwtxt); clear_linebuf(); return 0; } /* determine which keyword we have, and process it */ for (kwp = kwlist ; kwp->kw != 0 ; ++kwp) { /* is this our keyword? */ if (strlen(kwp->kw) == kwlen && memcmp(kwtxt, kwp->kw, kwlen) == 0) { /* * This is our directive. * * If we're in the false branch of a #if block, only * process the directive if it's a kind of directive * that we should process in false #if branches. The * only directives that we process in #if branches are * those that would affect the #if branching, such as a * #endif or a nested #if. */ if (!in_false_if() || kwp->process_in_false_if) { /* invoke the handler to process the directive */ (this->*(kwp->func))(); } else { /* * we're in a #if branch not taken - simply clear * the buffer */ clear_linebuf(); } /* we don't need to look any further */ break; } } /* * if we didn't find the keyword, log an error and otherwise * ignore the entire line */ if (kwp->kw == 0) log_error(TCERR_INV_PP_DIR, (int)kwlen, kwtxt); /* * Preprocessor lines must always be entirely self-contained. * Therefore, it's not valid for a string to start on a * preprocessor line and continue onto subsequent lines. If * we're marked as being inside a string, there must have been * an error on the preprocessor line. Simply clear the * in-string flag; we don't need to issue an error at this * point, since the preprocessor line handler should have * already caught the problem and reported an error. */ in_quote_ = '\0'; } else { /* * There's no preprocessor directive. * * If we're in a false #if branch, return an empty line. We * return an empty line rather than skipping to the next line so * that the caller sees the same number of lines as are in the * original source. */ if (in_false_if()) { /* * it's a #if not taken - we don't want to compile the line * at all, so just clear it out */ clear_linebuf(); expbuf_.clear_text(); } else { /* * If we ended the line in a string, splice additional lines * onto the end of this line until we find the end of the * string, then unsplice the part after the end of the * string. */ if (in_quote_ != '\0') { /* splice additional lines to finish the quote */ splice_string(); } /* * Expand macros in the line, splicing additional source * lines if necessary to fill out any incomplete actual * parameter lists. */ start_new_line(&linebuf_, 0); expand_macros_curline(TRUE, FALSE, FALSE); } /* store the line in the appropriate place */ if (pp_only_mode_) { /* * we're only preprocessing - store the macro-expanded line * back in the line buffer so that the caller can read out * the final preprocessed text */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } else { /* * We're compiling - simply read subsequent tokens out of * the expansion buffer. */ start_new_line(&expbuf_, 0); } } /* return success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Read the next line from the input file. Returns a pointer to the * start of the newly-read data on success, or null if we reach the end * of the input. * * If 'append' is true, we'll add the line on to the end of the existing * buffer; otherwise, we'll overwrite what's in the buffer. * * The only preprocessing performed in this routine is line-splicing. * Any line that ends with a backslash character will be spliced with * the following line, with the backslash and newline removed. * * The new line will be stored in our internal buffer, and will be * null-terminated with the trailing newline removed. * * If we reach the end of the current file, and there's an enclosing * file, we'll resume reading from the enclosing file. Hence, when this * routine returns non-zero, it indicates that we've reached the end of * the entire source, not just of the current file. */ int CTcTokenizer::read_line(int append) { /* if there's no input stream, indicate end-of-file */ if (str_ == 0) return -1; /* if we're not appending, clear out the line buffer */ if (!append) { /* start with an empty line */ clear_linebuf(); /* note the current input position */ last_desc_ = str_->get_desc(); last_linenum_ = str_->get_next_linenum(); } /* note where the new data starts */ size_t len = linebuf_.get_text_len(); size_t start_len = len; /* * if there's anything in the unsplice buffer, use it as the new * line */ if (unsplicebuf_.get_text_len() != 0) { /* * Copy the unsplice buffer as the current line. Note that we * don't have to worry about any of the complicated cases, such * as whether or not it ends with a newline or a backslash, * because the unspliced line was already processed as an input * line when we read it in the first place. */ linebuf_.append(unsplicebuf_.get_text(), unsplicebuf_.get_text_len()); /* clear the unsplice buffer, since it's been consumed now */ unsplicebuf_.clear_text(); /* * make the current line the appended line - if we're * unsplicing, it means that we appended, so the current line is * now the line from which the last appended text came */ last_desc_ = appended_desc_; last_linenum_ = appended_linenum_; /* return the offset of the new text */ return start_len; } /* if we're appending, note where the appendage is coming from */ if (append) { /* remember the last source line appended */ appended_desc_ = str_->get_desc(); appended_linenum_ = str_->get_next_linenum(); } /* keep going until we finish reading the input line */ for ( ;; ) { /* read a line of text from the input file */ size_t curlen = str_->get_src()->read_line( linebuf_.get_buf() + len, linebuf_.get_buf_size() - len); /* check for end of file */ if (curlen == 0) { /* * We've reached the end of the current input stream. If * we've already read anything into the current line, it * means that the file ended in mid-line, without a final * newline character; ignore this and proceed with the line * as it now stands in this case. */ if (len > start_len) break; /* * We've finished with this stream. If there's a parent * stream, return to it; otherwise, we're at the end of the * source. */ /* * if we didn't close all of the #if/#ifdef levels opened * within this file, flag one or more errors */ while (if_sp_ > str_->get_init_if_level()) { /* get the filename from the #if stack */ const char *fname = if_stack_[if_sp_ - 1].desc->get_fname(); /* if we're in test reporting mode, use the root name only */ if (test_report_mode_) fname = os_get_root_name((char *)fname); /* log the error */ log_error(TCERR_IF_WITHOUT_ENDIF, if_stack_[if_sp_ - 1].linenum, (int)strlen(fname), fname); /* discard the #if level */ pop_if(); } /* remember the old stream */ CTcTokStream *old_str = str_; /* return to the parent stream, if there is one */ str_ = str_->get_parent(); /* delete the old stream now that we're done with it */ delete old_str; /* note the new file the line will be coming from */ if (!append && str_ != 0) { last_desc_ = str_->get_desc(); last_linenum_ = str_->get_next_linenum(); } /* if there's no stream, return end of file */ if (str_ == 0) return -1; /* * restore the #pragma newline_spacing mode that was in effect * when we interrupted the parent stream */ string_newline_spacing_ = str_->get_newline_spacing(); /* if there's a parser, notify it of the new pragma C mode */ #if 0 // #pragma C is not currently used if (G_prs != 0) G_prs->set_pragma_c(str_->is_pragma_c()); #endif /* go back to read the next line from the parent */ continue; } /* set the new length of the buffer contents */ len += curlen - 1; linebuf_.set_text_len(len); /* * Check the result to see if it ends in a newline. If not, it * means either that we don't have room in the buffer for the * full source line, or we've reached the last line in the file, * and it doesn't end with a newline. * * Note that the file reader will always supply us with '\n' * newlines, regardless of the local operating system * conventions. * * Also, check to see if the line ends with '\\'. If so, remove * the '\\' character and read the next line, since this * indicates that the logical line continues onto the next * newline-deliminted line. */ if (len != 0 && linebuf_.get_text()[len - 1] != '\n') { /* * There's no newline, hence the file reader wasn't able to * fit the entire line into our buffer, or else we've read * the last line in the file and there's no newline at the * end. If we haven't reached the end of the file, expand * our line buffer to make room to read more from this same * line. */ if (!str_->get_src()->at_eof()) linebuf_.expand(); } else if (len > 1 && linebuf_.get_text()[len - 2] == '\\') { /* * There's a backslash at the end of the line, so they want * to continue this logical line. Remove the backslash, and * read the next line onto the end of the current line. * * Note that we must remove two characters from the end of * the line (and tested for buf_[len-2] above) because we * have both a backslash and a newline at the end of the * line. */ len -= 2; linebuf_.set_text_len(len); /* count reading the physical line */ str_->count_line(); } else { /* remove the newline from the buffer */ if (len != 0) { --len; linebuf_.set_text_len(len); } /* count reading the line */ str_->count_line(); /* done */ break; } } /* * remove comments from the newly-read material - this replaces each * comment by a single whitespace character */ process_comments(start_len); /* * we've successfully read a line -- return the offset of the start of * the newly-read text */ return start_len; } /* * Un-splice a line at the given point. This breaks the current source * line in two, keeping the part before the given point as the current * line, but making the part from the given point to the end of the line * a new source line. We'll put the new source line into a special * holding buffer, and then fetch this part as a new line the next time * we read a line in read_line(). */ void CTcTokenizer::unsplice_line(const char *new_line_start) { /* make sure the starting point is within the current line */ if (!(new_line_start >= linebuf_.get_text() && new_line_start <= linebuf_.get_text() + linebuf_.get_text_len())) { /* note the error - this is an internal problem */ throw_internal_error(TCERR_UNSPLICE_NOT_CUR); return; } /* calculate the length of the part we're keeping */ size_t keep_len = new_line_start - linebuf_.get_text(); /* * prepend the remainder of the current line into the unsplice buffer * (we prepend it because the unsplice line is text that comes after * the current line - so anything in the current line comes before * anything already in the unsplice buffer) */ unsplicebuf_.prepend(new_line_start, linebuf_.get_text_len() - keep_len); /* cut off the current line at the given point */ linebuf_.set_text_len(keep_len); } /* ------------------------------------------------------------------------ */ /* * Store text in the source array */ const char *CTcTokenizer::store_source(const char *txt, size_t len) { /* reserve space for the text */ reserve_source(len); /* store it */ const char *p = store_source_partial(txt, len); /* add a null terminator */ static const char nt[1] = { '\0' }; store_source_partial(nt, 1); /* return the pointer to the stored space */ return p; } /* * Store partial source; use this AFTER reserving the necessary space. If * you want null-termination, be sure to reserve the extra byte for that * and include it in the string. This can be used to build a string piece * by piece; we simply add the text without null-terminating it. */ const char *CTcTokenizer::store_source_partial(const char *txt, size_t len) { /* remember where the string starts */ const char *p = src_ptr_; /* store the text */ memcpy(src_ptr_, txt, len); /* advance the source block write position and length */ src_ptr_ += len; src_rem_ -= len; /* return the storage pointer */ return p; } /* * Reserve space for text in the source array. This always reserves the * requested amount of space, plus an extra byte for null termination. */ void CTcTokenizer::reserve_source(size_t len) { /* * if we don't have enough space for this line in the current source * block, start a new block */ if (len + 1 > src_rem_) { CTcTokSrcBlock *blk; /* * if the line is too long for a source block, throw a fatal * error */ if (len + 1 > TCTOK_SRC_BLOCK_SIZE) throw_fatal_error(TCERR_SRCLINE_TOO_LONG, (long)TCTOK_SRC_BLOCK_SIZE); /* allocate a new block */ blk = new CTcTokSrcBlock(); /* link it into our list */ src_cur_->set_next(blk); /* it's now the current block */ src_cur_ = blk; /* start writing at the start of this block */ src_rem_ = TCTOK_SRC_BLOCK_SIZE; src_ptr_ = blk->get_buf(); } } /* * Commit space previously reserved and now used in the source block * list */ void CTcTokenizer::commit_source(size_t len) { /* advance the write position past the committed text */ src_ptr_ += len; src_rem_ -= len; } /* ------------------------------------------------------------------------ */ /* * Expand macros in the current line from the current source pointer, * filling in expbuf_ with the expanded result. */ int CTcTokenizer::expand_macros_curline(int read_more, int allow_defined, int append_to_expbuf) { int err; /* expand macros in the current line */ err = expand_macros(&linebuf_, &p_, &expbuf_, read_more, allow_defined, append_to_expbuf); /* if that failed, return an error */ if (err != 0) return err; /* * if we're in preprocessor mode, clean up the text for human * consumption by removing our various expansion flags */ if (pp_only_mode_) remove_expansion_flags(&expbuf_); /* return the result */ return err; } /* ------------------------------------------------------------------------ */ /* * Remove the special internal macro expansion flags from an expanded macro * buffer. */ void CTcTokenizer::remove_expansion_flags(CTcTokString *buf) { utf8_ptr p; char *src; char *dst; /* * Scan the expansion buffer and remove all of the no-more-expansion * flag bytes - we're done expanding the macro now, so we don't need * this information any longer. When we're writing out the * preprocessed source for human viewing, we don't want to leave these * internal markers in the expanded source. */ for (src = dst = buf->get_buf(), p.set(src) ; p.getch() != '\0' ; ) { /* if this isn't a macro flag, copy it */ if (p.getch() == TOK_MACRO_EXP_END) { /* skip the flag byte and the following embedded pointer */ src += 1 + sizeof(CTcHashEntryPp *); p.set(src); } else if (p.getch() == TOK_FULLY_EXPANDED_FLAG) { /* skip the flag byte */ ++src; p.set(src); } else { /* skip this character */ p.inc(); /* copy the bytes of this character as-is */ while (src < p.getptr()) *dst++ = *src++; } } /* set the new buffer length */ buf->set_text_len(dst - buf->get_buf()); } /* ------------------------------------------------------------------------ */ /* * Expand macros in the current line, reading additional source lines if * necessary. * * 'src' is a pointer to the start of the text to expand; it must point * into the 'srcbuf' buffer. If 'src' is null, we'll simply start at * the beginning of the source buffer. */ int CTcTokenizer::expand_macros(CTcTokString *srcbuf, utf8_ptr *src, CTcTokString *expbuf, int read_more, int allow_defined, int append) { tc_toktyp_t typ; CTcToken tok; CTcTokString *subexp; size_t startofs; utf8_ptr local_src; CTcTokStringRef local_srcbuf; CTcMacroRsc *res; int err; /* presume success */ err = 0; /* get a macro expansion resource object */ res = alloc_macro_rsc(); if (res == 0) return 1; /* get our subexpression buffer from the resource object */ subexp = &res->line_exp_; /* if there's no source buffer or source pointer, provide one */ if (srcbuf == 0) { /* * there's no source buffer - provide our own non-allocated * buffer tied to the caller's buffer */ local_srcbuf.set_buffer(src->getptr(), strlen(src->getptr())); srcbuf = &local_srcbuf; } else if (src == 0) { /* * there's no source pointer - start at the beginning of the * source buffer */ local_src.set((char *)srcbuf->get_text()); src = &local_src; } /* clear the expansion buffer, unless we're appending to the buffer */ if (!append) expbuf->clear_text(); /* * Make sure we have room for a copy of the source line. This is an * optimization for the simple case where we'll just copy the source * line unchanged, so that we don't have to repeatedly expand the * buffer; we will, however, expand the buffer dynamically later, if * this pre-allocation should prove to be insufficient. */ expbuf->ensure_space(expbuf->get_text_len() + srcbuf->get_text_len()); /* note the starting offset, if we have an underlying string buffer */ startofs = src->getptr() - srcbuf->get_text(); /* read the first token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* scan through the tokens on the line, looking for macros to expand */ while (typ != TOKT_EOF) { /* * if it's a symbol, and it hasn't already been marked as fully * expanded, look it up in the #define table */ if (typ == TOKT_SYM && !tok.get_fully_expanded()) { CTcHashEntryPp *entry; /* * Look up the symbol in the #define symbol table. If we * find it, expand the macro. Otherwise, if the "defined" * operator is active, check for that. * * Do not expand the macro if we find that it has already * been expanded on a prior scan through the current text. */ entry = find_define(tok.get_text(), tok.get_text_len()); if ((entry != 0 && !scan_for_prior_expansion(*src, srcbuf->get_text_end(), entry)) || (allow_defined && tok.get_text_len() == 7 && memcmp(tok.get_text(), "defined", 7) == 0)) { size_t macro_ofs; size_t rem_len; int expanded; /* get the offset of the macro token in the source buffer */ macro_ofs = tok.get_text() - srcbuf->get_text(); /* expand it into our sub-expansion buffer */ if (entry != 0) { /* expand the macro */ err = expand_macro(res, subexp, srcbuf, src, macro_ofs, entry, read_more, allow_defined, &expanded); } else { /* parse and expand the defined() operator */ err = expand_defined(subexp, srcbuf, src); /* "defined" always expands if there's not an error */ expanded = TRUE; } /* if an error occurred, return failure */ if (err) goto done; /* * if we expanded something, append everything we * skipped preceding the macro, then rescan; otherwise, * just keep going without a rescan */ if (expanded) { /* copy the preceding text to the output */ expbuf->append(srcbuf->get_text() + startofs, macro_ofs - startofs); } else { /* * we didn't expand - get the next token after the * macro */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* continue processing from this token */ continue; } /* * We must now insert the expansion into the source * buffer at the current point, and re-scan the * expansion, *along with* the rest of the original * source line (this is how ANSI C specifies the * process). * * If we can read more, we must be reading out of the * main input line buffer, so insert the expansion text * directly into the original source stream, and * continue reading out of the source stream; this will * simplify the case where we must read more data from * the file in the course of the expansion. If we can't * read more, simply copy the remainder of the current * input line onto the expanded macro and use it as the * new input buffer. */ /* get the current offset in the source line */ startofs = src->getptr() - srcbuf->get_text(); /* figure out how much is left on the current line */ rem_len = srcbuf->get_text_len() - startofs; /* check to see if we can read more */ if (read_more) { /* * we're reading from the original line input buffer * -- insert the expansion into the source buffer at * the current point, replacing the original macro * text */ /* make sure we have room for adding the expansion text */ srcbuf->ensure_space(macro_ofs + rem_len + subexp->get_text_len()); /* make sure src is still pointing to the right place */ src->set(srcbuf->get_buf() + macro_ofs); /* move the remainder of the current line to make room */ memmove(srcbuf->get_buf() + macro_ofs + subexp->get_text_len(), srcbuf->get_buf() + startofs, rem_len); /* insert the expansion text */ memcpy(srcbuf->get_buf() + macro_ofs, subexp->get_buf(), subexp->get_text_len()); /* set the new source length */ srcbuf->set_text_len(macro_ofs + rem_len + subexp->get_text_len()); /* the new starting offset is the current position */ startofs = macro_ofs; /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* continue processing from this token */ continue; } else { /* * we're reading from a read-only buffer -- add the * remainder of the source to the expansion buffer, * and recursively parse the remainder */ subexp->append(srcbuf->get_text() + startofs, rem_len); /* * evaluate the remainder recursively and append it * to the expansion already in progress */ err = expand_macros(subexp, 0, expbuf, FALSE, allow_defined, TRUE); /* we're done */ goto done; } } } /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); } /* add the remainder of the source to the output */ expbuf->append(srcbuf->get_text() + startofs, tok.get_text() - startofs - srcbuf->get_text()); done: /* release our macro resource object */ release_macro_rsc(res); /* return the result */ return err; } /* * Allocate a macro resource object. If we're out of resource objects * in the pool, we'll add another object to the pool. */ CTcMacroRsc *CTcTokenizer::alloc_macro_rsc() { CTcMacroRsc *rsc; /* * if there's anything in the available list, take the first item * off the list and return it */ if (macro_res_avail_ != 0) { /* remember the item to return */ rsc = macro_res_avail_; /* remove it from the list */ macro_res_avail_ = macro_res_avail_->next_avail_; /* return it */ return rsc; } /* there's nothing on the available list - allocate a new item */ rsc = new CTcMacroRsc(); /* if that failed, return failure */ if (rsc == 0) { log_error(TCERR_OUT_OF_MEM_MAC_EXP); return 0; } /* add it onto the master list */ rsc->next_ = macro_res_head_; macro_res_head_ = rsc; /* return it */ return rsc; } /* * Release a macro resource, returning it to the pool */ void CTcTokenizer::release_macro_rsc(CTcMacroRsc *rsc) { /* put it back at the head of the available list */ rsc->next_avail_ = macro_res_avail_; macro_res_avail_ = rsc; } /* * Scan a buffer for a prior-expansion flag for a given macro. We'll * look through the buffer for a TOK_MACRO_EXP_END byte that mentions * the given symbol table entry; we'll return true if found, false if * not. True means that the symbol has already been expanded on a prior * scan of the text, so it should not be re-expanded now. */ int CTcTokenizer::scan_for_prior_expansion(utf8_ptr src, const char *src_end, const CTcHashEntryPp *entry) { /* scan the buffer for the expansion flag byte */ while (src.getptr() < src_end) { /* if this is the flag, check what follows */ if (src.getch() == TOK_MACRO_EXP_END) { CTcHashEntryPp *flag_entry; /* read the entry from the buffer */ memcpy(&flag_entry, src.getptr() + 1, sizeof(flag_entry)); /* if it matches, indicate that we found it */ if (entry == flag_entry) return TRUE; /* it's not a match - keep scanning after this flag sequence */ src.set(src.getptr() + 1 + sizeof(flag_entry)); } else { /* it's not the flag - skip this character */ src.inc(); } } /* we didn't find it */ return FALSE; } /* * Go through a macro expansion and translate from end-of-expansion * markers to individual token full-expansion markers. This is used * after we leave a recursion level to convert expanded text into text * suitable for use in further expansion at an enclosing recursion * level. */ void CTcTokenizer::mark_full_exp_tokens(CTcTokString *dstbuf, const CTcTokString *srcbuf, int append) const { utf8_ptr p; CTcToken tok; const char *start; tok_embed_ctx ec; /* clear the output buffer if we're not appending to existing text */ if (!append) dstbuf->clear_text(); /* remember the starting point */ start = srcbuf->get_text(); /* scan the source buffer */ p.set((char *)start); for (;;) { CTcHashEntryPp *cur_entry; tc_toktyp_t typ; char ch; /* get the next token; stop at the end of the line */ typ = next_on_line(srcbuf, &p, &tok, &ec, TRUE); if (typ == TOKT_EOF) break; /* * if this macro token is being expanded, and it's not already * marked for no more expansion, mark it */ if (typ == TOKT_SYM && !tok.get_fully_expanded() && (cur_entry = find_define(tok.get_text(), tok.get_text_len())) != 0 && scan_for_prior_expansion(p, srcbuf->get_text_end(), cur_entry)) { /* * This token has been fully expanded in the substitution * buffer but hasn't yet been marked as such - we must * insert the fully-expanded marker. First, add up to the * current point to the output buffer. */ if (tok.get_text() > start) dstbuf->append(start, tok.get_text() - start); /* add the fully-expanded marker */ ch = TOK_FULLY_EXPANDED_FLAG; dstbuf->append(&ch, 1); /* the new starting point is the start of the symbol token */ start = tok.get_text(); } } /* copy any remaining text to the output */ if (tok.get_text() > start) dstbuf->append(start, tok.get_text() - start); /* * Remove any macro expansion end markers from the output buffer. * We don't want to leave these around, because they don't apply to * the enclosing buffer into which we'll substitute this result. * Note that we've already ensured that these markers will be * respected for the substitution text by inserting "fully expanded" * markers in front of each token to which any of the markers we're * removing should apply. */ remove_end_markers(dstbuf); } /* * Remove end markers from a buffer */ void CTcTokenizer::remove_end_markers(CTcTokString *buf) { char *src; char *dst; utf8_ptr p; /* scan the buffer */ for (src = dst = buf->get_buf(), p.set(src) ; p.getptr() < buf->get_text_end() ; ) { /* check for our flag */ if (p.getch() == TOK_MACRO_EXP_END) { /* skip the flag byte and the following embedded pointer */ src += 1 + sizeof(CTcHashEntryPp *); p.set(src); } else { /* skip this character */ p.inc(); /* copy the bytes of this character as-is */ while (src < p.getptr()) *dst++ = *src++; } } /* set the new buffer size */ buf->set_text_len(dst - buf->get_buf()); } /* * Expand the macro at the current token in the current line. * * 'src' is a pointer to the current position in 'srcbuf'. We'll update * 'src' to point to the next token after macro or its actual parameters * list, if it has one. */ int CTcTokenizer::expand_macro(CTcMacroRsc *rsc, CTcTokString *expbuf, const CTcTokString *srcbuf, utf8_ptr *src, size_t macro_srcbuf_ofs, CTcHashEntryPp *entry, int read_more, int allow_defined, int *expanded) { CTcTokString *subexp; size_t argofs[TOK_MAX_MACRO_ARGS]; size_t arglen[TOK_MAX_MACRO_ARGS]; size_t startofs; const char *start; const char *end; int err; char flagbuf[1 + sizeof(entry)]; /* presume we won't do any expansion */ *expanded = FALSE; /* get our resources */ subexp = &rsc->macro_exp_; /* remember our parsing starting offset */ startofs = src->getptr() - srcbuf->get_text(); /* clear the expansion output buffer */ expbuf->clear_text(); /* if the macro has arguments, scan the actuals */ if (entry->has_args()) { int found_actuals; /* read the macro arguments */ if (parse_macro_actuals(srcbuf, src, entry, argofs, arglen, read_more, &found_actuals)) { err = 1; goto done; } /* * If we found no actuals, then this wasn't really an invocation * of the macro after all - a function-like macro invoked with * no arguments is simply not replaced. Store the original text * in the output buffer and return success. */ if (!found_actuals) { /* copy the original text */ expbuf->copy(srcbuf->get_text() + macro_srcbuf_ofs, startofs - macro_srcbuf_ofs); /* * restore the source read pointer to where it was when we * started */ src->set((char *)srcbuf->get_text() + startofs); /* return success */ err = 0; goto done; } } /* * if there are arguments, replace the macro and substitute actuals * for the formals; otherwise, just copy the replacement text * directly */ if (entry->get_argc() != 0) { /* substitute the actuals */ if (substitute_macro_actuals(rsc, subexp, entry, srcbuf, argofs, arglen, allow_defined)) { err = 1; goto done; } /* set up to parse from the expansion buffer */ start = subexp->get_text(); end = start + subexp->get_text_len(); } else { /* * use our local source buffer that simply references the * original expansion text, rather than making a copy of the * expansion text */ start = entry->get_expansion(); end = start + entry->get_expan_len(); } /* copy the expansion into the output buffer */ expbuf->copy(start, end - start); /* * After the end of the expansion sequence, insert the * fully-expanded flag plus a pointer to the symbol table entry that * we just expanded. This will allow us to detect during the * re-scan of the expansion text that this symbol has already been * expanded, in which case we must suppress further expansion of the * symbol. This allows us to follow the ANSI C rules for recursive * macro usage. */ flagbuf[0] = TOK_MACRO_EXP_END; memcpy(&flagbuf[1], &entry, sizeof(entry)); expbuf->append(flagbuf, sizeof(flagbuf)); /* indicate that we expanded the macro */ *expanded = TRUE; /* success */ err = 0; done: /* return the result */ return err; } /* * Parse a macro's actual parameter list, filling in the given hash * table with the arguments. Returns zero on success, non-zero on * error. 'entry' is the macro's defining symbol table entry. */ int CTcTokenizer::parse_macro_actuals(const CTcTokString *srcbuf, utf8_ptr *src, const CTcHashEntryPp *entry, size_t argofs[TOK_MAX_MACRO_ARGS], size_t arglen[TOK_MAX_MACRO_ARGS], int read_more, int *found_actuals) { tc_toktyp_t typ; CTcToken tok; int argc; int spliced; int i; /* presume we're not going to do any line splicing */ spliced = FALSE; /* no arguments parsed yet */ argc = 0; /* get the next token after the macro symbol */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* splice another line if necessary */ if (typ == TOKT_EOF && read_more) { /* splice a line */ typ = actual_splice_next_line(srcbuf, src, &tok); /* note the splice */ spliced = TRUE; } /* if we didn't find an open paren, there's no actual list after all */ if (typ != TOKT_LPAR) { /* tell the caller we didn't find any actuals */ *found_actuals = FALSE; /* if we spliced a line, unsplice it at the current token */ if (spliced) unsplice_line(tok.get_text()); /* return success */ return 0; } /* remember the offset of the start of the first argument */ argofs[argc] = tok.get_text() + tok.get_text_len() - srcbuf->get_text(); /* skip the open paren */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* read the arguments */ while (typ != TOKT_RPAR) { utf8_ptr p; int paren_depth, bracket_depth, brace_depth; int sp_cnt; /* if we have too many arguments, it's an error */ if ((argc >= entry->get_argc() && !entry->has_varargs()) || argc >= TOK_MAX_MACRO_ARGS) { /* log the error */ log_error(TCERR_PP_MANY_MACRO_ARGS, (int)entry->getlen(), entry->getstr()); /* scan ahead to to close paren or end of line */ while (typ != TOKT_RPAR && typ != TOKT_EOF) typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* done scanning arguments */ break; } /* * Skip tokens until we find the end of the argument. An argument * ends at: * * - a comma outside of nested parens, square brackets, or curly * braces * * - a close paren that doesn't match an open paren found earlier * in the same argument */ paren_depth = bracket_depth = brace_depth = 0; for (;;) { /* * If it's a comma, and we're not in any sort of nested * brackets (parens, square brackets, or curly braces), the * comma ends the argument. A comma within any type of * brackets is part of the argument text. */ if (typ == TOKT_COMMA && paren_depth == 0 && brace_depth == 0 && bracket_depth == 0) break; /* * If it's a close paren, and it doesn't match an earlier open * paren in the same argument, it's the end of the argument. */ if (typ == TOKT_RPAR && paren_depth == 0) break; /* * if it's an open or close paren, brace, or bracket, adjust * the depth accordingly */ switch(typ) { case TOKT_LPAR: ++paren_depth; break; case TOKT_RPAR: --paren_depth; break; case TOKT_LBRACE: ++brace_depth; break; case TOKT_RBRACE: --brace_depth; break; case TOKT_LBRACK: ++bracket_depth; break; case TOKT_RBRACK: --bracket_depth; break; default: break; } /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); /* * if we're at the end of the line, and we're allowed to * read more, splice the next line onto the current line */ if (typ == TOKT_EOF && read_more) { /* splice a line */ typ = actual_splice_next_line(srcbuf, src, &tok); /* note that we've done some line splicing */ spliced = TRUE; } /* if we've reached the end of the file, stop */ if (typ == TOKT_EOF) break; } /* if we've reached the end of the file, stop */ if (typ == TOKT_EOF) break; /* remove any trailing whitespace from the actual's text */ sp_cnt = 0; p.set((char *)tok.get_text()); while (p.getptr() > srcbuf->get_text() + argofs[argc]) { wchar_t ch; /* move to the prior character */ p.dec(); /* if it's not a space, stop looking */ ch = p.getch(); if (!is_space(ch)) { /* * advance past this character so that we keep it in the * expansion */ p.inc(); /* * if this last character was a backslash, and we removed * at least one space following it, keep the one space * that immediately follows the backslash, since that * space is part of the backslash's two-character escape * sequence */ if (ch == '\\' && sp_cnt != 0) p.inc(); /* stop scanning */ break; } /* that's one more trailing space we've removed - count it */ ++sp_cnt; } /* note the argument length */ arglen[argc] = (p.getptr() - srcbuf->get_text()) - argofs[argc]; /* count the argument */ ++argc; /* check for another argument */ if (typ == TOKT_COMMA) { /* remember the offset of the start of this argument */ argofs[argc] = tok.get_text() + tok.get_text_len() - srcbuf->get_text(); /* skip the comma and go back for another argument */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, TRUE); } else if (typ == TOKT_RPAR) { /* * No need to look any further. Note that we don't want to * get another token, since we're done parsing the input * now, and we want to leave the token stream positioned for * the caller just after the extent of the macro, which, in * the case of this function-like macro, ends with the * closing paren. */ break; } } /* if we didn't find the right paren, flag the error */ if (typ != TOKT_RPAR) { log_error(read_more ? TCERR_PP_MACRO_ARG_RPAR : TCERR_PP_MACRO_ARG_RPAR_1LINE, (int)entry->getlen(), entry->getstr()); return 1; } /* remove leading and trailing whitespace from each argument */ for (i = 0 ; i < argc ; ++i) { const char *start; const char *end; utf8_ptr p; size_t del_len; int sp_cnt; /* figure the limits of the argument text */ start = srcbuf->get_text() + argofs[i]; end = start + arglen[i]; /* remove leading whitespace */ for (p.set((char *)start) ; p.getptr() < end && is_space(p.getch()) ; p.inc()) ; /* set the new offset and length */ del_len = p.getptr() - start; argofs[i] += del_len; arglen[i] -= del_len; start += del_len; /* remove trailing whitespace */ p.set((char *)end); sp_cnt = 0; while (p.getptr() > start) { wchar_t ch; /* go to the prior character */ p.dec(); /* if it's not whitespace, keep it */ ch = p.getch(); if (!is_space(ch)) { /* put the character back */ p.inc(); /* * if this is a backslash, and a space follows, keep the * immediately following space, since it's part of the * backslash sequence */ if (ch == '\\' && sp_cnt != 0) p.inc(); /* we're done scanning */ break; } /* count another removed trailing space */ ++sp_cnt; } /* adjust the length */ arglen[i] -= (end - p.getptr()); } /* * if we did any line splicing, cut off the rest of the line and * push it back into the logical input stream as a new line - this * will allow better error message positioning if errors occur in * the remainder of the line, since this means we'll only * artificially join onto one line the part of the new line that * contained the macro parameters */ if (spliced) unsplice_line(tok.get_text() + tok.get_text_len()); /* make sure we found enough arguments */ if (argc < entry->get_min_argc()) { /* fill in the remaining arguments with empty strings */ for ( ; argc < entry->get_argc() ; ++argc) { argofs[argc] = 0; arglen[argc] = 0; } /* note the error, but proceed with empty arguments */ log_warning(TCERR_PP_FEW_MACRO_ARGS, (int)entry->getlen(), entry->getstr()); } /* * if we have varargs, always supply an empty marker for the last * argument */ if (entry->has_varargs() && argc < TOK_MAX_MACRO_ARGS) { argofs[argc] = 0; arglen[argc] = 0; } /* success - we found an actual parameter list */ *found_actuals = TRUE; return 0; } /* * Splice a line for macro actual parameters. Sets the source pointer * to the start of the new line. Reads the first token on the spliced * line and returns it. * * We will splice new lines until we find a non-empty line or reach the * end of the input. If this returns EOF, it indicates that we've * reached the end of the entire input. */ tc_toktyp_t CTcTokenizer::actual_splice_next_line( const CTcTokString *srcbuf, utf8_ptr *src, CTcToken *tok) { /* add a space onto the end of the current line */ linebuf_.append(" ", 1); /* keep going until we find a non-empty line */ for (;;) { int new_line_ofs; tc_toktyp_t typ; /* splice the next line onto the current line */ new_line_ofs = read_line(TRUE); /* * make sure we read additional lines as needed to complete any * strings left open at the end of the line */ if (in_quote_ != '\0') splice_string(); /* if there was no more, return end of file */ if (new_line_ofs == -1) return TOKT_EOF; /* set the source to the start of the additional line */ src->set((char *)linebuf_.get_text() + new_line_ofs); /* get the next token */ typ = next_on_line(srcbuf, src, tok, ¯o_in_embedding_, TRUE); /* if we didn't get EOF, it means we found a non-empty line */ if (typ != TOKT_EOF) return typ; } } /* * Substitute the actual parameters in a macro's expansion */ int CTcTokenizer::substitute_macro_actuals( CTcMacroRsc *rsc, CTcTokString *subexp, CTcHashEntryPp *entry, const CTcTokString *srcbuf, const size_t *argofs, const size_t *arglen, int allow_defined) { const char *start; utf8_ptr expsrc; CTcToken prvtok; CTcToken prvprvtok; CTcToken tok; tc_toktyp_t typ; CTcTokString *actual_exp_buf; const size_t expand_max = 10; static struct expand_info_t { /* type of expansion (#foreach, #ifempty, #ifnempty) */ tc_toktyp_t typ; /* * flag: this is an iterator type (if this is true, the varargs * formal should be expanded to the current argument given by our * 'arg' member; if this is false, the varargs formal should be * expanded as the full varargs list) */ int is_iterator; /* the marker character that delimits the foreach arguments */ wchar_t delim; /* location of start of expansion region for foreach */ utf8_ptr start; /* current argument index */ int arg; /* the current expansion part (0 = first part, etc) */ int part; } expand_stack[expand_max], *expand_sp; /* get the actual expansion buffer from the resource object */ actual_exp_buf = &rsc->actual_exp_buf_; /* * Scan the replacement text for formals, and replace each formal * with the actual. Set up a pointer at the start of the expansion * text. */ start = entry->get_expansion(); expsrc.set((char *)start); /* we don't yet have a previous token */ prvtok.settyp(TOKT_EOF); prvprvtok.settyp(TOKT_EOF); /* clear the expansion buffer */ subexp->clear_text(); /* we have no #foreach/#ifempty/#ifnempty stack yet */ expand_sp = expand_stack; /* scan the tokens in the expansion text */ for (typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE) ; typ != TOKT_EOF ; ) { /* * check to see if we've reached the end of a * #foreach/#ifempty/#ifnempty */ if (expand_sp != expand_stack) { /* check to see if we're at the delimiter */ if (utf8_ptr::s_getch(tok.get_text()) == (expand_sp-1)->delim) { /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* go back to the start of the token */ expsrc.set((char *)tok.get_text()); /* see what kind of token we're expanding */ switch((expand_sp-1)->typ) { case TOKT_MACRO_FOREACH: /* it's a #foreach - process the appropriate part */ switch ((expand_sp-1)->part) { case 0: /* * We've been doing the first part, which is the * main expansion per actual. This delimiter thus * introduces the 'between' portion, which we copy * between each iteration, but not after the last * iteration. So, if we've just done the last * actual, skip this part entirely; otherwise, * keep going, using this part. */ if (argofs[(expand_sp-1)->arg + 1] == 0) { /* skip this one remaining part */ skip_delimited_group(&expsrc, 1); /* we're finished with the iteration */ goto end_foreach; } else { /* * we have more arguments, so we want to * expand this part - skip the deliter and * keep going */ expsrc.inc(); /* we're now in the next part of the iterator */ (expand_sp-1)->part++; } break; case 1: /* * We've reached the end of the entire #foreach * string, so we're done with this iteration. * Skip the delimiter. */ expsrc.inc(); end_foreach: /* * if we have more arguments, start over with the * next iteration; otherwise, pop the #foreach * level */ if (argofs[(expand_sp-1)->arg + 1] == 0) { /* no more arguments - pop the #foreach level */ --expand_sp; } else { /* we have more arguments - move to the next */ (expand_sp-1)->arg++; /* go back to the start of the expansion */ expsrc = (expand_sp-1)->start; /* we have no previous token for pasting ops */ prvtok.settyp(TOKT_EOF); prvprvtok.settyp(TOKT_EOF); /* we're back in the first part of the iterator */ (expand_sp-1)->part = 0; } break; } break; case TOKT_MACRO_IFEMPTY: case TOKT_MACRO_IFNEMPTY: /* * #ifempty or #ifnempty - we've reached the end of * the conditional text, so simply pop a level and * keep going after the delimiter */ /* skip the delimiter */ expsrc.inc(); /* pop a level */ --expand_sp; /* done */ break; default: break; } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } } /* if it's a #foreach marker, start a #foreach iteration */ if (typ == TOKT_MACRO_FOREACH && entry->has_varargs()) { /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* push a #foreach level, if possible */ if (expand_sp - expand_stack >= expand_max) { /* * we can't create another level - log an error and ignore * this new level */ log_error(TCERR_PP_FOREACH_TOO_DEEP); } else if (argofs[entry->get_argc() - 1] == 0) { /* * we have no actuals for the variable part of the * formals, so we must iterate zero times through the * #foreach part - in other words, simply skip ahead to * the end of the #foreach */ skip_delimited_group(&expsrc, 2); } else { /* remember and skip the marker character */ expand_sp->delim = expsrc.getch(); expsrc.inc(); /* set the expansion type */ expand_sp->typ = typ; /* * remember the position where the #foreach started, since * we need to come back here for each use of the variable */ expand_sp->start = expsrc; /* we're an iterator type */ expand_sp->is_iterator = TRUE; /* * Start at the first argument in the variable part of the * argument list. The last formal corresponds to the * first variable argument. */ expand_sp->arg = entry->get_argc() - 1; /* we're in the main expansion part of the expression */ expand_sp->part = 0; /* push the new level */ ++expand_sp; } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a varargs #ifempty or #ifnempty flag, expand it */ if ((typ == TOKT_MACRO_IFEMPTY || typ == TOKT_MACRO_IFNEMPTY) && entry->has_varargs()) { int is_empty; int expand; /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* determine if the varargs list is empty or not */ is_empty = (argofs[entry->get_argc() - 1] == 0); /* * decide whether or not expand it, according to the empty * state and the flag type */ expand = ((is_empty && typ == TOKT_MACRO_IFEMPTY) || (!is_empty && typ == TOKT_MACRO_IFNEMPTY)); /* * if we're going to expand it, push a level; otherwise, just * skip the entire expansion */ if (expand) { /* make sure we have room for another level */ if (expand_sp - expand_stack >= expand_max) { /* no room - log an error and ignore the new level */ log_error(TCERR_PP_FOREACH_TOO_DEEP); } else { /* remember and skip the delimiter */ expand_sp->delim = expsrc.getch(); expsrc.inc(); /* * we're not an iterator type, so inherit the * enclosing level's meaning of the varargs formal */ if (expand_sp - expand_stack == 0) { /* outermost level - use the whole varargs list */ expand_sp->is_iterator = FALSE; } else { /* use the enclosing level's meaning */ expand_sp->is_iterator = (expand_sp-1)->is_iterator; expand_sp->arg = (expand_sp-1)->arg; } /* set the expansion type */ expand_sp->typ = typ; /* push the new level */ ++expand_sp; } } else { /* not expanding - just skip the entire expansion */ skip_delimited_group(&expsrc, 1); } /* the next chunk starts here */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a varargs #argcount indicator, expand it */ if (typ == TOKT_MACRO_ARGCOUNT && entry->has_varargs()) { char buf[20]; int i; /* copy the prior expansion so far */ if (tok.get_text() > start) subexp->append(start, tok.get_text() - start); /* * count the number of arguments after and including the * variable argument placeholder */ for (i = entry->get_argc() - 1 ; argofs[i] != 0 ; ++i) ; /* make a string out of the variable argument count */ sprintf(buf, "%d", i - (entry->get_argc() - 1)); /* add the argument count to the output buffer */ subexp->append(buf, strlen(buf)); /* the next chunk starts after the #argcount */ start = expsrc.getptr(); /* get the next token */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* we have the next token, so back and process it */ continue; } /* if it's a symbol, check for an actual */ if (typ == TOKT_MACRO_FORMAL) { const char *p; int argnum; size_t argnum_len; int pasting; int pasting_at_left, pasting_at_right; int stringize; char stringize_qu; tc_toktyp_t stringize_type; CTcToken paste_at_right_tok; /* assume we'll copy up to the start of this token */ p = tok.get_text(); /* * get the index of the actual in the argument vector -- * this is given by the second byte of the special macro * parameter flag token */ argnum = (int)(uchar)tok.get_text()[1] - 1; /* * If we have varargs, and this is the varargs argument, and * the current #foreach stack level indicates that we're * iterating through the varargs list, treat this as a * reference to the current argument in the iteration. */ if (expand_sp != expand_stack && argnum == entry->get_argc() - 1 && (expand_sp-1)->is_iterator) { /* * we're on a #foreach iterator, and this is the varargs * formal - use the current #foreach iteration element * instead */ argnum = (expand_sp-1)->arg; } /* * Get the length of this argument. If we have varargs, and * this is the last formal, which is the placeholder for the * variable argument list, and we're not in a #foreach * iterator, the value is the value of the entire string of * variable arguments, including the commas. */ if (expand_sp == expand_stack && entry->has_varargs() && argnum == entry->get_argc() - 1) { int i; /* * It's the full varargs list - use the length from the * first varargs argument to the last. Find the last * argument. */ for (i = argnum ; i < TOK_MAX_MACRO_ARGS && argofs[i] != 0 ; ++i) ; /* * The full list length is the distance from the offset of * the first to the end of the last. If there are no * varargs arguments at all, the length is zero. */ if (i == argnum) argnum_len = 0; else argnum_len = argofs[i-1] + arglen[i-1] - argofs[argnum]; } else { /* * it's not the full varargs list, so just use the length * of this single actual */ argnum_len = arglen[argnum]; } /* assume we won't do any token pasting or stringizing */ pasting = pasting_at_left = pasting_at_right = FALSE; stringize = FALSE; /* * if the previous token was a token-pasting operator, * remove it and any preceding whitespace from the source * material, since we want to append the actual parameter * text directly after the preceding token */ check_paste_left: if (prvtok.gettyp() == TOKT_POUNDPOUND) { wchar_t prv_ch; /* * note that we have token pasting - we're pasting * something to the left of this token (since we had a * "##" before this token */ pasting = TRUE; pasting_at_left = TRUE; /* go back to the ## token */ p = prvtok.get_text(); /* remove any preceding whitespace */ for (prv_ch = 0 ; p > start ; ) { const char *prvp; /* get the previous character */ prvp = utf8_ptr::s_dec((char *)p); prv_ch = utf8_ptr::s_getch((char *)prvp); /* if it's not a space, we're done */ if (!is_space(prv_ch)) break; /* move back over this character */ p = prvp; } /* * Weird special case: if the previous character was a * comma, and the formal we're pasting is a variable * argument formal (i.e., the last formal in a varargs * macro), and the varargs list is empty, then remove the * comma. This is a handy shorthand notation that allows * the varargs list to be added to a comma-delimited list, * such as a function call's actuals or the contents of a * list. */ if (prv_ch == ',' && entry->has_varargs() && argnum == entry->get_argc() - 1 && argofs[argnum] == 0) { /* * it's the special case - move back one more * character to delete the comma */ p = utf8_ptr::s_dec((char *)p); } } else if (prvtok.gettyp() == TOKT_POUND || prvtok.gettyp() == TOKT_POUNDAT) { /* go back to the # token */ p = prvtok.get_text(); /* note that we have stringizing */ stringize = TRUE; stringize_type = prvtok.gettyp(); stringize_qu = (prvtok.gettyp() == TOKT_POUND ? '"' : '\''); /* go back one more token */ prvtok = prvprvtok; prvprvtok.settyp(TOKT_EOF); /* * go back and check for pasting again, since we could * be pasting to a stringized token */ goto check_paste_left; } /* copy the prior expansion so far */ if (p > start) subexp->append(start, p - start); /* remember the symbol as the previous token */ prvprvtok = prvtok; prvtok = tok; /* get the next token after the formal */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* * If it's followed by a token-pasting operator, we need to * paste the next token directly onto the end of the text we * just added to the buffer, skipping any intervening * whitespace; otherwise, we want to start adding again at * the next character after the original token. */ if (typ == TOKT_POUNDPOUND) { utf8_ptr old_expsrc; CTcToken old_tok; /* note that we have pasting to the right of this token */ pasting = TRUE; pasting_at_right = TRUE; /* remember where we started */ old_expsrc = expsrc; /* remember the current token for a moment */ old_tok = tok; /* skip to the next token after the ## */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* remember the token we're pasting to the right */ paste_at_right_tok = tok; /* check for pasting to a stringizer */ if (stringize && typ == stringize_type) { /* * leave the ## in the stream for now - we'll fix it * up when we stringize the next token, rather than * doing so now */ expsrc = old_expsrc; tok = old_tok; } else { /* * remember that we have a token-pasting operator, * so that we can tell that we're pasting when we * look at the next token */ prvprvtok = prvtok; prvtok = old_tok; } /* start next text from here */ start = tok.get_text(); } else { /* Start at the end of the symbol token */ start = prvtok.get_text() + prvtok.get_text_len(); } /* * If we're not doing any pasting, recursively expand macros * in the actual expansion text. If we're pasting, do not * expand any macros in the expansion, since we want to do * the pasting before we do any expanding. */ if (pasting && stringize) { int add_open; int add_close; /* presume we'll include the open and close quotes */ add_close = TRUE; add_open = TRUE; /* * If we're pasting to the left, and the buffer so far * ends in the same quote we're adding to this token, * combine the strings by removing the preceding quote * and not adding the open quote on the new string */ if (subexp->get_text_len() > 0 && *(subexp->get_text_end() - 1) == stringize_qu) { /* remove the close quote from the expansion so far */ subexp->set_text_len(subexp->get_text_len() - 1); /* don't add the open quote to the new string */ add_open = FALSE; } /* * If we're pasting to the right, and we have a string * of the same type following, or we will be pasting a * stringizing pair, paste the two strings together to * form one string by removing the close quote from this * string and the open quote from the next string */ if (pasting_at_right && *tok.get_text() == stringize_qu) add_close = FALSE; /* * We're both stringizing this argument and pasting * another token - first stringize the actual. */ stringize_macro_actual(subexp, srcbuf->get_text() + argofs[argnum], argnum_len, stringize_qu, add_open, add_close); /* * if we decided to remove the closing quote, we want to * remove the open quote from the following string as * well - copy in the following string without its open * quote */ if (!add_close) { /* * append the following token without its first * character (its open quote) */ subexp->append(tok.get_text() + 1, tok.get_text_len() - 1); /* move on to the next token */ prvprvtok = prvtok; prvtok = tok; typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); /* start from the new token */ start = tok.get_text(); } } else if (pasting) { const char *argp; size_t len; int done; wchar_t quote_char; /* get the actual argument information */ argp = srcbuf->get_text() + argofs[argnum]; len = argnum_len; /* * if we're pasting to the left of this token, and the * token starts with a fully-expanded flag, remove the * flag - we're making up a new token out of this and * what comes before, so the token that we fully * expanded is disappearing, so the fully-expanded * status no longer applies */ if (pasting_at_left && *argp == TOK_FULLY_EXPANDED_FLAG) { /* skip the flag */ ++argp; --len; } /* presume we won't find any quoted strings */ quote_char = 0; /* * check for string concatenation to the left - if we're * concatenating two strings of the same type, remove * the adjacent quotes to make it a single string */ if (pasting_at_left && subexp->get_text_len() > 0 && (*argp == '\'' || *argp == '"') && *(subexp->get_text_end() - 1) == *argp) { /* remove the close quote from the expansion so far */ subexp->set_text_len(subexp->get_text_len() - 1); /* remember the quote character */ quote_char = *argp; /* don't add the open quote to the new string */ ++argp; --len; } /* presume we won't have to do anything special */ done = FALSE; /* * If we're pasting at the right, also remove any * fully-expanded flag just before the last token in the * expansion. */ if (pasting_at_right) { CTcToken old_tok; CTcToken tok; utf8_ptr p; /* scan for the final token in the expansion string */ p.set((char *)argp); old_tok.settyp(TOKT_INVALID); while (p.getptr() < argp + len) { /* * get another token - stop at EOF or if we go * past the bounds of the expansion text */ if (next_on_line(&p, &tok, ¯o_in_embedding_, TRUE) == TOKT_EOF || tok.get_text() >= argp + len) break; /* remember the previous token */ old_tok = tok; } /* * if the final token is a symbol, and it has the * fully-expanded flag, we must omit the flag from * the appended text */ if (old_tok.gettyp() == TOKT_SYM && old_tok.get_fully_expanded()) { /* * append up to but not including the flag byte * preceding the final token */ subexp->append(argp, tok.get_text() - 1 - argp); /* * append from the last token to the end of the * expansion, skipping the flag byte */ subexp->append(tok.get_text(), len - (tok.get_text() - argp)); /* we've done the appending */ done = TRUE; } else if (quote_char != 0 && paste_at_right_tok.get_text_len() != 0 && *paste_at_right_tok.get_text() == quote_char) { /* * we're pasting two strings together - append * up to but not including the close quote */ subexp->append(argp, len - 1); /* * append the next token, but do not include the * open quote */ subexp->append(paste_at_right_tok.get_text() + 1, paste_at_right_tok.get_text_len() - 1); /* * restart after the right token, since we've * now fully processed that token */ start = paste_at_right_tok.get_text() + paste_at_right_tok.get_text_len(); /* we're done */ done = TRUE; } } /* * append the actual without expansion, if we haven't * already handled it specially */ if (!done) subexp->append(argp, len); } else if (stringize) { /* stringize the actual */ stringize_macro_actual(subexp, srcbuf->get_text() + argofs[argnum], argnum_len, stringize_qu, TRUE, TRUE); } else { CTcTokStringRef actual_src_buf; /* recursively expand macros in the actual text */ actual_src_buf. set_buffer(srcbuf->get_text() + argofs[argnum], argnum_len); if (expand_macros(&actual_src_buf, 0, actual_exp_buf, FALSE, allow_defined, FALSE)) return 1; /* * Append the expanded actual, marking any * fully-expanded tokens as such and removing * end-of-expansion markers. * * We can't leave end-of-expansion markers in the * expanded actual text, because end-of-expansion * markers apply only to the current recursion level, * and we've now exited the actual's recursion level. * However, we must not expand further anything in the * actual's expansion that has already been fully * expanded. To achieve both of these goals, we switch * here from marking the run of text (with the end * marker) to marking individual tokens. */ mark_full_exp_tokens(subexp, actual_exp_buf, TRUE); } /* we've already read the next token, so proceed */ continue; } /* remember the current token as the previous token */ prvprvtok = prvtok; prvtok = tok; /* get the next token of the expansion */ typ = next_on_line(&expsrc, &tok, ¯o_in_embedding_, TRUE); } /* copy the remaining replacement text */ subexp->append(start, tok.get_text() - start); /* success */ return 0; } /* * Skip the source of a delimited macro expansion area (#foreach, * #ifempty, #ifnempty). */ void CTcTokenizer::skip_delimited_group(utf8_ptr *p, int parts_to_skip) { wchar_t delim; /* get the delimiter character */ delim = p->getch(); /* * if the delimiter put us at the end of the line, there's nothing to * skip */ if (delim == 0 || delim == TOK_END_PP_LINE) return; /* skip the delimiter */ p->inc(); /* keep going until we've skipped the desired number of parts */ while (parts_to_skip != 0) { wchar_t ch; /* read the next character */ ch = p->getch(); /* if it's the end of the line, give up */ if (ch == 0 || ch == TOK_END_PP_LINE) { /* * we ran out of input before reaching the delimiter, so this * is implicitly the end of it */ return; } /* check what we have */ if (ch == delim) { /* that's one less part to skip */ --parts_to_skip; /* skip it */ p->inc(); } else if (ch == TOK_MACRO_FOREACH_FLAG) { /* it's a nested #foreach - skip all of its parts */ skip_delimited_group(p, 2); } else if (ch == TOK_MACRO_IFEMPTY_FLAG || ch == TOK_MACRO_IFNEMPTY_FLAG) { /* nested #ifempty or #ifnempty - skip its expansion */ skip_delimited_group(p, 1); } else { /* it's nothing special to us - skip it */ p->inc(); } } } /* * Stringize a macro actual parameter value into a macro expansion * buffer */ void CTcTokenizer::stringize_macro_actual(CTcTokString *expbuf, const char *actual_val, size_t actual_len, char quote_char, int add_open_quote, int add_close_quote) { utf8_ptr src; const char *start; int in_inner_quote; wchar_t inner_quote_char; wchar_t prvch; /* add the open quote if desired */ if (add_open_quote) expbuf->append("e_char, 1); /* remember the start of the current segment */ start = actual_val; /* * add the characters of the actual parameter value, quoting any * quotes or backslashes */ for (src.set((char *)actual_val), in_inner_quote = FALSE, inner_quote_char = '\0', prvch = '\0' ; src.getptr() < actual_val + actual_len ; ) { wchar_t cur; /* get this character */ cur = src.getch(); /* compress runs of whitespace to single spaces */ if (is_space(cur) && prvch != '\\') { /* append up to this character */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* find the next non-space character */ for ( ; src.getptr() < actual_val + actual_len ; src.inc()) { if (!is_space(src.getch())) break; } /* * if we're not at the start or end of the string, add a * single space to replace the entire run of whitespace -- * don't do this at the start or end of the string, since * we must remove leading and trailing whitespace */ if (prvch != '\0' && src.getptr() < actual_val + actual_len) expbuf->append(" ", 1); /* note that the previous character is a space */ prvch = cur; /* this is the new starting point */ start = src.getptr(); /* proceed - we're already at the next character */ continue; } /* * Check to see if we need to quote this character. Quote any * quote mark matching the enclosing quotes; also quote any * backslash that occurs within nested quotes within the source * material, but not backslashes that occur originally outside * quotes. */ if (cur == quote_char || (cur == '\\' && in_inner_quote)) { /* append the segment up to (but not including) this character */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* add an extra backslash */ expbuf->append("\\", 1); /* remember the start of the next segment */ start = src.getptr(); } /* * if this is a quote character, and it's not itself escaped, * reverse our in-quote flag */ if (prvch != '\\') { /* * If we're in an inner quote, and it's a match for the open * inner quote, we're no longer in a quote. Otherwise, if * we're not in quotes and this is some kind of quote, enter * the new quotes. */ if (in_inner_quote && cur == inner_quote_char) { /* we're leaving the inner quoted string */ in_inner_quote = FALSE; } else if (!in_inner_quote && (cur == '"' || cur == '\'')) { /* we're entering a new inner quoted string */ in_inner_quote = TRUE; inner_quote_char = cur; } } /* remember this as the previous character */ prvch = cur; /* move on to the next character */ src.inc(); } /* if there's anything in the final segment, append it */ if (src.getptr() > start) expbuf->append(start, src.getptr() - start); /* add the close quote if desired */ if (add_close_quote) expbuf->append("e_char, 1); } /* * Expand a "defined" preprocessor operator */ int CTcTokenizer::expand_defined(CTcTokString *subexp, const CTcTokString *srcbuf, utf8_ptr *src) { CTcToken tok; tc_toktyp_t typ; int paren; int found; /* get the next token */ typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE); /* note whether we have an open paren; if we do, skip it */ paren = (typ == TOKT_LPAR); if (paren) typ = next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE); /* get the symbol */ if (typ != TOKT_SYM) { log_error(TCERR_PP_DEFINED_NO_SYM, (int)tok.get_text_len(), tok.get_text()); return 1; } /* look to see if the symbol is defined */ found = (find_define(tok.get_text(), tok.get_text_len()) != 0); /* expand the macro to "1" if found, "0" if not */ subexp->copy(found ? "1" : "0", 1); /* check for and skip the matching close paren */ if (paren) { /* require the closing paren */ if (next_on_line(srcbuf, src, &tok, ¯o_in_embedding_, FALSE) != TOKT_RPAR) { /* generate an error if we don't find it */ log_error(TCERR_PP_DEFINED_RPAR); return 1; } } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Process comments. Replaces each character of a comment with a space. */ void CTcTokenizer::process_comments(size_t start_ofs) { utf8_ptr src; utf8_ptr dst; /* we haven't found a backslash followed by trailing space yet */ int trailing_sp_after_bs = FALSE; /* * Scan the line. When inside a comment, replace each character of * the comment with a space. When outside comments, simply copy * characters intact. * * Note that we need a separate src and dst pointer, because the * character length of the original and replaced characters may * change. Fortunately, the length will never do anything but * shrink or stay the same, since the only change we make is to * insert spaces, which are always one byte apiece in UTF-8; we can * therefore update the buffer in place. */ for (src.set(linebuf_.get_buf() + start_ofs), dst.set(linebuf_.get_buf() + start_ofs) ; src.getch() != '\0' ; src.inc()) { /* get the current character */ wchar_t cur = src.getch(); /* check to see if we're in a comment */ if (str_->is_in_comment()) { /* * check to see if the comment is ending, or if we have an * apparent nested comment (which isn't allowed) */ if (cur == '*' && src.getch_at(1) == '/') { /* * skip an extra character of the source - we'll skip * one in the main loop, so we only need to skip one * more now */ src.inc(); /* we're no longer in a comment */ str_->set_in_comment(FALSE); } else if (cur == '/' && src.getch_at(1) == '*') { /* looks like a nested comment - warn about it */ if (!G_prs->get_syntax_only()) log_warning(TCERR_NESTED_COMMENT); } /* continue without copying anything from inside the comment */ continue; } else if (in_quote_ != '\0') { /* see what we have */ if (cur == '\\') { /* * It's a backslash sequence -- copy the backslash to * the output, and skip it. Note that we don't have to * worry about the line ending with a backslash, since * the line reader will already have considered that to * be a line splice. */ src.inc(); dst.setch(cur); /* get the next character, so we copy it directly */ cur = src.getch(); /* * if we're in a triple-quoted string, and this is an * escaped quote, the backslash escapes a whole run of * consecutive quotes that follow */ if (in_triple_ && cur == in_quote_) { /* copy and skip any run of quotes, minus the last one */ for (int qcnt = count_quotes(&src, cur) ; qcnt > 1 ; dst.setch(cur), src.inc(), --qcnt) ; } } else if (cur == in_quote_) { /* This is the close quote. Check for triple quotes. */ if (in_triple_) { /* triple-quoted - we need three quotes in a row */ int qcnt = count_quotes(&src, cur); if (qcnt >= 3) { /* copy and skip all but the last in the run */ for (int i = 1 ; i < qcnt ; ++i, src.inc()) dst.setch(cur); /* close the string */ in_quote_ = '\0'; } } else { /* regular string, so it ends at the matching quote */ in_quote_ = '\0'; } } else if (cur == '<' && src.getch_at(1) == '<') { /* * it's an embedded expression starting point - skip the * first of the '<' characters (the enclosing loop will * skip the second one) */ src.inc(); /* we're in an embedding now */ comment_in_embedding_.start_expr(in_quote_, in_triple_, FALSE); /* the string is done for now */ in_quote_ = '\0'; /* copy the extra '<' to the output */ dst.setch('<'); } } else { /* * Monitor the stream for a backslash followed by trailing * spaces. If this is a backslash, note that we might have a * backslash with trailing spaces; if it's a space, we might * still have this, so leave the flag alone; if it's anything * else, clear the flag, since we've found something other * than backslashes and spaces. */ if (cur == '\\') trailing_sp_after_bs = TRUE; else if (!is_space(cur)) trailing_sp_after_bs = FALSE; /* check to see if we're starting a comment */ if (cur == '/') { switch(src.getch_at(1)) { case '*': /* note that we're starting a comment */ str_->set_in_comment(TRUE); /* * replace the starting slash with a space - this * will effectively replace the entire comment with * a single space, since we won't copy anything else * from inside the comment */ cur = ' '; break; case '/': /* * comment to end of line - we can terminate the * line at the opening slash and return immediately, * because the entire rest of the line is to be * ignored */ dst.setch('\0'); return; default: /* not a comment - copy it as-is */ break; } } else if (cur == '"' || cur == '\'') { /* it's the start of a new string */ in_quote_ = cur; /* check for triple quotes */ in_triple_ = (count_quotes(&src, cur) >= 3); /* if in triple quotes, copy and skip the extra two qutoes */ if (in_triple_) { src.inc_by(2); dst.setch(cur); dst.setch(cur); } } else if (cur < 0x09) { /* * it's a special flag character - we need to guarantee * that this character never occurs in input (it * shouldn't anyway, since it's a control character), so * translate it to a space */ cur = ' '; } else if (comment_in_embedding_.in_expr() && (cur == '(' || cur == ')')) { /* adjust the paren level in an embedded expression */ comment_in_embedding_.parens(cur == '(' ? 1 : -1); } else if (comment_in_embedding_.in_expr() && comment_in_embedding_.parens() == 0 && cur == '>' && src.getch_at(1) == '>') { /* it's the end of an embedded expression */ in_quote_ = comment_in_embedding_.qu(); in_triple_ = comment_in_embedding_.triple(); comment_in_embedding_.end_expr(); /* skip the extra '>' and copy it to the output */ src.inc(); dst.setch('>'); } } /* set the current character in the output */ dst.setch(cur); } /* set the updated line buffer length */ linebuf_.set_text_len(dst.getptr() - linebuf_.get_buf()); /* * if we found a backslash with nothing following but whitespace, flag * a warning, since they might have meant the backslash as a line * continuation signal, but we're not interpreting it that way because * of the trailing whitespace */ if (trailing_sp_after_bs) log_warning(TCERR_TRAILING_SP_AFTER_BS); } /* * Splice strings. Splice additional lines onto the current line until * we find the end of the string. */ void CTcTokenizer::splice_string() { /* presume we'll find proper termination */ char unterm = '\0'; /* * remember the current in-quote and in-embedding status, as of the * end of the current line - when we splice, the line reader will * update these to the status at the end of the newly-read material, * but we want to scan from the beginning of the newly-read material */ wchar_t in_quote = in_quote_; int in_triple = in_triple_; tok_embed_ctx old_ec = comment_in_embedding_; /* note if the previous line ended with an explicit \n sequence */ int explicit_nl = (linebuf_.get_text_len() >= 2 && memcmp(linebuf_.get_text_end() - 2, "\\n", 2) == 0); /* keep going until we find the end of the string */ utf8_ptr p((char *)0); for (;;) { /* apply the newline spacing mode */ switch (string_newline_spacing_) { case NEWLINE_SPACING_COLLAPSE: /* * Replace the newline and any subsequent run of whitespace * characters with a single space, unless the last line ended * with an explicit newline. */ if (!explicit_nl) linebuf_.append(" ", 1); break; case NEWLINE_SPACING_DELETE: /* delete the newline and subsequent whitespace */ break; case NEWLINE_SPACING_PRESERVE: /* * preserve the newline and subsequent whitespace exactly as * written in the source code, so convert the newline to an * explicit \n sequence (in source form, since we're building a * source line at this point) */ linebuf_.append("\\n", 2); break; } /* splice another line */ int new_line_ofs = read_line(TRUE); /* if we reached end of file, there's no more splicing we can do */ if (new_line_ofs == -1) break; /* get a pointer to the new text */ char *new_line_p = (char *)linebuf_.get_text() + new_line_ofs; p.set(new_line_p); /* * If we're in COLLAPSE or DELETE mode for newline spacing, skip * leading spaces in the new line. However, override this if the * previous line ended with an explicit \n sequence; this tells us * that the line break is explicitly part of the string and that * the subsequent whitespace is to be preserved. */ if (string_newline_spacing_ != NEWLINE_SPACING_PRESERVE && !explicit_nl) for ( ; is_space(p.getch()) ; p.inc()) ; /* check to see if the newly spliced text ends in an explicit \n */ explicit_nl = (linebuf_.get_text_len() - new_line_ofs >= 2 && memcmp(linebuf_.get_text_end() - 2, "\\n", 2) == 0); /* if we skipped any spaces, remove them from the text */ if (p.getptr() > new_line_p) { /* calculate the length of the rest of the line */ size_t rem = linebuf_.get_text_len() - (p.getptr() - linebuf_.get_buf()); /* calculate the new length of the line */ size_t new_len = (new_line_p - linebuf_.get_buf()) + rem; /* move the rest of the line down over the spaces */ memmove(new_line_p, p.getptr(), rem); /* set the new length */ linebuf_.set_text_len(new_len); } /* * If the new line contains only "}" or ";", presume that the * string is unterminated and terminate it here. (This * heuristic could flag well-formed strings as erroneous, but * users can always work around this by moving these characters * onto lines that contain at least one other non-whitespace * character.) */ p.set(new_line_p); if (p.getch() == '}' || p.getch() == ';') { /* skip trailing whitespace */ for (p.inc() ; is_space(p.getch()) ; p.inc()) ; /* * if there's nothing else on the line, presume it's an * unterminated string */ if (p.getch() == '\0') { /* log the error */ log_error(TCERR_POSSIBLE_UNTERM_STR, appended_linenum_); /* remember that it's unterminated */ unterm = (char)in_quote; /* * since we're adding a presumed close quote that never * appears in the text, we need to figure the new * in-string status for the line; clear the in-quote * flag, and re-scan comments from the current point on * the line */ in_quote_ = '\0'; process_comments(new_line_p - linebuf_.get_buf()); /* we're done - unsplice from the start of the new line */ p.set(new_line_p); goto done; } } /* scan for the end of the string */ for (p.set(new_line_p) ; ; p.inc()) { /* get this character */ wchar_t cur = p.getch(); /* see what we have */ if (cur == '\\') { /* it's a backslash sequence - skip the extra character */ p.inc(); /* * in a triple-quoted string, a backslash escapes a whole * run of consecutive quote characters */ if (in_triple && p.getch() == in_quote) { /* skip all but the last consecutive quote */ int qcnt = count_quotes(&p, in_quote); p.inc_by(qcnt - 1); } else if (p.getch() == '<') { /* skip a run of <'s */ for ( ; p.getch() == '<' ; p.inc()) ; } } else if (cur == in_quote) { /* it's our quote character - check for triple quotes */ if (in_triple) { /* in a triple-quoted string - we need 3+ to end it */ int qcnt = count_quotes(&p, cur); if (qcnt >= 3) { /* we have at least 3 - skip them and exit */ p.inc_by(qcnt); goto done; } } else { /* regular string - skip it and exit the string */ p.inc(); goto done; } } else if (cur == '<' && p.getch_at(1) == '<') { /* * it's an embedded expression starter - skip the '<<' * sequence and stop scanning */ p.inc(); p.inc(); /* check for a '%' sprintf code */ if (p.getch() == '%') scan_sprintf_spec(&p); /* done */ goto done; } else if (cur == '\0') { /* end of line - go back and splice another line */ break; } } } done: /* if we actually spliced anything, unsplice it at the current point */ if (p.getptr() != 0) unsplice_line(p.getptr()); /* if we found an unterminated string, supply implicit termination */ if (unterm != '\0') linebuf_.append(&unterm, 1); } /* ------------------------------------------------------------------------ */ /* * Process a #pragma directive */ void CTcTokenizer::pp_pragma() { struct pp_kw_def { const char *kw; void (CTcTokenizer::*func)(); }; static pp_kw_def kwlist[] = { // { "c", &CTcTokenizer::pragma_c }, -- obsolete { "once", &CTcTokenizer::pragma_once }, { "all_once", &CTcTokenizer::pragma_all_once }, { "message", &CTcTokenizer::pragma_message }, { "newline_spacing", &CTcTokenizer::pragma_newline_spacing }, { "sourceTextGroup", &CTcTokenizer::pragma_source_text_group }, { 0, 0 } }; pp_kw_def *kwp; size_t kwlen; /* get the pragma keyword */ if (next_on_line() != TOKT_SYM) { log_warning(TCERR_UNKNOWN_PRAGMA, (int)curtok_.get_text_len(), curtok_.get_text()); return; } /* get the keyword length */ kwlen = curtok_.get_text_len(); /* scan the pragma list */ for (kwp = kwlist ; kwp->kw != 0 ; ++kwp) { /* is this our keyword? */ if (strlen(kwp->kw) == kwlen && memicmp(curtok_.get_text(), kwp->kw, kwlen) == 0) { /* this is our keyword - invoke the handler */ (this->*(kwp->func))(); /* we're done */ return; } } /* we didn't find it - generate a warning */ log_warning(TCERR_UNKNOWN_PRAGMA, kwlen, curtok_.get_text()); } #if 0 // #pragma C is not currently used /* * Process a #pragma C directive */ void CTcTokenizer::pragma_c() { tc_toktyp_t tok; int new_pragma_c; /* get the next token */ tok = next_on_line(); /* * "+" or empty (end of line or whitespace) indicates C mode; "-" * indicates standard mode */ if (tok == TOKT_PLUS || tok == TOKT_EOF) new_pragma_c = TRUE; else if (tok == TOKT_MINUS) new_pragma_c = FALSE; else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); new_pragma_c = str_->is_pragma_c(); } /* * retain the pragma in the result if we're in preprocess-only mode, * otherwise remove it */ if (!pp_only_mode_) clear_linebuf(); /* set the mode in the stream */ str_->set_pragma_c(new_pragma_c); /* if there's a parser, notify it of the change */ if (G_prs != 0) G_prs->set_pragma_c(new_pragma_c); } #endif /* * Process a #pragma once directive */ void CTcTokenizer::pragma_once() { /* add this file to the ONCE list */ add_include_once(str_->get_desc()->get_fname()); /* don't retain this pragma in the result */ clear_linebuf(); } /* * Process a #pragma all_once directive */ void CTcTokenizer::pragma_all_once() { tc_toktyp_t tok; /* get the next token */ tok = next_on_line(); /* * "+" or empty (end of line or whitespace) indicates ALL_ONCE mode; * '-' indicates standard mode */ if (tok == TOKT_PLUS || tok == TOKT_EOF) all_once_ = TRUE; else if (tok == TOKT_MINUS) all_once_ = FALSE; else log_warning(TCERR_BAD_PRAGMA_SYNTAX); /* don't retain this pragma in the result */ clear_linebuf(); } /* * Process a #pragma message directive */ void CTcTokenizer::pragma_message() { size_t startofs; /* * copy the source line through the "message" token to the macro * expansion buffer - we don't want to expand that part, but we want * it to appear in the expansion, so just copy the original */ startofs = (curtok_.get_text() + curtok_.get_text_len() - linebuf_.get_text()); expbuf_.copy(linebuf_.get_text(), startofs); /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, FALSE, TRUE)) { clear_linebuf(); return; } /* * If we're in normal compilation mode, display the message. If we're * in preprocess-only mode, simply retain the message in the * preprocessed result, so that it shows up when the result is * compiled. * * Ignore messages in list-includes mode. */ if (!pp_only_mode_ && !list_includes_mode_) { /* set up at the first post-processed token */ start_new_line(&expbuf_, startofs); /* if there's an open paren, skip it */ if (next_on_line_xlat(0) == TOKT_LPAR) next_on_line_xlat(0); else log_warning(TCERR_BAD_PRAGMA_SYNTAX); /* keep going until we reach the closing paren */ while (curtok_.gettyp() != TOKT_RPAR && curtok_.gettyp() != TOKT_EOF) { /* display this token */ switch(curtok_.gettyp()) { case TOKT_SSTR: case TOKT_DSTR: case TOKT_SYM: /* display the text of the token */ msg_str(curtok_.get_text(), curtok_.get_text_len()); break; case TOKT_INT: /* display the integer */ msg_long(curtok_.get_int_val()); break; default: /* ignore anything else */ break; } /* get the next token */ next_on_line_xlat(0); } /* end the line */ msg_str("\n", 1); /* remove the message from the result text */ clear_linebuf(); } else { /* preprocessing - copy expanded text to line buffer */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } } /* * Process a #pragma newline_spacing(on/off) directive */ void CTcTokenizer::pragma_newline_spacing() { newline_spacing_mode_t f; /* if we're in preprocess-only mode, just pass the pragma through */ if (pp_only_mode_) return; /* get the '(' token and the on/off token */ if (next_on_line() != TOKT_LPAR || next_on_line() != TOKT_SYM) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* note the new mode flag */ if (curtok_.get_text_len() == 2 && memcmp(curtok_.get_text(), "on", 2) == 0) { /* 'on' - this is the old version of 'collapse' */ f = NEWLINE_SPACING_COLLAPSE; } else if (curtok_.get_text_len() == 8 && memcmp(curtok_.get_text(), "collapse", 8) == 0) { f = NEWLINE_SPACING_COLLAPSE; } else if (curtok_.get_text_len() == 3 && memcmp(curtok_.get_text(), "off", 3) == 0) { /* 'off' - the old version of 'delete' */ f = NEWLINE_SPACING_DELETE; } else if (curtok_.get_text_len() == 6 && memcmp(curtok_.get_text(), "delete", 6) == 0) { f = NEWLINE_SPACING_DELETE; } else if (curtok_.get_text_len() == 8 && memcmp(curtok_.get_text(), "preserve", 8) == 0) { f = NEWLINE_SPACING_PRESERVE; } else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* make sure we have the ')' token */ if (next_on_line() != TOKT_RPAR) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* set the new mode */ string_newline_spacing_ = f; done: /* done - discard this line buffer */ clear_linebuf(); } /* * Process a #pragma sourceTextGroup(on/off) directive */ void CTcTokenizer::pragma_source_text_group() { tc_toktyp_t tok; int f; /* if we're in preprocess-only mode, just pass the pragma through */ if (pp_only_mode_) return; /* get the '(' token and the on/off token, if present */ if ((tok = next_on_line()) == TOKT_EOF) { /* no on/off - by default it's on */ f = TRUE; } else if (tok == TOKT_LPAR && next_on_line() == TOKT_SYM) { /* get the on/off mode */ if (curtok_.get_text_len() == 2 && memcmp(curtok_.get_text(), "on", 2) == 0) { /* it's 'on' */ f = TRUE; } else if (curtok_.get_text_len() == 3 && memcmp(curtok_.get_text(), "off", 3) == 0) { /* it's 'off' */ f = FALSE; } else { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* make sure we have the ')' token */ if (next_on_line() != TOKT_RPAR) { log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } } else { /* anything else is invalid syntax */ log_warning(TCERR_BAD_PRAGMA_SYNTAX); goto done; } /* set the new mode in the parser */ G_prs->set_source_text_group_mode(f); done: /* done - discard this line buffer */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #charset directive */ void CTcTokenizer::pp_charset() { /* * Encountering a #charset directive within the tokenizer is always * an error. If the file opener managed to use a #charset, we'll * never see it, because the file opener will have skipped it before * giving us the file. * * If we flagged a #charset error when opening the file, indicate * that the problem is that the character set given was unloadable; * otherwise, the problem is that #charset is in the wrong place. */ log_error(str_ != 0 && str_->get_charset_error() ? TCERR_CANT_LOAD_CHARSET : TCERR_UNEXPECTED_CHARSET); /* don't retain this pragma in the result */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #include directive */ void CTcTokenizer::pp_include() { wchar_t match; int is_local; int is_absolute; utf8_ptr fname; CTcSrcFile *new_src; int charset_error; int default_charset_error; char full_name[OSFNMAX]; char lcl_name[OSFNMAX]; int found; CTcTokFileDesc *desc; int expand; utf8_ptr start; /* presume we'll expand macros */ expand = TRUE; /* * Check to see if expansion is needed. Macro expansion is needed * only if the source line is not of one of the following forms: * *. #include "filename" *. #include */ for (start = p_ ; is_space(p_.getch()) ; p_.inc()) ; switch(p_.getch()) { case '<': /* look for a matching '>' */ match = '>'; goto find_match; case '"': /* look for a matching '"' */ match = '"'; goto find_match; find_match: /* find the matching character */ for (p_.inc() ; p_.getch() != '\0' && p_.getch() != match ; p_.inc()) ; /* if we found it, check for other characters on the line */ if (p_.getch() == match) { /* skip the matching character */ p_.inc(); /* skip whitespace */ while (is_space(p_.getch())) p_.inc(); /* * make sure there's nothing else on the line - if not, it's * one of the approved formats, so there's no need to do * macro expansion */ if (p_.getch() == 0) expand = FALSE; } break; } /* go back to read from the original starting point */ p_ = start; /* expand macros if necessary */ if (expand) { /* do the expansion */ if (expand_macros_curline(FALSE, FALSE, FALSE)) { /* clear the buffer and abort */ clear_linebuf(); return; } /* * remove any expansion flags, so that we don't have to worry about * parsing or skipping them */ remove_expansion_flags(&expbuf_); /* read from the expansion buffer */ start_new_line(&expbuf_, 0); } /* skip leading whitespace */ for ( ; is_space(p_.getch()) ; p_.inc()) ; /* we have to be looking at at '"' or '<' character */ if (p_.getch() == '"') { /* look for a matching quote, and look for a local file */ match = '"'; is_local = TRUE; } else if (p_.getch() == '<') { /* look for a matching angle bracket, and look for a system file */ match = '>'; is_local = FALSE; } else { /* invalid syntax - log an error and ignore the line */ log_error(TCERR_BAD_INC_SYNTAX); clear_linebuf(); return; } /* skip the open quote, and remember where the filename starts */ p_.inc(); fname = p_; /* find the matching quote */ for ( ; p_.getch() != '\0' && p_.getch() != match ; p_.inc()) ; /* if we didn't find the match, log an error and ignore the line */ if (p_.getch() == '\0') { log_error(TCERR_BAD_INC_SYNTAX); clear_linebuf(); return; } else { /* * We found the close quote. Before we parse the filename, make * one last check: if there's anything further on the line apart * from whitespace, it's extraneous, so issue a warning. */ /* remember where the close quote is */ utf8_ptr closep = p_; /* skip it, and then skip any trailing whitespace */ for (p_.inc() ; is_space(p_.getch()) ; p_.inc()) ; /* if we're not at the end of the line, issue a warning */ if (p_.getch() != '\0') log_warning(TCERR_EXTRA_INC_SYNTAX); /* * Null-terminate the filename. (We know there's nothing else * interesting in the buffer after the filename at this point, so * we don't care about overwriting the quote or anything that might * come after it.) */ closep.setch('\0'); } /* check to see if the filename is absolute */ is_absolute = os_is_file_absolute(fname.getptr()); /* we have yet to find the file */ found = FALSE; /* * in case the name is in portable URL notation, convert from URL * notation to local notation; we'll consider this form of the name * first, and only if we can't find it in this form will we try * treating the name as using local filename conventions */ os_cvt_url_dir(lcl_name, sizeof(lcl_name), fname.getptr()); /* * Search for the included file. * * First, if it's a local file (in quotes rather than angle * brackets), start the search in the directory containing the * current file, then look in the directory containing the parent * file, and so on. If we fail to find it, proceed as for a * non-local file. */ if (is_local && last_desc_ != 0) { CTcTokStream *cur_str; char pathbuf[OSFNMAX]; /* start with the current file, and search parents */ for (cur_str = str_ ; cur_str != 0 ; cur_str = cur_str->get_parent()) { /* get the path to the current file */ os_get_path_name(pathbuf, sizeof(pathbuf), last_desc_->get_fname()); /* * try the URL-converted name first - this takes precedence * over a local interpretation of the name */ os_build_full_path(full_name, sizeof(full_name), pathbuf, lcl_name); if (!osfacc(full_name)) { found = TRUE; break; } /* if it's a relative local name, try again with local naming */ if (!is_absolute) { /* * build the full filename, treating the name as using * local system conventions */ os_build_full_path(full_name, sizeof(full_name), pathbuf, fname.getptr()); /* if we found it, so note and stop searching */ if (!osfacc(full_name)) { found = TRUE; break; } } } } /* * If we still haven't found the file (or if it's a non-local file, * in angle brackets), search the include path. */ if (!found) { tctok_incpath_t *inc_path; /* scan the include path */ for (inc_path = incpath_head_ ; inc_path != 0 ; inc_path = inc_path->nxt) { /* try the URL-converted local name first */ os_build_full_path(full_name, sizeof(full_name), inc_path->path, lcl_name); if (!osfacc(full_name)) { found = TRUE; break; } /* try with the local name, if it's a relative local name */ if (!is_absolute) { /* build the full name for the file in this directory */ os_build_full_path(full_name, sizeof(full_name), inc_path->path, fname.getptr()); /* if we found it, stop searching */ if (!osfacc(full_name)) { found = TRUE; break; } } } } /* * If the filename specified an absolute path, and we didn't find a * file with any of the local interpretations, look at the absolute * path. Note that our portable URL-style notation doesn't allow * absolute notation, so we use only the exact name as specified in * the #include directive as the absolute form. */ if (is_absolute && !found) { /* use the original filename as the full name */ strcpy(full_name, fname.getptr()); /* try finding the file */ found = !osfacc(full_name); } /* * we have our copy of the filename now; we don't want to retain * this directive in the preprocessed source, so clear out the line * buffer now */ clear_linebuf(); /* * if we didn't find the file anywhere, show an error and ignore the * #include directive */ if (!found) { log_error(TCERR_INC_NOT_FOUND, (int)strlen(fname.getptr()), fname.getptr()); return; } /* * Check the list of included files that are marked for inclusion * only once. If we've already included this file, ignore this * redundant inclusion. Check based on the full filename that we * resolved from the search path. */ if (find_include_once(full_name)) { /* log an error if appropriate */ if (warn_on_ignore_incl_) log_warning(TCERR_REDUNDANT_INCLUDE, (int)strlen(full_name), full_name); /* ignore this #include directive */ return; } /* open a file source to read the file */ new_src = CTcSrcFile::open_source(full_name, res_loader_, default_charset_, &charset_error, &default_charset_error); /* if we couldn't open the file, log an error and ignore the line */ if (new_src == 0) { /* * if the error was due to the default character set, log that * problem; otherwise, log the general file-open problem */ if (default_charset_error) log_error(TCERR_CANT_LOAD_DEFAULT_CHARSET, default_charset_); else log_error(TCERR_INC_NOT_FOUND, (int)strlen(full_name), full_name); /* we can go no further */ return; } /* get the descriptor for the source file */ desc = get_file_desc(full_name, strlen(full_name), FALSE, fname.getptr(), fname.getptr() != 0 ? strlen(fname.getptr()) : 0); /* * remember the current #pragma newline_spacing mode, so we can restore * it when we reinstate the current stream */ str_->set_newline_spacing(string_newline_spacing_); /* * Create and install the new file reader stream object. By * installing it as the current reader, we'll activate it so that * the next line read will come from the new stream. Note that the * current stream becomes the parent of the new stream, so that we * revert to the current stream when the new stream is exhausted; * this will allow us to pick up reading from the current stream at * the next line after the #include directive when we've finished * including the new file. */ str_ = new CTcTokStream(desc, new_src, str_, charset_error, if_sp_); /* * If we're in ALL_ONCE mode, it means that every single file we * include should be included only once. */ if (all_once_) add_include_once(full_name); /* * if we're in list-includes mode, write the name of the include file * to the standard output */ if (list_includes_mode_) G_hostifc->print_msg("#include %s\n", full_name); } /* ------------------------------------------------------------------------ */ /* * Add a file to the include-once list. Once a file is in this list, we * won't include it again. */ void CTcTokenizer::add_include_once(const char *fname) { tctok_incfile_t *prvinc; /* if the file is already in the list, don't add it again */ if (find_include_once(fname)) return; /* create a new entry for the filename */ prvinc = (tctok_incfile_t *)t3malloc(sizeof(tctok_incfile_t) + strlen(fname)); /* save the filename */ strcpy(prvinc->fname, fname); /* link the new entry into our list */ prvinc->nxt = prev_includes_; prev_includes_ = prvinc; } /* * Find a file in the list of files to be included only once. Returns * true if the file is in the list, false if not. */ int CTcTokenizer::find_include_once(const char *fname) { tctok_incfile_t *prvinc; /* search the list */ for (prvinc = prev_includes_ ; prvinc != 0 ; prvinc = prvinc->nxt) { /* if this one matches, we found it, so return true */ if (os_file_names_equal(fname, prvinc->fname)) return TRUE; } /* we didn't find the file */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Process a #define directive */ void CTcTokenizer::pp_define() { const char *macro_name; size_t macro_len; const char *argv[TOK_MAX_MACRO_ARGS]; size_t argvlen[TOK_MAX_MACRO_ARGS]; int argc; int has_args; const char *expan; size_t expan_len; CTcHashEntryPp *entry, *old_entry; int has_varargs; /* get the macro name */ if (next_on_line() != TOKT_SYM) { log_error(TCERR_BAD_DEFINE_SYM, (int)curtok_.get_text_len(), curtok_.get_text()); clear_linebuf(); return; } /* make a copy of the macro name */ macro_name = curtok_.get_text(); macro_len = curtok_.get_text_len(); /* no arguments yet */ argc = 0; /* presume we won't find a varargs marker */ has_varargs = FALSE; /* * If there's a '(' immediately after the macro name, without any * intervening whitespace, it has arguments; otherwise, it has no * arguments. Note which case we have. */ if (p_.getch() == '(') { int done; tc_toktyp_t tok; /* note that we have an argument list */ has_args = TRUE; /* assume we're not done yet */ done = FALSE; /* skip the paren and get the next token */ p_.inc(); tok = next_on_line(); /* check for an empty argument list */ if (tok == TOKT_RPAR) { /* note that we're done with the arguments */ done = TRUE; } /* scan the argument list */ while (!done) { /* if we have too many arguments, it's an error */ if (argc >= TOK_MAX_MACRO_ARGS) { log_error(TCERR_TOO_MANY_MAC_PARMS, macro_name, macro_len, TOK_MAX_MACRO_ARGS); clear_linebuf(); return; } /* if we're at the end of the macro, it's an error */ if (tok == TOKT_EOF) { /* log the error and ignore the line */ log_error(TCERR_MACRO_NO_RPAR); clear_linebuf(); return; } /* check for a valid initial symbol character */ if (tok != TOKT_SYM) { log_error_curtok(TCERR_BAD_MACRO_ARG_NAME); clear_linebuf(); return; } /* remember the argument name */ argvlen[argc] = curtok_.get_text_len(); argv[argc++] = curtok_.get_text(); /* get the next token */ tok = next_on_line(); /* make sure we have a comma or paren following */ if (tok == TOKT_COMMA) { /* we have more arguments - skip the comma */ tok = next_on_line(); } else if (tok == TOKT_ELLIPSIS) { /* skip the ellipsis */ tok = next_on_line(); /* note the varargs marker */ has_varargs = TRUE; /* this must be the last argument */ if (tok != TOKT_RPAR) { /* log the error */ log_error_curtok(TCERR_MACRO_ELLIPSIS_REQ_RPAR); /* discard the line and give up */ clear_linebuf(); return; } /* that's the last argument - we can stop now */ done = TRUE; } else if (tok == TOKT_RPAR) { /* no more arguments - note that we can stop now */ done = TRUE; } else { /* invalid argument - log an error and discard the line */ log_error_curtok(TCERR_MACRO_EXP_COMMA); clear_linebuf(); return; } } } else { /* * there are no arguments - the macro's expansion starts * immediately after the end of the name and any subsequent * whitespace */ has_args = FALSE; } /* skip whitespace leading up to the expansion */ while (is_space(p_.getch())) p_.inc(); /* the rest of the line is the expansion */ expan = p_.getptr(); /* don't allow defining "defined" */ if (macro_len == 7 && memcmp(macro_name, "defined", 7) == 0) { /* log an error */ log_error(TCERR_REDEF_OP_DEFINED); /* don't retain the directive in the preprocessed result */ clear_linebuf(); /* ignore the definition */ return; } /* get the length of the expansion text */ expan_len = strlen(expan); /* * remove any trailing whitespace from the expansion text; however, * leave a trailing space if it's preceded by a backslash */ while (expan_len > 0 && is_space(expan[expan_len-1]) && !(expan_len > 1 && expan[expan_len-2] == '\\')) --expan_len; /* create an entry for the new macro */ entry = new CTcHashEntryPpDefine(macro_name, macro_len, TRUE, has_args, argc, has_varargs, argv, argvlen, expan, expan_len); /* * Check the symbol table to see if this symbol is already defined with * a different expansion. If so, show a warning, but honor the new * definition. */ old_entry = find_define(macro_name, macro_len); if (old_entry != 0) { /* * Check for a trivial redefinition - if the number of arguments is * the same, and the type (object-like or function-like) is the * same, and the expansion string is identical, there's no need to * warn, because the redefinition has no effect and can thus be * safely ignored. Note that we must ignore any differences in the * whitespace in the expansions for this comparision. * * Compare the *parsed* versions of the expansions. This ensures * that we take into account changes to parameter names. For * example, the following two macros are equivalent, even though * their unparsed source texts are different, because they'd still * have identical expansions for any given parameter value: * *. #define A(x) x *. #define A(y) y * * Likewise, the following two macros are NOT equivalent, even * though their unparsed source texts are identical, because they * could have different expansions for a given parameter value: * *. #define A(x) x *. #define A(y) x */ if ((old_entry->has_args() != 0) == (has_args != 0) && old_entry->get_argc() == argc && lib_strequal_collapse_spaces(entry->get_expansion(), entry->get_expan_len(), old_entry->get_expansion(), old_entry->get_expan_len())) { /* it's a non-trivial redefinition - ignore it */ delete entry; goto done; } /* log a warning about the redefinition */ log_warning(TCERR_MACRO_REDEF, (int)macro_len, macro_name); /* remove and delete the old entry */ defines_->remove(old_entry); /* if the item isn't already in the #undef table, add it */ if (find_undef(macro_name, macro_len) == 0) { /* * move the entry to the #undef table so that we can keep track * of the fact that this macro's definition has changed in the * course of the compilation */ undefs_->add(old_entry); } else { /* * the name is already in the #undef table, so we don't need * another copy - just forget about the old entry entirely */ delete old_entry; } } /* add it to the hash table */ defines_->add(entry); done: /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #ifdef directive */ void CTcTokenizer::pp_ifdef() { /* process the ifdef/ifndef with a positive sense */ pp_ifdef_or_ifndef(TRUE); } /* * Process a #ifndef directive */ void CTcTokenizer::pp_ifndef() { /* process the ifdef/ifndef with a negative sense */ pp_ifdef_or_ifndef(FALSE); } /* * Process a #ifdef or #ifndef. If 'sense' is true, we'll take the * branch if the symbol is defined (hence #ifdef), otherwise we'll take * it if the symbol isn't defined (hence #ifndef). */ void CTcTokenizer::pp_ifdef_or_ifndef(int sense) { char macro_name[TOK_SYM_MAX_BUFFER]; int found; tok_if_t state; /* make sure we have a valid symbol */ if (pp_get_lone_ident(macro_name, sizeof(macro_name))) { /* clear the line buffer */ clear_linebuf(); /* * push a true if to avoid cascading errors for matching #endif * or #else */ push_if(TOKIF_IF_YES); /* we're done */ return; } /* check to see if it's defined */ found = (find_define(macro_name, strlen(macro_name)) != 0); /* * if we found it and they wanted it found, or we didn't find it and * they didn't want it found, take a true branch; otherwise, take a * false branch */ if ((sense != 0) == (found != 0)) state = TOKIF_IF_YES; else state = TOKIF_IF_NO; /* push the new #if state */ push_if(state); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #if directive */ void CTcTokenizer::pp_if() { CTcConstVal val; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) goto do_error; /* * we don't need the original source line any more, and we don't * want to copy it to the preprocessed output, so clear it */ clear_linebuf(); /* parse out of the expansion buffer */ start_new_line(&expbuf_, 0); /* parse the preprocessor expression */ if (pp_parse_expr(&val, TRUE, TRUE, TRUE)) { /* * we can't get a value; treat the expression as true and * continue parsing, so that we don't throw off the #if nesting * level */ val.set_bool(TRUE); } /* push the new state according to the value of the expression */ push_if(val.get_val_bool() ? TOKIF_IF_YES : TOKIF_IF_NO); /* done */ return; do_error: /* clear the line buffer */ clear_linebuf(); /* * push a true if - even though we can't evaluate the condition, we * can at least avoid a cascade of errors for the matching #endif * and #else */ push_if(TOKIF_IF_YES); } /* ------------------------------------------------------------------------ */ /* * Process a #elif directive */ void CTcTokenizer::pp_elif() { CTcConstVal val; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) { clear_linebuf(); return; } /* parse out of the expansion buffer */ start_new_line(&expbuf_, 0); /* parse the preprocessor expression */ if (pp_parse_expr(&val, TRUE, TRUE, TRUE)) { clear_linebuf(); return; } /* * make sure that the #elif occurs in the same file as the * corresponding #if */ if (if_sp_ <= str_->get_init_if_level()) { /* log the error */ log_error(TCERR_PP_ELIF_NOT_IN_SAME_FILE); /* clear the text and abort */ clear_linebuf(); return; } /* check the current #if state */ switch(get_if_state()) { case TOKIF_IF_YES: /* * we just took the #if branch, so don't take this or any * subsequent #elif or #else branch, regardless of the value of * the condition - set the state to DONE to indicate that we're * skipping everything through the endif */ change_if_state(TOKIF_IF_DONE); break; case TOKIF_IF_NO: /* * We haven't yet taken a #if or #elif branch, so we can take * this branch if its condition is true. If this branch's * condition is false, stay with NO so that we will consider * future #elif and #else branches. */ if (val.get_val_bool()) change_if_state(TOKIF_IF_YES); break; case TOKIF_IF_DONE: /* * we've already taken a #if or #elif branch, so we must ignore * this and subsequent #elif and #else branches until we get to * our #endif - just stay in state DONE */ break; case TOKIF_NONE: case TOKIF_ELSE_YES: case TOKIF_ELSE_NO: /* * we're not in a #if branch at all, or we're inside a #else; a * #elif is not legal here */ log_error(TCERR_PP_ELIF_WITHOUT_IF); break; } /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #else directive */ void CTcTokenizer::pp_else() { /* make sure there's nothing but whitespace on the line */ if (next_on_line() != TOKT_EOF) log_error(TCERR_PP_EXTRA); /* * make sure that the #else occurs in the same file as the * corresponding #if */ if (if_sp_ <= str_->get_init_if_level()) { /* log the error */ log_error(TCERR_PP_ELSE_NOT_IN_SAME_FILE); /* clear the text and abort */ clear_linebuf(); return; } /* check our current #if state */ switch(get_if_state()) { case TOKIF_IF_YES: case TOKIF_IF_DONE: /* * we've already taken a true #if branch, so we don't want to * process the #else part - switch to a false #else branch */ change_if_state(TOKIF_ELSE_NO); break; case TOKIF_IF_NO: /* * we haven't yet found a true #if branch, so take the #else * branch -- switch to a true #else branch */ change_if_state(TOKIF_ELSE_YES); break; case TOKIF_NONE: case TOKIF_ELSE_YES: case TOKIF_ELSE_NO: /* * we're not in a #if at all, or we're in a #else - log an error * and ignore it */ log_error(TCERR_PP_ELSE_WITHOUT_IF); break; } /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #endif directive */ void CTcTokenizer::pp_endif() { /* make sure the rest of the line is blank */ if (next_on_line() != TOKT_EOF) log_error(TCERR_PP_EXTRA); /* ignore the rest of the line */ clear_linebuf(); /* if we're not in a #if in the same file it's an error */ if (if_sp_ == 0) { log_error(TCERR_PP_ENDIF_WITHOUT_IF); return; } else if (if_sp_ <= str_->get_init_if_level()) { log_error(TCERR_PP_ENDIF_NOT_IN_SAME_FILE); return; } /* pop a #if level */ pop_if(); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Process a #error directive */ void CTcTokenizer::pp_error() { size_t startofs; /* * copy the source line through the "error" token to the macro * expansion buffer - we don't want to expand that part, but we want * it to appear in the expansion, so just copy the original */ startofs = (curtok_.get_text() + curtok_.get_text_len() - linebuf_.get_text()); expbuf_.copy(linebuf_.get_text(), startofs); /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, FALSE, TRUE)) { clear_linebuf(); return; } /* clean up any expansion flags embedded in the buffer */ remove_expansion_flags(&expbuf_); /* * If we're in preprocess-only mode, simply retain the text in the * processed result, so that the error is processed on a subsequent * compilation of the result; otherwise, display the error. * * Ignore #error directives in list-includes mode as well. */ if (!pp_only_mode_ && !list_includes_mode_) { /* display the error */ log_error(TCERR_ERROR_DIRECTIVE, (int)expbuf_.get_text_len() - startofs, expbuf_.get_text() + startofs); /* clear the directive from the result */ clear_linebuf(); } else { /* preprocessing - copy expanded text to line buffer */ linebuf_.copy(expbuf_.get_text(), expbuf_.get_text_len()); } } /* ------------------------------------------------------------------------ */ /* * Process a #undef directive */ void CTcTokenizer::pp_undef() { char macro_name[TOK_SYM_MAX_BUFFER]; /* get the macro name */ if (pp_get_lone_ident(macro_name, sizeof(macro_name))) { clear_linebuf(); return; } /* remove it */ undefine(macro_name); /* don't retain the directive in the preprocessed source */ clear_linebuf(); } /* * Programmatically delete a preprocesor symbol */ void CTcTokenizer::undefine(const char *sym, size_t len) { CTcHashEntryPp *entry; /* * find the macro - if it wasn't defined, silently ignore it, since * it's legal to #undef a symbol that wasn't previously defined */ entry = find_define(sym, len); if (entry != 0 && entry->is_undefable()) { /* remove it */ defines_->remove(entry); /* if it's not already in the #undef table, move it there */ if (find_undef(sym, len) == 0) { /* move it to the #undef table */ undefs_->add(entry); } else { /* * the name is already in the #undef table, so we don't need to * add it again - we can forget about this entry entirely */ delete entry; } } } /* ------------------------------------------------------------------------ */ /* * Process a #line directive */ void CTcTokenizer::pp_line() { CTcConstVal val_line; CTcConstVal val_fname; CTcTokFileDesc *desc; /* expand macros; don't allow reading additional lines */ if (expand_macros_curline(FALSE, TRUE, FALSE)) { clear_linebuf(); return; } /* * we don't need the original source line any more, and we don't * want to copy it to the preprocessed output, so clear it */ clear_linebuf(); /* set up to parse from the expansion */ start_new_line(&expbuf_, 0); /* evaluate the line number expression */ if (pp_parse_expr(&val_line, TRUE, FALSE, TRUE)) return; /* if it's not an integer constant, it's an error */ if (val_line.get_type() != TC_CVT_INT) { log_error(TCERR_LINE_REQ_INT); return; } /* evaluate the filename expression */ if (pp_parse_expr(&val_fname, FALSE, TRUE, TRUE)) return; /* the filename must be a string expression */ if (val_fname.get_type() != TC_CVT_SSTR) { log_error(TCERR_LINE_FILE_REQ_STR); return; } /* find or create a descriptor for the filename */ desc = get_file_desc(val_fname.get_val_str(), val_fname.get_val_str_len(), FALSE, 0, 0); /* set the new line number and descriptor in the current stream */ if (str_ != 0) { str_->set_next_linenum(val_line.get_val_int()); str_->set_desc(desc); } /* * retain the pragma in the result if we're in preprocess-only mode, * otherwise remove it */ if (!pp_only_mode_) clear_linebuf(); } /* ------------------------------------------------------------------------ */ /* * Look up a symbol in the #define symbol table */ CTcHashEntryPp *CTcTokenizer::find_define(const char *sym, size_t len) const { /* look it up in the #define symbol table and return the result */ return (CTcHashEntryPp *)defines_->find(sym, len); } /* * Look up a symbol in the #undef table */ CTcHashEntryPp *CTcTokenizer::find_undef(const char *sym, size_t len) const { /* look it up in the #define symbol table and return the result */ return (CTcHashEntryPp *)undefs_->find(sym, len); } /* * Add a preprocessor macro definition */ void CTcTokenizer::add_define(const char *sym, size_t len, const char *expansion, size_t expan_len) { CTcHashEntryPp *entry; /* create an entry for the macro, with no argument list */ entry = new CTcHashEntryPpDefine(sym, len, TRUE, FALSE, 0, FALSE, 0, 0, expansion, expan_len); /* add the new entry to the table */ defines_->add(entry); } /* * Add a preprocessor macro definition */ void CTcTokenizer::add_define(CTcHashEntryPp *entry) { /* add the entry to our symbol table */ defines_->add(entry); } /* * parse an expression */ int CTcTokenizer::pp_parse_expr(CTcConstVal *val, int read_first, int last_on_line, int add_line_ending) { CTcPrsNode *expr_tree; char ch; /* add the line ending marker if required */ if (add_line_ending) { /* * append the special end-of-preprocess-line to the macro * expansion buffer */ ch = TOK_END_PP_LINE; expbuf_.append(&ch, 1); } /* * note that we're pasing a preprocessor expression; this affects * error logging in certain cases */ in_pp_expr_ = TRUE; /* * parse the expression in preprocessor mode, so that double-quoted * strings can be concatenated and compared */ G_prs->set_pp_expr_mode(TRUE); /* get the first token on the line if desired */ if (read_first) next(); /* parse the expression */ expr_tree = G_prs->parse_expr(); /* make sure we're at the end of the line if desired */ if (last_on_line && next() != TOKT_EOF) log_error(TCERR_PP_EXPR_EXTRA); /* if we added the special pp-line-ending marker, remove it */ if (add_line_ending) { /* * the marker is always the last character - remove it simply by * shortening the buffer by a character */ expbuf_.set_text_len(expbuf_.get_text_len() - 1); } /* return to normal expression mode */ G_prs->set_pp_expr_mode(FALSE); /* return to normal tokenizing mode */ in_pp_expr_ = FALSE; /* if we didn't get a valid expression, return failure */ if (expr_tree == 0) return 1; /* make sure we got a constant */ if (!expr_tree->is_const()) { log_error(TCERR_PP_EXPR_NOT_CONST); return 1; } /* fill in the caller's value */ *val = *expr_tree->get_const_val(); /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * #define enumeration callback context */ struct def_enum_cb_t { /* original callback function */ void (*cb)(void *, CTcHashEntryPp *); /* original callback context */ void *ctx; }; /* * #define enumeration callback. This is a simple impedence matcher on the * way to the real callbac; we cast the generic hash entry type to the * CTcHashEntryPp subclass for the benefit of the real callback. */ static void enum_defines_cb(void *ctx0, CVmHashEntry *entry) { def_enum_cb_t *ctx; /* get our real context */ ctx = (def_enum_cb_t *)ctx0; /* invoke the real callback, casting the entry reference appropriately */ (*ctx->cb)(ctx->ctx, (CTcHashEntryPp *)entry); } /* * Enumerate the entries in the #define table through a callback */ void CTcTokenizer::enum_defines(void (*cb)(void *, CTcHashEntryPp *), void *ctx) { def_enum_cb_t myctx; /* set up our impedence-matcher context with the real callback info */ myctx.cb = cb; myctx.ctx = ctx; /* enumerate through our impedence-matcher callback */ defines_->enum_entries(&enum_defines_cb, &myctx); } /* ------------------------------------------------------------------------ */ /* * Get a lone identifier for a preprocessor directive. The identifier * must be the only thing left on the line; we'll generate an error if * extra characters follow on the line. * * If there's no identifier on the line, or there's more information * after the identifier, logs an error and returns non-zero; returns * zero on success. */ int CTcTokenizer::pp_get_lone_ident(char *buf, size_t bufl) { /* get the next token, and make sure it's a symbol */ if (next_on_line() != TOKT_SYM) { log_error_curtok(TCERR_BAD_DEFINE_SYM); return 1; } /* return an error if it doesn't fit */ if (curtok_.get_text_len() > bufl) return 1; /* copy the text */ memcpy(buf, curtok_.get_text(), curtok_.get_text_len()); buf[curtok_.get_text_len()] = '\0'; /* make sure there's nothing else on the line but whitespace */ if (next_on_line() != TOKT_EOF) { log_error(TCERR_PP_EXTRA); return 1; } /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Push a new #if level */ void CTcTokenizer::push_if(tok_if_t state) { /* if we're out of space in the stack, throw a fatal error */ if (if_sp_ == TOK_MAX_IF_NESTING) throw_fatal_error(TCERR_IF_NESTING_OVERFLOW); /* * if we're in a nested #if in a false #if, increase the nested * false #if level */ if (in_false_if()) ++if_false_level_; /* push the state, remembering where the #if was defined */ if_stack_[if_sp_].desc = last_desc_; if_stack_[if_sp_].linenum = last_linenum_; if_stack_[if_sp_++].state = state; } /* * Pop a #if level */ void CTcTokenizer::pop_if() { /* if we're in a nested #if in a false #if, pop the nesting level */ if (if_false_level_ != 0) --if_false_level_; /* pop the main if level */ if (if_sp_ != 0) --if_sp_; } /* ------------------------------------------------------------------------ */ /* * Log an error */ void CTcTokenizer::log_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_ERROR, errnum, marker); va_end(marker); } /* * Log an error with the current token's text as the parameter data, * suitable for use with a "%.*s" display format entry */ void CTcTokenizer::log_error_curtok(int errnum) { /* * display the message, passing "%.*s" parameter data for the * current token text: an integer giving the length of the token * text, and a pointer to the token text */ log_error_or_warning_curtok(TC_SEV_ERROR, errnum); } /* * Log an error or warning for the current token */ void CTcTokenizer::log_error_or_warning_curtok(tc_severity_t sev, int errnum) { /* log the error with our current token */ log_error_or_warning_with_tok(sev, errnum, getcur()); } /* * Log an error or warning with the given token */ void CTcTokenizer::log_error_or_warning_with_tok( tc_severity_t sev, int errnum, const CTcToken *tok) { const char *tok_txt; size_t tok_len; char buf[128]; const char *prefix; const char *suffix; utf8_ptr src; utf8_ptr dst; size_t rem; size_t outchars; /* see what we have */ switch(tok->gettyp()) { case TOKT_SSTR: /* show the string in quotes, but limit the length */ prefix = "'"; suffix = "'"; goto format_string; case TOKT_DSTR: prefix = "\""; suffix = "\""; goto format_string; case TOKT_DSTR_START: prefix = "\""; suffix = "<<"; goto format_string; case TOKT_DSTR_MID: prefix = ">>"; suffix = "<<"; goto format_string; case TOKT_DSTR_END: prefix = ">>"; suffix = "\""; goto format_string; case TOKT_RESTR: prefix = "R'"; suffix = "'"; goto format_string; format_string: /* set the prefix */ strcpy(buf, prefix); /* * show the string, but limit the length, and convert control * characters to escaped representation */ src.set((char *)tok->get_text()); rem = tok->get_text_len(); for (dst.set(buf + strlen(buf)), outchars = 0 ; rem != 0 && outchars < 20 ; src.inc(&rem), ++outchars) { /* if this is a control character, escape it */ if (src.getch() < 32) { dst.setch('\\'); switch(src.getch()) { case 10: dst.setch('n'); break; case 13: dst.setch('r'); break; case 0x000F: dst.setch('^'); break; case 0x000E: dst.setch('v'); break; case 0x000B: dst.setch('b'); break; case 0x0015: dst.setch(' '); break; case 9: dst.setch('t'); break; default: dst.setch('x'); dst.setch('0' + int_to_xdigit((src.getch() >> 12) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch() >> 8) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch() >> 4) & 0xf)); dst.setch('0' + int_to_xdigit((src.getch()) & 0xf)); break; } } else { /* put this character as-is */ dst.setch(src.getch()); } } /* if there's more string left, add "..." */ if (rem != 0) { dst.setch('.'); dst.setch('.'); dst.setch('.'); } /* add the suffix */ strcpy(dst.getptr(), suffix); /* use this buffer as the token string to display */ tok_txt = buf; tok_len = strlen(tok_txt); break; case TOKT_EOF: /* show a special "" marker */ tok_txt = ""; tok_len = strlen(tok_txt); break; default: /* just show the current token text */ tok_txt = tok->get_text(); tok_len = tok->get_text_len(); break; } /* log the error */ G_tcmain->log_error(get_last_desc(), get_last_linenum(), sev, errnum, tok_len, tok_txt); } /* * Log a warning */ void CTcTokenizer::log_warning(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_WARNING, errnum, marker); va_end(marker); } /* * Log a pedantic warning */ void CTcTokenizer::log_pedantic(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_PEDANTIC, errnum, marker); va_end(marker); } /* * Log a warning with the current token's text as the parameter data, * suitable for use with a "%.*s" display format entry */ void CTcTokenizer::log_warning_curtok(int errnum) { /* * display the warning message, passing "%.*s" parameter data for * the current token text: an integer giving the length of the token * text, and a pointer to the token text */ log_error_or_warning_curtok(TC_SEV_WARNING, errnum); } /* * Log and throw an internal error */ void CTcTokenizer::throw_internal_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_INTERNAL, errnum, marker); va_end(marker); /* throw the generic internal error, since we've logged this */ err_throw(TCERR_INTERNAL_ERROR); } /* * Log and throw a fatal error */ void CTcTokenizer::throw_fatal_error(int errnum, ...) { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(G_tok->get_last_desc(), G_tok->get_last_linenum(), TC_SEV_FATAL, errnum, marker); va_end(marker); /* throw the generic fatal error, since we've logged this */ err_throw(TCERR_FATAL_ERROR); } /* * display a string value */ void CTcTokenizer::msg_str(const char *str, size_t len) const { /* display the string through the host interface */ G_hostifc->print_msg("%.*s", (int)len, str); } /* * display a numeric value */ void CTcTokenizer::msg_long(long val) const { /* display the number through the host interface */ G_hostifc->print_msg("%ld", val); } /* ------------------------------------------------------------------------ */ /* * Tokenizer Input Stream implementation */ /* * create a token input stream */ CTcTokStream::CTcTokStream(CTcTokFileDesc *desc, CTcSrcObject *src, CTcTokStream *parent, int charset_error, int init_if_level) { /* remember the underlying source file */ src_ = src; /* remember the file descriptor */ desc_ = desc; /* remember the containing stream */ parent_ = parent; /* the next line to read is line number 1 */ next_linenum_ = 1; /* remember if there was a #charset error */ charset_error_ = charset_error; /* we're not in a comment yet */ in_comment_ = FALSE; /* remember the starting #if level */ init_if_level_ = init_if_level; #if 0 // #pragma C is not currently used /* * start out in parent's pragma C mode, or in non-C mode if we have * no parent */ if (parent != 0) pragma_c_ = parent->is_pragma_c(); else pragma_c_ = TRUE; #endif } /* * delete a token input stream */ CTcTokStream::~CTcTokStream() { /* we own the underlying file, so delete it */ if (src_ != 0) delete src_; } /* ------------------------------------------------------------------------ */ /* * File Descriptor */ /* * Get the length of a string with each instance of the given quote * character escaped with a backslash. We'll also count the escapes we * need for each backslash. */ static size_t get_quoted_len(const char *str, wchar_t qu) { utf8_ptr p; size_t len; /* * scan the string for instances of the quote mark; each one adds an * extra byte to the length needed, since each one requires a * backslash character to escape the quote mark */ for (p.set((char *)str), len = strlen(str) ; p.getch() != '\0' ; p.inc()) { wchar_t ch; /* * check to see if this character is quotable - it is quotable if * it's a backslash or it's the quote character we're escaping */ ch = p.getch(); if (ch == qu || ch == '\\') { /* * we need to escape this character, so add a byte for the * backslash we'll need to insert */ ++len; } } /* return the length we calculated */ return len; } /* * Build a quoted string. Fills in dst with the source string with each * of the given quote marks and each backslash escaped with a backslash. * Use get_quoted_len() to determine how much space to allocate for the * destination buffer. */ static void build_quoted_str(char *dstbuf, const char *src, wchar_t qu) { utf8_ptr p; utf8_ptr dst; /* scan the source string for escapable characters */ for (p.set((char *)src), dst.set(dstbuf), dst.setch(qu) ; p.getch() != '\0' ; p.inc()) { wchar_t ch; /* get this source character */ ch = p.getch(); /* add a quote if we have a backslash or the quote character */ if (ch == '\\' || ch == qu) { /* add a backslash to escape the character */ dst.setch('\\'); } /* add the character */ dst.setch(ch); } /* add the close quote and trailing null */ dst.setch(qu); dst.setch('\0'); } /* * create a file descriptor */ CTcTokFileDesc::CTcTokFileDesc(const char *fname, size_t fname_len, int index, CTcTokFileDesc *orig_desc, const char *orig_fname, size_t orig_fname_len) { const char *rootname; /* no source pages are allocated yet */ src_pages_ = 0; src_pages_alo_ = 0; /* remember the first instance of this filename in the list */ orig_ = orig_desc; /* there's nothing else in our chain yet */ next_ = 0; /* remember my index in the master list */ index_ = index; /* if there's a filename, save a copy of the name */ fname_ = lib_copy_str(fname, fname_len); /* if there's an original filename save it as well */ orig_fname_ = lib_copy_str(orig_fname, orig_fname_len); /* * get the root filename, since we need to build a quoted version of * that as well as of the basic filename */ rootname = os_get_root_name(fname_); /* * Allocate space for the quoted versions of the filename - make room * for the filename plus the quotes (one on each end) and a null * terminator byte. */ dquoted_fname_ = (char *)t3malloc(get_quoted_len(fname_, '"') + 3); squoted_fname_ = (char *)t3malloc(get_quoted_len(fname_, '\'') + 3); dquoted_rootname_ = (char *)t3malloc(get_quoted_len(rootname, '"') + 3); squoted_rootname_ = (char *)t3malloc(get_quoted_len(rootname, '\'') + 3); /* build the quoted version of the name */ build_quoted_str(dquoted_fname_, fname_, '"'); build_quoted_str(squoted_fname_, fname_, '\''); build_quoted_str(dquoted_rootname_, rootname, '"'); build_quoted_str(squoted_rootname_, rootname, '\''); } /* * delete the descriptor */ CTcTokFileDesc::~CTcTokFileDesc() { /* delete the filename and original filename strings */ lib_free_str(fname_); lib_free_str(orig_fname_); /* delete the quotable filename strings */ t3free(dquoted_fname_); t3free(squoted_fname_); t3free(dquoted_rootname_); t3free(squoted_rootname_); /* delete each source page we've allocated */ if (src_pages_ != 0) { size_t i; /* go through the index array and delete each allocated page */ for (i = 0 ; i < src_pages_alo_ ; ++i) { /* if this page was allocated, delete it */ if (src_pages_[i] != 0) t3free(src_pages_[i]); } /* delete the source page index array */ t3free(src_pages_); } } /* * Source page structure. Each page tracks a block of source lines. */ const size_t TCTOK_SRC_PAGE_CNT = 1024; struct CTcTokSrcPage { /* * Array of line entries on this page. Each entry is zero if it * hasn't been assigned yet, and contains the absolute image file * address of the generated code for the source line if it has been * assigned. */ ulong ofs[TCTOK_SRC_PAGE_CNT]; }; /* * Add a source line */ void CTcTokFileDesc::add_source_line(ulong linenum, ulong line_addr) { size_t page_idx; size_t idx; /* get the index of the page containing this source line */ page_idx = linenum / TCTOK_SRC_PAGE_CNT; /* get the index of the entry within the page */ idx = linenum % TCTOK_SRC_PAGE_CNT; /* * determine if our page index table is large enough, and expand it * if not */ if (page_idx >= src_pages_alo_) { size_t siz; size_t new_alo; /* allocate or expand the source pages array */ new_alo = page_idx + 16; siz = new_alo * sizeof(src_pages_[0]); if (src_pages_ == 0) src_pages_ = (CTcTokSrcPage **)t3malloc(siz); else src_pages_ = (CTcTokSrcPage **)t3realloc(src_pages_, siz); /* clear the new part */ memset(src_pages_ + src_pages_alo_, 0, (new_alo - src_pages_alo_) * sizeof(src_pages_[0])); /* remember the new allocation size */ src_pages_alo_ = new_alo; } /* if this page isn't allocated, do so now */ if (src_pages_[page_idx] == 0) { /* allocate the new page */ src_pages_[page_idx] = (CTcTokSrcPage *) t3malloc(sizeof(CTcTokSrcPage)); /* clear it */ memset(src_pages_[page_idx], 0, sizeof(CTcTokSrcPage)); } /* * if this source line entry has been previously set, don't change * it; otherwise, store the new setting */ if (src_pages_[page_idx]->ofs[idx] == 0) src_pages_[page_idx]->ofs[idx] = line_addr; } /* * Enumerate source lines */ void CTcTokFileDesc::enum_source_lines(void (*cbfunc)(void *, ulong, ulong), void *cbctx) { size_t page_idx; CTcTokSrcPage **pg; /* loop over all of the pages */ for (page_idx = 0, pg = src_pages_ ; page_idx < src_pages_alo_ ; ++page_idx, ++pg) { size_t i; ulong linenum; ulong *p; /* if this page is not populated, skip it */ if (*pg == 0) continue; /* calculate the starting line number for this page */ linenum = page_idx * TCTOK_SRC_PAGE_CNT; /* loop over the entries on this page */ for (i = 0, p = (*pg)->ofs ; i < TCTOK_SRC_PAGE_CNT ; ++i, ++p, ++linenum) { /* if this entry has been set, call the callback */ if (*p != 0) (*cbfunc)(cbctx, linenum, *p); } } } /* ------------------------------------------------------------------------ */ /* * #define symbol table hash entry */ /* * create an entry */ CTcHashEntryPpDefine::CTcHashEntryPpDefine(const textchar_t *str, size_t len, int copy, int has_args, int argc, int has_varargs, const char **argv, const size_t *argvlen, const char *expansion, size_t expan_len) : CTcHashEntryPp(str, len, copy) { /* copy the argument list if necessary */ has_args_ = has_args; has_varargs_ = has_varargs; argc_ = argc; if (argc != 0) { int i; /* allocate the argument list */ argv_ = (char **)t3malloc(argc * sizeof(*argv_)); /* allocate the parameters hash table */ params_table_ = new CVmHashTable(16, new CVmHashFuncCS(), TRUE); /* allocate the entry list */ arg_entry_ = (CTcHashEntryPpArg **) t3malloc(argc * sizeof(arg_entry_[0])); /* copy the arguments */ for (i = 0 ; i < argc ; ++i) { CTcHashEntryPpArg *entry; /* copy the argument name */ argv_[i] = lib_copy_str(argv[i], argvlen[i]); /* * Create the hash entries for this parameters. We'll use * this entry to look up tokens in the expansion text for * matches to the formal names when expanding the macro. * * Note that we'll refer directly to our local copy of the * argument name, so we don't need to make another copy in * the hash entry. */ entry = new CTcHashEntryPpArg(argv_[i], argvlen[i], FALSE, i); params_table_->add(entry); /* add it to our by-index list */ arg_entry_[i] = entry; } } else { /* no arguments */ argv_ = 0; params_table_ = 0; arg_entry_ = 0; } /* save the original version of the expansion */ orig_expan_ = lib_copy_str(expansion, expan_len); orig_expan_len_ = expan_len; /* parse the expansion, and save the parsed result */ parse_expansion(argvlen); } /* * Parse the expansion text. */ void CTcHashEntryPpDefine::parse_expansion(const size_t *argvlen) { /* * If there are arguments, scan the expansion for formal parameter * names. For each one we find, replace it with the special * TOK_MACRO_FORMAL_FLAG character followed by a one-byte value giving * the argument index. This special sequence is less costly to find * when we're expanding the macros - by doing the search here, we only * need to do it once, rather than each time we expand the macro. */ if (argc_ != 0) { utf8_ptr src; size_t dstofs; tc_toktyp_t typ; CTcToken tok; const char *start; tok_embed_ctx ec; size_t expan_len = orig_expan_len_; /* * Generate our modified expansion text in the tokenizer's macro * definition scratch buffer. Initially, make sure we have room * for a copy of the text; we'll resize the buffer later if we find * we need even more. */ CTcTokString *defbuf = &G_tok->defbuf_; defbuf->ensure_space(expan_len); /* scan for argument names, and replace them */ for (start = orig_expan_, dstofs = 0, src.set((char *)orig_expan_) ;; ) { /* get the next token */ typ = CTcTokenizer::next_on_line(&src, &tok, &ec, FALSE); /* if we've reached the end of the expansion, we're done */ if (typ == TOKT_EOF) break; /* * If this is a formal parameter name, we'll replace it with a * special two-byte sequence; otherwise, we'll keep it * unchanged. */ if (typ == TOKT_SYM) { /* look up the symbol in our argument table */ CTcHashEntryPpArg *entry = (CTcHashEntryPpArg *)params_table_->find( tok.get_text(), tok.get_text_len()); /* check if we found it */ if (entry != 0) { /* get the argument number from the entry */ int i = entry->get_argnum(); /* get the length of the formal name */ size_t arg_len = argvlen[i]; /* * the normal replacement length for a formal parameter * is two bytes - one byte for the flag, and one for * the formal parameter index */ size_t repl_len = 2; /* by default, the flag byte is the formal flag */ char flag_byte = TOK_MACRO_FORMAL_FLAG; /* * Check for special varargs control suffixes. If we * matched the last argument name, and this is a * varargs macro, we might have a suffix. */ if (has_varargs_ && i == argc_ - 1 && src.getch() == '#') { /* check for the various suffixes */ if (memcmp(src.getptr() + 1, "foreach", 7) == 0 && !is_sym(src.getch_at(8))) { /* * include the suffix length in the token * length */ arg_len += 8; /* * the flag byte is the #foreach flag, which is * a one-byte sequence */ flag_byte = TOK_MACRO_FOREACH_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "argcount", 8) == 0 && !is_sym(src.getch_at(9))) { /* * include the suffix length in the token * length */ arg_len += 9; /* * the flag byte is the #argcount flag, which * is a one-byte sequence */ flag_byte = TOK_MACRO_ARGCOUNT_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "ifempty", 7) == 0 && !is_sym(src.getch_at(8))) { /* include the length */ arg_len += 8; /* set the one-byte flag */ flag_byte = TOK_MACRO_IFEMPTY_FLAG; repl_len = 1; } else if (memcmp(src.getptr() + 1, "ifnempty", 8) == 0 && !is_sym(src.getch_at(9))) { /* include the length */ arg_len += 9; /* set the one-byte flag */ flag_byte = TOK_MACRO_IFNEMPTY_FLAG; repl_len = 1; } } /* * calculate the new length - we're removing the * argument name and adding the replacement string in * its place */ size_t new_len = expan_len + repl_len - arg_len; /* * we need two bytes for the replacement - if this is * more than we're replacing, make sure we have room * for the extra */ if (new_len > expan_len) defbuf->ensure_space(new_len); /* * copy everything up to but not including the formal * name */ if (tok.get_text() > start) { /* store the text */ memcpy(defbuf->get_buf() + dstofs, start, tok.get_text() - start); /* move past the stored text in the output */ dstofs += tok.get_text() - start; } /* the next segment starts after this token */ start = tok.get_text() + arg_len; /* store the flag byte */ defbuf->get_buf()[dstofs++] = flag_byte; /* * If appropriate, store the argument index - this * always fits in one byte because our hard limit on * formal parameters is less than 128 per macro. Note * that we add one to the index so that we never store * a zero byte, to avoid any potential confusion with a * null terminator byte. */ if (repl_len > 1) defbuf->get_buf()[dstofs++] = (char)(i + 1); /* remember the new length */ expan_len = new_len; } } } /* copy the last segment */ if (tok.get_text() > start) memcpy(defbuf->get_buf() + dstofs, start, tok.get_text() - start); /* set the new length */ defbuf->set_text_len(expan_len); /* save the parsed expansion */ expan_ = lib_copy_str(defbuf->get_text()); expan_len_ = expan_len; } else { /* * There are no arguments, so there's no parsing we need to do. * Just use the original as the parsed expansion. */ expan_ = orig_expan_; expan_len_ = orig_expan_len_; } } /* * delete */ CTcHashEntryPpDefine::~CTcHashEntryPpDefine() { int i; /* delete the argument list */ if (argv_ != 0) { /* delete each argument string */ for (i = 0 ; i < argc_ ; ++i) lib_free_str(argv_[i]); /* delete the argument vector */ t3free(argv_); /* delete the argument entry list */ t3free(arg_entry_); /* delete the hash table */ delete params_table_; } /* delete the expansion */ lib_free_str(expan_); if (orig_expan_ != expan_) lib_free_str(orig_expan_); } /* * __LINE__ static buffer */ char CTcHashEntryPpLINE::buf_[20]; /* ------------------------------------------------------------------------ */ /* * Load macro definitions from a file. */ int CTcTokenizer::load_macros_from_file(CVmStream *fp, CTcTokLoadMacErr *err_handler) { long cnt; long i; size_t curarg; char *argv[TOK_MAX_MACRO_ARGS]; size_t argvlen[TOK_MAX_MACRO_ARGS]; size_t maxarg; int result; char *expan; size_t expmaxlen; /* we haven't allocated any argument buffers yet */ maxarg = 0; /* allocate an initial expansion buffer */ expmaxlen = 1024; expan = (char *)t3malloc(expmaxlen); /* presume success */ result = 0; /* read the number of macros */ cnt = fp->read_uint4(); /* read each macro */ for (i = 0 ; i < cnt ; ++i) { char namebuf[TOK_SYM_MAX_LEN]; size_t namelen; int flags; size_t argc; size_t explen; CTcHashEntryPp *entry; int has_args; int has_varargs; /* read the name's length */ namelen = fp->read_uint2(); if (namelen > sizeof(namebuf)) { /* log an error through the handler */ err_handler->log_error(1); /* give up - we can't read any more of the file */ result = 1; goto done; } /* read the name */ fp->read_bytes(namebuf, namelen); /* read and decode the flags */ flags = fp->read_uint2(); has_args = ((flags & 1) != 0); has_varargs = ((flags & 2) != 0); /* read the number of arguments, and read each argument */ argc = fp->read_uint2(); for (curarg = 0 ; curarg < argc ; ++curarg) { /* read the length, and make sure it's valid */ argvlen[curarg] = fp->read_uint2(); if (argvlen[curarg] > TOK_SYM_MAX_LEN) { /* log an error */ err_handler->log_error(2); /* give up - we can't read any more of the file */ result = 2; goto done; } /* * if we haven't allocated a buffer for this argument slot yet, * allocate it now; allocate the buffer at the maximum symbol * size, so we can reuse the same buffer for an argument of * other macros we read later */ while (curarg >= maxarg) argv[maxarg++] = (char *)t3malloc(TOK_SYM_MAX_LEN); /* read the argument text */ fp->read_bytes(argv[curarg], argvlen[curarg]); } /* read the expansion size */ explen = (size_t)fp->read_uint4(); /* expand the expansion buffer if necessary */ if (explen > expmaxlen) { /* * overshoot a bit, so that we won't have to reallocate again * if we find a slightly larger expansion for a future macro */ expmaxlen = explen + 512; /* allocate the new buffer */ expan = (char *)t3realloc(expan, expmaxlen); } /* read the expansion */ fp->read_bytes(expan, explen); /* * Before we create the entry, check to see if there's an existing * entry with the same name. */ entry = find_define(namebuf, namelen); if (entry != 0) { /* * We have another entry. If the entry is exactly the same, * then we can simply skip the current entry, because we simply * want to keep one copy of each macro that's defined * identically in mutiple compilation macros. If the entry is * different from the new one, delete both - a macro which * appears in two or more compilation units with different * meanings is NOT a global macro, and thus we can't include it * in the debugging records. */ if (entry->is_pseudo() || entry->has_args() != has_args || entry->has_varargs() != has_varargs || entry->get_argc() != (int)argc || entry->get_orig_expan_len() != explen || memcmp(entry->get_orig_expansion(), expan, explen) != 0) { /* * The existing entry is different from the new entry, so * the macro has different meanings in different * compilation units, hence we cannot keep *either* * definition in the debug records. Delete the existing * macro, and do not create the new macro. If the existing * macro is a pseudo-macro, keep the old one (since it's * provided by the compiler itself), but still discard the * new one. */ if (!entry->is_pseudo()) undefine(namebuf, namelen); } else { /* * The new entry is identical to the old one, so keep it. * We only need one copy of the entry, though, so simply * keep the old one - there's no need to create a new entry * for the object file data. */ } } else { /* * There's no existing macro with the same name, so create a * new entry based on the object file data. */ entry = new CTcHashEntryPpDefine(namebuf, namelen, TRUE, has_args, argc, has_varargs, (const char **)argv, argvlen, expan, explen); /* add it to the preprocessor's macro symbol table */ add_define(entry); } } done: /* free the argument buffers we allocated */ for (curarg = 0 ; curarg < maxarg ; ++curarg) t3free(argv[curarg]); /* free the expansion buffer */ t3free(expan); /* success */ return result; } /* ------------------------------------------------------------------------ */ /* * Callback context for writing enumerated #define symbols to a file */ struct write_macro_ctx_t { /* object file we're writing to */ CVmFile *fp; /* number of symbols written so far */ unsigned long cnt; }; /* * Enumeration callback for writing the #define symbols to a file */ static void write_macros_cb(void *ctx0, CTcHashEntryPp *entry) { write_macro_ctx_t *ctx = (write_macro_ctx_t *)ctx0; int flags; int i; CVmFile *fp = ctx->fp; /* * if this is a pseudo-macro (such as __LINE__ or __FILE__), ignore it * - these macros do not have permanent global definitions, so they're * not usable in the debugger */ if (entry->is_pseudo()) return; /* * If the macro was ever redefined or undefined, ignore it - the * debugger can only use truly global macros, which are macros that * have stable meanings throughout the compilation units where they * appear (and which do not have different meanings in different * compilation units, but that's not our concern at the moment). The * preprocessor keeps an "undef" table of everything undefined * (explicitly, or implicitly via redefinition), so look up this macro * in the undef table, and ignore the macro if it we find it. */ if (G_tok->find_undef(entry->getstr(), entry->getlen()) != 0) return; /* count this macro */ ctx->cnt++; /* write the macro's name */ fp->write_uint2(entry->getlen()); fp->write_bytes(entry->getstr(), entry->getlen()); /* write the flag bits */ flags = 0; if (entry->has_args()) flags |= 1; if (entry->has_varargs()) flags |= 2; fp->write_uint2(flags); /* write the number of arguments, and write each argument */ fp->write_uint2(entry->get_argc()); for (i = 0 ; i < entry->get_argc() ; ++i) { CTcHashEntryPpArg *arg; /* get the argument */ arg = entry->get_arg_entry(i); /* write the parameter name */ fp->write_uint2(arg->getlen()); fp->write_bytes(arg->getstr(), arg->getlen()); } /* write the expansion */ fp->write_uint4(entry->get_orig_expan_len()); fp->write_bytes(entry->get_orig_expansion(), entry->get_orig_expan_len()); } /* * Write all #define symbols to a file, for debugging purposes. Writes * only symbols that have never been undefined or redefined, since the * debugger can only make use of global symbols (i.e., symbols with * consistent meanings through all compilation units in which they * appear). */ void CTcTokenizer::write_macros_to_file_for_debug(CVmFile *fp) { long pos; long endpos; write_macro_ctx_t ctx; /* write a placeholder for the symbol count */ pos = fp->get_pos(); fp->write_uint4(0); /* write the symbols */ ctx.fp = fp; ctx.cnt = 0; enum_defines(&write_macros_cb, &ctx); /* go back and fix up the symbol count */ endpos = fp->get_pos(); fp->set_pos(pos); fp->write_uint4(ctx.cnt); /* seek back to where we left off */ fp->set_pos(endpos); } frobtads-1.2.3/tads3/derived/0000755000175000001440000000000012145614111015140 5ustar realncusersfrobtads-1.2.3/tads3/derived/vmuni_cs.cpp0000644000175000001440001673147712145203316017522 0ustar realncusers/* Copyright (c) 1999, 2012 Michael J. Roberts */ /* * TADS 3 Case Conversion Table * * THIS IS A GENERATED FILE. DO NOT EDIT - any changes will * be lost on the next build. * * This file is mechanically derived from the Unicode * character database listing. */ #include #include "vmuni.h" static const unsigned char chartype[32768] = { (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0001 | 0000 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0003 | 0002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0005 | 0004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0007 | 0006 */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_OTHER, /* 0009 | 0008 */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 000b | 000a */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_SPACE, /* 000d | 000c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 000f | 000e */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0011 | 0010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0013 | 0012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0015 | 0014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0017 | 0016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0019 | 0018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 001b | 001a */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 001d | 001c */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_VSPAC, /* 001f | 001e */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 0021 EXCLAMATION MARK | 0020 SPACE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0023 NUMBER SIGN | 0022 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0025 PERCENT SIGN | 0024 DOLLAR SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0027 APOSTROPHE | 0026 AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0029 RIGHT PARENTHESIS | 0028 LEFT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 002b PLUS SIGN | 002a ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 002d HYPHEN-MINUS | 002c COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 002f SOLIDUS | 002e FULL STOP */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0031 DIGIT ONE | 0030 DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0033 DIGIT THREE | 0032 DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0035 DIGIT FIVE | 0034 DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0037 DIGIT SEVEN | 0036 DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0039 DIGIT NINE | 0038 DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 003b SEMICOLON | 003a COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 003d EQUALS SIGN | 003c LESS-THAN SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 003f QUESTION MARK | 003e GREATER-THAN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_PUNCT, /* 0041 LATIN CAPITAL LETTER A | 0040 COMMERCIAL AT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0043 LATIN CAPITAL LETTER C | 0042 LATIN CAPITAL LETTER B */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0045 LATIN CAPITAL LETTER E | 0044 LATIN CAPITAL LETTER D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0047 LATIN CAPITAL LETTER G | 0046 LATIN CAPITAL LETTER F */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0049 LATIN CAPITAL LETTER I | 0048 LATIN CAPITAL LETTER H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004b LATIN CAPITAL LETTER K | 004a LATIN CAPITAL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004d LATIN CAPITAL LETTER M | 004c LATIN CAPITAL LETTER L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 004f LATIN CAPITAL LETTER O | 004e LATIN CAPITAL LETTER N */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0051 LATIN CAPITAL LETTER Q | 0050 LATIN CAPITAL LETTER P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0053 LATIN CAPITAL LETTER S | 0052 LATIN CAPITAL LETTER R */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0055 LATIN CAPITAL LETTER U | 0054 LATIN CAPITAL LETTER T */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0057 LATIN CAPITAL LETTER W | 0056 LATIN CAPITAL LETTER V */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0059 LATIN CAPITAL LETTER Y | 0058 LATIN CAPITAL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* 005b LEFT SQUARE BRACKET | 005a LATIN CAPITAL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 005d RIGHT SQUARE BRACKET | 005c REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 005f LOW LINE | 005e CIRCUMFLEX ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 0061 LATIN SMALL LETTER A | 0060 GRAVE ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0063 LATIN SMALL LETTER C | 0062 LATIN SMALL LETTER B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0065 LATIN SMALL LETTER E | 0064 LATIN SMALL LETTER D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0067 LATIN SMALL LETTER G | 0066 LATIN SMALL LETTER F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0069 LATIN SMALL LETTER I | 0068 LATIN SMALL LETTER H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006b LATIN SMALL LETTER K | 006a LATIN SMALL LETTER J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006d LATIN SMALL LETTER M | 006c LATIN SMALL LETTER L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 006f LATIN SMALL LETTER O | 006e LATIN SMALL LETTER N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0071 LATIN SMALL LETTER Q | 0070 LATIN SMALL LETTER P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0073 LATIN SMALL LETTER S | 0072 LATIN SMALL LETTER R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0075 LATIN SMALL LETTER U | 0074 LATIN SMALL LETTER T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0077 LATIN SMALL LETTER W | 0076 LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0079 LATIN SMALL LETTER Y | 0078 LATIN SMALL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_LOWER, /* 007b LEFT CURLY BRACKET | 007a LATIN SMALL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 007d RIGHT CURLY BRACKET | 007c VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 007f | 007e TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0081 | 0080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0083 | 0082 */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_OTHER, /* 0085 | 0084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0087 | 0086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0089 | 0088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008b | 008a */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008d | 008c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 008f | 008e */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0091 | 0090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0093 | 0092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0095 | 0094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0097 | 0096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0099 | 0098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009b | 009a */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009d | 009c */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 009f | 009e */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 00a1 INVERTED EXCLAMATION MARK | 00a0 NO-BREAK SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a3 POUND SIGN | 00a2 CENT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a5 YEN SIGN | 00a4 CURRENCY SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 00a7 SECTION SIGN | 00a6 BROKEN BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00a9 COPYRIGHT SIGN | 00a8 DIAERESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 00ab LEFT-POINTING DOUBLE ANGLE QUOTATION MA | 00aa FEMININE ORDINAL INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00ad SOFT HYPHEN | 00ac NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00af MACRON | 00ae REGISTERED SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b1 PLUS-MINUS SIGN | 00b0 DEGREE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b3 SUPERSCRIPT THREE | 00b2 SUPERSCRIPT TWO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 00b5 MICRO SIGN | 00b4 ACUTE ACCENT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 00b7 MIDDLE DOT | 00b6 PILCROW SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00b9 SUPERSCRIPT ONE | 00b8 CEDILLA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 00bb RIGHT-POINTING DOUBLE ANGLE QUOTATION M | 00ba MASCULINE ORDINAL INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 00bd VULGAR FRACTION ONE HALF | 00bc VULGAR FRACTION ONE QUARTER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 00bf INVERTED QUESTION MARK | 00be VULGAR FRACTION THREE QUARTERS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c1 LATIN CAPITAL LETTER A WITH ACUTE | 00c0 LATIN CAPITAL LETTER A WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c3 LATIN CAPITAL LETTER A WITH TILDE | 00c2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c5 LATIN CAPITAL LETTER A WITH RING ABOVE | 00c4 LATIN CAPITAL LETTER A WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c7 LATIN CAPITAL LETTER C WITH CEDILLA | 00c6 LATIN CAPITAL LETTER AE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00c9 LATIN CAPITAL LETTER E WITH ACUTE | 00c8 LATIN CAPITAL LETTER E WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cb LATIN CAPITAL LETTER E WITH DIAERESIS | 00ca LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cd LATIN CAPITAL LETTER I WITH ACUTE | 00cc LATIN CAPITAL LETTER I WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00cf LATIN CAPITAL LETTER I WITH DIAERESIS | 00ce LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d1 LATIN CAPITAL LETTER N WITH TILDE | 00d0 LATIN CAPITAL LETTER ETH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d3 LATIN CAPITAL LETTER O WITH ACUTE | 00d2 LATIN CAPITAL LETTER O WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d5 LATIN CAPITAL LETTER O WITH TILDE | 00d4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 00d7 MULTIPLICATION SIGN | 00d6 LATIN CAPITAL LETTER O WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00d9 LATIN CAPITAL LETTER U WITH GRAVE | 00d8 LATIN CAPITAL LETTER O WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00db LATIN CAPITAL LETTER U WITH CIRCUMFLEX | 00da LATIN CAPITAL LETTER U WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 00dd LATIN CAPITAL LETTER Y WITH ACUTE | 00dc LATIN CAPITAL LETTER U WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 00df LATIN SMALL LETTER SHARP S | 00de LATIN CAPITAL LETTER THORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e1 LATIN SMALL LETTER A WITH ACUTE | 00e0 LATIN SMALL LETTER A WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e3 LATIN SMALL LETTER A WITH TILDE | 00e2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e5 LATIN SMALL LETTER A WITH RING ABOVE | 00e4 LATIN SMALL LETTER A WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e7 LATIN SMALL LETTER C WITH CEDILLA | 00e6 LATIN SMALL LETTER AE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00e9 LATIN SMALL LETTER E WITH ACUTE | 00e8 LATIN SMALL LETTER E WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00eb LATIN SMALL LETTER E WITH DIAERESIS | 00ea LATIN SMALL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ed LATIN SMALL LETTER I WITH ACUTE | 00ec LATIN SMALL LETTER I WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ef LATIN SMALL LETTER I WITH DIAERESIS | 00ee LATIN SMALL LETTER I WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f1 LATIN SMALL LETTER N WITH TILDE | 00f0 LATIN SMALL LETTER ETH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f3 LATIN SMALL LETTER O WITH ACUTE | 00f2 LATIN SMALL LETTER O WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f5 LATIN SMALL LETTER O WITH TILDE | 00f4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 00f7 DIVISION SIGN | 00f6 LATIN SMALL LETTER O WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00f9 LATIN SMALL LETTER U WITH GRAVE | 00f8 LATIN SMALL LETTER O WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00fb LATIN SMALL LETTER U WITH CIRCUMFLEX | 00fa LATIN SMALL LETTER U WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00fd LATIN SMALL LETTER Y WITH ACUTE | 00fc LATIN SMALL LETTER U WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 00ff LATIN SMALL LETTER Y WITH DIAERESIS | 00fe LATIN SMALL LETTER THORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0101 LATIN SMALL LETTER A WITH MACRON | 0100 LATIN CAPITAL LETTER A WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0103 LATIN SMALL LETTER A WITH BREVE | 0102 LATIN CAPITAL LETTER A WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0105 LATIN SMALL LETTER A WITH OGONEK | 0104 LATIN CAPITAL LETTER A WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0107 LATIN SMALL LETTER C WITH ACUTE | 0106 LATIN CAPITAL LETTER C WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX | 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010b LATIN SMALL LETTER C WITH DOT ABOVE | 010a LATIN CAPITAL LETTER C WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010d LATIN SMALL LETTER C WITH CARON | 010c LATIN CAPITAL LETTER C WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 010f LATIN SMALL LETTER D WITH CARON | 010e LATIN CAPITAL LETTER D WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0111 LATIN SMALL LETTER D WITH STROKE | 0110 LATIN CAPITAL LETTER D WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0113 LATIN SMALL LETTER E WITH MACRON | 0112 LATIN CAPITAL LETTER E WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0115 LATIN SMALL LETTER E WITH BREVE | 0114 LATIN CAPITAL LETTER E WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0117 LATIN SMALL LETTER E WITH DOT ABOVE | 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0119 LATIN SMALL LETTER E WITH OGONEK | 0118 LATIN CAPITAL LETTER E WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011b LATIN SMALL LETTER E WITH CARON | 011a LATIN CAPITAL LETTER E WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011d LATIN SMALL LETTER G WITH CIRCUMFLEX | 011c LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 011f LATIN SMALL LETTER G WITH BREVE | 011e LATIN CAPITAL LETTER G WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0121 LATIN SMALL LETTER G WITH DOT ABOVE | 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0123 LATIN SMALL LETTER G WITH CEDILLA | 0122 LATIN CAPITAL LETTER G WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX | 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0127 LATIN SMALL LETTER H WITH STROKE | 0126 LATIN CAPITAL LETTER H WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0129 LATIN SMALL LETTER I WITH TILDE | 0128 LATIN CAPITAL LETTER I WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012b LATIN SMALL LETTER I WITH MACRON | 012a LATIN CAPITAL LETTER I WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012d LATIN SMALL LETTER I WITH BREVE | 012c LATIN CAPITAL LETTER I WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 012f LATIN SMALL LETTER I WITH OGONEK | 012e LATIN CAPITAL LETTER I WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0131 LATIN SMALL LETTER DOTLESS I | 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0133 LATIN SMALL LIGATURE IJ | 0132 LATIN CAPITAL LIGATURE IJ */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX | 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0137 LATIN SMALL LETTER K WITH CEDILLA | 0136 LATIN CAPITAL LETTER K WITH CEDILLA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0139 LATIN CAPITAL LETTER L WITH ACUTE | 0138 LATIN SMALL LETTER KRA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013b LATIN CAPITAL LETTER L WITH CEDILLA | 013a LATIN SMALL LETTER L WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013d LATIN CAPITAL LETTER L WITH CARON | 013c LATIN SMALL LETTER L WITH CEDILLA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 013f LATIN CAPITAL LETTER L WITH MIDDLE DOT | 013e LATIN SMALL LETTER L WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0141 LATIN CAPITAL LETTER L WITH STROKE | 0140 LATIN SMALL LETTER L WITH MIDDLE DOT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0143 LATIN CAPITAL LETTER N WITH ACUTE | 0142 LATIN SMALL LETTER L WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0145 LATIN CAPITAL LETTER N WITH CEDILLA | 0144 LATIN SMALL LETTER N WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0147 LATIN CAPITAL LETTER N WITH CARON | 0146 LATIN SMALL LETTER N WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0149 LATIN SMALL LETTER N PRECEDED BY APOSTR | 0148 LATIN SMALL LETTER N WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014b LATIN SMALL LETTER ENG | 014a LATIN CAPITAL LETTER ENG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014d LATIN SMALL LETTER O WITH MACRON | 014c LATIN CAPITAL LETTER O WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 014f LATIN SMALL LETTER O WITH BREVE | 014e LATIN CAPITAL LETTER O WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE | 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0153 LATIN SMALL LIGATURE OE | 0152 LATIN CAPITAL LIGATURE OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0155 LATIN SMALL LETTER R WITH ACUTE | 0154 LATIN CAPITAL LETTER R WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0157 LATIN SMALL LETTER R WITH CEDILLA | 0156 LATIN CAPITAL LETTER R WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0159 LATIN SMALL LETTER R WITH CARON | 0158 LATIN CAPITAL LETTER R WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015b LATIN SMALL LETTER S WITH ACUTE | 015a LATIN CAPITAL LETTER S WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015d LATIN SMALL LETTER S WITH CIRCUMFLEX | 015c LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 015f LATIN SMALL LETTER S WITH CEDILLA | 015e LATIN CAPITAL LETTER S WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0161 LATIN SMALL LETTER S WITH CARON | 0160 LATIN CAPITAL LETTER S WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0163 LATIN SMALL LETTER T WITH CEDILLA | 0162 LATIN CAPITAL LETTER T WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0165 LATIN SMALL LETTER T WITH CARON | 0164 LATIN CAPITAL LETTER T WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0167 LATIN SMALL LETTER T WITH STROKE | 0166 LATIN CAPITAL LETTER T WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0169 LATIN SMALL LETTER U WITH TILDE | 0168 LATIN CAPITAL LETTER U WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016b LATIN SMALL LETTER U WITH MACRON | 016a LATIN CAPITAL LETTER U WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016d LATIN SMALL LETTER U WITH BREVE | 016c LATIN CAPITAL LETTER U WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 016f LATIN SMALL LETTER U WITH RING ABOVE | 016e LATIN CAPITAL LETTER U WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE | 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0173 LATIN SMALL LETTER U WITH OGONEK | 0172 LATIN CAPITAL LETTER U WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX | 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX | 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0179 LATIN CAPITAL LETTER Z WITH ACUTE | 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 017b LATIN CAPITAL LETTER Z WITH DOT ABOVE | 017a LATIN SMALL LETTER Z WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 017d LATIN CAPITAL LETTER Z WITH CARON | 017c LATIN SMALL LETTER Z WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 017f LATIN SMALL LETTER LONG S | 017e LATIN SMALL LETTER Z WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0181 LATIN CAPITAL LETTER B WITH HOOK | 0180 LATIN SMALL LETTER B WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0183 LATIN SMALL LETTER B WITH TOPBAR | 0182 LATIN CAPITAL LETTER B WITH TOPBAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0185 LATIN SMALL LETTER TONE SIX | 0184 LATIN CAPITAL LETTER TONE SIX */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0187 LATIN CAPITAL LETTER C WITH HOOK | 0186 LATIN CAPITAL LETTER OPEN O */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0189 LATIN CAPITAL LETTER AFRICAN D | 0188 LATIN SMALL LETTER C WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 018b LATIN CAPITAL LETTER D WITH TOPBAR | 018a LATIN CAPITAL LETTER D WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 018d LATIN SMALL LETTER TURNED DELTA | 018c LATIN SMALL LETTER D WITH TOPBAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 018f LATIN CAPITAL LETTER SCHWA | 018e LATIN CAPITAL LETTER REVERSED E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0191 LATIN CAPITAL LETTER F WITH HOOK | 0190 LATIN CAPITAL LETTER OPEN E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0193 LATIN CAPITAL LETTER G WITH HOOK | 0192 LATIN SMALL LETTER F WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0195 LATIN SMALL LETTER HV | 0194 LATIN CAPITAL LETTER GAMMA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0197 LATIN CAPITAL LETTER I WITH STROKE | 0196 LATIN CAPITAL LETTER IOTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0199 LATIN SMALL LETTER K WITH HOOK | 0198 LATIN CAPITAL LETTER K WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 019b LATIN SMALL LETTER LAMBDA WITH STROKE | 019a LATIN SMALL LETTER L WITH BAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 019d LATIN CAPITAL LETTER N WITH LEFT HOOK | 019c LATIN CAPITAL LETTER TURNED M */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 019f LATIN CAPITAL LETTER O WITH MIDDLE TILD | 019e LATIN SMALL LETTER N WITH LONG RIGHT LE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a1 LATIN SMALL LETTER O WITH HORN | 01a0 LATIN CAPITAL LETTER O WITH HORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a3 LATIN SMALL LETTER OI | 01a2 LATIN CAPITAL LETTER OI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01a5 LATIN SMALL LETTER P WITH HOOK | 01a4 LATIN CAPITAL LETTER P WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01a7 LATIN CAPITAL LETTER TONE TWO | 01a6 LATIN LETTER YR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01a9 LATIN CAPITAL LETTER ESH | 01a8 LATIN SMALL LETTER TONE TWO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01ab LATIN SMALL LETTER T WITH PALATAL HOOK | 01aa LATIN LETTER REVERSED ESH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ad LATIN SMALL LETTER T WITH HOOK | 01ac LATIN CAPITAL LETTER T WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01af LATIN CAPITAL LETTER U WITH HORN | 01ae LATIN CAPITAL LETTER T WITH RETROFLEX H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b1 LATIN CAPITAL LETTER UPSILON | 01b0 LATIN SMALL LETTER U WITH HORN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01b3 LATIN CAPITAL LETTER Y WITH HOOK | 01b2 LATIN CAPITAL LETTER V WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b5 LATIN CAPITAL LETTER Z WITH STROKE | 01b4 LATIN SMALL LETTER Y WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01b7 LATIN CAPITAL LETTER EZH | 01b6 LATIN SMALL LETTER Z WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01b9 LATIN SMALL LETTER EZH REVERSED | 01b8 LATIN CAPITAL LETTER EZH REVERSED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* 01bb LATIN LETTER TWO WITH STROKE | 01ba LATIN SMALL LETTER EZH WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01bd LATIN SMALL LETTER TONE FIVE | 01bc LATIN CAPITAL LETTER TONE FIVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01bf LATIN LETTER WYNN | 01be LATIN LETTER INVERTED GLOTTAL STOP WITH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 01c1 LATIN LETTER LATERAL CLICK | 01c0 LATIN LETTER DENTAL CLICK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 01c3 LATIN LETTER RETROFLEX CLICK | 01c2 LATIN LETTER ALVEOLAR CLICK */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_UPPER, /* 01c5 LATIN CAPITAL LETTER D WITH SMALL LETTE | 01c4 LATIN CAPITAL LETTER DZ WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01c7 LATIN CAPITAL LETTER LJ | 01c6 LATIN SMALL LETTER DZ WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_TITLE, /* 01c9 LATIN SMALL LETTER LJ | 01c8 LATIN CAPITAL LETTER L WITH SMALL LETTE */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_UPPER, /* 01cb LATIN CAPITAL LETTER N WITH SMALL LETTE | 01ca LATIN CAPITAL LETTER NJ */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01cd LATIN CAPITAL LETTER A WITH CARON | 01cc LATIN SMALL LETTER NJ */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01cf LATIN CAPITAL LETTER I WITH CARON | 01ce LATIN SMALL LETTER A WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d1 LATIN CAPITAL LETTER O WITH CARON | 01d0 LATIN SMALL LETTER I WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d3 LATIN CAPITAL LETTER U WITH CARON | 01d2 LATIN SMALL LETTER O WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d5 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d4 LATIN SMALL LETTER U WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d7 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d6 LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01d9 LATIN CAPITAL LETTER U WITH DIAERESIS A | 01d8 LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01db LATIN CAPITAL LETTER U WITH DIAERESIS A | 01da LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 01dd LATIN SMALL LETTER TURNED E | 01dc LATIN SMALL LETTER U WITH DIAERESIS AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01df LATIN SMALL LETTER A WITH DIAERESIS AND | 01de LATIN CAPITAL LETTER A WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e1 LATIN SMALL LETTER A WITH DOT ABOVE AND | 01e0 LATIN CAPITAL LETTER A WITH DOT ABOVE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e3 LATIN SMALL LETTER AE WITH MACRON | 01e2 LATIN CAPITAL LETTER AE WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e5 LATIN SMALL LETTER G WITH STROKE | 01e4 LATIN CAPITAL LETTER G WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e7 LATIN SMALL LETTER G WITH CARON | 01e6 LATIN CAPITAL LETTER G WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01e9 LATIN SMALL LETTER K WITH CARON | 01e8 LATIN CAPITAL LETTER K WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01eb LATIN SMALL LETTER O WITH OGONEK | 01ea LATIN CAPITAL LETTER O WITH OGONEK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ed LATIN SMALL LETTER O WITH OGONEK AND MA | 01ec LATIN CAPITAL LETTER O WITH OGONEK AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ef LATIN SMALL LETTER EZH WITH CARON | 01ee LATIN CAPITAL LETTER EZH WITH CARON */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 01f1 LATIN CAPITAL LETTER DZ | 01f0 LATIN SMALL LETTER J WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_TITLE, /* 01f3 LATIN SMALL LETTER DZ | 01f2 LATIN CAPITAL LETTER D WITH SMALL LETTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01f5 LATIN SMALL LETTER G WITH ACUTE | 01f4 LATIN CAPITAL LETTER G WITH ACUTE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 01f7 LATIN CAPITAL LETTER WYNN | 01f6 LATIN CAPITAL LETTER HWAIR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01f9 LATIN SMALL LETTER N WITH GRAVE | 01f8 LATIN CAPITAL LETTER N WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01fb LATIN SMALL LETTER A WITH RING ABOVE AN | 01fa LATIN CAPITAL LETTER A WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01fd LATIN SMALL LETTER AE WITH ACUTE | 01fc LATIN CAPITAL LETTER AE WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 01ff LATIN SMALL LETTER O WITH STROKE AND AC | 01fe LATIN CAPITAL LETTER O WITH STROKE AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE | 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0203 LATIN SMALL LETTER A WITH INVERTED BREV | 0202 LATIN CAPITAL LETTER A WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE | 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0207 LATIN SMALL LETTER E WITH INVERTED BREV | 0206 LATIN CAPITAL LETTER E WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE | 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020b LATIN SMALL LETTER I WITH INVERTED BREV | 020a LATIN CAPITAL LETTER I WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020d LATIN SMALL LETTER O WITH DOUBLE GRAVE | 020c LATIN CAPITAL LETTER O WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 020f LATIN SMALL LETTER O WITH INVERTED BREV | 020e LATIN CAPITAL LETTER O WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE | 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0213 LATIN SMALL LETTER R WITH INVERTED BREV | 0212 LATIN CAPITAL LETTER R WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE | 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0217 LATIN SMALL LETTER U WITH INVERTED BREV | 0216 LATIN CAPITAL LETTER U WITH INVERTED BR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0219 LATIN SMALL LETTER S WITH COMMA BELOW | 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021b LATIN SMALL LETTER T WITH COMMA BELOW | 021a LATIN CAPITAL LETTER T WITH COMMA BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021d LATIN SMALL LETTER YOGH | 021c LATIN CAPITAL LETTER YOGH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 021f LATIN SMALL LETTER H WITH CARON | 021e LATIN CAPITAL LETTER H WITH CARON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0221 LATIN SMALL LETTER D WITH CURL | 0220 LATIN CAPITAL LETTER N WITH LONG RIGHT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0223 LATIN SMALL LETTER OU | 0222 LATIN CAPITAL LETTER OU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0225 LATIN SMALL LETTER Z WITH HOOK | 0224 LATIN CAPITAL LETTER Z WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0227 LATIN SMALL LETTER A WITH DOT ABOVE | 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0229 LATIN SMALL LETTER E WITH CEDILLA | 0228 LATIN CAPITAL LETTER E WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022b LATIN SMALL LETTER O WITH DIAERESIS AND | 022a LATIN CAPITAL LETTER O WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022d LATIN SMALL LETTER O WITH TILDE AND MAC | 022c LATIN CAPITAL LETTER O WITH TILDE AND M */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 022f LATIN SMALL LETTER O WITH DOT ABOVE | 022e LATIN CAPITAL LETTER O WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND | 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0233 LATIN SMALL LETTER Y WITH MACRON | 0232 LATIN CAPITAL LETTER Y WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0235 LATIN SMALL LETTER N WITH CURL | 0234 LATIN SMALL LETTER L WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0237 LATIN SMALL LETTER DOTLESS J | 0236 LATIN SMALL LETTER T WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0239 LATIN SMALL LETTER QP DIGRAPH | 0238 LATIN SMALL LETTER DB DIGRAPH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 023b LATIN CAPITAL LETTER C WITH STROKE | 023a LATIN CAPITAL LETTER A WITH STROKE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 023d LATIN CAPITAL LETTER L WITH BAR | 023c LATIN SMALL LETTER C WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 023f LATIN SMALL LETTER S WITH SWASH TAIL | 023e LATIN CAPITAL LETTER T WITH DIAGONAL ST */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0241 LATIN CAPITAL LETTER GLOTTAL STOP | 0240 LATIN SMALL LETTER Z WITH SWASH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0243 LATIN CAPITAL LETTER B WITH STROKE | 0242 LATIN SMALL LETTER GLOTTAL STOP */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0245 LATIN CAPITAL LETTER TURNED V | 0244 LATIN CAPITAL LETTER U BAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0247 LATIN SMALL LETTER E WITH STROKE | 0246 LATIN CAPITAL LETTER E WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0249 LATIN SMALL LETTER J WITH STROKE | 0248 LATIN CAPITAL LETTER J WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024b LATIN SMALL LETTER Q WITH HOOK TAIL | 024a LATIN CAPITAL LETTER SMALL Q WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024d LATIN SMALL LETTER R WITH STROKE | 024c LATIN CAPITAL LETTER R WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 024f LATIN SMALL LETTER Y WITH STROKE | 024e LATIN CAPITAL LETTER Y WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0251 LATIN SMALL LETTER ALPHA | 0250 LATIN SMALL LETTER TURNED A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0253 LATIN SMALL LETTER B WITH HOOK | 0252 LATIN SMALL LETTER TURNED ALPHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0255 LATIN SMALL LETTER C WITH CURL | 0254 LATIN SMALL LETTER OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0257 LATIN SMALL LETTER D WITH HOOK | 0256 LATIN SMALL LETTER D WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0259 LATIN SMALL LETTER SCHWA | 0258 LATIN SMALL LETTER REVERSED E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025b LATIN SMALL LETTER OPEN E | 025a LATIN SMALL LETTER SCHWA WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025d LATIN SMALL LETTER REVERSED OPEN E WITH | 025c LATIN SMALL LETTER REVERSED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 025f LATIN SMALL LETTER DOTLESS J WITH STROK | 025e LATIN SMALL LETTER CLOSED REVERSED OPEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0261 LATIN SMALL LETTER SCRIPT G | 0260 LATIN SMALL LETTER G WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0263 LATIN SMALL LETTER GAMMA | 0262 LATIN LETTER SMALL CAPITAL G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0265 LATIN SMALL LETTER TURNED H | 0264 LATIN SMALL LETTER RAMS HORN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0267 LATIN SMALL LETTER HENG WITH HOOK | 0266 LATIN SMALL LETTER H WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0269 LATIN SMALL LETTER IOTA | 0268 LATIN SMALL LETTER I WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026b LATIN SMALL LETTER L WITH MIDDLE TILDE | 026a LATIN LETTER SMALL CAPITAL I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026d LATIN SMALL LETTER L WITH RETROFLEX HOO | 026c LATIN SMALL LETTER L WITH BELT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 026f LATIN SMALL LETTER TURNED M | 026e LATIN SMALL LETTER LEZH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0271 LATIN SMALL LETTER M WITH HOOK | 0270 LATIN SMALL LETTER TURNED M WITH LONG L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0273 LATIN SMALL LETTER N WITH RETROFLEX HOO | 0272 LATIN SMALL LETTER N WITH LEFT HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0275 LATIN SMALL LETTER BARRED O | 0274 LATIN LETTER SMALL CAPITAL N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0277 LATIN SMALL LETTER CLOSED OMEGA | 0276 LATIN LETTER SMALL CAPITAL OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0279 LATIN SMALL LETTER TURNED R | 0278 LATIN SMALL LETTER PHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027b LATIN SMALL LETTER TURNED R WITH HOOK | 027a LATIN SMALL LETTER TURNED R WITH LONG L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027d LATIN SMALL LETTER R WITH TAIL | 027c LATIN SMALL LETTER R WITH LONG LEG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 027f LATIN SMALL LETTER REVERSED R WITH FISH | 027e LATIN SMALL LETTER R WITH FISHHOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0281 LATIN LETTER SMALL CAPITAL INVERTED R | 0280 LATIN LETTER SMALL CAPITAL R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0283 LATIN SMALL LETTER ESH | 0282 LATIN SMALL LETTER S WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0285 LATIN SMALL LETTER SQUAT REVERSED ESH | 0284 LATIN SMALL LETTER DOTLESS J WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0287 LATIN SMALL LETTER TURNED T | 0286 LATIN SMALL LETTER ESH WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0289 LATIN SMALL LETTER U BAR | 0288 LATIN SMALL LETTER T WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028b LATIN SMALL LETTER V WITH HOOK | 028a LATIN SMALL LETTER UPSILON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028d LATIN SMALL LETTER TURNED W | 028c LATIN SMALL LETTER TURNED V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 028f LATIN LETTER SMALL CAPITAL Y | 028e LATIN SMALL LETTER TURNED Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0291 LATIN SMALL LETTER Z WITH CURL | 0290 LATIN SMALL LETTER Z WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0293 LATIN SMALL LETTER EZH WITH CURL | 0292 LATIN SMALL LETTER EZH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_ALPHA, /* 0295 LATIN LETTER PHARYNGEAL VOICED FRICATIV | 0294 LATIN LETTER GLOTTAL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0297 LATIN LETTER STRETCHED C | 0296 LATIN LETTER INVERTED GLOTTAL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0299 LATIN LETTER SMALL CAPITAL B | 0298 LATIN LETTER BILABIAL CLICK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029b LATIN LETTER SMALL CAPITAL G WITH HOOK | 029a LATIN SMALL LETTER CLOSED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029d LATIN SMALL LETTER J WITH CROSSED-TAIL | 029c LATIN LETTER SMALL CAPITAL H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 029f LATIN LETTER SMALL CAPITAL L | 029e LATIN SMALL LETTER TURNED K */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a1 LATIN LETTER GLOTTAL STOP WITH STROKE | 02a0 LATIN SMALL LETTER Q WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a3 LATIN SMALL LETTER DZ DIGRAPH | 02a2 LATIN LETTER REVERSED GLOTTAL STOP WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a5 LATIN SMALL LETTER DZ DIGRAPH WITH CURL | 02a4 LATIN SMALL LETTER DEZH DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a7 LATIN SMALL LETTER TESH DIGRAPH | 02a6 LATIN SMALL LETTER TS DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02a9 LATIN SMALL LETTER FENG DIGRAPH | 02a8 LATIN SMALL LETTER TC DIGRAPH WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02ab LATIN SMALL LETTER LZ DIGRAPH | 02aa LATIN SMALL LETTER LS DIGRAPH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02ad LATIN LETTER BIDENTAL PERCUSSIVE | 02ac LATIN LETTER BILABIAL PERCUSSIVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 02af LATIN SMALL LETTER TURNED H WITH FISHHO | 02ae LATIN SMALL LETTER TURNED H WITH FISHHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b1 MODIFIER LETTER SMALL H WITH HOOK | 02b0 MODIFIER LETTER SMALL H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b3 MODIFIER LETTER SMALL R | 02b2 MODIFIER LETTER SMALL J */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b5 MODIFIER LETTER SMALL TURNED R WITH HOO | 02b4 MODIFIER LETTER SMALL TURNED R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b7 MODIFIER LETTER SMALL W | 02b6 MODIFIER LETTER SMALL CAPITAL INVERTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02b9 MODIFIER LETTER PRIME | 02b8 MODIFIER LETTER SMALL Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bb MODIFIER LETTER TURNED COMMA | 02ba MODIFIER LETTER DOUBLE PRIME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bd MODIFIER LETTER REVERSED COMMA | 02bc MODIFIER LETTER APOSTROPHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02bf MODIFIER LETTER LEFT HALF RING | 02be MODIFIER LETTER RIGHT HALF RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c1 MODIFIER LETTER REVERSED GLOTTAL STOP | 02c0 MODIFIER LETTER GLOTTAL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c3 MODIFIER LETTER RIGHT ARROWHEAD | 02c2 MODIFIER LETTER LEFT ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c5 MODIFIER LETTER DOWN ARROWHEAD | 02c4 MODIFIER LETTER UP ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c7 CARON | 02c6 MODIFIER LETTER CIRCUMFLEX ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02c9 MODIFIER LETTER MACRON | 02c8 MODIFIER LETTER VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cb MODIFIER LETTER GRAVE ACCENT | 02ca MODIFIER LETTER ACUTE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cd MODIFIER LETTER LOW MACRON | 02cc MODIFIER LETTER LOW VERTICAL LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02cf MODIFIER LETTER LOW ACUTE ACCENT | 02ce MODIFIER LETTER LOW GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d1 MODIFIER LETTER HALF TRIANGULAR COLON | 02d0 MODIFIER LETTER TRIANGULAR COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d3 MODIFIER LETTER CENTRED LEFT HALF RING | 02d2 MODIFIER LETTER CENTRED RIGHT HALF RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d5 MODIFIER LETTER DOWN TACK | 02d4 MODIFIER LETTER UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d7 MODIFIER LETTER MINUS SIGN | 02d6 MODIFIER LETTER PLUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02d9 DOT ABOVE | 02d8 BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02db OGONEK | 02da RING ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02dd DOUBLE ACUTE ACCENT | 02dc SMALL TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02df MODIFIER LETTER CROSS ACCENT | 02de MODIFIER LETTER RHOTIC HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e1 MODIFIER LETTER SMALL L | 02e0 MODIFIER LETTER SMALL GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e3 MODIFIER LETTER SMALL X | 02e2 MODIFIER LETTER SMALL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e5 MODIFIER LETTER EXTRA-HIGH TONE BAR | 02e4 MODIFIER LETTER SMALL REVERSED GLOTTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e7 MODIFIER LETTER MID TONE BAR | 02e6 MODIFIER LETTER HIGH TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02e9 MODIFIER LETTER EXTRA-LOW TONE BAR | 02e8 MODIFIER LETTER LOW TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02eb MODIFIER LETTER YANG DEPARTING TONE MAR | 02ea MODIFIER LETTER YIN DEPARTING TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ed MODIFIER LETTER UNASPIRATED | 02ec MODIFIER LETTER VOICING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ef MODIFIER LETTER LOW DOWN ARROWHEAD | 02ee MODIFIER LETTER DOUBLE APOSTROPHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f1 MODIFIER LETTER LOW LEFT ARROWHEAD | 02f0 MODIFIER LETTER LOW UP ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f3 MODIFIER LETTER LOW RING | 02f2 MODIFIER LETTER LOW RIGHT ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACC | 02f4 MODIFIER LETTER MIDDLE GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f7 MODIFIER LETTER LOW TILDE | 02f6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02f9 MODIFIER LETTER BEGIN HIGH TONE | 02f8 MODIFIER LETTER RAISED COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02fb MODIFIER LETTER BEGIN LOW TONE | 02fa MODIFIER LETTER END HIGH TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02fd MODIFIER LETTER SHELF | 02fc MODIFIER LETTER END LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 02ff MODIFIER LETTER LOW LEFT ARROW | 02fe MODIFIER LETTER OPEN SHELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0301 COMBINING ACUTE ACCENT | 0300 COMBINING GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0303 COMBINING TILDE | 0302 COMBINING CIRCUMFLEX ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0305 COMBINING OVERLINE | 0304 COMBINING MACRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0307 COMBINING DOT ABOVE | 0306 COMBINING BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0309 COMBINING HOOK ABOVE | 0308 COMBINING DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030b COMBINING DOUBLE ACUTE ACCENT | 030a COMBINING RING ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030d COMBINING VERTICAL LINE ABOVE | 030c COMBINING CARON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 030f COMBINING DOUBLE GRAVE ACCENT | 030e COMBINING DOUBLE VERTICAL LINE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0311 COMBINING INVERTED BREVE | 0310 COMBINING CANDRABINDU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0313 COMBINING COMMA ABOVE | 0312 COMBINING TURNED COMMA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0315 COMBINING COMMA ABOVE RIGHT | 0314 COMBINING REVERSED COMMA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0317 COMBINING ACUTE ACCENT BELOW | 0316 COMBINING GRAVE ACCENT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0319 COMBINING RIGHT TACK BELOW | 0318 COMBINING LEFT TACK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031b COMBINING HORN | 031a COMBINING LEFT ANGLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031d COMBINING UP TACK BELOW | 031c COMBINING LEFT HALF RING BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 031f COMBINING PLUS SIGN BELOW | 031e COMBINING DOWN TACK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0321 COMBINING PALATALIZED HOOK BELOW | 0320 COMBINING MINUS SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0323 COMBINING DOT BELOW | 0322 COMBINING RETROFLEX HOOK BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0325 COMBINING RING BELOW | 0324 COMBINING DIAERESIS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0327 COMBINING CEDILLA | 0326 COMBINING COMMA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0329 COMBINING VERTICAL LINE BELOW | 0328 COMBINING OGONEK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032b COMBINING INVERTED DOUBLE ARCH BELOW | 032a COMBINING BRIDGE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032d COMBINING CIRCUMFLEX ACCENT BELOW | 032c COMBINING CARON BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 032f COMBINING INVERTED BREVE BELOW | 032e COMBINING BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0331 COMBINING MACRON BELOW | 0330 COMBINING TILDE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0333 COMBINING DOUBLE LOW LINE | 0332 COMBINING LOW LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0335 COMBINING SHORT STROKE OVERLAY | 0334 COMBINING TILDE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0337 COMBINING SHORT SOLIDUS OVERLAY | 0336 COMBINING LONG STROKE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0339 COMBINING RIGHT HALF RING BELOW | 0338 COMBINING LONG SOLIDUS OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033b COMBINING SQUARE BELOW | 033a COMBINING INVERTED BRIDGE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033d COMBINING X ABOVE | 033c COMBINING SEAGULL BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 033f COMBINING DOUBLE OVERLINE | 033e COMBINING VERTICAL TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0341 COMBINING ACUTE TONE MARK | 0340 COMBINING GRAVE TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0343 COMBINING GREEK KORONIS | 0342 COMBINING GREEK PERISPOMENI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0345 COMBINING GREEK YPOGEGRAMMENI | 0344 COMBINING GREEK DIALYTIKA TONOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0347 COMBINING EQUALS SIGN BELOW | 0346 COMBINING BRIDGE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0349 COMBINING LEFT ANGLE BELOW | 0348 COMBINING DOUBLE VERTICAL LINE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034b COMBINING HOMOTHETIC ABOVE | 034a COMBINING NOT TILDE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034d COMBINING LEFT RIGHT ARROW BELOW | 034c COMBINING ALMOST EQUAL TO ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 034f COMBINING GRAPHEME JOINER | 034e COMBINING UPWARDS ARROW BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0351 COMBINING LEFT HALF RING ABOVE | 0350 COMBINING RIGHT ARROWHEAD ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0353 COMBINING X BELOW | 0352 COMBINING FERMATA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0355 COMBINING RIGHT ARROWHEAD BELOW | 0354 COMBINING LEFT ARROWHEAD BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0357 COMBINING RIGHT HALF RING ABOVE | 0356 COMBINING RIGHT ARROWHEAD AND UP ARROWH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0359 COMBINING ASTERISK BELOW | 0358 COMBINING DOT ABOVE RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035b COMBINING ZIGZAG ABOVE | 035a COMBINING DOUBLE RING BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035d COMBINING DOUBLE BREVE | 035c COMBINING DOUBLE BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 035f COMBINING DOUBLE MACRON BELOW | 035e COMBINING DOUBLE MACRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0361 COMBINING DOUBLE INVERTED BREVE | 0360 COMBINING DOUBLE TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0363 COMBINING LATIN SMALL LETTER A | 0362 COMBINING DOUBLE RIGHTWARDS ARROW BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0365 COMBINING LATIN SMALL LETTER I | 0364 COMBINING LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0367 COMBINING LATIN SMALL LETTER U | 0366 COMBINING LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0369 COMBINING LATIN SMALL LETTER D | 0368 COMBINING LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036b COMBINING LATIN SMALL LETTER M | 036a COMBINING LATIN SMALL LETTER H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036d COMBINING LATIN SMALL LETTER T | 036c COMBINING LATIN SMALL LETTER R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 036f COMBINING LATIN SMALL LETTER X | 036e COMBINING LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0371 GREEK SMALL LETTER HETA | 0370 GREEK CAPITAL LETTER HETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0373 GREEK SMALL LETTER ARCHAIC SAMPI | 0372 GREEK CAPITAL LETTER ARCHAIC SAMPI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0375 GREEK LOWER NUMERAL SIGN | 0374 GREEK NUMERAL SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0377 GREEK SMALL LETTER PAMPHYLIAN DIGAMMA | 0376 GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0379 (null) | 0378 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 037b GREEK SMALL REVERSED LUNATE SIGMA SYMBO | 037a GREEK YPOGEGRAMMENI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 037d GREEK SMALL REVERSED DOTTED LUNATE SIGM | 037c GREEK SMALL DOTTED LUNATE SIGMA SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 037f (null) | 037e GREEK QUESTION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0381 (null) | 0380 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0383 (null) | 0382 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0385 GREEK DIALYTIKA TONOS | 0384 GREEK TONOS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* 0387 GREEK ANO TELEIA | 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0389 GREEK CAPITAL LETTER ETA WITH TONOS | 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 038b (null) | 038a GREEK CAPITAL LETTER IOTA WITH TONOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 038d (null) | 038c GREEK CAPITAL LETTER OMICRON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 038f GREEK CAPITAL LETTER OMEGA WITH TONOS | 038e GREEK CAPITAL LETTER UPSILON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 0391 GREEK CAPITAL LETTER ALPHA | 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0393 GREEK CAPITAL LETTER GAMMA | 0392 GREEK CAPITAL LETTER BETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0395 GREEK CAPITAL LETTER EPSILON | 0394 GREEK CAPITAL LETTER DELTA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0397 GREEK CAPITAL LETTER ETA | 0396 GREEK CAPITAL LETTER ZETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0399 GREEK CAPITAL LETTER IOTA | 0398 GREEK CAPITAL LETTER THETA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039b GREEK CAPITAL LETTER LAMDA | 039a GREEK CAPITAL LETTER KAPPA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039d GREEK CAPITAL LETTER NU | 039c GREEK CAPITAL LETTER MU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 039f GREEK CAPITAL LETTER OMICRON | 039e GREEK CAPITAL LETTER XI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a1 GREEK CAPITAL LETTER RHO | 03a0 GREEK CAPITAL LETTER PI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 03a3 GREEK CAPITAL LETTER SIGMA | 03a2 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a5 GREEK CAPITAL LETTER UPSILON | 03a4 GREEK CAPITAL LETTER TAU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a7 GREEK CAPITAL LETTER CHI | 03a6 GREEK CAPITAL LETTER PHI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03a9 GREEK CAPITAL LETTER OMEGA | 03a8 GREEK CAPITAL LETTER PSI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03ab GREEK CAPITAL LETTER UPSILON WITH DIALY | 03aa GREEK CAPITAL LETTER IOTA WITH DIALYTIK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03ad GREEK SMALL LETTER EPSILON WITH TONOS | 03ac GREEK SMALL LETTER ALPHA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03af GREEK SMALL LETTER IOTA WITH TONOS | 03ae GREEK SMALL LETTER ETA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b1 GREEK SMALL LETTER ALPHA | 03b0 GREEK SMALL LETTER UPSILON WITH DIALYTI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b3 GREEK SMALL LETTER GAMMA | 03b2 GREEK SMALL LETTER BETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b5 GREEK SMALL LETTER EPSILON | 03b4 GREEK SMALL LETTER DELTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b7 GREEK SMALL LETTER ETA | 03b6 GREEK SMALL LETTER ZETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03b9 GREEK SMALL LETTER IOTA | 03b8 GREEK SMALL LETTER THETA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bb GREEK SMALL LETTER LAMDA | 03ba GREEK SMALL LETTER KAPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bd GREEK SMALL LETTER NU | 03bc GREEK SMALL LETTER MU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03bf GREEK SMALL LETTER OMICRON | 03be GREEK SMALL LETTER XI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c1 GREEK SMALL LETTER RHO | 03c0 GREEK SMALL LETTER PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c3 GREEK SMALL LETTER SIGMA | 03c2 GREEK SMALL LETTER FINAL SIGMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c5 GREEK SMALL LETTER UPSILON | 03c4 GREEK SMALL LETTER TAU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c7 GREEK SMALL LETTER CHI | 03c6 GREEK SMALL LETTER PHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03c9 GREEK SMALL LETTER OMEGA | 03c8 GREEK SMALL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03cb GREEK SMALL LETTER UPSILON WITH DIALYTI | 03ca GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03cd GREEK SMALL LETTER UPSILON WITH TONOS | 03cc GREEK SMALL LETTER OMICRON WITH TONOS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03cf GREEK CAPITAL KAI SYMBOL | 03ce GREEK SMALL LETTER OMEGA WITH TONOS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03d1 GREEK THETA SYMBOL | 03d0 GREEK BETA SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03d3 GREEK UPSILON WITH ACUTE AND HOOK SYMBO | 03d2 GREEK UPSILON WITH HOOK SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03d5 GREEK PHI SYMBOL | 03d4 GREEK UPSILON WITH DIAERESIS AND HOOK S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03d7 GREEK KAI SYMBOL | 03d6 GREEK PI SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03d9 GREEK SMALL LETTER ARCHAIC KOPPA | 03d8 GREEK LETTER ARCHAIC KOPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03db GREEK SMALL LETTER STIGMA | 03da GREEK LETTER STIGMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03dd GREEK SMALL LETTER DIGAMMA | 03dc GREEK LETTER DIGAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03df GREEK SMALL LETTER KOPPA | 03de GREEK LETTER KOPPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e1 GREEK SMALL LETTER SAMPI | 03e0 GREEK LETTER SAMPI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e3 COPTIC SMALL LETTER SHEI | 03e2 COPTIC CAPITAL LETTER SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e5 COPTIC SMALL LETTER FEI | 03e4 COPTIC CAPITAL LETTER FEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e7 COPTIC SMALL LETTER KHEI | 03e6 COPTIC CAPITAL LETTER KHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03e9 COPTIC SMALL LETTER HORI | 03e8 COPTIC CAPITAL LETTER HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03eb COPTIC SMALL LETTER GANGIA | 03ea COPTIC CAPITAL LETTER GANGIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03ed COPTIC SMALL LETTER SHIMA | 03ec COPTIC CAPITAL LETTER SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03ef COPTIC SMALL LETTER DEI | 03ee COPTIC CAPITAL LETTER DEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03f1 GREEK RHO SYMBOL | 03f0 GREEK KAPPA SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 03f3 GREEK LETTER YOT | 03f2 GREEK LUNATE SIGMA SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03f5 GREEK LUNATE EPSILON SYMBOL | 03f4 GREEK CAPITAL THETA SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 03f7 GREEK CAPITAL LETTER SHO | 03f6 GREEK REVERSED LUNATE EPSILON SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03f9 GREEK CAPITAL LUNATE SIGMA SYMBOL | 03f8 GREEK SMALL LETTER SHO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 03fb GREEK SMALL LETTER SAN | 03fa GREEK CAPITAL LETTER SAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 03fd GREEK CAPITAL REVERSED LUNATE SIGMA SYM | 03fc GREEK RHO WITH STROKE SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 03ff GREEK CAPITAL REVERSED DOTTED LUNATE SI | 03fe GREEK CAPITAL DOTTED LUNATE SIGMA SYMBO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0401 CYRILLIC CAPITAL LETTER IO | 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0403 CYRILLIC CAPITAL LETTER GJE | 0402 CYRILLIC CAPITAL LETTER DJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0405 CYRILLIC CAPITAL LETTER DZE | 0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0407 CYRILLIC CAPITAL LETTER YI | 0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0409 CYRILLIC CAPITAL LETTER LJE | 0408 CYRILLIC CAPITAL LETTER JE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040b CYRILLIC CAPITAL LETTER TSHE | 040a CYRILLIC CAPITAL LETTER NJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040d CYRILLIC CAPITAL LETTER I WITH GRAVE | 040c CYRILLIC CAPITAL LETTER KJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 040f CYRILLIC CAPITAL LETTER DZHE | 040e CYRILLIC CAPITAL LETTER SHORT U */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0411 CYRILLIC CAPITAL LETTER BE | 0410 CYRILLIC CAPITAL LETTER A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0413 CYRILLIC CAPITAL LETTER GHE | 0412 CYRILLIC CAPITAL LETTER VE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0415 CYRILLIC CAPITAL LETTER IE | 0414 CYRILLIC CAPITAL LETTER DE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0417 CYRILLIC CAPITAL LETTER ZE | 0416 CYRILLIC CAPITAL LETTER ZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0419 CYRILLIC CAPITAL LETTER SHORT I | 0418 CYRILLIC CAPITAL LETTER I */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041b CYRILLIC CAPITAL LETTER EL | 041a CYRILLIC CAPITAL LETTER KA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041d CYRILLIC CAPITAL LETTER EN | 041c CYRILLIC CAPITAL LETTER EM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 041f CYRILLIC CAPITAL LETTER PE | 041e CYRILLIC CAPITAL LETTER O */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0421 CYRILLIC CAPITAL LETTER ES | 0420 CYRILLIC CAPITAL LETTER ER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0423 CYRILLIC CAPITAL LETTER U | 0422 CYRILLIC CAPITAL LETTER TE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0425 CYRILLIC CAPITAL LETTER HA | 0424 CYRILLIC CAPITAL LETTER EF */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0427 CYRILLIC CAPITAL LETTER CHE | 0426 CYRILLIC CAPITAL LETTER TSE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0429 CYRILLIC CAPITAL LETTER SHCHA | 0428 CYRILLIC CAPITAL LETTER SHA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042b CYRILLIC CAPITAL LETTER YERU | 042a CYRILLIC CAPITAL LETTER HARD SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042d CYRILLIC CAPITAL LETTER E | 042c CYRILLIC CAPITAL LETTER SOFT SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 042f CYRILLIC CAPITAL LETTER YA | 042e CYRILLIC CAPITAL LETTER YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0431 CYRILLIC SMALL LETTER BE | 0430 CYRILLIC SMALL LETTER A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0433 CYRILLIC SMALL LETTER GHE | 0432 CYRILLIC SMALL LETTER VE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0435 CYRILLIC SMALL LETTER IE | 0434 CYRILLIC SMALL LETTER DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0437 CYRILLIC SMALL LETTER ZE | 0436 CYRILLIC SMALL LETTER ZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0439 CYRILLIC SMALL LETTER SHORT I | 0438 CYRILLIC SMALL LETTER I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043b CYRILLIC SMALL LETTER EL | 043a CYRILLIC SMALL LETTER KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043d CYRILLIC SMALL LETTER EN | 043c CYRILLIC SMALL LETTER EM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 043f CYRILLIC SMALL LETTER PE | 043e CYRILLIC SMALL LETTER O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0441 CYRILLIC SMALL LETTER ES | 0440 CYRILLIC SMALL LETTER ER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0443 CYRILLIC SMALL LETTER U | 0442 CYRILLIC SMALL LETTER TE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0445 CYRILLIC SMALL LETTER HA | 0444 CYRILLIC SMALL LETTER EF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0447 CYRILLIC SMALL LETTER CHE | 0446 CYRILLIC SMALL LETTER TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0449 CYRILLIC SMALL LETTER SHCHA | 0448 CYRILLIC SMALL LETTER SHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044b CYRILLIC SMALL LETTER YERU | 044a CYRILLIC SMALL LETTER HARD SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044d CYRILLIC SMALL LETTER E | 044c CYRILLIC SMALL LETTER SOFT SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 044f CYRILLIC SMALL LETTER YA | 044e CYRILLIC SMALL LETTER YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0451 CYRILLIC SMALL LETTER IO | 0450 CYRILLIC SMALL LETTER IE WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0453 CYRILLIC SMALL LETTER GJE | 0452 CYRILLIC SMALL LETTER DJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0455 CYRILLIC SMALL LETTER DZE | 0454 CYRILLIC SMALL LETTER UKRAINIAN IE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0457 CYRILLIC SMALL LETTER YI | 0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0459 CYRILLIC SMALL LETTER LJE | 0458 CYRILLIC SMALL LETTER JE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045b CYRILLIC SMALL LETTER TSHE | 045a CYRILLIC SMALL LETTER NJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045d CYRILLIC SMALL LETTER I WITH GRAVE | 045c CYRILLIC SMALL LETTER KJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 045f CYRILLIC SMALL LETTER DZHE | 045e CYRILLIC SMALL LETTER SHORT U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0461 CYRILLIC SMALL LETTER OMEGA | 0460 CYRILLIC CAPITAL LETTER OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0463 CYRILLIC SMALL LETTER YAT | 0462 CYRILLIC CAPITAL LETTER YAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0465 CYRILLIC SMALL LETTER IOTIFIED E | 0464 CYRILLIC CAPITAL LETTER IOTIFIED E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0467 CYRILLIC SMALL LETTER LITTLE YUS | 0466 CYRILLIC CAPITAL LETTER LITTLE YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0469 CYRILLIC SMALL LETTER IOTIFIED LITTLE Y | 0468 CYRILLIC CAPITAL LETTER IOTIFIED LITTLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046b CYRILLIC SMALL LETTER BIG YUS | 046a CYRILLIC CAPITAL LETTER BIG YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046d CYRILLIC SMALL LETTER IOTIFIED BIG YUS | 046c CYRILLIC CAPITAL LETTER IOTIFIED BIG YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 046f CYRILLIC SMALL LETTER KSI | 046e CYRILLIC CAPITAL LETTER KSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0471 CYRILLIC SMALL LETTER PSI | 0470 CYRILLIC CAPITAL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0473 CYRILLIC SMALL LETTER FITA | 0472 CYRILLIC CAPITAL LETTER FITA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0475 CYRILLIC SMALL LETTER IZHITSA | 0474 CYRILLIC CAPITAL LETTER IZHITSA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUB | 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0479 CYRILLIC SMALL LETTER UK | 0478 CYRILLIC CAPITAL LETTER UK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047b CYRILLIC SMALL LETTER ROUND OMEGA | 047a CYRILLIC CAPITAL LETTER ROUND OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047d CYRILLIC SMALL LETTER OMEGA WITH TITLO | 047c CYRILLIC CAPITAL LETTER OMEGA WITH TITL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 047f CYRILLIC SMALL LETTER OT | 047e CYRILLIC CAPITAL LETTER OT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0481 CYRILLIC SMALL LETTER KOPPA | 0480 CYRILLIC CAPITAL LETTER KOPPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0483 COMBINING CYRILLIC TITLO | 0482 CYRILLIC THOUSANDS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0485 COMBINING CYRILLIC DASIA PNEUMATA | 0484 COMBINING CYRILLIC PALATALIZATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0487 COMBINING CYRILLIC POKRYTIE | 0486 COMBINING CYRILLIC PSILI PNEUMATA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0489 COMBINING CYRILLIC MILLIONS SIGN | 0488 COMBINING CYRILLIC HUNDRED THOUSANDS SI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048b CYRILLIC SMALL LETTER SHORT I WITH TAIL | 048a CYRILLIC CAPITAL LETTER SHORT I WITH TA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048d CYRILLIC SMALL LETTER SEMISOFT SIGN | 048c CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 048f CYRILLIC SMALL LETTER ER WITH TICK | 048e CYRILLIC CAPITAL LETTER ER WITH TICK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0491 CYRILLIC SMALL LETTER GHE WITH UPTURN | 0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0493 CYRILLIC SMALL LETTER GHE WITH STROKE | 0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0495 CYRILLIC SMALL LETTER GHE WITH MIDDLE H | 0494 CYRILLIC CAPITAL LETTER GHE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDE | 0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0499 CYRILLIC SMALL LETTER ZE WITH DESCENDER | 0498 CYRILLIC CAPITAL LETTER ZE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049b CYRILLIC SMALL LETTER KA WITH DESCENDER | 049a CYRILLIC CAPITAL LETTER KA WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049d CYRILLIC SMALL LETTER KA WITH VERTICAL | 049c CYRILLIC CAPITAL LETTER KA WITH VERTICA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 049f CYRILLIC SMALL LETTER KA WITH STROKE | 049e CYRILLIC CAPITAL LETTER KA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a1 CYRILLIC SMALL LETTER BASHKIR KA | 04a0 CYRILLIC CAPITAL LETTER BASHKIR KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a3 CYRILLIC SMALL LETTER EN WITH DESCENDER | 04a2 CYRILLIC CAPITAL LETTER EN WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a5 CYRILLIC SMALL LIGATURE EN GHE | 04a4 CYRILLIC CAPITAL LIGATURE EN GHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a7 CYRILLIC SMALL LETTER PE WITH MIDDLE HO | 04a6 CYRILLIC CAPITAL LETTER PE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04a9 CYRILLIC SMALL LETTER ABKHASIAN HA | 04a8 CYRILLIC CAPITAL LETTER ABKHASIAN HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ab CYRILLIC SMALL LETTER ES WITH DESCENDER | 04aa CYRILLIC CAPITAL LETTER ES WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ad CYRILLIC SMALL LETTER TE WITH DESCENDER | 04ac CYRILLIC CAPITAL LETTER TE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04af CYRILLIC SMALL LETTER STRAIGHT U | 04ae CYRILLIC CAPITAL LETTER STRAIGHT U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b1 CYRILLIC SMALL LETTER STRAIGHT U WITH S | 04b0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b3 CYRILLIC SMALL LETTER HA WITH DESCENDER | 04b2 CYRILLIC CAPITAL LETTER HA WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b5 CYRILLIC SMALL LIGATURE TE TSE | 04b4 CYRILLIC CAPITAL LIGATURE TE TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b7 CYRILLIC SMALL LETTER CHE WITH DESCENDE | 04b6 CYRILLIC CAPITAL LETTER CHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04b9 CYRILLIC SMALL LETTER CHE WITH VERTICAL | 04b8 CYRILLIC CAPITAL LETTER CHE WITH VERTIC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bb CYRILLIC SMALL LETTER SHHA | 04ba CYRILLIC CAPITAL LETTER SHHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bd CYRILLIC SMALL LETTER ABKHASIAN CHE | 04bc CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04bf CYRILLIC SMALL LETTER ABKHASIAN CHE WIT | 04be CYRILLIC CAPITAL LETTER ABKHASIAN CHE W */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 04c1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE | 04c0 CYRILLIC LETTER PALOCHKA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c3 CYRILLIC CAPITAL LETTER KA WITH HOOK | 04c2 CYRILLIC SMALL LETTER ZHE WITH BREVE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c5 CYRILLIC CAPITAL LETTER EL WITH TAIL | 04c4 CYRILLIC SMALL LETTER KA WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c7 CYRILLIC CAPITAL LETTER EN WITH HOOK | 04c6 CYRILLIC SMALL LETTER EL WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04c9 CYRILLIC CAPITAL LETTER EN WITH TAIL | 04c8 CYRILLIC SMALL LETTER EN WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04cb CYRILLIC CAPITAL LETTER KHAKASSIAN CHE | 04ca CYRILLIC SMALL LETTER EN WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 04cd CYRILLIC CAPITAL LETTER EM WITH TAIL | 04cc CYRILLIC SMALL LETTER KHAKASSIAN CHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 04cf CYRILLIC SMALL LETTER PALOCHKA | 04ce CYRILLIC SMALL LETTER EM WITH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d1 CYRILLIC SMALL LETTER A WITH BREVE | 04d0 CYRILLIC CAPITAL LETTER A WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d3 CYRILLIC SMALL LETTER A WITH DIAERESIS | 04d2 CYRILLIC CAPITAL LETTER A WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d5 CYRILLIC SMALL LIGATURE A IE | 04d4 CYRILLIC CAPITAL LIGATURE A IE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d7 CYRILLIC SMALL LETTER IE WITH BREVE | 04d6 CYRILLIC CAPITAL LETTER IE WITH BREVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04d9 CYRILLIC SMALL LETTER SCHWA | 04d8 CYRILLIC CAPITAL LETTER SCHWA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04db CYRILLIC SMALL LETTER SCHWA WITH DIAERE | 04da CYRILLIC CAPITAL LETTER SCHWA WITH DIAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04dd CYRILLIC SMALL LETTER ZHE WITH DIAERESI | 04dc CYRILLIC CAPITAL LETTER ZHE WITH DIAERE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04df CYRILLIC SMALL LETTER ZE WITH DIAERESIS | 04de CYRILLIC CAPITAL LETTER ZE WITH DIAERES */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e1 CYRILLIC SMALL LETTER ABKHASIAN DZE | 04e0 CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e3 CYRILLIC SMALL LETTER I WITH MACRON | 04e2 CYRILLIC CAPITAL LETTER I WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e5 CYRILLIC SMALL LETTER I WITH DIAERESIS | 04e4 CYRILLIC CAPITAL LETTER I WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e7 CYRILLIC SMALL LETTER O WITH DIAERESIS | 04e6 CYRILLIC CAPITAL LETTER O WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04e9 CYRILLIC SMALL LETTER BARRED O | 04e8 CYRILLIC CAPITAL LETTER BARRED O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04eb CYRILLIC SMALL LETTER BARRED O WITH DIA | 04ea CYRILLIC CAPITAL LETTER BARRED O WITH D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ed CYRILLIC SMALL LETTER E WITH DIAERESIS | 04ec CYRILLIC CAPITAL LETTER E WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ef CYRILLIC SMALL LETTER U WITH MACRON | 04ee CYRILLIC CAPITAL LETTER U WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f1 CYRILLIC SMALL LETTER U WITH DIAERESIS | 04f0 CYRILLIC CAPITAL LETTER U WITH DIAERESI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f3 CYRILLIC SMALL LETTER U WITH DOUBLE ACU | 04f2 CYRILLIC CAPITAL LETTER U WITH DOUBLE A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f5 CYRILLIC SMALL LETTER CHE WITH DIAERESI | 04f4 CYRILLIC CAPITAL LETTER CHE WITH DIAERE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f7 CYRILLIC SMALL LETTER GHE WITH DESCENDE | 04f6 CYRILLIC CAPITAL LETTER GHE WITH DESCEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04f9 CYRILLIC SMALL LETTER YERU WITH DIAERES | 04f8 CYRILLIC CAPITAL LETTER YERU WITH DIAER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04fb CYRILLIC SMALL LETTER GHE WITH STROKE A | 04fa CYRILLIC CAPITAL LETTER GHE WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04fd CYRILLIC SMALL LETTER HA WITH HOOK | 04fc CYRILLIC CAPITAL LETTER HA WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 04ff CYRILLIC SMALL LETTER HA WITH STROKE | 04fe CYRILLIC CAPITAL LETTER HA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0501 CYRILLIC SMALL LETTER KOMI DE | 0500 CYRILLIC CAPITAL LETTER KOMI DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0503 CYRILLIC SMALL LETTER KOMI DJE | 0502 CYRILLIC CAPITAL LETTER KOMI DJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0505 CYRILLIC SMALL LETTER KOMI ZJE | 0504 CYRILLIC CAPITAL LETTER KOMI ZJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0507 CYRILLIC SMALL LETTER KOMI DZJE | 0506 CYRILLIC CAPITAL LETTER KOMI DZJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0509 CYRILLIC SMALL LETTER KOMI LJE | 0508 CYRILLIC CAPITAL LETTER KOMI LJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050b CYRILLIC SMALL LETTER KOMI NJE | 050a CYRILLIC CAPITAL LETTER KOMI NJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050d CYRILLIC SMALL LETTER KOMI SJE | 050c CYRILLIC CAPITAL LETTER KOMI SJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 050f CYRILLIC SMALL LETTER KOMI TJE | 050e CYRILLIC CAPITAL LETTER KOMI TJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0511 CYRILLIC SMALL LETTER REVERSED ZE | 0510 CYRILLIC CAPITAL LETTER REVERSED ZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0513 CYRILLIC SMALL LETTER EL WITH HOOK | 0512 CYRILLIC CAPITAL LETTER EL WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0515 CYRILLIC SMALL LETTER LHA | 0514 CYRILLIC CAPITAL LETTER LHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0517 CYRILLIC SMALL LETTER RHA | 0516 CYRILLIC CAPITAL LETTER RHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0519 CYRILLIC SMALL LETTER YAE | 0518 CYRILLIC CAPITAL LETTER YAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051b CYRILLIC SMALL LETTER QA | 051a CYRILLIC CAPITAL LETTER QA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051d CYRILLIC SMALL LETTER WE | 051c CYRILLIC CAPITAL LETTER WE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 051f CYRILLIC SMALL LETTER ALEUT KA | 051e CYRILLIC CAPITAL LETTER ALEUT KA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0521 CYRILLIC SMALL LETTER EL WITH MIDDLE HO | 0520 CYRILLIC CAPITAL LETTER EL WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0523 CYRILLIC SMALL LETTER EN WITH MIDDLE HO | 0522 CYRILLIC CAPITAL LETTER EN WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0525 CYRILLIC SMALL LETTER PE WITH DESCENDER | 0524 CYRILLIC CAPITAL LETTER PE WITH DESCEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 0527 CYRILLIC SMALL LETTER SHHA WITH DESCEND | 0526 CYRILLIC CAPITAL LETTER SHHA WITH DESCE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0529 (null) | 0528 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052b (null) | 052a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052d (null) | 052c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 052f (null) | 052e (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 0531 ARMENIAN CAPITAL LETTER AYB | 0530 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0533 ARMENIAN CAPITAL LETTER GIM | 0532 ARMENIAN CAPITAL LETTER BEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0535 ARMENIAN CAPITAL LETTER ECH | 0534 ARMENIAN CAPITAL LETTER DA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0537 ARMENIAN CAPITAL LETTER EH | 0536 ARMENIAN CAPITAL LETTER ZA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0539 ARMENIAN CAPITAL LETTER TO | 0538 ARMENIAN CAPITAL LETTER ET */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053b ARMENIAN CAPITAL LETTER INI | 053a ARMENIAN CAPITAL LETTER ZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053d ARMENIAN CAPITAL LETTER XEH | 053c ARMENIAN CAPITAL LETTER LIWN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 053f ARMENIAN CAPITAL LETTER KEN | 053e ARMENIAN CAPITAL LETTER CA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0541 ARMENIAN CAPITAL LETTER JA | 0540 ARMENIAN CAPITAL LETTER HO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0543 ARMENIAN CAPITAL LETTER CHEH | 0542 ARMENIAN CAPITAL LETTER GHAD */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0545 ARMENIAN CAPITAL LETTER YI | 0544 ARMENIAN CAPITAL LETTER MEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0547 ARMENIAN CAPITAL LETTER SHA | 0546 ARMENIAN CAPITAL LETTER NOW */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0549 ARMENIAN CAPITAL LETTER CHA | 0548 ARMENIAN CAPITAL LETTER VO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054b ARMENIAN CAPITAL LETTER JHEH | 054a ARMENIAN CAPITAL LETTER PEH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054d ARMENIAN CAPITAL LETTER SEH | 054c ARMENIAN CAPITAL LETTER RA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 054f ARMENIAN CAPITAL LETTER TIWN | 054e ARMENIAN CAPITAL LETTER VEW */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0551 ARMENIAN CAPITAL LETTER CO | 0550 ARMENIAN CAPITAL LETTER REH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0553 ARMENIAN CAPITAL LETTER PIWR | 0552 ARMENIAN CAPITAL LETTER YIWN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 0555 ARMENIAN CAPITAL LETTER OH | 0554 ARMENIAN CAPITAL LETTER KEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 0557 (null) | 0556 ARMENIAN CAPITAL LETTER FEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0559 ARMENIAN MODIFIER LETTER LEFT HALF RING | 0558 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055b ARMENIAN EMPHASIS MARK | 055a ARMENIAN APOSTROPHE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055d ARMENIAN COMMA | 055c ARMENIAN EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 055f ARMENIAN ABBREVIATION MARK | 055e ARMENIAN QUESTION MARK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 0561 ARMENIAN SMALL LETTER AYB | 0560 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0563 ARMENIAN SMALL LETTER GIM | 0562 ARMENIAN SMALL LETTER BEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0565 ARMENIAN SMALL LETTER ECH | 0564 ARMENIAN SMALL LETTER DA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0567 ARMENIAN SMALL LETTER EH | 0566 ARMENIAN SMALL LETTER ZA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0569 ARMENIAN SMALL LETTER TO | 0568 ARMENIAN SMALL LETTER ET */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056b ARMENIAN SMALL LETTER INI | 056a ARMENIAN SMALL LETTER ZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056d ARMENIAN SMALL LETTER XEH | 056c ARMENIAN SMALL LETTER LIWN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 056f ARMENIAN SMALL LETTER KEN | 056e ARMENIAN SMALL LETTER CA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0571 ARMENIAN SMALL LETTER JA | 0570 ARMENIAN SMALL LETTER HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0573 ARMENIAN SMALL LETTER CHEH | 0572 ARMENIAN SMALL LETTER GHAD */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0575 ARMENIAN SMALL LETTER YI | 0574 ARMENIAN SMALL LETTER MEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0577 ARMENIAN SMALL LETTER SHA | 0576 ARMENIAN SMALL LETTER NOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0579 ARMENIAN SMALL LETTER CHA | 0578 ARMENIAN SMALL LETTER VO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057b ARMENIAN SMALL LETTER JHEH | 057a ARMENIAN SMALL LETTER PEH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057d ARMENIAN SMALL LETTER SEH | 057c ARMENIAN SMALL LETTER RA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 057f ARMENIAN SMALL LETTER TIWN | 057e ARMENIAN SMALL LETTER VEW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0581 ARMENIAN SMALL LETTER CO | 0580 ARMENIAN SMALL LETTER REH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0583 ARMENIAN SMALL LETTER PIWR | 0582 ARMENIAN SMALL LETTER YIWN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0585 ARMENIAN SMALL LETTER OH | 0584 ARMENIAN SMALL LETTER KEH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 0587 ARMENIAN SMALL LIGATURE ECH YIWN | 0586 ARMENIAN SMALL LETTER FEH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 0589 ARMENIAN FULL STOP | 0588 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 058b (null) | 058a ARMENIAN HYPHEN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 058d (null) | 058c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 058f ARMENIAN DRAM SIGN | 058e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0591 HEBREW ACCENT ETNAHTA | 0590 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0593 HEBREW ACCENT SHALSHELET | 0592 HEBREW ACCENT SEGOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0595 HEBREW ACCENT ZAQEF GADOL | 0594 HEBREW ACCENT ZAQEF QATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0597 HEBREW ACCENT REVIA | 0596 HEBREW ACCENT TIPEHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0599 HEBREW ACCENT PASHTA | 0598 HEBREW ACCENT ZARQA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059b HEBREW ACCENT TEVIR | 059a HEBREW ACCENT YETIV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059d HEBREW ACCENT GERESH MUQDAM | 059c HEBREW ACCENT GERESH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 059f HEBREW ACCENT QARNEY PARA | 059e HEBREW ACCENT GERSHAYIM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a1 HEBREW ACCENT PAZER | 05a0 HEBREW ACCENT TELISHA GEDOLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a3 HEBREW ACCENT MUNAH | 05a2 HEBREW ACCENT ATNAH HAFUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a5 HEBREW ACCENT MERKHA | 05a4 HEBREW ACCENT MAHAPAKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a7 HEBREW ACCENT DARGA | 05a6 HEBREW ACCENT MERKHA KEFULA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05a9 HEBREW ACCENT TELISHA QETANA | 05a8 HEBREW ACCENT QADMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05ab HEBREW ACCENT OLE | 05aa HEBREW ACCENT YERAH BEN YOMO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05ad HEBREW ACCENT DEHI | 05ac HEBREW ACCENT ILUY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05af HEBREW MARK MASORA CIRCLE | 05ae HEBREW ACCENT ZINOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b1 HEBREW POINT HATAF SEGOL | 05b0 HEBREW POINT SHEVA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b3 HEBREW POINT HATAF QAMATS | 05b2 HEBREW POINT HATAF PATAH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b5 HEBREW POINT TSERE | 05b4 HEBREW POINT HIRIQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b7 HEBREW POINT PATAH | 05b6 HEBREW POINT SEGOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05b9 HEBREW POINT HOLAM | 05b8 HEBREW POINT QAMATS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05bb HEBREW POINT QUBUTS | 05ba HEBREW POINT HOLAM HASER FOR VAV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05bd HEBREW POINT METEG | 05bc HEBREW POINT DAGESH OR MAPIQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05bf HEBREW POINT RAFE | 05be HEBREW PUNCTUATION MAQAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05c1 HEBREW POINT SHIN DOT | 05c0 HEBREW PUNCTUATION PASEQ */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 05c3 HEBREW PUNCTUATION SOF PASUQ | 05c2 HEBREW POINT SIN DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 05c5 HEBREW MARK LOWER DOT | 05c4 HEBREW MARK UPPER DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 05c7 HEBREW POINT QAMATS QATAN | 05c6 HEBREW PUNCTUATION NUN HAFUKHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05c9 (null) | 05c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cb (null) | 05ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cd (null) | 05cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05cf (null) | 05ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d1 HEBREW LETTER BET | 05d0 HEBREW LETTER ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d3 HEBREW LETTER DALET | 05d2 HEBREW LETTER GIMEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d5 HEBREW LETTER VAV | 05d4 HEBREW LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d7 HEBREW LETTER HET | 05d6 HEBREW LETTER ZAYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05d9 HEBREW LETTER YOD | 05d8 HEBREW LETTER TET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05db HEBREW LETTER KAF | 05da HEBREW LETTER FINAL KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05dd HEBREW LETTER FINAL MEM | 05dc HEBREW LETTER LAMED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05df HEBREW LETTER FINAL NUN | 05de HEBREW LETTER MEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e1 HEBREW LETTER SAMEKH | 05e0 HEBREW LETTER NUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e3 HEBREW LETTER FINAL PE | 05e2 HEBREW LETTER AYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e5 HEBREW LETTER FINAL TSADI | 05e4 HEBREW LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e7 HEBREW LETTER QOF | 05e6 HEBREW LETTER TSADI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05e9 HEBREW LETTER SHIN | 05e8 HEBREW LETTER RESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 05eb (null) | 05ea HEBREW LETTER TAV */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ed (null) | 05ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ef (null) | 05ee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 05f1 HEBREW LIGATURE YIDDISH VAV YOD | 05f0 HEBREW LIGATURE YIDDISH DOUBLE VAV */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 05f3 HEBREW PUNCTUATION GERESH | 05f2 HEBREW LIGATURE YIDDISH DOUBLE YOD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 05f5 (null) | 05f4 HEBREW PUNCTUATION GERSHAYIM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05f7 (null) | 05f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05f9 (null) | 05f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05fb (null) | 05fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05fd (null) | 05fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 05ff (null) | 05fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0601 ARABIC SIGN SANAH | 0600 ARABIC NUMBER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0603 ARABIC SIGN SAFHA | 0602 ARABIC FOOTNOTE MARKER */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0605 (null) | 0604 ARABIC SIGN SAMVAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0607 ARABIC-INDIC FOURTH ROOT | 0606 ARABIC-INDIC CUBE ROOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0609 ARABIC-INDIC PER MILLE SIGN | 0608 ARABIC RAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 060b AFGHANI SIGN | 060a ARABIC-INDIC PER TEN THOUSAND SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 060d ARABIC DATE SEPARATOR | 060c ARABIC COMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 060f ARABIC SIGN MISRA | 060e ARABIC POETIC VERSE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0611 ARABIC SIGN ALAYHE ASSALLAM | 0610 ARABIC SIGN SALLALLAHOU ALAYHE WASSALLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0613 ARABIC SIGN RADI ALLAHOU ANHU | 0612 ARABIC SIGN RAHMATULLAH ALAYHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0615 ARABIC SMALL HIGH TAH | 0614 ARABIC SIGN TAKHALLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0617 ARABIC SMALL HIGH ZAIN | 0616 ARABIC SMALL HIGH LIGATURE ALEF WITH LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0619 ARABIC SMALL DAMMA | 0618 ARABIC SMALL FATHA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 061b ARABIC SEMICOLON | 061a ARABIC SMALL KASRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 061d (null) | 061c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 061f ARABIC QUESTION MARK | 061e ARABIC TRIPLE DOT PUNCTUATION MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0621 ARABIC LETTER HAMZA | 0620 ARABIC LETTER KASHMIRI YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0623 ARABIC LETTER ALEF WITH HAMZA ABOVE | 0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0625 ARABIC LETTER ALEF WITH HAMZA BELOW | 0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0627 ARABIC LETTER ALEF | 0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0629 ARABIC LETTER TEH MARBUTA | 0628 ARABIC LETTER BEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062b ARABIC LETTER THEH | 062a ARABIC LETTER TEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062d ARABIC LETTER HAH | 062c ARABIC LETTER JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 062f ARABIC LETTER DAL | 062e ARABIC LETTER KHAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0631 ARABIC LETTER REH | 0630 ARABIC LETTER THAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0633 ARABIC LETTER SEEN | 0632 ARABIC LETTER ZAIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0635 ARABIC LETTER SAD | 0634 ARABIC LETTER SHEEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0637 ARABIC LETTER TAH | 0636 ARABIC LETTER DAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0639 ARABIC LETTER AIN | 0638 ARABIC LETTER ZAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063b ARABIC LETTER KEHEH WITH TWO DOTS ABOVE | 063a ARABIC LETTER GHAIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063d ARABIC LETTER FARSI YEH WITH INVERTED V | 063c ARABIC LETTER KEHEH WITH THREE DOTS BEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 063f ARABIC LETTER FARSI YEH WITH THREE DOTS | 063e ARABIC LETTER FARSI YEH WITH TWO DOTS A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0641 ARABIC LETTER FEH | 0640 ARABIC TATWEEL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0643 ARABIC LETTER KAF | 0642 ARABIC LETTER QAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0645 ARABIC LETTER MEEM | 0644 ARABIC LETTER LAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0647 ARABIC LETTER HEH | 0646 ARABIC LETTER NOON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0649 ARABIC LETTER ALEF MAKSURA | 0648 ARABIC LETTER WAW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 064b ARABIC FATHATAN | 064a ARABIC LETTER YEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 064d ARABIC KASRATAN | 064c ARABIC DAMMATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 064f ARABIC DAMMA | 064e ARABIC FATHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0651 ARABIC SHADDA | 0650 ARABIC KASRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0653 ARABIC MADDAH ABOVE | 0652 ARABIC SUKUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0655 ARABIC HAMZA BELOW | 0654 ARABIC HAMZA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0657 ARABIC INVERTED DAMMA | 0656 ARABIC SUBSCRIPT ALEF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0659 ARABIC ZWARAKAY | 0658 ARABIC MARK NOON GHUNNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065b ARABIC VOWEL SIGN INVERTED SMALL V ABOV | 065a ARABIC VOWEL SIGN SMALL V ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065d ARABIC REVERSED DAMMA | 065c ARABIC VOWEL SIGN DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 065f ARABIC WAVY HAMZA BELOW | 065e ARABIC FATHA WITH TWO DOTS */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0661 ARABIC-INDIC DIGIT ONE | 0660 ARABIC-INDIC DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0663 ARABIC-INDIC DIGIT THREE | 0662 ARABIC-INDIC DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0665 ARABIC-INDIC DIGIT FIVE | 0664 ARABIC-INDIC DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0667 ARABIC-INDIC DIGIT SEVEN | 0666 ARABIC-INDIC DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0669 ARABIC-INDIC DIGIT NINE | 0668 ARABIC-INDIC DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 066b ARABIC DECIMAL SEPARATOR | 066a ARABIC PERCENT SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 066d ARABIC FIVE POINTED STAR | 066c ARABIC THOUSANDS SEPARATOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 066f ARABIC LETTER DOTLESS QAF | 066e ARABIC LETTER DOTLESS BEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0671 ARABIC LETTER ALEF WASLA | 0670 ARABIC LETTER SUPERSCRIPT ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0673 ARABIC LETTER ALEF WITH WAVY HAMZA BELO | 0672 ARABIC LETTER ALEF WITH WAVY HAMZA ABOV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0675 ARABIC LETTER HIGH HAMZA ALEF | 0674 ARABIC LETTER HIGH HAMZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0677 ARABIC LETTER U WITH HAMZA ABOVE | 0676 ARABIC LETTER HIGH HAMZA WAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0679 ARABIC LETTER TTEH | 0678 ARABIC LETTER HIGH HAMZA YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067b ARABIC LETTER BEEH | 067a ARABIC LETTER TTEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067d ARABIC LETTER TEH WITH THREE DOTS ABOVE | 067c ARABIC LETTER TEH WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 067f ARABIC LETTER TEHEH | 067e ARABIC LETTER PEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0681 ARABIC LETTER HAH WITH HAMZA ABOVE | 0680 ARABIC LETTER BEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0683 ARABIC LETTER NYEH | 0682 ARABIC LETTER HAH WITH TWO DOTS VERTICA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0685 ARABIC LETTER HAH WITH THREE DOTS ABOVE | 0684 ARABIC LETTER DYEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0687 ARABIC LETTER TCHEHEH | 0686 ARABIC LETTER TCHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0689 ARABIC LETTER DAL WITH RING | 0688 ARABIC LETTER DDAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068b ARABIC LETTER DAL WITH DOT BELOW AND SM | 068a ARABIC LETTER DAL WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068d ARABIC LETTER DDAHAL | 068c ARABIC LETTER DAHAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 068f ARABIC LETTER DAL WITH THREE DOTS ABOVE | 068e ARABIC LETTER DUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0691 ARABIC LETTER RREH | 0690 ARABIC LETTER DAL WITH FOUR DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0693 ARABIC LETTER REH WITH RING | 0692 ARABIC LETTER REH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0695 ARABIC LETTER REH WITH SMALL V BELOW | 0694 ARABIC LETTER REH WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0697 ARABIC LETTER REH WITH TWO DOTS ABOVE | 0696 ARABIC LETTER REH WITH DOT BELOW AND DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0699 ARABIC LETTER REH WITH FOUR DOTS ABOVE | 0698 ARABIC LETTER JEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069b ARABIC LETTER SEEN WITH THREE DOTS BELO | 069a ARABIC LETTER SEEN WITH DOT BELOW AND D */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069d ARABIC LETTER SAD WITH TWO DOTS BELOW | 069c ARABIC LETTER SEEN WITH THREE DOTS BELO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 069f ARABIC LETTER TAH WITH THREE DOTS ABOVE | 069e ARABIC LETTER SAD WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a1 ARABIC LETTER DOTLESS FEH | 06a0 ARABIC LETTER AIN WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a3 ARABIC LETTER FEH WITH DOT BELOW | 06a2 ARABIC LETTER FEH WITH DOT MOVED BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a5 ARABIC LETTER FEH WITH THREE DOTS BELOW | 06a4 ARABIC LETTER VEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a7 ARABIC LETTER QAF WITH DOT ABOVE | 06a6 ARABIC LETTER PEHEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06a9 ARABIC LETTER KEHEH | 06a8 ARABIC LETTER QAF WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ab ARABIC LETTER KAF WITH RING | 06aa ARABIC LETTER SWASH KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ad ARABIC LETTER NG | 06ac ARABIC LETTER KAF WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06af ARABIC LETTER GAF | 06ae ARABIC LETTER KAF WITH THREE DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b1 ARABIC LETTER NGOEH | 06b0 ARABIC LETTER GAF WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b3 ARABIC LETTER GUEH | 06b2 ARABIC LETTER GAF WITH TWO DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b5 ARABIC LETTER LAM WITH SMALL V | 06b4 ARABIC LETTER GAF WITH THREE DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b7 ARABIC LETTER LAM WITH THREE DOTS ABOVE | 06b6 ARABIC LETTER LAM WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06b9 ARABIC LETTER NOON WITH DOT BELOW | 06b8 ARABIC LETTER LAM WITH THREE DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bb ARABIC LETTER RNOON | 06ba ARABIC LETTER NOON GHUNNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bd ARABIC LETTER NOON WITH THREE DOTS ABOV | 06bc ARABIC LETTER NOON WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06bf ARABIC LETTER TCHEH WITH DOT ABOVE | 06be ARABIC LETTER HEH DOACHASHMEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c1 ARABIC LETTER HEH GOAL | 06c0 ARABIC LETTER HEH WITH YEH ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c3 ARABIC LETTER TEH MARBUTA GOAL | 06c2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c5 ARABIC LETTER KIRGHIZ OE | 06c4 ARABIC LETTER WAW WITH RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c7 ARABIC LETTER U | 06c6 ARABIC LETTER OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06c9 ARABIC LETTER KIRGHIZ YU | 06c8 ARABIC LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cb ARABIC LETTER VE | 06ca ARABIC LETTER WAW WITH TWO DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cd ARABIC LETTER YEH WITH TAIL | 06cc ARABIC LETTER FARSI YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06cf ARABIC LETTER WAW WITH DOT ABOVE | 06ce ARABIC LETTER YEH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06d1 ARABIC LETTER YEH WITH THREE DOTS BELOW | 06d0 ARABIC LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06d3 ARABIC LETTER YEH BARREE WITH HAMZA ABO | 06d2 ARABIC LETTER YEH BARREE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 06d5 ARABIC LETTER AE | 06d4 ARABIC FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06d7 ARABIC SMALL HIGH LIGATURE QAF WITH LAM | 06d6 ARABIC SMALL HIGH LIGATURE SAD WITH LAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06d9 ARABIC SMALL HIGH LAM ALEF | 06d8 ARABIC SMALL HIGH MEEM INITIAL FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06db ARABIC SMALL HIGH THREE DOTS | 06da ARABIC SMALL HIGH JEEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06dd ARABIC END OF AYAH | 06dc ARABIC SMALL HIGH SEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06df ARABIC SMALL HIGH ROUNDED ZERO | 06de ARABIC START OF RUB EL HIZB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e1 ARABIC SMALL HIGH DOTLESS HEAD OF KHAH | 06e0 ARABIC SMALL HIGH UPRIGHT RECTANGULAR Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e3 ARABIC SMALL LOW SEEN | 06e2 ARABIC SMALL HIGH MEEM ISOLATED FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e5 ARABIC SMALL WAW | 06e4 ARABIC SMALL HIGH MADDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e7 ARABIC SMALL HIGH YEH | 06e6 ARABIC SMALL YEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06e9 ARABIC PLACE OF SAJDAH | 06e8 ARABIC SMALL HIGH NOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06eb ARABIC EMPTY CENTRE HIGH STOP | 06ea ARABIC EMPTY CENTRE LOW STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 06ed ARABIC SMALL LOW MEEM | 06ec ARABIC ROUNDED HIGH STOP WITH FILLED CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06ef ARABIC LETTER REH WITH INVERTED V | 06ee ARABIC LETTER DAL WITH INVERTED V */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f1 EXTENDED ARABIC-INDIC DIGIT ONE | 06f0 EXTENDED ARABIC-INDIC DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f3 EXTENDED ARABIC-INDIC DIGIT THREE | 06f2 EXTENDED ARABIC-INDIC DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f5 EXTENDED ARABIC-INDIC DIGIT FIVE | 06f4 EXTENDED ARABIC-INDIC DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f7 EXTENDED ARABIC-INDIC DIGIT SEVEN | 06f6 EXTENDED ARABIC-INDIC DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 06f9 EXTENDED ARABIC-INDIC DIGIT NINE | 06f8 EXTENDED ARABIC-INDIC DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 06fb ARABIC LETTER DAD WITH DOT BELOW | 06fa ARABIC LETTER SHEEN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 06fd ARABIC SIGN SINDHI AMPERSAND | 06fc ARABIC LETTER GHAIN WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 06ff ARABIC LETTER HEH WITH INVERTED V | 06fe ARABIC SIGN SINDHI POSTPOSITION MEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0701 SYRIAC SUPRALINEAR FULL STOP | 0700 SYRIAC END OF PARAGRAPH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0703 SYRIAC SUPRALINEAR COLON | 0702 SYRIAC SUBLINEAR FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0705 SYRIAC HORIZONTAL COLON | 0704 SYRIAC SUBLINEAR COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0707 SYRIAC COLON SKEWED RIGHT | 0706 SYRIAC COLON SKEWED LEFT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0709 SYRIAC SUBLINEAR COLON SKEWED RIGHT | 0708 SYRIAC SUPRALINEAR COLON SKEWED LEFT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 070b SYRIAC HARKLEAN OBELUS | 070a SYRIAC CONTRACTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 070d SYRIAC HARKLEAN ASTERISCUS | 070c SYRIAC HARKLEAN METOBELUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 070f SYRIAC ABBREVIATION MARK | 070e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0711 SYRIAC LETTER SUPERSCRIPT ALAPH | 0710 SYRIAC LETTER ALAPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0713 SYRIAC LETTER GAMAL | 0712 SYRIAC LETTER BETH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0715 SYRIAC LETTER DALATH | 0714 SYRIAC LETTER GAMAL GARSHUNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0717 SYRIAC LETTER HE | 0716 SYRIAC LETTER DOTLESS DALATH RISH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0719 SYRIAC LETTER ZAIN | 0718 SYRIAC LETTER WAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071b SYRIAC LETTER TETH | 071a SYRIAC LETTER HETH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071d SYRIAC LETTER YUDH | 071c SYRIAC LETTER TETH GARSHUNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 071f SYRIAC LETTER KAPH | 071e SYRIAC LETTER YUDH HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0721 SYRIAC LETTER MIM | 0720 SYRIAC LETTER LAMADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0723 SYRIAC LETTER SEMKATH | 0722 SYRIAC LETTER NUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0725 SYRIAC LETTER E | 0724 SYRIAC LETTER FINAL SEMKATH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0727 SYRIAC LETTER REVERSED PE | 0726 SYRIAC LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0729 SYRIAC LETTER QAPH | 0728 SYRIAC LETTER SADHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072b SYRIAC LETTER SHIN | 072a SYRIAC LETTER RISH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072d SYRIAC LETTER PERSIAN BHETH | 072c SYRIAC LETTER TAW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 072f SYRIAC LETTER PERSIAN DHALATH | 072e SYRIAC LETTER PERSIAN GHAMAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0731 SYRIAC PTHAHA BELOW | 0730 SYRIAC PTHAHA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0733 SYRIAC ZQAPHA ABOVE | 0732 SYRIAC PTHAHA DOTTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0735 SYRIAC ZQAPHA DOTTED | 0734 SYRIAC ZQAPHA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0737 SYRIAC RBASA BELOW | 0736 SYRIAC RBASA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0739 SYRIAC DOTTED ZLAMA ANGULAR | 0738 SYRIAC DOTTED ZLAMA HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073b SYRIAC HBASA BELOW | 073a SYRIAC HBASA ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073d SYRIAC ESASA ABOVE | 073c SYRIAC HBASA-ESASA DOTTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 073f SYRIAC RWAHA | 073e SYRIAC ESASA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0741 SYRIAC QUSHSHAYA | 0740 SYRIAC FEMININE DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0743 SYRIAC TWO VERTICAL DOTS ABOVE | 0742 SYRIAC RUKKAKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0745 SYRIAC THREE DOTS ABOVE | 0744 SYRIAC TWO VERTICAL DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0747 SYRIAC OBLIQUE LINE ABOVE | 0746 SYRIAC THREE DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0749 SYRIAC MUSIC | 0748 SYRIAC OBLIQUE LINE BELOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 074b (null) | 074a SYRIAC BARREKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 074d SYRIAC LETTER SOGDIAN ZHAIN | 074c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 074f SYRIAC LETTER SOGDIAN FE | 074e SYRIAC LETTER SOGDIAN KHAPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0751 ARABIC LETTER BEH WITH DOT BELOW AND TH | 0750 ARABIC LETTER BEH WITH THREE DOTS HORIZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0753 ARABIC LETTER BEH WITH THREE DOTS POINT | 0752 ARABIC LETTER BEH WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0755 ARABIC LETTER BEH WITH INVERTED SMALL V | 0754 ARABIC LETTER BEH WITH TWO DOTS BELOW A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0757 ARABIC LETTER HAH WITH TWO DOTS ABOVE | 0756 ARABIC LETTER BEH WITH SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0759 ARABIC LETTER DAL WITH TWO DOTS VERTICA | 0758 ARABIC LETTER HAH WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075b ARABIC LETTER REH WITH STROKE | 075a ARABIC LETTER DAL WITH INVERTED SMALL V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075d ARABIC LETTER AIN WITH TWO DOTS ABOVE | 075c ARABIC LETTER SEEN WITH FOUR DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 075f ARABIC LETTER AIN WITH TWO DOTS VERTICA | 075e ARABIC LETTER AIN WITH THREE DOTS POINT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0761 ARABIC LETTER FEH WITH THREE DOTS POINT | 0760 ARABIC LETTER FEH WITH TWO DOTS BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0763 ARABIC LETTER KEHEH WITH THREE DOTS ABO | 0762 ARABIC LETTER KEHEH WITH DOT ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0765 ARABIC LETTER MEEM WITH DOT ABOVE | 0764 ARABIC LETTER KEHEH WITH THREE DOTS POI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0767 ARABIC LETTER NOON WITH TWO DOTS BELOW | 0766 ARABIC LETTER MEEM WITH DOT BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0769 ARABIC LETTER NOON WITH SMALL V | 0768 ARABIC LETTER NOON WITH SMALL TAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076b ARABIC LETTER REH WITH TWO DOTS VERTICA | 076a ARABIC LETTER LAM WITH BAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076d ARABIC LETTER SEEN WITH TWO DOTS VERTIC | 076c ARABIC LETTER REH WITH HAMZA ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 076f ARABIC LETTER HAH WITH SMALL ARABIC LET | 076e ARABIC LETTER HAH WITH SMALL ARABIC LET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0771 ARABIC LETTER REH WITH SMALL ARABIC LET | 0770 ARABIC LETTER SEEN WITH SMALL ARABIC LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0773 ARABIC LETTER ALEF WITH EXTENDED ARABIC | 0772 ARABIC LETTER HAH WITH SMALL ARABIC LET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0775 ARABIC LETTER FARSI YEH WITH EXTENDED A | 0774 ARABIC LETTER ALEF WITH EXTENDED ARABIC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0777 ARABIC LETTER FARSI YEH WITH EXTENDED A | 0776 ARABIC LETTER FARSI YEH WITH EXTENDED A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0779 ARABIC LETTER WAW WITH EXTENDED ARABIC- | 0778 ARABIC LETTER WAW WITH EXTENDED ARABIC- */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077b ARABIC LETTER YEH BARREE WITH EXTENDED | 077a ARABIC LETTER YEH BARREE WITH EXTENDED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077d ARABIC LETTER SEEN WITH EXTENDED ARABIC | 077c ARABIC LETTER HAH WITH EXTENDED ARABIC- */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 077f ARABIC LETTER KAF WITH TWO DOTS ABOVE | 077e ARABIC LETTER SEEN WITH INVERTED V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0781 THAANA LETTER SHAVIYANI | 0780 THAANA LETTER HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0783 THAANA LETTER RAA | 0782 THAANA LETTER NOONU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0785 THAANA LETTER LHAVIYANI | 0784 THAANA LETTER BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0787 THAANA LETTER ALIFU | 0786 THAANA LETTER KAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0789 THAANA LETTER MEEMU | 0788 THAANA LETTER VAAVU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078b THAANA LETTER DHAALU | 078a THAANA LETTER FAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078d THAANA LETTER LAAMU | 078c THAANA LETTER THAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 078f THAANA LETTER GNAVIYANI | 078e THAANA LETTER GAAFU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0791 THAANA LETTER DAVIYANI | 0790 THAANA LETTER SEENU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0793 THAANA LETTER TAVIYANI | 0792 THAANA LETTER ZAVIYANI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0795 THAANA LETTER PAVIYANI | 0794 THAANA LETTER YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0797 THAANA LETTER CHAVIYANI | 0796 THAANA LETTER JAVIYANI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0799 THAANA LETTER HHAA | 0798 THAANA LETTER TTAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079b THAANA LETTER THAALU | 079a THAANA LETTER KHAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079d THAANA LETTER SHEENU | 079c THAANA LETTER ZAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 079f THAANA LETTER DAADHU | 079e THAANA LETTER SAADHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a1 THAANA LETTER ZO | 07a0 THAANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a3 THAANA LETTER GHAINU | 07a2 THAANA LETTER AINU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07a5 THAANA LETTER WAAVU | 07a4 THAANA LETTER QAAFU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07a7 THAANA AABAAFILI | 07a6 THAANA ABAFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07a9 THAANA EEBEEFILI | 07a8 THAANA IBIFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ab THAANA OOBOOFILI | 07aa THAANA UBUFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ad THAANA EYBEYFILI | 07ac THAANA EBEFILI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07af THAANA OABOAFILI | 07ae THAANA OBOFILI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 07b1 THAANA LETTER NAA | 07b0 THAANA SUKUN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b3 (null) | 07b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b5 (null) | 07b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b7 (null) | 07b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07b9 (null) | 07b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bb (null) | 07ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bd (null) | 07bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07bf (null) | 07be (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c1 NKO DIGIT ONE | 07c0 NKO DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c3 NKO DIGIT THREE | 07c2 NKO DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c5 NKO DIGIT FIVE | 07c4 NKO DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c7 NKO DIGIT SEVEN | 07c6 NKO DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 07c9 NKO DIGIT NINE | 07c8 NKO DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cb NKO LETTER EE | 07ca NKO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cd NKO LETTER E | 07cc NKO LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07cf NKO LETTER OO | 07ce NKO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d1 NKO LETTER DAGBASINNA | 07d0 NKO LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d3 NKO LETTER BA | 07d2 NKO LETTER N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d5 NKO LETTER TA | 07d4 NKO LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d7 NKO LETTER CHA | 07d6 NKO LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07d9 NKO LETTER RA | 07d8 NKO LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07db NKO LETTER SA | 07da NKO LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07dd NKO LETTER FA | 07dc NKO LETTER GBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07df NKO LETTER LA | 07de NKO LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e1 NKO LETTER MA | 07e0 NKO LETTER NA WOLOSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e3 NKO LETTER NA | 07e2 NKO LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e5 NKO LETTER WA | 07e4 NKO LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e7 NKO LETTER NYA WOLOSO | 07e6 NKO LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 07e9 NKO LETTER JONA CHA | 07e8 NKO LETTER JONA JA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 07eb NKO COMBINING SHORT HIGH TONE | 07ea NKO LETTER JONA RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ed NKO COMBINING SHORT RISING TONE | 07ec NKO COMBINING SHORT LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07ef NKO COMBINING LONG HIGH TONE | 07ee NKO COMBINING LONG DESCENDING TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f1 NKO COMBINING LONG RISING TONE | 07f0 NKO COMBINING LONG LOW TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f3 NKO COMBINING DOUBLE DOT ABOVE | 07f2 NKO COMBINING NASALIZATION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 07f5 NKO LOW TONE APOSTROPHE | 07f4 NKO HIGH TONE APOSTROPHE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 07f7 NKO SYMBOL GBAKURUNEN | 07f6 NKO SYMBOL OO DENNEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 07f9 NKO EXCLAMATION MARK | 07f8 NKO COMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 07fb (null) | 07fa NKO LAJANYALAN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07fd (null) | 07fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 07ff (null) | 07fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0801 SAMARITAN LETTER BIT | 0800 SAMARITAN LETTER ALAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0803 SAMARITAN LETTER DALAT | 0802 SAMARITAN LETTER GAMAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0805 SAMARITAN LETTER BAA | 0804 SAMARITAN LETTER IY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0807 SAMARITAN LETTER IT | 0806 SAMARITAN LETTER ZEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0809 SAMARITAN LETTER YUT | 0808 SAMARITAN LETTER TIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080b SAMARITAN LETTER LABAT | 080a SAMARITAN LETTER KAAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080d SAMARITAN LETTER NUN | 080c SAMARITAN LETTER MIM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 080f SAMARITAN LETTER IN | 080e SAMARITAN LETTER SINGAAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0811 SAMARITAN LETTER TSAADIY | 0810 SAMARITAN LETTER FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0813 SAMARITAN LETTER RISH | 0812 SAMARITAN LETTER QUF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0815 SAMARITAN LETTER TAAF | 0814 SAMARITAN LETTER SHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0817 SAMARITAN MARK IN-ALAF | 0816 SAMARITAN MARK IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0819 SAMARITAN MARK DAGESH | 0818 SAMARITAN MARK OCCLUSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081b SAMARITAN MARK EPENTHETIC YUT | 081a SAMARITAN MODIFIER LETTER EPENTHETIC YU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081d SAMARITAN VOWEL SIGN E | 081c SAMARITAN VOWEL SIGN LONG E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 081f SAMARITAN VOWEL SIGN LONG AA | 081e SAMARITAN VOWEL SIGN OVERLONG AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0821 SAMARITAN VOWEL SIGN OVERLONG A | 0820 SAMARITAN VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0823 SAMARITAN VOWEL SIGN A | 0822 SAMARITAN VOWEL SIGN LONG A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0825 SAMARITAN VOWEL SIGN SHORT A | 0824 SAMARITAN MODIFIER LETTER SHORT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0827 SAMARITAN VOWEL SIGN U | 0826 SAMARITAN VOWEL SIGN LONG U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0829 SAMARITAN VOWEL SIGN LONG I | 0828 SAMARITAN MODIFIER LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 082b SAMARITAN VOWEL SIGN O | 082a SAMARITAN VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 082d SAMARITAN MARK NEQUDAA | 082c SAMARITAN VOWEL SIGN SUKUN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 082f (null) | 082e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0831 SAMARITAN PUNCTUATION AFSAAQ | 0830 SAMARITAN PUNCTUATION NEQUDAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0833 SAMARITAN PUNCTUATION BAU | 0832 SAMARITAN PUNCTUATION ANGED */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0835 SAMARITAN PUNCTUATION SHIYYAALAA | 0834 SAMARITAN PUNCTUATION ATMAAU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0837 SAMARITAN PUNCTUATION MELODIC QITSA | 0836 SAMARITAN ABBREVIATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0839 SAMARITAN PUNCTUATION QITSA | 0838 SAMARITAN PUNCTUATION ZIQAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 083b SAMARITAN PUNCTUATION TURU | 083a SAMARITAN PUNCTUATION ZAEF */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 083d SAMARITAN PUNCTUATION SOF MASHFAAT | 083c SAMARITAN PUNCTUATION ARKAANU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 083f (null) | 083e SAMARITAN PUNCTUATION ANNAAU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0841 MANDAIC LETTER AB | 0840 MANDAIC LETTER HALQA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0843 MANDAIC LETTER AD | 0842 MANDAIC LETTER AG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0845 MANDAIC LETTER USHENNA | 0844 MANDAIC LETTER AH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0847 MANDAIC LETTER IT | 0846 MANDAIC LETTER AZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0849 MANDAIC LETTER AKSA | 0848 MANDAIC LETTER ATT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084b MANDAIC LETTER AL | 084a MANDAIC LETTER AK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084d MANDAIC LETTER AN | 084c MANDAIC LETTER AM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 084f MANDAIC LETTER IN | 084e MANDAIC LETTER AS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0851 MANDAIC LETTER ASZ | 0850 MANDAIC LETTER AP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0853 MANDAIC LETTER AR | 0852 MANDAIC LETTER AQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0855 MANDAIC LETTER AT | 0854 MANDAIC LETTER ASH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0857 MANDAIC LETTER KAD | 0856 MANDAIC LETTER DUSHENNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0859 MANDAIC AFFRICATION MARK | 0858 MANDAIC LETTER AIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 085b MANDAIC GEMINATION MARK | 085a MANDAIC VOCALIZATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 085d (null) | 085c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 085f (null) | 085e MANDAIC PUNCTUATION */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0861 (null) | 0860 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0863 (null) | 0862 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0865 (null) | 0864 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0867 (null) | 0866 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0869 (null) | 0868 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086b (null) | 086a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086d (null) | 086c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 086f (null) | 086e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0871 (null) | 0870 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0873 (null) | 0872 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0875 (null) | 0874 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0877 (null) | 0876 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0879 (null) | 0878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087b (null) | 087a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087d (null) | 087c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 087f (null) | 087e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0881 (null) | 0880 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0883 (null) | 0882 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0885 (null) | 0884 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0887 (null) | 0886 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0889 (null) | 0888 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088b (null) | 088a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088d (null) | 088c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 088f (null) | 088e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0891 (null) | 0890 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0893 (null) | 0892 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0895 (null) | 0894 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0897 (null) | 0896 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0899 (null) | 0898 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089b (null) | 089a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089d (null) | 089c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 089f (null) | 089e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 08a1 (null) | 08a0 ARABIC LETTER BEH WITH SMALL V BELOW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a3 ARABIC LETTER TAH WITH TWO DOTS ABOVE | 08a2 ARABIC LETTER JEEM WITH TWO DOTS ABOVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a5 ARABIC LETTER QAF WITH DOT BELOW | 08a4 ARABIC LETTER FEH WITH DOT BELOW AND TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a7 ARABIC LETTER MEEM WITH THREE DOTS ABOV | 08a6 ARABIC LETTER LAM WITH DOUBLE BAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08a9 ARABIC LETTER YEH WITH TWO DOTS BELOW A | 08a8 ARABIC LETTER YEH WITH TWO DOTS BELOW A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 08ab ARABIC LETTER WAW WITH DOT WITHIN | 08aa ARABIC LETTER REH WITH LOOP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 08ad (null) | 08ac ARABIC LETTER ROHINGYA YEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08af (null) | 08ae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b1 (null) | 08b0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b3 (null) | 08b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b5 (null) | 08b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b7 (null) | 08b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08b9 (null) | 08b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bb (null) | 08ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bd (null) | 08bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08bf (null) | 08be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c1 (null) | 08c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c3 (null) | 08c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c5 (null) | 08c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c7 (null) | 08c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08c9 (null) | 08c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cb (null) | 08ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cd (null) | 08cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08cf (null) | 08ce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d1 (null) | 08d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d3 (null) | 08d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d5 (null) | 08d4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d7 (null) | 08d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08d9 (null) | 08d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08db (null) | 08da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08dd (null) | 08dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08df (null) | 08de (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08e1 (null) | 08e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 08e3 (null) | 08e2 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e5 ARABIC CURLY DAMMA | 08e4 ARABIC CURLY FATHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e7 ARABIC CURLY FATHATAN | 08e6 ARABIC CURLY KASRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08e9 ARABIC CURLY KASRATAN | 08e8 ARABIC CURLY DAMMATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08eb ARABIC TONE TWO DOTS ABOVE | 08ea ARABIC TONE ONE DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08ed ARABIC TONE ONE DOT BELOW | 08ec ARABIC TONE LOOP ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08ef ARABIC TONE LOOP BELOW | 08ee ARABIC TONE TWO DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f1 ARABIC OPEN DAMMATAN | 08f0 ARABIC OPEN FATHATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f3 ARABIC SMALL HIGH WAW | 08f2 ARABIC OPEN KASRATAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f5 ARABIC FATHA WITH DOT ABOVE | 08f4 ARABIC FATHA WITH RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f7 ARABIC LEFT ARROWHEAD ABOVE | 08f6 ARABIC KASRA WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08f9 ARABIC LEFT ARROWHEAD BELOW | 08f8 ARABIC RIGHT ARROWHEAD ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08fb ARABIC DOUBLE RIGHT ARROWHEAD ABOVE | 08fa ARABIC RIGHT ARROWHEAD BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 08fd ARABIC RIGHT ARROWHEAD ABOVE WITH DOT | 08fc ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WIT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 08ff (null) | 08fe ARABIC DAMMA WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0901 DEVANAGARI SIGN CANDRABINDU | 0900 DEVANAGARI SIGN INVERTED CANDRABINDU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0903 DEVANAGARI SIGN VISARGA | 0902 DEVANAGARI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0905 DEVANAGARI LETTER A | 0904 DEVANAGARI LETTER SHORT A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0907 DEVANAGARI LETTER I | 0906 DEVANAGARI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0909 DEVANAGARI LETTER U | 0908 DEVANAGARI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090b DEVANAGARI LETTER VOCALIC R | 090a DEVANAGARI LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090d DEVANAGARI LETTER CANDRA E | 090c DEVANAGARI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 090f DEVANAGARI LETTER E | 090e DEVANAGARI LETTER SHORT E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0911 DEVANAGARI LETTER CANDRA O | 0910 DEVANAGARI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0913 DEVANAGARI LETTER O | 0912 DEVANAGARI LETTER SHORT O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0915 DEVANAGARI LETTER KA | 0914 DEVANAGARI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0917 DEVANAGARI LETTER GA | 0916 DEVANAGARI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0919 DEVANAGARI LETTER NGA | 0918 DEVANAGARI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091b DEVANAGARI LETTER CHA | 091a DEVANAGARI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091d DEVANAGARI LETTER JHA | 091c DEVANAGARI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 091f DEVANAGARI LETTER TTA | 091e DEVANAGARI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0921 DEVANAGARI LETTER DDA | 0920 DEVANAGARI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0923 DEVANAGARI LETTER NNA | 0922 DEVANAGARI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0925 DEVANAGARI LETTER THA | 0924 DEVANAGARI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0927 DEVANAGARI LETTER DHA | 0926 DEVANAGARI LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0929 DEVANAGARI LETTER NNNA | 0928 DEVANAGARI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092b DEVANAGARI LETTER PHA | 092a DEVANAGARI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092d DEVANAGARI LETTER BHA | 092c DEVANAGARI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 092f DEVANAGARI LETTER YA | 092e DEVANAGARI LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0931 DEVANAGARI LETTER RRA | 0930 DEVANAGARI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0933 DEVANAGARI LETTER LLA | 0932 DEVANAGARI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0935 DEVANAGARI LETTER VA | 0934 DEVANAGARI LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0937 DEVANAGARI LETTER SSA | 0936 DEVANAGARI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0939 DEVANAGARI LETTER HA | 0938 DEVANAGARI LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 093b DEVANAGARI VOWEL SIGN OOE | 093a DEVANAGARI VOWEL SIGN OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 093d DEVANAGARI SIGN AVAGRAHA | 093c DEVANAGARI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 093f DEVANAGARI VOWEL SIGN I | 093e DEVANAGARI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0941 DEVANAGARI VOWEL SIGN U | 0940 DEVANAGARI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0943 DEVANAGARI VOWEL SIGN VOCALIC R | 0942 DEVANAGARI VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0945 DEVANAGARI VOWEL SIGN CANDRA E | 0944 DEVANAGARI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0947 DEVANAGARI VOWEL SIGN E | 0946 DEVANAGARI VOWEL SIGN SHORT E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0949 DEVANAGARI VOWEL SIGN CANDRA O | 0948 DEVANAGARI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094b DEVANAGARI VOWEL SIGN O | 094a DEVANAGARI VOWEL SIGN SHORT O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094d DEVANAGARI SIGN VIRAMA | 094c DEVANAGARI VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 094f DEVANAGARI VOWEL SIGN AW | 094e DEVANAGARI VOWEL SIGN PRISHTHAMATRA E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0951 DEVANAGARI STRESS SIGN UDATTA | 0950 DEVANAGARI OM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0953 DEVANAGARI GRAVE ACCENT | 0952 DEVANAGARI STRESS SIGN ANUDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0955 DEVANAGARI VOWEL SIGN CANDRA LONG E | 0954 DEVANAGARI ACUTE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0957 DEVANAGARI VOWEL SIGN UUE | 0956 DEVANAGARI VOWEL SIGN UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0959 DEVANAGARI LETTER KHHA | 0958 DEVANAGARI LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095b DEVANAGARI LETTER ZA | 095a DEVANAGARI LETTER GHHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095d DEVANAGARI LETTER RHA | 095c DEVANAGARI LETTER DDDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 095f DEVANAGARI LETTER YYA | 095e DEVANAGARI LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0961 DEVANAGARI LETTER VOCALIC LL | 0960 DEVANAGARI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0963 DEVANAGARI VOWEL SIGN VOCALIC LL | 0962 DEVANAGARI VOWEL SIGN VOCALIC L */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0965 DEVANAGARI DOUBLE DANDA | 0964 DEVANAGARI DANDA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0967 DEVANAGARI DIGIT ONE | 0966 DEVANAGARI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0969 DEVANAGARI DIGIT THREE | 0968 DEVANAGARI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096b DEVANAGARI DIGIT FIVE | 096a DEVANAGARI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096d DEVANAGARI DIGIT SEVEN | 096c DEVANAGARI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 096f DEVANAGARI DIGIT NINE | 096e DEVANAGARI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0971 DEVANAGARI SIGN HIGH SPACING DOT | 0970 DEVANAGARI ABBREVIATION SIGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0973 DEVANAGARI LETTER OE | 0972 DEVANAGARI LETTER CANDRA A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0975 DEVANAGARI LETTER AW | 0974 DEVANAGARI LETTER OOE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0977 DEVANAGARI LETTER UUE | 0976 DEVANAGARI LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0979 DEVANAGARI LETTER ZHA | 0978 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097b DEVANAGARI LETTER GGA | 097a DEVANAGARI LETTER HEAVY YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097d DEVANAGARI LETTER GLOTTAL STOP | 097c DEVANAGARI LETTER JJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 097f DEVANAGARI LETTER BBA | 097e DEVANAGARI LETTER DDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0981 BENGALI SIGN CANDRABINDU | 0980 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0983 BENGALI SIGN VISARGA | 0982 BENGALI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0985 BENGALI LETTER A | 0984 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0987 BENGALI LETTER I | 0986 BENGALI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0989 BENGALI LETTER U | 0988 BENGALI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 098b BENGALI LETTER VOCALIC R | 098a BENGALI LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 098d (null) | 098c BENGALI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 098f BENGALI LETTER E | 098e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0991 (null) | 0990 BENGALI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0993 BENGALI LETTER O | 0992 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0995 BENGALI LETTER KA | 0994 BENGALI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0997 BENGALI LETTER GA | 0996 BENGALI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0999 BENGALI LETTER NGA | 0998 BENGALI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099b BENGALI LETTER CHA | 099a BENGALI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099d BENGALI LETTER JHA | 099c BENGALI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 099f BENGALI LETTER TTA | 099e BENGALI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a1 BENGALI LETTER DDA | 09a0 BENGALI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a3 BENGALI LETTER NNA | 09a2 BENGALI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a5 BENGALI LETTER THA | 09a4 BENGALI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09a7 BENGALI LETTER DHA | 09a6 BENGALI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09a9 (null) | 09a8 BENGALI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09ab BENGALI LETTER PHA | 09aa BENGALI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09ad BENGALI LETTER BHA | 09ac BENGALI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09af BENGALI LETTER YA | 09ae BENGALI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09b1 (null) | 09b0 BENGALI LETTER RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09b3 (null) | 09b2 BENGALI LETTER LA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09b5 (null) | 09b4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09b7 BENGALI LETTER SSA | 09b6 BENGALI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09b9 BENGALI LETTER HA | 09b8 BENGALI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09bb (null) | 09ba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 09bd BENGALI SIGN AVAGRAHA | 09bc BENGALI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09bf BENGALI VOWEL SIGN I | 09be BENGALI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09c1 BENGALI VOWEL SIGN U | 09c0 BENGALI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09c3 BENGALI VOWEL SIGN VOCALIC R | 09c2 BENGALI VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 09c5 (null) | 09c4 BENGALI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09c7 BENGALI VOWEL SIGN E | 09c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 09c9 (null) | 09c8 BENGALI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09cb BENGALI VOWEL SIGN O | 09ca (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09cd BENGALI SIGN VIRAMA | 09cc BENGALI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 09cf (null) | 09ce BENGALI LETTER KHANDA TA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d1 (null) | 09d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d3 (null) | 09d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d5 (null) | 09d4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 09d7 BENGALI AU LENGTH MARK | 09d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09d9 (null) | 09d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09db (null) | 09da (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09dd BENGALI LETTER RHA | 09dc BENGALI LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 09df BENGALI LETTER YYA | 09de (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09e1 BENGALI LETTER VOCALIC LL | 09e0 BENGALI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09e3 BENGALI VOWEL SIGN VOCALIC LL | 09e2 BENGALI VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09e5 (null) | 09e4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09e7 BENGALI DIGIT ONE | 09e6 BENGALI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09e9 BENGALI DIGIT THREE | 09e8 BENGALI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09eb BENGALI DIGIT FIVE | 09ea BENGALI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09ed BENGALI DIGIT SEVEN | 09ec BENGALI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 09ef BENGALI DIGIT NINE | 09ee BENGALI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 09f1 BENGALI LETTER RA WITH LOWER DIAGONAL | 09f0 BENGALI LETTER RA WITH MIDDLE DIAGONAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f3 BENGALI RUPEE SIGN | 09f2 BENGALI RUPEE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f5 BENGALI CURRENCY NUMERATOR TWO | 09f4 BENGALI CURRENCY NUMERATOR ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f7 BENGALI CURRENCY NUMERATOR FOUR | 09f6 BENGALI CURRENCY NUMERATOR THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09f9 BENGALI CURRENCY DENOMINATOR SIXTEEN | 09f8 BENGALI CURRENCY NUMERATOR ONE LESS THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 09fb BENGALI GANDA MARK | 09fa BENGALI ISSHAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09fd (null) | 09fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 09ff (null) | 09fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a01 GURMUKHI SIGN ADAK BINDI | 0a00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a03 GURMUKHI SIGN VISARGA | 0a02 GURMUKHI SIGN BINDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a05 GURMUKHI LETTER A | 0a04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a07 GURMUKHI LETTER I | 0a06 GURMUKHI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a09 GURMUKHI LETTER U | 0a08 GURMUKHI LETTER II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a0b (null) | 0a0a GURMUKHI LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a0d (null) | 0a0c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a0f GURMUKHI LETTER EE | 0a0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a11 (null) | 0a10 GURMUKHI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a13 GURMUKHI LETTER OO | 0a12 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a15 GURMUKHI LETTER KA | 0a14 GURMUKHI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a17 GURMUKHI LETTER GA | 0a16 GURMUKHI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a19 GURMUKHI LETTER NGA | 0a18 GURMUKHI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1b GURMUKHI LETTER CHA | 0a1a GURMUKHI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1d GURMUKHI LETTER JHA | 0a1c GURMUKHI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a1f GURMUKHI LETTER TTA | 0a1e GURMUKHI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a21 GURMUKHI LETTER DDA | 0a20 GURMUKHI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a23 GURMUKHI LETTER NNA | 0a22 GURMUKHI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a25 GURMUKHI LETTER THA | 0a24 GURMUKHI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a27 GURMUKHI LETTER DHA | 0a26 GURMUKHI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a29 (null) | 0a28 GURMUKHI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2b GURMUKHI LETTER PHA | 0a2a GURMUKHI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2d GURMUKHI LETTER BHA | 0a2c GURMUKHI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a2f GURMUKHI LETTER YA | 0a2e GURMUKHI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a31 (null) | 0a30 GURMUKHI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a33 GURMUKHI LETTER LLA | 0a32 GURMUKHI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a35 GURMUKHI LETTER VA | 0a34 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a37 (null) | 0a36 GURMUKHI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a39 GURMUKHI LETTER HA | 0a38 GURMUKHI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a3b (null) | 0a3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a3d (null) | 0a3c GURMUKHI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a3f GURMUKHI VOWEL SIGN I | 0a3e GURMUKHI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a41 GURMUKHI VOWEL SIGN U | 0a40 GURMUKHI VOWEL SIGN II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a43 (null) | 0a42 GURMUKHI VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a45 (null) | 0a44 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a47 GURMUKHI VOWEL SIGN EE | 0a46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0a49 (null) | 0a48 GURMUKHI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a4b GURMUKHI VOWEL SIGN OO | 0a4a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a4d GURMUKHI SIGN VIRAMA | 0a4c GURMUKHI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a4f (null) | 0a4e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a51 GURMUKHI SIGN UDAAT | 0a50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a53 (null) | 0a52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a55 (null) | 0a54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a57 (null) | 0a56 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a59 GURMUKHI LETTER KHHA | 0a58 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a5b GURMUKHI LETTER ZA | 0a5a GURMUKHI LETTER GHHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a5d (null) | 0a5c GURMUKHI LETTER RRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0a5f (null) | 0a5e GURMUKHI LETTER FA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a61 (null) | 0a60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a63 (null) | 0a62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a65 (null) | 0a64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a67 GURMUKHI DIGIT ONE | 0a66 GURMUKHI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a69 GURMUKHI DIGIT THREE | 0a68 GURMUKHI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6b GURMUKHI DIGIT FIVE | 0a6a GURMUKHI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6d GURMUKHI DIGIT SEVEN | 0a6c GURMUKHI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0a6f GURMUKHI DIGIT NINE | 0a6e GURMUKHI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a71 GURMUKHI ADDAK | 0a70 GURMUKHI TIPPI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a73 GURMUKHI URA | 0a72 GURMUKHI IRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0a75 GURMUKHI SIGN YAKASH | 0a74 GURMUKHI EK ONKAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a77 (null) | 0a76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a79 (null) | 0a78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7b (null) | 0a7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7d (null) | 0a7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0a7f (null) | 0a7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0a81 GUJARATI SIGN CANDRABINDU | 0a80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0a83 GUJARATI SIGN VISARGA | 0a82 GUJARATI SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a85 GUJARATI LETTER A | 0a84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a87 GUJARATI LETTER I | 0a86 GUJARATI LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a89 GUJARATI LETTER U | 0a88 GUJARATI LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a8b GUJARATI LETTER VOCALIC R | 0a8a GUJARATI LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a8d GUJARATI VOWEL CANDRA E | 0a8c GUJARATI LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a8f GUJARATI LETTER E | 0a8e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a91 GUJARATI VOWEL CANDRA O | 0a90 GUJARATI LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0a93 GUJARATI LETTER O | 0a92 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a95 GUJARATI LETTER KA | 0a94 GUJARATI LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a97 GUJARATI LETTER GA | 0a96 GUJARATI LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a99 GUJARATI LETTER NGA | 0a98 GUJARATI LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9b GUJARATI LETTER CHA | 0a9a GUJARATI LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9d GUJARATI LETTER JHA | 0a9c GUJARATI LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0a9f GUJARATI LETTER TTA | 0a9e GUJARATI LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa1 GUJARATI LETTER DDA | 0aa0 GUJARATI LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa3 GUJARATI LETTER NNA | 0aa2 GUJARATI LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa5 GUJARATI LETTER THA | 0aa4 GUJARATI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aa7 GUJARATI LETTER DHA | 0aa6 GUJARATI LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0aa9 (null) | 0aa8 GUJARATI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aab GUJARATI LETTER PHA | 0aaa GUJARATI LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aad GUJARATI LETTER BHA | 0aac GUJARATI LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0aaf GUJARATI LETTER YA | 0aae GUJARATI LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ab1 (null) | 0ab0 GUJARATI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab3 GUJARATI LETTER LLA | 0ab2 GUJARATI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ab5 GUJARATI LETTER VA | 0ab4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab7 GUJARATI LETTER SSA | 0ab6 GUJARATI LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ab9 GUJARATI LETTER HA | 0ab8 GUJARATI LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0abb (null) | 0aba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0abd GUJARATI SIGN AVAGRAHA | 0abc GUJARATI SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0abf GUJARATI VOWEL SIGN I | 0abe GUJARATI VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac1 GUJARATI VOWEL SIGN U | 0ac0 GUJARATI VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac3 GUJARATI VOWEL SIGN VOCALIC R | 0ac2 GUJARATI VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac5 GUJARATI VOWEL SIGN CANDRA E | 0ac4 GUJARATI VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0ac7 GUJARATI VOWEL SIGN E | 0ac6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ac9 GUJARATI VOWEL SIGN CANDRA O | 0ac8 GUJARATI VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0acb GUJARATI VOWEL SIGN O | 0aca (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0acd GUJARATI SIGN VIRAMA | 0acc GUJARATI VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0acf (null) | 0ace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ad1 (null) | 0ad0 GUJARATI OM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad3 (null) | 0ad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad5 (null) | 0ad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad7 (null) | 0ad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ad9 (null) | 0ad8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0adb (null) | 0ada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0add (null) | 0adc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0adf (null) | 0ade (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ae1 GUJARATI LETTER VOCALIC LL | 0ae0 GUJARATI LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ae3 GUJARATI VOWEL SIGN VOCALIC LL | 0ae2 GUJARATI VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ae5 (null) | 0ae4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ae7 GUJARATI DIGIT ONE | 0ae6 GUJARATI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ae9 GUJARATI DIGIT THREE | 0ae8 GUJARATI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aeb GUJARATI DIGIT FIVE | 0aea GUJARATI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aed GUJARATI DIGIT SEVEN | 0aec GUJARATI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0aef GUJARATI DIGIT NINE | 0aee GUJARATI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0af1 GUJARATI RUPEE SIGN | 0af0 GUJARATI ABBREVIATION SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af3 (null) | 0af2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af5 (null) | 0af4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af7 (null) | 0af6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0af9 (null) | 0af8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0afb (null) | 0afa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0afd (null) | 0afc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0aff (null) | 0afe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b01 ORIYA SIGN CANDRABINDU | 0b00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b03 ORIYA SIGN VISARGA | 0b02 ORIYA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b05 ORIYA LETTER A | 0b04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b07 ORIYA LETTER I | 0b06 ORIYA LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b09 ORIYA LETTER U | 0b08 ORIYA LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b0b ORIYA LETTER VOCALIC R | 0b0a ORIYA LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b0d (null) | 0b0c ORIYA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b0f ORIYA LETTER E | 0b0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b11 (null) | 0b10 ORIYA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b13 ORIYA LETTER O | 0b12 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b15 ORIYA LETTER KA | 0b14 ORIYA LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b17 ORIYA LETTER GA | 0b16 ORIYA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b19 ORIYA LETTER NGA | 0b18 ORIYA LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1b ORIYA LETTER CHA | 0b1a ORIYA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1d ORIYA LETTER JHA | 0b1c ORIYA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b1f ORIYA LETTER TTA | 0b1e ORIYA LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b21 ORIYA LETTER DDA | 0b20 ORIYA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b23 ORIYA LETTER NNA | 0b22 ORIYA LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b25 ORIYA LETTER THA | 0b24 ORIYA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b27 ORIYA LETTER DHA | 0b26 ORIYA LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b29 (null) | 0b28 ORIYA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2b ORIYA LETTER PHA | 0b2a ORIYA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2d ORIYA LETTER BHA | 0b2c ORIYA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b2f ORIYA LETTER YA | 0b2e ORIYA LETTER MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b31 (null) | 0b30 ORIYA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b33 ORIYA LETTER LLA | 0b32 ORIYA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b35 ORIYA LETTER VA | 0b34 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b37 ORIYA LETTER SSA | 0b36 ORIYA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b39 ORIYA LETTER HA | 0b38 ORIYA LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b3b (null) | 0b3a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b3d ORIYA SIGN AVAGRAHA | 0b3c ORIYA SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b3f ORIYA VOWEL SIGN I | 0b3e ORIYA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b41 ORIYA VOWEL SIGN U | 0b40 ORIYA VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b43 ORIYA VOWEL SIGN VOCALIC R | 0b42 ORIYA VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0b45 (null) | 0b44 ORIYA VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b47 ORIYA VOWEL SIGN E | 0b46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0b49 (null) | 0b48 ORIYA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0b4b ORIYA VOWEL SIGN O | 0b4a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b4d ORIYA SIGN VIRAMA | 0b4c ORIYA VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b4f (null) | 0b4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b51 (null) | 0b50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b53 (null) | 0b52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b55 (null) | 0b54 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b57 ORIYA AU LENGTH MARK | 0b56 ORIYA AI LENGTH MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b59 (null) | 0b58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b5b (null) | 0b5a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b5d ORIYA LETTER RHA | 0b5c ORIYA LETTER RRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b5f ORIYA LETTER YYA | 0b5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b61 ORIYA LETTER VOCALIC LL | 0b60 ORIYA LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b63 ORIYA VOWEL SIGN VOCALIC LL | 0b62 ORIYA VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b65 (null) | 0b64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b67 ORIYA DIGIT ONE | 0b66 ORIYA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b69 ORIYA DIGIT THREE | 0b68 ORIYA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6b ORIYA DIGIT FIVE | 0b6a ORIYA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6d ORIYA DIGIT SEVEN | 0b6c ORIYA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0b6f ORIYA DIGIT NINE | 0b6e ORIYA DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b71 ORIYA LETTER WA | 0b70 ORIYA ISSHAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b73 ORIYA FRACTION ONE HALF | 0b72 ORIYA FRACTION ONE QUARTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b75 ORIYA FRACTION ONE SIXTEENTH | 0b74 ORIYA FRACTION THREE QUARTERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0b77 ORIYA FRACTION THREE SIXTEENTHS | 0b76 ORIYA FRACTION ONE EIGHTH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b79 (null) | 0b78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7b (null) | 0b7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7d (null) | 0b7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b7f (null) | 0b7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b81 (null) | 0b80 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0b83 TAMIL SIGN VISARGA | 0b82 TAMIL SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b85 TAMIL LETTER A | 0b84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b87 TAMIL LETTER I | 0b86 TAMIL LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b89 TAMIL LETTER U | 0b88 TAMIL LETTER II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b8b (null) | 0b8a TAMIL LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b8d (null) | 0b8c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b8f TAMIL LETTER EE | 0b8e TAMIL LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b91 (null) | 0b90 TAMIL LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b93 TAMIL LETTER OO | 0b92 TAMIL LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b95 TAMIL LETTER KA | 0b94 TAMIL LETTER AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0b97 (null) | 0b96 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0b99 TAMIL LETTER NGA | 0b98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b9b (null) | 0b9a TAMIL LETTER CA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0b9d (null) | 0b9c TAMIL LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0b9f TAMIL LETTER TTA | 0b9e TAMIL LETTER NYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ba1 (null) | 0ba0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ba3 TAMIL LETTER NNA | 0ba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ba5 (null) | 0ba4 TAMIL LETTER TA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ba7 (null) | 0ba6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ba9 TAMIL LETTER NNNA | 0ba8 TAMIL LETTER NA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0bab (null) | 0baa TAMIL LETTER PA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bad (null) | 0bac (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0baf TAMIL LETTER YA | 0bae TAMIL LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb1 TAMIL LETTER RRA | 0bb0 TAMIL LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb3 TAMIL LETTER LLA | 0bb2 TAMIL LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb5 TAMIL LETTER VA | 0bb4 TAMIL LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb7 TAMIL LETTER SSA | 0bb6 TAMIL LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0bb9 TAMIL LETTER HA | 0bb8 TAMIL LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bbb (null) | 0bba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bbd (null) | 0bbc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bbf TAMIL VOWEL SIGN I | 0bbe TAMIL VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bc1 TAMIL VOWEL SIGN U | 0bc0 TAMIL VOWEL SIGN II */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bc3 (null) | 0bc2 TAMIL VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bc5 (null) | 0bc4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bc7 TAMIL VOWEL SIGN EE | 0bc6 TAMIL VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bc9 (null) | 0bc8 TAMIL VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bcb TAMIL VOWEL SIGN OO | 0bca TAMIL VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bcd TAMIL SIGN VIRAMA | 0bcc TAMIL VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bcf (null) | 0bce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0bd1 (null) | 0bd0 TAMIL OM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd3 (null) | 0bd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd5 (null) | 0bd4 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0bd7 TAMIL AU LENGTH MARK | 0bd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bd9 (null) | 0bd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdb (null) | 0bda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdd (null) | 0bdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bdf (null) | 0bde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be1 (null) | 0be0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be3 (null) | 0be2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0be5 (null) | 0be4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0be7 TAMIL DIGIT ONE | 0be6 TAMIL DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0be9 TAMIL DIGIT THREE | 0be8 TAMIL DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0beb TAMIL DIGIT FIVE | 0bea TAMIL DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0bed TAMIL DIGIT SEVEN | 0bec TAMIL DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0bef TAMIL DIGIT NINE | 0bee TAMIL DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf1 TAMIL NUMBER ONE HUNDRED | 0bf0 TAMIL NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf3 TAMIL DAY SIGN | 0bf2 TAMIL NUMBER ONE THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf5 TAMIL YEAR SIGN | 0bf4 TAMIL MONTH SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf7 TAMIL CREDIT SIGN | 0bf6 TAMIL DEBIT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0bf9 TAMIL RUPEE SIGN | 0bf8 TAMIL AS ABOVE SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0bfb (null) | 0bfa TAMIL NUMBER SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bfd (null) | 0bfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0bff (null) | 0bfe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0c01 TELUGU SIGN CANDRABINDU | 0c00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c03 TELUGU SIGN VISARGA | 0c02 TELUGU SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c05 TELUGU LETTER A | 0c04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c07 TELUGU LETTER I | 0c06 TELUGU LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c09 TELUGU LETTER U | 0c08 TELUGU LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c0b TELUGU LETTER VOCALIC R | 0c0a TELUGU LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c0d (null) | 0c0c TELUGU LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c0f TELUGU LETTER EE | 0c0e TELUGU LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c11 (null) | 0c10 TELUGU LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c13 TELUGU LETTER OO | 0c12 TELUGU LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c15 TELUGU LETTER KA | 0c14 TELUGU LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c17 TELUGU LETTER GA | 0c16 TELUGU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c19 TELUGU LETTER NGA | 0c18 TELUGU LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1b TELUGU LETTER CHA | 0c1a TELUGU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1d TELUGU LETTER JHA | 0c1c TELUGU LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c1f TELUGU LETTER TTA | 0c1e TELUGU LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c21 TELUGU LETTER DDA | 0c20 TELUGU LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c23 TELUGU LETTER NNA | 0c22 TELUGU LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c25 TELUGU LETTER THA | 0c24 TELUGU LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c27 TELUGU LETTER DHA | 0c26 TELUGU LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c29 (null) | 0c28 TELUGU LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2b TELUGU LETTER PHA | 0c2a TELUGU LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2d TELUGU LETTER BHA | 0c2c TELUGU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c2f TELUGU LETTER YA | 0c2e TELUGU LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c31 TELUGU LETTER RRA | 0c30 TELUGU LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c33 TELUGU LETTER LLA | 0c32 TELUGU LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c35 TELUGU LETTER VA | 0c34 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c37 TELUGU LETTER SSA | 0c36 TELUGU LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c39 TELUGU LETTER HA | 0c38 TELUGU LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c3b (null) | 0c3a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c3d TELUGU SIGN AVAGRAHA | 0c3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c3f TELUGU VOWEL SIGN I | 0c3e TELUGU VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c41 TELUGU VOWEL SIGN U | 0c40 TELUGU VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c43 TELUGU VOWEL SIGN VOCALIC R | 0c42 TELUGU VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c45 (null) | 0c44 TELUGU VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c47 TELUGU VOWEL SIGN EE | 0c46 TELUGU VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c49 (null) | 0c48 TELUGU VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c4b TELUGU VOWEL SIGN OO | 0c4a TELUGU VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c4d TELUGU SIGN VIRAMA | 0c4c TELUGU VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c4f (null) | 0c4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c51 (null) | 0c50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c53 (null) | 0c52 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0c55 TELUGU LENGTH MARK | 0c54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0c57 (null) | 0c56 TELUGU AI LENGTH MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c59 TELUGU LETTER DZA | 0c58 TELUGU LETTER TSA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5b (null) | 0c5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5d (null) | 0c5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c5f (null) | 0c5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c61 TELUGU LETTER VOCALIC LL | 0c60 TELUGU LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c63 TELUGU VOWEL SIGN VOCALIC LL | 0c62 TELUGU VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c65 (null) | 0c64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c67 TELUGU DIGIT ONE | 0c66 TELUGU DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c69 TELUGU DIGIT THREE | 0c68 TELUGU DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6b TELUGU DIGIT FIVE | 0c6a TELUGU DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6d TELUGU DIGIT SEVEN | 0c6c TELUGU DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0c6f TELUGU DIGIT NINE | 0c6e TELUGU DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c71 (null) | 0c70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c73 (null) | 0c72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c75 (null) | 0c74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c77 (null) | 0c76 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c79 TELUGU FRACTION DIGIT ONE FOR ODD POWER | 0c78 TELUGU FRACTION DIGIT ZERO FOR ODD POWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7b TELUGU FRACTION DIGIT THREE FOR ODD POW | 0c7a TELUGU FRACTION DIGIT TWO FOR ODD POWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7d TELUGU FRACTION DIGIT TWO FOR EVEN POWE | 0c7c TELUGU FRACTION DIGIT ONE FOR EVEN POWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c7f TELUGU SIGN TUUMU | 0c7e TELUGU FRACTION DIGIT THREE FOR EVEN PO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0c81 (null) | 0c80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0c83 KANNADA SIGN VISARGA | 0c82 KANNADA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0c85 KANNADA LETTER A | 0c84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c87 KANNADA LETTER I | 0c86 KANNADA LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c89 KANNADA LETTER U | 0c88 KANNADA LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c8b KANNADA LETTER VOCALIC R | 0c8a KANNADA LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c8d (null) | 0c8c KANNADA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c8f KANNADA LETTER EE | 0c8e KANNADA LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0c91 (null) | 0c90 KANNADA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c93 KANNADA LETTER OO | 0c92 KANNADA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c95 KANNADA LETTER KA | 0c94 KANNADA LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c97 KANNADA LETTER GA | 0c96 KANNADA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c99 KANNADA LETTER NGA | 0c98 KANNADA LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9b KANNADA LETTER CHA | 0c9a KANNADA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9d KANNADA LETTER JHA | 0c9c KANNADA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0c9f KANNADA LETTER TTA | 0c9e KANNADA LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca1 KANNADA LETTER DDA | 0ca0 KANNADA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca3 KANNADA LETTER NNA | 0ca2 KANNADA LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca5 KANNADA LETTER THA | 0ca4 KANNADA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ca7 KANNADA LETTER DHA | 0ca6 KANNADA LETTER DA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ca9 (null) | 0ca8 KANNADA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cab KANNADA LETTER PHA | 0caa KANNADA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cad KANNADA LETTER BHA | 0cac KANNADA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0caf KANNADA LETTER YA | 0cae KANNADA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb1 KANNADA LETTER RRA | 0cb0 KANNADA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb3 KANNADA LETTER LLA | 0cb2 KANNADA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0cb5 KANNADA LETTER VA | 0cb4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb7 KANNADA LETTER SSA | 0cb6 KANNADA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0cb9 KANNADA LETTER HA | 0cb8 KANNADA LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cbb (null) | 0cba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0cbd KANNADA SIGN AVAGRAHA | 0cbc KANNADA SIGN NUKTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cbf KANNADA VOWEL SIGN I | 0cbe KANNADA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc1 KANNADA VOWEL SIGN U | 0cc0 KANNADA VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc3 KANNADA VOWEL SIGN VOCALIC R | 0cc2 KANNADA VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cc5 (null) | 0cc4 KANNADA VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0cc7 KANNADA VOWEL SIGN EE | 0cc6 KANNADA VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cc9 (null) | 0cc8 KANNADA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ccb KANNADA VOWEL SIGN OO | 0cca KANNADA VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ccd KANNADA SIGN VIRAMA | 0ccc KANNADA VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ccf (null) | 0cce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd1 (null) | 0cd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd3 (null) | 0cd2 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0cd5 KANNADA LENGTH MARK | 0cd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0cd7 (null) | 0cd6 KANNADA AI LENGTH MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cd9 (null) | 0cd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cdb (null) | 0cda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cdd (null) | 0cdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0cdf (null) | 0cde KANNADA LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ce1 KANNADA LETTER VOCALIC LL | 0ce0 KANNADA LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ce3 KANNADA VOWEL SIGN VOCALIC LL | 0ce2 KANNADA VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ce5 (null) | 0ce4 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ce7 KANNADA DIGIT ONE | 0ce6 KANNADA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ce9 KANNADA DIGIT THREE | 0ce8 KANNADA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ceb KANNADA DIGIT FIVE | 0cea KANNADA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ced KANNADA DIGIT SEVEN | 0cec KANNADA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0cef KANNADA DIGIT NINE | 0cee KANNADA DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0cf1 KANNADA SIGN JIHVAMULIYA | 0cf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0cf3 (null) | 0cf2 KANNADA SIGN UPADHMANIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf5 (null) | 0cf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf7 (null) | 0cf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cf9 (null) | 0cf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cfb (null) | 0cfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cfd (null) | 0cfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0cff (null) | 0cfe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d01 (null) | 0d00 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d03 MALAYALAM SIGN VISARGA | 0d02 MALAYALAM SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d05 MALAYALAM LETTER A | 0d04 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d07 MALAYALAM LETTER I | 0d06 MALAYALAM LETTER AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d09 MALAYALAM LETTER U | 0d08 MALAYALAM LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d0b MALAYALAM LETTER VOCALIC R | 0d0a MALAYALAM LETTER UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d0d (null) | 0d0c MALAYALAM LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d0f MALAYALAM LETTER EE | 0d0e MALAYALAM LETTER E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d11 (null) | 0d10 MALAYALAM LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d13 MALAYALAM LETTER OO | 0d12 MALAYALAM LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d15 MALAYALAM LETTER KA | 0d14 MALAYALAM LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d17 MALAYALAM LETTER GA | 0d16 MALAYALAM LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d19 MALAYALAM LETTER NGA | 0d18 MALAYALAM LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1b MALAYALAM LETTER CHA | 0d1a MALAYALAM LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1d MALAYALAM LETTER JHA | 0d1c MALAYALAM LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d1f MALAYALAM LETTER TTA | 0d1e MALAYALAM LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d21 MALAYALAM LETTER DDA | 0d20 MALAYALAM LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d23 MALAYALAM LETTER NNA | 0d22 MALAYALAM LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d25 MALAYALAM LETTER THA | 0d24 MALAYALAM LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d27 MALAYALAM LETTER DHA | 0d26 MALAYALAM LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d29 MALAYALAM LETTER NNNA | 0d28 MALAYALAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2b MALAYALAM LETTER PHA | 0d2a MALAYALAM LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2d MALAYALAM LETTER BHA | 0d2c MALAYALAM LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d2f MALAYALAM LETTER YA | 0d2e MALAYALAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d31 MALAYALAM LETTER RRA | 0d30 MALAYALAM LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d33 MALAYALAM LETTER LLA | 0d32 MALAYALAM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d35 MALAYALAM LETTER VA | 0d34 MALAYALAM LETTER LLLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d37 MALAYALAM LETTER SSA | 0d36 MALAYALAM LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d39 MALAYALAM LETTER HA | 0d38 MALAYALAM LETTER SA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d3b (null) | 0d3a MALAYALAM LETTER TTTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d3d MALAYALAM SIGN AVAGRAHA | 0d3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d3f MALAYALAM VOWEL SIGN I | 0d3e MALAYALAM VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d41 MALAYALAM VOWEL SIGN U | 0d40 MALAYALAM VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d43 MALAYALAM VOWEL SIGN VOCALIC R | 0d42 MALAYALAM VOWEL SIGN UU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0d45 (null) | 0d44 MALAYALAM VOWEL SIGN VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d47 MALAYALAM VOWEL SIGN EE | 0d46 MALAYALAM VOWEL SIGN E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0d49 (null) | 0d48 MALAYALAM VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d4b MALAYALAM VOWEL SIGN OO | 0d4a MALAYALAM VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d4d MALAYALAM SIGN VIRAMA | 0d4c MALAYALAM VOWEL SIGN AU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d4f (null) | 0d4e MALAYALAM LETTER DOT REPH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d51 (null) | 0d50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d53 (null) | 0d52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d55 (null) | 0d54 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0d57 MALAYALAM AU LENGTH MARK | 0d56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d59 (null) | 0d58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5b (null) | 0d5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5d (null) | 0d5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d5f (null) | 0d5e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d61 MALAYALAM LETTER VOCALIC LL | 0d60 MALAYALAM LETTER VOCALIC RR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d63 MALAYALAM VOWEL SIGN VOCALIC LL | 0d62 MALAYALAM VOWEL SIGN VOCALIC L */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d65 (null) | 0d64 (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d67 MALAYALAM DIGIT ONE | 0d66 MALAYALAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d69 MALAYALAM DIGIT THREE | 0d68 MALAYALAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6b MALAYALAM DIGIT FIVE | 0d6a MALAYALAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6d MALAYALAM DIGIT SEVEN | 0d6c MALAYALAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0d6f MALAYALAM DIGIT NINE | 0d6e MALAYALAM DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d71 MALAYALAM NUMBER ONE HUNDRED | 0d70 MALAYALAM NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d73 MALAYALAM FRACTION ONE QUARTER | 0d72 MALAYALAM NUMBER ONE THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d75 MALAYALAM FRACTION THREE QUARTERS | 0d74 MALAYALAM FRACTION ONE HALF */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d77 (null) | 0d76 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0d79 MALAYALAM DATE MARK | 0d78 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7b MALAYALAM LETTER CHILLU N | 0d7a MALAYALAM LETTER CHILLU NN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7d MALAYALAM LETTER CHILLU L | 0d7c MALAYALAM LETTER CHILLU RR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d7f MALAYALAM LETTER CHILLU K | 0d7e MALAYALAM LETTER CHILLU LL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d81 (null) | 0d80 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0d83 SINHALA SIGN VISARGAYA | 0d82 SINHALA SIGN ANUSVARAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0d85 SINHALA LETTER AYANNA | 0d84 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d87 SINHALA LETTER AEYANNA | 0d86 SINHALA LETTER AAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d89 SINHALA LETTER IYANNA | 0d88 SINHALA LETTER AEEYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8b SINHALA LETTER UYANNA | 0d8a SINHALA LETTER IIYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8d SINHALA LETTER IRUYANNA | 0d8c SINHALA LETTER UUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d8f SINHALA LETTER ILUYANNA | 0d8e SINHALA LETTER IRUUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d91 SINHALA LETTER EYANNA | 0d90 SINHALA LETTER ILUUYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d93 SINHALA LETTER AIYANNA | 0d92 SINHALA LETTER EEYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d95 SINHALA LETTER OOYANNA | 0d94 SINHALA LETTER OYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0d97 (null) | 0d96 SINHALA LETTER AUYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0d99 (null) | 0d98 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9b SINHALA LETTER MAHAAPRAANA KAYANNA | 0d9a SINHALA LETTER ALPAPRAANA KAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9d SINHALA LETTER MAHAAPRAANA GAYANNA | 0d9c SINHALA LETTER ALPAPRAANA GAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0d9f SINHALA LETTER SANYAKA GAYANNA | 0d9e SINHALA LETTER KANTAJA NAASIKYAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da1 SINHALA LETTER MAHAAPRAANA CAYANNA | 0da0 SINHALA LETTER ALPAPRAANA CAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da3 SINHALA LETTER MAHAAPRAANA JAYANNA | 0da2 SINHALA LETTER ALPAPRAANA JAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da5 SINHALA LETTER TAALUJA SANYOOGA NAAKSIK | 0da4 SINHALA LETTER TAALUJA NAASIKYAYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da7 SINHALA LETTER ALPAPRAANA TTAYANNA | 0da6 SINHALA LETTER SANYAKA JAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0da9 SINHALA LETTER ALPAPRAANA DDAYANNA | 0da8 SINHALA LETTER MAHAAPRAANA TTAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dab SINHALA LETTER MUURDHAJA NAYANNA | 0daa SINHALA LETTER MAHAAPRAANA DDAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dad SINHALA LETTER ALPAPRAANA TAYANNA | 0dac SINHALA LETTER SANYAKA DDAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0daf SINHALA LETTER ALPAPRAANA DAYANNA | 0dae SINHALA LETTER MAHAAPRAANA TAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db1 SINHALA LETTER DANTAJA NAYANNA | 0db0 SINHALA LETTER MAHAAPRAANA DAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0db3 SINHALA LETTER SANYAKA DAYANNA | 0db2 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db5 SINHALA LETTER MAHAAPRAANA PAYANNA | 0db4 SINHALA LETTER ALPAPRAANA PAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db7 SINHALA LETTER MAHAAPRAANA BAYANNA | 0db6 SINHALA LETTER ALPAPRAANA BAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0db9 SINHALA LETTER AMBA BAYANNA | 0db8 SINHALA LETTER MAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dbb SINHALA LETTER RAYANNA | 0dba SINHALA LETTER YAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0dbd SINHALA LETTER DANTAJA LAYANNA | 0dbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dbf (null) | 0dbe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc1 SINHALA LETTER TAALUJA SAYANNA | 0dc0 SINHALA LETTER VAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc3 SINHALA LETTER DANTAJA SAYANNA | 0dc2 SINHALA LETTER MUURDHAJA SAYANNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0dc5 SINHALA LETTER MUURDHAJA LAYANNA | 0dc4 SINHALA LETTER HAYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0dc7 (null) | 0dc6 SINHALA LETTER FAYANNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dc9 (null) | 0dc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dcb (null) | 0dca SINHALA SIGN AL-LAKUNA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dcd (null) | 0dcc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0dcf SINHALA VOWEL SIGN AELA-PILLA | 0dce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd1 SINHALA VOWEL SIGN DIGA AEDA-PILLA | 0dd0 SINHALA VOWEL SIGN KETTI AEDA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd3 SINHALA VOWEL SIGN DIGA IS-PILLA | 0dd2 SINHALA VOWEL SIGN KETTI IS-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dd5 (null) | 0dd4 SINHALA VOWEL SIGN KETTI PAA-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0dd7 (null) | 0dd6 SINHALA VOWEL SIGN DIGA PAA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0dd9 SINHALA VOWEL SIGN KOMBUVA | 0dd8 SINHALA VOWEL SIGN GAETTA-PILLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddb SINHALA VOWEL SIGN KOMBU DEKA | 0dda SINHALA VOWEL SIGN DIGA KOMBUVA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddd SINHALA VOWEL SIGN KOMBUVA HAA DIGA AEL | 0ddc SINHALA VOWEL SIGN KOMBUVA HAA AELA-PIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ddf SINHALA VOWEL SIGN GAYANUKITTA | 0dde SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de1 (null) | 0de0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de3 (null) | 0de2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de5 (null) | 0de4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de7 (null) | 0de6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0de9 (null) | 0de8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0deb (null) | 0dea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ded (null) | 0dec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0def (null) | 0dee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df1 (null) | 0df0 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0df3 SINHALA VOWEL SIGN DIGA GAYANUKITTA | 0df2 SINHALA VOWEL SIGN DIGA GAETTA-PILLA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 0df5 (null) | 0df4 SINHALA PUNCTUATION KUNDDALIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df7 (null) | 0df6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0df9 (null) | 0df8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dfb (null) | 0dfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dfd (null) | 0dfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0dff (null) | 0dfe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e01 THAI CHARACTER KO KAI | 0e00 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e03 THAI CHARACTER KHO KHUAT | 0e02 THAI CHARACTER KHO KHAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e05 THAI CHARACTER KHO KHON | 0e04 THAI CHARACTER KHO KHWAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e07 THAI CHARACTER NGO NGU | 0e06 THAI CHARACTER KHO RAKHANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e09 THAI CHARACTER CHO CHING | 0e08 THAI CHARACTER CHO CHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0b THAI CHARACTER SO SO | 0e0a THAI CHARACTER CHO CHANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0d THAI CHARACTER YO YING | 0e0c THAI CHARACTER CHO CHOE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e0f THAI CHARACTER TO PATAK | 0e0e THAI CHARACTER DO CHADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e11 THAI CHARACTER THO NANGMONTHO | 0e10 THAI CHARACTER THO THAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e13 THAI CHARACTER NO NEN | 0e12 THAI CHARACTER THO PHUTHAO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e15 THAI CHARACTER TO TAO | 0e14 THAI CHARACTER DO DEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e17 THAI CHARACTER THO THAHAN | 0e16 THAI CHARACTER THO THUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e19 THAI CHARACTER NO NU | 0e18 THAI CHARACTER THO THONG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1b THAI CHARACTER PO PLA | 0e1a THAI CHARACTER BO BAIMAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1d THAI CHARACTER FO FA | 0e1c THAI CHARACTER PHO PHUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e1f THAI CHARACTER FO FAN | 0e1e THAI CHARACTER PHO PHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e21 THAI CHARACTER MO MA | 0e20 THAI CHARACTER PHO SAMPHAO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e23 THAI CHARACTER RO RUA | 0e22 THAI CHARACTER YO YAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e25 THAI CHARACTER LO LING | 0e24 THAI CHARACTER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e27 THAI CHARACTER WO WAEN | 0e26 THAI CHARACTER LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e29 THAI CHARACTER SO RUSI | 0e28 THAI CHARACTER SO SALA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2b THAI CHARACTER HO HIP | 0e2a THAI CHARACTER SO SUA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2d THAI CHARACTER O ANG | 0e2c THAI CHARACTER LO CHULA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e2f THAI CHARACTER PAIYANNOI | 0e2e THAI CHARACTER HO NOKHUK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0e31 THAI CHARACTER MAI HAN-AKAT | 0e30 THAI CHARACTER SARA A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e33 THAI CHARACTER SARA AM | 0e32 THAI CHARACTER SARA AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e35 THAI CHARACTER SARA II | 0e34 THAI CHARACTER SARA I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e37 THAI CHARACTER SARA UEE | 0e36 THAI CHARACTER SARA UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e39 THAI CHARACTER SARA UU | 0e38 THAI CHARACTER SARA U */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0e3b (null) | 0e3a THAI CHARACTER PHINTHU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e3d (null) | 0e3c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0e3f THAI CURRENCY SYMBOL BAHT | 0e3e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e41 THAI CHARACTER SARA AE | 0e40 THAI CHARACTER SARA E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e43 THAI CHARACTER SARA AI MAIMUAN | 0e42 THAI CHARACTER SARA O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e45 THAI CHARACTER LAKKHANGYAO | 0e44 THAI CHARACTER SARA AI MAIMALAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e47 THAI CHARACTER MAITAIKHU | 0e46 THAI CHARACTER MAIYAMOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e49 THAI CHARACTER MAI THO | 0e48 THAI CHARACTER MAI EK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e4b THAI CHARACTER MAI CHATTAWA | 0e4a THAI CHARACTER MAI TRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0e4d THAI CHARACTER NIKHAHIT | 0e4c THAI CHARACTER THANTHAKHAT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0e4f THAI CHARACTER FONGMAN | 0e4e THAI CHARACTER YAMAKKAN */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e51 THAI DIGIT ONE | 0e50 THAI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e53 THAI DIGIT THREE | 0e52 THAI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e55 THAI DIGIT FIVE | 0e54 THAI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e57 THAI DIGIT SEVEN | 0e56 THAI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0e59 THAI DIGIT NINE | 0e58 THAI DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0e5b THAI CHARACTER KHOMUT | 0e5a THAI CHARACTER ANGKHANKHU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e5d (null) | 0e5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e5f (null) | 0e5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e61 (null) | 0e60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e63 (null) | 0e62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e65 (null) | 0e64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e67 (null) | 0e66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e69 (null) | 0e68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6b (null) | 0e6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6d (null) | 0e6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e6f (null) | 0e6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e71 (null) | 0e70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e73 (null) | 0e72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e75 (null) | 0e74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e77 (null) | 0e76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e79 (null) | 0e78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7b (null) | 0e7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7d (null) | 0e7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e7f (null) | 0e7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e81 LAO LETTER KO | 0e80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e83 (null) | 0e82 LAO LETTER KHO SUNG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e85 (null) | 0e84 LAO LETTER KHO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e87 LAO LETTER NGO | 0e86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e89 (null) | 0e88 LAO LETTER CO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0e8b (null) | 0e8a LAO LETTER SO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e8d LAO LETTER NYO | 0e8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e8f (null) | 0e8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e91 (null) | 0e90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0e93 (null) | 0e92 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e95 LAO LETTER TO | 0e94 LAO LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e97 LAO LETTER THO TAM | 0e96 LAO LETTER THO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0e99 LAO LETTER NO | 0e98 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9b LAO LETTER PO | 0e9a LAO LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9d LAO LETTER FO TAM | 0e9c LAO LETTER PHO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0e9f LAO LETTER FO SUNG | 0e9e LAO LETTER PHO TAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea1 LAO LETTER MO | 0ea0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ea3 LAO LETTER LO LING | 0ea2 LAO LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea5 LAO LETTER LO LOOT | 0ea4 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ea7 LAO LETTER WO | 0ea6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ea9 (null) | 0ea8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eab LAO LETTER HO SUNG | 0eaa LAO LETTER SO SUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0ead LAO LETTER O | 0eac (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eaf LAO ELLIPSIS | 0eae LAO LETTER HO TAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0eb1 LAO VOWEL SIGN MAI KAN | 0eb0 LAO VOWEL SIGN A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0eb3 LAO VOWEL SIGN AM | 0eb2 LAO VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb5 LAO VOWEL SIGN II | 0eb4 LAO VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb7 LAO VOWEL SIGN YY | 0eb6 LAO VOWEL SIGN Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0eb9 LAO VOWEL SIGN UU | 0eb8 LAO VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0ebb LAO VOWEL SIGN MAI KON | 0eba (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 0ebd LAO SEMIVOWEL SIGN NYO | 0ebc LAO SEMIVOWEL SIGN LO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ebf (null) | 0ebe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ec1 LAO VOWEL SIGN EI | 0ec0 LAO VOWEL SIGN E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0ec3 LAO VOWEL SIGN AY | 0ec2 LAO VOWEL SIGN O */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0ec5 (null) | 0ec4 LAO VOWEL SIGN AI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0ec7 (null) | 0ec6 LAO KO LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ec9 LAO TONE MAI THO | 0ec8 LAO TONE MAI EK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ecb LAO TONE MAI CATAWA | 0eca LAO TONE MAI TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0ecd LAO NIGGAHITA | 0ecc LAO CANCELLATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ecf (null) | 0ece (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed1 LAO DIGIT ONE | 0ed0 LAO DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed3 LAO DIGIT THREE | 0ed2 LAO DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed5 LAO DIGIT FIVE | 0ed4 LAO DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed7 LAO DIGIT SEVEN | 0ed6 LAO DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0ed9 LAO DIGIT NINE | 0ed8 LAO DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0edb (null) | 0eda (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0edd LAO HO MO | 0edc LAO HO NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0edf LAO LETTER KHMU NYO | 0ede LAO LETTER KHMU GO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee1 (null) | 0ee0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee3 (null) | 0ee2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee5 (null) | 0ee4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee7 (null) | 0ee6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ee9 (null) | 0ee8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eeb (null) | 0eea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eed (null) | 0eec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eef (null) | 0eee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef1 (null) | 0ef0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef3 (null) | 0ef2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef5 (null) | 0ef4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef7 (null) | 0ef6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ef9 (null) | 0ef8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0efb (null) | 0efa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0efd (null) | 0efc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0eff (null) | 0efe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0f01 TIBETAN MARK GTER YIG MGO TRUNCATED A | 0f00 TIBETAN SYLLABLE OM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f03 TIBETAN MARK GTER YIG MGO -UM GTER TSHE | 0f02 TIBETAN MARK GTER YIG MGO -UM RNAM BCAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f05 TIBETAN MARK CLOSING YIG MGO SGAB MA | 0f04 TIBETAN MARK INITIAL YIG MGO MDUN MA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f07 TIBETAN MARK YIG MGO TSHEG SHAD MA | 0f06 TIBETAN MARK CARET YIG MGO PHUR SHAD MA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f09 TIBETAN MARK BSKUR YIG MGO | 0f08 TIBETAN MARK SBRUL SHAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0b TIBETAN MARK INTERSYLLABIC TSHEG | 0f0a TIBETAN MARK BKA- SHOG YIG MGO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0d TIBETAN MARK SHAD | 0f0c TIBETAN MARK DELIMITER TSHEG BSTAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f0f TIBETAN MARK TSHEG SHAD | 0f0e TIBETAN MARK NYIS SHAD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f11 TIBETAN MARK RIN CHEN SPUNGS SHAD | 0f10 TIBETAN MARK NYIS TSHEG SHAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0f13 TIBETAN MARK CARET -DZUD RTAGS ME LONG | 0f12 TIBETAN MARK RGYA GRAM SHAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0f15 TIBETAN LOGOTYPE SIGN CHAD RTAGS | 0f14 TIBETAN MARK GTER TSHEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f17 TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CH | 0f16 TIBETAN LOGOTYPE SIGN LHAG RTAGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f19 TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS | 0f18 TIBETAN ASTROLOGICAL SIGN -KHYUD PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1b TIBETAN SIGN RDEL DKAR GNYIS | 0f1a TIBETAN SIGN RDEL DKAR GCIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1d TIBETAN SIGN RDEL NAG GCIG | 0f1c TIBETAN SIGN RDEL DKAR GSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f1f TIBETAN SIGN RDEL DKAR RDEL NAG | 0f1e TIBETAN SIGN RDEL NAG GNYIS */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f21 TIBETAN DIGIT ONE | 0f20 TIBETAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f23 TIBETAN DIGIT THREE | 0f22 TIBETAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f25 TIBETAN DIGIT FIVE | 0f24 TIBETAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f27 TIBETAN DIGIT SEVEN | 0f26 TIBETAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 0f29 TIBETAN DIGIT NINE | 0f28 TIBETAN DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2b TIBETAN DIGIT HALF TWO | 0f2a TIBETAN DIGIT HALF ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2d TIBETAN DIGIT HALF FOUR | 0f2c TIBETAN DIGIT HALF THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f2f TIBETAN DIGIT HALF SIX | 0f2e TIBETAN DIGIT HALF FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f31 TIBETAN DIGIT HALF EIGHT | 0f30 TIBETAN DIGIT HALF SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f33 TIBETAN DIGIT HALF ZERO | 0f32 TIBETAN DIGIT HALF NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f35 TIBETAN MARK NGAS BZUNG NYI ZLA | 0f34 TIBETAN MARK BSDUS RTAGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f37 TIBETAN MARK NGAS BZUNG SGOR RTAGS | 0f36 TIBETAN MARK CARET -DZUD RTAGS BZHI MIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f39 TIBETAN MARK TSA -PHRU | 0f38 TIBETAN MARK CHE MGO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f3b TIBETAN MARK GUG RTAGS GYAS | 0f3a TIBETAN MARK GUG RTAGS GYON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0f3d TIBETAN MARK ANG KHANG GYAS | 0f3c TIBETAN MARK ANG KHANG GYON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f3f TIBETAN SIGN MAR TSHES | 0f3e TIBETAN SIGN YAR TSHES */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f41 TIBETAN LETTER KHA | 0f40 TIBETAN LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f43 TIBETAN LETTER GHA | 0f42 TIBETAN LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f45 TIBETAN LETTER CA | 0f44 TIBETAN LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f47 TIBETAN LETTER JA | 0f46 TIBETAN LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 0f49 TIBETAN LETTER NYA | 0f48 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4b TIBETAN LETTER TTHA | 0f4a TIBETAN LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4d TIBETAN LETTER DDHA | 0f4c TIBETAN LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f4f TIBETAN LETTER TA | 0f4e TIBETAN LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f51 TIBETAN LETTER DA | 0f50 TIBETAN LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f53 TIBETAN LETTER NA | 0f52 TIBETAN LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f55 TIBETAN LETTER PHA | 0f54 TIBETAN LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f57 TIBETAN LETTER BHA | 0f56 TIBETAN LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f59 TIBETAN LETTER TSA | 0f58 TIBETAN LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5b TIBETAN LETTER DZA | 0f5a TIBETAN LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5d TIBETAN LETTER WA | 0f5c TIBETAN LETTER DZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f5f TIBETAN LETTER ZA | 0f5e TIBETAN LETTER ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f61 TIBETAN LETTER YA | 0f60 TIBETAN LETTER -A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f63 TIBETAN LETTER LA | 0f62 TIBETAN LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f65 TIBETAN LETTER SSA | 0f64 TIBETAN LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f67 TIBETAN LETTER HA | 0f66 TIBETAN LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f69 TIBETAN LETTER KSSA | 0f68 TIBETAN LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f6b TIBETAN LETTER KKA | 0f6a TIBETAN LETTER FIXED-FORM RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 0f6d (null) | 0f6c TIBETAN LETTER RRA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0f6f (null) | 0f6e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0f71 TIBETAN VOWEL SIGN AA | 0f70 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f73 TIBETAN VOWEL SIGN II | 0f72 TIBETAN VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f75 TIBETAN VOWEL SIGN UU | 0f74 TIBETAN VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f77 TIBETAN VOWEL SIGN VOCALIC RR | 0f76 TIBETAN VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f79 TIBETAN VOWEL SIGN VOCALIC LL | 0f78 TIBETAN VOWEL SIGN VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7b TIBETAN VOWEL SIGN EE | 0f7a TIBETAN VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7d TIBETAN VOWEL SIGN OO | 0f7c TIBETAN VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f7f TIBETAN SIGN RNAM BCAD | 0f7e TIBETAN SIGN RJES SU NGA RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f81 TIBETAN VOWEL SIGN REVERSED II | 0f80 TIBETAN VOWEL SIGN REVERSED I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f83 TIBETAN SIGN SNA LDAN | 0f82 TIBETAN SIGN NYI ZLA NAA DA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0f85 TIBETAN MARK PALUTA | 0f84 TIBETAN MARK HALANTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f87 TIBETAN SIGN YANG RTAGS | 0f86 TIBETAN SIGN LCI RTAGS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f89 TIBETAN SIGN MCHU CAN | 0f88 TIBETAN SIGN LCE TSA CAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 0f8b TIBETAN SIGN GRU MED RGYINGS | 0f8a TIBETAN SIGN GRU CAN RGYINGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 0f8d TIBETAN SUBJOINED SIGN LCE TSA CAN | 0f8c TIBETAN SIGN INVERTED MCHU CAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f8f TIBETAN SUBJOINED SIGN INVERTED MCHU CA | 0f8e TIBETAN SUBJOINED SIGN MCHU CAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f91 TIBETAN SUBJOINED LETTER KHA | 0f90 TIBETAN SUBJOINED LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f93 TIBETAN SUBJOINED LETTER GHA | 0f92 TIBETAN SUBJOINED LETTER GA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f95 TIBETAN SUBJOINED LETTER CA | 0f94 TIBETAN SUBJOINED LETTER NGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f97 TIBETAN SUBJOINED LETTER JA | 0f96 TIBETAN SUBJOINED LETTER CHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 0f99 TIBETAN SUBJOINED LETTER NYA | 0f98 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9b TIBETAN SUBJOINED LETTER TTHA | 0f9a TIBETAN SUBJOINED LETTER TTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9d TIBETAN SUBJOINED LETTER DDHA | 0f9c TIBETAN SUBJOINED LETTER DDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0f9f TIBETAN SUBJOINED LETTER TA | 0f9e TIBETAN SUBJOINED LETTER NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa1 TIBETAN SUBJOINED LETTER DA | 0fa0 TIBETAN SUBJOINED LETTER THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa3 TIBETAN SUBJOINED LETTER NA | 0fa2 TIBETAN SUBJOINED LETTER DHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa5 TIBETAN SUBJOINED LETTER PHA | 0fa4 TIBETAN SUBJOINED LETTER PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa7 TIBETAN SUBJOINED LETTER BHA | 0fa6 TIBETAN SUBJOINED LETTER BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fa9 TIBETAN SUBJOINED LETTER TSA | 0fa8 TIBETAN SUBJOINED LETTER MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fab TIBETAN SUBJOINED LETTER DZA | 0faa TIBETAN SUBJOINED LETTER TSHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fad TIBETAN SUBJOINED LETTER WA | 0fac TIBETAN SUBJOINED LETTER DZHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0faf TIBETAN SUBJOINED LETTER ZA | 0fae TIBETAN SUBJOINED LETTER ZHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb1 TIBETAN SUBJOINED LETTER YA | 0fb0 TIBETAN SUBJOINED LETTER -A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb3 TIBETAN SUBJOINED LETTER LA | 0fb2 TIBETAN SUBJOINED LETTER RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb5 TIBETAN SUBJOINED LETTER SSA | 0fb4 TIBETAN SUBJOINED LETTER SHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb7 TIBETAN SUBJOINED LETTER HA | 0fb6 TIBETAN SUBJOINED LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fb9 TIBETAN SUBJOINED LETTER KSSA | 0fb8 TIBETAN SUBJOINED LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fbb TIBETAN SUBJOINED LETTER FIXED-FORM YA | 0fba TIBETAN SUBJOINED LETTER FIXED-FORM WA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0fbd (null) | 0fbc TIBETAN SUBJOINED LETTER FIXED-FORM RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fbf TIBETAN KU RU KHA BZHI MIG CAN | 0fbe TIBETAN KU RU KHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc1 TIBETAN CANTILLATION SIGN LIGHT BEAT | 0fc0 TIBETAN CANTILLATION SIGN HEAVY BEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc3 TIBETAN CANTILLATION SIGN SBUB -CHAL | 0fc2 TIBETAN CANTILLATION SIGN CANG TE-U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc5 TIBETAN SYMBOL RDO RJE | 0fc4 TIBETAN SYMBOL DRIL BU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc7 TIBETAN SYMBOL RDO RJE RGYA GRAM | 0fc6 TIBETAN SYMBOL PADMA GDAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fc9 TIBETAN SYMBOL NOR BU | 0fc8 TIBETAN SYMBOL PHUR PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fcb TIBETAN SYMBOL NOR BU GSUM -KHYIL | 0fca TIBETAN SYMBOL NOR BU NYIS -KHYIL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 0fcd (null) | 0fcc TIBETAN SYMBOL NOR BU BZHI -KHYIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fcf TIBETAN SIGN RDEL NAG GSUM | 0fce TIBETAN SIGN RDEL NAG RDEL DKAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0fd1 TIBETAN MARK MNYAM YIG GI MGO RGYAN | 0fd0 TIBETAN MARK BSKA- SHOG GI MGO RGYAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 0fd3 TIBETAN MARK INITIAL BRDA RNYING YIG MG | 0fd2 TIBETAN MARK NYIS TSHEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 0fd5 RIGHT-FACING SVASTI SIGN | 0fd4 TIBETAN MARK CLOSING BRDA RNYING YIG MG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 0fd7 RIGHT-FACING SVASTI SIGN WITH DOTS | 0fd6 LEFT-FACING SVASTI SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 0fd9 TIBETAN MARK LEADING MCHAN RTAGS | 0fd8 LEFT-FACING SVASTI SIGN WITH DOTS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 0fdb (null) | 0fda TIBETAN MARK TRAILING MCHAN RTAGS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fdd (null) | 0fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fdf (null) | 0fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe1 (null) | 0fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe3 (null) | 0fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe5 (null) | 0fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe7 (null) | 0fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fe9 (null) | 0fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0feb (null) | 0fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fed (null) | 0fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fef (null) | 0fee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff1 (null) | 0ff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff3 (null) | 0ff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff5 (null) | 0ff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff7 (null) | 0ff6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ff9 (null) | 0ff8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ffb (null) | 0ffa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0ffd (null) | 0ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 0fff (null) | 0ffe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1001 MYANMAR LETTER KHA | 1000 MYANMAR LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1003 MYANMAR LETTER GHA | 1002 MYANMAR LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1005 MYANMAR LETTER CA | 1004 MYANMAR LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1007 MYANMAR LETTER JA | 1006 MYANMAR LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1009 MYANMAR LETTER NYA | 1008 MYANMAR LETTER JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100b MYANMAR LETTER TTA | 100a MYANMAR LETTER NNYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100d MYANMAR LETTER DDA | 100c MYANMAR LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 100f MYANMAR LETTER NNA | 100e MYANMAR LETTER DDHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1011 MYANMAR LETTER THA | 1010 MYANMAR LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1013 MYANMAR LETTER DHA | 1012 MYANMAR LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1015 MYANMAR LETTER PA | 1014 MYANMAR LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1017 MYANMAR LETTER BA | 1016 MYANMAR LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1019 MYANMAR LETTER MA | 1018 MYANMAR LETTER BHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101b MYANMAR LETTER RA | 101a MYANMAR LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101d MYANMAR LETTER WA | 101c MYANMAR LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 101f MYANMAR LETTER HA | 101e MYANMAR LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1021 MYANMAR LETTER A | 1020 MYANMAR LETTER LLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1023 MYANMAR LETTER I | 1022 MYANMAR LETTER SHAN A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1025 MYANMAR LETTER U | 1024 MYANMAR LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1027 MYANMAR LETTER E | 1026 MYANMAR LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1029 MYANMAR LETTER O | 1028 MYANMAR LETTER MON E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 102b MYANMAR VOWEL SIGN TALL AA | 102a MYANMAR LETTER AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 102d MYANMAR VOWEL SIGN I | 102c MYANMAR VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 102f MYANMAR VOWEL SIGN U | 102e MYANMAR VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1031 MYANMAR VOWEL SIGN E | 1030 MYANMAR VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1033 MYANMAR VOWEL SIGN MON II | 1032 MYANMAR VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1035 MYANMAR VOWEL SIGN E ABOVE | 1034 MYANMAR VOWEL SIGN MON O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1037 MYANMAR SIGN DOT BELOW | 1036 MYANMAR SIGN ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1039 MYANMAR SIGN VIRAMA | 1038 MYANMAR SIGN VISARGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 103b MYANMAR CONSONANT SIGN MEDIAL YA | 103a MYANMAR SIGN ASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 103d MYANMAR CONSONANT SIGN MEDIAL WA | 103c MYANMAR CONSONANT SIGN MEDIAL RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 103f MYANMAR LETTER GREAT SA | 103e MYANMAR CONSONANT SIGN MEDIAL HA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1041 MYANMAR DIGIT ONE | 1040 MYANMAR DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1043 MYANMAR DIGIT THREE | 1042 MYANMAR DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1045 MYANMAR DIGIT FIVE | 1044 MYANMAR DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1047 MYANMAR DIGIT SEVEN | 1046 MYANMAR DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1049 MYANMAR DIGIT NINE | 1048 MYANMAR DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104b MYANMAR SIGN SECTION | 104a MYANMAR SIGN LITTLE SECTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104d MYANMAR SYMBOL COMPLETED | 104c MYANMAR SYMBOL LOCATIVE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 104f MYANMAR SYMBOL GENITIVE | 104e MYANMAR SYMBOL AFOREMENTIONED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1051 MYANMAR LETTER SSA | 1050 MYANMAR LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1053 MYANMAR LETTER VOCALIC RR | 1052 MYANMAR LETTER VOCALIC R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1055 MYANMAR LETTER VOCALIC LL | 1054 MYANMAR LETTER VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1057 MYANMAR VOWEL SIGN VOCALIC RR | 1056 MYANMAR VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1059 MYANMAR VOWEL SIGN VOCALIC LL | 1058 MYANMAR VOWEL SIGN VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 105b MYANMAR LETTER MON JHA | 105a MYANMAR LETTER MON NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 105d MYANMAR LETTER MON BBE | 105c MYANMAR LETTER MON BBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 105f MYANMAR CONSONANT SIGN MON MEDIAL MA | 105e MYANMAR CONSONANT SIGN MON MEDIAL NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1061 MYANMAR LETTER SGAW KAREN SHA | 1060 MYANMAR CONSONANT SIGN MON MEDIAL LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1063 MYANMAR TONE MARK SGAW KAREN HATHI | 1062 MYANMAR VOWEL SIGN SGAW KAREN EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1065 MYANMAR LETTER WESTERN PWO KAREN THA | 1064 MYANMAR TONE MARK SGAW KAREN KE PHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1067 MYANMAR VOWEL SIGN WESTERN PWO KAREN EU | 1066 MYANMAR LETTER WESTERN PWO KAREN PWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1069 MYANMAR SIGN WESTERN PWO KAREN TONE-1 | 1068 MYANMAR VOWEL SIGN WESTERN PWO KAREN UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 106b MYANMAR SIGN WESTERN PWO KAREN TONE-3 | 106a MYANMAR SIGN WESTERN PWO KAREN TONE-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 106d MYANMAR SIGN WESTERN PWO KAREN TONE-5 | 106c MYANMAR SIGN WESTERN PWO KAREN TONE-4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 106f MYANMAR LETTER EASTERN PWO KAREN YWA | 106e MYANMAR LETTER EASTERN PWO KAREN NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1071 MYANMAR VOWEL SIGN GEBA KAREN I | 1070 MYANMAR LETTER EASTERN PWO KAREN GHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1073 MYANMAR VOWEL SIGN KAYAH U | 1072 MYANMAR VOWEL SIGN KAYAH OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1075 MYANMAR LETTER SHAN KA | 1074 MYANMAR VOWEL SIGN KAYAH EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1077 MYANMAR LETTER SHAN GA | 1076 MYANMAR LETTER SHAN KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1079 MYANMAR LETTER SHAN ZA | 1078 MYANMAR LETTER SHAN CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107b MYANMAR LETTER SHAN DA | 107a MYANMAR LETTER SHAN NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107d MYANMAR LETTER SHAN PHA | 107c MYANMAR LETTER SHAN NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 107f MYANMAR LETTER SHAN BA | 107e MYANMAR LETTER SHAN FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1081 MYANMAR LETTER SHAN HA | 1080 MYANMAR LETTER SHAN THA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1083 MYANMAR VOWEL SIGN SHAN AA | 1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1085 MYANMAR VOWEL SIGN SHAN E ABOVE | 1084 MYANMAR VOWEL SIGN SHAN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1087 MYANMAR SIGN SHAN TONE-2 | 1086 MYANMAR VOWEL SIGN SHAN FINAL Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1089 MYANMAR SIGN SHAN TONE-5 | 1088 MYANMAR SIGN SHAN TONE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 108b MYANMAR SIGN SHAN COUNCIL TONE-2 | 108a MYANMAR SIGN SHAN TONE-6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 108d MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE | 108c MYANMAR SIGN SHAN COUNCIL TONE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 108f MYANMAR SIGN RUMAI PALAUNG TONE-5 | 108e MYANMAR LETTER RUMAI PALAUNG FA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1091 MYANMAR SHAN DIGIT ONE | 1090 MYANMAR SHAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1093 MYANMAR SHAN DIGIT THREE | 1092 MYANMAR SHAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1095 MYANMAR SHAN DIGIT FIVE | 1094 MYANMAR SHAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1097 MYANMAR SHAN DIGIT SEVEN | 1096 MYANMAR SHAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1099 MYANMAR SHAN DIGIT NINE | 1098 MYANMAR SHAN DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109b MYANMAR SIGN KHAMTI TONE-3 | 109a MYANMAR SIGN KHAMTI TONE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109d MYANMAR VOWEL SIGN AITON AI | 109c MYANMAR VOWEL SIGN AITON A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 109f MYANMAR SYMBOL SHAN EXCLAMATION | 109e MYANMAR SYMBOL SHAN ONE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a1 GEORGIAN CAPITAL LETTER BAN | 10a0 GEORGIAN CAPITAL LETTER AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a3 GEORGIAN CAPITAL LETTER DON | 10a2 GEORGIAN CAPITAL LETTER GAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a5 GEORGIAN CAPITAL LETTER VIN | 10a4 GEORGIAN CAPITAL LETTER EN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a7 GEORGIAN CAPITAL LETTER TAN | 10a6 GEORGIAN CAPITAL LETTER ZEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10a9 GEORGIAN CAPITAL LETTER KAN | 10a8 GEORGIAN CAPITAL LETTER IN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10ab GEORGIAN CAPITAL LETTER MAN | 10aa GEORGIAN CAPITAL LETTER LAS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10ad GEORGIAN CAPITAL LETTER ON | 10ac GEORGIAN CAPITAL LETTER NAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10af GEORGIAN CAPITAL LETTER ZHAR | 10ae GEORGIAN CAPITAL LETTER PAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b1 GEORGIAN CAPITAL LETTER SAN | 10b0 GEORGIAN CAPITAL LETTER RAE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b3 GEORGIAN CAPITAL LETTER UN | 10b2 GEORGIAN CAPITAL LETTER TAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b5 GEORGIAN CAPITAL LETTER KHAR | 10b4 GEORGIAN CAPITAL LETTER PHAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b7 GEORGIAN CAPITAL LETTER QAR | 10b6 GEORGIAN CAPITAL LETTER GHAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10b9 GEORGIAN CAPITAL LETTER CHIN | 10b8 GEORGIAN CAPITAL LETTER SHIN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bb GEORGIAN CAPITAL LETTER JIL | 10ba GEORGIAN CAPITAL LETTER CAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bd GEORGIAN CAPITAL LETTER CHAR | 10bc GEORGIAN CAPITAL LETTER CIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10bf GEORGIAN CAPITAL LETTER JHAN | 10be GEORGIAN CAPITAL LETTER XAN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c1 GEORGIAN CAPITAL LETTER HE | 10c0 GEORGIAN CAPITAL LETTER HAE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c3 GEORGIAN CAPITAL LETTER WE | 10c2 GEORGIAN CAPITAL LETTER HIE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 10c5 GEORGIAN CAPITAL LETTER HOE | 10c4 GEORGIAN CAPITAL LETTER HAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 10c7 GEORGIAN CAPITAL LETTER YN | 10c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10c9 (null) | 10c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10cb (null) | 10ca (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 10cd GEORGIAN CAPITAL LETTER AEN | 10cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 10cf (null) | 10ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d1 GEORGIAN LETTER BAN | 10d0 GEORGIAN LETTER AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d3 GEORGIAN LETTER DON | 10d2 GEORGIAN LETTER GAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d5 GEORGIAN LETTER VIN | 10d4 GEORGIAN LETTER EN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d7 GEORGIAN LETTER TAN | 10d6 GEORGIAN LETTER ZEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10d9 GEORGIAN LETTER KAN | 10d8 GEORGIAN LETTER IN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10db GEORGIAN LETTER MAN | 10da GEORGIAN LETTER LAS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10dd GEORGIAN LETTER ON | 10dc GEORGIAN LETTER NAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10df GEORGIAN LETTER ZHAR | 10de GEORGIAN LETTER PAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e1 GEORGIAN LETTER SAN | 10e0 GEORGIAN LETTER RAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e3 GEORGIAN LETTER UN | 10e2 GEORGIAN LETTER TAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e5 GEORGIAN LETTER KHAR | 10e4 GEORGIAN LETTER PHAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e7 GEORGIAN LETTER QAR | 10e6 GEORGIAN LETTER GHAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10e9 GEORGIAN LETTER CHIN | 10e8 GEORGIAN LETTER SHIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10eb GEORGIAN LETTER JIL | 10ea GEORGIAN LETTER CAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ed GEORGIAN LETTER CHAR | 10ec GEORGIAN LETTER CIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ef GEORGIAN LETTER JHAN | 10ee GEORGIAN LETTER XAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f1 GEORGIAN LETTER HE | 10f0 GEORGIAN LETTER HAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f3 GEORGIAN LETTER WE | 10f2 GEORGIAN LETTER HIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f5 GEORGIAN LETTER HOE | 10f4 GEORGIAN LETTER HAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f7 GEORGIAN LETTER YN | 10f6 GEORGIAN LETTER FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10f9 GEORGIAN LETTER TURNED GAN | 10f8 GEORGIAN LETTER ELIFI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 10fb GEORGIAN PARAGRAPH SEPARATOR | 10fa GEORGIAN LETTER AIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 10fd GEORGIAN LETTER AEN | 10fc MODIFIER LETTER GEORGIAN NAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 10ff GEORGIAN LETTER LABIAL SIGN | 10fe GEORGIAN LETTER HARD SIGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1101 HANGUL CHOSEONG SSANGKIYEOK | 1100 HANGUL CHOSEONG KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1103 HANGUL CHOSEONG TIKEUT | 1102 HANGUL CHOSEONG NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1105 HANGUL CHOSEONG RIEUL | 1104 HANGUL CHOSEONG SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1107 HANGUL CHOSEONG PIEUP | 1106 HANGUL CHOSEONG MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1109 HANGUL CHOSEONG SIOS | 1108 HANGUL CHOSEONG SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110b HANGUL CHOSEONG IEUNG | 110a HANGUL CHOSEONG SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110d HANGUL CHOSEONG SSANGCIEUC | 110c HANGUL CHOSEONG CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 110f HANGUL CHOSEONG KHIEUKH | 110e HANGUL CHOSEONG CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1111 HANGUL CHOSEONG PHIEUPH | 1110 HANGUL CHOSEONG THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1113 HANGUL CHOSEONG NIEUN-KIYEOK | 1112 HANGUL CHOSEONG HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1115 HANGUL CHOSEONG NIEUN-TIKEUT | 1114 HANGUL CHOSEONG SSANGNIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1117 HANGUL CHOSEONG TIKEUT-KIYEOK | 1116 HANGUL CHOSEONG NIEUN-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1119 HANGUL CHOSEONG SSANGRIEUL | 1118 HANGUL CHOSEONG RIEUL-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111b HANGUL CHOSEONG KAPYEOUNRIEUL | 111a HANGUL CHOSEONG RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111d HANGUL CHOSEONG KAPYEOUNMIEUM | 111c HANGUL CHOSEONG MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 111f HANGUL CHOSEONG PIEUP-NIEUN | 111e HANGUL CHOSEONG PIEUP-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1121 HANGUL CHOSEONG PIEUP-SIOS | 1120 HANGUL CHOSEONG PIEUP-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1123 HANGUL CHOSEONG PIEUP-SIOS-TIKEUT | 1122 HANGUL CHOSEONG PIEUP-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1125 HANGUL CHOSEONG PIEUP-SSANGSIOS | 1124 HANGUL CHOSEONG PIEUP-SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1127 HANGUL CHOSEONG PIEUP-CIEUC | 1126 HANGUL CHOSEONG PIEUP-SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1129 HANGUL CHOSEONG PIEUP-THIEUTH | 1128 HANGUL CHOSEONG PIEUP-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112b HANGUL CHOSEONG KAPYEOUNPIEUP | 112a HANGUL CHOSEONG PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112d HANGUL CHOSEONG SIOS-KIYEOK | 112c HANGUL CHOSEONG KAPYEOUNSSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 112f HANGUL CHOSEONG SIOS-TIKEUT | 112e HANGUL CHOSEONG SIOS-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1131 HANGUL CHOSEONG SIOS-MIEUM | 1130 HANGUL CHOSEONG SIOS-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1133 HANGUL CHOSEONG SIOS-PIEUP-KIYEOK | 1132 HANGUL CHOSEONG SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1135 HANGUL CHOSEONG SIOS-IEUNG | 1134 HANGUL CHOSEONG SIOS-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1137 HANGUL CHOSEONG SIOS-CHIEUCH | 1136 HANGUL CHOSEONG SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1139 HANGUL CHOSEONG SIOS-THIEUTH | 1138 HANGUL CHOSEONG SIOS-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113b HANGUL CHOSEONG SIOS-HIEUH | 113a HANGUL CHOSEONG SIOS-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113d HANGUL CHOSEONG CHITUEUMSSANGSIOS | 113c HANGUL CHOSEONG CHITUEUMSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 113f HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS | 113e HANGUL CHOSEONG CEONGCHIEUMSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1141 HANGUL CHOSEONG IEUNG-KIYEOK | 1140 HANGUL CHOSEONG PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1143 HANGUL CHOSEONG IEUNG-MIEUM | 1142 HANGUL CHOSEONG IEUNG-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1145 HANGUL CHOSEONG IEUNG-SIOS | 1144 HANGUL CHOSEONG IEUNG-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1147 HANGUL CHOSEONG SSANGIEUNG | 1146 HANGUL CHOSEONG IEUNG-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1149 HANGUL CHOSEONG IEUNG-CHIEUCH | 1148 HANGUL CHOSEONG IEUNG-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114b HANGUL CHOSEONG IEUNG-PHIEUPH | 114a HANGUL CHOSEONG IEUNG-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114d HANGUL CHOSEONG CIEUC-IEUNG | 114c HANGUL CHOSEONG YESIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 114f HANGUL CHOSEONG CHITUEUMSSANGCIEUC | 114e HANGUL CHOSEONG CHITUEUMCIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1151 HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC | 1150 HANGUL CHOSEONG CEONGCHIEUMCIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1153 HANGUL CHOSEONG CHIEUCH-HIEUH | 1152 HANGUL CHOSEONG CHIEUCH-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1155 HANGUL CHOSEONG CEONGCHIEUMCHIEUCH | 1154 HANGUL CHOSEONG CHITUEUMCHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1157 HANGUL CHOSEONG KAPYEOUNPHIEUPH | 1156 HANGUL CHOSEONG PHIEUPH-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1159 HANGUL CHOSEONG YEORINHIEUH | 1158 HANGUL CHOSEONG SSANGHIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115b HANGUL CHOSEONG NIEUN-SIOS | 115a HANGUL CHOSEONG KIYEOK-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115d HANGUL CHOSEONG NIEUN-HIEUH | 115c HANGUL CHOSEONG NIEUN-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 115f HANGUL CHOSEONG FILLER | 115e HANGUL CHOSEONG TIKEUT-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1161 HANGUL JUNGSEONG A | 1160 HANGUL JUNGSEONG FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1163 HANGUL JUNGSEONG YA | 1162 HANGUL JUNGSEONG AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1165 HANGUL JUNGSEONG EO | 1164 HANGUL JUNGSEONG YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1167 HANGUL JUNGSEONG YEO | 1166 HANGUL JUNGSEONG E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1169 HANGUL JUNGSEONG O | 1168 HANGUL JUNGSEONG YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116b HANGUL JUNGSEONG WAE | 116a HANGUL JUNGSEONG WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116d HANGUL JUNGSEONG YO | 116c HANGUL JUNGSEONG OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 116f HANGUL JUNGSEONG WEO | 116e HANGUL JUNGSEONG U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1171 HANGUL JUNGSEONG WI | 1170 HANGUL JUNGSEONG WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1173 HANGUL JUNGSEONG EU | 1172 HANGUL JUNGSEONG YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1175 HANGUL JUNGSEONG I | 1174 HANGUL JUNGSEONG YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1177 HANGUL JUNGSEONG A-U | 1176 HANGUL JUNGSEONG A-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1179 HANGUL JUNGSEONG YA-YO | 1178 HANGUL JUNGSEONG YA-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117b HANGUL JUNGSEONG EO-U | 117a HANGUL JUNGSEONG EO-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117d HANGUL JUNGSEONG YEO-O | 117c HANGUL JUNGSEONG EO-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 117f HANGUL JUNGSEONG O-EO | 117e HANGUL JUNGSEONG YEO-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1181 HANGUL JUNGSEONG O-YE | 1180 HANGUL JUNGSEONG O-E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1183 HANGUL JUNGSEONG O-U | 1182 HANGUL JUNGSEONG O-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1185 HANGUL JUNGSEONG YO-YAE | 1184 HANGUL JUNGSEONG YO-YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1187 HANGUL JUNGSEONG YO-O | 1186 HANGUL JUNGSEONG YO-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1189 HANGUL JUNGSEONG U-A | 1188 HANGUL JUNGSEONG YO-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118b HANGUL JUNGSEONG U-EO-EU | 118a HANGUL JUNGSEONG U-AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118d HANGUL JUNGSEONG U-U | 118c HANGUL JUNGSEONG U-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 118f HANGUL JUNGSEONG YU-EO | 118e HANGUL JUNGSEONG YU-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1191 HANGUL JUNGSEONG YU-YEO | 1190 HANGUL JUNGSEONG YU-E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1193 HANGUL JUNGSEONG YU-U | 1192 HANGUL JUNGSEONG YU-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1195 HANGUL JUNGSEONG EU-U | 1194 HANGUL JUNGSEONG YU-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1197 HANGUL JUNGSEONG YI-U | 1196 HANGUL JUNGSEONG EU-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1199 HANGUL JUNGSEONG I-YA | 1198 HANGUL JUNGSEONG I-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119b HANGUL JUNGSEONG I-U | 119a HANGUL JUNGSEONG I-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119d HANGUL JUNGSEONG I-ARAEA | 119c HANGUL JUNGSEONG I-EU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 119f HANGUL JUNGSEONG ARAEA-EO | 119e HANGUL JUNGSEONG ARAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a1 HANGUL JUNGSEONG ARAEA-I | 11a0 HANGUL JUNGSEONG ARAEA-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a3 HANGUL JUNGSEONG A-EU | 11a2 HANGUL JUNGSEONG SSANGARAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a5 HANGUL JUNGSEONG YEO-YA | 11a4 HANGUL JUNGSEONG YA-U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a7 HANGUL JUNGSEONG O-YAE | 11a6 HANGUL JUNGSEONG O-YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11a9 HANGUL JONGSEONG SSANGKIYEOK | 11a8 HANGUL JONGSEONG KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ab HANGUL JONGSEONG NIEUN | 11aa HANGUL JONGSEONG KIYEOK-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ad HANGUL JONGSEONG NIEUN-HIEUH | 11ac HANGUL JONGSEONG NIEUN-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11af HANGUL JONGSEONG RIEUL | 11ae HANGUL JONGSEONG TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b1 HANGUL JONGSEONG RIEUL-MIEUM | 11b0 HANGUL JONGSEONG RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b3 HANGUL JONGSEONG RIEUL-SIOS | 11b2 HANGUL JONGSEONG RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b5 HANGUL JONGSEONG RIEUL-PHIEUPH | 11b4 HANGUL JONGSEONG RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b7 HANGUL JONGSEONG MIEUM | 11b6 HANGUL JONGSEONG RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11b9 HANGUL JONGSEONG PIEUP-SIOS | 11b8 HANGUL JONGSEONG PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bb HANGUL JONGSEONG SSANGSIOS | 11ba HANGUL JONGSEONG SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bd HANGUL JONGSEONG CIEUC | 11bc HANGUL JONGSEONG IEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11bf HANGUL JONGSEONG KHIEUKH | 11be HANGUL JONGSEONG CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c1 HANGUL JONGSEONG PHIEUPH | 11c0 HANGUL JONGSEONG THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c3 HANGUL JONGSEONG KIYEOK-RIEUL | 11c2 HANGUL JONGSEONG HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c5 HANGUL JONGSEONG NIEUN-KIYEOK | 11c4 HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c7 HANGUL JONGSEONG NIEUN-SIOS | 11c6 HANGUL JONGSEONG NIEUN-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11c9 HANGUL JONGSEONG NIEUN-THIEUTH | 11c8 HANGUL JONGSEONG NIEUN-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cb HANGUL JONGSEONG TIKEUT-RIEUL | 11ca HANGUL JONGSEONG TIKEUT-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cd HANGUL JONGSEONG RIEUL-NIEUN | 11cc HANGUL JONGSEONG RIEUL-KIYEOK-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11cf HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH | 11ce HANGUL JONGSEONG RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d1 HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK | 11d0 HANGUL JONGSEONG SSANGRIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d3 HANGUL JONGSEONG RIEUL-PIEUP-SIOS | 11d2 HANGUL JONGSEONG RIEUL-MIEUM-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d5 HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP | 11d4 HANGUL JONGSEONG RIEUL-PIEUP-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d7 HANGUL JONGSEONG RIEUL-PANSIOS | 11d6 HANGUL JONGSEONG RIEUL-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11d9 HANGUL JONGSEONG RIEUL-YEORINHIEUH | 11d8 HANGUL JONGSEONG RIEUL-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11db HANGUL JONGSEONG MIEUM-RIEUL | 11da HANGUL JONGSEONG MIEUM-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11dd HANGUL JONGSEONG MIEUM-SIOS | 11dc HANGUL JONGSEONG MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11df HANGUL JONGSEONG MIEUM-PANSIOS | 11de HANGUL JONGSEONG MIEUM-SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e1 HANGUL JONGSEONG MIEUM-HIEUH | 11e0 HANGUL JONGSEONG MIEUM-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e3 HANGUL JONGSEONG PIEUP-RIEUL | 11e2 HANGUL JONGSEONG KAPYEOUNMIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e5 HANGUL JONGSEONG PIEUP-HIEUH | 11e4 HANGUL JONGSEONG PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e7 HANGUL JONGSEONG SIOS-KIYEOK | 11e6 HANGUL JONGSEONG KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11e9 HANGUL JONGSEONG SIOS-RIEUL | 11e8 HANGUL JONGSEONG SIOS-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11eb HANGUL JONGSEONG PANSIOS | 11ea HANGUL JONGSEONG SIOS-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ed HANGUL JONGSEONG IEUNG-SSANGKIYEOK | 11ec HANGUL JONGSEONG IEUNG-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ef HANGUL JONGSEONG IEUNG-KHIEUKH | 11ee HANGUL JONGSEONG SSANGIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f1 HANGUL JONGSEONG YESIEUNG-SIOS | 11f0 HANGUL JONGSEONG YESIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f3 HANGUL JONGSEONG PHIEUPH-PIEUP | 11f2 HANGUL JONGSEONG YESIEUNG-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f5 HANGUL JONGSEONG HIEUH-NIEUN | 11f4 HANGUL JONGSEONG KAPYEOUNPHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f7 HANGUL JONGSEONG HIEUH-MIEUM | 11f6 HANGUL JONGSEONG HIEUH-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11f9 HANGUL JONGSEONG YEORINHIEUH | 11f8 HANGUL JONGSEONG HIEUH-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11fb HANGUL JONGSEONG KIYEOK-PIEUP | 11fa HANGUL JONGSEONG KIYEOK-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11fd HANGUL JONGSEONG KIYEOK-KHIEUKH | 11fc HANGUL JONGSEONG KIYEOK-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 11ff HANGUL JONGSEONG SSANGNIEUN | 11fe HANGUL JONGSEONG KIYEOK-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1201 ETHIOPIC SYLLABLE HU | 1200 ETHIOPIC SYLLABLE HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1203 ETHIOPIC SYLLABLE HAA | 1202 ETHIOPIC SYLLABLE HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1205 ETHIOPIC SYLLABLE HE | 1204 ETHIOPIC SYLLABLE HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1207 ETHIOPIC SYLLABLE HOA | 1206 ETHIOPIC SYLLABLE HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1209 ETHIOPIC SYLLABLE LU | 1208 ETHIOPIC SYLLABLE LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120b ETHIOPIC SYLLABLE LAA | 120a ETHIOPIC SYLLABLE LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120d ETHIOPIC SYLLABLE LE | 120c ETHIOPIC SYLLABLE LEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 120f ETHIOPIC SYLLABLE LWA | 120e ETHIOPIC SYLLABLE LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1211 ETHIOPIC SYLLABLE HHU | 1210 ETHIOPIC SYLLABLE HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1213 ETHIOPIC SYLLABLE HHAA | 1212 ETHIOPIC SYLLABLE HHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1215 ETHIOPIC SYLLABLE HHE | 1214 ETHIOPIC SYLLABLE HHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1217 ETHIOPIC SYLLABLE HHWA | 1216 ETHIOPIC SYLLABLE HHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1219 ETHIOPIC SYLLABLE MU | 1218 ETHIOPIC SYLLABLE MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121b ETHIOPIC SYLLABLE MAA | 121a ETHIOPIC SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121d ETHIOPIC SYLLABLE ME | 121c ETHIOPIC SYLLABLE MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 121f ETHIOPIC SYLLABLE MWA | 121e ETHIOPIC SYLLABLE MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1221 ETHIOPIC SYLLABLE SZU | 1220 ETHIOPIC SYLLABLE SZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1223 ETHIOPIC SYLLABLE SZAA | 1222 ETHIOPIC SYLLABLE SZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1225 ETHIOPIC SYLLABLE SZE | 1224 ETHIOPIC SYLLABLE SZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1227 ETHIOPIC SYLLABLE SZWA | 1226 ETHIOPIC SYLLABLE SZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1229 ETHIOPIC SYLLABLE RU | 1228 ETHIOPIC SYLLABLE RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122b ETHIOPIC SYLLABLE RAA | 122a ETHIOPIC SYLLABLE RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122d ETHIOPIC SYLLABLE RE | 122c ETHIOPIC SYLLABLE REE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 122f ETHIOPIC SYLLABLE RWA | 122e ETHIOPIC SYLLABLE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1231 ETHIOPIC SYLLABLE SU | 1230 ETHIOPIC SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1233 ETHIOPIC SYLLABLE SAA | 1232 ETHIOPIC SYLLABLE SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1235 ETHIOPIC SYLLABLE SE | 1234 ETHIOPIC SYLLABLE SEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1237 ETHIOPIC SYLLABLE SWA | 1236 ETHIOPIC SYLLABLE SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1239 ETHIOPIC SYLLABLE SHU | 1238 ETHIOPIC SYLLABLE SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123b ETHIOPIC SYLLABLE SHAA | 123a ETHIOPIC SYLLABLE SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123d ETHIOPIC SYLLABLE SHE | 123c ETHIOPIC SYLLABLE SHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 123f ETHIOPIC SYLLABLE SHWA | 123e ETHIOPIC SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1241 ETHIOPIC SYLLABLE QU | 1240 ETHIOPIC SYLLABLE QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1243 ETHIOPIC SYLLABLE QAA | 1242 ETHIOPIC SYLLABLE QI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1245 ETHIOPIC SYLLABLE QE | 1244 ETHIOPIC SYLLABLE QEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1247 ETHIOPIC SYLLABLE QOA | 1246 ETHIOPIC SYLLABLE QO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1249 (null) | 1248 ETHIOPIC SYLLABLE QWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 124b ETHIOPIC SYLLABLE QWAA | 124a ETHIOPIC SYLLABLE QWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 124d ETHIOPIC SYLLABLE QWE | 124c ETHIOPIC SYLLABLE QWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 124f (null) | 124e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1251 ETHIOPIC SYLLABLE QHU | 1250 ETHIOPIC SYLLABLE QHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1253 ETHIOPIC SYLLABLE QHAA | 1252 ETHIOPIC SYLLABLE QHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1255 ETHIOPIC SYLLABLE QHE | 1254 ETHIOPIC SYLLABLE QHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1257 (null) | 1256 ETHIOPIC SYLLABLE QHO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1259 (null) | 1258 ETHIOPIC SYLLABLE QHWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 125b ETHIOPIC SYLLABLE QHWAA | 125a ETHIOPIC SYLLABLE QHWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 125d ETHIOPIC SYLLABLE QHWE | 125c ETHIOPIC SYLLABLE QHWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 125f (null) | 125e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1261 ETHIOPIC SYLLABLE BU | 1260 ETHIOPIC SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1263 ETHIOPIC SYLLABLE BAA | 1262 ETHIOPIC SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1265 ETHIOPIC SYLLABLE BE | 1264 ETHIOPIC SYLLABLE BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1267 ETHIOPIC SYLLABLE BWA | 1266 ETHIOPIC SYLLABLE BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1269 ETHIOPIC SYLLABLE VU | 1268 ETHIOPIC SYLLABLE VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126b ETHIOPIC SYLLABLE VAA | 126a ETHIOPIC SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126d ETHIOPIC SYLLABLE VE | 126c ETHIOPIC SYLLABLE VEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 126f ETHIOPIC SYLLABLE VWA | 126e ETHIOPIC SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1271 ETHIOPIC SYLLABLE TU | 1270 ETHIOPIC SYLLABLE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1273 ETHIOPIC SYLLABLE TAA | 1272 ETHIOPIC SYLLABLE TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1275 ETHIOPIC SYLLABLE TE | 1274 ETHIOPIC SYLLABLE TEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1277 ETHIOPIC SYLLABLE TWA | 1276 ETHIOPIC SYLLABLE TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1279 ETHIOPIC SYLLABLE CU | 1278 ETHIOPIC SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127b ETHIOPIC SYLLABLE CAA | 127a ETHIOPIC SYLLABLE CI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127d ETHIOPIC SYLLABLE CE | 127c ETHIOPIC SYLLABLE CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 127f ETHIOPIC SYLLABLE CWA | 127e ETHIOPIC SYLLABLE CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1281 ETHIOPIC SYLLABLE XU | 1280 ETHIOPIC SYLLABLE XA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1283 ETHIOPIC SYLLABLE XAA | 1282 ETHIOPIC SYLLABLE XI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1285 ETHIOPIC SYLLABLE XE | 1284 ETHIOPIC SYLLABLE XEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1287 ETHIOPIC SYLLABLE XOA | 1286 ETHIOPIC SYLLABLE XO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1289 (null) | 1288 ETHIOPIC SYLLABLE XWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 128b ETHIOPIC SYLLABLE XWAA | 128a ETHIOPIC SYLLABLE XWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 128d ETHIOPIC SYLLABLE XWE | 128c ETHIOPIC SYLLABLE XWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 128f (null) | 128e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1291 ETHIOPIC SYLLABLE NU | 1290 ETHIOPIC SYLLABLE NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1293 ETHIOPIC SYLLABLE NAA | 1292 ETHIOPIC SYLLABLE NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1295 ETHIOPIC SYLLABLE NE | 1294 ETHIOPIC SYLLABLE NEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1297 ETHIOPIC SYLLABLE NWA | 1296 ETHIOPIC SYLLABLE NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1299 ETHIOPIC SYLLABLE NYU | 1298 ETHIOPIC SYLLABLE NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129b ETHIOPIC SYLLABLE NYAA | 129a ETHIOPIC SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129d ETHIOPIC SYLLABLE NYE | 129c ETHIOPIC SYLLABLE NYEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 129f ETHIOPIC SYLLABLE NYWA | 129e ETHIOPIC SYLLABLE NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a1 ETHIOPIC SYLLABLE GLOTTAL U | 12a0 ETHIOPIC SYLLABLE GLOTTAL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a3 ETHIOPIC SYLLABLE GLOTTAL AA | 12a2 ETHIOPIC SYLLABLE GLOTTAL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a5 ETHIOPIC SYLLABLE GLOTTAL E | 12a4 ETHIOPIC SYLLABLE GLOTTAL EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a7 ETHIOPIC SYLLABLE GLOTTAL WA | 12a6 ETHIOPIC SYLLABLE GLOTTAL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12a9 ETHIOPIC SYLLABLE KU | 12a8 ETHIOPIC SYLLABLE KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ab ETHIOPIC SYLLABLE KAA | 12aa ETHIOPIC SYLLABLE KI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ad ETHIOPIC SYLLABLE KE | 12ac ETHIOPIC SYLLABLE KEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12af ETHIOPIC SYLLABLE KOA | 12ae ETHIOPIC SYLLABLE KO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12b1 (null) | 12b0 ETHIOPIC SYLLABLE KWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b3 ETHIOPIC SYLLABLE KWAA | 12b2 ETHIOPIC SYLLABLE KWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b5 ETHIOPIC SYLLABLE KWE | 12b4 ETHIOPIC SYLLABLE KWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 12b7 (null) | 12b6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12b9 ETHIOPIC SYLLABLE KXU | 12b8 ETHIOPIC SYLLABLE KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12bb ETHIOPIC SYLLABLE KXAA | 12ba ETHIOPIC SYLLABLE KXI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12bd ETHIOPIC SYLLABLE KXE | 12bc ETHIOPIC SYLLABLE KXEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12bf (null) | 12be ETHIOPIC SYLLABLE KXO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12c1 (null) | 12c0 ETHIOPIC SYLLABLE KXWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c3 ETHIOPIC SYLLABLE KXWAA | 12c2 ETHIOPIC SYLLABLE KXWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c5 ETHIOPIC SYLLABLE KXWE | 12c4 ETHIOPIC SYLLABLE KXWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 12c7 (null) | 12c6 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12c9 ETHIOPIC SYLLABLE WU | 12c8 ETHIOPIC SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cb ETHIOPIC SYLLABLE WAA | 12ca ETHIOPIC SYLLABLE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cd ETHIOPIC SYLLABLE WE | 12cc ETHIOPIC SYLLABLE WEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12cf ETHIOPIC SYLLABLE WOA | 12ce ETHIOPIC SYLLABLE WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d1 ETHIOPIC SYLLABLE PHARYNGEAL U | 12d0 ETHIOPIC SYLLABLE PHARYNGEAL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d3 ETHIOPIC SYLLABLE PHARYNGEAL AA | 12d2 ETHIOPIC SYLLABLE PHARYNGEAL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d5 ETHIOPIC SYLLABLE PHARYNGEAL E | 12d4 ETHIOPIC SYLLABLE PHARYNGEAL EE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 12d7 (null) | 12d6 ETHIOPIC SYLLABLE PHARYNGEAL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12d9 ETHIOPIC SYLLABLE ZU | 12d8 ETHIOPIC SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12db ETHIOPIC SYLLABLE ZAA | 12da ETHIOPIC SYLLABLE ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12dd ETHIOPIC SYLLABLE ZE | 12dc ETHIOPIC SYLLABLE ZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12df ETHIOPIC SYLLABLE ZWA | 12de ETHIOPIC SYLLABLE ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e1 ETHIOPIC SYLLABLE ZHU | 12e0 ETHIOPIC SYLLABLE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e3 ETHIOPIC SYLLABLE ZHAA | 12e2 ETHIOPIC SYLLABLE ZHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e5 ETHIOPIC SYLLABLE ZHE | 12e4 ETHIOPIC SYLLABLE ZHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e7 ETHIOPIC SYLLABLE ZHWA | 12e6 ETHIOPIC SYLLABLE ZHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12e9 ETHIOPIC SYLLABLE YU | 12e8 ETHIOPIC SYLLABLE YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12eb ETHIOPIC SYLLABLE YAA | 12ea ETHIOPIC SYLLABLE YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ed ETHIOPIC SYLLABLE YE | 12ec ETHIOPIC SYLLABLE YEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ef ETHIOPIC SYLLABLE YOA | 12ee ETHIOPIC SYLLABLE YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f1 ETHIOPIC SYLLABLE DU | 12f0 ETHIOPIC SYLLABLE DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f3 ETHIOPIC SYLLABLE DAA | 12f2 ETHIOPIC SYLLABLE DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f5 ETHIOPIC SYLLABLE DE | 12f4 ETHIOPIC SYLLABLE DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f7 ETHIOPIC SYLLABLE DWA | 12f6 ETHIOPIC SYLLABLE DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12f9 ETHIOPIC SYLLABLE DDU | 12f8 ETHIOPIC SYLLABLE DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12fb ETHIOPIC SYLLABLE DDAA | 12fa ETHIOPIC SYLLABLE DDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12fd ETHIOPIC SYLLABLE DDE | 12fc ETHIOPIC SYLLABLE DDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 12ff ETHIOPIC SYLLABLE DDWA | 12fe ETHIOPIC SYLLABLE DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1301 ETHIOPIC SYLLABLE JU | 1300 ETHIOPIC SYLLABLE JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1303 ETHIOPIC SYLLABLE JAA | 1302 ETHIOPIC SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1305 ETHIOPIC SYLLABLE JE | 1304 ETHIOPIC SYLLABLE JEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1307 ETHIOPIC SYLLABLE JWA | 1306 ETHIOPIC SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1309 ETHIOPIC SYLLABLE GU | 1308 ETHIOPIC SYLLABLE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130b ETHIOPIC SYLLABLE GAA | 130a ETHIOPIC SYLLABLE GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130d ETHIOPIC SYLLABLE GE | 130c ETHIOPIC SYLLABLE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 130f ETHIOPIC SYLLABLE GOA | 130e ETHIOPIC SYLLABLE GO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1311 (null) | 1310 ETHIOPIC SYLLABLE GWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1313 ETHIOPIC SYLLABLE GWAA | 1312 ETHIOPIC SYLLABLE GWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1315 ETHIOPIC SYLLABLE GWE | 1314 ETHIOPIC SYLLABLE GWEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1317 (null) | 1316 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1319 ETHIOPIC SYLLABLE GGU | 1318 ETHIOPIC SYLLABLE GGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131b ETHIOPIC SYLLABLE GGAA | 131a ETHIOPIC SYLLABLE GGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131d ETHIOPIC SYLLABLE GGE | 131c ETHIOPIC SYLLABLE GGEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 131f ETHIOPIC SYLLABLE GGWAA | 131e ETHIOPIC SYLLABLE GGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1321 ETHIOPIC SYLLABLE THU | 1320 ETHIOPIC SYLLABLE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1323 ETHIOPIC SYLLABLE THAA | 1322 ETHIOPIC SYLLABLE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1325 ETHIOPIC SYLLABLE THE | 1324 ETHIOPIC SYLLABLE THEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1327 ETHIOPIC SYLLABLE THWA | 1326 ETHIOPIC SYLLABLE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1329 ETHIOPIC SYLLABLE CHU | 1328 ETHIOPIC SYLLABLE CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132b ETHIOPIC SYLLABLE CHAA | 132a ETHIOPIC SYLLABLE CHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132d ETHIOPIC SYLLABLE CHE | 132c ETHIOPIC SYLLABLE CHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 132f ETHIOPIC SYLLABLE CHWA | 132e ETHIOPIC SYLLABLE CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1331 ETHIOPIC SYLLABLE PHU | 1330 ETHIOPIC SYLLABLE PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1333 ETHIOPIC SYLLABLE PHAA | 1332 ETHIOPIC SYLLABLE PHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1335 ETHIOPIC SYLLABLE PHE | 1334 ETHIOPIC SYLLABLE PHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1337 ETHIOPIC SYLLABLE PHWA | 1336 ETHIOPIC SYLLABLE PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1339 ETHIOPIC SYLLABLE TSU | 1338 ETHIOPIC SYLLABLE TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133b ETHIOPIC SYLLABLE TSAA | 133a ETHIOPIC SYLLABLE TSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133d ETHIOPIC SYLLABLE TSE | 133c ETHIOPIC SYLLABLE TSEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 133f ETHIOPIC SYLLABLE TSWA | 133e ETHIOPIC SYLLABLE TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1341 ETHIOPIC SYLLABLE TZU | 1340 ETHIOPIC SYLLABLE TZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1343 ETHIOPIC SYLLABLE TZAA | 1342 ETHIOPIC SYLLABLE TZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1345 ETHIOPIC SYLLABLE TZE | 1344 ETHIOPIC SYLLABLE TZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1347 ETHIOPIC SYLLABLE TZOA | 1346 ETHIOPIC SYLLABLE TZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1349 ETHIOPIC SYLLABLE FU | 1348 ETHIOPIC SYLLABLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134b ETHIOPIC SYLLABLE FAA | 134a ETHIOPIC SYLLABLE FI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134d ETHIOPIC SYLLABLE FE | 134c ETHIOPIC SYLLABLE FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 134f ETHIOPIC SYLLABLE FWA | 134e ETHIOPIC SYLLABLE FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1351 ETHIOPIC SYLLABLE PU | 1350 ETHIOPIC SYLLABLE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1353 ETHIOPIC SYLLABLE PAA | 1352 ETHIOPIC SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1355 ETHIOPIC SYLLABLE PE | 1354 ETHIOPIC SYLLABLE PEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1357 ETHIOPIC SYLLABLE PWA | 1356 ETHIOPIC SYLLABLE PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1359 ETHIOPIC SYLLABLE MYA | 1358 ETHIOPIC SYLLABLE RYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 135b (null) | 135a ETHIOPIC SYLLABLE FYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 135d ETHIOPIC COMBINING GEMINATION AND VOWEL | 135c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 135f ETHIOPIC COMBINING GEMINATION MARK | 135e ETHIOPIC COMBINING VOWEL LENGTH MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1361 ETHIOPIC WORDSPACE | 1360 ETHIOPIC SECTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1363 ETHIOPIC COMMA | 1362 ETHIOPIC FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1365 ETHIOPIC COLON | 1364 ETHIOPIC SEMICOLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1367 ETHIOPIC QUESTION MARK | 1366 ETHIOPIC PREFACE COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1369 ETHIOPIC DIGIT ONE | 1368 ETHIOPIC PARAGRAPH SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136b ETHIOPIC DIGIT THREE | 136a ETHIOPIC DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136d ETHIOPIC DIGIT FIVE | 136c ETHIOPIC DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 136f ETHIOPIC DIGIT SEVEN | 136e ETHIOPIC DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1371 ETHIOPIC DIGIT NINE | 1370 ETHIOPIC DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1373 ETHIOPIC NUMBER TWENTY | 1372 ETHIOPIC NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1375 ETHIOPIC NUMBER FORTY | 1374 ETHIOPIC NUMBER THIRTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1377 ETHIOPIC NUMBER SIXTY | 1376 ETHIOPIC NUMBER FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1379 ETHIOPIC NUMBER EIGHTY | 1378 ETHIOPIC NUMBER SEVENTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 137b ETHIOPIC NUMBER HUNDRED | 137a ETHIOPIC NUMBER NINETY */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 137d (null) | 137c ETHIOPIC NUMBER TEN THOUSAND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 137f (null) | 137e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1381 ETHIOPIC SYLLABLE MWI | 1380 ETHIOPIC SYLLABLE SEBATBEIT MWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1383 ETHIOPIC SYLLABLE MWE | 1382 ETHIOPIC SYLLABLE MWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1385 ETHIOPIC SYLLABLE BWI | 1384 ETHIOPIC SYLLABLE SEBATBEIT BWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1387 ETHIOPIC SYLLABLE BWE | 1386 ETHIOPIC SYLLABLE BWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1389 ETHIOPIC SYLLABLE FWI | 1388 ETHIOPIC SYLLABLE SEBATBEIT FWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138b ETHIOPIC SYLLABLE FWE | 138a ETHIOPIC SYLLABLE FWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138d ETHIOPIC SYLLABLE PWI | 138c ETHIOPIC SYLLABLE SEBATBEIT PWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 138f ETHIOPIC SYLLABLE PWE | 138e ETHIOPIC SYLLABLE PWEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1391 ETHIOPIC TONAL MARK DERET | 1390 ETHIOPIC TONAL MARK YIZET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1393 ETHIOPIC TONAL MARK SHORT RIKRIK | 1392 ETHIOPIC TONAL MARK RIKRIK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1395 ETHIOPIC TONAL MARK KENAT | 1394 ETHIOPIC TONAL MARK DIFAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1397 ETHIOPIC TONAL MARK HIDET | 1396 ETHIOPIC TONAL MARK CHIRET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1399 ETHIOPIC TONAL MARK KURT | 1398 ETHIOPIC TONAL MARK DERET-HIDET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139b (null) | 139a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139d (null) | 139c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 139f (null) | 139e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a1 CHEROKEE LETTER E | 13a0 CHEROKEE LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a3 CHEROKEE LETTER O | 13a2 CHEROKEE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a5 CHEROKEE LETTER V | 13a4 CHEROKEE LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a7 CHEROKEE LETTER KA | 13a6 CHEROKEE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13a9 CHEROKEE LETTER GI | 13a8 CHEROKEE LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ab CHEROKEE LETTER GU | 13aa CHEROKEE LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ad CHEROKEE LETTER HA | 13ac CHEROKEE LETTER GV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13af CHEROKEE LETTER HI | 13ae CHEROKEE LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b1 CHEROKEE LETTER HU | 13b0 CHEROKEE LETTER HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b3 CHEROKEE LETTER LA | 13b2 CHEROKEE LETTER HV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b5 CHEROKEE LETTER LI | 13b4 CHEROKEE LETTER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b7 CHEROKEE LETTER LU | 13b6 CHEROKEE LETTER LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13b9 CHEROKEE LETTER MA | 13b8 CHEROKEE LETTER LV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bb CHEROKEE LETTER MI | 13ba CHEROKEE LETTER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bd CHEROKEE LETTER MU | 13bc CHEROKEE LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13bf CHEROKEE LETTER HNA | 13be CHEROKEE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c1 CHEROKEE LETTER NE | 13c0 CHEROKEE LETTER NAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c3 CHEROKEE LETTER NO | 13c2 CHEROKEE LETTER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c5 CHEROKEE LETTER NV | 13c4 CHEROKEE LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c7 CHEROKEE LETTER QUE | 13c6 CHEROKEE LETTER QUA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13c9 CHEROKEE LETTER QUO | 13c8 CHEROKEE LETTER QUI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cb CHEROKEE LETTER QUV | 13ca CHEROKEE LETTER QUU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cd CHEROKEE LETTER S | 13cc CHEROKEE LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13cf CHEROKEE LETTER SI | 13ce CHEROKEE LETTER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d1 CHEROKEE LETTER SU | 13d0 CHEROKEE LETTER SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d3 CHEROKEE LETTER DA | 13d2 CHEROKEE LETTER SV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d5 CHEROKEE LETTER DE | 13d4 CHEROKEE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d7 CHEROKEE LETTER DI | 13d6 CHEROKEE LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13d9 CHEROKEE LETTER DO | 13d8 CHEROKEE LETTER TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13db CHEROKEE LETTER DV | 13da CHEROKEE LETTER DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13dd CHEROKEE LETTER TLA | 13dc CHEROKEE LETTER DLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13df CHEROKEE LETTER TLI | 13de CHEROKEE LETTER TLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e1 CHEROKEE LETTER TLU | 13e0 CHEROKEE LETTER TLO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e3 CHEROKEE LETTER TSA | 13e2 CHEROKEE LETTER TLV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e5 CHEROKEE LETTER TSI | 13e4 CHEROKEE LETTER TSE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e7 CHEROKEE LETTER TSU | 13e6 CHEROKEE LETTER TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13e9 CHEROKEE LETTER WA | 13e8 CHEROKEE LETTER TSV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13eb CHEROKEE LETTER WI | 13ea CHEROKEE LETTER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ed CHEROKEE LETTER WU | 13ec CHEROKEE LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13ef CHEROKEE LETTER YA | 13ee CHEROKEE LETTER WV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13f1 CHEROKEE LETTER YI | 13f0 CHEROKEE LETTER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 13f3 CHEROKEE LETTER YU | 13f2 CHEROKEE LETTER YO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 13f5 (null) | 13f4 CHEROKEE LETTER YV */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13f7 (null) | 13f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13f9 (null) | 13f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13fb (null) | 13fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13fd (null) | 13fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 13ff (null) | 13fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 1401 CANADIAN SYLLABICS E | 1400 CANADIAN SYLLABICS HYPHEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1403 CANADIAN SYLLABICS I | 1402 CANADIAN SYLLABICS AAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1405 CANADIAN SYLLABICS O | 1404 CANADIAN SYLLABICS II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1407 CANADIAN SYLLABICS Y-CREE OO | 1406 CANADIAN SYLLABICS OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1409 CANADIAN SYLLABICS CARRIER I | 1408 CANADIAN SYLLABICS CARRIER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140b CANADIAN SYLLABICS AA | 140a CANADIAN SYLLABICS A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140d CANADIAN SYLLABICS WEST-CREE WE | 140c CANADIAN SYLLABICS WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 140f CANADIAN SYLLABICS WEST-CREE WI | 140e CANADIAN SYLLABICS WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1411 CANADIAN SYLLABICS WEST-CREE WII | 1410 CANADIAN SYLLABICS WII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1413 CANADIAN SYLLABICS WEST-CREE WO | 1412 CANADIAN SYLLABICS WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1415 CANADIAN SYLLABICS WEST-CREE WOO | 1414 CANADIAN SYLLABICS WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1417 CANADIAN SYLLABICS WA | 1416 CANADIAN SYLLABICS NASKAPI WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1419 CANADIAN SYLLABICS WAA | 1418 CANADIAN SYLLABICS WEST-CREE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141b CANADIAN SYLLABICS NASKAPI WAA | 141a CANADIAN SYLLABICS WEST-CREE WAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141d CANADIAN SYLLABICS Y-CREE W | 141c CANADIAN SYLLABICS AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 141f CANADIAN SYLLABICS FINAL ACUTE | 141e CANADIAN SYLLABICS GLOTTAL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1421 CANADIAN SYLLABICS FINAL BOTTOM HALF RI | 1420 CANADIAN SYLLABICS FINAL GRAVE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1423 CANADIAN SYLLABICS FINAL RIGHT HALF RIN | 1422 CANADIAN SYLLABICS FINAL TOP HALF RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1425 CANADIAN SYLLABICS FINAL DOUBLE ACUTE | 1424 CANADIAN SYLLABICS FINAL RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1427 CANADIAN SYLLABICS FINAL MIDDLE DOT | 1426 CANADIAN SYLLABICS FINAL DOUBLE SHORT V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1429 CANADIAN SYLLABICS FINAL PLUS | 1428 CANADIAN SYLLABICS FINAL SHORT HORIZONT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142b CANADIAN SYLLABICS EN | 142a CANADIAN SYLLABICS FINAL DOWN TACK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142d CANADIAN SYLLABICS ON | 142c CANADIAN SYLLABICS IN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 142f CANADIAN SYLLABICS PE | 142e CANADIAN SYLLABICS AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1431 CANADIAN SYLLABICS PI | 1430 CANADIAN SYLLABICS PAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1433 CANADIAN SYLLABICS PO | 1432 CANADIAN SYLLABICS PII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1435 CANADIAN SYLLABICS Y-CREE POO | 1434 CANADIAN SYLLABICS POO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1437 CANADIAN SYLLABICS CARRIER HI | 1436 CANADIAN SYLLABICS CARRIER HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1439 CANADIAN SYLLABICS PAA | 1438 CANADIAN SYLLABICS PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143b CANADIAN SYLLABICS WEST-CREE PWE | 143a CANADIAN SYLLABICS PWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143d CANADIAN SYLLABICS WEST-CREE PWI | 143c CANADIAN SYLLABICS PWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 143f CANADIAN SYLLABICS WEST-CREE PWII | 143e CANADIAN SYLLABICS PWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1441 CANADIAN SYLLABICS WEST-CREE PWO | 1440 CANADIAN SYLLABICS PWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1443 CANADIAN SYLLABICS WEST-CREE PWOO | 1442 CANADIAN SYLLABICS PWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1445 CANADIAN SYLLABICS WEST-CREE PWA | 1444 CANADIAN SYLLABICS PWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1447 CANADIAN SYLLABICS WEST-CREE PWAA | 1446 CANADIAN SYLLABICS PWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1449 CANADIAN SYLLABICS P | 1448 CANADIAN SYLLABICS Y-CREE PWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144b CANADIAN SYLLABICS CARRIER H | 144a CANADIAN SYLLABICS WEST-CREE P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144d CANADIAN SYLLABICS TAAI | 144c CANADIAN SYLLABICS TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 144f CANADIAN SYLLABICS TII | 144e CANADIAN SYLLABICS TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1451 CANADIAN SYLLABICS TOO | 1450 CANADIAN SYLLABICS TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1453 CANADIAN SYLLABICS CARRIER DEE | 1452 CANADIAN SYLLABICS Y-CREE TOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1455 CANADIAN SYLLABICS TA | 1454 CANADIAN SYLLABICS CARRIER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1457 CANADIAN SYLLABICS TWE | 1456 CANADIAN SYLLABICS TAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1459 CANADIAN SYLLABICS TWI | 1458 CANADIAN SYLLABICS WEST-CREE TWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145b CANADIAN SYLLABICS TWII | 145a CANADIAN SYLLABICS WEST-CREE TWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145d CANADIAN SYLLABICS TWO | 145c CANADIAN SYLLABICS WEST-CREE TWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 145f CANADIAN SYLLABICS TWOO | 145e CANADIAN SYLLABICS WEST-CREE TWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1461 CANADIAN SYLLABICS TWA | 1460 CANADIAN SYLLABICS WEST-CREE TWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1463 CANADIAN SYLLABICS TWAA | 1462 CANADIAN SYLLABICS WEST-CREE TWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1465 CANADIAN SYLLABICS NASKAPI TWAA | 1464 CANADIAN SYLLABICS WEST-CREE TWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1467 CANADIAN SYLLABICS TTE | 1466 CANADIAN SYLLABICS T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1469 CANADIAN SYLLABICS TTO | 1468 CANADIAN SYLLABICS TTI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146b CANADIAN SYLLABICS KE | 146a CANADIAN SYLLABICS TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146d CANADIAN SYLLABICS KI | 146c CANADIAN SYLLABICS KAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 146f CANADIAN SYLLABICS KO | 146e CANADIAN SYLLABICS KII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1471 CANADIAN SYLLABICS Y-CREE KOO | 1470 CANADIAN SYLLABICS KOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1473 CANADIAN SYLLABICS KAA | 1472 CANADIAN SYLLABICS KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1475 CANADIAN SYLLABICS WEST-CREE KWE | 1474 CANADIAN SYLLABICS KWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1477 CANADIAN SYLLABICS WEST-CREE KWI | 1476 CANADIAN SYLLABICS KWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1479 CANADIAN SYLLABICS WEST-CREE KWII | 1478 CANADIAN SYLLABICS KWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147b CANADIAN SYLLABICS WEST-CREE KWO | 147a CANADIAN SYLLABICS KWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147d CANADIAN SYLLABICS WEST-CREE KWOO | 147c CANADIAN SYLLABICS KWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 147f CANADIAN SYLLABICS WEST-CREE KWA | 147e CANADIAN SYLLABICS KWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1481 CANADIAN SYLLABICS WEST-CREE KWAA | 1480 CANADIAN SYLLABICS KWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1483 CANADIAN SYLLABICS K | 1482 CANADIAN SYLLABICS NASKAPI KWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1485 CANADIAN SYLLABICS SOUTH-SLAVEY KEH | 1484 CANADIAN SYLLABICS KW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1487 CANADIAN SYLLABICS SOUTH-SLAVEY KOH | 1486 CANADIAN SYLLABICS SOUTH-SLAVEY KIH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1489 CANADIAN SYLLABICS CE | 1488 CANADIAN SYLLABICS SOUTH-SLAVEY KAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148b CANADIAN SYLLABICS CI | 148a CANADIAN SYLLABICS CAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148d CANADIAN SYLLABICS CO | 148c CANADIAN SYLLABICS CII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 148f CANADIAN SYLLABICS Y-CREE COO | 148e CANADIAN SYLLABICS COO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1491 CANADIAN SYLLABICS CAA | 1490 CANADIAN SYLLABICS CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1493 CANADIAN SYLLABICS WEST-CREE CWE | 1492 CANADIAN SYLLABICS CWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1495 CANADIAN SYLLABICS WEST-CREE CWI | 1494 CANADIAN SYLLABICS CWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1497 CANADIAN SYLLABICS WEST-CREE CWII | 1496 CANADIAN SYLLABICS CWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1499 CANADIAN SYLLABICS WEST-CREE CWO | 1498 CANADIAN SYLLABICS CWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149b CANADIAN SYLLABICS WEST-CREE CWOO | 149a CANADIAN SYLLABICS CWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149d CANADIAN SYLLABICS WEST-CREE CWA | 149c CANADIAN SYLLABICS CWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 149f CANADIAN SYLLABICS WEST-CREE CWAA | 149e CANADIAN SYLLABICS CWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a1 CANADIAN SYLLABICS C | 14a0 CANADIAN SYLLABICS NASKAPI CWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a3 CANADIAN SYLLABICS ME | 14a2 CANADIAN SYLLABICS SAYISI TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a5 CANADIAN SYLLABICS MI | 14a4 CANADIAN SYLLABICS MAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a7 CANADIAN SYLLABICS MO | 14a6 CANADIAN SYLLABICS MII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14a9 CANADIAN SYLLABICS Y-CREE MOO | 14a8 CANADIAN SYLLABICS MOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ab CANADIAN SYLLABICS MAA | 14aa CANADIAN SYLLABICS MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ad CANADIAN SYLLABICS WEST-CREE MWE | 14ac CANADIAN SYLLABICS MWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14af CANADIAN SYLLABICS WEST-CREE MWI | 14ae CANADIAN SYLLABICS MWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b1 CANADIAN SYLLABICS WEST-CREE MWII | 14b0 CANADIAN SYLLABICS MWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b3 CANADIAN SYLLABICS WEST-CREE MWO | 14b2 CANADIAN SYLLABICS MWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b5 CANADIAN SYLLABICS WEST-CREE MWOO | 14b4 CANADIAN SYLLABICS MWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b7 CANADIAN SYLLABICS WEST-CREE MWA | 14b6 CANADIAN SYLLABICS MWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14b9 CANADIAN SYLLABICS WEST-CREE MWAA | 14b8 CANADIAN SYLLABICS MWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bb CANADIAN SYLLABICS M | 14ba CANADIAN SYLLABICS NASKAPI MWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bd CANADIAN SYLLABICS MH | 14bc CANADIAN SYLLABICS WEST-CREE M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14bf CANADIAN SYLLABICS SAYISI M | 14be CANADIAN SYLLABICS ATHAPASCAN M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c1 CANADIAN SYLLABICS NAAI | 14c0 CANADIAN SYLLABICS NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c3 CANADIAN SYLLABICS NII | 14c2 CANADIAN SYLLABICS NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c5 CANADIAN SYLLABICS NOO | 14c4 CANADIAN SYLLABICS NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c7 CANADIAN SYLLABICS NA | 14c6 CANADIAN SYLLABICS Y-CREE NOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14c9 CANADIAN SYLLABICS NWE | 14c8 CANADIAN SYLLABICS NAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cb CANADIAN SYLLABICS NWA | 14ca CANADIAN SYLLABICS WEST-CREE NWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cd CANADIAN SYLLABICS NWAA | 14cc CANADIAN SYLLABICS WEST-CREE NWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14cf CANADIAN SYLLABICS NASKAPI NWAA | 14ce CANADIAN SYLLABICS WEST-CREE NWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d1 CANADIAN SYLLABICS CARRIER NG | 14d0 CANADIAN SYLLABICS N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d3 CANADIAN SYLLABICS LE | 14d2 CANADIAN SYLLABICS NH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d5 CANADIAN SYLLABICS LI | 14d4 CANADIAN SYLLABICS LAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d7 CANADIAN SYLLABICS LO | 14d6 CANADIAN SYLLABICS LII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14d9 CANADIAN SYLLABICS Y-CREE LOO | 14d8 CANADIAN SYLLABICS LOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14db CANADIAN SYLLABICS LAA | 14da CANADIAN SYLLABICS LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14dd CANADIAN SYLLABICS WEST-CREE LWE | 14dc CANADIAN SYLLABICS LWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14df CANADIAN SYLLABICS WEST-CREE LWI | 14de CANADIAN SYLLABICS LWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e1 CANADIAN SYLLABICS WEST-CREE LWII | 14e0 CANADIAN SYLLABICS LWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e3 CANADIAN SYLLABICS WEST-CREE LWO | 14e2 CANADIAN SYLLABICS LWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e5 CANADIAN SYLLABICS WEST-CREE LWOO | 14e4 CANADIAN SYLLABICS LWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e7 CANADIAN SYLLABICS WEST-CREE LWA | 14e6 CANADIAN SYLLABICS LWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14e9 CANADIAN SYLLABICS WEST-CREE LWAA | 14e8 CANADIAN SYLLABICS LWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14eb CANADIAN SYLLABICS WEST-CREE L | 14ea CANADIAN SYLLABICS L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ed CANADIAN SYLLABICS SE | 14ec CANADIAN SYLLABICS MEDIAL L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ef CANADIAN SYLLABICS SI | 14ee CANADIAN SYLLABICS SAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f1 CANADIAN SYLLABICS SO | 14f0 CANADIAN SYLLABICS SII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f3 CANADIAN SYLLABICS Y-CREE SOO | 14f2 CANADIAN SYLLABICS SOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f5 CANADIAN SYLLABICS SAA | 14f4 CANADIAN SYLLABICS SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f7 CANADIAN SYLLABICS WEST-CREE SWE | 14f6 CANADIAN SYLLABICS SWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14f9 CANADIAN SYLLABICS WEST-CREE SWI | 14f8 CANADIAN SYLLABICS SWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14fb CANADIAN SYLLABICS WEST-CREE SWII | 14fa CANADIAN SYLLABICS SWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14fd CANADIAN SYLLABICS WEST-CREE SWO | 14fc CANADIAN SYLLABICS SWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 14ff CANADIAN SYLLABICS WEST-CREE SWOO | 14fe CANADIAN SYLLABICS SWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1501 CANADIAN SYLLABICS WEST-CREE SWA | 1500 CANADIAN SYLLABICS SWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1503 CANADIAN SYLLABICS WEST-CREE SWAA | 1502 CANADIAN SYLLABICS SWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1505 CANADIAN SYLLABICS S | 1504 CANADIAN SYLLABICS NASKAPI SWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1507 CANADIAN SYLLABICS SW | 1506 CANADIAN SYLLABICS ATHAPASCAN S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1509 CANADIAN SYLLABICS MOOSE-CREE SK | 1508 CANADIAN SYLLABICS BLACKFOOT S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150b CANADIAN SYLLABICS NASKAPI S-W | 150a CANADIAN SYLLABICS NASKAPI SKW */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150d CANADIAN SYLLABICS NASKAPI STWA | 150c CANADIAN SYLLABICS NASKAPI SPWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 150f CANADIAN SYLLABICS NASKAPI SCWA | 150e CANADIAN SYLLABICS NASKAPI SKWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1511 CANADIAN SYLLABICS SHI | 1510 CANADIAN SYLLABICS SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1513 CANADIAN SYLLABICS SHO | 1512 CANADIAN SYLLABICS SHII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1515 CANADIAN SYLLABICS SHA | 1514 CANADIAN SYLLABICS SHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1517 CANADIAN SYLLABICS SHWE | 1516 CANADIAN SYLLABICS SHAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1519 CANADIAN SYLLABICS SHWI | 1518 CANADIAN SYLLABICS WEST-CREE SHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151b CANADIAN SYLLABICS SHWII | 151a CANADIAN SYLLABICS WEST-CREE SHWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151d CANADIAN SYLLABICS SHWO | 151c CANADIAN SYLLABICS WEST-CREE SHWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 151f CANADIAN SYLLABICS SHWOO | 151e CANADIAN SYLLABICS WEST-CREE SHWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1521 CANADIAN SYLLABICS SHWA | 1520 CANADIAN SYLLABICS WEST-CREE SHWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1523 CANADIAN SYLLABICS SHWAA | 1522 CANADIAN SYLLABICS WEST-CREE SHWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1525 CANADIAN SYLLABICS SH | 1524 CANADIAN SYLLABICS WEST-CREE SHWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1527 CANADIAN SYLLABICS YAAI | 1526 CANADIAN SYLLABICS YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1529 CANADIAN SYLLABICS YII | 1528 CANADIAN SYLLABICS YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152b CANADIAN SYLLABICS YOO | 152a CANADIAN SYLLABICS YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152d CANADIAN SYLLABICS YA | 152c CANADIAN SYLLABICS Y-CREE YOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 152f CANADIAN SYLLABICS YWE | 152e CANADIAN SYLLABICS YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1531 CANADIAN SYLLABICS YWI | 1530 CANADIAN SYLLABICS WEST-CREE YWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1533 CANADIAN SYLLABICS YWII | 1532 CANADIAN SYLLABICS WEST-CREE YWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1535 CANADIAN SYLLABICS YWO | 1534 CANADIAN SYLLABICS WEST-CREE YWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1537 CANADIAN SYLLABICS YWOO | 1536 CANADIAN SYLLABICS WEST-CREE YWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1539 CANADIAN SYLLABICS YWA | 1538 CANADIAN SYLLABICS WEST-CREE YWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153b CANADIAN SYLLABICS YWAA | 153a CANADIAN SYLLABICS WEST-CREE YWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153d CANADIAN SYLLABICS NASKAPI YWAA | 153c CANADIAN SYLLABICS WEST-CREE YWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 153f CANADIAN SYLLABICS BIBLE-CREE Y | 153e CANADIAN SYLLABICS Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1541 CANADIAN SYLLABICS SAYISI YI | 1540 CANADIAN SYLLABICS WEST-CREE Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1543 CANADIAN SYLLABICS R-CREE RE | 1542 CANADIAN SYLLABICS RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1545 CANADIAN SYLLABICS RAAI | 1544 CANADIAN SYLLABICS WEST-CREE LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1547 CANADIAN SYLLABICS RII | 1546 CANADIAN SYLLABICS RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1549 CANADIAN SYLLABICS ROO | 1548 CANADIAN SYLLABICS RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154b CANADIAN SYLLABICS RA | 154a CANADIAN SYLLABICS WEST-CREE LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154d CANADIAN SYLLABICS WEST-CREE LA | 154c CANADIAN SYLLABICS RAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 154f CANADIAN SYLLABICS WEST-CREE RWAA | 154e CANADIAN SYLLABICS RWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1551 CANADIAN SYLLABICS WEST-CREE R | 1550 CANADIAN SYLLABICS R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1553 CANADIAN SYLLABICS FE | 1552 CANADIAN SYLLABICS MEDIAL R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1555 CANADIAN SYLLABICS FI | 1554 CANADIAN SYLLABICS FAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1557 CANADIAN SYLLABICS FO | 1556 CANADIAN SYLLABICS FII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1559 CANADIAN SYLLABICS FA | 1558 CANADIAN SYLLABICS FOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155b CANADIAN SYLLABICS FWAA | 155a CANADIAN SYLLABICS FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155d CANADIAN SYLLABICS F | 155c CANADIAN SYLLABICS WEST-CREE FWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 155f CANADIAN SYLLABICS N-CREE THE | 155e CANADIAN SYLLABICS THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1561 CANADIAN SYLLABICS N-CREE THI | 1560 CANADIAN SYLLABICS THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1563 CANADIAN SYLLABICS N-CREE THII | 1562 CANADIAN SYLLABICS THII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1565 CANADIAN SYLLABICS THOO | 1564 CANADIAN SYLLABICS THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1567 CANADIAN SYLLABICS THAA | 1566 CANADIAN SYLLABICS THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1569 CANADIAN SYLLABICS WEST-CREE THWAA | 1568 CANADIAN SYLLABICS THWAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156b CANADIAN SYLLABICS TTHE | 156a CANADIAN SYLLABICS TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156d CANADIAN SYLLABICS TTHO | 156c CANADIAN SYLLABICS TTHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 156f CANADIAN SYLLABICS TTH | 156e CANADIAN SYLLABICS TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1571 CANADIAN SYLLABICS TYI | 1570 CANADIAN SYLLABICS TYE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1573 CANADIAN SYLLABICS TYA | 1572 CANADIAN SYLLABICS TYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1575 CANADIAN SYLLABICS NUNAVIK HI | 1574 CANADIAN SYLLABICS NUNAVIK HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1577 CANADIAN SYLLABICS NUNAVIK HO | 1576 CANADIAN SYLLABICS NUNAVIK HII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1579 CANADIAN SYLLABICS NUNAVIK HA | 1578 CANADIAN SYLLABICS NUNAVIK HOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157b CANADIAN SYLLABICS NUNAVIK H | 157a CANADIAN SYLLABICS NUNAVIK HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157d CANADIAN SYLLABICS HK | 157c CANADIAN SYLLABICS NUNAVUT H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 157f CANADIAN SYLLABICS QI | 157e CANADIAN SYLLABICS QAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1581 CANADIAN SYLLABICS QO | 1580 CANADIAN SYLLABICS QII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1583 CANADIAN SYLLABICS QA | 1582 CANADIAN SYLLABICS QOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1585 CANADIAN SYLLABICS Q | 1584 CANADIAN SYLLABICS QAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1587 CANADIAN SYLLABICS TLHI | 1586 CANADIAN SYLLABICS TLHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1589 CANADIAN SYLLABICS TLHA | 1588 CANADIAN SYLLABICS TLHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158b CANADIAN SYLLABICS WEST-CREE RI | 158a CANADIAN SYLLABICS WEST-CREE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158d CANADIAN SYLLABICS WEST-CREE RA | 158c CANADIAN SYLLABICS WEST-CREE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 158f CANADIAN SYLLABICS NGI | 158e CANADIAN SYLLABICS NGAAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1591 CANADIAN SYLLABICS NGO | 1590 CANADIAN SYLLABICS NGII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1593 CANADIAN SYLLABICS NGA | 1592 CANADIAN SYLLABICS NGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1595 CANADIAN SYLLABICS NG | 1594 CANADIAN SYLLABICS NGAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1597 CANADIAN SYLLABICS SAYISI SHE | 1596 CANADIAN SYLLABICS NNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1599 CANADIAN SYLLABICS SAYISI SHO | 1598 CANADIAN SYLLABICS SAYISI SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159b CANADIAN SYLLABICS WOODS-CREE THE | 159a CANADIAN SYLLABICS SAYISI SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159d CANADIAN SYLLABICS WOODS-CREE THO | 159c CANADIAN SYLLABICS WOODS-CREE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 159f CANADIAN SYLLABICS WOODS-CREE TH | 159e CANADIAN SYLLABICS WOODS-CREE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a1 CANADIAN SYLLABICS LHII | 15a0 CANADIAN SYLLABICS LHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a3 CANADIAN SYLLABICS LHOO | 15a2 CANADIAN SYLLABICS LHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a5 CANADIAN SYLLABICS LHAA | 15a4 CANADIAN SYLLABICS LHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a7 CANADIAN SYLLABICS TH-CREE THE | 15a6 CANADIAN SYLLABICS LH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15a9 CANADIAN SYLLABICS TH-CREE THII | 15a8 CANADIAN SYLLABICS TH-CREE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ab CANADIAN SYLLABICS TH-CREE THOO | 15aa CANADIAN SYLLABICS TH-CREE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ad CANADIAN SYLLABICS TH-CREE THAA | 15ac CANADIAN SYLLABICS TH-CREE THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15af CANADIAN SYLLABICS AIVILIK B | 15ae CANADIAN SYLLABICS TH-CREE TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b1 CANADIAN SYLLABICS BLACKFOOT I | 15b0 CANADIAN SYLLABICS BLACKFOOT E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b3 CANADIAN SYLLABICS BLACKFOOT A | 15b2 CANADIAN SYLLABICS BLACKFOOT O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b5 CANADIAN SYLLABICS BLACKFOOT WI | 15b4 CANADIAN SYLLABICS BLACKFOOT WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b7 CANADIAN SYLLABICS BLACKFOOT WA | 15b6 CANADIAN SYLLABICS BLACKFOOT WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15b9 CANADIAN SYLLABICS BLACKFOOT NI | 15b8 CANADIAN SYLLABICS BLACKFOOT NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bb CANADIAN SYLLABICS BLACKFOOT NA | 15ba CANADIAN SYLLABICS BLACKFOOT NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bd CANADIAN SYLLABICS BLACKFOOT KI | 15bc CANADIAN SYLLABICS BLACKFOOT KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15bf CANADIAN SYLLABICS BLACKFOOT KA | 15be CANADIAN SYLLABICS BLACKFOOT KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c1 CANADIAN SYLLABICS SAYISI HI | 15c0 CANADIAN SYLLABICS SAYISI HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c3 CANADIAN SYLLABICS SAYISI HA | 15c2 CANADIAN SYLLABICS SAYISI HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c5 CANADIAN SYLLABICS CARRIER GHO | 15c4 CANADIAN SYLLABICS CARRIER GHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c7 CANADIAN SYLLABICS CARRIER GHEE | 15c6 CANADIAN SYLLABICS CARRIER GHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15c9 CANADIAN SYLLABICS CARRIER GHA | 15c8 CANADIAN SYLLABICS CARRIER GHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cb CANADIAN SYLLABICS CARRIER RO | 15ca CANADIAN SYLLABICS CARRIER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cd CANADIAN SYLLABICS CARRIER REE | 15cc CANADIAN SYLLABICS CARRIER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15cf CANADIAN SYLLABICS CARRIER RA | 15ce CANADIAN SYLLABICS CARRIER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d1 CANADIAN SYLLABICS CARRIER WO | 15d0 CANADIAN SYLLABICS CARRIER WU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d3 CANADIAN SYLLABICS CARRIER WEE | 15d2 CANADIAN SYLLABICS CARRIER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d5 CANADIAN SYLLABICS CARRIER WA | 15d4 CANADIAN SYLLABICS CARRIER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d7 CANADIAN SYLLABICS CARRIER HWO | 15d6 CANADIAN SYLLABICS CARRIER HWU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15d9 CANADIAN SYLLABICS CARRIER HWEE | 15d8 CANADIAN SYLLABICS CARRIER HWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15db CANADIAN SYLLABICS CARRIER HWA | 15da CANADIAN SYLLABICS CARRIER HWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15dd CANADIAN SYLLABICS CARRIER THO | 15dc CANADIAN SYLLABICS CARRIER THU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15df CANADIAN SYLLABICS CARRIER THEE | 15de CANADIAN SYLLABICS CARRIER THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e1 CANADIAN SYLLABICS CARRIER THA | 15e0 CANADIAN SYLLABICS CARRIER THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e3 CANADIAN SYLLABICS CARRIER TTO | 15e2 CANADIAN SYLLABICS CARRIER TTU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e5 CANADIAN SYLLABICS CARRIER TTEE | 15e4 CANADIAN SYLLABICS CARRIER TTE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e7 CANADIAN SYLLABICS CARRIER TTA | 15e6 CANADIAN SYLLABICS CARRIER TTI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15e9 CANADIAN SYLLABICS CARRIER PO | 15e8 CANADIAN SYLLABICS CARRIER PU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15eb CANADIAN SYLLABICS CARRIER PEE | 15ea CANADIAN SYLLABICS CARRIER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ed CANADIAN SYLLABICS CARRIER PA | 15ec CANADIAN SYLLABICS CARRIER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ef CANADIAN SYLLABICS CARRIER GU | 15ee CANADIAN SYLLABICS CARRIER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f1 CANADIAN SYLLABICS CARRIER GE | 15f0 CANADIAN SYLLABICS CARRIER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f3 CANADIAN SYLLABICS CARRIER GI | 15f2 CANADIAN SYLLABICS CARRIER GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f5 CANADIAN SYLLABICS CARRIER KHU | 15f4 CANADIAN SYLLABICS CARRIER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f7 CANADIAN SYLLABICS CARRIER KHE | 15f6 CANADIAN SYLLABICS CARRIER KHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15f9 CANADIAN SYLLABICS CARRIER KHI | 15f8 CANADIAN SYLLABICS CARRIER KHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15fb CANADIAN SYLLABICS CARRIER KKU | 15fa CANADIAN SYLLABICS CARRIER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15fd CANADIAN SYLLABICS CARRIER KKE | 15fc CANADIAN SYLLABICS CARRIER KKO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 15ff CANADIAN SYLLABICS CARRIER KKI | 15fe CANADIAN SYLLABICS CARRIER KKEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1601 CANADIAN SYLLABICS CARRIER KK | 1600 CANADIAN SYLLABICS CARRIER KKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1603 CANADIAN SYLLABICS CARRIER NO | 1602 CANADIAN SYLLABICS CARRIER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1605 CANADIAN SYLLABICS CARRIER NEE | 1604 CANADIAN SYLLABICS CARRIER NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1607 CANADIAN SYLLABICS CARRIER NA | 1606 CANADIAN SYLLABICS CARRIER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1609 CANADIAN SYLLABICS CARRIER MO | 1608 CANADIAN SYLLABICS CARRIER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160b CANADIAN SYLLABICS CARRIER MEE | 160a CANADIAN SYLLABICS CARRIER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160d CANADIAN SYLLABICS CARRIER MA | 160c CANADIAN SYLLABICS CARRIER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 160f CANADIAN SYLLABICS CARRIER YO | 160e CANADIAN SYLLABICS CARRIER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1611 CANADIAN SYLLABICS CARRIER YEE | 1610 CANADIAN SYLLABICS CARRIER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1613 CANADIAN SYLLABICS CARRIER YA | 1612 CANADIAN SYLLABICS CARRIER YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1615 CANADIAN SYLLABICS SAYISI JU | 1614 CANADIAN SYLLABICS CARRIER JU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1617 CANADIAN SYLLABICS CARRIER JE | 1616 CANADIAN SYLLABICS CARRIER JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1619 CANADIAN SYLLABICS CARRIER JI | 1618 CANADIAN SYLLABICS CARRIER JEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161b CANADIAN SYLLABICS CARRIER JA | 161a CANADIAN SYLLABICS SAYISI JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161d CANADIAN SYLLABICS CARRIER JJO | 161c CANADIAN SYLLABICS CARRIER JJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 161f CANADIAN SYLLABICS CARRIER JJEE | 161e CANADIAN SYLLABICS CARRIER JJE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1621 CANADIAN SYLLABICS CARRIER JJA | 1620 CANADIAN SYLLABICS CARRIER JJI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1623 CANADIAN SYLLABICS CARRIER LO | 1622 CANADIAN SYLLABICS CARRIER LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1625 CANADIAN SYLLABICS CARRIER LEE | 1624 CANADIAN SYLLABICS CARRIER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1627 CANADIAN SYLLABICS CARRIER LA | 1626 CANADIAN SYLLABICS CARRIER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1629 CANADIAN SYLLABICS CARRIER DLO | 1628 CANADIAN SYLLABICS CARRIER DLU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162b CANADIAN SYLLABICS CARRIER DLEE | 162a CANADIAN SYLLABICS CARRIER DLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162d CANADIAN SYLLABICS CARRIER DLA | 162c CANADIAN SYLLABICS CARRIER DLI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 162f CANADIAN SYLLABICS CARRIER LHO | 162e CANADIAN SYLLABICS CARRIER LHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1631 CANADIAN SYLLABICS CARRIER LHEE | 1630 CANADIAN SYLLABICS CARRIER LHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1633 CANADIAN SYLLABICS CARRIER LHA | 1632 CANADIAN SYLLABICS CARRIER LHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1635 CANADIAN SYLLABICS CARRIER TLHO | 1634 CANADIAN SYLLABICS CARRIER TLHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1637 CANADIAN SYLLABICS CARRIER TLHEE | 1636 CANADIAN SYLLABICS CARRIER TLHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1639 CANADIAN SYLLABICS CARRIER TLHA | 1638 CANADIAN SYLLABICS CARRIER TLHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163b CANADIAN SYLLABICS CARRIER TLO | 163a CANADIAN SYLLABICS CARRIER TLU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163d CANADIAN SYLLABICS CARRIER TLEE | 163c CANADIAN SYLLABICS CARRIER TLE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 163f CANADIAN SYLLABICS CARRIER TLA | 163e CANADIAN SYLLABICS CARRIER TLI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1641 CANADIAN SYLLABICS CARRIER ZO | 1640 CANADIAN SYLLABICS CARRIER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1643 CANADIAN SYLLABICS CARRIER ZEE | 1642 CANADIAN SYLLABICS CARRIER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1645 CANADIAN SYLLABICS CARRIER ZA | 1644 CANADIAN SYLLABICS CARRIER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1647 CANADIAN SYLLABICS CARRIER INITIAL Z | 1646 CANADIAN SYLLABICS CARRIER Z */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1649 CANADIAN SYLLABICS CARRIER DZO | 1648 CANADIAN SYLLABICS CARRIER DZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164b CANADIAN SYLLABICS CARRIER DZEE | 164a CANADIAN SYLLABICS CARRIER DZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164d CANADIAN SYLLABICS CARRIER DZA | 164c CANADIAN SYLLABICS CARRIER DZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 164f CANADIAN SYLLABICS CARRIER SO | 164e CANADIAN SYLLABICS CARRIER SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1651 CANADIAN SYLLABICS CARRIER SEE | 1650 CANADIAN SYLLABICS CARRIER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1653 CANADIAN SYLLABICS CARRIER SA | 1652 CANADIAN SYLLABICS CARRIER SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1655 CANADIAN SYLLABICS CARRIER SHO | 1654 CANADIAN SYLLABICS CARRIER SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1657 CANADIAN SYLLABICS CARRIER SHEE | 1656 CANADIAN SYLLABICS CARRIER SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1659 CANADIAN SYLLABICS CARRIER SHA | 1658 CANADIAN SYLLABICS CARRIER SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165b CANADIAN SYLLABICS CARRIER TSU | 165a CANADIAN SYLLABICS CARRIER SH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165d CANADIAN SYLLABICS CARRIER TSE | 165c CANADIAN SYLLABICS CARRIER TSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 165f CANADIAN SYLLABICS CARRIER TSI | 165e CANADIAN SYLLABICS CARRIER TSEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1661 CANADIAN SYLLABICS CARRIER CHU | 1660 CANADIAN SYLLABICS CARRIER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1663 CANADIAN SYLLABICS CARRIER CHE | 1662 CANADIAN SYLLABICS CARRIER CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1665 CANADIAN SYLLABICS CARRIER CHI | 1664 CANADIAN SYLLABICS CARRIER CHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1667 CANADIAN SYLLABICS CARRIER TTSU | 1666 CANADIAN SYLLABICS CARRIER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1669 CANADIAN SYLLABICS CARRIER TTSE | 1668 CANADIAN SYLLABICS CARRIER TTSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 166b CANADIAN SYLLABICS CARRIER TTSI | 166a CANADIAN SYLLABICS CARRIER TTSEE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 166d CANADIAN SYLLABICS CHI SIGN | 166c CANADIAN SYLLABICS CARRIER TTSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 166f CANADIAN SYLLABICS QAI | 166e CANADIAN SYLLABICS FULL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1671 CANADIAN SYLLABICS NNGI | 1670 CANADIAN SYLLABICS NGAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1673 CANADIAN SYLLABICS NNGO | 1672 CANADIAN SYLLABICS NNGII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1675 CANADIAN SYLLABICS NNGA | 1674 CANADIAN SYLLABICS NNGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1677 CANADIAN SYLLABICS WOODS-CREE THWEE | 1676 CANADIAN SYLLABICS NNGAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1679 CANADIAN SYLLABICS WOODS-CREE THWII | 1678 CANADIAN SYLLABICS WOODS-CREE THWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167b CANADIAN SYLLABICS WOODS-CREE THWOO | 167a CANADIAN SYLLABICS WOODS-CREE THWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167d CANADIAN SYLLABICS WOODS-CREE THWAA | 167c CANADIAN SYLLABICS WOODS-CREE THWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 167f CANADIAN SYLLABICS BLACKFOOT W | 167e CANADIAN SYLLABICS WOODS-CREE FINAL TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_SPACE, /* 1681 OGHAM LETTER BEITH | 1680 OGHAM SPACE MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1683 OGHAM LETTER FEARN | 1682 OGHAM LETTER LUIS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1685 OGHAM LETTER NION | 1684 OGHAM LETTER SAIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1687 OGHAM LETTER DAIR | 1686 OGHAM LETTER UATH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1689 OGHAM LETTER COLL | 1688 OGHAM LETTER TINNE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168b OGHAM LETTER MUIN | 168a OGHAM LETTER CEIRT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168d OGHAM LETTER NGEADAL | 168c OGHAM LETTER GORT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 168f OGHAM LETTER RUIS | 168e OGHAM LETTER STRAIF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1691 OGHAM LETTER ONN | 1690 OGHAM LETTER AILM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1693 OGHAM LETTER EADHADH | 1692 OGHAM LETTER UR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1695 OGHAM LETTER EABHADH | 1694 OGHAM LETTER IODHADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1697 OGHAM LETTER UILLEANN | 1696 OGHAM LETTER OR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1699 OGHAM LETTER EAMHANCHOLL | 1698 OGHAM LETTER IFIN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 169b OGHAM FEATHER MARK | 169a OGHAM LETTER PEITH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 169d (null) | 169c OGHAM REVERSED FEATHER MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 169f (null) | 169e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a1 RUNIC LETTER V | 16a0 RUNIC LETTER FEHU FEOH FE F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a3 RUNIC LETTER YR | 16a2 RUNIC LETTER URUZ UR U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a5 RUNIC LETTER W | 16a4 RUNIC LETTER Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a7 RUNIC LETTER ETH | 16a6 RUNIC LETTER THURISAZ THURS THORN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16a9 RUNIC LETTER OS O | 16a8 RUNIC LETTER ANSUZ A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16ab RUNIC LETTER AESC | 16aa RUNIC LETTER AC A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16ad RUNIC LETTER SHORT-TWIG-OSS O | 16ac RUNIC LETTER LONG-BRANCH-OSS O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16af RUNIC LETTER OE | 16ae RUNIC LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b1 RUNIC LETTER RAIDO RAD REID R | 16b0 RUNIC LETTER ON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b3 RUNIC LETTER CEN | 16b2 RUNIC LETTER KAUNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b5 RUNIC LETTER G | 16b4 RUNIC LETTER KAUN K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b7 RUNIC LETTER GEBO GYFU G | 16b6 RUNIC LETTER ENG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16b9 RUNIC LETTER WUNJO WYNN W | 16b8 RUNIC LETTER GAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bb RUNIC LETTER HAEGL H | 16ba RUNIC LETTER HAGLAZ H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bd RUNIC LETTER SHORT-TWIG-HAGALL H | 16bc RUNIC LETTER LONG-BRANCH-HAGALL H */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16bf RUNIC LETTER SHORT-TWIG-NAUD N | 16be RUNIC LETTER NAUDIZ NYD NAUD N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c1 RUNIC LETTER ISAZ IS ISS I | 16c0 RUNIC LETTER DOTTED-N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c3 RUNIC LETTER JERAN J | 16c2 RUNIC LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c5 RUNIC LETTER LONG-BRANCH-AR AE | 16c4 RUNIC LETTER GER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c7 RUNIC LETTER IWAZ EOH | 16c6 RUNIC LETTER SHORT-TWIG-AR A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16c9 RUNIC LETTER ALGIZ EOLHX | 16c8 RUNIC LETTER PERTHO PEORTH P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cb RUNIC LETTER SIGEL LONG-BRANCH-SOL S | 16ca RUNIC LETTER SOWILO S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cd RUNIC LETTER C | 16cc RUNIC LETTER SHORT-TWIG-SOL S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16cf RUNIC LETTER TIWAZ TIR TYR T | 16ce RUNIC LETTER Z */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d1 RUNIC LETTER D | 16d0 RUNIC LETTER SHORT-TWIG-TYR T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d3 RUNIC LETTER SHORT-TWIG-BJARKAN B | 16d2 RUNIC LETTER BERKANAN BEORC BJARKAN B */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d5 RUNIC LETTER OPEN-P | 16d4 RUNIC LETTER DOTTED-P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d7 RUNIC LETTER MANNAZ MAN M | 16d6 RUNIC LETTER EHWAZ EH E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16d9 RUNIC LETTER SHORT-TWIG-MADR M | 16d8 RUNIC LETTER LONG-BRANCH-MADR M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16db RUNIC LETTER DOTTED-L | 16da RUNIC LETTER LAUKAZ LAGU LOGR L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16dd RUNIC LETTER ING | 16dc RUNIC LETTER INGWAZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16df RUNIC LETTER OTHALAN ETHEL O | 16de RUNIC LETTER DAGAZ DAEG D */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e1 RUNIC LETTER IOR | 16e0 RUNIC LETTER EAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e3 RUNIC LETTER CALC | 16e2 RUNIC LETTER CWEORTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e5 RUNIC LETTER STAN | 16e4 RUNIC LETTER CEALC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e7 RUNIC LETTER SHORT-TWIG-YR | 16e6 RUNIC LETTER LONG-BRANCH-YR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 16e9 RUNIC LETTER Q | 16e8 RUNIC LETTER ICELANDIC-YR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 16eb RUNIC SINGLE PUNCTUATION | 16ea RUNIC LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 16ed RUNIC CROSS PUNCTUATION | 16ec RUNIC MULTIPLE PUNCTUATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 16ef RUNIC TVIMADUR SYMBOL | 16ee RUNIC ARLAUG SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 16f1 (null) | 16f0 RUNIC BELGTHOR SYMBOL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f3 (null) | 16f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f5 (null) | 16f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f7 (null) | 16f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16f9 (null) | 16f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16fb (null) | 16fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16fd (null) | 16fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 16ff (null) | 16fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1701 TAGALOG LETTER I | 1700 TAGALOG LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1703 TAGALOG LETTER KA | 1702 TAGALOG LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1705 TAGALOG LETTER NGA | 1704 TAGALOG LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1707 TAGALOG LETTER DA | 1706 TAGALOG LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1709 TAGALOG LETTER PA | 1708 TAGALOG LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 170b TAGALOG LETTER MA | 170a TAGALOG LETTER BA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 170d (null) | 170c TAGALOG LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 170f TAGALOG LETTER WA | 170e TAGALOG LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1711 TAGALOG LETTER HA | 1710 TAGALOG LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1713 TAGALOG VOWEL SIGN U | 1712 TAGALOG VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1715 (null) | 1714 TAGALOG SIGN VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1717 (null) | 1716 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1719 (null) | 1718 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171b (null) | 171a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171d (null) | 171c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 171f (null) | 171e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1721 HANUNOO LETTER I | 1720 HANUNOO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1723 HANUNOO LETTER KA | 1722 HANUNOO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1725 HANUNOO LETTER NGA | 1724 HANUNOO LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1727 HANUNOO LETTER DA | 1726 HANUNOO LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1729 HANUNOO LETTER PA | 1728 HANUNOO LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172b HANUNOO LETTER MA | 172a HANUNOO LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172d HANUNOO LETTER RA | 172c HANUNOO LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 172f HANUNOO LETTER WA | 172e HANUNOO LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1731 HANUNOO LETTER HA | 1730 HANUNOO LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1733 HANUNOO VOWEL SIGN U | 1732 HANUNOO VOWEL SIGN I */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 1735 PHILIPPINE SINGLE PUNCTUATION | 1734 HANUNOO SIGN PAMUDPOD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 1737 (null) | 1736 PHILIPPINE DOUBLE PUNCTUATION */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1739 (null) | 1738 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173b (null) | 173a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173d (null) | 173c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 173f (null) | 173e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1741 BUHID LETTER I | 1740 BUHID LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1743 BUHID LETTER KA | 1742 BUHID LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1745 BUHID LETTER NGA | 1744 BUHID LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1747 BUHID LETTER DA | 1746 BUHID LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1749 BUHID LETTER PA | 1748 BUHID LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174b BUHID LETTER MA | 174a BUHID LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174d BUHID LETTER RA | 174c BUHID LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 174f BUHID LETTER WA | 174e BUHID LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1751 BUHID LETTER HA | 1750 BUHID LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1753 BUHID VOWEL SIGN U | 1752 BUHID VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1755 (null) | 1754 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1757 (null) | 1756 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1759 (null) | 1758 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175b (null) | 175a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175d (null) | 175c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 175f (null) | 175e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1761 TAGBANWA LETTER I | 1760 TAGBANWA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1763 TAGBANWA LETTER KA | 1762 TAGBANWA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1765 TAGBANWA LETTER NGA | 1764 TAGBANWA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1767 TAGBANWA LETTER DA | 1766 TAGBANWA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1769 TAGBANWA LETTER PA | 1768 TAGBANWA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 176b TAGBANWA LETTER MA | 176a TAGBANWA LETTER BA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 176d (null) | 176c TAGBANWA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 176f TAGBANWA LETTER WA | 176e TAGBANWA LETTER LA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1771 (null) | 1770 TAGBANWA LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1773 TAGBANWA VOWEL SIGN U | 1772 TAGBANWA VOWEL SIGN I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1775 (null) | 1774 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1777 (null) | 1776 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1779 (null) | 1778 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177b (null) | 177a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177d (null) | 177c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 177f (null) | 177e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1781 KHMER LETTER KHA | 1780 KHMER LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1783 KHMER LETTER KHO | 1782 KHMER LETTER KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1785 KHMER LETTER CA | 1784 KHMER LETTER NGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1787 KHMER LETTER CO | 1786 KHMER LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1789 KHMER LETTER NYO | 1788 KHMER LETTER CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178b KHMER LETTER TTHA | 178a KHMER LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178d KHMER LETTER TTHO | 178c KHMER LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 178f KHMER LETTER TA | 178e KHMER LETTER NNO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1791 KHMER LETTER TO | 1790 KHMER LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1793 KHMER LETTER NO | 1792 KHMER LETTER THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1795 KHMER LETTER PHA | 1794 KHMER LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1797 KHMER LETTER PHO | 1796 KHMER LETTER PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1799 KHMER LETTER YO | 1798 KHMER LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179b KHMER LETTER LO | 179a KHMER LETTER RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179d KHMER LETTER SHA | 179c KHMER LETTER VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 179f KHMER LETTER SA | 179e KHMER LETTER SSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a1 KHMER LETTER LA | 17a0 KHMER LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a3 KHMER INDEPENDENT VOWEL QAQ | 17a2 KHMER LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a5 KHMER INDEPENDENT VOWEL QI | 17a4 KHMER INDEPENDENT VOWEL QAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a7 KHMER INDEPENDENT VOWEL QU | 17a6 KHMER INDEPENDENT VOWEL QII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17a9 KHMER INDEPENDENT VOWEL QUU | 17a8 KHMER INDEPENDENT VOWEL QUK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17ab KHMER INDEPENDENT VOWEL RY | 17aa KHMER INDEPENDENT VOWEL QUUV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17ad KHMER INDEPENDENT VOWEL LY | 17ac KHMER INDEPENDENT VOWEL RYY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17af KHMER INDEPENDENT VOWEL QE | 17ae KHMER INDEPENDENT VOWEL LYY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17b1 KHMER INDEPENDENT VOWEL QOO TYPE ONE | 17b0 KHMER INDEPENDENT VOWEL QAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 17b3 KHMER INDEPENDENT VOWEL QAU | 17b2 KHMER INDEPENDENT VOWEL QOO TYPE TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b5 KHMER VOWEL INHERENT AA | 17b4 KHMER VOWEL INHERENT AQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b7 KHMER VOWEL SIGN I | 17b6 KHMER VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17b9 KHMER VOWEL SIGN Y | 17b8 KHMER VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bb KHMER VOWEL SIGN U | 17ba KHMER VOWEL SIGN YY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bd KHMER VOWEL SIGN UA | 17bc KHMER VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17bf KHMER VOWEL SIGN YA | 17be KHMER VOWEL SIGN OE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c1 KHMER VOWEL SIGN E | 17c0 KHMER VOWEL SIGN IE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c3 KHMER VOWEL SIGN AI | 17c2 KHMER VOWEL SIGN AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c5 KHMER VOWEL SIGN AU | 17c4 KHMER VOWEL SIGN OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c7 KHMER SIGN REAHMUK | 17c6 KHMER SIGN NIKAHIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17c9 KHMER SIGN MUUSIKATOAN | 17c8 KHMER SIGN YUUKALEAPINTU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cb KHMER SIGN BANTOC | 17ca KHMER SIGN TRIISAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cd KHMER SIGN TOANDAKHIAT | 17cc KHMER SIGN ROBAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17cf KHMER SIGN AHSDA | 17ce KHMER SIGN KAKABAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17d1 KHMER SIGN VIRIAM | 17d0 KHMER SIGN SAMYOK SANNYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17d3 KHMER SIGN BATHAMASAT | 17d2 KHMER SIGN COENG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 17d5 KHMER SIGN BARIYOOSAN | 17d4 KHMER SIGN KHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 17d7 KHMER SIGN LEK TOO | 17d6 KHMER SIGN CAMNUC PII KUUH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 17d9 KHMER SIGN PHNAEK MUAN | 17d8 KHMER SIGN BEYYAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 17db KHMER CURRENCY SYMBOL RIEL | 17da KHMER SIGN KOOMUUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 17dd KHMER SIGN ATTHACAN | 17dc KHMER SIGN AVAKRAHASANYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17df (null) | 17de (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e1 KHMER DIGIT ONE | 17e0 KHMER DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e3 KHMER DIGIT THREE | 17e2 KHMER DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e5 KHMER DIGIT FIVE | 17e4 KHMER DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e7 KHMER DIGIT SEVEN | 17e6 KHMER DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 17e9 KHMER DIGIT NINE | 17e8 KHMER DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17eb (null) | 17ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ed (null) | 17ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ef (null) | 17ee (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f1 KHMER SYMBOL LEK ATTAK MUOY | 17f0 KHMER SYMBOL LEK ATTAK SON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f3 KHMER SYMBOL LEK ATTAK BEI | 17f2 KHMER SYMBOL LEK ATTAK PII */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f5 KHMER SYMBOL LEK ATTAK PRAM | 17f4 KHMER SYMBOL LEK ATTAK BUON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f7 KHMER SYMBOL LEK ATTAK PRAM-PII | 17f6 KHMER SYMBOL LEK ATTAK PRAM-MUOY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 17f9 KHMER SYMBOL LEK ATTAK PRAM-BUON | 17f8 KHMER SYMBOL LEK ATTAK PRAM-BEI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17fb (null) | 17fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17fd (null) | 17fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 17ff (null) | 17fe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1801 MONGOLIAN ELLIPSIS | 1800 MONGOLIAN BIRGA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1803 MONGOLIAN FULL STOP | 1802 MONGOLIAN COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1805 MONGOLIAN FOUR DOTS | 1804 MONGOLIAN COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1807 MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER | 1806 MONGOLIAN TODO SOFT HYPHEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1809 MONGOLIAN MANCHU FULL STOP | 1808 MONGOLIAN MANCHU COMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 180b MONGOLIAN FREE VARIATION SELECTOR ONE | 180a MONGOLIAN NIRUGU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 180d MONGOLIAN FREE VARIATION SELECTOR THREE | 180c MONGOLIAN FREE VARIATION SELECTOR TWO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_SPACE, /* 180f (null) | 180e MONGOLIAN VOWEL SEPARATOR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1811 MONGOLIAN DIGIT ONE | 1810 MONGOLIAN DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1813 MONGOLIAN DIGIT THREE | 1812 MONGOLIAN DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1815 MONGOLIAN DIGIT FIVE | 1814 MONGOLIAN DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1817 MONGOLIAN DIGIT SEVEN | 1816 MONGOLIAN DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1819 MONGOLIAN DIGIT NINE | 1818 MONGOLIAN DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181b (null) | 181a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181d (null) | 181c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 181f (null) | 181e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1821 MONGOLIAN LETTER E | 1820 MONGOLIAN LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1823 MONGOLIAN LETTER O | 1822 MONGOLIAN LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1825 MONGOLIAN LETTER OE | 1824 MONGOLIAN LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1827 MONGOLIAN LETTER EE | 1826 MONGOLIAN LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1829 MONGOLIAN LETTER ANG | 1828 MONGOLIAN LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182b MONGOLIAN LETTER PA | 182a MONGOLIAN LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182d MONGOLIAN LETTER GA | 182c MONGOLIAN LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 182f MONGOLIAN LETTER LA | 182e MONGOLIAN LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1831 MONGOLIAN LETTER SHA | 1830 MONGOLIAN LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1833 MONGOLIAN LETTER DA | 1832 MONGOLIAN LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1835 MONGOLIAN LETTER JA | 1834 MONGOLIAN LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1837 MONGOLIAN LETTER RA | 1836 MONGOLIAN LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1839 MONGOLIAN LETTER FA | 1838 MONGOLIAN LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183b MONGOLIAN LETTER KHA | 183a MONGOLIAN LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183d MONGOLIAN LETTER ZA | 183c MONGOLIAN LETTER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 183f MONGOLIAN LETTER ZRA | 183e MONGOLIAN LETTER HAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1841 MONGOLIAN LETTER ZHI | 1840 MONGOLIAN LETTER LHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1843 MONGOLIAN LETTER TODO LONG VOWEL SIGN | 1842 MONGOLIAN LETTER CHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1845 MONGOLIAN LETTER TODO I | 1844 MONGOLIAN LETTER TODO E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1847 MONGOLIAN LETTER TODO U | 1846 MONGOLIAN LETTER TODO O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1849 MONGOLIAN LETTER TODO UE | 1848 MONGOLIAN LETTER TODO OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184b MONGOLIAN LETTER TODO BA | 184a MONGOLIAN LETTER TODO ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184d MONGOLIAN LETTER TODO QA | 184c MONGOLIAN LETTER TODO PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 184f MONGOLIAN LETTER TODO MA | 184e MONGOLIAN LETTER TODO GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1851 MONGOLIAN LETTER TODO DA | 1850 MONGOLIAN LETTER TODO TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1853 MONGOLIAN LETTER TODO JA | 1852 MONGOLIAN LETTER TODO CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1855 MONGOLIAN LETTER TODO YA | 1854 MONGOLIAN LETTER TODO TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1857 MONGOLIAN LETTER TODO KA | 1856 MONGOLIAN LETTER TODO WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1859 MONGOLIAN LETTER TODO HAA | 1858 MONGOLIAN LETTER TODO GAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185b MONGOLIAN LETTER TODO NIA | 185a MONGOLIAN LETTER TODO JIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185d MONGOLIAN LETTER SIBE E | 185c MONGOLIAN LETTER TODO DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 185f MONGOLIAN LETTER SIBE IY | 185e MONGOLIAN LETTER SIBE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1861 MONGOLIAN LETTER SIBE U | 1860 MONGOLIAN LETTER SIBE UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1863 MONGOLIAN LETTER SIBE KA | 1862 MONGOLIAN LETTER SIBE ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1865 MONGOLIAN LETTER SIBE HA | 1864 MONGOLIAN LETTER SIBE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1867 MONGOLIAN LETTER SIBE SHA | 1866 MONGOLIAN LETTER SIBE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1869 MONGOLIAN LETTER SIBE DA | 1868 MONGOLIAN LETTER SIBE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186b MONGOLIAN LETTER SIBE FA | 186a MONGOLIAN LETTER SIBE JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186d MONGOLIAN LETTER SIBE HAA | 186c MONGOLIAN LETTER SIBE GAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 186f MONGOLIAN LETTER SIBE ZA | 186e MONGOLIAN LETTER SIBE TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1871 MONGOLIAN LETTER SIBE CHA | 1870 MONGOLIAN LETTER SIBE RAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1873 MONGOLIAN LETTER MANCHU I | 1872 MONGOLIAN LETTER SIBE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1875 MONGOLIAN LETTER MANCHU RA | 1874 MONGOLIAN LETTER MANCHU KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1877 MONGOLIAN LETTER MANCHU ZHA | 1876 MONGOLIAN LETTER MANCHU FA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1879 (null) | 1878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187b (null) | 187a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187d (null) | 187c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 187f (null) | 187e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1881 MONGOLIAN LETTER ALI GALI VISARGA ONE | 1880 MONGOLIAN LETTER ALI GALI ANUSVARA ONE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1883 MONGOLIAN LETTER ALI GALI UBADAMA | 1882 MONGOLIAN LETTER ALI GALI DAMARU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1885 MONGOLIAN LETTER ALI GALI BALUDA | 1884 MONGOLIAN LETTER ALI GALI INVERTED UBAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1887 MONGOLIAN LETTER ALI GALI A | 1886 MONGOLIAN LETTER ALI GALI THREE BALUDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1889 MONGOLIAN LETTER ALI GALI KA | 1888 MONGOLIAN LETTER ALI GALI I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188b MONGOLIAN LETTER ALI GALI CA | 188a MONGOLIAN LETTER ALI GALI NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188d MONGOLIAN LETTER ALI GALI TTHA | 188c MONGOLIAN LETTER ALI GALI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 188f MONGOLIAN LETTER ALI GALI NNA | 188e MONGOLIAN LETTER ALI GALI DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1891 MONGOLIAN LETTER ALI GALI DA | 1890 MONGOLIAN LETTER ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1893 MONGOLIAN LETTER ALI GALI PHA | 1892 MONGOLIAN LETTER ALI GALI PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1895 MONGOLIAN LETTER ALI GALI ZHA | 1894 MONGOLIAN LETTER ALI GALI SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1897 MONGOLIAN LETTER ALI GALI AH | 1896 MONGOLIAN LETTER ALI GALI ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1899 MONGOLIAN LETTER TODO ALI GALI ZHA | 1898 MONGOLIAN LETTER TODO ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189b MONGOLIAN LETTER MANCHU ALI GALI NGA | 189a MONGOLIAN LETTER MANCHU ALI GALI GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189d MONGOLIAN LETTER MANCHU ALI GALI JHA | 189c MONGOLIAN LETTER MANCHU ALI GALI CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 189f MONGOLIAN LETTER MANCHU ALI GALI DDHA | 189e MONGOLIAN LETTER MANCHU ALI GALI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a1 MONGOLIAN LETTER MANCHU ALI GALI DHA | 18a0 MONGOLIAN LETTER MANCHU ALI GALI TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a3 MONGOLIAN LETTER MANCHU ALI GALI CYA | 18a2 MONGOLIAN LETTER MANCHU ALI GALI SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a5 MONGOLIAN LETTER MANCHU ALI GALI ZA | 18a4 MONGOLIAN LETTER MANCHU ALI GALI ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18a7 MONGOLIAN LETTER ALI GALI HALF YA | 18a6 MONGOLIAN LETTER ALI GALI HALF U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 18a9 MONGOLIAN LETTER ALI GALI DAGALGA | 18a8 MONGOLIAN LETTER MANCHU ALI GALI BHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 18ab (null) | 18aa MONGOLIAN LETTER MANCHU ALI GALI LHA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18ad (null) | 18ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18af (null) | 18ae (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b1 CANADIAN SYLLABICS AY | 18b0 CANADIAN SYLLABICS OY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b3 CANADIAN SYLLABICS WAY | 18b2 CANADIAN SYLLABICS AAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b5 CANADIAN SYLLABICS PAY | 18b4 CANADIAN SYLLABICS POY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b7 CANADIAN SYLLABICS TAY | 18b6 CANADIAN SYLLABICS PWOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18b9 CANADIAN SYLLABICS KWAY | 18b8 CANADIAN SYLLABICS KAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bb CANADIAN SYLLABICS NOY | 18ba CANADIAN SYLLABICS MAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bd CANADIAN SYLLABICS LAY | 18bc CANADIAN SYLLABICS NAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18bf CANADIAN SYLLABICS SAY | 18be CANADIAN SYLLABICS SOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c1 CANADIAN SYLLABICS SHAY | 18c0 CANADIAN SYLLABICS SHOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c3 CANADIAN SYLLABICS YOY | 18c2 CANADIAN SYLLABICS SHWOY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c5 CANADIAN SYLLABICS RAY | 18c4 CANADIAN SYLLABICS YAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c7 CANADIAN SYLLABICS OJIBWAY NWI | 18c6 CANADIAN SYLLABICS NWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18c9 CANADIAN SYLLABICS OJIBWAY NWII | 18c8 CANADIAN SYLLABICS NWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cb CANADIAN SYLLABICS OJIBWAY NWO | 18ca CANADIAN SYLLABICS NWO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cd CANADIAN SYLLABICS OJIBWAY NWOO | 18cc CANADIAN SYLLABICS NWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18cf CANADIAN SYLLABICS RWI | 18ce CANADIAN SYLLABICS RWEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d1 CANADIAN SYLLABICS RWO | 18d0 CANADIAN SYLLABICS RWII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d3 CANADIAN SYLLABICS RWA | 18d2 CANADIAN SYLLABICS RWOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d5 CANADIAN SYLLABICS OJIBWAY T | 18d4 CANADIAN SYLLABICS OJIBWAY P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d7 CANADIAN SYLLABICS OJIBWAY C | 18d6 CANADIAN SYLLABICS OJIBWAY K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18d9 CANADIAN SYLLABICS OJIBWAY N | 18d8 CANADIAN SYLLABICS OJIBWAY M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18db CANADIAN SYLLABICS OJIBWAY SH | 18da CANADIAN SYLLABICS OJIBWAY S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18dd CANADIAN SYLLABICS WESTERN W | 18dc CANADIAN SYLLABICS EASTERN W */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18df CANADIAN SYLLABICS FINAL RAISED DOT | 18de CANADIAN SYLLABICS FINAL SMALL RING */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e1 CANADIAN SYLLABICS WEST-CREE LOO | 18e0 CANADIAN SYLLABICS R-CREE RWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e3 CANADIAN SYLLABICS THWE | 18e2 CANADIAN SYLLABICS WEST-CREE LAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e5 CANADIAN SYLLABICS TTHWE | 18e4 CANADIAN SYLLABICS THWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e7 CANADIAN SYLLABICS TTHAA | 18e6 CANADIAN SYLLABICS TTHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18e9 CANADIAN SYLLABICS TLHOO | 18e8 CANADIAN SYLLABICS TLHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18eb CANADIAN SYLLABICS SAYISI SHOO | 18ea CANADIAN SYLLABICS SAYISI SHWE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18ed CANADIAN SYLLABICS CARRIER GWU | 18ec CANADIAN SYLLABICS SAYISI HOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18ef CANADIAN SYLLABICS CARRIER GAA | 18ee CANADIAN SYLLABICS CARRIER DENE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f1 CANADIAN SYLLABICS SAYISI JUU | 18f0 CANADIAN SYLLABICS CARRIER GWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f3 CANADIAN SYLLABICS BEAVER DENE L | 18f2 CANADIAN SYLLABICS CARRIER JWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 18f5 CANADIAN SYLLABICS CARRIER DENTAL S | 18f4 CANADIAN SYLLABICS BEAVER DENE R */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18f7 (null) | 18f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18f9 (null) | 18f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18fb (null) | 18fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18fd (null) | 18fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 18ff (null) | 18fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1901 LIMBU LETTER KA | 1900 LIMBU VOWEL-CARRIER LETTER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1903 LIMBU LETTER GA | 1902 LIMBU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1905 LIMBU LETTER NGA | 1904 LIMBU LETTER GHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1907 LIMBU LETTER CHA | 1906 LIMBU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1909 LIMBU LETTER JHA | 1908 LIMBU LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190b LIMBU LETTER TA | 190a LIMBU LETTER YAN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190d LIMBU LETTER DA | 190c LIMBU LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 190f LIMBU LETTER NA | 190e LIMBU LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1911 LIMBU LETTER PHA | 1910 LIMBU LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1913 LIMBU LETTER BHA | 1912 LIMBU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1915 LIMBU LETTER YA | 1914 LIMBU LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1917 LIMBU LETTER LA | 1916 LIMBU LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1919 LIMBU LETTER SHA | 1918 LIMBU LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 191b LIMBU LETTER SA | 191a LIMBU LETTER SSA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 191d (null) | 191c LIMBU LETTER HA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 191f (null) | 191e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1921 LIMBU VOWEL SIGN I | 1920 LIMBU VOWEL SIGN A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1923 LIMBU VOWEL SIGN EE | 1922 LIMBU VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1925 LIMBU VOWEL SIGN OO | 1924 LIMBU VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1927 LIMBU VOWEL SIGN E | 1926 LIMBU VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1929 LIMBU SUBJOINED LETTER YA | 1928 LIMBU VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 192b LIMBU SUBJOINED LETTER WA | 192a LIMBU SUBJOINED LETTER RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 192d (null) | 192c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 192f (null) | 192e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1931 LIMBU SMALL LETTER NGA | 1930 LIMBU SMALL LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1933 LIMBU SMALL LETTER TA | 1932 LIMBU SMALL LETTER ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1935 LIMBU SMALL LETTER PA | 1934 LIMBU SMALL LETTER NA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1937 LIMBU SMALL LETTER RA | 1936 LIMBU SMALL LETTER MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1939 LIMBU SIGN MUKPHRENG | 1938 LIMBU SMALL LETTER LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 193b LIMBU SIGN SA-I | 193a LIMBU SIGN KEMPHRENG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 193d (null) | 193c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 193f (null) | 193e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1941 (null) | 1940 LIMBU SIGN LOO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1943 (null) | 1942 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1945 LIMBU QUESTION MARK | 1944 LIMBU EXCLAMATION MARK */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1947 LIMBU DIGIT ONE | 1946 LIMBU DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1949 LIMBU DIGIT THREE | 1948 LIMBU DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194b LIMBU DIGIT FIVE | 194a LIMBU DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194d LIMBU DIGIT SEVEN | 194c LIMBU DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 194f LIMBU DIGIT NINE | 194e LIMBU DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1951 TAI LE LETTER XA | 1950 TAI LE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1953 TAI LE LETTER TSA | 1952 TAI LE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1955 TAI LE LETTER YA | 1954 TAI LE LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1957 TAI LE LETTER THA | 1956 TAI LE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1959 TAI LE LETTER PA | 1958 TAI LE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195b TAI LE LETTER MA | 195a TAI LE LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195d TAI LE LETTER VA | 195c TAI LE LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 195f TAI LE LETTER QA | 195e TAI LE LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1961 TAI LE LETTER TSHA | 1960 TAI LE LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1963 TAI LE LETTER A | 1962 TAI LE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1965 TAI LE LETTER EE | 1964 TAI LE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1967 TAI LE LETTER U | 1966 TAI LE LETTER EH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1969 TAI LE LETTER O | 1968 TAI LE LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 196b TAI LE LETTER E | 196a TAI LE LETTER UE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 196d TAI LE LETTER AI | 196c TAI LE LETTER AUE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 196f (null) | 196e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1971 TAI LE LETTER TONE-3 | 1970 TAI LE LETTER TONE-2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1973 TAI LE LETTER TONE-5 | 1972 TAI LE LETTER TONE-4 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1975 (null) | 1974 TAI LE LETTER TONE-6 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1977 (null) | 1976 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1979 (null) | 1978 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197b (null) | 197a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197d (null) | 197c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 197f (null) | 197e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1981 NEW TAI LUE LETTER LOW QA | 1980 NEW TAI LUE LETTER HIGH QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1983 NEW TAI LUE LETTER HIGH XA | 1982 NEW TAI LUE LETTER HIGH KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1985 NEW TAI LUE LETTER LOW KA | 1984 NEW TAI LUE LETTER HIGH NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1987 NEW TAI LUE LETTER LOW NGA | 1986 NEW TAI LUE LETTER LOW XA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1989 NEW TAI LUE LETTER HIGH SA | 1988 NEW TAI LUE LETTER HIGH TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198b NEW TAI LUE LETTER LOW TSA | 198a NEW TAI LUE LETTER HIGH YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198d NEW TAI LUE LETTER LOW YA | 198c NEW TAI LUE LETTER LOW SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 198f NEW TAI LUE LETTER HIGH THA | 198e NEW TAI LUE LETTER HIGH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1991 NEW TAI LUE LETTER LOW TA | 1990 NEW TAI LUE LETTER HIGH NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1993 NEW TAI LUE LETTER LOW NA | 1992 NEW TAI LUE LETTER LOW THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1995 NEW TAI LUE LETTER HIGH PHA | 1994 NEW TAI LUE LETTER HIGH PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1997 NEW TAI LUE LETTER LOW PA | 1996 NEW TAI LUE LETTER HIGH MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1999 NEW TAI LUE LETTER LOW MA | 1998 NEW TAI LUE LETTER LOW PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199b NEW TAI LUE LETTER HIGH VA | 199a NEW TAI LUE LETTER HIGH FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199d NEW TAI LUE LETTER LOW FA | 199c NEW TAI LUE LETTER HIGH LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 199f NEW TAI LUE LETTER LOW LA | 199e NEW TAI LUE LETTER LOW VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a1 NEW TAI LUE LETTER HIGH DA | 19a0 NEW TAI LUE LETTER HIGH HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a3 NEW TAI LUE LETTER LOW HA | 19a2 NEW TAI LUE LETTER HIGH BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a5 NEW TAI LUE LETTER LOW BA | 19a4 NEW TAI LUE LETTER LOW DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a7 NEW TAI LUE LETTER HIGH XVA | 19a6 NEW TAI LUE LETTER HIGH KVA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19a9 NEW TAI LUE LETTER LOW XVA | 19a8 NEW TAI LUE LETTER LOW KVA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19ab NEW TAI LUE LETTER LOW SUA | 19aa NEW TAI LUE LETTER HIGH SUA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19ad (null) | 19ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19af (null) | 19ae (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b1 NEW TAI LUE VOWEL SIGN AA | 19b0 NEW TAI LUE VOWEL SIGN VOWEL SHORTENER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b3 NEW TAI LUE VOWEL SIGN U | 19b2 NEW TAI LUE VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b5 NEW TAI LUE VOWEL SIGN E | 19b4 NEW TAI LUE VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b7 NEW TAI LUE VOWEL SIGN O | 19b6 NEW TAI LUE VOWEL SIGN AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19b9 NEW TAI LUE VOWEL SIGN UE | 19b8 NEW TAI LUE VOWEL SIGN OA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bb NEW TAI LUE VOWEL SIGN AAY | 19ba NEW TAI LUE VOWEL SIGN AY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bd NEW TAI LUE VOWEL SIGN OY | 19bc NEW TAI LUE VOWEL SIGN UY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19bf NEW TAI LUE VOWEL SIGN UEY | 19be NEW TAI LUE VOWEL SIGN OAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 19c1 NEW TAI LUE LETTER FINAL V | 19c0 NEW TAI LUE VOWEL SIGN IY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c3 NEW TAI LUE LETTER FINAL N | 19c2 NEW TAI LUE LETTER FINAL NG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c5 NEW TAI LUE LETTER FINAL K | 19c4 NEW TAI LUE LETTER FINAL M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 19c7 NEW TAI LUE LETTER FINAL B | 19c6 NEW TAI LUE LETTER FINAL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19c9 NEW TAI LUE TONE MARK-2 | 19c8 NEW TAI LUE TONE MARK-1 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cb (null) | 19ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cd (null) | 19cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19cf (null) | 19ce (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d1 NEW TAI LUE DIGIT ONE | 19d0 NEW TAI LUE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d3 NEW TAI LUE DIGIT THREE | 19d2 NEW TAI LUE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d5 NEW TAI LUE DIGIT FIVE | 19d4 NEW TAI LUE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d7 NEW TAI LUE DIGIT SEVEN | 19d6 NEW TAI LUE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 19d9 NEW TAI LUE DIGIT NINE | 19d8 NEW TAI LUE DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 19db (null) | 19da NEW TAI LUE THAM DIGIT ONE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 19dd (null) | 19dc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19df NEW TAI LUE SIGN LAEV | 19de NEW TAI LUE SIGN LAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e1 KHMER SYMBOL MUOY KOET | 19e0 KHMER SYMBOL PATHAMASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e3 KHMER SYMBOL BEI KOET | 19e2 KHMER SYMBOL PII KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e5 KHMER SYMBOL PRAM KOET | 19e4 KHMER SYMBOL BUON KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e7 KHMER SYMBOL PRAM-PII KOET | 19e6 KHMER SYMBOL PRAM-MUOY KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19e9 KHMER SYMBOL PRAM-BUON KOET | 19e8 KHMER SYMBOL PRAM-BEI KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19eb KHMER SYMBOL DAP-MUOY KOET | 19ea KHMER SYMBOL DAP KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ed KHMER SYMBOL DAP-BEI KOET | 19ec KHMER SYMBOL DAP-PII KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ef KHMER SYMBOL DAP-PRAM KOET | 19ee KHMER SYMBOL DAP-BUON KOET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f1 KHMER SYMBOL MUOY ROC | 19f0 KHMER SYMBOL TUTEYASAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f3 KHMER SYMBOL BEI ROC | 19f2 KHMER SYMBOL PII ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f5 KHMER SYMBOL PRAM ROC | 19f4 KHMER SYMBOL BUON ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f7 KHMER SYMBOL PRAM-PII ROC | 19f6 KHMER SYMBOL PRAM-MUOY ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19f9 KHMER SYMBOL PRAM-BUON ROC | 19f8 KHMER SYMBOL PRAM-BEI ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19fb KHMER SYMBOL DAP-MUOY ROC | 19fa KHMER SYMBOL DAP ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19fd KHMER SYMBOL DAP-BEI ROC | 19fc KHMER SYMBOL DAP-PII ROC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 19ff KHMER SYMBOL DAP-PRAM ROC | 19fe KHMER SYMBOL DAP-BUON ROC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a01 BUGINESE LETTER GA | 1a00 BUGINESE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a03 BUGINESE LETTER NGKA | 1a02 BUGINESE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a05 BUGINESE LETTER BA | 1a04 BUGINESE LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a07 BUGINESE LETTER MPA | 1a06 BUGINESE LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a09 BUGINESE LETTER DA | 1a08 BUGINESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0b BUGINESE LETTER NRA | 1a0a BUGINESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0d BUGINESE LETTER JA | 1a0c BUGINESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a0f BUGINESE LETTER NYCA | 1a0e BUGINESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a11 BUGINESE LETTER RA | 1a10 BUGINESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a13 BUGINESE LETTER VA | 1a12 BUGINESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a15 BUGINESE LETTER A | 1a14 BUGINESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1a17 BUGINESE VOWEL SIGN I | 1a16 BUGINESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a19 BUGINESE VOWEL SIGN E | 1a18 BUGINESE VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a1b BUGINESE VOWEL SIGN AE | 1a1a BUGINESE VOWEL SIGN O */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a1d (null) | 1a1c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1a1f BUGINESE END OF SECTION | 1a1e BUGINESE PALLAWA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a21 TAI THAM LETTER HIGH KHA | 1a20 TAI THAM LETTER HIGH KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a23 TAI THAM LETTER LOW KA | 1a22 TAI THAM LETTER HIGH KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a25 TAI THAM LETTER LOW KHA | 1a24 TAI THAM LETTER LOW KXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a27 TAI THAM LETTER HIGH CA | 1a26 TAI THAM LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a29 TAI THAM LETTER LOW CA | 1a28 TAI THAM LETTER HIGH CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2b TAI THAM LETTER LOW CHA | 1a2a TAI THAM LETTER LOW SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2d TAI THAM LETTER RATA | 1a2c TAI THAM LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a2f TAI THAM LETTER DA | 1a2e TAI THAM LETTER HIGH RATHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a31 TAI THAM LETTER RANA | 1a30 TAI THAM LETTER LOW RATHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a33 TAI THAM LETTER HIGH THA | 1a32 TAI THAM LETTER HIGH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a35 TAI THAM LETTER LOW THA | 1a34 TAI THAM LETTER LOW TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a37 TAI THAM LETTER BA | 1a36 TAI THAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a39 TAI THAM LETTER HIGH PHA | 1a38 TAI THAM LETTER HIGH PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3b TAI THAM LETTER LOW PA | 1a3a TAI THAM LETTER HIGH FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3d TAI THAM LETTER LOW PHA | 1a3c TAI THAM LETTER LOW FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a3f TAI THAM LETTER LOW YA | 1a3e TAI THAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a41 TAI THAM LETTER RA | 1a40 TAI THAM LETTER HIGH YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a43 TAI THAM LETTER LA | 1a42 TAI THAM LETTER RUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a45 TAI THAM LETTER WA | 1a44 TAI THAM LETTER LUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a47 TAI THAM LETTER HIGH SSA | 1a46 TAI THAM LETTER HIGH SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a49 TAI THAM LETTER HIGH HA | 1a48 TAI THAM LETTER HIGH SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4b TAI THAM LETTER A | 1a4a TAI THAM LETTER LLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4d TAI THAM LETTER I | 1a4c TAI THAM LETTER LOW HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a4f TAI THAM LETTER U | 1a4e TAI THAM LETTER II */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a51 TAI THAM LETTER EE | 1a50 TAI THAM LETTER UU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1a53 TAI THAM LETTER LAE | 1a52 TAI THAM LETTER OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1a55 TAI THAM CONSONANT SIGN MEDIAL RA | 1a54 TAI THAM LETTER GREAT SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a57 TAI THAM CONSONANT SIGN LA TANG LAI | 1a56 TAI THAM CONSONANT SIGN MEDIAL LA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a59 TAI THAM CONSONANT SIGN FINAL NGA | 1a58 TAI THAM SIGN MAI KANG LAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a5b TAI THAM CONSONANT SIGN HIGH RATHA OR L | 1a5a TAI THAM CONSONANT SIGN LOW PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a5d TAI THAM CONSONANT SIGN BA | 1a5c TAI THAM CONSONANT SIGN MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1a5f (null) | 1a5e TAI THAM CONSONANT SIGN SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a61 TAI THAM VOWEL SIGN A | 1a60 TAI THAM SIGN SAKOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a63 TAI THAM VOWEL SIGN AA | 1a62 TAI THAM VOWEL SIGN MAI SAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a65 TAI THAM VOWEL SIGN I | 1a64 TAI THAM VOWEL SIGN TALL AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a67 TAI THAM VOWEL SIGN UE | 1a66 TAI THAM VOWEL SIGN II */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a69 TAI THAM VOWEL SIGN U | 1a68 TAI THAM VOWEL SIGN UUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6b TAI THAM VOWEL SIGN O | 1a6a TAI THAM VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6d TAI THAM VOWEL SIGN OY | 1a6c TAI THAM VOWEL SIGN OA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a6f TAI THAM VOWEL SIGN AE | 1a6e TAI THAM VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a71 TAI THAM VOWEL SIGN AI | 1a70 TAI THAM VOWEL SIGN OO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a73 TAI THAM VOWEL SIGN OA ABOVE | 1a72 TAI THAM VOWEL SIGN THAM AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a75 TAI THAM SIGN TONE-1 | 1a74 TAI THAM SIGN MAI KANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a77 TAI THAM SIGN KHUEN TONE-3 | 1a76 TAI THAM SIGN TONE-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a79 TAI THAM SIGN KHUEN TONE-5 | 1a78 TAI THAM SIGN KHUEN TONE-4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1a7b TAI THAM SIGN MAI SAM | 1a7a TAI THAM SIGN RA HAAM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1a7d (null) | 1a7c TAI THAM SIGN KHUEN-LUE KARAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 1a7f TAI THAM COMBINING CRYPTOGRAMMIC DOT | 1a7e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a81 TAI THAM HORA DIGIT ONE | 1a80 TAI THAM HORA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a83 TAI THAM HORA DIGIT THREE | 1a82 TAI THAM HORA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a85 TAI THAM HORA DIGIT FIVE | 1a84 TAI THAM HORA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a87 TAI THAM HORA DIGIT SEVEN | 1a86 TAI THAM HORA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a89 TAI THAM HORA DIGIT NINE | 1a88 TAI THAM HORA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8b (null) | 1a8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8d (null) | 1a8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a8f (null) | 1a8e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a91 TAI THAM THAM DIGIT ONE | 1a90 TAI THAM THAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a93 TAI THAM THAM DIGIT THREE | 1a92 TAI THAM THAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a95 TAI THAM THAM DIGIT FIVE | 1a94 TAI THAM THAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a97 TAI THAM THAM DIGIT SEVEN | 1a96 TAI THAM THAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1a99 TAI THAM THAM DIGIT NINE | 1a98 TAI THAM THAM DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9b (null) | 1a9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9d (null) | 1a9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1a9f (null) | 1a9e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa1 TAI THAM SIGN WIANGWAAK | 1aa0 TAI THAM SIGN WIANG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa3 TAI THAM SIGN KEOW | 1aa2 TAI THAM SIGN SAWAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa5 TAI THAM SIGN DOKMAI | 1aa4 TAI THAM SIGN HOY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1aa7 TAI THAM SIGN MAI YAMOK | 1aa6 TAI THAM SIGN REVERSED ROTATED RANA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aa9 TAI THAM SIGN KAANKUU | 1aa8 TAI THAM SIGN KAAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aab TAI THAM SIGN SATKAANKUU | 1aaa TAI THAM SIGN SATKAAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1aad TAI THAM SIGN CAANG | 1aac TAI THAM SIGN HANG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aaf (null) | 1aae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab1 (null) | 1ab0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab3 (null) | 1ab2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab5 (null) | 1ab4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab7 (null) | 1ab6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ab9 (null) | 1ab8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abb (null) | 1aba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abd (null) | 1abc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1abf (null) | 1abe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac1 (null) | 1ac0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac3 (null) | 1ac2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac5 (null) | 1ac4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac7 (null) | 1ac6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ac9 (null) | 1ac8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acb (null) | 1aca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acd (null) | 1acc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1acf (null) | 1ace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad1 (null) | 1ad0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad3 (null) | 1ad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad5 (null) | 1ad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad7 (null) | 1ad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ad9 (null) | 1ad8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1adb (null) | 1ada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1add (null) | 1adc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1adf (null) | 1ade (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae1 (null) | 1ae0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae3 (null) | 1ae2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae5 (null) | 1ae4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae7 (null) | 1ae6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ae9 (null) | 1ae8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aeb (null) | 1aea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aed (null) | 1aec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aef (null) | 1aee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af1 (null) | 1af0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af3 (null) | 1af2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af5 (null) | 1af4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af7 (null) | 1af6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1af9 (null) | 1af8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1afb (null) | 1afa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1afd (null) | 1afc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1aff (null) | 1afe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b01 BALINESE SIGN ULU CANDRA | 1b00 BALINESE SIGN ULU RICEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b03 BALINESE SIGN SURANG | 1b02 BALINESE SIGN CECEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b05 BALINESE LETTER AKARA | 1b04 BALINESE SIGN BISAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b07 BALINESE LETTER IKARA | 1b06 BALINESE LETTER AKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b09 BALINESE LETTER UKARA | 1b08 BALINESE LETTER IKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0b BALINESE LETTER RA REPA | 1b0a BALINESE LETTER UKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0d BALINESE LETTER LA LENGA | 1b0c BALINESE LETTER RA REPA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b0f BALINESE LETTER EKARA | 1b0e BALINESE LETTER LA LENGA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b11 BALINESE LETTER OKARA | 1b10 BALINESE LETTER AIKARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b13 BALINESE LETTER KA | 1b12 BALINESE LETTER OKARA TEDUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b15 BALINESE LETTER GA | 1b14 BALINESE LETTER KA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b17 BALINESE LETTER NGA | 1b16 BALINESE LETTER GA GORA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b19 BALINESE LETTER CA LACA | 1b18 BALINESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1b BALINESE LETTER JA JERA | 1b1a BALINESE LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1d BALINESE LETTER TA LATIK | 1b1c BALINESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b1f BALINESE LETTER DA MURDA ALPAPRANA | 1b1e BALINESE LETTER TA MURDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b21 BALINESE LETTER NA RAMBAT | 1b20 BALINESE LETTER DA MURDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b23 BALINESE LETTER TA TAWA | 1b22 BALINESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b25 BALINESE LETTER DA MADU | 1b24 BALINESE LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b27 BALINESE LETTER PA | 1b26 BALINESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b29 BALINESE LETTER BA | 1b28 BALINESE LETTER PA KAPAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2b BALINESE LETTER MA | 1b2a BALINESE LETTER BA KEMBANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2d BALINESE LETTER RA | 1b2c BALINESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b2f BALINESE LETTER WA | 1b2e BALINESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b31 BALINESE LETTER SA SAPA | 1b30 BALINESE LETTER SA SAGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b33 BALINESE LETTER HA | 1b32 BALINESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b35 BALINESE VOWEL SIGN TEDUNG | 1b34 BALINESE SIGN REREKAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b37 BALINESE VOWEL SIGN ULU SARI | 1b36 BALINESE VOWEL SIGN ULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b39 BALINESE VOWEL SIGN SUKU ILUT | 1b38 BALINESE VOWEL SIGN SUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3b BALINESE VOWEL SIGN RA REPA TEDUNG | 1b3a BALINESE VOWEL SIGN RA REPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3d BALINESE VOWEL SIGN LA LENGA TEDUNG | 1b3c BALINESE VOWEL SIGN LA LENGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b3f BALINESE VOWEL SIGN TALING REPA | 1b3e BALINESE VOWEL SIGN TALING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b41 BALINESE VOWEL SIGN TALING REPA TEDUNG | 1b40 BALINESE VOWEL SIGN TALING TEDUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b43 BALINESE VOWEL SIGN PEPET TEDUNG | 1b42 BALINESE VOWEL SIGN PEPET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b45 BALINESE LETTER KAF SASAK | 1b44 BALINESE ADEG ADEG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b47 BALINESE LETTER TZIR SASAK | 1b46 BALINESE LETTER KHOT SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b49 BALINESE LETTER VE SASAK | 1b48 BALINESE LETTER EF SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b4b BALINESE LETTER ASYURA SASAK | 1b4a BALINESE LETTER ZAL SASAK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b4d (null) | 1b4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b4f (null) | 1b4e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b51 BALINESE DIGIT ONE | 1b50 BALINESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b53 BALINESE DIGIT THREE | 1b52 BALINESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b55 BALINESE DIGIT FIVE | 1b54 BALINESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b57 BALINESE DIGIT SEVEN | 1b56 BALINESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1b59 BALINESE DIGIT NINE | 1b58 BALINESE DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5b BALINESE PAMADA | 1b5a BALINESE PANTI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5d BALINESE CARIK PAMUNGKAH | 1b5c BALINESE WINDU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1b5f BALINESE CARIK PAREREN | 1b5e BALINESE CARIK SIKI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 1b61 BALINESE MUSICAL SYMBOL DONG | 1b60 BALINESE PAMENENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b63 BALINESE MUSICAL SYMBOL DUNG | 1b62 BALINESE MUSICAL SYMBOL DENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b65 BALINESE MUSICAL SYMBOL DANG SURANG | 1b64 BALINESE MUSICAL SYMBOL DANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b67 BALINESE MUSICAL SYMBOL DAENG | 1b66 BALINESE MUSICAL SYMBOL DING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b69 BALINESE MUSICAL SYMBOL DAING | 1b68 BALINESE MUSICAL SYMBOL DEUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6b BALINESE MUSICAL SYMBOL COMBINING TEGEH | 1b6a BALINESE MUSICAL SYMBOL DANG GEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6d BALINESE MUSICAL SYMBOL COMBINING KEMPU | 1b6c BALINESE MUSICAL SYMBOL COMBINING ENDEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b6f BALINESE MUSICAL SYMBOL COMBINING JEGOG | 1b6e BALINESE MUSICAL SYMBOL COMBINING KEMPL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b71 BALINESE MUSICAL SYMBOL COMBINING KEMPL | 1b70 BALINESE MUSICAL SYMBOL COMBINING KEMPU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b73 BALINESE MUSICAL SYMBOL COMBINING GONG | 1b72 BALINESE MUSICAL SYMBOL COMBINING BENDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b75 BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN | 1b74 BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b77 BALINESE MUSICAL SYMBOL RIGHT-HAND CLOS | 1b76 BALINESE MUSICAL SYMBOL RIGHT-HAND CLOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b79 BALINESE MUSICAL SYMBOL LEFT-HAND OPEN | 1b78 BALINESE MUSICAL SYMBOL LEFT-HAND OPEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b7b BALINESE MUSICAL SYMBOL LEFT-HAND CLOSE | 1b7a BALINESE MUSICAL SYMBOL LEFT-HAND CLOSE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1b7d (null) | 1b7c BALINESE MUSICAL SYMBOL LEFT-HAND OPEN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1b7f (null) | 1b7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1b81 SUNDANESE SIGN PANGLAYAR | 1b80 SUNDANESE SIGN PANYECEK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1b83 SUNDANESE LETTER A | 1b82 SUNDANESE SIGN PANGWISAD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b85 SUNDANESE LETTER U | 1b84 SUNDANESE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b87 SUNDANESE LETTER O | 1b86 SUNDANESE LETTER AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b89 SUNDANESE LETTER EU | 1b88 SUNDANESE LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8b SUNDANESE LETTER QA | 1b8a SUNDANESE LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8d SUNDANESE LETTER NGA | 1b8c SUNDANESE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b8f SUNDANESE LETTER JA | 1b8e SUNDANESE LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b91 SUNDANESE LETTER NYA | 1b90 SUNDANESE LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b93 SUNDANESE LETTER DA | 1b92 SUNDANESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b95 SUNDANESE LETTER PA | 1b94 SUNDANESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b97 SUNDANESE LETTER VA | 1b96 SUNDANESE LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b99 SUNDANESE LETTER MA | 1b98 SUNDANESE LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9b SUNDANESE LETTER RA | 1b9a SUNDANESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9d SUNDANESE LETTER WA | 1b9c SUNDANESE LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1b9f SUNDANESE LETTER XA | 1b9e SUNDANESE LETTER SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1ba1 SUNDANESE CONSONANT SIGN PAMINGKAL | 1ba0 SUNDANESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba3 SUNDANESE CONSONANT SIGN PANYIKU | 1ba2 SUNDANESE CONSONANT SIGN PANYAKRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba5 SUNDANESE VOWEL SIGN PANYUKU | 1ba4 SUNDANESE VOWEL SIGN PANGHULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba7 SUNDANESE VOWEL SIGN PANOLONG | 1ba6 SUNDANESE VOWEL SIGN PANAELAENG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ba9 SUNDANESE VOWEL SIGN PANEULEUNG | 1ba8 SUNDANESE VOWEL SIGN PAMEPET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bab SUNDANESE SIGN VIRAMA | 1baa SUNDANESE SIGN PAMAAEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bad SUNDANESE CONSONANT SIGN PASANGAN WA | 1bac SUNDANESE CONSONANT SIGN PASANGAN MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1baf SUNDANESE LETTER SYA | 1bae SUNDANESE LETTER KHA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb1 SUNDANESE DIGIT ONE | 1bb0 SUNDANESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb3 SUNDANESE DIGIT THREE | 1bb2 SUNDANESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb5 SUNDANESE DIGIT FIVE | 1bb4 SUNDANESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb7 SUNDANESE DIGIT SEVEN | 1bb6 SUNDANESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1bb9 SUNDANESE DIGIT NINE | 1bb8 SUNDANESE DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbb SUNDANESE LETTER REU | 1bba SUNDANESE AVAGRAHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbd SUNDANESE LETTER BHA | 1bbc SUNDANESE LETTER LEU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bbf SUNDANESE LETTER FINAL M | 1bbe SUNDANESE LETTER FINAL K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc1 BATAK LETTER SIMALUNGUN A | 1bc0 BATAK LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc3 BATAK LETTER SIMALUNGUN HA | 1bc2 BATAK LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc5 BATAK LETTER BA | 1bc4 BATAK LETTER MANDAILING HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc7 BATAK LETTER PA | 1bc6 BATAK LETTER KARO BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bc9 BATAK LETTER NA | 1bc8 BATAK LETTER SIMALUNGUN PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcb BATAK LETTER WA | 1bca BATAK LETTER MANDAILING NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcd BATAK LETTER PAKPAK WA | 1bcc BATAK LETTER SIMALUNGUN WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bcf BATAK LETTER SIMALUNGUN GA | 1bce BATAK LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd1 BATAK LETTER DA | 1bd0 BATAK LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd3 BATAK LETTER SIMALUNGUN RA | 1bd2 BATAK LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd5 BATAK LETTER SIMALUNGUN MA | 1bd4 BATAK LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd7 BATAK LETTER NORTHERN TA | 1bd6 BATAK LETTER SOUTHERN TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bd9 BATAK LETTER SIMALUNGUN SA | 1bd8 BATAK LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdb BATAK LETTER YA | 1bda BATAK LETTER MANDAILING SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdd BATAK LETTER NGA | 1bdc BATAK LETTER SIMALUNGUN YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1bdf BATAK LETTER SIMALUNGUN LA | 1bde BATAK LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be1 BATAK LETTER CA | 1be0 BATAK LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be3 BATAK LETTER MBA | 1be2 BATAK LETTER NDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1be5 BATAK LETTER U | 1be4 BATAK LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1be7 BATAK VOWEL SIGN E | 1be6 BATAK SIGN TOMPI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1be9 BATAK VOWEL SIGN EE | 1be8 BATAK VOWEL SIGN PAKPAK E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1beb BATAK VOWEL SIGN KARO I | 1bea BATAK VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bed BATAK VOWEL SIGN KARO O | 1bec BATAK VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bef BATAK VOWEL SIGN U FOR SIMALUNGUN SA | 1bee BATAK VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bf1 BATAK CONSONANT SIGN H | 1bf0 BATAK CONSONANT SIGN NG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1bf3 BATAK PANONGONAN | 1bf2 BATAK PANGOLAT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf5 (null) | 1bf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf7 (null) | 1bf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bf9 (null) | 1bf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1bfb (null) | 1bfa (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1bfd BATAK SYMBOL BINDU PINARBORAS | 1bfc BATAK SYMBOL BINDU NA METEK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1bff BATAK SYMBOL BINDU PANGOLAT | 1bfe BATAK SYMBOL BINDU JUDUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c01 LEPCHA LETTER KLA | 1c00 LEPCHA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c03 LEPCHA LETTER GA | 1c02 LEPCHA LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c05 LEPCHA LETTER NGA | 1c04 LEPCHA LETTER GLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c07 LEPCHA LETTER CHA | 1c06 LEPCHA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c09 LEPCHA LETTER NYA | 1c08 LEPCHA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0b LEPCHA LETTER THA | 1c0a LEPCHA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0d LEPCHA LETTER NA | 1c0c LEPCHA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c0f LEPCHA LETTER PLA | 1c0e LEPCHA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c11 LEPCHA LETTER FA | 1c10 LEPCHA LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c13 LEPCHA LETTER BA | 1c12 LEPCHA LETTER FLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c15 LEPCHA LETTER MA | 1c14 LEPCHA LETTER BLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c17 LEPCHA LETTER TSA | 1c16 LEPCHA LETTER MLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c19 LEPCHA LETTER DZA | 1c18 LEPCHA LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1b LEPCHA LETTER RA | 1c1a LEPCHA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1d LEPCHA LETTER HA | 1c1c LEPCHA LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c1f LEPCHA LETTER VA | 1c1e LEPCHA LETTER HLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c21 LEPCHA LETTER SHA | 1c20 LEPCHA LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c23 LEPCHA LETTER A | 1c22 LEPCHA LETTER WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c25 LEPCHA SUBJOINED LETTER RA | 1c24 LEPCHA SUBJOINED LETTER YA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c27 LEPCHA VOWEL SIGN I | 1c26 LEPCHA VOWEL SIGN AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c29 LEPCHA VOWEL SIGN OO | 1c28 LEPCHA VOWEL SIGN O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2b LEPCHA VOWEL SIGN UU | 1c2a LEPCHA VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2d LEPCHA CONSONANT SIGN K | 1c2c LEPCHA VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c2f LEPCHA CONSONANT SIGN L | 1c2e LEPCHA CONSONANT SIGN M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c31 LEPCHA CONSONANT SIGN P | 1c30 LEPCHA CONSONANT SIGN N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c33 LEPCHA CONSONANT SIGN T | 1c32 LEPCHA CONSONANT SIGN R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c35 LEPCHA CONSONANT SIGN KANG | 1c34 LEPCHA CONSONANT SIGN NYIN-DO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c37 LEPCHA SIGN NUKTA | 1c36 LEPCHA SIGN RAN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c39 (null) | 1c38 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 1c3b LEPCHA PUNCTUATION TA-ROL | 1c3a (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c3d LEPCHA PUNCTUATION CER-WA | 1c3c LEPCHA PUNCTUATION NYET THYOOM TA-ROL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c3f LEPCHA PUNCTUATION TSHOOK | 1c3e LEPCHA PUNCTUATION TSHOOK CER-WA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c41 LEPCHA DIGIT ONE | 1c40 LEPCHA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c43 LEPCHA DIGIT THREE | 1c42 LEPCHA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c45 LEPCHA DIGIT FIVE | 1c44 LEPCHA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c47 LEPCHA DIGIT SEVEN | 1c46 LEPCHA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c49 LEPCHA DIGIT NINE | 1c48 LEPCHA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c4b (null) | 1c4a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 1c4d LEPCHA LETTER TTA | 1c4c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c4f LEPCHA LETTER DDA | 1c4e LEPCHA LETTER TTHA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c51 OL CHIKI DIGIT ONE | 1c50 OL CHIKI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c53 OL CHIKI DIGIT THREE | 1c52 OL CHIKI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c55 OL CHIKI DIGIT FIVE | 1c54 OL CHIKI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c57 OL CHIKI DIGIT SEVEN | 1c56 OL CHIKI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* 1c59 OL CHIKI DIGIT NINE | 1c58 OL CHIKI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5b OL CHIKI LETTER AT | 1c5a OL CHIKI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5d OL CHIKI LETTER ANG | 1c5c OL CHIKI LETTER AG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c5f OL CHIKI LETTER LAA | 1c5e OL CHIKI LETTER AL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c61 OL CHIKI LETTER AAJ | 1c60 OL CHIKI LETTER AAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c63 OL CHIKI LETTER AAW | 1c62 OL CHIKI LETTER AAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c65 OL CHIKI LETTER IS | 1c64 OL CHIKI LETTER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c67 OL CHIKI LETTER INY | 1c66 OL CHIKI LETTER IH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c69 OL CHIKI LETTER LU | 1c68 OL CHIKI LETTER IR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6b OL CHIKI LETTER UD | 1c6a OL CHIKI LETTER UC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6d OL CHIKI LETTER UY | 1c6c OL CHIKI LETTER UNN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c6f OL CHIKI LETTER EP | 1c6e OL CHIKI LETTER LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c71 OL CHIKI LETTER EN | 1c70 OL CHIKI LETTER EDD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c73 OL CHIKI LETTER LO | 1c72 OL CHIKI LETTER ERR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c75 OL CHIKI LETTER OB | 1c74 OL CHIKI LETTER OTT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1c77 OL CHIKI LETTER OH | 1c76 OL CHIKI LETTER OV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c79 OL CHIKI GAAHLAA TTUDDAAG | 1c78 OL CHIKI MU TTUDDAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c7b OL CHIKI RELAA | 1c7a OL CHIKI MU-GAAHLAA TTUDDAAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1c7d OL CHIKI AHAD | 1c7c OL CHIKI PHAARKAA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1c7f OL CHIKI PUNCTUATION DOUBLE MUCAAD | 1c7e OL CHIKI PUNCTUATION MUCAAD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c81 (null) | 1c80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c83 (null) | 1c82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c85 (null) | 1c84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c87 (null) | 1c86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c89 (null) | 1c88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8b (null) | 1c8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8d (null) | 1c8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c8f (null) | 1c8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c91 (null) | 1c90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c93 (null) | 1c92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c95 (null) | 1c94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c97 (null) | 1c96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c99 (null) | 1c98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9b (null) | 1c9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9d (null) | 1c9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1c9f (null) | 1c9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca1 (null) | 1ca0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca3 (null) | 1ca2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca5 (null) | 1ca4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca7 (null) | 1ca6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ca9 (null) | 1ca8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cab (null) | 1caa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cad (null) | 1cac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1caf (null) | 1cae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb1 (null) | 1cb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb3 (null) | 1cb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb5 (null) | 1cb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb7 (null) | 1cb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cb9 (null) | 1cb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbb (null) | 1cba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbd (null) | 1cbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cbf (null) | 1cbe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc1 SUNDANESE PUNCTUATION BINDU PANGLONG | 1cc0 SUNDANESE PUNCTUATION BINDU SURYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc3 SUNDANESE PUNCTUATION BINDU CAKRA | 1cc2 SUNDANESE PUNCTUATION BINDU PURNAMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc5 SUNDANESE PUNCTUATION BINDU KA SATANGA | 1cc4 SUNDANESE PUNCTUATION BINDU LEU SATANGA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 1cc7 SUNDANESE PUNCTUATION BINDU BA SATANGA | 1cc6 SUNDANESE PUNCTUATION BINDU DA SATANGA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cc9 (null) | 1cc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccb (null) | 1cca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccd (null) | 1ccc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ccf (null) | 1cce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd1 VEDIC TONE SHARA | 1cd0 VEDIC TONE KARSHANA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 1cd3 VEDIC SIGN NIHSHVASA | 1cd2 VEDIC TONE PRENKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd5 VEDIC TONE YAJURVEDIC AGGRAVATED INDEPE | 1cd4 VEDIC SIGN YAJURVEDIC MIDLINE SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd7 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDE | 1cd6 VEDIC TONE YAJURVEDIC INDEPENDENT SVARI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cd9 VEDIC TONE YAJURVEDIC KATHAKA INDEPENDE | 1cd8 VEDIC TONE CANDRA BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdb VEDIC TONE TRIPLE SVARITA | 1cda VEDIC TONE DOUBLE SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdd VEDIC TONE DOT BELOW | 1cdc VEDIC TONE KATHAKA ANUDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cdf VEDIC TONE THREE DOTS BELOW | 1cde VEDIC TONE TWO DOTS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce1 VEDIC TONE ATHARVAVEDIC INDEPENDENT SVA | 1ce0 VEDIC TONE RIGVEDIC KASHMIRI INDEPENDEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce3 VEDIC SIGN VISARGA UDATTA | 1ce2 VEDIC SIGN VISARGA SVARITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce5 VEDIC SIGN VISARGA ANUDATTA | 1ce4 VEDIC SIGN REVERSED VISARGA UDATTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ce7 VEDIC SIGN VISARGA UDATTA WITH TAIL | 1ce6 VEDIC SIGN REVERSED VISARGA ANUDATTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1ce9 VEDIC SIGN ANUSVARA ANTARGOMUKHA | 1ce8 VEDIC SIGN VISARGA ANUDATTA WITH TAIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1ceb VEDIC SIGN ANUSVARA VAMAGOMUKHA | 1cea VEDIC SIGN ANUSVARA BAHIRGOMUKHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 1ced VEDIC SIGN TIRYAK | 1cec VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1cef VEDIC SIGN LONG ANUSVARA | 1cee VEDIC SIGN HEXIFORM LONG ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 1cf1 VEDIC SIGN ANUSVARA UBHAYATO MUKHA | 1cf0 VEDIC SIGN RTHANG LONG ANUSVARA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1cf3 VEDIC SIGN ROTATED ARDHAVISARGA | 1cf2 VEDIC SIGN ARDHAVISARGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 1cf5 VEDIC SIGN JIHVAMULIYA | 1cf4 VEDIC TONE CANDRA ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 1cf7 (null) | 1cf6 VEDIC SIGN UPADHMANIYA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cf9 (null) | 1cf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cfb (null) | 1cfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cfd (null) | 1cfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1cff (null) | 1cfe (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d01 LATIN LETTER SMALL CAPITAL AE | 1d00 LATIN LETTER SMALL CAPITAL A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d03 LATIN LETTER SMALL CAPITAL BARRED B | 1d02 LATIN SMALL LETTER TURNED AE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d05 LATIN LETTER SMALL CAPITAL D | 1d04 LATIN LETTER SMALL CAPITAL C */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d07 LATIN LETTER SMALL CAPITAL E | 1d06 LATIN LETTER SMALL CAPITAL ETH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d09 LATIN SMALL LETTER TURNED I | 1d08 LATIN SMALL LETTER TURNED OPEN E */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0b LATIN LETTER SMALL CAPITAL K | 1d0a LATIN LETTER SMALL CAPITAL J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0d LATIN LETTER SMALL CAPITAL M | 1d0c LATIN LETTER SMALL CAPITAL L WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d0f LATIN LETTER SMALL CAPITAL O | 1d0e LATIN LETTER SMALL CAPITAL REVERSED N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d11 LATIN SMALL LETTER SIDEWAYS O | 1d10 LATIN LETTER SMALL CAPITAL OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d13 LATIN SMALL LETTER SIDEWAYS O WITH STRO | 1d12 LATIN SMALL LETTER SIDEWAYS OPEN O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d15 LATIN LETTER SMALL CAPITAL OU | 1d14 LATIN SMALL LETTER TURNED OE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d17 LATIN SMALL LETTER BOTTOM HALF O | 1d16 LATIN SMALL LETTER TOP HALF O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d19 LATIN LETTER SMALL CAPITAL REVERSED R | 1d18 LATIN LETTER SMALL CAPITAL P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1b LATIN LETTER SMALL CAPITAL T | 1d1a LATIN LETTER SMALL CAPITAL TURNED R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1d LATIN SMALL LETTER SIDEWAYS U | 1d1c LATIN LETTER SMALL CAPITAL U */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d1f LATIN SMALL LETTER SIDEWAYS TURNED M | 1d1e LATIN SMALL LETTER SIDEWAYS DIAERESIZED */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d21 LATIN LETTER SMALL CAPITAL W | 1d20 LATIN LETTER SMALL CAPITAL V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d23 LATIN LETTER SMALL CAPITAL EZH | 1d22 LATIN LETTER SMALL CAPITAL Z */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d25 LATIN LETTER AIN | 1d24 LATIN LETTER VOICED LARYNGEAL SPIRANT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d27 GREEK LETTER SMALL CAPITAL LAMDA | 1d26 GREEK LETTER SMALL CAPITAL GAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d29 GREEK LETTER SMALL CAPITAL RHO | 1d28 GREEK LETTER SMALL CAPITAL PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d2b CYRILLIC LETTER SMALL CAPITAL EL | 1d2a GREEK LETTER SMALL CAPITAL PSI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d2d MODIFIER LETTER CAPITAL AE | 1d2c MODIFIER LETTER CAPITAL A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d2f MODIFIER LETTER CAPITAL BARRED B | 1d2e MODIFIER LETTER CAPITAL B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d31 MODIFIER LETTER CAPITAL E | 1d30 MODIFIER LETTER CAPITAL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d33 MODIFIER LETTER CAPITAL G | 1d32 MODIFIER LETTER CAPITAL REVERSED E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d35 MODIFIER LETTER CAPITAL I | 1d34 MODIFIER LETTER CAPITAL H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d37 MODIFIER LETTER CAPITAL K | 1d36 MODIFIER LETTER CAPITAL J */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d39 MODIFIER LETTER CAPITAL M | 1d38 MODIFIER LETTER CAPITAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3b MODIFIER LETTER CAPITAL REVERSED N | 1d3a MODIFIER LETTER CAPITAL N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3d MODIFIER LETTER CAPITAL OU | 1d3c MODIFIER LETTER CAPITAL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d3f MODIFIER LETTER CAPITAL R | 1d3e MODIFIER LETTER CAPITAL P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d41 MODIFIER LETTER CAPITAL U | 1d40 MODIFIER LETTER CAPITAL T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d43 MODIFIER LETTER SMALL A | 1d42 MODIFIER LETTER CAPITAL W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d45 MODIFIER LETTER SMALL ALPHA | 1d44 MODIFIER LETTER SMALL TURNED A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d47 MODIFIER LETTER SMALL B | 1d46 MODIFIER LETTER SMALL TURNED AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d49 MODIFIER LETTER SMALL E | 1d48 MODIFIER LETTER SMALL D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4b MODIFIER LETTER SMALL OPEN E | 1d4a MODIFIER LETTER SMALL SCHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4d MODIFIER LETTER SMALL G | 1d4c MODIFIER LETTER SMALL TURNED OPEN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d4f MODIFIER LETTER SMALL K | 1d4e MODIFIER LETTER SMALL TURNED I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d51 MODIFIER LETTER SMALL ENG | 1d50 MODIFIER LETTER SMALL M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d53 MODIFIER LETTER SMALL OPEN O | 1d52 MODIFIER LETTER SMALL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d55 MODIFIER LETTER SMALL BOTTOM HALF O | 1d54 MODIFIER LETTER SMALL TOP HALF O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d57 MODIFIER LETTER SMALL T | 1d56 MODIFIER LETTER SMALL P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d59 MODIFIER LETTER SMALL SIDEWAYS U | 1d58 MODIFIER LETTER SMALL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5b MODIFIER LETTER SMALL V | 1d5a MODIFIER LETTER SMALL TURNED M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5d MODIFIER LETTER SMALL BETA | 1d5c MODIFIER LETTER SMALL AIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d5f MODIFIER LETTER SMALL DELTA | 1d5e MODIFIER LETTER SMALL GREEK GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d61 MODIFIER LETTER SMALL CHI | 1d60 MODIFIER LETTER SMALL GREEK PHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d63 LATIN SUBSCRIPT SMALL LETTER R | 1d62 LATIN SUBSCRIPT SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d65 LATIN SUBSCRIPT SMALL LETTER V | 1d64 LATIN SUBSCRIPT SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d67 GREEK SUBSCRIPT SMALL LETTER GAMMA | 1d66 GREEK SUBSCRIPT SMALL LETTER BETA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d69 GREEK SUBSCRIPT SMALL LETTER PHI | 1d68 GREEK SUBSCRIPT SMALL LETTER RHO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 1d6b LATIN SMALL LETTER UE | 1d6a GREEK SUBSCRIPT SMALL LETTER CHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d6d LATIN SMALL LETTER D WITH MIDDLE TILDE | 1d6c LATIN SMALL LETTER B WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d6f LATIN SMALL LETTER M WITH MIDDLE TILDE | 1d6e LATIN SMALL LETTER F WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d71 LATIN SMALL LETTER P WITH MIDDLE TILDE | 1d70 LATIN SMALL LETTER N WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d73 LATIN SMALL LETTER R WITH FISHHOOK AND | 1d72 LATIN SMALL LETTER R WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d75 LATIN SMALL LETTER T WITH MIDDLE TILDE | 1d74 LATIN SMALL LETTER S WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d77 LATIN SMALL LETTER TURNED G | 1d76 LATIN SMALL LETTER Z WITH MIDDLE TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 1d79 LATIN SMALL LETTER INSULAR G | 1d78 MODIFIER LETTER CYRILLIC EN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7b LATIN SMALL CAPITAL LETTER I WITH STROK | 1d7a LATIN SMALL LETTER TH WITH STRIKETHROUG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7d LATIN SMALL LETTER P WITH STROKE | 1d7c LATIN SMALL LETTER IOTA WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d7f LATIN SMALL LETTER UPSILON WITH STROKE | 1d7e LATIN SMALL CAPITAL LETTER U WITH STROK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d81 LATIN SMALL LETTER D WITH PALATAL HOOK | 1d80 LATIN SMALL LETTER B WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d83 LATIN SMALL LETTER G WITH PALATAL HOOK | 1d82 LATIN SMALL LETTER F WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d85 LATIN SMALL LETTER L WITH PALATAL HOOK | 1d84 LATIN SMALL LETTER K WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d87 LATIN SMALL LETTER N WITH PALATAL HOOK | 1d86 LATIN SMALL LETTER M WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d89 LATIN SMALL LETTER R WITH PALATAL HOOK | 1d88 LATIN SMALL LETTER P WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8b LATIN SMALL LETTER ESH WITH PALATAL HOO | 1d8a LATIN SMALL LETTER S WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8d LATIN SMALL LETTER X WITH PALATAL HOOK | 1d8c LATIN SMALL LETTER V WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d8f LATIN SMALL LETTER A WITH RETROFLEX HOO | 1d8e LATIN SMALL LETTER Z WITH PALATAL HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d91 LATIN SMALL LETTER D WITH HOOK AND TAIL | 1d90 LATIN SMALL LETTER ALPHA WITH RETROFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d93 LATIN SMALL LETTER OPEN E WITH RETROFLE | 1d92 LATIN SMALL LETTER E WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d95 LATIN SMALL LETTER SCHWA WITH RETROFLEX | 1d94 LATIN SMALL LETTER REVERSED OPEN E WITH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d97 LATIN SMALL LETTER OPEN O WITH RETROFLE | 1d96 LATIN SMALL LETTER I WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1d99 LATIN SMALL LETTER U WITH RETROFLEX HOO | 1d98 LATIN SMALL LETTER ESH WITH RETROFLEX H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 1d9b MODIFIER LETTER SMALL TURNED ALPHA | 1d9a LATIN SMALL LETTER EZH WITH RETROFLEX H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d9d MODIFIER LETTER SMALL C WITH CURL | 1d9c MODIFIER LETTER SMALL C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1d9f MODIFIER LETTER SMALL REVERSED OPEN E | 1d9e MODIFIER LETTER SMALL ETH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da1 MODIFIER LETTER SMALL DOTLESS J WITH ST | 1da0 MODIFIER LETTER SMALL F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da3 MODIFIER LETTER SMALL TURNED H | 1da2 MODIFIER LETTER SMALL SCRIPT G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da5 MODIFIER LETTER SMALL IOTA | 1da4 MODIFIER LETTER SMALL I WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da7 MODIFIER LETTER SMALL CAPITAL I WITH ST | 1da6 MODIFIER LETTER SMALL CAPITAL I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1da9 MODIFIER LETTER SMALL L WITH RETROFLEX | 1da8 MODIFIER LETTER SMALL J WITH CROSSED-TA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dab MODIFIER LETTER SMALL CAPITAL L | 1daa MODIFIER LETTER SMALL L WITH PALATAL HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dad MODIFIER LETTER SMALL TURNED M WITH LON | 1dac MODIFIER LETTER SMALL M WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1daf MODIFIER LETTER SMALL N WITH RETROFLEX | 1dae MODIFIER LETTER SMALL N WITH LEFT HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db1 MODIFIER LETTER SMALL BARRED O | 1db0 MODIFIER LETTER SMALL CAPITAL N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db3 MODIFIER LETTER SMALL S WITH HOOK | 1db2 MODIFIER LETTER SMALL PHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db5 MODIFIER LETTER SMALL T WITH PALATAL HO | 1db4 MODIFIER LETTER SMALL ESH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db7 MODIFIER LETTER SMALL UPSILON | 1db6 MODIFIER LETTER SMALL U BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1db9 MODIFIER LETTER SMALL V WITH HOOK | 1db8 MODIFIER LETTER SMALL CAPITAL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbb MODIFIER LETTER SMALL Z | 1dba MODIFIER LETTER SMALL TURNED V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbd MODIFIER LETTER SMALL Z WITH CURL | 1dbc MODIFIER LETTER SMALL Z WITH RETROFLEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dbf MODIFIER LETTER SMALL THETA | 1dbe MODIFIER LETTER SMALL EZH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc1 COMBINING DOTTED ACUTE ACCENT | 1dc0 COMBINING DOTTED GRAVE ACCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc3 COMBINING SUSPENSION MARK | 1dc2 COMBINING SNAKE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc5 COMBINING GRAVE-MACRON | 1dc4 COMBINING MACRON-ACUTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc7 COMBINING ACUTE-MACRON | 1dc6 COMBINING MACRON-GRAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dc9 COMBINING ACUTE-GRAVE-ACUTE | 1dc8 COMBINING GRAVE-ACUTE-GRAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcb COMBINING BREVE-MACRON | 1dca COMBINING LATIN SMALL LETTER R BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcd COMBINING DOUBLE CIRCUMFLEX ABOVE | 1dcc COMBINING MACRON-BREVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dcf COMBINING ZIGZAG BELOW | 1dce COMBINING OGONEK ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd1 COMBINING UR ABOVE | 1dd0 COMBINING IS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd3 COMBINING LATIN SMALL LETTER FLATTENED | 1dd2 COMBINING US ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd5 COMBINING LATIN SMALL LETTER AO | 1dd4 COMBINING LATIN SMALL LETTER AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd7 COMBINING LATIN SMALL LETTER C CEDILLA | 1dd6 COMBINING LATIN SMALL LETTER AV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dd9 COMBINING LATIN SMALL LETTER ETH | 1dd8 COMBINING LATIN SMALL LETTER INSULAR D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddb COMBINING LATIN LETTER SMALL CAPITAL G | 1dda COMBINING LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddd COMBINING LATIN SMALL LETTER L | 1ddc COMBINING LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1ddf COMBINING LATIN LETTER SMALL CAPITAL M | 1dde COMBINING LATIN LETTER SMALL CAPITAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de1 COMBINING LATIN LETTER SMALL CAPITAL N | 1de0 COMBINING LATIN SMALL LETTER N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de3 COMBINING LATIN SMALL LETTER R ROTUNDA | 1de2 COMBINING LATIN LETTER SMALL CAPITAL R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1de5 COMBINING LATIN SMALL LETTER LONG S | 1de4 COMBINING LATIN SMALL LETTER S */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1de7 (null) | 1de6 COMBINING LATIN SMALL LETTER Z */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1de9 (null) | 1de8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1deb (null) | 1dea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ded (null) | 1dec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1def (null) | 1dee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df1 (null) | 1df0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df3 (null) | 1df2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df5 (null) | 1df4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df7 (null) | 1df6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1df9 (null) | 1df8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1dfb (null) | 1dfa (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dfd COMBINING ALMOST EQUAL TO BELOW | 1dfc COMBINING DOUBLE INVERTED BREVE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1dff COMBINING RIGHT ARROWHEAD AND DOWN ARRO | 1dfe COMBINING LEFT ARROWHEAD ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e01 LATIN SMALL LETTER A WITH RING BELOW | 1e00 LATIN CAPITAL LETTER A WITH RING BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e03 LATIN SMALL LETTER B WITH DOT ABOVE | 1e02 LATIN CAPITAL LETTER B WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e05 LATIN SMALL LETTER B WITH DOT BELOW | 1e04 LATIN CAPITAL LETTER B WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e07 LATIN SMALL LETTER B WITH LINE BELOW | 1e06 LATIN CAPITAL LETTER B WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e09 LATIN SMALL LETTER C WITH CEDILLA AND A | 1e08 LATIN CAPITAL LETTER C WITH CEDILLA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0b LATIN SMALL LETTER D WITH DOT ABOVE | 1e0a LATIN CAPITAL LETTER D WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0d LATIN SMALL LETTER D WITH DOT BELOW | 1e0c LATIN CAPITAL LETTER D WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e0f LATIN SMALL LETTER D WITH LINE BELOW | 1e0e LATIN CAPITAL LETTER D WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e11 LATIN SMALL LETTER D WITH CEDILLA | 1e10 LATIN CAPITAL LETTER D WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e13 LATIN SMALL LETTER D WITH CIRCUMFLEX BE | 1e12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e15 LATIN SMALL LETTER E WITH MACRON AND GR | 1e14 LATIN CAPITAL LETTER E WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e17 LATIN SMALL LETTER E WITH MACRON AND AC | 1e16 LATIN CAPITAL LETTER E WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e19 LATIN SMALL LETTER E WITH CIRCUMFLEX BE | 1e18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1b LATIN SMALL LETTER E WITH TILDE BELOW | 1e1a LATIN CAPITAL LETTER E WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1d LATIN SMALL LETTER E WITH CEDILLA AND B | 1e1c LATIN CAPITAL LETTER E WITH CEDILLA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e1f LATIN SMALL LETTER F WITH DOT ABOVE | 1e1e LATIN CAPITAL LETTER F WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e21 LATIN SMALL LETTER G WITH MACRON | 1e20 LATIN CAPITAL LETTER G WITH MACRON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e23 LATIN SMALL LETTER H WITH DOT ABOVE | 1e22 LATIN CAPITAL LETTER H WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e25 LATIN SMALL LETTER H WITH DOT BELOW | 1e24 LATIN CAPITAL LETTER H WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e27 LATIN SMALL LETTER H WITH DIAERESIS | 1e26 LATIN CAPITAL LETTER H WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e29 LATIN SMALL LETTER H WITH CEDILLA | 1e28 LATIN CAPITAL LETTER H WITH CEDILLA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2b LATIN SMALL LETTER H WITH BREVE BELOW | 1e2a LATIN CAPITAL LETTER H WITH BREVE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2d LATIN SMALL LETTER I WITH TILDE BELOW | 1e2c LATIN CAPITAL LETTER I WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e2f LATIN SMALL LETTER I WITH DIAERESIS AND | 1e2e LATIN CAPITAL LETTER I WITH DIAERESIS A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e31 LATIN SMALL LETTER K WITH ACUTE | 1e30 LATIN CAPITAL LETTER K WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e33 LATIN SMALL LETTER K WITH DOT BELOW | 1e32 LATIN CAPITAL LETTER K WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e35 LATIN SMALL LETTER K WITH LINE BELOW | 1e34 LATIN CAPITAL LETTER K WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e37 LATIN SMALL LETTER L WITH DOT BELOW | 1e36 LATIN CAPITAL LETTER L WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e39 LATIN SMALL LETTER L WITH DOT BELOW AND | 1e38 LATIN CAPITAL LETTER L WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3b LATIN SMALL LETTER L WITH LINE BELOW | 1e3a LATIN CAPITAL LETTER L WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3d LATIN SMALL LETTER L WITH CIRCUMFLEX BE | 1e3c LATIN CAPITAL LETTER L WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e3f LATIN SMALL LETTER M WITH ACUTE | 1e3e LATIN CAPITAL LETTER M WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e41 LATIN SMALL LETTER M WITH DOT ABOVE | 1e40 LATIN CAPITAL LETTER M WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e43 LATIN SMALL LETTER M WITH DOT BELOW | 1e42 LATIN CAPITAL LETTER M WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e45 LATIN SMALL LETTER N WITH DOT ABOVE | 1e44 LATIN CAPITAL LETTER N WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e47 LATIN SMALL LETTER N WITH DOT BELOW | 1e46 LATIN CAPITAL LETTER N WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e49 LATIN SMALL LETTER N WITH LINE BELOW | 1e48 LATIN CAPITAL LETTER N WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4b LATIN SMALL LETTER N WITH CIRCUMFLEX BE | 1e4a LATIN CAPITAL LETTER N WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4d LATIN SMALL LETTER O WITH TILDE AND ACU | 1e4c LATIN CAPITAL LETTER O WITH TILDE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e4f LATIN SMALL LETTER O WITH TILDE AND DIA | 1e4e LATIN CAPITAL LETTER O WITH TILDE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e51 LATIN SMALL LETTER O WITH MACRON AND GR | 1e50 LATIN CAPITAL LETTER O WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e53 LATIN SMALL LETTER O WITH MACRON AND AC | 1e52 LATIN CAPITAL LETTER O WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e55 LATIN SMALL LETTER P WITH ACUTE | 1e54 LATIN CAPITAL LETTER P WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e57 LATIN SMALL LETTER P WITH DOT ABOVE | 1e56 LATIN CAPITAL LETTER P WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e59 LATIN SMALL LETTER R WITH DOT ABOVE | 1e58 LATIN CAPITAL LETTER R WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5b LATIN SMALL LETTER R WITH DOT BELOW | 1e5a LATIN CAPITAL LETTER R WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5d LATIN SMALL LETTER R WITH DOT BELOW AND | 1e5c LATIN CAPITAL LETTER R WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e5f LATIN SMALL LETTER R WITH LINE BELOW | 1e5e LATIN CAPITAL LETTER R WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e61 LATIN SMALL LETTER S WITH DOT ABOVE | 1e60 LATIN CAPITAL LETTER S WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e63 LATIN SMALL LETTER S WITH DOT BELOW | 1e62 LATIN CAPITAL LETTER S WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e65 LATIN SMALL LETTER S WITH ACUTE AND DOT | 1e64 LATIN CAPITAL LETTER S WITH ACUTE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e67 LATIN SMALL LETTER S WITH CARON AND DOT | 1e66 LATIN CAPITAL LETTER S WITH CARON AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e69 LATIN SMALL LETTER S WITH DOT BELOW AND | 1e68 LATIN CAPITAL LETTER S WITH DOT BELOW A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6b LATIN SMALL LETTER T WITH DOT ABOVE | 1e6a LATIN CAPITAL LETTER T WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6d LATIN SMALL LETTER T WITH DOT BELOW | 1e6c LATIN CAPITAL LETTER T WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e6f LATIN SMALL LETTER T WITH LINE BELOW | 1e6e LATIN CAPITAL LETTER T WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e71 LATIN SMALL LETTER T WITH CIRCUMFLEX BE | 1e70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e73 LATIN SMALL LETTER U WITH DIAERESIS BEL | 1e72 LATIN CAPITAL LETTER U WITH DIAERESIS B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e75 LATIN SMALL LETTER U WITH TILDE BELOW | 1e74 LATIN CAPITAL LETTER U WITH TILDE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e77 LATIN SMALL LETTER U WITH CIRCUMFLEX BE | 1e76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e79 LATIN SMALL LETTER U WITH TILDE AND ACU | 1e78 LATIN CAPITAL LETTER U WITH TILDE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7b LATIN SMALL LETTER U WITH MACRON AND DI | 1e7a LATIN CAPITAL LETTER U WITH MACRON AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7d LATIN SMALL LETTER V WITH TILDE | 1e7c LATIN CAPITAL LETTER V WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e7f LATIN SMALL LETTER V WITH DOT BELOW | 1e7e LATIN CAPITAL LETTER V WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e81 LATIN SMALL LETTER W WITH GRAVE | 1e80 LATIN CAPITAL LETTER W WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e83 LATIN SMALL LETTER W WITH ACUTE | 1e82 LATIN CAPITAL LETTER W WITH ACUTE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e85 LATIN SMALL LETTER W WITH DIAERESIS | 1e84 LATIN CAPITAL LETTER W WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e87 LATIN SMALL LETTER W WITH DOT ABOVE | 1e86 LATIN CAPITAL LETTER W WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e89 LATIN SMALL LETTER W WITH DOT BELOW | 1e88 LATIN CAPITAL LETTER W WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8b LATIN SMALL LETTER X WITH DOT ABOVE | 1e8a LATIN CAPITAL LETTER X WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8d LATIN SMALL LETTER X WITH DIAERESIS | 1e8c LATIN CAPITAL LETTER X WITH DIAERESIS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e8f LATIN SMALL LETTER Y WITH DOT ABOVE | 1e8e LATIN CAPITAL LETTER Y WITH DOT ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e91 LATIN SMALL LETTER Z WITH CIRCUMFLEX | 1e90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e93 LATIN SMALL LETTER Z WITH DOT BELOW | 1e92 LATIN CAPITAL LETTER Z WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e95 LATIN SMALL LETTER Z WITH LINE BELOW | 1e94 LATIN CAPITAL LETTER Z WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e97 LATIN SMALL LETTER T WITH DIAERESIS | 1e96 LATIN SMALL LETTER H WITH LINE BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e99 LATIN SMALL LETTER Y WITH RING ABOVE | 1e98 LATIN SMALL LETTER W WITH RING ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e9b LATIN SMALL LETTER LONG S WITH DOT ABOV | 1e9a LATIN SMALL LETTER A WITH RIGHT HALF RI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1e9d LATIN SMALL LETTER LONG S WITH HIGH STR | 1e9c LATIN SMALL LETTER LONG S WITH DIAGONAL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1e9f LATIN SMALL LETTER DELTA | 1e9e LATIN CAPITAL LETTER SHARP S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea1 LATIN SMALL LETTER A WITH DOT BELOW | 1ea0 LATIN CAPITAL LETTER A WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea3 LATIN SMALL LETTER A WITH HOOK ABOVE | 1ea2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea5 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea7 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ea9 LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1ea8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eab LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1eaa LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ead LATIN SMALL LETTER A WITH CIRCUMFLEX AN | 1eac LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eaf LATIN SMALL LETTER A WITH BREVE AND ACU | 1eae LATIN CAPITAL LETTER A WITH BREVE AND A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb1 LATIN SMALL LETTER A WITH BREVE AND GRA | 1eb0 LATIN CAPITAL LETTER A WITH BREVE AND G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb3 LATIN SMALL LETTER A WITH BREVE AND HOO | 1eb2 LATIN CAPITAL LETTER A WITH BREVE AND H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb5 LATIN SMALL LETTER A WITH BREVE AND TIL | 1eb4 LATIN CAPITAL LETTER A WITH BREVE AND T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb7 LATIN SMALL LETTER A WITH BREVE AND DOT | 1eb6 LATIN CAPITAL LETTER A WITH BREVE AND D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eb9 LATIN SMALL LETTER E WITH DOT BELOW | 1eb8 LATIN CAPITAL LETTER E WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebb LATIN SMALL LETTER E WITH HOOK ABOVE | 1eba LATIN CAPITAL LETTER E WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebd LATIN SMALL LETTER E WITH TILDE | 1ebc LATIN CAPITAL LETTER E WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ebf LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ebe LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec1 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec3 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec5 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec7 LATIN SMALL LETTER E WITH CIRCUMFLEX AN | 1ec6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ec9 LATIN SMALL LETTER I WITH HOOK ABOVE | 1ec8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecb LATIN SMALL LETTER I WITH DOT BELOW | 1eca LATIN CAPITAL LETTER I WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecd LATIN SMALL LETTER O WITH DOT BELOW | 1ecc LATIN CAPITAL LETTER O WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ecf LATIN SMALL LETTER O WITH HOOK ABOVE | 1ece LATIN CAPITAL LETTER O WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed1 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed3 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed5 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed7 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ed9 LATIN SMALL LETTER O WITH CIRCUMFLEX AN | 1ed8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edb LATIN SMALL LETTER O WITH HORN AND ACUT | 1eda LATIN CAPITAL LETTER O WITH HORN AND AC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edd LATIN SMALL LETTER O WITH HORN AND GRAV | 1edc LATIN CAPITAL LETTER O WITH HORN AND GR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1edf LATIN SMALL LETTER O WITH HORN AND HOOK | 1ede LATIN CAPITAL LETTER O WITH HORN AND HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee1 LATIN SMALL LETTER O WITH HORN AND TILD | 1ee0 LATIN CAPITAL LETTER O WITH HORN AND TI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee3 LATIN SMALL LETTER O WITH HORN AND DOT | 1ee2 LATIN CAPITAL LETTER O WITH HORN AND DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee5 LATIN SMALL LETTER U WITH DOT BELOW | 1ee4 LATIN CAPITAL LETTER U WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee7 LATIN SMALL LETTER U WITH HOOK ABOVE | 1ee6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ee9 LATIN SMALL LETTER U WITH HORN AND ACUT | 1ee8 LATIN CAPITAL LETTER U WITH HORN AND AC */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eeb LATIN SMALL LETTER U WITH HORN AND GRAV | 1eea LATIN CAPITAL LETTER U WITH HORN AND GR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eed LATIN SMALL LETTER U WITH HORN AND HOOK | 1eec LATIN CAPITAL LETTER U WITH HORN AND HO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eef LATIN SMALL LETTER U WITH HORN AND TILD | 1eee LATIN CAPITAL LETTER U WITH HORN AND TI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef1 LATIN SMALL LETTER U WITH HORN AND DOT | 1ef0 LATIN CAPITAL LETTER U WITH HORN AND DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef3 LATIN SMALL LETTER Y WITH GRAVE | 1ef2 LATIN CAPITAL LETTER Y WITH GRAVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef5 LATIN SMALL LETTER Y WITH DOT BELOW | 1ef4 LATIN CAPITAL LETTER Y WITH DOT BELOW */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef7 LATIN SMALL LETTER Y WITH HOOK ABOVE | 1ef6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1ef9 LATIN SMALL LETTER Y WITH TILDE | 1ef8 LATIN CAPITAL LETTER Y WITH TILDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1efb LATIN SMALL LETTER MIDDLE-WELSH LL | 1efa LATIN CAPITAL LETTER MIDDLE-WELSH LL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1efd LATIN SMALL LETTER MIDDLE-WELSH V | 1efc LATIN CAPITAL LETTER MIDDLE-WELSH V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 1eff LATIN SMALL LETTER Y WITH LOOP | 1efe LATIN CAPITAL LETTER Y WITH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f01 GREEK SMALL LETTER ALPHA WITH DASIA | 1f00 GREEK SMALL LETTER ALPHA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f03 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f02 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f05 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f04 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f07 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f06 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f09 GREEK CAPITAL LETTER ALPHA WITH DASIA | 1f08 GREEK CAPITAL LETTER ALPHA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0b GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0d GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f0f GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f0e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f11 GREEK SMALL LETTER EPSILON WITH DASIA | 1f10 GREEK SMALL LETTER EPSILON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f13 GREEK SMALL LETTER EPSILON WITH DASIA A | 1f12 GREEK SMALL LETTER EPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f15 GREEK SMALL LETTER EPSILON WITH DASIA A | 1f14 GREEK SMALL LETTER EPSILON WITH PSILI A */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f17 (null) | 1f16 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f19 GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f18 GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f1b GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f1a GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f1d GREEK CAPITAL LETTER EPSILON WITH DASIA | 1f1c GREEK CAPITAL LETTER EPSILON WITH PSILI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f1f (null) | 1f1e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f21 GREEK SMALL LETTER ETA WITH DASIA | 1f20 GREEK SMALL LETTER ETA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f23 GREEK SMALL LETTER ETA WITH DASIA AND V | 1f22 GREEK SMALL LETTER ETA WITH PSILI AND V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f25 GREEK SMALL LETTER ETA WITH DASIA AND O | 1f24 GREEK SMALL LETTER ETA WITH PSILI AND O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f27 GREEK SMALL LETTER ETA WITH DASIA AND P | 1f26 GREEK SMALL LETTER ETA WITH PSILI AND P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f29 GREEK CAPITAL LETTER ETA WITH DASIA | 1f28 GREEK CAPITAL LETTER ETA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2b GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2a GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2d GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2c GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f2f GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f2e GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f31 GREEK SMALL LETTER IOTA WITH DASIA | 1f30 GREEK SMALL LETTER IOTA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f33 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f32 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f35 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f34 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f37 GREEK SMALL LETTER IOTA WITH DASIA AND | 1f36 GREEK SMALL LETTER IOTA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f39 GREEK CAPITAL LETTER IOTA WITH DASIA | 1f38 GREEK CAPITAL LETTER IOTA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3b GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3a GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3d GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3c GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f3f GREEK CAPITAL LETTER IOTA WITH DASIA AN | 1f3e GREEK CAPITAL LETTER IOTA WITH PSILI AN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f41 GREEK SMALL LETTER OMICRON WITH DASIA | 1f40 GREEK SMALL LETTER OMICRON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f43 GREEK SMALL LETTER OMICRON WITH DASIA A | 1f42 GREEK SMALL LETTER OMICRON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f45 GREEK SMALL LETTER OMICRON WITH DASIA A | 1f44 GREEK SMALL LETTER OMICRON WITH PSILI A */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f47 (null) | 1f46 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f49 GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f48 GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f4b GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f4a GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f4d GREEK CAPITAL LETTER OMICRON WITH DASIA | 1f4c GREEK CAPITAL LETTER OMICRON WITH PSILI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f4f (null) | 1f4e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f51 GREEK SMALL LETTER UPSILON WITH DASIA | 1f50 GREEK SMALL LETTER UPSILON WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f53 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f52 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f55 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f54 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f57 GREEK SMALL LETTER UPSILON WITH DASIA A | 1f56 GREEK SMALL LETTER UPSILON WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f59 GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f58 (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5b GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5a (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5d GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5c (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UNDEF, /* 1f5f GREEK CAPITAL LETTER UPSILON WITH DASIA | 1f5e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f61 GREEK SMALL LETTER OMEGA WITH DASIA | 1f60 GREEK SMALL LETTER OMEGA WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f63 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f62 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f65 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f64 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1f66 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f69 GREEK CAPITAL LETTER OMEGA WITH DASIA | 1f68 GREEK CAPITAL LETTER OMEGA WITH PSILI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6b GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6a GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6d GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6c GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1f6f GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1f6e GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f71 GREEK SMALL LETTER ALPHA WITH OXIA | 1f70 GREEK SMALL LETTER ALPHA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f73 GREEK SMALL LETTER EPSILON WITH OXIA | 1f72 GREEK SMALL LETTER EPSILON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f75 GREEK SMALL LETTER ETA WITH OXIA | 1f74 GREEK SMALL LETTER ETA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f77 GREEK SMALL LETTER IOTA WITH OXIA | 1f76 GREEK SMALL LETTER IOTA WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f79 GREEK SMALL LETTER OMICRON WITH OXIA | 1f78 GREEK SMALL LETTER OMICRON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f7b GREEK SMALL LETTER UPSILON WITH OXIA | 1f7a GREEK SMALL LETTER UPSILON WITH VARIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f7d GREEK SMALL LETTER OMEGA WITH OXIA | 1f7c GREEK SMALL LETTER OMEGA WITH VARIA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1f7f (null) | 1f7e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f81 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f80 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f83 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f82 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f85 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f84 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f87 GREEK SMALL LETTER ALPHA WITH DASIA AND | 1f86 GREEK SMALL LETTER ALPHA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f89 GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f88 GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8b GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8d GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f8f GREEK CAPITAL LETTER ALPHA WITH DASIA A | 1f8e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f91 GREEK SMALL LETTER ETA WITH DASIA AND Y | 1f90 GREEK SMALL LETTER ETA WITH PSILI AND Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f93 GREEK SMALL LETTER ETA WITH DASIA AND V | 1f92 GREEK SMALL LETTER ETA WITH PSILI AND V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f95 GREEK SMALL LETTER ETA WITH DASIA AND O | 1f94 GREEK SMALL LETTER ETA WITH PSILI AND O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1f97 GREEK SMALL LETTER ETA WITH DASIA AND P | 1f96 GREEK SMALL LETTER ETA WITH PSILI AND P */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f99 GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f98 GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9b GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9a GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9d GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9c GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1f9f GREEK CAPITAL LETTER ETA WITH DASIA AND | 1f9e GREEK CAPITAL LETTER ETA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa1 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa0 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa3 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa2 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa5 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa4 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fa7 GREEK SMALL LETTER OMEGA WITH DASIA AND | 1fa6 GREEK SMALL LETTER OMEGA WITH PSILI AND */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fa9 GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fa8 GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fab GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1faa GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1fad GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fac GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_TITLE << 4) | T3_CTYPE_TITLE, /* 1faf GREEK CAPITAL LETTER OMEGA WITH DASIA A | 1fae GREEK CAPITAL LETTER OMEGA WITH PSILI A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb1 GREEK SMALL LETTER ALPHA WITH MACRON | 1fb0 GREEK SMALL LETTER ALPHA WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAM | 1fb2 GREEK SMALL LETTER ALPHA WITH VARIA AND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1fb5 (null) | 1fb4 GREEK SMALL LETTER ALPHA WITH OXIA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fb7 GREEK SMALL LETTER ALPHA WITH PERISPOME | 1fb6 GREEK SMALL LETTER ALPHA WITH PERISPOME */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fb9 GREEK CAPITAL LETTER ALPHA WITH MACRON | 1fb8 GREEK CAPITAL LETTER ALPHA WITH VRACHY */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fbb GREEK CAPITAL LETTER ALPHA WITH OXIA | 1fba GREEK CAPITAL LETTER ALPHA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1fbd GREEK KORONIS | 1fbc GREEK CAPITAL LETTER ALPHA WITH PROSGEG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 1fbf GREEK PSILI | 1fbe GREEK PROSGEGRAMMENI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fc1 GREEK DIALYTIKA AND PERISPOMENI | 1fc0 GREEK PERISPOMENI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fc3 GREEK SMALL LETTER ETA WITH YPOGEGRAMME | 1fc2 GREEK SMALL LETTER ETA WITH VARIA AND Y */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1fc5 (null) | 1fc4 GREEK SMALL LETTER ETA WITH OXIA AND YP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fc7 GREEK SMALL LETTER ETA WITH PERISPOMENI | 1fc6 GREEK SMALL LETTER ETA WITH PERISPOMENI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fc9 GREEK CAPITAL LETTER EPSILON WITH OXIA | 1fc8 GREEK CAPITAL LETTER EPSILON WITH VARIA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fcb GREEK CAPITAL LETTER ETA WITH OXIA | 1fca GREEK CAPITAL LETTER ETA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1fcd GREEK PSILI AND VARIA | 1fcc GREEK CAPITAL LETTER ETA WITH PROSGEGRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fcf GREEK PSILI AND PERISPOMENI | 1fce GREEK PSILI AND OXIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd1 GREEK SMALL LETTER IOTA WITH MACRON | 1fd0 GREEK SMALL LETTER IOTA WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd3 GREEK SMALL LETTER IOTA WITH DIALYTIKA | 1fd2 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1fd5 (null) | 1fd4 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fd7 GREEK SMALL LETTER IOTA WITH DIALYTIKA | 1fd6 GREEK SMALL LETTER IOTA WITH PERISPOMEN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fd9 GREEK CAPITAL LETTER IOTA WITH MACRON | 1fd8 GREEK CAPITAL LETTER IOTA WITH VRACHY */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fdb GREEK CAPITAL LETTER IOTA WITH OXIA | 1fda GREEK CAPITAL LETTER IOTA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 1fdd GREEK DASIA AND VARIA | 1fdc (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fdf GREEK DASIA AND PERISPOMENI | 1fde GREEK DASIA AND OXIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe1 GREEK SMALL LETTER UPSILON WITH MACRON | 1fe0 GREEK SMALL LETTER UPSILON WITH VRACHY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe3 GREEK SMALL LETTER UPSILON WITH DIALYTI | 1fe2 GREEK SMALL LETTER UPSILON WITH DIALYTI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe5 GREEK SMALL LETTER RHO WITH DASIA | 1fe4 GREEK SMALL LETTER RHO WITH PSILI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1fe7 GREEK SMALL LETTER UPSILON WITH DIALYTI | 1fe6 GREEK SMALL LETTER UPSILON WITH PERISPO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1fe9 GREEK CAPITAL LETTER UPSILON WITH MACRO | 1fe8 GREEK CAPITAL LETTER UPSILON WITH VRACH */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1feb GREEK CAPITAL LETTER UPSILON WITH OXIA | 1fea GREEK CAPITAL LETTER UPSILON WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 1fed GREEK DIALYTIKA AND VARIA | 1fec GREEK CAPITAL LETTER RHO WITH DASIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 1fef GREEK VARIA | 1fee GREEK DIALYTIKA AND OXIA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 1ff1 (null) | 1ff0 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1ff3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAM | 1ff2 GREEK SMALL LETTER OMEGA WITH VARIA AND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 1ff5 (null) | 1ff4 GREEK SMALL LETTER OMEGA WITH OXIA AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 1ff7 GREEK SMALL LETTER OMEGA WITH PERISPOME | 1ff6 GREEK SMALL LETTER OMEGA WITH PERISPOME */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1ff9 GREEK CAPITAL LETTER OMICRON WITH OXIA | 1ff8 GREEK CAPITAL LETTER OMICRON WITH VARIA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 1ffb GREEK CAPITAL LETTER OMEGA WITH OXIA | 1ffa GREEK CAPITAL LETTER OMEGA WITH VARIA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_TITLE, /* 1ffd GREEK OXIA | 1ffc GREEK CAPITAL LETTER OMEGA WITH PROSGEG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 1fff (null) | 1ffe GREEK DASIA */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2001 EM QUAD | 2000 EN QUAD */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2003 EM SPACE | 2002 EN SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2005 FOUR-PER-EM SPACE | 2004 THREE-PER-EM SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2007 FIGURE SPACE | 2006 SIX-PER-EM SPACE */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_SPACE, /* 2009 THIN SPACE | 2008 PUNCTUATION SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_SPACE, /* 200b ZERO WIDTH SPACE | 200a HAIR SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 200d ZERO WIDTH JOINER | 200c ZERO WIDTH NON-JOINER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 200f RIGHT-TO-LEFT MARK | 200e LEFT-TO-RIGHT MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2011 NON-BREAKING HYPHEN | 2010 HYPHEN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2013 EN DASH | 2012 FIGURE DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2015 HORIZONTAL BAR | 2014 EM DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2017 DOUBLE LOW LINE | 2016 DOUBLE VERTICAL LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2019 RIGHT SINGLE QUOTATION MARK | 2018 LEFT SINGLE QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201b SINGLE HIGH-REVERSED-9 QUOTATION MARK | 201a SINGLE LOW-9 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201d RIGHT DOUBLE QUOTATION MARK | 201c LEFT DOUBLE QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 201f DOUBLE HIGH-REVERSED-9 QUOTATION MARK | 201e DOUBLE LOW-9 QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2021 DOUBLE DAGGER | 2020 DAGGER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2023 TRIANGULAR BULLET | 2022 BULLET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2025 TWO DOT LEADER | 2024 ONE DOT LEADER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2027 HYPHENATION POINT | 2026 HORIZONTAL ELLIPSIS */ (T3_CTYPE_VSPAC << 4) | T3_CTYPE_VSPAC, /* 2029 PARAGRAPH SEPARATOR | 2028 LINE SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 202b RIGHT-TO-LEFT EMBEDDING | 202a LEFT-TO-RIGHT EMBEDDING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 202d LEFT-TO-RIGHT OVERRIDE | 202c POP DIRECTIONAL FORMATTING */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_OTHER, /* 202f NARROW NO-BREAK SPACE | 202e RIGHT-TO-LEFT OVERRIDE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2031 PER TEN THOUSAND SIGN | 2030 PER MILLE SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2033 DOUBLE PRIME | 2032 PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2035 REVERSED PRIME | 2034 TRIPLE PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2037 REVERSED TRIPLE PRIME | 2036 REVERSED DOUBLE PRIME */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2039 SINGLE LEFT-POINTING ANGLE QUOTATION MA | 2038 CARET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203b REFERENCE MARK | 203a SINGLE RIGHT-POINTING ANGLE QUOTATION M */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203d INTERROBANG | 203c DOUBLE EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 203f UNDERTIE | 203e OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2041 CARET INSERTION POINT | 2040 CHARACTER TIE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2043 HYPHEN BULLET | 2042 ASTERISM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2045 LEFT SQUARE BRACKET WITH QUILL | 2044 FRACTION SLASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2047 DOUBLE QUESTION MARK | 2046 RIGHT SQUARE BRACKET WITH QUILL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2049 EXCLAMATION QUESTION MARK | 2048 QUESTION EXCLAMATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204b REVERSED PILCROW SIGN | 204a TIRONIAN SIGN ET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204d BLACK RIGHTWARDS BULLET | 204c BLACK LEFTWARDS BULLET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 204f REVERSED SEMICOLON | 204e LOW ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2051 TWO ASTERISKS ALIGNED VERTICALLY | 2050 CLOSE UP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2053 SWUNG DASH | 2052 COMMERCIAL MINUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2055 FLOWER PUNCTUATION MARK | 2054 INVERTED UNDERTIE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2057 QUADRUPLE PRIME | 2056 THREE DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2059 FIVE DOT PUNCTUATION | 2058 FOUR DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 205b FOUR DOT MARK | 205a TWO DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 205d TRICOLON | 205c DOTTED CROSS */ (T3_CTYPE_SPACE << 4) | T3_CTYPE_PUNCT, /* 205f MEDIUM MATHEMATICAL SPACE | 205e VERTICAL FOUR DOTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2061 FUNCTION APPLICATION | 2060 WORD JOINER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2063 INVISIBLE SEPARATOR | 2062 INVISIBLE TIMES */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2065 (null) | 2064 INVISIBLE PLUS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2067 (null) | 2066 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2069 (null) | 2068 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206b ACTIVATE SYMMETRIC SWAPPING | 206a INHIBIT SYMMETRIC SWAPPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206d ACTIVATE ARABIC FORM SHAPING | 206c INHIBIT ARABIC FORM SHAPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 206f NOMINAL DIGIT SHAPES | 206e NATIONAL DIGIT SHAPES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2071 SUPERSCRIPT LATIN SMALL LETTER I | 2070 SUPERSCRIPT ZERO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2073 (null) | 2072 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2075 SUPERSCRIPT FIVE | 2074 SUPERSCRIPT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2077 SUPERSCRIPT SEVEN | 2076 SUPERSCRIPT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2079 SUPERSCRIPT NINE | 2078 SUPERSCRIPT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 207b SUPERSCRIPT MINUS | 207a SUPERSCRIPT PLUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 207d SUPERSCRIPT LEFT PARENTHESIS | 207c SUPERSCRIPT EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 207f SUPERSCRIPT LATIN SMALL LETTER N | 207e SUPERSCRIPT RIGHT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2081 SUBSCRIPT ONE | 2080 SUBSCRIPT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2083 SUBSCRIPT THREE | 2082 SUBSCRIPT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2085 SUBSCRIPT FIVE | 2084 SUBSCRIPT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2087 SUBSCRIPT SEVEN | 2086 SUBSCRIPT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2089 SUBSCRIPT NINE | 2088 SUBSCRIPT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 208b SUBSCRIPT MINUS | 208a SUBSCRIPT PLUS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 208d SUBSCRIPT LEFT PARENTHESIS | 208c SUBSCRIPT EQUALS SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 208f (null) | 208e SUBSCRIPT RIGHT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2091 LATIN SUBSCRIPT SMALL LETTER E | 2090 LATIN SUBSCRIPT SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2093 LATIN SUBSCRIPT SMALL LETTER X | 2092 LATIN SUBSCRIPT SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2095 LATIN SUBSCRIPT SMALL LETTER H | 2094 LATIN SUBSCRIPT SMALL LETTER SCHWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2097 LATIN SUBSCRIPT SMALL LETTER L | 2096 LATIN SUBSCRIPT SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2099 LATIN SUBSCRIPT SMALL LETTER N | 2098 LATIN SUBSCRIPT SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 209b LATIN SUBSCRIPT SMALL LETTER S | 209a LATIN SUBSCRIPT SMALL LETTER P */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 209d (null) | 209c LATIN SUBSCRIPT SMALL LETTER T */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 209f (null) | 209e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a1 COLON SIGN | 20a0 EURO-CURRENCY SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a3 FRENCH FRANC SIGN | 20a2 CRUZEIRO SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a5 MILL SIGN | 20a4 LIRA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a7 PESETA SIGN | 20a6 NAIRA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20a9 WON SIGN | 20a8 RUPEE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ab DONG SIGN | 20aa NEW SHEQEL SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ad KIP SIGN | 20ac EURO SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20af DRACHMA SIGN | 20ae TUGRIK SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b1 PESO SIGN | 20b0 GERMAN PENNY SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b3 AUSTRAL SIGN | 20b2 GUARANI SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b5 CEDI SIGN | 20b4 HRYVNIA SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b7 SPESMILO SIGN | 20b6 LIVRE TOURNOIS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20b9 INDIAN RUPEE SIGN | 20b8 TENGE SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bb (null) | 20ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bd (null) | 20bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20bf (null) | 20be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c1 (null) | 20c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c3 (null) | 20c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c5 (null) | 20c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c7 (null) | 20c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20c9 (null) | 20c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cb (null) | 20ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cd (null) | 20cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20cf (null) | 20ce (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d1 COMBINING RIGHT HARPOON ABOVE | 20d0 COMBINING LEFT HARPOON ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d3 COMBINING SHORT VERTICAL LINE OVERLAY | 20d2 COMBINING LONG VERTICAL LINE OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d5 COMBINING CLOCKWISE ARROW ABOVE | 20d4 COMBINING ANTICLOCKWISE ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d7 COMBINING RIGHT ARROW ABOVE | 20d6 COMBINING LEFT ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20d9 COMBINING CLOCKWISE RING OVERLAY | 20d8 COMBINING RING OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20db COMBINING THREE DOTS ABOVE | 20da COMBINING ANTICLOCKWISE RING OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20dd COMBINING ENCLOSING CIRCLE | 20dc COMBINING FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20df COMBINING ENCLOSING DIAMOND | 20de COMBINING ENCLOSING SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e1 COMBINING LEFT RIGHT ARROW ABOVE | 20e0 COMBINING ENCLOSING CIRCLE BACKSLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e3 COMBINING ENCLOSING KEYCAP | 20e2 COMBINING ENCLOSING SCREEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e5 COMBINING REVERSE SOLIDUS OVERLAY | 20e4 COMBINING ENCLOSING UPWARD POINTING TRI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e7 COMBINING ANNUITY SYMBOL | 20e6 COMBINING DOUBLE VERTICAL STROKE OVERLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20e9 COMBINING WIDE BRIDGE ABOVE | 20e8 COMBINING TRIPLE UNDERDOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20eb COMBINING LONG DOUBLE SOLIDUS OVERLAY | 20ea COMBINING LEFTWARDS ARROW OVERLAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ed COMBINING LEFTWARDS HARPOON WITH BARB D | 20ec COMBINING RIGHTWARDS HARPOON WITH BARB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 20ef COMBINING RIGHT ARROW BELOW | 20ee COMBINING LEFT ARROW BELOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 20f1 (null) | 20f0 COMBINING ASTERISK ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f3 (null) | 20f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f5 (null) | 20f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f7 (null) | 20f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20f9 (null) | 20f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20fb (null) | 20fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20fd (null) | 20fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 20ff (null) | 20fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2101 ADDRESSED TO THE SUBJECT | 2100 ACCOUNT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2103 DEGREE CELSIUS | 2102 DOUBLE-STRUCK CAPITAL C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2105 CARE OF | 2104 CENTRE LINE SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2107 EULER CONSTANT | 2106 CADA UNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2109 DEGREE FAHRENHEIT | 2108 SCRUPLE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 210b SCRIPT CAPITAL H | 210a SCRIPT SMALL G */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 210d DOUBLE-STRUCK CAPITAL H | 210c BLACK-LETTER CAPITAL H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 210f PLANCK CONSTANT OVER TWO PI | 210e PLANCK CONSTANT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2111 BLACK-LETTER CAPITAL I | 2110 SCRIPT CAPITAL I */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2113 SCRIPT SMALL L | 2112 SCRIPT CAPITAL L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2115 DOUBLE-STRUCK CAPITAL N | 2114 L B BAR SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2117 SOUND RECORDING COPYRIGHT | 2116 NUMERO SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2119 DOUBLE-STRUCK CAPITAL P | 2118 SCRIPT CAPITAL P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 211b SCRIPT CAPITAL R | 211a DOUBLE-STRUCK CAPITAL Q */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 211d DOUBLE-STRUCK CAPITAL R | 211c BLACK-LETTER CAPITAL R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 211f RESPONSE | 211e PRESCRIPTION TAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2121 TELEPHONE SIGN | 2120 SERVICE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2123 VERSICLE | 2122 TRADE MARK SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2125 OUNCE SIGN | 2124 DOUBLE-STRUCK CAPITAL Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2127 INVERTED OHM SIGN | 2126 OHM SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UPPER, /* 2129 TURNED GREEK SMALL LETTER IOTA | 2128 BLACK-LETTER CAPITAL Z */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 212b ANGSTROM SIGN | 212a KELVIN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 212d BLACK-LETTER CAPITAL C | 212c SCRIPT CAPITAL B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* 212f SCRIPT SMALL E | 212e ESTIMATED SYMBOL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2131 SCRIPT CAPITAL F | 2130 SCRIPT CAPITAL E */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2133 SCRIPT CAPITAL M | 2132 TURNED CAPITAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* 2135 ALEF SYMBOL | 2134 SCRIPT SMALL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2137 GIMEL SYMBOL | 2136 BET SYMBOL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_ALPHA, /* 2139 INFORMATION SOURCE | 2138 DALET SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 213b FACSIMILE SIGN | 213a ROTATED CAPITAL Q */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 213d DOUBLE-STRUCK SMALL GAMMA | 213c DOUBLE-STRUCK SMALL PI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 213f DOUBLE-STRUCK CAPITAL PI | 213e DOUBLE-STRUCK CAPITAL GAMMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2141 TURNED SANS-SERIF CAPITAL G | 2140 DOUBLE-STRUCK N-ARY SUMMATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2143 REVERSED SANS-SERIF CAPITAL L | 2142 TURNED SANS-SERIF CAPITAL L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2145 DOUBLE-STRUCK ITALIC CAPITAL D | 2144 TURNED SANS-SERIF CAPITAL Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2147 DOUBLE-STRUCK ITALIC SMALL E | 2146 DOUBLE-STRUCK ITALIC SMALL D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2149 DOUBLE-STRUCK ITALIC SMALL J | 2148 DOUBLE-STRUCK ITALIC SMALL I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 214b TURNED AMPERSAND | 214a PROPERTY LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 214d AKTIESELSKAB | 214c PER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 214f SYMBOL FOR SAMARITAN SOURCE | 214e TURNED SMALL F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2151 VULGAR FRACTION ONE NINTH | 2150 VULGAR FRACTION ONE SEVENTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2153 VULGAR FRACTION ONE THIRD | 2152 VULGAR FRACTION ONE TENTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2155 VULGAR FRACTION ONE FIFTH | 2154 VULGAR FRACTION TWO THIRDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2157 VULGAR FRACTION THREE FIFTHS | 2156 VULGAR FRACTION TWO FIFTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2159 VULGAR FRACTION ONE SIXTH | 2158 VULGAR FRACTION FOUR FIFTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215b VULGAR FRACTION ONE EIGHTH | 215a VULGAR FRACTION FIVE SIXTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215d VULGAR FRACTION FIVE EIGHTHS | 215c VULGAR FRACTION THREE EIGHTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 215f FRACTION NUMERATOR ONE | 215e VULGAR FRACTION SEVEN EIGHTHS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2161 ROMAN NUMERAL TWO | 2160 ROMAN NUMERAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2163 ROMAN NUMERAL FOUR | 2162 ROMAN NUMERAL THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2165 ROMAN NUMERAL SIX | 2164 ROMAN NUMERAL FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2167 ROMAN NUMERAL EIGHT | 2166 ROMAN NUMERAL SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2169 ROMAN NUMERAL TEN | 2168 ROMAN NUMERAL NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216b ROMAN NUMERAL TWELVE | 216a ROMAN NUMERAL ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216d ROMAN NUMERAL ONE HUNDRED | 216c ROMAN NUMERAL FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 216f ROMAN NUMERAL ONE THOUSAND | 216e ROMAN NUMERAL FIVE HUNDRED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2171 SMALL ROMAN NUMERAL TWO | 2170 SMALL ROMAN NUMERAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2173 SMALL ROMAN NUMERAL FOUR | 2172 SMALL ROMAN NUMERAL THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2175 SMALL ROMAN NUMERAL SIX | 2174 SMALL ROMAN NUMERAL FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2177 SMALL ROMAN NUMERAL EIGHT | 2176 SMALL ROMAN NUMERAL SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2179 SMALL ROMAN NUMERAL TEN | 2178 SMALL ROMAN NUMERAL NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217b SMALL ROMAN NUMERAL TWELVE | 217a SMALL ROMAN NUMERAL ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217d SMALL ROMAN NUMERAL ONE HUNDRED | 217c SMALL ROMAN NUMERAL FIFTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 217f SMALL ROMAN NUMERAL ONE THOUSAND | 217e SMALL ROMAN NUMERAL FIVE HUNDRED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2181 ROMAN NUMERAL FIVE THOUSAND | 2180 ROMAN NUMERAL ONE THOUSAND C D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2183 ROMAN NUMERAL REVERSED ONE HUNDRED | 2182 ROMAN NUMERAL TEN THOUSAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2185 ROMAN NUMERAL SIX LATE FORM | 2184 LATIN SMALL LETTER REVERSED C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2187 ROMAN NUMERAL FIFTY THOUSAND | 2186 ROMAN NUMERAL FIFTY EARLY FORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2189 VULGAR FRACTION ZERO THIRDS | 2188 ROMAN NUMERAL ONE HUNDRED THOUSAND */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218b (null) | 218a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218d (null) | 218c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 218f (null) | 218e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2191 UPWARDS ARROW | 2190 LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2193 DOWNWARDS ARROW | 2192 RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2195 UP DOWN ARROW | 2194 LEFT RIGHT ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2197 NORTH EAST ARROW | 2196 NORTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2199 SOUTH WEST ARROW | 2198 SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219b RIGHTWARDS ARROW WITH STROKE | 219a LEFTWARDS ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219d RIGHTWARDS WAVE ARROW | 219c LEFTWARDS WAVE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 219f UPWARDS TWO HEADED ARROW | 219e LEFTWARDS TWO HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a1 DOWNWARDS TWO HEADED ARROW | 21a0 RIGHTWARDS TWO HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a3 RIGHTWARDS ARROW WITH TAIL | 21a2 LEFTWARDS ARROW WITH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a5 UPWARDS ARROW FROM BAR | 21a4 LEFTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a7 DOWNWARDS ARROW FROM BAR | 21a6 RIGHTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21a9 LEFTWARDS ARROW WITH HOOK | 21a8 UP DOWN ARROW WITH BASE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ab LEFTWARDS ARROW WITH LOOP | 21aa RIGHTWARDS ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ad LEFT RIGHT WAVE ARROW | 21ac RIGHTWARDS ARROW WITH LOOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21af DOWNWARDS ZIGZAG ARROW | 21ae LEFT RIGHT ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b1 UPWARDS ARROW WITH TIP RIGHTWARDS | 21b0 UPWARDS ARROW WITH TIP LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b3 DOWNWARDS ARROW WITH TIP RIGHTWARDS | 21b2 DOWNWARDS ARROW WITH TIP LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS | 21b4 RIGHTWARDS ARROW WITH CORNER DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b7 CLOCKWISE TOP SEMICIRCLE ARROW | 21b6 ANTICLOCKWISE TOP SEMICIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS | 21b8 NORTH WEST ARROW TO LONG BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bb CLOCKWISE OPEN CIRCLE ARROW | 21ba ANTICLOCKWISE OPEN CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bd LEFTWARDS HARPOON WITH BARB DOWNWARDS | 21bc LEFTWARDS HARPOON WITH BARB UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21bf UPWARDS HARPOON WITH BARB LEFTWARDS | 21be UPWARDS HARPOON WITH BARB RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c1 RIGHTWARDS HARPOON WITH BARB DOWNWARDS | 21c0 RIGHTWARDS HARPOON WITH BARB UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c3 DOWNWARDS HARPOON WITH BARB LEFTWARDS | 21c2 DOWNWARDS HARPOON WITH BARB RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c5 UPWARDS ARROW LEFTWARDS OF DOWNWARDS AR | 21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c7 LEFTWARDS PAIRED ARROWS | 21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21c9 RIGHTWARDS PAIRED ARROWS | 21c8 UPWARDS PAIRED ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cb LEFTWARDS HARPOON OVER RIGHTWARDS HARPO | 21ca DOWNWARDS PAIRED ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cd LEFTWARDS DOUBLE ARROW WITH STROKE | 21cc RIGHTWARDS HARPOON OVER LEFTWARDS HARPO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21cf RIGHTWARDS DOUBLE ARROW WITH STROKE | 21ce LEFT RIGHT DOUBLE ARROW WITH STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d1 UPWARDS DOUBLE ARROW | 21d0 LEFTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d3 DOWNWARDS DOUBLE ARROW | 21d2 RIGHTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d5 UP DOWN DOUBLE ARROW | 21d4 LEFT RIGHT DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d7 NORTH EAST DOUBLE ARROW | 21d6 NORTH WEST DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21d9 SOUTH WEST DOUBLE ARROW | 21d8 SOUTH EAST DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21db RIGHTWARDS TRIPLE ARROW | 21da LEFTWARDS TRIPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21dd RIGHTWARDS SQUIGGLE ARROW | 21dc LEFTWARDS SQUIGGLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21df DOWNWARDS ARROW WITH DOUBLE STROKE | 21de UPWARDS ARROW WITH DOUBLE STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e1 UPWARDS DASHED ARROW | 21e0 LEFTWARDS DASHED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e3 DOWNWARDS DASHED ARROW | 21e2 RIGHTWARDS DASHED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e5 RIGHTWARDS ARROW TO BAR | 21e4 LEFTWARDS ARROW TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e7 UPWARDS WHITE ARROW | 21e6 LEFTWARDS WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21e9 DOWNWARDS WHITE ARROW | 21e8 RIGHTWARDS WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21eb UPWARDS WHITE ARROW ON PEDESTAL | 21ea UPWARDS WHITE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ed UPWARDS WHITE ARROW ON PEDESTAL WITH VE | 21ec UPWARDS WHITE ARROW ON PEDESTAL WITH HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ef UPWARDS WHITE DOUBLE ARROW ON PEDESTAL | 21ee UPWARDS WHITE DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f1 NORTH WEST ARROW TO CORNER | 21f0 RIGHTWARDS WHITE ARROW FROM WALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f3 UP DOWN WHITE ARROW | 21f2 SOUTH EAST ARROW TO CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f5 DOWNWARDS ARROW LEFTWARDS OF UPWARDS AR | 21f4 RIGHT ARROW WITH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f7 LEFTWARDS ARROW WITH VERTICAL STROKE | 21f6 THREE RIGHTWARDS ARROWS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE | 21f8 RIGHTWARDS ARROW WITH VERTICAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21fb RIGHTWARDS ARROW WITH DOUBLE VERTICAL S | 21fa LEFTWARDS ARROW WITH DOUBLE VERTICAL ST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21fd LEFTWARDS OPEN-HEADED ARROW | 21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 21ff LEFT RIGHT OPEN-HEADED ARROW | 21fe RIGHTWARDS OPEN-HEADED ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2201 COMPLEMENT | 2200 FOR ALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2203 THERE EXISTS | 2202 PARTIAL DIFFERENTIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2205 EMPTY SET | 2204 THERE DOES NOT EXIST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2207 NABLA | 2206 INCREMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2209 NOT AN ELEMENT OF | 2208 ELEMENT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220b CONTAINS AS MEMBER | 220a SMALL ELEMENT OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220d SMALL CONTAINS AS MEMBER | 220c DOES NOT CONTAIN AS MEMBER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 220f N-ARY PRODUCT | 220e END OF PROOF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2211 N-ARY SUMMATION | 2210 N-ARY COPRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2213 MINUS-OR-PLUS SIGN | 2212 MINUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2215 DIVISION SLASH | 2214 DOT PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2217 ASTERISK OPERATOR | 2216 SET MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2219 BULLET OPERATOR | 2218 RING OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221b CUBE ROOT | 221a SQUARE ROOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221d PROPORTIONAL TO | 221c FOURTH ROOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 221f RIGHT ANGLE | 221e INFINITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2221 MEASURED ANGLE | 2220 ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2223 DIVIDES | 2222 SPHERICAL ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2225 PARALLEL TO | 2224 DOES NOT DIVIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2227 LOGICAL AND | 2226 NOT PARALLEL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2229 INTERSECTION | 2228 LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222b INTEGRAL | 222a UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222d TRIPLE INTEGRAL | 222c DOUBLE INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 222f SURFACE INTEGRAL | 222e CONTOUR INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2231 CLOCKWISE INTEGRAL | 2230 VOLUME INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2233 ANTICLOCKWISE CONTOUR INTEGRAL | 2232 CLOCKWISE CONTOUR INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2235 BECAUSE | 2234 THEREFORE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2237 PROPORTION | 2236 RATIO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2239 EXCESS | 2238 DOT MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223b HOMOTHETIC | 223a GEOMETRIC PROPORTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223d REVERSED TILDE | 223c TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 223f SINE WAVE | 223e INVERTED LAZY S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2241 NOT TILDE | 2240 WREATH PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2243 ASYMPTOTICALLY EQUAL TO | 2242 MINUS TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2245 APPROXIMATELY EQUAL TO | 2244 NOT ASYMPTOTICALLY EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2247 NEITHER APPROXIMATELY NOR ACTUALLY EQUA | 2246 APPROXIMATELY BUT NOT ACTUALLY EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2249 NOT ALMOST EQUAL TO | 2248 ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224b TRIPLE TILDE | 224a ALMOST EQUAL OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224d EQUIVALENT TO | 224c ALL EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 224f DIFFERENCE BETWEEN | 224e GEOMETRICALLY EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2251 GEOMETRICALLY EQUAL TO | 2250 APPROACHES THE LIMIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2253 IMAGE OF OR APPROXIMATELY EQUAL TO | 2252 APPROXIMATELY EQUAL TO OR THE IMAGE OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2255 EQUALS COLON | 2254 COLON EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2257 RING EQUAL TO | 2256 RING IN EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2259 ESTIMATES | 2258 CORRESPONDS TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225b STAR EQUALS | 225a EQUIANGULAR TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225d EQUAL TO BY DEFINITION | 225c DELTA EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 225f QUESTIONED EQUAL TO | 225e MEASURED BY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2261 IDENTICAL TO | 2260 NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2263 STRICTLY EQUIVALENT TO | 2262 NOT IDENTICAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2265 GREATER-THAN OR EQUAL TO | 2264 LESS-THAN OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2267 GREATER-THAN OVER EQUAL TO | 2266 LESS-THAN OVER EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2269 GREATER-THAN BUT NOT EQUAL TO | 2268 LESS-THAN BUT NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226b MUCH GREATER-THAN | 226a MUCH LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226d NOT EQUIVALENT TO | 226c BETWEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 226f NOT GREATER-THAN | 226e NOT LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2271 NEITHER GREATER-THAN NOR EQUAL TO | 2270 NEITHER LESS-THAN NOR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2273 GREATER-THAN OR EQUIVALENT TO | 2272 LESS-THAN OR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2275 NEITHER GREATER-THAN NOR EQUIVALENT TO | 2274 NEITHER LESS-THAN NOR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2277 GREATER-THAN OR LESS-THAN | 2276 LESS-THAN OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2279 NEITHER GREATER-THAN NOR LESS-THAN | 2278 NEITHER LESS-THAN NOR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227b SUCCEEDS | 227a PRECEDES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227d SUCCEEDS OR EQUAL TO | 227c PRECEDES OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 227f SUCCEEDS OR EQUIVALENT TO | 227e PRECEDES OR EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2281 DOES NOT SUCCEED | 2280 DOES NOT PRECEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2283 SUPERSET OF | 2282 SUBSET OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2285 NOT A SUPERSET OF | 2284 NOT A SUBSET OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2287 SUPERSET OF OR EQUAL TO | 2286 SUBSET OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2289 NEITHER A SUPERSET OF NOR EQUAL TO | 2288 NEITHER A SUBSET OF NOR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228b SUPERSET OF WITH NOT EQUAL TO | 228a SUBSET OF WITH NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228d MULTISET MULTIPLICATION | 228c MULTISET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 228f SQUARE IMAGE OF | 228e MULTISET UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2291 SQUARE IMAGE OF OR EQUAL TO | 2290 SQUARE ORIGINAL OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2293 SQUARE CAP | 2292 SQUARE ORIGINAL OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2295 CIRCLED PLUS | 2294 SQUARE CUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2297 CIRCLED TIMES | 2296 CIRCLED MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2299 CIRCLED DOT OPERATOR | 2298 CIRCLED DIVISION SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229b CIRCLED ASTERISK OPERATOR | 229a CIRCLED RING OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229d CIRCLED DASH | 229c CIRCLED EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 229f SQUARED MINUS | 229e SQUARED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a1 SQUARED DOT OPERATOR | 22a0 SQUARED TIMES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a3 LEFT TACK | 22a2 RIGHT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a5 UP TACK | 22a4 DOWN TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a7 MODELS | 22a6 ASSERTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22a9 FORCES | 22a8 TRUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ab DOUBLE VERTICAL BAR DOUBLE RIGHT TURNST | 22aa TRIPLE VERTICAL BAR RIGHT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ad NOT TRUE | 22ac DOES NOT PROVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22af NEGATED DOUBLE VERTICAL BAR DOUBLE RIGH | 22ae DOES NOT FORCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b1 SUCCEEDS UNDER RELATION | 22b0 PRECEDES UNDER RELATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b3 CONTAINS AS NORMAL SUBGROUP | 22b2 NORMAL SUBGROUP OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b5 CONTAINS AS NORMAL SUBGROUP OR EQUAL TO | 22b4 NORMAL SUBGROUP OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b7 IMAGE OF | 22b6 ORIGINAL OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22b9 HERMITIAN CONJUGATE MATRIX | 22b8 MULTIMAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bb XOR | 22ba INTERCALATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bd NOR | 22bc NAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22bf RIGHT TRIANGLE | 22be RIGHT ANGLE WITH ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c1 N-ARY LOGICAL OR | 22c0 N-ARY LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c3 N-ARY UNION | 22c2 N-ARY INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c5 DOT OPERATOR | 22c4 DIAMOND OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c7 DIVISION TIMES | 22c6 STAR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22c9 LEFT NORMAL FACTOR SEMIDIRECT PRODUCT | 22c8 BOWTIE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cb LEFT SEMIDIRECT PRODUCT | 22ca RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cd REVERSED TILDE EQUALS | 22cc RIGHT SEMIDIRECT PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22cf CURLY LOGICAL AND | 22ce CURLY LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d1 DOUBLE SUPERSET | 22d0 DOUBLE SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d3 DOUBLE UNION | 22d2 DOUBLE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d5 EQUAL AND PARALLEL TO | 22d4 PITCHFORK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d7 GREATER-THAN WITH DOT | 22d6 LESS-THAN WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22d9 VERY MUCH GREATER-THAN | 22d8 VERY MUCH LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22db GREATER-THAN EQUAL TO OR LESS-THAN | 22da LESS-THAN EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22dd EQUAL TO OR GREATER-THAN | 22dc EQUAL TO OR LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22df EQUAL TO OR SUCCEEDS | 22de EQUAL TO OR PRECEDES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e1 DOES NOT SUCCEED OR EQUAL | 22e0 DOES NOT PRECEDE OR EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e3 NOT SQUARE ORIGINAL OF OR EQUAL TO | 22e2 NOT SQUARE IMAGE OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e5 SQUARE ORIGINAL OF OR NOT EQUAL TO | 22e4 SQUARE IMAGE OF OR NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e7 GREATER-THAN BUT NOT EQUIVALENT TO | 22e6 LESS-THAN BUT NOT EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22e9 SUCCEEDS BUT NOT EQUIVALENT TO | 22e8 PRECEDES BUT NOT EQUIVALENT TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22eb DOES NOT CONTAIN AS NORMAL SUBGROUP | 22ea NOT NORMAL SUBGROUP OF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ed DOES NOT CONTAIN AS NORMAL SUBGROUP OR | 22ec NOT NORMAL SUBGROUP OF OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ef MIDLINE HORIZONTAL ELLIPSIS | 22ee VERTICAL ELLIPSIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f1 DOWN RIGHT DIAGONAL ELLIPSIS | 22f0 UP RIGHT DIAGONAL ELLIPSIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f3 ELEMENT OF WITH VERTICAL BAR AT END OF | 22f2 ELEMENT OF WITH LONG HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f5 ELEMENT OF WITH DOT ABOVE | 22f4 SMALL ELEMENT OF WITH VERTICAL BAR AT E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f7 SMALL ELEMENT OF WITH OVERBAR | 22f6 ELEMENT OF WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22f9 ELEMENT OF WITH TWO HORIZONTAL STROKES | 22f8 ELEMENT OF WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22fb CONTAINS WITH VERTICAL BAR AT END OF HO | 22fa CONTAINS WITH LONG HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22fd CONTAINS WITH OVERBAR | 22fc SMALL CONTAINS WITH VERTICAL BAR AT END */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 22ff Z NOTATION BAG MEMBERSHIP | 22fe SMALL CONTAINS WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2301 ELECTRIC ARROW | 2300 DIAMETER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2303 UP ARROWHEAD | 2302 HOUSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2305 PROJECTIVE | 2304 DOWN ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2307 WAVY LINE | 2306 PERSPECTIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2309 RIGHT CEILING | 2308 LEFT CEILING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230b RIGHT FLOOR | 230a LEFT FLOOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230d BOTTOM LEFT CROP | 230c BOTTOM RIGHT CROP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 230f TOP LEFT CROP | 230e TOP RIGHT CROP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2311 SQUARE LOZENGE | 2310 REVERSED NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2313 SEGMENT | 2312 ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2315 TELEPHONE RECORDER | 2314 SECTOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2317 VIEWDATA SQUARE | 2316 POSITION INDICATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2319 TURNED NOT SIGN | 2318 PLACE OF INTEREST SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231b HOURGLASS | 231a WATCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231d TOP RIGHT CORNER | 231c TOP LEFT CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 231f BOTTOM RIGHT CORNER | 231e BOTTOM LEFT CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2321 BOTTOM HALF INTEGRAL | 2320 TOP HALF INTEGRAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2323 SMILE | 2322 FROWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2325 OPTION KEY | 2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2327 X IN A RECTANGLE BOX | 2326 ERASE TO THE RIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2329 LEFT-POINTING ANGLE BRACKET | 2328 KEYBOARD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 232b ERASE TO THE LEFT | 232a RIGHT-POINTING ANGLE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 232d CYLINDRICITY | 232c BENZENE RING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 232f SYMMETRY | 232e ALL AROUND-PROFILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2331 DIMENSION ORIGIN | 2330 TOTAL RUNOUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2333 SLOPE | 2332 CONICAL TAPER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2335 COUNTERSINK | 2334 COUNTERBORE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2337 APL FUNCTIONAL SYMBOL SQUISH QUAD | 2336 APL FUNCTIONAL SYMBOL I-BEAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2339 APL FUNCTIONAL SYMBOL QUAD DIVIDE | 2338 APL FUNCTIONAL SYMBOL QUAD EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233b APL FUNCTIONAL SYMBOL QUAD JOT | 233a APL FUNCTIONAL SYMBOL QUAD DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233d APL FUNCTIONAL SYMBOL CIRCLE STILE | 233c APL FUNCTIONAL SYMBOL QUAD CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 233f APL FUNCTIONAL SYMBOL SLASH BAR | 233e APL FUNCTIONAL SYMBOL CIRCLE JOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2341 APL FUNCTIONAL SYMBOL QUAD SLASH | 2340 APL FUNCTIONAL SYMBOL BACKSLASH BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2343 APL FUNCTIONAL SYMBOL QUAD LESS-THAN | 2342 APL FUNCTIONAL SYMBOL QUAD BACKSLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2345 APL FUNCTIONAL SYMBOL LEFTWARDS VANE | 2344 APL FUNCTIONAL SYMBOL QUAD GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2347 APL FUNCTIONAL SYMBOL QUAD LEFTWARDS AR | 2346 APL FUNCTIONAL SYMBOL RIGHTWARDS VANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2349 APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH | 2348 APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234b APL FUNCTIONAL SYMBOL DELTA STILE | 234a APL FUNCTIONAL SYMBOL DOWN TACK UNDERBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234d APL FUNCTIONAL SYMBOL QUAD DELTA | 234c APL FUNCTIONAL SYMBOL QUAD DOWN CARET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 234f APL FUNCTIONAL SYMBOL UPWARDS VANE | 234e APL FUNCTIONAL SYMBOL DOWN TACK JOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2351 APL FUNCTIONAL SYMBOL UP TACK OVERBAR | 2350 APL FUNCTIONAL SYMBOL QUAD UPWARDS ARRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2353 APL FUNCTIONAL SYMBOL QUAD UP CARET | 2352 APL FUNCTIONAL SYMBOL DEL STILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2355 APL FUNCTIONAL SYMBOL UP TACK JOT | 2354 APL FUNCTIONAL SYMBOL QUAD DEL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2357 APL FUNCTIONAL SYMBOL QUAD DOWNWARDS AR | 2356 APL FUNCTIONAL SYMBOL DOWNWARDS VANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2359 APL FUNCTIONAL SYMBOL DELTA UNDERBAR | 2358 APL FUNCTIONAL SYMBOL QUOTE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235b APL FUNCTIONAL SYMBOL JOT UNDERBAR | 235a APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235d APL FUNCTIONAL SYMBOL UP SHOE JOT | 235c APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 235f APL FUNCTIONAL SYMBOL CIRCLE STAR | 235e APL FUNCTIONAL SYMBOL QUOTE QUAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2361 APL FUNCTIONAL SYMBOL UP TACK DIAERESIS | 2360 APL FUNCTIONAL SYMBOL QUAD COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2363 APL FUNCTIONAL SYMBOL STAR DIAERESIS | 2362 APL FUNCTIONAL SYMBOL DEL DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2365 APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS | 2364 APL FUNCTIONAL SYMBOL JOT DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2367 APL FUNCTIONAL SYMBOL LEFT SHOE STILE | 2366 APL FUNCTIONAL SYMBOL DOWN SHOE STILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2369 APL FUNCTIONAL SYMBOL GREATER-THAN DIAE | 2368 APL FUNCTIONAL SYMBOL TILDE DIAERESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236b APL FUNCTIONAL SYMBOL DEL TILDE | 236a APL FUNCTIONAL SYMBOL COMMA BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236d APL FUNCTIONAL SYMBOL STILE TILDE | 236c APL FUNCTIONAL SYMBOL ZILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 236f APL FUNCTIONAL SYMBOL QUAD NOT EQUAL | 236e APL FUNCTIONAL SYMBOL SEMICOLON UNDERBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2371 APL FUNCTIONAL SYMBOL DOWN CARET TILDE | 2370 APL FUNCTIONAL SYMBOL QUAD QUESTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2373 APL FUNCTIONAL SYMBOL IOTA | 2372 APL FUNCTIONAL SYMBOL UP CARET TILDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2375 APL FUNCTIONAL SYMBOL OMEGA | 2374 APL FUNCTIONAL SYMBOL RHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2377 APL FUNCTIONAL SYMBOL EPSILON UNDERBAR | 2376 APL FUNCTIONAL SYMBOL ALPHA UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2379 APL FUNCTIONAL SYMBOL OMEGA UNDERBAR | 2378 APL FUNCTIONAL SYMBOL IOTA UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237b NOT CHECK MARK | 237a APL FUNCTIONAL SYMBOL ALPHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237d SHOULDERED OPEN BOX | 237c RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 237f VERTICAL LINE WITH MIDDLE DOT | 237e BELL SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2381 CONTINUOUS UNDERLINE SYMBOL | 2380 INSERTION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2383 EMPHASIS SYMBOL | 2382 DISCONTINUOUS UNDERLINE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2385 WHITE SQUARE WITH CENTRE VERTICAL LINE | 2384 COMPOSITION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2387 ALTERNATIVE KEY SYMBOL | 2386 ENTER SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2389 CIRCLED HORIZONTAL BAR WITH NOTCH | 2388 HELM SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238b BROKEN CIRCLE WITH NORTHWEST ARROW | 238a CIRCLED TRIANGLE DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238d MONOSTABLE SYMBOL | 238c UNDO SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 238f OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL | 238e HYSTERESIS SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2391 PASSIVE-PULL-DOWN-OUTPUT SYMBOL | 2390 OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2393 DIRECT CURRENT SYMBOL FORM TWO | 2392 PASSIVE-PULL-UP-OUTPUT SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2395 APL FUNCTIONAL SYMBOL QUAD | 2394 SOFTWARE-FUNCTION SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2397 PREVIOUS PAGE | 2396 DECIMAL SEPARATOR KEY SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2399 PRINT SCREEN SYMBOL | 2398 NEXT PAGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239b LEFT PARENTHESIS UPPER HOOK | 239a CLEAR SCREEN SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239d LEFT PARENTHESIS LOWER HOOK | 239c LEFT PARENTHESIS EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 239f RIGHT PARENTHESIS EXTENSION | 239e RIGHT PARENTHESIS UPPER HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a1 LEFT SQUARE BRACKET UPPER CORNER | 23a0 RIGHT PARENTHESIS LOWER HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a3 LEFT SQUARE BRACKET LOWER CORNER | 23a2 LEFT SQUARE BRACKET EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a5 RIGHT SQUARE BRACKET EXTENSION | 23a4 RIGHT SQUARE BRACKET UPPER CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a7 LEFT CURLY BRACKET UPPER HOOK | 23a6 RIGHT SQUARE BRACKET LOWER CORNER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23a9 LEFT CURLY BRACKET LOWER HOOK | 23a8 LEFT CURLY BRACKET MIDDLE PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ab RIGHT CURLY BRACKET UPPER HOOK | 23aa CURLY BRACKET EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ad RIGHT CURLY BRACKET LOWER HOOK | 23ac RIGHT CURLY BRACKET MIDDLE PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23af HORIZONTAL LINE EXTENSION | 23ae INTEGRAL EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b1 UPPER RIGHT OR LOWER LEFT CURLY BRACKET | 23b0 UPPER LEFT OR LOWER RIGHT CURLY BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b3 SUMMATION BOTTOM | 23b2 SUMMATION TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b5 BOTTOM SQUARE BRACKET | 23b4 TOP SQUARE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b7 RADICAL SYMBOL BOTTOM | 23b6 BOTTOM SQUARE BRACKET OVER TOP SQUARE B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23b9 RIGHT VERTICAL BOX LINE | 23b8 LEFT VERTICAL BOX LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bb HORIZONTAL SCAN LINE-3 | 23ba HORIZONTAL SCAN LINE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bd HORIZONTAL SCAN LINE-9 | 23bc HORIZONTAL SCAN LINE-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23bf DENTISTRY SYMBOL LIGHT VERTICAL AND BOT | 23be DENTISTRY SYMBOL LIGHT VERTICAL AND TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c1 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c0 DENTISTRY SYMBOL LIGHT VERTICAL WITH CI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c3 DENTISTRY SYMBOL LIGHT VERTICAL WITH TR | 23c2 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c5 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA | 23c4 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c7 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c6 DENTISTRY SYMBOL LIGHT VERTICAL AND WAV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23c9 DENTISTRY SYMBOL LIGHT DOWN AND HORIZON | 23c8 DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cb DENTISTRY SYMBOL LIGHT VERTICAL AND TOP | 23ca DENTISTRY SYMBOL LIGHT UP AND HORIZONTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cd SQUARE FOOT | 23cc DENTISTRY SYMBOL LIGHT VERTICAL AND BOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23cf EJECT SYMBOL | 23ce RETURN SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d1 METRICAL BREVE | 23d0 VERTICAL LINE EXTENSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d3 METRICAL SHORT OVER LONG | 23d2 METRICAL LONG OVER SHORT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d5 METRICAL TWO SHORTS OVER LONG | 23d4 METRICAL LONG OVER TWO SHORTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d7 METRICAL TRISEME | 23d6 METRICAL TWO SHORTS JOINED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23d9 METRICAL PENTASEME | 23d8 METRICAL TETRASEME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23db FUSE | 23da EARTH GROUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23dd BOTTOM PARENTHESIS | 23dc TOP PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23df BOTTOM CURLY BRACKET | 23de TOP CURLY BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e1 BOTTOM TORTOISE SHELL BRACKET | 23e0 TOP TORTOISE SHELL BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e3 BENZENE RING WITH CIRCLE | 23e2 WHITE TRAPEZIUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e5 FLATNESS | 23e4 STRAIGHTNESS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e7 ELECTRICAL INTERSECTION | 23e6 AC CURRENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23e9 BLACK RIGHT-POINTING DOUBLE TRIANGLE | 23e8 DECIMAL EXPONENT SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23eb BLACK UP-POINTING DOUBLE TRIANGLE | 23ea BLACK LEFT-POINTING DOUBLE TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ed BLACK RIGHT-POINTING DOUBLE TRIANGLE WI | 23ec BLACK DOWN-POINTING DOUBLE TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23ef BLACK RIGHT-POINTING TRIANGLE WITH DOUB | 23ee BLACK LEFT-POINTING DOUBLE TRIANGLE WIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23f1 STOPWATCH | 23f0 ALARM CLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 23f3 HOURGLASS WITH FLOWING SAND | 23f2 TIMER CLOCK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f5 (null) | 23f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f7 (null) | 23f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23f9 (null) | 23f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23fb (null) | 23fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23fd (null) | 23fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 23ff (null) | 23fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2401 SYMBOL FOR START OF HEADING | 2400 SYMBOL FOR NULL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2403 SYMBOL FOR END OF TEXT | 2402 SYMBOL FOR START OF TEXT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2405 SYMBOL FOR ENQUIRY | 2404 SYMBOL FOR END OF TRANSMISSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2407 SYMBOL FOR BELL | 2406 SYMBOL FOR ACKNOWLEDGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2409 SYMBOL FOR HORIZONTAL TABULATION | 2408 SYMBOL FOR BACKSPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240b SYMBOL FOR VERTICAL TABULATION | 240a SYMBOL FOR LINE FEED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240d SYMBOL FOR CARRIAGE RETURN | 240c SYMBOL FOR FORM FEED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 240f SYMBOL FOR SHIFT IN | 240e SYMBOL FOR SHIFT OUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2411 SYMBOL FOR DEVICE CONTROL ONE | 2410 SYMBOL FOR DATA LINK ESCAPE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2413 SYMBOL FOR DEVICE CONTROL THREE | 2412 SYMBOL FOR DEVICE CONTROL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2415 SYMBOL FOR NEGATIVE ACKNOWLEDGE | 2414 SYMBOL FOR DEVICE CONTROL FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2417 SYMBOL FOR END OF TRANSMISSION BLOCK | 2416 SYMBOL FOR SYNCHRONOUS IDLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2419 SYMBOL FOR END OF MEDIUM | 2418 SYMBOL FOR CANCEL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241b SYMBOL FOR ESCAPE | 241a SYMBOL FOR SUBSTITUTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241d SYMBOL FOR GROUP SEPARATOR | 241c SYMBOL FOR FILE SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 241f SYMBOL FOR UNIT SEPARATOR | 241e SYMBOL FOR RECORD SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2421 SYMBOL FOR DELETE | 2420 SYMBOL FOR SPACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2423 OPEN BOX | 2422 BLANK SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2425 SYMBOL FOR DELETE FORM TWO | 2424 SYMBOL FOR NEWLINE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2427 (null) | 2426 SYMBOL FOR SUBSTITUTE FORM TWO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2429 (null) | 2428 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242b (null) | 242a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242d (null) | 242c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 242f (null) | 242e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2431 (null) | 2430 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2433 (null) | 2432 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2435 (null) | 2434 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2437 (null) | 2436 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2439 (null) | 2438 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243b (null) | 243a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243d (null) | 243c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 243f (null) | 243e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2441 OCR CHAIR | 2440 OCR HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2443 OCR INVERTED FORK | 2442 OCR FORK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2445 OCR BOW TIE | 2444 OCR BELT BUCKLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2447 OCR AMOUNT OF CHECK | 2446 OCR BRANCH BANK IDENTIFICATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2449 OCR CUSTOMER ACCOUNT NUMBER | 2448 OCR DASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 244b (null) | 244a OCR DOUBLE BACKSLASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 244d (null) | 244c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 244f (null) | 244e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2451 (null) | 2450 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2453 (null) | 2452 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2455 (null) | 2454 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2457 (null) | 2456 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2459 (null) | 2458 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245b (null) | 245a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245d (null) | 245c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 245f (null) | 245e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2461 CIRCLED DIGIT TWO | 2460 CIRCLED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2463 CIRCLED DIGIT FOUR | 2462 CIRCLED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2465 CIRCLED DIGIT SIX | 2464 CIRCLED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2467 CIRCLED DIGIT EIGHT | 2466 CIRCLED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2469 CIRCLED NUMBER TEN | 2468 CIRCLED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246b CIRCLED NUMBER TWELVE | 246a CIRCLED NUMBER ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246d CIRCLED NUMBER FOURTEEN | 246c CIRCLED NUMBER THIRTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 246f CIRCLED NUMBER SIXTEEN | 246e CIRCLED NUMBER FIFTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2471 CIRCLED NUMBER EIGHTEEN | 2470 CIRCLED NUMBER SEVENTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2473 CIRCLED NUMBER TWENTY | 2472 CIRCLED NUMBER NINETEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2475 PARENTHESIZED DIGIT TWO | 2474 PARENTHESIZED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2477 PARENTHESIZED DIGIT FOUR | 2476 PARENTHESIZED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2479 PARENTHESIZED DIGIT SIX | 2478 PARENTHESIZED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247b PARENTHESIZED DIGIT EIGHT | 247a PARENTHESIZED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247d PARENTHESIZED NUMBER TEN | 247c PARENTHESIZED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 247f PARENTHESIZED NUMBER TWELVE | 247e PARENTHESIZED NUMBER ELEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2481 PARENTHESIZED NUMBER FOURTEEN | 2480 PARENTHESIZED NUMBER THIRTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2483 PARENTHESIZED NUMBER SIXTEEN | 2482 PARENTHESIZED NUMBER FIFTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2485 PARENTHESIZED NUMBER EIGHTEEN | 2484 PARENTHESIZED NUMBER SEVENTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2487 PARENTHESIZED NUMBER TWENTY | 2486 PARENTHESIZED NUMBER NINETEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2489 DIGIT TWO FULL STOP | 2488 DIGIT ONE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248b DIGIT FOUR FULL STOP | 248a DIGIT THREE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248d DIGIT SIX FULL STOP | 248c DIGIT FIVE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 248f DIGIT EIGHT FULL STOP | 248e DIGIT SEVEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2491 NUMBER TEN FULL STOP | 2490 DIGIT NINE FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2493 NUMBER TWELVE FULL STOP | 2492 NUMBER ELEVEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2495 NUMBER FOURTEEN FULL STOP | 2494 NUMBER THIRTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2497 NUMBER SIXTEEN FULL STOP | 2496 NUMBER FIFTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2499 NUMBER EIGHTEEN FULL STOP | 2498 NUMBER SEVENTEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249b NUMBER TWENTY FULL STOP | 249a NUMBER NINETEEN FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249d PARENTHESIZED LATIN SMALL LETTER B | 249c PARENTHESIZED LATIN SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 249f PARENTHESIZED LATIN SMALL LETTER D | 249e PARENTHESIZED LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a1 PARENTHESIZED LATIN SMALL LETTER F | 24a0 PARENTHESIZED LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a3 PARENTHESIZED LATIN SMALL LETTER H | 24a2 PARENTHESIZED LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a5 PARENTHESIZED LATIN SMALL LETTER J | 24a4 PARENTHESIZED LATIN SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a7 PARENTHESIZED LATIN SMALL LETTER L | 24a6 PARENTHESIZED LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24a9 PARENTHESIZED LATIN SMALL LETTER N | 24a8 PARENTHESIZED LATIN SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ab PARENTHESIZED LATIN SMALL LETTER P | 24aa PARENTHESIZED LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ad PARENTHESIZED LATIN SMALL LETTER R | 24ac PARENTHESIZED LATIN SMALL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24af PARENTHESIZED LATIN SMALL LETTER T | 24ae PARENTHESIZED LATIN SMALL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b1 PARENTHESIZED LATIN SMALL LETTER V | 24b0 PARENTHESIZED LATIN SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b3 PARENTHESIZED LATIN SMALL LETTER X | 24b2 PARENTHESIZED LATIN SMALL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b5 PARENTHESIZED LATIN SMALL LETTER Z | 24b4 PARENTHESIZED LATIN SMALL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b7 CIRCLED LATIN CAPITAL LETTER B | 24b6 CIRCLED LATIN CAPITAL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24b9 CIRCLED LATIN CAPITAL LETTER D | 24b8 CIRCLED LATIN CAPITAL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bb CIRCLED LATIN CAPITAL LETTER F | 24ba CIRCLED LATIN CAPITAL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bd CIRCLED LATIN CAPITAL LETTER H | 24bc CIRCLED LATIN CAPITAL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24bf CIRCLED LATIN CAPITAL LETTER J | 24be CIRCLED LATIN CAPITAL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c1 CIRCLED LATIN CAPITAL LETTER L | 24c0 CIRCLED LATIN CAPITAL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c3 CIRCLED LATIN CAPITAL LETTER N | 24c2 CIRCLED LATIN CAPITAL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c5 CIRCLED LATIN CAPITAL LETTER P | 24c4 CIRCLED LATIN CAPITAL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c7 CIRCLED LATIN CAPITAL LETTER R | 24c6 CIRCLED LATIN CAPITAL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24c9 CIRCLED LATIN CAPITAL LETTER T | 24c8 CIRCLED LATIN CAPITAL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cb CIRCLED LATIN CAPITAL LETTER V | 24ca CIRCLED LATIN CAPITAL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cd CIRCLED LATIN CAPITAL LETTER X | 24cc CIRCLED LATIN CAPITAL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24cf CIRCLED LATIN CAPITAL LETTER Z | 24ce CIRCLED LATIN CAPITAL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d1 CIRCLED LATIN SMALL LETTER B | 24d0 CIRCLED LATIN SMALL LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d3 CIRCLED LATIN SMALL LETTER D | 24d2 CIRCLED LATIN SMALL LETTER C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d5 CIRCLED LATIN SMALL LETTER F | 24d4 CIRCLED LATIN SMALL LETTER E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d7 CIRCLED LATIN SMALL LETTER H | 24d6 CIRCLED LATIN SMALL LETTER G */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24d9 CIRCLED LATIN SMALL LETTER J | 24d8 CIRCLED LATIN SMALL LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24db CIRCLED LATIN SMALL LETTER L | 24da CIRCLED LATIN SMALL LETTER K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24dd CIRCLED LATIN SMALL LETTER N | 24dc CIRCLED LATIN SMALL LETTER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24df CIRCLED LATIN SMALL LETTER P | 24de CIRCLED LATIN SMALL LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e1 CIRCLED LATIN SMALL LETTER R | 24e0 CIRCLED LATIN SMALL LETTER Q */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e3 CIRCLED LATIN SMALL LETTER T | 24e2 CIRCLED LATIN SMALL LETTER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e5 CIRCLED LATIN SMALL LETTER V | 24e4 CIRCLED LATIN SMALL LETTER U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e7 CIRCLED LATIN SMALL LETTER X | 24e6 CIRCLED LATIN SMALL LETTER W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24e9 CIRCLED LATIN SMALL LETTER Z | 24e8 CIRCLED LATIN SMALL LETTER Y */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24eb NEGATIVE CIRCLED NUMBER ELEVEN | 24ea CIRCLED DIGIT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ed NEGATIVE CIRCLED NUMBER THIRTEEN | 24ec NEGATIVE CIRCLED NUMBER TWELVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ef NEGATIVE CIRCLED NUMBER FIFTEEN | 24ee NEGATIVE CIRCLED NUMBER FOURTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f1 NEGATIVE CIRCLED NUMBER SEVENTEEN | 24f0 NEGATIVE CIRCLED NUMBER SIXTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f3 NEGATIVE CIRCLED NUMBER NINETEEN | 24f2 NEGATIVE CIRCLED NUMBER EIGHTEEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f5 DOUBLE CIRCLED DIGIT ONE | 24f4 NEGATIVE CIRCLED NUMBER TWENTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f7 DOUBLE CIRCLED DIGIT THREE | 24f6 DOUBLE CIRCLED DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24f9 DOUBLE CIRCLED DIGIT FIVE | 24f8 DOUBLE CIRCLED DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24fb DOUBLE CIRCLED DIGIT SEVEN | 24fa DOUBLE CIRCLED DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24fd DOUBLE CIRCLED DIGIT NINE | 24fc DOUBLE CIRCLED DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 24ff NEGATIVE CIRCLED DIGIT ZERO | 24fe DOUBLE CIRCLED NUMBER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2501 BOX DRAWINGS HEAVY HORIZONTAL | 2500 BOX DRAWINGS LIGHT HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2503 BOX DRAWINGS HEAVY VERTICAL | 2502 BOX DRAWINGS LIGHT VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2505 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONT | 2504 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2507 BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL | 2506 BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2509 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZ | 2508 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250b BOX DRAWINGS HEAVY QUADRUPLE DASH VERTI | 250a BOX DRAWINGS LIGHT QUADRUPLE DASH VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250d BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY | 250c BOX DRAWINGS LIGHT DOWN AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 250f BOX DRAWINGS HEAVY DOWN AND RIGHT | 250e BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2511 BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY | 2510 BOX DRAWINGS LIGHT DOWN AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2513 BOX DRAWINGS HEAVY DOWN AND LEFT | 2512 BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2515 BOX DRAWINGS UP LIGHT AND RIGHT HEAVY | 2514 BOX DRAWINGS LIGHT UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2517 BOX DRAWINGS HEAVY UP AND RIGHT | 2516 BOX DRAWINGS UP HEAVY AND RIGHT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2519 BOX DRAWINGS UP LIGHT AND LEFT HEAVY | 2518 BOX DRAWINGS LIGHT UP AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251b BOX DRAWINGS HEAVY UP AND LEFT | 251a BOX DRAWINGS UP HEAVY AND LEFT LIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251d BOX DRAWINGS VERTICAL LIGHT AND RIGHT H | 251c BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 251f BOX DRAWINGS DOWN HEAVY AND RIGHT UP LI | 251e BOX DRAWINGS UP HEAVY AND RIGHT DOWN LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2521 BOX DRAWINGS DOWN LIGHT AND RIGHT UP HE | 2520 BOX DRAWINGS VERTICAL HEAVY AND RIGHT L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2523 BOX DRAWINGS HEAVY VERTICAL AND RIGHT | 2522 BOX DRAWINGS UP LIGHT AND RIGHT DOWN HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2525 BOX DRAWINGS VERTICAL LIGHT AND LEFT HE | 2524 BOX DRAWINGS LIGHT VERTICAL AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2527 BOX DRAWINGS DOWN HEAVY AND LEFT UP LIG | 2526 BOX DRAWINGS UP HEAVY AND LEFT DOWN LIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2529 BOX DRAWINGS DOWN LIGHT AND LEFT UP HEA | 2528 BOX DRAWINGS VERTICAL HEAVY AND LEFT LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252b BOX DRAWINGS HEAVY VERTICAL AND LEFT | 252a BOX DRAWINGS UP LIGHT AND LEFT DOWN HEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252d BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN | 252c BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 252f BOX DRAWINGS DOWN LIGHT AND HORIZONTAL | 252e BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2531 BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN | 2530 BOX DRAWINGS DOWN HEAVY AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2533 BOX DRAWINGS HEAVY DOWN AND HORIZONTAL | 2532 BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2535 BOX DRAWINGS LEFT HEAVY AND RIGHT UP LI | 2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2537 BOX DRAWINGS UP LIGHT AND HORIZONTAL HE | 2536 BOX DRAWINGS RIGHT HEAVY AND LEFT UP LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2539 BOX DRAWINGS RIGHT LIGHT AND LEFT UP HE | 2538 BOX DRAWINGS UP HEAVY AND HORIZONTAL LI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253b BOX DRAWINGS HEAVY UP AND HORIZONTAL | 253a BOX DRAWINGS LEFT LIGHT AND RIGHT UP HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253d BOX DRAWINGS LEFT HEAVY AND RIGHT VERTI | 253c BOX DRAWINGS LIGHT VERTICAL AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 253f BOX DRAWINGS VERTICAL LIGHT AND HORIZON | 253e BOX DRAWINGS RIGHT HEAVY AND LEFT VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2541 BOX DRAWINGS DOWN HEAVY AND UP HORIZONT | 2540 BOX DRAWINGS UP HEAVY AND DOWN HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2543 BOX DRAWINGS LEFT UP HEAVY AND RIGHT DO | 2542 BOX DRAWINGS VERTICAL HEAVY AND HORIZON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2545 BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT | 2544 BOX DRAWINGS RIGHT UP HEAVY AND LEFT DO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2547 BOX DRAWINGS DOWN LIGHT AND UP HORIZONT | 2546 BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2549 BOX DRAWINGS RIGHT LIGHT AND LEFT VERTI | 2548 BOX DRAWINGS UP LIGHT AND DOWN HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254b BOX DRAWINGS HEAVY VERTICAL AND HORIZON | 254a BOX DRAWINGS LEFT LIGHT AND RIGHT VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254d BOX DRAWINGS HEAVY DOUBLE DASH HORIZONT | 254c BOX DRAWINGS LIGHT DOUBLE DASH HORIZONT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 254f BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL | 254e BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2551 BOX DRAWINGS DOUBLE VERTICAL | 2550 BOX DRAWINGS DOUBLE HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2553 BOX DRAWINGS DOWN DOUBLE AND RIGHT SING | 2552 BOX DRAWINGS DOWN SINGLE AND RIGHT DOUB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2555 BOX DRAWINGS DOWN SINGLE AND LEFT DOUBL | 2554 BOX DRAWINGS DOUBLE DOWN AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2557 BOX DRAWINGS DOUBLE DOWN AND LEFT | 2556 BOX DRAWINGS DOWN DOUBLE AND LEFT SINGL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2559 BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE | 2558 BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255b BOX DRAWINGS UP SINGLE AND LEFT DOUBLE | 255a BOX DRAWINGS DOUBLE UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255d BOX DRAWINGS DOUBLE UP AND LEFT | 255c BOX DRAWINGS UP DOUBLE AND LEFT SINGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 255f BOX DRAWINGS VERTICAL DOUBLE AND RIGHT | 255e BOX DRAWINGS VERTICAL SINGLE AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2561 BOX DRAWINGS VERTICAL SINGLE AND LEFT D | 2560 BOX DRAWINGS DOUBLE VERTICAL AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2563 BOX DRAWINGS DOUBLE VERTICAL AND LEFT | 2562 BOX DRAWINGS VERTICAL DOUBLE AND LEFT S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2565 BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL | 2564 BOX DRAWINGS DOWN SINGLE AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2567 BOX DRAWINGS UP SINGLE AND HORIZONTAL D | 2566 BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2569 BOX DRAWINGS DOUBLE UP AND HORIZONTAL | 2568 BOX DRAWINGS UP DOUBLE AND HORIZONTAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256b BOX DRAWINGS VERTICAL DOUBLE AND HORIZO | 256a BOX DRAWINGS VERTICAL SINGLE AND HORIZO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256d BOX DRAWINGS LIGHT ARC DOWN AND RIGHT | 256c BOX DRAWINGS DOUBLE VERTICAL AND HORIZO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 256f BOX DRAWINGS LIGHT ARC UP AND LEFT | 256e BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2571 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT | 2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2573 BOX DRAWINGS LIGHT DIAGONAL CROSS | 2572 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2575 BOX DRAWINGS LIGHT UP | 2574 BOX DRAWINGS LIGHT LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2577 BOX DRAWINGS LIGHT DOWN | 2576 BOX DRAWINGS LIGHT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2579 BOX DRAWINGS HEAVY UP | 2578 BOX DRAWINGS HEAVY LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257b BOX DRAWINGS HEAVY DOWN | 257a BOX DRAWINGS HEAVY RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257d BOX DRAWINGS LIGHT UP AND HEAVY DOWN | 257c BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 257f BOX DRAWINGS HEAVY UP AND LIGHT DOWN | 257e BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2581 LOWER ONE EIGHTH BLOCK | 2580 UPPER HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2583 LOWER THREE EIGHTHS BLOCK | 2582 LOWER ONE QUARTER BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2585 LOWER FIVE EIGHTHS BLOCK | 2584 LOWER HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2587 LOWER SEVEN EIGHTHS BLOCK | 2586 LOWER THREE QUARTERS BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2589 LEFT SEVEN EIGHTHS BLOCK | 2588 FULL BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258b LEFT FIVE EIGHTHS BLOCK | 258a LEFT THREE QUARTERS BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258d LEFT THREE EIGHTHS BLOCK | 258c LEFT HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 258f LEFT ONE EIGHTH BLOCK | 258e LEFT ONE QUARTER BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2591 LIGHT SHADE | 2590 RIGHT HALF BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2593 DARK SHADE | 2592 MEDIUM SHADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2595 RIGHT ONE EIGHTH BLOCK | 2594 UPPER ONE EIGHTH BLOCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2597 QUADRANT LOWER RIGHT | 2596 QUADRANT LOWER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2599 QUADRANT UPPER LEFT AND LOWER LEFT AND | 2598 QUADRANT UPPER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259b QUADRANT UPPER LEFT AND UPPER RIGHT AND | 259a QUADRANT UPPER LEFT AND LOWER RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259d QUADRANT UPPER RIGHT | 259c QUADRANT UPPER LEFT AND UPPER RIGHT AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 259f QUADRANT UPPER RIGHT AND LOWER LEFT AND | 259e QUADRANT UPPER RIGHT AND LOWER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a1 WHITE SQUARE | 25a0 BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a3 WHITE SQUARE CONTAINING BLACK SMALL SQU | 25a2 WHITE SQUARE WITH ROUNDED CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a5 SQUARE WITH VERTICAL FILL | 25a4 SQUARE WITH HORIZONTAL FILL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a7 SQUARE WITH UPPER LEFT TO LOWER RIGHT F | 25a6 SQUARE WITH ORTHOGONAL CROSSHATCH FILL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25a9 SQUARE WITH DIAGONAL CROSSHATCH FILL | 25a8 SQUARE WITH UPPER RIGHT TO LOWER LEFT F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ab WHITE SMALL SQUARE | 25aa BLACK SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ad WHITE RECTANGLE | 25ac BLACK RECTANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25af WHITE VERTICAL RECTANGLE | 25ae BLACK VERTICAL RECTANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b1 WHITE PARALLELOGRAM | 25b0 BLACK PARALLELOGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b3 WHITE UP-POINTING TRIANGLE | 25b2 BLACK UP-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b5 WHITE UP-POINTING SMALL TRIANGLE | 25b4 BLACK UP-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b7 WHITE RIGHT-POINTING TRIANGLE | 25b6 BLACK RIGHT-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25b9 WHITE RIGHT-POINTING SMALL TRIANGLE | 25b8 BLACK RIGHT-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bb WHITE RIGHT-POINTING POINTER | 25ba BLACK RIGHT-POINTING POINTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bd WHITE DOWN-POINTING TRIANGLE | 25bc BLACK DOWN-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25bf WHITE DOWN-POINTING SMALL TRIANGLE | 25be BLACK DOWN-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c1 WHITE LEFT-POINTING TRIANGLE | 25c0 BLACK LEFT-POINTING TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c3 WHITE LEFT-POINTING SMALL TRIANGLE | 25c2 BLACK LEFT-POINTING SMALL TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c5 WHITE LEFT-POINTING POINTER | 25c4 BLACK LEFT-POINTING POINTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c7 WHITE DIAMOND | 25c6 BLACK DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25c9 FISHEYE | 25c8 WHITE DIAMOND CONTAINING BLACK SMALL DI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cb WHITE CIRCLE | 25ca LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cd CIRCLE WITH VERTICAL FILL | 25cc DOTTED CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25cf BLACK CIRCLE | 25ce BULLSEYE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d1 CIRCLE WITH RIGHT HALF BLACK | 25d0 CIRCLE WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d3 CIRCLE WITH UPPER HALF BLACK | 25d2 CIRCLE WITH LOWER HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d5 CIRCLE WITH ALL BUT UPPER LEFT QUADRANT | 25d4 CIRCLE WITH UPPER RIGHT QUADRANT BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d7 RIGHT HALF BLACK CIRCLE | 25d6 LEFT HALF BLACK CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25d9 INVERSE WHITE CIRCLE | 25d8 INVERSE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25db LOWER HALF INVERSE WHITE CIRCLE | 25da UPPER HALF INVERSE WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25dd UPPER RIGHT QUADRANT CIRCULAR ARC | 25dc UPPER LEFT QUADRANT CIRCULAR ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25df LOWER LEFT QUADRANT CIRCULAR ARC | 25de LOWER RIGHT QUADRANT CIRCULAR ARC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e1 LOWER HALF CIRCLE | 25e0 UPPER HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e3 BLACK LOWER LEFT TRIANGLE | 25e2 BLACK LOWER RIGHT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e5 BLACK UPPER RIGHT TRIANGLE | 25e4 BLACK UPPER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e7 SQUARE WITH LEFT HALF BLACK | 25e6 WHITE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25e9 SQUARE WITH UPPER LEFT DIAGONAL HALF BL | 25e8 SQUARE WITH RIGHT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25eb WHITE SQUARE WITH VERTICAL BISECTING LI | 25ea SQUARE WITH LOWER RIGHT DIAGONAL HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ed UP-POINTING TRIANGLE WITH LEFT HALF BLA | 25ec WHITE UP-POINTING TRIANGLE WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ef LARGE CIRCLE | 25ee UP-POINTING TRIANGLE WITH RIGHT HALF BL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f1 WHITE SQUARE WITH LOWER LEFT QUADRANT | 25f0 WHITE SQUARE WITH UPPER LEFT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f3 WHITE SQUARE WITH UPPER RIGHT QUADRANT | 25f2 WHITE SQUARE WITH LOWER RIGHT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f5 WHITE CIRCLE WITH LOWER LEFT QUADRANT | 25f4 WHITE CIRCLE WITH UPPER LEFT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f7 WHITE CIRCLE WITH UPPER RIGHT QUADRANT | 25f6 WHITE CIRCLE WITH LOWER RIGHT QUADRANT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25f9 UPPER RIGHT TRIANGLE | 25f8 UPPER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25fb WHITE MEDIUM SQUARE | 25fa LOWER LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25fd WHITE MEDIUM SMALL SQUARE | 25fc BLACK MEDIUM SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 25ff LOWER RIGHT TRIANGLE | 25fe BLACK MEDIUM SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2601 CLOUD | 2600 BLACK SUN WITH RAYS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2603 SNOWMAN | 2602 UMBRELLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2605 BLACK STAR | 2604 COMET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2607 LIGHTNING | 2606 WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2609 SUN | 2608 THUNDERSTORM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260b DESCENDING NODE | 260a ASCENDING NODE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260d OPPOSITION | 260c CONJUNCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 260f WHITE TELEPHONE | 260e BLACK TELEPHONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2611 BALLOT BOX WITH CHECK | 2610 BALLOT BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2613 SALTIRE | 2612 BALLOT BOX WITH X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2615 HOT BEVERAGE | 2614 UMBRELLA WITH RAIN DROPS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2617 BLACK SHOGI PIECE | 2616 WHITE SHOGI PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2619 REVERSED ROTATED FLORAL HEART BULLET | 2618 SHAMROCK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261b BLACK RIGHT POINTING INDEX | 261a BLACK LEFT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261d WHITE UP POINTING INDEX | 261c WHITE LEFT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 261f WHITE DOWN POINTING INDEX | 261e WHITE RIGHT POINTING INDEX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2621 CAUTION SIGN | 2620 SKULL AND CROSSBONES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2623 BIOHAZARD SIGN | 2622 RADIOACTIVE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2625 ANKH | 2624 CADUCEUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2627 CHI RHO | 2626 ORTHODOX CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2629 CROSS OF JERUSALEM | 2628 CROSS OF LORRAINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262b FARSI SYMBOL | 262a STAR AND CRESCENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262d HAMMER AND SICKLE | 262c ADI SHAKTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 262f YIN YANG | 262e PEACE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2631 TRIGRAM FOR LAKE | 2630 TRIGRAM FOR HEAVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2633 TRIGRAM FOR THUNDER | 2632 TRIGRAM FOR FIRE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2635 TRIGRAM FOR WATER | 2634 TRIGRAM FOR WIND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2637 TRIGRAM FOR EARTH | 2636 TRIGRAM FOR MOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2639 WHITE FROWNING FACE | 2638 WHEEL OF DHARMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263b BLACK SMILING FACE | 263a WHITE SMILING FACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263d FIRST QUARTER MOON | 263c WHITE SUN WITH RAYS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 263f MERCURY | 263e LAST QUARTER MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2641 EARTH | 2640 FEMALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2643 JUPITER | 2642 MALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2645 URANUS | 2644 SATURN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2647 PLUTO | 2646 NEPTUNE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2649 TAURUS | 2648 ARIES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264b CANCER | 264a GEMINI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264d VIRGO | 264c LEO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 264f SCORPIUS | 264e LIBRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2651 CAPRICORN | 2650 SAGITTARIUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2653 PISCES | 2652 AQUARIUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2655 WHITE CHESS QUEEN | 2654 WHITE CHESS KING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2657 WHITE CHESS BISHOP | 2656 WHITE CHESS ROOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2659 WHITE CHESS PAWN | 2658 WHITE CHESS KNIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265b BLACK CHESS QUEEN | 265a BLACK CHESS KING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265d BLACK CHESS BISHOP | 265c BLACK CHESS ROOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 265f BLACK CHESS PAWN | 265e BLACK CHESS KNIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2661 WHITE HEART SUIT | 2660 BLACK SPADE SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2663 BLACK CLUB SUIT | 2662 WHITE DIAMOND SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2665 BLACK HEART SUIT | 2664 WHITE SPADE SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2667 WHITE CLUB SUIT | 2666 BLACK DIAMOND SUIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2669 QUARTER NOTE | 2668 HOT SPRINGS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266b BEAMED EIGHTH NOTES | 266a EIGHTH NOTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266d MUSIC FLAT SIGN | 266c BEAMED SIXTEENTH NOTES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 266f MUSIC SHARP SIGN | 266e MUSIC NATURAL SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2671 EAST SYRIAC CROSS | 2670 WEST SYRIAC CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2673 RECYCLING SYMBOL FOR TYPE-1 PLASTICS | 2672 UNIVERSAL RECYCLING SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2675 RECYCLING SYMBOL FOR TYPE-3 PLASTICS | 2674 RECYCLING SYMBOL FOR TYPE-2 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2677 RECYCLING SYMBOL FOR TYPE-5 PLASTICS | 2676 RECYCLING SYMBOL FOR TYPE-4 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2679 RECYCLING SYMBOL FOR TYPE-7 PLASTICS | 2678 RECYCLING SYMBOL FOR TYPE-6 PLASTICS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267b BLACK UNIVERSAL RECYCLING SYMBOL | 267a RECYCLING SYMBOL FOR GENERIC MATERIALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267d PARTIALLY-RECYCLED PAPER SYMBOL | 267c RECYCLED PAPER SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 267f WHEELCHAIR SYMBOL | 267e PERMANENT PAPER SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2681 DIE FACE-2 | 2680 DIE FACE-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2683 DIE FACE-4 | 2682 DIE FACE-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2685 DIE FACE-6 | 2684 DIE FACE-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2687 WHITE CIRCLE WITH TWO DOTS | 2686 WHITE CIRCLE WITH DOT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2689 BLACK CIRCLE WITH TWO WHITE DOTS | 2688 BLACK CIRCLE WITH WHITE DOT RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268b MONOGRAM FOR YIN | 268a MONOGRAM FOR YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268d DIGRAM FOR LESSER YIN | 268c DIGRAM FOR GREATER YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 268f DIGRAM FOR GREATER YIN | 268e DIGRAM FOR LESSER YANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2691 BLACK FLAG | 2690 WHITE FLAG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2693 ANCHOR | 2692 HAMMER AND PICK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2695 STAFF OF AESCULAPIUS | 2694 CROSSED SWORDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2697 ALEMBIC | 2696 SCALES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2699 GEAR | 2698 FLOWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269b ATOM SYMBOL | 269a STAFF OF HERMES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269d OUTLINED WHITE STAR | 269c FLEUR-DE-LIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 269f THREE LINES CONVERGING LEFT | 269e THREE LINES CONVERGING RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a1 HIGH VOLTAGE SIGN | 26a0 WARNING SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a3 DOUBLED MALE SIGN | 26a2 DOUBLED FEMALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a5 MALE AND FEMALE SIGN | 26a4 INTERLOCKED FEMALE AND MALE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a7 MALE WITH STROKE AND MALE AND FEMALE SI | 26a6 MALE WITH STROKE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26a9 HORIZONTAL MALE WITH STROKE SIGN | 26a8 VERTICAL MALE WITH STROKE SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ab MEDIUM BLACK CIRCLE | 26aa MEDIUM WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ad MARRIAGE SYMBOL | 26ac MEDIUM SMALL WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26af UNMARRIED PARTNERSHIP SYMBOL | 26ae DIVORCE SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b1 FUNERAL URN | 26b0 COFFIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b3 CERES | 26b2 NEUTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b5 JUNO | 26b4 PALLAS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b7 CHIRON | 26b6 VESTA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26b9 SEXTILE | 26b8 BLACK MOON LILITH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bb QUINCUNX | 26ba SEMISEXTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bd SOCCER BALL | 26bc SESQUIQUADRATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26bf SQUARED KEY | 26be BASEBALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c1 WHITE DRAUGHTS KING | 26c0 WHITE DRAUGHTS MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c3 BLACK DRAUGHTS KING | 26c2 BLACK DRAUGHTS MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c5 SUN BEHIND CLOUD | 26c4 SNOWMAN WITHOUT SNOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c7 BLACK SNOWMAN | 26c6 RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26c9 TURNED WHITE SHOGI PIECE | 26c8 THUNDER CLOUD AND RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cb WHITE DIAMOND IN SQUARE | 26ca TURNED BLACK SHOGI PIECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cd DISABLED CAR | 26cc CROSSING LANES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26cf PICK | 26ce OPHIUCHUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d1 HELMET WITH WHITE CROSS | 26d0 CAR SLIDING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d3 CHAINS | 26d2 CIRCLED CROSSING LANES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d5 ALTERNATE ONE-WAY LEFT WAY TRAFFIC | 26d4 NO ENTRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d7 WHITE TWO-WAY LEFT WAY TRAFFIC | 26d6 BLACK TWO-WAY LEFT WAY TRAFFIC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26d9 WHITE LEFT LANE MERGE | 26d8 BLACK LEFT LANE MERGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26db HEAVY WHITE DOWN-POINTING TRIANGLE | 26da DRIVE SLOW SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26dd SQUARED SALTIRE | 26dc LEFT CLOSED ENTRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26df BLACK TRUCK | 26de FALLING DIAGONAL IN WHITE CIRCLE IN BLA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e1 RESTRICTED LEFT ENTRY-2 | 26e0 RESTRICTED LEFT ENTRY-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e3 HEAVY CIRCLE WITH STROKE AND TWO DOTS A | 26e2 ASTRONOMICAL SYMBOL FOR URANUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e5 RIGHT-HANDED INTERLACED PENTAGRAM | 26e4 PENTAGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e7 INVERTED PENTAGRAM | 26e6 LEFT-HANDED INTERLACED PENTAGRAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26e9 SHINTO SHRINE | 26e8 BLACK CROSS ON SHIELD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26eb CASTLE | 26ea CHURCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ed GEAR WITHOUT HUB | 26ec HISTORIC SITE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ef MAP SYMBOL FOR LIGHTHOUSE | 26ee GEAR WITH HANDLES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f1 UMBRELLA ON GROUND | 26f0 MOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f3 FLAG IN HOLE | 26f2 FOUNTAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f5 SAILBOAT | 26f4 FERRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f7 SKIER | 26f6 SQUARE FOUR CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26f9 PERSON WITH BALL | 26f8 ICE SKATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26fb JAPANESE BANK SYMBOL | 26fa TENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26fd FUEL PUMP | 26fc HEADSTONE GRAVEYARD SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 26ff WHITE FLAG WITH HORIZONTAL MIDDLE BLACK | 26fe CUP ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2701 UPPER BLADE SCISSORS | 2700 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2703 LOWER BLADE SCISSORS | 2702 BLACK SCISSORS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2705 WHITE HEAVY CHECK MARK | 2704 WHITE SCISSORS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2707 TAPE DRIVE | 2706 TELEPHONE LOCATION SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2709 ENVELOPE | 2708 AIRPLANE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270b RAISED HAND | 270a RAISED FIST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270d WRITING HAND | 270c VICTORY HAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 270f PENCIL | 270e LOWER RIGHT PENCIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2711 WHITE NIB | 2710 UPPER RIGHT PENCIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2713 CHECK MARK | 2712 BLACK NIB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2715 MULTIPLICATION X | 2714 HEAVY CHECK MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2717 BALLOT X | 2716 HEAVY MULTIPLICATION X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2719 OUTLINED GREEK CROSS | 2718 HEAVY BALLOT X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271b OPEN CENTRE CROSS | 271a HEAVY GREEK CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271d LATIN CROSS | 271c HEAVY OPEN CENTRE CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 271f OUTLINED LATIN CROSS | 271e SHADOWED WHITE LATIN CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2721 STAR OF DAVID | 2720 MALTESE CROSS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2723 FOUR BALLOON-SPOKED ASTERISK | 2722 FOUR TEARDROP-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2725 FOUR CLUB-SPOKED ASTERISK | 2724 HEAVY FOUR BALLOON-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2727 WHITE FOUR POINTED STAR | 2726 BLACK FOUR POINTED STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2729 STRESS OUTLINED WHITE STAR | 2728 SPARKLES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272b OPEN CENTRE BLACK STAR | 272a CIRCLED WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272d OUTLINED BLACK STAR | 272c BLACK CENTRE WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 272f PINWHEEL STAR | 272e HEAVY OUTLINED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2731 HEAVY ASTERISK | 2730 SHADOWED WHITE STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2733 EIGHT SPOKED ASTERISK | 2732 OPEN CENTRE ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2735 EIGHT POINTED PINWHEEL STAR | 2734 EIGHT POINTED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2737 EIGHT POINTED RECTILINEAR BLACK STAR | 2736 SIX POINTED BLACK STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2739 TWELVE POINTED BLACK STAR | 2738 HEAVY EIGHT POINTED RECTILINEAR BLACK S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273b TEARDROP-SPOKED ASTERISK | 273a SIXTEEN POINTED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273d HEAVY TEARDROP-SPOKED ASTERISK | 273c OPEN CENTRE TEARDROP-SPOKED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 273f BLACK FLORETTE | 273e SIX PETALLED BLACK AND WHITE FLORETTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2741 EIGHT PETALLED OUTLINED BLACK FLORETTE | 2740 WHITE FLORETTE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2743 HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK | 2742 CIRCLED OPEN CENTRE EIGHT POINTED STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2745 TIGHT TRIFOLIATE SNOWFLAKE | 2744 SNOWFLAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2747 SPARKLE | 2746 HEAVY CHEVRON SNOWFLAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2749 BALLOON-SPOKED ASTERISK | 2748 HEAVY SPARKLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274b HEAVY EIGHT TEARDROP-SPOKED PROPELLER A | 274a EIGHT TEARDROP-SPOKED PROPELLER ASTERIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274d SHADOWED WHITE CIRCLE | 274c CROSS MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 274f LOWER RIGHT DROP-SHADOWED WHITE SQUARE | 274e NEGATIVE SQUARED CROSS MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2751 LOWER RIGHT SHADOWED WHITE SQUARE | 2750 UPPER RIGHT DROP-SHADOWED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2753 BLACK QUESTION MARK ORNAMENT | 2752 UPPER RIGHT SHADOWED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2755 WHITE EXCLAMATION MARK ORNAMENT | 2754 WHITE QUESTION MARK ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2757 HEAVY EXCLAMATION MARK SYMBOL | 2756 BLACK DIAMOND MINUS WHITE X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2759 MEDIUM VERTICAL BAR | 2758 LIGHT VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275b HEAVY SINGLE TURNED COMMA QUOTATION MAR | 275a HEAVY VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275d HEAVY DOUBLE TURNED COMMA QUOTATION MAR | 275c HEAVY SINGLE COMMA QUOTATION MARK ORNAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 275f HEAVY LOW SINGLE COMMA QUOTATION MARK O | 275e HEAVY DOUBLE COMMA QUOTATION MARK ORNAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2761 CURVED STEM PARAGRAPH SIGN ORNAMENT | 2760 HEAVY LOW DOUBLE COMMA QUOTATION MARK O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2763 HEAVY HEART EXCLAMATION MARK ORNAMENT | 2762 HEAVY EXCLAMATION MARK ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2765 ROTATED HEAVY BLACK HEART BULLET | 2764 HEAVY BLACK HEART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2767 ROTATED FLORAL HEART BULLET | 2766 FLORAL HEART */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2769 MEDIUM RIGHT PARENTHESIS ORNAMENT | 2768 MEDIUM LEFT PARENTHESIS ORNAMENT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276b MEDIUM FLATTENED RIGHT PARENTHESIS ORNA | 276a MEDIUM FLATTENED LEFT PARENTHESIS ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276d MEDIUM RIGHT-POINTING ANGLE BRACKET ORN | 276c MEDIUM LEFT-POINTING ANGLE BRACKET ORNA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 276f HEAVY RIGHT-POINTING ANGLE QUOTATION MA | 276e HEAVY LEFT-POINTING ANGLE QUOTATION MAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2771 HEAVY RIGHT-POINTING ANGLE BRACKET ORNA | 2770 HEAVY LEFT-POINTING ANGLE BRACKET ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2773 LIGHT RIGHT TORTOISE SHELL BRACKET ORNA | 2772 LIGHT LEFT TORTOISE SHELL BRACKET ORNAM */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2775 MEDIUM RIGHT CURLY BRACKET ORNAMENT | 2774 MEDIUM LEFT CURLY BRACKET ORNAMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2777 DINGBAT NEGATIVE CIRCLED DIGIT TWO | 2776 DINGBAT NEGATIVE CIRCLED DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2779 DINGBAT NEGATIVE CIRCLED DIGIT FOUR | 2778 DINGBAT NEGATIVE CIRCLED DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277b DINGBAT NEGATIVE CIRCLED DIGIT SIX | 277a DINGBAT NEGATIVE CIRCLED DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277d DINGBAT NEGATIVE CIRCLED DIGIT EIGHT | 277c DINGBAT NEGATIVE CIRCLED DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 277f DINGBAT NEGATIVE CIRCLED NUMBER TEN | 277e DINGBAT NEGATIVE CIRCLED DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2781 DINGBAT CIRCLED SANS-SERIF DIGIT TWO | 2780 DINGBAT CIRCLED SANS-SERIF DIGIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2783 DINGBAT CIRCLED SANS-SERIF DIGIT FOUR | 2782 DINGBAT CIRCLED SANS-SERIF DIGIT THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2785 DINGBAT CIRCLED SANS-SERIF DIGIT SIX | 2784 DINGBAT CIRCLED SANS-SERIF DIGIT FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2787 DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT | 2786 DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2789 DINGBAT CIRCLED SANS-SERIF NUMBER TEN | 2788 DINGBAT CIRCLED SANS-SERIF DIGIT NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278b DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278a DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278d DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278c DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 278f DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 278e DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2791 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG | 2790 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2793 DINGBAT NEGATIVE CIRCLED SANS-SERIF NUM | 2792 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2795 HEAVY PLUS SIGN | 2794 HEAVY WIDE-HEADED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2797 HEAVY DIVISION SIGN | 2796 HEAVY MINUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2799 HEAVY RIGHTWARDS ARROW | 2798 HEAVY SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279b DRAFTING POINT RIGHTWARDS ARROW | 279a HEAVY NORTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279d TRIANGLE-HEADED RIGHTWARDS ARROW | 279c HEAVY ROUND-TIPPED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 279f DASHED TRIANGLE-HEADED RIGHTWARDS ARROW | 279e HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a1 BLACK RIGHTWARDS ARROW | 27a0 HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a3 THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROW | 27a2 THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a5 HEAVY BLACK CURVED DOWNWARDS AND RIGHTW | 27a4 BLACK RIGHTWARDS ARROWHEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a7 SQUAT BLACK RIGHTWARDS ARROW | 27a6 HEAVY BLACK CURVED UPWARDS AND RIGHTWAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27a9 RIGHT-SHADED WHITE RIGHTWARDS ARROW | 27a8 HEAVY CONCAVE-POINTED BLACK RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ab BACK-TILTED SHADOWED WHITE RIGHTWARDS A | 27aa LEFT-SHADED WHITE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ad HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTW | 27ac FRONT-TILTED SHADOWED WHITE RIGHTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27af NOTCHED LOWER RIGHT-SHADOWED WHITE RIGH | 27ae HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b1 NOTCHED UPPER RIGHT-SHADOWED WHITE RIGH | 27b0 CURLY LOOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b3 WHITE-FEATHERED RIGHTWARDS ARROW | 27b2 CIRCLED HEAVY WHITE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b5 BLACK-FEATHERED RIGHTWARDS ARROW | 27b4 BLACK-FEATHERED SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b7 HEAVY BLACK-FEATHERED SOUTH EAST ARROW | 27b6 BLACK-FEATHERED NORTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27b9 HEAVY BLACK-FEATHERED NORTH EAST ARROW | 27b8 HEAVY BLACK-FEATHERED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bb HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW | 27ba TEARDROP-BARBED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bd HEAVY WEDGE-TAILED RIGHTWARDS ARROW | 27bc WEDGE-TAILED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27bf DOUBLE CURLY LOOP | 27be OPEN-OUTLINED RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c1 WHITE TRIANGLE CONTAINING SMALL WHITE T | 27c0 THREE DIMENSIONAL ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c3 OPEN SUBSET | 27c2 PERPENDICULAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 27c5 LEFT S-SHAPED BAG DELIMITER | 27c4 OPEN SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 27c7 OR WITH DOT INSIDE | 27c6 RIGHT S-SHAPED BAG DELIMITER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27c9 SUPERSET PRECEDING SOLIDUS | 27c8 REVERSE SOLIDUS PRECEDING SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cb MATHEMATICAL RISING DIAGONAL | 27ca VERTICAL BAR WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cd MATHEMATICAL FALLING DIAGONAL | 27cc LONG DIVISION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27cf SQUARED LOGICAL OR | 27ce SQUARED LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d1 AND WITH DOT | 27d0 WHITE DIAMOND WITH CENTRED DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d3 LOWER RIGHT CORNER WITH DOT | 27d2 ELEMENT OF OPENING UPWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d5 LEFT OUTER JOIN | 27d4 UPPER LEFT CORNER WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d7 FULL OUTER JOIN | 27d6 RIGHT OUTER JOIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27d9 LARGE DOWN TACK | 27d8 LARGE UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27db LEFT AND RIGHT TACK | 27da LEFT AND RIGHT DOUBLE TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27dd LONG RIGHT TACK | 27dc LEFT MULTIMAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27df UP TACK WITH CIRCLE ABOVE | 27de LONG LEFT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e1 WHITE CONCAVE-SIDED DIAMOND | 27e0 LOZENGE DIVIDED BY HORIZONTAL RULE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e3 WHITE CONCAVE-SIDED DIAMOND WITH RIGHTW | 27e2 WHITE CONCAVE-SIDED DIAMOND WITH LEFTWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27e5 WHITE SQUARE WITH RIGHTWARDS TICK | 27e4 WHITE SQUARE WITH LEFTWARDS TICK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27e7 MATHEMATICAL RIGHT WHITE SQUARE BRACKET | 27e6 MATHEMATICAL LEFT WHITE SQUARE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27e9 MATHEMATICAL RIGHT ANGLE BRACKET | 27e8 MATHEMATICAL LEFT ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27eb MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET | 27ea MATHEMATICAL LEFT DOUBLE ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27ed MATHEMATICAL RIGHT WHITE TORTOISE SHELL | 27ec MATHEMATICAL LEFT WHITE TORTOISE SHELL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 27ef MATHEMATICAL RIGHT FLATTENED PARENTHESI | 27ee MATHEMATICAL LEFT FLATTENED PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f1 DOWNWARDS QUADRUPLE ARROW | 27f0 UPWARDS QUADRUPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f3 CLOCKWISE GAPPED CIRCLE ARROW | 27f2 ANTICLOCKWISE GAPPED CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f5 LONG LEFTWARDS ARROW | 27f4 RIGHT ARROW WITH CIRCLED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f7 LONG LEFT RIGHT ARROW | 27f6 LONG RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27f9 LONG RIGHTWARDS DOUBLE ARROW | 27f8 LONG LEFTWARDS DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27fb LONG LEFTWARDS ARROW FROM BAR | 27fa LONG LEFT RIGHT DOUBLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27fd LONG LEFTWARDS DOUBLE ARROW FROM BAR | 27fc LONG RIGHTWARDS ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 27ff LONG RIGHTWARDS SQUIGGLE ARROW | 27fe LONG RIGHTWARDS DOUBLE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2801 BRAILLE PATTERN DOTS-1 | 2800 BRAILLE PATTERN BLANK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2803 BRAILLE PATTERN DOTS-12 | 2802 BRAILLE PATTERN DOTS-2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2805 BRAILLE PATTERN DOTS-13 | 2804 BRAILLE PATTERN DOTS-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2807 BRAILLE PATTERN DOTS-123 | 2806 BRAILLE PATTERN DOTS-23 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2809 BRAILLE PATTERN DOTS-14 | 2808 BRAILLE PATTERN DOTS-4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280b BRAILLE PATTERN DOTS-124 | 280a BRAILLE PATTERN DOTS-24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280d BRAILLE PATTERN DOTS-134 | 280c BRAILLE PATTERN DOTS-34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 280f BRAILLE PATTERN DOTS-1234 | 280e BRAILLE PATTERN DOTS-234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2811 BRAILLE PATTERN DOTS-15 | 2810 BRAILLE PATTERN DOTS-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2813 BRAILLE PATTERN DOTS-125 | 2812 BRAILLE PATTERN DOTS-25 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2815 BRAILLE PATTERN DOTS-135 | 2814 BRAILLE PATTERN DOTS-35 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2817 BRAILLE PATTERN DOTS-1235 | 2816 BRAILLE PATTERN DOTS-235 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2819 BRAILLE PATTERN DOTS-145 | 2818 BRAILLE PATTERN DOTS-45 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281b BRAILLE PATTERN DOTS-1245 | 281a BRAILLE PATTERN DOTS-245 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281d BRAILLE PATTERN DOTS-1345 | 281c BRAILLE PATTERN DOTS-345 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 281f BRAILLE PATTERN DOTS-12345 | 281e BRAILLE PATTERN DOTS-2345 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2821 BRAILLE PATTERN DOTS-16 | 2820 BRAILLE PATTERN DOTS-6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2823 BRAILLE PATTERN DOTS-126 | 2822 BRAILLE PATTERN DOTS-26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2825 BRAILLE PATTERN DOTS-136 | 2824 BRAILLE PATTERN DOTS-36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2827 BRAILLE PATTERN DOTS-1236 | 2826 BRAILLE PATTERN DOTS-236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2829 BRAILLE PATTERN DOTS-146 | 2828 BRAILLE PATTERN DOTS-46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282b BRAILLE PATTERN DOTS-1246 | 282a BRAILLE PATTERN DOTS-246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282d BRAILLE PATTERN DOTS-1346 | 282c BRAILLE PATTERN DOTS-346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 282f BRAILLE PATTERN DOTS-12346 | 282e BRAILLE PATTERN DOTS-2346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2831 BRAILLE PATTERN DOTS-156 | 2830 BRAILLE PATTERN DOTS-56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2833 BRAILLE PATTERN DOTS-1256 | 2832 BRAILLE PATTERN DOTS-256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2835 BRAILLE PATTERN DOTS-1356 | 2834 BRAILLE PATTERN DOTS-356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2837 BRAILLE PATTERN DOTS-12356 | 2836 BRAILLE PATTERN DOTS-2356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2839 BRAILLE PATTERN DOTS-1456 | 2838 BRAILLE PATTERN DOTS-456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283b BRAILLE PATTERN DOTS-12456 | 283a BRAILLE PATTERN DOTS-2456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283d BRAILLE PATTERN DOTS-13456 | 283c BRAILLE PATTERN DOTS-3456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 283f BRAILLE PATTERN DOTS-123456 | 283e BRAILLE PATTERN DOTS-23456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2841 BRAILLE PATTERN DOTS-17 | 2840 BRAILLE PATTERN DOTS-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2843 BRAILLE PATTERN DOTS-127 | 2842 BRAILLE PATTERN DOTS-27 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2845 BRAILLE PATTERN DOTS-137 | 2844 BRAILLE PATTERN DOTS-37 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2847 BRAILLE PATTERN DOTS-1237 | 2846 BRAILLE PATTERN DOTS-237 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2849 BRAILLE PATTERN DOTS-147 | 2848 BRAILLE PATTERN DOTS-47 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284b BRAILLE PATTERN DOTS-1247 | 284a BRAILLE PATTERN DOTS-247 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284d BRAILLE PATTERN DOTS-1347 | 284c BRAILLE PATTERN DOTS-347 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 284f BRAILLE PATTERN DOTS-12347 | 284e BRAILLE PATTERN DOTS-2347 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2851 BRAILLE PATTERN DOTS-157 | 2850 BRAILLE PATTERN DOTS-57 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2853 BRAILLE PATTERN DOTS-1257 | 2852 BRAILLE PATTERN DOTS-257 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2855 BRAILLE PATTERN DOTS-1357 | 2854 BRAILLE PATTERN DOTS-357 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2857 BRAILLE PATTERN DOTS-12357 | 2856 BRAILLE PATTERN DOTS-2357 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2859 BRAILLE PATTERN DOTS-1457 | 2858 BRAILLE PATTERN DOTS-457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285b BRAILLE PATTERN DOTS-12457 | 285a BRAILLE PATTERN DOTS-2457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285d BRAILLE PATTERN DOTS-13457 | 285c BRAILLE PATTERN DOTS-3457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 285f BRAILLE PATTERN DOTS-123457 | 285e BRAILLE PATTERN DOTS-23457 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2861 BRAILLE PATTERN DOTS-167 | 2860 BRAILLE PATTERN DOTS-67 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2863 BRAILLE PATTERN DOTS-1267 | 2862 BRAILLE PATTERN DOTS-267 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2865 BRAILLE PATTERN DOTS-1367 | 2864 BRAILLE PATTERN DOTS-367 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2867 BRAILLE PATTERN DOTS-12367 | 2866 BRAILLE PATTERN DOTS-2367 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2869 BRAILLE PATTERN DOTS-1467 | 2868 BRAILLE PATTERN DOTS-467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286b BRAILLE PATTERN DOTS-12467 | 286a BRAILLE PATTERN DOTS-2467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286d BRAILLE PATTERN DOTS-13467 | 286c BRAILLE PATTERN DOTS-3467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 286f BRAILLE PATTERN DOTS-123467 | 286e BRAILLE PATTERN DOTS-23467 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2871 BRAILLE PATTERN DOTS-1567 | 2870 BRAILLE PATTERN DOTS-567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2873 BRAILLE PATTERN DOTS-12567 | 2872 BRAILLE PATTERN DOTS-2567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2875 BRAILLE PATTERN DOTS-13567 | 2874 BRAILLE PATTERN DOTS-3567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2877 BRAILLE PATTERN DOTS-123567 | 2876 BRAILLE PATTERN DOTS-23567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2879 BRAILLE PATTERN DOTS-14567 | 2878 BRAILLE PATTERN DOTS-4567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287b BRAILLE PATTERN DOTS-124567 | 287a BRAILLE PATTERN DOTS-24567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287d BRAILLE PATTERN DOTS-134567 | 287c BRAILLE PATTERN DOTS-34567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 287f BRAILLE PATTERN DOTS-1234567 | 287e BRAILLE PATTERN DOTS-234567 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2881 BRAILLE PATTERN DOTS-18 | 2880 BRAILLE PATTERN DOTS-8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2883 BRAILLE PATTERN DOTS-128 | 2882 BRAILLE PATTERN DOTS-28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2885 BRAILLE PATTERN DOTS-138 | 2884 BRAILLE PATTERN DOTS-38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2887 BRAILLE PATTERN DOTS-1238 | 2886 BRAILLE PATTERN DOTS-238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2889 BRAILLE PATTERN DOTS-148 | 2888 BRAILLE PATTERN DOTS-48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288b BRAILLE PATTERN DOTS-1248 | 288a BRAILLE PATTERN DOTS-248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288d BRAILLE PATTERN DOTS-1348 | 288c BRAILLE PATTERN DOTS-348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 288f BRAILLE PATTERN DOTS-12348 | 288e BRAILLE PATTERN DOTS-2348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2891 BRAILLE PATTERN DOTS-158 | 2890 BRAILLE PATTERN DOTS-58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2893 BRAILLE PATTERN DOTS-1258 | 2892 BRAILLE PATTERN DOTS-258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2895 BRAILLE PATTERN DOTS-1358 | 2894 BRAILLE PATTERN DOTS-358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2897 BRAILLE PATTERN DOTS-12358 | 2896 BRAILLE PATTERN DOTS-2358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2899 BRAILLE PATTERN DOTS-1458 | 2898 BRAILLE PATTERN DOTS-458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289b BRAILLE PATTERN DOTS-12458 | 289a BRAILLE PATTERN DOTS-2458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289d BRAILLE PATTERN DOTS-13458 | 289c BRAILLE PATTERN DOTS-3458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 289f BRAILLE PATTERN DOTS-123458 | 289e BRAILLE PATTERN DOTS-23458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a1 BRAILLE PATTERN DOTS-168 | 28a0 BRAILLE PATTERN DOTS-68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a3 BRAILLE PATTERN DOTS-1268 | 28a2 BRAILLE PATTERN DOTS-268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a5 BRAILLE PATTERN DOTS-1368 | 28a4 BRAILLE PATTERN DOTS-368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a7 BRAILLE PATTERN DOTS-12368 | 28a6 BRAILLE PATTERN DOTS-2368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28a9 BRAILLE PATTERN DOTS-1468 | 28a8 BRAILLE PATTERN DOTS-468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ab BRAILLE PATTERN DOTS-12468 | 28aa BRAILLE PATTERN DOTS-2468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ad BRAILLE PATTERN DOTS-13468 | 28ac BRAILLE PATTERN DOTS-3468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28af BRAILLE PATTERN DOTS-123468 | 28ae BRAILLE PATTERN DOTS-23468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b1 BRAILLE PATTERN DOTS-1568 | 28b0 BRAILLE PATTERN DOTS-568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b3 BRAILLE PATTERN DOTS-12568 | 28b2 BRAILLE PATTERN DOTS-2568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b5 BRAILLE PATTERN DOTS-13568 | 28b4 BRAILLE PATTERN DOTS-3568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b7 BRAILLE PATTERN DOTS-123568 | 28b6 BRAILLE PATTERN DOTS-23568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28b9 BRAILLE PATTERN DOTS-14568 | 28b8 BRAILLE PATTERN DOTS-4568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bb BRAILLE PATTERN DOTS-124568 | 28ba BRAILLE PATTERN DOTS-24568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bd BRAILLE PATTERN DOTS-134568 | 28bc BRAILLE PATTERN DOTS-34568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28bf BRAILLE PATTERN DOTS-1234568 | 28be BRAILLE PATTERN DOTS-234568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c1 BRAILLE PATTERN DOTS-178 | 28c0 BRAILLE PATTERN DOTS-78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c3 BRAILLE PATTERN DOTS-1278 | 28c2 BRAILLE PATTERN DOTS-278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c5 BRAILLE PATTERN DOTS-1378 | 28c4 BRAILLE PATTERN DOTS-378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c7 BRAILLE PATTERN DOTS-12378 | 28c6 BRAILLE PATTERN DOTS-2378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28c9 BRAILLE PATTERN DOTS-1478 | 28c8 BRAILLE PATTERN DOTS-478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cb BRAILLE PATTERN DOTS-12478 | 28ca BRAILLE PATTERN DOTS-2478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cd BRAILLE PATTERN DOTS-13478 | 28cc BRAILLE PATTERN DOTS-3478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28cf BRAILLE PATTERN DOTS-123478 | 28ce BRAILLE PATTERN DOTS-23478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d1 BRAILLE PATTERN DOTS-1578 | 28d0 BRAILLE PATTERN DOTS-578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d3 BRAILLE PATTERN DOTS-12578 | 28d2 BRAILLE PATTERN DOTS-2578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d5 BRAILLE PATTERN DOTS-13578 | 28d4 BRAILLE PATTERN DOTS-3578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d7 BRAILLE PATTERN DOTS-123578 | 28d6 BRAILLE PATTERN DOTS-23578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28d9 BRAILLE PATTERN DOTS-14578 | 28d8 BRAILLE PATTERN DOTS-4578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28db BRAILLE PATTERN DOTS-124578 | 28da BRAILLE PATTERN DOTS-24578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28dd BRAILLE PATTERN DOTS-134578 | 28dc BRAILLE PATTERN DOTS-34578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28df BRAILLE PATTERN DOTS-1234578 | 28de BRAILLE PATTERN DOTS-234578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e1 BRAILLE PATTERN DOTS-1678 | 28e0 BRAILLE PATTERN DOTS-678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e3 BRAILLE PATTERN DOTS-12678 | 28e2 BRAILLE PATTERN DOTS-2678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e5 BRAILLE PATTERN DOTS-13678 | 28e4 BRAILLE PATTERN DOTS-3678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e7 BRAILLE PATTERN DOTS-123678 | 28e6 BRAILLE PATTERN DOTS-23678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28e9 BRAILLE PATTERN DOTS-14678 | 28e8 BRAILLE PATTERN DOTS-4678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28eb BRAILLE PATTERN DOTS-124678 | 28ea BRAILLE PATTERN DOTS-24678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ed BRAILLE PATTERN DOTS-134678 | 28ec BRAILLE PATTERN DOTS-34678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ef BRAILLE PATTERN DOTS-1234678 | 28ee BRAILLE PATTERN DOTS-234678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f1 BRAILLE PATTERN DOTS-15678 | 28f0 BRAILLE PATTERN DOTS-5678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f3 BRAILLE PATTERN DOTS-125678 | 28f2 BRAILLE PATTERN DOTS-25678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f5 BRAILLE PATTERN DOTS-135678 | 28f4 BRAILLE PATTERN DOTS-35678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f7 BRAILLE PATTERN DOTS-1235678 | 28f6 BRAILLE PATTERN DOTS-235678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28f9 BRAILLE PATTERN DOTS-145678 | 28f8 BRAILLE PATTERN DOTS-45678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28fb BRAILLE PATTERN DOTS-1245678 | 28fa BRAILLE PATTERN DOTS-245678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28fd BRAILLE PATTERN DOTS-1345678 | 28fc BRAILLE PATTERN DOTS-345678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 28ff BRAILLE PATTERN DOTS-12345678 | 28fe BRAILLE PATTERN DOTS-2345678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2901 RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE | 2900 RIGHTWARDS TWO-HEADED ARROW WITH VERTIC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2903 RIGHTWARDS DOUBLE ARROW WITH VERTICAL S | 2902 LEFTWARDS DOUBLE ARROW WITH VERTICAL ST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2905 RIGHTWARDS TWO-HEADED ARROW FROM BAR | 2904 LEFT RIGHT DOUBLE ARROW WITH VERTICAL S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2907 RIGHTWARDS DOUBLE ARROW FROM BAR | 2906 LEFTWARDS DOUBLE ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2909 UPWARDS ARROW WITH HORIZONTAL STROKE | 2908 DOWNWARDS ARROW WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290b DOWNWARDS TRIPLE ARROW | 290a UPWARDS TRIPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290d RIGHTWARDS DOUBLE DASH ARROW | 290c LEFTWARDS DOUBLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 290f RIGHTWARDS TRIPLE DASH ARROW | 290e LEFTWARDS TRIPLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2911 RIGHTWARDS ARROW WITH DOTTED STEM | 2910 RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2913 DOWNWARDS ARROW TO BAR | 2912 UPWARDS ARROW TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2915 RIGHTWARDS ARROW WITH TAIL WITH DOUBLE | 2914 RIGHTWARDS ARROW WITH TAIL WITH VERTICA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2917 RIGHTWARDS TWO-HEADED ARROW WITH TAIL W | 2916 RIGHTWARDS TWO-HEADED ARROW WITH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2919 LEFTWARDS ARROW-TAIL | 2918 RIGHTWARDS TWO-HEADED ARROW WITH TAIL W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291b LEFTWARDS DOUBLE ARROW-TAIL | 291a RIGHTWARDS ARROW-TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291d LEFTWARDS ARROW TO BLACK DIAMOND | 291c RIGHTWARDS DOUBLE ARROW-TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 291f LEFTWARDS ARROW FROM BAR TO BLACK DIAMO | 291e RIGHTWARDS ARROW TO BLACK DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2921 NORTH WEST AND SOUTH EAST ARROW | 2920 RIGHTWARDS ARROW FROM BAR TO BLACK DIAM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2923 NORTH WEST ARROW WITH HOOK | 2922 NORTH EAST AND SOUTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2925 SOUTH EAST ARROW WITH HOOK | 2924 NORTH EAST ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2927 NORTH WEST ARROW AND NORTH EAST ARROW | 2926 SOUTH WEST ARROW WITH HOOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2929 SOUTH EAST ARROW AND SOUTH WEST ARROW | 2928 NORTH EAST ARROW AND SOUTH EAST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292b RISING DIAGONAL CROSSING FALLING DIAGON | 292a SOUTH WEST ARROW AND NORTH WEST ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292d SOUTH EAST ARROW CROSSING NORTH EAST AR | 292c FALLING DIAGONAL CROSSING RISING DIAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 292f FALLING DIAGONAL CROSSING NORTH EAST AR | 292e NORTH EAST ARROW CROSSING SOUTH EAST AR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2931 NORTH EAST ARROW CROSSING NORTH WEST AR | 2930 RISING DIAGONAL CROSSING SOUTH EAST ARR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2933 WAVE ARROW POINTING DIRECTLY RIGHT | 2932 NORTH WEST ARROW CROSSING NORTH EAST AR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2935 ARROW POINTING RIGHTWARDS THEN CURVING | 2934 ARROW POINTING RIGHTWARDS THEN CURVING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2937 ARROW POINTING DOWNWARDS THEN CURVING R | 2936 ARROW POINTING DOWNWARDS THEN CURVING L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2939 LEFT-SIDE ARC ANTICLOCKWISE ARROW | 2938 RIGHT-SIDE ARC CLOCKWISE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293b BOTTOM ARC ANTICLOCKWISE ARROW | 293a TOP ARC ANTICLOCKWISE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293d TOP ARC ANTICLOCKWISE ARROW WITH PLUS | 293c TOP ARC CLOCKWISE ARROW WITH MINUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 293f LOWER LEFT SEMICIRCULAR ANTICLOCKWISE A | 293e LOWER RIGHT SEMICIRCULAR CLOCKWISE ARRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2941 CLOCKWISE CLOSED CIRCLE ARROW | 2940 ANTICLOCKWISE CLOSED CIRCLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2943 LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS | 2942 RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2945 RIGHTWARDS ARROW WITH PLUS BELOW | 2944 SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2947 RIGHTWARDS ARROW THROUGH X | 2946 LEFTWARDS ARROW WITH PLUS BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2949 UPWARDS TWO-HEADED ARROW FROM SMALL CIR | 2948 LEFT RIGHT ARROW THROUGH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294b LEFT BARB DOWN RIGHT BARB UP HARPOON | 294a LEFT BARB UP RIGHT BARB DOWN HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294d UP BARB LEFT DOWN BARB RIGHT HARPOON | 294c UP BARB RIGHT DOWN BARB LEFT HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 294f UP BARB RIGHT DOWN BARB RIGHT HARPOON | 294e LEFT BARB UP RIGHT BARB UP HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2951 UP BARB LEFT DOWN BARB LEFT HARPOON | 2950 LEFT BARB DOWN RIGHT BARB DOWN HARPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2953 RIGHTWARDS HARPOON WITH BARB UP TO BAR | 2952 LEFTWARDS HARPOON WITH BARB UP TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2955 DOWNWARDS HARPOON WITH BARB RIGHT TO BA | 2954 UPWARDS HARPOON WITH BARB RIGHT TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2957 RIGHTWARDS HARPOON WITH BARB DOWN TO BA | 2956 LEFTWARDS HARPOON WITH BARB DOWN TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2959 DOWNWARDS HARPOON WITH BARB LEFT TO BAR | 2958 UPWARDS HARPOON WITH BARB LEFT TO BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295b RIGHTWARDS HARPOON WITH BARB UP FROM BA | 295a LEFTWARDS HARPOON WITH BARB UP FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295d DOWNWARDS HARPOON WITH BARB RIGHT FROM | 295c UPWARDS HARPOON WITH BARB RIGHT FROM BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 295f RIGHTWARDS HARPOON WITH BARB DOWN FROM | 295e LEFTWARDS HARPOON WITH BARB DOWN FROM B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2961 DOWNWARDS HARPOON WITH BARB LEFT FROM B | 2960 UPWARDS HARPOON WITH BARB LEFT FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2963 UPWARDS HARPOON WITH BARB LEFT BESIDE U | 2962 LEFTWARDS HARPOON WITH BARB UP ABOVE LE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2965 DOWNWARDS HARPOON WITH BARB LEFT BESIDE | 2964 RIGHTWARDS HARPOON WITH BARB UP ABOVE R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2967 LEFTWARDS HARPOON WITH BARB DOWN ABOVE | 2966 LEFTWARDS HARPOON WITH BARB UP ABOVE RI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2969 RIGHTWARDS HARPOON WITH BARB DOWN ABOVE | 2968 RIGHTWARDS HARPOON WITH BARB UP ABOVE L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296b LEFTWARDS HARPOON WITH BARB DOWN BELOW | 296a LEFTWARDS HARPOON WITH BARB UP ABOVE LO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296d RIGHTWARDS HARPOON WITH BARB DOWN BELOW | 296c RIGHTWARDS HARPOON WITH BARB UP ABOVE L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 296f DOWNWARDS HARPOON WITH BARB LEFT BESIDE | 296e UPWARDS HARPOON WITH BARB LEFT BESIDE D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2971 EQUALS SIGN ABOVE RIGHTWARDS ARROW | 2970 RIGHT DOUBLE ARROW WITH ROUNDED HEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2973 LEFTWARDS ARROW ABOVE TILDE OPERATOR | 2972 TILDE OPERATOR ABOVE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2975 RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO | 2974 RIGHTWARDS ARROW ABOVE TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2977 LEFTWARDS ARROW THROUGH LESS-THAN | 2976 LESS-THAN ABOVE LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2979 SUBSET ABOVE RIGHTWARDS ARROW | 2978 GREATER-THAN ABOVE RIGHTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297b SUPERSET ABOVE LEFTWARDS ARROW | 297a LEFTWARDS ARROW THROUGH SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297d RIGHT FISH TAIL | 297c LEFT FISH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 297f DOWN FISH TAIL | 297e UP FISH TAIL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2981 Z NOTATION SPOT | 2980 TRIPLE VERTICAL BAR DELIMITER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* 2983 LEFT WHITE CURLY BRACKET | 2982 Z NOTATION TYPE COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2985 LEFT WHITE PARENTHESIS | 2984 RIGHT WHITE CURLY BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2987 Z NOTATION LEFT IMAGE BRACKET | 2986 RIGHT WHITE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2989 Z NOTATION LEFT BINDING BRACKET | 2988 Z NOTATION RIGHT IMAGE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298b LEFT SQUARE BRACKET WITH UNDERBAR | 298a Z NOTATION RIGHT BINDING BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298d LEFT SQUARE BRACKET WITH TICK IN TOP CO | 298c RIGHT SQUARE BRACKET WITH UNDERBAR */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 298f LEFT SQUARE BRACKET WITH TICK IN BOTTOM | 298e RIGHT SQUARE BRACKET WITH TICK IN BOTTO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2991 LEFT ANGLE BRACKET WITH DOT | 2990 RIGHT SQUARE BRACKET WITH TICK IN TOP C */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2993 LEFT ARC LESS-THAN BRACKET | 2992 RIGHT ANGLE BRACKET WITH DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2995 DOUBLE LEFT ARC GREATER-THAN BRACKET | 2994 RIGHT ARC GREATER-THAN BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2997 LEFT BLACK TORTOISE SHELL BRACKET | 2996 DOUBLE RIGHT ARC LESS-THAN BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2999 DOTTED FENCE | 2998 RIGHT BLACK TORTOISE SHELL BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299b MEASURED ANGLE OPENING LEFT | 299a VERTICAL ZIGZAG LINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299d MEASURED RIGHT ANGLE WITH DOT | 299c RIGHT ANGLE VARIANT WITH SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 299f ACUTE ANGLE | 299e ANGLE WITH S INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a1 SPHERICAL ANGLE OPENING UP | 29a0 SPHERICAL ANGLE OPENING LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a3 REVERSED ANGLE | 29a2 TURNED ANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a5 REVERSED ANGLE WITH UNDERBAR | 29a4 ANGLE WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a7 OBLIQUE ANGLE OPENING DOWN | 29a6 OBLIQUE ANGLE OPENING UP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29a9 MEASURED ANGLE WITH OPEN ARM ENDING IN | 29a8 MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ab MEASURED ANGLE WITH OPEN ARM ENDING IN | 29aa MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ad MEASURED ANGLE WITH OPEN ARM ENDING IN | 29ac MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29af MEASURED ANGLE WITH OPEN ARM ENDING IN | 29ae MEASURED ANGLE WITH OPEN ARM ENDING IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b1 EMPTY SET WITH OVERBAR | 29b0 REVERSED EMPTY SET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b3 EMPTY SET WITH RIGHT ARROW ABOVE | 29b2 EMPTY SET WITH SMALL CIRCLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b5 CIRCLE WITH HORIZONTAL BAR | 29b4 EMPTY SET WITH LEFT ARROW ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b7 CIRCLED PARALLEL | 29b6 CIRCLED VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29b9 CIRCLED PERPENDICULAR | 29b8 CIRCLED REVERSE SOLIDUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bb CIRCLE WITH SUPERIMPOSED X | 29ba CIRCLE DIVIDED BY HORIZONTAL BAR AND TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bd UP ARROW THROUGH CIRCLE | 29bc CIRCLED ANTICLOCKWISE-ROTATED DIVISION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29bf CIRCLED BULLET | 29be CIRCLED WHITE BULLET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c1 CIRCLED GREATER-THAN | 29c0 CIRCLED LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c3 CIRCLE WITH TWO HORIZONTAL STROKES TO T | 29c2 CIRCLE WITH SMALL CIRCLE TO THE RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c5 SQUARED FALLING DIAGONAL SLASH | 29c4 SQUARED RISING DIAGONAL SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c7 SQUARED SMALL CIRCLE | 29c6 SQUARED ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29c9 TWO JOINED SQUARES | 29c8 SQUARED SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cb TRIANGLE WITH UNDERBAR | 29ca TRIANGLE WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cd TRIANGLE WITH SERIFS AT BOTTOM | 29cc S IN TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29cf LEFT TRIANGLE BESIDE VERTICAL BAR | 29ce RIGHT TRIANGLE ABOVE LEFT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d1 BOWTIE WITH LEFT HALF BLACK | 29d0 VERTICAL BAR BESIDE RIGHT TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d3 BLACK BOWTIE | 29d2 BOWTIE WITH RIGHT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d5 TIMES WITH RIGHT HALF BLACK | 29d4 TIMES WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29d7 BLACK HOURGLASS | 29d6 WHITE HOURGLASS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29d9 RIGHT WIGGLY FENCE | 29d8 LEFT WIGGLY FENCE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29db RIGHT DOUBLE WIGGLY FENCE | 29da LEFT DOUBLE WIGGLY FENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29dd TIE OVER INFINITY | 29dc INCOMPLETE INFINITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29df DOUBLE-ENDED MULTIMAP | 29de INFINITY NEGATED WITH VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e1 INCREASES AS | 29e0 SQUARE WITH CONTOURED OUTLINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e3 EQUALS SIGN AND SLANTED PARALLEL | 29e2 SHUFFLE PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e5 IDENTICAL TO AND SLANTED PARALLEL | 29e4 EQUALS SIGN AND SLANTED PARALLEL WITH T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e7 THERMODYNAMIC | 29e6 GLEICH STARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29e9 DOWN-POINTING TRIANGLE WITH RIGHT HALF | 29e8 DOWN-POINTING TRIANGLE WITH LEFT HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29eb BLACK LOZENGE | 29ea BLACK DIAMOND WITH DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ed BLACK CIRCLE WITH DOWN ARROW | 29ec WHITE CIRCLE WITH DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ef ERROR-BARRED BLACK SQUARE | 29ee ERROR-BARRED WHITE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f1 ERROR-BARRED BLACK DIAMOND | 29f0 ERROR-BARRED WHITE DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f3 ERROR-BARRED BLACK CIRCLE | 29f2 ERROR-BARRED WHITE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f5 REVERSE SOLIDUS OPERATOR | 29f4 RULE-DELAYED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f7 REVERSE SOLIDUS WITH HORIZONTAL STROKE | 29f6 SOLIDUS WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29f9 BIG REVERSE SOLIDUS | 29f8 BIG SOLIDUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29fb TRIPLE PLUS | 29fa DOUBLE PLUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 29fd RIGHT-POINTING CURVED ANGLE BRACKET | 29fc LEFT-POINTING CURVED ANGLE BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 29ff MINY | 29fe TINY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a01 N-ARY CIRCLED PLUS OPERATOR | 2a00 N-ARY CIRCLED DOT OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a03 N-ARY UNION OPERATOR WITH DOT | 2a02 N-ARY CIRCLED TIMES OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a05 N-ARY SQUARE INTERSECTION OPERATOR | 2a04 N-ARY UNION OPERATOR WITH PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a07 TWO LOGICAL AND OPERATOR | 2a06 N-ARY SQUARE UNION OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a09 N-ARY TIMES OPERATOR | 2a08 TWO LOGICAL OR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0b SUMMATION WITH INTEGRAL | 2a0a MODULO TWO SUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0d FINITE PART INTEGRAL | 2a0c QUADRUPLE INTEGRAL OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a0f INTEGRAL AVERAGE WITH SLASH | 2a0e INTEGRAL WITH DOUBLE STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a11 ANTICLOCKWISE INTEGRATION | 2a10 CIRCULATION FUNCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a13 LINE INTEGRATION WITH SEMICIRCULAR PATH | 2a12 LINE INTEGRATION WITH RECTANGULAR PATH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a15 INTEGRAL AROUND A POINT OPERATOR | 2a14 LINE INTEGRATION NOT INCLUDING THE POLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a17 INTEGRAL WITH LEFTWARDS ARROW WITH HOOK | 2a16 QUATERNION INTEGRAL OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a19 INTEGRAL WITH INTERSECTION | 2a18 INTEGRAL WITH TIMES SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1b INTEGRAL WITH OVERBAR | 2a1a INTEGRAL WITH UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1d JOIN | 2a1c INTEGRAL WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a1f Z NOTATION SCHEMA COMPOSITION | 2a1e LARGE LEFT TRIANGLE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a21 Z NOTATION SCHEMA PROJECTION | 2a20 Z NOTATION SCHEMA PIPING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a23 PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE | 2a22 PLUS SIGN WITH SMALL CIRCLE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a25 PLUS SIGN WITH DOT BELOW | 2a24 PLUS SIGN WITH TILDE ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a27 PLUS SIGN WITH SUBSCRIPT TWO | 2a26 PLUS SIGN WITH TILDE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a29 MINUS SIGN WITH COMMA ABOVE | 2a28 PLUS SIGN WITH BLACK TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2b MINUS SIGN WITH FALLING DOTS | 2a2a MINUS SIGN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2d PLUS SIGN IN LEFT HALF CIRCLE | 2a2c MINUS SIGN WITH RISING DOTS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a2f VECTOR OR CROSS PRODUCT | 2a2e PLUS SIGN IN RIGHT HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a31 MULTIPLICATION SIGN WITH UNDERBAR | 2a30 MULTIPLICATION SIGN WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a33 SMASH PRODUCT | 2a32 SEMIDIRECT PRODUCT WITH BOTTOM CLOSED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a35 MULTIPLICATION SIGN IN RIGHT HALF CIRCL | 2a34 MULTIPLICATION SIGN IN LEFT HALF CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a37 MULTIPLICATION SIGN IN DOUBLE CIRCLE | 2a36 CIRCLED MULTIPLICATION SIGN WITH CIRCUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a39 PLUS SIGN IN TRIANGLE | 2a38 CIRCLED DIVISION SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3b MULTIPLICATION SIGN IN TRIANGLE | 2a3a MINUS SIGN IN TRIANGLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3d RIGHTHAND INTERIOR PRODUCT | 2a3c INTERIOR PRODUCT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a3f AMALGAMATION OR COPRODUCT | 2a3e Z NOTATION RELATIONAL COMPOSITION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a41 UNION WITH MINUS SIGN | 2a40 INTERSECTION WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a43 INTERSECTION WITH OVERBAR | 2a42 UNION WITH OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a45 UNION WITH LOGICAL OR | 2a44 INTERSECTION WITH LOGICAL AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a47 INTERSECTION ABOVE UNION | 2a46 UNION ABOVE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a49 INTERSECTION ABOVE BAR ABOVE UNION | 2a48 UNION ABOVE BAR ABOVE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4b INTERSECTION BESIDE AND JOINED WITH INT | 2a4a UNION BESIDE AND JOINED WITH UNION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4d CLOSED INTERSECTION WITH SERIFS | 2a4c CLOSED UNION WITH SERIFS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a4f DOUBLE SQUARE UNION | 2a4e DOUBLE SQUARE INTERSECTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a51 LOGICAL AND WITH DOT ABOVE | 2a50 CLOSED UNION WITH SERIFS AND SMASH PROD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a53 DOUBLE LOGICAL AND | 2a52 LOGICAL OR WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a55 TWO INTERSECTING LOGICAL AND | 2a54 DOUBLE LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a57 SLOPING LARGE OR | 2a56 TWO INTERSECTING LOGICAL OR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a59 LOGICAL OR OVERLAPPING LOGICAL AND | 2a58 SLOPING LARGE AND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5b LOGICAL OR WITH MIDDLE STEM | 2a5a LOGICAL AND WITH MIDDLE STEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5d LOGICAL OR WITH HORIZONTAL DASH | 2a5c LOGICAL AND WITH HORIZONTAL DASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a5f LOGICAL AND WITH UNDERBAR | 2a5e LOGICAL AND WITH DOUBLE OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a61 SMALL VEE WITH UNDERBAR | 2a60 LOGICAL AND WITH DOUBLE UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a63 LOGICAL OR WITH DOUBLE UNDERBAR | 2a62 LOGICAL OR WITH DOUBLE OVERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a65 Z NOTATION RANGE ANTIRESTRICTION | 2a64 Z NOTATION DOMAIN ANTIRESTRICTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a67 IDENTICAL WITH DOT ABOVE | 2a66 EQUALS SIGN WITH DOT BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a69 TRIPLE HORIZONTAL BAR WITH TRIPLE VERTI | 2a68 TRIPLE HORIZONTAL BAR WITH DOUBLE VERTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6b TILDE OPERATOR WITH RISING DOTS | 2a6a TILDE OPERATOR WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6d CONGRUENT WITH DOT ABOVE | 2a6c SIMILAR MINUS SIMILAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a6f ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT | 2a6e EQUALS WITH ASTERISK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a71 EQUALS SIGN ABOVE PLUS SIGN | 2a70 APPROXIMATELY EQUAL OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a73 EQUALS SIGN ABOVE TILDE OPERATOR | 2a72 PLUS SIGN ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a75 TWO CONSECUTIVE EQUALS SIGNS | 2a74 DOUBLE COLON EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a77 EQUALS SIGN WITH TWO DOTS ABOVE AND TWO | 2a76 THREE CONSECUTIVE EQUALS SIGNS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a79 LESS-THAN WITH CIRCLE INSIDE | 2a78 EQUIVALENT WITH FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7b LESS-THAN WITH QUESTION MARK ABOVE | 2a7a GREATER-THAN WITH CIRCLE INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7d LESS-THAN OR SLANTED EQUAL TO | 2a7c GREATER-THAN WITH QUESTION MARK ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a7f LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a7e GREATER-THAN OR SLANTED EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a81 LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a80 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a83 LESS-THAN OR SLANTED EQUAL TO WITH DOT | 2a82 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a85 LESS-THAN OR APPROXIMATE | 2a84 GREATER-THAN OR SLANTED EQUAL TO WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a87 LESS-THAN AND SINGLE-LINE NOT EQUAL TO | 2a86 GREATER-THAN OR APPROXIMATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a89 LESS-THAN AND NOT APPROXIMATE | 2a88 GREATER-THAN AND SINGLE-LINE NOT EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8b LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE | 2a8a GREATER-THAN AND NOT APPROXIMATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8d LESS-THAN ABOVE SIMILAR OR EQUAL | 2a8c GREATER-THAN ABOVE DOUBLE-LINE EQUAL AB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a8f LESS-THAN ABOVE SIMILAR ABOVE GREATER-T | 2a8e GREATER-THAN ABOVE SIMILAR OR EQUAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a91 LESS-THAN ABOVE GREATER-THAN ABOVE DOUB | 2a90 GREATER-THAN ABOVE SIMILAR ABOVE LESS-T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a93 LESS-THAN ABOVE SLANTED EQUAL ABOVE GRE | 2a92 GREATER-THAN ABOVE LESS-THAN ABOVE DOUB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a95 SLANTED EQUAL TO OR LESS-THAN | 2a94 GREATER-THAN ABOVE SLANTED EQUAL ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a97 SLANTED EQUAL TO OR LESS-THAN WITH DOT | 2a96 SLANTED EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a99 DOUBLE-LINE EQUAL TO OR LESS-THAN | 2a98 SLANTED EQUAL TO OR GREATER-THAN WITH D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9b DOUBLE-LINE SLANTED EQUAL TO OR LESS-TH | 2a9a DOUBLE-LINE EQUAL TO OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9d SIMILAR OR LESS-THAN | 2a9c DOUBLE-LINE SLANTED EQUAL TO OR GREATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2a9f SIMILAR ABOVE LESS-THAN ABOVE EQUALS SI | 2a9e SIMILAR OR GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa1 DOUBLE NESTED LESS-THAN | 2aa0 SIMILAR ABOVE GREATER-THAN ABOVE EQUALS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa3 DOUBLE NESTED LESS-THAN WITH UNDERBAR | 2aa2 DOUBLE NESTED GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa5 GREATER-THAN BESIDE LESS-THAN | 2aa4 GREATER-THAN OVERLAPPING LESS-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa7 GREATER-THAN CLOSED BY CURVE | 2aa6 LESS-THAN CLOSED BY CURVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aa9 GREATER-THAN CLOSED BY CURVE ABOVE SLAN | 2aa8 LESS-THAN CLOSED BY CURVE ABOVE SLANTED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aab LARGER THAN | 2aaa SMALLER THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aad LARGER THAN OR EQUAL TO | 2aac SMALLER THAN OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aaf PRECEDES ABOVE SINGLE-LINE EQUALS SIGN | 2aae EQUALS SIGN WITH BUMPY ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab1 PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO | 2ab0 SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab3 PRECEDES ABOVE EQUALS SIGN | 2ab2 SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab5 PRECEDES ABOVE NOT EQUAL TO | 2ab4 SUCCEEDS ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab7 PRECEDES ABOVE ALMOST EQUAL TO | 2ab6 SUCCEEDS ABOVE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ab9 PRECEDES ABOVE NOT ALMOST EQUAL TO | 2ab8 SUCCEEDS ABOVE ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abb DOUBLE PRECEDES | 2aba SUCCEEDS ABOVE NOT ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abd SUBSET WITH DOT | 2abc DOUBLE SUCCEEDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2abf SUBSET WITH PLUS SIGN BELOW | 2abe SUPERSET WITH DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac1 SUBSET WITH MULTIPLICATION SIGN BELOW | 2ac0 SUPERSET WITH PLUS SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac3 SUBSET OF OR EQUAL TO WITH DOT ABOVE | 2ac2 SUPERSET WITH MULTIPLICATION SIGN BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac5 SUBSET OF ABOVE EQUALS SIGN | 2ac4 SUPERSET OF OR EQUAL TO WITH DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac7 SUBSET OF ABOVE TILDE OPERATOR | 2ac6 SUPERSET OF ABOVE EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ac9 SUBSET OF ABOVE ALMOST EQUAL TO | 2ac8 SUPERSET OF ABOVE TILDE OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acb SUBSET OF ABOVE NOT EQUAL TO | 2aca SUPERSET OF ABOVE ALMOST EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acd SQUARE LEFT OPEN BOX OPERATOR | 2acc SUPERSET OF ABOVE NOT EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2acf CLOSED SUBSET | 2ace SQUARE RIGHT OPEN BOX OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad1 CLOSED SUBSET OR EQUAL TO | 2ad0 CLOSED SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad3 SUBSET ABOVE SUPERSET | 2ad2 CLOSED SUPERSET OR EQUAL TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad5 SUBSET ABOVE SUBSET | 2ad4 SUPERSET ABOVE SUBSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad7 SUPERSET BESIDE SUBSET | 2ad6 SUPERSET ABOVE SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ad9 ELEMENT OF OPENING DOWNWARDS | 2ad8 SUPERSET BESIDE AND JOINED BY DASH WITH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2adb TRANSVERSAL INTERSECTION | 2ada PITCHFORK WITH TEE TOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2add NONFORKING | 2adc FORKING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2adf SHORT DOWN TACK | 2ade SHORT LEFT TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae1 PERPENDICULAR WITH S | 2ae0 SHORT UP TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae3 DOUBLE VERTICAL BAR LEFT TURNSTILE | 2ae2 VERTICAL BAR TRIPLE RIGHT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae5 DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTI | 2ae4 VERTICAL BAR DOUBLE LEFT TURNSTILE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae7 SHORT DOWN TACK WITH OVERBAR | 2ae6 LONG DASH FROM LEFT MEMBER OF DOUBLE VE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ae9 SHORT UP TACK ABOVE SHORT DOWN TACK | 2ae8 SHORT UP TACK WITH UNDERBAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aeb DOUBLE UP TACK | 2aea DOUBLE DOWN TACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aed REVERSED DOUBLE STROKE NOT SIGN | 2aec DOUBLE STROKE NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aef VERTICAL LINE WITH CIRCLE ABOVE | 2aee DOES NOT DIVIDE WITH REVERSED NEGATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af1 DOWN TACK WITH CIRCLE BELOW | 2af0 VERTICAL LINE WITH CIRCLE BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af3 PARALLEL WITH TILDE OPERATOR | 2af2 PARALLEL WITH HORIZONTAL STROKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af5 TRIPLE VERTICAL BAR WITH HORIZONTAL STR | 2af4 TRIPLE VERTICAL BAR BINARY RELATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af7 TRIPLE NESTED LESS-THAN | 2af6 TRIPLE COLON OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2af9 DOUBLE-LINE SLANTED LESS-THAN OR EQUAL | 2af8 TRIPLE NESTED GREATER-THAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2afb TRIPLE SOLIDUS BINARY RELATION | 2afa DOUBLE-LINE SLANTED GREATER-THAN OR EQU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2afd DOUBLE SOLIDUS OPERATOR | 2afc LARGE TRIPLE VERTICAL BAR OPERATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2aff N-ARY WHITE VERTICAL BAR | 2afe WHITE VERTICAL BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b01 NORTH WEST WHITE ARROW | 2b00 NORTH EAST WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b03 SOUTH WEST WHITE ARROW | 2b02 SOUTH EAST WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b05 LEFTWARDS BLACK ARROW | 2b04 LEFT RIGHT WHITE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b07 DOWNWARDS BLACK ARROW | 2b06 UPWARDS BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b09 NORTH WEST BLACK ARROW | 2b08 NORTH EAST BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0b SOUTH WEST BLACK ARROW | 2b0a SOUTH EAST BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0d UP DOWN BLACK ARROW | 2b0c LEFT RIGHT BLACK ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b0f RIGHTWARDS ARROW WITH TIP UPWARDS | 2b0e RIGHTWARDS ARROW WITH TIP DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b11 LEFTWARDS ARROW WITH TIP UPWARDS | 2b10 LEFTWARDS ARROW WITH TIP DOWNWARDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b13 SQUARE WITH BOTTOM HALF BLACK | 2b12 SQUARE WITH TOP HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b15 SQUARE WITH LOWER LEFT DIAGONAL HALF BL | 2b14 SQUARE WITH UPPER RIGHT DIAGONAL HALF B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b17 DIAMOND WITH RIGHT HALF BLACK | 2b16 DIAMOND WITH LEFT HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b19 DIAMOND WITH BOTTOM HALF BLACK | 2b18 DIAMOND WITH TOP HALF BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1b BLACK LARGE SQUARE | 2b1a DOTTED SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1d BLACK VERY SMALL SQUARE | 2b1c WHITE LARGE SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b1f BLACK PENTAGON | 2b1e WHITE VERY SMALL SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b21 WHITE HEXAGON | 2b20 WHITE PENTAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b23 HORIZONTAL BLACK HEXAGON | 2b22 BLACK HEXAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b25 BLACK MEDIUM DIAMOND | 2b24 BLACK LARGE CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b27 BLACK MEDIUM LOZENGE | 2b26 WHITE MEDIUM DIAMOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b29 BLACK SMALL DIAMOND | 2b28 WHITE MEDIUM LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2b WHITE SMALL LOZENGE | 2b2a BLACK SMALL LOZENGE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2d WHITE HORIZONTAL ELLIPSE | 2b2c BLACK HORIZONTAL ELLIPSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b2f WHITE VERTICAL ELLIPSE | 2b2e BLACK VERTICAL ELLIPSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b31 THREE LEFTWARDS ARROWS | 2b30 LEFT ARROW WITH SMALL CIRCLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b33 LONG LEFTWARDS SQUIGGLE ARROW | 2b32 LEFT ARROW WITH CIRCLED PLUS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b35 LEFTWARDS TWO-HEADED ARROW WITH DOUBLE | 2b34 LEFTWARDS TWO-HEADED ARROW WITH VERTICA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b37 LEFTWARDS TWO-HEADED TRIPLE DASH ARROW | 2b36 LEFTWARDS TWO-HEADED ARROW FROM BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b39 LEFTWARDS ARROW WITH TAIL WITH VERTICAL | 2b38 LEFTWARDS ARROW WITH DOTTED STEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3b LEFTWARDS TWO-HEADED ARROW WITH TAIL | 2b3a LEFTWARDS ARROW WITH TAIL WITH DOUBLE V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3d LEFTWARDS TWO-HEADED ARROW WITH TAIL WI | 2b3c LEFTWARDS TWO-HEADED ARROW WITH TAIL WI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b3f WAVE ARROW POINTING DIRECTLY LEFT | 2b3e LEFTWARDS ARROW THROUGH X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b41 REVERSE TILDE OPERATOR ABOVE LEFTWARDS | 2b40 EQUALS SIGN ABOVE LEFTWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b43 RIGHTWARDS ARROW THROUGH GREATER-THAN | 2b42 LEFTWARDS ARROW ABOVE REVERSE ALMOST EQ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b45 LEFTWARDS QUADRUPLE ARROW | 2b44 RIGHTWARDS ARROW THROUGH SUPERSET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b47 REVERSE TILDE OPERATOR ABOVE RIGHTWARDS | 2b46 RIGHTWARDS QUADRUPLE ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b49 TILDE OPERATOR ABOVE LEFTWARDS ARROW | 2b48 RIGHTWARDS ARROW ABOVE REVERSE ALMOST E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b4b LEFTWARDS ARROW ABOVE REVERSE TILDE OPE | 2b4a LEFTWARDS ARROW ABOVE ALMOST EQUAL TO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 2b4d (null) | 2b4c RIGHTWARDS ARROW ABOVE REVERSE TILDE OP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b4f (null) | 2b4e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b51 BLACK SMALL STAR | 2b50 WHITE MEDIUM STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b53 BLACK RIGHT-POINTING PENTAGON | 2b52 WHITE SMALL STAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b55 HEAVY LARGE CIRCLE | 2b54 WHITE RIGHT-POINTING PENTAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b57 HEAVY CIRCLE WITH CIRCLE INSIDE | 2b56 HEAVY OVAL WITH OVAL INSIDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2b59 HEAVY CIRCLED SALTIRE | 2b58 HEAVY CIRCLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5b (null) | 2b5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5d (null) | 2b5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b5f (null) | 2b5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b61 (null) | 2b60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b63 (null) | 2b62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b65 (null) | 2b64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b67 (null) | 2b66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b69 (null) | 2b68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6b (null) | 2b6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6d (null) | 2b6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b6f (null) | 2b6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b71 (null) | 2b70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b73 (null) | 2b72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b75 (null) | 2b74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b77 (null) | 2b76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b79 (null) | 2b78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7b (null) | 2b7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7d (null) | 2b7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b7f (null) | 2b7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b81 (null) | 2b80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b83 (null) | 2b82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b85 (null) | 2b84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b87 (null) | 2b86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b89 (null) | 2b88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8b (null) | 2b8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8d (null) | 2b8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b8f (null) | 2b8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b91 (null) | 2b90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b93 (null) | 2b92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b95 (null) | 2b94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b97 (null) | 2b96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b99 (null) | 2b98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9b (null) | 2b9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9d (null) | 2b9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2b9f (null) | 2b9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba1 (null) | 2ba0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba3 (null) | 2ba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba5 (null) | 2ba4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba7 (null) | 2ba6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ba9 (null) | 2ba8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bab (null) | 2baa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bad (null) | 2bac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2baf (null) | 2bae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb1 (null) | 2bb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb3 (null) | 2bb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb5 (null) | 2bb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb7 (null) | 2bb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bb9 (null) | 2bb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbb (null) | 2bba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbd (null) | 2bbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bbf (null) | 2bbe (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc1 (null) | 2bc0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc3 (null) | 2bc2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc5 (null) | 2bc4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc7 (null) | 2bc6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bc9 (null) | 2bc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcb (null) | 2bca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcd (null) | 2bcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bcf (null) | 2bce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd1 (null) | 2bd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd3 (null) | 2bd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd5 (null) | 2bd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd7 (null) | 2bd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bd9 (null) | 2bd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdb (null) | 2bda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdd (null) | 2bdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bdf (null) | 2bde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be1 (null) | 2be0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be3 (null) | 2be2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be5 (null) | 2be4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be7 (null) | 2be6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2be9 (null) | 2be8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2beb (null) | 2bea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bed (null) | 2bec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bef (null) | 2bee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf1 (null) | 2bf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf3 (null) | 2bf2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf5 (null) | 2bf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf7 (null) | 2bf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bf9 (null) | 2bf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bfb (null) | 2bfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bfd (null) | 2bfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2bff (null) | 2bfe (null) */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c01 GLAGOLITIC CAPITAL LETTER BUKY | 2c00 GLAGOLITIC CAPITAL LETTER AZU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c03 GLAGOLITIC CAPITAL LETTER GLAGOLI | 2c02 GLAGOLITIC CAPITAL LETTER VEDE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c05 GLAGOLITIC CAPITAL LETTER YESTU | 2c04 GLAGOLITIC CAPITAL LETTER DOBRO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c07 GLAGOLITIC CAPITAL LETTER DZELO | 2c06 GLAGOLITIC CAPITAL LETTER ZHIVETE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c09 GLAGOLITIC CAPITAL LETTER IZHE | 2c08 GLAGOLITIC CAPITAL LETTER ZEMLJA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0b GLAGOLITIC CAPITAL LETTER I | 2c0a GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0d GLAGOLITIC CAPITAL LETTER KAKO | 2c0c GLAGOLITIC CAPITAL LETTER DJERVI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c0f GLAGOLITIC CAPITAL LETTER MYSLITE | 2c0e GLAGOLITIC CAPITAL LETTER LJUDIJE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c11 GLAGOLITIC CAPITAL LETTER ONU | 2c10 GLAGOLITIC CAPITAL LETTER NASHI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c13 GLAGOLITIC CAPITAL LETTER RITSI | 2c12 GLAGOLITIC CAPITAL LETTER POKOJI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c15 GLAGOLITIC CAPITAL LETTER TVRIDO | 2c14 GLAGOLITIC CAPITAL LETTER SLOVO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c17 GLAGOLITIC CAPITAL LETTER FRITU | 2c16 GLAGOLITIC CAPITAL LETTER UKU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c19 GLAGOLITIC CAPITAL LETTER OTU | 2c18 GLAGOLITIC CAPITAL LETTER HERU */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1b GLAGOLITIC CAPITAL LETTER SHTA | 2c1a GLAGOLITIC CAPITAL LETTER PE */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1d GLAGOLITIC CAPITAL LETTER CHRIVI | 2c1c GLAGOLITIC CAPITAL LETTER TSI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c1f GLAGOLITIC CAPITAL LETTER YERU | 2c1e GLAGOLITIC CAPITAL LETTER SHA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c21 GLAGOLITIC CAPITAL LETTER YATI | 2c20 GLAGOLITIC CAPITAL LETTER YERI */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c23 GLAGOLITIC CAPITAL LETTER YU | 2c22 GLAGOLITIC CAPITAL LETTER SPIDERY HA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c25 GLAGOLITIC CAPITAL LETTER SMALL YUS WIT | 2c24 GLAGOLITIC CAPITAL LETTER SMALL YUS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c27 GLAGOLITIC CAPITAL LETTER IOTATED SMALL | 2c26 GLAGOLITIC CAPITAL LETTER YO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c29 GLAGOLITIC CAPITAL LETTER IOTATED BIG Y | 2c28 GLAGOLITIC CAPITAL LETTER BIG YUS */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c2b GLAGOLITIC CAPITAL LETTER IZHITSA | 2c2a GLAGOLITIC CAPITAL LETTER FITA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c2d GLAGOLITIC CAPITAL LETTER TROKUTASTI A | 2c2c GLAGOLITIC CAPITAL LETTER SHTAPIC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* 2c2f (null) | 2c2e GLAGOLITIC CAPITAL LETTER LATINATE MYSL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c31 GLAGOLITIC SMALL LETTER BUKY | 2c30 GLAGOLITIC SMALL LETTER AZU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c33 GLAGOLITIC SMALL LETTER GLAGOLI | 2c32 GLAGOLITIC SMALL LETTER VEDE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c35 GLAGOLITIC SMALL LETTER YESTU | 2c34 GLAGOLITIC SMALL LETTER DOBRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c37 GLAGOLITIC SMALL LETTER DZELO | 2c36 GLAGOLITIC SMALL LETTER ZHIVETE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c39 GLAGOLITIC SMALL LETTER IZHE | 2c38 GLAGOLITIC SMALL LETTER ZEMLJA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3b GLAGOLITIC SMALL LETTER I | 2c3a GLAGOLITIC SMALL LETTER INITIAL IZHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3d GLAGOLITIC SMALL LETTER KAKO | 2c3c GLAGOLITIC SMALL LETTER DJERVI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c3f GLAGOLITIC SMALL LETTER MYSLITE | 2c3e GLAGOLITIC SMALL LETTER LJUDIJE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c41 GLAGOLITIC SMALL LETTER ONU | 2c40 GLAGOLITIC SMALL LETTER NASHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c43 GLAGOLITIC SMALL LETTER RITSI | 2c42 GLAGOLITIC SMALL LETTER POKOJI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c45 GLAGOLITIC SMALL LETTER TVRIDO | 2c44 GLAGOLITIC SMALL LETTER SLOVO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c47 GLAGOLITIC SMALL LETTER FRITU | 2c46 GLAGOLITIC SMALL LETTER UKU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c49 GLAGOLITIC SMALL LETTER OTU | 2c48 GLAGOLITIC SMALL LETTER HERU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4b GLAGOLITIC SMALL LETTER SHTA | 2c4a GLAGOLITIC SMALL LETTER PE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4d GLAGOLITIC SMALL LETTER CHRIVI | 2c4c GLAGOLITIC SMALL LETTER TSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c4f GLAGOLITIC SMALL LETTER YERU | 2c4e GLAGOLITIC SMALL LETTER SHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c51 GLAGOLITIC SMALL LETTER YATI | 2c50 GLAGOLITIC SMALL LETTER YERI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c53 GLAGOLITIC SMALL LETTER YU | 2c52 GLAGOLITIC SMALL LETTER SPIDERY HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c55 GLAGOLITIC SMALL LETTER SMALL YUS WITH | 2c54 GLAGOLITIC SMALL LETTER SMALL YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c57 GLAGOLITIC SMALL LETTER IOTATED SMALL Y | 2c56 GLAGOLITIC SMALL LETTER YO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c59 GLAGOLITIC SMALL LETTER IOTATED BIG YUS | 2c58 GLAGOLITIC SMALL LETTER BIG YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c5b GLAGOLITIC SMALL LETTER IZHITSA | 2c5a GLAGOLITIC SMALL LETTER FITA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c5d GLAGOLITIC SMALL LETTER TROKUTASTI A | 2c5c GLAGOLITIC SMALL LETTER SHTAPIC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* 2c5f (null) | 2c5e GLAGOLITIC SMALL LETTER LATINATE MYSLIT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c61 LATIN SMALL LETTER L WITH DOUBLE BAR | 2c60 LATIN CAPITAL LETTER L WITH DOUBLE BAR */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c63 LATIN CAPITAL LETTER P WITH STROKE | 2c62 LATIN CAPITAL LETTER L WITH MIDDLE TILD */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c65 LATIN SMALL LETTER A WITH STROKE | 2c64 LATIN CAPITAL LETTER R WITH TAIL */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c67 LATIN CAPITAL LETTER H WITH DESCENDER | 2c66 LATIN SMALL LETTER T WITH DIAGONAL STRO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c69 LATIN CAPITAL LETTER K WITH DESCENDER | 2c68 LATIN SMALL LETTER H WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c6b LATIN CAPITAL LETTER Z WITH DESCENDER | 2c6a LATIN SMALL LETTER K WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c6d LATIN CAPITAL LETTER ALPHA | 2c6c LATIN SMALL LETTER Z WITH DESCENDER */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c6f LATIN CAPITAL LETTER TURNED A | 2c6e LATIN CAPITAL LETTER M WITH HOOK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c71 LATIN SMALL LETTER V WITH RIGHT HOOK | 2c70 LATIN CAPITAL LETTER TURNED ALPHA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c73 LATIN SMALL LETTER W WITH HOOK | 2c72 LATIN CAPITAL LETTER W WITH HOOK */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2c75 LATIN CAPITAL LETTER HALF H | 2c74 LATIN SMALL LETTER V WITH CURL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c77 LATIN SMALL LETTER TAILLESS PHI | 2c76 LATIN SMALL LETTER HALF H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c79 LATIN SMALL LETTER TURNED R WITH TAIL | 2c78 LATIN SMALL LETTER E WITH NOTCH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2c7b LATIN LETTER SMALL CAPITAL TURNED E | 2c7a LATIN SMALL LETTER O WITH LOW RING INSI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2c7d MODIFIER LETTER CAPITAL V | 2c7c LATIN SUBSCRIPT SMALL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* 2c7f LATIN CAPITAL LETTER Z WITH SWASH TAIL | 2c7e LATIN CAPITAL LETTER S WITH SWASH TAIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c81 COPTIC SMALL LETTER ALFA | 2c80 COPTIC CAPITAL LETTER ALFA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c83 COPTIC SMALL LETTER VIDA | 2c82 COPTIC CAPITAL LETTER VIDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c85 COPTIC SMALL LETTER GAMMA | 2c84 COPTIC CAPITAL LETTER GAMMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c87 COPTIC SMALL LETTER DALDA | 2c86 COPTIC CAPITAL LETTER DALDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c89 COPTIC SMALL LETTER EIE | 2c88 COPTIC CAPITAL LETTER EIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8b COPTIC SMALL LETTER SOU | 2c8a COPTIC CAPITAL LETTER SOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8d COPTIC SMALL LETTER ZATA | 2c8c COPTIC CAPITAL LETTER ZATA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c8f COPTIC SMALL LETTER HATE | 2c8e COPTIC CAPITAL LETTER HATE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c91 COPTIC SMALL LETTER THETHE | 2c90 COPTIC CAPITAL LETTER THETHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c93 COPTIC SMALL LETTER IAUDA | 2c92 COPTIC CAPITAL LETTER IAUDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c95 COPTIC SMALL LETTER KAPA | 2c94 COPTIC CAPITAL LETTER KAPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c97 COPTIC SMALL LETTER LAULA | 2c96 COPTIC CAPITAL LETTER LAULA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c99 COPTIC SMALL LETTER MI | 2c98 COPTIC CAPITAL LETTER MI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9b COPTIC SMALL LETTER NI | 2c9a COPTIC CAPITAL LETTER NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9d COPTIC SMALL LETTER KSI | 2c9c COPTIC CAPITAL LETTER KSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2c9f COPTIC SMALL LETTER O | 2c9e COPTIC CAPITAL LETTER O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca1 COPTIC SMALL LETTER PI | 2ca0 COPTIC CAPITAL LETTER PI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca3 COPTIC SMALL LETTER RO | 2ca2 COPTIC CAPITAL LETTER RO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca5 COPTIC SMALL LETTER SIMA | 2ca4 COPTIC CAPITAL LETTER SIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca7 COPTIC SMALL LETTER TAU | 2ca6 COPTIC CAPITAL LETTER TAU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ca9 COPTIC SMALL LETTER UA | 2ca8 COPTIC CAPITAL LETTER UA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cab COPTIC SMALL LETTER FI | 2caa COPTIC CAPITAL LETTER FI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cad COPTIC SMALL LETTER KHI | 2cac COPTIC CAPITAL LETTER KHI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2caf COPTIC SMALL LETTER PSI | 2cae COPTIC CAPITAL LETTER PSI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb1 COPTIC SMALL LETTER OOU | 2cb0 COPTIC CAPITAL LETTER OOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb3 COPTIC SMALL LETTER DIALECT-P ALEF | 2cb2 COPTIC CAPITAL LETTER DIALECT-P ALEF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb5 COPTIC SMALL LETTER OLD COPTIC AIN | 2cb4 COPTIC CAPITAL LETTER OLD COPTIC AIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb7 COPTIC SMALL LETTER CRYPTOGRAMMIC EIE | 2cb6 COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cb9 COPTIC SMALL LETTER DIALECT-P KAPA | 2cb8 COPTIC CAPITAL LETTER DIALECT-P KAPA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbb COPTIC SMALL LETTER DIALECT-P NI | 2cba COPTIC CAPITAL LETTER DIALECT-P NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbd COPTIC SMALL LETTER CRYPTOGRAMMIC NI | 2cbc COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cbf COPTIC SMALL LETTER OLD COPTIC OOU | 2cbe COPTIC CAPITAL LETTER OLD COPTIC OOU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc1 COPTIC SMALL LETTER SAMPI | 2cc0 COPTIC CAPITAL LETTER SAMPI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc3 COPTIC SMALL LETTER CROSSED SHEI | 2cc2 COPTIC CAPITAL LETTER CROSSED SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc5 COPTIC SMALL LETTER OLD COPTIC SHEI | 2cc4 COPTIC CAPITAL LETTER OLD COPTIC SHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc7 COPTIC SMALL LETTER OLD COPTIC ESH | 2cc6 COPTIC CAPITAL LETTER OLD COPTIC ESH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cc9 COPTIC SMALL LETTER AKHMIMIC KHEI | 2cc8 COPTIC CAPITAL LETTER AKHMIMIC KHEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccb COPTIC SMALL LETTER DIALECT-P HORI | 2cca COPTIC CAPITAL LETTER DIALECT-P HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccd COPTIC SMALL LETTER OLD COPTIC HORI | 2ccc COPTIC CAPITAL LETTER OLD COPTIC HORI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ccf COPTIC SMALL LETTER OLD COPTIC HA | 2cce COPTIC CAPITAL LETTER OLD COPTIC HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd1 COPTIC SMALL LETTER L-SHAPED HA | 2cd0 COPTIC CAPITAL LETTER L-SHAPED HA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd3 COPTIC SMALL LETTER OLD COPTIC HEI | 2cd2 COPTIC CAPITAL LETTER OLD COPTIC HEI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd5 COPTIC SMALL LETTER OLD COPTIC HAT | 2cd4 COPTIC CAPITAL LETTER OLD COPTIC HAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd7 COPTIC SMALL LETTER OLD COPTIC GANGIA | 2cd6 COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cd9 COPTIC SMALL LETTER OLD COPTIC DJA | 2cd8 COPTIC CAPITAL LETTER OLD COPTIC DJA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdb COPTIC SMALL LETTER OLD COPTIC SHIMA | 2cda COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdd COPTIC SMALL LETTER OLD NUBIAN SHIMA | 2cdc COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cdf COPTIC SMALL LETTER OLD NUBIAN NGI | 2cde COPTIC CAPITAL LETTER OLD NUBIAN NGI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ce1 COPTIC SMALL LETTER OLD NUBIAN NYI | 2ce0 COPTIC CAPITAL LETTER OLD NUBIAN NYI */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2ce3 COPTIC SMALL LETTER OLD NUBIAN WAU | 2ce2 COPTIC CAPITAL LETTER OLD NUBIAN WAU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2ce5 COPTIC SYMBOL MI RO | 2ce4 COPTIC SYMBOL KAI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ce7 COPTIC SYMBOL STAUROS | 2ce6 COPTIC SYMBOL PI RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ce9 COPTIC SYMBOL KHI RO | 2ce8 COPTIC SYMBOL TAU RO */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* 2ceb COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHE | 2cea COPTIC SYMBOL SHIMA SIMA */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* 2ced COPTIC CAPITAL LETTER CRYPTOGRAMMIC GAN | 2cec COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_LOWER, /* 2cef COPTIC COMBINING NI ABOVE | 2cee COPTIC SMALL LETTER CRYPTOGRAMMIC GANGI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2cf1 COPTIC COMBINING SPIRITUS LENIS | 2cf0 COPTIC COMBINING SPIRITUS ASPER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* 2cf3 COPTIC SMALL LETTER BOHAIRIC KHEI | 2cf2 COPTIC CAPITAL LETTER BOHAIRIC KHEI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2cf5 (null) | 2cf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2cf7 (null) | 2cf6 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* 2cf9 COPTIC OLD NUBIAN FULL STOP | 2cf8 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2cfb COPTIC OLD NUBIAN INDIRECT QUESTION MAR | 2cfa COPTIC OLD NUBIAN DIRECT QUESTION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2cfd COPTIC FRACTION ONE HALF | 2cfc COPTIC OLD NUBIAN VERSE DIVIDER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2cff COPTIC MORPHOLOGICAL DIVIDER | 2cfe COPTIC FULL STOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d01 GEORGIAN SMALL LETTER BAN | 2d00 GEORGIAN SMALL LETTER AN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d03 GEORGIAN SMALL LETTER DON | 2d02 GEORGIAN SMALL LETTER GAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d05 GEORGIAN SMALL LETTER VIN | 2d04 GEORGIAN SMALL LETTER EN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d07 GEORGIAN SMALL LETTER TAN | 2d06 GEORGIAN SMALL LETTER ZEN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d09 GEORGIAN SMALL LETTER KAN | 2d08 GEORGIAN SMALL LETTER IN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0b GEORGIAN SMALL LETTER MAN | 2d0a GEORGIAN SMALL LETTER LAS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0d GEORGIAN SMALL LETTER ON | 2d0c GEORGIAN SMALL LETTER NAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d0f GEORGIAN SMALL LETTER ZHAR | 2d0e GEORGIAN SMALL LETTER PAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d11 GEORGIAN SMALL LETTER SAN | 2d10 GEORGIAN SMALL LETTER RAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d13 GEORGIAN SMALL LETTER UN | 2d12 GEORGIAN SMALL LETTER TAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d15 GEORGIAN SMALL LETTER KHAR | 2d14 GEORGIAN SMALL LETTER PHAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d17 GEORGIAN SMALL LETTER QAR | 2d16 GEORGIAN SMALL LETTER GHAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d19 GEORGIAN SMALL LETTER CHIN | 2d18 GEORGIAN SMALL LETTER SHIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1b GEORGIAN SMALL LETTER JIL | 2d1a GEORGIAN SMALL LETTER CAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1d GEORGIAN SMALL LETTER CHAR | 2d1c GEORGIAN SMALL LETTER CIL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d1f GEORGIAN SMALL LETTER JHAN | 2d1e GEORGIAN SMALL LETTER XAN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d21 GEORGIAN SMALL LETTER HE | 2d20 GEORGIAN SMALL LETTER HAE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d23 GEORGIAN SMALL LETTER WE | 2d22 GEORGIAN SMALL LETTER HIE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* 2d25 GEORGIAN SMALL LETTER HOE | 2d24 GEORGIAN SMALL LETTER HAR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 2d27 GEORGIAN SMALL LETTER YN | 2d26 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d29 (null) | 2d28 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d2b (null) | 2d2a (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* 2d2d GEORGIAN SMALL LETTER AEN | 2d2c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d2f (null) | 2d2e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d31 TIFINAGH LETTER YAB | 2d30 TIFINAGH LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d33 TIFINAGH LETTER YAG | 2d32 TIFINAGH LETTER YABH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d35 TIFINAGH LETTER BERBER ACADEMY YAJ | 2d34 TIFINAGH LETTER YAGHH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d37 TIFINAGH LETTER YAD | 2d36 TIFINAGH LETTER YAJ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d39 TIFINAGH LETTER YADD | 2d38 TIFINAGH LETTER YADH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3b TIFINAGH LETTER YEY | 2d3a TIFINAGH LETTER YADDH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3d TIFINAGH LETTER YAK | 2d3c TIFINAGH LETTER YAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d3f TIFINAGH LETTER YAKHH | 2d3e TIFINAGH LETTER TUAREG YAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d41 TIFINAGH LETTER BERBER ACADEMY YAH | 2d40 TIFINAGH LETTER YAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d43 TIFINAGH LETTER YAHH | 2d42 TIFINAGH LETTER TUAREG YAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d45 TIFINAGH LETTER YAKH | 2d44 TIFINAGH LETTER YAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d47 TIFINAGH LETTER YAQ | 2d46 TIFINAGH LETTER TUAREG YAKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d49 TIFINAGH LETTER YI | 2d48 TIFINAGH LETTER TUAREG YAQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4b TIFINAGH LETTER AHAGGAR YAZH | 2d4a TIFINAGH LETTER YAZH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4d TIFINAGH LETTER YAL | 2d4c TIFINAGH LETTER TUAREG YAZH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d4f TIFINAGH LETTER YAN | 2d4e TIFINAGH LETTER YAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d51 TIFINAGH LETTER TUAREG YANG | 2d50 TIFINAGH LETTER TUAREG YAGN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d53 TIFINAGH LETTER YU | 2d52 TIFINAGH LETTER YAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d55 TIFINAGH LETTER YARR | 2d54 TIFINAGH LETTER YAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d57 TIFINAGH LETTER TUAREG YAGH | 2d56 TIFINAGH LETTER YAGH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d59 TIFINAGH LETTER YAS | 2d58 TIFINAGH LETTER AYER YAGH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5b TIFINAGH LETTER YASH | 2d5a TIFINAGH LETTER YASS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5d TIFINAGH LETTER YATH | 2d5c TIFINAGH LETTER YAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d5f TIFINAGH LETTER YATT | 2d5e TIFINAGH LETTER YACH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d61 TIFINAGH LETTER YAW | 2d60 TIFINAGH LETTER YAV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d63 TIFINAGH LETTER YAZ | 2d62 TIFINAGH LETTER YAY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d65 TIFINAGH LETTER YAZZ | 2d64 TIFINAGH LETTER TAWELLEMET YAZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d67 TIFINAGH LETTER YO | 2d66 TIFINAGH LETTER YE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d69 (null) | 2d68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d6b (null) | 2d6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d6d (null) | 2d6c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2d6f TIFINAGH MODIFIER LETTER LABIALIZATION | 2d6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* 2d71 (null) | 2d70 TIFINAGH SEPARATOR MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d73 (null) | 2d72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d75 (null) | 2d74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d77 (null) | 2d76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d79 (null) | 2d78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d7b (null) | 2d7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d7d (null) | 2d7c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2d7f TIFINAGH CONSONANT JOINER | 2d7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d81 ETHIOPIC SYLLABLE MOA | 2d80 ETHIOPIC SYLLABLE LOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d83 ETHIOPIC SYLLABLE SOA | 2d82 ETHIOPIC SYLLABLE ROA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d85 ETHIOPIC SYLLABLE BOA | 2d84 ETHIOPIC SYLLABLE SHOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d87 ETHIOPIC SYLLABLE COA | 2d86 ETHIOPIC SYLLABLE TOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d89 ETHIOPIC SYLLABLE NYOA | 2d88 ETHIOPIC SYLLABLE NOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8b ETHIOPIC SYLLABLE ZOA | 2d8a ETHIOPIC SYLLABLE GLOTTAL OA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8d ETHIOPIC SYLLABLE DDOA | 2d8c ETHIOPIC SYLLABLE DOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d8f ETHIOPIC SYLLABLE THOA | 2d8e ETHIOPIC SYLLABLE JOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d91 ETHIOPIC SYLLABLE PHOA | 2d90 ETHIOPIC SYLLABLE CHOA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d93 ETHIOPIC SYLLABLE GGWA | 2d92 ETHIOPIC SYLLABLE POA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2d95 ETHIOPIC SYLLABLE GGWEE | 2d94 ETHIOPIC SYLLABLE GGWI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2d97 (null) | 2d96 ETHIOPIC SYLLABLE GGWE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d99 (null) | 2d98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9b (null) | 2d9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9d (null) | 2d9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2d9f (null) | 2d9e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da1 ETHIOPIC SYLLABLE SSU | 2da0 ETHIOPIC SYLLABLE SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da3 ETHIOPIC SYLLABLE SSAA | 2da2 ETHIOPIC SYLLABLE SSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da5 ETHIOPIC SYLLABLE SSE | 2da4 ETHIOPIC SYLLABLE SSEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2da7 (null) | 2da6 ETHIOPIC SYLLABLE SSO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2da9 ETHIOPIC SYLLABLE CCU | 2da8 ETHIOPIC SYLLABLE CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dab ETHIOPIC SYLLABLE CCAA | 2daa ETHIOPIC SYLLABLE CCI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dad ETHIOPIC SYLLABLE CCE | 2dac ETHIOPIC SYLLABLE CCEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2daf (null) | 2dae ETHIOPIC SYLLABLE CCO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db1 ETHIOPIC SYLLABLE ZZU | 2db0 ETHIOPIC SYLLABLE ZZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db3 ETHIOPIC SYLLABLE ZZAA | 2db2 ETHIOPIC SYLLABLE ZZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db5 ETHIOPIC SYLLABLE ZZE | 2db4 ETHIOPIC SYLLABLE ZZEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2db7 (null) | 2db6 ETHIOPIC SYLLABLE ZZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2db9 ETHIOPIC SYLLABLE CCHU | 2db8 ETHIOPIC SYLLABLE CCHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dbb ETHIOPIC SYLLABLE CCHAA | 2dba ETHIOPIC SYLLABLE CCHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dbd ETHIOPIC SYLLABLE CCHE | 2dbc ETHIOPIC SYLLABLE CCHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dbf (null) | 2dbe ETHIOPIC SYLLABLE CCHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc1 ETHIOPIC SYLLABLE QYU | 2dc0 ETHIOPIC SYLLABLE QYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc3 ETHIOPIC SYLLABLE QYAA | 2dc2 ETHIOPIC SYLLABLE QYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc5 ETHIOPIC SYLLABLE QYE | 2dc4 ETHIOPIC SYLLABLE QYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dc7 (null) | 2dc6 ETHIOPIC SYLLABLE QYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dc9 ETHIOPIC SYLLABLE KYU | 2dc8 ETHIOPIC SYLLABLE KYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dcb ETHIOPIC SYLLABLE KYAA | 2dca ETHIOPIC SYLLABLE KYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dcd ETHIOPIC SYLLABLE KYE | 2dcc ETHIOPIC SYLLABLE KYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dcf (null) | 2dce ETHIOPIC SYLLABLE KYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd1 ETHIOPIC SYLLABLE XYU | 2dd0 ETHIOPIC SYLLABLE XYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd3 ETHIOPIC SYLLABLE XYAA | 2dd2 ETHIOPIC SYLLABLE XYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd5 ETHIOPIC SYLLABLE XYE | 2dd4 ETHIOPIC SYLLABLE XYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2dd7 (null) | 2dd6 ETHIOPIC SYLLABLE XYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2dd9 ETHIOPIC SYLLABLE GYU | 2dd8 ETHIOPIC SYLLABLE GYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2ddb ETHIOPIC SYLLABLE GYAA | 2dda ETHIOPIC SYLLABLE GYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 2ddd ETHIOPIC SYLLABLE GYE | 2ddc ETHIOPIC SYLLABLE GYEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 2ddf (null) | 2dde ETHIOPIC SYLLABLE GYO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de1 COMBINING CYRILLIC LETTER VE | 2de0 COMBINING CYRILLIC LETTER BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de3 COMBINING CYRILLIC LETTER DE | 2de2 COMBINING CYRILLIC LETTER GHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de5 COMBINING CYRILLIC LETTER ZE | 2de4 COMBINING CYRILLIC LETTER ZHE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de7 COMBINING CYRILLIC LETTER EL | 2de6 COMBINING CYRILLIC LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2de9 COMBINING CYRILLIC LETTER EN | 2de8 COMBINING CYRILLIC LETTER EM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2deb COMBINING CYRILLIC LETTER PE | 2dea COMBINING CYRILLIC LETTER O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ded COMBINING CYRILLIC LETTER ES | 2dec COMBINING CYRILLIC LETTER ER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2def COMBINING CYRILLIC LETTER HA | 2dee COMBINING CYRILLIC LETTER TE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df1 COMBINING CYRILLIC LETTER CHE | 2df0 COMBINING CYRILLIC LETTER TSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df3 COMBINING CYRILLIC LETTER SHCHA | 2df2 COMBINING CYRILLIC LETTER SHA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df5 COMBINING CYRILLIC LETTER ES-TE | 2df4 COMBINING CYRILLIC LETTER FITA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df7 COMBINING CYRILLIC LETTER IE | 2df6 COMBINING CYRILLIC LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2df9 COMBINING CYRILLIC LETTER MONOGRAPH UK | 2df8 COMBINING CYRILLIC LETTER DJERV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dfb COMBINING CYRILLIC LETTER YU | 2dfa COMBINING CYRILLIC LETTER YAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dfd COMBINING CYRILLIC LETTER LITTLE YUS | 2dfc COMBINING CYRILLIC LETTER IOTIFIED A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2dff COMBINING CYRILLIC LETTER IOTIFIED BIG | 2dfe COMBINING CYRILLIC LETTER BIG YUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e01 RIGHT ANGLE DOTTED SUBSTITUTION MARKER | 2e00 RIGHT ANGLE SUBSTITUTION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e03 RIGHT SUBSTITUTION BRACKET | 2e02 LEFT SUBSTITUTION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e05 RIGHT DOTTED SUBSTITUTION BRACKET | 2e04 LEFT DOTTED SUBSTITUTION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e07 RAISED DOTTED INTERPOLATION MARKER | 2e06 RAISED INTERPOLATION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e09 LEFT TRANSPOSITION BRACKET | 2e08 DOTTED TRANSPOSITION MARKER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0b RAISED SQUARE | 2e0a RIGHT TRANSPOSITION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0d RIGHT RAISED OMISSION BRACKET | 2e0c LEFT RAISED OMISSION BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e0f PARAGRAPHOS | 2e0e EDITORIAL CORONIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e11 REVERSED FORKED PARAGRAPHOS | 2e10 FORKED PARAGRAPHOS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e13 DOTTED OBELOS | 2e12 HYPODIASTOLE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e15 UPWARDS ANCORA | 2e14 DOWNWARDS ANCORA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e17 DOUBLE OBLIQUE HYPHEN | 2e16 DOTTED RIGHT-POINTING ANGLE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e19 PALM BRANCH | 2e18 INVERTED INTERROBANG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1b TILDE WITH RING ABOVE | 2e1a HYPHEN WITH DIAERESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1d RIGHT LOW PARAPHRASE BRACKET | 2e1c LEFT LOW PARAPHRASE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e1f TILDE WITH DOT BELOW | 2e1e TILDE WITH DOT ABOVE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e21 RIGHT VERTICAL BAR WITH QUILL | 2e20 LEFT VERTICAL BAR WITH QUILL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e23 TOP RIGHT HALF BRACKET | 2e22 TOP LEFT HALF BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e25 BOTTOM RIGHT HALF BRACKET | 2e24 BOTTOM LEFT HALF BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e27 RIGHT SIDEWAYS U BRACKET | 2e26 LEFT SIDEWAYS U BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e29 RIGHT DOUBLE PARENTHESIS | 2e28 LEFT DOUBLE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e2b ONE DOT OVER TWO DOTS PUNCTUATION | 2e2a TWO DOTS OVER ONE DOT PUNCTUATION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e2d FIVE DOT MARK | 2e2c SQUARED FOUR DOT PUNCTUATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 2e2f VERTICAL TILDE | 2e2e REVERSED QUESTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e31 WORD SEPARATOR MIDDLE DOT | 2e30 RING POINT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e33 RAISED DOT | 2e32 TURNED COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e35 TURNED SEMICOLON | 2e34 RAISED COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e37 DAGGER WITH RIGHT GUARD | 2e36 DAGGER WITH LEFT GUARD */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e39 TOP HALF SECTION SIGN | 2e38 TURNED DAGGER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 2e3b THREE-EM DASH | 2e3a TWO-EM DASH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e3d (null) | 2e3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e3f (null) | 2e3e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e41 (null) | 2e40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e43 (null) | 2e42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e45 (null) | 2e44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e47 (null) | 2e46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e49 (null) | 2e48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4b (null) | 2e4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4d (null) | 2e4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e4f (null) | 2e4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e51 (null) | 2e50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e53 (null) | 2e52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e55 (null) | 2e54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e57 (null) | 2e56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e59 (null) | 2e58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5b (null) | 2e5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5d (null) | 2e5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e5f (null) | 2e5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e61 (null) | 2e60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e63 (null) | 2e62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e65 (null) | 2e64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e67 (null) | 2e66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e69 (null) | 2e68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6b (null) | 2e6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6d (null) | 2e6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e6f (null) | 2e6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e71 (null) | 2e70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e73 (null) | 2e72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e75 (null) | 2e74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e77 (null) | 2e76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e79 (null) | 2e78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7b (null) | 2e7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7d (null) | 2e7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2e7f (null) | 2e7e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e81 CJK RADICAL CLIFF | 2e80 CJK RADICAL REPEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e83 CJK RADICAL SECOND TWO | 2e82 CJK RADICAL SECOND ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e85 CJK RADICAL PERSON | 2e84 CJK RADICAL SECOND THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e87 CJK RADICAL TABLE | 2e86 CJK RADICAL BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e89 CJK RADICAL KNIFE TWO | 2e88 CJK RADICAL KNIFE ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8b CJK RADICAL SEAL | 2e8a CJK RADICAL DIVINATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8d CJK RADICAL SMALL TWO | 2e8c CJK RADICAL SMALL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e8f CJK RADICAL LAME TWO | 2e8e CJK RADICAL LAME ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e91 CJK RADICAL LAME FOUR | 2e90 CJK RADICAL LAME THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e93 CJK RADICAL THREAD | 2e92 CJK RADICAL SNAKE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e95 CJK RADICAL SNOUT TWO | 2e94 CJK RADICAL SNOUT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e97 CJK RADICAL HEART TWO | 2e96 CJK RADICAL HEART ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e99 CJK RADICAL RAP | 2e98 CJK RADICAL HAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 2e9b CJK RADICAL CHOKE | 2e9a (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e9d CJK RADICAL MOON | 2e9c CJK RADICAL SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2e9f CJK RADICAL MOTHER | 2e9e CJK RADICAL DEATH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea1 CJK RADICAL WATER ONE | 2ea0 CJK RADICAL CIVILIAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea3 CJK RADICAL FIRE | 2ea2 CJK RADICAL WATER TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea5 CJK RADICAL PAW TWO | 2ea4 CJK RADICAL PAW ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea7 CJK RADICAL COW | 2ea6 CJK RADICAL SIMPLIFIED HALF TREE TRUNK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ea9 CJK RADICAL JADE | 2ea8 CJK RADICAL DOG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eab CJK RADICAL EYE | 2eaa CJK RADICAL BOLT OF CLOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ead CJK RADICAL SPIRIT TWO | 2eac CJK RADICAL SPIRIT ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eaf CJK RADICAL SILK | 2eae CJK RADICAL BAMBOO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb1 CJK RADICAL NET ONE | 2eb0 CJK RADICAL C-SIMPLIFIED SILK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb3 CJK RADICAL NET THREE | 2eb2 CJK RADICAL NET TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb5 CJK RADICAL MESH | 2eb4 CJK RADICAL NET FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb7 CJK RADICAL RAM | 2eb6 CJK RADICAL SHEEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eb9 CJK RADICAL OLD | 2eb8 CJK RADICAL EWE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebb CJK RADICAL BRUSH TWO | 2eba CJK RADICAL BRUSH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebd CJK RADICAL MORTAR | 2ebc CJK RADICAL MEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ebf CJK RADICAL GRASS TWO | 2ebe CJK RADICAL GRASS ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec1 CJK RADICAL TIGER | 2ec0 CJK RADICAL GRASS THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec3 CJK RADICAL WEST ONE | 2ec2 CJK RADICAL CLOTHES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec5 CJK RADICAL C-SIMPLIFIED SEE | 2ec4 CJK RADICAL WEST TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec7 CJK RADICAL HORN | 2ec6 CJK RADICAL SIMPLIFIED HORN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ec9 CJK RADICAL C-SIMPLIFIED SHELL | 2ec8 CJK RADICAL C-SIMPLIFIED SPEECH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecb CJK RADICAL C-SIMPLIFIED CART | 2eca CJK RADICAL FOOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecd CJK RADICAL WALK ONE | 2ecc CJK RADICAL SIMPLIFIED WALK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ecf CJK RADICAL CITY | 2ece CJK RADICAL WALK TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed1 CJK RADICAL LONG ONE | 2ed0 CJK RADICAL C-SIMPLIFIED GOLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed3 CJK RADICAL C-SIMPLIFIED LONG | 2ed2 CJK RADICAL LONG TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed5 CJK RADICAL MOUND ONE | 2ed4 CJK RADICAL C-SIMPLIFIED GATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed7 CJK RADICAL RAIN | 2ed6 CJK RADICAL MOUND TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ed9 CJK RADICAL C-SIMPLIFIED TANNED LEATHER | 2ed8 CJK RADICAL BLUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edb CJK RADICAL C-SIMPLIFIED WIND | 2eda CJK RADICAL C-SIMPLIFIED LEAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edd CJK RADICAL EAT ONE | 2edc CJK RADICAL C-SIMPLIFIED FLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2edf CJK RADICAL EAT THREE | 2ede CJK RADICAL EAT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee1 CJK RADICAL HEAD | 2ee0 CJK RADICAL C-SIMPLIFIED EAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee3 CJK RADICAL BONE | 2ee2 CJK RADICAL C-SIMPLIFIED HORSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee5 CJK RADICAL C-SIMPLIFIED FISH | 2ee4 CJK RADICAL GHOST */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee7 CJK RADICAL C-SIMPLIFIED SALT | 2ee6 CJK RADICAL C-SIMPLIFIED BIRD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ee9 CJK RADICAL SIMPLIFIED YELLOW | 2ee8 CJK RADICAL SIMPLIFIED WHEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eeb CJK RADICAL J-SIMPLIFIED EVEN | 2eea CJK RADICAL C-SIMPLIFIED FROG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eed CJK RADICAL J-SIMPLIFIED TOOTH | 2eec CJK RADICAL C-SIMPLIFIED EVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2eef CJK RADICAL J-SIMPLIFIED DRAGON | 2eee CJK RADICAL C-SIMPLIFIED TOOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ef1 CJK RADICAL TURTLE | 2ef0 CJK RADICAL C-SIMPLIFIED DRAGON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ef3 CJK RADICAL C-SIMPLIFIED TURTLE | 2ef2 CJK RADICAL J-SIMPLIFIED TURTLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef5 (null) | 2ef4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef7 (null) | 2ef6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ef9 (null) | 2ef8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2efb (null) | 2efa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2efd (null) | 2efc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2eff (null) | 2efe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f01 KANGXI RADICAL LINE | 2f00 KANGXI RADICAL ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f03 KANGXI RADICAL SLASH | 2f02 KANGXI RADICAL DOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f05 KANGXI RADICAL HOOK | 2f04 KANGXI RADICAL SECOND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f07 KANGXI RADICAL LID | 2f06 KANGXI RADICAL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f09 KANGXI RADICAL LEGS | 2f08 KANGXI RADICAL MAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0b KANGXI RADICAL EIGHT | 2f0a KANGXI RADICAL ENTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0d KANGXI RADICAL COVER | 2f0c KANGXI RADICAL DOWN BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f0f KANGXI RADICAL TABLE | 2f0e KANGXI RADICAL ICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f11 KANGXI RADICAL KNIFE | 2f10 KANGXI RADICAL OPEN BOX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f13 KANGXI RADICAL WRAP | 2f12 KANGXI RADICAL POWER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f15 KANGXI RADICAL RIGHT OPEN BOX | 2f14 KANGXI RADICAL SPOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f17 KANGXI RADICAL TEN | 2f16 KANGXI RADICAL HIDING ENCLOSURE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f19 KANGXI RADICAL SEAL | 2f18 KANGXI RADICAL DIVINATION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1b KANGXI RADICAL PRIVATE | 2f1a KANGXI RADICAL CLIFF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1d KANGXI RADICAL MOUTH | 2f1c KANGXI RADICAL AGAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f1f KANGXI RADICAL EARTH | 2f1e KANGXI RADICAL ENCLOSURE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f21 KANGXI RADICAL GO | 2f20 KANGXI RADICAL SCHOLAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f23 KANGXI RADICAL EVENING | 2f22 KANGXI RADICAL GO SLOWLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f25 KANGXI RADICAL WOMAN | 2f24 KANGXI RADICAL BIG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f27 KANGXI RADICAL ROOF | 2f26 KANGXI RADICAL CHILD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f29 KANGXI RADICAL SMALL | 2f28 KANGXI RADICAL INCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2b KANGXI RADICAL CORPSE | 2f2a KANGXI RADICAL LAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2d KANGXI RADICAL MOUNTAIN | 2f2c KANGXI RADICAL SPROUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f2f KANGXI RADICAL WORK | 2f2e KANGXI RADICAL RIVER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f31 KANGXI RADICAL TURBAN | 2f30 KANGXI RADICAL ONESELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f33 KANGXI RADICAL SHORT THREAD | 2f32 KANGXI RADICAL DRY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f35 KANGXI RADICAL LONG STRIDE | 2f34 KANGXI RADICAL DOTTED CLIFF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f37 KANGXI RADICAL SHOOT | 2f36 KANGXI RADICAL TWO HANDS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f39 KANGXI RADICAL SNOUT | 2f38 KANGXI RADICAL BOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3b KANGXI RADICAL STEP | 2f3a KANGXI RADICAL BRISTLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3d KANGXI RADICAL HALBERD | 2f3c KANGXI RADICAL HEART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f3f KANGXI RADICAL HAND | 2f3e KANGXI RADICAL DOOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f41 KANGXI RADICAL RAP | 2f40 KANGXI RADICAL BRANCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f43 KANGXI RADICAL DIPPER | 2f42 KANGXI RADICAL SCRIPT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f45 KANGXI RADICAL SQUARE | 2f44 KANGXI RADICAL AXE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f47 KANGXI RADICAL SUN | 2f46 KANGXI RADICAL NOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f49 KANGXI RADICAL MOON | 2f48 KANGXI RADICAL SAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4b KANGXI RADICAL LACK | 2f4a KANGXI RADICAL TREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4d KANGXI RADICAL DEATH | 2f4c KANGXI RADICAL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f4f KANGXI RADICAL DO NOT | 2f4e KANGXI RADICAL WEAPON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f51 KANGXI RADICAL FUR | 2f50 KANGXI RADICAL COMPARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f53 KANGXI RADICAL STEAM | 2f52 KANGXI RADICAL CLAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f55 KANGXI RADICAL FIRE | 2f54 KANGXI RADICAL WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f57 KANGXI RADICAL FATHER | 2f56 KANGXI RADICAL CLAW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f59 KANGXI RADICAL HALF TREE TRUNK | 2f58 KANGXI RADICAL DOUBLE X */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5b KANGXI RADICAL FANG | 2f5a KANGXI RADICAL SLICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5d KANGXI RADICAL DOG | 2f5c KANGXI RADICAL COW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f5f KANGXI RADICAL JADE | 2f5e KANGXI RADICAL PROFOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f61 KANGXI RADICAL TILE | 2f60 KANGXI RADICAL MELON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f63 KANGXI RADICAL LIFE | 2f62 KANGXI RADICAL SWEET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f65 KANGXI RADICAL FIELD | 2f64 KANGXI RADICAL USE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f67 KANGXI RADICAL SICKNESS | 2f66 KANGXI RADICAL BOLT OF CLOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f69 KANGXI RADICAL WHITE | 2f68 KANGXI RADICAL DOTTED TENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6b KANGXI RADICAL DISH | 2f6a KANGXI RADICAL SKIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6d KANGXI RADICAL SPEAR | 2f6c KANGXI RADICAL EYE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f6f KANGXI RADICAL STONE | 2f6e KANGXI RADICAL ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f71 KANGXI RADICAL TRACK | 2f70 KANGXI RADICAL SPIRIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f73 KANGXI RADICAL CAVE | 2f72 KANGXI RADICAL GRAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f75 KANGXI RADICAL BAMBOO | 2f74 KANGXI RADICAL STAND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f77 KANGXI RADICAL SILK | 2f76 KANGXI RADICAL RICE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f79 KANGXI RADICAL NET | 2f78 KANGXI RADICAL JAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7b KANGXI RADICAL FEATHER | 2f7a KANGXI RADICAL SHEEP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7d KANGXI RADICAL AND | 2f7c KANGXI RADICAL OLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f7f KANGXI RADICAL EAR | 2f7e KANGXI RADICAL PLOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f81 KANGXI RADICAL MEAT | 2f80 KANGXI RADICAL BRUSH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f83 KANGXI RADICAL SELF | 2f82 KANGXI RADICAL MINISTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f85 KANGXI RADICAL MORTAR | 2f84 KANGXI RADICAL ARRIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f87 KANGXI RADICAL OPPOSE | 2f86 KANGXI RADICAL TONGUE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f89 KANGXI RADICAL STOPPING | 2f88 KANGXI RADICAL BOAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8b KANGXI RADICAL GRASS | 2f8a KANGXI RADICAL COLOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8d KANGXI RADICAL INSECT | 2f8c KANGXI RADICAL TIGER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f8f KANGXI RADICAL WALK ENCLOSURE | 2f8e KANGXI RADICAL BLOOD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f91 KANGXI RADICAL WEST | 2f90 KANGXI RADICAL CLOTHES */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f93 KANGXI RADICAL HORN | 2f92 KANGXI RADICAL SEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f95 KANGXI RADICAL VALLEY | 2f94 KANGXI RADICAL SPEECH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f97 KANGXI RADICAL PIG | 2f96 KANGXI RADICAL BEAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f99 KANGXI RADICAL SHELL | 2f98 KANGXI RADICAL BADGER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9b KANGXI RADICAL RUN | 2f9a KANGXI RADICAL RED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9d KANGXI RADICAL BODY | 2f9c KANGXI RADICAL FOOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2f9f KANGXI RADICAL BITTER | 2f9e KANGXI RADICAL CART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa1 KANGXI RADICAL WALK | 2fa0 KANGXI RADICAL MORNING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa3 KANGXI RADICAL WINE | 2fa2 KANGXI RADICAL CITY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa5 KANGXI RADICAL VILLAGE | 2fa4 KANGXI RADICAL DISTINGUISH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa7 KANGXI RADICAL LONG | 2fa6 KANGXI RADICAL GOLD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fa9 KANGXI RADICAL MOUND | 2fa8 KANGXI RADICAL GATE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fab KANGXI RADICAL SHORT TAILED BIRD | 2faa KANGXI RADICAL SLAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fad KANGXI RADICAL BLUE | 2fac KANGXI RADICAL RAIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2faf KANGXI RADICAL FACE | 2fae KANGXI RADICAL WRONG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb1 KANGXI RADICAL TANNED LEATHER | 2fb0 KANGXI RADICAL LEATHER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb3 KANGXI RADICAL SOUND | 2fb2 KANGXI RADICAL LEEK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb5 KANGXI RADICAL WIND | 2fb4 KANGXI RADICAL LEAF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb7 KANGXI RADICAL EAT | 2fb6 KANGXI RADICAL FLY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fb9 KANGXI RADICAL FRAGRANT | 2fb8 KANGXI RADICAL HEAD */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbb KANGXI RADICAL BONE | 2fba KANGXI RADICAL HORSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbd KANGXI RADICAL HAIR | 2fbc KANGXI RADICAL TALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fbf KANGXI RADICAL SACRIFICIAL WINE | 2fbe KANGXI RADICAL FIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc1 KANGXI RADICAL GHOST | 2fc0 KANGXI RADICAL CAULDRON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc3 KANGXI RADICAL BIRD | 2fc2 KANGXI RADICAL FISH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc5 KANGXI RADICAL DEER | 2fc4 KANGXI RADICAL SALT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc7 KANGXI RADICAL HEMP | 2fc6 KANGXI RADICAL WHEAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fc9 KANGXI RADICAL MILLET | 2fc8 KANGXI RADICAL YELLOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcb KANGXI RADICAL EMBROIDERY | 2fca KANGXI RADICAL BLACK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcd KANGXI RADICAL TRIPOD | 2fcc KANGXI RADICAL FROG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fcf KANGXI RADICAL RAT | 2fce KANGXI RADICAL DRUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd1 KANGXI RADICAL EVEN | 2fd0 KANGXI RADICAL NOSE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd3 KANGXI RADICAL DRAGON | 2fd2 KANGXI RADICAL TOOTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2fd5 KANGXI RADICAL FLUTE | 2fd4 KANGXI RADICAL TURTLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fd7 (null) | 2fd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fd9 (null) | 2fd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdb (null) | 2fda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdd (null) | 2fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fdf (null) | 2fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe1 (null) | 2fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe3 (null) | 2fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe5 (null) | 2fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe7 (null) | 2fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fe9 (null) | 2fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2feb (null) | 2fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fed (null) | 2fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fef (null) | 2fee (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff1 IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE | 2ff0 IDEOGRAPHIC DESCRIPTION CHARACTER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff3 IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE | 2ff2 IDEOGRAPHIC DESCRIPTION CHARACTER LEFT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff5 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff4 IDEOGRAPHIC DESCRIPTION CHARACTER FULL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff7 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff6 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ff9 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO | 2ff8 IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 2ffb IDEOGRAPHIC DESCRIPTION CHARACTER OVERL | 2ffa IDEOGRAPHIC DESCRIPTION CHARACTER SURRO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2ffd (null) | 2ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 2fff (null) | 2ffe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_SPACE, /* 3001 IDEOGRAPHIC COMMA | 3000 IDEOGRAPHIC SPACE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3003 DITTO MARK | 3002 IDEOGRAPHIC FULL STOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3005 IDEOGRAPHIC ITERATION MARK | 3004 JAPANESE INDUSTRIAL STANDARD SYMBOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* 3007 IDEOGRAPHIC NUMBER ZERO | 3006 IDEOGRAPHIC CLOSING MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3009 RIGHT ANGLE BRACKET | 3008 LEFT ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300b RIGHT DOUBLE ANGLE BRACKET | 300a LEFT DOUBLE ANGLE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300d RIGHT CORNER BRACKET | 300c LEFT CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 300f RIGHT WHITE CORNER BRACKET | 300e LEFT WHITE CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3011 RIGHT BLACK LENTICULAR BRACKET | 3010 LEFT BLACK LENTICULAR BRACKET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3013 GETA MARK | 3012 POSTAL MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3015 RIGHT TORTOISE SHELL BRACKET | 3014 LEFT TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3017 RIGHT WHITE LENTICULAR BRACKET | 3016 LEFT WHITE LENTICULAR BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 3019 RIGHT WHITE TORTOISE SHELL BRACKET | 3018 LEFT WHITE TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301b RIGHT WHITE SQUARE BRACKET | 301a LEFT WHITE SQUARE BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301d REVERSED DOUBLE PRIME QUOTATION MARK | 301c WAVE DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* 301f LOW DOUBLE PRIME QUOTATION MARK | 301e DOUBLE PRIME QUOTATION MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3021 HANGZHOU NUMERAL ONE | 3020 POSTAL MARK FACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3023 HANGZHOU NUMERAL THREE | 3022 HANGZHOU NUMERAL TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3025 HANGZHOU NUMERAL FIVE | 3024 HANGZHOU NUMERAL FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3027 HANGZHOU NUMERAL SEVEN | 3026 HANGZHOU NUMERAL SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3029 HANGZHOU NUMERAL NINE | 3028 HANGZHOU NUMERAL EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302b IDEOGRAPHIC RISING TONE MARK | 302a IDEOGRAPHIC LEVEL TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302d IDEOGRAPHIC ENTERING TONE MARK | 302c IDEOGRAPHIC DEPARTING TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 302f HANGUL DOUBLE DOT TONE MARK | 302e HANGUL SINGLE DOT TONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* 3031 VERTICAL KANA REPEAT MARK | 3030 WAVY DASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3033 VERTICAL KANA REPEAT MARK UPPER HALF | 3032 VERTICAL KANA REPEAT WITH VOICED SOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3035 VERTICAL KANA REPEAT MARK LOWER HALF | 3034 VERTICAL KANA REPEAT WITH VOICED SOUND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3037 IDEOGRAPHIC TELEGRAPH LINE FEED SEPARAT | 3036 CIRCLED POSTAL MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3039 HANGZHOU NUMERAL TWENTY | 3038 HANGZHOU NUMERAL TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 303b VERTICAL IDEOGRAPHIC ITERATION MARK | 303a HANGZHOU NUMERAL THIRTY */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 303d PART ALTERNATION MARK | 303c MASU MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 303f IDEOGRAPHIC HALF FILL SPACE | 303e IDEOGRAPHIC VARIATION INDICATOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3041 HIRAGANA LETTER SMALL A | 3040 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3043 HIRAGANA LETTER SMALL I | 3042 HIRAGANA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3045 HIRAGANA LETTER SMALL U | 3044 HIRAGANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3047 HIRAGANA LETTER SMALL E | 3046 HIRAGANA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3049 HIRAGANA LETTER SMALL O | 3048 HIRAGANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304b HIRAGANA LETTER KA | 304a HIRAGANA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304d HIRAGANA LETTER KI | 304c HIRAGANA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 304f HIRAGANA LETTER KU | 304e HIRAGANA LETTER GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3051 HIRAGANA LETTER KE | 3050 HIRAGANA LETTER GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3053 HIRAGANA LETTER KO | 3052 HIRAGANA LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3055 HIRAGANA LETTER SA | 3054 HIRAGANA LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3057 HIRAGANA LETTER SI | 3056 HIRAGANA LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3059 HIRAGANA LETTER SU | 3058 HIRAGANA LETTER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305b HIRAGANA LETTER SE | 305a HIRAGANA LETTER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305d HIRAGANA LETTER SO | 305c HIRAGANA LETTER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 305f HIRAGANA LETTER TA | 305e HIRAGANA LETTER ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3061 HIRAGANA LETTER TI | 3060 HIRAGANA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3063 HIRAGANA LETTER SMALL TU | 3062 HIRAGANA LETTER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3065 HIRAGANA LETTER DU | 3064 HIRAGANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3067 HIRAGANA LETTER DE | 3066 HIRAGANA LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3069 HIRAGANA LETTER DO | 3068 HIRAGANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306b HIRAGANA LETTER NI | 306a HIRAGANA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306d HIRAGANA LETTER NE | 306c HIRAGANA LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 306f HIRAGANA LETTER HA | 306e HIRAGANA LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3071 HIRAGANA LETTER PA | 3070 HIRAGANA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3073 HIRAGANA LETTER BI | 3072 HIRAGANA LETTER HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3075 HIRAGANA LETTER HU | 3074 HIRAGANA LETTER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3077 HIRAGANA LETTER PU | 3076 HIRAGANA LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3079 HIRAGANA LETTER BE | 3078 HIRAGANA LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307b HIRAGANA LETTER HO | 307a HIRAGANA LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307d HIRAGANA LETTER PO | 307c HIRAGANA LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 307f HIRAGANA LETTER MI | 307e HIRAGANA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3081 HIRAGANA LETTER ME | 3080 HIRAGANA LETTER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3083 HIRAGANA LETTER SMALL YA | 3082 HIRAGANA LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3085 HIRAGANA LETTER SMALL YU | 3084 HIRAGANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3087 HIRAGANA LETTER SMALL YO | 3086 HIRAGANA LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3089 HIRAGANA LETTER RA | 3088 HIRAGANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308b HIRAGANA LETTER RU | 308a HIRAGANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308d HIRAGANA LETTER RO | 308c HIRAGANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 308f HIRAGANA LETTER WA | 308e HIRAGANA LETTER SMALL WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3091 HIRAGANA LETTER WE | 3090 HIRAGANA LETTER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3093 HIRAGANA LETTER N | 3092 HIRAGANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3095 HIRAGANA LETTER SMALL KA | 3094 HIRAGANA LETTER VU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 3097 (null) | 3096 HIRAGANA LETTER SMALL KE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* 3099 COMBINING KATAKANA-HIRAGANA VOICED SOUN | 3098 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 309b KATAKANA-HIRAGANA VOICED SOUND MARK | 309a COMBINING KATAKANA-HIRAGANA SEMI-VOICED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 309d HIRAGANA ITERATION MARK | 309c KATAKANA-HIRAGANA SEMI-VOICED SOUND MAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 309f HIRAGANA DIGRAPH YORI | 309e HIRAGANA VOICED ITERATION MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* 30a1 KATAKANA LETTER SMALL A | 30a0 KATAKANA-HIRAGANA DOUBLE HYPHEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a3 KATAKANA LETTER SMALL I | 30a2 KATAKANA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a5 KATAKANA LETTER SMALL U | 30a4 KATAKANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a7 KATAKANA LETTER SMALL E | 30a6 KATAKANA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30a9 KATAKANA LETTER SMALL O | 30a8 KATAKANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ab KATAKANA LETTER KA | 30aa KATAKANA LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ad KATAKANA LETTER KI | 30ac KATAKANA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30af KATAKANA LETTER KU | 30ae KATAKANA LETTER GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b1 KATAKANA LETTER KE | 30b0 KATAKANA LETTER GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b3 KATAKANA LETTER KO | 30b2 KATAKANA LETTER GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b5 KATAKANA LETTER SA | 30b4 KATAKANA LETTER GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b7 KATAKANA LETTER SI | 30b6 KATAKANA LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30b9 KATAKANA LETTER SU | 30b8 KATAKANA LETTER ZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bb KATAKANA LETTER SE | 30ba KATAKANA LETTER ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bd KATAKANA LETTER SO | 30bc KATAKANA LETTER ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30bf KATAKANA LETTER TA | 30be KATAKANA LETTER ZO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c1 KATAKANA LETTER TI | 30c0 KATAKANA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c3 KATAKANA LETTER SMALL TU | 30c2 KATAKANA LETTER DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c5 KATAKANA LETTER DU | 30c4 KATAKANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c7 KATAKANA LETTER DE | 30c6 KATAKANA LETTER TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30c9 KATAKANA LETTER DO | 30c8 KATAKANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cb KATAKANA LETTER NI | 30ca KATAKANA LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cd KATAKANA LETTER NE | 30cc KATAKANA LETTER NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30cf KATAKANA LETTER HA | 30ce KATAKANA LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d1 KATAKANA LETTER PA | 30d0 KATAKANA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d3 KATAKANA LETTER BI | 30d2 KATAKANA LETTER HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d5 KATAKANA LETTER HU | 30d4 KATAKANA LETTER PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d7 KATAKANA LETTER PU | 30d6 KATAKANA LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30d9 KATAKANA LETTER BE | 30d8 KATAKANA LETTER HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30db KATAKANA LETTER HO | 30da KATAKANA LETTER PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30dd KATAKANA LETTER PO | 30dc KATAKANA LETTER BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30df KATAKANA LETTER MI | 30de KATAKANA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e1 KATAKANA LETTER ME | 30e0 KATAKANA LETTER MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e3 KATAKANA LETTER SMALL YA | 30e2 KATAKANA LETTER MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e5 KATAKANA LETTER SMALL YU | 30e4 KATAKANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e7 KATAKANA LETTER SMALL YO | 30e6 KATAKANA LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30e9 KATAKANA LETTER RA | 30e8 KATAKANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30eb KATAKANA LETTER RU | 30ea KATAKANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ed KATAKANA LETTER RO | 30ec KATAKANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30ef KATAKANA LETTER WA | 30ee KATAKANA LETTER SMALL WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f1 KATAKANA LETTER WE | 30f0 KATAKANA LETTER WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f3 KATAKANA LETTER N | 30f2 KATAKANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f5 KATAKANA LETTER SMALL KA | 30f4 KATAKANA LETTER VU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f7 KATAKANA LETTER VA | 30f6 KATAKANA LETTER SMALL KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 30f9 KATAKANA LETTER VE | 30f8 KATAKANA LETTER VI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_ALPHA, /* 30fb KATAKANA MIDDLE DOT | 30fa KATAKANA LETTER VO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 30fd KATAKANA ITERATION MARK | 30fc KATAKANA-HIRAGANA PROLONGED SOUND MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* 30ff KATAKANA DIGRAPH KOTO | 30fe KATAKANA VOICED ITERATION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 3101 (null) | 3100 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 3103 (null) | 3102 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3105 BOPOMOFO LETTER B | 3104 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3107 BOPOMOFO LETTER M | 3106 BOPOMOFO LETTER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3109 BOPOMOFO LETTER D | 3108 BOPOMOFO LETTER F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310b BOPOMOFO LETTER N | 310a BOPOMOFO LETTER T */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310d BOPOMOFO LETTER G | 310c BOPOMOFO LETTER L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 310f BOPOMOFO LETTER H | 310e BOPOMOFO LETTER K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3111 BOPOMOFO LETTER Q | 3110 BOPOMOFO LETTER J */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3113 BOPOMOFO LETTER ZH | 3112 BOPOMOFO LETTER X */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3115 BOPOMOFO LETTER SH | 3114 BOPOMOFO LETTER CH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3117 BOPOMOFO LETTER Z | 3116 BOPOMOFO LETTER R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3119 BOPOMOFO LETTER S | 3118 BOPOMOFO LETTER C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311b BOPOMOFO LETTER O | 311a BOPOMOFO LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311d BOPOMOFO LETTER EH | 311c BOPOMOFO LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 311f BOPOMOFO LETTER EI | 311e BOPOMOFO LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3121 BOPOMOFO LETTER OU | 3120 BOPOMOFO LETTER AU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3123 BOPOMOFO LETTER EN | 3122 BOPOMOFO LETTER AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3125 BOPOMOFO LETTER ENG | 3124 BOPOMOFO LETTER ANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3127 BOPOMOFO LETTER I | 3126 BOPOMOFO LETTER ER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3129 BOPOMOFO LETTER IU | 3128 BOPOMOFO LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 312b BOPOMOFO LETTER NG | 312a BOPOMOFO LETTER V */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 312d BOPOMOFO LETTER IH | 312c BOPOMOFO LETTER GN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 312f (null) | 312e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* 3131 HANGUL LETTER KIYEOK | 3130 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3133 HANGUL LETTER KIYEOK-SIOS | 3132 HANGUL LETTER SSANGKIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3135 HANGUL LETTER NIEUN-CIEUC | 3134 HANGUL LETTER NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3137 HANGUL LETTER TIKEUT | 3136 HANGUL LETTER NIEUN-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3139 HANGUL LETTER RIEUL | 3138 HANGUL LETTER SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313b HANGUL LETTER RIEUL-MIEUM | 313a HANGUL LETTER RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313d HANGUL LETTER RIEUL-SIOS | 313c HANGUL LETTER RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 313f HANGUL LETTER RIEUL-PHIEUPH | 313e HANGUL LETTER RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3141 HANGUL LETTER MIEUM | 3140 HANGUL LETTER RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3143 HANGUL LETTER SSANGPIEUP | 3142 HANGUL LETTER PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3145 HANGUL LETTER SIOS | 3144 HANGUL LETTER PIEUP-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3147 HANGUL LETTER IEUNG | 3146 HANGUL LETTER SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3149 HANGUL LETTER SSANGCIEUC | 3148 HANGUL LETTER CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314b HANGUL LETTER KHIEUKH | 314a HANGUL LETTER CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314d HANGUL LETTER PHIEUPH | 314c HANGUL LETTER THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 314f HANGUL LETTER A | 314e HANGUL LETTER HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3151 HANGUL LETTER YA | 3150 HANGUL LETTER AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3153 HANGUL LETTER EO | 3152 HANGUL LETTER YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3155 HANGUL LETTER YEO | 3154 HANGUL LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3157 HANGUL LETTER O | 3156 HANGUL LETTER YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3159 HANGUL LETTER WAE | 3158 HANGUL LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315b HANGUL LETTER YO | 315a HANGUL LETTER OE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315d HANGUL LETTER WEO | 315c HANGUL LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 315f HANGUL LETTER WI | 315e HANGUL LETTER WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3161 HANGUL LETTER EU | 3160 HANGUL LETTER YU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3163 HANGUL LETTER I | 3162 HANGUL LETTER YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3165 HANGUL LETTER SSANGNIEUN | 3164 HANGUL FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3167 HANGUL LETTER NIEUN-SIOS | 3166 HANGUL LETTER NIEUN-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3169 HANGUL LETTER RIEUL-KIYEOK-SIOS | 3168 HANGUL LETTER NIEUN-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316b HANGUL LETTER RIEUL-PIEUP-SIOS | 316a HANGUL LETTER RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316d HANGUL LETTER RIEUL-YEORINHIEUH | 316c HANGUL LETTER RIEUL-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 316f HANGUL LETTER MIEUM-SIOS | 316e HANGUL LETTER MIEUM-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3171 HANGUL LETTER KAPYEOUNMIEUM | 3170 HANGUL LETTER MIEUM-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3173 HANGUL LETTER PIEUP-TIKEUT | 3172 HANGUL LETTER PIEUP-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3175 HANGUL LETTER PIEUP-SIOS-TIKEUT | 3174 HANGUL LETTER PIEUP-SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3177 HANGUL LETTER PIEUP-THIEUTH | 3176 HANGUL LETTER PIEUP-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3179 HANGUL LETTER KAPYEOUNSSANGPIEUP | 3178 HANGUL LETTER KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317b HANGUL LETTER SIOS-NIEUN | 317a HANGUL LETTER SIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317d HANGUL LETTER SIOS-PIEUP | 317c HANGUL LETTER SIOS-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 317f HANGUL LETTER PANSIOS | 317e HANGUL LETTER SIOS-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3181 HANGUL LETTER YESIEUNG | 3180 HANGUL LETTER SSANGIEUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3183 HANGUL LETTER YESIEUNG-PANSIOS | 3182 HANGUL LETTER YESIEUNG-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3185 HANGUL LETTER SSANGHIEUH | 3184 HANGUL LETTER KAPYEOUNPHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3187 HANGUL LETTER YO-YA | 3186 HANGUL LETTER YEORINHIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3189 HANGUL LETTER YO-I | 3188 HANGUL LETTER YO-YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 318b HANGUL LETTER YU-YE | 318a HANGUL LETTER YU-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 318d HANGUL LETTER ARAEA | 318c HANGUL LETTER YU-I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 318f (null) | 318e HANGUL LETTER ARAEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3191 IDEOGRAPHIC ANNOTATION REVERSE MARK | 3190 IDEOGRAPHIC ANNOTATION LINKING MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3193 IDEOGRAPHIC ANNOTATION TWO MARK | 3192 IDEOGRAPHIC ANNOTATION ONE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3195 IDEOGRAPHIC ANNOTATION FOUR MARK | 3194 IDEOGRAPHIC ANNOTATION THREE MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3197 IDEOGRAPHIC ANNOTATION MIDDLE MARK | 3196 IDEOGRAPHIC ANNOTATION TOP MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3199 IDEOGRAPHIC ANNOTATION FIRST MARK | 3198 IDEOGRAPHIC ANNOTATION BOTTOM MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319b IDEOGRAPHIC ANNOTATION THIRD MARK | 319a IDEOGRAPHIC ANNOTATION SECOND MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319d IDEOGRAPHIC ANNOTATION HEAVEN MARK | 319c IDEOGRAPHIC ANNOTATION FOURTH MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 319f IDEOGRAPHIC ANNOTATION MAN MARK | 319e IDEOGRAPHIC ANNOTATION EARTH MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a1 BOPOMOFO LETTER ZI | 31a0 BOPOMOFO LETTER BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a3 BOPOMOFO LETTER GU | 31a2 BOPOMOFO LETTER JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a5 BOPOMOFO LETTER ENN | 31a4 BOPOMOFO LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a7 BOPOMOFO LETTER ONN | 31a6 BOPOMOFO LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31a9 BOPOMOFO LETTER ANN | 31a8 BOPOMOFO LETTER IR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ab BOPOMOFO LETTER UNN | 31aa BOPOMOFO LETTER INN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ad BOPOMOFO LETTER NGG | 31ac BOPOMOFO LETTER IM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31af BOPOMOFO LETTER AUNN | 31ae BOPOMOFO LETTER AINN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b1 BOPOMOFO LETTER OM | 31b0 BOPOMOFO LETTER AM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b3 BOPOMOFO LETTER INNN | 31b2 BOPOMOFO LETTER ONG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b5 BOPOMOFO FINAL LETTER T | 31b4 BOPOMOFO FINAL LETTER P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b7 BOPOMOFO FINAL LETTER H | 31b6 BOPOMOFO FINAL LETTER K */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31b9 BOPOMOFO LETTER LH | 31b8 BOPOMOFO LETTER GH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 31bb (null) | 31ba BOPOMOFO LETTER ZY */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31bd (null) | 31bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31bf (null) | 31be (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c1 CJK STROKE WG | 31c0 CJK STROKE T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c3 CJK STROKE BXG | 31c2 CJK STROKE XG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c5 CJK STROKE HZZ | 31c4 CJK STROKE SW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c7 CJK STROKE HP | 31c6 CJK STROKE HZG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31c9 CJK STROKE SZWG | 31c8 CJK STROKE HZWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cb CJK STROKE HZZP | 31ca CJK STROKE HZT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cd CJK STROKE HZW | 31cc CJK STROKE HPWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31cf CJK STROKE N | 31ce CJK STROKE HZZZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d1 CJK STROKE S | 31d0 CJK STROKE H */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d3 CJK STROKE SP | 31d2 CJK STROKE P */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d5 CJK STROKE HZ | 31d4 CJK STROKE D */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d7 CJK STROKE SZ | 31d6 CJK STROKE HG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31d9 CJK STROKE ST | 31d8 CJK STROKE SWZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31db CJK STROKE PD | 31da CJK STROKE SG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31dd CJK STROKE TN | 31dc CJK STROKE PZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31df CJK STROKE SWG | 31de CJK STROKE SZZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31e1 CJK STROKE HZZZG | 31e0 CJK STROKE HXWG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 31e3 CJK STROKE Q | 31e2 CJK STROKE PG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e5 (null) | 31e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e7 (null) | 31e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31e9 (null) | 31e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31eb (null) | 31ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31ed (null) | 31ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 31ef (null) | 31ee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f1 KATAKANA LETTER SMALL SI | 31f0 KATAKANA LETTER SMALL KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f3 KATAKANA LETTER SMALL TO | 31f2 KATAKANA LETTER SMALL SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f5 KATAKANA LETTER SMALL HA | 31f4 KATAKANA LETTER SMALL NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f7 KATAKANA LETTER SMALL HU | 31f6 KATAKANA LETTER SMALL HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31f9 KATAKANA LETTER SMALL HO | 31f8 KATAKANA LETTER SMALL HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31fb KATAKANA LETTER SMALL RA | 31fa KATAKANA LETTER SMALL MU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31fd KATAKANA LETTER SMALL RU | 31fc KATAKANA LETTER SMALL RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 31ff KATAKANA LETTER SMALL RO | 31fe KATAKANA LETTER SMALL RE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3201 PARENTHESIZED HANGUL NIEUN | 3200 PARENTHESIZED HANGUL KIYEOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3203 PARENTHESIZED HANGUL RIEUL | 3202 PARENTHESIZED HANGUL TIKEUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3205 PARENTHESIZED HANGUL PIEUP | 3204 PARENTHESIZED HANGUL MIEUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3207 PARENTHESIZED HANGUL IEUNG | 3206 PARENTHESIZED HANGUL SIOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3209 PARENTHESIZED HANGUL CHIEUCH | 3208 PARENTHESIZED HANGUL CIEUC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320b PARENTHESIZED HANGUL THIEUTH | 320a PARENTHESIZED HANGUL KHIEUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320d PARENTHESIZED HANGUL HIEUH | 320c PARENTHESIZED HANGUL PHIEUPH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 320f PARENTHESIZED HANGUL NIEUN A | 320e PARENTHESIZED HANGUL KIYEOK A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3211 PARENTHESIZED HANGUL RIEUL A | 3210 PARENTHESIZED HANGUL TIKEUT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3213 PARENTHESIZED HANGUL PIEUP A | 3212 PARENTHESIZED HANGUL MIEUM A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3215 PARENTHESIZED HANGUL IEUNG A | 3214 PARENTHESIZED HANGUL SIOS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3217 PARENTHESIZED HANGUL CHIEUCH A | 3216 PARENTHESIZED HANGUL CIEUC A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3219 PARENTHESIZED HANGUL THIEUTH A | 3218 PARENTHESIZED HANGUL KHIEUKH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 321b PARENTHESIZED HANGUL HIEUH A | 321a PARENTHESIZED HANGUL PHIEUPH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 321d PARENTHESIZED KOREAN CHARACTER OJEON | 321c PARENTHESIZED HANGUL CIEUC U */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 321f (null) | 321e PARENTHESIZED KOREAN CHARACTER O HU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3221 PARENTHESIZED IDEOGRAPH TWO | 3220 PARENTHESIZED IDEOGRAPH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3223 PARENTHESIZED IDEOGRAPH FOUR | 3222 PARENTHESIZED IDEOGRAPH THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3225 PARENTHESIZED IDEOGRAPH SIX | 3224 PARENTHESIZED IDEOGRAPH FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3227 PARENTHESIZED IDEOGRAPH EIGHT | 3226 PARENTHESIZED IDEOGRAPH SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3229 PARENTHESIZED IDEOGRAPH TEN | 3228 PARENTHESIZED IDEOGRAPH NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322b PARENTHESIZED IDEOGRAPH FIRE | 322a PARENTHESIZED IDEOGRAPH MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322d PARENTHESIZED IDEOGRAPH WOOD | 322c PARENTHESIZED IDEOGRAPH WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 322f PARENTHESIZED IDEOGRAPH EARTH | 322e PARENTHESIZED IDEOGRAPH METAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3231 PARENTHESIZED IDEOGRAPH STOCK | 3230 PARENTHESIZED IDEOGRAPH SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3233 PARENTHESIZED IDEOGRAPH SOCIETY | 3232 PARENTHESIZED IDEOGRAPH HAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3235 PARENTHESIZED IDEOGRAPH SPECIAL | 3234 PARENTHESIZED IDEOGRAPH NAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3237 PARENTHESIZED IDEOGRAPH CONGRATULATION | 3236 PARENTHESIZED IDEOGRAPH FINANCIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3239 PARENTHESIZED IDEOGRAPH REPRESENT | 3238 PARENTHESIZED IDEOGRAPH LABOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323b PARENTHESIZED IDEOGRAPH STUDY | 323a PARENTHESIZED IDEOGRAPH CALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323d PARENTHESIZED IDEOGRAPH ENTERPRISE | 323c PARENTHESIZED IDEOGRAPH SUPERVISE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 323f PARENTHESIZED IDEOGRAPH ALLIANCE | 323e PARENTHESIZED IDEOGRAPH RESOURCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3241 PARENTHESIZED IDEOGRAPH REST | 3240 PARENTHESIZED IDEOGRAPH FESTIVAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3243 PARENTHESIZED IDEOGRAPH REACH | 3242 PARENTHESIZED IDEOGRAPH SELF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3245 CIRCLED IDEOGRAPH KINDERGARTEN | 3244 CIRCLED IDEOGRAPH QUESTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3247 CIRCLED IDEOGRAPH KOTO | 3246 CIRCLED IDEOGRAPH SCHOOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3249 CIRCLED NUMBER TWENTY ON BLACK SQUARE | 3248 CIRCLED NUMBER TEN ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324b CIRCLED NUMBER FORTY ON BLACK SQUARE | 324a CIRCLED NUMBER THIRTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324d CIRCLED NUMBER SIXTY ON BLACK SQUARE | 324c CIRCLED NUMBER FIFTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 324f CIRCLED NUMBER EIGHTY ON BLACK SQUARE | 324e CIRCLED NUMBER SEVENTY ON BLACK SQUARE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3251 CIRCLED NUMBER TWENTY ONE | 3250 PARTNERSHIP SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3253 CIRCLED NUMBER TWENTY THREE | 3252 CIRCLED NUMBER TWENTY TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3255 CIRCLED NUMBER TWENTY FIVE | 3254 CIRCLED NUMBER TWENTY FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3257 CIRCLED NUMBER TWENTY SEVEN | 3256 CIRCLED NUMBER TWENTY SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3259 CIRCLED NUMBER TWENTY NINE | 3258 CIRCLED NUMBER TWENTY EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325b CIRCLED NUMBER THIRTY ONE | 325a CIRCLED NUMBER THIRTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325d CIRCLED NUMBER THIRTY THREE | 325c CIRCLED NUMBER THIRTY TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 325f CIRCLED NUMBER THIRTY FIVE | 325e CIRCLED NUMBER THIRTY FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3261 CIRCLED HANGUL NIEUN | 3260 CIRCLED HANGUL KIYEOK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3263 CIRCLED HANGUL RIEUL | 3262 CIRCLED HANGUL TIKEUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3265 CIRCLED HANGUL PIEUP | 3264 CIRCLED HANGUL MIEUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3267 CIRCLED HANGUL IEUNG | 3266 CIRCLED HANGUL SIOS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3269 CIRCLED HANGUL CHIEUCH | 3268 CIRCLED HANGUL CIEUC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326b CIRCLED HANGUL THIEUTH | 326a CIRCLED HANGUL KHIEUKH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326d CIRCLED HANGUL HIEUH | 326c CIRCLED HANGUL PHIEUPH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 326f CIRCLED HANGUL NIEUN A | 326e CIRCLED HANGUL KIYEOK A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3271 CIRCLED HANGUL RIEUL A | 3270 CIRCLED HANGUL TIKEUT A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3273 CIRCLED HANGUL PIEUP A | 3272 CIRCLED HANGUL MIEUM A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3275 CIRCLED HANGUL IEUNG A | 3274 CIRCLED HANGUL SIOS A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3277 CIRCLED HANGUL CHIEUCH A | 3276 CIRCLED HANGUL CIEUC A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3279 CIRCLED HANGUL THIEUTH A | 3278 CIRCLED HANGUL KHIEUKH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327b CIRCLED HANGUL HIEUH A | 327a CIRCLED HANGUL PHIEUPH A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327d CIRCLED KOREAN CHARACTER JUEUI | 327c CIRCLED KOREAN CHARACTER CHAMKO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 327f KOREAN STANDARD SYMBOL | 327e CIRCLED HANGUL IEUNG U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3281 CIRCLED IDEOGRAPH TWO | 3280 CIRCLED IDEOGRAPH ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3283 CIRCLED IDEOGRAPH FOUR | 3282 CIRCLED IDEOGRAPH THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3285 CIRCLED IDEOGRAPH SIX | 3284 CIRCLED IDEOGRAPH FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3287 CIRCLED IDEOGRAPH EIGHT | 3286 CIRCLED IDEOGRAPH SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3289 CIRCLED IDEOGRAPH TEN | 3288 CIRCLED IDEOGRAPH NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328b CIRCLED IDEOGRAPH FIRE | 328a CIRCLED IDEOGRAPH MOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328d CIRCLED IDEOGRAPH WOOD | 328c CIRCLED IDEOGRAPH WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 328f CIRCLED IDEOGRAPH EARTH | 328e CIRCLED IDEOGRAPH METAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3291 CIRCLED IDEOGRAPH STOCK | 3290 CIRCLED IDEOGRAPH SUN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3293 CIRCLED IDEOGRAPH SOCIETY | 3292 CIRCLED IDEOGRAPH HAVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3295 CIRCLED IDEOGRAPH SPECIAL | 3294 CIRCLED IDEOGRAPH NAME */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3297 CIRCLED IDEOGRAPH CONGRATULATION | 3296 CIRCLED IDEOGRAPH FINANCIAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3299 CIRCLED IDEOGRAPH SECRET | 3298 CIRCLED IDEOGRAPH LABOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329b CIRCLED IDEOGRAPH FEMALE | 329a CIRCLED IDEOGRAPH MALE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329d CIRCLED IDEOGRAPH EXCELLENT | 329c CIRCLED IDEOGRAPH SUITABLE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 329f CIRCLED IDEOGRAPH ATTENTION | 329e CIRCLED IDEOGRAPH PRINT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a1 CIRCLED IDEOGRAPH REST | 32a0 CIRCLED IDEOGRAPH ITEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a3 CIRCLED IDEOGRAPH CORRECT | 32a2 CIRCLED IDEOGRAPH COPY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a5 CIRCLED IDEOGRAPH CENTRE | 32a4 CIRCLED IDEOGRAPH HIGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a7 CIRCLED IDEOGRAPH LEFT | 32a6 CIRCLED IDEOGRAPH LOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32a9 CIRCLED IDEOGRAPH MEDICINE | 32a8 CIRCLED IDEOGRAPH RIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ab CIRCLED IDEOGRAPH STUDY | 32aa CIRCLED IDEOGRAPH RELIGION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ad CIRCLED IDEOGRAPH ENTERPRISE | 32ac CIRCLED IDEOGRAPH SUPERVISE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32af CIRCLED IDEOGRAPH ALLIANCE | 32ae CIRCLED IDEOGRAPH RESOURCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b1 CIRCLED NUMBER THIRTY SIX | 32b0 CIRCLED IDEOGRAPH NIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b3 CIRCLED NUMBER THIRTY EIGHT | 32b2 CIRCLED NUMBER THIRTY SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b5 CIRCLED NUMBER FORTY | 32b4 CIRCLED NUMBER THIRTY NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b7 CIRCLED NUMBER FORTY TWO | 32b6 CIRCLED NUMBER FORTY ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32b9 CIRCLED NUMBER FORTY FOUR | 32b8 CIRCLED NUMBER FORTY THREE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bb CIRCLED NUMBER FORTY SIX | 32ba CIRCLED NUMBER FORTY FIVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bd CIRCLED NUMBER FORTY EIGHT | 32bc CIRCLED NUMBER FORTY SEVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32bf CIRCLED NUMBER FIFTY | 32be CIRCLED NUMBER FORTY NINE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUA | 32c0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL | 32c2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE | 32c4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST | 32c6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32c9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBE | 32c8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMB | 32ca IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cd SQUARE ERG | 32cc SQUARE HG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32cf LIMITED LIABILITY SIGN | 32ce SQUARE EV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d1 CIRCLED KATAKANA I | 32d0 CIRCLED KATAKANA A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d3 CIRCLED KATAKANA E | 32d2 CIRCLED KATAKANA U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d5 CIRCLED KATAKANA KA | 32d4 CIRCLED KATAKANA O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d7 CIRCLED KATAKANA KU | 32d6 CIRCLED KATAKANA KI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32d9 CIRCLED KATAKANA KO | 32d8 CIRCLED KATAKANA KE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32db CIRCLED KATAKANA SI | 32da CIRCLED KATAKANA SA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32dd CIRCLED KATAKANA SE | 32dc CIRCLED KATAKANA SU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32df CIRCLED KATAKANA TA | 32de CIRCLED KATAKANA SO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e1 CIRCLED KATAKANA TU | 32e0 CIRCLED KATAKANA TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e3 CIRCLED KATAKANA TO | 32e2 CIRCLED KATAKANA TE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e5 CIRCLED KATAKANA NI | 32e4 CIRCLED KATAKANA NA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e7 CIRCLED KATAKANA NE | 32e6 CIRCLED KATAKANA NU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32e9 CIRCLED KATAKANA HA | 32e8 CIRCLED KATAKANA NO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32eb CIRCLED KATAKANA HU | 32ea CIRCLED KATAKANA HI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ed CIRCLED KATAKANA HO | 32ec CIRCLED KATAKANA HE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32ef CIRCLED KATAKANA MI | 32ee CIRCLED KATAKANA MA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f1 CIRCLED KATAKANA ME | 32f0 CIRCLED KATAKANA MU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f3 CIRCLED KATAKANA YA | 32f2 CIRCLED KATAKANA MO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f5 CIRCLED KATAKANA YO | 32f4 CIRCLED KATAKANA YU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f7 CIRCLED KATAKANA RI | 32f6 CIRCLED KATAKANA RA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32f9 CIRCLED KATAKANA RE | 32f8 CIRCLED KATAKANA RU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32fb CIRCLED KATAKANA WA | 32fa CIRCLED KATAKANA RO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 32fd CIRCLED KATAKANA WE | 32fc CIRCLED KATAKANA WI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* 32ff (null) | 32fe CIRCLED KATAKANA WO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3301 SQUARE ARUHUA | 3300 SQUARE APAATO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3303 SQUARE AARU | 3302 SQUARE ANPEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3305 SQUARE INTI | 3304 SQUARE ININGU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3307 SQUARE ESUKUUDO | 3306 SQUARE UON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3309 SQUARE ONSU | 3308 SQUARE EEKAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330b SQUARE KAIRI | 330a SQUARE OOMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330d SQUARE KARORII | 330c SQUARE KARATTO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 330f SQUARE GANMA | 330e SQUARE GARON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3311 SQUARE GINII | 3310 SQUARE GIGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3313 SQUARE GIRUDAA | 3312 SQUARE KYURII */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3315 SQUARE KIROGURAMU | 3314 SQUARE KIRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3317 SQUARE KIROWATTO | 3316 SQUARE KIROMEETORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3319 SQUARE GURAMUTON | 3318 SQUARE GURAMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331b SQUARE KUROONE | 331a SQUARE KURUZEIRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331d SQUARE KORUNA | 331c SQUARE KEESU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 331f SQUARE SAIKURU | 331e SQUARE KOOPO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3321 SQUARE SIRINGU | 3320 SQUARE SANTIIMU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3323 SQUARE SENTO | 3322 SQUARE SENTI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3325 SQUARE DESI | 3324 SQUARE DAASU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3327 SQUARE TON | 3326 SQUARE DORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3329 SQUARE NOTTO | 3328 SQUARE NANO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332b SQUARE PAASENTO | 332a SQUARE HAITU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332d SQUARE BAARERU | 332c SQUARE PAATU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 332f SQUARE PIKURU | 332e SQUARE PIASUTORU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3331 SQUARE BIRU | 3330 SQUARE PIKO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3333 SQUARE HUIITO | 3332 SQUARE HUARADDO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3335 SQUARE HURAN | 3334 SQUARE BUSSYERU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3337 SQUARE PESO | 3336 SQUARE HEKUTAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3339 SQUARE HERUTU | 3338 SQUARE PENIHI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333b SQUARE PEEZI | 333a SQUARE PENSU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333d SQUARE POINTO | 333c SQUARE BEETA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 333f SQUARE HON | 333e SQUARE BORUTO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3341 SQUARE HOORU | 3340 SQUARE PONDO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3343 SQUARE MAIKURO | 3342 SQUARE HOON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3345 SQUARE MAHHA | 3344 SQUARE MAIRU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3347 SQUARE MANSYON | 3346 SQUARE MARUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3349 SQUARE MIRI | 3348 SQUARE MIKURON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334b SQUARE MEGA | 334a SQUARE MIRIBAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334d SQUARE MEETORU | 334c SQUARE MEGATON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 334f SQUARE YAARU | 334e SQUARE YAADO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3351 SQUARE RITTORU | 3350 SQUARE YUAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3353 SQUARE RUPII | 3352 SQUARE RIRA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3355 SQUARE REMU | 3354 SQUARE RUUBURU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3357 SQUARE WATTO | 3356 SQUARE RENTOGEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3359 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR O | 3358 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR Z */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335b IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 335a IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335d IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F | 335c IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 335f IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S | 335e IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3361 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR N | 3360 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3363 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E | 3362 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3365 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 3364 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3367 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F | 3366 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3369 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S | 3368 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336b IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR N | 336a IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336d IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 336c IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 336f IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T | 336e IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3371 SQUARE HPA | 3370 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3373 SQUARE AU | 3372 SQUARE DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3375 SQUARE OV | 3374 SQUARE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3377 SQUARE DM | 3376 SQUARE PC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3379 SQUARE DM CUBED | 3378 SQUARE DM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337b SQUARE ERA NAME HEISEI | 337a SQUARE IU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337d SQUARE ERA NAME TAISYOU | 337c SQUARE ERA NAME SYOUWA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 337f SQUARE CORPORATION | 337e SQUARE ERA NAME MEIZI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3381 SQUARE NA | 3380 SQUARE PA AMPS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3383 SQUARE MA | 3382 SQUARE MU A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3385 SQUARE KB | 3384 SQUARE KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3387 SQUARE GB | 3386 SQUARE MB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3389 SQUARE KCAL | 3388 SQUARE CAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338b SQUARE NF | 338a SQUARE PF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338d SQUARE MU G | 338c SQUARE MU F */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 338f SQUARE KG | 338e SQUARE MG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3391 SQUARE KHZ | 3390 SQUARE HZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3393 SQUARE GHZ | 3392 SQUARE MHZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3395 SQUARE MU L | 3394 SQUARE THZ */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3397 SQUARE DL | 3396 SQUARE ML */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 3399 SQUARE FM | 3398 SQUARE KL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339b SQUARE MU M | 339a SQUARE NM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339d SQUARE CM | 339c SQUARE MM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 339f SQUARE MM SQUARED | 339e SQUARE KM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a1 SQUARE M SQUARED | 33a0 SQUARE CM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a3 SQUARE MM CUBED | 33a2 SQUARE KM SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a5 SQUARE M CUBED | 33a4 SQUARE CM CUBED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a7 SQUARE M OVER S | 33a6 SQUARE KM CUBED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33a9 SQUARE PA | 33a8 SQUARE M OVER S SQUARED */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ab SQUARE MPA | 33aa SQUARE KPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ad SQUARE RAD | 33ac SQUARE GPA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33af SQUARE RAD OVER S SQUARED | 33ae SQUARE RAD OVER S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b1 SQUARE NS | 33b0 SQUARE PS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b3 SQUARE MS | 33b2 SQUARE MU S */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b5 SQUARE NV | 33b4 SQUARE PV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b7 SQUARE MV | 33b6 SQUARE MU V */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33b9 SQUARE MV MEGA | 33b8 SQUARE KV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bb SQUARE NW | 33ba SQUARE PW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bd SQUARE MW | 33bc SQUARE MU W */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33bf SQUARE MW MEGA | 33be SQUARE KW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c1 SQUARE M OHM | 33c0 SQUARE K OHM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c3 SQUARE BQ | 33c2 SQUARE AM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c5 SQUARE CD | 33c4 SQUARE CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c7 SQUARE CO | 33c6 SQUARE C OVER KG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33c9 SQUARE GY | 33c8 SQUARE DB */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cb SQUARE HP | 33ca SQUARE HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cd SQUARE KK | 33cc SQUARE IN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33cf SQUARE KT | 33ce SQUARE KM CAPITAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d1 SQUARE LN | 33d0 SQUARE LM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d3 SQUARE LX | 33d2 SQUARE LOG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d5 SQUARE MIL | 33d4 SQUARE MB SMALL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d7 SQUARE PH | 33d6 SQUARE MOL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33d9 SQUARE PPM | 33d8 SQUARE PM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33db SQUARE SR | 33da SQUARE PR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33dd SQUARE WB | 33dc SQUARE SV */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33df SQUARE A OVER M | 33de SQUARE V OVER M */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33e0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FO | 33e2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SI | 33e4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EI | 33e6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33e9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TE | 33e8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33eb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33ea IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ed IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FO | 33ec IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ef IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SI | 33ee IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EI | 33f0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33f9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33f8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33fb IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW | 33fa IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33fd IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH | 33fc IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 33ff SQUARE GAL | 33fe IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3401 CJK Ideograph Extension A-3401 | 3400 CJK Ideograph Extension A-3400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3403 CJK Ideograph Extension A-3403 | 3402 CJK Ideograph Extension A-3402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3405 CJK Ideograph Extension A-3405 | 3404 CJK Ideograph Extension A-3404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3407 CJK Ideograph Extension A-3407 | 3406 CJK Ideograph Extension A-3406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3409 CJK Ideograph Extension A-3409 | 3408 CJK Ideograph Extension A-3408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340b CJK Ideograph Extension A-340B | 340a CJK Ideograph Extension A-340A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340d CJK Ideograph Extension A-340D | 340c CJK Ideograph Extension A-340C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 340f CJK Ideograph Extension A-340F | 340e CJK Ideograph Extension A-340E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3411 CJK Ideograph Extension A-3411 | 3410 CJK Ideograph Extension A-3410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3413 CJK Ideograph Extension A-3413 | 3412 CJK Ideograph Extension A-3412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3415 CJK Ideograph Extension A-3415 | 3414 CJK Ideograph Extension A-3414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3417 CJK Ideograph Extension A-3417 | 3416 CJK Ideograph Extension A-3416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3419 CJK Ideograph Extension A-3419 | 3418 CJK Ideograph Extension A-3418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341b CJK Ideograph Extension A-341B | 341a CJK Ideograph Extension A-341A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341d CJK Ideograph Extension A-341D | 341c CJK Ideograph Extension A-341C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 341f CJK Ideograph Extension A-341F | 341e CJK Ideograph Extension A-341E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3421 CJK Ideograph Extension A-3421 | 3420 CJK Ideograph Extension A-3420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3423 CJK Ideograph Extension A-3423 | 3422 CJK Ideograph Extension A-3422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3425 CJK Ideograph Extension A-3425 | 3424 CJK Ideograph Extension A-3424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3427 CJK Ideograph Extension A-3427 | 3426 CJK Ideograph Extension A-3426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3429 CJK Ideograph Extension A-3429 | 3428 CJK Ideograph Extension A-3428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342b CJK Ideograph Extension A-342B | 342a CJK Ideograph Extension A-342A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342d CJK Ideograph Extension A-342D | 342c CJK Ideograph Extension A-342C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 342f CJK Ideograph Extension A-342F | 342e CJK Ideograph Extension A-342E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3431 CJK Ideograph Extension A-3431 | 3430 CJK Ideograph Extension A-3430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3433 CJK Ideograph Extension A-3433 | 3432 CJK Ideograph Extension A-3432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3435 CJK Ideograph Extension A-3435 | 3434 CJK Ideograph Extension A-3434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3437 CJK Ideograph Extension A-3437 | 3436 CJK Ideograph Extension A-3436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3439 CJK Ideograph Extension A-3439 | 3438 CJK Ideograph Extension A-3438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343b CJK Ideograph Extension A-343B | 343a CJK Ideograph Extension A-343A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343d CJK Ideograph Extension A-343D | 343c CJK Ideograph Extension A-343C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 343f CJK Ideograph Extension A-343F | 343e CJK Ideograph Extension A-343E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3441 CJK Ideograph Extension A-3441 | 3440 CJK Ideograph Extension A-3440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3443 CJK Ideograph Extension A-3443 | 3442 CJK Ideograph Extension A-3442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3445 CJK Ideograph Extension A-3445 | 3444 CJK Ideograph Extension A-3444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3447 CJK Ideograph Extension A-3447 | 3446 CJK Ideograph Extension A-3446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3449 CJK Ideograph Extension A-3449 | 3448 CJK Ideograph Extension A-3448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344b CJK Ideograph Extension A-344B | 344a CJK Ideograph Extension A-344A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344d CJK Ideograph Extension A-344D | 344c CJK Ideograph Extension A-344C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 344f CJK Ideograph Extension A-344F | 344e CJK Ideograph Extension A-344E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3451 CJK Ideograph Extension A-3451 | 3450 CJK Ideograph Extension A-3450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3453 CJK Ideograph Extension A-3453 | 3452 CJK Ideograph Extension A-3452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3455 CJK Ideograph Extension A-3455 | 3454 CJK Ideograph Extension A-3454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3457 CJK Ideograph Extension A-3457 | 3456 CJK Ideograph Extension A-3456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3459 CJK Ideograph Extension A-3459 | 3458 CJK Ideograph Extension A-3458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345b CJK Ideograph Extension A-345B | 345a CJK Ideograph Extension A-345A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345d CJK Ideograph Extension A-345D | 345c CJK Ideograph Extension A-345C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 345f CJK Ideograph Extension A-345F | 345e CJK Ideograph Extension A-345E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3461 CJK Ideograph Extension A-3461 | 3460 CJK Ideograph Extension A-3460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3463 CJK Ideograph Extension A-3463 | 3462 CJK Ideograph Extension A-3462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3465 CJK Ideograph Extension A-3465 | 3464 CJK Ideograph Extension A-3464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3467 CJK Ideograph Extension A-3467 | 3466 CJK Ideograph Extension A-3466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3469 CJK Ideograph Extension A-3469 | 3468 CJK Ideograph Extension A-3468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346b CJK Ideograph Extension A-346B | 346a CJK Ideograph Extension A-346A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346d CJK Ideograph Extension A-346D | 346c CJK Ideograph Extension A-346C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 346f CJK Ideograph Extension A-346F | 346e CJK Ideograph Extension A-346E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3471 CJK Ideograph Extension A-3471 | 3470 CJK Ideograph Extension A-3470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3473 CJK Ideograph Extension A-3473 | 3472 CJK Ideograph Extension A-3472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3475 CJK Ideograph Extension A-3475 | 3474 CJK Ideograph Extension A-3474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3477 CJK Ideograph Extension A-3477 | 3476 CJK Ideograph Extension A-3476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3479 CJK Ideograph Extension A-3479 | 3478 CJK Ideograph Extension A-3478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347b CJK Ideograph Extension A-347B | 347a CJK Ideograph Extension A-347A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347d CJK Ideograph Extension A-347D | 347c CJK Ideograph Extension A-347C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 347f CJK Ideograph Extension A-347F | 347e CJK Ideograph Extension A-347E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3481 CJK Ideograph Extension A-3481 | 3480 CJK Ideograph Extension A-3480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3483 CJK Ideograph Extension A-3483 | 3482 CJK Ideograph Extension A-3482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3485 CJK Ideograph Extension A-3485 | 3484 CJK Ideograph Extension A-3484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3487 CJK Ideograph Extension A-3487 | 3486 CJK Ideograph Extension A-3486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3489 CJK Ideograph Extension A-3489 | 3488 CJK Ideograph Extension A-3488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348b CJK Ideograph Extension A-348B | 348a CJK Ideograph Extension A-348A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348d CJK Ideograph Extension A-348D | 348c CJK Ideograph Extension A-348C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 348f CJK Ideograph Extension A-348F | 348e CJK Ideograph Extension A-348E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3491 CJK Ideograph Extension A-3491 | 3490 CJK Ideograph Extension A-3490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3493 CJK Ideograph Extension A-3493 | 3492 CJK Ideograph Extension A-3492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3495 CJK Ideograph Extension A-3495 | 3494 CJK Ideograph Extension A-3494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3497 CJK Ideograph Extension A-3497 | 3496 CJK Ideograph Extension A-3496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3499 CJK Ideograph Extension A-3499 | 3498 CJK Ideograph Extension A-3498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349b CJK Ideograph Extension A-349B | 349a CJK Ideograph Extension A-349A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349d CJK Ideograph Extension A-349D | 349c CJK Ideograph Extension A-349C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 349f CJK Ideograph Extension A-349F | 349e CJK Ideograph Extension A-349E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a1 CJK Ideograph Extension A-34A1 | 34a0 CJK Ideograph Extension A-34A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a3 CJK Ideograph Extension A-34A3 | 34a2 CJK Ideograph Extension A-34A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a5 CJK Ideograph Extension A-34A5 | 34a4 CJK Ideograph Extension A-34A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a7 CJK Ideograph Extension A-34A7 | 34a6 CJK Ideograph Extension A-34A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34a9 CJK Ideograph Extension A-34A9 | 34a8 CJK Ideograph Extension A-34A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ab CJK Ideograph Extension A-34AB | 34aa CJK Ideograph Extension A-34AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ad CJK Ideograph Extension A-34AD | 34ac CJK Ideograph Extension A-34AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34af CJK Ideograph Extension A-34AF | 34ae CJK Ideograph Extension A-34AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b1 CJK Ideograph Extension A-34B1 | 34b0 CJK Ideograph Extension A-34B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b3 CJK Ideograph Extension A-34B3 | 34b2 CJK Ideograph Extension A-34B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b5 CJK Ideograph Extension A-34B5 | 34b4 CJK Ideograph Extension A-34B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b7 CJK Ideograph Extension A-34B7 | 34b6 CJK Ideograph Extension A-34B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34b9 CJK Ideograph Extension A-34B9 | 34b8 CJK Ideograph Extension A-34B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bb CJK Ideograph Extension A-34BB | 34ba CJK Ideograph Extension A-34BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bd CJK Ideograph Extension A-34BD | 34bc CJK Ideograph Extension A-34BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34bf CJK Ideograph Extension A-34BF | 34be CJK Ideograph Extension A-34BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c1 CJK Ideograph Extension A-34C1 | 34c0 CJK Ideograph Extension A-34C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c3 CJK Ideograph Extension A-34C3 | 34c2 CJK Ideograph Extension A-34C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c5 CJK Ideograph Extension A-34C5 | 34c4 CJK Ideograph Extension A-34C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c7 CJK Ideograph Extension A-34C7 | 34c6 CJK Ideograph Extension A-34C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34c9 CJK Ideograph Extension A-34C9 | 34c8 CJK Ideograph Extension A-34C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cb CJK Ideograph Extension A-34CB | 34ca CJK Ideograph Extension A-34CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cd CJK Ideograph Extension A-34CD | 34cc CJK Ideograph Extension A-34CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34cf CJK Ideograph Extension A-34CF | 34ce CJK Ideograph Extension A-34CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d1 CJK Ideograph Extension A-34D1 | 34d0 CJK Ideograph Extension A-34D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d3 CJK Ideograph Extension A-34D3 | 34d2 CJK Ideograph Extension A-34D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d5 CJK Ideograph Extension A-34D5 | 34d4 CJK Ideograph Extension A-34D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d7 CJK Ideograph Extension A-34D7 | 34d6 CJK Ideograph Extension A-34D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34d9 CJK Ideograph Extension A-34D9 | 34d8 CJK Ideograph Extension A-34D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34db CJK Ideograph Extension A-34DB | 34da CJK Ideograph Extension A-34DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34dd CJK Ideograph Extension A-34DD | 34dc CJK Ideograph Extension A-34DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34df CJK Ideograph Extension A-34DF | 34de CJK Ideograph Extension A-34DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e1 CJK Ideograph Extension A-34E1 | 34e0 CJK Ideograph Extension A-34E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e3 CJK Ideograph Extension A-34E3 | 34e2 CJK Ideograph Extension A-34E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e5 CJK Ideograph Extension A-34E5 | 34e4 CJK Ideograph Extension A-34E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e7 CJK Ideograph Extension A-34E7 | 34e6 CJK Ideograph Extension A-34E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34e9 CJK Ideograph Extension A-34E9 | 34e8 CJK Ideograph Extension A-34E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34eb CJK Ideograph Extension A-34EB | 34ea CJK Ideograph Extension A-34EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ed CJK Ideograph Extension A-34ED | 34ec CJK Ideograph Extension A-34EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ef CJK Ideograph Extension A-34EF | 34ee CJK Ideograph Extension A-34EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f1 CJK Ideograph Extension A-34F1 | 34f0 CJK Ideograph Extension A-34F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f3 CJK Ideograph Extension A-34F3 | 34f2 CJK Ideograph Extension A-34F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f5 CJK Ideograph Extension A-34F5 | 34f4 CJK Ideograph Extension A-34F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f7 CJK Ideograph Extension A-34F7 | 34f6 CJK Ideograph Extension A-34F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34f9 CJK Ideograph Extension A-34F9 | 34f8 CJK Ideograph Extension A-34F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34fb CJK Ideograph Extension A-34FB | 34fa CJK Ideograph Extension A-34FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34fd CJK Ideograph Extension A-34FD | 34fc CJK Ideograph Extension A-34FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 34ff CJK Ideograph Extension A-34FF | 34fe CJK Ideograph Extension A-34FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3501 CJK Ideograph Extension A-3501 | 3500 CJK Ideograph Extension A-3500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3503 CJK Ideograph Extension A-3503 | 3502 CJK Ideograph Extension A-3502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3505 CJK Ideograph Extension A-3505 | 3504 CJK Ideograph Extension A-3504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3507 CJK Ideograph Extension A-3507 | 3506 CJK Ideograph Extension A-3506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3509 CJK Ideograph Extension A-3509 | 3508 CJK Ideograph Extension A-3508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350b CJK Ideograph Extension A-350B | 350a CJK Ideograph Extension A-350A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350d CJK Ideograph Extension A-350D | 350c CJK Ideograph Extension A-350C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 350f CJK Ideograph Extension A-350F | 350e CJK Ideograph Extension A-350E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3511 CJK Ideograph Extension A-3511 | 3510 CJK Ideograph Extension A-3510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3513 CJK Ideograph Extension A-3513 | 3512 CJK Ideograph Extension A-3512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3515 CJK Ideograph Extension A-3515 | 3514 CJK Ideograph Extension A-3514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3517 CJK Ideograph Extension A-3517 | 3516 CJK Ideograph Extension A-3516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3519 CJK Ideograph Extension A-3519 | 3518 CJK Ideograph Extension A-3518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351b CJK Ideograph Extension A-351B | 351a CJK Ideograph Extension A-351A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351d CJK Ideograph Extension A-351D | 351c CJK Ideograph Extension A-351C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 351f CJK Ideograph Extension A-351F | 351e CJK Ideograph Extension A-351E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3521 CJK Ideograph Extension A-3521 | 3520 CJK Ideograph Extension A-3520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3523 CJK Ideograph Extension A-3523 | 3522 CJK Ideograph Extension A-3522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3525 CJK Ideograph Extension A-3525 | 3524 CJK Ideograph Extension A-3524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3527 CJK Ideograph Extension A-3527 | 3526 CJK Ideograph Extension A-3526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3529 CJK Ideograph Extension A-3529 | 3528 CJK Ideograph Extension A-3528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352b CJK Ideograph Extension A-352B | 352a CJK Ideograph Extension A-352A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352d CJK Ideograph Extension A-352D | 352c CJK Ideograph Extension A-352C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 352f CJK Ideograph Extension A-352F | 352e CJK Ideograph Extension A-352E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3531 CJK Ideograph Extension A-3531 | 3530 CJK Ideograph Extension A-3530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3533 CJK Ideograph Extension A-3533 | 3532 CJK Ideograph Extension A-3532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3535 CJK Ideograph Extension A-3535 | 3534 CJK Ideograph Extension A-3534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3537 CJK Ideograph Extension A-3537 | 3536 CJK Ideograph Extension A-3536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3539 CJK Ideograph Extension A-3539 | 3538 CJK Ideograph Extension A-3538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353b CJK Ideograph Extension A-353B | 353a CJK Ideograph Extension A-353A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353d CJK Ideograph Extension A-353D | 353c CJK Ideograph Extension A-353C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 353f CJK Ideograph Extension A-353F | 353e CJK Ideograph Extension A-353E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3541 CJK Ideograph Extension A-3541 | 3540 CJK Ideograph Extension A-3540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3543 CJK Ideograph Extension A-3543 | 3542 CJK Ideograph Extension A-3542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3545 CJK Ideograph Extension A-3545 | 3544 CJK Ideograph Extension A-3544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3547 CJK Ideograph Extension A-3547 | 3546 CJK Ideograph Extension A-3546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3549 CJK Ideograph Extension A-3549 | 3548 CJK Ideograph Extension A-3548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354b CJK Ideograph Extension A-354B | 354a CJK Ideograph Extension A-354A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354d CJK Ideograph Extension A-354D | 354c CJK Ideograph Extension A-354C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 354f CJK Ideograph Extension A-354F | 354e CJK Ideograph Extension A-354E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3551 CJK Ideograph Extension A-3551 | 3550 CJK Ideograph Extension A-3550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3553 CJK Ideograph Extension A-3553 | 3552 CJK Ideograph Extension A-3552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3555 CJK Ideograph Extension A-3555 | 3554 CJK Ideograph Extension A-3554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3557 CJK Ideograph Extension A-3557 | 3556 CJK Ideograph Extension A-3556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3559 CJK Ideograph Extension A-3559 | 3558 CJK Ideograph Extension A-3558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355b CJK Ideograph Extension A-355B | 355a CJK Ideograph Extension A-355A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355d CJK Ideograph Extension A-355D | 355c CJK Ideograph Extension A-355C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 355f CJK Ideograph Extension A-355F | 355e CJK Ideograph Extension A-355E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3561 CJK Ideograph Extension A-3561 | 3560 CJK Ideograph Extension A-3560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3563 CJK Ideograph Extension A-3563 | 3562 CJK Ideograph Extension A-3562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3565 CJK Ideograph Extension A-3565 | 3564 CJK Ideograph Extension A-3564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3567 CJK Ideograph Extension A-3567 | 3566 CJK Ideograph Extension A-3566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3569 CJK Ideograph Extension A-3569 | 3568 CJK Ideograph Extension A-3568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356b CJK Ideograph Extension A-356B | 356a CJK Ideograph Extension A-356A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356d CJK Ideograph Extension A-356D | 356c CJK Ideograph Extension A-356C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 356f CJK Ideograph Extension A-356F | 356e CJK Ideograph Extension A-356E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3571 CJK Ideograph Extension A-3571 | 3570 CJK Ideograph Extension A-3570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3573 CJK Ideograph Extension A-3573 | 3572 CJK Ideograph Extension A-3572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3575 CJK Ideograph Extension A-3575 | 3574 CJK Ideograph Extension A-3574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3577 CJK Ideograph Extension A-3577 | 3576 CJK Ideograph Extension A-3576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3579 CJK Ideograph Extension A-3579 | 3578 CJK Ideograph Extension A-3578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357b CJK Ideograph Extension A-357B | 357a CJK Ideograph Extension A-357A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357d CJK Ideograph Extension A-357D | 357c CJK Ideograph Extension A-357C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 357f CJK Ideograph Extension A-357F | 357e CJK Ideograph Extension A-357E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3581 CJK Ideograph Extension A-3581 | 3580 CJK Ideograph Extension A-3580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3583 CJK Ideograph Extension A-3583 | 3582 CJK Ideograph Extension A-3582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3585 CJK Ideograph Extension A-3585 | 3584 CJK Ideograph Extension A-3584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3587 CJK Ideograph Extension A-3587 | 3586 CJK Ideograph Extension A-3586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3589 CJK Ideograph Extension A-3589 | 3588 CJK Ideograph Extension A-3588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358b CJK Ideograph Extension A-358B | 358a CJK Ideograph Extension A-358A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358d CJK Ideograph Extension A-358D | 358c CJK Ideograph Extension A-358C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 358f CJK Ideograph Extension A-358F | 358e CJK Ideograph Extension A-358E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3591 CJK Ideograph Extension A-3591 | 3590 CJK Ideograph Extension A-3590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3593 CJK Ideograph Extension A-3593 | 3592 CJK Ideograph Extension A-3592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3595 CJK Ideograph Extension A-3595 | 3594 CJK Ideograph Extension A-3594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3597 CJK Ideograph Extension A-3597 | 3596 CJK Ideograph Extension A-3596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3599 CJK Ideograph Extension A-3599 | 3598 CJK Ideograph Extension A-3598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359b CJK Ideograph Extension A-359B | 359a CJK Ideograph Extension A-359A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359d CJK Ideograph Extension A-359D | 359c CJK Ideograph Extension A-359C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 359f CJK Ideograph Extension A-359F | 359e CJK Ideograph Extension A-359E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a1 CJK Ideograph Extension A-35A1 | 35a0 CJK Ideograph Extension A-35A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a3 CJK Ideograph Extension A-35A3 | 35a2 CJK Ideograph Extension A-35A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a5 CJK Ideograph Extension A-35A5 | 35a4 CJK Ideograph Extension A-35A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a7 CJK Ideograph Extension A-35A7 | 35a6 CJK Ideograph Extension A-35A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35a9 CJK Ideograph Extension A-35A9 | 35a8 CJK Ideograph Extension A-35A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ab CJK Ideograph Extension A-35AB | 35aa CJK Ideograph Extension A-35AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ad CJK Ideograph Extension A-35AD | 35ac CJK Ideograph Extension A-35AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35af CJK Ideograph Extension A-35AF | 35ae CJK Ideograph Extension A-35AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b1 CJK Ideograph Extension A-35B1 | 35b0 CJK Ideograph Extension A-35B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b3 CJK Ideograph Extension A-35B3 | 35b2 CJK Ideograph Extension A-35B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b5 CJK Ideograph Extension A-35B5 | 35b4 CJK Ideograph Extension A-35B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b7 CJK Ideograph Extension A-35B7 | 35b6 CJK Ideograph Extension A-35B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35b9 CJK Ideograph Extension A-35B9 | 35b8 CJK Ideograph Extension A-35B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bb CJK Ideograph Extension A-35BB | 35ba CJK Ideograph Extension A-35BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bd CJK Ideograph Extension A-35BD | 35bc CJK Ideograph Extension A-35BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35bf CJK Ideograph Extension A-35BF | 35be CJK Ideograph Extension A-35BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c1 CJK Ideograph Extension A-35C1 | 35c0 CJK Ideograph Extension A-35C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c3 CJK Ideograph Extension A-35C3 | 35c2 CJK Ideograph Extension A-35C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c5 CJK Ideograph Extension A-35C5 | 35c4 CJK Ideograph Extension A-35C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c7 CJK Ideograph Extension A-35C7 | 35c6 CJK Ideograph Extension A-35C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35c9 CJK Ideograph Extension A-35C9 | 35c8 CJK Ideograph Extension A-35C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cb CJK Ideograph Extension A-35CB | 35ca CJK Ideograph Extension A-35CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cd CJK Ideograph Extension A-35CD | 35cc CJK Ideograph Extension A-35CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35cf CJK Ideograph Extension A-35CF | 35ce CJK Ideograph Extension A-35CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d1 CJK Ideograph Extension A-35D1 | 35d0 CJK Ideograph Extension A-35D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d3 CJK Ideograph Extension A-35D3 | 35d2 CJK Ideograph Extension A-35D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d5 CJK Ideograph Extension A-35D5 | 35d4 CJK Ideograph Extension A-35D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d7 CJK Ideograph Extension A-35D7 | 35d6 CJK Ideograph Extension A-35D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35d9 CJK Ideograph Extension A-35D9 | 35d8 CJK Ideograph Extension A-35D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35db CJK Ideograph Extension A-35DB | 35da CJK Ideograph Extension A-35DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35dd CJK Ideograph Extension A-35DD | 35dc CJK Ideograph Extension A-35DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35df CJK Ideograph Extension A-35DF | 35de CJK Ideograph Extension A-35DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e1 CJK Ideograph Extension A-35E1 | 35e0 CJK Ideograph Extension A-35E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e3 CJK Ideograph Extension A-35E3 | 35e2 CJK Ideograph Extension A-35E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e5 CJK Ideograph Extension A-35E5 | 35e4 CJK Ideograph Extension A-35E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e7 CJK Ideograph Extension A-35E7 | 35e6 CJK Ideograph Extension A-35E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35e9 CJK Ideograph Extension A-35E9 | 35e8 CJK Ideograph Extension A-35E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35eb CJK Ideograph Extension A-35EB | 35ea CJK Ideograph Extension A-35EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ed CJK Ideograph Extension A-35ED | 35ec CJK Ideograph Extension A-35EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ef CJK Ideograph Extension A-35EF | 35ee CJK Ideograph Extension A-35EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f1 CJK Ideograph Extension A-35F1 | 35f0 CJK Ideograph Extension A-35F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f3 CJK Ideograph Extension A-35F3 | 35f2 CJK Ideograph Extension A-35F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f5 CJK Ideograph Extension A-35F5 | 35f4 CJK Ideograph Extension A-35F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f7 CJK Ideograph Extension A-35F7 | 35f6 CJK Ideograph Extension A-35F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35f9 CJK Ideograph Extension A-35F9 | 35f8 CJK Ideograph Extension A-35F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35fb CJK Ideograph Extension A-35FB | 35fa CJK Ideograph Extension A-35FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35fd CJK Ideograph Extension A-35FD | 35fc CJK Ideograph Extension A-35FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 35ff CJK Ideograph Extension A-35FF | 35fe CJK Ideograph Extension A-35FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3601 CJK Ideograph Extension A-3601 | 3600 CJK Ideograph Extension A-3600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3603 CJK Ideograph Extension A-3603 | 3602 CJK Ideograph Extension A-3602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3605 CJK Ideograph Extension A-3605 | 3604 CJK Ideograph Extension A-3604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3607 CJK Ideograph Extension A-3607 | 3606 CJK Ideograph Extension A-3606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3609 CJK Ideograph Extension A-3609 | 3608 CJK Ideograph Extension A-3608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360b CJK Ideograph Extension A-360B | 360a CJK Ideograph Extension A-360A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360d CJK Ideograph Extension A-360D | 360c CJK Ideograph Extension A-360C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 360f CJK Ideograph Extension A-360F | 360e CJK Ideograph Extension A-360E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3611 CJK Ideograph Extension A-3611 | 3610 CJK Ideograph Extension A-3610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3613 CJK Ideograph Extension A-3613 | 3612 CJK Ideograph Extension A-3612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3615 CJK Ideograph Extension A-3615 | 3614 CJK Ideograph Extension A-3614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3617 CJK Ideograph Extension A-3617 | 3616 CJK Ideograph Extension A-3616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3619 CJK Ideograph Extension A-3619 | 3618 CJK Ideograph Extension A-3618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361b CJK Ideograph Extension A-361B | 361a CJK Ideograph Extension A-361A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361d CJK Ideograph Extension A-361D | 361c CJK Ideograph Extension A-361C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 361f CJK Ideograph Extension A-361F | 361e CJK Ideograph Extension A-361E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3621 CJK Ideograph Extension A-3621 | 3620 CJK Ideograph Extension A-3620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3623 CJK Ideograph Extension A-3623 | 3622 CJK Ideograph Extension A-3622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3625 CJK Ideograph Extension A-3625 | 3624 CJK Ideograph Extension A-3624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3627 CJK Ideograph Extension A-3627 | 3626 CJK Ideograph Extension A-3626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3629 CJK Ideograph Extension A-3629 | 3628 CJK Ideograph Extension A-3628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362b CJK Ideograph Extension A-362B | 362a CJK Ideograph Extension A-362A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362d CJK Ideograph Extension A-362D | 362c CJK Ideograph Extension A-362C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 362f CJK Ideograph Extension A-362F | 362e CJK Ideograph Extension A-362E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3631 CJK Ideograph Extension A-3631 | 3630 CJK Ideograph Extension A-3630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3633 CJK Ideograph Extension A-3633 | 3632 CJK Ideograph Extension A-3632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3635 CJK Ideograph Extension A-3635 | 3634 CJK Ideograph Extension A-3634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3637 CJK Ideograph Extension A-3637 | 3636 CJK Ideograph Extension A-3636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3639 CJK Ideograph Extension A-3639 | 3638 CJK Ideograph Extension A-3638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363b CJK Ideograph Extension A-363B | 363a CJK Ideograph Extension A-363A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363d CJK Ideograph Extension A-363D | 363c CJK Ideograph Extension A-363C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 363f CJK Ideograph Extension A-363F | 363e CJK Ideograph Extension A-363E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3641 CJK Ideograph Extension A-3641 | 3640 CJK Ideograph Extension A-3640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3643 CJK Ideograph Extension A-3643 | 3642 CJK Ideograph Extension A-3642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3645 CJK Ideograph Extension A-3645 | 3644 CJK Ideograph Extension A-3644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3647 CJK Ideograph Extension A-3647 | 3646 CJK Ideograph Extension A-3646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3649 CJK Ideograph Extension A-3649 | 3648 CJK Ideograph Extension A-3648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364b CJK Ideograph Extension A-364B | 364a CJK Ideograph Extension A-364A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364d CJK Ideograph Extension A-364D | 364c CJK Ideograph Extension A-364C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 364f CJK Ideograph Extension A-364F | 364e CJK Ideograph Extension A-364E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3651 CJK Ideograph Extension A-3651 | 3650 CJK Ideograph Extension A-3650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3653 CJK Ideograph Extension A-3653 | 3652 CJK Ideograph Extension A-3652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3655 CJK Ideograph Extension A-3655 | 3654 CJK Ideograph Extension A-3654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3657 CJK Ideograph Extension A-3657 | 3656 CJK Ideograph Extension A-3656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3659 CJK Ideograph Extension A-3659 | 3658 CJK Ideograph Extension A-3658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365b CJK Ideograph Extension A-365B | 365a CJK Ideograph Extension A-365A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365d CJK Ideograph Extension A-365D | 365c CJK Ideograph Extension A-365C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 365f CJK Ideograph Extension A-365F | 365e CJK Ideograph Extension A-365E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3661 CJK Ideograph Extension A-3661 | 3660 CJK Ideograph Extension A-3660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3663 CJK Ideograph Extension A-3663 | 3662 CJK Ideograph Extension A-3662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3665 CJK Ideograph Extension A-3665 | 3664 CJK Ideograph Extension A-3664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3667 CJK Ideograph Extension A-3667 | 3666 CJK Ideograph Extension A-3666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3669 CJK Ideograph Extension A-3669 | 3668 CJK Ideograph Extension A-3668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366b CJK Ideograph Extension A-366B | 366a CJK Ideograph Extension A-366A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366d CJK Ideograph Extension A-366D | 366c CJK Ideograph Extension A-366C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 366f CJK Ideograph Extension A-366F | 366e CJK Ideograph Extension A-366E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3671 CJK Ideograph Extension A-3671 | 3670 CJK Ideograph Extension A-3670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3673 CJK Ideograph Extension A-3673 | 3672 CJK Ideograph Extension A-3672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3675 CJK Ideograph Extension A-3675 | 3674 CJK Ideograph Extension A-3674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3677 CJK Ideograph Extension A-3677 | 3676 CJK Ideograph Extension A-3676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3679 CJK Ideograph Extension A-3679 | 3678 CJK Ideograph Extension A-3678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367b CJK Ideograph Extension A-367B | 367a CJK Ideograph Extension A-367A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367d CJK Ideograph Extension A-367D | 367c CJK Ideograph Extension A-367C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 367f CJK Ideograph Extension A-367F | 367e CJK Ideograph Extension A-367E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3681 CJK Ideograph Extension A-3681 | 3680 CJK Ideograph Extension A-3680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3683 CJK Ideograph Extension A-3683 | 3682 CJK Ideograph Extension A-3682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3685 CJK Ideograph Extension A-3685 | 3684 CJK Ideograph Extension A-3684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3687 CJK Ideograph Extension A-3687 | 3686 CJK Ideograph Extension A-3686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3689 CJK Ideograph Extension A-3689 | 3688 CJK Ideograph Extension A-3688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368b CJK Ideograph Extension A-368B | 368a CJK Ideograph Extension A-368A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368d CJK Ideograph Extension A-368D | 368c CJK Ideograph Extension A-368C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 368f CJK Ideograph Extension A-368F | 368e CJK Ideograph Extension A-368E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3691 CJK Ideograph Extension A-3691 | 3690 CJK Ideograph Extension A-3690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3693 CJK Ideograph Extension A-3693 | 3692 CJK Ideograph Extension A-3692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3695 CJK Ideograph Extension A-3695 | 3694 CJK Ideograph Extension A-3694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3697 CJK Ideograph Extension A-3697 | 3696 CJK Ideograph Extension A-3696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3699 CJK Ideograph Extension A-3699 | 3698 CJK Ideograph Extension A-3698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369b CJK Ideograph Extension A-369B | 369a CJK Ideograph Extension A-369A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369d CJK Ideograph Extension A-369D | 369c CJK Ideograph Extension A-369C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 369f CJK Ideograph Extension A-369F | 369e CJK Ideograph Extension A-369E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a1 CJK Ideograph Extension A-36A1 | 36a0 CJK Ideograph Extension A-36A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a3 CJK Ideograph Extension A-36A3 | 36a2 CJK Ideograph Extension A-36A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a5 CJK Ideograph Extension A-36A5 | 36a4 CJK Ideograph Extension A-36A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a7 CJK Ideograph Extension A-36A7 | 36a6 CJK Ideograph Extension A-36A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36a9 CJK Ideograph Extension A-36A9 | 36a8 CJK Ideograph Extension A-36A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ab CJK Ideograph Extension A-36AB | 36aa CJK Ideograph Extension A-36AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ad CJK Ideograph Extension A-36AD | 36ac CJK Ideograph Extension A-36AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36af CJK Ideograph Extension A-36AF | 36ae CJK Ideograph Extension A-36AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b1 CJK Ideograph Extension A-36B1 | 36b0 CJK Ideograph Extension A-36B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b3 CJK Ideograph Extension A-36B3 | 36b2 CJK Ideograph Extension A-36B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b5 CJK Ideograph Extension A-36B5 | 36b4 CJK Ideograph Extension A-36B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b7 CJK Ideograph Extension A-36B7 | 36b6 CJK Ideograph Extension A-36B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36b9 CJK Ideograph Extension A-36B9 | 36b8 CJK Ideograph Extension A-36B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bb CJK Ideograph Extension A-36BB | 36ba CJK Ideograph Extension A-36BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bd CJK Ideograph Extension A-36BD | 36bc CJK Ideograph Extension A-36BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36bf CJK Ideograph Extension A-36BF | 36be CJK Ideograph Extension A-36BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c1 CJK Ideograph Extension A-36C1 | 36c0 CJK Ideograph Extension A-36C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c3 CJK Ideograph Extension A-36C3 | 36c2 CJK Ideograph Extension A-36C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c5 CJK Ideograph Extension A-36C5 | 36c4 CJK Ideograph Extension A-36C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c7 CJK Ideograph Extension A-36C7 | 36c6 CJK Ideograph Extension A-36C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36c9 CJK Ideograph Extension A-36C9 | 36c8 CJK Ideograph Extension A-36C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cb CJK Ideograph Extension A-36CB | 36ca CJK Ideograph Extension A-36CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cd CJK Ideograph Extension A-36CD | 36cc CJK Ideograph Extension A-36CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36cf CJK Ideograph Extension A-36CF | 36ce CJK Ideograph Extension A-36CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d1 CJK Ideograph Extension A-36D1 | 36d0 CJK Ideograph Extension A-36D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d3 CJK Ideograph Extension A-36D3 | 36d2 CJK Ideograph Extension A-36D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d5 CJK Ideograph Extension A-36D5 | 36d4 CJK Ideograph Extension A-36D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d7 CJK Ideograph Extension A-36D7 | 36d6 CJK Ideograph Extension A-36D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36d9 CJK Ideograph Extension A-36D9 | 36d8 CJK Ideograph Extension A-36D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36db CJK Ideograph Extension A-36DB | 36da CJK Ideograph Extension A-36DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36dd CJK Ideograph Extension A-36DD | 36dc CJK Ideograph Extension A-36DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36df CJK Ideograph Extension A-36DF | 36de CJK Ideograph Extension A-36DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e1 CJK Ideograph Extension A-36E1 | 36e0 CJK Ideograph Extension A-36E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e3 CJK Ideograph Extension A-36E3 | 36e2 CJK Ideograph Extension A-36E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e5 CJK Ideograph Extension A-36E5 | 36e4 CJK Ideograph Extension A-36E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e7 CJK Ideograph Extension A-36E7 | 36e6 CJK Ideograph Extension A-36E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36e9 CJK Ideograph Extension A-36E9 | 36e8 CJK Ideograph Extension A-36E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36eb CJK Ideograph Extension A-36EB | 36ea CJK Ideograph Extension A-36EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ed CJK Ideograph Extension A-36ED | 36ec CJK Ideograph Extension A-36EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ef CJK Ideograph Extension A-36EF | 36ee CJK Ideograph Extension A-36EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f1 CJK Ideograph Extension A-36F1 | 36f0 CJK Ideograph Extension A-36F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f3 CJK Ideograph Extension A-36F3 | 36f2 CJK Ideograph Extension A-36F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f5 CJK Ideograph Extension A-36F5 | 36f4 CJK Ideograph Extension A-36F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f7 CJK Ideograph Extension A-36F7 | 36f6 CJK Ideograph Extension A-36F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36f9 CJK Ideograph Extension A-36F9 | 36f8 CJK Ideograph Extension A-36F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36fb CJK Ideograph Extension A-36FB | 36fa CJK Ideograph Extension A-36FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36fd CJK Ideograph Extension A-36FD | 36fc CJK Ideograph Extension A-36FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 36ff CJK Ideograph Extension A-36FF | 36fe CJK Ideograph Extension A-36FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3701 CJK Ideograph Extension A-3701 | 3700 CJK Ideograph Extension A-3700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3703 CJK Ideograph Extension A-3703 | 3702 CJK Ideograph Extension A-3702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3705 CJK Ideograph Extension A-3705 | 3704 CJK Ideograph Extension A-3704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3707 CJK Ideograph Extension A-3707 | 3706 CJK Ideograph Extension A-3706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3709 CJK Ideograph Extension A-3709 | 3708 CJK Ideograph Extension A-3708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370b CJK Ideograph Extension A-370B | 370a CJK Ideograph Extension A-370A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370d CJK Ideograph Extension A-370D | 370c CJK Ideograph Extension A-370C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 370f CJK Ideograph Extension A-370F | 370e CJK Ideograph Extension A-370E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3711 CJK Ideograph Extension A-3711 | 3710 CJK Ideograph Extension A-3710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3713 CJK Ideograph Extension A-3713 | 3712 CJK Ideograph Extension A-3712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3715 CJK Ideograph Extension A-3715 | 3714 CJK Ideograph Extension A-3714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3717 CJK Ideograph Extension A-3717 | 3716 CJK Ideograph Extension A-3716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3719 CJK Ideograph Extension A-3719 | 3718 CJK Ideograph Extension A-3718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371b CJK Ideograph Extension A-371B | 371a CJK Ideograph Extension A-371A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371d CJK Ideograph Extension A-371D | 371c CJK Ideograph Extension A-371C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 371f CJK Ideograph Extension A-371F | 371e CJK Ideograph Extension A-371E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3721 CJK Ideograph Extension A-3721 | 3720 CJK Ideograph Extension A-3720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3723 CJK Ideograph Extension A-3723 | 3722 CJK Ideograph Extension A-3722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3725 CJK Ideograph Extension A-3725 | 3724 CJK Ideograph Extension A-3724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3727 CJK Ideograph Extension A-3727 | 3726 CJK Ideograph Extension A-3726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3729 CJK Ideograph Extension A-3729 | 3728 CJK Ideograph Extension A-3728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372b CJK Ideograph Extension A-372B | 372a CJK Ideograph Extension A-372A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372d CJK Ideograph Extension A-372D | 372c CJK Ideograph Extension A-372C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 372f CJK Ideograph Extension A-372F | 372e CJK Ideograph Extension A-372E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3731 CJK Ideograph Extension A-3731 | 3730 CJK Ideograph Extension A-3730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3733 CJK Ideograph Extension A-3733 | 3732 CJK Ideograph Extension A-3732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3735 CJK Ideograph Extension A-3735 | 3734 CJK Ideograph Extension A-3734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3737 CJK Ideograph Extension A-3737 | 3736 CJK Ideograph Extension A-3736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3739 CJK Ideograph Extension A-3739 | 3738 CJK Ideograph Extension A-3738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373b CJK Ideograph Extension A-373B | 373a CJK Ideograph Extension A-373A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373d CJK Ideograph Extension A-373D | 373c CJK Ideograph Extension A-373C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 373f CJK Ideograph Extension A-373F | 373e CJK Ideograph Extension A-373E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3741 CJK Ideograph Extension A-3741 | 3740 CJK Ideograph Extension A-3740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3743 CJK Ideograph Extension A-3743 | 3742 CJK Ideograph Extension A-3742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3745 CJK Ideograph Extension A-3745 | 3744 CJK Ideograph Extension A-3744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3747 CJK Ideograph Extension A-3747 | 3746 CJK Ideograph Extension A-3746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3749 CJK Ideograph Extension A-3749 | 3748 CJK Ideograph Extension A-3748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374b CJK Ideograph Extension A-374B | 374a CJK Ideograph Extension A-374A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374d CJK Ideograph Extension A-374D | 374c CJK Ideograph Extension A-374C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 374f CJK Ideograph Extension A-374F | 374e CJK Ideograph Extension A-374E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3751 CJK Ideograph Extension A-3751 | 3750 CJK Ideograph Extension A-3750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3753 CJK Ideograph Extension A-3753 | 3752 CJK Ideograph Extension A-3752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3755 CJK Ideograph Extension A-3755 | 3754 CJK Ideograph Extension A-3754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3757 CJK Ideograph Extension A-3757 | 3756 CJK Ideograph Extension A-3756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3759 CJK Ideograph Extension A-3759 | 3758 CJK Ideograph Extension A-3758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375b CJK Ideograph Extension A-375B | 375a CJK Ideograph Extension A-375A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375d CJK Ideograph Extension A-375D | 375c CJK Ideograph Extension A-375C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 375f CJK Ideograph Extension A-375F | 375e CJK Ideograph Extension A-375E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3761 CJK Ideograph Extension A-3761 | 3760 CJK Ideograph Extension A-3760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3763 CJK Ideograph Extension A-3763 | 3762 CJK Ideograph Extension A-3762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3765 CJK Ideograph Extension A-3765 | 3764 CJK Ideograph Extension A-3764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3767 CJK Ideograph Extension A-3767 | 3766 CJK Ideograph Extension A-3766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3769 CJK Ideograph Extension A-3769 | 3768 CJK Ideograph Extension A-3768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376b CJK Ideograph Extension A-376B | 376a CJK Ideograph Extension A-376A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376d CJK Ideograph Extension A-376D | 376c CJK Ideograph Extension A-376C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 376f CJK Ideograph Extension A-376F | 376e CJK Ideograph Extension A-376E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3771 CJK Ideograph Extension A-3771 | 3770 CJK Ideograph Extension A-3770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3773 CJK Ideograph Extension A-3773 | 3772 CJK Ideograph Extension A-3772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3775 CJK Ideograph Extension A-3775 | 3774 CJK Ideograph Extension A-3774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3777 CJK Ideograph Extension A-3777 | 3776 CJK Ideograph Extension A-3776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3779 CJK Ideograph Extension A-3779 | 3778 CJK Ideograph Extension A-3778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377b CJK Ideograph Extension A-377B | 377a CJK Ideograph Extension A-377A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377d CJK Ideograph Extension A-377D | 377c CJK Ideograph Extension A-377C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 377f CJK Ideograph Extension A-377F | 377e CJK Ideograph Extension A-377E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3781 CJK Ideograph Extension A-3781 | 3780 CJK Ideograph Extension A-3780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3783 CJK Ideograph Extension A-3783 | 3782 CJK Ideograph Extension A-3782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3785 CJK Ideograph Extension A-3785 | 3784 CJK Ideograph Extension A-3784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3787 CJK Ideograph Extension A-3787 | 3786 CJK Ideograph Extension A-3786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3789 CJK Ideograph Extension A-3789 | 3788 CJK Ideograph Extension A-3788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378b CJK Ideograph Extension A-378B | 378a CJK Ideograph Extension A-378A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378d CJK Ideograph Extension A-378D | 378c CJK Ideograph Extension A-378C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 378f CJK Ideograph Extension A-378F | 378e CJK Ideograph Extension A-378E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3791 CJK Ideograph Extension A-3791 | 3790 CJK Ideograph Extension A-3790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3793 CJK Ideograph Extension A-3793 | 3792 CJK Ideograph Extension A-3792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3795 CJK Ideograph Extension A-3795 | 3794 CJK Ideograph Extension A-3794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3797 CJK Ideograph Extension A-3797 | 3796 CJK Ideograph Extension A-3796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3799 CJK Ideograph Extension A-3799 | 3798 CJK Ideograph Extension A-3798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379b CJK Ideograph Extension A-379B | 379a CJK Ideograph Extension A-379A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379d CJK Ideograph Extension A-379D | 379c CJK Ideograph Extension A-379C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 379f CJK Ideograph Extension A-379F | 379e CJK Ideograph Extension A-379E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a1 CJK Ideograph Extension A-37A1 | 37a0 CJK Ideograph Extension A-37A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a3 CJK Ideograph Extension A-37A3 | 37a2 CJK Ideograph Extension A-37A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a5 CJK Ideograph Extension A-37A5 | 37a4 CJK Ideograph Extension A-37A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a7 CJK Ideograph Extension A-37A7 | 37a6 CJK Ideograph Extension A-37A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37a9 CJK Ideograph Extension A-37A9 | 37a8 CJK Ideograph Extension A-37A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ab CJK Ideograph Extension A-37AB | 37aa CJK Ideograph Extension A-37AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ad CJK Ideograph Extension A-37AD | 37ac CJK Ideograph Extension A-37AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37af CJK Ideograph Extension A-37AF | 37ae CJK Ideograph Extension A-37AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b1 CJK Ideograph Extension A-37B1 | 37b0 CJK Ideograph Extension A-37B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b3 CJK Ideograph Extension A-37B3 | 37b2 CJK Ideograph Extension A-37B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b5 CJK Ideograph Extension A-37B5 | 37b4 CJK Ideograph Extension A-37B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b7 CJK Ideograph Extension A-37B7 | 37b6 CJK Ideograph Extension A-37B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37b9 CJK Ideograph Extension A-37B9 | 37b8 CJK Ideograph Extension A-37B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bb CJK Ideograph Extension A-37BB | 37ba CJK Ideograph Extension A-37BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bd CJK Ideograph Extension A-37BD | 37bc CJK Ideograph Extension A-37BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37bf CJK Ideograph Extension A-37BF | 37be CJK Ideograph Extension A-37BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c1 CJK Ideograph Extension A-37C1 | 37c0 CJK Ideograph Extension A-37C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c3 CJK Ideograph Extension A-37C3 | 37c2 CJK Ideograph Extension A-37C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c5 CJK Ideograph Extension A-37C5 | 37c4 CJK Ideograph Extension A-37C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c7 CJK Ideograph Extension A-37C7 | 37c6 CJK Ideograph Extension A-37C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37c9 CJK Ideograph Extension A-37C9 | 37c8 CJK Ideograph Extension A-37C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cb CJK Ideograph Extension A-37CB | 37ca CJK Ideograph Extension A-37CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cd CJK Ideograph Extension A-37CD | 37cc CJK Ideograph Extension A-37CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37cf CJK Ideograph Extension A-37CF | 37ce CJK Ideograph Extension A-37CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d1 CJK Ideograph Extension A-37D1 | 37d0 CJK Ideograph Extension A-37D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d3 CJK Ideograph Extension A-37D3 | 37d2 CJK Ideograph Extension A-37D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d5 CJK Ideograph Extension A-37D5 | 37d4 CJK Ideograph Extension A-37D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d7 CJK Ideograph Extension A-37D7 | 37d6 CJK Ideograph Extension A-37D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37d9 CJK Ideograph Extension A-37D9 | 37d8 CJK Ideograph Extension A-37D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37db CJK Ideograph Extension A-37DB | 37da CJK Ideograph Extension A-37DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37dd CJK Ideograph Extension A-37DD | 37dc CJK Ideograph Extension A-37DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37df CJK Ideograph Extension A-37DF | 37de CJK Ideograph Extension A-37DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e1 CJK Ideograph Extension A-37E1 | 37e0 CJK Ideograph Extension A-37E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e3 CJK Ideograph Extension A-37E3 | 37e2 CJK Ideograph Extension A-37E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e5 CJK Ideograph Extension A-37E5 | 37e4 CJK Ideograph Extension A-37E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e7 CJK Ideograph Extension A-37E7 | 37e6 CJK Ideograph Extension A-37E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37e9 CJK Ideograph Extension A-37E9 | 37e8 CJK Ideograph Extension A-37E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37eb CJK Ideograph Extension A-37EB | 37ea CJK Ideograph Extension A-37EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ed CJK Ideograph Extension A-37ED | 37ec CJK Ideograph Extension A-37EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ef CJK Ideograph Extension A-37EF | 37ee CJK Ideograph Extension A-37EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f1 CJK Ideograph Extension A-37F1 | 37f0 CJK Ideograph Extension A-37F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f3 CJK Ideograph Extension A-37F3 | 37f2 CJK Ideograph Extension A-37F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f5 CJK Ideograph Extension A-37F5 | 37f4 CJK Ideograph Extension A-37F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f7 CJK Ideograph Extension A-37F7 | 37f6 CJK Ideograph Extension A-37F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37f9 CJK Ideograph Extension A-37F9 | 37f8 CJK Ideograph Extension A-37F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37fb CJK Ideograph Extension A-37FB | 37fa CJK Ideograph Extension A-37FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37fd CJK Ideograph Extension A-37FD | 37fc CJK Ideograph Extension A-37FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 37ff CJK Ideograph Extension A-37FF | 37fe CJK Ideograph Extension A-37FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3801 CJK Ideograph Extension A-3801 | 3800 CJK Ideograph Extension A-3800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3803 CJK Ideograph Extension A-3803 | 3802 CJK Ideograph Extension A-3802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3805 CJK Ideograph Extension A-3805 | 3804 CJK Ideograph Extension A-3804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3807 CJK Ideograph Extension A-3807 | 3806 CJK Ideograph Extension A-3806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3809 CJK Ideograph Extension A-3809 | 3808 CJK Ideograph Extension A-3808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380b CJK Ideograph Extension A-380B | 380a CJK Ideograph Extension A-380A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380d CJK Ideograph Extension A-380D | 380c CJK Ideograph Extension A-380C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 380f CJK Ideograph Extension A-380F | 380e CJK Ideograph Extension A-380E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3811 CJK Ideograph Extension A-3811 | 3810 CJK Ideograph Extension A-3810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3813 CJK Ideograph Extension A-3813 | 3812 CJK Ideograph Extension A-3812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3815 CJK Ideograph Extension A-3815 | 3814 CJK Ideograph Extension A-3814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3817 CJK Ideograph Extension A-3817 | 3816 CJK Ideograph Extension A-3816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3819 CJK Ideograph Extension A-3819 | 3818 CJK Ideograph Extension A-3818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381b CJK Ideograph Extension A-381B | 381a CJK Ideograph Extension A-381A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381d CJK Ideograph Extension A-381D | 381c CJK Ideograph Extension A-381C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 381f CJK Ideograph Extension A-381F | 381e CJK Ideograph Extension A-381E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3821 CJK Ideograph Extension A-3821 | 3820 CJK Ideograph Extension A-3820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3823 CJK Ideograph Extension A-3823 | 3822 CJK Ideograph Extension A-3822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3825 CJK Ideograph Extension A-3825 | 3824 CJK Ideograph Extension A-3824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3827 CJK Ideograph Extension A-3827 | 3826 CJK Ideograph Extension A-3826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3829 CJK Ideograph Extension A-3829 | 3828 CJK Ideograph Extension A-3828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382b CJK Ideograph Extension A-382B | 382a CJK Ideograph Extension A-382A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382d CJK Ideograph Extension A-382D | 382c CJK Ideograph Extension A-382C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 382f CJK Ideograph Extension A-382F | 382e CJK Ideograph Extension A-382E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3831 CJK Ideograph Extension A-3831 | 3830 CJK Ideograph Extension A-3830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3833 CJK Ideograph Extension A-3833 | 3832 CJK Ideograph Extension A-3832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3835 CJK Ideograph Extension A-3835 | 3834 CJK Ideograph Extension A-3834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3837 CJK Ideograph Extension A-3837 | 3836 CJK Ideograph Extension A-3836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3839 CJK Ideograph Extension A-3839 | 3838 CJK Ideograph Extension A-3838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383b CJK Ideograph Extension A-383B | 383a CJK Ideograph Extension A-383A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383d CJK Ideograph Extension A-383D | 383c CJK Ideograph Extension A-383C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 383f CJK Ideograph Extension A-383F | 383e CJK Ideograph Extension A-383E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3841 CJK Ideograph Extension A-3841 | 3840 CJK Ideograph Extension A-3840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3843 CJK Ideograph Extension A-3843 | 3842 CJK Ideograph Extension A-3842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3845 CJK Ideograph Extension A-3845 | 3844 CJK Ideograph Extension A-3844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3847 CJK Ideograph Extension A-3847 | 3846 CJK Ideograph Extension A-3846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3849 CJK Ideograph Extension A-3849 | 3848 CJK Ideograph Extension A-3848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384b CJK Ideograph Extension A-384B | 384a CJK Ideograph Extension A-384A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384d CJK Ideograph Extension A-384D | 384c CJK Ideograph Extension A-384C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 384f CJK Ideograph Extension A-384F | 384e CJK Ideograph Extension A-384E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3851 CJK Ideograph Extension A-3851 | 3850 CJK Ideograph Extension A-3850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3853 CJK Ideograph Extension A-3853 | 3852 CJK Ideograph Extension A-3852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3855 CJK Ideograph Extension A-3855 | 3854 CJK Ideograph Extension A-3854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3857 CJK Ideograph Extension A-3857 | 3856 CJK Ideograph Extension A-3856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3859 CJK Ideograph Extension A-3859 | 3858 CJK Ideograph Extension A-3858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385b CJK Ideograph Extension A-385B | 385a CJK Ideograph Extension A-385A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385d CJK Ideograph Extension A-385D | 385c CJK Ideograph Extension A-385C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 385f CJK Ideograph Extension A-385F | 385e CJK Ideograph Extension A-385E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3861 CJK Ideograph Extension A-3861 | 3860 CJK Ideograph Extension A-3860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3863 CJK Ideograph Extension A-3863 | 3862 CJK Ideograph Extension A-3862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3865 CJK Ideograph Extension A-3865 | 3864 CJK Ideograph Extension A-3864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3867 CJK Ideograph Extension A-3867 | 3866 CJK Ideograph Extension A-3866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3869 CJK Ideograph Extension A-3869 | 3868 CJK Ideograph Extension A-3868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386b CJK Ideograph Extension A-386B | 386a CJK Ideograph Extension A-386A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386d CJK Ideograph Extension A-386D | 386c CJK Ideograph Extension A-386C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 386f CJK Ideograph Extension A-386F | 386e CJK Ideograph Extension A-386E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3871 CJK Ideograph Extension A-3871 | 3870 CJK Ideograph Extension A-3870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3873 CJK Ideograph Extension A-3873 | 3872 CJK Ideograph Extension A-3872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3875 CJK Ideograph Extension A-3875 | 3874 CJK Ideograph Extension A-3874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3877 CJK Ideograph Extension A-3877 | 3876 CJK Ideograph Extension A-3876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3879 CJK Ideograph Extension A-3879 | 3878 CJK Ideograph Extension A-3878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387b CJK Ideograph Extension A-387B | 387a CJK Ideograph Extension A-387A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387d CJK Ideograph Extension A-387D | 387c CJK Ideograph Extension A-387C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 387f CJK Ideograph Extension A-387F | 387e CJK Ideograph Extension A-387E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3881 CJK Ideograph Extension A-3881 | 3880 CJK Ideograph Extension A-3880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3883 CJK Ideograph Extension A-3883 | 3882 CJK Ideograph Extension A-3882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3885 CJK Ideograph Extension A-3885 | 3884 CJK Ideograph Extension A-3884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3887 CJK Ideograph Extension A-3887 | 3886 CJK Ideograph Extension A-3886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3889 CJK Ideograph Extension A-3889 | 3888 CJK Ideograph Extension A-3888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388b CJK Ideograph Extension A-388B | 388a CJK Ideograph Extension A-388A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388d CJK Ideograph Extension A-388D | 388c CJK Ideograph Extension A-388C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 388f CJK Ideograph Extension A-388F | 388e CJK Ideograph Extension A-388E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3891 CJK Ideograph Extension A-3891 | 3890 CJK Ideograph Extension A-3890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3893 CJK Ideograph Extension A-3893 | 3892 CJK Ideograph Extension A-3892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3895 CJK Ideograph Extension A-3895 | 3894 CJK Ideograph Extension A-3894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3897 CJK Ideograph Extension A-3897 | 3896 CJK Ideograph Extension A-3896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3899 CJK Ideograph Extension A-3899 | 3898 CJK Ideograph Extension A-3898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389b CJK Ideograph Extension A-389B | 389a CJK Ideograph Extension A-389A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389d CJK Ideograph Extension A-389D | 389c CJK Ideograph Extension A-389C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 389f CJK Ideograph Extension A-389F | 389e CJK Ideograph Extension A-389E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a1 CJK Ideograph Extension A-38A1 | 38a0 CJK Ideograph Extension A-38A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a3 CJK Ideograph Extension A-38A3 | 38a2 CJK Ideograph Extension A-38A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a5 CJK Ideograph Extension A-38A5 | 38a4 CJK Ideograph Extension A-38A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a7 CJK Ideograph Extension A-38A7 | 38a6 CJK Ideograph Extension A-38A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38a9 CJK Ideograph Extension A-38A9 | 38a8 CJK Ideograph Extension A-38A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ab CJK Ideograph Extension A-38AB | 38aa CJK Ideograph Extension A-38AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ad CJK Ideograph Extension A-38AD | 38ac CJK Ideograph Extension A-38AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38af CJK Ideograph Extension A-38AF | 38ae CJK Ideograph Extension A-38AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b1 CJK Ideograph Extension A-38B1 | 38b0 CJK Ideograph Extension A-38B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b3 CJK Ideograph Extension A-38B3 | 38b2 CJK Ideograph Extension A-38B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b5 CJK Ideograph Extension A-38B5 | 38b4 CJK Ideograph Extension A-38B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b7 CJK Ideograph Extension A-38B7 | 38b6 CJK Ideograph Extension A-38B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38b9 CJK Ideograph Extension A-38B9 | 38b8 CJK Ideograph Extension A-38B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bb CJK Ideograph Extension A-38BB | 38ba CJK Ideograph Extension A-38BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bd CJK Ideograph Extension A-38BD | 38bc CJK Ideograph Extension A-38BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38bf CJK Ideograph Extension A-38BF | 38be CJK Ideograph Extension A-38BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c1 CJK Ideograph Extension A-38C1 | 38c0 CJK Ideograph Extension A-38C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c3 CJK Ideograph Extension A-38C3 | 38c2 CJK Ideograph Extension A-38C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c5 CJK Ideograph Extension A-38C5 | 38c4 CJK Ideograph Extension A-38C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c7 CJK Ideograph Extension A-38C7 | 38c6 CJK Ideograph Extension A-38C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38c9 CJK Ideograph Extension A-38C9 | 38c8 CJK Ideograph Extension A-38C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cb CJK Ideograph Extension A-38CB | 38ca CJK Ideograph Extension A-38CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cd CJK Ideograph Extension A-38CD | 38cc CJK Ideograph Extension A-38CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38cf CJK Ideograph Extension A-38CF | 38ce CJK Ideograph Extension A-38CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d1 CJK Ideograph Extension A-38D1 | 38d0 CJK Ideograph Extension A-38D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d3 CJK Ideograph Extension A-38D3 | 38d2 CJK Ideograph Extension A-38D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d5 CJK Ideograph Extension A-38D5 | 38d4 CJK Ideograph Extension A-38D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d7 CJK Ideograph Extension A-38D7 | 38d6 CJK Ideograph Extension A-38D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38d9 CJK Ideograph Extension A-38D9 | 38d8 CJK Ideograph Extension A-38D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38db CJK Ideograph Extension A-38DB | 38da CJK Ideograph Extension A-38DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38dd CJK Ideograph Extension A-38DD | 38dc CJK Ideograph Extension A-38DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38df CJK Ideograph Extension A-38DF | 38de CJK Ideograph Extension A-38DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e1 CJK Ideograph Extension A-38E1 | 38e0 CJK Ideograph Extension A-38E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e3 CJK Ideograph Extension A-38E3 | 38e2 CJK Ideograph Extension A-38E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e5 CJK Ideograph Extension A-38E5 | 38e4 CJK Ideograph Extension A-38E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e7 CJK Ideograph Extension A-38E7 | 38e6 CJK Ideograph Extension A-38E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38e9 CJK Ideograph Extension A-38E9 | 38e8 CJK Ideograph Extension A-38E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38eb CJK Ideograph Extension A-38EB | 38ea CJK Ideograph Extension A-38EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ed CJK Ideograph Extension A-38ED | 38ec CJK Ideograph Extension A-38EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ef CJK Ideograph Extension A-38EF | 38ee CJK Ideograph Extension A-38EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f1 CJK Ideograph Extension A-38F1 | 38f0 CJK Ideograph Extension A-38F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f3 CJK Ideograph Extension A-38F3 | 38f2 CJK Ideograph Extension A-38F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f5 CJK Ideograph Extension A-38F5 | 38f4 CJK Ideograph Extension A-38F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f7 CJK Ideograph Extension A-38F7 | 38f6 CJK Ideograph Extension A-38F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38f9 CJK Ideograph Extension A-38F9 | 38f8 CJK Ideograph Extension A-38F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38fb CJK Ideograph Extension A-38FB | 38fa CJK Ideograph Extension A-38FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38fd CJK Ideograph Extension A-38FD | 38fc CJK Ideograph Extension A-38FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 38ff CJK Ideograph Extension A-38FF | 38fe CJK Ideograph Extension A-38FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3901 CJK Ideograph Extension A-3901 | 3900 CJK Ideograph Extension A-3900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3903 CJK Ideograph Extension A-3903 | 3902 CJK Ideograph Extension A-3902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3905 CJK Ideograph Extension A-3905 | 3904 CJK Ideograph Extension A-3904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3907 CJK Ideograph Extension A-3907 | 3906 CJK Ideograph Extension A-3906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3909 CJK Ideograph Extension A-3909 | 3908 CJK Ideograph Extension A-3908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390b CJK Ideograph Extension A-390B | 390a CJK Ideograph Extension A-390A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390d CJK Ideograph Extension A-390D | 390c CJK Ideograph Extension A-390C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 390f CJK Ideograph Extension A-390F | 390e CJK Ideograph Extension A-390E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3911 CJK Ideograph Extension A-3911 | 3910 CJK Ideograph Extension A-3910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3913 CJK Ideograph Extension A-3913 | 3912 CJK Ideograph Extension A-3912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3915 CJK Ideograph Extension A-3915 | 3914 CJK Ideograph Extension A-3914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3917 CJK Ideograph Extension A-3917 | 3916 CJK Ideograph Extension A-3916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3919 CJK Ideograph Extension A-3919 | 3918 CJK Ideograph Extension A-3918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391b CJK Ideograph Extension A-391B | 391a CJK Ideograph Extension A-391A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391d CJK Ideograph Extension A-391D | 391c CJK Ideograph Extension A-391C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 391f CJK Ideograph Extension A-391F | 391e CJK Ideograph Extension A-391E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3921 CJK Ideograph Extension A-3921 | 3920 CJK Ideograph Extension A-3920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3923 CJK Ideograph Extension A-3923 | 3922 CJK Ideograph Extension A-3922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3925 CJK Ideograph Extension A-3925 | 3924 CJK Ideograph Extension A-3924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3927 CJK Ideograph Extension A-3927 | 3926 CJK Ideograph Extension A-3926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3929 CJK Ideograph Extension A-3929 | 3928 CJK Ideograph Extension A-3928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392b CJK Ideograph Extension A-392B | 392a CJK Ideograph Extension A-392A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392d CJK Ideograph Extension A-392D | 392c CJK Ideograph Extension A-392C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 392f CJK Ideograph Extension A-392F | 392e CJK Ideograph Extension A-392E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3931 CJK Ideograph Extension A-3931 | 3930 CJK Ideograph Extension A-3930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3933 CJK Ideograph Extension A-3933 | 3932 CJK Ideograph Extension A-3932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3935 CJK Ideograph Extension A-3935 | 3934 CJK Ideograph Extension A-3934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3937 CJK Ideograph Extension A-3937 | 3936 CJK Ideograph Extension A-3936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3939 CJK Ideograph Extension A-3939 | 3938 CJK Ideograph Extension A-3938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393b CJK Ideograph Extension A-393B | 393a CJK Ideograph Extension A-393A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393d CJK Ideograph Extension A-393D | 393c CJK Ideograph Extension A-393C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 393f CJK Ideograph Extension A-393F | 393e CJK Ideograph Extension A-393E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3941 CJK Ideograph Extension A-3941 | 3940 CJK Ideograph Extension A-3940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3943 CJK Ideograph Extension A-3943 | 3942 CJK Ideograph Extension A-3942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3945 CJK Ideograph Extension A-3945 | 3944 CJK Ideograph Extension A-3944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3947 CJK Ideograph Extension A-3947 | 3946 CJK Ideograph Extension A-3946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3949 CJK Ideograph Extension A-3949 | 3948 CJK Ideograph Extension A-3948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394b CJK Ideograph Extension A-394B | 394a CJK Ideograph Extension A-394A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394d CJK Ideograph Extension A-394D | 394c CJK Ideograph Extension A-394C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 394f CJK Ideograph Extension A-394F | 394e CJK Ideograph Extension A-394E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3951 CJK Ideograph Extension A-3951 | 3950 CJK Ideograph Extension A-3950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3953 CJK Ideograph Extension A-3953 | 3952 CJK Ideograph Extension A-3952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3955 CJK Ideograph Extension A-3955 | 3954 CJK Ideograph Extension A-3954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3957 CJK Ideograph Extension A-3957 | 3956 CJK Ideograph Extension A-3956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3959 CJK Ideograph Extension A-3959 | 3958 CJK Ideograph Extension A-3958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395b CJK Ideograph Extension A-395B | 395a CJK Ideograph Extension A-395A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395d CJK Ideograph Extension A-395D | 395c CJK Ideograph Extension A-395C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 395f CJK Ideograph Extension A-395F | 395e CJK Ideograph Extension A-395E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3961 CJK Ideograph Extension A-3961 | 3960 CJK Ideograph Extension A-3960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3963 CJK Ideograph Extension A-3963 | 3962 CJK Ideograph Extension A-3962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3965 CJK Ideograph Extension A-3965 | 3964 CJK Ideograph Extension A-3964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3967 CJK Ideograph Extension A-3967 | 3966 CJK Ideograph Extension A-3966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3969 CJK Ideograph Extension A-3969 | 3968 CJK Ideograph Extension A-3968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396b CJK Ideograph Extension A-396B | 396a CJK Ideograph Extension A-396A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396d CJK Ideograph Extension A-396D | 396c CJK Ideograph Extension A-396C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 396f CJK Ideograph Extension A-396F | 396e CJK Ideograph Extension A-396E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3971 CJK Ideograph Extension A-3971 | 3970 CJK Ideograph Extension A-3970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3973 CJK Ideograph Extension A-3973 | 3972 CJK Ideograph Extension A-3972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3975 CJK Ideograph Extension A-3975 | 3974 CJK Ideograph Extension A-3974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3977 CJK Ideograph Extension A-3977 | 3976 CJK Ideograph Extension A-3976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3979 CJK Ideograph Extension A-3979 | 3978 CJK Ideograph Extension A-3978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397b CJK Ideograph Extension A-397B | 397a CJK Ideograph Extension A-397A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397d CJK Ideograph Extension A-397D | 397c CJK Ideograph Extension A-397C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 397f CJK Ideograph Extension A-397F | 397e CJK Ideograph Extension A-397E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3981 CJK Ideograph Extension A-3981 | 3980 CJK Ideograph Extension A-3980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3983 CJK Ideograph Extension A-3983 | 3982 CJK Ideograph Extension A-3982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3985 CJK Ideograph Extension A-3985 | 3984 CJK Ideograph Extension A-3984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3987 CJK Ideograph Extension A-3987 | 3986 CJK Ideograph Extension A-3986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3989 CJK Ideograph Extension A-3989 | 3988 CJK Ideograph Extension A-3988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398b CJK Ideograph Extension A-398B | 398a CJK Ideograph Extension A-398A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398d CJK Ideograph Extension A-398D | 398c CJK Ideograph Extension A-398C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 398f CJK Ideograph Extension A-398F | 398e CJK Ideograph Extension A-398E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3991 CJK Ideograph Extension A-3991 | 3990 CJK Ideograph Extension A-3990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3993 CJK Ideograph Extension A-3993 | 3992 CJK Ideograph Extension A-3992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3995 CJK Ideograph Extension A-3995 | 3994 CJK Ideograph Extension A-3994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3997 CJK Ideograph Extension A-3997 | 3996 CJK Ideograph Extension A-3996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3999 CJK Ideograph Extension A-3999 | 3998 CJK Ideograph Extension A-3998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399b CJK Ideograph Extension A-399B | 399a CJK Ideograph Extension A-399A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399d CJK Ideograph Extension A-399D | 399c CJK Ideograph Extension A-399C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 399f CJK Ideograph Extension A-399F | 399e CJK Ideograph Extension A-399E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a1 CJK Ideograph Extension A-39A1 | 39a0 CJK Ideograph Extension A-39A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a3 CJK Ideograph Extension A-39A3 | 39a2 CJK Ideograph Extension A-39A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a5 CJK Ideograph Extension A-39A5 | 39a4 CJK Ideograph Extension A-39A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a7 CJK Ideograph Extension A-39A7 | 39a6 CJK Ideograph Extension A-39A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39a9 CJK Ideograph Extension A-39A9 | 39a8 CJK Ideograph Extension A-39A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ab CJK Ideograph Extension A-39AB | 39aa CJK Ideograph Extension A-39AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ad CJK Ideograph Extension A-39AD | 39ac CJK Ideograph Extension A-39AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39af CJK Ideograph Extension A-39AF | 39ae CJK Ideograph Extension A-39AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b1 CJK Ideograph Extension A-39B1 | 39b0 CJK Ideograph Extension A-39B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b3 CJK Ideograph Extension A-39B3 | 39b2 CJK Ideograph Extension A-39B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b5 CJK Ideograph Extension A-39B5 | 39b4 CJK Ideograph Extension A-39B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b7 CJK Ideograph Extension A-39B7 | 39b6 CJK Ideograph Extension A-39B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39b9 CJK Ideograph Extension A-39B9 | 39b8 CJK Ideograph Extension A-39B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bb CJK Ideograph Extension A-39BB | 39ba CJK Ideograph Extension A-39BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bd CJK Ideograph Extension A-39BD | 39bc CJK Ideograph Extension A-39BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39bf CJK Ideograph Extension A-39BF | 39be CJK Ideograph Extension A-39BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c1 CJK Ideograph Extension A-39C1 | 39c0 CJK Ideograph Extension A-39C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c3 CJK Ideograph Extension A-39C3 | 39c2 CJK Ideograph Extension A-39C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c5 CJK Ideograph Extension A-39C5 | 39c4 CJK Ideograph Extension A-39C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c7 CJK Ideograph Extension A-39C7 | 39c6 CJK Ideograph Extension A-39C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39c9 CJK Ideograph Extension A-39C9 | 39c8 CJK Ideograph Extension A-39C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cb CJK Ideograph Extension A-39CB | 39ca CJK Ideograph Extension A-39CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cd CJK Ideograph Extension A-39CD | 39cc CJK Ideograph Extension A-39CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39cf CJK Ideograph Extension A-39CF | 39ce CJK Ideograph Extension A-39CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d1 CJK Ideograph Extension A-39D1 | 39d0 CJK Ideograph Extension A-39D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d3 CJK Ideograph Extension A-39D3 | 39d2 CJK Ideograph Extension A-39D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d5 CJK Ideograph Extension A-39D5 | 39d4 CJK Ideograph Extension A-39D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d7 CJK Ideograph Extension A-39D7 | 39d6 CJK Ideograph Extension A-39D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39d9 CJK Ideograph Extension A-39D9 | 39d8 CJK Ideograph Extension A-39D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39db CJK Ideograph Extension A-39DB | 39da CJK Ideograph Extension A-39DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39dd CJK Ideograph Extension A-39DD | 39dc CJK Ideograph Extension A-39DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39df CJK Ideograph Extension A-39DF | 39de CJK Ideograph Extension A-39DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e1 CJK Ideograph Extension A-39E1 | 39e0 CJK Ideograph Extension A-39E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e3 CJK Ideograph Extension A-39E3 | 39e2 CJK Ideograph Extension A-39E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e5 CJK Ideograph Extension A-39E5 | 39e4 CJK Ideograph Extension A-39E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e7 CJK Ideograph Extension A-39E7 | 39e6 CJK Ideograph Extension A-39E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39e9 CJK Ideograph Extension A-39E9 | 39e8 CJK Ideograph Extension A-39E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39eb CJK Ideograph Extension A-39EB | 39ea CJK Ideograph Extension A-39EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ed CJK Ideograph Extension A-39ED | 39ec CJK Ideograph Extension A-39EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ef CJK Ideograph Extension A-39EF | 39ee CJK Ideograph Extension A-39EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f1 CJK Ideograph Extension A-39F1 | 39f0 CJK Ideograph Extension A-39F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f3 CJK Ideograph Extension A-39F3 | 39f2 CJK Ideograph Extension A-39F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f5 CJK Ideograph Extension A-39F5 | 39f4 CJK Ideograph Extension A-39F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f7 CJK Ideograph Extension A-39F7 | 39f6 CJK Ideograph Extension A-39F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39f9 CJK Ideograph Extension A-39F9 | 39f8 CJK Ideograph Extension A-39F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39fb CJK Ideograph Extension A-39FB | 39fa CJK Ideograph Extension A-39FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39fd CJK Ideograph Extension A-39FD | 39fc CJK Ideograph Extension A-39FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 39ff CJK Ideograph Extension A-39FF | 39fe CJK Ideograph Extension A-39FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a01 CJK Ideograph Extension A-3A01 | 3a00 CJK Ideograph Extension A-3A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a03 CJK Ideograph Extension A-3A03 | 3a02 CJK Ideograph Extension A-3A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a05 CJK Ideograph Extension A-3A05 | 3a04 CJK Ideograph Extension A-3A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a07 CJK Ideograph Extension A-3A07 | 3a06 CJK Ideograph Extension A-3A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a09 CJK Ideograph Extension A-3A09 | 3a08 CJK Ideograph Extension A-3A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0b CJK Ideograph Extension A-3A0B | 3a0a CJK Ideograph Extension A-3A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0d CJK Ideograph Extension A-3A0D | 3a0c CJK Ideograph Extension A-3A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a0f CJK Ideograph Extension A-3A0F | 3a0e CJK Ideograph Extension A-3A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a11 CJK Ideograph Extension A-3A11 | 3a10 CJK Ideograph Extension A-3A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a13 CJK Ideograph Extension A-3A13 | 3a12 CJK Ideograph Extension A-3A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a15 CJK Ideograph Extension A-3A15 | 3a14 CJK Ideograph Extension A-3A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a17 CJK Ideograph Extension A-3A17 | 3a16 CJK Ideograph Extension A-3A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a19 CJK Ideograph Extension A-3A19 | 3a18 CJK Ideograph Extension A-3A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1b CJK Ideograph Extension A-3A1B | 3a1a CJK Ideograph Extension A-3A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1d CJK Ideograph Extension A-3A1D | 3a1c CJK Ideograph Extension A-3A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a1f CJK Ideograph Extension A-3A1F | 3a1e CJK Ideograph Extension A-3A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a21 CJK Ideograph Extension A-3A21 | 3a20 CJK Ideograph Extension A-3A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a23 CJK Ideograph Extension A-3A23 | 3a22 CJK Ideograph Extension A-3A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a25 CJK Ideograph Extension A-3A25 | 3a24 CJK Ideograph Extension A-3A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a27 CJK Ideograph Extension A-3A27 | 3a26 CJK Ideograph Extension A-3A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a29 CJK Ideograph Extension A-3A29 | 3a28 CJK Ideograph Extension A-3A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2b CJK Ideograph Extension A-3A2B | 3a2a CJK Ideograph Extension A-3A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2d CJK Ideograph Extension A-3A2D | 3a2c CJK Ideograph Extension A-3A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a2f CJK Ideograph Extension A-3A2F | 3a2e CJK Ideograph Extension A-3A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a31 CJK Ideograph Extension A-3A31 | 3a30 CJK Ideograph Extension A-3A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a33 CJK Ideograph Extension A-3A33 | 3a32 CJK Ideograph Extension A-3A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a35 CJK Ideograph Extension A-3A35 | 3a34 CJK Ideograph Extension A-3A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a37 CJK Ideograph Extension A-3A37 | 3a36 CJK Ideograph Extension A-3A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a39 CJK Ideograph Extension A-3A39 | 3a38 CJK Ideograph Extension A-3A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3b CJK Ideograph Extension A-3A3B | 3a3a CJK Ideograph Extension A-3A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3d CJK Ideograph Extension A-3A3D | 3a3c CJK Ideograph Extension A-3A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a3f CJK Ideograph Extension A-3A3F | 3a3e CJK Ideograph Extension A-3A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a41 CJK Ideograph Extension A-3A41 | 3a40 CJK Ideograph Extension A-3A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a43 CJK Ideograph Extension A-3A43 | 3a42 CJK Ideograph Extension A-3A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a45 CJK Ideograph Extension A-3A45 | 3a44 CJK Ideograph Extension A-3A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a47 CJK Ideograph Extension A-3A47 | 3a46 CJK Ideograph Extension A-3A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a49 CJK Ideograph Extension A-3A49 | 3a48 CJK Ideograph Extension A-3A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4b CJK Ideograph Extension A-3A4B | 3a4a CJK Ideograph Extension A-3A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4d CJK Ideograph Extension A-3A4D | 3a4c CJK Ideograph Extension A-3A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a4f CJK Ideograph Extension A-3A4F | 3a4e CJK Ideograph Extension A-3A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a51 CJK Ideograph Extension A-3A51 | 3a50 CJK Ideograph Extension A-3A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a53 CJK Ideograph Extension A-3A53 | 3a52 CJK Ideograph Extension A-3A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a55 CJK Ideograph Extension A-3A55 | 3a54 CJK Ideograph Extension A-3A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a57 CJK Ideograph Extension A-3A57 | 3a56 CJK Ideograph Extension A-3A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a59 CJK Ideograph Extension A-3A59 | 3a58 CJK Ideograph Extension A-3A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5b CJK Ideograph Extension A-3A5B | 3a5a CJK Ideograph Extension A-3A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5d CJK Ideograph Extension A-3A5D | 3a5c CJK Ideograph Extension A-3A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a5f CJK Ideograph Extension A-3A5F | 3a5e CJK Ideograph Extension A-3A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a61 CJK Ideograph Extension A-3A61 | 3a60 CJK Ideograph Extension A-3A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a63 CJK Ideograph Extension A-3A63 | 3a62 CJK Ideograph Extension A-3A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a65 CJK Ideograph Extension A-3A65 | 3a64 CJK Ideograph Extension A-3A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a67 CJK Ideograph Extension A-3A67 | 3a66 CJK Ideograph Extension A-3A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a69 CJK Ideograph Extension A-3A69 | 3a68 CJK Ideograph Extension A-3A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6b CJK Ideograph Extension A-3A6B | 3a6a CJK Ideograph Extension A-3A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6d CJK Ideograph Extension A-3A6D | 3a6c CJK Ideograph Extension A-3A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a6f CJK Ideograph Extension A-3A6F | 3a6e CJK Ideograph Extension A-3A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a71 CJK Ideograph Extension A-3A71 | 3a70 CJK Ideograph Extension A-3A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a73 CJK Ideograph Extension A-3A73 | 3a72 CJK Ideograph Extension A-3A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a75 CJK Ideograph Extension A-3A75 | 3a74 CJK Ideograph Extension A-3A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a77 CJK Ideograph Extension A-3A77 | 3a76 CJK Ideograph Extension A-3A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a79 CJK Ideograph Extension A-3A79 | 3a78 CJK Ideograph Extension A-3A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7b CJK Ideograph Extension A-3A7B | 3a7a CJK Ideograph Extension A-3A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7d CJK Ideograph Extension A-3A7D | 3a7c CJK Ideograph Extension A-3A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a7f CJK Ideograph Extension A-3A7F | 3a7e CJK Ideograph Extension A-3A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a81 CJK Ideograph Extension A-3A81 | 3a80 CJK Ideograph Extension A-3A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a83 CJK Ideograph Extension A-3A83 | 3a82 CJK Ideograph Extension A-3A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a85 CJK Ideograph Extension A-3A85 | 3a84 CJK Ideograph Extension A-3A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a87 CJK Ideograph Extension A-3A87 | 3a86 CJK Ideograph Extension A-3A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a89 CJK Ideograph Extension A-3A89 | 3a88 CJK Ideograph Extension A-3A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8b CJK Ideograph Extension A-3A8B | 3a8a CJK Ideograph Extension A-3A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8d CJK Ideograph Extension A-3A8D | 3a8c CJK Ideograph Extension A-3A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a8f CJK Ideograph Extension A-3A8F | 3a8e CJK Ideograph Extension A-3A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a91 CJK Ideograph Extension A-3A91 | 3a90 CJK Ideograph Extension A-3A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a93 CJK Ideograph Extension A-3A93 | 3a92 CJK Ideograph Extension A-3A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a95 CJK Ideograph Extension A-3A95 | 3a94 CJK Ideograph Extension A-3A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a97 CJK Ideograph Extension A-3A97 | 3a96 CJK Ideograph Extension A-3A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a99 CJK Ideograph Extension A-3A99 | 3a98 CJK Ideograph Extension A-3A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9b CJK Ideograph Extension A-3A9B | 3a9a CJK Ideograph Extension A-3A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9d CJK Ideograph Extension A-3A9D | 3a9c CJK Ideograph Extension A-3A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3a9f CJK Ideograph Extension A-3A9F | 3a9e CJK Ideograph Extension A-3A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa1 CJK Ideograph Extension A-3AA1 | 3aa0 CJK Ideograph Extension A-3AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa3 CJK Ideograph Extension A-3AA3 | 3aa2 CJK Ideograph Extension A-3AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa5 CJK Ideograph Extension A-3AA5 | 3aa4 CJK Ideograph Extension A-3AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa7 CJK Ideograph Extension A-3AA7 | 3aa6 CJK Ideograph Extension A-3AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aa9 CJK Ideograph Extension A-3AA9 | 3aa8 CJK Ideograph Extension A-3AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aab CJK Ideograph Extension A-3AAB | 3aaa CJK Ideograph Extension A-3AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aad CJK Ideograph Extension A-3AAD | 3aac CJK Ideograph Extension A-3AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aaf CJK Ideograph Extension A-3AAF | 3aae CJK Ideograph Extension A-3AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab1 CJK Ideograph Extension A-3AB1 | 3ab0 CJK Ideograph Extension A-3AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab3 CJK Ideograph Extension A-3AB3 | 3ab2 CJK Ideograph Extension A-3AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab5 CJK Ideograph Extension A-3AB5 | 3ab4 CJK Ideograph Extension A-3AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab7 CJK Ideograph Extension A-3AB7 | 3ab6 CJK Ideograph Extension A-3AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ab9 CJK Ideograph Extension A-3AB9 | 3ab8 CJK Ideograph Extension A-3AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abb CJK Ideograph Extension A-3ABB | 3aba CJK Ideograph Extension A-3ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abd CJK Ideograph Extension A-3ABD | 3abc CJK Ideograph Extension A-3ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3abf CJK Ideograph Extension A-3ABF | 3abe CJK Ideograph Extension A-3ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac1 CJK Ideograph Extension A-3AC1 | 3ac0 CJK Ideograph Extension A-3AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac3 CJK Ideograph Extension A-3AC3 | 3ac2 CJK Ideograph Extension A-3AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac5 CJK Ideograph Extension A-3AC5 | 3ac4 CJK Ideograph Extension A-3AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac7 CJK Ideograph Extension A-3AC7 | 3ac6 CJK Ideograph Extension A-3AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ac9 CJK Ideograph Extension A-3AC9 | 3ac8 CJK Ideograph Extension A-3AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acb CJK Ideograph Extension A-3ACB | 3aca CJK Ideograph Extension A-3ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acd CJK Ideograph Extension A-3ACD | 3acc CJK Ideograph Extension A-3ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3acf CJK Ideograph Extension A-3ACF | 3ace CJK Ideograph Extension A-3ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad1 CJK Ideograph Extension A-3AD1 | 3ad0 CJK Ideograph Extension A-3AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad3 CJK Ideograph Extension A-3AD3 | 3ad2 CJK Ideograph Extension A-3AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad5 CJK Ideograph Extension A-3AD5 | 3ad4 CJK Ideograph Extension A-3AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad7 CJK Ideograph Extension A-3AD7 | 3ad6 CJK Ideograph Extension A-3AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ad9 CJK Ideograph Extension A-3AD9 | 3ad8 CJK Ideograph Extension A-3AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3adb CJK Ideograph Extension A-3ADB | 3ada CJK Ideograph Extension A-3ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3add CJK Ideograph Extension A-3ADD | 3adc CJK Ideograph Extension A-3ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3adf CJK Ideograph Extension A-3ADF | 3ade CJK Ideograph Extension A-3ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae1 CJK Ideograph Extension A-3AE1 | 3ae0 CJK Ideograph Extension A-3AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae3 CJK Ideograph Extension A-3AE3 | 3ae2 CJK Ideograph Extension A-3AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae5 CJK Ideograph Extension A-3AE5 | 3ae4 CJK Ideograph Extension A-3AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae7 CJK Ideograph Extension A-3AE7 | 3ae6 CJK Ideograph Extension A-3AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ae9 CJK Ideograph Extension A-3AE9 | 3ae8 CJK Ideograph Extension A-3AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aeb CJK Ideograph Extension A-3AEB | 3aea CJK Ideograph Extension A-3AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aed CJK Ideograph Extension A-3AED | 3aec CJK Ideograph Extension A-3AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aef CJK Ideograph Extension A-3AEF | 3aee CJK Ideograph Extension A-3AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af1 CJK Ideograph Extension A-3AF1 | 3af0 CJK Ideograph Extension A-3AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af3 CJK Ideograph Extension A-3AF3 | 3af2 CJK Ideograph Extension A-3AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af5 CJK Ideograph Extension A-3AF5 | 3af4 CJK Ideograph Extension A-3AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af7 CJK Ideograph Extension A-3AF7 | 3af6 CJK Ideograph Extension A-3AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3af9 CJK Ideograph Extension A-3AF9 | 3af8 CJK Ideograph Extension A-3AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3afb CJK Ideograph Extension A-3AFB | 3afa CJK Ideograph Extension A-3AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3afd CJK Ideograph Extension A-3AFD | 3afc CJK Ideograph Extension A-3AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3aff CJK Ideograph Extension A-3AFF | 3afe CJK Ideograph Extension A-3AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b01 CJK Ideograph Extension A-3B01 | 3b00 CJK Ideograph Extension A-3B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b03 CJK Ideograph Extension A-3B03 | 3b02 CJK Ideograph Extension A-3B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b05 CJK Ideograph Extension A-3B05 | 3b04 CJK Ideograph Extension A-3B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b07 CJK Ideograph Extension A-3B07 | 3b06 CJK Ideograph Extension A-3B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b09 CJK Ideograph Extension A-3B09 | 3b08 CJK Ideograph Extension A-3B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0b CJK Ideograph Extension A-3B0B | 3b0a CJK Ideograph Extension A-3B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0d CJK Ideograph Extension A-3B0D | 3b0c CJK Ideograph Extension A-3B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b0f CJK Ideograph Extension A-3B0F | 3b0e CJK Ideograph Extension A-3B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b11 CJK Ideograph Extension A-3B11 | 3b10 CJK Ideograph Extension A-3B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b13 CJK Ideograph Extension A-3B13 | 3b12 CJK Ideograph Extension A-3B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b15 CJK Ideograph Extension A-3B15 | 3b14 CJK Ideograph Extension A-3B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b17 CJK Ideograph Extension A-3B17 | 3b16 CJK Ideograph Extension A-3B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b19 CJK Ideograph Extension A-3B19 | 3b18 CJK Ideograph Extension A-3B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1b CJK Ideograph Extension A-3B1B | 3b1a CJK Ideograph Extension A-3B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1d CJK Ideograph Extension A-3B1D | 3b1c CJK Ideograph Extension A-3B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b1f CJK Ideograph Extension A-3B1F | 3b1e CJK Ideograph Extension A-3B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b21 CJK Ideograph Extension A-3B21 | 3b20 CJK Ideograph Extension A-3B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b23 CJK Ideograph Extension A-3B23 | 3b22 CJK Ideograph Extension A-3B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b25 CJK Ideograph Extension A-3B25 | 3b24 CJK Ideograph Extension A-3B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b27 CJK Ideograph Extension A-3B27 | 3b26 CJK Ideograph Extension A-3B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b29 CJK Ideograph Extension A-3B29 | 3b28 CJK Ideograph Extension A-3B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2b CJK Ideograph Extension A-3B2B | 3b2a CJK Ideograph Extension A-3B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2d CJK Ideograph Extension A-3B2D | 3b2c CJK Ideograph Extension A-3B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b2f CJK Ideograph Extension A-3B2F | 3b2e CJK Ideograph Extension A-3B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b31 CJK Ideograph Extension A-3B31 | 3b30 CJK Ideograph Extension A-3B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b33 CJK Ideograph Extension A-3B33 | 3b32 CJK Ideograph Extension A-3B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b35 CJK Ideograph Extension A-3B35 | 3b34 CJK Ideograph Extension A-3B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b37 CJK Ideograph Extension A-3B37 | 3b36 CJK Ideograph Extension A-3B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b39 CJK Ideograph Extension A-3B39 | 3b38 CJK Ideograph Extension A-3B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3b CJK Ideograph Extension A-3B3B | 3b3a CJK Ideograph Extension A-3B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3d CJK Ideograph Extension A-3B3D | 3b3c CJK Ideograph Extension A-3B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b3f CJK Ideograph Extension A-3B3F | 3b3e CJK Ideograph Extension A-3B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b41 CJK Ideograph Extension A-3B41 | 3b40 CJK Ideograph Extension A-3B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b43 CJK Ideograph Extension A-3B43 | 3b42 CJK Ideograph Extension A-3B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b45 CJK Ideograph Extension A-3B45 | 3b44 CJK Ideograph Extension A-3B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b47 CJK Ideograph Extension A-3B47 | 3b46 CJK Ideograph Extension A-3B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b49 CJK Ideograph Extension A-3B49 | 3b48 CJK Ideograph Extension A-3B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4b CJK Ideograph Extension A-3B4B | 3b4a CJK Ideograph Extension A-3B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4d CJK Ideograph Extension A-3B4D | 3b4c CJK Ideograph Extension A-3B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b4f CJK Ideograph Extension A-3B4F | 3b4e CJK Ideograph Extension A-3B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b51 CJK Ideograph Extension A-3B51 | 3b50 CJK Ideograph Extension A-3B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b53 CJK Ideograph Extension A-3B53 | 3b52 CJK Ideograph Extension A-3B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b55 CJK Ideograph Extension A-3B55 | 3b54 CJK Ideograph Extension A-3B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b57 CJK Ideograph Extension A-3B57 | 3b56 CJK Ideograph Extension A-3B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b59 CJK Ideograph Extension A-3B59 | 3b58 CJK Ideograph Extension A-3B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5b CJK Ideograph Extension A-3B5B | 3b5a CJK Ideograph Extension A-3B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5d CJK Ideograph Extension A-3B5D | 3b5c CJK Ideograph Extension A-3B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b5f CJK Ideograph Extension A-3B5F | 3b5e CJK Ideograph Extension A-3B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b61 CJK Ideograph Extension A-3B61 | 3b60 CJK Ideograph Extension A-3B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b63 CJK Ideograph Extension A-3B63 | 3b62 CJK Ideograph Extension A-3B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b65 CJK Ideograph Extension A-3B65 | 3b64 CJK Ideograph Extension A-3B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b67 CJK Ideograph Extension A-3B67 | 3b66 CJK Ideograph Extension A-3B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b69 CJK Ideograph Extension A-3B69 | 3b68 CJK Ideograph Extension A-3B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6b CJK Ideograph Extension A-3B6B | 3b6a CJK Ideograph Extension A-3B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6d CJK Ideograph Extension A-3B6D | 3b6c CJK Ideograph Extension A-3B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b6f CJK Ideograph Extension A-3B6F | 3b6e CJK Ideograph Extension A-3B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b71 CJK Ideograph Extension A-3B71 | 3b70 CJK Ideograph Extension A-3B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b73 CJK Ideograph Extension A-3B73 | 3b72 CJK Ideograph Extension A-3B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b75 CJK Ideograph Extension A-3B75 | 3b74 CJK Ideograph Extension A-3B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b77 CJK Ideograph Extension A-3B77 | 3b76 CJK Ideograph Extension A-3B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b79 CJK Ideograph Extension A-3B79 | 3b78 CJK Ideograph Extension A-3B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7b CJK Ideograph Extension A-3B7B | 3b7a CJK Ideograph Extension A-3B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7d CJK Ideograph Extension A-3B7D | 3b7c CJK Ideograph Extension A-3B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b7f CJK Ideograph Extension A-3B7F | 3b7e CJK Ideograph Extension A-3B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b81 CJK Ideograph Extension A-3B81 | 3b80 CJK Ideograph Extension A-3B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b83 CJK Ideograph Extension A-3B83 | 3b82 CJK Ideograph Extension A-3B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b85 CJK Ideograph Extension A-3B85 | 3b84 CJK Ideograph Extension A-3B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b87 CJK Ideograph Extension A-3B87 | 3b86 CJK Ideograph Extension A-3B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b89 CJK Ideograph Extension A-3B89 | 3b88 CJK Ideograph Extension A-3B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8b CJK Ideograph Extension A-3B8B | 3b8a CJK Ideograph Extension A-3B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8d CJK Ideograph Extension A-3B8D | 3b8c CJK Ideograph Extension A-3B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b8f CJK Ideograph Extension A-3B8F | 3b8e CJK Ideograph Extension A-3B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b91 CJK Ideograph Extension A-3B91 | 3b90 CJK Ideograph Extension A-3B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b93 CJK Ideograph Extension A-3B93 | 3b92 CJK Ideograph Extension A-3B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b95 CJK Ideograph Extension A-3B95 | 3b94 CJK Ideograph Extension A-3B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b97 CJK Ideograph Extension A-3B97 | 3b96 CJK Ideograph Extension A-3B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b99 CJK Ideograph Extension A-3B99 | 3b98 CJK Ideograph Extension A-3B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9b CJK Ideograph Extension A-3B9B | 3b9a CJK Ideograph Extension A-3B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9d CJK Ideograph Extension A-3B9D | 3b9c CJK Ideograph Extension A-3B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3b9f CJK Ideograph Extension A-3B9F | 3b9e CJK Ideograph Extension A-3B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba1 CJK Ideograph Extension A-3BA1 | 3ba0 CJK Ideograph Extension A-3BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba3 CJK Ideograph Extension A-3BA3 | 3ba2 CJK Ideograph Extension A-3BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba5 CJK Ideograph Extension A-3BA5 | 3ba4 CJK Ideograph Extension A-3BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba7 CJK Ideograph Extension A-3BA7 | 3ba6 CJK Ideograph Extension A-3BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ba9 CJK Ideograph Extension A-3BA9 | 3ba8 CJK Ideograph Extension A-3BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bab CJK Ideograph Extension A-3BAB | 3baa CJK Ideograph Extension A-3BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bad CJK Ideograph Extension A-3BAD | 3bac CJK Ideograph Extension A-3BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3baf CJK Ideograph Extension A-3BAF | 3bae CJK Ideograph Extension A-3BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb1 CJK Ideograph Extension A-3BB1 | 3bb0 CJK Ideograph Extension A-3BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb3 CJK Ideograph Extension A-3BB3 | 3bb2 CJK Ideograph Extension A-3BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb5 CJK Ideograph Extension A-3BB5 | 3bb4 CJK Ideograph Extension A-3BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb7 CJK Ideograph Extension A-3BB7 | 3bb6 CJK Ideograph Extension A-3BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bb9 CJK Ideograph Extension A-3BB9 | 3bb8 CJK Ideograph Extension A-3BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbb CJK Ideograph Extension A-3BBB | 3bba CJK Ideograph Extension A-3BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbd CJK Ideograph Extension A-3BBD | 3bbc CJK Ideograph Extension A-3BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bbf CJK Ideograph Extension A-3BBF | 3bbe CJK Ideograph Extension A-3BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc1 CJK Ideograph Extension A-3BC1 | 3bc0 CJK Ideograph Extension A-3BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc3 CJK Ideograph Extension A-3BC3 | 3bc2 CJK Ideograph Extension A-3BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc5 CJK Ideograph Extension A-3BC5 | 3bc4 CJK Ideograph Extension A-3BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc7 CJK Ideograph Extension A-3BC7 | 3bc6 CJK Ideograph Extension A-3BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bc9 CJK Ideograph Extension A-3BC9 | 3bc8 CJK Ideograph Extension A-3BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcb CJK Ideograph Extension A-3BCB | 3bca CJK Ideograph Extension A-3BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcd CJK Ideograph Extension A-3BCD | 3bcc CJK Ideograph Extension A-3BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bcf CJK Ideograph Extension A-3BCF | 3bce CJK Ideograph Extension A-3BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd1 CJK Ideograph Extension A-3BD1 | 3bd0 CJK Ideograph Extension A-3BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd3 CJK Ideograph Extension A-3BD3 | 3bd2 CJK Ideograph Extension A-3BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd5 CJK Ideograph Extension A-3BD5 | 3bd4 CJK Ideograph Extension A-3BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd7 CJK Ideograph Extension A-3BD7 | 3bd6 CJK Ideograph Extension A-3BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bd9 CJK Ideograph Extension A-3BD9 | 3bd8 CJK Ideograph Extension A-3BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdb CJK Ideograph Extension A-3BDB | 3bda CJK Ideograph Extension A-3BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdd CJK Ideograph Extension A-3BDD | 3bdc CJK Ideograph Extension A-3BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bdf CJK Ideograph Extension A-3BDF | 3bde CJK Ideograph Extension A-3BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be1 CJK Ideograph Extension A-3BE1 | 3be0 CJK Ideograph Extension A-3BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be3 CJK Ideograph Extension A-3BE3 | 3be2 CJK Ideograph Extension A-3BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be5 CJK Ideograph Extension A-3BE5 | 3be4 CJK Ideograph Extension A-3BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be7 CJK Ideograph Extension A-3BE7 | 3be6 CJK Ideograph Extension A-3BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3be9 CJK Ideograph Extension A-3BE9 | 3be8 CJK Ideograph Extension A-3BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3beb CJK Ideograph Extension A-3BEB | 3bea CJK Ideograph Extension A-3BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bed CJK Ideograph Extension A-3BED | 3bec CJK Ideograph Extension A-3BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bef CJK Ideograph Extension A-3BEF | 3bee CJK Ideograph Extension A-3BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf1 CJK Ideograph Extension A-3BF1 | 3bf0 CJK Ideograph Extension A-3BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf3 CJK Ideograph Extension A-3BF3 | 3bf2 CJK Ideograph Extension A-3BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf5 CJK Ideograph Extension A-3BF5 | 3bf4 CJK Ideograph Extension A-3BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf7 CJK Ideograph Extension A-3BF7 | 3bf6 CJK Ideograph Extension A-3BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bf9 CJK Ideograph Extension A-3BF9 | 3bf8 CJK Ideograph Extension A-3BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bfb CJK Ideograph Extension A-3BFB | 3bfa CJK Ideograph Extension A-3BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bfd CJK Ideograph Extension A-3BFD | 3bfc CJK Ideograph Extension A-3BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3bff CJK Ideograph Extension A-3BFF | 3bfe CJK Ideograph Extension A-3BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c01 CJK Ideograph Extension A-3C01 | 3c00 CJK Ideograph Extension A-3C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c03 CJK Ideograph Extension A-3C03 | 3c02 CJK Ideograph Extension A-3C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c05 CJK Ideograph Extension A-3C05 | 3c04 CJK Ideograph Extension A-3C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c07 CJK Ideograph Extension A-3C07 | 3c06 CJK Ideograph Extension A-3C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c09 CJK Ideograph Extension A-3C09 | 3c08 CJK Ideograph Extension A-3C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0b CJK Ideograph Extension A-3C0B | 3c0a CJK Ideograph Extension A-3C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0d CJK Ideograph Extension A-3C0D | 3c0c CJK Ideograph Extension A-3C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c0f CJK Ideograph Extension A-3C0F | 3c0e CJK Ideograph Extension A-3C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c11 CJK Ideograph Extension A-3C11 | 3c10 CJK Ideograph Extension A-3C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c13 CJK Ideograph Extension A-3C13 | 3c12 CJK Ideograph Extension A-3C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c15 CJK Ideograph Extension A-3C15 | 3c14 CJK Ideograph Extension A-3C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c17 CJK Ideograph Extension A-3C17 | 3c16 CJK Ideograph Extension A-3C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c19 CJK Ideograph Extension A-3C19 | 3c18 CJK Ideograph Extension A-3C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1b CJK Ideograph Extension A-3C1B | 3c1a CJK Ideograph Extension A-3C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1d CJK Ideograph Extension A-3C1D | 3c1c CJK Ideograph Extension A-3C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c1f CJK Ideograph Extension A-3C1F | 3c1e CJK Ideograph Extension A-3C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c21 CJK Ideograph Extension A-3C21 | 3c20 CJK Ideograph Extension A-3C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c23 CJK Ideograph Extension A-3C23 | 3c22 CJK Ideograph Extension A-3C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c25 CJK Ideograph Extension A-3C25 | 3c24 CJK Ideograph Extension A-3C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c27 CJK Ideograph Extension A-3C27 | 3c26 CJK Ideograph Extension A-3C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c29 CJK Ideograph Extension A-3C29 | 3c28 CJK Ideograph Extension A-3C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2b CJK Ideograph Extension A-3C2B | 3c2a CJK Ideograph Extension A-3C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2d CJK Ideograph Extension A-3C2D | 3c2c CJK Ideograph Extension A-3C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c2f CJK Ideograph Extension A-3C2F | 3c2e CJK Ideograph Extension A-3C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c31 CJK Ideograph Extension A-3C31 | 3c30 CJK Ideograph Extension A-3C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c33 CJK Ideograph Extension A-3C33 | 3c32 CJK Ideograph Extension A-3C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c35 CJK Ideograph Extension A-3C35 | 3c34 CJK Ideograph Extension A-3C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c37 CJK Ideograph Extension A-3C37 | 3c36 CJK Ideograph Extension A-3C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c39 CJK Ideograph Extension A-3C39 | 3c38 CJK Ideograph Extension A-3C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3b CJK Ideograph Extension A-3C3B | 3c3a CJK Ideograph Extension A-3C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3d CJK Ideograph Extension A-3C3D | 3c3c CJK Ideograph Extension A-3C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c3f CJK Ideograph Extension A-3C3F | 3c3e CJK Ideograph Extension A-3C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c41 CJK Ideograph Extension A-3C41 | 3c40 CJK Ideograph Extension A-3C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c43 CJK Ideograph Extension A-3C43 | 3c42 CJK Ideograph Extension A-3C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c45 CJK Ideograph Extension A-3C45 | 3c44 CJK Ideograph Extension A-3C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c47 CJK Ideograph Extension A-3C47 | 3c46 CJK Ideograph Extension A-3C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c49 CJK Ideograph Extension A-3C49 | 3c48 CJK Ideograph Extension A-3C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4b CJK Ideograph Extension A-3C4B | 3c4a CJK Ideograph Extension A-3C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4d CJK Ideograph Extension A-3C4D | 3c4c CJK Ideograph Extension A-3C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c4f CJK Ideograph Extension A-3C4F | 3c4e CJK Ideograph Extension A-3C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c51 CJK Ideograph Extension A-3C51 | 3c50 CJK Ideograph Extension A-3C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c53 CJK Ideograph Extension A-3C53 | 3c52 CJK Ideograph Extension A-3C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c55 CJK Ideograph Extension A-3C55 | 3c54 CJK Ideograph Extension A-3C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c57 CJK Ideograph Extension A-3C57 | 3c56 CJK Ideograph Extension A-3C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c59 CJK Ideograph Extension A-3C59 | 3c58 CJK Ideograph Extension A-3C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5b CJK Ideograph Extension A-3C5B | 3c5a CJK Ideograph Extension A-3C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5d CJK Ideograph Extension A-3C5D | 3c5c CJK Ideograph Extension A-3C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c5f CJK Ideograph Extension A-3C5F | 3c5e CJK Ideograph Extension A-3C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c61 CJK Ideograph Extension A-3C61 | 3c60 CJK Ideograph Extension A-3C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c63 CJK Ideograph Extension A-3C63 | 3c62 CJK Ideograph Extension A-3C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c65 CJK Ideograph Extension A-3C65 | 3c64 CJK Ideograph Extension A-3C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c67 CJK Ideograph Extension A-3C67 | 3c66 CJK Ideograph Extension A-3C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c69 CJK Ideograph Extension A-3C69 | 3c68 CJK Ideograph Extension A-3C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6b CJK Ideograph Extension A-3C6B | 3c6a CJK Ideograph Extension A-3C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6d CJK Ideograph Extension A-3C6D | 3c6c CJK Ideograph Extension A-3C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c6f CJK Ideograph Extension A-3C6F | 3c6e CJK Ideograph Extension A-3C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c71 CJK Ideograph Extension A-3C71 | 3c70 CJK Ideograph Extension A-3C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c73 CJK Ideograph Extension A-3C73 | 3c72 CJK Ideograph Extension A-3C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c75 CJK Ideograph Extension A-3C75 | 3c74 CJK Ideograph Extension A-3C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c77 CJK Ideograph Extension A-3C77 | 3c76 CJK Ideograph Extension A-3C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c79 CJK Ideograph Extension A-3C79 | 3c78 CJK Ideograph Extension A-3C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7b CJK Ideograph Extension A-3C7B | 3c7a CJK Ideograph Extension A-3C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7d CJK Ideograph Extension A-3C7D | 3c7c CJK Ideograph Extension A-3C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c7f CJK Ideograph Extension A-3C7F | 3c7e CJK Ideograph Extension A-3C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c81 CJK Ideograph Extension A-3C81 | 3c80 CJK Ideograph Extension A-3C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c83 CJK Ideograph Extension A-3C83 | 3c82 CJK Ideograph Extension A-3C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c85 CJK Ideograph Extension A-3C85 | 3c84 CJK Ideograph Extension A-3C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c87 CJK Ideograph Extension A-3C87 | 3c86 CJK Ideograph Extension A-3C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c89 CJK Ideograph Extension A-3C89 | 3c88 CJK Ideograph Extension A-3C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8b CJK Ideograph Extension A-3C8B | 3c8a CJK Ideograph Extension A-3C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8d CJK Ideograph Extension A-3C8D | 3c8c CJK Ideograph Extension A-3C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c8f CJK Ideograph Extension A-3C8F | 3c8e CJK Ideograph Extension A-3C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c91 CJK Ideograph Extension A-3C91 | 3c90 CJK Ideograph Extension A-3C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c93 CJK Ideograph Extension A-3C93 | 3c92 CJK Ideograph Extension A-3C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c95 CJK Ideograph Extension A-3C95 | 3c94 CJK Ideograph Extension A-3C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c97 CJK Ideograph Extension A-3C97 | 3c96 CJK Ideograph Extension A-3C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c99 CJK Ideograph Extension A-3C99 | 3c98 CJK Ideograph Extension A-3C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9b CJK Ideograph Extension A-3C9B | 3c9a CJK Ideograph Extension A-3C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9d CJK Ideograph Extension A-3C9D | 3c9c CJK Ideograph Extension A-3C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3c9f CJK Ideograph Extension A-3C9F | 3c9e CJK Ideograph Extension A-3C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca1 CJK Ideograph Extension A-3CA1 | 3ca0 CJK Ideograph Extension A-3CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca3 CJK Ideograph Extension A-3CA3 | 3ca2 CJK Ideograph Extension A-3CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca5 CJK Ideograph Extension A-3CA5 | 3ca4 CJK Ideograph Extension A-3CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca7 CJK Ideograph Extension A-3CA7 | 3ca6 CJK Ideograph Extension A-3CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ca9 CJK Ideograph Extension A-3CA9 | 3ca8 CJK Ideograph Extension A-3CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cab CJK Ideograph Extension A-3CAB | 3caa CJK Ideograph Extension A-3CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cad CJK Ideograph Extension A-3CAD | 3cac CJK Ideograph Extension A-3CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3caf CJK Ideograph Extension A-3CAF | 3cae CJK Ideograph Extension A-3CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb1 CJK Ideograph Extension A-3CB1 | 3cb0 CJK Ideograph Extension A-3CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb3 CJK Ideograph Extension A-3CB3 | 3cb2 CJK Ideograph Extension A-3CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb5 CJK Ideograph Extension A-3CB5 | 3cb4 CJK Ideograph Extension A-3CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb7 CJK Ideograph Extension A-3CB7 | 3cb6 CJK Ideograph Extension A-3CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cb9 CJK Ideograph Extension A-3CB9 | 3cb8 CJK Ideograph Extension A-3CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbb CJK Ideograph Extension A-3CBB | 3cba CJK Ideograph Extension A-3CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbd CJK Ideograph Extension A-3CBD | 3cbc CJK Ideograph Extension A-3CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cbf CJK Ideograph Extension A-3CBF | 3cbe CJK Ideograph Extension A-3CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc1 CJK Ideograph Extension A-3CC1 | 3cc0 CJK Ideograph Extension A-3CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc3 CJK Ideograph Extension A-3CC3 | 3cc2 CJK Ideograph Extension A-3CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc5 CJK Ideograph Extension A-3CC5 | 3cc4 CJK Ideograph Extension A-3CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc7 CJK Ideograph Extension A-3CC7 | 3cc6 CJK Ideograph Extension A-3CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cc9 CJK Ideograph Extension A-3CC9 | 3cc8 CJK Ideograph Extension A-3CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccb CJK Ideograph Extension A-3CCB | 3cca CJK Ideograph Extension A-3CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccd CJK Ideograph Extension A-3CCD | 3ccc CJK Ideograph Extension A-3CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ccf CJK Ideograph Extension A-3CCF | 3cce CJK Ideograph Extension A-3CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd1 CJK Ideograph Extension A-3CD1 | 3cd0 CJK Ideograph Extension A-3CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd3 CJK Ideograph Extension A-3CD3 | 3cd2 CJK Ideograph Extension A-3CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd5 CJK Ideograph Extension A-3CD5 | 3cd4 CJK Ideograph Extension A-3CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd7 CJK Ideograph Extension A-3CD7 | 3cd6 CJK Ideograph Extension A-3CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cd9 CJK Ideograph Extension A-3CD9 | 3cd8 CJK Ideograph Extension A-3CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdb CJK Ideograph Extension A-3CDB | 3cda CJK Ideograph Extension A-3CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdd CJK Ideograph Extension A-3CDD | 3cdc CJK Ideograph Extension A-3CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cdf CJK Ideograph Extension A-3CDF | 3cde CJK Ideograph Extension A-3CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce1 CJK Ideograph Extension A-3CE1 | 3ce0 CJK Ideograph Extension A-3CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce3 CJK Ideograph Extension A-3CE3 | 3ce2 CJK Ideograph Extension A-3CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce5 CJK Ideograph Extension A-3CE5 | 3ce4 CJK Ideograph Extension A-3CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce7 CJK Ideograph Extension A-3CE7 | 3ce6 CJK Ideograph Extension A-3CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ce9 CJK Ideograph Extension A-3CE9 | 3ce8 CJK Ideograph Extension A-3CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ceb CJK Ideograph Extension A-3CEB | 3cea CJK Ideograph Extension A-3CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ced CJK Ideograph Extension A-3CED | 3cec CJK Ideograph Extension A-3CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cef CJK Ideograph Extension A-3CEF | 3cee CJK Ideograph Extension A-3CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf1 CJK Ideograph Extension A-3CF1 | 3cf0 CJK Ideograph Extension A-3CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf3 CJK Ideograph Extension A-3CF3 | 3cf2 CJK Ideograph Extension A-3CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf5 CJK Ideograph Extension A-3CF5 | 3cf4 CJK Ideograph Extension A-3CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf7 CJK Ideograph Extension A-3CF7 | 3cf6 CJK Ideograph Extension A-3CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cf9 CJK Ideograph Extension A-3CF9 | 3cf8 CJK Ideograph Extension A-3CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cfb CJK Ideograph Extension A-3CFB | 3cfa CJK Ideograph Extension A-3CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cfd CJK Ideograph Extension A-3CFD | 3cfc CJK Ideograph Extension A-3CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3cff CJK Ideograph Extension A-3CFF | 3cfe CJK Ideograph Extension A-3CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d01 CJK Ideograph Extension A-3D01 | 3d00 CJK Ideograph Extension A-3D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d03 CJK Ideograph Extension A-3D03 | 3d02 CJK Ideograph Extension A-3D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d05 CJK Ideograph Extension A-3D05 | 3d04 CJK Ideograph Extension A-3D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d07 CJK Ideograph Extension A-3D07 | 3d06 CJK Ideograph Extension A-3D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d09 CJK Ideograph Extension A-3D09 | 3d08 CJK Ideograph Extension A-3D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0b CJK Ideograph Extension A-3D0B | 3d0a CJK Ideograph Extension A-3D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0d CJK Ideograph Extension A-3D0D | 3d0c CJK Ideograph Extension A-3D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d0f CJK Ideograph Extension A-3D0F | 3d0e CJK Ideograph Extension A-3D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d11 CJK Ideograph Extension A-3D11 | 3d10 CJK Ideograph Extension A-3D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d13 CJK Ideograph Extension A-3D13 | 3d12 CJK Ideograph Extension A-3D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d15 CJK Ideograph Extension A-3D15 | 3d14 CJK Ideograph Extension A-3D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d17 CJK Ideograph Extension A-3D17 | 3d16 CJK Ideograph Extension A-3D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d19 CJK Ideograph Extension A-3D19 | 3d18 CJK Ideograph Extension A-3D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1b CJK Ideograph Extension A-3D1B | 3d1a CJK Ideograph Extension A-3D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1d CJK Ideograph Extension A-3D1D | 3d1c CJK Ideograph Extension A-3D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d1f CJK Ideograph Extension A-3D1F | 3d1e CJK Ideograph Extension A-3D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d21 CJK Ideograph Extension A-3D21 | 3d20 CJK Ideograph Extension A-3D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d23 CJK Ideograph Extension A-3D23 | 3d22 CJK Ideograph Extension A-3D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d25 CJK Ideograph Extension A-3D25 | 3d24 CJK Ideograph Extension A-3D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d27 CJK Ideograph Extension A-3D27 | 3d26 CJK Ideograph Extension A-3D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d29 CJK Ideograph Extension A-3D29 | 3d28 CJK Ideograph Extension A-3D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2b CJK Ideograph Extension A-3D2B | 3d2a CJK Ideograph Extension A-3D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2d CJK Ideograph Extension A-3D2D | 3d2c CJK Ideograph Extension A-3D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d2f CJK Ideograph Extension A-3D2F | 3d2e CJK Ideograph Extension A-3D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d31 CJK Ideograph Extension A-3D31 | 3d30 CJK Ideograph Extension A-3D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d33 CJK Ideograph Extension A-3D33 | 3d32 CJK Ideograph Extension A-3D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d35 CJK Ideograph Extension A-3D35 | 3d34 CJK Ideograph Extension A-3D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d37 CJK Ideograph Extension A-3D37 | 3d36 CJK Ideograph Extension A-3D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d39 CJK Ideograph Extension A-3D39 | 3d38 CJK Ideograph Extension A-3D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3b CJK Ideograph Extension A-3D3B | 3d3a CJK Ideograph Extension A-3D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3d CJK Ideograph Extension A-3D3D | 3d3c CJK Ideograph Extension A-3D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d3f CJK Ideograph Extension A-3D3F | 3d3e CJK Ideograph Extension A-3D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d41 CJK Ideograph Extension A-3D41 | 3d40 CJK Ideograph Extension A-3D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d43 CJK Ideograph Extension A-3D43 | 3d42 CJK Ideograph Extension A-3D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d45 CJK Ideograph Extension A-3D45 | 3d44 CJK Ideograph Extension A-3D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d47 CJK Ideograph Extension A-3D47 | 3d46 CJK Ideograph Extension A-3D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d49 CJK Ideograph Extension A-3D49 | 3d48 CJK Ideograph Extension A-3D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4b CJK Ideograph Extension A-3D4B | 3d4a CJK Ideograph Extension A-3D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4d CJK Ideograph Extension A-3D4D | 3d4c CJK Ideograph Extension A-3D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d4f CJK Ideograph Extension A-3D4F | 3d4e CJK Ideograph Extension A-3D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d51 CJK Ideograph Extension A-3D51 | 3d50 CJK Ideograph Extension A-3D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d53 CJK Ideograph Extension A-3D53 | 3d52 CJK Ideograph Extension A-3D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d55 CJK Ideograph Extension A-3D55 | 3d54 CJK Ideograph Extension A-3D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d57 CJK Ideograph Extension A-3D57 | 3d56 CJK Ideograph Extension A-3D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d59 CJK Ideograph Extension A-3D59 | 3d58 CJK Ideograph Extension A-3D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5b CJK Ideograph Extension A-3D5B | 3d5a CJK Ideograph Extension A-3D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5d CJK Ideograph Extension A-3D5D | 3d5c CJK Ideograph Extension A-3D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d5f CJK Ideograph Extension A-3D5F | 3d5e CJK Ideograph Extension A-3D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d61 CJK Ideograph Extension A-3D61 | 3d60 CJK Ideograph Extension A-3D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d63 CJK Ideograph Extension A-3D63 | 3d62 CJK Ideograph Extension A-3D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d65 CJK Ideograph Extension A-3D65 | 3d64 CJK Ideograph Extension A-3D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d67 CJK Ideograph Extension A-3D67 | 3d66 CJK Ideograph Extension A-3D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d69 CJK Ideograph Extension A-3D69 | 3d68 CJK Ideograph Extension A-3D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6b CJK Ideograph Extension A-3D6B | 3d6a CJK Ideograph Extension A-3D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6d CJK Ideograph Extension A-3D6D | 3d6c CJK Ideograph Extension A-3D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d6f CJK Ideograph Extension A-3D6F | 3d6e CJK Ideograph Extension A-3D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d71 CJK Ideograph Extension A-3D71 | 3d70 CJK Ideograph Extension A-3D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d73 CJK Ideograph Extension A-3D73 | 3d72 CJK Ideograph Extension A-3D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d75 CJK Ideograph Extension A-3D75 | 3d74 CJK Ideograph Extension A-3D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d77 CJK Ideograph Extension A-3D77 | 3d76 CJK Ideograph Extension A-3D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d79 CJK Ideograph Extension A-3D79 | 3d78 CJK Ideograph Extension A-3D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7b CJK Ideograph Extension A-3D7B | 3d7a CJK Ideograph Extension A-3D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7d CJK Ideograph Extension A-3D7D | 3d7c CJK Ideograph Extension A-3D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d7f CJK Ideograph Extension A-3D7F | 3d7e CJK Ideograph Extension A-3D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d81 CJK Ideograph Extension A-3D81 | 3d80 CJK Ideograph Extension A-3D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d83 CJK Ideograph Extension A-3D83 | 3d82 CJK Ideograph Extension A-3D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d85 CJK Ideograph Extension A-3D85 | 3d84 CJK Ideograph Extension A-3D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d87 CJK Ideograph Extension A-3D87 | 3d86 CJK Ideograph Extension A-3D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d89 CJK Ideograph Extension A-3D89 | 3d88 CJK Ideograph Extension A-3D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8b CJK Ideograph Extension A-3D8B | 3d8a CJK Ideograph Extension A-3D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8d CJK Ideograph Extension A-3D8D | 3d8c CJK Ideograph Extension A-3D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d8f CJK Ideograph Extension A-3D8F | 3d8e CJK Ideograph Extension A-3D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d91 CJK Ideograph Extension A-3D91 | 3d90 CJK Ideograph Extension A-3D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d93 CJK Ideograph Extension A-3D93 | 3d92 CJK Ideograph Extension A-3D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d95 CJK Ideograph Extension A-3D95 | 3d94 CJK Ideograph Extension A-3D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d97 CJK Ideograph Extension A-3D97 | 3d96 CJK Ideograph Extension A-3D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d99 CJK Ideograph Extension A-3D99 | 3d98 CJK Ideograph Extension A-3D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9b CJK Ideograph Extension A-3D9B | 3d9a CJK Ideograph Extension A-3D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9d CJK Ideograph Extension A-3D9D | 3d9c CJK Ideograph Extension A-3D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3d9f CJK Ideograph Extension A-3D9F | 3d9e CJK Ideograph Extension A-3D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da1 CJK Ideograph Extension A-3DA1 | 3da0 CJK Ideograph Extension A-3DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da3 CJK Ideograph Extension A-3DA3 | 3da2 CJK Ideograph Extension A-3DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da5 CJK Ideograph Extension A-3DA5 | 3da4 CJK Ideograph Extension A-3DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da7 CJK Ideograph Extension A-3DA7 | 3da6 CJK Ideograph Extension A-3DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3da9 CJK Ideograph Extension A-3DA9 | 3da8 CJK Ideograph Extension A-3DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dab CJK Ideograph Extension A-3DAB | 3daa CJK Ideograph Extension A-3DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dad CJK Ideograph Extension A-3DAD | 3dac CJK Ideograph Extension A-3DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3daf CJK Ideograph Extension A-3DAF | 3dae CJK Ideograph Extension A-3DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db1 CJK Ideograph Extension A-3DB1 | 3db0 CJK Ideograph Extension A-3DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db3 CJK Ideograph Extension A-3DB3 | 3db2 CJK Ideograph Extension A-3DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db5 CJK Ideograph Extension A-3DB5 | 3db4 CJK Ideograph Extension A-3DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db7 CJK Ideograph Extension A-3DB7 | 3db6 CJK Ideograph Extension A-3DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3db9 CJK Ideograph Extension A-3DB9 | 3db8 CJK Ideograph Extension A-3DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbb CJK Ideograph Extension A-3DBB | 3dba CJK Ideograph Extension A-3DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbd CJK Ideograph Extension A-3DBD | 3dbc CJK Ideograph Extension A-3DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dbf CJK Ideograph Extension A-3DBF | 3dbe CJK Ideograph Extension A-3DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc1 CJK Ideograph Extension A-3DC1 | 3dc0 CJK Ideograph Extension A-3DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc3 CJK Ideograph Extension A-3DC3 | 3dc2 CJK Ideograph Extension A-3DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc5 CJK Ideograph Extension A-3DC5 | 3dc4 CJK Ideograph Extension A-3DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc7 CJK Ideograph Extension A-3DC7 | 3dc6 CJK Ideograph Extension A-3DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dc9 CJK Ideograph Extension A-3DC9 | 3dc8 CJK Ideograph Extension A-3DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcb CJK Ideograph Extension A-3DCB | 3dca CJK Ideograph Extension A-3DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcd CJK Ideograph Extension A-3DCD | 3dcc CJK Ideograph Extension A-3DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dcf CJK Ideograph Extension A-3DCF | 3dce CJK Ideograph Extension A-3DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd1 CJK Ideograph Extension A-3DD1 | 3dd0 CJK Ideograph Extension A-3DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd3 CJK Ideograph Extension A-3DD3 | 3dd2 CJK Ideograph Extension A-3DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd5 CJK Ideograph Extension A-3DD5 | 3dd4 CJK Ideograph Extension A-3DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd7 CJK Ideograph Extension A-3DD7 | 3dd6 CJK Ideograph Extension A-3DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dd9 CJK Ideograph Extension A-3DD9 | 3dd8 CJK Ideograph Extension A-3DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddb CJK Ideograph Extension A-3DDB | 3dda CJK Ideograph Extension A-3DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddd CJK Ideograph Extension A-3DDD | 3ddc CJK Ideograph Extension A-3DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ddf CJK Ideograph Extension A-3DDF | 3dde CJK Ideograph Extension A-3DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de1 CJK Ideograph Extension A-3DE1 | 3de0 CJK Ideograph Extension A-3DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de3 CJK Ideograph Extension A-3DE3 | 3de2 CJK Ideograph Extension A-3DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de5 CJK Ideograph Extension A-3DE5 | 3de4 CJK Ideograph Extension A-3DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de7 CJK Ideograph Extension A-3DE7 | 3de6 CJK Ideograph Extension A-3DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3de9 CJK Ideograph Extension A-3DE9 | 3de8 CJK Ideograph Extension A-3DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3deb CJK Ideograph Extension A-3DEB | 3dea CJK Ideograph Extension A-3DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ded CJK Ideograph Extension A-3DED | 3dec CJK Ideograph Extension A-3DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3def CJK Ideograph Extension A-3DEF | 3dee CJK Ideograph Extension A-3DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df1 CJK Ideograph Extension A-3DF1 | 3df0 CJK Ideograph Extension A-3DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df3 CJK Ideograph Extension A-3DF3 | 3df2 CJK Ideograph Extension A-3DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df5 CJK Ideograph Extension A-3DF5 | 3df4 CJK Ideograph Extension A-3DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df7 CJK Ideograph Extension A-3DF7 | 3df6 CJK Ideograph Extension A-3DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3df9 CJK Ideograph Extension A-3DF9 | 3df8 CJK Ideograph Extension A-3DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dfb CJK Ideograph Extension A-3DFB | 3dfa CJK Ideograph Extension A-3DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dfd CJK Ideograph Extension A-3DFD | 3dfc CJK Ideograph Extension A-3DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3dff CJK Ideograph Extension A-3DFF | 3dfe CJK Ideograph Extension A-3DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e01 CJK Ideograph Extension A-3E01 | 3e00 CJK Ideograph Extension A-3E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e03 CJK Ideograph Extension A-3E03 | 3e02 CJK Ideograph Extension A-3E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e05 CJK Ideograph Extension A-3E05 | 3e04 CJK Ideograph Extension A-3E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e07 CJK Ideograph Extension A-3E07 | 3e06 CJK Ideograph Extension A-3E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e09 CJK Ideograph Extension A-3E09 | 3e08 CJK Ideograph Extension A-3E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0b CJK Ideograph Extension A-3E0B | 3e0a CJK Ideograph Extension A-3E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0d CJK Ideograph Extension A-3E0D | 3e0c CJK Ideograph Extension A-3E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e0f CJK Ideograph Extension A-3E0F | 3e0e CJK Ideograph Extension A-3E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e11 CJK Ideograph Extension A-3E11 | 3e10 CJK Ideograph Extension A-3E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e13 CJK Ideograph Extension A-3E13 | 3e12 CJK Ideograph Extension A-3E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e15 CJK Ideograph Extension A-3E15 | 3e14 CJK Ideograph Extension A-3E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e17 CJK Ideograph Extension A-3E17 | 3e16 CJK Ideograph Extension A-3E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e19 CJK Ideograph Extension A-3E19 | 3e18 CJK Ideograph Extension A-3E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1b CJK Ideograph Extension A-3E1B | 3e1a CJK Ideograph Extension A-3E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1d CJK Ideograph Extension A-3E1D | 3e1c CJK Ideograph Extension A-3E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e1f CJK Ideograph Extension A-3E1F | 3e1e CJK Ideograph Extension A-3E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e21 CJK Ideograph Extension A-3E21 | 3e20 CJK Ideograph Extension A-3E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e23 CJK Ideograph Extension A-3E23 | 3e22 CJK Ideograph Extension A-3E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e25 CJK Ideograph Extension A-3E25 | 3e24 CJK Ideograph Extension A-3E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e27 CJK Ideograph Extension A-3E27 | 3e26 CJK Ideograph Extension A-3E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e29 CJK Ideograph Extension A-3E29 | 3e28 CJK Ideograph Extension A-3E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2b CJK Ideograph Extension A-3E2B | 3e2a CJK Ideograph Extension A-3E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2d CJK Ideograph Extension A-3E2D | 3e2c CJK Ideograph Extension A-3E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e2f CJK Ideograph Extension A-3E2F | 3e2e CJK Ideograph Extension A-3E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e31 CJK Ideograph Extension A-3E31 | 3e30 CJK Ideograph Extension A-3E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e33 CJK Ideograph Extension A-3E33 | 3e32 CJK Ideograph Extension A-3E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e35 CJK Ideograph Extension A-3E35 | 3e34 CJK Ideograph Extension A-3E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e37 CJK Ideograph Extension A-3E37 | 3e36 CJK Ideograph Extension A-3E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e39 CJK Ideograph Extension A-3E39 | 3e38 CJK Ideograph Extension A-3E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3b CJK Ideograph Extension A-3E3B | 3e3a CJK Ideograph Extension A-3E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3d CJK Ideograph Extension A-3E3D | 3e3c CJK Ideograph Extension A-3E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e3f CJK Ideograph Extension A-3E3F | 3e3e CJK Ideograph Extension A-3E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e41 CJK Ideograph Extension A-3E41 | 3e40 CJK Ideograph Extension A-3E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e43 CJK Ideograph Extension A-3E43 | 3e42 CJK Ideograph Extension A-3E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e45 CJK Ideograph Extension A-3E45 | 3e44 CJK Ideograph Extension A-3E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e47 CJK Ideograph Extension A-3E47 | 3e46 CJK Ideograph Extension A-3E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e49 CJK Ideograph Extension A-3E49 | 3e48 CJK Ideograph Extension A-3E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4b CJK Ideograph Extension A-3E4B | 3e4a CJK Ideograph Extension A-3E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4d CJK Ideograph Extension A-3E4D | 3e4c CJK Ideograph Extension A-3E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e4f CJK Ideograph Extension A-3E4F | 3e4e CJK Ideograph Extension A-3E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e51 CJK Ideograph Extension A-3E51 | 3e50 CJK Ideograph Extension A-3E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e53 CJK Ideograph Extension A-3E53 | 3e52 CJK Ideograph Extension A-3E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e55 CJK Ideograph Extension A-3E55 | 3e54 CJK Ideograph Extension A-3E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e57 CJK Ideograph Extension A-3E57 | 3e56 CJK Ideograph Extension A-3E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e59 CJK Ideograph Extension A-3E59 | 3e58 CJK Ideograph Extension A-3E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5b CJK Ideograph Extension A-3E5B | 3e5a CJK Ideograph Extension A-3E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5d CJK Ideograph Extension A-3E5D | 3e5c CJK Ideograph Extension A-3E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e5f CJK Ideograph Extension A-3E5F | 3e5e CJK Ideograph Extension A-3E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e61 CJK Ideograph Extension A-3E61 | 3e60 CJK Ideograph Extension A-3E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e63 CJK Ideograph Extension A-3E63 | 3e62 CJK Ideograph Extension A-3E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e65 CJK Ideograph Extension A-3E65 | 3e64 CJK Ideograph Extension A-3E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e67 CJK Ideograph Extension A-3E67 | 3e66 CJK Ideograph Extension A-3E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e69 CJK Ideograph Extension A-3E69 | 3e68 CJK Ideograph Extension A-3E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6b CJK Ideograph Extension A-3E6B | 3e6a CJK Ideograph Extension A-3E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6d CJK Ideograph Extension A-3E6D | 3e6c CJK Ideograph Extension A-3E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e6f CJK Ideograph Extension A-3E6F | 3e6e CJK Ideograph Extension A-3E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e71 CJK Ideograph Extension A-3E71 | 3e70 CJK Ideograph Extension A-3E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e73 CJK Ideograph Extension A-3E73 | 3e72 CJK Ideograph Extension A-3E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e75 CJK Ideograph Extension A-3E75 | 3e74 CJK Ideograph Extension A-3E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e77 CJK Ideograph Extension A-3E77 | 3e76 CJK Ideograph Extension A-3E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e79 CJK Ideograph Extension A-3E79 | 3e78 CJK Ideograph Extension A-3E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7b CJK Ideograph Extension A-3E7B | 3e7a CJK Ideograph Extension A-3E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7d CJK Ideograph Extension A-3E7D | 3e7c CJK Ideograph Extension A-3E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e7f CJK Ideograph Extension A-3E7F | 3e7e CJK Ideograph Extension A-3E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e81 CJK Ideograph Extension A-3E81 | 3e80 CJK Ideograph Extension A-3E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e83 CJK Ideograph Extension A-3E83 | 3e82 CJK Ideograph Extension A-3E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e85 CJK Ideograph Extension A-3E85 | 3e84 CJK Ideograph Extension A-3E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e87 CJK Ideograph Extension A-3E87 | 3e86 CJK Ideograph Extension A-3E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e89 CJK Ideograph Extension A-3E89 | 3e88 CJK Ideograph Extension A-3E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8b CJK Ideograph Extension A-3E8B | 3e8a CJK Ideograph Extension A-3E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8d CJK Ideograph Extension A-3E8D | 3e8c CJK Ideograph Extension A-3E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e8f CJK Ideograph Extension A-3E8F | 3e8e CJK Ideograph Extension A-3E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e91 CJK Ideograph Extension A-3E91 | 3e90 CJK Ideograph Extension A-3E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e93 CJK Ideograph Extension A-3E93 | 3e92 CJK Ideograph Extension A-3E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e95 CJK Ideograph Extension A-3E95 | 3e94 CJK Ideograph Extension A-3E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e97 CJK Ideograph Extension A-3E97 | 3e96 CJK Ideograph Extension A-3E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e99 CJK Ideograph Extension A-3E99 | 3e98 CJK Ideograph Extension A-3E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9b CJK Ideograph Extension A-3E9B | 3e9a CJK Ideograph Extension A-3E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9d CJK Ideograph Extension A-3E9D | 3e9c CJK Ideograph Extension A-3E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3e9f CJK Ideograph Extension A-3E9F | 3e9e CJK Ideograph Extension A-3E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea1 CJK Ideograph Extension A-3EA1 | 3ea0 CJK Ideograph Extension A-3EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea3 CJK Ideograph Extension A-3EA3 | 3ea2 CJK Ideograph Extension A-3EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea5 CJK Ideograph Extension A-3EA5 | 3ea4 CJK Ideograph Extension A-3EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea7 CJK Ideograph Extension A-3EA7 | 3ea6 CJK Ideograph Extension A-3EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ea9 CJK Ideograph Extension A-3EA9 | 3ea8 CJK Ideograph Extension A-3EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eab CJK Ideograph Extension A-3EAB | 3eaa CJK Ideograph Extension A-3EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ead CJK Ideograph Extension A-3EAD | 3eac CJK Ideograph Extension A-3EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eaf CJK Ideograph Extension A-3EAF | 3eae CJK Ideograph Extension A-3EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb1 CJK Ideograph Extension A-3EB1 | 3eb0 CJK Ideograph Extension A-3EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb3 CJK Ideograph Extension A-3EB3 | 3eb2 CJK Ideograph Extension A-3EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb5 CJK Ideograph Extension A-3EB5 | 3eb4 CJK Ideograph Extension A-3EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb7 CJK Ideograph Extension A-3EB7 | 3eb6 CJK Ideograph Extension A-3EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eb9 CJK Ideograph Extension A-3EB9 | 3eb8 CJK Ideograph Extension A-3EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebb CJK Ideograph Extension A-3EBB | 3eba CJK Ideograph Extension A-3EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebd CJK Ideograph Extension A-3EBD | 3ebc CJK Ideograph Extension A-3EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ebf CJK Ideograph Extension A-3EBF | 3ebe CJK Ideograph Extension A-3EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec1 CJK Ideograph Extension A-3EC1 | 3ec0 CJK Ideograph Extension A-3EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec3 CJK Ideograph Extension A-3EC3 | 3ec2 CJK Ideograph Extension A-3EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec5 CJK Ideograph Extension A-3EC5 | 3ec4 CJK Ideograph Extension A-3EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec7 CJK Ideograph Extension A-3EC7 | 3ec6 CJK Ideograph Extension A-3EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ec9 CJK Ideograph Extension A-3EC9 | 3ec8 CJK Ideograph Extension A-3EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecb CJK Ideograph Extension A-3ECB | 3eca CJK Ideograph Extension A-3ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecd CJK Ideograph Extension A-3ECD | 3ecc CJK Ideograph Extension A-3ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ecf CJK Ideograph Extension A-3ECF | 3ece CJK Ideograph Extension A-3ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed1 CJK Ideograph Extension A-3ED1 | 3ed0 CJK Ideograph Extension A-3ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed3 CJK Ideograph Extension A-3ED3 | 3ed2 CJK Ideograph Extension A-3ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed5 CJK Ideograph Extension A-3ED5 | 3ed4 CJK Ideograph Extension A-3ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed7 CJK Ideograph Extension A-3ED7 | 3ed6 CJK Ideograph Extension A-3ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ed9 CJK Ideograph Extension A-3ED9 | 3ed8 CJK Ideograph Extension A-3ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edb CJK Ideograph Extension A-3EDB | 3eda CJK Ideograph Extension A-3EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edd CJK Ideograph Extension A-3EDD | 3edc CJK Ideograph Extension A-3EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3edf CJK Ideograph Extension A-3EDF | 3ede CJK Ideograph Extension A-3EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee1 CJK Ideograph Extension A-3EE1 | 3ee0 CJK Ideograph Extension A-3EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee3 CJK Ideograph Extension A-3EE3 | 3ee2 CJK Ideograph Extension A-3EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee5 CJK Ideograph Extension A-3EE5 | 3ee4 CJK Ideograph Extension A-3EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee7 CJK Ideograph Extension A-3EE7 | 3ee6 CJK Ideograph Extension A-3EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ee9 CJK Ideograph Extension A-3EE9 | 3ee8 CJK Ideograph Extension A-3EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eeb CJK Ideograph Extension A-3EEB | 3eea CJK Ideograph Extension A-3EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eed CJK Ideograph Extension A-3EED | 3eec CJK Ideograph Extension A-3EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eef CJK Ideograph Extension A-3EEF | 3eee CJK Ideograph Extension A-3EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef1 CJK Ideograph Extension A-3EF1 | 3ef0 CJK Ideograph Extension A-3EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef3 CJK Ideograph Extension A-3EF3 | 3ef2 CJK Ideograph Extension A-3EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef5 CJK Ideograph Extension A-3EF5 | 3ef4 CJK Ideograph Extension A-3EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef7 CJK Ideograph Extension A-3EF7 | 3ef6 CJK Ideograph Extension A-3EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ef9 CJK Ideograph Extension A-3EF9 | 3ef8 CJK Ideograph Extension A-3EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3efb CJK Ideograph Extension A-3EFB | 3efa CJK Ideograph Extension A-3EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3efd CJK Ideograph Extension A-3EFD | 3efc CJK Ideograph Extension A-3EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3eff CJK Ideograph Extension A-3EFF | 3efe CJK Ideograph Extension A-3EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f01 CJK Ideograph Extension A-3F01 | 3f00 CJK Ideograph Extension A-3F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f03 CJK Ideograph Extension A-3F03 | 3f02 CJK Ideograph Extension A-3F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f05 CJK Ideograph Extension A-3F05 | 3f04 CJK Ideograph Extension A-3F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f07 CJK Ideograph Extension A-3F07 | 3f06 CJK Ideograph Extension A-3F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f09 CJK Ideograph Extension A-3F09 | 3f08 CJK Ideograph Extension A-3F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0b CJK Ideograph Extension A-3F0B | 3f0a CJK Ideograph Extension A-3F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0d CJK Ideograph Extension A-3F0D | 3f0c CJK Ideograph Extension A-3F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f0f CJK Ideograph Extension A-3F0F | 3f0e CJK Ideograph Extension A-3F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f11 CJK Ideograph Extension A-3F11 | 3f10 CJK Ideograph Extension A-3F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f13 CJK Ideograph Extension A-3F13 | 3f12 CJK Ideograph Extension A-3F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f15 CJK Ideograph Extension A-3F15 | 3f14 CJK Ideograph Extension A-3F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f17 CJK Ideograph Extension A-3F17 | 3f16 CJK Ideograph Extension A-3F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f19 CJK Ideograph Extension A-3F19 | 3f18 CJK Ideograph Extension A-3F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1b CJK Ideograph Extension A-3F1B | 3f1a CJK Ideograph Extension A-3F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1d CJK Ideograph Extension A-3F1D | 3f1c CJK Ideograph Extension A-3F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f1f CJK Ideograph Extension A-3F1F | 3f1e CJK Ideograph Extension A-3F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f21 CJK Ideograph Extension A-3F21 | 3f20 CJK Ideograph Extension A-3F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f23 CJK Ideograph Extension A-3F23 | 3f22 CJK Ideograph Extension A-3F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f25 CJK Ideograph Extension A-3F25 | 3f24 CJK Ideograph Extension A-3F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f27 CJK Ideograph Extension A-3F27 | 3f26 CJK Ideograph Extension A-3F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f29 CJK Ideograph Extension A-3F29 | 3f28 CJK Ideograph Extension A-3F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2b CJK Ideograph Extension A-3F2B | 3f2a CJK Ideograph Extension A-3F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2d CJK Ideograph Extension A-3F2D | 3f2c CJK Ideograph Extension A-3F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f2f CJK Ideograph Extension A-3F2F | 3f2e CJK Ideograph Extension A-3F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f31 CJK Ideograph Extension A-3F31 | 3f30 CJK Ideograph Extension A-3F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f33 CJK Ideograph Extension A-3F33 | 3f32 CJK Ideograph Extension A-3F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f35 CJK Ideograph Extension A-3F35 | 3f34 CJK Ideograph Extension A-3F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f37 CJK Ideograph Extension A-3F37 | 3f36 CJK Ideograph Extension A-3F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f39 CJK Ideograph Extension A-3F39 | 3f38 CJK Ideograph Extension A-3F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3b CJK Ideograph Extension A-3F3B | 3f3a CJK Ideograph Extension A-3F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3d CJK Ideograph Extension A-3F3D | 3f3c CJK Ideograph Extension A-3F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f3f CJK Ideograph Extension A-3F3F | 3f3e CJK Ideograph Extension A-3F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f41 CJK Ideograph Extension A-3F41 | 3f40 CJK Ideograph Extension A-3F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f43 CJK Ideograph Extension A-3F43 | 3f42 CJK Ideograph Extension A-3F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f45 CJK Ideograph Extension A-3F45 | 3f44 CJK Ideograph Extension A-3F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f47 CJK Ideograph Extension A-3F47 | 3f46 CJK Ideograph Extension A-3F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f49 CJK Ideograph Extension A-3F49 | 3f48 CJK Ideograph Extension A-3F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4b CJK Ideograph Extension A-3F4B | 3f4a CJK Ideograph Extension A-3F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4d CJK Ideograph Extension A-3F4D | 3f4c CJK Ideograph Extension A-3F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f4f CJK Ideograph Extension A-3F4F | 3f4e CJK Ideograph Extension A-3F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f51 CJK Ideograph Extension A-3F51 | 3f50 CJK Ideograph Extension A-3F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f53 CJK Ideograph Extension A-3F53 | 3f52 CJK Ideograph Extension A-3F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f55 CJK Ideograph Extension A-3F55 | 3f54 CJK Ideograph Extension A-3F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f57 CJK Ideograph Extension A-3F57 | 3f56 CJK Ideograph Extension A-3F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f59 CJK Ideograph Extension A-3F59 | 3f58 CJK Ideograph Extension A-3F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5b CJK Ideograph Extension A-3F5B | 3f5a CJK Ideograph Extension A-3F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5d CJK Ideograph Extension A-3F5D | 3f5c CJK Ideograph Extension A-3F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f5f CJK Ideograph Extension A-3F5F | 3f5e CJK Ideograph Extension A-3F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f61 CJK Ideograph Extension A-3F61 | 3f60 CJK Ideograph Extension A-3F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f63 CJK Ideograph Extension A-3F63 | 3f62 CJK Ideograph Extension A-3F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f65 CJK Ideograph Extension A-3F65 | 3f64 CJK Ideograph Extension A-3F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f67 CJK Ideograph Extension A-3F67 | 3f66 CJK Ideograph Extension A-3F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f69 CJK Ideograph Extension A-3F69 | 3f68 CJK Ideograph Extension A-3F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6b CJK Ideograph Extension A-3F6B | 3f6a CJK Ideograph Extension A-3F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6d CJK Ideograph Extension A-3F6D | 3f6c CJK Ideograph Extension A-3F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f6f CJK Ideograph Extension A-3F6F | 3f6e CJK Ideograph Extension A-3F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f71 CJK Ideograph Extension A-3F71 | 3f70 CJK Ideograph Extension A-3F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f73 CJK Ideograph Extension A-3F73 | 3f72 CJK Ideograph Extension A-3F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f75 CJK Ideograph Extension A-3F75 | 3f74 CJK Ideograph Extension A-3F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f77 CJK Ideograph Extension A-3F77 | 3f76 CJK Ideograph Extension A-3F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f79 CJK Ideograph Extension A-3F79 | 3f78 CJK Ideograph Extension A-3F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7b CJK Ideograph Extension A-3F7B | 3f7a CJK Ideograph Extension A-3F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7d CJK Ideograph Extension A-3F7D | 3f7c CJK Ideograph Extension A-3F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f7f CJK Ideograph Extension A-3F7F | 3f7e CJK Ideograph Extension A-3F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f81 CJK Ideograph Extension A-3F81 | 3f80 CJK Ideograph Extension A-3F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f83 CJK Ideograph Extension A-3F83 | 3f82 CJK Ideograph Extension A-3F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f85 CJK Ideograph Extension A-3F85 | 3f84 CJK Ideograph Extension A-3F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f87 CJK Ideograph Extension A-3F87 | 3f86 CJK Ideograph Extension A-3F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f89 CJK Ideograph Extension A-3F89 | 3f88 CJK Ideograph Extension A-3F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8b CJK Ideograph Extension A-3F8B | 3f8a CJK Ideograph Extension A-3F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8d CJK Ideograph Extension A-3F8D | 3f8c CJK Ideograph Extension A-3F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f8f CJK Ideograph Extension A-3F8F | 3f8e CJK Ideograph Extension A-3F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f91 CJK Ideograph Extension A-3F91 | 3f90 CJK Ideograph Extension A-3F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f93 CJK Ideograph Extension A-3F93 | 3f92 CJK Ideograph Extension A-3F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f95 CJK Ideograph Extension A-3F95 | 3f94 CJK Ideograph Extension A-3F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f97 CJK Ideograph Extension A-3F97 | 3f96 CJK Ideograph Extension A-3F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f99 CJK Ideograph Extension A-3F99 | 3f98 CJK Ideograph Extension A-3F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9b CJK Ideograph Extension A-3F9B | 3f9a CJK Ideograph Extension A-3F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9d CJK Ideograph Extension A-3F9D | 3f9c CJK Ideograph Extension A-3F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3f9f CJK Ideograph Extension A-3F9F | 3f9e CJK Ideograph Extension A-3F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa1 CJK Ideograph Extension A-3FA1 | 3fa0 CJK Ideograph Extension A-3FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa3 CJK Ideograph Extension A-3FA3 | 3fa2 CJK Ideograph Extension A-3FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa5 CJK Ideograph Extension A-3FA5 | 3fa4 CJK Ideograph Extension A-3FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa7 CJK Ideograph Extension A-3FA7 | 3fa6 CJK Ideograph Extension A-3FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fa9 CJK Ideograph Extension A-3FA9 | 3fa8 CJK Ideograph Extension A-3FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fab CJK Ideograph Extension A-3FAB | 3faa CJK Ideograph Extension A-3FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fad CJK Ideograph Extension A-3FAD | 3fac CJK Ideograph Extension A-3FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3faf CJK Ideograph Extension A-3FAF | 3fae CJK Ideograph Extension A-3FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb1 CJK Ideograph Extension A-3FB1 | 3fb0 CJK Ideograph Extension A-3FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb3 CJK Ideograph Extension A-3FB3 | 3fb2 CJK Ideograph Extension A-3FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb5 CJK Ideograph Extension A-3FB5 | 3fb4 CJK Ideograph Extension A-3FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb7 CJK Ideograph Extension A-3FB7 | 3fb6 CJK Ideograph Extension A-3FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fb9 CJK Ideograph Extension A-3FB9 | 3fb8 CJK Ideograph Extension A-3FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbb CJK Ideograph Extension A-3FBB | 3fba CJK Ideograph Extension A-3FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbd CJK Ideograph Extension A-3FBD | 3fbc CJK Ideograph Extension A-3FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fbf CJK Ideograph Extension A-3FBF | 3fbe CJK Ideograph Extension A-3FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc1 CJK Ideograph Extension A-3FC1 | 3fc0 CJK Ideograph Extension A-3FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc3 CJK Ideograph Extension A-3FC3 | 3fc2 CJK Ideograph Extension A-3FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc5 CJK Ideograph Extension A-3FC5 | 3fc4 CJK Ideograph Extension A-3FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc7 CJK Ideograph Extension A-3FC7 | 3fc6 CJK Ideograph Extension A-3FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fc9 CJK Ideograph Extension A-3FC9 | 3fc8 CJK Ideograph Extension A-3FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcb CJK Ideograph Extension A-3FCB | 3fca CJK Ideograph Extension A-3FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcd CJK Ideograph Extension A-3FCD | 3fcc CJK Ideograph Extension A-3FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fcf CJK Ideograph Extension A-3FCF | 3fce CJK Ideograph Extension A-3FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd1 CJK Ideograph Extension A-3FD1 | 3fd0 CJK Ideograph Extension A-3FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd3 CJK Ideograph Extension A-3FD3 | 3fd2 CJK Ideograph Extension A-3FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd5 CJK Ideograph Extension A-3FD5 | 3fd4 CJK Ideograph Extension A-3FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd7 CJK Ideograph Extension A-3FD7 | 3fd6 CJK Ideograph Extension A-3FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fd9 CJK Ideograph Extension A-3FD9 | 3fd8 CJK Ideograph Extension A-3FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdb CJK Ideograph Extension A-3FDB | 3fda CJK Ideograph Extension A-3FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdd CJK Ideograph Extension A-3FDD | 3fdc CJK Ideograph Extension A-3FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fdf CJK Ideograph Extension A-3FDF | 3fde CJK Ideograph Extension A-3FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe1 CJK Ideograph Extension A-3FE1 | 3fe0 CJK Ideograph Extension A-3FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe3 CJK Ideograph Extension A-3FE3 | 3fe2 CJK Ideograph Extension A-3FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe5 CJK Ideograph Extension A-3FE5 | 3fe4 CJK Ideograph Extension A-3FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe7 CJK Ideograph Extension A-3FE7 | 3fe6 CJK Ideograph Extension A-3FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fe9 CJK Ideograph Extension A-3FE9 | 3fe8 CJK Ideograph Extension A-3FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3feb CJK Ideograph Extension A-3FEB | 3fea CJK Ideograph Extension A-3FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fed CJK Ideograph Extension A-3FED | 3fec CJK Ideograph Extension A-3FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fef CJK Ideograph Extension A-3FEF | 3fee CJK Ideograph Extension A-3FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff1 CJK Ideograph Extension A-3FF1 | 3ff0 CJK Ideograph Extension A-3FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff3 CJK Ideograph Extension A-3FF3 | 3ff2 CJK Ideograph Extension A-3FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff5 CJK Ideograph Extension A-3FF5 | 3ff4 CJK Ideograph Extension A-3FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff7 CJK Ideograph Extension A-3FF7 | 3ff6 CJK Ideograph Extension A-3FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ff9 CJK Ideograph Extension A-3FF9 | 3ff8 CJK Ideograph Extension A-3FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ffb CJK Ideograph Extension A-3FFB | 3ffa CJK Ideograph Extension A-3FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3ffd CJK Ideograph Extension A-3FFD | 3ffc CJK Ideograph Extension A-3FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 3fff CJK Ideograph Extension A-3FFF | 3ffe CJK Ideograph Extension A-3FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4001 CJK Ideograph Extension A-4001 | 4000 CJK Ideograph Extension A-4000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4003 CJK Ideograph Extension A-4003 | 4002 CJK Ideograph Extension A-4002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4005 CJK Ideograph Extension A-4005 | 4004 CJK Ideograph Extension A-4004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4007 CJK Ideograph Extension A-4007 | 4006 CJK Ideograph Extension A-4006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4009 CJK Ideograph Extension A-4009 | 4008 CJK Ideograph Extension A-4008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400b CJK Ideograph Extension A-400B | 400a CJK Ideograph Extension A-400A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400d CJK Ideograph Extension A-400D | 400c CJK Ideograph Extension A-400C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 400f CJK Ideograph Extension A-400F | 400e CJK Ideograph Extension A-400E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4011 CJK Ideograph Extension A-4011 | 4010 CJK Ideograph Extension A-4010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4013 CJK Ideograph Extension A-4013 | 4012 CJK Ideograph Extension A-4012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4015 CJK Ideograph Extension A-4015 | 4014 CJK Ideograph Extension A-4014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4017 CJK Ideograph Extension A-4017 | 4016 CJK Ideograph Extension A-4016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4019 CJK Ideograph Extension A-4019 | 4018 CJK Ideograph Extension A-4018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401b CJK Ideograph Extension A-401B | 401a CJK Ideograph Extension A-401A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401d CJK Ideograph Extension A-401D | 401c CJK Ideograph Extension A-401C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 401f CJK Ideograph Extension A-401F | 401e CJK Ideograph Extension A-401E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4021 CJK Ideograph Extension A-4021 | 4020 CJK Ideograph Extension A-4020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4023 CJK Ideograph Extension A-4023 | 4022 CJK Ideograph Extension A-4022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4025 CJK Ideograph Extension A-4025 | 4024 CJK Ideograph Extension A-4024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4027 CJK Ideograph Extension A-4027 | 4026 CJK Ideograph Extension A-4026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4029 CJK Ideograph Extension A-4029 | 4028 CJK Ideograph Extension A-4028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402b CJK Ideograph Extension A-402B | 402a CJK Ideograph Extension A-402A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402d CJK Ideograph Extension A-402D | 402c CJK Ideograph Extension A-402C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 402f CJK Ideograph Extension A-402F | 402e CJK Ideograph Extension A-402E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4031 CJK Ideograph Extension A-4031 | 4030 CJK Ideograph Extension A-4030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4033 CJK Ideograph Extension A-4033 | 4032 CJK Ideograph Extension A-4032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4035 CJK Ideograph Extension A-4035 | 4034 CJK Ideograph Extension A-4034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4037 CJK Ideograph Extension A-4037 | 4036 CJK Ideograph Extension A-4036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4039 CJK Ideograph Extension A-4039 | 4038 CJK Ideograph Extension A-4038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403b CJK Ideograph Extension A-403B | 403a CJK Ideograph Extension A-403A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403d CJK Ideograph Extension A-403D | 403c CJK Ideograph Extension A-403C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 403f CJK Ideograph Extension A-403F | 403e CJK Ideograph Extension A-403E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4041 CJK Ideograph Extension A-4041 | 4040 CJK Ideograph Extension A-4040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4043 CJK Ideograph Extension A-4043 | 4042 CJK Ideograph Extension A-4042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4045 CJK Ideograph Extension A-4045 | 4044 CJK Ideograph Extension A-4044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4047 CJK Ideograph Extension A-4047 | 4046 CJK Ideograph Extension A-4046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4049 CJK Ideograph Extension A-4049 | 4048 CJK Ideograph Extension A-4048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404b CJK Ideograph Extension A-404B | 404a CJK Ideograph Extension A-404A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404d CJK Ideograph Extension A-404D | 404c CJK Ideograph Extension A-404C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 404f CJK Ideograph Extension A-404F | 404e CJK Ideograph Extension A-404E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4051 CJK Ideograph Extension A-4051 | 4050 CJK Ideograph Extension A-4050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4053 CJK Ideograph Extension A-4053 | 4052 CJK Ideograph Extension A-4052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4055 CJK Ideograph Extension A-4055 | 4054 CJK Ideograph Extension A-4054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4057 CJK Ideograph Extension A-4057 | 4056 CJK Ideograph Extension A-4056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4059 CJK Ideograph Extension A-4059 | 4058 CJK Ideograph Extension A-4058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405b CJK Ideograph Extension A-405B | 405a CJK Ideograph Extension A-405A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405d CJK Ideograph Extension A-405D | 405c CJK Ideograph Extension A-405C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 405f CJK Ideograph Extension A-405F | 405e CJK Ideograph Extension A-405E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4061 CJK Ideograph Extension A-4061 | 4060 CJK Ideograph Extension A-4060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4063 CJK Ideograph Extension A-4063 | 4062 CJK Ideograph Extension A-4062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4065 CJK Ideograph Extension A-4065 | 4064 CJK Ideograph Extension A-4064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4067 CJK Ideograph Extension A-4067 | 4066 CJK Ideograph Extension A-4066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4069 CJK Ideograph Extension A-4069 | 4068 CJK Ideograph Extension A-4068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406b CJK Ideograph Extension A-406B | 406a CJK Ideograph Extension A-406A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406d CJK Ideograph Extension A-406D | 406c CJK Ideograph Extension A-406C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 406f CJK Ideograph Extension A-406F | 406e CJK Ideograph Extension A-406E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4071 CJK Ideograph Extension A-4071 | 4070 CJK Ideograph Extension A-4070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4073 CJK Ideograph Extension A-4073 | 4072 CJK Ideograph Extension A-4072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4075 CJK Ideograph Extension A-4075 | 4074 CJK Ideograph Extension A-4074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4077 CJK Ideograph Extension A-4077 | 4076 CJK Ideograph Extension A-4076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4079 CJK Ideograph Extension A-4079 | 4078 CJK Ideograph Extension A-4078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407b CJK Ideograph Extension A-407B | 407a CJK Ideograph Extension A-407A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407d CJK Ideograph Extension A-407D | 407c CJK Ideograph Extension A-407C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 407f CJK Ideograph Extension A-407F | 407e CJK Ideograph Extension A-407E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4081 CJK Ideograph Extension A-4081 | 4080 CJK Ideograph Extension A-4080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4083 CJK Ideograph Extension A-4083 | 4082 CJK Ideograph Extension A-4082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4085 CJK Ideograph Extension A-4085 | 4084 CJK Ideograph Extension A-4084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4087 CJK Ideograph Extension A-4087 | 4086 CJK Ideograph Extension A-4086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4089 CJK Ideograph Extension A-4089 | 4088 CJK Ideograph Extension A-4088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408b CJK Ideograph Extension A-408B | 408a CJK Ideograph Extension A-408A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408d CJK Ideograph Extension A-408D | 408c CJK Ideograph Extension A-408C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 408f CJK Ideograph Extension A-408F | 408e CJK Ideograph Extension A-408E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4091 CJK Ideograph Extension A-4091 | 4090 CJK Ideograph Extension A-4090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4093 CJK Ideograph Extension A-4093 | 4092 CJK Ideograph Extension A-4092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4095 CJK Ideograph Extension A-4095 | 4094 CJK Ideograph Extension A-4094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4097 CJK Ideograph Extension A-4097 | 4096 CJK Ideograph Extension A-4096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4099 CJK Ideograph Extension A-4099 | 4098 CJK Ideograph Extension A-4098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409b CJK Ideograph Extension A-409B | 409a CJK Ideograph Extension A-409A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409d CJK Ideograph Extension A-409D | 409c CJK Ideograph Extension A-409C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 409f CJK Ideograph Extension A-409F | 409e CJK Ideograph Extension A-409E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a1 CJK Ideograph Extension A-40A1 | 40a0 CJK Ideograph Extension A-40A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a3 CJK Ideograph Extension A-40A3 | 40a2 CJK Ideograph Extension A-40A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a5 CJK Ideograph Extension A-40A5 | 40a4 CJK Ideograph Extension A-40A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a7 CJK Ideograph Extension A-40A7 | 40a6 CJK Ideograph Extension A-40A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40a9 CJK Ideograph Extension A-40A9 | 40a8 CJK Ideograph Extension A-40A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ab CJK Ideograph Extension A-40AB | 40aa CJK Ideograph Extension A-40AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ad CJK Ideograph Extension A-40AD | 40ac CJK Ideograph Extension A-40AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40af CJK Ideograph Extension A-40AF | 40ae CJK Ideograph Extension A-40AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b1 CJK Ideograph Extension A-40B1 | 40b0 CJK Ideograph Extension A-40B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b3 CJK Ideograph Extension A-40B3 | 40b2 CJK Ideograph Extension A-40B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b5 CJK Ideograph Extension A-40B5 | 40b4 CJK Ideograph Extension A-40B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b7 CJK Ideograph Extension A-40B7 | 40b6 CJK Ideograph Extension A-40B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40b9 CJK Ideograph Extension A-40B9 | 40b8 CJK Ideograph Extension A-40B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bb CJK Ideograph Extension A-40BB | 40ba CJK Ideograph Extension A-40BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bd CJK Ideograph Extension A-40BD | 40bc CJK Ideograph Extension A-40BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40bf CJK Ideograph Extension A-40BF | 40be CJK Ideograph Extension A-40BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c1 CJK Ideograph Extension A-40C1 | 40c0 CJK Ideograph Extension A-40C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c3 CJK Ideograph Extension A-40C3 | 40c2 CJK Ideograph Extension A-40C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c5 CJK Ideograph Extension A-40C5 | 40c4 CJK Ideograph Extension A-40C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c7 CJK Ideograph Extension A-40C7 | 40c6 CJK Ideograph Extension A-40C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40c9 CJK Ideograph Extension A-40C9 | 40c8 CJK Ideograph Extension A-40C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cb CJK Ideograph Extension A-40CB | 40ca CJK Ideograph Extension A-40CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cd CJK Ideograph Extension A-40CD | 40cc CJK Ideograph Extension A-40CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40cf CJK Ideograph Extension A-40CF | 40ce CJK Ideograph Extension A-40CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d1 CJK Ideograph Extension A-40D1 | 40d0 CJK Ideograph Extension A-40D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d3 CJK Ideograph Extension A-40D3 | 40d2 CJK Ideograph Extension A-40D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d5 CJK Ideograph Extension A-40D5 | 40d4 CJK Ideograph Extension A-40D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d7 CJK Ideograph Extension A-40D7 | 40d6 CJK Ideograph Extension A-40D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40d9 CJK Ideograph Extension A-40D9 | 40d8 CJK Ideograph Extension A-40D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40db CJK Ideograph Extension A-40DB | 40da CJK Ideograph Extension A-40DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40dd CJK Ideograph Extension A-40DD | 40dc CJK Ideograph Extension A-40DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40df CJK Ideograph Extension A-40DF | 40de CJK Ideograph Extension A-40DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e1 CJK Ideograph Extension A-40E1 | 40e0 CJK Ideograph Extension A-40E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e3 CJK Ideograph Extension A-40E3 | 40e2 CJK Ideograph Extension A-40E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e5 CJK Ideograph Extension A-40E5 | 40e4 CJK Ideograph Extension A-40E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e7 CJK Ideograph Extension A-40E7 | 40e6 CJK Ideograph Extension A-40E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40e9 CJK Ideograph Extension A-40E9 | 40e8 CJK Ideograph Extension A-40E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40eb CJK Ideograph Extension A-40EB | 40ea CJK Ideograph Extension A-40EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ed CJK Ideograph Extension A-40ED | 40ec CJK Ideograph Extension A-40EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ef CJK Ideograph Extension A-40EF | 40ee CJK Ideograph Extension A-40EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f1 CJK Ideograph Extension A-40F1 | 40f0 CJK Ideograph Extension A-40F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f3 CJK Ideograph Extension A-40F3 | 40f2 CJK Ideograph Extension A-40F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f5 CJK Ideograph Extension A-40F5 | 40f4 CJK Ideograph Extension A-40F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f7 CJK Ideograph Extension A-40F7 | 40f6 CJK Ideograph Extension A-40F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40f9 CJK Ideograph Extension A-40F9 | 40f8 CJK Ideograph Extension A-40F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40fb CJK Ideograph Extension A-40FB | 40fa CJK Ideograph Extension A-40FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40fd CJK Ideograph Extension A-40FD | 40fc CJK Ideograph Extension A-40FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 40ff CJK Ideograph Extension A-40FF | 40fe CJK Ideograph Extension A-40FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4101 CJK Ideograph Extension A-4101 | 4100 CJK Ideograph Extension A-4100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4103 CJK Ideograph Extension A-4103 | 4102 CJK Ideograph Extension A-4102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4105 CJK Ideograph Extension A-4105 | 4104 CJK Ideograph Extension A-4104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4107 CJK Ideograph Extension A-4107 | 4106 CJK Ideograph Extension A-4106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4109 CJK Ideograph Extension A-4109 | 4108 CJK Ideograph Extension A-4108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410b CJK Ideograph Extension A-410B | 410a CJK Ideograph Extension A-410A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410d CJK Ideograph Extension A-410D | 410c CJK Ideograph Extension A-410C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 410f CJK Ideograph Extension A-410F | 410e CJK Ideograph Extension A-410E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4111 CJK Ideograph Extension A-4111 | 4110 CJK Ideograph Extension A-4110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4113 CJK Ideograph Extension A-4113 | 4112 CJK Ideograph Extension A-4112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4115 CJK Ideograph Extension A-4115 | 4114 CJK Ideograph Extension A-4114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4117 CJK Ideograph Extension A-4117 | 4116 CJK Ideograph Extension A-4116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4119 CJK Ideograph Extension A-4119 | 4118 CJK Ideograph Extension A-4118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411b CJK Ideograph Extension A-411B | 411a CJK Ideograph Extension A-411A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411d CJK Ideograph Extension A-411D | 411c CJK Ideograph Extension A-411C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 411f CJK Ideograph Extension A-411F | 411e CJK Ideograph Extension A-411E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4121 CJK Ideograph Extension A-4121 | 4120 CJK Ideograph Extension A-4120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4123 CJK Ideograph Extension A-4123 | 4122 CJK Ideograph Extension A-4122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4125 CJK Ideograph Extension A-4125 | 4124 CJK Ideograph Extension A-4124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4127 CJK Ideograph Extension A-4127 | 4126 CJK Ideograph Extension A-4126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4129 CJK Ideograph Extension A-4129 | 4128 CJK Ideograph Extension A-4128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412b CJK Ideograph Extension A-412B | 412a CJK Ideograph Extension A-412A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412d CJK Ideograph Extension A-412D | 412c CJK Ideograph Extension A-412C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 412f CJK Ideograph Extension A-412F | 412e CJK Ideograph Extension A-412E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4131 CJK Ideograph Extension A-4131 | 4130 CJK Ideograph Extension A-4130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4133 CJK Ideograph Extension A-4133 | 4132 CJK Ideograph Extension A-4132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4135 CJK Ideograph Extension A-4135 | 4134 CJK Ideograph Extension A-4134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4137 CJK Ideograph Extension A-4137 | 4136 CJK Ideograph Extension A-4136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4139 CJK Ideograph Extension A-4139 | 4138 CJK Ideograph Extension A-4138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413b CJK Ideograph Extension A-413B | 413a CJK Ideograph Extension A-413A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413d CJK Ideograph Extension A-413D | 413c CJK Ideograph Extension A-413C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 413f CJK Ideograph Extension A-413F | 413e CJK Ideograph Extension A-413E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4141 CJK Ideograph Extension A-4141 | 4140 CJK Ideograph Extension A-4140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4143 CJK Ideograph Extension A-4143 | 4142 CJK Ideograph Extension A-4142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4145 CJK Ideograph Extension A-4145 | 4144 CJK Ideograph Extension A-4144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4147 CJK Ideograph Extension A-4147 | 4146 CJK Ideograph Extension A-4146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4149 CJK Ideograph Extension A-4149 | 4148 CJK Ideograph Extension A-4148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414b CJK Ideograph Extension A-414B | 414a CJK Ideograph Extension A-414A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414d CJK Ideograph Extension A-414D | 414c CJK Ideograph Extension A-414C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 414f CJK Ideograph Extension A-414F | 414e CJK Ideograph Extension A-414E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4151 CJK Ideograph Extension A-4151 | 4150 CJK Ideograph Extension A-4150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4153 CJK Ideograph Extension A-4153 | 4152 CJK Ideograph Extension A-4152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4155 CJK Ideograph Extension A-4155 | 4154 CJK Ideograph Extension A-4154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4157 CJK Ideograph Extension A-4157 | 4156 CJK Ideograph Extension A-4156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4159 CJK Ideograph Extension A-4159 | 4158 CJK Ideograph Extension A-4158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415b CJK Ideograph Extension A-415B | 415a CJK Ideograph Extension A-415A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415d CJK Ideograph Extension A-415D | 415c CJK Ideograph Extension A-415C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 415f CJK Ideograph Extension A-415F | 415e CJK Ideograph Extension A-415E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4161 CJK Ideograph Extension A-4161 | 4160 CJK Ideograph Extension A-4160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4163 CJK Ideograph Extension A-4163 | 4162 CJK Ideograph Extension A-4162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4165 CJK Ideograph Extension A-4165 | 4164 CJK Ideograph Extension A-4164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4167 CJK Ideograph Extension A-4167 | 4166 CJK Ideograph Extension A-4166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4169 CJK Ideograph Extension A-4169 | 4168 CJK Ideograph Extension A-4168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416b CJK Ideograph Extension A-416B | 416a CJK Ideograph Extension A-416A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416d CJK Ideograph Extension A-416D | 416c CJK Ideograph Extension A-416C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 416f CJK Ideograph Extension A-416F | 416e CJK Ideograph Extension A-416E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4171 CJK Ideograph Extension A-4171 | 4170 CJK Ideograph Extension A-4170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4173 CJK Ideograph Extension A-4173 | 4172 CJK Ideograph Extension A-4172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4175 CJK Ideograph Extension A-4175 | 4174 CJK Ideograph Extension A-4174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4177 CJK Ideograph Extension A-4177 | 4176 CJK Ideograph Extension A-4176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4179 CJK Ideograph Extension A-4179 | 4178 CJK Ideograph Extension A-4178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417b CJK Ideograph Extension A-417B | 417a CJK Ideograph Extension A-417A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417d CJK Ideograph Extension A-417D | 417c CJK Ideograph Extension A-417C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 417f CJK Ideograph Extension A-417F | 417e CJK Ideograph Extension A-417E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4181 CJK Ideograph Extension A-4181 | 4180 CJK Ideograph Extension A-4180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4183 CJK Ideograph Extension A-4183 | 4182 CJK Ideograph Extension A-4182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4185 CJK Ideograph Extension A-4185 | 4184 CJK Ideograph Extension A-4184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4187 CJK Ideograph Extension A-4187 | 4186 CJK Ideograph Extension A-4186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4189 CJK Ideograph Extension A-4189 | 4188 CJK Ideograph Extension A-4188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418b CJK Ideograph Extension A-418B | 418a CJK Ideograph Extension A-418A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418d CJK Ideograph Extension A-418D | 418c CJK Ideograph Extension A-418C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 418f CJK Ideograph Extension A-418F | 418e CJK Ideograph Extension A-418E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4191 CJK Ideograph Extension A-4191 | 4190 CJK Ideograph Extension A-4190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4193 CJK Ideograph Extension A-4193 | 4192 CJK Ideograph Extension A-4192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4195 CJK Ideograph Extension A-4195 | 4194 CJK Ideograph Extension A-4194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4197 CJK Ideograph Extension A-4197 | 4196 CJK Ideograph Extension A-4196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4199 CJK Ideograph Extension A-4199 | 4198 CJK Ideograph Extension A-4198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419b CJK Ideograph Extension A-419B | 419a CJK Ideograph Extension A-419A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419d CJK Ideograph Extension A-419D | 419c CJK Ideograph Extension A-419C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 419f CJK Ideograph Extension A-419F | 419e CJK Ideograph Extension A-419E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a1 CJK Ideograph Extension A-41A1 | 41a0 CJK Ideograph Extension A-41A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a3 CJK Ideograph Extension A-41A3 | 41a2 CJK Ideograph Extension A-41A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a5 CJK Ideograph Extension A-41A5 | 41a4 CJK Ideograph Extension A-41A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a7 CJK Ideograph Extension A-41A7 | 41a6 CJK Ideograph Extension A-41A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41a9 CJK Ideograph Extension A-41A9 | 41a8 CJK Ideograph Extension A-41A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ab CJK Ideograph Extension A-41AB | 41aa CJK Ideograph Extension A-41AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ad CJK Ideograph Extension A-41AD | 41ac CJK Ideograph Extension A-41AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41af CJK Ideograph Extension A-41AF | 41ae CJK Ideograph Extension A-41AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b1 CJK Ideograph Extension A-41B1 | 41b0 CJK Ideograph Extension A-41B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b3 CJK Ideograph Extension A-41B3 | 41b2 CJK Ideograph Extension A-41B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b5 CJK Ideograph Extension A-41B5 | 41b4 CJK Ideograph Extension A-41B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b7 CJK Ideograph Extension A-41B7 | 41b6 CJK Ideograph Extension A-41B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41b9 CJK Ideograph Extension A-41B9 | 41b8 CJK Ideograph Extension A-41B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bb CJK Ideograph Extension A-41BB | 41ba CJK Ideograph Extension A-41BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bd CJK Ideograph Extension A-41BD | 41bc CJK Ideograph Extension A-41BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41bf CJK Ideograph Extension A-41BF | 41be CJK Ideograph Extension A-41BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c1 CJK Ideograph Extension A-41C1 | 41c0 CJK Ideograph Extension A-41C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c3 CJK Ideograph Extension A-41C3 | 41c2 CJK Ideograph Extension A-41C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c5 CJK Ideograph Extension A-41C5 | 41c4 CJK Ideograph Extension A-41C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c7 CJK Ideograph Extension A-41C7 | 41c6 CJK Ideograph Extension A-41C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41c9 CJK Ideograph Extension A-41C9 | 41c8 CJK Ideograph Extension A-41C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cb CJK Ideograph Extension A-41CB | 41ca CJK Ideograph Extension A-41CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cd CJK Ideograph Extension A-41CD | 41cc CJK Ideograph Extension A-41CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41cf CJK Ideograph Extension A-41CF | 41ce CJK Ideograph Extension A-41CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d1 CJK Ideograph Extension A-41D1 | 41d0 CJK Ideograph Extension A-41D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d3 CJK Ideograph Extension A-41D3 | 41d2 CJK Ideograph Extension A-41D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d5 CJK Ideograph Extension A-41D5 | 41d4 CJK Ideograph Extension A-41D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d7 CJK Ideograph Extension A-41D7 | 41d6 CJK Ideograph Extension A-41D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41d9 CJK Ideograph Extension A-41D9 | 41d8 CJK Ideograph Extension A-41D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41db CJK Ideograph Extension A-41DB | 41da CJK Ideograph Extension A-41DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41dd CJK Ideograph Extension A-41DD | 41dc CJK Ideograph Extension A-41DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41df CJK Ideograph Extension A-41DF | 41de CJK Ideograph Extension A-41DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e1 CJK Ideograph Extension A-41E1 | 41e0 CJK Ideograph Extension A-41E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e3 CJK Ideograph Extension A-41E3 | 41e2 CJK Ideograph Extension A-41E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e5 CJK Ideograph Extension A-41E5 | 41e4 CJK Ideograph Extension A-41E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e7 CJK Ideograph Extension A-41E7 | 41e6 CJK Ideograph Extension A-41E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41e9 CJK Ideograph Extension A-41E9 | 41e8 CJK Ideograph Extension A-41E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41eb CJK Ideograph Extension A-41EB | 41ea CJK Ideograph Extension A-41EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ed CJK Ideograph Extension A-41ED | 41ec CJK Ideograph Extension A-41EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ef CJK Ideograph Extension A-41EF | 41ee CJK Ideograph Extension A-41EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f1 CJK Ideograph Extension A-41F1 | 41f0 CJK Ideograph Extension A-41F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f3 CJK Ideograph Extension A-41F3 | 41f2 CJK Ideograph Extension A-41F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f5 CJK Ideograph Extension A-41F5 | 41f4 CJK Ideograph Extension A-41F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f7 CJK Ideograph Extension A-41F7 | 41f6 CJK Ideograph Extension A-41F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41f9 CJK Ideograph Extension A-41F9 | 41f8 CJK Ideograph Extension A-41F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41fb CJK Ideograph Extension A-41FB | 41fa CJK Ideograph Extension A-41FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41fd CJK Ideograph Extension A-41FD | 41fc CJK Ideograph Extension A-41FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 41ff CJK Ideograph Extension A-41FF | 41fe CJK Ideograph Extension A-41FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4201 CJK Ideograph Extension A-4201 | 4200 CJK Ideograph Extension A-4200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4203 CJK Ideograph Extension A-4203 | 4202 CJK Ideograph Extension A-4202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4205 CJK Ideograph Extension A-4205 | 4204 CJK Ideograph Extension A-4204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4207 CJK Ideograph Extension A-4207 | 4206 CJK Ideograph Extension A-4206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4209 CJK Ideograph Extension A-4209 | 4208 CJK Ideograph Extension A-4208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420b CJK Ideograph Extension A-420B | 420a CJK Ideograph Extension A-420A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420d CJK Ideograph Extension A-420D | 420c CJK Ideograph Extension A-420C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 420f CJK Ideograph Extension A-420F | 420e CJK Ideograph Extension A-420E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4211 CJK Ideograph Extension A-4211 | 4210 CJK Ideograph Extension A-4210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4213 CJK Ideograph Extension A-4213 | 4212 CJK Ideograph Extension A-4212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4215 CJK Ideograph Extension A-4215 | 4214 CJK Ideograph Extension A-4214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4217 CJK Ideograph Extension A-4217 | 4216 CJK Ideograph Extension A-4216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4219 CJK Ideograph Extension A-4219 | 4218 CJK Ideograph Extension A-4218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421b CJK Ideograph Extension A-421B | 421a CJK Ideograph Extension A-421A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421d CJK Ideograph Extension A-421D | 421c CJK Ideograph Extension A-421C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 421f CJK Ideograph Extension A-421F | 421e CJK Ideograph Extension A-421E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4221 CJK Ideograph Extension A-4221 | 4220 CJK Ideograph Extension A-4220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4223 CJK Ideograph Extension A-4223 | 4222 CJK Ideograph Extension A-4222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4225 CJK Ideograph Extension A-4225 | 4224 CJK Ideograph Extension A-4224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4227 CJK Ideograph Extension A-4227 | 4226 CJK Ideograph Extension A-4226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4229 CJK Ideograph Extension A-4229 | 4228 CJK Ideograph Extension A-4228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422b CJK Ideograph Extension A-422B | 422a CJK Ideograph Extension A-422A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422d CJK Ideograph Extension A-422D | 422c CJK Ideograph Extension A-422C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 422f CJK Ideograph Extension A-422F | 422e CJK Ideograph Extension A-422E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4231 CJK Ideograph Extension A-4231 | 4230 CJK Ideograph Extension A-4230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4233 CJK Ideograph Extension A-4233 | 4232 CJK Ideograph Extension A-4232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4235 CJK Ideograph Extension A-4235 | 4234 CJK Ideograph Extension A-4234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4237 CJK Ideograph Extension A-4237 | 4236 CJK Ideograph Extension A-4236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4239 CJK Ideograph Extension A-4239 | 4238 CJK Ideograph Extension A-4238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423b CJK Ideograph Extension A-423B | 423a CJK Ideograph Extension A-423A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423d CJK Ideograph Extension A-423D | 423c CJK Ideograph Extension A-423C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 423f CJK Ideograph Extension A-423F | 423e CJK Ideograph Extension A-423E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4241 CJK Ideograph Extension A-4241 | 4240 CJK Ideograph Extension A-4240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4243 CJK Ideograph Extension A-4243 | 4242 CJK Ideograph Extension A-4242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4245 CJK Ideograph Extension A-4245 | 4244 CJK Ideograph Extension A-4244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4247 CJK Ideograph Extension A-4247 | 4246 CJK Ideograph Extension A-4246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4249 CJK Ideograph Extension A-4249 | 4248 CJK Ideograph Extension A-4248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424b CJK Ideograph Extension A-424B | 424a CJK Ideograph Extension A-424A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424d CJK Ideograph Extension A-424D | 424c CJK Ideograph Extension A-424C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 424f CJK Ideograph Extension A-424F | 424e CJK Ideograph Extension A-424E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4251 CJK Ideograph Extension A-4251 | 4250 CJK Ideograph Extension A-4250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4253 CJK Ideograph Extension A-4253 | 4252 CJK Ideograph Extension A-4252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4255 CJK Ideograph Extension A-4255 | 4254 CJK Ideograph Extension A-4254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4257 CJK Ideograph Extension A-4257 | 4256 CJK Ideograph Extension A-4256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4259 CJK Ideograph Extension A-4259 | 4258 CJK Ideograph Extension A-4258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425b CJK Ideograph Extension A-425B | 425a CJK Ideograph Extension A-425A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425d CJK Ideograph Extension A-425D | 425c CJK Ideograph Extension A-425C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 425f CJK Ideograph Extension A-425F | 425e CJK Ideograph Extension A-425E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4261 CJK Ideograph Extension A-4261 | 4260 CJK Ideograph Extension A-4260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4263 CJK Ideograph Extension A-4263 | 4262 CJK Ideograph Extension A-4262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4265 CJK Ideograph Extension A-4265 | 4264 CJK Ideograph Extension A-4264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4267 CJK Ideograph Extension A-4267 | 4266 CJK Ideograph Extension A-4266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4269 CJK Ideograph Extension A-4269 | 4268 CJK Ideograph Extension A-4268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426b CJK Ideograph Extension A-426B | 426a CJK Ideograph Extension A-426A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426d CJK Ideograph Extension A-426D | 426c CJK Ideograph Extension A-426C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 426f CJK Ideograph Extension A-426F | 426e CJK Ideograph Extension A-426E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4271 CJK Ideograph Extension A-4271 | 4270 CJK Ideograph Extension A-4270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4273 CJK Ideograph Extension A-4273 | 4272 CJK Ideograph Extension A-4272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4275 CJK Ideograph Extension A-4275 | 4274 CJK Ideograph Extension A-4274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4277 CJK Ideograph Extension A-4277 | 4276 CJK Ideograph Extension A-4276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4279 CJK Ideograph Extension A-4279 | 4278 CJK Ideograph Extension A-4278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427b CJK Ideograph Extension A-427B | 427a CJK Ideograph Extension A-427A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427d CJK Ideograph Extension A-427D | 427c CJK Ideograph Extension A-427C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 427f CJK Ideograph Extension A-427F | 427e CJK Ideograph Extension A-427E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4281 CJK Ideograph Extension A-4281 | 4280 CJK Ideograph Extension A-4280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4283 CJK Ideograph Extension A-4283 | 4282 CJK Ideograph Extension A-4282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4285 CJK Ideograph Extension A-4285 | 4284 CJK Ideograph Extension A-4284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4287 CJK Ideograph Extension A-4287 | 4286 CJK Ideograph Extension A-4286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4289 CJK Ideograph Extension A-4289 | 4288 CJK Ideograph Extension A-4288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428b CJK Ideograph Extension A-428B | 428a CJK Ideograph Extension A-428A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428d CJK Ideograph Extension A-428D | 428c CJK Ideograph Extension A-428C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 428f CJK Ideograph Extension A-428F | 428e CJK Ideograph Extension A-428E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4291 CJK Ideograph Extension A-4291 | 4290 CJK Ideograph Extension A-4290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4293 CJK Ideograph Extension A-4293 | 4292 CJK Ideograph Extension A-4292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4295 CJK Ideograph Extension A-4295 | 4294 CJK Ideograph Extension A-4294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4297 CJK Ideograph Extension A-4297 | 4296 CJK Ideograph Extension A-4296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4299 CJK Ideograph Extension A-4299 | 4298 CJK Ideograph Extension A-4298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429b CJK Ideograph Extension A-429B | 429a CJK Ideograph Extension A-429A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429d CJK Ideograph Extension A-429D | 429c CJK Ideograph Extension A-429C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 429f CJK Ideograph Extension A-429F | 429e CJK Ideograph Extension A-429E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a1 CJK Ideograph Extension A-42A1 | 42a0 CJK Ideograph Extension A-42A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a3 CJK Ideograph Extension A-42A3 | 42a2 CJK Ideograph Extension A-42A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a5 CJK Ideograph Extension A-42A5 | 42a4 CJK Ideograph Extension A-42A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a7 CJK Ideograph Extension A-42A7 | 42a6 CJK Ideograph Extension A-42A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42a9 CJK Ideograph Extension A-42A9 | 42a8 CJK Ideograph Extension A-42A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ab CJK Ideograph Extension A-42AB | 42aa CJK Ideograph Extension A-42AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ad CJK Ideograph Extension A-42AD | 42ac CJK Ideograph Extension A-42AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42af CJK Ideograph Extension A-42AF | 42ae CJK Ideograph Extension A-42AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b1 CJK Ideograph Extension A-42B1 | 42b0 CJK Ideograph Extension A-42B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b3 CJK Ideograph Extension A-42B3 | 42b2 CJK Ideograph Extension A-42B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b5 CJK Ideograph Extension A-42B5 | 42b4 CJK Ideograph Extension A-42B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b7 CJK Ideograph Extension A-42B7 | 42b6 CJK Ideograph Extension A-42B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42b9 CJK Ideograph Extension A-42B9 | 42b8 CJK Ideograph Extension A-42B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bb CJK Ideograph Extension A-42BB | 42ba CJK Ideograph Extension A-42BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bd CJK Ideograph Extension A-42BD | 42bc CJK Ideograph Extension A-42BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42bf CJK Ideograph Extension A-42BF | 42be CJK Ideograph Extension A-42BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c1 CJK Ideograph Extension A-42C1 | 42c0 CJK Ideograph Extension A-42C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c3 CJK Ideograph Extension A-42C3 | 42c2 CJK Ideograph Extension A-42C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c5 CJK Ideograph Extension A-42C5 | 42c4 CJK Ideograph Extension A-42C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c7 CJK Ideograph Extension A-42C7 | 42c6 CJK Ideograph Extension A-42C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42c9 CJK Ideograph Extension A-42C9 | 42c8 CJK Ideograph Extension A-42C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cb CJK Ideograph Extension A-42CB | 42ca CJK Ideograph Extension A-42CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cd CJK Ideograph Extension A-42CD | 42cc CJK Ideograph Extension A-42CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42cf CJK Ideograph Extension A-42CF | 42ce CJK Ideograph Extension A-42CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d1 CJK Ideograph Extension A-42D1 | 42d0 CJK Ideograph Extension A-42D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d3 CJK Ideograph Extension A-42D3 | 42d2 CJK Ideograph Extension A-42D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d5 CJK Ideograph Extension A-42D5 | 42d4 CJK Ideograph Extension A-42D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d7 CJK Ideograph Extension A-42D7 | 42d6 CJK Ideograph Extension A-42D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42d9 CJK Ideograph Extension A-42D9 | 42d8 CJK Ideograph Extension A-42D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42db CJK Ideograph Extension A-42DB | 42da CJK Ideograph Extension A-42DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42dd CJK Ideograph Extension A-42DD | 42dc CJK Ideograph Extension A-42DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42df CJK Ideograph Extension A-42DF | 42de CJK Ideograph Extension A-42DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e1 CJK Ideograph Extension A-42E1 | 42e0 CJK Ideograph Extension A-42E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e3 CJK Ideograph Extension A-42E3 | 42e2 CJK Ideograph Extension A-42E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e5 CJK Ideograph Extension A-42E5 | 42e4 CJK Ideograph Extension A-42E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e7 CJK Ideograph Extension A-42E7 | 42e6 CJK Ideograph Extension A-42E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42e9 CJK Ideograph Extension A-42E9 | 42e8 CJK Ideograph Extension A-42E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42eb CJK Ideograph Extension A-42EB | 42ea CJK Ideograph Extension A-42EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ed CJK Ideograph Extension A-42ED | 42ec CJK Ideograph Extension A-42EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ef CJK Ideograph Extension A-42EF | 42ee CJK Ideograph Extension A-42EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f1 CJK Ideograph Extension A-42F1 | 42f0 CJK Ideograph Extension A-42F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f3 CJK Ideograph Extension A-42F3 | 42f2 CJK Ideograph Extension A-42F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f5 CJK Ideograph Extension A-42F5 | 42f4 CJK Ideograph Extension A-42F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f7 CJK Ideograph Extension A-42F7 | 42f6 CJK Ideograph Extension A-42F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42f9 CJK Ideograph Extension A-42F9 | 42f8 CJK Ideograph Extension A-42F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42fb CJK Ideograph Extension A-42FB | 42fa CJK Ideograph Extension A-42FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42fd CJK Ideograph Extension A-42FD | 42fc CJK Ideograph Extension A-42FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 42ff CJK Ideograph Extension A-42FF | 42fe CJK Ideograph Extension A-42FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4301 CJK Ideograph Extension A-4301 | 4300 CJK Ideograph Extension A-4300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4303 CJK Ideograph Extension A-4303 | 4302 CJK Ideograph Extension A-4302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4305 CJK Ideograph Extension A-4305 | 4304 CJK Ideograph Extension A-4304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4307 CJK Ideograph Extension A-4307 | 4306 CJK Ideograph Extension A-4306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4309 CJK Ideograph Extension A-4309 | 4308 CJK Ideograph Extension A-4308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430b CJK Ideograph Extension A-430B | 430a CJK Ideograph Extension A-430A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430d CJK Ideograph Extension A-430D | 430c CJK Ideograph Extension A-430C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 430f CJK Ideograph Extension A-430F | 430e CJK Ideograph Extension A-430E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4311 CJK Ideograph Extension A-4311 | 4310 CJK Ideograph Extension A-4310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4313 CJK Ideograph Extension A-4313 | 4312 CJK Ideograph Extension A-4312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4315 CJK Ideograph Extension A-4315 | 4314 CJK Ideograph Extension A-4314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4317 CJK Ideograph Extension A-4317 | 4316 CJK Ideograph Extension A-4316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4319 CJK Ideograph Extension A-4319 | 4318 CJK Ideograph Extension A-4318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431b CJK Ideograph Extension A-431B | 431a CJK Ideograph Extension A-431A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431d CJK Ideograph Extension A-431D | 431c CJK Ideograph Extension A-431C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 431f CJK Ideograph Extension A-431F | 431e CJK Ideograph Extension A-431E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4321 CJK Ideograph Extension A-4321 | 4320 CJK Ideograph Extension A-4320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4323 CJK Ideograph Extension A-4323 | 4322 CJK Ideograph Extension A-4322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4325 CJK Ideograph Extension A-4325 | 4324 CJK Ideograph Extension A-4324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4327 CJK Ideograph Extension A-4327 | 4326 CJK Ideograph Extension A-4326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4329 CJK Ideograph Extension A-4329 | 4328 CJK Ideograph Extension A-4328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432b CJK Ideograph Extension A-432B | 432a CJK Ideograph Extension A-432A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432d CJK Ideograph Extension A-432D | 432c CJK Ideograph Extension A-432C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 432f CJK Ideograph Extension A-432F | 432e CJK Ideograph Extension A-432E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4331 CJK Ideograph Extension A-4331 | 4330 CJK Ideograph Extension A-4330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4333 CJK Ideograph Extension A-4333 | 4332 CJK Ideograph Extension A-4332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4335 CJK Ideograph Extension A-4335 | 4334 CJK Ideograph Extension A-4334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4337 CJK Ideograph Extension A-4337 | 4336 CJK Ideograph Extension A-4336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4339 CJK Ideograph Extension A-4339 | 4338 CJK Ideograph Extension A-4338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433b CJK Ideograph Extension A-433B | 433a CJK Ideograph Extension A-433A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433d CJK Ideograph Extension A-433D | 433c CJK Ideograph Extension A-433C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 433f CJK Ideograph Extension A-433F | 433e CJK Ideograph Extension A-433E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4341 CJK Ideograph Extension A-4341 | 4340 CJK Ideograph Extension A-4340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4343 CJK Ideograph Extension A-4343 | 4342 CJK Ideograph Extension A-4342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4345 CJK Ideograph Extension A-4345 | 4344 CJK Ideograph Extension A-4344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4347 CJK Ideograph Extension A-4347 | 4346 CJK Ideograph Extension A-4346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4349 CJK Ideograph Extension A-4349 | 4348 CJK Ideograph Extension A-4348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434b CJK Ideograph Extension A-434B | 434a CJK Ideograph Extension A-434A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434d CJK Ideograph Extension A-434D | 434c CJK Ideograph Extension A-434C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 434f CJK Ideograph Extension A-434F | 434e CJK Ideograph Extension A-434E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4351 CJK Ideograph Extension A-4351 | 4350 CJK Ideograph Extension A-4350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4353 CJK Ideograph Extension A-4353 | 4352 CJK Ideograph Extension A-4352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4355 CJK Ideograph Extension A-4355 | 4354 CJK Ideograph Extension A-4354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4357 CJK Ideograph Extension A-4357 | 4356 CJK Ideograph Extension A-4356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4359 CJK Ideograph Extension A-4359 | 4358 CJK Ideograph Extension A-4358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435b CJK Ideograph Extension A-435B | 435a CJK Ideograph Extension A-435A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435d CJK Ideograph Extension A-435D | 435c CJK Ideograph Extension A-435C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 435f CJK Ideograph Extension A-435F | 435e CJK Ideograph Extension A-435E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4361 CJK Ideograph Extension A-4361 | 4360 CJK Ideograph Extension A-4360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4363 CJK Ideograph Extension A-4363 | 4362 CJK Ideograph Extension A-4362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4365 CJK Ideograph Extension A-4365 | 4364 CJK Ideograph Extension A-4364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4367 CJK Ideograph Extension A-4367 | 4366 CJK Ideograph Extension A-4366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4369 CJK Ideograph Extension A-4369 | 4368 CJK Ideograph Extension A-4368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436b CJK Ideograph Extension A-436B | 436a CJK Ideograph Extension A-436A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436d CJK Ideograph Extension A-436D | 436c CJK Ideograph Extension A-436C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 436f CJK Ideograph Extension A-436F | 436e CJK Ideograph Extension A-436E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4371 CJK Ideograph Extension A-4371 | 4370 CJK Ideograph Extension A-4370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4373 CJK Ideograph Extension A-4373 | 4372 CJK Ideograph Extension A-4372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4375 CJK Ideograph Extension A-4375 | 4374 CJK Ideograph Extension A-4374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4377 CJK Ideograph Extension A-4377 | 4376 CJK Ideograph Extension A-4376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4379 CJK Ideograph Extension A-4379 | 4378 CJK Ideograph Extension A-4378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437b CJK Ideograph Extension A-437B | 437a CJK Ideograph Extension A-437A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437d CJK Ideograph Extension A-437D | 437c CJK Ideograph Extension A-437C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 437f CJK Ideograph Extension A-437F | 437e CJK Ideograph Extension A-437E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4381 CJK Ideograph Extension A-4381 | 4380 CJK Ideograph Extension A-4380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4383 CJK Ideograph Extension A-4383 | 4382 CJK Ideograph Extension A-4382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4385 CJK Ideograph Extension A-4385 | 4384 CJK Ideograph Extension A-4384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4387 CJK Ideograph Extension A-4387 | 4386 CJK Ideograph Extension A-4386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4389 CJK Ideograph Extension A-4389 | 4388 CJK Ideograph Extension A-4388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438b CJK Ideograph Extension A-438B | 438a CJK Ideograph Extension A-438A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438d CJK Ideograph Extension A-438D | 438c CJK Ideograph Extension A-438C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 438f CJK Ideograph Extension A-438F | 438e CJK Ideograph Extension A-438E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4391 CJK Ideograph Extension A-4391 | 4390 CJK Ideograph Extension A-4390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4393 CJK Ideograph Extension A-4393 | 4392 CJK Ideograph Extension A-4392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4395 CJK Ideograph Extension A-4395 | 4394 CJK Ideograph Extension A-4394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4397 CJK Ideograph Extension A-4397 | 4396 CJK Ideograph Extension A-4396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4399 CJK Ideograph Extension A-4399 | 4398 CJK Ideograph Extension A-4398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439b CJK Ideograph Extension A-439B | 439a CJK Ideograph Extension A-439A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439d CJK Ideograph Extension A-439D | 439c CJK Ideograph Extension A-439C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 439f CJK Ideograph Extension A-439F | 439e CJK Ideograph Extension A-439E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a1 CJK Ideograph Extension A-43A1 | 43a0 CJK Ideograph Extension A-43A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a3 CJK Ideograph Extension A-43A3 | 43a2 CJK Ideograph Extension A-43A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a5 CJK Ideograph Extension A-43A5 | 43a4 CJK Ideograph Extension A-43A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a7 CJK Ideograph Extension A-43A7 | 43a6 CJK Ideograph Extension A-43A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43a9 CJK Ideograph Extension A-43A9 | 43a8 CJK Ideograph Extension A-43A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ab CJK Ideograph Extension A-43AB | 43aa CJK Ideograph Extension A-43AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ad CJK Ideograph Extension A-43AD | 43ac CJK Ideograph Extension A-43AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43af CJK Ideograph Extension A-43AF | 43ae CJK Ideograph Extension A-43AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b1 CJK Ideograph Extension A-43B1 | 43b0 CJK Ideograph Extension A-43B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b3 CJK Ideograph Extension A-43B3 | 43b2 CJK Ideograph Extension A-43B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b5 CJK Ideograph Extension A-43B5 | 43b4 CJK Ideograph Extension A-43B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b7 CJK Ideograph Extension A-43B7 | 43b6 CJK Ideograph Extension A-43B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43b9 CJK Ideograph Extension A-43B9 | 43b8 CJK Ideograph Extension A-43B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bb CJK Ideograph Extension A-43BB | 43ba CJK Ideograph Extension A-43BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bd CJK Ideograph Extension A-43BD | 43bc CJK Ideograph Extension A-43BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43bf CJK Ideograph Extension A-43BF | 43be CJK Ideograph Extension A-43BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c1 CJK Ideograph Extension A-43C1 | 43c0 CJK Ideograph Extension A-43C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c3 CJK Ideograph Extension A-43C3 | 43c2 CJK Ideograph Extension A-43C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c5 CJK Ideograph Extension A-43C5 | 43c4 CJK Ideograph Extension A-43C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c7 CJK Ideograph Extension A-43C7 | 43c6 CJK Ideograph Extension A-43C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43c9 CJK Ideograph Extension A-43C9 | 43c8 CJK Ideograph Extension A-43C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cb CJK Ideograph Extension A-43CB | 43ca CJK Ideograph Extension A-43CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cd CJK Ideograph Extension A-43CD | 43cc CJK Ideograph Extension A-43CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43cf CJK Ideograph Extension A-43CF | 43ce CJK Ideograph Extension A-43CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d1 CJK Ideograph Extension A-43D1 | 43d0 CJK Ideograph Extension A-43D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d3 CJK Ideograph Extension A-43D3 | 43d2 CJK Ideograph Extension A-43D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d5 CJK Ideograph Extension A-43D5 | 43d4 CJK Ideograph Extension A-43D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d7 CJK Ideograph Extension A-43D7 | 43d6 CJK Ideograph Extension A-43D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43d9 CJK Ideograph Extension A-43D9 | 43d8 CJK Ideograph Extension A-43D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43db CJK Ideograph Extension A-43DB | 43da CJK Ideograph Extension A-43DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43dd CJK Ideograph Extension A-43DD | 43dc CJK Ideograph Extension A-43DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43df CJK Ideograph Extension A-43DF | 43de CJK Ideograph Extension A-43DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e1 CJK Ideograph Extension A-43E1 | 43e0 CJK Ideograph Extension A-43E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e3 CJK Ideograph Extension A-43E3 | 43e2 CJK Ideograph Extension A-43E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e5 CJK Ideograph Extension A-43E5 | 43e4 CJK Ideograph Extension A-43E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e7 CJK Ideograph Extension A-43E7 | 43e6 CJK Ideograph Extension A-43E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43e9 CJK Ideograph Extension A-43E9 | 43e8 CJK Ideograph Extension A-43E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43eb CJK Ideograph Extension A-43EB | 43ea CJK Ideograph Extension A-43EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ed CJK Ideograph Extension A-43ED | 43ec CJK Ideograph Extension A-43EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ef CJK Ideograph Extension A-43EF | 43ee CJK Ideograph Extension A-43EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f1 CJK Ideograph Extension A-43F1 | 43f0 CJK Ideograph Extension A-43F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f3 CJK Ideograph Extension A-43F3 | 43f2 CJK Ideograph Extension A-43F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f5 CJK Ideograph Extension A-43F5 | 43f4 CJK Ideograph Extension A-43F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f7 CJK Ideograph Extension A-43F7 | 43f6 CJK Ideograph Extension A-43F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43f9 CJK Ideograph Extension A-43F9 | 43f8 CJK Ideograph Extension A-43F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43fb CJK Ideograph Extension A-43FB | 43fa CJK Ideograph Extension A-43FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43fd CJK Ideograph Extension A-43FD | 43fc CJK Ideograph Extension A-43FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 43ff CJK Ideograph Extension A-43FF | 43fe CJK Ideograph Extension A-43FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4401 CJK Ideograph Extension A-4401 | 4400 CJK Ideograph Extension A-4400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4403 CJK Ideograph Extension A-4403 | 4402 CJK Ideograph Extension A-4402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4405 CJK Ideograph Extension A-4405 | 4404 CJK Ideograph Extension A-4404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4407 CJK Ideograph Extension A-4407 | 4406 CJK Ideograph Extension A-4406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4409 CJK Ideograph Extension A-4409 | 4408 CJK Ideograph Extension A-4408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440b CJK Ideograph Extension A-440B | 440a CJK Ideograph Extension A-440A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440d CJK Ideograph Extension A-440D | 440c CJK Ideograph Extension A-440C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 440f CJK Ideograph Extension A-440F | 440e CJK Ideograph Extension A-440E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4411 CJK Ideograph Extension A-4411 | 4410 CJK Ideograph Extension A-4410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4413 CJK Ideograph Extension A-4413 | 4412 CJK Ideograph Extension A-4412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4415 CJK Ideograph Extension A-4415 | 4414 CJK Ideograph Extension A-4414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4417 CJK Ideograph Extension A-4417 | 4416 CJK Ideograph Extension A-4416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4419 CJK Ideograph Extension A-4419 | 4418 CJK Ideograph Extension A-4418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441b CJK Ideograph Extension A-441B | 441a CJK Ideograph Extension A-441A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441d CJK Ideograph Extension A-441D | 441c CJK Ideograph Extension A-441C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 441f CJK Ideograph Extension A-441F | 441e CJK Ideograph Extension A-441E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4421 CJK Ideograph Extension A-4421 | 4420 CJK Ideograph Extension A-4420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4423 CJK Ideograph Extension A-4423 | 4422 CJK Ideograph Extension A-4422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4425 CJK Ideograph Extension A-4425 | 4424 CJK Ideograph Extension A-4424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4427 CJK Ideograph Extension A-4427 | 4426 CJK Ideograph Extension A-4426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4429 CJK Ideograph Extension A-4429 | 4428 CJK Ideograph Extension A-4428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442b CJK Ideograph Extension A-442B | 442a CJK Ideograph Extension A-442A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442d CJK Ideograph Extension A-442D | 442c CJK Ideograph Extension A-442C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 442f CJK Ideograph Extension A-442F | 442e CJK Ideograph Extension A-442E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4431 CJK Ideograph Extension A-4431 | 4430 CJK Ideograph Extension A-4430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4433 CJK Ideograph Extension A-4433 | 4432 CJK Ideograph Extension A-4432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4435 CJK Ideograph Extension A-4435 | 4434 CJK Ideograph Extension A-4434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4437 CJK Ideograph Extension A-4437 | 4436 CJK Ideograph Extension A-4436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4439 CJK Ideograph Extension A-4439 | 4438 CJK Ideograph Extension A-4438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443b CJK Ideograph Extension A-443B | 443a CJK Ideograph Extension A-443A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443d CJK Ideograph Extension A-443D | 443c CJK Ideograph Extension A-443C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 443f CJK Ideograph Extension A-443F | 443e CJK Ideograph Extension A-443E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4441 CJK Ideograph Extension A-4441 | 4440 CJK Ideograph Extension A-4440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4443 CJK Ideograph Extension A-4443 | 4442 CJK Ideograph Extension A-4442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4445 CJK Ideograph Extension A-4445 | 4444 CJK Ideograph Extension A-4444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4447 CJK Ideograph Extension A-4447 | 4446 CJK Ideograph Extension A-4446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4449 CJK Ideograph Extension A-4449 | 4448 CJK Ideograph Extension A-4448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444b CJK Ideograph Extension A-444B | 444a CJK Ideograph Extension A-444A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444d CJK Ideograph Extension A-444D | 444c CJK Ideograph Extension A-444C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 444f CJK Ideograph Extension A-444F | 444e CJK Ideograph Extension A-444E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4451 CJK Ideograph Extension A-4451 | 4450 CJK Ideograph Extension A-4450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4453 CJK Ideograph Extension A-4453 | 4452 CJK Ideograph Extension A-4452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4455 CJK Ideograph Extension A-4455 | 4454 CJK Ideograph Extension A-4454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4457 CJK Ideograph Extension A-4457 | 4456 CJK Ideograph Extension A-4456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4459 CJK Ideograph Extension A-4459 | 4458 CJK Ideograph Extension A-4458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445b CJK Ideograph Extension A-445B | 445a CJK Ideograph Extension A-445A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445d CJK Ideograph Extension A-445D | 445c CJK Ideograph Extension A-445C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 445f CJK Ideograph Extension A-445F | 445e CJK Ideograph Extension A-445E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4461 CJK Ideograph Extension A-4461 | 4460 CJK Ideograph Extension A-4460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4463 CJK Ideograph Extension A-4463 | 4462 CJK Ideograph Extension A-4462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4465 CJK Ideograph Extension A-4465 | 4464 CJK Ideograph Extension A-4464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4467 CJK Ideograph Extension A-4467 | 4466 CJK Ideograph Extension A-4466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4469 CJK Ideograph Extension A-4469 | 4468 CJK Ideograph Extension A-4468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446b CJK Ideograph Extension A-446B | 446a CJK Ideograph Extension A-446A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446d CJK Ideograph Extension A-446D | 446c CJK Ideograph Extension A-446C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 446f CJK Ideograph Extension A-446F | 446e CJK Ideograph Extension A-446E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4471 CJK Ideograph Extension A-4471 | 4470 CJK Ideograph Extension A-4470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4473 CJK Ideograph Extension A-4473 | 4472 CJK Ideograph Extension A-4472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4475 CJK Ideograph Extension A-4475 | 4474 CJK Ideograph Extension A-4474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4477 CJK Ideograph Extension A-4477 | 4476 CJK Ideograph Extension A-4476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4479 CJK Ideograph Extension A-4479 | 4478 CJK Ideograph Extension A-4478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447b CJK Ideograph Extension A-447B | 447a CJK Ideograph Extension A-447A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447d CJK Ideograph Extension A-447D | 447c CJK Ideograph Extension A-447C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 447f CJK Ideograph Extension A-447F | 447e CJK Ideograph Extension A-447E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4481 CJK Ideograph Extension A-4481 | 4480 CJK Ideograph Extension A-4480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4483 CJK Ideograph Extension A-4483 | 4482 CJK Ideograph Extension A-4482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4485 CJK Ideograph Extension A-4485 | 4484 CJK Ideograph Extension A-4484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4487 CJK Ideograph Extension A-4487 | 4486 CJK Ideograph Extension A-4486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4489 CJK Ideograph Extension A-4489 | 4488 CJK Ideograph Extension A-4488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448b CJK Ideograph Extension A-448B | 448a CJK Ideograph Extension A-448A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448d CJK Ideograph Extension A-448D | 448c CJK Ideograph Extension A-448C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 448f CJK Ideograph Extension A-448F | 448e CJK Ideograph Extension A-448E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4491 CJK Ideograph Extension A-4491 | 4490 CJK Ideograph Extension A-4490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4493 CJK Ideograph Extension A-4493 | 4492 CJK Ideograph Extension A-4492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4495 CJK Ideograph Extension A-4495 | 4494 CJK Ideograph Extension A-4494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4497 CJK Ideograph Extension A-4497 | 4496 CJK Ideograph Extension A-4496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4499 CJK Ideograph Extension A-4499 | 4498 CJK Ideograph Extension A-4498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449b CJK Ideograph Extension A-449B | 449a CJK Ideograph Extension A-449A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449d CJK Ideograph Extension A-449D | 449c CJK Ideograph Extension A-449C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 449f CJK Ideograph Extension A-449F | 449e CJK Ideograph Extension A-449E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a1 CJK Ideograph Extension A-44A1 | 44a0 CJK Ideograph Extension A-44A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a3 CJK Ideograph Extension A-44A3 | 44a2 CJK Ideograph Extension A-44A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a5 CJK Ideograph Extension A-44A5 | 44a4 CJK Ideograph Extension A-44A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a7 CJK Ideograph Extension A-44A7 | 44a6 CJK Ideograph Extension A-44A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44a9 CJK Ideograph Extension A-44A9 | 44a8 CJK Ideograph Extension A-44A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ab CJK Ideograph Extension A-44AB | 44aa CJK Ideograph Extension A-44AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ad CJK Ideograph Extension A-44AD | 44ac CJK Ideograph Extension A-44AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44af CJK Ideograph Extension A-44AF | 44ae CJK Ideograph Extension A-44AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b1 CJK Ideograph Extension A-44B1 | 44b0 CJK Ideograph Extension A-44B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b3 CJK Ideograph Extension A-44B3 | 44b2 CJK Ideograph Extension A-44B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b5 CJK Ideograph Extension A-44B5 | 44b4 CJK Ideograph Extension A-44B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b7 CJK Ideograph Extension A-44B7 | 44b6 CJK Ideograph Extension A-44B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44b9 CJK Ideograph Extension A-44B9 | 44b8 CJK Ideograph Extension A-44B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bb CJK Ideograph Extension A-44BB | 44ba CJK Ideograph Extension A-44BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bd CJK Ideograph Extension A-44BD | 44bc CJK Ideograph Extension A-44BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44bf CJK Ideograph Extension A-44BF | 44be CJK Ideograph Extension A-44BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c1 CJK Ideograph Extension A-44C1 | 44c0 CJK Ideograph Extension A-44C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c3 CJK Ideograph Extension A-44C3 | 44c2 CJK Ideograph Extension A-44C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c5 CJK Ideograph Extension A-44C5 | 44c4 CJK Ideograph Extension A-44C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c7 CJK Ideograph Extension A-44C7 | 44c6 CJK Ideograph Extension A-44C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44c9 CJK Ideograph Extension A-44C9 | 44c8 CJK Ideograph Extension A-44C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cb CJK Ideograph Extension A-44CB | 44ca CJK Ideograph Extension A-44CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cd CJK Ideograph Extension A-44CD | 44cc CJK Ideograph Extension A-44CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44cf CJK Ideograph Extension A-44CF | 44ce CJK Ideograph Extension A-44CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d1 CJK Ideograph Extension A-44D1 | 44d0 CJK Ideograph Extension A-44D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d3 CJK Ideograph Extension A-44D3 | 44d2 CJK Ideograph Extension A-44D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d5 CJK Ideograph Extension A-44D5 | 44d4 CJK Ideograph Extension A-44D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d7 CJK Ideograph Extension A-44D7 | 44d6 CJK Ideograph Extension A-44D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44d9 CJK Ideograph Extension A-44D9 | 44d8 CJK Ideograph Extension A-44D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44db CJK Ideograph Extension A-44DB | 44da CJK Ideograph Extension A-44DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44dd CJK Ideograph Extension A-44DD | 44dc CJK Ideograph Extension A-44DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44df CJK Ideograph Extension A-44DF | 44de CJK Ideograph Extension A-44DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e1 CJK Ideograph Extension A-44E1 | 44e0 CJK Ideograph Extension A-44E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e3 CJK Ideograph Extension A-44E3 | 44e2 CJK Ideograph Extension A-44E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e5 CJK Ideograph Extension A-44E5 | 44e4 CJK Ideograph Extension A-44E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e7 CJK Ideograph Extension A-44E7 | 44e6 CJK Ideograph Extension A-44E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44e9 CJK Ideograph Extension A-44E9 | 44e8 CJK Ideograph Extension A-44E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44eb CJK Ideograph Extension A-44EB | 44ea CJK Ideograph Extension A-44EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ed CJK Ideograph Extension A-44ED | 44ec CJK Ideograph Extension A-44EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ef CJK Ideograph Extension A-44EF | 44ee CJK Ideograph Extension A-44EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f1 CJK Ideograph Extension A-44F1 | 44f0 CJK Ideograph Extension A-44F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f3 CJK Ideograph Extension A-44F3 | 44f2 CJK Ideograph Extension A-44F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f5 CJK Ideograph Extension A-44F5 | 44f4 CJK Ideograph Extension A-44F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f7 CJK Ideograph Extension A-44F7 | 44f6 CJK Ideograph Extension A-44F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44f9 CJK Ideograph Extension A-44F9 | 44f8 CJK Ideograph Extension A-44F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44fb CJK Ideograph Extension A-44FB | 44fa CJK Ideograph Extension A-44FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44fd CJK Ideograph Extension A-44FD | 44fc CJK Ideograph Extension A-44FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 44ff CJK Ideograph Extension A-44FF | 44fe CJK Ideograph Extension A-44FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4501 CJK Ideograph Extension A-4501 | 4500 CJK Ideograph Extension A-4500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4503 CJK Ideograph Extension A-4503 | 4502 CJK Ideograph Extension A-4502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4505 CJK Ideograph Extension A-4505 | 4504 CJK Ideograph Extension A-4504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4507 CJK Ideograph Extension A-4507 | 4506 CJK Ideograph Extension A-4506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4509 CJK Ideograph Extension A-4509 | 4508 CJK Ideograph Extension A-4508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450b CJK Ideograph Extension A-450B | 450a CJK Ideograph Extension A-450A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450d CJK Ideograph Extension A-450D | 450c CJK Ideograph Extension A-450C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 450f CJK Ideograph Extension A-450F | 450e CJK Ideograph Extension A-450E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4511 CJK Ideograph Extension A-4511 | 4510 CJK Ideograph Extension A-4510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4513 CJK Ideograph Extension A-4513 | 4512 CJK Ideograph Extension A-4512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4515 CJK Ideograph Extension A-4515 | 4514 CJK Ideograph Extension A-4514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4517 CJK Ideograph Extension A-4517 | 4516 CJK Ideograph Extension A-4516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4519 CJK Ideograph Extension A-4519 | 4518 CJK Ideograph Extension A-4518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451b CJK Ideograph Extension A-451B | 451a CJK Ideograph Extension A-451A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451d CJK Ideograph Extension A-451D | 451c CJK Ideograph Extension A-451C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 451f CJK Ideograph Extension A-451F | 451e CJK Ideograph Extension A-451E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4521 CJK Ideograph Extension A-4521 | 4520 CJK Ideograph Extension A-4520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4523 CJK Ideograph Extension A-4523 | 4522 CJK Ideograph Extension A-4522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4525 CJK Ideograph Extension A-4525 | 4524 CJK Ideograph Extension A-4524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4527 CJK Ideograph Extension A-4527 | 4526 CJK Ideograph Extension A-4526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4529 CJK Ideograph Extension A-4529 | 4528 CJK Ideograph Extension A-4528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452b CJK Ideograph Extension A-452B | 452a CJK Ideograph Extension A-452A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452d CJK Ideograph Extension A-452D | 452c CJK Ideograph Extension A-452C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 452f CJK Ideograph Extension A-452F | 452e CJK Ideograph Extension A-452E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4531 CJK Ideograph Extension A-4531 | 4530 CJK Ideograph Extension A-4530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4533 CJK Ideograph Extension A-4533 | 4532 CJK Ideograph Extension A-4532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4535 CJK Ideograph Extension A-4535 | 4534 CJK Ideograph Extension A-4534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4537 CJK Ideograph Extension A-4537 | 4536 CJK Ideograph Extension A-4536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4539 CJK Ideograph Extension A-4539 | 4538 CJK Ideograph Extension A-4538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453b CJK Ideograph Extension A-453B | 453a CJK Ideograph Extension A-453A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453d CJK Ideograph Extension A-453D | 453c CJK Ideograph Extension A-453C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 453f CJK Ideograph Extension A-453F | 453e CJK Ideograph Extension A-453E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4541 CJK Ideograph Extension A-4541 | 4540 CJK Ideograph Extension A-4540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4543 CJK Ideograph Extension A-4543 | 4542 CJK Ideograph Extension A-4542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4545 CJK Ideograph Extension A-4545 | 4544 CJK Ideograph Extension A-4544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4547 CJK Ideograph Extension A-4547 | 4546 CJK Ideograph Extension A-4546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4549 CJK Ideograph Extension A-4549 | 4548 CJK Ideograph Extension A-4548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454b CJK Ideograph Extension A-454B | 454a CJK Ideograph Extension A-454A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454d CJK Ideograph Extension A-454D | 454c CJK Ideograph Extension A-454C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 454f CJK Ideograph Extension A-454F | 454e CJK Ideograph Extension A-454E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4551 CJK Ideograph Extension A-4551 | 4550 CJK Ideograph Extension A-4550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4553 CJK Ideograph Extension A-4553 | 4552 CJK Ideograph Extension A-4552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4555 CJK Ideograph Extension A-4555 | 4554 CJK Ideograph Extension A-4554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4557 CJK Ideograph Extension A-4557 | 4556 CJK Ideograph Extension A-4556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4559 CJK Ideograph Extension A-4559 | 4558 CJK Ideograph Extension A-4558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455b CJK Ideograph Extension A-455B | 455a CJK Ideograph Extension A-455A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455d CJK Ideograph Extension A-455D | 455c CJK Ideograph Extension A-455C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 455f CJK Ideograph Extension A-455F | 455e CJK Ideograph Extension A-455E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4561 CJK Ideograph Extension A-4561 | 4560 CJK Ideograph Extension A-4560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4563 CJK Ideograph Extension A-4563 | 4562 CJK Ideograph Extension A-4562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4565 CJK Ideograph Extension A-4565 | 4564 CJK Ideograph Extension A-4564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4567 CJK Ideograph Extension A-4567 | 4566 CJK Ideograph Extension A-4566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4569 CJK Ideograph Extension A-4569 | 4568 CJK Ideograph Extension A-4568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456b CJK Ideograph Extension A-456B | 456a CJK Ideograph Extension A-456A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456d CJK Ideograph Extension A-456D | 456c CJK Ideograph Extension A-456C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 456f CJK Ideograph Extension A-456F | 456e CJK Ideograph Extension A-456E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4571 CJK Ideograph Extension A-4571 | 4570 CJK Ideograph Extension A-4570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4573 CJK Ideograph Extension A-4573 | 4572 CJK Ideograph Extension A-4572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4575 CJK Ideograph Extension A-4575 | 4574 CJK Ideograph Extension A-4574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4577 CJK Ideograph Extension A-4577 | 4576 CJK Ideograph Extension A-4576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4579 CJK Ideograph Extension A-4579 | 4578 CJK Ideograph Extension A-4578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457b CJK Ideograph Extension A-457B | 457a CJK Ideograph Extension A-457A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457d CJK Ideograph Extension A-457D | 457c CJK Ideograph Extension A-457C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 457f CJK Ideograph Extension A-457F | 457e CJK Ideograph Extension A-457E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4581 CJK Ideograph Extension A-4581 | 4580 CJK Ideograph Extension A-4580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4583 CJK Ideograph Extension A-4583 | 4582 CJK Ideograph Extension A-4582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4585 CJK Ideograph Extension A-4585 | 4584 CJK Ideograph Extension A-4584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4587 CJK Ideograph Extension A-4587 | 4586 CJK Ideograph Extension A-4586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4589 CJK Ideograph Extension A-4589 | 4588 CJK Ideograph Extension A-4588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458b CJK Ideograph Extension A-458B | 458a CJK Ideograph Extension A-458A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458d CJK Ideograph Extension A-458D | 458c CJK Ideograph Extension A-458C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 458f CJK Ideograph Extension A-458F | 458e CJK Ideograph Extension A-458E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4591 CJK Ideograph Extension A-4591 | 4590 CJK Ideograph Extension A-4590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4593 CJK Ideograph Extension A-4593 | 4592 CJK Ideograph Extension A-4592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4595 CJK Ideograph Extension A-4595 | 4594 CJK Ideograph Extension A-4594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4597 CJK Ideograph Extension A-4597 | 4596 CJK Ideograph Extension A-4596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4599 CJK Ideograph Extension A-4599 | 4598 CJK Ideograph Extension A-4598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459b CJK Ideograph Extension A-459B | 459a CJK Ideograph Extension A-459A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459d CJK Ideograph Extension A-459D | 459c CJK Ideograph Extension A-459C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 459f CJK Ideograph Extension A-459F | 459e CJK Ideograph Extension A-459E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a1 CJK Ideograph Extension A-45A1 | 45a0 CJK Ideograph Extension A-45A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a3 CJK Ideograph Extension A-45A3 | 45a2 CJK Ideograph Extension A-45A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a5 CJK Ideograph Extension A-45A5 | 45a4 CJK Ideograph Extension A-45A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a7 CJK Ideograph Extension A-45A7 | 45a6 CJK Ideograph Extension A-45A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45a9 CJK Ideograph Extension A-45A9 | 45a8 CJK Ideograph Extension A-45A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ab CJK Ideograph Extension A-45AB | 45aa CJK Ideograph Extension A-45AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ad CJK Ideograph Extension A-45AD | 45ac CJK Ideograph Extension A-45AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45af CJK Ideograph Extension A-45AF | 45ae CJK Ideograph Extension A-45AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b1 CJK Ideograph Extension A-45B1 | 45b0 CJK Ideograph Extension A-45B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b3 CJK Ideograph Extension A-45B3 | 45b2 CJK Ideograph Extension A-45B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b5 CJK Ideograph Extension A-45B5 | 45b4 CJK Ideograph Extension A-45B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b7 CJK Ideograph Extension A-45B7 | 45b6 CJK Ideograph Extension A-45B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45b9 CJK Ideograph Extension A-45B9 | 45b8 CJK Ideograph Extension A-45B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bb CJK Ideograph Extension A-45BB | 45ba CJK Ideograph Extension A-45BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bd CJK Ideograph Extension A-45BD | 45bc CJK Ideograph Extension A-45BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45bf CJK Ideograph Extension A-45BF | 45be CJK Ideograph Extension A-45BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c1 CJK Ideograph Extension A-45C1 | 45c0 CJK Ideograph Extension A-45C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c3 CJK Ideograph Extension A-45C3 | 45c2 CJK Ideograph Extension A-45C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c5 CJK Ideograph Extension A-45C5 | 45c4 CJK Ideograph Extension A-45C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c7 CJK Ideograph Extension A-45C7 | 45c6 CJK Ideograph Extension A-45C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45c9 CJK Ideograph Extension A-45C9 | 45c8 CJK Ideograph Extension A-45C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cb CJK Ideograph Extension A-45CB | 45ca CJK Ideograph Extension A-45CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cd CJK Ideograph Extension A-45CD | 45cc CJK Ideograph Extension A-45CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45cf CJK Ideograph Extension A-45CF | 45ce CJK Ideograph Extension A-45CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d1 CJK Ideograph Extension A-45D1 | 45d0 CJK Ideograph Extension A-45D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d3 CJK Ideograph Extension A-45D3 | 45d2 CJK Ideograph Extension A-45D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d5 CJK Ideograph Extension A-45D5 | 45d4 CJK Ideograph Extension A-45D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d7 CJK Ideograph Extension A-45D7 | 45d6 CJK Ideograph Extension A-45D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45d9 CJK Ideograph Extension A-45D9 | 45d8 CJK Ideograph Extension A-45D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45db CJK Ideograph Extension A-45DB | 45da CJK Ideograph Extension A-45DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45dd CJK Ideograph Extension A-45DD | 45dc CJK Ideograph Extension A-45DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45df CJK Ideograph Extension A-45DF | 45de CJK Ideograph Extension A-45DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e1 CJK Ideograph Extension A-45E1 | 45e0 CJK Ideograph Extension A-45E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e3 CJK Ideograph Extension A-45E3 | 45e2 CJK Ideograph Extension A-45E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e5 CJK Ideograph Extension A-45E5 | 45e4 CJK Ideograph Extension A-45E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e7 CJK Ideograph Extension A-45E7 | 45e6 CJK Ideograph Extension A-45E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45e9 CJK Ideograph Extension A-45E9 | 45e8 CJK Ideograph Extension A-45E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45eb CJK Ideograph Extension A-45EB | 45ea CJK Ideograph Extension A-45EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ed CJK Ideograph Extension A-45ED | 45ec CJK Ideograph Extension A-45EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ef CJK Ideograph Extension A-45EF | 45ee CJK Ideograph Extension A-45EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f1 CJK Ideograph Extension A-45F1 | 45f0 CJK Ideograph Extension A-45F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f3 CJK Ideograph Extension A-45F3 | 45f2 CJK Ideograph Extension A-45F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f5 CJK Ideograph Extension A-45F5 | 45f4 CJK Ideograph Extension A-45F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f7 CJK Ideograph Extension A-45F7 | 45f6 CJK Ideograph Extension A-45F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45f9 CJK Ideograph Extension A-45F9 | 45f8 CJK Ideograph Extension A-45F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45fb CJK Ideograph Extension A-45FB | 45fa CJK Ideograph Extension A-45FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45fd CJK Ideograph Extension A-45FD | 45fc CJK Ideograph Extension A-45FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 45ff CJK Ideograph Extension A-45FF | 45fe CJK Ideograph Extension A-45FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4601 CJK Ideograph Extension A-4601 | 4600 CJK Ideograph Extension A-4600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4603 CJK Ideograph Extension A-4603 | 4602 CJK Ideograph Extension A-4602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4605 CJK Ideograph Extension A-4605 | 4604 CJK Ideograph Extension A-4604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4607 CJK Ideograph Extension A-4607 | 4606 CJK Ideograph Extension A-4606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4609 CJK Ideograph Extension A-4609 | 4608 CJK Ideograph Extension A-4608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460b CJK Ideograph Extension A-460B | 460a CJK Ideograph Extension A-460A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460d CJK Ideograph Extension A-460D | 460c CJK Ideograph Extension A-460C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 460f CJK Ideograph Extension A-460F | 460e CJK Ideograph Extension A-460E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4611 CJK Ideograph Extension A-4611 | 4610 CJK Ideograph Extension A-4610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4613 CJK Ideograph Extension A-4613 | 4612 CJK Ideograph Extension A-4612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4615 CJK Ideograph Extension A-4615 | 4614 CJK Ideograph Extension A-4614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4617 CJK Ideograph Extension A-4617 | 4616 CJK Ideograph Extension A-4616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4619 CJK Ideograph Extension A-4619 | 4618 CJK Ideograph Extension A-4618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461b CJK Ideograph Extension A-461B | 461a CJK Ideograph Extension A-461A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461d CJK Ideograph Extension A-461D | 461c CJK Ideograph Extension A-461C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 461f CJK Ideograph Extension A-461F | 461e CJK Ideograph Extension A-461E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4621 CJK Ideograph Extension A-4621 | 4620 CJK Ideograph Extension A-4620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4623 CJK Ideograph Extension A-4623 | 4622 CJK Ideograph Extension A-4622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4625 CJK Ideograph Extension A-4625 | 4624 CJK Ideograph Extension A-4624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4627 CJK Ideograph Extension A-4627 | 4626 CJK Ideograph Extension A-4626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4629 CJK Ideograph Extension A-4629 | 4628 CJK Ideograph Extension A-4628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462b CJK Ideograph Extension A-462B | 462a CJK Ideograph Extension A-462A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462d CJK Ideograph Extension A-462D | 462c CJK Ideograph Extension A-462C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 462f CJK Ideograph Extension A-462F | 462e CJK Ideograph Extension A-462E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4631 CJK Ideograph Extension A-4631 | 4630 CJK Ideograph Extension A-4630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4633 CJK Ideograph Extension A-4633 | 4632 CJK Ideograph Extension A-4632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4635 CJK Ideograph Extension A-4635 | 4634 CJK Ideograph Extension A-4634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4637 CJK Ideograph Extension A-4637 | 4636 CJK Ideograph Extension A-4636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4639 CJK Ideograph Extension A-4639 | 4638 CJK Ideograph Extension A-4638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463b CJK Ideograph Extension A-463B | 463a CJK Ideograph Extension A-463A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463d CJK Ideograph Extension A-463D | 463c CJK Ideograph Extension A-463C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 463f CJK Ideograph Extension A-463F | 463e CJK Ideograph Extension A-463E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4641 CJK Ideograph Extension A-4641 | 4640 CJK Ideograph Extension A-4640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4643 CJK Ideograph Extension A-4643 | 4642 CJK Ideograph Extension A-4642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4645 CJK Ideograph Extension A-4645 | 4644 CJK Ideograph Extension A-4644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4647 CJK Ideograph Extension A-4647 | 4646 CJK Ideograph Extension A-4646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4649 CJK Ideograph Extension A-4649 | 4648 CJK Ideograph Extension A-4648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464b CJK Ideograph Extension A-464B | 464a CJK Ideograph Extension A-464A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464d CJK Ideograph Extension A-464D | 464c CJK Ideograph Extension A-464C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 464f CJK Ideograph Extension A-464F | 464e CJK Ideograph Extension A-464E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4651 CJK Ideograph Extension A-4651 | 4650 CJK Ideograph Extension A-4650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4653 CJK Ideograph Extension A-4653 | 4652 CJK Ideograph Extension A-4652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4655 CJK Ideograph Extension A-4655 | 4654 CJK Ideograph Extension A-4654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4657 CJK Ideograph Extension A-4657 | 4656 CJK Ideograph Extension A-4656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4659 CJK Ideograph Extension A-4659 | 4658 CJK Ideograph Extension A-4658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465b CJK Ideograph Extension A-465B | 465a CJK Ideograph Extension A-465A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465d CJK Ideograph Extension A-465D | 465c CJK Ideograph Extension A-465C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 465f CJK Ideograph Extension A-465F | 465e CJK Ideograph Extension A-465E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4661 CJK Ideograph Extension A-4661 | 4660 CJK Ideograph Extension A-4660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4663 CJK Ideograph Extension A-4663 | 4662 CJK Ideograph Extension A-4662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4665 CJK Ideograph Extension A-4665 | 4664 CJK Ideograph Extension A-4664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4667 CJK Ideograph Extension A-4667 | 4666 CJK Ideograph Extension A-4666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4669 CJK Ideograph Extension A-4669 | 4668 CJK Ideograph Extension A-4668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466b CJK Ideograph Extension A-466B | 466a CJK Ideograph Extension A-466A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466d CJK Ideograph Extension A-466D | 466c CJK Ideograph Extension A-466C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 466f CJK Ideograph Extension A-466F | 466e CJK Ideograph Extension A-466E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4671 CJK Ideograph Extension A-4671 | 4670 CJK Ideograph Extension A-4670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4673 CJK Ideograph Extension A-4673 | 4672 CJK Ideograph Extension A-4672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4675 CJK Ideograph Extension A-4675 | 4674 CJK Ideograph Extension A-4674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4677 CJK Ideograph Extension A-4677 | 4676 CJK Ideograph Extension A-4676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4679 CJK Ideograph Extension A-4679 | 4678 CJK Ideograph Extension A-4678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467b CJK Ideograph Extension A-467B | 467a CJK Ideograph Extension A-467A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467d CJK Ideograph Extension A-467D | 467c CJK Ideograph Extension A-467C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 467f CJK Ideograph Extension A-467F | 467e CJK Ideograph Extension A-467E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4681 CJK Ideograph Extension A-4681 | 4680 CJK Ideograph Extension A-4680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4683 CJK Ideograph Extension A-4683 | 4682 CJK Ideograph Extension A-4682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4685 CJK Ideograph Extension A-4685 | 4684 CJK Ideograph Extension A-4684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4687 CJK Ideograph Extension A-4687 | 4686 CJK Ideograph Extension A-4686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4689 CJK Ideograph Extension A-4689 | 4688 CJK Ideograph Extension A-4688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468b CJK Ideograph Extension A-468B | 468a CJK Ideograph Extension A-468A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468d CJK Ideograph Extension A-468D | 468c CJK Ideograph Extension A-468C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 468f CJK Ideograph Extension A-468F | 468e CJK Ideograph Extension A-468E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4691 CJK Ideograph Extension A-4691 | 4690 CJK Ideograph Extension A-4690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4693 CJK Ideograph Extension A-4693 | 4692 CJK Ideograph Extension A-4692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4695 CJK Ideograph Extension A-4695 | 4694 CJK Ideograph Extension A-4694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4697 CJK Ideograph Extension A-4697 | 4696 CJK Ideograph Extension A-4696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4699 CJK Ideograph Extension A-4699 | 4698 CJK Ideograph Extension A-4698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469b CJK Ideograph Extension A-469B | 469a CJK Ideograph Extension A-469A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469d CJK Ideograph Extension A-469D | 469c CJK Ideograph Extension A-469C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 469f CJK Ideograph Extension A-469F | 469e CJK Ideograph Extension A-469E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a1 CJK Ideograph Extension A-46A1 | 46a0 CJK Ideograph Extension A-46A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a3 CJK Ideograph Extension A-46A3 | 46a2 CJK Ideograph Extension A-46A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a5 CJK Ideograph Extension A-46A5 | 46a4 CJK Ideograph Extension A-46A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a7 CJK Ideograph Extension A-46A7 | 46a6 CJK Ideograph Extension A-46A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46a9 CJK Ideograph Extension A-46A9 | 46a8 CJK Ideograph Extension A-46A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ab CJK Ideograph Extension A-46AB | 46aa CJK Ideograph Extension A-46AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ad CJK Ideograph Extension A-46AD | 46ac CJK Ideograph Extension A-46AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46af CJK Ideograph Extension A-46AF | 46ae CJK Ideograph Extension A-46AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b1 CJK Ideograph Extension A-46B1 | 46b0 CJK Ideograph Extension A-46B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b3 CJK Ideograph Extension A-46B3 | 46b2 CJK Ideograph Extension A-46B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b5 CJK Ideograph Extension A-46B5 | 46b4 CJK Ideograph Extension A-46B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b7 CJK Ideograph Extension A-46B7 | 46b6 CJK Ideograph Extension A-46B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46b9 CJK Ideograph Extension A-46B9 | 46b8 CJK Ideograph Extension A-46B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bb CJK Ideograph Extension A-46BB | 46ba CJK Ideograph Extension A-46BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bd CJK Ideograph Extension A-46BD | 46bc CJK Ideograph Extension A-46BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46bf CJK Ideograph Extension A-46BF | 46be CJK Ideograph Extension A-46BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c1 CJK Ideograph Extension A-46C1 | 46c0 CJK Ideograph Extension A-46C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c3 CJK Ideograph Extension A-46C3 | 46c2 CJK Ideograph Extension A-46C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c5 CJK Ideograph Extension A-46C5 | 46c4 CJK Ideograph Extension A-46C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c7 CJK Ideograph Extension A-46C7 | 46c6 CJK Ideograph Extension A-46C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46c9 CJK Ideograph Extension A-46C9 | 46c8 CJK Ideograph Extension A-46C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cb CJK Ideograph Extension A-46CB | 46ca CJK Ideograph Extension A-46CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cd CJK Ideograph Extension A-46CD | 46cc CJK Ideograph Extension A-46CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46cf CJK Ideograph Extension A-46CF | 46ce CJK Ideograph Extension A-46CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d1 CJK Ideograph Extension A-46D1 | 46d0 CJK Ideograph Extension A-46D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d3 CJK Ideograph Extension A-46D3 | 46d2 CJK Ideograph Extension A-46D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d5 CJK Ideograph Extension A-46D5 | 46d4 CJK Ideograph Extension A-46D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d7 CJK Ideograph Extension A-46D7 | 46d6 CJK Ideograph Extension A-46D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46d9 CJK Ideograph Extension A-46D9 | 46d8 CJK Ideograph Extension A-46D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46db CJK Ideograph Extension A-46DB | 46da CJK Ideograph Extension A-46DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46dd CJK Ideograph Extension A-46DD | 46dc CJK Ideograph Extension A-46DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46df CJK Ideograph Extension A-46DF | 46de CJK Ideograph Extension A-46DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e1 CJK Ideograph Extension A-46E1 | 46e0 CJK Ideograph Extension A-46E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e3 CJK Ideograph Extension A-46E3 | 46e2 CJK Ideograph Extension A-46E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e5 CJK Ideograph Extension A-46E5 | 46e4 CJK Ideograph Extension A-46E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e7 CJK Ideograph Extension A-46E7 | 46e6 CJK Ideograph Extension A-46E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46e9 CJK Ideograph Extension A-46E9 | 46e8 CJK Ideograph Extension A-46E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46eb CJK Ideograph Extension A-46EB | 46ea CJK Ideograph Extension A-46EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ed CJK Ideograph Extension A-46ED | 46ec CJK Ideograph Extension A-46EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ef CJK Ideograph Extension A-46EF | 46ee CJK Ideograph Extension A-46EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f1 CJK Ideograph Extension A-46F1 | 46f0 CJK Ideograph Extension A-46F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f3 CJK Ideograph Extension A-46F3 | 46f2 CJK Ideograph Extension A-46F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f5 CJK Ideograph Extension A-46F5 | 46f4 CJK Ideograph Extension A-46F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f7 CJK Ideograph Extension A-46F7 | 46f6 CJK Ideograph Extension A-46F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46f9 CJK Ideograph Extension A-46F9 | 46f8 CJK Ideograph Extension A-46F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46fb CJK Ideograph Extension A-46FB | 46fa CJK Ideograph Extension A-46FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46fd CJK Ideograph Extension A-46FD | 46fc CJK Ideograph Extension A-46FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 46ff CJK Ideograph Extension A-46FF | 46fe CJK Ideograph Extension A-46FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4701 CJK Ideograph Extension A-4701 | 4700 CJK Ideograph Extension A-4700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4703 CJK Ideograph Extension A-4703 | 4702 CJK Ideograph Extension A-4702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4705 CJK Ideograph Extension A-4705 | 4704 CJK Ideograph Extension A-4704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4707 CJK Ideograph Extension A-4707 | 4706 CJK Ideograph Extension A-4706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4709 CJK Ideograph Extension A-4709 | 4708 CJK Ideograph Extension A-4708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470b CJK Ideograph Extension A-470B | 470a CJK Ideograph Extension A-470A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470d CJK Ideograph Extension A-470D | 470c CJK Ideograph Extension A-470C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 470f CJK Ideograph Extension A-470F | 470e CJK Ideograph Extension A-470E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4711 CJK Ideograph Extension A-4711 | 4710 CJK Ideograph Extension A-4710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4713 CJK Ideograph Extension A-4713 | 4712 CJK Ideograph Extension A-4712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4715 CJK Ideograph Extension A-4715 | 4714 CJK Ideograph Extension A-4714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4717 CJK Ideograph Extension A-4717 | 4716 CJK Ideograph Extension A-4716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4719 CJK Ideograph Extension A-4719 | 4718 CJK Ideograph Extension A-4718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471b CJK Ideograph Extension A-471B | 471a CJK Ideograph Extension A-471A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471d CJK Ideograph Extension A-471D | 471c CJK Ideograph Extension A-471C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 471f CJK Ideograph Extension A-471F | 471e CJK Ideograph Extension A-471E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4721 CJK Ideograph Extension A-4721 | 4720 CJK Ideograph Extension A-4720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4723 CJK Ideograph Extension A-4723 | 4722 CJK Ideograph Extension A-4722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4725 CJK Ideograph Extension A-4725 | 4724 CJK Ideograph Extension A-4724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4727 CJK Ideograph Extension A-4727 | 4726 CJK Ideograph Extension A-4726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4729 CJK Ideograph Extension A-4729 | 4728 CJK Ideograph Extension A-4728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472b CJK Ideograph Extension A-472B | 472a CJK Ideograph Extension A-472A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472d CJK Ideograph Extension A-472D | 472c CJK Ideograph Extension A-472C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 472f CJK Ideograph Extension A-472F | 472e CJK Ideograph Extension A-472E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4731 CJK Ideograph Extension A-4731 | 4730 CJK Ideograph Extension A-4730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4733 CJK Ideograph Extension A-4733 | 4732 CJK Ideograph Extension A-4732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4735 CJK Ideograph Extension A-4735 | 4734 CJK Ideograph Extension A-4734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4737 CJK Ideograph Extension A-4737 | 4736 CJK Ideograph Extension A-4736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4739 CJK Ideograph Extension A-4739 | 4738 CJK Ideograph Extension A-4738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473b CJK Ideograph Extension A-473B | 473a CJK Ideograph Extension A-473A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473d CJK Ideograph Extension A-473D | 473c CJK Ideograph Extension A-473C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 473f CJK Ideograph Extension A-473F | 473e CJK Ideograph Extension A-473E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4741 CJK Ideograph Extension A-4741 | 4740 CJK Ideograph Extension A-4740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4743 CJK Ideograph Extension A-4743 | 4742 CJK Ideograph Extension A-4742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4745 CJK Ideograph Extension A-4745 | 4744 CJK Ideograph Extension A-4744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4747 CJK Ideograph Extension A-4747 | 4746 CJK Ideograph Extension A-4746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4749 CJK Ideograph Extension A-4749 | 4748 CJK Ideograph Extension A-4748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474b CJK Ideograph Extension A-474B | 474a CJK Ideograph Extension A-474A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474d CJK Ideograph Extension A-474D | 474c CJK Ideograph Extension A-474C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 474f CJK Ideograph Extension A-474F | 474e CJK Ideograph Extension A-474E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4751 CJK Ideograph Extension A-4751 | 4750 CJK Ideograph Extension A-4750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4753 CJK Ideograph Extension A-4753 | 4752 CJK Ideograph Extension A-4752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4755 CJK Ideograph Extension A-4755 | 4754 CJK Ideograph Extension A-4754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4757 CJK Ideograph Extension A-4757 | 4756 CJK Ideograph Extension A-4756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4759 CJK Ideograph Extension A-4759 | 4758 CJK Ideograph Extension A-4758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475b CJK Ideograph Extension A-475B | 475a CJK Ideograph Extension A-475A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475d CJK Ideograph Extension A-475D | 475c CJK Ideograph Extension A-475C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 475f CJK Ideograph Extension A-475F | 475e CJK Ideograph Extension A-475E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4761 CJK Ideograph Extension A-4761 | 4760 CJK Ideograph Extension A-4760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4763 CJK Ideograph Extension A-4763 | 4762 CJK Ideograph Extension A-4762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4765 CJK Ideograph Extension A-4765 | 4764 CJK Ideograph Extension A-4764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4767 CJK Ideograph Extension A-4767 | 4766 CJK Ideograph Extension A-4766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4769 CJK Ideograph Extension A-4769 | 4768 CJK Ideograph Extension A-4768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476b CJK Ideograph Extension A-476B | 476a CJK Ideograph Extension A-476A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476d CJK Ideograph Extension A-476D | 476c CJK Ideograph Extension A-476C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 476f CJK Ideograph Extension A-476F | 476e CJK Ideograph Extension A-476E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4771 CJK Ideograph Extension A-4771 | 4770 CJK Ideograph Extension A-4770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4773 CJK Ideograph Extension A-4773 | 4772 CJK Ideograph Extension A-4772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4775 CJK Ideograph Extension A-4775 | 4774 CJK Ideograph Extension A-4774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4777 CJK Ideograph Extension A-4777 | 4776 CJK Ideograph Extension A-4776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4779 CJK Ideograph Extension A-4779 | 4778 CJK Ideograph Extension A-4778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477b CJK Ideograph Extension A-477B | 477a CJK Ideograph Extension A-477A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477d CJK Ideograph Extension A-477D | 477c CJK Ideograph Extension A-477C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 477f CJK Ideograph Extension A-477F | 477e CJK Ideograph Extension A-477E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4781 CJK Ideograph Extension A-4781 | 4780 CJK Ideograph Extension A-4780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4783 CJK Ideograph Extension A-4783 | 4782 CJK Ideograph Extension A-4782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4785 CJK Ideograph Extension A-4785 | 4784 CJK Ideograph Extension A-4784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4787 CJK Ideograph Extension A-4787 | 4786 CJK Ideograph Extension A-4786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4789 CJK Ideograph Extension A-4789 | 4788 CJK Ideograph Extension A-4788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478b CJK Ideograph Extension A-478B | 478a CJK Ideograph Extension A-478A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478d CJK Ideograph Extension A-478D | 478c CJK Ideograph Extension A-478C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 478f CJK Ideograph Extension A-478F | 478e CJK Ideograph Extension A-478E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4791 CJK Ideograph Extension A-4791 | 4790 CJK Ideograph Extension A-4790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4793 CJK Ideograph Extension A-4793 | 4792 CJK Ideograph Extension A-4792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4795 CJK Ideograph Extension A-4795 | 4794 CJK Ideograph Extension A-4794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4797 CJK Ideograph Extension A-4797 | 4796 CJK Ideograph Extension A-4796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4799 CJK Ideograph Extension A-4799 | 4798 CJK Ideograph Extension A-4798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479b CJK Ideograph Extension A-479B | 479a CJK Ideograph Extension A-479A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479d CJK Ideograph Extension A-479D | 479c CJK Ideograph Extension A-479C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 479f CJK Ideograph Extension A-479F | 479e CJK Ideograph Extension A-479E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a1 CJK Ideograph Extension A-47A1 | 47a0 CJK Ideograph Extension A-47A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a3 CJK Ideograph Extension A-47A3 | 47a2 CJK Ideograph Extension A-47A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a5 CJK Ideograph Extension A-47A5 | 47a4 CJK Ideograph Extension A-47A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a7 CJK Ideograph Extension A-47A7 | 47a6 CJK Ideograph Extension A-47A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47a9 CJK Ideograph Extension A-47A9 | 47a8 CJK Ideograph Extension A-47A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ab CJK Ideograph Extension A-47AB | 47aa CJK Ideograph Extension A-47AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ad CJK Ideograph Extension A-47AD | 47ac CJK Ideograph Extension A-47AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47af CJK Ideograph Extension A-47AF | 47ae CJK Ideograph Extension A-47AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b1 CJK Ideograph Extension A-47B1 | 47b0 CJK Ideograph Extension A-47B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b3 CJK Ideograph Extension A-47B3 | 47b2 CJK Ideograph Extension A-47B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b5 CJK Ideograph Extension A-47B5 | 47b4 CJK Ideograph Extension A-47B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b7 CJK Ideograph Extension A-47B7 | 47b6 CJK Ideograph Extension A-47B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47b9 CJK Ideograph Extension A-47B9 | 47b8 CJK Ideograph Extension A-47B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bb CJK Ideograph Extension A-47BB | 47ba CJK Ideograph Extension A-47BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bd CJK Ideograph Extension A-47BD | 47bc CJK Ideograph Extension A-47BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47bf CJK Ideograph Extension A-47BF | 47be CJK Ideograph Extension A-47BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c1 CJK Ideograph Extension A-47C1 | 47c0 CJK Ideograph Extension A-47C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c3 CJK Ideograph Extension A-47C3 | 47c2 CJK Ideograph Extension A-47C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c5 CJK Ideograph Extension A-47C5 | 47c4 CJK Ideograph Extension A-47C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c7 CJK Ideograph Extension A-47C7 | 47c6 CJK Ideograph Extension A-47C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47c9 CJK Ideograph Extension A-47C9 | 47c8 CJK Ideograph Extension A-47C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cb CJK Ideograph Extension A-47CB | 47ca CJK Ideograph Extension A-47CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cd CJK Ideograph Extension A-47CD | 47cc CJK Ideograph Extension A-47CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47cf CJK Ideograph Extension A-47CF | 47ce CJK Ideograph Extension A-47CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d1 CJK Ideograph Extension A-47D1 | 47d0 CJK Ideograph Extension A-47D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d3 CJK Ideograph Extension A-47D3 | 47d2 CJK Ideograph Extension A-47D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d5 CJK Ideograph Extension A-47D5 | 47d4 CJK Ideograph Extension A-47D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d7 CJK Ideograph Extension A-47D7 | 47d6 CJK Ideograph Extension A-47D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47d9 CJK Ideograph Extension A-47D9 | 47d8 CJK Ideograph Extension A-47D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47db CJK Ideograph Extension A-47DB | 47da CJK Ideograph Extension A-47DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47dd CJK Ideograph Extension A-47DD | 47dc CJK Ideograph Extension A-47DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47df CJK Ideograph Extension A-47DF | 47de CJK Ideograph Extension A-47DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e1 CJK Ideograph Extension A-47E1 | 47e0 CJK Ideograph Extension A-47E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e3 CJK Ideograph Extension A-47E3 | 47e2 CJK Ideograph Extension A-47E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e5 CJK Ideograph Extension A-47E5 | 47e4 CJK Ideograph Extension A-47E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e7 CJK Ideograph Extension A-47E7 | 47e6 CJK Ideograph Extension A-47E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47e9 CJK Ideograph Extension A-47E9 | 47e8 CJK Ideograph Extension A-47E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47eb CJK Ideograph Extension A-47EB | 47ea CJK Ideograph Extension A-47EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ed CJK Ideograph Extension A-47ED | 47ec CJK Ideograph Extension A-47EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ef CJK Ideograph Extension A-47EF | 47ee CJK Ideograph Extension A-47EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f1 CJK Ideograph Extension A-47F1 | 47f0 CJK Ideograph Extension A-47F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f3 CJK Ideograph Extension A-47F3 | 47f2 CJK Ideograph Extension A-47F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f5 CJK Ideograph Extension A-47F5 | 47f4 CJK Ideograph Extension A-47F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f7 CJK Ideograph Extension A-47F7 | 47f6 CJK Ideograph Extension A-47F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47f9 CJK Ideograph Extension A-47F9 | 47f8 CJK Ideograph Extension A-47F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47fb CJK Ideograph Extension A-47FB | 47fa CJK Ideograph Extension A-47FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47fd CJK Ideograph Extension A-47FD | 47fc CJK Ideograph Extension A-47FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 47ff CJK Ideograph Extension A-47FF | 47fe CJK Ideograph Extension A-47FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4801 CJK Ideograph Extension A-4801 | 4800 CJK Ideograph Extension A-4800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4803 CJK Ideograph Extension A-4803 | 4802 CJK Ideograph Extension A-4802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4805 CJK Ideograph Extension A-4805 | 4804 CJK Ideograph Extension A-4804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4807 CJK Ideograph Extension A-4807 | 4806 CJK Ideograph Extension A-4806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4809 CJK Ideograph Extension A-4809 | 4808 CJK Ideograph Extension A-4808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480b CJK Ideograph Extension A-480B | 480a CJK Ideograph Extension A-480A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480d CJK Ideograph Extension A-480D | 480c CJK Ideograph Extension A-480C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 480f CJK Ideograph Extension A-480F | 480e CJK Ideograph Extension A-480E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4811 CJK Ideograph Extension A-4811 | 4810 CJK Ideograph Extension A-4810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4813 CJK Ideograph Extension A-4813 | 4812 CJK Ideograph Extension A-4812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4815 CJK Ideograph Extension A-4815 | 4814 CJK Ideograph Extension A-4814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4817 CJK Ideograph Extension A-4817 | 4816 CJK Ideograph Extension A-4816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4819 CJK Ideograph Extension A-4819 | 4818 CJK Ideograph Extension A-4818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481b CJK Ideograph Extension A-481B | 481a CJK Ideograph Extension A-481A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481d CJK Ideograph Extension A-481D | 481c CJK Ideograph Extension A-481C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 481f CJK Ideograph Extension A-481F | 481e CJK Ideograph Extension A-481E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4821 CJK Ideograph Extension A-4821 | 4820 CJK Ideograph Extension A-4820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4823 CJK Ideograph Extension A-4823 | 4822 CJK Ideograph Extension A-4822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4825 CJK Ideograph Extension A-4825 | 4824 CJK Ideograph Extension A-4824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4827 CJK Ideograph Extension A-4827 | 4826 CJK Ideograph Extension A-4826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4829 CJK Ideograph Extension A-4829 | 4828 CJK Ideograph Extension A-4828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482b CJK Ideograph Extension A-482B | 482a CJK Ideograph Extension A-482A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482d CJK Ideograph Extension A-482D | 482c CJK Ideograph Extension A-482C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 482f CJK Ideograph Extension A-482F | 482e CJK Ideograph Extension A-482E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4831 CJK Ideograph Extension A-4831 | 4830 CJK Ideograph Extension A-4830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4833 CJK Ideograph Extension A-4833 | 4832 CJK Ideograph Extension A-4832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4835 CJK Ideograph Extension A-4835 | 4834 CJK Ideograph Extension A-4834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4837 CJK Ideograph Extension A-4837 | 4836 CJK Ideograph Extension A-4836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4839 CJK Ideograph Extension A-4839 | 4838 CJK Ideograph Extension A-4838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483b CJK Ideograph Extension A-483B | 483a CJK Ideograph Extension A-483A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483d CJK Ideograph Extension A-483D | 483c CJK Ideograph Extension A-483C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 483f CJK Ideograph Extension A-483F | 483e CJK Ideograph Extension A-483E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4841 CJK Ideograph Extension A-4841 | 4840 CJK Ideograph Extension A-4840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4843 CJK Ideograph Extension A-4843 | 4842 CJK Ideograph Extension A-4842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4845 CJK Ideograph Extension A-4845 | 4844 CJK Ideograph Extension A-4844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4847 CJK Ideograph Extension A-4847 | 4846 CJK Ideograph Extension A-4846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4849 CJK Ideograph Extension A-4849 | 4848 CJK Ideograph Extension A-4848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484b CJK Ideograph Extension A-484B | 484a CJK Ideograph Extension A-484A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484d CJK Ideograph Extension A-484D | 484c CJK Ideograph Extension A-484C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 484f CJK Ideograph Extension A-484F | 484e CJK Ideograph Extension A-484E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4851 CJK Ideograph Extension A-4851 | 4850 CJK Ideograph Extension A-4850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4853 CJK Ideograph Extension A-4853 | 4852 CJK Ideograph Extension A-4852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4855 CJK Ideograph Extension A-4855 | 4854 CJK Ideograph Extension A-4854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4857 CJK Ideograph Extension A-4857 | 4856 CJK Ideograph Extension A-4856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4859 CJK Ideograph Extension A-4859 | 4858 CJK Ideograph Extension A-4858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485b CJK Ideograph Extension A-485B | 485a CJK Ideograph Extension A-485A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485d CJK Ideograph Extension A-485D | 485c CJK Ideograph Extension A-485C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 485f CJK Ideograph Extension A-485F | 485e CJK Ideograph Extension A-485E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4861 CJK Ideograph Extension A-4861 | 4860 CJK Ideograph Extension A-4860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4863 CJK Ideograph Extension A-4863 | 4862 CJK Ideograph Extension A-4862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4865 CJK Ideograph Extension A-4865 | 4864 CJK Ideograph Extension A-4864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4867 CJK Ideograph Extension A-4867 | 4866 CJK Ideograph Extension A-4866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4869 CJK Ideograph Extension A-4869 | 4868 CJK Ideograph Extension A-4868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486b CJK Ideograph Extension A-486B | 486a CJK Ideograph Extension A-486A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486d CJK Ideograph Extension A-486D | 486c CJK Ideograph Extension A-486C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 486f CJK Ideograph Extension A-486F | 486e CJK Ideograph Extension A-486E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4871 CJK Ideograph Extension A-4871 | 4870 CJK Ideograph Extension A-4870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4873 CJK Ideograph Extension A-4873 | 4872 CJK Ideograph Extension A-4872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4875 CJK Ideograph Extension A-4875 | 4874 CJK Ideograph Extension A-4874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4877 CJK Ideograph Extension A-4877 | 4876 CJK Ideograph Extension A-4876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4879 CJK Ideograph Extension A-4879 | 4878 CJK Ideograph Extension A-4878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487b CJK Ideograph Extension A-487B | 487a CJK Ideograph Extension A-487A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487d CJK Ideograph Extension A-487D | 487c CJK Ideograph Extension A-487C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 487f CJK Ideograph Extension A-487F | 487e CJK Ideograph Extension A-487E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4881 CJK Ideograph Extension A-4881 | 4880 CJK Ideograph Extension A-4880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4883 CJK Ideograph Extension A-4883 | 4882 CJK Ideograph Extension A-4882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4885 CJK Ideograph Extension A-4885 | 4884 CJK Ideograph Extension A-4884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4887 CJK Ideograph Extension A-4887 | 4886 CJK Ideograph Extension A-4886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4889 CJK Ideograph Extension A-4889 | 4888 CJK Ideograph Extension A-4888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488b CJK Ideograph Extension A-488B | 488a CJK Ideograph Extension A-488A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488d CJK Ideograph Extension A-488D | 488c CJK Ideograph Extension A-488C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 488f CJK Ideograph Extension A-488F | 488e CJK Ideograph Extension A-488E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4891 CJK Ideograph Extension A-4891 | 4890 CJK Ideograph Extension A-4890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4893 CJK Ideograph Extension A-4893 | 4892 CJK Ideograph Extension A-4892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4895 CJK Ideograph Extension A-4895 | 4894 CJK Ideograph Extension A-4894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4897 CJK Ideograph Extension A-4897 | 4896 CJK Ideograph Extension A-4896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4899 CJK Ideograph Extension A-4899 | 4898 CJK Ideograph Extension A-4898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489b CJK Ideograph Extension A-489B | 489a CJK Ideograph Extension A-489A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489d CJK Ideograph Extension A-489D | 489c CJK Ideograph Extension A-489C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 489f CJK Ideograph Extension A-489F | 489e CJK Ideograph Extension A-489E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a1 CJK Ideograph Extension A-48A1 | 48a0 CJK Ideograph Extension A-48A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a3 CJK Ideograph Extension A-48A3 | 48a2 CJK Ideograph Extension A-48A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a5 CJK Ideograph Extension A-48A5 | 48a4 CJK Ideograph Extension A-48A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a7 CJK Ideograph Extension A-48A7 | 48a6 CJK Ideograph Extension A-48A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48a9 CJK Ideograph Extension A-48A9 | 48a8 CJK Ideograph Extension A-48A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ab CJK Ideograph Extension A-48AB | 48aa CJK Ideograph Extension A-48AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ad CJK Ideograph Extension A-48AD | 48ac CJK Ideograph Extension A-48AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48af CJK Ideograph Extension A-48AF | 48ae CJK Ideograph Extension A-48AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b1 CJK Ideograph Extension A-48B1 | 48b0 CJK Ideograph Extension A-48B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b3 CJK Ideograph Extension A-48B3 | 48b2 CJK Ideograph Extension A-48B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b5 CJK Ideograph Extension A-48B5 | 48b4 CJK Ideograph Extension A-48B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b7 CJK Ideograph Extension A-48B7 | 48b6 CJK Ideograph Extension A-48B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48b9 CJK Ideograph Extension A-48B9 | 48b8 CJK Ideograph Extension A-48B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bb CJK Ideograph Extension A-48BB | 48ba CJK Ideograph Extension A-48BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bd CJK Ideograph Extension A-48BD | 48bc CJK Ideograph Extension A-48BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48bf CJK Ideograph Extension A-48BF | 48be CJK Ideograph Extension A-48BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c1 CJK Ideograph Extension A-48C1 | 48c0 CJK Ideograph Extension A-48C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c3 CJK Ideograph Extension A-48C3 | 48c2 CJK Ideograph Extension A-48C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c5 CJK Ideograph Extension A-48C5 | 48c4 CJK Ideograph Extension A-48C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c7 CJK Ideograph Extension A-48C7 | 48c6 CJK Ideograph Extension A-48C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48c9 CJK Ideograph Extension A-48C9 | 48c8 CJK Ideograph Extension A-48C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cb CJK Ideograph Extension A-48CB | 48ca CJK Ideograph Extension A-48CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cd CJK Ideograph Extension A-48CD | 48cc CJK Ideograph Extension A-48CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48cf CJK Ideograph Extension A-48CF | 48ce CJK Ideograph Extension A-48CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d1 CJK Ideograph Extension A-48D1 | 48d0 CJK Ideograph Extension A-48D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d3 CJK Ideograph Extension A-48D3 | 48d2 CJK Ideograph Extension A-48D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d5 CJK Ideograph Extension A-48D5 | 48d4 CJK Ideograph Extension A-48D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d7 CJK Ideograph Extension A-48D7 | 48d6 CJK Ideograph Extension A-48D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48d9 CJK Ideograph Extension A-48D9 | 48d8 CJK Ideograph Extension A-48D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48db CJK Ideograph Extension A-48DB | 48da CJK Ideograph Extension A-48DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48dd CJK Ideograph Extension A-48DD | 48dc CJK Ideograph Extension A-48DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48df CJK Ideograph Extension A-48DF | 48de CJK Ideograph Extension A-48DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e1 CJK Ideograph Extension A-48E1 | 48e0 CJK Ideograph Extension A-48E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e3 CJK Ideograph Extension A-48E3 | 48e2 CJK Ideograph Extension A-48E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e5 CJK Ideograph Extension A-48E5 | 48e4 CJK Ideograph Extension A-48E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e7 CJK Ideograph Extension A-48E7 | 48e6 CJK Ideograph Extension A-48E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48e9 CJK Ideograph Extension A-48E9 | 48e8 CJK Ideograph Extension A-48E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48eb CJK Ideograph Extension A-48EB | 48ea CJK Ideograph Extension A-48EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ed CJK Ideograph Extension A-48ED | 48ec CJK Ideograph Extension A-48EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ef CJK Ideograph Extension A-48EF | 48ee CJK Ideograph Extension A-48EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f1 CJK Ideograph Extension A-48F1 | 48f0 CJK Ideograph Extension A-48F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f3 CJK Ideograph Extension A-48F3 | 48f2 CJK Ideograph Extension A-48F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f5 CJK Ideograph Extension A-48F5 | 48f4 CJK Ideograph Extension A-48F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f7 CJK Ideograph Extension A-48F7 | 48f6 CJK Ideograph Extension A-48F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48f9 CJK Ideograph Extension A-48F9 | 48f8 CJK Ideograph Extension A-48F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48fb CJK Ideograph Extension A-48FB | 48fa CJK Ideograph Extension A-48FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48fd CJK Ideograph Extension A-48FD | 48fc CJK Ideograph Extension A-48FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 48ff CJK Ideograph Extension A-48FF | 48fe CJK Ideograph Extension A-48FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4901 CJK Ideograph Extension A-4901 | 4900 CJK Ideograph Extension A-4900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4903 CJK Ideograph Extension A-4903 | 4902 CJK Ideograph Extension A-4902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4905 CJK Ideograph Extension A-4905 | 4904 CJK Ideograph Extension A-4904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4907 CJK Ideograph Extension A-4907 | 4906 CJK Ideograph Extension A-4906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4909 CJK Ideograph Extension A-4909 | 4908 CJK Ideograph Extension A-4908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490b CJK Ideograph Extension A-490B | 490a CJK Ideograph Extension A-490A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490d CJK Ideograph Extension A-490D | 490c CJK Ideograph Extension A-490C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 490f CJK Ideograph Extension A-490F | 490e CJK Ideograph Extension A-490E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4911 CJK Ideograph Extension A-4911 | 4910 CJK Ideograph Extension A-4910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4913 CJK Ideograph Extension A-4913 | 4912 CJK Ideograph Extension A-4912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4915 CJK Ideograph Extension A-4915 | 4914 CJK Ideograph Extension A-4914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4917 CJK Ideograph Extension A-4917 | 4916 CJK Ideograph Extension A-4916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4919 CJK Ideograph Extension A-4919 | 4918 CJK Ideograph Extension A-4918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491b CJK Ideograph Extension A-491B | 491a CJK Ideograph Extension A-491A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491d CJK Ideograph Extension A-491D | 491c CJK Ideograph Extension A-491C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 491f CJK Ideograph Extension A-491F | 491e CJK Ideograph Extension A-491E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4921 CJK Ideograph Extension A-4921 | 4920 CJK Ideograph Extension A-4920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4923 CJK Ideograph Extension A-4923 | 4922 CJK Ideograph Extension A-4922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4925 CJK Ideograph Extension A-4925 | 4924 CJK Ideograph Extension A-4924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4927 CJK Ideograph Extension A-4927 | 4926 CJK Ideograph Extension A-4926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4929 CJK Ideograph Extension A-4929 | 4928 CJK Ideograph Extension A-4928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492b CJK Ideograph Extension A-492B | 492a CJK Ideograph Extension A-492A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492d CJK Ideograph Extension A-492D | 492c CJK Ideograph Extension A-492C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 492f CJK Ideograph Extension A-492F | 492e CJK Ideograph Extension A-492E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4931 CJK Ideograph Extension A-4931 | 4930 CJK Ideograph Extension A-4930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4933 CJK Ideograph Extension A-4933 | 4932 CJK Ideograph Extension A-4932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4935 CJK Ideograph Extension A-4935 | 4934 CJK Ideograph Extension A-4934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4937 CJK Ideograph Extension A-4937 | 4936 CJK Ideograph Extension A-4936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4939 CJK Ideograph Extension A-4939 | 4938 CJK Ideograph Extension A-4938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493b CJK Ideograph Extension A-493B | 493a CJK Ideograph Extension A-493A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493d CJK Ideograph Extension A-493D | 493c CJK Ideograph Extension A-493C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 493f CJK Ideograph Extension A-493F | 493e CJK Ideograph Extension A-493E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4941 CJK Ideograph Extension A-4941 | 4940 CJK Ideograph Extension A-4940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4943 CJK Ideograph Extension A-4943 | 4942 CJK Ideograph Extension A-4942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4945 CJK Ideograph Extension A-4945 | 4944 CJK Ideograph Extension A-4944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4947 CJK Ideograph Extension A-4947 | 4946 CJK Ideograph Extension A-4946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4949 CJK Ideograph Extension A-4949 | 4948 CJK Ideograph Extension A-4948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494b CJK Ideograph Extension A-494B | 494a CJK Ideograph Extension A-494A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494d CJK Ideograph Extension A-494D | 494c CJK Ideograph Extension A-494C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 494f CJK Ideograph Extension A-494F | 494e CJK Ideograph Extension A-494E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4951 CJK Ideograph Extension A-4951 | 4950 CJK Ideograph Extension A-4950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4953 CJK Ideograph Extension A-4953 | 4952 CJK Ideograph Extension A-4952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4955 CJK Ideograph Extension A-4955 | 4954 CJK Ideograph Extension A-4954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4957 CJK Ideograph Extension A-4957 | 4956 CJK Ideograph Extension A-4956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4959 CJK Ideograph Extension A-4959 | 4958 CJK Ideograph Extension A-4958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495b CJK Ideograph Extension A-495B | 495a CJK Ideograph Extension A-495A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495d CJK Ideograph Extension A-495D | 495c CJK Ideograph Extension A-495C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 495f CJK Ideograph Extension A-495F | 495e CJK Ideograph Extension A-495E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4961 CJK Ideograph Extension A-4961 | 4960 CJK Ideograph Extension A-4960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4963 CJK Ideograph Extension A-4963 | 4962 CJK Ideograph Extension A-4962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4965 CJK Ideograph Extension A-4965 | 4964 CJK Ideograph Extension A-4964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4967 CJK Ideograph Extension A-4967 | 4966 CJK Ideograph Extension A-4966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4969 CJK Ideograph Extension A-4969 | 4968 CJK Ideograph Extension A-4968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496b CJK Ideograph Extension A-496B | 496a CJK Ideograph Extension A-496A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496d CJK Ideograph Extension A-496D | 496c CJK Ideograph Extension A-496C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 496f CJK Ideograph Extension A-496F | 496e CJK Ideograph Extension A-496E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4971 CJK Ideograph Extension A-4971 | 4970 CJK Ideograph Extension A-4970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4973 CJK Ideograph Extension A-4973 | 4972 CJK Ideograph Extension A-4972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4975 CJK Ideograph Extension A-4975 | 4974 CJK Ideograph Extension A-4974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4977 CJK Ideograph Extension A-4977 | 4976 CJK Ideograph Extension A-4976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4979 CJK Ideograph Extension A-4979 | 4978 CJK Ideograph Extension A-4978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497b CJK Ideograph Extension A-497B | 497a CJK Ideograph Extension A-497A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497d CJK Ideograph Extension A-497D | 497c CJK Ideograph Extension A-497C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 497f CJK Ideograph Extension A-497F | 497e CJK Ideograph Extension A-497E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4981 CJK Ideograph Extension A-4981 | 4980 CJK Ideograph Extension A-4980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4983 CJK Ideograph Extension A-4983 | 4982 CJK Ideograph Extension A-4982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4985 CJK Ideograph Extension A-4985 | 4984 CJK Ideograph Extension A-4984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4987 CJK Ideograph Extension A-4987 | 4986 CJK Ideograph Extension A-4986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4989 CJK Ideograph Extension A-4989 | 4988 CJK Ideograph Extension A-4988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498b CJK Ideograph Extension A-498B | 498a CJK Ideograph Extension A-498A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498d CJK Ideograph Extension A-498D | 498c CJK Ideograph Extension A-498C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 498f CJK Ideograph Extension A-498F | 498e CJK Ideograph Extension A-498E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4991 CJK Ideograph Extension A-4991 | 4990 CJK Ideograph Extension A-4990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4993 CJK Ideograph Extension A-4993 | 4992 CJK Ideograph Extension A-4992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4995 CJK Ideograph Extension A-4995 | 4994 CJK Ideograph Extension A-4994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4997 CJK Ideograph Extension A-4997 | 4996 CJK Ideograph Extension A-4996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4999 CJK Ideograph Extension A-4999 | 4998 CJK Ideograph Extension A-4998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499b CJK Ideograph Extension A-499B | 499a CJK Ideograph Extension A-499A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499d CJK Ideograph Extension A-499D | 499c CJK Ideograph Extension A-499C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 499f CJK Ideograph Extension A-499F | 499e CJK Ideograph Extension A-499E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a1 CJK Ideograph Extension A-49A1 | 49a0 CJK Ideograph Extension A-49A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a3 CJK Ideograph Extension A-49A3 | 49a2 CJK Ideograph Extension A-49A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a5 CJK Ideograph Extension A-49A5 | 49a4 CJK Ideograph Extension A-49A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a7 CJK Ideograph Extension A-49A7 | 49a6 CJK Ideograph Extension A-49A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49a9 CJK Ideograph Extension A-49A9 | 49a8 CJK Ideograph Extension A-49A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ab CJK Ideograph Extension A-49AB | 49aa CJK Ideograph Extension A-49AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ad CJK Ideograph Extension A-49AD | 49ac CJK Ideograph Extension A-49AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49af CJK Ideograph Extension A-49AF | 49ae CJK Ideograph Extension A-49AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b1 CJK Ideograph Extension A-49B1 | 49b0 CJK Ideograph Extension A-49B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b3 CJK Ideograph Extension A-49B3 | 49b2 CJK Ideograph Extension A-49B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b5 CJK Ideograph Extension A-49B5 | 49b4 CJK Ideograph Extension A-49B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b7 CJK Ideograph Extension A-49B7 | 49b6 CJK Ideograph Extension A-49B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49b9 CJK Ideograph Extension A-49B9 | 49b8 CJK Ideograph Extension A-49B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bb CJK Ideograph Extension A-49BB | 49ba CJK Ideograph Extension A-49BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bd CJK Ideograph Extension A-49BD | 49bc CJK Ideograph Extension A-49BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49bf CJK Ideograph Extension A-49BF | 49be CJK Ideograph Extension A-49BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c1 CJK Ideograph Extension A-49C1 | 49c0 CJK Ideograph Extension A-49C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c3 CJK Ideograph Extension A-49C3 | 49c2 CJK Ideograph Extension A-49C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c5 CJK Ideograph Extension A-49C5 | 49c4 CJK Ideograph Extension A-49C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c7 CJK Ideograph Extension A-49C7 | 49c6 CJK Ideograph Extension A-49C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49c9 CJK Ideograph Extension A-49C9 | 49c8 CJK Ideograph Extension A-49C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cb CJK Ideograph Extension A-49CB | 49ca CJK Ideograph Extension A-49CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cd CJK Ideograph Extension A-49CD | 49cc CJK Ideograph Extension A-49CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49cf CJK Ideograph Extension A-49CF | 49ce CJK Ideograph Extension A-49CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d1 CJK Ideograph Extension A-49D1 | 49d0 CJK Ideograph Extension A-49D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d3 CJK Ideograph Extension A-49D3 | 49d2 CJK Ideograph Extension A-49D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d5 CJK Ideograph Extension A-49D5 | 49d4 CJK Ideograph Extension A-49D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d7 CJK Ideograph Extension A-49D7 | 49d6 CJK Ideograph Extension A-49D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49d9 CJK Ideograph Extension A-49D9 | 49d8 CJK Ideograph Extension A-49D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49db CJK Ideograph Extension A-49DB | 49da CJK Ideograph Extension A-49DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49dd CJK Ideograph Extension A-49DD | 49dc CJK Ideograph Extension A-49DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49df CJK Ideograph Extension A-49DF | 49de CJK Ideograph Extension A-49DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e1 CJK Ideograph Extension A-49E1 | 49e0 CJK Ideograph Extension A-49E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e3 CJK Ideograph Extension A-49E3 | 49e2 CJK Ideograph Extension A-49E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e5 CJK Ideograph Extension A-49E5 | 49e4 CJK Ideograph Extension A-49E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e7 CJK Ideograph Extension A-49E7 | 49e6 CJK Ideograph Extension A-49E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49e9 CJK Ideograph Extension A-49E9 | 49e8 CJK Ideograph Extension A-49E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49eb CJK Ideograph Extension A-49EB | 49ea CJK Ideograph Extension A-49EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ed CJK Ideograph Extension A-49ED | 49ec CJK Ideograph Extension A-49EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ef CJK Ideograph Extension A-49EF | 49ee CJK Ideograph Extension A-49EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f1 CJK Ideograph Extension A-49F1 | 49f0 CJK Ideograph Extension A-49F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f3 CJK Ideograph Extension A-49F3 | 49f2 CJK Ideograph Extension A-49F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f5 CJK Ideograph Extension A-49F5 | 49f4 CJK Ideograph Extension A-49F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f7 CJK Ideograph Extension A-49F7 | 49f6 CJK Ideograph Extension A-49F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49f9 CJK Ideograph Extension A-49F9 | 49f8 CJK Ideograph Extension A-49F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49fb CJK Ideograph Extension A-49FB | 49fa CJK Ideograph Extension A-49FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49fd CJK Ideograph Extension A-49FD | 49fc CJK Ideograph Extension A-49FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 49ff CJK Ideograph Extension A-49FF | 49fe CJK Ideograph Extension A-49FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a01 CJK Ideograph Extension A-4A01 | 4a00 CJK Ideograph Extension A-4A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a03 CJK Ideograph Extension A-4A03 | 4a02 CJK Ideograph Extension A-4A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a05 CJK Ideograph Extension A-4A05 | 4a04 CJK Ideograph Extension A-4A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a07 CJK Ideograph Extension A-4A07 | 4a06 CJK Ideograph Extension A-4A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a09 CJK Ideograph Extension A-4A09 | 4a08 CJK Ideograph Extension A-4A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0b CJK Ideograph Extension A-4A0B | 4a0a CJK Ideograph Extension A-4A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0d CJK Ideograph Extension A-4A0D | 4a0c CJK Ideograph Extension A-4A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a0f CJK Ideograph Extension A-4A0F | 4a0e CJK Ideograph Extension A-4A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a11 CJK Ideograph Extension A-4A11 | 4a10 CJK Ideograph Extension A-4A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a13 CJK Ideograph Extension A-4A13 | 4a12 CJK Ideograph Extension A-4A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a15 CJK Ideograph Extension A-4A15 | 4a14 CJK Ideograph Extension A-4A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a17 CJK Ideograph Extension A-4A17 | 4a16 CJK Ideograph Extension A-4A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a19 CJK Ideograph Extension A-4A19 | 4a18 CJK Ideograph Extension A-4A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1b CJK Ideograph Extension A-4A1B | 4a1a CJK Ideograph Extension A-4A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1d CJK Ideograph Extension A-4A1D | 4a1c CJK Ideograph Extension A-4A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a1f CJK Ideograph Extension A-4A1F | 4a1e CJK Ideograph Extension A-4A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a21 CJK Ideograph Extension A-4A21 | 4a20 CJK Ideograph Extension A-4A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a23 CJK Ideograph Extension A-4A23 | 4a22 CJK Ideograph Extension A-4A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a25 CJK Ideograph Extension A-4A25 | 4a24 CJK Ideograph Extension A-4A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a27 CJK Ideograph Extension A-4A27 | 4a26 CJK Ideograph Extension A-4A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a29 CJK Ideograph Extension A-4A29 | 4a28 CJK Ideograph Extension A-4A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2b CJK Ideograph Extension A-4A2B | 4a2a CJK Ideograph Extension A-4A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2d CJK Ideograph Extension A-4A2D | 4a2c CJK Ideograph Extension A-4A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a2f CJK Ideograph Extension A-4A2F | 4a2e CJK Ideograph Extension A-4A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a31 CJK Ideograph Extension A-4A31 | 4a30 CJK Ideograph Extension A-4A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a33 CJK Ideograph Extension A-4A33 | 4a32 CJK Ideograph Extension A-4A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a35 CJK Ideograph Extension A-4A35 | 4a34 CJK Ideograph Extension A-4A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a37 CJK Ideograph Extension A-4A37 | 4a36 CJK Ideograph Extension A-4A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a39 CJK Ideograph Extension A-4A39 | 4a38 CJK Ideograph Extension A-4A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3b CJK Ideograph Extension A-4A3B | 4a3a CJK Ideograph Extension A-4A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3d CJK Ideograph Extension A-4A3D | 4a3c CJK Ideograph Extension A-4A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a3f CJK Ideograph Extension A-4A3F | 4a3e CJK Ideograph Extension A-4A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a41 CJK Ideograph Extension A-4A41 | 4a40 CJK Ideograph Extension A-4A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a43 CJK Ideograph Extension A-4A43 | 4a42 CJK Ideograph Extension A-4A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a45 CJK Ideograph Extension A-4A45 | 4a44 CJK Ideograph Extension A-4A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a47 CJK Ideograph Extension A-4A47 | 4a46 CJK Ideograph Extension A-4A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a49 CJK Ideograph Extension A-4A49 | 4a48 CJK Ideograph Extension A-4A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4b CJK Ideograph Extension A-4A4B | 4a4a CJK Ideograph Extension A-4A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4d CJK Ideograph Extension A-4A4D | 4a4c CJK Ideograph Extension A-4A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a4f CJK Ideograph Extension A-4A4F | 4a4e CJK Ideograph Extension A-4A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a51 CJK Ideograph Extension A-4A51 | 4a50 CJK Ideograph Extension A-4A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a53 CJK Ideograph Extension A-4A53 | 4a52 CJK Ideograph Extension A-4A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a55 CJK Ideograph Extension A-4A55 | 4a54 CJK Ideograph Extension A-4A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a57 CJK Ideograph Extension A-4A57 | 4a56 CJK Ideograph Extension A-4A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a59 CJK Ideograph Extension A-4A59 | 4a58 CJK Ideograph Extension A-4A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5b CJK Ideograph Extension A-4A5B | 4a5a CJK Ideograph Extension A-4A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5d CJK Ideograph Extension A-4A5D | 4a5c CJK Ideograph Extension A-4A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a5f CJK Ideograph Extension A-4A5F | 4a5e CJK Ideograph Extension A-4A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a61 CJK Ideograph Extension A-4A61 | 4a60 CJK Ideograph Extension A-4A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a63 CJK Ideograph Extension A-4A63 | 4a62 CJK Ideograph Extension A-4A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a65 CJK Ideograph Extension A-4A65 | 4a64 CJK Ideograph Extension A-4A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a67 CJK Ideograph Extension A-4A67 | 4a66 CJK Ideograph Extension A-4A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a69 CJK Ideograph Extension A-4A69 | 4a68 CJK Ideograph Extension A-4A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6b CJK Ideograph Extension A-4A6B | 4a6a CJK Ideograph Extension A-4A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6d CJK Ideograph Extension A-4A6D | 4a6c CJK Ideograph Extension A-4A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a6f CJK Ideograph Extension A-4A6F | 4a6e CJK Ideograph Extension A-4A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a71 CJK Ideograph Extension A-4A71 | 4a70 CJK Ideograph Extension A-4A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a73 CJK Ideograph Extension A-4A73 | 4a72 CJK Ideograph Extension A-4A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a75 CJK Ideograph Extension A-4A75 | 4a74 CJK Ideograph Extension A-4A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a77 CJK Ideograph Extension A-4A77 | 4a76 CJK Ideograph Extension A-4A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a79 CJK Ideograph Extension A-4A79 | 4a78 CJK Ideograph Extension A-4A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7b CJK Ideograph Extension A-4A7B | 4a7a CJK Ideograph Extension A-4A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7d CJK Ideograph Extension A-4A7D | 4a7c CJK Ideograph Extension A-4A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a7f CJK Ideograph Extension A-4A7F | 4a7e CJK Ideograph Extension A-4A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a81 CJK Ideograph Extension A-4A81 | 4a80 CJK Ideograph Extension A-4A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a83 CJK Ideograph Extension A-4A83 | 4a82 CJK Ideograph Extension A-4A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a85 CJK Ideograph Extension A-4A85 | 4a84 CJK Ideograph Extension A-4A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a87 CJK Ideograph Extension A-4A87 | 4a86 CJK Ideograph Extension A-4A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a89 CJK Ideograph Extension A-4A89 | 4a88 CJK Ideograph Extension A-4A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8b CJK Ideograph Extension A-4A8B | 4a8a CJK Ideograph Extension A-4A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8d CJK Ideograph Extension A-4A8D | 4a8c CJK Ideograph Extension A-4A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a8f CJK Ideograph Extension A-4A8F | 4a8e CJK Ideograph Extension A-4A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a91 CJK Ideograph Extension A-4A91 | 4a90 CJK Ideograph Extension A-4A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a93 CJK Ideograph Extension A-4A93 | 4a92 CJK Ideograph Extension A-4A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a95 CJK Ideograph Extension A-4A95 | 4a94 CJK Ideograph Extension A-4A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a97 CJK Ideograph Extension A-4A97 | 4a96 CJK Ideograph Extension A-4A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a99 CJK Ideograph Extension A-4A99 | 4a98 CJK Ideograph Extension A-4A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9b CJK Ideograph Extension A-4A9B | 4a9a CJK Ideograph Extension A-4A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9d CJK Ideograph Extension A-4A9D | 4a9c CJK Ideograph Extension A-4A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4a9f CJK Ideograph Extension A-4A9F | 4a9e CJK Ideograph Extension A-4A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa1 CJK Ideograph Extension A-4AA1 | 4aa0 CJK Ideograph Extension A-4AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa3 CJK Ideograph Extension A-4AA3 | 4aa2 CJK Ideograph Extension A-4AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa5 CJK Ideograph Extension A-4AA5 | 4aa4 CJK Ideograph Extension A-4AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa7 CJK Ideograph Extension A-4AA7 | 4aa6 CJK Ideograph Extension A-4AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aa9 CJK Ideograph Extension A-4AA9 | 4aa8 CJK Ideograph Extension A-4AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aab CJK Ideograph Extension A-4AAB | 4aaa CJK Ideograph Extension A-4AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aad CJK Ideograph Extension A-4AAD | 4aac CJK Ideograph Extension A-4AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aaf CJK Ideograph Extension A-4AAF | 4aae CJK Ideograph Extension A-4AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab1 CJK Ideograph Extension A-4AB1 | 4ab0 CJK Ideograph Extension A-4AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab3 CJK Ideograph Extension A-4AB3 | 4ab2 CJK Ideograph Extension A-4AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab5 CJK Ideograph Extension A-4AB5 | 4ab4 CJK Ideograph Extension A-4AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab7 CJK Ideograph Extension A-4AB7 | 4ab6 CJK Ideograph Extension A-4AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ab9 CJK Ideograph Extension A-4AB9 | 4ab8 CJK Ideograph Extension A-4AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abb CJK Ideograph Extension A-4ABB | 4aba CJK Ideograph Extension A-4ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abd CJK Ideograph Extension A-4ABD | 4abc CJK Ideograph Extension A-4ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4abf CJK Ideograph Extension A-4ABF | 4abe CJK Ideograph Extension A-4ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac1 CJK Ideograph Extension A-4AC1 | 4ac0 CJK Ideograph Extension A-4AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac3 CJK Ideograph Extension A-4AC3 | 4ac2 CJK Ideograph Extension A-4AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac5 CJK Ideograph Extension A-4AC5 | 4ac4 CJK Ideograph Extension A-4AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac7 CJK Ideograph Extension A-4AC7 | 4ac6 CJK Ideograph Extension A-4AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ac9 CJK Ideograph Extension A-4AC9 | 4ac8 CJK Ideograph Extension A-4AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acb CJK Ideograph Extension A-4ACB | 4aca CJK Ideograph Extension A-4ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acd CJK Ideograph Extension A-4ACD | 4acc CJK Ideograph Extension A-4ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4acf CJK Ideograph Extension A-4ACF | 4ace CJK Ideograph Extension A-4ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad1 CJK Ideograph Extension A-4AD1 | 4ad0 CJK Ideograph Extension A-4AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad3 CJK Ideograph Extension A-4AD3 | 4ad2 CJK Ideograph Extension A-4AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad5 CJK Ideograph Extension A-4AD5 | 4ad4 CJK Ideograph Extension A-4AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad7 CJK Ideograph Extension A-4AD7 | 4ad6 CJK Ideograph Extension A-4AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ad9 CJK Ideograph Extension A-4AD9 | 4ad8 CJK Ideograph Extension A-4AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4adb CJK Ideograph Extension A-4ADB | 4ada CJK Ideograph Extension A-4ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4add CJK Ideograph Extension A-4ADD | 4adc CJK Ideograph Extension A-4ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4adf CJK Ideograph Extension A-4ADF | 4ade CJK Ideograph Extension A-4ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae1 CJK Ideograph Extension A-4AE1 | 4ae0 CJK Ideograph Extension A-4AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae3 CJK Ideograph Extension A-4AE3 | 4ae2 CJK Ideograph Extension A-4AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae5 CJK Ideograph Extension A-4AE5 | 4ae4 CJK Ideograph Extension A-4AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae7 CJK Ideograph Extension A-4AE7 | 4ae6 CJK Ideograph Extension A-4AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ae9 CJK Ideograph Extension A-4AE9 | 4ae8 CJK Ideograph Extension A-4AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aeb CJK Ideograph Extension A-4AEB | 4aea CJK Ideograph Extension A-4AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aed CJK Ideograph Extension A-4AED | 4aec CJK Ideograph Extension A-4AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aef CJK Ideograph Extension A-4AEF | 4aee CJK Ideograph Extension A-4AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af1 CJK Ideograph Extension A-4AF1 | 4af0 CJK Ideograph Extension A-4AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af3 CJK Ideograph Extension A-4AF3 | 4af2 CJK Ideograph Extension A-4AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af5 CJK Ideograph Extension A-4AF5 | 4af4 CJK Ideograph Extension A-4AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af7 CJK Ideograph Extension A-4AF7 | 4af6 CJK Ideograph Extension A-4AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4af9 CJK Ideograph Extension A-4AF9 | 4af8 CJK Ideograph Extension A-4AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4afb CJK Ideograph Extension A-4AFB | 4afa CJK Ideograph Extension A-4AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4afd CJK Ideograph Extension A-4AFD | 4afc CJK Ideograph Extension A-4AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4aff CJK Ideograph Extension A-4AFF | 4afe CJK Ideograph Extension A-4AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b01 CJK Ideograph Extension A-4B01 | 4b00 CJK Ideograph Extension A-4B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b03 CJK Ideograph Extension A-4B03 | 4b02 CJK Ideograph Extension A-4B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b05 CJK Ideograph Extension A-4B05 | 4b04 CJK Ideograph Extension A-4B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b07 CJK Ideograph Extension A-4B07 | 4b06 CJK Ideograph Extension A-4B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b09 CJK Ideograph Extension A-4B09 | 4b08 CJK Ideograph Extension A-4B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0b CJK Ideograph Extension A-4B0B | 4b0a CJK Ideograph Extension A-4B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0d CJK Ideograph Extension A-4B0D | 4b0c CJK Ideograph Extension A-4B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b0f CJK Ideograph Extension A-4B0F | 4b0e CJK Ideograph Extension A-4B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b11 CJK Ideograph Extension A-4B11 | 4b10 CJK Ideograph Extension A-4B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b13 CJK Ideograph Extension A-4B13 | 4b12 CJK Ideograph Extension A-4B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b15 CJK Ideograph Extension A-4B15 | 4b14 CJK Ideograph Extension A-4B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b17 CJK Ideograph Extension A-4B17 | 4b16 CJK Ideograph Extension A-4B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b19 CJK Ideograph Extension A-4B19 | 4b18 CJK Ideograph Extension A-4B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1b CJK Ideograph Extension A-4B1B | 4b1a CJK Ideograph Extension A-4B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1d CJK Ideograph Extension A-4B1D | 4b1c CJK Ideograph Extension A-4B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b1f CJK Ideograph Extension A-4B1F | 4b1e CJK Ideograph Extension A-4B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b21 CJK Ideograph Extension A-4B21 | 4b20 CJK Ideograph Extension A-4B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b23 CJK Ideograph Extension A-4B23 | 4b22 CJK Ideograph Extension A-4B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b25 CJK Ideograph Extension A-4B25 | 4b24 CJK Ideograph Extension A-4B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b27 CJK Ideograph Extension A-4B27 | 4b26 CJK Ideograph Extension A-4B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b29 CJK Ideograph Extension A-4B29 | 4b28 CJK Ideograph Extension A-4B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2b CJK Ideograph Extension A-4B2B | 4b2a CJK Ideograph Extension A-4B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2d CJK Ideograph Extension A-4B2D | 4b2c CJK Ideograph Extension A-4B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b2f CJK Ideograph Extension A-4B2F | 4b2e CJK Ideograph Extension A-4B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b31 CJK Ideograph Extension A-4B31 | 4b30 CJK Ideograph Extension A-4B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b33 CJK Ideograph Extension A-4B33 | 4b32 CJK Ideograph Extension A-4B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b35 CJK Ideograph Extension A-4B35 | 4b34 CJK Ideograph Extension A-4B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b37 CJK Ideograph Extension A-4B37 | 4b36 CJK Ideograph Extension A-4B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b39 CJK Ideograph Extension A-4B39 | 4b38 CJK Ideograph Extension A-4B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3b CJK Ideograph Extension A-4B3B | 4b3a CJK Ideograph Extension A-4B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3d CJK Ideograph Extension A-4B3D | 4b3c CJK Ideograph Extension A-4B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b3f CJK Ideograph Extension A-4B3F | 4b3e CJK Ideograph Extension A-4B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b41 CJK Ideograph Extension A-4B41 | 4b40 CJK Ideograph Extension A-4B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b43 CJK Ideograph Extension A-4B43 | 4b42 CJK Ideograph Extension A-4B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b45 CJK Ideograph Extension A-4B45 | 4b44 CJK Ideograph Extension A-4B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b47 CJK Ideograph Extension A-4B47 | 4b46 CJK Ideograph Extension A-4B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b49 CJK Ideograph Extension A-4B49 | 4b48 CJK Ideograph Extension A-4B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4b CJK Ideograph Extension A-4B4B | 4b4a CJK Ideograph Extension A-4B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4d CJK Ideograph Extension A-4B4D | 4b4c CJK Ideograph Extension A-4B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b4f CJK Ideograph Extension A-4B4F | 4b4e CJK Ideograph Extension A-4B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b51 CJK Ideograph Extension A-4B51 | 4b50 CJK Ideograph Extension A-4B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b53 CJK Ideograph Extension A-4B53 | 4b52 CJK Ideograph Extension A-4B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b55 CJK Ideograph Extension A-4B55 | 4b54 CJK Ideograph Extension A-4B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b57 CJK Ideograph Extension A-4B57 | 4b56 CJK Ideograph Extension A-4B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b59 CJK Ideograph Extension A-4B59 | 4b58 CJK Ideograph Extension A-4B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5b CJK Ideograph Extension A-4B5B | 4b5a CJK Ideograph Extension A-4B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5d CJK Ideograph Extension A-4B5D | 4b5c CJK Ideograph Extension A-4B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b5f CJK Ideograph Extension A-4B5F | 4b5e CJK Ideograph Extension A-4B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b61 CJK Ideograph Extension A-4B61 | 4b60 CJK Ideograph Extension A-4B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b63 CJK Ideograph Extension A-4B63 | 4b62 CJK Ideograph Extension A-4B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b65 CJK Ideograph Extension A-4B65 | 4b64 CJK Ideograph Extension A-4B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b67 CJK Ideograph Extension A-4B67 | 4b66 CJK Ideograph Extension A-4B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b69 CJK Ideograph Extension A-4B69 | 4b68 CJK Ideograph Extension A-4B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6b CJK Ideograph Extension A-4B6B | 4b6a CJK Ideograph Extension A-4B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6d CJK Ideograph Extension A-4B6D | 4b6c CJK Ideograph Extension A-4B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b6f CJK Ideograph Extension A-4B6F | 4b6e CJK Ideograph Extension A-4B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b71 CJK Ideograph Extension A-4B71 | 4b70 CJK Ideograph Extension A-4B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b73 CJK Ideograph Extension A-4B73 | 4b72 CJK Ideograph Extension A-4B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b75 CJK Ideograph Extension A-4B75 | 4b74 CJK Ideograph Extension A-4B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b77 CJK Ideograph Extension A-4B77 | 4b76 CJK Ideograph Extension A-4B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b79 CJK Ideograph Extension A-4B79 | 4b78 CJK Ideograph Extension A-4B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7b CJK Ideograph Extension A-4B7B | 4b7a CJK Ideograph Extension A-4B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7d CJK Ideograph Extension A-4B7D | 4b7c CJK Ideograph Extension A-4B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b7f CJK Ideograph Extension A-4B7F | 4b7e CJK Ideograph Extension A-4B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b81 CJK Ideograph Extension A-4B81 | 4b80 CJK Ideograph Extension A-4B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b83 CJK Ideograph Extension A-4B83 | 4b82 CJK Ideograph Extension A-4B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b85 CJK Ideograph Extension A-4B85 | 4b84 CJK Ideograph Extension A-4B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b87 CJK Ideograph Extension A-4B87 | 4b86 CJK Ideograph Extension A-4B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b89 CJK Ideograph Extension A-4B89 | 4b88 CJK Ideograph Extension A-4B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8b CJK Ideograph Extension A-4B8B | 4b8a CJK Ideograph Extension A-4B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8d CJK Ideograph Extension A-4B8D | 4b8c CJK Ideograph Extension A-4B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b8f CJK Ideograph Extension A-4B8F | 4b8e CJK Ideograph Extension A-4B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b91 CJK Ideograph Extension A-4B91 | 4b90 CJK Ideograph Extension A-4B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b93 CJK Ideograph Extension A-4B93 | 4b92 CJK Ideograph Extension A-4B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b95 CJK Ideograph Extension A-4B95 | 4b94 CJK Ideograph Extension A-4B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b97 CJK Ideograph Extension A-4B97 | 4b96 CJK Ideograph Extension A-4B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b99 CJK Ideograph Extension A-4B99 | 4b98 CJK Ideograph Extension A-4B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9b CJK Ideograph Extension A-4B9B | 4b9a CJK Ideograph Extension A-4B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9d CJK Ideograph Extension A-4B9D | 4b9c CJK Ideograph Extension A-4B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4b9f CJK Ideograph Extension A-4B9F | 4b9e CJK Ideograph Extension A-4B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba1 CJK Ideograph Extension A-4BA1 | 4ba0 CJK Ideograph Extension A-4BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba3 CJK Ideograph Extension A-4BA3 | 4ba2 CJK Ideograph Extension A-4BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba5 CJK Ideograph Extension A-4BA5 | 4ba4 CJK Ideograph Extension A-4BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba7 CJK Ideograph Extension A-4BA7 | 4ba6 CJK Ideograph Extension A-4BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ba9 CJK Ideograph Extension A-4BA9 | 4ba8 CJK Ideograph Extension A-4BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bab CJK Ideograph Extension A-4BAB | 4baa CJK Ideograph Extension A-4BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bad CJK Ideograph Extension A-4BAD | 4bac CJK Ideograph Extension A-4BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4baf CJK Ideograph Extension A-4BAF | 4bae CJK Ideograph Extension A-4BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb1 CJK Ideograph Extension A-4BB1 | 4bb0 CJK Ideograph Extension A-4BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb3 CJK Ideograph Extension A-4BB3 | 4bb2 CJK Ideograph Extension A-4BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb5 CJK Ideograph Extension A-4BB5 | 4bb4 CJK Ideograph Extension A-4BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb7 CJK Ideograph Extension A-4BB7 | 4bb6 CJK Ideograph Extension A-4BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bb9 CJK Ideograph Extension A-4BB9 | 4bb8 CJK Ideograph Extension A-4BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbb CJK Ideograph Extension A-4BBB | 4bba CJK Ideograph Extension A-4BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbd CJK Ideograph Extension A-4BBD | 4bbc CJK Ideograph Extension A-4BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bbf CJK Ideograph Extension A-4BBF | 4bbe CJK Ideograph Extension A-4BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc1 CJK Ideograph Extension A-4BC1 | 4bc0 CJK Ideograph Extension A-4BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc3 CJK Ideograph Extension A-4BC3 | 4bc2 CJK Ideograph Extension A-4BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc5 CJK Ideograph Extension A-4BC5 | 4bc4 CJK Ideograph Extension A-4BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc7 CJK Ideograph Extension A-4BC7 | 4bc6 CJK Ideograph Extension A-4BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bc9 CJK Ideograph Extension A-4BC9 | 4bc8 CJK Ideograph Extension A-4BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcb CJK Ideograph Extension A-4BCB | 4bca CJK Ideograph Extension A-4BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcd CJK Ideograph Extension A-4BCD | 4bcc CJK Ideograph Extension A-4BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bcf CJK Ideograph Extension A-4BCF | 4bce CJK Ideograph Extension A-4BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd1 CJK Ideograph Extension A-4BD1 | 4bd0 CJK Ideograph Extension A-4BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd3 CJK Ideograph Extension A-4BD3 | 4bd2 CJK Ideograph Extension A-4BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd5 CJK Ideograph Extension A-4BD5 | 4bd4 CJK Ideograph Extension A-4BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd7 CJK Ideograph Extension A-4BD7 | 4bd6 CJK Ideograph Extension A-4BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bd9 CJK Ideograph Extension A-4BD9 | 4bd8 CJK Ideograph Extension A-4BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdb CJK Ideograph Extension A-4BDB | 4bda CJK Ideograph Extension A-4BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdd CJK Ideograph Extension A-4BDD | 4bdc CJK Ideograph Extension A-4BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bdf CJK Ideograph Extension A-4BDF | 4bde CJK Ideograph Extension A-4BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be1 CJK Ideograph Extension A-4BE1 | 4be0 CJK Ideograph Extension A-4BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be3 CJK Ideograph Extension A-4BE3 | 4be2 CJK Ideograph Extension A-4BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be5 CJK Ideograph Extension A-4BE5 | 4be4 CJK Ideograph Extension A-4BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be7 CJK Ideograph Extension A-4BE7 | 4be6 CJK Ideograph Extension A-4BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4be9 CJK Ideograph Extension A-4BE9 | 4be8 CJK Ideograph Extension A-4BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4beb CJK Ideograph Extension A-4BEB | 4bea CJK Ideograph Extension A-4BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bed CJK Ideograph Extension A-4BED | 4bec CJK Ideograph Extension A-4BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bef CJK Ideograph Extension A-4BEF | 4bee CJK Ideograph Extension A-4BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf1 CJK Ideograph Extension A-4BF1 | 4bf0 CJK Ideograph Extension A-4BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf3 CJK Ideograph Extension A-4BF3 | 4bf2 CJK Ideograph Extension A-4BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf5 CJK Ideograph Extension A-4BF5 | 4bf4 CJK Ideograph Extension A-4BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf7 CJK Ideograph Extension A-4BF7 | 4bf6 CJK Ideograph Extension A-4BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bf9 CJK Ideograph Extension A-4BF9 | 4bf8 CJK Ideograph Extension A-4BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bfb CJK Ideograph Extension A-4BFB | 4bfa CJK Ideograph Extension A-4BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bfd CJK Ideograph Extension A-4BFD | 4bfc CJK Ideograph Extension A-4BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4bff CJK Ideograph Extension A-4BFF | 4bfe CJK Ideograph Extension A-4BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c01 CJK Ideograph Extension A-4C01 | 4c00 CJK Ideograph Extension A-4C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c03 CJK Ideograph Extension A-4C03 | 4c02 CJK Ideograph Extension A-4C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c05 CJK Ideograph Extension A-4C05 | 4c04 CJK Ideograph Extension A-4C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c07 CJK Ideograph Extension A-4C07 | 4c06 CJK Ideograph Extension A-4C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c09 CJK Ideograph Extension A-4C09 | 4c08 CJK Ideograph Extension A-4C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0b CJK Ideograph Extension A-4C0B | 4c0a CJK Ideograph Extension A-4C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0d CJK Ideograph Extension A-4C0D | 4c0c CJK Ideograph Extension A-4C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c0f CJK Ideograph Extension A-4C0F | 4c0e CJK Ideograph Extension A-4C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c11 CJK Ideograph Extension A-4C11 | 4c10 CJK Ideograph Extension A-4C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c13 CJK Ideograph Extension A-4C13 | 4c12 CJK Ideograph Extension A-4C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c15 CJK Ideograph Extension A-4C15 | 4c14 CJK Ideograph Extension A-4C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c17 CJK Ideograph Extension A-4C17 | 4c16 CJK Ideograph Extension A-4C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c19 CJK Ideograph Extension A-4C19 | 4c18 CJK Ideograph Extension A-4C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1b CJK Ideograph Extension A-4C1B | 4c1a CJK Ideograph Extension A-4C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1d CJK Ideograph Extension A-4C1D | 4c1c CJK Ideograph Extension A-4C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c1f CJK Ideograph Extension A-4C1F | 4c1e CJK Ideograph Extension A-4C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c21 CJK Ideograph Extension A-4C21 | 4c20 CJK Ideograph Extension A-4C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c23 CJK Ideograph Extension A-4C23 | 4c22 CJK Ideograph Extension A-4C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c25 CJK Ideograph Extension A-4C25 | 4c24 CJK Ideograph Extension A-4C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c27 CJK Ideograph Extension A-4C27 | 4c26 CJK Ideograph Extension A-4C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c29 CJK Ideograph Extension A-4C29 | 4c28 CJK Ideograph Extension A-4C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2b CJK Ideograph Extension A-4C2B | 4c2a CJK Ideograph Extension A-4C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2d CJK Ideograph Extension A-4C2D | 4c2c CJK Ideograph Extension A-4C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c2f CJK Ideograph Extension A-4C2F | 4c2e CJK Ideograph Extension A-4C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c31 CJK Ideograph Extension A-4C31 | 4c30 CJK Ideograph Extension A-4C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c33 CJK Ideograph Extension A-4C33 | 4c32 CJK Ideograph Extension A-4C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c35 CJK Ideograph Extension A-4C35 | 4c34 CJK Ideograph Extension A-4C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c37 CJK Ideograph Extension A-4C37 | 4c36 CJK Ideograph Extension A-4C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c39 CJK Ideograph Extension A-4C39 | 4c38 CJK Ideograph Extension A-4C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3b CJK Ideograph Extension A-4C3B | 4c3a CJK Ideograph Extension A-4C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3d CJK Ideograph Extension A-4C3D | 4c3c CJK Ideograph Extension A-4C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c3f CJK Ideograph Extension A-4C3F | 4c3e CJK Ideograph Extension A-4C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c41 CJK Ideograph Extension A-4C41 | 4c40 CJK Ideograph Extension A-4C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c43 CJK Ideograph Extension A-4C43 | 4c42 CJK Ideograph Extension A-4C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c45 CJK Ideograph Extension A-4C45 | 4c44 CJK Ideograph Extension A-4C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c47 CJK Ideograph Extension A-4C47 | 4c46 CJK Ideograph Extension A-4C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c49 CJK Ideograph Extension A-4C49 | 4c48 CJK Ideograph Extension A-4C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4b CJK Ideograph Extension A-4C4B | 4c4a CJK Ideograph Extension A-4C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4d CJK Ideograph Extension A-4C4D | 4c4c CJK Ideograph Extension A-4C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c4f CJK Ideograph Extension A-4C4F | 4c4e CJK Ideograph Extension A-4C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c51 CJK Ideograph Extension A-4C51 | 4c50 CJK Ideograph Extension A-4C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c53 CJK Ideograph Extension A-4C53 | 4c52 CJK Ideograph Extension A-4C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c55 CJK Ideograph Extension A-4C55 | 4c54 CJK Ideograph Extension A-4C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c57 CJK Ideograph Extension A-4C57 | 4c56 CJK Ideograph Extension A-4C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c59 CJK Ideograph Extension A-4C59 | 4c58 CJK Ideograph Extension A-4C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5b CJK Ideograph Extension A-4C5B | 4c5a CJK Ideograph Extension A-4C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5d CJK Ideograph Extension A-4C5D | 4c5c CJK Ideograph Extension A-4C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c5f CJK Ideograph Extension A-4C5F | 4c5e CJK Ideograph Extension A-4C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c61 CJK Ideograph Extension A-4C61 | 4c60 CJK Ideograph Extension A-4C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c63 CJK Ideograph Extension A-4C63 | 4c62 CJK Ideograph Extension A-4C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c65 CJK Ideograph Extension A-4C65 | 4c64 CJK Ideograph Extension A-4C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c67 CJK Ideograph Extension A-4C67 | 4c66 CJK Ideograph Extension A-4C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c69 CJK Ideograph Extension A-4C69 | 4c68 CJK Ideograph Extension A-4C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6b CJK Ideograph Extension A-4C6B | 4c6a CJK Ideograph Extension A-4C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6d CJK Ideograph Extension A-4C6D | 4c6c CJK Ideograph Extension A-4C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c6f CJK Ideograph Extension A-4C6F | 4c6e CJK Ideograph Extension A-4C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c71 CJK Ideograph Extension A-4C71 | 4c70 CJK Ideograph Extension A-4C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c73 CJK Ideograph Extension A-4C73 | 4c72 CJK Ideograph Extension A-4C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c75 CJK Ideograph Extension A-4C75 | 4c74 CJK Ideograph Extension A-4C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c77 CJK Ideograph Extension A-4C77 | 4c76 CJK Ideograph Extension A-4C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c79 CJK Ideograph Extension A-4C79 | 4c78 CJK Ideograph Extension A-4C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7b CJK Ideograph Extension A-4C7B | 4c7a CJK Ideograph Extension A-4C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7d CJK Ideograph Extension A-4C7D | 4c7c CJK Ideograph Extension A-4C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c7f CJK Ideograph Extension A-4C7F | 4c7e CJK Ideograph Extension A-4C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c81 CJK Ideograph Extension A-4C81 | 4c80 CJK Ideograph Extension A-4C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c83 CJK Ideograph Extension A-4C83 | 4c82 CJK Ideograph Extension A-4C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c85 CJK Ideograph Extension A-4C85 | 4c84 CJK Ideograph Extension A-4C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c87 CJK Ideograph Extension A-4C87 | 4c86 CJK Ideograph Extension A-4C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c89 CJK Ideograph Extension A-4C89 | 4c88 CJK Ideograph Extension A-4C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8b CJK Ideograph Extension A-4C8B | 4c8a CJK Ideograph Extension A-4C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8d CJK Ideograph Extension A-4C8D | 4c8c CJK Ideograph Extension A-4C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c8f CJK Ideograph Extension A-4C8F | 4c8e CJK Ideograph Extension A-4C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c91 CJK Ideograph Extension A-4C91 | 4c90 CJK Ideograph Extension A-4C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c93 CJK Ideograph Extension A-4C93 | 4c92 CJK Ideograph Extension A-4C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c95 CJK Ideograph Extension A-4C95 | 4c94 CJK Ideograph Extension A-4C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c97 CJK Ideograph Extension A-4C97 | 4c96 CJK Ideograph Extension A-4C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c99 CJK Ideograph Extension A-4C99 | 4c98 CJK Ideograph Extension A-4C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9b CJK Ideograph Extension A-4C9B | 4c9a CJK Ideograph Extension A-4C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9d CJK Ideograph Extension A-4C9D | 4c9c CJK Ideograph Extension A-4C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4c9f CJK Ideograph Extension A-4C9F | 4c9e CJK Ideograph Extension A-4C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca1 CJK Ideograph Extension A-4CA1 | 4ca0 CJK Ideograph Extension A-4CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca3 CJK Ideograph Extension A-4CA3 | 4ca2 CJK Ideograph Extension A-4CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca5 CJK Ideograph Extension A-4CA5 | 4ca4 CJK Ideograph Extension A-4CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca7 CJK Ideograph Extension A-4CA7 | 4ca6 CJK Ideograph Extension A-4CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ca9 CJK Ideograph Extension A-4CA9 | 4ca8 CJK Ideograph Extension A-4CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cab CJK Ideograph Extension A-4CAB | 4caa CJK Ideograph Extension A-4CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cad CJK Ideograph Extension A-4CAD | 4cac CJK Ideograph Extension A-4CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4caf CJK Ideograph Extension A-4CAF | 4cae CJK Ideograph Extension A-4CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb1 CJK Ideograph Extension A-4CB1 | 4cb0 CJK Ideograph Extension A-4CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb3 CJK Ideograph Extension A-4CB3 | 4cb2 CJK Ideograph Extension A-4CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb5 CJK Ideograph Extension A-4CB5 | 4cb4 CJK Ideograph Extension A-4CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb7 CJK Ideograph Extension A-4CB7 | 4cb6 CJK Ideograph Extension A-4CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cb9 CJK Ideograph Extension A-4CB9 | 4cb8 CJK Ideograph Extension A-4CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbb CJK Ideograph Extension A-4CBB | 4cba CJK Ideograph Extension A-4CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbd CJK Ideograph Extension A-4CBD | 4cbc CJK Ideograph Extension A-4CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cbf CJK Ideograph Extension A-4CBF | 4cbe CJK Ideograph Extension A-4CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc1 CJK Ideograph Extension A-4CC1 | 4cc0 CJK Ideograph Extension A-4CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc3 CJK Ideograph Extension A-4CC3 | 4cc2 CJK Ideograph Extension A-4CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc5 CJK Ideograph Extension A-4CC5 | 4cc4 CJK Ideograph Extension A-4CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc7 CJK Ideograph Extension A-4CC7 | 4cc6 CJK Ideograph Extension A-4CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cc9 CJK Ideograph Extension A-4CC9 | 4cc8 CJK Ideograph Extension A-4CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccb CJK Ideograph Extension A-4CCB | 4cca CJK Ideograph Extension A-4CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccd CJK Ideograph Extension A-4CCD | 4ccc CJK Ideograph Extension A-4CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ccf CJK Ideograph Extension A-4CCF | 4cce CJK Ideograph Extension A-4CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd1 CJK Ideograph Extension A-4CD1 | 4cd0 CJK Ideograph Extension A-4CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd3 CJK Ideograph Extension A-4CD3 | 4cd2 CJK Ideograph Extension A-4CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd5 CJK Ideograph Extension A-4CD5 | 4cd4 CJK Ideograph Extension A-4CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd7 CJK Ideograph Extension A-4CD7 | 4cd6 CJK Ideograph Extension A-4CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cd9 CJK Ideograph Extension A-4CD9 | 4cd8 CJK Ideograph Extension A-4CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdb CJK Ideograph Extension A-4CDB | 4cda CJK Ideograph Extension A-4CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdd CJK Ideograph Extension A-4CDD | 4cdc CJK Ideograph Extension A-4CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cdf CJK Ideograph Extension A-4CDF | 4cde CJK Ideograph Extension A-4CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce1 CJK Ideograph Extension A-4CE1 | 4ce0 CJK Ideograph Extension A-4CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce3 CJK Ideograph Extension A-4CE3 | 4ce2 CJK Ideograph Extension A-4CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce5 CJK Ideograph Extension A-4CE5 | 4ce4 CJK Ideograph Extension A-4CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce7 CJK Ideograph Extension A-4CE7 | 4ce6 CJK Ideograph Extension A-4CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ce9 CJK Ideograph Extension A-4CE9 | 4ce8 CJK Ideograph Extension A-4CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ceb CJK Ideograph Extension A-4CEB | 4cea CJK Ideograph Extension A-4CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ced CJK Ideograph Extension A-4CED | 4cec CJK Ideograph Extension A-4CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cef CJK Ideograph Extension A-4CEF | 4cee CJK Ideograph Extension A-4CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf1 CJK Ideograph Extension A-4CF1 | 4cf0 CJK Ideograph Extension A-4CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf3 CJK Ideograph Extension A-4CF3 | 4cf2 CJK Ideograph Extension A-4CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf5 CJK Ideograph Extension A-4CF5 | 4cf4 CJK Ideograph Extension A-4CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf7 CJK Ideograph Extension A-4CF7 | 4cf6 CJK Ideograph Extension A-4CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cf9 CJK Ideograph Extension A-4CF9 | 4cf8 CJK Ideograph Extension A-4CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cfb CJK Ideograph Extension A-4CFB | 4cfa CJK Ideograph Extension A-4CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cfd CJK Ideograph Extension A-4CFD | 4cfc CJK Ideograph Extension A-4CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4cff CJK Ideograph Extension A-4CFF | 4cfe CJK Ideograph Extension A-4CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d01 CJK Ideograph Extension A-4D01 | 4d00 CJK Ideograph Extension A-4D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d03 CJK Ideograph Extension A-4D03 | 4d02 CJK Ideograph Extension A-4D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d05 CJK Ideograph Extension A-4D05 | 4d04 CJK Ideograph Extension A-4D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d07 CJK Ideograph Extension A-4D07 | 4d06 CJK Ideograph Extension A-4D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d09 CJK Ideograph Extension A-4D09 | 4d08 CJK Ideograph Extension A-4D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0b CJK Ideograph Extension A-4D0B | 4d0a CJK Ideograph Extension A-4D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0d CJK Ideograph Extension A-4D0D | 4d0c CJK Ideograph Extension A-4D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d0f CJK Ideograph Extension A-4D0F | 4d0e CJK Ideograph Extension A-4D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d11 CJK Ideograph Extension A-4D11 | 4d10 CJK Ideograph Extension A-4D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d13 CJK Ideograph Extension A-4D13 | 4d12 CJK Ideograph Extension A-4D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d15 CJK Ideograph Extension A-4D15 | 4d14 CJK Ideograph Extension A-4D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d17 CJK Ideograph Extension A-4D17 | 4d16 CJK Ideograph Extension A-4D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d19 CJK Ideograph Extension A-4D19 | 4d18 CJK Ideograph Extension A-4D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1b CJK Ideograph Extension A-4D1B | 4d1a CJK Ideograph Extension A-4D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1d CJK Ideograph Extension A-4D1D | 4d1c CJK Ideograph Extension A-4D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d1f CJK Ideograph Extension A-4D1F | 4d1e CJK Ideograph Extension A-4D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d21 CJK Ideograph Extension A-4D21 | 4d20 CJK Ideograph Extension A-4D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d23 CJK Ideograph Extension A-4D23 | 4d22 CJK Ideograph Extension A-4D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d25 CJK Ideograph Extension A-4D25 | 4d24 CJK Ideograph Extension A-4D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d27 CJK Ideograph Extension A-4D27 | 4d26 CJK Ideograph Extension A-4D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d29 CJK Ideograph Extension A-4D29 | 4d28 CJK Ideograph Extension A-4D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2b CJK Ideograph Extension A-4D2B | 4d2a CJK Ideograph Extension A-4D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2d CJK Ideograph Extension A-4D2D | 4d2c CJK Ideograph Extension A-4D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d2f CJK Ideograph Extension A-4D2F | 4d2e CJK Ideograph Extension A-4D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d31 CJK Ideograph Extension A-4D31 | 4d30 CJK Ideograph Extension A-4D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d33 CJK Ideograph Extension A-4D33 | 4d32 CJK Ideograph Extension A-4D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d35 CJK Ideograph Extension A-4D35 | 4d34 CJK Ideograph Extension A-4D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d37 CJK Ideograph Extension A-4D37 | 4d36 CJK Ideograph Extension A-4D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d39 CJK Ideograph Extension A-4D39 | 4d38 CJK Ideograph Extension A-4D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3b CJK Ideograph Extension A-4D3B | 4d3a CJK Ideograph Extension A-4D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3d CJK Ideograph Extension A-4D3D | 4d3c CJK Ideograph Extension A-4D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d3f CJK Ideograph Extension A-4D3F | 4d3e CJK Ideograph Extension A-4D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d41 CJK Ideograph Extension A-4D41 | 4d40 CJK Ideograph Extension A-4D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d43 CJK Ideograph Extension A-4D43 | 4d42 CJK Ideograph Extension A-4D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d45 CJK Ideograph Extension A-4D45 | 4d44 CJK Ideograph Extension A-4D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d47 CJK Ideograph Extension A-4D47 | 4d46 CJK Ideograph Extension A-4D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d49 CJK Ideograph Extension A-4D49 | 4d48 CJK Ideograph Extension A-4D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4b CJK Ideograph Extension A-4D4B | 4d4a CJK Ideograph Extension A-4D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4d CJK Ideograph Extension A-4D4D | 4d4c CJK Ideograph Extension A-4D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d4f CJK Ideograph Extension A-4D4F | 4d4e CJK Ideograph Extension A-4D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d51 CJK Ideograph Extension A-4D51 | 4d50 CJK Ideograph Extension A-4D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d53 CJK Ideograph Extension A-4D53 | 4d52 CJK Ideograph Extension A-4D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d55 CJK Ideograph Extension A-4D55 | 4d54 CJK Ideograph Extension A-4D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d57 CJK Ideograph Extension A-4D57 | 4d56 CJK Ideograph Extension A-4D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d59 CJK Ideograph Extension A-4D59 | 4d58 CJK Ideograph Extension A-4D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5b CJK Ideograph Extension A-4D5B | 4d5a CJK Ideograph Extension A-4D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5d CJK Ideograph Extension A-4D5D | 4d5c CJK Ideograph Extension A-4D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d5f CJK Ideograph Extension A-4D5F | 4d5e CJK Ideograph Extension A-4D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d61 CJK Ideograph Extension A-4D61 | 4d60 CJK Ideograph Extension A-4D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d63 CJK Ideograph Extension A-4D63 | 4d62 CJK Ideograph Extension A-4D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d65 CJK Ideograph Extension A-4D65 | 4d64 CJK Ideograph Extension A-4D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d67 CJK Ideograph Extension A-4D67 | 4d66 CJK Ideograph Extension A-4D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d69 CJK Ideograph Extension A-4D69 | 4d68 CJK Ideograph Extension A-4D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6b CJK Ideograph Extension A-4D6B | 4d6a CJK Ideograph Extension A-4D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6d CJK Ideograph Extension A-4D6D | 4d6c CJK Ideograph Extension A-4D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d6f CJK Ideograph Extension A-4D6F | 4d6e CJK Ideograph Extension A-4D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d71 CJK Ideograph Extension A-4D71 | 4d70 CJK Ideograph Extension A-4D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d73 CJK Ideograph Extension A-4D73 | 4d72 CJK Ideograph Extension A-4D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d75 CJK Ideograph Extension A-4D75 | 4d74 CJK Ideograph Extension A-4D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d77 CJK Ideograph Extension A-4D77 | 4d76 CJK Ideograph Extension A-4D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d79 CJK Ideograph Extension A-4D79 | 4d78 CJK Ideograph Extension A-4D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7b CJK Ideograph Extension A-4D7B | 4d7a CJK Ideograph Extension A-4D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7d CJK Ideograph Extension A-4D7D | 4d7c CJK Ideograph Extension A-4D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d7f CJK Ideograph Extension A-4D7F | 4d7e CJK Ideograph Extension A-4D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d81 CJK Ideograph Extension A-4D81 | 4d80 CJK Ideograph Extension A-4D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d83 CJK Ideograph Extension A-4D83 | 4d82 CJK Ideograph Extension A-4D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d85 CJK Ideograph Extension A-4D85 | 4d84 CJK Ideograph Extension A-4D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d87 CJK Ideograph Extension A-4D87 | 4d86 CJK Ideograph Extension A-4D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d89 CJK Ideograph Extension A-4D89 | 4d88 CJK Ideograph Extension A-4D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8b CJK Ideograph Extension A-4D8B | 4d8a CJK Ideograph Extension A-4D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8d CJK Ideograph Extension A-4D8D | 4d8c CJK Ideograph Extension A-4D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d8f CJK Ideograph Extension A-4D8F | 4d8e CJK Ideograph Extension A-4D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d91 CJK Ideograph Extension A-4D91 | 4d90 CJK Ideograph Extension A-4D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d93 CJK Ideograph Extension A-4D93 | 4d92 CJK Ideograph Extension A-4D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d95 CJK Ideograph Extension A-4D95 | 4d94 CJK Ideograph Extension A-4D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d97 CJK Ideograph Extension A-4D97 | 4d96 CJK Ideograph Extension A-4D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d99 CJK Ideograph Extension A-4D99 | 4d98 CJK Ideograph Extension A-4D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9b CJK Ideograph Extension A-4D9B | 4d9a CJK Ideograph Extension A-4D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9d CJK Ideograph Extension A-4D9D | 4d9c CJK Ideograph Extension A-4D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4d9f CJK Ideograph Extension A-4D9F | 4d9e CJK Ideograph Extension A-4D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da1 CJK Ideograph Extension A-4DA1 | 4da0 CJK Ideograph Extension A-4DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da3 CJK Ideograph Extension A-4DA3 | 4da2 CJK Ideograph Extension A-4DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da5 CJK Ideograph Extension A-4DA5 | 4da4 CJK Ideograph Extension A-4DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da7 CJK Ideograph Extension A-4DA7 | 4da6 CJK Ideograph Extension A-4DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4da9 CJK Ideograph Extension A-4DA9 | 4da8 CJK Ideograph Extension A-4DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4dab CJK Ideograph Extension A-4DAB | 4daa CJK Ideograph Extension A-4DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4dad CJK Ideograph Extension A-4DAD | 4dac CJK Ideograph Extension A-4DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4daf CJK Ideograph Extension A-4DAF | 4dae CJK Ideograph Extension A-4DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db1 CJK Ideograph Extension A-4DB1 | 4db0 CJK Ideograph Extension A-4DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db3 CJK Ideograph Extension A-4DB3 | 4db2 CJK Ideograph Extension A-4DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4db5 CJK Ideograph Extension A-4DB5 | 4db4 CJK Ideograph Extension A-4DB4 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4db7 (null) | 4db6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4db9 (null) | 4db8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbb (null) | 4dba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbd (null) | 4dbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 4dbf (null) | 4dbe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc1 HEXAGRAM FOR THE RECEPTIVE EARTH | 4dc0 HEXAGRAM FOR THE CREATIVE HEAVEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc3 HEXAGRAM FOR YOUTHFUL FOLLY | 4dc2 HEXAGRAM FOR DIFFICULTY AT THE BEGINNIN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc5 HEXAGRAM FOR CONFLICT | 4dc4 HEXAGRAM FOR WAITING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc7 HEXAGRAM FOR HOLDING TOGETHER | 4dc6 HEXAGRAM FOR THE ARMY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dc9 HEXAGRAM FOR TREADING | 4dc8 HEXAGRAM FOR SMALL TAMING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcb HEXAGRAM FOR STANDSTILL | 4dca HEXAGRAM FOR PEACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcd HEXAGRAM FOR GREAT POSSESSION | 4dcc HEXAGRAM FOR FELLOWSHIP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dcf HEXAGRAM FOR ENTHUSIASM | 4dce HEXAGRAM FOR MODESTY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd1 HEXAGRAM FOR WORK ON THE DECAYED | 4dd0 HEXAGRAM FOR FOLLOWING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd3 HEXAGRAM FOR CONTEMPLATION | 4dd2 HEXAGRAM FOR APPROACH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd5 HEXAGRAM FOR GRACE | 4dd4 HEXAGRAM FOR BITING THROUGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd7 HEXAGRAM FOR RETURN | 4dd6 HEXAGRAM FOR SPLITTING APART */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dd9 HEXAGRAM FOR GREAT TAMING | 4dd8 HEXAGRAM FOR INNOCENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddb HEXAGRAM FOR GREAT PREPONDERANCE | 4dda HEXAGRAM FOR MOUTH CORNERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddd HEXAGRAM FOR THE CLINGING FIRE | 4ddc HEXAGRAM FOR THE ABYSMAL WATER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ddf HEXAGRAM FOR DURATION | 4dde HEXAGRAM FOR INFLUENCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de1 HEXAGRAM FOR GREAT POWER | 4de0 HEXAGRAM FOR RETREAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de3 HEXAGRAM FOR DARKENING OF THE LIGHT | 4de2 HEXAGRAM FOR PROGRESS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de5 HEXAGRAM FOR OPPOSITION | 4de4 HEXAGRAM FOR THE FAMILY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de7 HEXAGRAM FOR DELIVERANCE | 4de6 HEXAGRAM FOR OBSTRUCTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4de9 HEXAGRAM FOR INCREASE | 4de8 HEXAGRAM FOR DECREASE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4deb HEXAGRAM FOR COMING TO MEET | 4dea HEXAGRAM FOR BREAKTHROUGH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4ded HEXAGRAM FOR PUSHING UPWARD | 4dec HEXAGRAM FOR GATHERING TOGETHER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4def HEXAGRAM FOR THE WELL | 4dee HEXAGRAM FOR OPPRESSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df1 HEXAGRAM FOR THE CAULDRON | 4df0 HEXAGRAM FOR REVOLUTION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df3 HEXAGRAM FOR THE KEEPING STILL MOUNTAIN | 4df2 HEXAGRAM FOR THE AROUSING THUNDER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df5 HEXAGRAM FOR THE MARRYING MAIDEN | 4df4 HEXAGRAM FOR DEVELOPMENT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df7 HEXAGRAM FOR THE WANDERER | 4df6 HEXAGRAM FOR ABUNDANCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4df9 HEXAGRAM FOR THE JOYOUS LAKE | 4df8 HEXAGRAM FOR THE GENTLE WIND */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dfb HEXAGRAM FOR LIMITATION | 4dfa HEXAGRAM FOR DISPERSION */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dfd HEXAGRAM FOR SMALL PREPONDERANCE | 4dfc HEXAGRAM FOR INNER TRUTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* 4dff HEXAGRAM FOR BEFORE COMPLETION | 4dfe HEXAGRAM FOR AFTER COMPLETION */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e01 CJK Ideograph-4E01 | 4e00 CJK Ideograph-4E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e03 CJK Ideograph-4E03 | 4e02 CJK Ideograph-4E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e05 CJK Ideograph-4E05 | 4e04 CJK Ideograph-4E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e07 CJK Ideograph-4E07 | 4e06 CJK Ideograph-4E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e09 CJK Ideograph-4E09 | 4e08 CJK Ideograph-4E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0b CJK Ideograph-4E0B | 4e0a CJK Ideograph-4E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0d CJK Ideograph-4E0D | 4e0c CJK Ideograph-4E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e0f CJK Ideograph-4E0F | 4e0e CJK Ideograph-4E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e11 CJK Ideograph-4E11 | 4e10 CJK Ideograph-4E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e13 CJK Ideograph-4E13 | 4e12 CJK Ideograph-4E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e15 CJK Ideograph-4E15 | 4e14 CJK Ideograph-4E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e17 CJK Ideograph-4E17 | 4e16 CJK Ideograph-4E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e19 CJK Ideograph-4E19 | 4e18 CJK Ideograph-4E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1b CJK Ideograph-4E1B | 4e1a CJK Ideograph-4E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1d CJK Ideograph-4E1D | 4e1c CJK Ideograph-4E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e1f CJK Ideograph-4E1F | 4e1e CJK Ideograph-4E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e21 CJK Ideograph-4E21 | 4e20 CJK Ideograph-4E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e23 CJK Ideograph-4E23 | 4e22 CJK Ideograph-4E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e25 CJK Ideograph-4E25 | 4e24 CJK Ideograph-4E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e27 CJK Ideograph-4E27 | 4e26 CJK Ideograph-4E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e29 CJK Ideograph-4E29 | 4e28 CJK Ideograph-4E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2b CJK Ideograph-4E2B | 4e2a CJK Ideograph-4E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2d CJK Ideograph-4E2D | 4e2c CJK Ideograph-4E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e2f CJK Ideograph-4E2F | 4e2e CJK Ideograph-4E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e31 CJK Ideograph-4E31 | 4e30 CJK Ideograph-4E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e33 CJK Ideograph-4E33 | 4e32 CJK Ideograph-4E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e35 CJK Ideograph-4E35 | 4e34 CJK Ideograph-4E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e37 CJK Ideograph-4E37 | 4e36 CJK Ideograph-4E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e39 CJK Ideograph-4E39 | 4e38 CJK Ideograph-4E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3b CJK Ideograph-4E3B | 4e3a CJK Ideograph-4E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3d CJK Ideograph-4E3D | 4e3c CJK Ideograph-4E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e3f CJK Ideograph-4E3F | 4e3e CJK Ideograph-4E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e41 CJK Ideograph-4E41 | 4e40 CJK Ideograph-4E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e43 CJK Ideograph-4E43 | 4e42 CJK Ideograph-4E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e45 CJK Ideograph-4E45 | 4e44 CJK Ideograph-4E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e47 CJK Ideograph-4E47 | 4e46 CJK Ideograph-4E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e49 CJK Ideograph-4E49 | 4e48 CJK Ideograph-4E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4b CJK Ideograph-4E4B | 4e4a CJK Ideograph-4E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4d CJK Ideograph-4E4D | 4e4c CJK Ideograph-4E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e4f CJK Ideograph-4E4F | 4e4e CJK Ideograph-4E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e51 CJK Ideograph-4E51 | 4e50 CJK Ideograph-4E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e53 CJK Ideograph-4E53 | 4e52 CJK Ideograph-4E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e55 CJK Ideograph-4E55 | 4e54 CJK Ideograph-4E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e57 CJK Ideograph-4E57 | 4e56 CJK Ideograph-4E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e59 CJK Ideograph-4E59 | 4e58 CJK Ideograph-4E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5b CJK Ideograph-4E5B | 4e5a CJK Ideograph-4E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5d CJK Ideograph-4E5D | 4e5c CJK Ideograph-4E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e5f CJK Ideograph-4E5F | 4e5e CJK Ideograph-4E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e61 CJK Ideograph-4E61 | 4e60 CJK Ideograph-4E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e63 CJK Ideograph-4E63 | 4e62 CJK Ideograph-4E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e65 CJK Ideograph-4E65 | 4e64 CJK Ideograph-4E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e67 CJK Ideograph-4E67 | 4e66 CJK Ideograph-4E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e69 CJK Ideograph-4E69 | 4e68 CJK Ideograph-4E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6b CJK Ideograph-4E6B | 4e6a CJK Ideograph-4E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6d CJK Ideograph-4E6D | 4e6c CJK Ideograph-4E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e6f CJK Ideograph-4E6F | 4e6e CJK Ideograph-4E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e71 CJK Ideograph-4E71 | 4e70 CJK Ideograph-4E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e73 CJK Ideograph-4E73 | 4e72 CJK Ideograph-4E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e75 CJK Ideograph-4E75 | 4e74 CJK Ideograph-4E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e77 CJK Ideograph-4E77 | 4e76 CJK Ideograph-4E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e79 CJK Ideograph-4E79 | 4e78 CJK Ideograph-4E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7b CJK Ideograph-4E7B | 4e7a CJK Ideograph-4E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7d CJK Ideograph-4E7D | 4e7c CJK Ideograph-4E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e7f CJK Ideograph-4E7F | 4e7e CJK Ideograph-4E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e81 CJK Ideograph-4E81 | 4e80 CJK Ideograph-4E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e83 CJK Ideograph-4E83 | 4e82 CJK Ideograph-4E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e85 CJK Ideograph-4E85 | 4e84 CJK Ideograph-4E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e87 CJK Ideograph-4E87 | 4e86 CJK Ideograph-4E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e89 CJK Ideograph-4E89 | 4e88 CJK Ideograph-4E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8b CJK Ideograph-4E8B | 4e8a CJK Ideograph-4E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8d CJK Ideograph-4E8D | 4e8c CJK Ideograph-4E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e8f CJK Ideograph-4E8F | 4e8e CJK Ideograph-4E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e91 CJK Ideograph-4E91 | 4e90 CJK Ideograph-4E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e93 CJK Ideograph-4E93 | 4e92 CJK Ideograph-4E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e95 CJK Ideograph-4E95 | 4e94 CJK Ideograph-4E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e97 CJK Ideograph-4E97 | 4e96 CJK Ideograph-4E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e99 CJK Ideograph-4E99 | 4e98 CJK Ideograph-4E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9b CJK Ideograph-4E9B | 4e9a CJK Ideograph-4E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9d CJK Ideograph-4E9D | 4e9c CJK Ideograph-4E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4e9f CJK Ideograph-4E9F | 4e9e CJK Ideograph-4E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea1 CJK Ideograph-4EA1 | 4ea0 CJK Ideograph-4EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea3 CJK Ideograph-4EA3 | 4ea2 CJK Ideograph-4EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea5 CJK Ideograph-4EA5 | 4ea4 CJK Ideograph-4EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea7 CJK Ideograph-4EA7 | 4ea6 CJK Ideograph-4EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ea9 CJK Ideograph-4EA9 | 4ea8 CJK Ideograph-4EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eab CJK Ideograph-4EAB | 4eaa CJK Ideograph-4EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ead CJK Ideograph-4EAD | 4eac CJK Ideograph-4EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eaf CJK Ideograph-4EAF | 4eae CJK Ideograph-4EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb1 CJK Ideograph-4EB1 | 4eb0 CJK Ideograph-4EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb3 CJK Ideograph-4EB3 | 4eb2 CJK Ideograph-4EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb5 CJK Ideograph-4EB5 | 4eb4 CJK Ideograph-4EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb7 CJK Ideograph-4EB7 | 4eb6 CJK Ideograph-4EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eb9 CJK Ideograph-4EB9 | 4eb8 CJK Ideograph-4EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebb CJK Ideograph-4EBB | 4eba CJK Ideograph-4EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebd CJK Ideograph-4EBD | 4ebc CJK Ideograph-4EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ebf CJK Ideograph-4EBF | 4ebe CJK Ideograph-4EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec1 CJK Ideograph-4EC1 | 4ec0 CJK Ideograph-4EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec3 CJK Ideograph-4EC3 | 4ec2 CJK Ideograph-4EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec5 CJK Ideograph-4EC5 | 4ec4 CJK Ideograph-4EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec7 CJK Ideograph-4EC7 | 4ec6 CJK Ideograph-4EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ec9 CJK Ideograph-4EC9 | 4ec8 CJK Ideograph-4EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecb CJK Ideograph-4ECB | 4eca CJK Ideograph-4ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecd CJK Ideograph-4ECD | 4ecc CJK Ideograph-4ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ecf CJK Ideograph-4ECF | 4ece CJK Ideograph-4ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed1 CJK Ideograph-4ED1 | 4ed0 CJK Ideograph-4ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed3 CJK Ideograph-4ED3 | 4ed2 CJK Ideograph-4ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed5 CJK Ideograph-4ED5 | 4ed4 CJK Ideograph-4ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed7 CJK Ideograph-4ED7 | 4ed6 CJK Ideograph-4ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ed9 CJK Ideograph-4ED9 | 4ed8 CJK Ideograph-4ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edb CJK Ideograph-4EDB | 4eda CJK Ideograph-4EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edd CJK Ideograph-4EDD | 4edc CJK Ideograph-4EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4edf CJK Ideograph-4EDF | 4ede CJK Ideograph-4EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee1 CJK Ideograph-4EE1 | 4ee0 CJK Ideograph-4EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee3 CJK Ideograph-4EE3 | 4ee2 CJK Ideograph-4EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee5 CJK Ideograph-4EE5 | 4ee4 CJK Ideograph-4EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee7 CJK Ideograph-4EE7 | 4ee6 CJK Ideograph-4EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ee9 CJK Ideograph-4EE9 | 4ee8 CJK Ideograph-4EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eeb CJK Ideograph-4EEB | 4eea CJK Ideograph-4EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eed CJK Ideograph-4EED | 4eec CJK Ideograph-4EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eef CJK Ideograph-4EEF | 4eee CJK Ideograph-4EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef1 CJK Ideograph-4EF1 | 4ef0 CJK Ideograph-4EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef3 CJK Ideograph-4EF3 | 4ef2 CJK Ideograph-4EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef5 CJK Ideograph-4EF5 | 4ef4 CJK Ideograph-4EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef7 CJK Ideograph-4EF7 | 4ef6 CJK Ideograph-4EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ef9 CJK Ideograph-4EF9 | 4ef8 CJK Ideograph-4EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4efb CJK Ideograph-4EFB | 4efa CJK Ideograph-4EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4efd CJK Ideograph-4EFD | 4efc CJK Ideograph-4EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4eff CJK Ideograph-4EFF | 4efe CJK Ideograph-4EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f01 CJK Ideograph-4F01 | 4f00 CJK Ideograph-4F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f03 CJK Ideograph-4F03 | 4f02 CJK Ideograph-4F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f05 CJK Ideograph-4F05 | 4f04 CJK Ideograph-4F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f07 CJK Ideograph-4F07 | 4f06 CJK Ideograph-4F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f09 CJK Ideograph-4F09 | 4f08 CJK Ideograph-4F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0b CJK Ideograph-4F0B | 4f0a CJK Ideograph-4F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0d CJK Ideograph-4F0D | 4f0c CJK Ideograph-4F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f0f CJK Ideograph-4F0F | 4f0e CJK Ideograph-4F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f11 CJK Ideograph-4F11 | 4f10 CJK Ideograph-4F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f13 CJK Ideograph-4F13 | 4f12 CJK Ideograph-4F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f15 CJK Ideograph-4F15 | 4f14 CJK Ideograph-4F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f17 CJK Ideograph-4F17 | 4f16 CJK Ideograph-4F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f19 CJK Ideograph-4F19 | 4f18 CJK Ideograph-4F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1b CJK Ideograph-4F1B | 4f1a CJK Ideograph-4F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1d CJK Ideograph-4F1D | 4f1c CJK Ideograph-4F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f1f CJK Ideograph-4F1F | 4f1e CJK Ideograph-4F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f21 CJK Ideograph-4F21 | 4f20 CJK Ideograph-4F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f23 CJK Ideograph-4F23 | 4f22 CJK Ideograph-4F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f25 CJK Ideograph-4F25 | 4f24 CJK Ideograph-4F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f27 CJK Ideograph-4F27 | 4f26 CJK Ideograph-4F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f29 CJK Ideograph-4F29 | 4f28 CJK Ideograph-4F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2b CJK Ideograph-4F2B | 4f2a CJK Ideograph-4F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2d CJK Ideograph-4F2D | 4f2c CJK Ideograph-4F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f2f CJK Ideograph-4F2F | 4f2e CJK Ideograph-4F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f31 CJK Ideograph-4F31 | 4f30 CJK Ideograph-4F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f33 CJK Ideograph-4F33 | 4f32 CJK Ideograph-4F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f35 CJK Ideograph-4F35 | 4f34 CJK Ideograph-4F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f37 CJK Ideograph-4F37 | 4f36 CJK Ideograph-4F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f39 CJK Ideograph-4F39 | 4f38 CJK Ideograph-4F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3b CJK Ideograph-4F3B | 4f3a CJK Ideograph-4F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3d CJK Ideograph-4F3D | 4f3c CJK Ideograph-4F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f3f CJK Ideograph-4F3F | 4f3e CJK Ideograph-4F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f41 CJK Ideograph-4F41 | 4f40 CJK Ideograph-4F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f43 CJK Ideograph-4F43 | 4f42 CJK Ideograph-4F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f45 CJK Ideograph-4F45 | 4f44 CJK Ideograph-4F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f47 CJK Ideograph-4F47 | 4f46 CJK Ideograph-4F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f49 CJK Ideograph-4F49 | 4f48 CJK Ideograph-4F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4b CJK Ideograph-4F4B | 4f4a CJK Ideograph-4F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4d CJK Ideograph-4F4D | 4f4c CJK Ideograph-4F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f4f CJK Ideograph-4F4F | 4f4e CJK Ideograph-4F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f51 CJK Ideograph-4F51 | 4f50 CJK Ideograph-4F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f53 CJK Ideograph-4F53 | 4f52 CJK Ideograph-4F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f55 CJK Ideograph-4F55 | 4f54 CJK Ideograph-4F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f57 CJK Ideograph-4F57 | 4f56 CJK Ideograph-4F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f59 CJK Ideograph-4F59 | 4f58 CJK Ideograph-4F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5b CJK Ideograph-4F5B | 4f5a CJK Ideograph-4F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5d CJK Ideograph-4F5D | 4f5c CJK Ideograph-4F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f5f CJK Ideograph-4F5F | 4f5e CJK Ideograph-4F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f61 CJK Ideograph-4F61 | 4f60 CJK Ideograph-4F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f63 CJK Ideograph-4F63 | 4f62 CJK Ideograph-4F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f65 CJK Ideograph-4F65 | 4f64 CJK Ideograph-4F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f67 CJK Ideograph-4F67 | 4f66 CJK Ideograph-4F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f69 CJK Ideograph-4F69 | 4f68 CJK Ideograph-4F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6b CJK Ideograph-4F6B | 4f6a CJK Ideograph-4F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6d CJK Ideograph-4F6D | 4f6c CJK Ideograph-4F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f6f CJK Ideograph-4F6F | 4f6e CJK Ideograph-4F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f71 CJK Ideograph-4F71 | 4f70 CJK Ideograph-4F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f73 CJK Ideograph-4F73 | 4f72 CJK Ideograph-4F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f75 CJK Ideograph-4F75 | 4f74 CJK Ideograph-4F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f77 CJK Ideograph-4F77 | 4f76 CJK Ideograph-4F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f79 CJK Ideograph-4F79 | 4f78 CJK Ideograph-4F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7b CJK Ideograph-4F7B | 4f7a CJK Ideograph-4F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7d CJK Ideograph-4F7D | 4f7c CJK Ideograph-4F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f7f CJK Ideograph-4F7F | 4f7e CJK Ideograph-4F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f81 CJK Ideograph-4F81 | 4f80 CJK Ideograph-4F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f83 CJK Ideograph-4F83 | 4f82 CJK Ideograph-4F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f85 CJK Ideograph-4F85 | 4f84 CJK Ideograph-4F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f87 CJK Ideograph-4F87 | 4f86 CJK Ideograph-4F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f89 CJK Ideograph-4F89 | 4f88 CJK Ideograph-4F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8b CJK Ideograph-4F8B | 4f8a CJK Ideograph-4F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8d CJK Ideograph-4F8D | 4f8c CJK Ideograph-4F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f8f CJK Ideograph-4F8F | 4f8e CJK Ideograph-4F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f91 CJK Ideograph-4F91 | 4f90 CJK Ideograph-4F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f93 CJK Ideograph-4F93 | 4f92 CJK Ideograph-4F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f95 CJK Ideograph-4F95 | 4f94 CJK Ideograph-4F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f97 CJK Ideograph-4F97 | 4f96 CJK Ideograph-4F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f99 CJK Ideograph-4F99 | 4f98 CJK Ideograph-4F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9b CJK Ideograph-4F9B | 4f9a CJK Ideograph-4F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9d CJK Ideograph-4F9D | 4f9c CJK Ideograph-4F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4f9f CJK Ideograph-4F9F | 4f9e CJK Ideograph-4F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa1 CJK Ideograph-4FA1 | 4fa0 CJK Ideograph-4FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa3 CJK Ideograph-4FA3 | 4fa2 CJK Ideograph-4FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa5 CJK Ideograph-4FA5 | 4fa4 CJK Ideograph-4FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa7 CJK Ideograph-4FA7 | 4fa6 CJK Ideograph-4FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fa9 CJK Ideograph-4FA9 | 4fa8 CJK Ideograph-4FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fab CJK Ideograph-4FAB | 4faa CJK Ideograph-4FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fad CJK Ideograph-4FAD | 4fac CJK Ideograph-4FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4faf CJK Ideograph-4FAF | 4fae CJK Ideograph-4FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb1 CJK Ideograph-4FB1 | 4fb0 CJK Ideograph-4FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb3 CJK Ideograph-4FB3 | 4fb2 CJK Ideograph-4FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb5 CJK Ideograph-4FB5 | 4fb4 CJK Ideograph-4FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb7 CJK Ideograph-4FB7 | 4fb6 CJK Ideograph-4FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fb9 CJK Ideograph-4FB9 | 4fb8 CJK Ideograph-4FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbb CJK Ideograph-4FBB | 4fba CJK Ideograph-4FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbd CJK Ideograph-4FBD | 4fbc CJK Ideograph-4FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fbf CJK Ideograph-4FBF | 4fbe CJK Ideograph-4FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc1 CJK Ideograph-4FC1 | 4fc0 CJK Ideograph-4FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc3 CJK Ideograph-4FC3 | 4fc2 CJK Ideograph-4FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc5 CJK Ideograph-4FC5 | 4fc4 CJK Ideograph-4FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc7 CJK Ideograph-4FC7 | 4fc6 CJK Ideograph-4FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fc9 CJK Ideograph-4FC9 | 4fc8 CJK Ideograph-4FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcb CJK Ideograph-4FCB | 4fca CJK Ideograph-4FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcd CJK Ideograph-4FCD | 4fcc CJK Ideograph-4FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fcf CJK Ideograph-4FCF | 4fce CJK Ideograph-4FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd1 CJK Ideograph-4FD1 | 4fd0 CJK Ideograph-4FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd3 CJK Ideograph-4FD3 | 4fd2 CJK Ideograph-4FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd5 CJK Ideograph-4FD5 | 4fd4 CJK Ideograph-4FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd7 CJK Ideograph-4FD7 | 4fd6 CJK Ideograph-4FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fd9 CJK Ideograph-4FD9 | 4fd8 CJK Ideograph-4FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdb CJK Ideograph-4FDB | 4fda CJK Ideograph-4FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdd CJK Ideograph-4FDD | 4fdc CJK Ideograph-4FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fdf CJK Ideograph-4FDF | 4fde CJK Ideograph-4FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe1 CJK Ideograph-4FE1 | 4fe0 CJK Ideograph-4FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe3 CJK Ideograph-4FE3 | 4fe2 CJK Ideograph-4FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe5 CJK Ideograph-4FE5 | 4fe4 CJK Ideograph-4FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe7 CJK Ideograph-4FE7 | 4fe6 CJK Ideograph-4FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fe9 CJK Ideograph-4FE9 | 4fe8 CJK Ideograph-4FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4feb CJK Ideograph-4FEB | 4fea CJK Ideograph-4FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fed CJK Ideograph-4FED | 4fec CJK Ideograph-4FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fef CJK Ideograph-4FEF | 4fee CJK Ideograph-4FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff1 CJK Ideograph-4FF1 | 4ff0 CJK Ideograph-4FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff3 CJK Ideograph-4FF3 | 4ff2 CJK Ideograph-4FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff5 CJK Ideograph-4FF5 | 4ff4 CJK Ideograph-4FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff7 CJK Ideograph-4FF7 | 4ff6 CJK Ideograph-4FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ff9 CJK Ideograph-4FF9 | 4ff8 CJK Ideograph-4FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ffb CJK Ideograph-4FFB | 4ffa CJK Ideograph-4FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4ffd CJK Ideograph-4FFD | 4ffc CJK Ideograph-4FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 4fff CJK Ideograph-4FFF | 4ffe CJK Ideograph-4FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5001 CJK Ideograph-5001 | 5000 CJK Ideograph-5000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5003 CJK Ideograph-5003 | 5002 CJK Ideograph-5002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5005 CJK Ideograph-5005 | 5004 CJK Ideograph-5004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5007 CJK Ideograph-5007 | 5006 CJK Ideograph-5006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5009 CJK Ideograph-5009 | 5008 CJK Ideograph-5008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500b CJK Ideograph-500B | 500a CJK Ideograph-500A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500d CJK Ideograph-500D | 500c CJK Ideograph-500C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 500f CJK Ideograph-500F | 500e CJK Ideograph-500E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5011 CJK Ideograph-5011 | 5010 CJK Ideograph-5010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5013 CJK Ideograph-5013 | 5012 CJK Ideograph-5012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5015 CJK Ideograph-5015 | 5014 CJK Ideograph-5014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5017 CJK Ideograph-5017 | 5016 CJK Ideograph-5016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5019 CJK Ideograph-5019 | 5018 CJK Ideograph-5018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501b CJK Ideograph-501B | 501a CJK Ideograph-501A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501d CJK Ideograph-501D | 501c CJK Ideograph-501C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 501f CJK Ideograph-501F | 501e CJK Ideograph-501E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5021 CJK Ideograph-5021 | 5020 CJK Ideograph-5020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5023 CJK Ideograph-5023 | 5022 CJK Ideograph-5022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5025 CJK Ideograph-5025 | 5024 CJK Ideograph-5024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5027 CJK Ideograph-5027 | 5026 CJK Ideograph-5026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5029 CJK Ideograph-5029 | 5028 CJK Ideograph-5028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502b CJK Ideograph-502B | 502a CJK Ideograph-502A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502d CJK Ideograph-502D | 502c CJK Ideograph-502C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 502f CJK Ideograph-502F | 502e CJK Ideograph-502E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5031 CJK Ideograph-5031 | 5030 CJK Ideograph-5030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5033 CJK Ideograph-5033 | 5032 CJK Ideograph-5032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5035 CJK Ideograph-5035 | 5034 CJK Ideograph-5034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5037 CJK Ideograph-5037 | 5036 CJK Ideograph-5036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5039 CJK Ideograph-5039 | 5038 CJK Ideograph-5038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503b CJK Ideograph-503B | 503a CJK Ideograph-503A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503d CJK Ideograph-503D | 503c CJK Ideograph-503C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 503f CJK Ideograph-503F | 503e CJK Ideograph-503E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5041 CJK Ideograph-5041 | 5040 CJK Ideograph-5040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5043 CJK Ideograph-5043 | 5042 CJK Ideograph-5042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5045 CJK Ideograph-5045 | 5044 CJK Ideograph-5044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5047 CJK Ideograph-5047 | 5046 CJK Ideograph-5046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5049 CJK Ideograph-5049 | 5048 CJK Ideograph-5048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504b CJK Ideograph-504B | 504a CJK Ideograph-504A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504d CJK Ideograph-504D | 504c CJK Ideograph-504C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 504f CJK Ideograph-504F | 504e CJK Ideograph-504E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5051 CJK Ideograph-5051 | 5050 CJK Ideograph-5050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5053 CJK Ideograph-5053 | 5052 CJK Ideograph-5052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5055 CJK Ideograph-5055 | 5054 CJK Ideograph-5054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5057 CJK Ideograph-5057 | 5056 CJK Ideograph-5056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5059 CJK Ideograph-5059 | 5058 CJK Ideograph-5058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505b CJK Ideograph-505B | 505a CJK Ideograph-505A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505d CJK Ideograph-505D | 505c CJK Ideograph-505C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 505f CJK Ideograph-505F | 505e CJK Ideograph-505E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5061 CJK Ideograph-5061 | 5060 CJK Ideograph-5060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5063 CJK Ideograph-5063 | 5062 CJK Ideograph-5062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5065 CJK Ideograph-5065 | 5064 CJK Ideograph-5064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5067 CJK Ideograph-5067 | 5066 CJK Ideograph-5066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5069 CJK Ideograph-5069 | 5068 CJK Ideograph-5068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506b CJK Ideograph-506B | 506a CJK Ideograph-506A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506d CJK Ideograph-506D | 506c CJK Ideograph-506C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 506f CJK Ideograph-506F | 506e CJK Ideograph-506E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5071 CJK Ideograph-5071 | 5070 CJK Ideograph-5070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5073 CJK Ideograph-5073 | 5072 CJK Ideograph-5072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5075 CJK Ideograph-5075 | 5074 CJK Ideograph-5074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5077 CJK Ideograph-5077 | 5076 CJK Ideograph-5076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5079 CJK Ideograph-5079 | 5078 CJK Ideograph-5078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507b CJK Ideograph-507B | 507a CJK Ideograph-507A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507d CJK Ideograph-507D | 507c CJK Ideograph-507C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 507f CJK Ideograph-507F | 507e CJK Ideograph-507E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5081 CJK Ideograph-5081 | 5080 CJK Ideograph-5080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5083 CJK Ideograph-5083 | 5082 CJK Ideograph-5082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5085 CJK Ideograph-5085 | 5084 CJK Ideograph-5084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5087 CJK Ideograph-5087 | 5086 CJK Ideograph-5086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5089 CJK Ideograph-5089 | 5088 CJK Ideograph-5088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508b CJK Ideograph-508B | 508a CJK Ideograph-508A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508d CJK Ideograph-508D | 508c CJK Ideograph-508C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 508f CJK Ideograph-508F | 508e CJK Ideograph-508E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5091 CJK Ideograph-5091 | 5090 CJK Ideograph-5090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5093 CJK Ideograph-5093 | 5092 CJK Ideograph-5092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5095 CJK Ideograph-5095 | 5094 CJK Ideograph-5094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5097 CJK Ideograph-5097 | 5096 CJK Ideograph-5096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5099 CJK Ideograph-5099 | 5098 CJK Ideograph-5098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509b CJK Ideograph-509B | 509a CJK Ideograph-509A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509d CJK Ideograph-509D | 509c CJK Ideograph-509C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 509f CJK Ideograph-509F | 509e CJK Ideograph-509E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a1 CJK Ideograph-50A1 | 50a0 CJK Ideograph-50A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a3 CJK Ideograph-50A3 | 50a2 CJK Ideograph-50A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a5 CJK Ideograph-50A5 | 50a4 CJK Ideograph-50A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a7 CJK Ideograph-50A7 | 50a6 CJK Ideograph-50A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50a9 CJK Ideograph-50A9 | 50a8 CJK Ideograph-50A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ab CJK Ideograph-50AB | 50aa CJK Ideograph-50AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ad CJK Ideograph-50AD | 50ac CJK Ideograph-50AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50af CJK Ideograph-50AF | 50ae CJK Ideograph-50AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b1 CJK Ideograph-50B1 | 50b0 CJK Ideograph-50B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b3 CJK Ideograph-50B3 | 50b2 CJK Ideograph-50B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b5 CJK Ideograph-50B5 | 50b4 CJK Ideograph-50B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b7 CJK Ideograph-50B7 | 50b6 CJK Ideograph-50B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50b9 CJK Ideograph-50B9 | 50b8 CJK Ideograph-50B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bb CJK Ideograph-50BB | 50ba CJK Ideograph-50BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bd CJK Ideograph-50BD | 50bc CJK Ideograph-50BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50bf CJK Ideograph-50BF | 50be CJK Ideograph-50BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c1 CJK Ideograph-50C1 | 50c0 CJK Ideograph-50C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c3 CJK Ideograph-50C3 | 50c2 CJK Ideograph-50C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c5 CJK Ideograph-50C5 | 50c4 CJK Ideograph-50C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c7 CJK Ideograph-50C7 | 50c6 CJK Ideograph-50C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50c9 CJK Ideograph-50C9 | 50c8 CJK Ideograph-50C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cb CJK Ideograph-50CB | 50ca CJK Ideograph-50CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cd CJK Ideograph-50CD | 50cc CJK Ideograph-50CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50cf CJK Ideograph-50CF | 50ce CJK Ideograph-50CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d1 CJK Ideograph-50D1 | 50d0 CJK Ideograph-50D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d3 CJK Ideograph-50D3 | 50d2 CJK Ideograph-50D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d5 CJK Ideograph-50D5 | 50d4 CJK Ideograph-50D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d7 CJK Ideograph-50D7 | 50d6 CJK Ideograph-50D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50d9 CJK Ideograph-50D9 | 50d8 CJK Ideograph-50D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50db CJK Ideograph-50DB | 50da CJK Ideograph-50DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50dd CJK Ideograph-50DD | 50dc CJK Ideograph-50DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50df CJK Ideograph-50DF | 50de CJK Ideograph-50DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e1 CJK Ideograph-50E1 | 50e0 CJK Ideograph-50E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e3 CJK Ideograph-50E3 | 50e2 CJK Ideograph-50E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e5 CJK Ideograph-50E5 | 50e4 CJK Ideograph-50E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e7 CJK Ideograph-50E7 | 50e6 CJK Ideograph-50E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50e9 CJK Ideograph-50E9 | 50e8 CJK Ideograph-50E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50eb CJK Ideograph-50EB | 50ea CJK Ideograph-50EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ed CJK Ideograph-50ED | 50ec CJK Ideograph-50EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ef CJK Ideograph-50EF | 50ee CJK Ideograph-50EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f1 CJK Ideograph-50F1 | 50f0 CJK Ideograph-50F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f3 CJK Ideograph-50F3 | 50f2 CJK Ideograph-50F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f5 CJK Ideograph-50F5 | 50f4 CJK Ideograph-50F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f7 CJK Ideograph-50F7 | 50f6 CJK Ideograph-50F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50f9 CJK Ideograph-50F9 | 50f8 CJK Ideograph-50F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50fb CJK Ideograph-50FB | 50fa CJK Ideograph-50FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50fd CJK Ideograph-50FD | 50fc CJK Ideograph-50FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 50ff CJK Ideograph-50FF | 50fe CJK Ideograph-50FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5101 CJK Ideograph-5101 | 5100 CJK Ideograph-5100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5103 CJK Ideograph-5103 | 5102 CJK Ideograph-5102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5105 CJK Ideograph-5105 | 5104 CJK Ideograph-5104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5107 CJK Ideograph-5107 | 5106 CJK Ideograph-5106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5109 CJK Ideograph-5109 | 5108 CJK Ideograph-5108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510b CJK Ideograph-510B | 510a CJK Ideograph-510A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510d CJK Ideograph-510D | 510c CJK Ideograph-510C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 510f CJK Ideograph-510F | 510e CJK Ideograph-510E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5111 CJK Ideograph-5111 | 5110 CJK Ideograph-5110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5113 CJK Ideograph-5113 | 5112 CJK Ideograph-5112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5115 CJK Ideograph-5115 | 5114 CJK Ideograph-5114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5117 CJK Ideograph-5117 | 5116 CJK Ideograph-5116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5119 CJK Ideograph-5119 | 5118 CJK Ideograph-5118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511b CJK Ideograph-511B | 511a CJK Ideograph-511A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511d CJK Ideograph-511D | 511c CJK Ideograph-511C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 511f CJK Ideograph-511F | 511e CJK Ideograph-511E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5121 CJK Ideograph-5121 | 5120 CJK Ideograph-5120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5123 CJK Ideograph-5123 | 5122 CJK Ideograph-5122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5125 CJK Ideograph-5125 | 5124 CJK Ideograph-5124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5127 CJK Ideograph-5127 | 5126 CJK Ideograph-5126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5129 CJK Ideograph-5129 | 5128 CJK Ideograph-5128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512b CJK Ideograph-512B | 512a CJK Ideograph-512A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512d CJK Ideograph-512D | 512c CJK Ideograph-512C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 512f CJK Ideograph-512F | 512e CJK Ideograph-512E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5131 CJK Ideograph-5131 | 5130 CJK Ideograph-5130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5133 CJK Ideograph-5133 | 5132 CJK Ideograph-5132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5135 CJK Ideograph-5135 | 5134 CJK Ideograph-5134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5137 CJK Ideograph-5137 | 5136 CJK Ideograph-5136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5139 CJK Ideograph-5139 | 5138 CJK Ideograph-5138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513b CJK Ideograph-513B | 513a CJK Ideograph-513A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513d CJK Ideograph-513D | 513c CJK Ideograph-513C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 513f CJK Ideograph-513F | 513e CJK Ideograph-513E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5141 CJK Ideograph-5141 | 5140 CJK Ideograph-5140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5143 CJK Ideograph-5143 | 5142 CJK Ideograph-5142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5145 CJK Ideograph-5145 | 5144 CJK Ideograph-5144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5147 CJK Ideograph-5147 | 5146 CJK Ideograph-5146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5149 CJK Ideograph-5149 | 5148 CJK Ideograph-5148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514b CJK Ideograph-514B | 514a CJK Ideograph-514A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514d CJK Ideograph-514D | 514c CJK Ideograph-514C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 514f CJK Ideograph-514F | 514e CJK Ideograph-514E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5151 CJK Ideograph-5151 | 5150 CJK Ideograph-5150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5153 CJK Ideograph-5153 | 5152 CJK Ideograph-5152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5155 CJK Ideograph-5155 | 5154 CJK Ideograph-5154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5157 CJK Ideograph-5157 | 5156 CJK Ideograph-5156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5159 CJK Ideograph-5159 | 5158 CJK Ideograph-5158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515b CJK Ideograph-515B | 515a CJK Ideograph-515A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515d CJK Ideograph-515D | 515c CJK Ideograph-515C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 515f CJK Ideograph-515F | 515e CJK Ideograph-515E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5161 CJK Ideograph-5161 | 5160 CJK Ideograph-5160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5163 CJK Ideograph-5163 | 5162 CJK Ideograph-5162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5165 CJK Ideograph-5165 | 5164 CJK Ideograph-5164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5167 CJK Ideograph-5167 | 5166 CJK Ideograph-5166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5169 CJK Ideograph-5169 | 5168 CJK Ideograph-5168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516b CJK Ideograph-516B | 516a CJK Ideograph-516A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516d CJK Ideograph-516D | 516c CJK Ideograph-516C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 516f CJK Ideograph-516F | 516e CJK Ideograph-516E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5171 CJK Ideograph-5171 | 5170 CJK Ideograph-5170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5173 CJK Ideograph-5173 | 5172 CJK Ideograph-5172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5175 CJK Ideograph-5175 | 5174 CJK Ideograph-5174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5177 CJK Ideograph-5177 | 5176 CJK Ideograph-5176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5179 CJK Ideograph-5179 | 5178 CJK Ideograph-5178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517b CJK Ideograph-517B | 517a CJK Ideograph-517A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517d CJK Ideograph-517D | 517c CJK Ideograph-517C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 517f CJK Ideograph-517F | 517e CJK Ideograph-517E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5181 CJK Ideograph-5181 | 5180 CJK Ideograph-5180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5183 CJK Ideograph-5183 | 5182 CJK Ideograph-5182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5185 CJK Ideograph-5185 | 5184 CJK Ideograph-5184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5187 CJK Ideograph-5187 | 5186 CJK Ideograph-5186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5189 CJK Ideograph-5189 | 5188 CJK Ideograph-5188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518b CJK Ideograph-518B | 518a CJK Ideograph-518A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518d CJK Ideograph-518D | 518c CJK Ideograph-518C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 518f CJK Ideograph-518F | 518e CJK Ideograph-518E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5191 CJK Ideograph-5191 | 5190 CJK Ideograph-5190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5193 CJK Ideograph-5193 | 5192 CJK Ideograph-5192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5195 CJK Ideograph-5195 | 5194 CJK Ideograph-5194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5197 CJK Ideograph-5197 | 5196 CJK Ideograph-5196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5199 CJK Ideograph-5199 | 5198 CJK Ideograph-5198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519b CJK Ideograph-519B | 519a CJK Ideograph-519A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519d CJK Ideograph-519D | 519c CJK Ideograph-519C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 519f CJK Ideograph-519F | 519e CJK Ideograph-519E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a1 CJK Ideograph-51A1 | 51a0 CJK Ideograph-51A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a3 CJK Ideograph-51A3 | 51a2 CJK Ideograph-51A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a5 CJK Ideograph-51A5 | 51a4 CJK Ideograph-51A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a7 CJK Ideograph-51A7 | 51a6 CJK Ideograph-51A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51a9 CJK Ideograph-51A9 | 51a8 CJK Ideograph-51A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ab CJK Ideograph-51AB | 51aa CJK Ideograph-51AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ad CJK Ideograph-51AD | 51ac CJK Ideograph-51AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51af CJK Ideograph-51AF | 51ae CJK Ideograph-51AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b1 CJK Ideograph-51B1 | 51b0 CJK Ideograph-51B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b3 CJK Ideograph-51B3 | 51b2 CJK Ideograph-51B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b5 CJK Ideograph-51B5 | 51b4 CJK Ideograph-51B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b7 CJK Ideograph-51B7 | 51b6 CJK Ideograph-51B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51b9 CJK Ideograph-51B9 | 51b8 CJK Ideograph-51B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bb CJK Ideograph-51BB | 51ba CJK Ideograph-51BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bd CJK Ideograph-51BD | 51bc CJK Ideograph-51BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51bf CJK Ideograph-51BF | 51be CJK Ideograph-51BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c1 CJK Ideograph-51C1 | 51c0 CJK Ideograph-51C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c3 CJK Ideograph-51C3 | 51c2 CJK Ideograph-51C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c5 CJK Ideograph-51C5 | 51c4 CJK Ideograph-51C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c7 CJK Ideograph-51C7 | 51c6 CJK Ideograph-51C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51c9 CJK Ideograph-51C9 | 51c8 CJK Ideograph-51C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cb CJK Ideograph-51CB | 51ca CJK Ideograph-51CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cd CJK Ideograph-51CD | 51cc CJK Ideograph-51CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51cf CJK Ideograph-51CF | 51ce CJK Ideograph-51CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d1 CJK Ideograph-51D1 | 51d0 CJK Ideograph-51D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d3 CJK Ideograph-51D3 | 51d2 CJK Ideograph-51D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d5 CJK Ideograph-51D5 | 51d4 CJK Ideograph-51D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d7 CJK Ideograph-51D7 | 51d6 CJK Ideograph-51D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51d9 CJK Ideograph-51D9 | 51d8 CJK Ideograph-51D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51db CJK Ideograph-51DB | 51da CJK Ideograph-51DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51dd CJK Ideograph-51DD | 51dc CJK Ideograph-51DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51df CJK Ideograph-51DF | 51de CJK Ideograph-51DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e1 CJK Ideograph-51E1 | 51e0 CJK Ideograph-51E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e3 CJK Ideograph-51E3 | 51e2 CJK Ideograph-51E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e5 CJK Ideograph-51E5 | 51e4 CJK Ideograph-51E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e7 CJK Ideograph-51E7 | 51e6 CJK Ideograph-51E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51e9 CJK Ideograph-51E9 | 51e8 CJK Ideograph-51E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51eb CJK Ideograph-51EB | 51ea CJK Ideograph-51EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ed CJK Ideograph-51ED | 51ec CJK Ideograph-51EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ef CJK Ideograph-51EF | 51ee CJK Ideograph-51EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f1 CJK Ideograph-51F1 | 51f0 CJK Ideograph-51F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f3 CJK Ideograph-51F3 | 51f2 CJK Ideograph-51F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f5 CJK Ideograph-51F5 | 51f4 CJK Ideograph-51F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f7 CJK Ideograph-51F7 | 51f6 CJK Ideograph-51F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51f9 CJK Ideograph-51F9 | 51f8 CJK Ideograph-51F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51fb CJK Ideograph-51FB | 51fa CJK Ideograph-51FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51fd CJK Ideograph-51FD | 51fc CJK Ideograph-51FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 51ff CJK Ideograph-51FF | 51fe CJK Ideograph-51FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5201 CJK Ideograph-5201 | 5200 CJK Ideograph-5200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5203 CJK Ideograph-5203 | 5202 CJK Ideograph-5202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5205 CJK Ideograph-5205 | 5204 CJK Ideograph-5204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5207 CJK Ideograph-5207 | 5206 CJK Ideograph-5206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5209 CJK Ideograph-5209 | 5208 CJK Ideograph-5208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520b CJK Ideograph-520B | 520a CJK Ideograph-520A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520d CJK Ideograph-520D | 520c CJK Ideograph-520C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 520f CJK Ideograph-520F | 520e CJK Ideograph-520E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5211 CJK Ideograph-5211 | 5210 CJK Ideograph-5210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5213 CJK Ideograph-5213 | 5212 CJK Ideograph-5212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5215 CJK Ideograph-5215 | 5214 CJK Ideograph-5214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5217 CJK Ideograph-5217 | 5216 CJK Ideograph-5216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5219 CJK Ideograph-5219 | 5218 CJK Ideograph-5218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521b CJK Ideograph-521B | 521a CJK Ideograph-521A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521d CJK Ideograph-521D | 521c CJK Ideograph-521C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 521f CJK Ideograph-521F | 521e CJK Ideograph-521E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5221 CJK Ideograph-5221 | 5220 CJK Ideograph-5220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5223 CJK Ideograph-5223 | 5222 CJK Ideograph-5222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5225 CJK Ideograph-5225 | 5224 CJK Ideograph-5224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5227 CJK Ideograph-5227 | 5226 CJK Ideograph-5226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5229 CJK Ideograph-5229 | 5228 CJK Ideograph-5228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522b CJK Ideograph-522B | 522a CJK Ideograph-522A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522d CJK Ideograph-522D | 522c CJK Ideograph-522C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 522f CJK Ideograph-522F | 522e CJK Ideograph-522E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5231 CJK Ideograph-5231 | 5230 CJK Ideograph-5230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5233 CJK Ideograph-5233 | 5232 CJK Ideograph-5232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5235 CJK Ideograph-5235 | 5234 CJK Ideograph-5234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5237 CJK Ideograph-5237 | 5236 CJK Ideograph-5236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5239 CJK Ideograph-5239 | 5238 CJK Ideograph-5238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523b CJK Ideograph-523B | 523a CJK Ideograph-523A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523d CJK Ideograph-523D | 523c CJK Ideograph-523C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 523f CJK Ideograph-523F | 523e CJK Ideograph-523E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5241 CJK Ideograph-5241 | 5240 CJK Ideograph-5240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5243 CJK Ideograph-5243 | 5242 CJK Ideograph-5242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5245 CJK Ideograph-5245 | 5244 CJK Ideograph-5244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5247 CJK Ideograph-5247 | 5246 CJK Ideograph-5246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5249 CJK Ideograph-5249 | 5248 CJK Ideograph-5248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524b CJK Ideograph-524B | 524a CJK Ideograph-524A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524d CJK Ideograph-524D | 524c CJK Ideograph-524C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 524f CJK Ideograph-524F | 524e CJK Ideograph-524E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5251 CJK Ideograph-5251 | 5250 CJK Ideograph-5250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5253 CJK Ideograph-5253 | 5252 CJK Ideograph-5252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5255 CJK Ideograph-5255 | 5254 CJK Ideograph-5254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5257 CJK Ideograph-5257 | 5256 CJK Ideograph-5256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5259 CJK Ideograph-5259 | 5258 CJK Ideograph-5258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525b CJK Ideograph-525B | 525a CJK Ideograph-525A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525d CJK Ideograph-525D | 525c CJK Ideograph-525C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 525f CJK Ideograph-525F | 525e CJK Ideograph-525E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5261 CJK Ideograph-5261 | 5260 CJK Ideograph-5260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5263 CJK Ideograph-5263 | 5262 CJK Ideograph-5262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5265 CJK Ideograph-5265 | 5264 CJK Ideograph-5264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5267 CJK Ideograph-5267 | 5266 CJK Ideograph-5266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5269 CJK Ideograph-5269 | 5268 CJK Ideograph-5268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526b CJK Ideograph-526B | 526a CJK Ideograph-526A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526d CJK Ideograph-526D | 526c CJK Ideograph-526C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 526f CJK Ideograph-526F | 526e CJK Ideograph-526E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5271 CJK Ideograph-5271 | 5270 CJK Ideograph-5270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5273 CJK Ideograph-5273 | 5272 CJK Ideograph-5272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5275 CJK Ideograph-5275 | 5274 CJK Ideograph-5274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5277 CJK Ideograph-5277 | 5276 CJK Ideograph-5276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5279 CJK Ideograph-5279 | 5278 CJK Ideograph-5278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527b CJK Ideograph-527B | 527a CJK Ideograph-527A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527d CJK Ideograph-527D | 527c CJK Ideograph-527C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 527f CJK Ideograph-527F | 527e CJK Ideograph-527E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5281 CJK Ideograph-5281 | 5280 CJK Ideograph-5280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5283 CJK Ideograph-5283 | 5282 CJK Ideograph-5282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5285 CJK Ideograph-5285 | 5284 CJK Ideograph-5284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5287 CJK Ideograph-5287 | 5286 CJK Ideograph-5286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5289 CJK Ideograph-5289 | 5288 CJK Ideograph-5288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528b CJK Ideograph-528B | 528a CJK Ideograph-528A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528d CJK Ideograph-528D | 528c CJK Ideograph-528C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 528f CJK Ideograph-528F | 528e CJK Ideograph-528E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5291 CJK Ideograph-5291 | 5290 CJK Ideograph-5290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5293 CJK Ideograph-5293 | 5292 CJK Ideograph-5292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5295 CJK Ideograph-5295 | 5294 CJK Ideograph-5294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5297 CJK Ideograph-5297 | 5296 CJK Ideograph-5296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5299 CJK Ideograph-5299 | 5298 CJK Ideograph-5298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529b CJK Ideograph-529B | 529a CJK Ideograph-529A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529d CJK Ideograph-529D | 529c CJK Ideograph-529C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 529f CJK Ideograph-529F | 529e CJK Ideograph-529E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a1 CJK Ideograph-52A1 | 52a0 CJK Ideograph-52A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a3 CJK Ideograph-52A3 | 52a2 CJK Ideograph-52A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a5 CJK Ideograph-52A5 | 52a4 CJK Ideograph-52A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a7 CJK Ideograph-52A7 | 52a6 CJK Ideograph-52A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52a9 CJK Ideograph-52A9 | 52a8 CJK Ideograph-52A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ab CJK Ideograph-52AB | 52aa CJK Ideograph-52AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ad CJK Ideograph-52AD | 52ac CJK Ideograph-52AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52af CJK Ideograph-52AF | 52ae CJK Ideograph-52AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b1 CJK Ideograph-52B1 | 52b0 CJK Ideograph-52B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b3 CJK Ideograph-52B3 | 52b2 CJK Ideograph-52B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b5 CJK Ideograph-52B5 | 52b4 CJK Ideograph-52B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b7 CJK Ideograph-52B7 | 52b6 CJK Ideograph-52B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52b9 CJK Ideograph-52B9 | 52b8 CJK Ideograph-52B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bb CJK Ideograph-52BB | 52ba CJK Ideograph-52BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bd CJK Ideograph-52BD | 52bc CJK Ideograph-52BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52bf CJK Ideograph-52BF | 52be CJK Ideograph-52BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c1 CJK Ideograph-52C1 | 52c0 CJK Ideograph-52C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c3 CJK Ideograph-52C3 | 52c2 CJK Ideograph-52C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c5 CJK Ideograph-52C5 | 52c4 CJK Ideograph-52C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c7 CJK Ideograph-52C7 | 52c6 CJK Ideograph-52C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52c9 CJK Ideograph-52C9 | 52c8 CJK Ideograph-52C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cb CJK Ideograph-52CB | 52ca CJK Ideograph-52CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cd CJK Ideograph-52CD | 52cc CJK Ideograph-52CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52cf CJK Ideograph-52CF | 52ce CJK Ideograph-52CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d1 CJK Ideograph-52D1 | 52d0 CJK Ideograph-52D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d3 CJK Ideograph-52D3 | 52d2 CJK Ideograph-52D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d5 CJK Ideograph-52D5 | 52d4 CJK Ideograph-52D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d7 CJK Ideograph-52D7 | 52d6 CJK Ideograph-52D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52d9 CJK Ideograph-52D9 | 52d8 CJK Ideograph-52D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52db CJK Ideograph-52DB | 52da CJK Ideograph-52DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52dd CJK Ideograph-52DD | 52dc CJK Ideograph-52DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52df CJK Ideograph-52DF | 52de CJK Ideograph-52DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e1 CJK Ideograph-52E1 | 52e0 CJK Ideograph-52E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e3 CJK Ideograph-52E3 | 52e2 CJK Ideograph-52E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e5 CJK Ideograph-52E5 | 52e4 CJK Ideograph-52E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e7 CJK Ideograph-52E7 | 52e6 CJK Ideograph-52E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52e9 CJK Ideograph-52E9 | 52e8 CJK Ideograph-52E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52eb CJK Ideograph-52EB | 52ea CJK Ideograph-52EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ed CJK Ideograph-52ED | 52ec CJK Ideograph-52EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ef CJK Ideograph-52EF | 52ee CJK Ideograph-52EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f1 CJK Ideograph-52F1 | 52f0 CJK Ideograph-52F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f3 CJK Ideograph-52F3 | 52f2 CJK Ideograph-52F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f5 CJK Ideograph-52F5 | 52f4 CJK Ideograph-52F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f7 CJK Ideograph-52F7 | 52f6 CJK Ideograph-52F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52f9 CJK Ideograph-52F9 | 52f8 CJK Ideograph-52F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52fb CJK Ideograph-52FB | 52fa CJK Ideograph-52FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52fd CJK Ideograph-52FD | 52fc CJK Ideograph-52FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 52ff CJK Ideograph-52FF | 52fe CJK Ideograph-52FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5301 CJK Ideograph-5301 | 5300 CJK Ideograph-5300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5303 CJK Ideograph-5303 | 5302 CJK Ideograph-5302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5305 CJK Ideograph-5305 | 5304 CJK Ideograph-5304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5307 CJK Ideograph-5307 | 5306 CJK Ideograph-5306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5309 CJK Ideograph-5309 | 5308 CJK Ideograph-5308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530b CJK Ideograph-530B | 530a CJK Ideograph-530A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530d CJK Ideograph-530D | 530c CJK Ideograph-530C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 530f CJK Ideograph-530F | 530e CJK Ideograph-530E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5311 CJK Ideograph-5311 | 5310 CJK Ideograph-5310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5313 CJK Ideograph-5313 | 5312 CJK Ideograph-5312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5315 CJK Ideograph-5315 | 5314 CJK Ideograph-5314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5317 CJK Ideograph-5317 | 5316 CJK Ideograph-5316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5319 CJK Ideograph-5319 | 5318 CJK Ideograph-5318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531b CJK Ideograph-531B | 531a CJK Ideograph-531A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531d CJK Ideograph-531D | 531c CJK Ideograph-531C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 531f CJK Ideograph-531F | 531e CJK Ideograph-531E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5321 CJK Ideograph-5321 | 5320 CJK Ideograph-5320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5323 CJK Ideograph-5323 | 5322 CJK Ideograph-5322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5325 CJK Ideograph-5325 | 5324 CJK Ideograph-5324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5327 CJK Ideograph-5327 | 5326 CJK Ideograph-5326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5329 CJK Ideograph-5329 | 5328 CJK Ideograph-5328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532b CJK Ideograph-532B | 532a CJK Ideograph-532A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532d CJK Ideograph-532D | 532c CJK Ideograph-532C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 532f CJK Ideograph-532F | 532e CJK Ideograph-532E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5331 CJK Ideograph-5331 | 5330 CJK Ideograph-5330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5333 CJK Ideograph-5333 | 5332 CJK Ideograph-5332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5335 CJK Ideograph-5335 | 5334 CJK Ideograph-5334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5337 CJK Ideograph-5337 | 5336 CJK Ideograph-5336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5339 CJK Ideograph-5339 | 5338 CJK Ideograph-5338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533b CJK Ideograph-533B | 533a CJK Ideograph-533A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533d CJK Ideograph-533D | 533c CJK Ideograph-533C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 533f CJK Ideograph-533F | 533e CJK Ideograph-533E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5341 CJK Ideograph-5341 | 5340 CJK Ideograph-5340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5343 CJK Ideograph-5343 | 5342 CJK Ideograph-5342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5345 CJK Ideograph-5345 | 5344 CJK Ideograph-5344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5347 CJK Ideograph-5347 | 5346 CJK Ideograph-5346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5349 CJK Ideograph-5349 | 5348 CJK Ideograph-5348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534b CJK Ideograph-534B | 534a CJK Ideograph-534A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534d CJK Ideograph-534D | 534c CJK Ideograph-534C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 534f CJK Ideograph-534F | 534e CJK Ideograph-534E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5351 CJK Ideograph-5351 | 5350 CJK Ideograph-5350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5353 CJK Ideograph-5353 | 5352 CJK Ideograph-5352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5355 CJK Ideograph-5355 | 5354 CJK Ideograph-5354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5357 CJK Ideograph-5357 | 5356 CJK Ideograph-5356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5359 CJK Ideograph-5359 | 5358 CJK Ideograph-5358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535b CJK Ideograph-535B | 535a CJK Ideograph-535A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535d CJK Ideograph-535D | 535c CJK Ideograph-535C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 535f CJK Ideograph-535F | 535e CJK Ideograph-535E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5361 CJK Ideograph-5361 | 5360 CJK Ideograph-5360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5363 CJK Ideograph-5363 | 5362 CJK Ideograph-5362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5365 CJK Ideograph-5365 | 5364 CJK Ideograph-5364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5367 CJK Ideograph-5367 | 5366 CJK Ideograph-5366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5369 CJK Ideograph-5369 | 5368 CJK Ideograph-5368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536b CJK Ideograph-536B | 536a CJK Ideograph-536A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536d CJK Ideograph-536D | 536c CJK Ideograph-536C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 536f CJK Ideograph-536F | 536e CJK Ideograph-536E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5371 CJK Ideograph-5371 | 5370 CJK Ideograph-5370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5373 CJK Ideograph-5373 | 5372 CJK Ideograph-5372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5375 CJK Ideograph-5375 | 5374 CJK Ideograph-5374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5377 CJK Ideograph-5377 | 5376 CJK Ideograph-5376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5379 CJK Ideograph-5379 | 5378 CJK Ideograph-5378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537b CJK Ideograph-537B | 537a CJK Ideograph-537A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537d CJK Ideograph-537D | 537c CJK Ideograph-537C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 537f CJK Ideograph-537F | 537e CJK Ideograph-537E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5381 CJK Ideograph-5381 | 5380 CJK Ideograph-5380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5383 CJK Ideograph-5383 | 5382 CJK Ideograph-5382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5385 CJK Ideograph-5385 | 5384 CJK Ideograph-5384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5387 CJK Ideograph-5387 | 5386 CJK Ideograph-5386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5389 CJK Ideograph-5389 | 5388 CJK Ideograph-5388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538b CJK Ideograph-538B | 538a CJK Ideograph-538A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538d CJK Ideograph-538D | 538c CJK Ideograph-538C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 538f CJK Ideograph-538F | 538e CJK Ideograph-538E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5391 CJK Ideograph-5391 | 5390 CJK Ideograph-5390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5393 CJK Ideograph-5393 | 5392 CJK Ideograph-5392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5395 CJK Ideograph-5395 | 5394 CJK Ideograph-5394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5397 CJK Ideograph-5397 | 5396 CJK Ideograph-5396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5399 CJK Ideograph-5399 | 5398 CJK Ideograph-5398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539b CJK Ideograph-539B | 539a CJK Ideograph-539A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539d CJK Ideograph-539D | 539c CJK Ideograph-539C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 539f CJK Ideograph-539F | 539e CJK Ideograph-539E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a1 CJK Ideograph-53A1 | 53a0 CJK Ideograph-53A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a3 CJK Ideograph-53A3 | 53a2 CJK Ideograph-53A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a5 CJK Ideograph-53A5 | 53a4 CJK Ideograph-53A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a7 CJK Ideograph-53A7 | 53a6 CJK Ideograph-53A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53a9 CJK Ideograph-53A9 | 53a8 CJK Ideograph-53A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ab CJK Ideograph-53AB | 53aa CJK Ideograph-53AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ad CJK Ideograph-53AD | 53ac CJK Ideograph-53AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53af CJK Ideograph-53AF | 53ae CJK Ideograph-53AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b1 CJK Ideograph-53B1 | 53b0 CJK Ideograph-53B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b3 CJK Ideograph-53B3 | 53b2 CJK Ideograph-53B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b5 CJK Ideograph-53B5 | 53b4 CJK Ideograph-53B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b7 CJK Ideograph-53B7 | 53b6 CJK Ideograph-53B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53b9 CJK Ideograph-53B9 | 53b8 CJK Ideograph-53B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bb CJK Ideograph-53BB | 53ba CJK Ideograph-53BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bd CJK Ideograph-53BD | 53bc CJK Ideograph-53BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53bf CJK Ideograph-53BF | 53be CJK Ideograph-53BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c1 CJK Ideograph-53C1 | 53c0 CJK Ideograph-53C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c3 CJK Ideograph-53C3 | 53c2 CJK Ideograph-53C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c5 CJK Ideograph-53C5 | 53c4 CJK Ideograph-53C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c7 CJK Ideograph-53C7 | 53c6 CJK Ideograph-53C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53c9 CJK Ideograph-53C9 | 53c8 CJK Ideograph-53C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cb CJK Ideograph-53CB | 53ca CJK Ideograph-53CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cd CJK Ideograph-53CD | 53cc CJK Ideograph-53CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53cf CJK Ideograph-53CF | 53ce CJK Ideograph-53CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d1 CJK Ideograph-53D1 | 53d0 CJK Ideograph-53D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d3 CJK Ideograph-53D3 | 53d2 CJK Ideograph-53D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d5 CJK Ideograph-53D5 | 53d4 CJK Ideograph-53D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d7 CJK Ideograph-53D7 | 53d6 CJK Ideograph-53D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53d9 CJK Ideograph-53D9 | 53d8 CJK Ideograph-53D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53db CJK Ideograph-53DB | 53da CJK Ideograph-53DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53dd CJK Ideograph-53DD | 53dc CJK Ideograph-53DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53df CJK Ideograph-53DF | 53de CJK Ideograph-53DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e1 CJK Ideograph-53E1 | 53e0 CJK Ideograph-53E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e3 CJK Ideograph-53E3 | 53e2 CJK Ideograph-53E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e5 CJK Ideograph-53E5 | 53e4 CJK Ideograph-53E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e7 CJK Ideograph-53E7 | 53e6 CJK Ideograph-53E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53e9 CJK Ideograph-53E9 | 53e8 CJK Ideograph-53E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53eb CJK Ideograph-53EB | 53ea CJK Ideograph-53EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ed CJK Ideograph-53ED | 53ec CJK Ideograph-53EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ef CJK Ideograph-53EF | 53ee CJK Ideograph-53EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f1 CJK Ideograph-53F1 | 53f0 CJK Ideograph-53F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f3 CJK Ideograph-53F3 | 53f2 CJK Ideograph-53F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f5 CJK Ideograph-53F5 | 53f4 CJK Ideograph-53F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f7 CJK Ideograph-53F7 | 53f6 CJK Ideograph-53F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53f9 CJK Ideograph-53F9 | 53f8 CJK Ideograph-53F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53fb CJK Ideograph-53FB | 53fa CJK Ideograph-53FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53fd CJK Ideograph-53FD | 53fc CJK Ideograph-53FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 53ff CJK Ideograph-53FF | 53fe CJK Ideograph-53FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5401 CJK Ideograph-5401 | 5400 CJK Ideograph-5400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5403 CJK Ideograph-5403 | 5402 CJK Ideograph-5402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5405 CJK Ideograph-5405 | 5404 CJK Ideograph-5404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5407 CJK Ideograph-5407 | 5406 CJK Ideograph-5406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5409 CJK Ideograph-5409 | 5408 CJK Ideograph-5408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540b CJK Ideograph-540B | 540a CJK Ideograph-540A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540d CJK Ideograph-540D | 540c CJK Ideograph-540C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 540f CJK Ideograph-540F | 540e CJK Ideograph-540E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5411 CJK Ideograph-5411 | 5410 CJK Ideograph-5410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5413 CJK Ideograph-5413 | 5412 CJK Ideograph-5412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5415 CJK Ideograph-5415 | 5414 CJK Ideograph-5414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5417 CJK Ideograph-5417 | 5416 CJK Ideograph-5416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5419 CJK Ideograph-5419 | 5418 CJK Ideograph-5418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541b CJK Ideograph-541B | 541a CJK Ideograph-541A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541d CJK Ideograph-541D | 541c CJK Ideograph-541C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 541f CJK Ideograph-541F | 541e CJK Ideograph-541E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5421 CJK Ideograph-5421 | 5420 CJK Ideograph-5420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5423 CJK Ideograph-5423 | 5422 CJK Ideograph-5422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5425 CJK Ideograph-5425 | 5424 CJK Ideograph-5424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5427 CJK Ideograph-5427 | 5426 CJK Ideograph-5426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5429 CJK Ideograph-5429 | 5428 CJK Ideograph-5428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542b CJK Ideograph-542B | 542a CJK Ideograph-542A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542d CJK Ideograph-542D | 542c CJK Ideograph-542C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 542f CJK Ideograph-542F | 542e CJK Ideograph-542E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5431 CJK Ideograph-5431 | 5430 CJK Ideograph-5430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5433 CJK Ideograph-5433 | 5432 CJK Ideograph-5432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5435 CJK Ideograph-5435 | 5434 CJK Ideograph-5434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5437 CJK Ideograph-5437 | 5436 CJK Ideograph-5436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5439 CJK Ideograph-5439 | 5438 CJK Ideograph-5438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543b CJK Ideograph-543B | 543a CJK Ideograph-543A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543d CJK Ideograph-543D | 543c CJK Ideograph-543C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 543f CJK Ideograph-543F | 543e CJK Ideograph-543E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5441 CJK Ideograph-5441 | 5440 CJK Ideograph-5440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5443 CJK Ideograph-5443 | 5442 CJK Ideograph-5442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5445 CJK Ideograph-5445 | 5444 CJK Ideograph-5444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5447 CJK Ideograph-5447 | 5446 CJK Ideograph-5446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5449 CJK Ideograph-5449 | 5448 CJK Ideograph-5448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544b CJK Ideograph-544B | 544a CJK Ideograph-544A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544d CJK Ideograph-544D | 544c CJK Ideograph-544C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 544f CJK Ideograph-544F | 544e CJK Ideograph-544E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5451 CJK Ideograph-5451 | 5450 CJK Ideograph-5450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5453 CJK Ideograph-5453 | 5452 CJK Ideograph-5452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5455 CJK Ideograph-5455 | 5454 CJK Ideograph-5454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5457 CJK Ideograph-5457 | 5456 CJK Ideograph-5456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5459 CJK Ideograph-5459 | 5458 CJK Ideograph-5458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545b CJK Ideograph-545B | 545a CJK Ideograph-545A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545d CJK Ideograph-545D | 545c CJK Ideograph-545C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 545f CJK Ideograph-545F | 545e CJK Ideograph-545E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5461 CJK Ideograph-5461 | 5460 CJK Ideograph-5460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5463 CJK Ideograph-5463 | 5462 CJK Ideograph-5462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5465 CJK Ideograph-5465 | 5464 CJK Ideograph-5464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5467 CJK Ideograph-5467 | 5466 CJK Ideograph-5466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5469 CJK Ideograph-5469 | 5468 CJK Ideograph-5468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546b CJK Ideograph-546B | 546a CJK Ideograph-546A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546d CJK Ideograph-546D | 546c CJK Ideograph-546C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 546f CJK Ideograph-546F | 546e CJK Ideograph-546E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5471 CJK Ideograph-5471 | 5470 CJK Ideograph-5470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5473 CJK Ideograph-5473 | 5472 CJK Ideograph-5472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5475 CJK Ideograph-5475 | 5474 CJK Ideograph-5474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5477 CJK Ideograph-5477 | 5476 CJK Ideograph-5476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5479 CJK Ideograph-5479 | 5478 CJK Ideograph-5478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547b CJK Ideograph-547B | 547a CJK Ideograph-547A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547d CJK Ideograph-547D | 547c CJK Ideograph-547C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 547f CJK Ideograph-547F | 547e CJK Ideograph-547E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5481 CJK Ideograph-5481 | 5480 CJK Ideograph-5480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5483 CJK Ideograph-5483 | 5482 CJK Ideograph-5482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5485 CJK Ideograph-5485 | 5484 CJK Ideograph-5484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5487 CJK Ideograph-5487 | 5486 CJK Ideograph-5486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5489 CJK Ideograph-5489 | 5488 CJK Ideograph-5488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548b CJK Ideograph-548B | 548a CJK Ideograph-548A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548d CJK Ideograph-548D | 548c CJK Ideograph-548C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 548f CJK Ideograph-548F | 548e CJK Ideograph-548E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5491 CJK Ideograph-5491 | 5490 CJK Ideograph-5490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5493 CJK Ideograph-5493 | 5492 CJK Ideograph-5492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5495 CJK Ideograph-5495 | 5494 CJK Ideograph-5494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5497 CJK Ideograph-5497 | 5496 CJK Ideograph-5496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5499 CJK Ideograph-5499 | 5498 CJK Ideograph-5498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549b CJK Ideograph-549B | 549a CJK Ideograph-549A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549d CJK Ideograph-549D | 549c CJK Ideograph-549C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 549f CJK Ideograph-549F | 549e CJK Ideograph-549E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a1 CJK Ideograph-54A1 | 54a0 CJK Ideograph-54A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a3 CJK Ideograph-54A3 | 54a2 CJK Ideograph-54A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a5 CJK Ideograph-54A5 | 54a4 CJK Ideograph-54A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a7 CJK Ideograph-54A7 | 54a6 CJK Ideograph-54A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54a9 CJK Ideograph-54A9 | 54a8 CJK Ideograph-54A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ab CJK Ideograph-54AB | 54aa CJK Ideograph-54AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ad CJK Ideograph-54AD | 54ac CJK Ideograph-54AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54af CJK Ideograph-54AF | 54ae CJK Ideograph-54AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b1 CJK Ideograph-54B1 | 54b0 CJK Ideograph-54B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b3 CJK Ideograph-54B3 | 54b2 CJK Ideograph-54B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b5 CJK Ideograph-54B5 | 54b4 CJK Ideograph-54B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b7 CJK Ideograph-54B7 | 54b6 CJK Ideograph-54B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54b9 CJK Ideograph-54B9 | 54b8 CJK Ideograph-54B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bb CJK Ideograph-54BB | 54ba CJK Ideograph-54BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bd CJK Ideograph-54BD | 54bc CJK Ideograph-54BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54bf CJK Ideograph-54BF | 54be CJK Ideograph-54BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c1 CJK Ideograph-54C1 | 54c0 CJK Ideograph-54C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c3 CJK Ideograph-54C3 | 54c2 CJK Ideograph-54C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c5 CJK Ideograph-54C5 | 54c4 CJK Ideograph-54C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c7 CJK Ideograph-54C7 | 54c6 CJK Ideograph-54C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54c9 CJK Ideograph-54C9 | 54c8 CJK Ideograph-54C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cb CJK Ideograph-54CB | 54ca CJK Ideograph-54CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cd CJK Ideograph-54CD | 54cc CJK Ideograph-54CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54cf CJK Ideograph-54CF | 54ce CJK Ideograph-54CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d1 CJK Ideograph-54D1 | 54d0 CJK Ideograph-54D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d3 CJK Ideograph-54D3 | 54d2 CJK Ideograph-54D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d5 CJK Ideograph-54D5 | 54d4 CJK Ideograph-54D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d7 CJK Ideograph-54D7 | 54d6 CJK Ideograph-54D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54d9 CJK Ideograph-54D9 | 54d8 CJK Ideograph-54D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54db CJK Ideograph-54DB | 54da CJK Ideograph-54DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54dd CJK Ideograph-54DD | 54dc CJK Ideograph-54DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54df CJK Ideograph-54DF | 54de CJK Ideograph-54DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e1 CJK Ideograph-54E1 | 54e0 CJK Ideograph-54E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e3 CJK Ideograph-54E3 | 54e2 CJK Ideograph-54E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e5 CJK Ideograph-54E5 | 54e4 CJK Ideograph-54E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e7 CJK Ideograph-54E7 | 54e6 CJK Ideograph-54E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54e9 CJK Ideograph-54E9 | 54e8 CJK Ideograph-54E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54eb CJK Ideograph-54EB | 54ea CJK Ideograph-54EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ed CJK Ideograph-54ED | 54ec CJK Ideograph-54EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ef CJK Ideograph-54EF | 54ee CJK Ideograph-54EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f1 CJK Ideograph-54F1 | 54f0 CJK Ideograph-54F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f3 CJK Ideograph-54F3 | 54f2 CJK Ideograph-54F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f5 CJK Ideograph-54F5 | 54f4 CJK Ideograph-54F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f7 CJK Ideograph-54F7 | 54f6 CJK Ideograph-54F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54f9 CJK Ideograph-54F9 | 54f8 CJK Ideograph-54F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54fb CJK Ideograph-54FB | 54fa CJK Ideograph-54FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54fd CJK Ideograph-54FD | 54fc CJK Ideograph-54FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 54ff CJK Ideograph-54FF | 54fe CJK Ideograph-54FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5501 CJK Ideograph-5501 | 5500 CJK Ideograph-5500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5503 CJK Ideograph-5503 | 5502 CJK Ideograph-5502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5505 CJK Ideograph-5505 | 5504 CJK Ideograph-5504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5507 CJK Ideograph-5507 | 5506 CJK Ideograph-5506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5509 CJK Ideograph-5509 | 5508 CJK Ideograph-5508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550b CJK Ideograph-550B | 550a CJK Ideograph-550A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550d CJK Ideograph-550D | 550c CJK Ideograph-550C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 550f CJK Ideograph-550F | 550e CJK Ideograph-550E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5511 CJK Ideograph-5511 | 5510 CJK Ideograph-5510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5513 CJK Ideograph-5513 | 5512 CJK Ideograph-5512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5515 CJK Ideograph-5515 | 5514 CJK Ideograph-5514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5517 CJK Ideograph-5517 | 5516 CJK Ideograph-5516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5519 CJK Ideograph-5519 | 5518 CJK Ideograph-5518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551b CJK Ideograph-551B | 551a CJK Ideograph-551A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551d CJK Ideograph-551D | 551c CJK Ideograph-551C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 551f CJK Ideograph-551F | 551e CJK Ideograph-551E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5521 CJK Ideograph-5521 | 5520 CJK Ideograph-5520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5523 CJK Ideograph-5523 | 5522 CJK Ideograph-5522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5525 CJK Ideograph-5525 | 5524 CJK Ideograph-5524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5527 CJK Ideograph-5527 | 5526 CJK Ideograph-5526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5529 CJK Ideograph-5529 | 5528 CJK Ideograph-5528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552b CJK Ideograph-552B | 552a CJK Ideograph-552A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552d CJK Ideograph-552D | 552c CJK Ideograph-552C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 552f CJK Ideograph-552F | 552e CJK Ideograph-552E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5531 CJK Ideograph-5531 | 5530 CJK Ideograph-5530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5533 CJK Ideograph-5533 | 5532 CJK Ideograph-5532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5535 CJK Ideograph-5535 | 5534 CJK Ideograph-5534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5537 CJK Ideograph-5537 | 5536 CJK Ideograph-5536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5539 CJK Ideograph-5539 | 5538 CJK Ideograph-5538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553b CJK Ideograph-553B | 553a CJK Ideograph-553A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553d CJK Ideograph-553D | 553c CJK Ideograph-553C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 553f CJK Ideograph-553F | 553e CJK Ideograph-553E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5541 CJK Ideograph-5541 | 5540 CJK Ideograph-5540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5543 CJK Ideograph-5543 | 5542 CJK Ideograph-5542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5545 CJK Ideograph-5545 | 5544 CJK Ideograph-5544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5547 CJK Ideograph-5547 | 5546 CJK Ideograph-5546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5549 CJK Ideograph-5549 | 5548 CJK Ideograph-5548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554b CJK Ideograph-554B | 554a CJK Ideograph-554A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554d CJK Ideograph-554D | 554c CJK Ideograph-554C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 554f CJK Ideograph-554F | 554e CJK Ideograph-554E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5551 CJK Ideograph-5551 | 5550 CJK Ideograph-5550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5553 CJK Ideograph-5553 | 5552 CJK Ideograph-5552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5555 CJK Ideograph-5555 | 5554 CJK Ideograph-5554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5557 CJK Ideograph-5557 | 5556 CJK Ideograph-5556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5559 CJK Ideograph-5559 | 5558 CJK Ideograph-5558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555b CJK Ideograph-555B | 555a CJK Ideograph-555A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555d CJK Ideograph-555D | 555c CJK Ideograph-555C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 555f CJK Ideograph-555F | 555e CJK Ideograph-555E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5561 CJK Ideograph-5561 | 5560 CJK Ideograph-5560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5563 CJK Ideograph-5563 | 5562 CJK Ideograph-5562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5565 CJK Ideograph-5565 | 5564 CJK Ideograph-5564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5567 CJK Ideograph-5567 | 5566 CJK Ideograph-5566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5569 CJK Ideograph-5569 | 5568 CJK Ideograph-5568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556b CJK Ideograph-556B | 556a CJK Ideograph-556A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556d CJK Ideograph-556D | 556c CJK Ideograph-556C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 556f CJK Ideograph-556F | 556e CJK Ideograph-556E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5571 CJK Ideograph-5571 | 5570 CJK Ideograph-5570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5573 CJK Ideograph-5573 | 5572 CJK Ideograph-5572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5575 CJK Ideograph-5575 | 5574 CJK Ideograph-5574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5577 CJK Ideograph-5577 | 5576 CJK Ideograph-5576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5579 CJK Ideograph-5579 | 5578 CJK Ideograph-5578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557b CJK Ideograph-557B | 557a CJK Ideograph-557A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557d CJK Ideograph-557D | 557c CJK Ideograph-557C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 557f CJK Ideograph-557F | 557e CJK Ideograph-557E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5581 CJK Ideograph-5581 | 5580 CJK Ideograph-5580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5583 CJK Ideograph-5583 | 5582 CJK Ideograph-5582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5585 CJK Ideograph-5585 | 5584 CJK Ideograph-5584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5587 CJK Ideograph-5587 | 5586 CJK Ideograph-5586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5589 CJK Ideograph-5589 | 5588 CJK Ideograph-5588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558b CJK Ideograph-558B | 558a CJK Ideograph-558A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558d CJK Ideograph-558D | 558c CJK Ideograph-558C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 558f CJK Ideograph-558F | 558e CJK Ideograph-558E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5591 CJK Ideograph-5591 | 5590 CJK Ideograph-5590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5593 CJK Ideograph-5593 | 5592 CJK Ideograph-5592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5595 CJK Ideograph-5595 | 5594 CJK Ideograph-5594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5597 CJK Ideograph-5597 | 5596 CJK Ideograph-5596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5599 CJK Ideograph-5599 | 5598 CJK Ideograph-5598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559b CJK Ideograph-559B | 559a CJK Ideograph-559A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559d CJK Ideograph-559D | 559c CJK Ideograph-559C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 559f CJK Ideograph-559F | 559e CJK Ideograph-559E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a1 CJK Ideograph-55A1 | 55a0 CJK Ideograph-55A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a3 CJK Ideograph-55A3 | 55a2 CJK Ideograph-55A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a5 CJK Ideograph-55A5 | 55a4 CJK Ideograph-55A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a7 CJK Ideograph-55A7 | 55a6 CJK Ideograph-55A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55a9 CJK Ideograph-55A9 | 55a8 CJK Ideograph-55A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ab CJK Ideograph-55AB | 55aa CJK Ideograph-55AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ad CJK Ideograph-55AD | 55ac CJK Ideograph-55AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55af CJK Ideograph-55AF | 55ae CJK Ideograph-55AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b1 CJK Ideograph-55B1 | 55b0 CJK Ideograph-55B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b3 CJK Ideograph-55B3 | 55b2 CJK Ideograph-55B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b5 CJK Ideograph-55B5 | 55b4 CJK Ideograph-55B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b7 CJK Ideograph-55B7 | 55b6 CJK Ideograph-55B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55b9 CJK Ideograph-55B9 | 55b8 CJK Ideograph-55B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bb CJK Ideograph-55BB | 55ba CJK Ideograph-55BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bd CJK Ideograph-55BD | 55bc CJK Ideograph-55BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55bf CJK Ideograph-55BF | 55be CJK Ideograph-55BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c1 CJK Ideograph-55C1 | 55c0 CJK Ideograph-55C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c3 CJK Ideograph-55C3 | 55c2 CJK Ideograph-55C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c5 CJK Ideograph-55C5 | 55c4 CJK Ideograph-55C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c7 CJK Ideograph-55C7 | 55c6 CJK Ideograph-55C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55c9 CJK Ideograph-55C9 | 55c8 CJK Ideograph-55C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cb CJK Ideograph-55CB | 55ca CJK Ideograph-55CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cd CJK Ideograph-55CD | 55cc CJK Ideograph-55CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55cf CJK Ideograph-55CF | 55ce CJK Ideograph-55CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d1 CJK Ideograph-55D1 | 55d0 CJK Ideograph-55D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d3 CJK Ideograph-55D3 | 55d2 CJK Ideograph-55D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d5 CJK Ideograph-55D5 | 55d4 CJK Ideograph-55D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d7 CJK Ideograph-55D7 | 55d6 CJK Ideograph-55D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55d9 CJK Ideograph-55D9 | 55d8 CJK Ideograph-55D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55db CJK Ideograph-55DB | 55da CJK Ideograph-55DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55dd CJK Ideograph-55DD | 55dc CJK Ideograph-55DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55df CJK Ideograph-55DF | 55de CJK Ideograph-55DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e1 CJK Ideograph-55E1 | 55e0 CJK Ideograph-55E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e3 CJK Ideograph-55E3 | 55e2 CJK Ideograph-55E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e5 CJK Ideograph-55E5 | 55e4 CJK Ideograph-55E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e7 CJK Ideograph-55E7 | 55e6 CJK Ideograph-55E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55e9 CJK Ideograph-55E9 | 55e8 CJK Ideograph-55E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55eb CJK Ideograph-55EB | 55ea CJK Ideograph-55EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ed CJK Ideograph-55ED | 55ec CJK Ideograph-55EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ef CJK Ideograph-55EF | 55ee CJK Ideograph-55EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f1 CJK Ideograph-55F1 | 55f0 CJK Ideograph-55F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f3 CJK Ideograph-55F3 | 55f2 CJK Ideograph-55F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f5 CJK Ideograph-55F5 | 55f4 CJK Ideograph-55F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f7 CJK Ideograph-55F7 | 55f6 CJK Ideograph-55F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55f9 CJK Ideograph-55F9 | 55f8 CJK Ideograph-55F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55fb CJK Ideograph-55FB | 55fa CJK Ideograph-55FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55fd CJK Ideograph-55FD | 55fc CJK Ideograph-55FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 55ff CJK Ideograph-55FF | 55fe CJK Ideograph-55FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5601 CJK Ideograph-5601 | 5600 CJK Ideograph-5600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5603 CJK Ideograph-5603 | 5602 CJK Ideograph-5602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5605 CJK Ideograph-5605 | 5604 CJK Ideograph-5604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5607 CJK Ideograph-5607 | 5606 CJK Ideograph-5606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5609 CJK Ideograph-5609 | 5608 CJK Ideograph-5608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560b CJK Ideograph-560B | 560a CJK Ideograph-560A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560d CJK Ideograph-560D | 560c CJK Ideograph-560C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 560f CJK Ideograph-560F | 560e CJK Ideograph-560E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5611 CJK Ideograph-5611 | 5610 CJK Ideograph-5610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5613 CJK Ideograph-5613 | 5612 CJK Ideograph-5612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5615 CJK Ideograph-5615 | 5614 CJK Ideograph-5614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5617 CJK Ideograph-5617 | 5616 CJK Ideograph-5616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5619 CJK Ideograph-5619 | 5618 CJK Ideograph-5618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561b CJK Ideograph-561B | 561a CJK Ideograph-561A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561d CJK Ideograph-561D | 561c CJK Ideograph-561C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 561f CJK Ideograph-561F | 561e CJK Ideograph-561E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5621 CJK Ideograph-5621 | 5620 CJK Ideograph-5620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5623 CJK Ideograph-5623 | 5622 CJK Ideograph-5622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5625 CJK Ideograph-5625 | 5624 CJK Ideograph-5624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5627 CJK Ideograph-5627 | 5626 CJK Ideograph-5626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5629 CJK Ideograph-5629 | 5628 CJK Ideograph-5628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562b CJK Ideograph-562B | 562a CJK Ideograph-562A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562d CJK Ideograph-562D | 562c CJK Ideograph-562C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 562f CJK Ideograph-562F | 562e CJK Ideograph-562E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5631 CJK Ideograph-5631 | 5630 CJK Ideograph-5630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5633 CJK Ideograph-5633 | 5632 CJK Ideograph-5632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5635 CJK Ideograph-5635 | 5634 CJK Ideograph-5634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5637 CJK Ideograph-5637 | 5636 CJK Ideograph-5636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5639 CJK Ideograph-5639 | 5638 CJK Ideograph-5638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563b CJK Ideograph-563B | 563a CJK Ideograph-563A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563d CJK Ideograph-563D | 563c CJK Ideograph-563C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 563f CJK Ideograph-563F | 563e CJK Ideograph-563E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5641 CJK Ideograph-5641 | 5640 CJK Ideograph-5640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5643 CJK Ideograph-5643 | 5642 CJK Ideograph-5642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5645 CJK Ideograph-5645 | 5644 CJK Ideograph-5644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5647 CJK Ideograph-5647 | 5646 CJK Ideograph-5646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5649 CJK Ideograph-5649 | 5648 CJK Ideograph-5648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564b CJK Ideograph-564B | 564a CJK Ideograph-564A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564d CJK Ideograph-564D | 564c CJK Ideograph-564C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 564f CJK Ideograph-564F | 564e CJK Ideograph-564E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5651 CJK Ideograph-5651 | 5650 CJK Ideograph-5650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5653 CJK Ideograph-5653 | 5652 CJK Ideograph-5652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5655 CJK Ideograph-5655 | 5654 CJK Ideograph-5654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5657 CJK Ideograph-5657 | 5656 CJK Ideograph-5656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5659 CJK Ideograph-5659 | 5658 CJK Ideograph-5658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565b CJK Ideograph-565B | 565a CJK Ideograph-565A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565d CJK Ideograph-565D | 565c CJK Ideograph-565C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 565f CJK Ideograph-565F | 565e CJK Ideograph-565E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5661 CJK Ideograph-5661 | 5660 CJK Ideograph-5660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5663 CJK Ideograph-5663 | 5662 CJK Ideograph-5662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5665 CJK Ideograph-5665 | 5664 CJK Ideograph-5664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5667 CJK Ideograph-5667 | 5666 CJK Ideograph-5666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5669 CJK Ideograph-5669 | 5668 CJK Ideograph-5668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566b CJK Ideograph-566B | 566a CJK Ideograph-566A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566d CJK Ideograph-566D | 566c CJK Ideograph-566C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 566f CJK Ideograph-566F | 566e CJK Ideograph-566E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5671 CJK Ideograph-5671 | 5670 CJK Ideograph-5670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5673 CJK Ideograph-5673 | 5672 CJK Ideograph-5672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5675 CJK Ideograph-5675 | 5674 CJK Ideograph-5674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5677 CJK Ideograph-5677 | 5676 CJK Ideograph-5676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5679 CJK Ideograph-5679 | 5678 CJK Ideograph-5678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567b CJK Ideograph-567B | 567a CJK Ideograph-567A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567d CJK Ideograph-567D | 567c CJK Ideograph-567C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 567f CJK Ideograph-567F | 567e CJK Ideograph-567E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5681 CJK Ideograph-5681 | 5680 CJK Ideograph-5680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5683 CJK Ideograph-5683 | 5682 CJK Ideograph-5682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5685 CJK Ideograph-5685 | 5684 CJK Ideograph-5684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5687 CJK Ideograph-5687 | 5686 CJK Ideograph-5686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5689 CJK Ideograph-5689 | 5688 CJK Ideograph-5688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568b CJK Ideograph-568B | 568a CJK Ideograph-568A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568d CJK Ideograph-568D | 568c CJK Ideograph-568C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 568f CJK Ideograph-568F | 568e CJK Ideograph-568E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5691 CJK Ideograph-5691 | 5690 CJK Ideograph-5690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5693 CJK Ideograph-5693 | 5692 CJK Ideograph-5692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5695 CJK Ideograph-5695 | 5694 CJK Ideograph-5694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5697 CJK Ideograph-5697 | 5696 CJK Ideograph-5696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5699 CJK Ideograph-5699 | 5698 CJK Ideograph-5698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569b CJK Ideograph-569B | 569a CJK Ideograph-569A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569d CJK Ideograph-569D | 569c CJK Ideograph-569C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 569f CJK Ideograph-569F | 569e CJK Ideograph-569E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a1 CJK Ideograph-56A1 | 56a0 CJK Ideograph-56A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a3 CJK Ideograph-56A3 | 56a2 CJK Ideograph-56A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a5 CJK Ideograph-56A5 | 56a4 CJK Ideograph-56A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a7 CJK Ideograph-56A7 | 56a6 CJK Ideograph-56A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56a9 CJK Ideograph-56A9 | 56a8 CJK Ideograph-56A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ab CJK Ideograph-56AB | 56aa CJK Ideograph-56AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ad CJK Ideograph-56AD | 56ac CJK Ideograph-56AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56af CJK Ideograph-56AF | 56ae CJK Ideograph-56AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b1 CJK Ideograph-56B1 | 56b0 CJK Ideograph-56B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b3 CJK Ideograph-56B3 | 56b2 CJK Ideograph-56B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b5 CJK Ideograph-56B5 | 56b4 CJK Ideograph-56B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b7 CJK Ideograph-56B7 | 56b6 CJK Ideograph-56B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56b9 CJK Ideograph-56B9 | 56b8 CJK Ideograph-56B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bb CJK Ideograph-56BB | 56ba CJK Ideograph-56BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bd CJK Ideograph-56BD | 56bc CJK Ideograph-56BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56bf CJK Ideograph-56BF | 56be CJK Ideograph-56BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c1 CJK Ideograph-56C1 | 56c0 CJK Ideograph-56C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c3 CJK Ideograph-56C3 | 56c2 CJK Ideograph-56C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c5 CJK Ideograph-56C5 | 56c4 CJK Ideograph-56C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c7 CJK Ideograph-56C7 | 56c6 CJK Ideograph-56C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56c9 CJK Ideograph-56C9 | 56c8 CJK Ideograph-56C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cb CJK Ideograph-56CB | 56ca CJK Ideograph-56CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cd CJK Ideograph-56CD | 56cc CJK Ideograph-56CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56cf CJK Ideograph-56CF | 56ce CJK Ideograph-56CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d1 CJK Ideograph-56D1 | 56d0 CJK Ideograph-56D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d3 CJK Ideograph-56D3 | 56d2 CJK Ideograph-56D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d5 CJK Ideograph-56D5 | 56d4 CJK Ideograph-56D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d7 CJK Ideograph-56D7 | 56d6 CJK Ideograph-56D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56d9 CJK Ideograph-56D9 | 56d8 CJK Ideograph-56D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56db CJK Ideograph-56DB | 56da CJK Ideograph-56DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56dd CJK Ideograph-56DD | 56dc CJK Ideograph-56DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56df CJK Ideograph-56DF | 56de CJK Ideograph-56DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e1 CJK Ideograph-56E1 | 56e0 CJK Ideograph-56E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e3 CJK Ideograph-56E3 | 56e2 CJK Ideograph-56E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e5 CJK Ideograph-56E5 | 56e4 CJK Ideograph-56E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e7 CJK Ideograph-56E7 | 56e6 CJK Ideograph-56E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56e9 CJK Ideograph-56E9 | 56e8 CJK Ideograph-56E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56eb CJK Ideograph-56EB | 56ea CJK Ideograph-56EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ed CJK Ideograph-56ED | 56ec CJK Ideograph-56EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ef CJK Ideograph-56EF | 56ee CJK Ideograph-56EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f1 CJK Ideograph-56F1 | 56f0 CJK Ideograph-56F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f3 CJK Ideograph-56F3 | 56f2 CJK Ideograph-56F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f5 CJK Ideograph-56F5 | 56f4 CJK Ideograph-56F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f7 CJK Ideograph-56F7 | 56f6 CJK Ideograph-56F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56f9 CJK Ideograph-56F9 | 56f8 CJK Ideograph-56F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56fb CJK Ideograph-56FB | 56fa CJK Ideograph-56FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56fd CJK Ideograph-56FD | 56fc CJK Ideograph-56FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 56ff CJK Ideograph-56FF | 56fe CJK Ideograph-56FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5701 CJK Ideograph-5701 | 5700 CJK Ideograph-5700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5703 CJK Ideograph-5703 | 5702 CJK Ideograph-5702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5705 CJK Ideograph-5705 | 5704 CJK Ideograph-5704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5707 CJK Ideograph-5707 | 5706 CJK Ideograph-5706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5709 CJK Ideograph-5709 | 5708 CJK Ideograph-5708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570b CJK Ideograph-570B | 570a CJK Ideograph-570A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570d CJK Ideograph-570D | 570c CJK Ideograph-570C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 570f CJK Ideograph-570F | 570e CJK Ideograph-570E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5711 CJK Ideograph-5711 | 5710 CJK Ideograph-5710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5713 CJK Ideograph-5713 | 5712 CJK Ideograph-5712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5715 CJK Ideograph-5715 | 5714 CJK Ideograph-5714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5717 CJK Ideograph-5717 | 5716 CJK Ideograph-5716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5719 CJK Ideograph-5719 | 5718 CJK Ideograph-5718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571b CJK Ideograph-571B | 571a CJK Ideograph-571A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571d CJK Ideograph-571D | 571c CJK Ideograph-571C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 571f CJK Ideograph-571F | 571e CJK Ideograph-571E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5721 CJK Ideograph-5721 | 5720 CJK Ideograph-5720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5723 CJK Ideograph-5723 | 5722 CJK Ideograph-5722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5725 CJK Ideograph-5725 | 5724 CJK Ideograph-5724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5727 CJK Ideograph-5727 | 5726 CJK Ideograph-5726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5729 CJK Ideograph-5729 | 5728 CJK Ideograph-5728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572b CJK Ideograph-572B | 572a CJK Ideograph-572A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572d CJK Ideograph-572D | 572c CJK Ideograph-572C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 572f CJK Ideograph-572F | 572e CJK Ideograph-572E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5731 CJK Ideograph-5731 | 5730 CJK Ideograph-5730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5733 CJK Ideograph-5733 | 5732 CJK Ideograph-5732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5735 CJK Ideograph-5735 | 5734 CJK Ideograph-5734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5737 CJK Ideograph-5737 | 5736 CJK Ideograph-5736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5739 CJK Ideograph-5739 | 5738 CJK Ideograph-5738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573b CJK Ideograph-573B | 573a CJK Ideograph-573A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573d CJK Ideograph-573D | 573c CJK Ideograph-573C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 573f CJK Ideograph-573F | 573e CJK Ideograph-573E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5741 CJK Ideograph-5741 | 5740 CJK Ideograph-5740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5743 CJK Ideograph-5743 | 5742 CJK Ideograph-5742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5745 CJK Ideograph-5745 | 5744 CJK Ideograph-5744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5747 CJK Ideograph-5747 | 5746 CJK Ideograph-5746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5749 CJK Ideograph-5749 | 5748 CJK Ideograph-5748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574b CJK Ideograph-574B | 574a CJK Ideograph-574A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574d CJK Ideograph-574D | 574c CJK Ideograph-574C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 574f CJK Ideograph-574F | 574e CJK Ideograph-574E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5751 CJK Ideograph-5751 | 5750 CJK Ideograph-5750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5753 CJK Ideograph-5753 | 5752 CJK Ideograph-5752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5755 CJK Ideograph-5755 | 5754 CJK Ideograph-5754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5757 CJK Ideograph-5757 | 5756 CJK Ideograph-5756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5759 CJK Ideograph-5759 | 5758 CJK Ideograph-5758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575b CJK Ideograph-575B | 575a CJK Ideograph-575A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575d CJK Ideograph-575D | 575c CJK Ideograph-575C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 575f CJK Ideograph-575F | 575e CJK Ideograph-575E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5761 CJK Ideograph-5761 | 5760 CJK Ideograph-5760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5763 CJK Ideograph-5763 | 5762 CJK Ideograph-5762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5765 CJK Ideograph-5765 | 5764 CJK Ideograph-5764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5767 CJK Ideograph-5767 | 5766 CJK Ideograph-5766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5769 CJK Ideograph-5769 | 5768 CJK Ideograph-5768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576b CJK Ideograph-576B | 576a CJK Ideograph-576A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576d CJK Ideograph-576D | 576c CJK Ideograph-576C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 576f CJK Ideograph-576F | 576e CJK Ideograph-576E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5771 CJK Ideograph-5771 | 5770 CJK Ideograph-5770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5773 CJK Ideograph-5773 | 5772 CJK Ideograph-5772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5775 CJK Ideograph-5775 | 5774 CJK Ideograph-5774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5777 CJK Ideograph-5777 | 5776 CJK Ideograph-5776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5779 CJK Ideograph-5779 | 5778 CJK Ideograph-5778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577b CJK Ideograph-577B | 577a CJK Ideograph-577A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577d CJK Ideograph-577D | 577c CJK Ideograph-577C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 577f CJK Ideograph-577F | 577e CJK Ideograph-577E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5781 CJK Ideograph-5781 | 5780 CJK Ideograph-5780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5783 CJK Ideograph-5783 | 5782 CJK Ideograph-5782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5785 CJK Ideograph-5785 | 5784 CJK Ideograph-5784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5787 CJK Ideograph-5787 | 5786 CJK Ideograph-5786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5789 CJK Ideograph-5789 | 5788 CJK Ideograph-5788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578b CJK Ideograph-578B | 578a CJK Ideograph-578A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578d CJK Ideograph-578D | 578c CJK Ideograph-578C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 578f CJK Ideograph-578F | 578e CJK Ideograph-578E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5791 CJK Ideograph-5791 | 5790 CJK Ideograph-5790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5793 CJK Ideograph-5793 | 5792 CJK Ideograph-5792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5795 CJK Ideograph-5795 | 5794 CJK Ideograph-5794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5797 CJK Ideograph-5797 | 5796 CJK Ideograph-5796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5799 CJK Ideograph-5799 | 5798 CJK Ideograph-5798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579b CJK Ideograph-579B | 579a CJK Ideograph-579A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579d CJK Ideograph-579D | 579c CJK Ideograph-579C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 579f CJK Ideograph-579F | 579e CJK Ideograph-579E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a1 CJK Ideograph-57A1 | 57a0 CJK Ideograph-57A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a3 CJK Ideograph-57A3 | 57a2 CJK Ideograph-57A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a5 CJK Ideograph-57A5 | 57a4 CJK Ideograph-57A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a7 CJK Ideograph-57A7 | 57a6 CJK Ideograph-57A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57a9 CJK Ideograph-57A9 | 57a8 CJK Ideograph-57A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ab CJK Ideograph-57AB | 57aa CJK Ideograph-57AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ad CJK Ideograph-57AD | 57ac CJK Ideograph-57AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57af CJK Ideograph-57AF | 57ae CJK Ideograph-57AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b1 CJK Ideograph-57B1 | 57b0 CJK Ideograph-57B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b3 CJK Ideograph-57B3 | 57b2 CJK Ideograph-57B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b5 CJK Ideograph-57B5 | 57b4 CJK Ideograph-57B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b7 CJK Ideograph-57B7 | 57b6 CJK Ideograph-57B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57b9 CJK Ideograph-57B9 | 57b8 CJK Ideograph-57B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bb CJK Ideograph-57BB | 57ba CJK Ideograph-57BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bd CJK Ideograph-57BD | 57bc CJK Ideograph-57BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57bf CJK Ideograph-57BF | 57be CJK Ideograph-57BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c1 CJK Ideograph-57C1 | 57c0 CJK Ideograph-57C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c3 CJK Ideograph-57C3 | 57c2 CJK Ideograph-57C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c5 CJK Ideograph-57C5 | 57c4 CJK Ideograph-57C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c7 CJK Ideograph-57C7 | 57c6 CJK Ideograph-57C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57c9 CJK Ideograph-57C9 | 57c8 CJK Ideograph-57C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cb CJK Ideograph-57CB | 57ca CJK Ideograph-57CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cd CJK Ideograph-57CD | 57cc CJK Ideograph-57CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57cf CJK Ideograph-57CF | 57ce CJK Ideograph-57CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d1 CJK Ideograph-57D1 | 57d0 CJK Ideograph-57D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d3 CJK Ideograph-57D3 | 57d2 CJK Ideograph-57D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d5 CJK Ideograph-57D5 | 57d4 CJK Ideograph-57D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d7 CJK Ideograph-57D7 | 57d6 CJK Ideograph-57D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57d9 CJK Ideograph-57D9 | 57d8 CJK Ideograph-57D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57db CJK Ideograph-57DB | 57da CJK Ideograph-57DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57dd CJK Ideograph-57DD | 57dc CJK Ideograph-57DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57df CJK Ideograph-57DF | 57de CJK Ideograph-57DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e1 CJK Ideograph-57E1 | 57e0 CJK Ideograph-57E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e3 CJK Ideograph-57E3 | 57e2 CJK Ideograph-57E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e5 CJK Ideograph-57E5 | 57e4 CJK Ideograph-57E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e7 CJK Ideograph-57E7 | 57e6 CJK Ideograph-57E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57e9 CJK Ideograph-57E9 | 57e8 CJK Ideograph-57E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57eb CJK Ideograph-57EB | 57ea CJK Ideograph-57EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ed CJK Ideograph-57ED | 57ec CJK Ideograph-57EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ef CJK Ideograph-57EF | 57ee CJK Ideograph-57EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f1 CJK Ideograph-57F1 | 57f0 CJK Ideograph-57F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f3 CJK Ideograph-57F3 | 57f2 CJK Ideograph-57F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f5 CJK Ideograph-57F5 | 57f4 CJK Ideograph-57F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f7 CJK Ideograph-57F7 | 57f6 CJK Ideograph-57F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57f9 CJK Ideograph-57F9 | 57f8 CJK Ideograph-57F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57fb CJK Ideograph-57FB | 57fa CJK Ideograph-57FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57fd CJK Ideograph-57FD | 57fc CJK Ideograph-57FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 57ff CJK Ideograph-57FF | 57fe CJK Ideograph-57FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5801 CJK Ideograph-5801 | 5800 CJK Ideograph-5800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5803 CJK Ideograph-5803 | 5802 CJK Ideograph-5802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5805 CJK Ideograph-5805 | 5804 CJK Ideograph-5804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5807 CJK Ideograph-5807 | 5806 CJK Ideograph-5806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5809 CJK Ideograph-5809 | 5808 CJK Ideograph-5808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580b CJK Ideograph-580B | 580a CJK Ideograph-580A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580d CJK Ideograph-580D | 580c CJK Ideograph-580C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 580f CJK Ideograph-580F | 580e CJK Ideograph-580E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5811 CJK Ideograph-5811 | 5810 CJK Ideograph-5810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5813 CJK Ideograph-5813 | 5812 CJK Ideograph-5812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5815 CJK Ideograph-5815 | 5814 CJK Ideograph-5814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5817 CJK Ideograph-5817 | 5816 CJK Ideograph-5816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5819 CJK Ideograph-5819 | 5818 CJK Ideograph-5818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581b CJK Ideograph-581B | 581a CJK Ideograph-581A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581d CJK Ideograph-581D | 581c CJK Ideograph-581C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 581f CJK Ideograph-581F | 581e CJK Ideograph-581E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5821 CJK Ideograph-5821 | 5820 CJK Ideograph-5820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5823 CJK Ideograph-5823 | 5822 CJK Ideograph-5822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5825 CJK Ideograph-5825 | 5824 CJK Ideograph-5824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5827 CJK Ideograph-5827 | 5826 CJK Ideograph-5826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5829 CJK Ideograph-5829 | 5828 CJK Ideograph-5828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582b CJK Ideograph-582B | 582a CJK Ideograph-582A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582d CJK Ideograph-582D | 582c CJK Ideograph-582C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 582f CJK Ideograph-582F | 582e CJK Ideograph-582E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5831 CJK Ideograph-5831 | 5830 CJK Ideograph-5830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5833 CJK Ideograph-5833 | 5832 CJK Ideograph-5832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5835 CJK Ideograph-5835 | 5834 CJK Ideograph-5834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5837 CJK Ideograph-5837 | 5836 CJK Ideograph-5836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5839 CJK Ideograph-5839 | 5838 CJK Ideograph-5838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583b CJK Ideograph-583B | 583a CJK Ideograph-583A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583d CJK Ideograph-583D | 583c CJK Ideograph-583C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 583f CJK Ideograph-583F | 583e CJK Ideograph-583E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5841 CJK Ideograph-5841 | 5840 CJK Ideograph-5840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5843 CJK Ideograph-5843 | 5842 CJK Ideograph-5842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5845 CJK Ideograph-5845 | 5844 CJK Ideograph-5844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5847 CJK Ideograph-5847 | 5846 CJK Ideograph-5846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5849 CJK Ideograph-5849 | 5848 CJK Ideograph-5848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584b CJK Ideograph-584B | 584a CJK Ideograph-584A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584d CJK Ideograph-584D | 584c CJK Ideograph-584C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 584f CJK Ideograph-584F | 584e CJK Ideograph-584E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5851 CJK Ideograph-5851 | 5850 CJK Ideograph-5850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5853 CJK Ideograph-5853 | 5852 CJK Ideograph-5852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5855 CJK Ideograph-5855 | 5854 CJK Ideograph-5854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5857 CJK Ideograph-5857 | 5856 CJK Ideograph-5856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5859 CJK Ideograph-5859 | 5858 CJK Ideograph-5858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585b CJK Ideograph-585B | 585a CJK Ideograph-585A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585d CJK Ideograph-585D | 585c CJK Ideograph-585C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 585f CJK Ideograph-585F | 585e CJK Ideograph-585E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5861 CJK Ideograph-5861 | 5860 CJK Ideograph-5860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5863 CJK Ideograph-5863 | 5862 CJK Ideograph-5862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5865 CJK Ideograph-5865 | 5864 CJK Ideograph-5864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5867 CJK Ideograph-5867 | 5866 CJK Ideograph-5866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5869 CJK Ideograph-5869 | 5868 CJK Ideograph-5868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586b CJK Ideograph-586B | 586a CJK Ideograph-586A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586d CJK Ideograph-586D | 586c CJK Ideograph-586C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 586f CJK Ideograph-586F | 586e CJK Ideograph-586E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5871 CJK Ideograph-5871 | 5870 CJK Ideograph-5870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5873 CJK Ideograph-5873 | 5872 CJK Ideograph-5872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5875 CJK Ideograph-5875 | 5874 CJK Ideograph-5874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5877 CJK Ideograph-5877 | 5876 CJK Ideograph-5876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5879 CJK Ideograph-5879 | 5878 CJK Ideograph-5878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587b CJK Ideograph-587B | 587a CJK Ideograph-587A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587d CJK Ideograph-587D | 587c CJK Ideograph-587C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 587f CJK Ideograph-587F | 587e CJK Ideograph-587E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5881 CJK Ideograph-5881 | 5880 CJK Ideograph-5880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5883 CJK Ideograph-5883 | 5882 CJK Ideograph-5882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5885 CJK Ideograph-5885 | 5884 CJK Ideograph-5884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5887 CJK Ideograph-5887 | 5886 CJK Ideograph-5886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5889 CJK Ideograph-5889 | 5888 CJK Ideograph-5888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588b CJK Ideograph-588B | 588a CJK Ideograph-588A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588d CJK Ideograph-588D | 588c CJK Ideograph-588C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 588f CJK Ideograph-588F | 588e CJK Ideograph-588E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5891 CJK Ideograph-5891 | 5890 CJK Ideograph-5890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5893 CJK Ideograph-5893 | 5892 CJK Ideograph-5892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5895 CJK Ideograph-5895 | 5894 CJK Ideograph-5894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5897 CJK Ideograph-5897 | 5896 CJK Ideograph-5896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5899 CJK Ideograph-5899 | 5898 CJK Ideograph-5898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589b CJK Ideograph-589B | 589a CJK Ideograph-589A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589d CJK Ideograph-589D | 589c CJK Ideograph-589C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 589f CJK Ideograph-589F | 589e CJK Ideograph-589E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a1 CJK Ideograph-58A1 | 58a0 CJK Ideograph-58A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a3 CJK Ideograph-58A3 | 58a2 CJK Ideograph-58A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a5 CJK Ideograph-58A5 | 58a4 CJK Ideograph-58A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a7 CJK Ideograph-58A7 | 58a6 CJK Ideograph-58A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58a9 CJK Ideograph-58A9 | 58a8 CJK Ideograph-58A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ab CJK Ideograph-58AB | 58aa CJK Ideograph-58AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ad CJK Ideograph-58AD | 58ac CJK Ideograph-58AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58af CJK Ideograph-58AF | 58ae CJK Ideograph-58AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b1 CJK Ideograph-58B1 | 58b0 CJK Ideograph-58B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b3 CJK Ideograph-58B3 | 58b2 CJK Ideograph-58B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b5 CJK Ideograph-58B5 | 58b4 CJK Ideograph-58B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b7 CJK Ideograph-58B7 | 58b6 CJK Ideograph-58B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58b9 CJK Ideograph-58B9 | 58b8 CJK Ideograph-58B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bb CJK Ideograph-58BB | 58ba CJK Ideograph-58BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bd CJK Ideograph-58BD | 58bc CJK Ideograph-58BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58bf CJK Ideograph-58BF | 58be CJK Ideograph-58BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c1 CJK Ideograph-58C1 | 58c0 CJK Ideograph-58C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c3 CJK Ideograph-58C3 | 58c2 CJK Ideograph-58C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c5 CJK Ideograph-58C5 | 58c4 CJK Ideograph-58C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c7 CJK Ideograph-58C7 | 58c6 CJK Ideograph-58C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58c9 CJK Ideograph-58C9 | 58c8 CJK Ideograph-58C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cb CJK Ideograph-58CB | 58ca CJK Ideograph-58CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cd CJK Ideograph-58CD | 58cc CJK Ideograph-58CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58cf CJK Ideograph-58CF | 58ce CJK Ideograph-58CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d1 CJK Ideograph-58D1 | 58d0 CJK Ideograph-58D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d3 CJK Ideograph-58D3 | 58d2 CJK Ideograph-58D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d5 CJK Ideograph-58D5 | 58d4 CJK Ideograph-58D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d7 CJK Ideograph-58D7 | 58d6 CJK Ideograph-58D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58d9 CJK Ideograph-58D9 | 58d8 CJK Ideograph-58D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58db CJK Ideograph-58DB | 58da CJK Ideograph-58DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58dd CJK Ideograph-58DD | 58dc CJK Ideograph-58DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58df CJK Ideograph-58DF | 58de CJK Ideograph-58DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e1 CJK Ideograph-58E1 | 58e0 CJK Ideograph-58E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e3 CJK Ideograph-58E3 | 58e2 CJK Ideograph-58E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e5 CJK Ideograph-58E5 | 58e4 CJK Ideograph-58E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e7 CJK Ideograph-58E7 | 58e6 CJK Ideograph-58E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58e9 CJK Ideograph-58E9 | 58e8 CJK Ideograph-58E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58eb CJK Ideograph-58EB | 58ea CJK Ideograph-58EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ed CJK Ideograph-58ED | 58ec CJK Ideograph-58EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ef CJK Ideograph-58EF | 58ee CJK Ideograph-58EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f1 CJK Ideograph-58F1 | 58f0 CJK Ideograph-58F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f3 CJK Ideograph-58F3 | 58f2 CJK Ideograph-58F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f5 CJK Ideograph-58F5 | 58f4 CJK Ideograph-58F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f7 CJK Ideograph-58F7 | 58f6 CJK Ideograph-58F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58f9 CJK Ideograph-58F9 | 58f8 CJK Ideograph-58F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58fb CJK Ideograph-58FB | 58fa CJK Ideograph-58FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58fd CJK Ideograph-58FD | 58fc CJK Ideograph-58FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 58ff CJK Ideograph-58FF | 58fe CJK Ideograph-58FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5901 CJK Ideograph-5901 | 5900 CJK Ideograph-5900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5903 CJK Ideograph-5903 | 5902 CJK Ideograph-5902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5905 CJK Ideograph-5905 | 5904 CJK Ideograph-5904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5907 CJK Ideograph-5907 | 5906 CJK Ideograph-5906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5909 CJK Ideograph-5909 | 5908 CJK Ideograph-5908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590b CJK Ideograph-590B | 590a CJK Ideograph-590A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590d CJK Ideograph-590D | 590c CJK Ideograph-590C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 590f CJK Ideograph-590F | 590e CJK Ideograph-590E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5911 CJK Ideograph-5911 | 5910 CJK Ideograph-5910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5913 CJK Ideograph-5913 | 5912 CJK Ideograph-5912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5915 CJK Ideograph-5915 | 5914 CJK Ideograph-5914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5917 CJK Ideograph-5917 | 5916 CJK Ideograph-5916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5919 CJK Ideograph-5919 | 5918 CJK Ideograph-5918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591b CJK Ideograph-591B | 591a CJK Ideograph-591A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591d CJK Ideograph-591D | 591c CJK Ideograph-591C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 591f CJK Ideograph-591F | 591e CJK Ideograph-591E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5921 CJK Ideograph-5921 | 5920 CJK Ideograph-5920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5923 CJK Ideograph-5923 | 5922 CJK Ideograph-5922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5925 CJK Ideograph-5925 | 5924 CJK Ideograph-5924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5927 CJK Ideograph-5927 | 5926 CJK Ideograph-5926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5929 CJK Ideograph-5929 | 5928 CJK Ideograph-5928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592b CJK Ideograph-592B | 592a CJK Ideograph-592A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592d CJK Ideograph-592D | 592c CJK Ideograph-592C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 592f CJK Ideograph-592F | 592e CJK Ideograph-592E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5931 CJK Ideograph-5931 | 5930 CJK Ideograph-5930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5933 CJK Ideograph-5933 | 5932 CJK Ideograph-5932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5935 CJK Ideograph-5935 | 5934 CJK Ideograph-5934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5937 CJK Ideograph-5937 | 5936 CJK Ideograph-5936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5939 CJK Ideograph-5939 | 5938 CJK Ideograph-5938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593b CJK Ideograph-593B | 593a CJK Ideograph-593A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593d CJK Ideograph-593D | 593c CJK Ideograph-593C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 593f CJK Ideograph-593F | 593e CJK Ideograph-593E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5941 CJK Ideograph-5941 | 5940 CJK Ideograph-5940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5943 CJK Ideograph-5943 | 5942 CJK Ideograph-5942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5945 CJK Ideograph-5945 | 5944 CJK Ideograph-5944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5947 CJK Ideograph-5947 | 5946 CJK Ideograph-5946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5949 CJK Ideograph-5949 | 5948 CJK Ideograph-5948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594b CJK Ideograph-594B | 594a CJK Ideograph-594A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594d CJK Ideograph-594D | 594c CJK Ideograph-594C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 594f CJK Ideograph-594F | 594e CJK Ideograph-594E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5951 CJK Ideograph-5951 | 5950 CJK Ideograph-5950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5953 CJK Ideograph-5953 | 5952 CJK Ideograph-5952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5955 CJK Ideograph-5955 | 5954 CJK Ideograph-5954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5957 CJK Ideograph-5957 | 5956 CJK Ideograph-5956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5959 CJK Ideograph-5959 | 5958 CJK Ideograph-5958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595b CJK Ideograph-595B | 595a CJK Ideograph-595A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595d CJK Ideograph-595D | 595c CJK Ideograph-595C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 595f CJK Ideograph-595F | 595e CJK Ideograph-595E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5961 CJK Ideograph-5961 | 5960 CJK Ideograph-5960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5963 CJK Ideograph-5963 | 5962 CJK Ideograph-5962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5965 CJK Ideograph-5965 | 5964 CJK Ideograph-5964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5967 CJK Ideograph-5967 | 5966 CJK Ideograph-5966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5969 CJK Ideograph-5969 | 5968 CJK Ideograph-5968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596b CJK Ideograph-596B | 596a CJK Ideograph-596A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596d CJK Ideograph-596D | 596c CJK Ideograph-596C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 596f CJK Ideograph-596F | 596e CJK Ideograph-596E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5971 CJK Ideograph-5971 | 5970 CJK Ideograph-5970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5973 CJK Ideograph-5973 | 5972 CJK Ideograph-5972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5975 CJK Ideograph-5975 | 5974 CJK Ideograph-5974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5977 CJK Ideograph-5977 | 5976 CJK Ideograph-5976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5979 CJK Ideograph-5979 | 5978 CJK Ideograph-5978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597b CJK Ideograph-597B | 597a CJK Ideograph-597A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597d CJK Ideograph-597D | 597c CJK Ideograph-597C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 597f CJK Ideograph-597F | 597e CJK Ideograph-597E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5981 CJK Ideograph-5981 | 5980 CJK Ideograph-5980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5983 CJK Ideograph-5983 | 5982 CJK Ideograph-5982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5985 CJK Ideograph-5985 | 5984 CJK Ideograph-5984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5987 CJK Ideograph-5987 | 5986 CJK Ideograph-5986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5989 CJK Ideograph-5989 | 5988 CJK Ideograph-5988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598b CJK Ideograph-598B | 598a CJK Ideograph-598A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598d CJK Ideograph-598D | 598c CJK Ideograph-598C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 598f CJK Ideograph-598F | 598e CJK Ideograph-598E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5991 CJK Ideograph-5991 | 5990 CJK Ideograph-5990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5993 CJK Ideograph-5993 | 5992 CJK Ideograph-5992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5995 CJK Ideograph-5995 | 5994 CJK Ideograph-5994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5997 CJK Ideograph-5997 | 5996 CJK Ideograph-5996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5999 CJK Ideograph-5999 | 5998 CJK Ideograph-5998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599b CJK Ideograph-599B | 599a CJK Ideograph-599A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599d CJK Ideograph-599D | 599c CJK Ideograph-599C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 599f CJK Ideograph-599F | 599e CJK Ideograph-599E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a1 CJK Ideograph-59A1 | 59a0 CJK Ideograph-59A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a3 CJK Ideograph-59A3 | 59a2 CJK Ideograph-59A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a5 CJK Ideograph-59A5 | 59a4 CJK Ideograph-59A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a7 CJK Ideograph-59A7 | 59a6 CJK Ideograph-59A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59a9 CJK Ideograph-59A9 | 59a8 CJK Ideograph-59A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ab CJK Ideograph-59AB | 59aa CJK Ideograph-59AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ad CJK Ideograph-59AD | 59ac CJK Ideograph-59AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59af CJK Ideograph-59AF | 59ae CJK Ideograph-59AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b1 CJK Ideograph-59B1 | 59b0 CJK Ideograph-59B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b3 CJK Ideograph-59B3 | 59b2 CJK Ideograph-59B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b5 CJK Ideograph-59B5 | 59b4 CJK Ideograph-59B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b7 CJK Ideograph-59B7 | 59b6 CJK Ideograph-59B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59b9 CJK Ideograph-59B9 | 59b8 CJK Ideograph-59B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bb CJK Ideograph-59BB | 59ba CJK Ideograph-59BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bd CJK Ideograph-59BD | 59bc CJK Ideograph-59BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59bf CJK Ideograph-59BF | 59be CJK Ideograph-59BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c1 CJK Ideograph-59C1 | 59c0 CJK Ideograph-59C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c3 CJK Ideograph-59C3 | 59c2 CJK Ideograph-59C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c5 CJK Ideograph-59C5 | 59c4 CJK Ideograph-59C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c7 CJK Ideograph-59C7 | 59c6 CJK Ideograph-59C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59c9 CJK Ideograph-59C9 | 59c8 CJK Ideograph-59C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cb CJK Ideograph-59CB | 59ca CJK Ideograph-59CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cd CJK Ideograph-59CD | 59cc CJK Ideograph-59CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59cf CJK Ideograph-59CF | 59ce CJK Ideograph-59CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d1 CJK Ideograph-59D1 | 59d0 CJK Ideograph-59D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d3 CJK Ideograph-59D3 | 59d2 CJK Ideograph-59D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d5 CJK Ideograph-59D5 | 59d4 CJK Ideograph-59D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d7 CJK Ideograph-59D7 | 59d6 CJK Ideograph-59D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59d9 CJK Ideograph-59D9 | 59d8 CJK Ideograph-59D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59db CJK Ideograph-59DB | 59da CJK Ideograph-59DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59dd CJK Ideograph-59DD | 59dc CJK Ideograph-59DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59df CJK Ideograph-59DF | 59de CJK Ideograph-59DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e1 CJK Ideograph-59E1 | 59e0 CJK Ideograph-59E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e3 CJK Ideograph-59E3 | 59e2 CJK Ideograph-59E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e5 CJK Ideograph-59E5 | 59e4 CJK Ideograph-59E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e7 CJK Ideograph-59E7 | 59e6 CJK Ideograph-59E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59e9 CJK Ideograph-59E9 | 59e8 CJK Ideograph-59E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59eb CJK Ideograph-59EB | 59ea CJK Ideograph-59EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ed CJK Ideograph-59ED | 59ec CJK Ideograph-59EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ef CJK Ideograph-59EF | 59ee CJK Ideograph-59EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f1 CJK Ideograph-59F1 | 59f0 CJK Ideograph-59F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f3 CJK Ideograph-59F3 | 59f2 CJK Ideograph-59F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f5 CJK Ideograph-59F5 | 59f4 CJK Ideograph-59F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f7 CJK Ideograph-59F7 | 59f6 CJK Ideograph-59F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59f9 CJK Ideograph-59F9 | 59f8 CJK Ideograph-59F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59fb CJK Ideograph-59FB | 59fa CJK Ideograph-59FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59fd CJK Ideograph-59FD | 59fc CJK Ideograph-59FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 59ff CJK Ideograph-59FF | 59fe CJK Ideograph-59FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a01 CJK Ideograph-5A01 | 5a00 CJK Ideograph-5A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a03 CJK Ideograph-5A03 | 5a02 CJK Ideograph-5A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a05 CJK Ideograph-5A05 | 5a04 CJK Ideograph-5A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a07 CJK Ideograph-5A07 | 5a06 CJK Ideograph-5A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a09 CJK Ideograph-5A09 | 5a08 CJK Ideograph-5A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0b CJK Ideograph-5A0B | 5a0a CJK Ideograph-5A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0d CJK Ideograph-5A0D | 5a0c CJK Ideograph-5A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a0f CJK Ideograph-5A0F | 5a0e CJK Ideograph-5A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a11 CJK Ideograph-5A11 | 5a10 CJK Ideograph-5A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a13 CJK Ideograph-5A13 | 5a12 CJK Ideograph-5A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a15 CJK Ideograph-5A15 | 5a14 CJK Ideograph-5A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a17 CJK Ideograph-5A17 | 5a16 CJK Ideograph-5A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a19 CJK Ideograph-5A19 | 5a18 CJK Ideograph-5A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1b CJK Ideograph-5A1B | 5a1a CJK Ideograph-5A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1d CJK Ideograph-5A1D | 5a1c CJK Ideograph-5A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a1f CJK Ideograph-5A1F | 5a1e CJK Ideograph-5A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a21 CJK Ideograph-5A21 | 5a20 CJK Ideograph-5A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a23 CJK Ideograph-5A23 | 5a22 CJK Ideograph-5A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a25 CJK Ideograph-5A25 | 5a24 CJK Ideograph-5A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a27 CJK Ideograph-5A27 | 5a26 CJK Ideograph-5A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a29 CJK Ideograph-5A29 | 5a28 CJK Ideograph-5A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2b CJK Ideograph-5A2B | 5a2a CJK Ideograph-5A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2d CJK Ideograph-5A2D | 5a2c CJK Ideograph-5A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a2f CJK Ideograph-5A2F | 5a2e CJK Ideograph-5A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a31 CJK Ideograph-5A31 | 5a30 CJK Ideograph-5A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a33 CJK Ideograph-5A33 | 5a32 CJK Ideograph-5A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a35 CJK Ideograph-5A35 | 5a34 CJK Ideograph-5A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a37 CJK Ideograph-5A37 | 5a36 CJK Ideograph-5A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a39 CJK Ideograph-5A39 | 5a38 CJK Ideograph-5A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3b CJK Ideograph-5A3B | 5a3a CJK Ideograph-5A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3d CJK Ideograph-5A3D | 5a3c CJK Ideograph-5A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a3f CJK Ideograph-5A3F | 5a3e CJK Ideograph-5A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a41 CJK Ideograph-5A41 | 5a40 CJK Ideograph-5A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a43 CJK Ideograph-5A43 | 5a42 CJK Ideograph-5A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a45 CJK Ideograph-5A45 | 5a44 CJK Ideograph-5A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a47 CJK Ideograph-5A47 | 5a46 CJK Ideograph-5A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a49 CJK Ideograph-5A49 | 5a48 CJK Ideograph-5A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4b CJK Ideograph-5A4B | 5a4a CJK Ideograph-5A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4d CJK Ideograph-5A4D | 5a4c CJK Ideograph-5A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a4f CJK Ideograph-5A4F | 5a4e CJK Ideograph-5A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a51 CJK Ideograph-5A51 | 5a50 CJK Ideograph-5A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a53 CJK Ideograph-5A53 | 5a52 CJK Ideograph-5A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a55 CJK Ideograph-5A55 | 5a54 CJK Ideograph-5A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a57 CJK Ideograph-5A57 | 5a56 CJK Ideograph-5A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a59 CJK Ideograph-5A59 | 5a58 CJK Ideograph-5A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5b CJK Ideograph-5A5B | 5a5a CJK Ideograph-5A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5d CJK Ideograph-5A5D | 5a5c CJK Ideograph-5A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a5f CJK Ideograph-5A5F | 5a5e CJK Ideograph-5A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a61 CJK Ideograph-5A61 | 5a60 CJK Ideograph-5A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a63 CJK Ideograph-5A63 | 5a62 CJK Ideograph-5A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a65 CJK Ideograph-5A65 | 5a64 CJK Ideograph-5A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a67 CJK Ideograph-5A67 | 5a66 CJK Ideograph-5A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a69 CJK Ideograph-5A69 | 5a68 CJK Ideograph-5A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6b CJK Ideograph-5A6B | 5a6a CJK Ideograph-5A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6d CJK Ideograph-5A6D | 5a6c CJK Ideograph-5A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a6f CJK Ideograph-5A6F | 5a6e CJK Ideograph-5A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a71 CJK Ideograph-5A71 | 5a70 CJK Ideograph-5A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a73 CJK Ideograph-5A73 | 5a72 CJK Ideograph-5A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a75 CJK Ideograph-5A75 | 5a74 CJK Ideograph-5A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a77 CJK Ideograph-5A77 | 5a76 CJK Ideograph-5A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a79 CJK Ideograph-5A79 | 5a78 CJK Ideograph-5A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7b CJK Ideograph-5A7B | 5a7a CJK Ideograph-5A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7d CJK Ideograph-5A7D | 5a7c CJK Ideograph-5A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a7f CJK Ideograph-5A7F | 5a7e CJK Ideograph-5A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a81 CJK Ideograph-5A81 | 5a80 CJK Ideograph-5A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a83 CJK Ideograph-5A83 | 5a82 CJK Ideograph-5A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a85 CJK Ideograph-5A85 | 5a84 CJK Ideograph-5A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a87 CJK Ideograph-5A87 | 5a86 CJK Ideograph-5A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a89 CJK Ideograph-5A89 | 5a88 CJK Ideograph-5A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8b CJK Ideograph-5A8B | 5a8a CJK Ideograph-5A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8d CJK Ideograph-5A8D | 5a8c CJK Ideograph-5A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a8f CJK Ideograph-5A8F | 5a8e CJK Ideograph-5A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a91 CJK Ideograph-5A91 | 5a90 CJK Ideograph-5A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a93 CJK Ideograph-5A93 | 5a92 CJK Ideograph-5A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a95 CJK Ideograph-5A95 | 5a94 CJK Ideograph-5A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a97 CJK Ideograph-5A97 | 5a96 CJK Ideograph-5A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a99 CJK Ideograph-5A99 | 5a98 CJK Ideograph-5A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9b CJK Ideograph-5A9B | 5a9a CJK Ideograph-5A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9d CJK Ideograph-5A9D | 5a9c CJK Ideograph-5A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5a9f CJK Ideograph-5A9F | 5a9e CJK Ideograph-5A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa1 CJK Ideograph-5AA1 | 5aa0 CJK Ideograph-5AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa3 CJK Ideograph-5AA3 | 5aa2 CJK Ideograph-5AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa5 CJK Ideograph-5AA5 | 5aa4 CJK Ideograph-5AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa7 CJK Ideograph-5AA7 | 5aa6 CJK Ideograph-5AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aa9 CJK Ideograph-5AA9 | 5aa8 CJK Ideograph-5AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aab CJK Ideograph-5AAB | 5aaa CJK Ideograph-5AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aad CJK Ideograph-5AAD | 5aac CJK Ideograph-5AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aaf CJK Ideograph-5AAF | 5aae CJK Ideograph-5AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab1 CJK Ideograph-5AB1 | 5ab0 CJK Ideograph-5AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab3 CJK Ideograph-5AB3 | 5ab2 CJK Ideograph-5AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab5 CJK Ideograph-5AB5 | 5ab4 CJK Ideograph-5AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab7 CJK Ideograph-5AB7 | 5ab6 CJK Ideograph-5AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ab9 CJK Ideograph-5AB9 | 5ab8 CJK Ideograph-5AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abb CJK Ideograph-5ABB | 5aba CJK Ideograph-5ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abd CJK Ideograph-5ABD | 5abc CJK Ideograph-5ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5abf CJK Ideograph-5ABF | 5abe CJK Ideograph-5ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac1 CJK Ideograph-5AC1 | 5ac0 CJK Ideograph-5AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac3 CJK Ideograph-5AC3 | 5ac2 CJK Ideograph-5AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac5 CJK Ideograph-5AC5 | 5ac4 CJK Ideograph-5AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac7 CJK Ideograph-5AC7 | 5ac6 CJK Ideograph-5AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ac9 CJK Ideograph-5AC9 | 5ac8 CJK Ideograph-5AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acb CJK Ideograph-5ACB | 5aca CJK Ideograph-5ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acd CJK Ideograph-5ACD | 5acc CJK Ideograph-5ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5acf CJK Ideograph-5ACF | 5ace CJK Ideograph-5ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad1 CJK Ideograph-5AD1 | 5ad0 CJK Ideograph-5AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad3 CJK Ideograph-5AD3 | 5ad2 CJK Ideograph-5AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad5 CJK Ideograph-5AD5 | 5ad4 CJK Ideograph-5AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad7 CJK Ideograph-5AD7 | 5ad6 CJK Ideograph-5AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ad9 CJK Ideograph-5AD9 | 5ad8 CJK Ideograph-5AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5adb CJK Ideograph-5ADB | 5ada CJK Ideograph-5ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5add CJK Ideograph-5ADD | 5adc CJK Ideograph-5ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5adf CJK Ideograph-5ADF | 5ade CJK Ideograph-5ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae1 CJK Ideograph-5AE1 | 5ae0 CJK Ideograph-5AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae3 CJK Ideograph-5AE3 | 5ae2 CJK Ideograph-5AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae5 CJK Ideograph-5AE5 | 5ae4 CJK Ideograph-5AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae7 CJK Ideograph-5AE7 | 5ae6 CJK Ideograph-5AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ae9 CJK Ideograph-5AE9 | 5ae8 CJK Ideograph-5AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aeb CJK Ideograph-5AEB | 5aea CJK Ideograph-5AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aed CJK Ideograph-5AED | 5aec CJK Ideograph-5AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aef CJK Ideograph-5AEF | 5aee CJK Ideograph-5AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af1 CJK Ideograph-5AF1 | 5af0 CJK Ideograph-5AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af3 CJK Ideograph-5AF3 | 5af2 CJK Ideograph-5AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af5 CJK Ideograph-5AF5 | 5af4 CJK Ideograph-5AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af7 CJK Ideograph-5AF7 | 5af6 CJK Ideograph-5AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5af9 CJK Ideograph-5AF9 | 5af8 CJK Ideograph-5AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5afb CJK Ideograph-5AFB | 5afa CJK Ideograph-5AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5afd CJK Ideograph-5AFD | 5afc CJK Ideograph-5AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5aff CJK Ideograph-5AFF | 5afe CJK Ideograph-5AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b01 CJK Ideograph-5B01 | 5b00 CJK Ideograph-5B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b03 CJK Ideograph-5B03 | 5b02 CJK Ideograph-5B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b05 CJK Ideograph-5B05 | 5b04 CJK Ideograph-5B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b07 CJK Ideograph-5B07 | 5b06 CJK Ideograph-5B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b09 CJK Ideograph-5B09 | 5b08 CJK Ideograph-5B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0b CJK Ideograph-5B0B | 5b0a CJK Ideograph-5B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0d CJK Ideograph-5B0D | 5b0c CJK Ideograph-5B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b0f CJK Ideograph-5B0F | 5b0e CJK Ideograph-5B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b11 CJK Ideograph-5B11 | 5b10 CJK Ideograph-5B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b13 CJK Ideograph-5B13 | 5b12 CJK Ideograph-5B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b15 CJK Ideograph-5B15 | 5b14 CJK Ideograph-5B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b17 CJK Ideograph-5B17 | 5b16 CJK Ideograph-5B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b19 CJK Ideograph-5B19 | 5b18 CJK Ideograph-5B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1b CJK Ideograph-5B1B | 5b1a CJK Ideograph-5B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1d CJK Ideograph-5B1D | 5b1c CJK Ideograph-5B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b1f CJK Ideograph-5B1F | 5b1e CJK Ideograph-5B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b21 CJK Ideograph-5B21 | 5b20 CJK Ideograph-5B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b23 CJK Ideograph-5B23 | 5b22 CJK Ideograph-5B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b25 CJK Ideograph-5B25 | 5b24 CJK Ideograph-5B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b27 CJK Ideograph-5B27 | 5b26 CJK Ideograph-5B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b29 CJK Ideograph-5B29 | 5b28 CJK Ideograph-5B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2b CJK Ideograph-5B2B | 5b2a CJK Ideograph-5B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2d CJK Ideograph-5B2D | 5b2c CJK Ideograph-5B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b2f CJK Ideograph-5B2F | 5b2e CJK Ideograph-5B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b31 CJK Ideograph-5B31 | 5b30 CJK Ideograph-5B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b33 CJK Ideograph-5B33 | 5b32 CJK Ideograph-5B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b35 CJK Ideograph-5B35 | 5b34 CJK Ideograph-5B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b37 CJK Ideograph-5B37 | 5b36 CJK Ideograph-5B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b39 CJK Ideograph-5B39 | 5b38 CJK Ideograph-5B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3b CJK Ideograph-5B3B | 5b3a CJK Ideograph-5B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3d CJK Ideograph-5B3D | 5b3c CJK Ideograph-5B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b3f CJK Ideograph-5B3F | 5b3e CJK Ideograph-5B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b41 CJK Ideograph-5B41 | 5b40 CJK Ideograph-5B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b43 CJK Ideograph-5B43 | 5b42 CJK Ideograph-5B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b45 CJK Ideograph-5B45 | 5b44 CJK Ideograph-5B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b47 CJK Ideograph-5B47 | 5b46 CJK Ideograph-5B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b49 CJK Ideograph-5B49 | 5b48 CJK Ideograph-5B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4b CJK Ideograph-5B4B | 5b4a CJK Ideograph-5B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4d CJK Ideograph-5B4D | 5b4c CJK Ideograph-5B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b4f CJK Ideograph-5B4F | 5b4e CJK Ideograph-5B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b51 CJK Ideograph-5B51 | 5b50 CJK Ideograph-5B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b53 CJK Ideograph-5B53 | 5b52 CJK Ideograph-5B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b55 CJK Ideograph-5B55 | 5b54 CJK Ideograph-5B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b57 CJK Ideograph-5B57 | 5b56 CJK Ideograph-5B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b59 CJK Ideograph-5B59 | 5b58 CJK Ideograph-5B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5b CJK Ideograph-5B5B | 5b5a CJK Ideograph-5B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5d CJK Ideograph-5B5D | 5b5c CJK Ideograph-5B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b5f CJK Ideograph-5B5F | 5b5e CJK Ideograph-5B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b61 CJK Ideograph-5B61 | 5b60 CJK Ideograph-5B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b63 CJK Ideograph-5B63 | 5b62 CJK Ideograph-5B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b65 CJK Ideograph-5B65 | 5b64 CJK Ideograph-5B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b67 CJK Ideograph-5B67 | 5b66 CJK Ideograph-5B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b69 CJK Ideograph-5B69 | 5b68 CJK Ideograph-5B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6b CJK Ideograph-5B6B | 5b6a CJK Ideograph-5B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6d CJK Ideograph-5B6D | 5b6c CJK Ideograph-5B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b6f CJK Ideograph-5B6F | 5b6e CJK Ideograph-5B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b71 CJK Ideograph-5B71 | 5b70 CJK Ideograph-5B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b73 CJK Ideograph-5B73 | 5b72 CJK Ideograph-5B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b75 CJK Ideograph-5B75 | 5b74 CJK Ideograph-5B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b77 CJK Ideograph-5B77 | 5b76 CJK Ideograph-5B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b79 CJK Ideograph-5B79 | 5b78 CJK Ideograph-5B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7b CJK Ideograph-5B7B | 5b7a CJK Ideograph-5B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7d CJK Ideograph-5B7D | 5b7c CJK Ideograph-5B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b7f CJK Ideograph-5B7F | 5b7e CJK Ideograph-5B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b81 CJK Ideograph-5B81 | 5b80 CJK Ideograph-5B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b83 CJK Ideograph-5B83 | 5b82 CJK Ideograph-5B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b85 CJK Ideograph-5B85 | 5b84 CJK Ideograph-5B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b87 CJK Ideograph-5B87 | 5b86 CJK Ideograph-5B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b89 CJK Ideograph-5B89 | 5b88 CJK Ideograph-5B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8b CJK Ideograph-5B8B | 5b8a CJK Ideograph-5B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8d CJK Ideograph-5B8D | 5b8c CJK Ideograph-5B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b8f CJK Ideograph-5B8F | 5b8e CJK Ideograph-5B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b91 CJK Ideograph-5B91 | 5b90 CJK Ideograph-5B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b93 CJK Ideograph-5B93 | 5b92 CJK Ideograph-5B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b95 CJK Ideograph-5B95 | 5b94 CJK Ideograph-5B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b97 CJK Ideograph-5B97 | 5b96 CJK Ideograph-5B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b99 CJK Ideograph-5B99 | 5b98 CJK Ideograph-5B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9b CJK Ideograph-5B9B | 5b9a CJK Ideograph-5B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9d CJK Ideograph-5B9D | 5b9c CJK Ideograph-5B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5b9f CJK Ideograph-5B9F | 5b9e CJK Ideograph-5B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba1 CJK Ideograph-5BA1 | 5ba0 CJK Ideograph-5BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba3 CJK Ideograph-5BA3 | 5ba2 CJK Ideograph-5BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba5 CJK Ideograph-5BA5 | 5ba4 CJK Ideograph-5BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba7 CJK Ideograph-5BA7 | 5ba6 CJK Ideograph-5BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ba9 CJK Ideograph-5BA9 | 5ba8 CJK Ideograph-5BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bab CJK Ideograph-5BAB | 5baa CJK Ideograph-5BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bad CJK Ideograph-5BAD | 5bac CJK Ideograph-5BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5baf CJK Ideograph-5BAF | 5bae CJK Ideograph-5BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb1 CJK Ideograph-5BB1 | 5bb0 CJK Ideograph-5BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb3 CJK Ideograph-5BB3 | 5bb2 CJK Ideograph-5BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb5 CJK Ideograph-5BB5 | 5bb4 CJK Ideograph-5BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb7 CJK Ideograph-5BB7 | 5bb6 CJK Ideograph-5BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bb9 CJK Ideograph-5BB9 | 5bb8 CJK Ideograph-5BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbb CJK Ideograph-5BBB | 5bba CJK Ideograph-5BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbd CJK Ideograph-5BBD | 5bbc CJK Ideograph-5BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bbf CJK Ideograph-5BBF | 5bbe CJK Ideograph-5BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc1 CJK Ideograph-5BC1 | 5bc0 CJK Ideograph-5BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc3 CJK Ideograph-5BC3 | 5bc2 CJK Ideograph-5BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc5 CJK Ideograph-5BC5 | 5bc4 CJK Ideograph-5BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc7 CJK Ideograph-5BC7 | 5bc6 CJK Ideograph-5BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bc9 CJK Ideograph-5BC9 | 5bc8 CJK Ideograph-5BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcb CJK Ideograph-5BCB | 5bca CJK Ideograph-5BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcd CJK Ideograph-5BCD | 5bcc CJK Ideograph-5BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bcf CJK Ideograph-5BCF | 5bce CJK Ideograph-5BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd1 CJK Ideograph-5BD1 | 5bd0 CJK Ideograph-5BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd3 CJK Ideograph-5BD3 | 5bd2 CJK Ideograph-5BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd5 CJK Ideograph-5BD5 | 5bd4 CJK Ideograph-5BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd7 CJK Ideograph-5BD7 | 5bd6 CJK Ideograph-5BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bd9 CJK Ideograph-5BD9 | 5bd8 CJK Ideograph-5BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdb CJK Ideograph-5BDB | 5bda CJK Ideograph-5BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdd CJK Ideograph-5BDD | 5bdc CJK Ideograph-5BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bdf CJK Ideograph-5BDF | 5bde CJK Ideograph-5BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be1 CJK Ideograph-5BE1 | 5be0 CJK Ideograph-5BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be3 CJK Ideograph-5BE3 | 5be2 CJK Ideograph-5BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be5 CJK Ideograph-5BE5 | 5be4 CJK Ideograph-5BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be7 CJK Ideograph-5BE7 | 5be6 CJK Ideograph-5BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5be9 CJK Ideograph-5BE9 | 5be8 CJK Ideograph-5BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5beb CJK Ideograph-5BEB | 5bea CJK Ideograph-5BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bed CJK Ideograph-5BED | 5bec CJK Ideograph-5BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bef CJK Ideograph-5BEF | 5bee CJK Ideograph-5BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf1 CJK Ideograph-5BF1 | 5bf0 CJK Ideograph-5BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf3 CJK Ideograph-5BF3 | 5bf2 CJK Ideograph-5BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf5 CJK Ideograph-5BF5 | 5bf4 CJK Ideograph-5BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf7 CJK Ideograph-5BF7 | 5bf6 CJK Ideograph-5BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bf9 CJK Ideograph-5BF9 | 5bf8 CJK Ideograph-5BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bfb CJK Ideograph-5BFB | 5bfa CJK Ideograph-5BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bfd CJK Ideograph-5BFD | 5bfc CJK Ideograph-5BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5bff CJK Ideograph-5BFF | 5bfe CJK Ideograph-5BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c01 CJK Ideograph-5C01 | 5c00 CJK Ideograph-5C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c03 CJK Ideograph-5C03 | 5c02 CJK Ideograph-5C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c05 CJK Ideograph-5C05 | 5c04 CJK Ideograph-5C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c07 CJK Ideograph-5C07 | 5c06 CJK Ideograph-5C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c09 CJK Ideograph-5C09 | 5c08 CJK Ideograph-5C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0b CJK Ideograph-5C0B | 5c0a CJK Ideograph-5C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0d CJK Ideograph-5C0D | 5c0c CJK Ideograph-5C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c0f CJK Ideograph-5C0F | 5c0e CJK Ideograph-5C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c11 CJK Ideograph-5C11 | 5c10 CJK Ideograph-5C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c13 CJK Ideograph-5C13 | 5c12 CJK Ideograph-5C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c15 CJK Ideograph-5C15 | 5c14 CJK Ideograph-5C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c17 CJK Ideograph-5C17 | 5c16 CJK Ideograph-5C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c19 CJK Ideograph-5C19 | 5c18 CJK Ideograph-5C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1b CJK Ideograph-5C1B | 5c1a CJK Ideograph-5C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1d CJK Ideograph-5C1D | 5c1c CJK Ideograph-5C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c1f CJK Ideograph-5C1F | 5c1e CJK Ideograph-5C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c21 CJK Ideograph-5C21 | 5c20 CJK Ideograph-5C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c23 CJK Ideograph-5C23 | 5c22 CJK Ideograph-5C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c25 CJK Ideograph-5C25 | 5c24 CJK Ideograph-5C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c27 CJK Ideograph-5C27 | 5c26 CJK Ideograph-5C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c29 CJK Ideograph-5C29 | 5c28 CJK Ideograph-5C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2b CJK Ideograph-5C2B | 5c2a CJK Ideograph-5C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2d CJK Ideograph-5C2D | 5c2c CJK Ideograph-5C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c2f CJK Ideograph-5C2F | 5c2e CJK Ideograph-5C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c31 CJK Ideograph-5C31 | 5c30 CJK Ideograph-5C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c33 CJK Ideograph-5C33 | 5c32 CJK Ideograph-5C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c35 CJK Ideograph-5C35 | 5c34 CJK Ideograph-5C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c37 CJK Ideograph-5C37 | 5c36 CJK Ideograph-5C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c39 CJK Ideograph-5C39 | 5c38 CJK Ideograph-5C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3b CJK Ideograph-5C3B | 5c3a CJK Ideograph-5C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3d CJK Ideograph-5C3D | 5c3c CJK Ideograph-5C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c3f CJK Ideograph-5C3F | 5c3e CJK Ideograph-5C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c41 CJK Ideograph-5C41 | 5c40 CJK Ideograph-5C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c43 CJK Ideograph-5C43 | 5c42 CJK Ideograph-5C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c45 CJK Ideograph-5C45 | 5c44 CJK Ideograph-5C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c47 CJK Ideograph-5C47 | 5c46 CJK Ideograph-5C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c49 CJK Ideograph-5C49 | 5c48 CJK Ideograph-5C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4b CJK Ideograph-5C4B | 5c4a CJK Ideograph-5C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4d CJK Ideograph-5C4D | 5c4c CJK Ideograph-5C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c4f CJK Ideograph-5C4F | 5c4e CJK Ideograph-5C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c51 CJK Ideograph-5C51 | 5c50 CJK Ideograph-5C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c53 CJK Ideograph-5C53 | 5c52 CJK Ideograph-5C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c55 CJK Ideograph-5C55 | 5c54 CJK Ideograph-5C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c57 CJK Ideograph-5C57 | 5c56 CJK Ideograph-5C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c59 CJK Ideograph-5C59 | 5c58 CJK Ideograph-5C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5b CJK Ideograph-5C5B | 5c5a CJK Ideograph-5C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5d CJK Ideograph-5C5D | 5c5c CJK Ideograph-5C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c5f CJK Ideograph-5C5F | 5c5e CJK Ideograph-5C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c61 CJK Ideograph-5C61 | 5c60 CJK Ideograph-5C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c63 CJK Ideograph-5C63 | 5c62 CJK Ideograph-5C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c65 CJK Ideograph-5C65 | 5c64 CJK Ideograph-5C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c67 CJK Ideograph-5C67 | 5c66 CJK Ideograph-5C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c69 CJK Ideograph-5C69 | 5c68 CJK Ideograph-5C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6b CJK Ideograph-5C6B | 5c6a CJK Ideograph-5C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6d CJK Ideograph-5C6D | 5c6c CJK Ideograph-5C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c6f CJK Ideograph-5C6F | 5c6e CJK Ideograph-5C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c71 CJK Ideograph-5C71 | 5c70 CJK Ideograph-5C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c73 CJK Ideograph-5C73 | 5c72 CJK Ideograph-5C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c75 CJK Ideograph-5C75 | 5c74 CJK Ideograph-5C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c77 CJK Ideograph-5C77 | 5c76 CJK Ideograph-5C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c79 CJK Ideograph-5C79 | 5c78 CJK Ideograph-5C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7b CJK Ideograph-5C7B | 5c7a CJK Ideograph-5C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7d CJK Ideograph-5C7D | 5c7c CJK Ideograph-5C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c7f CJK Ideograph-5C7F | 5c7e CJK Ideograph-5C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c81 CJK Ideograph-5C81 | 5c80 CJK Ideograph-5C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c83 CJK Ideograph-5C83 | 5c82 CJK Ideograph-5C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c85 CJK Ideograph-5C85 | 5c84 CJK Ideograph-5C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c87 CJK Ideograph-5C87 | 5c86 CJK Ideograph-5C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c89 CJK Ideograph-5C89 | 5c88 CJK Ideograph-5C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8b CJK Ideograph-5C8B | 5c8a CJK Ideograph-5C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8d CJK Ideograph-5C8D | 5c8c CJK Ideograph-5C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c8f CJK Ideograph-5C8F | 5c8e CJK Ideograph-5C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c91 CJK Ideograph-5C91 | 5c90 CJK Ideograph-5C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c93 CJK Ideograph-5C93 | 5c92 CJK Ideograph-5C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c95 CJK Ideograph-5C95 | 5c94 CJK Ideograph-5C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c97 CJK Ideograph-5C97 | 5c96 CJK Ideograph-5C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c99 CJK Ideograph-5C99 | 5c98 CJK Ideograph-5C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9b CJK Ideograph-5C9B | 5c9a CJK Ideograph-5C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9d CJK Ideograph-5C9D | 5c9c CJK Ideograph-5C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5c9f CJK Ideograph-5C9F | 5c9e CJK Ideograph-5C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca1 CJK Ideograph-5CA1 | 5ca0 CJK Ideograph-5CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca3 CJK Ideograph-5CA3 | 5ca2 CJK Ideograph-5CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca5 CJK Ideograph-5CA5 | 5ca4 CJK Ideograph-5CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca7 CJK Ideograph-5CA7 | 5ca6 CJK Ideograph-5CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ca9 CJK Ideograph-5CA9 | 5ca8 CJK Ideograph-5CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cab CJK Ideograph-5CAB | 5caa CJK Ideograph-5CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cad CJK Ideograph-5CAD | 5cac CJK Ideograph-5CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5caf CJK Ideograph-5CAF | 5cae CJK Ideograph-5CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb1 CJK Ideograph-5CB1 | 5cb0 CJK Ideograph-5CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb3 CJK Ideograph-5CB3 | 5cb2 CJK Ideograph-5CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb5 CJK Ideograph-5CB5 | 5cb4 CJK Ideograph-5CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb7 CJK Ideograph-5CB7 | 5cb6 CJK Ideograph-5CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cb9 CJK Ideograph-5CB9 | 5cb8 CJK Ideograph-5CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbb CJK Ideograph-5CBB | 5cba CJK Ideograph-5CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbd CJK Ideograph-5CBD | 5cbc CJK Ideograph-5CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cbf CJK Ideograph-5CBF | 5cbe CJK Ideograph-5CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc1 CJK Ideograph-5CC1 | 5cc0 CJK Ideograph-5CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc3 CJK Ideograph-5CC3 | 5cc2 CJK Ideograph-5CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc5 CJK Ideograph-5CC5 | 5cc4 CJK Ideograph-5CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc7 CJK Ideograph-5CC7 | 5cc6 CJK Ideograph-5CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cc9 CJK Ideograph-5CC9 | 5cc8 CJK Ideograph-5CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccb CJK Ideograph-5CCB | 5cca CJK Ideograph-5CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccd CJK Ideograph-5CCD | 5ccc CJK Ideograph-5CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ccf CJK Ideograph-5CCF | 5cce CJK Ideograph-5CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd1 CJK Ideograph-5CD1 | 5cd0 CJK Ideograph-5CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd3 CJK Ideograph-5CD3 | 5cd2 CJK Ideograph-5CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd5 CJK Ideograph-5CD5 | 5cd4 CJK Ideograph-5CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd7 CJK Ideograph-5CD7 | 5cd6 CJK Ideograph-5CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cd9 CJK Ideograph-5CD9 | 5cd8 CJK Ideograph-5CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdb CJK Ideograph-5CDB | 5cda CJK Ideograph-5CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdd CJK Ideograph-5CDD | 5cdc CJK Ideograph-5CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cdf CJK Ideograph-5CDF | 5cde CJK Ideograph-5CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce1 CJK Ideograph-5CE1 | 5ce0 CJK Ideograph-5CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce3 CJK Ideograph-5CE3 | 5ce2 CJK Ideograph-5CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce5 CJK Ideograph-5CE5 | 5ce4 CJK Ideograph-5CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce7 CJK Ideograph-5CE7 | 5ce6 CJK Ideograph-5CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ce9 CJK Ideograph-5CE9 | 5ce8 CJK Ideograph-5CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ceb CJK Ideograph-5CEB | 5cea CJK Ideograph-5CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ced CJK Ideograph-5CED | 5cec CJK Ideograph-5CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cef CJK Ideograph-5CEF | 5cee CJK Ideograph-5CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf1 CJK Ideograph-5CF1 | 5cf0 CJK Ideograph-5CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf3 CJK Ideograph-5CF3 | 5cf2 CJK Ideograph-5CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf5 CJK Ideograph-5CF5 | 5cf4 CJK Ideograph-5CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf7 CJK Ideograph-5CF7 | 5cf6 CJK Ideograph-5CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cf9 CJK Ideograph-5CF9 | 5cf8 CJK Ideograph-5CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cfb CJK Ideograph-5CFB | 5cfa CJK Ideograph-5CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cfd CJK Ideograph-5CFD | 5cfc CJK Ideograph-5CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5cff CJK Ideograph-5CFF | 5cfe CJK Ideograph-5CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d01 CJK Ideograph-5D01 | 5d00 CJK Ideograph-5D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d03 CJK Ideograph-5D03 | 5d02 CJK Ideograph-5D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d05 CJK Ideograph-5D05 | 5d04 CJK Ideograph-5D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d07 CJK Ideograph-5D07 | 5d06 CJK Ideograph-5D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d09 CJK Ideograph-5D09 | 5d08 CJK Ideograph-5D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0b CJK Ideograph-5D0B | 5d0a CJK Ideograph-5D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0d CJK Ideograph-5D0D | 5d0c CJK Ideograph-5D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d0f CJK Ideograph-5D0F | 5d0e CJK Ideograph-5D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d11 CJK Ideograph-5D11 | 5d10 CJK Ideograph-5D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d13 CJK Ideograph-5D13 | 5d12 CJK Ideograph-5D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d15 CJK Ideograph-5D15 | 5d14 CJK Ideograph-5D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d17 CJK Ideograph-5D17 | 5d16 CJK Ideograph-5D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d19 CJK Ideograph-5D19 | 5d18 CJK Ideograph-5D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1b CJK Ideograph-5D1B | 5d1a CJK Ideograph-5D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1d CJK Ideograph-5D1D | 5d1c CJK Ideograph-5D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d1f CJK Ideograph-5D1F | 5d1e CJK Ideograph-5D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d21 CJK Ideograph-5D21 | 5d20 CJK Ideograph-5D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d23 CJK Ideograph-5D23 | 5d22 CJK Ideograph-5D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d25 CJK Ideograph-5D25 | 5d24 CJK Ideograph-5D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d27 CJK Ideograph-5D27 | 5d26 CJK Ideograph-5D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d29 CJK Ideograph-5D29 | 5d28 CJK Ideograph-5D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2b CJK Ideograph-5D2B | 5d2a CJK Ideograph-5D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2d CJK Ideograph-5D2D | 5d2c CJK Ideograph-5D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d2f CJK Ideograph-5D2F | 5d2e CJK Ideograph-5D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d31 CJK Ideograph-5D31 | 5d30 CJK Ideograph-5D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d33 CJK Ideograph-5D33 | 5d32 CJK Ideograph-5D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d35 CJK Ideograph-5D35 | 5d34 CJK Ideograph-5D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d37 CJK Ideograph-5D37 | 5d36 CJK Ideograph-5D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d39 CJK Ideograph-5D39 | 5d38 CJK Ideograph-5D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3b CJK Ideograph-5D3B | 5d3a CJK Ideograph-5D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3d CJK Ideograph-5D3D | 5d3c CJK Ideograph-5D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d3f CJK Ideograph-5D3F | 5d3e CJK Ideograph-5D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d41 CJK Ideograph-5D41 | 5d40 CJK Ideograph-5D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d43 CJK Ideograph-5D43 | 5d42 CJK Ideograph-5D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d45 CJK Ideograph-5D45 | 5d44 CJK Ideograph-5D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d47 CJK Ideograph-5D47 | 5d46 CJK Ideograph-5D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d49 CJK Ideograph-5D49 | 5d48 CJK Ideograph-5D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4b CJK Ideograph-5D4B | 5d4a CJK Ideograph-5D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4d CJK Ideograph-5D4D | 5d4c CJK Ideograph-5D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d4f CJK Ideograph-5D4F | 5d4e CJK Ideograph-5D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d51 CJK Ideograph-5D51 | 5d50 CJK Ideograph-5D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d53 CJK Ideograph-5D53 | 5d52 CJK Ideograph-5D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d55 CJK Ideograph-5D55 | 5d54 CJK Ideograph-5D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d57 CJK Ideograph-5D57 | 5d56 CJK Ideograph-5D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d59 CJK Ideograph-5D59 | 5d58 CJK Ideograph-5D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5b CJK Ideograph-5D5B | 5d5a CJK Ideograph-5D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5d CJK Ideograph-5D5D | 5d5c CJK Ideograph-5D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d5f CJK Ideograph-5D5F | 5d5e CJK Ideograph-5D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d61 CJK Ideograph-5D61 | 5d60 CJK Ideograph-5D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d63 CJK Ideograph-5D63 | 5d62 CJK Ideograph-5D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d65 CJK Ideograph-5D65 | 5d64 CJK Ideograph-5D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d67 CJK Ideograph-5D67 | 5d66 CJK Ideograph-5D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d69 CJK Ideograph-5D69 | 5d68 CJK Ideograph-5D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6b CJK Ideograph-5D6B | 5d6a CJK Ideograph-5D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6d CJK Ideograph-5D6D | 5d6c CJK Ideograph-5D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d6f CJK Ideograph-5D6F | 5d6e CJK Ideograph-5D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d71 CJK Ideograph-5D71 | 5d70 CJK Ideograph-5D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d73 CJK Ideograph-5D73 | 5d72 CJK Ideograph-5D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d75 CJK Ideograph-5D75 | 5d74 CJK Ideograph-5D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d77 CJK Ideograph-5D77 | 5d76 CJK Ideograph-5D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d79 CJK Ideograph-5D79 | 5d78 CJK Ideograph-5D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7b CJK Ideograph-5D7B | 5d7a CJK Ideograph-5D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7d CJK Ideograph-5D7D | 5d7c CJK Ideograph-5D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d7f CJK Ideograph-5D7F | 5d7e CJK Ideograph-5D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d81 CJK Ideograph-5D81 | 5d80 CJK Ideograph-5D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d83 CJK Ideograph-5D83 | 5d82 CJK Ideograph-5D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d85 CJK Ideograph-5D85 | 5d84 CJK Ideograph-5D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d87 CJK Ideograph-5D87 | 5d86 CJK Ideograph-5D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d89 CJK Ideograph-5D89 | 5d88 CJK Ideograph-5D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8b CJK Ideograph-5D8B | 5d8a CJK Ideograph-5D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8d CJK Ideograph-5D8D | 5d8c CJK Ideograph-5D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d8f CJK Ideograph-5D8F | 5d8e CJK Ideograph-5D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d91 CJK Ideograph-5D91 | 5d90 CJK Ideograph-5D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d93 CJK Ideograph-5D93 | 5d92 CJK Ideograph-5D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d95 CJK Ideograph-5D95 | 5d94 CJK Ideograph-5D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d97 CJK Ideograph-5D97 | 5d96 CJK Ideograph-5D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d99 CJK Ideograph-5D99 | 5d98 CJK Ideograph-5D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9b CJK Ideograph-5D9B | 5d9a CJK Ideograph-5D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9d CJK Ideograph-5D9D | 5d9c CJK Ideograph-5D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5d9f CJK Ideograph-5D9F | 5d9e CJK Ideograph-5D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da1 CJK Ideograph-5DA1 | 5da0 CJK Ideograph-5DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da3 CJK Ideograph-5DA3 | 5da2 CJK Ideograph-5DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da5 CJK Ideograph-5DA5 | 5da4 CJK Ideograph-5DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da7 CJK Ideograph-5DA7 | 5da6 CJK Ideograph-5DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5da9 CJK Ideograph-5DA9 | 5da8 CJK Ideograph-5DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dab CJK Ideograph-5DAB | 5daa CJK Ideograph-5DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dad CJK Ideograph-5DAD | 5dac CJK Ideograph-5DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5daf CJK Ideograph-5DAF | 5dae CJK Ideograph-5DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db1 CJK Ideograph-5DB1 | 5db0 CJK Ideograph-5DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db3 CJK Ideograph-5DB3 | 5db2 CJK Ideograph-5DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db5 CJK Ideograph-5DB5 | 5db4 CJK Ideograph-5DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db7 CJK Ideograph-5DB7 | 5db6 CJK Ideograph-5DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5db9 CJK Ideograph-5DB9 | 5db8 CJK Ideograph-5DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbb CJK Ideograph-5DBB | 5dba CJK Ideograph-5DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbd CJK Ideograph-5DBD | 5dbc CJK Ideograph-5DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dbf CJK Ideograph-5DBF | 5dbe CJK Ideograph-5DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc1 CJK Ideograph-5DC1 | 5dc0 CJK Ideograph-5DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc3 CJK Ideograph-5DC3 | 5dc2 CJK Ideograph-5DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc5 CJK Ideograph-5DC5 | 5dc4 CJK Ideograph-5DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc7 CJK Ideograph-5DC7 | 5dc6 CJK Ideograph-5DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dc9 CJK Ideograph-5DC9 | 5dc8 CJK Ideograph-5DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcb CJK Ideograph-5DCB | 5dca CJK Ideograph-5DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcd CJK Ideograph-5DCD | 5dcc CJK Ideograph-5DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dcf CJK Ideograph-5DCF | 5dce CJK Ideograph-5DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd1 CJK Ideograph-5DD1 | 5dd0 CJK Ideograph-5DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd3 CJK Ideograph-5DD3 | 5dd2 CJK Ideograph-5DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd5 CJK Ideograph-5DD5 | 5dd4 CJK Ideograph-5DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd7 CJK Ideograph-5DD7 | 5dd6 CJK Ideograph-5DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dd9 CJK Ideograph-5DD9 | 5dd8 CJK Ideograph-5DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddb CJK Ideograph-5DDB | 5dda CJK Ideograph-5DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddd CJK Ideograph-5DDD | 5ddc CJK Ideograph-5DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ddf CJK Ideograph-5DDF | 5dde CJK Ideograph-5DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de1 CJK Ideograph-5DE1 | 5de0 CJK Ideograph-5DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de3 CJK Ideograph-5DE3 | 5de2 CJK Ideograph-5DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de5 CJK Ideograph-5DE5 | 5de4 CJK Ideograph-5DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de7 CJK Ideograph-5DE7 | 5de6 CJK Ideograph-5DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5de9 CJK Ideograph-5DE9 | 5de8 CJK Ideograph-5DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5deb CJK Ideograph-5DEB | 5dea CJK Ideograph-5DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ded CJK Ideograph-5DED | 5dec CJK Ideograph-5DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5def CJK Ideograph-5DEF | 5dee CJK Ideograph-5DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df1 CJK Ideograph-5DF1 | 5df0 CJK Ideograph-5DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df3 CJK Ideograph-5DF3 | 5df2 CJK Ideograph-5DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df5 CJK Ideograph-5DF5 | 5df4 CJK Ideograph-5DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df7 CJK Ideograph-5DF7 | 5df6 CJK Ideograph-5DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5df9 CJK Ideograph-5DF9 | 5df8 CJK Ideograph-5DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dfb CJK Ideograph-5DFB | 5dfa CJK Ideograph-5DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dfd CJK Ideograph-5DFD | 5dfc CJK Ideograph-5DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5dff CJK Ideograph-5DFF | 5dfe CJK Ideograph-5DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e01 CJK Ideograph-5E01 | 5e00 CJK Ideograph-5E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e03 CJK Ideograph-5E03 | 5e02 CJK Ideograph-5E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e05 CJK Ideograph-5E05 | 5e04 CJK Ideograph-5E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e07 CJK Ideograph-5E07 | 5e06 CJK Ideograph-5E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e09 CJK Ideograph-5E09 | 5e08 CJK Ideograph-5E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0b CJK Ideograph-5E0B | 5e0a CJK Ideograph-5E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0d CJK Ideograph-5E0D | 5e0c CJK Ideograph-5E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e0f CJK Ideograph-5E0F | 5e0e CJK Ideograph-5E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e11 CJK Ideograph-5E11 | 5e10 CJK Ideograph-5E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e13 CJK Ideograph-5E13 | 5e12 CJK Ideograph-5E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e15 CJK Ideograph-5E15 | 5e14 CJK Ideograph-5E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e17 CJK Ideograph-5E17 | 5e16 CJK Ideograph-5E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e19 CJK Ideograph-5E19 | 5e18 CJK Ideograph-5E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1b CJK Ideograph-5E1B | 5e1a CJK Ideograph-5E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1d CJK Ideograph-5E1D | 5e1c CJK Ideograph-5E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e1f CJK Ideograph-5E1F | 5e1e CJK Ideograph-5E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e21 CJK Ideograph-5E21 | 5e20 CJK Ideograph-5E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e23 CJK Ideograph-5E23 | 5e22 CJK Ideograph-5E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e25 CJK Ideograph-5E25 | 5e24 CJK Ideograph-5E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e27 CJK Ideograph-5E27 | 5e26 CJK Ideograph-5E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e29 CJK Ideograph-5E29 | 5e28 CJK Ideograph-5E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2b CJK Ideograph-5E2B | 5e2a CJK Ideograph-5E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2d CJK Ideograph-5E2D | 5e2c CJK Ideograph-5E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e2f CJK Ideograph-5E2F | 5e2e CJK Ideograph-5E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e31 CJK Ideograph-5E31 | 5e30 CJK Ideograph-5E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e33 CJK Ideograph-5E33 | 5e32 CJK Ideograph-5E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e35 CJK Ideograph-5E35 | 5e34 CJK Ideograph-5E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e37 CJK Ideograph-5E37 | 5e36 CJK Ideograph-5E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e39 CJK Ideograph-5E39 | 5e38 CJK Ideograph-5E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3b CJK Ideograph-5E3B | 5e3a CJK Ideograph-5E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3d CJK Ideograph-5E3D | 5e3c CJK Ideograph-5E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e3f CJK Ideograph-5E3F | 5e3e CJK Ideograph-5E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e41 CJK Ideograph-5E41 | 5e40 CJK Ideograph-5E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e43 CJK Ideograph-5E43 | 5e42 CJK Ideograph-5E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e45 CJK Ideograph-5E45 | 5e44 CJK Ideograph-5E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e47 CJK Ideograph-5E47 | 5e46 CJK Ideograph-5E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e49 CJK Ideograph-5E49 | 5e48 CJK Ideograph-5E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4b CJK Ideograph-5E4B | 5e4a CJK Ideograph-5E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4d CJK Ideograph-5E4D | 5e4c CJK Ideograph-5E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e4f CJK Ideograph-5E4F | 5e4e CJK Ideograph-5E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e51 CJK Ideograph-5E51 | 5e50 CJK Ideograph-5E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e53 CJK Ideograph-5E53 | 5e52 CJK Ideograph-5E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e55 CJK Ideograph-5E55 | 5e54 CJK Ideograph-5E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e57 CJK Ideograph-5E57 | 5e56 CJK Ideograph-5E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e59 CJK Ideograph-5E59 | 5e58 CJK Ideograph-5E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5b CJK Ideograph-5E5B | 5e5a CJK Ideograph-5E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5d CJK Ideograph-5E5D | 5e5c CJK Ideograph-5E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e5f CJK Ideograph-5E5F | 5e5e CJK Ideograph-5E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e61 CJK Ideograph-5E61 | 5e60 CJK Ideograph-5E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e63 CJK Ideograph-5E63 | 5e62 CJK Ideograph-5E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e65 CJK Ideograph-5E65 | 5e64 CJK Ideograph-5E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e67 CJK Ideograph-5E67 | 5e66 CJK Ideograph-5E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e69 CJK Ideograph-5E69 | 5e68 CJK Ideograph-5E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6b CJK Ideograph-5E6B | 5e6a CJK Ideograph-5E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6d CJK Ideograph-5E6D | 5e6c CJK Ideograph-5E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e6f CJK Ideograph-5E6F | 5e6e CJK Ideograph-5E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e71 CJK Ideograph-5E71 | 5e70 CJK Ideograph-5E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e73 CJK Ideograph-5E73 | 5e72 CJK Ideograph-5E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e75 CJK Ideograph-5E75 | 5e74 CJK Ideograph-5E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e77 CJK Ideograph-5E77 | 5e76 CJK Ideograph-5E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e79 CJK Ideograph-5E79 | 5e78 CJK Ideograph-5E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7b CJK Ideograph-5E7B | 5e7a CJK Ideograph-5E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7d CJK Ideograph-5E7D | 5e7c CJK Ideograph-5E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e7f CJK Ideograph-5E7F | 5e7e CJK Ideograph-5E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e81 CJK Ideograph-5E81 | 5e80 CJK Ideograph-5E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e83 CJK Ideograph-5E83 | 5e82 CJK Ideograph-5E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e85 CJK Ideograph-5E85 | 5e84 CJK Ideograph-5E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e87 CJK Ideograph-5E87 | 5e86 CJK Ideograph-5E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e89 CJK Ideograph-5E89 | 5e88 CJK Ideograph-5E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8b CJK Ideograph-5E8B | 5e8a CJK Ideograph-5E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8d CJK Ideograph-5E8D | 5e8c CJK Ideograph-5E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e8f CJK Ideograph-5E8F | 5e8e CJK Ideograph-5E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e91 CJK Ideograph-5E91 | 5e90 CJK Ideograph-5E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e93 CJK Ideograph-5E93 | 5e92 CJK Ideograph-5E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e95 CJK Ideograph-5E95 | 5e94 CJK Ideograph-5E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e97 CJK Ideograph-5E97 | 5e96 CJK Ideograph-5E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e99 CJK Ideograph-5E99 | 5e98 CJK Ideograph-5E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9b CJK Ideograph-5E9B | 5e9a CJK Ideograph-5E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9d CJK Ideograph-5E9D | 5e9c CJK Ideograph-5E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5e9f CJK Ideograph-5E9F | 5e9e CJK Ideograph-5E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea1 CJK Ideograph-5EA1 | 5ea0 CJK Ideograph-5EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea3 CJK Ideograph-5EA3 | 5ea2 CJK Ideograph-5EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea5 CJK Ideograph-5EA5 | 5ea4 CJK Ideograph-5EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea7 CJK Ideograph-5EA7 | 5ea6 CJK Ideograph-5EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ea9 CJK Ideograph-5EA9 | 5ea8 CJK Ideograph-5EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eab CJK Ideograph-5EAB | 5eaa CJK Ideograph-5EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ead CJK Ideograph-5EAD | 5eac CJK Ideograph-5EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eaf CJK Ideograph-5EAF | 5eae CJK Ideograph-5EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb1 CJK Ideograph-5EB1 | 5eb0 CJK Ideograph-5EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb3 CJK Ideograph-5EB3 | 5eb2 CJK Ideograph-5EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb5 CJK Ideograph-5EB5 | 5eb4 CJK Ideograph-5EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb7 CJK Ideograph-5EB7 | 5eb6 CJK Ideograph-5EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eb9 CJK Ideograph-5EB9 | 5eb8 CJK Ideograph-5EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebb CJK Ideograph-5EBB | 5eba CJK Ideograph-5EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebd CJK Ideograph-5EBD | 5ebc CJK Ideograph-5EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ebf CJK Ideograph-5EBF | 5ebe CJK Ideograph-5EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec1 CJK Ideograph-5EC1 | 5ec0 CJK Ideograph-5EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec3 CJK Ideograph-5EC3 | 5ec2 CJK Ideograph-5EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec5 CJK Ideograph-5EC5 | 5ec4 CJK Ideograph-5EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec7 CJK Ideograph-5EC7 | 5ec6 CJK Ideograph-5EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ec9 CJK Ideograph-5EC9 | 5ec8 CJK Ideograph-5EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecb CJK Ideograph-5ECB | 5eca CJK Ideograph-5ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecd CJK Ideograph-5ECD | 5ecc CJK Ideograph-5ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ecf CJK Ideograph-5ECF | 5ece CJK Ideograph-5ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed1 CJK Ideograph-5ED1 | 5ed0 CJK Ideograph-5ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed3 CJK Ideograph-5ED3 | 5ed2 CJK Ideograph-5ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed5 CJK Ideograph-5ED5 | 5ed4 CJK Ideograph-5ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed7 CJK Ideograph-5ED7 | 5ed6 CJK Ideograph-5ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ed9 CJK Ideograph-5ED9 | 5ed8 CJK Ideograph-5ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edb CJK Ideograph-5EDB | 5eda CJK Ideograph-5EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edd CJK Ideograph-5EDD | 5edc CJK Ideograph-5EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5edf CJK Ideograph-5EDF | 5ede CJK Ideograph-5EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee1 CJK Ideograph-5EE1 | 5ee0 CJK Ideograph-5EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee3 CJK Ideograph-5EE3 | 5ee2 CJK Ideograph-5EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee5 CJK Ideograph-5EE5 | 5ee4 CJK Ideograph-5EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee7 CJK Ideograph-5EE7 | 5ee6 CJK Ideograph-5EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ee9 CJK Ideograph-5EE9 | 5ee8 CJK Ideograph-5EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eeb CJK Ideograph-5EEB | 5eea CJK Ideograph-5EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eed CJK Ideograph-5EED | 5eec CJK Ideograph-5EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eef CJK Ideograph-5EEF | 5eee CJK Ideograph-5EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef1 CJK Ideograph-5EF1 | 5ef0 CJK Ideograph-5EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef3 CJK Ideograph-5EF3 | 5ef2 CJK Ideograph-5EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef5 CJK Ideograph-5EF5 | 5ef4 CJK Ideograph-5EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef7 CJK Ideograph-5EF7 | 5ef6 CJK Ideograph-5EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ef9 CJK Ideograph-5EF9 | 5ef8 CJK Ideograph-5EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5efb CJK Ideograph-5EFB | 5efa CJK Ideograph-5EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5efd CJK Ideograph-5EFD | 5efc CJK Ideograph-5EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5eff CJK Ideograph-5EFF | 5efe CJK Ideograph-5EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f01 CJK Ideograph-5F01 | 5f00 CJK Ideograph-5F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f03 CJK Ideograph-5F03 | 5f02 CJK Ideograph-5F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f05 CJK Ideograph-5F05 | 5f04 CJK Ideograph-5F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f07 CJK Ideograph-5F07 | 5f06 CJK Ideograph-5F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f09 CJK Ideograph-5F09 | 5f08 CJK Ideograph-5F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0b CJK Ideograph-5F0B | 5f0a CJK Ideograph-5F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0d CJK Ideograph-5F0D | 5f0c CJK Ideograph-5F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f0f CJK Ideograph-5F0F | 5f0e CJK Ideograph-5F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f11 CJK Ideograph-5F11 | 5f10 CJK Ideograph-5F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f13 CJK Ideograph-5F13 | 5f12 CJK Ideograph-5F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f15 CJK Ideograph-5F15 | 5f14 CJK Ideograph-5F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f17 CJK Ideograph-5F17 | 5f16 CJK Ideograph-5F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f19 CJK Ideograph-5F19 | 5f18 CJK Ideograph-5F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1b CJK Ideograph-5F1B | 5f1a CJK Ideograph-5F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1d CJK Ideograph-5F1D | 5f1c CJK Ideograph-5F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f1f CJK Ideograph-5F1F | 5f1e CJK Ideograph-5F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f21 CJK Ideograph-5F21 | 5f20 CJK Ideograph-5F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f23 CJK Ideograph-5F23 | 5f22 CJK Ideograph-5F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f25 CJK Ideograph-5F25 | 5f24 CJK Ideograph-5F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f27 CJK Ideograph-5F27 | 5f26 CJK Ideograph-5F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f29 CJK Ideograph-5F29 | 5f28 CJK Ideograph-5F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2b CJK Ideograph-5F2B | 5f2a CJK Ideograph-5F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2d CJK Ideograph-5F2D | 5f2c CJK Ideograph-5F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f2f CJK Ideograph-5F2F | 5f2e CJK Ideograph-5F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f31 CJK Ideograph-5F31 | 5f30 CJK Ideograph-5F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f33 CJK Ideograph-5F33 | 5f32 CJK Ideograph-5F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f35 CJK Ideograph-5F35 | 5f34 CJK Ideograph-5F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f37 CJK Ideograph-5F37 | 5f36 CJK Ideograph-5F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f39 CJK Ideograph-5F39 | 5f38 CJK Ideograph-5F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3b CJK Ideograph-5F3B | 5f3a CJK Ideograph-5F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3d CJK Ideograph-5F3D | 5f3c CJK Ideograph-5F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f3f CJK Ideograph-5F3F | 5f3e CJK Ideograph-5F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f41 CJK Ideograph-5F41 | 5f40 CJK Ideograph-5F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f43 CJK Ideograph-5F43 | 5f42 CJK Ideograph-5F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f45 CJK Ideograph-5F45 | 5f44 CJK Ideograph-5F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f47 CJK Ideograph-5F47 | 5f46 CJK Ideograph-5F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f49 CJK Ideograph-5F49 | 5f48 CJK Ideograph-5F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4b CJK Ideograph-5F4B | 5f4a CJK Ideograph-5F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4d CJK Ideograph-5F4D | 5f4c CJK Ideograph-5F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f4f CJK Ideograph-5F4F | 5f4e CJK Ideograph-5F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f51 CJK Ideograph-5F51 | 5f50 CJK Ideograph-5F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f53 CJK Ideograph-5F53 | 5f52 CJK Ideograph-5F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f55 CJK Ideograph-5F55 | 5f54 CJK Ideograph-5F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f57 CJK Ideograph-5F57 | 5f56 CJK Ideograph-5F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f59 CJK Ideograph-5F59 | 5f58 CJK Ideograph-5F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5b CJK Ideograph-5F5B | 5f5a CJK Ideograph-5F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5d CJK Ideograph-5F5D | 5f5c CJK Ideograph-5F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f5f CJK Ideograph-5F5F | 5f5e CJK Ideograph-5F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f61 CJK Ideograph-5F61 | 5f60 CJK Ideograph-5F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f63 CJK Ideograph-5F63 | 5f62 CJK Ideograph-5F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f65 CJK Ideograph-5F65 | 5f64 CJK Ideograph-5F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f67 CJK Ideograph-5F67 | 5f66 CJK Ideograph-5F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f69 CJK Ideograph-5F69 | 5f68 CJK Ideograph-5F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6b CJK Ideograph-5F6B | 5f6a CJK Ideograph-5F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6d CJK Ideograph-5F6D | 5f6c CJK Ideograph-5F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f6f CJK Ideograph-5F6F | 5f6e CJK Ideograph-5F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f71 CJK Ideograph-5F71 | 5f70 CJK Ideograph-5F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f73 CJK Ideograph-5F73 | 5f72 CJK Ideograph-5F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f75 CJK Ideograph-5F75 | 5f74 CJK Ideograph-5F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f77 CJK Ideograph-5F77 | 5f76 CJK Ideograph-5F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f79 CJK Ideograph-5F79 | 5f78 CJK Ideograph-5F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7b CJK Ideograph-5F7B | 5f7a CJK Ideograph-5F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7d CJK Ideograph-5F7D | 5f7c CJK Ideograph-5F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f7f CJK Ideograph-5F7F | 5f7e CJK Ideograph-5F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f81 CJK Ideograph-5F81 | 5f80 CJK Ideograph-5F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f83 CJK Ideograph-5F83 | 5f82 CJK Ideograph-5F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f85 CJK Ideograph-5F85 | 5f84 CJK Ideograph-5F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f87 CJK Ideograph-5F87 | 5f86 CJK Ideograph-5F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f89 CJK Ideograph-5F89 | 5f88 CJK Ideograph-5F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8b CJK Ideograph-5F8B | 5f8a CJK Ideograph-5F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8d CJK Ideograph-5F8D | 5f8c CJK Ideograph-5F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f8f CJK Ideograph-5F8F | 5f8e CJK Ideograph-5F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f91 CJK Ideograph-5F91 | 5f90 CJK Ideograph-5F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f93 CJK Ideograph-5F93 | 5f92 CJK Ideograph-5F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f95 CJK Ideograph-5F95 | 5f94 CJK Ideograph-5F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f97 CJK Ideograph-5F97 | 5f96 CJK Ideograph-5F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f99 CJK Ideograph-5F99 | 5f98 CJK Ideograph-5F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9b CJK Ideograph-5F9B | 5f9a CJK Ideograph-5F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9d CJK Ideograph-5F9D | 5f9c CJK Ideograph-5F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5f9f CJK Ideograph-5F9F | 5f9e CJK Ideograph-5F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa1 CJK Ideograph-5FA1 | 5fa0 CJK Ideograph-5FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa3 CJK Ideograph-5FA3 | 5fa2 CJK Ideograph-5FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa5 CJK Ideograph-5FA5 | 5fa4 CJK Ideograph-5FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa7 CJK Ideograph-5FA7 | 5fa6 CJK Ideograph-5FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fa9 CJK Ideograph-5FA9 | 5fa8 CJK Ideograph-5FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fab CJK Ideograph-5FAB | 5faa CJK Ideograph-5FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fad CJK Ideograph-5FAD | 5fac CJK Ideograph-5FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5faf CJK Ideograph-5FAF | 5fae CJK Ideograph-5FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb1 CJK Ideograph-5FB1 | 5fb0 CJK Ideograph-5FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb3 CJK Ideograph-5FB3 | 5fb2 CJK Ideograph-5FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb5 CJK Ideograph-5FB5 | 5fb4 CJK Ideograph-5FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb7 CJK Ideograph-5FB7 | 5fb6 CJK Ideograph-5FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fb9 CJK Ideograph-5FB9 | 5fb8 CJK Ideograph-5FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbb CJK Ideograph-5FBB | 5fba CJK Ideograph-5FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbd CJK Ideograph-5FBD | 5fbc CJK Ideograph-5FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fbf CJK Ideograph-5FBF | 5fbe CJK Ideograph-5FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc1 CJK Ideograph-5FC1 | 5fc0 CJK Ideograph-5FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc3 CJK Ideograph-5FC3 | 5fc2 CJK Ideograph-5FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc5 CJK Ideograph-5FC5 | 5fc4 CJK Ideograph-5FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc7 CJK Ideograph-5FC7 | 5fc6 CJK Ideograph-5FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fc9 CJK Ideograph-5FC9 | 5fc8 CJK Ideograph-5FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcb CJK Ideograph-5FCB | 5fca CJK Ideograph-5FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcd CJK Ideograph-5FCD | 5fcc CJK Ideograph-5FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fcf CJK Ideograph-5FCF | 5fce CJK Ideograph-5FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd1 CJK Ideograph-5FD1 | 5fd0 CJK Ideograph-5FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd3 CJK Ideograph-5FD3 | 5fd2 CJK Ideograph-5FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd5 CJK Ideograph-5FD5 | 5fd4 CJK Ideograph-5FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd7 CJK Ideograph-5FD7 | 5fd6 CJK Ideograph-5FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fd9 CJK Ideograph-5FD9 | 5fd8 CJK Ideograph-5FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdb CJK Ideograph-5FDB | 5fda CJK Ideograph-5FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdd CJK Ideograph-5FDD | 5fdc CJK Ideograph-5FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fdf CJK Ideograph-5FDF | 5fde CJK Ideograph-5FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe1 CJK Ideograph-5FE1 | 5fe0 CJK Ideograph-5FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe3 CJK Ideograph-5FE3 | 5fe2 CJK Ideograph-5FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe5 CJK Ideograph-5FE5 | 5fe4 CJK Ideograph-5FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe7 CJK Ideograph-5FE7 | 5fe6 CJK Ideograph-5FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fe9 CJK Ideograph-5FE9 | 5fe8 CJK Ideograph-5FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5feb CJK Ideograph-5FEB | 5fea CJK Ideograph-5FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fed CJK Ideograph-5FED | 5fec CJK Ideograph-5FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fef CJK Ideograph-5FEF | 5fee CJK Ideograph-5FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff1 CJK Ideograph-5FF1 | 5ff0 CJK Ideograph-5FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff3 CJK Ideograph-5FF3 | 5ff2 CJK Ideograph-5FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff5 CJK Ideograph-5FF5 | 5ff4 CJK Ideograph-5FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff7 CJK Ideograph-5FF7 | 5ff6 CJK Ideograph-5FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ff9 CJK Ideograph-5FF9 | 5ff8 CJK Ideograph-5FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ffb CJK Ideograph-5FFB | 5ffa CJK Ideograph-5FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5ffd CJK Ideograph-5FFD | 5ffc CJK Ideograph-5FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 5fff CJK Ideograph-5FFF | 5ffe CJK Ideograph-5FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6001 CJK Ideograph-6001 | 6000 CJK Ideograph-6000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6003 CJK Ideograph-6003 | 6002 CJK Ideograph-6002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6005 CJK Ideograph-6005 | 6004 CJK Ideograph-6004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6007 CJK Ideograph-6007 | 6006 CJK Ideograph-6006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6009 CJK Ideograph-6009 | 6008 CJK Ideograph-6008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600b CJK Ideograph-600B | 600a CJK Ideograph-600A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600d CJK Ideograph-600D | 600c CJK Ideograph-600C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 600f CJK Ideograph-600F | 600e CJK Ideograph-600E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6011 CJK Ideograph-6011 | 6010 CJK Ideograph-6010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6013 CJK Ideograph-6013 | 6012 CJK Ideograph-6012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6015 CJK Ideograph-6015 | 6014 CJK Ideograph-6014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6017 CJK Ideograph-6017 | 6016 CJK Ideograph-6016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6019 CJK Ideograph-6019 | 6018 CJK Ideograph-6018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601b CJK Ideograph-601B | 601a CJK Ideograph-601A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601d CJK Ideograph-601D | 601c CJK Ideograph-601C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 601f CJK Ideograph-601F | 601e CJK Ideograph-601E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6021 CJK Ideograph-6021 | 6020 CJK Ideograph-6020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6023 CJK Ideograph-6023 | 6022 CJK Ideograph-6022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6025 CJK Ideograph-6025 | 6024 CJK Ideograph-6024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6027 CJK Ideograph-6027 | 6026 CJK Ideograph-6026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6029 CJK Ideograph-6029 | 6028 CJK Ideograph-6028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602b CJK Ideograph-602B | 602a CJK Ideograph-602A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602d CJK Ideograph-602D | 602c CJK Ideograph-602C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 602f CJK Ideograph-602F | 602e CJK Ideograph-602E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6031 CJK Ideograph-6031 | 6030 CJK Ideograph-6030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6033 CJK Ideograph-6033 | 6032 CJK Ideograph-6032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6035 CJK Ideograph-6035 | 6034 CJK Ideograph-6034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6037 CJK Ideograph-6037 | 6036 CJK Ideograph-6036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6039 CJK Ideograph-6039 | 6038 CJK Ideograph-6038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603b CJK Ideograph-603B | 603a CJK Ideograph-603A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603d CJK Ideograph-603D | 603c CJK Ideograph-603C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 603f CJK Ideograph-603F | 603e CJK Ideograph-603E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6041 CJK Ideograph-6041 | 6040 CJK Ideograph-6040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6043 CJK Ideograph-6043 | 6042 CJK Ideograph-6042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6045 CJK Ideograph-6045 | 6044 CJK Ideograph-6044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6047 CJK Ideograph-6047 | 6046 CJK Ideograph-6046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6049 CJK Ideograph-6049 | 6048 CJK Ideograph-6048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604b CJK Ideograph-604B | 604a CJK Ideograph-604A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604d CJK Ideograph-604D | 604c CJK Ideograph-604C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 604f CJK Ideograph-604F | 604e CJK Ideograph-604E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6051 CJK Ideograph-6051 | 6050 CJK Ideograph-6050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6053 CJK Ideograph-6053 | 6052 CJK Ideograph-6052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6055 CJK Ideograph-6055 | 6054 CJK Ideograph-6054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6057 CJK Ideograph-6057 | 6056 CJK Ideograph-6056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6059 CJK Ideograph-6059 | 6058 CJK Ideograph-6058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605b CJK Ideograph-605B | 605a CJK Ideograph-605A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605d CJK Ideograph-605D | 605c CJK Ideograph-605C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 605f CJK Ideograph-605F | 605e CJK Ideograph-605E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6061 CJK Ideograph-6061 | 6060 CJK Ideograph-6060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6063 CJK Ideograph-6063 | 6062 CJK Ideograph-6062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6065 CJK Ideograph-6065 | 6064 CJK Ideograph-6064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6067 CJK Ideograph-6067 | 6066 CJK Ideograph-6066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6069 CJK Ideograph-6069 | 6068 CJK Ideograph-6068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606b CJK Ideograph-606B | 606a CJK Ideograph-606A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606d CJK Ideograph-606D | 606c CJK Ideograph-606C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 606f CJK Ideograph-606F | 606e CJK Ideograph-606E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6071 CJK Ideograph-6071 | 6070 CJK Ideograph-6070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6073 CJK Ideograph-6073 | 6072 CJK Ideograph-6072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6075 CJK Ideograph-6075 | 6074 CJK Ideograph-6074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6077 CJK Ideograph-6077 | 6076 CJK Ideograph-6076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6079 CJK Ideograph-6079 | 6078 CJK Ideograph-6078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607b CJK Ideograph-607B | 607a CJK Ideograph-607A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607d CJK Ideograph-607D | 607c CJK Ideograph-607C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 607f CJK Ideograph-607F | 607e CJK Ideograph-607E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6081 CJK Ideograph-6081 | 6080 CJK Ideograph-6080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6083 CJK Ideograph-6083 | 6082 CJK Ideograph-6082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6085 CJK Ideograph-6085 | 6084 CJK Ideograph-6084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6087 CJK Ideograph-6087 | 6086 CJK Ideograph-6086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6089 CJK Ideograph-6089 | 6088 CJK Ideograph-6088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608b CJK Ideograph-608B | 608a CJK Ideograph-608A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608d CJK Ideograph-608D | 608c CJK Ideograph-608C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 608f CJK Ideograph-608F | 608e CJK Ideograph-608E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6091 CJK Ideograph-6091 | 6090 CJK Ideograph-6090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6093 CJK Ideograph-6093 | 6092 CJK Ideograph-6092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6095 CJK Ideograph-6095 | 6094 CJK Ideograph-6094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6097 CJK Ideograph-6097 | 6096 CJK Ideograph-6096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6099 CJK Ideograph-6099 | 6098 CJK Ideograph-6098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609b CJK Ideograph-609B | 609a CJK Ideograph-609A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609d CJK Ideograph-609D | 609c CJK Ideograph-609C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 609f CJK Ideograph-609F | 609e CJK Ideograph-609E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a1 CJK Ideograph-60A1 | 60a0 CJK Ideograph-60A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a3 CJK Ideograph-60A3 | 60a2 CJK Ideograph-60A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a5 CJK Ideograph-60A5 | 60a4 CJK Ideograph-60A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a7 CJK Ideograph-60A7 | 60a6 CJK Ideograph-60A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60a9 CJK Ideograph-60A9 | 60a8 CJK Ideograph-60A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ab CJK Ideograph-60AB | 60aa CJK Ideograph-60AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ad CJK Ideograph-60AD | 60ac CJK Ideograph-60AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60af CJK Ideograph-60AF | 60ae CJK Ideograph-60AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b1 CJK Ideograph-60B1 | 60b0 CJK Ideograph-60B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b3 CJK Ideograph-60B3 | 60b2 CJK Ideograph-60B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b5 CJK Ideograph-60B5 | 60b4 CJK Ideograph-60B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b7 CJK Ideograph-60B7 | 60b6 CJK Ideograph-60B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60b9 CJK Ideograph-60B9 | 60b8 CJK Ideograph-60B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bb CJK Ideograph-60BB | 60ba CJK Ideograph-60BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bd CJK Ideograph-60BD | 60bc CJK Ideograph-60BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60bf CJK Ideograph-60BF | 60be CJK Ideograph-60BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c1 CJK Ideograph-60C1 | 60c0 CJK Ideograph-60C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c3 CJK Ideograph-60C3 | 60c2 CJK Ideograph-60C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c5 CJK Ideograph-60C5 | 60c4 CJK Ideograph-60C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c7 CJK Ideograph-60C7 | 60c6 CJK Ideograph-60C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60c9 CJK Ideograph-60C9 | 60c8 CJK Ideograph-60C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cb CJK Ideograph-60CB | 60ca CJK Ideograph-60CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cd CJK Ideograph-60CD | 60cc CJK Ideograph-60CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60cf CJK Ideograph-60CF | 60ce CJK Ideograph-60CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d1 CJK Ideograph-60D1 | 60d0 CJK Ideograph-60D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d3 CJK Ideograph-60D3 | 60d2 CJK Ideograph-60D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d5 CJK Ideograph-60D5 | 60d4 CJK Ideograph-60D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d7 CJK Ideograph-60D7 | 60d6 CJK Ideograph-60D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60d9 CJK Ideograph-60D9 | 60d8 CJK Ideograph-60D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60db CJK Ideograph-60DB | 60da CJK Ideograph-60DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60dd CJK Ideograph-60DD | 60dc CJK Ideograph-60DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60df CJK Ideograph-60DF | 60de CJK Ideograph-60DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e1 CJK Ideograph-60E1 | 60e0 CJK Ideograph-60E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e3 CJK Ideograph-60E3 | 60e2 CJK Ideograph-60E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e5 CJK Ideograph-60E5 | 60e4 CJK Ideograph-60E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e7 CJK Ideograph-60E7 | 60e6 CJK Ideograph-60E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60e9 CJK Ideograph-60E9 | 60e8 CJK Ideograph-60E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60eb CJK Ideograph-60EB | 60ea CJK Ideograph-60EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ed CJK Ideograph-60ED | 60ec CJK Ideograph-60EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ef CJK Ideograph-60EF | 60ee CJK Ideograph-60EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f1 CJK Ideograph-60F1 | 60f0 CJK Ideograph-60F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f3 CJK Ideograph-60F3 | 60f2 CJK Ideograph-60F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f5 CJK Ideograph-60F5 | 60f4 CJK Ideograph-60F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f7 CJK Ideograph-60F7 | 60f6 CJK Ideograph-60F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60f9 CJK Ideograph-60F9 | 60f8 CJK Ideograph-60F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60fb CJK Ideograph-60FB | 60fa CJK Ideograph-60FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60fd CJK Ideograph-60FD | 60fc CJK Ideograph-60FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 60ff CJK Ideograph-60FF | 60fe CJK Ideograph-60FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6101 CJK Ideograph-6101 | 6100 CJK Ideograph-6100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6103 CJK Ideograph-6103 | 6102 CJK Ideograph-6102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6105 CJK Ideograph-6105 | 6104 CJK Ideograph-6104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6107 CJK Ideograph-6107 | 6106 CJK Ideograph-6106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6109 CJK Ideograph-6109 | 6108 CJK Ideograph-6108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610b CJK Ideograph-610B | 610a CJK Ideograph-610A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610d CJK Ideograph-610D | 610c CJK Ideograph-610C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 610f CJK Ideograph-610F | 610e CJK Ideograph-610E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6111 CJK Ideograph-6111 | 6110 CJK Ideograph-6110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6113 CJK Ideograph-6113 | 6112 CJK Ideograph-6112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6115 CJK Ideograph-6115 | 6114 CJK Ideograph-6114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6117 CJK Ideograph-6117 | 6116 CJK Ideograph-6116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6119 CJK Ideograph-6119 | 6118 CJK Ideograph-6118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611b CJK Ideograph-611B | 611a CJK Ideograph-611A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611d CJK Ideograph-611D | 611c CJK Ideograph-611C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 611f CJK Ideograph-611F | 611e CJK Ideograph-611E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6121 CJK Ideograph-6121 | 6120 CJK Ideograph-6120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6123 CJK Ideograph-6123 | 6122 CJK Ideograph-6122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6125 CJK Ideograph-6125 | 6124 CJK Ideograph-6124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6127 CJK Ideograph-6127 | 6126 CJK Ideograph-6126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6129 CJK Ideograph-6129 | 6128 CJK Ideograph-6128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612b CJK Ideograph-612B | 612a CJK Ideograph-612A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612d CJK Ideograph-612D | 612c CJK Ideograph-612C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 612f CJK Ideograph-612F | 612e CJK Ideograph-612E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6131 CJK Ideograph-6131 | 6130 CJK Ideograph-6130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6133 CJK Ideograph-6133 | 6132 CJK Ideograph-6132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6135 CJK Ideograph-6135 | 6134 CJK Ideograph-6134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6137 CJK Ideograph-6137 | 6136 CJK Ideograph-6136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6139 CJK Ideograph-6139 | 6138 CJK Ideograph-6138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613b CJK Ideograph-613B | 613a CJK Ideograph-613A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613d CJK Ideograph-613D | 613c CJK Ideograph-613C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 613f CJK Ideograph-613F | 613e CJK Ideograph-613E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6141 CJK Ideograph-6141 | 6140 CJK Ideograph-6140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6143 CJK Ideograph-6143 | 6142 CJK Ideograph-6142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6145 CJK Ideograph-6145 | 6144 CJK Ideograph-6144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6147 CJK Ideograph-6147 | 6146 CJK Ideograph-6146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6149 CJK Ideograph-6149 | 6148 CJK Ideograph-6148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614b CJK Ideograph-614B | 614a CJK Ideograph-614A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614d CJK Ideograph-614D | 614c CJK Ideograph-614C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 614f CJK Ideograph-614F | 614e CJK Ideograph-614E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6151 CJK Ideograph-6151 | 6150 CJK Ideograph-6150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6153 CJK Ideograph-6153 | 6152 CJK Ideograph-6152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6155 CJK Ideograph-6155 | 6154 CJK Ideograph-6154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6157 CJK Ideograph-6157 | 6156 CJK Ideograph-6156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6159 CJK Ideograph-6159 | 6158 CJK Ideograph-6158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615b CJK Ideograph-615B | 615a CJK Ideograph-615A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615d CJK Ideograph-615D | 615c CJK Ideograph-615C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 615f CJK Ideograph-615F | 615e CJK Ideograph-615E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6161 CJK Ideograph-6161 | 6160 CJK Ideograph-6160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6163 CJK Ideograph-6163 | 6162 CJK Ideograph-6162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6165 CJK Ideograph-6165 | 6164 CJK Ideograph-6164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6167 CJK Ideograph-6167 | 6166 CJK Ideograph-6166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6169 CJK Ideograph-6169 | 6168 CJK Ideograph-6168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616b CJK Ideograph-616B | 616a CJK Ideograph-616A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616d CJK Ideograph-616D | 616c CJK Ideograph-616C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 616f CJK Ideograph-616F | 616e CJK Ideograph-616E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6171 CJK Ideograph-6171 | 6170 CJK Ideograph-6170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6173 CJK Ideograph-6173 | 6172 CJK Ideograph-6172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6175 CJK Ideograph-6175 | 6174 CJK Ideograph-6174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6177 CJK Ideograph-6177 | 6176 CJK Ideograph-6176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6179 CJK Ideograph-6179 | 6178 CJK Ideograph-6178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617b CJK Ideograph-617B | 617a CJK Ideograph-617A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617d CJK Ideograph-617D | 617c CJK Ideograph-617C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 617f CJK Ideograph-617F | 617e CJK Ideograph-617E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6181 CJK Ideograph-6181 | 6180 CJK Ideograph-6180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6183 CJK Ideograph-6183 | 6182 CJK Ideograph-6182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6185 CJK Ideograph-6185 | 6184 CJK Ideograph-6184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6187 CJK Ideograph-6187 | 6186 CJK Ideograph-6186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6189 CJK Ideograph-6189 | 6188 CJK Ideograph-6188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618b CJK Ideograph-618B | 618a CJK Ideograph-618A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618d CJK Ideograph-618D | 618c CJK Ideograph-618C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 618f CJK Ideograph-618F | 618e CJK Ideograph-618E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6191 CJK Ideograph-6191 | 6190 CJK Ideograph-6190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6193 CJK Ideograph-6193 | 6192 CJK Ideograph-6192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6195 CJK Ideograph-6195 | 6194 CJK Ideograph-6194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6197 CJK Ideograph-6197 | 6196 CJK Ideograph-6196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6199 CJK Ideograph-6199 | 6198 CJK Ideograph-6198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619b CJK Ideograph-619B | 619a CJK Ideograph-619A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619d CJK Ideograph-619D | 619c CJK Ideograph-619C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 619f CJK Ideograph-619F | 619e CJK Ideograph-619E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a1 CJK Ideograph-61A1 | 61a0 CJK Ideograph-61A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a3 CJK Ideograph-61A3 | 61a2 CJK Ideograph-61A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a5 CJK Ideograph-61A5 | 61a4 CJK Ideograph-61A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a7 CJK Ideograph-61A7 | 61a6 CJK Ideograph-61A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61a9 CJK Ideograph-61A9 | 61a8 CJK Ideograph-61A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ab CJK Ideograph-61AB | 61aa CJK Ideograph-61AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ad CJK Ideograph-61AD | 61ac CJK Ideograph-61AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61af CJK Ideograph-61AF | 61ae CJK Ideograph-61AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b1 CJK Ideograph-61B1 | 61b0 CJK Ideograph-61B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b3 CJK Ideograph-61B3 | 61b2 CJK Ideograph-61B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b5 CJK Ideograph-61B5 | 61b4 CJK Ideograph-61B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b7 CJK Ideograph-61B7 | 61b6 CJK Ideograph-61B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61b9 CJK Ideograph-61B9 | 61b8 CJK Ideograph-61B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bb CJK Ideograph-61BB | 61ba CJK Ideograph-61BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bd CJK Ideograph-61BD | 61bc CJK Ideograph-61BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61bf CJK Ideograph-61BF | 61be CJK Ideograph-61BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c1 CJK Ideograph-61C1 | 61c0 CJK Ideograph-61C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c3 CJK Ideograph-61C3 | 61c2 CJK Ideograph-61C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c5 CJK Ideograph-61C5 | 61c4 CJK Ideograph-61C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c7 CJK Ideograph-61C7 | 61c6 CJK Ideograph-61C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61c9 CJK Ideograph-61C9 | 61c8 CJK Ideograph-61C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cb CJK Ideograph-61CB | 61ca CJK Ideograph-61CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cd CJK Ideograph-61CD | 61cc CJK Ideograph-61CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61cf CJK Ideograph-61CF | 61ce CJK Ideograph-61CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d1 CJK Ideograph-61D1 | 61d0 CJK Ideograph-61D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d3 CJK Ideograph-61D3 | 61d2 CJK Ideograph-61D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d5 CJK Ideograph-61D5 | 61d4 CJK Ideograph-61D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d7 CJK Ideograph-61D7 | 61d6 CJK Ideograph-61D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61d9 CJK Ideograph-61D9 | 61d8 CJK Ideograph-61D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61db CJK Ideograph-61DB | 61da CJK Ideograph-61DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61dd CJK Ideograph-61DD | 61dc CJK Ideograph-61DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61df CJK Ideograph-61DF | 61de CJK Ideograph-61DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e1 CJK Ideograph-61E1 | 61e0 CJK Ideograph-61E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e3 CJK Ideograph-61E3 | 61e2 CJK Ideograph-61E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e5 CJK Ideograph-61E5 | 61e4 CJK Ideograph-61E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e7 CJK Ideograph-61E7 | 61e6 CJK Ideograph-61E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61e9 CJK Ideograph-61E9 | 61e8 CJK Ideograph-61E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61eb CJK Ideograph-61EB | 61ea CJK Ideograph-61EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ed CJK Ideograph-61ED | 61ec CJK Ideograph-61EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ef CJK Ideograph-61EF | 61ee CJK Ideograph-61EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f1 CJK Ideograph-61F1 | 61f0 CJK Ideograph-61F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f3 CJK Ideograph-61F3 | 61f2 CJK Ideograph-61F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f5 CJK Ideograph-61F5 | 61f4 CJK Ideograph-61F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f7 CJK Ideograph-61F7 | 61f6 CJK Ideograph-61F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61f9 CJK Ideograph-61F9 | 61f8 CJK Ideograph-61F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61fb CJK Ideograph-61FB | 61fa CJK Ideograph-61FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61fd CJK Ideograph-61FD | 61fc CJK Ideograph-61FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 61ff CJK Ideograph-61FF | 61fe CJK Ideograph-61FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6201 CJK Ideograph-6201 | 6200 CJK Ideograph-6200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6203 CJK Ideograph-6203 | 6202 CJK Ideograph-6202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6205 CJK Ideograph-6205 | 6204 CJK Ideograph-6204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6207 CJK Ideograph-6207 | 6206 CJK Ideograph-6206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6209 CJK Ideograph-6209 | 6208 CJK Ideograph-6208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620b CJK Ideograph-620B | 620a CJK Ideograph-620A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620d CJK Ideograph-620D | 620c CJK Ideograph-620C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 620f CJK Ideograph-620F | 620e CJK Ideograph-620E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6211 CJK Ideograph-6211 | 6210 CJK Ideograph-6210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6213 CJK Ideograph-6213 | 6212 CJK Ideograph-6212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6215 CJK Ideograph-6215 | 6214 CJK Ideograph-6214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6217 CJK Ideograph-6217 | 6216 CJK Ideograph-6216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6219 CJK Ideograph-6219 | 6218 CJK Ideograph-6218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621b CJK Ideograph-621B | 621a CJK Ideograph-621A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621d CJK Ideograph-621D | 621c CJK Ideograph-621C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 621f CJK Ideograph-621F | 621e CJK Ideograph-621E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6221 CJK Ideograph-6221 | 6220 CJK Ideograph-6220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6223 CJK Ideograph-6223 | 6222 CJK Ideograph-6222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6225 CJK Ideograph-6225 | 6224 CJK Ideograph-6224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6227 CJK Ideograph-6227 | 6226 CJK Ideograph-6226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6229 CJK Ideograph-6229 | 6228 CJK Ideograph-6228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622b CJK Ideograph-622B | 622a CJK Ideograph-622A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622d CJK Ideograph-622D | 622c CJK Ideograph-622C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 622f CJK Ideograph-622F | 622e CJK Ideograph-622E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6231 CJK Ideograph-6231 | 6230 CJK Ideograph-6230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6233 CJK Ideograph-6233 | 6232 CJK Ideograph-6232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6235 CJK Ideograph-6235 | 6234 CJK Ideograph-6234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6237 CJK Ideograph-6237 | 6236 CJK Ideograph-6236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6239 CJK Ideograph-6239 | 6238 CJK Ideograph-6238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623b CJK Ideograph-623B | 623a CJK Ideograph-623A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623d CJK Ideograph-623D | 623c CJK Ideograph-623C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 623f CJK Ideograph-623F | 623e CJK Ideograph-623E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6241 CJK Ideograph-6241 | 6240 CJK Ideograph-6240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6243 CJK Ideograph-6243 | 6242 CJK Ideograph-6242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6245 CJK Ideograph-6245 | 6244 CJK Ideograph-6244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6247 CJK Ideograph-6247 | 6246 CJK Ideograph-6246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6249 CJK Ideograph-6249 | 6248 CJK Ideograph-6248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624b CJK Ideograph-624B | 624a CJK Ideograph-624A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624d CJK Ideograph-624D | 624c CJK Ideograph-624C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 624f CJK Ideograph-624F | 624e CJK Ideograph-624E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6251 CJK Ideograph-6251 | 6250 CJK Ideograph-6250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6253 CJK Ideograph-6253 | 6252 CJK Ideograph-6252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6255 CJK Ideograph-6255 | 6254 CJK Ideograph-6254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6257 CJK Ideograph-6257 | 6256 CJK Ideograph-6256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6259 CJK Ideograph-6259 | 6258 CJK Ideograph-6258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625b CJK Ideograph-625B | 625a CJK Ideograph-625A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625d CJK Ideograph-625D | 625c CJK Ideograph-625C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 625f CJK Ideograph-625F | 625e CJK Ideograph-625E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6261 CJK Ideograph-6261 | 6260 CJK Ideograph-6260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6263 CJK Ideograph-6263 | 6262 CJK Ideograph-6262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6265 CJK Ideograph-6265 | 6264 CJK Ideograph-6264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6267 CJK Ideograph-6267 | 6266 CJK Ideograph-6266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6269 CJK Ideograph-6269 | 6268 CJK Ideograph-6268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626b CJK Ideograph-626B | 626a CJK Ideograph-626A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626d CJK Ideograph-626D | 626c CJK Ideograph-626C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 626f CJK Ideograph-626F | 626e CJK Ideograph-626E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6271 CJK Ideograph-6271 | 6270 CJK Ideograph-6270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6273 CJK Ideograph-6273 | 6272 CJK Ideograph-6272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6275 CJK Ideograph-6275 | 6274 CJK Ideograph-6274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6277 CJK Ideograph-6277 | 6276 CJK Ideograph-6276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6279 CJK Ideograph-6279 | 6278 CJK Ideograph-6278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627b CJK Ideograph-627B | 627a CJK Ideograph-627A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627d CJK Ideograph-627D | 627c CJK Ideograph-627C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 627f CJK Ideograph-627F | 627e CJK Ideograph-627E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6281 CJK Ideograph-6281 | 6280 CJK Ideograph-6280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6283 CJK Ideograph-6283 | 6282 CJK Ideograph-6282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6285 CJK Ideograph-6285 | 6284 CJK Ideograph-6284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6287 CJK Ideograph-6287 | 6286 CJK Ideograph-6286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6289 CJK Ideograph-6289 | 6288 CJK Ideograph-6288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628b CJK Ideograph-628B | 628a CJK Ideograph-628A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628d CJK Ideograph-628D | 628c CJK Ideograph-628C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 628f CJK Ideograph-628F | 628e CJK Ideograph-628E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6291 CJK Ideograph-6291 | 6290 CJK Ideograph-6290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6293 CJK Ideograph-6293 | 6292 CJK Ideograph-6292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6295 CJK Ideograph-6295 | 6294 CJK Ideograph-6294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6297 CJK Ideograph-6297 | 6296 CJK Ideograph-6296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6299 CJK Ideograph-6299 | 6298 CJK Ideograph-6298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629b CJK Ideograph-629B | 629a CJK Ideograph-629A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629d CJK Ideograph-629D | 629c CJK Ideograph-629C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 629f CJK Ideograph-629F | 629e CJK Ideograph-629E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a1 CJK Ideograph-62A1 | 62a0 CJK Ideograph-62A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a3 CJK Ideograph-62A3 | 62a2 CJK Ideograph-62A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a5 CJK Ideograph-62A5 | 62a4 CJK Ideograph-62A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a7 CJK Ideograph-62A7 | 62a6 CJK Ideograph-62A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62a9 CJK Ideograph-62A9 | 62a8 CJK Ideograph-62A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ab CJK Ideograph-62AB | 62aa CJK Ideograph-62AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ad CJK Ideograph-62AD | 62ac CJK Ideograph-62AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62af CJK Ideograph-62AF | 62ae CJK Ideograph-62AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b1 CJK Ideograph-62B1 | 62b0 CJK Ideograph-62B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b3 CJK Ideograph-62B3 | 62b2 CJK Ideograph-62B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b5 CJK Ideograph-62B5 | 62b4 CJK Ideograph-62B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b7 CJK Ideograph-62B7 | 62b6 CJK Ideograph-62B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62b9 CJK Ideograph-62B9 | 62b8 CJK Ideograph-62B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bb CJK Ideograph-62BB | 62ba CJK Ideograph-62BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bd CJK Ideograph-62BD | 62bc CJK Ideograph-62BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62bf CJK Ideograph-62BF | 62be CJK Ideograph-62BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c1 CJK Ideograph-62C1 | 62c0 CJK Ideograph-62C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c3 CJK Ideograph-62C3 | 62c2 CJK Ideograph-62C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c5 CJK Ideograph-62C5 | 62c4 CJK Ideograph-62C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c7 CJK Ideograph-62C7 | 62c6 CJK Ideograph-62C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62c9 CJK Ideograph-62C9 | 62c8 CJK Ideograph-62C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cb CJK Ideograph-62CB | 62ca CJK Ideograph-62CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cd CJK Ideograph-62CD | 62cc CJK Ideograph-62CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62cf CJK Ideograph-62CF | 62ce CJK Ideograph-62CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d1 CJK Ideograph-62D1 | 62d0 CJK Ideograph-62D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d3 CJK Ideograph-62D3 | 62d2 CJK Ideograph-62D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d5 CJK Ideograph-62D5 | 62d4 CJK Ideograph-62D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d7 CJK Ideograph-62D7 | 62d6 CJK Ideograph-62D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62d9 CJK Ideograph-62D9 | 62d8 CJK Ideograph-62D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62db CJK Ideograph-62DB | 62da CJK Ideograph-62DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62dd CJK Ideograph-62DD | 62dc CJK Ideograph-62DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62df CJK Ideograph-62DF | 62de CJK Ideograph-62DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e1 CJK Ideograph-62E1 | 62e0 CJK Ideograph-62E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e3 CJK Ideograph-62E3 | 62e2 CJK Ideograph-62E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e5 CJK Ideograph-62E5 | 62e4 CJK Ideograph-62E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e7 CJK Ideograph-62E7 | 62e6 CJK Ideograph-62E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62e9 CJK Ideograph-62E9 | 62e8 CJK Ideograph-62E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62eb CJK Ideograph-62EB | 62ea CJK Ideograph-62EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ed CJK Ideograph-62ED | 62ec CJK Ideograph-62EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ef CJK Ideograph-62EF | 62ee CJK Ideograph-62EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f1 CJK Ideograph-62F1 | 62f0 CJK Ideograph-62F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f3 CJK Ideograph-62F3 | 62f2 CJK Ideograph-62F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f5 CJK Ideograph-62F5 | 62f4 CJK Ideograph-62F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f7 CJK Ideograph-62F7 | 62f6 CJK Ideograph-62F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62f9 CJK Ideograph-62F9 | 62f8 CJK Ideograph-62F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62fb CJK Ideograph-62FB | 62fa CJK Ideograph-62FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62fd CJK Ideograph-62FD | 62fc CJK Ideograph-62FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 62ff CJK Ideograph-62FF | 62fe CJK Ideograph-62FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6301 CJK Ideograph-6301 | 6300 CJK Ideograph-6300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6303 CJK Ideograph-6303 | 6302 CJK Ideograph-6302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6305 CJK Ideograph-6305 | 6304 CJK Ideograph-6304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6307 CJK Ideograph-6307 | 6306 CJK Ideograph-6306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6309 CJK Ideograph-6309 | 6308 CJK Ideograph-6308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630b CJK Ideograph-630B | 630a CJK Ideograph-630A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630d CJK Ideograph-630D | 630c CJK Ideograph-630C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 630f CJK Ideograph-630F | 630e CJK Ideograph-630E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6311 CJK Ideograph-6311 | 6310 CJK Ideograph-6310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6313 CJK Ideograph-6313 | 6312 CJK Ideograph-6312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6315 CJK Ideograph-6315 | 6314 CJK Ideograph-6314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6317 CJK Ideograph-6317 | 6316 CJK Ideograph-6316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6319 CJK Ideograph-6319 | 6318 CJK Ideograph-6318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631b CJK Ideograph-631B | 631a CJK Ideograph-631A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631d CJK Ideograph-631D | 631c CJK Ideograph-631C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 631f CJK Ideograph-631F | 631e CJK Ideograph-631E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6321 CJK Ideograph-6321 | 6320 CJK Ideograph-6320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6323 CJK Ideograph-6323 | 6322 CJK Ideograph-6322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6325 CJK Ideograph-6325 | 6324 CJK Ideograph-6324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6327 CJK Ideograph-6327 | 6326 CJK Ideograph-6326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6329 CJK Ideograph-6329 | 6328 CJK Ideograph-6328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632b CJK Ideograph-632B | 632a CJK Ideograph-632A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632d CJK Ideograph-632D | 632c CJK Ideograph-632C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 632f CJK Ideograph-632F | 632e CJK Ideograph-632E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6331 CJK Ideograph-6331 | 6330 CJK Ideograph-6330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6333 CJK Ideograph-6333 | 6332 CJK Ideograph-6332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6335 CJK Ideograph-6335 | 6334 CJK Ideograph-6334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6337 CJK Ideograph-6337 | 6336 CJK Ideograph-6336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6339 CJK Ideograph-6339 | 6338 CJK Ideograph-6338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633b CJK Ideograph-633B | 633a CJK Ideograph-633A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633d CJK Ideograph-633D | 633c CJK Ideograph-633C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 633f CJK Ideograph-633F | 633e CJK Ideograph-633E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6341 CJK Ideograph-6341 | 6340 CJK Ideograph-6340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6343 CJK Ideograph-6343 | 6342 CJK Ideograph-6342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6345 CJK Ideograph-6345 | 6344 CJK Ideograph-6344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6347 CJK Ideograph-6347 | 6346 CJK Ideograph-6346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6349 CJK Ideograph-6349 | 6348 CJK Ideograph-6348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634b CJK Ideograph-634B | 634a CJK Ideograph-634A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634d CJK Ideograph-634D | 634c CJK Ideograph-634C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 634f CJK Ideograph-634F | 634e CJK Ideograph-634E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6351 CJK Ideograph-6351 | 6350 CJK Ideograph-6350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6353 CJK Ideograph-6353 | 6352 CJK Ideograph-6352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6355 CJK Ideograph-6355 | 6354 CJK Ideograph-6354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6357 CJK Ideograph-6357 | 6356 CJK Ideograph-6356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6359 CJK Ideograph-6359 | 6358 CJK Ideograph-6358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635b CJK Ideograph-635B | 635a CJK Ideograph-635A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635d CJK Ideograph-635D | 635c CJK Ideograph-635C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 635f CJK Ideograph-635F | 635e CJK Ideograph-635E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6361 CJK Ideograph-6361 | 6360 CJK Ideograph-6360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6363 CJK Ideograph-6363 | 6362 CJK Ideograph-6362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6365 CJK Ideograph-6365 | 6364 CJK Ideograph-6364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6367 CJK Ideograph-6367 | 6366 CJK Ideograph-6366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6369 CJK Ideograph-6369 | 6368 CJK Ideograph-6368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636b CJK Ideograph-636B | 636a CJK Ideograph-636A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636d CJK Ideograph-636D | 636c CJK Ideograph-636C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 636f CJK Ideograph-636F | 636e CJK Ideograph-636E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6371 CJK Ideograph-6371 | 6370 CJK Ideograph-6370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6373 CJK Ideograph-6373 | 6372 CJK Ideograph-6372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6375 CJK Ideograph-6375 | 6374 CJK Ideograph-6374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6377 CJK Ideograph-6377 | 6376 CJK Ideograph-6376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6379 CJK Ideograph-6379 | 6378 CJK Ideograph-6378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637b CJK Ideograph-637B | 637a CJK Ideograph-637A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637d CJK Ideograph-637D | 637c CJK Ideograph-637C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 637f CJK Ideograph-637F | 637e CJK Ideograph-637E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6381 CJK Ideograph-6381 | 6380 CJK Ideograph-6380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6383 CJK Ideograph-6383 | 6382 CJK Ideograph-6382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6385 CJK Ideograph-6385 | 6384 CJK Ideograph-6384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6387 CJK Ideograph-6387 | 6386 CJK Ideograph-6386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6389 CJK Ideograph-6389 | 6388 CJK Ideograph-6388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638b CJK Ideograph-638B | 638a CJK Ideograph-638A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638d CJK Ideograph-638D | 638c CJK Ideograph-638C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 638f CJK Ideograph-638F | 638e CJK Ideograph-638E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6391 CJK Ideograph-6391 | 6390 CJK Ideograph-6390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6393 CJK Ideograph-6393 | 6392 CJK Ideograph-6392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6395 CJK Ideograph-6395 | 6394 CJK Ideograph-6394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6397 CJK Ideograph-6397 | 6396 CJK Ideograph-6396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6399 CJK Ideograph-6399 | 6398 CJK Ideograph-6398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639b CJK Ideograph-639B | 639a CJK Ideograph-639A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639d CJK Ideograph-639D | 639c CJK Ideograph-639C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 639f CJK Ideograph-639F | 639e CJK Ideograph-639E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a1 CJK Ideograph-63A1 | 63a0 CJK Ideograph-63A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a3 CJK Ideograph-63A3 | 63a2 CJK Ideograph-63A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a5 CJK Ideograph-63A5 | 63a4 CJK Ideograph-63A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a7 CJK Ideograph-63A7 | 63a6 CJK Ideograph-63A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63a9 CJK Ideograph-63A9 | 63a8 CJK Ideograph-63A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ab CJK Ideograph-63AB | 63aa CJK Ideograph-63AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ad CJK Ideograph-63AD | 63ac CJK Ideograph-63AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63af CJK Ideograph-63AF | 63ae CJK Ideograph-63AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b1 CJK Ideograph-63B1 | 63b0 CJK Ideograph-63B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b3 CJK Ideograph-63B3 | 63b2 CJK Ideograph-63B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b5 CJK Ideograph-63B5 | 63b4 CJK Ideograph-63B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b7 CJK Ideograph-63B7 | 63b6 CJK Ideograph-63B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63b9 CJK Ideograph-63B9 | 63b8 CJK Ideograph-63B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bb CJK Ideograph-63BB | 63ba CJK Ideograph-63BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bd CJK Ideograph-63BD | 63bc CJK Ideograph-63BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63bf CJK Ideograph-63BF | 63be CJK Ideograph-63BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c1 CJK Ideograph-63C1 | 63c0 CJK Ideograph-63C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c3 CJK Ideograph-63C3 | 63c2 CJK Ideograph-63C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c5 CJK Ideograph-63C5 | 63c4 CJK Ideograph-63C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c7 CJK Ideograph-63C7 | 63c6 CJK Ideograph-63C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63c9 CJK Ideograph-63C9 | 63c8 CJK Ideograph-63C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cb CJK Ideograph-63CB | 63ca CJK Ideograph-63CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cd CJK Ideograph-63CD | 63cc CJK Ideograph-63CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63cf CJK Ideograph-63CF | 63ce CJK Ideograph-63CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d1 CJK Ideograph-63D1 | 63d0 CJK Ideograph-63D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d3 CJK Ideograph-63D3 | 63d2 CJK Ideograph-63D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d5 CJK Ideograph-63D5 | 63d4 CJK Ideograph-63D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d7 CJK Ideograph-63D7 | 63d6 CJK Ideograph-63D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63d9 CJK Ideograph-63D9 | 63d8 CJK Ideograph-63D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63db CJK Ideograph-63DB | 63da CJK Ideograph-63DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63dd CJK Ideograph-63DD | 63dc CJK Ideograph-63DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63df CJK Ideograph-63DF | 63de CJK Ideograph-63DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e1 CJK Ideograph-63E1 | 63e0 CJK Ideograph-63E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e3 CJK Ideograph-63E3 | 63e2 CJK Ideograph-63E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e5 CJK Ideograph-63E5 | 63e4 CJK Ideograph-63E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e7 CJK Ideograph-63E7 | 63e6 CJK Ideograph-63E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63e9 CJK Ideograph-63E9 | 63e8 CJK Ideograph-63E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63eb CJK Ideograph-63EB | 63ea CJK Ideograph-63EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ed CJK Ideograph-63ED | 63ec CJK Ideograph-63EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ef CJK Ideograph-63EF | 63ee CJK Ideograph-63EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f1 CJK Ideograph-63F1 | 63f0 CJK Ideograph-63F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f3 CJK Ideograph-63F3 | 63f2 CJK Ideograph-63F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f5 CJK Ideograph-63F5 | 63f4 CJK Ideograph-63F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f7 CJK Ideograph-63F7 | 63f6 CJK Ideograph-63F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63f9 CJK Ideograph-63F9 | 63f8 CJK Ideograph-63F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63fb CJK Ideograph-63FB | 63fa CJK Ideograph-63FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63fd CJK Ideograph-63FD | 63fc CJK Ideograph-63FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 63ff CJK Ideograph-63FF | 63fe CJK Ideograph-63FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6401 CJK Ideograph-6401 | 6400 CJK Ideograph-6400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6403 CJK Ideograph-6403 | 6402 CJK Ideograph-6402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6405 CJK Ideograph-6405 | 6404 CJK Ideograph-6404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6407 CJK Ideograph-6407 | 6406 CJK Ideograph-6406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6409 CJK Ideograph-6409 | 6408 CJK Ideograph-6408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640b CJK Ideograph-640B | 640a CJK Ideograph-640A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640d CJK Ideograph-640D | 640c CJK Ideograph-640C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 640f CJK Ideograph-640F | 640e CJK Ideograph-640E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6411 CJK Ideograph-6411 | 6410 CJK Ideograph-6410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6413 CJK Ideograph-6413 | 6412 CJK Ideograph-6412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6415 CJK Ideograph-6415 | 6414 CJK Ideograph-6414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6417 CJK Ideograph-6417 | 6416 CJK Ideograph-6416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6419 CJK Ideograph-6419 | 6418 CJK Ideograph-6418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641b CJK Ideograph-641B | 641a CJK Ideograph-641A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641d CJK Ideograph-641D | 641c CJK Ideograph-641C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 641f CJK Ideograph-641F | 641e CJK Ideograph-641E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6421 CJK Ideograph-6421 | 6420 CJK Ideograph-6420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6423 CJK Ideograph-6423 | 6422 CJK Ideograph-6422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6425 CJK Ideograph-6425 | 6424 CJK Ideograph-6424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6427 CJK Ideograph-6427 | 6426 CJK Ideograph-6426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6429 CJK Ideograph-6429 | 6428 CJK Ideograph-6428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642b CJK Ideograph-642B | 642a CJK Ideograph-642A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642d CJK Ideograph-642D | 642c CJK Ideograph-642C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 642f CJK Ideograph-642F | 642e CJK Ideograph-642E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6431 CJK Ideograph-6431 | 6430 CJK Ideograph-6430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6433 CJK Ideograph-6433 | 6432 CJK Ideograph-6432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6435 CJK Ideograph-6435 | 6434 CJK Ideograph-6434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6437 CJK Ideograph-6437 | 6436 CJK Ideograph-6436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6439 CJK Ideograph-6439 | 6438 CJK Ideograph-6438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643b CJK Ideograph-643B | 643a CJK Ideograph-643A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643d CJK Ideograph-643D | 643c CJK Ideograph-643C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 643f CJK Ideograph-643F | 643e CJK Ideograph-643E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6441 CJK Ideograph-6441 | 6440 CJK Ideograph-6440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6443 CJK Ideograph-6443 | 6442 CJK Ideograph-6442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6445 CJK Ideograph-6445 | 6444 CJK Ideograph-6444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6447 CJK Ideograph-6447 | 6446 CJK Ideograph-6446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6449 CJK Ideograph-6449 | 6448 CJK Ideograph-6448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644b CJK Ideograph-644B | 644a CJK Ideograph-644A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644d CJK Ideograph-644D | 644c CJK Ideograph-644C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 644f CJK Ideograph-644F | 644e CJK Ideograph-644E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6451 CJK Ideograph-6451 | 6450 CJK Ideograph-6450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6453 CJK Ideograph-6453 | 6452 CJK Ideograph-6452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6455 CJK Ideograph-6455 | 6454 CJK Ideograph-6454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6457 CJK Ideograph-6457 | 6456 CJK Ideograph-6456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6459 CJK Ideograph-6459 | 6458 CJK Ideograph-6458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645b CJK Ideograph-645B | 645a CJK Ideograph-645A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645d CJK Ideograph-645D | 645c CJK Ideograph-645C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 645f CJK Ideograph-645F | 645e CJK Ideograph-645E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6461 CJK Ideograph-6461 | 6460 CJK Ideograph-6460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6463 CJK Ideograph-6463 | 6462 CJK Ideograph-6462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6465 CJK Ideograph-6465 | 6464 CJK Ideograph-6464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6467 CJK Ideograph-6467 | 6466 CJK Ideograph-6466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6469 CJK Ideograph-6469 | 6468 CJK Ideograph-6468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646b CJK Ideograph-646B | 646a CJK Ideograph-646A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646d CJK Ideograph-646D | 646c CJK Ideograph-646C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 646f CJK Ideograph-646F | 646e CJK Ideograph-646E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6471 CJK Ideograph-6471 | 6470 CJK Ideograph-6470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6473 CJK Ideograph-6473 | 6472 CJK Ideograph-6472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6475 CJK Ideograph-6475 | 6474 CJK Ideograph-6474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6477 CJK Ideograph-6477 | 6476 CJK Ideograph-6476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6479 CJK Ideograph-6479 | 6478 CJK Ideograph-6478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647b CJK Ideograph-647B | 647a CJK Ideograph-647A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647d CJK Ideograph-647D | 647c CJK Ideograph-647C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 647f CJK Ideograph-647F | 647e CJK Ideograph-647E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6481 CJK Ideograph-6481 | 6480 CJK Ideograph-6480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6483 CJK Ideograph-6483 | 6482 CJK Ideograph-6482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6485 CJK Ideograph-6485 | 6484 CJK Ideograph-6484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6487 CJK Ideograph-6487 | 6486 CJK Ideograph-6486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6489 CJK Ideograph-6489 | 6488 CJK Ideograph-6488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648b CJK Ideograph-648B | 648a CJK Ideograph-648A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648d CJK Ideograph-648D | 648c CJK Ideograph-648C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 648f CJK Ideograph-648F | 648e CJK Ideograph-648E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6491 CJK Ideograph-6491 | 6490 CJK Ideograph-6490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6493 CJK Ideograph-6493 | 6492 CJK Ideograph-6492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6495 CJK Ideograph-6495 | 6494 CJK Ideograph-6494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6497 CJK Ideograph-6497 | 6496 CJK Ideograph-6496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6499 CJK Ideograph-6499 | 6498 CJK Ideograph-6498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649b CJK Ideograph-649B | 649a CJK Ideograph-649A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649d CJK Ideograph-649D | 649c CJK Ideograph-649C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 649f CJK Ideograph-649F | 649e CJK Ideograph-649E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a1 CJK Ideograph-64A1 | 64a0 CJK Ideograph-64A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a3 CJK Ideograph-64A3 | 64a2 CJK Ideograph-64A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a5 CJK Ideograph-64A5 | 64a4 CJK Ideograph-64A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a7 CJK Ideograph-64A7 | 64a6 CJK Ideograph-64A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64a9 CJK Ideograph-64A9 | 64a8 CJK Ideograph-64A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ab CJK Ideograph-64AB | 64aa CJK Ideograph-64AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ad CJK Ideograph-64AD | 64ac CJK Ideograph-64AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64af CJK Ideograph-64AF | 64ae CJK Ideograph-64AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b1 CJK Ideograph-64B1 | 64b0 CJK Ideograph-64B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b3 CJK Ideograph-64B3 | 64b2 CJK Ideograph-64B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b5 CJK Ideograph-64B5 | 64b4 CJK Ideograph-64B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b7 CJK Ideograph-64B7 | 64b6 CJK Ideograph-64B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64b9 CJK Ideograph-64B9 | 64b8 CJK Ideograph-64B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bb CJK Ideograph-64BB | 64ba CJK Ideograph-64BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bd CJK Ideograph-64BD | 64bc CJK Ideograph-64BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64bf CJK Ideograph-64BF | 64be CJK Ideograph-64BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c1 CJK Ideograph-64C1 | 64c0 CJK Ideograph-64C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c3 CJK Ideograph-64C3 | 64c2 CJK Ideograph-64C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c5 CJK Ideograph-64C5 | 64c4 CJK Ideograph-64C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c7 CJK Ideograph-64C7 | 64c6 CJK Ideograph-64C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64c9 CJK Ideograph-64C9 | 64c8 CJK Ideograph-64C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cb CJK Ideograph-64CB | 64ca CJK Ideograph-64CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cd CJK Ideograph-64CD | 64cc CJK Ideograph-64CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64cf CJK Ideograph-64CF | 64ce CJK Ideograph-64CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d1 CJK Ideograph-64D1 | 64d0 CJK Ideograph-64D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d3 CJK Ideograph-64D3 | 64d2 CJK Ideograph-64D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d5 CJK Ideograph-64D5 | 64d4 CJK Ideograph-64D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d7 CJK Ideograph-64D7 | 64d6 CJK Ideograph-64D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64d9 CJK Ideograph-64D9 | 64d8 CJK Ideograph-64D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64db CJK Ideograph-64DB | 64da CJK Ideograph-64DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64dd CJK Ideograph-64DD | 64dc CJK Ideograph-64DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64df CJK Ideograph-64DF | 64de CJK Ideograph-64DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e1 CJK Ideograph-64E1 | 64e0 CJK Ideograph-64E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e3 CJK Ideograph-64E3 | 64e2 CJK Ideograph-64E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e5 CJK Ideograph-64E5 | 64e4 CJK Ideograph-64E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e7 CJK Ideograph-64E7 | 64e6 CJK Ideograph-64E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64e9 CJK Ideograph-64E9 | 64e8 CJK Ideograph-64E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64eb CJK Ideograph-64EB | 64ea CJK Ideograph-64EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ed CJK Ideograph-64ED | 64ec CJK Ideograph-64EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ef CJK Ideograph-64EF | 64ee CJK Ideograph-64EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f1 CJK Ideograph-64F1 | 64f0 CJK Ideograph-64F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f3 CJK Ideograph-64F3 | 64f2 CJK Ideograph-64F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f5 CJK Ideograph-64F5 | 64f4 CJK Ideograph-64F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f7 CJK Ideograph-64F7 | 64f6 CJK Ideograph-64F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64f9 CJK Ideograph-64F9 | 64f8 CJK Ideograph-64F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64fb CJK Ideograph-64FB | 64fa CJK Ideograph-64FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64fd CJK Ideograph-64FD | 64fc CJK Ideograph-64FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 64ff CJK Ideograph-64FF | 64fe CJK Ideograph-64FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6501 CJK Ideograph-6501 | 6500 CJK Ideograph-6500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6503 CJK Ideograph-6503 | 6502 CJK Ideograph-6502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6505 CJK Ideograph-6505 | 6504 CJK Ideograph-6504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6507 CJK Ideograph-6507 | 6506 CJK Ideograph-6506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6509 CJK Ideograph-6509 | 6508 CJK Ideograph-6508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650b CJK Ideograph-650B | 650a CJK Ideograph-650A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650d CJK Ideograph-650D | 650c CJK Ideograph-650C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 650f CJK Ideograph-650F | 650e CJK Ideograph-650E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6511 CJK Ideograph-6511 | 6510 CJK Ideograph-6510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6513 CJK Ideograph-6513 | 6512 CJK Ideograph-6512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6515 CJK Ideograph-6515 | 6514 CJK Ideograph-6514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6517 CJK Ideograph-6517 | 6516 CJK Ideograph-6516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6519 CJK Ideograph-6519 | 6518 CJK Ideograph-6518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651b CJK Ideograph-651B | 651a CJK Ideograph-651A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651d CJK Ideograph-651D | 651c CJK Ideograph-651C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 651f CJK Ideograph-651F | 651e CJK Ideograph-651E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6521 CJK Ideograph-6521 | 6520 CJK Ideograph-6520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6523 CJK Ideograph-6523 | 6522 CJK Ideograph-6522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6525 CJK Ideograph-6525 | 6524 CJK Ideograph-6524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6527 CJK Ideograph-6527 | 6526 CJK Ideograph-6526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6529 CJK Ideograph-6529 | 6528 CJK Ideograph-6528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652b CJK Ideograph-652B | 652a CJK Ideograph-652A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652d CJK Ideograph-652D | 652c CJK Ideograph-652C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 652f CJK Ideograph-652F | 652e CJK Ideograph-652E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6531 CJK Ideograph-6531 | 6530 CJK Ideograph-6530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6533 CJK Ideograph-6533 | 6532 CJK Ideograph-6532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6535 CJK Ideograph-6535 | 6534 CJK Ideograph-6534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6537 CJK Ideograph-6537 | 6536 CJK Ideograph-6536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6539 CJK Ideograph-6539 | 6538 CJK Ideograph-6538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653b CJK Ideograph-653B | 653a CJK Ideograph-653A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653d CJK Ideograph-653D | 653c CJK Ideograph-653C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 653f CJK Ideograph-653F | 653e CJK Ideograph-653E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6541 CJK Ideograph-6541 | 6540 CJK Ideograph-6540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6543 CJK Ideograph-6543 | 6542 CJK Ideograph-6542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6545 CJK Ideograph-6545 | 6544 CJK Ideograph-6544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6547 CJK Ideograph-6547 | 6546 CJK Ideograph-6546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6549 CJK Ideograph-6549 | 6548 CJK Ideograph-6548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654b CJK Ideograph-654B | 654a CJK Ideograph-654A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654d CJK Ideograph-654D | 654c CJK Ideograph-654C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 654f CJK Ideograph-654F | 654e CJK Ideograph-654E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6551 CJK Ideograph-6551 | 6550 CJK Ideograph-6550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6553 CJK Ideograph-6553 | 6552 CJK Ideograph-6552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6555 CJK Ideograph-6555 | 6554 CJK Ideograph-6554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6557 CJK Ideograph-6557 | 6556 CJK Ideograph-6556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6559 CJK Ideograph-6559 | 6558 CJK Ideograph-6558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655b CJK Ideograph-655B | 655a CJK Ideograph-655A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655d CJK Ideograph-655D | 655c CJK Ideograph-655C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 655f CJK Ideograph-655F | 655e CJK Ideograph-655E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6561 CJK Ideograph-6561 | 6560 CJK Ideograph-6560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6563 CJK Ideograph-6563 | 6562 CJK Ideograph-6562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6565 CJK Ideograph-6565 | 6564 CJK Ideograph-6564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6567 CJK Ideograph-6567 | 6566 CJK Ideograph-6566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6569 CJK Ideograph-6569 | 6568 CJK Ideograph-6568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656b CJK Ideograph-656B | 656a CJK Ideograph-656A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656d CJK Ideograph-656D | 656c CJK Ideograph-656C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 656f CJK Ideograph-656F | 656e CJK Ideograph-656E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6571 CJK Ideograph-6571 | 6570 CJK Ideograph-6570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6573 CJK Ideograph-6573 | 6572 CJK Ideograph-6572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6575 CJK Ideograph-6575 | 6574 CJK Ideograph-6574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6577 CJK Ideograph-6577 | 6576 CJK Ideograph-6576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6579 CJK Ideograph-6579 | 6578 CJK Ideograph-6578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657b CJK Ideograph-657B | 657a CJK Ideograph-657A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657d CJK Ideograph-657D | 657c CJK Ideograph-657C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 657f CJK Ideograph-657F | 657e CJK Ideograph-657E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6581 CJK Ideograph-6581 | 6580 CJK Ideograph-6580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6583 CJK Ideograph-6583 | 6582 CJK Ideograph-6582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6585 CJK Ideograph-6585 | 6584 CJK Ideograph-6584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6587 CJK Ideograph-6587 | 6586 CJK Ideograph-6586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6589 CJK Ideograph-6589 | 6588 CJK Ideograph-6588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658b CJK Ideograph-658B | 658a CJK Ideograph-658A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658d CJK Ideograph-658D | 658c CJK Ideograph-658C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 658f CJK Ideograph-658F | 658e CJK Ideograph-658E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6591 CJK Ideograph-6591 | 6590 CJK Ideograph-6590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6593 CJK Ideograph-6593 | 6592 CJK Ideograph-6592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6595 CJK Ideograph-6595 | 6594 CJK Ideograph-6594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6597 CJK Ideograph-6597 | 6596 CJK Ideograph-6596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6599 CJK Ideograph-6599 | 6598 CJK Ideograph-6598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659b CJK Ideograph-659B | 659a CJK Ideograph-659A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659d CJK Ideograph-659D | 659c CJK Ideograph-659C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 659f CJK Ideograph-659F | 659e CJK Ideograph-659E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a1 CJK Ideograph-65A1 | 65a0 CJK Ideograph-65A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a3 CJK Ideograph-65A3 | 65a2 CJK Ideograph-65A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a5 CJK Ideograph-65A5 | 65a4 CJK Ideograph-65A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a7 CJK Ideograph-65A7 | 65a6 CJK Ideograph-65A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65a9 CJK Ideograph-65A9 | 65a8 CJK Ideograph-65A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ab CJK Ideograph-65AB | 65aa CJK Ideograph-65AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ad CJK Ideograph-65AD | 65ac CJK Ideograph-65AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65af CJK Ideograph-65AF | 65ae CJK Ideograph-65AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b1 CJK Ideograph-65B1 | 65b0 CJK Ideograph-65B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b3 CJK Ideograph-65B3 | 65b2 CJK Ideograph-65B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b5 CJK Ideograph-65B5 | 65b4 CJK Ideograph-65B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b7 CJK Ideograph-65B7 | 65b6 CJK Ideograph-65B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65b9 CJK Ideograph-65B9 | 65b8 CJK Ideograph-65B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bb CJK Ideograph-65BB | 65ba CJK Ideograph-65BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bd CJK Ideograph-65BD | 65bc CJK Ideograph-65BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65bf CJK Ideograph-65BF | 65be CJK Ideograph-65BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c1 CJK Ideograph-65C1 | 65c0 CJK Ideograph-65C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c3 CJK Ideograph-65C3 | 65c2 CJK Ideograph-65C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c5 CJK Ideograph-65C5 | 65c4 CJK Ideograph-65C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c7 CJK Ideograph-65C7 | 65c6 CJK Ideograph-65C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65c9 CJK Ideograph-65C9 | 65c8 CJK Ideograph-65C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cb CJK Ideograph-65CB | 65ca CJK Ideograph-65CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cd CJK Ideograph-65CD | 65cc CJK Ideograph-65CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65cf CJK Ideograph-65CF | 65ce CJK Ideograph-65CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d1 CJK Ideograph-65D1 | 65d0 CJK Ideograph-65D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d3 CJK Ideograph-65D3 | 65d2 CJK Ideograph-65D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d5 CJK Ideograph-65D5 | 65d4 CJK Ideograph-65D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d7 CJK Ideograph-65D7 | 65d6 CJK Ideograph-65D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65d9 CJK Ideograph-65D9 | 65d8 CJK Ideograph-65D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65db CJK Ideograph-65DB | 65da CJK Ideograph-65DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65dd CJK Ideograph-65DD | 65dc CJK Ideograph-65DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65df CJK Ideograph-65DF | 65de CJK Ideograph-65DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e1 CJK Ideograph-65E1 | 65e0 CJK Ideograph-65E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e3 CJK Ideograph-65E3 | 65e2 CJK Ideograph-65E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e5 CJK Ideograph-65E5 | 65e4 CJK Ideograph-65E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e7 CJK Ideograph-65E7 | 65e6 CJK Ideograph-65E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65e9 CJK Ideograph-65E9 | 65e8 CJK Ideograph-65E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65eb CJK Ideograph-65EB | 65ea CJK Ideograph-65EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ed CJK Ideograph-65ED | 65ec CJK Ideograph-65EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ef CJK Ideograph-65EF | 65ee CJK Ideograph-65EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f1 CJK Ideograph-65F1 | 65f0 CJK Ideograph-65F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f3 CJK Ideograph-65F3 | 65f2 CJK Ideograph-65F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f5 CJK Ideograph-65F5 | 65f4 CJK Ideograph-65F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f7 CJK Ideograph-65F7 | 65f6 CJK Ideograph-65F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65f9 CJK Ideograph-65F9 | 65f8 CJK Ideograph-65F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65fb CJK Ideograph-65FB | 65fa CJK Ideograph-65FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65fd CJK Ideograph-65FD | 65fc CJK Ideograph-65FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 65ff CJK Ideograph-65FF | 65fe CJK Ideograph-65FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6601 CJK Ideograph-6601 | 6600 CJK Ideograph-6600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6603 CJK Ideograph-6603 | 6602 CJK Ideograph-6602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6605 CJK Ideograph-6605 | 6604 CJK Ideograph-6604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6607 CJK Ideograph-6607 | 6606 CJK Ideograph-6606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6609 CJK Ideograph-6609 | 6608 CJK Ideograph-6608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660b CJK Ideograph-660B | 660a CJK Ideograph-660A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660d CJK Ideograph-660D | 660c CJK Ideograph-660C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 660f CJK Ideograph-660F | 660e CJK Ideograph-660E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6611 CJK Ideograph-6611 | 6610 CJK Ideograph-6610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6613 CJK Ideograph-6613 | 6612 CJK Ideograph-6612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6615 CJK Ideograph-6615 | 6614 CJK Ideograph-6614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6617 CJK Ideograph-6617 | 6616 CJK Ideograph-6616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6619 CJK Ideograph-6619 | 6618 CJK Ideograph-6618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661b CJK Ideograph-661B | 661a CJK Ideograph-661A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661d CJK Ideograph-661D | 661c CJK Ideograph-661C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 661f CJK Ideograph-661F | 661e CJK Ideograph-661E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6621 CJK Ideograph-6621 | 6620 CJK Ideograph-6620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6623 CJK Ideograph-6623 | 6622 CJK Ideograph-6622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6625 CJK Ideograph-6625 | 6624 CJK Ideograph-6624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6627 CJK Ideograph-6627 | 6626 CJK Ideograph-6626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6629 CJK Ideograph-6629 | 6628 CJK Ideograph-6628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662b CJK Ideograph-662B | 662a CJK Ideograph-662A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662d CJK Ideograph-662D | 662c CJK Ideograph-662C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 662f CJK Ideograph-662F | 662e CJK Ideograph-662E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6631 CJK Ideograph-6631 | 6630 CJK Ideograph-6630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6633 CJK Ideograph-6633 | 6632 CJK Ideograph-6632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6635 CJK Ideograph-6635 | 6634 CJK Ideograph-6634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6637 CJK Ideograph-6637 | 6636 CJK Ideograph-6636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6639 CJK Ideograph-6639 | 6638 CJK Ideograph-6638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663b CJK Ideograph-663B | 663a CJK Ideograph-663A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663d CJK Ideograph-663D | 663c CJK Ideograph-663C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 663f CJK Ideograph-663F | 663e CJK Ideograph-663E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6641 CJK Ideograph-6641 | 6640 CJK Ideograph-6640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6643 CJK Ideograph-6643 | 6642 CJK Ideograph-6642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6645 CJK Ideograph-6645 | 6644 CJK Ideograph-6644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6647 CJK Ideograph-6647 | 6646 CJK Ideograph-6646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6649 CJK Ideograph-6649 | 6648 CJK Ideograph-6648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664b CJK Ideograph-664B | 664a CJK Ideograph-664A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664d CJK Ideograph-664D | 664c CJK Ideograph-664C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 664f CJK Ideograph-664F | 664e CJK Ideograph-664E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6651 CJK Ideograph-6651 | 6650 CJK Ideograph-6650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6653 CJK Ideograph-6653 | 6652 CJK Ideograph-6652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6655 CJK Ideograph-6655 | 6654 CJK Ideograph-6654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6657 CJK Ideograph-6657 | 6656 CJK Ideograph-6656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6659 CJK Ideograph-6659 | 6658 CJK Ideograph-6658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665b CJK Ideograph-665B | 665a CJK Ideograph-665A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665d CJK Ideograph-665D | 665c CJK Ideograph-665C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 665f CJK Ideograph-665F | 665e CJK Ideograph-665E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6661 CJK Ideograph-6661 | 6660 CJK Ideograph-6660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6663 CJK Ideograph-6663 | 6662 CJK Ideograph-6662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6665 CJK Ideograph-6665 | 6664 CJK Ideograph-6664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6667 CJK Ideograph-6667 | 6666 CJK Ideograph-6666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6669 CJK Ideograph-6669 | 6668 CJK Ideograph-6668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666b CJK Ideograph-666B | 666a CJK Ideograph-666A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666d CJK Ideograph-666D | 666c CJK Ideograph-666C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 666f CJK Ideograph-666F | 666e CJK Ideograph-666E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6671 CJK Ideograph-6671 | 6670 CJK Ideograph-6670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6673 CJK Ideograph-6673 | 6672 CJK Ideograph-6672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6675 CJK Ideograph-6675 | 6674 CJK Ideograph-6674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6677 CJK Ideograph-6677 | 6676 CJK Ideograph-6676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6679 CJK Ideograph-6679 | 6678 CJK Ideograph-6678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667b CJK Ideograph-667B | 667a CJK Ideograph-667A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667d CJK Ideograph-667D | 667c CJK Ideograph-667C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 667f CJK Ideograph-667F | 667e CJK Ideograph-667E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6681 CJK Ideograph-6681 | 6680 CJK Ideograph-6680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6683 CJK Ideograph-6683 | 6682 CJK Ideograph-6682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6685 CJK Ideograph-6685 | 6684 CJK Ideograph-6684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6687 CJK Ideograph-6687 | 6686 CJK Ideograph-6686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6689 CJK Ideograph-6689 | 6688 CJK Ideograph-6688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668b CJK Ideograph-668B | 668a CJK Ideograph-668A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668d CJK Ideograph-668D | 668c CJK Ideograph-668C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 668f CJK Ideograph-668F | 668e CJK Ideograph-668E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6691 CJK Ideograph-6691 | 6690 CJK Ideograph-6690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6693 CJK Ideograph-6693 | 6692 CJK Ideograph-6692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6695 CJK Ideograph-6695 | 6694 CJK Ideograph-6694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6697 CJK Ideograph-6697 | 6696 CJK Ideograph-6696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6699 CJK Ideograph-6699 | 6698 CJK Ideograph-6698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669b CJK Ideograph-669B | 669a CJK Ideograph-669A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669d CJK Ideograph-669D | 669c CJK Ideograph-669C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 669f CJK Ideograph-669F | 669e CJK Ideograph-669E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a1 CJK Ideograph-66A1 | 66a0 CJK Ideograph-66A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a3 CJK Ideograph-66A3 | 66a2 CJK Ideograph-66A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a5 CJK Ideograph-66A5 | 66a4 CJK Ideograph-66A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a7 CJK Ideograph-66A7 | 66a6 CJK Ideograph-66A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66a9 CJK Ideograph-66A9 | 66a8 CJK Ideograph-66A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ab CJK Ideograph-66AB | 66aa CJK Ideograph-66AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ad CJK Ideograph-66AD | 66ac CJK Ideograph-66AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66af CJK Ideograph-66AF | 66ae CJK Ideograph-66AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b1 CJK Ideograph-66B1 | 66b0 CJK Ideograph-66B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b3 CJK Ideograph-66B3 | 66b2 CJK Ideograph-66B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b5 CJK Ideograph-66B5 | 66b4 CJK Ideograph-66B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b7 CJK Ideograph-66B7 | 66b6 CJK Ideograph-66B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66b9 CJK Ideograph-66B9 | 66b8 CJK Ideograph-66B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bb CJK Ideograph-66BB | 66ba CJK Ideograph-66BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bd CJK Ideograph-66BD | 66bc CJK Ideograph-66BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66bf CJK Ideograph-66BF | 66be CJK Ideograph-66BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c1 CJK Ideograph-66C1 | 66c0 CJK Ideograph-66C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c3 CJK Ideograph-66C3 | 66c2 CJK Ideograph-66C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c5 CJK Ideograph-66C5 | 66c4 CJK Ideograph-66C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c7 CJK Ideograph-66C7 | 66c6 CJK Ideograph-66C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66c9 CJK Ideograph-66C9 | 66c8 CJK Ideograph-66C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cb CJK Ideograph-66CB | 66ca CJK Ideograph-66CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cd CJK Ideograph-66CD | 66cc CJK Ideograph-66CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66cf CJK Ideograph-66CF | 66ce CJK Ideograph-66CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d1 CJK Ideograph-66D1 | 66d0 CJK Ideograph-66D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d3 CJK Ideograph-66D3 | 66d2 CJK Ideograph-66D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d5 CJK Ideograph-66D5 | 66d4 CJK Ideograph-66D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d7 CJK Ideograph-66D7 | 66d6 CJK Ideograph-66D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66d9 CJK Ideograph-66D9 | 66d8 CJK Ideograph-66D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66db CJK Ideograph-66DB | 66da CJK Ideograph-66DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66dd CJK Ideograph-66DD | 66dc CJK Ideograph-66DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66df CJK Ideograph-66DF | 66de CJK Ideograph-66DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e1 CJK Ideograph-66E1 | 66e0 CJK Ideograph-66E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e3 CJK Ideograph-66E3 | 66e2 CJK Ideograph-66E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e5 CJK Ideograph-66E5 | 66e4 CJK Ideograph-66E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e7 CJK Ideograph-66E7 | 66e6 CJK Ideograph-66E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66e9 CJK Ideograph-66E9 | 66e8 CJK Ideograph-66E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66eb CJK Ideograph-66EB | 66ea CJK Ideograph-66EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ed CJK Ideograph-66ED | 66ec CJK Ideograph-66EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ef CJK Ideograph-66EF | 66ee CJK Ideograph-66EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f1 CJK Ideograph-66F1 | 66f0 CJK Ideograph-66F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f3 CJK Ideograph-66F3 | 66f2 CJK Ideograph-66F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f5 CJK Ideograph-66F5 | 66f4 CJK Ideograph-66F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f7 CJK Ideograph-66F7 | 66f6 CJK Ideograph-66F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66f9 CJK Ideograph-66F9 | 66f8 CJK Ideograph-66F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66fb CJK Ideograph-66FB | 66fa CJK Ideograph-66FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66fd CJK Ideograph-66FD | 66fc CJK Ideograph-66FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 66ff CJK Ideograph-66FF | 66fe CJK Ideograph-66FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6701 CJK Ideograph-6701 | 6700 CJK Ideograph-6700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6703 CJK Ideograph-6703 | 6702 CJK Ideograph-6702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6705 CJK Ideograph-6705 | 6704 CJK Ideograph-6704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6707 CJK Ideograph-6707 | 6706 CJK Ideograph-6706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6709 CJK Ideograph-6709 | 6708 CJK Ideograph-6708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670b CJK Ideograph-670B | 670a CJK Ideograph-670A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670d CJK Ideograph-670D | 670c CJK Ideograph-670C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 670f CJK Ideograph-670F | 670e CJK Ideograph-670E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6711 CJK Ideograph-6711 | 6710 CJK Ideograph-6710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6713 CJK Ideograph-6713 | 6712 CJK Ideograph-6712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6715 CJK Ideograph-6715 | 6714 CJK Ideograph-6714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6717 CJK Ideograph-6717 | 6716 CJK Ideograph-6716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6719 CJK Ideograph-6719 | 6718 CJK Ideograph-6718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671b CJK Ideograph-671B | 671a CJK Ideograph-671A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671d CJK Ideograph-671D | 671c CJK Ideograph-671C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 671f CJK Ideograph-671F | 671e CJK Ideograph-671E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6721 CJK Ideograph-6721 | 6720 CJK Ideograph-6720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6723 CJK Ideograph-6723 | 6722 CJK Ideograph-6722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6725 CJK Ideograph-6725 | 6724 CJK Ideograph-6724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6727 CJK Ideograph-6727 | 6726 CJK Ideograph-6726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6729 CJK Ideograph-6729 | 6728 CJK Ideograph-6728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672b CJK Ideograph-672B | 672a CJK Ideograph-672A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672d CJK Ideograph-672D | 672c CJK Ideograph-672C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 672f CJK Ideograph-672F | 672e CJK Ideograph-672E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6731 CJK Ideograph-6731 | 6730 CJK Ideograph-6730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6733 CJK Ideograph-6733 | 6732 CJK Ideograph-6732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6735 CJK Ideograph-6735 | 6734 CJK Ideograph-6734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6737 CJK Ideograph-6737 | 6736 CJK Ideograph-6736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6739 CJK Ideograph-6739 | 6738 CJK Ideograph-6738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673b CJK Ideograph-673B | 673a CJK Ideograph-673A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673d CJK Ideograph-673D | 673c CJK Ideograph-673C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 673f CJK Ideograph-673F | 673e CJK Ideograph-673E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6741 CJK Ideograph-6741 | 6740 CJK Ideograph-6740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6743 CJK Ideograph-6743 | 6742 CJK Ideograph-6742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6745 CJK Ideograph-6745 | 6744 CJK Ideograph-6744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6747 CJK Ideograph-6747 | 6746 CJK Ideograph-6746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6749 CJK Ideograph-6749 | 6748 CJK Ideograph-6748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674b CJK Ideograph-674B | 674a CJK Ideograph-674A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674d CJK Ideograph-674D | 674c CJK Ideograph-674C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 674f CJK Ideograph-674F | 674e CJK Ideograph-674E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6751 CJK Ideograph-6751 | 6750 CJK Ideograph-6750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6753 CJK Ideograph-6753 | 6752 CJK Ideograph-6752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6755 CJK Ideograph-6755 | 6754 CJK Ideograph-6754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6757 CJK Ideograph-6757 | 6756 CJK Ideograph-6756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6759 CJK Ideograph-6759 | 6758 CJK Ideograph-6758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675b CJK Ideograph-675B | 675a CJK Ideograph-675A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675d CJK Ideograph-675D | 675c CJK Ideograph-675C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 675f CJK Ideograph-675F | 675e CJK Ideograph-675E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6761 CJK Ideograph-6761 | 6760 CJK Ideograph-6760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6763 CJK Ideograph-6763 | 6762 CJK Ideograph-6762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6765 CJK Ideograph-6765 | 6764 CJK Ideograph-6764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6767 CJK Ideograph-6767 | 6766 CJK Ideograph-6766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6769 CJK Ideograph-6769 | 6768 CJK Ideograph-6768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676b CJK Ideograph-676B | 676a CJK Ideograph-676A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676d CJK Ideograph-676D | 676c CJK Ideograph-676C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 676f CJK Ideograph-676F | 676e CJK Ideograph-676E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6771 CJK Ideograph-6771 | 6770 CJK Ideograph-6770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6773 CJK Ideograph-6773 | 6772 CJK Ideograph-6772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6775 CJK Ideograph-6775 | 6774 CJK Ideograph-6774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6777 CJK Ideograph-6777 | 6776 CJK Ideograph-6776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6779 CJK Ideograph-6779 | 6778 CJK Ideograph-6778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677b CJK Ideograph-677B | 677a CJK Ideograph-677A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677d CJK Ideograph-677D | 677c CJK Ideograph-677C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 677f CJK Ideograph-677F | 677e CJK Ideograph-677E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6781 CJK Ideograph-6781 | 6780 CJK Ideograph-6780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6783 CJK Ideograph-6783 | 6782 CJK Ideograph-6782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6785 CJK Ideograph-6785 | 6784 CJK Ideograph-6784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6787 CJK Ideograph-6787 | 6786 CJK Ideograph-6786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6789 CJK Ideograph-6789 | 6788 CJK Ideograph-6788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678b CJK Ideograph-678B | 678a CJK Ideograph-678A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678d CJK Ideograph-678D | 678c CJK Ideograph-678C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 678f CJK Ideograph-678F | 678e CJK Ideograph-678E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6791 CJK Ideograph-6791 | 6790 CJK Ideograph-6790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6793 CJK Ideograph-6793 | 6792 CJK Ideograph-6792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6795 CJK Ideograph-6795 | 6794 CJK Ideograph-6794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6797 CJK Ideograph-6797 | 6796 CJK Ideograph-6796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6799 CJK Ideograph-6799 | 6798 CJK Ideograph-6798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679b CJK Ideograph-679B | 679a CJK Ideograph-679A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679d CJK Ideograph-679D | 679c CJK Ideograph-679C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 679f CJK Ideograph-679F | 679e CJK Ideograph-679E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a1 CJK Ideograph-67A1 | 67a0 CJK Ideograph-67A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a3 CJK Ideograph-67A3 | 67a2 CJK Ideograph-67A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a5 CJK Ideograph-67A5 | 67a4 CJK Ideograph-67A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a7 CJK Ideograph-67A7 | 67a6 CJK Ideograph-67A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67a9 CJK Ideograph-67A9 | 67a8 CJK Ideograph-67A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ab CJK Ideograph-67AB | 67aa CJK Ideograph-67AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ad CJK Ideograph-67AD | 67ac CJK Ideograph-67AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67af CJK Ideograph-67AF | 67ae CJK Ideograph-67AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b1 CJK Ideograph-67B1 | 67b0 CJK Ideograph-67B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b3 CJK Ideograph-67B3 | 67b2 CJK Ideograph-67B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b5 CJK Ideograph-67B5 | 67b4 CJK Ideograph-67B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b7 CJK Ideograph-67B7 | 67b6 CJK Ideograph-67B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67b9 CJK Ideograph-67B9 | 67b8 CJK Ideograph-67B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bb CJK Ideograph-67BB | 67ba CJK Ideograph-67BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bd CJK Ideograph-67BD | 67bc CJK Ideograph-67BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67bf CJK Ideograph-67BF | 67be CJK Ideograph-67BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c1 CJK Ideograph-67C1 | 67c0 CJK Ideograph-67C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c3 CJK Ideograph-67C3 | 67c2 CJK Ideograph-67C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c5 CJK Ideograph-67C5 | 67c4 CJK Ideograph-67C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c7 CJK Ideograph-67C7 | 67c6 CJK Ideograph-67C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67c9 CJK Ideograph-67C9 | 67c8 CJK Ideograph-67C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cb CJK Ideograph-67CB | 67ca CJK Ideograph-67CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cd CJK Ideograph-67CD | 67cc CJK Ideograph-67CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67cf CJK Ideograph-67CF | 67ce CJK Ideograph-67CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d1 CJK Ideograph-67D1 | 67d0 CJK Ideograph-67D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d3 CJK Ideograph-67D3 | 67d2 CJK Ideograph-67D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d5 CJK Ideograph-67D5 | 67d4 CJK Ideograph-67D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d7 CJK Ideograph-67D7 | 67d6 CJK Ideograph-67D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67d9 CJK Ideograph-67D9 | 67d8 CJK Ideograph-67D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67db CJK Ideograph-67DB | 67da CJK Ideograph-67DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67dd CJK Ideograph-67DD | 67dc CJK Ideograph-67DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67df CJK Ideograph-67DF | 67de CJK Ideograph-67DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e1 CJK Ideograph-67E1 | 67e0 CJK Ideograph-67E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e3 CJK Ideograph-67E3 | 67e2 CJK Ideograph-67E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e5 CJK Ideograph-67E5 | 67e4 CJK Ideograph-67E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e7 CJK Ideograph-67E7 | 67e6 CJK Ideograph-67E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67e9 CJK Ideograph-67E9 | 67e8 CJK Ideograph-67E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67eb CJK Ideograph-67EB | 67ea CJK Ideograph-67EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ed CJK Ideograph-67ED | 67ec CJK Ideograph-67EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ef CJK Ideograph-67EF | 67ee CJK Ideograph-67EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f1 CJK Ideograph-67F1 | 67f0 CJK Ideograph-67F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f3 CJK Ideograph-67F3 | 67f2 CJK Ideograph-67F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f5 CJK Ideograph-67F5 | 67f4 CJK Ideograph-67F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f7 CJK Ideograph-67F7 | 67f6 CJK Ideograph-67F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67f9 CJK Ideograph-67F9 | 67f8 CJK Ideograph-67F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67fb CJK Ideograph-67FB | 67fa CJK Ideograph-67FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67fd CJK Ideograph-67FD | 67fc CJK Ideograph-67FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 67ff CJK Ideograph-67FF | 67fe CJK Ideograph-67FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6801 CJK Ideograph-6801 | 6800 CJK Ideograph-6800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6803 CJK Ideograph-6803 | 6802 CJK Ideograph-6802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6805 CJK Ideograph-6805 | 6804 CJK Ideograph-6804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6807 CJK Ideograph-6807 | 6806 CJK Ideograph-6806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6809 CJK Ideograph-6809 | 6808 CJK Ideograph-6808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680b CJK Ideograph-680B | 680a CJK Ideograph-680A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680d CJK Ideograph-680D | 680c CJK Ideograph-680C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 680f CJK Ideograph-680F | 680e CJK Ideograph-680E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6811 CJK Ideograph-6811 | 6810 CJK Ideograph-6810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6813 CJK Ideograph-6813 | 6812 CJK Ideograph-6812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6815 CJK Ideograph-6815 | 6814 CJK Ideograph-6814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6817 CJK Ideograph-6817 | 6816 CJK Ideograph-6816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6819 CJK Ideograph-6819 | 6818 CJK Ideograph-6818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681b CJK Ideograph-681B | 681a CJK Ideograph-681A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681d CJK Ideograph-681D | 681c CJK Ideograph-681C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 681f CJK Ideograph-681F | 681e CJK Ideograph-681E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6821 CJK Ideograph-6821 | 6820 CJK Ideograph-6820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6823 CJK Ideograph-6823 | 6822 CJK Ideograph-6822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6825 CJK Ideograph-6825 | 6824 CJK Ideograph-6824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6827 CJK Ideograph-6827 | 6826 CJK Ideograph-6826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6829 CJK Ideograph-6829 | 6828 CJK Ideograph-6828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682b CJK Ideograph-682B | 682a CJK Ideograph-682A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682d CJK Ideograph-682D | 682c CJK Ideograph-682C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 682f CJK Ideograph-682F | 682e CJK Ideograph-682E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6831 CJK Ideograph-6831 | 6830 CJK Ideograph-6830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6833 CJK Ideograph-6833 | 6832 CJK Ideograph-6832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6835 CJK Ideograph-6835 | 6834 CJK Ideograph-6834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6837 CJK Ideograph-6837 | 6836 CJK Ideograph-6836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6839 CJK Ideograph-6839 | 6838 CJK Ideograph-6838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683b CJK Ideograph-683B | 683a CJK Ideograph-683A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683d CJK Ideograph-683D | 683c CJK Ideograph-683C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 683f CJK Ideograph-683F | 683e CJK Ideograph-683E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6841 CJK Ideograph-6841 | 6840 CJK Ideograph-6840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6843 CJK Ideograph-6843 | 6842 CJK Ideograph-6842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6845 CJK Ideograph-6845 | 6844 CJK Ideograph-6844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6847 CJK Ideograph-6847 | 6846 CJK Ideograph-6846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6849 CJK Ideograph-6849 | 6848 CJK Ideograph-6848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684b CJK Ideograph-684B | 684a CJK Ideograph-684A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684d CJK Ideograph-684D | 684c CJK Ideograph-684C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 684f CJK Ideograph-684F | 684e CJK Ideograph-684E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6851 CJK Ideograph-6851 | 6850 CJK Ideograph-6850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6853 CJK Ideograph-6853 | 6852 CJK Ideograph-6852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6855 CJK Ideograph-6855 | 6854 CJK Ideograph-6854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6857 CJK Ideograph-6857 | 6856 CJK Ideograph-6856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6859 CJK Ideograph-6859 | 6858 CJK Ideograph-6858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685b CJK Ideograph-685B | 685a CJK Ideograph-685A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685d CJK Ideograph-685D | 685c CJK Ideograph-685C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 685f CJK Ideograph-685F | 685e CJK Ideograph-685E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6861 CJK Ideograph-6861 | 6860 CJK Ideograph-6860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6863 CJK Ideograph-6863 | 6862 CJK Ideograph-6862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6865 CJK Ideograph-6865 | 6864 CJK Ideograph-6864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6867 CJK Ideograph-6867 | 6866 CJK Ideograph-6866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6869 CJK Ideograph-6869 | 6868 CJK Ideograph-6868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686b CJK Ideograph-686B | 686a CJK Ideograph-686A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686d CJK Ideograph-686D | 686c CJK Ideograph-686C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 686f CJK Ideograph-686F | 686e CJK Ideograph-686E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6871 CJK Ideograph-6871 | 6870 CJK Ideograph-6870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6873 CJK Ideograph-6873 | 6872 CJK Ideograph-6872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6875 CJK Ideograph-6875 | 6874 CJK Ideograph-6874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6877 CJK Ideograph-6877 | 6876 CJK Ideograph-6876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6879 CJK Ideograph-6879 | 6878 CJK Ideograph-6878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687b CJK Ideograph-687B | 687a CJK Ideograph-687A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687d CJK Ideograph-687D | 687c CJK Ideograph-687C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 687f CJK Ideograph-687F | 687e CJK Ideograph-687E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6881 CJK Ideograph-6881 | 6880 CJK Ideograph-6880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6883 CJK Ideograph-6883 | 6882 CJK Ideograph-6882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6885 CJK Ideograph-6885 | 6884 CJK Ideograph-6884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6887 CJK Ideograph-6887 | 6886 CJK Ideograph-6886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6889 CJK Ideograph-6889 | 6888 CJK Ideograph-6888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688b CJK Ideograph-688B | 688a CJK Ideograph-688A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688d CJK Ideograph-688D | 688c CJK Ideograph-688C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 688f CJK Ideograph-688F | 688e CJK Ideograph-688E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6891 CJK Ideograph-6891 | 6890 CJK Ideograph-6890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6893 CJK Ideograph-6893 | 6892 CJK Ideograph-6892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6895 CJK Ideograph-6895 | 6894 CJK Ideograph-6894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6897 CJK Ideograph-6897 | 6896 CJK Ideograph-6896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6899 CJK Ideograph-6899 | 6898 CJK Ideograph-6898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689b CJK Ideograph-689B | 689a CJK Ideograph-689A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689d CJK Ideograph-689D | 689c CJK Ideograph-689C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 689f CJK Ideograph-689F | 689e CJK Ideograph-689E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a1 CJK Ideograph-68A1 | 68a0 CJK Ideograph-68A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a3 CJK Ideograph-68A3 | 68a2 CJK Ideograph-68A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a5 CJK Ideograph-68A5 | 68a4 CJK Ideograph-68A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a7 CJK Ideograph-68A7 | 68a6 CJK Ideograph-68A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68a9 CJK Ideograph-68A9 | 68a8 CJK Ideograph-68A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ab CJK Ideograph-68AB | 68aa CJK Ideograph-68AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ad CJK Ideograph-68AD | 68ac CJK Ideograph-68AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68af CJK Ideograph-68AF | 68ae CJK Ideograph-68AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b1 CJK Ideograph-68B1 | 68b0 CJK Ideograph-68B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b3 CJK Ideograph-68B3 | 68b2 CJK Ideograph-68B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b5 CJK Ideograph-68B5 | 68b4 CJK Ideograph-68B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b7 CJK Ideograph-68B7 | 68b6 CJK Ideograph-68B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68b9 CJK Ideograph-68B9 | 68b8 CJK Ideograph-68B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bb CJK Ideograph-68BB | 68ba CJK Ideograph-68BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bd CJK Ideograph-68BD | 68bc CJK Ideograph-68BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68bf CJK Ideograph-68BF | 68be CJK Ideograph-68BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c1 CJK Ideograph-68C1 | 68c0 CJK Ideograph-68C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c3 CJK Ideograph-68C3 | 68c2 CJK Ideograph-68C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c5 CJK Ideograph-68C5 | 68c4 CJK Ideograph-68C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c7 CJK Ideograph-68C7 | 68c6 CJK Ideograph-68C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68c9 CJK Ideograph-68C9 | 68c8 CJK Ideograph-68C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cb CJK Ideograph-68CB | 68ca CJK Ideograph-68CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cd CJK Ideograph-68CD | 68cc CJK Ideograph-68CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68cf CJK Ideograph-68CF | 68ce CJK Ideograph-68CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d1 CJK Ideograph-68D1 | 68d0 CJK Ideograph-68D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d3 CJK Ideograph-68D3 | 68d2 CJK Ideograph-68D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d5 CJK Ideograph-68D5 | 68d4 CJK Ideograph-68D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d7 CJK Ideograph-68D7 | 68d6 CJK Ideograph-68D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68d9 CJK Ideograph-68D9 | 68d8 CJK Ideograph-68D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68db CJK Ideograph-68DB | 68da CJK Ideograph-68DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68dd CJK Ideograph-68DD | 68dc CJK Ideograph-68DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68df CJK Ideograph-68DF | 68de CJK Ideograph-68DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e1 CJK Ideograph-68E1 | 68e0 CJK Ideograph-68E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e3 CJK Ideograph-68E3 | 68e2 CJK Ideograph-68E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e5 CJK Ideograph-68E5 | 68e4 CJK Ideograph-68E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e7 CJK Ideograph-68E7 | 68e6 CJK Ideograph-68E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68e9 CJK Ideograph-68E9 | 68e8 CJK Ideograph-68E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68eb CJK Ideograph-68EB | 68ea CJK Ideograph-68EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ed CJK Ideograph-68ED | 68ec CJK Ideograph-68EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ef CJK Ideograph-68EF | 68ee CJK Ideograph-68EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f1 CJK Ideograph-68F1 | 68f0 CJK Ideograph-68F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f3 CJK Ideograph-68F3 | 68f2 CJK Ideograph-68F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f5 CJK Ideograph-68F5 | 68f4 CJK Ideograph-68F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f7 CJK Ideograph-68F7 | 68f6 CJK Ideograph-68F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68f9 CJK Ideograph-68F9 | 68f8 CJK Ideograph-68F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68fb CJK Ideograph-68FB | 68fa CJK Ideograph-68FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68fd CJK Ideograph-68FD | 68fc CJK Ideograph-68FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 68ff CJK Ideograph-68FF | 68fe CJK Ideograph-68FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6901 CJK Ideograph-6901 | 6900 CJK Ideograph-6900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6903 CJK Ideograph-6903 | 6902 CJK Ideograph-6902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6905 CJK Ideograph-6905 | 6904 CJK Ideograph-6904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6907 CJK Ideograph-6907 | 6906 CJK Ideograph-6906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6909 CJK Ideograph-6909 | 6908 CJK Ideograph-6908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690b CJK Ideograph-690B | 690a CJK Ideograph-690A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690d CJK Ideograph-690D | 690c CJK Ideograph-690C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 690f CJK Ideograph-690F | 690e CJK Ideograph-690E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6911 CJK Ideograph-6911 | 6910 CJK Ideograph-6910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6913 CJK Ideograph-6913 | 6912 CJK Ideograph-6912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6915 CJK Ideograph-6915 | 6914 CJK Ideograph-6914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6917 CJK Ideograph-6917 | 6916 CJK Ideograph-6916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6919 CJK Ideograph-6919 | 6918 CJK Ideograph-6918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691b CJK Ideograph-691B | 691a CJK Ideograph-691A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691d CJK Ideograph-691D | 691c CJK Ideograph-691C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 691f CJK Ideograph-691F | 691e CJK Ideograph-691E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6921 CJK Ideograph-6921 | 6920 CJK Ideograph-6920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6923 CJK Ideograph-6923 | 6922 CJK Ideograph-6922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6925 CJK Ideograph-6925 | 6924 CJK Ideograph-6924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6927 CJK Ideograph-6927 | 6926 CJK Ideograph-6926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6929 CJK Ideograph-6929 | 6928 CJK Ideograph-6928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692b CJK Ideograph-692B | 692a CJK Ideograph-692A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692d CJK Ideograph-692D | 692c CJK Ideograph-692C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 692f CJK Ideograph-692F | 692e CJK Ideograph-692E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6931 CJK Ideograph-6931 | 6930 CJK Ideograph-6930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6933 CJK Ideograph-6933 | 6932 CJK Ideograph-6932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6935 CJK Ideograph-6935 | 6934 CJK Ideograph-6934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6937 CJK Ideograph-6937 | 6936 CJK Ideograph-6936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6939 CJK Ideograph-6939 | 6938 CJK Ideograph-6938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693b CJK Ideograph-693B | 693a CJK Ideograph-693A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693d CJK Ideograph-693D | 693c CJK Ideograph-693C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 693f CJK Ideograph-693F | 693e CJK Ideograph-693E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6941 CJK Ideograph-6941 | 6940 CJK Ideograph-6940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6943 CJK Ideograph-6943 | 6942 CJK Ideograph-6942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6945 CJK Ideograph-6945 | 6944 CJK Ideograph-6944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6947 CJK Ideograph-6947 | 6946 CJK Ideograph-6946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6949 CJK Ideograph-6949 | 6948 CJK Ideograph-6948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694b CJK Ideograph-694B | 694a CJK Ideograph-694A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694d CJK Ideograph-694D | 694c CJK Ideograph-694C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 694f CJK Ideograph-694F | 694e CJK Ideograph-694E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6951 CJK Ideograph-6951 | 6950 CJK Ideograph-6950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6953 CJK Ideograph-6953 | 6952 CJK Ideograph-6952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6955 CJK Ideograph-6955 | 6954 CJK Ideograph-6954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6957 CJK Ideograph-6957 | 6956 CJK Ideograph-6956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6959 CJK Ideograph-6959 | 6958 CJK Ideograph-6958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695b CJK Ideograph-695B | 695a CJK Ideograph-695A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695d CJK Ideograph-695D | 695c CJK Ideograph-695C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 695f CJK Ideograph-695F | 695e CJK Ideograph-695E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6961 CJK Ideograph-6961 | 6960 CJK Ideograph-6960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6963 CJK Ideograph-6963 | 6962 CJK Ideograph-6962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6965 CJK Ideograph-6965 | 6964 CJK Ideograph-6964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6967 CJK Ideograph-6967 | 6966 CJK Ideograph-6966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6969 CJK Ideograph-6969 | 6968 CJK Ideograph-6968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696b CJK Ideograph-696B | 696a CJK Ideograph-696A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696d CJK Ideograph-696D | 696c CJK Ideograph-696C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 696f CJK Ideograph-696F | 696e CJK Ideograph-696E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6971 CJK Ideograph-6971 | 6970 CJK Ideograph-6970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6973 CJK Ideograph-6973 | 6972 CJK Ideograph-6972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6975 CJK Ideograph-6975 | 6974 CJK Ideograph-6974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6977 CJK Ideograph-6977 | 6976 CJK Ideograph-6976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6979 CJK Ideograph-6979 | 6978 CJK Ideograph-6978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697b CJK Ideograph-697B | 697a CJK Ideograph-697A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697d CJK Ideograph-697D | 697c CJK Ideograph-697C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 697f CJK Ideograph-697F | 697e CJK Ideograph-697E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6981 CJK Ideograph-6981 | 6980 CJK Ideograph-6980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6983 CJK Ideograph-6983 | 6982 CJK Ideograph-6982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6985 CJK Ideograph-6985 | 6984 CJK Ideograph-6984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6987 CJK Ideograph-6987 | 6986 CJK Ideograph-6986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6989 CJK Ideograph-6989 | 6988 CJK Ideograph-6988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698b CJK Ideograph-698B | 698a CJK Ideograph-698A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698d CJK Ideograph-698D | 698c CJK Ideograph-698C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 698f CJK Ideograph-698F | 698e CJK Ideograph-698E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6991 CJK Ideograph-6991 | 6990 CJK Ideograph-6990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6993 CJK Ideograph-6993 | 6992 CJK Ideograph-6992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6995 CJK Ideograph-6995 | 6994 CJK Ideograph-6994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6997 CJK Ideograph-6997 | 6996 CJK Ideograph-6996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6999 CJK Ideograph-6999 | 6998 CJK Ideograph-6998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699b CJK Ideograph-699B | 699a CJK Ideograph-699A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699d CJK Ideograph-699D | 699c CJK Ideograph-699C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 699f CJK Ideograph-699F | 699e CJK Ideograph-699E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a1 CJK Ideograph-69A1 | 69a0 CJK Ideograph-69A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a3 CJK Ideograph-69A3 | 69a2 CJK Ideograph-69A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a5 CJK Ideograph-69A5 | 69a4 CJK Ideograph-69A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a7 CJK Ideograph-69A7 | 69a6 CJK Ideograph-69A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69a9 CJK Ideograph-69A9 | 69a8 CJK Ideograph-69A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ab CJK Ideograph-69AB | 69aa CJK Ideograph-69AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ad CJK Ideograph-69AD | 69ac CJK Ideograph-69AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69af CJK Ideograph-69AF | 69ae CJK Ideograph-69AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b1 CJK Ideograph-69B1 | 69b0 CJK Ideograph-69B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b3 CJK Ideograph-69B3 | 69b2 CJK Ideograph-69B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b5 CJK Ideograph-69B5 | 69b4 CJK Ideograph-69B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b7 CJK Ideograph-69B7 | 69b6 CJK Ideograph-69B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69b9 CJK Ideograph-69B9 | 69b8 CJK Ideograph-69B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bb CJK Ideograph-69BB | 69ba CJK Ideograph-69BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bd CJK Ideograph-69BD | 69bc CJK Ideograph-69BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69bf CJK Ideograph-69BF | 69be CJK Ideograph-69BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c1 CJK Ideograph-69C1 | 69c0 CJK Ideograph-69C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c3 CJK Ideograph-69C3 | 69c2 CJK Ideograph-69C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c5 CJK Ideograph-69C5 | 69c4 CJK Ideograph-69C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c7 CJK Ideograph-69C7 | 69c6 CJK Ideograph-69C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69c9 CJK Ideograph-69C9 | 69c8 CJK Ideograph-69C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cb CJK Ideograph-69CB | 69ca CJK Ideograph-69CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cd CJK Ideograph-69CD | 69cc CJK Ideograph-69CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69cf CJK Ideograph-69CF | 69ce CJK Ideograph-69CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d1 CJK Ideograph-69D1 | 69d0 CJK Ideograph-69D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d3 CJK Ideograph-69D3 | 69d2 CJK Ideograph-69D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d5 CJK Ideograph-69D5 | 69d4 CJK Ideograph-69D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d7 CJK Ideograph-69D7 | 69d6 CJK Ideograph-69D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69d9 CJK Ideograph-69D9 | 69d8 CJK Ideograph-69D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69db CJK Ideograph-69DB | 69da CJK Ideograph-69DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69dd CJK Ideograph-69DD | 69dc CJK Ideograph-69DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69df CJK Ideograph-69DF | 69de CJK Ideograph-69DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e1 CJK Ideograph-69E1 | 69e0 CJK Ideograph-69E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e3 CJK Ideograph-69E3 | 69e2 CJK Ideograph-69E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e5 CJK Ideograph-69E5 | 69e4 CJK Ideograph-69E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e7 CJK Ideograph-69E7 | 69e6 CJK Ideograph-69E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69e9 CJK Ideograph-69E9 | 69e8 CJK Ideograph-69E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69eb CJK Ideograph-69EB | 69ea CJK Ideograph-69EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ed CJK Ideograph-69ED | 69ec CJK Ideograph-69EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ef CJK Ideograph-69EF | 69ee CJK Ideograph-69EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f1 CJK Ideograph-69F1 | 69f0 CJK Ideograph-69F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f3 CJK Ideograph-69F3 | 69f2 CJK Ideograph-69F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f5 CJK Ideograph-69F5 | 69f4 CJK Ideograph-69F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f7 CJK Ideograph-69F7 | 69f6 CJK Ideograph-69F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69f9 CJK Ideograph-69F9 | 69f8 CJK Ideograph-69F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69fb CJK Ideograph-69FB | 69fa CJK Ideograph-69FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69fd CJK Ideograph-69FD | 69fc CJK Ideograph-69FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 69ff CJK Ideograph-69FF | 69fe CJK Ideograph-69FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a01 CJK Ideograph-6A01 | 6a00 CJK Ideograph-6A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a03 CJK Ideograph-6A03 | 6a02 CJK Ideograph-6A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a05 CJK Ideograph-6A05 | 6a04 CJK Ideograph-6A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a07 CJK Ideograph-6A07 | 6a06 CJK Ideograph-6A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a09 CJK Ideograph-6A09 | 6a08 CJK Ideograph-6A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0b CJK Ideograph-6A0B | 6a0a CJK Ideograph-6A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0d CJK Ideograph-6A0D | 6a0c CJK Ideograph-6A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a0f CJK Ideograph-6A0F | 6a0e CJK Ideograph-6A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a11 CJK Ideograph-6A11 | 6a10 CJK Ideograph-6A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a13 CJK Ideograph-6A13 | 6a12 CJK Ideograph-6A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a15 CJK Ideograph-6A15 | 6a14 CJK Ideograph-6A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a17 CJK Ideograph-6A17 | 6a16 CJK Ideograph-6A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a19 CJK Ideograph-6A19 | 6a18 CJK Ideograph-6A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1b CJK Ideograph-6A1B | 6a1a CJK Ideograph-6A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1d CJK Ideograph-6A1D | 6a1c CJK Ideograph-6A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a1f CJK Ideograph-6A1F | 6a1e CJK Ideograph-6A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a21 CJK Ideograph-6A21 | 6a20 CJK Ideograph-6A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a23 CJK Ideograph-6A23 | 6a22 CJK Ideograph-6A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a25 CJK Ideograph-6A25 | 6a24 CJK Ideograph-6A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a27 CJK Ideograph-6A27 | 6a26 CJK Ideograph-6A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a29 CJK Ideograph-6A29 | 6a28 CJK Ideograph-6A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2b CJK Ideograph-6A2B | 6a2a CJK Ideograph-6A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2d CJK Ideograph-6A2D | 6a2c CJK Ideograph-6A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a2f CJK Ideograph-6A2F | 6a2e CJK Ideograph-6A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a31 CJK Ideograph-6A31 | 6a30 CJK Ideograph-6A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a33 CJK Ideograph-6A33 | 6a32 CJK Ideograph-6A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a35 CJK Ideograph-6A35 | 6a34 CJK Ideograph-6A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a37 CJK Ideograph-6A37 | 6a36 CJK Ideograph-6A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a39 CJK Ideograph-6A39 | 6a38 CJK Ideograph-6A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3b CJK Ideograph-6A3B | 6a3a CJK Ideograph-6A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3d CJK Ideograph-6A3D | 6a3c CJK Ideograph-6A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a3f CJK Ideograph-6A3F | 6a3e CJK Ideograph-6A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a41 CJK Ideograph-6A41 | 6a40 CJK Ideograph-6A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a43 CJK Ideograph-6A43 | 6a42 CJK Ideograph-6A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a45 CJK Ideograph-6A45 | 6a44 CJK Ideograph-6A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a47 CJK Ideograph-6A47 | 6a46 CJK Ideograph-6A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a49 CJK Ideograph-6A49 | 6a48 CJK Ideograph-6A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4b CJK Ideograph-6A4B | 6a4a CJK Ideograph-6A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4d CJK Ideograph-6A4D | 6a4c CJK Ideograph-6A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a4f CJK Ideograph-6A4F | 6a4e CJK Ideograph-6A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a51 CJK Ideograph-6A51 | 6a50 CJK Ideograph-6A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a53 CJK Ideograph-6A53 | 6a52 CJK Ideograph-6A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a55 CJK Ideograph-6A55 | 6a54 CJK Ideograph-6A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a57 CJK Ideograph-6A57 | 6a56 CJK Ideograph-6A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a59 CJK Ideograph-6A59 | 6a58 CJK Ideograph-6A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5b CJK Ideograph-6A5B | 6a5a CJK Ideograph-6A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5d CJK Ideograph-6A5D | 6a5c CJK Ideograph-6A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a5f CJK Ideograph-6A5F | 6a5e CJK Ideograph-6A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a61 CJK Ideograph-6A61 | 6a60 CJK Ideograph-6A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a63 CJK Ideograph-6A63 | 6a62 CJK Ideograph-6A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a65 CJK Ideograph-6A65 | 6a64 CJK Ideograph-6A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a67 CJK Ideograph-6A67 | 6a66 CJK Ideograph-6A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a69 CJK Ideograph-6A69 | 6a68 CJK Ideograph-6A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6b CJK Ideograph-6A6B | 6a6a CJK Ideograph-6A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6d CJK Ideograph-6A6D | 6a6c CJK Ideograph-6A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a6f CJK Ideograph-6A6F | 6a6e CJK Ideograph-6A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a71 CJK Ideograph-6A71 | 6a70 CJK Ideograph-6A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a73 CJK Ideograph-6A73 | 6a72 CJK Ideograph-6A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a75 CJK Ideograph-6A75 | 6a74 CJK Ideograph-6A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a77 CJK Ideograph-6A77 | 6a76 CJK Ideograph-6A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a79 CJK Ideograph-6A79 | 6a78 CJK Ideograph-6A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7b CJK Ideograph-6A7B | 6a7a CJK Ideograph-6A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7d CJK Ideograph-6A7D | 6a7c CJK Ideograph-6A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a7f CJK Ideograph-6A7F | 6a7e CJK Ideograph-6A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a81 CJK Ideograph-6A81 | 6a80 CJK Ideograph-6A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a83 CJK Ideograph-6A83 | 6a82 CJK Ideograph-6A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a85 CJK Ideograph-6A85 | 6a84 CJK Ideograph-6A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a87 CJK Ideograph-6A87 | 6a86 CJK Ideograph-6A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a89 CJK Ideograph-6A89 | 6a88 CJK Ideograph-6A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8b CJK Ideograph-6A8B | 6a8a CJK Ideograph-6A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8d CJK Ideograph-6A8D | 6a8c CJK Ideograph-6A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a8f CJK Ideograph-6A8F | 6a8e CJK Ideograph-6A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a91 CJK Ideograph-6A91 | 6a90 CJK Ideograph-6A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a93 CJK Ideograph-6A93 | 6a92 CJK Ideograph-6A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a95 CJK Ideograph-6A95 | 6a94 CJK Ideograph-6A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a97 CJK Ideograph-6A97 | 6a96 CJK Ideograph-6A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a99 CJK Ideograph-6A99 | 6a98 CJK Ideograph-6A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9b CJK Ideograph-6A9B | 6a9a CJK Ideograph-6A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9d CJK Ideograph-6A9D | 6a9c CJK Ideograph-6A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6a9f CJK Ideograph-6A9F | 6a9e CJK Ideograph-6A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa1 CJK Ideograph-6AA1 | 6aa0 CJK Ideograph-6AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa3 CJK Ideograph-6AA3 | 6aa2 CJK Ideograph-6AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa5 CJK Ideograph-6AA5 | 6aa4 CJK Ideograph-6AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa7 CJK Ideograph-6AA7 | 6aa6 CJK Ideograph-6AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aa9 CJK Ideograph-6AA9 | 6aa8 CJK Ideograph-6AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aab CJK Ideograph-6AAB | 6aaa CJK Ideograph-6AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aad CJK Ideograph-6AAD | 6aac CJK Ideograph-6AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aaf CJK Ideograph-6AAF | 6aae CJK Ideograph-6AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab1 CJK Ideograph-6AB1 | 6ab0 CJK Ideograph-6AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab3 CJK Ideograph-6AB3 | 6ab2 CJK Ideograph-6AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab5 CJK Ideograph-6AB5 | 6ab4 CJK Ideograph-6AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab7 CJK Ideograph-6AB7 | 6ab6 CJK Ideograph-6AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ab9 CJK Ideograph-6AB9 | 6ab8 CJK Ideograph-6AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abb CJK Ideograph-6ABB | 6aba CJK Ideograph-6ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abd CJK Ideograph-6ABD | 6abc CJK Ideograph-6ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6abf CJK Ideograph-6ABF | 6abe CJK Ideograph-6ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac1 CJK Ideograph-6AC1 | 6ac0 CJK Ideograph-6AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac3 CJK Ideograph-6AC3 | 6ac2 CJK Ideograph-6AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac5 CJK Ideograph-6AC5 | 6ac4 CJK Ideograph-6AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac7 CJK Ideograph-6AC7 | 6ac6 CJK Ideograph-6AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ac9 CJK Ideograph-6AC9 | 6ac8 CJK Ideograph-6AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acb CJK Ideograph-6ACB | 6aca CJK Ideograph-6ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acd CJK Ideograph-6ACD | 6acc CJK Ideograph-6ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6acf CJK Ideograph-6ACF | 6ace CJK Ideograph-6ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad1 CJK Ideograph-6AD1 | 6ad0 CJK Ideograph-6AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad3 CJK Ideograph-6AD3 | 6ad2 CJK Ideograph-6AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad5 CJK Ideograph-6AD5 | 6ad4 CJK Ideograph-6AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad7 CJK Ideograph-6AD7 | 6ad6 CJK Ideograph-6AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ad9 CJK Ideograph-6AD9 | 6ad8 CJK Ideograph-6AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6adb CJK Ideograph-6ADB | 6ada CJK Ideograph-6ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6add CJK Ideograph-6ADD | 6adc CJK Ideograph-6ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6adf CJK Ideograph-6ADF | 6ade CJK Ideograph-6ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae1 CJK Ideograph-6AE1 | 6ae0 CJK Ideograph-6AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae3 CJK Ideograph-6AE3 | 6ae2 CJK Ideograph-6AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae5 CJK Ideograph-6AE5 | 6ae4 CJK Ideograph-6AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae7 CJK Ideograph-6AE7 | 6ae6 CJK Ideograph-6AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ae9 CJK Ideograph-6AE9 | 6ae8 CJK Ideograph-6AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aeb CJK Ideograph-6AEB | 6aea CJK Ideograph-6AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aed CJK Ideograph-6AED | 6aec CJK Ideograph-6AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aef CJK Ideograph-6AEF | 6aee CJK Ideograph-6AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af1 CJK Ideograph-6AF1 | 6af0 CJK Ideograph-6AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af3 CJK Ideograph-6AF3 | 6af2 CJK Ideograph-6AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af5 CJK Ideograph-6AF5 | 6af4 CJK Ideograph-6AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af7 CJK Ideograph-6AF7 | 6af6 CJK Ideograph-6AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6af9 CJK Ideograph-6AF9 | 6af8 CJK Ideograph-6AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6afb CJK Ideograph-6AFB | 6afa CJK Ideograph-6AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6afd CJK Ideograph-6AFD | 6afc CJK Ideograph-6AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6aff CJK Ideograph-6AFF | 6afe CJK Ideograph-6AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b01 CJK Ideograph-6B01 | 6b00 CJK Ideograph-6B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b03 CJK Ideograph-6B03 | 6b02 CJK Ideograph-6B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b05 CJK Ideograph-6B05 | 6b04 CJK Ideograph-6B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b07 CJK Ideograph-6B07 | 6b06 CJK Ideograph-6B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b09 CJK Ideograph-6B09 | 6b08 CJK Ideograph-6B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0b CJK Ideograph-6B0B | 6b0a CJK Ideograph-6B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0d CJK Ideograph-6B0D | 6b0c CJK Ideograph-6B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b0f CJK Ideograph-6B0F | 6b0e CJK Ideograph-6B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b11 CJK Ideograph-6B11 | 6b10 CJK Ideograph-6B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b13 CJK Ideograph-6B13 | 6b12 CJK Ideograph-6B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b15 CJK Ideograph-6B15 | 6b14 CJK Ideograph-6B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b17 CJK Ideograph-6B17 | 6b16 CJK Ideograph-6B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b19 CJK Ideograph-6B19 | 6b18 CJK Ideograph-6B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1b CJK Ideograph-6B1B | 6b1a CJK Ideograph-6B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1d CJK Ideograph-6B1D | 6b1c CJK Ideograph-6B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b1f CJK Ideograph-6B1F | 6b1e CJK Ideograph-6B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b21 CJK Ideograph-6B21 | 6b20 CJK Ideograph-6B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b23 CJK Ideograph-6B23 | 6b22 CJK Ideograph-6B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b25 CJK Ideograph-6B25 | 6b24 CJK Ideograph-6B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b27 CJK Ideograph-6B27 | 6b26 CJK Ideograph-6B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b29 CJK Ideograph-6B29 | 6b28 CJK Ideograph-6B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2b CJK Ideograph-6B2B | 6b2a CJK Ideograph-6B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2d CJK Ideograph-6B2D | 6b2c CJK Ideograph-6B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b2f CJK Ideograph-6B2F | 6b2e CJK Ideograph-6B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b31 CJK Ideograph-6B31 | 6b30 CJK Ideograph-6B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b33 CJK Ideograph-6B33 | 6b32 CJK Ideograph-6B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b35 CJK Ideograph-6B35 | 6b34 CJK Ideograph-6B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b37 CJK Ideograph-6B37 | 6b36 CJK Ideograph-6B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b39 CJK Ideograph-6B39 | 6b38 CJK Ideograph-6B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3b CJK Ideograph-6B3B | 6b3a CJK Ideograph-6B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3d CJK Ideograph-6B3D | 6b3c CJK Ideograph-6B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b3f CJK Ideograph-6B3F | 6b3e CJK Ideograph-6B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b41 CJK Ideograph-6B41 | 6b40 CJK Ideograph-6B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b43 CJK Ideograph-6B43 | 6b42 CJK Ideograph-6B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b45 CJK Ideograph-6B45 | 6b44 CJK Ideograph-6B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b47 CJK Ideograph-6B47 | 6b46 CJK Ideograph-6B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b49 CJK Ideograph-6B49 | 6b48 CJK Ideograph-6B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4b CJK Ideograph-6B4B | 6b4a CJK Ideograph-6B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4d CJK Ideograph-6B4D | 6b4c CJK Ideograph-6B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b4f CJK Ideograph-6B4F | 6b4e CJK Ideograph-6B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b51 CJK Ideograph-6B51 | 6b50 CJK Ideograph-6B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b53 CJK Ideograph-6B53 | 6b52 CJK Ideograph-6B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b55 CJK Ideograph-6B55 | 6b54 CJK Ideograph-6B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b57 CJK Ideograph-6B57 | 6b56 CJK Ideograph-6B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b59 CJK Ideograph-6B59 | 6b58 CJK Ideograph-6B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5b CJK Ideograph-6B5B | 6b5a CJK Ideograph-6B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5d CJK Ideograph-6B5D | 6b5c CJK Ideograph-6B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b5f CJK Ideograph-6B5F | 6b5e CJK Ideograph-6B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b61 CJK Ideograph-6B61 | 6b60 CJK Ideograph-6B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b63 CJK Ideograph-6B63 | 6b62 CJK Ideograph-6B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b65 CJK Ideograph-6B65 | 6b64 CJK Ideograph-6B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b67 CJK Ideograph-6B67 | 6b66 CJK Ideograph-6B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b69 CJK Ideograph-6B69 | 6b68 CJK Ideograph-6B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6b CJK Ideograph-6B6B | 6b6a CJK Ideograph-6B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6d CJK Ideograph-6B6D | 6b6c CJK Ideograph-6B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b6f CJK Ideograph-6B6F | 6b6e CJK Ideograph-6B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b71 CJK Ideograph-6B71 | 6b70 CJK Ideograph-6B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b73 CJK Ideograph-6B73 | 6b72 CJK Ideograph-6B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b75 CJK Ideograph-6B75 | 6b74 CJK Ideograph-6B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b77 CJK Ideograph-6B77 | 6b76 CJK Ideograph-6B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b79 CJK Ideograph-6B79 | 6b78 CJK Ideograph-6B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7b CJK Ideograph-6B7B | 6b7a CJK Ideograph-6B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7d CJK Ideograph-6B7D | 6b7c CJK Ideograph-6B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b7f CJK Ideograph-6B7F | 6b7e CJK Ideograph-6B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b81 CJK Ideograph-6B81 | 6b80 CJK Ideograph-6B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b83 CJK Ideograph-6B83 | 6b82 CJK Ideograph-6B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b85 CJK Ideograph-6B85 | 6b84 CJK Ideograph-6B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b87 CJK Ideograph-6B87 | 6b86 CJK Ideograph-6B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b89 CJK Ideograph-6B89 | 6b88 CJK Ideograph-6B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8b CJK Ideograph-6B8B | 6b8a CJK Ideograph-6B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8d CJK Ideograph-6B8D | 6b8c CJK Ideograph-6B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b8f CJK Ideograph-6B8F | 6b8e CJK Ideograph-6B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b91 CJK Ideograph-6B91 | 6b90 CJK Ideograph-6B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b93 CJK Ideograph-6B93 | 6b92 CJK Ideograph-6B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b95 CJK Ideograph-6B95 | 6b94 CJK Ideograph-6B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b97 CJK Ideograph-6B97 | 6b96 CJK Ideograph-6B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b99 CJK Ideograph-6B99 | 6b98 CJK Ideograph-6B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9b CJK Ideograph-6B9B | 6b9a CJK Ideograph-6B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9d CJK Ideograph-6B9D | 6b9c CJK Ideograph-6B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6b9f CJK Ideograph-6B9F | 6b9e CJK Ideograph-6B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba1 CJK Ideograph-6BA1 | 6ba0 CJK Ideograph-6BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba3 CJK Ideograph-6BA3 | 6ba2 CJK Ideograph-6BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba5 CJK Ideograph-6BA5 | 6ba4 CJK Ideograph-6BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba7 CJK Ideograph-6BA7 | 6ba6 CJK Ideograph-6BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ba9 CJK Ideograph-6BA9 | 6ba8 CJK Ideograph-6BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bab CJK Ideograph-6BAB | 6baa CJK Ideograph-6BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bad CJK Ideograph-6BAD | 6bac CJK Ideograph-6BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6baf CJK Ideograph-6BAF | 6bae CJK Ideograph-6BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb1 CJK Ideograph-6BB1 | 6bb0 CJK Ideograph-6BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb3 CJK Ideograph-6BB3 | 6bb2 CJK Ideograph-6BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb5 CJK Ideograph-6BB5 | 6bb4 CJK Ideograph-6BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb7 CJK Ideograph-6BB7 | 6bb6 CJK Ideograph-6BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bb9 CJK Ideograph-6BB9 | 6bb8 CJK Ideograph-6BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbb CJK Ideograph-6BBB | 6bba CJK Ideograph-6BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbd CJK Ideograph-6BBD | 6bbc CJK Ideograph-6BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bbf CJK Ideograph-6BBF | 6bbe CJK Ideograph-6BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc1 CJK Ideograph-6BC1 | 6bc0 CJK Ideograph-6BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc3 CJK Ideograph-6BC3 | 6bc2 CJK Ideograph-6BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc5 CJK Ideograph-6BC5 | 6bc4 CJK Ideograph-6BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc7 CJK Ideograph-6BC7 | 6bc6 CJK Ideograph-6BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bc9 CJK Ideograph-6BC9 | 6bc8 CJK Ideograph-6BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcb CJK Ideograph-6BCB | 6bca CJK Ideograph-6BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcd CJK Ideograph-6BCD | 6bcc CJK Ideograph-6BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bcf CJK Ideograph-6BCF | 6bce CJK Ideograph-6BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd1 CJK Ideograph-6BD1 | 6bd0 CJK Ideograph-6BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd3 CJK Ideograph-6BD3 | 6bd2 CJK Ideograph-6BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd5 CJK Ideograph-6BD5 | 6bd4 CJK Ideograph-6BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd7 CJK Ideograph-6BD7 | 6bd6 CJK Ideograph-6BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bd9 CJK Ideograph-6BD9 | 6bd8 CJK Ideograph-6BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdb CJK Ideograph-6BDB | 6bda CJK Ideograph-6BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdd CJK Ideograph-6BDD | 6bdc CJK Ideograph-6BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bdf CJK Ideograph-6BDF | 6bde CJK Ideograph-6BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be1 CJK Ideograph-6BE1 | 6be0 CJK Ideograph-6BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be3 CJK Ideograph-6BE3 | 6be2 CJK Ideograph-6BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be5 CJK Ideograph-6BE5 | 6be4 CJK Ideograph-6BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be7 CJK Ideograph-6BE7 | 6be6 CJK Ideograph-6BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6be9 CJK Ideograph-6BE9 | 6be8 CJK Ideograph-6BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6beb CJK Ideograph-6BEB | 6bea CJK Ideograph-6BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bed CJK Ideograph-6BED | 6bec CJK Ideograph-6BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bef CJK Ideograph-6BEF | 6bee CJK Ideograph-6BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf1 CJK Ideograph-6BF1 | 6bf0 CJK Ideograph-6BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf3 CJK Ideograph-6BF3 | 6bf2 CJK Ideograph-6BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf5 CJK Ideograph-6BF5 | 6bf4 CJK Ideograph-6BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf7 CJK Ideograph-6BF7 | 6bf6 CJK Ideograph-6BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bf9 CJK Ideograph-6BF9 | 6bf8 CJK Ideograph-6BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bfb CJK Ideograph-6BFB | 6bfa CJK Ideograph-6BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bfd CJK Ideograph-6BFD | 6bfc CJK Ideograph-6BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6bff CJK Ideograph-6BFF | 6bfe CJK Ideograph-6BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c01 CJK Ideograph-6C01 | 6c00 CJK Ideograph-6C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c03 CJK Ideograph-6C03 | 6c02 CJK Ideograph-6C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c05 CJK Ideograph-6C05 | 6c04 CJK Ideograph-6C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c07 CJK Ideograph-6C07 | 6c06 CJK Ideograph-6C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c09 CJK Ideograph-6C09 | 6c08 CJK Ideograph-6C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0b CJK Ideograph-6C0B | 6c0a CJK Ideograph-6C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0d CJK Ideograph-6C0D | 6c0c CJK Ideograph-6C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c0f CJK Ideograph-6C0F | 6c0e CJK Ideograph-6C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c11 CJK Ideograph-6C11 | 6c10 CJK Ideograph-6C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c13 CJK Ideograph-6C13 | 6c12 CJK Ideograph-6C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c15 CJK Ideograph-6C15 | 6c14 CJK Ideograph-6C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c17 CJK Ideograph-6C17 | 6c16 CJK Ideograph-6C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c19 CJK Ideograph-6C19 | 6c18 CJK Ideograph-6C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1b CJK Ideograph-6C1B | 6c1a CJK Ideograph-6C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1d CJK Ideograph-6C1D | 6c1c CJK Ideograph-6C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c1f CJK Ideograph-6C1F | 6c1e CJK Ideograph-6C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c21 CJK Ideograph-6C21 | 6c20 CJK Ideograph-6C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c23 CJK Ideograph-6C23 | 6c22 CJK Ideograph-6C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c25 CJK Ideograph-6C25 | 6c24 CJK Ideograph-6C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c27 CJK Ideograph-6C27 | 6c26 CJK Ideograph-6C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c29 CJK Ideograph-6C29 | 6c28 CJK Ideograph-6C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2b CJK Ideograph-6C2B | 6c2a CJK Ideograph-6C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2d CJK Ideograph-6C2D | 6c2c CJK Ideograph-6C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c2f CJK Ideograph-6C2F | 6c2e CJK Ideograph-6C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c31 CJK Ideograph-6C31 | 6c30 CJK Ideograph-6C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c33 CJK Ideograph-6C33 | 6c32 CJK Ideograph-6C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c35 CJK Ideograph-6C35 | 6c34 CJK Ideograph-6C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c37 CJK Ideograph-6C37 | 6c36 CJK Ideograph-6C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c39 CJK Ideograph-6C39 | 6c38 CJK Ideograph-6C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3b CJK Ideograph-6C3B | 6c3a CJK Ideograph-6C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3d CJK Ideograph-6C3D | 6c3c CJK Ideograph-6C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c3f CJK Ideograph-6C3F | 6c3e CJK Ideograph-6C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c41 CJK Ideograph-6C41 | 6c40 CJK Ideograph-6C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c43 CJK Ideograph-6C43 | 6c42 CJK Ideograph-6C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c45 CJK Ideograph-6C45 | 6c44 CJK Ideograph-6C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c47 CJK Ideograph-6C47 | 6c46 CJK Ideograph-6C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c49 CJK Ideograph-6C49 | 6c48 CJK Ideograph-6C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4b CJK Ideograph-6C4B | 6c4a CJK Ideograph-6C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4d CJK Ideograph-6C4D | 6c4c CJK Ideograph-6C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c4f CJK Ideograph-6C4F | 6c4e CJK Ideograph-6C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c51 CJK Ideograph-6C51 | 6c50 CJK Ideograph-6C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c53 CJK Ideograph-6C53 | 6c52 CJK Ideograph-6C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c55 CJK Ideograph-6C55 | 6c54 CJK Ideograph-6C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c57 CJK Ideograph-6C57 | 6c56 CJK Ideograph-6C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c59 CJK Ideograph-6C59 | 6c58 CJK Ideograph-6C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5b CJK Ideograph-6C5B | 6c5a CJK Ideograph-6C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5d CJK Ideograph-6C5D | 6c5c CJK Ideograph-6C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c5f CJK Ideograph-6C5F | 6c5e CJK Ideograph-6C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c61 CJK Ideograph-6C61 | 6c60 CJK Ideograph-6C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c63 CJK Ideograph-6C63 | 6c62 CJK Ideograph-6C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c65 CJK Ideograph-6C65 | 6c64 CJK Ideograph-6C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c67 CJK Ideograph-6C67 | 6c66 CJK Ideograph-6C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c69 CJK Ideograph-6C69 | 6c68 CJK Ideograph-6C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6b CJK Ideograph-6C6B | 6c6a CJK Ideograph-6C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6d CJK Ideograph-6C6D | 6c6c CJK Ideograph-6C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c6f CJK Ideograph-6C6F | 6c6e CJK Ideograph-6C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c71 CJK Ideograph-6C71 | 6c70 CJK Ideograph-6C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c73 CJK Ideograph-6C73 | 6c72 CJK Ideograph-6C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c75 CJK Ideograph-6C75 | 6c74 CJK Ideograph-6C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c77 CJK Ideograph-6C77 | 6c76 CJK Ideograph-6C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c79 CJK Ideograph-6C79 | 6c78 CJK Ideograph-6C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7b CJK Ideograph-6C7B | 6c7a CJK Ideograph-6C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7d CJK Ideograph-6C7D | 6c7c CJK Ideograph-6C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c7f CJK Ideograph-6C7F | 6c7e CJK Ideograph-6C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c81 CJK Ideograph-6C81 | 6c80 CJK Ideograph-6C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c83 CJK Ideograph-6C83 | 6c82 CJK Ideograph-6C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c85 CJK Ideograph-6C85 | 6c84 CJK Ideograph-6C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c87 CJK Ideograph-6C87 | 6c86 CJK Ideograph-6C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c89 CJK Ideograph-6C89 | 6c88 CJK Ideograph-6C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8b CJK Ideograph-6C8B | 6c8a CJK Ideograph-6C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8d CJK Ideograph-6C8D | 6c8c CJK Ideograph-6C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c8f CJK Ideograph-6C8F | 6c8e CJK Ideograph-6C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c91 CJK Ideograph-6C91 | 6c90 CJK Ideograph-6C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c93 CJK Ideograph-6C93 | 6c92 CJK Ideograph-6C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c95 CJK Ideograph-6C95 | 6c94 CJK Ideograph-6C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c97 CJK Ideograph-6C97 | 6c96 CJK Ideograph-6C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c99 CJK Ideograph-6C99 | 6c98 CJK Ideograph-6C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9b CJK Ideograph-6C9B | 6c9a CJK Ideograph-6C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9d CJK Ideograph-6C9D | 6c9c CJK Ideograph-6C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6c9f CJK Ideograph-6C9F | 6c9e CJK Ideograph-6C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca1 CJK Ideograph-6CA1 | 6ca0 CJK Ideograph-6CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca3 CJK Ideograph-6CA3 | 6ca2 CJK Ideograph-6CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca5 CJK Ideograph-6CA5 | 6ca4 CJK Ideograph-6CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca7 CJK Ideograph-6CA7 | 6ca6 CJK Ideograph-6CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ca9 CJK Ideograph-6CA9 | 6ca8 CJK Ideograph-6CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cab CJK Ideograph-6CAB | 6caa CJK Ideograph-6CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cad CJK Ideograph-6CAD | 6cac CJK Ideograph-6CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6caf CJK Ideograph-6CAF | 6cae CJK Ideograph-6CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb1 CJK Ideograph-6CB1 | 6cb0 CJK Ideograph-6CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb3 CJK Ideograph-6CB3 | 6cb2 CJK Ideograph-6CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb5 CJK Ideograph-6CB5 | 6cb4 CJK Ideograph-6CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb7 CJK Ideograph-6CB7 | 6cb6 CJK Ideograph-6CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cb9 CJK Ideograph-6CB9 | 6cb8 CJK Ideograph-6CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbb CJK Ideograph-6CBB | 6cba CJK Ideograph-6CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbd CJK Ideograph-6CBD | 6cbc CJK Ideograph-6CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cbf CJK Ideograph-6CBF | 6cbe CJK Ideograph-6CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc1 CJK Ideograph-6CC1 | 6cc0 CJK Ideograph-6CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc3 CJK Ideograph-6CC3 | 6cc2 CJK Ideograph-6CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc5 CJK Ideograph-6CC5 | 6cc4 CJK Ideograph-6CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc7 CJK Ideograph-6CC7 | 6cc6 CJK Ideograph-6CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cc9 CJK Ideograph-6CC9 | 6cc8 CJK Ideograph-6CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccb CJK Ideograph-6CCB | 6cca CJK Ideograph-6CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccd CJK Ideograph-6CCD | 6ccc CJK Ideograph-6CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ccf CJK Ideograph-6CCF | 6cce CJK Ideograph-6CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd1 CJK Ideograph-6CD1 | 6cd0 CJK Ideograph-6CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd3 CJK Ideograph-6CD3 | 6cd2 CJK Ideograph-6CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd5 CJK Ideograph-6CD5 | 6cd4 CJK Ideograph-6CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd7 CJK Ideograph-6CD7 | 6cd6 CJK Ideograph-6CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cd9 CJK Ideograph-6CD9 | 6cd8 CJK Ideograph-6CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdb CJK Ideograph-6CDB | 6cda CJK Ideograph-6CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdd CJK Ideograph-6CDD | 6cdc CJK Ideograph-6CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cdf CJK Ideograph-6CDF | 6cde CJK Ideograph-6CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce1 CJK Ideograph-6CE1 | 6ce0 CJK Ideograph-6CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce3 CJK Ideograph-6CE3 | 6ce2 CJK Ideograph-6CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce5 CJK Ideograph-6CE5 | 6ce4 CJK Ideograph-6CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce7 CJK Ideograph-6CE7 | 6ce6 CJK Ideograph-6CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ce9 CJK Ideograph-6CE9 | 6ce8 CJK Ideograph-6CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ceb CJK Ideograph-6CEB | 6cea CJK Ideograph-6CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ced CJK Ideograph-6CED | 6cec CJK Ideograph-6CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cef CJK Ideograph-6CEF | 6cee CJK Ideograph-6CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf1 CJK Ideograph-6CF1 | 6cf0 CJK Ideograph-6CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf3 CJK Ideograph-6CF3 | 6cf2 CJK Ideograph-6CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf5 CJK Ideograph-6CF5 | 6cf4 CJK Ideograph-6CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf7 CJK Ideograph-6CF7 | 6cf6 CJK Ideograph-6CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cf9 CJK Ideograph-6CF9 | 6cf8 CJK Ideograph-6CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cfb CJK Ideograph-6CFB | 6cfa CJK Ideograph-6CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cfd CJK Ideograph-6CFD | 6cfc CJK Ideograph-6CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6cff CJK Ideograph-6CFF | 6cfe CJK Ideograph-6CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d01 CJK Ideograph-6D01 | 6d00 CJK Ideograph-6D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d03 CJK Ideograph-6D03 | 6d02 CJK Ideograph-6D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d05 CJK Ideograph-6D05 | 6d04 CJK Ideograph-6D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d07 CJK Ideograph-6D07 | 6d06 CJK Ideograph-6D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d09 CJK Ideograph-6D09 | 6d08 CJK Ideograph-6D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0b CJK Ideograph-6D0B | 6d0a CJK Ideograph-6D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0d CJK Ideograph-6D0D | 6d0c CJK Ideograph-6D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d0f CJK Ideograph-6D0F | 6d0e CJK Ideograph-6D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d11 CJK Ideograph-6D11 | 6d10 CJK Ideograph-6D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d13 CJK Ideograph-6D13 | 6d12 CJK Ideograph-6D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d15 CJK Ideograph-6D15 | 6d14 CJK Ideograph-6D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d17 CJK Ideograph-6D17 | 6d16 CJK Ideograph-6D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d19 CJK Ideograph-6D19 | 6d18 CJK Ideograph-6D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1b CJK Ideograph-6D1B | 6d1a CJK Ideograph-6D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1d CJK Ideograph-6D1D | 6d1c CJK Ideograph-6D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d1f CJK Ideograph-6D1F | 6d1e CJK Ideograph-6D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d21 CJK Ideograph-6D21 | 6d20 CJK Ideograph-6D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d23 CJK Ideograph-6D23 | 6d22 CJK Ideograph-6D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d25 CJK Ideograph-6D25 | 6d24 CJK Ideograph-6D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d27 CJK Ideograph-6D27 | 6d26 CJK Ideograph-6D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d29 CJK Ideograph-6D29 | 6d28 CJK Ideograph-6D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2b CJK Ideograph-6D2B | 6d2a CJK Ideograph-6D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2d CJK Ideograph-6D2D | 6d2c CJK Ideograph-6D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d2f CJK Ideograph-6D2F | 6d2e CJK Ideograph-6D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d31 CJK Ideograph-6D31 | 6d30 CJK Ideograph-6D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d33 CJK Ideograph-6D33 | 6d32 CJK Ideograph-6D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d35 CJK Ideograph-6D35 | 6d34 CJK Ideograph-6D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d37 CJK Ideograph-6D37 | 6d36 CJK Ideograph-6D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d39 CJK Ideograph-6D39 | 6d38 CJK Ideograph-6D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3b CJK Ideograph-6D3B | 6d3a CJK Ideograph-6D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3d CJK Ideograph-6D3D | 6d3c CJK Ideograph-6D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d3f CJK Ideograph-6D3F | 6d3e CJK Ideograph-6D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d41 CJK Ideograph-6D41 | 6d40 CJK Ideograph-6D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d43 CJK Ideograph-6D43 | 6d42 CJK Ideograph-6D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d45 CJK Ideograph-6D45 | 6d44 CJK Ideograph-6D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d47 CJK Ideograph-6D47 | 6d46 CJK Ideograph-6D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d49 CJK Ideograph-6D49 | 6d48 CJK Ideograph-6D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4b CJK Ideograph-6D4B | 6d4a CJK Ideograph-6D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4d CJK Ideograph-6D4D | 6d4c CJK Ideograph-6D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d4f CJK Ideograph-6D4F | 6d4e CJK Ideograph-6D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d51 CJK Ideograph-6D51 | 6d50 CJK Ideograph-6D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d53 CJK Ideograph-6D53 | 6d52 CJK Ideograph-6D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d55 CJK Ideograph-6D55 | 6d54 CJK Ideograph-6D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d57 CJK Ideograph-6D57 | 6d56 CJK Ideograph-6D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d59 CJK Ideograph-6D59 | 6d58 CJK Ideograph-6D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5b CJK Ideograph-6D5B | 6d5a CJK Ideograph-6D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5d CJK Ideograph-6D5D | 6d5c CJK Ideograph-6D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d5f CJK Ideograph-6D5F | 6d5e CJK Ideograph-6D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d61 CJK Ideograph-6D61 | 6d60 CJK Ideograph-6D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d63 CJK Ideograph-6D63 | 6d62 CJK Ideograph-6D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d65 CJK Ideograph-6D65 | 6d64 CJK Ideograph-6D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d67 CJK Ideograph-6D67 | 6d66 CJK Ideograph-6D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d69 CJK Ideograph-6D69 | 6d68 CJK Ideograph-6D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6b CJK Ideograph-6D6B | 6d6a CJK Ideograph-6D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6d CJK Ideograph-6D6D | 6d6c CJK Ideograph-6D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d6f CJK Ideograph-6D6F | 6d6e CJK Ideograph-6D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d71 CJK Ideograph-6D71 | 6d70 CJK Ideograph-6D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d73 CJK Ideograph-6D73 | 6d72 CJK Ideograph-6D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d75 CJK Ideograph-6D75 | 6d74 CJK Ideograph-6D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d77 CJK Ideograph-6D77 | 6d76 CJK Ideograph-6D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d79 CJK Ideograph-6D79 | 6d78 CJK Ideograph-6D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7b CJK Ideograph-6D7B | 6d7a CJK Ideograph-6D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7d CJK Ideograph-6D7D | 6d7c CJK Ideograph-6D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d7f CJK Ideograph-6D7F | 6d7e CJK Ideograph-6D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d81 CJK Ideograph-6D81 | 6d80 CJK Ideograph-6D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d83 CJK Ideograph-6D83 | 6d82 CJK Ideograph-6D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d85 CJK Ideograph-6D85 | 6d84 CJK Ideograph-6D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d87 CJK Ideograph-6D87 | 6d86 CJK Ideograph-6D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d89 CJK Ideograph-6D89 | 6d88 CJK Ideograph-6D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8b CJK Ideograph-6D8B | 6d8a CJK Ideograph-6D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8d CJK Ideograph-6D8D | 6d8c CJK Ideograph-6D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d8f CJK Ideograph-6D8F | 6d8e CJK Ideograph-6D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d91 CJK Ideograph-6D91 | 6d90 CJK Ideograph-6D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d93 CJK Ideograph-6D93 | 6d92 CJK Ideograph-6D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d95 CJK Ideograph-6D95 | 6d94 CJK Ideograph-6D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d97 CJK Ideograph-6D97 | 6d96 CJK Ideograph-6D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d99 CJK Ideograph-6D99 | 6d98 CJK Ideograph-6D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9b CJK Ideograph-6D9B | 6d9a CJK Ideograph-6D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9d CJK Ideograph-6D9D | 6d9c CJK Ideograph-6D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6d9f CJK Ideograph-6D9F | 6d9e CJK Ideograph-6D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da1 CJK Ideograph-6DA1 | 6da0 CJK Ideograph-6DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da3 CJK Ideograph-6DA3 | 6da2 CJK Ideograph-6DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da5 CJK Ideograph-6DA5 | 6da4 CJK Ideograph-6DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da7 CJK Ideograph-6DA7 | 6da6 CJK Ideograph-6DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6da9 CJK Ideograph-6DA9 | 6da8 CJK Ideograph-6DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dab CJK Ideograph-6DAB | 6daa CJK Ideograph-6DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dad CJK Ideograph-6DAD | 6dac CJK Ideograph-6DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6daf CJK Ideograph-6DAF | 6dae CJK Ideograph-6DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db1 CJK Ideograph-6DB1 | 6db0 CJK Ideograph-6DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db3 CJK Ideograph-6DB3 | 6db2 CJK Ideograph-6DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db5 CJK Ideograph-6DB5 | 6db4 CJK Ideograph-6DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db7 CJK Ideograph-6DB7 | 6db6 CJK Ideograph-6DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6db9 CJK Ideograph-6DB9 | 6db8 CJK Ideograph-6DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbb CJK Ideograph-6DBB | 6dba CJK Ideograph-6DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbd CJK Ideograph-6DBD | 6dbc CJK Ideograph-6DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dbf CJK Ideograph-6DBF | 6dbe CJK Ideograph-6DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc1 CJK Ideograph-6DC1 | 6dc0 CJK Ideograph-6DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc3 CJK Ideograph-6DC3 | 6dc2 CJK Ideograph-6DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc5 CJK Ideograph-6DC5 | 6dc4 CJK Ideograph-6DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc7 CJK Ideograph-6DC7 | 6dc6 CJK Ideograph-6DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dc9 CJK Ideograph-6DC9 | 6dc8 CJK Ideograph-6DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcb CJK Ideograph-6DCB | 6dca CJK Ideograph-6DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcd CJK Ideograph-6DCD | 6dcc CJK Ideograph-6DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dcf CJK Ideograph-6DCF | 6dce CJK Ideograph-6DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd1 CJK Ideograph-6DD1 | 6dd0 CJK Ideograph-6DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd3 CJK Ideograph-6DD3 | 6dd2 CJK Ideograph-6DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd5 CJK Ideograph-6DD5 | 6dd4 CJK Ideograph-6DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd7 CJK Ideograph-6DD7 | 6dd6 CJK Ideograph-6DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dd9 CJK Ideograph-6DD9 | 6dd8 CJK Ideograph-6DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddb CJK Ideograph-6DDB | 6dda CJK Ideograph-6DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddd CJK Ideograph-6DDD | 6ddc CJK Ideograph-6DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ddf CJK Ideograph-6DDF | 6dde CJK Ideograph-6DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de1 CJK Ideograph-6DE1 | 6de0 CJK Ideograph-6DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de3 CJK Ideograph-6DE3 | 6de2 CJK Ideograph-6DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de5 CJK Ideograph-6DE5 | 6de4 CJK Ideograph-6DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de7 CJK Ideograph-6DE7 | 6de6 CJK Ideograph-6DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6de9 CJK Ideograph-6DE9 | 6de8 CJK Ideograph-6DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6deb CJK Ideograph-6DEB | 6dea CJK Ideograph-6DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ded CJK Ideograph-6DED | 6dec CJK Ideograph-6DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6def CJK Ideograph-6DEF | 6dee CJK Ideograph-6DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df1 CJK Ideograph-6DF1 | 6df0 CJK Ideograph-6DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df3 CJK Ideograph-6DF3 | 6df2 CJK Ideograph-6DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df5 CJK Ideograph-6DF5 | 6df4 CJK Ideograph-6DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df7 CJK Ideograph-6DF7 | 6df6 CJK Ideograph-6DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6df9 CJK Ideograph-6DF9 | 6df8 CJK Ideograph-6DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dfb CJK Ideograph-6DFB | 6dfa CJK Ideograph-6DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dfd CJK Ideograph-6DFD | 6dfc CJK Ideograph-6DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6dff CJK Ideograph-6DFF | 6dfe CJK Ideograph-6DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e01 CJK Ideograph-6E01 | 6e00 CJK Ideograph-6E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e03 CJK Ideograph-6E03 | 6e02 CJK Ideograph-6E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e05 CJK Ideograph-6E05 | 6e04 CJK Ideograph-6E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e07 CJK Ideograph-6E07 | 6e06 CJK Ideograph-6E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e09 CJK Ideograph-6E09 | 6e08 CJK Ideograph-6E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0b CJK Ideograph-6E0B | 6e0a CJK Ideograph-6E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0d CJK Ideograph-6E0D | 6e0c CJK Ideograph-6E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e0f CJK Ideograph-6E0F | 6e0e CJK Ideograph-6E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e11 CJK Ideograph-6E11 | 6e10 CJK Ideograph-6E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e13 CJK Ideograph-6E13 | 6e12 CJK Ideograph-6E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e15 CJK Ideograph-6E15 | 6e14 CJK Ideograph-6E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e17 CJK Ideograph-6E17 | 6e16 CJK Ideograph-6E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e19 CJK Ideograph-6E19 | 6e18 CJK Ideograph-6E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1b CJK Ideograph-6E1B | 6e1a CJK Ideograph-6E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1d CJK Ideograph-6E1D | 6e1c CJK Ideograph-6E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e1f CJK Ideograph-6E1F | 6e1e CJK Ideograph-6E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e21 CJK Ideograph-6E21 | 6e20 CJK Ideograph-6E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e23 CJK Ideograph-6E23 | 6e22 CJK Ideograph-6E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e25 CJK Ideograph-6E25 | 6e24 CJK Ideograph-6E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e27 CJK Ideograph-6E27 | 6e26 CJK Ideograph-6E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e29 CJK Ideograph-6E29 | 6e28 CJK Ideograph-6E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2b CJK Ideograph-6E2B | 6e2a CJK Ideograph-6E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2d CJK Ideograph-6E2D | 6e2c CJK Ideograph-6E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e2f CJK Ideograph-6E2F | 6e2e CJK Ideograph-6E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e31 CJK Ideograph-6E31 | 6e30 CJK Ideograph-6E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e33 CJK Ideograph-6E33 | 6e32 CJK Ideograph-6E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e35 CJK Ideograph-6E35 | 6e34 CJK Ideograph-6E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e37 CJK Ideograph-6E37 | 6e36 CJK Ideograph-6E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e39 CJK Ideograph-6E39 | 6e38 CJK Ideograph-6E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3b CJK Ideograph-6E3B | 6e3a CJK Ideograph-6E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3d CJK Ideograph-6E3D | 6e3c CJK Ideograph-6E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e3f CJK Ideograph-6E3F | 6e3e CJK Ideograph-6E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e41 CJK Ideograph-6E41 | 6e40 CJK Ideograph-6E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e43 CJK Ideograph-6E43 | 6e42 CJK Ideograph-6E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e45 CJK Ideograph-6E45 | 6e44 CJK Ideograph-6E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e47 CJK Ideograph-6E47 | 6e46 CJK Ideograph-6E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e49 CJK Ideograph-6E49 | 6e48 CJK Ideograph-6E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4b CJK Ideograph-6E4B | 6e4a CJK Ideograph-6E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4d CJK Ideograph-6E4D | 6e4c CJK Ideograph-6E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e4f CJK Ideograph-6E4F | 6e4e CJK Ideograph-6E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e51 CJK Ideograph-6E51 | 6e50 CJK Ideograph-6E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e53 CJK Ideograph-6E53 | 6e52 CJK Ideograph-6E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e55 CJK Ideograph-6E55 | 6e54 CJK Ideograph-6E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e57 CJK Ideograph-6E57 | 6e56 CJK Ideograph-6E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e59 CJK Ideograph-6E59 | 6e58 CJK Ideograph-6E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5b CJK Ideograph-6E5B | 6e5a CJK Ideograph-6E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5d CJK Ideograph-6E5D | 6e5c CJK Ideograph-6E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e5f CJK Ideograph-6E5F | 6e5e CJK Ideograph-6E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e61 CJK Ideograph-6E61 | 6e60 CJK Ideograph-6E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e63 CJK Ideograph-6E63 | 6e62 CJK Ideograph-6E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e65 CJK Ideograph-6E65 | 6e64 CJK Ideograph-6E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e67 CJK Ideograph-6E67 | 6e66 CJK Ideograph-6E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e69 CJK Ideograph-6E69 | 6e68 CJK Ideograph-6E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6b CJK Ideograph-6E6B | 6e6a CJK Ideograph-6E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6d CJK Ideograph-6E6D | 6e6c CJK Ideograph-6E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e6f CJK Ideograph-6E6F | 6e6e CJK Ideograph-6E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e71 CJK Ideograph-6E71 | 6e70 CJK Ideograph-6E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e73 CJK Ideograph-6E73 | 6e72 CJK Ideograph-6E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e75 CJK Ideograph-6E75 | 6e74 CJK Ideograph-6E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e77 CJK Ideograph-6E77 | 6e76 CJK Ideograph-6E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e79 CJK Ideograph-6E79 | 6e78 CJK Ideograph-6E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7b CJK Ideograph-6E7B | 6e7a CJK Ideograph-6E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7d CJK Ideograph-6E7D | 6e7c CJK Ideograph-6E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e7f CJK Ideograph-6E7F | 6e7e CJK Ideograph-6E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e81 CJK Ideograph-6E81 | 6e80 CJK Ideograph-6E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e83 CJK Ideograph-6E83 | 6e82 CJK Ideograph-6E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e85 CJK Ideograph-6E85 | 6e84 CJK Ideograph-6E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e87 CJK Ideograph-6E87 | 6e86 CJK Ideograph-6E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e89 CJK Ideograph-6E89 | 6e88 CJK Ideograph-6E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8b CJK Ideograph-6E8B | 6e8a CJK Ideograph-6E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8d CJK Ideograph-6E8D | 6e8c CJK Ideograph-6E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e8f CJK Ideograph-6E8F | 6e8e CJK Ideograph-6E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e91 CJK Ideograph-6E91 | 6e90 CJK Ideograph-6E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e93 CJK Ideograph-6E93 | 6e92 CJK Ideograph-6E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e95 CJK Ideograph-6E95 | 6e94 CJK Ideograph-6E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e97 CJK Ideograph-6E97 | 6e96 CJK Ideograph-6E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e99 CJK Ideograph-6E99 | 6e98 CJK Ideograph-6E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9b CJK Ideograph-6E9B | 6e9a CJK Ideograph-6E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9d CJK Ideograph-6E9D | 6e9c CJK Ideograph-6E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6e9f CJK Ideograph-6E9F | 6e9e CJK Ideograph-6E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea1 CJK Ideograph-6EA1 | 6ea0 CJK Ideograph-6EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea3 CJK Ideograph-6EA3 | 6ea2 CJK Ideograph-6EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea5 CJK Ideograph-6EA5 | 6ea4 CJK Ideograph-6EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea7 CJK Ideograph-6EA7 | 6ea6 CJK Ideograph-6EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ea9 CJK Ideograph-6EA9 | 6ea8 CJK Ideograph-6EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eab CJK Ideograph-6EAB | 6eaa CJK Ideograph-6EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ead CJK Ideograph-6EAD | 6eac CJK Ideograph-6EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eaf CJK Ideograph-6EAF | 6eae CJK Ideograph-6EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb1 CJK Ideograph-6EB1 | 6eb0 CJK Ideograph-6EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb3 CJK Ideograph-6EB3 | 6eb2 CJK Ideograph-6EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb5 CJK Ideograph-6EB5 | 6eb4 CJK Ideograph-6EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb7 CJK Ideograph-6EB7 | 6eb6 CJK Ideograph-6EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eb9 CJK Ideograph-6EB9 | 6eb8 CJK Ideograph-6EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebb CJK Ideograph-6EBB | 6eba CJK Ideograph-6EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebd CJK Ideograph-6EBD | 6ebc CJK Ideograph-6EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ebf CJK Ideograph-6EBF | 6ebe CJK Ideograph-6EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec1 CJK Ideograph-6EC1 | 6ec0 CJK Ideograph-6EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec3 CJK Ideograph-6EC3 | 6ec2 CJK Ideograph-6EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec5 CJK Ideograph-6EC5 | 6ec4 CJK Ideograph-6EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec7 CJK Ideograph-6EC7 | 6ec6 CJK Ideograph-6EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ec9 CJK Ideograph-6EC9 | 6ec8 CJK Ideograph-6EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecb CJK Ideograph-6ECB | 6eca CJK Ideograph-6ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecd CJK Ideograph-6ECD | 6ecc CJK Ideograph-6ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ecf CJK Ideograph-6ECF | 6ece CJK Ideograph-6ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed1 CJK Ideograph-6ED1 | 6ed0 CJK Ideograph-6ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed3 CJK Ideograph-6ED3 | 6ed2 CJK Ideograph-6ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed5 CJK Ideograph-6ED5 | 6ed4 CJK Ideograph-6ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed7 CJK Ideograph-6ED7 | 6ed6 CJK Ideograph-6ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ed9 CJK Ideograph-6ED9 | 6ed8 CJK Ideograph-6ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edb CJK Ideograph-6EDB | 6eda CJK Ideograph-6EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edd CJK Ideograph-6EDD | 6edc CJK Ideograph-6EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6edf CJK Ideograph-6EDF | 6ede CJK Ideograph-6EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee1 CJK Ideograph-6EE1 | 6ee0 CJK Ideograph-6EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee3 CJK Ideograph-6EE3 | 6ee2 CJK Ideograph-6EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee5 CJK Ideograph-6EE5 | 6ee4 CJK Ideograph-6EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee7 CJK Ideograph-6EE7 | 6ee6 CJK Ideograph-6EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ee9 CJK Ideograph-6EE9 | 6ee8 CJK Ideograph-6EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eeb CJK Ideograph-6EEB | 6eea CJK Ideograph-6EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eed CJK Ideograph-6EED | 6eec CJK Ideograph-6EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eef CJK Ideograph-6EEF | 6eee CJK Ideograph-6EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef1 CJK Ideograph-6EF1 | 6ef0 CJK Ideograph-6EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef3 CJK Ideograph-6EF3 | 6ef2 CJK Ideograph-6EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef5 CJK Ideograph-6EF5 | 6ef4 CJK Ideograph-6EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef7 CJK Ideograph-6EF7 | 6ef6 CJK Ideograph-6EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ef9 CJK Ideograph-6EF9 | 6ef8 CJK Ideograph-6EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6efb CJK Ideograph-6EFB | 6efa CJK Ideograph-6EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6efd CJK Ideograph-6EFD | 6efc CJK Ideograph-6EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6eff CJK Ideograph-6EFF | 6efe CJK Ideograph-6EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f01 CJK Ideograph-6F01 | 6f00 CJK Ideograph-6F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f03 CJK Ideograph-6F03 | 6f02 CJK Ideograph-6F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f05 CJK Ideograph-6F05 | 6f04 CJK Ideograph-6F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f07 CJK Ideograph-6F07 | 6f06 CJK Ideograph-6F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f09 CJK Ideograph-6F09 | 6f08 CJK Ideograph-6F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0b CJK Ideograph-6F0B | 6f0a CJK Ideograph-6F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0d CJK Ideograph-6F0D | 6f0c CJK Ideograph-6F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f0f CJK Ideograph-6F0F | 6f0e CJK Ideograph-6F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f11 CJK Ideograph-6F11 | 6f10 CJK Ideograph-6F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f13 CJK Ideograph-6F13 | 6f12 CJK Ideograph-6F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f15 CJK Ideograph-6F15 | 6f14 CJK Ideograph-6F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f17 CJK Ideograph-6F17 | 6f16 CJK Ideograph-6F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f19 CJK Ideograph-6F19 | 6f18 CJK Ideograph-6F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1b CJK Ideograph-6F1B | 6f1a CJK Ideograph-6F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1d CJK Ideograph-6F1D | 6f1c CJK Ideograph-6F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f1f CJK Ideograph-6F1F | 6f1e CJK Ideograph-6F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f21 CJK Ideograph-6F21 | 6f20 CJK Ideograph-6F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f23 CJK Ideograph-6F23 | 6f22 CJK Ideograph-6F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f25 CJK Ideograph-6F25 | 6f24 CJK Ideograph-6F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f27 CJK Ideograph-6F27 | 6f26 CJK Ideograph-6F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f29 CJK Ideograph-6F29 | 6f28 CJK Ideograph-6F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2b CJK Ideograph-6F2B | 6f2a CJK Ideograph-6F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2d CJK Ideograph-6F2D | 6f2c CJK Ideograph-6F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f2f CJK Ideograph-6F2F | 6f2e CJK Ideograph-6F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f31 CJK Ideograph-6F31 | 6f30 CJK Ideograph-6F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f33 CJK Ideograph-6F33 | 6f32 CJK Ideograph-6F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f35 CJK Ideograph-6F35 | 6f34 CJK Ideograph-6F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f37 CJK Ideograph-6F37 | 6f36 CJK Ideograph-6F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f39 CJK Ideograph-6F39 | 6f38 CJK Ideograph-6F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3b CJK Ideograph-6F3B | 6f3a CJK Ideograph-6F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3d CJK Ideograph-6F3D | 6f3c CJK Ideograph-6F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f3f CJK Ideograph-6F3F | 6f3e CJK Ideograph-6F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f41 CJK Ideograph-6F41 | 6f40 CJK Ideograph-6F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f43 CJK Ideograph-6F43 | 6f42 CJK Ideograph-6F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f45 CJK Ideograph-6F45 | 6f44 CJK Ideograph-6F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f47 CJK Ideograph-6F47 | 6f46 CJK Ideograph-6F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f49 CJK Ideograph-6F49 | 6f48 CJK Ideograph-6F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4b CJK Ideograph-6F4B | 6f4a CJK Ideograph-6F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4d CJK Ideograph-6F4D | 6f4c CJK Ideograph-6F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f4f CJK Ideograph-6F4F | 6f4e CJK Ideograph-6F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f51 CJK Ideograph-6F51 | 6f50 CJK Ideograph-6F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f53 CJK Ideograph-6F53 | 6f52 CJK Ideograph-6F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f55 CJK Ideograph-6F55 | 6f54 CJK Ideograph-6F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f57 CJK Ideograph-6F57 | 6f56 CJK Ideograph-6F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f59 CJK Ideograph-6F59 | 6f58 CJK Ideograph-6F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5b CJK Ideograph-6F5B | 6f5a CJK Ideograph-6F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5d CJK Ideograph-6F5D | 6f5c CJK Ideograph-6F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f5f CJK Ideograph-6F5F | 6f5e CJK Ideograph-6F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f61 CJK Ideograph-6F61 | 6f60 CJK Ideograph-6F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f63 CJK Ideograph-6F63 | 6f62 CJK Ideograph-6F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f65 CJK Ideograph-6F65 | 6f64 CJK Ideograph-6F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f67 CJK Ideograph-6F67 | 6f66 CJK Ideograph-6F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f69 CJK Ideograph-6F69 | 6f68 CJK Ideograph-6F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6b CJK Ideograph-6F6B | 6f6a CJK Ideograph-6F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6d CJK Ideograph-6F6D | 6f6c CJK Ideograph-6F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f6f CJK Ideograph-6F6F | 6f6e CJK Ideograph-6F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f71 CJK Ideograph-6F71 | 6f70 CJK Ideograph-6F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f73 CJK Ideograph-6F73 | 6f72 CJK Ideograph-6F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f75 CJK Ideograph-6F75 | 6f74 CJK Ideograph-6F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f77 CJK Ideograph-6F77 | 6f76 CJK Ideograph-6F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f79 CJK Ideograph-6F79 | 6f78 CJK Ideograph-6F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7b CJK Ideograph-6F7B | 6f7a CJK Ideograph-6F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7d CJK Ideograph-6F7D | 6f7c CJK Ideograph-6F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f7f CJK Ideograph-6F7F | 6f7e CJK Ideograph-6F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f81 CJK Ideograph-6F81 | 6f80 CJK Ideograph-6F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f83 CJK Ideograph-6F83 | 6f82 CJK Ideograph-6F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f85 CJK Ideograph-6F85 | 6f84 CJK Ideograph-6F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f87 CJK Ideograph-6F87 | 6f86 CJK Ideograph-6F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f89 CJK Ideograph-6F89 | 6f88 CJK Ideograph-6F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8b CJK Ideograph-6F8B | 6f8a CJK Ideograph-6F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8d CJK Ideograph-6F8D | 6f8c CJK Ideograph-6F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f8f CJK Ideograph-6F8F | 6f8e CJK Ideograph-6F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f91 CJK Ideograph-6F91 | 6f90 CJK Ideograph-6F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f93 CJK Ideograph-6F93 | 6f92 CJK Ideograph-6F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f95 CJK Ideograph-6F95 | 6f94 CJK Ideograph-6F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f97 CJK Ideograph-6F97 | 6f96 CJK Ideograph-6F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f99 CJK Ideograph-6F99 | 6f98 CJK Ideograph-6F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9b CJK Ideograph-6F9B | 6f9a CJK Ideograph-6F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9d CJK Ideograph-6F9D | 6f9c CJK Ideograph-6F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6f9f CJK Ideograph-6F9F | 6f9e CJK Ideograph-6F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa1 CJK Ideograph-6FA1 | 6fa0 CJK Ideograph-6FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa3 CJK Ideograph-6FA3 | 6fa2 CJK Ideograph-6FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa5 CJK Ideograph-6FA5 | 6fa4 CJK Ideograph-6FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa7 CJK Ideograph-6FA7 | 6fa6 CJK Ideograph-6FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fa9 CJK Ideograph-6FA9 | 6fa8 CJK Ideograph-6FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fab CJK Ideograph-6FAB | 6faa CJK Ideograph-6FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fad CJK Ideograph-6FAD | 6fac CJK Ideograph-6FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6faf CJK Ideograph-6FAF | 6fae CJK Ideograph-6FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb1 CJK Ideograph-6FB1 | 6fb0 CJK Ideograph-6FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb3 CJK Ideograph-6FB3 | 6fb2 CJK Ideograph-6FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb5 CJK Ideograph-6FB5 | 6fb4 CJK Ideograph-6FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb7 CJK Ideograph-6FB7 | 6fb6 CJK Ideograph-6FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fb9 CJK Ideograph-6FB9 | 6fb8 CJK Ideograph-6FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbb CJK Ideograph-6FBB | 6fba CJK Ideograph-6FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbd CJK Ideograph-6FBD | 6fbc CJK Ideograph-6FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fbf CJK Ideograph-6FBF | 6fbe CJK Ideograph-6FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc1 CJK Ideograph-6FC1 | 6fc0 CJK Ideograph-6FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc3 CJK Ideograph-6FC3 | 6fc2 CJK Ideograph-6FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc5 CJK Ideograph-6FC5 | 6fc4 CJK Ideograph-6FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc7 CJK Ideograph-6FC7 | 6fc6 CJK Ideograph-6FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fc9 CJK Ideograph-6FC9 | 6fc8 CJK Ideograph-6FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcb CJK Ideograph-6FCB | 6fca CJK Ideograph-6FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcd CJK Ideograph-6FCD | 6fcc CJK Ideograph-6FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fcf CJK Ideograph-6FCF | 6fce CJK Ideograph-6FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd1 CJK Ideograph-6FD1 | 6fd0 CJK Ideograph-6FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd3 CJK Ideograph-6FD3 | 6fd2 CJK Ideograph-6FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd5 CJK Ideograph-6FD5 | 6fd4 CJK Ideograph-6FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd7 CJK Ideograph-6FD7 | 6fd6 CJK Ideograph-6FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fd9 CJK Ideograph-6FD9 | 6fd8 CJK Ideograph-6FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdb CJK Ideograph-6FDB | 6fda CJK Ideograph-6FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdd CJK Ideograph-6FDD | 6fdc CJK Ideograph-6FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fdf CJK Ideograph-6FDF | 6fde CJK Ideograph-6FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe1 CJK Ideograph-6FE1 | 6fe0 CJK Ideograph-6FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe3 CJK Ideograph-6FE3 | 6fe2 CJK Ideograph-6FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe5 CJK Ideograph-6FE5 | 6fe4 CJK Ideograph-6FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe7 CJK Ideograph-6FE7 | 6fe6 CJK Ideograph-6FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fe9 CJK Ideograph-6FE9 | 6fe8 CJK Ideograph-6FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6feb CJK Ideograph-6FEB | 6fea CJK Ideograph-6FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fed CJK Ideograph-6FED | 6fec CJK Ideograph-6FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fef CJK Ideograph-6FEF | 6fee CJK Ideograph-6FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff1 CJK Ideograph-6FF1 | 6ff0 CJK Ideograph-6FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff3 CJK Ideograph-6FF3 | 6ff2 CJK Ideograph-6FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff5 CJK Ideograph-6FF5 | 6ff4 CJK Ideograph-6FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff7 CJK Ideograph-6FF7 | 6ff6 CJK Ideograph-6FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ff9 CJK Ideograph-6FF9 | 6ff8 CJK Ideograph-6FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ffb CJK Ideograph-6FFB | 6ffa CJK Ideograph-6FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6ffd CJK Ideograph-6FFD | 6ffc CJK Ideograph-6FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 6fff CJK Ideograph-6FFF | 6ffe CJK Ideograph-6FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7001 CJK Ideograph-7001 | 7000 CJK Ideograph-7000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7003 CJK Ideograph-7003 | 7002 CJK Ideograph-7002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7005 CJK Ideograph-7005 | 7004 CJK Ideograph-7004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7007 CJK Ideograph-7007 | 7006 CJK Ideograph-7006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7009 CJK Ideograph-7009 | 7008 CJK Ideograph-7008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700b CJK Ideograph-700B | 700a CJK Ideograph-700A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700d CJK Ideograph-700D | 700c CJK Ideograph-700C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 700f CJK Ideograph-700F | 700e CJK Ideograph-700E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7011 CJK Ideograph-7011 | 7010 CJK Ideograph-7010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7013 CJK Ideograph-7013 | 7012 CJK Ideograph-7012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7015 CJK Ideograph-7015 | 7014 CJK Ideograph-7014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7017 CJK Ideograph-7017 | 7016 CJK Ideograph-7016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7019 CJK Ideograph-7019 | 7018 CJK Ideograph-7018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701b CJK Ideograph-701B | 701a CJK Ideograph-701A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701d CJK Ideograph-701D | 701c CJK Ideograph-701C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 701f CJK Ideograph-701F | 701e CJK Ideograph-701E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7021 CJK Ideograph-7021 | 7020 CJK Ideograph-7020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7023 CJK Ideograph-7023 | 7022 CJK Ideograph-7022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7025 CJK Ideograph-7025 | 7024 CJK Ideograph-7024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7027 CJK Ideograph-7027 | 7026 CJK Ideograph-7026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7029 CJK Ideograph-7029 | 7028 CJK Ideograph-7028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702b CJK Ideograph-702B | 702a CJK Ideograph-702A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702d CJK Ideograph-702D | 702c CJK Ideograph-702C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 702f CJK Ideograph-702F | 702e CJK Ideograph-702E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7031 CJK Ideograph-7031 | 7030 CJK Ideograph-7030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7033 CJK Ideograph-7033 | 7032 CJK Ideograph-7032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7035 CJK Ideograph-7035 | 7034 CJK Ideograph-7034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7037 CJK Ideograph-7037 | 7036 CJK Ideograph-7036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7039 CJK Ideograph-7039 | 7038 CJK Ideograph-7038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703b CJK Ideograph-703B | 703a CJK Ideograph-703A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703d CJK Ideograph-703D | 703c CJK Ideograph-703C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 703f CJK Ideograph-703F | 703e CJK Ideograph-703E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7041 CJK Ideograph-7041 | 7040 CJK Ideograph-7040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7043 CJK Ideograph-7043 | 7042 CJK Ideograph-7042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7045 CJK Ideograph-7045 | 7044 CJK Ideograph-7044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7047 CJK Ideograph-7047 | 7046 CJK Ideograph-7046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7049 CJK Ideograph-7049 | 7048 CJK Ideograph-7048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704b CJK Ideograph-704B | 704a CJK Ideograph-704A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704d CJK Ideograph-704D | 704c CJK Ideograph-704C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 704f CJK Ideograph-704F | 704e CJK Ideograph-704E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7051 CJK Ideograph-7051 | 7050 CJK Ideograph-7050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7053 CJK Ideograph-7053 | 7052 CJK Ideograph-7052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7055 CJK Ideograph-7055 | 7054 CJK Ideograph-7054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7057 CJK Ideograph-7057 | 7056 CJK Ideograph-7056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7059 CJK Ideograph-7059 | 7058 CJK Ideograph-7058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705b CJK Ideograph-705B | 705a CJK Ideograph-705A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705d CJK Ideograph-705D | 705c CJK Ideograph-705C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 705f CJK Ideograph-705F | 705e CJK Ideograph-705E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7061 CJK Ideograph-7061 | 7060 CJK Ideograph-7060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7063 CJK Ideograph-7063 | 7062 CJK Ideograph-7062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7065 CJK Ideograph-7065 | 7064 CJK Ideograph-7064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7067 CJK Ideograph-7067 | 7066 CJK Ideograph-7066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7069 CJK Ideograph-7069 | 7068 CJK Ideograph-7068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706b CJK Ideograph-706B | 706a CJK Ideograph-706A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706d CJK Ideograph-706D | 706c CJK Ideograph-706C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 706f CJK Ideograph-706F | 706e CJK Ideograph-706E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7071 CJK Ideograph-7071 | 7070 CJK Ideograph-7070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7073 CJK Ideograph-7073 | 7072 CJK Ideograph-7072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7075 CJK Ideograph-7075 | 7074 CJK Ideograph-7074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7077 CJK Ideograph-7077 | 7076 CJK Ideograph-7076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7079 CJK Ideograph-7079 | 7078 CJK Ideograph-7078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707b CJK Ideograph-707B | 707a CJK Ideograph-707A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707d CJK Ideograph-707D | 707c CJK Ideograph-707C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 707f CJK Ideograph-707F | 707e CJK Ideograph-707E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7081 CJK Ideograph-7081 | 7080 CJK Ideograph-7080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7083 CJK Ideograph-7083 | 7082 CJK Ideograph-7082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7085 CJK Ideograph-7085 | 7084 CJK Ideograph-7084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7087 CJK Ideograph-7087 | 7086 CJK Ideograph-7086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7089 CJK Ideograph-7089 | 7088 CJK Ideograph-7088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708b CJK Ideograph-708B | 708a CJK Ideograph-708A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708d CJK Ideograph-708D | 708c CJK Ideograph-708C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 708f CJK Ideograph-708F | 708e CJK Ideograph-708E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7091 CJK Ideograph-7091 | 7090 CJK Ideograph-7090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7093 CJK Ideograph-7093 | 7092 CJK Ideograph-7092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7095 CJK Ideograph-7095 | 7094 CJK Ideograph-7094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7097 CJK Ideograph-7097 | 7096 CJK Ideograph-7096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7099 CJK Ideograph-7099 | 7098 CJK Ideograph-7098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709b CJK Ideograph-709B | 709a CJK Ideograph-709A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709d CJK Ideograph-709D | 709c CJK Ideograph-709C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 709f CJK Ideograph-709F | 709e CJK Ideograph-709E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a1 CJK Ideograph-70A1 | 70a0 CJK Ideograph-70A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a3 CJK Ideograph-70A3 | 70a2 CJK Ideograph-70A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a5 CJK Ideograph-70A5 | 70a4 CJK Ideograph-70A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a7 CJK Ideograph-70A7 | 70a6 CJK Ideograph-70A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70a9 CJK Ideograph-70A9 | 70a8 CJK Ideograph-70A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ab CJK Ideograph-70AB | 70aa CJK Ideograph-70AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ad CJK Ideograph-70AD | 70ac CJK Ideograph-70AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70af CJK Ideograph-70AF | 70ae CJK Ideograph-70AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b1 CJK Ideograph-70B1 | 70b0 CJK Ideograph-70B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b3 CJK Ideograph-70B3 | 70b2 CJK Ideograph-70B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b5 CJK Ideograph-70B5 | 70b4 CJK Ideograph-70B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b7 CJK Ideograph-70B7 | 70b6 CJK Ideograph-70B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70b9 CJK Ideograph-70B9 | 70b8 CJK Ideograph-70B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bb CJK Ideograph-70BB | 70ba CJK Ideograph-70BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bd CJK Ideograph-70BD | 70bc CJK Ideograph-70BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70bf CJK Ideograph-70BF | 70be CJK Ideograph-70BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c1 CJK Ideograph-70C1 | 70c0 CJK Ideograph-70C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c3 CJK Ideograph-70C3 | 70c2 CJK Ideograph-70C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c5 CJK Ideograph-70C5 | 70c4 CJK Ideograph-70C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c7 CJK Ideograph-70C7 | 70c6 CJK Ideograph-70C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70c9 CJK Ideograph-70C9 | 70c8 CJK Ideograph-70C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cb CJK Ideograph-70CB | 70ca CJK Ideograph-70CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cd CJK Ideograph-70CD | 70cc CJK Ideograph-70CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70cf CJK Ideograph-70CF | 70ce CJK Ideograph-70CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d1 CJK Ideograph-70D1 | 70d0 CJK Ideograph-70D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d3 CJK Ideograph-70D3 | 70d2 CJK Ideograph-70D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d5 CJK Ideograph-70D5 | 70d4 CJK Ideograph-70D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d7 CJK Ideograph-70D7 | 70d6 CJK Ideograph-70D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70d9 CJK Ideograph-70D9 | 70d8 CJK Ideograph-70D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70db CJK Ideograph-70DB | 70da CJK Ideograph-70DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70dd CJK Ideograph-70DD | 70dc CJK Ideograph-70DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70df CJK Ideograph-70DF | 70de CJK Ideograph-70DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e1 CJK Ideograph-70E1 | 70e0 CJK Ideograph-70E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e3 CJK Ideograph-70E3 | 70e2 CJK Ideograph-70E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e5 CJK Ideograph-70E5 | 70e4 CJK Ideograph-70E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e7 CJK Ideograph-70E7 | 70e6 CJK Ideograph-70E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70e9 CJK Ideograph-70E9 | 70e8 CJK Ideograph-70E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70eb CJK Ideograph-70EB | 70ea CJK Ideograph-70EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ed CJK Ideograph-70ED | 70ec CJK Ideograph-70EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ef CJK Ideograph-70EF | 70ee CJK Ideograph-70EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f1 CJK Ideograph-70F1 | 70f0 CJK Ideograph-70F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f3 CJK Ideograph-70F3 | 70f2 CJK Ideograph-70F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f5 CJK Ideograph-70F5 | 70f4 CJK Ideograph-70F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f7 CJK Ideograph-70F7 | 70f6 CJK Ideograph-70F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70f9 CJK Ideograph-70F9 | 70f8 CJK Ideograph-70F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70fb CJK Ideograph-70FB | 70fa CJK Ideograph-70FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70fd CJK Ideograph-70FD | 70fc CJK Ideograph-70FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 70ff CJK Ideograph-70FF | 70fe CJK Ideograph-70FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7101 CJK Ideograph-7101 | 7100 CJK Ideograph-7100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7103 CJK Ideograph-7103 | 7102 CJK Ideograph-7102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7105 CJK Ideograph-7105 | 7104 CJK Ideograph-7104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7107 CJK Ideograph-7107 | 7106 CJK Ideograph-7106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7109 CJK Ideograph-7109 | 7108 CJK Ideograph-7108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710b CJK Ideograph-710B | 710a CJK Ideograph-710A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710d CJK Ideograph-710D | 710c CJK Ideograph-710C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 710f CJK Ideograph-710F | 710e CJK Ideograph-710E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7111 CJK Ideograph-7111 | 7110 CJK Ideograph-7110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7113 CJK Ideograph-7113 | 7112 CJK Ideograph-7112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7115 CJK Ideograph-7115 | 7114 CJK Ideograph-7114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7117 CJK Ideograph-7117 | 7116 CJK Ideograph-7116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7119 CJK Ideograph-7119 | 7118 CJK Ideograph-7118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711b CJK Ideograph-711B | 711a CJK Ideograph-711A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711d CJK Ideograph-711D | 711c CJK Ideograph-711C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 711f CJK Ideograph-711F | 711e CJK Ideograph-711E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7121 CJK Ideograph-7121 | 7120 CJK Ideograph-7120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7123 CJK Ideograph-7123 | 7122 CJK Ideograph-7122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7125 CJK Ideograph-7125 | 7124 CJK Ideograph-7124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7127 CJK Ideograph-7127 | 7126 CJK Ideograph-7126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7129 CJK Ideograph-7129 | 7128 CJK Ideograph-7128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712b CJK Ideograph-712B | 712a CJK Ideograph-712A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712d CJK Ideograph-712D | 712c CJK Ideograph-712C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 712f CJK Ideograph-712F | 712e CJK Ideograph-712E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7131 CJK Ideograph-7131 | 7130 CJK Ideograph-7130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7133 CJK Ideograph-7133 | 7132 CJK Ideograph-7132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7135 CJK Ideograph-7135 | 7134 CJK Ideograph-7134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7137 CJK Ideograph-7137 | 7136 CJK Ideograph-7136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7139 CJK Ideograph-7139 | 7138 CJK Ideograph-7138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713b CJK Ideograph-713B | 713a CJK Ideograph-713A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713d CJK Ideograph-713D | 713c CJK Ideograph-713C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 713f CJK Ideograph-713F | 713e CJK Ideograph-713E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7141 CJK Ideograph-7141 | 7140 CJK Ideograph-7140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7143 CJK Ideograph-7143 | 7142 CJK Ideograph-7142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7145 CJK Ideograph-7145 | 7144 CJK Ideograph-7144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7147 CJK Ideograph-7147 | 7146 CJK Ideograph-7146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7149 CJK Ideograph-7149 | 7148 CJK Ideograph-7148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714b CJK Ideograph-714B | 714a CJK Ideograph-714A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714d CJK Ideograph-714D | 714c CJK Ideograph-714C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 714f CJK Ideograph-714F | 714e CJK Ideograph-714E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7151 CJK Ideograph-7151 | 7150 CJK Ideograph-7150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7153 CJK Ideograph-7153 | 7152 CJK Ideograph-7152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7155 CJK Ideograph-7155 | 7154 CJK Ideograph-7154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7157 CJK Ideograph-7157 | 7156 CJK Ideograph-7156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7159 CJK Ideograph-7159 | 7158 CJK Ideograph-7158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715b CJK Ideograph-715B | 715a CJK Ideograph-715A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715d CJK Ideograph-715D | 715c CJK Ideograph-715C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 715f CJK Ideograph-715F | 715e CJK Ideograph-715E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7161 CJK Ideograph-7161 | 7160 CJK Ideograph-7160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7163 CJK Ideograph-7163 | 7162 CJK Ideograph-7162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7165 CJK Ideograph-7165 | 7164 CJK Ideograph-7164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7167 CJK Ideograph-7167 | 7166 CJK Ideograph-7166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7169 CJK Ideograph-7169 | 7168 CJK Ideograph-7168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716b CJK Ideograph-716B | 716a CJK Ideograph-716A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716d CJK Ideograph-716D | 716c CJK Ideograph-716C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 716f CJK Ideograph-716F | 716e CJK Ideograph-716E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7171 CJK Ideograph-7171 | 7170 CJK Ideograph-7170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7173 CJK Ideograph-7173 | 7172 CJK Ideograph-7172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7175 CJK Ideograph-7175 | 7174 CJK Ideograph-7174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7177 CJK Ideograph-7177 | 7176 CJK Ideograph-7176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7179 CJK Ideograph-7179 | 7178 CJK Ideograph-7178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717b CJK Ideograph-717B | 717a CJK Ideograph-717A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717d CJK Ideograph-717D | 717c CJK Ideograph-717C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 717f CJK Ideograph-717F | 717e CJK Ideograph-717E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7181 CJK Ideograph-7181 | 7180 CJK Ideograph-7180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7183 CJK Ideograph-7183 | 7182 CJK Ideograph-7182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7185 CJK Ideograph-7185 | 7184 CJK Ideograph-7184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7187 CJK Ideograph-7187 | 7186 CJK Ideograph-7186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7189 CJK Ideograph-7189 | 7188 CJK Ideograph-7188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718b CJK Ideograph-718B | 718a CJK Ideograph-718A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718d CJK Ideograph-718D | 718c CJK Ideograph-718C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 718f CJK Ideograph-718F | 718e CJK Ideograph-718E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7191 CJK Ideograph-7191 | 7190 CJK Ideograph-7190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7193 CJK Ideograph-7193 | 7192 CJK Ideograph-7192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7195 CJK Ideograph-7195 | 7194 CJK Ideograph-7194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7197 CJK Ideograph-7197 | 7196 CJK Ideograph-7196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7199 CJK Ideograph-7199 | 7198 CJK Ideograph-7198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719b CJK Ideograph-719B | 719a CJK Ideograph-719A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719d CJK Ideograph-719D | 719c CJK Ideograph-719C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 719f CJK Ideograph-719F | 719e CJK Ideograph-719E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a1 CJK Ideograph-71A1 | 71a0 CJK Ideograph-71A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a3 CJK Ideograph-71A3 | 71a2 CJK Ideograph-71A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a5 CJK Ideograph-71A5 | 71a4 CJK Ideograph-71A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a7 CJK Ideograph-71A7 | 71a6 CJK Ideograph-71A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71a9 CJK Ideograph-71A9 | 71a8 CJK Ideograph-71A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ab CJK Ideograph-71AB | 71aa CJK Ideograph-71AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ad CJK Ideograph-71AD | 71ac CJK Ideograph-71AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71af CJK Ideograph-71AF | 71ae CJK Ideograph-71AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b1 CJK Ideograph-71B1 | 71b0 CJK Ideograph-71B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b3 CJK Ideograph-71B3 | 71b2 CJK Ideograph-71B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b5 CJK Ideograph-71B5 | 71b4 CJK Ideograph-71B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b7 CJK Ideograph-71B7 | 71b6 CJK Ideograph-71B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71b9 CJK Ideograph-71B9 | 71b8 CJK Ideograph-71B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bb CJK Ideograph-71BB | 71ba CJK Ideograph-71BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bd CJK Ideograph-71BD | 71bc CJK Ideograph-71BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71bf CJK Ideograph-71BF | 71be CJK Ideograph-71BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c1 CJK Ideograph-71C1 | 71c0 CJK Ideograph-71C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c3 CJK Ideograph-71C3 | 71c2 CJK Ideograph-71C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c5 CJK Ideograph-71C5 | 71c4 CJK Ideograph-71C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c7 CJK Ideograph-71C7 | 71c6 CJK Ideograph-71C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71c9 CJK Ideograph-71C9 | 71c8 CJK Ideograph-71C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cb CJK Ideograph-71CB | 71ca CJK Ideograph-71CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cd CJK Ideograph-71CD | 71cc CJK Ideograph-71CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71cf CJK Ideograph-71CF | 71ce CJK Ideograph-71CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d1 CJK Ideograph-71D1 | 71d0 CJK Ideograph-71D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d3 CJK Ideograph-71D3 | 71d2 CJK Ideograph-71D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d5 CJK Ideograph-71D5 | 71d4 CJK Ideograph-71D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d7 CJK Ideograph-71D7 | 71d6 CJK Ideograph-71D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71d9 CJK Ideograph-71D9 | 71d8 CJK Ideograph-71D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71db CJK Ideograph-71DB | 71da CJK Ideograph-71DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71dd CJK Ideograph-71DD | 71dc CJK Ideograph-71DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71df CJK Ideograph-71DF | 71de CJK Ideograph-71DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e1 CJK Ideograph-71E1 | 71e0 CJK Ideograph-71E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e3 CJK Ideograph-71E3 | 71e2 CJK Ideograph-71E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e5 CJK Ideograph-71E5 | 71e4 CJK Ideograph-71E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e7 CJK Ideograph-71E7 | 71e6 CJK Ideograph-71E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71e9 CJK Ideograph-71E9 | 71e8 CJK Ideograph-71E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71eb CJK Ideograph-71EB | 71ea CJK Ideograph-71EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ed CJK Ideograph-71ED | 71ec CJK Ideograph-71EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ef CJK Ideograph-71EF | 71ee CJK Ideograph-71EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f1 CJK Ideograph-71F1 | 71f0 CJK Ideograph-71F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f3 CJK Ideograph-71F3 | 71f2 CJK Ideograph-71F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f5 CJK Ideograph-71F5 | 71f4 CJK Ideograph-71F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f7 CJK Ideograph-71F7 | 71f6 CJK Ideograph-71F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71f9 CJK Ideograph-71F9 | 71f8 CJK Ideograph-71F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71fb CJK Ideograph-71FB | 71fa CJK Ideograph-71FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71fd CJK Ideograph-71FD | 71fc CJK Ideograph-71FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 71ff CJK Ideograph-71FF | 71fe CJK Ideograph-71FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7201 CJK Ideograph-7201 | 7200 CJK Ideograph-7200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7203 CJK Ideograph-7203 | 7202 CJK Ideograph-7202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7205 CJK Ideograph-7205 | 7204 CJK Ideograph-7204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7207 CJK Ideograph-7207 | 7206 CJK Ideograph-7206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7209 CJK Ideograph-7209 | 7208 CJK Ideograph-7208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720b CJK Ideograph-720B | 720a CJK Ideograph-720A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720d CJK Ideograph-720D | 720c CJK Ideograph-720C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 720f CJK Ideograph-720F | 720e CJK Ideograph-720E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7211 CJK Ideograph-7211 | 7210 CJK Ideograph-7210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7213 CJK Ideograph-7213 | 7212 CJK Ideograph-7212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7215 CJK Ideograph-7215 | 7214 CJK Ideograph-7214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7217 CJK Ideograph-7217 | 7216 CJK Ideograph-7216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7219 CJK Ideograph-7219 | 7218 CJK Ideograph-7218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721b CJK Ideograph-721B | 721a CJK Ideograph-721A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721d CJK Ideograph-721D | 721c CJK Ideograph-721C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 721f CJK Ideograph-721F | 721e CJK Ideograph-721E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7221 CJK Ideograph-7221 | 7220 CJK Ideograph-7220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7223 CJK Ideograph-7223 | 7222 CJK Ideograph-7222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7225 CJK Ideograph-7225 | 7224 CJK Ideograph-7224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7227 CJK Ideograph-7227 | 7226 CJK Ideograph-7226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7229 CJK Ideograph-7229 | 7228 CJK Ideograph-7228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722b CJK Ideograph-722B | 722a CJK Ideograph-722A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722d CJK Ideograph-722D | 722c CJK Ideograph-722C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 722f CJK Ideograph-722F | 722e CJK Ideograph-722E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7231 CJK Ideograph-7231 | 7230 CJK Ideograph-7230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7233 CJK Ideograph-7233 | 7232 CJK Ideograph-7232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7235 CJK Ideograph-7235 | 7234 CJK Ideograph-7234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7237 CJK Ideograph-7237 | 7236 CJK Ideograph-7236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7239 CJK Ideograph-7239 | 7238 CJK Ideograph-7238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723b CJK Ideograph-723B | 723a CJK Ideograph-723A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723d CJK Ideograph-723D | 723c CJK Ideograph-723C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 723f CJK Ideograph-723F | 723e CJK Ideograph-723E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7241 CJK Ideograph-7241 | 7240 CJK Ideograph-7240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7243 CJK Ideograph-7243 | 7242 CJK Ideograph-7242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7245 CJK Ideograph-7245 | 7244 CJK Ideograph-7244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7247 CJK Ideograph-7247 | 7246 CJK Ideograph-7246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7249 CJK Ideograph-7249 | 7248 CJK Ideograph-7248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724b CJK Ideograph-724B | 724a CJK Ideograph-724A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724d CJK Ideograph-724D | 724c CJK Ideograph-724C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 724f CJK Ideograph-724F | 724e CJK Ideograph-724E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7251 CJK Ideograph-7251 | 7250 CJK Ideograph-7250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7253 CJK Ideograph-7253 | 7252 CJK Ideograph-7252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7255 CJK Ideograph-7255 | 7254 CJK Ideograph-7254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7257 CJK Ideograph-7257 | 7256 CJK Ideograph-7256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7259 CJK Ideograph-7259 | 7258 CJK Ideograph-7258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725b CJK Ideograph-725B | 725a CJK Ideograph-725A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725d CJK Ideograph-725D | 725c CJK Ideograph-725C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 725f CJK Ideograph-725F | 725e CJK Ideograph-725E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7261 CJK Ideograph-7261 | 7260 CJK Ideograph-7260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7263 CJK Ideograph-7263 | 7262 CJK Ideograph-7262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7265 CJK Ideograph-7265 | 7264 CJK Ideograph-7264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7267 CJK Ideograph-7267 | 7266 CJK Ideograph-7266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7269 CJK Ideograph-7269 | 7268 CJK Ideograph-7268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726b CJK Ideograph-726B | 726a CJK Ideograph-726A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726d CJK Ideograph-726D | 726c CJK Ideograph-726C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 726f CJK Ideograph-726F | 726e CJK Ideograph-726E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7271 CJK Ideograph-7271 | 7270 CJK Ideograph-7270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7273 CJK Ideograph-7273 | 7272 CJK Ideograph-7272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7275 CJK Ideograph-7275 | 7274 CJK Ideograph-7274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7277 CJK Ideograph-7277 | 7276 CJK Ideograph-7276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7279 CJK Ideograph-7279 | 7278 CJK Ideograph-7278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727b CJK Ideograph-727B | 727a CJK Ideograph-727A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727d CJK Ideograph-727D | 727c CJK Ideograph-727C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 727f CJK Ideograph-727F | 727e CJK Ideograph-727E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7281 CJK Ideograph-7281 | 7280 CJK Ideograph-7280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7283 CJK Ideograph-7283 | 7282 CJK Ideograph-7282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7285 CJK Ideograph-7285 | 7284 CJK Ideograph-7284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7287 CJK Ideograph-7287 | 7286 CJK Ideograph-7286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7289 CJK Ideograph-7289 | 7288 CJK Ideograph-7288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728b CJK Ideograph-728B | 728a CJK Ideograph-728A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728d CJK Ideograph-728D | 728c CJK Ideograph-728C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 728f CJK Ideograph-728F | 728e CJK Ideograph-728E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7291 CJK Ideograph-7291 | 7290 CJK Ideograph-7290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7293 CJK Ideograph-7293 | 7292 CJK Ideograph-7292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7295 CJK Ideograph-7295 | 7294 CJK Ideograph-7294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7297 CJK Ideograph-7297 | 7296 CJK Ideograph-7296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7299 CJK Ideograph-7299 | 7298 CJK Ideograph-7298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729b CJK Ideograph-729B | 729a CJK Ideograph-729A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729d CJK Ideograph-729D | 729c CJK Ideograph-729C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 729f CJK Ideograph-729F | 729e CJK Ideograph-729E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a1 CJK Ideograph-72A1 | 72a0 CJK Ideograph-72A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a3 CJK Ideograph-72A3 | 72a2 CJK Ideograph-72A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a5 CJK Ideograph-72A5 | 72a4 CJK Ideograph-72A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a7 CJK Ideograph-72A7 | 72a6 CJK Ideograph-72A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72a9 CJK Ideograph-72A9 | 72a8 CJK Ideograph-72A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ab CJK Ideograph-72AB | 72aa CJK Ideograph-72AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ad CJK Ideograph-72AD | 72ac CJK Ideograph-72AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72af CJK Ideograph-72AF | 72ae CJK Ideograph-72AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b1 CJK Ideograph-72B1 | 72b0 CJK Ideograph-72B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b3 CJK Ideograph-72B3 | 72b2 CJK Ideograph-72B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b5 CJK Ideograph-72B5 | 72b4 CJK Ideograph-72B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b7 CJK Ideograph-72B7 | 72b6 CJK Ideograph-72B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72b9 CJK Ideograph-72B9 | 72b8 CJK Ideograph-72B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bb CJK Ideograph-72BB | 72ba CJK Ideograph-72BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bd CJK Ideograph-72BD | 72bc CJK Ideograph-72BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72bf CJK Ideograph-72BF | 72be CJK Ideograph-72BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c1 CJK Ideograph-72C1 | 72c0 CJK Ideograph-72C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c3 CJK Ideograph-72C3 | 72c2 CJK Ideograph-72C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c5 CJK Ideograph-72C5 | 72c4 CJK Ideograph-72C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c7 CJK Ideograph-72C7 | 72c6 CJK Ideograph-72C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72c9 CJK Ideograph-72C9 | 72c8 CJK Ideograph-72C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cb CJK Ideograph-72CB | 72ca CJK Ideograph-72CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cd CJK Ideograph-72CD | 72cc CJK Ideograph-72CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72cf CJK Ideograph-72CF | 72ce CJK Ideograph-72CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d1 CJK Ideograph-72D1 | 72d0 CJK Ideograph-72D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d3 CJK Ideograph-72D3 | 72d2 CJK Ideograph-72D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d5 CJK Ideograph-72D5 | 72d4 CJK Ideograph-72D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d7 CJK Ideograph-72D7 | 72d6 CJK Ideograph-72D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72d9 CJK Ideograph-72D9 | 72d8 CJK Ideograph-72D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72db CJK Ideograph-72DB | 72da CJK Ideograph-72DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72dd CJK Ideograph-72DD | 72dc CJK Ideograph-72DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72df CJK Ideograph-72DF | 72de CJK Ideograph-72DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e1 CJK Ideograph-72E1 | 72e0 CJK Ideograph-72E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e3 CJK Ideograph-72E3 | 72e2 CJK Ideograph-72E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e5 CJK Ideograph-72E5 | 72e4 CJK Ideograph-72E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e7 CJK Ideograph-72E7 | 72e6 CJK Ideograph-72E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72e9 CJK Ideograph-72E9 | 72e8 CJK Ideograph-72E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72eb CJK Ideograph-72EB | 72ea CJK Ideograph-72EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ed CJK Ideograph-72ED | 72ec CJK Ideograph-72EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ef CJK Ideograph-72EF | 72ee CJK Ideograph-72EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f1 CJK Ideograph-72F1 | 72f0 CJK Ideograph-72F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f3 CJK Ideograph-72F3 | 72f2 CJK Ideograph-72F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f5 CJK Ideograph-72F5 | 72f4 CJK Ideograph-72F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f7 CJK Ideograph-72F7 | 72f6 CJK Ideograph-72F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72f9 CJK Ideograph-72F9 | 72f8 CJK Ideograph-72F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72fb CJK Ideograph-72FB | 72fa CJK Ideograph-72FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72fd CJK Ideograph-72FD | 72fc CJK Ideograph-72FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 72ff CJK Ideograph-72FF | 72fe CJK Ideograph-72FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7301 CJK Ideograph-7301 | 7300 CJK Ideograph-7300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7303 CJK Ideograph-7303 | 7302 CJK Ideograph-7302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7305 CJK Ideograph-7305 | 7304 CJK Ideograph-7304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7307 CJK Ideograph-7307 | 7306 CJK Ideograph-7306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7309 CJK Ideograph-7309 | 7308 CJK Ideograph-7308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730b CJK Ideograph-730B | 730a CJK Ideograph-730A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730d CJK Ideograph-730D | 730c CJK Ideograph-730C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 730f CJK Ideograph-730F | 730e CJK Ideograph-730E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7311 CJK Ideograph-7311 | 7310 CJK Ideograph-7310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7313 CJK Ideograph-7313 | 7312 CJK Ideograph-7312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7315 CJK Ideograph-7315 | 7314 CJK Ideograph-7314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7317 CJK Ideograph-7317 | 7316 CJK Ideograph-7316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7319 CJK Ideograph-7319 | 7318 CJK Ideograph-7318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731b CJK Ideograph-731B | 731a CJK Ideograph-731A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731d CJK Ideograph-731D | 731c CJK Ideograph-731C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 731f CJK Ideograph-731F | 731e CJK Ideograph-731E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7321 CJK Ideograph-7321 | 7320 CJK Ideograph-7320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7323 CJK Ideograph-7323 | 7322 CJK Ideograph-7322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7325 CJK Ideograph-7325 | 7324 CJK Ideograph-7324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7327 CJK Ideograph-7327 | 7326 CJK Ideograph-7326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7329 CJK Ideograph-7329 | 7328 CJK Ideograph-7328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732b CJK Ideograph-732B | 732a CJK Ideograph-732A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732d CJK Ideograph-732D | 732c CJK Ideograph-732C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 732f CJK Ideograph-732F | 732e CJK Ideograph-732E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7331 CJK Ideograph-7331 | 7330 CJK Ideograph-7330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7333 CJK Ideograph-7333 | 7332 CJK Ideograph-7332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7335 CJK Ideograph-7335 | 7334 CJK Ideograph-7334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7337 CJK Ideograph-7337 | 7336 CJK Ideograph-7336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7339 CJK Ideograph-7339 | 7338 CJK Ideograph-7338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733b CJK Ideograph-733B | 733a CJK Ideograph-733A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733d CJK Ideograph-733D | 733c CJK Ideograph-733C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 733f CJK Ideograph-733F | 733e CJK Ideograph-733E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7341 CJK Ideograph-7341 | 7340 CJK Ideograph-7340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7343 CJK Ideograph-7343 | 7342 CJK Ideograph-7342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7345 CJK Ideograph-7345 | 7344 CJK Ideograph-7344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7347 CJK Ideograph-7347 | 7346 CJK Ideograph-7346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7349 CJK Ideograph-7349 | 7348 CJK Ideograph-7348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734b CJK Ideograph-734B | 734a CJK Ideograph-734A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734d CJK Ideograph-734D | 734c CJK Ideograph-734C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 734f CJK Ideograph-734F | 734e CJK Ideograph-734E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7351 CJK Ideograph-7351 | 7350 CJK Ideograph-7350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7353 CJK Ideograph-7353 | 7352 CJK Ideograph-7352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7355 CJK Ideograph-7355 | 7354 CJK Ideograph-7354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7357 CJK Ideograph-7357 | 7356 CJK Ideograph-7356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7359 CJK Ideograph-7359 | 7358 CJK Ideograph-7358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735b CJK Ideograph-735B | 735a CJK Ideograph-735A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735d CJK Ideograph-735D | 735c CJK Ideograph-735C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 735f CJK Ideograph-735F | 735e CJK Ideograph-735E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7361 CJK Ideograph-7361 | 7360 CJK Ideograph-7360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7363 CJK Ideograph-7363 | 7362 CJK Ideograph-7362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7365 CJK Ideograph-7365 | 7364 CJK Ideograph-7364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7367 CJK Ideograph-7367 | 7366 CJK Ideograph-7366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7369 CJK Ideograph-7369 | 7368 CJK Ideograph-7368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736b CJK Ideograph-736B | 736a CJK Ideograph-736A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736d CJK Ideograph-736D | 736c CJK Ideograph-736C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 736f CJK Ideograph-736F | 736e CJK Ideograph-736E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7371 CJK Ideograph-7371 | 7370 CJK Ideograph-7370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7373 CJK Ideograph-7373 | 7372 CJK Ideograph-7372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7375 CJK Ideograph-7375 | 7374 CJK Ideograph-7374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7377 CJK Ideograph-7377 | 7376 CJK Ideograph-7376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7379 CJK Ideograph-7379 | 7378 CJK Ideograph-7378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737b CJK Ideograph-737B | 737a CJK Ideograph-737A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737d CJK Ideograph-737D | 737c CJK Ideograph-737C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 737f CJK Ideograph-737F | 737e CJK Ideograph-737E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7381 CJK Ideograph-7381 | 7380 CJK Ideograph-7380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7383 CJK Ideograph-7383 | 7382 CJK Ideograph-7382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7385 CJK Ideograph-7385 | 7384 CJK Ideograph-7384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7387 CJK Ideograph-7387 | 7386 CJK Ideograph-7386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7389 CJK Ideograph-7389 | 7388 CJK Ideograph-7388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738b CJK Ideograph-738B | 738a CJK Ideograph-738A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738d CJK Ideograph-738D | 738c CJK Ideograph-738C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 738f CJK Ideograph-738F | 738e CJK Ideograph-738E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7391 CJK Ideograph-7391 | 7390 CJK Ideograph-7390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7393 CJK Ideograph-7393 | 7392 CJK Ideograph-7392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7395 CJK Ideograph-7395 | 7394 CJK Ideograph-7394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7397 CJK Ideograph-7397 | 7396 CJK Ideograph-7396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7399 CJK Ideograph-7399 | 7398 CJK Ideograph-7398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739b CJK Ideograph-739B | 739a CJK Ideograph-739A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739d CJK Ideograph-739D | 739c CJK Ideograph-739C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 739f CJK Ideograph-739F | 739e CJK Ideograph-739E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a1 CJK Ideograph-73A1 | 73a0 CJK Ideograph-73A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a3 CJK Ideograph-73A3 | 73a2 CJK Ideograph-73A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a5 CJK Ideograph-73A5 | 73a4 CJK Ideograph-73A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a7 CJK Ideograph-73A7 | 73a6 CJK Ideograph-73A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73a9 CJK Ideograph-73A9 | 73a8 CJK Ideograph-73A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ab CJK Ideograph-73AB | 73aa CJK Ideograph-73AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ad CJK Ideograph-73AD | 73ac CJK Ideograph-73AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73af CJK Ideograph-73AF | 73ae CJK Ideograph-73AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b1 CJK Ideograph-73B1 | 73b0 CJK Ideograph-73B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b3 CJK Ideograph-73B3 | 73b2 CJK Ideograph-73B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b5 CJK Ideograph-73B5 | 73b4 CJK Ideograph-73B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b7 CJK Ideograph-73B7 | 73b6 CJK Ideograph-73B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73b9 CJK Ideograph-73B9 | 73b8 CJK Ideograph-73B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bb CJK Ideograph-73BB | 73ba CJK Ideograph-73BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bd CJK Ideograph-73BD | 73bc CJK Ideograph-73BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73bf CJK Ideograph-73BF | 73be CJK Ideograph-73BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c1 CJK Ideograph-73C1 | 73c0 CJK Ideograph-73C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c3 CJK Ideograph-73C3 | 73c2 CJK Ideograph-73C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c5 CJK Ideograph-73C5 | 73c4 CJK Ideograph-73C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c7 CJK Ideograph-73C7 | 73c6 CJK Ideograph-73C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73c9 CJK Ideograph-73C9 | 73c8 CJK Ideograph-73C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cb CJK Ideograph-73CB | 73ca CJK Ideograph-73CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cd CJK Ideograph-73CD | 73cc CJK Ideograph-73CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73cf CJK Ideograph-73CF | 73ce CJK Ideograph-73CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d1 CJK Ideograph-73D1 | 73d0 CJK Ideograph-73D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d3 CJK Ideograph-73D3 | 73d2 CJK Ideograph-73D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d5 CJK Ideograph-73D5 | 73d4 CJK Ideograph-73D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d7 CJK Ideograph-73D7 | 73d6 CJK Ideograph-73D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73d9 CJK Ideograph-73D9 | 73d8 CJK Ideograph-73D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73db CJK Ideograph-73DB | 73da CJK Ideograph-73DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73dd CJK Ideograph-73DD | 73dc CJK Ideograph-73DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73df CJK Ideograph-73DF | 73de CJK Ideograph-73DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e1 CJK Ideograph-73E1 | 73e0 CJK Ideograph-73E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e3 CJK Ideograph-73E3 | 73e2 CJK Ideograph-73E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e5 CJK Ideograph-73E5 | 73e4 CJK Ideograph-73E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e7 CJK Ideograph-73E7 | 73e6 CJK Ideograph-73E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73e9 CJK Ideograph-73E9 | 73e8 CJK Ideograph-73E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73eb CJK Ideograph-73EB | 73ea CJK Ideograph-73EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ed CJK Ideograph-73ED | 73ec CJK Ideograph-73EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ef CJK Ideograph-73EF | 73ee CJK Ideograph-73EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f1 CJK Ideograph-73F1 | 73f0 CJK Ideograph-73F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f3 CJK Ideograph-73F3 | 73f2 CJK Ideograph-73F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f5 CJK Ideograph-73F5 | 73f4 CJK Ideograph-73F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f7 CJK Ideograph-73F7 | 73f6 CJK Ideograph-73F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73f9 CJK Ideograph-73F9 | 73f8 CJK Ideograph-73F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73fb CJK Ideograph-73FB | 73fa CJK Ideograph-73FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73fd CJK Ideograph-73FD | 73fc CJK Ideograph-73FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 73ff CJK Ideograph-73FF | 73fe CJK Ideograph-73FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7401 CJK Ideograph-7401 | 7400 CJK Ideograph-7400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7403 CJK Ideograph-7403 | 7402 CJK Ideograph-7402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7405 CJK Ideograph-7405 | 7404 CJK Ideograph-7404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7407 CJK Ideograph-7407 | 7406 CJK Ideograph-7406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7409 CJK Ideograph-7409 | 7408 CJK Ideograph-7408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740b CJK Ideograph-740B | 740a CJK Ideograph-740A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740d CJK Ideograph-740D | 740c CJK Ideograph-740C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 740f CJK Ideograph-740F | 740e CJK Ideograph-740E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7411 CJK Ideograph-7411 | 7410 CJK Ideograph-7410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7413 CJK Ideograph-7413 | 7412 CJK Ideograph-7412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7415 CJK Ideograph-7415 | 7414 CJK Ideograph-7414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7417 CJK Ideograph-7417 | 7416 CJK Ideograph-7416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7419 CJK Ideograph-7419 | 7418 CJK Ideograph-7418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741b CJK Ideograph-741B | 741a CJK Ideograph-741A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741d CJK Ideograph-741D | 741c CJK Ideograph-741C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 741f CJK Ideograph-741F | 741e CJK Ideograph-741E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7421 CJK Ideograph-7421 | 7420 CJK Ideograph-7420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7423 CJK Ideograph-7423 | 7422 CJK Ideograph-7422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7425 CJK Ideograph-7425 | 7424 CJK Ideograph-7424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7427 CJK Ideograph-7427 | 7426 CJK Ideograph-7426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7429 CJK Ideograph-7429 | 7428 CJK Ideograph-7428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742b CJK Ideograph-742B | 742a CJK Ideograph-742A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742d CJK Ideograph-742D | 742c CJK Ideograph-742C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 742f CJK Ideograph-742F | 742e CJK Ideograph-742E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7431 CJK Ideograph-7431 | 7430 CJK Ideograph-7430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7433 CJK Ideograph-7433 | 7432 CJK Ideograph-7432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7435 CJK Ideograph-7435 | 7434 CJK Ideograph-7434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7437 CJK Ideograph-7437 | 7436 CJK Ideograph-7436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7439 CJK Ideograph-7439 | 7438 CJK Ideograph-7438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743b CJK Ideograph-743B | 743a CJK Ideograph-743A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743d CJK Ideograph-743D | 743c CJK Ideograph-743C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 743f CJK Ideograph-743F | 743e CJK Ideograph-743E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7441 CJK Ideograph-7441 | 7440 CJK Ideograph-7440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7443 CJK Ideograph-7443 | 7442 CJK Ideograph-7442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7445 CJK Ideograph-7445 | 7444 CJK Ideograph-7444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7447 CJK Ideograph-7447 | 7446 CJK Ideograph-7446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7449 CJK Ideograph-7449 | 7448 CJK Ideograph-7448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744b CJK Ideograph-744B | 744a CJK Ideograph-744A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744d CJK Ideograph-744D | 744c CJK Ideograph-744C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 744f CJK Ideograph-744F | 744e CJK Ideograph-744E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7451 CJK Ideograph-7451 | 7450 CJK Ideograph-7450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7453 CJK Ideograph-7453 | 7452 CJK Ideograph-7452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7455 CJK Ideograph-7455 | 7454 CJK Ideograph-7454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7457 CJK Ideograph-7457 | 7456 CJK Ideograph-7456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7459 CJK Ideograph-7459 | 7458 CJK Ideograph-7458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745b CJK Ideograph-745B | 745a CJK Ideograph-745A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745d CJK Ideograph-745D | 745c CJK Ideograph-745C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 745f CJK Ideograph-745F | 745e CJK Ideograph-745E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7461 CJK Ideograph-7461 | 7460 CJK Ideograph-7460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7463 CJK Ideograph-7463 | 7462 CJK Ideograph-7462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7465 CJK Ideograph-7465 | 7464 CJK Ideograph-7464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7467 CJK Ideograph-7467 | 7466 CJK Ideograph-7466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7469 CJK Ideograph-7469 | 7468 CJK Ideograph-7468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746b CJK Ideograph-746B | 746a CJK Ideograph-746A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746d CJK Ideograph-746D | 746c CJK Ideograph-746C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 746f CJK Ideograph-746F | 746e CJK Ideograph-746E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7471 CJK Ideograph-7471 | 7470 CJK Ideograph-7470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7473 CJK Ideograph-7473 | 7472 CJK Ideograph-7472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7475 CJK Ideograph-7475 | 7474 CJK Ideograph-7474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7477 CJK Ideograph-7477 | 7476 CJK Ideograph-7476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7479 CJK Ideograph-7479 | 7478 CJK Ideograph-7478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747b CJK Ideograph-747B | 747a CJK Ideograph-747A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747d CJK Ideograph-747D | 747c CJK Ideograph-747C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 747f CJK Ideograph-747F | 747e CJK Ideograph-747E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7481 CJK Ideograph-7481 | 7480 CJK Ideograph-7480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7483 CJK Ideograph-7483 | 7482 CJK Ideograph-7482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7485 CJK Ideograph-7485 | 7484 CJK Ideograph-7484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7487 CJK Ideograph-7487 | 7486 CJK Ideograph-7486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7489 CJK Ideograph-7489 | 7488 CJK Ideograph-7488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748b CJK Ideograph-748B | 748a CJK Ideograph-748A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748d CJK Ideograph-748D | 748c CJK Ideograph-748C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 748f CJK Ideograph-748F | 748e CJK Ideograph-748E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7491 CJK Ideograph-7491 | 7490 CJK Ideograph-7490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7493 CJK Ideograph-7493 | 7492 CJK Ideograph-7492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7495 CJK Ideograph-7495 | 7494 CJK Ideograph-7494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7497 CJK Ideograph-7497 | 7496 CJK Ideograph-7496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7499 CJK Ideograph-7499 | 7498 CJK Ideograph-7498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749b CJK Ideograph-749B | 749a CJK Ideograph-749A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749d CJK Ideograph-749D | 749c CJK Ideograph-749C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 749f CJK Ideograph-749F | 749e CJK Ideograph-749E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a1 CJK Ideograph-74A1 | 74a0 CJK Ideograph-74A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a3 CJK Ideograph-74A3 | 74a2 CJK Ideograph-74A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a5 CJK Ideograph-74A5 | 74a4 CJK Ideograph-74A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a7 CJK Ideograph-74A7 | 74a6 CJK Ideograph-74A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74a9 CJK Ideograph-74A9 | 74a8 CJK Ideograph-74A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ab CJK Ideograph-74AB | 74aa CJK Ideograph-74AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ad CJK Ideograph-74AD | 74ac CJK Ideograph-74AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74af CJK Ideograph-74AF | 74ae CJK Ideograph-74AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b1 CJK Ideograph-74B1 | 74b0 CJK Ideograph-74B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b3 CJK Ideograph-74B3 | 74b2 CJK Ideograph-74B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b5 CJK Ideograph-74B5 | 74b4 CJK Ideograph-74B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b7 CJK Ideograph-74B7 | 74b6 CJK Ideograph-74B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74b9 CJK Ideograph-74B9 | 74b8 CJK Ideograph-74B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bb CJK Ideograph-74BB | 74ba CJK Ideograph-74BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bd CJK Ideograph-74BD | 74bc CJK Ideograph-74BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74bf CJK Ideograph-74BF | 74be CJK Ideograph-74BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c1 CJK Ideograph-74C1 | 74c0 CJK Ideograph-74C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c3 CJK Ideograph-74C3 | 74c2 CJK Ideograph-74C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c5 CJK Ideograph-74C5 | 74c4 CJK Ideograph-74C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c7 CJK Ideograph-74C7 | 74c6 CJK Ideograph-74C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74c9 CJK Ideograph-74C9 | 74c8 CJK Ideograph-74C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cb CJK Ideograph-74CB | 74ca CJK Ideograph-74CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cd CJK Ideograph-74CD | 74cc CJK Ideograph-74CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74cf CJK Ideograph-74CF | 74ce CJK Ideograph-74CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d1 CJK Ideograph-74D1 | 74d0 CJK Ideograph-74D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d3 CJK Ideograph-74D3 | 74d2 CJK Ideograph-74D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d5 CJK Ideograph-74D5 | 74d4 CJK Ideograph-74D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d7 CJK Ideograph-74D7 | 74d6 CJK Ideograph-74D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74d9 CJK Ideograph-74D9 | 74d8 CJK Ideograph-74D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74db CJK Ideograph-74DB | 74da CJK Ideograph-74DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74dd CJK Ideograph-74DD | 74dc CJK Ideograph-74DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74df CJK Ideograph-74DF | 74de CJK Ideograph-74DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e1 CJK Ideograph-74E1 | 74e0 CJK Ideograph-74E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e3 CJK Ideograph-74E3 | 74e2 CJK Ideograph-74E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e5 CJK Ideograph-74E5 | 74e4 CJK Ideograph-74E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e7 CJK Ideograph-74E7 | 74e6 CJK Ideograph-74E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74e9 CJK Ideograph-74E9 | 74e8 CJK Ideograph-74E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74eb CJK Ideograph-74EB | 74ea CJK Ideograph-74EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ed CJK Ideograph-74ED | 74ec CJK Ideograph-74EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ef CJK Ideograph-74EF | 74ee CJK Ideograph-74EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f1 CJK Ideograph-74F1 | 74f0 CJK Ideograph-74F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f3 CJK Ideograph-74F3 | 74f2 CJK Ideograph-74F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f5 CJK Ideograph-74F5 | 74f4 CJK Ideograph-74F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f7 CJK Ideograph-74F7 | 74f6 CJK Ideograph-74F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74f9 CJK Ideograph-74F9 | 74f8 CJK Ideograph-74F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74fb CJK Ideograph-74FB | 74fa CJK Ideograph-74FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74fd CJK Ideograph-74FD | 74fc CJK Ideograph-74FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 74ff CJK Ideograph-74FF | 74fe CJK Ideograph-74FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7501 CJK Ideograph-7501 | 7500 CJK Ideograph-7500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7503 CJK Ideograph-7503 | 7502 CJK Ideograph-7502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7505 CJK Ideograph-7505 | 7504 CJK Ideograph-7504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7507 CJK Ideograph-7507 | 7506 CJK Ideograph-7506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7509 CJK Ideograph-7509 | 7508 CJK Ideograph-7508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750b CJK Ideograph-750B | 750a CJK Ideograph-750A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750d CJK Ideograph-750D | 750c CJK Ideograph-750C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 750f CJK Ideograph-750F | 750e CJK Ideograph-750E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7511 CJK Ideograph-7511 | 7510 CJK Ideograph-7510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7513 CJK Ideograph-7513 | 7512 CJK Ideograph-7512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7515 CJK Ideograph-7515 | 7514 CJK Ideograph-7514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7517 CJK Ideograph-7517 | 7516 CJK Ideograph-7516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7519 CJK Ideograph-7519 | 7518 CJK Ideograph-7518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751b CJK Ideograph-751B | 751a CJK Ideograph-751A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751d CJK Ideograph-751D | 751c CJK Ideograph-751C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 751f CJK Ideograph-751F | 751e CJK Ideograph-751E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7521 CJK Ideograph-7521 | 7520 CJK Ideograph-7520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7523 CJK Ideograph-7523 | 7522 CJK Ideograph-7522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7525 CJK Ideograph-7525 | 7524 CJK Ideograph-7524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7527 CJK Ideograph-7527 | 7526 CJK Ideograph-7526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7529 CJK Ideograph-7529 | 7528 CJK Ideograph-7528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752b CJK Ideograph-752B | 752a CJK Ideograph-752A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752d CJK Ideograph-752D | 752c CJK Ideograph-752C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 752f CJK Ideograph-752F | 752e CJK Ideograph-752E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7531 CJK Ideograph-7531 | 7530 CJK Ideograph-7530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7533 CJK Ideograph-7533 | 7532 CJK Ideograph-7532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7535 CJK Ideograph-7535 | 7534 CJK Ideograph-7534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7537 CJK Ideograph-7537 | 7536 CJK Ideograph-7536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7539 CJK Ideograph-7539 | 7538 CJK Ideograph-7538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753b CJK Ideograph-753B | 753a CJK Ideograph-753A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753d CJK Ideograph-753D | 753c CJK Ideograph-753C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 753f CJK Ideograph-753F | 753e CJK Ideograph-753E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7541 CJK Ideograph-7541 | 7540 CJK Ideograph-7540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7543 CJK Ideograph-7543 | 7542 CJK Ideograph-7542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7545 CJK Ideograph-7545 | 7544 CJK Ideograph-7544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7547 CJK Ideograph-7547 | 7546 CJK Ideograph-7546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7549 CJK Ideograph-7549 | 7548 CJK Ideograph-7548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754b CJK Ideograph-754B | 754a CJK Ideograph-754A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754d CJK Ideograph-754D | 754c CJK Ideograph-754C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 754f CJK Ideograph-754F | 754e CJK Ideograph-754E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7551 CJK Ideograph-7551 | 7550 CJK Ideograph-7550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7553 CJK Ideograph-7553 | 7552 CJK Ideograph-7552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7555 CJK Ideograph-7555 | 7554 CJK Ideograph-7554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7557 CJK Ideograph-7557 | 7556 CJK Ideograph-7556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7559 CJK Ideograph-7559 | 7558 CJK Ideograph-7558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755b CJK Ideograph-755B | 755a CJK Ideograph-755A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755d CJK Ideograph-755D | 755c CJK Ideograph-755C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 755f CJK Ideograph-755F | 755e CJK Ideograph-755E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7561 CJK Ideograph-7561 | 7560 CJK Ideograph-7560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7563 CJK Ideograph-7563 | 7562 CJK Ideograph-7562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7565 CJK Ideograph-7565 | 7564 CJK Ideograph-7564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7567 CJK Ideograph-7567 | 7566 CJK Ideograph-7566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7569 CJK Ideograph-7569 | 7568 CJK Ideograph-7568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756b CJK Ideograph-756B | 756a CJK Ideograph-756A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756d CJK Ideograph-756D | 756c CJK Ideograph-756C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 756f CJK Ideograph-756F | 756e CJK Ideograph-756E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7571 CJK Ideograph-7571 | 7570 CJK Ideograph-7570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7573 CJK Ideograph-7573 | 7572 CJK Ideograph-7572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7575 CJK Ideograph-7575 | 7574 CJK Ideograph-7574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7577 CJK Ideograph-7577 | 7576 CJK Ideograph-7576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7579 CJK Ideograph-7579 | 7578 CJK Ideograph-7578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757b CJK Ideograph-757B | 757a CJK Ideograph-757A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757d CJK Ideograph-757D | 757c CJK Ideograph-757C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 757f CJK Ideograph-757F | 757e CJK Ideograph-757E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7581 CJK Ideograph-7581 | 7580 CJK Ideograph-7580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7583 CJK Ideograph-7583 | 7582 CJK Ideograph-7582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7585 CJK Ideograph-7585 | 7584 CJK Ideograph-7584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7587 CJK Ideograph-7587 | 7586 CJK Ideograph-7586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7589 CJK Ideograph-7589 | 7588 CJK Ideograph-7588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758b CJK Ideograph-758B | 758a CJK Ideograph-758A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758d CJK Ideograph-758D | 758c CJK Ideograph-758C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 758f CJK Ideograph-758F | 758e CJK Ideograph-758E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7591 CJK Ideograph-7591 | 7590 CJK Ideograph-7590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7593 CJK Ideograph-7593 | 7592 CJK Ideograph-7592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7595 CJK Ideograph-7595 | 7594 CJK Ideograph-7594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7597 CJK Ideograph-7597 | 7596 CJK Ideograph-7596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7599 CJK Ideograph-7599 | 7598 CJK Ideograph-7598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759b CJK Ideograph-759B | 759a CJK Ideograph-759A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759d CJK Ideograph-759D | 759c CJK Ideograph-759C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 759f CJK Ideograph-759F | 759e CJK Ideograph-759E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a1 CJK Ideograph-75A1 | 75a0 CJK Ideograph-75A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a3 CJK Ideograph-75A3 | 75a2 CJK Ideograph-75A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a5 CJK Ideograph-75A5 | 75a4 CJK Ideograph-75A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a7 CJK Ideograph-75A7 | 75a6 CJK Ideograph-75A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75a9 CJK Ideograph-75A9 | 75a8 CJK Ideograph-75A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ab CJK Ideograph-75AB | 75aa CJK Ideograph-75AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ad CJK Ideograph-75AD | 75ac CJK Ideograph-75AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75af CJK Ideograph-75AF | 75ae CJK Ideograph-75AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b1 CJK Ideograph-75B1 | 75b0 CJK Ideograph-75B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b3 CJK Ideograph-75B3 | 75b2 CJK Ideograph-75B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b5 CJK Ideograph-75B5 | 75b4 CJK Ideograph-75B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b7 CJK Ideograph-75B7 | 75b6 CJK Ideograph-75B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75b9 CJK Ideograph-75B9 | 75b8 CJK Ideograph-75B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bb CJK Ideograph-75BB | 75ba CJK Ideograph-75BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bd CJK Ideograph-75BD | 75bc CJK Ideograph-75BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75bf CJK Ideograph-75BF | 75be CJK Ideograph-75BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c1 CJK Ideograph-75C1 | 75c0 CJK Ideograph-75C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c3 CJK Ideograph-75C3 | 75c2 CJK Ideograph-75C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c5 CJK Ideograph-75C5 | 75c4 CJK Ideograph-75C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c7 CJK Ideograph-75C7 | 75c6 CJK Ideograph-75C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75c9 CJK Ideograph-75C9 | 75c8 CJK Ideograph-75C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cb CJK Ideograph-75CB | 75ca CJK Ideograph-75CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cd CJK Ideograph-75CD | 75cc CJK Ideograph-75CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75cf CJK Ideograph-75CF | 75ce CJK Ideograph-75CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d1 CJK Ideograph-75D1 | 75d0 CJK Ideograph-75D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d3 CJK Ideograph-75D3 | 75d2 CJK Ideograph-75D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d5 CJK Ideograph-75D5 | 75d4 CJK Ideograph-75D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d7 CJK Ideograph-75D7 | 75d6 CJK Ideograph-75D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75d9 CJK Ideograph-75D9 | 75d8 CJK Ideograph-75D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75db CJK Ideograph-75DB | 75da CJK Ideograph-75DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75dd CJK Ideograph-75DD | 75dc CJK Ideograph-75DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75df CJK Ideograph-75DF | 75de CJK Ideograph-75DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e1 CJK Ideograph-75E1 | 75e0 CJK Ideograph-75E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e3 CJK Ideograph-75E3 | 75e2 CJK Ideograph-75E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e5 CJK Ideograph-75E5 | 75e4 CJK Ideograph-75E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e7 CJK Ideograph-75E7 | 75e6 CJK Ideograph-75E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75e9 CJK Ideograph-75E9 | 75e8 CJK Ideograph-75E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75eb CJK Ideograph-75EB | 75ea CJK Ideograph-75EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ed CJK Ideograph-75ED | 75ec CJK Ideograph-75EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ef CJK Ideograph-75EF | 75ee CJK Ideograph-75EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f1 CJK Ideograph-75F1 | 75f0 CJK Ideograph-75F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f3 CJK Ideograph-75F3 | 75f2 CJK Ideograph-75F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f5 CJK Ideograph-75F5 | 75f4 CJK Ideograph-75F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f7 CJK Ideograph-75F7 | 75f6 CJK Ideograph-75F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75f9 CJK Ideograph-75F9 | 75f8 CJK Ideograph-75F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75fb CJK Ideograph-75FB | 75fa CJK Ideograph-75FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75fd CJK Ideograph-75FD | 75fc CJK Ideograph-75FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 75ff CJK Ideograph-75FF | 75fe CJK Ideograph-75FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7601 CJK Ideograph-7601 | 7600 CJK Ideograph-7600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7603 CJK Ideograph-7603 | 7602 CJK Ideograph-7602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7605 CJK Ideograph-7605 | 7604 CJK Ideograph-7604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7607 CJK Ideograph-7607 | 7606 CJK Ideograph-7606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7609 CJK Ideograph-7609 | 7608 CJK Ideograph-7608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760b CJK Ideograph-760B | 760a CJK Ideograph-760A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760d CJK Ideograph-760D | 760c CJK Ideograph-760C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 760f CJK Ideograph-760F | 760e CJK Ideograph-760E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7611 CJK Ideograph-7611 | 7610 CJK Ideograph-7610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7613 CJK Ideograph-7613 | 7612 CJK Ideograph-7612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7615 CJK Ideograph-7615 | 7614 CJK Ideograph-7614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7617 CJK Ideograph-7617 | 7616 CJK Ideograph-7616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7619 CJK Ideograph-7619 | 7618 CJK Ideograph-7618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761b CJK Ideograph-761B | 761a CJK Ideograph-761A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761d CJK Ideograph-761D | 761c CJK Ideograph-761C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 761f CJK Ideograph-761F | 761e CJK Ideograph-761E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7621 CJK Ideograph-7621 | 7620 CJK Ideograph-7620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7623 CJK Ideograph-7623 | 7622 CJK Ideograph-7622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7625 CJK Ideograph-7625 | 7624 CJK Ideograph-7624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7627 CJK Ideograph-7627 | 7626 CJK Ideograph-7626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7629 CJK Ideograph-7629 | 7628 CJK Ideograph-7628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762b CJK Ideograph-762B | 762a CJK Ideograph-762A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762d CJK Ideograph-762D | 762c CJK Ideograph-762C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 762f CJK Ideograph-762F | 762e CJK Ideograph-762E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7631 CJK Ideograph-7631 | 7630 CJK Ideograph-7630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7633 CJK Ideograph-7633 | 7632 CJK Ideograph-7632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7635 CJK Ideograph-7635 | 7634 CJK Ideograph-7634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7637 CJK Ideograph-7637 | 7636 CJK Ideograph-7636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7639 CJK Ideograph-7639 | 7638 CJK Ideograph-7638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763b CJK Ideograph-763B | 763a CJK Ideograph-763A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763d CJK Ideograph-763D | 763c CJK Ideograph-763C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 763f CJK Ideograph-763F | 763e CJK Ideograph-763E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7641 CJK Ideograph-7641 | 7640 CJK Ideograph-7640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7643 CJK Ideograph-7643 | 7642 CJK Ideograph-7642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7645 CJK Ideograph-7645 | 7644 CJK Ideograph-7644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7647 CJK Ideograph-7647 | 7646 CJK Ideograph-7646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7649 CJK Ideograph-7649 | 7648 CJK Ideograph-7648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764b CJK Ideograph-764B | 764a CJK Ideograph-764A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764d CJK Ideograph-764D | 764c CJK Ideograph-764C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 764f CJK Ideograph-764F | 764e CJK Ideograph-764E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7651 CJK Ideograph-7651 | 7650 CJK Ideograph-7650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7653 CJK Ideograph-7653 | 7652 CJK Ideograph-7652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7655 CJK Ideograph-7655 | 7654 CJK Ideograph-7654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7657 CJK Ideograph-7657 | 7656 CJK Ideograph-7656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7659 CJK Ideograph-7659 | 7658 CJK Ideograph-7658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765b CJK Ideograph-765B | 765a CJK Ideograph-765A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765d CJK Ideograph-765D | 765c CJK Ideograph-765C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 765f CJK Ideograph-765F | 765e CJK Ideograph-765E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7661 CJK Ideograph-7661 | 7660 CJK Ideograph-7660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7663 CJK Ideograph-7663 | 7662 CJK Ideograph-7662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7665 CJK Ideograph-7665 | 7664 CJK Ideograph-7664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7667 CJK Ideograph-7667 | 7666 CJK Ideograph-7666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7669 CJK Ideograph-7669 | 7668 CJK Ideograph-7668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766b CJK Ideograph-766B | 766a CJK Ideograph-766A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766d CJK Ideograph-766D | 766c CJK Ideograph-766C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 766f CJK Ideograph-766F | 766e CJK Ideograph-766E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7671 CJK Ideograph-7671 | 7670 CJK Ideograph-7670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7673 CJK Ideograph-7673 | 7672 CJK Ideograph-7672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7675 CJK Ideograph-7675 | 7674 CJK Ideograph-7674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7677 CJK Ideograph-7677 | 7676 CJK Ideograph-7676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7679 CJK Ideograph-7679 | 7678 CJK Ideograph-7678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767b CJK Ideograph-767B | 767a CJK Ideograph-767A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767d CJK Ideograph-767D | 767c CJK Ideograph-767C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 767f CJK Ideograph-767F | 767e CJK Ideograph-767E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7681 CJK Ideograph-7681 | 7680 CJK Ideograph-7680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7683 CJK Ideograph-7683 | 7682 CJK Ideograph-7682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7685 CJK Ideograph-7685 | 7684 CJK Ideograph-7684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7687 CJK Ideograph-7687 | 7686 CJK Ideograph-7686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7689 CJK Ideograph-7689 | 7688 CJK Ideograph-7688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768b CJK Ideograph-768B | 768a CJK Ideograph-768A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768d CJK Ideograph-768D | 768c CJK Ideograph-768C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 768f CJK Ideograph-768F | 768e CJK Ideograph-768E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7691 CJK Ideograph-7691 | 7690 CJK Ideograph-7690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7693 CJK Ideograph-7693 | 7692 CJK Ideograph-7692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7695 CJK Ideograph-7695 | 7694 CJK Ideograph-7694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7697 CJK Ideograph-7697 | 7696 CJK Ideograph-7696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7699 CJK Ideograph-7699 | 7698 CJK Ideograph-7698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769b CJK Ideograph-769B | 769a CJK Ideograph-769A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769d CJK Ideograph-769D | 769c CJK Ideograph-769C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 769f CJK Ideograph-769F | 769e CJK Ideograph-769E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a1 CJK Ideograph-76A1 | 76a0 CJK Ideograph-76A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a3 CJK Ideograph-76A3 | 76a2 CJK Ideograph-76A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a5 CJK Ideograph-76A5 | 76a4 CJK Ideograph-76A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a7 CJK Ideograph-76A7 | 76a6 CJK Ideograph-76A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76a9 CJK Ideograph-76A9 | 76a8 CJK Ideograph-76A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ab CJK Ideograph-76AB | 76aa CJK Ideograph-76AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ad CJK Ideograph-76AD | 76ac CJK Ideograph-76AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76af CJK Ideograph-76AF | 76ae CJK Ideograph-76AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b1 CJK Ideograph-76B1 | 76b0 CJK Ideograph-76B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b3 CJK Ideograph-76B3 | 76b2 CJK Ideograph-76B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b5 CJK Ideograph-76B5 | 76b4 CJK Ideograph-76B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b7 CJK Ideograph-76B7 | 76b6 CJK Ideograph-76B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76b9 CJK Ideograph-76B9 | 76b8 CJK Ideograph-76B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bb CJK Ideograph-76BB | 76ba CJK Ideograph-76BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bd CJK Ideograph-76BD | 76bc CJK Ideograph-76BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76bf CJK Ideograph-76BF | 76be CJK Ideograph-76BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c1 CJK Ideograph-76C1 | 76c0 CJK Ideograph-76C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c3 CJK Ideograph-76C3 | 76c2 CJK Ideograph-76C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c5 CJK Ideograph-76C5 | 76c4 CJK Ideograph-76C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c7 CJK Ideograph-76C7 | 76c6 CJK Ideograph-76C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76c9 CJK Ideograph-76C9 | 76c8 CJK Ideograph-76C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cb CJK Ideograph-76CB | 76ca CJK Ideograph-76CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cd CJK Ideograph-76CD | 76cc CJK Ideograph-76CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76cf CJK Ideograph-76CF | 76ce CJK Ideograph-76CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d1 CJK Ideograph-76D1 | 76d0 CJK Ideograph-76D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d3 CJK Ideograph-76D3 | 76d2 CJK Ideograph-76D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d5 CJK Ideograph-76D5 | 76d4 CJK Ideograph-76D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d7 CJK Ideograph-76D7 | 76d6 CJK Ideograph-76D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76d9 CJK Ideograph-76D9 | 76d8 CJK Ideograph-76D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76db CJK Ideograph-76DB | 76da CJK Ideograph-76DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76dd CJK Ideograph-76DD | 76dc CJK Ideograph-76DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76df CJK Ideograph-76DF | 76de CJK Ideograph-76DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e1 CJK Ideograph-76E1 | 76e0 CJK Ideograph-76E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e3 CJK Ideograph-76E3 | 76e2 CJK Ideograph-76E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e5 CJK Ideograph-76E5 | 76e4 CJK Ideograph-76E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e7 CJK Ideograph-76E7 | 76e6 CJK Ideograph-76E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76e9 CJK Ideograph-76E9 | 76e8 CJK Ideograph-76E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76eb CJK Ideograph-76EB | 76ea CJK Ideograph-76EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ed CJK Ideograph-76ED | 76ec CJK Ideograph-76EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ef CJK Ideograph-76EF | 76ee CJK Ideograph-76EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f1 CJK Ideograph-76F1 | 76f0 CJK Ideograph-76F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f3 CJK Ideograph-76F3 | 76f2 CJK Ideograph-76F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f5 CJK Ideograph-76F5 | 76f4 CJK Ideograph-76F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f7 CJK Ideograph-76F7 | 76f6 CJK Ideograph-76F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76f9 CJK Ideograph-76F9 | 76f8 CJK Ideograph-76F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76fb CJK Ideograph-76FB | 76fa CJK Ideograph-76FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76fd CJK Ideograph-76FD | 76fc CJK Ideograph-76FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 76ff CJK Ideograph-76FF | 76fe CJK Ideograph-76FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7701 CJK Ideograph-7701 | 7700 CJK Ideograph-7700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7703 CJK Ideograph-7703 | 7702 CJK Ideograph-7702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7705 CJK Ideograph-7705 | 7704 CJK Ideograph-7704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7707 CJK Ideograph-7707 | 7706 CJK Ideograph-7706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7709 CJK Ideograph-7709 | 7708 CJK Ideograph-7708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770b CJK Ideograph-770B | 770a CJK Ideograph-770A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770d CJK Ideograph-770D | 770c CJK Ideograph-770C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 770f CJK Ideograph-770F | 770e CJK Ideograph-770E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7711 CJK Ideograph-7711 | 7710 CJK Ideograph-7710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7713 CJK Ideograph-7713 | 7712 CJK Ideograph-7712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7715 CJK Ideograph-7715 | 7714 CJK Ideograph-7714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7717 CJK Ideograph-7717 | 7716 CJK Ideograph-7716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7719 CJK Ideograph-7719 | 7718 CJK Ideograph-7718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771b CJK Ideograph-771B | 771a CJK Ideograph-771A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771d CJK Ideograph-771D | 771c CJK Ideograph-771C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 771f CJK Ideograph-771F | 771e CJK Ideograph-771E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7721 CJK Ideograph-7721 | 7720 CJK Ideograph-7720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7723 CJK Ideograph-7723 | 7722 CJK Ideograph-7722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7725 CJK Ideograph-7725 | 7724 CJK Ideograph-7724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7727 CJK Ideograph-7727 | 7726 CJK Ideograph-7726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7729 CJK Ideograph-7729 | 7728 CJK Ideograph-7728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772b CJK Ideograph-772B | 772a CJK Ideograph-772A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772d CJK Ideograph-772D | 772c CJK Ideograph-772C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 772f CJK Ideograph-772F | 772e CJK Ideograph-772E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7731 CJK Ideograph-7731 | 7730 CJK Ideograph-7730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7733 CJK Ideograph-7733 | 7732 CJK Ideograph-7732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7735 CJK Ideograph-7735 | 7734 CJK Ideograph-7734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7737 CJK Ideograph-7737 | 7736 CJK Ideograph-7736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7739 CJK Ideograph-7739 | 7738 CJK Ideograph-7738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773b CJK Ideograph-773B | 773a CJK Ideograph-773A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773d CJK Ideograph-773D | 773c CJK Ideograph-773C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 773f CJK Ideograph-773F | 773e CJK Ideograph-773E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7741 CJK Ideograph-7741 | 7740 CJK Ideograph-7740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7743 CJK Ideograph-7743 | 7742 CJK Ideograph-7742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7745 CJK Ideograph-7745 | 7744 CJK Ideograph-7744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7747 CJK Ideograph-7747 | 7746 CJK Ideograph-7746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7749 CJK Ideograph-7749 | 7748 CJK Ideograph-7748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774b CJK Ideograph-774B | 774a CJK Ideograph-774A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774d CJK Ideograph-774D | 774c CJK Ideograph-774C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 774f CJK Ideograph-774F | 774e CJK Ideograph-774E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7751 CJK Ideograph-7751 | 7750 CJK Ideograph-7750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7753 CJK Ideograph-7753 | 7752 CJK Ideograph-7752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7755 CJK Ideograph-7755 | 7754 CJK Ideograph-7754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7757 CJK Ideograph-7757 | 7756 CJK Ideograph-7756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7759 CJK Ideograph-7759 | 7758 CJK Ideograph-7758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775b CJK Ideograph-775B | 775a CJK Ideograph-775A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775d CJK Ideograph-775D | 775c CJK Ideograph-775C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 775f CJK Ideograph-775F | 775e CJK Ideograph-775E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7761 CJK Ideograph-7761 | 7760 CJK Ideograph-7760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7763 CJK Ideograph-7763 | 7762 CJK Ideograph-7762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7765 CJK Ideograph-7765 | 7764 CJK Ideograph-7764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7767 CJK Ideograph-7767 | 7766 CJK Ideograph-7766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7769 CJK Ideograph-7769 | 7768 CJK Ideograph-7768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776b CJK Ideograph-776B | 776a CJK Ideograph-776A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776d CJK Ideograph-776D | 776c CJK Ideograph-776C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 776f CJK Ideograph-776F | 776e CJK Ideograph-776E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7771 CJK Ideograph-7771 | 7770 CJK Ideograph-7770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7773 CJK Ideograph-7773 | 7772 CJK Ideograph-7772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7775 CJK Ideograph-7775 | 7774 CJK Ideograph-7774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7777 CJK Ideograph-7777 | 7776 CJK Ideograph-7776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7779 CJK Ideograph-7779 | 7778 CJK Ideograph-7778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777b CJK Ideograph-777B | 777a CJK Ideograph-777A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777d CJK Ideograph-777D | 777c CJK Ideograph-777C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 777f CJK Ideograph-777F | 777e CJK Ideograph-777E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7781 CJK Ideograph-7781 | 7780 CJK Ideograph-7780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7783 CJK Ideograph-7783 | 7782 CJK Ideograph-7782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7785 CJK Ideograph-7785 | 7784 CJK Ideograph-7784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7787 CJK Ideograph-7787 | 7786 CJK Ideograph-7786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7789 CJK Ideograph-7789 | 7788 CJK Ideograph-7788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778b CJK Ideograph-778B | 778a CJK Ideograph-778A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778d CJK Ideograph-778D | 778c CJK Ideograph-778C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 778f CJK Ideograph-778F | 778e CJK Ideograph-778E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7791 CJK Ideograph-7791 | 7790 CJK Ideograph-7790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7793 CJK Ideograph-7793 | 7792 CJK Ideograph-7792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7795 CJK Ideograph-7795 | 7794 CJK Ideograph-7794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7797 CJK Ideograph-7797 | 7796 CJK Ideograph-7796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7799 CJK Ideograph-7799 | 7798 CJK Ideograph-7798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779b CJK Ideograph-779B | 779a CJK Ideograph-779A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779d CJK Ideograph-779D | 779c CJK Ideograph-779C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 779f CJK Ideograph-779F | 779e CJK Ideograph-779E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a1 CJK Ideograph-77A1 | 77a0 CJK Ideograph-77A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a3 CJK Ideograph-77A3 | 77a2 CJK Ideograph-77A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a5 CJK Ideograph-77A5 | 77a4 CJK Ideograph-77A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a7 CJK Ideograph-77A7 | 77a6 CJK Ideograph-77A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77a9 CJK Ideograph-77A9 | 77a8 CJK Ideograph-77A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ab CJK Ideograph-77AB | 77aa CJK Ideograph-77AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ad CJK Ideograph-77AD | 77ac CJK Ideograph-77AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77af CJK Ideograph-77AF | 77ae CJK Ideograph-77AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b1 CJK Ideograph-77B1 | 77b0 CJK Ideograph-77B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b3 CJK Ideograph-77B3 | 77b2 CJK Ideograph-77B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b5 CJK Ideograph-77B5 | 77b4 CJK Ideograph-77B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b7 CJK Ideograph-77B7 | 77b6 CJK Ideograph-77B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77b9 CJK Ideograph-77B9 | 77b8 CJK Ideograph-77B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bb CJK Ideograph-77BB | 77ba CJK Ideograph-77BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bd CJK Ideograph-77BD | 77bc CJK Ideograph-77BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77bf CJK Ideograph-77BF | 77be CJK Ideograph-77BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c1 CJK Ideograph-77C1 | 77c0 CJK Ideograph-77C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c3 CJK Ideograph-77C3 | 77c2 CJK Ideograph-77C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c5 CJK Ideograph-77C5 | 77c4 CJK Ideograph-77C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c7 CJK Ideograph-77C7 | 77c6 CJK Ideograph-77C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77c9 CJK Ideograph-77C9 | 77c8 CJK Ideograph-77C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cb CJK Ideograph-77CB | 77ca CJK Ideograph-77CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cd CJK Ideograph-77CD | 77cc CJK Ideograph-77CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77cf CJK Ideograph-77CF | 77ce CJK Ideograph-77CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d1 CJK Ideograph-77D1 | 77d0 CJK Ideograph-77D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d3 CJK Ideograph-77D3 | 77d2 CJK Ideograph-77D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d5 CJK Ideograph-77D5 | 77d4 CJK Ideograph-77D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d7 CJK Ideograph-77D7 | 77d6 CJK Ideograph-77D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77d9 CJK Ideograph-77D9 | 77d8 CJK Ideograph-77D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77db CJK Ideograph-77DB | 77da CJK Ideograph-77DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77dd CJK Ideograph-77DD | 77dc CJK Ideograph-77DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77df CJK Ideograph-77DF | 77de CJK Ideograph-77DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e1 CJK Ideograph-77E1 | 77e0 CJK Ideograph-77E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e3 CJK Ideograph-77E3 | 77e2 CJK Ideograph-77E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e5 CJK Ideograph-77E5 | 77e4 CJK Ideograph-77E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e7 CJK Ideograph-77E7 | 77e6 CJK Ideograph-77E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77e9 CJK Ideograph-77E9 | 77e8 CJK Ideograph-77E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77eb CJK Ideograph-77EB | 77ea CJK Ideograph-77EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ed CJK Ideograph-77ED | 77ec CJK Ideograph-77EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ef CJK Ideograph-77EF | 77ee CJK Ideograph-77EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f1 CJK Ideograph-77F1 | 77f0 CJK Ideograph-77F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f3 CJK Ideograph-77F3 | 77f2 CJK Ideograph-77F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f5 CJK Ideograph-77F5 | 77f4 CJK Ideograph-77F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f7 CJK Ideograph-77F7 | 77f6 CJK Ideograph-77F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77f9 CJK Ideograph-77F9 | 77f8 CJK Ideograph-77F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77fb CJK Ideograph-77FB | 77fa CJK Ideograph-77FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77fd CJK Ideograph-77FD | 77fc CJK Ideograph-77FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 77ff CJK Ideograph-77FF | 77fe CJK Ideograph-77FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7801 CJK Ideograph-7801 | 7800 CJK Ideograph-7800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7803 CJK Ideograph-7803 | 7802 CJK Ideograph-7802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7805 CJK Ideograph-7805 | 7804 CJK Ideograph-7804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7807 CJK Ideograph-7807 | 7806 CJK Ideograph-7806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7809 CJK Ideograph-7809 | 7808 CJK Ideograph-7808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780b CJK Ideograph-780B | 780a CJK Ideograph-780A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780d CJK Ideograph-780D | 780c CJK Ideograph-780C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 780f CJK Ideograph-780F | 780e CJK Ideograph-780E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7811 CJK Ideograph-7811 | 7810 CJK Ideograph-7810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7813 CJK Ideograph-7813 | 7812 CJK Ideograph-7812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7815 CJK Ideograph-7815 | 7814 CJK Ideograph-7814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7817 CJK Ideograph-7817 | 7816 CJK Ideograph-7816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7819 CJK Ideograph-7819 | 7818 CJK Ideograph-7818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781b CJK Ideograph-781B | 781a CJK Ideograph-781A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781d CJK Ideograph-781D | 781c CJK Ideograph-781C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 781f CJK Ideograph-781F | 781e CJK Ideograph-781E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7821 CJK Ideograph-7821 | 7820 CJK Ideograph-7820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7823 CJK Ideograph-7823 | 7822 CJK Ideograph-7822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7825 CJK Ideograph-7825 | 7824 CJK Ideograph-7824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7827 CJK Ideograph-7827 | 7826 CJK Ideograph-7826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7829 CJK Ideograph-7829 | 7828 CJK Ideograph-7828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782b CJK Ideograph-782B | 782a CJK Ideograph-782A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782d CJK Ideograph-782D | 782c CJK Ideograph-782C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 782f CJK Ideograph-782F | 782e CJK Ideograph-782E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7831 CJK Ideograph-7831 | 7830 CJK Ideograph-7830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7833 CJK Ideograph-7833 | 7832 CJK Ideograph-7832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7835 CJK Ideograph-7835 | 7834 CJK Ideograph-7834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7837 CJK Ideograph-7837 | 7836 CJK Ideograph-7836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7839 CJK Ideograph-7839 | 7838 CJK Ideograph-7838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783b CJK Ideograph-783B | 783a CJK Ideograph-783A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783d CJK Ideograph-783D | 783c CJK Ideograph-783C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 783f CJK Ideograph-783F | 783e CJK Ideograph-783E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7841 CJK Ideograph-7841 | 7840 CJK Ideograph-7840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7843 CJK Ideograph-7843 | 7842 CJK Ideograph-7842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7845 CJK Ideograph-7845 | 7844 CJK Ideograph-7844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7847 CJK Ideograph-7847 | 7846 CJK Ideograph-7846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7849 CJK Ideograph-7849 | 7848 CJK Ideograph-7848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784b CJK Ideograph-784B | 784a CJK Ideograph-784A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784d CJK Ideograph-784D | 784c CJK Ideograph-784C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 784f CJK Ideograph-784F | 784e CJK Ideograph-784E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7851 CJK Ideograph-7851 | 7850 CJK Ideograph-7850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7853 CJK Ideograph-7853 | 7852 CJK Ideograph-7852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7855 CJK Ideograph-7855 | 7854 CJK Ideograph-7854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7857 CJK Ideograph-7857 | 7856 CJK Ideograph-7856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7859 CJK Ideograph-7859 | 7858 CJK Ideograph-7858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785b CJK Ideograph-785B | 785a CJK Ideograph-785A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785d CJK Ideograph-785D | 785c CJK Ideograph-785C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 785f CJK Ideograph-785F | 785e CJK Ideograph-785E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7861 CJK Ideograph-7861 | 7860 CJK Ideograph-7860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7863 CJK Ideograph-7863 | 7862 CJK Ideograph-7862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7865 CJK Ideograph-7865 | 7864 CJK Ideograph-7864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7867 CJK Ideograph-7867 | 7866 CJK Ideograph-7866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7869 CJK Ideograph-7869 | 7868 CJK Ideograph-7868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786b CJK Ideograph-786B | 786a CJK Ideograph-786A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786d CJK Ideograph-786D | 786c CJK Ideograph-786C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 786f CJK Ideograph-786F | 786e CJK Ideograph-786E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7871 CJK Ideograph-7871 | 7870 CJK Ideograph-7870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7873 CJK Ideograph-7873 | 7872 CJK Ideograph-7872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7875 CJK Ideograph-7875 | 7874 CJK Ideograph-7874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7877 CJK Ideograph-7877 | 7876 CJK Ideograph-7876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7879 CJK Ideograph-7879 | 7878 CJK Ideograph-7878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787b CJK Ideograph-787B | 787a CJK Ideograph-787A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787d CJK Ideograph-787D | 787c CJK Ideograph-787C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 787f CJK Ideograph-787F | 787e CJK Ideograph-787E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7881 CJK Ideograph-7881 | 7880 CJK Ideograph-7880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7883 CJK Ideograph-7883 | 7882 CJK Ideograph-7882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7885 CJK Ideograph-7885 | 7884 CJK Ideograph-7884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7887 CJK Ideograph-7887 | 7886 CJK Ideograph-7886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7889 CJK Ideograph-7889 | 7888 CJK Ideograph-7888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788b CJK Ideograph-788B | 788a CJK Ideograph-788A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788d CJK Ideograph-788D | 788c CJK Ideograph-788C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 788f CJK Ideograph-788F | 788e CJK Ideograph-788E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7891 CJK Ideograph-7891 | 7890 CJK Ideograph-7890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7893 CJK Ideograph-7893 | 7892 CJK Ideograph-7892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7895 CJK Ideograph-7895 | 7894 CJK Ideograph-7894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7897 CJK Ideograph-7897 | 7896 CJK Ideograph-7896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7899 CJK Ideograph-7899 | 7898 CJK Ideograph-7898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789b CJK Ideograph-789B | 789a CJK Ideograph-789A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789d CJK Ideograph-789D | 789c CJK Ideograph-789C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 789f CJK Ideograph-789F | 789e CJK Ideograph-789E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a1 CJK Ideograph-78A1 | 78a0 CJK Ideograph-78A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a3 CJK Ideograph-78A3 | 78a2 CJK Ideograph-78A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a5 CJK Ideograph-78A5 | 78a4 CJK Ideograph-78A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a7 CJK Ideograph-78A7 | 78a6 CJK Ideograph-78A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78a9 CJK Ideograph-78A9 | 78a8 CJK Ideograph-78A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ab CJK Ideograph-78AB | 78aa CJK Ideograph-78AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ad CJK Ideograph-78AD | 78ac CJK Ideograph-78AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78af CJK Ideograph-78AF | 78ae CJK Ideograph-78AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b1 CJK Ideograph-78B1 | 78b0 CJK Ideograph-78B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b3 CJK Ideograph-78B3 | 78b2 CJK Ideograph-78B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b5 CJK Ideograph-78B5 | 78b4 CJK Ideograph-78B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b7 CJK Ideograph-78B7 | 78b6 CJK Ideograph-78B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78b9 CJK Ideograph-78B9 | 78b8 CJK Ideograph-78B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bb CJK Ideograph-78BB | 78ba CJK Ideograph-78BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bd CJK Ideograph-78BD | 78bc CJK Ideograph-78BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78bf CJK Ideograph-78BF | 78be CJK Ideograph-78BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c1 CJK Ideograph-78C1 | 78c0 CJK Ideograph-78C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c3 CJK Ideograph-78C3 | 78c2 CJK Ideograph-78C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c5 CJK Ideograph-78C5 | 78c4 CJK Ideograph-78C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c7 CJK Ideograph-78C7 | 78c6 CJK Ideograph-78C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78c9 CJK Ideograph-78C9 | 78c8 CJK Ideograph-78C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cb CJK Ideograph-78CB | 78ca CJK Ideograph-78CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cd CJK Ideograph-78CD | 78cc CJK Ideograph-78CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78cf CJK Ideograph-78CF | 78ce CJK Ideograph-78CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d1 CJK Ideograph-78D1 | 78d0 CJK Ideograph-78D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d3 CJK Ideograph-78D3 | 78d2 CJK Ideograph-78D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d5 CJK Ideograph-78D5 | 78d4 CJK Ideograph-78D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d7 CJK Ideograph-78D7 | 78d6 CJK Ideograph-78D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78d9 CJK Ideograph-78D9 | 78d8 CJK Ideograph-78D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78db CJK Ideograph-78DB | 78da CJK Ideograph-78DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78dd CJK Ideograph-78DD | 78dc CJK Ideograph-78DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78df CJK Ideograph-78DF | 78de CJK Ideograph-78DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e1 CJK Ideograph-78E1 | 78e0 CJK Ideograph-78E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e3 CJK Ideograph-78E3 | 78e2 CJK Ideograph-78E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e5 CJK Ideograph-78E5 | 78e4 CJK Ideograph-78E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e7 CJK Ideograph-78E7 | 78e6 CJK Ideograph-78E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78e9 CJK Ideograph-78E9 | 78e8 CJK Ideograph-78E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78eb CJK Ideograph-78EB | 78ea CJK Ideograph-78EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ed CJK Ideograph-78ED | 78ec CJK Ideograph-78EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ef CJK Ideograph-78EF | 78ee CJK Ideograph-78EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f1 CJK Ideograph-78F1 | 78f0 CJK Ideograph-78F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f3 CJK Ideograph-78F3 | 78f2 CJK Ideograph-78F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f5 CJK Ideograph-78F5 | 78f4 CJK Ideograph-78F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f7 CJK Ideograph-78F7 | 78f6 CJK Ideograph-78F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78f9 CJK Ideograph-78F9 | 78f8 CJK Ideograph-78F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78fb CJK Ideograph-78FB | 78fa CJK Ideograph-78FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78fd CJK Ideograph-78FD | 78fc CJK Ideograph-78FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 78ff CJK Ideograph-78FF | 78fe CJK Ideograph-78FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7901 CJK Ideograph-7901 | 7900 CJK Ideograph-7900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7903 CJK Ideograph-7903 | 7902 CJK Ideograph-7902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7905 CJK Ideograph-7905 | 7904 CJK Ideograph-7904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7907 CJK Ideograph-7907 | 7906 CJK Ideograph-7906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7909 CJK Ideograph-7909 | 7908 CJK Ideograph-7908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790b CJK Ideograph-790B | 790a CJK Ideograph-790A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790d CJK Ideograph-790D | 790c CJK Ideograph-790C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 790f CJK Ideograph-790F | 790e CJK Ideograph-790E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7911 CJK Ideograph-7911 | 7910 CJK Ideograph-7910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7913 CJK Ideograph-7913 | 7912 CJK Ideograph-7912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7915 CJK Ideograph-7915 | 7914 CJK Ideograph-7914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7917 CJK Ideograph-7917 | 7916 CJK Ideograph-7916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7919 CJK Ideograph-7919 | 7918 CJK Ideograph-7918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791b CJK Ideograph-791B | 791a CJK Ideograph-791A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791d CJK Ideograph-791D | 791c CJK Ideograph-791C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 791f CJK Ideograph-791F | 791e CJK Ideograph-791E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7921 CJK Ideograph-7921 | 7920 CJK Ideograph-7920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7923 CJK Ideograph-7923 | 7922 CJK Ideograph-7922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7925 CJK Ideograph-7925 | 7924 CJK Ideograph-7924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7927 CJK Ideograph-7927 | 7926 CJK Ideograph-7926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7929 CJK Ideograph-7929 | 7928 CJK Ideograph-7928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792b CJK Ideograph-792B | 792a CJK Ideograph-792A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792d CJK Ideograph-792D | 792c CJK Ideograph-792C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 792f CJK Ideograph-792F | 792e CJK Ideograph-792E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7931 CJK Ideograph-7931 | 7930 CJK Ideograph-7930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7933 CJK Ideograph-7933 | 7932 CJK Ideograph-7932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7935 CJK Ideograph-7935 | 7934 CJK Ideograph-7934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7937 CJK Ideograph-7937 | 7936 CJK Ideograph-7936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7939 CJK Ideograph-7939 | 7938 CJK Ideograph-7938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793b CJK Ideograph-793B | 793a CJK Ideograph-793A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793d CJK Ideograph-793D | 793c CJK Ideograph-793C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 793f CJK Ideograph-793F | 793e CJK Ideograph-793E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7941 CJK Ideograph-7941 | 7940 CJK Ideograph-7940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7943 CJK Ideograph-7943 | 7942 CJK Ideograph-7942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7945 CJK Ideograph-7945 | 7944 CJK Ideograph-7944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7947 CJK Ideograph-7947 | 7946 CJK Ideograph-7946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7949 CJK Ideograph-7949 | 7948 CJK Ideograph-7948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794b CJK Ideograph-794B | 794a CJK Ideograph-794A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794d CJK Ideograph-794D | 794c CJK Ideograph-794C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 794f CJK Ideograph-794F | 794e CJK Ideograph-794E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7951 CJK Ideograph-7951 | 7950 CJK Ideograph-7950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7953 CJK Ideograph-7953 | 7952 CJK Ideograph-7952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7955 CJK Ideograph-7955 | 7954 CJK Ideograph-7954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7957 CJK Ideograph-7957 | 7956 CJK Ideograph-7956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7959 CJK Ideograph-7959 | 7958 CJK Ideograph-7958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795b CJK Ideograph-795B | 795a CJK Ideograph-795A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795d CJK Ideograph-795D | 795c CJK Ideograph-795C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 795f CJK Ideograph-795F | 795e CJK Ideograph-795E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7961 CJK Ideograph-7961 | 7960 CJK Ideograph-7960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7963 CJK Ideograph-7963 | 7962 CJK Ideograph-7962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7965 CJK Ideograph-7965 | 7964 CJK Ideograph-7964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7967 CJK Ideograph-7967 | 7966 CJK Ideograph-7966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7969 CJK Ideograph-7969 | 7968 CJK Ideograph-7968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796b CJK Ideograph-796B | 796a CJK Ideograph-796A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796d CJK Ideograph-796D | 796c CJK Ideograph-796C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 796f CJK Ideograph-796F | 796e CJK Ideograph-796E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7971 CJK Ideograph-7971 | 7970 CJK Ideograph-7970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7973 CJK Ideograph-7973 | 7972 CJK Ideograph-7972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7975 CJK Ideograph-7975 | 7974 CJK Ideograph-7974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7977 CJK Ideograph-7977 | 7976 CJK Ideograph-7976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7979 CJK Ideograph-7979 | 7978 CJK Ideograph-7978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797b CJK Ideograph-797B | 797a CJK Ideograph-797A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797d CJK Ideograph-797D | 797c CJK Ideograph-797C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 797f CJK Ideograph-797F | 797e CJK Ideograph-797E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7981 CJK Ideograph-7981 | 7980 CJK Ideograph-7980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7983 CJK Ideograph-7983 | 7982 CJK Ideograph-7982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7985 CJK Ideograph-7985 | 7984 CJK Ideograph-7984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7987 CJK Ideograph-7987 | 7986 CJK Ideograph-7986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7989 CJK Ideograph-7989 | 7988 CJK Ideograph-7988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798b CJK Ideograph-798B | 798a CJK Ideograph-798A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798d CJK Ideograph-798D | 798c CJK Ideograph-798C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 798f CJK Ideograph-798F | 798e CJK Ideograph-798E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7991 CJK Ideograph-7991 | 7990 CJK Ideograph-7990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7993 CJK Ideograph-7993 | 7992 CJK Ideograph-7992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7995 CJK Ideograph-7995 | 7994 CJK Ideograph-7994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7997 CJK Ideograph-7997 | 7996 CJK Ideograph-7996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7999 CJK Ideograph-7999 | 7998 CJK Ideograph-7998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799b CJK Ideograph-799B | 799a CJK Ideograph-799A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799d CJK Ideograph-799D | 799c CJK Ideograph-799C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 799f CJK Ideograph-799F | 799e CJK Ideograph-799E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a1 CJK Ideograph-79A1 | 79a0 CJK Ideograph-79A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a3 CJK Ideograph-79A3 | 79a2 CJK Ideograph-79A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a5 CJK Ideograph-79A5 | 79a4 CJK Ideograph-79A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a7 CJK Ideograph-79A7 | 79a6 CJK Ideograph-79A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79a9 CJK Ideograph-79A9 | 79a8 CJK Ideograph-79A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ab CJK Ideograph-79AB | 79aa CJK Ideograph-79AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ad CJK Ideograph-79AD | 79ac CJK Ideograph-79AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79af CJK Ideograph-79AF | 79ae CJK Ideograph-79AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b1 CJK Ideograph-79B1 | 79b0 CJK Ideograph-79B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b3 CJK Ideograph-79B3 | 79b2 CJK Ideograph-79B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b5 CJK Ideograph-79B5 | 79b4 CJK Ideograph-79B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b7 CJK Ideograph-79B7 | 79b6 CJK Ideograph-79B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79b9 CJK Ideograph-79B9 | 79b8 CJK Ideograph-79B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bb CJK Ideograph-79BB | 79ba CJK Ideograph-79BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bd CJK Ideograph-79BD | 79bc CJK Ideograph-79BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79bf CJK Ideograph-79BF | 79be CJK Ideograph-79BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c1 CJK Ideograph-79C1 | 79c0 CJK Ideograph-79C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c3 CJK Ideograph-79C3 | 79c2 CJK Ideograph-79C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c5 CJK Ideograph-79C5 | 79c4 CJK Ideograph-79C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c7 CJK Ideograph-79C7 | 79c6 CJK Ideograph-79C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79c9 CJK Ideograph-79C9 | 79c8 CJK Ideograph-79C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cb CJK Ideograph-79CB | 79ca CJK Ideograph-79CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cd CJK Ideograph-79CD | 79cc CJK Ideograph-79CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79cf CJK Ideograph-79CF | 79ce CJK Ideograph-79CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d1 CJK Ideograph-79D1 | 79d0 CJK Ideograph-79D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d3 CJK Ideograph-79D3 | 79d2 CJK Ideograph-79D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d5 CJK Ideograph-79D5 | 79d4 CJK Ideograph-79D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d7 CJK Ideograph-79D7 | 79d6 CJK Ideograph-79D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79d9 CJK Ideograph-79D9 | 79d8 CJK Ideograph-79D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79db CJK Ideograph-79DB | 79da CJK Ideograph-79DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79dd CJK Ideograph-79DD | 79dc CJK Ideograph-79DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79df CJK Ideograph-79DF | 79de CJK Ideograph-79DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e1 CJK Ideograph-79E1 | 79e0 CJK Ideograph-79E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e3 CJK Ideograph-79E3 | 79e2 CJK Ideograph-79E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e5 CJK Ideograph-79E5 | 79e4 CJK Ideograph-79E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e7 CJK Ideograph-79E7 | 79e6 CJK Ideograph-79E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79e9 CJK Ideograph-79E9 | 79e8 CJK Ideograph-79E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79eb CJK Ideograph-79EB | 79ea CJK Ideograph-79EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ed CJK Ideograph-79ED | 79ec CJK Ideograph-79EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ef CJK Ideograph-79EF | 79ee CJK Ideograph-79EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f1 CJK Ideograph-79F1 | 79f0 CJK Ideograph-79F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f3 CJK Ideograph-79F3 | 79f2 CJK Ideograph-79F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f5 CJK Ideograph-79F5 | 79f4 CJK Ideograph-79F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f7 CJK Ideograph-79F7 | 79f6 CJK Ideograph-79F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79f9 CJK Ideograph-79F9 | 79f8 CJK Ideograph-79F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79fb CJK Ideograph-79FB | 79fa CJK Ideograph-79FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79fd CJK Ideograph-79FD | 79fc CJK Ideograph-79FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 79ff CJK Ideograph-79FF | 79fe CJK Ideograph-79FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a01 CJK Ideograph-7A01 | 7a00 CJK Ideograph-7A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a03 CJK Ideograph-7A03 | 7a02 CJK Ideograph-7A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a05 CJK Ideograph-7A05 | 7a04 CJK Ideograph-7A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a07 CJK Ideograph-7A07 | 7a06 CJK Ideograph-7A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a09 CJK Ideograph-7A09 | 7a08 CJK Ideograph-7A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0b CJK Ideograph-7A0B | 7a0a CJK Ideograph-7A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0d CJK Ideograph-7A0D | 7a0c CJK Ideograph-7A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a0f CJK Ideograph-7A0F | 7a0e CJK Ideograph-7A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a11 CJK Ideograph-7A11 | 7a10 CJK Ideograph-7A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a13 CJK Ideograph-7A13 | 7a12 CJK Ideograph-7A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a15 CJK Ideograph-7A15 | 7a14 CJK Ideograph-7A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a17 CJK Ideograph-7A17 | 7a16 CJK Ideograph-7A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a19 CJK Ideograph-7A19 | 7a18 CJK Ideograph-7A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1b CJK Ideograph-7A1B | 7a1a CJK Ideograph-7A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1d CJK Ideograph-7A1D | 7a1c CJK Ideograph-7A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a1f CJK Ideograph-7A1F | 7a1e CJK Ideograph-7A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a21 CJK Ideograph-7A21 | 7a20 CJK Ideograph-7A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a23 CJK Ideograph-7A23 | 7a22 CJK Ideograph-7A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a25 CJK Ideograph-7A25 | 7a24 CJK Ideograph-7A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a27 CJK Ideograph-7A27 | 7a26 CJK Ideograph-7A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a29 CJK Ideograph-7A29 | 7a28 CJK Ideograph-7A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2b CJK Ideograph-7A2B | 7a2a CJK Ideograph-7A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2d CJK Ideograph-7A2D | 7a2c CJK Ideograph-7A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a2f CJK Ideograph-7A2F | 7a2e CJK Ideograph-7A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a31 CJK Ideograph-7A31 | 7a30 CJK Ideograph-7A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a33 CJK Ideograph-7A33 | 7a32 CJK Ideograph-7A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a35 CJK Ideograph-7A35 | 7a34 CJK Ideograph-7A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a37 CJK Ideograph-7A37 | 7a36 CJK Ideograph-7A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a39 CJK Ideograph-7A39 | 7a38 CJK Ideograph-7A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3b CJK Ideograph-7A3B | 7a3a CJK Ideograph-7A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3d CJK Ideograph-7A3D | 7a3c CJK Ideograph-7A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a3f CJK Ideograph-7A3F | 7a3e CJK Ideograph-7A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a41 CJK Ideograph-7A41 | 7a40 CJK Ideograph-7A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a43 CJK Ideograph-7A43 | 7a42 CJK Ideograph-7A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a45 CJK Ideograph-7A45 | 7a44 CJK Ideograph-7A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a47 CJK Ideograph-7A47 | 7a46 CJK Ideograph-7A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a49 CJK Ideograph-7A49 | 7a48 CJK Ideograph-7A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4b CJK Ideograph-7A4B | 7a4a CJK Ideograph-7A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4d CJK Ideograph-7A4D | 7a4c CJK Ideograph-7A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a4f CJK Ideograph-7A4F | 7a4e CJK Ideograph-7A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a51 CJK Ideograph-7A51 | 7a50 CJK Ideograph-7A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a53 CJK Ideograph-7A53 | 7a52 CJK Ideograph-7A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a55 CJK Ideograph-7A55 | 7a54 CJK Ideograph-7A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a57 CJK Ideograph-7A57 | 7a56 CJK Ideograph-7A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a59 CJK Ideograph-7A59 | 7a58 CJK Ideograph-7A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5b CJK Ideograph-7A5B | 7a5a CJK Ideograph-7A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5d CJK Ideograph-7A5D | 7a5c CJK Ideograph-7A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a5f CJK Ideograph-7A5F | 7a5e CJK Ideograph-7A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a61 CJK Ideograph-7A61 | 7a60 CJK Ideograph-7A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a63 CJK Ideograph-7A63 | 7a62 CJK Ideograph-7A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a65 CJK Ideograph-7A65 | 7a64 CJK Ideograph-7A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a67 CJK Ideograph-7A67 | 7a66 CJK Ideograph-7A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a69 CJK Ideograph-7A69 | 7a68 CJK Ideograph-7A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6b CJK Ideograph-7A6B | 7a6a CJK Ideograph-7A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6d CJK Ideograph-7A6D | 7a6c CJK Ideograph-7A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a6f CJK Ideograph-7A6F | 7a6e CJK Ideograph-7A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a71 CJK Ideograph-7A71 | 7a70 CJK Ideograph-7A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a73 CJK Ideograph-7A73 | 7a72 CJK Ideograph-7A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a75 CJK Ideograph-7A75 | 7a74 CJK Ideograph-7A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a77 CJK Ideograph-7A77 | 7a76 CJK Ideograph-7A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a79 CJK Ideograph-7A79 | 7a78 CJK Ideograph-7A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7b CJK Ideograph-7A7B | 7a7a CJK Ideograph-7A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7d CJK Ideograph-7A7D | 7a7c CJK Ideograph-7A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a7f CJK Ideograph-7A7F | 7a7e CJK Ideograph-7A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a81 CJK Ideograph-7A81 | 7a80 CJK Ideograph-7A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a83 CJK Ideograph-7A83 | 7a82 CJK Ideograph-7A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a85 CJK Ideograph-7A85 | 7a84 CJK Ideograph-7A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a87 CJK Ideograph-7A87 | 7a86 CJK Ideograph-7A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a89 CJK Ideograph-7A89 | 7a88 CJK Ideograph-7A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8b CJK Ideograph-7A8B | 7a8a CJK Ideograph-7A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8d CJK Ideograph-7A8D | 7a8c CJK Ideograph-7A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a8f CJK Ideograph-7A8F | 7a8e CJK Ideograph-7A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a91 CJK Ideograph-7A91 | 7a90 CJK Ideograph-7A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a93 CJK Ideograph-7A93 | 7a92 CJK Ideograph-7A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a95 CJK Ideograph-7A95 | 7a94 CJK Ideograph-7A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a97 CJK Ideograph-7A97 | 7a96 CJK Ideograph-7A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a99 CJK Ideograph-7A99 | 7a98 CJK Ideograph-7A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9b CJK Ideograph-7A9B | 7a9a CJK Ideograph-7A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9d CJK Ideograph-7A9D | 7a9c CJK Ideograph-7A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7a9f CJK Ideograph-7A9F | 7a9e CJK Ideograph-7A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa1 CJK Ideograph-7AA1 | 7aa0 CJK Ideograph-7AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa3 CJK Ideograph-7AA3 | 7aa2 CJK Ideograph-7AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa5 CJK Ideograph-7AA5 | 7aa4 CJK Ideograph-7AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa7 CJK Ideograph-7AA7 | 7aa6 CJK Ideograph-7AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aa9 CJK Ideograph-7AA9 | 7aa8 CJK Ideograph-7AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aab CJK Ideograph-7AAB | 7aaa CJK Ideograph-7AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aad CJK Ideograph-7AAD | 7aac CJK Ideograph-7AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aaf CJK Ideograph-7AAF | 7aae CJK Ideograph-7AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab1 CJK Ideograph-7AB1 | 7ab0 CJK Ideograph-7AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab3 CJK Ideograph-7AB3 | 7ab2 CJK Ideograph-7AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab5 CJK Ideograph-7AB5 | 7ab4 CJK Ideograph-7AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab7 CJK Ideograph-7AB7 | 7ab6 CJK Ideograph-7AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ab9 CJK Ideograph-7AB9 | 7ab8 CJK Ideograph-7AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abb CJK Ideograph-7ABB | 7aba CJK Ideograph-7ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abd CJK Ideograph-7ABD | 7abc CJK Ideograph-7ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7abf CJK Ideograph-7ABF | 7abe CJK Ideograph-7ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac1 CJK Ideograph-7AC1 | 7ac0 CJK Ideograph-7AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac3 CJK Ideograph-7AC3 | 7ac2 CJK Ideograph-7AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac5 CJK Ideograph-7AC5 | 7ac4 CJK Ideograph-7AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac7 CJK Ideograph-7AC7 | 7ac6 CJK Ideograph-7AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ac9 CJK Ideograph-7AC9 | 7ac8 CJK Ideograph-7AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acb CJK Ideograph-7ACB | 7aca CJK Ideograph-7ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acd CJK Ideograph-7ACD | 7acc CJK Ideograph-7ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7acf CJK Ideograph-7ACF | 7ace CJK Ideograph-7ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad1 CJK Ideograph-7AD1 | 7ad0 CJK Ideograph-7AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad3 CJK Ideograph-7AD3 | 7ad2 CJK Ideograph-7AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad5 CJK Ideograph-7AD5 | 7ad4 CJK Ideograph-7AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad7 CJK Ideograph-7AD7 | 7ad6 CJK Ideograph-7AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ad9 CJK Ideograph-7AD9 | 7ad8 CJK Ideograph-7AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7adb CJK Ideograph-7ADB | 7ada CJK Ideograph-7ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7add CJK Ideograph-7ADD | 7adc CJK Ideograph-7ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7adf CJK Ideograph-7ADF | 7ade CJK Ideograph-7ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae1 CJK Ideograph-7AE1 | 7ae0 CJK Ideograph-7AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae3 CJK Ideograph-7AE3 | 7ae2 CJK Ideograph-7AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae5 CJK Ideograph-7AE5 | 7ae4 CJK Ideograph-7AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae7 CJK Ideograph-7AE7 | 7ae6 CJK Ideograph-7AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ae9 CJK Ideograph-7AE9 | 7ae8 CJK Ideograph-7AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aeb CJK Ideograph-7AEB | 7aea CJK Ideograph-7AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aed CJK Ideograph-7AED | 7aec CJK Ideograph-7AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aef CJK Ideograph-7AEF | 7aee CJK Ideograph-7AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af1 CJK Ideograph-7AF1 | 7af0 CJK Ideograph-7AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af3 CJK Ideograph-7AF3 | 7af2 CJK Ideograph-7AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af5 CJK Ideograph-7AF5 | 7af4 CJK Ideograph-7AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af7 CJK Ideograph-7AF7 | 7af6 CJK Ideograph-7AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7af9 CJK Ideograph-7AF9 | 7af8 CJK Ideograph-7AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7afb CJK Ideograph-7AFB | 7afa CJK Ideograph-7AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7afd CJK Ideograph-7AFD | 7afc CJK Ideograph-7AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7aff CJK Ideograph-7AFF | 7afe CJK Ideograph-7AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b01 CJK Ideograph-7B01 | 7b00 CJK Ideograph-7B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b03 CJK Ideograph-7B03 | 7b02 CJK Ideograph-7B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b05 CJK Ideograph-7B05 | 7b04 CJK Ideograph-7B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b07 CJK Ideograph-7B07 | 7b06 CJK Ideograph-7B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b09 CJK Ideograph-7B09 | 7b08 CJK Ideograph-7B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0b CJK Ideograph-7B0B | 7b0a CJK Ideograph-7B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0d CJK Ideograph-7B0D | 7b0c CJK Ideograph-7B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b0f CJK Ideograph-7B0F | 7b0e CJK Ideograph-7B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b11 CJK Ideograph-7B11 | 7b10 CJK Ideograph-7B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b13 CJK Ideograph-7B13 | 7b12 CJK Ideograph-7B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b15 CJK Ideograph-7B15 | 7b14 CJK Ideograph-7B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b17 CJK Ideograph-7B17 | 7b16 CJK Ideograph-7B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b19 CJK Ideograph-7B19 | 7b18 CJK Ideograph-7B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1b CJK Ideograph-7B1B | 7b1a CJK Ideograph-7B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1d CJK Ideograph-7B1D | 7b1c CJK Ideograph-7B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b1f CJK Ideograph-7B1F | 7b1e CJK Ideograph-7B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b21 CJK Ideograph-7B21 | 7b20 CJK Ideograph-7B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b23 CJK Ideograph-7B23 | 7b22 CJK Ideograph-7B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b25 CJK Ideograph-7B25 | 7b24 CJK Ideograph-7B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b27 CJK Ideograph-7B27 | 7b26 CJK Ideograph-7B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b29 CJK Ideograph-7B29 | 7b28 CJK Ideograph-7B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2b CJK Ideograph-7B2B | 7b2a CJK Ideograph-7B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2d CJK Ideograph-7B2D | 7b2c CJK Ideograph-7B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b2f CJK Ideograph-7B2F | 7b2e CJK Ideograph-7B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b31 CJK Ideograph-7B31 | 7b30 CJK Ideograph-7B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b33 CJK Ideograph-7B33 | 7b32 CJK Ideograph-7B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b35 CJK Ideograph-7B35 | 7b34 CJK Ideograph-7B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b37 CJK Ideograph-7B37 | 7b36 CJK Ideograph-7B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b39 CJK Ideograph-7B39 | 7b38 CJK Ideograph-7B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3b CJK Ideograph-7B3B | 7b3a CJK Ideograph-7B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3d CJK Ideograph-7B3D | 7b3c CJK Ideograph-7B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b3f CJK Ideograph-7B3F | 7b3e CJK Ideograph-7B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b41 CJK Ideograph-7B41 | 7b40 CJK Ideograph-7B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b43 CJK Ideograph-7B43 | 7b42 CJK Ideograph-7B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b45 CJK Ideograph-7B45 | 7b44 CJK Ideograph-7B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b47 CJK Ideograph-7B47 | 7b46 CJK Ideograph-7B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b49 CJK Ideograph-7B49 | 7b48 CJK Ideograph-7B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4b CJK Ideograph-7B4B | 7b4a CJK Ideograph-7B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4d CJK Ideograph-7B4D | 7b4c CJK Ideograph-7B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b4f CJK Ideograph-7B4F | 7b4e CJK Ideograph-7B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b51 CJK Ideograph-7B51 | 7b50 CJK Ideograph-7B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b53 CJK Ideograph-7B53 | 7b52 CJK Ideograph-7B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b55 CJK Ideograph-7B55 | 7b54 CJK Ideograph-7B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b57 CJK Ideograph-7B57 | 7b56 CJK Ideograph-7B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b59 CJK Ideograph-7B59 | 7b58 CJK Ideograph-7B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5b CJK Ideograph-7B5B | 7b5a CJK Ideograph-7B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5d CJK Ideograph-7B5D | 7b5c CJK Ideograph-7B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b5f CJK Ideograph-7B5F | 7b5e CJK Ideograph-7B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b61 CJK Ideograph-7B61 | 7b60 CJK Ideograph-7B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b63 CJK Ideograph-7B63 | 7b62 CJK Ideograph-7B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b65 CJK Ideograph-7B65 | 7b64 CJK Ideograph-7B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b67 CJK Ideograph-7B67 | 7b66 CJK Ideograph-7B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b69 CJK Ideograph-7B69 | 7b68 CJK Ideograph-7B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6b CJK Ideograph-7B6B | 7b6a CJK Ideograph-7B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6d CJK Ideograph-7B6D | 7b6c CJK Ideograph-7B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b6f CJK Ideograph-7B6F | 7b6e CJK Ideograph-7B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b71 CJK Ideograph-7B71 | 7b70 CJK Ideograph-7B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b73 CJK Ideograph-7B73 | 7b72 CJK Ideograph-7B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b75 CJK Ideograph-7B75 | 7b74 CJK Ideograph-7B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b77 CJK Ideograph-7B77 | 7b76 CJK Ideograph-7B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b79 CJK Ideograph-7B79 | 7b78 CJK Ideograph-7B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7b CJK Ideograph-7B7B | 7b7a CJK Ideograph-7B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7d CJK Ideograph-7B7D | 7b7c CJK Ideograph-7B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b7f CJK Ideograph-7B7F | 7b7e CJK Ideograph-7B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b81 CJK Ideograph-7B81 | 7b80 CJK Ideograph-7B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b83 CJK Ideograph-7B83 | 7b82 CJK Ideograph-7B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b85 CJK Ideograph-7B85 | 7b84 CJK Ideograph-7B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b87 CJK Ideograph-7B87 | 7b86 CJK Ideograph-7B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b89 CJK Ideograph-7B89 | 7b88 CJK Ideograph-7B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8b CJK Ideograph-7B8B | 7b8a CJK Ideograph-7B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8d CJK Ideograph-7B8D | 7b8c CJK Ideograph-7B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b8f CJK Ideograph-7B8F | 7b8e CJK Ideograph-7B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b91 CJK Ideograph-7B91 | 7b90 CJK Ideograph-7B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b93 CJK Ideograph-7B93 | 7b92 CJK Ideograph-7B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b95 CJK Ideograph-7B95 | 7b94 CJK Ideograph-7B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b97 CJK Ideograph-7B97 | 7b96 CJK Ideograph-7B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b99 CJK Ideograph-7B99 | 7b98 CJK Ideograph-7B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9b CJK Ideograph-7B9B | 7b9a CJK Ideograph-7B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9d CJK Ideograph-7B9D | 7b9c CJK Ideograph-7B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7b9f CJK Ideograph-7B9F | 7b9e CJK Ideograph-7B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba1 CJK Ideograph-7BA1 | 7ba0 CJK Ideograph-7BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba3 CJK Ideograph-7BA3 | 7ba2 CJK Ideograph-7BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba5 CJK Ideograph-7BA5 | 7ba4 CJK Ideograph-7BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba7 CJK Ideograph-7BA7 | 7ba6 CJK Ideograph-7BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ba9 CJK Ideograph-7BA9 | 7ba8 CJK Ideograph-7BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bab CJK Ideograph-7BAB | 7baa CJK Ideograph-7BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bad CJK Ideograph-7BAD | 7bac CJK Ideograph-7BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7baf CJK Ideograph-7BAF | 7bae CJK Ideograph-7BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb1 CJK Ideograph-7BB1 | 7bb0 CJK Ideograph-7BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb3 CJK Ideograph-7BB3 | 7bb2 CJK Ideograph-7BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb5 CJK Ideograph-7BB5 | 7bb4 CJK Ideograph-7BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb7 CJK Ideograph-7BB7 | 7bb6 CJK Ideograph-7BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bb9 CJK Ideograph-7BB9 | 7bb8 CJK Ideograph-7BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbb CJK Ideograph-7BBB | 7bba CJK Ideograph-7BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbd CJK Ideograph-7BBD | 7bbc CJK Ideograph-7BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bbf CJK Ideograph-7BBF | 7bbe CJK Ideograph-7BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc1 CJK Ideograph-7BC1 | 7bc0 CJK Ideograph-7BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc3 CJK Ideograph-7BC3 | 7bc2 CJK Ideograph-7BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc5 CJK Ideograph-7BC5 | 7bc4 CJK Ideograph-7BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc7 CJK Ideograph-7BC7 | 7bc6 CJK Ideograph-7BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bc9 CJK Ideograph-7BC9 | 7bc8 CJK Ideograph-7BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcb CJK Ideograph-7BCB | 7bca CJK Ideograph-7BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcd CJK Ideograph-7BCD | 7bcc CJK Ideograph-7BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bcf CJK Ideograph-7BCF | 7bce CJK Ideograph-7BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd1 CJK Ideograph-7BD1 | 7bd0 CJK Ideograph-7BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd3 CJK Ideograph-7BD3 | 7bd2 CJK Ideograph-7BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd5 CJK Ideograph-7BD5 | 7bd4 CJK Ideograph-7BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd7 CJK Ideograph-7BD7 | 7bd6 CJK Ideograph-7BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bd9 CJK Ideograph-7BD9 | 7bd8 CJK Ideograph-7BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdb CJK Ideograph-7BDB | 7bda CJK Ideograph-7BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdd CJK Ideograph-7BDD | 7bdc CJK Ideograph-7BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bdf CJK Ideograph-7BDF | 7bde CJK Ideograph-7BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be1 CJK Ideograph-7BE1 | 7be0 CJK Ideograph-7BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be3 CJK Ideograph-7BE3 | 7be2 CJK Ideograph-7BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be5 CJK Ideograph-7BE5 | 7be4 CJK Ideograph-7BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be7 CJK Ideograph-7BE7 | 7be6 CJK Ideograph-7BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7be9 CJK Ideograph-7BE9 | 7be8 CJK Ideograph-7BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7beb CJK Ideograph-7BEB | 7bea CJK Ideograph-7BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bed CJK Ideograph-7BED | 7bec CJK Ideograph-7BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bef CJK Ideograph-7BEF | 7bee CJK Ideograph-7BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf1 CJK Ideograph-7BF1 | 7bf0 CJK Ideograph-7BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf3 CJK Ideograph-7BF3 | 7bf2 CJK Ideograph-7BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf5 CJK Ideograph-7BF5 | 7bf4 CJK Ideograph-7BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf7 CJK Ideograph-7BF7 | 7bf6 CJK Ideograph-7BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bf9 CJK Ideograph-7BF9 | 7bf8 CJK Ideograph-7BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bfb CJK Ideograph-7BFB | 7bfa CJK Ideograph-7BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bfd CJK Ideograph-7BFD | 7bfc CJK Ideograph-7BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7bff CJK Ideograph-7BFF | 7bfe CJK Ideograph-7BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c01 CJK Ideograph-7C01 | 7c00 CJK Ideograph-7C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c03 CJK Ideograph-7C03 | 7c02 CJK Ideograph-7C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c05 CJK Ideograph-7C05 | 7c04 CJK Ideograph-7C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c07 CJK Ideograph-7C07 | 7c06 CJK Ideograph-7C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c09 CJK Ideograph-7C09 | 7c08 CJK Ideograph-7C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0b CJK Ideograph-7C0B | 7c0a CJK Ideograph-7C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0d CJK Ideograph-7C0D | 7c0c CJK Ideograph-7C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c0f CJK Ideograph-7C0F | 7c0e CJK Ideograph-7C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c11 CJK Ideograph-7C11 | 7c10 CJK Ideograph-7C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c13 CJK Ideograph-7C13 | 7c12 CJK Ideograph-7C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c15 CJK Ideograph-7C15 | 7c14 CJK Ideograph-7C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c17 CJK Ideograph-7C17 | 7c16 CJK Ideograph-7C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c19 CJK Ideograph-7C19 | 7c18 CJK Ideograph-7C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1b CJK Ideograph-7C1B | 7c1a CJK Ideograph-7C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1d CJK Ideograph-7C1D | 7c1c CJK Ideograph-7C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c1f CJK Ideograph-7C1F | 7c1e CJK Ideograph-7C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c21 CJK Ideograph-7C21 | 7c20 CJK Ideograph-7C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c23 CJK Ideograph-7C23 | 7c22 CJK Ideograph-7C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c25 CJK Ideograph-7C25 | 7c24 CJK Ideograph-7C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c27 CJK Ideograph-7C27 | 7c26 CJK Ideograph-7C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c29 CJK Ideograph-7C29 | 7c28 CJK Ideograph-7C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2b CJK Ideograph-7C2B | 7c2a CJK Ideograph-7C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2d CJK Ideograph-7C2D | 7c2c CJK Ideograph-7C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c2f CJK Ideograph-7C2F | 7c2e CJK Ideograph-7C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c31 CJK Ideograph-7C31 | 7c30 CJK Ideograph-7C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c33 CJK Ideograph-7C33 | 7c32 CJK Ideograph-7C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c35 CJK Ideograph-7C35 | 7c34 CJK Ideograph-7C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c37 CJK Ideograph-7C37 | 7c36 CJK Ideograph-7C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c39 CJK Ideograph-7C39 | 7c38 CJK Ideograph-7C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3b CJK Ideograph-7C3B | 7c3a CJK Ideograph-7C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3d CJK Ideograph-7C3D | 7c3c CJK Ideograph-7C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c3f CJK Ideograph-7C3F | 7c3e CJK Ideograph-7C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c41 CJK Ideograph-7C41 | 7c40 CJK Ideograph-7C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c43 CJK Ideograph-7C43 | 7c42 CJK Ideograph-7C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c45 CJK Ideograph-7C45 | 7c44 CJK Ideograph-7C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c47 CJK Ideograph-7C47 | 7c46 CJK Ideograph-7C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c49 CJK Ideograph-7C49 | 7c48 CJK Ideograph-7C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4b CJK Ideograph-7C4B | 7c4a CJK Ideograph-7C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4d CJK Ideograph-7C4D | 7c4c CJK Ideograph-7C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c4f CJK Ideograph-7C4F | 7c4e CJK Ideograph-7C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c51 CJK Ideograph-7C51 | 7c50 CJK Ideograph-7C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c53 CJK Ideograph-7C53 | 7c52 CJK Ideograph-7C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c55 CJK Ideograph-7C55 | 7c54 CJK Ideograph-7C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c57 CJK Ideograph-7C57 | 7c56 CJK Ideograph-7C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c59 CJK Ideograph-7C59 | 7c58 CJK Ideograph-7C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5b CJK Ideograph-7C5B | 7c5a CJK Ideograph-7C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5d CJK Ideograph-7C5D | 7c5c CJK Ideograph-7C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c5f CJK Ideograph-7C5F | 7c5e CJK Ideograph-7C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c61 CJK Ideograph-7C61 | 7c60 CJK Ideograph-7C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c63 CJK Ideograph-7C63 | 7c62 CJK Ideograph-7C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c65 CJK Ideograph-7C65 | 7c64 CJK Ideograph-7C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c67 CJK Ideograph-7C67 | 7c66 CJK Ideograph-7C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c69 CJK Ideograph-7C69 | 7c68 CJK Ideograph-7C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6b CJK Ideograph-7C6B | 7c6a CJK Ideograph-7C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6d CJK Ideograph-7C6D | 7c6c CJK Ideograph-7C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c6f CJK Ideograph-7C6F | 7c6e CJK Ideograph-7C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c71 CJK Ideograph-7C71 | 7c70 CJK Ideograph-7C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c73 CJK Ideograph-7C73 | 7c72 CJK Ideograph-7C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c75 CJK Ideograph-7C75 | 7c74 CJK Ideograph-7C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c77 CJK Ideograph-7C77 | 7c76 CJK Ideograph-7C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c79 CJK Ideograph-7C79 | 7c78 CJK Ideograph-7C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7b CJK Ideograph-7C7B | 7c7a CJK Ideograph-7C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7d CJK Ideograph-7C7D | 7c7c CJK Ideograph-7C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c7f CJK Ideograph-7C7F | 7c7e CJK Ideograph-7C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c81 CJK Ideograph-7C81 | 7c80 CJK Ideograph-7C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c83 CJK Ideograph-7C83 | 7c82 CJK Ideograph-7C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c85 CJK Ideograph-7C85 | 7c84 CJK Ideograph-7C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c87 CJK Ideograph-7C87 | 7c86 CJK Ideograph-7C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c89 CJK Ideograph-7C89 | 7c88 CJK Ideograph-7C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8b CJK Ideograph-7C8B | 7c8a CJK Ideograph-7C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8d CJK Ideograph-7C8D | 7c8c CJK Ideograph-7C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c8f CJK Ideograph-7C8F | 7c8e CJK Ideograph-7C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c91 CJK Ideograph-7C91 | 7c90 CJK Ideograph-7C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c93 CJK Ideograph-7C93 | 7c92 CJK Ideograph-7C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c95 CJK Ideograph-7C95 | 7c94 CJK Ideograph-7C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c97 CJK Ideograph-7C97 | 7c96 CJK Ideograph-7C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c99 CJK Ideograph-7C99 | 7c98 CJK Ideograph-7C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9b CJK Ideograph-7C9B | 7c9a CJK Ideograph-7C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9d CJK Ideograph-7C9D | 7c9c CJK Ideograph-7C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7c9f CJK Ideograph-7C9F | 7c9e CJK Ideograph-7C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca1 CJK Ideograph-7CA1 | 7ca0 CJK Ideograph-7CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca3 CJK Ideograph-7CA3 | 7ca2 CJK Ideograph-7CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca5 CJK Ideograph-7CA5 | 7ca4 CJK Ideograph-7CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca7 CJK Ideograph-7CA7 | 7ca6 CJK Ideograph-7CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ca9 CJK Ideograph-7CA9 | 7ca8 CJK Ideograph-7CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cab CJK Ideograph-7CAB | 7caa CJK Ideograph-7CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cad CJK Ideograph-7CAD | 7cac CJK Ideograph-7CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7caf CJK Ideograph-7CAF | 7cae CJK Ideograph-7CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb1 CJK Ideograph-7CB1 | 7cb0 CJK Ideograph-7CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb3 CJK Ideograph-7CB3 | 7cb2 CJK Ideograph-7CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb5 CJK Ideograph-7CB5 | 7cb4 CJK Ideograph-7CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb7 CJK Ideograph-7CB7 | 7cb6 CJK Ideograph-7CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cb9 CJK Ideograph-7CB9 | 7cb8 CJK Ideograph-7CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbb CJK Ideograph-7CBB | 7cba CJK Ideograph-7CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbd CJK Ideograph-7CBD | 7cbc CJK Ideograph-7CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cbf CJK Ideograph-7CBF | 7cbe CJK Ideograph-7CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc1 CJK Ideograph-7CC1 | 7cc0 CJK Ideograph-7CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc3 CJK Ideograph-7CC3 | 7cc2 CJK Ideograph-7CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc5 CJK Ideograph-7CC5 | 7cc4 CJK Ideograph-7CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc7 CJK Ideograph-7CC7 | 7cc6 CJK Ideograph-7CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cc9 CJK Ideograph-7CC9 | 7cc8 CJK Ideograph-7CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccb CJK Ideograph-7CCB | 7cca CJK Ideograph-7CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccd CJK Ideograph-7CCD | 7ccc CJK Ideograph-7CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ccf CJK Ideograph-7CCF | 7cce CJK Ideograph-7CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd1 CJK Ideograph-7CD1 | 7cd0 CJK Ideograph-7CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd3 CJK Ideograph-7CD3 | 7cd2 CJK Ideograph-7CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd5 CJK Ideograph-7CD5 | 7cd4 CJK Ideograph-7CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd7 CJK Ideograph-7CD7 | 7cd6 CJK Ideograph-7CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cd9 CJK Ideograph-7CD9 | 7cd8 CJK Ideograph-7CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdb CJK Ideograph-7CDB | 7cda CJK Ideograph-7CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdd CJK Ideograph-7CDD | 7cdc CJK Ideograph-7CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cdf CJK Ideograph-7CDF | 7cde CJK Ideograph-7CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce1 CJK Ideograph-7CE1 | 7ce0 CJK Ideograph-7CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce3 CJK Ideograph-7CE3 | 7ce2 CJK Ideograph-7CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce5 CJK Ideograph-7CE5 | 7ce4 CJK Ideograph-7CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce7 CJK Ideograph-7CE7 | 7ce6 CJK Ideograph-7CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ce9 CJK Ideograph-7CE9 | 7ce8 CJK Ideograph-7CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ceb CJK Ideograph-7CEB | 7cea CJK Ideograph-7CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ced CJK Ideograph-7CED | 7cec CJK Ideograph-7CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cef CJK Ideograph-7CEF | 7cee CJK Ideograph-7CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf1 CJK Ideograph-7CF1 | 7cf0 CJK Ideograph-7CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf3 CJK Ideograph-7CF3 | 7cf2 CJK Ideograph-7CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf5 CJK Ideograph-7CF5 | 7cf4 CJK Ideograph-7CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf7 CJK Ideograph-7CF7 | 7cf6 CJK Ideograph-7CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cf9 CJK Ideograph-7CF9 | 7cf8 CJK Ideograph-7CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cfb CJK Ideograph-7CFB | 7cfa CJK Ideograph-7CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cfd CJK Ideograph-7CFD | 7cfc CJK Ideograph-7CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7cff CJK Ideograph-7CFF | 7cfe CJK Ideograph-7CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d01 CJK Ideograph-7D01 | 7d00 CJK Ideograph-7D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d03 CJK Ideograph-7D03 | 7d02 CJK Ideograph-7D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d05 CJK Ideograph-7D05 | 7d04 CJK Ideograph-7D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d07 CJK Ideograph-7D07 | 7d06 CJK Ideograph-7D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d09 CJK Ideograph-7D09 | 7d08 CJK Ideograph-7D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0b CJK Ideograph-7D0B | 7d0a CJK Ideograph-7D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0d CJK Ideograph-7D0D | 7d0c CJK Ideograph-7D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d0f CJK Ideograph-7D0F | 7d0e CJK Ideograph-7D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d11 CJK Ideograph-7D11 | 7d10 CJK Ideograph-7D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d13 CJK Ideograph-7D13 | 7d12 CJK Ideograph-7D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d15 CJK Ideograph-7D15 | 7d14 CJK Ideograph-7D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d17 CJK Ideograph-7D17 | 7d16 CJK Ideograph-7D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d19 CJK Ideograph-7D19 | 7d18 CJK Ideograph-7D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1b CJK Ideograph-7D1B | 7d1a CJK Ideograph-7D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1d CJK Ideograph-7D1D | 7d1c CJK Ideograph-7D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d1f CJK Ideograph-7D1F | 7d1e CJK Ideograph-7D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d21 CJK Ideograph-7D21 | 7d20 CJK Ideograph-7D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d23 CJK Ideograph-7D23 | 7d22 CJK Ideograph-7D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d25 CJK Ideograph-7D25 | 7d24 CJK Ideograph-7D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d27 CJK Ideograph-7D27 | 7d26 CJK Ideograph-7D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d29 CJK Ideograph-7D29 | 7d28 CJK Ideograph-7D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2b CJK Ideograph-7D2B | 7d2a CJK Ideograph-7D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2d CJK Ideograph-7D2D | 7d2c CJK Ideograph-7D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d2f CJK Ideograph-7D2F | 7d2e CJK Ideograph-7D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d31 CJK Ideograph-7D31 | 7d30 CJK Ideograph-7D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d33 CJK Ideograph-7D33 | 7d32 CJK Ideograph-7D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d35 CJK Ideograph-7D35 | 7d34 CJK Ideograph-7D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d37 CJK Ideograph-7D37 | 7d36 CJK Ideograph-7D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d39 CJK Ideograph-7D39 | 7d38 CJK Ideograph-7D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3b CJK Ideograph-7D3B | 7d3a CJK Ideograph-7D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3d CJK Ideograph-7D3D | 7d3c CJK Ideograph-7D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d3f CJK Ideograph-7D3F | 7d3e CJK Ideograph-7D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d41 CJK Ideograph-7D41 | 7d40 CJK Ideograph-7D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d43 CJK Ideograph-7D43 | 7d42 CJK Ideograph-7D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d45 CJK Ideograph-7D45 | 7d44 CJK Ideograph-7D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d47 CJK Ideograph-7D47 | 7d46 CJK Ideograph-7D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d49 CJK Ideograph-7D49 | 7d48 CJK Ideograph-7D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4b CJK Ideograph-7D4B | 7d4a CJK Ideograph-7D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4d CJK Ideograph-7D4D | 7d4c CJK Ideograph-7D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d4f CJK Ideograph-7D4F | 7d4e CJK Ideograph-7D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d51 CJK Ideograph-7D51 | 7d50 CJK Ideograph-7D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d53 CJK Ideograph-7D53 | 7d52 CJK Ideograph-7D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d55 CJK Ideograph-7D55 | 7d54 CJK Ideograph-7D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d57 CJK Ideograph-7D57 | 7d56 CJK Ideograph-7D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d59 CJK Ideograph-7D59 | 7d58 CJK Ideograph-7D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5b CJK Ideograph-7D5B | 7d5a CJK Ideograph-7D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5d CJK Ideograph-7D5D | 7d5c CJK Ideograph-7D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d5f CJK Ideograph-7D5F | 7d5e CJK Ideograph-7D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d61 CJK Ideograph-7D61 | 7d60 CJK Ideograph-7D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d63 CJK Ideograph-7D63 | 7d62 CJK Ideograph-7D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d65 CJK Ideograph-7D65 | 7d64 CJK Ideograph-7D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d67 CJK Ideograph-7D67 | 7d66 CJK Ideograph-7D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d69 CJK Ideograph-7D69 | 7d68 CJK Ideograph-7D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6b CJK Ideograph-7D6B | 7d6a CJK Ideograph-7D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6d CJK Ideograph-7D6D | 7d6c CJK Ideograph-7D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d6f CJK Ideograph-7D6F | 7d6e CJK Ideograph-7D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d71 CJK Ideograph-7D71 | 7d70 CJK Ideograph-7D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d73 CJK Ideograph-7D73 | 7d72 CJK Ideograph-7D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d75 CJK Ideograph-7D75 | 7d74 CJK Ideograph-7D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d77 CJK Ideograph-7D77 | 7d76 CJK Ideograph-7D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d79 CJK Ideograph-7D79 | 7d78 CJK Ideograph-7D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7b CJK Ideograph-7D7B | 7d7a CJK Ideograph-7D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7d CJK Ideograph-7D7D | 7d7c CJK Ideograph-7D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d7f CJK Ideograph-7D7F | 7d7e CJK Ideograph-7D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d81 CJK Ideograph-7D81 | 7d80 CJK Ideograph-7D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d83 CJK Ideograph-7D83 | 7d82 CJK Ideograph-7D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d85 CJK Ideograph-7D85 | 7d84 CJK Ideograph-7D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d87 CJK Ideograph-7D87 | 7d86 CJK Ideograph-7D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d89 CJK Ideograph-7D89 | 7d88 CJK Ideograph-7D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8b CJK Ideograph-7D8B | 7d8a CJK Ideograph-7D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8d CJK Ideograph-7D8D | 7d8c CJK Ideograph-7D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d8f CJK Ideograph-7D8F | 7d8e CJK Ideograph-7D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d91 CJK Ideograph-7D91 | 7d90 CJK Ideograph-7D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d93 CJK Ideograph-7D93 | 7d92 CJK Ideograph-7D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d95 CJK Ideograph-7D95 | 7d94 CJK Ideograph-7D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d97 CJK Ideograph-7D97 | 7d96 CJK Ideograph-7D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d99 CJK Ideograph-7D99 | 7d98 CJK Ideograph-7D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9b CJK Ideograph-7D9B | 7d9a CJK Ideograph-7D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9d CJK Ideograph-7D9D | 7d9c CJK Ideograph-7D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7d9f CJK Ideograph-7D9F | 7d9e CJK Ideograph-7D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da1 CJK Ideograph-7DA1 | 7da0 CJK Ideograph-7DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da3 CJK Ideograph-7DA3 | 7da2 CJK Ideograph-7DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da5 CJK Ideograph-7DA5 | 7da4 CJK Ideograph-7DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da7 CJK Ideograph-7DA7 | 7da6 CJK Ideograph-7DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7da9 CJK Ideograph-7DA9 | 7da8 CJK Ideograph-7DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dab CJK Ideograph-7DAB | 7daa CJK Ideograph-7DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dad CJK Ideograph-7DAD | 7dac CJK Ideograph-7DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7daf CJK Ideograph-7DAF | 7dae CJK Ideograph-7DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db1 CJK Ideograph-7DB1 | 7db0 CJK Ideograph-7DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db3 CJK Ideograph-7DB3 | 7db2 CJK Ideograph-7DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db5 CJK Ideograph-7DB5 | 7db4 CJK Ideograph-7DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db7 CJK Ideograph-7DB7 | 7db6 CJK Ideograph-7DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7db9 CJK Ideograph-7DB9 | 7db8 CJK Ideograph-7DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbb CJK Ideograph-7DBB | 7dba CJK Ideograph-7DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbd CJK Ideograph-7DBD | 7dbc CJK Ideograph-7DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dbf CJK Ideograph-7DBF | 7dbe CJK Ideograph-7DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc1 CJK Ideograph-7DC1 | 7dc0 CJK Ideograph-7DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc3 CJK Ideograph-7DC3 | 7dc2 CJK Ideograph-7DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc5 CJK Ideograph-7DC5 | 7dc4 CJK Ideograph-7DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc7 CJK Ideograph-7DC7 | 7dc6 CJK Ideograph-7DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dc9 CJK Ideograph-7DC9 | 7dc8 CJK Ideograph-7DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcb CJK Ideograph-7DCB | 7dca CJK Ideograph-7DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcd CJK Ideograph-7DCD | 7dcc CJK Ideograph-7DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dcf CJK Ideograph-7DCF | 7dce CJK Ideograph-7DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd1 CJK Ideograph-7DD1 | 7dd0 CJK Ideograph-7DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd3 CJK Ideograph-7DD3 | 7dd2 CJK Ideograph-7DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd5 CJK Ideograph-7DD5 | 7dd4 CJK Ideograph-7DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd7 CJK Ideograph-7DD7 | 7dd6 CJK Ideograph-7DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dd9 CJK Ideograph-7DD9 | 7dd8 CJK Ideograph-7DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddb CJK Ideograph-7DDB | 7dda CJK Ideograph-7DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddd CJK Ideograph-7DDD | 7ddc CJK Ideograph-7DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ddf CJK Ideograph-7DDF | 7dde CJK Ideograph-7DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de1 CJK Ideograph-7DE1 | 7de0 CJK Ideograph-7DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de3 CJK Ideograph-7DE3 | 7de2 CJK Ideograph-7DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de5 CJK Ideograph-7DE5 | 7de4 CJK Ideograph-7DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de7 CJK Ideograph-7DE7 | 7de6 CJK Ideograph-7DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7de9 CJK Ideograph-7DE9 | 7de8 CJK Ideograph-7DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7deb CJK Ideograph-7DEB | 7dea CJK Ideograph-7DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ded CJK Ideograph-7DED | 7dec CJK Ideograph-7DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7def CJK Ideograph-7DEF | 7dee CJK Ideograph-7DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df1 CJK Ideograph-7DF1 | 7df0 CJK Ideograph-7DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df3 CJK Ideograph-7DF3 | 7df2 CJK Ideograph-7DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df5 CJK Ideograph-7DF5 | 7df4 CJK Ideograph-7DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df7 CJK Ideograph-7DF7 | 7df6 CJK Ideograph-7DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7df9 CJK Ideograph-7DF9 | 7df8 CJK Ideograph-7DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dfb CJK Ideograph-7DFB | 7dfa CJK Ideograph-7DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dfd CJK Ideograph-7DFD | 7dfc CJK Ideograph-7DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7dff CJK Ideograph-7DFF | 7dfe CJK Ideograph-7DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e01 CJK Ideograph-7E01 | 7e00 CJK Ideograph-7E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e03 CJK Ideograph-7E03 | 7e02 CJK Ideograph-7E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e05 CJK Ideograph-7E05 | 7e04 CJK Ideograph-7E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e07 CJK Ideograph-7E07 | 7e06 CJK Ideograph-7E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e09 CJK Ideograph-7E09 | 7e08 CJK Ideograph-7E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0b CJK Ideograph-7E0B | 7e0a CJK Ideograph-7E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0d CJK Ideograph-7E0D | 7e0c CJK Ideograph-7E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e0f CJK Ideograph-7E0F | 7e0e CJK Ideograph-7E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e11 CJK Ideograph-7E11 | 7e10 CJK Ideograph-7E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e13 CJK Ideograph-7E13 | 7e12 CJK Ideograph-7E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e15 CJK Ideograph-7E15 | 7e14 CJK Ideograph-7E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e17 CJK Ideograph-7E17 | 7e16 CJK Ideograph-7E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e19 CJK Ideograph-7E19 | 7e18 CJK Ideograph-7E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1b CJK Ideograph-7E1B | 7e1a CJK Ideograph-7E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1d CJK Ideograph-7E1D | 7e1c CJK Ideograph-7E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e1f CJK Ideograph-7E1F | 7e1e CJK Ideograph-7E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e21 CJK Ideograph-7E21 | 7e20 CJK Ideograph-7E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e23 CJK Ideograph-7E23 | 7e22 CJK Ideograph-7E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e25 CJK Ideograph-7E25 | 7e24 CJK Ideograph-7E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e27 CJK Ideograph-7E27 | 7e26 CJK Ideograph-7E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e29 CJK Ideograph-7E29 | 7e28 CJK Ideograph-7E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2b CJK Ideograph-7E2B | 7e2a CJK Ideograph-7E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2d CJK Ideograph-7E2D | 7e2c CJK Ideograph-7E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e2f CJK Ideograph-7E2F | 7e2e CJK Ideograph-7E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e31 CJK Ideograph-7E31 | 7e30 CJK Ideograph-7E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e33 CJK Ideograph-7E33 | 7e32 CJK Ideograph-7E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e35 CJK Ideograph-7E35 | 7e34 CJK Ideograph-7E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e37 CJK Ideograph-7E37 | 7e36 CJK Ideograph-7E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e39 CJK Ideograph-7E39 | 7e38 CJK Ideograph-7E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3b CJK Ideograph-7E3B | 7e3a CJK Ideograph-7E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3d CJK Ideograph-7E3D | 7e3c CJK Ideograph-7E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e3f CJK Ideograph-7E3F | 7e3e CJK Ideograph-7E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e41 CJK Ideograph-7E41 | 7e40 CJK Ideograph-7E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e43 CJK Ideograph-7E43 | 7e42 CJK Ideograph-7E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e45 CJK Ideograph-7E45 | 7e44 CJK Ideograph-7E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e47 CJK Ideograph-7E47 | 7e46 CJK Ideograph-7E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e49 CJK Ideograph-7E49 | 7e48 CJK Ideograph-7E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4b CJK Ideograph-7E4B | 7e4a CJK Ideograph-7E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4d CJK Ideograph-7E4D | 7e4c CJK Ideograph-7E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e4f CJK Ideograph-7E4F | 7e4e CJK Ideograph-7E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e51 CJK Ideograph-7E51 | 7e50 CJK Ideograph-7E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e53 CJK Ideograph-7E53 | 7e52 CJK Ideograph-7E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e55 CJK Ideograph-7E55 | 7e54 CJK Ideograph-7E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e57 CJK Ideograph-7E57 | 7e56 CJK Ideograph-7E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e59 CJK Ideograph-7E59 | 7e58 CJK Ideograph-7E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5b CJK Ideograph-7E5B | 7e5a CJK Ideograph-7E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5d CJK Ideograph-7E5D | 7e5c CJK Ideograph-7E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e5f CJK Ideograph-7E5F | 7e5e CJK Ideograph-7E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e61 CJK Ideograph-7E61 | 7e60 CJK Ideograph-7E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e63 CJK Ideograph-7E63 | 7e62 CJK Ideograph-7E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e65 CJK Ideograph-7E65 | 7e64 CJK Ideograph-7E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e67 CJK Ideograph-7E67 | 7e66 CJK Ideograph-7E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e69 CJK Ideograph-7E69 | 7e68 CJK Ideograph-7E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6b CJK Ideograph-7E6B | 7e6a CJK Ideograph-7E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6d CJK Ideograph-7E6D | 7e6c CJK Ideograph-7E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e6f CJK Ideograph-7E6F | 7e6e CJK Ideograph-7E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e71 CJK Ideograph-7E71 | 7e70 CJK Ideograph-7E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e73 CJK Ideograph-7E73 | 7e72 CJK Ideograph-7E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e75 CJK Ideograph-7E75 | 7e74 CJK Ideograph-7E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e77 CJK Ideograph-7E77 | 7e76 CJK Ideograph-7E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e79 CJK Ideograph-7E79 | 7e78 CJK Ideograph-7E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7b CJK Ideograph-7E7B | 7e7a CJK Ideograph-7E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7d CJK Ideograph-7E7D | 7e7c CJK Ideograph-7E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e7f CJK Ideograph-7E7F | 7e7e CJK Ideograph-7E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e81 CJK Ideograph-7E81 | 7e80 CJK Ideograph-7E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e83 CJK Ideograph-7E83 | 7e82 CJK Ideograph-7E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e85 CJK Ideograph-7E85 | 7e84 CJK Ideograph-7E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e87 CJK Ideograph-7E87 | 7e86 CJK Ideograph-7E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e89 CJK Ideograph-7E89 | 7e88 CJK Ideograph-7E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8b CJK Ideograph-7E8B | 7e8a CJK Ideograph-7E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8d CJK Ideograph-7E8D | 7e8c CJK Ideograph-7E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e8f CJK Ideograph-7E8F | 7e8e CJK Ideograph-7E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e91 CJK Ideograph-7E91 | 7e90 CJK Ideograph-7E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e93 CJK Ideograph-7E93 | 7e92 CJK Ideograph-7E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e95 CJK Ideograph-7E95 | 7e94 CJK Ideograph-7E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e97 CJK Ideograph-7E97 | 7e96 CJK Ideograph-7E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e99 CJK Ideograph-7E99 | 7e98 CJK Ideograph-7E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9b CJK Ideograph-7E9B | 7e9a CJK Ideograph-7E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9d CJK Ideograph-7E9D | 7e9c CJK Ideograph-7E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7e9f CJK Ideograph-7E9F | 7e9e CJK Ideograph-7E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea1 CJK Ideograph-7EA1 | 7ea0 CJK Ideograph-7EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea3 CJK Ideograph-7EA3 | 7ea2 CJK Ideograph-7EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea5 CJK Ideograph-7EA5 | 7ea4 CJK Ideograph-7EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea7 CJK Ideograph-7EA7 | 7ea6 CJK Ideograph-7EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ea9 CJK Ideograph-7EA9 | 7ea8 CJK Ideograph-7EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eab CJK Ideograph-7EAB | 7eaa CJK Ideograph-7EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ead CJK Ideograph-7EAD | 7eac CJK Ideograph-7EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eaf CJK Ideograph-7EAF | 7eae CJK Ideograph-7EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb1 CJK Ideograph-7EB1 | 7eb0 CJK Ideograph-7EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb3 CJK Ideograph-7EB3 | 7eb2 CJK Ideograph-7EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb5 CJK Ideograph-7EB5 | 7eb4 CJK Ideograph-7EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb7 CJK Ideograph-7EB7 | 7eb6 CJK Ideograph-7EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eb9 CJK Ideograph-7EB9 | 7eb8 CJK Ideograph-7EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebb CJK Ideograph-7EBB | 7eba CJK Ideograph-7EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebd CJK Ideograph-7EBD | 7ebc CJK Ideograph-7EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ebf CJK Ideograph-7EBF | 7ebe CJK Ideograph-7EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec1 CJK Ideograph-7EC1 | 7ec0 CJK Ideograph-7EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec3 CJK Ideograph-7EC3 | 7ec2 CJK Ideograph-7EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec5 CJK Ideograph-7EC5 | 7ec4 CJK Ideograph-7EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec7 CJK Ideograph-7EC7 | 7ec6 CJK Ideograph-7EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ec9 CJK Ideograph-7EC9 | 7ec8 CJK Ideograph-7EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecb CJK Ideograph-7ECB | 7eca CJK Ideograph-7ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecd CJK Ideograph-7ECD | 7ecc CJK Ideograph-7ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ecf CJK Ideograph-7ECF | 7ece CJK Ideograph-7ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed1 CJK Ideograph-7ED1 | 7ed0 CJK Ideograph-7ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed3 CJK Ideograph-7ED3 | 7ed2 CJK Ideograph-7ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed5 CJK Ideograph-7ED5 | 7ed4 CJK Ideograph-7ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed7 CJK Ideograph-7ED7 | 7ed6 CJK Ideograph-7ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ed9 CJK Ideograph-7ED9 | 7ed8 CJK Ideograph-7ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edb CJK Ideograph-7EDB | 7eda CJK Ideograph-7EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edd CJK Ideograph-7EDD | 7edc CJK Ideograph-7EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7edf CJK Ideograph-7EDF | 7ede CJK Ideograph-7EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee1 CJK Ideograph-7EE1 | 7ee0 CJK Ideograph-7EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee3 CJK Ideograph-7EE3 | 7ee2 CJK Ideograph-7EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee5 CJK Ideograph-7EE5 | 7ee4 CJK Ideograph-7EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee7 CJK Ideograph-7EE7 | 7ee6 CJK Ideograph-7EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ee9 CJK Ideograph-7EE9 | 7ee8 CJK Ideograph-7EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eeb CJK Ideograph-7EEB | 7eea CJK Ideograph-7EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eed CJK Ideograph-7EED | 7eec CJK Ideograph-7EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eef CJK Ideograph-7EEF | 7eee CJK Ideograph-7EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef1 CJK Ideograph-7EF1 | 7ef0 CJK Ideograph-7EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef3 CJK Ideograph-7EF3 | 7ef2 CJK Ideograph-7EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef5 CJK Ideograph-7EF5 | 7ef4 CJK Ideograph-7EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef7 CJK Ideograph-7EF7 | 7ef6 CJK Ideograph-7EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ef9 CJK Ideograph-7EF9 | 7ef8 CJK Ideograph-7EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7efb CJK Ideograph-7EFB | 7efa CJK Ideograph-7EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7efd CJK Ideograph-7EFD | 7efc CJK Ideograph-7EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7eff CJK Ideograph-7EFF | 7efe CJK Ideograph-7EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f01 CJK Ideograph-7F01 | 7f00 CJK Ideograph-7F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f03 CJK Ideograph-7F03 | 7f02 CJK Ideograph-7F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f05 CJK Ideograph-7F05 | 7f04 CJK Ideograph-7F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f07 CJK Ideograph-7F07 | 7f06 CJK Ideograph-7F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f09 CJK Ideograph-7F09 | 7f08 CJK Ideograph-7F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0b CJK Ideograph-7F0B | 7f0a CJK Ideograph-7F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0d CJK Ideograph-7F0D | 7f0c CJK Ideograph-7F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f0f CJK Ideograph-7F0F | 7f0e CJK Ideograph-7F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f11 CJK Ideograph-7F11 | 7f10 CJK Ideograph-7F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f13 CJK Ideograph-7F13 | 7f12 CJK Ideograph-7F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f15 CJK Ideograph-7F15 | 7f14 CJK Ideograph-7F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f17 CJK Ideograph-7F17 | 7f16 CJK Ideograph-7F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f19 CJK Ideograph-7F19 | 7f18 CJK Ideograph-7F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1b CJK Ideograph-7F1B | 7f1a CJK Ideograph-7F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1d CJK Ideograph-7F1D | 7f1c CJK Ideograph-7F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f1f CJK Ideograph-7F1F | 7f1e CJK Ideograph-7F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f21 CJK Ideograph-7F21 | 7f20 CJK Ideograph-7F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f23 CJK Ideograph-7F23 | 7f22 CJK Ideograph-7F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f25 CJK Ideograph-7F25 | 7f24 CJK Ideograph-7F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f27 CJK Ideograph-7F27 | 7f26 CJK Ideograph-7F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f29 CJK Ideograph-7F29 | 7f28 CJK Ideograph-7F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2b CJK Ideograph-7F2B | 7f2a CJK Ideograph-7F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2d CJK Ideograph-7F2D | 7f2c CJK Ideograph-7F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f2f CJK Ideograph-7F2F | 7f2e CJK Ideograph-7F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f31 CJK Ideograph-7F31 | 7f30 CJK Ideograph-7F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f33 CJK Ideograph-7F33 | 7f32 CJK Ideograph-7F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f35 CJK Ideograph-7F35 | 7f34 CJK Ideograph-7F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f37 CJK Ideograph-7F37 | 7f36 CJK Ideograph-7F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f39 CJK Ideograph-7F39 | 7f38 CJK Ideograph-7F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3b CJK Ideograph-7F3B | 7f3a CJK Ideograph-7F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3d CJK Ideograph-7F3D | 7f3c CJK Ideograph-7F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f3f CJK Ideograph-7F3F | 7f3e CJK Ideograph-7F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f41 CJK Ideograph-7F41 | 7f40 CJK Ideograph-7F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f43 CJK Ideograph-7F43 | 7f42 CJK Ideograph-7F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f45 CJK Ideograph-7F45 | 7f44 CJK Ideograph-7F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f47 CJK Ideograph-7F47 | 7f46 CJK Ideograph-7F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f49 CJK Ideograph-7F49 | 7f48 CJK Ideograph-7F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4b CJK Ideograph-7F4B | 7f4a CJK Ideograph-7F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4d CJK Ideograph-7F4D | 7f4c CJK Ideograph-7F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f4f CJK Ideograph-7F4F | 7f4e CJK Ideograph-7F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f51 CJK Ideograph-7F51 | 7f50 CJK Ideograph-7F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f53 CJK Ideograph-7F53 | 7f52 CJK Ideograph-7F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f55 CJK Ideograph-7F55 | 7f54 CJK Ideograph-7F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f57 CJK Ideograph-7F57 | 7f56 CJK Ideograph-7F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f59 CJK Ideograph-7F59 | 7f58 CJK Ideograph-7F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5b CJK Ideograph-7F5B | 7f5a CJK Ideograph-7F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5d CJK Ideograph-7F5D | 7f5c CJK Ideograph-7F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f5f CJK Ideograph-7F5F | 7f5e CJK Ideograph-7F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f61 CJK Ideograph-7F61 | 7f60 CJK Ideograph-7F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f63 CJK Ideograph-7F63 | 7f62 CJK Ideograph-7F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f65 CJK Ideograph-7F65 | 7f64 CJK Ideograph-7F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f67 CJK Ideograph-7F67 | 7f66 CJK Ideograph-7F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f69 CJK Ideograph-7F69 | 7f68 CJK Ideograph-7F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6b CJK Ideograph-7F6B | 7f6a CJK Ideograph-7F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6d CJK Ideograph-7F6D | 7f6c CJK Ideograph-7F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f6f CJK Ideograph-7F6F | 7f6e CJK Ideograph-7F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f71 CJK Ideograph-7F71 | 7f70 CJK Ideograph-7F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f73 CJK Ideograph-7F73 | 7f72 CJK Ideograph-7F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f75 CJK Ideograph-7F75 | 7f74 CJK Ideograph-7F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f77 CJK Ideograph-7F77 | 7f76 CJK Ideograph-7F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f79 CJK Ideograph-7F79 | 7f78 CJK Ideograph-7F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7b CJK Ideograph-7F7B | 7f7a CJK Ideograph-7F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7d CJK Ideograph-7F7D | 7f7c CJK Ideograph-7F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f7f CJK Ideograph-7F7F | 7f7e CJK Ideograph-7F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f81 CJK Ideograph-7F81 | 7f80 CJK Ideograph-7F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f83 CJK Ideograph-7F83 | 7f82 CJK Ideograph-7F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f85 CJK Ideograph-7F85 | 7f84 CJK Ideograph-7F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f87 CJK Ideograph-7F87 | 7f86 CJK Ideograph-7F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f89 CJK Ideograph-7F89 | 7f88 CJK Ideograph-7F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8b CJK Ideograph-7F8B | 7f8a CJK Ideograph-7F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8d CJK Ideograph-7F8D | 7f8c CJK Ideograph-7F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f8f CJK Ideograph-7F8F | 7f8e CJK Ideograph-7F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f91 CJK Ideograph-7F91 | 7f90 CJK Ideograph-7F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f93 CJK Ideograph-7F93 | 7f92 CJK Ideograph-7F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f95 CJK Ideograph-7F95 | 7f94 CJK Ideograph-7F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f97 CJK Ideograph-7F97 | 7f96 CJK Ideograph-7F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f99 CJK Ideograph-7F99 | 7f98 CJK Ideograph-7F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9b CJK Ideograph-7F9B | 7f9a CJK Ideograph-7F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9d CJK Ideograph-7F9D | 7f9c CJK Ideograph-7F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7f9f CJK Ideograph-7F9F | 7f9e CJK Ideograph-7F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa1 CJK Ideograph-7FA1 | 7fa0 CJK Ideograph-7FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa3 CJK Ideograph-7FA3 | 7fa2 CJK Ideograph-7FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa5 CJK Ideograph-7FA5 | 7fa4 CJK Ideograph-7FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa7 CJK Ideograph-7FA7 | 7fa6 CJK Ideograph-7FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fa9 CJK Ideograph-7FA9 | 7fa8 CJK Ideograph-7FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fab CJK Ideograph-7FAB | 7faa CJK Ideograph-7FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fad CJK Ideograph-7FAD | 7fac CJK Ideograph-7FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7faf CJK Ideograph-7FAF | 7fae CJK Ideograph-7FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb1 CJK Ideograph-7FB1 | 7fb0 CJK Ideograph-7FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb3 CJK Ideograph-7FB3 | 7fb2 CJK Ideograph-7FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb5 CJK Ideograph-7FB5 | 7fb4 CJK Ideograph-7FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb7 CJK Ideograph-7FB7 | 7fb6 CJK Ideograph-7FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fb9 CJK Ideograph-7FB9 | 7fb8 CJK Ideograph-7FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbb CJK Ideograph-7FBB | 7fba CJK Ideograph-7FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbd CJK Ideograph-7FBD | 7fbc CJK Ideograph-7FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fbf CJK Ideograph-7FBF | 7fbe CJK Ideograph-7FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc1 CJK Ideograph-7FC1 | 7fc0 CJK Ideograph-7FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc3 CJK Ideograph-7FC3 | 7fc2 CJK Ideograph-7FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc5 CJK Ideograph-7FC5 | 7fc4 CJK Ideograph-7FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc7 CJK Ideograph-7FC7 | 7fc6 CJK Ideograph-7FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fc9 CJK Ideograph-7FC9 | 7fc8 CJK Ideograph-7FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcb CJK Ideograph-7FCB | 7fca CJK Ideograph-7FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcd CJK Ideograph-7FCD | 7fcc CJK Ideograph-7FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fcf CJK Ideograph-7FCF | 7fce CJK Ideograph-7FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd1 CJK Ideograph-7FD1 | 7fd0 CJK Ideograph-7FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd3 CJK Ideograph-7FD3 | 7fd2 CJK Ideograph-7FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd5 CJK Ideograph-7FD5 | 7fd4 CJK Ideograph-7FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd7 CJK Ideograph-7FD7 | 7fd6 CJK Ideograph-7FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fd9 CJK Ideograph-7FD9 | 7fd8 CJK Ideograph-7FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdb CJK Ideograph-7FDB | 7fda CJK Ideograph-7FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdd CJK Ideograph-7FDD | 7fdc CJK Ideograph-7FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fdf CJK Ideograph-7FDF | 7fde CJK Ideograph-7FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe1 CJK Ideograph-7FE1 | 7fe0 CJK Ideograph-7FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe3 CJK Ideograph-7FE3 | 7fe2 CJK Ideograph-7FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe5 CJK Ideograph-7FE5 | 7fe4 CJK Ideograph-7FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe7 CJK Ideograph-7FE7 | 7fe6 CJK Ideograph-7FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fe9 CJK Ideograph-7FE9 | 7fe8 CJK Ideograph-7FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7feb CJK Ideograph-7FEB | 7fea CJK Ideograph-7FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fed CJK Ideograph-7FED | 7fec CJK Ideograph-7FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fef CJK Ideograph-7FEF | 7fee CJK Ideograph-7FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff1 CJK Ideograph-7FF1 | 7ff0 CJK Ideograph-7FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff3 CJK Ideograph-7FF3 | 7ff2 CJK Ideograph-7FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff5 CJK Ideograph-7FF5 | 7ff4 CJK Ideograph-7FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff7 CJK Ideograph-7FF7 | 7ff6 CJK Ideograph-7FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ff9 CJK Ideograph-7FF9 | 7ff8 CJK Ideograph-7FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ffb CJK Ideograph-7FFB | 7ffa CJK Ideograph-7FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7ffd CJK Ideograph-7FFD | 7ffc CJK Ideograph-7FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 7fff CJK Ideograph-7FFF | 7ffe CJK Ideograph-7FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8001 CJK Ideograph-8001 | 8000 CJK Ideograph-8000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8003 CJK Ideograph-8003 | 8002 CJK Ideograph-8002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8005 CJK Ideograph-8005 | 8004 CJK Ideograph-8004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8007 CJK Ideograph-8007 | 8006 CJK Ideograph-8006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8009 CJK Ideograph-8009 | 8008 CJK Ideograph-8008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800b CJK Ideograph-800B | 800a CJK Ideograph-800A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800d CJK Ideograph-800D | 800c CJK Ideograph-800C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 800f CJK Ideograph-800F | 800e CJK Ideograph-800E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8011 CJK Ideograph-8011 | 8010 CJK Ideograph-8010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8013 CJK Ideograph-8013 | 8012 CJK Ideograph-8012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8015 CJK Ideograph-8015 | 8014 CJK Ideograph-8014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8017 CJK Ideograph-8017 | 8016 CJK Ideograph-8016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8019 CJK Ideograph-8019 | 8018 CJK Ideograph-8018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801b CJK Ideograph-801B | 801a CJK Ideograph-801A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801d CJK Ideograph-801D | 801c CJK Ideograph-801C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 801f CJK Ideograph-801F | 801e CJK Ideograph-801E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8021 CJK Ideograph-8021 | 8020 CJK Ideograph-8020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8023 CJK Ideograph-8023 | 8022 CJK Ideograph-8022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8025 CJK Ideograph-8025 | 8024 CJK Ideograph-8024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8027 CJK Ideograph-8027 | 8026 CJK Ideograph-8026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8029 CJK Ideograph-8029 | 8028 CJK Ideograph-8028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802b CJK Ideograph-802B | 802a CJK Ideograph-802A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802d CJK Ideograph-802D | 802c CJK Ideograph-802C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 802f CJK Ideograph-802F | 802e CJK Ideograph-802E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8031 CJK Ideograph-8031 | 8030 CJK Ideograph-8030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8033 CJK Ideograph-8033 | 8032 CJK Ideograph-8032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8035 CJK Ideograph-8035 | 8034 CJK Ideograph-8034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8037 CJK Ideograph-8037 | 8036 CJK Ideograph-8036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8039 CJK Ideograph-8039 | 8038 CJK Ideograph-8038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803b CJK Ideograph-803B | 803a CJK Ideograph-803A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803d CJK Ideograph-803D | 803c CJK Ideograph-803C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 803f CJK Ideograph-803F | 803e CJK Ideograph-803E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8041 CJK Ideograph-8041 | 8040 CJK Ideograph-8040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8043 CJK Ideograph-8043 | 8042 CJK Ideograph-8042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8045 CJK Ideograph-8045 | 8044 CJK Ideograph-8044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8047 CJK Ideograph-8047 | 8046 CJK Ideograph-8046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8049 CJK Ideograph-8049 | 8048 CJK Ideograph-8048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804b CJK Ideograph-804B | 804a CJK Ideograph-804A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804d CJK Ideograph-804D | 804c CJK Ideograph-804C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 804f CJK Ideograph-804F | 804e CJK Ideograph-804E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8051 CJK Ideograph-8051 | 8050 CJK Ideograph-8050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8053 CJK Ideograph-8053 | 8052 CJK Ideograph-8052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8055 CJK Ideograph-8055 | 8054 CJK Ideograph-8054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8057 CJK Ideograph-8057 | 8056 CJK Ideograph-8056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8059 CJK Ideograph-8059 | 8058 CJK Ideograph-8058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805b CJK Ideograph-805B | 805a CJK Ideograph-805A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805d CJK Ideograph-805D | 805c CJK Ideograph-805C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 805f CJK Ideograph-805F | 805e CJK Ideograph-805E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8061 CJK Ideograph-8061 | 8060 CJK Ideograph-8060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8063 CJK Ideograph-8063 | 8062 CJK Ideograph-8062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8065 CJK Ideograph-8065 | 8064 CJK Ideograph-8064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8067 CJK Ideograph-8067 | 8066 CJK Ideograph-8066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8069 CJK Ideograph-8069 | 8068 CJK Ideograph-8068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806b CJK Ideograph-806B | 806a CJK Ideograph-806A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806d CJK Ideograph-806D | 806c CJK Ideograph-806C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 806f CJK Ideograph-806F | 806e CJK Ideograph-806E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8071 CJK Ideograph-8071 | 8070 CJK Ideograph-8070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8073 CJK Ideograph-8073 | 8072 CJK Ideograph-8072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8075 CJK Ideograph-8075 | 8074 CJK Ideograph-8074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8077 CJK Ideograph-8077 | 8076 CJK Ideograph-8076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8079 CJK Ideograph-8079 | 8078 CJK Ideograph-8078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807b CJK Ideograph-807B | 807a CJK Ideograph-807A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807d CJK Ideograph-807D | 807c CJK Ideograph-807C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 807f CJK Ideograph-807F | 807e CJK Ideograph-807E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8081 CJK Ideograph-8081 | 8080 CJK Ideograph-8080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8083 CJK Ideograph-8083 | 8082 CJK Ideograph-8082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8085 CJK Ideograph-8085 | 8084 CJK Ideograph-8084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8087 CJK Ideograph-8087 | 8086 CJK Ideograph-8086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8089 CJK Ideograph-8089 | 8088 CJK Ideograph-8088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808b CJK Ideograph-808B | 808a CJK Ideograph-808A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808d CJK Ideograph-808D | 808c CJK Ideograph-808C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 808f CJK Ideograph-808F | 808e CJK Ideograph-808E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8091 CJK Ideograph-8091 | 8090 CJK Ideograph-8090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8093 CJK Ideograph-8093 | 8092 CJK Ideograph-8092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8095 CJK Ideograph-8095 | 8094 CJK Ideograph-8094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8097 CJK Ideograph-8097 | 8096 CJK Ideograph-8096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8099 CJK Ideograph-8099 | 8098 CJK Ideograph-8098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809b CJK Ideograph-809B | 809a CJK Ideograph-809A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809d CJK Ideograph-809D | 809c CJK Ideograph-809C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 809f CJK Ideograph-809F | 809e CJK Ideograph-809E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a1 CJK Ideograph-80A1 | 80a0 CJK Ideograph-80A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a3 CJK Ideograph-80A3 | 80a2 CJK Ideograph-80A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a5 CJK Ideograph-80A5 | 80a4 CJK Ideograph-80A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a7 CJK Ideograph-80A7 | 80a6 CJK Ideograph-80A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80a9 CJK Ideograph-80A9 | 80a8 CJK Ideograph-80A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ab CJK Ideograph-80AB | 80aa CJK Ideograph-80AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ad CJK Ideograph-80AD | 80ac CJK Ideograph-80AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80af CJK Ideograph-80AF | 80ae CJK Ideograph-80AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b1 CJK Ideograph-80B1 | 80b0 CJK Ideograph-80B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b3 CJK Ideograph-80B3 | 80b2 CJK Ideograph-80B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b5 CJK Ideograph-80B5 | 80b4 CJK Ideograph-80B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b7 CJK Ideograph-80B7 | 80b6 CJK Ideograph-80B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80b9 CJK Ideograph-80B9 | 80b8 CJK Ideograph-80B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bb CJK Ideograph-80BB | 80ba CJK Ideograph-80BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bd CJK Ideograph-80BD | 80bc CJK Ideograph-80BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80bf CJK Ideograph-80BF | 80be CJK Ideograph-80BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c1 CJK Ideograph-80C1 | 80c0 CJK Ideograph-80C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c3 CJK Ideograph-80C3 | 80c2 CJK Ideograph-80C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c5 CJK Ideograph-80C5 | 80c4 CJK Ideograph-80C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c7 CJK Ideograph-80C7 | 80c6 CJK Ideograph-80C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80c9 CJK Ideograph-80C9 | 80c8 CJK Ideograph-80C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cb CJK Ideograph-80CB | 80ca CJK Ideograph-80CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cd CJK Ideograph-80CD | 80cc CJK Ideograph-80CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80cf CJK Ideograph-80CF | 80ce CJK Ideograph-80CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d1 CJK Ideograph-80D1 | 80d0 CJK Ideograph-80D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d3 CJK Ideograph-80D3 | 80d2 CJK Ideograph-80D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d5 CJK Ideograph-80D5 | 80d4 CJK Ideograph-80D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d7 CJK Ideograph-80D7 | 80d6 CJK Ideograph-80D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80d9 CJK Ideograph-80D9 | 80d8 CJK Ideograph-80D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80db CJK Ideograph-80DB | 80da CJK Ideograph-80DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80dd CJK Ideograph-80DD | 80dc CJK Ideograph-80DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80df CJK Ideograph-80DF | 80de CJK Ideograph-80DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e1 CJK Ideograph-80E1 | 80e0 CJK Ideograph-80E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e3 CJK Ideograph-80E3 | 80e2 CJK Ideograph-80E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e5 CJK Ideograph-80E5 | 80e4 CJK Ideograph-80E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e7 CJK Ideograph-80E7 | 80e6 CJK Ideograph-80E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80e9 CJK Ideograph-80E9 | 80e8 CJK Ideograph-80E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80eb CJK Ideograph-80EB | 80ea CJK Ideograph-80EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ed CJK Ideograph-80ED | 80ec CJK Ideograph-80EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ef CJK Ideograph-80EF | 80ee CJK Ideograph-80EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f1 CJK Ideograph-80F1 | 80f0 CJK Ideograph-80F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f3 CJK Ideograph-80F3 | 80f2 CJK Ideograph-80F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f5 CJK Ideograph-80F5 | 80f4 CJK Ideograph-80F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f7 CJK Ideograph-80F7 | 80f6 CJK Ideograph-80F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80f9 CJK Ideograph-80F9 | 80f8 CJK Ideograph-80F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80fb CJK Ideograph-80FB | 80fa CJK Ideograph-80FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80fd CJK Ideograph-80FD | 80fc CJK Ideograph-80FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 80ff CJK Ideograph-80FF | 80fe CJK Ideograph-80FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8101 CJK Ideograph-8101 | 8100 CJK Ideograph-8100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8103 CJK Ideograph-8103 | 8102 CJK Ideograph-8102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8105 CJK Ideograph-8105 | 8104 CJK Ideograph-8104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8107 CJK Ideograph-8107 | 8106 CJK Ideograph-8106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8109 CJK Ideograph-8109 | 8108 CJK Ideograph-8108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810b CJK Ideograph-810B | 810a CJK Ideograph-810A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810d CJK Ideograph-810D | 810c CJK Ideograph-810C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 810f CJK Ideograph-810F | 810e CJK Ideograph-810E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8111 CJK Ideograph-8111 | 8110 CJK Ideograph-8110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8113 CJK Ideograph-8113 | 8112 CJK Ideograph-8112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8115 CJK Ideograph-8115 | 8114 CJK Ideograph-8114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8117 CJK Ideograph-8117 | 8116 CJK Ideograph-8116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8119 CJK Ideograph-8119 | 8118 CJK Ideograph-8118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811b CJK Ideograph-811B | 811a CJK Ideograph-811A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811d CJK Ideograph-811D | 811c CJK Ideograph-811C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 811f CJK Ideograph-811F | 811e CJK Ideograph-811E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8121 CJK Ideograph-8121 | 8120 CJK Ideograph-8120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8123 CJK Ideograph-8123 | 8122 CJK Ideograph-8122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8125 CJK Ideograph-8125 | 8124 CJK Ideograph-8124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8127 CJK Ideograph-8127 | 8126 CJK Ideograph-8126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8129 CJK Ideograph-8129 | 8128 CJK Ideograph-8128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812b CJK Ideograph-812B | 812a CJK Ideograph-812A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812d CJK Ideograph-812D | 812c CJK Ideograph-812C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 812f CJK Ideograph-812F | 812e CJK Ideograph-812E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8131 CJK Ideograph-8131 | 8130 CJK Ideograph-8130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8133 CJK Ideograph-8133 | 8132 CJK Ideograph-8132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8135 CJK Ideograph-8135 | 8134 CJK Ideograph-8134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8137 CJK Ideograph-8137 | 8136 CJK Ideograph-8136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8139 CJK Ideograph-8139 | 8138 CJK Ideograph-8138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813b CJK Ideograph-813B | 813a CJK Ideograph-813A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813d CJK Ideograph-813D | 813c CJK Ideograph-813C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 813f CJK Ideograph-813F | 813e CJK Ideograph-813E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8141 CJK Ideograph-8141 | 8140 CJK Ideograph-8140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8143 CJK Ideograph-8143 | 8142 CJK Ideograph-8142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8145 CJK Ideograph-8145 | 8144 CJK Ideograph-8144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8147 CJK Ideograph-8147 | 8146 CJK Ideograph-8146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8149 CJK Ideograph-8149 | 8148 CJK Ideograph-8148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814b CJK Ideograph-814B | 814a CJK Ideograph-814A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814d CJK Ideograph-814D | 814c CJK Ideograph-814C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 814f CJK Ideograph-814F | 814e CJK Ideograph-814E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8151 CJK Ideograph-8151 | 8150 CJK Ideograph-8150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8153 CJK Ideograph-8153 | 8152 CJK Ideograph-8152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8155 CJK Ideograph-8155 | 8154 CJK Ideograph-8154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8157 CJK Ideograph-8157 | 8156 CJK Ideograph-8156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8159 CJK Ideograph-8159 | 8158 CJK Ideograph-8158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815b CJK Ideograph-815B | 815a CJK Ideograph-815A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815d CJK Ideograph-815D | 815c CJK Ideograph-815C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 815f CJK Ideograph-815F | 815e CJK Ideograph-815E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8161 CJK Ideograph-8161 | 8160 CJK Ideograph-8160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8163 CJK Ideograph-8163 | 8162 CJK Ideograph-8162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8165 CJK Ideograph-8165 | 8164 CJK Ideograph-8164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8167 CJK Ideograph-8167 | 8166 CJK Ideograph-8166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8169 CJK Ideograph-8169 | 8168 CJK Ideograph-8168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816b CJK Ideograph-816B | 816a CJK Ideograph-816A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816d CJK Ideograph-816D | 816c CJK Ideograph-816C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 816f CJK Ideograph-816F | 816e CJK Ideograph-816E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8171 CJK Ideograph-8171 | 8170 CJK Ideograph-8170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8173 CJK Ideograph-8173 | 8172 CJK Ideograph-8172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8175 CJK Ideograph-8175 | 8174 CJK Ideograph-8174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8177 CJK Ideograph-8177 | 8176 CJK Ideograph-8176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8179 CJK Ideograph-8179 | 8178 CJK Ideograph-8178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817b CJK Ideograph-817B | 817a CJK Ideograph-817A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817d CJK Ideograph-817D | 817c CJK Ideograph-817C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 817f CJK Ideograph-817F | 817e CJK Ideograph-817E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8181 CJK Ideograph-8181 | 8180 CJK Ideograph-8180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8183 CJK Ideograph-8183 | 8182 CJK Ideograph-8182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8185 CJK Ideograph-8185 | 8184 CJK Ideograph-8184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8187 CJK Ideograph-8187 | 8186 CJK Ideograph-8186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8189 CJK Ideograph-8189 | 8188 CJK Ideograph-8188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818b CJK Ideograph-818B | 818a CJK Ideograph-818A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818d CJK Ideograph-818D | 818c CJK Ideograph-818C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 818f CJK Ideograph-818F | 818e CJK Ideograph-818E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8191 CJK Ideograph-8191 | 8190 CJK Ideograph-8190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8193 CJK Ideograph-8193 | 8192 CJK Ideograph-8192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8195 CJK Ideograph-8195 | 8194 CJK Ideograph-8194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8197 CJK Ideograph-8197 | 8196 CJK Ideograph-8196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8199 CJK Ideograph-8199 | 8198 CJK Ideograph-8198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819b CJK Ideograph-819B | 819a CJK Ideograph-819A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819d CJK Ideograph-819D | 819c CJK Ideograph-819C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 819f CJK Ideograph-819F | 819e CJK Ideograph-819E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a1 CJK Ideograph-81A1 | 81a0 CJK Ideograph-81A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a3 CJK Ideograph-81A3 | 81a2 CJK Ideograph-81A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a5 CJK Ideograph-81A5 | 81a4 CJK Ideograph-81A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a7 CJK Ideograph-81A7 | 81a6 CJK Ideograph-81A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81a9 CJK Ideograph-81A9 | 81a8 CJK Ideograph-81A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ab CJK Ideograph-81AB | 81aa CJK Ideograph-81AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ad CJK Ideograph-81AD | 81ac CJK Ideograph-81AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81af CJK Ideograph-81AF | 81ae CJK Ideograph-81AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b1 CJK Ideograph-81B1 | 81b0 CJK Ideograph-81B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b3 CJK Ideograph-81B3 | 81b2 CJK Ideograph-81B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b5 CJK Ideograph-81B5 | 81b4 CJK Ideograph-81B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b7 CJK Ideograph-81B7 | 81b6 CJK Ideograph-81B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81b9 CJK Ideograph-81B9 | 81b8 CJK Ideograph-81B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bb CJK Ideograph-81BB | 81ba CJK Ideograph-81BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bd CJK Ideograph-81BD | 81bc CJK Ideograph-81BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81bf CJK Ideograph-81BF | 81be CJK Ideograph-81BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c1 CJK Ideograph-81C1 | 81c0 CJK Ideograph-81C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c3 CJK Ideograph-81C3 | 81c2 CJK Ideograph-81C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c5 CJK Ideograph-81C5 | 81c4 CJK Ideograph-81C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c7 CJK Ideograph-81C7 | 81c6 CJK Ideograph-81C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81c9 CJK Ideograph-81C9 | 81c8 CJK Ideograph-81C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cb CJK Ideograph-81CB | 81ca CJK Ideograph-81CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cd CJK Ideograph-81CD | 81cc CJK Ideograph-81CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81cf CJK Ideograph-81CF | 81ce CJK Ideograph-81CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d1 CJK Ideograph-81D1 | 81d0 CJK Ideograph-81D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d3 CJK Ideograph-81D3 | 81d2 CJK Ideograph-81D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d5 CJK Ideograph-81D5 | 81d4 CJK Ideograph-81D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d7 CJK Ideograph-81D7 | 81d6 CJK Ideograph-81D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81d9 CJK Ideograph-81D9 | 81d8 CJK Ideograph-81D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81db CJK Ideograph-81DB | 81da CJK Ideograph-81DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81dd CJK Ideograph-81DD | 81dc CJK Ideograph-81DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81df CJK Ideograph-81DF | 81de CJK Ideograph-81DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e1 CJK Ideograph-81E1 | 81e0 CJK Ideograph-81E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e3 CJK Ideograph-81E3 | 81e2 CJK Ideograph-81E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e5 CJK Ideograph-81E5 | 81e4 CJK Ideograph-81E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e7 CJK Ideograph-81E7 | 81e6 CJK Ideograph-81E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81e9 CJK Ideograph-81E9 | 81e8 CJK Ideograph-81E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81eb CJK Ideograph-81EB | 81ea CJK Ideograph-81EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ed CJK Ideograph-81ED | 81ec CJK Ideograph-81EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ef CJK Ideograph-81EF | 81ee CJK Ideograph-81EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f1 CJK Ideograph-81F1 | 81f0 CJK Ideograph-81F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f3 CJK Ideograph-81F3 | 81f2 CJK Ideograph-81F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f5 CJK Ideograph-81F5 | 81f4 CJK Ideograph-81F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f7 CJK Ideograph-81F7 | 81f6 CJK Ideograph-81F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81f9 CJK Ideograph-81F9 | 81f8 CJK Ideograph-81F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81fb CJK Ideograph-81FB | 81fa CJK Ideograph-81FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81fd CJK Ideograph-81FD | 81fc CJK Ideograph-81FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 81ff CJK Ideograph-81FF | 81fe CJK Ideograph-81FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8201 CJK Ideograph-8201 | 8200 CJK Ideograph-8200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8203 CJK Ideograph-8203 | 8202 CJK Ideograph-8202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8205 CJK Ideograph-8205 | 8204 CJK Ideograph-8204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8207 CJK Ideograph-8207 | 8206 CJK Ideograph-8206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8209 CJK Ideograph-8209 | 8208 CJK Ideograph-8208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820b CJK Ideograph-820B | 820a CJK Ideograph-820A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820d CJK Ideograph-820D | 820c CJK Ideograph-820C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 820f CJK Ideograph-820F | 820e CJK Ideograph-820E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8211 CJK Ideograph-8211 | 8210 CJK Ideograph-8210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8213 CJK Ideograph-8213 | 8212 CJK Ideograph-8212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8215 CJK Ideograph-8215 | 8214 CJK Ideograph-8214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8217 CJK Ideograph-8217 | 8216 CJK Ideograph-8216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8219 CJK Ideograph-8219 | 8218 CJK Ideograph-8218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821b CJK Ideograph-821B | 821a CJK Ideograph-821A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821d CJK Ideograph-821D | 821c CJK Ideograph-821C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 821f CJK Ideograph-821F | 821e CJK Ideograph-821E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8221 CJK Ideograph-8221 | 8220 CJK Ideograph-8220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8223 CJK Ideograph-8223 | 8222 CJK Ideograph-8222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8225 CJK Ideograph-8225 | 8224 CJK Ideograph-8224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8227 CJK Ideograph-8227 | 8226 CJK Ideograph-8226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8229 CJK Ideograph-8229 | 8228 CJK Ideograph-8228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822b CJK Ideograph-822B | 822a CJK Ideograph-822A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822d CJK Ideograph-822D | 822c CJK Ideograph-822C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 822f CJK Ideograph-822F | 822e CJK Ideograph-822E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8231 CJK Ideograph-8231 | 8230 CJK Ideograph-8230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8233 CJK Ideograph-8233 | 8232 CJK Ideograph-8232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8235 CJK Ideograph-8235 | 8234 CJK Ideograph-8234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8237 CJK Ideograph-8237 | 8236 CJK Ideograph-8236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8239 CJK Ideograph-8239 | 8238 CJK Ideograph-8238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823b CJK Ideograph-823B | 823a CJK Ideograph-823A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823d CJK Ideograph-823D | 823c CJK Ideograph-823C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 823f CJK Ideograph-823F | 823e CJK Ideograph-823E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8241 CJK Ideograph-8241 | 8240 CJK Ideograph-8240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8243 CJK Ideograph-8243 | 8242 CJK Ideograph-8242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8245 CJK Ideograph-8245 | 8244 CJK Ideograph-8244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8247 CJK Ideograph-8247 | 8246 CJK Ideograph-8246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8249 CJK Ideograph-8249 | 8248 CJK Ideograph-8248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824b CJK Ideograph-824B | 824a CJK Ideograph-824A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824d CJK Ideograph-824D | 824c CJK Ideograph-824C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 824f CJK Ideograph-824F | 824e CJK Ideograph-824E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8251 CJK Ideograph-8251 | 8250 CJK Ideograph-8250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8253 CJK Ideograph-8253 | 8252 CJK Ideograph-8252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8255 CJK Ideograph-8255 | 8254 CJK Ideograph-8254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8257 CJK Ideograph-8257 | 8256 CJK Ideograph-8256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8259 CJK Ideograph-8259 | 8258 CJK Ideograph-8258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825b CJK Ideograph-825B | 825a CJK Ideograph-825A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825d CJK Ideograph-825D | 825c CJK Ideograph-825C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 825f CJK Ideograph-825F | 825e CJK Ideograph-825E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8261 CJK Ideograph-8261 | 8260 CJK Ideograph-8260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8263 CJK Ideograph-8263 | 8262 CJK Ideograph-8262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8265 CJK Ideograph-8265 | 8264 CJK Ideograph-8264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8267 CJK Ideograph-8267 | 8266 CJK Ideograph-8266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8269 CJK Ideograph-8269 | 8268 CJK Ideograph-8268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826b CJK Ideograph-826B | 826a CJK Ideograph-826A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826d CJK Ideograph-826D | 826c CJK Ideograph-826C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 826f CJK Ideograph-826F | 826e CJK Ideograph-826E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8271 CJK Ideograph-8271 | 8270 CJK Ideograph-8270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8273 CJK Ideograph-8273 | 8272 CJK Ideograph-8272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8275 CJK Ideograph-8275 | 8274 CJK Ideograph-8274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8277 CJK Ideograph-8277 | 8276 CJK Ideograph-8276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8279 CJK Ideograph-8279 | 8278 CJK Ideograph-8278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827b CJK Ideograph-827B | 827a CJK Ideograph-827A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827d CJK Ideograph-827D | 827c CJK Ideograph-827C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 827f CJK Ideograph-827F | 827e CJK Ideograph-827E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8281 CJK Ideograph-8281 | 8280 CJK Ideograph-8280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8283 CJK Ideograph-8283 | 8282 CJK Ideograph-8282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8285 CJK Ideograph-8285 | 8284 CJK Ideograph-8284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8287 CJK Ideograph-8287 | 8286 CJK Ideograph-8286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8289 CJK Ideograph-8289 | 8288 CJK Ideograph-8288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828b CJK Ideograph-828B | 828a CJK Ideograph-828A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828d CJK Ideograph-828D | 828c CJK Ideograph-828C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 828f CJK Ideograph-828F | 828e CJK Ideograph-828E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8291 CJK Ideograph-8291 | 8290 CJK Ideograph-8290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8293 CJK Ideograph-8293 | 8292 CJK Ideograph-8292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8295 CJK Ideograph-8295 | 8294 CJK Ideograph-8294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8297 CJK Ideograph-8297 | 8296 CJK Ideograph-8296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8299 CJK Ideograph-8299 | 8298 CJK Ideograph-8298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829b CJK Ideograph-829B | 829a CJK Ideograph-829A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829d CJK Ideograph-829D | 829c CJK Ideograph-829C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 829f CJK Ideograph-829F | 829e CJK Ideograph-829E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a1 CJK Ideograph-82A1 | 82a0 CJK Ideograph-82A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a3 CJK Ideograph-82A3 | 82a2 CJK Ideograph-82A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a5 CJK Ideograph-82A5 | 82a4 CJK Ideograph-82A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a7 CJK Ideograph-82A7 | 82a6 CJK Ideograph-82A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82a9 CJK Ideograph-82A9 | 82a8 CJK Ideograph-82A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ab CJK Ideograph-82AB | 82aa CJK Ideograph-82AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ad CJK Ideograph-82AD | 82ac CJK Ideograph-82AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82af CJK Ideograph-82AF | 82ae CJK Ideograph-82AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b1 CJK Ideograph-82B1 | 82b0 CJK Ideograph-82B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b3 CJK Ideograph-82B3 | 82b2 CJK Ideograph-82B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b5 CJK Ideograph-82B5 | 82b4 CJK Ideograph-82B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b7 CJK Ideograph-82B7 | 82b6 CJK Ideograph-82B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82b9 CJK Ideograph-82B9 | 82b8 CJK Ideograph-82B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bb CJK Ideograph-82BB | 82ba CJK Ideograph-82BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bd CJK Ideograph-82BD | 82bc CJK Ideograph-82BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82bf CJK Ideograph-82BF | 82be CJK Ideograph-82BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c1 CJK Ideograph-82C1 | 82c0 CJK Ideograph-82C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c3 CJK Ideograph-82C3 | 82c2 CJK Ideograph-82C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c5 CJK Ideograph-82C5 | 82c4 CJK Ideograph-82C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c7 CJK Ideograph-82C7 | 82c6 CJK Ideograph-82C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82c9 CJK Ideograph-82C9 | 82c8 CJK Ideograph-82C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cb CJK Ideograph-82CB | 82ca CJK Ideograph-82CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cd CJK Ideograph-82CD | 82cc CJK Ideograph-82CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82cf CJK Ideograph-82CF | 82ce CJK Ideograph-82CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d1 CJK Ideograph-82D1 | 82d0 CJK Ideograph-82D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d3 CJK Ideograph-82D3 | 82d2 CJK Ideograph-82D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d5 CJK Ideograph-82D5 | 82d4 CJK Ideograph-82D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d7 CJK Ideograph-82D7 | 82d6 CJK Ideograph-82D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82d9 CJK Ideograph-82D9 | 82d8 CJK Ideograph-82D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82db CJK Ideograph-82DB | 82da CJK Ideograph-82DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82dd CJK Ideograph-82DD | 82dc CJK Ideograph-82DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82df CJK Ideograph-82DF | 82de CJK Ideograph-82DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e1 CJK Ideograph-82E1 | 82e0 CJK Ideograph-82E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e3 CJK Ideograph-82E3 | 82e2 CJK Ideograph-82E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e5 CJK Ideograph-82E5 | 82e4 CJK Ideograph-82E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e7 CJK Ideograph-82E7 | 82e6 CJK Ideograph-82E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82e9 CJK Ideograph-82E9 | 82e8 CJK Ideograph-82E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82eb CJK Ideograph-82EB | 82ea CJK Ideograph-82EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ed CJK Ideograph-82ED | 82ec CJK Ideograph-82EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ef CJK Ideograph-82EF | 82ee CJK Ideograph-82EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f1 CJK Ideograph-82F1 | 82f0 CJK Ideograph-82F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f3 CJK Ideograph-82F3 | 82f2 CJK Ideograph-82F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f5 CJK Ideograph-82F5 | 82f4 CJK Ideograph-82F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f7 CJK Ideograph-82F7 | 82f6 CJK Ideograph-82F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82f9 CJK Ideograph-82F9 | 82f8 CJK Ideograph-82F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82fb CJK Ideograph-82FB | 82fa CJK Ideograph-82FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82fd CJK Ideograph-82FD | 82fc CJK Ideograph-82FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 82ff CJK Ideograph-82FF | 82fe CJK Ideograph-82FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8301 CJK Ideograph-8301 | 8300 CJK Ideograph-8300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8303 CJK Ideograph-8303 | 8302 CJK Ideograph-8302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8305 CJK Ideograph-8305 | 8304 CJK Ideograph-8304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8307 CJK Ideograph-8307 | 8306 CJK Ideograph-8306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8309 CJK Ideograph-8309 | 8308 CJK Ideograph-8308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830b CJK Ideograph-830B | 830a CJK Ideograph-830A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830d CJK Ideograph-830D | 830c CJK Ideograph-830C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 830f CJK Ideograph-830F | 830e CJK Ideograph-830E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8311 CJK Ideograph-8311 | 8310 CJK Ideograph-8310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8313 CJK Ideograph-8313 | 8312 CJK Ideograph-8312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8315 CJK Ideograph-8315 | 8314 CJK Ideograph-8314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8317 CJK Ideograph-8317 | 8316 CJK Ideograph-8316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8319 CJK Ideograph-8319 | 8318 CJK Ideograph-8318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831b CJK Ideograph-831B | 831a CJK Ideograph-831A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831d CJK Ideograph-831D | 831c CJK Ideograph-831C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 831f CJK Ideograph-831F | 831e CJK Ideograph-831E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8321 CJK Ideograph-8321 | 8320 CJK Ideograph-8320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8323 CJK Ideograph-8323 | 8322 CJK Ideograph-8322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8325 CJK Ideograph-8325 | 8324 CJK Ideograph-8324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8327 CJK Ideograph-8327 | 8326 CJK Ideograph-8326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8329 CJK Ideograph-8329 | 8328 CJK Ideograph-8328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832b CJK Ideograph-832B | 832a CJK Ideograph-832A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832d CJK Ideograph-832D | 832c CJK Ideograph-832C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 832f CJK Ideograph-832F | 832e CJK Ideograph-832E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8331 CJK Ideograph-8331 | 8330 CJK Ideograph-8330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8333 CJK Ideograph-8333 | 8332 CJK Ideograph-8332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8335 CJK Ideograph-8335 | 8334 CJK Ideograph-8334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8337 CJK Ideograph-8337 | 8336 CJK Ideograph-8336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8339 CJK Ideograph-8339 | 8338 CJK Ideograph-8338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833b CJK Ideograph-833B | 833a CJK Ideograph-833A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833d CJK Ideograph-833D | 833c CJK Ideograph-833C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 833f CJK Ideograph-833F | 833e CJK Ideograph-833E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8341 CJK Ideograph-8341 | 8340 CJK Ideograph-8340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8343 CJK Ideograph-8343 | 8342 CJK Ideograph-8342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8345 CJK Ideograph-8345 | 8344 CJK Ideograph-8344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8347 CJK Ideograph-8347 | 8346 CJK Ideograph-8346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8349 CJK Ideograph-8349 | 8348 CJK Ideograph-8348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834b CJK Ideograph-834B | 834a CJK Ideograph-834A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834d CJK Ideograph-834D | 834c CJK Ideograph-834C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 834f CJK Ideograph-834F | 834e CJK Ideograph-834E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8351 CJK Ideograph-8351 | 8350 CJK Ideograph-8350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8353 CJK Ideograph-8353 | 8352 CJK Ideograph-8352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8355 CJK Ideograph-8355 | 8354 CJK Ideograph-8354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8357 CJK Ideograph-8357 | 8356 CJK Ideograph-8356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8359 CJK Ideograph-8359 | 8358 CJK Ideograph-8358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835b CJK Ideograph-835B | 835a CJK Ideograph-835A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835d CJK Ideograph-835D | 835c CJK Ideograph-835C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 835f CJK Ideograph-835F | 835e CJK Ideograph-835E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8361 CJK Ideograph-8361 | 8360 CJK Ideograph-8360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8363 CJK Ideograph-8363 | 8362 CJK Ideograph-8362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8365 CJK Ideograph-8365 | 8364 CJK Ideograph-8364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8367 CJK Ideograph-8367 | 8366 CJK Ideograph-8366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8369 CJK Ideograph-8369 | 8368 CJK Ideograph-8368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836b CJK Ideograph-836B | 836a CJK Ideograph-836A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836d CJK Ideograph-836D | 836c CJK Ideograph-836C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 836f CJK Ideograph-836F | 836e CJK Ideograph-836E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8371 CJK Ideograph-8371 | 8370 CJK Ideograph-8370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8373 CJK Ideograph-8373 | 8372 CJK Ideograph-8372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8375 CJK Ideograph-8375 | 8374 CJK Ideograph-8374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8377 CJK Ideograph-8377 | 8376 CJK Ideograph-8376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8379 CJK Ideograph-8379 | 8378 CJK Ideograph-8378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837b CJK Ideograph-837B | 837a CJK Ideograph-837A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837d CJK Ideograph-837D | 837c CJK Ideograph-837C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 837f CJK Ideograph-837F | 837e CJK Ideograph-837E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8381 CJK Ideograph-8381 | 8380 CJK Ideograph-8380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8383 CJK Ideograph-8383 | 8382 CJK Ideograph-8382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8385 CJK Ideograph-8385 | 8384 CJK Ideograph-8384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8387 CJK Ideograph-8387 | 8386 CJK Ideograph-8386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8389 CJK Ideograph-8389 | 8388 CJK Ideograph-8388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838b CJK Ideograph-838B | 838a CJK Ideograph-838A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838d CJK Ideograph-838D | 838c CJK Ideograph-838C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 838f CJK Ideograph-838F | 838e CJK Ideograph-838E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8391 CJK Ideograph-8391 | 8390 CJK Ideograph-8390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8393 CJK Ideograph-8393 | 8392 CJK Ideograph-8392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8395 CJK Ideograph-8395 | 8394 CJK Ideograph-8394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8397 CJK Ideograph-8397 | 8396 CJK Ideograph-8396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8399 CJK Ideograph-8399 | 8398 CJK Ideograph-8398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839b CJK Ideograph-839B | 839a CJK Ideograph-839A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839d CJK Ideograph-839D | 839c CJK Ideograph-839C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 839f CJK Ideograph-839F | 839e CJK Ideograph-839E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a1 CJK Ideograph-83A1 | 83a0 CJK Ideograph-83A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a3 CJK Ideograph-83A3 | 83a2 CJK Ideograph-83A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a5 CJK Ideograph-83A5 | 83a4 CJK Ideograph-83A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a7 CJK Ideograph-83A7 | 83a6 CJK Ideograph-83A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83a9 CJK Ideograph-83A9 | 83a8 CJK Ideograph-83A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ab CJK Ideograph-83AB | 83aa CJK Ideograph-83AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ad CJK Ideograph-83AD | 83ac CJK Ideograph-83AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83af CJK Ideograph-83AF | 83ae CJK Ideograph-83AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b1 CJK Ideograph-83B1 | 83b0 CJK Ideograph-83B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b3 CJK Ideograph-83B3 | 83b2 CJK Ideograph-83B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b5 CJK Ideograph-83B5 | 83b4 CJK Ideograph-83B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b7 CJK Ideograph-83B7 | 83b6 CJK Ideograph-83B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83b9 CJK Ideograph-83B9 | 83b8 CJK Ideograph-83B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bb CJK Ideograph-83BB | 83ba CJK Ideograph-83BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bd CJK Ideograph-83BD | 83bc CJK Ideograph-83BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83bf CJK Ideograph-83BF | 83be CJK Ideograph-83BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c1 CJK Ideograph-83C1 | 83c0 CJK Ideograph-83C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c3 CJK Ideograph-83C3 | 83c2 CJK Ideograph-83C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c5 CJK Ideograph-83C5 | 83c4 CJK Ideograph-83C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c7 CJK Ideograph-83C7 | 83c6 CJK Ideograph-83C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83c9 CJK Ideograph-83C9 | 83c8 CJK Ideograph-83C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cb CJK Ideograph-83CB | 83ca CJK Ideograph-83CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cd CJK Ideograph-83CD | 83cc CJK Ideograph-83CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83cf CJK Ideograph-83CF | 83ce CJK Ideograph-83CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d1 CJK Ideograph-83D1 | 83d0 CJK Ideograph-83D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d3 CJK Ideograph-83D3 | 83d2 CJK Ideograph-83D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d5 CJK Ideograph-83D5 | 83d4 CJK Ideograph-83D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d7 CJK Ideograph-83D7 | 83d6 CJK Ideograph-83D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83d9 CJK Ideograph-83D9 | 83d8 CJK Ideograph-83D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83db CJK Ideograph-83DB | 83da CJK Ideograph-83DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83dd CJK Ideograph-83DD | 83dc CJK Ideograph-83DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83df CJK Ideograph-83DF | 83de CJK Ideograph-83DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e1 CJK Ideograph-83E1 | 83e0 CJK Ideograph-83E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e3 CJK Ideograph-83E3 | 83e2 CJK Ideograph-83E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e5 CJK Ideograph-83E5 | 83e4 CJK Ideograph-83E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e7 CJK Ideograph-83E7 | 83e6 CJK Ideograph-83E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83e9 CJK Ideograph-83E9 | 83e8 CJK Ideograph-83E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83eb CJK Ideograph-83EB | 83ea CJK Ideograph-83EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ed CJK Ideograph-83ED | 83ec CJK Ideograph-83EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ef CJK Ideograph-83EF | 83ee CJK Ideograph-83EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f1 CJK Ideograph-83F1 | 83f0 CJK Ideograph-83F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f3 CJK Ideograph-83F3 | 83f2 CJK Ideograph-83F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f5 CJK Ideograph-83F5 | 83f4 CJK Ideograph-83F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f7 CJK Ideograph-83F7 | 83f6 CJK Ideograph-83F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83f9 CJK Ideograph-83F9 | 83f8 CJK Ideograph-83F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83fb CJK Ideograph-83FB | 83fa CJK Ideograph-83FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83fd CJK Ideograph-83FD | 83fc CJK Ideograph-83FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 83ff CJK Ideograph-83FF | 83fe CJK Ideograph-83FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8401 CJK Ideograph-8401 | 8400 CJK Ideograph-8400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8403 CJK Ideograph-8403 | 8402 CJK Ideograph-8402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8405 CJK Ideograph-8405 | 8404 CJK Ideograph-8404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8407 CJK Ideograph-8407 | 8406 CJK Ideograph-8406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8409 CJK Ideograph-8409 | 8408 CJK Ideograph-8408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840b CJK Ideograph-840B | 840a CJK Ideograph-840A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840d CJK Ideograph-840D | 840c CJK Ideograph-840C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 840f CJK Ideograph-840F | 840e CJK Ideograph-840E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8411 CJK Ideograph-8411 | 8410 CJK Ideograph-8410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8413 CJK Ideograph-8413 | 8412 CJK Ideograph-8412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8415 CJK Ideograph-8415 | 8414 CJK Ideograph-8414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8417 CJK Ideograph-8417 | 8416 CJK Ideograph-8416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8419 CJK Ideograph-8419 | 8418 CJK Ideograph-8418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841b CJK Ideograph-841B | 841a CJK Ideograph-841A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841d CJK Ideograph-841D | 841c CJK Ideograph-841C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 841f CJK Ideograph-841F | 841e CJK Ideograph-841E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8421 CJK Ideograph-8421 | 8420 CJK Ideograph-8420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8423 CJK Ideograph-8423 | 8422 CJK Ideograph-8422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8425 CJK Ideograph-8425 | 8424 CJK Ideograph-8424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8427 CJK Ideograph-8427 | 8426 CJK Ideograph-8426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8429 CJK Ideograph-8429 | 8428 CJK Ideograph-8428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842b CJK Ideograph-842B | 842a CJK Ideograph-842A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842d CJK Ideograph-842D | 842c CJK Ideograph-842C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 842f CJK Ideograph-842F | 842e CJK Ideograph-842E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8431 CJK Ideograph-8431 | 8430 CJK Ideograph-8430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8433 CJK Ideograph-8433 | 8432 CJK Ideograph-8432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8435 CJK Ideograph-8435 | 8434 CJK Ideograph-8434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8437 CJK Ideograph-8437 | 8436 CJK Ideograph-8436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8439 CJK Ideograph-8439 | 8438 CJK Ideograph-8438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843b CJK Ideograph-843B | 843a CJK Ideograph-843A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843d CJK Ideograph-843D | 843c CJK Ideograph-843C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 843f CJK Ideograph-843F | 843e CJK Ideograph-843E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8441 CJK Ideograph-8441 | 8440 CJK Ideograph-8440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8443 CJK Ideograph-8443 | 8442 CJK Ideograph-8442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8445 CJK Ideograph-8445 | 8444 CJK Ideograph-8444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8447 CJK Ideograph-8447 | 8446 CJK Ideograph-8446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8449 CJK Ideograph-8449 | 8448 CJK Ideograph-8448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844b CJK Ideograph-844B | 844a CJK Ideograph-844A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844d CJK Ideograph-844D | 844c CJK Ideograph-844C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 844f CJK Ideograph-844F | 844e CJK Ideograph-844E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8451 CJK Ideograph-8451 | 8450 CJK Ideograph-8450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8453 CJK Ideograph-8453 | 8452 CJK Ideograph-8452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8455 CJK Ideograph-8455 | 8454 CJK Ideograph-8454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8457 CJK Ideograph-8457 | 8456 CJK Ideograph-8456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8459 CJK Ideograph-8459 | 8458 CJK Ideograph-8458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845b CJK Ideograph-845B | 845a CJK Ideograph-845A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845d CJK Ideograph-845D | 845c CJK Ideograph-845C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 845f CJK Ideograph-845F | 845e CJK Ideograph-845E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8461 CJK Ideograph-8461 | 8460 CJK Ideograph-8460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8463 CJK Ideograph-8463 | 8462 CJK Ideograph-8462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8465 CJK Ideograph-8465 | 8464 CJK Ideograph-8464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8467 CJK Ideograph-8467 | 8466 CJK Ideograph-8466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8469 CJK Ideograph-8469 | 8468 CJK Ideograph-8468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846b CJK Ideograph-846B | 846a CJK Ideograph-846A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846d CJK Ideograph-846D | 846c CJK Ideograph-846C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 846f CJK Ideograph-846F | 846e CJK Ideograph-846E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8471 CJK Ideograph-8471 | 8470 CJK Ideograph-8470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8473 CJK Ideograph-8473 | 8472 CJK Ideograph-8472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8475 CJK Ideograph-8475 | 8474 CJK Ideograph-8474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8477 CJK Ideograph-8477 | 8476 CJK Ideograph-8476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8479 CJK Ideograph-8479 | 8478 CJK Ideograph-8478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847b CJK Ideograph-847B | 847a CJK Ideograph-847A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847d CJK Ideograph-847D | 847c CJK Ideograph-847C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 847f CJK Ideograph-847F | 847e CJK Ideograph-847E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8481 CJK Ideograph-8481 | 8480 CJK Ideograph-8480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8483 CJK Ideograph-8483 | 8482 CJK Ideograph-8482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8485 CJK Ideograph-8485 | 8484 CJK Ideograph-8484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8487 CJK Ideograph-8487 | 8486 CJK Ideograph-8486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8489 CJK Ideograph-8489 | 8488 CJK Ideograph-8488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848b CJK Ideograph-848B | 848a CJK Ideograph-848A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848d CJK Ideograph-848D | 848c CJK Ideograph-848C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 848f CJK Ideograph-848F | 848e CJK Ideograph-848E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8491 CJK Ideograph-8491 | 8490 CJK Ideograph-8490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8493 CJK Ideograph-8493 | 8492 CJK Ideograph-8492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8495 CJK Ideograph-8495 | 8494 CJK Ideograph-8494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8497 CJK Ideograph-8497 | 8496 CJK Ideograph-8496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8499 CJK Ideograph-8499 | 8498 CJK Ideograph-8498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849b CJK Ideograph-849B | 849a CJK Ideograph-849A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849d CJK Ideograph-849D | 849c CJK Ideograph-849C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 849f CJK Ideograph-849F | 849e CJK Ideograph-849E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a1 CJK Ideograph-84A1 | 84a0 CJK Ideograph-84A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a3 CJK Ideograph-84A3 | 84a2 CJK Ideograph-84A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a5 CJK Ideograph-84A5 | 84a4 CJK Ideograph-84A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a7 CJK Ideograph-84A7 | 84a6 CJK Ideograph-84A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84a9 CJK Ideograph-84A9 | 84a8 CJK Ideograph-84A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ab CJK Ideograph-84AB | 84aa CJK Ideograph-84AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ad CJK Ideograph-84AD | 84ac CJK Ideograph-84AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84af CJK Ideograph-84AF | 84ae CJK Ideograph-84AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b1 CJK Ideograph-84B1 | 84b0 CJK Ideograph-84B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b3 CJK Ideograph-84B3 | 84b2 CJK Ideograph-84B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b5 CJK Ideograph-84B5 | 84b4 CJK Ideograph-84B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b7 CJK Ideograph-84B7 | 84b6 CJK Ideograph-84B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84b9 CJK Ideograph-84B9 | 84b8 CJK Ideograph-84B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bb CJK Ideograph-84BB | 84ba CJK Ideograph-84BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bd CJK Ideograph-84BD | 84bc CJK Ideograph-84BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84bf CJK Ideograph-84BF | 84be CJK Ideograph-84BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c1 CJK Ideograph-84C1 | 84c0 CJK Ideograph-84C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c3 CJK Ideograph-84C3 | 84c2 CJK Ideograph-84C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c5 CJK Ideograph-84C5 | 84c4 CJK Ideograph-84C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c7 CJK Ideograph-84C7 | 84c6 CJK Ideograph-84C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84c9 CJK Ideograph-84C9 | 84c8 CJK Ideograph-84C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cb CJK Ideograph-84CB | 84ca CJK Ideograph-84CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cd CJK Ideograph-84CD | 84cc CJK Ideograph-84CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84cf CJK Ideograph-84CF | 84ce CJK Ideograph-84CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d1 CJK Ideograph-84D1 | 84d0 CJK Ideograph-84D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d3 CJK Ideograph-84D3 | 84d2 CJK Ideograph-84D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d5 CJK Ideograph-84D5 | 84d4 CJK Ideograph-84D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d7 CJK Ideograph-84D7 | 84d6 CJK Ideograph-84D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84d9 CJK Ideograph-84D9 | 84d8 CJK Ideograph-84D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84db CJK Ideograph-84DB | 84da CJK Ideograph-84DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84dd CJK Ideograph-84DD | 84dc CJK Ideograph-84DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84df CJK Ideograph-84DF | 84de CJK Ideograph-84DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e1 CJK Ideograph-84E1 | 84e0 CJK Ideograph-84E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e3 CJK Ideograph-84E3 | 84e2 CJK Ideograph-84E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e5 CJK Ideograph-84E5 | 84e4 CJK Ideograph-84E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e7 CJK Ideograph-84E7 | 84e6 CJK Ideograph-84E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84e9 CJK Ideograph-84E9 | 84e8 CJK Ideograph-84E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84eb CJK Ideograph-84EB | 84ea CJK Ideograph-84EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ed CJK Ideograph-84ED | 84ec CJK Ideograph-84EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ef CJK Ideograph-84EF | 84ee CJK Ideograph-84EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f1 CJK Ideograph-84F1 | 84f0 CJK Ideograph-84F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f3 CJK Ideograph-84F3 | 84f2 CJK Ideograph-84F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f5 CJK Ideograph-84F5 | 84f4 CJK Ideograph-84F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f7 CJK Ideograph-84F7 | 84f6 CJK Ideograph-84F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84f9 CJK Ideograph-84F9 | 84f8 CJK Ideograph-84F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84fb CJK Ideograph-84FB | 84fa CJK Ideograph-84FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84fd CJK Ideograph-84FD | 84fc CJK Ideograph-84FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 84ff CJK Ideograph-84FF | 84fe CJK Ideograph-84FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8501 CJK Ideograph-8501 | 8500 CJK Ideograph-8500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8503 CJK Ideograph-8503 | 8502 CJK Ideograph-8502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8505 CJK Ideograph-8505 | 8504 CJK Ideograph-8504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8507 CJK Ideograph-8507 | 8506 CJK Ideograph-8506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8509 CJK Ideograph-8509 | 8508 CJK Ideograph-8508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850b CJK Ideograph-850B | 850a CJK Ideograph-850A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850d CJK Ideograph-850D | 850c CJK Ideograph-850C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 850f CJK Ideograph-850F | 850e CJK Ideograph-850E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8511 CJK Ideograph-8511 | 8510 CJK Ideograph-8510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8513 CJK Ideograph-8513 | 8512 CJK Ideograph-8512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8515 CJK Ideograph-8515 | 8514 CJK Ideograph-8514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8517 CJK Ideograph-8517 | 8516 CJK Ideograph-8516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8519 CJK Ideograph-8519 | 8518 CJK Ideograph-8518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851b CJK Ideograph-851B | 851a CJK Ideograph-851A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851d CJK Ideograph-851D | 851c CJK Ideograph-851C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 851f CJK Ideograph-851F | 851e CJK Ideograph-851E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8521 CJK Ideograph-8521 | 8520 CJK Ideograph-8520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8523 CJK Ideograph-8523 | 8522 CJK Ideograph-8522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8525 CJK Ideograph-8525 | 8524 CJK Ideograph-8524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8527 CJK Ideograph-8527 | 8526 CJK Ideograph-8526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8529 CJK Ideograph-8529 | 8528 CJK Ideograph-8528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852b CJK Ideograph-852B | 852a CJK Ideograph-852A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852d CJK Ideograph-852D | 852c CJK Ideograph-852C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 852f CJK Ideograph-852F | 852e CJK Ideograph-852E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8531 CJK Ideograph-8531 | 8530 CJK Ideograph-8530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8533 CJK Ideograph-8533 | 8532 CJK Ideograph-8532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8535 CJK Ideograph-8535 | 8534 CJK Ideograph-8534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8537 CJK Ideograph-8537 | 8536 CJK Ideograph-8536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8539 CJK Ideograph-8539 | 8538 CJK Ideograph-8538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853b CJK Ideograph-853B | 853a CJK Ideograph-853A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853d CJK Ideograph-853D | 853c CJK Ideograph-853C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 853f CJK Ideograph-853F | 853e CJK Ideograph-853E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8541 CJK Ideograph-8541 | 8540 CJK Ideograph-8540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8543 CJK Ideograph-8543 | 8542 CJK Ideograph-8542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8545 CJK Ideograph-8545 | 8544 CJK Ideograph-8544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8547 CJK Ideograph-8547 | 8546 CJK Ideograph-8546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8549 CJK Ideograph-8549 | 8548 CJK Ideograph-8548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854b CJK Ideograph-854B | 854a CJK Ideograph-854A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854d CJK Ideograph-854D | 854c CJK Ideograph-854C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 854f CJK Ideograph-854F | 854e CJK Ideograph-854E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8551 CJK Ideograph-8551 | 8550 CJK Ideograph-8550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8553 CJK Ideograph-8553 | 8552 CJK Ideograph-8552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8555 CJK Ideograph-8555 | 8554 CJK Ideograph-8554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8557 CJK Ideograph-8557 | 8556 CJK Ideograph-8556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8559 CJK Ideograph-8559 | 8558 CJK Ideograph-8558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855b CJK Ideograph-855B | 855a CJK Ideograph-855A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855d CJK Ideograph-855D | 855c CJK Ideograph-855C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 855f CJK Ideograph-855F | 855e CJK Ideograph-855E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8561 CJK Ideograph-8561 | 8560 CJK Ideograph-8560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8563 CJK Ideograph-8563 | 8562 CJK Ideograph-8562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8565 CJK Ideograph-8565 | 8564 CJK Ideograph-8564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8567 CJK Ideograph-8567 | 8566 CJK Ideograph-8566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8569 CJK Ideograph-8569 | 8568 CJK Ideograph-8568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856b CJK Ideograph-856B | 856a CJK Ideograph-856A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856d CJK Ideograph-856D | 856c CJK Ideograph-856C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 856f CJK Ideograph-856F | 856e CJK Ideograph-856E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8571 CJK Ideograph-8571 | 8570 CJK Ideograph-8570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8573 CJK Ideograph-8573 | 8572 CJK Ideograph-8572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8575 CJK Ideograph-8575 | 8574 CJK Ideograph-8574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8577 CJK Ideograph-8577 | 8576 CJK Ideograph-8576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8579 CJK Ideograph-8579 | 8578 CJK Ideograph-8578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857b CJK Ideograph-857B | 857a CJK Ideograph-857A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857d CJK Ideograph-857D | 857c CJK Ideograph-857C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 857f CJK Ideograph-857F | 857e CJK Ideograph-857E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8581 CJK Ideograph-8581 | 8580 CJK Ideograph-8580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8583 CJK Ideograph-8583 | 8582 CJK Ideograph-8582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8585 CJK Ideograph-8585 | 8584 CJK Ideograph-8584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8587 CJK Ideograph-8587 | 8586 CJK Ideograph-8586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8589 CJK Ideograph-8589 | 8588 CJK Ideograph-8588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858b CJK Ideograph-858B | 858a CJK Ideograph-858A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858d CJK Ideograph-858D | 858c CJK Ideograph-858C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 858f CJK Ideograph-858F | 858e CJK Ideograph-858E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8591 CJK Ideograph-8591 | 8590 CJK Ideograph-8590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8593 CJK Ideograph-8593 | 8592 CJK Ideograph-8592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8595 CJK Ideograph-8595 | 8594 CJK Ideograph-8594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8597 CJK Ideograph-8597 | 8596 CJK Ideograph-8596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8599 CJK Ideograph-8599 | 8598 CJK Ideograph-8598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859b CJK Ideograph-859B | 859a CJK Ideograph-859A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859d CJK Ideograph-859D | 859c CJK Ideograph-859C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 859f CJK Ideograph-859F | 859e CJK Ideograph-859E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a1 CJK Ideograph-85A1 | 85a0 CJK Ideograph-85A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a3 CJK Ideograph-85A3 | 85a2 CJK Ideograph-85A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a5 CJK Ideograph-85A5 | 85a4 CJK Ideograph-85A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a7 CJK Ideograph-85A7 | 85a6 CJK Ideograph-85A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85a9 CJK Ideograph-85A9 | 85a8 CJK Ideograph-85A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ab CJK Ideograph-85AB | 85aa CJK Ideograph-85AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ad CJK Ideograph-85AD | 85ac CJK Ideograph-85AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85af CJK Ideograph-85AF | 85ae CJK Ideograph-85AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b1 CJK Ideograph-85B1 | 85b0 CJK Ideograph-85B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b3 CJK Ideograph-85B3 | 85b2 CJK Ideograph-85B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b5 CJK Ideograph-85B5 | 85b4 CJK Ideograph-85B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b7 CJK Ideograph-85B7 | 85b6 CJK Ideograph-85B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85b9 CJK Ideograph-85B9 | 85b8 CJK Ideograph-85B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bb CJK Ideograph-85BB | 85ba CJK Ideograph-85BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bd CJK Ideograph-85BD | 85bc CJK Ideograph-85BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85bf CJK Ideograph-85BF | 85be CJK Ideograph-85BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c1 CJK Ideograph-85C1 | 85c0 CJK Ideograph-85C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c3 CJK Ideograph-85C3 | 85c2 CJK Ideograph-85C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c5 CJK Ideograph-85C5 | 85c4 CJK Ideograph-85C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c7 CJK Ideograph-85C7 | 85c6 CJK Ideograph-85C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85c9 CJK Ideograph-85C9 | 85c8 CJK Ideograph-85C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cb CJK Ideograph-85CB | 85ca CJK Ideograph-85CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cd CJK Ideograph-85CD | 85cc CJK Ideograph-85CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85cf CJK Ideograph-85CF | 85ce CJK Ideograph-85CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d1 CJK Ideograph-85D1 | 85d0 CJK Ideograph-85D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d3 CJK Ideograph-85D3 | 85d2 CJK Ideograph-85D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d5 CJK Ideograph-85D5 | 85d4 CJK Ideograph-85D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d7 CJK Ideograph-85D7 | 85d6 CJK Ideograph-85D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85d9 CJK Ideograph-85D9 | 85d8 CJK Ideograph-85D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85db CJK Ideograph-85DB | 85da CJK Ideograph-85DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85dd CJK Ideograph-85DD | 85dc CJK Ideograph-85DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85df CJK Ideograph-85DF | 85de CJK Ideograph-85DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e1 CJK Ideograph-85E1 | 85e0 CJK Ideograph-85E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e3 CJK Ideograph-85E3 | 85e2 CJK Ideograph-85E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e5 CJK Ideograph-85E5 | 85e4 CJK Ideograph-85E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e7 CJK Ideograph-85E7 | 85e6 CJK Ideograph-85E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85e9 CJK Ideograph-85E9 | 85e8 CJK Ideograph-85E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85eb CJK Ideograph-85EB | 85ea CJK Ideograph-85EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ed CJK Ideograph-85ED | 85ec CJK Ideograph-85EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ef CJK Ideograph-85EF | 85ee CJK Ideograph-85EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f1 CJK Ideograph-85F1 | 85f0 CJK Ideograph-85F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f3 CJK Ideograph-85F3 | 85f2 CJK Ideograph-85F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f5 CJK Ideograph-85F5 | 85f4 CJK Ideograph-85F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f7 CJK Ideograph-85F7 | 85f6 CJK Ideograph-85F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85f9 CJK Ideograph-85F9 | 85f8 CJK Ideograph-85F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85fb CJK Ideograph-85FB | 85fa CJK Ideograph-85FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85fd CJK Ideograph-85FD | 85fc CJK Ideograph-85FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 85ff CJK Ideograph-85FF | 85fe CJK Ideograph-85FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8601 CJK Ideograph-8601 | 8600 CJK Ideograph-8600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8603 CJK Ideograph-8603 | 8602 CJK Ideograph-8602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8605 CJK Ideograph-8605 | 8604 CJK Ideograph-8604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8607 CJK Ideograph-8607 | 8606 CJK Ideograph-8606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8609 CJK Ideograph-8609 | 8608 CJK Ideograph-8608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860b CJK Ideograph-860B | 860a CJK Ideograph-860A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860d CJK Ideograph-860D | 860c CJK Ideograph-860C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 860f CJK Ideograph-860F | 860e CJK Ideograph-860E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8611 CJK Ideograph-8611 | 8610 CJK Ideograph-8610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8613 CJK Ideograph-8613 | 8612 CJK Ideograph-8612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8615 CJK Ideograph-8615 | 8614 CJK Ideograph-8614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8617 CJK Ideograph-8617 | 8616 CJK Ideograph-8616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8619 CJK Ideograph-8619 | 8618 CJK Ideograph-8618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861b CJK Ideograph-861B | 861a CJK Ideograph-861A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861d CJK Ideograph-861D | 861c CJK Ideograph-861C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 861f CJK Ideograph-861F | 861e CJK Ideograph-861E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8621 CJK Ideograph-8621 | 8620 CJK Ideograph-8620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8623 CJK Ideograph-8623 | 8622 CJK Ideograph-8622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8625 CJK Ideograph-8625 | 8624 CJK Ideograph-8624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8627 CJK Ideograph-8627 | 8626 CJK Ideograph-8626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8629 CJK Ideograph-8629 | 8628 CJK Ideograph-8628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862b CJK Ideograph-862B | 862a CJK Ideograph-862A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862d CJK Ideograph-862D | 862c CJK Ideograph-862C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 862f CJK Ideograph-862F | 862e CJK Ideograph-862E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8631 CJK Ideograph-8631 | 8630 CJK Ideograph-8630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8633 CJK Ideograph-8633 | 8632 CJK Ideograph-8632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8635 CJK Ideograph-8635 | 8634 CJK Ideograph-8634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8637 CJK Ideograph-8637 | 8636 CJK Ideograph-8636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8639 CJK Ideograph-8639 | 8638 CJK Ideograph-8638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863b CJK Ideograph-863B | 863a CJK Ideograph-863A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863d CJK Ideograph-863D | 863c CJK Ideograph-863C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 863f CJK Ideograph-863F | 863e CJK Ideograph-863E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8641 CJK Ideograph-8641 | 8640 CJK Ideograph-8640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8643 CJK Ideograph-8643 | 8642 CJK Ideograph-8642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8645 CJK Ideograph-8645 | 8644 CJK Ideograph-8644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8647 CJK Ideograph-8647 | 8646 CJK Ideograph-8646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8649 CJK Ideograph-8649 | 8648 CJK Ideograph-8648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864b CJK Ideograph-864B | 864a CJK Ideograph-864A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864d CJK Ideograph-864D | 864c CJK Ideograph-864C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 864f CJK Ideograph-864F | 864e CJK Ideograph-864E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8651 CJK Ideograph-8651 | 8650 CJK Ideograph-8650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8653 CJK Ideograph-8653 | 8652 CJK Ideograph-8652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8655 CJK Ideograph-8655 | 8654 CJK Ideograph-8654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8657 CJK Ideograph-8657 | 8656 CJK Ideograph-8656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8659 CJK Ideograph-8659 | 8658 CJK Ideograph-8658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865b CJK Ideograph-865B | 865a CJK Ideograph-865A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865d CJK Ideograph-865D | 865c CJK Ideograph-865C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 865f CJK Ideograph-865F | 865e CJK Ideograph-865E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8661 CJK Ideograph-8661 | 8660 CJK Ideograph-8660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8663 CJK Ideograph-8663 | 8662 CJK Ideograph-8662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8665 CJK Ideograph-8665 | 8664 CJK Ideograph-8664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8667 CJK Ideograph-8667 | 8666 CJK Ideograph-8666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8669 CJK Ideograph-8669 | 8668 CJK Ideograph-8668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866b CJK Ideograph-866B | 866a CJK Ideograph-866A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866d CJK Ideograph-866D | 866c CJK Ideograph-866C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 866f CJK Ideograph-866F | 866e CJK Ideograph-866E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8671 CJK Ideograph-8671 | 8670 CJK Ideograph-8670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8673 CJK Ideograph-8673 | 8672 CJK Ideograph-8672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8675 CJK Ideograph-8675 | 8674 CJK Ideograph-8674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8677 CJK Ideograph-8677 | 8676 CJK Ideograph-8676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8679 CJK Ideograph-8679 | 8678 CJK Ideograph-8678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867b CJK Ideograph-867B | 867a CJK Ideograph-867A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867d CJK Ideograph-867D | 867c CJK Ideograph-867C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 867f CJK Ideograph-867F | 867e CJK Ideograph-867E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8681 CJK Ideograph-8681 | 8680 CJK Ideograph-8680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8683 CJK Ideograph-8683 | 8682 CJK Ideograph-8682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8685 CJK Ideograph-8685 | 8684 CJK Ideograph-8684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8687 CJK Ideograph-8687 | 8686 CJK Ideograph-8686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8689 CJK Ideograph-8689 | 8688 CJK Ideograph-8688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868b CJK Ideograph-868B | 868a CJK Ideograph-868A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868d CJK Ideograph-868D | 868c CJK Ideograph-868C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 868f CJK Ideograph-868F | 868e CJK Ideograph-868E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8691 CJK Ideograph-8691 | 8690 CJK Ideograph-8690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8693 CJK Ideograph-8693 | 8692 CJK Ideograph-8692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8695 CJK Ideograph-8695 | 8694 CJK Ideograph-8694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8697 CJK Ideograph-8697 | 8696 CJK Ideograph-8696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8699 CJK Ideograph-8699 | 8698 CJK Ideograph-8698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869b CJK Ideograph-869B | 869a CJK Ideograph-869A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869d CJK Ideograph-869D | 869c CJK Ideograph-869C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 869f CJK Ideograph-869F | 869e CJK Ideograph-869E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a1 CJK Ideograph-86A1 | 86a0 CJK Ideograph-86A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a3 CJK Ideograph-86A3 | 86a2 CJK Ideograph-86A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a5 CJK Ideograph-86A5 | 86a4 CJK Ideograph-86A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a7 CJK Ideograph-86A7 | 86a6 CJK Ideograph-86A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86a9 CJK Ideograph-86A9 | 86a8 CJK Ideograph-86A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ab CJK Ideograph-86AB | 86aa CJK Ideograph-86AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ad CJK Ideograph-86AD | 86ac CJK Ideograph-86AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86af CJK Ideograph-86AF | 86ae CJK Ideograph-86AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b1 CJK Ideograph-86B1 | 86b0 CJK Ideograph-86B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b3 CJK Ideograph-86B3 | 86b2 CJK Ideograph-86B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b5 CJK Ideograph-86B5 | 86b4 CJK Ideograph-86B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b7 CJK Ideograph-86B7 | 86b6 CJK Ideograph-86B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86b9 CJK Ideograph-86B9 | 86b8 CJK Ideograph-86B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bb CJK Ideograph-86BB | 86ba CJK Ideograph-86BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bd CJK Ideograph-86BD | 86bc CJK Ideograph-86BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86bf CJK Ideograph-86BF | 86be CJK Ideograph-86BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c1 CJK Ideograph-86C1 | 86c0 CJK Ideograph-86C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c3 CJK Ideograph-86C3 | 86c2 CJK Ideograph-86C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c5 CJK Ideograph-86C5 | 86c4 CJK Ideograph-86C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c7 CJK Ideograph-86C7 | 86c6 CJK Ideograph-86C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86c9 CJK Ideograph-86C9 | 86c8 CJK Ideograph-86C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cb CJK Ideograph-86CB | 86ca CJK Ideograph-86CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cd CJK Ideograph-86CD | 86cc CJK Ideograph-86CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86cf CJK Ideograph-86CF | 86ce CJK Ideograph-86CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d1 CJK Ideograph-86D1 | 86d0 CJK Ideograph-86D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d3 CJK Ideograph-86D3 | 86d2 CJK Ideograph-86D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d5 CJK Ideograph-86D5 | 86d4 CJK Ideograph-86D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d7 CJK Ideograph-86D7 | 86d6 CJK Ideograph-86D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86d9 CJK Ideograph-86D9 | 86d8 CJK Ideograph-86D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86db CJK Ideograph-86DB | 86da CJK Ideograph-86DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86dd CJK Ideograph-86DD | 86dc CJK Ideograph-86DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86df CJK Ideograph-86DF | 86de CJK Ideograph-86DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e1 CJK Ideograph-86E1 | 86e0 CJK Ideograph-86E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e3 CJK Ideograph-86E3 | 86e2 CJK Ideograph-86E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e5 CJK Ideograph-86E5 | 86e4 CJK Ideograph-86E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e7 CJK Ideograph-86E7 | 86e6 CJK Ideograph-86E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86e9 CJK Ideograph-86E9 | 86e8 CJK Ideograph-86E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86eb CJK Ideograph-86EB | 86ea CJK Ideograph-86EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ed CJK Ideograph-86ED | 86ec CJK Ideograph-86EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ef CJK Ideograph-86EF | 86ee CJK Ideograph-86EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f1 CJK Ideograph-86F1 | 86f0 CJK Ideograph-86F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f3 CJK Ideograph-86F3 | 86f2 CJK Ideograph-86F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f5 CJK Ideograph-86F5 | 86f4 CJK Ideograph-86F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f7 CJK Ideograph-86F7 | 86f6 CJK Ideograph-86F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86f9 CJK Ideograph-86F9 | 86f8 CJK Ideograph-86F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86fb CJK Ideograph-86FB | 86fa CJK Ideograph-86FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86fd CJK Ideograph-86FD | 86fc CJK Ideograph-86FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 86ff CJK Ideograph-86FF | 86fe CJK Ideograph-86FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8701 CJK Ideograph-8701 | 8700 CJK Ideograph-8700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8703 CJK Ideograph-8703 | 8702 CJK Ideograph-8702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8705 CJK Ideograph-8705 | 8704 CJK Ideograph-8704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8707 CJK Ideograph-8707 | 8706 CJK Ideograph-8706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8709 CJK Ideograph-8709 | 8708 CJK Ideograph-8708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870b CJK Ideograph-870B | 870a CJK Ideograph-870A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870d CJK Ideograph-870D | 870c CJK Ideograph-870C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 870f CJK Ideograph-870F | 870e CJK Ideograph-870E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8711 CJK Ideograph-8711 | 8710 CJK Ideograph-8710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8713 CJK Ideograph-8713 | 8712 CJK Ideograph-8712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8715 CJK Ideograph-8715 | 8714 CJK Ideograph-8714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8717 CJK Ideograph-8717 | 8716 CJK Ideograph-8716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8719 CJK Ideograph-8719 | 8718 CJK Ideograph-8718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871b CJK Ideograph-871B | 871a CJK Ideograph-871A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871d CJK Ideograph-871D | 871c CJK Ideograph-871C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 871f CJK Ideograph-871F | 871e CJK Ideograph-871E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8721 CJK Ideograph-8721 | 8720 CJK Ideograph-8720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8723 CJK Ideograph-8723 | 8722 CJK Ideograph-8722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8725 CJK Ideograph-8725 | 8724 CJK Ideograph-8724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8727 CJK Ideograph-8727 | 8726 CJK Ideograph-8726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8729 CJK Ideograph-8729 | 8728 CJK Ideograph-8728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872b CJK Ideograph-872B | 872a CJK Ideograph-872A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872d CJK Ideograph-872D | 872c CJK Ideograph-872C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 872f CJK Ideograph-872F | 872e CJK Ideograph-872E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8731 CJK Ideograph-8731 | 8730 CJK Ideograph-8730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8733 CJK Ideograph-8733 | 8732 CJK Ideograph-8732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8735 CJK Ideograph-8735 | 8734 CJK Ideograph-8734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8737 CJK Ideograph-8737 | 8736 CJK Ideograph-8736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8739 CJK Ideograph-8739 | 8738 CJK Ideograph-8738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873b CJK Ideograph-873B | 873a CJK Ideograph-873A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873d CJK Ideograph-873D | 873c CJK Ideograph-873C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 873f CJK Ideograph-873F | 873e CJK Ideograph-873E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8741 CJK Ideograph-8741 | 8740 CJK Ideograph-8740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8743 CJK Ideograph-8743 | 8742 CJK Ideograph-8742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8745 CJK Ideograph-8745 | 8744 CJK Ideograph-8744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8747 CJK Ideograph-8747 | 8746 CJK Ideograph-8746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8749 CJK Ideograph-8749 | 8748 CJK Ideograph-8748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874b CJK Ideograph-874B | 874a CJK Ideograph-874A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874d CJK Ideograph-874D | 874c CJK Ideograph-874C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 874f CJK Ideograph-874F | 874e CJK Ideograph-874E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8751 CJK Ideograph-8751 | 8750 CJK Ideograph-8750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8753 CJK Ideograph-8753 | 8752 CJK Ideograph-8752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8755 CJK Ideograph-8755 | 8754 CJK Ideograph-8754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8757 CJK Ideograph-8757 | 8756 CJK Ideograph-8756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8759 CJK Ideograph-8759 | 8758 CJK Ideograph-8758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875b CJK Ideograph-875B | 875a CJK Ideograph-875A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875d CJK Ideograph-875D | 875c CJK Ideograph-875C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 875f CJK Ideograph-875F | 875e CJK Ideograph-875E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8761 CJK Ideograph-8761 | 8760 CJK Ideograph-8760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8763 CJK Ideograph-8763 | 8762 CJK Ideograph-8762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8765 CJK Ideograph-8765 | 8764 CJK Ideograph-8764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8767 CJK Ideograph-8767 | 8766 CJK Ideograph-8766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8769 CJK Ideograph-8769 | 8768 CJK Ideograph-8768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876b CJK Ideograph-876B | 876a CJK Ideograph-876A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876d CJK Ideograph-876D | 876c CJK Ideograph-876C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 876f CJK Ideograph-876F | 876e CJK Ideograph-876E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8771 CJK Ideograph-8771 | 8770 CJK Ideograph-8770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8773 CJK Ideograph-8773 | 8772 CJK Ideograph-8772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8775 CJK Ideograph-8775 | 8774 CJK Ideograph-8774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8777 CJK Ideograph-8777 | 8776 CJK Ideograph-8776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8779 CJK Ideograph-8779 | 8778 CJK Ideograph-8778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877b CJK Ideograph-877B | 877a CJK Ideograph-877A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877d CJK Ideograph-877D | 877c CJK Ideograph-877C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 877f CJK Ideograph-877F | 877e CJK Ideograph-877E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8781 CJK Ideograph-8781 | 8780 CJK Ideograph-8780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8783 CJK Ideograph-8783 | 8782 CJK Ideograph-8782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8785 CJK Ideograph-8785 | 8784 CJK Ideograph-8784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8787 CJK Ideograph-8787 | 8786 CJK Ideograph-8786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8789 CJK Ideograph-8789 | 8788 CJK Ideograph-8788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878b CJK Ideograph-878B | 878a CJK Ideograph-878A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878d CJK Ideograph-878D | 878c CJK Ideograph-878C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 878f CJK Ideograph-878F | 878e CJK Ideograph-878E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8791 CJK Ideograph-8791 | 8790 CJK Ideograph-8790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8793 CJK Ideograph-8793 | 8792 CJK Ideograph-8792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8795 CJK Ideograph-8795 | 8794 CJK Ideograph-8794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8797 CJK Ideograph-8797 | 8796 CJK Ideograph-8796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8799 CJK Ideograph-8799 | 8798 CJK Ideograph-8798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879b CJK Ideograph-879B | 879a CJK Ideograph-879A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879d CJK Ideograph-879D | 879c CJK Ideograph-879C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 879f CJK Ideograph-879F | 879e CJK Ideograph-879E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a1 CJK Ideograph-87A1 | 87a0 CJK Ideograph-87A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a3 CJK Ideograph-87A3 | 87a2 CJK Ideograph-87A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a5 CJK Ideograph-87A5 | 87a4 CJK Ideograph-87A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a7 CJK Ideograph-87A7 | 87a6 CJK Ideograph-87A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87a9 CJK Ideograph-87A9 | 87a8 CJK Ideograph-87A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ab CJK Ideograph-87AB | 87aa CJK Ideograph-87AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ad CJK Ideograph-87AD | 87ac CJK Ideograph-87AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87af CJK Ideograph-87AF | 87ae CJK Ideograph-87AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b1 CJK Ideograph-87B1 | 87b0 CJK Ideograph-87B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b3 CJK Ideograph-87B3 | 87b2 CJK Ideograph-87B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b5 CJK Ideograph-87B5 | 87b4 CJK Ideograph-87B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b7 CJK Ideograph-87B7 | 87b6 CJK Ideograph-87B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87b9 CJK Ideograph-87B9 | 87b8 CJK Ideograph-87B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bb CJK Ideograph-87BB | 87ba CJK Ideograph-87BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bd CJK Ideograph-87BD | 87bc CJK Ideograph-87BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87bf CJK Ideograph-87BF | 87be CJK Ideograph-87BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c1 CJK Ideograph-87C1 | 87c0 CJK Ideograph-87C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c3 CJK Ideograph-87C3 | 87c2 CJK Ideograph-87C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c5 CJK Ideograph-87C5 | 87c4 CJK Ideograph-87C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c7 CJK Ideograph-87C7 | 87c6 CJK Ideograph-87C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87c9 CJK Ideograph-87C9 | 87c8 CJK Ideograph-87C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cb CJK Ideograph-87CB | 87ca CJK Ideograph-87CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cd CJK Ideograph-87CD | 87cc CJK Ideograph-87CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87cf CJK Ideograph-87CF | 87ce CJK Ideograph-87CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d1 CJK Ideograph-87D1 | 87d0 CJK Ideograph-87D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d3 CJK Ideograph-87D3 | 87d2 CJK Ideograph-87D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d5 CJK Ideograph-87D5 | 87d4 CJK Ideograph-87D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d7 CJK Ideograph-87D7 | 87d6 CJK Ideograph-87D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87d9 CJK Ideograph-87D9 | 87d8 CJK Ideograph-87D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87db CJK Ideograph-87DB | 87da CJK Ideograph-87DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87dd CJK Ideograph-87DD | 87dc CJK Ideograph-87DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87df CJK Ideograph-87DF | 87de CJK Ideograph-87DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e1 CJK Ideograph-87E1 | 87e0 CJK Ideograph-87E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e3 CJK Ideograph-87E3 | 87e2 CJK Ideograph-87E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e5 CJK Ideograph-87E5 | 87e4 CJK Ideograph-87E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e7 CJK Ideograph-87E7 | 87e6 CJK Ideograph-87E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87e9 CJK Ideograph-87E9 | 87e8 CJK Ideograph-87E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87eb CJK Ideograph-87EB | 87ea CJK Ideograph-87EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ed CJK Ideograph-87ED | 87ec CJK Ideograph-87EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ef CJK Ideograph-87EF | 87ee CJK Ideograph-87EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f1 CJK Ideograph-87F1 | 87f0 CJK Ideograph-87F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f3 CJK Ideograph-87F3 | 87f2 CJK Ideograph-87F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f5 CJK Ideograph-87F5 | 87f4 CJK Ideograph-87F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f7 CJK Ideograph-87F7 | 87f6 CJK Ideograph-87F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87f9 CJK Ideograph-87F9 | 87f8 CJK Ideograph-87F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87fb CJK Ideograph-87FB | 87fa CJK Ideograph-87FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87fd CJK Ideograph-87FD | 87fc CJK Ideograph-87FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 87ff CJK Ideograph-87FF | 87fe CJK Ideograph-87FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8801 CJK Ideograph-8801 | 8800 CJK Ideograph-8800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8803 CJK Ideograph-8803 | 8802 CJK Ideograph-8802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8805 CJK Ideograph-8805 | 8804 CJK Ideograph-8804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8807 CJK Ideograph-8807 | 8806 CJK Ideograph-8806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8809 CJK Ideograph-8809 | 8808 CJK Ideograph-8808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880b CJK Ideograph-880B | 880a CJK Ideograph-880A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880d CJK Ideograph-880D | 880c CJK Ideograph-880C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 880f CJK Ideograph-880F | 880e CJK Ideograph-880E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8811 CJK Ideograph-8811 | 8810 CJK Ideograph-8810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8813 CJK Ideograph-8813 | 8812 CJK Ideograph-8812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8815 CJK Ideograph-8815 | 8814 CJK Ideograph-8814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8817 CJK Ideograph-8817 | 8816 CJK Ideograph-8816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8819 CJK Ideograph-8819 | 8818 CJK Ideograph-8818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881b CJK Ideograph-881B | 881a CJK Ideograph-881A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881d CJK Ideograph-881D | 881c CJK Ideograph-881C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 881f CJK Ideograph-881F | 881e CJK Ideograph-881E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8821 CJK Ideograph-8821 | 8820 CJK Ideograph-8820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8823 CJK Ideograph-8823 | 8822 CJK Ideograph-8822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8825 CJK Ideograph-8825 | 8824 CJK Ideograph-8824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8827 CJK Ideograph-8827 | 8826 CJK Ideograph-8826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8829 CJK Ideograph-8829 | 8828 CJK Ideograph-8828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882b CJK Ideograph-882B | 882a CJK Ideograph-882A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882d CJK Ideograph-882D | 882c CJK Ideograph-882C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 882f CJK Ideograph-882F | 882e CJK Ideograph-882E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8831 CJK Ideograph-8831 | 8830 CJK Ideograph-8830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8833 CJK Ideograph-8833 | 8832 CJK Ideograph-8832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8835 CJK Ideograph-8835 | 8834 CJK Ideograph-8834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8837 CJK Ideograph-8837 | 8836 CJK Ideograph-8836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8839 CJK Ideograph-8839 | 8838 CJK Ideograph-8838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883b CJK Ideograph-883B | 883a CJK Ideograph-883A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883d CJK Ideograph-883D | 883c CJK Ideograph-883C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 883f CJK Ideograph-883F | 883e CJK Ideograph-883E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8841 CJK Ideograph-8841 | 8840 CJK Ideograph-8840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8843 CJK Ideograph-8843 | 8842 CJK Ideograph-8842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8845 CJK Ideograph-8845 | 8844 CJK Ideograph-8844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8847 CJK Ideograph-8847 | 8846 CJK Ideograph-8846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8849 CJK Ideograph-8849 | 8848 CJK Ideograph-8848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884b CJK Ideograph-884B | 884a CJK Ideograph-884A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884d CJK Ideograph-884D | 884c CJK Ideograph-884C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 884f CJK Ideograph-884F | 884e CJK Ideograph-884E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8851 CJK Ideograph-8851 | 8850 CJK Ideograph-8850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8853 CJK Ideograph-8853 | 8852 CJK Ideograph-8852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8855 CJK Ideograph-8855 | 8854 CJK Ideograph-8854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8857 CJK Ideograph-8857 | 8856 CJK Ideograph-8856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8859 CJK Ideograph-8859 | 8858 CJK Ideograph-8858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885b CJK Ideograph-885B | 885a CJK Ideograph-885A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885d CJK Ideograph-885D | 885c CJK Ideograph-885C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 885f CJK Ideograph-885F | 885e CJK Ideograph-885E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8861 CJK Ideograph-8861 | 8860 CJK Ideograph-8860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8863 CJK Ideograph-8863 | 8862 CJK Ideograph-8862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8865 CJK Ideograph-8865 | 8864 CJK Ideograph-8864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8867 CJK Ideograph-8867 | 8866 CJK Ideograph-8866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8869 CJK Ideograph-8869 | 8868 CJK Ideograph-8868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886b CJK Ideograph-886B | 886a CJK Ideograph-886A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886d CJK Ideograph-886D | 886c CJK Ideograph-886C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 886f CJK Ideograph-886F | 886e CJK Ideograph-886E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8871 CJK Ideograph-8871 | 8870 CJK Ideograph-8870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8873 CJK Ideograph-8873 | 8872 CJK Ideograph-8872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8875 CJK Ideograph-8875 | 8874 CJK Ideograph-8874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8877 CJK Ideograph-8877 | 8876 CJK Ideograph-8876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8879 CJK Ideograph-8879 | 8878 CJK Ideograph-8878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887b CJK Ideograph-887B | 887a CJK Ideograph-887A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887d CJK Ideograph-887D | 887c CJK Ideograph-887C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 887f CJK Ideograph-887F | 887e CJK Ideograph-887E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8881 CJK Ideograph-8881 | 8880 CJK Ideograph-8880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8883 CJK Ideograph-8883 | 8882 CJK Ideograph-8882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8885 CJK Ideograph-8885 | 8884 CJK Ideograph-8884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8887 CJK Ideograph-8887 | 8886 CJK Ideograph-8886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8889 CJK Ideograph-8889 | 8888 CJK Ideograph-8888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888b CJK Ideograph-888B | 888a CJK Ideograph-888A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888d CJK Ideograph-888D | 888c CJK Ideograph-888C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 888f CJK Ideograph-888F | 888e CJK Ideograph-888E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8891 CJK Ideograph-8891 | 8890 CJK Ideograph-8890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8893 CJK Ideograph-8893 | 8892 CJK Ideograph-8892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8895 CJK Ideograph-8895 | 8894 CJK Ideograph-8894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8897 CJK Ideograph-8897 | 8896 CJK Ideograph-8896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8899 CJK Ideograph-8899 | 8898 CJK Ideograph-8898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889b CJK Ideograph-889B | 889a CJK Ideograph-889A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889d CJK Ideograph-889D | 889c CJK Ideograph-889C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 889f CJK Ideograph-889F | 889e CJK Ideograph-889E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a1 CJK Ideograph-88A1 | 88a0 CJK Ideograph-88A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a3 CJK Ideograph-88A3 | 88a2 CJK Ideograph-88A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a5 CJK Ideograph-88A5 | 88a4 CJK Ideograph-88A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a7 CJK Ideograph-88A7 | 88a6 CJK Ideograph-88A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88a9 CJK Ideograph-88A9 | 88a8 CJK Ideograph-88A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ab CJK Ideograph-88AB | 88aa CJK Ideograph-88AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ad CJK Ideograph-88AD | 88ac CJK Ideograph-88AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88af CJK Ideograph-88AF | 88ae CJK Ideograph-88AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b1 CJK Ideograph-88B1 | 88b0 CJK Ideograph-88B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b3 CJK Ideograph-88B3 | 88b2 CJK Ideograph-88B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b5 CJK Ideograph-88B5 | 88b4 CJK Ideograph-88B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b7 CJK Ideograph-88B7 | 88b6 CJK Ideograph-88B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88b9 CJK Ideograph-88B9 | 88b8 CJK Ideograph-88B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bb CJK Ideograph-88BB | 88ba CJK Ideograph-88BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bd CJK Ideograph-88BD | 88bc CJK Ideograph-88BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88bf CJK Ideograph-88BF | 88be CJK Ideograph-88BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c1 CJK Ideograph-88C1 | 88c0 CJK Ideograph-88C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c3 CJK Ideograph-88C3 | 88c2 CJK Ideograph-88C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c5 CJK Ideograph-88C5 | 88c4 CJK Ideograph-88C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c7 CJK Ideograph-88C7 | 88c6 CJK Ideograph-88C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88c9 CJK Ideograph-88C9 | 88c8 CJK Ideograph-88C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cb CJK Ideograph-88CB | 88ca CJK Ideograph-88CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cd CJK Ideograph-88CD | 88cc CJK Ideograph-88CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88cf CJK Ideograph-88CF | 88ce CJK Ideograph-88CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d1 CJK Ideograph-88D1 | 88d0 CJK Ideograph-88D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d3 CJK Ideograph-88D3 | 88d2 CJK Ideograph-88D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d5 CJK Ideograph-88D5 | 88d4 CJK Ideograph-88D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d7 CJK Ideograph-88D7 | 88d6 CJK Ideograph-88D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88d9 CJK Ideograph-88D9 | 88d8 CJK Ideograph-88D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88db CJK Ideograph-88DB | 88da CJK Ideograph-88DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88dd CJK Ideograph-88DD | 88dc CJK Ideograph-88DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88df CJK Ideograph-88DF | 88de CJK Ideograph-88DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e1 CJK Ideograph-88E1 | 88e0 CJK Ideograph-88E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e3 CJK Ideograph-88E3 | 88e2 CJK Ideograph-88E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e5 CJK Ideograph-88E5 | 88e4 CJK Ideograph-88E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e7 CJK Ideograph-88E7 | 88e6 CJK Ideograph-88E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88e9 CJK Ideograph-88E9 | 88e8 CJK Ideograph-88E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88eb CJK Ideograph-88EB | 88ea CJK Ideograph-88EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ed CJK Ideograph-88ED | 88ec CJK Ideograph-88EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ef CJK Ideograph-88EF | 88ee CJK Ideograph-88EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f1 CJK Ideograph-88F1 | 88f0 CJK Ideograph-88F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f3 CJK Ideograph-88F3 | 88f2 CJK Ideograph-88F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f5 CJK Ideograph-88F5 | 88f4 CJK Ideograph-88F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f7 CJK Ideograph-88F7 | 88f6 CJK Ideograph-88F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88f9 CJK Ideograph-88F9 | 88f8 CJK Ideograph-88F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88fb CJK Ideograph-88FB | 88fa CJK Ideograph-88FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88fd CJK Ideograph-88FD | 88fc CJK Ideograph-88FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 88ff CJK Ideograph-88FF | 88fe CJK Ideograph-88FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8901 CJK Ideograph-8901 | 8900 CJK Ideograph-8900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8903 CJK Ideograph-8903 | 8902 CJK Ideograph-8902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8905 CJK Ideograph-8905 | 8904 CJK Ideograph-8904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8907 CJK Ideograph-8907 | 8906 CJK Ideograph-8906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8909 CJK Ideograph-8909 | 8908 CJK Ideograph-8908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890b CJK Ideograph-890B | 890a CJK Ideograph-890A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890d CJK Ideograph-890D | 890c CJK Ideograph-890C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 890f CJK Ideograph-890F | 890e CJK Ideograph-890E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8911 CJK Ideograph-8911 | 8910 CJK Ideograph-8910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8913 CJK Ideograph-8913 | 8912 CJK Ideograph-8912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8915 CJK Ideograph-8915 | 8914 CJK Ideograph-8914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8917 CJK Ideograph-8917 | 8916 CJK Ideograph-8916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8919 CJK Ideograph-8919 | 8918 CJK Ideograph-8918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891b CJK Ideograph-891B | 891a CJK Ideograph-891A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891d CJK Ideograph-891D | 891c CJK Ideograph-891C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 891f CJK Ideograph-891F | 891e CJK Ideograph-891E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8921 CJK Ideograph-8921 | 8920 CJK Ideograph-8920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8923 CJK Ideograph-8923 | 8922 CJK Ideograph-8922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8925 CJK Ideograph-8925 | 8924 CJK Ideograph-8924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8927 CJK Ideograph-8927 | 8926 CJK Ideograph-8926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8929 CJK Ideograph-8929 | 8928 CJK Ideograph-8928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892b CJK Ideograph-892B | 892a CJK Ideograph-892A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892d CJK Ideograph-892D | 892c CJK Ideograph-892C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 892f CJK Ideograph-892F | 892e CJK Ideograph-892E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8931 CJK Ideograph-8931 | 8930 CJK Ideograph-8930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8933 CJK Ideograph-8933 | 8932 CJK Ideograph-8932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8935 CJK Ideograph-8935 | 8934 CJK Ideograph-8934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8937 CJK Ideograph-8937 | 8936 CJK Ideograph-8936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8939 CJK Ideograph-8939 | 8938 CJK Ideograph-8938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893b CJK Ideograph-893B | 893a CJK Ideograph-893A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893d CJK Ideograph-893D | 893c CJK Ideograph-893C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 893f CJK Ideograph-893F | 893e CJK Ideograph-893E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8941 CJK Ideograph-8941 | 8940 CJK Ideograph-8940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8943 CJK Ideograph-8943 | 8942 CJK Ideograph-8942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8945 CJK Ideograph-8945 | 8944 CJK Ideograph-8944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8947 CJK Ideograph-8947 | 8946 CJK Ideograph-8946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8949 CJK Ideograph-8949 | 8948 CJK Ideograph-8948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894b CJK Ideograph-894B | 894a CJK Ideograph-894A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894d CJK Ideograph-894D | 894c CJK Ideograph-894C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 894f CJK Ideograph-894F | 894e CJK Ideograph-894E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8951 CJK Ideograph-8951 | 8950 CJK Ideograph-8950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8953 CJK Ideograph-8953 | 8952 CJK Ideograph-8952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8955 CJK Ideograph-8955 | 8954 CJK Ideograph-8954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8957 CJK Ideograph-8957 | 8956 CJK Ideograph-8956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8959 CJK Ideograph-8959 | 8958 CJK Ideograph-8958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895b CJK Ideograph-895B | 895a CJK Ideograph-895A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895d CJK Ideograph-895D | 895c CJK Ideograph-895C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 895f CJK Ideograph-895F | 895e CJK Ideograph-895E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8961 CJK Ideograph-8961 | 8960 CJK Ideograph-8960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8963 CJK Ideograph-8963 | 8962 CJK Ideograph-8962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8965 CJK Ideograph-8965 | 8964 CJK Ideograph-8964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8967 CJK Ideograph-8967 | 8966 CJK Ideograph-8966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8969 CJK Ideograph-8969 | 8968 CJK Ideograph-8968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896b CJK Ideograph-896B | 896a CJK Ideograph-896A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896d CJK Ideograph-896D | 896c CJK Ideograph-896C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 896f CJK Ideograph-896F | 896e CJK Ideograph-896E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8971 CJK Ideograph-8971 | 8970 CJK Ideograph-8970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8973 CJK Ideograph-8973 | 8972 CJK Ideograph-8972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8975 CJK Ideograph-8975 | 8974 CJK Ideograph-8974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8977 CJK Ideograph-8977 | 8976 CJK Ideograph-8976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8979 CJK Ideograph-8979 | 8978 CJK Ideograph-8978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897b CJK Ideograph-897B | 897a CJK Ideograph-897A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897d CJK Ideograph-897D | 897c CJK Ideograph-897C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 897f CJK Ideograph-897F | 897e CJK Ideograph-897E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8981 CJK Ideograph-8981 | 8980 CJK Ideograph-8980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8983 CJK Ideograph-8983 | 8982 CJK Ideograph-8982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8985 CJK Ideograph-8985 | 8984 CJK Ideograph-8984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8987 CJK Ideograph-8987 | 8986 CJK Ideograph-8986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8989 CJK Ideograph-8989 | 8988 CJK Ideograph-8988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898b CJK Ideograph-898B | 898a CJK Ideograph-898A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898d CJK Ideograph-898D | 898c CJK Ideograph-898C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 898f CJK Ideograph-898F | 898e CJK Ideograph-898E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8991 CJK Ideograph-8991 | 8990 CJK Ideograph-8990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8993 CJK Ideograph-8993 | 8992 CJK Ideograph-8992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8995 CJK Ideograph-8995 | 8994 CJK Ideograph-8994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8997 CJK Ideograph-8997 | 8996 CJK Ideograph-8996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8999 CJK Ideograph-8999 | 8998 CJK Ideograph-8998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899b CJK Ideograph-899B | 899a CJK Ideograph-899A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899d CJK Ideograph-899D | 899c CJK Ideograph-899C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 899f CJK Ideograph-899F | 899e CJK Ideograph-899E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a1 CJK Ideograph-89A1 | 89a0 CJK Ideograph-89A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a3 CJK Ideograph-89A3 | 89a2 CJK Ideograph-89A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a5 CJK Ideograph-89A5 | 89a4 CJK Ideograph-89A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a7 CJK Ideograph-89A7 | 89a6 CJK Ideograph-89A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89a9 CJK Ideograph-89A9 | 89a8 CJK Ideograph-89A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ab CJK Ideograph-89AB | 89aa CJK Ideograph-89AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ad CJK Ideograph-89AD | 89ac CJK Ideograph-89AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89af CJK Ideograph-89AF | 89ae CJK Ideograph-89AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b1 CJK Ideograph-89B1 | 89b0 CJK Ideograph-89B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b3 CJK Ideograph-89B3 | 89b2 CJK Ideograph-89B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b5 CJK Ideograph-89B5 | 89b4 CJK Ideograph-89B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b7 CJK Ideograph-89B7 | 89b6 CJK Ideograph-89B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89b9 CJK Ideograph-89B9 | 89b8 CJK Ideograph-89B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bb CJK Ideograph-89BB | 89ba CJK Ideograph-89BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bd CJK Ideograph-89BD | 89bc CJK Ideograph-89BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89bf CJK Ideograph-89BF | 89be CJK Ideograph-89BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c1 CJK Ideograph-89C1 | 89c0 CJK Ideograph-89C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c3 CJK Ideograph-89C3 | 89c2 CJK Ideograph-89C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c5 CJK Ideograph-89C5 | 89c4 CJK Ideograph-89C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c7 CJK Ideograph-89C7 | 89c6 CJK Ideograph-89C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89c9 CJK Ideograph-89C9 | 89c8 CJK Ideograph-89C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cb CJK Ideograph-89CB | 89ca CJK Ideograph-89CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cd CJK Ideograph-89CD | 89cc CJK Ideograph-89CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89cf CJK Ideograph-89CF | 89ce CJK Ideograph-89CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d1 CJK Ideograph-89D1 | 89d0 CJK Ideograph-89D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d3 CJK Ideograph-89D3 | 89d2 CJK Ideograph-89D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d5 CJK Ideograph-89D5 | 89d4 CJK Ideograph-89D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d7 CJK Ideograph-89D7 | 89d6 CJK Ideograph-89D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89d9 CJK Ideograph-89D9 | 89d8 CJK Ideograph-89D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89db CJK Ideograph-89DB | 89da CJK Ideograph-89DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89dd CJK Ideograph-89DD | 89dc CJK Ideograph-89DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89df CJK Ideograph-89DF | 89de CJK Ideograph-89DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e1 CJK Ideograph-89E1 | 89e0 CJK Ideograph-89E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e3 CJK Ideograph-89E3 | 89e2 CJK Ideograph-89E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e5 CJK Ideograph-89E5 | 89e4 CJK Ideograph-89E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e7 CJK Ideograph-89E7 | 89e6 CJK Ideograph-89E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89e9 CJK Ideograph-89E9 | 89e8 CJK Ideograph-89E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89eb CJK Ideograph-89EB | 89ea CJK Ideograph-89EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ed CJK Ideograph-89ED | 89ec CJK Ideograph-89EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ef CJK Ideograph-89EF | 89ee CJK Ideograph-89EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f1 CJK Ideograph-89F1 | 89f0 CJK Ideograph-89F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f3 CJK Ideograph-89F3 | 89f2 CJK Ideograph-89F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f5 CJK Ideograph-89F5 | 89f4 CJK Ideograph-89F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f7 CJK Ideograph-89F7 | 89f6 CJK Ideograph-89F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89f9 CJK Ideograph-89F9 | 89f8 CJK Ideograph-89F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89fb CJK Ideograph-89FB | 89fa CJK Ideograph-89FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89fd CJK Ideograph-89FD | 89fc CJK Ideograph-89FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 89ff CJK Ideograph-89FF | 89fe CJK Ideograph-89FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a01 CJK Ideograph-8A01 | 8a00 CJK Ideograph-8A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a03 CJK Ideograph-8A03 | 8a02 CJK Ideograph-8A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a05 CJK Ideograph-8A05 | 8a04 CJK Ideograph-8A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a07 CJK Ideograph-8A07 | 8a06 CJK Ideograph-8A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a09 CJK Ideograph-8A09 | 8a08 CJK Ideograph-8A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0b CJK Ideograph-8A0B | 8a0a CJK Ideograph-8A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0d CJK Ideograph-8A0D | 8a0c CJK Ideograph-8A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a0f CJK Ideograph-8A0F | 8a0e CJK Ideograph-8A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a11 CJK Ideograph-8A11 | 8a10 CJK Ideograph-8A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a13 CJK Ideograph-8A13 | 8a12 CJK Ideograph-8A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a15 CJK Ideograph-8A15 | 8a14 CJK Ideograph-8A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a17 CJK Ideograph-8A17 | 8a16 CJK Ideograph-8A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a19 CJK Ideograph-8A19 | 8a18 CJK Ideograph-8A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1b CJK Ideograph-8A1B | 8a1a CJK Ideograph-8A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1d CJK Ideograph-8A1D | 8a1c CJK Ideograph-8A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a1f CJK Ideograph-8A1F | 8a1e CJK Ideograph-8A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a21 CJK Ideograph-8A21 | 8a20 CJK Ideograph-8A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a23 CJK Ideograph-8A23 | 8a22 CJK Ideograph-8A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a25 CJK Ideograph-8A25 | 8a24 CJK Ideograph-8A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a27 CJK Ideograph-8A27 | 8a26 CJK Ideograph-8A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a29 CJK Ideograph-8A29 | 8a28 CJK Ideograph-8A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2b CJK Ideograph-8A2B | 8a2a CJK Ideograph-8A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2d CJK Ideograph-8A2D | 8a2c CJK Ideograph-8A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a2f CJK Ideograph-8A2F | 8a2e CJK Ideograph-8A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a31 CJK Ideograph-8A31 | 8a30 CJK Ideograph-8A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a33 CJK Ideograph-8A33 | 8a32 CJK Ideograph-8A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a35 CJK Ideograph-8A35 | 8a34 CJK Ideograph-8A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a37 CJK Ideograph-8A37 | 8a36 CJK Ideograph-8A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a39 CJK Ideograph-8A39 | 8a38 CJK Ideograph-8A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3b CJK Ideograph-8A3B | 8a3a CJK Ideograph-8A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3d CJK Ideograph-8A3D | 8a3c CJK Ideograph-8A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a3f CJK Ideograph-8A3F | 8a3e CJK Ideograph-8A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a41 CJK Ideograph-8A41 | 8a40 CJK Ideograph-8A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a43 CJK Ideograph-8A43 | 8a42 CJK Ideograph-8A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a45 CJK Ideograph-8A45 | 8a44 CJK Ideograph-8A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a47 CJK Ideograph-8A47 | 8a46 CJK Ideograph-8A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a49 CJK Ideograph-8A49 | 8a48 CJK Ideograph-8A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4b CJK Ideograph-8A4B | 8a4a CJK Ideograph-8A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4d CJK Ideograph-8A4D | 8a4c CJK Ideograph-8A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a4f CJK Ideograph-8A4F | 8a4e CJK Ideograph-8A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a51 CJK Ideograph-8A51 | 8a50 CJK Ideograph-8A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a53 CJK Ideograph-8A53 | 8a52 CJK Ideograph-8A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a55 CJK Ideograph-8A55 | 8a54 CJK Ideograph-8A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a57 CJK Ideograph-8A57 | 8a56 CJK Ideograph-8A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a59 CJK Ideograph-8A59 | 8a58 CJK Ideograph-8A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5b CJK Ideograph-8A5B | 8a5a CJK Ideograph-8A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5d CJK Ideograph-8A5D | 8a5c CJK Ideograph-8A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a5f CJK Ideograph-8A5F | 8a5e CJK Ideograph-8A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a61 CJK Ideograph-8A61 | 8a60 CJK Ideograph-8A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a63 CJK Ideograph-8A63 | 8a62 CJK Ideograph-8A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a65 CJK Ideograph-8A65 | 8a64 CJK Ideograph-8A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a67 CJK Ideograph-8A67 | 8a66 CJK Ideograph-8A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a69 CJK Ideograph-8A69 | 8a68 CJK Ideograph-8A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6b CJK Ideograph-8A6B | 8a6a CJK Ideograph-8A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6d CJK Ideograph-8A6D | 8a6c CJK Ideograph-8A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a6f CJK Ideograph-8A6F | 8a6e CJK Ideograph-8A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a71 CJK Ideograph-8A71 | 8a70 CJK Ideograph-8A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a73 CJK Ideograph-8A73 | 8a72 CJK Ideograph-8A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a75 CJK Ideograph-8A75 | 8a74 CJK Ideograph-8A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a77 CJK Ideograph-8A77 | 8a76 CJK Ideograph-8A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a79 CJK Ideograph-8A79 | 8a78 CJK Ideograph-8A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7b CJK Ideograph-8A7B | 8a7a CJK Ideograph-8A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7d CJK Ideograph-8A7D | 8a7c CJK Ideograph-8A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a7f CJK Ideograph-8A7F | 8a7e CJK Ideograph-8A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a81 CJK Ideograph-8A81 | 8a80 CJK Ideograph-8A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a83 CJK Ideograph-8A83 | 8a82 CJK Ideograph-8A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a85 CJK Ideograph-8A85 | 8a84 CJK Ideograph-8A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a87 CJK Ideograph-8A87 | 8a86 CJK Ideograph-8A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a89 CJK Ideograph-8A89 | 8a88 CJK Ideograph-8A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8b CJK Ideograph-8A8B | 8a8a CJK Ideograph-8A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8d CJK Ideograph-8A8D | 8a8c CJK Ideograph-8A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a8f CJK Ideograph-8A8F | 8a8e CJK Ideograph-8A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a91 CJK Ideograph-8A91 | 8a90 CJK Ideograph-8A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a93 CJK Ideograph-8A93 | 8a92 CJK Ideograph-8A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a95 CJK Ideograph-8A95 | 8a94 CJK Ideograph-8A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a97 CJK Ideograph-8A97 | 8a96 CJK Ideograph-8A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a99 CJK Ideograph-8A99 | 8a98 CJK Ideograph-8A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9b CJK Ideograph-8A9B | 8a9a CJK Ideograph-8A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9d CJK Ideograph-8A9D | 8a9c CJK Ideograph-8A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8a9f CJK Ideograph-8A9F | 8a9e CJK Ideograph-8A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa1 CJK Ideograph-8AA1 | 8aa0 CJK Ideograph-8AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa3 CJK Ideograph-8AA3 | 8aa2 CJK Ideograph-8AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa5 CJK Ideograph-8AA5 | 8aa4 CJK Ideograph-8AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa7 CJK Ideograph-8AA7 | 8aa6 CJK Ideograph-8AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aa9 CJK Ideograph-8AA9 | 8aa8 CJK Ideograph-8AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aab CJK Ideograph-8AAB | 8aaa CJK Ideograph-8AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aad CJK Ideograph-8AAD | 8aac CJK Ideograph-8AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aaf CJK Ideograph-8AAF | 8aae CJK Ideograph-8AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab1 CJK Ideograph-8AB1 | 8ab0 CJK Ideograph-8AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab3 CJK Ideograph-8AB3 | 8ab2 CJK Ideograph-8AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab5 CJK Ideograph-8AB5 | 8ab4 CJK Ideograph-8AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab7 CJK Ideograph-8AB7 | 8ab6 CJK Ideograph-8AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ab9 CJK Ideograph-8AB9 | 8ab8 CJK Ideograph-8AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abb CJK Ideograph-8ABB | 8aba CJK Ideograph-8ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abd CJK Ideograph-8ABD | 8abc CJK Ideograph-8ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8abf CJK Ideograph-8ABF | 8abe CJK Ideograph-8ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac1 CJK Ideograph-8AC1 | 8ac0 CJK Ideograph-8AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac3 CJK Ideograph-8AC3 | 8ac2 CJK Ideograph-8AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac5 CJK Ideograph-8AC5 | 8ac4 CJK Ideograph-8AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac7 CJK Ideograph-8AC7 | 8ac6 CJK Ideograph-8AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ac9 CJK Ideograph-8AC9 | 8ac8 CJK Ideograph-8AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acb CJK Ideograph-8ACB | 8aca CJK Ideograph-8ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acd CJK Ideograph-8ACD | 8acc CJK Ideograph-8ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8acf CJK Ideograph-8ACF | 8ace CJK Ideograph-8ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad1 CJK Ideograph-8AD1 | 8ad0 CJK Ideograph-8AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad3 CJK Ideograph-8AD3 | 8ad2 CJK Ideograph-8AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad5 CJK Ideograph-8AD5 | 8ad4 CJK Ideograph-8AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad7 CJK Ideograph-8AD7 | 8ad6 CJK Ideograph-8AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ad9 CJK Ideograph-8AD9 | 8ad8 CJK Ideograph-8AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8adb CJK Ideograph-8ADB | 8ada CJK Ideograph-8ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8add CJK Ideograph-8ADD | 8adc CJK Ideograph-8ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8adf CJK Ideograph-8ADF | 8ade CJK Ideograph-8ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae1 CJK Ideograph-8AE1 | 8ae0 CJK Ideograph-8AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae3 CJK Ideograph-8AE3 | 8ae2 CJK Ideograph-8AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae5 CJK Ideograph-8AE5 | 8ae4 CJK Ideograph-8AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae7 CJK Ideograph-8AE7 | 8ae6 CJK Ideograph-8AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ae9 CJK Ideograph-8AE9 | 8ae8 CJK Ideograph-8AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aeb CJK Ideograph-8AEB | 8aea CJK Ideograph-8AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aed CJK Ideograph-8AED | 8aec CJK Ideograph-8AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aef CJK Ideograph-8AEF | 8aee CJK Ideograph-8AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af1 CJK Ideograph-8AF1 | 8af0 CJK Ideograph-8AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af3 CJK Ideograph-8AF3 | 8af2 CJK Ideograph-8AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af5 CJK Ideograph-8AF5 | 8af4 CJK Ideograph-8AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af7 CJK Ideograph-8AF7 | 8af6 CJK Ideograph-8AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8af9 CJK Ideograph-8AF9 | 8af8 CJK Ideograph-8AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8afb CJK Ideograph-8AFB | 8afa CJK Ideograph-8AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8afd CJK Ideograph-8AFD | 8afc CJK Ideograph-8AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8aff CJK Ideograph-8AFF | 8afe CJK Ideograph-8AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b01 CJK Ideograph-8B01 | 8b00 CJK Ideograph-8B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b03 CJK Ideograph-8B03 | 8b02 CJK Ideograph-8B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b05 CJK Ideograph-8B05 | 8b04 CJK Ideograph-8B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b07 CJK Ideograph-8B07 | 8b06 CJK Ideograph-8B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b09 CJK Ideograph-8B09 | 8b08 CJK Ideograph-8B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0b CJK Ideograph-8B0B | 8b0a CJK Ideograph-8B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0d CJK Ideograph-8B0D | 8b0c CJK Ideograph-8B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b0f CJK Ideograph-8B0F | 8b0e CJK Ideograph-8B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b11 CJK Ideograph-8B11 | 8b10 CJK Ideograph-8B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b13 CJK Ideograph-8B13 | 8b12 CJK Ideograph-8B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b15 CJK Ideograph-8B15 | 8b14 CJK Ideograph-8B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b17 CJK Ideograph-8B17 | 8b16 CJK Ideograph-8B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b19 CJK Ideograph-8B19 | 8b18 CJK Ideograph-8B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1b CJK Ideograph-8B1B | 8b1a CJK Ideograph-8B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1d CJK Ideograph-8B1D | 8b1c CJK Ideograph-8B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b1f CJK Ideograph-8B1F | 8b1e CJK Ideograph-8B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b21 CJK Ideograph-8B21 | 8b20 CJK Ideograph-8B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b23 CJK Ideograph-8B23 | 8b22 CJK Ideograph-8B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b25 CJK Ideograph-8B25 | 8b24 CJK Ideograph-8B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b27 CJK Ideograph-8B27 | 8b26 CJK Ideograph-8B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b29 CJK Ideograph-8B29 | 8b28 CJK Ideograph-8B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2b CJK Ideograph-8B2B | 8b2a CJK Ideograph-8B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2d CJK Ideograph-8B2D | 8b2c CJK Ideograph-8B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b2f CJK Ideograph-8B2F | 8b2e CJK Ideograph-8B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b31 CJK Ideograph-8B31 | 8b30 CJK Ideograph-8B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b33 CJK Ideograph-8B33 | 8b32 CJK Ideograph-8B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b35 CJK Ideograph-8B35 | 8b34 CJK Ideograph-8B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b37 CJK Ideograph-8B37 | 8b36 CJK Ideograph-8B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b39 CJK Ideograph-8B39 | 8b38 CJK Ideograph-8B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3b CJK Ideograph-8B3B | 8b3a CJK Ideograph-8B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3d CJK Ideograph-8B3D | 8b3c CJK Ideograph-8B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b3f CJK Ideograph-8B3F | 8b3e CJK Ideograph-8B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b41 CJK Ideograph-8B41 | 8b40 CJK Ideograph-8B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b43 CJK Ideograph-8B43 | 8b42 CJK Ideograph-8B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b45 CJK Ideograph-8B45 | 8b44 CJK Ideograph-8B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b47 CJK Ideograph-8B47 | 8b46 CJK Ideograph-8B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b49 CJK Ideograph-8B49 | 8b48 CJK Ideograph-8B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4b CJK Ideograph-8B4B | 8b4a CJK Ideograph-8B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4d CJK Ideograph-8B4D | 8b4c CJK Ideograph-8B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b4f CJK Ideograph-8B4F | 8b4e CJK Ideograph-8B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b51 CJK Ideograph-8B51 | 8b50 CJK Ideograph-8B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b53 CJK Ideograph-8B53 | 8b52 CJK Ideograph-8B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b55 CJK Ideograph-8B55 | 8b54 CJK Ideograph-8B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b57 CJK Ideograph-8B57 | 8b56 CJK Ideograph-8B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b59 CJK Ideograph-8B59 | 8b58 CJK Ideograph-8B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5b CJK Ideograph-8B5B | 8b5a CJK Ideograph-8B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5d CJK Ideograph-8B5D | 8b5c CJK Ideograph-8B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b5f CJK Ideograph-8B5F | 8b5e CJK Ideograph-8B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b61 CJK Ideograph-8B61 | 8b60 CJK Ideograph-8B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b63 CJK Ideograph-8B63 | 8b62 CJK Ideograph-8B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b65 CJK Ideograph-8B65 | 8b64 CJK Ideograph-8B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b67 CJK Ideograph-8B67 | 8b66 CJK Ideograph-8B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b69 CJK Ideograph-8B69 | 8b68 CJK Ideograph-8B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6b CJK Ideograph-8B6B | 8b6a CJK Ideograph-8B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6d CJK Ideograph-8B6D | 8b6c CJK Ideograph-8B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b6f CJK Ideograph-8B6F | 8b6e CJK Ideograph-8B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b71 CJK Ideograph-8B71 | 8b70 CJK Ideograph-8B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b73 CJK Ideograph-8B73 | 8b72 CJK Ideograph-8B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b75 CJK Ideograph-8B75 | 8b74 CJK Ideograph-8B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b77 CJK Ideograph-8B77 | 8b76 CJK Ideograph-8B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b79 CJK Ideograph-8B79 | 8b78 CJK Ideograph-8B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7b CJK Ideograph-8B7B | 8b7a CJK Ideograph-8B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7d CJK Ideograph-8B7D | 8b7c CJK Ideograph-8B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b7f CJK Ideograph-8B7F | 8b7e CJK Ideograph-8B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b81 CJK Ideograph-8B81 | 8b80 CJK Ideograph-8B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b83 CJK Ideograph-8B83 | 8b82 CJK Ideograph-8B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b85 CJK Ideograph-8B85 | 8b84 CJK Ideograph-8B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b87 CJK Ideograph-8B87 | 8b86 CJK Ideograph-8B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b89 CJK Ideograph-8B89 | 8b88 CJK Ideograph-8B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8b CJK Ideograph-8B8B | 8b8a CJK Ideograph-8B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8d CJK Ideograph-8B8D | 8b8c CJK Ideograph-8B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b8f CJK Ideograph-8B8F | 8b8e CJK Ideograph-8B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b91 CJK Ideograph-8B91 | 8b90 CJK Ideograph-8B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b93 CJK Ideograph-8B93 | 8b92 CJK Ideograph-8B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b95 CJK Ideograph-8B95 | 8b94 CJK Ideograph-8B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b97 CJK Ideograph-8B97 | 8b96 CJK Ideograph-8B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b99 CJK Ideograph-8B99 | 8b98 CJK Ideograph-8B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9b CJK Ideograph-8B9B | 8b9a CJK Ideograph-8B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9d CJK Ideograph-8B9D | 8b9c CJK Ideograph-8B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8b9f CJK Ideograph-8B9F | 8b9e CJK Ideograph-8B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba1 CJK Ideograph-8BA1 | 8ba0 CJK Ideograph-8BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba3 CJK Ideograph-8BA3 | 8ba2 CJK Ideograph-8BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba5 CJK Ideograph-8BA5 | 8ba4 CJK Ideograph-8BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba7 CJK Ideograph-8BA7 | 8ba6 CJK Ideograph-8BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ba9 CJK Ideograph-8BA9 | 8ba8 CJK Ideograph-8BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bab CJK Ideograph-8BAB | 8baa CJK Ideograph-8BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bad CJK Ideograph-8BAD | 8bac CJK Ideograph-8BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8baf CJK Ideograph-8BAF | 8bae CJK Ideograph-8BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb1 CJK Ideograph-8BB1 | 8bb0 CJK Ideograph-8BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb3 CJK Ideograph-8BB3 | 8bb2 CJK Ideograph-8BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb5 CJK Ideograph-8BB5 | 8bb4 CJK Ideograph-8BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb7 CJK Ideograph-8BB7 | 8bb6 CJK Ideograph-8BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bb9 CJK Ideograph-8BB9 | 8bb8 CJK Ideograph-8BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbb CJK Ideograph-8BBB | 8bba CJK Ideograph-8BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbd CJK Ideograph-8BBD | 8bbc CJK Ideograph-8BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bbf CJK Ideograph-8BBF | 8bbe CJK Ideograph-8BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc1 CJK Ideograph-8BC1 | 8bc0 CJK Ideograph-8BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc3 CJK Ideograph-8BC3 | 8bc2 CJK Ideograph-8BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc5 CJK Ideograph-8BC5 | 8bc4 CJK Ideograph-8BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc7 CJK Ideograph-8BC7 | 8bc6 CJK Ideograph-8BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bc9 CJK Ideograph-8BC9 | 8bc8 CJK Ideograph-8BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcb CJK Ideograph-8BCB | 8bca CJK Ideograph-8BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcd CJK Ideograph-8BCD | 8bcc CJK Ideograph-8BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bcf CJK Ideograph-8BCF | 8bce CJK Ideograph-8BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd1 CJK Ideograph-8BD1 | 8bd0 CJK Ideograph-8BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd3 CJK Ideograph-8BD3 | 8bd2 CJK Ideograph-8BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd5 CJK Ideograph-8BD5 | 8bd4 CJK Ideograph-8BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd7 CJK Ideograph-8BD7 | 8bd6 CJK Ideograph-8BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bd9 CJK Ideograph-8BD9 | 8bd8 CJK Ideograph-8BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdb CJK Ideograph-8BDB | 8bda CJK Ideograph-8BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdd CJK Ideograph-8BDD | 8bdc CJK Ideograph-8BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bdf CJK Ideograph-8BDF | 8bde CJK Ideograph-8BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be1 CJK Ideograph-8BE1 | 8be0 CJK Ideograph-8BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be3 CJK Ideograph-8BE3 | 8be2 CJK Ideograph-8BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be5 CJK Ideograph-8BE5 | 8be4 CJK Ideograph-8BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be7 CJK Ideograph-8BE7 | 8be6 CJK Ideograph-8BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8be9 CJK Ideograph-8BE9 | 8be8 CJK Ideograph-8BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8beb CJK Ideograph-8BEB | 8bea CJK Ideograph-8BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bed CJK Ideograph-8BED | 8bec CJK Ideograph-8BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bef CJK Ideograph-8BEF | 8bee CJK Ideograph-8BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf1 CJK Ideograph-8BF1 | 8bf0 CJK Ideograph-8BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf3 CJK Ideograph-8BF3 | 8bf2 CJK Ideograph-8BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf5 CJK Ideograph-8BF5 | 8bf4 CJK Ideograph-8BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf7 CJK Ideograph-8BF7 | 8bf6 CJK Ideograph-8BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bf9 CJK Ideograph-8BF9 | 8bf8 CJK Ideograph-8BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bfb CJK Ideograph-8BFB | 8bfa CJK Ideograph-8BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bfd CJK Ideograph-8BFD | 8bfc CJK Ideograph-8BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8bff CJK Ideograph-8BFF | 8bfe CJK Ideograph-8BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c01 CJK Ideograph-8C01 | 8c00 CJK Ideograph-8C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c03 CJK Ideograph-8C03 | 8c02 CJK Ideograph-8C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c05 CJK Ideograph-8C05 | 8c04 CJK Ideograph-8C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c07 CJK Ideograph-8C07 | 8c06 CJK Ideograph-8C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c09 CJK Ideograph-8C09 | 8c08 CJK Ideograph-8C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0b CJK Ideograph-8C0B | 8c0a CJK Ideograph-8C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0d CJK Ideograph-8C0D | 8c0c CJK Ideograph-8C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c0f CJK Ideograph-8C0F | 8c0e CJK Ideograph-8C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c11 CJK Ideograph-8C11 | 8c10 CJK Ideograph-8C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c13 CJK Ideograph-8C13 | 8c12 CJK Ideograph-8C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c15 CJK Ideograph-8C15 | 8c14 CJK Ideograph-8C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c17 CJK Ideograph-8C17 | 8c16 CJK Ideograph-8C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c19 CJK Ideograph-8C19 | 8c18 CJK Ideograph-8C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1b CJK Ideograph-8C1B | 8c1a CJK Ideograph-8C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1d CJK Ideograph-8C1D | 8c1c CJK Ideograph-8C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c1f CJK Ideograph-8C1F | 8c1e CJK Ideograph-8C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c21 CJK Ideograph-8C21 | 8c20 CJK Ideograph-8C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c23 CJK Ideograph-8C23 | 8c22 CJK Ideograph-8C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c25 CJK Ideograph-8C25 | 8c24 CJK Ideograph-8C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c27 CJK Ideograph-8C27 | 8c26 CJK Ideograph-8C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c29 CJK Ideograph-8C29 | 8c28 CJK Ideograph-8C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2b CJK Ideograph-8C2B | 8c2a CJK Ideograph-8C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2d CJK Ideograph-8C2D | 8c2c CJK Ideograph-8C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c2f CJK Ideograph-8C2F | 8c2e CJK Ideograph-8C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c31 CJK Ideograph-8C31 | 8c30 CJK Ideograph-8C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c33 CJK Ideograph-8C33 | 8c32 CJK Ideograph-8C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c35 CJK Ideograph-8C35 | 8c34 CJK Ideograph-8C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c37 CJK Ideograph-8C37 | 8c36 CJK Ideograph-8C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c39 CJK Ideograph-8C39 | 8c38 CJK Ideograph-8C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3b CJK Ideograph-8C3B | 8c3a CJK Ideograph-8C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3d CJK Ideograph-8C3D | 8c3c CJK Ideograph-8C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c3f CJK Ideograph-8C3F | 8c3e CJK Ideograph-8C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c41 CJK Ideograph-8C41 | 8c40 CJK Ideograph-8C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c43 CJK Ideograph-8C43 | 8c42 CJK Ideograph-8C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c45 CJK Ideograph-8C45 | 8c44 CJK Ideograph-8C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c47 CJK Ideograph-8C47 | 8c46 CJK Ideograph-8C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c49 CJK Ideograph-8C49 | 8c48 CJK Ideograph-8C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4b CJK Ideograph-8C4B | 8c4a CJK Ideograph-8C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4d CJK Ideograph-8C4D | 8c4c CJK Ideograph-8C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c4f CJK Ideograph-8C4F | 8c4e CJK Ideograph-8C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c51 CJK Ideograph-8C51 | 8c50 CJK Ideograph-8C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c53 CJK Ideograph-8C53 | 8c52 CJK Ideograph-8C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c55 CJK Ideograph-8C55 | 8c54 CJK Ideograph-8C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c57 CJK Ideograph-8C57 | 8c56 CJK Ideograph-8C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c59 CJK Ideograph-8C59 | 8c58 CJK Ideograph-8C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5b CJK Ideograph-8C5B | 8c5a CJK Ideograph-8C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5d CJK Ideograph-8C5D | 8c5c CJK Ideograph-8C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c5f CJK Ideograph-8C5F | 8c5e CJK Ideograph-8C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c61 CJK Ideograph-8C61 | 8c60 CJK Ideograph-8C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c63 CJK Ideograph-8C63 | 8c62 CJK Ideograph-8C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c65 CJK Ideograph-8C65 | 8c64 CJK Ideograph-8C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c67 CJK Ideograph-8C67 | 8c66 CJK Ideograph-8C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c69 CJK Ideograph-8C69 | 8c68 CJK Ideograph-8C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6b CJK Ideograph-8C6B | 8c6a CJK Ideograph-8C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6d CJK Ideograph-8C6D | 8c6c CJK Ideograph-8C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c6f CJK Ideograph-8C6F | 8c6e CJK Ideograph-8C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c71 CJK Ideograph-8C71 | 8c70 CJK Ideograph-8C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c73 CJK Ideograph-8C73 | 8c72 CJK Ideograph-8C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c75 CJK Ideograph-8C75 | 8c74 CJK Ideograph-8C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c77 CJK Ideograph-8C77 | 8c76 CJK Ideograph-8C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c79 CJK Ideograph-8C79 | 8c78 CJK Ideograph-8C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7b CJK Ideograph-8C7B | 8c7a CJK Ideograph-8C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7d CJK Ideograph-8C7D | 8c7c CJK Ideograph-8C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c7f CJK Ideograph-8C7F | 8c7e CJK Ideograph-8C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c81 CJK Ideograph-8C81 | 8c80 CJK Ideograph-8C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c83 CJK Ideograph-8C83 | 8c82 CJK Ideograph-8C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c85 CJK Ideograph-8C85 | 8c84 CJK Ideograph-8C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c87 CJK Ideograph-8C87 | 8c86 CJK Ideograph-8C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c89 CJK Ideograph-8C89 | 8c88 CJK Ideograph-8C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8b CJK Ideograph-8C8B | 8c8a CJK Ideograph-8C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8d CJK Ideograph-8C8D | 8c8c CJK Ideograph-8C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c8f CJK Ideograph-8C8F | 8c8e CJK Ideograph-8C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c91 CJK Ideograph-8C91 | 8c90 CJK Ideograph-8C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c93 CJK Ideograph-8C93 | 8c92 CJK Ideograph-8C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c95 CJK Ideograph-8C95 | 8c94 CJK Ideograph-8C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c97 CJK Ideograph-8C97 | 8c96 CJK Ideograph-8C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c99 CJK Ideograph-8C99 | 8c98 CJK Ideograph-8C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9b CJK Ideograph-8C9B | 8c9a CJK Ideograph-8C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9d CJK Ideograph-8C9D | 8c9c CJK Ideograph-8C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8c9f CJK Ideograph-8C9F | 8c9e CJK Ideograph-8C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca1 CJK Ideograph-8CA1 | 8ca0 CJK Ideograph-8CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca3 CJK Ideograph-8CA3 | 8ca2 CJK Ideograph-8CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca5 CJK Ideograph-8CA5 | 8ca4 CJK Ideograph-8CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca7 CJK Ideograph-8CA7 | 8ca6 CJK Ideograph-8CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ca9 CJK Ideograph-8CA9 | 8ca8 CJK Ideograph-8CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cab CJK Ideograph-8CAB | 8caa CJK Ideograph-8CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cad CJK Ideograph-8CAD | 8cac CJK Ideograph-8CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8caf CJK Ideograph-8CAF | 8cae CJK Ideograph-8CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb1 CJK Ideograph-8CB1 | 8cb0 CJK Ideograph-8CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb3 CJK Ideograph-8CB3 | 8cb2 CJK Ideograph-8CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb5 CJK Ideograph-8CB5 | 8cb4 CJK Ideograph-8CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb7 CJK Ideograph-8CB7 | 8cb6 CJK Ideograph-8CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cb9 CJK Ideograph-8CB9 | 8cb8 CJK Ideograph-8CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbb CJK Ideograph-8CBB | 8cba CJK Ideograph-8CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbd CJK Ideograph-8CBD | 8cbc CJK Ideograph-8CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cbf CJK Ideograph-8CBF | 8cbe CJK Ideograph-8CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc1 CJK Ideograph-8CC1 | 8cc0 CJK Ideograph-8CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc3 CJK Ideograph-8CC3 | 8cc2 CJK Ideograph-8CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc5 CJK Ideograph-8CC5 | 8cc4 CJK Ideograph-8CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc7 CJK Ideograph-8CC7 | 8cc6 CJK Ideograph-8CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cc9 CJK Ideograph-8CC9 | 8cc8 CJK Ideograph-8CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccb CJK Ideograph-8CCB | 8cca CJK Ideograph-8CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccd CJK Ideograph-8CCD | 8ccc CJK Ideograph-8CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ccf CJK Ideograph-8CCF | 8cce CJK Ideograph-8CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd1 CJK Ideograph-8CD1 | 8cd0 CJK Ideograph-8CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd3 CJK Ideograph-8CD3 | 8cd2 CJK Ideograph-8CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd5 CJK Ideograph-8CD5 | 8cd4 CJK Ideograph-8CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd7 CJK Ideograph-8CD7 | 8cd6 CJK Ideograph-8CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cd9 CJK Ideograph-8CD9 | 8cd8 CJK Ideograph-8CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdb CJK Ideograph-8CDB | 8cda CJK Ideograph-8CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdd CJK Ideograph-8CDD | 8cdc CJK Ideograph-8CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cdf CJK Ideograph-8CDF | 8cde CJK Ideograph-8CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce1 CJK Ideograph-8CE1 | 8ce0 CJK Ideograph-8CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce3 CJK Ideograph-8CE3 | 8ce2 CJK Ideograph-8CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce5 CJK Ideograph-8CE5 | 8ce4 CJK Ideograph-8CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce7 CJK Ideograph-8CE7 | 8ce6 CJK Ideograph-8CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ce9 CJK Ideograph-8CE9 | 8ce8 CJK Ideograph-8CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ceb CJK Ideograph-8CEB | 8cea CJK Ideograph-8CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ced CJK Ideograph-8CED | 8cec CJK Ideograph-8CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cef CJK Ideograph-8CEF | 8cee CJK Ideograph-8CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf1 CJK Ideograph-8CF1 | 8cf0 CJK Ideograph-8CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf3 CJK Ideograph-8CF3 | 8cf2 CJK Ideograph-8CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf5 CJK Ideograph-8CF5 | 8cf4 CJK Ideograph-8CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf7 CJK Ideograph-8CF7 | 8cf6 CJK Ideograph-8CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cf9 CJK Ideograph-8CF9 | 8cf8 CJK Ideograph-8CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cfb CJK Ideograph-8CFB | 8cfa CJK Ideograph-8CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cfd CJK Ideograph-8CFD | 8cfc CJK Ideograph-8CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8cff CJK Ideograph-8CFF | 8cfe CJK Ideograph-8CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d01 CJK Ideograph-8D01 | 8d00 CJK Ideograph-8D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d03 CJK Ideograph-8D03 | 8d02 CJK Ideograph-8D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d05 CJK Ideograph-8D05 | 8d04 CJK Ideograph-8D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d07 CJK Ideograph-8D07 | 8d06 CJK Ideograph-8D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d09 CJK Ideograph-8D09 | 8d08 CJK Ideograph-8D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0b CJK Ideograph-8D0B | 8d0a CJK Ideograph-8D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0d CJK Ideograph-8D0D | 8d0c CJK Ideograph-8D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d0f CJK Ideograph-8D0F | 8d0e CJK Ideograph-8D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d11 CJK Ideograph-8D11 | 8d10 CJK Ideograph-8D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d13 CJK Ideograph-8D13 | 8d12 CJK Ideograph-8D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d15 CJK Ideograph-8D15 | 8d14 CJK Ideograph-8D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d17 CJK Ideograph-8D17 | 8d16 CJK Ideograph-8D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d19 CJK Ideograph-8D19 | 8d18 CJK Ideograph-8D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1b CJK Ideograph-8D1B | 8d1a CJK Ideograph-8D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1d CJK Ideograph-8D1D | 8d1c CJK Ideograph-8D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d1f CJK Ideograph-8D1F | 8d1e CJK Ideograph-8D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d21 CJK Ideograph-8D21 | 8d20 CJK Ideograph-8D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d23 CJK Ideograph-8D23 | 8d22 CJK Ideograph-8D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d25 CJK Ideograph-8D25 | 8d24 CJK Ideograph-8D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d27 CJK Ideograph-8D27 | 8d26 CJK Ideograph-8D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d29 CJK Ideograph-8D29 | 8d28 CJK Ideograph-8D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2b CJK Ideograph-8D2B | 8d2a CJK Ideograph-8D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2d CJK Ideograph-8D2D | 8d2c CJK Ideograph-8D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d2f CJK Ideograph-8D2F | 8d2e CJK Ideograph-8D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d31 CJK Ideograph-8D31 | 8d30 CJK Ideograph-8D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d33 CJK Ideograph-8D33 | 8d32 CJK Ideograph-8D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d35 CJK Ideograph-8D35 | 8d34 CJK Ideograph-8D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d37 CJK Ideograph-8D37 | 8d36 CJK Ideograph-8D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d39 CJK Ideograph-8D39 | 8d38 CJK Ideograph-8D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3b CJK Ideograph-8D3B | 8d3a CJK Ideograph-8D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3d CJK Ideograph-8D3D | 8d3c CJK Ideograph-8D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d3f CJK Ideograph-8D3F | 8d3e CJK Ideograph-8D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d41 CJK Ideograph-8D41 | 8d40 CJK Ideograph-8D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d43 CJK Ideograph-8D43 | 8d42 CJK Ideograph-8D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d45 CJK Ideograph-8D45 | 8d44 CJK Ideograph-8D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d47 CJK Ideograph-8D47 | 8d46 CJK Ideograph-8D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d49 CJK Ideograph-8D49 | 8d48 CJK Ideograph-8D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4b CJK Ideograph-8D4B | 8d4a CJK Ideograph-8D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4d CJK Ideograph-8D4D | 8d4c CJK Ideograph-8D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d4f CJK Ideograph-8D4F | 8d4e CJK Ideograph-8D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d51 CJK Ideograph-8D51 | 8d50 CJK Ideograph-8D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d53 CJK Ideograph-8D53 | 8d52 CJK Ideograph-8D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d55 CJK Ideograph-8D55 | 8d54 CJK Ideograph-8D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d57 CJK Ideograph-8D57 | 8d56 CJK Ideograph-8D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d59 CJK Ideograph-8D59 | 8d58 CJK Ideograph-8D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5b CJK Ideograph-8D5B | 8d5a CJK Ideograph-8D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5d CJK Ideograph-8D5D | 8d5c CJK Ideograph-8D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d5f CJK Ideograph-8D5F | 8d5e CJK Ideograph-8D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d61 CJK Ideograph-8D61 | 8d60 CJK Ideograph-8D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d63 CJK Ideograph-8D63 | 8d62 CJK Ideograph-8D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d65 CJK Ideograph-8D65 | 8d64 CJK Ideograph-8D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d67 CJK Ideograph-8D67 | 8d66 CJK Ideograph-8D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d69 CJK Ideograph-8D69 | 8d68 CJK Ideograph-8D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6b CJK Ideograph-8D6B | 8d6a CJK Ideograph-8D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6d CJK Ideograph-8D6D | 8d6c CJK Ideograph-8D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d6f CJK Ideograph-8D6F | 8d6e CJK Ideograph-8D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d71 CJK Ideograph-8D71 | 8d70 CJK Ideograph-8D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d73 CJK Ideograph-8D73 | 8d72 CJK Ideograph-8D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d75 CJK Ideograph-8D75 | 8d74 CJK Ideograph-8D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d77 CJK Ideograph-8D77 | 8d76 CJK Ideograph-8D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d79 CJK Ideograph-8D79 | 8d78 CJK Ideograph-8D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7b CJK Ideograph-8D7B | 8d7a CJK Ideograph-8D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7d CJK Ideograph-8D7D | 8d7c CJK Ideograph-8D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d7f CJK Ideograph-8D7F | 8d7e CJK Ideograph-8D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d81 CJK Ideograph-8D81 | 8d80 CJK Ideograph-8D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d83 CJK Ideograph-8D83 | 8d82 CJK Ideograph-8D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d85 CJK Ideograph-8D85 | 8d84 CJK Ideograph-8D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d87 CJK Ideograph-8D87 | 8d86 CJK Ideograph-8D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d89 CJK Ideograph-8D89 | 8d88 CJK Ideograph-8D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8b CJK Ideograph-8D8B | 8d8a CJK Ideograph-8D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8d CJK Ideograph-8D8D | 8d8c CJK Ideograph-8D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d8f CJK Ideograph-8D8F | 8d8e CJK Ideograph-8D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d91 CJK Ideograph-8D91 | 8d90 CJK Ideograph-8D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d93 CJK Ideograph-8D93 | 8d92 CJK Ideograph-8D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d95 CJK Ideograph-8D95 | 8d94 CJK Ideograph-8D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d97 CJK Ideograph-8D97 | 8d96 CJK Ideograph-8D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d99 CJK Ideograph-8D99 | 8d98 CJK Ideograph-8D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9b CJK Ideograph-8D9B | 8d9a CJK Ideograph-8D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9d CJK Ideograph-8D9D | 8d9c CJK Ideograph-8D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8d9f CJK Ideograph-8D9F | 8d9e CJK Ideograph-8D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da1 CJK Ideograph-8DA1 | 8da0 CJK Ideograph-8DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da3 CJK Ideograph-8DA3 | 8da2 CJK Ideograph-8DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da5 CJK Ideograph-8DA5 | 8da4 CJK Ideograph-8DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da7 CJK Ideograph-8DA7 | 8da6 CJK Ideograph-8DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8da9 CJK Ideograph-8DA9 | 8da8 CJK Ideograph-8DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dab CJK Ideograph-8DAB | 8daa CJK Ideograph-8DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dad CJK Ideograph-8DAD | 8dac CJK Ideograph-8DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8daf CJK Ideograph-8DAF | 8dae CJK Ideograph-8DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db1 CJK Ideograph-8DB1 | 8db0 CJK Ideograph-8DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db3 CJK Ideograph-8DB3 | 8db2 CJK Ideograph-8DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db5 CJK Ideograph-8DB5 | 8db4 CJK Ideograph-8DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db7 CJK Ideograph-8DB7 | 8db6 CJK Ideograph-8DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8db9 CJK Ideograph-8DB9 | 8db8 CJK Ideograph-8DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbb CJK Ideograph-8DBB | 8dba CJK Ideograph-8DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbd CJK Ideograph-8DBD | 8dbc CJK Ideograph-8DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dbf CJK Ideograph-8DBF | 8dbe CJK Ideograph-8DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc1 CJK Ideograph-8DC1 | 8dc0 CJK Ideograph-8DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc3 CJK Ideograph-8DC3 | 8dc2 CJK Ideograph-8DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc5 CJK Ideograph-8DC5 | 8dc4 CJK Ideograph-8DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc7 CJK Ideograph-8DC7 | 8dc6 CJK Ideograph-8DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dc9 CJK Ideograph-8DC9 | 8dc8 CJK Ideograph-8DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcb CJK Ideograph-8DCB | 8dca CJK Ideograph-8DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcd CJK Ideograph-8DCD | 8dcc CJK Ideograph-8DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dcf CJK Ideograph-8DCF | 8dce CJK Ideograph-8DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd1 CJK Ideograph-8DD1 | 8dd0 CJK Ideograph-8DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd3 CJK Ideograph-8DD3 | 8dd2 CJK Ideograph-8DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd5 CJK Ideograph-8DD5 | 8dd4 CJK Ideograph-8DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd7 CJK Ideograph-8DD7 | 8dd6 CJK Ideograph-8DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dd9 CJK Ideograph-8DD9 | 8dd8 CJK Ideograph-8DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddb CJK Ideograph-8DDB | 8dda CJK Ideograph-8DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddd CJK Ideograph-8DDD | 8ddc CJK Ideograph-8DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ddf CJK Ideograph-8DDF | 8dde CJK Ideograph-8DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de1 CJK Ideograph-8DE1 | 8de0 CJK Ideograph-8DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de3 CJK Ideograph-8DE3 | 8de2 CJK Ideograph-8DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de5 CJK Ideograph-8DE5 | 8de4 CJK Ideograph-8DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de7 CJK Ideograph-8DE7 | 8de6 CJK Ideograph-8DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8de9 CJK Ideograph-8DE9 | 8de8 CJK Ideograph-8DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8deb CJK Ideograph-8DEB | 8dea CJK Ideograph-8DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ded CJK Ideograph-8DED | 8dec CJK Ideograph-8DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8def CJK Ideograph-8DEF | 8dee CJK Ideograph-8DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df1 CJK Ideograph-8DF1 | 8df0 CJK Ideograph-8DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df3 CJK Ideograph-8DF3 | 8df2 CJK Ideograph-8DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df5 CJK Ideograph-8DF5 | 8df4 CJK Ideograph-8DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df7 CJK Ideograph-8DF7 | 8df6 CJK Ideograph-8DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8df9 CJK Ideograph-8DF9 | 8df8 CJK Ideograph-8DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dfb CJK Ideograph-8DFB | 8dfa CJK Ideograph-8DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dfd CJK Ideograph-8DFD | 8dfc CJK Ideograph-8DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8dff CJK Ideograph-8DFF | 8dfe CJK Ideograph-8DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e01 CJK Ideograph-8E01 | 8e00 CJK Ideograph-8E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e03 CJK Ideograph-8E03 | 8e02 CJK Ideograph-8E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e05 CJK Ideograph-8E05 | 8e04 CJK Ideograph-8E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e07 CJK Ideograph-8E07 | 8e06 CJK Ideograph-8E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e09 CJK Ideograph-8E09 | 8e08 CJK Ideograph-8E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0b CJK Ideograph-8E0B | 8e0a CJK Ideograph-8E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0d CJK Ideograph-8E0D | 8e0c CJK Ideograph-8E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e0f CJK Ideograph-8E0F | 8e0e CJK Ideograph-8E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e11 CJK Ideograph-8E11 | 8e10 CJK Ideograph-8E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e13 CJK Ideograph-8E13 | 8e12 CJK Ideograph-8E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e15 CJK Ideograph-8E15 | 8e14 CJK Ideograph-8E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e17 CJK Ideograph-8E17 | 8e16 CJK Ideograph-8E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e19 CJK Ideograph-8E19 | 8e18 CJK Ideograph-8E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1b CJK Ideograph-8E1B | 8e1a CJK Ideograph-8E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1d CJK Ideograph-8E1D | 8e1c CJK Ideograph-8E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e1f CJK Ideograph-8E1F | 8e1e CJK Ideograph-8E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e21 CJK Ideograph-8E21 | 8e20 CJK Ideograph-8E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e23 CJK Ideograph-8E23 | 8e22 CJK Ideograph-8E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e25 CJK Ideograph-8E25 | 8e24 CJK Ideograph-8E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e27 CJK Ideograph-8E27 | 8e26 CJK Ideograph-8E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e29 CJK Ideograph-8E29 | 8e28 CJK Ideograph-8E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2b CJK Ideograph-8E2B | 8e2a CJK Ideograph-8E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2d CJK Ideograph-8E2D | 8e2c CJK Ideograph-8E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e2f CJK Ideograph-8E2F | 8e2e CJK Ideograph-8E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e31 CJK Ideograph-8E31 | 8e30 CJK Ideograph-8E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e33 CJK Ideograph-8E33 | 8e32 CJK Ideograph-8E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e35 CJK Ideograph-8E35 | 8e34 CJK Ideograph-8E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e37 CJK Ideograph-8E37 | 8e36 CJK Ideograph-8E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e39 CJK Ideograph-8E39 | 8e38 CJK Ideograph-8E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3b CJK Ideograph-8E3B | 8e3a CJK Ideograph-8E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3d CJK Ideograph-8E3D | 8e3c CJK Ideograph-8E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e3f CJK Ideograph-8E3F | 8e3e CJK Ideograph-8E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e41 CJK Ideograph-8E41 | 8e40 CJK Ideograph-8E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e43 CJK Ideograph-8E43 | 8e42 CJK Ideograph-8E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e45 CJK Ideograph-8E45 | 8e44 CJK Ideograph-8E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e47 CJK Ideograph-8E47 | 8e46 CJK Ideograph-8E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e49 CJK Ideograph-8E49 | 8e48 CJK Ideograph-8E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4b CJK Ideograph-8E4B | 8e4a CJK Ideograph-8E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4d CJK Ideograph-8E4D | 8e4c CJK Ideograph-8E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e4f CJK Ideograph-8E4F | 8e4e CJK Ideograph-8E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e51 CJK Ideograph-8E51 | 8e50 CJK Ideograph-8E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e53 CJK Ideograph-8E53 | 8e52 CJK Ideograph-8E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e55 CJK Ideograph-8E55 | 8e54 CJK Ideograph-8E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e57 CJK Ideograph-8E57 | 8e56 CJK Ideograph-8E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e59 CJK Ideograph-8E59 | 8e58 CJK Ideograph-8E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5b CJK Ideograph-8E5B | 8e5a CJK Ideograph-8E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5d CJK Ideograph-8E5D | 8e5c CJK Ideograph-8E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e5f CJK Ideograph-8E5F | 8e5e CJK Ideograph-8E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e61 CJK Ideograph-8E61 | 8e60 CJK Ideograph-8E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e63 CJK Ideograph-8E63 | 8e62 CJK Ideograph-8E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e65 CJK Ideograph-8E65 | 8e64 CJK Ideograph-8E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e67 CJK Ideograph-8E67 | 8e66 CJK Ideograph-8E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e69 CJK Ideograph-8E69 | 8e68 CJK Ideograph-8E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6b CJK Ideograph-8E6B | 8e6a CJK Ideograph-8E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6d CJK Ideograph-8E6D | 8e6c CJK Ideograph-8E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e6f CJK Ideograph-8E6F | 8e6e CJK Ideograph-8E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e71 CJK Ideograph-8E71 | 8e70 CJK Ideograph-8E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e73 CJK Ideograph-8E73 | 8e72 CJK Ideograph-8E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e75 CJK Ideograph-8E75 | 8e74 CJK Ideograph-8E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e77 CJK Ideograph-8E77 | 8e76 CJK Ideograph-8E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e79 CJK Ideograph-8E79 | 8e78 CJK Ideograph-8E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7b CJK Ideograph-8E7B | 8e7a CJK Ideograph-8E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7d CJK Ideograph-8E7D | 8e7c CJK Ideograph-8E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e7f CJK Ideograph-8E7F | 8e7e CJK Ideograph-8E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e81 CJK Ideograph-8E81 | 8e80 CJK Ideograph-8E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e83 CJK Ideograph-8E83 | 8e82 CJK Ideograph-8E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e85 CJK Ideograph-8E85 | 8e84 CJK Ideograph-8E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e87 CJK Ideograph-8E87 | 8e86 CJK Ideograph-8E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e89 CJK Ideograph-8E89 | 8e88 CJK Ideograph-8E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8b CJK Ideograph-8E8B | 8e8a CJK Ideograph-8E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8d CJK Ideograph-8E8D | 8e8c CJK Ideograph-8E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e8f CJK Ideograph-8E8F | 8e8e CJK Ideograph-8E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e91 CJK Ideograph-8E91 | 8e90 CJK Ideograph-8E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e93 CJK Ideograph-8E93 | 8e92 CJK Ideograph-8E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e95 CJK Ideograph-8E95 | 8e94 CJK Ideograph-8E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e97 CJK Ideograph-8E97 | 8e96 CJK Ideograph-8E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e99 CJK Ideograph-8E99 | 8e98 CJK Ideograph-8E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9b CJK Ideograph-8E9B | 8e9a CJK Ideograph-8E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9d CJK Ideograph-8E9D | 8e9c CJK Ideograph-8E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8e9f CJK Ideograph-8E9F | 8e9e CJK Ideograph-8E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea1 CJK Ideograph-8EA1 | 8ea0 CJK Ideograph-8EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea3 CJK Ideograph-8EA3 | 8ea2 CJK Ideograph-8EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea5 CJK Ideograph-8EA5 | 8ea4 CJK Ideograph-8EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea7 CJK Ideograph-8EA7 | 8ea6 CJK Ideograph-8EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ea9 CJK Ideograph-8EA9 | 8ea8 CJK Ideograph-8EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eab CJK Ideograph-8EAB | 8eaa CJK Ideograph-8EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ead CJK Ideograph-8EAD | 8eac CJK Ideograph-8EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eaf CJK Ideograph-8EAF | 8eae CJK Ideograph-8EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb1 CJK Ideograph-8EB1 | 8eb0 CJK Ideograph-8EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb3 CJK Ideograph-8EB3 | 8eb2 CJK Ideograph-8EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb5 CJK Ideograph-8EB5 | 8eb4 CJK Ideograph-8EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb7 CJK Ideograph-8EB7 | 8eb6 CJK Ideograph-8EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eb9 CJK Ideograph-8EB9 | 8eb8 CJK Ideograph-8EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebb CJK Ideograph-8EBB | 8eba CJK Ideograph-8EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebd CJK Ideograph-8EBD | 8ebc CJK Ideograph-8EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ebf CJK Ideograph-8EBF | 8ebe CJK Ideograph-8EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec1 CJK Ideograph-8EC1 | 8ec0 CJK Ideograph-8EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec3 CJK Ideograph-8EC3 | 8ec2 CJK Ideograph-8EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec5 CJK Ideograph-8EC5 | 8ec4 CJK Ideograph-8EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec7 CJK Ideograph-8EC7 | 8ec6 CJK Ideograph-8EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ec9 CJK Ideograph-8EC9 | 8ec8 CJK Ideograph-8EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecb CJK Ideograph-8ECB | 8eca CJK Ideograph-8ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecd CJK Ideograph-8ECD | 8ecc CJK Ideograph-8ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ecf CJK Ideograph-8ECF | 8ece CJK Ideograph-8ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed1 CJK Ideograph-8ED1 | 8ed0 CJK Ideograph-8ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed3 CJK Ideograph-8ED3 | 8ed2 CJK Ideograph-8ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed5 CJK Ideograph-8ED5 | 8ed4 CJK Ideograph-8ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed7 CJK Ideograph-8ED7 | 8ed6 CJK Ideograph-8ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ed9 CJK Ideograph-8ED9 | 8ed8 CJK Ideograph-8ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edb CJK Ideograph-8EDB | 8eda CJK Ideograph-8EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edd CJK Ideograph-8EDD | 8edc CJK Ideograph-8EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8edf CJK Ideograph-8EDF | 8ede CJK Ideograph-8EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee1 CJK Ideograph-8EE1 | 8ee0 CJK Ideograph-8EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee3 CJK Ideograph-8EE3 | 8ee2 CJK Ideograph-8EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee5 CJK Ideograph-8EE5 | 8ee4 CJK Ideograph-8EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee7 CJK Ideograph-8EE7 | 8ee6 CJK Ideograph-8EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ee9 CJK Ideograph-8EE9 | 8ee8 CJK Ideograph-8EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eeb CJK Ideograph-8EEB | 8eea CJK Ideograph-8EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eed CJK Ideograph-8EED | 8eec CJK Ideograph-8EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eef CJK Ideograph-8EEF | 8eee CJK Ideograph-8EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef1 CJK Ideograph-8EF1 | 8ef0 CJK Ideograph-8EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef3 CJK Ideograph-8EF3 | 8ef2 CJK Ideograph-8EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef5 CJK Ideograph-8EF5 | 8ef4 CJK Ideograph-8EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef7 CJK Ideograph-8EF7 | 8ef6 CJK Ideograph-8EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ef9 CJK Ideograph-8EF9 | 8ef8 CJK Ideograph-8EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8efb CJK Ideograph-8EFB | 8efa CJK Ideograph-8EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8efd CJK Ideograph-8EFD | 8efc CJK Ideograph-8EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8eff CJK Ideograph-8EFF | 8efe CJK Ideograph-8EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f01 CJK Ideograph-8F01 | 8f00 CJK Ideograph-8F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f03 CJK Ideograph-8F03 | 8f02 CJK Ideograph-8F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f05 CJK Ideograph-8F05 | 8f04 CJK Ideograph-8F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f07 CJK Ideograph-8F07 | 8f06 CJK Ideograph-8F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f09 CJK Ideograph-8F09 | 8f08 CJK Ideograph-8F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0b CJK Ideograph-8F0B | 8f0a CJK Ideograph-8F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0d CJK Ideograph-8F0D | 8f0c CJK Ideograph-8F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f0f CJK Ideograph-8F0F | 8f0e CJK Ideograph-8F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f11 CJK Ideograph-8F11 | 8f10 CJK Ideograph-8F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f13 CJK Ideograph-8F13 | 8f12 CJK Ideograph-8F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f15 CJK Ideograph-8F15 | 8f14 CJK Ideograph-8F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f17 CJK Ideograph-8F17 | 8f16 CJK Ideograph-8F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f19 CJK Ideograph-8F19 | 8f18 CJK Ideograph-8F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1b CJK Ideograph-8F1B | 8f1a CJK Ideograph-8F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1d CJK Ideograph-8F1D | 8f1c CJK Ideograph-8F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f1f CJK Ideograph-8F1F | 8f1e CJK Ideograph-8F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f21 CJK Ideograph-8F21 | 8f20 CJK Ideograph-8F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f23 CJK Ideograph-8F23 | 8f22 CJK Ideograph-8F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f25 CJK Ideograph-8F25 | 8f24 CJK Ideograph-8F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f27 CJK Ideograph-8F27 | 8f26 CJK Ideograph-8F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f29 CJK Ideograph-8F29 | 8f28 CJK Ideograph-8F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2b CJK Ideograph-8F2B | 8f2a CJK Ideograph-8F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2d CJK Ideograph-8F2D | 8f2c CJK Ideograph-8F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f2f CJK Ideograph-8F2F | 8f2e CJK Ideograph-8F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f31 CJK Ideograph-8F31 | 8f30 CJK Ideograph-8F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f33 CJK Ideograph-8F33 | 8f32 CJK Ideograph-8F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f35 CJK Ideograph-8F35 | 8f34 CJK Ideograph-8F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f37 CJK Ideograph-8F37 | 8f36 CJK Ideograph-8F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f39 CJK Ideograph-8F39 | 8f38 CJK Ideograph-8F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3b CJK Ideograph-8F3B | 8f3a CJK Ideograph-8F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3d CJK Ideograph-8F3D | 8f3c CJK Ideograph-8F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f3f CJK Ideograph-8F3F | 8f3e CJK Ideograph-8F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f41 CJK Ideograph-8F41 | 8f40 CJK Ideograph-8F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f43 CJK Ideograph-8F43 | 8f42 CJK Ideograph-8F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f45 CJK Ideograph-8F45 | 8f44 CJK Ideograph-8F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f47 CJK Ideograph-8F47 | 8f46 CJK Ideograph-8F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f49 CJK Ideograph-8F49 | 8f48 CJK Ideograph-8F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4b CJK Ideograph-8F4B | 8f4a CJK Ideograph-8F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4d CJK Ideograph-8F4D | 8f4c CJK Ideograph-8F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f4f CJK Ideograph-8F4F | 8f4e CJK Ideograph-8F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f51 CJK Ideograph-8F51 | 8f50 CJK Ideograph-8F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f53 CJK Ideograph-8F53 | 8f52 CJK Ideograph-8F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f55 CJK Ideograph-8F55 | 8f54 CJK Ideograph-8F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f57 CJK Ideograph-8F57 | 8f56 CJK Ideograph-8F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f59 CJK Ideograph-8F59 | 8f58 CJK Ideograph-8F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5b CJK Ideograph-8F5B | 8f5a CJK Ideograph-8F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5d CJK Ideograph-8F5D | 8f5c CJK Ideograph-8F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f5f CJK Ideograph-8F5F | 8f5e CJK Ideograph-8F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f61 CJK Ideograph-8F61 | 8f60 CJK Ideograph-8F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f63 CJK Ideograph-8F63 | 8f62 CJK Ideograph-8F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f65 CJK Ideograph-8F65 | 8f64 CJK Ideograph-8F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f67 CJK Ideograph-8F67 | 8f66 CJK Ideograph-8F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f69 CJK Ideograph-8F69 | 8f68 CJK Ideograph-8F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6b CJK Ideograph-8F6B | 8f6a CJK Ideograph-8F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6d CJK Ideograph-8F6D | 8f6c CJK Ideograph-8F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f6f CJK Ideograph-8F6F | 8f6e CJK Ideograph-8F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f71 CJK Ideograph-8F71 | 8f70 CJK Ideograph-8F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f73 CJK Ideograph-8F73 | 8f72 CJK Ideograph-8F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f75 CJK Ideograph-8F75 | 8f74 CJK Ideograph-8F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f77 CJK Ideograph-8F77 | 8f76 CJK Ideograph-8F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f79 CJK Ideograph-8F79 | 8f78 CJK Ideograph-8F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7b CJK Ideograph-8F7B | 8f7a CJK Ideograph-8F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7d CJK Ideograph-8F7D | 8f7c CJK Ideograph-8F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f7f CJK Ideograph-8F7F | 8f7e CJK Ideograph-8F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f81 CJK Ideograph-8F81 | 8f80 CJK Ideograph-8F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f83 CJK Ideograph-8F83 | 8f82 CJK Ideograph-8F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f85 CJK Ideograph-8F85 | 8f84 CJK Ideograph-8F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f87 CJK Ideograph-8F87 | 8f86 CJK Ideograph-8F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f89 CJK Ideograph-8F89 | 8f88 CJK Ideograph-8F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8b CJK Ideograph-8F8B | 8f8a CJK Ideograph-8F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8d CJK Ideograph-8F8D | 8f8c CJK Ideograph-8F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f8f CJK Ideograph-8F8F | 8f8e CJK Ideograph-8F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f91 CJK Ideograph-8F91 | 8f90 CJK Ideograph-8F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f93 CJK Ideograph-8F93 | 8f92 CJK Ideograph-8F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f95 CJK Ideograph-8F95 | 8f94 CJK Ideograph-8F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f97 CJK Ideograph-8F97 | 8f96 CJK Ideograph-8F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f99 CJK Ideograph-8F99 | 8f98 CJK Ideograph-8F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9b CJK Ideograph-8F9B | 8f9a CJK Ideograph-8F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9d CJK Ideograph-8F9D | 8f9c CJK Ideograph-8F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8f9f CJK Ideograph-8F9F | 8f9e CJK Ideograph-8F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa1 CJK Ideograph-8FA1 | 8fa0 CJK Ideograph-8FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa3 CJK Ideograph-8FA3 | 8fa2 CJK Ideograph-8FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa5 CJK Ideograph-8FA5 | 8fa4 CJK Ideograph-8FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa7 CJK Ideograph-8FA7 | 8fa6 CJK Ideograph-8FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fa9 CJK Ideograph-8FA9 | 8fa8 CJK Ideograph-8FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fab CJK Ideograph-8FAB | 8faa CJK Ideograph-8FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fad CJK Ideograph-8FAD | 8fac CJK Ideograph-8FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8faf CJK Ideograph-8FAF | 8fae CJK Ideograph-8FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb1 CJK Ideograph-8FB1 | 8fb0 CJK Ideograph-8FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb3 CJK Ideograph-8FB3 | 8fb2 CJK Ideograph-8FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb5 CJK Ideograph-8FB5 | 8fb4 CJK Ideograph-8FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb7 CJK Ideograph-8FB7 | 8fb6 CJK Ideograph-8FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fb9 CJK Ideograph-8FB9 | 8fb8 CJK Ideograph-8FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbb CJK Ideograph-8FBB | 8fba CJK Ideograph-8FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbd CJK Ideograph-8FBD | 8fbc CJK Ideograph-8FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fbf CJK Ideograph-8FBF | 8fbe CJK Ideograph-8FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc1 CJK Ideograph-8FC1 | 8fc0 CJK Ideograph-8FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc3 CJK Ideograph-8FC3 | 8fc2 CJK Ideograph-8FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc5 CJK Ideograph-8FC5 | 8fc4 CJK Ideograph-8FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc7 CJK Ideograph-8FC7 | 8fc6 CJK Ideograph-8FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fc9 CJK Ideograph-8FC9 | 8fc8 CJK Ideograph-8FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcb CJK Ideograph-8FCB | 8fca CJK Ideograph-8FCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcd CJK Ideograph-8FCD | 8fcc CJK Ideograph-8FCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fcf CJK Ideograph-8FCF | 8fce CJK Ideograph-8FCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd1 CJK Ideograph-8FD1 | 8fd0 CJK Ideograph-8FD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd3 CJK Ideograph-8FD3 | 8fd2 CJK Ideograph-8FD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd5 CJK Ideograph-8FD5 | 8fd4 CJK Ideograph-8FD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd7 CJK Ideograph-8FD7 | 8fd6 CJK Ideograph-8FD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fd9 CJK Ideograph-8FD9 | 8fd8 CJK Ideograph-8FD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdb CJK Ideograph-8FDB | 8fda CJK Ideograph-8FDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdd CJK Ideograph-8FDD | 8fdc CJK Ideograph-8FDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fdf CJK Ideograph-8FDF | 8fde CJK Ideograph-8FDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe1 CJK Ideograph-8FE1 | 8fe0 CJK Ideograph-8FE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe3 CJK Ideograph-8FE3 | 8fe2 CJK Ideograph-8FE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe5 CJK Ideograph-8FE5 | 8fe4 CJK Ideograph-8FE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe7 CJK Ideograph-8FE7 | 8fe6 CJK Ideograph-8FE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fe9 CJK Ideograph-8FE9 | 8fe8 CJK Ideograph-8FE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8feb CJK Ideograph-8FEB | 8fea CJK Ideograph-8FEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fed CJK Ideograph-8FED | 8fec CJK Ideograph-8FEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fef CJK Ideograph-8FEF | 8fee CJK Ideograph-8FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff1 CJK Ideograph-8FF1 | 8ff0 CJK Ideograph-8FF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff3 CJK Ideograph-8FF3 | 8ff2 CJK Ideograph-8FF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff5 CJK Ideograph-8FF5 | 8ff4 CJK Ideograph-8FF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff7 CJK Ideograph-8FF7 | 8ff6 CJK Ideograph-8FF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ff9 CJK Ideograph-8FF9 | 8ff8 CJK Ideograph-8FF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ffb CJK Ideograph-8FFB | 8ffa CJK Ideograph-8FFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8ffd CJK Ideograph-8FFD | 8ffc CJK Ideograph-8FFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 8fff CJK Ideograph-8FFF | 8ffe CJK Ideograph-8FFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9001 CJK Ideograph-9001 | 9000 CJK Ideograph-9000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9003 CJK Ideograph-9003 | 9002 CJK Ideograph-9002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9005 CJK Ideograph-9005 | 9004 CJK Ideograph-9004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9007 CJK Ideograph-9007 | 9006 CJK Ideograph-9006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9009 CJK Ideograph-9009 | 9008 CJK Ideograph-9008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900b CJK Ideograph-900B | 900a CJK Ideograph-900A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900d CJK Ideograph-900D | 900c CJK Ideograph-900C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 900f CJK Ideograph-900F | 900e CJK Ideograph-900E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9011 CJK Ideograph-9011 | 9010 CJK Ideograph-9010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9013 CJK Ideograph-9013 | 9012 CJK Ideograph-9012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9015 CJK Ideograph-9015 | 9014 CJK Ideograph-9014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9017 CJK Ideograph-9017 | 9016 CJK Ideograph-9016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9019 CJK Ideograph-9019 | 9018 CJK Ideograph-9018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901b CJK Ideograph-901B | 901a CJK Ideograph-901A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901d CJK Ideograph-901D | 901c CJK Ideograph-901C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 901f CJK Ideograph-901F | 901e CJK Ideograph-901E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9021 CJK Ideograph-9021 | 9020 CJK Ideograph-9020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9023 CJK Ideograph-9023 | 9022 CJK Ideograph-9022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9025 CJK Ideograph-9025 | 9024 CJK Ideograph-9024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9027 CJK Ideograph-9027 | 9026 CJK Ideograph-9026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9029 CJK Ideograph-9029 | 9028 CJK Ideograph-9028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902b CJK Ideograph-902B | 902a CJK Ideograph-902A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902d CJK Ideograph-902D | 902c CJK Ideograph-902C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 902f CJK Ideograph-902F | 902e CJK Ideograph-902E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9031 CJK Ideograph-9031 | 9030 CJK Ideograph-9030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9033 CJK Ideograph-9033 | 9032 CJK Ideograph-9032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9035 CJK Ideograph-9035 | 9034 CJK Ideograph-9034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9037 CJK Ideograph-9037 | 9036 CJK Ideograph-9036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9039 CJK Ideograph-9039 | 9038 CJK Ideograph-9038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903b CJK Ideograph-903B | 903a CJK Ideograph-903A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903d CJK Ideograph-903D | 903c CJK Ideograph-903C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 903f CJK Ideograph-903F | 903e CJK Ideograph-903E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9041 CJK Ideograph-9041 | 9040 CJK Ideograph-9040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9043 CJK Ideograph-9043 | 9042 CJK Ideograph-9042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9045 CJK Ideograph-9045 | 9044 CJK Ideograph-9044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9047 CJK Ideograph-9047 | 9046 CJK Ideograph-9046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9049 CJK Ideograph-9049 | 9048 CJK Ideograph-9048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904b CJK Ideograph-904B | 904a CJK Ideograph-904A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904d CJK Ideograph-904D | 904c CJK Ideograph-904C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 904f CJK Ideograph-904F | 904e CJK Ideograph-904E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9051 CJK Ideograph-9051 | 9050 CJK Ideograph-9050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9053 CJK Ideograph-9053 | 9052 CJK Ideograph-9052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9055 CJK Ideograph-9055 | 9054 CJK Ideograph-9054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9057 CJK Ideograph-9057 | 9056 CJK Ideograph-9056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9059 CJK Ideograph-9059 | 9058 CJK Ideograph-9058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905b CJK Ideograph-905B | 905a CJK Ideograph-905A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905d CJK Ideograph-905D | 905c CJK Ideograph-905C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 905f CJK Ideograph-905F | 905e CJK Ideograph-905E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9061 CJK Ideograph-9061 | 9060 CJK Ideograph-9060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9063 CJK Ideograph-9063 | 9062 CJK Ideograph-9062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9065 CJK Ideograph-9065 | 9064 CJK Ideograph-9064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9067 CJK Ideograph-9067 | 9066 CJK Ideograph-9066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9069 CJK Ideograph-9069 | 9068 CJK Ideograph-9068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906b CJK Ideograph-906B | 906a CJK Ideograph-906A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906d CJK Ideograph-906D | 906c CJK Ideograph-906C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 906f CJK Ideograph-906F | 906e CJK Ideograph-906E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9071 CJK Ideograph-9071 | 9070 CJK Ideograph-9070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9073 CJK Ideograph-9073 | 9072 CJK Ideograph-9072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9075 CJK Ideograph-9075 | 9074 CJK Ideograph-9074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9077 CJK Ideograph-9077 | 9076 CJK Ideograph-9076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9079 CJK Ideograph-9079 | 9078 CJK Ideograph-9078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907b CJK Ideograph-907B | 907a CJK Ideograph-907A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907d CJK Ideograph-907D | 907c CJK Ideograph-907C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 907f CJK Ideograph-907F | 907e CJK Ideograph-907E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9081 CJK Ideograph-9081 | 9080 CJK Ideograph-9080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9083 CJK Ideograph-9083 | 9082 CJK Ideograph-9082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9085 CJK Ideograph-9085 | 9084 CJK Ideograph-9084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9087 CJK Ideograph-9087 | 9086 CJK Ideograph-9086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9089 CJK Ideograph-9089 | 9088 CJK Ideograph-9088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908b CJK Ideograph-908B | 908a CJK Ideograph-908A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908d CJK Ideograph-908D | 908c CJK Ideograph-908C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 908f CJK Ideograph-908F | 908e CJK Ideograph-908E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9091 CJK Ideograph-9091 | 9090 CJK Ideograph-9090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9093 CJK Ideograph-9093 | 9092 CJK Ideograph-9092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9095 CJK Ideograph-9095 | 9094 CJK Ideograph-9094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9097 CJK Ideograph-9097 | 9096 CJK Ideograph-9096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9099 CJK Ideograph-9099 | 9098 CJK Ideograph-9098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909b CJK Ideograph-909B | 909a CJK Ideograph-909A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909d CJK Ideograph-909D | 909c CJK Ideograph-909C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 909f CJK Ideograph-909F | 909e CJK Ideograph-909E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a1 CJK Ideograph-90A1 | 90a0 CJK Ideograph-90A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a3 CJK Ideograph-90A3 | 90a2 CJK Ideograph-90A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a5 CJK Ideograph-90A5 | 90a4 CJK Ideograph-90A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a7 CJK Ideograph-90A7 | 90a6 CJK Ideograph-90A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90a9 CJK Ideograph-90A9 | 90a8 CJK Ideograph-90A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ab CJK Ideograph-90AB | 90aa CJK Ideograph-90AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ad CJK Ideograph-90AD | 90ac CJK Ideograph-90AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90af CJK Ideograph-90AF | 90ae CJK Ideograph-90AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b1 CJK Ideograph-90B1 | 90b0 CJK Ideograph-90B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b3 CJK Ideograph-90B3 | 90b2 CJK Ideograph-90B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b5 CJK Ideograph-90B5 | 90b4 CJK Ideograph-90B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b7 CJK Ideograph-90B7 | 90b6 CJK Ideograph-90B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90b9 CJK Ideograph-90B9 | 90b8 CJK Ideograph-90B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bb CJK Ideograph-90BB | 90ba CJK Ideograph-90BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bd CJK Ideograph-90BD | 90bc CJK Ideograph-90BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90bf CJK Ideograph-90BF | 90be CJK Ideograph-90BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c1 CJK Ideograph-90C1 | 90c0 CJK Ideograph-90C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c3 CJK Ideograph-90C3 | 90c2 CJK Ideograph-90C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c5 CJK Ideograph-90C5 | 90c4 CJK Ideograph-90C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c7 CJK Ideograph-90C7 | 90c6 CJK Ideograph-90C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90c9 CJK Ideograph-90C9 | 90c8 CJK Ideograph-90C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cb CJK Ideograph-90CB | 90ca CJK Ideograph-90CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cd CJK Ideograph-90CD | 90cc CJK Ideograph-90CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90cf CJK Ideograph-90CF | 90ce CJK Ideograph-90CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d1 CJK Ideograph-90D1 | 90d0 CJK Ideograph-90D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d3 CJK Ideograph-90D3 | 90d2 CJK Ideograph-90D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d5 CJK Ideograph-90D5 | 90d4 CJK Ideograph-90D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d7 CJK Ideograph-90D7 | 90d6 CJK Ideograph-90D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90d9 CJK Ideograph-90D9 | 90d8 CJK Ideograph-90D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90db CJK Ideograph-90DB | 90da CJK Ideograph-90DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90dd CJK Ideograph-90DD | 90dc CJK Ideograph-90DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90df CJK Ideograph-90DF | 90de CJK Ideograph-90DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e1 CJK Ideograph-90E1 | 90e0 CJK Ideograph-90E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e3 CJK Ideograph-90E3 | 90e2 CJK Ideograph-90E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e5 CJK Ideograph-90E5 | 90e4 CJK Ideograph-90E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e7 CJK Ideograph-90E7 | 90e6 CJK Ideograph-90E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90e9 CJK Ideograph-90E9 | 90e8 CJK Ideograph-90E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90eb CJK Ideograph-90EB | 90ea CJK Ideograph-90EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ed CJK Ideograph-90ED | 90ec CJK Ideograph-90EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ef CJK Ideograph-90EF | 90ee CJK Ideograph-90EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f1 CJK Ideograph-90F1 | 90f0 CJK Ideograph-90F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f3 CJK Ideograph-90F3 | 90f2 CJK Ideograph-90F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f5 CJK Ideograph-90F5 | 90f4 CJK Ideograph-90F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f7 CJK Ideograph-90F7 | 90f6 CJK Ideograph-90F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90f9 CJK Ideograph-90F9 | 90f8 CJK Ideograph-90F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90fb CJK Ideograph-90FB | 90fa CJK Ideograph-90FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90fd CJK Ideograph-90FD | 90fc CJK Ideograph-90FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 90ff CJK Ideograph-90FF | 90fe CJK Ideograph-90FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9101 CJK Ideograph-9101 | 9100 CJK Ideograph-9100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9103 CJK Ideograph-9103 | 9102 CJK Ideograph-9102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9105 CJK Ideograph-9105 | 9104 CJK Ideograph-9104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9107 CJK Ideograph-9107 | 9106 CJK Ideograph-9106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9109 CJK Ideograph-9109 | 9108 CJK Ideograph-9108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910b CJK Ideograph-910B | 910a CJK Ideograph-910A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910d CJK Ideograph-910D | 910c CJK Ideograph-910C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 910f CJK Ideograph-910F | 910e CJK Ideograph-910E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9111 CJK Ideograph-9111 | 9110 CJK Ideograph-9110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9113 CJK Ideograph-9113 | 9112 CJK Ideograph-9112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9115 CJK Ideograph-9115 | 9114 CJK Ideograph-9114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9117 CJK Ideograph-9117 | 9116 CJK Ideograph-9116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9119 CJK Ideograph-9119 | 9118 CJK Ideograph-9118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911b CJK Ideograph-911B | 911a CJK Ideograph-911A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911d CJK Ideograph-911D | 911c CJK Ideograph-911C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 911f CJK Ideograph-911F | 911e CJK Ideograph-911E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9121 CJK Ideograph-9121 | 9120 CJK Ideograph-9120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9123 CJK Ideograph-9123 | 9122 CJK Ideograph-9122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9125 CJK Ideograph-9125 | 9124 CJK Ideograph-9124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9127 CJK Ideograph-9127 | 9126 CJK Ideograph-9126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9129 CJK Ideograph-9129 | 9128 CJK Ideograph-9128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912b CJK Ideograph-912B | 912a CJK Ideograph-912A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912d CJK Ideograph-912D | 912c CJK Ideograph-912C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 912f CJK Ideograph-912F | 912e CJK Ideograph-912E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9131 CJK Ideograph-9131 | 9130 CJK Ideograph-9130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9133 CJK Ideograph-9133 | 9132 CJK Ideograph-9132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9135 CJK Ideograph-9135 | 9134 CJK Ideograph-9134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9137 CJK Ideograph-9137 | 9136 CJK Ideograph-9136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9139 CJK Ideograph-9139 | 9138 CJK Ideograph-9138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913b CJK Ideograph-913B | 913a CJK Ideograph-913A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913d CJK Ideograph-913D | 913c CJK Ideograph-913C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 913f CJK Ideograph-913F | 913e CJK Ideograph-913E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9141 CJK Ideograph-9141 | 9140 CJK Ideograph-9140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9143 CJK Ideograph-9143 | 9142 CJK Ideograph-9142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9145 CJK Ideograph-9145 | 9144 CJK Ideograph-9144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9147 CJK Ideograph-9147 | 9146 CJK Ideograph-9146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9149 CJK Ideograph-9149 | 9148 CJK Ideograph-9148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914b CJK Ideograph-914B | 914a CJK Ideograph-914A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914d CJK Ideograph-914D | 914c CJK Ideograph-914C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 914f CJK Ideograph-914F | 914e CJK Ideograph-914E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9151 CJK Ideograph-9151 | 9150 CJK Ideograph-9150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9153 CJK Ideograph-9153 | 9152 CJK Ideograph-9152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9155 CJK Ideograph-9155 | 9154 CJK Ideograph-9154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9157 CJK Ideograph-9157 | 9156 CJK Ideograph-9156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9159 CJK Ideograph-9159 | 9158 CJK Ideograph-9158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915b CJK Ideograph-915B | 915a CJK Ideograph-915A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915d CJK Ideograph-915D | 915c CJK Ideograph-915C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 915f CJK Ideograph-915F | 915e CJK Ideograph-915E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9161 CJK Ideograph-9161 | 9160 CJK Ideograph-9160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9163 CJK Ideograph-9163 | 9162 CJK Ideograph-9162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9165 CJK Ideograph-9165 | 9164 CJK Ideograph-9164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9167 CJK Ideograph-9167 | 9166 CJK Ideograph-9166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9169 CJK Ideograph-9169 | 9168 CJK Ideograph-9168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916b CJK Ideograph-916B | 916a CJK Ideograph-916A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916d CJK Ideograph-916D | 916c CJK Ideograph-916C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 916f CJK Ideograph-916F | 916e CJK Ideograph-916E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9171 CJK Ideograph-9171 | 9170 CJK Ideograph-9170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9173 CJK Ideograph-9173 | 9172 CJK Ideograph-9172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9175 CJK Ideograph-9175 | 9174 CJK Ideograph-9174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9177 CJK Ideograph-9177 | 9176 CJK Ideograph-9176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9179 CJK Ideograph-9179 | 9178 CJK Ideograph-9178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917b CJK Ideograph-917B | 917a CJK Ideograph-917A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917d CJK Ideograph-917D | 917c CJK Ideograph-917C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 917f CJK Ideograph-917F | 917e CJK Ideograph-917E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9181 CJK Ideograph-9181 | 9180 CJK Ideograph-9180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9183 CJK Ideograph-9183 | 9182 CJK Ideograph-9182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9185 CJK Ideograph-9185 | 9184 CJK Ideograph-9184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9187 CJK Ideograph-9187 | 9186 CJK Ideograph-9186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9189 CJK Ideograph-9189 | 9188 CJK Ideograph-9188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918b CJK Ideograph-918B | 918a CJK Ideograph-918A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918d CJK Ideograph-918D | 918c CJK Ideograph-918C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 918f CJK Ideograph-918F | 918e CJK Ideograph-918E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9191 CJK Ideograph-9191 | 9190 CJK Ideograph-9190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9193 CJK Ideograph-9193 | 9192 CJK Ideograph-9192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9195 CJK Ideograph-9195 | 9194 CJK Ideograph-9194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9197 CJK Ideograph-9197 | 9196 CJK Ideograph-9196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9199 CJK Ideograph-9199 | 9198 CJK Ideograph-9198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919b CJK Ideograph-919B | 919a CJK Ideograph-919A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919d CJK Ideograph-919D | 919c CJK Ideograph-919C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 919f CJK Ideograph-919F | 919e CJK Ideograph-919E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a1 CJK Ideograph-91A1 | 91a0 CJK Ideograph-91A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a3 CJK Ideograph-91A3 | 91a2 CJK Ideograph-91A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a5 CJK Ideograph-91A5 | 91a4 CJK Ideograph-91A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a7 CJK Ideograph-91A7 | 91a6 CJK Ideograph-91A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91a9 CJK Ideograph-91A9 | 91a8 CJK Ideograph-91A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ab CJK Ideograph-91AB | 91aa CJK Ideograph-91AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ad CJK Ideograph-91AD | 91ac CJK Ideograph-91AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91af CJK Ideograph-91AF | 91ae CJK Ideograph-91AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b1 CJK Ideograph-91B1 | 91b0 CJK Ideograph-91B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b3 CJK Ideograph-91B3 | 91b2 CJK Ideograph-91B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b5 CJK Ideograph-91B5 | 91b4 CJK Ideograph-91B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b7 CJK Ideograph-91B7 | 91b6 CJK Ideograph-91B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91b9 CJK Ideograph-91B9 | 91b8 CJK Ideograph-91B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bb CJK Ideograph-91BB | 91ba CJK Ideograph-91BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bd CJK Ideograph-91BD | 91bc CJK Ideograph-91BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91bf CJK Ideograph-91BF | 91be CJK Ideograph-91BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c1 CJK Ideograph-91C1 | 91c0 CJK Ideograph-91C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c3 CJK Ideograph-91C3 | 91c2 CJK Ideograph-91C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c5 CJK Ideograph-91C5 | 91c4 CJK Ideograph-91C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c7 CJK Ideograph-91C7 | 91c6 CJK Ideograph-91C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91c9 CJK Ideograph-91C9 | 91c8 CJK Ideograph-91C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cb CJK Ideograph-91CB | 91ca CJK Ideograph-91CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cd CJK Ideograph-91CD | 91cc CJK Ideograph-91CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91cf CJK Ideograph-91CF | 91ce CJK Ideograph-91CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d1 CJK Ideograph-91D1 | 91d0 CJK Ideograph-91D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d3 CJK Ideograph-91D3 | 91d2 CJK Ideograph-91D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d5 CJK Ideograph-91D5 | 91d4 CJK Ideograph-91D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d7 CJK Ideograph-91D7 | 91d6 CJK Ideograph-91D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91d9 CJK Ideograph-91D9 | 91d8 CJK Ideograph-91D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91db CJK Ideograph-91DB | 91da CJK Ideograph-91DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91dd CJK Ideograph-91DD | 91dc CJK Ideograph-91DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91df CJK Ideograph-91DF | 91de CJK Ideograph-91DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e1 CJK Ideograph-91E1 | 91e0 CJK Ideograph-91E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e3 CJK Ideograph-91E3 | 91e2 CJK Ideograph-91E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e5 CJK Ideograph-91E5 | 91e4 CJK Ideograph-91E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e7 CJK Ideograph-91E7 | 91e6 CJK Ideograph-91E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91e9 CJK Ideograph-91E9 | 91e8 CJK Ideograph-91E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91eb CJK Ideograph-91EB | 91ea CJK Ideograph-91EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ed CJK Ideograph-91ED | 91ec CJK Ideograph-91EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ef CJK Ideograph-91EF | 91ee CJK Ideograph-91EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f1 CJK Ideograph-91F1 | 91f0 CJK Ideograph-91F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f3 CJK Ideograph-91F3 | 91f2 CJK Ideograph-91F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f5 CJK Ideograph-91F5 | 91f4 CJK Ideograph-91F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f7 CJK Ideograph-91F7 | 91f6 CJK Ideograph-91F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91f9 CJK Ideograph-91F9 | 91f8 CJK Ideograph-91F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91fb CJK Ideograph-91FB | 91fa CJK Ideograph-91FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91fd CJK Ideograph-91FD | 91fc CJK Ideograph-91FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 91ff CJK Ideograph-91FF | 91fe CJK Ideograph-91FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9201 CJK Ideograph-9201 | 9200 CJK Ideograph-9200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9203 CJK Ideograph-9203 | 9202 CJK Ideograph-9202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9205 CJK Ideograph-9205 | 9204 CJK Ideograph-9204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9207 CJK Ideograph-9207 | 9206 CJK Ideograph-9206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9209 CJK Ideograph-9209 | 9208 CJK Ideograph-9208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920b CJK Ideograph-920B | 920a CJK Ideograph-920A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920d CJK Ideograph-920D | 920c CJK Ideograph-920C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 920f CJK Ideograph-920F | 920e CJK Ideograph-920E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9211 CJK Ideograph-9211 | 9210 CJK Ideograph-9210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9213 CJK Ideograph-9213 | 9212 CJK Ideograph-9212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9215 CJK Ideograph-9215 | 9214 CJK Ideograph-9214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9217 CJK Ideograph-9217 | 9216 CJK Ideograph-9216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9219 CJK Ideograph-9219 | 9218 CJK Ideograph-9218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921b CJK Ideograph-921B | 921a CJK Ideograph-921A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921d CJK Ideograph-921D | 921c CJK Ideograph-921C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 921f CJK Ideograph-921F | 921e CJK Ideograph-921E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9221 CJK Ideograph-9221 | 9220 CJK Ideograph-9220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9223 CJK Ideograph-9223 | 9222 CJK Ideograph-9222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9225 CJK Ideograph-9225 | 9224 CJK Ideograph-9224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9227 CJK Ideograph-9227 | 9226 CJK Ideograph-9226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9229 CJK Ideograph-9229 | 9228 CJK Ideograph-9228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922b CJK Ideograph-922B | 922a CJK Ideograph-922A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922d CJK Ideograph-922D | 922c CJK Ideograph-922C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 922f CJK Ideograph-922F | 922e CJK Ideograph-922E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9231 CJK Ideograph-9231 | 9230 CJK Ideograph-9230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9233 CJK Ideograph-9233 | 9232 CJK Ideograph-9232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9235 CJK Ideograph-9235 | 9234 CJK Ideograph-9234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9237 CJK Ideograph-9237 | 9236 CJK Ideograph-9236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9239 CJK Ideograph-9239 | 9238 CJK Ideograph-9238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923b CJK Ideograph-923B | 923a CJK Ideograph-923A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923d CJK Ideograph-923D | 923c CJK Ideograph-923C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 923f CJK Ideograph-923F | 923e CJK Ideograph-923E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9241 CJK Ideograph-9241 | 9240 CJK Ideograph-9240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9243 CJK Ideograph-9243 | 9242 CJK Ideograph-9242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9245 CJK Ideograph-9245 | 9244 CJK Ideograph-9244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9247 CJK Ideograph-9247 | 9246 CJK Ideograph-9246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9249 CJK Ideograph-9249 | 9248 CJK Ideograph-9248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924b CJK Ideograph-924B | 924a CJK Ideograph-924A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924d CJK Ideograph-924D | 924c CJK Ideograph-924C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 924f CJK Ideograph-924F | 924e CJK Ideograph-924E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9251 CJK Ideograph-9251 | 9250 CJK Ideograph-9250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9253 CJK Ideograph-9253 | 9252 CJK Ideograph-9252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9255 CJK Ideograph-9255 | 9254 CJK Ideograph-9254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9257 CJK Ideograph-9257 | 9256 CJK Ideograph-9256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9259 CJK Ideograph-9259 | 9258 CJK Ideograph-9258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925b CJK Ideograph-925B | 925a CJK Ideograph-925A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925d CJK Ideograph-925D | 925c CJK Ideograph-925C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 925f CJK Ideograph-925F | 925e CJK Ideograph-925E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9261 CJK Ideograph-9261 | 9260 CJK Ideograph-9260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9263 CJK Ideograph-9263 | 9262 CJK Ideograph-9262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9265 CJK Ideograph-9265 | 9264 CJK Ideograph-9264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9267 CJK Ideograph-9267 | 9266 CJK Ideograph-9266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9269 CJK Ideograph-9269 | 9268 CJK Ideograph-9268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926b CJK Ideograph-926B | 926a CJK Ideograph-926A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926d CJK Ideograph-926D | 926c CJK Ideograph-926C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 926f CJK Ideograph-926F | 926e CJK Ideograph-926E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9271 CJK Ideograph-9271 | 9270 CJK Ideograph-9270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9273 CJK Ideograph-9273 | 9272 CJK Ideograph-9272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9275 CJK Ideograph-9275 | 9274 CJK Ideograph-9274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9277 CJK Ideograph-9277 | 9276 CJK Ideograph-9276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9279 CJK Ideograph-9279 | 9278 CJK Ideograph-9278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927b CJK Ideograph-927B | 927a CJK Ideograph-927A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927d CJK Ideograph-927D | 927c CJK Ideograph-927C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 927f CJK Ideograph-927F | 927e CJK Ideograph-927E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9281 CJK Ideograph-9281 | 9280 CJK Ideograph-9280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9283 CJK Ideograph-9283 | 9282 CJK Ideograph-9282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9285 CJK Ideograph-9285 | 9284 CJK Ideograph-9284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9287 CJK Ideograph-9287 | 9286 CJK Ideograph-9286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9289 CJK Ideograph-9289 | 9288 CJK Ideograph-9288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928b CJK Ideograph-928B | 928a CJK Ideograph-928A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928d CJK Ideograph-928D | 928c CJK Ideograph-928C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 928f CJK Ideograph-928F | 928e CJK Ideograph-928E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9291 CJK Ideograph-9291 | 9290 CJK Ideograph-9290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9293 CJK Ideograph-9293 | 9292 CJK Ideograph-9292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9295 CJK Ideograph-9295 | 9294 CJK Ideograph-9294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9297 CJK Ideograph-9297 | 9296 CJK Ideograph-9296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9299 CJK Ideograph-9299 | 9298 CJK Ideograph-9298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929b CJK Ideograph-929B | 929a CJK Ideograph-929A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929d CJK Ideograph-929D | 929c CJK Ideograph-929C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 929f CJK Ideograph-929F | 929e CJK Ideograph-929E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a1 CJK Ideograph-92A1 | 92a0 CJK Ideograph-92A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a3 CJK Ideograph-92A3 | 92a2 CJK Ideograph-92A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a5 CJK Ideograph-92A5 | 92a4 CJK Ideograph-92A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a7 CJK Ideograph-92A7 | 92a6 CJK Ideograph-92A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92a9 CJK Ideograph-92A9 | 92a8 CJK Ideograph-92A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ab CJK Ideograph-92AB | 92aa CJK Ideograph-92AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ad CJK Ideograph-92AD | 92ac CJK Ideograph-92AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92af CJK Ideograph-92AF | 92ae CJK Ideograph-92AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b1 CJK Ideograph-92B1 | 92b0 CJK Ideograph-92B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b3 CJK Ideograph-92B3 | 92b2 CJK Ideograph-92B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b5 CJK Ideograph-92B5 | 92b4 CJK Ideograph-92B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b7 CJK Ideograph-92B7 | 92b6 CJK Ideograph-92B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92b9 CJK Ideograph-92B9 | 92b8 CJK Ideograph-92B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bb CJK Ideograph-92BB | 92ba CJK Ideograph-92BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bd CJK Ideograph-92BD | 92bc CJK Ideograph-92BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92bf CJK Ideograph-92BF | 92be CJK Ideograph-92BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c1 CJK Ideograph-92C1 | 92c0 CJK Ideograph-92C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c3 CJK Ideograph-92C3 | 92c2 CJK Ideograph-92C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c5 CJK Ideograph-92C5 | 92c4 CJK Ideograph-92C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c7 CJK Ideograph-92C7 | 92c6 CJK Ideograph-92C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92c9 CJK Ideograph-92C9 | 92c8 CJK Ideograph-92C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cb CJK Ideograph-92CB | 92ca CJK Ideograph-92CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cd CJK Ideograph-92CD | 92cc CJK Ideograph-92CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92cf CJK Ideograph-92CF | 92ce CJK Ideograph-92CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d1 CJK Ideograph-92D1 | 92d0 CJK Ideograph-92D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d3 CJK Ideograph-92D3 | 92d2 CJK Ideograph-92D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d5 CJK Ideograph-92D5 | 92d4 CJK Ideograph-92D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d7 CJK Ideograph-92D7 | 92d6 CJK Ideograph-92D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92d9 CJK Ideograph-92D9 | 92d8 CJK Ideograph-92D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92db CJK Ideograph-92DB | 92da CJK Ideograph-92DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92dd CJK Ideograph-92DD | 92dc CJK Ideograph-92DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92df CJK Ideograph-92DF | 92de CJK Ideograph-92DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e1 CJK Ideograph-92E1 | 92e0 CJK Ideograph-92E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e3 CJK Ideograph-92E3 | 92e2 CJK Ideograph-92E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e5 CJK Ideograph-92E5 | 92e4 CJK Ideograph-92E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e7 CJK Ideograph-92E7 | 92e6 CJK Ideograph-92E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92e9 CJK Ideograph-92E9 | 92e8 CJK Ideograph-92E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92eb CJK Ideograph-92EB | 92ea CJK Ideograph-92EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ed CJK Ideograph-92ED | 92ec CJK Ideograph-92EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ef CJK Ideograph-92EF | 92ee CJK Ideograph-92EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f1 CJK Ideograph-92F1 | 92f0 CJK Ideograph-92F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f3 CJK Ideograph-92F3 | 92f2 CJK Ideograph-92F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f5 CJK Ideograph-92F5 | 92f4 CJK Ideograph-92F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f7 CJK Ideograph-92F7 | 92f6 CJK Ideograph-92F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92f9 CJK Ideograph-92F9 | 92f8 CJK Ideograph-92F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92fb CJK Ideograph-92FB | 92fa CJK Ideograph-92FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92fd CJK Ideograph-92FD | 92fc CJK Ideograph-92FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 92ff CJK Ideograph-92FF | 92fe CJK Ideograph-92FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9301 CJK Ideograph-9301 | 9300 CJK Ideograph-9300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9303 CJK Ideograph-9303 | 9302 CJK Ideograph-9302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9305 CJK Ideograph-9305 | 9304 CJK Ideograph-9304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9307 CJK Ideograph-9307 | 9306 CJK Ideograph-9306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9309 CJK Ideograph-9309 | 9308 CJK Ideograph-9308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930b CJK Ideograph-930B | 930a CJK Ideograph-930A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930d CJK Ideograph-930D | 930c CJK Ideograph-930C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 930f CJK Ideograph-930F | 930e CJK Ideograph-930E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9311 CJK Ideograph-9311 | 9310 CJK Ideograph-9310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9313 CJK Ideograph-9313 | 9312 CJK Ideograph-9312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9315 CJK Ideograph-9315 | 9314 CJK Ideograph-9314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9317 CJK Ideograph-9317 | 9316 CJK Ideograph-9316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9319 CJK Ideograph-9319 | 9318 CJK Ideograph-9318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931b CJK Ideograph-931B | 931a CJK Ideograph-931A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931d CJK Ideograph-931D | 931c CJK Ideograph-931C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 931f CJK Ideograph-931F | 931e CJK Ideograph-931E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9321 CJK Ideograph-9321 | 9320 CJK Ideograph-9320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9323 CJK Ideograph-9323 | 9322 CJK Ideograph-9322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9325 CJK Ideograph-9325 | 9324 CJK Ideograph-9324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9327 CJK Ideograph-9327 | 9326 CJK Ideograph-9326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9329 CJK Ideograph-9329 | 9328 CJK Ideograph-9328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932b CJK Ideograph-932B | 932a CJK Ideograph-932A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932d CJK Ideograph-932D | 932c CJK Ideograph-932C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 932f CJK Ideograph-932F | 932e CJK Ideograph-932E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9331 CJK Ideograph-9331 | 9330 CJK Ideograph-9330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9333 CJK Ideograph-9333 | 9332 CJK Ideograph-9332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9335 CJK Ideograph-9335 | 9334 CJK Ideograph-9334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9337 CJK Ideograph-9337 | 9336 CJK Ideograph-9336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9339 CJK Ideograph-9339 | 9338 CJK Ideograph-9338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933b CJK Ideograph-933B | 933a CJK Ideograph-933A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933d CJK Ideograph-933D | 933c CJK Ideograph-933C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 933f CJK Ideograph-933F | 933e CJK Ideograph-933E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9341 CJK Ideograph-9341 | 9340 CJK Ideograph-9340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9343 CJK Ideograph-9343 | 9342 CJK Ideograph-9342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9345 CJK Ideograph-9345 | 9344 CJK Ideograph-9344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9347 CJK Ideograph-9347 | 9346 CJK Ideograph-9346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9349 CJK Ideograph-9349 | 9348 CJK Ideograph-9348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934b CJK Ideograph-934B | 934a CJK Ideograph-934A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934d CJK Ideograph-934D | 934c CJK Ideograph-934C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 934f CJK Ideograph-934F | 934e CJK Ideograph-934E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9351 CJK Ideograph-9351 | 9350 CJK Ideograph-9350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9353 CJK Ideograph-9353 | 9352 CJK Ideograph-9352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9355 CJK Ideograph-9355 | 9354 CJK Ideograph-9354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9357 CJK Ideograph-9357 | 9356 CJK Ideograph-9356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9359 CJK Ideograph-9359 | 9358 CJK Ideograph-9358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935b CJK Ideograph-935B | 935a CJK Ideograph-935A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935d CJK Ideograph-935D | 935c CJK Ideograph-935C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 935f CJK Ideograph-935F | 935e CJK Ideograph-935E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9361 CJK Ideograph-9361 | 9360 CJK Ideograph-9360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9363 CJK Ideograph-9363 | 9362 CJK Ideograph-9362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9365 CJK Ideograph-9365 | 9364 CJK Ideograph-9364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9367 CJK Ideograph-9367 | 9366 CJK Ideograph-9366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9369 CJK Ideograph-9369 | 9368 CJK Ideograph-9368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936b CJK Ideograph-936B | 936a CJK Ideograph-936A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936d CJK Ideograph-936D | 936c CJK Ideograph-936C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 936f CJK Ideograph-936F | 936e CJK Ideograph-936E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9371 CJK Ideograph-9371 | 9370 CJK Ideograph-9370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9373 CJK Ideograph-9373 | 9372 CJK Ideograph-9372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9375 CJK Ideograph-9375 | 9374 CJK Ideograph-9374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9377 CJK Ideograph-9377 | 9376 CJK Ideograph-9376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9379 CJK Ideograph-9379 | 9378 CJK Ideograph-9378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937b CJK Ideograph-937B | 937a CJK Ideograph-937A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937d CJK Ideograph-937D | 937c CJK Ideograph-937C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 937f CJK Ideograph-937F | 937e CJK Ideograph-937E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9381 CJK Ideograph-9381 | 9380 CJK Ideograph-9380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9383 CJK Ideograph-9383 | 9382 CJK Ideograph-9382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9385 CJK Ideograph-9385 | 9384 CJK Ideograph-9384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9387 CJK Ideograph-9387 | 9386 CJK Ideograph-9386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9389 CJK Ideograph-9389 | 9388 CJK Ideograph-9388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938b CJK Ideograph-938B | 938a CJK Ideograph-938A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938d CJK Ideograph-938D | 938c CJK Ideograph-938C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 938f CJK Ideograph-938F | 938e CJK Ideograph-938E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9391 CJK Ideograph-9391 | 9390 CJK Ideograph-9390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9393 CJK Ideograph-9393 | 9392 CJK Ideograph-9392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9395 CJK Ideograph-9395 | 9394 CJK Ideograph-9394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9397 CJK Ideograph-9397 | 9396 CJK Ideograph-9396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9399 CJK Ideograph-9399 | 9398 CJK Ideograph-9398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939b CJK Ideograph-939B | 939a CJK Ideograph-939A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939d CJK Ideograph-939D | 939c CJK Ideograph-939C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 939f CJK Ideograph-939F | 939e CJK Ideograph-939E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a1 CJK Ideograph-93A1 | 93a0 CJK Ideograph-93A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a3 CJK Ideograph-93A3 | 93a2 CJK Ideograph-93A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a5 CJK Ideograph-93A5 | 93a4 CJK Ideograph-93A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a7 CJK Ideograph-93A7 | 93a6 CJK Ideograph-93A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93a9 CJK Ideograph-93A9 | 93a8 CJK Ideograph-93A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ab CJK Ideograph-93AB | 93aa CJK Ideograph-93AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ad CJK Ideograph-93AD | 93ac CJK Ideograph-93AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93af CJK Ideograph-93AF | 93ae CJK Ideograph-93AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b1 CJK Ideograph-93B1 | 93b0 CJK Ideograph-93B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b3 CJK Ideograph-93B3 | 93b2 CJK Ideograph-93B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b5 CJK Ideograph-93B5 | 93b4 CJK Ideograph-93B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b7 CJK Ideograph-93B7 | 93b6 CJK Ideograph-93B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93b9 CJK Ideograph-93B9 | 93b8 CJK Ideograph-93B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bb CJK Ideograph-93BB | 93ba CJK Ideograph-93BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bd CJK Ideograph-93BD | 93bc CJK Ideograph-93BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93bf CJK Ideograph-93BF | 93be CJK Ideograph-93BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c1 CJK Ideograph-93C1 | 93c0 CJK Ideograph-93C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c3 CJK Ideograph-93C3 | 93c2 CJK Ideograph-93C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c5 CJK Ideograph-93C5 | 93c4 CJK Ideograph-93C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c7 CJK Ideograph-93C7 | 93c6 CJK Ideograph-93C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93c9 CJK Ideograph-93C9 | 93c8 CJK Ideograph-93C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cb CJK Ideograph-93CB | 93ca CJK Ideograph-93CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cd CJK Ideograph-93CD | 93cc CJK Ideograph-93CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93cf CJK Ideograph-93CF | 93ce CJK Ideograph-93CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d1 CJK Ideograph-93D1 | 93d0 CJK Ideograph-93D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d3 CJK Ideograph-93D3 | 93d2 CJK Ideograph-93D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d5 CJK Ideograph-93D5 | 93d4 CJK Ideograph-93D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d7 CJK Ideograph-93D7 | 93d6 CJK Ideograph-93D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93d9 CJK Ideograph-93D9 | 93d8 CJK Ideograph-93D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93db CJK Ideograph-93DB | 93da CJK Ideograph-93DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93dd CJK Ideograph-93DD | 93dc CJK Ideograph-93DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93df CJK Ideograph-93DF | 93de CJK Ideograph-93DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e1 CJK Ideograph-93E1 | 93e0 CJK Ideograph-93E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e3 CJK Ideograph-93E3 | 93e2 CJK Ideograph-93E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e5 CJK Ideograph-93E5 | 93e4 CJK Ideograph-93E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e7 CJK Ideograph-93E7 | 93e6 CJK Ideograph-93E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93e9 CJK Ideograph-93E9 | 93e8 CJK Ideograph-93E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93eb CJK Ideograph-93EB | 93ea CJK Ideograph-93EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ed CJK Ideograph-93ED | 93ec CJK Ideograph-93EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ef CJK Ideograph-93EF | 93ee CJK Ideograph-93EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f1 CJK Ideograph-93F1 | 93f0 CJK Ideograph-93F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f3 CJK Ideograph-93F3 | 93f2 CJK Ideograph-93F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f5 CJK Ideograph-93F5 | 93f4 CJK Ideograph-93F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f7 CJK Ideograph-93F7 | 93f6 CJK Ideograph-93F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93f9 CJK Ideograph-93F9 | 93f8 CJK Ideograph-93F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93fb CJK Ideograph-93FB | 93fa CJK Ideograph-93FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93fd CJK Ideograph-93FD | 93fc CJK Ideograph-93FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 93ff CJK Ideograph-93FF | 93fe CJK Ideograph-93FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9401 CJK Ideograph-9401 | 9400 CJK Ideograph-9400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9403 CJK Ideograph-9403 | 9402 CJK Ideograph-9402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9405 CJK Ideograph-9405 | 9404 CJK Ideograph-9404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9407 CJK Ideograph-9407 | 9406 CJK Ideograph-9406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9409 CJK Ideograph-9409 | 9408 CJK Ideograph-9408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940b CJK Ideograph-940B | 940a CJK Ideograph-940A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940d CJK Ideograph-940D | 940c CJK Ideograph-940C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 940f CJK Ideograph-940F | 940e CJK Ideograph-940E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9411 CJK Ideograph-9411 | 9410 CJK Ideograph-9410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9413 CJK Ideograph-9413 | 9412 CJK Ideograph-9412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9415 CJK Ideograph-9415 | 9414 CJK Ideograph-9414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9417 CJK Ideograph-9417 | 9416 CJK Ideograph-9416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9419 CJK Ideograph-9419 | 9418 CJK Ideograph-9418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941b CJK Ideograph-941B | 941a CJK Ideograph-941A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941d CJK Ideograph-941D | 941c CJK Ideograph-941C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 941f CJK Ideograph-941F | 941e CJK Ideograph-941E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9421 CJK Ideograph-9421 | 9420 CJK Ideograph-9420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9423 CJK Ideograph-9423 | 9422 CJK Ideograph-9422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9425 CJK Ideograph-9425 | 9424 CJK Ideograph-9424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9427 CJK Ideograph-9427 | 9426 CJK Ideograph-9426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9429 CJK Ideograph-9429 | 9428 CJK Ideograph-9428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942b CJK Ideograph-942B | 942a CJK Ideograph-942A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942d CJK Ideograph-942D | 942c CJK Ideograph-942C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 942f CJK Ideograph-942F | 942e CJK Ideograph-942E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9431 CJK Ideograph-9431 | 9430 CJK Ideograph-9430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9433 CJK Ideograph-9433 | 9432 CJK Ideograph-9432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9435 CJK Ideograph-9435 | 9434 CJK Ideograph-9434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9437 CJK Ideograph-9437 | 9436 CJK Ideograph-9436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9439 CJK Ideograph-9439 | 9438 CJK Ideograph-9438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943b CJK Ideograph-943B | 943a CJK Ideograph-943A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943d CJK Ideograph-943D | 943c CJK Ideograph-943C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 943f CJK Ideograph-943F | 943e CJK Ideograph-943E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9441 CJK Ideograph-9441 | 9440 CJK Ideograph-9440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9443 CJK Ideograph-9443 | 9442 CJK Ideograph-9442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9445 CJK Ideograph-9445 | 9444 CJK Ideograph-9444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9447 CJK Ideograph-9447 | 9446 CJK Ideograph-9446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9449 CJK Ideograph-9449 | 9448 CJK Ideograph-9448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944b CJK Ideograph-944B | 944a CJK Ideograph-944A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944d CJK Ideograph-944D | 944c CJK Ideograph-944C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 944f CJK Ideograph-944F | 944e CJK Ideograph-944E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9451 CJK Ideograph-9451 | 9450 CJK Ideograph-9450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9453 CJK Ideograph-9453 | 9452 CJK Ideograph-9452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9455 CJK Ideograph-9455 | 9454 CJK Ideograph-9454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9457 CJK Ideograph-9457 | 9456 CJK Ideograph-9456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9459 CJK Ideograph-9459 | 9458 CJK Ideograph-9458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945b CJK Ideograph-945B | 945a CJK Ideograph-945A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945d CJK Ideograph-945D | 945c CJK Ideograph-945C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 945f CJK Ideograph-945F | 945e CJK Ideograph-945E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9461 CJK Ideograph-9461 | 9460 CJK Ideograph-9460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9463 CJK Ideograph-9463 | 9462 CJK Ideograph-9462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9465 CJK Ideograph-9465 | 9464 CJK Ideograph-9464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9467 CJK Ideograph-9467 | 9466 CJK Ideograph-9466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9469 CJK Ideograph-9469 | 9468 CJK Ideograph-9468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946b CJK Ideograph-946B | 946a CJK Ideograph-946A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946d CJK Ideograph-946D | 946c CJK Ideograph-946C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 946f CJK Ideograph-946F | 946e CJK Ideograph-946E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9471 CJK Ideograph-9471 | 9470 CJK Ideograph-9470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9473 CJK Ideograph-9473 | 9472 CJK Ideograph-9472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9475 CJK Ideograph-9475 | 9474 CJK Ideograph-9474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9477 CJK Ideograph-9477 | 9476 CJK Ideograph-9476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9479 CJK Ideograph-9479 | 9478 CJK Ideograph-9478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947b CJK Ideograph-947B | 947a CJK Ideograph-947A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947d CJK Ideograph-947D | 947c CJK Ideograph-947C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 947f CJK Ideograph-947F | 947e CJK Ideograph-947E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9481 CJK Ideograph-9481 | 9480 CJK Ideograph-9480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9483 CJK Ideograph-9483 | 9482 CJK Ideograph-9482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9485 CJK Ideograph-9485 | 9484 CJK Ideograph-9484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9487 CJK Ideograph-9487 | 9486 CJK Ideograph-9486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9489 CJK Ideograph-9489 | 9488 CJK Ideograph-9488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948b CJK Ideograph-948B | 948a CJK Ideograph-948A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948d CJK Ideograph-948D | 948c CJK Ideograph-948C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 948f CJK Ideograph-948F | 948e CJK Ideograph-948E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9491 CJK Ideograph-9491 | 9490 CJK Ideograph-9490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9493 CJK Ideograph-9493 | 9492 CJK Ideograph-9492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9495 CJK Ideograph-9495 | 9494 CJK Ideograph-9494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9497 CJK Ideograph-9497 | 9496 CJK Ideograph-9496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9499 CJK Ideograph-9499 | 9498 CJK Ideograph-9498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949b CJK Ideograph-949B | 949a CJK Ideograph-949A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949d CJK Ideograph-949D | 949c CJK Ideograph-949C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 949f CJK Ideograph-949F | 949e CJK Ideograph-949E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a1 CJK Ideograph-94A1 | 94a0 CJK Ideograph-94A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a3 CJK Ideograph-94A3 | 94a2 CJK Ideograph-94A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a5 CJK Ideograph-94A5 | 94a4 CJK Ideograph-94A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a7 CJK Ideograph-94A7 | 94a6 CJK Ideograph-94A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94a9 CJK Ideograph-94A9 | 94a8 CJK Ideograph-94A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ab CJK Ideograph-94AB | 94aa CJK Ideograph-94AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ad CJK Ideograph-94AD | 94ac CJK Ideograph-94AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94af CJK Ideograph-94AF | 94ae CJK Ideograph-94AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b1 CJK Ideograph-94B1 | 94b0 CJK Ideograph-94B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b3 CJK Ideograph-94B3 | 94b2 CJK Ideograph-94B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b5 CJK Ideograph-94B5 | 94b4 CJK Ideograph-94B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b7 CJK Ideograph-94B7 | 94b6 CJK Ideograph-94B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94b9 CJK Ideograph-94B9 | 94b8 CJK Ideograph-94B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bb CJK Ideograph-94BB | 94ba CJK Ideograph-94BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bd CJK Ideograph-94BD | 94bc CJK Ideograph-94BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94bf CJK Ideograph-94BF | 94be CJK Ideograph-94BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c1 CJK Ideograph-94C1 | 94c0 CJK Ideograph-94C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c3 CJK Ideograph-94C3 | 94c2 CJK Ideograph-94C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c5 CJK Ideograph-94C5 | 94c4 CJK Ideograph-94C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c7 CJK Ideograph-94C7 | 94c6 CJK Ideograph-94C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94c9 CJK Ideograph-94C9 | 94c8 CJK Ideograph-94C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cb CJK Ideograph-94CB | 94ca CJK Ideograph-94CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cd CJK Ideograph-94CD | 94cc CJK Ideograph-94CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94cf CJK Ideograph-94CF | 94ce CJK Ideograph-94CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d1 CJK Ideograph-94D1 | 94d0 CJK Ideograph-94D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d3 CJK Ideograph-94D3 | 94d2 CJK Ideograph-94D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d5 CJK Ideograph-94D5 | 94d4 CJK Ideograph-94D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d7 CJK Ideograph-94D7 | 94d6 CJK Ideograph-94D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94d9 CJK Ideograph-94D9 | 94d8 CJK Ideograph-94D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94db CJK Ideograph-94DB | 94da CJK Ideograph-94DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94dd CJK Ideograph-94DD | 94dc CJK Ideograph-94DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94df CJK Ideograph-94DF | 94de CJK Ideograph-94DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e1 CJK Ideograph-94E1 | 94e0 CJK Ideograph-94E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e3 CJK Ideograph-94E3 | 94e2 CJK Ideograph-94E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e5 CJK Ideograph-94E5 | 94e4 CJK Ideograph-94E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e7 CJK Ideograph-94E7 | 94e6 CJK Ideograph-94E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94e9 CJK Ideograph-94E9 | 94e8 CJK Ideograph-94E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94eb CJK Ideograph-94EB | 94ea CJK Ideograph-94EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ed CJK Ideograph-94ED | 94ec CJK Ideograph-94EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ef CJK Ideograph-94EF | 94ee CJK Ideograph-94EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f1 CJK Ideograph-94F1 | 94f0 CJK Ideograph-94F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f3 CJK Ideograph-94F3 | 94f2 CJK Ideograph-94F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f5 CJK Ideograph-94F5 | 94f4 CJK Ideograph-94F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f7 CJK Ideograph-94F7 | 94f6 CJK Ideograph-94F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94f9 CJK Ideograph-94F9 | 94f8 CJK Ideograph-94F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94fb CJK Ideograph-94FB | 94fa CJK Ideograph-94FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94fd CJK Ideograph-94FD | 94fc CJK Ideograph-94FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 94ff CJK Ideograph-94FF | 94fe CJK Ideograph-94FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9501 CJK Ideograph-9501 | 9500 CJK Ideograph-9500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9503 CJK Ideograph-9503 | 9502 CJK Ideograph-9502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9505 CJK Ideograph-9505 | 9504 CJK Ideograph-9504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9507 CJK Ideograph-9507 | 9506 CJK Ideograph-9506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9509 CJK Ideograph-9509 | 9508 CJK Ideograph-9508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950b CJK Ideograph-950B | 950a CJK Ideograph-950A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950d CJK Ideograph-950D | 950c CJK Ideograph-950C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 950f CJK Ideograph-950F | 950e CJK Ideograph-950E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9511 CJK Ideograph-9511 | 9510 CJK Ideograph-9510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9513 CJK Ideograph-9513 | 9512 CJK Ideograph-9512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9515 CJK Ideograph-9515 | 9514 CJK Ideograph-9514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9517 CJK Ideograph-9517 | 9516 CJK Ideograph-9516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9519 CJK Ideograph-9519 | 9518 CJK Ideograph-9518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951b CJK Ideograph-951B | 951a CJK Ideograph-951A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951d CJK Ideograph-951D | 951c CJK Ideograph-951C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 951f CJK Ideograph-951F | 951e CJK Ideograph-951E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9521 CJK Ideograph-9521 | 9520 CJK Ideograph-9520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9523 CJK Ideograph-9523 | 9522 CJK Ideograph-9522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9525 CJK Ideograph-9525 | 9524 CJK Ideograph-9524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9527 CJK Ideograph-9527 | 9526 CJK Ideograph-9526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9529 CJK Ideograph-9529 | 9528 CJK Ideograph-9528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952b CJK Ideograph-952B | 952a CJK Ideograph-952A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952d CJK Ideograph-952D | 952c CJK Ideograph-952C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 952f CJK Ideograph-952F | 952e CJK Ideograph-952E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9531 CJK Ideograph-9531 | 9530 CJK Ideograph-9530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9533 CJK Ideograph-9533 | 9532 CJK Ideograph-9532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9535 CJK Ideograph-9535 | 9534 CJK Ideograph-9534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9537 CJK Ideograph-9537 | 9536 CJK Ideograph-9536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9539 CJK Ideograph-9539 | 9538 CJK Ideograph-9538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953b CJK Ideograph-953B | 953a CJK Ideograph-953A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953d CJK Ideograph-953D | 953c CJK Ideograph-953C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 953f CJK Ideograph-953F | 953e CJK Ideograph-953E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9541 CJK Ideograph-9541 | 9540 CJK Ideograph-9540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9543 CJK Ideograph-9543 | 9542 CJK Ideograph-9542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9545 CJK Ideograph-9545 | 9544 CJK Ideograph-9544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9547 CJK Ideograph-9547 | 9546 CJK Ideograph-9546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9549 CJK Ideograph-9549 | 9548 CJK Ideograph-9548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954b CJK Ideograph-954B | 954a CJK Ideograph-954A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954d CJK Ideograph-954D | 954c CJK Ideograph-954C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 954f CJK Ideograph-954F | 954e CJK Ideograph-954E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9551 CJK Ideograph-9551 | 9550 CJK Ideograph-9550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9553 CJK Ideograph-9553 | 9552 CJK Ideograph-9552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9555 CJK Ideograph-9555 | 9554 CJK Ideograph-9554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9557 CJK Ideograph-9557 | 9556 CJK Ideograph-9556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9559 CJK Ideograph-9559 | 9558 CJK Ideograph-9558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955b CJK Ideograph-955B | 955a CJK Ideograph-955A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955d CJK Ideograph-955D | 955c CJK Ideograph-955C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 955f CJK Ideograph-955F | 955e CJK Ideograph-955E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9561 CJK Ideograph-9561 | 9560 CJK Ideograph-9560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9563 CJK Ideograph-9563 | 9562 CJK Ideograph-9562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9565 CJK Ideograph-9565 | 9564 CJK Ideograph-9564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9567 CJK Ideograph-9567 | 9566 CJK Ideograph-9566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9569 CJK Ideograph-9569 | 9568 CJK Ideograph-9568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956b CJK Ideograph-956B | 956a CJK Ideograph-956A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956d CJK Ideograph-956D | 956c CJK Ideograph-956C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 956f CJK Ideograph-956F | 956e CJK Ideograph-956E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9571 CJK Ideograph-9571 | 9570 CJK Ideograph-9570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9573 CJK Ideograph-9573 | 9572 CJK Ideograph-9572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9575 CJK Ideograph-9575 | 9574 CJK Ideograph-9574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9577 CJK Ideograph-9577 | 9576 CJK Ideograph-9576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9579 CJK Ideograph-9579 | 9578 CJK Ideograph-9578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957b CJK Ideograph-957B | 957a CJK Ideograph-957A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957d CJK Ideograph-957D | 957c CJK Ideograph-957C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 957f CJK Ideograph-957F | 957e CJK Ideograph-957E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9581 CJK Ideograph-9581 | 9580 CJK Ideograph-9580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9583 CJK Ideograph-9583 | 9582 CJK Ideograph-9582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9585 CJK Ideograph-9585 | 9584 CJK Ideograph-9584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9587 CJK Ideograph-9587 | 9586 CJK Ideograph-9586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9589 CJK Ideograph-9589 | 9588 CJK Ideograph-9588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958b CJK Ideograph-958B | 958a CJK Ideograph-958A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958d CJK Ideograph-958D | 958c CJK Ideograph-958C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 958f CJK Ideograph-958F | 958e CJK Ideograph-958E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9591 CJK Ideograph-9591 | 9590 CJK Ideograph-9590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9593 CJK Ideograph-9593 | 9592 CJK Ideograph-9592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9595 CJK Ideograph-9595 | 9594 CJK Ideograph-9594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9597 CJK Ideograph-9597 | 9596 CJK Ideograph-9596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9599 CJK Ideograph-9599 | 9598 CJK Ideograph-9598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959b CJK Ideograph-959B | 959a CJK Ideograph-959A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959d CJK Ideograph-959D | 959c CJK Ideograph-959C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 959f CJK Ideograph-959F | 959e CJK Ideograph-959E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a1 CJK Ideograph-95A1 | 95a0 CJK Ideograph-95A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a3 CJK Ideograph-95A3 | 95a2 CJK Ideograph-95A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a5 CJK Ideograph-95A5 | 95a4 CJK Ideograph-95A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a7 CJK Ideograph-95A7 | 95a6 CJK Ideograph-95A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95a9 CJK Ideograph-95A9 | 95a8 CJK Ideograph-95A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ab CJK Ideograph-95AB | 95aa CJK Ideograph-95AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ad CJK Ideograph-95AD | 95ac CJK Ideograph-95AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95af CJK Ideograph-95AF | 95ae CJK Ideograph-95AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b1 CJK Ideograph-95B1 | 95b0 CJK Ideograph-95B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b3 CJK Ideograph-95B3 | 95b2 CJK Ideograph-95B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b5 CJK Ideograph-95B5 | 95b4 CJK Ideograph-95B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b7 CJK Ideograph-95B7 | 95b6 CJK Ideograph-95B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95b9 CJK Ideograph-95B9 | 95b8 CJK Ideograph-95B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bb CJK Ideograph-95BB | 95ba CJK Ideograph-95BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bd CJK Ideograph-95BD | 95bc CJK Ideograph-95BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95bf CJK Ideograph-95BF | 95be CJK Ideograph-95BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c1 CJK Ideograph-95C1 | 95c0 CJK Ideograph-95C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c3 CJK Ideograph-95C3 | 95c2 CJK Ideograph-95C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c5 CJK Ideograph-95C5 | 95c4 CJK Ideograph-95C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c7 CJK Ideograph-95C7 | 95c6 CJK Ideograph-95C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95c9 CJK Ideograph-95C9 | 95c8 CJK Ideograph-95C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cb CJK Ideograph-95CB | 95ca CJK Ideograph-95CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cd CJK Ideograph-95CD | 95cc CJK Ideograph-95CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95cf CJK Ideograph-95CF | 95ce CJK Ideograph-95CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d1 CJK Ideograph-95D1 | 95d0 CJK Ideograph-95D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d3 CJK Ideograph-95D3 | 95d2 CJK Ideograph-95D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d5 CJK Ideograph-95D5 | 95d4 CJK Ideograph-95D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d7 CJK Ideograph-95D7 | 95d6 CJK Ideograph-95D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95d9 CJK Ideograph-95D9 | 95d8 CJK Ideograph-95D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95db CJK Ideograph-95DB | 95da CJK Ideograph-95DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95dd CJK Ideograph-95DD | 95dc CJK Ideograph-95DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95df CJK Ideograph-95DF | 95de CJK Ideograph-95DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e1 CJK Ideograph-95E1 | 95e0 CJK Ideograph-95E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e3 CJK Ideograph-95E3 | 95e2 CJK Ideograph-95E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e5 CJK Ideograph-95E5 | 95e4 CJK Ideograph-95E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e7 CJK Ideograph-95E7 | 95e6 CJK Ideograph-95E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95e9 CJK Ideograph-95E9 | 95e8 CJK Ideograph-95E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95eb CJK Ideograph-95EB | 95ea CJK Ideograph-95EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ed CJK Ideograph-95ED | 95ec CJK Ideograph-95EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ef CJK Ideograph-95EF | 95ee CJK Ideograph-95EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f1 CJK Ideograph-95F1 | 95f0 CJK Ideograph-95F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f3 CJK Ideograph-95F3 | 95f2 CJK Ideograph-95F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f5 CJK Ideograph-95F5 | 95f4 CJK Ideograph-95F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f7 CJK Ideograph-95F7 | 95f6 CJK Ideograph-95F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95f9 CJK Ideograph-95F9 | 95f8 CJK Ideograph-95F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95fb CJK Ideograph-95FB | 95fa CJK Ideograph-95FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95fd CJK Ideograph-95FD | 95fc CJK Ideograph-95FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 95ff CJK Ideograph-95FF | 95fe CJK Ideograph-95FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9601 CJK Ideograph-9601 | 9600 CJK Ideograph-9600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9603 CJK Ideograph-9603 | 9602 CJK Ideograph-9602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9605 CJK Ideograph-9605 | 9604 CJK Ideograph-9604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9607 CJK Ideograph-9607 | 9606 CJK Ideograph-9606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9609 CJK Ideograph-9609 | 9608 CJK Ideograph-9608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960b CJK Ideograph-960B | 960a CJK Ideograph-960A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960d CJK Ideograph-960D | 960c CJK Ideograph-960C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 960f CJK Ideograph-960F | 960e CJK Ideograph-960E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9611 CJK Ideograph-9611 | 9610 CJK Ideograph-9610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9613 CJK Ideograph-9613 | 9612 CJK Ideograph-9612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9615 CJK Ideograph-9615 | 9614 CJK Ideograph-9614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9617 CJK Ideograph-9617 | 9616 CJK Ideograph-9616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9619 CJK Ideograph-9619 | 9618 CJK Ideograph-9618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961b CJK Ideograph-961B | 961a CJK Ideograph-961A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961d CJK Ideograph-961D | 961c CJK Ideograph-961C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 961f CJK Ideograph-961F | 961e CJK Ideograph-961E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9621 CJK Ideograph-9621 | 9620 CJK Ideograph-9620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9623 CJK Ideograph-9623 | 9622 CJK Ideograph-9622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9625 CJK Ideograph-9625 | 9624 CJK Ideograph-9624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9627 CJK Ideograph-9627 | 9626 CJK Ideograph-9626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9629 CJK Ideograph-9629 | 9628 CJK Ideograph-9628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962b CJK Ideograph-962B | 962a CJK Ideograph-962A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962d CJK Ideograph-962D | 962c CJK Ideograph-962C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 962f CJK Ideograph-962F | 962e CJK Ideograph-962E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9631 CJK Ideograph-9631 | 9630 CJK Ideograph-9630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9633 CJK Ideograph-9633 | 9632 CJK Ideograph-9632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9635 CJK Ideograph-9635 | 9634 CJK Ideograph-9634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9637 CJK Ideograph-9637 | 9636 CJK Ideograph-9636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9639 CJK Ideograph-9639 | 9638 CJK Ideograph-9638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963b CJK Ideograph-963B | 963a CJK Ideograph-963A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963d CJK Ideograph-963D | 963c CJK Ideograph-963C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 963f CJK Ideograph-963F | 963e CJK Ideograph-963E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9641 CJK Ideograph-9641 | 9640 CJK Ideograph-9640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9643 CJK Ideograph-9643 | 9642 CJK Ideograph-9642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9645 CJK Ideograph-9645 | 9644 CJK Ideograph-9644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9647 CJK Ideograph-9647 | 9646 CJK Ideograph-9646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9649 CJK Ideograph-9649 | 9648 CJK Ideograph-9648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964b CJK Ideograph-964B | 964a CJK Ideograph-964A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964d CJK Ideograph-964D | 964c CJK Ideograph-964C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 964f CJK Ideograph-964F | 964e CJK Ideograph-964E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9651 CJK Ideograph-9651 | 9650 CJK Ideograph-9650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9653 CJK Ideograph-9653 | 9652 CJK Ideograph-9652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9655 CJK Ideograph-9655 | 9654 CJK Ideograph-9654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9657 CJK Ideograph-9657 | 9656 CJK Ideograph-9656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9659 CJK Ideograph-9659 | 9658 CJK Ideograph-9658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965b CJK Ideograph-965B | 965a CJK Ideograph-965A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965d CJK Ideograph-965D | 965c CJK Ideograph-965C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 965f CJK Ideograph-965F | 965e CJK Ideograph-965E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9661 CJK Ideograph-9661 | 9660 CJK Ideograph-9660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9663 CJK Ideograph-9663 | 9662 CJK Ideograph-9662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9665 CJK Ideograph-9665 | 9664 CJK Ideograph-9664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9667 CJK Ideograph-9667 | 9666 CJK Ideograph-9666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9669 CJK Ideograph-9669 | 9668 CJK Ideograph-9668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966b CJK Ideograph-966B | 966a CJK Ideograph-966A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966d CJK Ideograph-966D | 966c CJK Ideograph-966C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 966f CJK Ideograph-966F | 966e CJK Ideograph-966E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9671 CJK Ideograph-9671 | 9670 CJK Ideograph-9670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9673 CJK Ideograph-9673 | 9672 CJK Ideograph-9672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9675 CJK Ideograph-9675 | 9674 CJK Ideograph-9674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9677 CJK Ideograph-9677 | 9676 CJK Ideograph-9676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9679 CJK Ideograph-9679 | 9678 CJK Ideograph-9678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967b CJK Ideograph-967B | 967a CJK Ideograph-967A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967d CJK Ideograph-967D | 967c CJK Ideograph-967C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 967f CJK Ideograph-967F | 967e CJK Ideograph-967E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9681 CJK Ideograph-9681 | 9680 CJK Ideograph-9680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9683 CJK Ideograph-9683 | 9682 CJK Ideograph-9682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9685 CJK Ideograph-9685 | 9684 CJK Ideograph-9684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9687 CJK Ideograph-9687 | 9686 CJK Ideograph-9686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9689 CJK Ideograph-9689 | 9688 CJK Ideograph-9688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968b CJK Ideograph-968B | 968a CJK Ideograph-968A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968d CJK Ideograph-968D | 968c CJK Ideograph-968C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 968f CJK Ideograph-968F | 968e CJK Ideograph-968E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9691 CJK Ideograph-9691 | 9690 CJK Ideograph-9690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9693 CJK Ideograph-9693 | 9692 CJK Ideograph-9692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9695 CJK Ideograph-9695 | 9694 CJK Ideograph-9694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9697 CJK Ideograph-9697 | 9696 CJK Ideograph-9696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9699 CJK Ideograph-9699 | 9698 CJK Ideograph-9698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969b CJK Ideograph-969B | 969a CJK Ideograph-969A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969d CJK Ideograph-969D | 969c CJK Ideograph-969C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 969f CJK Ideograph-969F | 969e CJK Ideograph-969E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a1 CJK Ideograph-96A1 | 96a0 CJK Ideograph-96A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a3 CJK Ideograph-96A3 | 96a2 CJK Ideograph-96A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a5 CJK Ideograph-96A5 | 96a4 CJK Ideograph-96A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a7 CJK Ideograph-96A7 | 96a6 CJK Ideograph-96A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96a9 CJK Ideograph-96A9 | 96a8 CJK Ideograph-96A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ab CJK Ideograph-96AB | 96aa CJK Ideograph-96AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ad CJK Ideograph-96AD | 96ac CJK Ideograph-96AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96af CJK Ideograph-96AF | 96ae CJK Ideograph-96AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b1 CJK Ideograph-96B1 | 96b0 CJK Ideograph-96B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b3 CJK Ideograph-96B3 | 96b2 CJK Ideograph-96B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b5 CJK Ideograph-96B5 | 96b4 CJK Ideograph-96B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b7 CJK Ideograph-96B7 | 96b6 CJK Ideograph-96B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96b9 CJK Ideograph-96B9 | 96b8 CJK Ideograph-96B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bb CJK Ideograph-96BB | 96ba CJK Ideograph-96BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bd CJK Ideograph-96BD | 96bc CJK Ideograph-96BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96bf CJK Ideograph-96BF | 96be CJK Ideograph-96BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c1 CJK Ideograph-96C1 | 96c0 CJK Ideograph-96C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c3 CJK Ideograph-96C3 | 96c2 CJK Ideograph-96C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c5 CJK Ideograph-96C5 | 96c4 CJK Ideograph-96C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c7 CJK Ideograph-96C7 | 96c6 CJK Ideograph-96C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96c9 CJK Ideograph-96C9 | 96c8 CJK Ideograph-96C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cb CJK Ideograph-96CB | 96ca CJK Ideograph-96CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cd CJK Ideograph-96CD | 96cc CJK Ideograph-96CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96cf CJK Ideograph-96CF | 96ce CJK Ideograph-96CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d1 CJK Ideograph-96D1 | 96d0 CJK Ideograph-96D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d3 CJK Ideograph-96D3 | 96d2 CJK Ideograph-96D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d5 CJK Ideograph-96D5 | 96d4 CJK Ideograph-96D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d7 CJK Ideograph-96D7 | 96d6 CJK Ideograph-96D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96d9 CJK Ideograph-96D9 | 96d8 CJK Ideograph-96D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96db CJK Ideograph-96DB | 96da CJK Ideograph-96DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96dd CJK Ideograph-96DD | 96dc CJK Ideograph-96DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96df CJK Ideograph-96DF | 96de CJK Ideograph-96DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e1 CJK Ideograph-96E1 | 96e0 CJK Ideograph-96E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e3 CJK Ideograph-96E3 | 96e2 CJK Ideograph-96E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e5 CJK Ideograph-96E5 | 96e4 CJK Ideograph-96E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e7 CJK Ideograph-96E7 | 96e6 CJK Ideograph-96E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96e9 CJK Ideograph-96E9 | 96e8 CJK Ideograph-96E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96eb CJK Ideograph-96EB | 96ea CJK Ideograph-96EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ed CJK Ideograph-96ED | 96ec CJK Ideograph-96EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ef CJK Ideograph-96EF | 96ee CJK Ideograph-96EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f1 CJK Ideograph-96F1 | 96f0 CJK Ideograph-96F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f3 CJK Ideograph-96F3 | 96f2 CJK Ideograph-96F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f5 CJK Ideograph-96F5 | 96f4 CJK Ideograph-96F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f7 CJK Ideograph-96F7 | 96f6 CJK Ideograph-96F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96f9 CJK Ideograph-96F9 | 96f8 CJK Ideograph-96F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96fb CJK Ideograph-96FB | 96fa CJK Ideograph-96FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96fd CJK Ideograph-96FD | 96fc CJK Ideograph-96FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 96ff CJK Ideograph-96FF | 96fe CJK Ideograph-96FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9701 CJK Ideograph-9701 | 9700 CJK Ideograph-9700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9703 CJK Ideograph-9703 | 9702 CJK Ideograph-9702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9705 CJK Ideograph-9705 | 9704 CJK Ideograph-9704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9707 CJK Ideograph-9707 | 9706 CJK Ideograph-9706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9709 CJK Ideograph-9709 | 9708 CJK Ideograph-9708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970b CJK Ideograph-970B | 970a CJK Ideograph-970A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970d CJK Ideograph-970D | 970c CJK Ideograph-970C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 970f CJK Ideograph-970F | 970e CJK Ideograph-970E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9711 CJK Ideograph-9711 | 9710 CJK Ideograph-9710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9713 CJK Ideograph-9713 | 9712 CJK Ideograph-9712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9715 CJK Ideograph-9715 | 9714 CJK Ideograph-9714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9717 CJK Ideograph-9717 | 9716 CJK Ideograph-9716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9719 CJK Ideograph-9719 | 9718 CJK Ideograph-9718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971b CJK Ideograph-971B | 971a CJK Ideograph-971A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971d CJK Ideograph-971D | 971c CJK Ideograph-971C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 971f CJK Ideograph-971F | 971e CJK Ideograph-971E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9721 CJK Ideograph-9721 | 9720 CJK Ideograph-9720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9723 CJK Ideograph-9723 | 9722 CJK Ideograph-9722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9725 CJK Ideograph-9725 | 9724 CJK Ideograph-9724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9727 CJK Ideograph-9727 | 9726 CJK Ideograph-9726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9729 CJK Ideograph-9729 | 9728 CJK Ideograph-9728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972b CJK Ideograph-972B | 972a CJK Ideograph-972A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972d CJK Ideograph-972D | 972c CJK Ideograph-972C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 972f CJK Ideograph-972F | 972e CJK Ideograph-972E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9731 CJK Ideograph-9731 | 9730 CJK Ideograph-9730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9733 CJK Ideograph-9733 | 9732 CJK Ideograph-9732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9735 CJK Ideograph-9735 | 9734 CJK Ideograph-9734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9737 CJK Ideograph-9737 | 9736 CJK Ideograph-9736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9739 CJK Ideograph-9739 | 9738 CJK Ideograph-9738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973b CJK Ideograph-973B | 973a CJK Ideograph-973A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973d CJK Ideograph-973D | 973c CJK Ideograph-973C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 973f CJK Ideograph-973F | 973e CJK Ideograph-973E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9741 CJK Ideograph-9741 | 9740 CJK Ideograph-9740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9743 CJK Ideograph-9743 | 9742 CJK Ideograph-9742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9745 CJK Ideograph-9745 | 9744 CJK Ideograph-9744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9747 CJK Ideograph-9747 | 9746 CJK Ideograph-9746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9749 CJK Ideograph-9749 | 9748 CJK Ideograph-9748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974b CJK Ideograph-974B | 974a CJK Ideograph-974A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974d CJK Ideograph-974D | 974c CJK Ideograph-974C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 974f CJK Ideograph-974F | 974e CJK Ideograph-974E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9751 CJK Ideograph-9751 | 9750 CJK Ideograph-9750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9753 CJK Ideograph-9753 | 9752 CJK Ideograph-9752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9755 CJK Ideograph-9755 | 9754 CJK Ideograph-9754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9757 CJK Ideograph-9757 | 9756 CJK Ideograph-9756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9759 CJK Ideograph-9759 | 9758 CJK Ideograph-9758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975b CJK Ideograph-975B | 975a CJK Ideograph-975A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975d CJK Ideograph-975D | 975c CJK Ideograph-975C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 975f CJK Ideograph-975F | 975e CJK Ideograph-975E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9761 CJK Ideograph-9761 | 9760 CJK Ideograph-9760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9763 CJK Ideograph-9763 | 9762 CJK Ideograph-9762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9765 CJK Ideograph-9765 | 9764 CJK Ideograph-9764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9767 CJK Ideograph-9767 | 9766 CJK Ideograph-9766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9769 CJK Ideograph-9769 | 9768 CJK Ideograph-9768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976b CJK Ideograph-976B | 976a CJK Ideograph-976A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976d CJK Ideograph-976D | 976c CJK Ideograph-976C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 976f CJK Ideograph-976F | 976e CJK Ideograph-976E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9771 CJK Ideograph-9771 | 9770 CJK Ideograph-9770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9773 CJK Ideograph-9773 | 9772 CJK Ideograph-9772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9775 CJK Ideograph-9775 | 9774 CJK Ideograph-9774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9777 CJK Ideograph-9777 | 9776 CJK Ideograph-9776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9779 CJK Ideograph-9779 | 9778 CJK Ideograph-9778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977b CJK Ideograph-977B | 977a CJK Ideograph-977A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977d CJK Ideograph-977D | 977c CJK Ideograph-977C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 977f CJK Ideograph-977F | 977e CJK Ideograph-977E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9781 CJK Ideograph-9781 | 9780 CJK Ideograph-9780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9783 CJK Ideograph-9783 | 9782 CJK Ideograph-9782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9785 CJK Ideograph-9785 | 9784 CJK Ideograph-9784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9787 CJK Ideograph-9787 | 9786 CJK Ideograph-9786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9789 CJK Ideograph-9789 | 9788 CJK Ideograph-9788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978b CJK Ideograph-978B | 978a CJK Ideograph-978A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978d CJK Ideograph-978D | 978c CJK Ideograph-978C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 978f CJK Ideograph-978F | 978e CJK Ideograph-978E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9791 CJK Ideograph-9791 | 9790 CJK Ideograph-9790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9793 CJK Ideograph-9793 | 9792 CJK Ideograph-9792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9795 CJK Ideograph-9795 | 9794 CJK Ideograph-9794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9797 CJK Ideograph-9797 | 9796 CJK Ideograph-9796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9799 CJK Ideograph-9799 | 9798 CJK Ideograph-9798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979b CJK Ideograph-979B | 979a CJK Ideograph-979A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979d CJK Ideograph-979D | 979c CJK Ideograph-979C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 979f CJK Ideograph-979F | 979e CJK Ideograph-979E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a1 CJK Ideograph-97A1 | 97a0 CJK Ideograph-97A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a3 CJK Ideograph-97A3 | 97a2 CJK Ideograph-97A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a5 CJK Ideograph-97A5 | 97a4 CJK Ideograph-97A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a7 CJK Ideograph-97A7 | 97a6 CJK Ideograph-97A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97a9 CJK Ideograph-97A9 | 97a8 CJK Ideograph-97A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ab CJK Ideograph-97AB | 97aa CJK Ideograph-97AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ad CJK Ideograph-97AD | 97ac CJK Ideograph-97AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97af CJK Ideograph-97AF | 97ae CJK Ideograph-97AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b1 CJK Ideograph-97B1 | 97b0 CJK Ideograph-97B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b3 CJK Ideograph-97B3 | 97b2 CJK Ideograph-97B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b5 CJK Ideograph-97B5 | 97b4 CJK Ideograph-97B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b7 CJK Ideograph-97B7 | 97b6 CJK Ideograph-97B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97b9 CJK Ideograph-97B9 | 97b8 CJK Ideograph-97B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bb CJK Ideograph-97BB | 97ba CJK Ideograph-97BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bd CJK Ideograph-97BD | 97bc CJK Ideograph-97BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97bf CJK Ideograph-97BF | 97be CJK Ideograph-97BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c1 CJK Ideograph-97C1 | 97c0 CJK Ideograph-97C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c3 CJK Ideograph-97C3 | 97c2 CJK Ideograph-97C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c5 CJK Ideograph-97C5 | 97c4 CJK Ideograph-97C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c7 CJK Ideograph-97C7 | 97c6 CJK Ideograph-97C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97c9 CJK Ideograph-97C9 | 97c8 CJK Ideograph-97C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cb CJK Ideograph-97CB | 97ca CJK Ideograph-97CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cd CJK Ideograph-97CD | 97cc CJK Ideograph-97CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97cf CJK Ideograph-97CF | 97ce CJK Ideograph-97CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d1 CJK Ideograph-97D1 | 97d0 CJK Ideograph-97D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d3 CJK Ideograph-97D3 | 97d2 CJK Ideograph-97D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d5 CJK Ideograph-97D5 | 97d4 CJK Ideograph-97D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d7 CJK Ideograph-97D7 | 97d6 CJK Ideograph-97D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97d9 CJK Ideograph-97D9 | 97d8 CJK Ideograph-97D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97db CJK Ideograph-97DB | 97da CJK Ideograph-97DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97dd CJK Ideograph-97DD | 97dc CJK Ideograph-97DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97df CJK Ideograph-97DF | 97de CJK Ideograph-97DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e1 CJK Ideograph-97E1 | 97e0 CJK Ideograph-97E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e3 CJK Ideograph-97E3 | 97e2 CJK Ideograph-97E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e5 CJK Ideograph-97E5 | 97e4 CJK Ideograph-97E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e7 CJK Ideograph-97E7 | 97e6 CJK Ideograph-97E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97e9 CJK Ideograph-97E9 | 97e8 CJK Ideograph-97E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97eb CJK Ideograph-97EB | 97ea CJK Ideograph-97EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ed CJK Ideograph-97ED | 97ec CJK Ideograph-97EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ef CJK Ideograph-97EF | 97ee CJK Ideograph-97EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f1 CJK Ideograph-97F1 | 97f0 CJK Ideograph-97F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f3 CJK Ideograph-97F3 | 97f2 CJK Ideograph-97F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f5 CJK Ideograph-97F5 | 97f4 CJK Ideograph-97F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f7 CJK Ideograph-97F7 | 97f6 CJK Ideograph-97F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97f9 CJK Ideograph-97F9 | 97f8 CJK Ideograph-97F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97fb CJK Ideograph-97FB | 97fa CJK Ideograph-97FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97fd CJK Ideograph-97FD | 97fc CJK Ideograph-97FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 97ff CJK Ideograph-97FF | 97fe CJK Ideograph-97FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9801 CJK Ideograph-9801 | 9800 CJK Ideograph-9800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9803 CJK Ideograph-9803 | 9802 CJK Ideograph-9802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9805 CJK Ideograph-9805 | 9804 CJK Ideograph-9804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9807 CJK Ideograph-9807 | 9806 CJK Ideograph-9806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9809 CJK Ideograph-9809 | 9808 CJK Ideograph-9808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980b CJK Ideograph-980B | 980a CJK Ideograph-980A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980d CJK Ideograph-980D | 980c CJK Ideograph-980C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 980f CJK Ideograph-980F | 980e CJK Ideograph-980E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9811 CJK Ideograph-9811 | 9810 CJK Ideograph-9810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9813 CJK Ideograph-9813 | 9812 CJK Ideograph-9812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9815 CJK Ideograph-9815 | 9814 CJK Ideograph-9814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9817 CJK Ideograph-9817 | 9816 CJK Ideograph-9816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9819 CJK Ideograph-9819 | 9818 CJK Ideograph-9818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981b CJK Ideograph-981B | 981a CJK Ideograph-981A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981d CJK Ideograph-981D | 981c CJK Ideograph-981C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 981f CJK Ideograph-981F | 981e CJK Ideograph-981E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9821 CJK Ideograph-9821 | 9820 CJK Ideograph-9820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9823 CJK Ideograph-9823 | 9822 CJK Ideograph-9822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9825 CJK Ideograph-9825 | 9824 CJK Ideograph-9824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9827 CJK Ideograph-9827 | 9826 CJK Ideograph-9826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9829 CJK Ideograph-9829 | 9828 CJK Ideograph-9828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982b CJK Ideograph-982B | 982a CJK Ideograph-982A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982d CJK Ideograph-982D | 982c CJK Ideograph-982C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 982f CJK Ideograph-982F | 982e CJK Ideograph-982E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9831 CJK Ideograph-9831 | 9830 CJK Ideograph-9830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9833 CJK Ideograph-9833 | 9832 CJK Ideograph-9832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9835 CJK Ideograph-9835 | 9834 CJK Ideograph-9834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9837 CJK Ideograph-9837 | 9836 CJK Ideograph-9836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9839 CJK Ideograph-9839 | 9838 CJK Ideograph-9838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983b CJK Ideograph-983B | 983a CJK Ideograph-983A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983d CJK Ideograph-983D | 983c CJK Ideograph-983C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 983f CJK Ideograph-983F | 983e CJK Ideograph-983E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9841 CJK Ideograph-9841 | 9840 CJK Ideograph-9840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9843 CJK Ideograph-9843 | 9842 CJK Ideograph-9842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9845 CJK Ideograph-9845 | 9844 CJK Ideograph-9844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9847 CJK Ideograph-9847 | 9846 CJK Ideograph-9846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9849 CJK Ideograph-9849 | 9848 CJK Ideograph-9848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984b CJK Ideograph-984B | 984a CJK Ideograph-984A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984d CJK Ideograph-984D | 984c CJK Ideograph-984C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 984f CJK Ideograph-984F | 984e CJK Ideograph-984E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9851 CJK Ideograph-9851 | 9850 CJK Ideograph-9850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9853 CJK Ideograph-9853 | 9852 CJK Ideograph-9852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9855 CJK Ideograph-9855 | 9854 CJK Ideograph-9854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9857 CJK Ideograph-9857 | 9856 CJK Ideograph-9856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9859 CJK Ideograph-9859 | 9858 CJK Ideograph-9858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985b CJK Ideograph-985B | 985a CJK Ideograph-985A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985d CJK Ideograph-985D | 985c CJK Ideograph-985C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 985f CJK Ideograph-985F | 985e CJK Ideograph-985E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9861 CJK Ideograph-9861 | 9860 CJK Ideograph-9860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9863 CJK Ideograph-9863 | 9862 CJK Ideograph-9862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9865 CJK Ideograph-9865 | 9864 CJK Ideograph-9864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9867 CJK Ideograph-9867 | 9866 CJK Ideograph-9866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9869 CJK Ideograph-9869 | 9868 CJK Ideograph-9868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986b CJK Ideograph-986B | 986a CJK Ideograph-986A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986d CJK Ideograph-986D | 986c CJK Ideograph-986C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 986f CJK Ideograph-986F | 986e CJK Ideograph-986E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9871 CJK Ideograph-9871 | 9870 CJK Ideograph-9870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9873 CJK Ideograph-9873 | 9872 CJK Ideograph-9872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9875 CJK Ideograph-9875 | 9874 CJK Ideograph-9874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9877 CJK Ideograph-9877 | 9876 CJK Ideograph-9876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9879 CJK Ideograph-9879 | 9878 CJK Ideograph-9878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987b CJK Ideograph-987B | 987a CJK Ideograph-987A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987d CJK Ideograph-987D | 987c CJK Ideograph-987C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 987f CJK Ideograph-987F | 987e CJK Ideograph-987E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9881 CJK Ideograph-9881 | 9880 CJK Ideograph-9880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9883 CJK Ideograph-9883 | 9882 CJK Ideograph-9882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9885 CJK Ideograph-9885 | 9884 CJK Ideograph-9884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9887 CJK Ideograph-9887 | 9886 CJK Ideograph-9886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9889 CJK Ideograph-9889 | 9888 CJK Ideograph-9888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988b CJK Ideograph-988B | 988a CJK Ideograph-988A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988d CJK Ideograph-988D | 988c CJK Ideograph-988C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 988f CJK Ideograph-988F | 988e CJK Ideograph-988E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9891 CJK Ideograph-9891 | 9890 CJK Ideograph-9890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9893 CJK Ideograph-9893 | 9892 CJK Ideograph-9892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9895 CJK Ideograph-9895 | 9894 CJK Ideograph-9894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9897 CJK Ideograph-9897 | 9896 CJK Ideograph-9896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9899 CJK Ideograph-9899 | 9898 CJK Ideograph-9898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989b CJK Ideograph-989B | 989a CJK Ideograph-989A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989d CJK Ideograph-989D | 989c CJK Ideograph-989C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 989f CJK Ideograph-989F | 989e CJK Ideograph-989E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a1 CJK Ideograph-98A1 | 98a0 CJK Ideograph-98A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a3 CJK Ideograph-98A3 | 98a2 CJK Ideograph-98A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a5 CJK Ideograph-98A5 | 98a4 CJK Ideograph-98A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a7 CJK Ideograph-98A7 | 98a6 CJK Ideograph-98A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98a9 CJK Ideograph-98A9 | 98a8 CJK Ideograph-98A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ab CJK Ideograph-98AB | 98aa CJK Ideograph-98AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ad CJK Ideograph-98AD | 98ac CJK Ideograph-98AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98af CJK Ideograph-98AF | 98ae CJK Ideograph-98AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b1 CJK Ideograph-98B1 | 98b0 CJK Ideograph-98B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b3 CJK Ideograph-98B3 | 98b2 CJK Ideograph-98B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b5 CJK Ideograph-98B5 | 98b4 CJK Ideograph-98B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b7 CJK Ideograph-98B7 | 98b6 CJK Ideograph-98B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98b9 CJK Ideograph-98B9 | 98b8 CJK Ideograph-98B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bb CJK Ideograph-98BB | 98ba CJK Ideograph-98BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bd CJK Ideograph-98BD | 98bc CJK Ideograph-98BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98bf CJK Ideograph-98BF | 98be CJK Ideograph-98BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c1 CJK Ideograph-98C1 | 98c0 CJK Ideograph-98C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c3 CJK Ideograph-98C3 | 98c2 CJK Ideograph-98C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c5 CJK Ideograph-98C5 | 98c4 CJK Ideograph-98C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c7 CJK Ideograph-98C7 | 98c6 CJK Ideograph-98C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98c9 CJK Ideograph-98C9 | 98c8 CJK Ideograph-98C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cb CJK Ideograph-98CB | 98ca CJK Ideograph-98CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cd CJK Ideograph-98CD | 98cc CJK Ideograph-98CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98cf CJK Ideograph-98CF | 98ce CJK Ideograph-98CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d1 CJK Ideograph-98D1 | 98d0 CJK Ideograph-98D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d3 CJK Ideograph-98D3 | 98d2 CJK Ideograph-98D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d5 CJK Ideograph-98D5 | 98d4 CJK Ideograph-98D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d7 CJK Ideograph-98D7 | 98d6 CJK Ideograph-98D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98d9 CJK Ideograph-98D9 | 98d8 CJK Ideograph-98D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98db CJK Ideograph-98DB | 98da CJK Ideograph-98DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98dd CJK Ideograph-98DD | 98dc CJK Ideograph-98DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98df CJK Ideograph-98DF | 98de CJK Ideograph-98DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e1 CJK Ideograph-98E1 | 98e0 CJK Ideograph-98E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e3 CJK Ideograph-98E3 | 98e2 CJK Ideograph-98E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e5 CJK Ideograph-98E5 | 98e4 CJK Ideograph-98E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e7 CJK Ideograph-98E7 | 98e6 CJK Ideograph-98E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98e9 CJK Ideograph-98E9 | 98e8 CJK Ideograph-98E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98eb CJK Ideograph-98EB | 98ea CJK Ideograph-98EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ed CJK Ideograph-98ED | 98ec CJK Ideograph-98EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ef CJK Ideograph-98EF | 98ee CJK Ideograph-98EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f1 CJK Ideograph-98F1 | 98f0 CJK Ideograph-98F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f3 CJK Ideograph-98F3 | 98f2 CJK Ideograph-98F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f5 CJK Ideograph-98F5 | 98f4 CJK Ideograph-98F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f7 CJK Ideograph-98F7 | 98f6 CJK Ideograph-98F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98f9 CJK Ideograph-98F9 | 98f8 CJK Ideograph-98F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98fb CJK Ideograph-98FB | 98fa CJK Ideograph-98FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98fd CJK Ideograph-98FD | 98fc CJK Ideograph-98FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 98ff CJK Ideograph-98FF | 98fe CJK Ideograph-98FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9901 CJK Ideograph-9901 | 9900 CJK Ideograph-9900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9903 CJK Ideograph-9903 | 9902 CJK Ideograph-9902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9905 CJK Ideograph-9905 | 9904 CJK Ideograph-9904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9907 CJK Ideograph-9907 | 9906 CJK Ideograph-9906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9909 CJK Ideograph-9909 | 9908 CJK Ideograph-9908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990b CJK Ideograph-990B | 990a CJK Ideograph-990A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990d CJK Ideograph-990D | 990c CJK Ideograph-990C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 990f CJK Ideograph-990F | 990e CJK Ideograph-990E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9911 CJK Ideograph-9911 | 9910 CJK Ideograph-9910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9913 CJK Ideograph-9913 | 9912 CJK Ideograph-9912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9915 CJK Ideograph-9915 | 9914 CJK Ideograph-9914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9917 CJK Ideograph-9917 | 9916 CJK Ideograph-9916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9919 CJK Ideograph-9919 | 9918 CJK Ideograph-9918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991b CJK Ideograph-991B | 991a CJK Ideograph-991A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991d CJK Ideograph-991D | 991c CJK Ideograph-991C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 991f CJK Ideograph-991F | 991e CJK Ideograph-991E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9921 CJK Ideograph-9921 | 9920 CJK Ideograph-9920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9923 CJK Ideograph-9923 | 9922 CJK Ideograph-9922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9925 CJK Ideograph-9925 | 9924 CJK Ideograph-9924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9927 CJK Ideograph-9927 | 9926 CJK Ideograph-9926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9929 CJK Ideograph-9929 | 9928 CJK Ideograph-9928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992b CJK Ideograph-992B | 992a CJK Ideograph-992A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992d CJK Ideograph-992D | 992c CJK Ideograph-992C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 992f CJK Ideograph-992F | 992e CJK Ideograph-992E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9931 CJK Ideograph-9931 | 9930 CJK Ideograph-9930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9933 CJK Ideograph-9933 | 9932 CJK Ideograph-9932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9935 CJK Ideograph-9935 | 9934 CJK Ideograph-9934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9937 CJK Ideograph-9937 | 9936 CJK Ideograph-9936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9939 CJK Ideograph-9939 | 9938 CJK Ideograph-9938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993b CJK Ideograph-993B | 993a CJK Ideograph-993A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993d CJK Ideograph-993D | 993c CJK Ideograph-993C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 993f CJK Ideograph-993F | 993e CJK Ideograph-993E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9941 CJK Ideograph-9941 | 9940 CJK Ideograph-9940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9943 CJK Ideograph-9943 | 9942 CJK Ideograph-9942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9945 CJK Ideograph-9945 | 9944 CJK Ideograph-9944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9947 CJK Ideograph-9947 | 9946 CJK Ideograph-9946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9949 CJK Ideograph-9949 | 9948 CJK Ideograph-9948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994b CJK Ideograph-994B | 994a CJK Ideograph-994A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994d CJK Ideograph-994D | 994c CJK Ideograph-994C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 994f CJK Ideograph-994F | 994e CJK Ideograph-994E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9951 CJK Ideograph-9951 | 9950 CJK Ideograph-9950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9953 CJK Ideograph-9953 | 9952 CJK Ideograph-9952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9955 CJK Ideograph-9955 | 9954 CJK Ideograph-9954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9957 CJK Ideograph-9957 | 9956 CJK Ideograph-9956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9959 CJK Ideograph-9959 | 9958 CJK Ideograph-9958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995b CJK Ideograph-995B | 995a CJK Ideograph-995A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995d CJK Ideograph-995D | 995c CJK Ideograph-995C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 995f CJK Ideograph-995F | 995e CJK Ideograph-995E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9961 CJK Ideograph-9961 | 9960 CJK Ideograph-9960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9963 CJK Ideograph-9963 | 9962 CJK Ideograph-9962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9965 CJK Ideograph-9965 | 9964 CJK Ideograph-9964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9967 CJK Ideograph-9967 | 9966 CJK Ideograph-9966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9969 CJK Ideograph-9969 | 9968 CJK Ideograph-9968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996b CJK Ideograph-996B | 996a CJK Ideograph-996A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996d CJK Ideograph-996D | 996c CJK Ideograph-996C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 996f CJK Ideograph-996F | 996e CJK Ideograph-996E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9971 CJK Ideograph-9971 | 9970 CJK Ideograph-9970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9973 CJK Ideograph-9973 | 9972 CJK Ideograph-9972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9975 CJK Ideograph-9975 | 9974 CJK Ideograph-9974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9977 CJK Ideograph-9977 | 9976 CJK Ideograph-9976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9979 CJK Ideograph-9979 | 9978 CJK Ideograph-9978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997b CJK Ideograph-997B | 997a CJK Ideograph-997A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997d CJK Ideograph-997D | 997c CJK Ideograph-997C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 997f CJK Ideograph-997F | 997e CJK Ideograph-997E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9981 CJK Ideograph-9981 | 9980 CJK Ideograph-9980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9983 CJK Ideograph-9983 | 9982 CJK Ideograph-9982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9985 CJK Ideograph-9985 | 9984 CJK Ideograph-9984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9987 CJK Ideograph-9987 | 9986 CJK Ideograph-9986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9989 CJK Ideograph-9989 | 9988 CJK Ideograph-9988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998b CJK Ideograph-998B | 998a CJK Ideograph-998A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998d CJK Ideograph-998D | 998c CJK Ideograph-998C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 998f CJK Ideograph-998F | 998e CJK Ideograph-998E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9991 CJK Ideograph-9991 | 9990 CJK Ideograph-9990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9993 CJK Ideograph-9993 | 9992 CJK Ideograph-9992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9995 CJK Ideograph-9995 | 9994 CJK Ideograph-9994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9997 CJK Ideograph-9997 | 9996 CJK Ideograph-9996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9999 CJK Ideograph-9999 | 9998 CJK Ideograph-9998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999b CJK Ideograph-999B | 999a CJK Ideograph-999A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999d CJK Ideograph-999D | 999c CJK Ideograph-999C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 999f CJK Ideograph-999F | 999e CJK Ideograph-999E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a1 CJK Ideograph-99A1 | 99a0 CJK Ideograph-99A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a3 CJK Ideograph-99A3 | 99a2 CJK Ideograph-99A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a5 CJK Ideograph-99A5 | 99a4 CJK Ideograph-99A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a7 CJK Ideograph-99A7 | 99a6 CJK Ideograph-99A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99a9 CJK Ideograph-99A9 | 99a8 CJK Ideograph-99A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ab CJK Ideograph-99AB | 99aa CJK Ideograph-99AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ad CJK Ideograph-99AD | 99ac CJK Ideograph-99AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99af CJK Ideograph-99AF | 99ae CJK Ideograph-99AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b1 CJK Ideograph-99B1 | 99b0 CJK Ideograph-99B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b3 CJK Ideograph-99B3 | 99b2 CJK Ideograph-99B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b5 CJK Ideograph-99B5 | 99b4 CJK Ideograph-99B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b7 CJK Ideograph-99B7 | 99b6 CJK Ideograph-99B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99b9 CJK Ideograph-99B9 | 99b8 CJK Ideograph-99B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bb CJK Ideograph-99BB | 99ba CJK Ideograph-99BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bd CJK Ideograph-99BD | 99bc CJK Ideograph-99BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99bf CJK Ideograph-99BF | 99be CJK Ideograph-99BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c1 CJK Ideograph-99C1 | 99c0 CJK Ideograph-99C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c3 CJK Ideograph-99C3 | 99c2 CJK Ideograph-99C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c5 CJK Ideograph-99C5 | 99c4 CJK Ideograph-99C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c7 CJK Ideograph-99C7 | 99c6 CJK Ideograph-99C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99c9 CJK Ideograph-99C9 | 99c8 CJK Ideograph-99C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cb CJK Ideograph-99CB | 99ca CJK Ideograph-99CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cd CJK Ideograph-99CD | 99cc CJK Ideograph-99CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99cf CJK Ideograph-99CF | 99ce CJK Ideograph-99CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d1 CJK Ideograph-99D1 | 99d0 CJK Ideograph-99D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d3 CJK Ideograph-99D3 | 99d2 CJK Ideograph-99D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d5 CJK Ideograph-99D5 | 99d4 CJK Ideograph-99D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d7 CJK Ideograph-99D7 | 99d6 CJK Ideograph-99D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99d9 CJK Ideograph-99D9 | 99d8 CJK Ideograph-99D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99db CJK Ideograph-99DB | 99da CJK Ideograph-99DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99dd CJK Ideograph-99DD | 99dc CJK Ideograph-99DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99df CJK Ideograph-99DF | 99de CJK Ideograph-99DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e1 CJK Ideograph-99E1 | 99e0 CJK Ideograph-99E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e3 CJK Ideograph-99E3 | 99e2 CJK Ideograph-99E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e5 CJK Ideograph-99E5 | 99e4 CJK Ideograph-99E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e7 CJK Ideograph-99E7 | 99e6 CJK Ideograph-99E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99e9 CJK Ideograph-99E9 | 99e8 CJK Ideograph-99E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99eb CJK Ideograph-99EB | 99ea CJK Ideograph-99EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ed CJK Ideograph-99ED | 99ec CJK Ideograph-99EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ef CJK Ideograph-99EF | 99ee CJK Ideograph-99EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f1 CJK Ideograph-99F1 | 99f0 CJK Ideograph-99F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f3 CJK Ideograph-99F3 | 99f2 CJK Ideograph-99F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f5 CJK Ideograph-99F5 | 99f4 CJK Ideograph-99F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f7 CJK Ideograph-99F7 | 99f6 CJK Ideograph-99F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99f9 CJK Ideograph-99F9 | 99f8 CJK Ideograph-99F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99fb CJK Ideograph-99FB | 99fa CJK Ideograph-99FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99fd CJK Ideograph-99FD | 99fc CJK Ideograph-99FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 99ff CJK Ideograph-99FF | 99fe CJK Ideograph-99FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a01 CJK Ideograph-9A01 | 9a00 CJK Ideograph-9A00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a03 CJK Ideograph-9A03 | 9a02 CJK Ideograph-9A02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a05 CJK Ideograph-9A05 | 9a04 CJK Ideograph-9A04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a07 CJK Ideograph-9A07 | 9a06 CJK Ideograph-9A06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a09 CJK Ideograph-9A09 | 9a08 CJK Ideograph-9A08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0b CJK Ideograph-9A0B | 9a0a CJK Ideograph-9A0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0d CJK Ideograph-9A0D | 9a0c CJK Ideograph-9A0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a0f CJK Ideograph-9A0F | 9a0e CJK Ideograph-9A0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a11 CJK Ideograph-9A11 | 9a10 CJK Ideograph-9A10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a13 CJK Ideograph-9A13 | 9a12 CJK Ideograph-9A12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a15 CJK Ideograph-9A15 | 9a14 CJK Ideograph-9A14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a17 CJK Ideograph-9A17 | 9a16 CJK Ideograph-9A16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a19 CJK Ideograph-9A19 | 9a18 CJK Ideograph-9A18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1b CJK Ideograph-9A1B | 9a1a CJK Ideograph-9A1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1d CJK Ideograph-9A1D | 9a1c CJK Ideograph-9A1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a1f CJK Ideograph-9A1F | 9a1e CJK Ideograph-9A1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a21 CJK Ideograph-9A21 | 9a20 CJK Ideograph-9A20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a23 CJK Ideograph-9A23 | 9a22 CJK Ideograph-9A22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a25 CJK Ideograph-9A25 | 9a24 CJK Ideograph-9A24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a27 CJK Ideograph-9A27 | 9a26 CJK Ideograph-9A26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a29 CJK Ideograph-9A29 | 9a28 CJK Ideograph-9A28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2b CJK Ideograph-9A2B | 9a2a CJK Ideograph-9A2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2d CJK Ideograph-9A2D | 9a2c CJK Ideograph-9A2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a2f CJK Ideograph-9A2F | 9a2e CJK Ideograph-9A2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a31 CJK Ideograph-9A31 | 9a30 CJK Ideograph-9A30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a33 CJK Ideograph-9A33 | 9a32 CJK Ideograph-9A32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a35 CJK Ideograph-9A35 | 9a34 CJK Ideograph-9A34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a37 CJK Ideograph-9A37 | 9a36 CJK Ideograph-9A36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a39 CJK Ideograph-9A39 | 9a38 CJK Ideograph-9A38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3b CJK Ideograph-9A3B | 9a3a CJK Ideograph-9A3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3d CJK Ideograph-9A3D | 9a3c CJK Ideograph-9A3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a3f CJK Ideograph-9A3F | 9a3e CJK Ideograph-9A3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a41 CJK Ideograph-9A41 | 9a40 CJK Ideograph-9A40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a43 CJK Ideograph-9A43 | 9a42 CJK Ideograph-9A42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a45 CJK Ideograph-9A45 | 9a44 CJK Ideograph-9A44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a47 CJK Ideograph-9A47 | 9a46 CJK Ideograph-9A46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a49 CJK Ideograph-9A49 | 9a48 CJK Ideograph-9A48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4b CJK Ideograph-9A4B | 9a4a CJK Ideograph-9A4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4d CJK Ideograph-9A4D | 9a4c CJK Ideograph-9A4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a4f CJK Ideograph-9A4F | 9a4e CJK Ideograph-9A4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a51 CJK Ideograph-9A51 | 9a50 CJK Ideograph-9A50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a53 CJK Ideograph-9A53 | 9a52 CJK Ideograph-9A52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a55 CJK Ideograph-9A55 | 9a54 CJK Ideograph-9A54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a57 CJK Ideograph-9A57 | 9a56 CJK Ideograph-9A56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a59 CJK Ideograph-9A59 | 9a58 CJK Ideograph-9A58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5b CJK Ideograph-9A5B | 9a5a CJK Ideograph-9A5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5d CJK Ideograph-9A5D | 9a5c CJK Ideograph-9A5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a5f CJK Ideograph-9A5F | 9a5e CJK Ideograph-9A5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a61 CJK Ideograph-9A61 | 9a60 CJK Ideograph-9A60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a63 CJK Ideograph-9A63 | 9a62 CJK Ideograph-9A62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a65 CJK Ideograph-9A65 | 9a64 CJK Ideograph-9A64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a67 CJK Ideograph-9A67 | 9a66 CJK Ideograph-9A66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a69 CJK Ideograph-9A69 | 9a68 CJK Ideograph-9A68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6b CJK Ideograph-9A6B | 9a6a CJK Ideograph-9A6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6d CJK Ideograph-9A6D | 9a6c CJK Ideograph-9A6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a6f CJK Ideograph-9A6F | 9a6e CJK Ideograph-9A6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a71 CJK Ideograph-9A71 | 9a70 CJK Ideograph-9A70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a73 CJK Ideograph-9A73 | 9a72 CJK Ideograph-9A72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a75 CJK Ideograph-9A75 | 9a74 CJK Ideograph-9A74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a77 CJK Ideograph-9A77 | 9a76 CJK Ideograph-9A76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a79 CJK Ideograph-9A79 | 9a78 CJK Ideograph-9A78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7b CJK Ideograph-9A7B | 9a7a CJK Ideograph-9A7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7d CJK Ideograph-9A7D | 9a7c CJK Ideograph-9A7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a7f CJK Ideograph-9A7F | 9a7e CJK Ideograph-9A7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a81 CJK Ideograph-9A81 | 9a80 CJK Ideograph-9A80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a83 CJK Ideograph-9A83 | 9a82 CJK Ideograph-9A82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a85 CJK Ideograph-9A85 | 9a84 CJK Ideograph-9A84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a87 CJK Ideograph-9A87 | 9a86 CJK Ideograph-9A86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a89 CJK Ideograph-9A89 | 9a88 CJK Ideograph-9A88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8b CJK Ideograph-9A8B | 9a8a CJK Ideograph-9A8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8d CJK Ideograph-9A8D | 9a8c CJK Ideograph-9A8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a8f CJK Ideograph-9A8F | 9a8e CJK Ideograph-9A8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a91 CJK Ideograph-9A91 | 9a90 CJK Ideograph-9A90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a93 CJK Ideograph-9A93 | 9a92 CJK Ideograph-9A92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a95 CJK Ideograph-9A95 | 9a94 CJK Ideograph-9A94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a97 CJK Ideograph-9A97 | 9a96 CJK Ideograph-9A96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a99 CJK Ideograph-9A99 | 9a98 CJK Ideograph-9A98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9b CJK Ideograph-9A9B | 9a9a CJK Ideograph-9A9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9d CJK Ideograph-9A9D | 9a9c CJK Ideograph-9A9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9a9f CJK Ideograph-9A9F | 9a9e CJK Ideograph-9A9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa1 CJK Ideograph-9AA1 | 9aa0 CJK Ideograph-9AA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa3 CJK Ideograph-9AA3 | 9aa2 CJK Ideograph-9AA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa5 CJK Ideograph-9AA5 | 9aa4 CJK Ideograph-9AA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa7 CJK Ideograph-9AA7 | 9aa6 CJK Ideograph-9AA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aa9 CJK Ideograph-9AA9 | 9aa8 CJK Ideograph-9AA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aab CJK Ideograph-9AAB | 9aaa CJK Ideograph-9AAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aad CJK Ideograph-9AAD | 9aac CJK Ideograph-9AAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aaf CJK Ideograph-9AAF | 9aae CJK Ideograph-9AAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab1 CJK Ideograph-9AB1 | 9ab0 CJK Ideograph-9AB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab3 CJK Ideograph-9AB3 | 9ab2 CJK Ideograph-9AB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab5 CJK Ideograph-9AB5 | 9ab4 CJK Ideograph-9AB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab7 CJK Ideograph-9AB7 | 9ab6 CJK Ideograph-9AB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ab9 CJK Ideograph-9AB9 | 9ab8 CJK Ideograph-9AB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abb CJK Ideograph-9ABB | 9aba CJK Ideograph-9ABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abd CJK Ideograph-9ABD | 9abc CJK Ideograph-9ABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9abf CJK Ideograph-9ABF | 9abe CJK Ideograph-9ABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac1 CJK Ideograph-9AC1 | 9ac0 CJK Ideograph-9AC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac3 CJK Ideograph-9AC3 | 9ac2 CJK Ideograph-9AC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac5 CJK Ideograph-9AC5 | 9ac4 CJK Ideograph-9AC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac7 CJK Ideograph-9AC7 | 9ac6 CJK Ideograph-9AC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ac9 CJK Ideograph-9AC9 | 9ac8 CJK Ideograph-9AC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acb CJK Ideograph-9ACB | 9aca CJK Ideograph-9ACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acd CJK Ideograph-9ACD | 9acc CJK Ideograph-9ACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9acf CJK Ideograph-9ACF | 9ace CJK Ideograph-9ACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad1 CJK Ideograph-9AD1 | 9ad0 CJK Ideograph-9AD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad3 CJK Ideograph-9AD3 | 9ad2 CJK Ideograph-9AD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad5 CJK Ideograph-9AD5 | 9ad4 CJK Ideograph-9AD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad7 CJK Ideograph-9AD7 | 9ad6 CJK Ideograph-9AD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ad9 CJK Ideograph-9AD9 | 9ad8 CJK Ideograph-9AD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9adb CJK Ideograph-9ADB | 9ada CJK Ideograph-9ADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9add CJK Ideograph-9ADD | 9adc CJK Ideograph-9ADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9adf CJK Ideograph-9ADF | 9ade CJK Ideograph-9ADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae1 CJK Ideograph-9AE1 | 9ae0 CJK Ideograph-9AE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae3 CJK Ideograph-9AE3 | 9ae2 CJK Ideograph-9AE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae5 CJK Ideograph-9AE5 | 9ae4 CJK Ideograph-9AE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae7 CJK Ideograph-9AE7 | 9ae6 CJK Ideograph-9AE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ae9 CJK Ideograph-9AE9 | 9ae8 CJK Ideograph-9AE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aeb CJK Ideograph-9AEB | 9aea CJK Ideograph-9AEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aed CJK Ideograph-9AED | 9aec CJK Ideograph-9AEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aef CJK Ideograph-9AEF | 9aee CJK Ideograph-9AEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af1 CJK Ideograph-9AF1 | 9af0 CJK Ideograph-9AF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af3 CJK Ideograph-9AF3 | 9af2 CJK Ideograph-9AF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af5 CJK Ideograph-9AF5 | 9af4 CJK Ideograph-9AF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af7 CJK Ideograph-9AF7 | 9af6 CJK Ideograph-9AF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9af9 CJK Ideograph-9AF9 | 9af8 CJK Ideograph-9AF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9afb CJK Ideograph-9AFB | 9afa CJK Ideograph-9AFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9afd CJK Ideograph-9AFD | 9afc CJK Ideograph-9AFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9aff CJK Ideograph-9AFF | 9afe CJK Ideograph-9AFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b01 CJK Ideograph-9B01 | 9b00 CJK Ideograph-9B00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b03 CJK Ideograph-9B03 | 9b02 CJK Ideograph-9B02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b05 CJK Ideograph-9B05 | 9b04 CJK Ideograph-9B04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b07 CJK Ideograph-9B07 | 9b06 CJK Ideograph-9B06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b09 CJK Ideograph-9B09 | 9b08 CJK Ideograph-9B08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0b CJK Ideograph-9B0B | 9b0a CJK Ideograph-9B0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0d CJK Ideograph-9B0D | 9b0c CJK Ideograph-9B0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b0f CJK Ideograph-9B0F | 9b0e CJK Ideograph-9B0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b11 CJK Ideograph-9B11 | 9b10 CJK Ideograph-9B10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b13 CJK Ideograph-9B13 | 9b12 CJK Ideograph-9B12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b15 CJK Ideograph-9B15 | 9b14 CJK Ideograph-9B14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b17 CJK Ideograph-9B17 | 9b16 CJK Ideograph-9B16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b19 CJK Ideograph-9B19 | 9b18 CJK Ideograph-9B18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1b CJK Ideograph-9B1B | 9b1a CJK Ideograph-9B1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1d CJK Ideograph-9B1D | 9b1c CJK Ideograph-9B1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b1f CJK Ideograph-9B1F | 9b1e CJK Ideograph-9B1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b21 CJK Ideograph-9B21 | 9b20 CJK Ideograph-9B20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b23 CJK Ideograph-9B23 | 9b22 CJK Ideograph-9B22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b25 CJK Ideograph-9B25 | 9b24 CJK Ideograph-9B24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b27 CJK Ideograph-9B27 | 9b26 CJK Ideograph-9B26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b29 CJK Ideograph-9B29 | 9b28 CJK Ideograph-9B28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2b CJK Ideograph-9B2B | 9b2a CJK Ideograph-9B2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2d CJK Ideograph-9B2D | 9b2c CJK Ideograph-9B2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b2f CJK Ideograph-9B2F | 9b2e CJK Ideograph-9B2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b31 CJK Ideograph-9B31 | 9b30 CJK Ideograph-9B30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b33 CJK Ideograph-9B33 | 9b32 CJK Ideograph-9B32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b35 CJK Ideograph-9B35 | 9b34 CJK Ideograph-9B34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b37 CJK Ideograph-9B37 | 9b36 CJK Ideograph-9B36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b39 CJK Ideograph-9B39 | 9b38 CJK Ideograph-9B38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3b CJK Ideograph-9B3B | 9b3a CJK Ideograph-9B3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3d CJK Ideograph-9B3D | 9b3c CJK Ideograph-9B3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b3f CJK Ideograph-9B3F | 9b3e CJK Ideograph-9B3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b41 CJK Ideograph-9B41 | 9b40 CJK Ideograph-9B40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b43 CJK Ideograph-9B43 | 9b42 CJK Ideograph-9B42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b45 CJK Ideograph-9B45 | 9b44 CJK Ideograph-9B44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b47 CJK Ideograph-9B47 | 9b46 CJK Ideograph-9B46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b49 CJK Ideograph-9B49 | 9b48 CJK Ideograph-9B48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4b CJK Ideograph-9B4B | 9b4a CJK Ideograph-9B4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4d CJK Ideograph-9B4D | 9b4c CJK Ideograph-9B4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b4f CJK Ideograph-9B4F | 9b4e CJK Ideograph-9B4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b51 CJK Ideograph-9B51 | 9b50 CJK Ideograph-9B50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b53 CJK Ideograph-9B53 | 9b52 CJK Ideograph-9B52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b55 CJK Ideograph-9B55 | 9b54 CJK Ideograph-9B54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b57 CJK Ideograph-9B57 | 9b56 CJK Ideograph-9B56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b59 CJK Ideograph-9B59 | 9b58 CJK Ideograph-9B58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5b CJK Ideograph-9B5B | 9b5a CJK Ideograph-9B5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5d CJK Ideograph-9B5D | 9b5c CJK Ideograph-9B5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b5f CJK Ideograph-9B5F | 9b5e CJK Ideograph-9B5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b61 CJK Ideograph-9B61 | 9b60 CJK Ideograph-9B60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b63 CJK Ideograph-9B63 | 9b62 CJK Ideograph-9B62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b65 CJK Ideograph-9B65 | 9b64 CJK Ideograph-9B64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b67 CJK Ideograph-9B67 | 9b66 CJK Ideograph-9B66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b69 CJK Ideograph-9B69 | 9b68 CJK Ideograph-9B68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6b CJK Ideograph-9B6B | 9b6a CJK Ideograph-9B6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6d CJK Ideograph-9B6D | 9b6c CJK Ideograph-9B6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b6f CJK Ideograph-9B6F | 9b6e CJK Ideograph-9B6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b71 CJK Ideograph-9B71 | 9b70 CJK Ideograph-9B70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b73 CJK Ideograph-9B73 | 9b72 CJK Ideograph-9B72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b75 CJK Ideograph-9B75 | 9b74 CJK Ideograph-9B74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b77 CJK Ideograph-9B77 | 9b76 CJK Ideograph-9B76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b79 CJK Ideograph-9B79 | 9b78 CJK Ideograph-9B78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7b CJK Ideograph-9B7B | 9b7a CJK Ideograph-9B7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7d CJK Ideograph-9B7D | 9b7c CJK Ideograph-9B7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b7f CJK Ideograph-9B7F | 9b7e CJK Ideograph-9B7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b81 CJK Ideograph-9B81 | 9b80 CJK Ideograph-9B80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b83 CJK Ideograph-9B83 | 9b82 CJK Ideograph-9B82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b85 CJK Ideograph-9B85 | 9b84 CJK Ideograph-9B84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b87 CJK Ideograph-9B87 | 9b86 CJK Ideograph-9B86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b89 CJK Ideograph-9B89 | 9b88 CJK Ideograph-9B88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8b CJK Ideograph-9B8B | 9b8a CJK Ideograph-9B8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8d CJK Ideograph-9B8D | 9b8c CJK Ideograph-9B8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b8f CJK Ideograph-9B8F | 9b8e CJK Ideograph-9B8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b91 CJK Ideograph-9B91 | 9b90 CJK Ideograph-9B90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b93 CJK Ideograph-9B93 | 9b92 CJK Ideograph-9B92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b95 CJK Ideograph-9B95 | 9b94 CJK Ideograph-9B94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b97 CJK Ideograph-9B97 | 9b96 CJK Ideograph-9B96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b99 CJK Ideograph-9B99 | 9b98 CJK Ideograph-9B98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9b CJK Ideograph-9B9B | 9b9a CJK Ideograph-9B9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9d CJK Ideograph-9B9D | 9b9c CJK Ideograph-9B9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9b9f CJK Ideograph-9B9F | 9b9e CJK Ideograph-9B9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba1 CJK Ideograph-9BA1 | 9ba0 CJK Ideograph-9BA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba3 CJK Ideograph-9BA3 | 9ba2 CJK Ideograph-9BA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba5 CJK Ideograph-9BA5 | 9ba4 CJK Ideograph-9BA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba7 CJK Ideograph-9BA7 | 9ba6 CJK Ideograph-9BA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ba9 CJK Ideograph-9BA9 | 9ba8 CJK Ideograph-9BA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bab CJK Ideograph-9BAB | 9baa CJK Ideograph-9BAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bad CJK Ideograph-9BAD | 9bac CJK Ideograph-9BAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9baf CJK Ideograph-9BAF | 9bae CJK Ideograph-9BAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb1 CJK Ideograph-9BB1 | 9bb0 CJK Ideograph-9BB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb3 CJK Ideograph-9BB3 | 9bb2 CJK Ideograph-9BB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb5 CJK Ideograph-9BB5 | 9bb4 CJK Ideograph-9BB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb7 CJK Ideograph-9BB7 | 9bb6 CJK Ideograph-9BB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bb9 CJK Ideograph-9BB9 | 9bb8 CJK Ideograph-9BB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbb CJK Ideograph-9BBB | 9bba CJK Ideograph-9BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbd CJK Ideograph-9BBD | 9bbc CJK Ideograph-9BBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bbf CJK Ideograph-9BBF | 9bbe CJK Ideograph-9BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc1 CJK Ideograph-9BC1 | 9bc0 CJK Ideograph-9BC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc3 CJK Ideograph-9BC3 | 9bc2 CJK Ideograph-9BC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc5 CJK Ideograph-9BC5 | 9bc4 CJK Ideograph-9BC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc7 CJK Ideograph-9BC7 | 9bc6 CJK Ideograph-9BC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bc9 CJK Ideograph-9BC9 | 9bc8 CJK Ideograph-9BC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcb CJK Ideograph-9BCB | 9bca CJK Ideograph-9BCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcd CJK Ideograph-9BCD | 9bcc CJK Ideograph-9BCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bcf CJK Ideograph-9BCF | 9bce CJK Ideograph-9BCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd1 CJK Ideograph-9BD1 | 9bd0 CJK Ideograph-9BD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd3 CJK Ideograph-9BD3 | 9bd2 CJK Ideograph-9BD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd5 CJK Ideograph-9BD5 | 9bd4 CJK Ideograph-9BD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd7 CJK Ideograph-9BD7 | 9bd6 CJK Ideograph-9BD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bd9 CJK Ideograph-9BD9 | 9bd8 CJK Ideograph-9BD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdb CJK Ideograph-9BDB | 9bda CJK Ideograph-9BDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdd CJK Ideograph-9BDD | 9bdc CJK Ideograph-9BDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bdf CJK Ideograph-9BDF | 9bde CJK Ideograph-9BDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be1 CJK Ideograph-9BE1 | 9be0 CJK Ideograph-9BE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be3 CJK Ideograph-9BE3 | 9be2 CJK Ideograph-9BE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be5 CJK Ideograph-9BE5 | 9be4 CJK Ideograph-9BE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be7 CJK Ideograph-9BE7 | 9be6 CJK Ideograph-9BE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9be9 CJK Ideograph-9BE9 | 9be8 CJK Ideograph-9BE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9beb CJK Ideograph-9BEB | 9bea CJK Ideograph-9BEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bed CJK Ideograph-9BED | 9bec CJK Ideograph-9BEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bef CJK Ideograph-9BEF | 9bee CJK Ideograph-9BEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf1 CJK Ideograph-9BF1 | 9bf0 CJK Ideograph-9BF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf3 CJK Ideograph-9BF3 | 9bf2 CJK Ideograph-9BF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf5 CJK Ideograph-9BF5 | 9bf4 CJK Ideograph-9BF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf7 CJK Ideograph-9BF7 | 9bf6 CJK Ideograph-9BF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bf9 CJK Ideograph-9BF9 | 9bf8 CJK Ideograph-9BF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bfb CJK Ideograph-9BFB | 9bfa CJK Ideograph-9BFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bfd CJK Ideograph-9BFD | 9bfc CJK Ideograph-9BFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9bff CJK Ideograph-9BFF | 9bfe CJK Ideograph-9BFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c01 CJK Ideograph-9C01 | 9c00 CJK Ideograph-9C00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c03 CJK Ideograph-9C03 | 9c02 CJK Ideograph-9C02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c05 CJK Ideograph-9C05 | 9c04 CJK Ideograph-9C04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c07 CJK Ideograph-9C07 | 9c06 CJK Ideograph-9C06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c09 CJK Ideograph-9C09 | 9c08 CJK Ideograph-9C08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0b CJK Ideograph-9C0B | 9c0a CJK Ideograph-9C0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0d CJK Ideograph-9C0D | 9c0c CJK Ideograph-9C0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c0f CJK Ideograph-9C0F | 9c0e CJK Ideograph-9C0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c11 CJK Ideograph-9C11 | 9c10 CJK Ideograph-9C10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c13 CJK Ideograph-9C13 | 9c12 CJK Ideograph-9C12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c15 CJK Ideograph-9C15 | 9c14 CJK Ideograph-9C14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c17 CJK Ideograph-9C17 | 9c16 CJK Ideograph-9C16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c19 CJK Ideograph-9C19 | 9c18 CJK Ideograph-9C18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1b CJK Ideograph-9C1B | 9c1a CJK Ideograph-9C1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1d CJK Ideograph-9C1D | 9c1c CJK Ideograph-9C1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c1f CJK Ideograph-9C1F | 9c1e CJK Ideograph-9C1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c21 CJK Ideograph-9C21 | 9c20 CJK Ideograph-9C20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c23 CJK Ideograph-9C23 | 9c22 CJK Ideograph-9C22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c25 CJK Ideograph-9C25 | 9c24 CJK Ideograph-9C24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c27 CJK Ideograph-9C27 | 9c26 CJK Ideograph-9C26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c29 CJK Ideograph-9C29 | 9c28 CJK Ideograph-9C28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2b CJK Ideograph-9C2B | 9c2a CJK Ideograph-9C2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2d CJK Ideograph-9C2D | 9c2c CJK Ideograph-9C2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c2f CJK Ideograph-9C2F | 9c2e CJK Ideograph-9C2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c31 CJK Ideograph-9C31 | 9c30 CJK Ideograph-9C30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c33 CJK Ideograph-9C33 | 9c32 CJK Ideograph-9C32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c35 CJK Ideograph-9C35 | 9c34 CJK Ideograph-9C34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c37 CJK Ideograph-9C37 | 9c36 CJK Ideograph-9C36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c39 CJK Ideograph-9C39 | 9c38 CJK Ideograph-9C38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3b CJK Ideograph-9C3B | 9c3a CJK Ideograph-9C3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3d CJK Ideograph-9C3D | 9c3c CJK Ideograph-9C3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c3f CJK Ideograph-9C3F | 9c3e CJK Ideograph-9C3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c41 CJK Ideograph-9C41 | 9c40 CJK Ideograph-9C40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c43 CJK Ideograph-9C43 | 9c42 CJK Ideograph-9C42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c45 CJK Ideograph-9C45 | 9c44 CJK Ideograph-9C44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c47 CJK Ideograph-9C47 | 9c46 CJK Ideograph-9C46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c49 CJK Ideograph-9C49 | 9c48 CJK Ideograph-9C48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4b CJK Ideograph-9C4B | 9c4a CJK Ideograph-9C4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4d CJK Ideograph-9C4D | 9c4c CJK Ideograph-9C4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c4f CJK Ideograph-9C4F | 9c4e CJK Ideograph-9C4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c51 CJK Ideograph-9C51 | 9c50 CJK Ideograph-9C50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c53 CJK Ideograph-9C53 | 9c52 CJK Ideograph-9C52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c55 CJK Ideograph-9C55 | 9c54 CJK Ideograph-9C54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c57 CJK Ideograph-9C57 | 9c56 CJK Ideograph-9C56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c59 CJK Ideograph-9C59 | 9c58 CJK Ideograph-9C58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5b CJK Ideograph-9C5B | 9c5a CJK Ideograph-9C5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5d CJK Ideograph-9C5D | 9c5c CJK Ideograph-9C5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c5f CJK Ideograph-9C5F | 9c5e CJK Ideograph-9C5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c61 CJK Ideograph-9C61 | 9c60 CJK Ideograph-9C60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c63 CJK Ideograph-9C63 | 9c62 CJK Ideograph-9C62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c65 CJK Ideograph-9C65 | 9c64 CJK Ideograph-9C64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c67 CJK Ideograph-9C67 | 9c66 CJK Ideograph-9C66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c69 CJK Ideograph-9C69 | 9c68 CJK Ideograph-9C68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6b CJK Ideograph-9C6B | 9c6a CJK Ideograph-9C6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6d CJK Ideograph-9C6D | 9c6c CJK Ideograph-9C6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c6f CJK Ideograph-9C6F | 9c6e CJK Ideograph-9C6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c71 CJK Ideograph-9C71 | 9c70 CJK Ideograph-9C70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c73 CJK Ideograph-9C73 | 9c72 CJK Ideograph-9C72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c75 CJK Ideograph-9C75 | 9c74 CJK Ideograph-9C74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c77 CJK Ideograph-9C77 | 9c76 CJK Ideograph-9C76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c79 CJK Ideograph-9C79 | 9c78 CJK Ideograph-9C78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7b CJK Ideograph-9C7B | 9c7a CJK Ideograph-9C7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7d CJK Ideograph-9C7D | 9c7c CJK Ideograph-9C7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c7f CJK Ideograph-9C7F | 9c7e CJK Ideograph-9C7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c81 CJK Ideograph-9C81 | 9c80 CJK Ideograph-9C80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c83 CJK Ideograph-9C83 | 9c82 CJK Ideograph-9C82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c85 CJK Ideograph-9C85 | 9c84 CJK Ideograph-9C84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c87 CJK Ideograph-9C87 | 9c86 CJK Ideograph-9C86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c89 CJK Ideograph-9C89 | 9c88 CJK Ideograph-9C88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8b CJK Ideograph-9C8B | 9c8a CJK Ideograph-9C8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8d CJK Ideograph-9C8D | 9c8c CJK Ideograph-9C8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c8f CJK Ideograph-9C8F | 9c8e CJK Ideograph-9C8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c91 CJK Ideograph-9C91 | 9c90 CJK Ideograph-9C90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c93 CJK Ideograph-9C93 | 9c92 CJK Ideograph-9C92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c95 CJK Ideograph-9C95 | 9c94 CJK Ideograph-9C94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c97 CJK Ideograph-9C97 | 9c96 CJK Ideograph-9C96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c99 CJK Ideograph-9C99 | 9c98 CJK Ideograph-9C98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9b CJK Ideograph-9C9B | 9c9a CJK Ideograph-9C9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9d CJK Ideograph-9C9D | 9c9c CJK Ideograph-9C9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9c9f CJK Ideograph-9C9F | 9c9e CJK Ideograph-9C9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca1 CJK Ideograph-9CA1 | 9ca0 CJK Ideograph-9CA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca3 CJK Ideograph-9CA3 | 9ca2 CJK Ideograph-9CA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca5 CJK Ideograph-9CA5 | 9ca4 CJK Ideograph-9CA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca7 CJK Ideograph-9CA7 | 9ca6 CJK Ideograph-9CA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ca9 CJK Ideograph-9CA9 | 9ca8 CJK Ideograph-9CA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cab CJK Ideograph-9CAB | 9caa CJK Ideograph-9CAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cad CJK Ideograph-9CAD | 9cac CJK Ideograph-9CAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9caf CJK Ideograph-9CAF | 9cae CJK Ideograph-9CAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb1 CJK Ideograph-9CB1 | 9cb0 CJK Ideograph-9CB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb3 CJK Ideograph-9CB3 | 9cb2 CJK Ideograph-9CB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb5 CJK Ideograph-9CB5 | 9cb4 CJK Ideograph-9CB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb7 CJK Ideograph-9CB7 | 9cb6 CJK Ideograph-9CB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cb9 CJK Ideograph-9CB9 | 9cb8 CJK Ideograph-9CB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbb CJK Ideograph-9CBB | 9cba CJK Ideograph-9CBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbd CJK Ideograph-9CBD | 9cbc CJK Ideograph-9CBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cbf CJK Ideograph-9CBF | 9cbe CJK Ideograph-9CBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc1 CJK Ideograph-9CC1 | 9cc0 CJK Ideograph-9CC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc3 CJK Ideograph-9CC3 | 9cc2 CJK Ideograph-9CC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc5 CJK Ideograph-9CC5 | 9cc4 CJK Ideograph-9CC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc7 CJK Ideograph-9CC7 | 9cc6 CJK Ideograph-9CC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cc9 CJK Ideograph-9CC9 | 9cc8 CJK Ideograph-9CC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccb CJK Ideograph-9CCB | 9cca CJK Ideograph-9CCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccd CJK Ideograph-9CCD | 9ccc CJK Ideograph-9CCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ccf CJK Ideograph-9CCF | 9cce CJK Ideograph-9CCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd1 CJK Ideograph-9CD1 | 9cd0 CJK Ideograph-9CD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd3 CJK Ideograph-9CD3 | 9cd2 CJK Ideograph-9CD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd5 CJK Ideograph-9CD5 | 9cd4 CJK Ideograph-9CD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd7 CJK Ideograph-9CD7 | 9cd6 CJK Ideograph-9CD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cd9 CJK Ideograph-9CD9 | 9cd8 CJK Ideograph-9CD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdb CJK Ideograph-9CDB | 9cda CJK Ideograph-9CDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdd CJK Ideograph-9CDD | 9cdc CJK Ideograph-9CDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cdf CJK Ideograph-9CDF | 9cde CJK Ideograph-9CDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce1 CJK Ideograph-9CE1 | 9ce0 CJK Ideograph-9CE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce3 CJK Ideograph-9CE3 | 9ce2 CJK Ideograph-9CE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce5 CJK Ideograph-9CE5 | 9ce4 CJK Ideograph-9CE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce7 CJK Ideograph-9CE7 | 9ce6 CJK Ideograph-9CE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ce9 CJK Ideograph-9CE9 | 9ce8 CJK Ideograph-9CE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ceb CJK Ideograph-9CEB | 9cea CJK Ideograph-9CEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ced CJK Ideograph-9CED | 9cec CJK Ideograph-9CEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cef CJK Ideograph-9CEF | 9cee CJK Ideograph-9CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf1 CJK Ideograph-9CF1 | 9cf0 CJK Ideograph-9CF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf3 CJK Ideograph-9CF3 | 9cf2 CJK Ideograph-9CF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf5 CJK Ideograph-9CF5 | 9cf4 CJK Ideograph-9CF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf7 CJK Ideograph-9CF7 | 9cf6 CJK Ideograph-9CF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cf9 CJK Ideograph-9CF9 | 9cf8 CJK Ideograph-9CF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cfb CJK Ideograph-9CFB | 9cfa CJK Ideograph-9CFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cfd CJK Ideograph-9CFD | 9cfc CJK Ideograph-9CFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9cff CJK Ideograph-9CFF | 9cfe CJK Ideograph-9CFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d01 CJK Ideograph-9D01 | 9d00 CJK Ideograph-9D00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d03 CJK Ideograph-9D03 | 9d02 CJK Ideograph-9D02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d05 CJK Ideograph-9D05 | 9d04 CJK Ideograph-9D04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d07 CJK Ideograph-9D07 | 9d06 CJK Ideograph-9D06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d09 CJK Ideograph-9D09 | 9d08 CJK Ideograph-9D08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0b CJK Ideograph-9D0B | 9d0a CJK Ideograph-9D0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0d CJK Ideograph-9D0D | 9d0c CJK Ideograph-9D0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d0f CJK Ideograph-9D0F | 9d0e CJK Ideograph-9D0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d11 CJK Ideograph-9D11 | 9d10 CJK Ideograph-9D10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d13 CJK Ideograph-9D13 | 9d12 CJK Ideograph-9D12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d15 CJK Ideograph-9D15 | 9d14 CJK Ideograph-9D14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d17 CJK Ideograph-9D17 | 9d16 CJK Ideograph-9D16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d19 CJK Ideograph-9D19 | 9d18 CJK Ideograph-9D18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1b CJK Ideograph-9D1B | 9d1a CJK Ideograph-9D1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1d CJK Ideograph-9D1D | 9d1c CJK Ideograph-9D1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d1f CJK Ideograph-9D1F | 9d1e CJK Ideograph-9D1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d21 CJK Ideograph-9D21 | 9d20 CJK Ideograph-9D20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d23 CJK Ideograph-9D23 | 9d22 CJK Ideograph-9D22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d25 CJK Ideograph-9D25 | 9d24 CJK Ideograph-9D24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d27 CJK Ideograph-9D27 | 9d26 CJK Ideograph-9D26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d29 CJK Ideograph-9D29 | 9d28 CJK Ideograph-9D28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2b CJK Ideograph-9D2B | 9d2a CJK Ideograph-9D2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2d CJK Ideograph-9D2D | 9d2c CJK Ideograph-9D2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d2f CJK Ideograph-9D2F | 9d2e CJK Ideograph-9D2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d31 CJK Ideograph-9D31 | 9d30 CJK Ideograph-9D30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d33 CJK Ideograph-9D33 | 9d32 CJK Ideograph-9D32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d35 CJK Ideograph-9D35 | 9d34 CJK Ideograph-9D34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d37 CJK Ideograph-9D37 | 9d36 CJK Ideograph-9D36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d39 CJK Ideograph-9D39 | 9d38 CJK Ideograph-9D38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3b CJK Ideograph-9D3B | 9d3a CJK Ideograph-9D3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3d CJK Ideograph-9D3D | 9d3c CJK Ideograph-9D3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d3f CJK Ideograph-9D3F | 9d3e CJK Ideograph-9D3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d41 CJK Ideograph-9D41 | 9d40 CJK Ideograph-9D40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d43 CJK Ideograph-9D43 | 9d42 CJK Ideograph-9D42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d45 CJK Ideograph-9D45 | 9d44 CJK Ideograph-9D44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d47 CJK Ideograph-9D47 | 9d46 CJK Ideograph-9D46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d49 CJK Ideograph-9D49 | 9d48 CJK Ideograph-9D48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4b CJK Ideograph-9D4B | 9d4a CJK Ideograph-9D4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4d CJK Ideograph-9D4D | 9d4c CJK Ideograph-9D4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d4f CJK Ideograph-9D4F | 9d4e CJK Ideograph-9D4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d51 CJK Ideograph-9D51 | 9d50 CJK Ideograph-9D50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d53 CJK Ideograph-9D53 | 9d52 CJK Ideograph-9D52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d55 CJK Ideograph-9D55 | 9d54 CJK Ideograph-9D54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d57 CJK Ideograph-9D57 | 9d56 CJK Ideograph-9D56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d59 CJK Ideograph-9D59 | 9d58 CJK Ideograph-9D58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5b CJK Ideograph-9D5B | 9d5a CJK Ideograph-9D5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5d CJK Ideograph-9D5D | 9d5c CJK Ideograph-9D5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d5f CJK Ideograph-9D5F | 9d5e CJK Ideograph-9D5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d61 CJK Ideograph-9D61 | 9d60 CJK Ideograph-9D60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d63 CJK Ideograph-9D63 | 9d62 CJK Ideograph-9D62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d65 CJK Ideograph-9D65 | 9d64 CJK Ideograph-9D64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d67 CJK Ideograph-9D67 | 9d66 CJK Ideograph-9D66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d69 CJK Ideograph-9D69 | 9d68 CJK Ideograph-9D68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6b CJK Ideograph-9D6B | 9d6a CJK Ideograph-9D6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6d CJK Ideograph-9D6D | 9d6c CJK Ideograph-9D6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d6f CJK Ideograph-9D6F | 9d6e CJK Ideograph-9D6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d71 CJK Ideograph-9D71 | 9d70 CJK Ideograph-9D70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d73 CJK Ideograph-9D73 | 9d72 CJK Ideograph-9D72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d75 CJK Ideograph-9D75 | 9d74 CJK Ideograph-9D74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d77 CJK Ideograph-9D77 | 9d76 CJK Ideograph-9D76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d79 CJK Ideograph-9D79 | 9d78 CJK Ideograph-9D78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7b CJK Ideograph-9D7B | 9d7a CJK Ideograph-9D7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7d CJK Ideograph-9D7D | 9d7c CJK Ideograph-9D7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d7f CJK Ideograph-9D7F | 9d7e CJK Ideograph-9D7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d81 CJK Ideograph-9D81 | 9d80 CJK Ideograph-9D80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d83 CJK Ideograph-9D83 | 9d82 CJK Ideograph-9D82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d85 CJK Ideograph-9D85 | 9d84 CJK Ideograph-9D84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d87 CJK Ideograph-9D87 | 9d86 CJK Ideograph-9D86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d89 CJK Ideograph-9D89 | 9d88 CJK Ideograph-9D88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8b CJK Ideograph-9D8B | 9d8a CJK Ideograph-9D8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8d CJK Ideograph-9D8D | 9d8c CJK Ideograph-9D8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d8f CJK Ideograph-9D8F | 9d8e CJK Ideograph-9D8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d91 CJK Ideograph-9D91 | 9d90 CJK Ideograph-9D90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d93 CJK Ideograph-9D93 | 9d92 CJK Ideograph-9D92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d95 CJK Ideograph-9D95 | 9d94 CJK Ideograph-9D94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d97 CJK Ideograph-9D97 | 9d96 CJK Ideograph-9D96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d99 CJK Ideograph-9D99 | 9d98 CJK Ideograph-9D98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9b CJK Ideograph-9D9B | 9d9a CJK Ideograph-9D9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9d CJK Ideograph-9D9D | 9d9c CJK Ideograph-9D9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9d9f CJK Ideograph-9D9F | 9d9e CJK Ideograph-9D9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da1 CJK Ideograph-9DA1 | 9da0 CJK Ideograph-9DA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da3 CJK Ideograph-9DA3 | 9da2 CJK Ideograph-9DA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da5 CJK Ideograph-9DA5 | 9da4 CJK Ideograph-9DA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da7 CJK Ideograph-9DA7 | 9da6 CJK Ideograph-9DA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9da9 CJK Ideograph-9DA9 | 9da8 CJK Ideograph-9DA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dab CJK Ideograph-9DAB | 9daa CJK Ideograph-9DAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dad CJK Ideograph-9DAD | 9dac CJK Ideograph-9DAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9daf CJK Ideograph-9DAF | 9dae CJK Ideograph-9DAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db1 CJK Ideograph-9DB1 | 9db0 CJK Ideograph-9DB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db3 CJK Ideograph-9DB3 | 9db2 CJK Ideograph-9DB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db5 CJK Ideograph-9DB5 | 9db4 CJK Ideograph-9DB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db7 CJK Ideograph-9DB7 | 9db6 CJK Ideograph-9DB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9db9 CJK Ideograph-9DB9 | 9db8 CJK Ideograph-9DB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbb CJK Ideograph-9DBB | 9dba CJK Ideograph-9DBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbd CJK Ideograph-9DBD | 9dbc CJK Ideograph-9DBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dbf CJK Ideograph-9DBF | 9dbe CJK Ideograph-9DBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc1 CJK Ideograph-9DC1 | 9dc0 CJK Ideograph-9DC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc3 CJK Ideograph-9DC3 | 9dc2 CJK Ideograph-9DC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc5 CJK Ideograph-9DC5 | 9dc4 CJK Ideograph-9DC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc7 CJK Ideograph-9DC7 | 9dc6 CJK Ideograph-9DC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dc9 CJK Ideograph-9DC9 | 9dc8 CJK Ideograph-9DC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcb CJK Ideograph-9DCB | 9dca CJK Ideograph-9DCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcd CJK Ideograph-9DCD | 9dcc CJK Ideograph-9DCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dcf CJK Ideograph-9DCF | 9dce CJK Ideograph-9DCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd1 CJK Ideograph-9DD1 | 9dd0 CJK Ideograph-9DD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd3 CJK Ideograph-9DD3 | 9dd2 CJK Ideograph-9DD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd5 CJK Ideograph-9DD5 | 9dd4 CJK Ideograph-9DD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd7 CJK Ideograph-9DD7 | 9dd6 CJK Ideograph-9DD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dd9 CJK Ideograph-9DD9 | 9dd8 CJK Ideograph-9DD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddb CJK Ideograph-9DDB | 9dda CJK Ideograph-9DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddd CJK Ideograph-9DDD | 9ddc CJK Ideograph-9DDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ddf CJK Ideograph-9DDF | 9dde CJK Ideograph-9DDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de1 CJK Ideograph-9DE1 | 9de0 CJK Ideograph-9DE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de3 CJK Ideograph-9DE3 | 9de2 CJK Ideograph-9DE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de5 CJK Ideograph-9DE5 | 9de4 CJK Ideograph-9DE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de7 CJK Ideograph-9DE7 | 9de6 CJK Ideograph-9DE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9de9 CJK Ideograph-9DE9 | 9de8 CJK Ideograph-9DE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9deb CJK Ideograph-9DEB | 9dea CJK Ideograph-9DEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ded CJK Ideograph-9DED | 9dec CJK Ideograph-9DEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9def CJK Ideograph-9DEF | 9dee CJK Ideograph-9DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df1 CJK Ideograph-9DF1 | 9df0 CJK Ideograph-9DF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df3 CJK Ideograph-9DF3 | 9df2 CJK Ideograph-9DF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df5 CJK Ideograph-9DF5 | 9df4 CJK Ideograph-9DF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df7 CJK Ideograph-9DF7 | 9df6 CJK Ideograph-9DF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9df9 CJK Ideograph-9DF9 | 9df8 CJK Ideograph-9DF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dfb CJK Ideograph-9DFB | 9dfa CJK Ideograph-9DFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dfd CJK Ideograph-9DFD | 9dfc CJK Ideograph-9DFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9dff CJK Ideograph-9DFF | 9dfe CJK Ideograph-9DFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e01 CJK Ideograph-9E01 | 9e00 CJK Ideograph-9E00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e03 CJK Ideograph-9E03 | 9e02 CJK Ideograph-9E02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e05 CJK Ideograph-9E05 | 9e04 CJK Ideograph-9E04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e07 CJK Ideograph-9E07 | 9e06 CJK Ideograph-9E06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e09 CJK Ideograph-9E09 | 9e08 CJK Ideograph-9E08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0b CJK Ideograph-9E0B | 9e0a CJK Ideograph-9E0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0d CJK Ideograph-9E0D | 9e0c CJK Ideograph-9E0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e0f CJK Ideograph-9E0F | 9e0e CJK Ideograph-9E0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e11 CJK Ideograph-9E11 | 9e10 CJK Ideograph-9E10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e13 CJK Ideograph-9E13 | 9e12 CJK Ideograph-9E12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e15 CJK Ideograph-9E15 | 9e14 CJK Ideograph-9E14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e17 CJK Ideograph-9E17 | 9e16 CJK Ideograph-9E16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e19 CJK Ideograph-9E19 | 9e18 CJK Ideograph-9E18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1b CJK Ideograph-9E1B | 9e1a CJK Ideograph-9E1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1d CJK Ideograph-9E1D | 9e1c CJK Ideograph-9E1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e1f CJK Ideograph-9E1F | 9e1e CJK Ideograph-9E1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e21 CJK Ideograph-9E21 | 9e20 CJK Ideograph-9E20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e23 CJK Ideograph-9E23 | 9e22 CJK Ideograph-9E22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e25 CJK Ideograph-9E25 | 9e24 CJK Ideograph-9E24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e27 CJK Ideograph-9E27 | 9e26 CJK Ideograph-9E26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e29 CJK Ideograph-9E29 | 9e28 CJK Ideograph-9E28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2b CJK Ideograph-9E2B | 9e2a CJK Ideograph-9E2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2d CJK Ideograph-9E2D | 9e2c CJK Ideograph-9E2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e2f CJK Ideograph-9E2F | 9e2e CJK Ideograph-9E2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e31 CJK Ideograph-9E31 | 9e30 CJK Ideograph-9E30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e33 CJK Ideograph-9E33 | 9e32 CJK Ideograph-9E32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e35 CJK Ideograph-9E35 | 9e34 CJK Ideograph-9E34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e37 CJK Ideograph-9E37 | 9e36 CJK Ideograph-9E36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e39 CJK Ideograph-9E39 | 9e38 CJK Ideograph-9E38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3b CJK Ideograph-9E3B | 9e3a CJK Ideograph-9E3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3d CJK Ideograph-9E3D | 9e3c CJK Ideograph-9E3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e3f CJK Ideograph-9E3F | 9e3e CJK Ideograph-9E3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e41 CJK Ideograph-9E41 | 9e40 CJK Ideograph-9E40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e43 CJK Ideograph-9E43 | 9e42 CJK Ideograph-9E42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e45 CJK Ideograph-9E45 | 9e44 CJK Ideograph-9E44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e47 CJK Ideograph-9E47 | 9e46 CJK Ideograph-9E46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e49 CJK Ideograph-9E49 | 9e48 CJK Ideograph-9E48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4b CJK Ideograph-9E4B | 9e4a CJK Ideograph-9E4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4d CJK Ideograph-9E4D | 9e4c CJK Ideograph-9E4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e4f CJK Ideograph-9E4F | 9e4e CJK Ideograph-9E4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e51 CJK Ideograph-9E51 | 9e50 CJK Ideograph-9E50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e53 CJK Ideograph-9E53 | 9e52 CJK Ideograph-9E52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e55 CJK Ideograph-9E55 | 9e54 CJK Ideograph-9E54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e57 CJK Ideograph-9E57 | 9e56 CJK Ideograph-9E56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e59 CJK Ideograph-9E59 | 9e58 CJK Ideograph-9E58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5b CJK Ideograph-9E5B | 9e5a CJK Ideograph-9E5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5d CJK Ideograph-9E5D | 9e5c CJK Ideograph-9E5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e5f CJK Ideograph-9E5F | 9e5e CJK Ideograph-9E5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e61 CJK Ideograph-9E61 | 9e60 CJK Ideograph-9E60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e63 CJK Ideograph-9E63 | 9e62 CJK Ideograph-9E62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e65 CJK Ideograph-9E65 | 9e64 CJK Ideograph-9E64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e67 CJK Ideograph-9E67 | 9e66 CJK Ideograph-9E66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e69 CJK Ideograph-9E69 | 9e68 CJK Ideograph-9E68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6b CJK Ideograph-9E6B | 9e6a CJK Ideograph-9E6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6d CJK Ideograph-9E6D | 9e6c CJK Ideograph-9E6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e6f CJK Ideograph-9E6F | 9e6e CJK Ideograph-9E6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e71 CJK Ideograph-9E71 | 9e70 CJK Ideograph-9E70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e73 CJK Ideograph-9E73 | 9e72 CJK Ideograph-9E72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e75 CJK Ideograph-9E75 | 9e74 CJK Ideograph-9E74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e77 CJK Ideograph-9E77 | 9e76 CJK Ideograph-9E76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e79 CJK Ideograph-9E79 | 9e78 CJK Ideograph-9E78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7b CJK Ideograph-9E7B | 9e7a CJK Ideograph-9E7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7d CJK Ideograph-9E7D | 9e7c CJK Ideograph-9E7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e7f CJK Ideograph-9E7F | 9e7e CJK Ideograph-9E7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e81 CJK Ideograph-9E81 | 9e80 CJK Ideograph-9E80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e83 CJK Ideograph-9E83 | 9e82 CJK Ideograph-9E82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e85 CJK Ideograph-9E85 | 9e84 CJK Ideograph-9E84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e87 CJK Ideograph-9E87 | 9e86 CJK Ideograph-9E86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e89 CJK Ideograph-9E89 | 9e88 CJK Ideograph-9E88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8b CJK Ideograph-9E8B | 9e8a CJK Ideograph-9E8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8d CJK Ideograph-9E8D | 9e8c CJK Ideograph-9E8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e8f CJK Ideograph-9E8F | 9e8e CJK Ideograph-9E8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e91 CJK Ideograph-9E91 | 9e90 CJK Ideograph-9E90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e93 CJK Ideograph-9E93 | 9e92 CJK Ideograph-9E92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e95 CJK Ideograph-9E95 | 9e94 CJK Ideograph-9E94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e97 CJK Ideograph-9E97 | 9e96 CJK Ideograph-9E96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e99 CJK Ideograph-9E99 | 9e98 CJK Ideograph-9E98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9b CJK Ideograph-9E9B | 9e9a CJK Ideograph-9E9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9d CJK Ideograph-9E9D | 9e9c CJK Ideograph-9E9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9e9f CJK Ideograph-9E9F | 9e9e CJK Ideograph-9E9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea1 CJK Ideograph-9EA1 | 9ea0 CJK Ideograph-9EA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea3 CJK Ideograph-9EA3 | 9ea2 CJK Ideograph-9EA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea5 CJK Ideograph-9EA5 | 9ea4 CJK Ideograph-9EA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea7 CJK Ideograph-9EA7 | 9ea6 CJK Ideograph-9EA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ea9 CJK Ideograph-9EA9 | 9ea8 CJK Ideograph-9EA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eab CJK Ideograph-9EAB | 9eaa CJK Ideograph-9EAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ead CJK Ideograph-9EAD | 9eac CJK Ideograph-9EAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eaf CJK Ideograph-9EAF | 9eae CJK Ideograph-9EAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb1 CJK Ideograph-9EB1 | 9eb0 CJK Ideograph-9EB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb3 CJK Ideograph-9EB3 | 9eb2 CJK Ideograph-9EB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb5 CJK Ideograph-9EB5 | 9eb4 CJK Ideograph-9EB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb7 CJK Ideograph-9EB7 | 9eb6 CJK Ideograph-9EB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eb9 CJK Ideograph-9EB9 | 9eb8 CJK Ideograph-9EB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebb CJK Ideograph-9EBB | 9eba CJK Ideograph-9EBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebd CJK Ideograph-9EBD | 9ebc CJK Ideograph-9EBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ebf CJK Ideograph-9EBF | 9ebe CJK Ideograph-9EBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec1 CJK Ideograph-9EC1 | 9ec0 CJK Ideograph-9EC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec3 CJK Ideograph-9EC3 | 9ec2 CJK Ideograph-9EC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec5 CJK Ideograph-9EC5 | 9ec4 CJK Ideograph-9EC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec7 CJK Ideograph-9EC7 | 9ec6 CJK Ideograph-9EC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ec9 CJK Ideograph-9EC9 | 9ec8 CJK Ideograph-9EC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecb CJK Ideograph-9ECB | 9eca CJK Ideograph-9ECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecd CJK Ideograph-9ECD | 9ecc CJK Ideograph-9ECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ecf CJK Ideograph-9ECF | 9ece CJK Ideograph-9ECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed1 CJK Ideograph-9ED1 | 9ed0 CJK Ideograph-9ED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed3 CJK Ideograph-9ED3 | 9ed2 CJK Ideograph-9ED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed5 CJK Ideograph-9ED5 | 9ed4 CJK Ideograph-9ED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed7 CJK Ideograph-9ED7 | 9ed6 CJK Ideograph-9ED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ed9 CJK Ideograph-9ED9 | 9ed8 CJK Ideograph-9ED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edb CJK Ideograph-9EDB | 9eda CJK Ideograph-9EDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edd CJK Ideograph-9EDD | 9edc CJK Ideograph-9EDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9edf CJK Ideograph-9EDF | 9ede CJK Ideograph-9EDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee1 CJK Ideograph-9EE1 | 9ee0 CJK Ideograph-9EE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee3 CJK Ideograph-9EE3 | 9ee2 CJK Ideograph-9EE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee5 CJK Ideograph-9EE5 | 9ee4 CJK Ideograph-9EE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee7 CJK Ideograph-9EE7 | 9ee6 CJK Ideograph-9EE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ee9 CJK Ideograph-9EE9 | 9ee8 CJK Ideograph-9EE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eeb CJK Ideograph-9EEB | 9eea CJK Ideograph-9EEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eed CJK Ideograph-9EED | 9eec CJK Ideograph-9EEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eef CJK Ideograph-9EEF | 9eee CJK Ideograph-9EEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef1 CJK Ideograph-9EF1 | 9ef0 CJK Ideograph-9EF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef3 CJK Ideograph-9EF3 | 9ef2 CJK Ideograph-9EF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef5 CJK Ideograph-9EF5 | 9ef4 CJK Ideograph-9EF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef7 CJK Ideograph-9EF7 | 9ef6 CJK Ideograph-9EF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9ef9 CJK Ideograph-9EF9 | 9ef8 CJK Ideograph-9EF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9efb CJK Ideograph-9EFB | 9efa CJK Ideograph-9EFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9efd CJK Ideograph-9EFD | 9efc CJK Ideograph-9EFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9eff CJK Ideograph-9EFF | 9efe CJK Ideograph-9EFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f01 CJK Ideograph-9F01 | 9f00 CJK Ideograph-9F00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f03 CJK Ideograph-9F03 | 9f02 CJK Ideograph-9F02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f05 CJK Ideograph-9F05 | 9f04 CJK Ideograph-9F04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f07 CJK Ideograph-9F07 | 9f06 CJK Ideograph-9F06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f09 CJK Ideograph-9F09 | 9f08 CJK Ideograph-9F08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0b CJK Ideograph-9F0B | 9f0a CJK Ideograph-9F0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0d CJK Ideograph-9F0D | 9f0c CJK Ideograph-9F0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f0f CJK Ideograph-9F0F | 9f0e CJK Ideograph-9F0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f11 CJK Ideograph-9F11 | 9f10 CJK Ideograph-9F10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f13 CJK Ideograph-9F13 | 9f12 CJK Ideograph-9F12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f15 CJK Ideograph-9F15 | 9f14 CJK Ideograph-9F14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f17 CJK Ideograph-9F17 | 9f16 CJK Ideograph-9F16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f19 CJK Ideograph-9F19 | 9f18 CJK Ideograph-9F18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1b CJK Ideograph-9F1B | 9f1a CJK Ideograph-9F1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1d CJK Ideograph-9F1D | 9f1c CJK Ideograph-9F1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f1f CJK Ideograph-9F1F | 9f1e CJK Ideograph-9F1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f21 CJK Ideograph-9F21 | 9f20 CJK Ideograph-9F20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f23 CJK Ideograph-9F23 | 9f22 CJK Ideograph-9F22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f25 CJK Ideograph-9F25 | 9f24 CJK Ideograph-9F24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f27 CJK Ideograph-9F27 | 9f26 CJK Ideograph-9F26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f29 CJK Ideograph-9F29 | 9f28 CJK Ideograph-9F28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2b CJK Ideograph-9F2B | 9f2a CJK Ideograph-9F2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2d CJK Ideograph-9F2D | 9f2c CJK Ideograph-9F2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f2f CJK Ideograph-9F2F | 9f2e CJK Ideograph-9F2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f31 CJK Ideograph-9F31 | 9f30 CJK Ideograph-9F30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f33 CJK Ideograph-9F33 | 9f32 CJK Ideograph-9F32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f35 CJK Ideograph-9F35 | 9f34 CJK Ideograph-9F34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f37 CJK Ideograph-9F37 | 9f36 CJK Ideograph-9F36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f39 CJK Ideograph-9F39 | 9f38 CJK Ideograph-9F38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3b CJK Ideograph-9F3B | 9f3a CJK Ideograph-9F3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3d CJK Ideograph-9F3D | 9f3c CJK Ideograph-9F3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f3f CJK Ideograph-9F3F | 9f3e CJK Ideograph-9F3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f41 CJK Ideograph-9F41 | 9f40 CJK Ideograph-9F40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f43 CJK Ideograph-9F43 | 9f42 CJK Ideograph-9F42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f45 CJK Ideograph-9F45 | 9f44 CJK Ideograph-9F44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f47 CJK Ideograph-9F47 | 9f46 CJK Ideograph-9F46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f49 CJK Ideograph-9F49 | 9f48 CJK Ideograph-9F48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4b CJK Ideograph-9F4B | 9f4a CJK Ideograph-9F4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4d CJK Ideograph-9F4D | 9f4c CJK Ideograph-9F4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f4f CJK Ideograph-9F4F | 9f4e CJK Ideograph-9F4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f51 CJK Ideograph-9F51 | 9f50 CJK Ideograph-9F50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f53 CJK Ideograph-9F53 | 9f52 CJK Ideograph-9F52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f55 CJK Ideograph-9F55 | 9f54 CJK Ideograph-9F54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f57 CJK Ideograph-9F57 | 9f56 CJK Ideograph-9F56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f59 CJK Ideograph-9F59 | 9f58 CJK Ideograph-9F58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5b CJK Ideograph-9F5B | 9f5a CJK Ideograph-9F5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5d CJK Ideograph-9F5D | 9f5c CJK Ideograph-9F5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f5f CJK Ideograph-9F5F | 9f5e CJK Ideograph-9F5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f61 CJK Ideograph-9F61 | 9f60 CJK Ideograph-9F60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f63 CJK Ideograph-9F63 | 9f62 CJK Ideograph-9F62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f65 CJK Ideograph-9F65 | 9f64 CJK Ideograph-9F64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f67 CJK Ideograph-9F67 | 9f66 CJK Ideograph-9F66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f69 CJK Ideograph-9F69 | 9f68 CJK Ideograph-9F68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6b CJK Ideograph-9F6B | 9f6a CJK Ideograph-9F6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6d CJK Ideograph-9F6D | 9f6c CJK Ideograph-9F6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f6f CJK Ideograph-9F6F | 9f6e CJK Ideograph-9F6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f71 CJK Ideograph-9F71 | 9f70 CJK Ideograph-9F70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f73 CJK Ideograph-9F73 | 9f72 CJK Ideograph-9F72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f75 CJK Ideograph-9F75 | 9f74 CJK Ideograph-9F74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f77 CJK Ideograph-9F77 | 9f76 CJK Ideograph-9F76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f79 CJK Ideograph-9F79 | 9f78 CJK Ideograph-9F78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7b CJK Ideograph-9F7B | 9f7a CJK Ideograph-9F7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7d CJK Ideograph-9F7D | 9f7c CJK Ideograph-9F7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f7f CJK Ideograph-9F7F | 9f7e CJK Ideograph-9F7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f81 CJK Ideograph-9F81 | 9f80 CJK Ideograph-9F80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f83 CJK Ideograph-9F83 | 9f82 CJK Ideograph-9F82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f85 CJK Ideograph-9F85 | 9f84 CJK Ideograph-9F84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f87 CJK Ideograph-9F87 | 9f86 CJK Ideograph-9F86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f89 CJK Ideograph-9F89 | 9f88 CJK Ideograph-9F88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8b CJK Ideograph-9F8B | 9f8a CJK Ideograph-9F8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8d CJK Ideograph-9F8D | 9f8c CJK Ideograph-9F8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f8f CJK Ideograph-9F8F | 9f8e CJK Ideograph-9F8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f91 CJK Ideograph-9F91 | 9f90 CJK Ideograph-9F90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f93 CJK Ideograph-9F93 | 9f92 CJK Ideograph-9F92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f95 CJK Ideograph-9F95 | 9f94 CJK Ideograph-9F94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f97 CJK Ideograph-9F97 | 9f96 CJK Ideograph-9F96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f99 CJK Ideograph-9F99 | 9f98 CJK Ideograph-9F98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9b CJK Ideograph-9F9B | 9f9a CJK Ideograph-9F9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9d CJK Ideograph-9F9D | 9f9c CJK Ideograph-9F9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9f9f CJK Ideograph-9F9F | 9f9e CJK Ideograph-9F9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa1 CJK Ideograph-9FA1 | 9fa0 CJK Ideograph-9FA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa3 CJK Ideograph-9FA3 | 9fa2 CJK Ideograph-9FA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa5 CJK Ideograph-9FA5 | 9fa4 CJK Ideograph-9FA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa7 CJK Ideograph-9FA7 | 9fa6 CJK Ideograph-9FA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fa9 CJK Ideograph-9FA9 | 9fa8 CJK Ideograph-9FA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fab CJK Ideograph-9FAB | 9faa CJK Ideograph-9FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fad CJK Ideograph-9FAD | 9fac CJK Ideograph-9FAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9faf CJK Ideograph-9FAF | 9fae CJK Ideograph-9FAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb1 CJK Ideograph-9FB1 | 9fb0 CJK Ideograph-9FB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb3 CJK Ideograph-9FB3 | 9fb2 CJK Ideograph-9FB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb5 CJK Ideograph-9FB5 | 9fb4 CJK Ideograph-9FB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb7 CJK Ideograph-9FB7 | 9fb6 CJK Ideograph-9FB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fb9 CJK Ideograph-9FB9 | 9fb8 CJK Ideograph-9FB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbb CJK Ideograph-9FBB | 9fba CJK Ideograph-9FBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbd CJK Ideograph-9FBD | 9fbc CJK Ideograph-9FBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fbf CJK Ideograph-9FBF | 9fbe CJK Ideograph-9FBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc1 CJK Ideograph-9FC1 | 9fc0 CJK Ideograph-9FC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc3 CJK Ideograph-9FC3 | 9fc2 CJK Ideograph-9FC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc5 CJK Ideograph-9FC5 | 9fc4 CJK Ideograph-9FC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc7 CJK Ideograph-9FC7 | 9fc6 CJK Ideograph-9FC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fc9 CJK Ideograph-9FC9 | 9fc8 CJK Ideograph-9FC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* 9fcb CJK Ideograph-9FCB | 9fca CJK Ideograph-9FCA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* 9fcd (null) | 9fcc CJK Ideograph-9FCC */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fcf (null) | 9fce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd1 (null) | 9fd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd3 (null) | 9fd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd5 (null) | 9fd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd7 (null) | 9fd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fd9 (null) | 9fd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdb (null) | 9fda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdd (null) | 9fdc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fdf (null) | 9fde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe1 (null) | 9fe0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe3 (null) | 9fe2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe5 (null) | 9fe4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe7 (null) | 9fe6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fe9 (null) | 9fe8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9feb (null) | 9fea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fed (null) | 9fec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fef (null) | 9fee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff1 (null) | 9ff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff3 (null) | 9ff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff5 (null) | 9ff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff7 (null) | 9ff6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ff9 (null) | 9ff8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ffb (null) | 9ffa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9ffd (null) | 9ffc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* 9fff (null) | 9ffe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a001 YI SYLLABLE IX | a000 YI SYLLABLE IT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a003 YI SYLLABLE IP | a002 YI SYLLABLE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a005 YI SYLLABLE IEX | a004 YI SYLLABLE IET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a007 YI SYLLABLE IEP | a006 YI SYLLABLE IE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a009 YI SYLLABLE AX | a008 YI SYLLABLE AT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00b YI SYLLABLE AP | a00a YI SYLLABLE A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00d YI SYLLABLE UO | a00c YI SYLLABLE UOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a00f YI SYLLABLE OT | a00e YI SYLLABLE UOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a011 YI SYLLABLE O | a010 YI SYLLABLE OX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a013 YI SYLLABLE EX | a012 YI SYLLABLE OP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a015 YI SYLLABLE WU | a014 YI SYLLABLE E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a017 YI SYLLABLE BIX | a016 YI SYLLABLE BIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a019 YI SYLLABLE BIP | a018 YI SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01b YI SYLLABLE BIEX | a01a YI SYLLABLE BIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01d YI SYLLABLE BIEP | a01c YI SYLLABLE BIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a01f YI SYLLABLE BAX | a01e YI SYLLABLE BAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a021 YI SYLLABLE BAP | a020 YI SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a023 YI SYLLABLE BUO | a022 YI SYLLABLE BUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a025 YI SYLLABLE BOT | a024 YI SYLLABLE BUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a027 YI SYLLABLE BO | a026 YI SYLLABLE BOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a029 YI SYLLABLE BEX | a028 YI SYLLABLE BOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02b YI SYLLABLE BEP | a02a YI SYLLABLE BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02d YI SYLLABLE BUX | a02c YI SYLLABLE BUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a02f YI SYLLABLE BUP | a02e YI SYLLABLE BU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a031 YI SYLLABLE BUR | a030 YI SYLLABLE BURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a033 YI SYLLABLE BYX | a032 YI SYLLABLE BYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a035 YI SYLLABLE BYP | a034 YI SYLLABLE BY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a037 YI SYLLABLE BYR | a036 YI SYLLABLE BYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a039 YI SYLLABLE PIX | a038 YI SYLLABLE PIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03b YI SYLLABLE PIP | a03a YI SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03d YI SYLLABLE PIE | a03c YI SYLLABLE PIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a03f YI SYLLABLE PAT | a03e YI SYLLABLE PIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a041 YI SYLLABLE PA | a040 YI SYLLABLE PAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a043 YI SYLLABLE PUOX | a042 YI SYLLABLE PAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a045 YI SYLLABLE PUOP | a044 YI SYLLABLE PUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a047 YI SYLLABLE POX | a046 YI SYLLABLE POT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a049 YI SYLLABLE POP | a048 YI SYLLABLE PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04b YI SYLLABLE PUX | a04a YI SYLLABLE PUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04d YI SYLLABLE PUP | a04c YI SYLLABLE PU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a04f YI SYLLABLE PUR | a04e YI SYLLABLE PURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a051 YI SYLLABLE PYX | a050 YI SYLLABLE PYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a053 YI SYLLABLE PYP | a052 YI SYLLABLE PY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a055 YI SYLLABLE PYR | a054 YI SYLLABLE PYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a057 YI SYLLABLE BBIX | a056 YI SYLLABLE BBIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a059 YI SYLLABLE BBIP | a058 YI SYLLABLE BBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05b YI SYLLABLE BBIEX | a05a YI SYLLABLE BBIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05d YI SYLLABLE BBIEP | a05c YI SYLLABLE BBIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a05f YI SYLLABLE BBAX | a05e YI SYLLABLE BBAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a061 YI SYLLABLE BBAP | a060 YI SYLLABLE BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a063 YI SYLLABLE BBUO | a062 YI SYLLABLE BBUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a065 YI SYLLABLE BBOT | a064 YI SYLLABLE BBUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a067 YI SYLLABLE BBO | a066 YI SYLLABLE BBOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a069 YI SYLLABLE BBEX | a068 YI SYLLABLE BBOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06b YI SYLLABLE BBEP | a06a YI SYLLABLE BBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06d YI SYLLABLE BBUX | a06c YI SYLLABLE BBUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a06f YI SYLLABLE BBUP | a06e YI SYLLABLE BBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a071 YI SYLLABLE BBUR | a070 YI SYLLABLE BBURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a073 YI SYLLABLE BBYX | a072 YI SYLLABLE BBYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a075 YI SYLLABLE BBYP | a074 YI SYLLABLE BBY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a077 YI SYLLABLE NBIX | a076 YI SYLLABLE NBIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a079 YI SYLLABLE NBIP | a078 YI SYLLABLE NBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07b YI SYLLABLE NBIE | a07a YI SYLLABLE NBIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07d YI SYLLABLE NBAT | a07c YI SYLLABLE NBIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a07f YI SYLLABLE NBA | a07e YI SYLLABLE NBAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a081 YI SYLLABLE NBOT | a080 YI SYLLABLE NBAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a083 YI SYLLABLE NBO | a082 YI SYLLABLE NBOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a085 YI SYLLABLE NBUT | a084 YI SYLLABLE NBOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a087 YI SYLLABLE NBU | a086 YI SYLLABLE NBUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a089 YI SYLLABLE NBURX | a088 YI SYLLABLE NBUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08b YI SYLLABLE NBYT | a08a YI SYLLABLE NBUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08d YI SYLLABLE NBY | a08c YI SYLLABLE NBYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a08f YI SYLLABLE NBYRX | a08e YI SYLLABLE NBYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a091 YI SYLLABLE HMIT | a090 YI SYLLABLE NBYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a093 YI SYLLABLE HMI | a092 YI SYLLABLE HMIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a095 YI SYLLABLE HMIEX | a094 YI SYLLABLE HMIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a097 YI SYLLABLE HMIEP | a096 YI SYLLABLE HMIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a099 YI SYLLABLE HMAX | a098 YI SYLLABLE HMAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09b YI SYLLABLE HMAP | a09a YI SYLLABLE HMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09d YI SYLLABLE HMUO | a09c YI SYLLABLE HMUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a09f YI SYLLABLE HMOT | a09e YI SYLLABLE HMUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a1 YI SYLLABLE HMO | a0a0 YI SYLLABLE HMOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a3 YI SYLLABLE HMUT | a0a2 YI SYLLABLE HMOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a5 YI SYLLABLE HMU | a0a4 YI SYLLABLE HMUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a7 YI SYLLABLE HMURX | a0a6 YI SYLLABLE HMUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0a9 YI SYLLABLE HMYX | a0a8 YI SYLLABLE HMUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ab YI SYLLABLE HMYP | a0aa YI SYLLABLE HMY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ad YI SYLLABLE HMYR | a0ac YI SYLLABLE HMYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0af YI SYLLABLE MIX | a0ae YI SYLLABLE MIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b1 YI SYLLABLE MIP | a0b0 YI SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b3 YI SYLLABLE MIE | a0b2 YI SYLLABLE MIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b5 YI SYLLABLE MAT | a0b4 YI SYLLABLE MIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b7 YI SYLLABLE MA | a0b6 YI SYLLABLE MAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0b9 YI SYLLABLE MUOT | a0b8 YI SYLLABLE MAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bb YI SYLLABLE MUO | a0ba YI SYLLABLE MUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bd YI SYLLABLE MOT | a0bc YI SYLLABLE MUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0bf YI SYLLABLE MO | a0be YI SYLLABLE MOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c1 YI SYLLABLE MEX | a0c0 YI SYLLABLE MOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c3 YI SYLLABLE MUT | a0c2 YI SYLLABLE ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c5 YI SYLLABLE MU | a0c4 YI SYLLABLE MUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c7 YI SYLLABLE MURX | a0c6 YI SYLLABLE MUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0c9 YI SYLLABLE MYT | a0c8 YI SYLLABLE MUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cb YI SYLLABLE MY | a0ca YI SYLLABLE MYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cd YI SYLLABLE FIT | a0cc YI SYLLABLE MYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0cf YI SYLLABLE FI | a0ce YI SYLLABLE FIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d1 YI SYLLABLE FAT | a0d0 YI SYLLABLE FIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d3 YI SYLLABLE FA | a0d2 YI SYLLABLE FAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d5 YI SYLLABLE FOX | a0d4 YI SYLLABLE FAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d7 YI SYLLABLE FOP | a0d6 YI SYLLABLE FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0d9 YI SYLLABLE FUX | a0d8 YI SYLLABLE FUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0db YI SYLLABLE FUP | a0da YI SYLLABLE FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0dd YI SYLLABLE FUR | a0dc YI SYLLABLE FURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0df YI SYLLABLE FYX | a0de YI SYLLABLE FYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e1 YI SYLLABLE FYP | a0e0 YI SYLLABLE FY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e3 YI SYLLABLE VIX | a0e2 YI SYLLABLE VIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e5 YI SYLLABLE VIP | a0e4 YI SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e7 YI SYLLABLE VIEX | a0e6 YI SYLLABLE VIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0e9 YI SYLLABLE VIEP | a0e8 YI SYLLABLE VIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0eb YI SYLLABLE VAX | a0ea YI SYLLABLE VAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ed YI SYLLABLE VAP | a0ec YI SYLLABLE VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ef YI SYLLABLE VOX | a0ee YI SYLLABLE VOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f1 YI SYLLABLE VOP | a0f0 YI SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f3 YI SYLLABLE VEP | a0f2 YI SYLLABLE VEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f5 YI SYLLABLE VUX | a0f4 YI SYLLABLE VUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f7 YI SYLLABLE VUP | a0f6 YI SYLLABLE VU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0f9 YI SYLLABLE VUR | a0f8 YI SYLLABLE VURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0fb YI SYLLABLE VYX | a0fa YI SYLLABLE VYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0fd YI SYLLABLE VYP | a0fc YI SYLLABLE VY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a0ff YI SYLLABLE VYR | a0fe YI SYLLABLE VYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a101 YI SYLLABLE DIX | a100 YI SYLLABLE DIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a103 YI SYLLABLE DIP | a102 YI SYLLABLE DI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a105 YI SYLLABLE DIE | a104 YI SYLLABLE DIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a107 YI SYLLABLE DAT | a106 YI SYLLABLE DIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a109 YI SYLLABLE DA | a108 YI SYLLABLE DAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10b YI SYLLABLE DUOX | a10a YI SYLLABLE DAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10d YI SYLLABLE DOT | a10c YI SYLLABLE DUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a10f YI SYLLABLE DO | a10e YI SYLLABLE DOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a111 YI SYLLABLE DEX | a110 YI SYLLABLE DOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a113 YI SYLLABLE DEP | a112 YI SYLLABLE DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a115 YI SYLLABLE DUX | a114 YI SYLLABLE DUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a117 YI SYLLABLE DUP | a116 YI SYLLABLE DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a119 YI SYLLABLE DUR | a118 YI SYLLABLE DURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11b YI SYLLABLE TIX | a11a YI SYLLABLE TIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11d YI SYLLABLE TIP | a11c YI SYLLABLE TI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a11f YI SYLLABLE TIE | a11e YI SYLLABLE TIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a121 YI SYLLABLE TAT | a120 YI SYLLABLE TIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a123 YI SYLLABLE TA | a122 YI SYLLABLE TAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a125 YI SYLLABLE TUOT | a124 YI SYLLABLE TAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a127 YI SYLLABLE TUO | a126 YI SYLLABLE TUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a129 YI SYLLABLE TOT | a128 YI SYLLABLE TUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12b YI SYLLABLE TO | a12a YI SYLLABLE TOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12d YI SYLLABLE TEX | a12c YI SYLLABLE TOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a12f YI SYLLABLE TEP | a12e YI SYLLABLE TE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a131 YI SYLLABLE TUX | a130 YI SYLLABLE TUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a133 YI SYLLABLE TUP | a132 YI SYLLABLE TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a135 YI SYLLABLE TUR | a134 YI SYLLABLE TURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a137 YI SYLLABLE DDIX | a136 YI SYLLABLE DDIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a139 YI SYLLABLE DDIP | a138 YI SYLLABLE DDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13b YI SYLLABLE DDIE | a13a YI SYLLABLE DDIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13d YI SYLLABLE DDAT | a13c YI SYLLABLE DDIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a13f YI SYLLABLE DDA | a13e YI SYLLABLE DDAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a141 YI SYLLABLE DDUOX | a140 YI SYLLABLE DDAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a143 YI SYLLABLE DDUOP | a142 YI SYLLABLE DDUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a145 YI SYLLABLE DDOX | a144 YI SYLLABLE DDOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a147 YI SYLLABLE DDOP | a146 YI SYLLABLE DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a149 YI SYLLABLE DDE | a148 YI SYLLABLE DDEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14b YI SYLLABLE DDUT | a14a YI SYLLABLE DDEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14d YI SYLLABLE DDU | a14c YI SYLLABLE DDUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a14f YI SYLLABLE DDURX | a14e YI SYLLABLE DDUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a151 YI SYLLABLE NDIT | a150 YI SYLLABLE DDUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a153 YI SYLLABLE NDI | a152 YI SYLLABLE NDIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a155 YI SYLLABLE NDIEX | a154 YI SYLLABLE NDIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a157 YI SYLLABLE NDAT | a156 YI SYLLABLE NDIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a159 YI SYLLABLE NDA | a158 YI SYLLABLE NDAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15b YI SYLLABLE NDOT | a15a YI SYLLABLE NDAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15d YI SYLLABLE NDO | a15c YI SYLLABLE NDOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a15f YI SYLLABLE NDEX | a15e YI SYLLABLE NDOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a161 YI SYLLABLE NDEP | a160 YI SYLLABLE NDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a163 YI SYLLABLE NDUX | a162 YI SYLLABLE NDUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a165 YI SYLLABLE NDUP | a164 YI SYLLABLE NDU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a167 YI SYLLABLE NDUR | a166 YI SYLLABLE NDURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a169 YI SYLLABLE HNIX | a168 YI SYLLABLE HNIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16b YI SYLLABLE HNIP | a16a YI SYLLABLE HNI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16d YI SYLLABLE HNIEX | a16c YI SYLLABLE HNIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a16f YI SYLLABLE HNIEP | a16e YI SYLLABLE HNIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a171 YI SYLLABLE HNAX | a170 YI SYLLABLE HNAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a173 YI SYLLABLE HNAP | a172 YI SYLLABLE HNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a175 YI SYLLABLE HNUO | a174 YI SYLLABLE HNUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a177 YI SYLLABLE HNOX | a176 YI SYLLABLE HNOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a179 YI SYLLABLE HNEX | a178 YI SYLLABLE HNOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17b YI SYLLABLE HNEP | a17a YI SYLLABLE HNE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17d YI SYLLABLE NIT | a17c YI SYLLABLE HNUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a17f YI SYLLABLE NI | a17e YI SYLLABLE NIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a181 YI SYLLABLE NIEX | a180 YI SYLLABLE NIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a183 YI SYLLABLE NIEP | a182 YI SYLLABLE NIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a185 YI SYLLABLE NA | a184 YI SYLLABLE NAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a187 YI SYLLABLE NUOX | a186 YI SYLLABLE NAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a189 YI SYLLABLE NUOP | a188 YI SYLLABLE NUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18b YI SYLLABLE NOX | a18a YI SYLLABLE NOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18d YI SYLLABLE NOP | a18c YI SYLLABLE NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a18f YI SYLLABLE NE | a18e YI SYLLABLE NEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a191 YI SYLLABLE NUT | a190 YI SYLLABLE NEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a193 YI SYLLABLE NU | a192 YI SYLLABLE NUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a195 YI SYLLABLE NURX | a194 YI SYLLABLE NUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a197 YI SYLLABLE HLIT | a196 YI SYLLABLE NUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a199 YI SYLLABLE HLI | a198 YI SYLLABLE HLIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19b YI SYLLABLE HLIEX | a19a YI SYLLABLE HLIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19d YI SYLLABLE HLIEP | a19c YI SYLLABLE HLIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a19f YI SYLLABLE HLAX | a19e YI SYLLABLE HLAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a1 YI SYLLABLE HLAP | a1a0 YI SYLLABLE HLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a3 YI SYLLABLE HLUO | a1a2 YI SYLLABLE HLUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a5 YI SYLLABLE HLOX | a1a4 YI SYLLABLE HLUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a7 YI SYLLABLE HLOP | a1a6 YI SYLLABLE HLO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1a9 YI SYLLABLE HLE | a1a8 YI SYLLABLE HLEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ab YI SYLLABLE HLUT | a1aa YI SYLLABLE HLEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ad YI SYLLABLE HLU | a1ac YI SYLLABLE HLUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1af YI SYLLABLE HLURX | a1ae YI SYLLABLE HLUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b1 YI SYLLABLE HLYT | a1b0 YI SYLLABLE HLUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b3 YI SYLLABLE HLY | a1b2 YI SYLLABLE HLYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b5 YI SYLLABLE HLYRX | a1b4 YI SYLLABLE HLYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b7 YI SYLLABLE LIT | a1b6 YI SYLLABLE HLYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1b9 YI SYLLABLE LI | a1b8 YI SYLLABLE LIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bb YI SYLLABLE LIET | a1ba YI SYLLABLE LIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bd YI SYLLABLE LIE | a1bc YI SYLLABLE LIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1bf YI SYLLABLE LAT | a1be YI SYLLABLE LIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c1 YI SYLLABLE LA | a1c0 YI SYLLABLE LAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c3 YI SYLLABLE LUOT | a1c2 YI SYLLABLE LAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c5 YI SYLLABLE LUO | a1c4 YI SYLLABLE LUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c7 YI SYLLABLE LOT | a1c6 YI SYLLABLE LUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1c9 YI SYLLABLE LO | a1c8 YI SYLLABLE LOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cb YI SYLLABLE LEX | a1ca YI SYLLABLE LOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cd YI SYLLABLE LEP | a1cc YI SYLLABLE LE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1cf YI SYLLABLE LUX | a1ce YI SYLLABLE LUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d1 YI SYLLABLE LUP | a1d0 YI SYLLABLE LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d3 YI SYLLABLE LUR | a1d2 YI SYLLABLE LURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d5 YI SYLLABLE LYX | a1d4 YI SYLLABLE LYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d7 YI SYLLABLE LYP | a1d6 YI SYLLABLE LY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1d9 YI SYLLABLE LYR | a1d8 YI SYLLABLE LYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1db YI SYLLABLE GIX | a1da YI SYLLABLE GIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1dd YI SYLLABLE GIP | a1dc YI SYLLABLE GI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1df YI SYLLABLE GIEX | a1de YI SYLLABLE GIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e1 YI SYLLABLE GIEP | a1e0 YI SYLLABLE GIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e3 YI SYLLABLE GAX | a1e2 YI SYLLABLE GAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e5 YI SYLLABLE GAP | a1e4 YI SYLLABLE GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e7 YI SYLLABLE GUOX | a1e6 YI SYLLABLE GUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1e9 YI SYLLABLE GUOP | a1e8 YI SYLLABLE GUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1eb YI SYLLABLE GOX | a1ea YI SYLLABLE GOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ed YI SYLLABLE GOP | a1ec YI SYLLABLE GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ef YI SYLLABLE GEX | a1ee YI SYLLABLE GET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f1 YI SYLLABLE GEP | a1f0 YI SYLLABLE GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f3 YI SYLLABLE GUX | a1f2 YI SYLLABLE GUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f5 YI SYLLABLE GUP | a1f4 YI SYLLABLE GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f7 YI SYLLABLE GUR | a1f6 YI SYLLABLE GURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1f9 YI SYLLABLE KIX | a1f8 YI SYLLABLE KIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1fb YI SYLLABLE KIP | a1fa YI SYLLABLE KI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1fd YI SYLLABLE KIE | a1fc YI SYLLABLE KIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a1ff YI SYLLABLE KAT | a1fe YI SYLLABLE KIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a201 YI SYLLABLE KA | a200 YI SYLLABLE KAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a203 YI SYLLABLE KUOX | a202 YI SYLLABLE KAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a205 YI SYLLABLE KUOP | a204 YI SYLLABLE KUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a207 YI SYLLABLE KOX | a206 YI SYLLABLE KOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a209 YI SYLLABLE KOP | a208 YI SYLLABLE KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20b YI SYLLABLE KEX | a20a YI SYLLABLE KET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20d YI SYLLABLE KEP | a20c YI SYLLABLE KE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a20f YI SYLLABLE KUX | a20e YI SYLLABLE KUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a211 YI SYLLABLE KUP | a210 YI SYLLABLE KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a213 YI SYLLABLE KUR | a212 YI SYLLABLE KURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a215 YI SYLLABLE GGIX | a214 YI SYLLABLE GGIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a217 YI SYLLABLE GGIEX | a216 YI SYLLABLE GGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a219 YI SYLLABLE GGIEP | a218 YI SYLLABLE GGIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21b YI SYLLABLE GGAX | a21a YI SYLLABLE GGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21d YI SYLLABLE GGAP | a21c YI SYLLABLE GGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a21f YI SYLLABLE GGUOX | a21e YI SYLLABLE GGUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a221 YI SYLLABLE GGUOP | a220 YI SYLLABLE GGUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a223 YI SYLLABLE GGOX | a222 YI SYLLABLE GGOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a225 YI SYLLABLE GGOP | a224 YI SYLLABLE GGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a227 YI SYLLABLE GGEX | a226 YI SYLLABLE GGET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a229 YI SYLLABLE GGEP | a228 YI SYLLABLE GGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22b YI SYLLABLE GGUX | a22a YI SYLLABLE GGUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22d YI SYLLABLE GGUP | a22c YI SYLLABLE GGU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a22f YI SYLLABLE GGUR | a22e YI SYLLABLE GGURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a231 YI SYLLABLE MGIE | a230 YI SYLLABLE MGIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a233 YI SYLLABLE MGAX | a232 YI SYLLABLE MGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a235 YI SYLLABLE MGAP | a234 YI SYLLABLE MGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a237 YI SYLLABLE MGUO | a236 YI SYLLABLE MGUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a239 YI SYLLABLE MGOT | a238 YI SYLLABLE MGUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23b YI SYLLABLE MGO | a23a YI SYLLABLE MGOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23d YI SYLLABLE MGEX | a23c YI SYLLABLE MGOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a23f YI SYLLABLE MGEP | a23e YI SYLLABLE MGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a241 YI SYLLABLE MGUX | a240 YI SYLLABLE MGUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a243 YI SYLLABLE MGUP | a242 YI SYLLABLE MGU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a245 YI SYLLABLE MGUR | a244 YI SYLLABLE MGURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a247 YI SYLLABLE HXIX | a246 YI SYLLABLE HXIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a249 YI SYLLABLE HXIP | a248 YI SYLLABLE HXI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24b YI SYLLABLE HXIEX | a24a YI SYLLABLE HXIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24d YI SYLLABLE HXIEP | a24c YI SYLLABLE HXIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a24f YI SYLLABLE HXAX | a24e YI SYLLABLE HXAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a251 YI SYLLABLE HXAP | a250 YI SYLLABLE HXA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a253 YI SYLLABLE HXUOX | a252 YI SYLLABLE HXUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a255 YI SYLLABLE HXUOP | a254 YI SYLLABLE HXUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a257 YI SYLLABLE HXOX | a256 YI SYLLABLE HXOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a259 YI SYLLABLE HXOP | a258 YI SYLLABLE HXO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25b YI SYLLABLE HXE | a25a YI SYLLABLE HXEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25d YI SYLLABLE NGIEX | a25c YI SYLLABLE HXEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a25f YI SYLLABLE NGIEP | a25e YI SYLLABLE NGIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a261 YI SYLLABLE NGAX | a260 YI SYLLABLE NGAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a263 YI SYLLABLE NGAP | a262 YI SYLLABLE NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a265 YI SYLLABLE NGUOX | a264 YI SYLLABLE NGUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a267 YI SYLLABLE NGOT | a266 YI SYLLABLE NGUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a269 YI SYLLABLE NGO | a268 YI SYLLABLE NGOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26b YI SYLLABLE NGEX | a26a YI SYLLABLE NGOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26d YI SYLLABLE NGEP | a26c YI SYLLABLE NGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a26f YI SYLLABLE HIEX | a26e YI SYLLABLE HIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a271 YI SYLLABLE HAT | a270 YI SYLLABLE HIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a273 YI SYLLABLE HA | a272 YI SYLLABLE HAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a275 YI SYLLABLE HUOT | a274 YI SYLLABLE HAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a277 YI SYLLABLE HUO | a276 YI SYLLABLE HUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a279 YI SYLLABLE HOT | a278 YI SYLLABLE HUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27b YI SYLLABLE HO | a27a YI SYLLABLE HOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27d YI SYLLABLE HEX | a27c YI SYLLABLE HOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a27f YI SYLLABLE HEP | a27e YI SYLLABLE HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a281 YI SYLLABLE WAX | a280 YI SYLLABLE WAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a283 YI SYLLABLE WAP | a282 YI SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a285 YI SYLLABLE WUO | a284 YI SYLLABLE WUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a287 YI SYLLABLE WOX | a286 YI SYLLABLE WUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a289 YI SYLLABLE WOP | a288 YI SYLLABLE WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28b YI SYLLABLE WE | a28a YI SYLLABLE WEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28d YI SYLLABLE ZIT | a28c YI SYLLABLE WEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a28f YI SYLLABLE ZI | a28e YI SYLLABLE ZIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a291 YI SYLLABLE ZIEX | a290 YI SYLLABLE ZIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a293 YI SYLLABLE ZIEP | a292 YI SYLLABLE ZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a295 YI SYLLABLE ZAX | a294 YI SYLLABLE ZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a297 YI SYLLABLE ZAP | a296 YI SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a299 YI SYLLABLE ZUO | a298 YI SYLLABLE ZUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29b YI SYLLABLE ZOT | a29a YI SYLLABLE ZUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29d YI SYLLABLE ZO | a29c YI SYLLABLE ZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a29f YI SYLLABLE ZEX | a29e YI SYLLABLE ZOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a1 YI SYLLABLE ZEP | a2a0 YI SYLLABLE ZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a3 YI SYLLABLE ZUX | a2a2 YI SYLLABLE ZUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a5 YI SYLLABLE ZUP | a2a4 YI SYLLABLE ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a7 YI SYLLABLE ZUR | a2a6 YI SYLLABLE ZURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2a9 YI SYLLABLE ZYX | a2a8 YI SYLLABLE ZYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ab YI SYLLABLE ZYP | a2aa YI SYLLABLE ZY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ad YI SYLLABLE ZYR | a2ac YI SYLLABLE ZYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2af YI SYLLABLE CIX | a2ae YI SYLLABLE CIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b1 YI SYLLABLE CIP | a2b0 YI SYLLABLE CI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b3 YI SYLLABLE CIEX | a2b2 YI SYLLABLE CIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b5 YI SYLLABLE CIEP | a2b4 YI SYLLABLE CIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b7 YI SYLLABLE CAX | a2b6 YI SYLLABLE CAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2b9 YI SYLLABLE CAP | a2b8 YI SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bb YI SYLLABLE CUO | a2ba YI SYLLABLE CUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bd YI SYLLABLE COT | a2bc YI SYLLABLE CUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2bf YI SYLLABLE CO | a2be YI SYLLABLE COX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c1 YI SYLLABLE CEX | a2c0 YI SYLLABLE COP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c3 YI SYLLABLE CEP | a2c2 YI SYLLABLE CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c5 YI SYLLABLE CUX | a2c4 YI SYLLABLE CUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c7 YI SYLLABLE CUP | a2c6 YI SYLLABLE CU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2c9 YI SYLLABLE CUR | a2c8 YI SYLLABLE CURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cb YI SYLLABLE CYX | a2ca YI SYLLABLE CYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cd YI SYLLABLE CYP | a2cc YI SYLLABLE CY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2cf YI SYLLABLE CYR | a2ce YI SYLLABLE CYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d1 YI SYLLABLE ZZIX | a2d0 YI SYLLABLE ZZIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d3 YI SYLLABLE ZZIP | a2d2 YI SYLLABLE ZZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d5 YI SYLLABLE ZZIEX | a2d4 YI SYLLABLE ZZIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d7 YI SYLLABLE ZZIEP | a2d6 YI SYLLABLE ZZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2d9 YI SYLLABLE ZZAX | a2d8 YI SYLLABLE ZZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2db YI SYLLABLE ZZAP | a2da YI SYLLABLE ZZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2dd YI SYLLABLE ZZO | a2dc YI SYLLABLE ZZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2df YI SYLLABLE ZZEX | a2de YI SYLLABLE ZZOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e1 YI SYLLABLE ZZEP | a2e0 YI SYLLABLE ZZE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e3 YI SYLLABLE ZZU | a2e2 YI SYLLABLE ZZUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e5 YI SYLLABLE ZZURX | a2e4 YI SYLLABLE ZZUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e7 YI SYLLABLE ZZYT | a2e6 YI SYLLABLE ZZUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2e9 YI SYLLABLE ZZY | a2e8 YI SYLLABLE ZZYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2eb YI SYLLABLE ZZYRX | a2ea YI SYLLABLE ZZYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ed YI SYLLABLE NZIT | a2ec YI SYLLABLE ZZYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ef YI SYLLABLE NZI | a2ee YI SYLLABLE NZIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f1 YI SYLLABLE NZIEX | a2f0 YI SYLLABLE NZIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f3 YI SYLLABLE NZIEP | a2f2 YI SYLLABLE NZIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f5 YI SYLLABLE NZAX | a2f4 YI SYLLABLE NZAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f7 YI SYLLABLE NZAP | a2f6 YI SYLLABLE NZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2f9 YI SYLLABLE NZUO | a2f8 YI SYLLABLE NZUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2fb YI SYLLABLE NZOP | a2fa YI SYLLABLE NZOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2fd YI SYLLABLE NZE | a2fc YI SYLLABLE NZEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a2ff YI SYLLABLE NZU | a2fe YI SYLLABLE NZUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a301 YI SYLLABLE NZURX | a300 YI SYLLABLE NZUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a303 YI SYLLABLE NZYT | a302 YI SYLLABLE NZUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a305 YI SYLLABLE NZY | a304 YI SYLLABLE NZYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a307 YI SYLLABLE NZYRX | a306 YI SYLLABLE NZYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a309 YI SYLLABLE SIT | a308 YI SYLLABLE NZYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30b YI SYLLABLE SI | a30a YI SYLLABLE SIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30d YI SYLLABLE SIEX | a30c YI SYLLABLE SIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a30f YI SYLLABLE SIEP | a30e YI SYLLABLE SIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a311 YI SYLLABLE SAX | a310 YI SYLLABLE SAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a313 YI SYLLABLE SAP | a312 YI SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a315 YI SYLLABLE SUO | a314 YI SYLLABLE SUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a317 YI SYLLABLE SOT | a316 YI SYLLABLE SUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a319 YI SYLLABLE SO | a318 YI SYLLABLE SOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31b YI SYLLABLE SEX | a31a YI SYLLABLE SOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31d YI SYLLABLE SEP | a31c YI SYLLABLE SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a31f YI SYLLABLE SUX | a31e YI SYLLABLE SUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a321 YI SYLLABLE SUP | a320 YI SYLLABLE SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a323 YI SYLLABLE SUR | a322 YI SYLLABLE SURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a325 YI SYLLABLE SYX | a324 YI SYLLABLE SYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a327 YI SYLLABLE SYP | a326 YI SYLLABLE SY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a329 YI SYLLABLE SYR | a328 YI SYLLABLE SYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32b YI SYLLABLE SSIX | a32a YI SYLLABLE SSIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32d YI SYLLABLE SSIP | a32c YI SYLLABLE SSI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a32f YI SYLLABLE SSIE | a32e YI SYLLABLE SSIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a331 YI SYLLABLE SSAT | a330 YI SYLLABLE SSIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a333 YI SYLLABLE SSA | a332 YI SYLLABLE SSAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a335 YI SYLLABLE SSOT | a334 YI SYLLABLE SSAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a337 YI SYLLABLE SSO | a336 YI SYLLABLE SSOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a339 YI SYLLABLE SSEX | a338 YI SYLLABLE SSOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33b YI SYLLABLE SSEP | a33a YI SYLLABLE SSE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33d YI SYLLABLE SSUX | a33c YI SYLLABLE SSUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a33f YI SYLLABLE SSUP | a33e YI SYLLABLE SSU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a341 YI SYLLABLE SSYX | a340 YI SYLLABLE SSYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a343 YI SYLLABLE SSYP | a342 YI SYLLABLE SSY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a345 YI SYLLABLE SSYR | a344 YI SYLLABLE SSYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a347 YI SYLLABLE ZHAX | a346 YI SYLLABLE ZHAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a349 YI SYLLABLE ZHAP | a348 YI SYLLABLE ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34b YI SYLLABLE ZHUO | a34a YI SYLLABLE ZHUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34d YI SYLLABLE ZHOT | a34c YI SYLLABLE ZHUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a34f YI SYLLABLE ZHO | a34e YI SYLLABLE ZHOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a351 YI SYLLABLE ZHET | a350 YI SYLLABLE ZHOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a353 YI SYLLABLE ZHE | a352 YI SYLLABLE ZHEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a355 YI SYLLABLE ZHUT | a354 YI SYLLABLE ZHEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a357 YI SYLLABLE ZHU | a356 YI SYLLABLE ZHUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a359 YI SYLLABLE ZHURX | a358 YI SYLLABLE ZHUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35b YI SYLLABLE ZHYT | a35a YI SYLLABLE ZHUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35d YI SYLLABLE ZHY | a35c YI SYLLABLE ZHYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a35f YI SYLLABLE ZHYRX | a35e YI SYLLABLE ZHYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a361 YI SYLLABLE CHAT | a360 YI SYLLABLE ZHYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a363 YI SYLLABLE CHA | a362 YI SYLLABLE CHAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a365 YI SYLLABLE CHUOT | a364 YI SYLLABLE CHAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a367 YI SYLLABLE CHUO | a366 YI SYLLABLE CHUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a369 YI SYLLABLE CHOT | a368 YI SYLLABLE CHUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36b YI SYLLABLE CHO | a36a YI SYLLABLE CHOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36d YI SYLLABLE CHET | a36c YI SYLLABLE CHOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a36f YI SYLLABLE CHE | a36e YI SYLLABLE CHEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a371 YI SYLLABLE CHUX | a370 YI SYLLABLE CHEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a373 YI SYLLABLE CHUP | a372 YI SYLLABLE CHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a375 YI SYLLABLE CHUR | a374 YI SYLLABLE CHURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a377 YI SYLLABLE CHYX | a376 YI SYLLABLE CHYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a379 YI SYLLABLE CHYP | a378 YI SYLLABLE CHY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37b YI SYLLABLE CHYR | a37a YI SYLLABLE CHYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37d YI SYLLABLE RRA | a37c YI SYLLABLE RRAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a37f YI SYLLABLE RRUO | a37e YI SYLLABLE RRUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a381 YI SYLLABLE RROX | a380 YI SYLLABLE RROT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a383 YI SYLLABLE RROP | a382 YI SYLLABLE RRO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a385 YI SYLLABLE RREX | a384 YI SYLLABLE RRET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a387 YI SYLLABLE RREP | a386 YI SYLLABLE RRE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a389 YI SYLLABLE RRUX | a388 YI SYLLABLE RRUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38b YI SYLLABLE RRUP | a38a YI SYLLABLE RRU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38d YI SYLLABLE RRUR | a38c YI SYLLABLE RRURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a38f YI SYLLABLE RRYX | a38e YI SYLLABLE RRYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a391 YI SYLLABLE RRYP | a390 YI SYLLABLE RRY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a393 YI SYLLABLE RRYR | a392 YI SYLLABLE RRYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a395 YI SYLLABLE NRAX | a394 YI SYLLABLE NRAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a397 YI SYLLABLE NRAP | a396 YI SYLLABLE NRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a399 YI SYLLABLE NRO | a398 YI SYLLABLE NROX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39b YI SYLLABLE NRET | a39a YI SYLLABLE NROP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39d YI SYLLABLE NRE | a39c YI SYLLABLE NREX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a39f YI SYLLABLE NRUT | a39e YI SYLLABLE NREP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a1 YI SYLLABLE NRU | a3a0 YI SYLLABLE NRUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a3 YI SYLLABLE NRURX | a3a2 YI SYLLABLE NRUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a5 YI SYLLABLE NRYT | a3a4 YI SYLLABLE NRUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a7 YI SYLLABLE NRY | a3a6 YI SYLLABLE NRYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3a9 YI SYLLABLE NRYRX | a3a8 YI SYLLABLE NRYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ab YI SYLLABLE SHAT | a3aa YI SYLLABLE NRYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ad YI SYLLABLE SHA | a3ac YI SYLLABLE SHAX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3af YI SYLLABLE SHUOX | a3ae YI SYLLABLE SHAP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b1 YI SYLLABLE SHUOP | a3b0 YI SYLLABLE SHUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b3 YI SYLLABLE SHOX | a3b2 YI SYLLABLE SHOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b5 YI SYLLABLE SHOP | a3b4 YI SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b7 YI SYLLABLE SHEX | a3b6 YI SYLLABLE SHET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3b9 YI SYLLABLE SHEP | a3b8 YI SYLLABLE SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bb YI SYLLABLE SHUX | a3ba YI SYLLABLE SHUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bd YI SYLLABLE SHUP | a3bc YI SYLLABLE SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3bf YI SYLLABLE SHUR | a3be YI SYLLABLE SHURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c1 YI SYLLABLE SHYX | a3c0 YI SYLLABLE SHYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c3 YI SYLLABLE SHYP | a3c2 YI SYLLABLE SHY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c5 YI SYLLABLE SHYR | a3c4 YI SYLLABLE SHYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c7 YI SYLLABLE RAX | a3c6 YI SYLLABLE RAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3c9 YI SYLLABLE RAP | a3c8 YI SYLLABLE RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cb YI SYLLABLE RUO | a3ca YI SYLLABLE RUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cd YI SYLLABLE ROT | a3cc YI SYLLABLE RUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3cf YI SYLLABLE RO | a3ce YI SYLLABLE ROX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d1 YI SYLLABLE REX | a3d0 YI SYLLABLE ROP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d3 YI SYLLABLE REP | a3d2 YI SYLLABLE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d5 YI SYLLABLE RUX | a3d4 YI SYLLABLE RUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d7 YI SYLLABLE RUP | a3d6 YI SYLLABLE RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3d9 YI SYLLABLE RUR | a3d8 YI SYLLABLE RURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3db YI SYLLABLE RYX | a3da YI SYLLABLE RYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3dd YI SYLLABLE RYP | a3dc YI SYLLABLE RY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3df YI SYLLABLE RYR | a3de YI SYLLABLE RYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e1 YI SYLLABLE JIX | a3e0 YI SYLLABLE JIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e3 YI SYLLABLE JIP | a3e2 YI SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e5 YI SYLLABLE JIEX | a3e4 YI SYLLABLE JIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e7 YI SYLLABLE JIEP | a3e6 YI SYLLABLE JIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3e9 YI SYLLABLE JUOX | a3e8 YI SYLLABLE JUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3eb YI SYLLABLE JUOP | a3ea YI SYLLABLE JUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ed YI SYLLABLE JOX | a3ec YI SYLLABLE JOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ef YI SYLLABLE JOP | a3ee YI SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f1 YI SYLLABLE JUX | a3f0 YI SYLLABLE JUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f3 YI SYLLABLE JUP | a3f2 YI SYLLABLE JU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f5 YI SYLLABLE JUR | a3f4 YI SYLLABLE JURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f7 YI SYLLABLE JYX | a3f6 YI SYLLABLE JYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3f9 YI SYLLABLE JYP | a3f8 YI SYLLABLE JY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3fb YI SYLLABLE JYR | a3fa YI SYLLABLE JYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3fd YI SYLLABLE QIX | a3fc YI SYLLABLE QIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a3ff YI SYLLABLE QIP | a3fe YI SYLLABLE QI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a401 YI SYLLABLE QIEX | a400 YI SYLLABLE QIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a403 YI SYLLABLE QIEP | a402 YI SYLLABLE QIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a405 YI SYLLABLE QUOX | a404 YI SYLLABLE QUOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a407 YI SYLLABLE QUOP | a406 YI SYLLABLE QUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a409 YI SYLLABLE QOX | a408 YI SYLLABLE QOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40b YI SYLLABLE QOP | a40a YI SYLLABLE QO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40d YI SYLLABLE QUX | a40c YI SYLLABLE QUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a40f YI SYLLABLE QUP | a40e YI SYLLABLE QU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a411 YI SYLLABLE QUR | a410 YI SYLLABLE QURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a413 YI SYLLABLE QYX | a412 YI SYLLABLE QYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a415 YI SYLLABLE QYP | a414 YI SYLLABLE QY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a417 YI SYLLABLE QYR | a416 YI SYLLABLE QYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a419 YI SYLLABLE JJIX | a418 YI SYLLABLE JJIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41b YI SYLLABLE JJIP | a41a YI SYLLABLE JJI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41d YI SYLLABLE JJIEX | a41c YI SYLLABLE JJIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a41f YI SYLLABLE JJIEP | a41e YI SYLLABLE JJIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a421 YI SYLLABLE JJUO | a420 YI SYLLABLE JJUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a423 YI SYLLABLE JJOT | a422 YI SYLLABLE JJUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a425 YI SYLLABLE JJO | a424 YI SYLLABLE JJOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a427 YI SYLLABLE JJUT | a426 YI SYLLABLE JJOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a429 YI SYLLABLE JJU | a428 YI SYLLABLE JJUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42b YI SYLLABLE JJURX | a42a YI SYLLABLE JJUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42d YI SYLLABLE JJYT | a42c YI SYLLABLE JJUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a42f YI SYLLABLE JJY | a42e YI SYLLABLE JJYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a431 YI SYLLABLE NJIT | a430 YI SYLLABLE JJYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a433 YI SYLLABLE NJI | a432 YI SYLLABLE NJIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a435 YI SYLLABLE NJIET | a434 YI SYLLABLE NJIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a437 YI SYLLABLE NJIE | a436 YI SYLLABLE NJIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a439 YI SYLLABLE NJUOX | a438 YI SYLLABLE NJIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43b YI SYLLABLE NJOT | a43a YI SYLLABLE NJUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43d YI SYLLABLE NJO | a43c YI SYLLABLE NJOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a43f YI SYLLABLE NJUX | a43e YI SYLLABLE NJOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a441 YI SYLLABLE NJUP | a440 YI SYLLABLE NJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a443 YI SYLLABLE NJUR | a442 YI SYLLABLE NJURX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a445 YI SYLLABLE NJYX | a444 YI SYLLABLE NJYT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a447 YI SYLLABLE NJYP | a446 YI SYLLABLE NJY */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a449 YI SYLLABLE NJYR | a448 YI SYLLABLE NJYRX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44b YI SYLLABLE NYIX | a44a YI SYLLABLE NYIT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44d YI SYLLABLE NYIP | a44c YI SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a44f YI SYLLABLE NYIEX | a44e YI SYLLABLE NYIET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a451 YI SYLLABLE NYIEP | a450 YI SYLLABLE NYIE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a453 YI SYLLABLE NYUO | a452 YI SYLLABLE NYUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a455 YI SYLLABLE NYOT | a454 YI SYLLABLE NYUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a457 YI SYLLABLE NYO | a456 YI SYLLABLE NYOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a459 YI SYLLABLE NYUT | a458 YI SYLLABLE NYOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45b YI SYLLABLE NYU | a45a YI SYLLABLE NYUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45d YI SYLLABLE XIT | a45c YI SYLLABLE NYUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a45f YI SYLLABLE XI | a45e YI SYLLABLE XIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a461 YI SYLLABLE XIET | a460 YI SYLLABLE XIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a463 YI SYLLABLE XIE | a462 YI SYLLABLE XIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a465 YI SYLLABLE XUOX | a464 YI SYLLABLE XIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a467 YI SYLLABLE XOT | a466 YI SYLLABLE XUO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a469 YI SYLLABLE XO | a468 YI SYLLABLE XOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46b YI SYLLABLE XYT | a46a YI SYLLABLE XOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46d YI SYLLABLE XY | a46c YI SYLLABLE XYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a46f YI SYLLABLE XYRX | a46e YI SYLLABLE XYP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a471 YI SYLLABLE YIT | a470 YI SYLLABLE XYR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a473 YI SYLLABLE YI | a472 YI SYLLABLE YIX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a475 YI SYLLABLE YIET | a474 YI SYLLABLE YIP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a477 YI SYLLABLE YIE | a476 YI SYLLABLE YIEX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a479 YI SYLLABLE YUOT | a478 YI SYLLABLE YIEP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47b YI SYLLABLE YUO | a47a YI SYLLABLE YUOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47d YI SYLLABLE YOT | a47c YI SYLLABLE YUOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a47f YI SYLLABLE YO | a47e YI SYLLABLE YOX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a481 YI SYLLABLE YUT | a480 YI SYLLABLE YOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a483 YI SYLLABLE YU | a482 YI SYLLABLE YUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a485 YI SYLLABLE YURX | a484 YI SYLLABLE YUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a487 YI SYLLABLE YYT | a486 YI SYLLABLE YUR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a489 YI SYLLABLE YY | a488 YI SYLLABLE YYX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a48b YI SYLLABLE YYRX | a48a YI SYLLABLE YYP */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* a48d (null) | a48c YI SYLLABLE YYR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a48f (null) | a48e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a491 YI RADICAL LI | a490 YI RADICAL QOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a493 YI RADICAL NYIP | a492 YI RADICAL KIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a495 YI RADICAL SSI | a494 YI RADICAL CYP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a497 YI RADICAL GEP | a496 YI RADICAL GGOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a499 YI RADICAL HXIT | a498 YI RADICAL MI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49b YI RADICAL BBUT | a49a YI RADICAL LYR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49d YI RADICAL YO | a49c YI RADICAL MOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a49f YI RADICAL HXUO | a49e YI RADICAL PUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a1 YI RADICAL GA | a4a0 YI RADICAL TAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a3 YI RADICAL CYT | a4a2 YI RADICAL ZUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a5 YI RADICAL BUR | a4a4 YI RADICAL DDUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a7 YI RADICAL NYOP | a4a6 YI RADICAL GGUO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4a9 YI RADICAL OP | a4a8 YI RADICAL TU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4ab YI RADICAL ZOT | a4aa YI RADICAL JJUT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4ad YI RADICAL HMO | a4ac YI RADICAL PYT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4af YI RADICAL VUR | a4ae YI RADICAL YIT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b1 YI RADICAL VEP | a4b0 YI RADICAL SHY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b3 YI RADICAL JO | a4b2 YI RADICAL ZA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b5 YI RADICAL JJY | a4b4 YI RADICAL NZUP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b7 YI RADICAL JJIE | a4b6 YI RADICAL GOT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4b9 YI RADICAL DU | a4b8 YI RADICAL WO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bb YI RADICAL LIE | a4ba YI RADICAL SHUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bd YI RADICAL CUOP | a4bc YI RADICAL CY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4bf YI RADICAL HXOP | a4be YI RADICAL CIP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c1 YI RADICAL ZUR | a4c0 YI RADICAL SHAT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c3 YI RADICAL CHE | a4c2 YI RADICAL SHOP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4c5 YI RADICAL NBIE | a4c4 YI RADICAL ZZIET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* a4c7 (null) | a4c6 YI RADICAL KE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4c9 (null) | a4c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cb (null) | a4ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cd (null) | a4cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a4cf (null) | a4ce (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d1 LISU LETTER PA | a4d0 LISU LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d3 LISU LETTER DA | a4d2 LISU LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d5 LISU LETTER THA | a4d4 LISU LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d7 LISU LETTER KA | a4d6 LISU LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4d9 LISU LETTER JA | a4d8 LISU LETTER KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4db LISU LETTER CHA | a4da LISU LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4dd LISU LETTER TSA | a4dc LISU LETTER DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4df LISU LETTER MA | a4de LISU LETTER TSHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e1 LISU LETTER LA | a4e0 LISU LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e3 LISU LETTER ZHA | a4e2 LISU LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e5 LISU LETTER NGA | a4e4 LISU LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e7 LISU LETTER XA | a4e6 LISU LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4e9 LISU LETTER FA | a4e8 LISU LETTER HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4eb LISU LETTER SHA | a4ea LISU LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4ed LISU LETTER GHA | a4ec LISU LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4ef LISU LETTER AE | a4ee LISU LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f1 LISU LETTER EU | a4f0 LISU LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f3 LISU LETTER O | a4f2 LISU LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f5 LISU LETTER UE | a4f4 LISU LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a4f7 LISU LETTER OE | a4f6 LISU LETTER UH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4f9 LISU LETTER TONE NA PO | a4f8 LISU LETTER TONE MYA TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4fb LISU LETTER TONE MYA BO | a4fa LISU LETTER TONE MYA CYA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a4fd LISU LETTER TONE MYA JEU | a4fc LISU LETTER TONE MYA NA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a4ff LISU PUNCTUATION FULL STOP | a4fe LISU PUNCTUATION COMMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a501 VAI SYLLABLE EEN | a500 VAI SYLLABLE EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a503 VAI SYLLABLE WEE | a502 VAI SYLLABLE HEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a505 VAI SYLLABLE PEE | a504 VAI SYLLABLE WEEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a507 VAI SYLLABLE BEE | a506 VAI SYLLABLE BHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a509 VAI SYLLABLE KPEE | a508 VAI SYLLABLE MBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50b VAI SYLLABLE GBEE | a50a VAI SYLLABLE MGBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50d VAI SYLLABLE VEE | a50c VAI SYLLABLE FEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a50f VAI SYLLABLE THEE | a50e VAI SYLLABLE TEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a511 VAI SYLLABLE DHHEE | a510 VAI SYLLABLE DHEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a513 VAI SYLLABLE REE | a512 VAI SYLLABLE LEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a515 VAI SYLLABLE NDEE | a514 VAI SYLLABLE DEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a517 VAI SYLLABLE SHEE | a516 VAI SYLLABLE SEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a519 VAI SYLLABLE ZHEE | a518 VAI SYLLABLE ZEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51b VAI SYLLABLE JEE | a51a VAI SYLLABLE CEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51d VAI SYLLABLE YEE | a51c VAI SYLLABLE NJEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a51f VAI SYLLABLE NGGEE | a51e VAI SYLLABLE KEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a521 VAI SYLLABLE MEE | a520 VAI SYLLABLE GEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a523 VAI SYLLABLE NYEE | a522 VAI SYLLABLE NEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a525 VAI SYLLABLE IN | a524 VAI SYLLABLE I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a527 VAI SYLLABLE HIN | a526 VAI SYLLABLE HI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a529 VAI SYLLABLE WIN | a528 VAI SYLLABLE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52b VAI SYLLABLE BHI | a52a VAI SYLLABLE PI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52d VAI SYLLABLE MBI | a52c VAI SYLLABLE BI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a52f VAI SYLLABLE MGBI | a52e VAI SYLLABLE KPI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a531 VAI SYLLABLE FI | a530 VAI SYLLABLE GBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a533 VAI SYLLABLE TI | a532 VAI SYLLABLE VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a535 VAI SYLLABLE DHI | a534 VAI SYLLABLE THI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a537 VAI SYLLABLE LI | a536 VAI SYLLABLE DHHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a539 VAI SYLLABLE DI | a538 VAI SYLLABLE RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53b VAI SYLLABLE SI | a53a VAI SYLLABLE NDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53d VAI SYLLABLE ZI | a53c VAI SYLLABLE SHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a53f VAI SYLLABLE CI | a53e VAI SYLLABLE ZHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a541 VAI SYLLABLE NJI | a540 VAI SYLLABLE JI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a543 VAI SYLLABLE KI | a542 VAI SYLLABLE YI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a545 VAI SYLLABLE GI | a544 VAI SYLLABLE NGGI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a547 VAI SYLLABLE NI | a546 VAI SYLLABLE MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a549 VAI SYLLABLE A | a548 VAI SYLLABLE NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54b VAI SYLLABLE NGAN | a54a VAI SYLLABLE AN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54d VAI SYLLABLE HAN | a54c VAI SYLLABLE HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a54f VAI SYLLABLE WAN | a54e VAI SYLLABLE WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a551 VAI SYLLABLE BHA | a550 VAI SYLLABLE PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a553 VAI SYLLABLE MBA | a552 VAI SYLLABLE BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a555 VAI SYLLABLE KPAN | a554 VAI SYLLABLE KPA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a557 VAI SYLLABLE GBA | a556 VAI SYLLABLE MGBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a559 VAI SYLLABLE VA | a558 VAI SYLLABLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55b VAI SYLLABLE THA | a55a VAI SYLLABLE TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55d VAI SYLLABLE DHHA | a55c VAI SYLLABLE DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a55f VAI SYLLABLE RA | a55e VAI SYLLABLE LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a561 VAI SYLLABLE NDA | a560 VAI SYLLABLE DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a563 VAI SYLLABLE SHA | a562 VAI SYLLABLE SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a565 VAI SYLLABLE ZHA | a564 VAI SYLLABLE ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a567 VAI SYLLABLE JA | a566 VAI SYLLABLE CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a569 VAI SYLLABLE YA | a568 VAI SYLLABLE NJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56b VAI SYLLABLE KAN | a56a VAI SYLLABLE KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56d VAI SYLLABLE GA | a56c VAI SYLLABLE NGGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a56f VAI SYLLABLE NA | a56e VAI SYLLABLE MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a571 VAI SYLLABLE OO | a570 VAI SYLLABLE NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a573 VAI SYLLABLE HOO | a572 VAI SYLLABLE OON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a575 VAI SYLLABLE WOON | a574 VAI SYLLABLE WOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a577 VAI SYLLABLE BHOO | a576 VAI SYLLABLE POO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a579 VAI SYLLABLE MBOO | a578 VAI SYLLABLE BOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57b VAI SYLLABLE MGBOO | a57a VAI SYLLABLE KPOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57d VAI SYLLABLE FOO | a57c VAI SYLLABLE GBOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a57f VAI SYLLABLE TOO | a57e VAI SYLLABLE VOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a581 VAI SYLLABLE DHOO | a580 VAI SYLLABLE THOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a583 VAI SYLLABLE LOO | a582 VAI SYLLABLE DHHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a585 VAI SYLLABLE DOO | a584 VAI SYLLABLE ROO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a587 VAI SYLLABLE SOO | a586 VAI SYLLABLE NDOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a589 VAI SYLLABLE ZOO | a588 VAI SYLLABLE SHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58b VAI SYLLABLE COO | a58a VAI SYLLABLE ZHOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58d VAI SYLLABLE NJOO | a58c VAI SYLLABLE JOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a58f VAI SYLLABLE KOO | a58e VAI SYLLABLE YOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a591 VAI SYLLABLE GOO | a590 VAI SYLLABLE NGGOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a593 VAI SYLLABLE NOO | a592 VAI SYLLABLE MOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a595 VAI SYLLABLE U | a594 VAI SYLLABLE NYOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a597 VAI SYLLABLE HU | a596 VAI SYLLABLE UN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a599 VAI SYLLABLE WU | a598 VAI SYLLABLE HUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59b VAI SYLLABLE PU | a59a VAI SYLLABLE WUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59d VAI SYLLABLE BU | a59c VAI SYLLABLE BHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a59f VAI SYLLABLE KPU | a59e VAI SYLLABLE MBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a1 VAI SYLLABLE GBU | a5a0 VAI SYLLABLE MGBU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a3 VAI SYLLABLE VU | a5a2 VAI SYLLABLE FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a5 VAI SYLLABLE THU | a5a4 VAI SYLLABLE TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a7 VAI SYLLABLE DHHU | a5a6 VAI SYLLABLE DHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5a9 VAI SYLLABLE RU | a5a8 VAI SYLLABLE LU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ab VAI SYLLABLE NDU | a5aa VAI SYLLABLE DU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ad VAI SYLLABLE SHU | a5ac VAI SYLLABLE SU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5af VAI SYLLABLE ZHU | a5ae VAI SYLLABLE ZU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b1 VAI SYLLABLE JU | a5b0 VAI SYLLABLE CU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b3 VAI SYLLABLE YU | a5b2 VAI SYLLABLE NJU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b5 VAI SYLLABLE NGGU | a5b4 VAI SYLLABLE KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b7 VAI SYLLABLE MU | a5b6 VAI SYLLABLE GU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5b9 VAI SYLLABLE NYU | a5b8 VAI SYLLABLE NU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bb VAI SYLLABLE ON | a5ba VAI SYLLABLE O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bd VAI SYLLABLE HO | a5bc VAI SYLLABLE NGON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5bf VAI SYLLABLE WO | a5be VAI SYLLABLE HON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c1 VAI SYLLABLE PO | a5c0 VAI SYLLABLE WON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c3 VAI SYLLABLE BO | a5c2 VAI SYLLABLE BHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c5 VAI SYLLABLE KPO | a5c4 VAI SYLLABLE MBO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c7 VAI SYLLABLE GBO | a5c6 VAI SYLLABLE MGBO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5c9 VAI SYLLABLE FO | a5c8 VAI SYLLABLE GBON */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cb VAI SYLLABLE TO | a5ca VAI SYLLABLE VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cd VAI SYLLABLE DHO | a5cc VAI SYLLABLE THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5cf VAI SYLLABLE LO | a5ce VAI SYLLABLE DHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d1 VAI SYLLABLE DO | a5d0 VAI SYLLABLE RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d3 VAI SYLLABLE SO | a5d2 VAI SYLLABLE NDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d5 VAI SYLLABLE ZO | a5d4 VAI SYLLABLE SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d7 VAI SYLLABLE CO | a5d6 VAI SYLLABLE ZHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5d9 VAI SYLLABLE NJO | a5d8 VAI SYLLABLE JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5db VAI SYLLABLE KO | a5da VAI SYLLABLE YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5dd VAI SYLLABLE GO | a5dc VAI SYLLABLE NGGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5df VAI SYLLABLE NO | a5de VAI SYLLABLE MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e1 VAI SYLLABLE E | a5e0 VAI SYLLABLE NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e3 VAI SYLLABLE NGEN | a5e2 VAI SYLLABLE EN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e5 VAI SYLLABLE HEN | a5e4 VAI SYLLABLE HE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e7 VAI SYLLABLE WEN | a5e6 VAI SYLLABLE WE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5e9 VAI SYLLABLE BHE | a5e8 VAI SYLLABLE PE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5eb VAI SYLLABLE MBE | a5ea VAI SYLLABLE BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ed VAI SYLLABLE KPEN | a5ec VAI SYLLABLE KPE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ef VAI SYLLABLE GBE | a5ee VAI SYLLABLE MGBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f1 VAI SYLLABLE FE | a5f0 VAI SYLLABLE GBEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f3 VAI SYLLABLE TE | a5f2 VAI SYLLABLE VE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f5 VAI SYLLABLE DHE | a5f4 VAI SYLLABLE THE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f7 VAI SYLLABLE LE | a5f6 VAI SYLLABLE DHHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5f9 VAI SYLLABLE DE | a5f8 VAI SYLLABLE RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5fb VAI SYLLABLE SE | a5fa VAI SYLLABLE NDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5fd VAI SYLLABLE ZE | a5fc VAI SYLLABLE SHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a5ff VAI SYLLABLE CE | a5fe VAI SYLLABLE ZHE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a601 VAI SYLLABLE NJE | a600 VAI SYLLABLE JE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a603 VAI SYLLABLE KE | a602 VAI SYLLABLE YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a605 VAI SYLLABLE NGGEN | a604 VAI SYLLABLE NGGE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a607 VAI SYLLABLE GEN | a606 VAI SYLLABLE GE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a609 VAI SYLLABLE NE | a608 VAI SYLLABLE ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a60b VAI SYLLABLE NG | a60a VAI SYLLABLE NYE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a60d VAI COMMA | a60c VAI SYLLABLE LENGTHENER */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a60f VAI QUESTION MARK | a60e VAI FULL STOP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a611 VAI SYLLABLE NDOLE KA | a610 VAI SYLLABLE NDOLE FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a613 VAI SYMBOL FEENG | a612 VAI SYLLABLE NDOLE SOO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a615 VAI SYMBOL TING | a614 VAI SYMBOL KEENG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a617 VAI SYMBOL BANG | a616 VAI SYMBOL NII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a619 VAI SYMBOL TAA | a618 VAI SYMBOL FAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61b VAI SYMBOL DOONG | a61a VAI SYMBOL DANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61d VAI SYMBOL TONG | a61c VAI SYMBOL KUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a61f VAI SYMBOL JONG | a61e VAI SYMBOL DO-O */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a621 VAI DIGIT ONE | a620 VAI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a623 VAI DIGIT THREE | a622 VAI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a625 VAI DIGIT FIVE | a624 VAI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a627 VAI DIGIT SEVEN | a626 VAI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a629 VAI DIGIT NINE | a628 VAI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a62b VAI SYLLABLE NDOLE DO | a62a VAI SYLLABLE NDOLE MA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a62d (null) | a62c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a62f (null) | a62e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a631 (null) | a630 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a633 (null) | a632 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a635 (null) | a634 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a637 (null) | a636 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a639 (null) | a638 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63b (null) | a63a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63d (null) | a63c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a63f (null) | a63e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a641 CYRILLIC SMALL LETTER ZEMLYA | a640 CYRILLIC CAPITAL LETTER ZEMLYA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a643 CYRILLIC SMALL LETTER DZELO | a642 CYRILLIC CAPITAL LETTER DZELO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a645 CYRILLIC SMALL LETTER REVERSED DZE | a644 CYRILLIC CAPITAL LETTER REVERSED DZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a647 CYRILLIC SMALL LETTER IOTA | a646 CYRILLIC CAPITAL LETTER IOTA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a649 CYRILLIC SMALL LETTER DJERV | a648 CYRILLIC CAPITAL LETTER DJERV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64b CYRILLIC SMALL LETTER MONOGRAPH UK | a64a CYRILLIC CAPITAL LETTER MONOGRAPH UK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64d CYRILLIC SMALL LETTER BROAD OMEGA | a64c CYRILLIC CAPITAL LETTER BROAD OMEGA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a64f CYRILLIC SMALL LETTER NEUTRAL YER | a64e CYRILLIC CAPITAL LETTER NEUTRAL YER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a651 CYRILLIC SMALL LETTER YERU WITH BACK YE | a650 CYRILLIC CAPITAL LETTER YERU WITH BACK */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a653 CYRILLIC SMALL LETTER IOTIFIED YAT | a652 CYRILLIC CAPITAL LETTER IOTIFIED YAT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a655 CYRILLIC SMALL LETTER REVERSED YU | a654 CYRILLIC CAPITAL LETTER REVERSED YU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a657 CYRILLIC SMALL LETTER IOTIFIED A | a656 CYRILLIC CAPITAL LETTER IOTIFIED A */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a659 CYRILLIC SMALL LETTER CLOSED LITTLE YUS | a658 CYRILLIC CAPITAL LETTER CLOSED LITTLE Y */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65b CYRILLIC SMALL LETTER BLENDED YUS | a65a CYRILLIC CAPITAL LETTER BLENDED YUS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65d CYRILLIC SMALL LETTER IOTIFIED CLOSED L | a65c CYRILLIC CAPITAL LETTER IOTIFIED CLOSED */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a65f CYRILLIC SMALL LETTER YN | a65e CYRILLIC CAPITAL LETTER YN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a661 CYRILLIC SMALL LETTER REVERSED TSE | a660 CYRILLIC CAPITAL LETTER REVERSED TSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a663 CYRILLIC SMALL LETTER SOFT DE | a662 CYRILLIC CAPITAL LETTER SOFT DE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a665 CYRILLIC SMALL LETTER SOFT EL | a664 CYRILLIC CAPITAL LETTER SOFT EL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a667 CYRILLIC SMALL LETTER SOFT EM | a666 CYRILLIC CAPITAL LETTER SOFT EM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a669 CYRILLIC SMALL LETTER MONOCULAR O | a668 CYRILLIC CAPITAL LETTER MONOCULAR O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a66b CYRILLIC SMALL LETTER BINOCULAR O | a66a CYRILLIC CAPITAL LETTER BINOCULAR O */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a66d CYRILLIC SMALL LETTER DOUBLE MONOCULAR | a66c CYRILLIC CAPITAL LETTER DOUBLE MONOCULA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a66f COMBINING CYRILLIC VZMET | a66e CYRILLIC LETTER MULTIOCULAR O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a671 COMBINING CYRILLIC HUNDRED MILLIONS SIG | a670 COMBINING CYRILLIC TEN MILLIONS SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a673 SLAVONIC ASTERISK | a672 COMBINING CYRILLIC THOUSAND MILLIONS SI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a675 COMBINING CYRILLIC LETTER I | a674 COMBINING CYRILLIC LETTER UKRAINIAN IE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a677 COMBINING CYRILLIC LETTER U | a676 COMBINING CYRILLIC LETTER YI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a679 COMBINING CYRILLIC LETTER YERU | a678 COMBINING CYRILLIC LETTER HARD SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a67b COMBINING CYRILLIC LETTER OMEGA | a67a COMBINING CYRILLIC LETTER SOFT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a67d COMBINING CYRILLIC PAYEROK | a67c COMBINING CYRILLIC KAVYKA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* a67f CYRILLIC PAYEROK | a67e CYRILLIC KAVYKA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a681 CYRILLIC SMALL LETTER DWE | a680 CYRILLIC CAPITAL LETTER DWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a683 CYRILLIC SMALL LETTER DZWE | a682 CYRILLIC CAPITAL LETTER DZWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a685 CYRILLIC SMALL LETTER ZHWE | a684 CYRILLIC CAPITAL LETTER ZHWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a687 CYRILLIC SMALL LETTER CCHE | a686 CYRILLIC CAPITAL LETTER CCHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a689 CYRILLIC SMALL LETTER DZZE | a688 CYRILLIC CAPITAL LETTER DZZE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68b CYRILLIC SMALL LETTER TE WITH MIDDLE HO | a68a CYRILLIC CAPITAL LETTER TE WITH MIDDLE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68d CYRILLIC SMALL LETTER TWE | a68c CYRILLIC CAPITAL LETTER TWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a68f CYRILLIC SMALL LETTER TSWE | a68e CYRILLIC CAPITAL LETTER TSWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a691 CYRILLIC SMALL LETTER TSSE | a690 CYRILLIC CAPITAL LETTER TSSE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a693 CYRILLIC SMALL LETTER TCHE | a692 CYRILLIC CAPITAL LETTER TCHE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a695 CYRILLIC SMALL LETTER HWE | a694 CYRILLIC CAPITAL LETTER HWE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a697 CYRILLIC SMALL LETTER SHWE | a696 CYRILLIC CAPITAL LETTER SHWE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a699 (null) | a698 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a69b (null) | a69a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a69d (null) | a69c (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* a69f COMBINING CYRILLIC LETTER IOTIFIED E | a69e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a1 BAMUM LETTER KA | a6a0 BAMUM LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a3 BAMUM LETTER KU | a6a2 BAMUM LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a5 BAMUM LETTER REE | a6a4 BAMUM LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a7 BAMUM LETTER O | a6a6 BAMUM LETTER TAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6a9 BAMUM LETTER I | a6a8 BAMUM LETTER NYI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6ab BAMUM LETTER PA | a6aa BAMUM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6ad BAMUM LETTER RIEE | a6ac BAMUM LETTER RII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6af BAMUM LETTER MEEEE | a6ae BAMUM LETTER LEEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b1 BAMUM LETTER NDAA | a6b0 BAMUM LETTER TAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b3 BAMUM LETTER M | a6b2 BAMUM LETTER NJAEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b5 BAMUM LETTER MU | a6b4 BAMUM LETTER SUU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b7 BAMUM LETTER SI | a6b6 BAMUM LETTER SHII */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6b9 BAMUM LETTER SEUX | a6b8 BAMUM LETTER SHEUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bb BAMUM LETTER KET | a6ba BAMUM LETTER KYEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bd BAMUM LETTER NU | a6bc BAMUM LETTER NUAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6bf BAMUM LETTER YOQ | a6be BAMUM LETTER NJUAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c1 BAMUM LETTER YUQ | a6c0 BAMUM LETTER SHU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c3 BAMUM LETTER NSHA | a6c2 BAMUM LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c5 BAMUM LETTER PEUX | a6c4 BAMUM LETTER KEUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c7 BAMUM LETTER NTEE | a6c6 BAMUM LETTER NJEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6c9 BAMUM LETTER WUE | a6c8 BAMUM LETTER PUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cb BAMUM LETTER FEE | a6ca BAMUM LETTER PEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cd BAMUM LETTER LU | a6cc BAMUM LETTER RU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6cf BAMUM LETTER NI | a6ce BAMUM LETTER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d1 BAMUM LETTER RAE | a6d0 BAMUM LETTER REUX */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d3 BAMUM LETTER NGKWAEN | a6d2 BAMUM LETTER KEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d5 BAMUM LETTER NGA | a6d4 BAMUM LETTER NGGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d7 BAMUM LETTER PUAE | a6d6 BAMUM LETTER SHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6d9 BAMUM LETTER FOM | a6d8 BAMUM LETTER FU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6db BAMUM LETTER NA | a6da BAMUM LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6dd BAMUM LETTER PI | a6dc BAMUM LETTER LI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6df BAMUM LETTER KO | a6de BAMUM LETTER LOQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e1 BAMUM LETTER REN | a6e0 BAMUM LETTER MBEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e3 BAMUM LETTER MA | a6e2 BAMUM LETTER MEN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a6e5 BAMUM LETTER KI | a6e4 BAMUM LETTER TI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6e7 BAMUM LETTER MBAA | a6e6 BAMUM LETTER MO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6e9 BAMUM LETTER KPA | a6e8 BAMUM LETTER TET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6eb BAMUM LETTER NTUU | a6ea BAMUM LETTER TEN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6ed BAMUM LETTER FAAMAE | a6ec BAMUM LETTER SAMBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6ef BAMUM LETTER KOGHOM | a6ee BAMUM LETTER KOVUU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a6f1 BAMUM COMBINING MARK TUKWENTIS | a6f0 BAMUM COMBINING MARK KOQNDON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f3 BAMUM FULL STOP | a6f2 BAMUM NJAEMLI */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f5 BAMUM COMMA | a6f4 BAMUM COLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a6f7 BAMUM QUESTION MARK | a6f6 BAMUM SEMICOLON */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6f9 (null) | a6f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6fb (null) | a6fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6fd (null) | a6fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a6ff (null) | a6fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a701 MODIFIER LETTER CHINESE TONE YANG PING | a700 MODIFIER LETTER CHINESE TONE YIN PING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a703 MODIFIER LETTER CHINESE TONE YANG SHANG | a702 MODIFIER LETTER CHINESE TONE YIN SHANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a705 MODIFIER LETTER CHINESE TONE YANG QU | a704 MODIFIER LETTER CHINESE TONE YIN QU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a707 MODIFIER LETTER CHINESE TONE YANG RU | a706 MODIFIER LETTER CHINESE TONE YIN RU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a709 MODIFIER LETTER HIGH DOTTED TONE BAR | a708 MODIFIER LETTER EXTRA-HIGH DOTTED TONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70b MODIFIER LETTER LOW DOTTED TONE BAR | a70a MODIFIER LETTER MID DOTTED TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70d MODIFIER LETTER EXTRA-HIGH DOTTED LEFT- | a70c MODIFIER LETTER EXTRA-LOW DOTTED TONE B */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a70f MODIFIER LETTER MID DOTTED LEFT-STEM TO | a70e MODIFIER LETTER HIGH DOTTED LEFT-STEM T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a711 MODIFIER LETTER EXTRA-LOW DOTTED LEFT-S | a710 MODIFIER LETTER LOW DOTTED LEFT-STEM TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a713 MODIFIER LETTER HIGH LEFT-STEM TONE BAR | a712 MODIFIER LETTER EXTRA-HIGH LEFT-STEM TO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a715 MODIFIER LETTER LOW LEFT-STEM TONE BAR | a714 MODIFIER LETTER MID LEFT-STEM TONE BAR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a717 MODIFIER LETTER DOT VERTICAL BAR | a716 MODIFIER LETTER EXTRA-LOW LEFT-STEM TON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a719 MODIFIER LETTER DOT HORIZONTAL BAR | a718 MODIFIER LETTER DOT SLASH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71b MODIFIER LETTER RAISED UP ARROW | a71a MODIFIER LETTER LOWER RIGHT CORNER ANGL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71d MODIFIER LETTER RAISED EXCLAMATION MARK | a71c MODIFIER LETTER RAISED DOWN ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a71f MODIFIER LETTER LOW INVERTED EXCLAMATIO | a71e MODIFIER LETTER RAISED INVERTED EXCLAMA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a721 MODIFIER LETTER STRESS AND LOW TONE | a720 MODIFIER LETTER STRESS AND HIGH TONE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a723 LATIN SMALL LETTER EGYPTOLOGICAL ALEF | a722 LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a725 LATIN SMALL LETTER EGYPTOLOGICAL AIN | a724 LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a727 LATIN SMALL LETTER HENG | a726 LATIN CAPITAL LETTER HENG */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a729 LATIN SMALL LETTER TZ | a728 LATIN CAPITAL LETTER TZ */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72b LATIN SMALL LETTER TRESILLO | a72a LATIN CAPITAL LETTER TRESILLO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72d LATIN SMALL LETTER CUATRILLO | a72c LATIN CAPITAL LETTER CUATRILLO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a72f LATIN SMALL LETTER CUATRILLO WITH COMMA | a72e LATIN CAPITAL LETTER CUATRILLO WITH COM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a731 LATIN LETTER SMALL CAPITAL S | a730 LATIN LETTER SMALL CAPITAL F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a733 LATIN SMALL LETTER AA | a732 LATIN CAPITAL LETTER AA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a735 LATIN SMALL LETTER AO | a734 LATIN CAPITAL LETTER AO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a737 LATIN SMALL LETTER AU | a736 LATIN CAPITAL LETTER AU */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a739 LATIN SMALL LETTER AV | a738 LATIN CAPITAL LETTER AV */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73b LATIN SMALL LETTER AV WITH HORIZONTAL B | a73a LATIN CAPITAL LETTER AV WITH HORIZONTAL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73d LATIN SMALL LETTER AY | a73c LATIN CAPITAL LETTER AY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a73f LATIN SMALL LETTER REVERSED C WITH DOT | a73e LATIN CAPITAL LETTER REVERSED C WITH DO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a741 LATIN SMALL LETTER K WITH STROKE | a740 LATIN CAPITAL LETTER K WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a743 LATIN SMALL LETTER K WITH DIAGONAL STRO | a742 LATIN CAPITAL LETTER K WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a745 LATIN SMALL LETTER K WITH STROKE AND DI | a744 LATIN CAPITAL LETTER K WITH STROKE AND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a747 LATIN SMALL LETTER BROKEN L | a746 LATIN CAPITAL LETTER BROKEN L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a749 LATIN SMALL LETTER L WITH HIGH STROKE | a748 LATIN CAPITAL LETTER L WITH HIGH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74b LATIN SMALL LETTER O WITH LONG STROKE O | a74a LATIN CAPITAL LETTER O WITH LONG STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74d LATIN SMALL LETTER O WITH LOOP | a74c LATIN CAPITAL LETTER O WITH LOOP */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a74f LATIN SMALL LETTER OO | a74e LATIN CAPITAL LETTER OO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a751 LATIN SMALL LETTER P WITH STROKE THROUG | a750 LATIN CAPITAL LETTER P WITH STROKE THRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a753 LATIN SMALL LETTER P WITH FLOURISH | a752 LATIN CAPITAL LETTER P WITH FLOURISH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a755 LATIN SMALL LETTER P WITH SQUIRREL TAIL | a754 LATIN CAPITAL LETTER P WITH SQUIRREL TA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a757 LATIN SMALL LETTER Q WITH STROKE THROUG | a756 LATIN CAPITAL LETTER Q WITH STROKE THRO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a759 LATIN SMALL LETTER Q WITH DIAGONAL STRO | a758 LATIN CAPITAL LETTER Q WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75b LATIN SMALL LETTER R ROTUNDA | a75a LATIN CAPITAL LETTER R ROTUNDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75d LATIN SMALL LETTER RUM ROTUNDA | a75c LATIN CAPITAL LETTER RUM ROTUNDA */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a75f LATIN SMALL LETTER V WITH DIAGONAL STRO | a75e LATIN CAPITAL LETTER V WITH DIAGONAL ST */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a761 LATIN SMALL LETTER VY | a760 LATIN CAPITAL LETTER VY */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a763 LATIN SMALL LETTER VISIGOTHIC Z | a762 LATIN CAPITAL LETTER VISIGOTHIC Z */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a765 LATIN SMALL LETTER THORN WITH STROKE | a764 LATIN CAPITAL LETTER THORN WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a767 LATIN SMALL LETTER THORN WITH STROKE TH | a766 LATIN CAPITAL LETTER THORN WITH STROKE */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a769 LATIN SMALL LETTER VEND | a768 LATIN CAPITAL LETTER VEND */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76b LATIN SMALL LETTER ET | a76a LATIN CAPITAL LETTER ET */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76d LATIN SMALL LETTER IS | a76c LATIN CAPITAL LETTER IS */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a76f LATIN SMALL LETTER CON | a76e LATIN CAPITAL LETTER CON */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* a771 LATIN SMALL LETTER DUM | a770 MODIFIER LETTER US */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a773 LATIN SMALL LETTER MUM | a772 LATIN SMALL LETTER LUM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a775 LATIN SMALL LETTER RUM | a774 LATIN SMALL LETTER NUM */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* a777 LATIN SMALL LETTER TUM | a776 LATIN LETTER SMALL CAPITAL RUM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a779 LATIN CAPITAL LETTER INSULAR D | a778 LATIN SMALL LETTER UM */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a77b LATIN CAPITAL LETTER INSULAR F | a77a LATIN SMALL LETTER INSULAR D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a77d LATIN CAPITAL LETTER INSULAR G | a77c LATIN SMALL LETTER INSULAR F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a77f LATIN SMALL LETTER TURNED INSULAR G | a77e LATIN CAPITAL LETTER TURNED INSULAR G */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a781 LATIN SMALL LETTER TURNED L | a780 LATIN CAPITAL LETTER TURNED L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a783 LATIN SMALL LETTER INSULAR R | a782 LATIN CAPITAL LETTER INSULAR R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a785 LATIN SMALL LETTER INSULAR S | a784 LATIN CAPITAL LETTER INSULAR S */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a787 LATIN SMALL LETTER INSULAR T | a786 LATIN CAPITAL LETTER INSULAR T */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a789 MODIFIER LETTER COLON | a788 MODIFIER LETTER LOW CIRCUMFLEX ACCENT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_OTHER, /* a78b LATIN CAPITAL LETTER SALTILLO | a78a MODIFIER LETTER SHORT EQUALS SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_LOWER, /* a78d LATIN CAPITAL LETTER TURNED H | a78c LATIN SMALL LETTER SALTILLO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* a78f (null) | a78e LATIN SMALL LETTER L WITH RETROFLEX HOO */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a791 LATIN SMALL LETTER N WITH DESCENDER | a790 LATIN CAPITAL LETTER N WITH DESCENDER */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a793 LATIN SMALL LETTER C WITH BAR | a792 LATIN CAPITAL LETTER C WITH BAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a795 (null) | a794 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a797 (null) | a796 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a799 (null) | a798 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79b (null) | a79a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79d (null) | a79c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a79f (null) | a79e (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a1 LATIN SMALL LETTER G WITH OBLIQUE STROK | a7a0 LATIN CAPITAL LETTER G WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a3 LATIN SMALL LETTER K WITH OBLIQUE STROK | a7a2 LATIN CAPITAL LETTER K WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a5 LATIN SMALL LETTER N WITH OBLIQUE STROK | a7a4 LATIN CAPITAL LETTER N WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a7 LATIN SMALL LETTER R WITH OBLIQUE STROK | a7a6 LATIN CAPITAL LETTER R WITH OBLIQUE STR */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UPPER, /* a7a9 LATIN SMALL LETTER S WITH OBLIQUE STROK | a7a8 LATIN CAPITAL LETTER S WITH OBLIQUE STR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UPPER, /* a7ab (null) | a7aa LATIN CAPITAL LETTER H WITH HOOK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ad (null) | a7ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7af (null) | a7ae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b1 (null) | a7b0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b3 (null) | a7b2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b5 (null) | a7b4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b7 (null) | a7b6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7b9 (null) | a7b8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bb (null) | a7ba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bd (null) | a7bc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7bf (null) | a7be (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c1 (null) | a7c0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c3 (null) | a7c2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c5 (null) | a7c4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c7 (null) | a7c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7c9 (null) | a7c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cb (null) | a7ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cd (null) | a7cc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7cf (null) | a7ce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d1 (null) | a7d0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d3 (null) | a7d2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d5 (null) | a7d4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d7 (null) | a7d6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7d9 (null) | a7d8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7db (null) | a7da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7dd (null) | a7dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7df (null) | a7de (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e1 (null) | a7e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e3 (null) | a7e2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e5 (null) | a7e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e7 (null) | a7e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7e9 (null) | a7e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7eb (null) | a7ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ed (null) | a7ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7ef (null) | a7ee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f1 (null) | a7f0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f3 (null) | a7f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f5 (null) | a7f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a7f7 (null) | a7f6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a7f9 MODIFIER LETTER SMALL LIGATURE OE | a7f8 MODIFIER LETTER CAPITAL H WITH STROKE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_LOWER, /* a7fb LATIN EPIGRAPHIC LETTER REVERSED F | a7fa LATIN LETTER SMALL CAPITAL TURNED M */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a7fd LATIN EPIGRAPHIC LETTER INVERTED M | a7fc LATIN EPIGRAPHIC LETTER REVERSED P */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a7ff LATIN EPIGRAPHIC LETTER ARCHAIC M | a7fe LATIN EPIGRAPHIC LETTER I LONGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a801 SYLOTI NAGRI LETTER I | a800 SYLOTI NAGRI LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* a803 SYLOTI NAGRI LETTER U | a802 SYLOTI NAGRI SIGN DVISVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a805 SYLOTI NAGRI LETTER O | a804 SYLOTI NAGRI LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* a807 SYLOTI NAGRI LETTER KO | a806 SYLOTI NAGRI SIGN HASANTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a809 SYLOTI NAGRI LETTER GO | a808 SYLOTI NAGRI LETTER KHO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a80b SYLOTI NAGRI SIGN ANUSVARA | a80a SYLOTI NAGRI LETTER GHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a80d SYLOTI NAGRI LETTER CHO | a80c SYLOTI NAGRI LETTER CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a80f SYLOTI NAGRI LETTER JHO | a80e SYLOTI NAGRI LETTER JO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a811 SYLOTI NAGRI LETTER TTHO | a810 SYLOTI NAGRI LETTER TTO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a813 SYLOTI NAGRI LETTER DDHO | a812 SYLOTI NAGRI LETTER DDO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a815 SYLOTI NAGRI LETTER THO | a814 SYLOTI NAGRI LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a817 SYLOTI NAGRI LETTER DHO | a816 SYLOTI NAGRI LETTER DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a819 SYLOTI NAGRI LETTER PO | a818 SYLOTI NAGRI LETTER NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81b SYLOTI NAGRI LETTER BO | a81a SYLOTI NAGRI LETTER PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81d SYLOTI NAGRI LETTER MO | a81c SYLOTI NAGRI LETTER BHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a81f SYLOTI NAGRI LETTER LO | a81e SYLOTI NAGRI LETTER RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a821 SYLOTI NAGRI LETTER SO | a820 SYLOTI NAGRI LETTER RRO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a823 SYLOTI NAGRI VOWEL SIGN A | a822 SYLOTI NAGRI LETTER HO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a825 SYLOTI NAGRI VOWEL SIGN U | a824 SYLOTI NAGRI VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a827 SYLOTI NAGRI VOWEL SIGN OO | a826 SYLOTI NAGRI VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a829 SYLOTI NAGRI POETRY MARK-2 | a828 SYLOTI NAGRI POETRY MARK-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a82b SYLOTI NAGRI POETRY MARK-4 | a82a SYLOTI NAGRI POETRY MARK-3 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a82d (null) | a82c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a82f (null) | a82e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a831 NORTH INDIC FRACTION ONE HALF | a830 NORTH INDIC FRACTION ONE QUARTER */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a833 NORTH INDIC FRACTION ONE SIXTEENTH | a832 NORTH INDIC FRACTION THREE QUARTERS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a835 NORTH INDIC FRACTION THREE SIXTEENTHS | a834 NORTH INDIC FRACTION ONE EIGHTH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a837 NORTH INDIC PLACEHOLDER MARK | a836 NORTH INDIC QUARTER MARK */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a839 NORTH INDIC QUANTITY MARK | a838 NORTH INDIC RUPEE MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83b (null) | a83a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83d (null) | a83c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a83f (null) | a83e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a841 PHAGS-PA LETTER KHA | a840 PHAGS-PA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a843 PHAGS-PA LETTER NGA | a842 PHAGS-PA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a845 PHAGS-PA LETTER CHA | a844 PHAGS-PA LETTER CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a847 PHAGS-PA LETTER NYA | a846 PHAGS-PA LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a849 PHAGS-PA LETTER THA | a848 PHAGS-PA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84b PHAGS-PA LETTER NA | a84a PHAGS-PA LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84d PHAGS-PA LETTER PHA | a84c PHAGS-PA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a84f PHAGS-PA LETTER MA | a84e PHAGS-PA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a851 PHAGS-PA LETTER TSHA | a850 PHAGS-PA LETTER TSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a853 PHAGS-PA LETTER WA | a852 PHAGS-PA LETTER DZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a855 PHAGS-PA LETTER ZA | a854 PHAGS-PA LETTER ZHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a857 PHAGS-PA LETTER YA | a856 PHAGS-PA LETTER SMALL A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a859 PHAGS-PA LETTER LA | a858 PHAGS-PA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85b PHAGS-PA LETTER SA | a85a PHAGS-PA LETTER SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85d PHAGS-PA LETTER A | a85c PHAGS-PA LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a85f PHAGS-PA LETTER U | a85e PHAGS-PA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a861 PHAGS-PA LETTER O | a860 PHAGS-PA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a863 PHAGS-PA LETTER XA | a862 PHAGS-PA LETTER QA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a865 PHAGS-PA LETTER GGA | a864 PHAGS-PA LETTER FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a867 PHAGS-PA SUBJOINED LETTER WA | a866 PHAGS-PA LETTER EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a869 PHAGS-PA LETTER TTA | a868 PHAGS-PA SUBJOINED LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86b PHAGS-PA LETTER DDA | a86a PHAGS-PA LETTER TTHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86d PHAGS-PA LETTER ALTERNATE YA | a86c PHAGS-PA LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a86f PHAGS-PA LETTER VOICED HA | a86e PHAGS-PA LETTER VOICELESS SHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a871 PHAGS-PA SUBJOINED LETTER RA | a870 PHAGS-PA LETTER ASPIRATED FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a873 PHAGS-PA LETTER CANDRABINDU | a872 PHAGS-PA SUPERFIXED LETTER RA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a875 PHAGS-PA DOUBLE HEAD MARK | a874 PHAGS-PA SINGLE HEAD MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a877 PHAGS-PA MARK DOUBLE SHAD | a876 PHAGS-PA MARK SHAD */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a879 (null) | a878 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87b (null) | a87a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87d (null) | a87c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a87f (null) | a87e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a881 SAURASHTRA SIGN VISARGA | a880 SAURASHTRA SIGN ANUSVARA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a883 SAURASHTRA LETTER AA | a882 SAURASHTRA LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a885 SAURASHTRA LETTER II | a884 SAURASHTRA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a887 SAURASHTRA LETTER UU | a886 SAURASHTRA LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a889 SAURASHTRA LETTER VOCALIC RR | a888 SAURASHTRA LETTER VOCALIC R */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88b SAURASHTRA LETTER VOCALIC LL | a88a SAURASHTRA LETTER VOCALIC L */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88d SAURASHTRA LETTER EE | a88c SAURASHTRA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a88f SAURASHTRA LETTER O | a88e SAURASHTRA LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a891 SAURASHTRA LETTER AU | a890 SAURASHTRA LETTER OO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a893 SAURASHTRA LETTER KHA | a892 SAURASHTRA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a895 SAURASHTRA LETTER GHA | a894 SAURASHTRA LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a897 SAURASHTRA LETTER CA | a896 SAURASHTRA LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a899 SAURASHTRA LETTER JA | a898 SAURASHTRA LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89b SAURASHTRA LETTER NYA | a89a SAURASHTRA LETTER JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89d SAURASHTRA LETTER TTHA | a89c SAURASHTRA LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a89f SAURASHTRA LETTER DDHA | a89e SAURASHTRA LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a1 SAURASHTRA LETTER TA | a8a0 SAURASHTRA LETTER NNA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a3 SAURASHTRA LETTER DA | a8a2 SAURASHTRA LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a5 SAURASHTRA LETTER NA | a8a4 SAURASHTRA LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a7 SAURASHTRA LETTER PHA | a8a6 SAURASHTRA LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8a9 SAURASHTRA LETTER BHA | a8a8 SAURASHTRA LETTER BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8ab SAURASHTRA LETTER YA | a8aa SAURASHTRA LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8ad SAURASHTRA LETTER LA | a8ac SAURASHTRA LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8af SAURASHTRA LETTER SHA | a8ae SAURASHTRA LETTER VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8b1 SAURASHTRA LETTER SA | a8b0 SAURASHTRA LETTER SSA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8b3 SAURASHTRA LETTER LLA | a8b2 SAURASHTRA LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b5 SAURASHTRA VOWEL SIGN AA | a8b4 SAURASHTRA CONSONANT SIGN HAARU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b7 SAURASHTRA VOWEL SIGN II | a8b6 SAURASHTRA VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8b9 SAURASHTRA VOWEL SIGN UU | a8b8 SAURASHTRA VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bb SAURASHTRA VOWEL SIGN VOCALIC RR | a8ba SAURASHTRA VOWEL SIGN VOCALIC R */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bd SAURASHTRA VOWEL SIGN VOCALIC LL | a8bc SAURASHTRA VOWEL SIGN VOCALIC L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8bf SAURASHTRA VOWEL SIGN EE | a8be SAURASHTRA VOWEL SIGN E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8c1 SAURASHTRA VOWEL SIGN O | a8c0 SAURASHTRA VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8c3 SAURASHTRA VOWEL SIGN AU | a8c2 SAURASHTRA VOWEL SIGN OO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* a8c5 (null) | a8c4 SAURASHTRA SIGN VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8c7 (null) | a8c6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8c9 (null) | a8c8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8cb (null) | a8ca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8cd (null) | a8cc (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a8cf SAURASHTRA DOUBLE DANDA | a8ce SAURASHTRA DANDA */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d1 SAURASHTRA DIGIT ONE | a8d0 SAURASHTRA DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d3 SAURASHTRA DIGIT THREE | a8d2 SAURASHTRA DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d5 SAURASHTRA DIGIT FIVE | a8d4 SAURASHTRA DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d7 SAURASHTRA DIGIT SEVEN | a8d6 SAURASHTRA DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a8d9 SAURASHTRA DIGIT NINE | a8d8 SAURASHTRA DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8db (null) | a8da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8dd (null) | a8dc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8df (null) | a8de (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e1 COMBINING DEVANAGARI DIGIT ONE | a8e0 COMBINING DEVANAGARI DIGIT ZERO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e3 COMBINING DEVANAGARI DIGIT THREE | a8e2 COMBINING DEVANAGARI DIGIT TWO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e5 COMBINING DEVANAGARI DIGIT FIVE | a8e4 COMBINING DEVANAGARI DIGIT FOUR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e7 COMBINING DEVANAGARI DIGIT SEVEN | a8e6 COMBINING DEVANAGARI DIGIT SIX */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8e9 COMBINING DEVANAGARI DIGIT NINE | a8e8 COMBINING DEVANAGARI DIGIT EIGHT */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8eb COMBINING DEVANAGARI LETTER U | a8ea COMBINING DEVANAGARI LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8ed COMBINING DEVANAGARI LETTER NA | a8ec COMBINING DEVANAGARI LETTER KA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8ef COMBINING DEVANAGARI LETTER RA | a8ee COMBINING DEVANAGARI LETTER PA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a8f1 COMBINING DEVANAGARI SIGN AVAGRAHA | a8f0 COMBINING DEVANAGARI LETTER VI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f3 DEVANAGARI SIGN CANDRABINDU VIRAMA | a8f2 DEVANAGARI SIGN SPACING CANDRABINDU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f5 DEVANAGARI SIGN CANDRABINDU TWO | a8f4 DEVANAGARI SIGN DOUBLE CANDRABINDU VIRA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a8f7 DEVANAGARI SIGN CANDRABINDU AVAGRAHA | a8f6 DEVANAGARI SIGN CANDRABINDU THREE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a8f9 DEVANAGARI GAP FILLER | a8f8 DEVANAGARI SIGN PUSHPIKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_PUNCT, /* a8fb DEVANAGARI HEADSTROKE | a8fa DEVANAGARI CARET */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8fd (null) | a8fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a8ff (null) | a8fe (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a901 KAYAH LI DIGIT ONE | a900 KAYAH LI DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a903 KAYAH LI DIGIT THREE | a902 KAYAH LI DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a905 KAYAH LI DIGIT FIVE | a904 KAYAH LI DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a907 KAYAH LI DIGIT SEVEN | a906 KAYAH LI DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a909 KAYAH LI DIGIT NINE | a908 KAYAH LI DIGIT EIGHT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90b KAYAH LI LETTER KHA | a90a KAYAH LI LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90d KAYAH LI LETTER NGA | a90c KAYAH LI LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a90f KAYAH LI LETTER SHA | a90e KAYAH LI LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a911 KAYAH LI LETTER NYA | a910 KAYAH LI LETTER ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a913 KAYAH LI LETTER HTA | a912 KAYAH LI LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a915 KAYAH LI LETTER PA | a914 KAYAH LI LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a917 KAYAH LI LETTER MA | a916 KAYAH LI LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a919 KAYAH LI LETTER BA | a918 KAYAH LI LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91b KAYAH LI LETTER YA | a91a KAYAH LI LETTER RA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91d KAYAH LI LETTER WA | a91c KAYAH LI LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a91f KAYAH LI LETTER HA | a91e KAYAH LI LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a921 KAYAH LI LETTER CA | a920 KAYAH LI LETTER VA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a923 KAYAH LI LETTER OE | a922 KAYAH LI LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a925 KAYAH LI LETTER OO | a924 KAYAH LI LETTER I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a927 KAYAH LI VOWEL E | a926 KAYAH LI VOWEL UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a929 KAYAH LI VOWEL EE | a928 KAYAH LI VOWEL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a92b KAYAH LI TONE PLOPHU | a92a KAYAH LI VOWEL O */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a92d KAYAH LI TONE CALYA PLOPHU | a92c KAYAH LI TONE CALYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a92f KAYAH LI SIGN SHYA | a92e KAYAH LI SIGN CWI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a931 REJANG LETTER GA | a930 REJANG LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a933 REJANG LETTER TA | a932 REJANG LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a935 REJANG LETTER NA | a934 REJANG LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a937 REJANG LETTER BA | a936 REJANG LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a939 REJANG LETTER CA | a938 REJANG LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93b REJANG LETTER NYA | a93a REJANG LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93d REJANG LETTER RA | a93c REJANG LETTER SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a93f REJANG LETTER YA | a93e REJANG LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a941 REJANG LETTER HA | a940 REJANG LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a943 REJANG LETTER NGGA | a942 REJANG LETTER MBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a945 REJANG LETTER NYJA | a944 REJANG LETTER NDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a947 REJANG VOWEL SIGN I | a946 REJANG LETTER A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a949 REJANG VOWEL SIGN E | a948 REJANG VOWEL SIGN U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94b REJANG VOWEL SIGN O | a94a REJANG VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94d REJANG VOWEL SIGN EU | a94c REJANG VOWEL SIGN AU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a94f REJANG CONSONANT SIGN NG | a94e REJANG VOWEL SIGN EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a951 REJANG CONSONANT SIGN R | a950 REJANG CONSONANT SIGN N */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a953 REJANG VIRAMA | a952 REJANG CONSONANT SIGN H */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a955 (null) | a954 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a957 (null) | a956 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a959 (null) | a958 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a95b (null) | a95a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a95d (null) | a95c (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* a95f REJANG SECTION MARK | a95e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a961 HANGUL CHOSEONG TIKEUT-PIEUP | a960 HANGUL CHOSEONG TIKEUT-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a963 HANGUL CHOSEONG TIKEUT-CIEUC | a962 HANGUL CHOSEONG TIKEUT-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a965 HANGUL CHOSEONG RIEUL-SSANGKIYEOK | a964 HANGUL CHOSEONG RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a967 HANGUL CHOSEONG RIEUL-SSANGTIKEUT | a966 HANGUL CHOSEONG RIEUL-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a969 HANGUL CHOSEONG RIEUL-PIEUP | a968 HANGUL CHOSEONG RIEUL-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96b HANGUL CHOSEONG RIEUL-KAPYEOUNPIEUP | a96a HANGUL CHOSEONG RIEUL-SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96d HANGUL CHOSEONG RIEUL-CIEUC | a96c HANGUL CHOSEONG RIEUL-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a96f HANGUL CHOSEONG MIEUM-KIYEOK | a96e HANGUL CHOSEONG RIEUL-KHIEUKH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a971 HANGUL CHOSEONG MIEUM-SIOS | a970 HANGUL CHOSEONG MIEUM-TIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a973 HANGUL CHOSEONG PIEUP-KHIEUKH | a972 HANGUL CHOSEONG PIEUP-SIOS-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a975 HANGUL CHOSEONG SSANGSIOS-PIEUP | a974 HANGUL CHOSEONG PIEUP-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a977 HANGUL CHOSEONG IEUNG-HIEUH | a976 HANGUL CHOSEONG IEUNG-RIEUL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a979 HANGUL CHOSEONG SSANGTHIEUTH | a978 HANGUL CHOSEONG SSANGCIEUC-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a97b HANGUL CHOSEONG HIEUH-SIOS | a97a HANGUL CHOSEONG PHIEUPH-HIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* a97d (null) | a97c HANGUL CHOSEONG SSANGYEORINHIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a97f (null) | a97e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a981 JAVANESE SIGN CECAK | a980 JAVANESE SIGN PANYANGGA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a983 JAVANESE SIGN WIGNYAN | a982 JAVANESE SIGN LAYAR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a985 JAVANESE LETTER I KAWI | a984 JAVANESE LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a987 JAVANESE LETTER II | a986 JAVANESE LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a989 JAVANESE LETTER PA CEREK | a988 JAVANESE LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98b JAVANESE LETTER NGA LELET RASWADI | a98a JAVANESE LETTER NGA LELET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98d JAVANESE LETTER AI | a98c JAVANESE LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a98f JAVANESE LETTER KA | a98e JAVANESE LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a991 JAVANESE LETTER KA MURDA | a990 JAVANESE LETTER KA SASAK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a993 JAVANESE LETTER GA MURDA | a992 JAVANESE LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a995 JAVANESE LETTER CA | a994 JAVANESE LETTER NGA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a997 JAVANESE LETTER JA | a996 JAVANESE LETTER CA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a999 JAVANESE LETTER JA MAHAPRANA | a998 JAVANESE LETTER NYA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99b JAVANESE LETTER TTA | a99a JAVANESE LETTER NYA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99d JAVANESE LETTER DDA | a99c JAVANESE LETTER TTA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a99f JAVANESE LETTER NA MURDA | a99e JAVANESE LETTER DDA MAHAPRANA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a1 JAVANESE LETTER TA MURDA | a9a0 JAVANESE LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a3 JAVANESE LETTER DA MAHAPRANA | a9a2 JAVANESE LETTER DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a5 JAVANESE LETTER PA | a9a4 JAVANESE LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a7 JAVANESE LETTER BA | a9a6 JAVANESE LETTER PA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9a9 JAVANESE LETTER MA | a9a8 JAVANESE LETTER BA MURDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9ab JAVANESE LETTER RA | a9aa JAVANESE LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9ad JAVANESE LETTER LA | a9ac JAVANESE LETTER RA AGUNG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9af JAVANESE LETTER SA MURDA | a9ae JAVANESE LETTER WA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* a9b1 JAVANESE LETTER SA | a9b0 JAVANESE LETTER SA MAHAPRANA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* a9b3 JAVANESE SIGN CECAK TELU | a9b2 JAVANESE LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b5 JAVANESE VOWEL SIGN TOLONG | a9b4 JAVANESE VOWEL SIGN TARUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b7 JAVANESE VOWEL SIGN WULU MELIK | a9b6 JAVANESE VOWEL SIGN WULU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9b9 JAVANESE VOWEL SIGN SUKU MENDUT | a9b8 JAVANESE VOWEL SIGN SUKU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bb JAVANESE VOWEL SIGN DIRGA MURE | a9ba JAVANESE VOWEL SIGN TALING */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bd JAVANESE CONSONANT SIGN KERET | a9bc JAVANESE VOWEL SIGN PEPET */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* a9bf JAVANESE CONSONANT SIGN CAKRA | a9be JAVANESE CONSONANT SIGN PENGKAL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* a9c1 JAVANESE LEFT RERENGGAN | a9c0 JAVANESE PANGKON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c3 JAVANESE PADA ANDAP | a9c2 JAVANESE RIGHT RERENGGAN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c5 JAVANESE PADA LUHUR | a9c4 JAVANESE PADA MADYA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c7 JAVANESE PADA PANGKAT | a9c6 JAVANESE PADA WINDU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9c9 JAVANESE PADA LUNGSI | a9c8 JAVANESE PADA LINGSA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9cb JAVANESE PADA ADEG ADEG | a9ca JAVANESE PADA ADEG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9cd JAVANESE TURNED PADA PISELEH | a9cc JAVANESE PADA PISELEH */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* a9cf JAVANESE PANGRANGKEP | a9ce (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d1 JAVANESE DIGIT ONE | a9d0 JAVANESE DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d3 JAVANESE DIGIT THREE | a9d2 JAVANESE DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d5 JAVANESE DIGIT FIVE | a9d4 JAVANESE DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d7 JAVANESE DIGIT SEVEN | a9d6 JAVANESE DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* a9d9 JAVANESE DIGIT NINE | a9d8 JAVANESE DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9db (null) | a9da (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9dd (null) | a9dc (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* a9df JAVANESE PADA ISEN-ISEN | a9de JAVANESE PADA TIRTA TUMETES */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e1 (null) | a9e0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e3 (null) | a9e2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e5 (null) | a9e4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e7 (null) | a9e6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9e9 (null) | a9e8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9eb (null) | a9ea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ed (null) | a9ec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ef (null) | a9ee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f1 (null) | a9f0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f3 (null) | a9f2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f5 (null) | a9f4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f7 (null) | a9f6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9f9 (null) | a9f8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9fb (null) | a9fa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9fd (null) | a9fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* a9ff (null) | a9fe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa01 CHAM LETTER I | aa00 CHAM LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa03 CHAM LETTER E | aa02 CHAM LETTER U */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa05 CHAM LETTER O | aa04 CHAM LETTER AI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa07 CHAM LETTER KHA | aa06 CHAM LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa09 CHAM LETTER GHA | aa08 CHAM LETTER GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0b CHAM LETTER NGA | aa0a CHAM LETTER NGUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0d CHAM LETTER CHHA | aa0c CHAM LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa0f CHAM LETTER JHA | aa0e CHAM LETTER JA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa11 CHAM LETTER NHA | aa10 CHAM LETTER NHUE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa13 CHAM LETTER TA | aa12 CHAM LETTER NHJA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa15 CHAM LETTER DA | aa14 CHAM LETTER THA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa17 CHAM LETTER NUE | aa16 CHAM LETTER DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa19 CHAM LETTER DDA | aa18 CHAM LETTER NA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1b CHAM LETTER PPA | aa1a CHAM LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1d CHAM LETTER BA | aa1c CHAM LETTER PHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa1f CHAM LETTER MUE | aa1e CHAM LETTER BHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa21 CHAM LETTER BBA | aa20 CHAM LETTER MA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa23 CHAM LETTER RA | aa22 CHAM LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa25 CHAM LETTER VA | aa24 CHAM LETTER LA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa27 CHAM LETTER SA | aa26 CHAM LETTER SSA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa29 CHAM VOWEL SIGN AA | aa28 CHAM LETTER HA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2b CHAM VOWEL SIGN II | aa2a CHAM VOWEL SIGN I */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2d CHAM VOWEL SIGN U | aa2c CHAM VOWEL SIGN EI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa2f CHAM VOWEL SIGN O | aa2e CHAM VOWEL SIGN OE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa31 CHAM VOWEL SIGN AU | aa30 CHAM VOWEL SIGN AI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa33 CHAM CONSONANT SIGN YA | aa32 CHAM VOWEL SIGN UE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa35 CHAM CONSONANT SIGN LA | aa34 CHAM CONSONANT SIGN RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* aa37 (null) | aa36 CHAM CONSONANT SIGN WA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa39 (null) | aa38 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3b (null) | aa3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3d (null) | aa3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa3f (null) | aa3e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa41 CHAM LETTER FINAL G | aa40 CHAM LETTER FINAL K */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa43 CHAM CONSONANT SIGN FINAL NG | aa42 CHAM LETTER FINAL NG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa45 CHAM LETTER FINAL T | aa44 CHAM LETTER FINAL CH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa47 CHAM LETTER FINAL P | aa46 CHAM LETTER FINAL N */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa49 CHAM LETTER FINAL R | aa48 CHAM LETTER FINAL Y */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa4b CHAM LETTER FINAL SS | aa4a CHAM LETTER FINAL L */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa4d CHAM CONSONANT SIGN FINAL H | aa4c CHAM CONSONANT SIGN FINAL M */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa4f (null) | aa4e (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa51 CHAM DIGIT ONE | aa50 CHAM DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa53 CHAM DIGIT THREE | aa52 CHAM DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa55 CHAM DIGIT FIVE | aa54 CHAM DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa57 CHAM DIGIT SEVEN | aa56 CHAM DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* aa59 CHAM DIGIT NINE | aa58 CHAM DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa5b (null) | aa5a (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aa5d CHAM PUNCTUATION DANDA | aa5c CHAM PUNCTUATION SPIRAL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aa5f CHAM PUNCTUATION TRIPLE DANDA | aa5e CHAM PUNCTUATION DOUBLE DANDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa61 MYANMAR LETTER KHAMTI CA | aa60 MYANMAR LETTER KHAMTI GA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa63 MYANMAR LETTER KHAMTI JA | aa62 MYANMAR LETTER KHAMTI CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa65 MYANMAR LETTER KHAMTI NYA | aa64 MYANMAR LETTER KHAMTI JHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa67 MYANMAR LETTER KHAMTI TTHA | aa66 MYANMAR LETTER KHAMTI TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa69 MYANMAR LETTER KHAMTI DDHA | aa68 MYANMAR LETTER KHAMTI DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6b MYANMAR LETTER KHAMTI NA | aa6a MYANMAR LETTER KHAMTI DHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6d MYANMAR LETTER KHAMTI HA | aa6c MYANMAR LETTER KHAMTI SA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa6f MYANMAR LETTER KHAMTI FA | aa6e MYANMAR LETTER KHAMTI HHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aa71 MYANMAR LETTER KHAMTI XA | aa70 MYANMAR MODIFIER LETTER KHAMTI REDUPLIC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa73 MYANMAR LETTER KHAMTI RA | aa72 MYANMAR LETTER KHAMTI ZA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa75 MYANMAR LOGOGRAM KHAMTI QN | aa74 MYANMAR LOGOGRAM KHAMTI OAY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa77 MYANMAR SYMBOL AITON EXCLAMATION | aa76 MYANMAR LOGOGRAM KHAMTI HM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aa79 MYANMAR SYMBOL AITON TWO | aa78 MYANMAR SYMBOL AITON ONE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aa7b MYANMAR SIGN PAO KAREN TONE | aa7a MYANMAR LETTER AITON RA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa7d (null) | aa7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aa7f (null) | aa7e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa81 TAI VIET LETTER HIGH KO | aa80 TAI VIET LETTER LOW KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa83 TAI VIET LETTER HIGH KHO | aa82 TAI VIET LETTER LOW KHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa85 TAI VIET LETTER HIGH KHHO | aa84 TAI VIET LETTER LOW KHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa87 TAI VIET LETTER HIGH GO | aa86 TAI VIET LETTER LOW GO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa89 TAI VIET LETTER HIGH NGO | aa88 TAI VIET LETTER LOW NGO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8b TAI VIET LETTER HIGH CO | aa8a TAI VIET LETTER LOW CO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8d TAI VIET LETTER HIGH CHO | aa8c TAI VIET LETTER LOW CHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa8f TAI VIET LETTER HIGH SO | aa8e TAI VIET LETTER LOW SO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa91 TAI VIET LETTER HIGH NYO | aa90 TAI VIET LETTER LOW NYO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa93 TAI VIET LETTER HIGH DO | aa92 TAI VIET LETTER LOW DO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa95 TAI VIET LETTER HIGH TO | aa94 TAI VIET LETTER LOW TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa97 TAI VIET LETTER HIGH THO | aa96 TAI VIET LETTER LOW THO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa99 TAI VIET LETTER HIGH NO | aa98 TAI VIET LETTER LOW NO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9b TAI VIET LETTER HIGH BO | aa9a TAI VIET LETTER LOW BO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9d TAI VIET LETTER HIGH PO | aa9c TAI VIET LETTER LOW PO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aa9f TAI VIET LETTER HIGH PHO | aa9e TAI VIET LETTER LOW PHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa1 TAI VIET LETTER HIGH FO | aaa0 TAI VIET LETTER LOW FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa3 TAI VIET LETTER HIGH MO | aaa2 TAI VIET LETTER LOW MO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa5 TAI VIET LETTER HIGH YO | aaa4 TAI VIET LETTER LOW YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa7 TAI VIET LETTER HIGH RO | aaa6 TAI VIET LETTER LOW RO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaa9 TAI VIET LETTER HIGH LO | aaa8 TAI VIET LETTER LOW LO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaab TAI VIET LETTER HIGH VO | aaaa TAI VIET LETTER LOW VO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaad TAI VIET LETTER HIGH HO | aaac TAI VIET LETTER LOW HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aaaf TAI VIET LETTER HIGH O | aaae TAI VIET LETTER LOW O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab1 TAI VIET VOWEL AA | aab0 TAI VIET MAI KANG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aab3 TAI VIET VOWEL UE | aab2 TAI VIET VOWEL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab5 TAI VIET VOWEL E | aab4 TAI VIET VOWEL U */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aab7 TAI VIET MAI KHIT | aab6 TAI VIET VOWEL O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* aab9 TAI VIET VOWEL UEA | aab8 TAI VIET VOWEL IA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aabb TAI VIET VOWEL AUE | aaba TAI VIET VOWEL UA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aabd TAI VIET VOWEL AN | aabc TAI VIET VOWEL AY */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aabf TAI VIET TONE MAI EK | aabe TAI VIET VOWEL AM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aac1 TAI VIET TONE MAI THO | aac0 TAI VIET TONE MAI NUENG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* aac3 (null) | aac2 TAI VIET TONE MAI SONG */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac5 (null) | aac4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac7 (null) | aac6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aac9 (null) | aac8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacb (null) | aaca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacd (null) | aacc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aacf (null) | aace (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad1 (null) | aad0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad3 (null) | aad2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad5 (null) | aad4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad7 (null) | aad6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aad9 (null) | aad8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* aadb TAI VIET SYMBOL KON | aada (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aadd TAI VIET SYMBOL SAM | aadc TAI VIET SYMBOL NUENG */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aadf TAI VIET SYMBOL KOI KOI | aade TAI VIET SYMBOL HO HOI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae1 MEETEI MAYEK LETTER O | aae0 MEETEI MAYEK LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae3 MEETEI MAYEK LETTER NYA | aae2 MEETEI MAYEK LETTER CHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae5 MEETEI MAYEK LETTER TTHA | aae4 MEETEI MAYEK LETTER TTA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae7 MEETEI MAYEK LETTER DDHA | aae6 MEETEI MAYEK LETTER DDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aae9 MEETEI MAYEK LETTER SHA | aae8 MEETEI MAYEK LETTER NNA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aaeb MEETEI MAYEK VOWEL SIGN II | aaea MEETEI MAYEK LETTER SSA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaed MEETEI MAYEK VOWEL SIGN AAI | aaec MEETEI MAYEK VOWEL SIGN UU */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaef MEETEI MAYEK VOWEL SIGN AAU | aaee MEETEI MAYEK VOWEL SIGN AU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* aaf1 MEETEI MAYEK AHANG KHUDAM | aaf0 MEETEI MAYEK CHEIKHAN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* aaf3 MEETEI MAYEK SYLLABLE REPETITION MARK | aaf2 MEETEI MAYEK ANJI */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* aaf5 MEETEI MAYEK VOWEL SIGN VISARGA | aaf4 MEETEI MAYEK WORD REPETITION MARK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* aaf7 (null) | aaf6 MEETEI MAYEK VIRAMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aaf9 (null) | aaf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aafb (null) | aafa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aafd (null) | aafc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aaff (null) | aafe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab01 ETHIOPIC SYLLABLE TTHU | ab00 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab03 ETHIOPIC SYLLABLE TTHAA | ab02 ETHIOPIC SYLLABLE TTHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab05 ETHIOPIC SYLLABLE TTHE | ab04 ETHIOPIC SYLLABLE TTHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab07 (null) | ab06 ETHIOPIC SYLLABLE TTHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab09 ETHIOPIC SYLLABLE DDHU | ab08 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab0b ETHIOPIC SYLLABLE DDHAA | ab0a ETHIOPIC SYLLABLE DDHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab0d ETHIOPIC SYLLABLE DDHE | ab0c ETHIOPIC SYLLABLE DDHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab0f (null) | ab0e ETHIOPIC SYLLABLE DDHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* ab11 ETHIOPIC SYLLABLE DZU | ab10 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab13 ETHIOPIC SYLLABLE DZAA | ab12 ETHIOPIC SYLLABLE DZI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab15 ETHIOPIC SYLLABLE DZE | ab14 ETHIOPIC SYLLABLE DZEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab17 (null) | ab16 ETHIOPIC SYLLABLE DZO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab19 (null) | ab18 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1b (null) | ab1a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1d (null) | ab1c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab1f (null) | ab1e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab21 ETHIOPIC SYLLABLE CCHHU | ab20 ETHIOPIC SYLLABLE CCHHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab23 ETHIOPIC SYLLABLE CCHHAA | ab22 ETHIOPIC SYLLABLE CCHHI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab25 ETHIOPIC SYLLABLE CCHHE | ab24 ETHIOPIC SYLLABLE CCHHEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab27 (null) | ab26 ETHIOPIC SYLLABLE CCHHO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab29 ETHIOPIC SYLLABLE BBU | ab28 ETHIOPIC SYLLABLE BBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab2b ETHIOPIC SYLLABLE BBAA | ab2a ETHIOPIC SYLLABLE BBI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ab2d ETHIOPIC SYLLABLE BBE | ab2c ETHIOPIC SYLLABLE BBEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ab2f (null) | ab2e ETHIOPIC SYLLABLE BBO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab31 (null) | ab30 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab33 (null) | ab32 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab35 (null) | ab34 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab37 (null) | ab36 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab39 (null) | ab38 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3b (null) | ab3a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3d (null) | ab3c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab3f (null) | ab3e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab41 (null) | ab40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab43 (null) | ab42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab45 (null) | ab44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab47 (null) | ab46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab49 (null) | ab48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4b (null) | ab4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4d (null) | ab4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab4f (null) | ab4e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab51 (null) | ab50 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab53 (null) | ab52 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab55 (null) | ab54 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab57 (null) | ab56 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab59 (null) | ab58 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5b (null) | ab5a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5d (null) | ab5c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab5f (null) | ab5e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab61 (null) | ab60 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab63 (null) | ab62 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab65 (null) | ab64 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab67 (null) | ab66 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab69 (null) | ab68 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6b (null) | ab6a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6d (null) | ab6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab6f (null) | ab6e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab71 (null) | ab70 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab73 (null) | ab72 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab75 (null) | ab74 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab77 (null) | ab76 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab79 (null) | ab78 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7b (null) | ab7a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7d (null) | ab7c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab7f (null) | ab7e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab81 (null) | ab80 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab83 (null) | ab82 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab85 (null) | ab84 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab87 (null) | ab86 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab89 (null) | ab88 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8b (null) | ab8a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8d (null) | ab8c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab8f (null) | ab8e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab91 (null) | ab90 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab93 (null) | ab92 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab95 (null) | ab94 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab97 (null) | ab96 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab99 (null) | ab98 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9b (null) | ab9a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9d (null) | ab9c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ab9f (null) | ab9e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba1 (null) | aba0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba3 (null) | aba2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba5 (null) | aba4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba7 (null) | aba6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* aba9 (null) | aba8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abab (null) | abaa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abad (null) | abac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abaf (null) | abae (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb1 (null) | abb0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb3 (null) | abb2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb5 (null) | abb4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb7 (null) | abb6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abb9 (null) | abb8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbb (null) | abba (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbd (null) | abbc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abbf (null) | abbe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc1 MEETEI MAYEK LETTER SAM | abc0 MEETEI MAYEK LETTER KOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc3 MEETEI MAYEK LETTER MIT | abc2 MEETEI MAYEK LETTER LAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc5 MEETEI MAYEK LETTER NA | abc4 MEETEI MAYEK LETTER PA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc7 MEETEI MAYEK LETTER TIL | abc6 MEETEI MAYEK LETTER CHIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abc9 MEETEI MAYEK LETTER NGOU | abc8 MEETEI MAYEK LETTER KHOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcb MEETEI MAYEK LETTER WAI | abca MEETEI MAYEK LETTER THOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcd MEETEI MAYEK LETTER HUK | abcc MEETEI MAYEK LETTER YANG */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abcf MEETEI MAYEK LETTER I | abce MEETEI MAYEK LETTER UN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd1 MEETEI MAYEK LETTER ATIYA | abd0 MEETEI MAYEK LETTER PHAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd3 MEETEI MAYEK LETTER JHAM | abd2 MEETEI MAYEK LETTER GOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd5 MEETEI MAYEK LETTER BA | abd4 MEETEI MAYEK LETTER RAI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd7 MEETEI MAYEK LETTER DIL | abd6 MEETEI MAYEK LETTER JIL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abd9 MEETEI MAYEK LETTER DHOU | abd8 MEETEI MAYEK LETTER GHOU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdb MEETEI MAYEK LETTER KOK LONSUM | abda MEETEI MAYEK LETTER BHAM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdd MEETEI MAYEK LETTER MIT LONSUM | abdc MEETEI MAYEK LETTER LAI LONSUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abdf MEETEI MAYEK LETTER NA LONSUM | abde MEETEI MAYEK LETTER PA LONSUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* abe1 MEETEI MAYEK LETTER NGOU LONSUM | abe0 MEETEI MAYEK LETTER TIL LONSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* abe3 MEETEI MAYEK VOWEL SIGN ONAP | abe2 MEETEI MAYEK LETTER I LONSUM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe5 MEETEI MAYEK VOWEL SIGN ANAP | abe4 MEETEI MAYEK VOWEL SIGN INAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe7 MEETEI MAYEK VOWEL SIGN SOUNAP | abe6 MEETEI MAYEK VOWEL SIGN YENAP */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abe9 MEETEI MAYEK VOWEL SIGN CHEINAP | abe8 MEETEI MAYEK VOWEL SIGN UNAP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* abeb MEETEI MAYEK CHEIKHEI | abea MEETEI MAYEK VOWEL SIGN NUNG */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* abed MEETEI MAYEK APUN IYEK | abec MEETEI MAYEK LUM IYEK */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abef (null) | abee (null) */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf1 MEETEI MAYEK DIGIT ONE | abf0 MEETEI MAYEK DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf3 MEETEI MAYEK DIGIT THREE | abf2 MEETEI MAYEK DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf5 MEETEI MAYEK DIGIT FIVE | abf4 MEETEI MAYEK DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf7 MEETEI MAYEK DIGIT SEVEN | abf6 MEETEI MAYEK DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* abf9 MEETEI MAYEK DIGIT NINE | abf8 MEETEI MAYEK DIGIT EIGHT */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abfb (null) | abfa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abfd (null) | abfc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* abff (null) | abfe (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac01 Hangul Syllable-AC01 | ac00 Hangul Syllable-AC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac03 Hangul Syllable-AC03 | ac02 Hangul Syllable-AC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac05 Hangul Syllable-AC05 | ac04 Hangul Syllable-AC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac07 Hangul Syllable-AC07 | ac06 Hangul Syllable-AC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac09 Hangul Syllable-AC09 | ac08 Hangul Syllable-AC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0b Hangul Syllable-AC0B | ac0a Hangul Syllable-AC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0d Hangul Syllable-AC0D | ac0c Hangul Syllable-AC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac0f Hangul Syllable-AC0F | ac0e Hangul Syllable-AC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac11 Hangul Syllable-AC11 | ac10 Hangul Syllable-AC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac13 Hangul Syllable-AC13 | ac12 Hangul Syllable-AC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac15 Hangul Syllable-AC15 | ac14 Hangul Syllable-AC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac17 Hangul Syllable-AC17 | ac16 Hangul Syllable-AC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac19 Hangul Syllable-AC19 | ac18 Hangul Syllable-AC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1b Hangul Syllable-AC1B | ac1a Hangul Syllable-AC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1d Hangul Syllable-AC1D | ac1c Hangul Syllable-AC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac1f Hangul Syllable-AC1F | ac1e Hangul Syllable-AC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac21 Hangul Syllable-AC21 | ac20 Hangul Syllable-AC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac23 Hangul Syllable-AC23 | ac22 Hangul Syllable-AC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac25 Hangul Syllable-AC25 | ac24 Hangul Syllable-AC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac27 Hangul Syllable-AC27 | ac26 Hangul Syllable-AC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac29 Hangul Syllable-AC29 | ac28 Hangul Syllable-AC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2b Hangul Syllable-AC2B | ac2a Hangul Syllable-AC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2d Hangul Syllable-AC2D | ac2c Hangul Syllable-AC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac2f Hangul Syllable-AC2F | ac2e Hangul Syllable-AC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac31 Hangul Syllable-AC31 | ac30 Hangul Syllable-AC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac33 Hangul Syllable-AC33 | ac32 Hangul Syllable-AC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac35 Hangul Syllable-AC35 | ac34 Hangul Syllable-AC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac37 Hangul Syllable-AC37 | ac36 Hangul Syllable-AC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac39 Hangul Syllable-AC39 | ac38 Hangul Syllable-AC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3b Hangul Syllable-AC3B | ac3a Hangul Syllable-AC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3d Hangul Syllable-AC3D | ac3c Hangul Syllable-AC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac3f Hangul Syllable-AC3F | ac3e Hangul Syllable-AC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac41 Hangul Syllable-AC41 | ac40 Hangul Syllable-AC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac43 Hangul Syllable-AC43 | ac42 Hangul Syllable-AC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac45 Hangul Syllable-AC45 | ac44 Hangul Syllable-AC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac47 Hangul Syllable-AC47 | ac46 Hangul Syllable-AC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac49 Hangul Syllable-AC49 | ac48 Hangul Syllable-AC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4b Hangul Syllable-AC4B | ac4a Hangul Syllable-AC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4d Hangul Syllable-AC4D | ac4c Hangul Syllable-AC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac4f Hangul Syllable-AC4F | ac4e Hangul Syllable-AC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac51 Hangul Syllable-AC51 | ac50 Hangul Syllable-AC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac53 Hangul Syllable-AC53 | ac52 Hangul Syllable-AC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac55 Hangul Syllable-AC55 | ac54 Hangul Syllable-AC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac57 Hangul Syllable-AC57 | ac56 Hangul Syllable-AC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac59 Hangul Syllable-AC59 | ac58 Hangul Syllable-AC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5b Hangul Syllable-AC5B | ac5a Hangul Syllable-AC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5d Hangul Syllable-AC5D | ac5c Hangul Syllable-AC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac5f Hangul Syllable-AC5F | ac5e Hangul Syllable-AC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac61 Hangul Syllable-AC61 | ac60 Hangul Syllable-AC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac63 Hangul Syllable-AC63 | ac62 Hangul Syllable-AC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac65 Hangul Syllable-AC65 | ac64 Hangul Syllable-AC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac67 Hangul Syllable-AC67 | ac66 Hangul Syllable-AC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac69 Hangul Syllable-AC69 | ac68 Hangul Syllable-AC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6b Hangul Syllable-AC6B | ac6a Hangul Syllable-AC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6d Hangul Syllable-AC6D | ac6c Hangul Syllable-AC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac6f Hangul Syllable-AC6F | ac6e Hangul Syllable-AC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac71 Hangul Syllable-AC71 | ac70 Hangul Syllable-AC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac73 Hangul Syllable-AC73 | ac72 Hangul Syllable-AC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac75 Hangul Syllable-AC75 | ac74 Hangul Syllable-AC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac77 Hangul Syllable-AC77 | ac76 Hangul Syllable-AC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac79 Hangul Syllable-AC79 | ac78 Hangul Syllable-AC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7b Hangul Syllable-AC7B | ac7a Hangul Syllable-AC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7d Hangul Syllable-AC7D | ac7c Hangul Syllable-AC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac7f Hangul Syllable-AC7F | ac7e Hangul Syllable-AC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac81 Hangul Syllable-AC81 | ac80 Hangul Syllable-AC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac83 Hangul Syllable-AC83 | ac82 Hangul Syllable-AC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac85 Hangul Syllable-AC85 | ac84 Hangul Syllable-AC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac87 Hangul Syllable-AC87 | ac86 Hangul Syllable-AC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac89 Hangul Syllable-AC89 | ac88 Hangul Syllable-AC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8b Hangul Syllable-AC8B | ac8a Hangul Syllable-AC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8d Hangul Syllable-AC8D | ac8c Hangul Syllable-AC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac8f Hangul Syllable-AC8F | ac8e Hangul Syllable-AC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac91 Hangul Syllable-AC91 | ac90 Hangul Syllable-AC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac93 Hangul Syllable-AC93 | ac92 Hangul Syllable-AC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac95 Hangul Syllable-AC95 | ac94 Hangul Syllable-AC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac97 Hangul Syllable-AC97 | ac96 Hangul Syllable-AC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac99 Hangul Syllable-AC99 | ac98 Hangul Syllable-AC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9b Hangul Syllable-AC9B | ac9a Hangul Syllable-AC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9d Hangul Syllable-AC9D | ac9c Hangul Syllable-AC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ac9f Hangul Syllable-AC9F | ac9e Hangul Syllable-AC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca1 Hangul Syllable-ACA1 | aca0 Hangul Syllable-ACA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca3 Hangul Syllable-ACA3 | aca2 Hangul Syllable-ACA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca5 Hangul Syllable-ACA5 | aca4 Hangul Syllable-ACA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca7 Hangul Syllable-ACA7 | aca6 Hangul Syllable-ACA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aca9 Hangul Syllable-ACA9 | aca8 Hangul Syllable-ACA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acab Hangul Syllable-ACAB | acaa Hangul Syllable-ACAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acad Hangul Syllable-ACAD | acac Hangul Syllable-ACAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acaf Hangul Syllable-ACAF | acae Hangul Syllable-ACAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb1 Hangul Syllable-ACB1 | acb0 Hangul Syllable-ACB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb3 Hangul Syllable-ACB3 | acb2 Hangul Syllable-ACB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb5 Hangul Syllable-ACB5 | acb4 Hangul Syllable-ACB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb7 Hangul Syllable-ACB7 | acb6 Hangul Syllable-ACB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acb9 Hangul Syllable-ACB9 | acb8 Hangul Syllable-ACB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbb Hangul Syllable-ACBB | acba Hangul Syllable-ACBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbd Hangul Syllable-ACBD | acbc Hangul Syllable-ACBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acbf Hangul Syllable-ACBF | acbe Hangul Syllable-ACBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc1 Hangul Syllable-ACC1 | acc0 Hangul Syllable-ACC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc3 Hangul Syllable-ACC3 | acc2 Hangul Syllable-ACC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc5 Hangul Syllable-ACC5 | acc4 Hangul Syllable-ACC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc7 Hangul Syllable-ACC7 | acc6 Hangul Syllable-ACC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acc9 Hangul Syllable-ACC9 | acc8 Hangul Syllable-ACC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accb Hangul Syllable-ACCB | acca Hangul Syllable-ACCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accd Hangul Syllable-ACCD | accc Hangul Syllable-ACCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* accf Hangul Syllable-ACCF | acce Hangul Syllable-ACCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd1 Hangul Syllable-ACD1 | acd0 Hangul Syllable-ACD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd3 Hangul Syllable-ACD3 | acd2 Hangul Syllable-ACD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd5 Hangul Syllable-ACD5 | acd4 Hangul Syllable-ACD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd7 Hangul Syllable-ACD7 | acd6 Hangul Syllable-ACD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acd9 Hangul Syllable-ACD9 | acd8 Hangul Syllable-ACD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdb Hangul Syllable-ACDB | acda Hangul Syllable-ACDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdd Hangul Syllable-ACDD | acdc Hangul Syllable-ACDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acdf Hangul Syllable-ACDF | acde Hangul Syllable-ACDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace1 Hangul Syllable-ACE1 | ace0 Hangul Syllable-ACE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace3 Hangul Syllable-ACE3 | ace2 Hangul Syllable-ACE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace5 Hangul Syllable-ACE5 | ace4 Hangul Syllable-ACE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace7 Hangul Syllable-ACE7 | ace6 Hangul Syllable-ACE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ace9 Hangul Syllable-ACE9 | ace8 Hangul Syllable-ACE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aceb Hangul Syllable-ACEB | acea Hangul Syllable-ACEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aced Hangul Syllable-ACED | acec Hangul Syllable-ACEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acef Hangul Syllable-ACEF | acee Hangul Syllable-ACEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf1 Hangul Syllable-ACF1 | acf0 Hangul Syllable-ACF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf3 Hangul Syllable-ACF3 | acf2 Hangul Syllable-ACF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf5 Hangul Syllable-ACF5 | acf4 Hangul Syllable-ACF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf7 Hangul Syllable-ACF7 | acf6 Hangul Syllable-ACF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acf9 Hangul Syllable-ACF9 | acf8 Hangul Syllable-ACF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acfb Hangul Syllable-ACFB | acfa Hangul Syllable-ACFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acfd Hangul Syllable-ACFD | acfc Hangul Syllable-ACFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* acff Hangul Syllable-ACFF | acfe Hangul Syllable-ACFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad01 Hangul Syllable-AD01 | ad00 Hangul Syllable-AD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad03 Hangul Syllable-AD03 | ad02 Hangul Syllable-AD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad05 Hangul Syllable-AD05 | ad04 Hangul Syllable-AD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad07 Hangul Syllable-AD07 | ad06 Hangul Syllable-AD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad09 Hangul Syllable-AD09 | ad08 Hangul Syllable-AD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0b Hangul Syllable-AD0B | ad0a Hangul Syllable-AD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0d Hangul Syllable-AD0D | ad0c Hangul Syllable-AD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad0f Hangul Syllable-AD0F | ad0e Hangul Syllable-AD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad11 Hangul Syllable-AD11 | ad10 Hangul Syllable-AD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad13 Hangul Syllable-AD13 | ad12 Hangul Syllable-AD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad15 Hangul Syllable-AD15 | ad14 Hangul Syllable-AD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad17 Hangul Syllable-AD17 | ad16 Hangul Syllable-AD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad19 Hangul Syllable-AD19 | ad18 Hangul Syllable-AD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1b Hangul Syllable-AD1B | ad1a Hangul Syllable-AD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1d Hangul Syllable-AD1D | ad1c Hangul Syllable-AD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad1f Hangul Syllable-AD1F | ad1e Hangul Syllable-AD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad21 Hangul Syllable-AD21 | ad20 Hangul Syllable-AD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad23 Hangul Syllable-AD23 | ad22 Hangul Syllable-AD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad25 Hangul Syllable-AD25 | ad24 Hangul Syllable-AD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad27 Hangul Syllable-AD27 | ad26 Hangul Syllable-AD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad29 Hangul Syllable-AD29 | ad28 Hangul Syllable-AD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2b Hangul Syllable-AD2B | ad2a Hangul Syllable-AD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2d Hangul Syllable-AD2D | ad2c Hangul Syllable-AD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad2f Hangul Syllable-AD2F | ad2e Hangul Syllable-AD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad31 Hangul Syllable-AD31 | ad30 Hangul Syllable-AD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad33 Hangul Syllable-AD33 | ad32 Hangul Syllable-AD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad35 Hangul Syllable-AD35 | ad34 Hangul Syllable-AD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad37 Hangul Syllable-AD37 | ad36 Hangul Syllable-AD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad39 Hangul Syllable-AD39 | ad38 Hangul Syllable-AD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3b Hangul Syllable-AD3B | ad3a Hangul Syllable-AD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3d Hangul Syllable-AD3D | ad3c Hangul Syllable-AD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad3f Hangul Syllable-AD3F | ad3e Hangul Syllable-AD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad41 Hangul Syllable-AD41 | ad40 Hangul Syllable-AD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad43 Hangul Syllable-AD43 | ad42 Hangul Syllable-AD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad45 Hangul Syllable-AD45 | ad44 Hangul Syllable-AD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad47 Hangul Syllable-AD47 | ad46 Hangul Syllable-AD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad49 Hangul Syllable-AD49 | ad48 Hangul Syllable-AD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4b Hangul Syllable-AD4B | ad4a Hangul Syllable-AD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4d Hangul Syllable-AD4D | ad4c Hangul Syllable-AD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad4f Hangul Syllable-AD4F | ad4e Hangul Syllable-AD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad51 Hangul Syllable-AD51 | ad50 Hangul Syllable-AD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad53 Hangul Syllable-AD53 | ad52 Hangul Syllable-AD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad55 Hangul Syllable-AD55 | ad54 Hangul Syllable-AD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad57 Hangul Syllable-AD57 | ad56 Hangul Syllable-AD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad59 Hangul Syllable-AD59 | ad58 Hangul Syllable-AD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5b Hangul Syllable-AD5B | ad5a Hangul Syllable-AD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5d Hangul Syllable-AD5D | ad5c Hangul Syllable-AD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad5f Hangul Syllable-AD5F | ad5e Hangul Syllable-AD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad61 Hangul Syllable-AD61 | ad60 Hangul Syllable-AD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad63 Hangul Syllable-AD63 | ad62 Hangul Syllable-AD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad65 Hangul Syllable-AD65 | ad64 Hangul Syllable-AD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad67 Hangul Syllable-AD67 | ad66 Hangul Syllable-AD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad69 Hangul Syllable-AD69 | ad68 Hangul Syllable-AD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6b Hangul Syllable-AD6B | ad6a Hangul Syllable-AD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6d Hangul Syllable-AD6D | ad6c Hangul Syllable-AD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad6f Hangul Syllable-AD6F | ad6e Hangul Syllable-AD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad71 Hangul Syllable-AD71 | ad70 Hangul Syllable-AD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad73 Hangul Syllable-AD73 | ad72 Hangul Syllable-AD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad75 Hangul Syllable-AD75 | ad74 Hangul Syllable-AD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad77 Hangul Syllable-AD77 | ad76 Hangul Syllable-AD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad79 Hangul Syllable-AD79 | ad78 Hangul Syllable-AD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7b Hangul Syllable-AD7B | ad7a Hangul Syllable-AD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7d Hangul Syllable-AD7D | ad7c Hangul Syllable-AD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad7f Hangul Syllable-AD7F | ad7e Hangul Syllable-AD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad81 Hangul Syllable-AD81 | ad80 Hangul Syllable-AD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad83 Hangul Syllable-AD83 | ad82 Hangul Syllable-AD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad85 Hangul Syllable-AD85 | ad84 Hangul Syllable-AD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad87 Hangul Syllable-AD87 | ad86 Hangul Syllable-AD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad89 Hangul Syllable-AD89 | ad88 Hangul Syllable-AD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8b Hangul Syllable-AD8B | ad8a Hangul Syllable-AD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8d Hangul Syllable-AD8D | ad8c Hangul Syllable-AD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad8f Hangul Syllable-AD8F | ad8e Hangul Syllable-AD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad91 Hangul Syllable-AD91 | ad90 Hangul Syllable-AD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad93 Hangul Syllable-AD93 | ad92 Hangul Syllable-AD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad95 Hangul Syllable-AD95 | ad94 Hangul Syllable-AD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad97 Hangul Syllable-AD97 | ad96 Hangul Syllable-AD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad99 Hangul Syllable-AD99 | ad98 Hangul Syllable-AD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9b Hangul Syllable-AD9B | ad9a Hangul Syllable-AD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9d Hangul Syllable-AD9D | ad9c Hangul Syllable-AD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ad9f Hangul Syllable-AD9F | ad9e Hangul Syllable-AD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada1 Hangul Syllable-ADA1 | ada0 Hangul Syllable-ADA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada3 Hangul Syllable-ADA3 | ada2 Hangul Syllable-ADA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada5 Hangul Syllable-ADA5 | ada4 Hangul Syllable-ADA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada7 Hangul Syllable-ADA7 | ada6 Hangul Syllable-ADA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ada9 Hangul Syllable-ADA9 | ada8 Hangul Syllable-ADA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adab Hangul Syllable-ADAB | adaa Hangul Syllable-ADAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adad Hangul Syllable-ADAD | adac Hangul Syllable-ADAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adaf Hangul Syllable-ADAF | adae Hangul Syllable-ADAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb1 Hangul Syllable-ADB1 | adb0 Hangul Syllable-ADB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb3 Hangul Syllable-ADB3 | adb2 Hangul Syllable-ADB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb5 Hangul Syllable-ADB5 | adb4 Hangul Syllable-ADB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb7 Hangul Syllable-ADB7 | adb6 Hangul Syllable-ADB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adb9 Hangul Syllable-ADB9 | adb8 Hangul Syllable-ADB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbb Hangul Syllable-ADBB | adba Hangul Syllable-ADBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbd Hangul Syllable-ADBD | adbc Hangul Syllable-ADBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adbf Hangul Syllable-ADBF | adbe Hangul Syllable-ADBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc1 Hangul Syllable-ADC1 | adc0 Hangul Syllable-ADC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc3 Hangul Syllable-ADC3 | adc2 Hangul Syllable-ADC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc5 Hangul Syllable-ADC5 | adc4 Hangul Syllable-ADC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc7 Hangul Syllable-ADC7 | adc6 Hangul Syllable-ADC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adc9 Hangul Syllable-ADC9 | adc8 Hangul Syllable-ADC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcb Hangul Syllable-ADCB | adca Hangul Syllable-ADCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcd Hangul Syllable-ADCD | adcc Hangul Syllable-ADCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adcf Hangul Syllable-ADCF | adce Hangul Syllable-ADCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add1 Hangul Syllable-ADD1 | add0 Hangul Syllable-ADD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add3 Hangul Syllable-ADD3 | add2 Hangul Syllable-ADD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add5 Hangul Syllable-ADD5 | add4 Hangul Syllable-ADD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add7 Hangul Syllable-ADD7 | add6 Hangul Syllable-ADD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* add9 Hangul Syllable-ADD9 | add8 Hangul Syllable-ADD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addb Hangul Syllable-ADDB | adda Hangul Syllable-ADDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addd Hangul Syllable-ADDD | addc Hangul Syllable-ADDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* addf Hangul Syllable-ADDF | adde Hangul Syllable-ADDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade1 Hangul Syllable-ADE1 | ade0 Hangul Syllable-ADE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade3 Hangul Syllable-ADE3 | ade2 Hangul Syllable-ADE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade5 Hangul Syllable-ADE5 | ade4 Hangul Syllable-ADE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade7 Hangul Syllable-ADE7 | ade6 Hangul Syllable-ADE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ade9 Hangul Syllable-ADE9 | ade8 Hangul Syllable-ADE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adeb Hangul Syllable-ADEB | adea Hangul Syllable-ADEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aded Hangul Syllable-ADED | adec Hangul Syllable-ADEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adef Hangul Syllable-ADEF | adee Hangul Syllable-ADEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf1 Hangul Syllable-ADF1 | adf0 Hangul Syllable-ADF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf3 Hangul Syllable-ADF3 | adf2 Hangul Syllable-ADF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf5 Hangul Syllable-ADF5 | adf4 Hangul Syllable-ADF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf7 Hangul Syllable-ADF7 | adf6 Hangul Syllable-ADF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adf9 Hangul Syllable-ADF9 | adf8 Hangul Syllable-ADF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adfb Hangul Syllable-ADFB | adfa Hangul Syllable-ADFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adfd Hangul Syllable-ADFD | adfc Hangul Syllable-ADFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* adff Hangul Syllable-ADFF | adfe Hangul Syllable-ADFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae01 Hangul Syllable-AE01 | ae00 Hangul Syllable-AE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae03 Hangul Syllable-AE03 | ae02 Hangul Syllable-AE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae05 Hangul Syllable-AE05 | ae04 Hangul Syllable-AE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae07 Hangul Syllable-AE07 | ae06 Hangul Syllable-AE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae09 Hangul Syllable-AE09 | ae08 Hangul Syllable-AE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0b Hangul Syllable-AE0B | ae0a Hangul Syllable-AE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0d Hangul Syllable-AE0D | ae0c Hangul Syllable-AE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae0f Hangul Syllable-AE0F | ae0e Hangul Syllable-AE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae11 Hangul Syllable-AE11 | ae10 Hangul Syllable-AE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae13 Hangul Syllable-AE13 | ae12 Hangul Syllable-AE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae15 Hangul Syllable-AE15 | ae14 Hangul Syllable-AE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae17 Hangul Syllable-AE17 | ae16 Hangul Syllable-AE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae19 Hangul Syllable-AE19 | ae18 Hangul Syllable-AE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1b Hangul Syllable-AE1B | ae1a Hangul Syllable-AE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1d Hangul Syllable-AE1D | ae1c Hangul Syllable-AE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae1f Hangul Syllable-AE1F | ae1e Hangul Syllable-AE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae21 Hangul Syllable-AE21 | ae20 Hangul Syllable-AE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae23 Hangul Syllable-AE23 | ae22 Hangul Syllable-AE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae25 Hangul Syllable-AE25 | ae24 Hangul Syllable-AE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae27 Hangul Syllable-AE27 | ae26 Hangul Syllable-AE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae29 Hangul Syllable-AE29 | ae28 Hangul Syllable-AE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2b Hangul Syllable-AE2B | ae2a Hangul Syllable-AE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2d Hangul Syllable-AE2D | ae2c Hangul Syllable-AE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae2f Hangul Syllable-AE2F | ae2e Hangul Syllable-AE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae31 Hangul Syllable-AE31 | ae30 Hangul Syllable-AE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae33 Hangul Syllable-AE33 | ae32 Hangul Syllable-AE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae35 Hangul Syllable-AE35 | ae34 Hangul Syllable-AE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae37 Hangul Syllable-AE37 | ae36 Hangul Syllable-AE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae39 Hangul Syllable-AE39 | ae38 Hangul Syllable-AE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3b Hangul Syllable-AE3B | ae3a Hangul Syllable-AE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3d Hangul Syllable-AE3D | ae3c Hangul Syllable-AE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae3f Hangul Syllable-AE3F | ae3e Hangul Syllable-AE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae41 Hangul Syllable-AE41 | ae40 Hangul Syllable-AE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae43 Hangul Syllable-AE43 | ae42 Hangul Syllable-AE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae45 Hangul Syllable-AE45 | ae44 Hangul Syllable-AE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae47 Hangul Syllable-AE47 | ae46 Hangul Syllable-AE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae49 Hangul Syllable-AE49 | ae48 Hangul Syllable-AE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4b Hangul Syllable-AE4B | ae4a Hangul Syllable-AE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4d Hangul Syllable-AE4D | ae4c Hangul Syllable-AE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae4f Hangul Syllable-AE4F | ae4e Hangul Syllable-AE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae51 Hangul Syllable-AE51 | ae50 Hangul Syllable-AE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae53 Hangul Syllable-AE53 | ae52 Hangul Syllable-AE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae55 Hangul Syllable-AE55 | ae54 Hangul Syllable-AE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae57 Hangul Syllable-AE57 | ae56 Hangul Syllable-AE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae59 Hangul Syllable-AE59 | ae58 Hangul Syllable-AE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5b Hangul Syllable-AE5B | ae5a Hangul Syllable-AE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5d Hangul Syllable-AE5D | ae5c Hangul Syllable-AE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae5f Hangul Syllable-AE5F | ae5e Hangul Syllable-AE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae61 Hangul Syllable-AE61 | ae60 Hangul Syllable-AE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae63 Hangul Syllable-AE63 | ae62 Hangul Syllable-AE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae65 Hangul Syllable-AE65 | ae64 Hangul Syllable-AE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae67 Hangul Syllable-AE67 | ae66 Hangul Syllable-AE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae69 Hangul Syllable-AE69 | ae68 Hangul Syllable-AE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6b Hangul Syllable-AE6B | ae6a Hangul Syllable-AE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6d Hangul Syllable-AE6D | ae6c Hangul Syllable-AE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae6f Hangul Syllable-AE6F | ae6e Hangul Syllable-AE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae71 Hangul Syllable-AE71 | ae70 Hangul Syllable-AE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae73 Hangul Syllable-AE73 | ae72 Hangul Syllable-AE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae75 Hangul Syllable-AE75 | ae74 Hangul Syllable-AE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae77 Hangul Syllable-AE77 | ae76 Hangul Syllable-AE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae79 Hangul Syllable-AE79 | ae78 Hangul Syllable-AE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7b Hangul Syllable-AE7B | ae7a Hangul Syllable-AE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7d Hangul Syllable-AE7D | ae7c Hangul Syllable-AE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae7f Hangul Syllable-AE7F | ae7e Hangul Syllable-AE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae81 Hangul Syllable-AE81 | ae80 Hangul Syllable-AE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae83 Hangul Syllable-AE83 | ae82 Hangul Syllable-AE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae85 Hangul Syllable-AE85 | ae84 Hangul Syllable-AE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae87 Hangul Syllable-AE87 | ae86 Hangul Syllable-AE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae89 Hangul Syllable-AE89 | ae88 Hangul Syllable-AE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8b Hangul Syllable-AE8B | ae8a Hangul Syllable-AE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8d Hangul Syllable-AE8D | ae8c Hangul Syllable-AE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae8f Hangul Syllable-AE8F | ae8e Hangul Syllable-AE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae91 Hangul Syllable-AE91 | ae90 Hangul Syllable-AE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae93 Hangul Syllable-AE93 | ae92 Hangul Syllable-AE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae95 Hangul Syllable-AE95 | ae94 Hangul Syllable-AE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae97 Hangul Syllable-AE97 | ae96 Hangul Syllable-AE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae99 Hangul Syllable-AE99 | ae98 Hangul Syllable-AE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9b Hangul Syllable-AE9B | ae9a Hangul Syllable-AE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9d Hangul Syllable-AE9D | ae9c Hangul Syllable-AE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ae9f Hangul Syllable-AE9F | ae9e Hangul Syllable-AE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea1 Hangul Syllable-AEA1 | aea0 Hangul Syllable-AEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea3 Hangul Syllable-AEA3 | aea2 Hangul Syllable-AEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea5 Hangul Syllable-AEA5 | aea4 Hangul Syllable-AEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea7 Hangul Syllable-AEA7 | aea6 Hangul Syllable-AEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aea9 Hangul Syllable-AEA9 | aea8 Hangul Syllable-AEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeab Hangul Syllable-AEAB | aeaa Hangul Syllable-AEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aead Hangul Syllable-AEAD | aeac Hangul Syllable-AEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeaf Hangul Syllable-AEAF | aeae Hangul Syllable-AEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb1 Hangul Syllable-AEB1 | aeb0 Hangul Syllable-AEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb3 Hangul Syllable-AEB3 | aeb2 Hangul Syllable-AEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb5 Hangul Syllable-AEB5 | aeb4 Hangul Syllable-AEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb7 Hangul Syllable-AEB7 | aeb6 Hangul Syllable-AEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeb9 Hangul Syllable-AEB9 | aeb8 Hangul Syllable-AEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebb Hangul Syllable-AEBB | aeba Hangul Syllable-AEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebd Hangul Syllable-AEBD | aebc Hangul Syllable-AEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aebf Hangul Syllable-AEBF | aebe Hangul Syllable-AEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec1 Hangul Syllable-AEC1 | aec0 Hangul Syllable-AEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec3 Hangul Syllable-AEC3 | aec2 Hangul Syllable-AEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec5 Hangul Syllable-AEC5 | aec4 Hangul Syllable-AEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec7 Hangul Syllable-AEC7 | aec6 Hangul Syllable-AEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aec9 Hangul Syllable-AEC9 | aec8 Hangul Syllable-AEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecb Hangul Syllable-AECB | aeca Hangul Syllable-AECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecd Hangul Syllable-AECD | aecc Hangul Syllable-AECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aecf Hangul Syllable-AECF | aece Hangul Syllable-AECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed1 Hangul Syllable-AED1 | aed0 Hangul Syllable-AED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed3 Hangul Syllable-AED3 | aed2 Hangul Syllable-AED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed5 Hangul Syllable-AED5 | aed4 Hangul Syllable-AED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed7 Hangul Syllable-AED7 | aed6 Hangul Syllable-AED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aed9 Hangul Syllable-AED9 | aed8 Hangul Syllable-AED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedb Hangul Syllable-AEDB | aeda Hangul Syllable-AEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedd Hangul Syllable-AEDD | aedc Hangul Syllable-AEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aedf Hangul Syllable-AEDF | aede Hangul Syllable-AEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee1 Hangul Syllable-AEE1 | aee0 Hangul Syllable-AEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee3 Hangul Syllable-AEE3 | aee2 Hangul Syllable-AEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee5 Hangul Syllable-AEE5 | aee4 Hangul Syllable-AEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee7 Hangul Syllable-AEE7 | aee6 Hangul Syllable-AEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aee9 Hangul Syllable-AEE9 | aee8 Hangul Syllable-AEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeeb Hangul Syllable-AEEB | aeea Hangul Syllable-AEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeed Hangul Syllable-AEED | aeec Hangul Syllable-AEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeef Hangul Syllable-AEEF | aeee Hangul Syllable-AEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef1 Hangul Syllable-AEF1 | aef0 Hangul Syllable-AEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef3 Hangul Syllable-AEF3 | aef2 Hangul Syllable-AEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef5 Hangul Syllable-AEF5 | aef4 Hangul Syllable-AEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef7 Hangul Syllable-AEF7 | aef6 Hangul Syllable-AEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aef9 Hangul Syllable-AEF9 | aef8 Hangul Syllable-AEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aefb Hangul Syllable-AEFB | aefa Hangul Syllable-AEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aefd Hangul Syllable-AEFD | aefc Hangul Syllable-AEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aeff Hangul Syllable-AEFF | aefe Hangul Syllable-AEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af01 Hangul Syllable-AF01 | af00 Hangul Syllable-AF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af03 Hangul Syllable-AF03 | af02 Hangul Syllable-AF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af05 Hangul Syllable-AF05 | af04 Hangul Syllable-AF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af07 Hangul Syllable-AF07 | af06 Hangul Syllable-AF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af09 Hangul Syllable-AF09 | af08 Hangul Syllable-AF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0b Hangul Syllable-AF0B | af0a Hangul Syllable-AF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0d Hangul Syllable-AF0D | af0c Hangul Syllable-AF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af0f Hangul Syllable-AF0F | af0e Hangul Syllable-AF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af11 Hangul Syllable-AF11 | af10 Hangul Syllable-AF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af13 Hangul Syllable-AF13 | af12 Hangul Syllable-AF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af15 Hangul Syllable-AF15 | af14 Hangul Syllable-AF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af17 Hangul Syllable-AF17 | af16 Hangul Syllable-AF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af19 Hangul Syllable-AF19 | af18 Hangul Syllable-AF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1b Hangul Syllable-AF1B | af1a Hangul Syllable-AF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1d Hangul Syllable-AF1D | af1c Hangul Syllable-AF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af1f Hangul Syllable-AF1F | af1e Hangul Syllable-AF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af21 Hangul Syllable-AF21 | af20 Hangul Syllable-AF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af23 Hangul Syllable-AF23 | af22 Hangul Syllable-AF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af25 Hangul Syllable-AF25 | af24 Hangul Syllable-AF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af27 Hangul Syllable-AF27 | af26 Hangul Syllable-AF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af29 Hangul Syllable-AF29 | af28 Hangul Syllable-AF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2b Hangul Syllable-AF2B | af2a Hangul Syllable-AF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2d Hangul Syllable-AF2D | af2c Hangul Syllable-AF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af2f Hangul Syllable-AF2F | af2e Hangul Syllable-AF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af31 Hangul Syllable-AF31 | af30 Hangul Syllable-AF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af33 Hangul Syllable-AF33 | af32 Hangul Syllable-AF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af35 Hangul Syllable-AF35 | af34 Hangul Syllable-AF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af37 Hangul Syllable-AF37 | af36 Hangul Syllable-AF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af39 Hangul Syllable-AF39 | af38 Hangul Syllable-AF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3b Hangul Syllable-AF3B | af3a Hangul Syllable-AF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3d Hangul Syllable-AF3D | af3c Hangul Syllable-AF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af3f Hangul Syllable-AF3F | af3e Hangul Syllable-AF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af41 Hangul Syllable-AF41 | af40 Hangul Syllable-AF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af43 Hangul Syllable-AF43 | af42 Hangul Syllable-AF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af45 Hangul Syllable-AF45 | af44 Hangul Syllable-AF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af47 Hangul Syllable-AF47 | af46 Hangul Syllable-AF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af49 Hangul Syllable-AF49 | af48 Hangul Syllable-AF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4b Hangul Syllable-AF4B | af4a Hangul Syllable-AF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4d Hangul Syllable-AF4D | af4c Hangul Syllable-AF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af4f Hangul Syllable-AF4F | af4e Hangul Syllable-AF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af51 Hangul Syllable-AF51 | af50 Hangul Syllable-AF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af53 Hangul Syllable-AF53 | af52 Hangul Syllable-AF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af55 Hangul Syllable-AF55 | af54 Hangul Syllable-AF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af57 Hangul Syllable-AF57 | af56 Hangul Syllable-AF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af59 Hangul Syllable-AF59 | af58 Hangul Syllable-AF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5b Hangul Syllable-AF5B | af5a Hangul Syllable-AF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5d Hangul Syllable-AF5D | af5c Hangul Syllable-AF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af5f Hangul Syllable-AF5F | af5e Hangul Syllable-AF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af61 Hangul Syllable-AF61 | af60 Hangul Syllable-AF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af63 Hangul Syllable-AF63 | af62 Hangul Syllable-AF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af65 Hangul Syllable-AF65 | af64 Hangul Syllable-AF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af67 Hangul Syllable-AF67 | af66 Hangul Syllable-AF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af69 Hangul Syllable-AF69 | af68 Hangul Syllable-AF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6b Hangul Syllable-AF6B | af6a Hangul Syllable-AF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6d Hangul Syllable-AF6D | af6c Hangul Syllable-AF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af6f Hangul Syllable-AF6F | af6e Hangul Syllable-AF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af71 Hangul Syllable-AF71 | af70 Hangul Syllable-AF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af73 Hangul Syllable-AF73 | af72 Hangul Syllable-AF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af75 Hangul Syllable-AF75 | af74 Hangul Syllable-AF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af77 Hangul Syllable-AF77 | af76 Hangul Syllable-AF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af79 Hangul Syllable-AF79 | af78 Hangul Syllable-AF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7b Hangul Syllable-AF7B | af7a Hangul Syllable-AF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7d Hangul Syllable-AF7D | af7c Hangul Syllable-AF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af7f Hangul Syllable-AF7F | af7e Hangul Syllable-AF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af81 Hangul Syllable-AF81 | af80 Hangul Syllable-AF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af83 Hangul Syllable-AF83 | af82 Hangul Syllable-AF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af85 Hangul Syllable-AF85 | af84 Hangul Syllable-AF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af87 Hangul Syllable-AF87 | af86 Hangul Syllable-AF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af89 Hangul Syllable-AF89 | af88 Hangul Syllable-AF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8b Hangul Syllable-AF8B | af8a Hangul Syllable-AF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8d Hangul Syllable-AF8D | af8c Hangul Syllable-AF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af8f Hangul Syllable-AF8F | af8e Hangul Syllable-AF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af91 Hangul Syllable-AF91 | af90 Hangul Syllable-AF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af93 Hangul Syllable-AF93 | af92 Hangul Syllable-AF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af95 Hangul Syllable-AF95 | af94 Hangul Syllable-AF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af97 Hangul Syllable-AF97 | af96 Hangul Syllable-AF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af99 Hangul Syllable-AF99 | af98 Hangul Syllable-AF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9b Hangul Syllable-AF9B | af9a Hangul Syllable-AF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9d Hangul Syllable-AF9D | af9c Hangul Syllable-AF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* af9f Hangul Syllable-AF9F | af9e Hangul Syllable-AF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa1 Hangul Syllable-AFA1 | afa0 Hangul Syllable-AFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa3 Hangul Syllable-AFA3 | afa2 Hangul Syllable-AFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa5 Hangul Syllable-AFA5 | afa4 Hangul Syllable-AFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa7 Hangul Syllable-AFA7 | afa6 Hangul Syllable-AFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afa9 Hangul Syllable-AFA9 | afa8 Hangul Syllable-AFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afab Hangul Syllable-AFAB | afaa Hangul Syllable-AFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afad Hangul Syllable-AFAD | afac Hangul Syllable-AFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afaf Hangul Syllable-AFAF | afae Hangul Syllable-AFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb1 Hangul Syllable-AFB1 | afb0 Hangul Syllable-AFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb3 Hangul Syllable-AFB3 | afb2 Hangul Syllable-AFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb5 Hangul Syllable-AFB5 | afb4 Hangul Syllable-AFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb7 Hangul Syllable-AFB7 | afb6 Hangul Syllable-AFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afb9 Hangul Syllable-AFB9 | afb8 Hangul Syllable-AFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbb Hangul Syllable-AFBB | afba Hangul Syllable-AFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbd Hangul Syllable-AFBD | afbc Hangul Syllable-AFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afbf Hangul Syllable-AFBF | afbe Hangul Syllable-AFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc1 Hangul Syllable-AFC1 | afc0 Hangul Syllable-AFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc3 Hangul Syllable-AFC3 | afc2 Hangul Syllable-AFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc5 Hangul Syllable-AFC5 | afc4 Hangul Syllable-AFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc7 Hangul Syllable-AFC7 | afc6 Hangul Syllable-AFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afc9 Hangul Syllable-AFC9 | afc8 Hangul Syllable-AFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcb Hangul Syllable-AFCB | afca Hangul Syllable-AFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcd Hangul Syllable-AFCD | afcc Hangul Syllable-AFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afcf Hangul Syllable-AFCF | afce Hangul Syllable-AFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd1 Hangul Syllable-AFD1 | afd0 Hangul Syllable-AFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd3 Hangul Syllable-AFD3 | afd2 Hangul Syllable-AFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd5 Hangul Syllable-AFD5 | afd4 Hangul Syllable-AFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd7 Hangul Syllable-AFD7 | afd6 Hangul Syllable-AFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afd9 Hangul Syllable-AFD9 | afd8 Hangul Syllable-AFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdb Hangul Syllable-AFDB | afda Hangul Syllable-AFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdd Hangul Syllable-AFDD | afdc Hangul Syllable-AFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afdf Hangul Syllable-AFDF | afde Hangul Syllable-AFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe1 Hangul Syllable-AFE1 | afe0 Hangul Syllable-AFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe3 Hangul Syllable-AFE3 | afe2 Hangul Syllable-AFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe5 Hangul Syllable-AFE5 | afe4 Hangul Syllable-AFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe7 Hangul Syllable-AFE7 | afe6 Hangul Syllable-AFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afe9 Hangul Syllable-AFE9 | afe8 Hangul Syllable-AFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afeb Hangul Syllable-AFEB | afea Hangul Syllable-AFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afed Hangul Syllable-AFED | afec Hangul Syllable-AFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afef Hangul Syllable-AFEF | afee Hangul Syllable-AFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff1 Hangul Syllable-AFF1 | aff0 Hangul Syllable-AFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff3 Hangul Syllable-AFF3 | aff2 Hangul Syllable-AFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff5 Hangul Syllable-AFF5 | aff4 Hangul Syllable-AFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff7 Hangul Syllable-AFF7 | aff6 Hangul Syllable-AFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* aff9 Hangul Syllable-AFF9 | aff8 Hangul Syllable-AFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* affb Hangul Syllable-AFFB | affa Hangul Syllable-AFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* affd Hangul Syllable-AFFD | affc Hangul Syllable-AFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* afff Hangul Syllable-AFFF | affe Hangul Syllable-AFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b001 Hangul Syllable-B001 | b000 Hangul Syllable-B000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b003 Hangul Syllable-B003 | b002 Hangul Syllable-B002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b005 Hangul Syllable-B005 | b004 Hangul Syllable-B004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b007 Hangul Syllable-B007 | b006 Hangul Syllable-B006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b009 Hangul Syllable-B009 | b008 Hangul Syllable-B008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00b Hangul Syllable-B00B | b00a Hangul Syllable-B00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00d Hangul Syllable-B00D | b00c Hangul Syllable-B00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b00f Hangul Syllable-B00F | b00e Hangul Syllable-B00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b011 Hangul Syllable-B011 | b010 Hangul Syllable-B010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b013 Hangul Syllable-B013 | b012 Hangul Syllable-B012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b015 Hangul Syllable-B015 | b014 Hangul Syllable-B014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b017 Hangul Syllable-B017 | b016 Hangul Syllable-B016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b019 Hangul Syllable-B019 | b018 Hangul Syllable-B018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01b Hangul Syllable-B01B | b01a Hangul Syllable-B01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01d Hangul Syllable-B01D | b01c Hangul Syllable-B01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b01f Hangul Syllable-B01F | b01e Hangul Syllable-B01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b021 Hangul Syllable-B021 | b020 Hangul Syllable-B020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b023 Hangul Syllable-B023 | b022 Hangul Syllable-B022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b025 Hangul Syllable-B025 | b024 Hangul Syllable-B024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b027 Hangul Syllable-B027 | b026 Hangul Syllable-B026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b029 Hangul Syllable-B029 | b028 Hangul Syllable-B028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02b Hangul Syllable-B02B | b02a Hangul Syllable-B02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02d Hangul Syllable-B02D | b02c Hangul Syllable-B02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b02f Hangul Syllable-B02F | b02e Hangul Syllable-B02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b031 Hangul Syllable-B031 | b030 Hangul Syllable-B030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b033 Hangul Syllable-B033 | b032 Hangul Syllable-B032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b035 Hangul Syllable-B035 | b034 Hangul Syllable-B034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b037 Hangul Syllable-B037 | b036 Hangul Syllable-B036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b039 Hangul Syllable-B039 | b038 Hangul Syllable-B038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03b Hangul Syllable-B03B | b03a Hangul Syllable-B03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03d Hangul Syllable-B03D | b03c Hangul Syllable-B03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b03f Hangul Syllable-B03F | b03e Hangul Syllable-B03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b041 Hangul Syllable-B041 | b040 Hangul Syllable-B040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b043 Hangul Syllable-B043 | b042 Hangul Syllable-B042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b045 Hangul Syllable-B045 | b044 Hangul Syllable-B044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b047 Hangul Syllable-B047 | b046 Hangul Syllable-B046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b049 Hangul Syllable-B049 | b048 Hangul Syllable-B048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04b Hangul Syllable-B04B | b04a Hangul Syllable-B04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04d Hangul Syllable-B04D | b04c Hangul Syllable-B04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b04f Hangul Syllable-B04F | b04e Hangul Syllable-B04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b051 Hangul Syllable-B051 | b050 Hangul Syllable-B050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b053 Hangul Syllable-B053 | b052 Hangul Syllable-B052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b055 Hangul Syllable-B055 | b054 Hangul Syllable-B054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b057 Hangul Syllable-B057 | b056 Hangul Syllable-B056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b059 Hangul Syllable-B059 | b058 Hangul Syllable-B058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05b Hangul Syllable-B05B | b05a Hangul Syllable-B05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05d Hangul Syllable-B05D | b05c Hangul Syllable-B05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b05f Hangul Syllable-B05F | b05e Hangul Syllable-B05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b061 Hangul Syllable-B061 | b060 Hangul Syllable-B060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b063 Hangul Syllable-B063 | b062 Hangul Syllable-B062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b065 Hangul Syllable-B065 | b064 Hangul Syllable-B064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b067 Hangul Syllable-B067 | b066 Hangul Syllable-B066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b069 Hangul Syllable-B069 | b068 Hangul Syllable-B068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06b Hangul Syllable-B06B | b06a Hangul Syllable-B06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06d Hangul Syllable-B06D | b06c Hangul Syllable-B06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b06f Hangul Syllable-B06F | b06e Hangul Syllable-B06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b071 Hangul Syllable-B071 | b070 Hangul Syllable-B070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b073 Hangul Syllable-B073 | b072 Hangul Syllable-B072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b075 Hangul Syllable-B075 | b074 Hangul Syllable-B074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b077 Hangul Syllable-B077 | b076 Hangul Syllable-B076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b079 Hangul Syllable-B079 | b078 Hangul Syllable-B078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07b Hangul Syllable-B07B | b07a Hangul Syllable-B07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07d Hangul Syllable-B07D | b07c Hangul Syllable-B07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b07f Hangul Syllable-B07F | b07e Hangul Syllable-B07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b081 Hangul Syllable-B081 | b080 Hangul Syllable-B080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b083 Hangul Syllable-B083 | b082 Hangul Syllable-B082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b085 Hangul Syllable-B085 | b084 Hangul Syllable-B084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b087 Hangul Syllable-B087 | b086 Hangul Syllable-B086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b089 Hangul Syllable-B089 | b088 Hangul Syllable-B088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08b Hangul Syllable-B08B | b08a Hangul Syllable-B08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08d Hangul Syllable-B08D | b08c Hangul Syllable-B08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b08f Hangul Syllable-B08F | b08e Hangul Syllable-B08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b091 Hangul Syllable-B091 | b090 Hangul Syllable-B090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b093 Hangul Syllable-B093 | b092 Hangul Syllable-B092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b095 Hangul Syllable-B095 | b094 Hangul Syllable-B094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b097 Hangul Syllable-B097 | b096 Hangul Syllable-B096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b099 Hangul Syllable-B099 | b098 Hangul Syllable-B098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09b Hangul Syllable-B09B | b09a Hangul Syllable-B09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09d Hangul Syllable-B09D | b09c Hangul Syllable-B09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b09f Hangul Syllable-B09F | b09e Hangul Syllable-B09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a1 Hangul Syllable-B0A1 | b0a0 Hangul Syllable-B0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a3 Hangul Syllable-B0A3 | b0a2 Hangul Syllable-B0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a5 Hangul Syllable-B0A5 | b0a4 Hangul Syllable-B0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a7 Hangul Syllable-B0A7 | b0a6 Hangul Syllable-B0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0a9 Hangul Syllable-B0A9 | b0a8 Hangul Syllable-B0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ab Hangul Syllable-B0AB | b0aa Hangul Syllable-B0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ad Hangul Syllable-B0AD | b0ac Hangul Syllable-B0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0af Hangul Syllable-B0AF | b0ae Hangul Syllable-B0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b1 Hangul Syllable-B0B1 | b0b0 Hangul Syllable-B0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b3 Hangul Syllable-B0B3 | b0b2 Hangul Syllable-B0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b5 Hangul Syllable-B0B5 | b0b4 Hangul Syllable-B0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b7 Hangul Syllable-B0B7 | b0b6 Hangul Syllable-B0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0b9 Hangul Syllable-B0B9 | b0b8 Hangul Syllable-B0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bb Hangul Syllable-B0BB | b0ba Hangul Syllable-B0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bd Hangul Syllable-B0BD | b0bc Hangul Syllable-B0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0bf Hangul Syllable-B0BF | b0be Hangul Syllable-B0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c1 Hangul Syllable-B0C1 | b0c0 Hangul Syllable-B0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c3 Hangul Syllable-B0C3 | b0c2 Hangul Syllable-B0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c5 Hangul Syllable-B0C5 | b0c4 Hangul Syllable-B0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c7 Hangul Syllable-B0C7 | b0c6 Hangul Syllable-B0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0c9 Hangul Syllable-B0C9 | b0c8 Hangul Syllable-B0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cb Hangul Syllable-B0CB | b0ca Hangul Syllable-B0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cd Hangul Syllable-B0CD | b0cc Hangul Syllable-B0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0cf Hangul Syllable-B0CF | b0ce Hangul Syllable-B0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d1 Hangul Syllable-B0D1 | b0d0 Hangul Syllable-B0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d3 Hangul Syllable-B0D3 | b0d2 Hangul Syllable-B0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d5 Hangul Syllable-B0D5 | b0d4 Hangul Syllable-B0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d7 Hangul Syllable-B0D7 | b0d6 Hangul Syllable-B0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0d9 Hangul Syllable-B0D9 | b0d8 Hangul Syllable-B0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0db Hangul Syllable-B0DB | b0da Hangul Syllable-B0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0dd Hangul Syllable-B0DD | b0dc Hangul Syllable-B0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0df Hangul Syllable-B0DF | b0de Hangul Syllable-B0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e1 Hangul Syllable-B0E1 | b0e0 Hangul Syllable-B0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e3 Hangul Syllable-B0E3 | b0e2 Hangul Syllable-B0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e5 Hangul Syllable-B0E5 | b0e4 Hangul Syllable-B0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e7 Hangul Syllable-B0E7 | b0e6 Hangul Syllable-B0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0e9 Hangul Syllable-B0E9 | b0e8 Hangul Syllable-B0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0eb Hangul Syllable-B0EB | b0ea Hangul Syllable-B0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ed Hangul Syllable-B0ED | b0ec Hangul Syllable-B0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ef Hangul Syllable-B0EF | b0ee Hangul Syllable-B0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f1 Hangul Syllable-B0F1 | b0f0 Hangul Syllable-B0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f3 Hangul Syllable-B0F3 | b0f2 Hangul Syllable-B0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f5 Hangul Syllable-B0F5 | b0f4 Hangul Syllable-B0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f7 Hangul Syllable-B0F7 | b0f6 Hangul Syllable-B0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0f9 Hangul Syllable-B0F9 | b0f8 Hangul Syllable-B0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0fb Hangul Syllable-B0FB | b0fa Hangul Syllable-B0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0fd Hangul Syllable-B0FD | b0fc Hangul Syllable-B0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b0ff Hangul Syllable-B0FF | b0fe Hangul Syllable-B0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b101 Hangul Syllable-B101 | b100 Hangul Syllable-B100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b103 Hangul Syllable-B103 | b102 Hangul Syllable-B102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b105 Hangul Syllable-B105 | b104 Hangul Syllable-B104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b107 Hangul Syllable-B107 | b106 Hangul Syllable-B106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b109 Hangul Syllable-B109 | b108 Hangul Syllable-B108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10b Hangul Syllable-B10B | b10a Hangul Syllable-B10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10d Hangul Syllable-B10D | b10c Hangul Syllable-B10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b10f Hangul Syllable-B10F | b10e Hangul Syllable-B10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b111 Hangul Syllable-B111 | b110 Hangul Syllable-B110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b113 Hangul Syllable-B113 | b112 Hangul Syllable-B112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b115 Hangul Syllable-B115 | b114 Hangul Syllable-B114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b117 Hangul Syllable-B117 | b116 Hangul Syllable-B116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b119 Hangul Syllable-B119 | b118 Hangul Syllable-B118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11b Hangul Syllable-B11B | b11a Hangul Syllable-B11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11d Hangul Syllable-B11D | b11c Hangul Syllable-B11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b11f Hangul Syllable-B11F | b11e Hangul Syllable-B11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b121 Hangul Syllable-B121 | b120 Hangul Syllable-B120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b123 Hangul Syllable-B123 | b122 Hangul Syllable-B122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b125 Hangul Syllable-B125 | b124 Hangul Syllable-B124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b127 Hangul Syllable-B127 | b126 Hangul Syllable-B126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b129 Hangul Syllable-B129 | b128 Hangul Syllable-B128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12b Hangul Syllable-B12B | b12a Hangul Syllable-B12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12d Hangul Syllable-B12D | b12c Hangul Syllable-B12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b12f Hangul Syllable-B12F | b12e Hangul Syllable-B12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b131 Hangul Syllable-B131 | b130 Hangul Syllable-B130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b133 Hangul Syllable-B133 | b132 Hangul Syllable-B132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b135 Hangul Syllable-B135 | b134 Hangul Syllable-B134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b137 Hangul Syllable-B137 | b136 Hangul Syllable-B136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b139 Hangul Syllable-B139 | b138 Hangul Syllable-B138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13b Hangul Syllable-B13B | b13a Hangul Syllable-B13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13d Hangul Syllable-B13D | b13c Hangul Syllable-B13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b13f Hangul Syllable-B13F | b13e Hangul Syllable-B13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b141 Hangul Syllable-B141 | b140 Hangul Syllable-B140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b143 Hangul Syllable-B143 | b142 Hangul Syllable-B142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b145 Hangul Syllable-B145 | b144 Hangul Syllable-B144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b147 Hangul Syllable-B147 | b146 Hangul Syllable-B146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b149 Hangul Syllable-B149 | b148 Hangul Syllable-B148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14b Hangul Syllable-B14B | b14a Hangul Syllable-B14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14d Hangul Syllable-B14D | b14c Hangul Syllable-B14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b14f Hangul Syllable-B14F | b14e Hangul Syllable-B14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b151 Hangul Syllable-B151 | b150 Hangul Syllable-B150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b153 Hangul Syllable-B153 | b152 Hangul Syllable-B152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b155 Hangul Syllable-B155 | b154 Hangul Syllable-B154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b157 Hangul Syllable-B157 | b156 Hangul Syllable-B156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b159 Hangul Syllable-B159 | b158 Hangul Syllable-B158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15b Hangul Syllable-B15B | b15a Hangul Syllable-B15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15d Hangul Syllable-B15D | b15c Hangul Syllable-B15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b15f Hangul Syllable-B15F | b15e Hangul Syllable-B15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b161 Hangul Syllable-B161 | b160 Hangul Syllable-B160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b163 Hangul Syllable-B163 | b162 Hangul Syllable-B162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b165 Hangul Syllable-B165 | b164 Hangul Syllable-B164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b167 Hangul Syllable-B167 | b166 Hangul Syllable-B166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b169 Hangul Syllable-B169 | b168 Hangul Syllable-B168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16b Hangul Syllable-B16B | b16a Hangul Syllable-B16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16d Hangul Syllable-B16D | b16c Hangul Syllable-B16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b16f Hangul Syllable-B16F | b16e Hangul Syllable-B16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b171 Hangul Syllable-B171 | b170 Hangul Syllable-B170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b173 Hangul Syllable-B173 | b172 Hangul Syllable-B172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b175 Hangul Syllable-B175 | b174 Hangul Syllable-B174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b177 Hangul Syllable-B177 | b176 Hangul Syllable-B176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b179 Hangul Syllable-B179 | b178 Hangul Syllable-B178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17b Hangul Syllable-B17B | b17a Hangul Syllable-B17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17d Hangul Syllable-B17D | b17c Hangul Syllable-B17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b17f Hangul Syllable-B17F | b17e Hangul Syllable-B17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b181 Hangul Syllable-B181 | b180 Hangul Syllable-B180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b183 Hangul Syllable-B183 | b182 Hangul Syllable-B182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b185 Hangul Syllable-B185 | b184 Hangul Syllable-B184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b187 Hangul Syllable-B187 | b186 Hangul Syllable-B186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b189 Hangul Syllable-B189 | b188 Hangul Syllable-B188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18b Hangul Syllable-B18B | b18a Hangul Syllable-B18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18d Hangul Syllable-B18D | b18c Hangul Syllable-B18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b18f Hangul Syllable-B18F | b18e Hangul Syllable-B18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b191 Hangul Syllable-B191 | b190 Hangul Syllable-B190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b193 Hangul Syllable-B193 | b192 Hangul Syllable-B192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b195 Hangul Syllable-B195 | b194 Hangul Syllable-B194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b197 Hangul Syllable-B197 | b196 Hangul Syllable-B196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b199 Hangul Syllable-B199 | b198 Hangul Syllable-B198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19b Hangul Syllable-B19B | b19a Hangul Syllable-B19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19d Hangul Syllable-B19D | b19c Hangul Syllable-B19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b19f Hangul Syllable-B19F | b19e Hangul Syllable-B19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a1 Hangul Syllable-B1A1 | b1a0 Hangul Syllable-B1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a3 Hangul Syllable-B1A3 | b1a2 Hangul Syllable-B1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a5 Hangul Syllable-B1A5 | b1a4 Hangul Syllable-B1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a7 Hangul Syllable-B1A7 | b1a6 Hangul Syllable-B1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1a9 Hangul Syllable-B1A9 | b1a8 Hangul Syllable-B1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ab Hangul Syllable-B1AB | b1aa Hangul Syllable-B1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ad Hangul Syllable-B1AD | b1ac Hangul Syllable-B1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1af Hangul Syllable-B1AF | b1ae Hangul Syllable-B1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b1 Hangul Syllable-B1B1 | b1b0 Hangul Syllable-B1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b3 Hangul Syllable-B1B3 | b1b2 Hangul Syllable-B1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b5 Hangul Syllable-B1B5 | b1b4 Hangul Syllable-B1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b7 Hangul Syllable-B1B7 | b1b6 Hangul Syllable-B1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1b9 Hangul Syllable-B1B9 | b1b8 Hangul Syllable-B1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bb Hangul Syllable-B1BB | b1ba Hangul Syllable-B1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bd Hangul Syllable-B1BD | b1bc Hangul Syllable-B1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1bf Hangul Syllable-B1BF | b1be Hangul Syllable-B1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c1 Hangul Syllable-B1C1 | b1c0 Hangul Syllable-B1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c3 Hangul Syllable-B1C3 | b1c2 Hangul Syllable-B1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c5 Hangul Syllable-B1C5 | b1c4 Hangul Syllable-B1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c7 Hangul Syllable-B1C7 | b1c6 Hangul Syllable-B1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1c9 Hangul Syllable-B1C9 | b1c8 Hangul Syllable-B1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cb Hangul Syllable-B1CB | b1ca Hangul Syllable-B1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cd Hangul Syllable-B1CD | b1cc Hangul Syllable-B1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1cf Hangul Syllable-B1CF | b1ce Hangul Syllable-B1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d1 Hangul Syllable-B1D1 | b1d0 Hangul Syllable-B1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d3 Hangul Syllable-B1D3 | b1d2 Hangul Syllable-B1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d5 Hangul Syllable-B1D5 | b1d4 Hangul Syllable-B1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d7 Hangul Syllable-B1D7 | b1d6 Hangul Syllable-B1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1d9 Hangul Syllable-B1D9 | b1d8 Hangul Syllable-B1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1db Hangul Syllable-B1DB | b1da Hangul Syllable-B1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1dd Hangul Syllable-B1DD | b1dc Hangul Syllable-B1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1df Hangul Syllable-B1DF | b1de Hangul Syllable-B1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e1 Hangul Syllable-B1E1 | b1e0 Hangul Syllable-B1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e3 Hangul Syllable-B1E3 | b1e2 Hangul Syllable-B1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e5 Hangul Syllable-B1E5 | b1e4 Hangul Syllable-B1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e7 Hangul Syllable-B1E7 | b1e6 Hangul Syllable-B1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1e9 Hangul Syllable-B1E9 | b1e8 Hangul Syllable-B1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1eb Hangul Syllable-B1EB | b1ea Hangul Syllable-B1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ed Hangul Syllable-B1ED | b1ec Hangul Syllable-B1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ef Hangul Syllable-B1EF | b1ee Hangul Syllable-B1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f1 Hangul Syllable-B1F1 | b1f0 Hangul Syllable-B1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f3 Hangul Syllable-B1F3 | b1f2 Hangul Syllable-B1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f5 Hangul Syllable-B1F5 | b1f4 Hangul Syllable-B1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f7 Hangul Syllable-B1F7 | b1f6 Hangul Syllable-B1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1f9 Hangul Syllable-B1F9 | b1f8 Hangul Syllable-B1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1fb Hangul Syllable-B1FB | b1fa Hangul Syllable-B1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1fd Hangul Syllable-B1FD | b1fc Hangul Syllable-B1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b1ff Hangul Syllable-B1FF | b1fe Hangul Syllable-B1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b201 Hangul Syllable-B201 | b200 Hangul Syllable-B200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b203 Hangul Syllable-B203 | b202 Hangul Syllable-B202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b205 Hangul Syllable-B205 | b204 Hangul Syllable-B204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b207 Hangul Syllable-B207 | b206 Hangul Syllable-B206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b209 Hangul Syllable-B209 | b208 Hangul Syllable-B208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20b Hangul Syllable-B20B | b20a Hangul Syllable-B20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20d Hangul Syllable-B20D | b20c Hangul Syllable-B20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b20f Hangul Syllable-B20F | b20e Hangul Syllable-B20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b211 Hangul Syllable-B211 | b210 Hangul Syllable-B210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b213 Hangul Syllable-B213 | b212 Hangul Syllable-B212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b215 Hangul Syllable-B215 | b214 Hangul Syllable-B214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b217 Hangul Syllable-B217 | b216 Hangul Syllable-B216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b219 Hangul Syllable-B219 | b218 Hangul Syllable-B218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21b Hangul Syllable-B21B | b21a Hangul Syllable-B21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21d Hangul Syllable-B21D | b21c Hangul Syllable-B21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b21f Hangul Syllable-B21F | b21e Hangul Syllable-B21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b221 Hangul Syllable-B221 | b220 Hangul Syllable-B220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b223 Hangul Syllable-B223 | b222 Hangul Syllable-B222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b225 Hangul Syllable-B225 | b224 Hangul Syllable-B224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b227 Hangul Syllable-B227 | b226 Hangul Syllable-B226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b229 Hangul Syllable-B229 | b228 Hangul Syllable-B228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22b Hangul Syllable-B22B | b22a Hangul Syllable-B22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22d Hangul Syllable-B22D | b22c Hangul Syllable-B22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b22f Hangul Syllable-B22F | b22e Hangul Syllable-B22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b231 Hangul Syllable-B231 | b230 Hangul Syllable-B230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b233 Hangul Syllable-B233 | b232 Hangul Syllable-B232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b235 Hangul Syllable-B235 | b234 Hangul Syllable-B234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b237 Hangul Syllable-B237 | b236 Hangul Syllable-B236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b239 Hangul Syllable-B239 | b238 Hangul Syllable-B238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23b Hangul Syllable-B23B | b23a Hangul Syllable-B23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23d Hangul Syllable-B23D | b23c Hangul Syllable-B23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b23f Hangul Syllable-B23F | b23e Hangul Syllable-B23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b241 Hangul Syllable-B241 | b240 Hangul Syllable-B240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b243 Hangul Syllable-B243 | b242 Hangul Syllable-B242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b245 Hangul Syllable-B245 | b244 Hangul Syllable-B244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b247 Hangul Syllable-B247 | b246 Hangul Syllable-B246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b249 Hangul Syllable-B249 | b248 Hangul Syllable-B248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24b Hangul Syllable-B24B | b24a Hangul Syllable-B24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24d Hangul Syllable-B24D | b24c Hangul Syllable-B24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b24f Hangul Syllable-B24F | b24e Hangul Syllable-B24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b251 Hangul Syllable-B251 | b250 Hangul Syllable-B250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b253 Hangul Syllable-B253 | b252 Hangul Syllable-B252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b255 Hangul Syllable-B255 | b254 Hangul Syllable-B254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b257 Hangul Syllable-B257 | b256 Hangul Syllable-B256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b259 Hangul Syllable-B259 | b258 Hangul Syllable-B258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25b Hangul Syllable-B25B | b25a Hangul Syllable-B25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25d Hangul Syllable-B25D | b25c Hangul Syllable-B25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b25f Hangul Syllable-B25F | b25e Hangul Syllable-B25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b261 Hangul Syllable-B261 | b260 Hangul Syllable-B260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b263 Hangul Syllable-B263 | b262 Hangul Syllable-B262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b265 Hangul Syllable-B265 | b264 Hangul Syllable-B264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b267 Hangul Syllable-B267 | b266 Hangul Syllable-B266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b269 Hangul Syllable-B269 | b268 Hangul Syllable-B268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26b Hangul Syllable-B26B | b26a Hangul Syllable-B26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26d Hangul Syllable-B26D | b26c Hangul Syllable-B26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b26f Hangul Syllable-B26F | b26e Hangul Syllable-B26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b271 Hangul Syllable-B271 | b270 Hangul Syllable-B270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b273 Hangul Syllable-B273 | b272 Hangul Syllable-B272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b275 Hangul Syllable-B275 | b274 Hangul Syllable-B274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b277 Hangul Syllable-B277 | b276 Hangul Syllable-B276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b279 Hangul Syllable-B279 | b278 Hangul Syllable-B278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27b Hangul Syllable-B27B | b27a Hangul Syllable-B27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27d Hangul Syllable-B27D | b27c Hangul Syllable-B27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b27f Hangul Syllable-B27F | b27e Hangul Syllable-B27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b281 Hangul Syllable-B281 | b280 Hangul Syllable-B280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b283 Hangul Syllable-B283 | b282 Hangul Syllable-B282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b285 Hangul Syllable-B285 | b284 Hangul Syllable-B284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b287 Hangul Syllable-B287 | b286 Hangul Syllable-B286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b289 Hangul Syllable-B289 | b288 Hangul Syllable-B288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28b Hangul Syllable-B28B | b28a Hangul Syllable-B28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28d Hangul Syllable-B28D | b28c Hangul Syllable-B28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b28f Hangul Syllable-B28F | b28e Hangul Syllable-B28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b291 Hangul Syllable-B291 | b290 Hangul Syllable-B290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b293 Hangul Syllable-B293 | b292 Hangul Syllable-B292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b295 Hangul Syllable-B295 | b294 Hangul Syllable-B294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b297 Hangul Syllable-B297 | b296 Hangul Syllable-B296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b299 Hangul Syllable-B299 | b298 Hangul Syllable-B298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29b Hangul Syllable-B29B | b29a Hangul Syllable-B29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29d Hangul Syllable-B29D | b29c Hangul Syllable-B29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b29f Hangul Syllable-B29F | b29e Hangul Syllable-B29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a1 Hangul Syllable-B2A1 | b2a0 Hangul Syllable-B2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a3 Hangul Syllable-B2A3 | b2a2 Hangul Syllable-B2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a5 Hangul Syllable-B2A5 | b2a4 Hangul Syllable-B2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a7 Hangul Syllable-B2A7 | b2a6 Hangul Syllable-B2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2a9 Hangul Syllable-B2A9 | b2a8 Hangul Syllable-B2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ab Hangul Syllable-B2AB | b2aa Hangul Syllable-B2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ad Hangul Syllable-B2AD | b2ac Hangul Syllable-B2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2af Hangul Syllable-B2AF | b2ae Hangul Syllable-B2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b1 Hangul Syllable-B2B1 | b2b0 Hangul Syllable-B2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b3 Hangul Syllable-B2B3 | b2b2 Hangul Syllable-B2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b5 Hangul Syllable-B2B5 | b2b4 Hangul Syllable-B2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b7 Hangul Syllable-B2B7 | b2b6 Hangul Syllable-B2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2b9 Hangul Syllable-B2B9 | b2b8 Hangul Syllable-B2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bb Hangul Syllable-B2BB | b2ba Hangul Syllable-B2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bd Hangul Syllable-B2BD | b2bc Hangul Syllable-B2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2bf Hangul Syllable-B2BF | b2be Hangul Syllable-B2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c1 Hangul Syllable-B2C1 | b2c0 Hangul Syllable-B2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c3 Hangul Syllable-B2C3 | b2c2 Hangul Syllable-B2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c5 Hangul Syllable-B2C5 | b2c4 Hangul Syllable-B2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c7 Hangul Syllable-B2C7 | b2c6 Hangul Syllable-B2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2c9 Hangul Syllable-B2C9 | b2c8 Hangul Syllable-B2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cb Hangul Syllable-B2CB | b2ca Hangul Syllable-B2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cd Hangul Syllable-B2CD | b2cc Hangul Syllable-B2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2cf Hangul Syllable-B2CF | b2ce Hangul Syllable-B2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d1 Hangul Syllable-B2D1 | b2d0 Hangul Syllable-B2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d3 Hangul Syllable-B2D3 | b2d2 Hangul Syllable-B2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d5 Hangul Syllable-B2D5 | b2d4 Hangul Syllable-B2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d7 Hangul Syllable-B2D7 | b2d6 Hangul Syllable-B2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2d9 Hangul Syllable-B2D9 | b2d8 Hangul Syllable-B2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2db Hangul Syllable-B2DB | b2da Hangul Syllable-B2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2dd Hangul Syllable-B2DD | b2dc Hangul Syllable-B2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2df Hangul Syllable-B2DF | b2de Hangul Syllable-B2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e1 Hangul Syllable-B2E1 | b2e0 Hangul Syllable-B2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e3 Hangul Syllable-B2E3 | b2e2 Hangul Syllable-B2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e5 Hangul Syllable-B2E5 | b2e4 Hangul Syllable-B2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e7 Hangul Syllable-B2E7 | b2e6 Hangul Syllable-B2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2e9 Hangul Syllable-B2E9 | b2e8 Hangul Syllable-B2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2eb Hangul Syllable-B2EB | b2ea Hangul Syllable-B2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ed Hangul Syllable-B2ED | b2ec Hangul Syllable-B2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ef Hangul Syllable-B2EF | b2ee Hangul Syllable-B2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f1 Hangul Syllable-B2F1 | b2f0 Hangul Syllable-B2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f3 Hangul Syllable-B2F3 | b2f2 Hangul Syllable-B2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f5 Hangul Syllable-B2F5 | b2f4 Hangul Syllable-B2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f7 Hangul Syllable-B2F7 | b2f6 Hangul Syllable-B2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2f9 Hangul Syllable-B2F9 | b2f8 Hangul Syllable-B2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2fb Hangul Syllable-B2FB | b2fa Hangul Syllable-B2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2fd Hangul Syllable-B2FD | b2fc Hangul Syllable-B2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b2ff Hangul Syllable-B2FF | b2fe Hangul Syllable-B2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b301 Hangul Syllable-B301 | b300 Hangul Syllable-B300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b303 Hangul Syllable-B303 | b302 Hangul Syllable-B302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b305 Hangul Syllable-B305 | b304 Hangul Syllable-B304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b307 Hangul Syllable-B307 | b306 Hangul Syllable-B306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b309 Hangul Syllable-B309 | b308 Hangul Syllable-B308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30b Hangul Syllable-B30B | b30a Hangul Syllable-B30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30d Hangul Syllable-B30D | b30c Hangul Syllable-B30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b30f Hangul Syllable-B30F | b30e Hangul Syllable-B30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b311 Hangul Syllable-B311 | b310 Hangul Syllable-B310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b313 Hangul Syllable-B313 | b312 Hangul Syllable-B312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b315 Hangul Syllable-B315 | b314 Hangul Syllable-B314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b317 Hangul Syllable-B317 | b316 Hangul Syllable-B316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b319 Hangul Syllable-B319 | b318 Hangul Syllable-B318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31b Hangul Syllable-B31B | b31a Hangul Syllable-B31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31d Hangul Syllable-B31D | b31c Hangul Syllable-B31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b31f Hangul Syllable-B31F | b31e Hangul Syllable-B31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b321 Hangul Syllable-B321 | b320 Hangul Syllable-B320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b323 Hangul Syllable-B323 | b322 Hangul Syllable-B322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b325 Hangul Syllable-B325 | b324 Hangul Syllable-B324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b327 Hangul Syllable-B327 | b326 Hangul Syllable-B326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b329 Hangul Syllable-B329 | b328 Hangul Syllable-B328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32b Hangul Syllable-B32B | b32a Hangul Syllable-B32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32d Hangul Syllable-B32D | b32c Hangul Syllable-B32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b32f Hangul Syllable-B32F | b32e Hangul Syllable-B32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b331 Hangul Syllable-B331 | b330 Hangul Syllable-B330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b333 Hangul Syllable-B333 | b332 Hangul Syllable-B332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b335 Hangul Syllable-B335 | b334 Hangul Syllable-B334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b337 Hangul Syllable-B337 | b336 Hangul Syllable-B336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b339 Hangul Syllable-B339 | b338 Hangul Syllable-B338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33b Hangul Syllable-B33B | b33a Hangul Syllable-B33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33d Hangul Syllable-B33D | b33c Hangul Syllable-B33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b33f Hangul Syllable-B33F | b33e Hangul Syllable-B33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b341 Hangul Syllable-B341 | b340 Hangul Syllable-B340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b343 Hangul Syllable-B343 | b342 Hangul Syllable-B342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b345 Hangul Syllable-B345 | b344 Hangul Syllable-B344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b347 Hangul Syllable-B347 | b346 Hangul Syllable-B346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b349 Hangul Syllable-B349 | b348 Hangul Syllable-B348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34b Hangul Syllable-B34B | b34a Hangul Syllable-B34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34d Hangul Syllable-B34D | b34c Hangul Syllable-B34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b34f Hangul Syllable-B34F | b34e Hangul Syllable-B34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b351 Hangul Syllable-B351 | b350 Hangul Syllable-B350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b353 Hangul Syllable-B353 | b352 Hangul Syllable-B352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b355 Hangul Syllable-B355 | b354 Hangul Syllable-B354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b357 Hangul Syllable-B357 | b356 Hangul Syllable-B356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b359 Hangul Syllable-B359 | b358 Hangul Syllable-B358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35b Hangul Syllable-B35B | b35a Hangul Syllable-B35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35d Hangul Syllable-B35D | b35c Hangul Syllable-B35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b35f Hangul Syllable-B35F | b35e Hangul Syllable-B35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b361 Hangul Syllable-B361 | b360 Hangul Syllable-B360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b363 Hangul Syllable-B363 | b362 Hangul Syllable-B362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b365 Hangul Syllable-B365 | b364 Hangul Syllable-B364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b367 Hangul Syllable-B367 | b366 Hangul Syllable-B366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b369 Hangul Syllable-B369 | b368 Hangul Syllable-B368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36b Hangul Syllable-B36B | b36a Hangul Syllable-B36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36d Hangul Syllable-B36D | b36c Hangul Syllable-B36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b36f Hangul Syllable-B36F | b36e Hangul Syllable-B36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b371 Hangul Syllable-B371 | b370 Hangul Syllable-B370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b373 Hangul Syllable-B373 | b372 Hangul Syllable-B372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b375 Hangul Syllable-B375 | b374 Hangul Syllable-B374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b377 Hangul Syllable-B377 | b376 Hangul Syllable-B376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b379 Hangul Syllable-B379 | b378 Hangul Syllable-B378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37b Hangul Syllable-B37B | b37a Hangul Syllable-B37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37d Hangul Syllable-B37D | b37c Hangul Syllable-B37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b37f Hangul Syllable-B37F | b37e Hangul Syllable-B37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b381 Hangul Syllable-B381 | b380 Hangul Syllable-B380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b383 Hangul Syllable-B383 | b382 Hangul Syllable-B382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b385 Hangul Syllable-B385 | b384 Hangul Syllable-B384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b387 Hangul Syllable-B387 | b386 Hangul Syllable-B386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b389 Hangul Syllable-B389 | b388 Hangul Syllable-B388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38b Hangul Syllable-B38B | b38a Hangul Syllable-B38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38d Hangul Syllable-B38D | b38c Hangul Syllable-B38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b38f Hangul Syllable-B38F | b38e Hangul Syllable-B38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b391 Hangul Syllable-B391 | b390 Hangul Syllable-B390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b393 Hangul Syllable-B393 | b392 Hangul Syllable-B392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b395 Hangul Syllable-B395 | b394 Hangul Syllable-B394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b397 Hangul Syllable-B397 | b396 Hangul Syllable-B396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b399 Hangul Syllable-B399 | b398 Hangul Syllable-B398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39b Hangul Syllable-B39B | b39a Hangul Syllable-B39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39d Hangul Syllable-B39D | b39c Hangul Syllable-B39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b39f Hangul Syllable-B39F | b39e Hangul Syllable-B39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a1 Hangul Syllable-B3A1 | b3a0 Hangul Syllable-B3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a3 Hangul Syllable-B3A3 | b3a2 Hangul Syllable-B3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a5 Hangul Syllable-B3A5 | b3a4 Hangul Syllable-B3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a7 Hangul Syllable-B3A7 | b3a6 Hangul Syllable-B3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3a9 Hangul Syllable-B3A9 | b3a8 Hangul Syllable-B3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ab Hangul Syllable-B3AB | b3aa Hangul Syllable-B3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ad Hangul Syllable-B3AD | b3ac Hangul Syllable-B3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3af Hangul Syllable-B3AF | b3ae Hangul Syllable-B3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b1 Hangul Syllable-B3B1 | b3b0 Hangul Syllable-B3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b3 Hangul Syllable-B3B3 | b3b2 Hangul Syllable-B3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b5 Hangul Syllable-B3B5 | b3b4 Hangul Syllable-B3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b7 Hangul Syllable-B3B7 | b3b6 Hangul Syllable-B3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3b9 Hangul Syllable-B3B9 | b3b8 Hangul Syllable-B3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bb Hangul Syllable-B3BB | b3ba Hangul Syllable-B3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bd Hangul Syllable-B3BD | b3bc Hangul Syllable-B3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3bf Hangul Syllable-B3BF | b3be Hangul Syllable-B3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c1 Hangul Syllable-B3C1 | b3c0 Hangul Syllable-B3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c3 Hangul Syllable-B3C3 | b3c2 Hangul Syllable-B3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c5 Hangul Syllable-B3C5 | b3c4 Hangul Syllable-B3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c7 Hangul Syllable-B3C7 | b3c6 Hangul Syllable-B3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3c9 Hangul Syllable-B3C9 | b3c8 Hangul Syllable-B3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cb Hangul Syllable-B3CB | b3ca Hangul Syllable-B3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cd Hangul Syllable-B3CD | b3cc Hangul Syllable-B3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3cf Hangul Syllable-B3CF | b3ce Hangul Syllable-B3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d1 Hangul Syllable-B3D1 | b3d0 Hangul Syllable-B3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d3 Hangul Syllable-B3D3 | b3d2 Hangul Syllable-B3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d5 Hangul Syllable-B3D5 | b3d4 Hangul Syllable-B3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d7 Hangul Syllable-B3D7 | b3d6 Hangul Syllable-B3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3d9 Hangul Syllable-B3D9 | b3d8 Hangul Syllable-B3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3db Hangul Syllable-B3DB | b3da Hangul Syllable-B3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3dd Hangul Syllable-B3DD | b3dc Hangul Syllable-B3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3df Hangul Syllable-B3DF | b3de Hangul Syllable-B3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e1 Hangul Syllable-B3E1 | b3e0 Hangul Syllable-B3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e3 Hangul Syllable-B3E3 | b3e2 Hangul Syllable-B3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e5 Hangul Syllable-B3E5 | b3e4 Hangul Syllable-B3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e7 Hangul Syllable-B3E7 | b3e6 Hangul Syllable-B3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3e9 Hangul Syllable-B3E9 | b3e8 Hangul Syllable-B3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3eb Hangul Syllable-B3EB | b3ea Hangul Syllable-B3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ed Hangul Syllable-B3ED | b3ec Hangul Syllable-B3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ef Hangul Syllable-B3EF | b3ee Hangul Syllable-B3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f1 Hangul Syllable-B3F1 | b3f0 Hangul Syllable-B3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f3 Hangul Syllable-B3F3 | b3f2 Hangul Syllable-B3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f5 Hangul Syllable-B3F5 | b3f4 Hangul Syllable-B3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f7 Hangul Syllable-B3F7 | b3f6 Hangul Syllable-B3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3f9 Hangul Syllable-B3F9 | b3f8 Hangul Syllable-B3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3fb Hangul Syllable-B3FB | b3fa Hangul Syllable-B3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3fd Hangul Syllable-B3FD | b3fc Hangul Syllable-B3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b3ff Hangul Syllable-B3FF | b3fe Hangul Syllable-B3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b401 Hangul Syllable-B401 | b400 Hangul Syllable-B400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b403 Hangul Syllable-B403 | b402 Hangul Syllable-B402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b405 Hangul Syllable-B405 | b404 Hangul Syllable-B404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b407 Hangul Syllable-B407 | b406 Hangul Syllable-B406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b409 Hangul Syllable-B409 | b408 Hangul Syllable-B408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40b Hangul Syllable-B40B | b40a Hangul Syllable-B40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40d Hangul Syllable-B40D | b40c Hangul Syllable-B40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b40f Hangul Syllable-B40F | b40e Hangul Syllable-B40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b411 Hangul Syllable-B411 | b410 Hangul Syllable-B410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b413 Hangul Syllable-B413 | b412 Hangul Syllable-B412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b415 Hangul Syllable-B415 | b414 Hangul Syllable-B414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b417 Hangul Syllable-B417 | b416 Hangul Syllable-B416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b419 Hangul Syllable-B419 | b418 Hangul Syllable-B418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41b Hangul Syllable-B41B | b41a Hangul Syllable-B41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41d Hangul Syllable-B41D | b41c Hangul Syllable-B41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b41f Hangul Syllable-B41F | b41e Hangul Syllable-B41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b421 Hangul Syllable-B421 | b420 Hangul Syllable-B420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b423 Hangul Syllable-B423 | b422 Hangul Syllable-B422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b425 Hangul Syllable-B425 | b424 Hangul Syllable-B424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b427 Hangul Syllable-B427 | b426 Hangul Syllable-B426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b429 Hangul Syllable-B429 | b428 Hangul Syllable-B428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42b Hangul Syllable-B42B | b42a Hangul Syllable-B42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42d Hangul Syllable-B42D | b42c Hangul Syllable-B42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b42f Hangul Syllable-B42F | b42e Hangul Syllable-B42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b431 Hangul Syllable-B431 | b430 Hangul Syllable-B430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b433 Hangul Syllable-B433 | b432 Hangul Syllable-B432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b435 Hangul Syllable-B435 | b434 Hangul Syllable-B434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b437 Hangul Syllable-B437 | b436 Hangul Syllable-B436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b439 Hangul Syllable-B439 | b438 Hangul Syllable-B438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43b Hangul Syllable-B43B | b43a Hangul Syllable-B43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43d Hangul Syllable-B43D | b43c Hangul Syllable-B43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b43f Hangul Syllable-B43F | b43e Hangul Syllable-B43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b441 Hangul Syllable-B441 | b440 Hangul Syllable-B440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b443 Hangul Syllable-B443 | b442 Hangul Syllable-B442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b445 Hangul Syllable-B445 | b444 Hangul Syllable-B444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b447 Hangul Syllable-B447 | b446 Hangul Syllable-B446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b449 Hangul Syllable-B449 | b448 Hangul Syllable-B448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44b Hangul Syllable-B44B | b44a Hangul Syllable-B44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44d Hangul Syllable-B44D | b44c Hangul Syllable-B44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b44f Hangul Syllable-B44F | b44e Hangul Syllable-B44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b451 Hangul Syllable-B451 | b450 Hangul Syllable-B450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b453 Hangul Syllable-B453 | b452 Hangul Syllable-B452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b455 Hangul Syllable-B455 | b454 Hangul Syllable-B454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b457 Hangul Syllable-B457 | b456 Hangul Syllable-B456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b459 Hangul Syllable-B459 | b458 Hangul Syllable-B458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45b Hangul Syllable-B45B | b45a Hangul Syllable-B45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45d Hangul Syllable-B45D | b45c Hangul Syllable-B45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b45f Hangul Syllable-B45F | b45e Hangul Syllable-B45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b461 Hangul Syllable-B461 | b460 Hangul Syllable-B460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b463 Hangul Syllable-B463 | b462 Hangul Syllable-B462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b465 Hangul Syllable-B465 | b464 Hangul Syllable-B464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b467 Hangul Syllable-B467 | b466 Hangul Syllable-B466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b469 Hangul Syllable-B469 | b468 Hangul Syllable-B468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46b Hangul Syllable-B46B | b46a Hangul Syllable-B46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46d Hangul Syllable-B46D | b46c Hangul Syllable-B46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b46f Hangul Syllable-B46F | b46e Hangul Syllable-B46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b471 Hangul Syllable-B471 | b470 Hangul Syllable-B470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b473 Hangul Syllable-B473 | b472 Hangul Syllable-B472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b475 Hangul Syllable-B475 | b474 Hangul Syllable-B474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b477 Hangul Syllable-B477 | b476 Hangul Syllable-B476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b479 Hangul Syllable-B479 | b478 Hangul Syllable-B478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47b Hangul Syllable-B47B | b47a Hangul Syllable-B47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47d Hangul Syllable-B47D | b47c Hangul Syllable-B47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b47f Hangul Syllable-B47F | b47e Hangul Syllable-B47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b481 Hangul Syllable-B481 | b480 Hangul Syllable-B480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b483 Hangul Syllable-B483 | b482 Hangul Syllable-B482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b485 Hangul Syllable-B485 | b484 Hangul Syllable-B484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b487 Hangul Syllable-B487 | b486 Hangul Syllable-B486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b489 Hangul Syllable-B489 | b488 Hangul Syllable-B488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48b Hangul Syllable-B48B | b48a Hangul Syllable-B48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48d Hangul Syllable-B48D | b48c Hangul Syllable-B48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b48f Hangul Syllable-B48F | b48e Hangul Syllable-B48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b491 Hangul Syllable-B491 | b490 Hangul Syllable-B490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b493 Hangul Syllable-B493 | b492 Hangul Syllable-B492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b495 Hangul Syllable-B495 | b494 Hangul Syllable-B494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b497 Hangul Syllable-B497 | b496 Hangul Syllable-B496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b499 Hangul Syllable-B499 | b498 Hangul Syllable-B498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49b Hangul Syllable-B49B | b49a Hangul Syllable-B49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49d Hangul Syllable-B49D | b49c Hangul Syllable-B49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b49f Hangul Syllable-B49F | b49e Hangul Syllable-B49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a1 Hangul Syllable-B4A1 | b4a0 Hangul Syllable-B4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a3 Hangul Syllable-B4A3 | b4a2 Hangul Syllable-B4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a5 Hangul Syllable-B4A5 | b4a4 Hangul Syllable-B4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a7 Hangul Syllable-B4A7 | b4a6 Hangul Syllable-B4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4a9 Hangul Syllable-B4A9 | b4a8 Hangul Syllable-B4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ab Hangul Syllable-B4AB | b4aa Hangul Syllable-B4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ad Hangul Syllable-B4AD | b4ac Hangul Syllable-B4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4af Hangul Syllable-B4AF | b4ae Hangul Syllable-B4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b1 Hangul Syllable-B4B1 | b4b0 Hangul Syllable-B4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b3 Hangul Syllable-B4B3 | b4b2 Hangul Syllable-B4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b5 Hangul Syllable-B4B5 | b4b4 Hangul Syllable-B4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b7 Hangul Syllable-B4B7 | b4b6 Hangul Syllable-B4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4b9 Hangul Syllable-B4B9 | b4b8 Hangul Syllable-B4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bb Hangul Syllable-B4BB | b4ba Hangul Syllable-B4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bd Hangul Syllable-B4BD | b4bc Hangul Syllable-B4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4bf Hangul Syllable-B4BF | b4be Hangul Syllable-B4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c1 Hangul Syllable-B4C1 | b4c0 Hangul Syllable-B4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c3 Hangul Syllable-B4C3 | b4c2 Hangul Syllable-B4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c5 Hangul Syllable-B4C5 | b4c4 Hangul Syllable-B4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c7 Hangul Syllable-B4C7 | b4c6 Hangul Syllable-B4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4c9 Hangul Syllable-B4C9 | b4c8 Hangul Syllable-B4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cb Hangul Syllable-B4CB | b4ca Hangul Syllable-B4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cd Hangul Syllable-B4CD | b4cc Hangul Syllable-B4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4cf Hangul Syllable-B4CF | b4ce Hangul Syllable-B4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d1 Hangul Syllable-B4D1 | b4d0 Hangul Syllable-B4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d3 Hangul Syllable-B4D3 | b4d2 Hangul Syllable-B4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d5 Hangul Syllable-B4D5 | b4d4 Hangul Syllable-B4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d7 Hangul Syllable-B4D7 | b4d6 Hangul Syllable-B4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4d9 Hangul Syllable-B4D9 | b4d8 Hangul Syllable-B4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4db Hangul Syllable-B4DB | b4da Hangul Syllable-B4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4dd Hangul Syllable-B4DD | b4dc Hangul Syllable-B4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4df Hangul Syllable-B4DF | b4de Hangul Syllable-B4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e1 Hangul Syllable-B4E1 | b4e0 Hangul Syllable-B4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e3 Hangul Syllable-B4E3 | b4e2 Hangul Syllable-B4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e5 Hangul Syllable-B4E5 | b4e4 Hangul Syllable-B4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e7 Hangul Syllable-B4E7 | b4e6 Hangul Syllable-B4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4e9 Hangul Syllable-B4E9 | b4e8 Hangul Syllable-B4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4eb Hangul Syllable-B4EB | b4ea Hangul Syllable-B4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ed Hangul Syllable-B4ED | b4ec Hangul Syllable-B4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ef Hangul Syllable-B4EF | b4ee Hangul Syllable-B4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f1 Hangul Syllable-B4F1 | b4f0 Hangul Syllable-B4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f3 Hangul Syllable-B4F3 | b4f2 Hangul Syllable-B4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f5 Hangul Syllable-B4F5 | b4f4 Hangul Syllable-B4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f7 Hangul Syllable-B4F7 | b4f6 Hangul Syllable-B4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4f9 Hangul Syllable-B4F9 | b4f8 Hangul Syllable-B4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4fb Hangul Syllable-B4FB | b4fa Hangul Syllable-B4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4fd Hangul Syllable-B4FD | b4fc Hangul Syllable-B4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b4ff Hangul Syllable-B4FF | b4fe Hangul Syllable-B4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b501 Hangul Syllable-B501 | b500 Hangul Syllable-B500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b503 Hangul Syllable-B503 | b502 Hangul Syllable-B502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b505 Hangul Syllable-B505 | b504 Hangul Syllable-B504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b507 Hangul Syllable-B507 | b506 Hangul Syllable-B506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b509 Hangul Syllable-B509 | b508 Hangul Syllable-B508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50b Hangul Syllable-B50B | b50a Hangul Syllable-B50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50d Hangul Syllable-B50D | b50c Hangul Syllable-B50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b50f Hangul Syllable-B50F | b50e Hangul Syllable-B50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b511 Hangul Syllable-B511 | b510 Hangul Syllable-B510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b513 Hangul Syllable-B513 | b512 Hangul Syllable-B512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b515 Hangul Syllable-B515 | b514 Hangul Syllable-B514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b517 Hangul Syllable-B517 | b516 Hangul Syllable-B516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b519 Hangul Syllable-B519 | b518 Hangul Syllable-B518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51b Hangul Syllable-B51B | b51a Hangul Syllable-B51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51d Hangul Syllable-B51D | b51c Hangul Syllable-B51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b51f Hangul Syllable-B51F | b51e Hangul Syllable-B51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b521 Hangul Syllable-B521 | b520 Hangul Syllable-B520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b523 Hangul Syllable-B523 | b522 Hangul Syllable-B522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b525 Hangul Syllable-B525 | b524 Hangul Syllable-B524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b527 Hangul Syllable-B527 | b526 Hangul Syllable-B526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b529 Hangul Syllable-B529 | b528 Hangul Syllable-B528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52b Hangul Syllable-B52B | b52a Hangul Syllable-B52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52d Hangul Syllable-B52D | b52c Hangul Syllable-B52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b52f Hangul Syllable-B52F | b52e Hangul Syllable-B52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b531 Hangul Syllable-B531 | b530 Hangul Syllable-B530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b533 Hangul Syllable-B533 | b532 Hangul Syllable-B532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b535 Hangul Syllable-B535 | b534 Hangul Syllable-B534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b537 Hangul Syllable-B537 | b536 Hangul Syllable-B536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b539 Hangul Syllable-B539 | b538 Hangul Syllable-B538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53b Hangul Syllable-B53B | b53a Hangul Syllable-B53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53d Hangul Syllable-B53D | b53c Hangul Syllable-B53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b53f Hangul Syllable-B53F | b53e Hangul Syllable-B53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b541 Hangul Syllable-B541 | b540 Hangul Syllable-B540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b543 Hangul Syllable-B543 | b542 Hangul Syllable-B542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b545 Hangul Syllable-B545 | b544 Hangul Syllable-B544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b547 Hangul Syllable-B547 | b546 Hangul Syllable-B546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b549 Hangul Syllable-B549 | b548 Hangul Syllable-B548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54b Hangul Syllable-B54B | b54a Hangul Syllable-B54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54d Hangul Syllable-B54D | b54c Hangul Syllable-B54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b54f Hangul Syllable-B54F | b54e Hangul Syllable-B54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b551 Hangul Syllable-B551 | b550 Hangul Syllable-B550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b553 Hangul Syllable-B553 | b552 Hangul Syllable-B552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b555 Hangul Syllable-B555 | b554 Hangul Syllable-B554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b557 Hangul Syllable-B557 | b556 Hangul Syllable-B556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b559 Hangul Syllable-B559 | b558 Hangul Syllable-B558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55b Hangul Syllable-B55B | b55a Hangul Syllable-B55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55d Hangul Syllable-B55D | b55c Hangul Syllable-B55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b55f Hangul Syllable-B55F | b55e Hangul Syllable-B55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b561 Hangul Syllable-B561 | b560 Hangul Syllable-B560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b563 Hangul Syllable-B563 | b562 Hangul Syllable-B562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b565 Hangul Syllable-B565 | b564 Hangul Syllable-B564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b567 Hangul Syllable-B567 | b566 Hangul Syllable-B566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b569 Hangul Syllable-B569 | b568 Hangul Syllable-B568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56b Hangul Syllable-B56B | b56a Hangul Syllable-B56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56d Hangul Syllable-B56D | b56c Hangul Syllable-B56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b56f Hangul Syllable-B56F | b56e Hangul Syllable-B56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b571 Hangul Syllable-B571 | b570 Hangul Syllable-B570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b573 Hangul Syllable-B573 | b572 Hangul Syllable-B572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b575 Hangul Syllable-B575 | b574 Hangul Syllable-B574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b577 Hangul Syllable-B577 | b576 Hangul Syllable-B576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b579 Hangul Syllable-B579 | b578 Hangul Syllable-B578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57b Hangul Syllable-B57B | b57a Hangul Syllable-B57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57d Hangul Syllable-B57D | b57c Hangul Syllable-B57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b57f Hangul Syllable-B57F | b57e Hangul Syllable-B57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b581 Hangul Syllable-B581 | b580 Hangul Syllable-B580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b583 Hangul Syllable-B583 | b582 Hangul Syllable-B582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b585 Hangul Syllable-B585 | b584 Hangul Syllable-B584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b587 Hangul Syllable-B587 | b586 Hangul Syllable-B586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b589 Hangul Syllable-B589 | b588 Hangul Syllable-B588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58b Hangul Syllable-B58B | b58a Hangul Syllable-B58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58d Hangul Syllable-B58D | b58c Hangul Syllable-B58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b58f Hangul Syllable-B58F | b58e Hangul Syllable-B58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b591 Hangul Syllable-B591 | b590 Hangul Syllable-B590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b593 Hangul Syllable-B593 | b592 Hangul Syllable-B592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b595 Hangul Syllable-B595 | b594 Hangul Syllable-B594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b597 Hangul Syllable-B597 | b596 Hangul Syllable-B596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b599 Hangul Syllable-B599 | b598 Hangul Syllable-B598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59b Hangul Syllable-B59B | b59a Hangul Syllable-B59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59d Hangul Syllable-B59D | b59c Hangul Syllable-B59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b59f Hangul Syllable-B59F | b59e Hangul Syllable-B59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a1 Hangul Syllable-B5A1 | b5a0 Hangul Syllable-B5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a3 Hangul Syllable-B5A3 | b5a2 Hangul Syllable-B5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a5 Hangul Syllable-B5A5 | b5a4 Hangul Syllable-B5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a7 Hangul Syllable-B5A7 | b5a6 Hangul Syllable-B5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5a9 Hangul Syllable-B5A9 | b5a8 Hangul Syllable-B5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ab Hangul Syllable-B5AB | b5aa Hangul Syllable-B5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ad Hangul Syllable-B5AD | b5ac Hangul Syllable-B5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5af Hangul Syllable-B5AF | b5ae Hangul Syllable-B5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b1 Hangul Syllable-B5B1 | b5b0 Hangul Syllable-B5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b3 Hangul Syllable-B5B3 | b5b2 Hangul Syllable-B5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b5 Hangul Syllable-B5B5 | b5b4 Hangul Syllable-B5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b7 Hangul Syllable-B5B7 | b5b6 Hangul Syllable-B5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5b9 Hangul Syllable-B5B9 | b5b8 Hangul Syllable-B5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bb Hangul Syllable-B5BB | b5ba Hangul Syllable-B5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bd Hangul Syllable-B5BD | b5bc Hangul Syllable-B5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5bf Hangul Syllable-B5BF | b5be Hangul Syllable-B5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c1 Hangul Syllable-B5C1 | b5c0 Hangul Syllable-B5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c3 Hangul Syllable-B5C3 | b5c2 Hangul Syllable-B5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c5 Hangul Syllable-B5C5 | b5c4 Hangul Syllable-B5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c7 Hangul Syllable-B5C7 | b5c6 Hangul Syllable-B5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5c9 Hangul Syllable-B5C9 | b5c8 Hangul Syllable-B5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cb Hangul Syllable-B5CB | b5ca Hangul Syllable-B5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cd Hangul Syllable-B5CD | b5cc Hangul Syllable-B5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5cf Hangul Syllable-B5CF | b5ce Hangul Syllable-B5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d1 Hangul Syllable-B5D1 | b5d0 Hangul Syllable-B5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d3 Hangul Syllable-B5D3 | b5d2 Hangul Syllable-B5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d5 Hangul Syllable-B5D5 | b5d4 Hangul Syllable-B5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d7 Hangul Syllable-B5D7 | b5d6 Hangul Syllable-B5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5d9 Hangul Syllable-B5D9 | b5d8 Hangul Syllable-B5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5db Hangul Syllable-B5DB | b5da Hangul Syllable-B5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5dd Hangul Syllable-B5DD | b5dc Hangul Syllable-B5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5df Hangul Syllable-B5DF | b5de Hangul Syllable-B5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e1 Hangul Syllable-B5E1 | b5e0 Hangul Syllable-B5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e3 Hangul Syllable-B5E3 | b5e2 Hangul Syllable-B5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e5 Hangul Syllable-B5E5 | b5e4 Hangul Syllable-B5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e7 Hangul Syllable-B5E7 | b5e6 Hangul Syllable-B5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5e9 Hangul Syllable-B5E9 | b5e8 Hangul Syllable-B5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5eb Hangul Syllable-B5EB | b5ea Hangul Syllable-B5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ed Hangul Syllable-B5ED | b5ec Hangul Syllable-B5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ef Hangul Syllable-B5EF | b5ee Hangul Syllable-B5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f1 Hangul Syllable-B5F1 | b5f0 Hangul Syllable-B5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f3 Hangul Syllable-B5F3 | b5f2 Hangul Syllable-B5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f5 Hangul Syllable-B5F5 | b5f4 Hangul Syllable-B5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f7 Hangul Syllable-B5F7 | b5f6 Hangul Syllable-B5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5f9 Hangul Syllable-B5F9 | b5f8 Hangul Syllable-B5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5fb Hangul Syllable-B5FB | b5fa Hangul Syllable-B5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5fd Hangul Syllable-B5FD | b5fc Hangul Syllable-B5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b5ff Hangul Syllable-B5FF | b5fe Hangul Syllable-B5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b601 Hangul Syllable-B601 | b600 Hangul Syllable-B600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b603 Hangul Syllable-B603 | b602 Hangul Syllable-B602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b605 Hangul Syllable-B605 | b604 Hangul Syllable-B604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b607 Hangul Syllable-B607 | b606 Hangul Syllable-B606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b609 Hangul Syllable-B609 | b608 Hangul Syllable-B608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60b Hangul Syllable-B60B | b60a Hangul Syllable-B60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60d Hangul Syllable-B60D | b60c Hangul Syllable-B60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b60f Hangul Syllable-B60F | b60e Hangul Syllable-B60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b611 Hangul Syllable-B611 | b610 Hangul Syllable-B610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b613 Hangul Syllable-B613 | b612 Hangul Syllable-B612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b615 Hangul Syllable-B615 | b614 Hangul Syllable-B614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b617 Hangul Syllable-B617 | b616 Hangul Syllable-B616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b619 Hangul Syllable-B619 | b618 Hangul Syllable-B618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61b Hangul Syllable-B61B | b61a Hangul Syllable-B61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61d Hangul Syllable-B61D | b61c Hangul Syllable-B61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b61f Hangul Syllable-B61F | b61e Hangul Syllable-B61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b621 Hangul Syllable-B621 | b620 Hangul Syllable-B620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b623 Hangul Syllable-B623 | b622 Hangul Syllable-B622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b625 Hangul Syllable-B625 | b624 Hangul Syllable-B624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b627 Hangul Syllable-B627 | b626 Hangul Syllable-B626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b629 Hangul Syllable-B629 | b628 Hangul Syllable-B628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62b Hangul Syllable-B62B | b62a Hangul Syllable-B62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62d Hangul Syllable-B62D | b62c Hangul Syllable-B62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b62f Hangul Syllable-B62F | b62e Hangul Syllable-B62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b631 Hangul Syllable-B631 | b630 Hangul Syllable-B630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b633 Hangul Syllable-B633 | b632 Hangul Syllable-B632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b635 Hangul Syllable-B635 | b634 Hangul Syllable-B634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b637 Hangul Syllable-B637 | b636 Hangul Syllable-B636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b639 Hangul Syllable-B639 | b638 Hangul Syllable-B638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63b Hangul Syllable-B63B | b63a Hangul Syllable-B63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63d Hangul Syllable-B63D | b63c Hangul Syllable-B63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b63f Hangul Syllable-B63F | b63e Hangul Syllable-B63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b641 Hangul Syllable-B641 | b640 Hangul Syllable-B640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b643 Hangul Syllable-B643 | b642 Hangul Syllable-B642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b645 Hangul Syllable-B645 | b644 Hangul Syllable-B644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b647 Hangul Syllable-B647 | b646 Hangul Syllable-B646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b649 Hangul Syllable-B649 | b648 Hangul Syllable-B648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64b Hangul Syllable-B64B | b64a Hangul Syllable-B64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64d Hangul Syllable-B64D | b64c Hangul Syllable-B64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b64f Hangul Syllable-B64F | b64e Hangul Syllable-B64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b651 Hangul Syllable-B651 | b650 Hangul Syllable-B650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b653 Hangul Syllable-B653 | b652 Hangul Syllable-B652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b655 Hangul Syllable-B655 | b654 Hangul Syllable-B654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b657 Hangul Syllable-B657 | b656 Hangul Syllable-B656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b659 Hangul Syllable-B659 | b658 Hangul Syllable-B658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65b Hangul Syllable-B65B | b65a Hangul Syllable-B65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65d Hangul Syllable-B65D | b65c Hangul Syllable-B65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b65f Hangul Syllable-B65F | b65e Hangul Syllable-B65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b661 Hangul Syllable-B661 | b660 Hangul Syllable-B660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b663 Hangul Syllable-B663 | b662 Hangul Syllable-B662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b665 Hangul Syllable-B665 | b664 Hangul Syllable-B664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b667 Hangul Syllable-B667 | b666 Hangul Syllable-B666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b669 Hangul Syllable-B669 | b668 Hangul Syllable-B668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66b Hangul Syllable-B66B | b66a Hangul Syllable-B66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66d Hangul Syllable-B66D | b66c Hangul Syllable-B66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b66f Hangul Syllable-B66F | b66e Hangul Syllable-B66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b671 Hangul Syllable-B671 | b670 Hangul Syllable-B670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b673 Hangul Syllable-B673 | b672 Hangul Syllable-B672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b675 Hangul Syllable-B675 | b674 Hangul Syllable-B674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b677 Hangul Syllable-B677 | b676 Hangul Syllable-B676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b679 Hangul Syllable-B679 | b678 Hangul Syllable-B678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67b Hangul Syllable-B67B | b67a Hangul Syllable-B67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67d Hangul Syllable-B67D | b67c Hangul Syllable-B67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b67f Hangul Syllable-B67F | b67e Hangul Syllable-B67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b681 Hangul Syllable-B681 | b680 Hangul Syllable-B680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b683 Hangul Syllable-B683 | b682 Hangul Syllable-B682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b685 Hangul Syllable-B685 | b684 Hangul Syllable-B684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b687 Hangul Syllable-B687 | b686 Hangul Syllable-B686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b689 Hangul Syllable-B689 | b688 Hangul Syllable-B688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68b Hangul Syllable-B68B | b68a Hangul Syllable-B68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68d Hangul Syllable-B68D | b68c Hangul Syllable-B68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b68f Hangul Syllable-B68F | b68e Hangul Syllable-B68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b691 Hangul Syllable-B691 | b690 Hangul Syllable-B690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b693 Hangul Syllable-B693 | b692 Hangul Syllable-B692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b695 Hangul Syllable-B695 | b694 Hangul Syllable-B694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b697 Hangul Syllable-B697 | b696 Hangul Syllable-B696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b699 Hangul Syllable-B699 | b698 Hangul Syllable-B698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69b Hangul Syllable-B69B | b69a Hangul Syllable-B69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69d Hangul Syllable-B69D | b69c Hangul Syllable-B69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b69f Hangul Syllable-B69F | b69e Hangul Syllable-B69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a1 Hangul Syllable-B6A1 | b6a0 Hangul Syllable-B6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a3 Hangul Syllable-B6A3 | b6a2 Hangul Syllable-B6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a5 Hangul Syllable-B6A5 | b6a4 Hangul Syllable-B6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a7 Hangul Syllable-B6A7 | b6a6 Hangul Syllable-B6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6a9 Hangul Syllable-B6A9 | b6a8 Hangul Syllable-B6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ab Hangul Syllable-B6AB | b6aa Hangul Syllable-B6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ad Hangul Syllable-B6AD | b6ac Hangul Syllable-B6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6af Hangul Syllable-B6AF | b6ae Hangul Syllable-B6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b1 Hangul Syllable-B6B1 | b6b0 Hangul Syllable-B6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b3 Hangul Syllable-B6B3 | b6b2 Hangul Syllable-B6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b5 Hangul Syllable-B6B5 | b6b4 Hangul Syllable-B6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b7 Hangul Syllable-B6B7 | b6b6 Hangul Syllable-B6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6b9 Hangul Syllable-B6B9 | b6b8 Hangul Syllable-B6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bb Hangul Syllable-B6BB | b6ba Hangul Syllable-B6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bd Hangul Syllable-B6BD | b6bc Hangul Syllable-B6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6bf Hangul Syllable-B6BF | b6be Hangul Syllable-B6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c1 Hangul Syllable-B6C1 | b6c0 Hangul Syllable-B6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c3 Hangul Syllable-B6C3 | b6c2 Hangul Syllable-B6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c5 Hangul Syllable-B6C5 | b6c4 Hangul Syllable-B6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c7 Hangul Syllable-B6C7 | b6c6 Hangul Syllable-B6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6c9 Hangul Syllable-B6C9 | b6c8 Hangul Syllable-B6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cb Hangul Syllable-B6CB | b6ca Hangul Syllable-B6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cd Hangul Syllable-B6CD | b6cc Hangul Syllable-B6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6cf Hangul Syllable-B6CF | b6ce Hangul Syllable-B6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d1 Hangul Syllable-B6D1 | b6d0 Hangul Syllable-B6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d3 Hangul Syllable-B6D3 | b6d2 Hangul Syllable-B6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d5 Hangul Syllable-B6D5 | b6d4 Hangul Syllable-B6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d7 Hangul Syllable-B6D7 | b6d6 Hangul Syllable-B6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6d9 Hangul Syllable-B6D9 | b6d8 Hangul Syllable-B6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6db Hangul Syllable-B6DB | b6da Hangul Syllable-B6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6dd Hangul Syllable-B6DD | b6dc Hangul Syllable-B6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6df Hangul Syllable-B6DF | b6de Hangul Syllable-B6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e1 Hangul Syllable-B6E1 | b6e0 Hangul Syllable-B6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e3 Hangul Syllable-B6E3 | b6e2 Hangul Syllable-B6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e5 Hangul Syllable-B6E5 | b6e4 Hangul Syllable-B6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e7 Hangul Syllable-B6E7 | b6e6 Hangul Syllable-B6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6e9 Hangul Syllable-B6E9 | b6e8 Hangul Syllable-B6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6eb Hangul Syllable-B6EB | b6ea Hangul Syllable-B6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ed Hangul Syllable-B6ED | b6ec Hangul Syllable-B6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ef Hangul Syllable-B6EF | b6ee Hangul Syllable-B6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f1 Hangul Syllable-B6F1 | b6f0 Hangul Syllable-B6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f3 Hangul Syllable-B6F3 | b6f2 Hangul Syllable-B6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f5 Hangul Syllable-B6F5 | b6f4 Hangul Syllable-B6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f7 Hangul Syllable-B6F7 | b6f6 Hangul Syllable-B6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6f9 Hangul Syllable-B6F9 | b6f8 Hangul Syllable-B6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6fb Hangul Syllable-B6FB | b6fa Hangul Syllable-B6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6fd Hangul Syllable-B6FD | b6fc Hangul Syllable-B6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b6ff Hangul Syllable-B6FF | b6fe Hangul Syllable-B6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b701 Hangul Syllable-B701 | b700 Hangul Syllable-B700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b703 Hangul Syllable-B703 | b702 Hangul Syllable-B702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b705 Hangul Syllable-B705 | b704 Hangul Syllable-B704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b707 Hangul Syllable-B707 | b706 Hangul Syllable-B706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b709 Hangul Syllable-B709 | b708 Hangul Syllable-B708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70b Hangul Syllable-B70B | b70a Hangul Syllable-B70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70d Hangul Syllable-B70D | b70c Hangul Syllable-B70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b70f Hangul Syllable-B70F | b70e Hangul Syllable-B70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b711 Hangul Syllable-B711 | b710 Hangul Syllable-B710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b713 Hangul Syllable-B713 | b712 Hangul Syllable-B712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b715 Hangul Syllable-B715 | b714 Hangul Syllable-B714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b717 Hangul Syllable-B717 | b716 Hangul Syllable-B716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b719 Hangul Syllable-B719 | b718 Hangul Syllable-B718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71b Hangul Syllable-B71B | b71a Hangul Syllable-B71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71d Hangul Syllable-B71D | b71c Hangul Syllable-B71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b71f Hangul Syllable-B71F | b71e Hangul Syllable-B71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b721 Hangul Syllable-B721 | b720 Hangul Syllable-B720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b723 Hangul Syllable-B723 | b722 Hangul Syllable-B722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b725 Hangul Syllable-B725 | b724 Hangul Syllable-B724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b727 Hangul Syllable-B727 | b726 Hangul Syllable-B726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b729 Hangul Syllable-B729 | b728 Hangul Syllable-B728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72b Hangul Syllable-B72B | b72a Hangul Syllable-B72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72d Hangul Syllable-B72D | b72c Hangul Syllable-B72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b72f Hangul Syllable-B72F | b72e Hangul Syllable-B72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b731 Hangul Syllable-B731 | b730 Hangul Syllable-B730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b733 Hangul Syllable-B733 | b732 Hangul Syllable-B732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b735 Hangul Syllable-B735 | b734 Hangul Syllable-B734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b737 Hangul Syllable-B737 | b736 Hangul Syllable-B736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b739 Hangul Syllable-B739 | b738 Hangul Syllable-B738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73b Hangul Syllable-B73B | b73a Hangul Syllable-B73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73d Hangul Syllable-B73D | b73c Hangul Syllable-B73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b73f Hangul Syllable-B73F | b73e Hangul Syllable-B73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b741 Hangul Syllable-B741 | b740 Hangul Syllable-B740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b743 Hangul Syllable-B743 | b742 Hangul Syllable-B742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b745 Hangul Syllable-B745 | b744 Hangul Syllable-B744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b747 Hangul Syllable-B747 | b746 Hangul Syllable-B746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b749 Hangul Syllable-B749 | b748 Hangul Syllable-B748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74b Hangul Syllable-B74B | b74a Hangul Syllable-B74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74d Hangul Syllable-B74D | b74c Hangul Syllable-B74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b74f Hangul Syllable-B74F | b74e Hangul Syllable-B74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b751 Hangul Syllable-B751 | b750 Hangul Syllable-B750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b753 Hangul Syllable-B753 | b752 Hangul Syllable-B752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b755 Hangul Syllable-B755 | b754 Hangul Syllable-B754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b757 Hangul Syllable-B757 | b756 Hangul Syllable-B756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b759 Hangul Syllable-B759 | b758 Hangul Syllable-B758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75b Hangul Syllable-B75B | b75a Hangul Syllable-B75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75d Hangul Syllable-B75D | b75c Hangul Syllable-B75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b75f Hangul Syllable-B75F | b75e Hangul Syllable-B75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b761 Hangul Syllable-B761 | b760 Hangul Syllable-B760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b763 Hangul Syllable-B763 | b762 Hangul Syllable-B762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b765 Hangul Syllable-B765 | b764 Hangul Syllable-B764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b767 Hangul Syllable-B767 | b766 Hangul Syllable-B766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b769 Hangul Syllable-B769 | b768 Hangul Syllable-B768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76b Hangul Syllable-B76B | b76a Hangul Syllable-B76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76d Hangul Syllable-B76D | b76c Hangul Syllable-B76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b76f Hangul Syllable-B76F | b76e Hangul Syllable-B76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b771 Hangul Syllable-B771 | b770 Hangul Syllable-B770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b773 Hangul Syllable-B773 | b772 Hangul Syllable-B772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b775 Hangul Syllable-B775 | b774 Hangul Syllable-B774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b777 Hangul Syllable-B777 | b776 Hangul Syllable-B776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b779 Hangul Syllable-B779 | b778 Hangul Syllable-B778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77b Hangul Syllable-B77B | b77a Hangul Syllable-B77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77d Hangul Syllable-B77D | b77c Hangul Syllable-B77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b77f Hangul Syllable-B77F | b77e Hangul Syllable-B77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b781 Hangul Syllable-B781 | b780 Hangul Syllable-B780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b783 Hangul Syllable-B783 | b782 Hangul Syllable-B782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b785 Hangul Syllable-B785 | b784 Hangul Syllable-B784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b787 Hangul Syllable-B787 | b786 Hangul Syllable-B786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b789 Hangul Syllable-B789 | b788 Hangul Syllable-B788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78b Hangul Syllable-B78B | b78a Hangul Syllable-B78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78d Hangul Syllable-B78D | b78c Hangul Syllable-B78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b78f Hangul Syllable-B78F | b78e Hangul Syllable-B78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b791 Hangul Syllable-B791 | b790 Hangul Syllable-B790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b793 Hangul Syllable-B793 | b792 Hangul Syllable-B792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b795 Hangul Syllable-B795 | b794 Hangul Syllable-B794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b797 Hangul Syllable-B797 | b796 Hangul Syllable-B796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b799 Hangul Syllable-B799 | b798 Hangul Syllable-B798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79b Hangul Syllable-B79B | b79a Hangul Syllable-B79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79d Hangul Syllable-B79D | b79c Hangul Syllable-B79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b79f Hangul Syllable-B79F | b79e Hangul Syllable-B79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a1 Hangul Syllable-B7A1 | b7a0 Hangul Syllable-B7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a3 Hangul Syllable-B7A3 | b7a2 Hangul Syllable-B7A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a5 Hangul Syllable-B7A5 | b7a4 Hangul Syllable-B7A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a7 Hangul Syllable-B7A7 | b7a6 Hangul Syllable-B7A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7a9 Hangul Syllable-B7A9 | b7a8 Hangul Syllable-B7A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ab Hangul Syllable-B7AB | b7aa Hangul Syllable-B7AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ad Hangul Syllable-B7AD | b7ac Hangul Syllable-B7AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7af Hangul Syllable-B7AF | b7ae Hangul Syllable-B7AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b1 Hangul Syllable-B7B1 | b7b0 Hangul Syllable-B7B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b3 Hangul Syllable-B7B3 | b7b2 Hangul Syllable-B7B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b5 Hangul Syllable-B7B5 | b7b4 Hangul Syllable-B7B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b7 Hangul Syllable-B7B7 | b7b6 Hangul Syllable-B7B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7b9 Hangul Syllable-B7B9 | b7b8 Hangul Syllable-B7B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bb Hangul Syllable-B7BB | b7ba Hangul Syllable-B7BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bd Hangul Syllable-B7BD | b7bc Hangul Syllable-B7BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7bf Hangul Syllable-B7BF | b7be Hangul Syllable-B7BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c1 Hangul Syllable-B7C1 | b7c0 Hangul Syllable-B7C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c3 Hangul Syllable-B7C3 | b7c2 Hangul Syllable-B7C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c5 Hangul Syllable-B7C5 | b7c4 Hangul Syllable-B7C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c7 Hangul Syllable-B7C7 | b7c6 Hangul Syllable-B7C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7c9 Hangul Syllable-B7C9 | b7c8 Hangul Syllable-B7C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cb Hangul Syllable-B7CB | b7ca Hangul Syllable-B7CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cd Hangul Syllable-B7CD | b7cc Hangul Syllable-B7CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7cf Hangul Syllable-B7CF | b7ce Hangul Syllable-B7CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d1 Hangul Syllable-B7D1 | b7d0 Hangul Syllable-B7D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d3 Hangul Syllable-B7D3 | b7d2 Hangul Syllable-B7D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d5 Hangul Syllable-B7D5 | b7d4 Hangul Syllable-B7D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d7 Hangul Syllable-B7D7 | b7d6 Hangul Syllable-B7D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7d9 Hangul Syllable-B7D9 | b7d8 Hangul Syllable-B7D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7db Hangul Syllable-B7DB | b7da Hangul Syllable-B7DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7dd Hangul Syllable-B7DD | b7dc Hangul Syllable-B7DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7df Hangul Syllable-B7DF | b7de Hangul Syllable-B7DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e1 Hangul Syllable-B7E1 | b7e0 Hangul Syllable-B7E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e3 Hangul Syllable-B7E3 | b7e2 Hangul Syllable-B7E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e5 Hangul Syllable-B7E5 | b7e4 Hangul Syllable-B7E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e7 Hangul Syllable-B7E7 | b7e6 Hangul Syllable-B7E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7e9 Hangul Syllable-B7E9 | b7e8 Hangul Syllable-B7E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7eb Hangul Syllable-B7EB | b7ea Hangul Syllable-B7EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ed Hangul Syllable-B7ED | b7ec Hangul Syllable-B7EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ef Hangul Syllable-B7EF | b7ee Hangul Syllable-B7EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f1 Hangul Syllable-B7F1 | b7f0 Hangul Syllable-B7F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f3 Hangul Syllable-B7F3 | b7f2 Hangul Syllable-B7F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f5 Hangul Syllable-B7F5 | b7f4 Hangul Syllable-B7F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f7 Hangul Syllable-B7F7 | b7f6 Hangul Syllable-B7F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7f9 Hangul Syllable-B7F9 | b7f8 Hangul Syllable-B7F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7fb Hangul Syllable-B7FB | b7fa Hangul Syllable-B7FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7fd Hangul Syllable-B7FD | b7fc Hangul Syllable-B7FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b7ff Hangul Syllable-B7FF | b7fe Hangul Syllable-B7FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b801 Hangul Syllable-B801 | b800 Hangul Syllable-B800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b803 Hangul Syllable-B803 | b802 Hangul Syllable-B802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b805 Hangul Syllable-B805 | b804 Hangul Syllable-B804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b807 Hangul Syllable-B807 | b806 Hangul Syllable-B806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b809 Hangul Syllable-B809 | b808 Hangul Syllable-B808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80b Hangul Syllable-B80B | b80a Hangul Syllable-B80A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80d Hangul Syllable-B80D | b80c Hangul Syllable-B80C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b80f Hangul Syllable-B80F | b80e Hangul Syllable-B80E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b811 Hangul Syllable-B811 | b810 Hangul Syllable-B810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b813 Hangul Syllable-B813 | b812 Hangul Syllable-B812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b815 Hangul Syllable-B815 | b814 Hangul Syllable-B814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b817 Hangul Syllable-B817 | b816 Hangul Syllable-B816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b819 Hangul Syllable-B819 | b818 Hangul Syllable-B818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81b Hangul Syllable-B81B | b81a Hangul Syllable-B81A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81d Hangul Syllable-B81D | b81c Hangul Syllable-B81C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b81f Hangul Syllable-B81F | b81e Hangul Syllable-B81E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b821 Hangul Syllable-B821 | b820 Hangul Syllable-B820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b823 Hangul Syllable-B823 | b822 Hangul Syllable-B822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b825 Hangul Syllable-B825 | b824 Hangul Syllable-B824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b827 Hangul Syllable-B827 | b826 Hangul Syllable-B826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b829 Hangul Syllable-B829 | b828 Hangul Syllable-B828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82b Hangul Syllable-B82B | b82a Hangul Syllable-B82A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82d Hangul Syllable-B82D | b82c Hangul Syllable-B82C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b82f Hangul Syllable-B82F | b82e Hangul Syllable-B82E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b831 Hangul Syllable-B831 | b830 Hangul Syllable-B830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b833 Hangul Syllable-B833 | b832 Hangul Syllable-B832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b835 Hangul Syllable-B835 | b834 Hangul Syllable-B834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b837 Hangul Syllable-B837 | b836 Hangul Syllable-B836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b839 Hangul Syllable-B839 | b838 Hangul Syllable-B838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83b Hangul Syllable-B83B | b83a Hangul Syllable-B83A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83d Hangul Syllable-B83D | b83c Hangul Syllable-B83C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b83f Hangul Syllable-B83F | b83e Hangul Syllable-B83E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b841 Hangul Syllable-B841 | b840 Hangul Syllable-B840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b843 Hangul Syllable-B843 | b842 Hangul Syllable-B842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b845 Hangul Syllable-B845 | b844 Hangul Syllable-B844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b847 Hangul Syllable-B847 | b846 Hangul Syllable-B846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b849 Hangul Syllable-B849 | b848 Hangul Syllable-B848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84b Hangul Syllable-B84B | b84a Hangul Syllable-B84A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84d Hangul Syllable-B84D | b84c Hangul Syllable-B84C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b84f Hangul Syllable-B84F | b84e Hangul Syllable-B84E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b851 Hangul Syllable-B851 | b850 Hangul Syllable-B850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b853 Hangul Syllable-B853 | b852 Hangul Syllable-B852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b855 Hangul Syllable-B855 | b854 Hangul Syllable-B854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b857 Hangul Syllable-B857 | b856 Hangul Syllable-B856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b859 Hangul Syllable-B859 | b858 Hangul Syllable-B858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85b Hangul Syllable-B85B | b85a Hangul Syllable-B85A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85d Hangul Syllable-B85D | b85c Hangul Syllable-B85C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b85f Hangul Syllable-B85F | b85e Hangul Syllable-B85E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b861 Hangul Syllable-B861 | b860 Hangul Syllable-B860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b863 Hangul Syllable-B863 | b862 Hangul Syllable-B862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b865 Hangul Syllable-B865 | b864 Hangul Syllable-B864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b867 Hangul Syllable-B867 | b866 Hangul Syllable-B866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b869 Hangul Syllable-B869 | b868 Hangul Syllable-B868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86b Hangul Syllable-B86B | b86a Hangul Syllable-B86A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86d Hangul Syllable-B86D | b86c Hangul Syllable-B86C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b86f Hangul Syllable-B86F | b86e Hangul Syllable-B86E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b871 Hangul Syllable-B871 | b870 Hangul Syllable-B870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b873 Hangul Syllable-B873 | b872 Hangul Syllable-B872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b875 Hangul Syllable-B875 | b874 Hangul Syllable-B874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b877 Hangul Syllable-B877 | b876 Hangul Syllable-B876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b879 Hangul Syllable-B879 | b878 Hangul Syllable-B878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87b Hangul Syllable-B87B | b87a Hangul Syllable-B87A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87d Hangul Syllable-B87D | b87c Hangul Syllable-B87C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b87f Hangul Syllable-B87F | b87e Hangul Syllable-B87E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b881 Hangul Syllable-B881 | b880 Hangul Syllable-B880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b883 Hangul Syllable-B883 | b882 Hangul Syllable-B882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b885 Hangul Syllable-B885 | b884 Hangul Syllable-B884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b887 Hangul Syllable-B887 | b886 Hangul Syllable-B886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b889 Hangul Syllable-B889 | b888 Hangul Syllable-B888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88b Hangul Syllable-B88B | b88a Hangul Syllable-B88A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88d Hangul Syllable-B88D | b88c Hangul Syllable-B88C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b88f Hangul Syllable-B88F | b88e Hangul Syllable-B88E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b891 Hangul Syllable-B891 | b890 Hangul Syllable-B890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b893 Hangul Syllable-B893 | b892 Hangul Syllable-B892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b895 Hangul Syllable-B895 | b894 Hangul Syllable-B894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b897 Hangul Syllable-B897 | b896 Hangul Syllable-B896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b899 Hangul Syllable-B899 | b898 Hangul Syllable-B898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89b Hangul Syllable-B89B | b89a Hangul Syllable-B89A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89d Hangul Syllable-B89D | b89c Hangul Syllable-B89C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b89f Hangul Syllable-B89F | b89e Hangul Syllable-B89E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a1 Hangul Syllable-B8A1 | b8a0 Hangul Syllable-B8A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a3 Hangul Syllable-B8A3 | b8a2 Hangul Syllable-B8A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a5 Hangul Syllable-B8A5 | b8a4 Hangul Syllable-B8A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a7 Hangul Syllable-B8A7 | b8a6 Hangul Syllable-B8A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8a9 Hangul Syllable-B8A9 | b8a8 Hangul Syllable-B8A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ab Hangul Syllable-B8AB | b8aa Hangul Syllable-B8AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ad Hangul Syllable-B8AD | b8ac Hangul Syllable-B8AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8af Hangul Syllable-B8AF | b8ae Hangul Syllable-B8AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b1 Hangul Syllable-B8B1 | b8b0 Hangul Syllable-B8B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b3 Hangul Syllable-B8B3 | b8b2 Hangul Syllable-B8B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b5 Hangul Syllable-B8B5 | b8b4 Hangul Syllable-B8B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b7 Hangul Syllable-B8B7 | b8b6 Hangul Syllable-B8B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8b9 Hangul Syllable-B8B9 | b8b8 Hangul Syllable-B8B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bb Hangul Syllable-B8BB | b8ba Hangul Syllable-B8BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bd Hangul Syllable-B8BD | b8bc Hangul Syllable-B8BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8bf Hangul Syllable-B8BF | b8be Hangul Syllable-B8BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c1 Hangul Syllable-B8C1 | b8c0 Hangul Syllable-B8C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c3 Hangul Syllable-B8C3 | b8c2 Hangul Syllable-B8C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c5 Hangul Syllable-B8C5 | b8c4 Hangul Syllable-B8C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c7 Hangul Syllable-B8C7 | b8c6 Hangul Syllable-B8C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8c9 Hangul Syllable-B8C9 | b8c8 Hangul Syllable-B8C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cb Hangul Syllable-B8CB | b8ca Hangul Syllable-B8CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cd Hangul Syllable-B8CD | b8cc Hangul Syllable-B8CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8cf Hangul Syllable-B8CF | b8ce Hangul Syllable-B8CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d1 Hangul Syllable-B8D1 | b8d0 Hangul Syllable-B8D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d3 Hangul Syllable-B8D3 | b8d2 Hangul Syllable-B8D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d5 Hangul Syllable-B8D5 | b8d4 Hangul Syllable-B8D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d7 Hangul Syllable-B8D7 | b8d6 Hangul Syllable-B8D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8d9 Hangul Syllable-B8D9 | b8d8 Hangul Syllable-B8D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8db Hangul Syllable-B8DB | b8da Hangul Syllable-B8DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8dd Hangul Syllable-B8DD | b8dc Hangul Syllable-B8DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8df Hangul Syllable-B8DF | b8de Hangul Syllable-B8DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e1 Hangul Syllable-B8E1 | b8e0 Hangul Syllable-B8E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e3 Hangul Syllable-B8E3 | b8e2 Hangul Syllable-B8E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e5 Hangul Syllable-B8E5 | b8e4 Hangul Syllable-B8E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e7 Hangul Syllable-B8E7 | b8e6 Hangul Syllable-B8E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8e9 Hangul Syllable-B8E9 | b8e8 Hangul Syllable-B8E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8eb Hangul Syllable-B8EB | b8ea Hangul Syllable-B8EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ed Hangul Syllable-B8ED | b8ec Hangul Syllable-B8EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ef Hangul Syllable-B8EF | b8ee Hangul Syllable-B8EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f1 Hangul Syllable-B8F1 | b8f0 Hangul Syllable-B8F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f3 Hangul Syllable-B8F3 | b8f2 Hangul Syllable-B8F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f5 Hangul Syllable-B8F5 | b8f4 Hangul Syllable-B8F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f7 Hangul Syllable-B8F7 | b8f6 Hangul Syllable-B8F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8f9 Hangul Syllable-B8F9 | b8f8 Hangul Syllable-B8F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8fb Hangul Syllable-B8FB | b8fa Hangul Syllable-B8FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8fd Hangul Syllable-B8FD | b8fc Hangul Syllable-B8FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b8ff Hangul Syllable-B8FF | b8fe Hangul Syllable-B8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b901 Hangul Syllable-B901 | b900 Hangul Syllable-B900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b903 Hangul Syllable-B903 | b902 Hangul Syllable-B902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b905 Hangul Syllable-B905 | b904 Hangul Syllable-B904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b907 Hangul Syllable-B907 | b906 Hangul Syllable-B906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b909 Hangul Syllable-B909 | b908 Hangul Syllable-B908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90b Hangul Syllable-B90B | b90a Hangul Syllable-B90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90d Hangul Syllable-B90D | b90c Hangul Syllable-B90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b90f Hangul Syllable-B90F | b90e Hangul Syllable-B90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b911 Hangul Syllable-B911 | b910 Hangul Syllable-B910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b913 Hangul Syllable-B913 | b912 Hangul Syllable-B912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b915 Hangul Syllable-B915 | b914 Hangul Syllable-B914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b917 Hangul Syllable-B917 | b916 Hangul Syllable-B916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b919 Hangul Syllable-B919 | b918 Hangul Syllable-B918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91b Hangul Syllable-B91B | b91a Hangul Syllable-B91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91d Hangul Syllable-B91D | b91c Hangul Syllable-B91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b91f Hangul Syllable-B91F | b91e Hangul Syllable-B91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b921 Hangul Syllable-B921 | b920 Hangul Syllable-B920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b923 Hangul Syllable-B923 | b922 Hangul Syllable-B922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b925 Hangul Syllable-B925 | b924 Hangul Syllable-B924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b927 Hangul Syllable-B927 | b926 Hangul Syllable-B926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b929 Hangul Syllable-B929 | b928 Hangul Syllable-B928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92b Hangul Syllable-B92B | b92a Hangul Syllable-B92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92d Hangul Syllable-B92D | b92c Hangul Syllable-B92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b92f Hangul Syllable-B92F | b92e Hangul Syllable-B92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b931 Hangul Syllable-B931 | b930 Hangul Syllable-B930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b933 Hangul Syllable-B933 | b932 Hangul Syllable-B932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b935 Hangul Syllable-B935 | b934 Hangul Syllable-B934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b937 Hangul Syllable-B937 | b936 Hangul Syllable-B936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b939 Hangul Syllable-B939 | b938 Hangul Syllable-B938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93b Hangul Syllable-B93B | b93a Hangul Syllable-B93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93d Hangul Syllable-B93D | b93c Hangul Syllable-B93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b93f Hangul Syllable-B93F | b93e Hangul Syllable-B93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b941 Hangul Syllable-B941 | b940 Hangul Syllable-B940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b943 Hangul Syllable-B943 | b942 Hangul Syllable-B942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b945 Hangul Syllable-B945 | b944 Hangul Syllable-B944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b947 Hangul Syllable-B947 | b946 Hangul Syllable-B946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b949 Hangul Syllable-B949 | b948 Hangul Syllable-B948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94b Hangul Syllable-B94B | b94a Hangul Syllable-B94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94d Hangul Syllable-B94D | b94c Hangul Syllable-B94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b94f Hangul Syllable-B94F | b94e Hangul Syllable-B94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b951 Hangul Syllable-B951 | b950 Hangul Syllable-B950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b953 Hangul Syllable-B953 | b952 Hangul Syllable-B952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b955 Hangul Syllable-B955 | b954 Hangul Syllable-B954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b957 Hangul Syllable-B957 | b956 Hangul Syllable-B956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b959 Hangul Syllable-B959 | b958 Hangul Syllable-B958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95b Hangul Syllable-B95B | b95a Hangul Syllable-B95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95d Hangul Syllable-B95D | b95c Hangul Syllable-B95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b95f Hangul Syllable-B95F | b95e Hangul Syllable-B95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b961 Hangul Syllable-B961 | b960 Hangul Syllable-B960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b963 Hangul Syllable-B963 | b962 Hangul Syllable-B962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b965 Hangul Syllable-B965 | b964 Hangul Syllable-B964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b967 Hangul Syllable-B967 | b966 Hangul Syllable-B966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b969 Hangul Syllable-B969 | b968 Hangul Syllable-B968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96b Hangul Syllable-B96B | b96a Hangul Syllable-B96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96d Hangul Syllable-B96D | b96c Hangul Syllable-B96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b96f Hangul Syllable-B96F | b96e Hangul Syllable-B96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b971 Hangul Syllable-B971 | b970 Hangul Syllable-B970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b973 Hangul Syllable-B973 | b972 Hangul Syllable-B972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b975 Hangul Syllable-B975 | b974 Hangul Syllable-B974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b977 Hangul Syllable-B977 | b976 Hangul Syllable-B976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b979 Hangul Syllable-B979 | b978 Hangul Syllable-B978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97b Hangul Syllable-B97B | b97a Hangul Syllable-B97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97d Hangul Syllable-B97D | b97c Hangul Syllable-B97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b97f Hangul Syllable-B97F | b97e Hangul Syllable-B97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b981 Hangul Syllable-B981 | b980 Hangul Syllable-B980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b983 Hangul Syllable-B983 | b982 Hangul Syllable-B982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b985 Hangul Syllable-B985 | b984 Hangul Syllable-B984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b987 Hangul Syllable-B987 | b986 Hangul Syllable-B986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b989 Hangul Syllable-B989 | b988 Hangul Syllable-B988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98b Hangul Syllable-B98B | b98a Hangul Syllable-B98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98d Hangul Syllable-B98D | b98c Hangul Syllable-B98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b98f Hangul Syllable-B98F | b98e Hangul Syllable-B98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b991 Hangul Syllable-B991 | b990 Hangul Syllable-B990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b993 Hangul Syllable-B993 | b992 Hangul Syllable-B992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b995 Hangul Syllable-B995 | b994 Hangul Syllable-B994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b997 Hangul Syllable-B997 | b996 Hangul Syllable-B996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b999 Hangul Syllable-B999 | b998 Hangul Syllable-B998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99b Hangul Syllable-B99B | b99a Hangul Syllable-B99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99d Hangul Syllable-B99D | b99c Hangul Syllable-B99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b99f Hangul Syllable-B99F | b99e Hangul Syllable-B99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a1 Hangul Syllable-B9A1 | b9a0 Hangul Syllable-B9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a3 Hangul Syllable-B9A3 | b9a2 Hangul Syllable-B9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a5 Hangul Syllable-B9A5 | b9a4 Hangul Syllable-B9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a7 Hangul Syllable-B9A7 | b9a6 Hangul Syllable-B9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9a9 Hangul Syllable-B9A9 | b9a8 Hangul Syllable-B9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ab Hangul Syllable-B9AB | b9aa Hangul Syllable-B9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ad Hangul Syllable-B9AD | b9ac Hangul Syllable-B9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9af Hangul Syllable-B9AF | b9ae Hangul Syllable-B9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b1 Hangul Syllable-B9B1 | b9b0 Hangul Syllable-B9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b3 Hangul Syllable-B9B3 | b9b2 Hangul Syllable-B9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b5 Hangul Syllable-B9B5 | b9b4 Hangul Syllable-B9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b7 Hangul Syllable-B9B7 | b9b6 Hangul Syllable-B9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9b9 Hangul Syllable-B9B9 | b9b8 Hangul Syllable-B9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bb Hangul Syllable-B9BB | b9ba Hangul Syllable-B9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bd Hangul Syllable-B9BD | b9bc Hangul Syllable-B9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9bf Hangul Syllable-B9BF | b9be Hangul Syllable-B9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c1 Hangul Syllable-B9C1 | b9c0 Hangul Syllable-B9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c3 Hangul Syllable-B9C3 | b9c2 Hangul Syllable-B9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c5 Hangul Syllable-B9C5 | b9c4 Hangul Syllable-B9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c7 Hangul Syllable-B9C7 | b9c6 Hangul Syllable-B9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9c9 Hangul Syllable-B9C9 | b9c8 Hangul Syllable-B9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cb Hangul Syllable-B9CB | b9ca Hangul Syllable-B9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cd Hangul Syllable-B9CD | b9cc Hangul Syllable-B9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9cf Hangul Syllable-B9CF | b9ce Hangul Syllable-B9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d1 Hangul Syllable-B9D1 | b9d0 Hangul Syllable-B9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d3 Hangul Syllable-B9D3 | b9d2 Hangul Syllable-B9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d5 Hangul Syllable-B9D5 | b9d4 Hangul Syllable-B9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d7 Hangul Syllable-B9D7 | b9d6 Hangul Syllable-B9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9d9 Hangul Syllable-B9D9 | b9d8 Hangul Syllable-B9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9db Hangul Syllable-B9DB | b9da Hangul Syllable-B9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9dd Hangul Syllable-B9DD | b9dc Hangul Syllable-B9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9df Hangul Syllable-B9DF | b9de Hangul Syllable-B9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e1 Hangul Syllable-B9E1 | b9e0 Hangul Syllable-B9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e3 Hangul Syllable-B9E3 | b9e2 Hangul Syllable-B9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e5 Hangul Syllable-B9E5 | b9e4 Hangul Syllable-B9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e7 Hangul Syllable-B9E7 | b9e6 Hangul Syllable-B9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9e9 Hangul Syllable-B9E9 | b9e8 Hangul Syllable-B9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9eb Hangul Syllable-B9EB | b9ea Hangul Syllable-B9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ed Hangul Syllable-B9ED | b9ec Hangul Syllable-B9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ef Hangul Syllable-B9EF | b9ee Hangul Syllable-B9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f1 Hangul Syllable-B9F1 | b9f0 Hangul Syllable-B9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f3 Hangul Syllable-B9F3 | b9f2 Hangul Syllable-B9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f5 Hangul Syllable-B9F5 | b9f4 Hangul Syllable-B9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f7 Hangul Syllable-B9F7 | b9f6 Hangul Syllable-B9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9f9 Hangul Syllable-B9F9 | b9f8 Hangul Syllable-B9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9fb Hangul Syllable-B9FB | b9fa Hangul Syllable-B9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9fd Hangul Syllable-B9FD | b9fc Hangul Syllable-B9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* b9ff Hangul Syllable-B9FF | b9fe Hangul Syllable-B9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba01 Hangul Syllable-BA01 | ba00 Hangul Syllable-BA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba03 Hangul Syllable-BA03 | ba02 Hangul Syllable-BA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba05 Hangul Syllable-BA05 | ba04 Hangul Syllable-BA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba07 Hangul Syllable-BA07 | ba06 Hangul Syllable-BA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba09 Hangul Syllable-BA09 | ba08 Hangul Syllable-BA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0b Hangul Syllable-BA0B | ba0a Hangul Syllable-BA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0d Hangul Syllable-BA0D | ba0c Hangul Syllable-BA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba0f Hangul Syllable-BA0F | ba0e Hangul Syllable-BA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba11 Hangul Syllable-BA11 | ba10 Hangul Syllable-BA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba13 Hangul Syllable-BA13 | ba12 Hangul Syllable-BA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba15 Hangul Syllable-BA15 | ba14 Hangul Syllable-BA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba17 Hangul Syllable-BA17 | ba16 Hangul Syllable-BA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba19 Hangul Syllable-BA19 | ba18 Hangul Syllable-BA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1b Hangul Syllable-BA1B | ba1a Hangul Syllable-BA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1d Hangul Syllable-BA1D | ba1c Hangul Syllable-BA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba1f Hangul Syllable-BA1F | ba1e Hangul Syllable-BA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba21 Hangul Syllable-BA21 | ba20 Hangul Syllable-BA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba23 Hangul Syllable-BA23 | ba22 Hangul Syllable-BA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba25 Hangul Syllable-BA25 | ba24 Hangul Syllable-BA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba27 Hangul Syllable-BA27 | ba26 Hangul Syllable-BA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba29 Hangul Syllable-BA29 | ba28 Hangul Syllable-BA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2b Hangul Syllable-BA2B | ba2a Hangul Syllable-BA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2d Hangul Syllable-BA2D | ba2c Hangul Syllable-BA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba2f Hangul Syllable-BA2F | ba2e Hangul Syllable-BA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba31 Hangul Syllable-BA31 | ba30 Hangul Syllable-BA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba33 Hangul Syllable-BA33 | ba32 Hangul Syllable-BA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba35 Hangul Syllable-BA35 | ba34 Hangul Syllable-BA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba37 Hangul Syllable-BA37 | ba36 Hangul Syllable-BA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba39 Hangul Syllable-BA39 | ba38 Hangul Syllable-BA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3b Hangul Syllable-BA3B | ba3a Hangul Syllable-BA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3d Hangul Syllable-BA3D | ba3c Hangul Syllable-BA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba3f Hangul Syllable-BA3F | ba3e Hangul Syllable-BA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba41 Hangul Syllable-BA41 | ba40 Hangul Syllable-BA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba43 Hangul Syllable-BA43 | ba42 Hangul Syllable-BA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba45 Hangul Syllable-BA45 | ba44 Hangul Syllable-BA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba47 Hangul Syllable-BA47 | ba46 Hangul Syllable-BA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba49 Hangul Syllable-BA49 | ba48 Hangul Syllable-BA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4b Hangul Syllable-BA4B | ba4a Hangul Syllable-BA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4d Hangul Syllable-BA4D | ba4c Hangul Syllable-BA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba4f Hangul Syllable-BA4F | ba4e Hangul Syllable-BA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba51 Hangul Syllable-BA51 | ba50 Hangul Syllable-BA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba53 Hangul Syllable-BA53 | ba52 Hangul Syllable-BA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba55 Hangul Syllable-BA55 | ba54 Hangul Syllable-BA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba57 Hangul Syllable-BA57 | ba56 Hangul Syllable-BA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba59 Hangul Syllable-BA59 | ba58 Hangul Syllable-BA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5b Hangul Syllable-BA5B | ba5a Hangul Syllable-BA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5d Hangul Syllable-BA5D | ba5c Hangul Syllable-BA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba5f Hangul Syllable-BA5F | ba5e Hangul Syllable-BA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba61 Hangul Syllable-BA61 | ba60 Hangul Syllable-BA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba63 Hangul Syllable-BA63 | ba62 Hangul Syllable-BA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba65 Hangul Syllable-BA65 | ba64 Hangul Syllable-BA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba67 Hangul Syllable-BA67 | ba66 Hangul Syllable-BA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba69 Hangul Syllable-BA69 | ba68 Hangul Syllable-BA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6b Hangul Syllable-BA6B | ba6a Hangul Syllable-BA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6d Hangul Syllable-BA6D | ba6c Hangul Syllable-BA6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba6f Hangul Syllable-BA6F | ba6e Hangul Syllable-BA6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba71 Hangul Syllable-BA71 | ba70 Hangul Syllable-BA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba73 Hangul Syllable-BA73 | ba72 Hangul Syllable-BA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba75 Hangul Syllable-BA75 | ba74 Hangul Syllable-BA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba77 Hangul Syllable-BA77 | ba76 Hangul Syllable-BA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba79 Hangul Syllable-BA79 | ba78 Hangul Syllable-BA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7b Hangul Syllable-BA7B | ba7a Hangul Syllable-BA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7d Hangul Syllable-BA7D | ba7c Hangul Syllable-BA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba7f Hangul Syllable-BA7F | ba7e Hangul Syllable-BA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba81 Hangul Syllable-BA81 | ba80 Hangul Syllable-BA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba83 Hangul Syllable-BA83 | ba82 Hangul Syllable-BA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba85 Hangul Syllable-BA85 | ba84 Hangul Syllable-BA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba87 Hangul Syllable-BA87 | ba86 Hangul Syllable-BA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba89 Hangul Syllable-BA89 | ba88 Hangul Syllable-BA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8b Hangul Syllable-BA8B | ba8a Hangul Syllable-BA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8d Hangul Syllable-BA8D | ba8c Hangul Syllable-BA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba8f Hangul Syllable-BA8F | ba8e Hangul Syllable-BA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba91 Hangul Syllable-BA91 | ba90 Hangul Syllable-BA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba93 Hangul Syllable-BA93 | ba92 Hangul Syllable-BA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba95 Hangul Syllable-BA95 | ba94 Hangul Syllable-BA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba97 Hangul Syllable-BA97 | ba96 Hangul Syllable-BA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba99 Hangul Syllable-BA99 | ba98 Hangul Syllable-BA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9b Hangul Syllable-BA9B | ba9a Hangul Syllable-BA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9d Hangul Syllable-BA9D | ba9c Hangul Syllable-BA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ba9f Hangul Syllable-BA9F | ba9e Hangul Syllable-BA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa1 Hangul Syllable-BAA1 | baa0 Hangul Syllable-BAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa3 Hangul Syllable-BAA3 | baa2 Hangul Syllable-BAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa5 Hangul Syllable-BAA5 | baa4 Hangul Syllable-BAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa7 Hangul Syllable-BAA7 | baa6 Hangul Syllable-BAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baa9 Hangul Syllable-BAA9 | baa8 Hangul Syllable-BAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baab Hangul Syllable-BAAB | baaa Hangul Syllable-BAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baad Hangul Syllable-BAAD | baac Hangul Syllable-BAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baaf Hangul Syllable-BAAF | baae Hangul Syllable-BAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab1 Hangul Syllable-BAB1 | bab0 Hangul Syllable-BAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab3 Hangul Syllable-BAB3 | bab2 Hangul Syllable-BAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab5 Hangul Syllable-BAB5 | bab4 Hangul Syllable-BAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab7 Hangul Syllable-BAB7 | bab6 Hangul Syllable-BAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bab9 Hangul Syllable-BAB9 | bab8 Hangul Syllable-BAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babb Hangul Syllable-BABB | baba Hangul Syllable-BABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babd Hangul Syllable-BABD | babc Hangul Syllable-BABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* babf Hangul Syllable-BABF | babe Hangul Syllable-BABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac1 Hangul Syllable-BAC1 | bac0 Hangul Syllable-BAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac3 Hangul Syllable-BAC3 | bac2 Hangul Syllable-BAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac5 Hangul Syllable-BAC5 | bac4 Hangul Syllable-BAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac7 Hangul Syllable-BAC7 | bac6 Hangul Syllable-BAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bac9 Hangul Syllable-BAC9 | bac8 Hangul Syllable-BAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacb Hangul Syllable-BACB | baca Hangul Syllable-BACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacd Hangul Syllable-BACD | bacc Hangul Syllable-BACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bacf Hangul Syllable-BACF | bace Hangul Syllable-BACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad1 Hangul Syllable-BAD1 | bad0 Hangul Syllable-BAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad3 Hangul Syllable-BAD3 | bad2 Hangul Syllable-BAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad5 Hangul Syllable-BAD5 | bad4 Hangul Syllable-BAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad7 Hangul Syllable-BAD7 | bad6 Hangul Syllable-BAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bad9 Hangul Syllable-BAD9 | bad8 Hangul Syllable-BAD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badb Hangul Syllable-BADB | bada Hangul Syllable-BADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badd Hangul Syllable-BADD | badc Hangul Syllable-BADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* badf Hangul Syllable-BADF | bade Hangul Syllable-BADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae1 Hangul Syllable-BAE1 | bae0 Hangul Syllable-BAE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae3 Hangul Syllable-BAE3 | bae2 Hangul Syllable-BAE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae5 Hangul Syllable-BAE5 | bae4 Hangul Syllable-BAE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae7 Hangul Syllable-BAE7 | bae6 Hangul Syllable-BAE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bae9 Hangul Syllable-BAE9 | bae8 Hangul Syllable-BAE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baeb Hangul Syllable-BAEB | baea Hangul Syllable-BAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baed Hangul Syllable-BAED | baec Hangul Syllable-BAEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baef Hangul Syllable-BAEF | baee Hangul Syllable-BAEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf1 Hangul Syllable-BAF1 | baf0 Hangul Syllable-BAF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf3 Hangul Syllable-BAF3 | baf2 Hangul Syllable-BAF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf5 Hangul Syllable-BAF5 | baf4 Hangul Syllable-BAF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf7 Hangul Syllable-BAF7 | baf6 Hangul Syllable-BAF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baf9 Hangul Syllable-BAF9 | baf8 Hangul Syllable-BAF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bafb Hangul Syllable-BAFB | bafa Hangul Syllable-BAFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bafd Hangul Syllable-BAFD | bafc Hangul Syllable-BAFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* baff Hangul Syllable-BAFF | bafe Hangul Syllable-BAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb01 Hangul Syllable-BB01 | bb00 Hangul Syllable-BB00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb03 Hangul Syllable-BB03 | bb02 Hangul Syllable-BB02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb05 Hangul Syllable-BB05 | bb04 Hangul Syllable-BB04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb07 Hangul Syllable-BB07 | bb06 Hangul Syllable-BB06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb09 Hangul Syllable-BB09 | bb08 Hangul Syllable-BB08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0b Hangul Syllable-BB0B | bb0a Hangul Syllable-BB0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0d Hangul Syllable-BB0D | bb0c Hangul Syllable-BB0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb0f Hangul Syllable-BB0F | bb0e Hangul Syllable-BB0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb11 Hangul Syllable-BB11 | bb10 Hangul Syllable-BB10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb13 Hangul Syllable-BB13 | bb12 Hangul Syllable-BB12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb15 Hangul Syllable-BB15 | bb14 Hangul Syllable-BB14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb17 Hangul Syllable-BB17 | bb16 Hangul Syllable-BB16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb19 Hangul Syllable-BB19 | bb18 Hangul Syllable-BB18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1b Hangul Syllable-BB1B | bb1a Hangul Syllable-BB1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1d Hangul Syllable-BB1D | bb1c Hangul Syllable-BB1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb1f Hangul Syllable-BB1F | bb1e Hangul Syllable-BB1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb21 Hangul Syllable-BB21 | bb20 Hangul Syllable-BB20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb23 Hangul Syllable-BB23 | bb22 Hangul Syllable-BB22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb25 Hangul Syllable-BB25 | bb24 Hangul Syllable-BB24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb27 Hangul Syllable-BB27 | bb26 Hangul Syllable-BB26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb29 Hangul Syllable-BB29 | bb28 Hangul Syllable-BB28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2b Hangul Syllable-BB2B | bb2a Hangul Syllable-BB2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2d Hangul Syllable-BB2D | bb2c Hangul Syllable-BB2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb2f Hangul Syllable-BB2F | bb2e Hangul Syllable-BB2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb31 Hangul Syllable-BB31 | bb30 Hangul Syllable-BB30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb33 Hangul Syllable-BB33 | bb32 Hangul Syllable-BB32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb35 Hangul Syllable-BB35 | bb34 Hangul Syllable-BB34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb37 Hangul Syllable-BB37 | bb36 Hangul Syllable-BB36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb39 Hangul Syllable-BB39 | bb38 Hangul Syllable-BB38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3b Hangul Syllable-BB3B | bb3a Hangul Syllable-BB3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3d Hangul Syllable-BB3D | bb3c Hangul Syllable-BB3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb3f Hangul Syllable-BB3F | bb3e Hangul Syllable-BB3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb41 Hangul Syllable-BB41 | bb40 Hangul Syllable-BB40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb43 Hangul Syllable-BB43 | bb42 Hangul Syllable-BB42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb45 Hangul Syllable-BB45 | bb44 Hangul Syllable-BB44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb47 Hangul Syllable-BB47 | bb46 Hangul Syllable-BB46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb49 Hangul Syllable-BB49 | bb48 Hangul Syllable-BB48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4b Hangul Syllable-BB4B | bb4a Hangul Syllable-BB4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4d Hangul Syllable-BB4D | bb4c Hangul Syllable-BB4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb4f Hangul Syllable-BB4F | bb4e Hangul Syllable-BB4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb51 Hangul Syllable-BB51 | bb50 Hangul Syllable-BB50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb53 Hangul Syllable-BB53 | bb52 Hangul Syllable-BB52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb55 Hangul Syllable-BB55 | bb54 Hangul Syllable-BB54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb57 Hangul Syllable-BB57 | bb56 Hangul Syllable-BB56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb59 Hangul Syllable-BB59 | bb58 Hangul Syllable-BB58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5b Hangul Syllable-BB5B | bb5a Hangul Syllable-BB5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5d Hangul Syllable-BB5D | bb5c Hangul Syllable-BB5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb5f Hangul Syllable-BB5F | bb5e Hangul Syllable-BB5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb61 Hangul Syllable-BB61 | bb60 Hangul Syllable-BB60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb63 Hangul Syllable-BB63 | bb62 Hangul Syllable-BB62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb65 Hangul Syllable-BB65 | bb64 Hangul Syllable-BB64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb67 Hangul Syllable-BB67 | bb66 Hangul Syllable-BB66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb69 Hangul Syllable-BB69 | bb68 Hangul Syllable-BB68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6b Hangul Syllable-BB6B | bb6a Hangul Syllable-BB6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6d Hangul Syllable-BB6D | bb6c Hangul Syllable-BB6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb6f Hangul Syllable-BB6F | bb6e Hangul Syllable-BB6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb71 Hangul Syllable-BB71 | bb70 Hangul Syllable-BB70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb73 Hangul Syllable-BB73 | bb72 Hangul Syllable-BB72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb75 Hangul Syllable-BB75 | bb74 Hangul Syllable-BB74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb77 Hangul Syllable-BB77 | bb76 Hangul Syllable-BB76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb79 Hangul Syllable-BB79 | bb78 Hangul Syllable-BB78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7b Hangul Syllable-BB7B | bb7a Hangul Syllable-BB7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7d Hangul Syllable-BB7D | bb7c Hangul Syllable-BB7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb7f Hangul Syllable-BB7F | bb7e Hangul Syllable-BB7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb81 Hangul Syllable-BB81 | bb80 Hangul Syllable-BB80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb83 Hangul Syllable-BB83 | bb82 Hangul Syllable-BB82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb85 Hangul Syllable-BB85 | bb84 Hangul Syllable-BB84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb87 Hangul Syllable-BB87 | bb86 Hangul Syllable-BB86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb89 Hangul Syllable-BB89 | bb88 Hangul Syllable-BB88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8b Hangul Syllable-BB8B | bb8a Hangul Syllable-BB8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8d Hangul Syllable-BB8D | bb8c Hangul Syllable-BB8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb8f Hangul Syllable-BB8F | bb8e Hangul Syllable-BB8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb91 Hangul Syllable-BB91 | bb90 Hangul Syllable-BB90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb93 Hangul Syllable-BB93 | bb92 Hangul Syllable-BB92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb95 Hangul Syllable-BB95 | bb94 Hangul Syllable-BB94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb97 Hangul Syllable-BB97 | bb96 Hangul Syllable-BB96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb99 Hangul Syllable-BB99 | bb98 Hangul Syllable-BB98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9b Hangul Syllable-BB9B | bb9a Hangul Syllable-BB9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9d Hangul Syllable-BB9D | bb9c Hangul Syllable-BB9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bb9f Hangul Syllable-BB9F | bb9e Hangul Syllable-BB9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba1 Hangul Syllable-BBA1 | bba0 Hangul Syllable-BBA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba3 Hangul Syllable-BBA3 | bba2 Hangul Syllable-BBA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba5 Hangul Syllable-BBA5 | bba4 Hangul Syllable-BBA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba7 Hangul Syllable-BBA7 | bba6 Hangul Syllable-BBA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bba9 Hangul Syllable-BBA9 | bba8 Hangul Syllable-BBA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbab Hangul Syllable-BBAB | bbaa Hangul Syllable-BBAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbad Hangul Syllable-BBAD | bbac Hangul Syllable-BBAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbaf Hangul Syllable-BBAF | bbae Hangul Syllable-BBAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb1 Hangul Syllable-BBB1 | bbb0 Hangul Syllable-BBB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb3 Hangul Syllable-BBB3 | bbb2 Hangul Syllable-BBB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb5 Hangul Syllable-BBB5 | bbb4 Hangul Syllable-BBB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb7 Hangul Syllable-BBB7 | bbb6 Hangul Syllable-BBB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbb9 Hangul Syllable-BBB9 | bbb8 Hangul Syllable-BBB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbb Hangul Syllable-BBBB | bbba Hangul Syllable-BBBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbd Hangul Syllable-BBBD | bbbc Hangul Syllable-BBBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbbf Hangul Syllable-BBBF | bbbe Hangul Syllable-BBBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc1 Hangul Syllable-BBC1 | bbc0 Hangul Syllable-BBC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc3 Hangul Syllable-BBC3 | bbc2 Hangul Syllable-BBC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc5 Hangul Syllable-BBC5 | bbc4 Hangul Syllable-BBC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc7 Hangul Syllable-BBC7 | bbc6 Hangul Syllable-BBC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbc9 Hangul Syllable-BBC9 | bbc8 Hangul Syllable-BBC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcb Hangul Syllable-BBCB | bbca Hangul Syllable-BBCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcd Hangul Syllable-BBCD | bbcc Hangul Syllable-BBCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbcf Hangul Syllable-BBCF | bbce Hangul Syllable-BBCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd1 Hangul Syllable-BBD1 | bbd0 Hangul Syllable-BBD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd3 Hangul Syllable-BBD3 | bbd2 Hangul Syllable-BBD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd5 Hangul Syllable-BBD5 | bbd4 Hangul Syllable-BBD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd7 Hangul Syllable-BBD7 | bbd6 Hangul Syllable-BBD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbd9 Hangul Syllable-BBD9 | bbd8 Hangul Syllable-BBD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdb Hangul Syllable-BBDB | bbda Hangul Syllable-BBDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdd Hangul Syllable-BBDD | bbdc Hangul Syllable-BBDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbdf Hangul Syllable-BBDF | bbde Hangul Syllable-BBDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe1 Hangul Syllable-BBE1 | bbe0 Hangul Syllable-BBE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe3 Hangul Syllable-BBE3 | bbe2 Hangul Syllable-BBE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe5 Hangul Syllable-BBE5 | bbe4 Hangul Syllable-BBE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe7 Hangul Syllable-BBE7 | bbe6 Hangul Syllable-BBE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbe9 Hangul Syllable-BBE9 | bbe8 Hangul Syllable-BBE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbeb Hangul Syllable-BBEB | bbea Hangul Syllable-BBEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbed Hangul Syllable-BBED | bbec Hangul Syllable-BBEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbef Hangul Syllable-BBEF | bbee Hangul Syllable-BBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf1 Hangul Syllable-BBF1 | bbf0 Hangul Syllable-BBF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf3 Hangul Syllable-BBF3 | bbf2 Hangul Syllable-BBF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf5 Hangul Syllable-BBF5 | bbf4 Hangul Syllable-BBF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf7 Hangul Syllable-BBF7 | bbf6 Hangul Syllable-BBF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbf9 Hangul Syllable-BBF9 | bbf8 Hangul Syllable-BBF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbfb Hangul Syllable-BBFB | bbfa Hangul Syllable-BBFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbfd Hangul Syllable-BBFD | bbfc Hangul Syllable-BBFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bbff Hangul Syllable-BBFF | bbfe Hangul Syllable-BBFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc01 Hangul Syllable-BC01 | bc00 Hangul Syllable-BC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc03 Hangul Syllable-BC03 | bc02 Hangul Syllable-BC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc05 Hangul Syllable-BC05 | bc04 Hangul Syllable-BC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc07 Hangul Syllable-BC07 | bc06 Hangul Syllable-BC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc09 Hangul Syllable-BC09 | bc08 Hangul Syllable-BC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0b Hangul Syllable-BC0B | bc0a Hangul Syllable-BC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0d Hangul Syllable-BC0D | bc0c Hangul Syllable-BC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc0f Hangul Syllable-BC0F | bc0e Hangul Syllable-BC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc11 Hangul Syllable-BC11 | bc10 Hangul Syllable-BC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc13 Hangul Syllable-BC13 | bc12 Hangul Syllable-BC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc15 Hangul Syllable-BC15 | bc14 Hangul Syllable-BC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc17 Hangul Syllable-BC17 | bc16 Hangul Syllable-BC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc19 Hangul Syllable-BC19 | bc18 Hangul Syllable-BC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1b Hangul Syllable-BC1B | bc1a Hangul Syllable-BC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1d Hangul Syllable-BC1D | bc1c Hangul Syllable-BC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc1f Hangul Syllable-BC1F | bc1e Hangul Syllable-BC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc21 Hangul Syllable-BC21 | bc20 Hangul Syllable-BC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc23 Hangul Syllable-BC23 | bc22 Hangul Syllable-BC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc25 Hangul Syllable-BC25 | bc24 Hangul Syllable-BC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc27 Hangul Syllable-BC27 | bc26 Hangul Syllable-BC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc29 Hangul Syllable-BC29 | bc28 Hangul Syllable-BC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2b Hangul Syllable-BC2B | bc2a Hangul Syllable-BC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2d Hangul Syllable-BC2D | bc2c Hangul Syllable-BC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc2f Hangul Syllable-BC2F | bc2e Hangul Syllable-BC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc31 Hangul Syllable-BC31 | bc30 Hangul Syllable-BC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc33 Hangul Syllable-BC33 | bc32 Hangul Syllable-BC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc35 Hangul Syllable-BC35 | bc34 Hangul Syllable-BC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc37 Hangul Syllable-BC37 | bc36 Hangul Syllable-BC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc39 Hangul Syllable-BC39 | bc38 Hangul Syllable-BC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3b Hangul Syllable-BC3B | bc3a Hangul Syllable-BC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3d Hangul Syllable-BC3D | bc3c Hangul Syllable-BC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc3f Hangul Syllable-BC3F | bc3e Hangul Syllable-BC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc41 Hangul Syllable-BC41 | bc40 Hangul Syllable-BC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc43 Hangul Syllable-BC43 | bc42 Hangul Syllable-BC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc45 Hangul Syllable-BC45 | bc44 Hangul Syllable-BC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc47 Hangul Syllable-BC47 | bc46 Hangul Syllable-BC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc49 Hangul Syllable-BC49 | bc48 Hangul Syllable-BC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4b Hangul Syllable-BC4B | bc4a Hangul Syllable-BC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4d Hangul Syllable-BC4D | bc4c Hangul Syllable-BC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc4f Hangul Syllable-BC4F | bc4e Hangul Syllable-BC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc51 Hangul Syllable-BC51 | bc50 Hangul Syllable-BC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc53 Hangul Syllable-BC53 | bc52 Hangul Syllable-BC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc55 Hangul Syllable-BC55 | bc54 Hangul Syllable-BC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc57 Hangul Syllable-BC57 | bc56 Hangul Syllable-BC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc59 Hangul Syllable-BC59 | bc58 Hangul Syllable-BC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5b Hangul Syllable-BC5B | bc5a Hangul Syllable-BC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5d Hangul Syllable-BC5D | bc5c Hangul Syllable-BC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc5f Hangul Syllable-BC5F | bc5e Hangul Syllable-BC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc61 Hangul Syllable-BC61 | bc60 Hangul Syllable-BC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc63 Hangul Syllable-BC63 | bc62 Hangul Syllable-BC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc65 Hangul Syllable-BC65 | bc64 Hangul Syllable-BC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc67 Hangul Syllable-BC67 | bc66 Hangul Syllable-BC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc69 Hangul Syllable-BC69 | bc68 Hangul Syllable-BC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6b Hangul Syllable-BC6B | bc6a Hangul Syllable-BC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6d Hangul Syllable-BC6D | bc6c Hangul Syllable-BC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc6f Hangul Syllable-BC6F | bc6e Hangul Syllable-BC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc71 Hangul Syllable-BC71 | bc70 Hangul Syllable-BC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc73 Hangul Syllable-BC73 | bc72 Hangul Syllable-BC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc75 Hangul Syllable-BC75 | bc74 Hangul Syllable-BC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc77 Hangul Syllable-BC77 | bc76 Hangul Syllable-BC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc79 Hangul Syllable-BC79 | bc78 Hangul Syllable-BC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7b Hangul Syllable-BC7B | bc7a Hangul Syllable-BC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7d Hangul Syllable-BC7D | bc7c Hangul Syllable-BC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc7f Hangul Syllable-BC7F | bc7e Hangul Syllable-BC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc81 Hangul Syllable-BC81 | bc80 Hangul Syllable-BC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc83 Hangul Syllable-BC83 | bc82 Hangul Syllable-BC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc85 Hangul Syllable-BC85 | bc84 Hangul Syllable-BC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc87 Hangul Syllable-BC87 | bc86 Hangul Syllable-BC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc89 Hangul Syllable-BC89 | bc88 Hangul Syllable-BC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8b Hangul Syllable-BC8B | bc8a Hangul Syllable-BC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8d Hangul Syllable-BC8D | bc8c Hangul Syllable-BC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc8f Hangul Syllable-BC8F | bc8e Hangul Syllable-BC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc91 Hangul Syllable-BC91 | bc90 Hangul Syllable-BC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc93 Hangul Syllable-BC93 | bc92 Hangul Syllable-BC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc95 Hangul Syllable-BC95 | bc94 Hangul Syllable-BC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc97 Hangul Syllable-BC97 | bc96 Hangul Syllable-BC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc99 Hangul Syllable-BC99 | bc98 Hangul Syllable-BC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9b Hangul Syllable-BC9B | bc9a Hangul Syllable-BC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9d Hangul Syllable-BC9D | bc9c Hangul Syllable-BC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bc9f Hangul Syllable-BC9F | bc9e Hangul Syllable-BC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca1 Hangul Syllable-BCA1 | bca0 Hangul Syllable-BCA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca3 Hangul Syllable-BCA3 | bca2 Hangul Syllable-BCA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca5 Hangul Syllable-BCA5 | bca4 Hangul Syllable-BCA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca7 Hangul Syllable-BCA7 | bca6 Hangul Syllable-BCA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bca9 Hangul Syllable-BCA9 | bca8 Hangul Syllable-BCA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcab Hangul Syllable-BCAB | bcaa Hangul Syllable-BCAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcad Hangul Syllable-BCAD | bcac Hangul Syllable-BCAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcaf Hangul Syllable-BCAF | bcae Hangul Syllable-BCAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb1 Hangul Syllable-BCB1 | bcb0 Hangul Syllable-BCB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb3 Hangul Syllable-BCB3 | bcb2 Hangul Syllable-BCB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb5 Hangul Syllable-BCB5 | bcb4 Hangul Syllable-BCB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb7 Hangul Syllable-BCB7 | bcb6 Hangul Syllable-BCB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcb9 Hangul Syllable-BCB9 | bcb8 Hangul Syllable-BCB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbb Hangul Syllable-BCBB | bcba Hangul Syllable-BCBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbd Hangul Syllable-BCBD | bcbc Hangul Syllable-BCBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcbf Hangul Syllable-BCBF | bcbe Hangul Syllable-BCBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc1 Hangul Syllable-BCC1 | bcc0 Hangul Syllable-BCC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc3 Hangul Syllable-BCC3 | bcc2 Hangul Syllable-BCC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc5 Hangul Syllable-BCC5 | bcc4 Hangul Syllable-BCC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc7 Hangul Syllable-BCC7 | bcc6 Hangul Syllable-BCC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcc9 Hangul Syllable-BCC9 | bcc8 Hangul Syllable-BCC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccb Hangul Syllable-BCCB | bcca Hangul Syllable-BCCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccd Hangul Syllable-BCCD | bccc Hangul Syllable-BCCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bccf Hangul Syllable-BCCF | bcce Hangul Syllable-BCCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd1 Hangul Syllable-BCD1 | bcd0 Hangul Syllable-BCD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd3 Hangul Syllable-BCD3 | bcd2 Hangul Syllable-BCD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd5 Hangul Syllable-BCD5 | bcd4 Hangul Syllable-BCD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd7 Hangul Syllable-BCD7 | bcd6 Hangul Syllable-BCD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcd9 Hangul Syllable-BCD9 | bcd8 Hangul Syllable-BCD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdb Hangul Syllable-BCDB | bcda Hangul Syllable-BCDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdd Hangul Syllable-BCDD | bcdc Hangul Syllable-BCDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcdf Hangul Syllable-BCDF | bcde Hangul Syllable-BCDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce1 Hangul Syllable-BCE1 | bce0 Hangul Syllable-BCE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce3 Hangul Syllable-BCE3 | bce2 Hangul Syllable-BCE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce5 Hangul Syllable-BCE5 | bce4 Hangul Syllable-BCE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce7 Hangul Syllable-BCE7 | bce6 Hangul Syllable-BCE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bce9 Hangul Syllable-BCE9 | bce8 Hangul Syllable-BCE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bceb Hangul Syllable-BCEB | bcea Hangul Syllable-BCEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bced Hangul Syllable-BCED | bcec Hangul Syllable-BCEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcef Hangul Syllable-BCEF | bcee Hangul Syllable-BCEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf1 Hangul Syllable-BCF1 | bcf0 Hangul Syllable-BCF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf3 Hangul Syllable-BCF3 | bcf2 Hangul Syllable-BCF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf5 Hangul Syllable-BCF5 | bcf4 Hangul Syllable-BCF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf7 Hangul Syllable-BCF7 | bcf6 Hangul Syllable-BCF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcf9 Hangul Syllable-BCF9 | bcf8 Hangul Syllable-BCF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcfb Hangul Syllable-BCFB | bcfa Hangul Syllable-BCFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcfd Hangul Syllable-BCFD | bcfc Hangul Syllable-BCFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bcff Hangul Syllable-BCFF | bcfe Hangul Syllable-BCFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd01 Hangul Syllable-BD01 | bd00 Hangul Syllable-BD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd03 Hangul Syllable-BD03 | bd02 Hangul Syllable-BD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd05 Hangul Syllable-BD05 | bd04 Hangul Syllable-BD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd07 Hangul Syllable-BD07 | bd06 Hangul Syllable-BD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd09 Hangul Syllable-BD09 | bd08 Hangul Syllable-BD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0b Hangul Syllable-BD0B | bd0a Hangul Syllable-BD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0d Hangul Syllable-BD0D | bd0c Hangul Syllable-BD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd0f Hangul Syllable-BD0F | bd0e Hangul Syllable-BD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd11 Hangul Syllable-BD11 | bd10 Hangul Syllable-BD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd13 Hangul Syllable-BD13 | bd12 Hangul Syllable-BD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd15 Hangul Syllable-BD15 | bd14 Hangul Syllable-BD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd17 Hangul Syllable-BD17 | bd16 Hangul Syllable-BD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd19 Hangul Syllable-BD19 | bd18 Hangul Syllable-BD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1b Hangul Syllable-BD1B | bd1a Hangul Syllable-BD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1d Hangul Syllable-BD1D | bd1c Hangul Syllable-BD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd1f Hangul Syllable-BD1F | bd1e Hangul Syllable-BD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd21 Hangul Syllable-BD21 | bd20 Hangul Syllable-BD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd23 Hangul Syllable-BD23 | bd22 Hangul Syllable-BD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd25 Hangul Syllable-BD25 | bd24 Hangul Syllable-BD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd27 Hangul Syllable-BD27 | bd26 Hangul Syllable-BD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd29 Hangul Syllable-BD29 | bd28 Hangul Syllable-BD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2b Hangul Syllable-BD2B | bd2a Hangul Syllable-BD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2d Hangul Syllable-BD2D | bd2c Hangul Syllable-BD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd2f Hangul Syllable-BD2F | bd2e Hangul Syllable-BD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd31 Hangul Syllable-BD31 | bd30 Hangul Syllable-BD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd33 Hangul Syllable-BD33 | bd32 Hangul Syllable-BD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd35 Hangul Syllable-BD35 | bd34 Hangul Syllable-BD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd37 Hangul Syllable-BD37 | bd36 Hangul Syllable-BD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd39 Hangul Syllable-BD39 | bd38 Hangul Syllable-BD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3b Hangul Syllable-BD3B | bd3a Hangul Syllable-BD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3d Hangul Syllable-BD3D | bd3c Hangul Syllable-BD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd3f Hangul Syllable-BD3F | bd3e Hangul Syllable-BD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd41 Hangul Syllable-BD41 | bd40 Hangul Syllable-BD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd43 Hangul Syllable-BD43 | bd42 Hangul Syllable-BD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd45 Hangul Syllable-BD45 | bd44 Hangul Syllable-BD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd47 Hangul Syllable-BD47 | bd46 Hangul Syllable-BD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd49 Hangul Syllable-BD49 | bd48 Hangul Syllable-BD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4b Hangul Syllable-BD4B | bd4a Hangul Syllable-BD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4d Hangul Syllable-BD4D | bd4c Hangul Syllable-BD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd4f Hangul Syllable-BD4F | bd4e Hangul Syllable-BD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd51 Hangul Syllable-BD51 | bd50 Hangul Syllable-BD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd53 Hangul Syllable-BD53 | bd52 Hangul Syllable-BD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd55 Hangul Syllable-BD55 | bd54 Hangul Syllable-BD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd57 Hangul Syllable-BD57 | bd56 Hangul Syllable-BD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd59 Hangul Syllable-BD59 | bd58 Hangul Syllable-BD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5b Hangul Syllable-BD5B | bd5a Hangul Syllable-BD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5d Hangul Syllable-BD5D | bd5c Hangul Syllable-BD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd5f Hangul Syllable-BD5F | bd5e Hangul Syllable-BD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd61 Hangul Syllable-BD61 | bd60 Hangul Syllable-BD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd63 Hangul Syllable-BD63 | bd62 Hangul Syllable-BD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd65 Hangul Syllable-BD65 | bd64 Hangul Syllable-BD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd67 Hangul Syllable-BD67 | bd66 Hangul Syllable-BD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd69 Hangul Syllable-BD69 | bd68 Hangul Syllable-BD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6b Hangul Syllable-BD6B | bd6a Hangul Syllable-BD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6d Hangul Syllable-BD6D | bd6c Hangul Syllable-BD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd6f Hangul Syllable-BD6F | bd6e Hangul Syllable-BD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd71 Hangul Syllable-BD71 | bd70 Hangul Syllable-BD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd73 Hangul Syllable-BD73 | bd72 Hangul Syllable-BD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd75 Hangul Syllable-BD75 | bd74 Hangul Syllable-BD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd77 Hangul Syllable-BD77 | bd76 Hangul Syllable-BD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd79 Hangul Syllable-BD79 | bd78 Hangul Syllable-BD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7b Hangul Syllable-BD7B | bd7a Hangul Syllable-BD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7d Hangul Syllable-BD7D | bd7c Hangul Syllable-BD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd7f Hangul Syllable-BD7F | bd7e Hangul Syllable-BD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd81 Hangul Syllable-BD81 | bd80 Hangul Syllable-BD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd83 Hangul Syllable-BD83 | bd82 Hangul Syllable-BD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd85 Hangul Syllable-BD85 | bd84 Hangul Syllable-BD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd87 Hangul Syllable-BD87 | bd86 Hangul Syllable-BD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd89 Hangul Syllable-BD89 | bd88 Hangul Syllable-BD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8b Hangul Syllable-BD8B | bd8a Hangul Syllable-BD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8d Hangul Syllable-BD8D | bd8c Hangul Syllable-BD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd8f Hangul Syllable-BD8F | bd8e Hangul Syllable-BD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd91 Hangul Syllable-BD91 | bd90 Hangul Syllable-BD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd93 Hangul Syllable-BD93 | bd92 Hangul Syllable-BD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd95 Hangul Syllable-BD95 | bd94 Hangul Syllable-BD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd97 Hangul Syllable-BD97 | bd96 Hangul Syllable-BD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd99 Hangul Syllable-BD99 | bd98 Hangul Syllable-BD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9b Hangul Syllable-BD9B | bd9a Hangul Syllable-BD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9d Hangul Syllable-BD9D | bd9c Hangul Syllable-BD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bd9f Hangul Syllable-BD9F | bd9e Hangul Syllable-BD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda1 Hangul Syllable-BDA1 | bda0 Hangul Syllable-BDA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda3 Hangul Syllable-BDA3 | bda2 Hangul Syllable-BDA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda5 Hangul Syllable-BDA5 | bda4 Hangul Syllable-BDA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda7 Hangul Syllable-BDA7 | bda6 Hangul Syllable-BDA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bda9 Hangul Syllable-BDA9 | bda8 Hangul Syllable-BDA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdab Hangul Syllable-BDAB | bdaa Hangul Syllable-BDAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdad Hangul Syllable-BDAD | bdac Hangul Syllable-BDAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdaf Hangul Syllable-BDAF | bdae Hangul Syllable-BDAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb1 Hangul Syllable-BDB1 | bdb0 Hangul Syllable-BDB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb3 Hangul Syllable-BDB3 | bdb2 Hangul Syllable-BDB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb5 Hangul Syllable-BDB5 | bdb4 Hangul Syllable-BDB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb7 Hangul Syllable-BDB7 | bdb6 Hangul Syllable-BDB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdb9 Hangul Syllable-BDB9 | bdb8 Hangul Syllable-BDB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbb Hangul Syllable-BDBB | bdba Hangul Syllable-BDBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbd Hangul Syllable-BDBD | bdbc Hangul Syllable-BDBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdbf Hangul Syllable-BDBF | bdbe Hangul Syllable-BDBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc1 Hangul Syllable-BDC1 | bdc0 Hangul Syllable-BDC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc3 Hangul Syllable-BDC3 | bdc2 Hangul Syllable-BDC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc5 Hangul Syllable-BDC5 | bdc4 Hangul Syllable-BDC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc7 Hangul Syllable-BDC7 | bdc6 Hangul Syllable-BDC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdc9 Hangul Syllable-BDC9 | bdc8 Hangul Syllable-BDC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcb Hangul Syllable-BDCB | bdca Hangul Syllable-BDCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcd Hangul Syllable-BDCD | bdcc Hangul Syllable-BDCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdcf Hangul Syllable-BDCF | bdce Hangul Syllable-BDCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd1 Hangul Syllable-BDD1 | bdd0 Hangul Syllable-BDD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd3 Hangul Syllable-BDD3 | bdd2 Hangul Syllable-BDD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd5 Hangul Syllable-BDD5 | bdd4 Hangul Syllable-BDD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd7 Hangul Syllable-BDD7 | bdd6 Hangul Syllable-BDD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdd9 Hangul Syllable-BDD9 | bdd8 Hangul Syllable-BDD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddb Hangul Syllable-BDDB | bdda Hangul Syllable-BDDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddd Hangul Syllable-BDDD | bddc Hangul Syllable-BDDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bddf Hangul Syllable-BDDF | bdde Hangul Syllable-BDDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde1 Hangul Syllable-BDE1 | bde0 Hangul Syllable-BDE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde3 Hangul Syllable-BDE3 | bde2 Hangul Syllable-BDE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde5 Hangul Syllable-BDE5 | bde4 Hangul Syllable-BDE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde7 Hangul Syllable-BDE7 | bde6 Hangul Syllable-BDE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bde9 Hangul Syllable-BDE9 | bde8 Hangul Syllable-BDE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdeb Hangul Syllable-BDEB | bdea Hangul Syllable-BDEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bded Hangul Syllable-BDED | bdec Hangul Syllable-BDEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdef Hangul Syllable-BDEF | bdee Hangul Syllable-BDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf1 Hangul Syllable-BDF1 | bdf0 Hangul Syllable-BDF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf3 Hangul Syllable-BDF3 | bdf2 Hangul Syllable-BDF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf5 Hangul Syllable-BDF5 | bdf4 Hangul Syllable-BDF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf7 Hangul Syllable-BDF7 | bdf6 Hangul Syllable-BDF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdf9 Hangul Syllable-BDF9 | bdf8 Hangul Syllable-BDF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdfb Hangul Syllable-BDFB | bdfa Hangul Syllable-BDFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdfd Hangul Syllable-BDFD | bdfc Hangul Syllable-BDFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bdff Hangul Syllable-BDFF | bdfe Hangul Syllable-BDFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be01 Hangul Syllable-BE01 | be00 Hangul Syllable-BE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be03 Hangul Syllable-BE03 | be02 Hangul Syllable-BE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be05 Hangul Syllable-BE05 | be04 Hangul Syllable-BE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be07 Hangul Syllable-BE07 | be06 Hangul Syllable-BE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be09 Hangul Syllable-BE09 | be08 Hangul Syllable-BE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0b Hangul Syllable-BE0B | be0a Hangul Syllable-BE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0d Hangul Syllable-BE0D | be0c Hangul Syllable-BE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be0f Hangul Syllable-BE0F | be0e Hangul Syllable-BE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be11 Hangul Syllable-BE11 | be10 Hangul Syllable-BE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be13 Hangul Syllable-BE13 | be12 Hangul Syllable-BE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be15 Hangul Syllable-BE15 | be14 Hangul Syllable-BE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be17 Hangul Syllable-BE17 | be16 Hangul Syllable-BE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be19 Hangul Syllable-BE19 | be18 Hangul Syllable-BE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1b Hangul Syllable-BE1B | be1a Hangul Syllable-BE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1d Hangul Syllable-BE1D | be1c Hangul Syllable-BE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be1f Hangul Syllable-BE1F | be1e Hangul Syllable-BE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be21 Hangul Syllable-BE21 | be20 Hangul Syllable-BE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be23 Hangul Syllable-BE23 | be22 Hangul Syllable-BE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be25 Hangul Syllable-BE25 | be24 Hangul Syllable-BE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be27 Hangul Syllable-BE27 | be26 Hangul Syllable-BE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be29 Hangul Syllable-BE29 | be28 Hangul Syllable-BE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2b Hangul Syllable-BE2B | be2a Hangul Syllable-BE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2d Hangul Syllable-BE2D | be2c Hangul Syllable-BE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be2f Hangul Syllable-BE2F | be2e Hangul Syllable-BE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be31 Hangul Syllable-BE31 | be30 Hangul Syllable-BE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be33 Hangul Syllable-BE33 | be32 Hangul Syllable-BE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be35 Hangul Syllable-BE35 | be34 Hangul Syllable-BE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be37 Hangul Syllable-BE37 | be36 Hangul Syllable-BE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be39 Hangul Syllable-BE39 | be38 Hangul Syllable-BE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3b Hangul Syllable-BE3B | be3a Hangul Syllable-BE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3d Hangul Syllable-BE3D | be3c Hangul Syllable-BE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be3f Hangul Syllable-BE3F | be3e Hangul Syllable-BE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be41 Hangul Syllable-BE41 | be40 Hangul Syllable-BE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be43 Hangul Syllable-BE43 | be42 Hangul Syllable-BE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be45 Hangul Syllable-BE45 | be44 Hangul Syllable-BE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be47 Hangul Syllable-BE47 | be46 Hangul Syllable-BE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be49 Hangul Syllable-BE49 | be48 Hangul Syllable-BE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4b Hangul Syllable-BE4B | be4a Hangul Syllable-BE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4d Hangul Syllable-BE4D | be4c Hangul Syllable-BE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be4f Hangul Syllable-BE4F | be4e Hangul Syllable-BE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be51 Hangul Syllable-BE51 | be50 Hangul Syllable-BE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be53 Hangul Syllable-BE53 | be52 Hangul Syllable-BE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be55 Hangul Syllable-BE55 | be54 Hangul Syllable-BE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be57 Hangul Syllable-BE57 | be56 Hangul Syllable-BE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be59 Hangul Syllable-BE59 | be58 Hangul Syllable-BE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5b Hangul Syllable-BE5B | be5a Hangul Syllable-BE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5d Hangul Syllable-BE5D | be5c Hangul Syllable-BE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be5f Hangul Syllable-BE5F | be5e Hangul Syllable-BE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be61 Hangul Syllable-BE61 | be60 Hangul Syllable-BE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be63 Hangul Syllable-BE63 | be62 Hangul Syllable-BE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be65 Hangul Syllable-BE65 | be64 Hangul Syllable-BE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be67 Hangul Syllable-BE67 | be66 Hangul Syllable-BE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be69 Hangul Syllable-BE69 | be68 Hangul Syllable-BE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6b Hangul Syllable-BE6B | be6a Hangul Syllable-BE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6d Hangul Syllable-BE6D | be6c Hangul Syllable-BE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be6f Hangul Syllable-BE6F | be6e Hangul Syllable-BE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be71 Hangul Syllable-BE71 | be70 Hangul Syllable-BE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be73 Hangul Syllable-BE73 | be72 Hangul Syllable-BE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be75 Hangul Syllable-BE75 | be74 Hangul Syllable-BE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be77 Hangul Syllable-BE77 | be76 Hangul Syllable-BE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be79 Hangul Syllable-BE79 | be78 Hangul Syllable-BE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7b Hangul Syllable-BE7B | be7a Hangul Syllable-BE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7d Hangul Syllable-BE7D | be7c Hangul Syllable-BE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be7f Hangul Syllable-BE7F | be7e Hangul Syllable-BE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be81 Hangul Syllable-BE81 | be80 Hangul Syllable-BE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be83 Hangul Syllable-BE83 | be82 Hangul Syllable-BE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be85 Hangul Syllable-BE85 | be84 Hangul Syllable-BE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be87 Hangul Syllable-BE87 | be86 Hangul Syllable-BE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be89 Hangul Syllable-BE89 | be88 Hangul Syllable-BE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8b Hangul Syllable-BE8B | be8a Hangul Syllable-BE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8d Hangul Syllable-BE8D | be8c Hangul Syllable-BE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be8f Hangul Syllable-BE8F | be8e Hangul Syllable-BE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be91 Hangul Syllable-BE91 | be90 Hangul Syllable-BE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be93 Hangul Syllable-BE93 | be92 Hangul Syllable-BE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be95 Hangul Syllable-BE95 | be94 Hangul Syllable-BE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be97 Hangul Syllable-BE97 | be96 Hangul Syllable-BE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be99 Hangul Syllable-BE99 | be98 Hangul Syllable-BE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9b Hangul Syllable-BE9B | be9a Hangul Syllable-BE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9d Hangul Syllable-BE9D | be9c Hangul Syllable-BE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* be9f Hangul Syllable-BE9F | be9e Hangul Syllable-BE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea1 Hangul Syllable-BEA1 | bea0 Hangul Syllable-BEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea3 Hangul Syllable-BEA3 | bea2 Hangul Syllable-BEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea5 Hangul Syllable-BEA5 | bea4 Hangul Syllable-BEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea7 Hangul Syllable-BEA7 | bea6 Hangul Syllable-BEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bea9 Hangul Syllable-BEA9 | bea8 Hangul Syllable-BEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beab Hangul Syllable-BEAB | beaa Hangul Syllable-BEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bead Hangul Syllable-BEAD | beac Hangul Syllable-BEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beaf Hangul Syllable-BEAF | beae Hangul Syllable-BEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb1 Hangul Syllable-BEB1 | beb0 Hangul Syllable-BEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb3 Hangul Syllable-BEB3 | beb2 Hangul Syllable-BEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb5 Hangul Syllable-BEB5 | beb4 Hangul Syllable-BEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb7 Hangul Syllable-BEB7 | beb6 Hangul Syllable-BEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beb9 Hangul Syllable-BEB9 | beb8 Hangul Syllable-BEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebb Hangul Syllable-BEBB | beba Hangul Syllable-BEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebd Hangul Syllable-BEBD | bebc Hangul Syllable-BEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bebf Hangul Syllable-BEBF | bebe Hangul Syllable-BEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec1 Hangul Syllable-BEC1 | bec0 Hangul Syllable-BEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec3 Hangul Syllable-BEC3 | bec2 Hangul Syllable-BEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec5 Hangul Syllable-BEC5 | bec4 Hangul Syllable-BEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec7 Hangul Syllable-BEC7 | bec6 Hangul Syllable-BEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bec9 Hangul Syllable-BEC9 | bec8 Hangul Syllable-BEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becb Hangul Syllable-BECB | beca Hangul Syllable-BECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becd Hangul Syllable-BECD | becc Hangul Syllable-BECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* becf Hangul Syllable-BECF | bece Hangul Syllable-BECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed1 Hangul Syllable-BED1 | bed0 Hangul Syllable-BED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed3 Hangul Syllable-BED3 | bed2 Hangul Syllable-BED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed5 Hangul Syllable-BED5 | bed4 Hangul Syllable-BED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed7 Hangul Syllable-BED7 | bed6 Hangul Syllable-BED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bed9 Hangul Syllable-BED9 | bed8 Hangul Syllable-BED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedb Hangul Syllable-BEDB | beda Hangul Syllable-BEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedd Hangul Syllable-BEDD | bedc Hangul Syllable-BEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bedf Hangul Syllable-BEDF | bede Hangul Syllable-BEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee1 Hangul Syllable-BEE1 | bee0 Hangul Syllable-BEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee3 Hangul Syllable-BEE3 | bee2 Hangul Syllable-BEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee5 Hangul Syllable-BEE5 | bee4 Hangul Syllable-BEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee7 Hangul Syllable-BEE7 | bee6 Hangul Syllable-BEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bee9 Hangul Syllable-BEE9 | bee8 Hangul Syllable-BEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beeb Hangul Syllable-BEEB | beea Hangul Syllable-BEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beed Hangul Syllable-BEED | beec Hangul Syllable-BEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beef Hangul Syllable-BEEF | beee Hangul Syllable-BEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef1 Hangul Syllable-BEF1 | bef0 Hangul Syllable-BEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef3 Hangul Syllable-BEF3 | bef2 Hangul Syllable-BEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef5 Hangul Syllable-BEF5 | bef4 Hangul Syllable-BEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef7 Hangul Syllable-BEF7 | bef6 Hangul Syllable-BEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bef9 Hangul Syllable-BEF9 | bef8 Hangul Syllable-BEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* befb Hangul Syllable-BEFB | befa Hangul Syllable-BEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* befd Hangul Syllable-BEFD | befc Hangul Syllable-BEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* beff Hangul Syllable-BEFF | befe Hangul Syllable-BEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf01 Hangul Syllable-BF01 | bf00 Hangul Syllable-BF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf03 Hangul Syllable-BF03 | bf02 Hangul Syllable-BF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf05 Hangul Syllable-BF05 | bf04 Hangul Syllable-BF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf07 Hangul Syllable-BF07 | bf06 Hangul Syllable-BF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf09 Hangul Syllable-BF09 | bf08 Hangul Syllable-BF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0b Hangul Syllable-BF0B | bf0a Hangul Syllable-BF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0d Hangul Syllable-BF0D | bf0c Hangul Syllable-BF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf0f Hangul Syllable-BF0F | bf0e Hangul Syllable-BF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf11 Hangul Syllable-BF11 | bf10 Hangul Syllable-BF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf13 Hangul Syllable-BF13 | bf12 Hangul Syllable-BF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf15 Hangul Syllable-BF15 | bf14 Hangul Syllable-BF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf17 Hangul Syllable-BF17 | bf16 Hangul Syllable-BF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf19 Hangul Syllable-BF19 | bf18 Hangul Syllable-BF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1b Hangul Syllable-BF1B | bf1a Hangul Syllable-BF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1d Hangul Syllable-BF1D | bf1c Hangul Syllable-BF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf1f Hangul Syllable-BF1F | bf1e Hangul Syllable-BF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf21 Hangul Syllable-BF21 | bf20 Hangul Syllable-BF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf23 Hangul Syllable-BF23 | bf22 Hangul Syllable-BF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf25 Hangul Syllable-BF25 | bf24 Hangul Syllable-BF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf27 Hangul Syllable-BF27 | bf26 Hangul Syllable-BF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf29 Hangul Syllable-BF29 | bf28 Hangul Syllable-BF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2b Hangul Syllable-BF2B | bf2a Hangul Syllable-BF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2d Hangul Syllable-BF2D | bf2c Hangul Syllable-BF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf2f Hangul Syllable-BF2F | bf2e Hangul Syllable-BF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf31 Hangul Syllable-BF31 | bf30 Hangul Syllable-BF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf33 Hangul Syllable-BF33 | bf32 Hangul Syllable-BF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf35 Hangul Syllable-BF35 | bf34 Hangul Syllable-BF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf37 Hangul Syllable-BF37 | bf36 Hangul Syllable-BF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf39 Hangul Syllable-BF39 | bf38 Hangul Syllable-BF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3b Hangul Syllable-BF3B | bf3a Hangul Syllable-BF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3d Hangul Syllable-BF3D | bf3c Hangul Syllable-BF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf3f Hangul Syllable-BF3F | bf3e Hangul Syllable-BF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf41 Hangul Syllable-BF41 | bf40 Hangul Syllable-BF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf43 Hangul Syllable-BF43 | bf42 Hangul Syllable-BF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf45 Hangul Syllable-BF45 | bf44 Hangul Syllable-BF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf47 Hangul Syllable-BF47 | bf46 Hangul Syllable-BF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf49 Hangul Syllable-BF49 | bf48 Hangul Syllable-BF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4b Hangul Syllable-BF4B | bf4a Hangul Syllable-BF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4d Hangul Syllable-BF4D | bf4c Hangul Syllable-BF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf4f Hangul Syllable-BF4F | bf4e Hangul Syllable-BF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf51 Hangul Syllable-BF51 | bf50 Hangul Syllable-BF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf53 Hangul Syllable-BF53 | bf52 Hangul Syllable-BF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf55 Hangul Syllable-BF55 | bf54 Hangul Syllable-BF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf57 Hangul Syllable-BF57 | bf56 Hangul Syllable-BF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf59 Hangul Syllable-BF59 | bf58 Hangul Syllable-BF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5b Hangul Syllable-BF5B | bf5a Hangul Syllable-BF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5d Hangul Syllable-BF5D | bf5c Hangul Syllable-BF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf5f Hangul Syllable-BF5F | bf5e Hangul Syllable-BF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf61 Hangul Syllable-BF61 | bf60 Hangul Syllable-BF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf63 Hangul Syllable-BF63 | bf62 Hangul Syllable-BF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf65 Hangul Syllable-BF65 | bf64 Hangul Syllable-BF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf67 Hangul Syllable-BF67 | bf66 Hangul Syllable-BF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf69 Hangul Syllable-BF69 | bf68 Hangul Syllable-BF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6b Hangul Syllable-BF6B | bf6a Hangul Syllable-BF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6d Hangul Syllable-BF6D | bf6c Hangul Syllable-BF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf6f Hangul Syllable-BF6F | bf6e Hangul Syllable-BF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf71 Hangul Syllable-BF71 | bf70 Hangul Syllable-BF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf73 Hangul Syllable-BF73 | bf72 Hangul Syllable-BF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf75 Hangul Syllable-BF75 | bf74 Hangul Syllable-BF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf77 Hangul Syllable-BF77 | bf76 Hangul Syllable-BF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf79 Hangul Syllable-BF79 | bf78 Hangul Syllable-BF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7b Hangul Syllable-BF7B | bf7a Hangul Syllable-BF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7d Hangul Syllable-BF7D | bf7c Hangul Syllable-BF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf7f Hangul Syllable-BF7F | bf7e Hangul Syllable-BF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf81 Hangul Syllable-BF81 | bf80 Hangul Syllable-BF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf83 Hangul Syllable-BF83 | bf82 Hangul Syllable-BF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf85 Hangul Syllable-BF85 | bf84 Hangul Syllable-BF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf87 Hangul Syllable-BF87 | bf86 Hangul Syllable-BF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf89 Hangul Syllable-BF89 | bf88 Hangul Syllable-BF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8b Hangul Syllable-BF8B | bf8a Hangul Syllable-BF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8d Hangul Syllable-BF8D | bf8c Hangul Syllable-BF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf8f Hangul Syllable-BF8F | bf8e Hangul Syllable-BF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf91 Hangul Syllable-BF91 | bf90 Hangul Syllable-BF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf93 Hangul Syllable-BF93 | bf92 Hangul Syllable-BF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf95 Hangul Syllable-BF95 | bf94 Hangul Syllable-BF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf97 Hangul Syllable-BF97 | bf96 Hangul Syllable-BF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf99 Hangul Syllable-BF99 | bf98 Hangul Syllable-BF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9b Hangul Syllable-BF9B | bf9a Hangul Syllable-BF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9d Hangul Syllable-BF9D | bf9c Hangul Syllable-BF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bf9f Hangul Syllable-BF9F | bf9e Hangul Syllable-BF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa1 Hangul Syllable-BFA1 | bfa0 Hangul Syllable-BFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa3 Hangul Syllable-BFA3 | bfa2 Hangul Syllable-BFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa5 Hangul Syllable-BFA5 | bfa4 Hangul Syllable-BFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa7 Hangul Syllable-BFA7 | bfa6 Hangul Syllable-BFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfa9 Hangul Syllable-BFA9 | bfa8 Hangul Syllable-BFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfab Hangul Syllable-BFAB | bfaa Hangul Syllable-BFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfad Hangul Syllable-BFAD | bfac Hangul Syllable-BFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfaf Hangul Syllable-BFAF | bfae Hangul Syllable-BFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb1 Hangul Syllable-BFB1 | bfb0 Hangul Syllable-BFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb3 Hangul Syllable-BFB3 | bfb2 Hangul Syllable-BFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb5 Hangul Syllable-BFB5 | bfb4 Hangul Syllable-BFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb7 Hangul Syllable-BFB7 | bfb6 Hangul Syllable-BFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfb9 Hangul Syllable-BFB9 | bfb8 Hangul Syllable-BFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbb Hangul Syllable-BFBB | bfba Hangul Syllable-BFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbd Hangul Syllable-BFBD | bfbc Hangul Syllable-BFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfbf Hangul Syllable-BFBF | bfbe Hangul Syllable-BFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc1 Hangul Syllable-BFC1 | bfc0 Hangul Syllable-BFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc3 Hangul Syllable-BFC3 | bfc2 Hangul Syllable-BFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc5 Hangul Syllable-BFC5 | bfc4 Hangul Syllable-BFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc7 Hangul Syllable-BFC7 | bfc6 Hangul Syllable-BFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfc9 Hangul Syllable-BFC9 | bfc8 Hangul Syllable-BFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcb Hangul Syllable-BFCB | bfca Hangul Syllable-BFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcd Hangul Syllable-BFCD | bfcc Hangul Syllable-BFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfcf Hangul Syllable-BFCF | bfce Hangul Syllable-BFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd1 Hangul Syllable-BFD1 | bfd0 Hangul Syllable-BFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd3 Hangul Syllable-BFD3 | bfd2 Hangul Syllable-BFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd5 Hangul Syllable-BFD5 | bfd4 Hangul Syllable-BFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd7 Hangul Syllable-BFD7 | bfd6 Hangul Syllable-BFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfd9 Hangul Syllable-BFD9 | bfd8 Hangul Syllable-BFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdb Hangul Syllable-BFDB | bfda Hangul Syllable-BFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdd Hangul Syllable-BFDD | bfdc Hangul Syllable-BFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfdf Hangul Syllable-BFDF | bfde Hangul Syllable-BFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe1 Hangul Syllable-BFE1 | bfe0 Hangul Syllable-BFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe3 Hangul Syllable-BFE3 | bfe2 Hangul Syllable-BFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe5 Hangul Syllable-BFE5 | bfe4 Hangul Syllable-BFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe7 Hangul Syllable-BFE7 | bfe6 Hangul Syllable-BFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfe9 Hangul Syllable-BFE9 | bfe8 Hangul Syllable-BFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfeb Hangul Syllable-BFEB | bfea Hangul Syllable-BFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfed Hangul Syllable-BFED | bfec Hangul Syllable-BFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfef Hangul Syllable-BFEF | bfee Hangul Syllable-BFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff1 Hangul Syllable-BFF1 | bff0 Hangul Syllable-BFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff3 Hangul Syllable-BFF3 | bff2 Hangul Syllable-BFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff5 Hangul Syllable-BFF5 | bff4 Hangul Syllable-BFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff7 Hangul Syllable-BFF7 | bff6 Hangul Syllable-BFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bff9 Hangul Syllable-BFF9 | bff8 Hangul Syllable-BFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bffb Hangul Syllable-BFFB | bffa Hangul Syllable-BFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bffd Hangul Syllable-BFFD | bffc Hangul Syllable-BFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* bfff Hangul Syllable-BFFF | bffe Hangul Syllable-BFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c001 Hangul Syllable-C001 | c000 Hangul Syllable-C000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c003 Hangul Syllable-C003 | c002 Hangul Syllable-C002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c005 Hangul Syllable-C005 | c004 Hangul Syllable-C004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c007 Hangul Syllable-C007 | c006 Hangul Syllable-C006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c009 Hangul Syllable-C009 | c008 Hangul Syllable-C008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00b Hangul Syllable-C00B | c00a Hangul Syllable-C00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00d Hangul Syllable-C00D | c00c Hangul Syllable-C00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c00f Hangul Syllable-C00F | c00e Hangul Syllable-C00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c011 Hangul Syllable-C011 | c010 Hangul Syllable-C010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c013 Hangul Syllable-C013 | c012 Hangul Syllable-C012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c015 Hangul Syllable-C015 | c014 Hangul Syllable-C014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c017 Hangul Syllable-C017 | c016 Hangul Syllable-C016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c019 Hangul Syllable-C019 | c018 Hangul Syllable-C018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01b Hangul Syllable-C01B | c01a Hangul Syllable-C01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01d Hangul Syllable-C01D | c01c Hangul Syllable-C01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c01f Hangul Syllable-C01F | c01e Hangul Syllable-C01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c021 Hangul Syllable-C021 | c020 Hangul Syllable-C020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c023 Hangul Syllable-C023 | c022 Hangul Syllable-C022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c025 Hangul Syllable-C025 | c024 Hangul Syllable-C024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c027 Hangul Syllable-C027 | c026 Hangul Syllable-C026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c029 Hangul Syllable-C029 | c028 Hangul Syllable-C028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02b Hangul Syllable-C02B | c02a Hangul Syllable-C02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02d Hangul Syllable-C02D | c02c Hangul Syllable-C02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c02f Hangul Syllable-C02F | c02e Hangul Syllable-C02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c031 Hangul Syllable-C031 | c030 Hangul Syllable-C030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c033 Hangul Syllable-C033 | c032 Hangul Syllable-C032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c035 Hangul Syllable-C035 | c034 Hangul Syllable-C034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c037 Hangul Syllable-C037 | c036 Hangul Syllable-C036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c039 Hangul Syllable-C039 | c038 Hangul Syllable-C038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03b Hangul Syllable-C03B | c03a Hangul Syllable-C03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03d Hangul Syllable-C03D | c03c Hangul Syllable-C03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c03f Hangul Syllable-C03F | c03e Hangul Syllable-C03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c041 Hangul Syllable-C041 | c040 Hangul Syllable-C040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c043 Hangul Syllable-C043 | c042 Hangul Syllable-C042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c045 Hangul Syllable-C045 | c044 Hangul Syllable-C044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c047 Hangul Syllable-C047 | c046 Hangul Syllable-C046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c049 Hangul Syllable-C049 | c048 Hangul Syllable-C048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04b Hangul Syllable-C04B | c04a Hangul Syllable-C04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04d Hangul Syllable-C04D | c04c Hangul Syllable-C04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c04f Hangul Syllable-C04F | c04e Hangul Syllable-C04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c051 Hangul Syllable-C051 | c050 Hangul Syllable-C050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c053 Hangul Syllable-C053 | c052 Hangul Syllable-C052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c055 Hangul Syllable-C055 | c054 Hangul Syllable-C054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c057 Hangul Syllable-C057 | c056 Hangul Syllable-C056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c059 Hangul Syllable-C059 | c058 Hangul Syllable-C058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05b Hangul Syllable-C05B | c05a Hangul Syllable-C05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05d Hangul Syllable-C05D | c05c Hangul Syllable-C05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c05f Hangul Syllable-C05F | c05e Hangul Syllable-C05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c061 Hangul Syllable-C061 | c060 Hangul Syllable-C060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c063 Hangul Syllable-C063 | c062 Hangul Syllable-C062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c065 Hangul Syllable-C065 | c064 Hangul Syllable-C064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c067 Hangul Syllable-C067 | c066 Hangul Syllable-C066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c069 Hangul Syllable-C069 | c068 Hangul Syllable-C068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06b Hangul Syllable-C06B | c06a Hangul Syllable-C06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06d Hangul Syllable-C06D | c06c Hangul Syllable-C06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c06f Hangul Syllable-C06F | c06e Hangul Syllable-C06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c071 Hangul Syllable-C071 | c070 Hangul Syllable-C070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c073 Hangul Syllable-C073 | c072 Hangul Syllable-C072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c075 Hangul Syllable-C075 | c074 Hangul Syllable-C074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c077 Hangul Syllable-C077 | c076 Hangul Syllable-C076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c079 Hangul Syllable-C079 | c078 Hangul Syllable-C078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07b Hangul Syllable-C07B | c07a Hangul Syllable-C07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07d Hangul Syllable-C07D | c07c Hangul Syllable-C07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c07f Hangul Syllable-C07F | c07e Hangul Syllable-C07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c081 Hangul Syllable-C081 | c080 Hangul Syllable-C080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c083 Hangul Syllable-C083 | c082 Hangul Syllable-C082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c085 Hangul Syllable-C085 | c084 Hangul Syllable-C084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c087 Hangul Syllable-C087 | c086 Hangul Syllable-C086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c089 Hangul Syllable-C089 | c088 Hangul Syllable-C088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08b Hangul Syllable-C08B | c08a Hangul Syllable-C08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08d Hangul Syllable-C08D | c08c Hangul Syllable-C08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c08f Hangul Syllable-C08F | c08e Hangul Syllable-C08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c091 Hangul Syllable-C091 | c090 Hangul Syllable-C090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c093 Hangul Syllable-C093 | c092 Hangul Syllable-C092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c095 Hangul Syllable-C095 | c094 Hangul Syllable-C094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c097 Hangul Syllable-C097 | c096 Hangul Syllable-C096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c099 Hangul Syllable-C099 | c098 Hangul Syllable-C098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09b Hangul Syllable-C09B | c09a Hangul Syllable-C09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09d Hangul Syllable-C09D | c09c Hangul Syllable-C09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c09f Hangul Syllable-C09F | c09e Hangul Syllable-C09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a1 Hangul Syllable-C0A1 | c0a0 Hangul Syllable-C0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a3 Hangul Syllable-C0A3 | c0a2 Hangul Syllable-C0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a5 Hangul Syllable-C0A5 | c0a4 Hangul Syllable-C0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a7 Hangul Syllable-C0A7 | c0a6 Hangul Syllable-C0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0a9 Hangul Syllable-C0A9 | c0a8 Hangul Syllable-C0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ab Hangul Syllable-C0AB | c0aa Hangul Syllable-C0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ad Hangul Syllable-C0AD | c0ac Hangul Syllable-C0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0af Hangul Syllable-C0AF | c0ae Hangul Syllable-C0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b1 Hangul Syllable-C0B1 | c0b0 Hangul Syllable-C0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b3 Hangul Syllable-C0B3 | c0b2 Hangul Syllable-C0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b5 Hangul Syllable-C0B5 | c0b4 Hangul Syllable-C0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b7 Hangul Syllable-C0B7 | c0b6 Hangul Syllable-C0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0b9 Hangul Syllable-C0B9 | c0b8 Hangul Syllable-C0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bb Hangul Syllable-C0BB | c0ba Hangul Syllable-C0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bd Hangul Syllable-C0BD | c0bc Hangul Syllable-C0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0bf Hangul Syllable-C0BF | c0be Hangul Syllable-C0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c1 Hangul Syllable-C0C1 | c0c0 Hangul Syllable-C0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c3 Hangul Syllable-C0C3 | c0c2 Hangul Syllable-C0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c5 Hangul Syllable-C0C5 | c0c4 Hangul Syllable-C0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c7 Hangul Syllable-C0C7 | c0c6 Hangul Syllable-C0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0c9 Hangul Syllable-C0C9 | c0c8 Hangul Syllable-C0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cb Hangul Syllable-C0CB | c0ca Hangul Syllable-C0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cd Hangul Syllable-C0CD | c0cc Hangul Syllable-C0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0cf Hangul Syllable-C0CF | c0ce Hangul Syllable-C0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d1 Hangul Syllable-C0D1 | c0d0 Hangul Syllable-C0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d3 Hangul Syllable-C0D3 | c0d2 Hangul Syllable-C0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d5 Hangul Syllable-C0D5 | c0d4 Hangul Syllable-C0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d7 Hangul Syllable-C0D7 | c0d6 Hangul Syllable-C0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0d9 Hangul Syllable-C0D9 | c0d8 Hangul Syllable-C0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0db Hangul Syllable-C0DB | c0da Hangul Syllable-C0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0dd Hangul Syllable-C0DD | c0dc Hangul Syllable-C0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0df Hangul Syllable-C0DF | c0de Hangul Syllable-C0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e1 Hangul Syllable-C0E1 | c0e0 Hangul Syllable-C0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e3 Hangul Syllable-C0E3 | c0e2 Hangul Syllable-C0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e5 Hangul Syllable-C0E5 | c0e4 Hangul Syllable-C0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e7 Hangul Syllable-C0E7 | c0e6 Hangul Syllable-C0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0e9 Hangul Syllable-C0E9 | c0e8 Hangul Syllable-C0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0eb Hangul Syllable-C0EB | c0ea Hangul Syllable-C0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ed Hangul Syllable-C0ED | c0ec Hangul Syllable-C0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ef Hangul Syllable-C0EF | c0ee Hangul Syllable-C0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f1 Hangul Syllable-C0F1 | c0f0 Hangul Syllable-C0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f3 Hangul Syllable-C0F3 | c0f2 Hangul Syllable-C0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f5 Hangul Syllable-C0F5 | c0f4 Hangul Syllable-C0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f7 Hangul Syllable-C0F7 | c0f6 Hangul Syllable-C0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0f9 Hangul Syllable-C0F9 | c0f8 Hangul Syllable-C0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0fb Hangul Syllable-C0FB | c0fa Hangul Syllable-C0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0fd Hangul Syllable-C0FD | c0fc Hangul Syllable-C0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c0ff Hangul Syllable-C0FF | c0fe Hangul Syllable-C0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c101 Hangul Syllable-C101 | c100 Hangul Syllable-C100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c103 Hangul Syllable-C103 | c102 Hangul Syllable-C102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c105 Hangul Syllable-C105 | c104 Hangul Syllable-C104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c107 Hangul Syllable-C107 | c106 Hangul Syllable-C106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c109 Hangul Syllable-C109 | c108 Hangul Syllable-C108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10b Hangul Syllable-C10B | c10a Hangul Syllable-C10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10d Hangul Syllable-C10D | c10c Hangul Syllable-C10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c10f Hangul Syllable-C10F | c10e Hangul Syllable-C10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c111 Hangul Syllable-C111 | c110 Hangul Syllable-C110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c113 Hangul Syllable-C113 | c112 Hangul Syllable-C112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c115 Hangul Syllable-C115 | c114 Hangul Syllable-C114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c117 Hangul Syllable-C117 | c116 Hangul Syllable-C116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c119 Hangul Syllable-C119 | c118 Hangul Syllable-C118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11b Hangul Syllable-C11B | c11a Hangul Syllable-C11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11d Hangul Syllable-C11D | c11c Hangul Syllable-C11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c11f Hangul Syllable-C11F | c11e Hangul Syllable-C11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c121 Hangul Syllable-C121 | c120 Hangul Syllable-C120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c123 Hangul Syllable-C123 | c122 Hangul Syllable-C122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c125 Hangul Syllable-C125 | c124 Hangul Syllable-C124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c127 Hangul Syllable-C127 | c126 Hangul Syllable-C126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c129 Hangul Syllable-C129 | c128 Hangul Syllable-C128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12b Hangul Syllable-C12B | c12a Hangul Syllable-C12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12d Hangul Syllable-C12D | c12c Hangul Syllable-C12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c12f Hangul Syllable-C12F | c12e Hangul Syllable-C12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c131 Hangul Syllable-C131 | c130 Hangul Syllable-C130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c133 Hangul Syllable-C133 | c132 Hangul Syllable-C132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c135 Hangul Syllable-C135 | c134 Hangul Syllable-C134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c137 Hangul Syllable-C137 | c136 Hangul Syllable-C136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c139 Hangul Syllable-C139 | c138 Hangul Syllable-C138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13b Hangul Syllable-C13B | c13a Hangul Syllable-C13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13d Hangul Syllable-C13D | c13c Hangul Syllable-C13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c13f Hangul Syllable-C13F | c13e Hangul Syllable-C13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c141 Hangul Syllable-C141 | c140 Hangul Syllable-C140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c143 Hangul Syllable-C143 | c142 Hangul Syllable-C142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c145 Hangul Syllable-C145 | c144 Hangul Syllable-C144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c147 Hangul Syllable-C147 | c146 Hangul Syllable-C146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c149 Hangul Syllable-C149 | c148 Hangul Syllable-C148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14b Hangul Syllable-C14B | c14a Hangul Syllable-C14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14d Hangul Syllable-C14D | c14c Hangul Syllable-C14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c14f Hangul Syllable-C14F | c14e Hangul Syllable-C14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c151 Hangul Syllable-C151 | c150 Hangul Syllable-C150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c153 Hangul Syllable-C153 | c152 Hangul Syllable-C152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c155 Hangul Syllable-C155 | c154 Hangul Syllable-C154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c157 Hangul Syllable-C157 | c156 Hangul Syllable-C156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c159 Hangul Syllable-C159 | c158 Hangul Syllable-C158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15b Hangul Syllable-C15B | c15a Hangul Syllable-C15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15d Hangul Syllable-C15D | c15c Hangul Syllable-C15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c15f Hangul Syllable-C15F | c15e Hangul Syllable-C15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c161 Hangul Syllable-C161 | c160 Hangul Syllable-C160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c163 Hangul Syllable-C163 | c162 Hangul Syllable-C162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c165 Hangul Syllable-C165 | c164 Hangul Syllable-C164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c167 Hangul Syllable-C167 | c166 Hangul Syllable-C166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c169 Hangul Syllable-C169 | c168 Hangul Syllable-C168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16b Hangul Syllable-C16B | c16a Hangul Syllable-C16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16d Hangul Syllable-C16D | c16c Hangul Syllable-C16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c16f Hangul Syllable-C16F | c16e Hangul Syllable-C16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c171 Hangul Syllable-C171 | c170 Hangul Syllable-C170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c173 Hangul Syllable-C173 | c172 Hangul Syllable-C172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c175 Hangul Syllable-C175 | c174 Hangul Syllable-C174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c177 Hangul Syllable-C177 | c176 Hangul Syllable-C176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c179 Hangul Syllable-C179 | c178 Hangul Syllable-C178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17b Hangul Syllable-C17B | c17a Hangul Syllable-C17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17d Hangul Syllable-C17D | c17c Hangul Syllable-C17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c17f Hangul Syllable-C17F | c17e Hangul Syllable-C17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c181 Hangul Syllable-C181 | c180 Hangul Syllable-C180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c183 Hangul Syllable-C183 | c182 Hangul Syllable-C182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c185 Hangul Syllable-C185 | c184 Hangul Syllable-C184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c187 Hangul Syllable-C187 | c186 Hangul Syllable-C186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c189 Hangul Syllable-C189 | c188 Hangul Syllable-C188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18b Hangul Syllable-C18B | c18a Hangul Syllable-C18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18d Hangul Syllable-C18D | c18c Hangul Syllable-C18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c18f Hangul Syllable-C18F | c18e Hangul Syllable-C18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c191 Hangul Syllable-C191 | c190 Hangul Syllable-C190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c193 Hangul Syllable-C193 | c192 Hangul Syllable-C192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c195 Hangul Syllable-C195 | c194 Hangul Syllable-C194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c197 Hangul Syllable-C197 | c196 Hangul Syllable-C196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c199 Hangul Syllable-C199 | c198 Hangul Syllable-C198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19b Hangul Syllable-C19B | c19a Hangul Syllable-C19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19d Hangul Syllable-C19D | c19c Hangul Syllable-C19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c19f Hangul Syllable-C19F | c19e Hangul Syllable-C19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a1 Hangul Syllable-C1A1 | c1a0 Hangul Syllable-C1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a3 Hangul Syllable-C1A3 | c1a2 Hangul Syllable-C1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a5 Hangul Syllable-C1A5 | c1a4 Hangul Syllable-C1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a7 Hangul Syllable-C1A7 | c1a6 Hangul Syllable-C1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1a9 Hangul Syllable-C1A9 | c1a8 Hangul Syllable-C1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ab Hangul Syllable-C1AB | c1aa Hangul Syllable-C1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ad Hangul Syllable-C1AD | c1ac Hangul Syllable-C1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1af Hangul Syllable-C1AF | c1ae Hangul Syllable-C1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b1 Hangul Syllable-C1B1 | c1b0 Hangul Syllable-C1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b3 Hangul Syllable-C1B3 | c1b2 Hangul Syllable-C1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b5 Hangul Syllable-C1B5 | c1b4 Hangul Syllable-C1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b7 Hangul Syllable-C1B7 | c1b6 Hangul Syllable-C1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1b9 Hangul Syllable-C1B9 | c1b8 Hangul Syllable-C1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bb Hangul Syllable-C1BB | c1ba Hangul Syllable-C1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bd Hangul Syllable-C1BD | c1bc Hangul Syllable-C1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1bf Hangul Syllable-C1BF | c1be Hangul Syllable-C1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c1 Hangul Syllable-C1C1 | c1c0 Hangul Syllable-C1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c3 Hangul Syllable-C1C3 | c1c2 Hangul Syllable-C1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c5 Hangul Syllable-C1C5 | c1c4 Hangul Syllable-C1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c7 Hangul Syllable-C1C7 | c1c6 Hangul Syllable-C1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1c9 Hangul Syllable-C1C9 | c1c8 Hangul Syllable-C1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cb Hangul Syllable-C1CB | c1ca Hangul Syllable-C1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cd Hangul Syllable-C1CD | c1cc Hangul Syllable-C1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1cf Hangul Syllable-C1CF | c1ce Hangul Syllable-C1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d1 Hangul Syllable-C1D1 | c1d0 Hangul Syllable-C1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d3 Hangul Syllable-C1D3 | c1d2 Hangul Syllable-C1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d5 Hangul Syllable-C1D5 | c1d4 Hangul Syllable-C1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d7 Hangul Syllable-C1D7 | c1d6 Hangul Syllable-C1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1d9 Hangul Syllable-C1D9 | c1d8 Hangul Syllable-C1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1db Hangul Syllable-C1DB | c1da Hangul Syllable-C1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1dd Hangul Syllable-C1DD | c1dc Hangul Syllable-C1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1df Hangul Syllable-C1DF | c1de Hangul Syllable-C1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e1 Hangul Syllable-C1E1 | c1e0 Hangul Syllable-C1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e3 Hangul Syllable-C1E3 | c1e2 Hangul Syllable-C1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e5 Hangul Syllable-C1E5 | c1e4 Hangul Syllable-C1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e7 Hangul Syllable-C1E7 | c1e6 Hangul Syllable-C1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1e9 Hangul Syllable-C1E9 | c1e8 Hangul Syllable-C1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1eb Hangul Syllable-C1EB | c1ea Hangul Syllable-C1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ed Hangul Syllable-C1ED | c1ec Hangul Syllable-C1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ef Hangul Syllable-C1EF | c1ee Hangul Syllable-C1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f1 Hangul Syllable-C1F1 | c1f0 Hangul Syllable-C1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f3 Hangul Syllable-C1F3 | c1f2 Hangul Syllable-C1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f5 Hangul Syllable-C1F5 | c1f4 Hangul Syllable-C1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f7 Hangul Syllable-C1F7 | c1f6 Hangul Syllable-C1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1f9 Hangul Syllable-C1F9 | c1f8 Hangul Syllable-C1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1fb Hangul Syllable-C1FB | c1fa Hangul Syllable-C1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1fd Hangul Syllable-C1FD | c1fc Hangul Syllable-C1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c1ff Hangul Syllable-C1FF | c1fe Hangul Syllable-C1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c201 Hangul Syllable-C201 | c200 Hangul Syllable-C200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c203 Hangul Syllable-C203 | c202 Hangul Syllable-C202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c205 Hangul Syllable-C205 | c204 Hangul Syllable-C204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c207 Hangul Syllable-C207 | c206 Hangul Syllable-C206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c209 Hangul Syllable-C209 | c208 Hangul Syllable-C208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20b Hangul Syllable-C20B | c20a Hangul Syllable-C20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20d Hangul Syllable-C20D | c20c Hangul Syllable-C20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c20f Hangul Syllable-C20F | c20e Hangul Syllable-C20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c211 Hangul Syllable-C211 | c210 Hangul Syllable-C210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c213 Hangul Syllable-C213 | c212 Hangul Syllable-C212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c215 Hangul Syllable-C215 | c214 Hangul Syllable-C214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c217 Hangul Syllable-C217 | c216 Hangul Syllable-C216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c219 Hangul Syllable-C219 | c218 Hangul Syllable-C218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21b Hangul Syllable-C21B | c21a Hangul Syllable-C21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21d Hangul Syllable-C21D | c21c Hangul Syllable-C21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c21f Hangul Syllable-C21F | c21e Hangul Syllable-C21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c221 Hangul Syllable-C221 | c220 Hangul Syllable-C220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c223 Hangul Syllable-C223 | c222 Hangul Syllable-C222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c225 Hangul Syllable-C225 | c224 Hangul Syllable-C224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c227 Hangul Syllable-C227 | c226 Hangul Syllable-C226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c229 Hangul Syllable-C229 | c228 Hangul Syllable-C228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22b Hangul Syllable-C22B | c22a Hangul Syllable-C22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22d Hangul Syllable-C22D | c22c Hangul Syllable-C22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c22f Hangul Syllable-C22F | c22e Hangul Syllable-C22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c231 Hangul Syllable-C231 | c230 Hangul Syllable-C230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c233 Hangul Syllable-C233 | c232 Hangul Syllable-C232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c235 Hangul Syllable-C235 | c234 Hangul Syllable-C234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c237 Hangul Syllable-C237 | c236 Hangul Syllable-C236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c239 Hangul Syllable-C239 | c238 Hangul Syllable-C238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23b Hangul Syllable-C23B | c23a Hangul Syllable-C23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23d Hangul Syllable-C23D | c23c Hangul Syllable-C23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c23f Hangul Syllable-C23F | c23e Hangul Syllable-C23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c241 Hangul Syllable-C241 | c240 Hangul Syllable-C240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c243 Hangul Syllable-C243 | c242 Hangul Syllable-C242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c245 Hangul Syllable-C245 | c244 Hangul Syllable-C244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c247 Hangul Syllable-C247 | c246 Hangul Syllable-C246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c249 Hangul Syllable-C249 | c248 Hangul Syllable-C248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24b Hangul Syllable-C24B | c24a Hangul Syllable-C24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24d Hangul Syllable-C24D | c24c Hangul Syllable-C24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c24f Hangul Syllable-C24F | c24e Hangul Syllable-C24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c251 Hangul Syllable-C251 | c250 Hangul Syllable-C250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c253 Hangul Syllable-C253 | c252 Hangul Syllable-C252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c255 Hangul Syllable-C255 | c254 Hangul Syllable-C254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c257 Hangul Syllable-C257 | c256 Hangul Syllable-C256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c259 Hangul Syllable-C259 | c258 Hangul Syllable-C258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25b Hangul Syllable-C25B | c25a Hangul Syllable-C25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25d Hangul Syllable-C25D | c25c Hangul Syllable-C25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c25f Hangul Syllable-C25F | c25e Hangul Syllable-C25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c261 Hangul Syllable-C261 | c260 Hangul Syllable-C260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c263 Hangul Syllable-C263 | c262 Hangul Syllable-C262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c265 Hangul Syllable-C265 | c264 Hangul Syllable-C264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c267 Hangul Syllable-C267 | c266 Hangul Syllable-C266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c269 Hangul Syllable-C269 | c268 Hangul Syllable-C268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26b Hangul Syllable-C26B | c26a Hangul Syllable-C26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26d Hangul Syllable-C26D | c26c Hangul Syllable-C26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c26f Hangul Syllable-C26F | c26e Hangul Syllable-C26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c271 Hangul Syllable-C271 | c270 Hangul Syllable-C270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c273 Hangul Syllable-C273 | c272 Hangul Syllable-C272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c275 Hangul Syllable-C275 | c274 Hangul Syllable-C274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c277 Hangul Syllable-C277 | c276 Hangul Syllable-C276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c279 Hangul Syllable-C279 | c278 Hangul Syllable-C278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27b Hangul Syllable-C27B | c27a Hangul Syllable-C27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27d Hangul Syllable-C27D | c27c Hangul Syllable-C27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c27f Hangul Syllable-C27F | c27e Hangul Syllable-C27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c281 Hangul Syllable-C281 | c280 Hangul Syllable-C280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c283 Hangul Syllable-C283 | c282 Hangul Syllable-C282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c285 Hangul Syllable-C285 | c284 Hangul Syllable-C284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c287 Hangul Syllable-C287 | c286 Hangul Syllable-C286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c289 Hangul Syllable-C289 | c288 Hangul Syllable-C288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28b Hangul Syllable-C28B | c28a Hangul Syllable-C28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28d Hangul Syllable-C28D | c28c Hangul Syllable-C28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c28f Hangul Syllable-C28F | c28e Hangul Syllable-C28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c291 Hangul Syllable-C291 | c290 Hangul Syllable-C290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c293 Hangul Syllable-C293 | c292 Hangul Syllable-C292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c295 Hangul Syllable-C295 | c294 Hangul Syllable-C294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c297 Hangul Syllable-C297 | c296 Hangul Syllable-C296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c299 Hangul Syllable-C299 | c298 Hangul Syllable-C298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29b Hangul Syllable-C29B | c29a Hangul Syllable-C29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29d Hangul Syllable-C29D | c29c Hangul Syllable-C29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c29f Hangul Syllable-C29F | c29e Hangul Syllable-C29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a1 Hangul Syllable-C2A1 | c2a0 Hangul Syllable-C2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a3 Hangul Syllable-C2A3 | c2a2 Hangul Syllable-C2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a5 Hangul Syllable-C2A5 | c2a4 Hangul Syllable-C2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a7 Hangul Syllable-C2A7 | c2a6 Hangul Syllable-C2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2a9 Hangul Syllable-C2A9 | c2a8 Hangul Syllable-C2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ab Hangul Syllable-C2AB | c2aa Hangul Syllable-C2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ad Hangul Syllable-C2AD | c2ac Hangul Syllable-C2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2af Hangul Syllable-C2AF | c2ae Hangul Syllable-C2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b1 Hangul Syllable-C2B1 | c2b0 Hangul Syllable-C2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b3 Hangul Syllable-C2B3 | c2b2 Hangul Syllable-C2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b5 Hangul Syllable-C2B5 | c2b4 Hangul Syllable-C2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b7 Hangul Syllable-C2B7 | c2b6 Hangul Syllable-C2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2b9 Hangul Syllable-C2B9 | c2b8 Hangul Syllable-C2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bb Hangul Syllable-C2BB | c2ba Hangul Syllable-C2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bd Hangul Syllable-C2BD | c2bc Hangul Syllable-C2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2bf Hangul Syllable-C2BF | c2be Hangul Syllable-C2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c1 Hangul Syllable-C2C1 | c2c0 Hangul Syllable-C2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c3 Hangul Syllable-C2C3 | c2c2 Hangul Syllable-C2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c5 Hangul Syllable-C2C5 | c2c4 Hangul Syllable-C2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c7 Hangul Syllable-C2C7 | c2c6 Hangul Syllable-C2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2c9 Hangul Syllable-C2C9 | c2c8 Hangul Syllable-C2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cb Hangul Syllable-C2CB | c2ca Hangul Syllable-C2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cd Hangul Syllable-C2CD | c2cc Hangul Syllable-C2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2cf Hangul Syllable-C2CF | c2ce Hangul Syllable-C2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d1 Hangul Syllable-C2D1 | c2d0 Hangul Syllable-C2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d3 Hangul Syllable-C2D3 | c2d2 Hangul Syllable-C2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d5 Hangul Syllable-C2D5 | c2d4 Hangul Syllable-C2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d7 Hangul Syllable-C2D7 | c2d6 Hangul Syllable-C2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2d9 Hangul Syllable-C2D9 | c2d8 Hangul Syllable-C2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2db Hangul Syllable-C2DB | c2da Hangul Syllable-C2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2dd Hangul Syllable-C2DD | c2dc Hangul Syllable-C2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2df Hangul Syllable-C2DF | c2de Hangul Syllable-C2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e1 Hangul Syllable-C2E1 | c2e0 Hangul Syllable-C2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e3 Hangul Syllable-C2E3 | c2e2 Hangul Syllable-C2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e5 Hangul Syllable-C2E5 | c2e4 Hangul Syllable-C2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e7 Hangul Syllable-C2E7 | c2e6 Hangul Syllable-C2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2e9 Hangul Syllable-C2E9 | c2e8 Hangul Syllable-C2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2eb Hangul Syllable-C2EB | c2ea Hangul Syllable-C2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ed Hangul Syllable-C2ED | c2ec Hangul Syllable-C2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ef Hangul Syllable-C2EF | c2ee Hangul Syllable-C2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f1 Hangul Syllable-C2F1 | c2f0 Hangul Syllable-C2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f3 Hangul Syllable-C2F3 | c2f2 Hangul Syllable-C2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f5 Hangul Syllable-C2F5 | c2f4 Hangul Syllable-C2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f7 Hangul Syllable-C2F7 | c2f6 Hangul Syllable-C2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2f9 Hangul Syllable-C2F9 | c2f8 Hangul Syllable-C2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2fb Hangul Syllable-C2FB | c2fa Hangul Syllable-C2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2fd Hangul Syllable-C2FD | c2fc Hangul Syllable-C2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c2ff Hangul Syllable-C2FF | c2fe Hangul Syllable-C2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c301 Hangul Syllable-C301 | c300 Hangul Syllable-C300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c303 Hangul Syllable-C303 | c302 Hangul Syllable-C302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c305 Hangul Syllable-C305 | c304 Hangul Syllable-C304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c307 Hangul Syllable-C307 | c306 Hangul Syllable-C306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c309 Hangul Syllable-C309 | c308 Hangul Syllable-C308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30b Hangul Syllable-C30B | c30a Hangul Syllable-C30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30d Hangul Syllable-C30D | c30c Hangul Syllable-C30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c30f Hangul Syllable-C30F | c30e Hangul Syllable-C30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c311 Hangul Syllable-C311 | c310 Hangul Syllable-C310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c313 Hangul Syllable-C313 | c312 Hangul Syllable-C312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c315 Hangul Syllable-C315 | c314 Hangul Syllable-C314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c317 Hangul Syllable-C317 | c316 Hangul Syllable-C316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c319 Hangul Syllable-C319 | c318 Hangul Syllable-C318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31b Hangul Syllable-C31B | c31a Hangul Syllable-C31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31d Hangul Syllable-C31D | c31c Hangul Syllable-C31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c31f Hangul Syllable-C31F | c31e Hangul Syllable-C31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c321 Hangul Syllable-C321 | c320 Hangul Syllable-C320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c323 Hangul Syllable-C323 | c322 Hangul Syllable-C322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c325 Hangul Syllable-C325 | c324 Hangul Syllable-C324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c327 Hangul Syllable-C327 | c326 Hangul Syllable-C326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c329 Hangul Syllable-C329 | c328 Hangul Syllable-C328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32b Hangul Syllable-C32B | c32a Hangul Syllable-C32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32d Hangul Syllable-C32D | c32c Hangul Syllable-C32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c32f Hangul Syllable-C32F | c32e Hangul Syllable-C32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c331 Hangul Syllable-C331 | c330 Hangul Syllable-C330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c333 Hangul Syllable-C333 | c332 Hangul Syllable-C332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c335 Hangul Syllable-C335 | c334 Hangul Syllable-C334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c337 Hangul Syllable-C337 | c336 Hangul Syllable-C336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c339 Hangul Syllable-C339 | c338 Hangul Syllable-C338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33b Hangul Syllable-C33B | c33a Hangul Syllable-C33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33d Hangul Syllable-C33D | c33c Hangul Syllable-C33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c33f Hangul Syllable-C33F | c33e Hangul Syllable-C33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c341 Hangul Syllable-C341 | c340 Hangul Syllable-C340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c343 Hangul Syllable-C343 | c342 Hangul Syllable-C342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c345 Hangul Syllable-C345 | c344 Hangul Syllable-C344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c347 Hangul Syllable-C347 | c346 Hangul Syllable-C346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c349 Hangul Syllable-C349 | c348 Hangul Syllable-C348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34b Hangul Syllable-C34B | c34a Hangul Syllable-C34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34d Hangul Syllable-C34D | c34c Hangul Syllable-C34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c34f Hangul Syllable-C34F | c34e Hangul Syllable-C34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c351 Hangul Syllable-C351 | c350 Hangul Syllable-C350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c353 Hangul Syllable-C353 | c352 Hangul Syllable-C352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c355 Hangul Syllable-C355 | c354 Hangul Syllable-C354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c357 Hangul Syllable-C357 | c356 Hangul Syllable-C356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c359 Hangul Syllable-C359 | c358 Hangul Syllable-C358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35b Hangul Syllable-C35B | c35a Hangul Syllable-C35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35d Hangul Syllable-C35D | c35c Hangul Syllable-C35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c35f Hangul Syllable-C35F | c35e Hangul Syllable-C35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c361 Hangul Syllable-C361 | c360 Hangul Syllable-C360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c363 Hangul Syllable-C363 | c362 Hangul Syllable-C362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c365 Hangul Syllable-C365 | c364 Hangul Syllable-C364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c367 Hangul Syllable-C367 | c366 Hangul Syllable-C366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c369 Hangul Syllable-C369 | c368 Hangul Syllable-C368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36b Hangul Syllable-C36B | c36a Hangul Syllable-C36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36d Hangul Syllable-C36D | c36c Hangul Syllable-C36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c36f Hangul Syllable-C36F | c36e Hangul Syllable-C36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c371 Hangul Syllable-C371 | c370 Hangul Syllable-C370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c373 Hangul Syllable-C373 | c372 Hangul Syllable-C372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c375 Hangul Syllable-C375 | c374 Hangul Syllable-C374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c377 Hangul Syllable-C377 | c376 Hangul Syllable-C376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c379 Hangul Syllable-C379 | c378 Hangul Syllable-C378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37b Hangul Syllable-C37B | c37a Hangul Syllable-C37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37d Hangul Syllable-C37D | c37c Hangul Syllable-C37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c37f Hangul Syllable-C37F | c37e Hangul Syllable-C37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c381 Hangul Syllable-C381 | c380 Hangul Syllable-C380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c383 Hangul Syllable-C383 | c382 Hangul Syllable-C382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c385 Hangul Syllable-C385 | c384 Hangul Syllable-C384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c387 Hangul Syllable-C387 | c386 Hangul Syllable-C386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c389 Hangul Syllable-C389 | c388 Hangul Syllable-C388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38b Hangul Syllable-C38B | c38a Hangul Syllable-C38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38d Hangul Syllable-C38D | c38c Hangul Syllable-C38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c38f Hangul Syllable-C38F | c38e Hangul Syllable-C38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c391 Hangul Syllable-C391 | c390 Hangul Syllable-C390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c393 Hangul Syllable-C393 | c392 Hangul Syllable-C392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c395 Hangul Syllable-C395 | c394 Hangul Syllable-C394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c397 Hangul Syllable-C397 | c396 Hangul Syllable-C396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c399 Hangul Syllable-C399 | c398 Hangul Syllable-C398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39b Hangul Syllable-C39B | c39a Hangul Syllable-C39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39d Hangul Syllable-C39D | c39c Hangul Syllable-C39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c39f Hangul Syllable-C39F | c39e Hangul Syllable-C39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a1 Hangul Syllable-C3A1 | c3a0 Hangul Syllable-C3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a3 Hangul Syllable-C3A3 | c3a2 Hangul Syllable-C3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a5 Hangul Syllable-C3A5 | c3a4 Hangul Syllable-C3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a7 Hangul Syllable-C3A7 | c3a6 Hangul Syllable-C3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3a9 Hangul Syllable-C3A9 | c3a8 Hangul Syllable-C3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ab Hangul Syllable-C3AB | c3aa Hangul Syllable-C3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ad Hangul Syllable-C3AD | c3ac Hangul Syllable-C3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3af Hangul Syllable-C3AF | c3ae Hangul Syllable-C3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b1 Hangul Syllable-C3B1 | c3b0 Hangul Syllable-C3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b3 Hangul Syllable-C3B3 | c3b2 Hangul Syllable-C3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b5 Hangul Syllable-C3B5 | c3b4 Hangul Syllable-C3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b7 Hangul Syllable-C3B7 | c3b6 Hangul Syllable-C3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3b9 Hangul Syllable-C3B9 | c3b8 Hangul Syllable-C3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bb Hangul Syllable-C3BB | c3ba Hangul Syllable-C3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bd Hangul Syllable-C3BD | c3bc Hangul Syllable-C3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3bf Hangul Syllable-C3BF | c3be Hangul Syllable-C3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c1 Hangul Syllable-C3C1 | c3c0 Hangul Syllable-C3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c3 Hangul Syllable-C3C3 | c3c2 Hangul Syllable-C3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c5 Hangul Syllable-C3C5 | c3c4 Hangul Syllable-C3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c7 Hangul Syllable-C3C7 | c3c6 Hangul Syllable-C3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3c9 Hangul Syllable-C3C9 | c3c8 Hangul Syllable-C3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cb Hangul Syllable-C3CB | c3ca Hangul Syllable-C3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cd Hangul Syllable-C3CD | c3cc Hangul Syllable-C3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3cf Hangul Syllable-C3CF | c3ce Hangul Syllable-C3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d1 Hangul Syllable-C3D1 | c3d0 Hangul Syllable-C3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d3 Hangul Syllable-C3D3 | c3d2 Hangul Syllable-C3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d5 Hangul Syllable-C3D5 | c3d4 Hangul Syllable-C3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d7 Hangul Syllable-C3D7 | c3d6 Hangul Syllable-C3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3d9 Hangul Syllable-C3D9 | c3d8 Hangul Syllable-C3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3db Hangul Syllable-C3DB | c3da Hangul Syllable-C3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3dd Hangul Syllable-C3DD | c3dc Hangul Syllable-C3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3df Hangul Syllable-C3DF | c3de Hangul Syllable-C3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e1 Hangul Syllable-C3E1 | c3e0 Hangul Syllable-C3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e3 Hangul Syllable-C3E3 | c3e2 Hangul Syllable-C3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e5 Hangul Syllable-C3E5 | c3e4 Hangul Syllable-C3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e7 Hangul Syllable-C3E7 | c3e6 Hangul Syllable-C3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3e9 Hangul Syllable-C3E9 | c3e8 Hangul Syllable-C3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3eb Hangul Syllable-C3EB | c3ea Hangul Syllable-C3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ed Hangul Syllable-C3ED | c3ec Hangul Syllable-C3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ef Hangul Syllable-C3EF | c3ee Hangul Syllable-C3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f1 Hangul Syllable-C3F1 | c3f0 Hangul Syllable-C3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f3 Hangul Syllable-C3F3 | c3f2 Hangul Syllable-C3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f5 Hangul Syllable-C3F5 | c3f4 Hangul Syllable-C3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f7 Hangul Syllable-C3F7 | c3f6 Hangul Syllable-C3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3f9 Hangul Syllable-C3F9 | c3f8 Hangul Syllable-C3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3fb Hangul Syllable-C3FB | c3fa Hangul Syllable-C3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3fd Hangul Syllable-C3FD | c3fc Hangul Syllable-C3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c3ff Hangul Syllable-C3FF | c3fe Hangul Syllable-C3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c401 Hangul Syllable-C401 | c400 Hangul Syllable-C400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c403 Hangul Syllable-C403 | c402 Hangul Syllable-C402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c405 Hangul Syllable-C405 | c404 Hangul Syllable-C404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c407 Hangul Syllable-C407 | c406 Hangul Syllable-C406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c409 Hangul Syllable-C409 | c408 Hangul Syllable-C408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40b Hangul Syllable-C40B | c40a Hangul Syllable-C40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40d Hangul Syllable-C40D | c40c Hangul Syllable-C40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c40f Hangul Syllable-C40F | c40e Hangul Syllable-C40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c411 Hangul Syllable-C411 | c410 Hangul Syllable-C410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c413 Hangul Syllable-C413 | c412 Hangul Syllable-C412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c415 Hangul Syllable-C415 | c414 Hangul Syllable-C414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c417 Hangul Syllable-C417 | c416 Hangul Syllable-C416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c419 Hangul Syllable-C419 | c418 Hangul Syllable-C418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41b Hangul Syllable-C41B | c41a Hangul Syllable-C41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41d Hangul Syllable-C41D | c41c Hangul Syllable-C41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c41f Hangul Syllable-C41F | c41e Hangul Syllable-C41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c421 Hangul Syllable-C421 | c420 Hangul Syllable-C420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c423 Hangul Syllable-C423 | c422 Hangul Syllable-C422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c425 Hangul Syllable-C425 | c424 Hangul Syllable-C424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c427 Hangul Syllable-C427 | c426 Hangul Syllable-C426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c429 Hangul Syllable-C429 | c428 Hangul Syllable-C428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42b Hangul Syllable-C42B | c42a Hangul Syllable-C42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42d Hangul Syllable-C42D | c42c Hangul Syllable-C42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c42f Hangul Syllable-C42F | c42e Hangul Syllable-C42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c431 Hangul Syllable-C431 | c430 Hangul Syllable-C430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c433 Hangul Syllable-C433 | c432 Hangul Syllable-C432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c435 Hangul Syllable-C435 | c434 Hangul Syllable-C434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c437 Hangul Syllable-C437 | c436 Hangul Syllable-C436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c439 Hangul Syllable-C439 | c438 Hangul Syllable-C438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43b Hangul Syllable-C43B | c43a Hangul Syllable-C43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43d Hangul Syllable-C43D | c43c Hangul Syllable-C43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c43f Hangul Syllable-C43F | c43e Hangul Syllable-C43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c441 Hangul Syllable-C441 | c440 Hangul Syllable-C440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c443 Hangul Syllable-C443 | c442 Hangul Syllable-C442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c445 Hangul Syllable-C445 | c444 Hangul Syllable-C444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c447 Hangul Syllable-C447 | c446 Hangul Syllable-C446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c449 Hangul Syllable-C449 | c448 Hangul Syllable-C448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44b Hangul Syllable-C44B | c44a Hangul Syllable-C44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44d Hangul Syllable-C44D | c44c Hangul Syllable-C44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c44f Hangul Syllable-C44F | c44e Hangul Syllable-C44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c451 Hangul Syllable-C451 | c450 Hangul Syllable-C450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c453 Hangul Syllable-C453 | c452 Hangul Syllable-C452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c455 Hangul Syllable-C455 | c454 Hangul Syllable-C454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c457 Hangul Syllable-C457 | c456 Hangul Syllable-C456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c459 Hangul Syllable-C459 | c458 Hangul Syllable-C458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45b Hangul Syllable-C45B | c45a Hangul Syllable-C45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45d Hangul Syllable-C45D | c45c Hangul Syllable-C45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c45f Hangul Syllable-C45F | c45e Hangul Syllable-C45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c461 Hangul Syllable-C461 | c460 Hangul Syllable-C460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c463 Hangul Syllable-C463 | c462 Hangul Syllable-C462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c465 Hangul Syllable-C465 | c464 Hangul Syllable-C464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c467 Hangul Syllable-C467 | c466 Hangul Syllable-C466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c469 Hangul Syllable-C469 | c468 Hangul Syllable-C468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46b Hangul Syllable-C46B | c46a Hangul Syllable-C46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46d Hangul Syllable-C46D | c46c Hangul Syllable-C46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c46f Hangul Syllable-C46F | c46e Hangul Syllable-C46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c471 Hangul Syllable-C471 | c470 Hangul Syllable-C470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c473 Hangul Syllable-C473 | c472 Hangul Syllable-C472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c475 Hangul Syllable-C475 | c474 Hangul Syllable-C474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c477 Hangul Syllable-C477 | c476 Hangul Syllable-C476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c479 Hangul Syllable-C479 | c478 Hangul Syllable-C478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47b Hangul Syllable-C47B | c47a Hangul Syllable-C47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47d Hangul Syllable-C47D | c47c Hangul Syllable-C47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c47f Hangul Syllable-C47F | c47e Hangul Syllable-C47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c481 Hangul Syllable-C481 | c480 Hangul Syllable-C480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c483 Hangul Syllable-C483 | c482 Hangul Syllable-C482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c485 Hangul Syllable-C485 | c484 Hangul Syllable-C484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c487 Hangul Syllable-C487 | c486 Hangul Syllable-C486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c489 Hangul Syllable-C489 | c488 Hangul Syllable-C488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48b Hangul Syllable-C48B | c48a Hangul Syllable-C48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48d Hangul Syllable-C48D | c48c Hangul Syllable-C48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c48f Hangul Syllable-C48F | c48e Hangul Syllable-C48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c491 Hangul Syllable-C491 | c490 Hangul Syllable-C490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c493 Hangul Syllable-C493 | c492 Hangul Syllable-C492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c495 Hangul Syllable-C495 | c494 Hangul Syllable-C494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c497 Hangul Syllable-C497 | c496 Hangul Syllable-C496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c499 Hangul Syllable-C499 | c498 Hangul Syllable-C498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49b Hangul Syllable-C49B | c49a Hangul Syllable-C49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49d Hangul Syllable-C49D | c49c Hangul Syllable-C49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c49f Hangul Syllable-C49F | c49e Hangul Syllable-C49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a1 Hangul Syllable-C4A1 | c4a0 Hangul Syllable-C4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a3 Hangul Syllable-C4A3 | c4a2 Hangul Syllable-C4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a5 Hangul Syllable-C4A5 | c4a4 Hangul Syllable-C4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a7 Hangul Syllable-C4A7 | c4a6 Hangul Syllable-C4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4a9 Hangul Syllable-C4A9 | c4a8 Hangul Syllable-C4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ab Hangul Syllable-C4AB | c4aa Hangul Syllable-C4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ad Hangul Syllable-C4AD | c4ac Hangul Syllable-C4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4af Hangul Syllable-C4AF | c4ae Hangul Syllable-C4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b1 Hangul Syllable-C4B1 | c4b0 Hangul Syllable-C4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b3 Hangul Syllable-C4B3 | c4b2 Hangul Syllable-C4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b5 Hangul Syllable-C4B5 | c4b4 Hangul Syllable-C4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b7 Hangul Syllable-C4B7 | c4b6 Hangul Syllable-C4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4b9 Hangul Syllable-C4B9 | c4b8 Hangul Syllable-C4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bb Hangul Syllable-C4BB | c4ba Hangul Syllable-C4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bd Hangul Syllable-C4BD | c4bc Hangul Syllable-C4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4bf Hangul Syllable-C4BF | c4be Hangul Syllable-C4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c1 Hangul Syllable-C4C1 | c4c0 Hangul Syllable-C4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c3 Hangul Syllable-C4C3 | c4c2 Hangul Syllable-C4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c5 Hangul Syllable-C4C5 | c4c4 Hangul Syllable-C4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c7 Hangul Syllable-C4C7 | c4c6 Hangul Syllable-C4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4c9 Hangul Syllable-C4C9 | c4c8 Hangul Syllable-C4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cb Hangul Syllable-C4CB | c4ca Hangul Syllable-C4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cd Hangul Syllable-C4CD | c4cc Hangul Syllable-C4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4cf Hangul Syllable-C4CF | c4ce Hangul Syllable-C4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d1 Hangul Syllable-C4D1 | c4d0 Hangul Syllable-C4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d3 Hangul Syllable-C4D3 | c4d2 Hangul Syllable-C4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d5 Hangul Syllable-C4D5 | c4d4 Hangul Syllable-C4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d7 Hangul Syllable-C4D7 | c4d6 Hangul Syllable-C4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4d9 Hangul Syllable-C4D9 | c4d8 Hangul Syllable-C4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4db Hangul Syllable-C4DB | c4da Hangul Syllable-C4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4dd Hangul Syllable-C4DD | c4dc Hangul Syllable-C4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4df Hangul Syllable-C4DF | c4de Hangul Syllable-C4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e1 Hangul Syllable-C4E1 | c4e0 Hangul Syllable-C4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e3 Hangul Syllable-C4E3 | c4e2 Hangul Syllable-C4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e5 Hangul Syllable-C4E5 | c4e4 Hangul Syllable-C4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e7 Hangul Syllable-C4E7 | c4e6 Hangul Syllable-C4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4e9 Hangul Syllable-C4E9 | c4e8 Hangul Syllable-C4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4eb Hangul Syllable-C4EB | c4ea Hangul Syllable-C4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ed Hangul Syllable-C4ED | c4ec Hangul Syllable-C4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ef Hangul Syllable-C4EF | c4ee Hangul Syllable-C4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f1 Hangul Syllable-C4F1 | c4f0 Hangul Syllable-C4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f3 Hangul Syllable-C4F3 | c4f2 Hangul Syllable-C4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f5 Hangul Syllable-C4F5 | c4f4 Hangul Syllable-C4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f7 Hangul Syllable-C4F7 | c4f6 Hangul Syllable-C4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4f9 Hangul Syllable-C4F9 | c4f8 Hangul Syllable-C4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4fb Hangul Syllable-C4FB | c4fa Hangul Syllable-C4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4fd Hangul Syllable-C4FD | c4fc Hangul Syllable-C4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c4ff Hangul Syllable-C4FF | c4fe Hangul Syllable-C4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c501 Hangul Syllable-C501 | c500 Hangul Syllable-C500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c503 Hangul Syllable-C503 | c502 Hangul Syllable-C502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c505 Hangul Syllable-C505 | c504 Hangul Syllable-C504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c507 Hangul Syllable-C507 | c506 Hangul Syllable-C506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c509 Hangul Syllable-C509 | c508 Hangul Syllable-C508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50b Hangul Syllable-C50B | c50a Hangul Syllable-C50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50d Hangul Syllable-C50D | c50c Hangul Syllable-C50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c50f Hangul Syllable-C50F | c50e Hangul Syllable-C50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c511 Hangul Syllable-C511 | c510 Hangul Syllable-C510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c513 Hangul Syllable-C513 | c512 Hangul Syllable-C512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c515 Hangul Syllable-C515 | c514 Hangul Syllable-C514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c517 Hangul Syllable-C517 | c516 Hangul Syllable-C516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c519 Hangul Syllable-C519 | c518 Hangul Syllable-C518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51b Hangul Syllable-C51B | c51a Hangul Syllable-C51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51d Hangul Syllable-C51D | c51c Hangul Syllable-C51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c51f Hangul Syllable-C51F | c51e Hangul Syllable-C51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c521 Hangul Syllable-C521 | c520 Hangul Syllable-C520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c523 Hangul Syllable-C523 | c522 Hangul Syllable-C522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c525 Hangul Syllable-C525 | c524 Hangul Syllable-C524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c527 Hangul Syllable-C527 | c526 Hangul Syllable-C526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c529 Hangul Syllable-C529 | c528 Hangul Syllable-C528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52b Hangul Syllable-C52B | c52a Hangul Syllable-C52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52d Hangul Syllable-C52D | c52c Hangul Syllable-C52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c52f Hangul Syllable-C52F | c52e Hangul Syllable-C52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c531 Hangul Syllable-C531 | c530 Hangul Syllable-C530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c533 Hangul Syllable-C533 | c532 Hangul Syllable-C532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c535 Hangul Syllable-C535 | c534 Hangul Syllable-C534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c537 Hangul Syllable-C537 | c536 Hangul Syllable-C536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c539 Hangul Syllable-C539 | c538 Hangul Syllable-C538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53b Hangul Syllable-C53B | c53a Hangul Syllable-C53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53d Hangul Syllable-C53D | c53c Hangul Syllable-C53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c53f Hangul Syllable-C53F | c53e Hangul Syllable-C53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c541 Hangul Syllable-C541 | c540 Hangul Syllable-C540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c543 Hangul Syllable-C543 | c542 Hangul Syllable-C542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c545 Hangul Syllable-C545 | c544 Hangul Syllable-C544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c547 Hangul Syllable-C547 | c546 Hangul Syllable-C546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c549 Hangul Syllable-C549 | c548 Hangul Syllable-C548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54b Hangul Syllable-C54B | c54a Hangul Syllable-C54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54d Hangul Syllable-C54D | c54c Hangul Syllable-C54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c54f Hangul Syllable-C54F | c54e Hangul Syllable-C54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c551 Hangul Syllable-C551 | c550 Hangul Syllable-C550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c553 Hangul Syllable-C553 | c552 Hangul Syllable-C552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c555 Hangul Syllable-C555 | c554 Hangul Syllable-C554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c557 Hangul Syllable-C557 | c556 Hangul Syllable-C556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c559 Hangul Syllable-C559 | c558 Hangul Syllable-C558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55b Hangul Syllable-C55B | c55a Hangul Syllable-C55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55d Hangul Syllable-C55D | c55c Hangul Syllable-C55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c55f Hangul Syllable-C55F | c55e Hangul Syllable-C55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c561 Hangul Syllable-C561 | c560 Hangul Syllable-C560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c563 Hangul Syllable-C563 | c562 Hangul Syllable-C562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c565 Hangul Syllable-C565 | c564 Hangul Syllable-C564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c567 Hangul Syllable-C567 | c566 Hangul Syllable-C566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c569 Hangul Syllable-C569 | c568 Hangul Syllable-C568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56b Hangul Syllable-C56B | c56a Hangul Syllable-C56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56d Hangul Syllable-C56D | c56c Hangul Syllable-C56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c56f Hangul Syllable-C56F | c56e Hangul Syllable-C56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c571 Hangul Syllable-C571 | c570 Hangul Syllable-C570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c573 Hangul Syllable-C573 | c572 Hangul Syllable-C572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c575 Hangul Syllable-C575 | c574 Hangul Syllable-C574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c577 Hangul Syllable-C577 | c576 Hangul Syllable-C576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c579 Hangul Syllable-C579 | c578 Hangul Syllable-C578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57b Hangul Syllable-C57B | c57a Hangul Syllable-C57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57d Hangul Syllable-C57D | c57c Hangul Syllable-C57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c57f Hangul Syllable-C57F | c57e Hangul Syllable-C57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c581 Hangul Syllable-C581 | c580 Hangul Syllable-C580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c583 Hangul Syllable-C583 | c582 Hangul Syllable-C582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c585 Hangul Syllable-C585 | c584 Hangul Syllable-C584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c587 Hangul Syllable-C587 | c586 Hangul Syllable-C586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c589 Hangul Syllable-C589 | c588 Hangul Syllable-C588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58b Hangul Syllable-C58B | c58a Hangul Syllable-C58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58d Hangul Syllable-C58D | c58c Hangul Syllable-C58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c58f Hangul Syllable-C58F | c58e Hangul Syllable-C58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c591 Hangul Syllable-C591 | c590 Hangul Syllable-C590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c593 Hangul Syllable-C593 | c592 Hangul Syllable-C592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c595 Hangul Syllable-C595 | c594 Hangul Syllable-C594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c597 Hangul Syllable-C597 | c596 Hangul Syllable-C596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c599 Hangul Syllable-C599 | c598 Hangul Syllable-C598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59b Hangul Syllable-C59B | c59a Hangul Syllable-C59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59d Hangul Syllable-C59D | c59c Hangul Syllable-C59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c59f Hangul Syllable-C59F | c59e Hangul Syllable-C59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a1 Hangul Syllable-C5A1 | c5a0 Hangul Syllable-C5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a3 Hangul Syllable-C5A3 | c5a2 Hangul Syllable-C5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a5 Hangul Syllable-C5A5 | c5a4 Hangul Syllable-C5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a7 Hangul Syllable-C5A7 | c5a6 Hangul Syllable-C5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5a9 Hangul Syllable-C5A9 | c5a8 Hangul Syllable-C5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ab Hangul Syllable-C5AB | c5aa Hangul Syllable-C5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ad Hangul Syllable-C5AD | c5ac Hangul Syllable-C5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5af Hangul Syllable-C5AF | c5ae Hangul Syllable-C5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b1 Hangul Syllable-C5B1 | c5b0 Hangul Syllable-C5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b3 Hangul Syllable-C5B3 | c5b2 Hangul Syllable-C5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b5 Hangul Syllable-C5B5 | c5b4 Hangul Syllable-C5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b7 Hangul Syllable-C5B7 | c5b6 Hangul Syllable-C5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5b9 Hangul Syllable-C5B9 | c5b8 Hangul Syllable-C5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bb Hangul Syllable-C5BB | c5ba Hangul Syllable-C5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bd Hangul Syllable-C5BD | c5bc Hangul Syllable-C5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5bf Hangul Syllable-C5BF | c5be Hangul Syllable-C5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c1 Hangul Syllable-C5C1 | c5c0 Hangul Syllable-C5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c3 Hangul Syllable-C5C3 | c5c2 Hangul Syllable-C5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c5 Hangul Syllable-C5C5 | c5c4 Hangul Syllable-C5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c7 Hangul Syllable-C5C7 | c5c6 Hangul Syllable-C5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5c9 Hangul Syllable-C5C9 | c5c8 Hangul Syllable-C5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cb Hangul Syllable-C5CB | c5ca Hangul Syllable-C5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cd Hangul Syllable-C5CD | c5cc Hangul Syllable-C5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5cf Hangul Syllable-C5CF | c5ce Hangul Syllable-C5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d1 Hangul Syllable-C5D1 | c5d0 Hangul Syllable-C5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d3 Hangul Syllable-C5D3 | c5d2 Hangul Syllable-C5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d5 Hangul Syllable-C5D5 | c5d4 Hangul Syllable-C5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d7 Hangul Syllable-C5D7 | c5d6 Hangul Syllable-C5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5d9 Hangul Syllable-C5D9 | c5d8 Hangul Syllable-C5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5db Hangul Syllable-C5DB | c5da Hangul Syllable-C5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5dd Hangul Syllable-C5DD | c5dc Hangul Syllable-C5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5df Hangul Syllable-C5DF | c5de Hangul Syllable-C5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e1 Hangul Syllable-C5E1 | c5e0 Hangul Syllable-C5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e3 Hangul Syllable-C5E3 | c5e2 Hangul Syllable-C5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e5 Hangul Syllable-C5E5 | c5e4 Hangul Syllable-C5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e7 Hangul Syllable-C5E7 | c5e6 Hangul Syllable-C5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5e9 Hangul Syllable-C5E9 | c5e8 Hangul Syllable-C5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5eb Hangul Syllable-C5EB | c5ea Hangul Syllable-C5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ed Hangul Syllable-C5ED | c5ec Hangul Syllable-C5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ef Hangul Syllable-C5EF | c5ee Hangul Syllable-C5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f1 Hangul Syllable-C5F1 | c5f0 Hangul Syllable-C5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f3 Hangul Syllable-C5F3 | c5f2 Hangul Syllable-C5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f5 Hangul Syllable-C5F5 | c5f4 Hangul Syllable-C5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f7 Hangul Syllable-C5F7 | c5f6 Hangul Syllable-C5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5f9 Hangul Syllable-C5F9 | c5f8 Hangul Syllable-C5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5fb Hangul Syllable-C5FB | c5fa Hangul Syllable-C5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5fd Hangul Syllable-C5FD | c5fc Hangul Syllable-C5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c5ff Hangul Syllable-C5FF | c5fe Hangul Syllable-C5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c601 Hangul Syllable-C601 | c600 Hangul Syllable-C600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c603 Hangul Syllable-C603 | c602 Hangul Syllable-C602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c605 Hangul Syllable-C605 | c604 Hangul Syllable-C604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c607 Hangul Syllable-C607 | c606 Hangul Syllable-C606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c609 Hangul Syllable-C609 | c608 Hangul Syllable-C608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60b Hangul Syllable-C60B | c60a Hangul Syllable-C60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60d Hangul Syllable-C60D | c60c Hangul Syllable-C60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c60f Hangul Syllable-C60F | c60e Hangul Syllable-C60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c611 Hangul Syllable-C611 | c610 Hangul Syllable-C610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c613 Hangul Syllable-C613 | c612 Hangul Syllable-C612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c615 Hangul Syllable-C615 | c614 Hangul Syllable-C614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c617 Hangul Syllable-C617 | c616 Hangul Syllable-C616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c619 Hangul Syllable-C619 | c618 Hangul Syllable-C618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61b Hangul Syllable-C61B | c61a Hangul Syllable-C61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61d Hangul Syllable-C61D | c61c Hangul Syllable-C61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c61f Hangul Syllable-C61F | c61e Hangul Syllable-C61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c621 Hangul Syllable-C621 | c620 Hangul Syllable-C620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c623 Hangul Syllable-C623 | c622 Hangul Syllable-C622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c625 Hangul Syllable-C625 | c624 Hangul Syllable-C624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c627 Hangul Syllable-C627 | c626 Hangul Syllable-C626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c629 Hangul Syllable-C629 | c628 Hangul Syllable-C628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62b Hangul Syllable-C62B | c62a Hangul Syllable-C62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62d Hangul Syllable-C62D | c62c Hangul Syllable-C62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c62f Hangul Syllable-C62F | c62e Hangul Syllable-C62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c631 Hangul Syllable-C631 | c630 Hangul Syllable-C630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c633 Hangul Syllable-C633 | c632 Hangul Syllable-C632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c635 Hangul Syllable-C635 | c634 Hangul Syllable-C634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c637 Hangul Syllable-C637 | c636 Hangul Syllable-C636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c639 Hangul Syllable-C639 | c638 Hangul Syllable-C638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63b Hangul Syllable-C63B | c63a Hangul Syllable-C63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63d Hangul Syllable-C63D | c63c Hangul Syllable-C63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c63f Hangul Syllable-C63F | c63e Hangul Syllable-C63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c641 Hangul Syllable-C641 | c640 Hangul Syllable-C640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c643 Hangul Syllable-C643 | c642 Hangul Syllable-C642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c645 Hangul Syllable-C645 | c644 Hangul Syllable-C644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c647 Hangul Syllable-C647 | c646 Hangul Syllable-C646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c649 Hangul Syllable-C649 | c648 Hangul Syllable-C648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64b Hangul Syllable-C64B | c64a Hangul Syllable-C64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64d Hangul Syllable-C64D | c64c Hangul Syllable-C64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c64f Hangul Syllable-C64F | c64e Hangul Syllable-C64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c651 Hangul Syllable-C651 | c650 Hangul Syllable-C650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c653 Hangul Syllable-C653 | c652 Hangul Syllable-C652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c655 Hangul Syllable-C655 | c654 Hangul Syllable-C654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c657 Hangul Syllable-C657 | c656 Hangul Syllable-C656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c659 Hangul Syllable-C659 | c658 Hangul Syllable-C658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65b Hangul Syllable-C65B | c65a Hangul Syllable-C65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65d Hangul Syllable-C65D | c65c Hangul Syllable-C65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c65f Hangul Syllable-C65F | c65e Hangul Syllable-C65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c661 Hangul Syllable-C661 | c660 Hangul Syllable-C660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c663 Hangul Syllable-C663 | c662 Hangul Syllable-C662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c665 Hangul Syllable-C665 | c664 Hangul Syllable-C664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c667 Hangul Syllable-C667 | c666 Hangul Syllable-C666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c669 Hangul Syllable-C669 | c668 Hangul Syllable-C668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66b Hangul Syllable-C66B | c66a Hangul Syllable-C66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66d Hangul Syllable-C66D | c66c Hangul Syllable-C66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c66f Hangul Syllable-C66F | c66e Hangul Syllable-C66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c671 Hangul Syllable-C671 | c670 Hangul Syllable-C670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c673 Hangul Syllable-C673 | c672 Hangul Syllable-C672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c675 Hangul Syllable-C675 | c674 Hangul Syllable-C674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c677 Hangul Syllable-C677 | c676 Hangul Syllable-C676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c679 Hangul Syllable-C679 | c678 Hangul Syllable-C678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67b Hangul Syllable-C67B | c67a Hangul Syllable-C67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67d Hangul Syllable-C67D | c67c Hangul Syllable-C67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c67f Hangul Syllable-C67F | c67e Hangul Syllable-C67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c681 Hangul Syllable-C681 | c680 Hangul Syllable-C680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c683 Hangul Syllable-C683 | c682 Hangul Syllable-C682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c685 Hangul Syllable-C685 | c684 Hangul Syllable-C684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c687 Hangul Syllable-C687 | c686 Hangul Syllable-C686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c689 Hangul Syllable-C689 | c688 Hangul Syllable-C688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68b Hangul Syllable-C68B | c68a Hangul Syllable-C68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68d Hangul Syllable-C68D | c68c Hangul Syllable-C68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c68f Hangul Syllable-C68F | c68e Hangul Syllable-C68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c691 Hangul Syllable-C691 | c690 Hangul Syllable-C690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c693 Hangul Syllable-C693 | c692 Hangul Syllable-C692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c695 Hangul Syllable-C695 | c694 Hangul Syllable-C694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c697 Hangul Syllable-C697 | c696 Hangul Syllable-C696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c699 Hangul Syllable-C699 | c698 Hangul Syllable-C698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69b Hangul Syllable-C69B | c69a Hangul Syllable-C69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69d Hangul Syllable-C69D | c69c Hangul Syllable-C69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c69f Hangul Syllable-C69F | c69e Hangul Syllable-C69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a1 Hangul Syllable-C6A1 | c6a0 Hangul Syllable-C6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a3 Hangul Syllable-C6A3 | c6a2 Hangul Syllable-C6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a5 Hangul Syllable-C6A5 | c6a4 Hangul Syllable-C6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a7 Hangul Syllable-C6A7 | c6a6 Hangul Syllable-C6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6a9 Hangul Syllable-C6A9 | c6a8 Hangul Syllable-C6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ab Hangul Syllable-C6AB | c6aa Hangul Syllable-C6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ad Hangul Syllable-C6AD | c6ac Hangul Syllable-C6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6af Hangul Syllable-C6AF | c6ae Hangul Syllable-C6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b1 Hangul Syllable-C6B1 | c6b0 Hangul Syllable-C6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b3 Hangul Syllable-C6B3 | c6b2 Hangul Syllable-C6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b5 Hangul Syllable-C6B5 | c6b4 Hangul Syllable-C6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b7 Hangul Syllable-C6B7 | c6b6 Hangul Syllable-C6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6b9 Hangul Syllable-C6B9 | c6b8 Hangul Syllable-C6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bb Hangul Syllable-C6BB | c6ba Hangul Syllable-C6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bd Hangul Syllable-C6BD | c6bc Hangul Syllable-C6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6bf Hangul Syllable-C6BF | c6be Hangul Syllable-C6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c1 Hangul Syllable-C6C1 | c6c0 Hangul Syllable-C6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c3 Hangul Syllable-C6C3 | c6c2 Hangul Syllable-C6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c5 Hangul Syllable-C6C5 | c6c4 Hangul Syllable-C6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c7 Hangul Syllable-C6C7 | c6c6 Hangul Syllable-C6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6c9 Hangul Syllable-C6C9 | c6c8 Hangul Syllable-C6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cb Hangul Syllable-C6CB | c6ca Hangul Syllable-C6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cd Hangul Syllable-C6CD | c6cc Hangul Syllable-C6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6cf Hangul Syllable-C6CF | c6ce Hangul Syllable-C6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d1 Hangul Syllable-C6D1 | c6d0 Hangul Syllable-C6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d3 Hangul Syllable-C6D3 | c6d2 Hangul Syllable-C6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d5 Hangul Syllable-C6D5 | c6d4 Hangul Syllable-C6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d7 Hangul Syllable-C6D7 | c6d6 Hangul Syllable-C6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6d9 Hangul Syllable-C6D9 | c6d8 Hangul Syllable-C6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6db Hangul Syllable-C6DB | c6da Hangul Syllable-C6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6dd Hangul Syllable-C6DD | c6dc Hangul Syllable-C6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6df Hangul Syllable-C6DF | c6de Hangul Syllable-C6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e1 Hangul Syllable-C6E1 | c6e0 Hangul Syllable-C6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e3 Hangul Syllable-C6E3 | c6e2 Hangul Syllable-C6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e5 Hangul Syllable-C6E5 | c6e4 Hangul Syllable-C6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e7 Hangul Syllable-C6E7 | c6e6 Hangul Syllable-C6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6e9 Hangul Syllable-C6E9 | c6e8 Hangul Syllable-C6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6eb Hangul Syllable-C6EB | c6ea Hangul Syllable-C6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ed Hangul Syllable-C6ED | c6ec Hangul Syllable-C6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ef Hangul Syllable-C6EF | c6ee Hangul Syllable-C6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f1 Hangul Syllable-C6F1 | c6f0 Hangul Syllable-C6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f3 Hangul Syllable-C6F3 | c6f2 Hangul Syllable-C6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f5 Hangul Syllable-C6F5 | c6f4 Hangul Syllable-C6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f7 Hangul Syllable-C6F7 | c6f6 Hangul Syllable-C6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6f9 Hangul Syllable-C6F9 | c6f8 Hangul Syllable-C6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6fb Hangul Syllable-C6FB | c6fa Hangul Syllable-C6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6fd Hangul Syllable-C6FD | c6fc Hangul Syllable-C6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c6ff Hangul Syllable-C6FF | c6fe Hangul Syllable-C6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c701 Hangul Syllable-C701 | c700 Hangul Syllable-C700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c703 Hangul Syllable-C703 | c702 Hangul Syllable-C702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c705 Hangul Syllable-C705 | c704 Hangul Syllable-C704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c707 Hangul Syllable-C707 | c706 Hangul Syllable-C706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c709 Hangul Syllable-C709 | c708 Hangul Syllable-C708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70b Hangul Syllable-C70B | c70a Hangul Syllable-C70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70d Hangul Syllable-C70D | c70c Hangul Syllable-C70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c70f Hangul Syllable-C70F | c70e Hangul Syllable-C70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c711 Hangul Syllable-C711 | c710 Hangul Syllable-C710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c713 Hangul Syllable-C713 | c712 Hangul Syllable-C712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c715 Hangul Syllable-C715 | c714 Hangul Syllable-C714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c717 Hangul Syllable-C717 | c716 Hangul Syllable-C716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c719 Hangul Syllable-C719 | c718 Hangul Syllable-C718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71b Hangul Syllable-C71B | c71a Hangul Syllable-C71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71d Hangul Syllable-C71D | c71c Hangul Syllable-C71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c71f Hangul Syllable-C71F | c71e Hangul Syllable-C71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c721 Hangul Syllable-C721 | c720 Hangul Syllable-C720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c723 Hangul Syllable-C723 | c722 Hangul Syllable-C722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c725 Hangul Syllable-C725 | c724 Hangul Syllable-C724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c727 Hangul Syllable-C727 | c726 Hangul Syllable-C726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c729 Hangul Syllable-C729 | c728 Hangul Syllable-C728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72b Hangul Syllable-C72B | c72a Hangul Syllable-C72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72d Hangul Syllable-C72D | c72c Hangul Syllable-C72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c72f Hangul Syllable-C72F | c72e Hangul Syllable-C72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c731 Hangul Syllable-C731 | c730 Hangul Syllable-C730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c733 Hangul Syllable-C733 | c732 Hangul Syllable-C732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c735 Hangul Syllable-C735 | c734 Hangul Syllable-C734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c737 Hangul Syllable-C737 | c736 Hangul Syllable-C736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c739 Hangul Syllable-C739 | c738 Hangul Syllable-C738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73b Hangul Syllable-C73B | c73a Hangul Syllable-C73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73d Hangul Syllable-C73D | c73c Hangul Syllable-C73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c73f Hangul Syllable-C73F | c73e Hangul Syllable-C73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c741 Hangul Syllable-C741 | c740 Hangul Syllable-C740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c743 Hangul Syllable-C743 | c742 Hangul Syllable-C742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c745 Hangul Syllable-C745 | c744 Hangul Syllable-C744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c747 Hangul Syllable-C747 | c746 Hangul Syllable-C746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c749 Hangul Syllable-C749 | c748 Hangul Syllable-C748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74b Hangul Syllable-C74B | c74a Hangul Syllable-C74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74d Hangul Syllable-C74D | c74c Hangul Syllable-C74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c74f Hangul Syllable-C74F | c74e Hangul Syllable-C74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c751 Hangul Syllable-C751 | c750 Hangul Syllable-C750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c753 Hangul Syllable-C753 | c752 Hangul Syllable-C752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c755 Hangul Syllable-C755 | c754 Hangul Syllable-C754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c757 Hangul Syllable-C757 | c756 Hangul Syllable-C756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c759 Hangul Syllable-C759 | c758 Hangul Syllable-C758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75b Hangul Syllable-C75B | c75a Hangul Syllable-C75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75d Hangul Syllable-C75D | c75c Hangul Syllable-C75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c75f Hangul Syllable-C75F | c75e Hangul Syllable-C75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c761 Hangul Syllable-C761 | c760 Hangul Syllable-C760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c763 Hangul Syllable-C763 | c762 Hangul Syllable-C762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c765 Hangul Syllable-C765 | c764 Hangul Syllable-C764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c767 Hangul Syllable-C767 | c766 Hangul Syllable-C766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c769 Hangul Syllable-C769 | c768 Hangul Syllable-C768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76b Hangul Syllable-C76B | c76a Hangul Syllable-C76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76d Hangul Syllable-C76D | c76c Hangul Syllable-C76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c76f Hangul Syllable-C76F | c76e Hangul Syllable-C76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c771 Hangul Syllable-C771 | c770 Hangul Syllable-C770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c773 Hangul Syllable-C773 | c772 Hangul Syllable-C772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c775 Hangul Syllable-C775 | c774 Hangul Syllable-C774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c777 Hangul Syllable-C777 | c776 Hangul Syllable-C776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c779 Hangul Syllable-C779 | c778 Hangul Syllable-C778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77b Hangul Syllable-C77B | c77a Hangul Syllable-C77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77d Hangul Syllable-C77D | c77c Hangul Syllable-C77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c77f Hangul Syllable-C77F | c77e Hangul Syllable-C77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c781 Hangul Syllable-C781 | c780 Hangul Syllable-C780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c783 Hangul Syllable-C783 | c782 Hangul Syllable-C782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c785 Hangul Syllable-C785 | c784 Hangul Syllable-C784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c787 Hangul Syllable-C787 | c786 Hangul Syllable-C786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c789 Hangul Syllable-C789 | c788 Hangul Syllable-C788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78b Hangul Syllable-C78B | c78a Hangul Syllable-C78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78d Hangul Syllable-C78D | c78c Hangul Syllable-C78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c78f Hangul Syllable-C78F | c78e Hangul Syllable-C78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c791 Hangul Syllable-C791 | c790 Hangul Syllable-C790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c793 Hangul Syllable-C793 | c792 Hangul Syllable-C792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c795 Hangul Syllable-C795 | c794 Hangul Syllable-C794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c797 Hangul Syllable-C797 | c796 Hangul Syllable-C796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c799 Hangul Syllable-C799 | c798 Hangul Syllable-C798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79b Hangul Syllable-C79B | c79a Hangul Syllable-C79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79d Hangul Syllable-C79D | c79c Hangul Syllable-C79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c79f Hangul Syllable-C79F | c79e Hangul Syllable-C79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a1 Hangul Syllable-C7A1 | c7a0 Hangul Syllable-C7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a3 Hangul Syllable-C7A3 | c7a2 Hangul Syllable-C7A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a5 Hangul Syllable-C7A5 | c7a4 Hangul Syllable-C7A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a7 Hangul Syllable-C7A7 | c7a6 Hangul Syllable-C7A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7a9 Hangul Syllable-C7A9 | c7a8 Hangul Syllable-C7A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ab Hangul Syllable-C7AB | c7aa Hangul Syllable-C7AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ad Hangul Syllable-C7AD | c7ac Hangul Syllable-C7AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7af Hangul Syllable-C7AF | c7ae Hangul Syllable-C7AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b1 Hangul Syllable-C7B1 | c7b0 Hangul Syllable-C7B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b3 Hangul Syllable-C7B3 | c7b2 Hangul Syllable-C7B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b5 Hangul Syllable-C7B5 | c7b4 Hangul Syllable-C7B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b7 Hangul Syllable-C7B7 | c7b6 Hangul Syllable-C7B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7b9 Hangul Syllable-C7B9 | c7b8 Hangul Syllable-C7B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bb Hangul Syllable-C7BB | c7ba Hangul Syllable-C7BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bd Hangul Syllable-C7BD | c7bc Hangul Syllable-C7BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7bf Hangul Syllable-C7BF | c7be Hangul Syllable-C7BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c1 Hangul Syllable-C7C1 | c7c0 Hangul Syllable-C7C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c3 Hangul Syllable-C7C3 | c7c2 Hangul Syllable-C7C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c5 Hangul Syllable-C7C5 | c7c4 Hangul Syllable-C7C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c7 Hangul Syllable-C7C7 | c7c6 Hangul Syllable-C7C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7c9 Hangul Syllable-C7C9 | c7c8 Hangul Syllable-C7C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cb Hangul Syllable-C7CB | c7ca Hangul Syllable-C7CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cd Hangul Syllable-C7CD | c7cc Hangul Syllable-C7CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7cf Hangul Syllable-C7CF | c7ce Hangul Syllable-C7CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d1 Hangul Syllable-C7D1 | c7d0 Hangul Syllable-C7D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d3 Hangul Syllable-C7D3 | c7d2 Hangul Syllable-C7D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d5 Hangul Syllable-C7D5 | c7d4 Hangul Syllable-C7D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d7 Hangul Syllable-C7D7 | c7d6 Hangul Syllable-C7D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7d9 Hangul Syllable-C7D9 | c7d8 Hangul Syllable-C7D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7db Hangul Syllable-C7DB | c7da Hangul Syllable-C7DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7dd Hangul Syllable-C7DD | c7dc Hangul Syllable-C7DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7df Hangul Syllable-C7DF | c7de Hangul Syllable-C7DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e1 Hangul Syllable-C7E1 | c7e0 Hangul Syllable-C7E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e3 Hangul Syllable-C7E3 | c7e2 Hangul Syllable-C7E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e5 Hangul Syllable-C7E5 | c7e4 Hangul Syllable-C7E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e7 Hangul Syllable-C7E7 | c7e6 Hangul Syllable-C7E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7e9 Hangul Syllable-C7E9 | c7e8 Hangul Syllable-C7E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7eb Hangul Syllable-C7EB | c7ea Hangul Syllable-C7EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ed Hangul Syllable-C7ED | c7ec Hangul Syllable-C7EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ef Hangul Syllable-C7EF | c7ee Hangul Syllable-C7EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f1 Hangul Syllable-C7F1 | c7f0 Hangul Syllable-C7F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f3 Hangul Syllable-C7F3 | c7f2 Hangul Syllable-C7F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f5 Hangul Syllable-C7F5 | c7f4 Hangul Syllable-C7F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f7 Hangul Syllable-C7F7 | c7f6 Hangul Syllable-C7F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7f9 Hangul Syllable-C7F9 | c7f8 Hangul Syllable-C7F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7fb Hangul Syllable-C7FB | c7fa Hangul Syllable-C7FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7fd Hangul Syllable-C7FD | c7fc Hangul Syllable-C7FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c7ff Hangul Syllable-C7FF | c7fe Hangul Syllable-C7FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c801 Hangul Syllable-C801 | c800 Hangul Syllable-C800 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c803 Hangul Syllable-C803 | c802 Hangul Syllable-C802 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c805 Hangul Syllable-C805 | c804 Hangul Syllable-C804 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c807 Hangul Syllable-C807 | c806 Hangul Syllable-C806 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c809 Hangul Syllable-C809 | c808 Hangul Syllable-C808 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80b Hangul Syllable-C80B | c80a Hangul Syllable-C80A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80d Hangul Syllable-C80D | c80c Hangul Syllable-C80C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c80f Hangul Syllable-C80F | c80e Hangul Syllable-C80E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c811 Hangul Syllable-C811 | c810 Hangul Syllable-C810 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c813 Hangul Syllable-C813 | c812 Hangul Syllable-C812 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c815 Hangul Syllable-C815 | c814 Hangul Syllable-C814 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c817 Hangul Syllable-C817 | c816 Hangul Syllable-C816 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c819 Hangul Syllable-C819 | c818 Hangul Syllable-C818 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81b Hangul Syllable-C81B | c81a Hangul Syllable-C81A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81d Hangul Syllable-C81D | c81c Hangul Syllable-C81C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c81f Hangul Syllable-C81F | c81e Hangul Syllable-C81E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c821 Hangul Syllable-C821 | c820 Hangul Syllable-C820 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c823 Hangul Syllable-C823 | c822 Hangul Syllable-C822 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c825 Hangul Syllable-C825 | c824 Hangul Syllable-C824 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c827 Hangul Syllable-C827 | c826 Hangul Syllable-C826 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c829 Hangul Syllable-C829 | c828 Hangul Syllable-C828 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82b Hangul Syllable-C82B | c82a Hangul Syllable-C82A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82d Hangul Syllable-C82D | c82c Hangul Syllable-C82C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c82f Hangul Syllable-C82F | c82e Hangul Syllable-C82E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c831 Hangul Syllable-C831 | c830 Hangul Syllable-C830 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c833 Hangul Syllable-C833 | c832 Hangul Syllable-C832 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c835 Hangul Syllable-C835 | c834 Hangul Syllable-C834 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c837 Hangul Syllable-C837 | c836 Hangul Syllable-C836 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c839 Hangul Syllable-C839 | c838 Hangul Syllable-C838 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83b Hangul Syllable-C83B | c83a Hangul Syllable-C83A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83d Hangul Syllable-C83D | c83c Hangul Syllable-C83C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c83f Hangul Syllable-C83F | c83e Hangul Syllable-C83E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c841 Hangul Syllable-C841 | c840 Hangul Syllable-C840 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c843 Hangul Syllable-C843 | c842 Hangul Syllable-C842 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c845 Hangul Syllable-C845 | c844 Hangul Syllable-C844 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c847 Hangul Syllable-C847 | c846 Hangul Syllable-C846 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c849 Hangul Syllable-C849 | c848 Hangul Syllable-C848 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84b Hangul Syllable-C84B | c84a Hangul Syllable-C84A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84d Hangul Syllable-C84D | c84c Hangul Syllable-C84C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c84f Hangul Syllable-C84F | c84e Hangul Syllable-C84E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c851 Hangul Syllable-C851 | c850 Hangul Syllable-C850 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c853 Hangul Syllable-C853 | c852 Hangul Syllable-C852 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c855 Hangul Syllable-C855 | c854 Hangul Syllable-C854 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c857 Hangul Syllable-C857 | c856 Hangul Syllable-C856 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c859 Hangul Syllable-C859 | c858 Hangul Syllable-C858 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85b Hangul Syllable-C85B | c85a Hangul Syllable-C85A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85d Hangul Syllable-C85D | c85c Hangul Syllable-C85C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c85f Hangul Syllable-C85F | c85e Hangul Syllable-C85E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c861 Hangul Syllable-C861 | c860 Hangul Syllable-C860 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c863 Hangul Syllable-C863 | c862 Hangul Syllable-C862 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c865 Hangul Syllable-C865 | c864 Hangul Syllable-C864 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c867 Hangul Syllable-C867 | c866 Hangul Syllable-C866 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c869 Hangul Syllable-C869 | c868 Hangul Syllable-C868 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86b Hangul Syllable-C86B | c86a Hangul Syllable-C86A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86d Hangul Syllable-C86D | c86c Hangul Syllable-C86C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c86f Hangul Syllable-C86F | c86e Hangul Syllable-C86E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c871 Hangul Syllable-C871 | c870 Hangul Syllable-C870 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c873 Hangul Syllable-C873 | c872 Hangul Syllable-C872 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c875 Hangul Syllable-C875 | c874 Hangul Syllable-C874 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c877 Hangul Syllable-C877 | c876 Hangul Syllable-C876 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c879 Hangul Syllable-C879 | c878 Hangul Syllable-C878 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87b Hangul Syllable-C87B | c87a Hangul Syllable-C87A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87d Hangul Syllable-C87D | c87c Hangul Syllable-C87C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c87f Hangul Syllable-C87F | c87e Hangul Syllable-C87E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c881 Hangul Syllable-C881 | c880 Hangul Syllable-C880 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c883 Hangul Syllable-C883 | c882 Hangul Syllable-C882 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c885 Hangul Syllable-C885 | c884 Hangul Syllable-C884 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c887 Hangul Syllable-C887 | c886 Hangul Syllable-C886 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c889 Hangul Syllable-C889 | c888 Hangul Syllable-C888 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88b Hangul Syllable-C88B | c88a Hangul Syllable-C88A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88d Hangul Syllable-C88D | c88c Hangul Syllable-C88C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c88f Hangul Syllable-C88F | c88e Hangul Syllable-C88E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c891 Hangul Syllable-C891 | c890 Hangul Syllable-C890 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c893 Hangul Syllable-C893 | c892 Hangul Syllable-C892 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c895 Hangul Syllable-C895 | c894 Hangul Syllable-C894 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c897 Hangul Syllable-C897 | c896 Hangul Syllable-C896 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c899 Hangul Syllable-C899 | c898 Hangul Syllable-C898 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89b Hangul Syllable-C89B | c89a Hangul Syllable-C89A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89d Hangul Syllable-C89D | c89c Hangul Syllable-C89C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c89f Hangul Syllable-C89F | c89e Hangul Syllable-C89E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a1 Hangul Syllable-C8A1 | c8a0 Hangul Syllable-C8A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a3 Hangul Syllable-C8A3 | c8a2 Hangul Syllable-C8A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a5 Hangul Syllable-C8A5 | c8a4 Hangul Syllable-C8A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a7 Hangul Syllable-C8A7 | c8a6 Hangul Syllable-C8A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8a9 Hangul Syllable-C8A9 | c8a8 Hangul Syllable-C8A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ab Hangul Syllable-C8AB | c8aa Hangul Syllable-C8AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ad Hangul Syllable-C8AD | c8ac Hangul Syllable-C8AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8af Hangul Syllable-C8AF | c8ae Hangul Syllable-C8AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b1 Hangul Syllable-C8B1 | c8b0 Hangul Syllable-C8B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b3 Hangul Syllable-C8B3 | c8b2 Hangul Syllable-C8B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b5 Hangul Syllable-C8B5 | c8b4 Hangul Syllable-C8B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b7 Hangul Syllable-C8B7 | c8b6 Hangul Syllable-C8B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8b9 Hangul Syllable-C8B9 | c8b8 Hangul Syllable-C8B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bb Hangul Syllable-C8BB | c8ba Hangul Syllable-C8BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bd Hangul Syllable-C8BD | c8bc Hangul Syllable-C8BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8bf Hangul Syllable-C8BF | c8be Hangul Syllable-C8BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c1 Hangul Syllable-C8C1 | c8c0 Hangul Syllable-C8C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c3 Hangul Syllable-C8C3 | c8c2 Hangul Syllable-C8C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c5 Hangul Syllable-C8C5 | c8c4 Hangul Syllable-C8C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c7 Hangul Syllable-C8C7 | c8c6 Hangul Syllable-C8C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8c9 Hangul Syllable-C8C9 | c8c8 Hangul Syllable-C8C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cb Hangul Syllable-C8CB | c8ca Hangul Syllable-C8CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cd Hangul Syllable-C8CD | c8cc Hangul Syllable-C8CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8cf Hangul Syllable-C8CF | c8ce Hangul Syllable-C8CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d1 Hangul Syllable-C8D1 | c8d0 Hangul Syllable-C8D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d3 Hangul Syllable-C8D3 | c8d2 Hangul Syllable-C8D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d5 Hangul Syllable-C8D5 | c8d4 Hangul Syllable-C8D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d7 Hangul Syllable-C8D7 | c8d6 Hangul Syllable-C8D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8d9 Hangul Syllable-C8D9 | c8d8 Hangul Syllable-C8D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8db Hangul Syllable-C8DB | c8da Hangul Syllable-C8DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8dd Hangul Syllable-C8DD | c8dc Hangul Syllable-C8DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8df Hangul Syllable-C8DF | c8de Hangul Syllable-C8DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e1 Hangul Syllable-C8E1 | c8e0 Hangul Syllable-C8E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e3 Hangul Syllable-C8E3 | c8e2 Hangul Syllable-C8E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e5 Hangul Syllable-C8E5 | c8e4 Hangul Syllable-C8E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e7 Hangul Syllable-C8E7 | c8e6 Hangul Syllable-C8E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8e9 Hangul Syllable-C8E9 | c8e8 Hangul Syllable-C8E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8eb Hangul Syllable-C8EB | c8ea Hangul Syllable-C8EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ed Hangul Syllable-C8ED | c8ec Hangul Syllable-C8EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ef Hangul Syllable-C8EF | c8ee Hangul Syllable-C8EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f1 Hangul Syllable-C8F1 | c8f0 Hangul Syllable-C8F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f3 Hangul Syllable-C8F3 | c8f2 Hangul Syllable-C8F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f5 Hangul Syllable-C8F5 | c8f4 Hangul Syllable-C8F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f7 Hangul Syllable-C8F7 | c8f6 Hangul Syllable-C8F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8f9 Hangul Syllable-C8F9 | c8f8 Hangul Syllable-C8F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8fb Hangul Syllable-C8FB | c8fa Hangul Syllable-C8FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8fd Hangul Syllable-C8FD | c8fc Hangul Syllable-C8FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c8ff Hangul Syllable-C8FF | c8fe Hangul Syllable-C8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c901 Hangul Syllable-C901 | c900 Hangul Syllable-C900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c903 Hangul Syllable-C903 | c902 Hangul Syllable-C902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c905 Hangul Syllable-C905 | c904 Hangul Syllable-C904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c907 Hangul Syllable-C907 | c906 Hangul Syllable-C906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c909 Hangul Syllable-C909 | c908 Hangul Syllable-C908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90b Hangul Syllable-C90B | c90a Hangul Syllable-C90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90d Hangul Syllable-C90D | c90c Hangul Syllable-C90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c90f Hangul Syllable-C90F | c90e Hangul Syllable-C90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c911 Hangul Syllable-C911 | c910 Hangul Syllable-C910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c913 Hangul Syllable-C913 | c912 Hangul Syllable-C912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c915 Hangul Syllable-C915 | c914 Hangul Syllable-C914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c917 Hangul Syllable-C917 | c916 Hangul Syllable-C916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c919 Hangul Syllable-C919 | c918 Hangul Syllable-C918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91b Hangul Syllable-C91B | c91a Hangul Syllable-C91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91d Hangul Syllable-C91D | c91c Hangul Syllable-C91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c91f Hangul Syllable-C91F | c91e Hangul Syllable-C91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c921 Hangul Syllable-C921 | c920 Hangul Syllable-C920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c923 Hangul Syllable-C923 | c922 Hangul Syllable-C922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c925 Hangul Syllable-C925 | c924 Hangul Syllable-C924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c927 Hangul Syllable-C927 | c926 Hangul Syllable-C926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c929 Hangul Syllable-C929 | c928 Hangul Syllable-C928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92b Hangul Syllable-C92B | c92a Hangul Syllable-C92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92d Hangul Syllable-C92D | c92c Hangul Syllable-C92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c92f Hangul Syllable-C92F | c92e Hangul Syllable-C92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c931 Hangul Syllable-C931 | c930 Hangul Syllable-C930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c933 Hangul Syllable-C933 | c932 Hangul Syllable-C932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c935 Hangul Syllable-C935 | c934 Hangul Syllable-C934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c937 Hangul Syllable-C937 | c936 Hangul Syllable-C936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c939 Hangul Syllable-C939 | c938 Hangul Syllable-C938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93b Hangul Syllable-C93B | c93a Hangul Syllable-C93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93d Hangul Syllable-C93D | c93c Hangul Syllable-C93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c93f Hangul Syllable-C93F | c93e Hangul Syllable-C93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c941 Hangul Syllable-C941 | c940 Hangul Syllable-C940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c943 Hangul Syllable-C943 | c942 Hangul Syllable-C942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c945 Hangul Syllable-C945 | c944 Hangul Syllable-C944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c947 Hangul Syllable-C947 | c946 Hangul Syllable-C946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c949 Hangul Syllable-C949 | c948 Hangul Syllable-C948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94b Hangul Syllable-C94B | c94a Hangul Syllable-C94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94d Hangul Syllable-C94D | c94c Hangul Syllable-C94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c94f Hangul Syllable-C94F | c94e Hangul Syllable-C94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c951 Hangul Syllable-C951 | c950 Hangul Syllable-C950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c953 Hangul Syllable-C953 | c952 Hangul Syllable-C952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c955 Hangul Syllable-C955 | c954 Hangul Syllable-C954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c957 Hangul Syllable-C957 | c956 Hangul Syllable-C956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c959 Hangul Syllable-C959 | c958 Hangul Syllable-C958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95b Hangul Syllable-C95B | c95a Hangul Syllable-C95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95d Hangul Syllable-C95D | c95c Hangul Syllable-C95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c95f Hangul Syllable-C95F | c95e Hangul Syllable-C95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c961 Hangul Syllable-C961 | c960 Hangul Syllable-C960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c963 Hangul Syllable-C963 | c962 Hangul Syllable-C962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c965 Hangul Syllable-C965 | c964 Hangul Syllable-C964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c967 Hangul Syllable-C967 | c966 Hangul Syllable-C966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c969 Hangul Syllable-C969 | c968 Hangul Syllable-C968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96b Hangul Syllable-C96B | c96a Hangul Syllable-C96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96d Hangul Syllable-C96D | c96c Hangul Syllable-C96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c96f Hangul Syllable-C96F | c96e Hangul Syllable-C96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c971 Hangul Syllable-C971 | c970 Hangul Syllable-C970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c973 Hangul Syllable-C973 | c972 Hangul Syllable-C972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c975 Hangul Syllable-C975 | c974 Hangul Syllable-C974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c977 Hangul Syllable-C977 | c976 Hangul Syllable-C976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c979 Hangul Syllable-C979 | c978 Hangul Syllable-C978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97b Hangul Syllable-C97B | c97a Hangul Syllable-C97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97d Hangul Syllable-C97D | c97c Hangul Syllable-C97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c97f Hangul Syllable-C97F | c97e Hangul Syllable-C97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c981 Hangul Syllable-C981 | c980 Hangul Syllable-C980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c983 Hangul Syllable-C983 | c982 Hangul Syllable-C982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c985 Hangul Syllable-C985 | c984 Hangul Syllable-C984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c987 Hangul Syllable-C987 | c986 Hangul Syllable-C986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c989 Hangul Syllable-C989 | c988 Hangul Syllable-C988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98b Hangul Syllable-C98B | c98a Hangul Syllable-C98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98d Hangul Syllable-C98D | c98c Hangul Syllable-C98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c98f Hangul Syllable-C98F | c98e Hangul Syllable-C98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c991 Hangul Syllable-C991 | c990 Hangul Syllable-C990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c993 Hangul Syllable-C993 | c992 Hangul Syllable-C992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c995 Hangul Syllable-C995 | c994 Hangul Syllable-C994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c997 Hangul Syllable-C997 | c996 Hangul Syllable-C996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c999 Hangul Syllable-C999 | c998 Hangul Syllable-C998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99b Hangul Syllable-C99B | c99a Hangul Syllable-C99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99d Hangul Syllable-C99D | c99c Hangul Syllable-C99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c99f Hangul Syllable-C99F | c99e Hangul Syllable-C99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a1 Hangul Syllable-C9A1 | c9a0 Hangul Syllable-C9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a3 Hangul Syllable-C9A3 | c9a2 Hangul Syllable-C9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a5 Hangul Syllable-C9A5 | c9a4 Hangul Syllable-C9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a7 Hangul Syllable-C9A7 | c9a6 Hangul Syllable-C9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9a9 Hangul Syllable-C9A9 | c9a8 Hangul Syllable-C9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ab Hangul Syllable-C9AB | c9aa Hangul Syllable-C9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ad Hangul Syllable-C9AD | c9ac Hangul Syllable-C9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9af Hangul Syllable-C9AF | c9ae Hangul Syllable-C9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b1 Hangul Syllable-C9B1 | c9b0 Hangul Syllable-C9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b3 Hangul Syllable-C9B3 | c9b2 Hangul Syllable-C9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b5 Hangul Syllable-C9B5 | c9b4 Hangul Syllable-C9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b7 Hangul Syllable-C9B7 | c9b6 Hangul Syllable-C9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9b9 Hangul Syllable-C9B9 | c9b8 Hangul Syllable-C9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bb Hangul Syllable-C9BB | c9ba Hangul Syllable-C9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bd Hangul Syllable-C9BD | c9bc Hangul Syllable-C9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9bf Hangul Syllable-C9BF | c9be Hangul Syllable-C9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c1 Hangul Syllable-C9C1 | c9c0 Hangul Syllable-C9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c3 Hangul Syllable-C9C3 | c9c2 Hangul Syllable-C9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c5 Hangul Syllable-C9C5 | c9c4 Hangul Syllable-C9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c7 Hangul Syllable-C9C7 | c9c6 Hangul Syllable-C9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9c9 Hangul Syllable-C9C9 | c9c8 Hangul Syllable-C9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cb Hangul Syllable-C9CB | c9ca Hangul Syllable-C9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cd Hangul Syllable-C9CD | c9cc Hangul Syllable-C9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9cf Hangul Syllable-C9CF | c9ce Hangul Syllable-C9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d1 Hangul Syllable-C9D1 | c9d0 Hangul Syllable-C9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d3 Hangul Syllable-C9D3 | c9d2 Hangul Syllable-C9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d5 Hangul Syllable-C9D5 | c9d4 Hangul Syllable-C9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d7 Hangul Syllable-C9D7 | c9d6 Hangul Syllable-C9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9d9 Hangul Syllable-C9D9 | c9d8 Hangul Syllable-C9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9db Hangul Syllable-C9DB | c9da Hangul Syllable-C9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9dd Hangul Syllable-C9DD | c9dc Hangul Syllable-C9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9df Hangul Syllable-C9DF | c9de Hangul Syllable-C9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e1 Hangul Syllable-C9E1 | c9e0 Hangul Syllable-C9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e3 Hangul Syllable-C9E3 | c9e2 Hangul Syllable-C9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e5 Hangul Syllable-C9E5 | c9e4 Hangul Syllable-C9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e7 Hangul Syllable-C9E7 | c9e6 Hangul Syllable-C9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9e9 Hangul Syllable-C9E9 | c9e8 Hangul Syllable-C9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9eb Hangul Syllable-C9EB | c9ea Hangul Syllable-C9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ed Hangul Syllable-C9ED | c9ec Hangul Syllable-C9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ef Hangul Syllable-C9EF | c9ee Hangul Syllable-C9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f1 Hangul Syllable-C9F1 | c9f0 Hangul Syllable-C9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f3 Hangul Syllable-C9F3 | c9f2 Hangul Syllable-C9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f5 Hangul Syllable-C9F5 | c9f4 Hangul Syllable-C9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f7 Hangul Syllable-C9F7 | c9f6 Hangul Syllable-C9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9f9 Hangul Syllable-C9F9 | c9f8 Hangul Syllable-C9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9fb Hangul Syllable-C9FB | c9fa Hangul Syllable-C9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9fd Hangul Syllable-C9FD | c9fc Hangul Syllable-C9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* c9ff Hangul Syllable-C9FF | c9fe Hangul Syllable-C9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca01 Hangul Syllable-CA01 | ca00 Hangul Syllable-CA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca03 Hangul Syllable-CA03 | ca02 Hangul Syllable-CA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca05 Hangul Syllable-CA05 | ca04 Hangul Syllable-CA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca07 Hangul Syllable-CA07 | ca06 Hangul Syllable-CA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca09 Hangul Syllable-CA09 | ca08 Hangul Syllable-CA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0b Hangul Syllable-CA0B | ca0a Hangul Syllable-CA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0d Hangul Syllable-CA0D | ca0c Hangul Syllable-CA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca0f Hangul Syllable-CA0F | ca0e Hangul Syllable-CA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca11 Hangul Syllable-CA11 | ca10 Hangul Syllable-CA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca13 Hangul Syllable-CA13 | ca12 Hangul Syllable-CA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca15 Hangul Syllable-CA15 | ca14 Hangul Syllable-CA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca17 Hangul Syllable-CA17 | ca16 Hangul Syllable-CA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca19 Hangul Syllable-CA19 | ca18 Hangul Syllable-CA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1b Hangul Syllable-CA1B | ca1a Hangul Syllable-CA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1d Hangul Syllable-CA1D | ca1c Hangul Syllable-CA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca1f Hangul Syllable-CA1F | ca1e Hangul Syllable-CA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca21 Hangul Syllable-CA21 | ca20 Hangul Syllable-CA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca23 Hangul Syllable-CA23 | ca22 Hangul Syllable-CA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca25 Hangul Syllable-CA25 | ca24 Hangul Syllable-CA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca27 Hangul Syllable-CA27 | ca26 Hangul Syllable-CA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca29 Hangul Syllable-CA29 | ca28 Hangul Syllable-CA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2b Hangul Syllable-CA2B | ca2a Hangul Syllable-CA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2d Hangul Syllable-CA2D | ca2c Hangul Syllable-CA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca2f Hangul Syllable-CA2F | ca2e Hangul Syllable-CA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca31 Hangul Syllable-CA31 | ca30 Hangul Syllable-CA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca33 Hangul Syllable-CA33 | ca32 Hangul Syllable-CA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca35 Hangul Syllable-CA35 | ca34 Hangul Syllable-CA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca37 Hangul Syllable-CA37 | ca36 Hangul Syllable-CA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca39 Hangul Syllable-CA39 | ca38 Hangul Syllable-CA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3b Hangul Syllable-CA3B | ca3a Hangul Syllable-CA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3d Hangul Syllable-CA3D | ca3c Hangul Syllable-CA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca3f Hangul Syllable-CA3F | ca3e Hangul Syllable-CA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca41 Hangul Syllable-CA41 | ca40 Hangul Syllable-CA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca43 Hangul Syllable-CA43 | ca42 Hangul Syllable-CA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca45 Hangul Syllable-CA45 | ca44 Hangul Syllable-CA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca47 Hangul Syllable-CA47 | ca46 Hangul Syllable-CA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca49 Hangul Syllable-CA49 | ca48 Hangul Syllable-CA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4b Hangul Syllable-CA4B | ca4a Hangul Syllable-CA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4d Hangul Syllable-CA4D | ca4c Hangul Syllable-CA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca4f Hangul Syllable-CA4F | ca4e Hangul Syllable-CA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca51 Hangul Syllable-CA51 | ca50 Hangul Syllable-CA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca53 Hangul Syllable-CA53 | ca52 Hangul Syllable-CA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca55 Hangul Syllable-CA55 | ca54 Hangul Syllable-CA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca57 Hangul Syllable-CA57 | ca56 Hangul Syllable-CA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca59 Hangul Syllable-CA59 | ca58 Hangul Syllable-CA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5b Hangul Syllable-CA5B | ca5a Hangul Syllable-CA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5d Hangul Syllable-CA5D | ca5c Hangul Syllable-CA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca5f Hangul Syllable-CA5F | ca5e Hangul Syllable-CA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca61 Hangul Syllable-CA61 | ca60 Hangul Syllable-CA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca63 Hangul Syllable-CA63 | ca62 Hangul Syllable-CA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca65 Hangul Syllable-CA65 | ca64 Hangul Syllable-CA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca67 Hangul Syllable-CA67 | ca66 Hangul Syllable-CA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca69 Hangul Syllable-CA69 | ca68 Hangul Syllable-CA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6b Hangul Syllable-CA6B | ca6a Hangul Syllable-CA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6d Hangul Syllable-CA6D | ca6c Hangul Syllable-CA6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca6f Hangul Syllable-CA6F | ca6e Hangul Syllable-CA6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca71 Hangul Syllable-CA71 | ca70 Hangul Syllable-CA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca73 Hangul Syllable-CA73 | ca72 Hangul Syllable-CA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca75 Hangul Syllable-CA75 | ca74 Hangul Syllable-CA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca77 Hangul Syllable-CA77 | ca76 Hangul Syllable-CA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca79 Hangul Syllable-CA79 | ca78 Hangul Syllable-CA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7b Hangul Syllable-CA7B | ca7a Hangul Syllable-CA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7d Hangul Syllable-CA7D | ca7c Hangul Syllable-CA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca7f Hangul Syllable-CA7F | ca7e Hangul Syllable-CA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca81 Hangul Syllable-CA81 | ca80 Hangul Syllable-CA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca83 Hangul Syllable-CA83 | ca82 Hangul Syllable-CA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca85 Hangul Syllable-CA85 | ca84 Hangul Syllable-CA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca87 Hangul Syllable-CA87 | ca86 Hangul Syllable-CA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca89 Hangul Syllable-CA89 | ca88 Hangul Syllable-CA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8b Hangul Syllable-CA8B | ca8a Hangul Syllable-CA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8d Hangul Syllable-CA8D | ca8c Hangul Syllable-CA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca8f Hangul Syllable-CA8F | ca8e Hangul Syllable-CA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca91 Hangul Syllable-CA91 | ca90 Hangul Syllable-CA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca93 Hangul Syllable-CA93 | ca92 Hangul Syllable-CA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca95 Hangul Syllable-CA95 | ca94 Hangul Syllable-CA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca97 Hangul Syllable-CA97 | ca96 Hangul Syllable-CA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca99 Hangul Syllable-CA99 | ca98 Hangul Syllable-CA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9b Hangul Syllable-CA9B | ca9a Hangul Syllable-CA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9d Hangul Syllable-CA9D | ca9c Hangul Syllable-CA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ca9f Hangul Syllable-CA9F | ca9e Hangul Syllable-CA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa1 Hangul Syllable-CAA1 | caa0 Hangul Syllable-CAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa3 Hangul Syllable-CAA3 | caa2 Hangul Syllable-CAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa5 Hangul Syllable-CAA5 | caa4 Hangul Syllable-CAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa7 Hangul Syllable-CAA7 | caa6 Hangul Syllable-CAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caa9 Hangul Syllable-CAA9 | caa8 Hangul Syllable-CAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caab Hangul Syllable-CAAB | caaa Hangul Syllable-CAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caad Hangul Syllable-CAAD | caac Hangul Syllable-CAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caaf Hangul Syllable-CAAF | caae Hangul Syllable-CAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab1 Hangul Syllable-CAB1 | cab0 Hangul Syllable-CAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab3 Hangul Syllable-CAB3 | cab2 Hangul Syllable-CAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab5 Hangul Syllable-CAB5 | cab4 Hangul Syllable-CAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab7 Hangul Syllable-CAB7 | cab6 Hangul Syllable-CAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cab9 Hangul Syllable-CAB9 | cab8 Hangul Syllable-CAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabb Hangul Syllable-CABB | caba Hangul Syllable-CABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabd Hangul Syllable-CABD | cabc Hangul Syllable-CABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cabf Hangul Syllable-CABF | cabe Hangul Syllable-CABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac1 Hangul Syllable-CAC1 | cac0 Hangul Syllable-CAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac3 Hangul Syllable-CAC3 | cac2 Hangul Syllable-CAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac5 Hangul Syllable-CAC5 | cac4 Hangul Syllable-CAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac7 Hangul Syllable-CAC7 | cac6 Hangul Syllable-CAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cac9 Hangul Syllable-CAC9 | cac8 Hangul Syllable-CAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacb Hangul Syllable-CACB | caca Hangul Syllable-CACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacd Hangul Syllable-CACD | cacc Hangul Syllable-CACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cacf Hangul Syllable-CACF | cace Hangul Syllable-CACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad1 Hangul Syllable-CAD1 | cad0 Hangul Syllable-CAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad3 Hangul Syllable-CAD3 | cad2 Hangul Syllable-CAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad5 Hangul Syllable-CAD5 | cad4 Hangul Syllable-CAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad7 Hangul Syllable-CAD7 | cad6 Hangul Syllable-CAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cad9 Hangul Syllable-CAD9 | cad8 Hangul Syllable-CAD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadb Hangul Syllable-CADB | cada Hangul Syllable-CADA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadd Hangul Syllable-CADD | cadc Hangul Syllable-CADC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cadf Hangul Syllable-CADF | cade Hangul Syllable-CADE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae1 Hangul Syllable-CAE1 | cae0 Hangul Syllable-CAE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae3 Hangul Syllable-CAE3 | cae2 Hangul Syllable-CAE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae5 Hangul Syllable-CAE5 | cae4 Hangul Syllable-CAE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae7 Hangul Syllable-CAE7 | cae6 Hangul Syllable-CAE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cae9 Hangul Syllable-CAE9 | cae8 Hangul Syllable-CAE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caeb Hangul Syllable-CAEB | caea Hangul Syllable-CAEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caed Hangul Syllable-CAED | caec Hangul Syllable-CAEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caef Hangul Syllable-CAEF | caee Hangul Syllable-CAEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf1 Hangul Syllable-CAF1 | caf0 Hangul Syllable-CAF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf3 Hangul Syllable-CAF3 | caf2 Hangul Syllable-CAF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf5 Hangul Syllable-CAF5 | caf4 Hangul Syllable-CAF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf7 Hangul Syllable-CAF7 | caf6 Hangul Syllable-CAF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caf9 Hangul Syllable-CAF9 | caf8 Hangul Syllable-CAF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cafb Hangul Syllable-CAFB | cafa Hangul Syllable-CAFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cafd Hangul Syllable-CAFD | cafc Hangul Syllable-CAFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* caff Hangul Syllable-CAFF | cafe Hangul Syllable-CAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb01 Hangul Syllable-CB01 | cb00 Hangul Syllable-CB00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb03 Hangul Syllable-CB03 | cb02 Hangul Syllable-CB02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb05 Hangul Syllable-CB05 | cb04 Hangul Syllable-CB04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb07 Hangul Syllable-CB07 | cb06 Hangul Syllable-CB06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb09 Hangul Syllable-CB09 | cb08 Hangul Syllable-CB08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0b Hangul Syllable-CB0B | cb0a Hangul Syllable-CB0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0d Hangul Syllable-CB0D | cb0c Hangul Syllable-CB0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb0f Hangul Syllable-CB0F | cb0e Hangul Syllable-CB0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb11 Hangul Syllable-CB11 | cb10 Hangul Syllable-CB10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb13 Hangul Syllable-CB13 | cb12 Hangul Syllable-CB12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb15 Hangul Syllable-CB15 | cb14 Hangul Syllable-CB14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb17 Hangul Syllable-CB17 | cb16 Hangul Syllable-CB16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb19 Hangul Syllable-CB19 | cb18 Hangul Syllable-CB18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1b Hangul Syllable-CB1B | cb1a Hangul Syllable-CB1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1d Hangul Syllable-CB1D | cb1c Hangul Syllable-CB1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb1f Hangul Syllable-CB1F | cb1e Hangul Syllable-CB1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb21 Hangul Syllable-CB21 | cb20 Hangul Syllable-CB20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb23 Hangul Syllable-CB23 | cb22 Hangul Syllable-CB22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb25 Hangul Syllable-CB25 | cb24 Hangul Syllable-CB24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb27 Hangul Syllable-CB27 | cb26 Hangul Syllable-CB26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb29 Hangul Syllable-CB29 | cb28 Hangul Syllable-CB28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2b Hangul Syllable-CB2B | cb2a Hangul Syllable-CB2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2d Hangul Syllable-CB2D | cb2c Hangul Syllable-CB2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb2f Hangul Syllable-CB2F | cb2e Hangul Syllable-CB2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb31 Hangul Syllable-CB31 | cb30 Hangul Syllable-CB30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb33 Hangul Syllable-CB33 | cb32 Hangul Syllable-CB32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb35 Hangul Syllable-CB35 | cb34 Hangul Syllable-CB34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb37 Hangul Syllable-CB37 | cb36 Hangul Syllable-CB36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb39 Hangul Syllable-CB39 | cb38 Hangul Syllable-CB38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3b Hangul Syllable-CB3B | cb3a Hangul Syllable-CB3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3d Hangul Syllable-CB3D | cb3c Hangul Syllable-CB3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb3f Hangul Syllable-CB3F | cb3e Hangul Syllable-CB3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb41 Hangul Syllable-CB41 | cb40 Hangul Syllable-CB40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb43 Hangul Syllable-CB43 | cb42 Hangul Syllable-CB42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb45 Hangul Syllable-CB45 | cb44 Hangul Syllable-CB44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb47 Hangul Syllable-CB47 | cb46 Hangul Syllable-CB46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb49 Hangul Syllable-CB49 | cb48 Hangul Syllable-CB48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4b Hangul Syllable-CB4B | cb4a Hangul Syllable-CB4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4d Hangul Syllable-CB4D | cb4c Hangul Syllable-CB4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb4f Hangul Syllable-CB4F | cb4e Hangul Syllable-CB4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb51 Hangul Syllable-CB51 | cb50 Hangul Syllable-CB50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb53 Hangul Syllable-CB53 | cb52 Hangul Syllable-CB52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb55 Hangul Syllable-CB55 | cb54 Hangul Syllable-CB54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb57 Hangul Syllable-CB57 | cb56 Hangul Syllable-CB56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb59 Hangul Syllable-CB59 | cb58 Hangul Syllable-CB58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5b Hangul Syllable-CB5B | cb5a Hangul Syllable-CB5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5d Hangul Syllable-CB5D | cb5c Hangul Syllable-CB5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb5f Hangul Syllable-CB5F | cb5e Hangul Syllable-CB5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb61 Hangul Syllable-CB61 | cb60 Hangul Syllable-CB60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb63 Hangul Syllable-CB63 | cb62 Hangul Syllable-CB62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb65 Hangul Syllable-CB65 | cb64 Hangul Syllable-CB64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb67 Hangul Syllable-CB67 | cb66 Hangul Syllable-CB66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb69 Hangul Syllable-CB69 | cb68 Hangul Syllable-CB68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6b Hangul Syllable-CB6B | cb6a Hangul Syllable-CB6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6d Hangul Syllable-CB6D | cb6c Hangul Syllable-CB6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb6f Hangul Syllable-CB6F | cb6e Hangul Syllable-CB6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb71 Hangul Syllable-CB71 | cb70 Hangul Syllable-CB70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb73 Hangul Syllable-CB73 | cb72 Hangul Syllable-CB72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb75 Hangul Syllable-CB75 | cb74 Hangul Syllable-CB74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb77 Hangul Syllable-CB77 | cb76 Hangul Syllable-CB76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb79 Hangul Syllable-CB79 | cb78 Hangul Syllable-CB78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7b Hangul Syllable-CB7B | cb7a Hangul Syllable-CB7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7d Hangul Syllable-CB7D | cb7c Hangul Syllable-CB7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb7f Hangul Syllable-CB7F | cb7e Hangul Syllable-CB7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb81 Hangul Syllable-CB81 | cb80 Hangul Syllable-CB80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb83 Hangul Syllable-CB83 | cb82 Hangul Syllable-CB82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb85 Hangul Syllable-CB85 | cb84 Hangul Syllable-CB84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb87 Hangul Syllable-CB87 | cb86 Hangul Syllable-CB86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb89 Hangul Syllable-CB89 | cb88 Hangul Syllable-CB88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8b Hangul Syllable-CB8B | cb8a Hangul Syllable-CB8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8d Hangul Syllable-CB8D | cb8c Hangul Syllable-CB8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb8f Hangul Syllable-CB8F | cb8e Hangul Syllable-CB8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb91 Hangul Syllable-CB91 | cb90 Hangul Syllable-CB90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb93 Hangul Syllable-CB93 | cb92 Hangul Syllable-CB92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb95 Hangul Syllable-CB95 | cb94 Hangul Syllable-CB94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb97 Hangul Syllable-CB97 | cb96 Hangul Syllable-CB96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb99 Hangul Syllable-CB99 | cb98 Hangul Syllable-CB98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9b Hangul Syllable-CB9B | cb9a Hangul Syllable-CB9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9d Hangul Syllable-CB9D | cb9c Hangul Syllable-CB9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cb9f Hangul Syllable-CB9F | cb9e Hangul Syllable-CB9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba1 Hangul Syllable-CBA1 | cba0 Hangul Syllable-CBA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba3 Hangul Syllable-CBA3 | cba2 Hangul Syllable-CBA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba5 Hangul Syllable-CBA5 | cba4 Hangul Syllable-CBA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba7 Hangul Syllable-CBA7 | cba6 Hangul Syllable-CBA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cba9 Hangul Syllable-CBA9 | cba8 Hangul Syllable-CBA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbab Hangul Syllable-CBAB | cbaa Hangul Syllable-CBAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbad Hangul Syllable-CBAD | cbac Hangul Syllable-CBAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbaf Hangul Syllable-CBAF | cbae Hangul Syllable-CBAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb1 Hangul Syllable-CBB1 | cbb0 Hangul Syllable-CBB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb3 Hangul Syllable-CBB3 | cbb2 Hangul Syllable-CBB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb5 Hangul Syllable-CBB5 | cbb4 Hangul Syllable-CBB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb7 Hangul Syllable-CBB7 | cbb6 Hangul Syllable-CBB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbb9 Hangul Syllable-CBB9 | cbb8 Hangul Syllable-CBB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbb Hangul Syllable-CBBB | cbba Hangul Syllable-CBBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbd Hangul Syllable-CBBD | cbbc Hangul Syllable-CBBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbbf Hangul Syllable-CBBF | cbbe Hangul Syllable-CBBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc1 Hangul Syllable-CBC1 | cbc0 Hangul Syllable-CBC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc3 Hangul Syllable-CBC3 | cbc2 Hangul Syllable-CBC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc5 Hangul Syllable-CBC5 | cbc4 Hangul Syllable-CBC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc7 Hangul Syllable-CBC7 | cbc6 Hangul Syllable-CBC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbc9 Hangul Syllable-CBC9 | cbc8 Hangul Syllable-CBC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcb Hangul Syllable-CBCB | cbca Hangul Syllable-CBCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcd Hangul Syllable-CBCD | cbcc Hangul Syllable-CBCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbcf Hangul Syllable-CBCF | cbce Hangul Syllable-CBCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd1 Hangul Syllable-CBD1 | cbd0 Hangul Syllable-CBD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd3 Hangul Syllable-CBD3 | cbd2 Hangul Syllable-CBD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd5 Hangul Syllable-CBD5 | cbd4 Hangul Syllable-CBD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd7 Hangul Syllable-CBD7 | cbd6 Hangul Syllable-CBD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbd9 Hangul Syllable-CBD9 | cbd8 Hangul Syllable-CBD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdb Hangul Syllable-CBDB | cbda Hangul Syllable-CBDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdd Hangul Syllable-CBDD | cbdc Hangul Syllable-CBDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbdf Hangul Syllable-CBDF | cbde Hangul Syllable-CBDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe1 Hangul Syllable-CBE1 | cbe0 Hangul Syllable-CBE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe3 Hangul Syllable-CBE3 | cbe2 Hangul Syllable-CBE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe5 Hangul Syllable-CBE5 | cbe4 Hangul Syllable-CBE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe7 Hangul Syllable-CBE7 | cbe6 Hangul Syllable-CBE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbe9 Hangul Syllable-CBE9 | cbe8 Hangul Syllable-CBE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbeb Hangul Syllable-CBEB | cbea Hangul Syllable-CBEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbed Hangul Syllable-CBED | cbec Hangul Syllable-CBEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbef Hangul Syllable-CBEF | cbee Hangul Syllable-CBEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf1 Hangul Syllable-CBF1 | cbf0 Hangul Syllable-CBF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf3 Hangul Syllable-CBF3 | cbf2 Hangul Syllable-CBF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf5 Hangul Syllable-CBF5 | cbf4 Hangul Syllable-CBF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf7 Hangul Syllable-CBF7 | cbf6 Hangul Syllable-CBF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbf9 Hangul Syllable-CBF9 | cbf8 Hangul Syllable-CBF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbfb Hangul Syllable-CBFB | cbfa Hangul Syllable-CBFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbfd Hangul Syllable-CBFD | cbfc Hangul Syllable-CBFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cbff Hangul Syllable-CBFF | cbfe Hangul Syllable-CBFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc01 Hangul Syllable-CC01 | cc00 Hangul Syllable-CC00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc03 Hangul Syllable-CC03 | cc02 Hangul Syllable-CC02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc05 Hangul Syllable-CC05 | cc04 Hangul Syllable-CC04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc07 Hangul Syllable-CC07 | cc06 Hangul Syllable-CC06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc09 Hangul Syllable-CC09 | cc08 Hangul Syllable-CC08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0b Hangul Syllable-CC0B | cc0a Hangul Syllable-CC0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0d Hangul Syllable-CC0D | cc0c Hangul Syllable-CC0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc0f Hangul Syllable-CC0F | cc0e Hangul Syllable-CC0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc11 Hangul Syllable-CC11 | cc10 Hangul Syllable-CC10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc13 Hangul Syllable-CC13 | cc12 Hangul Syllable-CC12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc15 Hangul Syllable-CC15 | cc14 Hangul Syllable-CC14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc17 Hangul Syllable-CC17 | cc16 Hangul Syllable-CC16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc19 Hangul Syllable-CC19 | cc18 Hangul Syllable-CC18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1b Hangul Syllable-CC1B | cc1a Hangul Syllable-CC1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1d Hangul Syllable-CC1D | cc1c Hangul Syllable-CC1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc1f Hangul Syllable-CC1F | cc1e Hangul Syllable-CC1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc21 Hangul Syllable-CC21 | cc20 Hangul Syllable-CC20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc23 Hangul Syllable-CC23 | cc22 Hangul Syllable-CC22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc25 Hangul Syllable-CC25 | cc24 Hangul Syllable-CC24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc27 Hangul Syllable-CC27 | cc26 Hangul Syllable-CC26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc29 Hangul Syllable-CC29 | cc28 Hangul Syllable-CC28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2b Hangul Syllable-CC2B | cc2a Hangul Syllable-CC2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2d Hangul Syllable-CC2D | cc2c Hangul Syllable-CC2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc2f Hangul Syllable-CC2F | cc2e Hangul Syllable-CC2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc31 Hangul Syllable-CC31 | cc30 Hangul Syllable-CC30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc33 Hangul Syllable-CC33 | cc32 Hangul Syllable-CC32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc35 Hangul Syllable-CC35 | cc34 Hangul Syllable-CC34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc37 Hangul Syllable-CC37 | cc36 Hangul Syllable-CC36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc39 Hangul Syllable-CC39 | cc38 Hangul Syllable-CC38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3b Hangul Syllable-CC3B | cc3a Hangul Syllable-CC3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3d Hangul Syllable-CC3D | cc3c Hangul Syllable-CC3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc3f Hangul Syllable-CC3F | cc3e Hangul Syllable-CC3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc41 Hangul Syllable-CC41 | cc40 Hangul Syllable-CC40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc43 Hangul Syllable-CC43 | cc42 Hangul Syllable-CC42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc45 Hangul Syllable-CC45 | cc44 Hangul Syllable-CC44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc47 Hangul Syllable-CC47 | cc46 Hangul Syllable-CC46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc49 Hangul Syllable-CC49 | cc48 Hangul Syllable-CC48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4b Hangul Syllable-CC4B | cc4a Hangul Syllable-CC4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4d Hangul Syllable-CC4D | cc4c Hangul Syllable-CC4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc4f Hangul Syllable-CC4F | cc4e Hangul Syllable-CC4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc51 Hangul Syllable-CC51 | cc50 Hangul Syllable-CC50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc53 Hangul Syllable-CC53 | cc52 Hangul Syllable-CC52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc55 Hangul Syllable-CC55 | cc54 Hangul Syllable-CC54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc57 Hangul Syllable-CC57 | cc56 Hangul Syllable-CC56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc59 Hangul Syllable-CC59 | cc58 Hangul Syllable-CC58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5b Hangul Syllable-CC5B | cc5a Hangul Syllable-CC5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5d Hangul Syllable-CC5D | cc5c Hangul Syllable-CC5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc5f Hangul Syllable-CC5F | cc5e Hangul Syllable-CC5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc61 Hangul Syllable-CC61 | cc60 Hangul Syllable-CC60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc63 Hangul Syllable-CC63 | cc62 Hangul Syllable-CC62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc65 Hangul Syllable-CC65 | cc64 Hangul Syllable-CC64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc67 Hangul Syllable-CC67 | cc66 Hangul Syllable-CC66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc69 Hangul Syllable-CC69 | cc68 Hangul Syllable-CC68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6b Hangul Syllable-CC6B | cc6a Hangul Syllable-CC6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6d Hangul Syllable-CC6D | cc6c Hangul Syllable-CC6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc6f Hangul Syllable-CC6F | cc6e Hangul Syllable-CC6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc71 Hangul Syllable-CC71 | cc70 Hangul Syllable-CC70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc73 Hangul Syllable-CC73 | cc72 Hangul Syllable-CC72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc75 Hangul Syllable-CC75 | cc74 Hangul Syllable-CC74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc77 Hangul Syllable-CC77 | cc76 Hangul Syllable-CC76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc79 Hangul Syllable-CC79 | cc78 Hangul Syllable-CC78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7b Hangul Syllable-CC7B | cc7a Hangul Syllable-CC7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7d Hangul Syllable-CC7D | cc7c Hangul Syllable-CC7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc7f Hangul Syllable-CC7F | cc7e Hangul Syllable-CC7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc81 Hangul Syllable-CC81 | cc80 Hangul Syllable-CC80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc83 Hangul Syllable-CC83 | cc82 Hangul Syllable-CC82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc85 Hangul Syllable-CC85 | cc84 Hangul Syllable-CC84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc87 Hangul Syllable-CC87 | cc86 Hangul Syllable-CC86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc89 Hangul Syllable-CC89 | cc88 Hangul Syllable-CC88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8b Hangul Syllable-CC8B | cc8a Hangul Syllable-CC8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8d Hangul Syllable-CC8D | cc8c Hangul Syllable-CC8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc8f Hangul Syllable-CC8F | cc8e Hangul Syllable-CC8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc91 Hangul Syllable-CC91 | cc90 Hangul Syllable-CC90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc93 Hangul Syllable-CC93 | cc92 Hangul Syllable-CC92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc95 Hangul Syllable-CC95 | cc94 Hangul Syllable-CC94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc97 Hangul Syllable-CC97 | cc96 Hangul Syllable-CC96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc99 Hangul Syllable-CC99 | cc98 Hangul Syllable-CC98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9b Hangul Syllable-CC9B | cc9a Hangul Syllable-CC9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9d Hangul Syllable-CC9D | cc9c Hangul Syllable-CC9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cc9f Hangul Syllable-CC9F | cc9e Hangul Syllable-CC9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca1 Hangul Syllable-CCA1 | cca0 Hangul Syllable-CCA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca3 Hangul Syllable-CCA3 | cca2 Hangul Syllable-CCA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca5 Hangul Syllable-CCA5 | cca4 Hangul Syllable-CCA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca7 Hangul Syllable-CCA7 | cca6 Hangul Syllable-CCA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cca9 Hangul Syllable-CCA9 | cca8 Hangul Syllable-CCA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccab Hangul Syllable-CCAB | ccaa Hangul Syllable-CCAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccad Hangul Syllable-CCAD | ccac Hangul Syllable-CCAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccaf Hangul Syllable-CCAF | ccae Hangul Syllable-CCAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb1 Hangul Syllable-CCB1 | ccb0 Hangul Syllable-CCB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb3 Hangul Syllable-CCB3 | ccb2 Hangul Syllable-CCB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb5 Hangul Syllable-CCB5 | ccb4 Hangul Syllable-CCB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb7 Hangul Syllable-CCB7 | ccb6 Hangul Syllable-CCB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccb9 Hangul Syllable-CCB9 | ccb8 Hangul Syllable-CCB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbb Hangul Syllable-CCBB | ccba Hangul Syllable-CCBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbd Hangul Syllable-CCBD | ccbc Hangul Syllable-CCBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccbf Hangul Syllable-CCBF | ccbe Hangul Syllable-CCBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc1 Hangul Syllable-CCC1 | ccc0 Hangul Syllable-CCC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc3 Hangul Syllable-CCC3 | ccc2 Hangul Syllable-CCC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc5 Hangul Syllable-CCC5 | ccc4 Hangul Syllable-CCC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc7 Hangul Syllable-CCC7 | ccc6 Hangul Syllable-CCC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccc9 Hangul Syllable-CCC9 | ccc8 Hangul Syllable-CCC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccb Hangul Syllable-CCCB | ccca Hangul Syllable-CCCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccd Hangul Syllable-CCCD | cccc Hangul Syllable-CCCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cccf Hangul Syllable-CCCF | ccce Hangul Syllable-CCCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd1 Hangul Syllable-CCD1 | ccd0 Hangul Syllable-CCD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd3 Hangul Syllable-CCD3 | ccd2 Hangul Syllable-CCD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd5 Hangul Syllable-CCD5 | ccd4 Hangul Syllable-CCD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd7 Hangul Syllable-CCD7 | ccd6 Hangul Syllable-CCD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccd9 Hangul Syllable-CCD9 | ccd8 Hangul Syllable-CCD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdb Hangul Syllable-CCDB | ccda Hangul Syllable-CCDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdd Hangul Syllable-CCDD | ccdc Hangul Syllable-CCDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccdf Hangul Syllable-CCDF | ccde Hangul Syllable-CCDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce1 Hangul Syllable-CCE1 | cce0 Hangul Syllable-CCE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce3 Hangul Syllable-CCE3 | cce2 Hangul Syllable-CCE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce5 Hangul Syllable-CCE5 | cce4 Hangul Syllable-CCE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce7 Hangul Syllable-CCE7 | cce6 Hangul Syllable-CCE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cce9 Hangul Syllable-CCE9 | cce8 Hangul Syllable-CCE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cceb Hangul Syllable-CCEB | ccea Hangul Syllable-CCEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cced Hangul Syllable-CCED | ccec Hangul Syllable-CCEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccef Hangul Syllable-CCEF | ccee Hangul Syllable-CCEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf1 Hangul Syllable-CCF1 | ccf0 Hangul Syllable-CCF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf3 Hangul Syllable-CCF3 | ccf2 Hangul Syllable-CCF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf5 Hangul Syllable-CCF5 | ccf4 Hangul Syllable-CCF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf7 Hangul Syllable-CCF7 | ccf6 Hangul Syllable-CCF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccf9 Hangul Syllable-CCF9 | ccf8 Hangul Syllable-CCF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccfb Hangul Syllable-CCFB | ccfa Hangul Syllable-CCFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccfd Hangul Syllable-CCFD | ccfc Hangul Syllable-CCFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ccff Hangul Syllable-CCFF | ccfe Hangul Syllable-CCFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd01 Hangul Syllable-CD01 | cd00 Hangul Syllable-CD00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd03 Hangul Syllable-CD03 | cd02 Hangul Syllable-CD02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd05 Hangul Syllable-CD05 | cd04 Hangul Syllable-CD04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd07 Hangul Syllable-CD07 | cd06 Hangul Syllable-CD06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd09 Hangul Syllable-CD09 | cd08 Hangul Syllable-CD08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0b Hangul Syllable-CD0B | cd0a Hangul Syllable-CD0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0d Hangul Syllable-CD0D | cd0c Hangul Syllable-CD0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd0f Hangul Syllable-CD0F | cd0e Hangul Syllable-CD0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd11 Hangul Syllable-CD11 | cd10 Hangul Syllable-CD10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd13 Hangul Syllable-CD13 | cd12 Hangul Syllable-CD12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd15 Hangul Syllable-CD15 | cd14 Hangul Syllable-CD14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd17 Hangul Syllable-CD17 | cd16 Hangul Syllable-CD16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd19 Hangul Syllable-CD19 | cd18 Hangul Syllable-CD18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1b Hangul Syllable-CD1B | cd1a Hangul Syllable-CD1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1d Hangul Syllable-CD1D | cd1c Hangul Syllable-CD1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd1f Hangul Syllable-CD1F | cd1e Hangul Syllable-CD1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd21 Hangul Syllable-CD21 | cd20 Hangul Syllable-CD20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd23 Hangul Syllable-CD23 | cd22 Hangul Syllable-CD22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd25 Hangul Syllable-CD25 | cd24 Hangul Syllable-CD24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd27 Hangul Syllable-CD27 | cd26 Hangul Syllable-CD26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd29 Hangul Syllable-CD29 | cd28 Hangul Syllable-CD28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2b Hangul Syllable-CD2B | cd2a Hangul Syllable-CD2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2d Hangul Syllable-CD2D | cd2c Hangul Syllable-CD2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd2f Hangul Syllable-CD2F | cd2e Hangul Syllable-CD2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd31 Hangul Syllable-CD31 | cd30 Hangul Syllable-CD30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd33 Hangul Syllable-CD33 | cd32 Hangul Syllable-CD32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd35 Hangul Syllable-CD35 | cd34 Hangul Syllable-CD34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd37 Hangul Syllable-CD37 | cd36 Hangul Syllable-CD36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd39 Hangul Syllable-CD39 | cd38 Hangul Syllable-CD38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3b Hangul Syllable-CD3B | cd3a Hangul Syllable-CD3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3d Hangul Syllable-CD3D | cd3c Hangul Syllable-CD3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd3f Hangul Syllable-CD3F | cd3e Hangul Syllable-CD3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd41 Hangul Syllable-CD41 | cd40 Hangul Syllable-CD40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd43 Hangul Syllable-CD43 | cd42 Hangul Syllable-CD42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd45 Hangul Syllable-CD45 | cd44 Hangul Syllable-CD44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd47 Hangul Syllable-CD47 | cd46 Hangul Syllable-CD46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd49 Hangul Syllable-CD49 | cd48 Hangul Syllable-CD48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4b Hangul Syllable-CD4B | cd4a Hangul Syllable-CD4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4d Hangul Syllable-CD4D | cd4c Hangul Syllable-CD4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd4f Hangul Syllable-CD4F | cd4e Hangul Syllable-CD4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd51 Hangul Syllable-CD51 | cd50 Hangul Syllable-CD50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd53 Hangul Syllable-CD53 | cd52 Hangul Syllable-CD52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd55 Hangul Syllable-CD55 | cd54 Hangul Syllable-CD54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd57 Hangul Syllable-CD57 | cd56 Hangul Syllable-CD56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd59 Hangul Syllable-CD59 | cd58 Hangul Syllable-CD58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5b Hangul Syllable-CD5B | cd5a Hangul Syllable-CD5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5d Hangul Syllable-CD5D | cd5c Hangul Syllable-CD5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd5f Hangul Syllable-CD5F | cd5e Hangul Syllable-CD5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd61 Hangul Syllable-CD61 | cd60 Hangul Syllable-CD60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd63 Hangul Syllable-CD63 | cd62 Hangul Syllable-CD62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd65 Hangul Syllable-CD65 | cd64 Hangul Syllable-CD64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd67 Hangul Syllable-CD67 | cd66 Hangul Syllable-CD66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd69 Hangul Syllable-CD69 | cd68 Hangul Syllable-CD68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6b Hangul Syllable-CD6B | cd6a Hangul Syllable-CD6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6d Hangul Syllable-CD6D | cd6c Hangul Syllable-CD6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd6f Hangul Syllable-CD6F | cd6e Hangul Syllable-CD6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd71 Hangul Syllable-CD71 | cd70 Hangul Syllable-CD70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd73 Hangul Syllable-CD73 | cd72 Hangul Syllable-CD72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd75 Hangul Syllable-CD75 | cd74 Hangul Syllable-CD74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd77 Hangul Syllable-CD77 | cd76 Hangul Syllable-CD76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd79 Hangul Syllable-CD79 | cd78 Hangul Syllable-CD78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7b Hangul Syllable-CD7B | cd7a Hangul Syllable-CD7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7d Hangul Syllable-CD7D | cd7c Hangul Syllable-CD7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd7f Hangul Syllable-CD7F | cd7e Hangul Syllable-CD7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd81 Hangul Syllable-CD81 | cd80 Hangul Syllable-CD80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd83 Hangul Syllable-CD83 | cd82 Hangul Syllable-CD82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd85 Hangul Syllable-CD85 | cd84 Hangul Syllable-CD84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd87 Hangul Syllable-CD87 | cd86 Hangul Syllable-CD86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd89 Hangul Syllable-CD89 | cd88 Hangul Syllable-CD88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8b Hangul Syllable-CD8B | cd8a Hangul Syllable-CD8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8d Hangul Syllable-CD8D | cd8c Hangul Syllable-CD8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd8f Hangul Syllable-CD8F | cd8e Hangul Syllable-CD8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd91 Hangul Syllable-CD91 | cd90 Hangul Syllable-CD90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd93 Hangul Syllable-CD93 | cd92 Hangul Syllable-CD92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd95 Hangul Syllable-CD95 | cd94 Hangul Syllable-CD94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd97 Hangul Syllable-CD97 | cd96 Hangul Syllable-CD96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd99 Hangul Syllable-CD99 | cd98 Hangul Syllable-CD98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9b Hangul Syllable-CD9B | cd9a Hangul Syllable-CD9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9d Hangul Syllable-CD9D | cd9c Hangul Syllable-CD9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cd9f Hangul Syllable-CD9F | cd9e Hangul Syllable-CD9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda1 Hangul Syllable-CDA1 | cda0 Hangul Syllable-CDA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda3 Hangul Syllable-CDA3 | cda2 Hangul Syllable-CDA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda5 Hangul Syllable-CDA5 | cda4 Hangul Syllable-CDA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda7 Hangul Syllable-CDA7 | cda6 Hangul Syllable-CDA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cda9 Hangul Syllable-CDA9 | cda8 Hangul Syllable-CDA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdab Hangul Syllable-CDAB | cdaa Hangul Syllable-CDAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdad Hangul Syllable-CDAD | cdac Hangul Syllable-CDAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdaf Hangul Syllable-CDAF | cdae Hangul Syllable-CDAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb1 Hangul Syllable-CDB1 | cdb0 Hangul Syllable-CDB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb3 Hangul Syllable-CDB3 | cdb2 Hangul Syllable-CDB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb5 Hangul Syllable-CDB5 | cdb4 Hangul Syllable-CDB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb7 Hangul Syllable-CDB7 | cdb6 Hangul Syllable-CDB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdb9 Hangul Syllable-CDB9 | cdb8 Hangul Syllable-CDB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbb Hangul Syllable-CDBB | cdba Hangul Syllable-CDBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbd Hangul Syllable-CDBD | cdbc Hangul Syllable-CDBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdbf Hangul Syllable-CDBF | cdbe Hangul Syllable-CDBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc1 Hangul Syllable-CDC1 | cdc0 Hangul Syllable-CDC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc3 Hangul Syllable-CDC3 | cdc2 Hangul Syllable-CDC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc5 Hangul Syllable-CDC5 | cdc4 Hangul Syllable-CDC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc7 Hangul Syllable-CDC7 | cdc6 Hangul Syllable-CDC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdc9 Hangul Syllable-CDC9 | cdc8 Hangul Syllable-CDC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcb Hangul Syllable-CDCB | cdca Hangul Syllable-CDCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcd Hangul Syllable-CDCD | cdcc Hangul Syllable-CDCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdcf Hangul Syllable-CDCF | cdce Hangul Syllable-CDCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd1 Hangul Syllable-CDD1 | cdd0 Hangul Syllable-CDD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd3 Hangul Syllable-CDD3 | cdd2 Hangul Syllable-CDD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd5 Hangul Syllable-CDD5 | cdd4 Hangul Syllable-CDD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd7 Hangul Syllable-CDD7 | cdd6 Hangul Syllable-CDD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdd9 Hangul Syllable-CDD9 | cdd8 Hangul Syllable-CDD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddb Hangul Syllable-CDDB | cdda Hangul Syllable-CDDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddd Hangul Syllable-CDDD | cddc Hangul Syllable-CDDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cddf Hangul Syllable-CDDF | cdde Hangul Syllable-CDDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde1 Hangul Syllable-CDE1 | cde0 Hangul Syllable-CDE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde3 Hangul Syllable-CDE3 | cde2 Hangul Syllable-CDE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde5 Hangul Syllable-CDE5 | cde4 Hangul Syllable-CDE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde7 Hangul Syllable-CDE7 | cde6 Hangul Syllable-CDE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cde9 Hangul Syllable-CDE9 | cde8 Hangul Syllable-CDE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdeb Hangul Syllable-CDEB | cdea Hangul Syllable-CDEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cded Hangul Syllable-CDED | cdec Hangul Syllable-CDEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdef Hangul Syllable-CDEF | cdee Hangul Syllable-CDEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf1 Hangul Syllable-CDF1 | cdf0 Hangul Syllable-CDF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf3 Hangul Syllable-CDF3 | cdf2 Hangul Syllable-CDF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf5 Hangul Syllable-CDF5 | cdf4 Hangul Syllable-CDF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf7 Hangul Syllable-CDF7 | cdf6 Hangul Syllable-CDF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdf9 Hangul Syllable-CDF9 | cdf8 Hangul Syllable-CDF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdfb Hangul Syllable-CDFB | cdfa Hangul Syllable-CDFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdfd Hangul Syllable-CDFD | cdfc Hangul Syllable-CDFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cdff Hangul Syllable-CDFF | cdfe Hangul Syllable-CDFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce01 Hangul Syllable-CE01 | ce00 Hangul Syllable-CE00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce03 Hangul Syllable-CE03 | ce02 Hangul Syllable-CE02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce05 Hangul Syllable-CE05 | ce04 Hangul Syllable-CE04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce07 Hangul Syllable-CE07 | ce06 Hangul Syllable-CE06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce09 Hangul Syllable-CE09 | ce08 Hangul Syllable-CE08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0b Hangul Syllable-CE0B | ce0a Hangul Syllable-CE0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0d Hangul Syllable-CE0D | ce0c Hangul Syllable-CE0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce0f Hangul Syllable-CE0F | ce0e Hangul Syllable-CE0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce11 Hangul Syllable-CE11 | ce10 Hangul Syllable-CE10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce13 Hangul Syllable-CE13 | ce12 Hangul Syllable-CE12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce15 Hangul Syllable-CE15 | ce14 Hangul Syllable-CE14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce17 Hangul Syllable-CE17 | ce16 Hangul Syllable-CE16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce19 Hangul Syllable-CE19 | ce18 Hangul Syllable-CE18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1b Hangul Syllable-CE1B | ce1a Hangul Syllable-CE1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1d Hangul Syllable-CE1D | ce1c Hangul Syllable-CE1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce1f Hangul Syllable-CE1F | ce1e Hangul Syllable-CE1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce21 Hangul Syllable-CE21 | ce20 Hangul Syllable-CE20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce23 Hangul Syllable-CE23 | ce22 Hangul Syllable-CE22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce25 Hangul Syllable-CE25 | ce24 Hangul Syllable-CE24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce27 Hangul Syllable-CE27 | ce26 Hangul Syllable-CE26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce29 Hangul Syllable-CE29 | ce28 Hangul Syllable-CE28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2b Hangul Syllable-CE2B | ce2a Hangul Syllable-CE2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2d Hangul Syllable-CE2D | ce2c Hangul Syllable-CE2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce2f Hangul Syllable-CE2F | ce2e Hangul Syllable-CE2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce31 Hangul Syllable-CE31 | ce30 Hangul Syllable-CE30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce33 Hangul Syllable-CE33 | ce32 Hangul Syllable-CE32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce35 Hangul Syllable-CE35 | ce34 Hangul Syllable-CE34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce37 Hangul Syllable-CE37 | ce36 Hangul Syllable-CE36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce39 Hangul Syllable-CE39 | ce38 Hangul Syllable-CE38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3b Hangul Syllable-CE3B | ce3a Hangul Syllable-CE3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3d Hangul Syllable-CE3D | ce3c Hangul Syllable-CE3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce3f Hangul Syllable-CE3F | ce3e Hangul Syllable-CE3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce41 Hangul Syllable-CE41 | ce40 Hangul Syllable-CE40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce43 Hangul Syllable-CE43 | ce42 Hangul Syllable-CE42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce45 Hangul Syllable-CE45 | ce44 Hangul Syllable-CE44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce47 Hangul Syllable-CE47 | ce46 Hangul Syllable-CE46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce49 Hangul Syllable-CE49 | ce48 Hangul Syllable-CE48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4b Hangul Syllable-CE4B | ce4a Hangul Syllable-CE4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4d Hangul Syllable-CE4D | ce4c Hangul Syllable-CE4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce4f Hangul Syllable-CE4F | ce4e Hangul Syllable-CE4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce51 Hangul Syllable-CE51 | ce50 Hangul Syllable-CE50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce53 Hangul Syllable-CE53 | ce52 Hangul Syllable-CE52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce55 Hangul Syllable-CE55 | ce54 Hangul Syllable-CE54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce57 Hangul Syllable-CE57 | ce56 Hangul Syllable-CE56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce59 Hangul Syllable-CE59 | ce58 Hangul Syllable-CE58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5b Hangul Syllable-CE5B | ce5a Hangul Syllable-CE5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5d Hangul Syllable-CE5D | ce5c Hangul Syllable-CE5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce5f Hangul Syllable-CE5F | ce5e Hangul Syllable-CE5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce61 Hangul Syllable-CE61 | ce60 Hangul Syllable-CE60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce63 Hangul Syllable-CE63 | ce62 Hangul Syllable-CE62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce65 Hangul Syllable-CE65 | ce64 Hangul Syllable-CE64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce67 Hangul Syllable-CE67 | ce66 Hangul Syllable-CE66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce69 Hangul Syllable-CE69 | ce68 Hangul Syllable-CE68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6b Hangul Syllable-CE6B | ce6a Hangul Syllable-CE6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6d Hangul Syllable-CE6D | ce6c Hangul Syllable-CE6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce6f Hangul Syllable-CE6F | ce6e Hangul Syllable-CE6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce71 Hangul Syllable-CE71 | ce70 Hangul Syllable-CE70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce73 Hangul Syllable-CE73 | ce72 Hangul Syllable-CE72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce75 Hangul Syllable-CE75 | ce74 Hangul Syllable-CE74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce77 Hangul Syllable-CE77 | ce76 Hangul Syllable-CE76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce79 Hangul Syllable-CE79 | ce78 Hangul Syllable-CE78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7b Hangul Syllable-CE7B | ce7a Hangul Syllable-CE7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7d Hangul Syllable-CE7D | ce7c Hangul Syllable-CE7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce7f Hangul Syllable-CE7F | ce7e Hangul Syllable-CE7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce81 Hangul Syllable-CE81 | ce80 Hangul Syllable-CE80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce83 Hangul Syllable-CE83 | ce82 Hangul Syllable-CE82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce85 Hangul Syllable-CE85 | ce84 Hangul Syllable-CE84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce87 Hangul Syllable-CE87 | ce86 Hangul Syllable-CE86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce89 Hangul Syllable-CE89 | ce88 Hangul Syllable-CE88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8b Hangul Syllable-CE8B | ce8a Hangul Syllable-CE8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8d Hangul Syllable-CE8D | ce8c Hangul Syllable-CE8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce8f Hangul Syllable-CE8F | ce8e Hangul Syllable-CE8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce91 Hangul Syllable-CE91 | ce90 Hangul Syllable-CE90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce93 Hangul Syllable-CE93 | ce92 Hangul Syllable-CE92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce95 Hangul Syllable-CE95 | ce94 Hangul Syllable-CE94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce97 Hangul Syllable-CE97 | ce96 Hangul Syllable-CE96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce99 Hangul Syllable-CE99 | ce98 Hangul Syllable-CE98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9b Hangul Syllable-CE9B | ce9a Hangul Syllable-CE9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9d Hangul Syllable-CE9D | ce9c Hangul Syllable-CE9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ce9f Hangul Syllable-CE9F | ce9e Hangul Syllable-CE9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea1 Hangul Syllable-CEA1 | cea0 Hangul Syllable-CEA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea3 Hangul Syllable-CEA3 | cea2 Hangul Syllable-CEA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea5 Hangul Syllable-CEA5 | cea4 Hangul Syllable-CEA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea7 Hangul Syllable-CEA7 | cea6 Hangul Syllable-CEA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cea9 Hangul Syllable-CEA9 | cea8 Hangul Syllable-CEA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceab Hangul Syllable-CEAB | ceaa Hangul Syllable-CEAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cead Hangul Syllable-CEAD | ceac Hangul Syllable-CEAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceaf Hangul Syllable-CEAF | ceae Hangul Syllable-CEAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb1 Hangul Syllable-CEB1 | ceb0 Hangul Syllable-CEB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb3 Hangul Syllable-CEB3 | ceb2 Hangul Syllable-CEB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb5 Hangul Syllable-CEB5 | ceb4 Hangul Syllable-CEB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb7 Hangul Syllable-CEB7 | ceb6 Hangul Syllable-CEB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceb9 Hangul Syllable-CEB9 | ceb8 Hangul Syllable-CEB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebb Hangul Syllable-CEBB | ceba Hangul Syllable-CEBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebd Hangul Syllable-CEBD | cebc Hangul Syllable-CEBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cebf Hangul Syllable-CEBF | cebe Hangul Syllable-CEBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec1 Hangul Syllable-CEC1 | cec0 Hangul Syllable-CEC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec3 Hangul Syllable-CEC3 | cec2 Hangul Syllable-CEC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec5 Hangul Syllable-CEC5 | cec4 Hangul Syllable-CEC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec7 Hangul Syllable-CEC7 | cec6 Hangul Syllable-CEC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cec9 Hangul Syllable-CEC9 | cec8 Hangul Syllable-CEC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecb Hangul Syllable-CECB | ceca Hangul Syllable-CECA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecd Hangul Syllable-CECD | cecc Hangul Syllable-CECC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cecf Hangul Syllable-CECF | cece Hangul Syllable-CECE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced1 Hangul Syllable-CED1 | ced0 Hangul Syllable-CED0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced3 Hangul Syllable-CED3 | ced2 Hangul Syllable-CED2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced5 Hangul Syllable-CED5 | ced4 Hangul Syllable-CED4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced7 Hangul Syllable-CED7 | ced6 Hangul Syllable-CED6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ced9 Hangul Syllable-CED9 | ced8 Hangul Syllable-CED8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedb Hangul Syllable-CEDB | ceda Hangul Syllable-CEDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedd Hangul Syllable-CEDD | cedc Hangul Syllable-CEDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cedf Hangul Syllable-CEDF | cede Hangul Syllable-CEDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee1 Hangul Syllable-CEE1 | cee0 Hangul Syllable-CEE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee3 Hangul Syllable-CEE3 | cee2 Hangul Syllable-CEE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee5 Hangul Syllable-CEE5 | cee4 Hangul Syllable-CEE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee7 Hangul Syllable-CEE7 | cee6 Hangul Syllable-CEE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cee9 Hangul Syllable-CEE9 | cee8 Hangul Syllable-CEE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceeb Hangul Syllable-CEEB | ceea Hangul Syllable-CEEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceed Hangul Syllable-CEED | ceec Hangul Syllable-CEEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceef Hangul Syllable-CEEF | ceee Hangul Syllable-CEEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef1 Hangul Syllable-CEF1 | cef0 Hangul Syllable-CEF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef3 Hangul Syllable-CEF3 | cef2 Hangul Syllable-CEF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef5 Hangul Syllable-CEF5 | cef4 Hangul Syllable-CEF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef7 Hangul Syllable-CEF7 | cef6 Hangul Syllable-CEF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cef9 Hangul Syllable-CEF9 | cef8 Hangul Syllable-CEF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cefb Hangul Syllable-CEFB | cefa Hangul Syllable-CEFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cefd Hangul Syllable-CEFD | cefc Hangul Syllable-CEFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ceff Hangul Syllable-CEFF | cefe Hangul Syllable-CEFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf01 Hangul Syllable-CF01 | cf00 Hangul Syllable-CF00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf03 Hangul Syllable-CF03 | cf02 Hangul Syllable-CF02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf05 Hangul Syllable-CF05 | cf04 Hangul Syllable-CF04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf07 Hangul Syllable-CF07 | cf06 Hangul Syllable-CF06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf09 Hangul Syllable-CF09 | cf08 Hangul Syllable-CF08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0b Hangul Syllable-CF0B | cf0a Hangul Syllable-CF0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0d Hangul Syllable-CF0D | cf0c Hangul Syllable-CF0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf0f Hangul Syllable-CF0F | cf0e Hangul Syllable-CF0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf11 Hangul Syllable-CF11 | cf10 Hangul Syllable-CF10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf13 Hangul Syllable-CF13 | cf12 Hangul Syllable-CF12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf15 Hangul Syllable-CF15 | cf14 Hangul Syllable-CF14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf17 Hangul Syllable-CF17 | cf16 Hangul Syllable-CF16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf19 Hangul Syllable-CF19 | cf18 Hangul Syllable-CF18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1b Hangul Syllable-CF1B | cf1a Hangul Syllable-CF1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1d Hangul Syllable-CF1D | cf1c Hangul Syllable-CF1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf1f Hangul Syllable-CF1F | cf1e Hangul Syllable-CF1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf21 Hangul Syllable-CF21 | cf20 Hangul Syllable-CF20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf23 Hangul Syllable-CF23 | cf22 Hangul Syllable-CF22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf25 Hangul Syllable-CF25 | cf24 Hangul Syllable-CF24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf27 Hangul Syllable-CF27 | cf26 Hangul Syllable-CF26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf29 Hangul Syllable-CF29 | cf28 Hangul Syllable-CF28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2b Hangul Syllable-CF2B | cf2a Hangul Syllable-CF2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2d Hangul Syllable-CF2D | cf2c Hangul Syllable-CF2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf2f Hangul Syllable-CF2F | cf2e Hangul Syllable-CF2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf31 Hangul Syllable-CF31 | cf30 Hangul Syllable-CF30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf33 Hangul Syllable-CF33 | cf32 Hangul Syllable-CF32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf35 Hangul Syllable-CF35 | cf34 Hangul Syllable-CF34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf37 Hangul Syllable-CF37 | cf36 Hangul Syllable-CF36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf39 Hangul Syllable-CF39 | cf38 Hangul Syllable-CF38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3b Hangul Syllable-CF3B | cf3a Hangul Syllable-CF3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3d Hangul Syllable-CF3D | cf3c Hangul Syllable-CF3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf3f Hangul Syllable-CF3F | cf3e Hangul Syllable-CF3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf41 Hangul Syllable-CF41 | cf40 Hangul Syllable-CF40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf43 Hangul Syllable-CF43 | cf42 Hangul Syllable-CF42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf45 Hangul Syllable-CF45 | cf44 Hangul Syllable-CF44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf47 Hangul Syllable-CF47 | cf46 Hangul Syllable-CF46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf49 Hangul Syllable-CF49 | cf48 Hangul Syllable-CF48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4b Hangul Syllable-CF4B | cf4a Hangul Syllable-CF4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4d Hangul Syllable-CF4D | cf4c Hangul Syllable-CF4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf4f Hangul Syllable-CF4F | cf4e Hangul Syllable-CF4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf51 Hangul Syllable-CF51 | cf50 Hangul Syllable-CF50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf53 Hangul Syllable-CF53 | cf52 Hangul Syllable-CF52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf55 Hangul Syllable-CF55 | cf54 Hangul Syllable-CF54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf57 Hangul Syllable-CF57 | cf56 Hangul Syllable-CF56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf59 Hangul Syllable-CF59 | cf58 Hangul Syllable-CF58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5b Hangul Syllable-CF5B | cf5a Hangul Syllable-CF5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5d Hangul Syllable-CF5D | cf5c Hangul Syllable-CF5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf5f Hangul Syllable-CF5F | cf5e Hangul Syllable-CF5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf61 Hangul Syllable-CF61 | cf60 Hangul Syllable-CF60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf63 Hangul Syllable-CF63 | cf62 Hangul Syllable-CF62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf65 Hangul Syllable-CF65 | cf64 Hangul Syllable-CF64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf67 Hangul Syllable-CF67 | cf66 Hangul Syllable-CF66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf69 Hangul Syllable-CF69 | cf68 Hangul Syllable-CF68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6b Hangul Syllable-CF6B | cf6a Hangul Syllable-CF6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6d Hangul Syllable-CF6D | cf6c Hangul Syllable-CF6C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf6f Hangul Syllable-CF6F | cf6e Hangul Syllable-CF6E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf71 Hangul Syllable-CF71 | cf70 Hangul Syllable-CF70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf73 Hangul Syllable-CF73 | cf72 Hangul Syllable-CF72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf75 Hangul Syllable-CF75 | cf74 Hangul Syllable-CF74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf77 Hangul Syllable-CF77 | cf76 Hangul Syllable-CF76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf79 Hangul Syllable-CF79 | cf78 Hangul Syllable-CF78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7b Hangul Syllable-CF7B | cf7a Hangul Syllable-CF7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7d Hangul Syllable-CF7D | cf7c Hangul Syllable-CF7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf7f Hangul Syllable-CF7F | cf7e Hangul Syllable-CF7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf81 Hangul Syllable-CF81 | cf80 Hangul Syllable-CF80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf83 Hangul Syllable-CF83 | cf82 Hangul Syllable-CF82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf85 Hangul Syllable-CF85 | cf84 Hangul Syllable-CF84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf87 Hangul Syllable-CF87 | cf86 Hangul Syllable-CF86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf89 Hangul Syllable-CF89 | cf88 Hangul Syllable-CF88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8b Hangul Syllable-CF8B | cf8a Hangul Syllable-CF8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8d Hangul Syllable-CF8D | cf8c Hangul Syllable-CF8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf8f Hangul Syllable-CF8F | cf8e Hangul Syllable-CF8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf91 Hangul Syllable-CF91 | cf90 Hangul Syllable-CF90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf93 Hangul Syllable-CF93 | cf92 Hangul Syllable-CF92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf95 Hangul Syllable-CF95 | cf94 Hangul Syllable-CF94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf97 Hangul Syllable-CF97 | cf96 Hangul Syllable-CF96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf99 Hangul Syllable-CF99 | cf98 Hangul Syllable-CF98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9b Hangul Syllable-CF9B | cf9a Hangul Syllable-CF9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9d Hangul Syllable-CF9D | cf9c Hangul Syllable-CF9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cf9f Hangul Syllable-CF9F | cf9e Hangul Syllable-CF9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa1 Hangul Syllable-CFA1 | cfa0 Hangul Syllable-CFA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa3 Hangul Syllable-CFA3 | cfa2 Hangul Syllable-CFA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa5 Hangul Syllable-CFA5 | cfa4 Hangul Syllable-CFA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa7 Hangul Syllable-CFA7 | cfa6 Hangul Syllable-CFA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfa9 Hangul Syllable-CFA9 | cfa8 Hangul Syllable-CFA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfab Hangul Syllable-CFAB | cfaa Hangul Syllable-CFAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfad Hangul Syllable-CFAD | cfac Hangul Syllable-CFAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfaf Hangul Syllable-CFAF | cfae Hangul Syllable-CFAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb1 Hangul Syllable-CFB1 | cfb0 Hangul Syllable-CFB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb3 Hangul Syllable-CFB3 | cfb2 Hangul Syllable-CFB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb5 Hangul Syllable-CFB5 | cfb4 Hangul Syllable-CFB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb7 Hangul Syllable-CFB7 | cfb6 Hangul Syllable-CFB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfb9 Hangul Syllable-CFB9 | cfb8 Hangul Syllable-CFB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbb Hangul Syllable-CFBB | cfba Hangul Syllable-CFBA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbd Hangul Syllable-CFBD | cfbc Hangul Syllable-CFBC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfbf Hangul Syllable-CFBF | cfbe Hangul Syllable-CFBE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc1 Hangul Syllable-CFC1 | cfc0 Hangul Syllable-CFC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc3 Hangul Syllable-CFC3 | cfc2 Hangul Syllable-CFC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc5 Hangul Syllable-CFC5 | cfc4 Hangul Syllable-CFC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc7 Hangul Syllable-CFC7 | cfc6 Hangul Syllable-CFC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfc9 Hangul Syllable-CFC9 | cfc8 Hangul Syllable-CFC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcb Hangul Syllable-CFCB | cfca Hangul Syllable-CFCA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcd Hangul Syllable-CFCD | cfcc Hangul Syllable-CFCC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfcf Hangul Syllable-CFCF | cfce Hangul Syllable-CFCE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd1 Hangul Syllable-CFD1 | cfd0 Hangul Syllable-CFD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd3 Hangul Syllable-CFD3 | cfd2 Hangul Syllable-CFD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd5 Hangul Syllable-CFD5 | cfd4 Hangul Syllable-CFD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd7 Hangul Syllable-CFD7 | cfd6 Hangul Syllable-CFD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfd9 Hangul Syllable-CFD9 | cfd8 Hangul Syllable-CFD8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdb Hangul Syllable-CFDB | cfda Hangul Syllable-CFDA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdd Hangul Syllable-CFDD | cfdc Hangul Syllable-CFDC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfdf Hangul Syllable-CFDF | cfde Hangul Syllable-CFDE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe1 Hangul Syllable-CFE1 | cfe0 Hangul Syllable-CFE0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe3 Hangul Syllable-CFE3 | cfe2 Hangul Syllable-CFE2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe5 Hangul Syllable-CFE5 | cfe4 Hangul Syllable-CFE4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe7 Hangul Syllable-CFE7 | cfe6 Hangul Syllable-CFE6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfe9 Hangul Syllable-CFE9 | cfe8 Hangul Syllable-CFE8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfeb Hangul Syllable-CFEB | cfea Hangul Syllable-CFEA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfed Hangul Syllable-CFED | cfec Hangul Syllable-CFEC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfef Hangul Syllable-CFEF | cfee Hangul Syllable-CFEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff1 Hangul Syllable-CFF1 | cff0 Hangul Syllable-CFF0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff3 Hangul Syllable-CFF3 | cff2 Hangul Syllable-CFF2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff5 Hangul Syllable-CFF5 | cff4 Hangul Syllable-CFF4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff7 Hangul Syllable-CFF7 | cff6 Hangul Syllable-CFF6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cff9 Hangul Syllable-CFF9 | cff8 Hangul Syllable-CFF8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cffb Hangul Syllable-CFFB | cffa Hangul Syllable-CFFA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cffd Hangul Syllable-CFFD | cffc Hangul Syllable-CFFC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* cfff Hangul Syllable-CFFF | cffe Hangul Syllable-CFFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d001 Hangul Syllable-D001 | d000 Hangul Syllable-D000 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d003 Hangul Syllable-D003 | d002 Hangul Syllable-D002 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d005 Hangul Syllable-D005 | d004 Hangul Syllable-D004 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d007 Hangul Syllable-D007 | d006 Hangul Syllable-D006 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d009 Hangul Syllable-D009 | d008 Hangul Syllable-D008 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00b Hangul Syllable-D00B | d00a Hangul Syllable-D00A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00d Hangul Syllable-D00D | d00c Hangul Syllable-D00C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d00f Hangul Syllable-D00F | d00e Hangul Syllable-D00E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d011 Hangul Syllable-D011 | d010 Hangul Syllable-D010 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d013 Hangul Syllable-D013 | d012 Hangul Syllable-D012 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d015 Hangul Syllable-D015 | d014 Hangul Syllable-D014 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d017 Hangul Syllable-D017 | d016 Hangul Syllable-D016 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d019 Hangul Syllable-D019 | d018 Hangul Syllable-D018 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01b Hangul Syllable-D01B | d01a Hangul Syllable-D01A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01d Hangul Syllable-D01D | d01c Hangul Syllable-D01C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d01f Hangul Syllable-D01F | d01e Hangul Syllable-D01E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d021 Hangul Syllable-D021 | d020 Hangul Syllable-D020 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d023 Hangul Syllable-D023 | d022 Hangul Syllable-D022 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d025 Hangul Syllable-D025 | d024 Hangul Syllable-D024 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d027 Hangul Syllable-D027 | d026 Hangul Syllable-D026 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d029 Hangul Syllable-D029 | d028 Hangul Syllable-D028 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02b Hangul Syllable-D02B | d02a Hangul Syllable-D02A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02d Hangul Syllable-D02D | d02c Hangul Syllable-D02C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d02f Hangul Syllable-D02F | d02e Hangul Syllable-D02E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d031 Hangul Syllable-D031 | d030 Hangul Syllable-D030 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d033 Hangul Syllable-D033 | d032 Hangul Syllable-D032 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d035 Hangul Syllable-D035 | d034 Hangul Syllable-D034 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d037 Hangul Syllable-D037 | d036 Hangul Syllable-D036 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d039 Hangul Syllable-D039 | d038 Hangul Syllable-D038 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03b Hangul Syllable-D03B | d03a Hangul Syllable-D03A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03d Hangul Syllable-D03D | d03c Hangul Syllable-D03C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d03f Hangul Syllable-D03F | d03e Hangul Syllable-D03E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d041 Hangul Syllable-D041 | d040 Hangul Syllable-D040 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d043 Hangul Syllable-D043 | d042 Hangul Syllable-D042 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d045 Hangul Syllable-D045 | d044 Hangul Syllable-D044 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d047 Hangul Syllable-D047 | d046 Hangul Syllable-D046 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d049 Hangul Syllable-D049 | d048 Hangul Syllable-D048 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04b Hangul Syllable-D04B | d04a Hangul Syllable-D04A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04d Hangul Syllable-D04D | d04c Hangul Syllable-D04C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d04f Hangul Syllable-D04F | d04e Hangul Syllable-D04E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d051 Hangul Syllable-D051 | d050 Hangul Syllable-D050 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d053 Hangul Syllable-D053 | d052 Hangul Syllable-D052 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d055 Hangul Syllable-D055 | d054 Hangul Syllable-D054 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d057 Hangul Syllable-D057 | d056 Hangul Syllable-D056 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d059 Hangul Syllable-D059 | d058 Hangul Syllable-D058 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05b Hangul Syllable-D05B | d05a Hangul Syllable-D05A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05d Hangul Syllable-D05D | d05c Hangul Syllable-D05C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d05f Hangul Syllable-D05F | d05e Hangul Syllable-D05E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d061 Hangul Syllable-D061 | d060 Hangul Syllable-D060 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d063 Hangul Syllable-D063 | d062 Hangul Syllable-D062 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d065 Hangul Syllable-D065 | d064 Hangul Syllable-D064 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d067 Hangul Syllable-D067 | d066 Hangul Syllable-D066 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d069 Hangul Syllable-D069 | d068 Hangul Syllable-D068 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06b Hangul Syllable-D06B | d06a Hangul Syllable-D06A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06d Hangul Syllable-D06D | d06c Hangul Syllable-D06C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d06f Hangul Syllable-D06F | d06e Hangul Syllable-D06E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d071 Hangul Syllable-D071 | d070 Hangul Syllable-D070 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d073 Hangul Syllable-D073 | d072 Hangul Syllable-D072 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d075 Hangul Syllable-D075 | d074 Hangul Syllable-D074 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d077 Hangul Syllable-D077 | d076 Hangul Syllable-D076 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d079 Hangul Syllable-D079 | d078 Hangul Syllable-D078 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07b Hangul Syllable-D07B | d07a Hangul Syllable-D07A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07d Hangul Syllable-D07D | d07c Hangul Syllable-D07C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d07f Hangul Syllable-D07F | d07e Hangul Syllable-D07E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d081 Hangul Syllable-D081 | d080 Hangul Syllable-D080 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d083 Hangul Syllable-D083 | d082 Hangul Syllable-D082 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d085 Hangul Syllable-D085 | d084 Hangul Syllable-D084 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d087 Hangul Syllable-D087 | d086 Hangul Syllable-D086 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d089 Hangul Syllable-D089 | d088 Hangul Syllable-D088 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08b Hangul Syllable-D08B | d08a Hangul Syllable-D08A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08d Hangul Syllable-D08D | d08c Hangul Syllable-D08C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d08f Hangul Syllable-D08F | d08e Hangul Syllable-D08E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d091 Hangul Syllable-D091 | d090 Hangul Syllable-D090 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d093 Hangul Syllable-D093 | d092 Hangul Syllable-D092 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d095 Hangul Syllable-D095 | d094 Hangul Syllable-D094 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d097 Hangul Syllable-D097 | d096 Hangul Syllable-D096 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d099 Hangul Syllable-D099 | d098 Hangul Syllable-D098 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09b Hangul Syllable-D09B | d09a Hangul Syllable-D09A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09d Hangul Syllable-D09D | d09c Hangul Syllable-D09C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d09f Hangul Syllable-D09F | d09e Hangul Syllable-D09E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a1 Hangul Syllable-D0A1 | d0a0 Hangul Syllable-D0A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a3 Hangul Syllable-D0A3 | d0a2 Hangul Syllable-D0A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a5 Hangul Syllable-D0A5 | d0a4 Hangul Syllable-D0A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a7 Hangul Syllable-D0A7 | d0a6 Hangul Syllable-D0A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0a9 Hangul Syllable-D0A9 | d0a8 Hangul Syllable-D0A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ab Hangul Syllable-D0AB | d0aa Hangul Syllable-D0AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ad Hangul Syllable-D0AD | d0ac Hangul Syllable-D0AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0af Hangul Syllable-D0AF | d0ae Hangul Syllable-D0AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b1 Hangul Syllable-D0B1 | d0b0 Hangul Syllable-D0B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b3 Hangul Syllable-D0B3 | d0b2 Hangul Syllable-D0B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b5 Hangul Syllable-D0B5 | d0b4 Hangul Syllable-D0B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b7 Hangul Syllable-D0B7 | d0b6 Hangul Syllable-D0B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0b9 Hangul Syllable-D0B9 | d0b8 Hangul Syllable-D0B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bb Hangul Syllable-D0BB | d0ba Hangul Syllable-D0BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bd Hangul Syllable-D0BD | d0bc Hangul Syllable-D0BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0bf Hangul Syllable-D0BF | d0be Hangul Syllable-D0BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c1 Hangul Syllable-D0C1 | d0c0 Hangul Syllable-D0C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c3 Hangul Syllable-D0C3 | d0c2 Hangul Syllable-D0C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c5 Hangul Syllable-D0C5 | d0c4 Hangul Syllable-D0C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c7 Hangul Syllable-D0C7 | d0c6 Hangul Syllable-D0C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0c9 Hangul Syllable-D0C9 | d0c8 Hangul Syllable-D0C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cb Hangul Syllable-D0CB | d0ca Hangul Syllable-D0CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cd Hangul Syllable-D0CD | d0cc Hangul Syllable-D0CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0cf Hangul Syllable-D0CF | d0ce Hangul Syllable-D0CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d1 Hangul Syllable-D0D1 | d0d0 Hangul Syllable-D0D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d3 Hangul Syllable-D0D3 | d0d2 Hangul Syllable-D0D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d5 Hangul Syllable-D0D5 | d0d4 Hangul Syllable-D0D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d7 Hangul Syllable-D0D7 | d0d6 Hangul Syllable-D0D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0d9 Hangul Syllable-D0D9 | d0d8 Hangul Syllable-D0D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0db Hangul Syllable-D0DB | d0da Hangul Syllable-D0DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0dd Hangul Syllable-D0DD | d0dc Hangul Syllable-D0DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0df Hangul Syllable-D0DF | d0de Hangul Syllable-D0DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e1 Hangul Syllable-D0E1 | d0e0 Hangul Syllable-D0E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e3 Hangul Syllable-D0E3 | d0e2 Hangul Syllable-D0E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e5 Hangul Syllable-D0E5 | d0e4 Hangul Syllable-D0E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e7 Hangul Syllable-D0E7 | d0e6 Hangul Syllable-D0E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0e9 Hangul Syllable-D0E9 | d0e8 Hangul Syllable-D0E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0eb Hangul Syllable-D0EB | d0ea Hangul Syllable-D0EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ed Hangul Syllable-D0ED | d0ec Hangul Syllable-D0EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ef Hangul Syllable-D0EF | d0ee Hangul Syllable-D0EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f1 Hangul Syllable-D0F1 | d0f0 Hangul Syllable-D0F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f3 Hangul Syllable-D0F3 | d0f2 Hangul Syllable-D0F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f5 Hangul Syllable-D0F5 | d0f4 Hangul Syllable-D0F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f7 Hangul Syllable-D0F7 | d0f6 Hangul Syllable-D0F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0f9 Hangul Syllable-D0F9 | d0f8 Hangul Syllable-D0F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0fb Hangul Syllable-D0FB | d0fa Hangul Syllable-D0FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0fd Hangul Syllable-D0FD | d0fc Hangul Syllable-D0FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d0ff Hangul Syllable-D0FF | d0fe Hangul Syllable-D0FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d101 Hangul Syllable-D101 | d100 Hangul Syllable-D100 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d103 Hangul Syllable-D103 | d102 Hangul Syllable-D102 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d105 Hangul Syllable-D105 | d104 Hangul Syllable-D104 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d107 Hangul Syllable-D107 | d106 Hangul Syllable-D106 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d109 Hangul Syllable-D109 | d108 Hangul Syllable-D108 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10b Hangul Syllable-D10B | d10a Hangul Syllable-D10A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10d Hangul Syllable-D10D | d10c Hangul Syllable-D10C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d10f Hangul Syllable-D10F | d10e Hangul Syllable-D10E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d111 Hangul Syllable-D111 | d110 Hangul Syllable-D110 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d113 Hangul Syllable-D113 | d112 Hangul Syllable-D112 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d115 Hangul Syllable-D115 | d114 Hangul Syllable-D114 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d117 Hangul Syllable-D117 | d116 Hangul Syllable-D116 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d119 Hangul Syllable-D119 | d118 Hangul Syllable-D118 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11b Hangul Syllable-D11B | d11a Hangul Syllable-D11A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11d Hangul Syllable-D11D | d11c Hangul Syllable-D11C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d11f Hangul Syllable-D11F | d11e Hangul Syllable-D11E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d121 Hangul Syllable-D121 | d120 Hangul Syllable-D120 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d123 Hangul Syllable-D123 | d122 Hangul Syllable-D122 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d125 Hangul Syllable-D125 | d124 Hangul Syllable-D124 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d127 Hangul Syllable-D127 | d126 Hangul Syllable-D126 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d129 Hangul Syllable-D129 | d128 Hangul Syllable-D128 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12b Hangul Syllable-D12B | d12a Hangul Syllable-D12A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12d Hangul Syllable-D12D | d12c Hangul Syllable-D12C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d12f Hangul Syllable-D12F | d12e Hangul Syllable-D12E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d131 Hangul Syllable-D131 | d130 Hangul Syllable-D130 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d133 Hangul Syllable-D133 | d132 Hangul Syllable-D132 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d135 Hangul Syllable-D135 | d134 Hangul Syllable-D134 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d137 Hangul Syllable-D137 | d136 Hangul Syllable-D136 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d139 Hangul Syllable-D139 | d138 Hangul Syllable-D138 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13b Hangul Syllable-D13B | d13a Hangul Syllable-D13A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13d Hangul Syllable-D13D | d13c Hangul Syllable-D13C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d13f Hangul Syllable-D13F | d13e Hangul Syllable-D13E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d141 Hangul Syllable-D141 | d140 Hangul Syllable-D140 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d143 Hangul Syllable-D143 | d142 Hangul Syllable-D142 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d145 Hangul Syllable-D145 | d144 Hangul Syllable-D144 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d147 Hangul Syllable-D147 | d146 Hangul Syllable-D146 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d149 Hangul Syllable-D149 | d148 Hangul Syllable-D148 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14b Hangul Syllable-D14B | d14a Hangul Syllable-D14A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14d Hangul Syllable-D14D | d14c Hangul Syllable-D14C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d14f Hangul Syllable-D14F | d14e Hangul Syllable-D14E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d151 Hangul Syllable-D151 | d150 Hangul Syllable-D150 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d153 Hangul Syllable-D153 | d152 Hangul Syllable-D152 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d155 Hangul Syllable-D155 | d154 Hangul Syllable-D154 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d157 Hangul Syllable-D157 | d156 Hangul Syllable-D156 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d159 Hangul Syllable-D159 | d158 Hangul Syllable-D158 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15b Hangul Syllable-D15B | d15a Hangul Syllable-D15A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15d Hangul Syllable-D15D | d15c Hangul Syllable-D15C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d15f Hangul Syllable-D15F | d15e Hangul Syllable-D15E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d161 Hangul Syllable-D161 | d160 Hangul Syllable-D160 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d163 Hangul Syllable-D163 | d162 Hangul Syllable-D162 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d165 Hangul Syllable-D165 | d164 Hangul Syllable-D164 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d167 Hangul Syllable-D167 | d166 Hangul Syllable-D166 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d169 Hangul Syllable-D169 | d168 Hangul Syllable-D168 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16b Hangul Syllable-D16B | d16a Hangul Syllable-D16A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16d Hangul Syllable-D16D | d16c Hangul Syllable-D16C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d16f Hangul Syllable-D16F | d16e Hangul Syllable-D16E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d171 Hangul Syllable-D171 | d170 Hangul Syllable-D170 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d173 Hangul Syllable-D173 | d172 Hangul Syllable-D172 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d175 Hangul Syllable-D175 | d174 Hangul Syllable-D174 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d177 Hangul Syllable-D177 | d176 Hangul Syllable-D176 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d179 Hangul Syllable-D179 | d178 Hangul Syllable-D178 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17b Hangul Syllable-D17B | d17a Hangul Syllable-D17A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17d Hangul Syllable-D17D | d17c Hangul Syllable-D17C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d17f Hangul Syllable-D17F | d17e Hangul Syllable-D17E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d181 Hangul Syllable-D181 | d180 Hangul Syllable-D180 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d183 Hangul Syllable-D183 | d182 Hangul Syllable-D182 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d185 Hangul Syllable-D185 | d184 Hangul Syllable-D184 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d187 Hangul Syllable-D187 | d186 Hangul Syllable-D186 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d189 Hangul Syllable-D189 | d188 Hangul Syllable-D188 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18b Hangul Syllable-D18B | d18a Hangul Syllable-D18A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18d Hangul Syllable-D18D | d18c Hangul Syllable-D18C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d18f Hangul Syllable-D18F | d18e Hangul Syllable-D18E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d191 Hangul Syllable-D191 | d190 Hangul Syllable-D190 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d193 Hangul Syllable-D193 | d192 Hangul Syllable-D192 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d195 Hangul Syllable-D195 | d194 Hangul Syllable-D194 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d197 Hangul Syllable-D197 | d196 Hangul Syllable-D196 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d199 Hangul Syllable-D199 | d198 Hangul Syllable-D198 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19b Hangul Syllable-D19B | d19a Hangul Syllable-D19A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19d Hangul Syllable-D19D | d19c Hangul Syllable-D19C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d19f Hangul Syllable-D19F | d19e Hangul Syllable-D19E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a1 Hangul Syllable-D1A1 | d1a0 Hangul Syllable-D1A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a3 Hangul Syllable-D1A3 | d1a2 Hangul Syllable-D1A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a5 Hangul Syllable-D1A5 | d1a4 Hangul Syllable-D1A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a7 Hangul Syllable-D1A7 | d1a6 Hangul Syllable-D1A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1a9 Hangul Syllable-D1A9 | d1a8 Hangul Syllable-D1A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ab Hangul Syllable-D1AB | d1aa Hangul Syllable-D1AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ad Hangul Syllable-D1AD | d1ac Hangul Syllable-D1AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1af Hangul Syllable-D1AF | d1ae Hangul Syllable-D1AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b1 Hangul Syllable-D1B1 | d1b0 Hangul Syllable-D1B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b3 Hangul Syllable-D1B3 | d1b2 Hangul Syllable-D1B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b5 Hangul Syllable-D1B5 | d1b4 Hangul Syllable-D1B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b7 Hangul Syllable-D1B7 | d1b6 Hangul Syllable-D1B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1b9 Hangul Syllable-D1B9 | d1b8 Hangul Syllable-D1B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bb Hangul Syllable-D1BB | d1ba Hangul Syllable-D1BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bd Hangul Syllable-D1BD | d1bc Hangul Syllable-D1BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1bf Hangul Syllable-D1BF | d1be Hangul Syllable-D1BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c1 Hangul Syllable-D1C1 | d1c0 Hangul Syllable-D1C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c3 Hangul Syllable-D1C3 | d1c2 Hangul Syllable-D1C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c5 Hangul Syllable-D1C5 | d1c4 Hangul Syllable-D1C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c7 Hangul Syllable-D1C7 | d1c6 Hangul Syllable-D1C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1c9 Hangul Syllable-D1C9 | d1c8 Hangul Syllable-D1C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cb Hangul Syllable-D1CB | d1ca Hangul Syllable-D1CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cd Hangul Syllable-D1CD | d1cc Hangul Syllable-D1CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1cf Hangul Syllable-D1CF | d1ce Hangul Syllable-D1CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d1 Hangul Syllable-D1D1 | d1d0 Hangul Syllable-D1D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d3 Hangul Syllable-D1D3 | d1d2 Hangul Syllable-D1D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d5 Hangul Syllable-D1D5 | d1d4 Hangul Syllable-D1D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d7 Hangul Syllable-D1D7 | d1d6 Hangul Syllable-D1D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1d9 Hangul Syllable-D1D9 | d1d8 Hangul Syllable-D1D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1db Hangul Syllable-D1DB | d1da Hangul Syllable-D1DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1dd Hangul Syllable-D1DD | d1dc Hangul Syllable-D1DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1df Hangul Syllable-D1DF | d1de Hangul Syllable-D1DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e1 Hangul Syllable-D1E1 | d1e0 Hangul Syllable-D1E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e3 Hangul Syllable-D1E3 | d1e2 Hangul Syllable-D1E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e5 Hangul Syllable-D1E5 | d1e4 Hangul Syllable-D1E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e7 Hangul Syllable-D1E7 | d1e6 Hangul Syllable-D1E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1e9 Hangul Syllable-D1E9 | d1e8 Hangul Syllable-D1E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1eb Hangul Syllable-D1EB | d1ea Hangul Syllable-D1EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ed Hangul Syllable-D1ED | d1ec Hangul Syllable-D1EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ef Hangul Syllable-D1EF | d1ee Hangul Syllable-D1EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f1 Hangul Syllable-D1F1 | d1f0 Hangul Syllable-D1F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f3 Hangul Syllable-D1F3 | d1f2 Hangul Syllable-D1F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f5 Hangul Syllable-D1F5 | d1f4 Hangul Syllable-D1F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f7 Hangul Syllable-D1F7 | d1f6 Hangul Syllable-D1F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1f9 Hangul Syllable-D1F9 | d1f8 Hangul Syllable-D1F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1fb Hangul Syllable-D1FB | d1fa Hangul Syllable-D1FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1fd Hangul Syllable-D1FD | d1fc Hangul Syllable-D1FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d1ff Hangul Syllable-D1FF | d1fe Hangul Syllable-D1FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d201 Hangul Syllable-D201 | d200 Hangul Syllable-D200 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d203 Hangul Syllable-D203 | d202 Hangul Syllable-D202 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d205 Hangul Syllable-D205 | d204 Hangul Syllable-D204 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d207 Hangul Syllable-D207 | d206 Hangul Syllable-D206 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d209 Hangul Syllable-D209 | d208 Hangul Syllable-D208 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20b Hangul Syllable-D20B | d20a Hangul Syllable-D20A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20d Hangul Syllable-D20D | d20c Hangul Syllable-D20C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d20f Hangul Syllable-D20F | d20e Hangul Syllable-D20E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d211 Hangul Syllable-D211 | d210 Hangul Syllable-D210 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d213 Hangul Syllable-D213 | d212 Hangul Syllable-D212 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d215 Hangul Syllable-D215 | d214 Hangul Syllable-D214 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d217 Hangul Syllable-D217 | d216 Hangul Syllable-D216 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d219 Hangul Syllable-D219 | d218 Hangul Syllable-D218 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21b Hangul Syllable-D21B | d21a Hangul Syllable-D21A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21d Hangul Syllable-D21D | d21c Hangul Syllable-D21C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d21f Hangul Syllable-D21F | d21e Hangul Syllable-D21E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d221 Hangul Syllable-D221 | d220 Hangul Syllable-D220 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d223 Hangul Syllable-D223 | d222 Hangul Syllable-D222 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d225 Hangul Syllable-D225 | d224 Hangul Syllable-D224 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d227 Hangul Syllable-D227 | d226 Hangul Syllable-D226 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d229 Hangul Syllable-D229 | d228 Hangul Syllable-D228 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22b Hangul Syllable-D22B | d22a Hangul Syllable-D22A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22d Hangul Syllable-D22D | d22c Hangul Syllable-D22C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d22f Hangul Syllable-D22F | d22e Hangul Syllable-D22E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d231 Hangul Syllable-D231 | d230 Hangul Syllable-D230 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d233 Hangul Syllable-D233 | d232 Hangul Syllable-D232 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d235 Hangul Syllable-D235 | d234 Hangul Syllable-D234 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d237 Hangul Syllable-D237 | d236 Hangul Syllable-D236 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d239 Hangul Syllable-D239 | d238 Hangul Syllable-D238 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23b Hangul Syllable-D23B | d23a Hangul Syllable-D23A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23d Hangul Syllable-D23D | d23c Hangul Syllable-D23C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d23f Hangul Syllable-D23F | d23e Hangul Syllable-D23E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d241 Hangul Syllable-D241 | d240 Hangul Syllable-D240 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d243 Hangul Syllable-D243 | d242 Hangul Syllable-D242 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d245 Hangul Syllable-D245 | d244 Hangul Syllable-D244 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d247 Hangul Syllable-D247 | d246 Hangul Syllable-D246 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d249 Hangul Syllable-D249 | d248 Hangul Syllable-D248 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24b Hangul Syllable-D24B | d24a Hangul Syllable-D24A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24d Hangul Syllable-D24D | d24c Hangul Syllable-D24C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d24f Hangul Syllable-D24F | d24e Hangul Syllable-D24E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d251 Hangul Syllable-D251 | d250 Hangul Syllable-D250 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d253 Hangul Syllable-D253 | d252 Hangul Syllable-D252 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d255 Hangul Syllable-D255 | d254 Hangul Syllable-D254 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d257 Hangul Syllable-D257 | d256 Hangul Syllable-D256 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d259 Hangul Syllable-D259 | d258 Hangul Syllable-D258 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25b Hangul Syllable-D25B | d25a Hangul Syllable-D25A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25d Hangul Syllable-D25D | d25c Hangul Syllable-D25C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d25f Hangul Syllable-D25F | d25e Hangul Syllable-D25E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d261 Hangul Syllable-D261 | d260 Hangul Syllable-D260 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d263 Hangul Syllable-D263 | d262 Hangul Syllable-D262 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d265 Hangul Syllable-D265 | d264 Hangul Syllable-D264 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d267 Hangul Syllable-D267 | d266 Hangul Syllable-D266 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d269 Hangul Syllable-D269 | d268 Hangul Syllable-D268 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26b Hangul Syllable-D26B | d26a Hangul Syllable-D26A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26d Hangul Syllable-D26D | d26c Hangul Syllable-D26C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d26f Hangul Syllable-D26F | d26e Hangul Syllable-D26E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d271 Hangul Syllable-D271 | d270 Hangul Syllable-D270 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d273 Hangul Syllable-D273 | d272 Hangul Syllable-D272 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d275 Hangul Syllable-D275 | d274 Hangul Syllable-D274 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d277 Hangul Syllable-D277 | d276 Hangul Syllable-D276 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d279 Hangul Syllable-D279 | d278 Hangul Syllable-D278 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27b Hangul Syllable-D27B | d27a Hangul Syllable-D27A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27d Hangul Syllable-D27D | d27c Hangul Syllable-D27C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d27f Hangul Syllable-D27F | d27e Hangul Syllable-D27E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d281 Hangul Syllable-D281 | d280 Hangul Syllable-D280 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d283 Hangul Syllable-D283 | d282 Hangul Syllable-D282 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d285 Hangul Syllable-D285 | d284 Hangul Syllable-D284 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d287 Hangul Syllable-D287 | d286 Hangul Syllable-D286 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d289 Hangul Syllable-D289 | d288 Hangul Syllable-D288 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28b Hangul Syllable-D28B | d28a Hangul Syllable-D28A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28d Hangul Syllable-D28D | d28c Hangul Syllable-D28C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d28f Hangul Syllable-D28F | d28e Hangul Syllable-D28E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d291 Hangul Syllable-D291 | d290 Hangul Syllable-D290 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d293 Hangul Syllable-D293 | d292 Hangul Syllable-D292 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d295 Hangul Syllable-D295 | d294 Hangul Syllable-D294 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d297 Hangul Syllable-D297 | d296 Hangul Syllable-D296 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d299 Hangul Syllable-D299 | d298 Hangul Syllable-D298 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29b Hangul Syllable-D29B | d29a Hangul Syllable-D29A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29d Hangul Syllable-D29D | d29c Hangul Syllable-D29C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d29f Hangul Syllable-D29F | d29e Hangul Syllable-D29E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a1 Hangul Syllable-D2A1 | d2a0 Hangul Syllable-D2A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a3 Hangul Syllable-D2A3 | d2a2 Hangul Syllable-D2A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a5 Hangul Syllable-D2A5 | d2a4 Hangul Syllable-D2A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a7 Hangul Syllable-D2A7 | d2a6 Hangul Syllable-D2A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2a9 Hangul Syllable-D2A9 | d2a8 Hangul Syllable-D2A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ab Hangul Syllable-D2AB | d2aa Hangul Syllable-D2AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ad Hangul Syllable-D2AD | d2ac Hangul Syllable-D2AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2af Hangul Syllable-D2AF | d2ae Hangul Syllable-D2AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b1 Hangul Syllable-D2B1 | d2b0 Hangul Syllable-D2B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b3 Hangul Syllable-D2B3 | d2b2 Hangul Syllable-D2B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b5 Hangul Syllable-D2B5 | d2b4 Hangul Syllable-D2B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b7 Hangul Syllable-D2B7 | d2b6 Hangul Syllable-D2B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2b9 Hangul Syllable-D2B9 | d2b8 Hangul Syllable-D2B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bb Hangul Syllable-D2BB | d2ba Hangul Syllable-D2BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bd Hangul Syllable-D2BD | d2bc Hangul Syllable-D2BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2bf Hangul Syllable-D2BF | d2be Hangul Syllable-D2BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c1 Hangul Syllable-D2C1 | d2c0 Hangul Syllable-D2C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c3 Hangul Syllable-D2C3 | d2c2 Hangul Syllable-D2C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c5 Hangul Syllable-D2C5 | d2c4 Hangul Syllable-D2C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c7 Hangul Syllable-D2C7 | d2c6 Hangul Syllable-D2C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2c9 Hangul Syllable-D2C9 | d2c8 Hangul Syllable-D2C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cb Hangul Syllable-D2CB | d2ca Hangul Syllable-D2CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cd Hangul Syllable-D2CD | d2cc Hangul Syllable-D2CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2cf Hangul Syllable-D2CF | d2ce Hangul Syllable-D2CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d1 Hangul Syllable-D2D1 | d2d0 Hangul Syllable-D2D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d3 Hangul Syllable-D2D3 | d2d2 Hangul Syllable-D2D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d5 Hangul Syllable-D2D5 | d2d4 Hangul Syllable-D2D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d7 Hangul Syllable-D2D7 | d2d6 Hangul Syllable-D2D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2d9 Hangul Syllable-D2D9 | d2d8 Hangul Syllable-D2D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2db Hangul Syllable-D2DB | d2da Hangul Syllable-D2DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2dd Hangul Syllable-D2DD | d2dc Hangul Syllable-D2DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2df Hangul Syllable-D2DF | d2de Hangul Syllable-D2DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e1 Hangul Syllable-D2E1 | d2e0 Hangul Syllable-D2E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e3 Hangul Syllable-D2E3 | d2e2 Hangul Syllable-D2E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e5 Hangul Syllable-D2E5 | d2e4 Hangul Syllable-D2E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e7 Hangul Syllable-D2E7 | d2e6 Hangul Syllable-D2E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2e9 Hangul Syllable-D2E9 | d2e8 Hangul Syllable-D2E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2eb Hangul Syllable-D2EB | d2ea Hangul Syllable-D2EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ed Hangul Syllable-D2ED | d2ec Hangul Syllable-D2EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ef Hangul Syllable-D2EF | d2ee Hangul Syllable-D2EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f1 Hangul Syllable-D2F1 | d2f0 Hangul Syllable-D2F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f3 Hangul Syllable-D2F3 | d2f2 Hangul Syllable-D2F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f5 Hangul Syllable-D2F5 | d2f4 Hangul Syllable-D2F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f7 Hangul Syllable-D2F7 | d2f6 Hangul Syllable-D2F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2f9 Hangul Syllable-D2F9 | d2f8 Hangul Syllable-D2F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2fb Hangul Syllable-D2FB | d2fa Hangul Syllable-D2FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2fd Hangul Syllable-D2FD | d2fc Hangul Syllable-D2FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d2ff Hangul Syllable-D2FF | d2fe Hangul Syllable-D2FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d301 Hangul Syllable-D301 | d300 Hangul Syllable-D300 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d303 Hangul Syllable-D303 | d302 Hangul Syllable-D302 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d305 Hangul Syllable-D305 | d304 Hangul Syllable-D304 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d307 Hangul Syllable-D307 | d306 Hangul Syllable-D306 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d309 Hangul Syllable-D309 | d308 Hangul Syllable-D308 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30b Hangul Syllable-D30B | d30a Hangul Syllable-D30A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30d Hangul Syllable-D30D | d30c Hangul Syllable-D30C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d30f Hangul Syllable-D30F | d30e Hangul Syllable-D30E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d311 Hangul Syllable-D311 | d310 Hangul Syllable-D310 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d313 Hangul Syllable-D313 | d312 Hangul Syllable-D312 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d315 Hangul Syllable-D315 | d314 Hangul Syllable-D314 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d317 Hangul Syllable-D317 | d316 Hangul Syllable-D316 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d319 Hangul Syllable-D319 | d318 Hangul Syllable-D318 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31b Hangul Syllable-D31B | d31a Hangul Syllable-D31A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31d Hangul Syllable-D31D | d31c Hangul Syllable-D31C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d31f Hangul Syllable-D31F | d31e Hangul Syllable-D31E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d321 Hangul Syllable-D321 | d320 Hangul Syllable-D320 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d323 Hangul Syllable-D323 | d322 Hangul Syllable-D322 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d325 Hangul Syllable-D325 | d324 Hangul Syllable-D324 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d327 Hangul Syllable-D327 | d326 Hangul Syllable-D326 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d329 Hangul Syllable-D329 | d328 Hangul Syllable-D328 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32b Hangul Syllable-D32B | d32a Hangul Syllable-D32A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32d Hangul Syllable-D32D | d32c Hangul Syllable-D32C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d32f Hangul Syllable-D32F | d32e Hangul Syllable-D32E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d331 Hangul Syllable-D331 | d330 Hangul Syllable-D330 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d333 Hangul Syllable-D333 | d332 Hangul Syllable-D332 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d335 Hangul Syllable-D335 | d334 Hangul Syllable-D334 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d337 Hangul Syllable-D337 | d336 Hangul Syllable-D336 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d339 Hangul Syllable-D339 | d338 Hangul Syllable-D338 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33b Hangul Syllable-D33B | d33a Hangul Syllable-D33A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33d Hangul Syllable-D33D | d33c Hangul Syllable-D33C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d33f Hangul Syllable-D33F | d33e Hangul Syllable-D33E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d341 Hangul Syllable-D341 | d340 Hangul Syllable-D340 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d343 Hangul Syllable-D343 | d342 Hangul Syllable-D342 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d345 Hangul Syllable-D345 | d344 Hangul Syllable-D344 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d347 Hangul Syllable-D347 | d346 Hangul Syllable-D346 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d349 Hangul Syllable-D349 | d348 Hangul Syllable-D348 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34b Hangul Syllable-D34B | d34a Hangul Syllable-D34A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34d Hangul Syllable-D34D | d34c Hangul Syllable-D34C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d34f Hangul Syllable-D34F | d34e Hangul Syllable-D34E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d351 Hangul Syllable-D351 | d350 Hangul Syllable-D350 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d353 Hangul Syllable-D353 | d352 Hangul Syllable-D352 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d355 Hangul Syllable-D355 | d354 Hangul Syllable-D354 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d357 Hangul Syllable-D357 | d356 Hangul Syllable-D356 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d359 Hangul Syllable-D359 | d358 Hangul Syllable-D358 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35b Hangul Syllable-D35B | d35a Hangul Syllable-D35A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35d Hangul Syllable-D35D | d35c Hangul Syllable-D35C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d35f Hangul Syllable-D35F | d35e Hangul Syllable-D35E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d361 Hangul Syllable-D361 | d360 Hangul Syllable-D360 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d363 Hangul Syllable-D363 | d362 Hangul Syllable-D362 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d365 Hangul Syllable-D365 | d364 Hangul Syllable-D364 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d367 Hangul Syllable-D367 | d366 Hangul Syllable-D366 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d369 Hangul Syllable-D369 | d368 Hangul Syllable-D368 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36b Hangul Syllable-D36B | d36a Hangul Syllable-D36A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36d Hangul Syllable-D36D | d36c Hangul Syllable-D36C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d36f Hangul Syllable-D36F | d36e Hangul Syllable-D36E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d371 Hangul Syllable-D371 | d370 Hangul Syllable-D370 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d373 Hangul Syllable-D373 | d372 Hangul Syllable-D372 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d375 Hangul Syllable-D375 | d374 Hangul Syllable-D374 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d377 Hangul Syllable-D377 | d376 Hangul Syllable-D376 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d379 Hangul Syllable-D379 | d378 Hangul Syllable-D378 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37b Hangul Syllable-D37B | d37a Hangul Syllable-D37A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37d Hangul Syllable-D37D | d37c Hangul Syllable-D37C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d37f Hangul Syllable-D37F | d37e Hangul Syllable-D37E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d381 Hangul Syllable-D381 | d380 Hangul Syllable-D380 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d383 Hangul Syllable-D383 | d382 Hangul Syllable-D382 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d385 Hangul Syllable-D385 | d384 Hangul Syllable-D384 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d387 Hangul Syllable-D387 | d386 Hangul Syllable-D386 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d389 Hangul Syllable-D389 | d388 Hangul Syllable-D388 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38b Hangul Syllable-D38B | d38a Hangul Syllable-D38A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38d Hangul Syllable-D38D | d38c Hangul Syllable-D38C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d38f Hangul Syllable-D38F | d38e Hangul Syllable-D38E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d391 Hangul Syllable-D391 | d390 Hangul Syllable-D390 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d393 Hangul Syllable-D393 | d392 Hangul Syllable-D392 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d395 Hangul Syllable-D395 | d394 Hangul Syllable-D394 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d397 Hangul Syllable-D397 | d396 Hangul Syllable-D396 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d399 Hangul Syllable-D399 | d398 Hangul Syllable-D398 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39b Hangul Syllable-D39B | d39a Hangul Syllable-D39A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39d Hangul Syllable-D39D | d39c Hangul Syllable-D39C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d39f Hangul Syllable-D39F | d39e Hangul Syllable-D39E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a1 Hangul Syllable-D3A1 | d3a0 Hangul Syllable-D3A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a3 Hangul Syllable-D3A3 | d3a2 Hangul Syllable-D3A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a5 Hangul Syllable-D3A5 | d3a4 Hangul Syllable-D3A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a7 Hangul Syllable-D3A7 | d3a6 Hangul Syllable-D3A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3a9 Hangul Syllable-D3A9 | d3a8 Hangul Syllable-D3A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ab Hangul Syllable-D3AB | d3aa Hangul Syllable-D3AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ad Hangul Syllable-D3AD | d3ac Hangul Syllable-D3AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3af Hangul Syllable-D3AF | d3ae Hangul Syllable-D3AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b1 Hangul Syllable-D3B1 | d3b0 Hangul Syllable-D3B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b3 Hangul Syllable-D3B3 | d3b2 Hangul Syllable-D3B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b5 Hangul Syllable-D3B5 | d3b4 Hangul Syllable-D3B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b7 Hangul Syllable-D3B7 | d3b6 Hangul Syllable-D3B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3b9 Hangul Syllable-D3B9 | d3b8 Hangul Syllable-D3B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bb Hangul Syllable-D3BB | d3ba Hangul Syllable-D3BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bd Hangul Syllable-D3BD | d3bc Hangul Syllable-D3BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3bf Hangul Syllable-D3BF | d3be Hangul Syllable-D3BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c1 Hangul Syllable-D3C1 | d3c0 Hangul Syllable-D3C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c3 Hangul Syllable-D3C3 | d3c2 Hangul Syllable-D3C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c5 Hangul Syllable-D3C5 | d3c4 Hangul Syllable-D3C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c7 Hangul Syllable-D3C7 | d3c6 Hangul Syllable-D3C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3c9 Hangul Syllable-D3C9 | d3c8 Hangul Syllable-D3C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cb Hangul Syllable-D3CB | d3ca Hangul Syllable-D3CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cd Hangul Syllable-D3CD | d3cc Hangul Syllable-D3CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3cf Hangul Syllable-D3CF | d3ce Hangul Syllable-D3CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d1 Hangul Syllable-D3D1 | d3d0 Hangul Syllable-D3D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d3 Hangul Syllable-D3D3 | d3d2 Hangul Syllable-D3D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d5 Hangul Syllable-D3D5 | d3d4 Hangul Syllable-D3D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d7 Hangul Syllable-D3D7 | d3d6 Hangul Syllable-D3D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3d9 Hangul Syllable-D3D9 | d3d8 Hangul Syllable-D3D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3db Hangul Syllable-D3DB | d3da Hangul Syllable-D3DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3dd Hangul Syllable-D3DD | d3dc Hangul Syllable-D3DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3df Hangul Syllable-D3DF | d3de Hangul Syllable-D3DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e1 Hangul Syllable-D3E1 | d3e0 Hangul Syllable-D3E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e3 Hangul Syllable-D3E3 | d3e2 Hangul Syllable-D3E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e5 Hangul Syllable-D3E5 | d3e4 Hangul Syllable-D3E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e7 Hangul Syllable-D3E7 | d3e6 Hangul Syllable-D3E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3e9 Hangul Syllable-D3E9 | d3e8 Hangul Syllable-D3E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3eb Hangul Syllable-D3EB | d3ea Hangul Syllable-D3EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ed Hangul Syllable-D3ED | d3ec Hangul Syllable-D3EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ef Hangul Syllable-D3EF | d3ee Hangul Syllable-D3EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f1 Hangul Syllable-D3F1 | d3f0 Hangul Syllable-D3F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f3 Hangul Syllable-D3F3 | d3f2 Hangul Syllable-D3F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f5 Hangul Syllable-D3F5 | d3f4 Hangul Syllable-D3F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f7 Hangul Syllable-D3F7 | d3f6 Hangul Syllable-D3F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3f9 Hangul Syllable-D3F9 | d3f8 Hangul Syllable-D3F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3fb Hangul Syllable-D3FB | d3fa Hangul Syllable-D3FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3fd Hangul Syllable-D3FD | d3fc Hangul Syllable-D3FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d3ff Hangul Syllable-D3FF | d3fe Hangul Syllable-D3FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d401 Hangul Syllable-D401 | d400 Hangul Syllable-D400 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d403 Hangul Syllable-D403 | d402 Hangul Syllable-D402 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d405 Hangul Syllable-D405 | d404 Hangul Syllable-D404 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d407 Hangul Syllable-D407 | d406 Hangul Syllable-D406 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d409 Hangul Syllable-D409 | d408 Hangul Syllable-D408 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40b Hangul Syllable-D40B | d40a Hangul Syllable-D40A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40d Hangul Syllable-D40D | d40c Hangul Syllable-D40C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d40f Hangul Syllable-D40F | d40e Hangul Syllable-D40E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d411 Hangul Syllable-D411 | d410 Hangul Syllable-D410 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d413 Hangul Syllable-D413 | d412 Hangul Syllable-D412 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d415 Hangul Syllable-D415 | d414 Hangul Syllable-D414 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d417 Hangul Syllable-D417 | d416 Hangul Syllable-D416 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d419 Hangul Syllable-D419 | d418 Hangul Syllable-D418 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41b Hangul Syllable-D41B | d41a Hangul Syllable-D41A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41d Hangul Syllable-D41D | d41c Hangul Syllable-D41C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d41f Hangul Syllable-D41F | d41e Hangul Syllable-D41E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d421 Hangul Syllable-D421 | d420 Hangul Syllable-D420 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d423 Hangul Syllable-D423 | d422 Hangul Syllable-D422 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d425 Hangul Syllable-D425 | d424 Hangul Syllable-D424 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d427 Hangul Syllable-D427 | d426 Hangul Syllable-D426 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d429 Hangul Syllable-D429 | d428 Hangul Syllable-D428 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42b Hangul Syllable-D42B | d42a Hangul Syllable-D42A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42d Hangul Syllable-D42D | d42c Hangul Syllable-D42C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d42f Hangul Syllable-D42F | d42e Hangul Syllable-D42E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d431 Hangul Syllable-D431 | d430 Hangul Syllable-D430 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d433 Hangul Syllable-D433 | d432 Hangul Syllable-D432 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d435 Hangul Syllable-D435 | d434 Hangul Syllable-D434 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d437 Hangul Syllable-D437 | d436 Hangul Syllable-D436 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d439 Hangul Syllable-D439 | d438 Hangul Syllable-D438 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43b Hangul Syllable-D43B | d43a Hangul Syllable-D43A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43d Hangul Syllable-D43D | d43c Hangul Syllable-D43C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d43f Hangul Syllable-D43F | d43e Hangul Syllable-D43E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d441 Hangul Syllable-D441 | d440 Hangul Syllable-D440 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d443 Hangul Syllable-D443 | d442 Hangul Syllable-D442 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d445 Hangul Syllable-D445 | d444 Hangul Syllable-D444 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d447 Hangul Syllable-D447 | d446 Hangul Syllable-D446 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d449 Hangul Syllable-D449 | d448 Hangul Syllable-D448 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44b Hangul Syllable-D44B | d44a Hangul Syllable-D44A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44d Hangul Syllable-D44D | d44c Hangul Syllable-D44C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d44f Hangul Syllable-D44F | d44e Hangul Syllable-D44E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d451 Hangul Syllable-D451 | d450 Hangul Syllable-D450 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d453 Hangul Syllable-D453 | d452 Hangul Syllable-D452 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d455 Hangul Syllable-D455 | d454 Hangul Syllable-D454 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d457 Hangul Syllable-D457 | d456 Hangul Syllable-D456 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d459 Hangul Syllable-D459 | d458 Hangul Syllable-D458 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45b Hangul Syllable-D45B | d45a Hangul Syllable-D45A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45d Hangul Syllable-D45D | d45c Hangul Syllable-D45C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d45f Hangul Syllable-D45F | d45e Hangul Syllable-D45E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d461 Hangul Syllable-D461 | d460 Hangul Syllable-D460 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d463 Hangul Syllable-D463 | d462 Hangul Syllable-D462 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d465 Hangul Syllable-D465 | d464 Hangul Syllable-D464 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d467 Hangul Syllable-D467 | d466 Hangul Syllable-D466 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d469 Hangul Syllable-D469 | d468 Hangul Syllable-D468 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46b Hangul Syllable-D46B | d46a Hangul Syllable-D46A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46d Hangul Syllable-D46D | d46c Hangul Syllable-D46C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d46f Hangul Syllable-D46F | d46e Hangul Syllable-D46E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d471 Hangul Syllable-D471 | d470 Hangul Syllable-D470 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d473 Hangul Syllable-D473 | d472 Hangul Syllable-D472 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d475 Hangul Syllable-D475 | d474 Hangul Syllable-D474 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d477 Hangul Syllable-D477 | d476 Hangul Syllable-D476 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d479 Hangul Syllable-D479 | d478 Hangul Syllable-D478 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47b Hangul Syllable-D47B | d47a Hangul Syllable-D47A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47d Hangul Syllable-D47D | d47c Hangul Syllable-D47C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d47f Hangul Syllable-D47F | d47e Hangul Syllable-D47E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d481 Hangul Syllable-D481 | d480 Hangul Syllable-D480 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d483 Hangul Syllable-D483 | d482 Hangul Syllable-D482 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d485 Hangul Syllable-D485 | d484 Hangul Syllable-D484 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d487 Hangul Syllable-D487 | d486 Hangul Syllable-D486 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d489 Hangul Syllable-D489 | d488 Hangul Syllable-D488 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48b Hangul Syllable-D48B | d48a Hangul Syllable-D48A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48d Hangul Syllable-D48D | d48c Hangul Syllable-D48C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d48f Hangul Syllable-D48F | d48e Hangul Syllable-D48E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d491 Hangul Syllable-D491 | d490 Hangul Syllable-D490 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d493 Hangul Syllable-D493 | d492 Hangul Syllable-D492 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d495 Hangul Syllable-D495 | d494 Hangul Syllable-D494 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d497 Hangul Syllable-D497 | d496 Hangul Syllable-D496 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d499 Hangul Syllable-D499 | d498 Hangul Syllable-D498 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49b Hangul Syllable-D49B | d49a Hangul Syllable-D49A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49d Hangul Syllable-D49D | d49c Hangul Syllable-D49C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d49f Hangul Syllable-D49F | d49e Hangul Syllable-D49E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a1 Hangul Syllable-D4A1 | d4a0 Hangul Syllable-D4A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a3 Hangul Syllable-D4A3 | d4a2 Hangul Syllable-D4A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a5 Hangul Syllable-D4A5 | d4a4 Hangul Syllable-D4A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a7 Hangul Syllable-D4A7 | d4a6 Hangul Syllable-D4A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4a9 Hangul Syllable-D4A9 | d4a8 Hangul Syllable-D4A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ab Hangul Syllable-D4AB | d4aa Hangul Syllable-D4AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ad Hangul Syllable-D4AD | d4ac Hangul Syllable-D4AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4af Hangul Syllable-D4AF | d4ae Hangul Syllable-D4AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b1 Hangul Syllable-D4B1 | d4b0 Hangul Syllable-D4B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b3 Hangul Syllable-D4B3 | d4b2 Hangul Syllable-D4B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b5 Hangul Syllable-D4B5 | d4b4 Hangul Syllable-D4B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b7 Hangul Syllable-D4B7 | d4b6 Hangul Syllable-D4B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4b9 Hangul Syllable-D4B9 | d4b8 Hangul Syllable-D4B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bb Hangul Syllable-D4BB | d4ba Hangul Syllable-D4BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bd Hangul Syllable-D4BD | d4bc Hangul Syllable-D4BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4bf Hangul Syllable-D4BF | d4be Hangul Syllable-D4BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c1 Hangul Syllable-D4C1 | d4c0 Hangul Syllable-D4C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c3 Hangul Syllable-D4C3 | d4c2 Hangul Syllable-D4C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c5 Hangul Syllable-D4C5 | d4c4 Hangul Syllable-D4C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c7 Hangul Syllable-D4C7 | d4c6 Hangul Syllable-D4C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4c9 Hangul Syllable-D4C9 | d4c8 Hangul Syllable-D4C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cb Hangul Syllable-D4CB | d4ca Hangul Syllable-D4CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cd Hangul Syllable-D4CD | d4cc Hangul Syllable-D4CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4cf Hangul Syllable-D4CF | d4ce Hangul Syllable-D4CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d1 Hangul Syllable-D4D1 | d4d0 Hangul Syllable-D4D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d3 Hangul Syllable-D4D3 | d4d2 Hangul Syllable-D4D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d5 Hangul Syllable-D4D5 | d4d4 Hangul Syllable-D4D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d7 Hangul Syllable-D4D7 | d4d6 Hangul Syllable-D4D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4d9 Hangul Syllable-D4D9 | d4d8 Hangul Syllable-D4D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4db Hangul Syllable-D4DB | d4da Hangul Syllable-D4DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4dd Hangul Syllable-D4DD | d4dc Hangul Syllable-D4DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4df Hangul Syllable-D4DF | d4de Hangul Syllable-D4DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e1 Hangul Syllable-D4E1 | d4e0 Hangul Syllable-D4E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e3 Hangul Syllable-D4E3 | d4e2 Hangul Syllable-D4E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e5 Hangul Syllable-D4E5 | d4e4 Hangul Syllable-D4E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e7 Hangul Syllable-D4E7 | d4e6 Hangul Syllable-D4E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4e9 Hangul Syllable-D4E9 | d4e8 Hangul Syllable-D4E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4eb Hangul Syllable-D4EB | d4ea Hangul Syllable-D4EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ed Hangul Syllable-D4ED | d4ec Hangul Syllable-D4EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ef Hangul Syllable-D4EF | d4ee Hangul Syllable-D4EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f1 Hangul Syllable-D4F1 | d4f0 Hangul Syllable-D4F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f3 Hangul Syllable-D4F3 | d4f2 Hangul Syllable-D4F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f5 Hangul Syllable-D4F5 | d4f4 Hangul Syllable-D4F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f7 Hangul Syllable-D4F7 | d4f6 Hangul Syllable-D4F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4f9 Hangul Syllable-D4F9 | d4f8 Hangul Syllable-D4F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4fb Hangul Syllable-D4FB | d4fa Hangul Syllable-D4FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4fd Hangul Syllable-D4FD | d4fc Hangul Syllable-D4FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d4ff Hangul Syllable-D4FF | d4fe Hangul Syllable-D4FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d501 Hangul Syllable-D501 | d500 Hangul Syllable-D500 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d503 Hangul Syllable-D503 | d502 Hangul Syllable-D502 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d505 Hangul Syllable-D505 | d504 Hangul Syllable-D504 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d507 Hangul Syllable-D507 | d506 Hangul Syllable-D506 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d509 Hangul Syllable-D509 | d508 Hangul Syllable-D508 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50b Hangul Syllable-D50B | d50a Hangul Syllable-D50A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50d Hangul Syllable-D50D | d50c Hangul Syllable-D50C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d50f Hangul Syllable-D50F | d50e Hangul Syllable-D50E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d511 Hangul Syllable-D511 | d510 Hangul Syllable-D510 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d513 Hangul Syllable-D513 | d512 Hangul Syllable-D512 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d515 Hangul Syllable-D515 | d514 Hangul Syllable-D514 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d517 Hangul Syllable-D517 | d516 Hangul Syllable-D516 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d519 Hangul Syllable-D519 | d518 Hangul Syllable-D518 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51b Hangul Syllable-D51B | d51a Hangul Syllable-D51A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51d Hangul Syllable-D51D | d51c Hangul Syllable-D51C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d51f Hangul Syllable-D51F | d51e Hangul Syllable-D51E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d521 Hangul Syllable-D521 | d520 Hangul Syllable-D520 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d523 Hangul Syllable-D523 | d522 Hangul Syllable-D522 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d525 Hangul Syllable-D525 | d524 Hangul Syllable-D524 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d527 Hangul Syllable-D527 | d526 Hangul Syllable-D526 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d529 Hangul Syllable-D529 | d528 Hangul Syllable-D528 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52b Hangul Syllable-D52B | d52a Hangul Syllable-D52A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52d Hangul Syllable-D52D | d52c Hangul Syllable-D52C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d52f Hangul Syllable-D52F | d52e Hangul Syllable-D52E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d531 Hangul Syllable-D531 | d530 Hangul Syllable-D530 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d533 Hangul Syllable-D533 | d532 Hangul Syllable-D532 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d535 Hangul Syllable-D535 | d534 Hangul Syllable-D534 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d537 Hangul Syllable-D537 | d536 Hangul Syllable-D536 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d539 Hangul Syllable-D539 | d538 Hangul Syllable-D538 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53b Hangul Syllable-D53B | d53a Hangul Syllable-D53A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53d Hangul Syllable-D53D | d53c Hangul Syllable-D53C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d53f Hangul Syllable-D53F | d53e Hangul Syllable-D53E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d541 Hangul Syllable-D541 | d540 Hangul Syllable-D540 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d543 Hangul Syllable-D543 | d542 Hangul Syllable-D542 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d545 Hangul Syllable-D545 | d544 Hangul Syllable-D544 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d547 Hangul Syllable-D547 | d546 Hangul Syllable-D546 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d549 Hangul Syllable-D549 | d548 Hangul Syllable-D548 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54b Hangul Syllable-D54B | d54a Hangul Syllable-D54A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54d Hangul Syllable-D54D | d54c Hangul Syllable-D54C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d54f Hangul Syllable-D54F | d54e Hangul Syllable-D54E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d551 Hangul Syllable-D551 | d550 Hangul Syllable-D550 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d553 Hangul Syllable-D553 | d552 Hangul Syllable-D552 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d555 Hangul Syllable-D555 | d554 Hangul Syllable-D554 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d557 Hangul Syllable-D557 | d556 Hangul Syllable-D556 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d559 Hangul Syllable-D559 | d558 Hangul Syllable-D558 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55b Hangul Syllable-D55B | d55a Hangul Syllable-D55A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55d Hangul Syllable-D55D | d55c Hangul Syllable-D55C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d55f Hangul Syllable-D55F | d55e Hangul Syllable-D55E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d561 Hangul Syllable-D561 | d560 Hangul Syllable-D560 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d563 Hangul Syllable-D563 | d562 Hangul Syllable-D562 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d565 Hangul Syllable-D565 | d564 Hangul Syllable-D564 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d567 Hangul Syllable-D567 | d566 Hangul Syllable-D566 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d569 Hangul Syllable-D569 | d568 Hangul Syllable-D568 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56b Hangul Syllable-D56B | d56a Hangul Syllable-D56A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56d Hangul Syllable-D56D | d56c Hangul Syllable-D56C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d56f Hangul Syllable-D56F | d56e Hangul Syllable-D56E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d571 Hangul Syllable-D571 | d570 Hangul Syllable-D570 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d573 Hangul Syllable-D573 | d572 Hangul Syllable-D572 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d575 Hangul Syllable-D575 | d574 Hangul Syllable-D574 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d577 Hangul Syllable-D577 | d576 Hangul Syllable-D576 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d579 Hangul Syllable-D579 | d578 Hangul Syllable-D578 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57b Hangul Syllable-D57B | d57a Hangul Syllable-D57A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57d Hangul Syllable-D57D | d57c Hangul Syllable-D57C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d57f Hangul Syllable-D57F | d57e Hangul Syllable-D57E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d581 Hangul Syllable-D581 | d580 Hangul Syllable-D580 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d583 Hangul Syllable-D583 | d582 Hangul Syllable-D582 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d585 Hangul Syllable-D585 | d584 Hangul Syllable-D584 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d587 Hangul Syllable-D587 | d586 Hangul Syllable-D586 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d589 Hangul Syllable-D589 | d588 Hangul Syllable-D588 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58b Hangul Syllable-D58B | d58a Hangul Syllable-D58A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58d Hangul Syllable-D58D | d58c Hangul Syllable-D58C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d58f Hangul Syllable-D58F | d58e Hangul Syllable-D58E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d591 Hangul Syllable-D591 | d590 Hangul Syllable-D590 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d593 Hangul Syllable-D593 | d592 Hangul Syllable-D592 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d595 Hangul Syllable-D595 | d594 Hangul Syllable-D594 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d597 Hangul Syllable-D597 | d596 Hangul Syllable-D596 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d599 Hangul Syllable-D599 | d598 Hangul Syllable-D598 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59b Hangul Syllable-D59B | d59a Hangul Syllable-D59A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59d Hangul Syllable-D59D | d59c Hangul Syllable-D59C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d59f Hangul Syllable-D59F | d59e Hangul Syllable-D59E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a1 Hangul Syllable-D5A1 | d5a0 Hangul Syllable-D5A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a3 Hangul Syllable-D5A3 | d5a2 Hangul Syllable-D5A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a5 Hangul Syllable-D5A5 | d5a4 Hangul Syllable-D5A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a7 Hangul Syllable-D5A7 | d5a6 Hangul Syllable-D5A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5a9 Hangul Syllable-D5A9 | d5a8 Hangul Syllable-D5A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ab Hangul Syllable-D5AB | d5aa Hangul Syllable-D5AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ad Hangul Syllable-D5AD | d5ac Hangul Syllable-D5AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5af Hangul Syllable-D5AF | d5ae Hangul Syllable-D5AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b1 Hangul Syllable-D5B1 | d5b0 Hangul Syllable-D5B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b3 Hangul Syllable-D5B3 | d5b2 Hangul Syllable-D5B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b5 Hangul Syllable-D5B5 | d5b4 Hangul Syllable-D5B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b7 Hangul Syllable-D5B7 | d5b6 Hangul Syllable-D5B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5b9 Hangul Syllable-D5B9 | d5b8 Hangul Syllable-D5B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bb Hangul Syllable-D5BB | d5ba Hangul Syllable-D5BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bd Hangul Syllable-D5BD | d5bc Hangul Syllable-D5BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5bf Hangul Syllable-D5BF | d5be Hangul Syllable-D5BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c1 Hangul Syllable-D5C1 | d5c0 Hangul Syllable-D5C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c3 Hangul Syllable-D5C3 | d5c2 Hangul Syllable-D5C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c5 Hangul Syllable-D5C5 | d5c4 Hangul Syllable-D5C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c7 Hangul Syllable-D5C7 | d5c6 Hangul Syllable-D5C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5c9 Hangul Syllable-D5C9 | d5c8 Hangul Syllable-D5C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cb Hangul Syllable-D5CB | d5ca Hangul Syllable-D5CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cd Hangul Syllable-D5CD | d5cc Hangul Syllable-D5CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5cf Hangul Syllable-D5CF | d5ce Hangul Syllable-D5CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d1 Hangul Syllable-D5D1 | d5d0 Hangul Syllable-D5D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d3 Hangul Syllable-D5D3 | d5d2 Hangul Syllable-D5D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d5 Hangul Syllable-D5D5 | d5d4 Hangul Syllable-D5D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d7 Hangul Syllable-D5D7 | d5d6 Hangul Syllable-D5D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5d9 Hangul Syllable-D5D9 | d5d8 Hangul Syllable-D5D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5db Hangul Syllable-D5DB | d5da Hangul Syllable-D5DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5dd Hangul Syllable-D5DD | d5dc Hangul Syllable-D5DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5df Hangul Syllable-D5DF | d5de Hangul Syllable-D5DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e1 Hangul Syllable-D5E1 | d5e0 Hangul Syllable-D5E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e3 Hangul Syllable-D5E3 | d5e2 Hangul Syllable-D5E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e5 Hangul Syllable-D5E5 | d5e4 Hangul Syllable-D5E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e7 Hangul Syllable-D5E7 | d5e6 Hangul Syllable-D5E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5e9 Hangul Syllable-D5E9 | d5e8 Hangul Syllable-D5E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5eb Hangul Syllable-D5EB | d5ea Hangul Syllable-D5EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ed Hangul Syllable-D5ED | d5ec Hangul Syllable-D5EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ef Hangul Syllable-D5EF | d5ee Hangul Syllable-D5EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f1 Hangul Syllable-D5F1 | d5f0 Hangul Syllable-D5F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f3 Hangul Syllable-D5F3 | d5f2 Hangul Syllable-D5F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f5 Hangul Syllable-D5F5 | d5f4 Hangul Syllable-D5F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f7 Hangul Syllable-D5F7 | d5f6 Hangul Syllable-D5F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5f9 Hangul Syllable-D5F9 | d5f8 Hangul Syllable-D5F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5fb Hangul Syllable-D5FB | d5fa Hangul Syllable-D5FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5fd Hangul Syllable-D5FD | d5fc Hangul Syllable-D5FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d5ff Hangul Syllable-D5FF | d5fe Hangul Syllable-D5FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d601 Hangul Syllable-D601 | d600 Hangul Syllable-D600 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d603 Hangul Syllable-D603 | d602 Hangul Syllable-D602 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d605 Hangul Syllable-D605 | d604 Hangul Syllable-D604 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d607 Hangul Syllable-D607 | d606 Hangul Syllable-D606 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d609 Hangul Syllable-D609 | d608 Hangul Syllable-D608 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60b Hangul Syllable-D60B | d60a Hangul Syllable-D60A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60d Hangul Syllable-D60D | d60c Hangul Syllable-D60C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d60f Hangul Syllable-D60F | d60e Hangul Syllable-D60E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d611 Hangul Syllable-D611 | d610 Hangul Syllable-D610 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d613 Hangul Syllable-D613 | d612 Hangul Syllable-D612 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d615 Hangul Syllable-D615 | d614 Hangul Syllable-D614 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d617 Hangul Syllable-D617 | d616 Hangul Syllable-D616 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d619 Hangul Syllable-D619 | d618 Hangul Syllable-D618 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61b Hangul Syllable-D61B | d61a Hangul Syllable-D61A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61d Hangul Syllable-D61D | d61c Hangul Syllable-D61C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d61f Hangul Syllable-D61F | d61e Hangul Syllable-D61E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d621 Hangul Syllable-D621 | d620 Hangul Syllable-D620 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d623 Hangul Syllable-D623 | d622 Hangul Syllable-D622 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d625 Hangul Syllable-D625 | d624 Hangul Syllable-D624 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d627 Hangul Syllable-D627 | d626 Hangul Syllable-D626 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d629 Hangul Syllable-D629 | d628 Hangul Syllable-D628 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62b Hangul Syllable-D62B | d62a Hangul Syllable-D62A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62d Hangul Syllable-D62D | d62c Hangul Syllable-D62C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d62f Hangul Syllable-D62F | d62e Hangul Syllable-D62E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d631 Hangul Syllable-D631 | d630 Hangul Syllable-D630 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d633 Hangul Syllable-D633 | d632 Hangul Syllable-D632 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d635 Hangul Syllable-D635 | d634 Hangul Syllable-D634 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d637 Hangul Syllable-D637 | d636 Hangul Syllable-D636 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d639 Hangul Syllable-D639 | d638 Hangul Syllable-D638 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63b Hangul Syllable-D63B | d63a Hangul Syllable-D63A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63d Hangul Syllable-D63D | d63c Hangul Syllable-D63C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d63f Hangul Syllable-D63F | d63e Hangul Syllable-D63E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d641 Hangul Syllable-D641 | d640 Hangul Syllable-D640 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d643 Hangul Syllable-D643 | d642 Hangul Syllable-D642 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d645 Hangul Syllable-D645 | d644 Hangul Syllable-D644 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d647 Hangul Syllable-D647 | d646 Hangul Syllable-D646 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d649 Hangul Syllable-D649 | d648 Hangul Syllable-D648 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64b Hangul Syllable-D64B | d64a Hangul Syllable-D64A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64d Hangul Syllable-D64D | d64c Hangul Syllable-D64C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d64f Hangul Syllable-D64F | d64e Hangul Syllable-D64E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d651 Hangul Syllable-D651 | d650 Hangul Syllable-D650 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d653 Hangul Syllable-D653 | d652 Hangul Syllable-D652 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d655 Hangul Syllable-D655 | d654 Hangul Syllable-D654 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d657 Hangul Syllable-D657 | d656 Hangul Syllable-D656 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d659 Hangul Syllable-D659 | d658 Hangul Syllable-D658 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65b Hangul Syllable-D65B | d65a Hangul Syllable-D65A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65d Hangul Syllable-D65D | d65c Hangul Syllable-D65C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d65f Hangul Syllable-D65F | d65e Hangul Syllable-D65E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d661 Hangul Syllable-D661 | d660 Hangul Syllable-D660 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d663 Hangul Syllable-D663 | d662 Hangul Syllable-D662 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d665 Hangul Syllable-D665 | d664 Hangul Syllable-D664 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d667 Hangul Syllable-D667 | d666 Hangul Syllable-D666 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d669 Hangul Syllable-D669 | d668 Hangul Syllable-D668 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66b Hangul Syllable-D66B | d66a Hangul Syllable-D66A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66d Hangul Syllable-D66D | d66c Hangul Syllable-D66C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d66f Hangul Syllable-D66F | d66e Hangul Syllable-D66E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d671 Hangul Syllable-D671 | d670 Hangul Syllable-D670 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d673 Hangul Syllable-D673 | d672 Hangul Syllable-D672 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d675 Hangul Syllable-D675 | d674 Hangul Syllable-D674 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d677 Hangul Syllable-D677 | d676 Hangul Syllable-D676 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d679 Hangul Syllable-D679 | d678 Hangul Syllable-D678 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67b Hangul Syllable-D67B | d67a Hangul Syllable-D67A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67d Hangul Syllable-D67D | d67c Hangul Syllable-D67C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d67f Hangul Syllable-D67F | d67e Hangul Syllable-D67E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d681 Hangul Syllable-D681 | d680 Hangul Syllable-D680 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d683 Hangul Syllable-D683 | d682 Hangul Syllable-D682 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d685 Hangul Syllable-D685 | d684 Hangul Syllable-D684 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d687 Hangul Syllable-D687 | d686 Hangul Syllable-D686 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d689 Hangul Syllable-D689 | d688 Hangul Syllable-D688 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68b Hangul Syllable-D68B | d68a Hangul Syllable-D68A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68d Hangul Syllable-D68D | d68c Hangul Syllable-D68C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d68f Hangul Syllable-D68F | d68e Hangul Syllable-D68E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d691 Hangul Syllable-D691 | d690 Hangul Syllable-D690 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d693 Hangul Syllable-D693 | d692 Hangul Syllable-D692 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d695 Hangul Syllable-D695 | d694 Hangul Syllable-D694 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d697 Hangul Syllable-D697 | d696 Hangul Syllable-D696 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d699 Hangul Syllable-D699 | d698 Hangul Syllable-D698 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69b Hangul Syllable-D69B | d69a Hangul Syllable-D69A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69d Hangul Syllable-D69D | d69c Hangul Syllable-D69C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d69f Hangul Syllable-D69F | d69e Hangul Syllable-D69E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a1 Hangul Syllable-D6A1 | d6a0 Hangul Syllable-D6A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a3 Hangul Syllable-D6A3 | d6a2 Hangul Syllable-D6A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a5 Hangul Syllable-D6A5 | d6a4 Hangul Syllable-D6A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a7 Hangul Syllable-D6A7 | d6a6 Hangul Syllable-D6A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6a9 Hangul Syllable-D6A9 | d6a8 Hangul Syllable-D6A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ab Hangul Syllable-D6AB | d6aa Hangul Syllable-D6AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ad Hangul Syllable-D6AD | d6ac Hangul Syllable-D6AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6af Hangul Syllable-D6AF | d6ae Hangul Syllable-D6AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b1 Hangul Syllable-D6B1 | d6b0 Hangul Syllable-D6B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b3 Hangul Syllable-D6B3 | d6b2 Hangul Syllable-D6B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b5 Hangul Syllable-D6B5 | d6b4 Hangul Syllable-D6B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b7 Hangul Syllable-D6B7 | d6b6 Hangul Syllable-D6B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6b9 Hangul Syllable-D6B9 | d6b8 Hangul Syllable-D6B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bb Hangul Syllable-D6BB | d6ba Hangul Syllable-D6BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bd Hangul Syllable-D6BD | d6bc Hangul Syllable-D6BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6bf Hangul Syllable-D6BF | d6be Hangul Syllable-D6BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c1 Hangul Syllable-D6C1 | d6c0 Hangul Syllable-D6C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c3 Hangul Syllable-D6C3 | d6c2 Hangul Syllable-D6C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c5 Hangul Syllable-D6C5 | d6c4 Hangul Syllable-D6C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c7 Hangul Syllable-D6C7 | d6c6 Hangul Syllable-D6C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6c9 Hangul Syllable-D6C9 | d6c8 Hangul Syllable-D6C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cb Hangul Syllable-D6CB | d6ca Hangul Syllable-D6CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cd Hangul Syllable-D6CD | d6cc Hangul Syllable-D6CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6cf Hangul Syllable-D6CF | d6ce Hangul Syllable-D6CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d1 Hangul Syllable-D6D1 | d6d0 Hangul Syllable-D6D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d3 Hangul Syllable-D6D3 | d6d2 Hangul Syllable-D6D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d5 Hangul Syllable-D6D5 | d6d4 Hangul Syllable-D6D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d7 Hangul Syllable-D6D7 | d6d6 Hangul Syllable-D6D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6d9 Hangul Syllable-D6D9 | d6d8 Hangul Syllable-D6D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6db Hangul Syllable-D6DB | d6da Hangul Syllable-D6DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6dd Hangul Syllable-D6DD | d6dc Hangul Syllable-D6DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6df Hangul Syllable-D6DF | d6de Hangul Syllable-D6DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e1 Hangul Syllable-D6E1 | d6e0 Hangul Syllable-D6E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e3 Hangul Syllable-D6E3 | d6e2 Hangul Syllable-D6E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e5 Hangul Syllable-D6E5 | d6e4 Hangul Syllable-D6E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e7 Hangul Syllable-D6E7 | d6e6 Hangul Syllable-D6E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6e9 Hangul Syllable-D6E9 | d6e8 Hangul Syllable-D6E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6eb Hangul Syllable-D6EB | d6ea Hangul Syllable-D6EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ed Hangul Syllable-D6ED | d6ec Hangul Syllable-D6EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ef Hangul Syllable-D6EF | d6ee Hangul Syllable-D6EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f1 Hangul Syllable-D6F1 | d6f0 Hangul Syllable-D6F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f3 Hangul Syllable-D6F3 | d6f2 Hangul Syllable-D6F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f5 Hangul Syllable-D6F5 | d6f4 Hangul Syllable-D6F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f7 Hangul Syllable-D6F7 | d6f6 Hangul Syllable-D6F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6f9 Hangul Syllable-D6F9 | d6f8 Hangul Syllable-D6F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6fb Hangul Syllable-D6FB | d6fa Hangul Syllable-D6FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6fd Hangul Syllable-D6FD | d6fc Hangul Syllable-D6FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d6ff Hangul Syllable-D6FF | d6fe Hangul Syllable-D6FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d701 Hangul Syllable-D701 | d700 Hangul Syllable-D700 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d703 Hangul Syllable-D703 | d702 Hangul Syllable-D702 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d705 Hangul Syllable-D705 | d704 Hangul Syllable-D704 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d707 Hangul Syllable-D707 | d706 Hangul Syllable-D706 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d709 Hangul Syllable-D709 | d708 Hangul Syllable-D708 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70b Hangul Syllable-D70B | d70a Hangul Syllable-D70A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70d Hangul Syllable-D70D | d70c Hangul Syllable-D70C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d70f Hangul Syllable-D70F | d70e Hangul Syllable-D70E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d711 Hangul Syllable-D711 | d710 Hangul Syllable-D710 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d713 Hangul Syllable-D713 | d712 Hangul Syllable-D712 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d715 Hangul Syllable-D715 | d714 Hangul Syllable-D714 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d717 Hangul Syllable-D717 | d716 Hangul Syllable-D716 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d719 Hangul Syllable-D719 | d718 Hangul Syllable-D718 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71b Hangul Syllable-D71B | d71a Hangul Syllable-D71A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71d Hangul Syllable-D71D | d71c Hangul Syllable-D71C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d71f Hangul Syllable-D71F | d71e Hangul Syllable-D71E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d721 Hangul Syllable-D721 | d720 Hangul Syllable-D720 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d723 Hangul Syllable-D723 | d722 Hangul Syllable-D722 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d725 Hangul Syllable-D725 | d724 Hangul Syllable-D724 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d727 Hangul Syllable-D727 | d726 Hangul Syllable-D726 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d729 Hangul Syllable-D729 | d728 Hangul Syllable-D728 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72b Hangul Syllable-D72B | d72a Hangul Syllable-D72A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72d Hangul Syllable-D72D | d72c Hangul Syllable-D72C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d72f Hangul Syllable-D72F | d72e Hangul Syllable-D72E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d731 Hangul Syllable-D731 | d730 Hangul Syllable-D730 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d733 Hangul Syllable-D733 | d732 Hangul Syllable-D732 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d735 Hangul Syllable-D735 | d734 Hangul Syllable-D734 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d737 Hangul Syllable-D737 | d736 Hangul Syllable-D736 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d739 Hangul Syllable-D739 | d738 Hangul Syllable-D738 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73b Hangul Syllable-D73B | d73a Hangul Syllable-D73A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73d Hangul Syllable-D73D | d73c Hangul Syllable-D73C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d73f Hangul Syllable-D73F | d73e Hangul Syllable-D73E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d741 Hangul Syllable-D741 | d740 Hangul Syllable-D740 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d743 Hangul Syllable-D743 | d742 Hangul Syllable-D742 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d745 Hangul Syllable-D745 | d744 Hangul Syllable-D744 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d747 Hangul Syllable-D747 | d746 Hangul Syllable-D746 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d749 Hangul Syllable-D749 | d748 Hangul Syllable-D748 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74b Hangul Syllable-D74B | d74a Hangul Syllable-D74A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74d Hangul Syllable-D74D | d74c Hangul Syllable-D74C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d74f Hangul Syllable-D74F | d74e Hangul Syllable-D74E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d751 Hangul Syllable-D751 | d750 Hangul Syllable-D750 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d753 Hangul Syllable-D753 | d752 Hangul Syllable-D752 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d755 Hangul Syllable-D755 | d754 Hangul Syllable-D754 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d757 Hangul Syllable-D757 | d756 Hangul Syllable-D756 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d759 Hangul Syllable-D759 | d758 Hangul Syllable-D758 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75b Hangul Syllable-D75B | d75a Hangul Syllable-D75A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75d Hangul Syllable-D75D | d75c Hangul Syllable-D75C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d75f Hangul Syllable-D75F | d75e Hangul Syllable-D75E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d761 Hangul Syllable-D761 | d760 Hangul Syllable-D760 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d763 Hangul Syllable-D763 | d762 Hangul Syllable-D762 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d765 Hangul Syllable-D765 | d764 Hangul Syllable-D764 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d767 Hangul Syllable-D767 | d766 Hangul Syllable-D766 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d769 Hangul Syllable-D769 | d768 Hangul Syllable-D768 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76b Hangul Syllable-D76B | d76a Hangul Syllable-D76A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76d Hangul Syllable-D76D | d76c Hangul Syllable-D76C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d76f Hangul Syllable-D76F | d76e Hangul Syllable-D76E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d771 Hangul Syllable-D771 | d770 Hangul Syllable-D770 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d773 Hangul Syllable-D773 | d772 Hangul Syllable-D772 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d775 Hangul Syllable-D775 | d774 Hangul Syllable-D774 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d777 Hangul Syllable-D777 | d776 Hangul Syllable-D776 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d779 Hangul Syllable-D779 | d778 Hangul Syllable-D778 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77b Hangul Syllable-D77B | d77a Hangul Syllable-D77A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77d Hangul Syllable-D77D | d77c Hangul Syllable-D77C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d77f Hangul Syllable-D77F | d77e Hangul Syllable-D77E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d781 Hangul Syllable-D781 | d780 Hangul Syllable-D780 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d783 Hangul Syllable-D783 | d782 Hangul Syllable-D782 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d785 Hangul Syllable-D785 | d784 Hangul Syllable-D784 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d787 Hangul Syllable-D787 | d786 Hangul Syllable-D786 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d789 Hangul Syllable-D789 | d788 Hangul Syllable-D788 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78b Hangul Syllable-D78B | d78a Hangul Syllable-D78A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78d Hangul Syllable-D78D | d78c Hangul Syllable-D78C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d78f Hangul Syllable-D78F | d78e Hangul Syllable-D78E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d791 Hangul Syllable-D791 | d790 Hangul Syllable-D790 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d793 Hangul Syllable-D793 | d792 Hangul Syllable-D792 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d795 Hangul Syllable-D795 | d794 Hangul Syllable-D794 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d797 Hangul Syllable-D797 | d796 Hangul Syllable-D796 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d799 Hangul Syllable-D799 | d798 Hangul Syllable-D798 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79b Hangul Syllable-D79B | d79a Hangul Syllable-D79A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79d Hangul Syllable-D79D | d79c Hangul Syllable-D79C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d79f Hangul Syllable-D79F | d79e Hangul Syllable-D79E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7a1 Hangul Syllable-D7A1 | d7a0 Hangul Syllable-D7A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7a3 Hangul Syllable-D7A3 | d7a2 Hangul Syllable-D7A2 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a5 (null) | d7a4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a7 (null) | d7a6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7a9 (null) | d7a8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ab (null) | d7aa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ad (null) | d7ac (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7af (null) | d7ae (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b1 HANGUL JUNGSEONG O-O-I | d7b0 HANGUL JUNGSEONG O-YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b3 HANGUL JUNGSEONG YO-AE | d7b2 HANGUL JUNGSEONG YO-A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b5 HANGUL JUNGSEONG U-YEO | d7b4 HANGUL JUNGSEONG YO-EO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b7 HANGUL JUNGSEONG YU-AE | d7b6 HANGUL JUNGSEONG U-I-I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7b9 HANGUL JUNGSEONG EU-A | d7b8 HANGUL JUNGSEONG YU-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bb HANGUL JUNGSEONG EU-E | d7ba HANGUL JUNGSEONG EU-EO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bd HANGUL JUNGSEONG I-YA-O | d7bc HANGUL JUNGSEONG EU-O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7bf HANGUL JUNGSEONG I-YEO | d7be HANGUL JUNGSEONG I-YAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c1 HANGUL JUNGSEONG I-O-I | d7c0 HANGUL JUNGSEONG I-YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c3 HANGUL JUNGSEONG I-YU | d7c2 HANGUL JUNGSEONG I-YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7c5 HANGUL JUNGSEONG ARAEA-A | d7c4 HANGUL JUNGSEONG I-I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* d7c7 (null) | d7c6 HANGUL JUNGSEONG ARAEA-E */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7c9 (null) | d7c8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* d7cb HANGUL JONGSEONG NIEUN-RIEUL | d7ca (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7cd HANGUL JONGSEONG SSANGTIKEUT | d7cc HANGUL JONGSEONG NIEUN-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7cf HANGUL JONGSEONG TIKEUT-PIEUP | d7ce HANGUL JONGSEONG SSANGTIKEUT-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d1 HANGUL JONGSEONG TIKEUT-SIOS-KIYEOK | d7d0 HANGUL JONGSEONG TIKEUT-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d3 HANGUL JONGSEONG TIKEUT-CHIEUCH | d7d2 HANGUL JONGSEONG TIKEUT-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d5 HANGUL JONGSEONG RIEUL-SSANGKIYEOK | d7d4 HANGUL JONGSEONG TIKEUT-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d7 HANGUL JONGSEONG SSANGRIEUL-KHIEUKH | d7d6 HANGUL JONGSEONG RIEUL-KIYEOK-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7d9 HANGUL JONGSEONG RIEUL-PIEUP-TIKEUT | d7d8 HANGUL JONGSEONG RIEUL-MIEUM-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7db HANGUL JONGSEONG RIEUL-YESIEUNG | d7da HANGUL JONGSEONG RIEUL-PIEUP-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7dd HANGUL JONGSEONG KAPYEOUNRIEUL | d7dc HANGUL JONGSEONG RIEUL-YEORINHIEUH-HIEU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7df HANGUL JONGSEONG MIEUM-SSANGNIEUN | d7de HANGUL JONGSEONG MIEUM-NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e1 HANGUL JONGSEONG MIEUM-PIEUP-SIOS | d7e0 HANGUL JONGSEONG SSANGMIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e3 HANGUL JONGSEONG PIEUP-TIKEUT | d7e2 HANGUL JONGSEONG MIEUM-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e5 HANGUL JONGSEONG PIEUP-MIEUM | d7e4 HANGUL JONGSEONG PIEUP-RIEUL-PHIEUPH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e7 HANGUL JONGSEONG PIEUP-SIOS-TIKEUT | d7e6 HANGUL JONGSEONG SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7e9 HANGUL JONGSEONG PIEUP-CHIEUCH | d7e8 HANGUL JONGSEONG PIEUP-CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7eb HANGUL JONGSEONG SIOS-KAPYEOUNPIEUP | d7ea HANGUL JONGSEONG SIOS-MIEUM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7ed HANGUL JONGSEONG SSANGSIOS-TIKEUT | d7ec HANGUL JONGSEONG SSANGSIOS-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7ef HANGUL JONGSEONG SIOS-CIEUC | d7ee HANGUL JONGSEONG SIOS-PANSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f1 HANGUL JONGSEONG SIOS-THIEUTH | d7f0 HANGUL JONGSEONG SIOS-CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f3 HANGUL JONGSEONG PANSIOS-PIEUP | d7f2 HANGUL JONGSEONG SIOS-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f5 HANGUL JONGSEONG YESIEUNG-MIEUM | d7f4 HANGUL JONGSEONG PANSIOS-KAPYEOUNPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f7 HANGUL JONGSEONG CIEUC-PIEUP | d7f6 HANGUL JONGSEONG YESIEUNG-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7f9 HANGUL JONGSEONG SSANGCIEUC | d7f8 HANGUL JONGSEONG CIEUC-SSANGPIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* d7fb HANGUL JONGSEONG PHIEUPH-THIEUTH | d7fa HANGUL JONGSEONG PHIEUPH-SIOS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7fd (null) | d7fc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* d7ff (null) | d7fe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d801 Non Private Use High Surrogate-D801 | d800 Non Private Use High Surrogate-D800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d803 Non Private Use High Surrogate-D803 | d802 Non Private Use High Surrogate-D802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d805 Non Private Use High Surrogate-D805 | d804 Non Private Use High Surrogate-D804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d807 Non Private Use High Surrogate-D807 | d806 Non Private Use High Surrogate-D806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d809 Non Private Use High Surrogate-D809 | d808 Non Private Use High Surrogate-D808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80b Non Private Use High Surrogate-D80B | d80a Non Private Use High Surrogate-D80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80d Non Private Use High Surrogate-D80D | d80c Non Private Use High Surrogate-D80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d80f Non Private Use High Surrogate-D80F | d80e Non Private Use High Surrogate-D80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d811 Non Private Use High Surrogate-D811 | d810 Non Private Use High Surrogate-D810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d813 Non Private Use High Surrogate-D813 | d812 Non Private Use High Surrogate-D812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d815 Non Private Use High Surrogate-D815 | d814 Non Private Use High Surrogate-D814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d817 Non Private Use High Surrogate-D817 | d816 Non Private Use High Surrogate-D816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d819 Non Private Use High Surrogate-D819 | d818 Non Private Use High Surrogate-D818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81b Non Private Use High Surrogate-D81B | d81a Non Private Use High Surrogate-D81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81d Non Private Use High Surrogate-D81D | d81c Non Private Use High Surrogate-D81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d81f Non Private Use High Surrogate-D81F | d81e Non Private Use High Surrogate-D81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d821 Non Private Use High Surrogate-D821 | d820 Non Private Use High Surrogate-D820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d823 Non Private Use High Surrogate-D823 | d822 Non Private Use High Surrogate-D822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d825 Non Private Use High Surrogate-D825 | d824 Non Private Use High Surrogate-D824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d827 Non Private Use High Surrogate-D827 | d826 Non Private Use High Surrogate-D826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d829 Non Private Use High Surrogate-D829 | d828 Non Private Use High Surrogate-D828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82b Non Private Use High Surrogate-D82B | d82a Non Private Use High Surrogate-D82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82d Non Private Use High Surrogate-D82D | d82c Non Private Use High Surrogate-D82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d82f Non Private Use High Surrogate-D82F | d82e Non Private Use High Surrogate-D82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d831 Non Private Use High Surrogate-D831 | d830 Non Private Use High Surrogate-D830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d833 Non Private Use High Surrogate-D833 | d832 Non Private Use High Surrogate-D832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d835 Non Private Use High Surrogate-D835 | d834 Non Private Use High Surrogate-D834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d837 Non Private Use High Surrogate-D837 | d836 Non Private Use High Surrogate-D836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d839 Non Private Use High Surrogate-D839 | d838 Non Private Use High Surrogate-D838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83b Non Private Use High Surrogate-D83B | d83a Non Private Use High Surrogate-D83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83d Non Private Use High Surrogate-D83D | d83c Non Private Use High Surrogate-D83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d83f Non Private Use High Surrogate-D83F | d83e Non Private Use High Surrogate-D83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d841 Non Private Use High Surrogate-D841 | d840 Non Private Use High Surrogate-D840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d843 Non Private Use High Surrogate-D843 | d842 Non Private Use High Surrogate-D842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d845 Non Private Use High Surrogate-D845 | d844 Non Private Use High Surrogate-D844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d847 Non Private Use High Surrogate-D847 | d846 Non Private Use High Surrogate-D846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d849 Non Private Use High Surrogate-D849 | d848 Non Private Use High Surrogate-D848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84b Non Private Use High Surrogate-D84B | d84a Non Private Use High Surrogate-D84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84d Non Private Use High Surrogate-D84D | d84c Non Private Use High Surrogate-D84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d84f Non Private Use High Surrogate-D84F | d84e Non Private Use High Surrogate-D84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d851 Non Private Use High Surrogate-D851 | d850 Non Private Use High Surrogate-D850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d853 Non Private Use High Surrogate-D853 | d852 Non Private Use High Surrogate-D852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d855 Non Private Use High Surrogate-D855 | d854 Non Private Use High Surrogate-D854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d857 Non Private Use High Surrogate-D857 | d856 Non Private Use High Surrogate-D856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d859 Non Private Use High Surrogate-D859 | d858 Non Private Use High Surrogate-D858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85b Non Private Use High Surrogate-D85B | d85a Non Private Use High Surrogate-D85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85d Non Private Use High Surrogate-D85D | d85c Non Private Use High Surrogate-D85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d85f Non Private Use High Surrogate-D85F | d85e Non Private Use High Surrogate-D85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d861 Non Private Use High Surrogate-D861 | d860 Non Private Use High Surrogate-D860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d863 Non Private Use High Surrogate-D863 | d862 Non Private Use High Surrogate-D862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d865 Non Private Use High Surrogate-D865 | d864 Non Private Use High Surrogate-D864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d867 Non Private Use High Surrogate-D867 | d866 Non Private Use High Surrogate-D866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d869 Non Private Use High Surrogate-D869 | d868 Non Private Use High Surrogate-D868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86b Non Private Use High Surrogate-D86B | d86a Non Private Use High Surrogate-D86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86d Non Private Use High Surrogate-D86D | d86c Non Private Use High Surrogate-D86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d86f Non Private Use High Surrogate-D86F | d86e Non Private Use High Surrogate-D86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d871 Non Private Use High Surrogate-D871 | d870 Non Private Use High Surrogate-D870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d873 Non Private Use High Surrogate-D873 | d872 Non Private Use High Surrogate-D872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d875 Non Private Use High Surrogate-D875 | d874 Non Private Use High Surrogate-D874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d877 Non Private Use High Surrogate-D877 | d876 Non Private Use High Surrogate-D876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d879 Non Private Use High Surrogate-D879 | d878 Non Private Use High Surrogate-D878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87b Non Private Use High Surrogate-D87B | d87a Non Private Use High Surrogate-D87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87d Non Private Use High Surrogate-D87D | d87c Non Private Use High Surrogate-D87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d87f Non Private Use High Surrogate-D87F | d87e Non Private Use High Surrogate-D87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d881 Non Private Use High Surrogate-D881 | d880 Non Private Use High Surrogate-D880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d883 Non Private Use High Surrogate-D883 | d882 Non Private Use High Surrogate-D882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d885 Non Private Use High Surrogate-D885 | d884 Non Private Use High Surrogate-D884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d887 Non Private Use High Surrogate-D887 | d886 Non Private Use High Surrogate-D886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d889 Non Private Use High Surrogate-D889 | d888 Non Private Use High Surrogate-D888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88b Non Private Use High Surrogate-D88B | d88a Non Private Use High Surrogate-D88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88d Non Private Use High Surrogate-D88D | d88c Non Private Use High Surrogate-D88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d88f Non Private Use High Surrogate-D88F | d88e Non Private Use High Surrogate-D88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d891 Non Private Use High Surrogate-D891 | d890 Non Private Use High Surrogate-D890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d893 Non Private Use High Surrogate-D893 | d892 Non Private Use High Surrogate-D892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d895 Non Private Use High Surrogate-D895 | d894 Non Private Use High Surrogate-D894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d897 Non Private Use High Surrogate-D897 | d896 Non Private Use High Surrogate-D896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d899 Non Private Use High Surrogate-D899 | d898 Non Private Use High Surrogate-D898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89b Non Private Use High Surrogate-D89B | d89a Non Private Use High Surrogate-D89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89d Non Private Use High Surrogate-D89D | d89c Non Private Use High Surrogate-D89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d89f Non Private Use High Surrogate-D89F | d89e Non Private Use High Surrogate-D89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a1 Non Private Use High Surrogate-D8A1 | d8a0 Non Private Use High Surrogate-D8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a3 Non Private Use High Surrogate-D8A3 | d8a2 Non Private Use High Surrogate-D8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a5 Non Private Use High Surrogate-D8A5 | d8a4 Non Private Use High Surrogate-D8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a7 Non Private Use High Surrogate-D8A7 | d8a6 Non Private Use High Surrogate-D8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8a9 Non Private Use High Surrogate-D8A9 | d8a8 Non Private Use High Surrogate-D8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ab Non Private Use High Surrogate-D8AB | d8aa Non Private Use High Surrogate-D8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ad Non Private Use High Surrogate-D8AD | d8ac Non Private Use High Surrogate-D8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8af Non Private Use High Surrogate-D8AF | d8ae Non Private Use High Surrogate-D8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b1 Non Private Use High Surrogate-D8B1 | d8b0 Non Private Use High Surrogate-D8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b3 Non Private Use High Surrogate-D8B3 | d8b2 Non Private Use High Surrogate-D8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b5 Non Private Use High Surrogate-D8B5 | d8b4 Non Private Use High Surrogate-D8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b7 Non Private Use High Surrogate-D8B7 | d8b6 Non Private Use High Surrogate-D8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8b9 Non Private Use High Surrogate-D8B9 | d8b8 Non Private Use High Surrogate-D8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bb Non Private Use High Surrogate-D8BB | d8ba Non Private Use High Surrogate-D8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bd Non Private Use High Surrogate-D8BD | d8bc Non Private Use High Surrogate-D8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8bf Non Private Use High Surrogate-D8BF | d8be Non Private Use High Surrogate-D8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c1 Non Private Use High Surrogate-D8C1 | d8c0 Non Private Use High Surrogate-D8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c3 Non Private Use High Surrogate-D8C3 | d8c2 Non Private Use High Surrogate-D8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c5 Non Private Use High Surrogate-D8C5 | d8c4 Non Private Use High Surrogate-D8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c7 Non Private Use High Surrogate-D8C7 | d8c6 Non Private Use High Surrogate-D8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8c9 Non Private Use High Surrogate-D8C9 | d8c8 Non Private Use High Surrogate-D8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cb Non Private Use High Surrogate-D8CB | d8ca Non Private Use High Surrogate-D8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cd Non Private Use High Surrogate-D8CD | d8cc Non Private Use High Surrogate-D8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8cf Non Private Use High Surrogate-D8CF | d8ce Non Private Use High Surrogate-D8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d1 Non Private Use High Surrogate-D8D1 | d8d0 Non Private Use High Surrogate-D8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d3 Non Private Use High Surrogate-D8D3 | d8d2 Non Private Use High Surrogate-D8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d5 Non Private Use High Surrogate-D8D5 | d8d4 Non Private Use High Surrogate-D8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d7 Non Private Use High Surrogate-D8D7 | d8d6 Non Private Use High Surrogate-D8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8d9 Non Private Use High Surrogate-D8D9 | d8d8 Non Private Use High Surrogate-D8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8db Non Private Use High Surrogate-D8DB | d8da Non Private Use High Surrogate-D8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8dd Non Private Use High Surrogate-D8DD | d8dc Non Private Use High Surrogate-D8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8df Non Private Use High Surrogate-D8DF | d8de Non Private Use High Surrogate-D8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e1 Non Private Use High Surrogate-D8E1 | d8e0 Non Private Use High Surrogate-D8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e3 Non Private Use High Surrogate-D8E3 | d8e2 Non Private Use High Surrogate-D8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e5 Non Private Use High Surrogate-D8E5 | d8e4 Non Private Use High Surrogate-D8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e7 Non Private Use High Surrogate-D8E7 | d8e6 Non Private Use High Surrogate-D8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8e9 Non Private Use High Surrogate-D8E9 | d8e8 Non Private Use High Surrogate-D8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8eb Non Private Use High Surrogate-D8EB | d8ea Non Private Use High Surrogate-D8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ed Non Private Use High Surrogate-D8ED | d8ec Non Private Use High Surrogate-D8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ef Non Private Use High Surrogate-D8EF | d8ee Non Private Use High Surrogate-D8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f1 Non Private Use High Surrogate-D8F1 | d8f0 Non Private Use High Surrogate-D8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f3 Non Private Use High Surrogate-D8F3 | d8f2 Non Private Use High Surrogate-D8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f5 Non Private Use High Surrogate-D8F5 | d8f4 Non Private Use High Surrogate-D8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f7 Non Private Use High Surrogate-D8F7 | d8f6 Non Private Use High Surrogate-D8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8f9 Non Private Use High Surrogate-D8F9 | d8f8 Non Private Use High Surrogate-D8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8fb Non Private Use High Surrogate-D8FB | d8fa Non Private Use High Surrogate-D8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8fd Non Private Use High Surrogate-D8FD | d8fc Non Private Use High Surrogate-D8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d8ff Non Private Use High Surrogate-D8FF | d8fe Non Private Use High Surrogate-D8FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d901 Non Private Use High Surrogate-D901 | d900 Non Private Use High Surrogate-D900 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d903 Non Private Use High Surrogate-D903 | d902 Non Private Use High Surrogate-D902 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d905 Non Private Use High Surrogate-D905 | d904 Non Private Use High Surrogate-D904 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d907 Non Private Use High Surrogate-D907 | d906 Non Private Use High Surrogate-D906 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d909 Non Private Use High Surrogate-D909 | d908 Non Private Use High Surrogate-D908 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90b Non Private Use High Surrogate-D90B | d90a Non Private Use High Surrogate-D90A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90d Non Private Use High Surrogate-D90D | d90c Non Private Use High Surrogate-D90C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d90f Non Private Use High Surrogate-D90F | d90e Non Private Use High Surrogate-D90E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d911 Non Private Use High Surrogate-D911 | d910 Non Private Use High Surrogate-D910 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d913 Non Private Use High Surrogate-D913 | d912 Non Private Use High Surrogate-D912 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d915 Non Private Use High Surrogate-D915 | d914 Non Private Use High Surrogate-D914 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d917 Non Private Use High Surrogate-D917 | d916 Non Private Use High Surrogate-D916 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d919 Non Private Use High Surrogate-D919 | d918 Non Private Use High Surrogate-D918 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91b Non Private Use High Surrogate-D91B | d91a Non Private Use High Surrogate-D91A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91d Non Private Use High Surrogate-D91D | d91c Non Private Use High Surrogate-D91C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d91f Non Private Use High Surrogate-D91F | d91e Non Private Use High Surrogate-D91E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d921 Non Private Use High Surrogate-D921 | d920 Non Private Use High Surrogate-D920 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d923 Non Private Use High Surrogate-D923 | d922 Non Private Use High Surrogate-D922 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d925 Non Private Use High Surrogate-D925 | d924 Non Private Use High Surrogate-D924 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d927 Non Private Use High Surrogate-D927 | d926 Non Private Use High Surrogate-D926 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d929 Non Private Use High Surrogate-D929 | d928 Non Private Use High Surrogate-D928 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92b Non Private Use High Surrogate-D92B | d92a Non Private Use High Surrogate-D92A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92d Non Private Use High Surrogate-D92D | d92c Non Private Use High Surrogate-D92C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d92f Non Private Use High Surrogate-D92F | d92e Non Private Use High Surrogate-D92E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d931 Non Private Use High Surrogate-D931 | d930 Non Private Use High Surrogate-D930 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d933 Non Private Use High Surrogate-D933 | d932 Non Private Use High Surrogate-D932 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d935 Non Private Use High Surrogate-D935 | d934 Non Private Use High Surrogate-D934 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d937 Non Private Use High Surrogate-D937 | d936 Non Private Use High Surrogate-D936 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d939 Non Private Use High Surrogate-D939 | d938 Non Private Use High Surrogate-D938 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93b Non Private Use High Surrogate-D93B | d93a Non Private Use High Surrogate-D93A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93d Non Private Use High Surrogate-D93D | d93c Non Private Use High Surrogate-D93C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d93f Non Private Use High Surrogate-D93F | d93e Non Private Use High Surrogate-D93E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d941 Non Private Use High Surrogate-D941 | d940 Non Private Use High Surrogate-D940 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d943 Non Private Use High Surrogate-D943 | d942 Non Private Use High Surrogate-D942 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d945 Non Private Use High Surrogate-D945 | d944 Non Private Use High Surrogate-D944 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d947 Non Private Use High Surrogate-D947 | d946 Non Private Use High Surrogate-D946 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d949 Non Private Use High Surrogate-D949 | d948 Non Private Use High Surrogate-D948 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94b Non Private Use High Surrogate-D94B | d94a Non Private Use High Surrogate-D94A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94d Non Private Use High Surrogate-D94D | d94c Non Private Use High Surrogate-D94C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d94f Non Private Use High Surrogate-D94F | d94e Non Private Use High Surrogate-D94E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d951 Non Private Use High Surrogate-D951 | d950 Non Private Use High Surrogate-D950 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d953 Non Private Use High Surrogate-D953 | d952 Non Private Use High Surrogate-D952 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d955 Non Private Use High Surrogate-D955 | d954 Non Private Use High Surrogate-D954 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d957 Non Private Use High Surrogate-D957 | d956 Non Private Use High Surrogate-D956 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d959 Non Private Use High Surrogate-D959 | d958 Non Private Use High Surrogate-D958 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95b Non Private Use High Surrogate-D95B | d95a Non Private Use High Surrogate-D95A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95d Non Private Use High Surrogate-D95D | d95c Non Private Use High Surrogate-D95C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d95f Non Private Use High Surrogate-D95F | d95e Non Private Use High Surrogate-D95E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d961 Non Private Use High Surrogate-D961 | d960 Non Private Use High Surrogate-D960 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d963 Non Private Use High Surrogate-D963 | d962 Non Private Use High Surrogate-D962 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d965 Non Private Use High Surrogate-D965 | d964 Non Private Use High Surrogate-D964 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d967 Non Private Use High Surrogate-D967 | d966 Non Private Use High Surrogate-D966 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d969 Non Private Use High Surrogate-D969 | d968 Non Private Use High Surrogate-D968 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96b Non Private Use High Surrogate-D96B | d96a Non Private Use High Surrogate-D96A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96d Non Private Use High Surrogate-D96D | d96c Non Private Use High Surrogate-D96C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d96f Non Private Use High Surrogate-D96F | d96e Non Private Use High Surrogate-D96E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d971 Non Private Use High Surrogate-D971 | d970 Non Private Use High Surrogate-D970 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d973 Non Private Use High Surrogate-D973 | d972 Non Private Use High Surrogate-D972 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d975 Non Private Use High Surrogate-D975 | d974 Non Private Use High Surrogate-D974 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d977 Non Private Use High Surrogate-D977 | d976 Non Private Use High Surrogate-D976 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d979 Non Private Use High Surrogate-D979 | d978 Non Private Use High Surrogate-D978 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97b Non Private Use High Surrogate-D97B | d97a Non Private Use High Surrogate-D97A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97d Non Private Use High Surrogate-D97D | d97c Non Private Use High Surrogate-D97C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d97f Non Private Use High Surrogate-D97F | d97e Non Private Use High Surrogate-D97E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d981 Non Private Use High Surrogate-D981 | d980 Non Private Use High Surrogate-D980 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d983 Non Private Use High Surrogate-D983 | d982 Non Private Use High Surrogate-D982 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d985 Non Private Use High Surrogate-D985 | d984 Non Private Use High Surrogate-D984 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d987 Non Private Use High Surrogate-D987 | d986 Non Private Use High Surrogate-D986 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d989 Non Private Use High Surrogate-D989 | d988 Non Private Use High Surrogate-D988 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98b Non Private Use High Surrogate-D98B | d98a Non Private Use High Surrogate-D98A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98d Non Private Use High Surrogate-D98D | d98c Non Private Use High Surrogate-D98C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d98f Non Private Use High Surrogate-D98F | d98e Non Private Use High Surrogate-D98E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d991 Non Private Use High Surrogate-D991 | d990 Non Private Use High Surrogate-D990 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d993 Non Private Use High Surrogate-D993 | d992 Non Private Use High Surrogate-D992 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d995 Non Private Use High Surrogate-D995 | d994 Non Private Use High Surrogate-D994 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d997 Non Private Use High Surrogate-D997 | d996 Non Private Use High Surrogate-D996 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d999 Non Private Use High Surrogate-D999 | d998 Non Private Use High Surrogate-D998 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99b Non Private Use High Surrogate-D99B | d99a Non Private Use High Surrogate-D99A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99d Non Private Use High Surrogate-D99D | d99c Non Private Use High Surrogate-D99C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d99f Non Private Use High Surrogate-D99F | d99e Non Private Use High Surrogate-D99E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a1 Non Private Use High Surrogate-D9A1 | d9a0 Non Private Use High Surrogate-D9A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a3 Non Private Use High Surrogate-D9A3 | d9a2 Non Private Use High Surrogate-D9A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a5 Non Private Use High Surrogate-D9A5 | d9a4 Non Private Use High Surrogate-D9A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a7 Non Private Use High Surrogate-D9A7 | d9a6 Non Private Use High Surrogate-D9A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9a9 Non Private Use High Surrogate-D9A9 | d9a8 Non Private Use High Surrogate-D9A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ab Non Private Use High Surrogate-D9AB | d9aa Non Private Use High Surrogate-D9AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ad Non Private Use High Surrogate-D9AD | d9ac Non Private Use High Surrogate-D9AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9af Non Private Use High Surrogate-D9AF | d9ae Non Private Use High Surrogate-D9AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b1 Non Private Use High Surrogate-D9B1 | d9b0 Non Private Use High Surrogate-D9B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b3 Non Private Use High Surrogate-D9B3 | d9b2 Non Private Use High Surrogate-D9B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b5 Non Private Use High Surrogate-D9B5 | d9b4 Non Private Use High Surrogate-D9B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b7 Non Private Use High Surrogate-D9B7 | d9b6 Non Private Use High Surrogate-D9B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9b9 Non Private Use High Surrogate-D9B9 | d9b8 Non Private Use High Surrogate-D9B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bb Non Private Use High Surrogate-D9BB | d9ba Non Private Use High Surrogate-D9BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bd Non Private Use High Surrogate-D9BD | d9bc Non Private Use High Surrogate-D9BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9bf Non Private Use High Surrogate-D9BF | d9be Non Private Use High Surrogate-D9BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c1 Non Private Use High Surrogate-D9C1 | d9c0 Non Private Use High Surrogate-D9C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c3 Non Private Use High Surrogate-D9C3 | d9c2 Non Private Use High Surrogate-D9C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c5 Non Private Use High Surrogate-D9C5 | d9c4 Non Private Use High Surrogate-D9C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c7 Non Private Use High Surrogate-D9C7 | d9c6 Non Private Use High Surrogate-D9C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9c9 Non Private Use High Surrogate-D9C9 | d9c8 Non Private Use High Surrogate-D9C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cb Non Private Use High Surrogate-D9CB | d9ca Non Private Use High Surrogate-D9CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cd Non Private Use High Surrogate-D9CD | d9cc Non Private Use High Surrogate-D9CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9cf Non Private Use High Surrogate-D9CF | d9ce Non Private Use High Surrogate-D9CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d1 Non Private Use High Surrogate-D9D1 | d9d0 Non Private Use High Surrogate-D9D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d3 Non Private Use High Surrogate-D9D3 | d9d2 Non Private Use High Surrogate-D9D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d5 Non Private Use High Surrogate-D9D5 | d9d4 Non Private Use High Surrogate-D9D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d7 Non Private Use High Surrogate-D9D7 | d9d6 Non Private Use High Surrogate-D9D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9d9 Non Private Use High Surrogate-D9D9 | d9d8 Non Private Use High Surrogate-D9D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9db Non Private Use High Surrogate-D9DB | d9da Non Private Use High Surrogate-D9DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9dd Non Private Use High Surrogate-D9DD | d9dc Non Private Use High Surrogate-D9DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9df Non Private Use High Surrogate-D9DF | d9de Non Private Use High Surrogate-D9DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e1 Non Private Use High Surrogate-D9E1 | d9e0 Non Private Use High Surrogate-D9E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e3 Non Private Use High Surrogate-D9E3 | d9e2 Non Private Use High Surrogate-D9E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e5 Non Private Use High Surrogate-D9E5 | d9e4 Non Private Use High Surrogate-D9E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e7 Non Private Use High Surrogate-D9E7 | d9e6 Non Private Use High Surrogate-D9E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9e9 Non Private Use High Surrogate-D9E9 | d9e8 Non Private Use High Surrogate-D9E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9eb Non Private Use High Surrogate-D9EB | d9ea Non Private Use High Surrogate-D9EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ed Non Private Use High Surrogate-D9ED | d9ec Non Private Use High Surrogate-D9EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ef Non Private Use High Surrogate-D9EF | d9ee Non Private Use High Surrogate-D9EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f1 Non Private Use High Surrogate-D9F1 | d9f0 Non Private Use High Surrogate-D9F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f3 Non Private Use High Surrogate-D9F3 | d9f2 Non Private Use High Surrogate-D9F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f5 Non Private Use High Surrogate-D9F5 | d9f4 Non Private Use High Surrogate-D9F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f7 Non Private Use High Surrogate-D9F7 | d9f6 Non Private Use High Surrogate-D9F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9f9 Non Private Use High Surrogate-D9F9 | d9f8 Non Private Use High Surrogate-D9F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9fb Non Private Use High Surrogate-D9FB | d9fa Non Private Use High Surrogate-D9FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9fd Non Private Use High Surrogate-D9FD | d9fc Non Private Use High Surrogate-D9FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* d9ff Non Private Use High Surrogate-D9FF | d9fe Non Private Use High Surrogate-D9FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da01 Non Private Use High Surrogate-DA01 | da00 Non Private Use High Surrogate-DA00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da03 Non Private Use High Surrogate-DA03 | da02 Non Private Use High Surrogate-DA02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da05 Non Private Use High Surrogate-DA05 | da04 Non Private Use High Surrogate-DA04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da07 Non Private Use High Surrogate-DA07 | da06 Non Private Use High Surrogate-DA06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da09 Non Private Use High Surrogate-DA09 | da08 Non Private Use High Surrogate-DA08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0b Non Private Use High Surrogate-DA0B | da0a Non Private Use High Surrogate-DA0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0d Non Private Use High Surrogate-DA0D | da0c Non Private Use High Surrogate-DA0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da0f Non Private Use High Surrogate-DA0F | da0e Non Private Use High Surrogate-DA0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da11 Non Private Use High Surrogate-DA11 | da10 Non Private Use High Surrogate-DA10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da13 Non Private Use High Surrogate-DA13 | da12 Non Private Use High Surrogate-DA12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da15 Non Private Use High Surrogate-DA15 | da14 Non Private Use High Surrogate-DA14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da17 Non Private Use High Surrogate-DA17 | da16 Non Private Use High Surrogate-DA16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da19 Non Private Use High Surrogate-DA19 | da18 Non Private Use High Surrogate-DA18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1b Non Private Use High Surrogate-DA1B | da1a Non Private Use High Surrogate-DA1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1d Non Private Use High Surrogate-DA1D | da1c Non Private Use High Surrogate-DA1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da1f Non Private Use High Surrogate-DA1F | da1e Non Private Use High Surrogate-DA1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da21 Non Private Use High Surrogate-DA21 | da20 Non Private Use High Surrogate-DA20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da23 Non Private Use High Surrogate-DA23 | da22 Non Private Use High Surrogate-DA22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da25 Non Private Use High Surrogate-DA25 | da24 Non Private Use High Surrogate-DA24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da27 Non Private Use High Surrogate-DA27 | da26 Non Private Use High Surrogate-DA26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da29 Non Private Use High Surrogate-DA29 | da28 Non Private Use High Surrogate-DA28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2b Non Private Use High Surrogate-DA2B | da2a Non Private Use High Surrogate-DA2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2d Non Private Use High Surrogate-DA2D | da2c Non Private Use High Surrogate-DA2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da2f Non Private Use High Surrogate-DA2F | da2e Non Private Use High Surrogate-DA2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da31 Non Private Use High Surrogate-DA31 | da30 Non Private Use High Surrogate-DA30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da33 Non Private Use High Surrogate-DA33 | da32 Non Private Use High Surrogate-DA32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da35 Non Private Use High Surrogate-DA35 | da34 Non Private Use High Surrogate-DA34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da37 Non Private Use High Surrogate-DA37 | da36 Non Private Use High Surrogate-DA36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da39 Non Private Use High Surrogate-DA39 | da38 Non Private Use High Surrogate-DA38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3b Non Private Use High Surrogate-DA3B | da3a Non Private Use High Surrogate-DA3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3d Non Private Use High Surrogate-DA3D | da3c Non Private Use High Surrogate-DA3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da3f Non Private Use High Surrogate-DA3F | da3e Non Private Use High Surrogate-DA3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da41 Non Private Use High Surrogate-DA41 | da40 Non Private Use High Surrogate-DA40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da43 Non Private Use High Surrogate-DA43 | da42 Non Private Use High Surrogate-DA42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da45 Non Private Use High Surrogate-DA45 | da44 Non Private Use High Surrogate-DA44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da47 Non Private Use High Surrogate-DA47 | da46 Non Private Use High Surrogate-DA46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da49 Non Private Use High Surrogate-DA49 | da48 Non Private Use High Surrogate-DA48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4b Non Private Use High Surrogate-DA4B | da4a Non Private Use High Surrogate-DA4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4d Non Private Use High Surrogate-DA4D | da4c Non Private Use High Surrogate-DA4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da4f Non Private Use High Surrogate-DA4F | da4e Non Private Use High Surrogate-DA4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da51 Non Private Use High Surrogate-DA51 | da50 Non Private Use High Surrogate-DA50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da53 Non Private Use High Surrogate-DA53 | da52 Non Private Use High Surrogate-DA52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da55 Non Private Use High Surrogate-DA55 | da54 Non Private Use High Surrogate-DA54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da57 Non Private Use High Surrogate-DA57 | da56 Non Private Use High Surrogate-DA56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da59 Non Private Use High Surrogate-DA59 | da58 Non Private Use High Surrogate-DA58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5b Non Private Use High Surrogate-DA5B | da5a Non Private Use High Surrogate-DA5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5d Non Private Use High Surrogate-DA5D | da5c Non Private Use High Surrogate-DA5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da5f Non Private Use High Surrogate-DA5F | da5e Non Private Use High Surrogate-DA5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da61 Non Private Use High Surrogate-DA61 | da60 Non Private Use High Surrogate-DA60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da63 Non Private Use High Surrogate-DA63 | da62 Non Private Use High Surrogate-DA62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da65 Non Private Use High Surrogate-DA65 | da64 Non Private Use High Surrogate-DA64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da67 Non Private Use High Surrogate-DA67 | da66 Non Private Use High Surrogate-DA66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da69 Non Private Use High Surrogate-DA69 | da68 Non Private Use High Surrogate-DA68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6b Non Private Use High Surrogate-DA6B | da6a Non Private Use High Surrogate-DA6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6d Non Private Use High Surrogate-DA6D | da6c Non Private Use High Surrogate-DA6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da6f Non Private Use High Surrogate-DA6F | da6e Non Private Use High Surrogate-DA6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da71 Non Private Use High Surrogate-DA71 | da70 Non Private Use High Surrogate-DA70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da73 Non Private Use High Surrogate-DA73 | da72 Non Private Use High Surrogate-DA72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da75 Non Private Use High Surrogate-DA75 | da74 Non Private Use High Surrogate-DA74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da77 Non Private Use High Surrogate-DA77 | da76 Non Private Use High Surrogate-DA76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da79 Non Private Use High Surrogate-DA79 | da78 Non Private Use High Surrogate-DA78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7b Non Private Use High Surrogate-DA7B | da7a Non Private Use High Surrogate-DA7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7d Non Private Use High Surrogate-DA7D | da7c Non Private Use High Surrogate-DA7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da7f Non Private Use High Surrogate-DA7F | da7e Non Private Use High Surrogate-DA7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da81 Non Private Use High Surrogate-DA81 | da80 Non Private Use High Surrogate-DA80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da83 Non Private Use High Surrogate-DA83 | da82 Non Private Use High Surrogate-DA82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da85 Non Private Use High Surrogate-DA85 | da84 Non Private Use High Surrogate-DA84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da87 Non Private Use High Surrogate-DA87 | da86 Non Private Use High Surrogate-DA86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da89 Non Private Use High Surrogate-DA89 | da88 Non Private Use High Surrogate-DA88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8b Non Private Use High Surrogate-DA8B | da8a Non Private Use High Surrogate-DA8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8d Non Private Use High Surrogate-DA8D | da8c Non Private Use High Surrogate-DA8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da8f Non Private Use High Surrogate-DA8F | da8e Non Private Use High Surrogate-DA8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da91 Non Private Use High Surrogate-DA91 | da90 Non Private Use High Surrogate-DA90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da93 Non Private Use High Surrogate-DA93 | da92 Non Private Use High Surrogate-DA92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da95 Non Private Use High Surrogate-DA95 | da94 Non Private Use High Surrogate-DA94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da97 Non Private Use High Surrogate-DA97 | da96 Non Private Use High Surrogate-DA96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da99 Non Private Use High Surrogate-DA99 | da98 Non Private Use High Surrogate-DA98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9b Non Private Use High Surrogate-DA9B | da9a Non Private Use High Surrogate-DA9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9d Non Private Use High Surrogate-DA9D | da9c Non Private Use High Surrogate-DA9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* da9f Non Private Use High Surrogate-DA9F | da9e Non Private Use High Surrogate-DA9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa1 Non Private Use High Surrogate-DAA1 | daa0 Non Private Use High Surrogate-DAA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa3 Non Private Use High Surrogate-DAA3 | daa2 Non Private Use High Surrogate-DAA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa5 Non Private Use High Surrogate-DAA5 | daa4 Non Private Use High Surrogate-DAA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa7 Non Private Use High Surrogate-DAA7 | daa6 Non Private Use High Surrogate-DAA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daa9 Non Private Use High Surrogate-DAA9 | daa8 Non Private Use High Surrogate-DAA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daab Non Private Use High Surrogate-DAAB | daaa Non Private Use High Surrogate-DAAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daad Non Private Use High Surrogate-DAAD | daac Non Private Use High Surrogate-DAAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daaf Non Private Use High Surrogate-DAAF | daae Non Private Use High Surrogate-DAAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab1 Non Private Use High Surrogate-DAB1 | dab0 Non Private Use High Surrogate-DAB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab3 Non Private Use High Surrogate-DAB3 | dab2 Non Private Use High Surrogate-DAB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab5 Non Private Use High Surrogate-DAB5 | dab4 Non Private Use High Surrogate-DAB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab7 Non Private Use High Surrogate-DAB7 | dab6 Non Private Use High Surrogate-DAB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dab9 Non Private Use High Surrogate-DAB9 | dab8 Non Private Use High Surrogate-DAB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabb Non Private Use High Surrogate-DABB | daba Non Private Use High Surrogate-DABA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabd Non Private Use High Surrogate-DABD | dabc Non Private Use High Surrogate-DABC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dabf Non Private Use High Surrogate-DABF | dabe Non Private Use High Surrogate-DABE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac1 Non Private Use High Surrogate-DAC1 | dac0 Non Private Use High Surrogate-DAC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac3 Non Private Use High Surrogate-DAC3 | dac2 Non Private Use High Surrogate-DAC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac5 Non Private Use High Surrogate-DAC5 | dac4 Non Private Use High Surrogate-DAC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac7 Non Private Use High Surrogate-DAC7 | dac6 Non Private Use High Surrogate-DAC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dac9 Non Private Use High Surrogate-DAC9 | dac8 Non Private Use High Surrogate-DAC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacb Non Private Use High Surrogate-DACB | daca Non Private Use High Surrogate-DACA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacd Non Private Use High Surrogate-DACD | dacc Non Private Use High Surrogate-DACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dacf Non Private Use High Surrogate-DACF | dace Non Private Use High Surrogate-DACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad1 Non Private Use High Surrogate-DAD1 | dad0 Non Private Use High Surrogate-DAD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad3 Non Private Use High Surrogate-DAD3 | dad2 Non Private Use High Surrogate-DAD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad5 Non Private Use High Surrogate-DAD5 | dad4 Non Private Use High Surrogate-DAD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad7 Non Private Use High Surrogate-DAD7 | dad6 Non Private Use High Surrogate-DAD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dad9 Non Private Use High Surrogate-DAD9 | dad8 Non Private Use High Surrogate-DAD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadb Non Private Use High Surrogate-DADB | dada Non Private Use High Surrogate-DADA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadd Non Private Use High Surrogate-DADD | dadc Non Private Use High Surrogate-DADC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dadf Non Private Use High Surrogate-DADF | dade Non Private Use High Surrogate-DADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae1 Non Private Use High Surrogate-DAE1 | dae0 Non Private Use High Surrogate-DAE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae3 Non Private Use High Surrogate-DAE3 | dae2 Non Private Use High Surrogate-DAE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae5 Non Private Use High Surrogate-DAE5 | dae4 Non Private Use High Surrogate-DAE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae7 Non Private Use High Surrogate-DAE7 | dae6 Non Private Use High Surrogate-DAE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dae9 Non Private Use High Surrogate-DAE9 | dae8 Non Private Use High Surrogate-DAE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daeb Non Private Use High Surrogate-DAEB | daea Non Private Use High Surrogate-DAEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daed Non Private Use High Surrogate-DAED | daec Non Private Use High Surrogate-DAEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daef Non Private Use High Surrogate-DAEF | daee Non Private Use High Surrogate-DAEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf1 Non Private Use High Surrogate-DAF1 | daf0 Non Private Use High Surrogate-DAF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf3 Non Private Use High Surrogate-DAF3 | daf2 Non Private Use High Surrogate-DAF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf5 Non Private Use High Surrogate-DAF5 | daf4 Non Private Use High Surrogate-DAF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf7 Non Private Use High Surrogate-DAF7 | daf6 Non Private Use High Surrogate-DAF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daf9 Non Private Use High Surrogate-DAF9 | daf8 Non Private Use High Surrogate-DAF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dafb Non Private Use High Surrogate-DAFB | dafa Non Private Use High Surrogate-DAFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dafd Non Private Use High Surrogate-DAFD | dafc Non Private Use High Surrogate-DAFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* daff Non Private Use High Surrogate-DAFF | dafe Non Private Use High Surrogate-DAFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db01 Non Private Use High Surrogate-DB01 | db00 Non Private Use High Surrogate-DB00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db03 Non Private Use High Surrogate-DB03 | db02 Non Private Use High Surrogate-DB02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db05 Non Private Use High Surrogate-DB05 | db04 Non Private Use High Surrogate-DB04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db07 Non Private Use High Surrogate-DB07 | db06 Non Private Use High Surrogate-DB06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db09 Non Private Use High Surrogate-DB09 | db08 Non Private Use High Surrogate-DB08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0b Non Private Use High Surrogate-DB0B | db0a Non Private Use High Surrogate-DB0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0d Non Private Use High Surrogate-DB0D | db0c Non Private Use High Surrogate-DB0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db0f Non Private Use High Surrogate-DB0F | db0e Non Private Use High Surrogate-DB0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db11 Non Private Use High Surrogate-DB11 | db10 Non Private Use High Surrogate-DB10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db13 Non Private Use High Surrogate-DB13 | db12 Non Private Use High Surrogate-DB12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db15 Non Private Use High Surrogate-DB15 | db14 Non Private Use High Surrogate-DB14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db17 Non Private Use High Surrogate-DB17 | db16 Non Private Use High Surrogate-DB16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db19 Non Private Use High Surrogate-DB19 | db18 Non Private Use High Surrogate-DB18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1b Non Private Use High Surrogate-DB1B | db1a Non Private Use High Surrogate-DB1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1d Non Private Use High Surrogate-DB1D | db1c Non Private Use High Surrogate-DB1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db1f Non Private Use High Surrogate-DB1F | db1e Non Private Use High Surrogate-DB1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db21 Non Private Use High Surrogate-DB21 | db20 Non Private Use High Surrogate-DB20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db23 Non Private Use High Surrogate-DB23 | db22 Non Private Use High Surrogate-DB22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db25 Non Private Use High Surrogate-DB25 | db24 Non Private Use High Surrogate-DB24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db27 Non Private Use High Surrogate-DB27 | db26 Non Private Use High Surrogate-DB26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db29 Non Private Use High Surrogate-DB29 | db28 Non Private Use High Surrogate-DB28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2b Non Private Use High Surrogate-DB2B | db2a Non Private Use High Surrogate-DB2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2d Non Private Use High Surrogate-DB2D | db2c Non Private Use High Surrogate-DB2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db2f Non Private Use High Surrogate-DB2F | db2e Non Private Use High Surrogate-DB2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db31 Non Private Use High Surrogate-DB31 | db30 Non Private Use High Surrogate-DB30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db33 Non Private Use High Surrogate-DB33 | db32 Non Private Use High Surrogate-DB32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db35 Non Private Use High Surrogate-DB35 | db34 Non Private Use High Surrogate-DB34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db37 Non Private Use High Surrogate-DB37 | db36 Non Private Use High Surrogate-DB36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db39 Non Private Use High Surrogate-DB39 | db38 Non Private Use High Surrogate-DB38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3b Non Private Use High Surrogate-DB3B | db3a Non Private Use High Surrogate-DB3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3d Non Private Use High Surrogate-DB3D | db3c Non Private Use High Surrogate-DB3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db3f Non Private Use High Surrogate-DB3F | db3e Non Private Use High Surrogate-DB3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db41 Non Private Use High Surrogate-DB41 | db40 Non Private Use High Surrogate-DB40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db43 Non Private Use High Surrogate-DB43 | db42 Non Private Use High Surrogate-DB42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db45 Non Private Use High Surrogate-DB45 | db44 Non Private Use High Surrogate-DB44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db47 Non Private Use High Surrogate-DB47 | db46 Non Private Use High Surrogate-DB46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db49 Non Private Use High Surrogate-DB49 | db48 Non Private Use High Surrogate-DB48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4b Non Private Use High Surrogate-DB4B | db4a Non Private Use High Surrogate-DB4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4d Non Private Use High Surrogate-DB4D | db4c Non Private Use High Surrogate-DB4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db4f Non Private Use High Surrogate-DB4F | db4e Non Private Use High Surrogate-DB4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db51 Non Private Use High Surrogate-DB51 | db50 Non Private Use High Surrogate-DB50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db53 Non Private Use High Surrogate-DB53 | db52 Non Private Use High Surrogate-DB52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db55 Non Private Use High Surrogate-DB55 | db54 Non Private Use High Surrogate-DB54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db57 Non Private Use High Surrogate-DB57 | db56 Non Private Use High Surrogate-DB56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db59 Non Private Use High Surrogate-DB59 | db58 Non Private Use High Surrogate-DB58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5b Non Private Use High Surrogate-DB5B | db5a Non Private Use High Surrogate-DB5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5d Non Private Use High Surrogate-DB5D | db5c Non Private Use High Surrogate-DB5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db5f Non Private Use High Surrogate-DB5F | db5e Non Private Use High Surrogate-DB5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db61 Non Private Use High Surrogate-DB61 | db60 Non Private Use High Surrogate-DB60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db63 Non Private Use High Surrogate-DB63 | db62 Non Private Use High Surrogate-DB62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db65 Non Private Use High Surrogate-DB65 | db64 Non Private Use High Surrogate-DB64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db67 Non Private Use High Surrogate-DB67 | db66 Non Private Use High Surrogate-DB66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db69 Non Private Use High Surrogate-DB69 | db68 Non Private Use High Surrogate-DB68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6b Non Private Use High Surrogate-DB6B | db6a Non Private Use High Surrogate-DB6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6d Non Private Use High Surrogate-DB6D | db6c Non Private Use High Surrogate-DB6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db6f Non Private Use High Surrogate-DB6F | db6e Non Private Use High Surrogate-DB6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db71 Non Private Use High Surrogate-DB71 | db70 Non Private Use High Surrogate-DB70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db73 Non Private Use High Surrogate-DB73 | db72 Non Private Use High Surrogate-DB72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db75 Non Private Use High Surrogate-DB75 | db74 Non Private Use High Surrogate-DB74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db77 Non Private Use High Surrogate-DB77 | db76 Non Private Use High Surrogate-DB76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db79 Non Private Use High Surrogate-DB79 | db78 Non Private Use High Surrogate-DB78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7b Non Private Use High Surrogate-DB7B | db7a Non Private Use High Surrogate-DB7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7d Non Private Use High Surrogate-DB7D | db7c Non Private Use High Surrogate-DB7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db7f Non Private Use High Surrogate-DB7F | db7e Non Private Use High Surrogate-DB7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db81 Private Use High Surrogate-DB81 | db80 Private Use High Surrogate-DB80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db83 Private Use High Surrogate-DB83 | db82 Private Use High Surrogate-DB82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db85 Private Use High Surrogate-DB85 | db84 Private Use High Surrogate-DB84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db87 Private Use High Surrogate-DB87 | db86 Private Use High Surrogate-DB86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db89 Private Use High Surrogate-DB89 | db88 Private Use High Surrogate-DB88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8b Private Use High Surrogate-DB8B | db8a Private Use High Surrogate-DB8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8d Private Use High Surrogate-DB8D | db8c Private Use High Surrogate-DB8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db8f Private Use High Surrogate-DB8F | db8e Private Use High Surrogate-DB8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db91 Private Use High Surrogate-DB91 | db90 Private Use High Surrogate-DB90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db93 Private Use High Surrogate-DB93 | db92 Private Use High Surrogate-DB92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db95 Private Use High Surrogate-DB95 | db94 Private Use High Surrogate-DB94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db97 Private Use High Surrogate-DB97 | db96 Private Use High Surrogate-DB96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db99 Private Use High Surrogate-DB99 | db98 Private Use High Surrogate-DB98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9b Private Use High Surrogate-DB9B | db9a Private Use High Surrogate-DB9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9d Private Use High Surrogate-DB9D | db9c Private Use High Surrogate-DB9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* db9f Private Use High Surrogate-DB9F | db9e Private Use High Surrogate-DB9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba1 Private Use High Surrogate-DBA1 | dba0 Private Use High Surrogate-DBA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba3 Private Use High Surrogate-DBA3 | dba2 Private Use High Surrogate-DBA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba5 Private Use High Surrogate-DBA5 | dba4 Private Use High Surrogate-DBA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba7 Private Use High Surrogate-DBA7 | dba6 Private Use High Surrogate-DBA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dba9 Private Use High Surrogate-DBA9 | dba8 Private Use High Surrogate-DBA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbab Private Use High Surrogate-DBAB | dbaa Private Use High Surrogate-DBAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbad Private Use High Surrogate-DBAD | dbac Private Use High Surrogate-DBAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbaf Private Use High Surrogate-DBAF | dbae Private Use High Surrogate-DBAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb1 Private Use High Surrogate-DBB1 | dbb0 Private Use High Surrogate-DBB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb3 Private Use High Surrogate-DBB3 | dbb2 Private Use High Surrogate-DBB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb5 Private Use High Surrogate-DBB5 | dbb4 Private Use High Surrogate-DBB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb7 Private Use High Surrogate-DBB7 | dbb6 Private Use High Surrogate-DBB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbb9 Private Use High Surrogate-DBB9 | dbb8 Private Use High Surrogate-DBB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbb Private Use High Surrogate-DBBB | dbba Private Use High Surrogate-DBBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbd Private Use High Surrogate-DBBD | dbbc Private Use High Surrogate-DBBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbbf Private Use High Surrogate-DBBF | dbbe Private Use High Surrogate-DBBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc1 Private Use High Surrogate-DBC1 | dbc0 Private Use High Surrogate-DBC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc3 Private Use High Surrogate-DBC3 | dbc2 Private Use High Surrogate-DBC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc5 Private Use High Surrogate-DBC5 | dbc4 Private Use High Surrogate-DBC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc7 Private Use High Surrogate-DBC7 | dbc6 Private Use High Surrogate-DBC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbc9 Private Use High Surrogate-DBC9 | dbc8 Private Use High Surrogate-DBC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcb Private Use High Surrogate-DBCB | dbca Private Use High Surrogate-DBCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcd Private Use High Surrogate-DBCD | dbcc Private Use High Surrogate-DBCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbcf Private Use High Surrogate-DBCF | dbce Private Use High Surrogate-DBCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd1 Private Use High Surrogate-DBD1 | dbd0 Private Use High Surrogate-DBD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd3 Private Use High Surrogate-DBD3 | dbd2 Private Use High Surrogate-DBD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd5 Private Use High Surrogate-DBD5 | dbd4 Private Use High Surrogate-DBD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd7 Private Use High Surrogate-DBD7 | dbd6 Private Use High Surrogate-DBD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbd9 Private Use High Surrogate-DBD9 | dbd8 Private Use High Surrogate-DBD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdb Private Use High Surrogate-DBDB | dbda Private Use High Surrogate-DBDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdd Private Use High Surrogate-DBDD | dbdc Private Use High Surrogate-DBDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbdf Private Use High Surrogate-DBDF | dbde Private Use High Surrogate-DBDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe1 Private Use High Surrogate-DBE1 | dbe0 Private Use High Surrogate-DBE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe3 Private Use High Surrogate-DBE3 | dbe2 Private Use High Surrogate-DBE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe5 Private Use High Surrogate-DBE5 | dbe4 Private Use High Surrogate-DBE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe7 Private Use High Surrogate-DBE7 | dbe6 Private Use High Surrogate-DBE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbe9 Private Use High Surrogate-DBE9 | dbe8 Private Use High Surrogate-DBE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbeb Private Use High Surrogate-DBEB | dbea Private Use High Surrogate-DBEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbed Private Use High Surrogate-DBED | dbec Private Use High Surrogate-DBEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbef Private Use High Surrogate-DBEF | dbee Private Use High Surrogate-DBEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf1 Private Use High Surrogate-DBF1 | dbf0 Private Use High Surrogate-DBF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf3 Private Use High Surrogate-DBF3 | dbf2 Private Use High Surrogate-DBF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf5 Private Use High Surrogate-DBF5 | dbf4 Private Use High Surrogate-DBF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf7 Private Use High Surrogate-DBF7 | dbf6 Private Use High Surrogate-DBF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbf9 Private Use High Surrogate-DBF9 | dbf8 Private Use High Surrogate-DBF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbfb Private Use High Surrogate-DBFB | dbfa Private Use High Surrogate-DBFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbfd Private Use High Surrogate-DBFD | dbfc Private Use High Surrogate-DBFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dbff Private Use High Surrogate-DBFF | dbfe Private Use High Surrogate-DBFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc01 Low Surrogate-DC01 | dc00 Low Surrogate-DC00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc03 Low Surrogate-DC03 | dc02 Low Surrogate-DC02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc05 Low Surrogate-DC05 | dc04 Low Surrogate-DC04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc07 Low Surrogate-DC07 | dc06 Low Surrogate-DC06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc09 Low Surrogate-DC09 | dc08 Low Surrogate-DC08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0b Low Surrogate-DC0B | dc0a Low Surrogate-DC0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0d Low Surrogate-DC0D | dc0c Low Surrogate-DC0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc0f Low Surrogate-DC0F | dc0e Low Surrogate-DC0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc11 Low Surrogate-DC11 | dc10 Low Surrogate-DC10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc13 Low Surrogate-DC13 | dc12 Low Surrogate-DC12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc15 Low Surrogate-DC15 | dc14 Low Surrogate-DC14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc17 Low Surrogate-DC17 | dc16 Low Surrogate-DC16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc19 Low Surrogate-DC19 | dc18 Low Surrogate-DC18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1b Low Surrogate-DC1B | dc1a Low Surrogate-DC1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1d Low Surrogate-DC1D | dc1c Low Surrogate-DC1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc1f Low Surrogate-DC1F | dc1e Low Surrogate-DC1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc21 Low Surrogate-DC21 | dc20 Low Surrogate-DC20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc23 Low Surrogate-DC23 | dc22 Low Surrogate-DC22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc25 Low Surrogate-DC25 | dc24 Low Surrogate-DC24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc27 Low Surrogate-DC27 | dc26 Low Surrogate-DC26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc29 Low Surrogate-DC29 | dc28 Low Surrogate-DC28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2b Low Surrogate-DC2B | dc2a Low Surrogate-DC2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2d Low Surrogate-DC2D | dc2c Low Surrogate-DC2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc2f Low Surrogate-DC2F | dc2e Low Surrogate-DC2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc31 Low Surrogate-DC31 | dc30 Low Surrogate-DC30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc33 Low Surrogate-DC33 | dc32 Low Surrogate-DC32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc35 Low Surrogate-DC35 | dc34 Low Surrogate-DC34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc37 Low Surrogate-DC37 | dc36 Low Surrogate-DC36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc39 Low Surrogate-DC39 | dc38 Low Surrogate-DC38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3b Low Surrogate-DC3B | dc3a Low Surrogate-DC3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3d Low Surrogate-DC3D | dc3c Low Surrogate-DC3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc3f Low Surrogate-DC3F | dc3e Low Surrogate-DC3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc41 Low Surrogate-DC41 | dc40 Low Surrogate-DC40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc43 Low Surrogate-DC43 | dc42 Low Surrogate-DC42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc45 Low Surrogate-DC45 | dc44 Low Surrogate-DC44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc47 Low Surrogate-DC47 | dc46 Low Surrogate-DC46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc49 Low Surrogate-DC49 | dc48 Low Surrogate-DC48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4b Low Surrogate-DC4B | dc4a Low Surrogate-DC4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4d Low Surrogate-DC4D | dc4c Low Surrogate-DC4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc4f Low Surrogate-DC4F | dc4e Low Surrogate-DC4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc51 Low Surrogate-DC51 | dc50 Low Surrogate-DC50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc53 Low Surrogate-DC53 | dc52 Low Surrogate-DC52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc55 Low Surrogate-DC55 | dc54 Low Surrogate-DC54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc57 Low Surrogate-DC57 | dc56 Low Surrogate-DC56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc59 Low Surrogate-DC59 | dc58 Low Surrogate-DC58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5b Low Surrogate-DC5B | dc5a Low Surrogate-DC5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5d Low Surrogate-DC5D | dc5c Low Surrogate-DC5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc5f Low Surrogate-DC5F | dc5e Low Surrogate-DC5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc61 Low Surrogate-DC61 | dc60 Low Surrogate-DC60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc63 Low Surrogate-DC63 | dc62 Low Surrogate-DC62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc65 Low Surrogate-DC65 | dc64 Low Surrogate-DC64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc67 Low Surrogate-DC67 | dc66 Low Surrogate-DC66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc69 Low Surrogate-DC69 | dc68 Low Surrogate-DC68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6b Low Surrogate-DC6B | dc6a Low Surrogate-DC6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6d Low Surrogate-DC6D | dc6c Low Surrogate-DC6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc6f Low Surrogate-DC6F | dc6e Low Surrogate-DC6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc71 Low Surrogate-DC71 | dc70 Low Surrogate-DC70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc73 Low Surrogate-DC73 | dc72 Low Surrogate-DC72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc75 Low Surrogate-DC75 | dc74 Low Surrogate-DC74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc77 Low Surrogate-DC77 | dc76 Low Surrogate-DC76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc79 Low Surrogate-DC79 | dc78 Low Surrogate-DC78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7b Low Surrogate-DC7B | dc7a Low Surrogate-DC7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7d Low Surrogate-DC7D | dc7c Low Surrogate-DC7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc7f Low Surrogate-DC7F | dc7e Low Surrogate-DC7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc81 Low Surrogate-DC81 | dc80 Low Surrogate-DC80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc83 Low Surrogate-DC83 | dc82 Low Surrogate-DC82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc85 Low Surrogate-DC85 | dc84 Low Surrogate-DC84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc87 Low Surrogate-DC87 | dc86 Low Surrogate-DC86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc89 Low Surrogate-DC89 | dc88 Low Surrogate-DC88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8b Low Surrogate-DC8B | dc8a Low Surrogate-DC8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8d Low Surrogate-DC8D | dc8c Low Surrogate-DC8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc8f Low Surrogate-DC8F | dc8e Low Surrogate-DC8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc91 Low Surrogate-DC91 | dc90 Low Surrogate-DC90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc93 Low Surrogate-DC93 | dc92 Low Surrogate-DC92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc95 Low Surrogate-DC95 | dc94 Low Surrogate-DC94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc97 Low Surrogate-DC97 | dc96 Low Surrogate-DC96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc99 Low Surrogate-DC99 | dc98 Low Surrogate-DC98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9b Low Surrogate-DC9B | dc9a Low Surrogate-DC9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9d Low Surrogate-DC9D | dc9c Low Surrogate-DC9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dc9f Low Surrogate-DC9F | dc9e Low Surrogate-DC9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca1 Low Surrogate-DCA1 | dca0 Low Surrogate-DCA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca3 Low Surrogate-DCA3 | dca2 Low Surrogate-DCA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca5 Low Surrogate-DCA5 | dca4 Low Surrogate-DCA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca7 Low Surrogate-DCA7 | dca6 Low Surrogate-DCA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dca9 Low Surrogate-DCA9 | dca8 Low Surrogate-DCA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcab Low Surrogate-DCAB | dcaa Low Surrogate-DCAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcad Low Surrogate-DCAD | dcac Low Surrogate-DCAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcaf Low Surrogate-DCAF | dcae Low Surrogate-DCAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb1 Low Surrogate-DCB1 | dcb0 Low Surrogate-DCB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb3 Low Surrogate-DCB3 | dcb2 Low Surrogate-DCB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb5 Low Surrogate-DCB5 | dcb4 Low Surrogate-DCB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb7 Low Surrogate-DCB7 | dcb6 Low Surrogate-DCB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcb9 Low Surrogate-DCB9 | dcb8 Low Surrogate-DCB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbb Low Surrogate-DCBB | dcba Low Surrogate-DCBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbd Low Surrogate-DCBD | dcbc Low Surrogate-DCBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcbf Low Surrogate-DCBF | dcbe Low Surrogate-DCBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc1 Low Surrogate-DCC1 | dcc0 Low Surrogate-DCC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc3 Low Surrogate-DCC3 | dcc2 Low Surrogate-DCC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc5 Low Surrogate-DCC5 | dcc4 Low Surrogate-DCC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc7 Low Surrogate-DCC7 | dcc6 Low Surrogate-DCC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcc9 Low Surrogate-DCC9 | dcc8 Low Surrogate-DCC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccb Low Surrogate-DCCB | dcca Low Surrogate-DCCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccd Low Surrogate-DCCD | dccc Low Surrogate-DCCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dccf Low Surrogate-DCCF | dcce Low Surrogate-DCCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd1 Low Surrogate-DCD1 | dcd0 Low Surrogate-DCD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd3 Low Surrogate-DCD3 | dcd2 Low Surrogate-DCD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd5 Low Surrogate-DCD5 | dcd4 Low Surrogate-DCD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd7 Low Surrogate-DCD7 | dcd6 Low Surrogate-DCD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcd9 Low Surrogate-DCD9 | dcd8 Low Surrogate-DCD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdb Low Surrogate-DCDB | dcda Low Surrogate-DCDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdd Low Surrogate-DCDD | dcdc Low Surrogate-DCDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcdf Low Surrogate-DCDF | dcde Low Surrogate-DCDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce1 Low Surrogate-DCE1 | dce0 Low Surrogate-DCE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce3 Low Surrogate-DCE3 | dce2 Low Surrogate-DCE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce5 Low Surrogate-DCE5 | dce4 Low Surrogate-DCE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce7 Low Surrogate-DCE7 | dce6 Low Surrogate-DCE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dce9 Low Surrogate-DCE9 | dce8 Low Surrogate-DCE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dceb Low Surrogate-DCEB | dcea Low Surrogate-DCEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dced Low Surrogate-DCED | dcec Low Surrogate-DCEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcef Low Surrogate-DCEF | dcee Low Surrogate-DCEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf1 Low Surrogate-DCF1 | dcf0 Low Surrogate-DCF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf3 Low Surrogate-DCF3 | dcf2 Low Surrogate-DCF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf5 Low Surrogate-DCF5 | dcf4 Low Surrogate-DCF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf7 Low Surrogate-DCF7 | dcf6 Low Surrogate-DCF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcf9 Low Surrogate-DCF9 | dcf8 Low Surrogate-DCF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcfb Low Surrogate-DCFB | dcfa Low Surrogate-DCFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcfd Low Surrogate-DCFD | dcfc Low Surrogate-DCFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dcff Low Surrogate-DCFF | dcfe Low Surrogate-DCFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd01 Low Surrogate-DD01 | dd00 Low Surrogate-DD00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd03 Low Surrogate-DD03 | dd02 Low Surrogate-DD02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd05 Low Surrogate-DD05 | dd04 Low Surrogate-DD04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd07 Low Surrogate-DD07 | dd06 Low Surrogate-DD06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd09 Low Surrogate-DD09 | dd08 Low Surrogate-DD08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0b Low Surrogate-DD0B | dd0a Low Surrogate-DD0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0d Low Surrogate-DD0D | dd0c Low Surrogate-DD0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd0f Low Surrogate-DD0F | dd0e Low Surrogate-DD0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd11 Low Surrogate-DD11 | dd10 Low Surrogate-DD10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd13 Low Surrogate-DD13 | dd12 Low Surrogate-DD12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd15 Low Surrogate-DD15 | dd14 Low Surrogate-DD14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd17 Low Surrogate-DD17 | dd16 Low Surrogate-DD16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd19 Low Surrogate-DD19 | dd18 Low Surrogate-DD18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1b Low Surrogate-DD1B | dd1a Low Surrogate-DD1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1d Low Surrogate-DD1D | dd1c Low Surrogate-DD1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd1f Low Surrogate-DD1F | dd1e Low Surrogate-DD1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd21 Low Surrogate-DD21 | dd20 Low Surrogate-DD20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd23 Low Surrogate-DD23 | dd22 Low Surrogate-DD22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd25 Low Surrogate-DD25 | dd24 Low Surrogate-DD24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd27 Low Surrogate-DD27 | dd26 Low Surrogate-DD26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd29 Low Surrogate-DD29 | dd28 Low Surrogate-DD28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2b Low Surrogate-DD2B | dd2a Low Surrogate-DD2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2d Low Surrogate-DD2D | dd2c Low Surrogate-DD2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd2f Low Surrogate-DD2F | dd2e Low Surrogate-DD2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd31 Low Surrogate-DD31 | dd30 Low Surrogate-DD30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd33 Low Surrogate-DD33 | dd32 Low Surrogate-DD32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd35 Low Surrogate-DD35 | dd34 Low Surrogate-DD34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd37 Low Surrogate-DD37 | dd36 Low Surrogate-DD36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd39 Low Surrogate-DD39 | dd38 Low Surrogate-DD38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3b Low Surrogate-DD3B | dd3a Low Surrogate-DD3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3d Low Surrogate-DD3D | dd3c Low Surrogate-DD3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd3f Low Surrogate-DD3F | dd3e Low Surrogate-DD3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd41 Low Surrogate-DD41 | dd40 Low Surrogate-DD40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd43 Low Surrogate-DD43 | dd42 Low Surrogate-DD42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd45 Low Surrogate-DD45 | dd44 Low Surrogate-DD44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd47 Low Surrogate-DD47 | dd46 Low Surrogate-DD46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd49 Low Surrogate-DD49 | dd48 Low Surrogate-DD48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4b Low Surrogate-DD4B | dd4a Low Surrogate-DD4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4d Low Surrogate-DD4D | dd4c Low Surrogate-DD4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd4f Low Surrogate-DD4F | dd4e Low Surrogate-DD4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd51 Low Surrogate-DD51 | dd50 Low Surrogate-DD50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd53 Low Surrogate-DD53 | dd52 Low Surrogate-DD52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd55 Low Surrogate-DD55 | dd54 Low Surrogate-DD54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd57 Low Surrogate-DD57 | dd56 Low Surrogate-DD56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd59 Low Surrogate-DD59 | dd58 Low Surrogate-DD58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5b Low Surrogate-DD5B | dd5a Low Surrogate-DD5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5d Low Surrogate-DD5D | dd5c Low Surrogate-DD5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd5f Low Surrogate-DD5F | dd5e Low Surrogate-DD5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd61 Low Surrogate-DD61 | dd60 Low Surrogate-DD60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd63 Low Surrogate-DD63 | dd62 Low Surrogate-DD62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd65 Low Surrogate-DD65 | dd64 Low Surrogate-DD64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd67 Low Surrogate-DD67 | dd66 Low Surrogate-DD66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd69 Low Surrogate-DD69 | dd68 Low Surrogate-DD68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6b Low Surrogate-DD6B | dd6a Low Surrogate-DD6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6d Low Surrogate-DD6D | dd6c Low Surrogate-DD6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd6f Low Surrogate-DD6F | dd6e Low Surrogate-DD6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd71 Low Surrogate-DD71 | dd70 Low Surrogate-DD70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd73 Low Surrogate-DD73 | dd72 Low Surrogate-DD72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd75 Low Surrogate-DD75 | dd74 Low Surrogate-DD74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd77 Low Surrogate-DD77 | dd76 Low Surrogate-DD76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd79 Low Surrogate-DD79 | dd78 Low Surrogate-DD78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7b Low Surrogate-DD7B | dd7a Low Surrogate-DD7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7d Low Surrogate-DD7D | dd7c Low Surrogate-DD7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd7f Low Surrogate-DD7F | dd7e Low Surrogate-DD7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd81 Low Surrogate-DD81 | dd80 Low Surrogate-DD80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd83 Low Surrogate-DD83 | dd82 Low Surrogate-DD82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd85 Low Surrogate-DD85 | dd84 Low Surrogate-DD84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd87 Low Surrogate-DD87 | dd86 Low Surrogate-DD86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd89 Low Surrogate-DD89 | dd88 Low Surrogate-DD88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8b Low Surrogate-DD8B | dd8a Low Surrogate-DD8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8d Low Surrogate-DD8D | dd8c Low Surrogate-DD8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd8f Low Surrogate-DD8F | dd8e Low Surrogate-DD8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd91 Low Surrogate-DD91 | dd90 Low Surrogate-DD90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd93 Low Surrogate-DD93 | dd92 Low Surrogate-DD92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd95 Low Surrogate-DD95 | dd94 Low Surrogate-DD94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd97 Low Surrogate-DD97 | dd96 Low Surrogate-DD96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd99 Low Surrogate-DD99 | dd98 Low Surrogate-DD98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9b Low Surrogate-DD9B | dd9a Low Surrogate-DD9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9d Low Surrogate-DD9D | dd9c Low Surrogate-DD9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dd9f Low Surrogate-DD9F | dd9e Low Surrogate-DD9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda1 Low Surrogate-DDA1 | dda0 Low Surrogate-DDA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda3 Low Surrogate-DDA3 | dda2 Low Surrogate-DDA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda5 Low Surrogate-DDA5 | dda4 Low Surrogate-DDA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda7 Low Surrogate-DDA7 | dda6 Low Surrogate-DDA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dda9 Low Surrogate-DDA9 | dda8 Low Surrogate-DDA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddab Low Surrogate-DDAB | ddaa Low Surrogate-DDAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddad Low Surrogate-DDAD | ddac Low Surrogate-DDAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddaf Low Surrogate-DDAF | ddae Low Surrogate-DDAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb1 Low Surrogate-DDB1 | ddb0 Low Surrogate-DDB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb3 Low Surrogate-DDB3 | ddb2 Low Surrogate-DDB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb5 Low Surrogate-DDB5 | ddb4 Low Surrogate-DDB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb7 Low Surrogate-DDB7 | ddb6 Low Surrogate-DDB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddb9 Low Surrogate-DDB9 | ddb8 Low Surrogate-DDB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbb Low Surrogate-DDBB | ddba Low Surrogate-DDBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbd Low Surrogate-DDBD | ddbc Low Surrogate-DDBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddbf Low Surrogate-DDBF | ddbe Low Surrogate-DDBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc1 Low Surrogate-DDC1 | ddc0 Low Surrogate-DDC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc3 Low Surrogate-DDC3 | ddc2 Low Surrogate-DDC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc5 Low Surrogate-DDC5 | ddc4 Low Surrogate-DDC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc7 Low Surrogate-DDC7 | ddc6 Low Surrogate-DDC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddc9 Low Surrogate-DDC9 | ddc8 Low Surrogate-DDC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcb Low Surrogate-DDCB | ddca Low Surrogate-DDCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcd Low Surrogate-DDCD | ddcc Low Surrogate-DDCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddcf Low Surrogate-DDCF | ddce Low Surrogate-DDCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd1 Low Surrogate-DDD1 | ddd0 Low Surrogate-DDD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd3 Low Surrogate-DDD3 | ddd2 Low Surrogate-DDD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd5 Low Surrogate-DDD5 | ddd4 Low Surrogate-DDD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd7 Low Surrogate-DDD7 | ddd6 Low Surrogate-DDD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddd9 Low Surrogate-DDD9 | ddd8 Low Surrogate-DDD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddb Low Surrogate-DDDB | ddda Low Surrogate-DDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddd Low Surrogate-DDDD | dddc Low Surrogate-DDDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dddf Low Surrogate-DDDF | ddde Low Surrogate-DDDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde1 Low Surrogate-DDE1 | dde0 Low Surrogate-DDE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde3 Low Surrogate-DDE3 | dde2 Low Surrogate-DDE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde5 Low Surrogate-DDE5 | dde4 Low Surrogate-DDE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde7 Low Surrogate-DDE7 | dde6 Low Surrogate-DDE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dde9 Low Surrogate-DDE9 | dde8 Low Surrogate-DDE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddeb Low Surrogate-DDEB | ddea Low Surrogate-DDEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dded Low Surrogate-DDED | ddec Low Surrogate-DDEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddef Low Surrogate-DDEF | ddee Low Surrogate-DDEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf1 Low Surrogate-DDF1 | ddf0 Low Surrogate-DDF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf3 Low Surrogate-DDF3 | ddf2 Low Surrogate-DDF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf5 Low Surrogate-DDF5 | ddf4 Low Surrogate-DDF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf7 Low Surrogate-DDF7 | ddf6 Low Surrogate-DDF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddf9 Low Surrogate-DDF9 | ddf8 Low Surrogate-DDF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddfb Low Surrogate-DDFB | ddfa Low Surrogate-DDFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddfd Low Surrogate-DDFD | ddfc Low Surrogate-DDFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ddff Low Surrogate-DDFF | ddfe Low Surrogate-DDFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de01 Low Surrogate-DE01 | de00 Low Surrogate-DE00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de03 Low Surrogate-DE03 | de02 Low Surrogate-DE02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de05 Low Surrogate-DE05 | de04 Low Surrogate-DE04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de07 Low Surrogate-DE07 | de06 Low Surrogate-DE06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de09 Low Surrogate-DE09 | de08 Low Surrogate-DE08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0b Low Surrogate-DE0B | de0a Low Surrogate-DE0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0d Low Surrogate-DE0D | de0c Low Surrogate-DE0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de0f Low Surrogate-DE0F | de0e Low Surrogate-DE0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de11 Low Surrogate-DE11 | de10 Low Surrogate-DE10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de13 Low Surrogate-DE13 | de12 Low Surrogate-DE12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de15 Low Surrogate-DE15 | de14 Low Surrogate-DE14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de17 Low Surrogate-DE17 | de16 Low Surrogate-DE16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de19 Low Surrogate-DE19 | de18 Low Surrogate-DE18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1b Low Surrogate-DE1B | de1a Low Surrogate-DE1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1d Low Surrogate-DE1D | de1c Low Surrogate-DE1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de1f Low Surrogate-DE1F | de1e Low Surrogate-DE1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de21 Low Surrogate-DE21 | de20 Low Surrogate-DE20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de23 Low Surrogate-DE23 | de22 Low Surrogate-DE22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de25 Low Surrogate-DE25 | de24 Low Surrogate-DE24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de27 Low Surrogate-DE27 | de26 Low Surrogate-DE26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de29 Low Surrogate-DE29 | de28 Low Surrogate-DE28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2b Low Surrogate-DE2B | de2a Low Surrogate-DE2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2d Low Surrogate-DE2D | de2c Low Surrogate-DE2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de2f Low Surrogate-DE2F | de2e Low Surrogate-DE2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de31 Low Surrogate-DE31 | de30 Low Surrogate-DE30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de33 Low Surrogate-DE33 | de32 Low Surrogate-DE32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de35 Low Surrogate-DE35 | de34 Low Surrogate-DE34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de37 Low Surrogate-DE37 | de36 Low Surrogate-DE36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de39 Low Surrogate-DE39 | de38 Low Surrogate-DE38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3b Low Surrogate-DE3B | de3a Low Surrogate-DE3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3d Low Surrogate-DE3D | de3c Low Surrogate-DE3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de3f Low Surrogate-DE3F | de3e Low Surrogate-DE3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de41 Low Surrogate-DE41 | de40 Low Surrogate-DE40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de43 Low Surrogate-DE43 | de42 Low Surrogate-DE42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de45 Low Surrogate-DE45 | de44 Low Surrogate-DE44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de47 Low Surrogate-DE47 | de46 Low Surrogate-DE46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de49 Low Surrogate-DE49 | de48 Low Surrogate-DE48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4b Low Surrogate-DE4B | de4a Low Surrogate-DE4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4d Low Surrogate-DE4D | de4c Low Surrogate-DE4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de4f Low Surrogate-DE4F | de4e Low Surrogate-DE4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de51 Low Surrogate-DE51 | de50 Low Surrogate-DE50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de53 Low Surrogate-DE53 | de52 Low Surrogate-DE52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de55 Low Surrogate-DE55 | de54 Low Surrogate-DE54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de57 Low Surrogate-DE57 | de56 Low Surrogate-DE56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de59 Low Surrogate-DE59 | de58 Low Surrogate-DE58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5b Low Surrogate-DE5B | de5a Low Surrogate-DE5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5d Low Surrogate-DE5D | de5c Low Surrogate-DE5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de5f Low Surrogate-DE5F | de5e Low Surrogate-DE5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de61 Low Surrogate-DE61 | de60 Low Surrogate-DE60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de63 Low Surrogate-DE63 | de62 Low Surrogate-DE62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de65 Low Surrogate-DE65 | de64 Low Surrogate-DE64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de67 Low Surrogate-DE67 | de66 Low Surrogate-DE66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de69 Low Surrogate-DE69 | de68 Low Surrogate-DE68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6b Low Surrogate-DE6B | de6a Low Surrogate-DE6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6d Low Surrogate-DE6D | de6c Low Surrogate-DE6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de6f Low Surrogate-DE6F | de6e Low Surrogate-DE6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de71 Low Surrogate-DE71 | de70 Low Surrogate-DE70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de73 Low Surrogate-DE73 | de72 Low Surrogate-DE72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de75 Low Surrogate-DE75 | de74 Low Surrogate-DE74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de77 Low Surrogate-DE77 | de76 Low Surrogate-DE76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de79 Low Surrogate-DE79 | de78 Low Surrogate-DE78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7b Low Surrogate-DE7B | de7a Low Surrogate-DE7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7d Low Surrogate-DE7D | de7c Low Surrogate-DE7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de7f Low Surrogate-DE7F | de7e Low Surrogate-DE7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de81 Low Surrogate-DE81 | de80 Low Surrogate-DE80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de83 Low Surrogate-DE83 | de82 Low Surrogate-DE82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de85 Low Surrogate-DE85 | de84 Low Surrogate-DE84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de87 Low Surrogate-DE87 | de86 Low Surrogate-DE86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de89 Low Surrogate-DE89 | de88 Low Surrogate-DE88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8b Low Surrogate-DE8B | de8a Low Surrogate-DE8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8d Low Surrogate-DE8D | de8c Low Surrogate-DE8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de8f Low Surrogate-DE8F | de8e Low Surrogate-DE8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de91 Low Surrogate-DE91 | de90 Low Surrogate-DE90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de93 Low Surrogate-DE93 | de92 Low Surrogate-DE92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de95 Low Surrogate-DE95 | de94 Low Surrogate-DE94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de97 Low Surrogate-DE97 | de96 Low Surrogate-DE96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de99 Low Surrogate-DE99 | de98 Low Surrogate-DE98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9b Low Surrogate-DE9B | de9a Low Surrogate-DE9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9d Low Surrogate-DE9D | de9c Low Surrogate-DE9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* de9f Low Surrogate-DE9F | de9e Low Surrogate-DE9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea1 Low Surrogate-DEA1 | dea0 Low Surrogate-DEA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea3 Low Surrogate-DEA3 | dea2 Low Surrogate-DEA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea5 Low Surrogate-DEA5 | dea4 Low Surrogate-DEA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea7 Low Surrogate-DEA7 | dea6 Low Surrogate-DEA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dea9 Low Surrogate-DEA9 | dea8 Low Surrogate-DEA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deab Low Surrogate-DEAB | deaa Low Surrogate-DEAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dead Low Surrogate-DEAD | deac Low Surrogate-DEAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deaf Low Surrogate-DEAF | deae Low Surrogate-DEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb1 Low Surrogate-DEB1 | deb0 Low Surrogate-DEB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb3 Low Surrogate-DEB3 | deb2 Low Surrogate-DEB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb5 Low Surrogate-DEB5 | deb4 Low Surrogate-DEB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb7 Low Surrogate-DEB7 | deb6 Low Surrogate-DEB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deb9 Low Surrogate-DEB9 | deb8 Low Surrogate-DEB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debb Low Surrogate-DEBB | deba Low Surrogate-DEBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debd Low Surrogate-DEBD | debc Low Surrogate-DEBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* debf Low Surrogate-DEBF | debe Low Surrogate-DEBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec1 Low Surrogate-DEC1 | dec0 Low Surrogate-DEC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec3 Low Surrogate-DEC3 | dec2 Low Surrogate-DEC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec5 Low Surrogate-DEC5 | dec4 Low Surrogate-DEC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec7 Low Surrogate-DEC7 | dec6 Low Surrogate-DEC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dec9 Low Surrogate-DEC9 | dec8 Low Surrogate-DEC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decb Low Surrogate-DECB | deca Low Surrogate-DECA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decd Low Surrogate-DECD | decc Low Surrogate-DECC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* decf Low Surrogate-DECF | dece Low Surrogate-DECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded1 Low Surrogate-DED1 | ded0 Low Surrogate-DED0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded3 Low Surrogate-DED3 | ded2 Low Surrogate-DED2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded5 Low Surrogate-DED5 | ded4 Low Surrogate-DED4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded7 Low Surrogate-DED7 | ded6 Low Surrogate-DED6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ded9 Low Surrogate-DED9 | ded8 Low Surrogate-DED8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedb Low Surrogate-DEDB | deda Low Surrogate-DEDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedd Low Surrogate-DEDD | dedc Low Surrogate-DEDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dedf Low Surrogate-DEDF | dede Low Surrogate-DEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee1 Low Surrogate-DEE1 | dee0 Low Surrogate-DEE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee3 Low Surrogate-DEE3 | dee2 Low Surrogate-DEE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee5 Low Surrogate-DEE5 | dee4 Low Surrogate-DEE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee7 Low Surrogate-DEE7 | dee6 Low Surrogate-DEE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dee9 Low Surrogate-DEE9 | dee8 Low Surrogate-DEE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deeb Low Surrogate-DEEB | deea Low Surrogate-DEEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deed Low Surrogate-DEED | deec Low Surrogate-DEEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deef Low Surrogate-DEEF | deee Low Surrogate-DEEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def1 Low Surrogate-DEF1 | def0 Low Surrogate-DEF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def3 Low Surrogate-DEF3 | def2 Low Surrogate-DEF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def5 Low Surrogate-DEF5 | def4 Low Surrogate-DEF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def7 Low Surrogate-DEF7 | def6 Low Surrogate-DEF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* def9 Low Surrogate-DEF9 | def8 Low Surrogate-DEF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* defb Low Surrogate-DEFB | defa Low Surrogate-DEFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* defd Low Surrogate-DEFD | defc Low Surrogate-DEFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* deff Low Surrogate-DEFF | defe Low Surrogate-DEFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df01 Low Surrogate-DF01 | df00 Low Surrogate-DF00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df03 Low Surrogate-DF03 | df02 Low Surrogate-DF02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df05 Low Surrogate-DF05 | df04 Low Surrogate-DF04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df07 Low Surrogate-DF07 | df06 Low Surrogate-DF06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df09 Low Surrogate-DF09 | df08 Low Surrogate-DF08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0b Low Surrogate-DF0B | df0a Low Surrogate-DF0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0d Low Surrogate-DF0D | df0c Low Surrogate-DF0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df0f Low Surrogate-DF0F | df0e Low Surrogate-DF0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df11 Low Surrogate-DF11 | df10 Low Surrogate-DF10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df13 Low Surrogate-DF13 | df12 Low Surrogate-DF12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df15 Low Surrogate-DF15 | df14 Low Surrogate-DF14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df17 Low Surrogate-DF17 | df16 Low Surrogate-DF16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df19 Low Surrogate-DF19 | df18 Low Surrogate-DF18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1b Low Surrogate-DF1B | df1a Low Surrogate-DF1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1d Low Surrogate-DF1D | df1c Low Surrogate-DF1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df1f Low Surrogate-DF1F | df1e Low Surrogate-DF1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df21 Low Surrogate-DF21 | df20 Low Surrogate-DF20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df23 Low Surrogate-DF23 | df22 Low Surrogate-DF22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df25 Low Surrogate-DF25 | df24 Low Surrogate-DF24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df27 Low Surrogate-DF27 | df26 Low Surrogate-DF26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df29 Low Surrogate-DF29 | df28 Low Surrogate-DF28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2b Low Surrogate-DF2B | df2a Low Surrogate-DF2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2d Low Surrogate-DF2D | df2c Low Surrogate-DF2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df2f Low Surrogate-DF2F | df2e Low Surrogate-DF2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df31 Low Surrogate-DF31 | df30 Low Surrogate-DF30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df33 Low Surrogate-DF33 | df32 Low Surrogate-DF32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df35 Low Surrogate-DF35 | df34 Low Surrogate-DF34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df37 Low Surrogate-DF37 | df36 Low Surrogate-DF36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df39 Low Surrogate-DF39 | df38 Low Surrogate-DF38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3b Low Surrogate-DF3B | df3a Low Surrogate-DF3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3d Low Surrogate-DF3D | df3c Low Surrogate-DF3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df3f Low Surrogate-DF3F | df3e Low Surrogate-DF3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df41 Low Surrogate-DF41 | df40 Low Surrogate-DF40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df43 Low Surrogate-DF43 | df42 Low Surrogate-DF42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df45 Low Surrogate-DF45 | df44 Low Surrogate-DF44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df47 Low Surrogate-DF47 | df46 Low Surrogate-DF46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df49 Low Surrogate-DF49 | df48 Low Surrogate-DF48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4b Low Surrogate-DF4B | df4a Low Surrogate-DF4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4d Low Surrogate-DF4D | df4c Low Surrogate-DF4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df4f Low Surrogate-DF4F | df4e Low Surrogate-DF4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df51 Low Surrogate-DF51 | df50 Low Surrogate-DF50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df53 Low Surrogate-DF53 | df52 Low Surrogate-DF52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df55 Low Surrogate-DF55 | df54 Low Surrogate-DF54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df57 Low Surrogate-DF57 | df56 Low Surrogate-DF56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df59 Low Surrogate-DF59 | df58 Low Surrogate-DF58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5b Low Surrogate-DF5B | df5a Low Surrogate-DF5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5d Low Surrogate-DF5D | df5c Low Surrogate-DF5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df5f Low Surrogate-DF5F | df5e Low Surrogate-DF5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df61 Low Surrogate-DF61 | df60 Low Surrogate-DF60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df63 Low Surrogate-DF63 | df62 Low Surrogate-DF62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df65 Low Surrogate-DF65 | df64 Low Surrogate-DF64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df67 Low Surrogate-DF67 | df66 Low Surrogate-DF66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df69 Low Surrogate-DF69 | df68 Low Surrogate-DF68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6b Low Surrogate-DF6B | df6a Low Surrogate-DF6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6d Low Surrogate-DF6D | df6c Low Surrogate-DF6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df6f Low Surrogate-DF6F | df6e Low Surrogate-DF6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df71 Low Surrogate-DF71 | df70 Low Surrogate-DF70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df73 Low Surrogate-DF73 | df72 Low Surrogate-DF72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df75 Low Surrogate-DF75 | df74 Low Surrogate-DF74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df77 Low Surrogate-DF77 | df76 Low Surrogate-DF76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df79 Low Surrogate-DF79 | df78 Low Surrogate-DF78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7b Low Surrogate-DF7B | df7a Low Surrogate-DF7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7d Low Surrogate-DF7D | df7c Low Surrogate-DF7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df7f Low Surrogate-DF7F | df7e Low Surrogate-DF7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df81 Low Surrogate-DF81 | df80 Low Surrogate-DF80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df83 Low Surrogate-DF83 | df82 Low Surrogate-DF82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df85 Low Surrogate-DF85 | df84 Low Surrogate-DF84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df87 Low Surrogate-DF87 | df86 Low Surrogate-DF86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df89 Low Surrogate-DF89 | df88 Low Surrogate-DF88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8b Low Surrogate-DF8B | df8a Low Surrogate-DF8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8d Low Surrogate-DF8D | df8c Low Surrogate-DF8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df8f Low Surrogate-DF8F | df8e Low Surrogate-DF8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df91 Low Surrogate-DF91 | df90 Low Surrogate-DF90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df93 Low Surrogate-DF93 | df92 Low Surrogate-DF92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df95 Low Surrogate-DF95 | df94 Low Surrogate-DF94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df97 Low Surrogate-DF97 | df96 Low Surrogate-DF96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df99 Low Surrogate-DF99 | df98 Low Surrogate-DF98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9b Low Surrogate-DF9B | df9a Low Surrogate-DF9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9d Low Surrogate-DF9D | df9c Low Surrogate-DF9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* df9f Low Surrogate-DF9F | df9e Low Surrogate-DF9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa1 Low Surrogate-DFA1 | dfa0 Low Surrogate-DFA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa3 Low Surrogate-DFA3 | dfa2 Low Surrogate-DFA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa5 Low Surrogate-DFA5 | dfa4 Low Surrogate-DFA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa7 Low Surrogate-DFA7 | dfa6 Low Surrogate-DFA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfa9 Low Surrogate-DFA9 | dfa8 Low Surrogate-DFA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfab Low Surrogate-DFAB | dfaa Low Surrogate-DFAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfad Low Surrogate-DFAD | dfac Low Surrogate-DFAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfaf Low Surrogate-DFAF | dfae Low Surrogate-DFAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb1 Low Surrogate-DFB1 | dfb0 Low Surrogate-DFB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb3 Low Surrogate-DFB3 | dfb2 Low Surrogate-DFB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb5 Low Surrogate-DFB5 | dfb4 Low Surrogate-DFB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb7 Low Surrogate-DFB7 | dfb6 Low Surrogate-DFB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfb9 Low Surrogate-DFB9 | dfb8 Low Surrogate-DFB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbb Low Surrogate-DFBB | dfba Low Surrogate-DFBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbd Low Surrogate-DFBD | dfbc Low Surrogate-DFBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfbf Low Surrogate-DFBF | dfbe Low Surrogate-DFBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc1 Low Surrogate-DFC1 | dfc0 Low Surrogate-DFC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc3 Low Surrogate-DFC3 | dfc2 Low Surrogate-DFC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc5 Low Surrogate-DFC5 | dfc4 Low Surrogate-DFC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc7 Low Surrogate-DFC7 | dfc6 Low Surrogate-DFC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfc9 Low Surrogate-DFC9 | dfc8 Low Surrogate-DFC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcb Low Surrogate-DFCB | dfca Low Surrogate-DFCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcd Low Surrogate-DFCD | dfcc Low Surrogate-DFCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfcf Low Surrogate-DFCF | dfce Low Surrogate-DFCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd1 Low Surrogate-DFD1 | dfd0 Low Surrogate-DFD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd3 Low Surrogate-DFD3 | dfd2 Low Surrogate-DFD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd5 Low Surrogate-DFD5 | dfd4 Low Surrogate-DFD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd7 Low Surrogate-DFD7 | dfd6 Low Surrogate-DFD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfd9 Low Surrogate-DFD9 | dfd8 Low Surrogate-DFD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdb Low Surrogate-DFDB | dfda Low Surrogate-DFDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdd Low Surrogate-DFDD | dfdc Low Surrogate-DFDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfdf Low Surrogate-DFDF | dfde Low Surrogate-DFDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe1 Low Surrogate-DFE1 | dfe0 Low Surrogate-DFE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe3 Low Surrogate-DFE3 | dfe2 Low Surrogate-DFE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe5 Low Surrogate-DFE5 | dfe4 Low Surrogate-DFE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe7 Low Surrogate-DFE7 | dfe6 Low Surrogate-DFE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfe9 Low Surrogate-DFE9 | dfe8 Low Surrogate-DFE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfeb Low Surrogate-DFEB | dfea Low Surrogate-DFEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfed Low Surrogate-DFED | dfec Low Surrogate-DFEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfef Low Surrogate-DFEF | dfee Low Surrogate-DFEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff1 Low Surrogate-DFF1 | dff0 Low Surrogate-DFF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff3 Low Surrogate-DFF3 | dff2 Low Surrogate-DFF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff5 Low Surrogate-DFF5 | dff4 Low Surrogate-DFF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff7 Low Surrogate-DFF7 | dff6 Low Surrogate-DFF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dff9 Low Surrogate-DFF9 | dff8 Low Surrogate-DFF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dffb Low Surrogate-DFFB | dffa Low Surrogate-DFFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dffd Low Surrogate-DFFD | dffc Low Surrogate-DFFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* dfff Low Surrogate-DFFF | dffe Low Surrogate-DFFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e001 Private Use-E001 | e000 Private Use-E000 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e003 Private Use-E003 | e002 Private Use-E002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e005 Private Use-E005 | e004 Private Use-E004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e007 Private Use-E007 | e006 Private Use-E006 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e009 Private Use-E009 | e008 Private Use-E008 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00b Private Use-E00B | e00a Private Use-E00A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00d Private Use-E00D | e00c Private Use-E00C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e00f Private Use-E00F | e00e Private Use-E00E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e011 Private Use-E011 | e010 Private Use-E010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e013 Private Use-E013 | e012 Private Use-E012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e015 Private Use-E015 | e014 Private Use-E014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e017 Private Use-E017 | e016 Private Use-E016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e019 Private Use-E019 | e018 Private Use-E018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01b Private Use-E01B | e01a Private Use-E01A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01d Private Use-E01D | e01c Private Use-E01C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e01f Private Use-E01F | e01e Private Use-E01E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e021 Private Use-E021 | e020 Private Use-E020 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e023 Private Use-E023 | e022 Private Use-E022 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e025 Private Use-E025 | e024 Private Use-E024 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e027 Private Use-E027 | e026 Private Use-E026 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e029 Private Use-E029 | e028 Private Use-E028 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02b Private Use-E02B | e02a Private Use-E02A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02d Private Use-E02D | e02c Private Use-E02C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e02f Private Use-E02F | e02e Private Use-E02E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e031 Private Use-E031 | e030 Private Use-E030 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e033 Private Use-E033 | e032 Private Use-E032 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e035 Private Use-E035 | e034 Private Use-E034 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e037 Private Use-E037 | e036 Private Use-E036 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e039 Private Use-E039 | e038 Private Use-E038 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03b Private Use-E03B | e03a Private Use-E03A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03d Private Use-E03D | e03c Private Use-E03C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e03f Private Use-E03F | e03e Private Use-E03E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e041 Private Use-E041 | e040 Private Use-E040 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e043 Private Use-E043 | e042 Private Use-E042 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e045 Private Use-E045 | e044 Private Use-E044 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e047 Private Use-E047 | e046 Private Use-E046 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e049 Private Use-E049 | e048 Private Use-E048 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04b Private Use-E04B | e04a Private Use-E04A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04d Private Use-E04D | e04c Private Use-E04C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e04f Private Use-E04F | e04e Private Use-E04E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e051 Private Use-E051 | e050 Private Use-E050 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e053 Private Use-E053 | e052 Private Use-E052 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e055 Private Use-E055 | e054 Private Use-E054 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e057 Private Use-E057 | e056 Private Use-E056 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e059 Private Use-E059 | e058 Private Use-E058 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05b Private Use-E05B | e05a Private Use-E05A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05d Private Use-E05D | e05c Private Use-E05C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e05f Private Use-E05F | e05e Private Use-E05E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e061 Private Use-E061 | e060 Private Use-E060 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e063 Private Use-E063 | e062 Private Use-E062 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e065 Private Use-E065 | e064 Private Use-E064 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e067 Private Use-E067 | e066 Private Use-E066 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e069 Private Use-E069 | e068 Private Use-E068 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06b Private Use-E06B | e06a Private Use-E06A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06d Private Use-E06D | e06c Private Use-E06C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e06f Private Use-E06F | e06e Private Use-E06E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e071 Private Use-E071 | e070 Private Use-E070 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e073 Private Use-E073 | e072 Private Use-E072 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e075 Private Use-E075 | e074 Private Use-E074 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e077 Private Use-E077 | e076 Private Use-E076 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e079 Private Use-E079 | e078 Private Use-E078 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07b Private Use-E07B | e07a Private Use-E07A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07d Private Use-E07D | e07c Private Use-E07C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e07f Private Use-E07F | e07e Private Use-E07E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e081 Private Use-E081 | e080 Private Use-E080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e083 Private Use-E083 | e082 Private Use-E082 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e085 Private Use-E085 | e084 Private Use-E084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e087 Private Use-E087 | e086 Private Use-E086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e089 Private Use-E089 | e088 Private Use-E088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08b Private Use-E08B | e08a Private Use-E08A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08d Private Use-E08D | e08c Private Use-E08C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e08f Private Use-E08F | e08e Private Use-E08E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e091 Private Use-E091 | e090 Private Use-E090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e093 Private Use-E093 | e092 Private Use-E092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e095 Private Use-E095 | e094 Private Use-E094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e097 Private Use-E097 | e096 Private Use-E096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e099 Private Use-E099 | e098 Private Use-E098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09b Private Use-E09B | e09a Private Use-E09A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09d Private Use-E09D | e09c Private Use-E09C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e09f Private Use-E09F | e09e Private Use-E09E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a1 Private Use-E0A1 | e0a0 Private Use-E0A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a3 Private Use-E0A3 | e0a2 Private Use-E0A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a5 Private Use-E0A5 | e0a4 Private Use-E0A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a7 Private Use-E0A7 | e0a6 Private Use-E0A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0a9 Private Use-E0A9 | e0a8 Private Use-E0A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ab Private Use-E0AB | e0aa Private Use-E0AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ad Private Use-E0AD | e0ac Private Use-E0AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0af Private Use-E0AF | e0ae Private Use-E0AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b1 Private Use-E0B1 | e0b0 Private Use-E0B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b3 Private Use-E0B3 | e0b2 Private Use-E0B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b5 Private Use-E0B5 | e0b4 Private Use-E0B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b7 Private Use-E0B7 | e0b6 Private Use-E0B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0b9 Private Use-E0B9 | e0b8 Private Use-E0B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bb Private Use-E0BB | e0ba Private Use-E0BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bd Private Use-E0BD | e0bc Private Use-E0BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0bf Private Use-E0BF | e0be Private Use-E0BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c1 Private Use-E0C1 | e0c0 Private Use-E0C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c3 Private Use-E0C3 | e0c2 Private Use-E0C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c5 Private Use-E0C5 | e0c4 Private Use-E0C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c7 Private Use-E0C7 | e0c6 Private Use-E0C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0c9 Private Use-E0C9 | e0c8 Private Use-E0C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cb Private Use-E0CB | e0ca Private Use-E0CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cd Private Use-E0CD | e0cc Private Use-E0CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0cf Private Use-E0CF | e0ce Private Use-E0CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d1 Private Use-E0D1 | e0d0 Private Use-E0D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d3 Private Use-E0D3 | e0d2 Private Use-E0D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d5 Private Use-E0D5 | e0d4 Private Use-E0D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d7 Private Use-E0D7 | e0d6 Private Use-E0D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0d9 Private Use-E0D9 | e0d8 Private Use-E0D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0db Private Use-E0DB | e0da Private Use-E0DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0dd Private Use-E0DD | e0dc Private Use-E0DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0df Private Use-E0DF | e0de Private Use-E0DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e1 Private Use-E0E1 | e0e0 Private Use-E0E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e3 Private Use-E0E3 | e0e2 Private Use-E0E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e5 Private Use-E0E5 | e0e4 Private Use-E0E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e7 Private Use-E0E7 | e0e6 Private Use-E0E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0e9 Private Use-E0E9 | e0e8 Private Use-E0E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0eb Private Use-E0EB | e0ea Private Use-E0EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ed Private Use-E0ED | e0ec Private Use-E0EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ef Private Use-E0EF | e0ee Private Use-E0EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f1 Private Use-E0F1 | e0f0 Private Use-E0F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f3 Private Use-E0F3 | e0f2 Private Use-E0F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f5 Private Use-E0F5 | e0f4 Private Use-E0F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f7 Private Use-E0F7 | e0f6 Private Use-E0F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0f9 Private Use-E0F9 | e0f8 Private Use-E0F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0fb Private Use-E0FB | e0fa Private Use-E0FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0fd Private Use-E0FD | e0fc Private Use-E0FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e0ff Private Use-E0FF | e0fe Private Use-E0FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e101 Private Use-E101 | e100 Private Use-E100 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e103 Private Use-E103 | e102 Private Use-E102 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e105 Private Use-E105 | e104 Private Use-E104 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e107 Private Use-E107 | e106 Private Use-E106 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e109 Private Use-E109 | e108 Private Use-E108 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10b Private Use-E10B | e10a Private Use-E10A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10d Private Use-E10D | e10c Private Use-E10C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e10f Private Use-E10F | e10e Private Use-E10E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e111 Private Use-E111 | e110 Private Use-E110 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e113 Private Use-E113 | e112 Private Use-E112 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e115 Private Use-E115 | e114 Private Use-E114 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e117 Private Use-E117 | e116 Private Use-E116 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e119 Private Use-E119 | e118 Private Use-E118 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11b Private Use-E11B | e11a Private Use-E11A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11d Private Use-E11D | e11c Private Use-E11C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e11f Private Use-E11F | e11e Private Use-E11E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e121 Private Use-E121 | e120 Private Use-E120 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e123 Private Use-E123 | e122 Private Use-E122 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e125 Private Use-E125 | e124 Private Use-E124 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e127 Private Use-E127 | e126 Private Use-E126 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e129 Private Use-E129 | e128 Private Use-E128 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12b Private Use-E12B | e12a Private Use-E12A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12d Private Use-E12D | e12c Private Use-E12C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e12f Private Use-E12F | e12e Private Use-E12E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e131 Private Use-E131 | e130 Private Use-E130 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e133 Private Use-E133 | e132 Private Use-E132 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e135 Private Use-E135 | e134 Private Use-E134 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e137 Private Use-E137 | e136 Private Use-E136 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e139 Private Use-E139 | e138 Private Use-E138 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13b Private Use-E13B | e13a Private Use-E13A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13d Private Use-E13D | e13c Private Use-E13C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e13f Private Use-E13F | e13e Private Use-E13E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e141 Private Use-E141 | e140 Private Use-E140 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e143 Private Use-E143 | e142 Private Use-E142 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e145 Private Use-E145 | e144 Private Use-E144 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e147 Private Use-E147 | e146 Private Use-E146 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e149 Private Use-E149 | e148 Private Use-E148 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14b Private Use-E14B | e14a Private Use-E14A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14d Private Use-E14D | e14c Private Use-E14C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e14f Private Use-E14F | e14e Private Use-E14E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e151 Private Use-E151 | e150 Private Use-E150 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e153 Private Use-E153 | e152 Private Use-E152 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e155 Private Use-E155 | e154 Private Use-E154 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e157 Private Use-E157 | e156 Private Use-E156 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e159 Private Use-E159 | e158 Private Use-E158 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15b Private Use-E15B | e15a Private Use-E15A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15d Private Use-E15D | e15c Private Use-E15C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e15f Private Use-E15F | e15e Private Use-E15E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e161 Private Use-E161 | e160 Private Use-E160 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e163 Private Use-E163 | e162 Private Use-E162 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e165 Private Use-E165 | e164 Private Use-E164 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e167 Private Use-E167 | e166 Private Use-E166 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e169 Private Use-E169 | e168 Private Use-E168 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16b Private Use-E16B | e16a Private Use-E16A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16d Private Use-E16D | e16c Private Use-E16C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e16f Private Use-E16F | e16e Private Use-E16E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e171 Private Use-E171 | e170 Private Use-E170 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e173 Private Use-E173 | e172 Private Use-E172 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e175 Private Use-E175 | e174 Private Use-E174 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e177 Private Use-E177 | e176 Private Use-E176 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e179 Private Use-E179 | e178 Private Use-E178 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17b Private Use-E17B | e17a Private Use-E17A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17d Private Use-E17D | e17c Private Use-E17C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e17f Private Use-E17F | e17e Private Use-E17E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e181 Private Use-E181 | e180 Private Use-E180 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e183 Private Use-E183 | e182 Private Use-E182 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e185 Private Use-E185 | e184 Private Use-E184 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e187 Private Use-E187 | e186 Private Use-E186 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e189 Private Use-E189 | e188 Private Use-E188 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18b Private Use-E18B | e18a Private Use-E18A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18d Private Use-E18D | e18c Private Use-E18C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e18f Private Use-E18F | e18e Private Use-E18E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e191 Private Use-E191 | e190 Private Use-E190 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e193 Private Use-E193 | e192 Private Use-E192 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e195 Private Use-E195 | e194 Private Use-E194 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e197 Private Use-E197 | e196 Private Use-E196 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e199 Private Use-E199 | e198 Private Use-E198 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19b Private Use-E19B | e19a Private Use-E19A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19d Private Use-E19D | e19c Private Use-E19C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e19f Private Use-E19F | e19e Private Use-E19E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a1 Private Use-E1A1 | e1a0 Private Use-E1A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a3 Private Use-E1A3 | e1a2 Private Use-E1A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a5 Private Use-E1A5 | e1a4 Private Use-E1A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a7 Private Use-E1A7 | e1a6 Private Use-E1A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1a9 Private Use-E1A9 | e1a8 Private Use-E1A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ab Private Use-E1AB | e1aa Private Use-E1AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ad Private Use-E1AD | e1ac Private Use-E1AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1af Private Use-E1AF | e1ae Private Use-E1AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b1 Private Use-E1B1 | e1b0 Private Use-E1B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b3 Private Use-E1B3 | e1b2 Private Use-E1B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b5 Private Use-E1B5 | e1b4 Private Use-E1B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b7 Private Use-E1B7 | e1b6 Private Use-E1B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1b9 Private Use-E1B9 | e1b8 Private Use-E1B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bb Private Use-E1BB | e1ba Private Use-E1BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bd Private Use-E1BD | e1bc Private Use-E1BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1bf Private Use-E1BF | e1be Private Use-E1BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c1 Private Use-E1C1 | e1c0 Private Use-E1C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c3 Private Use-E1C3 | e1c2 Private Use-E1C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c5 Private Use-E1C5 | e1c4 Private Use-E1C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c7 Private Use-E1C7 | e1c6 Private Use-E1C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1c9 Private Use-E1C9 | e1c8 Private Use-E1C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cb Private Use-E1CB | e1ca Private Use-E1CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cd Private Use-E1CD | e1cc Private Use-E1CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1cf Private Use-E1CF | e1ce Private Use-E1CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d1 Private Use-E1D1 | e1d0 Private Use-E1D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d3 Private Use-E1D3 | e1d2 Private Use-E1D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d5 Private Use-E1D5 | e1d4 Private Use-E1D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d7 Private Use-E1D7 | e1d6 Private Use-E1D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1d9 Private Use-E1D9 | e1d8 Private Use-E1D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1db Private Use-E1DB | e1da Private Use-E1DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1dd Private Use-E1DD | e1dc Private Use-E1DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1df Private Use-E1DF | e1de Private Use-E1DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e1 Private Use-E1E1 | e1e0 Private Use-E1E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e3 Private Use-E1E3 | e1e2 Private Use-E1E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e5 Private Use-E1E5 | e1e4 Private Use-E1E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e7 Private Use-E1E7 | e1e6 Private Use-E1E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1e9 Private Use-E1E9 | e1e8 Private Use-E1E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1eb Private Use-E1EB | e1ea Private Use-E1EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ed Private Use-E1ED | e1ec Private Use-E1EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ef Private Use-E1EF | e1ee Private Use-E1EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f1 Private Use-E1F1 | e1f0 Private Use-E1F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f3 Private Use-E1F3 | e1f2 Private Use-E1F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f5 Private Use-E1F5 | e1f4 Private Use-E1F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f7 Private Use-E1F7 | e1f6 Private Use-E1F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1f9 Private Use-E1F9 | e1f8 Private Use-E1F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1fb Private Use-E1FB | e1fa Private Use-E1FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1fd Private Use-E1FD | e1fc Private Use-E1FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e1ff Private Use-E1FF | e1fe Private Use-E1FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e201 Private Use-E201 | e200 Private Use-E200 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e203 Private Use-E203 | e202 Private Use-E202 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e205 Private Use-E205 | e204 Private Use-E204 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e207 Private Use-E207 | e206 Private Use-E206 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e209 Private Use-E209 | e208 Private Use-E208 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20b Private Use-E20B | e20a Private Use-E20A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20d Private Use-E20D | e20c Private Use-E20C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e20f Private Use-E20F | e20e Private Use-E20E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e211 Private Use-E211 | e210 Private Use-E210 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e213 Private Use-E213 | e212 Private Use-E212 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e215 Private Use-E215 | e214 Private Use-E214 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e217 Private Use-E217 | e216 Private Use-E216 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e219 Private Use-E219 | e218 Private Use-E218 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21b Private Use-E21B | e21a Private Use-E21A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21d Private Use-E21D | e21c Private Use-E21C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e21f Private Use-E21F | e21e Private Use-E21E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e221 Private Use-E221 | e220 Private Use-E220 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e223 Private Use-E223 | e222 Private Use-E222 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e225 Private Use-E225 | e224 Private Use-E224 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e227 Private Use-E227 | e226 Private Use-E226 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e229 Private Use-E229 | e228 Private Use-E228 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22b Private Use-E22B | e22a Private Use-E22A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22d Private Use-E22D | e22c Private Use-E22C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e22f Private Use-E22F | e22e Private Use-E22E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e231 Private Use-E231 | e230 Private Use-E230 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e233 Private Use-E233 | e232 Private Use-E232 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e235 Private Use-E235 | e234 Private Use-E234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e237 Private Use-E237 | e236 Private Use-E236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e239 Private Use-E239 | e238 Private Use-E238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23b Private Use-E23B | e23a Private Use-E23A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23d Private Use-E23D | e23c Private Use-E23C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e23f Private Use-E23F | e23e Private Use-E23E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e241 Private Use-E241 | e240 Private Use-E240 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e243 Private Use-E243 | e242 Private Use-E242 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e245 Private Use-E245 | e244 Private Use-E244 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e247 Private Use-E247 | e246 Private Use-E246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e249 Private Use-E249 | e248 Private Use-E248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24b Private Use-E24B | e24a Private Use-E24A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24d Private Use-E24D | e24c Private Use-E24C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e24f Private Use-E24F | e24e Private Use-E24E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e251 Private Use-E251 | e250 Private Use-E250 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e253 Private Use-E253 | e252 Private Use-E252 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e255 Private Use-E255 | e254 Private Use-E254 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e257 Private Use-E257 | e256 Private Use-E256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e259 Private Use-E259 | e258 Private Use-E258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25b Private Use-E25B | e25a Private Use-E25A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25d Private Use-E25D | e25c Private Use-E25C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e25f Private Use-E25F | e25e Private Use-E25E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e261 Private Use-E261 | e260 Private Use-E260 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e263 Private Use-E263 | e262 Private Use-E262 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e265 Private Use-E265 | e264 Private Use-E264 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e267 Private Use-E267 | e266 Private Use-E266 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e269 Private Use-E269 | e268 Private Use-E268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26b Private Use-E26B | e26a Private Use-E26A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26d Private Use-E26D | e26c Private Use-E26C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e26f Private Use-E26F | e26e Private Use-E26E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e271 Private Use-E271 | e270 Private Use-E270 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e273 Private Use-E273 | e272 Private Use-E272 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e275 Private Use-E275 | e274 Private Use-E274 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e277 Private Use-E277 | e276 Private Use-E276 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e279 Private Use-E279 | e278 Private Use-E278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27b Private Use-E27B | e27a Private Use-E27A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27d Private Use-E27D | e27c Private Use-E27C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e27f Private Use-E27F | e27e Private Use-E27E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e281 Private Use-E281 | e280 Private Use-E280 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e283 Private Use-E283 | e282 Private Use-E282 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e285 Private Use-E285 | e284 Private Use-E284 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e287 Private Use-E287 | e286 Private Use-E286 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e289 Private Use-E289 | e288 Private Use-E288 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28b Private Use-E28B | e28a Private Use-E28A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28d Private Use-E28D | e28c Private Use-E28C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e28f Private Use-E28F | e28e Private Use-E28E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e291 Private Use-E291 | e290 Private Use-E290 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e293 Private Use-E293 | e292 Private Use-E292 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e295 Private Use-E295 | e294 Private Use-E294 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e297 Private Use-E297 | e296 Private Use-E296 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e299 Private Use-E299 | e298 Private Use-E298 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29b Private Use-E29B | e29a Private Use-E29A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29d Private Use-E29D | e29c Private Use-E29C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e29f Private Use-E29F | e29e Private Use-E29E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a1 Private Use-E2A1 | e2a0 Private Use-E2A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a3 Private Use-E2A3 | e2a2 Private Use-E2A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a5 Private Use-E2A5 | e2a4 Private Use-E2A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a7 Private Use-E2A7 | e2a6 Private Use-E2A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2a9 Private Use-E2A9 | e2a8 Private Use-E2A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ab Private Use-E2AB | e2aa Private Use-E2AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ad Private Use-E2AD | e2ac Private Use-E2AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2af Private Use-E2AF | e2ae Private Use-E2AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b1 Private Use-E2B1 | e2b0 Private Use-E2B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b3 Private Use-E2B3 | e2b2 Private Use-E2B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b5 Private Use-E2B5 | e2b4 Private Use-E2B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b7 Private Use-E2B7 | e2b6 Private Use-E2B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2b9 Private Use-E2B9 | e2b8 Private Use-E2B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bb Private Use-E2BB | e2ba Private Use-E2BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bd Private Use-E2BD | e2bc Private Use-E2BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2bf Private Use-E2BF | e2be Private Use-E2BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c1 Private Use-E2C1 | e2c0 Private Use-E2C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c3 Private Use-E2C3 | e2c2 Private Use-E2C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c5 Private Use-E2C5 | e2c4 Private Use-E2C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c7 Private Use-E2C7 | e2c6 Private Use-E2C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2c9 Private Use-E2C9 | e2c8 Private Use-E2C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cb Private Use-E2CB | e2ca Private Use-E2CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cd Private Use-E2CD | e2cc Private Use-E2CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2cf Private Use-E2CF | e2ce Private Use-E2CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d1 Private Use-E2D1 | e2d0 Private Use-E2D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d3 Private Use-E2D3 | e2d2 Private Use-E2D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d5 Private Use-E2D5 | e2d4 Private Use-E2D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d7 Private Use-E2D7 | e2d6 Private Use-E2D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2d9 Private Use-E2D9 | e2d8 Private Use-E2D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2db Private Use-E2DB | e2da Private Use-E2DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2dd Private Use-E2DD | e2dc Private Use-E2DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2df Private Use-E2DF | e2de Private Use-E2DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e1 Private Use-E2E1 | e2e0 Private Use-E2E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e3 Private Use-E2E3 | e2e2 Private Use-E2E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e5 Private Use-E2E5 | e2e4 Private Use-E2E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e7 Private Use-E2E7 | e2e6 Private Use-E2E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2e9 Private Use-E2E9 | e2e8 Private Use-E2E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2eb Private Use-E2EB | e2ea Private Use-E2EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ed Private Use-E2ED | e2ec Private Use-E2EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ef Private Use-E2EF | e2ee Private Use-E2EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f1 Private Use-E2F1 | e2f0 Private Use-E2F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f3 Private Use-E2F3 | e2f2 Private Use-E2F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f5 Private Use-E2F5 | e2f4 Private Use-E2F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f7 Private Use-E2F7 | e2f6 Private Use-E2F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2f9 Private Use-E2F9 | e2f8 Private Use-E2F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2fb Private Use-E2FB | e2fa Private Use-E2FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2fd Private Use-E2FD | e2fc Private Use-E2FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e2ff Private Use-E2FF | e2fe Private Use-E2FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e301 Private Use-E301 | e300 Private Use-E300 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e303 Private Use-E303 | e302 Private Use-E302 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e305 Private Use-E305 | e304 Private Use-E304 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e307 Private Use-E307 | e306 Private Use-E306 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e309 Private Use-E309 | e308 Private Use-E308 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30b Private Use-E30B | e30a Private Use-E30A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30d Private Use-E30D | e30c Private Use-E30C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e30f Private Use-E30F | e30e Private Use-E30E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e311 Private Use-E311 | e310 Private Use-E310 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e313 Private Use-E313 | e312 Private Use-E312 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e315 Private Use-E315 | e314 Private Use-E314 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e317 Private Use-E317 | e316 Private Use-E316 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e319 Private Use-E319 | e318 Private Use-E318 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31b Private Use-E31B | e31a Private Use-E31A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31d Private Use-E31D | e31c Private Use-E31C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e31f Private Use-E31F | e31e Private Use-E31E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e321 Private Use-E321 | e320 Private Use-E320 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e323 Private Use-E323 | e322 Private Use-E322 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e325 Private Use-E325 | e324 Private Use-E324 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e327 Private Use-E327 | e326 Private Use-E326 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e329 Private Use-E329 | e328 Private Use-E328 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32b Private Use-E32B | e32a Private Use-E32A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32d Private Use-E32D | e32c Private Use-E32C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e32f Private Use-E32F | e32e Private Use-E32E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e331 Private Use-E331 | e330 Private Use-E330 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e333 Private Use-E333 | e332 Private Use-E332 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e335 Private Use-E335 | e334 Private Use-E334 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e337 Private Use-E337 | e336 Private Use-E336 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e339 Private Use-E339 | e338 Private Use-E338 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33b Private Use-E33B | e33a Private Use-E33A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33d Private Use-E33D | e33c Private Use-E33C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e33f Private Use-E33F | e33e Private Use-E33E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e341 Private Use-E341 | e340 Private Use-E340 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e343 Private Use-E343 | e342 Private Use-E342 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e345 Private Use-E345 | e344 Private Use-E344 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e347 Private Use-E347 | e346 Private Use-E346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e349 Private Use-E349 | e348 Private Use-E348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34b Private Use-E34B | e34a Private Use-E34A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34d Private Use-E34D | e34c Private Use-E34C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e34f Private Use-E34F | e34e Private Use-E34E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e351 Private Use-E351 | e350 Private Use-E350 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e353 Private Use-E353 | e352 Private Use-E352 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e355 Private Use-E355 | e354 Private Use-E354 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e357 Private Use-E357 | e356 Private Use-E356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e359 Private Use-E359 | e358 Private Use-E358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35b Private Use-E35B | e35a Private Use-E35A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35d Private Use-E35D | e35c Private Use-E35C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e35f Private Use-E35F | e35e Private Use-E35E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e361 Private Use-E361 | e360 Private Use-E360 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e363 Private Use-E363 | e362 Private Use-E362 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e365 Private Use-E365 | e364 Private Use-E364 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e367 Private Use-E367 | e366 Private Use-E366 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e369 Private Use-E369 | e368 Private Use-E368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36b Private Use-E36B | e36a Private Use-E36A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36d Private Use-E36D | e36c Private Use-E36C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e36f Private Use-E36F | e36e Private Use-E36E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e371 Private Use-E371 | e370 Private Use-E370 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e373 Private Use-E373 | e372 Private Use-E372 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e375 Private Use-E375 | e374 Private Use-E374 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e377 Private Use-E377 | e376 Private Use-E376 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e379 Private Use-E379 | e378 Private Use-E378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37b Private Use-E37B | e37a Private Use-E37A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37d Private Use-E37D | e37c Private Use-E37C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e37f Private Use-E37F | e37e Private Use-E37E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e381 Private Use-E381 | e380 Private Use-E380 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e383 Private Use-E383 | e382 Private Use-E382 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e385 Private Use-E385 | e384 Private Use-E384 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e387 Private Use-E387 | e386 Private Use-E386 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e389 Private Use-E389 | e388 Private Use-E388 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38b Private Use-E38B | e38a Private Use-E38A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38d Private Use-E38D | e38c Private Use-E38C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e38f Private Use-E38F | e38e Private Use-E38E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e391 Private Use-E391 | e390 Private Use-E390 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e393 Private Use-E393 | e392 Private Use-E392 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e395 Private Use-E395 | e394 Private Use-E394 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e397 Private Use-E397 | e396 Private Use-E396 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e399 Private Use-E399 | e398 Private Use-E398 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39b Private Use-E39B | e39a Private Use-E39A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39d Private Use-E39D | e39c Private Use-E39C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e39f Private Use-E39F | e39e Private Use-E39E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a1 Private Use-E3A1 | e3a0 Private Use-E3A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a3 Private Use-E3A3 | e3a2 Private Use-E3A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a5 Private Use-E3A5 | e3a4 Private Use-E3A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a7 Private Use-E3A7 | e3a6 Private Use-E3A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3a9 Private Use-E3A9 | e3a8 Private Use-E3A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ab Private Use-E3AB | e3aa Private Use-E3AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ad Private Use-E3AD | e3ac Private Use-E3AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3af Private Use-E3AF | e3ae Private Use-E3AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b1 Private Use-E3B1 | e3b0 Private Use-E3B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b3 Private Use-E3B3 | e3b2 Private Use-E3B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b5 Private Use-E3B5 | e3b4 Private Use-E3B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b7 Private Use-E3B7 | e3b6 Private Use-E3B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3b9 Private Use-E3B9 | e3b8 Private Use-E3B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bb Private Use-E3BB | e3ba Private Use-E3BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bd Private Use-E3BD | e3bc Private Use-E3BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3bf Private Use-E3BF | e3be Private Use-E3BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c1 Private Use-E3C1 | e3c0 Private Use-E3C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c3 Private Use-E3C3 | e3c2 Private Use-E3C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c5 Private Use-E3C5 | e3c4 Private Use-E3C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c7 Private Use-E3C7 | e3c6 Private Use-E3C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3c9 Private Use-E3C9 | e3c8 Private Use-E3C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cb Private Use-E3CB | e3ca Private Use-E3CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cd Private Use-E3CD | e3cc Private Use-E3CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3cf Private Use-E3CF | e3ce Private Use-E3CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d1 Private Use-E3D1 | e3d0 Private Use-E3D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d3 Private Use-E3D3 | e3d2 Private Use-E3D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d5 Private Use-E3D5 | e3d4 Private Use-E3D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d7 Private Use-E3D7 | e3d6 Private Use-E3D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3d9 Private Use-E3D9 | e3d8 Private Use-E3D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3db Private Use-E3DB | e3da Private Use-E3DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3dd Private Use-E3DD | e3dc Private Use-E3DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3df Private Use-E3DF | e3de Private Use-E3DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e1 Private Use-E3E1 | e3e0 Private Use-E3E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e3 Private Use-E3E3 | e3e2 Private Use-E3E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e5 Private Use-E3E5 | e3e4 Private Use-E3E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e7 Private Use-E3E7 | e3e6 Private Use-E3E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3e9 Private Use-E3E9 | e3e8 Private Use-E3E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3eb Private Use-E3EB | e3ea Private Use-E3EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ed Private Use-E3ED | e3ec Private Use-E3EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ef Private Use-E3EF | e3ee Private Use-E3EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f1 Private Use-E3F1 | e3f0 Private Use-E3F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f3 Private Use-E3F3 | e3f2 Private Use-E3F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f5 Private Use-E3F5 | e3f4 Private Use-E3F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f7 Private Use-E3F7 | e3f6 Private Use-E3F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3f9 Private Use-E3F9 | e3f8 Private Use-E3F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3fb Private Use-E3FB | e3fa Private Use-E3FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3fd Private Use-E3FD | e3fc Private Use-E3FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e3ff Private Use-E3FF | e3fe Private Use-E3FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e401 Private Use-E401 | e400 Private Use-E400 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e403 Private Use-E403 | e402 Private Use-E402 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e405 Private Use-E405 | e404 Private Use-E404 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e407 Private Use-E407 | e406 Private Use-E406 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e409 Private Use-E409 | e408 Private Use-E408 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40b Private Use-E40B | e40a Private Use-E40A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40d Private Use-E40D | e40c Private Use-E40C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e40f Private Use-E40F | e40e Private Use-E40E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e411 Private Use-E411 | e410 Private Use-E410 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e413 Private Use-E413 | e412 Private Use-E412 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e415 Private Use-E415 | e414 Private Use-E414 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e417 Private Use-E417 | e416 Private Use-E416 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e419 Private Use-E419 | e418 Private Use-E418 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41b Private Use-E41B | e41a Private Use-E41A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41d Private Use-E41D | e41c Private Use-E41C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e41f Private Use-E41F | e41e Private Use-E41E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e421 Private Use-E421 | e420 Private Use-E420 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e423 Private Use-E423 | e422 Private Use-E422 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e425 Private Use-E425 | e424 Private Use-E424 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e427 Private Use-E427 | e426 Private Use-E426 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e429 Private Use-E429 | e428 Private Use-E428 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42b Private Use-E42B | e42a Private Use-E42A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42d Private Use-E42D | e42c Private Use-E42C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e42f Private Use-E42F | e42e Private Use-E42E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e431 Private Use-E431 | e430 Private Use-E430 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e433 Private Use-E433 | e432 Private Use-E432 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e435 Private Use-E435 | e434 Private Use-E434 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e437 Private Use-E437 | e436 Private Use-E436 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e439 Private Use-E439 | e438 Private Use-E438 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43b Private Use-E43B | e43a Private Use-E43A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43d Private Use-E43D | e43c Private Use-E43C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e43f Private Use-E43F | e43e Private Use-E43E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e441 Private Use-E441 | e440 Private Use-E440 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e443 Private Use-E443 | e442 Private Use-E442 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e445 Private Use-E445 | e444 Private Use-E444 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e447 Private Use-E447 | e446 Private Use-E446 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e449 Private Use-E449 | e448 Private Use-E448 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44b Private Use-E44B | e44a Private Use-E44A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44d Private Use-E44D | e44c Private Use-E44C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e44f Private Use-E44F | e44e Private Use-E44E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e451 Private Use-E451 | e450 Private Use-E450 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e453 Private Use-E453 | e452 Private Use-E452 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e455 Private Use-E455 | e454 Private Use-E454 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e457 Private Use-E457 | e456 Private Use-E456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e459 Private Use-E459 | e458 Private Use-E458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45b Private Use-E45B | e45a Private Use-E45A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45d Private Use-E45D | e45c Private Use-E45C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e45f Private Use-E45F | e45e Private Use-E45E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e461 Private Use-E461 | e460 Private Use-E460 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e463 Private Use-E463 | e462 Private Use-E462 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e465 Private Use-E465 | e464 Private Use-E464 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e467 Private Use-E467 | e466 Private Use-E466 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e469 Private Use-E469 | e468 Private Use-E468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46b Private Use-E46B | e46a Private Use-E46A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46d Private Use-E46D | e46c Private Use-E46C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e46f Private Use-E46F | e46e Private Use-E46E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e471 Private Use-E471 | e470 Private Use-E470 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e473 Private Use-E473 | e472 Private Use-E472 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e475 Private Use-E475 | e474 Private Use-E474 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e477 Private Use-E477 | e476 Private Use-E476 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e479 Private Use-E479 | e478 Private Use-E478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47b Private Use-E47B | e47a Private Use-E47A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47d Private Use-E47D | e47c Private Use-E47C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e47f Private Use-E47F | e47e Private Use-E47E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e481 Private Use-E481 | e480 Private Use-E480 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e483 Private Use-E483 | e482 Private Use-E482 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e485 Private Use-E485 | e484 Private Use-E484 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e487 Private Use-E487 | e486 Private Use-E486 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e489 Private Use-E489 | e488 Private Use-E488 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48b Private Use-E48B | e48a Private Use-E48A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48d Private Use-E48D | e48c Private Use-E48C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e48f Private Use-E48F | e48e Private Use-E48E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e491 Private Use-E491 | e490 Private Use-E490 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e493 Private Use-E493 | e492 Private Use-E492 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e495 Private Use-E495 | e494 Private Use-E494 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e497 Private Use-E497 | e496 Private Use-E496 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e499 Private Use-E499 | e498 Private Use-E498 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49b Private Use-E49B | e49a Private Use-E49A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49d Private Use-E49D | e49c Private Use-E49C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e49f Private Use-E49F | e49e Private Use-E49E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a1 Private Use-E4A1 | e4a0 Private Use-E4A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a3 Private Use-E4A3 | e4a2 Private Use-E4A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a5 Private Use-E4A5 | e4a4 Private Use-E4A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a7 Private Use-E4A7 | e4a6 Private Use-E4A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4a9 Private Use-E4A9 | e4a8 Private Use-E4A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ab Private Use-E4AB | e4aa Private Use-E4AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ad Private Use-E4AD | e4ac Private Use-E4AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4af Private Use-E4AF | e4ae Private Use-E4AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b1 Private Use-E4B1 | e4b0 Private Use-E4B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b3 Private Use-E4B3 | e4b2 Private Use-E4B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b5 Private Use-E4B5 | e4b4 Private Use-E4B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b7 Private Use-E4B7 | e4b6 Private Use-E4B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4b9 Private Use-E4B9 | e4b8 Private Use-E4B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bb Private Use-E4BB | e4ba Private Use-E4BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bd Private Use-E4BD | e4bc Private Use-E4BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4bf Private Use-E4BF | e4be Private Use-E4BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c1 Private Use-E4C1 | e4c0 Private Use-E4C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c3 Private Use-E4C3 | e4c2 Private Use-E4C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c5 Private Use-E4C5 | e4c4 Private Use-E4C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c7 Private Use-E4C7 | e4c6 Private Use-E4C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4c9 Private Use-E4C9 | e4c8 Private Use-E4C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cb Private Use-E4CB | e4ca Private Use-E4CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cd Private Use-E4CD | e4cc Private Use-E4CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4cf Private Use-E4CF | e4ce Private Use-E4CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d1 Private Use-E4D1 | e4d0 Private Use-E4D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d3 Private Use-E4D3 | e4d2 Private Use-E4D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d5 Private Use-E4D5 | e4d4 Private Use-E4D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d7 Private Use-E4D7 | e4d6 Private Use-E4D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4d9 Private Use-E4D9 | e4d8 Private Use-E4D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4db Private Use-E4DB | e4da Private Use-E4DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4dd Private Use-E4DD | e4dc Private Use-E4DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4df Private Use-E4DF | e4de Private Use-E4DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e1 Private Use-E4E1 | e4e0 Private Use-E4E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e3 Private Use-E4E3 | e4e2 Private Use-E4E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e5 Private Use-E4E5 | e4e4 Private Use-E4E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e7 Private Use-E4E7 | e4e6 Private Use-E4E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4e9 Private Use-E4E9 | e4e8 Private Use-E4E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4eb Private Use-E4EB | e4ea Private Use-E4EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ed Private Use-E4ED | e4ec Private Use-E4EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ef Private Use-E4EF | e4ee Private Use-E4EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f1 Private Use-E4F1 | e4f0 Private Use-E4F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f3 Private Use-E4F3 | e4f2 Private Use-E4F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f5 Private Use-E4F5 | e4f4 Private Use-E4F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f7 Private Use-E4F7 | e4f6 Private Use-E4F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4f9 Private Use-E4F9 | e4f8 Private Use-E4F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4fb Private Use-E4FB | e4fa Private Use-E4FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4fd Private Use-E4FD | e4fc Private Use-E4FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e4ff Private Use-E4FF | e4fe Private Use-E4FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e501 Private Use-E501 | e500 Private Use-E500 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e503 Private Use-E503 | e502 Private Use-E502 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e505 Private Use-E505 | e504 Private Use-E504 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e507 Private Use-E507 | e506 Private Use-E506 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e509 Private Use-E509 | e508 Private Use-E508 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50b Private Use-E50B | e50a Private Use-E50A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50d Private Use-E50D | e50c Private Use-E50C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e50f Private Use-E50F | e50e Private Use-E50E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e511 Private Use-E511 | e510 Private Use-E510 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e513 Private Use-E513 | e512 Private Use-E512 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e515 Private Use-E515 | e514 Private Use-E514 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e517 Private Use-E517 | e516 Private Use-E516 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e519 Private Use-E519 | e518 Private Use-E518 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51b Private Use-E51B | e51a Private Use-E51A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51d Private Use-E51D | e51c Private Use-E51C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e51f Private Use-E51F | e51e Private Use-E51E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e521 Private Use-E521 | e520 Private Use-E520 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e523 Private Use-E523 | e522 Private Use-E522 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e525 Private Use-E525 | e524 Private Use-E524 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e527 Private Use-E527 | e526 Private Use-E526 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e529 Private Use-E529 | e528 Private Use-E528 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52b Private Use-E52B | e52a Private Use-E52A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52d Private Use-E52D | e52c Private Use-E52C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e52f Private Use-E52F | e52e Private Use-E52E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e531 Private Use-E531 | e530 Private Use-E530 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e533 Private Use-E533 | e532 Private Use-E532 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e535 Private Use-E535 | e534 Private Use-E534 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e537 Private Use-E537 | e536 Private Use-E536 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e539 Private Use-E539 | e538 Private Use-E538 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53b Private Use-E53B | e53a Private Use-E53A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53d Private Use-E53D | e53c Private Use-E53C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e53f Private Use-E53F | e53e Private Use-E53E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e541 Private Use-E541 | e540 Private Use-E540 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e543 Private Use-E543 | e542 Private Use-E542 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e545 Private Use-E545 | e544 Private Use-E544 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e547 Private Use-E547 | e546 Private Use-E546 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e549 Private Use-E549 | e548 Private Use-E548 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54b Private Use-E54B | e54a Private Use-E54A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54d Private Use-E54D | e54c Private Use-E54C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e54f Private Use-E54F | e54e Private Use-E54E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e551 Private Use-E551 | e550 Private Use-E550 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e553 Private Use-E553 | e552 Private Use-E552 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e555 Private Use-E555 | e554 Private Use-E554 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e557 Private Use-E557 | e556 Private Use-E556 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e559 Private Use-E559 | e558 Private Use-E558 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55b Private Use-E55B | e55a Private Use-E55A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55d Private Use-E55D | e55c Private Use-E55C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e55f Private Use-E55F | e55e Private Use-E55E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e561 Private Use-E561 | e560 Private Use-E560 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e563 Private Use-E563 | e562 Private Use-E562 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e565 Private Use-E565 | e564 Private Use-E564 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e567 Private Use-E567 | e566 Private Use-E566 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e569 Private Use-E569 | e568 Private Use-E568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56b Private Use-E56B | e56a Private Use-E56A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56d Private Use-E56D | e56c Private Use-E56C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e56f Private Use-E56F | e56e Private Use-E56E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e571 Private Use-E571 | e570 Private Use-E570 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e573 Private Use-E573 | e572 Private Use-E572 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e575 Private Use-E575 | e574 Private Use-E574 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e577 Private Use-E577 | e576 Private Use-E576 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e579 Private Use-E579 | e578 Private Use-E578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57b Private Use-E57B | e57a Private Use-E57A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57d Private Use-E57D | e57c Private Use-E57C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e57f Private Use-E57F | e57e Private Use-E57E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e581 Private Use-E581 | e580 Private Use-E580 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e583 Private Use-E583 | e582 Private Use-E582 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e585 Private Use-E585 | e584 Private Use-E584 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e587 Private Use-E587 | e586 Private Use-E586 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e589 Private Use-E589 | e588 Private Use-E588 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58b Private Use-E58B | e58a Private Use-E58A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58d Private Use-E58D | e58c Private Use-E58C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e58f Private Use-E58F | e58e Private Use-E58E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e591 Private Use-E591 | e590 Private Use-E590 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e593 Private Use-E593 | e592 Private Use-E592 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e595 Private Use-E595 | e594 Private Use-E594 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e597 Private Use-E597 | e596 Private Use-E596 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e599 Private Use-E599 | e598 Private Use-E598 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59b Private Use-E59B | e59a Private Use-E59A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59d Private Use-E59D | e59c Private Use-E59C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e59f Private Use-E59F | e59e Private Use-E59E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a1 Private Use-E5A1 | e5a0 Private Use-E5A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a3 Private Use-E5A3 | e5a2 Private Use-E5A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a5 Private Use-E5A5 | e5a4 Private Use-E5A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a7 Private Use-E5A7 | e5a6 Private Use-E5A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5a9 Private Use-E5A9 | e5a8 Private Use-E5A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ab Private Use-E5AB | e5aa Private Use-E5AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ad Private Use-E5AD | e5ac Private Use-E5AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5af Private Use-E5AF | e5ae Private Use-E5AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b1 Private Use-E5B1 | e5b0 Private Use-E5B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b3 Private Use-E5B3 | e5b2 Private Use-E5B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b5 Private Use-E5B5 | e5b4 Private Use-E5B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b7 Private Use-E5B7 | e5b6 Private Use-E5B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5b9 Private Use-E5B9 | e5b8 Private Use-E5B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bb Private Use-E5BB | e5ba Private Use-E5BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bd Private Use-E5BD | e5bc Private Use-E5BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5bf Private Use-E5BF | e5be Private Use-E5BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c1 Private Use-E5C1 | e5c0 Private Use-E5C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c3 Private Use-E5C3 | e5c2 Private Use-E5C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c5 Private Use-E5C5 | e5c4 Private Use-E5C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c7 Private Use-E5C7 | e5c6 Private Use-E5C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5c9 Private Use-E5C9 | e5c8 Private Use-E5C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cb Private Use-E5CB | e5ca Private Use-E5CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cd Private Use-E5CD | e5cc Private Use-E5CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5cf Private Use-E5CF | e5ce Private Use-E5CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d1 Private Use-E5D1 | e5d0 Private Use-E5D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d3 Private Use-E5D3 | e5d2 Private Use-E5D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d5 Private Use-E5D5 | e5d4 Private Use-E5D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d7 Private Use-E5D7 | e5d6 Private Use-E5D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5d9 Private Use-E5D9 | e5d8 Private Use-E5D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5db Private Use-E5DB | e5da Private Use-E5DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5dd Private Use-E5DD | e5dc Private Use-E5DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5df Private Use-E5DF | e5de Private Use-E5DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e1 Private Use-E5E1 | e5e0 Private Use-E5E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e3 Private Use-E5E3 | e5e2 Private Use-E5E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e5 Private Use-E5E5 | e5e4 Private Use-E5E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e7 Private Use-E5E7 | e5e6 Private Use-E5E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5e9 Private Use-E5E9 | e5e8 Private Use-E5E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5eb Private Use-E5EB | e5ea Private Use-E5EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ed Private Use-E5ED | e5ec Private Use-E5EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ef Private Use-E5EF | e5ee Private Use-E5EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f1 Private Use-E5F1 | e5f0 Private Use-E5F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f3 Private Use-E5F3 | e5f2 Private Use-E5F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f5 Private Use-E5F5 | e5f4 Private Use-E5F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f7 Private Use-E5F7 | e5f6 Private Use-E5F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5f9 Private Use-E5F9 | e5f8 Private Use-E5F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5fb Private Use-E5FB | e5fa Private Use-E5FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5fd Private Use-E5FD | e5fc Private Use-E5FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e5ff Private Use-E5FF | e5fe Private Use-E5FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e601 Private Use-E601 | e600 Private Use-E600 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e603 Private Use-E603 | e602 Private Use-E602 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e605 Private Use-E605 | e604 Private Use-E604 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e607 Private Use-E607 | e606 Private Use-E606 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e609 Private Use-E609 | e608 Private Use-E608 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60b Private Use-E60B | e60a Private Use-E60A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60d Private Use-E60D | e60c Private Use-E60C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e60f Private Use-E60F | e60e Private Use-E60E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e611 Private Use-E611 | e610 Private Use-E610 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e613 Private Use-E613 | e612 Private Use-E612 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e615 Private Use-E615 | e614 Private Use-E614 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e617 Private Use-E617 | e616 Private Use-E616 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e619 Private Use-E619 | e618 Private Use-E618 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61b Private Use-E61B | e61a Private Use-E61A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61d Private Use-E61D | e61c Private Use-E61C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e61f Private Use-E61F | e61e Private Use-E61E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e621 Private Use-E621 | e620 Private Use-E620 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e623 Private Use-E623 | e622 Private Use-E622 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e625 Private Use-E625 | e624 Private Use-E624 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e627 Private Use-E627 | e626 Private Use-E626 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e629 Private Use-E629 | e628 Private Use-E628 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62b Private Use-E62B | e62a Private Use-E62A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62d Private Use-E62D | e62c Private Use-E62C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e62f Private Use-E62F | e62e Private Use-E62E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e631 Private Use-E631 | e630 Private Use-E630 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e633 Private Use-E633 | e632 Private Use-E632 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e635 Private Use-E635 | e634 Private Use-E634 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e637 Private Use-E637 | e636 Private Use-E636 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e639 Private Use-E639 | e638 Private Use-E638 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63b Private Use-E63B | e63a Private Use-E63A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63d Private Use-E63D | e63c Private Use-E63C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e63f Private Use-E63F | e63e Private Use-E63E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e641 Private Use-E641 | e640 Private Use-E640 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e643 Private Use-E643 | e642 Private Use-E642 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e645 Private Use-E645 | e644 Private Use-E644 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e647 Private Use-E647 | e646 Private Use-E646 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e649 Private Use-E649 | e648 Private Use-E648 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64b Private Use-E64B | e64a Private Use-E64A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64d Private Use-E64D | e64c Private Use-E64C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e64f Private Use-E64F | e64e Private Use-E64E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e651 Private Use-E651 | e650 Private Use-E650 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e653 Private Use-E653 | e652 Private Use-E652 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e655 Private Use-E655 | e654 Private Use-E654 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e657 Private Use-E657 | e656 Private Use-E656 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e659 Private Use-E659 | e658 Private Use-E658 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65b Private Use-E65B | e65a Private Use-E65A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65d Private Use-E65D | e65c Private Use-E65C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e65f Private Use-E65F | e65e Private Use-E65E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e661 Private Use-E661 | e660 Private Use-E660 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e663 Private Use-E663 | e662 Private Use-E662 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e665 Private Use-E665 | e664 Private Use-E664 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e667 Private Use-E667 | e666 Private Use-E666 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e669 Private Use-E669 | e668 Private Use-E668 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66b Private Use-E66B | e66a Private Use-E66A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66d Private Use-E66D | e66c Private Use-E66C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e66f Private Use-E66F | e66e Private Use-E66E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e671 Private Use-E671 | e670 Private Use-E670 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e673 Private Use-E673 | e672 Private Use-E672 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e675 Private Use-E675 | e674 Private Use-E674 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e677 Private Use-E677 | e676 Private Use-E676 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e679 Private Use-E679 | e678 Private Use-E678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67b Private Use-E67B | e67a Private Use-E67A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67d Private Use-E67D | e67c Private Use-E67C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e67f Private Use-E67F | e67e Private Use-E67E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e681 Private Use-E681 | e680 Private Use-E680 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e683 Private Use-E683 | e682 Private Use-E682 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e685 Private Use-E685 | e684 Private Use-E684 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e687 Private Use-E687 | e686 Private Use-E686 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e689 Private Use-E689 | e688 Private Use-E688 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68b Private Use-E68B | e68a Private Use-E68A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68d Private Use-E68D | e68c Private Use-E68C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e68f Private Use-E68F | e68e Private Use-E68E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e691 Private Use-E691 | e690 Private Use-E690 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e693 Private Use-E693 | e692 Private Use-E692 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e695 Private Use-E695 | e694 Private Use-E694 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e697 Private Use-E697 | e696 Private Use-E696 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e699 Private Use-E699 | e698 Private Use-E698 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69b Private Use-E69B | e69a Private Use-E69A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69d Private Use-E69D | e69c Private Use-E69C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e69f Private Use-E69F | e69e Private Use-E69E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a1 Private Use-E6A1 | e6a0 Private Use-E6A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a3 Private Use-E6A3 | e6a2 Private Use-E6A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a5 Private Use-E6A5 | e6a4 Private Use-E6A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a7 Private Use-E6A7 | e6a6 Private Use-E6A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6a9 Private Use-E6A9 | e6a8 Private Use-E6A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ab Private Use-E6AB | e6aa Private Use-E6AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ad Private Use-E6AD | e6ac Private Use-E6AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6af Private Use-E6AF | e6ae Private Use-E6AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b1 Private Use-E6B1 | e6b0 Private Use-E6B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b3 Private Use-E6B3 | e6b2 Private Use-E6B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b5 Private Use-E6B5 | e6b4 Private Use-E6B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b7 Private Use-E6B7 | e6b6 Private Use-E6B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6b9 Private Use-E6B9 | e6b8 Private Use-E6B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bb Private Use-E6BB | e6ba Private Use-E6BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bd Private Use-E6BD | e6bc Private Use-E6BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6bf Private Use-E6BF | e6be Private Use-E6BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c1 Private Use-E6C1 | e6c0 Private Use-E6C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c3 Private Use-E6C3 | e6c2 Private Use-E6C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c5 Private Use-E6C5 | e6c4 Private Use-E6C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c7 Private Use-E6C7 | e6c6 Private Use-E6C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6c9 Private Use-E6C9 | e6c8 Private Use-E6C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cb Private Use-E6CB | e6ca Private Use-E6CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cd Private Use-E6CD | e6cc Private Use-E6CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6cf Private Use-E6CF | e6ce Private Use-E6CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d1 Private Use-E6D1 | e6d0 Private Use-E6D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d3 Private Use-E6D3 | e6d2 Private Use-E6D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d5 Private Use-E6D5 | e6d4 Private Use-E6D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d7 Private Use-E6D7 | e6d6 Private Use-E6D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6d9 Private Use-E6D9 | e6d8 Private Use-E6D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6db Private Use-E6DB | e6da Private Use-E6DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6dd Private Use-E6DD | e6dc Private Use-E6DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6df Private Use-E6DF | e6de Private Use-E6DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e1 Private Use-E6E1 | e6e0 Private Use-E6E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e3 Private Use-E6E3 | e6e2 Private Use-E6E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e5 Private Use-E6E5 | e6e4 Private Use-E6E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e7 Private Use-E6E7 | e6e6 Private Use-E6E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6e9 Private Use-E6E9 | e6e8 Private Use-E6E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6eb Private Use-E6EB | e6ea Private Use-E6EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ed Private Use-E6ED | e6ec Private Use-E6EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ef Private Use-E6EF | e6ee Private Use-E6EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f1 Private Use-E6F1 | e6f0 Private Use-E6F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f3 Private Use-E6F3 | e6f2 Private Use-E6F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f5 Private Use-E6F5 | e6f4 Private Use-E6F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f7 Private Use-E6F7 | e6f6 Private Use-E6F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6f9 Private Use-E6F9 | e6f8 Private Use-E6F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6fb Private Use-E6FB | e6fa Private Use-E6FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6fd Private Use-E6FD | e6fc Private Use-E6FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e6ff Private Use-E6FF | e6fe Private Use-E6FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e701 Private Use-E701 | e700 Private Use-E700 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e703 Private Use-E703 | e702 Private Use-E702 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e705 Private Use-E705 | e704 Private Use-E704 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e707 Private Use-E707 | e706 Private Use-E706 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e709 Private Use-E709 | e708 Private Use-E708 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70b Private Use-E70B | e70a Private Use-E70A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70d Private Use-E70D | e70c Private Use-E70C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e70f Private Use-E70F | e70e Private Use-E70E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e711 Private Use-E711 | e710 Private Use-E710 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e713 Private Use-E713 | e712 Private Use-E712 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e715 Private Use-E715 | e714 Private Use-E714 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e717 Private Use-E717 | e716 Private Use-E716 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e719 Private Use-E719 | e718 Private Use-E718 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71b Private Use-E71B | e71a Private Use-E71A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71d Private Use-E71D | e71c Private Use-E71C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e71f Private Use-E71F | e71e Private Use-E71E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e721 Private Use-E721 | e720 Private Use-E720 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e723 Private Use-E723 | e722 Private Use-E722 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e725 Private Use-E725 | e724 Private Use-E724 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e727 Private Use-E727 | e726 Private Use-E726 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e729 Private Use-E729 | e728 Private Use-E728 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72b Private Use-E72B | e72a Private Use-E72A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72d Private Use-E72D | e72c Private Use-E72C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e72f Private Use-E72F | e72e Private Use-E72E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e731 Private Use-E731 | e730 Private Use-E730 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e733 Private Use-E733 | e732 Private Use-E732 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e735 Private Use-E735 | e734 Private Use-E734 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e737 Private Use-E737 | e736 Private Use-E736 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e739 Private Use-E739 | e738 Private Use-E738 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73b Private Use-E73B | e73a Private Use-E73A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73d Private Use-E73D | e73c Private Use-E73C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e73f Private Use-E73F | e73e Private Use-E73E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e741 Private Use-E741 | e740 Private Use-E740 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e743 Private Use-E743 | e742 Private Use-E742 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e745 Private Use-E745 | e744 Private Use-E744 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e747 Private Use-E747 | e746 Private Use-E746 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e749 Private Use-E749 | e748 Private Use-E748 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74b Private Use-E74B | e74a Private Use-E74A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74d Private Use-E74D | e74c Private Use-E74C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e74f Private Use-E74F | e74e Private Use-E74E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e751 Private Use-E751 | e750 Private Use-E750 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e753 Private Use-E753 | e752 Private Use-E752 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e755 Private Use-E755 | e754 Private Use-E754 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e757 Private Use-E757 | e756 Private Use-E756 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e759 Private Use-E759 | e758 Private Use-E758 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75b Private Use-E75B | e75a Private Use-E75A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75d Private Use-E75D | e75c Private Use-E75C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e75f Private Use-E75F | e75e Private Use-E75E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e761 Private Use-E761 | e760 Private Use-E760 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e763 Private Use-E763 | e762 Private Use-E762 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e765 Private Use-E765 | e764 Private Use-E764 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e767 Private Use-E767 | e766 Private Use-E766 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e769 Private Use-E769 | e768 Private Use-E768 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76b Private Use-E76B | e76a Private Use-E76A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76d Private Use-E76D | e76c Private Use-E76C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e76f Private Use-E76F | e76e Private Use-E76E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e771 Private Use-E771 | e770 Private Use-E770 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e773 Private Use-E773 | e772 Private Use-E772 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e775 Private Use-E775 | e774 Private Use-E774 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e777 Private Use-E777 | e776 Private Use-E776 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e779 Private Use-E779 | e778 Private Use-E778 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77b Private Use-E77B | e77a Private Use-E77A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77d Private Use-E77D | e77c Private Use-E77C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e77f Private Use-E77F | e77e Private Use-E77E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e781 Private Use-E781 | e780 Private Use-E780 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e783 Private Use-E783 | e782 Private Use-E782 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e785 Private Use-E785 | e784 Private Use-E784 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e787 Private Use-E787 | e786 Private Use-E786 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e789 Private Use-E789 | e788 Private Use-E788 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78b Private Use-E78B | e78a Private Use-E78A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78d Private Use-E78D | e78c Private Use-E78C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e78f Private Use-E78F | e78e Private Use-E78E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e791 Private Use-E791 | e790 Private Use-E790 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e793 Private Use-E793 | e792 Private Use-E792 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e795 Private Use-E795 | e794 Private Use-E794 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e797 Private Use-E797 | e796 Private Use-E796 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e799 Private Use-E799 | e798 Private Use-E798 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79b Private Use-E79B | e79a Private Use-E79A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79d Private Use-E79D | e79c Private Use-E79C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e79f Private Use-E79F | e79e Private Use-E79E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a1 Private Use-E7A1 | e7a0 Private Use-E7A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a3 Private Use-E7A3 | e7a2 Private Use-E7A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a5 Private Use-E7A5 | e7a4 Private Use-E7A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a7 Private Use-E7A7 | e7a6 Private Use-E7A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7a9 Private Use-E7A9 | e7a8 Private Use-E7A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ab Private Use-E7AB | e7aa Private Use-E7AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ad Private Use-E7AD | e7ac Private Use-E7AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7af Private Use-E7AF | e7ae Private Use-E7AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b1 Private Use-E7B1 | e7b0 Private Use-E7B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b3 Private Use-E7B3 | e7b2 Private Use-E7B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b5 Private Use-E7B5 | e7b4 Private Use-E7B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b7 Private Use-E7B7 | e7b6 Private Use-E7B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7b9 Private Use-E7B9 | e7b8 Private Use-E7B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bb Private Use-E7BB | e7ba Private Use-E7BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bd Private Use-E7BD | e7bc Private Use-E7BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7bf Private Use-E7BF | e7be Private Use-E7BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c1 Private Use-E7C1 | e7c0 Private Use-E7C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c3 Private Use-E7C3 | e7c2 Private Use-E7C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c5 Private Use-E7C5 | e7c4 Private Use-E7C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c7 Private Use-E7C7 | e7c6 Private Use-E7C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7c9 Private Use-E7C9 | e7c8 Private Use-E7C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cb Private Use-E7CB | e7ca Private Use-E7CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cd Private Use-E7CD | e7cc Private Use-E7CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7cf Private Use-E7CF | e7ce Private Use-E7CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d1 Private Use-E7D1 | e7d0 Private Use-E7D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d3 Private Use-E7D3 | e7d2 Private Use-E7D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d5 Private Use-E7D5 | e7d4 Private Use-E7D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d7 Private Use-E7D7 | e7d6 Private Use-E7D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7d9 Private Use-E7D9 | e7d8 Private Use-E7D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7db Private Use-E7DB | e7da Private Use-E7DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7dd Private Use-E7DD | e7dc Private Use-E7DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7df Private Use-E7DF | e7de Private Use-E7DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e1 Private Use-E7E1 | e7e0 Private Use-E7E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e3 Private Use-E7E3 | e7e2 Private Use-E7E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e5 Private Use-E7E5 | e7e4 Private Use-E7E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e7 Private Use-E7E7 | e7e6 Private Use-E7E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7e9 Private Use-E7E9 | e7e8 Private Use-E7E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7eb Private Use-E7EB | e7ea Private Use-E7EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ed Private Use-E7ED | e7ec Private Use-E7EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ef Private Use-E7EF | e7ee Private Use-E7EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f1 Private Use-E7F1 | e7f0 Private Use-E7F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f3 Private Use-E7F3 | e7f2 Private Use-E7F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f5 Private Use-E7F5 | e7f4 Private Use-E7F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f7 Private Use-E7F7 | e7f6 Private Use-E7F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7f9 Private Use-E7F9 | e7f8 Private Use-E7F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7fb Private Use-E7FB | e7fa Private Use-E7FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7fd Private Use-E7FD | e7fc Private Use-E7FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e7ff Private Use-E7FF | e7fe Private Use-E7FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e801 Private Use-E801 | e800 Private Use-E800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e803 Private Use-E803 | e802 Private Use-E802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e805 Private Use-E805 | e804 Private Use-E804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e807 Private Use-E807 | e806 Private Use-E806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e809 Private Use-E809 | e808 Private Use-E808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80b Private Use-E80B | e80a Private Use-E80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80d Private Use-E80D | e80c Private Use-E80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e80f Private Use-E80F | e80e Private Use-E80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e811 Private Use-E811 | e810 Private Use-E810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e813 Private Use-E813 | e812 Private Use-E812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e815 Private Use-E815 | e814 Private Use-E814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e817 Private Use-E817 | e816 Private Use-E816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e819 Private Use-E819 | e818 Private Use-E818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81b Private Use-E81B | e81a Private Use-E81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81d Private Use-E81D | e81c Private Use-E81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e81f Private Use-E81F | e81e Private Use-E81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e821 Private Use-E821 | e820 Private Use-E820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e823 Private Use-E823 | e822 Private Use-E822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e825 Private Use-E825 | e824 Private Use-E824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e827 Private Use-E827 | e826 Private Use-E826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e829 Private Use-E829 | e828 Private Use-E828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82b Private Use-E82B | e82a Private Use-E82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82d Private Use-E82D | e82c Private Use-E82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e82f Private Use-E82F | e82e Private Use-E82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e831 Private Use-E831 | e830 Private Use-E830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e833 Private Use-E833 | e832 Private Use-E832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e835 Private Use-E835 | e834 Private Use-E834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e837 Private Use-E837 | e836 Private Use-E836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e839 Private Use-E839 | e838 Private Use-E838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83b Private Use-E83B | e83a Private Use-E83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83d Private Use-E83D | e83c Private Use-E83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e83f Private Use-E83F | e83e Private Use-E83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e841 Private Use-E841 | e840 Private Use-E840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e843 Private Use-E843 | e842 Private Use-E842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e845 Private Use-E845 | e844 Private Use-E844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e847 Private Use-E847 | e846 Private Use-E846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e849 Private Use-E849 | e848 Private Use-E848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84b Private Use-E84B | e84a Private Use-E84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84d Private Use-E84D | e84c Private Use-E84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e84f Private Use-E84F | e84e Private Use-E84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e851 Private Use-E851 | e850 Private Use-E850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e853 Private Use-E853 | e852 Private Use-E852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e855 Private Use-E855 | e854 Private Use-E854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e857 Private Use-E857 | e856 Private Use-E856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e859 Private Use-E859 | e858 Private Use-E858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85b Private Use-E85B | e85a Private Use-E85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85d Private Use-E85D | e85c Private Use-E85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e85f Private Use-E85F | e85e Private Use-E85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e861 Private Use-E861 | e860 Private Use-E860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e863 Private Use-E863 | e862 Private Use-E862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e865 Private Use-E865 | e864 Private Use-E864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e867 Private Use-E867 | e866 Private Use-E866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e869 Private Use-E869 | e868 Private Use-E868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86b Private Use-E86B | e86a Private Use-E86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86d Private Use-E86D | e86c Private Use-E86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e86f Private Use-E86F | e86e Private Use-E86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e871 Private Use-E871 | e870 Private Use-E870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e873 Private Use-E873 | e872 Private Use-E872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e875 Private Use-E875 | e874 Private Use-E874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e877 Private Use-E877 | e876 Private Use-E876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e879 Private Use-E879 | e878 Private Use-E878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87b Private Use-E87B | e87a Private Use-E87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87d Private Use-E87D | e87c Private Use-E87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e87f Private Use-E87F | e87e Private Use-E87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e881 Private Use-E881 | e880 Private Use-E880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e883 Private Use-E883 | e882 Private Use-E882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e885 Private Use-E885 | e884 Private Use-E884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e887 Private Use-E887 | e886 Private Use-E886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e889 Private Use-E889 | e888 Private Use-E888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88b Private Use-E88B | e88a Private Use-E88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88d Private Use-E88D | e88c Private Use-E88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e88f Private Use-E88F | e88e Private Use-E88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e891 Private Use-E891 | e890 Private Use-E890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e893 Private Use-E893 | e892 Private Use-E892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e895 Private Use-E895 | e894 Private Use-E894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e897 Private Use-E897 | e896 Private Use-E896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e899 Private Use-E899 | e898 Private Use-E898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89b Private Use-E89B | e89a Private Use-E89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89d Private Use-E89D | e89c Private Use-E89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e89f Private Use-E89F | e89e Private Use-E89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a1 Private Use-E8A1 | e8a0 Private Use-E8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a3 Private Use-E8A3 | e8a2 Private Use-E8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a5 Private Use-E8A5 | e8a4 Private Use-E8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a7 Private Use-E8A7 | e8a6 Private Use-E8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8a9 Private Use-E8A9 | e8a8 Private Use-E8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ab Private Use-E8AB | e8aa Private Use-E8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ad Private Use-E8AD | e8ac Private Use-E8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8af Private Use-E8AF | e8ae Private Use-E8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b1 Private Use-E8B1 | e8b0 Private Use-E8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b3 Private Use-E8B3 | e8b2 Private Use-E8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b5 Private Use-E8B5 | e8b4 Private Use-E8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b7 Private Use-E8B7 | e8b6 Private Use-E8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8b9 Private Use-E8B9 | e8b8 Private Use-E8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bb Private Use-E8BB | e8ba Private Use-E8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bd Private Use-E8BD | e8bc Private Use-E8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8bf Private Use-E8BF | e8be Private Use-E8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c1 Private Use-E8C1 | e8c0 Private Use-E8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c3 Private Use-E8C3 | e8c2 Private Use-E8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c5 Private Use-E8C5 | e8c4 Private Use-E8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c7 Private Use-E8C7 | e8c6 Private Use-E8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8c9 Private Use-E8C9 | e8c8 Private Use-E8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cb Private Use-E8CB | e8ca Private Use-E8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cd Private Use-E8CD | e8cc Private Use-E8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8cf Private Use-E8CF | e8ce Private Use-E8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d1 Private Use-E8D1 | e8d0 Private Use-E8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d3 Private Use-E8D3 | e8d2 Private Use-E8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d5 Private Use-E8D5 | e8d4 Private Use-E8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d7 Private Use-E8D7 | e8d6 Private Use-E8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8d9 Private Use-E8D9 | e8d8 Private Use-E8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8db Private Use-E8DB | e8da Private Use-E8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8dd Private Use-E8DD | e8dc Private Use-E8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8df Private Use-E8DF | e8de Private Use-E8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e1 Private Use-E8E1 | e8e0 Private Use-E8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e3 Private Use-E8E3 | e8e2 Private Use-E8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e5 Private Use-E8E5 | e8e4 Private Use-E8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e7 Private Use-E8E7 | e8e6 Private Use-E8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8e9 Private Use-E8E9 | e8e8 Private Use-E8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8eb Private Use-E8EB | e8ea Private Use-E8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ed Private Use-E8ED | e8ec Private Use-E8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ef Private Use-E8EF | e8ee Private Use-E8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f1 Private Use-E8F1 | e8f0 Private Use-E8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f3 Private Use-E8F3 | e8f2 Private Use-E8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f5 Private Use-E8F5 | e8f4 Private Use-E8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f7 Private Use-E8F7 | e8f6 Private Use-E8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8f9 Private Use-E8F9 | e8f8 Private Use-E8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8fb Private Use-E8FB | e8fa Private Use-E8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8fd Private Use-E8FD | e8fc Private Use-E8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e8ff Private Use-E8FF | e8fe Private Use-E8FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e901 Private Use-E901 | e900 Private Use-E900 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e903 Private Use-E903 | e902 Private Use-E902 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e905 Private Use-E905 | e904 Private Use-E904 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e907 Private Use-E907 | e906 Private Use-E906 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e909 Private Use-E909 | e908 Private Use-E908 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90b Private Use-E90B | e90a Private Use-E90A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90d Private Use-E90D | e90c Private Use-E90C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e90f Private Use-E90F | e90e Private Use-E90E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e911 Private Use-E911 | e910 Private Use-E910 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e913 Private Use-E913 | e912 Private Use-E912 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e915 Private Use-E915 | e914 Private Use-E914 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e917 Private Use-E917 | e916 Private Use-E916 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e919 Private Use-E919 | e918 Private Use-E918 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91b Private Use-E91B | e91a Private Use-E91A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91d Private Use-E91D | e91c Private Use-E91C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e91f Private Use-E91F | e91e Private Use-E91E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e921 Private Use-E921 | e920 Private Use-E920 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e923 Private Use-E923 | e922 Private Use-E922 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e925 Private Use-E925 | e924 Private Use-E924 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e927 Private Use-E927 | e926 Private Use-E926 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e929 Private Use-E929 | e928 Private Use-E928 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92b Private Use-E92B | e92a Private Use-E92A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92d Private Use-E92D | e92c Private Use-E92C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e92f Private Use-E92F | e92e Private Use-E92E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e931 Private Use-E931 | e930 Private Use-E930 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e933 Private Use-E933 | e932 Private Use-E932 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e935 Private Use-E935 | e934 Private Use-E934 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e937 Private Use-E937 | e936 Private Use-E936 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e939 Private Use-E939 | e938 Private Use-E938 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93b Private Use-E93B | e93a Private Use-E93A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93d Private Use-E93D | e93c Private Use-E93C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e93f Private Use-E93F | e93e Private Use-E93E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e941 Private Use-E941 | e940 Private Use-E940 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e943 Private Use-E943 | e942 Private Use-E942 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e945 Private Use-E945 | e944 Private Use-E944 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e947 Private Use-E947 | e946 Private Use-E946 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e949 Private Use-E949 | e948 Private Use-E948 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94b Private Use-E94B | e94a Private Use-E94A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94d Private Use-E94D | e94c Private Use-E94C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e94f Private Use-E94F | e94e Private Use-E94E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e951 Private Use-E951 | e950 Private Use-E950 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e953 Private Use-E953 | e952 Private Use-E952 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e955 Private Use-E955 | e954 Private Use-E954 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e957 Private Use-E957 | e956 Private Use-E956 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e959 Private Use-E959 | e958 Private Use-E958 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95b Private Use-E95B | e95a Private Use-E95A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95d Private Use-E95D | e95c Private Use-E95C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e95f Private Use-E95F | e95e Private Use-E95E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e961 Private Use-E961 | e960 Private Use-E960 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e963 Private Use-E963 | e962 Private Use-E962 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e965 Private Use-E965 | e964 Private Use-E964 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e967 Private Use-E967 | e966 Private Use-E966 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e969 Private Use-E969 | e968 Private Use-E968 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96b Private Use-E96B | e96a Private Use-E96A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96d Private Use-E96D | e96c Private Use-E96C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e96f Private Use-E96F | e96e Private Use-E96E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e971 Private Use-E971 | e970 Private Use-E970 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e973 Private Use-E973 | e972 Private Use-E972 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e975 Private Use-E975 | e974 Private Use-E974 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e977 Private Use-E977 | e976 Private Use-E976 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e979 Private Use-E979 | e978 Private Use-E978 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97b Private Use-E97B | e97a Private Use-E97A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97d Private Use-E97D | e97c Private Use-E97C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e97f Private Use-E97F | e97e Private Use-E97E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e981 Private Use-E981 | e980 Private Use-E980 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e983 Private Use-E983 | e982 Private Use-E982 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e985 Private Use-E985 | e984 Private Use-E984 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e987 Private Use-E987 | e986 Private Use-E986 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e989 Private Use-E989 | e988 Private Use-E988 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98b Private Use-E98B | e98a Private Use-E98A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98d Private Use-E98D | e98c Private Use-E98C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e98f Private Use-E98F | e98e Private Use-E98E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e991 Private Use-E991 | e990 Private Use-E990 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e993 Private Use-E993 | e992 Private Use-E992 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e995 Private Use-E995 | e994 Private Use-E994 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e997 Private Use-E997 | e996 Private Use-E996 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e999 Private Use-E999 | e998 Private Use-E998 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99b Private Use-E99B | e99a Private Use-E99A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99d Private Use-E99D | e99c Private Use-E99C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e99f Private Use-E99F | e99e Private Use-E99E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a1 Private Use-E9A1 | e9a0 Private Use-E9A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a3 Private Use-E9A3 | e9a2 Private Use-E9A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a5 Private Use-E9A5 | e9a4 Private Use-E9A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a7 Private Use-E9A7 | e9a6 Private Use-E9A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9a9 Private Use-E9A9 | e9a8 Private Use-E9A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ab Private Use-E9AB | e9aa Private Use-E9AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ad Private Use-E9AD | e9ac Private Use-E9AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9af Private Use-E9AF | e9ae Private Use-E9AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b1 Private Use-E9B1 | e9b0 Private Use-E9B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b3 Private Use-E9B3 | e9b2 Private Use-E9B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b5 Private Use-E9B5 | e9b4 Private Use-E9B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b7 Private Use-E9B7 | e9b6 Private Use-E9B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9b9 Private Use-E9B9 | e9b8 Private Use-E9B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bb Private Use-E9BB | e9ba Private Use-E9BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bd Private Use-E9BD | e9bc Private Use-E9BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9bf Private Use-E9BF | e9be Private Use-E9BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c1 Private Use-E9C1 | e9c0 Private Use-E9C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c3 Private Use-E9C3 | e9c2 Private Use-E9C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c5 Private Use-E9C5 | e9c4 Private Use-E9C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c7 Private Use-E9C7 | e9c6 Private Use-E9C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9c9 Private Use-E9C9 | e9c8 Private Use-E9C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cb Private Use-E9CB | e9ca Private Use-E9CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cd Private Use-E9CD | e9cc Private Use-E9CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9cf Private Use-E9CF | e9ce Private Use-E9CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d1 Private Use-E9D1 | e9d0 Private Use-E9D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d3 Private Use-E9D3 | e9d2 Private Use-E9D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d5 Private Use-E9D5 | e9d4 Private Use-E9D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d7 Private Use-E9D7 | e9d6 Private Use-E9D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9d9 Private Use-E9D9 | e9d8 Private Use-E9D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9db Private Use-E9DB | e9da Private Use-E9DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9dd Private Use-E9DD | e9dc Private Use-E9DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9df Private Use-E9DF | e9de Private Use-E9DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e1 Private Use-E9E1 | e9e0 Private Use-E9E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e3 Private Use-E9E3 | e9e2 Private Use-E9E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e5 Private Use-E9E5 | e9e4 Private Use-E9E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e7 Private Use-E9E7 | e9e6 Private Use-E9E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9e9 Private Use-E9E9 | e9e8 Private Use-E9E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9eb Private Use-E9EB | e9ea Private Use-E9EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ed Private Use-E9ED | e9ec Private Use-E9EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ef Private Use-E9EF | e9ee Private Use-E9EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f1 Private Use-E9F1 | e9f0 Private Use-E9F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f3 Private Use-E9F3 | e9f2 Private Use-E9F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f5 Private Use-E9F5 | e9f4 Private Use-E9F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f7 Private Use-E9F7 | e9f6 Private Use-E9F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9f9 Private Use-E9F9 | e9f8 Private Use-E9F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9fb Private Use-E9FB | e9fa Private Use-E9FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9fd Private Use-E9FD | e9fc Private Use-E9FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* e9ff Private Use-E9FF | e9fe Private Use-E9FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea01 Private Use-EA01 | ea00 Private Use-EA00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea03 Private Use-EA03 | ea02 Private Use-EA02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea05 Private Use-EA05 | ea04 Private Use-EA04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea07 Private Use-EA07 | ea06 Private Use-EA06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea09 Private Use-EA09 | ea08 Private Use-EA08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0b Private Use-EA0B | ea0a Private Use-EA0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0d Private Use-EA0D | ea0c Private Use-EA0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea0f Private Use-EA0F | ea0e Private Use-EA0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea11 Private Use-EA11 | ea10 Private Use-EA10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea13 Private Use-EA13 | ea12 Private Use-EA12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea15 Private Use-EA15 | ea14 Private Use-EA14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea17 Private Use-EA17 | ea16 Private Use-EA16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea19 Private Use-EA19 | ea18 Private Use-EA18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1b Private Use-EA1B | ea1a Private Use-EA1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1d Private Use-EA1D | ea1c Private Use-EA1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea1f Private Use-EA1F | ea1e Private Use-EA1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea21 Private Use-EA21 | ea20 Private Use-EA20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea23 Private Use-EA23 | ea22 Private Use-EA22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea25 Private Use-EA25 | ea24 Private Use-EA24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea27 Private Use-EA27 | ea26 Private Use-EA26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea29 Private Use-EA29 | ea28 Private Use-EA28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2b Private Use-EA2B | ea2a Private Use-EA2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2d Private Use-EA2D | ea2c Private Use-EA2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea2f Private Use-EA2F | ea2e Private Use-EA2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea31 Private Use-EA31 | ea30 Private Use-EA30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea33 Private Use-EA33 | ea32 Private Use-EA32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea35 Private Use-EA35 | ea34 Private Use-EA34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea37 Private Use-EA37 | ea36 Private Use-EA36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea39 Private Use-EA39 | ea38 Private Use-EA38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3b Private Use-EA3B | ea3a Private Use-EA3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3d Private Use-EA3D | ea3c Private Use-EA3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea3f Private Use-EA3F | ea3e Private Use-EA3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea41 Private Use-EA41 | ea40 Private Use-EA40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea43 Private Use-EA43 | ea42 Private Use-EA42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea45 Private Use-EA45 | ea44 Private Use-EA44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea47 Private Use-EA47 | ea46 Private Use-EA46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea49 Private Use-EA49 | ea48 Private Use-EA48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4b Private Use-EA4B | ea4a Private Use-EA4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4d Private Use-EA4D | ea4c Private Use-EA4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea4f Private Use-EA4F | ea4e Private Use-EA4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea51 Private Use-EA51 | ea50 Private Use-EA50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea53 Private Use-EA53 | ea52 Private Use-EA52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea55 Private Use-EA55 | ea54 Private Use-EA54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea57 Private Use-EA57 | ea56 Private Use-EA56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea59 Private Use-EA59 | ea58 Private Use-EA58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5b Private Use-EA5B | ea5a Private Use-EA5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5d Private Use-EA5D | ea5c Private Use-EA5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea5f Private Use-EA5F | ea5e Private Use-EA5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea61 Private Use-EA61 | ea60 Private Use-EA60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea63 Private Use-EA63 | ea62 Private Use-EA62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea65 Private Use-EA65 | ea64 Private Use-EA64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea67 Private Use-EA67 | ea66 Private Use-EA66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea69 Private Use-EA69 | ea68 Private Use-EA68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6b Private Use-EA6B | ea6a Private Use-EA6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6d Private Use-EA6D | ea6c Private Use-EA6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea6f Private Use-EA6F | ea6e Private Use-EA6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea71 Private Use-EA71 | ea70 Private Use-EA70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea73 Private Use-EA73 | ea72 Private Use-EA72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea75 Private Use-EA75 | ea74 Private Use-EA74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea77 Private Use-EA77 | ea76 Private Use-EA76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea79 Private Use-EA79 | ea78 Private Use-EA78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7b Private Use-EA7B | ea7a Private Use-EA7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7d Private Use-EA7D | ea7c Private Use-EA7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea7f Private Use-EA7F | ea7e Private Use-EA7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea81 Private Use-EA81 | ea80 Private Use-EA80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea83 Private Use-EA83 | ea82 Private Use-EA82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea85 Private Use-EA85 | ea84 Private Use-EA84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea87 Private Use-EA87 | ea86 Private Use-EA86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea89 Private Use-EA89 | ea88 Private Use-EA88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8b Private Use-EA8B | ea8a Private Use-EA8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8d Private Use-EA8D | ea8c Private Use-EA8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea8f Private Use-EA8F | ea8e Private Use-EA8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea91 Private Use-EA91 | ea90 Private Use-EA90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea93 Private Use-EA93 | ea92 Private Use-EA92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea95 Private Use-EA95 | ea94 Private Use-EA94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea97 Private Use-EA97 | ea96 Private Use-EA96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea99 Private Use-EA99 | ea98 Private Use-EA98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9b Private Use-EA9B | ea9a Private Use-EA9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9d Private Use-EA9D | ea9c Private Use-EA9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ea9f Private Use-EA9F | ea9e Private Use-EA9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa1 Private Use-EAA1 | eaa0 Private Use-EAA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa3 Private Use-EAA3 | eaa2 Private Use-EAA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa5 Private Use-EAA5 | eaa4 Private Use-EAA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa7 Private Use-EAA7 | eaa6 Private Use-EAA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaa9 Private Use-EAA9 | eaa8 Private Use-EAA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaab Private Use-EAAB | eaaa Private Use-EAAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaad Private Use-EAAD | eaac Private Use-EAAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaaf Private Use-EAAF | eaae Private Use-EAAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab1 Private Use-EAB1 | eab0 Private Use-EAB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab3 Private Use-EAB3 | eab2 Private Use-EAB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab5 Private Use-EAB5 | eab4 Private Use-EAB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab7 Private Use-EAB7 | eab6 Private Use-EAB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eab9 Private Use-EAB9 | eab8 Private Use-EAB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabb Private Use-EABB | eaba Private Use-EABA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabd Private Use-EABD | eabc Private Use-EABC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eabf Private Use-EABF | eabe Private Use-EABE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac1 Private Use-EAC1 | eac0 Private Use-EAC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac3 Private Use-EAC3 | eac2 Private Use-EAC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac5 Private Use-EAC5 | eac4 Private Use-EAC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac7 Private Use-EAC7 | eac6 Private Use-EAC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eac9 Private Use-EAC9 | eac8 Private Use-EAC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacb Private Use-EACB | eaca Private Use-EACA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacd Private Use-EACD | eacc Private Use-EACC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eacf Private Use-EACF | eace Private Use-EACE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead1 Private Use-EAD1 | ead0 Private Use-EAD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead3 Private Use-EAD3 | ead2 Private Use-EAD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead5 Private Use-EAD5 | ead4 Private Use-EAD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead7 Private Use-EAD7 | ead6 Private Use-EAD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ead9 Private Use-EAD9 | ead8 Private Use-EAD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadb Private Use-EADB | eada Private Use-EADA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadd Private Use-EADD | eadc Private Use-EADC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eadf Private Use-EADF | eade Private Use-EADE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae1 Private Use-EAE1 | eae0 Private Use-EAE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae3 Private Use-EAE3 | eae2 Private Use-EAE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae5 Private Use-EAE5 | eae4 Private Use-EAE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae7 Private Use-EAE7 | eae6 Private Use-EAE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eae9 Private Use-EAE9 | eae8 Private Use-EAE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaeb Private Use-EAEB | eaea Private Use-EAEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaed Private Use-EAED | eaec Private Use-EAEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaef Private Use-EAEF | eaee Private Use-EAEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf1 Private Use-EAF1 | eaf0 Private Use-EAF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf3 Private Use-EAF3 | eaf2 Private Use-EAF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf5 Private Use-EAF5 | eaf4 Private Use-EAF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf7 Private Use-EAF7 | eaf6 Private Use-EAF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaf9 Private Use-EAF9 | eaf8 Private Use-EAF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eafb Private Use-EAFB | eafa Private Use-EAFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eafd Private Use-EAFD | eafc Private Use-EAFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eaff Private Use-EAFF | eafe Private Use-EAFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb01 Private Use-EB01 | eb00 Private Use-EB00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb03 Private Use-EB03 | eb02 Private Use-EB02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb05 Private Use-EB05 | eb04 Private Use-EB04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb07 Private Use-EB07 | eb06 Private Use-EB06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb09 Private Use-EB09 | eb08 Private Use-EB08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0b Private Use-EB0B | eb0a Private Use-EB0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0d Private Use-EB0D | eb0c Private Use-EB0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb0f Private Use-EB0F | eb0e Private Use-EB0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb11 Private Use-EB11 | eb10 Private Use-EB10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb13 Private Use-EB13 | eb12 Private Use-EB12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb15 Private Use-EB15 | eb14 Private Use-EB14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb17 Private Use-EB17 | eb16 Private Use-EB16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb19 Private Use-EB19 | eb18 Private Use-EB18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1b Private Use-EB1B | eb1a Private Use-EB1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1d Private Use-EB1D | eb1c Private Use-EB1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb1f Private Use-EB1F | eb1e Private Use-EB1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb21 Private Use-EB21 | eb20 Private Use-EB20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb23 Private Use-EB23 | eb22 Private Use-EB22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb25 Private Use-EB25 | eb24 Private Use-EB24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb27 Private Use-EB27 | eb26 Private Use-EB26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb29 Private Use-EB29 | eb28 Private Use-EB28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2b Private Use-EB2B | eb2a Private Use-EB2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2d Private Use-EB2D | eb2c Private Use-EB2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb2f Private Use-EB2F | eb2e Private Use-EB2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb31 Private Use-EB31 | eb30 Private Use-EB30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb33 Private Use-EB33 | eb32 Private Use-EB32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb35 Private Use-EB35 | eb34 Private Use-EB34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb37 Private Use-EB37 | eb36 Private Use-EB36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb39 Private Use-EB39 | eb38 Private Use-EB38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3b Private Use-EB3B | eb3a Private Use-EB3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3d Private Use-EB3D | eb3c Private Use-EB3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb3f Private Use-EB3F | eb3e Private Use-EB3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb41 Private Use-EB41 | eb40 Private Use-EB40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb43 Private Use-EB43 | eb42 Private Use-EB42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb45 Private Use-EB45 | eb44 Private Use-EB44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb47 Private Use-EB47 | eb46 Private Use-EB46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb49 Private Use-EB49 | eb48 Private Use-EB48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4b Private Use-EB4B | eb4a Private Use-EB4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4d Private Use-EB4D | eb4c Private Use-EB4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb4f Private Use-EB4F | eb4e Private Use-EB4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb51 Private Use-EB51 | eb50 Private Use-EB50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb53 Private Use-EB53 | eb52 Private Use-EB52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb55 Private Use-EB55 | eb54 Private Use-EB54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb57 Private Use-EB57 | eb56 Private Use-EB56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb59 Private Use-EB59 | eb58 Private Use-EB58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5b Private Use-EB5B | eb5a Private Use-EB5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5d Private Use-EB5D | eb5c Private Use-EB5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb5f Private Use-EB5F | eb5e Private Use-EB5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb61 Private Use-EB61 | eb60 Private Use-EB60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb63 Private Use-EB63 | eb62 Private Use-EB62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb65 Private Use-EB65 | eb64 Private Use-EB64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb67 Private Use-EB67 | eb66 Private Use-EB66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb69 Private Use-EB69 | eb68 Private Use-EB68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6b Private Use-EB6B | eb6a Private Use-EB6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6d Private Use-EB6D | eb6c Private Use-EB6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb6f Private Use-EB6F | eb6e Private Use-EB6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb71 Private Use-EB71 | eb70 Private Use-EB70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb73 Private Use-EB73 | eb72 Private Use-EB72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb75 Private Use-EB75 | eb74 Private Use-EB74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb77 Private Use-EB77 | eb76 Private Use-EB76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb79 Private Use-EB79 | eb78 Private Use-EB78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7b Private Use-EB7B | eb7a Private Use-EB7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7d Private Use-EB7D | eb7c Private Use-EB7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb7f Private Use-EB7F | eb7e Private Use-EB7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb81 Private Use-EB81 | eb80 Private Use-EB80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb83 Private Use-EB83 | eb82 Private Use-EB82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb85 Private Use-EB85 | eb84 Private Use-EB84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb87 Private Use-EB87 | eb86 Private Use-EB86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb89 Private Use-EB89 | eb88 Private Use-EB88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8b Private Use-EB8B | eb8a Private Use-EB8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8d Private Use-EB8D | eb8c Private Use-EB8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb8f Private Use-EB8F | eb8e Private Use-EB8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb91 Private Use-EB91 | eb90 Private Use-EB90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb93 Private Use-EB93 | eb92 Private Use-EB92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb95 Private Use-EB95 | eb94 Private Use-EB94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb97 Private Use-EB97 | eb96 Private Use-EB96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb99 Private Use-EB99 | eb98 Private Use-EB98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9b Private Use-EB9B | eb9a Private Use-EB9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9d Private Use-EB9D | eb9c Private Use-EB9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eb9f Private Use-EB9F | eb9e Private Use-EB9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba1 Private Use-EBA1 | eba0 Private Use-EBA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba3 Private Use-EBA3 | eba2 Private Use-EBA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba5 Private Use-EBA5 | eba4 Private Use-EBA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba7 Private Use-EBA7 | eba6 Private Use-EBA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eba9 Private Use-EBA9 | eba8 Private Use-EBA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebab Private Use-EBAB | ebaa Private Use-EBAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebad Private Use-EBAD | ebac Private Use-EBAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebaf Private Use-EBAF | ebae Private Use-EBAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb1 Private Use-EBB1 | ebb0 Private Use-EBB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb3 Private Use-EBB3 | ebb2 Private Use-EBB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb5 Private Use-EBB5 | ebb4 Private Use-EBB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb7 Private Use-EBB7 | ebb6 Private Use-EBB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebb9 Private Use-EBB9 | ebb8 Private Use-EBB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbb Private Use-EBBB | ebba Private Use-EBBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbd Private Use-EBBD | ebbc Private Use-EBBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebbf Private Use-EBBF | ebbe Private Use-EBBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc1 Private Use-EBC1 | ebc0 Private Use-EBC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc3 Private Use-EBC3 | ebc2 Private Use-EBC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc5 Private Use-EBC5 | ebc4 Private Use-EBC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc7 Private Use-EBC7 | ebc6 Private Use-EBC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebc9 Private Use-EBC9 | ebc8 Private Use-EBC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcb Private Use-EBCB | ebca Private Use-EBCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcd Private Use-EBCD | ebcc Private Use-EBCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebcf Private Use-EBCF | ebce Private Use-EBCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd1 Private Use-EBD1 | ebd0 Private Use-EBD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd3 Private Use-EBD3 | ebd2 Private Use-EBD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd5 Private Use-EBD5 | ebd4 Private Use-EBD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd7 Private Use-EBD7 | ebd6 Private Use-EBD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebd9 Private Use-EBD9 | ebd8 Private Use-EBD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdb Private Use-EBDB | ebda Private Use-EBDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdd Private Use-EBDD | ebdc Private Use-EBDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebdf Private Use-EBDF | ebde Private Use-EBDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe1 Private Use-EBE1 | ebe0 Private Use-EBE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe3 Private Use-EBE3 | ebe2 Private Use-EBE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe5 Private Use-EBE5 | ebe4 Private Use-EBE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe7 Private Use-EBE7 | ebe6 Private Use-EBE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebe9 Private Use-EBE9 | ebe8 Private Use-EBE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebeb Private Use-EBEB | ebea Private Use-EBEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebed Private Use-EBED | ebec Private Use-EBEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebef Private Use-EBEF | ebee Private Use-EBEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf1 Private Use-EBF1 | ebf0 Private Use-EBF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf3 Private Use-EBF3 | ebf2 Private Use-EBF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf5 Private Use-EBF5 | ebf4 Private Use-EBF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf7 Private Use-EBF7 | ebf6 Private Use-EBF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebf9 Private Use-EBF9 | ebf8 Private Use-EBF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebfb Private Use-EBFB | ebfa Private Use-EBFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebfd Private Use-EBFD | ebfc Private Use-EBFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ebff Private Use-EBFF | ebfe Private Use-EBFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec01 Private Use-EC01 | ec00 Private Use-EC00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec03 Private Use-EC03 | ec02 Private Use-EC02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec05 Private Use-EC05 | ec04 Private Use-EC04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec07 Private Use-EC07 | ec06 Private Use-EC06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec09 Private Use-EC09 | ec08 Private Use-EC08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0b Private Use-EC0B | ec0a Private Use-EC0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0d Private Use-EC0D | ec0c Private Use-EC0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec0f Private Use-EC0F | ec0e Private Use-EC0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec11 Private Use-EC11 | ec10 Private Use-EC10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec13 Private Use-EC13 | ec12 Private Use-EC12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec15 Private Use-EC15 | ec14 Private Use-EC14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec17 Private Use-EC17 | ec16 Private Use-EC16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec19 Private Use-EC19 | ec18 Private Use-EC18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1b Private Use-EC1B | ec1a Private Use-EC1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1d Private Use-EC1D | ec1c Private Use-EC1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec1f Private Use-EC1F | ec1e Private Use-EC1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec21 Private Use-EC21 | ec20 Private Use-EC20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec23 Private Use-EC23 | ec22 Private Use-EC22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec25 Private Use-EC25 | ec24 Private Use-EC24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec27 Private Use-EC27 | ec26 Private Use-EC26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec29 Private Use-EC29 | ec28 Private Use-EC28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2b Private Use-EC2B | ec2a Private Use-EC2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2d Private Use-EC2D | ec2c Private Use-EC2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec2f Private Use-EC2F | ec2e Private Use-EC2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec31 Private Use-EC31 | ec30 Private Use-EC30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec33 Private Use-EC33 | ec32 Private Use-EC32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec35 Private Use-EC35 | ec34 Private Use-EC34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec37 Private Use-EC37 | ec36 Private Use-EC36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec39 Private Use-EC39 | ec38 Private Use-EC38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3b Private Use-EC3B | ec3a Private Use-EC3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3d Private Use-EC3D | ec3c Private Use-EC3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec3f Private Use-EC3F | ec3e Private Use-EC3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec41 Private Use-EC41 | ec40 Private Use-EC40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec43 Private Use-EC43 | ec42 Private Use-EC42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec45 Private Use-EC45 | ec44 Private Use-EC44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec47 Private Use-EC47 | ec46 Private Use-EC46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec49 Private Use-EC49 | ec48 Private Use-EC48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4b Private Use-EC4B | ec4a Private Use-EC4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4d Private Use-EC4D | ec4c Private Use-EC4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec4f Private Use-EC4F | ec4e Private Use-EC4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec51 Private Use-EC51 | ec50 Private Use-EC50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec53 Private Use-EC53 | ec52 Private Use-EC52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec55 Private Use-EC55 | ec54 Private Use-EC54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec57 Private Use-EC57 | ec56 Private Use-EC56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec59 Private Use-EC59 | ec58 Private Use-EC58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5b Private Use-EC5B | ec5a Private Use-EC5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5d Private Use-EC5D | ec5c Private Use-EC5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec5f Private Use-EC5F | ec5e Private Use-EC5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec61 Private Use-EC61 | ec60 Private Use-EC60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec63 Private Use-EC63 | ec62 Private Use-EC62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec65 Private Use-EC65 | ec64 Private Use-EC64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec67 Private Use-EC67 | ec66 Private Use-EC66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec69 Private Use-EC69 | ec68 Private Use-EC68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6b Private Use-EC6B | ec6a Private Use-EC6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6d Private Use-EC6D | ec6c Private Use-EC6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec6f Private Use-EC6F | ec6e Private Use-EC6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec71 Private Use-EC71 | ec70 Private Use-EC70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec73 Private Use-EC73 | ec72 Private Use-EC72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec75 Private Use-EC75 | ec74 Private Use-EC74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec77 Private Use-EC77 | ec76 Private Use-EC76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec79 Private Use-EC79 | ec78 Private Use-EC78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7b Private Use-EC7B | ec7a Private Use-EC7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7d Private Use-EC7D | ec7c Private Use-EC7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec7f Private Use-EC7F | ec7e Private Use-EC7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec81 Private Use-EC81 | ec80 Private Use-EC80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec83 Private Use-EC83 | ec82 Private Use-EC82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec85 Private Use-EC85 | ec84 Private Use-EC84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec87 Private Use-EC87 | ec86 Private Use-EC86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec89 Private Use-EC89 | ec88 Private Use-EC88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8b Private Use-EC8B | ec8a Private Use-EC8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8d Private Use-EC8D | ec8c Private Use-EC8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec8f Private Use-EC8F | ec8e Private Use-EC8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec91 Private Use-EC91 | ec90 Private Use-EC90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec93 Private Use-EC93 | ec92 Private Use-EC92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec95 Private Use-EC95 | ec94 Private Use-EC94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec97 Private Use-EC97 | ec96 Private Use-EC96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec99 Private Use-EC99 | ec98 Private Use-EC98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9b Private Use-EC9B | ec9a Private Use-EC9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9d Private Use-EC9D | ec9c Private Use-EC9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ec9f Private Use-EC9F | ec9e Private Use-EC9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca1 Private Use-ECA1 | eca0 Private Use-ECA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca3 Private Use-ECA3 | eca2 Private Use-ECA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca5 Private Use-ECA5 | eca4 Private Use-ECA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca7 Private Use-ECA7 | eca6 Private Use-ECA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eca9 Private Use-ECA9 | eca8 Private Use-ECA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecab Private Use-ECAB | ecaa Private Use-ECAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecad Private Use-ECAD | ecac Private Use-ECAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecaf Private Use-ECAF | ecae Private Use-ECAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb1 Private Use-ECB1 | ecb0 Private Use-ECB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb3 Private Use-ECB3 | ecb2 Private Use-ECB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb5 Private Use-ECB5 | ecb4 Private Use-ECB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb7 Private Use-ECB7 | ecb6 Private Use-ECB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecb9 Private Use-ECB9 | ecb8 Private Use-ECB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbb Private Use-ECBB | ecba Private Use-ECBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbd Private Use-ECBD | ecbc Private Use-ECBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecbf Private Use-ECBF | ecbe Private Use-ECBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc1 Private Use-ECC1 | ecc0 Private Use-ECC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc3 Private Use-ECC3 | ecc2 Private Use-ECC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc5 Private Use-ECC5 | ecc4 Private Use-ECC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc7 Private Use-ECC7 | ecc6 Private Use-ECC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecc9 Private Use-ECC9 | ecc8 Private Use-ECC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccb Private Use-ECCB | ecca Private Use-ECCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccd Private Use-ECCD | eccc Private Use-ECCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eccf Private Use-ECCF | ecce Private Use-ECCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd1 Private Use-ECD1 | ecd0 Private Use-ECD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd3 Private Use-ECD3 | ecd2 Private Use-ECD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd5 Private Use-ECD5 | ecd4 Private Use-ECD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd7 Private Use-ECD7 | ecd6 Private Use-ECD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecd9 Private Use-ECD9 | ecd8 Private Use-ECD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdb Private Use-ECDB | ecda Private Use-ECDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdd Private Use-ECDD | ecdc Private Use-ECDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecdf Private Use-ECDF | ecde Private Use-ECDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece1 Private Use-ECE1 | ece0 Private Use-ECE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece3 Private Use-ECE3 | ece2 Private Use-ECE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece5 Private Use-ECE5 | ece4 Private Use-ECE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece7 Private Use-ECE7 | ece6 Private Use-ECE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ece9 Private Use-ECE9 | ece8 Private Use-ECE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eceb Private Use-ECEB | ecea Private Use-ECEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eced Private Use-ECED | ecec Private Use-ECEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecef Private Use-ECEF | ecee Private Use-ECEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf1 Private Use-ECF1 | ecf0 Private Use-ECF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf3 Private Use-ECF3 | ecf2 Private Use-ECF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf5 Private Use-ECF5 | ecf4 Private Use-ECF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf7 Private Use-ECF7 | ecf6 Private Use-ECF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecf9 Private Use-ECF9 | ecf8 Private Use-ECF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecfb Private Use-ECFB | ecfa Private Use-ECFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecfd Private Use-ECFD | ecfc Private Use-ECFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ecff Private Use-ECFF | ecfe Private Use-ECFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed01 Private Use-ED01 | ed00 Private Use-ED00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed03 Private Use-ED03 | ed02 Private Use-ED02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed05 Private Use-ED05 | ed04 Private Use-ED04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed07 Private Use-ED07 | ed06 Private Use-ED06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed09 Private Use-ED09 | ed08 Private Use-ED08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0b Private Use-ED0B | ed0a Private Use-ED0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0d Private Use-ED0D | ed0c Private Use-ED0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed0f Private Use-ED0F | ed0e Private Use-ED0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed11 Private Use-ED11 | ed10 Private Use-ED10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed13 Private Use-ED13 | ed12 Private Use-ED12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed15 Private Use-ED15 | ed14 Private Use-ED14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed17 Private Use-ED17 | ed16 Private Use-ED16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed19 Private Use-ED19 | ed18 Private Use-ED18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1b Private Use-ED1B | ed1a Private Use-ED1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1d Private Use-ED1D | ed1c Private Use-ED1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed1f Private Use-ED1F | ed1e Private Use-ED1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed21 Private Use-ED21 | ed20 Private Use-ED20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed23 Private Use-ED23 | ed22 Private Use-ED22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed25 Private Use-ED25 | ed24 Private Use-ED24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed27 Private Use-ED27 | ed26 Private Use-ED26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed29 Private Use-ED29 | ed28 Private Use-ED28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2b Private Use-ED2B | ed2a Private Use-ED2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2d Private Use-ED2D | ed2c Private Use-ED2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed2f Private Use-ED2F | ed2e Private Use-ED2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed31 Private Use-ED31 | ed30 Private Use-ED30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed33 Private Use-ED33 | ed32 Private Use-ED32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed35 Private Use-ED35 | ed34 Private Use-ED34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed37 Private Use-ED37 | ed36 Private Use-ED36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed39 Private Use-ED39 | ed38 Private Use-ED38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3b Private Use-ED3B | ed3a Private Use-ED3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3d Private Use-ED3D | ed3c Private Use-ED3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed3f Private Use-ED3F | ed3e Private Use-ED3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed41 Private Use-ED41 | ed40 Private Use-ED40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed43 Private Use-ED43 | ed42 Private Use-ED42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed45 Private Use-ED45 | ed44 Private Use-ED44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed47 Private Use-ED47 | ed46 Private Use-ED46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed49 Private Use-ED49 | ed48 Private Use-ED48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4b Private Use-ED4B | ed4a Private Use-ED4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4d Private Use-ED4D | ed4c Private Use-ED4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed4f Private Use-ED4F | ed4e Private Use-ED4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed51 Private Use-ED51 | ed50 Private Use-ED50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed53 Private Use-ED53 | ed52 Private Use-ED52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed55 Private Use-ED55 | ed54 Private Use-ED54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed57 Private Use-ED57 | ed56 Private Use-ED56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed59 Private Use-ED59 | ed58 Private Use-ED58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5b Private Use-ED5B | ed5a Private Use-ED5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5d Private Use-ED5D | ed5c Private Use-ED5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed5f Private Use-ED5F | ed5e Private Use-ED5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed61 Private Use-ED61 | ed60 Private Use-ED60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed63 Private Use-ED63 | ed62 Private Use-ED62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed65 Private Use-ED65 | ed64 Private Use-ED64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed67 Private Use-ED67 | ed66 Private Use-ED66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed69 Private Use-ED69 | ed68 Private Use-ED68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6b Private Use-ED6B | ed6a Private Use-ED6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6d Private Use-ED6D | ed6c Private Use-ED6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed6f Private Use-ED6F | ed6e Private Use-ED6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed71 Private Use-ED71 | ed70 Private Use-ED70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed73 Private Use-ED73 | ed72 Private Use-ED72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed75 Private Use-ED75 | ed74 Private Use-ED74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed77 Private Use-ED77 | ed76 Private Use-ED76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed79 Private Use-ED79 | ed78 Private Use-ED78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7b Private Use-ED7B | ed7a Private Use-ED7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7d Private Use-ED7D | ed7c Private Use-ED7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed7f Private Use-ED7F | ed7e Private Use-ED7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed81 Private Use-ED81 | ed80 Private Use-ED80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed83 Private Use-ED83 | ed82 Private Use-ED82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed85 Private Use-ED85 | ed84 Private Use-ED84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed87 Private Use-ED87 | ed86 Private Use-ED86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed89 Private Use-ED89 | ed88 Private Use-ED88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8b Private Use-ED8B | ed8a Private Use-ED8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8d Private Use-ED8D | ed8c Private Use-ED8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed8f Private Use-ED8F | ed8e Private Use-ED8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed91 Private Use-ED91 | ed90 Private Use-ED90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed93 Private Use-ED93 | ed92 Private Use-ED92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed95 Private Use-ED95 | ed94 Private Use-ED94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed97 Private Use-ED97 | ed96 Private Use-ED96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed99 Private Use-ED99 | ed98 Private Use-ED98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9b Private Use-ED9B | ed9a Private Use-ED9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9d Private Use-ED9D | ed9c Private Use-ED9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ed9f Private Use-ED9F | ed9e Private Use-ED9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda1 Private Use-EDA1 | eda0 Private Use-EDA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda3 Private Use-EDA3 | eda2 Private Use-EDA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda5 Private Use-EDA5 | eda4 Private Use-EDA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda7 Private Use-EDA7 | eda6 Private Use-EDA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eda9 Private Use-EDA9 | eda8 Private Use-EDA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edab Private Use-EDAB | edaa Private Use-EDAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edad Private Use-EDAD | edac Private Use-EDAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edaf Private Use-EDAF | edae Private Use-EDAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb1 Private Use-EDB1 | edb0 Private Use-EDB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb3 Private Use-EDB3 | edb2 Private Use-EDB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb5 Private Use-EDB5 | edb4 Private Use-EDB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb7 Private Use-EDB7 | edb6 Private Use-EDB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edb9 Private Use-EDB9 | edb8 Private Use-EDB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbb Private Use-EDBB | edba Private Use-EDBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbd Private Use-EDBD | edbc Private Use-EDBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edbf Private Use-EDBF | edbe Private Use-EDBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc1 Private Use-EDC1 | edc0 Private Use-EDC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc3 Private Use-EDC3 | edc2 Private Use-EDC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc5 Private Use-EDC5 | edc4 Private Use-EDC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc7 Private Use-EDC7 | edc6 Private Use-EDC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edc9 Private Use-EDC9 | edc8 Private Use-EDC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcb Private Use-EDCB | edca Private Use-EDCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcd Private Use-EDCD | edcc Private Use-EDCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edcf Private Use-EDCF | edce Private Use-EDCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd1 Private Use-EDD1 | edd0 Private Use-EDD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd3 Private Use-EDD3 | edd2 Private Use-EDD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd5 Private Use-EDD5 | edd4 Private Use-EDD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd7 Private Use-EDD7 | edd6 Private Use-EDD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edd9 Private Use-EDD9 | edd8 Private Use-EDD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddb Private Use-EDDB | edda Private Use-EDDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddd Private Use-EDDD | eddc Private Use-EDDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eddf Private Use-EDDF | edde Private Use-EDDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede1 Private Use-EDE1 | ede0 Private Use-EDE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede3 Private Use-EDE3 | ede2 Private Use-EDE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede5 Private Use-EDE5 | ede4 Private Use-EDE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede7 Private Use-EDE7 | ede6 Private Use-EDE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ede9 Private Use-EDE9 | ede8 Private Use-EDE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edeb Private Use-EDEB | edea Private Use-EDEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eded Private Use-EDED | edec Private Use-EDEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edef Private Use-EDEF | edee Private Use-EDEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf1 Private Use-EDF1 | edf0 Private Use-EDF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf3 Private Use-EDF3 | edf2 Private Use-EDF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf5 Private Use-EDF5 | edf4 Private Use-EDF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf7 Private Use-EDF7 | edf6 Private Use-EDF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edf9 Private Use-EDF9 | edf8 Private Use-EDF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edfb Private Use-EDFB | edfa Private Use-EDFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edfd Private Use-EDFD | edfc Private Use-EDFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* edff Private Use-EDFF | edfe Private Use-EDFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee01 Private Use-EE01 | ee00 Private Use-EE00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee03 Private Use-EE03 | ee02 Private Use-EE02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee05 Private Use-EE05 | ee04 Private Use-EE04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee07 Private Use-EE07 | ee06 Private Use-EE06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee09 Private Use-EE09 | ee08 Private Use-EE08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0b Private Use-EE0B | ee0a Private Use-EE0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0d Private Use-EE0D | ee0c Private Use-EE0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee0f Private Use-EE0F | ee0e Private Use-EE0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee11 Private Use-EE11 | ee10 Private Use-EE10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee13 Private Use-EE13 | ee12 Private Use-EE12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee15 Private Use-EE15 | ee14 Private Use-EE14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee17 Private Use-EE17 | ee16 Private Use-EE16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee19 Private Use-EE19 | ee18 Private Use-EE18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1b Private Use-EE1B | ee1a Private Use-EE1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1d Private Use-EE1D | ee1c Private Use-EE1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee1f Private Use-EE1F | ee1e Private Use-EE1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee21 Private Use-EE21 | ee20 Private Use-EE20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee23 Private Use-EE23 | ee22 Private Use-EE22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee25 Private Use-EE25 | ee24 Private Use-EE24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee27 Private Use-EE27 | ee26 Private Use-EE26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee29 Private Use-EE29 | ee28 Private Use-EE28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2b Private Use-EE2B | ee2a Private Use-EE2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2d Private Use-EE2D | ee2c Private Use-EE2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee2f Private Use-EE2F | ee2e Private Use-EE2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee31 Private Use-EE31 | ee30 Private Use-EE30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee33 Private Use-EE33 | ee32 Private Use-EE32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee35 Private Use-EE35 | ee34 Private Use-EE34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee37 Private Use-EE37 | ee36 Private Use-EE36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee39 Private Use-EE39 | ee38 Private Use-EE38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3b Private Use-EE3B | ee3a Private Use-EE3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3d Private Use-EE3D | ee3c Private Use-EE3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee3f Private Use-EE3F | ee3e Private Use-EE3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee41 Private Use-EE41 | ee40 Private Use-EE40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee43 Private Use-EE43 | ee42 Private Use-EE42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee45 Private Use-EE45 | ee44 Private Use-EE44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee47 Private Use-EE47 | ee46 Private Use-EE46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee49 Private Use-EE49 | ee48 Private Use-EE48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4b Private Use-EE4B | ee4a Private Use-EE4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4d Private Use-EE4D | ee4c Private Use-EE4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee4f Private Use-EE4F | ee4e Private Use-EE4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee51 Private Use-EE51 | ee50 Private Use-EE50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee53 Private Use-EE53 | ee52 Private Use-EE52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee55 Private Use-EE55 | ee54 Private Use-EE54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee57 Private Use-EE57 | ee56 Private Use-EE56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee59 Private Use-EE59 | ee58 Private Use-EE58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5b Private Use-EE5B | ee5a Private Use-EE5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5d Private Use-EE5D | ee5c Private Use-EE5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee5f Private Use-EE5F | ee5e Private Use-EE5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee61 Private Use-EE61 | ee60 Private Use-EE60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee63 Private Use-EE63 | ee62 Private Use-EE62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee65 Private Use-EE65 | ee64 Private Use-EE64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee67 Private Use-EE67 | ee66 Private Use-EE66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee69 Private Use-EE69 | ee68 Private Use-EE68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6b Private Use-EE6B | ee6a Private Use-EE6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6d Private Use-EE6D | ee6c Private Use-EE6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee6f Private Use-EE6F | ee6e Private Use-EE6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee71 Private Use-EE71 | ee70 Private Use-EE70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee73 Private Use-EE73 | ee72 Private Use-EE72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee75 Private Use-EE75 | ee74 Private Use-EE74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee77 Private Use-EE77 | ee76 Private Use-EE76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee79 Private Use-EE79 | ee78 Private Use-EE78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7b Private Use-EE7B | ee7a Private Use-EE7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7d Private Use-EE7D | ee7c Private Use-EE7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee7f Private Use-EE7F | ee7e Private Use-EE7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee81 Private Use-EE81 | ee80 Private Use-EE80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee83 Private Use-EE83 | ee82 Private Use-EE82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee85 Private Use-EE85 | ee84 Private Use-EE84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee87 Private Use-EE87 | ee86 Private Use-EE86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee89 Private Use-EE89 | ee88 Private Use-EE88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8b Private Use-EE8B | ee8a Private Use-EE8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8d Private Use-EE8D | ee8c Private Use-EE8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee8f Private Use-EE8F | ee8e Private Use-EE8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee91 Private Use-EE91 | ee90 Private Use-EE90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee93 Private Use-EE93 | ee92 Private Use-EE92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee95 Private Use-EE95 | ee94 Private Use-EE94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee97 Private Use-EE97 | ee96 Private Use-EE96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee99 Private Use-EE99 | ee98 Private Use-EE98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9b Private Use-EE9B | ee9a Private Use-EE9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9d Private Use-EE9D | ee9c Private Use-EE9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ee9f Private Use-EE9F | ee9e Private Use-EE9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea1 Private Use-EEA1 | eea0 Private Use-EEA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea3 Private Use-EEA3 | eea2 Private Use-EEA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea5 Private Use-EEA5 | eea4 Private Use-EEA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea7 Private Use-EEA7 | eea6 Private Use-EEA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eea9 Private Use-EEA9 | eea8 Private Use-EEA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeab Private Use-EEAB | eeaa Private Use-EEAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eead Private Use-EEAD | eeac Private Use-EEAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeaf Private Use-EEAF | eeae Private Use-EEAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb1 Private Use-EEB1 | eeb0 Private Use-EEB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb3 Private Use-EEB3 | eeb2 Private Use-EEB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb5 Private Use-EEB5 | eeb4 Private Use-EEB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb7 Private Use-EEB7 | eeb6 Private Use-EEB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeb9 Private Use-EEB9 | eeb8 Private Use-EEB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebb Private Use-EEBB | eeba Private Use-EEBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebd Private Use-EEBD | eebc Private Use-EEBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eebf Private Use-EEBF | eebe Private Use-EEBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec1 Private Use-EEC1 | eec0 Private Use-EEC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec3 Private Use-EEC3 | eec2 Private Use-EEC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec5 Private Use-EEC5 | eec4 Private Use-EEC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec7 Private Use-EEC7 | eec6 Private Use-EEC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eec9 Private Use-EEC9 | eec8 Private Use-EEC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecb Private Use-EECB | eeca Private Use-EECA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecd Private Use-EECD | eecc Private Use-EECC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eecf Private Use-EECF | eece Private Use-EECE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed1 Private Use-EED1 | eed0 Private Use-EED0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed3 Private Use-EED3 | eed2 Private Use-EED2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed5 Private Use-EED5 | eed4 Private Use-EED4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed7 Private Use-EED7 | eed6 Private Use-EED6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eed9 Private Use-EED9 | eed8 Private Use-EED8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedb Private Use-EEDB | eeda Private Use-EEDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedd Private Use-EEDD | eedc Private Use-EEDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eedf Private Use-EEDF | eede Private Use-EEDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee1 Private Use-EEE1 | eee0 Private Use-EEE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee3 Private Use-EEE3 | eee2 Private Use-EEE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee5 Private Use-EEE5 | eee4 Private Use-EEE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee7 Private Use-EEE7 | eee6 Private Use-EEE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eee9 Private Use-EEE9 | eee8 Private Use-EEE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeeb Private Use-EEEB | eeea Private Use-EEEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeed Private Use-EEED | eeec Private Use-EEEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeef Private Use-EEEF | eeee Private Use-EEEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef1 Private Use-EEF1 | eef0 Private Use-EEF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef3 Private Use-EEF3 | eef2 Private Use-EEF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef5 Private Use-EEF5 | eef4 Private Use-EEF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef7 Private Use-EEF7 | eef6 Private Use-EEF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eef9 Private Use-EEF9 | eef8 Private Use-EEF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eefb Private Use-EEFB | eefa Private Use-EEFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eefd Private Use-EEFD | eefc Private Use-EEFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eeff Private Use-EEFF | eefe Private Use-EEFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef01 Private Use-EF01 | ef00 Private Use-EF00 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef03 Private Use-EF03 | ef02 Private Use-EF02 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef05 Private Use-EF05 | ef04 Private Use-EF04 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef07 Private Use-EF07 | ef06 Private Use-EF06 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef09 Private Use-EF09 | ef08 Private Use-EF08 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0b Private Use-EF0B | ef0a Private Use-EF0A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0d Private Use-EF0D | ef0c Private Use-EF0C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef0f Private Use-EF0F | ef0e Private Use-EF0E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef11 Private Use-EF11 | ef10 Private Use-EF10 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef13 Private Use-EF13 | ef12 Private Use-EF12 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef15 Private Use-EF15 | ef14 Private Use-EF14 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef17 Private Use-EF17 | ef16 Private Use-EF16 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef19 Private Use-EF19 | ef18 Private Use-EF18 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1b Private Use-EF1B | ef1a Private Use-EF1A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1d Private Use-EF1D | ef1c Private Use-EF1C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef1f Private Use-EF1F | ef1e Private Use-EF1E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef21 Private Use-EF21 | ef20 Private Use-EF20 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef23 Private Use-EF23 | ef22 Private Use-EF22 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef25 Private Use-EF25 | ef24 Private Use-EF24 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef27 Private Use-EF27 | ef26 Private Use-EF26 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef29 Private Use-EF29 | ef28 Private Use-EF28 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2b Private Use-EF2B | ef2a Private Use-EF2A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2d Private Use-EF2D | ef2c Private Use-EF2C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef2f Private Use-EF2F | ef2e Private Use-EF2E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef31 Private Use-EF31 | ef30 Private Use-EF30 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef33 Private Use-EF33 | ef32 Private Use-EF32 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef35 Private Use-EF35 | ef34 Private Use-EF34 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef37 Private Use-EF37 | ef36 Private Use-EF36 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef39 Private Use-EF39 | ef38 Private Use-EF38 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3b Private Use-EF3B | ef3a Private Use-EF3A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3d Private Use-EF3D | ef3c Private Use-EF3C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef3f Private Use-EF3F | ef3e Private Use-EF3E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef41 Private Use-EF41 | ef40 Private Use-EF40 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef43 Private Use-EF43 | ef42 Private Use-EF42 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef45 Private Use-EF45 | ef44 Private Use-EF44 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef47 Private Use-EF47 | ef46 Private Use-EF46 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef49 Private Use-EF49 | ef48 Private Use-EF48 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4b Private Use-EF4B | ef4a Private Use-EF4A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4d Private Use-EF4D | ef4c Private Use-EF4C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef4f Private Use-EF4F | ef4e Private Use-EF4E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef51 Private Use-EF51 | ef50 Private Use-EF50 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef53 Private Use-EF53 | ef52 Private Use-EF52 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef55 Private Use-EF55 | ef54 Private Use-EF54 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef57 Private Use-EF57 | ef56 Private Use-EF56 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef59 Private Use-EF59 | ef58 Private Use-EF58 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5b Private Use-EF5B | ef5a Private Use-EF5A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5d Private Use-EF5D | ef5c Private Use-EF5C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef5f Private Use-EF5F | ef5e Private Use-EF5E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef61 Private Use-EF61 | ef60 Private Use-EF60 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef63 Private Use-EF63 | ef62 Private Use-EF62 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef65 Private Use-EF65 | ef64 Private Use-EF64 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef67 Private Use-EF67 | ef66 Private Use-EF66 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef69 Private Use-EF69 | ef68 Private Use-EF68 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6b Private Use-EF6B | ef6a Private Use-EF6A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6d Private Use-EF6D | ef6c Private Use-EF6C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef6f Private Use-EF6F | ef6e Private Use-EF6E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef71 Private Use-EF71 | ef70 Private Use-EF70 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef73 Private Use-EF73 | ef72 Private Use-EF72 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef75 Private Use-EF75 | ef74 Private Use-EF74 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef77 Private Use-EF77 | ef76 Private Use-EF76 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef79 Private Use-EF79 | ef78 Private Use-EF78 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7b Private Use-EF7B | ef7a Private Use-EF7A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7d Private Use-EF7D | ef7c Private Use-EF7C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef7f Private Use-EF7F | ef7e Private Use-EF7E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef81 Private Use-EF81 | ef80 Private Use-EF80 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef83 Private Use-EF83 | ef82 Private Use-EF82 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef85 Private Use-EF85 | ef84 Private Use-EF84 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef87 Private Use-EF87 | ef86 Private Use-EF86 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef89 Private Use-EF89 | ef88 Private Use-EF88 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8b Private Use-EF8B | ef8a Private Use-EF8A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8d Private Use-EF8D | ef8c Private Use-EF8C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef8f Private Use-EF8F | ef8e Private Use-EF8E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef91 Private Use-EF91 | ef90 Private Use-EF90 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef93 Private Use-EF93 | ef92 Private Use-EF92 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef95 Private Use-EF95 | ef94 Private Use-EF94 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef97 Private Use-EF97 | ef96 Private Use-EF96 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef99 Private Use-EF99 | ef98 Private Use-EF98 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9b Private Use-EF9B | ef9a Private Use-EF9A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9d Private Use-EF9D | ef9c Private Use-EF9C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ef9f Private Use-EF9F | ef9e Private Use-EF9E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa1 Private Use-EFA1 | efa0 Private Use-EFA0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa3 Private Use-EFA3 | efa2 Private Use-EFA2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa5 Private Use-EFA5 | efa4 Private Use-EFA4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa7 Private Use-EFA7 | efa6 Private Use-EFA6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efa9 Private Use-EFA9 | efa8 Private Use-EFA8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efab Private Use-EFAB | efaa Private Use-EFAA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efad Private Use-EFAD | efac Private Use-EFAC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efaf Private Use-EFAF | efae Private Use-EFAE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb1 Private Use-EFB1 | efb0 Private Use-EFB0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb3 Private Use-EFB3 | efb2 Private Use-EFB2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb5 Private Use-EFB5 | efb4 Private Use-EFB4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb7 Private Use-EFB7 | efb6 Private Use-EFB6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efb9 Private Use-EFB9 | efb8 Private Use-EFB8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbb Private Use-EFBB | efba Private Use-EFBA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbd Private Use-EFBD | efbc Private Use-EFBC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efbf Private Use-EFBF | efbe Private Use-EFBE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc1 Private Use-EFC1 | efc0 Private Use-EFC0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc3 Private Use-EFC3 | efc2 Private Use-EFC2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc5 Private Use-EFC5 | efc4 Private Use-EFC4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc7 Private Use-EFC7 | efc6 Private Use-EFC6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efc9 Private Use-EFC9 | efc8 Private Use-EFC8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcb Private Use-EFCB | efca Private Use-EFCA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcd Private Use-EFCD | efcc Private Use-EFCC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efcf Private Use-EFCF | efce Private Use-EFCE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd1 Private Use-EFD1 | efd0 Private Use-EFD0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd3 Private Use-EFD3 | efd2 Private Use-EFD2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd5 Private Use-EFD5 | efd4 Private Use-EFD4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd7 Private Use-EFD7 | efd6 Private Use-EFD6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efd9 Private Use-EFD9 | efd8 Private Use-EFD8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdb Private Use-EFDB | efda Private Use-EFDA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdd Private Use-EFDD | efdc Private Use-EFDC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efdf Private Use-EFDF | efde Private Use-EFDE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe1 Private Use-EFE1 | efe0 Private Use-EFE0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe3 Private Use-EFE3 | efe2 Private Use-EFE2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe5 Private Use-EFE5 | efe4 Private Use-EFE4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe7 Private Use-EFE7 | efe6 Private Use-EFE6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efe9 Private Use-EFE9 | efe8 Private Use-EFE8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efeb Private Use-EFEB | efea Private Use-EFEA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efed Private Use-EFED | efec Private Use-EFEC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efef Private Use-EFEF | efee Private Use-EFEE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff1 Private Use-EFF1 | eff0 Private Use-EFF0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff3 Private Use-EFF3 | eff2 Private Use-EFF2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff5 Private Use-EFF5 | eff4 Private Use-EFF4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff7 Private Use-EFF7 | eff6 Private Use-EFF6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* eff9 Private Use-EFF9 | eff8 Private Use-EFF8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* effb Private Use-EFFB | effa Private Use-EFFA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* effd Private Use-EFFD | effc Private Use-EFFC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* efff Private Use-EFFF | effe Private Use-EFFE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f001 Private Use-F001 | f000 Private Use-F000 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f003 Private Use-F003 | f002 Private Use-F002 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f005 Private Use-F005 | f004 Private Use-F004 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f007 Private Use-F007 | f006 Private Use-F006 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f009 Private Use-F009 | f008 Private Use-F008 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00b Private Use-F00B | f00a Private Use-F00A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00d Private Use-F00D | f00c Private Use-F00C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f00f Private Use-F00F | f00e Private Use-F00E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f011 Private Use-F011 | f010 Private Use-F010 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f013 Private Use-F013 | f012 Private Use-F012 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f015 Private Use-F015 | f014 Private Use-F014 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f017 Private Use-F017 | f016 Private Use-F016 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f019 Private Use-F019 | f018 Private Use-F018 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01b Private Use-F01B | f01a Private Use-F01A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01d Private Use-F01D | f01c Private Use-F01C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f01f Private Use-F01F | f01e Private Use-F01E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f021 Private Use-F021 | f020 Private Use-F020 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f023 Private Use-F023 | f022 Private Use-F022 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f025 Private Use-F025 | f024 Private Use-F024 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f027 Private Use-F027 | f026 Private Use-F026 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f029 Private Use-F029 | f028 Private Use-F028 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02b Private Use-F02B | f02a Private Use-F02A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02d Private Use-F02D | f02c Private Use-F02C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f02f Private Use-F02F | f02e Private Use-F02E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f031 Private Use-F031 | f030 Private Use-F030 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f033 Private Use-F033 | f032 Private Use-F032 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f035 Private Use-F035 | f034 Private Use-F034 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f037 Private Use-F037 | f036 Private Use-F036 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f039 Private Use-F039 | f038 Private Use-F038 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03b Private Use-F03B | f03a Private Use-F03A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03d Private Use-F03D | f03c Private Use-F03C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f03f Private Use-F03F | f03e Private Use-F03E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f041 Private Use-F041 | f040 Private Use-F040 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f043 Private Use-F043 | f042 Private Use-F042 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f045 Private Use-F045 | f044 Private Use-F044 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f047 Private Use-F047 | f046 Private Use-F046 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f049 Private Use-F049 | f048 Private Use-F048 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04b Private Use-F04B | f04a Private Use-F04A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04d Private Use-F04D | f04c Private Use-F04C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f04f Private Use-F04F | f04e Private Use-F04E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f051 Private Use-F051 | f050 Private Use-F050 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f053 Private Use-F053 | f052 Private Use-F052 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f055 Private Use-F055 | f054 Private Use-F054 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f057 Private Use-F057 | f056 Private Use-F056 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f059 Private Use-F059 | f058 Private Use-F058 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05b Private Use-F05B | f05a Private Use-F05A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05d Private Use-F05D | f05c Private Use-F05C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f05f Private Use-F05F | f05e Private Use-F05E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f061 Private Use-F061 | f060 Private Use-F060 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f063 Private Use-F063 | f062 Private Use-F062 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f065 Private Use-F065 | f064 Private Use-F064 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f067 Private Use-F067 | f066 Private Use-F066 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f069 Private Use-F069 | f068 Private Use-F068 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06b Private Use-F06B | f06a Private Use-F06A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06d Private Use-F06D | f06c Private Use-F06C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f06f Private Use-F06F | f06e Private Use-F06E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f071 Private Use-F071 | f070 Private Use-F070 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f073 Private Use-F073 | f072 Private Use-F072 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f075 Private Use-F075 | f074 Private Use-F074 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f077 Private Use-F077 | f076 Private Use-F076 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f079 Private Use-F079 | f078 Private Use-F078 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07b Private Use-F07B | f07a Private Use-F07A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07d Private Use-F07D | f07c Private Use-F07C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f07f Private Use-F07F | f07e Private Use-F07E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f081 Private Use-F081 | f080 Private Use-F080 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f083 Private Use-F083 | f082 Private Use-F082 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f085 Private Use-F085 | f084 Private Use-F084 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f087 Private Use-F087 | f086 Private Use-F086 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f089 Private Use-F089 | f088 Private Use-F088 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08b Private Use-F08B | f08a Private Use-F08A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08d Private Use-F08D | f08c Private Use-F08C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f08f Private Use-F08F | f08e Private Use-F08E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f091 Private Use-F091 | f090 Private Use-F090 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f093 Private Use-F093 | f092 Private Use-F092 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f095 Private Use-F095 | f094 Private Use-F094 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f097 Private Use-F097 | f096 Private Use-F096 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f099 Private Use-F099 | f098 Private Use-F098 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09b Private Use-F09B | f09a Private Use-F09A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09d Private Use-F09D | f09c Private Use-F09C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f09f Private Use-F09F | f09e Private Use-F09E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a1 Private Use-F0A1 | f0a0 Private Use-F0A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a3 Private Use-F0A3 | f0a2 Private Use-F0A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a5 Private Use-F0A5 | f0a4 Private Use-F0A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a7 Private Use-F0A7 | f0a6 Private Use-F0A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0a9 Private Use-F0A9 | f0a8 Private Use-F0A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ab Private Use-F0AB | f0aa Private Use-F0AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ad Private Use-F0AD | f0ac Private Use-F0AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0af Private Use-F0AF | f0ae Private Use-F0AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b1 Private Use-F0B1 | f0b0 Private Use-F0B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b3 Private Use-F0B3 | f0b2 Private Use-F0B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b5 Private Use-F0B5 | f0b4 Private Use-F0B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b7 Private Use-F0B7 | f0b6 Private Use-F0B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0b9 Private Use-F0B9 | f0b8 Private Use-F0B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bb Private Use-F0BB | f0ba Private Use-F0BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bd Private Use-F0BD | f0bc Private Use-F0BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0bf Private Use-F0BF | f0be Private Use-F0BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c1 Private Use-F0C1 | f0c0 Private Use-F0C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c3 Private Use-F0C3 | f0c2 Private Use-F0C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c5 Private Use-F0C5 | f0c4 Private Use-F0C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c7 Private Use-F0C7 | f0c6 Private Use-F0C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0c9 Private Use-F0C9 | f0c8 Private Use-F0C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cb Private Use-F0CB | f0ca Private Use-F0CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cd Private Use-F0CD | f0cc Private Use-F0CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0cf Private Use-F0CF | f0ce Private Use-F0CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d1 Private Use-F0D1 | f0d0 Private Use-F0D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d3 Private Use-F0D3 | f0d2 Private Use-F0D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d5 Private Use-F0D5 | f0d4 Private Use-F0D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d7 Private Use-F0D7 | f0d6 Private Use-F0D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0d9 Private Use-F0D9 | f0d8 Private Use-F0D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0db Private Use-F0DB | f0da Private Use-F0DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0dd Private Use-F0DD | f0dc Private Use-F0DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0df Private Use-F0DF | f0de Private Use-F0DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e1 Private Use-F0E1 | f0e0 Private Use-F0E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e3 Private Use-F0E3 | f0e2 Private Use-F0E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e5 Private Use-F0E5 | f0e4 Private Use-F0E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e7 Private Use-F0E7 | f0e6 Private Use-F0E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0e9 Private Use-F0E9 | f0e8 Private Use-F0E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0eb Private Use-F0EB | f0ea Private Use-F0EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ed Private Use-F0ED | f0ec Private Use-F0EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ef Private Use-F0EF | f0ee Private Use-F0EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f1 Private Use-F0F1 | f0f0 Private Use-F0F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f3 Private Use-F0F3 | f0f2 Private Use-F0F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f5 Private Use-F0F5 | f0f4 Private Use-F0F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f7 Private Use-F0F7 | f0f6 Private Use-F0F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0f9 Private Use-F0F9 | f0f8 Private Use-F0F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0fb Private Use-F0FB | f0fa Private Use-F0FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0fd Private Use-F0FD | f0fc Private Use-F0FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f0ff Private Use-F0FF | f0fe Private Use-F0FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f101 Private Use-F101 | f100 Private Use-F100 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f103 Private Use-F103 | f102 Private Use-F102 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f105 Private Use-F105 | f104 Private Use-F104 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f107 Private Use-F107 | f106 Private Use-F106 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f109 Private Use-F109 | f108 Private Use-F108 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10b Private Use-F10B | f10a Private Use-F10A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10d Private Use-F10D | f10c Private Use-F10C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f10f Private Use-F10F | f10e Private Use-F10E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f111 Private Use-F111 | f110 Private Use-F110 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f113 Private Use-F113 | f112 Private Use-F112 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f115 Private Use-F115 | f114 Private Use-F114 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f117 Private Use-F117 | f116 Private Use-F116 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f119 Private Use-F119 | f118 Private Use-F118 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11b Private Use-F11B | f11a Private Use-F11A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11d Private Use-F11D | f11c Private Use-F11C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f11f Private Use-F11F | f11e Private Use-F11E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f121 Private Use-F121 | f120 Private Use-F120 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f123 Private Use-F123 | f122 Private Use-F122 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f125 Private Use-F125 | f124 Private Use-F124 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f127 Private Use-F127 | f126 Private Use-F126 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f129 Private Use-F129 | f128 Private Use-F128 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12b Private Use-F12B | f12a Private Use-F12A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12d Private Use-F12D | f12c Private Use-F12C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f12f Private Use-F12F | f12e Private Use-F12E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f131 Private Use-F131 | f130 Private Use-F130 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f133 Private Use-F133 | f132 Private Use-F132 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f135 Private Use-F135 | f134 Private Use-F134 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f137 Private Use-F137 | f136 Private Use-F136 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f139 Private Use-F139 | f138 Private Use-F138 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13b Private Use-F13B | f13a Private Use-F13A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13d Private Use-F13D | f13c Private Use-F13C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f13f Private Use-F13F | f13e Private Use-F13E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f141 Private Use-F141 | f140 Private Use-F140 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f143 Private Use-F143 | f142 Private Use-F142 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f145 Private Use-F145 | f144 Private Use-F144 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f147 Private Use-F147 | f146 Private Use-F146 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f149 Private Use-F149 | f148 Private Use-F148 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14b Private Use-F14B | f14a Private Use-F14A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14d Private Use-F14D | f14c Private Use-F14C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f14f Private Use-F14F | f14e Private Use-F14E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f151 Private Use-F151 | f150 Private Use-F150 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f153 Private Use-F153 | f152 Private Use-F152 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f155 Private Use-F155 | f154 Private Use-F154 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f157 Private Use-F157 | f156 Private Use-F156 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f159 Private Use-F159 | f158 Private Use-F158 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15b Private Use-F15B | f15a Private Use-F15A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15d Private Use-F15D | f15c Private Use-F15C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f15f Private Use-F15F | f15e Private Use-F15E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f161 Private Use-F161 | f160 Private Use-F160 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f163 Private Use-F163 | f162 Private Use-F162 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f165 Private Use-F165 | f164 Private Use-F164 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f167 Private Use-F167 | f166 Private Use-F166 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f169 Private Use-F169 | f168 Private Use-F168 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16b Private Use-F16B | f16a Private Use-F16A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16d Private Use-F16D | f16c Private Use-F16C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f16f Private Use-F16F | f16e Private Use-F16E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f171 Private Use-F171 | f170 Private Use-F170 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f173 Private Use-F173 | f172 Private Use-F172 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f175 Private Use-F175 | f174 Private Use-F174 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f177 Private Use-F177 | f176 Private Use-F176 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f179 Private Use-F179 | f178 Private Use-F178 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17b Private Use-F17B | f17a Private Use-F17A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17d Private Use-F17D | f17c Private Use-F17C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f17f Private Use-F17F | f17e Private Use-F17E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f181 Private Use-F181 | f180 Private Use-F180 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f183 Private Use-F183 | f182 Private Use-F182 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f185 Private Use-F185 | f184 Private Use-F184 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f187 Private Use-F187 | f186 Private Use-F186 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f189 Private Use-F189 | f188 Private Use-F188 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18b Private Use-F18B | f18a Private Use-F18A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18d Private Use-F18D | f18c Private Use-F18C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f18f Private Use-F18F | f18e Private Use-F18E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f191 Private Use-F191 | f190 Private Use-F190 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f193 Private Use-F193 | f192 Private Use-F192 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f195 Private Use-F195 | f194 Private Use-F194 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f197 Private Use-F197 | f196 Private Use-F196 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f199 Private Use-F199 | f198 Private Use-F198 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19b Private Use-F19B | f19a Private Use-F19A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19d Private Use-F19D | f19c Private Use-F19C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f19f Private Use-F19F | f19e Private Use-F19E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a1 Private Use-F1A1 | f1a0 Private Use-F1A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a3 Private Use-F1A3 | f1a2 Private Use-F1A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a5 Private Use-F1A5 | f1a4 Private Use-F1A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a7 Private Use-F1A7 | f1a6 Private Use-F1A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1a9 Private Use-F1A9 | f1a8 Private Use-F1A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ab Private Use-F1AB | f1aa Private Use-F1AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ad Private Use-F1AD | f1ac Private Use-F1AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1af Private Use-F1AF | f1ae Private Use-F1AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b1 Private Use-F1B1 | f1b0 Private Use-F1B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b3 Private Use-F1B3 | f1b2 Private Use-F1B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b5 Private Use-F1B5 | f1b4 Private Use-F1B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b7 Private Use-F1B7 | f1b6 Private Use-F1B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1b9 Private Use-F1B9 | f1b8 Private Use-F1B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bb Private Use-F1BB | f1ba Private Use-F1BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bd Private Use-F1BD | f1bc Private Use-F1BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1bf Private Use-F1BF | f1be Private Use-F1BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c1 Private Use-F1C1 | f1c0 Private Use-F1C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c3 Private Use-F1C3 | f1c2 Private Use-F1C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c5 Private Use-F1C5 | f1c4 Private Use-F1C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c7 Private Use-F1C7 | f1c6 Private Use-F1C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1c9 Private Use-F1C9 | f1c8 Private Use-F1C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cb Private Use-F1CB | f1ca Private Use-F1CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cd Private Use-F1CD | f1cc Private Use-F1CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1cf Private Use-F1CF | f1ce Private Use-F1CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d1 Private Use-F1D1 | f1d0 Private Use-F1D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d3 Private Use-F1D3 | f1d2 Private Use-F1D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d5 Private Use-F1D5 | f1d4 Private Use-F1D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d7 Private Use-F1D7 | f1d6 Private Use-F1D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1d9 Private Use-F1D9 | f1d8 Private Use-F1D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1db Private Use-F1DB | f1da Private Use-F1DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1dd Private Use-F1DD | f1dc Private Use-F1DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1df Private Use-F1DF | f1de Private Use-F1DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e1 Private Use-F1E1 | f1e0 Private Use-F1E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e3 Private Use-F1E3 | f1e2 Private Use-F1E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e5 Private Use-F1E5 | f1e4 Private Use-F1E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e7 Private Use-F1E7 | f1e6 Private Use-F1E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1e9 Private Use-F1E9 | f1e8 Private Use-F1E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1eb Private Use-F1EB | f1ea Private Use-F1EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ed Private Use-F1ED | f1ec Private Use-F1EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ef Private Use-F1EF | f1ee Private Use-F1EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f1 Private Use-F1F1 | f1f0 Private Use-F1F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f3 Private Use-F1F3 | f1f2 Private Use-F1F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f5 Private Use-F1F5 | f1f4 Private Use-F1F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f7 Private Use-F1F7 | f1f6 Private Use-F1F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1f9 Private Use-F1F9 | f1f8 Private Use-F1F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1fb Private Use-F1FB | f1fa Private Use-F1FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1fd Private Use-F1FD | f1fc Private Use-F1FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f1ff Private Use-F1FF | f1fe Private Use-F1FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f201 Private Use-F201 | f200 Private Use-F200 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f203 Private Use-F203 | f202 Private Use-F202 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f205 Private Use-F205 | f204 Private Use-F204 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f207 Private Use-F207 | f206 Private Use-F206 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f209 Private Use-F209 | f208 Private Use-F208 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20b Private Use-F20B | f20a Private Use-F20A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20d Private Use-F20D | f20c Private Use-F20C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f20f Private Use-F20F | f20e Private Use-F20E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f211 Private Use-F211 | f210 Private Use-F210 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f213 Private Use-F213 | f212 Private Use-F212 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f215 Private Use-F215 | f214 Private Use-F214 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f217 Private Use-F217 | f216 Private Use-F216 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f219 Private Use-F219 | f218 Private Use-F218 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21b Private Use-F21B | f21a Private Use-F21A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21d Private Use-F21D | f21c Private Use-F21C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f21f Private Use-F21F | f21e Private Use-F21E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f221 Private Use-F221 | f220 Private Use-F220 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f223 Private Use-F223 | f222 Private Use-F222 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f225 Private Use-F225 | f224 Private Use-F224 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f227 Private Use-F227 | f226 Private Use-F226 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f229 Private Use-F229 | f228 Private Use-F228 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22b Private Use-F22B | f22a Private Use-F22A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22d Private Use-F22D | f22c Private Use-F22C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f22f Private Use-F22F | f22e Private Use-F22E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f231 Private Use-F231 | f230 Private Use-F230 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f233 Private Use-F233 | f232 Private Use-F232 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f235 Private Use-F235 | f234 Private Use-F234 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f237 Private Use-F237 | f236 Private Use-F236 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f239 Private Use-F239 | f238 Private Use-F238 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23b Private Use-F23B | f23a Private Use-F23A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23d Private Use-F23D | f23c Private Use-F23C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f23f Private Use-F23F | f23e Private Use-F23E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f241 Private Use-F241 | f240 Private Use-F240 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f243 Private Use-F243 | f242 Private Use-F242 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f245 Private Use-F245 | f244 Private Use-F244 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f247 Private Use-F247 | f246 Private Use-F246 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f249 Private Use-F249 | f248 Private Use-F248 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24b Private Use-F24B | f24a Private Use-F24A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24d Private Use-F24D | f24c Private Use-F24C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f24f Private Use-F24F | f24e Private Use-F24E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f251 Private Use-F251 | f250 Private Use-F250 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f253 Private Use-F253 | f252 Private Use-F252 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f255 Private Use-F255 | f254 Private Use-F254 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f257 Private Use-F257 | f256 Private Use-F256 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f259 Private Use-F259 | f258 Private Use-F258 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25b Private Use-F25B | f25a Private Use-F25A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25d Private Use-F25D | f25c Private Use-F25C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f25f Private Use-F25F | f25e Private Use-F25E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f261 Private Use-F261 | f260 Private Use-F260 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f263 Private Use-F263 | f262 Private Use-F262 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f265 Private Use-F265 | f264 Private Use-F264 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f267 Private Use-F267 | f266 Private Use-F266 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f269 Private Use-F269 | f268 Private Use-F268 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26b Private Use-F26B | f26a Private Use-F26A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26d Private Use-F26D | f26c Private Use-F26C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f26f Private Use-F26F | f26e Private Use-F26E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f271 Private Use-F271 | f270 Private Use-F270 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f273 Private Use-F273 | f272 Private Use-F272 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f275 Private Use-F275 | f274 Private Use-F274 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f277 Private Use-F277 | f276 Private Use-F276 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f279 Private Use-F279 | f278 Private Use-F278 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27b Private Use-F27B | f27a Private Use-F27A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27d Private Use-F27D | f27c Private Use-F27C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f27f Private Use-F27F | f27e Private Use-F27E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f281 Private Use-F281 | f280 Private Use-F280 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f283 Private Use-F283 | f282 Private Use-F282 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f285 Private Use-F285 | f284 Private Use-F284 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f287 Private Use-F287 | f286 Private Use-F286 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f289 Private Use-F289 | f288 Private Use-F288 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28b Private Use-F28B | f28a Private Use-F28A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28d Private Use-F28D | f28c Private Use-F28C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f28f Private Use-F28F | f28e Private Use-F28E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f291 Private Use-F291 | f290 Private Use-F290 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f293 Private Use-F293 | f292 Private Use-F292 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f295 Private Use-F295 | f294 Private Use-F294 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f297 Private Use-F297 | f296 Private Use-F296 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f299 Private Use-F299 | f298 Private Use-F298 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29b Private Use-F29B | f29a Private Use-F29A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29d Private Use-F29D | f29c Private Use-F29C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f29f Private Use-F29F | f29e Private Use-F29E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a1 Private Use-F2A1 | f2a0 Private Use-F2A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a3 Private Use-F2A3 | f2a2 Private Use-F2A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a5 Private Use-F2A5 | f2a4 Private Use-F2A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a7 Private Use-F2A7 | f2a6 Private Use-F2A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2a9 Private Use-F2A9 | f2a8 Private Use-F2A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ab Private Use-F2AB | f2aa Private Use-F2AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ad Private Use-F2AD | f2ac Private Use-F2AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2af Private Use-F2AF | f2ae Private Use-F2AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b1 Private Use-F2B1 | f2b0 Private Use-F2B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b3 Private Use-F2B3 | f2b2 Private Use-F2B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b5 Private Use-F2B5 | f2b4 Private Use-F2B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b7 Private Use-F2B7 | f2b6 Private Use-F2B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2b9 Private Use-F2B9 | f2b8 Private Use-F2B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bb Private Use-F2BB | f2ba Private Use-F2BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bd Private Use-F2BD | f2bc Private Use-F2BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2bf Private Use-F2BF | f2be Private Use-F2BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c1 Private Use-F2C1 | f2c0 Private Use-F2C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c3 Private Use-F2C3 | f2c2 Private Use-F2C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c5 Private Use-F2C5 | f2c4 Private Use-F2C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c7 Private Use-F2C7 | f2c6 Private Use-F2C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2c9 Private Use-F2C9 | f2c8 Private Use-F2C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cb Private Use-F2CB | f2ca Private Use-F2CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cd Private Use-F2CD | f2cc Private Use-F2CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2cf Private Use-F2CF | f2ce Private Use-F2CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d1 Private Use-F2D1 | f2d0 Private Use-F2D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d3 Private Use-F2D3 | f2d2 Private Use-F2D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d5 Private Use-F2D5 | f2d4 Private Use-F2D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d7 Private Use-F2D7 | f2d6 Private Use-F2D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2d9 Private Use-F2D9 | f2d8 Private Use-F2D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2db Private Use-F2DB | f2da Private Use-F2DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2dd Private Use-F2DD | f2dc Private Use-F2DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2df Private Use-F2DF | f2de Private Use-F2DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e1 Private Use-F2E1 | f2e0 Private Use-F2E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e3 Private Use-F2E3 | f2e2 Private Use-F2E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e5 Private Use-F2E5 | f2e4 Private Use-F2E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e7 Private Use-F2E7 | f2e6 Private Use-F2E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2e9 Private Use-F2E9 | f2e8 Private Use-F2E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2eb Private Use-F2EB | f2ea Private Use-F2EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ed Private Use-F2ED | f2ec Private Use-F2EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ef Private Use-F2EF | f2ee Private Use-F2EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f1 Private Use-F2F1 | f2f0 Private Use-F2F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f3 Private Use-F2F3 | f2f2 Private Use-F2F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f5 Private Use-F2F5 | f2f4 Private Use-F2F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f7 Private Use-F2F7 | f2f6 Private Use-F2F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2f9 Private Use-F2F9 | f2f8 Private Use-F2F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2fb Private Use-F2FB | f2fa Private Use-F2FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2fd Private Use-F2FD | f2fc Private Use-F2FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f2ff Private Use-F2FF | f2fe Private Use-F2FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f301 Private Use-F301 | f300 Private Use-F300 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f303 Private Use-F303 | f302 Private Use-F302 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f305 Private Use-F305 | f304 Private Use-F304 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f307 Private Use-F307 | f306 Private Use-F306 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f309 Private Use-F309 | f308 Private Use-F308 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30b Private Use-F30B | f30a Private Use-F30A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30d Private Use-F30D | f30c Private Use-F30C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f30f Private Use-F30F | f30e Private Use-F30E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f311 Private Use-F311 | f310 Private Use-F310 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f313 Private Use-F313 | f312 Private Use-F312 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f315 Private Use-F315 | f314 Private Use-F314 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f317 Private Use-F317 | f316 Private Use-F316 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f319 Private Use-F319 | f318 Private Use-F318 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31b Private Use-F31B | f31a Private Use-F31A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31d Private Use-F31D | f31c Private Use-F31C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f31f Private Use-F31F | f31e Private Use-F31E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f321 Private Use-F321 | f320 Private Use-F320 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f323 Private Use-F323 | f322 Private Use-F322 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f325 Private Use-F325 | f324 Private Use-F324 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f327 Private Use-F327 | f326 Private Use-F326 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f329 Private Use-F329 | f328 Private Use-F328 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32b Private Use-F32B | f32a Private Use-F32A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32d Private Use-F32D | f32c Private Use-F32C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f32f Private Use-F32F | f32e Private Use-F32E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f331 Private Use-F331 | f330 Private Use-F330 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f333 Private Use-F333 | f332 Private Use-F332 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f335 Private Use-F335 | f334 Private Use-F334 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f337 Private Use-F337 | f336 Private Use-F336 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f339 Private Use-F339 | f338 Private Use-F338 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33b Private Use-F33B | f33a Private Use-F33A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33d Private Use-F33D | f33c Private Use-F33C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f33f Private Use-F33F | f33e Private Use-F33E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f341 Private Use-F341 | f340 Private Use-F340 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f343 Private Use-F343 | f342 Private Use-F342 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f345 Private Use-F345 | f344 Private Use-F344 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f347 Private Use-F347 | f346 Private Use-F346 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f349 Private Use-F349 | f348 Private Use-F348 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34b Private Use-F34B | f34a Private Use-F34A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34d Private Use-F34D | f34c Private Use-F34C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f34f Private Use-F34F | f34e Private Use-F34E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f351 Private Use-F351 | f350 Private Use-F350 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f353 Private Use-F353 | f352 Private Use-F352 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f355 Private Use-F355 | f354 Private Use-F354 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f357 Private Use-F357 | f356 Private Use-F356 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f359 Private Use-F359 | f358 Private Use-F358 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35b Private Use-F35B | f35a Private Use-F35A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35d Private Use-F35D | f35c Private Use-F35C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f35f Private Use-F35F | f35e Private Use-F35E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f361 Private Use-F361 | f360 Private Use-F360 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f363 Private Use-F363 | f362 Private Use-F362 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f365 Private Use-F365 | f364 Private Use-F364 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f367 Private Use-F367 | f366 Private Use-F366 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f369 Private Use-F369 | f368 Private Use-F368 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36b Private Use-F36B | f36a Private Use-F36A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36d Private Use-F36D | f36c Private Use-F36C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f36f Private Use-F36F | f36e Private Use-F36E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f371 Private Use-F371 | f370 Private Use-F370 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f373 Private Use-F373 | f372 Private Use-F372 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f375 Private Use-F375 | f374 Private Use-F374 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f377 Private Use-F377 | f376 Private Use-F376 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f379 Private Use-F379 | f378 Private Use-F378 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37b Private Use-F37B | f37a Private Use-F37A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37d Private Use-F37D | f37c Private Use-F37C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f37f Private Use-F37F | f37e Private Use-F37E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f381 Private Use-F381 | f380 Private Use-F380 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f383 Private Use-F383 | f382 Private Use-F382 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f385 Private Use-F385 | f384 Private Use-F384 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f387 Private Use-F387 | f386 Private Use-F386 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f389 Private Use-F389 | f388 Private Use-F388 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38b Private Use-F38B | f38a Private Use-F38A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38d Private Use-F38D | f38c Private Use-F38C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f38f Private Use-F38F | f38e Private Use-F38E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f391 Private Use-F391 | f390 Private Use-F390 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f393 Private Use-F393 | f392 Private Use-F392 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f395 Private Use-F395 | f394 Private Use-F394 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f397 Private Use-F397 | f396 Private Use-F396 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f399 Private Use-F399 | f398 Private Use-F398 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39b Private Use-F39B | f39a Private Use-F39A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39d Private Use-F39D | f39c Private Use-F39C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f39f Private Use-F39F | f39e Private Use-F39E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a1 Private Use-F3A1 | f3a0 Private Use-F3A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a3 Private Use-F3A3 | f3a2 Private Use-F3A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a5 Private Use-F3A5 | f3a4 Private Use-F3A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a7 Private Use-F3A7 | f3a6 Private Use-F3A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3a9 Private Use-F3A9 | f3a8 Private Use-F3A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ab Private Use-F3AB | f3aa Private Use-F3AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ad Private Use-F3AD | f3ac Private Use-F3AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3af Private Use-F3AF | f3ae Private Use-F3AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b1 Private Use-F3B1 | f3b0 Private Use-F3B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b3 Private Use-F3B3 | f3b2 Private Use-F3B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b5 Private Use-F3B5 | f3b4 Private Use-F3B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b7 Private Use-F3B7 | f3b6 Private Use-F3B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3b9 Private Use-F3B9 | f3b8 Private Use-F3B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bb Private Use-F3BB | f3ba Private Use-F3BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bd Private Use-F3BD | f3bc Private Use-F3BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3bf Private Use-F3BF | f3be Private Use-F3BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c1 Private Use-F3C1 | f3c0 Private Use-F3C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c3 Private Use-F3C3 | f3c2 Private Use-F3C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c5 Private Use-F3C5 | f3c4 Private Use-F3C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c7 Private Use-F3C7 | f3c6 Private Use-F3C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3c9 Private Use-F3C9 | f3c8 Private Use-F3C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cb Private Use-F3CB | f3ca Private Use-F3CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cd Private Use-F3CD | f3cc Private Use-F3CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3cf Private Use-F3CF | f3ce Private Use-F3CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d1 Private Use-F3D1 | f3d0 Private Use-F3D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d3 Private Use-F3D3 | f3d2 Private Use-F3D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d5 Private Use-F3D5 | f3d4 Private Use-F3D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d7 Private Use-F3D7 | f3d6 Private Use-F3D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3d9 Private Use-F3D9 | f3d8 Private Use-F3D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3db Private Use-F3DB | f3da Private Use-F3DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3dd Private Use-F3DD | f3dc Private Use-F3DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3df Private Use-F3DF | f3de Private Use-F3DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e1 Private Use-F3E1 | f3e0 Private Use-F3E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e3 Private Use-F3E3 | f3e2 Private Use-F3E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e5 Private Use-F3E5 | f3e4 Private Use-F3E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e7 Private Use-F3E7 | f3e6 Private Use-F3E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3e9 Private Use-F3E9 | f3e8 Private Use-F3E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3eb Private Use-F3EB | f3ea Private Use-F3EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ed Private Use-F3ED | f3ec Private Use-F3EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ef Private Use-F3EF | f3ee Private Use-F3EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f1 Private Use-F3F1 | f3f0 Private Use-F3F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f3 Private Use-F3F3 | f3f2 Private Use-F3F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f5 Private Use-F3F5 | f3f4 Private Use-F3F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f7 Private Use-F3F7 | f3f6 Private Use-F3F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3f9 Private Use-F3F9 | f3f8 Private Use-F3F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3fb Private Use-F3FB | f3fa Private Use-F3FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3fd Private Use-F3FD | f3fc Private Use-F3FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f3ff Private Use-F3FF | f3fe Private Use-F3FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f401 Private Use-F401 | f400 Private Use-F400 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f403 Private Use-F403 | f402 Private Use-F402 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f405 Private Use-F405 | f404 Private Use-F404 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f407 Private Use-F407 | f406 Private Use-F406 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f409 Private Use-F409 | f408 Private Use-F408 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40b Private Use-F40B | f40a Private Use-F40A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40d Private Use-F40D | f40c Private Use-F40C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f40f Private Use-F40F | f40e Private Use-F40E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f411 Private Use-F411 | f410 Private Use-F410 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f413 Private Use-F413 | f412 Private Use-F412 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f415 Private Use-F415 | f414 Private Use-F414 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f417 Private Use-F417 | f416 Private Use-F416 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f419 Private Use-F419 | f418 Private Use-F418 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41b Private Use-F41B | f41a Private Use-F41A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41d Private Use-F41D | f41c Private Use-F41C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f41f Private Use-F41F | f41e Private Use-F41E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f421 Private Use-F421 | f420 Private Use-F420 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f423 Private Use-F423 | f422 Private Use-F422 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f425 Private Use-F425 | f424 Private Use-F424 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f427 Private Use-F427 | f426 Private Use-F426 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f429 Private Use-F429 | f428 Private Use-F428 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42b Private Use-F42B | f42a Private Use-F42A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42d Private Use-F42D | f42c Private Use-F42C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f42f Private Use-F42F | f42e Private Use-F42E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f431 Private Use-F431 | f430 Private Use-F430 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f433 Private Use-F433 | f432 Private Use-F432 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f435 Private Use-F435 | f434 Private Use-F434 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f437 Private Use-F437 | f436 Private Use-F436 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f439 Private Use-F439 | f438 Private Use-F438 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43b Private Use-F43B | f43a Private Use-F43A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43d Private Use-F43D | f43c Private Use-F43C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f43f Private Use-F43F | f43e Private Use-F43E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f441 Private Use-F441 | f440 Private Use-F440 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f443 Private Use-F443 | f442 Private Use-F442 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f445 Private Use-F445 | f444 Private Use-F444 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f447 Private Use-F447 | f446 Private Use-F446 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f449 Private Use-F449 | f448 Private Use-F448 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44b Private Use-F44B | f44a Private Use-F44A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44d Private Use-F44D | f44c Private Use-F44C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f44f Private Use-F44F | f44e Private Use-F44E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f451 Private Use-F451 | f450 Private Use-F450 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f453 Private Use-F453 | f452 Private Use-F452 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f455 Private Use-F455 | f454 Private Use-F454 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f457 Private Use-F457 | f456 Private Use-F456 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f459 Private Use-F459 | f458 Private Use-F458 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45b Private Use-F45B | f45a Private Use-F45A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45d Private Use-F45D | f45c Private Use-F45C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f45f Private Use-F45F | f45e Private Use-F45E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f461 Private Use-F461 | f460 Private Use-F460 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f463 Private Use-F463 | f462 Private Use-F462 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f465 Private Use-F465 | f464 Private Use-F464 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f467 Private Use-F467 | f466 Private Use-F466 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f469 Private Use-F469 | f468 Private Use-F468 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46b Private Use-F46B | f46a Private Use-F46A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46d Private Use-F46D | f46c Private Use-F46C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f46f Private Use-F46F | f46e Private Use-F46E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f471 Private Use-F471 | f470 Private Use-F470 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f473 Private Use-F473 | f472 Private Use-F472 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f475 Private Use-F475 | f474 Private Use-F474 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f477 Private Use-F477 | f476 Private Use-F476 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f479 Private Use-F479 | f478 Private Use-F478 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47b Private Use-F47B | f47a Private Use-F47A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47d Private Use-F47D | f47c Private Use-F47C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f47f Private Use-F47F | f47e Private Use-F47E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f481 Private Use-F481 | f480 Private Use-F480 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f483 Private Use-F483 | f482 Private Use-F482 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f485 Private Use-F485 | f484 Private Use-F484 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f487 Private Use-F487 | f486 Private Use-F486 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f489 Private Use-F489 | f488 Private Use-F488 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48b Private Use-F48B | f48a Private Use-F48A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48d Private Use-F48D | f48c Private Use-F48C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f48f Private Use-F48F | f48e Private Use-F48E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f491 Private Use-F491 | f490 Private Use-F490 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f493 Private Use-F493 | f492 Private Use-F492 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f495 Private Use-F495 | f494 Private Use-F494 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f497 Private Use-F497 | f496 Private Use-F496 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f499 Private Use-F499 | f498 Private Use-F498 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49b Private Use-F49B | f49a Private Use-F49A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49d Private Use-F49D | f49c Private Use-F49C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f49f Private Use-F49F | f49e Private Use-F49E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a1 Private Use-F4A1 | f4a0 Private Use-F4A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a3 Private Use-F4A3 | f4a2 Private Use-F4A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a5 Private Use-F4A5 | f4a4 Private Use-F4A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a7 Private Use-F4A7 | f4a6 Private Use-F4A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4a9 Private Use-F4A9 | f4a8 Private Use-F4A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ab Private Use-F4AB | f4aa Private Use-F4AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ad Private Use-F4AD | f4ac Private Use-F4AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4af Private Use-F4AF | f4ae Private Use-F4AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b1 Private Use-F4B1 | f4b0 Private Use-F4B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b3 Private Use-F4B3 | f4b2 Private Use-F4B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b5 Private Use-F4B5 | f4b4 Private Use-F4B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b7 Private Use-F4B7 | f4b6 Private Use-F4B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4b9 Private Use-F4B9 | f4b8 Private Use-F4B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bb Private Use-F4BB | f4ba Private Use-F4BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bd Private Use-F4BD | f4bc Private Use-F4BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4bf Private Use-F4BF | f4be Private Use-F4BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c1 Private Use-F4C1 | f4c0 Private Use-F4C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c3 Private Use-F4C3 | f4c2 Private Use-F4C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c5 Private Use-F4C5 | f4c4 Private Use-F4C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c7 Private Use-F4C7 | f4c6 Private Use-F4C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4c9 Private Use-F4C9 | f4c8 Private Use-F4C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cb Private Use-F4CB | f4ca Private Use-F4CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cd Private Use-F4CD | f4cc Private Use-F4CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4cf Private Use-F4CF | f4ce Private Use-F4CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d1 Private Use-F4D1 | f4d0 Private Use-F4D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d3 Private Use-F4D3 | f4d2 Private Use-F4D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d5 Private Use-F4D5 | f4d4 Private Use-F4D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d7 Private Use-F4D7 | f4d6 Private Use-F4D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4d9 Private Use-F4D9 | f4d8 Private Use-F4D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4db Private Use-F4DB | f4da Private Use-F4DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4dd Private Use-F4DD | f4dc Private Use-F4DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4df Private Use-F4DF | f4de Private Use-F4DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e1 Private Use-F4E1 | f4e0 Private Use-F4E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e3 Private Use-F4E3 | f4e2 Private Use-F4E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e5 Private Use-F4E5 | f4e4 Private Use-F4E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e7 Private Use-F4E7 | f4e6 Private Use-F4E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4e9 Private Use-F4E9 | f4e8 Private Use-F4E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4eb Private Use-F4EB | f4ea Private Use-F4EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ed Private Use-F4ED | f4ec Private Use-F4EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ef Private Use-F4EF | f4ee Private Use-F4EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f1 Private Use-F4F1 | f4f0 Private Use-F4F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f3 Private Use-F4F3 | f4f2 Private Use-F4F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f5 Private Use-F4F5 | f4f4 Private Use-F4F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f7 Private Use-F4F7 | f4f6 Private Use-F4F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4f9 Private Use-F4F9 | f4f8 Private Use-F4F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4fb Private Use-F4FB | f4fa Private Use-F4FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4fd Private Use-F4FD | f4fc Private Use-F4FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f4ff Private Use-F4FF | f4fe Private Use-F4FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f501 Private Use-F501 | f500 Private Use-F500 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f503 Private Use-F503 | f502 Private Use-F502 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f505 Private Use-F505 | f504 Private Use-F504 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f507 Private Use-F507 | f506 Private Use-F506 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f509 Private Use-F509 | f508 Private Use-F508 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50b Private Use-F50B | f50a Private Use-F50A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50d Private Use-F50D | f50c Private Use-F50C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f50f Private Use-F50F | f50e Private Use-F50E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f511 Private Use-F511 | f510 Private Use-F510 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f513 Private Use-F513 | f512 Private Use-F512 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f515 Private Use-F515 | f514 Private Use-F514 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f517 Private Use-F517 | f516 Private Use-F516 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f519 Private Use-F519 | f518 Private Use-F518 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51b Private Use-F51B | f51a Private Use-F51A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51d Private Use-F51D | f51c Private Use-F51C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f51f Private Use-F51F | f51e Private Use-F51E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f521 Private Use-F521 | f520 Private Use-F520 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f523 Private Use-F523 | f522 Private Use-F522 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f525 Private Use-F525 | f524 Private Use-F524 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f527 Private Use-F527 | f526 Private Use-F526 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f529 Private Use-F529 | f528 Private Use-F528 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52b Private Use-F52B | f52a Private Use-F52A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52d Private Use-F52D | f52c Private Use-F52C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f52f Private Use-F52F | f52e Private Use-F52E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f531 Private Use-F531 | f530 Private Use-F530 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f533 Private Use-F533 | f532 Private Use-F532 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f535 Private Use-F535 | f534 Private Use-F534 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f537 Private Use-F537 | f536 Private Use-F536 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f539 Private Use-F539 | f538 Private Use-F538 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53b Private Use-F53B | f53a Private Use-F53A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53d Private Use-F53D | f53c Private Use-F53C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f53f Private Use-F53F | f53e Private Use-F53E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f541 Private Use-F541 | f540 Private Use-F540 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f543 Private Use-F543 | f542 Private Use-F542 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f545 Private Use-F545 | f544 Private Use-F544 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f547 Private Use-F547 | f546 Private Use-F546 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f549 Private Use-F549 | f548 Private Use-F548 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54b Private Use-F54B | f54a Private Use-F54A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54d Private Use-F54D | f54c Private Use-F54C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f54f Private Use-F54F | f54e Private Use-F54E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f551 Private Use-F551 | f550 Private Use-F550 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f553 Private Use-F553 | f552 Private Use-F552 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f555 Private Use-F555 | f554 Private Use-F554 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f557 Private Use-F557 | f556 Private Use-F556 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f559 Private Use-F559 | f558 Private Use-F558 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55b Private Use-F55B | f55a Private Use-F55A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55d Private Use-F55D | f55c Private Use-F55C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f55f Private Use-F55F | f55e Private Use-F55E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f561 Private Use-F561 | f560 Private Use-F560 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f563 Private Use-F563 | f562 Private Use-F562 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f565 Private Use-F565 | f564 Private Use-F564 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f567 Private Use-F567 | f566 Private Use-F566 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f569 Private Use-F569 | f568 Private Use-F568 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56b Private Use-F56B | f56a Private Use-F56A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56d Private Use-F56D | f56c Private Use-F56C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f56f Private Use-F56F | f56e Private Use-F56E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f571 Private Use-F571 | f570 Private Use-F570 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f573 Private Use-F573 | f572 Private Use-F572 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f575 Private Use-F575 | f574 Private Use-F574 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f577 Private Use-F577 | f576 Private Use-F576 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f579 Private Use-F579 | f578 Private Use-F578 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57b Private Use-F57B | f57a Private Use-F57A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57d Private Use-F57D | f57c Private Use-F57C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f57f Private Use-F57F | f57e Private Use-F57E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f581 Private Use-F581 | f580 Private Use-F580 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f583 Private Use-F583 | f582 Private Use-F582 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f585 Private Use-F585 | f584 Private Use-F584 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f587 Private Use-F587 | f586 Private Use-F586 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f589 Private Use-F589 | f588 Private Use-F588 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58b Private Use-F58B | f58a Private Use-F58A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58d Private Use-F58D | f58c Private Use-F58C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f58f Private Use-F58F | f58e Private Use-F58E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f591 Private Use-F591 | f590 Private Use-F590 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f593 Private Use-F593 | f592 Private Use-F592 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f595 Private Use-F595 | f594 Private Use-F594 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f597 Private Use-F597 | f596 Private Use-F596 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f599 Private Use-F599 | f598 Private Use-F598 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59b Private Use-F59B | f59a Private Use-F59A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59d Private Use-F59D | f59c Private Use-F59C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f59f Private Use-F59F | f59e Private Use-F59E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a1 Private Use-F5A1 | f5a0 Private Use-F5A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a3 Private Use-F5A3 | f5a2 Private Use-F5A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a5 Private Use-F5A5 | f5a4 Private Use-F5A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a7 Private Use-F5A7 | f5a6 Private Use-F5A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5a9 Private Use-F5A9 | f5a8 Private Use-F5A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ab Private Use-F5AB | f5aa Private Use-F5AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ad Private Use-F5AD | f5ac Private Use-F5AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5af Private Use-F5AF | f5ae Private Use-F5AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b1 Private Use-F5B1 | f5b0 Private Use-F5B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b3 Private Use-F5B3 | f5b2 Private Use-F5B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b5 Private Use-F5B5 | f5b4 Private Use-F5B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b7 Private Use-F5B7 | f5b6 Private Use-F5B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5b9 Private Use-F5B9 | f5b8 Private Use-F5B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bb Private Use-F5BB | f5ba Private Use-F5BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bd Private Use-F5BD | f5bc Private Use-F5BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5bf Private Use-F5BF | f5be Private Use-F5BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c1 Private Use-F5C1 | f5c0 Private Use-F5C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c3 Private Use-F5C3 | f5c2 Private Use-F5C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c5 Private Use-F5C5 | f5c4 Private Use-F5C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c7 Private Use-F5C7 | f5c6 Private Use-F5C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5c9 Private Use-F5C9 | f5c8 Private Use-F5C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cb Private Use-F5CB | f5ca Private Use-F5CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cd Private Use-F5CD | f5cc Private Use-F5CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5cf Private Use-F5CF | f5ce Private Use-F5CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d1 Private Use-F5D1 | f5d0 Private Use-F5D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d3 Private Use-F5D3 | f5d2 Private Use-F5D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d5 Private Use-F5D5 | f5d4 Private Use-F5D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d7 Private Use-F5D7 | f5d6 Private Use-F5D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5d9 Private Use-F5D9 | f5d8 Private Use-F5D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5db Private Use-F5DB | f5da Private Use-F5DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5dd Private Use-F5DD | f5dc Private Use-F5DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5df Private Use-F5DF | f5de Private Use-F5DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e1 Private Use-F5E1 | f5e0 Private Use-F5E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e3 Private Use-F5E3 | f5e2 Private Use-F5E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e5 Private Use-F5E5 | f5e4 Private Use-F5E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e7 Private Use-F5E7 | f5e6 Private Use-F5E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5e9 Private Use-F5E9 | f5e8 Private Use-F5E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5eb Private Use-F5EB | f5ea Private Use-F5EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ed Private Use-F5ED | f5ec Private Use-F5EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ef Private Use-F5EF | f5ee Private Use-F5EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f1 Private Use-F5F1 | f5f0 Private Use-F5F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f3 Private Use-F5F3 | f5f2 Private Use-F5F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f5 Private Use-F5F5 | f5f4 Private Use-F5F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f7 Private Use-F5F7 | f5f6 Private Use-F5F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5f9 Private Use-F5F9 | f5f8 Private Use-F5F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5fb Private Use-F5FB | f5fa Private Use-F5FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5fd Private Use-F5FD | f5fc Private Use-F5FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f5ff Private Use-F5FF | f5fe Private Use-F5FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f601 Private Use-F601 | f600 Private Use-F600 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f603 Private Use-F603 | f602 Private Use-F602 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f605 Private Use-F605 | f604 Private Use-F604 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f607 Private Use-F607 | f606 Private Use-F606 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f609 Private Use-F609 | f608 Private Use-F608 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60b Private Use-F60B | f60a Private Use-F60A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60d Private Use-F60D | f60c Private Use-F60C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f60f Private Use-F60F | f60e Private Use-F60E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f611 Private Use-F611 | f610 Private Use-F610 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f613 Private Use-F613 | f612 Private Use-F612 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f615 Private Use-F615 | f614 Private Use-F614 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f617 Private Use-F617 | f616 Private Use-F616 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f619 Private Use-F619 | f618 Private Use-F618 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61b Private Use-F61B | f61a Private Use-F61A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61d Private Use-F61D | f61c Private Use-F61C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f61f Private Use-F61F | f61e Private Use-F61E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f621 Private Use-F621 | f620 Private Use-F620 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f623 Private Use-F623 | f622 Private Use-F622 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f625 Private Use-F625 | f624 Private Use-F624 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f627 Private Use-F627 | f626 Private Use-F626 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f629 Private Use-F629 | f628 Private Use-F628 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62b Private Use-F62B | f62a Private Use-F62A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62d Private Use-F62D | f62c Private Use-F62C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f62f Private Use-F62F | f62e Private Use-F62E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f631 Private Use-F631 | f630 Private Use-F630 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f633 Private Use-F633 | f632 Private Use-F632 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f635 Private Use-F635 | f634 Private Use-F634 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f637 Private Use-F637 | f636 Private Use-F636 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f639 Private Use-F639 | f638 Private Use-F638 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63b Private Use-F63B | f63a Private Use-F63A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63d Private Use-F63D | f63c Private Use-F63C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f63f Private Use-F63F | f63e Private Use-F63E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f641 Private Use-F641 | f640 Private Use-F640 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f643 Private Use-F643 | f642 Private Use-F642 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f645 Private Use-F645 | f644 Private Use-F644 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f647 Private Use-F647 | f646 Private Use-F646 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f649 Private Use-F649 | f648 Private Use-F648 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64b Private Use-F64B | f64a Private Use-F64A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64d Private Use-F64D | f64c Private Use-F64C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f64f Private Use-F64F | f64e Private Use-F64E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f651 Private Use-F651 | f650 Private Use-F650 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f653 Private Use-F653 | f652 Private Use-F652 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f655 Private Use-F655 | f654 Private Use-F654 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f657 Private Use-F657 | f656 Private Use-F656 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f659 Private Use-F659 | f658 Private Use-F658 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65b Private Use-F65B | f65a Private Use-F65A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65d Private Use-F65D | f65c Private Use-F65C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f65f Private Use-F65F | f65e Private Use-F65E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f661 Private Use-F661 | f660 Private Use-F660 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f663 Private Use-F663 | f662 Private Use-F662 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f665 Private Use-F665 | f664 Private Use-F664 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f667 Private Use-F667 | f666 Private Use-F666 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f669 Private Use-F669 | f668 Private Use-F668 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66b Private Use-F66B | f66a Private Use-F66A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66d Private Use-F66D | f66c Private Use-F66C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f66f Private Use-F66F | f66e Private Use-F66E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f671 Private Use-F671 | f670 Private Use-F670 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f673 Private Use-F673 | f672 Private Use-F672 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f675 Private Use-F675 | f674 Private Use-F674 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f677 Private Use-F677 | f676 Private Use-F676 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f679 Private Use-F679 | f678 Private Use-F678 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67b Private Use-F67B | f67a Private Use-F67A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67d Private Use-F67D | f67c Private Use-F67C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f67f Private Use-F67F | f67e Private Use-F67E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f681 Private Use-F681 | f680 Private Use-F680 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f683 Private Use-F683 | f682 Private Use-F682 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f685 Private Use-F685 | f684 Private Use-F684 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f687 Private Use-F687 | f686 Private Use-F686 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f689 Private Use-F689 | f688 Private Use-F688 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68b Private Use-F68B | f68a Private Use-F68A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68d Private Use-F68D | f68c Private Use-F68C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f68f Private Use-F68F | f68e Private Use-F68E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f691 Private Use-F691 | f690 Private Use-F690 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f693 Private Use-F693 | f692 Private Use-F692 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f695 Private Use-F695 | f694 Private Use-F694 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f697 Private Use-F697 | f696 Private Use-F696 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f699 Private Use-F699 | f698 Private Use-F698 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69b Private Use-F69B | f69a Private Use-F69A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69d Private Use-F69D | f69c Private Use-F69C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f69f Private Use-F69F | f69e Private Use-F69E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a1 Private Use-F6A1 | f6a0 Private Use-F6A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a3 Private Use-F6A3 | f6a2 Private Use-F6A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a5 Private Use-F6A5 | f6a4 Private Use-F6A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a7 Private Use-F6A7 | f6a6 Private Use-F6A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6a9 Private Use-F6A9 | f6a8 Private Use-F6A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ab Private Use-F6AB | f6aa Private Use-F6AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ad Private Use-F6AD | f6ac Private Use-F6AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6af Private Use-F6AF | f6ae Private Use-F6AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b1 Private Use-F6B1 | f6b0 Private Use-F6B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b3 Private Use-F6B3 | f6b2 Private Use-F6B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b5 Private Use-F6B5 | f6b4 Private Use-F6B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b7 Private Use-F6B7 | f6b6 Private Use-F6B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6b9 Private Use-F6B9 | f6b8 Private Use-F6B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bb Private Use-F6BB | f6ba Private Use-F6BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bd Private Use-F6BD | f6bc Private Use-F6BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6bf Private Use-F6BF | f6be Private Use-F6BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c1 Private Use-F6C1 | f6c0 Private Use-F6C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c3 Private Use-F6C3 | f6c2 Private Use-F6C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c5 Private Use-F6C5 | f6c4 Private Use-F6C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c7 Private Use-F6C7 | f6c6 Private Use-F6C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6c9 Private Use-F6C9 | f6c8 Private Use-F6C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cb Private Use-F6CB | f6ca Private Use-F6CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cd Private Use-F6CD | f6cc Private Use-F6CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6cf Private Use-F6CF | f6ce Private Use-F6CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d1 Private Use-F6D1 | f6d0 Private Use-F6D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d3 Private Use-F6D3 | f6d2 Private Use-F6D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d5 Private Use-F6D5 | f6d4 Private Use-F6D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d7 Private Use-F6D7 | f6d6 Private Use-F6D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6d9 Private Use-F6D9 | f6d8 Private Use-F6D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6db Private Use-F6DB | f6da Private Use-F6DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6dd Private Use-F6DD | f6dc Private Use-F6DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6df Private Use-F6DF | f6de Private Use-F6DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e1 Private Use-F6E1 | f6e0 Private Use-F6E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e3 Private Use-F6E3 | f6e2 Private Use-F6E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e5 Private Use-F6E5 | f6e4 Private Use-F6E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e7 Private Use-F6E7 | f6e6 Private Use-F6E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6e9 Private Use-F6E9 | f6e8 Private Use-F6E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6eb Private Use-F6EB | f6ea Private Use-F6EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ed Private Use-F6ED | f6ec Private Use-F6EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ef Private Use-F6EF | f6ee Private Use-F6EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f1 Private Use-F6F1 | f6f0 Private Use-F6F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f3 Private Use-F6F3 | f6f2 Private Use-F6F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f5 Private Use-F6F5 | f6f4 Private Use-F6F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f7 Private Use-F6F7 | f6f6 Private Use-F6F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6f9 Private Use-F6F9 | f6f8 Private Use-F6F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6fb Private Use-F6FB | f6fa Private Use-F6FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6fd Private Use-F6FD | f6fc Private Use-F6FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f6ff Private Use-F6FF | f6fe Private Use-F6FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f701 Private Use-F701 | f700 Private Use-F700 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f703 Private Use-F703 | f702 Private Use-F702 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f705 Private Use-F705 | f704 Private Use-F704 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f707 Private Use-F707 | f706 Private Use-F706 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f709 Private Use-F709 | f708 Private Use-F708 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70b Private Use-F70B | f70a Private Use-F70A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70d Private Use-F70D | f70c Private Use-F70C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f70f Private Use-F70F | f70e Private Use-F70E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f711 Private Use-F711 | f710 Private Use-F710 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f713 Private Use-F713 | f712 Private Use-F712 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f715 Private Use-F715 | f714 Private Use-F714 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f717 Private Use-F717 | f716 Private Use-F716 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f719 Private Use-F719 | f718 Private Use-F718 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71b Private Use-F71B | f71a Private Use-F71A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71d Private Use-F71D | f71c Private Use-F71C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f71f Private Use-F71F | f71e Private Use-F71E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f721 Private Use-F721 | f720 Private Use-F720 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f723 Private Use-F723 | f722 Private Use-F722 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f725 Private Use-F725 | f724 Private Use-F724 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f727 Private Use-F727 | f726 Private Use-F726 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f729 Private Use-F729 | f728 Private Use-F728 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72b Private Use-F72B | f72a Private Use-F72A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72d Private Use-F72D | f72c Private Use-F72C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f72f Private Use-F72F | f72e Private Use-F72E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f731 Private Use-F731 | f730 Private Use-F730 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f733 Private Use-F733 | f732 Private Use-F732 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f735 Private Use-F735 | f734 Private Use-F734 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f737 Private Use-F737 | f736 Private Use-F736 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f739 Private Use-F739 | f738 Private Use-F738 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73b Private Use-F73B | f73a Private Use-F73A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73d Private Use-F73D | f73c Private Use-F73C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f73f Private Use-F73F | f73e Private Use-F73E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f741 Private Use-F741 | f740 Private Use-F740 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f743 Private Use-F743 | f742 Private Use-F742 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f745 Private Use-F745 | f744 Private Use-F744 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f747 Private Use-F747 | f746 Private Use-F746 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f749 Private Use-F749 | f748 Private Use-F748 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74b Private Use-F74B | f74a Private Use-F74A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74d Private Use-F74D | f74c Private Use-F74C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f74f Private Use-F74F | f74e Private Use-F74E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f751 Private Use-F751 | f750 Private Use-F750 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f753 Private Use-F753 | f752 Private Use-F752 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f755 Private Use-F755 | f754 Private Use-F754 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f757 Private Use-F757 | f756 Private Use-F756 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f759 Private Use-F759 | f758 Private Use-F758 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75b Private Use-F75B | f75a Private Use-F75A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75d Private Use-F75D | f75c Private Use-F75C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f75f Private Use-F75F | f75e Private Use-F75E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f761 Private Use-F761 | f760 Private Use-F760 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f763 Private Use-F763 | f762 Private Use-F762 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f765 Private Use-F765 | f764 Private Use-F764 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f767 Private Use-F767 | f766 Private Use-F766 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f769 Private Use-F769 | f768 Private Use-F768 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76b Private Use-F76B | f76a Private Use-F76A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76d Private Use-F76D | f76c Private Use-F76C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f76f Private Use-F76F | f76e Private Use-F76E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f771 Private Use-F771 | f770 Private Use-F770 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f773 Private Use-F773 | f772 Private Use-F772 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f775 Private Use-F775 | f774 Private Use-F774 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f777 Private Use-F777 | f776 Private Use-F776 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f779 Private Use-F779 | f778 Private Use-F778 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77b Private Use-F77B | f77a Private Use-F77A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77d Private Use-F77D | f77c Private Use-F77C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f77f Private Use-F77F | f77e Private Use-F77E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f781 Private Use-F781 | f780 Private Use-F780 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f783 Private Use-F783 | f782 Private Use-F782 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f785 Private Use-F785 | f784 Private Use-F784 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f787 Private Use-F787 | f786 Private Use-F786 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f789 Private Use-F789 | f788 Private Use-F788 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78b Private Use-F78B | f78a Private Use-F78A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78d Private Use-F78D | f78c Private Use-F78C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f78f Private Use-F78F | f78e Private Use-F78E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f791 Private Use-F791 | f790 Private Use-F790 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f793 Private Use-F793 | f792 Private Use-F792 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f795 Private Use-F795 | f794 Private Use-F794 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f797 Private Use-F797 | f796 Private Use-F796 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f799 Private Use-F799 | f798 Private Use-F798 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79b Private Use-F79B | f79a Private Use-F79A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79d Private Use-F79D | f79c Private Use-F79C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f79f Private Use-F79F | f79e Private Use-F79E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a1 Private Use-F7A1 | f7a0 Private Use-F7A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a3 Private Use-F7A3 | f7a2 Private Use-F7A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a5 Private Use-F7A5 | f7a4 Private Use-F7A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a7 Private Use-F7A7 | f7a6 Private Use-F7A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7a9 Private Use-F7A9 | f7a8 Private Use-F7A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ab Private Use-F7AB | f7aa Private Use-F7AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ad Private Use-F7AD | f7ac Private Use-F7AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7af Private Use-F7AF | f7ae Private Use-F7AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b1 Private Use-F7B1 | f7b0 Private Use-F7B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b3 Private Use-F7B3 | f7b2 Private Use-F7B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b5 Private Use-F7B5 | f7b4 Private Use-F7B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b7 Private Use-F7B7 | f7b6 Private Use-F7B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7b9 Private Use-F7B9 | f7b8 Private Use-F7B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bb Private Use-F7BB | f7ba Private Use-F7BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bd Private Use-F7BD | f7bc Private Use-F7BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7bf Private Use-F7BF | f7be Private Use-F7BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c1 Private Use-F7C1 | f7c0 Private Use-F7C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c3 Private Use-F7C3 | f7c2 Private Use-F7C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c5 Private Use-F7C5 | f7c4 Private Use-F7C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c7 Private Use-F7C7 | f7c6 Private Use-F7C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7c9 Private Use-F7C9 | f7c8 Private Use-F7C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cb Private Use-F7CB | f7ca Private Use-F7CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cd Private Use-F7CD | f7cc Private Use-F7CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7cf Private Use-F7CF | f7ce Private Use-F7CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d1 Private Use-F7D1 | f7d0 Private Use-F7D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d3 Private Use-F7D3 | f7d2 Private Use-F7D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d5 Private Use-F7D5 | f7d4 Private Use-F7D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d7 Private Use-F7D7 | f7d6 Private Use-F7D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7d9 Private Use-F7D9 | f7d8 Private Use-F7D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7db Private Use-F7DB | f7da Private Use-F7DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7dd Private Use-F7DD | f7dc Private Use-F7DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7df Private Use-F7DF | f7de Private Use-F7DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e1 Private Use-F7E1 | f7e0 Private Use-F7E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e3 Private Use-F7E3 | f7e2 Private Use-F7E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e5 Private Use-F7E5 | f7e4 Private Use-F7E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e7 Private Use-F7E7 | f7e6 Private Use-F7E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7e9 Private Use-F7E9 | f7e8 Private Use-F7E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7eb Private Use-F7EB | f7ea Private Use-F7EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ed Private Use-F7ED | f7ec Private Use-F7EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ef Private Use-F7EF | f7ee Private Use-F7EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f1 Private Use-F7F1 | f7f0 Private Use-F7F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f3 Private Use-F7F3 | f7f2 Private Use-F7F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f5 Private Use-F7F5 | f7f4 Private Use-F7F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f7 Private Use-F7F7 | f7f6 Private Use-F7F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7f9 Private Use-F7F9 | f7f8 Private Use-F7F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7fb Private Use-F7FB | f7fa Private Use-F7FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7fd Private Use-F7FD | f7fc Private Use-F7FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f7ff Private Use-F7FF | f7fe Private Use-F7FE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f801 Private Use-F801 | f800 Private Use-F800 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f803 Private Use-F803 | f802 Private Use-F802 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f805 Private Use-F805 | f804 Private Use-F804 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f807 Private Use-F807 | f806 Private Use-F806 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f809 Private Use-F809 | f808 Private Use-F808 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80b Private Use-F80B | f80a Private Use-F80A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80d Private Use-F80D | f80c Private Use-F80C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f80f Private Use-F80F | f80e Private Use-F80E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f811 Private Use-F811 | f810 Private Use-F810 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f813 Private Use-F813 | f812 Private Use-F812 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f815 Private Use-F815 | f814 Private Use-F814 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f817 Private Use-F817 | f816 Private Use-F816 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f819 Private Use-F819 | f818 Private Use-F818 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81b Private Use-F81B | f81a Private Use-F81A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81d Private Use-F81D | f81c Private Use-F81C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f81f Private Use-F81F | f81e Private Use-F81E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f821 Private Use-F821 | f820 Private Use-F820 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f823 Private Use-F823 | f822 Private Use-F822 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f825 Private Use-F825 | f824 Private Use-F824 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f827 Private Use-F827 | f826 Private Use-F826 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f829 Private Use-F829 | f828 Private Use-F828 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82b Private Use-F82B | f82a Private Use-F82A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82d Private Use-F82D | f82c Private Use-F82C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f82f Private Use-F82F | f82e Private Use-F82E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f831 Private Use-F831 | f830 Private Use-F830 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f833 Private Use-F833 | f832 Private Use-F832 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f835 Private Use-F835 | f834 Private Use-F834 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f837 Private Use-F837 | f836 Private Use-F836 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f839 Private Use-F839 | f838 Private Use-F838 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83b Private Use-F83B | f83a Private Use-F83A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83d Private Use-F83D | f83c Private Use-F83C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f83f Private Use-F83F | f83e Private Use-F83E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f841 Private Use-F841 | f840 Private Use-F840 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f843 Private Use-F843 | f842 Private Use-F842 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f845 Private Use-F845 | f844 Private Use-F844 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f847 Private Use-F847 | f846 Private Use-F846 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f849 Private Use-F849 | f848 Private Use-F848 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84b Private Use-F84B | f84a Private Use-F84A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84d Private Use-F84D | f84c Private Use-F84C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f84f Private Use-F84F | f84e Private Use-F84E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f851 Private Use-F851 | f850 Private Use-F850 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f853 Private Use-F853 | f852 Private Use-F852 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f855 Private Use-F855 | f854 Private Use-F854 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f857 Private Use-F857 | f856 Private Use-F856 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f859 Private Use-F859 | f858 Private Use-F858 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85b Private Use-F85B | f85a Private Use-F85A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85d Private Use-F85D | f85c Private Use-F85C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f85f Private Use-F85F | f85e Private Use-F85E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f861 Private Use-F861 | f860 Private Use-F860 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f863 Private Use-F863 | f862 Private Use-F862 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f865 Private Use-F865 | f864 Private Use-F864 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f867 Private Use-F867 | f866 Private Use-F866 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f869 Private Use-F869 | f868 Private Use-F868 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86b Private Use-F86B | f86a Private Use-F86A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86d Private Use-F86D | f86c Private Use-F86C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f86f Private Use-F86F | f86e Private Use-F86E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f871 Private Use-F871 | f870 Private Use-F870 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f873 Private Use-F873 | f872 Private Use-F872 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f875 Private Use-F875 | f874 Private Use-F874 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f877 Private Use-F877 | f876 Private Use-F876 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f879 Private Use-F879 | f878 Private Use-F878 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87b Private Use-F87B | f87a Private Use-F87A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87d Private Use-F87D | f87c Private Use-F87C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f87f Private Use-F87F | f87e Private Use-F87E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f881 Private Use-F881 | f880 Private Use-F880 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f883 Private Use-F883 | f882 Private Use-F882 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f885 Private Use-F885 | f884 Private Use-F884 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f887 Private Use-F887 | f886 Private Use-F886 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f889 Private Use-F889 | f888 Private Use-F888 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88b Private Use-F88B | f88a Private Use-F88A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88d Private Use-F88D | f88c Private Use-F88C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f88f Private Use-F88F | f88e Private Use-F88E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f891 Private Use-F891 | f890 Private Use-F890 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f893 Private Use-F893 | f892 Private Use-F892 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f895 Private Use-F895 | f894 Private Use-F894 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f897 Private Use-F897 | f896 Private Use-F896 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f899 Private Use-F899 | f898 Private Use-F898 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89b Private Use-F89B | f89a Private Use-F89A */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89d Private Use-F89D | f89c Private Use-F89C */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f89f Private Use-F89F | f89e Private Use-F89E */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a1 Private Use-F8A1 | f8a0 Private Use-F8A0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a3 Private Use-F8A3 | f8a2 Private Use-F8A2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a5 Private Use-F8A5 | f8a4 Private Use-F8A4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a7 Private Use-F8A7 | f8a6 Private Use-F8A6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8a9 Private Use-F8A9 | f8a8 Private Use-F8A8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ab Private Use-F8AB | f8aa Private Use-F8AA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ad Private Use-F8AD | f8ac Private Use-F8AC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8af Private Use-F8AF | f8ae Private Use-F8AE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b1 Private Use-F8B1 | f8b0 Private Use-F8B0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b3 Private Use-F8B3 | f8b2 Private Use-F8B2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b5 Private Use-F8B5 | f8b4 Private Use-F8B4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b7 Private Use-F8B7 | f8b6 Private Use-F8B6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8b9 Private Use-F8B9 | f8b8 Private Use-F8B8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bb Private Use-F8BB | f8ba Private Use-F8BA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bd Private Use-F8BD | f8bc Private Use-F8BC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8bf Private Use-F8BF | f8be Private Use-F8BE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c1 Private Use-F8C1 | f8c0 Private Use-F8C0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c3 Private Use-F8C3 | f8c2 Private Use-F8C2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c5 Private Use-F8C5 | f8c4 Private Use-F8C4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c7 Private Use-F8C7 | f8c6 Private Use-F8C6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8c9 Private Use-F8C9 | f8c8 Private Use-F8C8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cb Private Use-F8CB | f8ca Private Use-F8CA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cd Private Use-F8CD | f8cc Private Use-F8CC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8cf Private Use-F8CF | f8ce Private Use-F8CE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d1 Private Use-F8D1 | f8d0 Private Use-F8D0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d3 Private Use-F8D3 | f8d2 Private Use-F8D2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d5 Private Use-F8D5 | f8d4 Private Use-F8D4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d7 Private Use-F8D7 | f8d6 Private Use-F8D6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8d9 Private Use-F8D9 | f8d8 Private Use-F8D8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8db Private Use-F8DB | f8da Private Use-F8DA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8dd Private Use-F8DD | f8dc Private Use-F8DC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8df Private Use-F8DF | f8de Private Use-F8DE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e1 Private Use-F8E1 | f8e0 Private Use-F8E0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e3 Private Use-F8E3 | f8e2 Private Use-F8E2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e5 Private Use-F8E5 | f8e4 Private Use-F8E4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e7 Private Use-F8E7 | f8e6 Private Use-F8E6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8e9 Private Use-F8E9 | f8e8 Private Use-F8E8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8eb Private Use-F8EB | f8ea Private Use-F8EA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ed Private Use-F8ED | f8ec Private Use-F8EC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ef Private Use-F8EF | f8ee Private Use-F8EE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f1 Private Use-F8F1 | f8f0 Private Use-F8F0 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f3 Private Use-F8F3 | f8f2 Private Use-F8F2 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f5 Private Use-F8F5 | f8f4 Private Use-F8F4 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f7 Private Use-F8F7 | f8f6 Private Use-F8F6 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8f9 Private Use-F8F9 | f8f8 Private Use-F8F8 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8fb Private Use-F8FB | f8fa Private Use-F8FA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8fd Private Use-F8FD | f8fc Private Use-F8FC */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* f8ff Private Use-F8FF | f8fe Private Use-F8FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f901 CJK COMPATIBILITY IDEOGRAPH-F901 | f900 CJK COMPATIBILITY IDEOGRAPH-F900 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f903 CJK COMPATIBILITY IDEOGRAPH-F903 | f902 CJK COMPATIBILITY IDEOGRAPH-F902 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f905 CJK COMPATIBILITY IDEOGRAPH-F905 | f904 CJK COMPATIBILITY IDEOGRAPH-F904 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f907 CJK COMPATIBILITY IDEOGRAPH-F907 | f906 CJK COMPATIBILITY IDEOGRAPH-F906 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f909 CJK COMPATIBILITY IDEOGRAPH-F909 | f908 CJK COMPATIBILITY IDEOGRAPH-F908 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90b CJK COMPATIBILITY IDEOGRAPH-F90B | f90a CJK COMPATIBILITY IDEOGRAPH-F90A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90d CJK COMPATIBILITY IDEOGRAPH-F90D | f90c CJK COMPATIBILITY IDEOGRAPH-F90C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f90f CJK COMPATIBILITY IDEOGRAPH-F90F | f90e CJK COMPATIBILITY IDEOGRAPH-F90E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f911 CJK COMPATIBILITY IDEOGRAPH-F911 | f910 CJK COMPATIBILITY IDEOGRAPH-F910 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f913 CJK COMPATIBILITY IDEOGRAPH-F913 | f912 CJK COMPATIBILITY IDEOGRAPH-F912 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f915 CJK COMPATIBILITY IDEOGRAPH-F915 | f914 CJK COMPATIBILITY IDEOGRAPH-F914 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f917 CJK COMPATIBILITY IDEOGRAPH-F917 | f916 CJK COMPATIBILITY IDEOGRAPH-F916 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f919 CJK COMPATIBILITY IDEOGRAPH-F919 | f918 CJK COMPATIBILITY IDEOGRAPH-F918 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91b CJK COMPATIBILITY IDEOGRAPH-F91B | f91a CJK COMPATIBILITY IDEOGRAPH-F91A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91d CJK COMPATIBILITY IDEOGRAPH-F91D | f91c CJK COMPATIBILITY IDEOGRAPH-F91C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f91f CJK COMPATIBILITY IDEOGRAPH-F91F | f91e CJK COMPATIBILITY IDEOGRAPH-F91E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f921 CJK COMPATIBILITY IDEOGRAPH-F921 | f920 CJK COMPATIBILITY IDEOGRAPH-F920 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f923 CJK COMPATIBILITY IDEOGRAPH-F923 | f922 CJK COMPATIBILITY IDEOGRAPH-F922 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f925 CJK COMPATIBILITY IDEOGRAPH-F925 | f924 CJK COMPATIBILITY IDEOGRAPH-F924 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f927 CJK COMPATIBILITY IDEOGRAPH-F927 | f926 CJK COMPATIBILITY IDEOGRAPH-F926 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f929 CJK COMPATIBILITY IDEOGRAPH-F929 | f928 CJK COMPATIBILITY IDEOGRAPH-F928 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92b CJK COMPATIBILITY IDEOGRAPH-F92B | f92a CJK COMPATIBILITY IDEOGRAPH-F92A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92d CJK COMPATIBILITY IDEOGRAPH-F92D | f92c CJK COMPATIBILITY IDEOGRAPH-F92C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f92f CJK COMPATIBILITY IDEOGRAPH-F92F | f92e CJK COMPATIBILITY IDEOGRAPH-F92E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f931 CJK COMPATIBILITY IDEOGRAPH-F931 | f930 CJK COMPATIBILITY IDEOGRAPH-F930 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f933 CJK COMPATIBILITY IDEOGRAPH-F933 | f932 CJK COMPATIBILITY IDEOGRAPH-F932 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f935 CJK COMPATIBILITY IDEOGRAPH-F935 | f934 CJK COMPATIBILITY IDEOGRAPH-F934 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f937 CJK COMPATIBILITY IDEOGRAPH-F937 | f936 CJK COMPATIBILITY IDEOGRAPH-F936 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f939 CJK COMPATIBILITY IDEOGRAPH-F939 | f938 CJK COMPATIBILITY IDEOGRAPH-F938 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93b CJK COMPATIBILITY IDEOGRAPH-F93B | f93a CJK COMPATIBILITY IDEOGRAPH-F93A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93d CJK COMPATIBILITY IDEOGRAPH-F93D | f93c CJK COMPATIBILITY IDEOGRAPH-F93C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f93f CJK COMPATIBILITY IDEOGRAPH-F93F | f93e CJK COMPATIBILITY IDEOGRAPH-F93E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f941 CJK COMPATIBILITY IDEOGRAPH-F941 | f940 CJK COMPATIBILITY IDEOGRAPH-F940 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f943 CJK COMPATIBILITY IDEOGRAPH-F943 | f942 CJK COMPATIBILITY IDEOGRAPH-F942 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f945 CJK COMPATIBILITY IDEOGRAPH-F945 | f944 CJK COMPATIBILITY IDEOGRAPH-F944 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f947 CJK COMPATIBILITY IDEOGRAPH-F947 | f946 CJK COMPATIBILITY IDEOGRAPH-F946 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f949 CJK COMPATIBILITY IDEOGRAPH-F949 | f948 CJK COMPATIBILITY IDEOGRAPH-F948 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94b CJK COMPATIBILITY IDEOGRAPH-F94B | f94a CJK COMPATIBILITY IDEOGRAPH-F94A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94d CJK COMPATIBILITY IDEOGRAPH-F94D | f94c CJK COMPATIBILITY IDEOGRAPH-F94C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f94f CJK COMPATIBILITY IDEOGRAPH-F94F | f94e CJK COMPATIBILITY IDEOGRAPH-F94E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f951 CJK COMPATIBILITY IDEOGRAPH-F951 | f950 CJK COMPATIBILITY IDEOGRAPH-F950 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f953 CJK COMPATIBILITY IDEOGRAPH-F953 | f952 CJK COMPATIBILITY IDEOGRAPH-F952 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f955 CJK COMPATIBILITY IDEOGRAPH-F955 | f954 CJK COMPATIBILITY IDEOGRAPH-F954 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f957 CJK COMPATIBILITY IDEOGRAPH-F957 | f956 CJK COMPATIBILITY IDEOGRAPH-F956 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f959 CJK COMPATIBILITY IDEOGRAPH-F959 | f958 CJK COMPATIBILITY IDEOGRAPH-F958 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95b CJK COMPATIBILITY IDEOGRAPH-F95B | f95a CJK COMPATIBILITY IDEOGRAPH-F95A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95d CJK COMPATIBILITY IDEOGRAPH-F95D | f95c CJK COMPATIBILITY IDEOGRAPH-F95C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f95f CJK COMPATIBILITY IDEOGRAPH-F95F | f95e CJK COMPATIBILITY IDEOGRAPH-F95E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f961 CJK COMPATIBILITY IDEOGRAPH-F961 | f960 CJK COMPATIBILITY IDEOGRAPH-F960 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f963 CJK COMPATIBILITY IDEOGRAPH-F963 | f962 CJK COMPATIBILITY IDEOGRAPH-F962 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f965 CJK COMPATIBILITY IDEOGRAPH-F965 | f964 CJK COMPATIBILITY IDEOGRAPH-F964 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f967 CJK COMPATIBILITY IDEOGRAPH-F967 | f966 CJK COMPATIBILITY IDEOGRAPH-F966 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f969 CJK COMPATIBILITY IDEOGRAPH-F969 | f968 CJK COMPATIBILITY IDEOGRAPH-F968 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96b CJK COMPATIBILITY IDEOGRAPH-F96B | f96a CJK COMPATIBILITY IDEOGRAPH-F96A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96d CJK COMPATIBILITY IDEOGRAPH-F96D | f96c CJK COMPATIBILITY IDEOGRAPH-F96C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f96f CJK COMPATIBILITY IDEOGRAPH-F96F | f96e CJK COMPATIBILITY IDEOGRAPH-F96E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f971 CJK COMPATIBILITY IDEOGRAPH-F971 | f970 CJK COMPATIBILITY IDEOGRAPH-F970 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f973 CJK COMPATIBILITY IDEOGRAPH-F973 | f972 CJK COMPATIBILITY IDEOGRAPH-F972 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f975 CJK COMPATIBILITY IDEOGRAPH-F975 | f974 CJK COMPATIBILITY IDEOGRAPH-F974 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f977 CJK COMPATIBILITY IDEOGRAPH-F977 | f976 CJK COMPATIBILITY IDEOGRAPH-F976 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f979 CJK COMPATIBILITY IDEOGRAPH-F979 | f978 CJK COMPATIBILITY IDEOGRAPH-F978 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97b CJK COMPATIBILITY IDEOGRAPH-F97B | f97a CJK COMPATIBILITY IDEOGRAPH-F97A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97d CJK COMPATIBILITY IDEOGRAPH-F97D | f97c CJK COMPATIBILITY IDEOGRAPH-F97C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f97f CJK COMPATIBILITY IDEOGRAPH-F97F | f97e CJK COMPATIBILITY IDEOGRAPH-F97E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f981 CJK COMPATIBILITY IDEOGRAPH-F981 | f980 CJK COMPATIBILITY IDEOGRAPH-F980 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f983 CJK COMPATIBILITY IDEOGRAPH-F983 | f982 CJK COMPATIBILITY IDEOGRAPH-F982 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f985 CJK COMPATIBILITY IDEOGRAPH-F985 | f984 CJK COMPATIBILITY IDEOGRAPH-F984 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f987 CJK COMPATIBILITY IDEOGRAPH-F987 | f986 CJK COMPATIBILITY IDEOGRAPH-F986 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f989 CJK COMPATIBILITY IDEOGRAPH-F989 | f988 CJK COMPATIBILITY IDEOGRAPH-F988 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98b CJK COMPATIBILITY IDEOGRAPH-F98B | f98a CJK COMPATIBILITY IDEOGRAPH-F98A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98d CJK COMPATIBILITY IDEOGRAPH-F98D | f98c CJK COMPATIBILITY IDEOGRAPH-F98C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f98f CJK COMPATIBILITY IDEOGRAPH-F98F | f98e CJK COMPATIBILITY IDEOGRAPH-F98E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f991 CJK COMPATIBILITY IDEOGRAPH-F991 | f990 CJK COMPATIBILITY IDEOGRAPH-F990 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f993 CJK COMPATIBILITY IDEOGRAPH-F993 | f992 CJK COMPATIBILITY IDEOGRAPH-F992 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f995 CJK COMPATIBILITY IDEOGRAPH-F995 | f994 CJK COMPATIBILITY IDEOGRAPH-F994 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f997 CJK COMPATIBILITY IDEOGRAPH-F997 | f996 CJK COMPATIBILITY IDEOGRAPH-F996 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f999 CJK COMPATIBILITY IDEOGRAPH-F999 | f998 CJK COMPATIBILITY IDEOGRAPH-F998 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99b CJK COMPATIBILITY IDEOGRAPH-F99B | f99a CJK COMPATIBILITY IDEOGRAPH-F99A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99d CJK COMPATIBILITY IDEOGRAPH-F99D | f99c CJK COMPATIBILITY IDEOGRAPH-F99C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f99f CJK COMPATIBILITY IDEOGRAPH-F99F | f99e CJK COMPATIBILITY IDEOGRAPH-F99E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a1 CJK COMPATIBILITY IDEOGRAPH-F9A1 | f9a0 CJK COMPATIBILITY IDEOGRAPH-F9A0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a3 CJK COMPATIBILITY IDEOGRAPH-F9A3 | f9a2 CJK COMPATIBILITY IDEOGRAPH-F9A2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a5 CJK COMPATIBILITY IDEOGRAPH-F9A5 | f9a4 CJK COMPATIBILITY IDEOGRAPH-F9A4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a7 CJK COMPATIBILITY IDEOGRAPH-F9A7 | f9a6 CJK COMPATIBILITY IDEOGRAPH-F9A6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9a9 CJK COMPATIBILITY IDEOGRAPH-F9A9 | f9a8 CJK COMPATIBILITY IDEOGRAPH-F9A8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ab CJK COMPATIBILITY IDEOGRAPH-F9AB | f9aa CJK COMPATIBILITY IDEOGRAPH-F9AA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ad CJK COMPATIBILITY IDEOGRAPH-F9AD | f9ac CJK COMPATIBILITY IDEOGRAPH-F9AC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9af CJK COMPATIBILITY IDEOGRAPH-F9AF | f9ae CJK COMPATIBILITY IDEOGRAPH-F9AE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b1 CJK COMPATIBILITY IDEOGRAPH-F9B1 | f9b0 CJK COMPATIBILITY IDEOGRAPH-F9B0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b3 CJK COMPATIBILITY IDEOGRAPH-F9B3 | f9b2 CJK COMPATIBILITY IDEOGRAPH-F9B2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b5 CJK COMPATIBILITY IDEOGRAPH-F9B5 | f9b4 CJK COMPATIBILITY IDEOGRAPH-F9B4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b7 CJK COMPATIBILITY IDEOGRAPH-F9B7 | f9b6 CJK COMPATIBILITY IDEOGRAPH-F9B6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9b9 CJK COMPATIBILITY IDEOGRAPH-F9B9 | f9b8 CJK COMPATIBILITY IDEOGRAPH-F9B8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bb CJK COMPATIBILITY IDEOGRAPH-F9BB | f9ba CJK COMPATIBILITY IDEOGRAPH-F9BA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bd CJK COMPATIBILITY IDEOGRAPH-F9BD | f9bc CJK COMPATIBILITY IDEOGRAPH-F9BC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9bf CJK COMPATIBILITY IDEOGRAPH-F9BF | f9be CJK COMPATIBILITY IDEOGRAPH-F9BE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c1 CJK COMPATIBILITY IDEOGRAPH-F9C1 | f9c0 CJK COMPATIBILITY IDEOGRAPH-F9C0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c3 CJK COMPATIBILITY IDEOGRAPH-F9C3 | f9c2 CJK COMPATIBILITY IDEOGRAPH-F9C2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c5 CJK COMPATIBILITY IDEOGRAPH-F9C5 | f9c4 CJK COMPATIBILITY IDEOGRAPH-F9C4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c7 CJK COMPATIBILITY IDEOGRAPH-F9C7 | f9c6 CJK COMPATIBILITY IDEOGRAPH-F9C6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9c9 CJK COMPATIBILITY IDEOGRAPH-F9C9 | f9c8 CJK COMPATIBILITY IDEOGRAPH-F9C8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cb CJK COMPATIBILITY IDEOGRAPH-F9CB | f9ca CJK COMPATIBILITY IDEOGRAPH-F9CA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cd CJK COMPATIBILITY IDEOGRAPH-F9CD | f9cc CJK COMPATIBILITY IDEOGRAPH-F9CC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9cf CJK COMPATIBILITY IDEOGRAPH-F9CF | f9ce CJK COMPATIBILITY IDEOGRAPH-F9CE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d1 CJK COMPATIBILITY IDEOGRAPH-F9D1 | f9d0 CJK COMPATIBILITY IDEOGRAPH-F9D0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d3 CJK COMPATIBILITY IDEOGRAPH-F9D3 | f9d2 CJK COMPATIBILITY IDEOGRAPH-F9D2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d5 CJK COMPATIBILITY IDEOGRAPH-F9D5 | f9d4 CJK COMPATIBILITY IDEOGRAPH-F9D4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d7 CJK COMPATIBILITY IDEOGRAPH-F9D7 | f9d6 CJK COMPATIBILITY IDEOGRAPH-F9D6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9d9 CJK COMPATIBILITY IDEOGRAPH-F9D9 | f9d8 CJK COMPATIBILITY IDEOGRAPH-F9D8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9db CJK COMPATIBILITY IDEOGRAPH-F9DB | f9da CJK COMPATIBILITY IDEOGRAPH-F9DA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9dd CJK COMPATIBILITY IDEOGRAPH-F9DD | f9dc CJK COMPATIBILITY IDEOGRAPH-F9DC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9df CJK COMPATIBILITY IDEOGRAPH-F9DF | f9de CJK COMPATIBILITY IDEOGRAPH-F9DE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e1 CJK COMPATIBILITY IDEOGRAPH-F9E1 | f9e0 CJK COMPATIBILITY IDEOGRAPH-F9E0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e3 CJK COMPATIBILITY IDEOGRAPH-F9E3 | f9e2 CJK COMPATIBILITY IDEOGRAPH-F9E2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e5 CJK COMPATIBILITY IDEOGRAPH-F9E5 | f9e4 CJK COMPATIBILITY IDEOGRAPH-F9E4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e7 CJK COMPATIBILITY IDEOGRAPH-F9E7 | f9e6 CJK COMPATIBILITY IDEOGRAPH-F9E6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9e9 CJK COMPATIBILITY IDEOGRAPH-F9E9 | f9e8 CJK COMPATIBILITY IDEOGRAPH-F9E8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9eb CJK COMPATIBILITY IDEOGRAPH-F9EB | f9ea CJK COMPATIBILITY IDEOGRAPH-F9EA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ed CJK COMPATIBILITY IDEOGRAPH-F9ED | f9ec CJK COMPATIBILITY IDEOGRAPH-F9EC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ef CJK COMPATIBILITY IDEOGRAPH-F9EF | f9ee CJK COMPATIBILITY IDEOGRAPH-F9EE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f1 CJK COMPATIBILITY IDEOGRAPH-F9F1 | f9f0 CJK COMPATIBILITY IDEOGRAPH-F9F0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f3 CJK COMPATIBILITY IDEOGRAPH-F9F3 | f9f2 CJK COMPATIBILITY IDEOGRAPH-F9F2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f5 CJK COMPATIBILITY IDEOGRAPH-F9F5 | f9f4 CJK COMPATIBILITY IDEOGRAPH-F9F4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f7 CJK COMPATIBILITY IDEOGRAPH-F9F7 | f9f6 CJK COMPATIBILITY IDEOGRAPH-F9F6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9f9 CJK COMPATIBILITY IDEOGRAPH-F9F9 | f9f8 CJK COMPATIBILITY IDEOGRAPH-F9F8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9fb CJK COMPATIBILITY IDEOGRAPH-F9FB | f9fa CJK COMPATIBILITY IDEOGRAPH-F9FA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9fd CJK COMPATIBILITY IDEOGRAPH-F9FD | f9fc CJK COMPATIBILITY IDEOGRAPH-F9FC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* f9ff CJK COMPATIBILITY IDEOGRAPH-F9FF | f9fe CJK COMPATIBILITY IDEOGRAPH-F9FE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa01 CJK COMPATIBILITY IDEOGRAPH-FA01 | fa00 CJK COMPATIBILITY IDEOGRAPH-FA00 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa03 CJK COMPATIBILITY IDEOGRAPH-FA03 | fa02 CJK COMPATIBILITY IDEOGRAPH-FA02 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa05 CJK COMPATIBILITY IDEOGRAPH-FA05 | fa04 CJK COMPATIBILITY IDEOGRAPH-FA04 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa07 CJK COMPATIBILITY IDEOGRAPH-FA07 | fa06 CJK COMPATIBILITY IDEOGRAPH-FA06 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa09 CJK COMPATIBILITY IDEOGRAPH-FA09 | fa08 CJK COMPATIBILITY IDEOGRAPH-FA08 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0b CJK COMPATIBILITY IDEOGRAPH-FA0B | fa0a CJK COMPATIBILITY IDEOGRAPH-FA0A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0d CJK COMPATIBILITY IDEOGRAPH-FA0D | fa0c CJK COMPATIBILITY IDEOGRAPH-FA0C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa0f CJK COMPATIBILITY IDEOGRAPH-FA0F | fa0e CJK COMPATIBILITY IDEOGRAPH-FA0E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa11 CJK COMPATIBILITY IDEOGRAPH-FA11 | fa10 CJK COMPATIBILITY IDEOGRAPH-FA10 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa13 CJK COMPATIBILITY IDEOGRAPH-FA13 | fa12 CJK COMPATIBILITY IDEOGRAPH-FA12 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa15 CJK COMPATIBILITY IDEOGRAPH-FA15 | fa14 CJK COMPATIBILITY IDEOGRAPH-FA14 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa17 CJK COMPATIBILITY IDEOGRAPH-FA17 | fa16 CJK COMPATIBILITY IDEOGRAPH-FA16 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa19 CJK COMPATIBILITY IDEOGRAPH-FA19 | fa18 CJK COMPATIBILITY IDEOGRAPH-FA18 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1b CJK COMPATIBILITY IDEOGRAPH-FA1B | fa1a CJK COMPATIBILITY IDEOGRAPH-FA1A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1d CJK COMPATIBILITY IDEOGRAPH-FA1D | fa1c CJK COMPATIBILITY IDEOGRAPH-FA1C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa1f CJK COMPATIBILITY IDEOGRAPH-FA1F | fa1e CJK COMPATIBILITY IDEOGRAPH-FA1E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa21 CJK COMPATIBILITY IDEOGRAPH-FA21 | fa20 CJK COMPATIBILITY IDEOGRAPH-FA20 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa23 CJK COMPATIBILITY IDEOGRAPH-FA23 | fa22 CJK COMPATIBILITY IDEOGRAPH-FA22 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa25 CJK COMPATIBILITY IDEOGRAPH-FA25 | fa24 CJK COMPATIBILITY IDEOGRAPH-FA24 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa27 CJK COMPATIBILITY IDEOGRAPH-FA27 | fa26 CJK COMPATIBILITY IDEOGRAPH-FA26 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa29 CJK COMPATIBILITY IDEOGRAPH-FA29 | fa28 CJK COMPATIBILITY IDEOGRAPH-FA28 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2b CJK COMPATIBILITY IDEOGRAPH-FA2B | fa2a CJK COMPATIBILITY IDEOGRAPH-FA2A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2d CJK COMPATIBILITY IDEOGRAPH-FA2D | fa2c CJK COMPATIBILITY IDEOGRAPH-FA2C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa2f CJK COMPATIBILITY IDEOGRAPH-FA2F | fa2e CJK COMPATIBILITY IDEOGRAPH-FA2E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa31 CJK COMPATIBILITY IDEOGRAPH-FA31 | fa30 CJK COMPATIBILITY IDEOGRAPH-FA30 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa33 CJK COMPATIBILITY IDEOGRAPH-FA33 | fa32 CJK COMPATIBILITY IDEOGRAPH-FA32 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa35 CJK COMPATIBILITY IDEOGRAPH-FA35 | fa34 CJK COMPATIBILITY IDEOGRAPH-FA34 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa37 CJK COMPATIBILITY IDEOGRAPH-FA37 | fa36 CJK COMPATIBILITY IDEOGRAPH-FA36 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa39 CJK COMPATIBILITY IDEOGRAPH-FA39 | fa38 CJK COMPATIBILITY IDEOGRAPH-FA38 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3b CJK COMPATIBILITY IDEOGRAPH-FA3B | fa3a CJK COMPATIBILITY IDEOGRAPH-FA3A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3d CJK COMPATIBILITY IDEOGRAPH-FA3D | fa3c CJK COMPATIBILITY IDEOGRAPH-FA3C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa3f CJK COMPATIBILITY IDEOGRAPH-FA3F | fa3e CJK COMPATIBILITY IDEOGRAPH-FA3E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa41 CJK COMPATIBILITY IDEOGRAPH-FA41 | fa40 CJK COMPATIBILITY IDEOGRAPH-FA40 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa43 CJK COMPATIBILITY IDEOGRAPH-FA43 | fa42 CJK COMPATIBILITY IDEOGRAPH-FA42 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa45 CJK COMPATIBILITY IDEOGRAPH-FA45 | fa44 CJK COMPATIBILITY IDEOGRAPH-FA44 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa47 CJK COMPATIBILITY IDEOGRAPH-FA47 | fa46 CJK COMPATIBILITY IDEOGRAPH-FA46 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa49 CJK COMPATIBILITY IDEOGRAPH-FA49 | fa48 CJK COMPATIBILITY IDEOGRAPH-FA48 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4b CJK COMPATIBILITY IDEOGRAPH-FA4B | fa4a CJK COMPATIBILITY IDEOGRAPH-FA4A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4d CJK COMPATIBILITY IDEOGRAPH-FA4D | fa4c CJK COMPATIBILITY IDEOGRAPH-FA4C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa4f CJK COMPATIBILITY IDEOGRAPH-FA4F | fa4e CJK COMPATIBILITY IDEOGRAPH-FA4E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa51 CJK COMPATIBILITY IDEOGRAPH-FA51 | fa50 CJK COMPATIBILITY IDEOGRAPH-FA50 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa53 CJK COMPATIBILITY IDEOGRAPH-FA53 | fa52 CJK COMPATIBILITY IDEOGRAPH-FA52 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa55 CJK COMPATIBILITY IDEOGRAPH-FA55 | fa54 CJK COMPATIBILITY IDEOGRAPH-FA54 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa57 CJK COMPATIBILITY IDEOGRAPH-FA57 | fa56 CJK COMPATIBILITY IDEOGRAPH-FA56 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa59 CJK COMPATIBILITY IDEOGRAPH-FA59 | fa58 CJK COMPATIBILITY IDEOGRAPH-FA58 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5b CJK COMPATIBILITY IDEOGRAPH-FA5B | fa5a CJK COMPATIBILITY IDEOGRAPH-FA5A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5d CJK COMPATIBILITY IDEOGRAPH-FA5D | fa5c CJK COMPATIBILITY IDEOGRAPH-FA5C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa5f CJK COMPATIBILITY IDEOGRAPH-FA5F | fa5e CJK COMPATIBILITY IDEOGRAPH-FA5E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa61 CJK COMPATIBILITY IDEOGRAPH-FA61 | fa60 CJK COMPATIBILITY IDEOGRAPH-FA60 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa63 CJK COMPATIBILITY IDEOGRAPH-FA63 | fa62 CJK COMPATIBILITY IDEOGRAPH-FA62 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa65 CJK COMPATIBILITY IDEOGRAPH-FA65 | fa64 CJK COMPATIBILITY IDEOGRAPH-FA64 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa67 CJK COMPATIBILITY IDEOGRAPH-FA67 | fa66 CJK COMPATIBILITY IDEOGRAPH-FA66 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa69 CJK COMPATIBILITY IDEOGRAPH-FA69 | fa68 CJK COMPATIBILITY IDEOGRAPH-FA68 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa6b CJK COMPATIBILITY IDEOGRAPH-FA6B | fa6a CJK COMPATIBILITY IDEOGRAPH-FA6A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa6d CJK COMPATIBILITY IDEOGRAPH-FA6D | fa6c CJK COMPATIBILITY IDEOGRAPH-FA6C */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fa6f (null) | fa6e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa71 CJK COMPATIBILITY IDEOGRAPH-FA71 | fa70 CJK COMPATIBILITY IDEOGRAPH-FA70 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa73 CJK COMPATIBILITY IDEOGRAPH-FA73 | fa72 CJK COMPATIBILITY IDEOGRAPH-FA72 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa75 CJK COMPATIBILITY IDEOGRAPH-FA75 | fa74 CJK COMPATIBILITY IDEOGRAPH-FA74 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa77 CJK COMPATIBILITY IDEOGRAPH-FA77 | fa76 CJK COMPATIBILITY IDEOGRAPH-FA76 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa79 CJK COMPATIBILITY IDEOGRAPH-FA79 | fa78 CJK COMPATIBILITY IDEOGRAPH-FA78 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7b CJK COMPATIBILITY IDEOGRAPH-FA7B | fa7a CJK COMPATIBILITY IDEOGRAPH-FA7A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7d CJK COMPATIBILITY IDEOGRAPH-FA7D | fa7c CJK COMPATIBILITY IDEOGRAPH-FA7C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa7f CJK COMPATIBILITY IDEOGRAPH-FA7F | fa7e CJK COMPATIBILITY IDEOGRAPH-FA7E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa81 CJK COMPATIBILITY IDEOGRAPH-FA81 | fa80 CJK COMPATIBILITY IDEOGRAPH-FA80 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa83 CJK COMPATIBILITY IDEOGRAPH-FA83 | fa82 CJK COMPATIBILITY IDEOGRAPH-FA82 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa85 CJK COMPATIBILITY IDEOGRAPH-FA85 | fa84 CJK COMPATIBILITY IDEOGRAPH-FA84 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa87 CJK COMPATIBILITY IDEOGRAPH-FA87 | fa86 CJK COMPATIBILITY IDEOGRAPH-FA86 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa89 CJK COMPATIBILITY IDEOGRAPH-FA89 | fa88 CJK COMPATIBILITY IDEOGRAPH-FA88 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8b CJK COMPATIBILITY IDEOGRAPH-FA8B | fa8a CJK COMPATIBILITY IDEOGRAPH-FA8A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8d CJK COMPATIBILITY IDEOGRAPH-FA8D | fa8c CJK COMPATIBILITY IDEOGRAPH-FA8C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa8f CJK COMPATIBILITY IDEOGRAPH-FA8F | fa8e CJK COMPATIBILITY IDEOGRAPH-FA8E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa91 CJK COMPATIBILITY IDEOGRAPH-FA91 | fa90 CJK COMPATIBILITY IDEOGRAPH-FA90 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa93 CJK COMPATIBILITY IDEOGRAPH-FA93 | fa92 CJK COMPATIBILITY IDEOGRAPH-FA92 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa95 CJK COMPATIBILITY IDEOGRAPH-FA95 | fa94 CJK COMPATIBILITY IDEOGRAPH-FA94 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa97 CJK COMPATIBILITY IDEOGRAPH-FA97 | fa96 CJK COMPATIBILITY IDEOGRAPH-FA96 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa99 CJK COMPATIBILITY IDEOGRAPH-FA99 | fa98 CJK COMPATIBILITY IDEOGRAPH-FA98 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9b CJK COMPATIBILITY IDEOGRAPH-FA9B | fa9a CJK COMPATIBILITY IDEOGRAPH-FA9A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9d CJK COMPATIBILITY IDEOGRAPH-FA9D | fa9c CJK COMPATIBILITY IDEOGRAPH-FA9C */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fa9f CJK COMPATIBILITY IDEOGRAPH-FA9F | fa9e CJK COMPATIBILITY IDEOGRAPH-FA9E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa1 CJK COMPATIBILITY IDEOGRAPH-FAA1 | faa0 CJK COMPATIBILITY IDEOGRAPH-FAA0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa3 CJK COMPATIBILITY IDEOGRAPH-FAA3 | faa2 CJK COMPATIBILITY IDEOGRAPH-FAA2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa5 CJK COMPATIBILITY IDEOGRAPH-FAA5 | faa4 CJK COMPATIBILITY IDEOGRAPH-FAA4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa7 CJK COMPATIBILITY IDEOGRAPH-FAA7 | faa6 CJK COMPATIBILITY IDEOGRAPH-FAA6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faa9 CJK COMPATIBILITY IDEOGRAPH-FAA9 | faa8 CJK COMPATIBILITY IDEOGRAPH-FAA8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faab CJK COMPATIBILITY IDEOGRAPH-FAAB | faaa CJK COMPATIBILITY IDEOGRAPH-FAAA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faad CJK COMPATIBILITY IDEOGRAPH-FAAD | faac CJK COMPATIBILITY IDEOGRAPH-FAAC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* faaf CJK COMPATIBILITY IDEOGRAPH-FAAF | faae CJK COMPATIBILITY IDEOGRAPH-FAAE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab1 CJK COMPATIBILITY IDEOGRAPH-FAB1 | fab0 CJK COMPATIBILITY IDEOGRAPH-FAB0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab3 CJK COMPATIBILITY IDEOGRAPH-FAB3 | fab2 CJK COMPATIBILITY IDEOGRAPH-FAB2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab5 CJK COMPATIBILITY IDEOGRAPH-FAB5 | fab4 CJK COMPATIBILITY IDEOGRAPH-FAB4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab7 CJK COMPATIBILITY IDEOGRAPH-FAB7 | fab6 CJK COMPATIBILITY IDEOGRAPH-FAB6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fab9 CJK COMPATIBILITY IDEOGRAPH-FAB9 | fab8 CJK COMPATIBILITY IDEOGRAPH-FAB8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabb CJK COMPATIBILITY IDEOGRAPH-FABB | faba CJK COMPATIBILITY IDEOGRAPH-FABA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabd CJK COMPATIBILITY IDEOGRAPH-FABD | fabc CJK COMPATIBILITY IDEOGRAPH-FABC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fabf CJK COMPATIBILITY IDEOGRAPH-FABF | fabe CJK COMPATIBILITY IDEOGRAPH-FABE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac1 CJK COMPATIBILITY IDEOGRAPH-FAC1 | fac0 CJK COMPATIBILITY IDEOGRAPH-FAC0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac3 CJK COMPATIBILITY IDEOGRAPH-FAC3 | fac2 CJK COMPATIBILITY IDEOGRAPH-FAC2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac5 CJK COMPATIBILITY IDEOGRAPH-FAC5 | fac4 CJK COMPATIBILITY IDEOGRAPH-FAC4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac7 CJK COMPATIBILITY IDEOGRAPH-FAC7 | fac6 CJK COMPATIBILITY IDEOGRAPH-FAC6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fac9 CJK COMPATIBILITY IDEOGRAPH-FAC9 | fac8 CJK COMPATIBILITY IDEOGRAPH-FAC8 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facb CJK COMPATIBILITY IDEOGRAPH-FACB | faca CJK COMPATIBILITY IDEOGRAPH-FACA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facd CJK COMPATIBILITY IDEOGRAPH-FACD | facc CJK COMPATIBILITY IDEOGRAPH-FACC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* facf CJK COMPATIBILITY IDEOGRAPH-FACF | face CJK COMPATIBILITY IDEOGRAPH-FACE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad1 CJK COMPATIBILITY IDEOGRAPH-FAD1 | fad0 CJK COMPATIBILITY IDEOGRAPH-FAD0 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad3 CJK COMPATIBILITY IDEOGRAPH-FAD3 | fad2 CJK COMPATIBILITY IDEOGRAPH-FAD2 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad5 CJK COMPATIBILITY IDEOGRAPH-FAD5 | fad4 CJK COMPATIBILITY IDEOGRAPH-FAD4 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad7 CJK COMPATIBILITY IDEOGRAPH-FAD7 | fad6 CJK COMPATIBILITY IDEOGRAPH-FAD6 */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fad9 CJK COMPATIBILITY IDEOGRAPH-FAD9 | fad8 CJK COMPATIBILITY IDEOGRAPH-FAD8 */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadb (null) | fada (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadd (null) | fadc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fadf (null) | fade (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae1 (null) | fae0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae3 (null) | fae2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae5 (null) | fae4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae7 (null) | fae6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fae9 (null) | fae8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faeb (null) | faea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faed (null) | faec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faef (null) | faee (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf1 (null) | faf0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf3 (null) | faf2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf5 (null) | faf4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf7 (null) | faf6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faf9 (null) | faf8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fafb (null) | fafa (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fafd (null) | fafc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* faff (null) | fafe (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb01 LATIN SMALL LIGATURE FI | fb00 LATIN SMALL LIGATURE FF */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb03 LATIN SMALL LIGATURE FFI | fb02 LATIN SMALL LIGATURE FL */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb05 LATIN SMALL LIGATURE LONG S T | fb04 LATIN SMALL LIGATURE FFL */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_LOWER, /* fb07 (null) | fb06 LATIN SMALL LIGATURE ST */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb09 (null) | fb08 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0b (null) | fb0a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0d (null) | fb0c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb0f (null) | fb0e (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb11 (null) | fb10 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_UNDEF, /* fb13 ARMENIAN SMALL LIGATURE MEN NOW | fb12 (null) */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb15 ARMENIAN SMALL LIGATURE MEN INI | fb14 ARMENIAN SMALL LIGATURE MEN ECH */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* fb17 ARMENIAN SMALL LIGATURE MEN XEH | fb16 ARMENIAN SMALL LIGATURE VEW NOW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb19 (null) | fb18 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fb1b (null) | fb1a (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fb1d HEBREW LETTER YOD WITH HIRIQ | fb1c (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* fb1f HEBREW LIGATURE YIDDISH YOD YOD PATAH | fb1e HEBREW POINT JUDEO-SPANISH VARIKA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb21 HEBREW LETTER WIDE ALEF | fb20 HEBREW LETTER ALTERNATIVE AYIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb23 HEBREW LETTER WIDE HE | fb22 HEBREW LETTER WIDE DALET */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb25 HEBREW LETTER WIDE LAMED | fb24 HEBREW LETTER WIDE KAF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb27 HEBREW LETTER WIDE RESH | fb26 HEBREW LETTER WIDE FINAL MEM */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_ALPHA, /* fb29 HEBREW LETTER ALTERNATIVE PLUS SIGN | fb28 HEBREW LETTER WIDE TAV */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2b HEBREW LETTER SHIN WITH SIN DOT | fb2a HEBREW LETTER SHIN WITH SHIN DOT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2d HEBREW LETTER SHIN WITH DAGESH AND SIN | fb2c HEBREW LETTER SHIN WITH DAGESH AND SHIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb2f HEBREW LETTER ALEF WITH QAMATS | fb2e HEBREW LETTER ALEF WITH PATAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb31 HEBREW LETTER BET WITH DAGESH | fb30 HEBREW LETTER ALEF WITH MAPIQ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb33 HEBREW LETTER DALET WITH DAGESH | fb32 HEBREW LETTER GIMEL WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb35 HEBREW LETTER VAV WITH DAGESH | fb34 HEBREW LETTER HE WITH MAPIQ */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb37 (null) | fb36 HEBREW LETTER ZAYIN WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb39 HEBREW LETTER YOD WITH DAGESH | fb38 HEBREW LETTER TET WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb3b HEBREW LETTER KAF WITH DAGESH | fb3a HEBREW LETTER FINAL KAF WITH DAGESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb3d (null) | fb3c HEBREW LETTER LAMED WITH DAGESH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb3f (null) | fb3e HEBREW LETTER MEM WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb41 HEBREW LETTER SAMEKH WITH DAGESH | fb40 HEBREW LETTER NUN WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fb43 HEBREW LETTER FINAL PE WITH DAGESH | fb42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fb45 (null) | fb44 HEBREW LETTER PE WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb47 HEBREW LETTER QOF WITH DAGESH | fb46 HEBREW LETTER TSADI WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb49 HEBREW LETTER SHIN WITH DAGESH | fb48 HEBREW LETTER RESH WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4b HEBREW LETTER VAV WITH HOLAM | fb4a HEBREW LETTER TAV WITH DAGESH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4d HEBREW LETTER KAF WITH RAFE | fb4c HEBREW LETTER BET WITH RAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb4f HEBREW LIGATURE ALEF LAMED | fb4e HEBREW LETTER PE WITH RAFE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb51 ARABIC LETTER ALEF WASLA FINAL FORM | fb50 ARABIC LETTER ALEF WASLA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb53 ARABIC LETTER BEEH FINAL FORM | fb52 ARABIC LETTER BEEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb55 ARABIC LETTER BEEH MEDIAL FORM | fb54 ARABIC LETTER BEEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb57 ARABIC LETTER PEH FINAL FORM | fb56 ARABIC LETTER PEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb59 ARABIC LETTER PEH MEDIAL FORM | fb58 ARABIC LETTER PEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5b ARABIC LETTER BEHEH FINAL FORM | fb5a ARABIC LETTER BEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5d ARABIC LETTER BEHEH MEDIAL FORM | fb5c ARABIC LETTER BEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb5f ARABIC LETTER TTEHEH FINAL FORM | fb5e ARABIC LETTER TTEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb61 ARABIC LETTER TTEHEH MEDIAL FORM | fb60 ARABIC LETTER TTEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb63 ARABIC LETTER TEHEH FINAL FORM | fb62 ARABIC LETTER TEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb65 ARABIC LETTER TEHEH MEDIAL FORM | fb64 ARABIC LETTER TEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb67 ARABIC LETTER TTEH FINAL FORM | fb66 ARABIC LETTER TTEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb69 ARABIC LETTER TTEH MEDIAL FORM | fb68 ARABIC LETTER TTEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6b ARABIC LETTER VEH FINAL FORM | fb6a ARABIC LETTER VEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6d ARABIC LETTER VEH MEDIAL FORM | fb6c ARABIC LETTER VEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb6f ARABIC LETTER PEHEH FINAL FORM | fb6e ARABIC LETTER PEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb71 ARABIC LETTER PEHEH MEDIAL FORM | fb70 ARABIC LETTER PEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb73 ARABIC LETTER DYEH FINAL FORM | fb72 ARABIC LETTER DYEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb75 ARABIC LETTER DYEH MEDIAL FORM | fb74 ARABIC LETTER DYEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb77 ARABIC LETTER NYEH FINAL FORM | fb76 ARABIC LETTER NYEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb79 ARABIC LETTER NYEH MEDIAL FORM | fb78 ARABIC LETTER NYEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7b ARABIC LETTER TCHEH FINAL FORM | fb7a ARABIC LETTER TCHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7d ARABIC LETTER TCHEH MEDIAL FORM | fb7c ARABIC LETTER TCHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb7f ARABIC LETTER TCHEHEH FINAL FORM | fb7e ARABIC LETTER TCHEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb81 ARABIC LETTER TCHEHEH MEDIAL FORM | fb80 ARABIC LETTER TCHEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb83 ARABIC LETTER DDAHAL FINAL FORM | fb82 ARABIC LETTER DDAHAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb85 ARABIC LETTER DAHAL FINAL FORM | fb84 ARABIC LETTER DAHAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb87 ARABIC LETTER DUL FINAL FORM | fb86 ARABIC LETTER DUL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb89 ARABIC LETTER DDAL FINAL FORM | fb88 ARABIC LETTER DDAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8b ARABIC LETTER JEH FINAL FORM | fb8a ARABIC LETTER JEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8d ARABIC LETTER RREH FINAL FORM | fb8c ARABIC LETTER RREH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb8f ARABIC LETTER KEHEH FINAL FORM | fb8e ARABIC LETTER KEHEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb91 ARABIC LETTER KEHEH MEDIAL FORM | fb90 ARABIC LETTER KEHEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb93 ARABIC LETTER GAF FINAL FORM | fb92 ARABIC LETTER GAF ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb95 ARABIC LETTER GAF MEDIAL FORM | fb94 ARABIC LETTER GAF INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb97 ARABIC LETTER GUEH FINAL FORM | fb96 ARABIC LETTER GUEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb99 ARABIC LETTER GUEH MEDIAL FORM | fb98 ARABIC LETTER GUEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9b ARABIC LETTER NGOEH FINAL FORM | fb9a ARABIC LETTER NGOEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9d ARABIC LETTER NGOEH MEDIAL FORM | fb9c ARABIC LETTER NGOEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fb9f ARABIC LETTER NOON GHUNNA FINAL FORM | fb9e ARABIC LETTER NOON GHUNNA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba1 ARABIC LETTER RNOON FINAL FORM | fba0 ARABIC LETTER RNOON ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba3 ARABIC LETTER RNOON MEDIAL FORM | fba2 ARABIC LETTER RNOON INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba5 ARABIC LETTER HEH WITH YEH ABOVE FINAL | fba4 ARABIC LETTER HEH WITH YEH ABOVE ISOLAT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba7 ARABIC LETTER HEH GOAL FINAL FORM | fba6 ARABIC LETTER HEH GOAL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fba9 ARABIC LETTER HEH GOAL MEDIAL FORM | fba8 ARABIC LETTER HEH GOAL INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbab ARABIC LETTER HEH DOACHASHMEE FINAL FOR | fbaa ARABIC LETTER HEH DOACHASHMEE ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbad ARABIC LETTER HEH DOACHASHMEE MEDIAL FO | fbac ARABIC LETTER HEH DOACHASHMEE INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbaf ARABIC LETTER YEH BARREE FINAL FORM | fbae ARABIC LETTER YEH BARREE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbb1 ARABIC LETTER YEH BARREE WITH HAMZA ABO | fbb0 ARABIC LETTER YEH BARREE WITH HAMZA ABO */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb3 ARABIC SYMBOL DOT BELOW | fbb2 ARABIC SYMBOL DOT ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb5 ARABIC SYMBOL TWO DOTS BELOW | fbb4 ARABIC SYMBOL TWO DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb7 ARABIC SYMBOL THREE DOTS BELOW | fbb6 ARABIC SYMBOL THREE DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbb9 ARABIC SYMBOL THREE DOTS POINTING DOWNW | fbb8 ARABIC SYMBOL THREE DOTS POINTING DOWNW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbb ARABIC SYMBOL FOUR DOTS BELOW | fbba ARABIC SYMBOL FOUR DOTS ABOVE */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbd ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE | fbbc ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbbf ARABIC SYMBOL RING | fbbe ARABIC SYMBOL TWO DOTS VERTICALLY BELOW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fbc1 ARABIC SYMBOL SMALL TAH BELOW | fbc0 ARABIC SYMBOL SMALL TAH ABOVE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc3 (null) | fbc2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc5 (null) | fbc4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc7 (null) | fbc6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbc9 (null) | fbc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcb (null) | fbca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcd (null) | fbcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbcf (null) | fbce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fbd1 (null) | fbd0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_UNDEF, /* fbd3 ARABIC LETTER NG ISOLATED FORM | fbd2 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd5 ARABIC LETTER NG INITIAL FORM | fbd4 ARABIC LETTER NG FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd7 ARABIC LETTER U ISOLATED FORM | fbd6 ARABIC LETTER NG MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbd9 ARABIC LETTER OE ISOLATED FORM | fbd8 ARABIC LETTER U FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdb ARABIC LETTER YU ISOLATED FORM | fbda ARABIC LETTER OE FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdd ARABIC LETTER U WITH HAMZA ABOVE ISOLAT | fbdc ARABIC LETTER YU FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbdf ARABIC LETTER VE FINAL FORM | fbde ARABIC LETTER VE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe1 ARABIC LETTER KIRGHIZ OE FINAL FORM | fbe0 ARABIC LETTER KIRGHIZ OE ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe3 ARABIC LETTER KIRGHIZ YU FINAL FORM | fbe2 ARABIC LETTER KIRGHIZ YU ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe5 ARABIC LETTER E FINAL FORM | fbe4 ARABIC LETTER E ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe7 ARABIC LETTER E MEDIAL FORM | fbe6 ARABIC LETTER E INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbe9 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALE | fbe8 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbeb ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbea ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbed ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbec ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbef ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbee ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf1 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf3 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf2 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf5 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf4 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf7 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fbf6 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbf9 ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH | fbf8 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbfb ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH | fbfa ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbfd ARABIC LETTER FARSI YEH FINAL FORM | fbfc ARABIC LETTER FARSI YEH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fbff ARABIC LETTER FARSI YEH MEDIAL FORM | fbfe ARABIC LETTER FARSI YEH INITIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc01 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc00 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc03 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc02 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc05 ARABIC LIGATURE BEH WITH JEEM ISOLATED | fc04 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc07 ARABIC LIGATURE BEH WITH KHAH ISOLATED | fc06 ARABIC LIGATURE BEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc09 ARABIC LIGATURE BEH WITH ALEF MAKSURA I | fc08 ARABIC LIGATURE BEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0b ARABIC LIGATURE TEH WITH JEEM ISOLATED | fc0a ARABIC LIGATURE BEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0d ARABIC LIGATURE TEH WITH KHAH ISOLATED | fc0c ARABIC LIGATURE TEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc0f ARABIC LIGATURE TEH WITH ALEF MAKSURA I | fc0e ARABIC LIGATURE TEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc11 ARABIC LIGATURE THEH WITH JEEM ISOLATED | fc10 ARABIC LIGATURE TEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc13 ARABIC LIGATURE THEH WITH ALEF MAKSURA | fc12 ARABIC LIGATURE THEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc15 ARABIC LIGATURE JEEM WITH HAH ISOLATED | fc14 ARABIC LIGATURE THEH WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc17 ARABIC LIGATURE HAH WITH JEEM ISOLATED | fc16 ARABIC LIGATURE JEEM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc19 ARABIC LIGATURE KHAH WITH JEEM ISOLATED | fc18 ARABIC LIGATURE HAH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1b ARABIC LIGATURE KHAH WITH MEEM ISOLATED | fc1a ARABIC LIGATURE KHAH WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1d ARABIC LIGATURE SEEN WITH HAH ISOLATED | fc1c ARABIC LIGATURE SEEN WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc1f ARABIC LIGATURE SEEN WITH MEEM ISOLATED | fc1e ARABIC LIGATURE SEEN WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc21 ARABIC LIGATURE SAD WITH MEEM ISOLATED | fc20 ARABIC LIGATURE SAD WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc23 ARABIC LIGATURE DAD WITH HAH ISOLATED F | fc22 ARABIC LIGATURE DAD WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc25 ARABIC LIGATURE DAD WITH MEEM ISOLATED | fc24 ARABIC LIGATURE DAD WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc27 ARABIC LIGATURE TAH WITH MEEM ISOLATED | fc26 ARABIC LIGATURE TAH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc29 ARABIC LIGATURE AIN WITH JEEM ISOLATED | fc28 ARABIC LIGATURE ZAH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2b ARABIC LIGATURE GHAIN WITH JEEM ISOLATE | fc2a ARABIC LIGATURE AIN WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2d ARABIC LIGATURE FEH WITH JEEM ISOLATED | fc2c ARABIC LIGATURE GHAIN WITH MEEM ISOLATE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc2f ARABIC LIGATURE FEH WITH KHAH ISOLATED | fc2e ARABIC LIGATURE FEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc31 ARABIC LIGATURE FEH WITH ALEF MAKSURA I | fc30 ARABIC LIGATURE FEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc33 ARABIC LIGATURE QAF WITH HAH ISOLATED F | fc32 ARABIC LIGATURE FEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc35 ARABIC LIGATURE QAF WITH ALEF MAKSURA I | fc34 ARABIC LIGATURE QAF WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc37 ARABIC LIGATURE KAF WITH ALEF ISOLATED | fc36 ARABIC LIGATURE QAF WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc39 ARABIC LIGATURE KAF WITH HAH ISOLATED F | fc38 ARABIC LIGATURE KAF WITH JEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3b ARABIC LIGATURE KAF WITH LAM ISOLATED F | fc3a ARABIC LIGATURE KAF WITH KHAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3d ARABIC LIGATURE KAF WITH ALEF MAKSURA I | fc3c ARABIC LIGATURE KAF WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc3f ARABIC LIGATURE LAM WITH JEEM ISOLATED | fc3e ARABIC LIGATURE KAF WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc41 ARABIC LIGATURE LAM WITH KHAH ISOLATED | fc40 ARABIC LIGATURE LAM WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc43 ARABIC LIGATURE LAM WITH ALEF MAKSURA I | fc42 ARABIC LIGATURE LAM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc45 ARABIC LIGATURE MEEM WITH JEEM ISOLATED | fc44 ARABIC LIGATURE LAM WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc47 ARABIC LIGATURE MEEM WITH KHAH ISOLATED | fc46 ARABIC LIGATURE MEEM WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc49 ARABIC LIGATURE MEEM WITH ALEF MAKSURA | fc48 ARABIC LIGATURE MEEM WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4b ARABIC LIGATURE NOON WITH JEEM ISOLATED | fc4a ARABIC LIGATURE MEEM WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4d ARABIC LIGATURE NOON WITH KHAH ISOLATED | fc4c ARABIC LIGATURE NOON WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc4f ARABIC LIGATURE NOON WITH ALEF MAKSURA | fc4e ARABIC LIGATURE NOON WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc51 ARABIC LIGATURE HEH WITH JEEM ISOLATED | fc50 ARABIC LIGATURE NOON WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc53 ARABIC LIGATURE HEH WITH ALEF MAKSURA I | fc52 ARABIC LIGATURE HEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc55 ARABIC LIGATURE YEH WITH JEEM ISOLATED | fc54 ARABIC LIGATURE HEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc57 ARABIC LIGATURE YEH WITH KHAH ISOLATED | fc56 ARABIC LIGATURE YEH WITH HAH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc59 ARABIC LIGATURE YEH WITH ALEF MAKSURA I | fc58 ARABIC LIGATURE YEH WITH MEEM ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5b ARABIC LIGATURE THAL WITH SUPERSCRIPT A | fc5a ARABIC LIGATURE YEH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5d ARABIC LIGATURE ALEF MAKSURA WITH SUPER | fc5c ARABIC LIGATURE REH WITH SUPERSCRIPT AL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc5f ARABIC LIGATURE SHADDA WITH KASRATAN IS | fc5e ARABIC LIGATURE SHADDA WITH DAMMATAN IS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc61 ARABIC LIGATURE SHADDA WITH DAMMA ISOLA | fc60 ARABIC LIGATURE SHADDA WITH FATHA ISOLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc63 ARABIC LIGATURE SHADDA WITH SUPERSCRIPT | fc62 ARABIC LIGATURE SHADDA WITH KASRA ISOLA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc65 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc64 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc67 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc66 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc69 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc68 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6b ARABIC LIGATURE BEH WITH ZAIN FINAL FOR | fc6a ARABIC LIGATURE BEH WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6d ARABIC LIGATURE BEH WITH NOON FINAL FOR | fc6c ARABIC LIGATURE BEH WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc6f ARABIC LIGATURE BEH WITH YEH FINAL FORM | fc6e ARABIC LIGATURE BEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc71 ARABIC LIGATURE TEH WITH ZAIN FINAL FOR | fc70 ARABIC LIGATURE TEH WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc73 ARABIC LIGATURE TEH WITH NOON FINAL FOR | fc72 ARABIC LIGATURE TEH WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc75 ARABIC LIGATURE TEH WITH YEH FINAL FORM | fc74 ARABIC LIGATURE TEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc77 ARABIC LIGATURE THEH WITH ZAIN FINAL FO | fc76 ARABIC LIGATURE THEH WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc79 ARABIC LIGATURE THEH WITH NOON FINAL FO | fc78 ARABIC LIGATURE THEH WITH MEEM FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7b ARABIC LIGATURE THEH WITH YEH FINAL FOR | fc7a ARABIC LIGATURE THEH WITH ALEF MAKSURA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7d ARABIC LIGATURE FEH WITH YEH FINAL FORM | fc7c ARABIC LIGATURE FEH WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc7f ARABIC LIGATURE QAF WITH YEH FINAL FORM | fc7e ARABIC LIGATURE QAF WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc81 ARABIC LIGATURE KAF WITH LAM FINAL FORM | fc80 ARABIC LIGATURE KAF WITH ALEF FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc83 ARABIC LIGATURE KAF WITH ALEF MAKSURA F | fc82 ARABIC LIGATURE KAF WITH MEEM FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc85 ARABIC LIGATURE LAM WITH MEEM FINAL FOR | fc84 ARABIC LIGATURE KAF WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc87 ARABIC LIGATURE LAM WITH YEH FINAL FORM | fc86 ARABIC LIGATURE LAM WITH ALEF MAKSURA F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc89 ARABIC LIGATURE MEEM WITH MEEM FINAL FO | fc88 ARABIC LIGATURE MEEM WITH ALEF FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8b ARABIC LIGATURE NOON WITH ZAIN FINAL FO | fc8a ARABIC LIGATURE NOON WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8d ARABIC LIGATURE NOON WITH NOON FINAL FO | fc8c ARABIC LIGATURE NOON WITH MEEM FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc8f ARABIC LIGATURE NOON WITH YEH FINAL FOR | fc8e ARABIC LIGATURE NOON WITH ALEF MAKSURA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc91 ARABIC LIGATURE YEH WITH REH FINAL FORM | fc90 ARABIC LIGATURE ALEF MAKSURA WITH SUPER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc93 ARABIC LIGATURE YEH WITH MEEM FINAL FOR | fc92 ARABIC LIGATURE YEH WITH ZAIN FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc95 ARABIC LIGATURE YEH WITH ALEF MAKSURA F | fc94 ARABIC LIGATURE YEH WITH NOON FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc97 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc96 ARABIC LIGATURE YEH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc99 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc98 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9b ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fc9a ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9d ARABIC LIGATURE BEH WITH HAH INITIAL FO | fc9c ARABIC LIGATURE BEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fc9f ARABIC LIGATURE BEH WITH MEEM INITIAL F | fc9e ARABIC LIGATURE BEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca1 ARABIC LIGATURE TEH WITH JEEM INITIAL F | fca0 ARABIC LIGATURE BEH WITH HEH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca3 ARABIC LIGATURE TEH WITH KHAH INITIAL F | fca2 ARABIC LIGATURE TEH WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca5 ARABIC LIGATURE TEH WITH HEH INITIAL FO | fca4 ARABIC LIGATURE TEH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca7 ARABIC LIGATURE JEEM WITH HAH INITIAL F | fca6 ARABIC LIGATURE THEH WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fca9 ARABIC LIGATURE HAH WITH JEEM INITIAL F | fca8 ARABIC LIGATURE JEEM WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcab ARABIC LIGATURE KHAH WITH JEEM INITIAL | fcaa ARABIC LIGATURE HAH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcad ARABIC LIGATURE SEEN WITH JEEM INITIAL | fcac ARABIC LIGATURE KHAH WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcaf ARABIC LIGATURE SEEN WITH KHAH INITIAL | fcae ARABIC LIGATURE SEEN WITH HAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb1 ARABIC LIGATURE SAD WITH HAH INITIAL FO | fcb0 ARABIC LIGATURE SEEN WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb3 ARABIC LIGATURE SAD WITH MEEM INITIAL F | fcb2 ARABIC LIGATURE SAD WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb5 ARABIC LIGATURE DAD WITH HAH INITIAL FO | fcb4 ARABIC LIGATURE DAD WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb7 ARABIC LIGATURE DAD WITH MEEM INITIAL F | fcb6 ARABIC LIGATURE DAD WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcb9 ARABIC LIGATURE ZAH WITH MEEM INITIAL F | fcb8 ARABIC LIGATURE TAH WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbb ARABIC LIGATURE AIN WITH MEEM INITIAL F | fcba ARABIC LIGATURE AIN WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbd ARABIC LIGATURE GHAIN WITH MEEM INITIAL | fcbc ARABIC LIGATURE GHAIN WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcbf ARABIC LIGATURE FEH WITH HAH INITIAL FO | fcbe ARABIC LIGATURE FEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc1 ARABIC LIGATURE FEH WITH MEEM INITIAL F | fcc0 ARABIC LIGATURE FEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc3 ARABIC LIGATURE QAF WITH MEEM INITIAL F | fcc2 ARABIC LIGATURE QAF WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc5 ARABIC LIGATURE KAF WITH HAH INITIAL FO | fcc4 ARABIC LIGATURE KAF WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc7 ARABIC LIGATURE KAF WITH LAM INITIAL FO | fcc6 ARABIC LIGATURE KAF WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcc9 ARABIC LIGATURE LAM WITH JEEM INITIAL F | fcc8 ARABIC LIGATURE KAF WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccb ARABIC LIGATURE LAM WITH KHAH INITIAL F | fcca ARABIC LIGATURE LAM WITH HAH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccd ARABIC LIGATURE LAM WITH HEH INITIAL FO | fccc ARABIC LIGATURE LAM WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fccf ARABIC LIGATURE MEEM WITH HAH INITIAL F | fcce ARABIC LIGATURE MEEM WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd1 ARABIC LIGATURE MEEM WITH MEEM INITIAL | fcd0 ARABIC LIGATURE MEEM WITH KHAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd3 ARABIC LIGATURE NOON WITH HAH INITIAL F | fcd2 ARABIC LIGATURE NOON WITH JEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd5 ARABIC LIGATURE NOON WITH MEEM INITIAL | fcd4 ARABIC LIGATURE NOON WITH KHAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd7 ARABIC LIGATURE HEH WITH JEEM INITIAL F | fcd6 ARABIC LIGATURE NOON WITH HEH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcd9 ARABIC LIGATURE HEH WITH SUPERSCRIPT AL | fcd8 ARABIC LIGATURE HEH WITH MEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdb ARABIC LIGATURE YEH WITH HAH INITIAL FO | fcda ARABIC LIGATURE YEH WITH JEEM INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdd ARABIC LIGATURE YEH WITH MEEM INITIAL F | fcdc ARABIC LIGATURE YEH WITH KHAH INITIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcdf ARABIC LIGATURE YEH WITH HAMZA ABOVE WI | fcde ARABIC LIGATURE YEH WITH HEH INITIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce1 ARABIC LIGATURE BEH WITH MEEM MEDIAL FO | fce0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce3 ARABIC LIGATURE TEH WITH MEEM MEDIAL FO | fce2 ARABIC LIGATURE BEH WITH HEH MEDIAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce5 ARABIC LIGATURE THEH WITH MEEM MEDIAL F | fce4 ARABIC LIGATURE TEH WITH HEH MEDIAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce7 ARABIC LIGATURE SEEN WITH MEEM MEDIAL F | fce6 ARABIC LIGATURE THEH WITH HEH MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fce9 ARABIC LIGATURE SHEEN WITH MEEM MEDIAL | fce8 ARABIC LIGATURE SEEN WITH HEH MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fceb ARABIC LIGATURE KAF WITH LAM MEDIAL FOR | fcea ARABIC LIGATURE SHEEN WITH HEH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fced ARABIC LIGATURE LAM WITH MEEM MEDIAL FO | fcec ARABIC LIGATURE KAF WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcef ARABIC LIGATURE NOON WITH HEH MEDIAL FO | fcee ARABIC LIGATURE NOON WITH MEEM MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf1 ARABIC LIGATURE YEH WITH HEH MEDIAL FOR | fcf0 ARABIC LIGATURE YEH WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf3 ARABIC LIGATURE SHADDA WITH DAMMA MEDIA | fcf2 ARABIC LIGATURE SHADDA WITH FATHA MEDIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf5 ARABIC LIGATURE TAH WITH ALEF MAKSURA I | fcf4 ARABIC LIGATURE SHADDA WITH KASRA MEDIA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf7 ARABIC LIGATURE AIN WITH ALEF MAKSURA I | fcf6 ARABIC LIGATURE TAH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcf9 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA | fcf8 ARABIC LIGATURE AIN WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcfb ARABIC LIGATURE SEEN WITH ALEF MAKSURA | fcfa ARABIC LIGATURE GHAIN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcfd ARABIC LIGATURE SHEEN WITH ALEF MAKSURA | fcfc ARABIC LIGATURE SEEN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fcff ARABIC LIGATURE HAH WITH ALEF MAKSURA I | fcfe ARABIC LIGATURE SHEEN WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd01 ARABIC LIGATURE JEEM WITH ALEF MAKSURA | fd00 ARABIC LIGATURE HAH WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd03 ARABIC LIGATURE KHAH WITH ALEF MAKSURA | fd02 ARABIC LIGATURE JEEM WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd05 ARABIC LIGATURE SAD WITH ALEF MAKSURA I | fd04 ARABIC LIGATURE KHAH WITH YEH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd07 ARABIC LIGATURE DAD WITH ALEF MAKSURA I | fd06 ARABIC LIGATURE SAD WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd09 ARABIC LIGATURE SHEEN WITH JEEM ISOLATE | fd08 ARABIC LIGATURE DAD WITH YEH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0b ARABIC LIGATURE SHEEN WITH KHAH ISOLATE | fd0a ARABIC LIGATURE SHEEN WITH HAH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0d ARABIC LIGATURE SHEEN WITH REH ISOLATED | fd0c ARABIC LIGATURE SHEEN WITH MEEM ISOLATE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd0f ARABIC LIGATURE SAD WITH REH ISOLATED F | fd0e ARABIC LIGATURE SEEN WITH REH ISOLATED */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd11 ARABIC LIGATURE TAH WITH ALEF MAKSURA F | fd10 ARABIC LIGATURE DAD WITH REH ISOLATED F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd13 ARABIC LIGATURE AIN WITH ALEF MAKSURA F | fd12 ARABIC LIGATURE TAH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd15 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA | fd14 ARABIC LIGATURE AIN WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd17 ARABIC LIGATURE SEEN WITH ALEF MAKSURA | fd16 ARABIC LIGATURE GHAIN WITH YEH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd19 ARABIC LIGATURE SHEEN WITH ALEF MAKSURA | fd18 ARABIC LIGATURE SEEN WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1b ARABIC LIGATURE HAH WITH ALEF MAKSURA F | fd1a ARABIC LIGATURE SHEEN WITH YEH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1d ARABIC LIGATURE JEEM WITH ALEF MAKSURA | fd1c ARABIC LIGATURE HAH WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd1f ARABIC LIGATURE KHAH WITH ALEF MAKSURA | fd1e ARABIC LIGATURE JEEM WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd21 ARABIC LIGATURE SAD WITH ALEF MAKSURA F | fd20 ARABIC LIGATURE KHAH WITH YEH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd23 ARABIC LIGATURE DAD WITH ALEF MAKSURA F | fd22 ARABIC LIGATURE SAD WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd25 ARABIC LIGATURE SHEEN WITH JEEM FINAL F | fd24 ARABIC LIGATURE DAD WITH YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd27 ARABIC LIGATURE SHEEN WITH KHAH FINAL F | fd26 ARABIC LIGATURE SHEEN WITH HAH FINAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd29 ARABIC LIGATURE SHEEN WITH REH FINAL FO | fd28 ARABIC LIGATURE SHEEN WITH MEEM FINAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2b ARABIC LIGATURE SAD WITH REH FINAL FORM | fd2a ARABIC LIGATURE SEEN WITH REH FINAL FOR */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2d ARABIC LIGATURE SHEEN WITH JEEM INITIAL | fd2c ARABIC LIGATURE DAD WITH REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd2f ARABIC LIGATURE SHEEN WITH KHAH INITIAL | fd2e ARABIC LIGATURE SHEEN WITH HAH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd31 ARABIC LIGATURE SEEN WITH HEH INITIAL F | fd30 ARABIC LIGATURE SHEEN WITH MEEM INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd33 ARABIC LIGATURE TAH WITH MEEM INITIAL F | fd32 ARABIC LIGATURE SHEEN WITH HEH INITIAL */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd35 ARABIC LIGATURE SEEN WITH HAH MEDIAL FO | fd34 ARABIC LIGATURE SEEN WITH JEEM MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd37 ARABIC LIGATURE SHEEN WITH JEEM MEDIAL | fd36 ARABIC LIGATURE SEEN WITH KHAH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd39 ARABIC LIGATURE SHEEN WITH KHAH MEDIAL | fd38 ARABIC LIGATURE SHEEN WITH HAH MEDIAL F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd3b ARABIC LIGATURE ZAH WITH MEEM MEDIAL FO | fd3a ARABIC LIGATURE TAH WITH MEEM MEDIAL FO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd3d ARABIC LIGATURE ALEF WITH FATHATAN ISOL | fd3c ARABIC LIGATURE ALEF WITH FATHATAN FINA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fd3f ORNATE RIGHT PARENTHESIS | fd3e ORNATE LEFT PARENTHESIS */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd41 (null) | fd40 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd43 (null) | fd42 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd45 (null) | fd44 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd47 (null) | fd46 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd49 (null) | fd48 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4b (null) | fd4a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4d (null) | fd4c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd4f (null) | fd4e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd51 ARABIC LIGATURE TEH WITH HAH WITH JEEM | fd50 ARABIC LIGATURE TEH WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd53 ARABIC LIGATURE TEH WITH HAH WITH MEEM | fd52 ARABIC LIGATURE TEH WITH HAH WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd55 ARABIC LIGATURE TEH WITH MEEM WITH JEEM | fd54 ARABIC LIGATURE TEH WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd57 ARABIC LIGATURE TEH WITH MEEM WITH KHAH | fd56 ARABIC LIGATURE TEH WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd59 ARABIC LIGATURE JEEM WITH MEEM WITH HAH | fd58 ARABIC LIGATURE JEEM WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5b ARABIC LIGATURE HAH WITH MEEM WITH ALEF | fd5a ARABIC LIGATURE HAH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5d ARABIC LIGATURE SEEN WITH JEEM WITH HAH | fd5c ARABIC LIGATURE SEEN WITH HAH WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd5f ARABIC LIGATURE SEEN WITH MEEM WITH HAH | fd5e ARABIC LIGATURE SEEN WITH JEEM WITH ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd61 ARABIC LIGATURE SEEN WITH MEEM WITH JEE | fd60 ARABIC LIGATURE SEEN WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd63 ARABIC LIGATURE SEEN WITH MEEM WITH MEE | fd62 ARABIC LIGATURE SEEN WITH MEEM WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd65 ARABIC LIGATURE SAD WITH HAH WITH HAH I | fd64 ARABIC LIGATURE SAD WITH HAH WITH HAH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd67 ARABIC LIGATURE SHEEN WITH HAH WITH MEE | fd66 ARABIC LIGATURE SAD WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd69 ARABIC LIGATURE SHEEN WITH JEEM WITH YE | fd68 ARABIC LIGATURE SHEEN WITH HAH WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6b ARABIC LIGATURE SHEEN WITH MEEM WITH KH | fd6a ARABIC LIGATURE SHEEN WITH MEEM WITH KH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6d ARABIC LIGATURE SHEEN WITH MEEM WITH ME | fd6c ARABIC LIGATURE SHEEN WITH MEEM WITH ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd6f ARABIC LIGATURE DAD WITH KHAH WITH MEEM | fd6e ARABIC LIGATURE DAD WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd71 ARABIC LIGATURE TAH WITH MEEM WITH HAH | fd70 ARABIC LIGATURE DAD WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd73 ARABIC LIGATURE TAH WITH MEEM WITH MEEM | fd72 ARABIC LIGATURE TAH WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd75 ARABIC LIGATURE AIN WITH JEEM WITH MEEM | fd74 ARABIC LIGATURE TAH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd77 ARABIC LIGATURE AIN WITH MEEM WITH MEEM | fd76 ARABIC LIGATURE AIN WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd79 ARABIC LIGATURE GHAIN WITH MEEM WITH ME | fd78 ARABIC LIGATURE AIN WITH MEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7b ARABIC LIGATURE GHAIN WITH MEEM WITH AL | fd7a ARABIC LIGATURE GHAIN WITH MEEM WITH YE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7d ARABIC LIGATURE FEH WITH KHAH WITH MEEM | fd7c ARABIC LIGATURE FEH WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd7f ARABIC LIGATURE QAF WITH MEEM WITH MEEM | fd7e ARABIC LIGATURE QAF WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd81 ARABIC LIGATURE LAM WITH HAH WITH YEH F | fd80 ARABIC LIGATURE LAM WITH HAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd83 ARABIC LIGATURE LAM WITH JEEM WITH JEEM | fd82 ARABIC LIGATURE LAM WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd85 ARABIC LIGATURE LAM WITH KHAH WITH MEEM | fd84 ARABIC LIGATURE LAM WITH JEEM WITH JEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd87 ARABIC LIGATURE LAM WITH MEEM WITH HAH | fd86 ARABIC LIGATURE LAM WITH KHAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd89 ARABIC LIGATURE MEEM WITH HAH WITH JEEM | fd88 ARABIC LIGATURE LAM WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8b ARABIC LIGATURE MEEM WITH HAH WITH YEH | fd8a ARABIC LIGATURE MEEM WITH HAH WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8d ARABIC LIGATURE MEEM WITH JEEM WITH MEE | fd8c ARABIC LIGATURE MEEM WITH JEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd8f ARABIC LIGATURE MEEM WITH KHAH WITH MEE | fd8e ARABIC LIGATURE MEEM WITH KHAH WITH JEE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fd91 (null) | fd90 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd93 ARABIC LIGATURE HEH WITH MEEM WITH JEEM | fd92 ARABIC LIGATURE MEEM WITH JEEM WITH KHA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd95 ARABIC LIGATURE NOON WITH HAH WITH MEEM | fd94 ARABIC LIGATURE HEH WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd97 ARABIC LIGATURE NOON WITH JEEM WITH MEE | fd96 ARABIC LIGATURE NOON WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd99 ARABIC LIGATURE NOON WITH JEEM WITH ALE | fd98 ARABIC LIGATURE NOON WITH JEEM WITH MEE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9b ARABIC LIGATURE NOON WITH MEEM WITH ALE | fd9a ARABIC LIGATURE NOON WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9d ARABIC LIGATURE YEH WITH MEEM WITH MEEM | fd9c ARABIC LIGATURE YEH WITH MEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fd9f ARABIC LIGATURE TEH WITH JEEM WITH YEH | fd9e ARABIC LIGATURE BEH WITH KHAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda1 ARABIC LIGATURE TEH WITH KHAH WITH YEH | fda0 ARABIC LIGATURE TEH WITH JEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda3 ARABIC LIGATURE TEH WITH MEEM WITH YEH | fda2 ARABIC LIGATURE TEH WITH KHAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda5 ARABIC LIGATURE JEEM WITH MEEM WITH YEH | fda4 ARABIC LIGATURE TEH WITH MEEM WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda7 ARABIC LIGATURE JEEM WITH MEEM WITH ALE | fda6 ARABIC LIGATURE JEEM WITH HAH WITH ALEF */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fda9 ARABIC LIGATURE SAD WITH HAH WITH YEH F | fda8 ARABIC LIGATURE SEEN WITH KHAH WITH ALE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdab ARABIC LIGATURE DAD WITH HAH WITH YEH F | fdaa ARABIC LIGATURE SHEEN WITH HAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdad ARABIC LIGATURE LAM WITH MEEM WITH YEH | fdac ARABIC LIGATURE LAM WITH JEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdaf ARABIC LIGATURE YEH WITH JEEM WITH YEH | fdae ARABIC LIGATURE YEH WITH HAH WITH YEH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb1 ARABIC LIGATURE MEEM WITH MEEM WITH YEH | fdb0 ARABIC LIGATURE YEH WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb3 ARABIC LIGATURE NOON WITH HAH WITH YEH | fdb2 ARABIC LIGATURE QAF WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb5 ARABIC LIGATURE LAM WITH HAH WITH MEEM | fdb4 ARABIC LIGATURE QAF WITH MEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb7 ARABIC LIGATURE KAF WITH MEEM WITH YEH | fdb6 ARABIC LIGATURE AIN WITH MEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdb9 ARABIC LIGATURE MEEM WITH KHAH WITH YEH | fdb8 ARABIC LIGATURE NOON WITH JEEM WITH HAH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbb ARABIC LIGATURE KAF WITH MEEM WITH MEEM | fdba ARABIC LIGATURE LAM WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbd ARABIC LIGATURE NOON WITH JEEM WITH HAH | fdbc ARABIC LIGATURE LAM WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdbf ARABIC LIGATURE HAH WITH JEEM WITH YEH | fdbe ARABIC LIGATURE JEEM WITH HAH WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc1 ARABIC LIGATURE FEH WITH MEEM WITH YEH | fdc0 ARABIC LIGATURE MEEM WITH JEEM WITH YEH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc3 ARABIC LIGATURE KAF WITH MEEM WITH MEEM | fdc2 ARABIC LIGATURE BEH WITH HAH WITH YEH F */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc5 ARABIC LIGATURE SAD WITH MEEM WITH MEEM | fdc4 ARABIC LIGATURE AIN WITH JEEM WITH MEEM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdc7 ARABIC LIGATURE NOON WITH JEEM WITH YEH | fdc6 ARABIC LIGATURE SEEN WITH KHAH WITH YEH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdc9 (null) | fdc8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcb (null) | fdca (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcd (null) | fdcc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdcf (null) | fdce (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd1 (null) | fdd0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd3 (null) | fdd2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd5 (null) | fdd4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd7 (null) | fdd6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdd9 (null) | fdd8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddb (null) | fdda (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddd (null) | fddc (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fddf (null) | fdde (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde1 (null) | fde0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde3 (null) | fde2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde5 (null) | fde4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde7 (null) | fde6 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fde9 (null) | fde8 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdeb (null) | fdea (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fded (null) | fdec (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdef (null) | fdee (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf1 ARABIC LIGATURE QALA USED AS KORANIC ST | fdf0 ARABIC LIGATURE SALLA USED AS KORANIC S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf3 ARABIC LIGATURE AKBAR ISOLATED FORM | fdf2 ARABIC LIGATURE ALLAH ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf5 ARABIC LIGATURE SALAM ISOLATED FORM | fdf4 ARABIC LIGATURE MOHAMMAD ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf7 ARABIC LIGATURE ALAYHE ISOLATED FORM | fdf6 ARABIC LIGATURE RASOUL ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdf9 ARABIC LIGATURE SALLA ISOLATED FORM | fdf8 ARABIC LIGATURE WASALLAM ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fdfb ARABIC LIGATURE JALLAJALALOUHOU | fdfa ARABIC LIGATURE SALLALLAHOU ALAYHE WASA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fdfd ARABIC LIGATURE BISMILLAH AR-RAHMAN AR- | fdfc RIAL SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fdff (null) | fdfe (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe01 VARIATION SELECTOR-2 | fe00 VARIATION SELECTOR-1 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe03 VARIATION SELECTOR-4 | fe02 VARIATION SELECTOR-3 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe05 VARIATION SELECTOR-6 | fe04 VARIATION SELECTOR-5 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe07 VARIATION SELECTOR-8 | fe06 VARIATION SELECTOR-7 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe09 VARIATION SELECTOR-10 | fe08 VARIATION SELECTOR-9 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0b VARIATION SELECTOR-12 | fe0a VARIATION SELECTOR-11 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0d VARIATION SELECTOR-14 | fe0c VARIATION SELECTOR-13 */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe0f VARIATION SELECTOR-16 | fe0e VARIATION SELECTOR-15 */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe11 PRESENTATION FORM FOR VERTICAL IDEOGRAP | fe10 PRESENTATION FORM FOR VERTICAL COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe13 PRESENTATION FORM FOR VERTICAL COLON | fe12 PRESENTATION FORM FOR VERTICAL IDEOGRAP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe15 PRESENTATION FORM FOR VERTICAL EXCLAMAT | fe14 PRESENTATION FORM FOR VERTICAL SEMICOLO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe17 PRESENTATION FORM FOR VERTICAL LEFT WHI | fe16 PRESENTATION FORM FOR VERTICAL QUESTION */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe19 PRESENTATION FORM FOR VERTICAL HORIZONT | fe18 PRESENTATION FORM FOR VERTICAL RIGHT WH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1b (null) | fe1a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1d (null) | fe1c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe1f (null) | fe1e (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe21 COMBINING LIGATURE RIGHT HALF | fe20 COMBINING LIGATURE LEFT HALF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe23 COMBINING DOUBLE TILDE RIGHT HALF | fe22 COMBINING DOUBLE TILDE LEFT HALF */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe25 COMBINING MACRON RIGHT HALF | fe24 COMBINING MACRON LEFT HALF */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* fe27 (null) | fe26 COMBINING CONJOINING MACRON */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe29 (null) | fe28 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2b (null) | fe2a (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2d (null) | fe2c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe2f (null) | fe2e (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe31 PRESENTATION FORM FOR VERTICAL EM DASH | fe30 PRESENTATION FORM FOR VERTICAL TWO DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe33 PRESENTATION FORM FOR VERTICAL LOW LINE | fe32 PRESENTATION FORM FOR VERTICAL EN DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe35 PRESENTATION FORM FOR VERTICAL LEFT PAR | fe34 PRESENTATION FORM FOR VERTICAL WAVY LOW */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe37 PRESENTATION FORM FOR VERTICAL LEFT CUR | fe36 PRESENTATION FORM FOR VERTICAL RIGHT PA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe39 PRESENTATION FORM FOR VERTICAL LEFT TOR | fe38 PRESENTATION FORM FOR VERTICAL RIGHT CU */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3b PRESENTATION FORM FOR VERTICAL LEFT BLA | fe3a PRESENTATION FORM FOR VERTICAL RIGHT TO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3d PRESENTATION FORM FOR VERTICAL LEFT DOU | fe3c PRESENTATION FORM FOR VERTICAL RIGHT BL */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe3f PRESENTATION FORM FOR VERTICAL LEFT ANG | fe3e PRESENTATION FORM FOR VERTICAL RIGHT DO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe41 PRESENTATION FORM FOR VERTICAL LEFT COR | fe40 PRESENTATION FORM FOR VERTICAL RIGHT AN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe43 PRESENTATION FORM FOR VERTICAL LEFT WHI | fe42 PRESENTATION FORM FOR VERTICAL RIGHT CO */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe45 SESAME DOT | fe44 PRESENTATION FORM FOR VERTICAL RIGHT WH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe47 PRESENTATION FORM FOR VERTICAL LEFT SQU | fe46 WHITE SESAME DOT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe49 DASHED OVERLINE | fe48 PRESENTATION FORM FOR VERTICAL RIGHT SQ */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4b WAVY OVERLINE | fe4a CENTRELINE OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4d DASHED LOW LINE | fe4c DOUBLE WAVY OVERLINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe4f WAVY LOW LINE | fe4e CENTRELINE LOW LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe51 SMALL IDEOGRAPHIC COMMA | fe50 SMALL COMMA */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_PUNCT, /* fe53 (null) | fe52 SMALL FULL STOP */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe55 SMALL COLON | fe54 SMALL SEMICOLON */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe57 SMALL EXCLAMATION MARK | fe56 SMALL QUESTION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe59 SMALL LEFT PARENTHESIS | fe58 SMALL EM DASH */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5b SMALL LEFT CURLY BRACKET | fe5a SMALL RIGHT PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5d SMALL LEFT TORTOISE SHELL BRACKET | fe5c SMALL RIGHT CURLY BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe5f SMALL NUMBER SIGN | fe5e SMALL RIGHT TORTOISE SHELL BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe61 SMALL ASTERISK | fe60 SMALL AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* fe63 SMALL HYPHEN-MINUS | fe62 SMALL PLUS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fe65 SMALL GREATER-THAN SIGN | fe64 SMALL LESS-THAN SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* fe67 (null) | fe66 SMALL EQUALS SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* fe69 SMALL DOLLAR SIGN | fe68 SMALL REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* fe6b SMALL COMMERCIAL AT | fe6a SMALL PERCENT SIGN */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe6d (null) | fe6c (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fe6f (null) | fe6e (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe71 ARABIC TATWEEL WITH FATHATAN ABOVE | fe70 ARABIC FATHATAN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe73 ARABIC TAIL FRAGMENT | fe72 ARABIC DAMMATAN ISOLATED FORM */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fe75 (null) | fe74 ARABIC KASRATAN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe77 ARABIC FATHA MEDIAL FORM | fe76 ARABIC FATHA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe79 ARABIC DAMMA MEDIAL FORM | fe78 ARABIC DAMMA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7b ARABIC KASRA MEDIAL FORM | fe7a ARABIC KASRA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7d ARABIC SHADDA MEDIAL FORM | fe7c ARABIC SHADDA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe7f ARABIC SUKUN MEDIAL FORM | fe7e ARABIC SUKUN ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe81 ARABIC LETTER ALEF WITH MADDA ABOVE ISO | fe80 ARABIC LETTER HAMZA ISOLATED FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe83 ARABIC LETTER ALEF WITH HAMZA ABOVE ISO | fe82 ARABIC LETTER ALEF WITH MADDA ABOVE FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe85 ARABIC LETTER WAW WITH HAMZA ABOVE ISOL | fe84 ARABIC LETTER ALEF WITH HAMZA ABOVE FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe87 ARABIC LETTER ALEF WITH HAMZA BELOW ISO | fe86 ARABIC LETTER WAW WITH HAMZA ABOVE FINA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe89 ARABIC LETTER YEH WITH HAMZA ABOVE ISOL | fe88 ARABIC LETTER ALEF WITH HAMZA BELOW FIN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8b ARABIC LETTER YEH WITH HAMZA ABOVE INIT | fe8a ARABIC LETTER YEH WITH HAMZA ABOVE FINA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8d ARABIC LETTER ALEF ISOLATED FORM | fe8c ARABIC LETTER YEH WITH HAMZA ABOVE MEDI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe8f ARABIC LETTER BEH ISOLATED FORM | fe8e ARABIC LETTER ALEF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe91 ARABIC LETTER BEH INITIAL FORM | fe90 ARABIC LETTER BEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe93 ARABIC LETTER TEH MARBUTA ISOLATED FORM | fe92 ARABIC LETTER BEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe95 ARABIC LETTER TEH ISOLATED FORM | fe94 ARABIC LETTER TEH MARBUTA FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe97 ARABIC LETTER TEH INITIAL FORM | fe96 ARABIC LETTER TEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe99 ARABIC LETTER THEH ISOLATED FORM | fe98 ARABIC LETTER TEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9b ARABIC LETTER THEH INITIAL FORM | fe9a ARABIC LETTER THEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9d ARABIC LETTER JEEM ISOLATED FORM | fe9c ARABIC LETTER THEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fe9f ARABIC LETTER JEEM INITIAL FORM | fe9e ARABIC LETTER JEEM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea1 ARABIC LETTER HAH ISOLATED FORM | fea0 ARABIC LETTER JEEM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea3 ARABIC LETTER HAH INITIAL FORM | fea2 ARABIC LETTER HAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea5 ARABIC LETTER KHAH ISOLATED FORM | fea4 ARABIC LETTER HAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea7 ARABIC LETTER KHAH INITIAL FORM | fea6 ARABIC LETTER KHAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fea9 ARABIC LETTER DAL ISOLATED FORM | fea8 ARABIC LETTER KHAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feab ARABIC LETTER THAL ISOLATED FORM | feaa ARABIC LETTER DAL FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fead ARABIC LETTER REH ISOLATED FORM | feac ARABIC LETTER THAL FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feaf ARABIC LETTER ZAIN ISOLATED FORM | feae ARABIC LETTER REH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb1 ARABIC LETTER SEEN ISOLATED FORM | feb0 ARABIC LETTER ZAIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb3 ARABIC LETTER SEEN INITIAL FORM | feb2 ARABIC LETTER SEEN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb5 ARABIC LETTER SHEEN ISOLATED FORM | feb4 ARABIC LETTER SEEN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb7 ARABIC LETTER SHEEN INITIAL FORM | feb6 ARABIC LETTER SHEEN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feb9 ARABIC LETTER SAD ISOLATED FORM | feb8 ARABIC LETTER SHEEN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febb ARABIC LETTER SAD INITIAL FORM | feba ARABIC LETTER SAD FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febd ARABIC LETTER DAD ISOLATED FORM | febc ARABIC LETTER SAD MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* febf ARABIC LETTER DAD INITIAL FORM | febe ARABIC LETTER DAD FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec1 ARABIC LETTER TAH ISOLATED FORM | fec0 ARABIC LETTER DAD MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec3 ARABIC LETTER TAH INITIAL FORM | fec2 ARABIC LETTER TAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec5 ARABIC LETTER ZAH ISOLATED FORM | fec4 ARABIC LETTER TAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec7 ARABIC LETTER ZAH INITIAL FORM | fec6 ARABIC LETTER ZAH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fec9 ARABIC LETTER AIN ISOLATED FORM | fec8 ARABIC LETTER ZAH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecb ARABIC LETTER AIN INITIAL FORM | feca ARABIC LETTER AIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecd ARABIC LETTER GHAIN ISOLATED FORM | fecc ARABIC LETTER AIN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fecf ARABIC LETTER GHAIN INITIAL FORM | fece ARABIC LETTER GHAIN FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed1 ARABIC LETTER FEH ISOLATED FORM | fed0 ARABIC LETTER GHAIN MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed3 ARABIC LETTER FEH INITIAL FORM | fed2 ARABIC LETTER FEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed5 ARABIC LETTER QAF ISOLATED FORM | fed4 ARABIC LETTER FEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed7 ARABIC LETTER QAF INITIAL FORM | fed6 ARABIC LETTER QAF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fed9 ARABIC LETTER KAF ISOLATED FORM | fed8 ARABIC LETTER QAF MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedb ARABIC LETTER KAF INITIAL FORM | feda ARABIC LETTER KAF FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedd ARABIC LETTER LAM ISOLATED FORM | fedc ARABIC LETTER KAF MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fedf ARABIC LETTER LAM INITIAL FORM | fede ARABIC LETTER LAM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee1 ARABIC LETTER MEEM ISOLATED FORM | fee0 ARABIC LETTER LAM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee3 ARABIC LETTER MEEM INITIAL FORM | fee2 ARABIC LETTER MEEM FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee5 ARABIC LETTER NOON ISOLATED FORM | fee4 ARABIC LETTER MEEM MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee7 ARABIC LETTER NOON INITIAL FORM | fee6 ARABIC LETTER NOON FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fee9 ARABIC LETTER HEH ISOLATED FORM | fee8 ARABIC LETTER NOON MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feeb ARABIC LETTER HEH INITIAL FORM | feea ARABIC LETTER HEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feed ARABIC LETTER WAW ISOLATED FORM | feec ARABIC LETTER HEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* feef ARABIC LETTER ALEF MAKSURA ISOLATED FOR | feee ARABIC LETTER WAW FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef1 ARABIC LETTER YEH ISOLATED FORM | fef0 ARABIC LETTER ALEF MAKSURA FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef3 ARABIC LETTER YEH INITIAL FORM | fef2 ARABIC LETTER YEH FINAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef5 ARABIC LIGATURE LAM WITH ALEF WITH MADD | fef4 ARABIC LETTER YEH MEDIAL FORM */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef7 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ | fef6 ARABIC LIGATURE LAM WITH ALEF WITH MADD */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fef9 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ | fef8 ARABIC LIGATURE LAM WITH ALEF WITH HAMZ */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* fefb ARABIC LIGATURE LAM WITH ALEF ISOLATED | fefa ARABIC LIGATURE LAM WITH ALEF WITH HAMZ */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* fefd (null) | fefc ARABIC LIGATURE LAM WITH ALEF FINAL FOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* feff ZERO WIDTH NO-BREAK SPACE | fefe (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UNDEF, /* ff01 FULLWIDTH EXCLAMATION MARK | ff00 (null) */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff03 FULLWIDTH NUMBER SIGN | ff02 FULLWIDTH QUOTATION MARK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff05 FULLWIDTH PERCENT SIGN | ff04 FULLWIDTH DOLLAR SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff07 FULLWIDTH APOSTROPHE | ff06 FULLWIDTH AMPERSAND */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff09 FULLWIDTH RIGHT PARENTHESIS | ff08 FULLWIDTH LEFT PARENTHESIS */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_PUNCT, /* ff0b FULLWIDTH PLUS SIGN | ff0a FULLWIDTH ASTERISK */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff0d FULLWIDTH HYPHEN-MINUS | ff0c FULLWIDTH COMMA */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff0f FULLWIDTH SOLIDUS | ff0e FULLWIDTH FULL STOP */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff11 FULLWIDTH DIGIT ONE | ff10 FULLWIDTH DIGIT ZERO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff13 FULLWIDTH DIGIT THREE | ff12 FULLWIDTH DIGIT TWO */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff15 FULLWIDTH DIGIT FIVE | ff14 FULLWIDTH DIGIT FOUR */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff17 FULLWIDTH DIGIT SEVEN | ff16 FULLWIDTH DIGIT SIX */ (T3_CTYPE_DIGIT << 4) | T3_CTYPE_DIGIT, /* ff19 FULLWIDTH DIGIT NINE | ff18 FULLWIDTH DIGIT EIGHT */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff1b FULLWIDTH SEMICOLON | ff1a FULLWIDTH COLON */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ff1d FULLWIDTH EQUALS SIGN | ff1c FULLWIDTH LESS-THAN SIGN */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff1f FULLWIDTH QUESTION MARK | ff1e FULLWIDTH GREATER-THAN SIGN */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_PUNCT, /* ff21 FULLWIDTH LATIN CAPITAL LETTER A | ff20 FULLWIDTH COMMERCIAL AT */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff23 FULLWIDTH LATIN CAPITAL LETTER C | ff22 FULLWIDTH LATIN CAPITAL LETTER B */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff25 FULLWIDTH LATIN CAPITAL LETTER E | ff24 FULLWIDTH LATIN CAPITAL LETTER D */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff27 FULLWIDTH LATIN CAPITAL LETTER G | ff26 FULLWIDTH LATIN CAPITAL LETTER F */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff29 FULLWIDTH LATIN CAPITAL LETTER I | ff28 FULLWIDTH LATIN CAPITAL LETTER H */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2b FULLWIDTH LATIN CAPITAL LETTER K | ff2a FULLWIDTH LATIN CAPITAL LETTER J */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2d FULLWIDTH LATIN CAPITAL LETTER M | ff2c FULLWIDTH LATIN CAPITAL LETTER L */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff2f FULLWIDTH LATIN CAPITAL LETTER O | ff2e FULLWIDTH LATIN CAPITAL LETTER N */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff31 FULLWIDTH LATIN CAPITAL LETTER Q | ff30 FULLWIDTH LATIN CAPITAL LETTER P */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff33 FULLWIDTH LATIN CAPITAL LETTER S | ff32 FULLWIDTH LATIN CAPITAL LETTER R */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff35 FULLWIDTH LATIN CAPITAL LETTER U | ff34 FULLWIDTH LATIN CAPITAL LETTER T */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff37 FULLWIDTH LATIN CAPITAL LETTER W | ff36 FULLWIDTH LATIN CAPITAL LETTER V */ (T3_CTYPE_UPPER << 4) | T3_CTYPE_UPPER, /* ff39 FULLWIDTH LATIN CAPITAL LETTER Y | ff38 FULLWIDTH LATIN CAPITAL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_UPPER, /* ff3b FULLWIDTH LEFT SQUARE BRACKET | ff3a FULLWIDTH LATIN CAPITAL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff3d FULLWIDTH RIGHT SQUARE BRACKET | ff3c FULLWIDTH REVERSE SOLIDUS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff3f FULLWIDTH LOW LINE | ff3e FULLWIDTH CIRCUMFLEX ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_OTHER, /* ff41 FULLWIDTH LATIN SMALL LETTER A | ff40 FULLWIDTH GRAVE ACCENT */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff43 FULLWIDTH LATIN SMALL LETTER C | ff42 FULLWIDTH LATIN SMALL LETTER B */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff45 FULLWIDTH LATIN SMALL LETTER E | ff44 FULLWIDTH LATIN SMALL LETTER D */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff47 FULLWIDTH LATIN SMALL LETTER G | ff46 FULLWIDTH LATIN SMALL LETTER F */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff49 FULLWIDTH LATIN SMALL LETTER I | ff48 FULLWIDTH LATIN SMALL LETTER H */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4b FULLWIDTH LATIN SMALL LETTER K | ff4a FULLWIDTH LATIN SMALL LETTER J */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4d FULLWIDTH LATIN SMALL LETTER M | ff4c FULLWIDTH LATIN SMALL LETTER L */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff4f FULLWIDTH LATIN SMALL LETTER O | ff4e FULLWIDTH LATIN SMALL LETTER N */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff51 FULLWIDTH LATIN SMALL LETTER Q | ff50 FULLWIDTH LATIN SMALL LETTER P */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff53 FULLWIDTH LATIN SMALL LETTER S | ff52 FULLWIDTH LATIN SMALL LETTER R */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff55 FULLWIDTH LATIN SMALL LETTER U | ff54 FULLWIDTH LATIN SMALL LETTER T */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff57 FULLWIDTH LATIN SMALL LETTER W | ff56 FULLWIDTH LATIN SMALL LETTER V */ (T3_CTYPE_LOWER << 4) | T3_CTYPE_LOWER, /* ff59 FULLWIDTH LATIN SMALL LETTER Y | ff58 FULLWIDTH LATIN SMALL LETTER X */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_LOWER, /* ff5b FULLWIDTH LEFT CURLY BRACKET | ff5a FULLWIDTH LATIN SMALL LETTER Z */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff5d FULLWIDTH RIGHT CURLY BRACKET | ff5c FULLWIDTH VERTICAL LINE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_OTHER, /* ff5f FULLWIDTH LEFT WHITE PARENTHESIS | ff5e FULLWIDTH TILDE */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff61 HALFWIDTH IDEOGRAPHIC FULL STOP | ff60 FULLWIDTH RIGHT WHITE PARENTHESIS */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff63 HALFWIDTH RIGHT CORNER BRACKET | ff62 HALFWIDTH LEFT CORNER BRACKET */ (T3_CTYPE_PUNCT << 4) | T3_CTYPE_PUNCT, /* ff65 HALFWIDTH KATAKANA MIDDLE DOT | ff64 HALFWIDTH IDEOGRAPHIC COMMA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff67 HALFWIDTH KATAKANA LETTER SMALL A | ff66 HALFWIDTH KATAKANA LETTER WO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff69 HALFWIDTH KATAKANA LETTER SMALL U | ff68 HALFWIDTH KATAKANA LETTER SMALL I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6b HALFWIDTH KATAKANA LETTER SMALL O | ff6a HALFWIDTH KATAKANA LETTER SMALL E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6d HALFWIDTH KATAKANA LETTER SMALL YU | ff6c HALFWIDTH KATAKANA LETTER SMALL YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff6f HALFWIDTH KATAKANA LETTER SMALL TU | ff6e HALFWIDTH KATAKANA LETTER SMALL YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_OTHER, /* ff71 HALFWIDTH KATAKANA LETTER A | ff70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED S */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff73 HALFWIDTH KATAKANA LETTER U | ff72 HALFWIDTH KATAKANA LETTER I */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff75 HALFWIDTH KATAKANA LETTER O | ff74 HALFWIDTH KATAKANA LETTER E */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff77 HALFWIDTH KATAKANA LETTER KI | ff76 HALFWIDTH KATAKANA LETTER KA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff79 HALFWIDTH KATAKANA LETTER KE | ff78 HALFWIDTH KATAKANA LETTER KU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7b HALFWIDTH KATAKANA LETTER SA | ff7a HALFWIDTH KATAKANA LETTER KO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7d HALFWIDTH KATAKANA LETTER SU | ff7c HALFWIDTH KATAKANA LETTER SI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff7f HALFWIDTH KATAKANA LETTER SO | ff7e HALFWIDTH KATAKANA LETTER SE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff81 HALFWIDTH KATAKANA LETTER TI | ff80 HALFWIDTH KATAKANA LETTER TA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff83 HALFWIDTH KATAKANA LETTER TE | ff82 HALFWIDTH KATAKANA LETTER TU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff85 HALFWIDTH KATAKANA LETTER NA | ff84 HALFWIDTH KATAKANA LETTER TO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff87 HALFWIDTH KATAKANA LETTER NU | ff86 HALFWIDTH KATAKANA LETTER NI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff89 HALFWIDTH KATAKANA LETTER NO | ff88 HALFWIDTH KATAKANA LETTER NE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8b HALFWIDTH KATAKANA LETTER HI | ff8a HALFWIDTH KATAKANA LETTER HA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8d HALFWIDTH KATAKANA LETTER HE | ff8c HALFWIDTH KATAKANA LETTER HU */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff8f HALFWIDTH KATAKANA LETTER MA | ff8e HALFWIDTH KATAKANA LETTER HO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff91 HALFWIDTH KATAKANA LETTER MU | ff90 HALFWIDTH KATAKANA LETTER MI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff93 HALFWIDTH KATAKANA LETTER MO | ff92 HALFWIDTH KATAKANA LETTER ME */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff95 HALFWIDTH KATAKANA LETTER YU | ff94 HALFWIDTH KATAKANA LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff97 HALFWIDTH KATAKANA LETTER RA | ff96 HALFWIDTH KATAKANA LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff99 HALFWIDTH KATAKANA LETTER RU | ff98 HALFWIDTH KATAKANA LETTER RI */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff9b HALFWIDTH KATAKANA LETTER RO | ff9a HALFWIDTH KATAKANA LETTER RE */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ff9d HALFWIDTH KATAKANA LETTER N | ff9c HALFWIDTH KATAKANA LETTER WA */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ff9f HALFWIDTH KATAKANA SEMI-VOICED SOUND MA | ff9e HALFWIDTH KATAKANA VOICED SOUND MARK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa1 HALFWIDTH HANGUL LETTER KIYEOK | ffa0 HALFWIDTH HANGUL FILLER */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS | ffa2 HALFWIDTH HANGUL LETTER SSANGKIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa5 HALFWIDTH HANGUL LETTER NIEUN-CIEUC | ffa4 HALFWIDTH HANGUL LETTER NIEUN */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa7 HALFWIDTH HANGUL LETTER TIKEUT | ffa6 HALFWIDTH HANGUL LETTER NIEUN-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffa9 HALFWIDTH HANGUL LETTER RIEUL | ffa8 HALFWIDTH HANGUL LETTER SSANGTIKEUT */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffab HALFWIDTH HANGUL LETTER RIEUL-MIEUM | ffaa HALFWIDTH HANGUL LETTER RIEUL-KIYEOK */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffad HALFWIDTH HANGUL LETTER RIEUL-SIOS | ffac HALFWIDTH HANGUL LETTER RIEUL-PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffaf HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH | ffae HALFWIDTH HANGUL LETTER RIEUL-THIEUTH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb1 HALFWIDTH HANGUL LETTER MIEUM | ffb0 HALFWIDTH HANGUL LETTER RIEUL-HIEUH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb3 HALFWIDTH HANGUL LETTER SSANGPIEUP | ffb2 HALFWIDTH HANGUL LETTER PIEUP */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb5 HALFWIDTH HANGUL LETTER SIOS | ffb4 HALFWIDTH HANGUL LETTER PIEUP-SIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb7 HALFWIDTH HANGUL LETTER IEUNG | ffb6 HALFWIDTH HANGUL LETTER SSANGSIOS */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffb9 HALFWIDTH HANGUL LETTER SSANGCIEUC | ffb8 HALFWIDTH HANGUL LETTER CIEUC */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffbb HALFWIDTH HANGUL LETTER KHIEUKH | ffba HALFWIDTH HANGUL LETTER CHIEUCH */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffbd HALFWIDTH HANGUL LETTER PHIEUPH | ffbc HALFWIDTH HANGUL LETTER THIEUTH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ffbf (null) | ffbe HALFWIDTH HANGUL LETTER HIEUH */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffc1 (null) | ffc0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc3 HALFWIDTH HANGUL LETTER AE | ffc2 HALFWIDTH HANGUL LETTER A */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc5 HALFWIDTH HANGUL LETTER YAE | ffc4 HALFWIDTH HANGUL LETTER YA */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffc7 HALFWIDTH HANGUL LETTER E | ffc6 HALFWIDTH HANGUL LETTER EO */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffc9 (null) | ffc8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcb HALFWIDTH HANGUL LETTER YE | ffca HALFWIDTH HANGUL LETTER YEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcd HALFWIDTH HANGUL LETTER WA | ffcc HALFWIDTH HANGUL LETTER O */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffcf HALFWIDTH HANGUL LETTER OE | ffce HALFWIDTH HANGUL LETTER WAE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffd1 (null) | ffd0 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd3 HALFWIDTH HANGUL LETTER U | ffd2 HALFWIDTH HANGUL LETTER YO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd5 HALFWIDTH HANGUL LETTER WE | ffd4 HALFWIDTH HANGUL LETTER WEO */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffd7 HALFWIDTH HANGUL LETTER YU | ffd6 HALFWIDTH HANGUL LETTER WI */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffd9 (null) | ffd8 (null) */ (T3_CTYPE_ALPHA << 4) | T3_CTYPE_ALPHA, /* ffdb HALFWIDTH HANGUL LETTER YI | ffda HALFWIDTH HANGUL LETTER EU */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_ALPHA, /* ffdd (null) | ffdc HALFWIDTH HANGUL LETTER I */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* ffdf (null) | ffde (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe1 FULLWIDTH POUND SIGN | ffe0 FULLWIDTH CENT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe3 FULLWIDTH MACRON | ffe2 FULLWIDTH NOT SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe5 FULLWIDTH YEN SIGN | ffe4 FULLWIDTH BROKEN BAR */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* ffe7 (null) | ffe6 FULLWIDTH WON SIGN */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffe9 HALFWIDTH LEFTWARDS ARROW | ffe8 HALFWIDTH FORMS LIGHT VERTICAL */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffeb HALFWIDTH RIGHTWARDS ARROW | ffea HALFWIDTH UPWARDS ARROW */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* ffed HALFWIDTH BLACK SQUARE | ffec HALFWIDTH DOWNWARDS ARROW */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_OTHER, /* ffef (null) | ffee HALFWIDTH WHITE CIRCLE */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff1 (null) | fff0 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff3 (null) | fff2 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff5 (null) | fff4 (null) */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF, /* fff7 (null) | fff6 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_UNDEF, /* fff9 INTERLINEAR ANNOTATION ANCHOR | fff8 (null) */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fffb INTERLINEAR ANNOTATION TERMINATOR | fffa INTERLINEAR ANNOTATION SEPARATOR */ (T3_CTYPE_OTHER << 4) | T3_CTYPE_OTHER, /* fffd REPLACEMENT CHARACTER | fffc OBJECT REPLACEMENT CHARACTER */ (T3_CTYPE_UNDEF << 4) | T3_CTYPE_UNDEF /* ffff (null) | fffe (null) */ }; int t3_get_chartype(wchar_t ch) { return (chartype[ch >> 1] >> ((ch & 1) << 2)) & 0x0F; } struct case_table { uint16_t lower; uint16_t title; uint16_t upper; uint16_t fold; }; static case_table case_pg_000[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0000 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0001 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0002 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0003 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0004 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0005 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0006 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0007 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0008 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0009 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 000f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0010 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0011 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0012 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0013 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0014 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0015 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0016 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0017 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0018 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0019 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 001f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0020 SPACE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0021 EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0022 QUOTATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0023 NUMBER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0024 DOLLAR SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0025 PERCENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0026 AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0027 APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0028 LEFT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0029 RIGHT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002a ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002b PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002c COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002d HYPHEN-MINUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002e FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 002f SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0030 DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0031 DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0032 DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0033 DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0034 DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0035 DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0036 DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0037 DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0038 DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0039 DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003a COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003b SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003c LESS-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003d EQUALS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003e GREATER-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 003f QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0040 COMMERCIAL AT */ { 0x0001, 0x0000, 0x0000, 0x0001 }, /* 0041 LATIN CAPITAL LETTER A */ { 0x0003, 0x0000, 0x0000, 0x0003 }, /* 0042 LATIN CAPITAL LETTER B */ { 0x0005, 0x0000, 0x0000, 0x0005 }, /* 0043 LATIN CAPITAL LETTER C */ { 0x0007, 0x0000, 0x0000, 0x0007 }, /* 0044 LATIN CAPITAL LETTER D */ { 0x0009, 0x0000, 0x0000, 0x0009 }, /* 0045 LATIN CAPITAL LETTER E */ { 0x000b, 0x0000, 0x0000, 0x000b }, /* 0046 LATIN CAPITAL LETTER F */ { 0x000d, 0x0000, 0x0000, 0x000d }, /* 0047 LATIN CAPITAL LETTER G */ { 0x000f, 0x0000, 0x0000, 0x000f }, /* 0048 LATIN CAPITAL LETTER H */ { 0x0011, 0x0000, 0x0000, 0x0011 }, /* 0049 LATIN CAPITAL LETTER I */ { 0x0013, 0x0000, 0x0000, 0x0013 }, /* 004a LATIN CAPITAL LETTER J */ { 0x0015, 0x0000, 0x0000, 0x0015 }, /* 004b LATIN CAPITAL LETTER K */ { 0x0017, 0x0000, 0x0000, 0x0017 }, /* 004c LATIN CAPITAL LETTER L */ { 0x0019, 0x0000, 0x0000, 0x0019 }, /* 004d LATIN CAPITAL LETTER M */ { 0x001b, 0x0000, 0x0000, 0x001b }, /* 004e LATIN CAPITAL LETTER N */ { 0x001d, 0x0000, 0x0000, 0x001d }, /* 004f LATIN CAPITAL LETTER O */ { 0x001f, 0x0000, 0x0000, 0x001f }, /* 0050 LATIN CAPITAL LETTER P */ { 0x0021, 0x0000, 0x0000, 0x0021 }, /* 0051 LATIN CAPITAL LETTER Q */ { 0x0023, 0x0000, 0x0000, 0x0023 }, /* 0052 LATIN CAPITAL LETTER R */ { 0x0025, 0x0000, 0x0000, 0x0025 }, /* 0053 LATIN CAPITAL LETTER S */ { 0x0027, 0x0000, 0x0000, 0x0027 }, /* 0054 LATIN CAPITAL LETTER T */ { 0x0029, 0x0000, 0x0000, 0x0029 }, /* 0055 LATIN CAPITAL LETTER U */ { 0x002b, 0x0000, 0x0000, 0x002b }, /* 0056 LATIN CAPITAL LETTER V */ { 0x002d, 0x0000, 0x0000, 0x002d }, /* 0057 LATIN CAPITAL LETTER W */ { 0x002f, 0x0000, 0x0000, 0x002f }, /* 0058 LATIN CAPITAL LETTER X */ { 0x0031, 0x0000, 0x0000, 0x0031 }, /* 0059 LATIN CAPITAL LETTER Y */ { 0x0033, 0x0000, 0x0000, 0x0033 }, /* 005a LATIN CAPITAL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005b LEFT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005c REVERSE SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005d RIGHT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005e CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 005f LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0060 GRAVE ACCENT */ { 0x0000, 0x0035, 0x0035, 0x0000 }, /* 0061 LATIN SMALL LETTER A */ { 0x0000, 0x0037, 0x0037, 0x0000 }, /* 0062 LATIN SMALL LETTER B */ { 0x0000, 0x0039, 0x0039, 0x0000 }, /* 0063 LATIN SMALL LETTER C */ { 0x0000, 0x003b, 0x003b, 0x0000 }, /* 0064 LATIN SMALL LETTER D */ { 0x0000, 0x003d, 0x003d, 0x0000 }, /* 0065 LATIN SMALL LETTER E */ { 0x0000, 0x003f, 0x003f, 0x0000 }, /* 0066 LATIN SMALL LETTER F */ { 0x0000, 0x0041, 0x0041, 0x0000 }, /* 0067 LATIN SMALL LETTER G */ { 0x0000, 0x0043, 0x0043, 0x0000 }, /* 0068 LATIN SMALL LETTER H */ { 0x0000, 0x0045, 0x0045, 0x0000 }, /* 0069 LATIN SMALL LETTER I */ { 0x0000, 0x0047, 0x0047, 0x0000 }, /* 006a LATIN SMALL LETTER J */ { 0x0000, 0x0049, 0x0049, 0x0000 }, /* 006b LATIN SMALL LETTER K */ { 0x0000, 0x004b, 0x004b, 0x0000 }, /* 006c LATIN SMALL LETTER L */ { 0x0000, 0x004d, 0x004d, 0x0000 }, /* 006d LATIN SMALL LETTER M */ { 0x0000, 0x004f, 0x004f, 0x0000 }, /* 006e LATIN SMALL LETTER N */ { 0x0000, 0x0051, 0x0051, 0x0000 }, /* 006f LATIN SMALL LETTER O */ { 0x0000, 0x0053, 0x0053, 0x0000 }, /* 0070 LATIN SMALL LETTER P */ { 0x0000, 0x0055, 0x0055, 0x0000 }, /* 0071 LATIN SMALL LETTER Q */ { 0x0000, 0x0057, 0x0057, 0x0000 }, /* 0072 LATIN SMALL LETTER R */ { 0x0000, 0x0059, 0x0059, 0x0000 }, /* 0073 LATIN SMALL LETTER S */ { 0x0000, 0x005b, 0x005b, 0x0000 }, /* 0074 LATIN SMALL LETTER T */ { 0x0000, 0x005d, 0x005d, 0x0000 }, /* 0075 LATIN SMALL LETTER U */ { 0x0000, 0x005f, 0x005f, 0x0000 }, /* 0076 LATIN SMALL LETTER V */ { 0x0000, 0x0061, 0x0061, 0x0000 }, /* 0077 LATIN SMALL LETTER W */ { 0x0000, 0x0063, 0x0063, 0x0000 }, /* 0078 LATIN SMALL LETTER X */ { 0x0000, 0x0065, 0x0065, 0x0000 }, /* 0079 LATIN SMALL LETTER Y */ { 0x0000, 0x0067, 0x0067, 0x0000 }, /* 007a LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007b LEFT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007c VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007d RIGHT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 007e TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 007f */ }; static case_table case_pg_001[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0080 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0081 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0082 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0083 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0084 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0085 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0086 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0087 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0088 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0089 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 008f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0090 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0091 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0092 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0093 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0094 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0095 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0096 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0097 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0098 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0099 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009a */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009b */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009c */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009d */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009e */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 009f */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a0 NO-BREAK SPACE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a1 INVERTED EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a2 CENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a3 POUND SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a4 CURRENCY SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a5 YEN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a6 BROKEN BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a7 SECTION SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a8 DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00a9 COPYRIGHT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00aa FEMININE ORDINAL INDICATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ab LEFT-POINTING DOUBLE ANGLE QUOTATION MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ac NOT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ad SOFT HYPHEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ae REGISTERED SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00af MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b0 DEGREE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b1 PLUS-MINUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b2 SUPERSCRIPT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b3 SUPERSCRIPT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b4 ACUTE ACCENT */ { 0x0000, 0x0069, 0x0069, 0x006b }, /* 00b5 MICRO SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b6 PILCROW SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b7 MIDDLE DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b8 CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00b9 SUPERSCRIPT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00ba MASCULINE ORDINAL INDICATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bb RIGHT-POINTING DOUBLE ANGLE QUOTATION M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bc VULGAR FRACTION ONE QUARTER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bd VULGAR FRACTION ONE HALF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00be VULGAR FRACTION THREE QUARTERS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00bf INVERTED QUESTION MARK */ { 0x006d, 0x0000, 0x0000, 0x006d }, /* 00c0 LATIN CAPITAL LETTER A WITH GRAVE */ { 0x006f, 0x0000, 0x0000, 0x006f }, /* 00c1 LATIN CAPITAL LETTER A WITH ACUTE */ { 0x0071, 0x0000, 0x0000, 0x0071 }, /* 00c2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0073, 0x0000, 0x0000, 0x0073 }, /* 00c3 LATIN CAPITAL LETTER A WITH TILDE */ { 0x0075, 0x0000, 0x0000, 0x0075 }, /* 00c4 LATIN CAPITAL LETTER A WITH DIAERESIS */ { 0x0077, 0x0000, 0x0000, 0x0077 }, /* 00c5 LATIN CAPITAL LETTER A WITH RING ABOVE */ { 0x0079, 0x0000, 0x0000, 0x0079 }, /* 00c6 LATIN CAPITAL LETTER AE */ { 0x007b, 0x0000, 0x0000, 0x007b }, /* 00c7 LATIN CAPITAL LETTER C WITH CEDILLA */ { 0x007d, 0x0000, 0x0000, 0x007d }, /* 00c8 LATIN CAPITAL LETTER E WITH GRAVE */ { 0x007f, 0x0000, 0x0000, 0x007f }, /* 00c9 LATIN CAPITAL LETTER E WITH ACUTE */ { 0x0081, 0x0000, 0x0000, 0x0081 }, /* 00ca LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0083, 0x0000, 0x0000, 0x0083 }, /* 00cb LATIN CAPITAL LETTER E WITH DIAERESIS */ { 0x0085, 0x0000, 0x0000, 0x0085 }, /* 00cc LATIN CAPITAL LETTER I WITH GRAVE */ { 0x0087, 0x0000, 0x0000, 0x0087 }, /* 00cd LATIN CAPITAL LETTER I WITH ACUTE */ { 0x0089, 0x0000, 0x0000, 0x0089 }, /* 00ce LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ { 0x008b, 0x0000, 0x0000, 0x008b }, /* 00cf LATIN CAPITAL LETTER I WITH DIAERESIS */ { 0x008d, 0x0000, 0x0000, 0x008d }, /* 00d0 LATIN CAPITAL LETTER ETH */ { 0x008f, 0x0000, 0x0000, 0x008f }, /* 00d1 LATIN CAPITAL LETTER N WITH TILDE */ { 0x0091, 0x0000, 0x0000, 0x0091 }, /* 00d2 LATIN CAPITAL LETTER O WITH GRAVE */ { 0x0093, 0x0000, 0x0000, 0x0093 }, /* 00d3 LATIN CAPITAL LETTER O WITH ACUTE */ { 0x0095, 0x0000, 0x0000, 0x0095 }, /* 00d4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0097, 0x0000, 0x0000, 0x0097 }, /* 00d5 LATIN CAPITAL LETTER O WITH TILDE */ { 0x0099, 0x0000, 0x0000, 0x0099 }, /* 00d6 LATIN CAPITAL LETTER O WITH DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00d7 MULTIPLICATION SIGN */ { 0x009b, 0x0000, 0x0000, 0x009b }, /* 00d8 LATIN CAPITAL LETTER O WITH STROKE */ { 0x009d, 0x0000, 0x0000, 0x009d }, /* 00d9 LATIN CAPITAL LETTER U WITH GRAVE */ { 0x009f, 0x0000, 0x0000, 0x009f }, /* 00da LATIN CAPITAL LETTER U WITH ACUTE */ { 0x00a1, 0x0000, 0x0000, 0x00a1 }, /* 00db LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ { 0x00a3, 0x0000, 0x0000, 0x00a3 }, /* 00dc LATIN CAPITAL LETTER U WITH DIAERESIS */ { 0x00a5, 0x0000, 0x0000, 0x00a5 }, /* 00dd LATIN CAPITAL LETTER Y WITH ACUTE */ { 0x00a7, 0x0000, 0x0000, 0x00a7 }, /* 00de LATIN CAPITAL LETTER THORN */ { 0x0000, 0x00a9, 0x00ac, 0x00af }, /* 00df LATIN SMALL LETTER SHARP S */ { 0x0000, 0x00b2, 0x00b2, 0x0000 }, /* 00e0 LATIN SMALL LETTER A WITH GRAVE */ { 0x0000, 0x00b4, 0x00b4, 0x0000 }, /* 00e1 LATIN SMALL LETTER A WITH ACUTE */ { 0x0000, 0x00b6, 0x00b6, 0x0000 }, /* 00e2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x00b8, 0x00b8, 0x0000 }, /* 00e3 LATIN SMALL LETTER A WITH TILDE */ { 0x0000, 0x00ba, 0x00ba, 0x0000 }, /* 00e4 LATIN SMALL LETTER A WITH DIAERESIS */ { 0x0000, 0x00bc, 0x00bc, 0x0000 }, /* 00e5 LATIN SMALL LETTER A WITH RING ABOVE */ { 0x0000, 0x00be, 0x00be, 0x0000 }, /* 00e6 LATIN SMALL LETTER AE */ { 0x0000, 0x00c0, 0x00c0, 0x0000 }, /* 00e7 LATIN SMALL LETTER C WITH CEDILLA */ { 0x0000, 0x00c2, 0x00c2, 0x0000 }, /* 00e8 LATIN SMALL LETTER E WITH GRAVE */ { 0x0000, 0x00c4, 0x00c4, 0x0000 }, /* 00e9 LATIN SMALL LETTER E WITH ACUTE */ { 0x0000, 0x00c6, 0x00c6, 0x0000 }, /* 00ea LATIN SMALL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x00c8, 0x00c8, 0x0000 }, /* 00eb LATIN SMALL LETTER E WITH DIAERESIS */ { 0x0000, 0x00ca, 0x00ca, 0x0000 }, /* 00ec LATIN SMALL LETTER I WITH GRAVE */ { 0x0000, 0x00cc, 0x00cc, 0x0000 }, /* 00ed LATIN SMALL LETTER I WITH ACUTE */ { 0x0000, 0x00ce, 0x00ce, 0x0000 }, /* 00ee LATIN SMALL LETTER I WITH CIRCUMFLEX */ { 0x0000, 0x00d0, 0x00d0, 0x0000 }, /* 00ef LATIN SMALL LETTER I WITH DIAERESIS */ { 0x0000, 0x00d2, 0x00d2, 0x0000 }, /* 00f0 LATIN SMALL LETTER ETH */ { 0x0000, 0x00d4, 0x00d4, 0x0000 }, /* 00f1 LATIN SMALL LETTER N WITH TILDE */ { 0x0000, 0x00d6, 0x00d6, 0x0000 }, /* 00f2 LATIN SMALL LETTER O WITH GRAVE */ { 0x0000, 0x00d8, 0x00d8, 0x0000 }, /* 00f3 LATIN SMALL LETTER O WITH ACUTE */ { 0x0000, 0x00da, 0x00da, 0x0000 }, /* 00f4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x00dc, 0x00dc, 0x0000 }, /* 00f5 LATIN SMALL LETTER O WITH TILDE */ { 0x0000, 0x00de, 0x00de, 0x0000 }, /* 00f6 LATIN SMALL LETTER O WITH DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 00f7 DIVISION SIGN */ { 0x0000, 0x00e0, 0x00e0, 0x0000 }, /* 00f8 LATIN SMALL LETTER O WITH STROKE */ { 0x0000, 0x00e2, 0x00e2, 0x0000 }, /* 00f9 LATIN SMALL LETTER U WITH GRAVE */ { 0x0000, 0x00e4, 0x00e4, 0x0000 }, /* 00fa LATIN SMALL LETTER U WITH ACUTE */ { 0x0000, 0x00e6, 0x00e6, 0x0000 }, /* 00fb LATIN SMALL LETTER U WITH CIRCUMFLEX */ { 0x0000, 0x00e8, 0x00e8, 0x0000 }, /* 00fc LATIN SMALL LETTER U WITH DIAERESIS */ { 0x0000, 0x00ea, 0x00ea, 0x0000 }, /* 00fd LATIN SMALL LETTER Y WITH ACUTE */ { 0x0000, 0x00ec, 0x00ec, 0x0000 }, /* 00fe LATIN SMALL LETTER THORN */ { 0x0000, 0x00ee, 0x00ee, 0x0000 } /* 00ff LATIN SMALL LETTER Y WITH DIAERESIS */ }; static case_table case_pg_002[128] = { { 0x00f0, 0x0000, 0x0000, 0x00f0 }, /* 0100 LATIN CAPITAL LETTER A WITH MACRON */ { 0x0000, 0x00f2, 0x00f2, 0x0000 }, /* 0101 LATIN SMALL LETTER A WITH MACRON */ { 0x00f4, 0x0000, 0x0000, 0x00f4 }, /* 0102 LATIN CAPITAL LETTER A WITH BREVE */ { 0x0000, 0x00f6, 0x00f6, 0x0000 }, /* 0103 LATIN SMALL LETTER A WITH BREVE */ { 0x00f8, 0x0000, 0x0000, 0x00f8 }, /* 0104 LATIN CAPITAL LETTER A WITH OGONEK */ { 0x0000, 0x00fa, 0x00fa, 0x0000 }, /* 0105 LATIN SMALL LETTER A WITH OGONEK */ { 0x00fc, 0x0000, 0x0000, 0x00fc }, /* 0106 LATIN CAPITAL LETTER C WITH ACUTE */ { 0x0000, 0x00fe, 0x00fe, 0x0000 }, /* 0107 LATIN SMALL LETTER C WITH ACUTE */ { 0x0100, 0x0000, 0x0000, 0x0100 }, /* 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x0000, 0x0102, 0x0102, 0x0000 }, /* 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x0104, 0x0000, 0x0000, 0x0104 }, /* 010a LATIN CAPITAL LETTER C WITH DOT ABOVE */ { 0x0000, 0x0106, 0x0106, 0x0000 }, /* 010b LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x0108, 0x0000, 0x0000, 0x0108 }, /* 010c LATIN CAPITAL LETTER C WITH CARON */ { 0x0000, 0x010a, 0x010a, 0x0000 }, /* 010d LATIN SMALL LETTER C WITH CARON */ { 0x010c, 0x0000, 0x0000, 0x010c }, /* 010e LATIN CAPITAL LETTER D WITH CARON */ { 0x0000, 0x010e, 0x010e, 0x0000 }, /* 010f LATIN SMALL LETTER D WITH CARON */ { 0x0110, 0x0000, 0x0000, 0x0110 }, /* 0110 LATIN CAPITAL LETTER D WITH STROKE */ { 0x0000, 0x0112, 0x0112, 0x0000 }, /* 0111 LATIN SMALL LETTER D WITH STROKE */ { 0x0114, 0x0000, 0x0000, 0x0114 }, /* 0112 LATIN CAPITAL LETTER E WITH MACRON */ { 0x0000, 0x0116, 0x0116, 0x0000 }, /* 0113 LATIN SMALL LETTER E WITH MACRON */ { 0x0118, 0x0000, 0x0000, 0x0118 }, /* 0114 LATIN CAPITAL LETTER E WITH BREVE */ { 0x0000, 0x011a, 0x011a, 0x0000 }, /* 0115 LATIN SMALL LETTER E WITH BREVE */ { 0x011c, 0x0000, 0x0000, 0x011c }, /* 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ { 0x0000, 0x011e, 0x011e, 0x0000 }, /* 0117 LATIN SMALL LETTER E WITH DOT ABOVE */ { 0x0120, 0x0000, 0x0000, 0x0120 }, /* 0118 LATIN CAPITAL LETTER E WITH OGONEK */ { 0x0000, 0x0122, 0x0122, 0x0000 }, /* 0119 LATIN SMALL LETTER E WITH OGONEK */ { 0x0124, 0x0000, 0x0000, 0x0124 }, /* 011a LATIN CAPITAL LETTER E WITH CARON */ { 0x0000, 0x0126, 0x0126, 0x0000 }, /* 011b LATIN SMALL LETTER E WITH CARON */ { 0x0128, 0x0000, 0x0000, 0x0128 }, /* 011c LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ { 0x0000, 0x012a, 0x012a, 0x0000 }, /* 011d LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x012c, 0x0000, 0x0000, 0x012c }, /* 011e LATIN CAPITAL LETTER G WITH BREVE */ { 0x0000, 0x012e, 0x012e, 0x0000 }, /* 011f LATIN SMALL LETTER G WITH BREVE */ { 0x0130, 0x0000, 0x0000, 0x0130 }, /* 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE */ { 0x0000, 0x0132, 0x0132, 0x0000 }, /* 0121 LATIN SMALL LETTER G WITH DOT ABOVE */ { 0x0134, 0x0000, 0x0000, 0x0134 }, /* 0122 LATIN CAPITAL LETTER G WITH CEDILLA */ { 0x0000, 0x0136, 0x0136, 0x0000 }, /* 0123 LATIN SMALL LETTER G WITH CEDILLA */ { 0x0138, 0x0000, 0x0000, 0x0138 }, /* 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ { 0x0000, 0x013a, 0x013a, 0x0000 }, /* 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX */ { 0x013c, 0x0000, 0x0000, 0x013c }, /* 0126 LATIN CAPITAL LETTER H WITH STROKE */ { 0x0000, 0x013e, 0x013e, 0x0000 }, /* 0127 LATIN SMALL LETTER H WITH STROKE */ { 0x0140, 0x0000, 0x0000, 0x0140 }, /* 0128 LATIN CAPITAL LETTER I WITH TILDE */ { 0x0000, 0x0142, 0x0142, 0x0000 }, /* 0129 LATIN SMALL LETTER I WITH TILDE */ { 0x0144, 0x0000, 0x0000, 0x0144 }, /* 012a LATIN CAPITAL LETTER I WITH MACRON */ { 0x0000, 0x0146, 0x0146, 0x0000 }, /* 012b LATIN SMALL LETTER I WITH MACRON */ { 0x0148, 0x0000, 0x0000, 0x0148 }, /* 012c LATIN CAPITAL LETTER I WITH BREVE */ { 0x0000, 0x014a, 0x014a, 0x0000 }, /* 012d LATIN SMALL LETTER I WITH BREVE */ { 0x014c, 0x0000, 0x0000, 0x014c }, /* 012e LATIN CAPITAL LETTER I WITH OGONEK */ { 0x0000, 0x014e, 0x014e, 0x0000 }, /* 012f LATIN SMALL LETTER I WITH OGONEK */ { 0x0150, 0x0000, 0x0000, 0x0150 }, /* 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ { 0x0000, 0x0153, 0x0153, 0x0000 }, /* 0131 LATIN SMALL LETTER DOTLESS I */ { 0x0155, 0x0000, 0x0000, 0x0155 }, /* 0132 LATIN CAPITAL LIGATURE IJ */ { 0x0000, 0x0157, 0x0157, 0x0000 }, /* 0133 LATIN SMALL LIGATURE IJ */ { 0x0159, 0x0000, 0x0000, 0x0159 }, /* 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ { 0x0000, 0x015b, 0x015b, 0x0000 }, /* 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX */ { 0x015d, 0x0000, 0x0000, 0x015d }, /* 0136 LATIN CAPITAL LETTER K WITH CEDILLA */ { 0x0000, 0x015f, 0x015f, 0x0000 }, /* 0137 LATIN SMALL LETTER K WITH CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0138 LATIN SMALL LETTER KRA */ { 0x0161, 0x0000, 0x0000, 0x0161 }, /* 0139 LATIN CAPITAL LETTER L WITH ACUTE */ { 0x0000, 0x0163, 0x0163, 0x0000 }, /* 013a LATIN SMALL LETTER L WITH ACUTE */ { 0x0165, 0x0000, 0x0000, 0x0165 }, /* 013b LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x0000, 0x0167, 0x0167, 0x0000 }, /* 013c LATIN SMALL LETTER L WITH CEDILLA */ { 0x0169, 0x0000, 0x0000, 0x0169 }, /* 013d LATIN CAPITAL LETTER L WITH CARON */ { 0x0000, 0x016b, 0x016b, 0x0000 }, /* 013e LATIN SMALL LETTER L WITH CARON */ { 0x016d, 0x0000, 0x0000, 0x016d }, /* 013f LATIN CAPITAL LETTER L WITH MIDDLE DOT */ { 0x0000, 0x016f, 0x016f, 0x0000 }, /* 0140 LATIN SMALL LETTER L WITH MIDDLE DOT */ { 0x0171, 0x0000, 0x0000, 0x0171 }, /* 0141 LATIN CAPITAL LETTER L WITH STROKE */ { 0x0000, 0x0173, 0x0173, 0x0000 }, /* 0142 LATIN SMALL LETTER L WITH STROKE */ { 0x0175, 0x0000, 0x0000, 0x0175 }, /* 0143 LATIN CAPITAL LETTER N WITH ACUTE */ { 0x0000, 0x0177, 0x0177, 0x0000 }, /* 0144 LATIN SMALL LETTER N WITH ACUTE */ { 0x0179, 0x0000, 0x0000, 0x0179 }, /* 0145 LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x0000, 0x017b, 0x017b, 0x0000 }, /* 0146 LATIN SMALL LETTER N WITH CEDILLA */ { 0x017d, 0x0000, 0x0000, 0x017d }, /* 0147 LATIN CAPITAL LETTER N WITH CARON */ { 0x0000, 0x017f, 0x017f, 0x0000 }, /* 0148 LATIN SMALL LETTER N WITH CARON */ { 0x0000, 0x0181, 0x0181, 0x0184 }, /* 0149 LATIN SMALL LETTER N PRECEDED BY APOSTR */ { 0x0187, 0x0000, 0x0000, 0x0187 }, /* 014a LATIN CAPITAL LETTER ENG */ { 0x0000, 0x0189, 0x0189, 0x0000 }, /* 014b LATIN SMALL LETTER ENG */ { 0x018b, 0x0000, 0x0000, 0x018b }, /* 014c LATIN CAPITAL LETTER O WITH MACRON */ { 0x0000, 0x018d, 0x018d, 0x0000 }, /* 014d LATIN SMALL LETTER O WITH MACRON */ { 0x018f, 0x0000, 0x0000, 0x018f }, /* 014e LATIN CAPITAL LETTER O WITH BREVE */ { 0x0000, 0x0191, 0x0191, 0x0000 }, /* 014f LATIN SMALL LETTER O WITH BREVE */ { 0x0193, 0x0000, 0x0000, 0x0193 }, /* 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUT */ { 0x0000, 0x0195, 0x0195, 0x0000 }, /* 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x0197, 0x0000, 0x0000, 0x0197 }, /* 0152 LATIN CAPITAL LIGATURE OE */ { 0x0000, 0x0199, 0x0199, 0x0000 }, /* 0153 LATIN SMALL LIGATURE OE */ { 0x019b, 0x0000, 0x0000, 0x019b }, /* 0154 LATIN CAPITAL LETTER R WITH ACUTE */ { 0x0000, 0x019d, 0x019d, 0x0000 }, /* 0155 LATIN SMALL LETTER R WITH ACUTE */ { 0x019f, 0x0000, 0x0000, 0x019f }, /* 0156 LATIN CAPITAL LETTER R WITH CEDILLA */ { 0x0000, 0x01a1, 0x01a1, 0x0000 }, /* 0157 LATIN SMALL LETTER R WITH CEDILLA */ { 0x01a3, 0x0000, 0x0000, 0x01a3 }, /* 0158 LATIN CAPITAL LETTER R WITH CARON */ { 0x0000, 0x01a5, 0x01a5, 0x0000 }, /* 0159 LATIN SMALL LETTER R WITH CARON */ { 0x01a7, 0x0000, 0x0000, 0x01a7 }, /* 015a LATIN CAPITAL LETTER S WITH ACUTE */ { 0x0000, 0x01a9, 0x01a9, 0x0000 }, /* 015b LATIN SMALL LETTER S WITH ACUTE */ { 0x01ab, 0x0000, 0x0000, 0x01ab }, /* 015c LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ { 0x0000, 0x01ad, 0x01ad, 0x0000 }, /* 015d LATIN SMALL LETTER S WITH CIRCUMFLEX */ { 0x01af, 0x0000, 0x0000, 0x01af }, /* 015e LATIN CAPITAL LETTER S WITH CEDILLA */ { 0x0000, 0x01b1, 0x01b1, 0x0000 }, /* 015f LATIN SMALL LETTER S WITH CEDILLA */ { 0x01b3, 0x0000, 0x0000, 0x01b3 }, /* 0160 LATIN CAPITAL LETTER S WITH CARON */ { 0x0000, 0x01b5, 0x01b5, 0x0000 }, /* 0161 LATIN SMALL LETTER S WITH CARON */ { 0x01b7, 0x0000, 0x0000, 0x01b7 }, /* 0162 LATIN CAPITAL LETTER T WITH CEDILLA */ { 0x0000, 0x01b9, 0x01b9, 0x0000 }, /* 0163 LATIN SMALL LETTER T WITH CEDILLA */ { 0x01bb, 0x0000, 0x0000, 0x01bb }, /* 0164 LATIN CAPITAL LETTER T WITH CARON */ { 0x0000, 0x01bd, 0x01bd, 0x0000 }, /* 0165 LATIN SMALL LETTER T WITH CARON */ { 0x01bf, 0x0000, 0x0000, 0x01bf }, /* 0166 LATIN CAPITAL LETTER T WITH STROKE */ { 0x0000, 0x01c1, 0x01c1, 0x0000 }, /* 0167 LATIN SMALL LETTER T WITH STROKE */ { 0x01c3, 0x0000, 0x0000, 0x01c3 }, /* 0168 LATIN CAPITAL LETTER U WITH TILDE */ { 0x0000, 0x01c5, 0x01c5, 0x0000 }, /* 0169 LATIN SMALL LETTER U WITH TILDE */ { 0x01c7, 0x0000, 0x0000, 0x01c7 }, /* 016a LATIN CAPITAL LETTER U WITH MACRON */ { 0x0000, 0x01c9, 0x01c9, 0x0000 }, /* 016b LATIN SMALL LETTER U WITH MACRON */ { 0x01cb, 0x0000, 0x0000, 0x01cb }, /* 016c LATIN CAPITAL LETTER U WITH BREVE */ { 0x0000, 0x01cd, 0x01cd, 0x0000 }, /* 016d LATIN SMALL LETTER U WITH BREVE */ { 0x01cf, 0x0000, 0x0000, 0x01cf }, /* 016e LATIN CAPITAL LETTER U WITH RING ABOVE */ { 0x0000, 0x01d1, 0x01d1, 0x0000 }, /* 016f LATIN SMALL LETTER U WITH RING ABOVE */ { 0x01d3, 0x0000, 0x0000, 0x01d3 }, /* 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUT */ { 0x0000, 0x01d5, 0x01d5, 0x0000 }, /* 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE */ { 0x01d7, 0x0000, 0x0000, 0x01d7 }, /* 0172 LATIN CAPITAL LETTER U WITH OGONEK */ { 0x0000, 0x01d9, 0x01d9, 0x0000 }, /* 0173 LATIN SMALL LETTER U WITH OGONEK */ { 0x01db, 0x0000, 0x0000, 0x01db }, /* 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ { 0x0000, 0x01dd, 0x01dd, 0x0000 }, /* 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX */ { 0x01df, 0x0000, 0x0000, 0x01df }, /* 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ { 0x0000, 0x01e1, 0x01e1, 0x0000 }, /* 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX */ { 0x01e3, 0x0000, 0x0000, 0x01e3 }, /* 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS */ { 0x01e5, 0x0000, 0x0000, 0x01e5 }, /* 0179 LATIN CAPITAL LETTER Z WITH ACUTE */ { 0x0000, 0x01e7, 0x01e7, 0x0000 }, /* 017a LATIN SMALL LETTER Z WITH ACUTE */ { 0x01e9, 0x0000, 0x0000, 0x01e9 }, /* 017b LATIN CAPITAL LETTER Z WITH DOT ABOVE */ { 0x0000, 0x01eb, 0x01eb, 0x0000 }, /* 017c LATIN SMALL LETTER Z WITH DOT ABOVE */ { 0x01ed, 0x0000, 0x0000, 0x01ed }, /* 017d LATIN CAPITAL LETTER Z WITH CARON */ { 0x0000, 0x01ef, 0x01ef, 0x0000 }, /* 017e LATIN SMALL LETTER Z WITH CARON */ { 0x0000, 0x01f1, 0x01f1, 0x01f3 } /* 017f LATIN SMALL LETTER LONG S */ }; static case_table case_pg_003[128] = { { 0x0000, 0x01f5, 0x01f5, 0x0000 }, /* 0180 LATIN SMALL LETTER B WITH STROKE */ { 0x01f7, 0x0000, 0x0000, 0x01f7 }, /* 0181 LATIN CAPITAL LETTER B WITH HOOK */ { 0x01f9, 0x0000, 0x0000, 0x01f9 }, /* 0182 LATIN CAPITAL LETTER B WITH TOPBAR */ { 0x0000, 0x01fb, 0x01fb, 0x0000 }, /* 0183 LATIN SMALL LETTER B WITH TOPBAR */ { 0x01fd, 0x0000, 0x0000, 0x01fd }, /* 0184 LATIN CAPITAL LETTER TONE SIX */ { 0x0000, 0x01ff, 0x01ff, 0x0000 }, /* 0185 LATIN SMALL LETTER TONE SIX */ { 0x0201, 0x0000, 0x0000, 0x0201 }, /* 0186 LATIN CAPITAL LETTER OPEN O */ { 0x0203, 0x0000, 0x0000, 0x0203 }, /* 0187 LATIN CAPITAL LETTER C WITH HOOK */ { 0x0000, 0x0205, 0x0205, 0x0000 }, /* 0188 LATIN SMALL LETTER C WITH HOOK */ { 0x0207, 0x0000, 0x0000, 0x0207 }, /* 0189 LATIN CAPITAL LETTER AFRICAN D */ { 0x0209, 0x0000, 0x0000, 0x0209 }, /* 018a LATIN CAPITAL LETTER D WITH HOOK */ { 0x020b, 0x0000, 0x0000, 0x020b }, /* 018b LATIN CAPITAL LETTER D WITH TOPBAR */ { 0x0000, 0x020d, 0x020d, 0x0000 }, /* 018c LATIN SMALL LETTER D WITH TOPBAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 018d LATIN SMALL LETTER TURNED DELTA */ { 0x020f, 0x0000, 0x0000, 0x020f }, /* 018e LATIN CAPITAL LETTER REVERSED E */ { 0x0211, 0x0000, 0x0000, 0x0211 }, /* 018f LATIN CAPITAL LETTER SCHWA */ { 0x0213, 0x0000, 0x0000, 0x0213 }, /* 0190 LATIN CAPITAL LETTER OPEN E */ { 0x0215, 0x0000, 0x0000, 0x0215 }, /* 0191 LATIN CAPITAL LETTER F WITH HOOK */ { 0x0000, 0x0217, 0x0217, 0x0000 }, /* 0192 LATIN SMALL LETTER F WITH HOOK */ { 0x0219, 0x0000, 0x0000, 0x0219 }, /* 0193 LATIN CAPITAL LETTER G WITH HOOK */ { 0x021b, 0x0000, 0x0000, 0x021b }, /* 0194 LATIN CAPITAL LETTER GAMMA */ { 0x0000, 0x021d, 0x021d, 0x0000 }, /* 0195 LATIN SMALL LETTER HV */ { 0x021f, 0x0000, 0x0000, 0x021f }, /* 0196 LATIN CAPITAL LETTER IOTA */ { 0x0221, 0x0000, 0x0000, 0x0221 }, /* 0197 LATIN CAPITAL LETTER I WITH STROKE */ { 0x0223, 0x0000, 0x0000, 0x0223 }, /* 0198 LATIN CAPITAL LETTER K WITH HOOK */ { 0x0000, 0x0225, 0x0225, 0x0000 }, /* 0199 LATIN SMALL LETTER K WITH HOOK */ { 0x0000, 0x0227, 0x0227, 0x0000 }, /* 019a LATIN SMALL LETTER L WITH BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 019b LATIN SMALL LETTER LAMBDA WITH STROKE */ { 0x0229, 0x0000, 0x0000, 0x0229 }, /* 019c LATIN CAPITAL LETTER TURNED M */ { 0x022b, 0x0000, 0x0000, 0x022b }, /* 019d LATIN CAPITAL LETTER N WITH LEFT HOOK */ { 0x0000, 0x022d, 0x022d, 0x0000 }, /* 019e LATIN SMALL LETTER N WITH LONG RIGHT LE */ { 0x022f, 0x0000, 0x0000, 0x022f }, /* 019f LATIN CAPITAL LETTER O WITH MIDDLE TILD */ { 0x0231, 0x0000, 0x0000, 0x0231 }, /* 01a0 LATIN CAPITAL LETTER O WITH HORN */ { 0x0000, 0x0233, 0x0233, 0x0000 }, /* 01a1 LATIN SMALL LETTER O WITH HORN */ { 0x0235, 0x0000, 0x0000, 0x0235 }, /* 01a2 LATIN CAPITAL LETTER OI */ { 0x0000, 0x0237, 0x0237, 0x0000 }, /* 01a3 LATIN SMALL LETTER OI */ { 0x0239, 0x0000, 0x0000, 0x0239 }, /* 01a4 LATIN CAPITAL LETTER P WITH HOOK */ { 0x0000, 0x023b, 0x023b, 0x0000 }, /* 01a5 LATIN SMALL LETTER P WITH HOOK */ { 0x023d, 0x0000, 0x0000, 0x023d }, /* 01a6 LATIN LETTER YR */ { 0x023f, 0x0000, 0x0000, 0x023f }, /* 01a7 LATIN CAPITAL LETTER TONE TWO */ { 0x0000, 0x0241, 0x0241, 0x0000 }, /* 01a8 LATIN SMALL LETTER TONE TWO */ { 0x0243, 0x0000, 0x0000, 0x0243 }, /* 01a9 LATIN CAPITAL LETTER ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01aa LATIN LETTER REVERSED ESH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01ab LATIN SMALL LETTER T WITH PALATAL HOOK */ { 0x0245, 0x0000, 0x0000, 0x0245 }, /* 01ac LATIN CAPITAL LETTER T WITH HOOK */ { 0x0000, 0x0247, 0x0247, 0x0000 }, /* 01ad LATIN SMALL LETTER T WITH HOOK */ { 0x0249, 0x0000, 0x0000, 0x0249 }, /* 01ae LATIN CAPITAL LETTER T WITH RETROFLEX H */ { 0x024b, 0x0000, 0x0000, 0x024b }, /* 01af LATIN CAPITAL LETTER U WITH HORN */ { 0x0000, 0x024d, 0x024d, 0x0000 }, /* 01b0 LATIN SMALL LETTER U WITH HORN */ { 0x024f, 0x0000, 0x0000, 0x024f }, /* 01b1 LATIN CAPITAL LETTER UPSILON */ { 0x0251, 0x0000, 0x0000, 0x0251 }, /* 01b2 LATIN CAPITAL LETTER V WITH HOOK */ { 0x0253, 0x0000, 0x0000, 0x0253 }, /* 01b3 LATIN CAPITAL LETTER Y WITH HOOK */ { 0x0000, 0x0255, 0x0255, 0x0000 }, /* 01b4 LATIN SMALL LETTER Y WITH HOOK */ { 0x0257, 0x0000, 0x0000, 0x0257 }, /* 01b5 LATIN CAPITAL LETTER Z WITH STROKE */ { 0x0000, 0x0259, 0x0259, 0x0000 }, /* 01b6 LATIN SMALL LETTER Z WITH STROKE */ { 0x025b, 0x0000, 0x0000, 0x025b }, /* 01b7 LATIN CAPITAL LETTER EZH */ { 0x025d, 0x0000, 0x0000, 0x025d }, /* 01b8 LATIN CAPITAL LETTER EZH REVERSED */ { 0x0000, 0x025f, 0x025f, 0x0000 }, /* 01b9 LATIN SMALL LETTER EZH REVERSED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01ba LATIN SMALL LETTER EZH WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01bb LATIN LETTER TWO WITH STROKE */ { 0x0261, 0x0000, 0x0000, 0x0261 }, /* 01bc LATIN CAPITAL LETTER TONE FIVE */ { 0x0000, 0x0263, 0x0263, 0x0000 }, /* 01bd LATIN SMALL LETTER TONE FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01be LATIN LETTER INVERTED GLOTTAL STOP WITH */ { 0x0000, 0x0265, 0x0265, 0x0000 }, /* 01bf LATIN LETTER WYNN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c0 LATIN LETTER DENTAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c1 LATIN LETTER LATERAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c2 LATIN LETTER ALVEOLAR CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 01c3 LATIN LETTER RETROFLEX CLICK */ { 0x0267, 0x0269, 0x0000, 0x0267 }, /* 01c4 LATIN CAPITAL LETTER DZ WITH CARON */ { 0x026b, 0x026d, 0x026f, 0x026b }, /* 01c5 LATIN CAPITAL LETTER D WITH SMALL LETTE */ { 0x0000, 0x0271, 0x0273, 0x0000 }, /* 01c6 LATIN SMALL LETTER DZ WITH CARON */ { 0x0275, 0x0277, 0x0000, 0x0275 }, /* 01c7 LATIN CAPITAL LETTER LJ */ { 0x0279, 0x027b, 0x027d, 0x0279 }, /* 01c8 LATIN CAPITAL LETTER L WITH SMALL LETTE */ { 0x0000, 0x027f, 0x0281, 0x0000 }, /* 01c9 LATIN SMALL LETTER LJ */ { 0x0283, 0x0285, 0x0000, 0x0283 }, /* 01ca LATIN CAPITAL LETTER NJ */ { 0x0287, 0x0289, 0x028b, 0x0287 }, /* 01cb LATIN CAPITAL LETTER N WITH SMALL LETTE */ { 0x0000, 0x028d, 0x028f, 0x0000 }, /* 01cc LATIN SMALL LETTER NJ */ { 0x0291, 0x0000, 0x0000, 0x0291 }, /* 01cd LATIN CAPITAL LETTER A WITH CARON */ { 0x0000, 0x0293, 0x0293, 0x0000 }, /* 01ce LATIN SMALL LETTER A WITH CARON */ { 0x0295, 0x0000, 0x0000, 0x0295 }, /* 01cf LATIN CAPITAL LETTER I WITH CARON */ { 0x0000, 0x0297, 0x0297, 0x0000 }, /* 01d0 LATIN SMALL LETTER I WITH CARON */ { 0x0299, 0x0000, 0x0000, 0x0299 }, /* 01d1 LATIN CAPITAL LETTER O WITH CARON */ { 0x0000, 0x029b, 0x029b, 0x0000 }, /* 01d2 LATIN SMALL LETTER O WITH CARON */ { 0x029d, 0x0000, 0x0000, 0x029d }, /* 01d3 LATIN CAPITAL LETTER U WITH CARON */ { 0x0000, 0x029f, 0x029f, 0x0000 }, /* 01d4 LATIN SMALL LETTER U WITH CARON */ { 0x02a1, 0x0000, 0x0000, 0x02a1 }, /* 01d5 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02a3, 0x02a3, 0x0000 }, /* 01d6 LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02a5, 0x0000, 0x0000, 0x02a5 }, /* 01d7 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02a7, 0x02a7, 0x0000 }, /* 01d8 LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02a9, 0x0000, 0x0000, 0x02a9 }, /* 01d9 LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02ab, 0x02ab, 0x0000 }, /* 01da LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x02ad, 0x0000, 0x0000, 0x02ad }, /* 01db LATIN CAPITAL LETTER U WITH DIAERESIS A */ { 0x0000, 0x02af, 0x02af, 0x0000 }, /* 01dc LATIN SMALL LETTER U WITH DIAERESIS AND */ { 0x0000, 0x02b1, 0x02b1, 0x0000 }, /* 01dd LATIN SMALL LETTER TURNED E */ { 0x02b3, 0x0000, 0x0000, 0x02b3 }, /* 01de LATIN CAPITAL LETTER A WITH DIAERESIS A */ { 0x0000, 0x02b5, 0x02b5, 0x0000 }, /* 01df LATIN SMALL LETTER A WITH DIAERESIS AND */ { 0x02b7, 0x0000, 0x0000, 0x02b7 }, /* 01e0 LATIN CAPITAL LETTER A WITH DOT ABOVE A */ { 0x0000, 0x02b9, 0x02b9, 0x0000 }, /* 01e1 LATIN SMALL LETTER A WITH DOT ABOVE AND */ { 0x02bb, 0x0000, 0x0000, 0x02bb }, /* 01e2 LATIN CAPITAL LETTER AE WITH MACRON */ { 0x0000, 0x02bd, 0x02bd, 0x0000 }, /* 01e3 LATIN SMALL LETTER AE WITH MACRON */ { 0x02bf, 0x0000, 0x0000, 0x02bf }, /* 01e4 LATIN CAPITAL LETTER G WITH STROKE */ { 0x0000, 0x02c1, 0x02c1, 0x0000 }, /* 01e5 LATIN SMALL LETTER G WITH STROKE */ { 0x02c3, 0x0000, 0x0000, 0x02c3 }, /* 01e6 LATIN CAPITAL LETTER G WITH CARON */ { 0x0000, 0x02c5, 0x02c5, 0x0000 }, /* 01e7 LATIN SMALL LETTER G WITH CARON */ { 0x02c7, 0x0000, 0x0000, 0x02c7 }, /* 01e8 LATIN CAPITAL LETTER K WITH CARON */ { 0x0000, 0x02c9, 0x02c9, 0x0000 }, /* 01e9 LATIN SMALL LETTER K WITH CARON */ { 0x02cb, 0x0000, 0x0000, 0x02cb }, /* 01ea LATIN CAPITAL LETTER O WITH OGONEK */ { 0x0000, 0x02cd, 0x02cd, 0x0000 }, /* 01eb LATIN SMALL LETTER O WITH OGONEK */ { 0x02cf, 0x0000, 0x0000, 0x02cf }, /* 01ec LATIN CAPITAL LETTER O WITH OGONEK AND */ { 0x0000, 0x02d1, 0x02d1, 0x0000 }, /* 01ed LATIN SMALL LETTER O WITH OGONEK AND MA */ { 0x02d3, 0x0000, 0x0000, 0x02d3 }, /* 01ee LATIN CAPITAL LETTER EZH WITH CARON */ { 0x0000, 0x02d5, 0x02d5, 0x0000 }, /* 01ef LATIN SMALL LETTER EZH WITH CARON */ { 0x0000, 0x02d7, 0x02d7, 0x02da }, /* 01f0 LATIN SMALL LETTER J WITH CARON */ { 0x02dd, 0x02df, 0x0000, 0x02dd }, /* 01f1 LATIN CAPITAL LETTER DZ */ { 0x02e1, 0x02e3, 0x02e5, 0x02e1 }, /* 01f2 LATIN CAPITAL LETTER D WITH SMALL LETTE */ { 0x0000, 0x02e7, 0x02e9, 0x0000 }, /* 01f3 LATIN SMALL LETTER DZ */ { 0x02eb, 0x0000, 0x0000, 0x02eb }, /* 01f4 LATIN CAPITAL LETTER G WITH ACUTE */ { 0x0000, 0x02ed, 0x02ed, 0x0000 }, /* 01f5 LATIN SMALL LETTER G WITH ACUTE */ { 0x02ef, 0x0000, 0x0000, 0x02ef }, /* 01f6 LATIN CAPITAL LETTER HWAIR */ { 0x02f1, 0x0000, 0x0000, 0x02f1 }, /* 01f7 LATIN CAPITAL LETTER WYNN */ { 0x02f3, 0x0000, 0x0000, 0x02f3 }, /* 01f8 LATIN CAPITAL LETTER N WITH GRAVE */ { 0x0000, 0x02f5, 0x02f5, 0x0000 }, /* 01f9 LATIN SMALL LETTER N WITH GRAVE */ { 0x02f7, 0x0000, 0x0000, 0x02f7 }, /* 01fa LATIN CAPITAL LETTER A WITH RING ABOVE */ { 0x0000, 0x02f9, 0x02f9, 0x0000 }, /* 01fb LATIN SMALL LETTER A WITH RING ABOVE AN */ { 0x02fb, 0x0000, 0x0000, 0x02fb }, /* 01fc LATIN CAPITAL LETTER AE WITH ACUTE */ { 0x0000, 0x02fd, 0x02fd, 0x0000 }, /* 01fd LATIN SMALL LETTER AE WITH ACUTE */ { 0x02ff, 0x0000, 0x0000, 0x02ff }, /* 01fe LATIN CAPITAL LETTER O WITH STROKE AND */ { 0x0000, 0x0301, 0x0301, 0x0000 } /* 01ff LATIN SMALL LETTER O WITH STROKE AND AC */ }; static case_table case_pg_004[128] = { { 0x0303, 0x0000, 0x0000, 0x0303 }, /* 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAV */ { 0x0000, 0x0305, 0x0305, 0x0000 }, /* 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE */ { 0x0307, 0x0000, 0x0000, 0x0307 }, /* 0202 LATIN CAPITAL LETTER A WITH INVERTED BR */ { 0x0000, 0x0309, 0x0309, 0x0000 }, /* 0203 LATIN SMALL LETTER A WITH INVERTED BREV */ { 0x030b, 0x0000, 0x0000, 0x030b }, /* 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAV */ { 0x0000, 0x030d, 0x030d, 0x0000 }, /* 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE */ { 0x030f, 0x0000, 0x0000, 0x030f }, /* 0206 LATIN CAPITAL LETTER E WITH INVERTED BR */ { 0x0000, 0x0311, 0x0311, 0x0000 }, /* 0207 LATIN SMALL LETTER E WITH INVERTED BREV */ { 0x0313, 0x0000, 0x0000, 0x0313 }, /* 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAV */ { 0x0000, 0x0315, 0x0315, 0x0000 }, /* 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE */ { 0x0317, 0x0000, 0x0000, 0x0317 }, /* 020a LATIN CAPITAL LETTER I WITH INVERTED BR */ { 0x0000, 0x0319, 0x0319, 0x0000 }, /* 020b LATIN SMALL LETTER I WITH INVERTED BREV */ { 0x031b, 0x0000, 0x0000, 0x031b }, /* 020c LATIN CAPITAL LETTER O WITH DOUBLE GRAV */ { 0x0000, 0x031d, 0x031d, 0x0000 }, /* 020d LATIN SMALL LETTER O WITH DOUBLE GRAVE */ { 0x031f, 0x0000, 0x0000, 0x031f }, /* 020e LATIN CAPITAL LETTER O WITH INVERTED BR */ { 0x0000, 0x0321, 0x0321, 0x0000 }, /* 020f LATIN SMALL LETTER O WITH INVERTED BREV */ { 0x0323, 0x0000, 0x0000, 0x0323 }, /* 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAV */ { 0x0000, 0x0325, 0x0325, 0x0000 }, /* 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE */ { 0x0327, 0x0000, 0x0000, 0x0327 }, /* 0212 LATIN CAPITAL LETTER R WITH INVERTED BR */ { 0x0000, 0x0329, 0x0329, 0x0000 }, /* 0213 LATIN SMALL LETTER R WITH INVERTED BREV */ { 0x032b, 0x0000, 0x0000, 0x032b }, /* 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAV */ { 0x0000, 0x032d, 0x032d, 0x0000 }, /* 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE */ { 0x032f, 0x0000, 0x0000, 0x032f }, /* 0216 LATIN CAPITAL LETTER U WITH INVERTED BR */ { 0x0000, 0x0331, 0x0331, 0x0000 }, /* 0217 LATIN SMALL LETTER U WITH INVERTED BREV */ { 0x0333, 0x0000, 0x0000, 0x0333 }, /* 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW */ { 0x0000, 0x0335, 0x0335, 0x0000 }, /* 0219 LATIN SMALL LETTER S WITH COMMA BELOW */ { 0x0337, 0x0000, 0x0000, 0x0337 }, /* 021a LATIN CAPITAL LETTER T WITH COMMA BELOW */ { 0x0000, 0x0339, 0x0339, 0x0000 }, /* 021b LATIN SMALL LETTER T WITH COMMA BELOW */ { 0x033b, 0x0000, 0x0000, 0x033b }, /* 021c LATIN CAPITAL LETTER YOGH */ { 0x0000, 0x033d, 0x033d, 0x0000 }, /* 021d LATIN SMALL LETTER YOGH */ { 0x033f, 0x0000, 0x0000, 0x033f }, /* 021e LATIN CAPITAL LETTER H WITH CARON */ { 0x0000, 0x0341, 0x0341, 0x0000 }, /* 021f LATIN SMALL LETTER H WITH CARON */ { 0x0343, 0x0000, 0x0000, 0x0343 }, /* 0220 LATIN CAPITAL LETTER N WITH LONG RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0221 LATIN SMALL LETTER D WITH CURL */ { 0x0345, 0x0000, 0x0000, 0x0345 }, /* 0222 LATIN CAPITAL LETTER OU */ { 0x0000, 0x0347, 0x0347, 0x0000 }, /* 0223 LATIN SMALL LETTER OU */ { 0x0349, 0x0000, 0x0000, 0x0349 }, /* 0224 LATIN CAPITAL LETTER Z WITH HOOK */ { 0x0000, 0x034b, 0x034b, 0x0000 }, /* 0225 LATIN SMALL LETTER Z WITH HOOK */ { 0x034d, 0x0000, 0x0000, 0x034d }, /* 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE */ { 0x0000, 0x034f, 0x034f, 0x0000 }, /* 0227 LATIN SMALL LETTER A WITH DOT ABOVE */ { 0x0351, 0x0000, 0x0000, 0x0351 }, /* 0228 LATIN CAPITAL LETTER E WITH CEDILLA */ { 0x0000, 0x0353, 0x0353, 0x0000 }, /* 0229 LATIN SMALL LETTER E WITH CEDILLA */ { 0x0355, 0x0000, 0x0000, 0x0355 }, /* 022a LATIN CAPITAL LETTER O WITH DIAERESIS A */ { 0x0000, 0x0357, 0x0357, 0x0000 }, /* 022b LATIN SMALL LETTER O WITH DIAERESIS AND */ { 0x0359, 0x0000, 0x0000, 0x0359 }, /* 022c LATIN CAPITAL LETTER O WITH TILDE AND M */ { 0x0000, 0x035b, 0x035b, 0x0000 }, /* 022d LATIN SMALL LETTER O WITH TILDE AND MAC */ { 0x035d, 0x0000, 0x0000, 0x035d }, /* 022e LATIN CAPITAL LETTER O WITH DOT ABOVE */ { 0x0000, 0x035f, 0x035f, 0x0000 }, /* 022f LATIN SMALL LETTER O WITH DOT ABOVE */ { 0x0361, 0x0000, 0x0000, 0x0361 }, /* 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE A */ { 0x0000, 0x0363, 0x0363, 0x0000 }, /* 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND */ { 0x0365, 0x0000, 0x0000, 0x0365 }, /* 0232 LATIN CAPITAL LETTER Y WITH MACRON */ { 0x0000, 0x0367, 0x0367, 0x0000 }, /* 0233 LATIN SMALL LETTER Y WITH MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0234 LATIN SMALL LETTER L WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0235 LATIN SMALL LETTER N WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0236 LATIN SMALL LETTER T WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0237 LATIN SMALL LETTER DOTLESS J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0238 LATIN SMALL LETTER DB DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0239 LATIN SMALL LETTER QP DIGRAPH */ { 0x0369, 0x0000, 0x0000, 0x0369 }, /* 023a LATIN CAPITAL LETTER A WITH STROKE */ { 0x036b, 0x0000, 0x0000, 0x036b }, /* 023b LATIN CAPITAL LETTER C WITH STROKE */ { 0x0000, 0x036d, 0x036d, 0x0000 }, /* 023c LATIN SMALL LETTER C WITH STROKE */ { 0x036f, 0x0000, 0x0000, 0x036f }, /* 023d LATIN CAPITAL LETTER L WITH BAR */ { 0x0371, 0x0000, 0x0000, 0x0371 }, /* 023e LATIN CAPITAL LETTER T WITH DIAGONAL ST */ { 0x0000, 0x0373, 0x0373, 0x0000 }, /* 023f LATIN SMALL LETTER S WITH SWASH TAIL */ { 0x0000, 0x0375, 0x0375, 0x0000 }, /* 0240 LATIN SMALL LETTER Z WITH SWASH TAIL */ { 0x0377, 0x0000, 0x0000, 0x0377 }, /* 0241 LATIN CAPITAL LETTER GLOTTAL STOP */ { 0x0000, 0x0379, 0x0379, 0x0000 }, /* 0242 LATIN SMALL LETTER GLOTTAL STOP */ { 0x037b, 0x0000, 0x0000, 0x037b }, /* 0243 LATIN CAPITAL LETTER B WITH STROKE */ { 0x037d, 0x0000, 0x0000, 0x037d }, /* 0244 LATIN CAPITAL LETTER U BAR */ { 0x037f, 0x0000, 0x0000, 0x037f }, /* 0245 LATIN CAPITAL LETTER TURNED V */ { 0x0381, 0x0000, 0x0000, 0x0381 }, /* 0246 LATIN CAPITAL LETTER E WITH STROKE */ { 0x0000, 0x0383, 0x0383, 0x0000 }, /* 0247 LATIN SMALL LETTER E WITH STROKE */ { 0x0385, 0x0000, 0x0000, 0x0385 }, /* 0248 LATIN CAPITAL LETTER J WITH STROKE */ { 0x0000, 0x0387, 0x0387, 0x0000 }, /* 0249 LATIN SMALL LETTER J WITH STROKE */ { 0x0389, 0x0000, 0x0000, 0x0389 }, /* 024a LATIN CAPITAL LETTER SMALL Q WITH HOOK */ { 0x0000, 0x038b, 0x038b, 0x0000 }, /* 024b LATIN SMALL LETTER Q WITH HOOK TAIL */ { 0x038d, 0x0000, 0x0000, 0x038d }, /* 024c LATIN CAPITAL LETTER R WITH STROKE */ { 0x0000, 0x038f, 0x038f, 0x0000 }, /* 024d LATIN SMALL LETTER R WITH STROKE */ { 0x0391, 0x0000, 0x0000, 0x0391 }, /* 024e LATIN CAPITAL LETTER Y WITH STROKE */ { 0x0000, 0x0393, 0x0393, 0x0000 }, /* 024f LATIN SMALL LETTER Y WITH STROKE */ { 0x0000, 0x0395, 0x0395, 0x0000 }, /* 0250 LATIN SMALL LETTER TURNED A */ { 0x0000, 0x0397, 0x0397, 0x0000 }, /* 0251 LATIN SMALL LETTER ALPHA */ { 0x0000, 0x0399, 0x0399, 0x0000 }, /* 0252 LATIN SMALL LETTER TURNED ALPHA */ { 0x0000, 0x039b, 0x039b, 0x0000 }, /* 0253 LATIN SMALL LETTER B WITH HOOK */ { 0x0000, 0x039d, 0x039d, 0x0000 }, /* 0254 LATIN SMALL LETTER OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0255 LATIN SMALL LETTER C WITH CURL */ { 0x0000, 0x039f, 0x039f, 0x0000 }, /* 0256 LATIN SMALL LETTER D WITH TAIL */ { 0x0000, 0x03a1, 0x03a1, 0x0000 }, /* 0257 LATIN SMALL LETTER D WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0258 LATIN SMALL LETTER REVERSED E */ { 0x0000, 0x03a3, 0x03a3, 0x0000 }, /* 0259 LATIN SMALL LETTER SCHWA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025a LATIN SMALL LETTER SCHWA WITH HOOK */ { 0x0000, 0x03a5, 0x03a5, 0x0000 }, /* 025b LATIN SMALL LETTER OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025c LATIN SMALL LETTER REVERSED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025d LATIN SMALL LETTER REVERSED OPEN E WITH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025e LATIN SMALL LETTER CLOSED REVERSED OPEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 025f LATIN SMALL LETTER DOTLESS J WITH STROK */ { 0x0000, 0x03a7, 0x03a7, 0x0000 }, /* 0260 LATIN SMALL LETTER G WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0261 LATIN SMALL LETTER SCRIPT G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0262 LATIN LETTER SMALL CAPITAL G */ { 0x0000, 0x03a9, 0x03a9, 0x0000 }, /* 0263 LATIN SMALL LETTER GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0264 LATIN SMALL LETTER RAMS HORN */ { 0x0000, 0x03ab, 0x03ab, 0x0000 }, /* 0265 LATIN SMALL LETTER TURNED H */ { 0x0000, 0x03ad, 0x03ad, 0x0000 }, /* 0266 LATIN SMALL LETTER H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0267 LATIN SMALL LETTER HENG WITH HOOK */ { 0x0000, 0x03af, 0x03af, 0x0000 }, /* 0268 LATIN SMALL LETTER I WITH STROKE */ { 0x0000, 0x03b1, 0x03b1, 0x0000 }, /* 0269 LATIN SMALL LETTER IOTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026a LATIN LETTER SMALL CAPITAL I */ { 0x0000, 0x03b3, 0x03b3, 0x0000 }, /* 026b LATIN SMALL LETTER L WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026c LATIN SMALL LETTER L WITH BELT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026d LATIN SMALL LETTER L WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 026e LATIN SMALL LETTER LEZH */ { 0x0000, 0x03b5, 0x03b5, 0x0000 }, /* 026f LATIN SMALL LETTER TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0270 LATIN SMALL LETTER TURNED M WITH LONG L */ { 0x0000, 0x03b7, 0x03b7, 0x0000 }, /* 0271 LATIN SMALL LETTER M WITH HOOK */ { 0x0000, 0x03b9, 0x03b9, 0x0000 }, /* 0272 LATIN SMALL LETTER N WITH LEFT HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0273 LATIN SMALL LETTER N WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0274 LATIN LETTER SMALL CAPITAL N */ { 0x0000, 0x03bb, 0x03bb, 0x0000 }, /* 0275 LATIN SMALL LETTER BARRED O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0276 LATIN LETTER SMALL CAPITAL OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0277 LATIN SMALL LETTER CLOSED OMEGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0278 LATIN SMALL LETTER PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0279 LATIN SMALL LETTER TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027a LATIN SMALL LETTER TURNED R WITH LONG L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027b LATIN SMALL LETTER TURNED R WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027c LATIN SMALL LETTER R WITH LONG LEG */ { 0x0000, 0x03bd, 0x03bd, 0x0000 }, /* 027d LATIN SMALL LETTER R WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 027e LATIN SMALL LETTER R WITH FISHHOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 027f LATIN SMALL LETTER REVERSED R WITH FISH */ }; static case_table case_pg_005[128] = { { 0x0000, 0x03bf, 0x03bf, 0x0000 }, /* 0280 LATIN LETTER SMALL CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0281 LATIN LETTER SMALL CAPITAL INVERTED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0282 LATIN SMALL LETTER S WITH HOOK */ { 0x0000, 0x03c1, 0x03c1, 0x0000 }, /* 0283 LATIN SMALL LETTER ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0284 LATIN SMALL LETTER DOTLESS J WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0285 LATIN SMALL LETTER SQUAT REVERSED ESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0286 LATIN SMALL LETTER ESH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0287 LATIN SMALL LETTER TURNED T */ { 0x0000, 0x03c3, 0x03c3, 0x0000 }, /* 0288 LATIN SMALL LETTER T WITH RETROFLEX HOO */ { 0x0000, 0x03c5, 0x03c5, 0x0000 }, /* 0289 LATIN SMALL LETTER U BAR */ { 0x0000, 0x03c7, 0x03c7, 0x0000 }, /* 028a LATIN SMALL LETTER UPSILON */ { 0x0000, 0x03c9, 0x03c9, 0x0000 }, /* 028b LATIN SMALL LETTER V WITH HOOK */ { 0x0000, 0x03cb, 0x03cb, 0x0000 }, /* 028c LATIN SMALL LETTER TURNED V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028d LATIN SMALL LETTER TURNED W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028e LATIN SMALL LETTER TURNED Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 028f LATIN LETTER SMALL CAPITAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0290 LATIN SMALL LETTER Z WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0291 LATIN SMALL LETTER Z WITH CURL */ { 0x0000, 0x03cd, 0x03cd, 0x0000 }, /* 0292 LATIN SMALL LETTER EZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0293 LATIN SMALL LETTER EZH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0294 LATIN LETTER GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0295 LATIN LETTER PHARYNGEAL VOICED FRICATIV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0296 LATIN LETTER INVERTED GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0297 LATIN LETTER STRETCHED C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0298 LATIN LETTER BILABIAL CLICK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0299 LATIN LETTER SMALL CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029a LATIN SMALL LETTER CLOSED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029b LATIN LETTER SMALL CAPITAL G WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029c LATIN LETTER SMALL CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029d LATIN SMALL LETTER J WITH CROSSED-TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029e LATIN SMALL LETTER TURNED K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 029f LATIN LETTER SMALL CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a0 LATIN SMALL LETTER Q WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a1 LATIN LETTER GLOTTAL STOP WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a2 LATIN LETTER REVERSED GLOTTAL STOP WITH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a3 LATIN SMALL LETTER DZ DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a4 LATIN SMALL LETTER DEZH DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a5 LATIN SMALL LETTER DZ DIGRAPH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a6 LATIN SMALL LETTER TS DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a7 LATIN SMALL LETTER TESH DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a8 LATIN SMALL LETTER TC DIGRAPH WITH CURL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02a9 LATIN SMALL LETTER FENG DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02aa LATIN SMALL LETTER LS DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ab LATIN SMALL LETTER LZ DIGRAPH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ac LATIN LETTER BILABIAL PERCUSSIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ad LATIN LETTER BIDENTAL PERCUSSIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ae LATIN SMALL LETTER TURNED H WITH FISHHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02af LATIN SMALL LETTER TURNED H WITH FISHHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b0 MODIFIER LETTER SMALL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b1 MODIFIER LETTER SMALL H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b2 MODIFIER LETTER SMALL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b3 MODIFIER LETTER SMALL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b4 MODIFIER LETTER SMALL TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b5 MODIFIER LETTER SMALL TURNED R WITH HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b6 MODIFIER LETTER SMALL CAPITAL INVERTED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b7 MODIFIER LETTER SMALL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b8 MODIFIER LETTER SMALL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02b9 MODIFIER LETTER PRIME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ba MODIFIER LETTER DOUBLE PRIME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bb MODIFIER LETTER TURNED COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bc MODIFIER LETTER APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bd MODIFIER LETTER REVERSED COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02be MODIFIER LETTER RIGHT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02bf MODIFIER LETTER LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c0 MODIFIER LETTER GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c1 MODIFIER LETTER REVERSED GLOTTAL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c2 MODIFIER LETTER LEFT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c3 MODIFIER LETTER RIGHT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c4 MODIFIER LETTER UP ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c5 MODIFIER LETTER DOWN ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c6 MODIFIER LETTER CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c7 CARON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c8 MODIFIER LETTER VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02c9 MODIFIER LETTER MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ca MODIFIER LETTER ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cb MODIFIER LETTER GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cc MODIFIER LETTER LOW VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cd MODIFIER LETTER LOW MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ce MODIFIER LETTER LOW GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02cf MODIFIER LETTER LOW ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d0 MODIFIER LETTER TRIANGULAR COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d1 MODIFIER LETTER HALF TRIANGULAR COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d2 MODIFIER LETTER CENTRED RIGHT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d3 MODIFIER LETTER CENTRED LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d4 MODIFIER LETTER UP TACK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d5 MODIFIER LETTER DOWN TACK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d6 MODIFIER LETTER PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d7 MODIFIER LETTER MINUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d8 BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02d9 DOT ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02da RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02db OGONEK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02dc SMALL TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02dd DOUBLE ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02de MODIFIER LETTER RHOTIC HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02df MODIFIER LETTER CROSS ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e0 MODIFIER LETTER SMALL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e1 MODIFIER LETTER SMALL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e2 MODIFIER LETTER SMALL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e3 MODIFIER LETTER SMALL X */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e4 MODIFIER LETTER SMALL REVERSED GLOTTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e5 MODIFIER LETTER EXTRA-HIGH TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e6 MODIFIER LETTER HIGH TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e7 MODIFIER LETTER MID TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e8 MODIFIER LETTER LOW TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02e9 MODIFIER LETTER EXTRA-LOW TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ea MODIFIER LETTER YIN DEPARTING TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02eb MODIFIER LETTER YANG DEPARTING TONE MAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ec MODIFIER LETTER VOICING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ed MODIFIER LETTER UNASPIRATED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ee MODIFIER LETTER DOUBLE APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02ef MODIFIER LETTER LOW DOWN ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f0 MODIFIER LETTER LOW UP ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f1 MODIFIER LETTER LOW LEFT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f2 MODIFIER LETTER LOW RIGHT ARROWHEAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f3 MODIFIER LETTER LOW RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f4 MODIFIER LETTER MIDDLE GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACC */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACC */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f7 MODIFIER LETTER LOW TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f8 MODIFIER LETTER RAISED COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02f9 MODIFIER LETTER BEGIN HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fa MODIFIER LETTER END HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fb MODIFIER LETTER BEGIN LOW TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fc MODIFIER LETTER END LOW TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fd MODIFIER LETTER SHELF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 02fe MODIFIER LETTER OPEN SHELF */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 02ff MODIFIER LETTER LOW LEFT ARROW */ }; static case_table case_pg_006[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0300 COMBINING GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0301 COMBINING ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0302 COMBINING CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0303 COMBINING TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0304 COMBINING MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0305 COMBINING OVERLINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0306 COMBINING BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0307 COMBINING DOT ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0308 COMBINING DIAERESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0309 COMBINING HOOK ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030a COMBINING RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030b COMBINING DOUBLE ACUTE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030c COMBINING CARON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030d COMBINING VERTICAL LINE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030e COMBINING DOUBLE VERTICAL LINE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 030f COMBINING DOUBLE GRAVE ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0310 COMBINING CANDRABINDU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0311 COMBINING INVERTED BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0312 COMBINING TURNED COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0313 COMBINING COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0314 COMBINING REVERSED COMMA ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0315 COMBINING COMMA ABOVE RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0316 COMBINING GRAVE ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0317 COMBINING ACUTE ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0318 COMBINING LEFT TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0319 COMBINING RIGHT TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031a COMBINING LEFT ANGLE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031b COMBINING HORN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031c COMBINING LEFT HALF RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031d COMBINING UP TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031e COMBINING DOWN TACK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 031f COMBINING PLUS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0320 COMBINING MINUS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0321 COMBINING PALATALIZED HOOK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0322 COMBINING RETROFLEX HOOK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0323 COMBINING DOT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0324 COMBINING DIAERESIS BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0325 COMBINING RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0326 COMBINING COMMA BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0327 COMBINING CEDILLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0328 COMBINING OGONEK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0329 COMBINING VERTICAL LINE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032a COMBINING BRIDGE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032b COMBINING INVERTED DOUBLE ARCH BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032c COMBINING CARON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032d COMBINING CIRCUMFLEX ACCENT BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032e COMBINING BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 032f COMBINING INVERTED BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0330 COMBINING TILDE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0331 COMBINING MACRON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0332 COMBINING LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0333 COMBINING DOUBLE LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0334 COMBINING TILDE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0335 COMBINING SHORT STROKE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0336 COMBINING LONG STROKE OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0337 COMBINING SHORT SOLIDUS OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0338 COMBINING LONG SOLIDUS OVERLAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0339 COMBINING RIGHT HALF RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033a COMBINING INVERTED BRIDGE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033b COMBINING SQUARE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033c COMBINING SEAGULL BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033d COMBINING X ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033e COMBINING VERTICAL TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 033f COMBINING DOUBLE OVERLINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0340 COMBINING GRAVE TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0341 COMBINING ACUTE TONE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0342 COMBINING GREEK PERISPOMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0343 COMBINING GREEK KORONIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0344 COMBINING GREEK DIALYTIKA TONOS */ { 0x0000, 0x03cf, 0x03cf, 0x03d1 }, /* 0345 COMBINING GREEK YPOGEGRAMMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0346 COMBINING BRIDGE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0347 COMBINING EQUALS SIGN BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0348 COMBINING DOUBLE VERTICAL LINE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0349 COMBINING LEFT ANGLE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034a COMBINING NOT TILDE ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034b COMBINING HOMOTHETIC ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034c COMBINING ALMOST EQUAL TO ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034d COMBINING LEFT RIGHT ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034e COMBINING UPWARDS ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 034f COMBINING GRAPHEME JOINER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0350 COMBINING RIGHT ARROWHEAD ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0351 COMBINING LEFT HALF RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0352 COMBINING FERMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0353 COMBINING X BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0354 COMBINING LEFT ARROWHEAD BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0355 COMBINING RIGHT ARROWHEAD BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0356 COMBINING RIGHT ARROWHEAD AND UP ARROWH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0357 COMBINING RIGHT HALF RING ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0358 COMBINING DOT ABOVE RIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0359 COMBINING ASTERISK BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035a COMBINING DOUBLE RING BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035b COMBINING ZIGZAG ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035c COMBINING DOUBLE BREVE BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035d COMBINING DOUBLE BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035e COMBINING DOUBLE MACRON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 035f COMBINING DOUBLE MACRON BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0360 COMBINING DOUBLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0361 COMBINING DOUBLE INVERTED BREVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0362 COMBINING DOUBLE RIGHTWARDS ARROW BELOW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0363 COMBINING LATIN SMALL LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0364 COMBINING LATIN SMALL LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0365 COMBINING LATIN SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0366 COMBINING LATIN SMALL LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0367 COMBINING LATIN SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0368 COMBINING LATIN SMALL LETTER C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0369 COMBINING LATIN SMALL LETTER D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036a COMBINING LATIN SMALL LETTER H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036b COMBINING LATIN SMALL LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036c COMBINING LATIN SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036d COMBINING LATIN SMALL LETTER T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036e COMBINING LATIN SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 036f COMBINING LATIN SMALL LETTER X */ { 0x03d3, 0x0000, 0x0000, 0x03d3 }, /* 0370 GREEK CAPITAL LETTER HETA */ { 0x0000, 0x03d5, 0x03d5, 0x0000 }, /* 0371 GREEK SMALL LETTER HETA */ { 0x03d7, 0x0000, 0x0000, 0x03d7 }, /* 0372 GREEK CAPITAL LETTER ARCHAIC SAMPI */ { 0x0000, 0x03d9, 0x03d9, 0x0000 }, /* 0373 GREEK SMALL LETTER ARCHAIC SAMPI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0374 GREEK NUMERAL SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0375 GREEK LOWER NUMERAL SIGN */ { 0x03db, 0x0000, 0x0000, 0x03db }, /* 0376 GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ { 0x0000, 0x03dd, 0x03dd, 0x0000 }, /* 0377 GREEK SMALL LETTER PAMPHYLIAN DIGAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0378 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0379 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 037a GREEK YPOGEGRAMMENI */ { 0x0000, 0x03df, 0x03df, 0x0000 }, /* 037b GREEK SMALL REVERSED LUNATE SIGMA SYMBO */ { 0x0000, 0x03e1, 0x03e1, 0x0000 }, /* 037c GREEK SMALL DOTTED LUNATE SIGMA SYMBOL */ { 0x0000, 0x03e3, 0x03e3, 0x0000 }, /* 037d GREEK SMALL REVERSED DOTTED LUNATE SIGM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 037e GREEK QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 037f (null) */ }; static case_table case_pg_007[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0380 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0381 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0382 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0383 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0384 GREEK TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0385 GREEK DIALYTIKA TONOS */ { 0x03e5, 0x0000, 0x0000, 0x03e5 }, /* 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0387 GREEK ANO TELEIA */ { 0x03e7, 0x0000, 0x0000, 0x03e7 }, /* 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS */ { 0x03e9, 0x0000, 0x0000, 0x03e9 }, /* 0389 GREEK CAPITAL LETTER ETA WITH TONOS */ { 0x03eb, 0x0000, 0x0000, 0x03eb }, /* 038a GREEK CAPITAL LETTER IOTA WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 038b (null) */ { 0x03ed, 0x0000, 0x0000, 0x03ed }, /* 038c GREEK CAPITAL LETTER OMICRON WITH TONOS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 038d (null) */ { 0x03ef, 0x0000, 0x0000, 0x03ef }, /* 038e GREEK CAPITAL LETTER UPSILON WITH TONOS */ { 0x03f1, 0x0000, 0x0000, 0x03f1 }, /* 038f GREEK CAPITAL LETTER OMEGA WITH TONOS */ { 0x0000, 0x03f3, 0x03f3, 0x03f7 }, /* 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x03fb, 0x0000, 0x0000, 0x03fb }, /* 0391 GREEK CAPITAL LETTER ALPHA */ { 0x03fd, 0x0000, 0x0000, 0x03fd }, /* 0392 GREEK CAPITAL LETTER BETA */ { 0x03ff, 0x0000, 0x0000, 0x03ff }, /* 0393 GREEK CAPITAL LETTER GAMMA */ { 0x0401, 0x0000, 0x0000, 0x0401 }, /* 0394 GREEK CAPITAL LETTER DELTA */ { 0x0403, 0x0000, 0x0000, 0x0403 }, /* 0395 GREEK CAPITAL LETTER EPSILON */ { 0x0405, 0x0000, 0x0000, 0x0405 }, /* 0396 GREEK CAPITAL LETTER ZETA */ { 0x0407, 0x0000, 0x0000, 0x0407 }, /* 0397 GREEK CAPITAL LETTER ETA */ { 0x0409, 0x0000, 0x0000, 0x0409 }, /* 0398 GREEK CAPITAL LETTER THETA */ { 0x040b, 0x0000, 0x0000, 0x040b }, /* 0399 GREEK CAPITAL LETTER IOTA */ { 0x040d, 0x0000, 0x0000, 0x040d }, /* 039a GREEK CAPITAL LETTER KAPPA */ { 0x040f, 0x0000, 0x0000, 0x040f }, /* 039b GREEK CAPITAL LETTER LAMDA */ { 0x0411, 0x0000, 0x0000, 0x0411 }, /* 039c GREEK CAPITAL LETTER MU */ { 0x0413, 0x0000, 0x0000, 0x0413 }, /* 039d GREEK CAPITAL LETTER NU */ { 0x0415, 0x0000, 0x0000, 0x0415 }, /* 039e GREEK CAPITAL LETTER XI */ { 0x0417, 0x0000, 0x0000, 0x0417 }, /* 039f GREEK CAPITAL LETTER OMICRON */ { 0x0419, 0x0000, 0x0000, 0x0419 }, /* 03a0 GREEK CAPITAL LETTER PI */ { 0x041b, 0x0000, 0x0000, 0x041b }, /* 03a1 GREEK CAPITAL LETTER RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03a2 (null) */ { 0x041d, 0x0000, 0x0000, 0x041d }, /* 03a3 GREEK CAPITAL LETTER SIGMA */ { 0x041f, 0x0000, 0x0000, 0x041f }, /* 03a4 GREEK CAPITAL LETTER TAU */ { 0x0421, 0x0000, 0x0000, 0x0421 }, /* 03a5 GREEK CAPITAL LETTER UPSILON */ { 0x0423, 0x0000, 0x0000, 0x0423 }, /* 03a6 GREEK CAPITAL LETTER PHI */ { 0x0425, 0x0000, 0x0000, 0x0425 }, /* 03a7 GREEK CAPITAL LETTER CHI */ { 0x0427, 0x0000, 0x0000, 0x0427 }, /* 03a8 GREEK CAPITAL LETTER PSI */ { 0x0429, 0x0000, 0x0000, 0x0429 }, /* 03a9 GREEK CAPITAL LETTER OMEGA */ { 0x042b, 0x0000, 0x0000, 0x042b }, /* 03aa GREEK CAPITAL LETTER IOTA WITH DIALYTIK */ { 0x042d, 0x0000, 0x0000, 0x042d }, /* 03ab GREEK CAPITAL LETTER UPSILON WITH DIALY */ { 0x0000, 0x042f, 0x042f, 0x0000 }, /* 03ac GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x0000, 0x0431, 0x0431, 0x0000 }, /* 03ad GREEK SMALL LETTER EPSILON WITH TONOS */ { 0x0000, 0x0433, 0x0433, 0x0000 }, /* 03ae GREEK SMALL LETTER ETA WITH TONOS */ { 0x0000, 0x0435, 0x0435, 0x0000 }, /* 03af GREEK SMALL LETTER IOTA WITH TONOS */ { 0x0000, 0x0437, 0x0437, 0x043b }, /* 03b0 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x043f, 0x043f, 0x0000 }, /* 03b1 GREEK SMALL LETTER ALPHA */ { 0x0000, 0x0441, 0x0441, 0x0000 }, /* 03b2 GREEK SMALL LETTER BETA */ { 0x0000, 0x0443, 0x0443, 0x0000 }, /* 03b3 GREEK SMALL LETTER GAMMA */ { 0x0000, 0x0445, 0x0445, 0x0000 }, /* 03b4 GREEK SMALL LETTER DELTA */ { 0x0000, 0x0447, 0x0447, 0x0000 }, /* 03b5 GREEK SMALL LETTER EPSILON */ { 0x0000, 0x0449, 0x0449, 0x0000 }, /* 03b6 GREEK SMALL LETTER ZETA */ { 0x0000, 0x044b, 0x044b, 0x0000 }, /* 03b7 GREEK SMALL LETTER ETA */ { 0x0000, 0x044d, 0x044d, 0x0000 }, /* 03b8 GREEK SMALL LETTER THETA */ { 0x0000, 0x044f, 0x044f, 0x0000 }, /* 03b9 GREEK SMALL LETTER IOTA */ { 0x0000, 0x0451, 0x0451, 0x0000 }, /* 03ba GREEK SMALL LETTER KAPPA */ { 0x0000, 0x0453, 0x0453, 0x0000 }, /* 03bb GREEK SMALL LETTER LAMDA */ { 0x0000, 0x0455, 0x0455, 0x0000 }, /* 03bc GREEK SMALL LETTER MU */ { 0x0000, 0x0457, 0x0457, 0x0000 }, /* 03bd GREEK SMALL LETTER NU */ { 0x0000, 0x0459, 0x0459, 0x0000 }, /* 03be GREEK SMALL LETTER XI */ { 0x0000, 0x045b, 0x045b, 0x0000 }, /* 03bf GREEK SMALL LETTER OMICRON */ { 0x0000, 0x045d, 0x045d, 0x0000 }, /* 03c0 GREEK SMALL LETTER PI */ { 0x0000, 0x045f, 0x045f, 0x0000 }, /* 03c1 GREEK SMALL LETTER RHO */ { 0x0000, 0x0461, 0x0461, 0x0463 }, /* 03c2 GREEK SMALL LETTER FINAL SIGMA */ { 0x0000, 0x0465, 0x0465, 0x0000 }, /* 03c3 GREEK SMALL LETTER SIGMA */ { 0x0000, 0x0467, 0x0467, 0x0000 }, /* 03c4 GREEK SMALL LETTER TAU */ { 0x0000, 0x0469, 0x0469, 0x0000 }, /* 03c5 GREEK SMALL LETTER UPSILON */ { 0x0000, 0x046b, 0x046b, 0x0000 }, /* 03c6 GREEK SMALL LETTER PHI */ { 0x0000, 0x046d, 0x046d, 0x0000 }, /* 03c7 GREEK SMALL LETTER CHI */ { 0x0000, 0x046f, 0x046f, 0x0000 }, /* 03c8 GREEK SMALL LETTER PSI */ { 0x0000, 0x0471, 0x0471, 0x0000 }, /* 03c9 GREEK SMALL LETTER OMEGA */ { 0x0000, 0x0473, 0x0473, 0x0000 }, /* 03ca GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0475, 0x0475, 0x0000 }, /* 03cb GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0477, 0x0477, 0x0000 }, /* 03cc GREEK SMALL LETTER OMICRON WITH TONOS */ { 0x0000, 0x0479, 0x0479, 0x0000 }, /* 03cd GREEK SMALL LETTER UPSILON WITH TONOS */ { 0x0000, 0x047b, 0x047b, 0x0000 }, /* 03ce GREEK SMALL LETTER OMEGA WITH TONOS */ { 0x047d, 0x0000, 0x0000, 0x047d }, /* 03cf GREEK CAPITAL KAI SYMBOL */ { 0x0000, 0x047f, 0x047f, 0x0481 }, /* 03d0 GREEK BETA SYMBOL */ { 0x0000, 0x0483, 0x0483, 0x0485 }, /* 03d1 GREEK THETA SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d2 GREEK UPSILON WITH HOOK SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d3 GREEK UPSILON WITH ACUTE AND HOOK SYMBO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03d4 GREEK UPSILON WITH DIAERESIS AND HOOK S */ { 0x0000, 0x0487, 0x0487, 0x0489 }, /* 03d5 GREEK PHI SYMBOL */ { 0x0000, 0x048b, 0x048b, 0x048d }, /* 03d6 GREEK PI SYMBOL */ { 0x0000, 0x048f, 0x048f, 0x0000 }, /* 03d7 GREEK KAI SYMBOL */ { 0x0491, 0x0000, 0x0000, 0x0491 }, /* 03d8 GREEK LETTER ARCHAIC KOPPA */ { 0x0000, 0x0493, 0x0493, 0x0000 }, /* 03d9 GREEK SMALL LETTER ARCHAIC KOPPA */ { 0x0495, 0x0000, 0x0000, 0x0495 }, /* 03da GREEK LETTER STIGMA */ { 0x0000, 0x0497, 0x0497, 0x0000 }, /* 03db GREEK SMALL LETTER STIGMA */ { 0x0499, 0x0000, 0x0000, 0x0499 }, /* 03dc GREEK LETTER DIGAMMA */ { 0x0000, 0x049b, 0x049b, 0x0000 }, /* 03dd GREEK SMALL LETTER DIGAMMA */ { 0x049d, 0x0000, 0x0000, 0x049d }, /* 03de GREEK LETTER KOPPA */ { 0x0000, 0x049f, 0x049f, 0x0000 }, /* 03df GREEK SMALL LETTER KOPPA */ { 0x04a1, 0x0000, 0x0000, 0x04a1 }, /* 03e0 GREEK LETTER SAMPI */ { 0x0000, 0x04a3, 0x04a3, 0x0000 }, /* 03e1 GREEK SMALL LETTER SAMPI */ { 0x04a5, 0x0000, 0x0000, 0x04a5 }, /* 03e2 COPTIC CAPITAL LETTER SHEI */ { 0x0000, 0x04a7, 0x04a7, 0x0000 }, /* 03e3 COPTIC SMALL LETTER SHEI */ { 0x04a9, 0x0000, 0x0000, 0x04a9 }, /* 03e4 COPTIC CAPITAL LETTER FEI */ { 0x0000, 0x04ab, 0x04ab, 0x0000 }, /* 03e5 COPTIC SMALL LETTER FEI */ { 0x04ad, 0x0000, 0x0000, 0x04ad }, /* 03e6 COPTIC CAPITAL LETTER KHEI */ { 0x0000, 0x04af, 0x04af, 0x0000 }, /* 03e7 COPTIC SMALL LETTER KHEI */ { 0x04b1, 0x0000, 0x0000, 0x04b1 }, /* 03e8 COPTIC CAPITAL LETTER HORI */ { 0x0000, 0x04b3, 0x04b3, 0x0000 }, /* 03e9 COPTIC SMALL LETTER HORI */ { 0x04b5, 0x0000, 0x0000, 0x04b5 }, /* 03ea COPTIC CAPITAL LETTER GANGIA */ { 0x0000, 0x04b7, 0x04b7, 0x0000 }, /* 03eb COPTIC SMALL LETTER GANGIA */ { 0x04b9, 0x0000, 0x0000, 0x04b9 }, /* 03ec COPTIC CAPITAL LETTER SHIMA */ { 0x0000, 0x04bb, 0x04bb, 0x0000 }, /* 03ed COPTIC SMALL LETTER SHIMA */ { 0x04bd, 0x0000, 0x0000, 0x04bd }, /* 03ee COPTIC CAPITAL LETTER DEI */ { 0x0000, 0x04bf, 0x04bf, 0x0000 }, /* 03ef COPTIC SMALL LETTER DEI */ { 0x0000, 0x04c1, 0x04c1, 0x04c3 }, /* 03f0 GREEK KAPPA SYMBOL */ { 0x0000, 0x04c5, 0x04c5, 0x04c7 }, /* 03f1 GREEK RHO SYMBOL */ { 0x0000, 0x04c9, 0x04c9, 0x0000 }, /* 03f2 GREEK LUNATE SIGMA SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03f3 GREEK LETTER YOT */ { 0x04cb, 0x0000, 0x0000, 0x04cb }, /* 03f4 GREEK CAPITAL THETA SYMBOL */ { 0x0000, 0x04cd, 0x04cd, 0x04cf }, /* 03f5 GREEK LUNATE EPSILON SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03f6 GREEK REVERSED LUNATE EPSILON SYMBOL */ { 0x04d1, 0x0000, 0x0000, 0x04d1 }, /* 03f7 GREEK CAPITAL LETTER SHO */ { 0x0000, 0x04d3, 0x04d3, 0x0000 }, /* 03f8 GREEK SMALL LETTER SHO */ { 0x04d5, 0x0000, 0x0000, 0x04d5 }, /* 03f9 GREEK CAPITAL LUNATE SIGMA SYMBOL */ { 0x04d7, 0x0000, 0x0000, 0x04d7 }, /* 03fa GREEK CAPITAL LETTER SAN */ { 0x0000, 0x04d9, 0x04d9, 0x0000 }, /* 03fb GREEK SMALL LETTER SAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 03fc GREEK RHO WITH STROKE SYMBOL */ { 0x04db, 0x0000, 0x0000, 0x04db }, /* 03fd GREEK CAPITAL REVERSED LUNATE SIGMA SYM */ { 0x04dd, 0x0000, 0x0000, 0x04dd }, /* 03fe GREEK CAPITAL DOTTED LUNATE SIGMA SYMBO */ { 0x04df, 0x0000, 0x0000, 0x04df } /* 03ff GREEK CAPITAL REVERSED DOTTED LUNATE SI */ }; static case_table case_pg_008[128] = { { 0x04e1, 0x0000, 0x0000, 0x04e1 }, /* 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE */ { 0x04e3, 0x0000, 0x0000, 0x04e3 }, /* 0401 CYRILLIC CAPITAL LETTER IO */ { 0x04e5, 0x0000, 0x0000, 0x04e5 }, /* 0402 CYRILLIC CAPITAL LETTER DJE */ { 0x04e7, 0x0000, 0x0000, 0x04e7 }, /* 0403 CYRILLIC CAPITAL LETTER GJE */ { 0x04e9, 0x0000, 0x0000, 0x04e9 }, /* 0404 CYRILLIC CAPITAL LETTER UKRAINIAN IE */ { 0x04eb, 0x0000, 0x0000, 0x04eb }, /* 0405 CYRILLIC CAPITAL LETTER DZE */ { 0x04ed, 0x0000, 0x0000, 0x04ed }, /* 0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UK */ { 0x04ef, 0x0000, 0x0000, 0x04ef }, /* 0407 CYRILLIC CAPITAL LETTER YI */ { 0x04f1, 0x0000, 0x0000, 0x04f1 }, /* 0408 CYRILLIC CAPITAL LETTER JE */ { 0x04f3, 0x0000, 0x0000, 0x04f3 }, /* 0409 CYRILLIC CAPITAL LETTER LJE */ { 0x04f5, 0x0000, 0x0000, 0x04f5 }, /* 040a CYRILLIC CAPITAL LETTER NJE */ { 0x04f7, 0x0000, 0x0000, 0x04f7 }, /* 040b CYRILLIC CAPITAL LETTER TSHE */ { 0x04f9, 0x0000, 0x0000, 0x04f9 }, /* 040c CYRILLIC CAPITAL LETTER KJE */ { 0x04fb, 0x0000, 0x0000, 0x04fb }, /* 040d CYRILLIC CAPITAL LETTER I WITH GRAVE */ { 0x04fd, 0x0000, 0x0000, 0x04fd }, /* 040e CYRILLIC CAPITAL LETTER SHORT U */ { 0x04ff, 0x0000, 0x0000, 0x04ff }, /* 040f CYRILLIC CAPITAL LETTER DZHE */ { 0x0501, 0x0000, 0x0000, 0x0501 }, /* 0410 CYRILLIC CAPITAL LETTER A */ { 0x0503, 0x0000, 0x0000, 0x0503 }, /* 0411 CYRILLIC CAPITAL LETTER BE */ { 0x0505, 0x0000, 0x0000, 0x0505 }, /* 0412 CYRILLIC CAPITAL LETTER VE */ { 0x0507, 0x0000, 0x0000, 0x0507 }, /* 0413 CYRILLIC CAPITAL LETTER GHE */ { 0x0509, 0x0000, 0x0000, 0x0509 }, /* 0414 CYRILLIC CAPITAL LETTER DE */ { 0x050b, 0x0000, 0x0000, 0x050b }, /* 0415 CYRILLIC CAPITAL LETTER IE */ { 0x050d, 0x0000, 0x0000, 0x050d }, /* 0416 CYRILLIC CAPITAL LETTER ZHE */ { 0x050f, 0x0000, 0x0000, 0x050f }, /* 0417 CYRILLIC CAPITAL LETTER ZE */ { 0x0511, 0x0000, 0x0000, 0x0511 }, /* 0418 CYRILLIC CAPITAL LETTER I */ { 0x0513, 0x0000, 0x0000, 0x0513 }, /* 0419 CYRILLIC CAPITAL LETTER SHORT I */ { 0x0515, 0x0000, 0x0000, 0x0515 }, /* 041a CYRILLIC CAPITAL LETTER KA */ { 0x0517, 0x0000, 0x0000, 0x0517 }, /* 041b CYRILLIC CAPITAL LETTER EL */ { 0x0519, 0x0000, 0x0000, 0x0519 }, /* 041c CYRILLIC CAPITAL LETTER EM */ { 0x051b, 0x0000, 0x0000, 0x051b }, /* 041d CYRILLIC CAPITAL LETTER EN */ { 0x051d, 0x0000, 0x0000, 0x051d }, /* 041e CYRILLIC CAPITAL LETTER O */ { 0x051f, 0x0000, 0x0000, 0x051f }, /* 041f CYRILLIC CAPITAL LETTER PE */ { 0x0521, 0x0000, 0x0000, 0x0521 }, /* 0420 CYRILLIC CAPITAL LETTER ER */ { 0x0523, 0x0000, 0x0000, 0x0523 }, /* 0421 CYRILLIC CAPITAL LETTER ES */ { 0x0525, 0x0000, 0x0000, 0x0525 }, /* 0422 CYRILLIC CAPITAL LETTER TE */ { 0x0527, 0x0000, 0x0000, 0x0527 }, /* 0423 CYRILLIC CAPITAL LETTER U */ { 0x0529, 0x0000, 0x0000, 0x0529 }, /* 0424 CYRILLIC CAPITAL LETTER EF */ { 0x052b, 0x0000, 0x0000, 0x052b }, /* 0425 CYRILLIC CAPITAL LETTER HA */ { 0x052d, 0x0000, 0x0000, 0x052d }, /* 0426 CYRILLIC CAPITAL LETTER TSE */ { 0x052f, 0x0000, 0x0000, 0x052f }, /* 0427 CYRILLIC CAPITAL LETTER CHE */ { 0x0531, 0x0000, 0x0000, 0x0531 }, /* 0428 CYRILLIC CAPITAL LETTER SHA */ { 0x0533, 0x0000, 0x0000, 0x0533 }, /* 0429 CYRILLIC CAPITAL LETTER SHCHA */ { 0x0535, 0x0000, 0x0000, 0x0535 }, /* 042a CYRILLIC CAPITAL LETTER HARD SIGN */ { 0x0537, 0x0000, 0x0000, 0x0537 }, /* 042b CYRILLIC CAPITAL LETTER YERU */ { 0x0539, 0x0000, 0x0000, 0x0539 }, /* 042c CYRILLIC CAPITAL LETTER SOFT SIGN */ { 0x053b, 0x0000, 0x0000, 0x053b }, /* 042d CYRILLIC CAPITAL LETTER E */ { 0x053d, 0x0000, 0x0000, 0x053d }, /* 042e CYRILLIC CAPITAL LETTER YU */ { 0x053f, 0x0000, 0x0000, 0x053f }, /* 042f CYRILLIC CAPITAL LETTER YA */ { 0x0000, 0x0541, 0x0541, 0x0000 }, /* 0430 CYRILLIC SMALL LETTER A */ { 0x0000, 0x0543, 0x0543, 0x0000 }, /* 0431 CYRILLIC SMALL LETTER BE */ { 0x0000, 0x0545, 0x0545, 0x0000 }, /* 0432 CYRILLIC SMALL LETTER VE */ { 0x0000, 0x0547, 0x0547, 0x0000 }, /* 0433 CYRILLIC SMALL LETTER GHE */ { 0x0000, 0x0549, 0x0549, 0x0000 }, /* 0434 CYRILLIC SMALL LETTER DE */ { 0x0000, 0x054b, 0x054b, 0x0000 }, /* 0435 CYRILLIC SMALL LETTER IE */ { 0x0000, 0x054d, 0x054d, 0x0000 }, /* 0436 CYRILLIC SMALL LETTER ZHE */ { 0x0000, 0x054f, 0x054f, 0x0000 }, /* 0437 CYRILLIC SMALL LETTER ZE */ { 0x0000, 0x0551, 0x0551, 0x0000 }, /* 0438 CYRILLIC SMALL LETTER I */ { 0x0000, 0x0553, 0x0553, 0x0000 }, /* 0439 CYRILLIC SMALL LETTER SHORT I */ { 0x0000, 0x0555, 0x0555, 0x0000 }, /* 043a CYRILLIC SMALL LETTER KA */ { 0x0000, 0x0557, 0x0557, 0x0000 }, /* 043b CYRILLIC SMALL LETTER EL */ { 0x0000, 0x0559, 0x0559, 0x0000 }, /* 043c CYRILLIC SMALL LETTER EM */ { 0x0000, 0x055b, 0x055b, 0x0000 }, /* 043d CYRILLIC SMALL LETTER EN */ { 0x0000, 0x055d, 0x055d, 0x0000 }, /* 043e CYRILLIC SMALL LETTER O */ { 0x0000, 0x055f, 0x055f, 0x0000 }, /* 043f CYRILLIC SMALL LETTER PE */ { 0x0000, 0x0561, 0x0561, 0x0000 }, /* 0440 CYRILLIC SMALL LETTER ER */ { 0x0000, 0x0563, 0x0563, 0x0000 }, /* 0441 CYRILLIC SMALL LETTER ES */ { 0x0000, 0x0565, 0x0565, 0x0000 }, /* 0442 CYRILLIC SMALL LETTER TE */ { 0x0000, 0x0567, 0x0567, 0x0000 }, /* 0443 CYRILLIC SMALL LETTER U */ { 0x0000, 0x0569, 0x0569, 0x0000 }, /* 0444 CYRILLIC SMALL LETTER EF */ { 0x0000, 0x056b, 0x056b, 0x0000 }, /* 0445 CYRILLIC SMALL LETTER HA */ { 0x0000, 0x056d, 0x056d, 0x0000 }, /* 0446 CYRILLIC SMALL LETTER TSE */ { 0x0000, 0x056f, 0x056f, 0x0000 }, /* 0447 CYRILLIC SMALL LETTER CHE */ { 0x0000, 0x0571, 0x0571, 0x0000 }, /* 0448 CYRILLIC SMALL LETTER SHA */ { 0x0000, 0x0573, 0x0573, 0x0000 }, /* 0449 CYRILLIC SMALL LETTER SHCHA */ { 0x0000, 0x0575, 0x0575, 0x0000 }, /* 044a CYRILLIC SMALL LETTER HARD SIGN */ { 0x0000, 0x0577, 0x0577, 0x0000 }, /* 044b CYRILLIC SMALL LETTER YERU */ { 0x0000, 0x0579, 0x0579, 0x0000 }, /* 044c CYRILLIC SMALL LETTER SOFT SIGN */ { 0x0000, 0x057b, 0x057b, 0x0000 }, /* 044d CYRILLIC SMALL LETTER E */ { 0x0000, 0x057d, 0x057d, 0x0000 }, /* 044e CYRILLIC SMALL LETTER YU */ { 0x0000, 0x057f, 0x057f, 0x0000 }, /* 044f CYRILLIC SMALL LETTER YA */ { 0x0000, 0x0581, 0x0581, 0x0000 }, /* 0450 CYRILLIC SMALL LETTER IE WITH GRAVE */ { 0x0000, 0x0583, 0x0583, 0x0000 }, /* 0451 CYRILLIC SMALL LETTER IO */ { 0x0000, 0x0585, 0x0585, 0x0000 }, /* 0452 CYRILLIC SMALL LETTER DJE */ { 0x0000, 0x0587, 0x0587, 0x0000 }, /* 0453 CYRILLIC SMALL LETTER GJE */ { 0x0000, 0x0589, 0x0589, 0x0000 }, /* 0454 CYRILLIC SMALL LETTER UKRAINIAN IE */ { 0x0000, 0x058b, 0x058b, 0x0000 }, /* 0455 CYRILLIC SMALL LETTER DZE */ { 0x0000, 0x058d, 0x058d, 0x0000 }, /* 0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRA */ { 0x0000, 0x058f, 0x058f, 0x0000 }, /* 0457 CYRILLIC SMALL LETTER YI */ { 0x0000, 0x0591, 0x0591, 0x0000 }, /* 0458 CYRILLIC SMALL LETTER JE */ { 0x0000, 0x0593, 0x0593, 0x0000 }, /* 0459 CYRILLIC SMALL LETTER LJE */ { 0x0000, 0x0595, 0x0595, 0x0000 }, /* 045a CYRILLIC SMALL LETTER NJE */ { 0x0000, 0x0597, 0x0597, 0x0000 }, /* 045b CYRILLIC SMALL LETTER TSHE */ { 0x0000, 0x0599, 0x0599, 0x0000 }, /* 045c CYRILLIC SMALL LETTER KJE */ { 0x0000, 0x059b, 0x059b, 0x0000 }, /* 045d CYRILLIC SMALL LETTER I WITH GRAVE */ { 0x0000, 0x059d, 0x059d, 0x0000 }, /* 045e CYRILLIC SMALL LETTER SHORT U */ { 0x0000, 0x059f, 0x059f, 0x0000 }, /* 045f CYRILLIC SMALL LETTER DZHE */ { 0x05a1, 0x0000, 0x0000, 0x05a1 }, /* 0460 CYRILLIC CAPITAL LETTER OMEGA */ { 0x0000, 0x05a3, 0x05a3, 0x0000 }, /* 0461 CYRILLIC SMALL LETTER OMEGA */ { 0x05a5, 0x0000, 0x0000, 0x05a5 }, /* 0462 CYRILLIC CAPITAL LETTER YAT */ { 0x0000, 0x05a7, 0x05a7, 0x0000 }, /* 0463 CYRILLIC SMALL LETTER YAT */ { 0x05a9, 0x0000, 0x0000, 0x05a9 }, /* 0464 CYRILLIC CAPITAL LETTER IOTIFIED E */ { 0x0000, 0x05ab, 0x05ab, 0x0000 }, /* 0465 CYRILLIC SMALL LETTER IOTIFIED E */ { 0x05ad, 0x0000, 0x0000, 0x05ad }, /* 0466 CYRILLIC CAPITAL LETTER LITTLE YUS */ { 0x0000, 0x05af, 0x05af, 0x0000 }, /* 0467 CYRILLIC SMALL LETTER LITTLE YUS */ { 0x05b1, 0x0000, 0x0000, 0x05b1 }, /* 0468 CYRILLIC CAPITAL LETTER IOTIFIED LITTLE */ { 0x0000, 0x05b3, 0x05b3, 0x0000 }, /* 0469 CYRILLIC SMALL LETTER IOTIFIED LITTLE Y */ { 0x05b5, 0x0000, 0x0000, 0x05b5 }, /* 046a CYRILLIC CAPITAL LETTER BIG YUS */ { 0x0000, 0x05b7, 0x05b7, 0x0000 }, /* 046b CYRILLIC SMALL LETTER BIG YUS */ { 0x05b9, 0x0000, 0x0000, 0x05b9 }, /* 046c CYRILLIC CAPITAL LETTER IOTIFIED BIG YU */ { 0x0000, 0x05bb, 0x05bb, 0x0000 }, /* 046d CYRILLIC SMALL LETTER IOTIFIED BIG YUS */ { 0x05bd, 0x0000, 0x0000, 0x05bd }, /* 046e CYRILLIC CAPITAL LETTER KSI */ { 0x0000, 0x05bf, 0x05bf, 0x0000 }, /* 046f CYRILLIC SMALL LETTER KSI */ { 0x05c1, 0x0000, 0x0000, 0x05c1 }, /* 0470 CYRILLIC CAPITAL LETTER PSI */ { 0x0000, 0x05c3, 0x05c3, 0x0000 }, /* 0471 CYRILLIC SMALL LETTER PSI */ { 0x05c5, 0x0000, 0x0000, 0x05c5 }, /* 0472 CYRILLIC CAPITAL LETTER FITA */ { 0x0000, 0x05c7, 0x05c7, 0x0000 }, /* 0473 CYRILLIC SMALL LETTER FITA */ { 0x05c9, 0x0000, 0x0000, 0x05c9 }, /* 0474 CYRILLIC CAPITAL LETTER IZHITSA */ { 0x0000, 0x05cb, 0x05cb, 0x0000 }, /* 0475 CYRILLIC SMALL LETTER IZHITSA */ { 0x05cd, 0x0000, 0x0000, 0x05cd }, /* 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DO */ { 0x0000, 0x05cf, 0x05cf, 0x0000 }, /* 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUB */ { 0x05d1, 0x0000, 0x0000, 0x05d1 }, /* 0478 CYRILLIC CAPITAL LETTER UK */ { 0x0000, 0x05d3, 0x05d3, 0x0000 }, /* 0479 CYRILLIC SMALL LETTER UK */ { 0x05d5, 0x0000, 0x0000, 0x05d5 }, /* 047a CYRILLIC CAPITAL LETTER ROUND OMEGA */ { 0x0000, 0x05d7, 0x05d7, 0x0000 }, /* 047b CYRILLIC SMALL LETTER ROUND OMEGA */ { 0x05d9, 0x0000, 0x0000, 0x05d9 }, /* 047c CYRILLIC CAPITAL LETTER OMEGA WITH TITL */ { 0x0000, 0x05db, 0x05db, 0x0000 }, /* 047d CYRILLIC SMALL LETTER OMEGA WITH TITLO */ { 0x05dd, 0x0000, 0x0000, 0x05dd }, /* 047e CYRILLIC CAPITAL LETTER OT */ { 0x0000, 0x05df, 0x05df, 0x0000 } /* 047f CYRILLIC SMALL LETTER OT */ }; static case_table case_pg_009[128] = { { 0x05e1, 0x0000, 0x0000, 0x05e1 }, /* 0480 CYRILLIC CAPITAL LETTER KOPPA */ { 0x0000, 0x05e3, 0x05e3, 0x0000 }, /* 0481 CYRILLIC SMALL LETTER KOPPA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0482 CYRILLIC THOUSANDS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0483 COMBINING CYRILLIC TITLO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0484 COMBINING CYRILLIC PALATALIZATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0485 COMBINING CYRILLIC DASIA PNEUMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0486 COMBINING CYRILLIC PSILI PNEUMATA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0487 COMBINING CYRILLIC POKRYTIE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0488 COMBINING CYRILLIC HUNDRED THOUSANDS SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0489 COMBINING CYRILLIC MILLIONS SIGN */ { 0x05e5, 0x0000, 0x0000, 0x05e5 }, /* 048a CYRILLIC CAPITAL LETTER SHORT I WITH TA */ { 0x0000, 0x05e7, 0x05e7, 0x0000 }, /* 048b CYRILLIC SMALL LETTER SHORT I WITH TAIL */ { 0x05e9, 0x0000, 0x0000, 0x05e9 }, /* 048c CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ { 0x0000, 0x05eb, 0x05eb, 0x0000 }, /* 048d CYRILLIC SMALL LETTER SEMISOFT SIGN */ { 0x05ed, 0x0000, 0x0000, 0x05ed }, /* 048e CYRILLIC CAPITAL LETTER ER WITH TICK */ { 0x0000, 0x05ef, 0x05ef, 0x0000 }, /* 048f CYRILLIC SMALL LETTER ER WITH TICK */ { 0x05f1, 0x0000, 0x0000, 0x05f1 }, /* 0490 CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ { 0x0000, 0x05f3, 0x05f3, 0x0000 }, /* 0491 CYRILLIC SMALL LETTER GHE WITH UPTURN */ { 0x05f5, 0x0000, 0x0000, 0x05f5 }, /* 0492 CYRILLIC CAPITAL LETTER GHE WITH STROKE */ { 0x0000, 0x05f7, 0x05f7, 0x0000 }, /* 0493 CYRILLIC SMALL LETTER GHE WITH STROKE */ { 0x05f9, 0x0000, 0x0000, 0x05f9 }, /* 0494 CYRILLIC CAPITAL LETTER GHE WITH MIDDLE */ { 0x0000, 0x05fb, 0x05fb, 0x0000 }, /* 0495 CYRILLIC SMALL LETTER GHE WITH MIDDLE H */ { 0x05fd, 0x0000, 0x0000, 0x05fd }, /* 0496 CYRILLIC CAPITAL LETTER ZHE WITH DESCEN */ { 0x0000, 0x05ff, 0x05ff, 0x0000 }, /* 0497 CYRILLIC SMALL LETTER ZHE WITH DESCENDE */ { 0x0601, 0x0000, 0x0000, 0x0601 }, /* 0498 CYRILLIC CAPITAL LETTER ZE WITH DESCEND */ { 0x0000, 0x0603, 0x0603, 0x0000 }, /* 0499 CYRILLIC SMALL LETTER ZE WITH DESCENDER */ { 0x0605, 0x0000, 0x0000, 0x0605 }, /* 049a CYRILLIC CAPITAL LETTER KA WITH DESCEND */ { 0x0000, 0x0607, 0x0607, 0x0000 }, /* 049b CYRILLIC SMALL LETTER KA WITH DESCENDER */ { 0x0609, 0x0000, 0x0000, 0x0609 }, /* 049c CYRILLIC CAPITAL LETTER KA WITH VERTICA */ { 0x0000, 0x060b, 0x060b, 0x0000 }, /* 049d CYRILLIC SMALL LETTER KA WITH VERTICAL */ { 0x060d, 0x0000, 0x0000, 0x060d }, /* 049e CYRILLIC CAPITAL LETTER KA WITH STROKE */ { 0x0000, 0x060f, 0x060f, 0x0000 }, /* 049f CYRILLIC SMALL LETTER KA WITH STROKE */ { 0x0611, 0x0000, 0x0000, 0x0611 }, /* 04a0 CYRILLIC CAPITAL LETTER BASHKIR KA */ { 0x0000, 0x0613, 0x0613, 0x0000 }, /* 04a1 CYRILLIC SMALL LETTER BASHKIR KA */ { 0x0615, 0x0000, 0x0000, 0x0615 }, /* 04a2 CYRILLIC CAPITAL LETTER EN WITH DESCEND */ { 0x0000, 0x0617, 0x0617, 0x0000 }, /* 04a3 CYRILLIC SMALL LETTER EN WITH DESCENDER */ { 0x0619, 0x0000, 0x0000, 0x0619 }, /* 04a4 CYRILLIC CAPITAL LIGATURE EN GHE */ { 0x0000, 0x061b, 0x061b, 0x0000 }, /* 04a5 CYRILLIC SMALL LIGATURE EN GHE */ { 0x061d, 0x0000, 0x0000, 0x061d }, /* 04a6 CYRILLIC CAPITAL LETTER PE WITH MIDDLE */ { 0x0000, 0x061f, 0x061f, 0x0000 }, /* 04a7 CYRILLIC SMALL LETTER PE WITH MIDDLE HO */ { 0x0621, 0x0000, 0x0000, 0x0621 }, /* 04a8 CYRILLIC CAPITAL LETTER ABKHASIAN HA */ { 0x0000, 0x0623, 0x0623, 0x0000 }, /* 04a9 CYRILLIC SMALL LETTER ABKHASIAN HA */ { 0x0625, 0x0000, 0x0000, 0x0625 }, /* 04aa CYRILLIC CAPITAL LETTER ES WITH DESCEND */ { 0x0000, 0x0627, 0x0627, 0x0000 }, /* 04ab CYRILLIC SMALL LETTER ES WITH DESCENDER */ { 0x0629, 0x0000, 0x0000, 0x0629 }, /* 04ac CYRILLIC CAPITAL LETTER TE WITH DESCEND */ { 0x0000, 0x062b, 0x062b, 0x0000 }, /* 04ad CYRILLIC SMALL LETTER TE WITH DESCENDER */ { 0x062d, 0x0000, 0x0000, 0x062d }, /* 04ae CYRILLIC CAPITAL LETTER STRAIGHT U */ { 0x0000, 0x062f, 0x062f, 0x0000 }, /* 04af CYRILLIC SMALL LETTER STRAIGHT U */ { 0x0631, 0x0000, 0x0000, 0x0631 }, /* 04b0 CYRILLIC CAPITAL LETTER STRAIGHT U WITH */ { 0x0000, 0x0633, 0x0633, 0x0000 }, /* 04b1 CYRILLIC SMALL LETTER STRAIGHT U WITH S */ { 0x0635, 0x0000, 0x0000, 0x0635 }, /* 04b2 CYRILLIC CAPITAL LETTER HA WITH DESCEND */ { 0x0000, 0x0637, 0x0637, 0x0000 }, /* 04b3 CYRILLIC SMALL LETTER HA WITH DESCENDER */ { 0x0639, 0x0000, 0x0000, 0x0639 }, /* 04b4 CYRILLIC CAPITAL LIGATURE TE TSE */ { 0x0000, 0x063b, 0x063b, 0x0000 }, /* 04b5 CYRILLIC SMALL LIGATURE TE TSE */ { 0x063d, 0x0000, 0x0000, 0x063d }, /* 04b6 CYRILLIC CAPITAL LETTER CHE WITH DESCEN */ { 0x0000, 0x063f, 0x063f, 0x0000 }, /* 04b7 CYRILLIC SMALL LETTER CHE WITH DESCENDE */ { 0x0641, 0x0000, 0x0000, 0x0641 }, /* 04b8 CYRILLIC CAPITAL LETTER CHE WITH VERTIC */ { 0x0000, 0x0643, 0x0643, 0x0000 }, /* 04b9 CYRILLIC SMALL LETTER CHE WITH VERTICAL */ { 0x0645, 0x0000, 0x0000, 0x0645 }, /* 04ba CYRILLIC CAPITAL LETTER SHHA */ { 0x0000, 0x0647, 0x0647, 0x0000 }, /* 04bb CYRILLIC SMALL LETTER SHHA */ { 0x0649, 0x0000, 0x0000, 0x0649 }, /* 04bc CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ { 0x0000, 0x064b, 0x064b, 0x0000 }, /* 04bd CYRILLIC SMALL LETTER ABKHASIAN CHE */ { 0x064d, 0x0000, 0x0000, 0x064d }, /* 04be CYRILLIC CAPITAL LETTER ABKHASIAN CHE W */ { 0x0000, 0x064f, 0x064f, 0x0000 }, /* 04bf CYRILLIC SMALL LETTER ABKHASIAN CHE WIT */ { 0x0651, 0x0000, 0x0000, 0x0651 }, /* 04c0 CYRILLIC LETTER PALOCHKA */ { 0x0653, 0x0000, 0x0000, 0x0653 }, /* 04c1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ { 0x0000, 0x0655, 0x0655, 0x0000 }, /* 04c2 CYRILLIC SMALL LETTER ZHE WITH BREVE */ { 0x0657, 0x0000, 0x0000, 0x0657 }, /* 04c3 CYRILLIC CAPITAL LETTER KA WITH HOOK */ { 0x0000, 0x0659, 0x0659, 0x0000 }, /* 04c4 CYRILLIC SMALL LETTER KA WITH HOOK */ { 0x065b, 0x0000, 0x0000, 0x065b }, /* 04c5 CYRILLIC CAPITAL LETTER EL WITH TAIL */ { 0x0000, 0x065d, 0x065d, 0x0000 }, /* 04c6 CYRILLIC SMALL LETTER EL WITH TAIL */ { 0x065f, 0x0000, 0x0000, 0x065f }, /* 04c7 CYRILLIC CAPITAL LETTER EN WITH HOOK */ { 0x0000, 0x0661, 0x0661, 0x0000 }, /* 04c8 CYRILLIC SMALL LETTER EN WITH HOOK */ { 0x0663, 0x0000, 0x0000, 0x0663 }, /* 04c9 CYRILLIC CAPITAL LETTER EN WITH TAIL */ { 0x0000, 0x0665, 0x0665, 0x0000 }, /* 04ca CYRILLIC SMALL LETTER EN WITH TAIL */ { 0x0667, 0x0000, 0x0000, 0x0667 }, /* 04cb CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ { 0x0000, 0x0669, 0x0669, 0x0000 }, /* 04cc CYRILLIC SMALL LETTER KHAKASSIAN CHE */ { 0x066b, 0x0000, 0x0000, 0x066b }, /* 04cd CYRILLIC CAPITAL LETTER EM WITH TAIL */ { 0x0000, 0x066d, 0x066d, 0x0000 }, /* 04ce CYRILLIC SMALL LETTER EM WITH TAIL */ { 0x0000, 0x066f, 0x066f, 0x0000 }, /* 04cf CYRILLIC SMALL LETTER PALOCHKA */ { 0x0671, 0x0000, 0x0000, 0x0671 }, /* 04d0 CYRILLIC CAPITAL LETTER A WITH BREVE */ { 0x0000, 0x0673, 0x0673, 0x0000 }, /* 04d1 CYRILLIC SMALL LETTER A WITH BREVE */ { 0x0675, 0x0000, 0x0000, 0x0675 }, /* 04d2 CYRILLIC CAPITAL LETTER A WITH DIAERESI */ { 0x0000, 0x0677, 0x0677, 0x0000 }, /* 04d3 CYRILLIC SMALL LETTER A WITH DIAERESIS */ { 0x0679, 0x0000, 0x0000, 0x0679 }, /* 04d4 CYRILLIC CAPITAL LIGATURE A IE */ { 0x0000, 0x067b, 0x067b, 0x0000 }, /* 04d5 CYRILLIC SMALL LIGATURE A IE */ { 0x067d, 0x0000, 0x0000, 0x067d }, /* 04d6 CYRILLIC CAPITAL LETTER IE WITH BREVE */ { 0x0000, 0x067f, 0x067f, 0x0000 }, /* 04d7 CYRILLIC SMALL LETTER IE WITH BREVE */ { 0x0681, 0x0000, 0x0000, 0x0681 }, /* 04d8 CYRILLIC CAPITAL LETTER SCHWA */ { 0x0000, 0x0683, 0x0683, 0x0000 }, /* 04d9 CYRILLIC SMALL LETTER SCHWA */ { 0x0685, 0x0000, 0x0000, 0x0685 }, /* 04da CYRILLIC CAPITAL LETTER SCHWA WITH DIAE */ { 0x0000, 0x0687, 0x0687, 0x0000 }, /* 04db CYRILLIC SMALL LETTER SCHWA WITH DIAERE */ { 0x0689, 0x0000, 0x0000, 0x0689 }, /* 04dc CYRILLIC CAPITAL LETTER ZHE WITH DIAERE */ { 0x0000, 0x068b, 0x068b, 0x0000 }, /* 04dd CYRILLIC SMALL LETTER ZHE WITH DIAERESI */ { 0x068d, 0x0000, 0x0000, 0x068d }, /* 04de CYRILLIC CAPITAL LETTER ZE WITH DIAERES */ { 0x0000, 0x068f, 0x068f, 0x0000 }, /* 04df CYRILLIC SMALL LETTER ZE WITH DIAERESIS */ { 0x0691, 0x0000, 0x0000, 0x0691 }, /* 04e0 CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ { 0x0000, 0x0693, 0x0693, 0x0000 }, /* 04e1 CYRILLIC SMALL LETTER ABKHASIAN DZE */ { 0x0695, 0x0000, 0x0000, 0x0695 }, /* 04e2 CYRILLIC CAPITAL LETTER I WITH MACRON */ { 0x0000, 0x0697, 0x0697, 0x0000 }, /* 04e3 CYRILLIC SMALL LETTER I WITH MACRON */ { 0x0699, 0x0000, 0x0000, 0x0699 }, /* 04e4 CYRILLIC CAPITAL LETTER I WITH DIAERESI */ { 0x0000, 0x069b, 0x069b, 0x0000 }, /* 04e5 CYRILLIC SMALL LETTER I WITH DIAERESIS */ { 0x069d, 0x0000, 0x0000, 0x069d }, /* 04e6 CYRILLIC CAPITAL LETTER O WITH DIAERESI */ { 0x0000, 0x069f, 0x069f, 0x0000 }, /* 04e7 CYRILLIC SMALL LETTER O WITH DIAERESIS */ { 0x06a1, 0x0000, 0x0000, 0x06a1 }, /* 04e8 CYRILLIC CAPITAL LETTER BARRED O */ { 0x0000, 0x06a3, 0x06a3, 0x0000 }, /* 04e9 CYRILLIC SMALL LETTER BARRED O */ { 0x06a5, 0x0000, 0x0000, 0x06a5 }, /* 04ea CYRILLIC CAPITAL LETTER BARRED O WITH D */ { 0x0000, 0x06a7, 0x06a7, 0x0000 }, /* 04eb CYRILLIC SMALL LETTER BARRED O WITH DIA */ { 0x06a9, 0x0000, 0x0000, 0x06a9 }, /* 04ec CYRILLIC CAPITAL LETTER E WITH DIAERESI */ { 0x0000, 0x06ab, 0x06ab, 0x0000 }, /* 04ed CYRILLIC SMALL LETTER E WITH DIAERESIS */ { 0x06ad, 0x0000, 0x0000, 0x06ad }, /* 04ee CYRILLIC CAPITAL LETTER U WITH MACRON */ { 0x0000, 0x06af, 0x06af, 0x0000 }, /* 04ef CYRILLIC SMALL LETTER U WITH MACRON */ { 0x06b1, 0x0000, 0x0000, 0x06b1 }, /* 04f0 CYRILLIC CAPITAL LETTER U WITH DIAERESI */ { 0x0000, 0x06b3, 0x06b3, 0x0000 }, /* 04f1 CYRILLIC SMALL LETTER U WITH DIAERESIS */ { 0x06b5, 0x0000, 0x0000, 0x06b5 }, /* 04f2 CYRILLIC CAPITAL LETTER U WITH DOUBLE A */ { 0x0000, 0x06b7, 0x06b7, 0x0000 }, /* 04f3 CYRILLIC SMALL LETTER U WITH DOUBLE ACU */ { 0x06b9, 0x0000, 0x0000, 0x06b9 }, /* 04f4 CYRILLIC CAPITAL LETTER CHE WITH DIAERE */ { 0x0000, 0x06bb, 0x06bb, 0x0000 }, /* 04f5 CYRILLIC SMALL LETTER CHE WITH DIAERESI */ { 0x06bd, 0x0000, 0x0000, 0x06bd }, /* 04f6 CYRILLIC CAPITAL LETTER GHE WITH DESCEN */ { 0x0000, 0x06bf, 0x06bf, 0x0000 }, /* 04f7 CYRILLIC SMALL LETTER GHE WITH DESCENDE */ { 0x06c1, 0x0000, 0x0000, 0x06c1 }, /* 04f8 CYRILLIC CAPITAL LETTER YERU WITH DIAER */ { 0x0000, 0x06c3, 0x06c3, 0x0000 }, /* 04f9 CYRILLIC SMALL LETTER YERU WITH DIAERES */ { 0x06c5, 0x0000, 0x0000, 0x06c5 }, /* 04fa CYRILLIC CAPITAL LETTER GHE WITH STROKE */ { 0x0000, 0x06c7, 0x06c7, 0x0000 }, /* 04fb CYRILLIC SMALL LETTER GHE WITH STROKE A */ { 0x06c9, 0x0000, 0x0000, 0x06c9 }, /* 04fc CYRILLIC CAPITAL LETTER HA WITH HOOK */ { 0x0000, 0x06cb, 0x06cb, 0x0000 }, /* 04fd CYRILLIC SMALL LETTER HA WITH HOOK */ { 0x06cd, 0x0000, 0x0000, 0x06cd }, /* 04fe CYRILLIC CAPITAL LETTER HA WITH STROKE */ { 0x0000, 0x06cf, 0x06cf, 0x0000 } /* 04ff CYRILLIC SMALL LETTER HA WITH STROKE */ }; static case_table case_pg_00a[128] = { { 0x06d1, 0x0000, 0x0000, 0x06d1 }, /* 0500 CYRILLIC CAPITAL LETTER KOMI DE */ { 0x0000, 0x06d3, 0x06d3, 0x0000 }, /* 0501 CYRILLIC SMALL LETTER KOMI DE */ { 0x06d5, 0x0000, 0x0000, 0x06d5 }, /* 0502 CYRILLIC CAPITAL LETTER KOMI DJE */ { 0x0000, 0x06d7, 0x06d7, 0x0000 }, /* 0503 CYRILLIC SMALL LETTER KOMI DJE */ { 0x06d9, 0x0000, 0x0000, 0x06d9 }, /* 0504 CYRILLIC CAPITAL LETTER KOMI ZJE */ { 0x0000, 0x06db, 0x06db, 0x0000 }, /* 0505 CYRILLIC SMALL LETTER KOMI ZJE */ { 0x06dd, 0x0000, 0x0000, 0x06dd }, /* 0506 CYRILLIC CAPITAL LETTER KOMI DZJE */ { 0x0000, 0x06df, 0x06df, 0x0000 }, /* 0507 CYRILLIC SMALL LETTER KOMI DZJE */ { 0x06e1, 0x0000, 0x0000, 0x06e1 }, /* 0508 CYRILLIC CAPITAL LETTER KOMI LJE */ { 0x0000, 0x06e3, 0x06e3, 0x0000 }, /* 0509 CYRILLIC SMALL LETTER KOMI LJE */ { 0x06e5, 0x0000, 0x0000, 0x06e5 }, /* 050a CYRILLIC CAPITAL LETTER KOMI NJE */ { 0x0000, 0x06e7, 0x06e7, 0x0000 }, /* 050b CYRILLIC SMALL LETTER KOMI NJE */ { 0x06e9, 0x0000, 0x0000, 0x06e9 }, /* 050c CYRILLIC CAPITAL LETTER KOMI SJE */ { 0x0000, 0x06eb, 0x06eb, 0x0000 }, /* 050d CYRILLIC SMALL LETTER KOMI SJE */ { 0x06ed, 0x0000, 0x0000, 0x06ed }, /* 050e CYRILLIC CAPITAL LETTER KOMI TJE */ { 0x0000, 0x06ef, 0x06ef, 0x0000 }, /* 050f CYRILLIC SMALL LETTER KOMI TJE */ { 0x06f1, 0x0000, 0x0000, 0x06f1 }, /* 0510 CYRILLIC CAPITAL LETTER REVERSED ZE */ { 0x0000, 0x06f3, 0x06f3, 0x0000 }, /* 0511 CYRILLIC SMALL LETTER REVERSED ZE */ { 0x06f5, 0x0000, 0x0000, 0x06f5 }, /* 0512 CYRILLIC CAPITAL LETTER EL WITH HOOK */ { 0x0000, 0x06f7, 0x06f7, 0x0000 }, /* 0513 CYRILLIC SMALL LETTER EL WITH HOOK */ { 0x06f9, 0x0000, 0x0000, 0x06f9 }, /* 0514 CYRILLIC CAPITAL LETTER LHA */ { 0x0000, 0x06fb, 0x06fb, 0x0000 }, /* 0515 CYRILLIC SMALL LETTER LHA */ { 0x06fd, 0x0000, 0x0000, 0x06fd }, /* 0516 CYRILLIC CAPITAL LETTER RHA */ { 0x0000, 0x06ff, 0x06ff, 0x0000 }, /* 0517 CYRILLIC SMALL LETTER RHA */ { 0x0701, 0x0000, 0x0000, 0x0701 }, /* 0518 CYRILLIC CAPITAL LETTER YAE */ { 0x0000, 0x0703, 0x0703, 0x0000 }, /* 0519 CYRILLIC SMALL LETTER YAE */ { 0x0705, 0x0000, 0x0000, 0x0705 }, /* 051a CYRILLIC CAPITAL LETTER QA */ { 0x0000, 0x0707, 0x0707, 0x0000 }, /* 051b CYRILLIC SMALL LETTER QA */ { 0x0709, 0x0000, 0x0000, 0x0709 }, /* 051c CYRILLIC CAPITAL LETTER WE */ { 0x0000, 0x070b, 0x070b, 0x0000 }, /* 051d CYRILLIC SMALL LETTER WE */ { 0x070d, 0x0000, 0x0000, 0x070d }, /* 051e CYRILLIC CAPITAL LETTER ALEUT KA */ { 0x0000, 0x070f, 0x070f, 0x0000 }, /* 051f CYRILLIC SMALL LETTER ALEUT KA */ { 0x0711, 0x0000, 0x0000, 0x0711 }, /* 0520 CYRILLIC CAPITAL LETTER EL WITH MIDDLE */ { 0x0000, 0x0713, 0x0713, 0x0000 }, /* 0521 CYRILLIC SMALL LETTER EL WITH MIDDLE HO */ { 0x0715, 0x0000, 0x0000, 0x0715 }, /* 0522 CYRILLIC CAPITAL LETTER EN WITH MIDDLE */ { 0x0000, 0x0717, 0x0717, 0x0000 }, /* 0523 CYRILLIC SMALL LETTER EN WITH MIDDLE HO */ { 0x0719, 0x0000, 0x0000, 0x0719 }, /* 0524 CYRILLIC CAPITAL LETTER PE WITH DESCEND */ { 0x0000, 0x071b, 0x071b, 0x0000 }, /* 0525 CYRILLIC SMALL LETTER PE WITH DESCENDER */ { 0x071d, 0x0000, 0x0000, 0x071d }, /* 0526 CYRILLIC CAPITAL LETTER SHHA WITH DESCE */ { 0x0000, 0x071f, 0x071f, 0x0000 }, /* 0527 CYRILLIC SMALL LETTER SHHA WITH DESCEND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0528 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0529 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 052f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0530 (null) */ { 0x0721, 0x0000, 0x0000, 0x0721 }, /* 0531 ARMENIAN CAPITAL LETTER AYB */ { 0x0723, 0x0000, 0x0000, 0x0723 }, /* 0532 ARMENIAN CAPITAL LETTER BEN */ { 0x0725, 0x0000, 0x0000, 0x0725 }, /* 0533 ARMENIAN CAPITAL LETTER GIM */ { 0x0727, 0x0000, 0x0000, 0x0727 }, /* 0534 ARMENIAN CAPITAL LETTER DA */ { 0x0729, 0x0000, 0x0000, 0x0729 }, /* 0535 ARMENIAN CAPITAL LETTER ECH */ { 0x072b, 0x0000, 0x0000, 0x072b }, /* 0536 ARMENIAN CAPITAL LETTER ZA */ { 0x072d, 0x0000, 0x0000, 0x072d }, /* 0537 ARMENIAN CAPITAL LETTER EH */ { 0x072f, 0x0000, 0x0000, 0x072f }, /* 0538 ARMENIAN CAPITAL LETTER ET */ { 0x0731, 0x0000, 0x0000, 0x0731 }, /* 0539 ARMENIAN CAPITAL LETTER TO */ { 0x0733, 0x0000, 0x0000, 0x0733 }, /* 053a ARMENIAN CAPITAL LETTER ZHE */ { 0x0735, 0x0000, 0x0000, 0x0735 }, /* 053b ARMENIAN CAPITAL LETTER INI */ { 0x0737, 0x0000, 0x0000, 0x0737 }, /* 053c ARMENIAN CAPITAL LETTER LIWN */ { 0x0739, 0x0000, 0x0000, 0x0739 }, /* 053d ARMENIAN CAPITAL LETTER XEH */ { 0x073b, 0x0000, 0x0000, 0x073b }, /* 053e ARMENIAN CAPITAL LETTER CA */ { 0x073d, 0x0000, 0x0000, 0x073d }, /* 053f ARMENIAN CAPITAL LETTER KEN */ { 0x073f, 0x0000, 0x0000, 0x073f }, /* 0540 ARMENIAN CAPITAL LETTER HO */ { 0x0741, 0x0000, 0x0000, 0x0741 }, /* 0541 ARMENIAN CAPITAL LETTER JA */ { 0x0743, 0x0000, 0x0000, 0x0743 }, /* 0542 ARMENIAN CAPITAL LETTER GHAD */ { 0x0745, 0x0000, 0x0000, 0x0745 }, /* 0543 ARMENIAN CAPITAL LETTER CHEH */ { 0x0747, 0x0000, 0x0000, 0x0747 }, /* 0544 ARMENIAN CAPITAL LETTER MEN */ { 0x0749, 0x0000, 0x0000, 0x0749 }, /* 0545 ARMENIAN CAPITAL LETTER YI */ { 0x074b, 0x0000, 0x0000, 0x074b }, /* 0546 ARMENIAN CAPITAL LETTER NOW */ { 0x074d, 0x0000, 0x0000, 0x074d }, /* 0547 ARMENIAN CAPITAL LETTER SHA */ { 0x074f, 0x0000, 0x0000, 0x074f }, /* 0548 ARMENIAN CAPITAL LETTER VO */ { 0x0751, 0x0000, 0x0000, 0x0751 }, /* 0549 ARMENIAN CAPITAL LETTER CHA */ { 0x0753, 0x0000, 0x0000, 0x0753 }, /* 054a ARMENIAN CAPITAL LETTER PEH */ { 0x0755, 0x0000, 0x0000, 0x0755 }, /* 054b ARMENIAN CAPITAL LETTER JHEH */ { 0x0757, 0x0000, 0x0000, 0x0757 }, /* 054c ARMENIAN CAPITAL LETTER RA */ { 0x0759, 0x0000, 0x0000, 0x0759 }, /* 054d ARMENIAN CAPITAL LETTER SEH */ { 0x075b, 0x0000, 0x0000, 0x075b }, /* 054e ARMENIAN CAPITAL LETTER VEW */ { 0x075d, 0x0000, 0x0000, 0x075d }, /* 054f ARMENIAN CAPITAL LETTER TIWN */ { 0x075f, 0x0000, 0x0000, 0x075f }, /* 0550 ARMENIAN CAPITAL LETTER REH */ { 0x0761, 0x0000, 0x0000, 0x0761 }, /* 0551 ARMENIAN CAPITAL LETTER CO */ { 0x0763, 0x0000, 0x0000, 0x0763 }, /* 0552 ARMENIAN CAPITAL LETTER YIWN */ { 0x0765, 0x0000, 0x0000, 0x0765 }, /* 0553 ARMENIAN CAPITAL LETTER PIWR */ { 0x0767, 0x0000, 0x0000, 0x0767 }, /* 0554 ARMENIAN CAPITAL LETTER KEH */ { 0x0769, 0x0000, 0x0000, 0x0769 }, /* 0555 ARMENIAN CAPITAL LETTER OH */ { 0x076b, 0x0000, 0x0000, 0x076b }, /* 0556 ARMENIAN CAPITAL LETTER FEH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0557 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0558 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0559 ARMENIAN MODIFIER LETTER LEFT HALF RING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055a ARMENIAN APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055b ARMENIAN EMPHASIS MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055c ARMENIAN EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055d ARMENIAN COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055e ARMENIAN QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 055f ARMENIAN ABBREVIATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0560 (null) */ { 0x0000, 0x076d, 0x076d, 0x0000 }, /* 0561 ARMENIAN SMALL LETTER AYB */ { 0x0000, 0x076f, 0x076f, 0x0000 }, /* 0562 ARMENIAN SMALL LETTER BEN */ { 0x0000, 0x0771, 0x0771, 0x0000 }, /* 0563 ARMENIAN SMALL LETTER GIM */ { 0x0000, 0x0773, 0x0773, 0x0000 }, /* 0564 ARMENIAN SMALL LETTER DA */ { 0x0000, 0x0775, 0x0775, 0x0000 }, /* 0565 ARMENIAN SMALL LETTER ECH */ { 0x0000, 0x0777, 0x0777, 0x0000 }, /* 0566 ARMENIAN SMALL LETTER ZA */ { 0x0000, 0x0779, 0x0779, 0x0000 }, /* 0567 ARMENIAN SMALL LETTER EH */ { 0x0000, 0x077b, 0x077b, 0x0000 }, /* 0568 ARMENIAN SMALL LETTER ET */ { 0x0000, 0x077d, 0x077d, 0x0000 }, /* 0569 ARMENIAN SMALL LETTER TO */ { 0x0000, 0x077f, 0x077f, 0x0000 }, /* 056a ARMENIAN SMALL LETTER ZHE */ { 0x0000, 0x0781, 0x0781, 0x0000 }, /* 056b ARMENIAN SMALL LETTER INI */ { 0x0000, 0x0783, 0x0783, 0x0000 }, /* 056c ARMENIAN SMALL LETTER LIWN */ { 0x0000, 0x0785, 0x0785, 0x0000 }, /* 056d ARMENIAN SMALL LETTER XEH */ { 0x0000, 0x0787, 0x0787, 0x0000 }, /* 056e ARMENIAN SMALL LETTER CA */ { 0x0000, 0x0789, 0x0789, 0x0000 }, /* 056f ARMENIAN SMALL LETTER KEN */ { 0x0000, 0x078b, 0x078b, 0x0000 }, /* 0570 ARMENIAN SMALL LETTER HO */ { 0x0000, 0x078d, 0x078d, 0x0000 }, /* 0571 ARMENIAN SMALL LETTER JA */ { 0x0000, 0x078f, 0x078f, 0x0000 }, /* 0572 ARMENIAN SMALL LETTER GHAD */ { 0x0000, 0x0791, 0x0791, 0x0000 }, /* 0573 ARMENIAN SMALL LETTER CHEH */ { 0x0000, 0x0793, 0x0793, 0x0000 }, /* 0574 ARMENIAN SMALL LETTER MEN */ { 0x0000, 0x0795, 0x0795, 0x0000 }, /* 0575 ARMENIAN SMALL LETTER YI */ { 0x0000, 0x0797, 0x0797, 0x0000 }, /* 0576 ARMENIAN SMALL LETTER NOW */ { 0x0000, 0x0799, 0x0799, 0x0000 }, /* 0577 ARMENIAN SMALL LETTER SHA */ { 0x0000, 0x079b, 0x079b, 0x0000 }, /* 0578 ARMENIAN SMALL LETTER VO */ { 0x0000, 0x079d, 0x079d, 0x0000 }, /* 0579 ARMENIAN SMALL LETTER CHA */ { 0x0000, 0x079f, 0x079f, 0x0000 }, /* 057a ARMENIAN SMALL LETTER PEH */ { 0x0000, 0x07a1, 0x07a1, 0x0000 }, /* 057b ARMENIAN SMALL LETTER JHEH */ { 0x0000, 0x07a3, 0x07a3, 0x0000 }, /* 057c ARMENIAN SMALL LETTER RA */ { 0x0000, 0x07a5, 0x07a5, 0x0000 }, /* 057d ARMENIAN SMALL LETTER SEH */ { 0x0000, 0x07a7, 0x07a7, 0x0000 }, /* 057e ARMENIAN SMALL LETTER VEW */ { 0x0000, 0x07a9, 0x07a9, 0x0000 } /* 057f ARMENIAN SMALL LETTER TIWN */ }; static case_table case_pg_00b[128] = { { 0x0000, 0x07ab, 0x07ab, 0x0000 }, /* 0580 ARMENIAN SMALL LETTER REH */ { 0x0000, 0x07ad, 0x07ad, 0x0000 }, /* 0581 ARMENIAN SMALL LETTER CO */ { 0x0000, 0x07af, 0x07af, 0x0000 }, /* 0582 ARMENIAN SMALL LETTER YIWN */ { 0x0000, 0x07b1, 0x07b1, 0x0000 }, /* 0583 ARMENIAN SMALL LETTER PIWR */ { 0x0000, 0x07b3, 0x07b3, 0x0000 }, /* 0584 ARMENIAN SMALL LETTER KEH */ { 0x0000, 0x07b5, 0x07b5, 0x0000 }, /* 0585 ARMENIAN SMALL LETTER OH */ { 0x0000, 0x07b7, 0x07b7, 0x0000 }, /* 0586 ARMENIAN SMALL LETTER FEH */ { 0x0000, 0x07b9, 0x07bc, 0x07bf }, /* 0587 ARMENIAN SMALL LIGATURE ECH YIWN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0588 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0589 ARMENIAN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058a ARMENIAN HYPHEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 058f ARMENIAN DRAM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0590 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0591 HEBREW ACCENT ETNAHTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0592 HEBREW ACCENT SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0593 HEBREW ACCENT SHALSHELET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0594 HEBREW ACCENT ZAQEF QATAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0595 HEBREW ACCENT ZAQEF GADOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0596 HEBREW ACCENT TIPEHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0597 HEBREW ACCENT REVIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0598 HEBREW ACCENT ZARQA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0599 HEBREW ACCENT PASHTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059a HEBREW ACCENT YETIV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059b HEBREW ACCENT TEVIR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059c HEBREW ACCENT GERESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059d HEBREW ACCENT GERESH MUQDAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059e HEBREW ACCENT GERSHAYIM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 059f HEBREW ACCENT QARNEY PARA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a0 HEBREW ACCENT TELISHA GEDOLA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a1 HEBREW ACCENT PAZER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a2 HEBREW ACCENT ATNAH HAFUKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a3 HEBREW ACCENT MUNAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a4 HEBREW ACCENT MAHAPAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a5 HEBREW ACCENT MERKHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a6 HEBREW ACCENT MERKHA KEFULA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a7 HEBREW ACCENT DARGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a8 HEBREW ACCENT QADMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05a9 HEBREW ACCENT TELISHA QETANA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05aa HEBREW ACCENT YERAH BEN YOMO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ab HEBREW ACCENT OLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ac HEBREW ACCENT ILUY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ad HEBREW ACCENT DEHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ae HEBREW ACCENT ZINOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05af HEBREW MARK MASORA CIRCLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b0 HEBREW POINT SHEVA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b1 HEBREW POINT HATAF SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b2 HEBREW POINT HATAF PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b3 HEBREW POINT HATAF QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b4 HEBREW POINT HIRIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b5 HEBREW POINT TSERE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b6 HEBREW POINT SEGOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b7 HEBREW POINT PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b8 HEBREW POINT QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05b9 HEBREW POINT HOLAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ba HEBREW POINT HOLAM HASER FOR VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bb HEBREW POINT QUBUTS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bc HEBREW POINT DAGESH OR MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bd HEBREW POINT METEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05be HEBREW PUNCTUATION MAQAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05bf HEBREW POINT RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c0 HEBREW PUNCTUATION PASEQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c1 HEBREW POINT SHIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c2 HEBREW POINT SIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c3 HEBREW PUNCTUATION SOF PASUQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c4 HEBREW MARK UPPER DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c5 HEBREW MARK LOWER DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c6 HEBREW PUNCTUATION NUN HAFUKHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c7 HEBREW POINT QAMATS QATAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d0 HEBREW LETTER ALEF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d1 HEBREW LETTER BET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d2 HEBREW LETTER GIMEL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d3 HEBREW LETTER DALET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d4 HEBREW LETTER HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d5 HEBREW LETTER VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d6 HEBREW LETTER ZAYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d7 HEBREW LETTER HET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d8 HEBREW LETTER TET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05d9 HEBREW LETTER YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05da HEBREW LETTER FINAL KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05db HEBREW LETTER KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05dc HEBREW LETTER LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05dd HEBREW LETTER FINAL MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05de HEBREW LETTER MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05df HEBREW LETTER FINAL NUN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e0 HEBREW LETTER NUN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e1 HEBREW LETTER SAMEKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e2 HEBREW LETTER AYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e3 HEBREW LETTER FINAL PE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e4 HEBREW LETTER PE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e5 HEBREW LETTER FINAL TSADI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e6 HEBREW LETTER TSADI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e7 HEBREW LETTER QOF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e8 HEBREW LETTER RESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05e9 HEBREW LETTER SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ea HEBREW LETTER TAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05eb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ec (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ed (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ee (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05ef (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f0 HEBREW LIGATURE YIDDISH DOUBLE VAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f1 HEBREW LIGATURE YIDDISH VAV YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f2 HEBREW LIGATURE YIDDISH DOUBLE YOD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f3 HEBREW PUNCTUATION GERESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f4 HEBREW PUNCTUATION GERSHAYIM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05f9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fa (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 05fe (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 05ff (null) */ }; static case_table case_pg_021[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1080 MYANMAR LETTER SHAN THA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1081 MYANMAR LETTER SHAN HA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1083 MYANMAR VOWEL SIGN SHAN AA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1084 MYANMAR VOWEL SIGN SHAN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1085 MYANMAR VOWEL SIGN SHAN E ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1086 MYANMAR VOWEL SIGN SHAN FINAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1087 MYANMAR SIGN SHAN TONE-2 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1088 MYANMAR SIGN SHAN TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1089 MYANMAR SIGN SHAN TONE-5 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108a MYANMAR SIGN SHAN TONE-6 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108b MYANMAR SIGN SHAN COUNCIL TONE-2 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108c MYANMAR SIGN SHAN COUNCIL TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108d MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108e MYANMAR LETTER RUMAI PALAUNG FA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 108f MYANMAR SIGN RUMAI PALAUNG TONE-5 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1090 MYANMAR SHAN DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1091 MYANMAR SHAN DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1092 MYANMAR SHAN DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1093 MYANMAR SHAN DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1094 MYANMAR SHAN DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1095 MYANMAR SHAN DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1096 MYANMAR SHAN DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1097 MYANMAR SHAN DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1098 MYANMAR SHAN DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1099 MYANMAR SHAN DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109a MYANMAR SIGN KHAMTI TONE-1 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109b MYANMAR SIGN KHAMTI TONE-3 */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109c MYANMAR VOWEL SIGN AITON A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109d MYANMAR VOWEL SIGN AITON AI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109e MYANMAR SYMBOL SHAN ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 109f MYANMAR SYMBOL SHAN EXCLAMATION */ { 0x07c2, 0x0000, 0x0000, 0x07c2 }, /* 10a0 GEORGIAN CAPITAL LETTER AN */ { 0x07c4, 0x0000, 0x0000, 0x07c4 }, /* 10a1 GEORGIAN CAPITAL LETTER BAN */ { 0x07c6, 0x0000, 0x0000, 0x07c6 }, /* 10a2 GEORGIAN CAPITAL LETTER GAN */ { 0x07c8, 0x0000, 0x0000, 0x07c8 }, /* 10a3 GEORGIAN CAPITAL LETTER DON */ { 0x07ca, 0x0000, 0x0000, 0x07ca }, /* 10a4 GEORGIAN CAPITAL LETTER EN */ { 0x07cc, 0x0000, 0x0000, 0x07cc }, /* 10a5 GEORGIAN CAPITAL LETTER VIN */ { 0x07ce, 0x0000, 0x0000, 0x07ce }, /* 10a6 GEORGIAN CAPITAL LETTER ZEN */ { 0x07d0, 0x0000, 0x0000, 0x07d0 }, /* 10a7 GEORGIAN CAPITAL LETTER TAN */ { 0x07d2, 0x0000, 0x0000, 0x07d2 }, /* 10a8 GEORGIAN CAPITAL LETTER IN */ { 0x07d4, 0x0000, 0x0000, 0x07d4 }, /* 10a9 GEORGIAN CAPITAL LETTER KAN */ { 0x07d6, 0x0000, 0x0000, 0x07d6 }, /* 10aa GEORGIAN CAPITAL LETTER LAS */ { 0x07d8, 0x0000, 0x0000, 0x07d8 }, /* 10ab GEORGIAN CAPITAL LETTER MAN */ { 0x07da, 0x0000, 0x0000, 0x07da }, /* 10ac GEORGIAN CAPITAL LETTER NAR */ { 0x07dc, 0x0000, 0x0000, 0x07dc }, /* 10ad GEORGIAN CAPITAL LETTER ON */ { 0x07de, 0x0000, 0x0000, 0x07de }, /* 10ae GEORGIAN CAPITAL LETTER PAR */ { 0x07e0, 0x0000, 0x0000, 0x07e0 }, /* 10af GEORGIAN CAPITAL LETTER ZHAR */ { 0x07e2, 0x0000, 0x0000, 0x07e2 }, /* 10b0 GEORGIAN CAPITAL LETTER RAE */ { 0x07e4, 0x0000, 0x0000, 0x07e4 }, /* 10b1 GEORGIAN CAPITAL LETTER SAN */ { 0x07e6, 0x0000, 0x0000, 0x07e6 }, /* 10b2 GEORGIAN CAPITAL LETTER TAR */ { 0x07e8, 0x0000, 0x0000, 0x07e8 }, /* 10b3 GEORGIAN CAPITAL LETTER UN */ { 0x07ea, 0x0000, 0x0000, 0x07ea }, /* 10b4 GEORGIAN CAPITAL LETTER PHAR */ { 0x07ec, 0x0000, 0x0000, 0x07ec }, /* 10b5 GEORGIAN CAPITAL LETTER KHAR */ { 0x07ee, 0x0000, 0x0000, 0x07ee }, /* 10b6 GEORGIAN CAPITAL LETTER GHAN */ { 0x07f0, 0x0000, 0x0000, 0x07f0 }, /* 10b7 GEORGIAN CAPITAL LETTER QAR */ { 0x07f2, 0x0000, 0x0000, 0x07f2 }, /* 10b8 GEORGIAN CAPITAL LETTER SHIN */ { 0x07f4, 0x0000, 0x0000, 0x07f4 }, /* 10b9 GEORGIAN CAPITAL LETTER CHIN */ { 0x07f6, 0x0000, 0x0000, 0x07f6 }, /* 10ba GEORGIAN CAPITAL LETTER CAN */ { 0x07f8, 0x0000, 0x0000, 0x07f8 }, /* 10bb GEORGIAN CAPITAL LETTER JIL */ { 0x07fa, 0x0000, 0x0000, 0x07fa }, /* 10bc GEORGIAN CAPITAL LETTER CIL */ { 0x07fc, 0x0000, 0x0000, 0x07fc }, /* 10bd GEORGIAN CAPITAL LETTER CHAR */ { 0x07fe, 0x0000, 0x0000, 0x07fe }, /* 10be GEORGIAN CAPITAL LETTER XAN */ { 0x0800, 0x0000, 0x0000, 0x0800 }, /* 10bf GEORGIAN CAPITAL LETTER JHAN */ { 0x0802, 0x0000, 0x0000, 0x0802 }, /* 10c0 GEORGIAN CAPITAL LETTER HAE */ { 0x0804, 0x0000, 0x0000, 0x0804 }, /* 10c1 GEORGIAN CAPITAL LETTER HE */ { 0x0806, 0x0000, 0x0000, 0x0806 }, /* 10c2 GEORGIAN CAPITAL LETTER HIE */ { 0x0808, 0x0000, 0x0000, 0x0808 }, /* 10c3 GEORGIAN CAPITAL LETTER WE */ { 0x080a, 0x0000, 0x0000, 0x080a }, /* 10c4 GEORGIAN CAPITAL LETTER HAR */ { 0x080c, 0x0000, 0x0000, 0x080c }, /* 10c5 GEORGIAN CAPITAL LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c6 (null) */ { 0x080e, 0x0000, 0x0000, 0x080e }, /* 10c7 GEORGIAN CAPITAL LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cc (null) */ { 0x0810, 0x0000, 0x0000, 0x0810 }, /* 10cd GEORGIAN CAPITAL LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d0 GEORGIAN LETTER AN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d1 GEORGIAN LETTER BAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d2 GEORGIAN LETTER GAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d3 GEORGIAN LETTER DON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d4 GEORGIAN LETTER EN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d5 GEORGIAN LETTER VIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d6 GEORGIAN LETTER ZEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d7 GEORGIAN LETTER TAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d8 GEORGIAN LETTER IN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10d9 GEORGIAN LETTER KAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10da GEORGIAN LETTER LAS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10db GEORGIAN LETTER MAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10dc GEORGIAN LETTER NAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10dd GEORGIAN LETTER ON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10de GEORGIAN LETTER PAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10df GEORGIAN LETTER ZHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e0 GEORGIAN LETTER RAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e1 GEORGIAN LETTER SAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e2 GEORGIAN LETTER TAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e3 GEORGIAN LETTER UN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e4 GEORGIAN LETTER PHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e5 GEORGIAN LETTER KHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e6 GEORGIAN LETTER GHAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e7 GEORGIAN LETTER QAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e8 GEORGIAN LETTER SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10e9 GEORGIAN LETTER CHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ea GEORGIAN LETTER CAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10eb GEORGIAN LETTER JIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ec GEORGIAN LETTER CIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ed GEORGIAN LETTER CHAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ee GEORGIAN LETTER XAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10ef GEORGIAN LETTER JHAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f0 GEORGIAN LETTER HAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f1 GEORGIAN LETTER HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f2 GEORGIAN LETTER HIE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f3 GEORGIAN LETTER WE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f4 GEORGIAN LETTER HAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f5 GEORGIAN LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f6 GEORGIAN LETTER FI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f7 GEORGIAN LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f8 GEORGIAN LETTER ELIFI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10f9 GEORGIAN LETTER TURNED GAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fa GEORGIAN LETTER AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fb GEORGIAN PARAGRAPH SEPARATOR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fc MODIFIER LETTER GEORGIAN NAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fd GEORGIAN LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 10fe GEORGIAN LETTER HARD SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 10ff GEORGIAN LETTER LABIAL SIGN */ }; static case_table case_pg_03a[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d00 LATIN LETTER SMALL CAPITAL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d01 LATIN LETTER SMALL CAPITAL AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d02 LATIN SMALL LETTER TURNED AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d03 LATIN LETTER SMALL CAPITAL BARRED B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d04 LATIN LETTER SMALL CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d05 LATIN LETTER SMALL CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d06 LATIN LETTER SMALL CAPITAL ETH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d07 LATIN LETTER SMALL CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d08 LATIN SMALL LETTER TURNED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d09 LATIN SMALL LETTER TURNED I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0a LATIN LETTER SMALL CAPITAL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0b LATIN LETTER SMALL CAPITAL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0c LATIN LETTER SMALL CAPITAL L WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0d LATIN LETTER SMALL CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0e LATIN LETTER SMALL CAPITAL REVERSED N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d0f LATIN LETTER SMALL CAPITAL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d10 LATIN LETTER SMALL CAPITAL OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d11 LATIN SMALL LETTER SIDEWAYS O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d12 LATIN SMALL LETTER SIDEWAYS OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d13 LATIN SMALL LETTER SIDEWAYS O WITH STRO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d14 LATIN SMALL LETTER TURNED OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d15 LATIN LETTER SMALL CAPITAL OU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d16 LATIN SMALL LETTER TOP HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d17 LATIN SMALL LETTER BOTTOM HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d18 LATIN LETTER SMALL CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d19 LATIN LETTER SMALL CAPITAL REVERSED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1a LATIN LETTER SMALL CAPITAL TURNED R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1b LATIN LETTER SMALL CAPITAL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1c LATIN LETTER SMALL CAPITAL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1d LATIN SMALL LETTER SIDEWAYS U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1e LATIN SMALL LETTER SIDEWAYS DIAERESIZED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d1f LATIN SMALL LETTER SIDEWAYS TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d20 LATIN LETTER SMALL CAPITAL V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d21 LATIN LETTER SMALL CAPITAL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d22 LATIN LETTER SMALL CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d23 LATIN LETTER SMALL CAPITAL EZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d24 LATIN LETTER VOICED LARYNGEAL SPIRANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d25 LATIN LETTER AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d26 GREEK LETTER SMALL CAPITAL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d27 GREEK LETTER SMALL CAPITAL LAMDA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d28 GREEK LETTER SMALL CAPITAL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d29 GREEK LETTER SMALL CAPITAL RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2a GREEK LETTER SMALL CAPITAL PSI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2b CYRILLIC LETTER SMALL CAPITAL EL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2c MODIFIER LETTER CAPITAL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2d MODIFIER LETTER CAPITAL AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2e MODIFIER LETTER CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d2f MODIFIER LETTER CAPITAL BARRED B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d30 MODIFIER LETTER CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d31 MODIFIER LETTER CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d32 MODIFIER LETTER CAPITAL REVERSED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d33 MODIFIER LETTER CAPITAL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d34 MODIFIER LETTER CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d35 MODIFIER LETTER CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d36 MODIFIER LETTER CAPITAL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d37 MODIFIER LETTER CAPITAL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d38 MODIFIER LETTER CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d39 MODIFIER LETTER CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3a MODIFIER LETTER CAPITAL N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3b MODIFIER LETTER CAPITAL REVERSED N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3c MODIFIER LETTER CAPITAL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3d MODIFIER LETTER CAPITAL OU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3e MODIFIER LETTER CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d3f MODIFIER LETTER CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d40 MODIFIER LETTER CAPITAL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d41 MODIFIER LETTER CAPITAL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d42 MODIFIER LETTER CAPITAL W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d43 MODIFIER LETTER SMALL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d44 MODIFIER LETTER SMALL TURNED A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d45 MODIFIER LETTER SMALL ALPHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d46 MODIFIER LETTER SMALL TURNED AE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d47 MODIFIER LETTER SMALL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d48 MODIFIER LETTER SMALL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d49 MODIFIER LETTER SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4a MODIFIER LETTER SMALL SCHWA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4b MODIFIER LETTER SMALL OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4c MODIFIER LETTER SMALL TURNED OPEN E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4d MODIFIER LETTER SMALL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4e MODIFIER LETTER SMALL TURNED I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d4f MODIFIER LETTER SMALL K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d50 MODIFIER LETTER SMALL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d51 MODIFIER LETTER SMALL ENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d52 MODIFIER LETTER SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d53 MODIFIER LETTER SMALL OPEN O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d54 MODIFIER LETTER SMALL TOP HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d55 MODIFIER LETTER SMALL BOTTOM HALF O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d56 MODIFIER LETTER SMALL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d57 MODIFIER LETTER SMALL T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d58 MODIFIER LETTER SMALL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d59 MODIFIER LETTER SMALL SIDEWAYS U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5a MODIFIER LETTER SMALL TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5b MODIFIER LETTER SMALL V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5c MODIFIER LETTER SMALL AIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5d MODIFIER LETTER SMALL BETA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5e MODIFIER LETTER SMALL GREEK GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d5f MODIFIER LETTER SMALL DELTA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d60 MODIFIER LETTER SMALL GREEK PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d61 MODIFIER LETTER SMALL CHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d62 LATIN SUBSCRIPT SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d63 LATIN SUBSCRIPT SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d64 LATIN SUBSCRIPT SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d65 LATIN SUBSCRIPT SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d66 GREEK SUBSCRIPT SMALL LETTER BETA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d67 GREEK SUBSCRIPT SMALL LETTER GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d68 GREEK SUBSCRIPT SMALL LETTER RHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d69 GREEK SUBSCRIPT SMALL LETTER PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6a GREEK SUBSCRIPT SMALL LETTER CHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6b LATIN SMALL LETTER UE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6c LATIN SMALL LETTER B WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6d LATIN SMALL LETTER D WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6e LATIN SMALL LETTER F WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d6f LATIN SMALL LETTER M WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d70 LATIN SMALL LETTER N WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d71 LATIN SMALL LETTER P WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d72 LATIN SMALL LETTER R WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d73 LATIN SMALL LETTER R WITH FISHHOOK AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d74 LATIN SMALL LETTER S WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d75 LATIN SMALL LETTER T WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d76 LATIN SMALL LETTER Z WITH MIDDLE TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d77 LATIN SMALL LETTER TURNED G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d78 MODIFIER LETTER CYRILLIC EN */ { 0x0000, 0x0812, 0x0812, 0x0000 }, /* 1d79 LATIN SMALL LETTER INSULAR G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7a LATIN SMALL LETTER TH WITH STRIKETHROUG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7b LATIN SMALL CAPITAL LETTER I WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7c LATIN SMALL LETTER IOTA WITH STROKE */ { 0x0000, 0x0814, 0x0814, 0x0000 }, /* 1d7d LATIN SMALL LETTER P WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1d7e LATIN SMALL CAPITAL LETTER U WITH STROK */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1d7f LATIN SMALL LETTER UPSILON WITH STROKE */ }; static case_table case_pg_03c[128] = { { 0x0816, 0x0000, 0x0000, 0x0816 }, /* 1e00 LATIN CAPITAL LETTER A WITH RING BELOW */ { 0x0000, 0x0818, 0x0818, 0x0000 }, /* 1e01 LATIN SMALL LETTER A WITH RING BELOW */ { 0x081a, 0x0000, 0x0000, 0x081a }, /* 1e02 LATIN CAPITAL LETTER B WITH DOT ABOVE */ { 0x0000, 0x081c, 0x081c, 0x0000 }, /* 1e03 LATIN SMALL LETTER B WITH DOT ABOVE */ { 0x081e, 0x0000, 0x0000, 0x081e }, /* 1e04 LATIN CAPITAL LETTER B WITH DOT BELOW */ { 0x0000, 0x0820, 0x0820, 0x0000 }, /* 1e05 LATIN SMALL LETTER B WITH DOT BELOW */ { 0x0822, 0x0000, 0x0000, 0x0822 }, /* 1e06 LATIN CAPITAL LETTER B WITH LINE BELOW */ { 0x0000, 0x0824, 0x0824, 0x0000 }, /* 1e07 LATIN SMALL LETTER B WITH LINE BELOW */ { 0x0826, 0x0000, 0x0000, 0x0826 }, /* 1e08 LATIN CAPITAL LETTER C WITH CEDILLA AND */ { 0x0000, 0x0828, 0x0828, 0x0000 }, /* 1e09 LATIN SMALL LETTER C WITH CEDILLA AND A */ { 0x082a, 0x0000, 0x0000, 0x082a }, /* 1e0a LATIN CAPITAL LETTER D WITH DOT ABOVE */ { 0x0000, 0x082c, 0x082c, 0x0000 }, /* 1e0b LATIN SMALL LETTER D WITH DOT ABOVE */ { 0x082e, 0x0000, 0x0000, 0x082e }, /* 1e0c LATIN CAPITAL LETTER D WITH DOT BELOW */ { 0x0000, 0x0830, 0x0830, 0x0000 }, /* 1e0d LATIN SMALL LETTER D WITH DOT BELOW */ { 0x0832, 0x0000, 0x0000, 0x0832 }, /* 1e0e LATIN CAPITAL LETTER D WITH LINE BELOW */ { 0x0000, 0x0834, 0x0834, 0x0000 }, /* 1e0f LATIN SMALL LETTER D WITH LINE BELOW */ { 0x0836, 0x0000, 0x0000, 0x0836 }, /* 1e10 LATIN CAPITAL LETTER D WITH CEDILLA */ { 0x0000, 0x0838, 0x0838, 0x0000 }, /* 1e11 LATIN SMALL LETTER D WITH CEDILLA */ { 0x083a, 0x0000, 0x0000, 0x083a }, /* 1e12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX */ { 0x0000, 0x083c, 0x083c, 0x0000 }, /* 1e13 LATIN SMALL LETTER D WITH CIRCUMFLEX BE */ { 0x083e, 0x0000, 0x0000, 0x083e }, /* 1e14 LATIN CAPITAL LETTER E WITH MACRON AND */ { 0x0000, 0x0840, 0x0840, 0x0000 }, /* 1e15 LATIN SMALL LETTER E WITH MACRON AND GR */ { 0x0842, 0x0000, 0x0000, 0x0842 }, /* 1e16 LATIN CAPITAL LETTER E WITH MACRON AND */ { 0x0000, 0x0844, 0x0844, 0x0000 }, /* 1e17 LATIN SMALL LETTER E WITH MACRON AND AC */ { 0x0846, 0x0000, 0x0000, 0x0846 }, /* 1e18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x0848, 0x0848, 0x0000 }, /* 1e19 LATIN SMALL LETTER E WITH CIRCUMFLEX BE */ { 0x084a, 0x0000, 0x0000, 0x084a }, /* 1e1a LATIN CAPITAL LETTER E WITH TILDE BELOW */ { 0x0000, 0x084c, 0x084c, 0x0000 }, /* 1e1b LATIN SMALL LETTER E WITH TILDE BELOW */ { 0x084e, 0x0000, 0x0000, 0x084e }, /* 1e1c LATIN CAPITAL LETTER E WITH CEDILLA AND */ { 0x0000, 0x0850, 0x0850, 0x0000 }, /* 1e1d LATIN SMALL LETTER E WITH CEDILLA AND B */ { 0x0852, 0x0000, 0x0000, 0x0852 }, /* 1e1e LATIN CAPITAL LETTER F WITH DOT ABOVE */ { 0x0000, 0x0854, 0x0854, 0x0000 }, /* 1e1f LATIN SMALL LETTER F WITH DOT ABOVE */ { 0x0856, 0x0000, 0x0000, 0x0856 }, /* 1e20 LATIN CAPITAL LETTER G WITH MACRON */ { 0x0000, 0x0858, 0x0858, 0x0000 }, /* 1e21 LATIN SMALL LETTER G WITH MACRON */ { 0x085a, 0x0000, 0x0000, 0x085a }, /* 1e22 LATIN CAPITAL LETTER H WITH DOT ABOVE */ { 0x0000, 0x085c, 0x085c, 0x0000 }, /* 1e23 LATIN SMALL LETTER H WITH DOT ABOVE */ { 0x085e, 0x0000, 0x0000, 0x085e }, /* 1e24 LATIN CAPITAL LETTER H WITH DOT BELOW */ { 0x0000, 0x0860, 0x0860, 0x0000 }, /* 1e25 LATIN SMALL LETTER H WITH DOT BELOW */ { 0x0862, 0x0000, 0x0000, 0x0862 }, /* 1e26 LATIN CAPITAL LETTER H WITH DIAERESIS */ { 0x0000, 0x0864, 0x0864, 0x0000 }, /* 1e27 LATIN SMALL LETTER H WITH DIAERESIS */ { 0x0866, 0x0000, 0x0000, 0x0866 }, /* 1e28 LATIN CAPITAL LETTER H WITH CEDILLA */ { 0x0000, 0x0868, 0x0868, 0x0000 }, /* 1e29 LATIN SMALL LETTER H WITH CEDILLA */ { 0x086a, 0x0000, 0x0000, 0x086a }, /* 1e2a LATIN CAPITAL LETTER H WITH BREVE BELOW */ { 0x0000, 0x086c, 0x086c, 0x0000 }, /* 1e2b LATIN SMALL LETTER H WITH BREVE BELOW */ { 0x086e, 0x0000, 0x0000, 0x086e }, /* 1e2c LATIN CAPITAL LETTER I WITH TILDE BELOW */ { 0x0000, 0x0870, 0x0870, 0x0000 }, /* 1e2d LATIN SMALL LETTER I WITH TILDE BELOW */ { 0x0872, 0x0000, 0x0000, 0x0872 }, /* 1e2e LATIN CAPITAL LETTER I WITH DIAERESIS A */ { 0x0000, 0x0874, 0x0874, 0x0000 }, /* 1e2f LATIN SMALL LETTER I WITH DIAERESIS AND */ { 0x0876, 0x0000, 0x0000, 0x0876 }, /* 1e30 LATIN CAPITAL LETTER K WITH ACUTE */ { 0x0000, 0x0878, 0x0878, 0x0000 }, /* 1e31 LATIN SMALL LETTER K WITH ACUTE */ { 0x087a, 0x0000, 0x0000, 0x087a }, /* 1e32 LATIN CAPITAL LETTER K WITH DOT BELOW */ { 0x0000, 0x087c, 0x087c, 0x0000 }, /* 1e33 LATIN SMALL LETTER K WITH DOT BELOW */ { 0x087e, 0x0000, 0x0000, 0x087e }, /* 1e34 LATIN CAPITAL LETTER K WITH LINE BELOW */ { 0x0000, 0x0880, 0x0880, 0x0000 }, /* 1e35 LATIN SMALL LETTER K WITH LINE BELOW */ { 0x0882, 0x0000, 0x0000, 0x0882 }, /* 1e36 LATIN CAPITAL LETTER L WITH DOT BELOW */ { 0x0000, 0x0884, 0x0884, 0x0000 }, /* 1e37 LATIN SMALL LETTER L WITH DOT BELOW */ { 0x0886, 0x0000, 0x0000, 0x0886 }, /* 1e38 LATIN CAPITAL LETTER L WITH DOT BELOW A */ { 0x0000, 0x0888, 0x0888, 0x0000 }, /* 1e39 LATIN SMALL LETTER L WITH DOT BELOW AND */ { 0x088a, 0x0000, 0x0000, 0x088a }, /* 1e3a LATIN CAPITAL LETTER L WITH LINE BELOW */ { 0x0000, 0x088c, 0x088c, 0x0000 }, /* 1e3b LATIN SMALL LETTER L WITH LINE BELOW */ { 0x088e, 0x0000, 0x0000, 0x088e }, /* 1e3c LATIN CAPITAL LETTER L WITH CIRCUMFLEX */ { 0x0000, 0x0890, 0x0890, 0x0000 }, /* 1e3d LATIN SMALL LETTER L WITH CIRCUMFLEX BE */ { 0x0892, 0x0000, 0x0000, 0x0892 }, /* 1e3e LATIN CAPITAL LETTER M WITH ACUTE */ { 0x0000, 0x0894, 0x0894, 0x0000 }, /* 1e3f LATIN SMALL LETTER M WITH ACUTE */ { 0x0896, 0x0000, 0x0000, 0x0896 }, /* 1e40 LATIN CAPITAL LETTER M WITH DOT ABOVE */ { 0x0000, 0x0898, 0x0898, 0x0000 }, /* 1e41 LATIN SMALL LETTER M WITH DOT ABOVE */ { 0x089a, 0x0000, 0x0000, 0x089a }, /* 1e42 LATIN CAPITAL LETTER M WITH DOT BELOW */ { 0x0000, 0x089c, 0x089c, 0x0000 }, /* 1e43 LATIN SMALL LETTER M WITH DOT BELOW */ { 0x089e, 0x0000, 0x0000, 0x089e }, /* 1e44 LATIN CAPITAL LETTER N WITH DOT ABOVE */ { 0x0000, 0x08a0, 0x08a0, 0x0000 }, /* 1e45 LATIN SMALL LETTER N WITH DOT ABOVE */ { 0x08a2, 0x0000, 0x0000, 0x08a2 }, /* 1e46 LATIN CAPITAL LETTER N WITH DOT BELOW */ { 0x0000, 0x08a4, 0x08a4, 0x0000 }, /* 1e47 LATIN SMALL LETTER N WITH DOT BELOW */ { 0x08a6, 0x0000, 0x0000, 0x08a6 }, /* 1e48 LATIN CAPITAL LETTER N WITH LINE BELOW */ { 0x0000, 0x08a8, 0x08a8, 0x0000 }, /* 1e49 LATIN SMALL LETTER N WITH LINE BELOW */ { 0x08aa, 0x0000, 0x0000, 0x08aa }, /* 1e4a LATIN CAPITAL LETTER N WITH CIRCUMFLEX */ { 0x0000, 0x08ac, 0x08ac, 0x0000 }, /* 1e4b LATIN SMALL LETTER N WITH CIRCUMFLEX BE */ { 0x08ae, 0x0000, 0x0000, 0x08ae }, /* 1e4c LATIN CAPITAL LETTER O WITH TILDE AND A */ { 0x0000, 0x08b0, 0x08b0, 0x0000 }, /* 1e4d LATIN SMALL LETTER O WITH TILDE AND ACU */ { 0x08b2, 0x0000, 0x0000, 0x08b2 }, /* 1e4e LATIN CAPITAL LETTER O WITH TILDE AND D */ { 0x0000, 0x08b4, 0x08b4, 0x0000 }, /* 1e4f LATIN SMALL LETTER O WITH TILDE AND DIA */ { 0x08b6, 0x0000, 0x0000, 0x08b6 }, /* 1e50 LATIN CAPITAL LETTER O WITH MACRON AND */ { 0x0000, 0x08b8, 0x08b8, 0x0000 }, /* 1e51 LATIN SMALL LETTER O WITH MACRON AND GR */ { 0x08ba, 0x0000, 0x0000, 0x08ba }, /* 1e52 LATIN CAPITAL LETTER O WITH MACRON AND */ { 0x0000, 0x08bc, 0x08bc, 0x0000 }, /* 1e53 LATIN SMALL LETTER O WITH MACRON AND AC */ { 0x08be, 0x0000, 0x0000, 0x08be }, /* 1e54 LATIN CAPITAL LETTER P WITH ACUTE */ { 0x0000, 0x08c0, 0x08c0, 0x0000 }, /* 1e55 LATIN SMALL LETTER P WITH ACUTE */ { 0x08c2, 0x0000, 0x0000, 0x08c2 }, /* 1e56 LATIN CAPITAL LETTER P WITH DOT ABOVE */ { 0x0000, 0x08c4, 0x08c4, 0x0000 }, /* 1e57 LATIN SMALL LETTER P WITH DOT ABOVE */ { 0x08c6, 0x0000, 0x0000, 0x08c6 }, /* 1e58 LATIN CAPITAL LETTER R WITH DOT ABOVE */ { 0x0000, 0x08c8, 0x08c8, 0x0000 }, /* 1e59 LATIN SMALL LETTER R WITH DOT ABOVE */ { 0x08ca, 0x0000, 0x0000, 0x08ca }, /* 1e5a LATIN CAPITAL LETTER R WITH DOT BELOW */ { 0x0000, 0x08cc, 0x08cc, 0x0000 }, /* 1e5b LATIN SMALL LETTER R WITH DOT BELOW */ { 0x08ce, 0x0000, 0x0000, 0x08ce }, /* 1e5c LATIN CAPITAL LETTER R WITH DOT BELOW A */ { 0x0000, 0x08d0, 0x08d0, 0x0000 }, /* 1e5d LATIN SMALL LETTER R WITH DOT BELOW AND */ { 0x08d2, 0x0000, 0x0000, 0x08d2 }, /* 1e5e LATIN CAPITAL LETTER R WITH LINE BELOW */ { 0x0000, 0x08d4, 0x08d4, 0x0000 }, /* 1e5f LATIN SMALL LETTER R WITH LINE BELOW */ { 0x08d6, 0x0000, 0x0000, 0x08d6 }, /* 1e60 LATIN CAPITAL LETTER S WITH DOT ABOVE */ { 0x0000, 0x08d8, 0x08d8, 0x0000 }, /* 1e61 LATIN SMALL LETTER S WITH DOT ABOVE */ { 0x08da, 0x0000, 0x0000, 0x08da }, /* 1e62 LATIN CAPITAL LETTER S WITH DOT BELOW */ { 0x0000, 0x08dc, 0x08dc, 0x0000 }, /* 1e63 LATIN SMALL LETTER S WITH DOT BELOW */ { 0x08de, 0x0000, 0x0000, 0x08de }, /* 1e64 LATIN CAPITAL LETTER S WITH ACUTE AND D */ { 0x0000, 0x08e0, 0x08e0, 0x0000 }, /* 1e65 LATIN SMALL LETTER S WITH ACUTE AND DOT */ { 0x08e2, 0x0000, 0x0000, 0x08e2 }, /* 1e66 LATIN CAPITAL LETTER S WITH CARON AND D */ { 0x0000, 0x08e4, 0x08e4, 0x0000 }, /* 1e67 LATIN SMALL LETTER S WITH CARON AND DOT */ { 0x08e6, 0x0000, 0x0000, 0x08e6 }, /* 1e68 LATIN CAPITAL LETTER S WITH DOT BELOW A */ { 0x0000, 0x08e8, 0x08e8, 0x0000 }, /* 1e69 LATIN SMALL LETTER S WITH DOT BELOW AND */ { 0x08ea, 0x0000, 0x0000, 0x08ea }, /* 1e6a LATIN CAPITAL LETTER T WITH DOT ABOVE */ { 0x0000, 0x08ec, 0x08ec, 0x0000 }, /* 1e6b LATIN SMALL LETTER T WITH DOT ABOVE */ { 0x08ee, 0x0000, 0x0000, 0x08ee }, /* 1e6c LATIN CAPITAL LETTER T WITH DOT BELOW */ { 0x0000, 0x08f0, 0x08f0, 0x0000 }, /* 1e6d LATIN SMALL LETTER T WITH DOT BELOW */ { 0x08f2, 0x0000, 0x0000, 0x08f2 }, /* 1e6e LATIN CAPITAL LETTER T WITH LINE BELOW */ { 0x0000, 0x08f4, 0x08f4, 0x0000 }, /* 1e6f LATIN SMALL LETTER T WITH LINE BELOW */ { 0x08f6, 0x0000, 0x0000, 0x08f6 }, /* 1e70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX */ { 0x0000, 0x08f8, 0x08f8, 0x0000 }, /* 1e71 LATIN SMALL LETTER T WITH CIRCUMFLEX BE */ { 0x08fa, 0x0000, 0x0000, 0x08fa }, /* 1e72 LATIN CAPITAL LETTER U WITH DIAERESIS B */ { 0x0000, 0x08fc, 0x08fc, 0x0000 }, /* 1e73 LATIN SMALL LETTER U WITH DIAERESIS BEL */ { 0x08fe, 0x0000, 0x0000, 0x08fe }, /* 1e74 LATIN CAPITAL LETTER U WITH TILDE BELOW */ { 0x0000, 0x0900, 0x0900, 0x0000 }, /* 1e75 LATIN SMALL LETTER U WITH TILDE BELOW */ { 0x0902, 0x0000, 0x0000, 0x0902 }, /* 1e76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ { 0x0000, 0x0904, 0x0904, 0x0000 }, /* 1e77 LATIN SMALL LETTER U WITH CIRCUMFLEX BE */ { 0x0906, 0x0000, 0x0000, 0x0906 }, /* 1e78 LATIN CAPITAL LETTER U WITH TILDE AND A */ { 0x0000, 0x0908, 0x0908, 0x0000 }, /* 1e79 LATIN SMALL LETTER U WITH TILDE AND ACU */ { 0x090a, 0x0000, 0x0000, 0x090a }, /* 1e7a LATIN CAPITAL LETTER U WITH MACRON AND */ { 0x0000, 0x090c, 0x090c, 0x0000 }, /* 1e7b LATIN SMALL LETTER U WITH MACRON AND DI */ { 0x090e, 0x0000, 0x0000, 0x090e }, /* 1e7c LATIN CAPITAL LETTER V WITH TILDE */ { 0x0000, 0x0910, 0x0910, 0x0000 }, /* 1e7d LATIN SMALL LETTER V WITH TILDE */ { 0x0912, 0x0000, 0x0000, 0x0912 }, /* 1e7e LATIN CAPITAL LETTER V WITH DOT BELOW */ { 0x0000, 0x0914, 0x0914, 0x0000 } /* 1e7f LATIN SMALL LETTER V WITH DOT BELOW */ }; static case_table case_pg_03d[128] = { { 0x0916, 0x0000, 0x0000, 0x0916 }, /* 1e80 LATIN CAPITAL LETTER W WITH GRAVE */ { 0x0000, 0x0918, 0x0918, 0x0000 }, /* 1e81 LATIN SMALL LETTER W WITH GRAVE */ { 0x091a, 0x0000, 0x0000, 0x091a }, /* 1e82 LATIN CAPITAL LETTER W WITH ACUTE */ { 0x0000, 0x091c, 0x091c, 0x0000 }, /* 1e83 LATIN SMALL LETTER W WITH ACUTE */ { 0x091e, 0x0000, 0x0000, 0x091e }, /* 1e84 LATIN CAPITAL LETTER W WITH DIAERESIS */ { 0x0000, 0x0920, 0x0920, 0x0000 }, /* 1e85 LATIN SMALL LETTER W WITH DIAERESIS */ { 0x0922, 0x0000, 0x0000, 0x0922 }, /* 1e86 LATIN CAPITAL LETTER W WITH DOT ABOVE */ { 0x0000, 0x0924, 0x0924, 0x0000 }, /* 1e87 LATIN SMALL LETTER W WITH DOT ABOVE */ { 0x0926, 0x0000, 0x0000, 0x0926 }, /* 1e88 LATIN CAPITAL LETTER W WITH DOT BELOW */ { 0x0000, 0x0928, 0x0928, 0x0000 }, /* 1e89 LATIN SMALL LETTER W WITH DOT BELOW */ { 0x092a, 0x0000, 0x0000, 0x092a }, /* 1e8a LATIN CAPITAL LETTER X WITH DOT ABOVE */ { 0x0000, 0x092c, 0x092c, 0x0000 }, /* 1e8b LATIN SMALL LETTER X WITH DOT ABOVE */ { 0x092e, 0x0000, 0x0000, 0x092e }, /* 1e8c LATIN CAPITAL LETTER X WITH DIAERESIS */ { 0x0000, 0x0930, 0x0930, 0x0000 }, /* 1e8d LATIN SMALL LETTER X WITH DIAERESIS */ { 0x0932, 0x0000, 0x0000, 0x0932 }, /* 1e8e LATIN CAPITAL LETTER Y WITH DOT ABOVE */ { 0x0000, 0x0934, 0x0934, 0x0000 }, /* 1e8f LATIN SMALL LETTER Y WITH DOT ABOVE */ { 0x0936, 0x0000, 0x0000, 0x0936 }, /* 1e90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ { 0x0000, 0x0938, 0x0938, 0x0000 }, /* 1e91 LATIN SMALL LETTER Z WITH CIRCUMFLEX */ { 0x093a, 0x0000, 0x0000, 0x093a }, /* 1e92 LATIN CAPITAL LETTER Z WITH DOT BELOW */ { 0x0000, 0x093c, 0x093c, 0x0000 }, /* 1e93 LATIN SMALL LETTER Z WITH DOT BELOW */ { 0x093e, 0x0000, 0x0000, 0x093e }, /* 1e94 LATIN CAPITAL LETTER Z WITH LINE BELOW */ { 0x0000, 0x0940, 0x0940, 0x0000 }, /* 1e95 LATIN SMALL LETTER Z WITH LINE BELOW */ { 0x0000, 0x0942, 0x0942, 0x0945 }, /* 1e96 LATIN SMALL LETTER H WITH LINE BELOW */ { 0x0000, 0x0948, 0x0948, 0x094b }, /* 1e97 LATIN SMALL LETTER T WITH DIAERESIS */ { 0x0000, 0x094e, 0x094e, 0x0951 }, /* 1e98 LATIN SMALL LETTER W WITH RING ABOVE */ { 0x0000, 0x0954, 0x0954, 0x0957 }, /* 1e99 LATIN SMALL LETTER Y WITH RING ABOVE */ { 0x0000, 0x095a, 0x095a, 0x095d }, /* 1e9a LATIN SMALL LETTER A WITH RIGHT HALF RI */ { 0x0000, 0x0960, 0x0960, 0x0962 }, /* 1e9b LATIN SMALL LETTER LONG S WITH DOT ABOV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9c LATIN SMALL LETTER LONG S WITH DIAGONAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9d LATIN SMALL LETTER LONG S WITH HIGH STR */ { 0x0964, 0x0000, 0x0000, 0x0966 }, /* 1e9e LATIN CAPITAL LETTER SHARP S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1e9f LATIN SMALL LETTER DELTA */ { 0x0969, 0x0000, 0x0000, 0x0969 }, /* 1ea0 LATIN CAPITAL LETTER A WITH DOT BELOW */ { 0x0000, 0x096b, 0x096b, 0x0000 }, /* 1ea1 LATIN SMALL LETTER A WITH DOT BELOW */ { 0x096d, 0x0000, 0x0000, 0x096d }, /* 1ea2 LATIN CAPITAL LETTER A WITH HOOK ABOVE */ { 0x0000, 0x096f, 0x096f, 0x0000 }, /* 1ea3 LATIN SMALL LETTER A WITH HOOK ABOVE */ { 0x0971, 0x0000, 0x0000, 0x0971 }, /* 1ea4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0973, 0x0973, 0x0000 }, /* 1ea5 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0975, 0x0000, 0x0000, 0x0975 }, /* 1ea6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0977, 0x0977, 0x0000 }, /* 1ea7 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0979, 0x0000, 0x0000, 0x0979 }, /* 1ea8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x097b, 0x097b, 0x0000 }, /* 1ea9 LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x097d, 0x0000, 0x0000, 0x097d }, /* 1eaa LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x097f, 0x097f, 0x0000 }, /* 1eab LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0981, 0x0000, 0x0000, 0x0981 }, /* 1eac LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ { 0x0000, 0x0983, 0x0983, 0x0000 }, /* 1ead LATIN SMALL LETTER A WITH CIRCUMFLEX AN */ { 0x0985, 0x0000, 0x0000, 0x0985 }, /* 1eae LATIN CAPITAL LETTER A WITH BREVE AND A */ { 0x0000, 0x0987, 0x0987, 0x0000 }, /* 1eaf LATIN SMALL LETTER A WITH BREVE AND ACU */ { 0x0989, 0x0000, 0x0000, 0x0989 }, /* 1eb0 LATIN CAPITAL LETTER A WITH BREVE AND G */ { 0x0000, 0x098b, 0x098b, 0x0000 }, /* 1eb1 LATIN SMALL LETTER A WITH BREVE AND GRA */ { 0x098d, 0x0000, 0x0000, 0x098d }, /* 1eb2 LATIN CAPITAL LETTER A WITH BREVE AND H */ { 0x0000, 0x098f, 0x098f, 0x0000 }, /* 1eb3 LATIN SMALL LETTER A WITH BREVE AND HOO */ { 0x0991, 0x0000, 0x0000, 0x0991 }, /* 1eb4 LATIN CAPITAL LETTER A WITH BREVE AND T */ { 0x0000, 0x0993, 0x0993, 0x0000 }, /* 1eb5 LATIN SMALL LETTER A WITH BREVE AND TIL */ { 0x0995, 0x0000, 0x0000, 0x0995 }, /* 1eb6 LATIN CAPITAL LETTER A WITH BREVE AND D */ { 0x0000, 0x0997, 0x0997, 0x0000 }, /* 1eb7 LATIN SMALL LETTER A WITH BREVE AND DOT */ { 0x0999, 0x0000, 0x0000, 0x0999 }, /* 1eb8 LATIN CAPITAL LETTER E WITH DOT BELOW */ { 0x0000, 0x099b, 0x099b, 0x0000 }, /* 1eb9 LATIN SMALL LETTER E WITH DOT BELOW */ { 0x099d, 0x0000, 0x0000, 0x099d }, /* 1eba LATIN CAPITAL LETTER E WITH HOOK ABOVE */ { 0x0000, 0x099f, 0x099f, 0x0000 }, /* 1ebb LATIN SMALL LETTER E WITH HOOK ABOVE */ { 0x09a1, 0x0000, 0x0000, 0x09a1 }, /* 1ebc LATIN CAPITAL LETTER E WITH TILDE */ { 0x0000, 0x09a3, 0x09a3, 0x0000 }, /* 1ebd LATIN SMALL LETTER E WITH TILDE */ { 0x09a5, 0x0000, 0x0000, 0x09a5 }, /* 1ebe LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09a7, 0x09a7, 0x0000 }, /* 1ebf LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09a9, 0x0000, 0x0000, 0x09a9 }, /* 1ec0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09ab, 0x09ab, 0x0000 }, /* 1ec1 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09ad, 0x0000, 0x0000, 0x09ad }, /* 1ec2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09af, 0x09af, 0x0000 }, /* 1ec3 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b1, 0x0000, 0x0000, 0x09b1 }, /* 1ec4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09b3, 0x09b3, 0x0000 }, /* 1ec5 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b5, 0x0000, 0x0000, 0x09b5 }, /* 1ec6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ { 0x0000, 0x09b7, 0x09b7, 0x0000 }, /* 1ec7 LATIN SMALL LETTER E WITH CIRCUMFLEX AN */ { 0x09b9, 0x0000, 0x0000, 0x09b9 }, /* 1ec8 LATIN CAPITAL LETTER I WITH HOOK ABOVE */ { 0x0000, 0x09bb, 0x09bb, 0x0000 }, /* 1ec9 LATIN SMALL LETTER I WITH HOOK ABOVE */ { 0x09bd, 0x0000, 0x0000, 0x09bd }, /* 1eca LATIN CAPITAL LETTER I WITH DOT BELOW */ { 0x0000, 0x09bf, 0x09bf, 0x0000 }, /* 1ecb LATIN SMALL LETTER I WITH DOT BELOW */ { 0x09c1, 0x0000, 0x0000, 0x09c1 }, /* 1ecc LATIN CAPITAL LETTER O WITH DOT BELOW */ { 0x0000, 0x09c3, 0x09c3, 0x0000 }, /* 1ecd LATIN SMALL LETTER O WITH DOT BELOW */ { 0x09c5, 0x0000, 0x0000, 0x09c5 }, /* 1ece LATIN CAPITAL LETTER O WITH HOOK ABOVE */ { 0x0000, 0x09c7, 0x09c7, 0x0000 }, /* 1ecf LATIN SMALL LETTER O WITH HOOK ABOVE */ { 0x09c9, 0x0000, 0x0000, 0x09c9 }, /* 1ed0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09cb, 0x09cb, 0x0000 }, /* 1ed1 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09cd, 0x0000, 0x0000, 0x09cd }, /* 1ed2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09cf, 0x09cf, 0x0000 }, /* 1ed3 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d1, 0x0000, 0x0000, 0x09d1 }, /* 1ed4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09d3, 0x09d3, 0x0000 }, /* 1ed5 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d5, 0x0000, 0x0000, 0x09d5 }, /* 1ed6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09d7, 0x09d7, 0x0000 }, /* 1ed7 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09d9, 0x0000, 0x0000, 0x09d9 }, /* 1ed8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ { 0x0000, 0x09db, 0x09db, 0x0000 }, /* 1ed9 LATIN SMALL LETTER O WITH CIRCUMFLEX AN */ { 0x09dd, 0x0000, 0x0000, 0x09dd }, /* 1eda LATIN CAPITAL LETTER O WITH HORN AND AC */ { 0x0000, 0x09df, 0x09df, 0x0000 }, /* 1edb LATIN SMALL LETTER O WITH HORN AND ACUT */ { 0x09e1, 0x0000, 0x0000, 0x09e1 }, /* 1edc LATIN CAPITAL LETTER O WITH HORN AND GR */ { 0x0000, 0x09e3, 0x09e3, 0x0000 }, /* 1edd LATIN SMALL LETTER O WITH HORN AND GRAV */ { 0x09e5, 0x0000, 0x0000, 0x09e5 }, /* 1ede LATIN CAPITAL LETTER O WITH HORN AND HO */ { 0x0000, 0x09e7, 0x09e7, 0x0000 }, /* 1edf LATIN SMALL LETTER O WITH HORN AND HOOK */ { 0x09e9, 0x0000, 0x0000, 0x09e9 }, /* 1ee0 LATIN CAPITAL LETTER O WITH HORN AND TI */ { 0x0000, 0x09eb, 0x09eb, 0x0000 }, /* 1ee1 LATIN SMALL LETTER O WITH HORN AND TILD */ { 0x09ed, 0x0000, 0x0000, 0x09ed }, /* 1ee2 LATIN CAPITAL LETTER O WITH HORN AND DO */ { 0x0000, 0x09ef, 0x09ef, 0x0000 }, /* 1ee3 LATIN SMALL LETTER O WITH HORN AND DOT */ { 0x09f1, 0x0000, 0x0000, 0x09f1 }, /* 1ee4 LATIN CAPITAL LETTER U WITH DOT BELOW */ { 0x0000, 0x09f3, 0x09f3, 0x0000 }, /* 1ee5 LATIN SMALL LETTER U WITH DOT BELOW */ { 0x09f5, 0x0000, 0x0000, 0x09f5 }, /* 1ee6 LATIN CAPITAL LETTER U WITH HOOK ABOVE */ { 0x0000, 0x09f7, 0x09f7, 0x0000 }, /* 1ee7 LATIN SMALL LETTER U WITH HOOK ABOVE */ { 0x09f9, 0x0000, 0x0000, 0x09f9 }, /* 1ee8 LATIN CAPITAL LETTER U WITH HORN AND AC */ { 0x0000, 0x09fb, 0x09fb, 0x0000 }, /* 1ee9 LATIN SMALL LETTER U WITH HORN AND ACUT */ { 0x09fd, 0x0000, 0x0000, 0x09fd }, /* 1eea LATIN CAPITAL LETTER U WITH HORN AND GR */ { 0x0000, 0x09ff, 0x09ff, 0x0000 }, /* 1eeb LATIN SMALL LETTER U WITH HORN AND GRAV */ { 0x0a01, 0x0000, 0x0000, 0x0a01 }, /* 1eec LATIN CAPITAL LETTER U WITH HORN AND HO */ { 0x0000, 0x0a03, 0x0a03, 0x0000 }, /* 1eed LATIN SMALL LETTER U WITH HORN AND HOOK */ { 0x0a05, 0x0000, 0x0000, 0x0a05 }, /* 1eee LATIN CAPITAL LETTER U WITH HORN AND TI */ { 0x0000, 0x0a07, 0x0a07, 0x0000 }, /* 1eef LATIN SMALL LETTER U WITH HORN AND TILD */ { 0x0a09, 0x0000, 0x0000, 0x0a09 }, /* 1ef0 LATIN CAPITAL LETTER U WITH HORN AND DO */ { 0x0000, 0x0a0b, 0x0a0b, 0x0000 }, /* 1ef1 LATIN SMALL LETTER U WITH HORN AND DOT */ { 0x0a0d, 0x0000, 0x0000, 0x0a0d }, /* 1ef2 LATIN CAPITAL LETTER Y WITH GRAVE */ { 0x0000, 0x0a0f, 0x0a0f, 0x0000 }, /* 1ef3 LATIN SMALL LETTER Y WITH GRAVE */ { 0x0a11, 0x0000, 0x0000, 0x0a11 }, /* 1ef4 LATIN CAPITAL LETTER Y WITH DOT BELOW */ { 0x0000, 0x0a13, 0x0a13, 0x0000 }, /* 1ef5 LATIN SMALL LETTER Y WITH DOT BELOW */ { 0x0a15, 0x0000, 0x0000, 0x0a15 }, /* 1ef6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ { 0x0000, 0x0a17, 0x0a17, 0x0000 }, /* 1ef7 LATIN SMALL LETTER Y WITH HOOK ABOVE */ { 0x0a19, 0x0000, 0x0000, 0x0a19 }, /* 1ef8 LATIN CAPITAL LETTER Y WITH TILDE */ { 0x0000, 0x0a1b, 0x0a1b, 0x0000 }, /* 1ef9 LATIN SMALL LETTER Y WITH TILDE */ { 0x0a1d, 0x0000, 0x0000, 0x0a1d }, /* 1efa LATIN CAPITAL LETTER MIDDLE-WELSH LL */ { 0x0000, 0x0a1f, 0x0a1f, 0x0000 }, /* 1efb LATIN SMALL LETTER MIDDLE-WELSH LL */ { 0x0a21, 0x0000, 0x0000, 0x0a21 }, /* 1efc LATIN CAPITAL LETTER MIDDLE-WELSH V */ { 0x0000, 0x0a23, 0x0a23, 0x0000 }, /* 1efd LATIN SMALL LETTER MIDDLE-WELSH V */ { 0x0a25, 0x0000, 0x0000, 0x0a25 }, /* 1efe LATIN CAPITAL LETTER Y WITH LOOP */ { 0x0000, 0x0a27, 0x0a27, 0x0000 } /* 1eff LATIN SMALL LETTER Y WITH LOOP */ }; static case_table case_pg_03e[128] = { { 0x0000, 0x0a29, 0x0a29, 0x0000 }, /* 1f00 GREEK SMALL LETTER ALPHA WITH PSILI */ { 0x0000, 0x0a2b, 0x0a2b, 0x0000 }, /* 1f01 GREEK SMALL LETTER ALPHA WITH DASIA */ { 0x0000, 0x0a2d, 0x0a2d, 0x0000 }, /* 1f02 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a2f, 0x0a2f, 0x0000 }, /* 1f03 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0a31, 0x0a31, 0x0000 }, /* 1f04 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a33, 0x0a33, 0x0000 }, /* 1f05 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0a35, 0x0a35, 0x0000 }, /* 1f06 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0a37, 0x0a37, 0x0000 }, /* 1f07 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0a39, 0x0000, 0x0000, 0x0a39 }, /* 1f08 GREEK CAPITAL LETTER ALPHA WITH PSILI */ { 0x0a3b, 0x0000, 0x0000, 0x0a3b }, /* 1f09 GREEK CAPITAL LETTER ALPHA WITH DASIA */ { 0x0a3d, 0x0000, 0x0000, 0x0a3d }, /* 1f0a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a3f, 0x0000, 0x0000, 0x0a3f }, /* 1f0b GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0a41, 0x0000, 0x0000, 0x0a41 }, /* 1f0c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a43, 0x0000, 0x0000, 0x0a43 }, /* 1f0d GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0a45, 0x0000, 0x0000, 0x0a45 }, /* 1f0e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0a47, 0x0000, 0x0000, 0x0a47 }, /* 1f0f GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0000, 0x0a49, 0x0a49, 0x0000 }, /* 1f10 GREEK SMALL LETTER EPSILON WITH PSILI */ { 0x0000, 0x0a4b, 0x0a4b, 0x0000 }, /* 1f11 GREEK SMALL LETTER EPSILON WITH DASIA */ { 0x0000, 0x0a4d, 0x0a4d, 0x0000 }, /* 1f12 GREEK SMALL LETTER EPSILON WITH PSILI A */ { 0x0000, 0x0a4f, 0x0a4f, 0x0000 }, /* 1f13 GREEK SMALL LETTER EPSILON WITH DASIA A */ { 0x0000, 0x0a51, 0x0a51, 0x0000 }, /* 1f14 GREEK SMALL LETTER EPSILON WITH PSILI A */ { 0x0000, 0x0a53, 0x0a53, 0x0000 }, /* 1f15 GREEK SMALL LETTER EPSILON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f16 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f17 (null) */ { 0x0a55, 0x0000, 0x0000, 0x0a55 }, /* 1f18 GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a57, 0x0000, 0x0000, 0x0a57 }, /* 1f19 GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0a59, 0x0000, 0x0000, 0x0a59 }, /* 1f1a GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a5b, 0x0000, 0x0000, 0x0a5b }, /* 1f1b GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0a5d, 0x0000, 0x0000, 0x0a5d }, /* 1f1c GREEK CAPITAL LETTER EPSILON WITH PSILI */ { 0x0a5f, 0x0000, 0x0000, 0x0a5f }, /* 1f1d GREEK CAPITAL LETTER EPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f1e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f1f (null) */ { 0x0000, 0x0a61, 0x0a61, 0x0000 }, /* 1f20 GREEK SMALL LETTER ETA WITH PSILI */ { 0x0000, 0x0a63, 0x0a63, 0x0000 }, /* 1f21 GREEK SMALL LETTER ETA WITH DASIA */ { 0x0000, 0x0a65, 0x0a65, 0x0000 }, /* 1f22 GREEK SMALL LETTER ETA WITH PSILI AND V */ { 0x0000, 0x0a67, 0x0a67, 0x0000 }, /* 1f23 GREEK SMALL LETTER ETA WITH DASIA AND V */ { 0x0000, 0x0a69, 0x0a69, 0x0000 }, /* 1f24 GREEK SMALL LETTER ETA WITH PSILI AND O */ { 0x0000, 0x0a6b, 0x0a6b, 0x0000 }, /* 1f25 GREEK SMALL LETTER ETA WITH DASIA AND O */ { 0x0000, 0x0a6d, 0x0a6d, 0x0000 }, /* 1f26 GREEK SMALL LETTER ETA WITH PSILI AND P */ { 0x0000, 0x0a6f, 0x0a6f, 0x0000 }, /* 1f27 GREEK SMALL LETTER ETA WITH DASIA AND P */ { 0x0a71, 0x0000, 0x0000, 0x0a71 }, /* 1f28 GREEK CAPITAL LETTER ETA WITH PSILI */ { 0x0a73, 0x0000, 0x0000, 0x0a73 }, /* 1f29 GREEK CAPITAL LETTER ETA WITH DASIA */ { 0x0a75, 0x0000, 0x0000, 0x0a75 }, /* 1f2a GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a77, 0x0000, 0x0000, 0x0a77 }, /* 1f2b GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0a79, 0x0000, 0x0000, 0x0a79 }, /* 1f2c GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a7b, 0x0000, 0x0000, 0x0a7b }, /* 1f2d GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0a7d, 0x0000, 0x0000, 0x0a7d }, /* 1f2e GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0a7f, 0x0000, 0x0000, 0x0a7f }, /* 1f2f GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0000, 0x0a81, 0x0a81, 0x0000 }, /* 1f30 GREEK SMALL LETTER IOTA WITH PSILI */ { 0x0000, 0x0a83, 0x0a83, 0x0000 }, /* 1f31 GREEK SMALL LETTER IOTA WITH DASIA */ { 0x0000, 0x0a85, 0x0a85, 0x0000 }, /* 1f32 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a87, 0x0a87, 0x0000 }, /* 1f33 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0000, 0x0a89, 0x0a89, 0x0000 }, /* 1f34 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a8b, 0x0a8b, 0x0000 }, /* 1f35 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0000, 0x0a8d, 0x0a8d, 0x0000 }, /* 1f36 GREEK SMALL LETTER IOTA WITH PSILI AND */ { 0x0000, 0x0a8f, 0x0a8f, 0x0000 }, /* 1f37 GREEK SMALL LETTER IOTA WITH DASIA AND */ { 0x0a91, 0x0000, 0x0000, 0x0a91 }, /* 1f38 GREEK CAPITAL LETTER IOTA WITH PSILI */ { 0x0a93, 0x0000, 0x0000, 0x0a93 }, /* 1f39 GREEK CAPITAL LETTER IOTA WITH DASIA */ { 0x0a95, 0x0000, 0x0000, 0x0a95 }, /* 1f3a GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a97, 0x0000, 0x0000, 0x0a97 }, /* 1f3b GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0a99, 0x0000, 0x0000, 0x0a99 }, /* 1f3c GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a9b, 0x0000, 0x0000, 0x0a9b }, /* 1f3d GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0a9d, 0x0000, 0x0000, 0x0a9d }, /* 1f3e GREEK CAPITAL LETTER IOTA WITH PSILI AN */ { 0x0a9f, 0x0000, 0x0000, 0x0a9f }, /* 1f3f GREEK CAPITAL LETTER IOTA WITH DASIA AN */ { 0x0000, 0x0aa1, 0x0aa1, 0x0000 }, /* 1f40 GREEK SMALL LETTER OMICRON WITH PSILI */ { 0x0000, 0x0aa3, 0x0aa3, 0x0000 }, /* 1f41 GREEK SMALL LETTER OMICRON WITH DASIA */ { 0x0000, 0x0aa5, 0x0aa5, 0x0000 }, /* 1f42 GREEK SMALL LETTER OMICRON WITH PSILI A */ { 0x0000, 0x0aa7, 0x0aa7, 0x0000 }, /* 1f43 GREEK SMALL LETTER OMICRON WITH DASIA A */ { 0x0000, 0x0aa9, 0x0aa9, 0x0000 }, /* 1f44 GREEK SMALL LETTER OMICRON WITH PSILI A */ { 0x0000, 0x0aab, 0x0aab, 0x0000 }, /* 1f45 GREEK SMALL LETTER OMICRON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f46 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f47 (null) */ { 0x0aad, 0x0000, 0x0000, 0x0aad }, /* 1f48 GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0aaf, 0x0000, 0x0000, 0x0aaf }, /* 1f49 GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0ab1, 0x0000, 0x0000, 0x0ab1 }, /* 1f4a GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0ab3, 0x0000, 0x0000, 0x0ab3 }, /* 1f4b GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0ab5, 0x0000, 0x0000, 0x0ab5 }, /* 1f4c GREEK CAPITAL LETTER OMICRON WITH PSILI */ { 0x0ab7, 0x0000, 0x0000, 0x0ab7 }, /* 1f4d GREEK CAPITAL LETTER OMICRON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f4e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f4f (null) */ { 0x0000, 0x0ab9, 0x0ab9, 0x0abc }, /* 1f50 GREEK SMALL LETTER UPSILON WITH PSILI */ { 0x0000, 0x0abf, 0x0abf, 0x0000 }, /* 1f51 GREEK SMALL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0ac1, 0x0ac1, 0x0ac5 }, /* 1f52 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0ac9, 0x0ac9, 0x0000 }, /* 1f53 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0acb, 0x0acb, 0x0acf }, /* 1f54 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0ad3, 0x0ad3, 0x0000 }, /* 1f55 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0ad5, 0x0ad5, 0x0ad9 }, /* 1f56 GREEK SMALL LETTER UPSILON WITH PSILI A */ { 0x0000, 0x0add, 0x0add, 0x0000 }, /* 1f57 GREEK SMALL LETTER UPSILON WITH DASIA A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f58 (null) */ { 0x0adf, 0x0000, 0x0000, 0x0adf }, /* 1f59 GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5a (null) */ { 0x0ae1, 0x0000, 0x0000, 0x0ae1 }, /* 1f5b GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5c (null) */ { 0x0ae3, 0x0000, 0x0000, 0x0ae3 }, /* 1f5d GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f5e (null) */ { 0x0ae5, 0x0000, 0x0000, 0x0ae5 }, /* 1f5f GREEK CAPITAL LETTER UPSILON WITH DASIA */ { 0x0000, 0x0ae7, 0x0ae7, 0x0000 }, /* 1f60 GREEK SMALL LETTER OMEGA WITH PSILI */ { 0x0000, 0x0ae9, 0x0ae9, 0x0000 }, /* 1f61 GREEK SMALL LETTER OMEGA WITH DASIA */ { 0x0000, 0x0aeb, 0x0aeb, 0x0000 }, /* 1f62 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0aed, 0x0aed, 0x0000 }, /* 1f63 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0aef, 0x0aef, 0x0000 }, /* 1f64 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0af1, 0x0af1, 0x0000 }, /* 1f65 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0af3, 0x0af3, 0x0000 }, /* 1f66 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0af5, 0x0af5, 0x0000 }, /* 1f67 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0af7, 0x0000, 0x0000, 0x0af7 }, /* 1f68 GREEK CAPITAL LETTER OMEGA WITH PSILI */ { 0x0af9, 0x0000, 0x0000, 0x0af9 }, /* 1f69 GREEK CAPITAL LETTER OMEGA WITH DASIA */ { 0x0afb, 0x0000, 0x0000, 0x0afb }, /* 1f6a GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0afd, 0x0000, 0x0000, 0x0afd }, /* 1f6b GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0aff, 0x0000, 0x0000, 0x0aff }, /* 1f6c GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0b01, 0x0000, 0x0000, 0x0b01 }, /* 1f6d GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0b03, 0x0000, 0x0000, 0x0b03 }, /* 1f6e GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0b05, 0x0000, 0x0000, 0x0b05 }, /* 1f6f GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0000, 0x0b07, 0x0b07, 0x0000 }, /* 1f70 GREEK SMALL LETTER ALPHA WITH VARIA */ { 0x0000, 0x0b09, 0x0b09, 0x0000 }, /* 1f71 GREEK SMALL LETTER ALPHA WITH OXIA */ { 0x0000, 0x0b0b, 0x0b0b, 0x0000 }, /* 1f72 GREEK SMALL LETTER EPSILON WITH VARIA */ { 0x0000, 0x0b0d, 0x0b0d, 0x0000 }, /* 1f73 GREEK SMALL LETTER EPSILON WITH OXIA */ { 0x0000, 0x0b0f, 0x0b0f, 0x0000 }, /* 1f74 GREEK SMALL LETTER ETA WITH VARIA */ { 0x0000, 0x0b11, 0x0b11, 0x0000 }, /* 1f75 GREEK SMALL LETTER ETA WITH OXIA */ { 0x0000, 0x0b13, 0x0b13, 0x0000 }, /* 1f76 GREEK SMALL LETTER IOTA WITH VARIA */ { 0x0000, 0x0b15, 0x0b15, 0x0000 }, /* 1f77 GREEK SMALL LETTER IOTA WITH OXIA */ { 0x0000, 0x0b17, 0x0b17, 0x0000 }, /* 1f78 GREEK SMALL LETTER OMICRON WITH VARIA */ { 0x0000, 0x0b19, 0x0b19, 0x0000 }, /* 1f79 GREEK SMALL LETTER OMICRON WITH OXIA */ { 0x0000, 0x0b1b, 0x0b1b, 0x0000 }, /* 1f7a GREEK SMALL LETTER UPSILON WITH VARIA */ { 0x0000, 0x0b1d, 0x0b1d, 0x0000 }, /* 1f7b GREEK SMALL LETTER UPSILON WITH OXIA */ { 0x0000, 0x0b1f, 0x0b1f, 0x0000 }, /* 1f7c GREEK SMALL LETTER OMEGA WITH VARIA */ { 0x0000, 0x0b21, 0x0b21, 0x0000 }, /* 1f7d GREEK SMALL LETTER OMEGA WITH OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1f7e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1f7f (null) */ }; static case_table case_pg_03f[128] = { { 0x0000, 0x0b23, 0x0b25, 0x0b28 }, /* 1f80 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b2b, 0x0b2d, 0x0b30 }, /* 1f81 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b33, 0x0b35, 0x0b38 }, /* 1f82 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b3b, 0x0b3d, 0x0b40 }, /* 1f83 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b43, 0x0b45, 0x0b48 }, /* 1f84 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b4b, 0x0b4d, 0x0b50 }, /* 1f85 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0000, 0x0b53, 0x0b55, 0x0b58 }, /* 1f86 GREEK SMALL LETTER ALPHA WITH PSILI AND */ { 0x0000, 0x0b5b, 0x0b5d, 0x0b60 }, /* 1f87 GREEK SMALL LETTER ALPHA WITH DASIA AND */ { 0x0b63, 0x0000, 0x0b65, 0x0b68 }, /* 1f88 GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b6b, 0x0000, 0x0b6d, 0x0b70 }, /* 1f89 GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b73, 0x0000, 0x0b75, 0x0b78 }, /* 1f8a GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b7b, 0x0000, 0x0b7d, 0x0b80 }, /* 1f8b GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b83, 0x0000, 0x0b85, 0x0b88 }, /* 1f8c GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b8b, 0x0000, 0x0b8d, 0x0b90 }, /* 1f8d GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0b93, 0x0000, 0x0b95, 0x0b98 }, /* 1f8e GREEK CAPITAL LETTER ALPHA WITH PSILI A */ { 0x0b9b, 0x0000, 0x0b9d, 0x0ba0 }, /* 1f8f GREEK CAPITAL LETTER ALPHA WITH DASIA A */ { 0x0000, 0x0ba3, 0x0ba5, 0x0ba8 }, /* 1f90 GREEK SMALL LETTER ETA WITH PSILI AND Y */ { 0x0000, 0x0bab, 0x0bad, 0x0bb0 }, /* 1f91 GREEK SMALL LETTER ETA WITH DASIA AND Y */ { 0x0000, 0x0bb3, 0x0bb5, 0x0bb8 }, /* 1f92 GREEK SMALL LETTER ETA WITH PSILI AND V */ { 0x0000, 0x0bbb, 0x0bbd, 0x0bc0 }, /* 1f93 GREEK SMALL LETTER ETA WITH DASIA AND V */ { 0x0000, 0x0bc3, 0x0bc5, 0x0bc8 }, /* 1f94 GREEK SMALL LETTER ETA WITH PSILI AND O */ { 0x0000, 0x0bcb, 0x0bcd, 0x0bd0 }, /* 1f95 GREEK SMALL LETTER ETA WITH DASIA AND O */ { 0x0000, 0x0bd3, 0x0bd5, 0x0bd8 }, /* 1f96 GREEK SMALL LETTER ETA WITH PSILI AND P */ { 0x0000, 0x0bdb, 0x0bdd, 0x0be0 }, /* 1f97 GREEK SMALL LETTER ETA WITH DASIA AND P */ { 0x0be3, 0x0000, 0x0be5, 0x0be8 }, /* 1f98 GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0beb, 0x0000, 0x0bed, 0x0bf0 }, /* 1f99 GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0bf3, 0x0000, 0x0bf5, 0x0bf8 }, /* 1f9a GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0bfb, 0x0000, 0x0bfd, 0x0c00 }, /* 1f9b GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0c03, 0x0000, 0x0c05, 0x0c08 }, /* 1f9c GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0c0b, 0x0000, 0x0c0d, 0x0c10 }, /* 1f9d GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0c13, 0x0000, 0x0c15, 0x0c18 }, /* 1f9e GREEK CAPITAL LETTER ETA WITH PSILI AND */ { 0x0c1b, 0x0000, 0x0c1d, 0x0c20 }, /* 1f9f GREEK CAPITAL LETTER ETA WITH DASIA AND */ { 0x0000, 0x0c23, 0x0c25, 0x0c28 }, /* 1fa0 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c2b, 0x0c2d, 0x0c30 }, /* 1fa1 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c33, 0x0c35, 0x0c38 }, /* 1fa2 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c3b, 0x0c3d, 0x0c40 }, /* 1fa3 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c43, 0x0c45, 0x0c48 }, /* 1fa4 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c4b, 0x0c4d, 0x0c50 }, /* 1fa5 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0000, 0x0c53, 0x0c55, 0x0c58 }, /* 1fa6 GREEK SMALL LETTER OMEGA WITH PSILI AND */ { 0x0000, 0x0c5b, 0x0c5d, 0x0c60 }, /* 1fa7 GREEK SMALL LETTER OMEGA WITH DASIA AND */ { 0x0c63, 0x0000, 0x0c65, 0x0c68 }, /* 1fa8 GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c6b, 0x0000, 0x0c6d, 0x0c70 }, /* 1fa9 GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c73, 0x0000, 0x0c75, 0x0c78 }, /* 1faa GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c7b, 0x0000, 0x0c7d, 0x0c80 }, /* 1fab GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c83, 0x0000, 0x0c85, 0x0c88 }, /* 1fac GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c8b, 0x0000, 0x0c8d, 0x0c90 }, /* 1fad GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0c93, 0x0000, 0x0c95, 0x0c98 }, /* 1fae GREEK CAPITAL LETTER OMEGA WITH PSILI A */ { 0x0c9b, 0x0000, 0x0c9d, 0x0ca0 }, /* 1faf GREEK CAPITAL LETTER OMEGA WITH DASIA A */ { 0x0000, 0x0ca3, 0x0ca3, 0x0000 }, /* 1fb0 GREEK SMALL LETTER ALPHA WITH VRACHY */ { 0x0000, 0x0ca5, 0x0ca5, 0x0000 }, /* 1fb1 GREEK SMALL LETTER ALPHA WITH MACRON */ { 0x0000, 0x0ca7, 0x0caa, 0x0cad }, /* 1fb2 GREEK SMALL LETTER ALPHA WITH VARIA AND */ { 0x0000, 0x0cb0, 0x0cb2, 0x0cb5 }, /* 1fb3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAM */ { 0x0000, 0x0cb8, 0x0cbb, 0x0cbe }, /* 1fb4 GREEK SMALL LETTER ALPHA WITH OXIA AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fb5 (null) */ { 0x0000, 0x0cc1, 0x0cc1, 0x0cc4 }, /* 1fb6 GREEK SMALL LETTER ALPHA WITH PERISPOME */ { 0x0000, 0x0cc7, 0x0ccb, 0x0ccf }, /* 1fb7 GREEK SMALL LETTER ALPHA WITH PERISPOME */ { 0x0cd3, 0x0000, 0x0000, 0x0cd3 }, /* 1fb8 GREEK CAPITAL LETTER ALPHA WITH VRACHY */ { 0x0cd5, 0x0000, 0x0000, 0x0cd5 }, /* 1fb9 GREEK CAPITAL LETTER ALPHA WITH MACRON */ { 0x0cd7, 0x0000, 0x0000, 0x0cd7 }, /* 1fba GREEK CAPITAL LETTER ALPHA WITH VARIA */ { 0x0cd9, 0x0000, 0x0000, 0x0cd9 }, /* 1fbb GREEK CAPITAL LETTER ALPHA WITH OXIA */ { 0x0cdb, 0x0000, 0x0cdd, 0x0ce0 }, /* 1fbc GREEK CAPITAL LETTER ALPHA WITH PROSGEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fbd GREEK KORONIS */ { 0x0000, 0x0ce3, 0x0ce3, 0x0ce5 }, /* 1fbe GREEK PROSGEGRAMMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fbf GREEK PSILI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc0 GREEK PERISPOMENI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc1 GREEK DIALYTIKA AND PERISPOMENI */ { 0x0000, 0x0ce7, 0x0cea, 0x0ced }, /* 1fc2 GREEK SMALL LETTER ETA WITH VARIA AND Y */ { 0x0000, 0x0cf0, 0x0cf2, 0x0cf5 }, /* 1fc3 GREEK SMALL LETTER ETA WITH YPOGEGRAMME */ { 0x0000, 0x0cf8, 0x0cfb, 0x0cfe }, /* 1fc4 GREEK SMALL LETTER ETA WITH OXIA AND YP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fc5 (null) */ { 0x0000, 0x0d01, 0x0d01, 0x0d04 }, /* 1fc6 GREEK SMALL LETTER ETA WITH PERISPOMENI */ { 0x0000, 0x0d07, 0x0d0b, 0x0d0f }, /* 1fc7 GREEK SMALL LETTER ETA WITH PERISPOMENI */ { 0x0d13, 0x0000, 0x0000, 0x0d13 }, /* 1fc8 GREEK CAPITAL LETTER EPSILON WITH VARIA */ { 0x0d15, 0x0000, 0x0000, 0x0d15 }, /* 1fc9 GREEK CAPITAL LETTER EPSILON WITH OXIA */ { 0x0d17, 0x0000, 0x0000, 0x0d17 }, /* 1fca GREEK CAPITAL LETTER ETA WITH VARIA */ { 0x0d19, 0x0000, 0x0000, 0x0d19 }, /* 1fcb GREEK CAPITAL LETTER ETA WITH OXIA */ { 0x0d1b, 0x0000, 0x0d1d, 0x0d20 }, /* 1fcc GREEK CAPITAL LETTER ETA WITH PROSGEGRA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fcd GREEK PSILI AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fce GREEK PSILI AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fcf GREEK PSILI AND PERISPOMENI */ { 0x0000, 0x0d23, 0x0d23, 0x0000 }, /* 1fd0 GREEK SMALL LETTER IOTA WITH VRACHY */ { 0x0000, 0x0d25, 0x0d25, 0x0000 }, /* 1fd1 GREEK SMALL LETTER IOTA WITH MACRON */ { 0x0000, 0x0d27, 0x0d27, 0x0d2b }, /* 1fd2 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0d2f, 0x0d2f, 0x0d33 }, /* 1fd3 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fd4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fd5 (null) */ { 0x0000, 0x0d37, 0x0d37, 0x0d3a }, /* 1fd6 GREEK SMALL LETTER IOTA WITH PERISPOMEN */ { 0x0000, 0x0d3d, 0x0d3d, 0x0d41 }, /* 1fd7 GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x0d45, 0x0000, 0x0000, 0x0d45 }, /* 1fd8 GREEK CAPITAL LETTER IOTA WITH VRACHY */ { 0x0d47, 0x0000, 0x0000, 0x0d47 }, /* 1fd9 GREEK CAPITAL LETTER IOTA WITH MACRON */ { 0x0d49, 0x0000, 0x0000, 0x0d49 }, /* 1fda GREEK CAPITAL LETTER IOTA WITH VARIA */ { 0x0d4b, 0x0000, 0x0000, 0x0d4b }, /* 1fdb GREEK CAPITAL LETTER IOTA WITH OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdd GREEK DASIA AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fde GREEK DASIA AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fdf GREEK DASIA AND PERISPOMENI */ { 0x0000, 0x0d4d, 0x0d4d, 0x0000 }, /* 1fe0 GREEK SMALL LETTER UPSILON WITH VRACHY */ { 0x0000, 0x0d4f, 0x0d4f, 0x0000 }, /* 1fe1 GREEK SMALL LETTER UPSILON WITH MACRON */ { 0x0000, 0x0d51, 0x0d51, 0x0d55 }, /* 1fe2 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0d59, 0x0d59, 0x0d5d }, /* 1fe3 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0000, 0x0d61, 0x0d61, 0x0d64 }, /* 1fe4 GREEK SMALL LETTER RHO WITH PSILI */ { 0x0000, 0x0d67, 0x0d67, 0x0000 }, /* 1fe5 GREEK SMALL LETTER RHO WITH DASIA */ { 0x0000, 0x0d69, 0x0d69, 0x0d6c }, /* 1fe6 GREEK SMALL LETTER UPSILON WITH PERISPO */ { 0x0000, 0x0d6f, 0x0d6f, 0x0d73 }, /* 1fe7 GREEK SMALL LETTER UPSILON WITH DIALYTI */ { 0x0d77, 0x0000, 0x0000, 0x0d77 }, /* 1fe8 GREEK CAPITAL LETTER UPSILON WITH VRACH */ { 0x0d79, 0x0000, 0x0000, 0x0d79 }, /* 1fe9 GREEK CAPITAL LETTER UPSILON WITH MACRO */ { 0x0d7b, 0x0000, 0x0000, 0x0d7b }, /* 1fea GREEK CAPITAL LETTER UPSILON WITH VARIA */ { 0x0d7d, 0x0000, 0x0000, 0x0d7d }, /* 1feb GREEK CAPITAL LETTER UPSILON WITH OXIA */ { 0x0d7f, 0x0000, 0x0000, 0x0d7f }, /* 1fec GREEK CAPITAL LETTER RHO WITH DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fed GREEK DIALYTIKA AND VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fee GREEK DIALYTIKA AND OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1fef GREEK VARIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff1 (null) */ { 0x0000, 0x0d81, 0x0d84, 0x0d87 }, /* 1ff2 GREEK SMALL LETTER OMEGA WITH VARIA AND */ { 0x0000, 0x0d8a, 0x0d8c, 0x0d8f }, /* 1ff3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAM */ { 0x0000, 0x0d92, 0x0d95, 0x0d98 }, /* 1ff4 GREEK SMALL LETTER OMEGA WITH OXIA AND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ff5 (null) */ { 0x0000, 0x0d9b, 0x0d9b, 0x0d9e }, /* 1ff6 GREEK SMALL LETTER OMEGA WITH PERISPOME */ { 0x0000, 0x0da1, 0x0da5, 0x0da9 }, /* 1ff7 GREEK SMALL LETTER OMEGA WITH PERISPOME */ { 0x0dad, 0x0000, 0x0000, 0x0dad }, /* 1ff8 GREEK CAPITAL LETTER OMICRON WITH VARIA */ { 0x0daf, 0x0000, 0x0000, 0x0daf }, /* 1ff9 GREEK CAPITAL LETTER OMICRON WITH OXIA */ { 0x0db1, 0x0000, 0x0000, 0x0db1 }, /* 1ffa GREEK CAPITAL LETTER OMEGA WITH VARIA */ { 0x0db3, 0x0000, 0x0000, 0x0db3 }, /* 1ffb GREEK CAPITAL LETTER OMEGA WITH OXIA */ { 0x0db5, 0x0000, 0x0db7, 0x0dba }, /* 1ffc GREEK CAPITAL LETTER OMEGA WITH PROSGEG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ffd GREEK OXIA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 1ffe GREEK DASIA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 1fff (null) */ }; static case_table case_pg_042[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2100 ACCOUNT OF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2101 ADDRESSED TO THE SUBJECT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2102 DOUBLE-STRUCK CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2103 DEGREE CELSIUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2104 CENTRE LINE SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2105 CARE OF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2106 CADA UNA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2107 EULER CONSTANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2108 SCRUPLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2109 DEGREE FAHRENHEIT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210a SCRIPT SMALL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210b SCRIPT CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210c BLACK-LETTER CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210d DOUBLE-STRUCK CAPITAL H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210e PLANCK CONSTANT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 210f PLANCK CONSTANT OVER TWO PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2110 SCRIPT CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2111 BLACK-LETTER CAPITAL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2112 SCRIPT CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2113 SCRIPT SMALL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2114 L B BAR SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2115 DOUBLE-STRUCK CAPITAL N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2116 NUMERO SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2117 SOUND RECORDING COPYRIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2118 SCRIPT CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2119 DOUBLE-STRUCK CAPITAL P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211a DOUBLE-STRUCK CAPITAL Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211b SCRIPT CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211c BLACK-LETTER CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211d DOUBLE-STRUCK CAPITAL R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211e PRESCRIPTION TAKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 211f RESPONSE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2120 SERVICE MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2121 TELEPHONE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2122 TRADE MARK SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2123 VERSICLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2124 DOUBLE-STRUCK CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2125 OUNCE SIGN */ { 0x0dbd, 0x0000, 0x0000, 0x0dbd }, /* 2126 OHM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2127 INVERTED OHM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2128 BLACK-LETTER CAPITAL Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2129 TURNED GREEK SMALL LETTER IOTA */ { 0x0dbf, 0x0000, 0x0000, 0x0dbf }, /* 212a KELVIN SIGN */ { 0x0dc1, 0x0000, 0x0000, 0x0dc1 }, /* 212b ANGSTROM SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212c SCRIPT CAPITAL B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212d BLACK-LETTER CAPITAL C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212e ESTIMATED SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 212f SCRIPT SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2130 SCRIPT CAPITAL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2131 SCRIPT CAPITAL F */ { 0x0dc3, 0x0000, 0x0000, 0x0dc3 }, /* 2132 TURNED CAPITAL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2133 SCRIPT CAPITAL M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2134 SCRIPT SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2135 ALEF SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2136 BET SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2137 GIMEL SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2138 DALET SYMBOL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2139 INFORMATION SOURCE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213a ROTATED CAPITAL Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213b FACSIMILE SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213c DOUBLE-STRUCK SMALL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213d DOUBLE-STRUCK SMALL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213e DOUBLE-STRUCK CAPITAL GAMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 213f DOUBLE-STRUCK CAPITAL PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2140 DOUBLE-STRUCK N-ARY SUMMATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2141 TURNED SANS-SERIF CAPITAL G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2142 TURNED SANS-SERIF CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2143 REVERSED SANS-SERIF CAPITAL L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2144 TURNED SANS-SERIF CAPITAL Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2145 DOUBLE-STRUCK ITALIC CAPITAL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2146 DOUBLE-STRUCK ITALIC SMALL D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2147 DOUBLE-STRUCK ITALIC SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2148 DOUBLE-STRUCK ITALIC SMALL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2149 DOUBLE-STRUCK ITALIC SMALL J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214a PROPERTY LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214b TURNED AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214c PER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214d AKTIESELSKAB */ { 0x0000, 0x0dc5, 0x0dc5, 0x0000 }, /* 214e TURNED SMALL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 214f SYMBOL FOR SAMARITAN SOURCE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2150 VULGAR FRACTION ONE SEVENTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2151 VULGAR FRACTION ONE NINTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2152 VULGAR FRACTION ONE TENTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2153 VULGAR FRACTION ONE THIRD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2154 VULGAR FRACTION TWO THIRDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2155 VULGAR FRACTION ONE FIFTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2156 VULGAR FRACTION TWO FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2157 VULGAR FRACTION THREE FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2158 VULGAR FRACTION FOUR FIFTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2159 VULGAR FRACTION ONE SIXTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215a VULGAR FRACTION FIVE SIXTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215b VULGAR FRACTION ONE EIGHTH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215c VULGAR FRACTION THREE EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215d VULGAR FRACTION FIVE EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215e VULGAR FRACTION SEVEN EIGHTHS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 215f FRACTION NUMERATOR ONE */ { 0x0dc7, 0x0000, 0x0000, 0x0dc7 }, /* 2160 ROMAN NUMERAL ONE */ { 0x0dc9, 0x0000, 0x0000, 0x0dc9 }, /* 2161 ROMAN NUMERAL TWO */ { 0x0dcb, 0x0000, 0x0000, 0x0dcb }, /* 2162 ROMAN NUMERAL THREE */ { 0x0dcd, 0x0000, 0x0000, 0x0dcd }, /* 2163 ROMAN NUMERAL FOUR */ { 0x0dcf, 0x0000, 0x0000, 0x0dcf }, /* 2164 ROMAN NUMERAL FIVE */ { 0x0dd1, 0x0000, 0x0000, 0x0dd1 }, /* 2165 ROMAN NUMERAL SIX */ { 0x0dd3, 0x0000, 0x0000, 0x0dd3 }, /* 2166 ROMAN NUMERAL SEVEN */ { 0x0dd5, 0x0000, 0x0000, 0x0dd5 }, /* 2167 ROMAN NUMERAL EIGHT */ { 0x0dd7, 0x0000, 0x0000, 0x0dd7 }, /* 2168 ROMAN NUMERAL NINE */ { 0x0dd9, 0x0000, 0x0000, 0x0dd9 }, /* 2169 ROMAN NUMERAL TEN */ { 0x0ddb, 0x0000, 0x0000, 0x0ddb }, /* 216a ROMAN NUMERAL ELEVEN */ { 0x0ddd, 0x0000, 0x0000, 0x0ddd }, /* 216b ROMAN NUMERAL TWELVE */ { 0x0ddf, 0x0000, 0x0000, 0x0ddf }, /* 216c ROMAN NUMERAL FIFTY */ { 0x0de1, 0x0000, 0x0000, 0x0de1 }, /* 216d ROMAN NUMERAL ONE HUNDRED */ { 0x0de3, 0x0000, 0x0000, 0x0de3 }, /* 216e ROMAN NUMERAL FIVE HUNDRED */ { 0x0de5, 0x0000, 0x0000, 0x0de5 }, /* 216f ROMAN NUMERAL ONE THOUSAND */ { 0x0000, 0x0de7, 0x0de7, 0x0000 }, /* 2170 SMALL ROMAN NUMERAL ONE */ { 0x0000, 0x0de9, 0x0de9, 0x0000 }, /* 2171 SMALL ROMAN NUMERAL TWO */ { 0x0000, 0x0deb, 0x0deb, 0x0000 }, /* 2172 SMALL ROMAN NUMERAL THREE */ { 0x0000, 0x0ded, 0x0ded, 0x0000 }, /* 2173 SMALL ROMAN NUMERAL FOUR */ { 0x0000, 0x0def, 0x0def, 0x0000 }, /* 2174 SMALL ROMAN NUMERAL FIVE */ { 0x0000, 0x0df1, 0x0df1, 0x0000 }, /* 2175 SMALL ROMAN NUMERAL SIX */ { 0x0000, 0x0df3, 0x0df3, 0x0000 }, /* 2176 SMALL ROMAN NUMERAL SEVEN */ { 0x0000, 0x0df5, 0x0df5, 0x0000 }, /* 2177 SMALL ROMAN NUMERAL EIGHT */ { 0x0000, 0x0df7, 0x0df7, 0x0000 }, /* 2178 SMALL ROMAN NUMERAL NINE */ { 0x0000, 0x0df9, 0x0df9, 0x0000 }, /* 2179 SMALL ROMAN NUMERAL TEN */ { 0x0000, 0x0dfb, 0x0dfb, 0x0000 }, /* 217a SMALL ROMAN NUMERAL ELEVEN */ { 0x0000, 0x0dfd, 0x0dfd, 0x0000 }, /* 217b SMALL ROMAN NUMERAL TWELVE */ { 0x0000, 0x0dff, 0x0dff, 0x0000 }, /* 217c SMALL ROMAN NUMERAL FIFTY */ { 0x0000, 0x0e01, 0x0e01, 0x0000 }, /* 217d SMALL ROMAN NUMERAL ONE HUNDRED */ { 0x0000, 0x0e03, 0x0e03, 0x0000 }, /* 217e SMALL ROMAN NUMERAL FIVE HUNDRED */ { 0x0000, 0x0e05, 0x0e05, 0x0000 } /* 217f SMALL ROMAN NUMERAL ONE THOUSAND */ }; static case_table case_pg_043[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2180 ROMAN NUMERAL ONE THOUSAND C D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2181 ROMAN NUMERAL FIVE THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2182 ROMAN NUMERAL TEN THOUSAND */ { 0x0e07, 0x0000, 0x0000, 0x0e07 }, /* 2183 ROMAN NUMERAL REVERSED ONE HUNDRED */ { 0x0000, 0x0e09, 0x0e09, 0x0000 }, /* 2184 LATIN SMALL LETTER REVERSED C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2185 ROMAN NUMERAL SIX LATE FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2186 ROMAN NUMERAL FIFTY EARLY FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2187 ROMAN NUMERAL FIFTY THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2188 ROMAN NUMERAL ONE HUNDRED THOUSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2189 VULGAR FRACTION ZERO THIRDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 218f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2190 LEFTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2191 UPWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2192 RIGHTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2193 DOWNWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2194 LEFT RIGHT ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2195 UP DOWN ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2196 NORTH WEST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2197 NORTH EAST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2198 SOUTH EAST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2199 SOUTH WEST ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219a LEFTWARDS ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219b RIGHTWARDS ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219c LEFTWARDS WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219d RIGHTWARDS WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219e LEFTWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 219f UPWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a0 RIGHTWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a1 DOWNWARDS TWO HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a2 LEFTWARDS ARROW WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a3 RIGHTWARDS ARROW WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a4 LEFTWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a5 UPWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a6 RIGHTWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a7 DOWNWARDS ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a8 UP DOWN ARROW WITH BASE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21a9 LEFTWARDS ARROW WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21aa RIGHTWARDS ARROW WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ab LEFTWARDS ARROW WITH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ac RIGHTWARDS ARROW WITH LOOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ad LEFT RIGHT WAVE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ae LEFT RIGHT ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21af DOWNWARDS ZIGZAG ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b0 UPWARDS ARROW WITH TIP LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b1 UPWARDS ARROW WITH TIP RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b2 DOWNWARDS ARROW WITH TIP LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b3 DOWNWARDS ARROW WITH TIP RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b4 RIGHTWARDS ARROW WITH CORNER DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b6 ANTICLOCKWISE TOP SEMICIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b7 CLOCKWISE TOP SEMICIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b8 NORTH WEST ARROW TO LONG BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21b9 LEFTWARDS ARROW TO BAR OVER RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ba ANTICLOCKWISE OPEN CIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bb CLOCKWISE OPEN CIRCLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bc LEFTWARDS HARPOON WITH BARB UPWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bd LEFTWARDS HARPOON WITH BARB DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21be UPWARDS HARPOON WITH BARB RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21bf UPWARDS HARPOON WITH BARB LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c0 RIGHTWARDS HARPOON WITH BARB UPWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c1 RIGHTWARDS HARPOON WITH BARB DOWNWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c2 DOWNWARDS HARPOON WITH BARB RIGHTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c3 DOWNWARDS HARPOON WITH BARB LEFTWARDS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c4 RIGHTWARDS ARROW OVER LEFTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c5 UPWARDS ARROW LEFTWARDS OF DOWNWARDS AR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c6 LEFTWARDS ARROW OVER RIGHTWARDS ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c7 LEFTWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c8 UPWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21c9 RIGHTWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ca DOWNWARDS PAIRED ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cb LEFTWARDS HARPOON OVER RIGHTWARDS HARPO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cc RIGHTWARDS HARPOON OVER LEFTWARDS HARPO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cd LEFTWARDS DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ce LEFT RIGHT DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21cf RIGHTWARDS DOUBLE ARROW WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d0 LEFTWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d1 UPWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d2 RIGHTWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d3 DOWNWARDS DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d4 LEFT RIGHT DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d5 UP DOWN DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d6 NORTH WEST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d7 NORTH EAST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d8 SOUTH EAST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21d9 SOUTH WEST DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21da LEFTWARDS TRIPLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21db RIGHTWARDS TRIPLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21dc LEFTWARDS SQUIGGLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21dd RIGHTWARDS SQUIGGLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21de UPWARDS ARROW WITH DOUBLE STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21df DOWNWARDS ARROW WITH DOUBLE STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e0 LEFTWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e1 UPWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e2 RIGHTWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e3 DOWNWARDS DASHED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e4 LEFTWARDS ARROW TO BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e5 RIGHTWARDS ARROW TO BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e6 LEFTWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e7 UPWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e8 RIGHTWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21e9 DOWNWARDS WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ea UPWARDS WHITE ARROW FROM BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21eb UPWARDS WHITE ARROW ON PEDESTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ec UPWARDS WHITE ARROW ON PEDESTAL WITH HO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ed UPWARDS WHITE ARROW ON PEDESTAL WITH VE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ee UPWARDS WHITE DOUBLE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21ef UPWARDS WHITE DOUBLE ARROW ON PEDESTAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f0 RIGHTWARDS WHITE ARROW FROM WALL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f1 NORTH WEST ARROW TO CORNER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f2 SOUTH EAST ARROW TO CORNER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f3 UP DOWN WHITE ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f4 RIGHT ARROW WITH SMALL CIRCLE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f5 DOWNWARDS ARROW LEFTWARDS OF UPWARDS AR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f6 THREE RIGHTWARDS ARROWS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f7 LEFTWARDS ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f8 RIGHTWARDS ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21f9 LEFT RIGHT ARROW WITH VERTICAL STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fa LEFTWARDS ARROW WITH DOUBLE VERTICAL ST */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fb RIGHTWARDS ARROW WITH DOUBLE VERTICAL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fc LEFT RIGHT ARROW WITH DOUBLE VERTICAL S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fd LEFTWARDS OPEN-HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 21fe RIGHTWARDS OPEN-HEADED ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 21ff LEFT RIGHT OPEN-HEADED ARROW */ }; static case_table case_pg_049[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2480 PARENTHESIZED NUMBER THIRTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2481 PARENTHESIZED NUMBER FOURTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2482 PARENTHESIZED NUMBER FIFTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2483 PARENTHESIZED NUMBER SIXTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2484 PARENTHESIZED NUMBER SEVENTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2485 PARENTHESIZED NUMBER EIGHTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2486 PARENTHESIZED NUMBER NINETEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2487 PARENTHESIZED NUMBER TWENTY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2488 DIGIT ONE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2489 DIGIT TWO FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248a DIGIT THREE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248b DIGIT FOUR FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248c DIGIT FIVE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248d DIGIT SIX FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248e DIGIT SEVEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 248f DIGIT EIGHT FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2490 DIGIT NINE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2491 NUMBER TEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2492 NUMBER ELEVEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2493 NUMBER TWELVE FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2494 NUMBER THIRTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2495 NUMBER FOURTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2496 NUMBER FIFTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2497 NUMBER SIXTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2498 NUMBER SEVENTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2499 NUMBER EIGHTEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249a NUMBER NINETEEN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249b NUMBER TWENTY FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249c PARENTHESIZED LATIN SMALL LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249d PARENTHESIZED LATIN SMALL LETTER B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249e PARENTHESIZED LATIN SMALL LETTER C */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 249f PARENTHESIZED LATIN SMALL LETTER D */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a0 PARENTHESIZED LATIN SMALL LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a1 PARENTHESIZED LATIN SMALL LETTER F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a2 PARENTHESIZED LATIN SMALL LETTER G */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a3 PARENTHESIZED LATIN SMALL LETTER H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a4 PARENTHESIZED LATIN SMALL LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a5 PARENTHESIZED LATIN SMALL LETTER J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a6 PARENTHESIZED LATIN SMALL LETTER K */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a7 PARENTHESIZED LATIN SMALL LETTER L */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a8 PARENTHESIZED LATIN SMALL LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24a9 PARENTHESIZED LATIN SMALL LETTER N */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24aa PARENTHESIZED LATIN SMALL LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ab PARENTHESIZED LATIN SMALL LETTER P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ac PARENTHESIZED LATIN SMALL LETTER Q */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ad PARENTHESIZED LATIN SMALL LETTER R */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ae PARENTHESIZED LATIN SMALL LETTER S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24af PARENTHESIZED LATIN SMALL LETTER T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b0 PARENTHESIZED LATIN SMALL LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b1 PARENTHESIZED LATIN SMALL LETTER V */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b2 PARENTHESIZED LATIN SMALL LETTER W */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b3 PARENTHESIZED LATIN SMALL LETTER X */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b4 PARENTHESIZED LATIN SMALL LETTER Y */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24b5 PARENTHESIZED LATIN SMALL LETTER Z */ { 0x0e0b, 0x0000, 0x0000, 0x0e0b }, /* 24b6 CIRCLED LATIN CAPITAL LETTER A */ { 0x0e0d, 0x0000, 0x0000, 0x0e0d }, /* 24b7 CIRCLED LATIN CAPITAL LETTER B */ { 0x0e0f, 0x0000, 0x0000, 0x0e0f }, /* 24b8 CIRCLED LATIN CAPITAL LETTER C */ { 0x0e11, 0x0000, 0x0000, 0x0e11 }, /* 24b9 CIRCLED LATIN CAPITAL LETTER D */ { 0x0e13, 0x0000, 0x0000, 0x0e13 }, /* 24ba CIRCLED LATIN CAPITAL LETTER E */ { 0x0e15, 0x0000, 0x0000, 0x0e15 }, /* 24bb CIRCLED LATIN CAPITAL LETTER F */ { 0x0e17, 0x0000, 0x0000, 0x0e17 }, /* 24bc CIRCLED LATIN CAPITAL LETTER G */ { 0x0e19, 0x0000, 0x0000, 0x0e19 }, /* 24bd CIRCLED LATIN CAPITAL LETTER H */ { 0x0e1b, 0x0000, 0x0000, 0x0e1b }, /* 24be CIRCLED LATIN CAPITAL LETTER I */ { 0x0e1d, 0x0000, 0x0000, 0x0e1d }, /* 24bf CIRCLED LATIN CAPITAL LETTER J */ { 0x0e1f, 0x0000, 0x0000, 0x0e1f }, /* 24c0 CIRCLED LATIN CAPITAL LETTER K */ { 0x0e21, 0x0000, 0x0000, 0x0e21 }, /* 24c1 CIRCLED LATIN CAPITAL LETTER L */ { 0x0e23, 0x0000, 0x0000, 0x0e23 }, /* 24c2 CIRCLED LATIN CAPITAL LETTER M */ { 0x0e25, 0x0000, 0x0000, 0x0e25 }, /* 24c3 CIRCLED LATIN CAPITAL LETTER N */ { 0x0e27, 0x0000, 0x0000, 0x0e27 }, /* 24c4 CIRCLED LATIN CAPITAL LETTER O */ { 0x0e29, 0x0000, 0x0000, 0x0e29 }, /* 24c5 CIRCLED LATIN CAPITAL LETTER P */ { 0x0e2b, 0x0000, 0x0000, 0x0e2b }, /* 24c6 CIRCLED LATIN CAPITAL LETTER Q */ { 0x0e2d, 0x0000, 0x0000, 0x0e2d }, /* 24c7 CIRCLED LATIN CAPITAL LETTER R */ { 0x0e2f, 0x0000, 0x0000, 0x0e2f }, /* 24c8 CIRCLED LATIN CAPITAL LETTER S */ { 0x0e31, 0x0000, 0x0000, 0x0e31 }, /* 24c9 CIRCLED LATIN CAPITAL LETTER T */ { 0x0e33, 0x0000, 0x0000, 0x0e33 }, /* 24ca CIRCLED LATIN CAPITAL LETTER U */ { 0x0e35, 0x0000, 0x0000, 0x0e35 }, /* 24cb CIRCLED LATIN CAPITAL LETTER V */ { 0x0e37, 0x0000, 0x0000, 0x0e37 }, /* 24cc CIRCLED LATIN CAPITAL LETTER W */ { 0x0e39, 0x0000, 0x0000, 0x0e39 }, /* 24cd CIRCLED LATIN CAPITAL LETTER X */ { 0x0e3b, 0x0000, 0x0000, 0x0e3b }, /* 24ce CIRCLED LATIN CAPITAL LETTER Y */ { 0x0e3d, 0x0000, 0x0000, 0x0e3d }, /* 24cf CIRCLED LATIN CAPITAL LETTER Z */ { 0x0000, 0x0e3f, 0x0e3f, 0x0000 }, /* 24d0 CIRCLED LATIN SMALL LETTER A */ { 0x0000, 0x0e41, 0x0e41, 0x0000 }, /* 24d1 CIRCLED LATIN SMALL LETTER B */ { 0x0000, 0x0e43, 0x0e43, 0x0000 }, /* 24d2 CIRCLED LATIN SMALL LETTER C */ { 0x0000, 0x0e45, 0x0e45, 0x0000 }, /* 24d3 CIRCLED LATIN SMALL LETTER D */ { 0x0000, 0x0e47, 0x0e47, 0x0000 }, /* 24d4 CIRCLED LATIN SMALL LETTER E */ { 0x0000, 0x0e49, 0x0e49, 0x0000 }, /* 24d5 CIRCLED LATIN SMALL LETTER F */ { 0x0000, 0x0e4b, 0x0e4b, 0x0000 }, /* 24d6 CIRCLED LATIN SMALL LETTER G */ { 0x0000, 0x0e4d, 0x0e4d, 0x0000 }, /* 24d7 CIRCLED LATIN SMALL LETTER H */ { 0x0000, 0x0e4f, 0x0e4f, 0x0000 }, /* 24d8 CIRCLED LATIN SMALL LETTER I */ { 0x0000, 0x0e51, 0x0e51, 0x0000 }, /* 24d9 CIRCLED LATIN SMALL LETTER J */ { 0x0000, 0x0e53, 0x0e53, 0x0000 }, /* 24da CIRCLED LATIN SMALL LETTER K */ { 0x0000, 0x0e55, 0x0e55, 0x0000 }, /* 24db CIRCLED LATIN SMALL LETTER L */ { 0x0000, 0x0e57, 0x0e57, 0x0000 }, /* 24dc CIRCLED LATIN SMALL LETTER M */ { 0x0000, 0x0e59, 0x0e59, 0x0000 }, /* 24dd CIRCLED LATIN SMALL LETTER N */ { 0x0000, 0x0e5b, 0x0e5b, 0x0000 }, /* 24de CIRCLED LATIN SMALL LETTER O */ { 0x0000, 0x0e5d, 0x0e5d, 0x0000 }, /* 24df CIRCLED LATIN SMALL LETTER P */ { 0x0000, 0x0e5f, 0x0e5f, 0x0000 }, /* 24e0 CIRCLED LATIN SMALL LETTER Q */ { 0x0000, 0x0e61, 0x0e61, 0x0000 }, /* 24e1 CIRCLED LATIN SMALL LETTER R */ { 0x0000, 0x0e63, 0x0e63, 0x0000 }, /* 24e2 CIRCLED LATIN SMALL LETTER S */ { 0x0000, 0x0e65, 0x0e65, 0x0000 }, /* 24e3 CIRCLED LATIN SMALL LETTER T */ { 0x0000, 0x0e67, 0x0e67, 0x0000 }, /* 24e4 CIRCLED LATIN SMALL LETTER U */ { 0x0000, 0x0e69, 0x0e69, 0x0000 }, /* 24e5 CIRCLED LATIN SMALL LETTER V */ { 0x0000, 0x0e6b, 0x0e6b, 0x0000 }, /* 24e6 CIRCLED LATIN SMALL LETTER W */ { 0x0000, 0x0e6d, 0x0e6d, 0x0000 }, /* 24e7 CIRCLED LATIN SMALL LETTER X */ { 0x0000, 0x0e6f, 0x0e6f, 0x0000 }, /* 24e8 CIRCLED LATIN SMALL LETTER Y */ { 0x0000, 0x0e71, 0x0e71, 0x0000 }, /* 24e9 CIRCLED LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ea CIRCLED DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24eb NEGATIVE CIRCLED NUMBER ELEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ec NEGATIVE CIRCLED NUMBER TWELVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ed NEGATIVE CIRCLED NUMBER THIRTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ee NEGATIVE CIRCLED NUMBER FOURTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24ef NEGATIVE CIRCLED NUMBER FIFTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f0 NEGATIVE CIRCLED NUMBER SIXTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f1 NEGATIVE CIRCLED NUMBER SEVENTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f2 NEGATIVE CIRCLED NUMBER EIGHTEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f3 NEGATIVE CIRCLED NUMBER NINETEEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f4 NEGATIVE CIRCLED NUMBER TWENTY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f5 DOUBLE CIRCLED DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f6 DOUBLE CIRCLED DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f7 DOUBLE CIRCLED DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f8 DOUBLE CIRCLED DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24f9 DOUBLE CIRCLED DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fa DOUBLE CIRCLED DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fb DOUBLE CIRCLED DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fc DOUBLE CIRCLED DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fd DOUBLE CIRCLED DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 24fe DOUBLE CIRCLED NUMBER TEN */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 24ff NEGATIVE CIRCLED DIGIT ZERO */ }; static case_table case_pg_058[128] = { { 0x0e73, 0x0000, 0x0000, 0x0e73 }, /* 2c00 GLAGOLITIC CAPITAL LETTER AZU */ { 0x0e75, 0x0000, 0x0000, 0x0e75 }, /* 2c01 GLAGOLITIC CAPITAL LETTER BUKY */ { 0x0e77, 0x0000, 0x0000, 0x0e77 }, /* 2c02 GLAGOLITIC CAPITAL LETTER VEDE */ { 0x0e79, 0x0000, 0x0000, 0x0e79 }, /* 2c03 GLAGOLITIC CAPITAL LETTER GLAGOLI */ { 0x0e7b, 0x0000, 0x0000, 0x0e7b }, /* 2c04 GLAGOLITIC CAPITAL LETTER DOBRO */ { 0x0e7d, 0x0000, 0x0000, 0x0e7d }, /* 2c05 GLAGOLITIC CAPITAL LETTER YESTU */ { 0x0e7f, 0x0000, 0x0000, 0x0e7f }, /* 2c06 GLAGOLITIC CAPITAL LETTER ZHIVETE */ { 0x0e81, 0x0000, 0x0000, 0x0e81 }, /* 2c07 GLAGOLITIC CAPITAL LETTER DZELO */ { 0x0e83, 0x0000, 0x0000, 0x0e83 }, /* 2c08 GLAGOLITIC CAPITAL LETTER ZEMLJA */ { 0x0e85, 0x0000, 0x0000, 0x0e85 }, /* 2c09 GLAGOLITIC CAPITAL LETTER IZHE */ { 0x0e87, 0x0000, 0x0000, 0x0e87 }, /* 2c0a GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ { 0x0e89, 0x0000, 0x0000, 0x0e89 }, /* 2c0b GLAGOLITIC CAPITAL LETTER I */ { 0x0e8b, 0x0000, 0x0000, 0x0e8b }, /* 2c0c GLAGOLITIC CAPITAL LETTER DJERVI */ { 0x0e8d, 0x0000, 0x0000, 0x0e8d }, /* 2c0d GLAGOLITIC CAPITAL LETTER KAKO */ { 0x0e8f, 0x0000, 0x0000, 0x0e8f }, /* 2c0e GLAGOLITIC CAPITAL LETTER LJUDIJE */ { 0x0e91, 0x0000, 0x0000, 0x0e91 }, /* 2c0f GLAGOLITIC CAPITAL LETTER MYSLITE */ { 0x0e93, 0x0000, 0x0000, 0x0e93 }, /* 2c10 GLAGOLITIC CAPITAL LETTER NASHI */ { 0x0e95, 0x0000, 0x0000, 0x0e95 }, /* 2c11 GLAGOLITIC CAPITAL LETTER ONU */ { 0x0e97, 0x0000, 0x0000, 0x0e97 }, /* 2c12 GLAGOLITIC CAPITAL LETTER POKOJI */ { 0x0e99, 0x0000, 0x0000, 0x0e99 }, /* 2c13 GLAGOLITIC CAPITAL LETTER RITSI */ { 0x0e9b, 0x0000, 0x0000, 0x0e9b }, /* 2c14 GLAGOLITIC CAPITAL LETTER SLOVO */ { 0x0e9d, 0x0000, 0x0000, 0x0e9d }, /* 2c15 GLAGOLITIC CAPITAL LETTER TVRIDO */ { 0x0e9f, 0x0000, 0x0000, 0x0e9f }, /* 2c16 GLAGOLITIC CAPITAL LETTER UKU */ { 0x0ea1, 0x0000, 0x0000, 0x0ea1 }, /* 2c17 GLAGOLITIC CAPITAL LETTER FRITU */ { 0x0ea3, 0x0000, 0x0000, 0x0ea3 }, /* 2c18 GLAGOLITIC CAPITAL LETTER HERU */ { 0x0ea5, 0x0000, 0x0000, 0x0ea5 }, /* 2c19 GLAGOLITIC CAPITAL LETTER OTU */ { 0x0ea7, 0x0000, 0x0000, 0x0ea7 }, /* 2c1a GLAGOLITIC CAPITAL LETTER PE */ { 0x0ea9, 0x0000, 0x0000, 0x0ea9 }, /* 2c1b GLAGOLITIC CAPITAL LETTER SHTA */ { 0x0eab, 0x0000, 0x0000, 0x0eab }, /* 2c1c GLAGOLITIC CAPITAL LETTER TSI */ { 0x0ead, 0x0000, 0x0000, 0x0ead }, /* 2c1d GLAGOLITIC CAPITAL LETTER CHRIVI */ { 0x0eaf, 0x0000, 0x0000, 0x0eaf }, /* 2c1e GLAGOLITIC CAPITAL LETTER SHA */ { 0x0eb1, 0x0000, 0x0000, 0x0eb1 }, /* 2c1f GLAGOLITIC CAPITAL LETTER YERU */ { 0x0eb3, 0x0000, 0x0000, 0x0eb3 }, /* 2c20 GLAGOLITIC CAPITAL LETTER YERI */ { 0x0eb5, 0x0000, 0x0000, 0x0eb5 }, /* 2c21 GLAGOLITIC CAPITAL LETTER YATI */ { 0x0eb7, 0x0000, 0x0000, 0x0eb7 }, /* 2c22 GLAGOLITIC CAPITAL LETTER SPIDERY HA */ { 0x0eb9, 0x0000, 0x0000, 0x0eb9 }, /* 2c23 GLAGOLITIC CAPITAL LETTER YU */ { 0x0ebb, 0x0000, 0x0000, 0x0ebb }, /* 2c24 GLAGOLITIC CAPITAL LETTER SMALL YUS */ { 0x0ebd, 0x0000, 0x0000, 0x0ebd }, /* 2c25 GLAGOLITIC CAPITAL LETTER SMALL YUS WIT */ { 0x0ebf, 0x0000, 0x0000, 0x0ebf }, /* 2c26 GLAGOLITIC CAPITAL LETTER YO */ { 0x0ec1, 0x0000, 0x0000, 0x0ec1 }, /* 2c27 GLAGOLITIC CAPITAL LETTER IOTATED SMALL */ { 0x0ec3, 0x0000, 0x0000, 0x0ec3 }, /* 2c28 GLAGOLITIC CAPITAL LETTER BIG YUS */ { 0x0ec5, 0x0000, 0x0000, 0x0ec5 }, /* 2c29 GLAGOLITIC CAPITAL LETTER IOTATED BIG Y */ { 0x0ec7, 0x0000, 0x0000, 0x0ec7 }, /* 2c2a GLAGOLITIC CAPITAL LETTER FITA */ { 0x0ec9, 0x0000, 0x0000, 0x0ec9 }, /* 2c2b GLAGOLITIC CAPITAL LETTER IZHITSA */ { 0x0ecb, 0x0000, 0x0000, 0x0ecb }, /* 2c2c GLAGOLITIC CAPITAL LETTER SHTAPIC */ { 0x0ecd, 0x0000, 0x0000, 0x0ecd }, /* 2c2d GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ { 0x0ecf, 0x0000, 0x0000, 0x0ecf }, /* 2c2e GLAGOLITIC CAPITAL LETTER LATINATE MYSL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c2f (null) */ { 0x0000, 0x0ed1, 0x0ed1, 0x0000 }, /* 2c30 GLAGOLITIC SMALL LETTER AZU */ { 0x0000, 0x0ed3, 0x0ed3, 0x0000 }, /* 2c31 GLAGOLITIC SMALL LETTER BUKY */ { 0x0000, 0x0ed5, 0x0ed5, 0x0000 }, /* 2c32 GLAGOLITIC SMALL LETTER VEDE */ { 0x0000, 0x0ed7, 0x0ed7, 0x0000 }, /* 2c33 GLAGOLITIC SMALL LETTER GLAGOLI */ { 0x0000, 0x0ed9, 0x0ed9, 0x0000 }, /* 2c34 GLAGOLITIC SMALL LETTER DOBRO */ { 0x0000, 0x0edb, 0x0edb, 0x0000 }, /* 2c35 GLAGOLITIC SMALL LETTER YESTU */ { 0x0000, 0x0edd, 0x0edd, 0x0000 }, /* 2c36 GLAGOLITIC SMALL LETTER ZHIVETE */ { 0x0000, 0x0edf, 0x0edf, 0x0000 }, /* 2c37 GLAGOLITIC SMALL LETTER DZELO */ { 0x0000, 0x0ee1, 0x0ee1, 0x0000 }, /* 2c38 GLAGOLITIC SMALL LETTER ZEMLJA */ { 0x0000, 0x0ee3, 0x0ee3, 0x0000 }, /* 2c39 GLAGOLITIC SMALL LETTER IZHE */ { 0x0000, 0x0ee5, 0x0ee5, 0x0000 }, /* 2c3a GLAGOLITIC SMALL LETTER INITIAL IZHE */ { 0x0000, 0x0ee7, 0x0ee7, 0x0000 }, /* 2c3b GLAGOLITIC SMALL LETTER I */ { 0x0000, 0x0ee9, 0x0ee9, 0x0000 }, /* 2c3c GLAGOLITIC SMALL LETTER DJERVI */ { 0x0000, 0x0eeb, 0x0eeb, 0x0000 }, /* 2c3d GLAGOLITIC SMALL LETTER KAKO */ { 0x0000, 0x0eed, 0x0eed, 0x0000 }, /* 2c3e GLAGOLITIC SMALL LETTER LJUDIJE */ { 0x0000, 0x0eef, 0x0eef, 0x0000 }, /* 2c3f GLAGOLITIC SMALL LETTER MYSLITE */ { 0x0000, 0x0ef1, 0x0ef1, 0x0000 }, /* 2c40 GLAGOLITIC SMALL LETTER NASHI */ { 0x0000, 0x0ef3, 0x0ef3, 0x0000 }, /* 2c41 GLAGOLITIC SMALL LETTER ONU */ { 0x0000, 0x0ef5, 0x0ef5, 0x0000 }, /* 2c42 GLAGOLITIC SMALL LETTER POKOJI */ { 0x0000, 0x0ef7, 0x0ef7, 0x0000 }, /* 2c43 GLAGOLITIC SMALL LETTER RITSI */ { 0x0000, 0x0ef9, 0x0ef9, 0x0000 }, /* 2c44 GLAGOLITIC SMALL LETTER SLOVO */ { 0x0000, 0x0efb, 0x0efb, 0x0000 }, /* 2c45 GLAGOLITIC SMALL LETTER TVRIDO */ { 0x0000, 0x0efd, 0x0efd, 0x0000 }, /* 2c46 GLAGOLITIC SMALL LETTER UKU */ { 0x0000, 0x0eff, 0x0eff, 0x0000 }, /* 2c47 GLAGOLITIC SMALL LETTER FRITU */ { 0x0000, 0x0f01, 0x0f01, 0x0000 }, /* 2c48 GLAGOLITIC SMALL LETTER HERU */ { 0x0000, 0x0f03, 0x0f03, 0x0000 }, /* 2c49 GLAGOLITIC SMALL LETTER OTU */ { 0x0000, 0x0f05, 0x0f05, 0x0000 }, /* 2c4a GLAGOLITIC SMALL LETTER PE */ { 0x0000, 0x0f07, 0x0f07, 0x0000 }, /* 2c4b GLAGOLITIC SMALL LETTER SHTA */ { 0x0000, 0x0f09, 0x0f09, 0x0000 }, /* 2c4c GLAGOLITIC SMALL LETTER TSI */ { 0x0000, 0x0f0b, 0x0f0b, 0x0000 }, /* 2c4d GLAGOLITIC SMALL LETTER CHRIVI */ { 0x0000, 0x0f0d, 0x0f0d, 0x0000 }, /* 2c4e GLAGOLITIC SMALL LETTER SHA */ { 0x0000, 0x0f0f, 0x0f0f, 0x0000 }, /* 2c4f GLAGOLITIC SMALL LETTER YERU */ { 0x0000, 0x0f11, 0x0f11, 0x0000 }, /* 2c50 GLAGOLITIC SMALL LETTER YERI */ { 0x0000, 0x0f13, 0x0f13, 0x0000 }, /* 2c51 GLAGOLITIC SMALL LETTER YATI */ { 0x0000, 0x0f15, 0x0f15, 0x0000 }, /* 2c52 GLAGOLITIC SMALL LETTER SPIDERY HA */ { 0x0000, 0x0f17, 0x0f17, 0x0000 }, /* 2c53 GLAGOLITIC SMALL LETTER YU */ { 0x0000, 0x0f19, 0x0f19, 0x0000 }, /* 2c54 GLAGOLITIC SMALL LETTER SMALL YUS */ { 0x0000, 0x0f1b, 0x0f1b, 0x0000 }, /* 2c55 GLAGOLITIC SMALL LETTER SMALL YUS WITH */ { 0x0000, 0x0f1d, 0x0f1d, 0x0000 }, /* 2c56 GLAGOLITIC SMALL LETTER YO */ { 0x0000, 0x0f1f, 0x0f1f, 0x0000 }, /* 2c57 GLAGOLITIC SMALL LETTER IOTATED SMALL Y */ { 0x0000, 0x0f21, 0x0f21, 0x0000 }, /* 2c58 GLAGOLITIC SMALL LETTER BIG YUS */ { 0x0000, 0x0f23, 0x0f23, 0x0000 }, /* 2c59 GLAGOLITIC SMALL LETTER IOTATED BIG YUS */ { 0x0000, 0x0f25, 0x0f25, 0x0000 }, /* 2c5a GLAGOLITIC SMALL LETTER FITA */ { 0x0000, 0x0f27, 0x0f27, 0x0000 }, /* 2c5b GLAGOLITIC SMALL LETTER IZHITSA */ { 0x0000, 0x0f29, 0x0f29, 0x0000 }, /* 2c5c GLAGOLITIC SMALL LETTER SHTAPIC */ { 0x0000, 0x0f2b, 0x0f2b, 0x0000 }, /* 2c5d GLAGOLITIC SMALL LETTER TROKUTASTI A */ { 0x0000, 0x0f2d, 0x0f2d, 0x0000 }, /* 2c5e GLAGOLITIC SMALL LETTER LATINATE MYSLIT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c5f (null) */ { 0x0f2f, 0x0000, 0x0000, 0x0f2f }, /* 2c60 LATIN CAPITAL LETTER L WITH DOUBLE BAR */ { 0x0000, 0x0f31, 0x0f31, 0x0000 }, /* 2c61 LATIN SMALL LETTER L WITH DOUBLE BAR */ { 0x0f33, 0x0000, 0x0000, 0x0f33 }, /* 2c62 LATIN CAPITAL LETTER L WITH MIDDLE TILD */ { 0x0f35, 0x0000, 0x0000, 0x0f35 }, /* 2c63 LATIN CAPITAL LETTER P WITH STROKE */ { 0x0f37, 0x0000, 0x0000, 0x0f37 }, /* 2c64 LATIN CAPITAL LETTER R WITH TAIL */ { 0x0000, 0x0f39, 0x0f39, 0x0000 }, /* 2c65 LATIN SMALL LETTER A WITH STROKE */ { 0x0000, 0x0f3b, 0x0f3b, 0x0000 }, /* 2c66 LATIN SMALL LETTER T WITH DIAGONAL STRO */ { 0x0f3d, 0x0000, 0x0000, 0x0f3d }, /* 2c67 LATIN CAPITAL LETTER H WITH DESCENDER */ { 0x0000, 0x0f3f, 0x0f3f, 0x0000 }, /* 2c68 LATIN SMALL LETTER H WITH DESCENDER */ { 0x0f41, 0x0000, 0x0000, 0x0f41 }, /* 2c69 LATIN CAPITAL LETTER K WITH DESCENDER */ { 0x0000, 0x0f43, 0x0f43, 0x0000 }, /* 2c6a LATIN SMALL LETTER K WITH DESCENDER */ { 0x0f45, 0x0000, 0x0000, 0x0f45 }, /* 2c6b LATIN CAPITAL LETTER Z WITH DESCENDER */ { 0x0000, 0x0f47, 0x0f47, 0x0000 }, /* 2c6c LATIN SMALL LETTER Z WITH DESCENDER */ { 0x0f49, 0x0000, 0x0000, 0x0f49 }, /* 2c6d LATIN CAPITAL LETTER ALPHA */ { 0x0f4b, 0x0000, 0x0000, 0x0f4b }, /* 2c6e LATIN CAPITAL LETTER M WITH HOOK */ { 0x0f4d, 0x0000, 0x0000, 0x0f4d }, /* 2c6f LATIN CAPITAL LETTER TURNED A */ { 0x0f4f, 0x0000, 0x0000, 0x0f4f }, /* 2c70 LATIN CAPITAL LETTER TURNED ALPHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c71 LATIN SMALL LETTER V WITH RIGHT HOOK */ { 0x0f51, 0x0000, 0x0000, 0x0f51 }, /* 2c72 LATIN CAPITAL LETTER W WITH HOOK */ { 0x0000, 0x0f53, 0x0f53, 0x0000 }, /* 2c73 LATIN SMALL LETTER W WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c74 LATIN SMALL LETTER V WITH CURL */ { 0x0f55, 0x0000, 0x0000, 0x0f55 }, /* 2c75 LATIN CAPITAL LETTER HALF H */ { 0x0000, 0x0f57, 0x0f57, 0x0000 }, /* 2c76 LATIN SMALL LETTER HALF H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c77 LATIN SMALL LETTER TAILLESS PHI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c78 LATIN SMALL LETTER E WITH NOTCH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c79 LATIN SMALL LETTER TURNED R WITH TAIL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7a LATIN SMALL LETTER O WITH LOW RING INSI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7b LATIN LETTER SMALL CAPITAL TURNED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7c LATIN SUBSCRIPT SMALL LETTER J */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2c7d MODIFIER LETTER CAPITAL V */ { 0x0f59, 0x0000, 0x0000, 0x0f59 }, /* 2c7e LATIN CAPITAL LETTER S WITH SWASH TAIL */ { 0x0f5b, 0x0000, 0x0000, 0x0f5b } /* 2c7f LATIN CAPITAL LETTER Z WITH SWASH TAIL */ }; static case_table case_pg_059[128] = { { 0x0f5d, 0x0000, 0x0000, 0x0f5d }, /* 2c80 COPTIC CAPITAL LETTER ALFA */ { 0x0000, 0x0f5f, 0x0f5f, 0x0000 }, /* 2c81 COPTIC SMALL LETTER ALFA */ { 0x0f61, 0x0000, 0x0000, 0x0f61 }, /* 2c82 COPTIC CAPITAL LETTER VIDA */ { 0x0000, 0x0f63, 0x0f63, 0x0000 }, /* 2c83 COPTIC SMALL LETTER VIDA */ { 0x0f65, 0x0000, 0x0000, 0x0f65 }, /* 2c84 COPTIC CAPITAL LETTER GAMMA */ { 0x0000, 0x0f67, 0x0f67, 0x0000 }, /* 2c85 COPTIC SMALL LETTER GAMMA */ { 0x0f69, 0x0000, 0x0000, 0x0f69 }, /* 2c86 COPTIC CAPITAL LETTER DALDA */ { 0x0000, 0x0f6b, 0x0f6b, 0x0000 }, /* 2c87 COPTIC SMALL LETTER DALDA */ { 0x0f6d, 0x0000, 0x0000, 0x0f6d }, /* 2c88 COPTIC CAPITAL LETTER EIE */ { 0x0000, 0x0f6f, 0x0f6f, 0x0000 }, /* 2c89 COPTIC SMALL LETTER EIE */ { 0x0f71, 0x0000, 0x0000, 0x0f71 }, /* 2c8a COPTIC CAPITAL LETTER SOU */ { 0x0000, 0x0f73, 0x0f73, 0x0000 }, /* 2c8b COPTIC SMALL LETTER SOU */ { 0x0f75, 0x0000, 0x0000, 0x0f75 }, /* 2c8c COPTIC CAPITAL LETTER ZATA */ { 0x0000, 0x0f77, 0x0f77, 0x0000 }, /* 2c8d COPTIC SMALL LETTER ZATA */ { 0x0f79, 0x0000, 0x0000, 0x0f79 }, /* 2c8e COPTIC CAPITAL LETTER HATE */ { 0x0000, 0x0f7b, 0x0f7b, 0x0000 }, /* 2c8f COPTIC SMALL LETTER HATE */ { 0x0f7d, 0x0000, 0x0000, 0x0f7d }, /* 2c90 COPTIC CAPITAL LETTER THETHE */ { 0x0000, 0x0f7f, 0x0f7f, 0x0000 }, /* 2c91 COPTIC SMALL LETTER THETHE */ { 0x0f81, 0x0000, 0x0000, 0x0f81 }, /* 2c92 COPTIC CAPITAL LETTER IAUDA */ { 0x0000, 0x0f83, 0x0f83, 0x0000 }, /* 2c93 COPTIC SMALL LETTER IAUDA */ { 0x0f85, 0x0000, 0x0000, 0x0f85 }, /* 2c94 COPTIC CAPITAL LETTER KAPA */ { 0x0000, 0x0f87, 0x0f87, 0x0000 }, /* 2c95 COPTIC SMALL LETTER KAPA */ { 0x0f89, 0x0000, 0x0000, 0x0f89 }, /* 2c96 COPTIC CAPITAL LETTER LAULA */ { 0x0000, 0x0f8b, 0x0f8b, 0x0000 }, /* 2c97 COPTIC SMALL LETTER LAULA */ { 0x0f8d, 0x0000, 0x0000, 0x0f8d }, /* 2c98 COPTIC CAPITAL LETTER MI */ { 0x0000, 0x0f8f, 0x0f8f, 0x0000 }, /* 2c99 COPTIC SMALL LETTER MI */ { 0x0f91, 0x0000, 0x0000, 0x0f91 }, /* 2c9a COPTIC CAPITAL LETTER NI */ { 0x0000, 0x0f93, 0x0f93, 0x0000 }, /* 2c9b COPTIC SMALL LETTER NI */ { 0x0f95, 0x0000, 0x0000, 0x0f95 }, /* 2c9c COPTIC CAPITAL LETTER KSI */ { 0x0000, 0x0f97, 0x0f97, 0x0000 }, /* 2c9d COPTIC SMALL LETTER KSI */ { 0x0f99, 0x0000, 0x0000, 0x0f99 }, /* 2c9e COPTIC CAPITAL LETTER O */ { 0x0000, 0x0f9b, 0x0f9b, 0x0000 }, /* 2c9f COPTIC SMALL LETTER O */ { 0x0f9d, 0x0000, 0x0000, 0x0f9d }, /* 2ca0 COPTIC CAPITAL LETTER PI */ { 0x0000, 0x0f9f, 0x0f9f, 0x0000 }, /* 2ca1 COPTIC SMALL LETTER PI */ { 0x0fa1, 0x0000, 0x0000, 0x0fa1 }, /* 2ca2 COPTIC CAPITAL LETTER RO */ { 0x0000, 0x0fa3, 0x0fa3, 0x0000 }, /* 2ca3 COPTIC SMALL LETTER RO */ { 0x0fa5, 0x0000, 0x0000, 0x0fa5 }, /* 2ca4 COPTIC CAPITAL LETTER SIMA */ { 0x0000, 0x0fa7, 0x0fa7, 0x0000 }, /* 2ca5 COPTIC SMALL LETTER SIMA */ { 0x0fa9, 0x0000, 0x0000, 0x0fa9 }, /* 2ca6 COPTIC CAPITAL LETTER TAU */ { 0x0000, 0x0fab, 0x0fab, 0x0000 }, /* 2ca7 COPTIC SMALL LETTER TAU */ { 0x0fad, 0x0000, 0x0000, 0x0fad }, /* 2ca8 COPTIC CAPITAL LETTER UA */ { 0x0000, 0x0faf, 0x0faf, 0x0000 }, /* 2ca9 COPTIC SMALL LETTER UA */ { 0x0fb1, 0x0000, 0x0000, 0x0fb1 }, /* 2caa COPTIC CAPITAL LETTER FI */ { 0x0000, 0x0fb3, 0x0fb3, 0x0000 }, /* 2cab COPTIC SMALL LETTER FI */ { 0x0fb5, 0x0000, 0x0000, 0x0fb5 }, /* 2cac COPTIC CAPITAL LETTER KHI */ { 0x0000, 0x0fb7, 0x0fb7, 0x0000 }, /* 2cad COPTIC SMALL LETTER KHI */ { 0x0fb9, 0x0000, 0x0000, 0x0fb9 }, /* 2cae COPTIC CAPITAL LETTER PSI */ { 0x0000, 0x0fbb, 0x0fbb, 0x0000 }, /* 2caf COPTIC SMALL LETTER PSI */ { 0x0fbd, 0x0000, 0x0000, 0x0fbd }, /* 2cb0 COPTIC CAPITAL LETTER OOU */ { 0x0000, 0x0fbf, 0x0fbf, 0x0000 }, /* 2cb1 COPTIC SMALL LETTER OOU */ { 0x0fc1, 0x0000, 0x0000, 0x0fc1 }, /* 2cb2 COPTIC CAPITAL LETTER DIALECT-P ALEF */ { 0x0000, 0x0fc3, 0x0fc3, 0x0000 }, /* 2cb3 COPTIC SMALL LETTER DIALECT-P ALEF */ { 0x0fc5, 0x0000, 0x0000, 0x0fc5 }, /* 2cb4 COPTIC CAPITAL LETTER OLD COPTIC AIN */ { 0x0000, 0x0fc7, 0x0fc7, 0x0000 }, /* 2cb5 COPTIC SMALL LETTER OLD COPTIC AIN */ { 0x0fc9, 0x0000, 0x0000, 0x0fc9 }, /* 2cb6 COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ { 0x0000, 0x0fcb, 0x0fcb, 0x0000 }, /* 2cb7 COPTIC SMALL LETTER CRYPTOGRAMMIC EIE */ { 0x0fcd, 0x0000, 0x0000, 0x0fcd }, /* 2cb8 COPTIC CAPITAL LETTER DIALECT-P KAPA */ { 0x0000, 0x0fcf, 0x0fcf, 0x0000 }, /* 2cb9 COPTIC SMALL LETTER DIALECT-P KAPA */ { 0x0fd1, 0x0000, 0x0000, 0x0fd1 }, /* 2cba COPTIC CAPITAL LETTER DIALECT-P NI */ { 0x0000, 0x0fd3, 0x0fd3, 0x0000 }, /* 2cbb COPTIC SMALL LETTER DIALECT-P NI */ { 0x0fd5, 0x0000, 0x0000, 0x0fd5 }, /* 2cbc COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ { 0x0000, 0x0fd7, 0x0fd7, 0x0000 }, /* 2cbd COPTIC SMALL LETTER CRYPTOGRAMMIC NI */ { 0x0fd9, 0x0000, 0x0000, 0x0fd9 }, /* 2cbe COPTIC CAPITAL LETTER OLD COPTIC OOU */ { 0x0000, 0x0fdb, 0x0fdb, 0x0000 }, /* 2cbf COPTIC SMALL LETTER OLD COPTIC OOU */ { 0x0fdd, 0x0000, 0x0000, 0x0fdd }, /* 2cc0 COPTIC CAPITAL LETTER SAMPI */ { 0x0000, 0x0fdf, 0x0fdf, 0x0000 }, /* 2cc1 COPTIC SMALL LETTER SAMPI */ { 0x0fe1, 0x0000, 0x0000, 0x0fe1 }, /* 2cc2 COPTIC CAPITAL LETTER CROSSED SHEI */ { 0x0000, 0x0fe3, 0x0fe3, 0x0000 }, /* 2cc3 COPTIC SMALL LETTER CROSSED SHEI */ { 0x0fe5, 0x0000, 0x0000, 0x0fe5 }, /* 2cc4 COPTIC CAPITAL LETTER OLD COPTIC SHEI */ { 0x0000, 0x0fe7, 0x0fe7, 0x0000 }, /* 2cc5 COPTIC SMALL LETTER OLD COPTIC SHEI */ { 0x0fe9, 0x0000, 0x0000, 0x0fe9 }, /* 2cc6 COPTIC CAPITAL LETTER OLD COPTIC ESH */ { 0x0000, 0x0feb, 0x0feb, 0x0000 }, /* 2cc7 COPTIC SMALL LETTER OLD COPTIC ESH */ { 0x0fed, 0x0000, 0x0000, 0x0fed }, /* 2cc8 COPTIC CAPITAL LETTER AKHMIMIC KHEI */ { 0x0000, 0x0fef, 0x0fef, 0x0000 }, /* 2cc9 COPTIC SMALL LETTER AKHMIMIC KHEI */ { 0x0ff1, 0x0000, 0x0000, 0x0ff1 }, /* 2cca COPTIC CAPITAL LETTER DIALECT-P HORI */ { 0x0000, 0x0ff3, 0x0ff3, 0x0000 }, /* 2ccb COPTIC SMALL LETTER DIALECT-P HORI */ { 0x0ff5, 0x0000, 0x0000, 0x0ff5 }, /* 2ccc COPTIC CAPITAL LETTER OLD COPTIC HORI */ { 0x0000, 0x0ff7, 0x0ff7, 0x0000 }, /* 2ccd COPTIC SMALL LETTER OLD COPTIC HORI */ { 0x0ff9, 0x0000, 0x0000, 0x0ff9 }, /* 2cce COPTIC CAPITAL LETTER OLD COPTIC HA */ { 0x0000, 0x0ffb, 0x0ffb, 0x0000 }, /* 2ccf COPTIC SMALL LETTER OLD COPTIC HA */ { 0x0ffd, 0x0000, 0x0000, 0x0ffd }, /* 2cd0 COPTIC CAPITAL LETTER L-SHAPED HA */ { 0x0000, 0x0fff, 0x0fff, 0x0000 }, /* 2cd1 COPTIC SMALL LETTER L-SHAPED HA */ { 0x1001, 0x0000, 0x0000, 0x1001 }, /* 2cd2 COPTIC CAPITAL LETTER OLD COPTIC HEI */ { 0x0000, 0x1003, 0x1003, 0x0000 }, /* 2cd3 COPTIC SMALL LETTER OLD COPTIC HEI */ { 0x1005, 0x0000, 0x0000, 0x1005 }, /* 2cd4 COPTIC CAPITAL LETTER OLD COPTIC HAT */ { 0x0000, 0x1007, 0x1007, 0x0000 }, /* 2cd5 COPTIC SMALL LETTER OLD COPTIC HAT */ { 0x1009, 0x0000, 0x0000, 0x1009 }, /* 2cd6 COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ { 0x0000, 0x100b, 0x100b, 0x0000 }, /* 2cd7 COPTIC SMALL LETTER OLD COPTIC GANGIA */ { 0x100d, 0x0000, 0x0000, 0x100d }, /* 2cd8 COPTIC CAPITAL LETTER OLD COPTIC DJA */ { 0x0000, 0x100f, 0x100f, 0x0000 }, /* 2cd9 COPTIC SMALL LETTER OLD COPTIC DJA */ { 0x1011, 0x0000, 0x0000, 0x1011 }, /* 2cda COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ { 0x0000, 0x1013, 0x1013, 0x0000 }, /* 2cdb COPTIC SMALL LETTER OLD COPTIC SHIMA */ { 0x1015, 0x0000, 0x0000, 0x1015 }, /* 2cdc COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ { 0x0000, 0x1017, 0x1017, 0x0000 }, /* 2cdd COPTIC SMALL LETTER OLD NUBIAN SHIMA */ { 0x1019, 0x0000, 0x0000, 0x1019 }, /* 2cde COPTIC CAPITAL LETTER OLD NUBIAN NGI */ { 0x0000, 0x101b, 0x101b, 0x0000 }, /* 2cdf COPTIC SMALL LETTER OLD NUBIAN NGI */ { 0x101d, 0x0000, 0x0000, 0x101d }, /* 2ce0 COPTIC CAPITAL LETTER OLD NUBIAN NYI */ { 0x0000, 0x101f, 0x101f, 0x0000 }, /* 2ce1 COPTIC SMALL LETTER OLD NUBIAN NYI */ { 0x1021, 0x0000, 0x0000, 0x1021 }, /* 2ce2 COPTIC CAPITAL LETTER OLD NUBIAN WAU */ { 0x0000, 0x1023, 0x1023, 0x0000 }, /* 2ce3 COPTIC SMALL LETTER OLD NUBIAN WAU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce4 COPTIC SYMBOL KAI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce5 COPTIC SYMBOL MI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce6 COPTIC SYMBOL PI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce7 COPTIC SYMBOL STAUROS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce8 COPTIC SYMBOL TAU RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2ce9 COPTIC SYMBOL KHI RO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cea COPTIC SYMBOL SHIMA SIMA */ { 0x1025, 0x0000, 0x0000, 0x1025 }, /* 2ceb COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHE */ { 0x0000, 0x1027, 0x1027, 0x0000 }, /* 2cec COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ { 0x1029, 0x0000, 0x0000, 0x1029 }, /* 2ced COPTIC CAPITAL LETTER CRYPTOGRAMMIC GAN */ { 0x0000, 0x102b, 0x102b, 0x0000 }, /* 2cee COPTIC SMALL LETTER CRYPTOGRAMMIC GANGI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cef COPTIC COMBINING NI ABOVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf0 COPTIC COMBINING SPIRITUS ASPER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf1 COPTIC COMBINING SPIRITUS LENIS */ { 0x102d, 0x0000, 0x0000, 0x102d }, /* 2cf2 COPTIC CAPITAL LETTER BOHAIRIC KHEI */ { 0x0000, 0x102f, 0x102f, 0x0000 }, /* 2cf3 COPTIC SMALL LETTER BOHAIRIC KHEI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cf9 COPTIC OLD NUBIAN FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfa COPTIC OLD NUBIAN DIRECT QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfb COPTIC OLD NUBIAN INDIRECT QUESTION MAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfc COPTIC OLD NUBIAN VERSE DIVIDER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfd COPTIC FRACTION ONE HALF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2cfe COPTIC FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 2cff COPTIC MORPHOLOGICAL DIVIDER */ }; static case_table case_pg_05a[128] = { { 0x0000, 0x1031, 0x1031, 0x0000 }, /* 2d00 GEORGIAN SMALL LETTER AN */ { 0x0000, 0x1033, 0x1033, 0x0000 }, /* 2d01 GEORGIAN SMALL LETTER BAN */ { 0x0000, 0x1035, 0x1035, 0x0000 }, /* 2d02 GEORGIAN SMALL LETTER GAN */ { 0x0000, 0x1037, 0x1037, 0x0000 }, /* 2d03 GEORGIAN SMALL LETTER DON */ { 0x0000, 0x1039, 0x1039, 0x0000 }, /* 2d04 GEORGIAN SMALL LETTER EN */ { 0x0000, 0x103b, 0x103b, 0x0000 }, /* 2d05 GEORGIAN SMALL LETTER VIN */ { 0x0000, 0x103d, 0x103d, 0x0000 }, /* 2d06 GEORGIAN SMALL LETTER ZEN */ { 0x0000, 0x103f, 0x103f, 0x0000 }, /* 2d07 GEORGIAN SMALL LETTER TAN */ { 0x0000, 0x1041, 0x1041, 0x0000 }, /* 2d08 GEORGIAN SMALL LETTER IN */ { 0x0000, 0x1043, 0x1043, 0x0000 }, /* 2d09 GEORGIAN SMALL LETTER KAN */ { 0x0000, 0x1045, 0x1045, 0x0000 }, /* 2d0a GEORGIAN SMALL LETTER LAS */ { 0x0000, 0x1047, 0x1047, 0x0000 }, /* 2d0b GEORGIAN SMALL LETTER MAN */ { 0x0000, 0x1049, 0x1049, 0x0000 }, /* 2d0c GEORGIAN SMALL LETTER NAR */ { 0x0000, 0x104b, 0x104b, 0x0000 }, /* 2d0d GEORGIAN SMALL LETTER ON */ { 0x0000, 0x104d, 0x104d, 0x0000 }, /* 2d0e GEORGIAN SMALL LETTER PAR */ { 0x0000, 0x104f, 0x104f, 0x0000 }, /* 2d0f GEORGIAN SMALL LETTER ZHAR */ { 0x0000, 0x1051, 0x1051, 0x0000 }, /* 2d10 GEORGIAN SMALL LETTER RAE */ { 0x0000, 0x1053, 0x1053, 0x0000 }, /* 2d11 GEORGIAN SMALL LETTER SAN */ { 0x0000, 0x1055, 0x1055, 0x0000 }, /* 2d12 GEORGIAN SMALL LETTER TAR */ { 0x0000, 0x1057, 0x1057, 0x0000 }, /* 2d13 GEORGIAN SMALL LETTER UN */ { 0x0000, 0x1059, 0x1059, 0x0000 }, /* 2d14 GEORGIAN SMALL LETTER PHAR */ { 0x0000, 0x105b, 0x105b, 0x0000 }, /* 2d15 GEORGIAN SMALL LETTER KHAR */ { 0x0000, 0x105d, 0x105d, 0x0000 }, /* 2d16 GEORGIAN SMALL LETTER GHAN */ { 0x0000, 0x105f, 0x105f, 0x0000 }, /* 2d17 GEORGIAN SMALL LETTER QAR */ { 0x0000, 0x1061, 0x1061, 0x0000 }, /* 2d18 GEORGIAN SMALL LETTER SHIN */ { 0x0000, 0x1063, 0x1063, 0x0000 }, /* 2d19 GEORGIAN SMALL LETTER CHIN */ { 0x0000, 0x1065, 0x1065, 0x0000 }, /* 2d1a GEORGIAN SMALL LETTER CAN */ { 0x0000, 0x1067, 0x1067, 0x0000 }, /* 2d1b GEORGIAN SMALL LETTER JIL */ { 0x0000, 0x1069, 0x1069, 0x0000 }, /* 2d1c GEORGIAN SMALL LETTER CIL */ { 0x0000, 0x106b, 0x106b, 0x0000 }, /* 2d1d GEORGIAN SMALL LETTER CHAR */ { 0x0000, 0x106d, 0x106d, 0x0000 }, /* 2d1e GEORGIAN SMALL LETTER XAN */ { 0x0000, 0x106f, 0x106f, 0x0000 }, /* 2d1f GEORGIAN SMALL LETTER JHAN */ { 0x0000, 0x1071, 0x1071, 0x0000 }, /* 2d20 GEORGIAN SMALL LETTER HAE */ { 0x0000, 0x1073, 0x1073, 0x0000 }, /* 2d21 GEORGIAN SMALL LETTER HE */ { 0x0000, 0x1075, 0x1075, 0x0000 }, /* 2d22 GEORGIAN SMALL LETTER HIE */ { 0x0000, 0x1077, 0x1077, 0x0000 }, /* 2d23 GEORGIAN SMALL LETTER WE */ { 0x0000, 0x1079, 0x1079, 0x0000 }, /* 2d24 GEORGIAN SMALL LETTER HAR */ { 0x0000, 0x107b, 0x107b, 0x0000 }, /* 2d25 GEORGIAN SMALL LETTER HOE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d26 (null) */ { 0x0000, 0x107d, 0x107d, 0x0000 }, /* 2d27 GEORGIAN SMALL LETTER YN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d28 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d29 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2c (null) */ { 0x0000, 0x107f, 0x107f, 0x0000 }, /* 2d2d GEORGIAN SMALL LETTER AEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d2f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d30 TIFINAGH LETTER YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d31 TIFINAGH LETTER YAB */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d32 TIFINAGH LETTER YABH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d33 TIFINAGH LETTER YAG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d34 TIFINAGH LETTER YAGHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d35 TIFINAGH LETTER BERBER ACADEMY YAJ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d36 TIFINAGH LETTER YAJ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d37 TIFINAGH LETTER YAD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d38 TIFINAGH LETTER YADH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d39 TIFINAGH LETTER YADD */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3a TIFINAGH LETTER YADDH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3b TIFINAGH LETTER YEY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3c TIFINAGH LETTER YAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3d TIFINAGH LETTER YAK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3e TIFINAGH LETTER TUAREG YAK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d3f TIFINAGH LETTER YAKHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d40 TIFINAGH LETTER YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d41 TIFINAGH LETTER BERBER ACADEMY YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d42 TIFINAGH LETTER TUAREG YAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d43 TIFINAGH LETTER YAHH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d44 TIFINAGH LETTER YAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d45 TIFINAGH LETTER YAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d46 TIFINAGH LETTER TUAREG YAKH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d47 TIFINAGH LETTER YAQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d48 TIFINAGH LETTER TUAREG YAQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d49 TIFINAGH LETTER YI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4a TIFINAGH LETTER YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4b TIFINAGH LETTER AHAGGAR YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4c TIFINAGH LETTER TUAREG YAZH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4d TIFINAGH LETTER YAL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4e TIFINAGH LETTER YAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d4f TIFINAGH LETTER YAN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d50 TIFINAGH LETTER TUAREG YAGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d51 TIFINAGH LETTER TUAREG YANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d52 TIFINAGH LETTER YAP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d53 TIFINAGH LETTER YU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d54 TIFINAGH LETTER YAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d55 TIFINAGH LETTER YARR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d56 TIFINAGH LETTER YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d57 TIFINAGH LETTER TUAREG YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d58 TIFINAGH LETTER AYER YAGH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d59 TIFINAGH LETTER YAS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5a TIFINAGH LETTER YASS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5b TIFINAGH LETTER YASH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5c TIFINAGH LETTER YAT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5d TIFINAGH LETTER YATH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5e TIFINAGH LETTER YACH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d5f TIFINAGH LETTER YATT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d60 TIFINAGH LETTER YAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d61 TIFINAGH LETTER YAW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d62 TIFINAGH LETTER YAY */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d63 TIFINAGH LETTER YAZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d64 TIFINAGH LETTER TAWELLEMET YAZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d65 TIFINAGH LETTER YAZZ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d66 TIFINAGH LETTER YE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d67 TIFINAGH LETTER YO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d68 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d69 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d6f TIFINAGH MODIFIER LETTER LABIALIZATION */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d70 TIFINAGH SEPARATOR MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d71 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d72 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d73 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d74 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d75 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d76 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d77 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d78 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d79 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* 2d7e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* 2d7f TIFINAGH CONSONANT JOINER */ }; static case_table case_pg_14c[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a600 VAI SYLLABLE JE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a601 VAI SYLLABLE NJE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a602 VAI SYLLABLE YE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a603 VAI SYLLABLE KE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a604 VAI SYLLABLE NGGE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a605 VAI SYLLABLE NGGEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a606 VAI SYLLABLE GE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a607 VAI SYLLABLE GEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a608 VAI SYLLABLE ME */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a609 VAI SYLLABLE NE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60a VAI SYLLABLE NYE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60b VAI SYLLABLE NG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60c VAI SYLLABLE LENGTHENER */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60d VAI COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60e VAI FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a60f VAI QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a610 VAI SYLLABLE NDOLE FA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a611 VAI SYLLABLE NDOLE KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a612 VAI SYLLABLE NDOLE SOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a613 VAI SYMBOL FEENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a614 VAI SYMBOL KEENG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a615 VAI SYMBOL TING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a616 VAI SYMBOL NII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a617 VAI SYMBOL BANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a618 VAI SYMBOL FAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a619 VAI SYMBOL TAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61a VAI SYMBOL DANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61b VAI SYMBOL DOONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61c VAI SYMBOL KUNG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61d VAI SYMBOL TONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61e VAI SYMBOL DO-O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a61f VAI SYMBOL JONG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a620 VAI DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a621 VAI DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a622 VAI DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a623 VAI DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a624 VAI DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a625 VAI DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a626 VAI DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a627 VAI DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a628 VAI DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a629 VAI DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62a VAI SYLLABLE NDOLE MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62b VAI SYLLABLE NDOLE DO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a62f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a630 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a631 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a632 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a633 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a634 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a635 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a636 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a637 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a638 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a639 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a63f (null) */ { 0x1081, 0x0000, 0x0000, 0x1081 }, /* a640 CYRILLIC CAPITAL LETTER ZEMLYA */ { 0x0000, 0x1083, 0x1083, 0x0000 }, /* a641 CYRILLIC SMALL LETTER ZEMLYA */ { 0x1085, 0x0000, 0x0000, 0x1085 }, /* a642 CYRILLIC CAPITAL LETTER DZELO */ { 0x0000, 0x1087, 0x1087, 0x0000 }, /* a643 CYRILLIC SMALL LETTER DZELO */ { 0x1089, 0x0000, 0x0000, 0x1089 }, /* a644 CYRILLIC CAPITAL LETTER REVERSED DZE */ { 0x0000, 0x108b, 0x108b, 0x0000 }, /* a645 CYRILLIC SMALL LETTER REVERSED DZE */ { 0x108d, 0x0000, 0x0000, 0x108d }, /* a646 CYRILLIC CAPITAL LETTER IOTA */ { 0x0000, 0x108f, 0x108f, 0x0000 }, /* a647 CYRILLIC SMALL LETTER IOTA */ { 0x1091, 0x0000, 0x0000, 0x1091 }, /* a648 CYRILLIC CAPITAL LETTER DJERV */ { 0x0000, 0x1093, 0x1093, 0x0000 }, /* a649 CYRILLIC SMALL LETTER DJERV */ { 0x1095, 0x0000, 0x0000, 0x1095 }, /* a64a CYRILLIC CAPITAL LETTER MONOGRAPH UK */ { 0x0000, 0x1097, 0x1097, 0x0000 }, /* a64b CYRILLIC SMALL LETTER MONOGRAPH UK */ { 0x1099, 0x0000, 0x0000, 0x1099 }, /* a64c CYRILLIC CAPITAL LETTER BROAD OMEGA */ { 0x0000, 0x109b, 0x109b, 0x0000 }, /* a64d CYRILLIC SMALL LETTER BROAD OMEGA */ { 0x109d, 0x0000, 0x0000, 0x109d }, /* a64e CYRILLIC CAPITAL LETTER NEUTRAL YER */ { 0x0000, 0x109f, 0x109f, 0x0000 }, /* a64f CYRILLIC SMALL LETTER NEUTRAL YER */ { 0x10a1, 0x0000, 0x0000, 0x10a1 }, /* a650 CYRILLIC CAPITAL LETTER YERU WITH BACK */ { 0x0000, 0x10a3, 0x10a3, 0x0000 }, /* a651 CYRILLIC SMALL LETTER YERU WITH BACK YE */ { 0x10a5, 0x0000, 0x0000, 0x10a5 }, /* a652 CYRILLIC CAPITAL LETTER IOTIFIED YAT */ { 0x0000, 0x10a7, 0x10a7, 0x0000 }, /* a653 CYRILLIC SMALL LETTER IOTIFIED YAT */ { 0x10a9, 0x0000, 0x0000, 0x10a9 }, /* a654 CYRILLIC CAPITAL LETTER REVERSED YU */ { 0x0000, 0x10ab, 0x10ab, 0x0000 }, /* a655 CYRILLIC SMALL LETTER REVERSED YU */ { 0x10ad, 0x0000, 0x0000, 0x10ad }, /* a656 CYRILLIC CAPITAL LETTER IOTIFIED A */ { 0x0000, 0x10af, 0x10af, 0x0000 }, /* a657 CYRILLIC SMALL LETTER IOTIFIED A */ { 0x10b1, 0x0000, 0x0000, 0x10b1 }, /* a658 CYRILLIC CAPITAL LETTER CLOSED LITTLE Y */ { 0x0000, 0x10b3, 0x10b3, 0x0000 }, /* a659 CYRILLIC SMALL LETTER CLOSED LITTLE YUS */ { 0x10b5, 0x0000, 0x0000, 0x10b5 }, /* a65a CYRILLIC CAPITAL LETTER BLENDED YUS */ { 0x0000, 0x10b7, 0x10b7, 0x0000 }, /* a65b CYRILLIC SMALL LETTER BLENDED YUS */ { 0x10b9, 0x0000, 0x0000, 0x10b9 }, /* a65c CYRILLIC CAPITAL LETTER IOTIFIED CLOSED */ { 0x0000, 0x10bb, 0x10bb, 0x0000 }, /* a65d CYRILLIC SMALL LETTER IOTIFIED CLOSED L */ { 0x10bd, 0x0000, 0x0000, 0x10bd }, /* a65e CYRILLIC CAPITAL LETTER YN */ { 0x0000, 0x10bf, 0x10bf, 0x0000 }, /* a65f CYRILLIC SMALL LETTER YN */ { 0x10c1, 0x0000, 0x0000, 0x10c1 }, /* a660 CYRILLIC CAPITAL LETTER REVERSED TSE */ { 0x0000, 0x10c3, 0x10c3, 0x0000 }, /* a661 CYRILLIC SMALL LETTER REVERSED TSE */ { 0x10c5, 0x0000, 0x0000, 0x10c5 }, /* a662 CYRILLIC CAPITAL LETTER SOFT DE */ { 0x0000, 0x10c7, 0x10c7, 0x0000 }, /* a663 CYRILLIC SMALL LETTER SOFT DE */ { 0x10c9, 0x0000, 0x0000, 0x10c9 }, /* a664 CYRILLIC CAPITAL LETTER SOFT EL */ { 0x0000, 0x10cb, 0x10cb, 0x0000 }, /* a665 CYRILLIC SMALL LETTER SOFT EL */ { 0x10cd, 0x0000, 0x0000, 0x10cd }, /* a666 CYRILLIC CAPITAL LETTER SOFT EM */ { 0x0000, 0x10cf, 0x10cf, 0x0000 }, /* a667 CYRILLIC SMALL LETTER SOFT EM */ { 0x10d1, 0x0000, 0x0000, 0x10d1 }, /* a668 CYRILLIC CAPITAL LETTER MONOCULAR O */ { 0x0000, 0x10d3, 0x10d3, 0x0000 }, /* a669 CYRILLIC SMALL LETTER MONOCULAR O */ { 0x10d5, 0x0000, 0x0000, 0x10d5 }, /* a66a CYRILLIC CAPITAL LETTER BINOCULAR O */ { 0x0000, 0x10d7, 0x10d7, 0x0000 }, /* a66b CYRILLIC SMALL LETTER BINOCULAR O */ { 0x10d9, 0x0000, 0x0000, 0x10d9 }, /* a66c CYRILLIC CAPITAL LETTER DOUBLE MONOCULA */ { 0x0000, 0x10db, 0x10db, 0x0000 }, /* a66d CYRILLIC SMALL LETTER DOUBLE MONOCULAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a66e CYRILLIC LETTER MULTIOCULAR O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a66f COMBINING CYRILLIC VZMET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a670 COMBINING CYRILLIC TEN MILLIONS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a671 COMBINING CYRILLIC HUNDRED MILLIONS SIG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a672 COMBINING CYRILLIC THOUSAND MILLIONS SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a673 SLAVONIC ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a674 COMBINING CYRILLIC LETTER UKRAINIAN IE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a675 COMBINING CYRILLIC LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a676 COMBINING CYRILLIC LETTER YI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a677 COMBINING CYRILLIC LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a678 COMBINING CYRILLIC LETTER HARD SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a679 COMBINING CYRILLIC LETTER YERU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67a COMBINING CYRILLIC LETTER SOFT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67b COMBINING CYRILLIC LETTER OMEGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67c COMBINING CYRILLIC KAVYKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67d COMBINING CYRILLIC PAYEROK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a67e CYRILLIC KAVYKA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a67f CYRILLIC PAYEROK */ }; static case_table case_pg_14d[128] = { { 0x10dd, 0x0000, 0x0000, 0x10dd }, /* a680 CYRILLIC CAPITAL LETTER DWE */ { 0x0000, 0x10df, 0x10df, 0x0000 }, /* a681 CYRILLIC SMALL LETTER DWE */ { 0x10e1, 0x0000, 0x0000, 0x10e1 }, /* a682 CYRILLIC CAPITAL LETTER DZWE */ { 0x0000, 0x10e3, 0x10e3, 0x0000 }, /* a683 CYRILLIC SMALL LETTER DZWE */ { 0x10e5, 0x0000, 0x0000, 0x10e5 }, /* a684 CYRILLIC CAPITAL LETTER ZHWE */ { 0x0000, 0x10e7, 0x10e7, 0x0000 }, /* a685 CYRILLIC SMALL LETTER ZHWE */ { 0x10e9, 0x0000, 0x0000, 0x10e9 }, /* a686 CYRILLIC CAPITAL LETTER CCHE */ { 0x0000, 0x10eb, 0x10eb, 0x0000 }, /* a687 CYRILLIC SMALL LETTER CCHE */ { 0x10ed, 0x0000, 0x0000, 0x10ed }, /* a688 CYRILLIC CAPITAL LETTER DZZE */ { 0x0000, 0x10ef, 0x10ef, 0x0000 }, /* a689 CYRILLIC SMALL LETTER DZZE */ { 0x10f1, 0x0000, 0x0000, 0x10f1 }, /* a68a CYRILLIC CAPITAL LETTER TE WITH MIDDLE */ { 0x0000, 0x10f3, 0x10f3, 0x0000 }, /* a68b CYRILLIC SMALL LETTER TE WITH MIDDLE HO */ { 0x10f5, 0x0000, 0x0000, 0x10f5 }, /* a68c CYRILLIC CAPITAL LETTER TWE */ { 0x0000, 0x10f7, 0x10f7, 0x0000 }, /* a68d CYRILLIC SMALL LETTER TWE */ { 0x10f9, 0x0000, 0x0000, 0x10f9 }, /* a68e CYRILLIC CAPITAL LETTER TSWE */ { 0x0000, 0x10fb, 0x10fb, 0x0000 }, /* a68f CYRILLIC SMALL LETTER TSWE */ { 0x10fd, 0x0000, 0x0000, 0x10fd }, /* a690 CYRILLIC CAPITAL LETTER TSSE */ { 0x0000, 0x10ff, 0x10ff, 0x0000 }, /* a691 CYRILLIC SMALL LETTER TSSE */ { 0x1101, 0x0000, 0x0000, 0x1101 }, /* a692 CYRILLIC CAPITAL LETTER TCHE */ { 0x0000, 0x1103, 0x1103, 0x0000 }, /* a693 CYRILLIC SMALL LETTER TCHE */ { 0x1105, 0x0000, 0x0000, 0x1105 }, /* a694 CYRILLIC CAPITAL LETTER HWE */ { 0x0000, 0x1107, 0x1107, 0x0000 }, /* a695 CYRILLIC SMALL LETTER HWE */ { 0x1109, 0x0000, 0x0000, 0x1109 }, /* a696 CYRILLIC CAPITAL LETTER SHWE */ { 0x0000, 0x110b, 0x110b, 0x0000 }, /* a697 CYRILLIC SMALL LETTER SHWE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a698 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a699 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a69f COMBINING CYRILLIC LETTER IOTIFIED E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a0 BAMUM LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a1 BAMUM LETTER KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a2 BAMUM LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a3 BAMUM LETTER KU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a4 BAMUM LETTER EE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a5 BAMUM LETTER REE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a6 BAMUM LETTER TAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a7 BAMUM LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a8 BAMUM LETTER NYI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6a9 BAMUM LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6aa BAMUM LETTER LA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ab BAMUM LETTER PA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ac BAMUM LETTER RII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ad BAMUM LETTER RIEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ae BAMUM LETTER LEEEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6af BAMUM LETTER MEEEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b0 BAMUM LETTER TAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b1 BAMUM LETTER NDAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b2 BAMUM LETTER NJAEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b3 BAMUM LETTER M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b4 BAMUM LETTER SUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b5 BAMUM LETTER MU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b6 BAMUM LETTER SHII */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b7 BAMUM LETTER SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b8 BAMUM LETTER SHEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6b9 BAMUM LETTER SEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ba BAMUM LETTER KYEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bb BAMUM LETTER KET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bc BAMUM LETTER NUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bd BAMUM LETTER NU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6be BAMUM LETTER NJUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6bf BAMUM LETTER YOQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c0 BAMUM LETTER SHU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c1 BAMUM LETTER YUQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c2 BAMUM LETTER YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c3 BAMUM LETTER NSHA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c4 BAMUM LETTER KEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c5 BAMUM LETTER PEUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c6 BAMUM LETTER NJEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c7 BAMUM LETTER NTEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c8 BAMUM LETTER PUE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6c9 BAMUM LETTER WUE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ca BAMUM LETTER PEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cb BAMUM LETTER FEE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cc BAMUM LETTER RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cd BAMUM LETTER LU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ce BAMUM LETTER MI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6cf BAMUM LETTER NI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d0 BAMUM LETTER REUX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d1 BAMUM LETTER RAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d2 BAMUM LETTER KEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d3 BAMUM LETTER NGKWAEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d4 BAMUM LETTER NGGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d5 BAMUM LETTER NGA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d6 BAMUM LETTER SHO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d7 BAMUM LETTER PUAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d8 BAMUM LETTER FU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6d9 BAMUM LETTER FOM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6da BAMUM LETTER WA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6db BAMUM LETTER NA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6dc BAMUM LETTER LI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6dd BAMUM LETTER PI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6de BAMUM LETTER LOQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6df BAMUM LETTER KO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e0 BAMUM LETTER MBEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e1 BAMUM LETTER REN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e2 BAMUM LETTER MEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e3 BAMUM LETTER MA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e4 BAMUM LETTER TI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e5 BAMUM LETTER KI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e6 BAMUM LETTER MO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e7 BAMUM LETTER MBAA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e8 BAMUM LETTER TET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6e9 BAMUM LETTER KPA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ea BAMUM LETTER TEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6eb BAMUM LETTER NTUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ec BAMUM LETTER SAMBA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ed BAMUM LETTER FAAMAE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ee BAMUM LETTER KOVUU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6ef BAMUM LETTER KOGHOM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f0 BAMUM COMBINING MARK KOQNDON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f1 BAMUM COMBINING MARK TUKWENTIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f2 BAMUM NJAEMLI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f3 BAMUM FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f4 BAMUM COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f5 BAMUM COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f6 BAMUM SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f7 BAMUM QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6f9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fa (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a6fe (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a6ff (null) */ }; static case_table case_pg_14e[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a700 MODIFIER LETTER CHINESE TONE YIN PING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a701 MODIFIER LETTER CHINESE TONE YANG PING */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a702 MODIFIER LETTER CHINESE TONE YIN SHANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a703 MODIFIER LETTER CHINESE TONE YANG SHANG */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a704 MODIFIER LETTER CHINESE TONE YIN QU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a705 MODIFIER LETTER CHINESE TONE YANG QU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a706 MODIFIER LETTER CHINESE TONE YIN RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a707 MODIFIER LETTER CHINESE TONE YANG RU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a708 MODIFIER LETTER EXTRA-HIGH DOTTED TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a709 MODIFIER LETTER HIGH DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70a MODIFIER LETTER MID DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70b MODIFIER LETTER LOW DOTTED TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70c MODIFIER LETTER EXTRA-LOW DOTTED TONE B */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70d MODIFIER LETTER EXTRA-HIGH DOTTED LEFT- */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70e MODIFIER LETTER HIGH DOTTED LEFT-STEM T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a70f MODIFIER LETTER MID DOTTED LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a710 MODIFIER LETTER LOW DOTTED LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a711 MODIFIER LETTER EXTRA-LOW DOTTED LEFT-S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a712 MODIFIER LETTER EXTRA-HIGH LEFT-STEM TO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a713 MODIFIER LETTER HIGH LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a714 MODIFIER LETTER MID LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a715 MODIFIER LETTER LOW LEFT-STEM TONE BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a716 MODIFIER LETTER EXTRA-LOW LEFT-STEM TON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a717 MODIFIER LETTER DOT VERTICAL BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a718 MODIFIER LETTER DOT SLASH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a719 MODIFIER LETTER DOT HORIZONTAL BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71a MODIFIER LETTER LOWER RIGHT CORNER ANGL */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71b MODIFIER LETTER RAISED UP ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71c MODIFIER LETTER RAISED DOWN ARROW */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71d MODIFIER LETTER RAISED EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71e MODIFIER LETTER RAISED INVERTED EXCLAMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a71f MODIFIER LETTER LOW INVERTED EXCLAMATIO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a720 MODIFIER LETTER STRESS AND HIGH TONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a721 MODIFIER LETTER STRESS AND LOW TONE */ { 0x110d, 0x0000, 0x0000, 0x110d }, /* a722 LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ { 0x0000, 0x110f, 0x110f, 0x0000 }, /* a723 LATIN SMALL LETTER EGYPTOLOGICAL ALEF */ { 0x1111, 0x0000, 0x0000, 0x1111 }, /* a724 LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ { 0x0000, 0x1113, 0x1113, 0x0000 }, /* a725 LATIN SMALL LETTER EGYPTOLOGICAL AIN */ { 0x1115, 0x0000, 0x0000, 0x1115 }, /* a726 LATIN CAPITAL LETTER HENG */ { 0x0000, 0x1117, 0x1117, 0x0000 }, /* a727 LATIN SMALL LETTER HENG */ { 0x1119, 0x0000, 0x0000, 0x1119 }, /* a728 LATIN CAPITAL LETTER TZ */ { 0x0000, 0x111b, 0x111b, 0x0000 }, /* a729 LATIN SMALL LETTER TZ */ { 0x111d, 0x0000, 0x0000, 0x111d }, /* a72a LATIN CAPITAL LETTER TRESILLO */ { 0x0000, 0x111f, 0x111f, 0x0000 }, /* a72b LATIN SMALL LETTER TRESILLO */ { 0x1121, 0x0000, 0x0000, 0x1121 }, /* a72c LATIN CAPITAL LETTER CUATRILLO */ { 0x0000, 0x1123, 0x1123, 0x0000 }, /* a72d LATIN SMALL LETTER CUATRILLO */ { 0x1125, 0x0000, 0x0000, 0x1125 }, /* a72e LATIN CAPITAL LETTER CUATRILLO WITH COM */ { 0x0000, 0x1127, 0x1127, 0x0000 }, /* a72f LATIN SMALL LETTER CUATRILLO WITH COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a730 LATIN LETTER SMALL CAPITAL F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a731 LATIN LETTER SMALL CAPITAL S */ { 0x1129, 0x0000, 0x0000, 0x1129 }, /* a732 LATIN CAPITAL LETTER AA */ { 0x0000, 0x112b, 0x112b, 0x0000 }, /* a733 LATIN SMALL LETTER AA */ { 0x112d, 0x0000, 0x0000, 0x112d }, /* a734 LATIN CAPITAL LETTER AO */ { 0x0000, 0x112f, 0x112f, 0x0000 }, /* a735 LATIN SMALL LETTER AO */ { 0x1131, 0x0000, 0x0000, 0x1131 }, /* a736 LATIN CAPITAL LETTER AU */ { 0x0000, 0x1133, 0x1133, 0x0000 }, /* a737 LATIN SMALL LETTER AU */ { 0x1135, 0x0000, 0x0000, 0x1135 }, /* a738 LATIN CAPITAL LETTER AV */ { 0x0000, 0x1137, 0x1137, 0x0000 }, /* a739 LATIN SMALL LETTER AV */ { 0x1139, 0x0000, 0x0000, 0x1139 }, /* a73a LATIN CAPITAL LETTER AV WITH HORIZONTAL */ { 0x0000, 0x113b, 0x113b, 0x0000 }, /* a73b LATIN SMALL LETTER AV WITH HORIZONTAL B */ { 0x113d, 0x0000, 0x0000, 0x113d }, /* a73c LATIN CAPITAL LETTER AY */ { 0x0000, 0x113f, 0x113f, 0x0000 }, /* a73d LATIN SMALL LETTER AY */ { 0x1141, 0x0000, 0x0000, 0x1141 }, /* a73e LATIN CAPITAL LETTER REVERSED C WITH DO */ { 0x0000, 0x1143, 0x1143, 0x0000 }, /* a73f LATIN SMALL LETTER REVERSED C WITH DOT */ { 0x1145, 0x0000, 0x0000, 0x1145 }, /* a740 LATIN CAPITAL LETTER K WITH STROKE */ { 0x0000, 0x1147, 0x1147, 0x0000 }, /* a741 LATIN SMALL LETTER K WITH STROKE */ { 0x1149, 0x0000, 0x0000, 0x1149 }, /* a742 LATIN CAPITAL LETTER K WITH DIAGONAL ST */ { 0x0000, 0x114b, 0x114b, 0x0000 }, /* a743 LATIN SMALL LETTER K WITH DIAGONAL STRO */ { 0x114d, 0x0000, 0x0000, 0x114d }, /* a744 LATIN CAPITAL LETTER K WITH STROKE AND */ { 0x0000, 0x114f, 0x114f, 0x0000 }, /* a745 LATIN SMALL LETTER K WITH STROKE AND DI */ { 0x1151, 0x0000, 0x0000, 0x1151 }, /* a746 LATIN CAPITAL LETTER BROKEN L */ { 0x0000, 0x1153, 0x1153, 0x0000 }, /* a747 LATIN SMALL LETTER BROKEN L */ { 0x1155, 0x0000, 0x0000, 0x1155 }, /* a748 LATIN CAPITAL LETTER L WITH HIGH STROKE */ { 0x0000, 0x1157, 0x1157, 0x0000 }, /* a749 LATIN SMALL LETTER L WITH HIGH STROKE */ { 0x1159, 0x0000, 0x0000, 0x1159 }, /* a74a LATIN CAPITAL LETTER O WITH LONG STROKE */ { 0x0000, 0x115b, 0x115b, 0x0000 }, /* a74b LATIN SMALL LETTER O WITH LONG STROKE O */ { 0x115d, 0x0000, 0x0000, 0x115d }, /* a74c LATIN CAPITAL LETTER O WITH LOOP */ { 0x0000, 0x115f, 0x115f, 0x0000 }, /* a74d LATIN SMALL LETTER O WITH LOOP */ { 0x1161, 0x0000, 0x0000, 0x1161 }, /* a74e LATIN CAPITAL LETTER OO */ { 0x0000, 0x1163, 0x1163, 0x0000 }, /* a74f LATIN SMALL LETTER OO */ { 0x1165, 0x0000, 0x0000, 0x1165 }, /* a750 LATIN CAPITAL LETTER P WITH STROKE THRO */ { 0x0000, 0x1167, 0x1167, 0x0000 }, /* a751 LATIN SMALL LETTER P WITH STROKE THROUG */ { 0x1169, 0x0000, 0x0000, 0x1169 }, /* a752 LATIN CAPITAL LETTER P WITH FLOURISH */ { 0x0000, 0x116b, 0x116b, 0x0000 }, /* a753 LATIN SMALL LETTER P WITH FLOURISH */ { 0x116d, 0x0000, 0x0000, 0x116d }, /* a754 LATIN CAPITAL LETTER P WITH SQUIRREL TA */ { 0x0000, 0x116f, 0x116f, 0x0000 }, /* a755 LATIN SMALL LETTER P WITH SQUIRREL TAIL */ { 0x1171, 0x0000, 0x0000, 0x1171 }, /* a756 LATIN CAPITAL LETTER Q WITH STROKE THRO */ { 0x0000, 0x1173, 0x1173, 0x0000 }, /* a757 LATIN SMALL LETTER Q WITH STROKE THROUG */ { 0x1175, 0x0000, 0x0000, 0x1175 }, /* a758 LATIN CAPITAL LETTER Q WITH DIAGONAL ST */ { 0x0000, 0x1177, 0x1177, 0x0000 }, /* a759 LATIN SMALL LETTER Q WITH DIAGONAL STRO */ { 0x1179, 0x0000, 0x0000, 0x1179 }, /* a75a LATIN CAPITAL LETTER R ROTUNDA */ { 0x0000, 0x117b, 0x117b, 0x0000 }, /* a75b LATIN SMALL LETTER R ROTUNDA */ { 0x117d, 0x0000, 0x0000, 0x117d }, /* a75c LATIN CAPITAL LETTER RUM ROTUNDA */ { 0x0000, 0x117f, 0x117f, 0x0000 }, /* a75d LATIN SMALL LETTER RUM ROTUNDA */ { 0x1181, 0x0000, 0x0000, 0x1181 }, /* a75e LATIN CAPITAL LETTER V WITH DIAGONAL ST */ { 0x0000, 0x1183, 0x1183, 0x0000 }, /* a75f LATIN SMALL LETTER V WITH DIAGONAL STRO */ { 0x1185, 0x0000, 0x0000, 0x1185 }, /* a760 LATIN CAPITAL LETTER VY */ { 0x0000, 0x1187, 0x1187, 0x0000 }, /* a761 LATIN SMALL LETTER VY */ { 0x1189, 0x0000, 0x0000, 0x1189 }, /* a762 LATIN CAPITAL LETTER VISIGOTHIC Z */ { 0x0000, 0x118b, 0x118b, 0x0000 }, /* a763 LATIN SMALL LETTER VISIGOTHIC Z */ { 0x118d, 0x0000, 0x0000, 0x118d }, /* a764 LATIN CAPITAL LETTER THORN WITH STROKE */ { 0x0000, 0x118f, 0x118f, 0x0000 }, /* a765 LATIN SMALL LETTER THORN WITH STROKE */ { 0x1191, 0x0000, 0x0000, 0x1191 }, /* a766 LATIN CAPITAL LETTER THORN WITH STROKE */ { 0x0000, 0x1193, 0x1193, 0x0000 }, /* a767 LATIN SMALL LETTER THORN WITH STROKE TH */ { 0x1195, 0x0000, 0x0000, 0x1195 }, /* a768 LATIN CAPITAL LETTER VEND */ { 0x0000, 0x1197, 0x1197, 0x0000 }, /* a769 LATIN SMALL LETTER VEND */ { 0x1199, 0x0000, 0x0000, 0x1199 }, /* a76a LATIN CAPITAL LETTER ET */ { 0x0000, 0x119b, 0x119b, 0x0000 }, /* a76b LATIN SMALL LETTER ET */ { 0x119d, 0x0000, 0x0000, 0x119d }, /* a76c LATIN CAPITAL LETTER IS */ { 0x0000, 0x119f, 0x119f, 0x0000 }, /* a76d LATIN SMALL LETTER IS */ { 0x11a1, 0x0000, 0x0000, 0x11a1 }, /* a76e LATIN CAPITAL LETTER CON */ { 0x0000, 0x11a3, 0x11a3, 0x0000 }, /* a76f LATIN SMALL LETTER CON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a770 MODIFIER LETTER US */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a771 LATIN SMALL LETTER DUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a772 LATIN SMALL LETTER LUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a773 LATIN SMALL LETTER MUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a774 LATIN SMALL LETTER NUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a775 LATIN SMALL LETTER RUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a776 LATIN LETTER SMALL CAPITAL RUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a777 LATIN SMALL LETTER TUM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a778 LATIN SMALL LETTER UM */ { 0x11a5, 0x0000, 0x0000, 0x11a5 }, /* a779 LATIN CAPITAL LETTER INSULAR D */ { 0x0000, 0x11a7, 0x11a7, 0x0000 }, /* a77a LATIN SMALL LETTER INSULAR D */ { 0x11a9, 0x0000, 0x0000, 0x11a9 }, /* a77b LATIN CAPITAL LETTER INSULAR F */ { 0x0000, 0x11ab, 0x11ab, 0x0000 }, /* a77c LATIN SMALL LETTER INSULAR F */ { 0x11ad, 0x0000, 0x0000, 0x11ad }, /* a77d LATIN CAPITAL LETTER INSULAR G */ { 0x11af, 0x0000, 0x0000, 0x11af }, /* a77e LATIN CAPITAL LETTER TURNED INSULAR G */ { 0x0000, 0x11b1, 0x11b1, 0x0000 } /* a77f LATIN SMALL LETTER TURNED INSULAR G */ }; static case_table case_pg_14f[128] = { { 0x11b3, 0x0000, 0x0000, 0x11b3 }, /* a780 LATIN CAPITAL LETTER TURNED L */ { 0x0000, 0x11b5, 0x11b5, 0x0000 }, /* a781 LATIN SMALL LETTER TURNED L */ { 0x11b7, 0x0000, 0x0000, 0x11b7 }, /* a782 LATIN CAPITAL LETTER INSULAR R */ { 0x0000, 0x11b9, 0x11b9, 0x0000 }, /* a783 LATIN SMALL LETTER INSULAR R */ { 0x11bb, 0x0000, 0x0000, 0x11bb }, /* a784 LATIN CAPITAL LETTER INSULAR S */ { 0x0000, 0x11bd, 0x11bd, 0x0000 }, /* a785 LATIN SMALL LETTER INSULAR S */ { 0x11bf, 0x0000, 0x0000, 0x11bf }, /* a786 LATIN CAPITAL LETTER INSULAR T */ { 0x0000, 0x11c1, 0x11c1, 0x0000 }, /* a787 LATIN SMALL LETTER INSULAR T */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a788 MODIFIER LETTER LOW CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a789 MODIFIER LETTER COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78a MODIFIER LETTER SHORT EQUALS SIGN */ { 0x11c3, 0x0000, 0x0000, 0x11c3 }, /* a78b LATIN CAPITAL LETTER SALTILLO */ { 0x0000, 0x11c5, 0x11c5, 0x0000 }, /* a78c LATIN SMALL LETTER SALTILLO */ { 0x11c7, 0x0000, 0x0000, 0x11c7 }, /* a78d LATIN CAPITAL LETTER TURNED H */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78e LATIN SMALL LETTER L WITH RETROFLEX HOO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a78f (null) */ { 0x11c9, 0x0000, 0x0000, 0x11c9 }, /* a790 LATIN CAPITAL LETTER N WITH DESCENDER */ { 0x0000, 0x11cb, 0x11cb, 0x0000 }, /* a791 LATIN SMALL LETTER N WITH DESCENDER */ { 0x11cd, 0x0000, 0x0000, 0x11cd }, /* a792 LATIN CAPITAL LETTER C WITH BAR */ { 0x0000, 0x11cf, 0x11cf, 0x0000 }, /* a793 LATIN SMALL LETTER C WITH BAR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a794 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a795 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a796 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a797 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a798 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a799 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a79f (null) */ { 0x11d1, 0x0000, 0x0000, 0x11d1 }, /* a7a0 LATIN CAPITAL LETTER G WITH OBLIQUE STR */ { 0x0000, 0x11d3, 0x11d3, 0x0000 }, /* a7a1 LATIN SMALL LETTER G WITH OBLIQUE STROK */ { 0x11d5, 0x0000, 0x0000, 0x11d5 }, /* a7a2 LATIN CAPITAL LETTER K WITH OBLIQUE STR */ { 0x0000, 0x11d7, 0x11d7, 0x0000 }, /* a7a3 LATIN SMALL LETTER K WITH OBLIQUE STROK */ { 0x11d9, 0x0000, 0x0000, 0x11d9 }, /* a7a4 LATIN CAPITAL LETTER N WITH OBLIQUE STR */ { 0x0000, 0x11db, 0x11db, 0x0000 }, /* a7a5 LATIN SMALL LETTER N WITH OBLIQUE STROK */ { 0x11dd, 0x0000, 0x0000, 0x11dd }, /* a7a6 LATIN CAPITAL LETTER R WITH OBLIQUE STR */ { 0x0000, 0x11df, 0x11df, 0x0000 }, /* a7a7 LATIN SMALL LETTER R WITH OBLIQUE STROK */ { 0x11e1, 0x0000, 0x0000, 0x11e1 }, /* a7a8 LATIN CAPITAL LETTER S WITH OBLIQUE STR */ { 0x0000, 0x11e3, 0x11e3, 0x0000 }, /* a7a9 LATIN SMALL LETTER S WITH OBLIQUE STROK */ { 0x11e5, 0x0000, 0x0000, 0x11e5 }, /* a7aa LATIN CAPITAL LETTER H WITH HOOK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ab (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ac (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ad (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ae (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7af (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7b9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ba (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7be (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7bf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7c9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ca (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ce (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7cf (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7d9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7da (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7db (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7dc (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7dd (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7de (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7df (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e8 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7e9 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ea (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7eb (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ec (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ed (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ee (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7ef (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f0 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f1 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f2 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f3 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f4 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f5 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f6 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f7 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f8 MODIFIER LETTER CAPITAL H WITH STROKE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7f9 MODIFIER LETTER SMALL LIGATURE OE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fa LATIN LETTER SMALL CAPITAL TURNED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fb LATIN EPIGRAPHIC LETTER REVERSED F */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fc LATIN EPIGRAPHIC LETTER REVERSED P */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fd LATIN EPIGRAPHIC LETTER INVERTED M */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* a7fe LATIN EPIGRAPHIC LETTER I LONGA */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* a7ff LATIN EPIGRAPHIC LETTER ARCHAIC M */ }; static case_table case_pg_1f6[128] = { { 0x0000, 0x11e7, 0x11ea, 0x11ed }, /* fb00 LATIN SMALL LIGATURE FF */ { 0x0000, 0x11f0, 0x11f3, 0x11f6 }, /* fb01 LATIN SMALL LIGATURE FI */ { 0x0000, 0x11f9, 0x11fc, 0x11ff }, /* fb02 LATIN SMALL LIGATURE FL */ { 0x0000, 0x1202, 0x1206, 0x120a }, /* fb03 LATIN SMALL LIGATURE FFI */ { 0x0000, 0x120e, 0x1212, 0x1216 }, /* fb04 LATIN SMALL LIGATURE FFL */ { 0x0000, 0x121a, 0x121d, 0x1220 }, /* fb05 LATIN SMALL LIGATURE LONG S T */ { 0x0000, 0x1223, 0x1226, 0x1229 }, /* fb06 LATIN SMALL LIGATURE ST */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb07 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb08 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb09 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0e (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb0f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb10 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb11 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb12 (null) */ { 0x0000, 0x122c, 0x122f, 0x1232 }, /* fb13 ARMENIAN SMALL LIGATURE MEN NOW */ { 0x0000, 0x1235, 0x1238, 0x123b }, /* fb14 ARMENIAN SMALL LIGATURE MEN ECH */ { 0x0000, 0x123e, 0x1241, 0x1244 }, /* fb15 ARMENIAN SMALL LIGATURE MEN INI */ { 0x0000, 0x1247, 0x124a, 0x124d }, /* fb16 ARMENIAN SMALL LIGATURE VEW NOW */ { 0x0000, 0x1250, 0x1253, 0x1256 }, /* fb17 ARMENIAN SMALL LIGATURE MEN XEH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb18 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb19 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1a (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1b (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1c (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1d HEBREW LETTER YOD WITH HIRIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1e HEBREW POINT JUDEO-SPANISH VARIKA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb1f HEBREW LIGATURE YIDDISH YOD YOD PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb20 HEBREW LETTER ALTERNATIVE AYIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb21 HEBREW LETTER WIDE ALEF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb22 HEBREW LETTER WIDE DALET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb23 HEBREW LETTER WIDE HE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb24 HEBREW LETTER WIDE KAF */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb25 HEBREW LETTER WIDE LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb26 HEBREW LETTER WIDE FINAL MEM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb27 HEBREW LETTER WIDE RESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb28 HEBREW LETTER WIDE TAV */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb29 HEBREW LETTER ALTERNATIVE PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2a HEBREW LETTER SHIN WITH SHIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2b HEBREW LETTER SHIN WITH SIN DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2c HEBREW LETTER SHIN WITH DAGESH AND SHIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2d HEBREW LETTER SHIN WITH DAGESH AND SIN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2e HEBREW LETTER ALEF WITH PATAH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb2f HEBREW LETTER ALEF WITH QAMATS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb30 HEBREW LETTER ALEF WITH MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb31 HEBREW LETTER BET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb32 HEBREW LETTER GIMEL WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb33 HEBREW LETTER DALET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb34 HEBREW LETTER HE WITH MAPIQ */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb35 HEBREW LETTER VAV WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb36 HEBREW LETTER ZAYIN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb37 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb38 HEBREW LETTER TET WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb39 HEBREW LETTER YOD WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3a HEBREW LETTER FINAL KAF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3b HEBREW LETTER KAF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3c HEBREW LETTER LAMED WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3d (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3e HEBREW LETTER MEM WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb3f (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb40 HEBREW LETTER NUN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb41 HEBREW LETTER SAMEKH WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb42 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb43 HEBREW LETTER FINAL PE WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb44 HEBREW LETTER PE WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb45 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb46 HEBREW LETTER TSADI WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb47 HEBREW LETTER QOF WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb48 HEBREW LETTER RESH WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb49 HEBREW LETTER SHIN WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4a HEBREW LETTER TAV WITH DAGESH */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4b HEBREW LETTER VAV WITH HOLAM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4c HEBREW LETTER BET WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4d HEBREW LETTER KAF WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4e HEBREW LETTER PE WITH RAFE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb4f HEBREW LIGATURE ALEF LAMED */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb50 ARABIC LETTER ALEF WASLA ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb51 ARABIC LETTER ALEF WASLA FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb52 ARABIC LETTER BEEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb53 ARABIC LETTER BEEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb54 ARABIC LETTER BEEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb55 ARABIC LETTER BEEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb56 ARABIC LETTER PEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb57 ARABIC LETTER PEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb58 ARABIC LETTER PEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb59 ARABIC LETTER PEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5a ARABIC LETTER BEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5b ARABIC LETTER BEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5c ARABIC LETTER BEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5d ARABIC LETTER BEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5e ARABIC LETTER TTEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb5f ARABIC LETTER TTEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb60 ARABIC LETTER TTEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb61 ARABIC LETTER TTEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb62 ARABIC LETTER TEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb63 ARABIC LETTER TEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb64 ARABIC LETTER TEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb65 ARABIC LETTER TEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb66 ARABIC LETTER TTEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb67 ARABIC LETTER TTEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb68 ARABIC LETTER TTEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb69 ARABIC LETTER TTEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6a ARABIC LETTER VEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6b ARABIC LETTER VEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6c ARABIC LETTER VEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6d ARABIC LETTER VEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6e ARABIC LETTER PEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb6f ARABIC LETTER PEHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb70 ARABIC LETTER PEHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb71 ARABIC LETTER PEHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb72 ARABIC LETTER DYEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb73 ARABIC LETTER DYEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb74 ARABIC LETTER DYEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb75 ARABIC LETTER DYEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb76 ARABIC LETTER NYEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb77 ARABIC LETTER NYEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb78 ARABIC LETTER NYEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb79 ARABIC LETTER NYEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7a ARABIC LETTER TCHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7b ARABIC LETTER TCHEH FINAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7c ARABIC LETTER TCHEH INITIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7d ARABIC LETTER TCHEH MEDIAL FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* fb7e ARABIC LETTER TCHEHEH ISOLATED FORM */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* fb7f ARABIC LETTER TCHEHEH FINAL FORM */ }; static case_table case_pg_1fe[128] = { { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff00 (null) */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff01 FULLWIDTH EXCLAMATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff02 FULLWIDTH QUOTATION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff03 FULLWIDTH NUMBER SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff04 FULLWIDTH DOLLAR SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff05 FULLWIDTH PERCENT SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff06 FULLWIDTH AMPERSAND */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff07 FULLWIDTH APOSTROPHE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff08 FULLWIDTH LEFT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff09 FULLWIDTH RIGHT PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0a FULLWIDTH ASTERISK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0b FULLWIDTH PLUS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0c FULLWIDTH COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0d FULLWIDTH HYPHEN-MINUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0e FULLWIDTH FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff0f FULLWIDTH SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff10 FULLWIDTH DIGIT ZERO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff11 FULLWIDTH DIGIT ONE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff12 FULLWIDTH DIGIT TWO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff13 FULLWIDTH DIGIT THREE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff14 FULLWIDTH DIGIT FOUR */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff15 FULLWIDTH DIGIT FIVE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff16 FULLWIDTH DIGIT SIX */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff17 FULLWIDTH DIGIT SEVEN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff18 FULLWIDTH DIGIT EIGHT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff19 FULLWIDTH DIGIT NINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1a FULLWIDTH COLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1b FULLWIDTH SEMICOLON */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1c FULLWIDTH LESS-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1d FULLWIDTH EQUALS SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1e FULLWIDTH GREATER-THAN SIGN */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff1f FULLWIDTH QUESTION MARK */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff20 FULLWIDTH COMMERCIAL AT */ { 0x1259, 0x0000, 0x0000, 0x1259 }, /* ff21 FULLWIDTH LATIN CAPITAL LETTER A */ { 0x125b, 0x0000, 0x0000, 0x125b }, /* ff22 FULLWIDTH LATIN CAPITAL LETTER B */ { 0x125d, 0x0000, 0x0000, 0x125d }, /* ff23 FULLWIDTH LATIN CAPITAL LETTER C */ { 0x125f, 0x0000, 0x0000, 0x125f }, /* ff24 FULLWIDTH LATIN CAPITAL LETTER D */ { 0x1261, 0x0000, 0x0000, 0x1261 }, /* ff25 FULLWIDTH LATIN CAPITAL LETTER E */ { 0x1263, 0x0000, 0x0000, 0x1263 }, /* ff26 FULLWIDTH LATIN CAPITAL LETTER F */ { 0x1265, 0x0000, 0x0000, 0x1265 }, /* ff27 FULLWIDTH LATIN CAPITAL LETTER G */ { 0x1267, 0x0000, 0x0000, 0x1267 }, /* ff28 FULLWIDTH LATIN CAPITAL LETTER H */ { 0x1269, 0x0000, 0x0000, 0x1269 }, /* ff29 FULLWIDTH LATIN CAPITAL LETTER I */ { 0x126b, 0x0000, 0x0000, 0x126b }, /* ff2a FULLWIDTH LATIN CAPITAL LETTER J */ { 0x126d, 0x0000, 0x0000, 0x126d }, /* ff2b FULLWIDTH LATIN CAPITAL LETTER K */ { 0x126f, 0x0000, 0x0000, 0x126f }, /* ff2c FULLWIDTH LATIN CAPITAL LETTER L */ { 0x1271, 0x0000, 0x0000, 0x1271 }, /* ff2d FULLWIDTH LATIN CAPITAL LETTER M */ { 0x1273, 0x0000, 0x0000, 0x1273 }, /* ff2e FULLWIDTH LATIN CAPITAL LETTER N */ { 0x1275, 0x0000, 0x0000, 0x1275 }, /* ff2f FULLWIDTH LATIN CAPITAL LETTER O */ { 0x1277, 0x0000, 0x0000, 0x1277 }, /* ff30 FULLWIDTH LATIN CAPITAL LETTER P */ { 0x1279, 0x0000, 0x0000, 0x1279 }, /* ff31 FULLWIDTH LATIN CAPITAL LETTER Q */ { 0x127b, 0x0000, 0x0000, 0x127b }, /* ff32 FULLWIDTH LATIN CAPITAL LETTER R */ { 0x127d, 0x0000, 0x0000, 0x127d }, /* ff33 FULLWIDTH LATIN CAPITAL LETTER S */ { 0x127f, 0x0000, 0x0000, 0x127f }, /* ff34 FULLWIDTH LATIN CAPITAL LETTER T */ { 0x1281, 0x0000, 0x0000, 0x1281 }, /* ff35 FULLWIDTH LATIN CAPITAL LETTER U */ { 0x1283, 0x0000, 0x0000, 0x1283 }, /* ff36 FULLWIDTH LATIN CAPITAL LETTER V */ { 0x1285, 0x0000, 0x0000, 0x1285 }, /* ff37 FULLWIDTH LATIN CAPITAL LETTER W */ { 0x1287, 0x0000, 0x0000, 0x1287 }, /* ff38 FULLWIDTH LATIN CAPITAL LETTER X */ { 0x1289, 0x0000, 0x0000, 0x1289 }, /* ff39 FULLWIDTH LATIN CAPITAL LETTER Y */ { 0x128b, 0x0000, 0x0000, 0x128b }, /* ff3a FULLWIDTH LATIN CAPITAL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3b FULLWIDTH LEFT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3c FULLWIDTH REVERSE SOLIDUS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3d FULLWIDTH RIGHT SQUARE BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3e FULLWIDTH CIRCUMFLEX ACCENT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff3f FULLWIDTH LOW LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff40 FULLWIDTH GRAVE ACCENT */ { 0x0000, 0x128d, 0x128d, 0x0000 }, /* ff41 FULLWIDTH LATIN SMALL LETTER A */ { 0x0000, 0x128f, 0x128f, 0x0000 }, /* ff42 FULLWIDTH LATIN SMALL LETTER B */ { 0x0000, 0x1291, 0x1291, 0x0000 }, /* ff43 FULLWIDTH LATIN SMALL LETTER C */ { 0x0000, 0x1293, 0x1293, 0x0000 }, /* ff44 FULLWIDTH LATIN SMALL LETTER D */ { 0x0000, 0x1295, 0x1295, 0x0000 }, /* ff45 FULLWIDTH LATIN SMALL LETTER E */ { 0x0000, 0x1297, 0x1297, 0x0000 }, /* ff46 FULLWIDTH LATIN SMALL LETTER F */ { 0x0000, 0x1299, 0x1299, 0x0000 }, /* ff47 FULLWIDTH LATIN SMALL LETTER G */ { 0x0000, 0x129b, 0x129b, 0x0000 }, /* ff48 FULLWIDTH LATIN SMALL LETTER H */ { 0x0000, 0x129d, 0x129d, 0x0000 }, /* ff49 FULLWIDTH LATIN SMALL LETTER I */ { 0x0000, 0x129f, 0x129f, 0x0000 }, /* ff4a FULLWIDTH LATIN SMALL LETTER J */ { 0x0000, 0x12a1, 0x12a1, 0x0000 }, /* ff4b FULLWIDTH LATIN SMALL LETTER K */ { 0x0000, 0x12a3, 0x12a3, 0x0000 }, /* ff4c FULLWIDTH LATIN SMALL LETTER L */ { 0x0000, 0x12a5, 0x12a5, 0x0000 }, /* ff4d FULLWIDTH LATIN SMALL LETTER M */ { 0x0000, 0x12a7, 0x12a7, 0x0000 }, /* ff4e FULLWIDTH LATIN SMALL LETTER N */ { 0x0000, 0x12a9, 0x12a9, 0x0000 }, /* ff4f FULLWIDTH LATIN SMALL LETTER O */ { 0x0000, 0x12ab, 0x12ab, 0x0000 }, /* ff50 FULLWIDTH LATIN SMALL LETTER P */ { 0x0000, 0x12ad, 0x12ad, 0x0000 }, /* ff51 FULLWIDTH LATIN SMALL LETTER Q */ { 0x0000, 0x12af, 0x12af, 0x0000 }, /* ff52 FULLWIDTH LATIN SMALL LETTER R */ { 0x0000, 0x12b1, 0x12b1, 0x0000 }, /* ff53 FULLWIDTH LATIN SMALL LETTER S */ { 0x0000, 0x12b3, 0x12b3, 0x0000 }, /* ff54 FULLWIDTH LATIN SMALL LETTER T */ { 0x0000, 0x12b5, 0x12b5, 0x0000 }, /* ff55 FULLWIDTH LATIN SMALL LETTER U */ { 0x0000, 0x12b7, 0x12b7, 0x0000 }, /* ff56 FULLWIDTH LATIN SMALL LETTER V */ { 0x0000, 0x12b9, 0x12b9, 0x0000 }, /* ff57 FULLWIDTH LATIN SMALL LETTER W */ { 0x0000, 0x12bb, 0x12bb, 0x0000 }, /* ff58 FULLWIDTH LATIN SMALL LETTER X */ { 0x0000, 0x12bd, 0x12bd, 0x0000 }, /* ff59 FULLWIDTH LATIN SMALL LETTER Y */ { 0x0000, 0x12bf, 0x12bf, 0x0000 }, /* ff5a FULLWIDTH LATIN SMALL LETTER Z */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5b FULLWIDTH LEFT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5c FULLWIDTH VERTICAL LINE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5d FULLWIDTH RIGHT CURLY BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5e FULLWIDTH TILDE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff5f FULLWIDTH LEFT WHITE PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff60 FULLWIDTH RIGHT WHITE PARENTHESIS */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff61 HALFWIDTH IDEOGRAPHIC FULL STOP */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff62 HALFWIDTH LEFT CORNER BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff63 HALFWIDTH RIGHT CORNER BRACKET */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff64 HALFWIDTH IDEOGRAPHIC COMMA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff65 HALFWIDTH KATAKANA MIDDLE DOT */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff66 HALFWIDTH KATAKANA LETTER WO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff67 HALFWIDTH KATAKANA LETTER SMALL A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff68 HALFWIDTH KATAKANA LETTER SMALL I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff69 HALFWIDTH KATAKANA LETTER SMALL U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6a HALFWIDTH KATAKANA LETTER SMALL E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6b HALFWIDTH KATAKANA LETTER SMALL O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6c HALFWIDTH KATAKANA LETTER SMALL YA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6d HALFWIDTH KATAKANA LETTER SMALL YU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6e HALFWIDTH KATAKANA LETTER SMALL YO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff6f HALFWIDTH KATAKANA LETTER SMALL TU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED S */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff71 HALFWIDTH KATAKANA LETTER A */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff72 HALFWIDTH KATAKANA LETTER I */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff73 HALFWIDTH KATAKANA LETTER U */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff74 HALFWIDTH KATAKANA LETTER E */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff75 HALFWIDTH KATAKANA LETTER O */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff76 HALFWIDTH KATAKANA LETTER KA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff77 HALFWIDTH KATAKANA LETTER KI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff78 HALFWIDTH KATAKANA LETTER KU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff79 HALFWIDTH KATAKANA LETTER KE */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7a HALFWIDTH KATAKANA LETTER KO */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7b HALFWIDTH KATAKANA LETTER SA */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7c HALFWIDTH KATAKANA LETTER SI */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7d HALFWIDTH KATAKANA LETTER SU */ { 0x0000, 0x0000, 0x0000, 0x0000 }, /* ff7e HALFWIDTH KATAKANA LETTER SE */ { 0x0000, 0x0000, 0x0000, 0x0000 } /* ff7f HALFWIDTH KATAKANA LETTER SO */ }; static case_table *case_tab[512] = { case_pg_000, case_pg_001, case_pg_002, case_pg_003, case_pg_004, case_pg_005, case_pg_006, case_pg_007, case_pg_008, case_pg_009, case_pg_00a, case_pg_00b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_021, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_03a, 0, case_pg_03c, case_pg_03d, case_pg_03e, case_pg_03f, 0, 0, case_pg_042, case_pg_043, 0, 0, 0, 0, 0, case_pg_049, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_058, case_pg_059, case_pg_05a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_14c, case_pg_14d, case_pg_14e, case_pg_14f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case_pg_1f6, 0, 0, 0, 0, 0, 0, 0, case_pg_1fe, 0 }; static wchar_t case_expansion[] = { /* 0000 */ 0x0000, /* 0001 */ 0x0061, 0x0000, /* 0003 */ 0x0062, 0x0000, /* 0005 */ 0x0063, 0x0000, /* 0007 */ 0x0064, 0x0000, /* 0009 */ 0x0065, 0x0000, /* 000b */ 0x0066, 0x0000, /* 000d */ 0x0067, 0x0000, /* 000f */ 0x0068, 0x0000, /* 0011 */ 0x0069, 0x0000, /* 0013 */ 0x006a, 0x0000, /* 0015 */ 0x006b, 0x0000, /* 0017 */ 0x006c, 0x0000, /* 0019 */ 0x006d, 0x0000, /* 001b */ 0x006e, 0x0000, /* 001d */ 0x006f, 0x0000, /* 001f */ 0x0070, 0x0000, /* 0021 */ 0x0071, 0x0000, /* 0023 */ 0x0072, 0x0000, /* 0025 */ 0x0073, 0x0000, /* 0027 */ 0x0074, 0x0000, /* 0029 */ 0x0075, 0x0000, /* 002b */ 0x0076, 0x0000, /* 002d */ 0x0077, 0x0000, /* 002f */ 0x0078, 0x0000, /* 0031 */ 0x0079, 0x0000, /* 0033 */ 0x007a, 0x0000, /* 0035 */ 0x0041, 0x0000, /* 0037 */ 0x0042, 0x0000, /* 0039 */ 0x0043, 0x0000, /* 003b */ 0x0044, 0x0000, /* 003d */ 0x0045, 0x0000, /* 003f */ 0x0046, 0x0000, /* 0041 */ 0x0047, 0x0000, /* 0043 */ 0x0048, 0x0000, /* 0045 */ 0x0049, 0x0000, /* 0047 */ 0x004a, 0x0000, /* 0049 */ 0x004b, 0x0000, /* 004b */ 0x004c, 0x0000, /* 004d */ 0x004d, 0x0000, /* 004f */ 0x004e, 0x0000, /* 0051 */ 0x004f, 0x0000, /* 0053 */ 0x0050, 0x0000, /* 0055 */ 0x0051, 0x0000, /* 0057 */ 0x0052, 0x0000, /* 0059 */ 0x0053, 0x0000, /* 005b */ 0x0054, 0x0000, /* 005d */ 0x0055, 0x0000, /* 005f */ 0x0056, 0x0000, /* 0061 */ 0x0057, 0x0000, /* 0063 */ 0x0058, 0x0000, /* 0065 */ 0x0059, 0x0000, /* 0067 */ 0x005a, 0x0000, /* 0069 */ 0x039c, 0x0000, /* 006b */ 0x03bc, 0x0000, /* 006d */ 0x00e0, 0x0000, /* 006f */ 0x00e1, 0x0000, /* 0071 */ 0x00e2, 0x0000, /* 0073 */ 0x00e3, 0x0000, /* 0075 */ 0x00e4, 0x0000, /* 0077 */ 0x00e5, 0x0000, /* 0079 */ 0x00e6, 0x0000, /* 007b */ 0x00e7, 0x0000, /* 007d */ 0x00e8, 0x0000, /* 007f */ 0x00e9, 0x0000, /* 0081 */ 0x00ea, 0x0000, /* 0083 */ 0x00eb, 0x0000, /* 0085 */ 0x00ec, 0x0000, /* 0087 */ 0x00ed, 0x0000, /* 0089 */ 0x00ee, 0x0000, /* 008b */ 0x00ef, 0x0000, /* 008d */ 0x00f0, 0x0000, /* 008f */ 0x00f1, 0x0000, /* 0091 */ 0x00f2, 0x0000, /* 0093 */ 0x00f3, 0x0000, /* 0095 */ 0x00f4, 0x0000, /* 0097 */ 0x00f5, 0x0000, /* 0099 */ 0x00f6, 0x0000, /* 009b */ 0x00f8, 0x0000, /* 009d */ 0x00f9, 0x0000, /* 009f */ 0x00fa, 0x0000, /* 00a1 */ 0x00fb, 0x0000, /* 00a3 */ 0x00fc, 0x0000, /* 00a5 */ 0x00fd, 0x0000, /* 00a7 */ 0x00fe, 0x0000, /* 00a9 */ 0x0053, 0x0073, 0x0000, /* 00ac */ 0x0053, 0x0053, 0x0000, /* 00af */ 0x0073, 0x0073, 0x0000, /* 00b2 */ 0x00c0, 0x0000, /* 00b4 */ 0x00c1, 0x0000, /* 00b6 */ 0x00c2, 0x0000, /* 00b8 */ 0x00c3, 0x0000, /* 00ba */ 0x00c4, 0x0000, /* 00bc */ 0x00c5, 0x0000, /* 00be */ 0x00c6, 0x0000, /* 00c0 */ 0x00c7, 0x0000, /* 00c2 */ 0x00c8, 0x0000, /* 00c4 */ 0x00c9, 0x0000, /* 00c6 */ 0x00ca, 0x0000, /* 00c8 */ 0x00cb, 0x0000, /* 00ca */ 0x00cc, 0x0000, /* 00cc */ 0x00cd, 0x0000, /* 00ce */ 0x00ce, 0x0000, /* 00d0 */ 0x00cf, 0x0000, /* 00d2 */ 0x00d0, 0x0000, /* 00d4 */ 0x00d1, 0x0000, /* 00d6 */ 0x00d2, 0x0000, /* 00d8 */ 0x00d3, 0x0000, /* 00da */ 0x00d4, 0x0000, /* 00dc */ 0x00d5, 0x0000, /* 00de */ 0x00d6, 0x0000, /* 00e0 */ 0x00d8, 0x0000, /* 00e2 */ 0x00d9, 0x0000, /* 00e4 */ 0x00da, 0x0000, /* 00e6 */ 0x00db, 0x0000, /* 00e8 */ 0x00dc, 0x0000, /* 00ea */ 0x00dd, 0x0000, /* 00ec */ 0x00de, 0x0000, /* 00ee */ 0x0178, 0x0000, /* 00f0 */ 0x0101, 0x0000, /* 00f2 */ 0x0100, 0x0000, /* 00f4 */ 0x0103, 0x0000, /* 00f6 */ 0x0102, 0x0000, /* 00f8 */ 0x0105, 0x0000, /* 00fa */ 0x0104, 0x0000, /* 00fc */ 0x0107, 0x0000, /* 00fe */ 0x0106, 0x0000, /* 0100 */ 0x0109, 0x0000, /* 0102 */ 0x0108, 0x0000, /* 0104 */ 0x010b, 0x0000, /* 0106 */ 0x010a, 0x0000, /* 0108 */ 0x010d, 0x0000, /* 010a */ 0x010c, 0x0000, /* 010c */ 0x010f, 0x0000, /* 010e */ 0x010e, 0x0000, /* 0110 */ 0x0111, 0x0000, /* 0112 */ 0x0110, 0x0000, /* 0114 */ 0x0113, 0x0000, /* 0116 */ 0x0112, 0x0000, /* 0118 */ 0x0115, 0x0000, /* 011a */ 0x0114, 0x0000, /* 011c */ 0x0117, 0x0000, /* 011e */ 0x0116, 0x0000, /* 0120 */ 0x0119, 0x0000, /* 0122 */ 0x0118, 0x0000, /* 0124 */ 0x011b, 0x0000, /* 0126 */ 0x011a, 0x0000, /* 0128 */ 0x011d, 0x0000, /* 012a */ 0x011c, 0x0000, /* 012c */ 0x011f, 0x0000, /* 012e */ 0x011e, 0x0000, /* 0130 */ 0x0121, 0x0000, /* 0132 */ 0x0120, 0x0000, /* 0134 */ 0x0123, 0x0000, /* 0136 */ 0x0122, 0x0000, /* 0138 */ 0x0125, 0x0000, /* 013a */ 0x0124, 0x0000, /* 013c */ 0x0127, 0x0000, /* 013e */ 0x0126, 0x0000, /* 0140 */ 0x0129, 0x0000, /* 0142 */ 0x0128, 0x0000, /* 0144 */ 0x012b, 0x0000, /* 0146 */ 0x012a, 0x0000, /* 0148 */ 0x012d, 0x0000, /* 014a */ 0x012c, 0x0000, /* 014c */ 0x012f, 0x0000, /* 014e */ 0x012e, 0x0000, /* 0150 */ 0x0069, 0x0307, 0x0000, /* 0153 */ 0x0049, 0x0000, /* 0155 */ 0x0133, 0x0000, /* 0157 */ 0x0132, 0x0000, /* 0159 */ 0x0135, 0x0000, /* 015b */ 0x0134, 0x0000, /* 015d */ 0x0137, 0x0000, /* 015f */ 0x0136, 0x0000, /* 0161 */ 0x013a, 0x0000, /* 0163 */ 0x0139, 0x0000, /* 0165 */ 0x013c, 0x0000, /* 0167 */ 0x013b, 0x0000, /* 0169 */ 0x013e, 0x0000, /* 016b */ 0x013d, 0x0000, /* 016d */ 0x0140, 0x0000, /* 016f */ 0x013f, 0x0000, /* 0171 */ 0x0142, 0x0000, /* 0173 */ 0x0141, 0x0000, /* 0175 */ 0x0144, 0x0000, /* 0177 */ 0x0143, 0x0000, /* 0179 */ 0x0146, 0x0000, /* 017b */ 0x0145, 0x0000, /* 017d */ 0x0148, 0x0000, /* 017f */ 0x0147, 0x0000, /* 0181 */ 0x02bc, 0x004e, 0x0000, /* 0184 */ 0x02bc, 0x006e, 0x0000, /* 0187 */ 0x014b, 0x0000, /* 0189 */ 0x014a, 0x0000, /* 018b */ 0x014d, 0x0000, /* 018d */ 0x014c, 0x0000, /* 018f */ 0x014f, 0x0000, /* 0191 */ 0x014e, 0x0000, /* 0193 */ 0x0151, 0x0000, /* 0195 */ 0x0150, 0x0000, /* 0197 */ 0x0153, 0x0000, /* 0199 */ 0x0152, 0x0000, /* 019b */ 0x0155, 0x0000, /* 019d */ 0x0154, 0x0000, /* 019f */ 0x0157, 0x0000, /* 01a1 */ 0x0156, 0x0000, /* 01a3 */ 0x0159, 0x0000, /* 01a5 */ 0x0158, 0x0000, /* 01a7 */ 0x015b, 0x0000, /* 01a9 */ 0x015a, 0x0000, /* 01ab */ 0x015d, 0x0000, /* 01ad */ 0x015c, 0x0000, /* 01af */ 0x015f, 0x0000, /* 01b1 */ 0x015e, 0x0000, /* 01b3 */ 0x0161, 0x0000, /* 01b5 */ 0x0160, 0x0000, /* 01b7 */ 0x0163, 0x0000, /* 01b9 */ 0x0162, 0x0000, /* 01bb */ 0x0165, 0x0000, /* 01bd */ 0x0164, 0x0000, /* 01bf */ 0x0167, 0x0000, /* 01c1 */ 0x0166, 0x0000, /* 01c3 */ 0x0169, 0x0000, /* 01c5 */ 0x0168, 0x0000, /* 01c7 */ 0x016b, 0x0000, /* 01c9 */ 0x016a, 0x0000, /* 01cb */ 0x016d, 0x0000, /* 01cd */ 0x016c, 0x0000, /* 01cf */ 0x016f, 0x0000, /* 01d1 */ 0x016e, 0x0000, /* 01d3 */ 0x0171, 0x0000, /* 01d5 */ 0x0170, 0x0000, /* 01d7 */ 0x0173, 0x0000, /* 01d9 */ 0x0172, 0x0000, /* 01db */ 0x0175, 0x0000, /* 01dd */ 0x0174, 0x0000, /* 01df */ 0x0177, 0x0000, /* 01e1 */ 0x0176, 0x0000, /* 01e3 */ 0x00ff, 0x0000, /* 01e5 */ 0x017a, 0x0000, /* 01e7 */ 0x0179, 0x0000, /* 01e9 */ 0x017c, 0x0000, /* 01eb */ 0x017b, 0x0000, /* 01ed */ 0x017e, 0x0000, /* 01ef */ 0x017d, 0x0000, /* 01f1 */ 0x0053, 0x0000, /* 01f3 */ 0x0073, 0x0000, /* 01f5 */ 0x0243, 0x0000, /* 01f7 */ 0x0253, 0x0000, /* 01f9 */ 0x0183, 0x0000, /* 01fb */ 0x0182, 0x0000, /* 01fd */ 0x0185, 0x0000, /* 01ff */ 0x0184, 0x0000, /* 0201 */ 0x0254, 0x0000, /* 0203 */ 0x0188, 0x0000, /* 0205 */ 0x0187, 0x0000, /* 0207 */ 0x0256, 0x0000, /* 0209 */ 0x0257, 0x0000, /* 020b */ 0x018c, 0x0000, /* 020d */ 0x018b, 0x0000, /* 020f */ 0x01dd, 0x0000, /* 0211 */ 0x0259, 0x0000, /* 0213 */ 0x025b, 0x0000, /* 0215 */ 0x0192, 0x0000, /* 0217 */ 0x0191, 0x0000, /* 0219 */ 0x0260, 0x0000, /* 021b */ 0x0263, 0x0000, /* 021d */ 0x01f6, 0x0000, /* 021f */ 0x0269, 0x0000, /* 0221 */ 0x0268, 0x0000, /* 0223 */ 0x0199, 0x0000, /* 0225 */ 0x0198, 0x0000, /* 0227 */ 0x023d, 0x0000, /* 0229 */ 0x026f, 0x0000, /* 022b */ 0x0272, 0x0000, /* 022d */ 0x0220, 0x0000, /* 022f */ 0x0275, 0x0000, /* 0231 */ 0x01a1, 0x0000, /* 0233 */ 0x01a0, 0x0000, /* 0235 */ 0x01a3, 0x0000, /* 0237 */ 0x01a2, 0x0000, /* 0239 */ 0x01a5, 0x0000, /* 023b */ 0x01a4, 0x0000, /* 023d */ 0x0280, 0x0000, /* 023f */ 0x01a8, 0x0000, /* 0241 */ 0x01a7, 0x0000, /* 0243 */ 0x0283, 0x0000, /* 0245 */ 0x01ad, 0x0000, /* 0247 */ 0x01ac, 0x0000, /* 0249 */ 0x0288, 0x0000, /* 024b */ 0x01b0, 0x0000, /* 024d */ 0x01af, 0x0000, /* 024f */ 0x028a, 0x0000, /* 0251 */ 0x028b, 0x0000, /* 0253 */ 0x01b4, 0x0000, /* 0255 */ 0x01b3, 0x0000, /* 0257 */ 0x01b6, 0x0000, /* 0259 */ 0x01b5, 0x0000, /* 025b */ 0x0292, 0x0000, /* 025d */ 0x01b9, 0x0000, /* 025f */ 0x01b8, 0x0000, /* 0261 */ 0x01bd, 0x0000, /* 0263 */ 0x01bc, 0x0000, /* 0265 */ 0x01f7, 0x0000, /* 0267 */ 0x01c6, 0x0000, /* 0269 */ 0x01c5, 0x0000, /* 026b */ 0x01c6, 0x0000, /* 026d */ 0x01c5, 0x0000, /* 026f */ 0x01c4, 0x0000, /* 0271 */ 0x01c5, 0x0000, /* 0273 */ 0x01c4, 0x0000, /* 0275 */ 0x01c9, 0x0000, /* 0277 */ 0x01c8, 0x0000, /* 0279 */ 0x01c9, 0x0000, /* 027b */ 0x01c8, 0x0000, /* 027d */ 0x01c7, 0x0000, /* 027f */ 0x01c8, 0x0000, /* 0281 */ 0x01c7, 0x0000, /* 0283 */ 0x01cc, 0x0000, /* 0285 */ 0x01cb, 0x0000, /* 0287 */ 0x01cc, 0x0000, /* 0289 */ 0x01cb, 0x0000, /* 028b */ 0x01ca, 0x0000, /* 028d */ 0x01cb, 0x0000, /* 028f */ 0x01ca, 0x0000, /* 0291 */ 0x01ce, 0x0000, /* 0293 */ 0x01cd, 0x0000, /* 0295 */ 0x01d0, 0x0000, /* 0297 */ 0x01cf, 0x0000, /* 0299 */ 0x01d2, 0x0000, /* 029b */ 0x01d1, 0x0000, /* 029d */ 0x01d4, 0x0000, /* 029f */ 0x01d3, 0x0000, /* 02a1 */ 0x01d6, 0x0000, /* 02a3 */ 0x01d5, 0x0000, /* 02a5 */ 0x01d8, 0x0000, /* 02a7 */ 0x01d7, 0x0000, /* 02a9 */ 0x01da, 0x0000, /* 02ab */ 0x01d9, 0x0000, /* 02ad */ 0x01dc, 0x0000, /* 02af */ 0x01db, 0x0000, /* 02b1 */ 0x018e, 0x0000, /* 02b3 */ 0x01df, 0x0000, /* 02b5 */ 0x01de, 0x0000, /* 02b7 */ 0x01e1, 0x0000, /* 02b9 */ 0x01e0, 0x0000, /* 02bb */ 0x01e3, 0x0000, /* 02bd */ 0x01e2, 0x0000, /* 02bf */ 0x01e5, 0x0000, /* 02c1 */ 0x01e4, 0x0000, /* 02c3 */ 0x01e7, 0x0000, /* 02c5 */ 0x01e6, 0x0000, /* 02c7 */ 0x01e9, 0x0000, /* 02c9 */ 0x01e8, 0x0000, /* 02cb */ 0x01eb, 0x0000, /* 02cd */ 0x01ea, 0x0000, /* 02cf */ 0x01ed, 0x0000, /* 02d1 */ 0x01ec, 0x0000, /* 02d3 */ 0x01ef, 0x0000, /* 02d5 */ 0x01ee, 0x0000, /* 02d7 */ 0x004a, 0x030c, 0x0000, /* 02da */ 0x006a, 0x030c, 0x0000, /* 02dd */ 0x01f3, 0x0000, /* 02df */ 0x01f2, 0x0000, /* 02e1 */ 0x01f3, 0x0000, /* 02e3 */ 0x01f2, 0x0000, /* 02e5 */ 0x01f1, 0x0000, /* 02e7 */ 0x01f2, 0x0000, /* 02e9 */ 0x01f1, 0x0000, /* 02eb */ 0x01f5, 0x0000, /* 02ed */ 0x01f4, 0x0000, /* 02ef */ 0x0195, 0x0000, /* 02f1 */ 0x01bf, 0x0000, /* 02f3 */ 0x01f9, 0x0000, /* 02f5 */ 0x01f8, 0x0000, /* 02f7 */ 0x01fb, 0x0000, /* 02f9 */ 0x01fa, 0x0000, /* 02fb */ 0x01fd, 0x0000, /* 02fd */ 0x01fc, 0x0000, /* 02ff */ 0x01ff, 0x0000, /* 0301 */ 0x01fe, 0x0000, /* 0303 */ 0x0201, 0x0000, /* 0305 */ 0x0200, 0x0000, /* 0307 */ 0x0203, 0x0000, /* 0309 */ 0x0202, 0x0000, /* 030b */ 0x0205, 0x0000, /* 030d */ 0x0204, 0x0000, /* 030f */ 0x0207, 0x0000, /* 0311 */ 0x0206, 0x0000, /* 0313 */ 0x0209, 0x0000, /* 0315 */ 0x0208, 0x0000, /* 0317 */ 0x020b, 0x0000, /* 0319 */ 0x020a, 0x0000, /* 031b */ 0x020d, 0x0000, /* 031d */ 0x020c, 0x0000, /* 031f */ 0x020f, 0x0000, /* 0321 */ 0x020e, 0x0000, /* 0323 */ 0x0211, 0x0000, /* 0325 */ 0x0210, 0x0000, /* 0327 */ 0x0213, 0x0000, /* 0329 */ 0x0212, 0x0000, /* 032b */ 0x0215, 0x0000, /* 032d */ 0x0214, 0x0000, /* 032f */ 0x0217, 0x0000, /* 0331 */ 0x0216, 0x0000, /* 0333 */ 0x0219, 0x0000, /* 0335 */ 0x0218, 0x0000, /* 0337 */ 0x021b, 0x0000, /* 0339 */ 0x021a, 0x0000, /* 033b */ 0x021d, 0x0000, /* 033d */ 0x021c, 0x0000, /* 033f */ 0x021f, 0x0000, /* 0341 */ 0x021e, 0x0000, /* 0343 */ 0x019e, 0x0000, /* 0345 */ 0x0223, 0x0000, /* 0347 */ 0x0222, 0x0000, /* 0349 */ 0x0225, 0x0000, /* 034b */ 0x0224, 0x0000, /* 034d */ 0x0227, 0x0000, /* 034f */ 0x0226, 0x0000, /* 0351 */ 0x0229, 0x0000, /* 0353 */ 0x0228, 0x0000, /* 0355 */ 0x022b, 0x0000, /* 0357 */ 0x022a, 0x0000, /* 0359 */ 0x022d, 0x0000, /* 035b */ 0x022c, 0x0000, /* 035d */ 0x022f, 0x0000, /* 035f */ 0x022e, 0x0000, /* 0361 */ 0x0231, 0x0000, /* 0363 */ 0x0230, 0x0000, /* 0365 */ 0x0233, 0x0000, /* 0367 */ 0x0232, 0x0000, /* 0369 */ 0x2c65, 0x0000, /* 036b */ 0x023c, 0x0000, /* 036d */ 0x023b, 0x0000, /* 036f */ 0x019a, 0x0000, /* 0371 */ 0x2c66, 0x0000, /* 0373 */ 0x2c7e, 0x0000, /* 0375 */ 0x2c7f, 0x0000, /* 0377 */ 0x0242, 0x0000, /* 0379 */ 0x0241, 0x0000, /* 037b */ 0x0180, 0x0000, /* 037d */ 0x0289, 0x0000, /* 037f */ 0x028c, 0x0000, /* 0381 */ 0x0247, 0x0000, /* 0383 */ 0x0246, 0x0000, /* 0385 */ 0x0249, 0x0000, /* 0387 */ 0x0248, 0x0000, /* 0389 */ 0x024b, 0x0000, /* 038b */ 0x024a, 0x0000, /* 038d */ 0x024d, 0x0000, /* 038f */ 0x024c, 0x0000, /* 0391 */ 0x024f, 0x0000, /* 0393 */ 0x024e, 0x0000, /* 0395 */ 0x2c6f, 0x0000, /* 0397 */ 0x2c6d, 0x0000, /* 0399 */ 0x2c70, 0x0000, /* 039b */ 0x0181, 0x0000, /* 039d */ 0x0186, 0x0000, /* 039f */ 0x0189, 0x0000, /* 03a1 */ 0x018a, 0x0000, /* 03a3 */ 0x018f, 0x0000, /* 03a5 */ 0x0190, 0x0000, /* 03a7 */ 0x0193, 0x0000, /* 03a9 */ 0x0194, 0x0000, /* 03ab */ 0xa78d, 0x0000, /* 03ad */ 0xa7aa, 0x0000, /* 03af */ 0x0197, 0x0000, /* 03b1 */ 0x0196, 0x0000, /* 03b3 */ 0x2c62, 0x0000, /* 03b5 */ 0x019c, 0x0000, /* 03b7 */ 0x2c6e, 0x0000, /* 03b9 */ 0x019d, 0x0000, /* 03bb */ 0x019f, 0x0000, /* 03bd */ 0x2c64, 0x0000, /* 03bf */ 0x01a6, 0x0000, /* 03c1 */ 0x01a9, 0x0000, /* 03c3 */ 0x01ae, 0x0000, /* 03c5 */ 0x0244, 0x0000, /* 03c7 */ 0x01b1, 0x0000, /* 03c9 */ 0x01b2, 0x0000, /* 03cb */ 0x0245, 0x0000, /* 03cd */ 0x01b7, 0x0000, /* 03cf */ 0x0399, 0x0000, /* 03d1 */ 0x03b9, 0x0000, /* 03d3 */ 0x0371, 0x0000, /* 03d5 */ 0x0370, 0x0000, /* 03d7 */ 0x0373, 0x0000, /* 03d9 */ 0x0372, 0x0000, /* 03db */ 0x0377, 0x0000, /* 03dd */ 0x0376, 0x0000, /* 03df */ 0x03fd, 0x0000, /* 03e1 */ 0x03fe, 0x0000, /* 03e3 */ 0x03ff, 0x0000, /* 03e5 */ 0x03ac, 0x0000, /* 03e7 */ 0x03ad, 0x0000, /* 03e9 */ 0x03ae, 0x0000, /* 03eb */ 0x03af, 0x0000, /* 03ed */ 0x03cc, 0x0000, /* 03ef */ 0x03cd, 0x0000, /* 03f1 */ 0x03ce, 0x0000, /* 03f3 */ 0x0399, 0x0308, 0x0301, 0x0000, /* 03f7 */ 0x03b9, 0x0308, 0x0301, 0x0000, /* 03fb */ 0x03b1, 0x0000, /* 03fd */ 0x03b2, 0x0000, /* 03ff */ 0x03b3, 0x0000, /* 0401 */ 0x03b4, 0x0000, /* 0403 */ 0x03b5, 0x0000, /* 0405 */ 0x03b6, 0x0000, /* 0407 */ 0x03b7, 0x0000, /* 0409 */ 0x03b8, 0x0000, /* 040b */ 0x03b9, 0x0000, /* 040d */ 0x03ba, 0x0000, /* 040f */ 0x03bb, 0x0000, /* 0411 */ 0x03bc, 0x0000, /* 0413 */ 0x03bd, 0x0000, /* 0415 */ 0x03be, 0x0000, /* 0417 */ 0x03bf, 0x0000, /* 0419 */ 0x03c0, 0x0000, /* 041b */ 0x03c1, 0x0000, /* 041d */ 0x03c3, 0x0000, /* 041f */ 0x03c4, 0x0000, /* 0421 */ 0x03c5, 0x0000, /* 0423 */ 0x03c6, 0x0000, /* 0425 */ 0x03c7, 0x0000, /* 0427 */ 0x03c8, 0x0000, /* 0429 */ 0x03c9, 0x0000, /* 042b */ 0x03ca, 0x0000, /* 042d */ 0x03cb, 0x0000, /* 042f */ 0x0386, 0x0000, /* 0431 */ 0x0388, 0x0000, /* 0433 */ 0x0389, 0x0000, /* 0435 */ 0x038a, 0x0000, /* 0437 */ 0x03a5, 0x0308, 0x0301, 0x0000, /* 043b */ 0x03c5, 0x0308, 0x0301, 0x0000, /* 043f */ 0x0391, 0x0000, /* 0441 */ 0x0392, 0x0000, /* 0443 */ 0x0393, 0x0000, /* 0445 */ 0x0394, 0x0000, /* 0447 */ 0x0395, 0x0000, /* 0449 */ 0x0396, 0x0000, /* 044b */ 0x0397, 0x0000, /* 044d */ 0x0398, 0x0000, /* 044f */ 0x0399, 0x0000, /* 0451 */ 0x039a, 0x0000, /* 0453 */ 0x039b, 0x0000, /* 0455 */ 0x039c, 0x0000, /* 0457 */ 0x039d, 0x0000, /* 0459 */ 0x039e, 0x0000, /* 045b */ 0x039f, 0x0000, /* 045d */ 0x03a0, 0x0000, /* 045f */ 0x03a1, 0x0000, /* 0461 */ 0x03a3, 0x0000, /* 0463 */ 0x03c3, 0x0000, /* 0465 */ 0x03a3, 0x0000, /* 0467 */ 0x03a4, 0x0000, /* 0469 */ 0x03a5, 0x0000, /* 046b */ 0x03a6, 0x0000, /* 046d */ 0x03a7, 0x0000, /* 046f */ 0x03a8, 0x0000, /* 0471 */ 0x03a9, 0x0000, /* 0473 */ 0x03aa, 0x0000, /* 0475 */ 0x03ab, 0x0000, /* 0477 */ 0x038c, 0x0000, /* 0479 */ 0x038e, 0x0000, /* 047b */ 0x038f, 0x0000, /* 047d */ 0x03d7, 0x0000, /* 047f */ 0x0392, 0x0000, /* 0481 */ 0x03b2, 0x0000, /* 0483 */ 0x0398, 0x0000, /* 0485 */ 0x03b8, 0x0000, /* 0487 */ 0x03a6, 0x0000, /* 0489 */ 0x03c6, 0x0000, /* 048b */ 0x03a0, 0x0000, /* 048d */ 0x03c0, 0x0000, /* 048f */ 0x03cf, 0x0000, /* 0491 */ 0x03d9, 0x0000, /* 0493 */ 0x03d8, 0x0000, /* 0495 */ 0x03db, 0x0000, /* 0497 */ 0x03da, 0x0000, /* 0499 */ 0x03dd, 0x0000, /* 049b */ 0x03dc, 0x0000, /* 049d */ 0x03df, 0x0000, /* 049f */ 0x03de, 0x0000, /* 04a1 */ 0x03e1, 0x0000, /* 04a3 */ 0x03e0, 0x0000, /* 04a5 */ 0x03e3, 0x0000, /* 04a7 */ 0x03e2, 0x0000, /* 04a9 */ 0x03e5, 0x0000, /* 04ab */ 0x03e4, 0x0000, /* 04ad */ 0x03e7, 0x0000, /* 04af */ 0x03e6, 0x0000, /* 04b1 */ 0x03e9, 0x0000, /* 04b3 */ 0x03e8, 0x0000, /* 04b5 */ 0x03eb, 0x0000, /* 04b7 */ 0x03ea, 0x0000, /* 04b9 */ 0x03ed, 0x0000, /* 04bb */ 0x03ec, 0x0000, /* 04bd */ 0x03ef, 0x0000, /* 04bf */ 0x03ee, 0x0000, /* 04c1 */ 0x039a, 0x0000, /* 04c3 */ 0x03ba, 0x0000, /* 04c5 */ 0x03a1, 0x0000, /* 04c7 */ 0x03c1, 0x0000, /* 04c9 */ 0x03f9, 0x0000, /* 04cb */ 0x03b8, 0x0000, /* 04cd */ 0x0395, 0x0000, /* 04cf */ 0x03b5, 0x0000, /* 04d1 */ 0x03f8, 0x0000, /* 04d3 */ 0x03f7, 0x0000, /* 04d5 */ 0x03f2, 0x0000, /* 04d7 */ 0x03fb, 0x0000, /* 04d9 */ 0x03fa, 0x0000, /* 04db */ 0x037b, 0x0000, /* 04dd */ 0x037c, 0x0000, /* 04df */ 0x037d, 0x0000, /* 04e1 */ 0x0450, 0x0000, /* 04e3 */ 0x0451, 0x0000, /* 04e5 */ 0x0452, 0x0000, /* 04e7 */ 0x0453, 0x0000, /* 04e9 */ 0x0454, 0x0000, /* 04eb */ 0x0455, 0x0000, /* 04ed */ 0x0456, 0x0000, /* 04ef */ 0x0457, 0x0000, /* 04f1 */ 0x0458, 0x0000, /* 04f3 */ 0x0459, 0x0000, /* 04f5 */ 0x045a, 0x0000, /* 04f7 */ 0x045b, 0x0000, /* 04f9 */ 0x045c, 0x0000, /* 04fb */ 0x045d, 0x0000, /* 04fd */ 0x045e, 0x0000, /* 04ff */ 0x045f, 0x0000, /* 0501 */ 0x0430, 0x0000, /* 0503 */ 0x0431, 0x0000, /* 0505 */ 0x0432, 0x0000, /* 0507 */ 0x0433, 0x0000, /* 0509 */ 0x0434, 0x0000, /* 050b */ 0x0435, 0x0000, /* 050d */ 0x0436, 0x0000, /* 050f */ 0x0437, 0x0000, /* 0511 */ 0x0438, 0x0000, /* 0513 */ 0x0439, 0x0000, /* 0515 */ 0x043a, 0x0000, /* 0517 */ 0x043b, 0x0000, /* 0519 */ 0x043c, 0x0000, /* 051b */ 0x043d, 0x0000, /* 051d */ 0x043e, 0x0000, /* 051f */ 0x043f, 0x0000, /* 0521 */ 0x0440, 0x0000, /* 0523 */ 0x0441, 0x0000, /* 0525 */ 0x0442, 0x0000, /* 0527 */ 0x0443, 0x0000, /* 0529 */ 0x0444, 0x0000, /* 052b */ 0x0445, 0x0000, /* 052d */ 0x0446, 0x0000, /* 052f */ 0x0447, 0x0000, /* 0531 */ 0x0448, 0x0000, /* 0533 */ 0x0449, 0x0000, /* 0535 */ 0x044a, 0x0000, /* 0537 */ 0x044b, 0x0000, /* 0539 */ 0x044c, 0x0000, /* 053b */ 0x044d, 0x0000, /* 053d */ 0x044e, 0x0000, /* 053f */ 0x044f, 0x0000, /* 0541 */ 0x0410, 0x0000, /* 0543 */ 0x0411, 0x0000, /* 0545 */ 0x0412, 0x0000, /* 0547 */ 0x0413, 0x0000, /* 0549 */ 0x0414, 0x0000, /* 054b */ 0x0415, 0x0000, /* 054d */ 0x0416, 0x0000, /* 054f */ 0x0417, 0x0000, /* 0551 */ 0x0418, 0x0000, /* 0553 */ 0x0419, 0x0000, /* 0555 */ 0x041a, 0x0000, /* 0557 */ 0x041b, 0x0000, /* 0559 */ 0x041c, 0x0000, /* 055b */ 0x041d, 0x0000, /* 055d */ 0x041e, 0x0000, /* 055f */ 0x041f, 0x0000, /* 0561 */ 0x0420, 0x0000, /* 0563 */ 0x0421, 0x0000, /* 0565 */ 0x0422, 0x0000, /* 0567 */ 0x0423, 0x0000, /* 0569 */ 0x0424, 0x0000, /* 056b */ 0x0425, 0x0000, /* 056d */ 0x0426, 0x0000, /* 056f */ 0x0427, 0x0000, /* 0571 */ 0x0428, 0x0000, /* 0573 */ 0x0429, 0x0000, /* 0575 */ 0x042a, 0x0000, /* 0577 */ 0x042b, 0x0000, /* 0579 */ 0x042c, 0x0000, /* 057b */ 0x042d, 0x0000, /* 057d */ 0x042e, 0x0000, /* 057f */ 0x042f, 0x0000, /* 0581 */ 0x0400, 0x0000, /* 0583 */ 0x0401, 0x0000, /* 0585 */ 0x0402, 0x0000, /* 0587 */ 0x0403, 0x0000, /* 0589 */ 0x0404, 0x0000, /* 058b */ 0x0405, 0x0000, /* 058d */ 0x0406, 0x0000, /* 058f */ 0x0407, 0x0000, /* 0591 */ 0x0408, 0x0000, /* 0593 */ 0x0409, 0x0000, /* 0595 */ 0x040a, 0x0000, /* 0597 */ 0x040b, 0x0000, /* 0599 */ 0x040c, 0x0000, /* 059b */ 0x040d, 0x0000, /* 059d */ 0x040e, 0x0000, /* 059f */ 0x040f, 0x0000, /* 05a1 */ 0x0461, 0x0000, /* 05a3 */ 0x0460, 0x0000, /* 05a5 */ 0x0463, 0x0000, /* 05a7 */ 0x0462, 0x0000, /* 05a9 */ 0x0465, 0x0000, /* 05ab */ 0x0464, 0x0000, /* 05ad */ 0x0467, 0x0000, /* 05af */ 0x0466, 0x0000, /* 05b1 */ 0x0469, 0x0000, /* 05b3 */ 0x0468, 0x0000, /* 05b5 */ 0x046b, 0x0000, /* 05b7 */ 0x046a, 0x0000, /* 05b9 */ 0x046d, 0x0000, /* 05bb */ 0x046c, 0x0000, /* 05bd */ 0x046f, 0x0000, /* 05bf */ 0x046e, 0x0000, /* 05c1 */ 0x0471, 0x0000, /* 05c3 */ 0x0470, 0x0000, /* 05c5 */ 0x0473, 0x0000, /* 05c7 */ 0x0472, 0x0000, /* 05c9 */ 0x0475, 0x0000, /* 05cb */ 0x0474, 0x0000, /* 05cd */ 0x0477, 0x0000, /* 05cf */ 0x0476, 0x0000, /* 05d1 */ 0x0479, 0x0000, /* 05d3 */ 0x0478, 0x0000, /* 05d5 */ 0x047b, 0x0000, /* 05d7 */ 0x047a, 0x0000, /* 05d9 */ 0x047d, 0x0000, /* 05db */ 0x047c, 0x0000, /* 05dd */ 0x047f, 0x0000, /* 05df */ 0x047e, 0x0000, /* 05e1 */ 0x0481, 0x0000, /* 05e3 */ 0x0480, 0x0000, /* 05e5 */ 0x048b, 0x0000, /* 05e7 */ 0x048a, 0x0000, /* 05e9 */ 0x048d, 0x0000, /* 05eb */ 0x048c, 0x0000, /* 05ed */ 0x048f, 0x0000, /* 05ef */ 0x048e, 0x0000, /* 05f1 */ 0x0491, 0x0000, /* 05f3 */ 0x0490, 0x0000, /* 05f5 */ 0x0493, 0x0000, /* 05f7 */ 0x0492, 0x0000, /* 05f9 */ 0x0495, 0x0000, /* 05fb */ 0x0494, 0x0000, /* 05fd */ 0x0497, 0x0000, /* 05ff */ 0x0496, 0x0000, /* 0601 */ 0x0499, 0x0000, /* 0603 */ 0x0498, 0x0000, /* 0605 */ 0x049b, 0x0000, /* 0607 */ 0x049a, 0x0000, /* 0609 */ 0x049d, 0x0000, /* 060b */ 0x049c, 0x0000, /* 060d */ 0x049f, 0x0000, /* 060f */ 0x049e, 0x0000, /* 0611 */ 0x04a1, 0x0000, /* 0613 */ 0x04a0, 0x0000, /* 0615 */ 0x04a3, 0x0000, /* 0617 */ 0x04a2, 0x0000, /* 0619 */ 0x04a5, 0x0000, /* 061b */ 0x04a4, 0x0000, /* 061d */ 0x04a7, 0x0000, /* 061f */ 0x04a6, 0x0000, /* 0621 */ 0x04a9, 0x0000, /* 0623 */ 0x04a8, 0x0000, /* 0625 */ 0x04ab, 0x0000, /* 0627 */ 0x04aa, 0x0000, /* 0629 */ 0x04ad, 0x0000, /* 062b */ 0x04ac, 0x0000, /* 062d */ 0x04af, 0x0000, /* 062f */ 0x04ae, 0x0000, /* 0631 */ 0x04b1, 0x0000, /* 0633 */ 0x04b0, 0x0000, /* 0635 */ 0x04b3, 0x0000, /* 0637 */ 0x04b2, 0x0000, /* 0639 */ 0x04b5, 0x0000, /* 063b */ 0x04b4, 0x0000, /* 063d */ 0x04b7, 0x0000, /* 063f */ 0x04b6, 0x0000, /* 0641 */ 0x04b9, 0x0000, /* 0643 */ 0x04b8, 0x0000, /* 0645 */ 0x04bb, 0x0000, /* 0647 */ 0x04ba, 0x0000, /* 0649 */ 0x04bd, 0x0000, /* 064b */ 0x04bc, 0x0000, /* 064d */ 0x04bf, 0x0000, /* 064f */ 0x04be, 0x0000, /* 0651 */ 0x04cf, 0x0000, /* 0653 */ 0x04c2, 0x0000, /* 0655 */ 0x04c1, 0x0000, /* 0657 */ 0x04c4, 0x0000, /* 0659 */ 0x04c3, 0x0000, /* 065b */ 0x04c6, 0x0000, /* 065d */ 0x04c5, 0x0000, /* 065f */ 0x04c8, 0x0000, /* 0661 */ 0x04c7, 0x0000, /* 0663 */ 0x04ca, 0x0000, /* 0665 */ 0x04c9, 0x0000, /* 0667 */ 0x04cc, 0x0000, /* 0669 */ 0x04cb, 0x0000, /* 066b */ 0x04ce, 0x0000, /* 066d */ 0x04cd, 0x0000, /* 066f */ 0x04c0, 0x0000, /* 0671 */ 0x04d1, 0x0000, /* 0673 */ 0x04d0, 0x0000, /* 0675 */ 0x04d3, 0x0000, /* 0677 */ 0x04d2, 0x0000, /* 0679 */ 0x04d5, 0x0000, /* 067b */ 0x04d4, 0x0000, /* 067d */ 0x04d7, 0x0000, /* 067f */ 0x04d6, 0x0000, /* 0681 */ 0x04d9, 0x0000, /* 0683 */ 0x04d8, 0x0000, /* 0685 */ 0x04db, 0x0000, /* 0687 */ 0x04da, 0x0000, /* 0689 */ 0x04dd, 0x0000, /* 068b */ 0x04dc, 0x0000, /* 068d */ 0x04df, 0x0000, /* 068f */ 0x04de, 0x0000, /* 0691 */ 0x04e1, 0x0000, /* 0693 */ 0x04e0, 0x0000, /* 0695 */ 0x04e3, 0x0000, /* 0697 */ 0x04e2, 0x0000, /* 0699 */ 0x04e5, 0x0000, /* 069b */ 0x04e4, 0x0000, /* 069d */ 0x04e7, 0x0000, /* 069f */ 0x04e6, 0x0000, /* 06a1 */ 0x04e9, 0x0000, /* 06a3 */ 0x04e8, 0x0000, /* 06a5 */ 0x04eb, 0x0000, /* 06a7 */ 0x04ea, 0x0000, /* 06a9 */ 0x04ed, 0x0000, /* 06ab */ 0x04ec, 0x0000, /* 06ad */ 0x04ef, 0x0000, /* 06af */ 0x04ee, 0x0000, /* 06b1 */ 0x04f1, 0x0000, /* 06b3 */ 0x04f0, 0x0000, /* 06b5 */ 0x04f3, 0x0000, /* 06b7 */ 0x04f2, 0x0000, /* 06b9 */ 0x04f5, 0x0000, /* 06bb */ 0x04f4, 0x0000, /* 06bd */ 0x04f7, 0x0000, /* 06bf */ 0x04f6, 0x0000, /* 06c1 */ 0x04f9, 0x0000, /* 06c3 */ 0x04f8, 0x0000, /* 06c5 */ 0x04fb, 0x0000, /* 06c7 */ 0x04fa, 0x0000, /* 06c9 */ 0x04fd, 0x0000, /* 06cb */ 0x04fc, 0x0000, /* 06cd */ 0x04ff, 0x0000, /* 06cf */ 0x04fe, 0x0000, /* 06d1 */ 0x0501, 0x0000, /* 06d3 */ 0x0500, 0x0000, /* 06d5 */ 0x0503, 0x0000, /* 06d7 */ 0x0502, 0x0000, /* 06d9 */ 0x0505, 0x0000, /* 06db */ 0x0504, 0x0000, /* 06dd */ 0x0507, 0x0000, /* 06df */ 0x0506, 0x0000, /* 06e1 */ 0x0509, 0x0000, /* 06e3 */ 0x0508, 0x0000, /* 06e5 */ 0x050b, 0x0000, /* 06e7 */ 0x050a, 0x0000, /* 06e9 */ 0x050d, 0x0000, /* 06eb */ 0x050c, 0x0000, /* 06ed */ 0x050f, 0x0000, /* 06ef */ 0x050e, 0x0000, /* 06f1 */ 0x0511, 0x0000, /* 06f3 */ 0x0510, 0x0000, /* 06f5 */ 0x0513, 0x0000, /* 06f7 */ 0x0512, 0x0000, /* 06f9 */ 0x0515, 0x0000, /* 06fb */ 0x0514, 0x0000, /* 06fd */ 0x0517, 0x0000, /* 06ff */ 0x0516, 0x0000, /* 0701 */ 0x0519, 0x0000, /* 0703 */ 0x0518, 0x0000, /* 0705 */ 0x051b, 0x0000, /* 0707 */ 0x051a, 0x0000, /* 0709 */ 0x051d, 0x0000, /* 070b */ 0x051c, 0x0000, /* 070d */ 0x051f, 0x0000, /* 070f */ 0x051e, 0x0000, /* 0711 */ 0x0521, 0x0000, /* 0713 */ 0x0520, 0x0000, /* 0715 */ 0x0523, 0x0000, /* 0717 */ 0x0522, 0x0000, /* 0719 */ 0x0525, 0x0000, /* 071b */ 0x0524, 0x0000, /* 071d */ 0x0527, 0x0000, /* 071f */ 0x0526, 0x0000, /* 0721 */ 0x0561, 0x0000, /* 0723 */ 0x0562, 0x0000, /* 0725 */ 0x0563, 0x0000, /* 0727 */ 0x0564, 0x0000, /* 0729 */ 0x0565, 0x0000, /* 072b */ 0x0566, 0x0000, /* 072d */ 0x0567, 0x0000, /* 072f */ 0x0568, 0x0000, /* 0731 */ 0x0569, 0x0000, /* 0733 */ 0x056a, 0x0000, /* 0735 */ 0x056b, 0x0000, /* 0737 */ 0x056c, 0x0000, /* 0739 */ 0x056d, 0x0000, /* 073b */ 0x056e, 0x0000, /* 073d */ 0x056f, 0x0000, /* 073f */ 0x0570, 0x0000, /* 0741 */ 0x0571, 0x0000, /* 0743 */ 0x0572, 0x0000, /* 0745 */ 0x0573, 0x0000, /* 0747 */ 0x0574, 0x0000, /* 0749 */ 0x0575, 0x0000, /* 074b */ 0x0576, 0x0000, /* 074d */ 0x0577, 0x0000, /* 074f */ 0x0578, 0x0000, /* 0751 */ 0x0579, 0x0000, /* 0753 */ 0x057a, 0x0000, /* 0755 */ 0x057b, 0x0000, /* 0757 */ 0x057c, 0x0000, /* 0759 */ 0x057d, 0x0000, /* 075b */ 0x057e, 0x0000, /* 075d */ 0x057f, 0x0000, /* 075f */ 0x0580, 0x0000, /* 0761 */ 0x0581, 0x0000, /* 0763 */ 0x0582, 0x0000, /* 0765 */ 0x0583, 0x0000, /* 0767 */ 0x0584, 0x0000, /* 0769 */ 0x0585, 0x0000, /* 076b */ 0x0586, 0x0000, /* 076d */ 0x0531, 0x0000, /* 076f */ 0x0532, 0x0000, /* 0771 */ 0x0533, 0x0000, /* 0773 */ 0x0534, 0x0000, /* 0775 */ 0x0535, 0x0000, /* 0777 */ 0x0536, 0x0000, /* 0779 */ 0x0537, 0x0000, /* 077b */ 0x0538, 0x0000, /* 077d */ 0x0539, 0x0000, /* 077f */ 0x053a, 0x0000, /* 0781 */ 0x053b, 0x0000, /* 0783 */ 0x053c, 0x0000, /* 0785 */ 0x053d, 0x0000, /* 0787 */ 0x053e, 0x0000, /* 0789 */ 0x053f, 0x0000, /* 078b */ 0x0540, 0x0000, /* 078d */ 0x0541, 0x0000, /* 078f */ 0x0542, 0x0000, /* 0791 */ 0x0543, 0x0000, /* 0793 */ 0x0544, 0x0000, /* 0795 */ 0x0545, 0x0000, /* 0797 */ 0x0546, 0x0000, /* 0799 */ 0x0547, 0x0000, /* 079b */ 0x0548, 0x0000, /* 079d */ 0x0549, 0x0000, /* 079f */ 0x054a, 0x0000, /* 07a1 */ 0x054b, 0x0000, /* 07a3 */ 0x054c, 0x0000, /* 07a5 */ 0x054d, 0x0000, /* 07a7 */ 0x054e, 0x0000, /* 07a9 */ 0x054f, 0x0000, /* 07ab */ 0x0550, 0x0000, /* 07ad */ 0x0551, 0x0000, /* 07af */ 0x0552, 0x0000, /* 07b1 */ 0x0553, 0x0000, /* 07b3 */ 0x0554, 0x0000, /* 07b5 */ 0x0555, 0x0000, /* 07b7 */ 0x0556, 0x0000, /* 07b9 */ 0x0535, 0x0582, 0x0000, /* 07bc */ 0x0535, 0x0552, 0x0000, /* 07bf */ 0x0565, 0x0582, 0x0000, /* 07c2 */ 0x2d00, 0x0000, /* 07c4 */ 0x2d01, 0x0000, /* 07c6 */ 0x2d02, 0x0000, /* 07c8 */ 0x2d03, 0x0000, /* 07ca */ 0x2d04, 0x0000, /* 07cc */ 0x2d05, 0x0000, /* 07ce */ 0x2d06, 0x0000, /* 07d0 */ 0x2d07, 0x0000, /* 07d2 */ 0x2d08, 0x0000, /* 07d4 */ 0x2d09, 0x0000, /* 07d6 */ 0x2d0a, 0x0000, /* 07d8 */ 0x2d0b, 0x0000, /* 07da */ 0x2d0c, 0x0000, /* 07dc */ 0x2d0d, 0x0000, /* 07de */ 0x2d0e, 0x0000, /* 07e0 */ 0x2d0f, 0x0000, /* 07e2 */ 0x2d10, 0x0000, /* 07e4 */ 0x2d11, 0x0000, /* 07e6 */ 0x2d12, 0x0000, /* 07e8 */ 0x2d13, 0x0000, /* 07ea */ 0x2d14, 0x0000, /* 07ec */ 0x2d15, 0x0000, /* 07ee */ 0x2d16, 0x0000, /* 07f0 */ 0x2d17, 0x0000, /* 07f2 */ 0x2d18, 0x0000, /* 07f4 */ 0x2d19, 0x0000, /* 07f6 */ 0x2d1a, 0x0000, /* 07f8 */ 0x2d1b, 0x0000, /* 07fa */ 0x2d1c, 0x0000, /* 07fc */ 0x2d1d, 0x0000, /* 07fe */ 0x2d1e, 0x0000, /* 0800 */ 0x2d1f, 0x0000, /* 0802 */ 0x2d20, 0x0000, /* 0804 */ 0x2d21, 0x0000, /* 0806 */ 0x2d22, 0x0000, /* 0808 */ 0x2d23, 0x0000, /* 080a */ 0x2d24, 0x0000, /* 080c */ 0x2d25, 0x0000, /* 080e */ 0x2d27, 0x0000, /* 0810 */ 0x2d2d, 0x0000, /* 0812 */ 0xa77d, 0x0000, /* 0814 */ 0x2c63, 0x0000, /* 0816 */ 0x1e01, 0x0000, /* 0818 */ 0x1e00, 0x0000, /* 081a */ 0x1e03, 0x0000, /* 081c */ 0x1e02, 0x0000, /* 081e */ 0x1e05, 0x0000, /* 0820 */ 0x1e04, 0x0000, /* 0822 */ 0x1e07, 0x0000, /* 0824 */ 0x1e06, 0x0000, /* 0826 */ 0x1e09, 0x0000, /* 0828 */ 0x1e08, 0x0000, /* 082a */ 0x1e0b, 0x0000, /* 082c */ 0x1e0a, 0x0000, /* 082e */ 0x1e0d, 0x0000, /* 0830 */ 0x1e0c, 0x0000, /* 0832 */ 0x1e0f, 0x0000, /* 0834 */ 0x1e0e, 0x0000, /* 0836 */ 0x1e11, 0x0000, /* 0838 */ 0x1e10, 0x0000, /* 083a */ 0x1e13, 0x0000, /* 083c */ 0x1e12, 0x0000, /* 083e */ 0x1e15, 0x0000, /* 0840 */ 0x1e14, 0x0000, /* 0842 */ 0x1e17, 0x0000, /* 0844 */ 0x1e16, 0x0000, /* 0846 */ 0x1e19, 0x0000, /* 0848 */ 0x1e18, 0x0000, /* 084a */ 0x1e1b, 0x0000, /* 084c */ 0x1e1a, 0x0000, /* 084e */ 0x1e1d, 0x0000, /* 0850 */ 0x1e1c, 0x0000, /* 0852 */ 0x1e1f, 0x0000, /* 0854 */ 0x1e1e, 0x0000, /* 0856 */ 0x1e21, 0x0000, /* 0858 */ 0x1e20, 0x0000, /* 085a */ 0x1e23, 0x0000, /* 085c */ 0x1e22, 0x0000, /* 085e */ 0x1e25, 0x0000, /* 0860 */ 0x1e24, 0x0000, /* 0862 */ 0x1e27, 0x0000, /* 0864 */ 0x1e26, 0x0000, /* 0866 */ 0x1e29, 0x0000, /* 0868 */ 0x1e28, 0x0000, /* 086a */ 0x1e2b, 0x0000, /* 086c */ 0x1e2a, 0x0000, /* 086e */ 0x1e2d, 0x0000, /* 0870 */ 0x1e2c, 0x0000, /* 0872 */ 0x1e2f, 0x0000, /* 0874 */ 0x1e2e, 0x0000, /* 0876 */ 0x1e31, 0x0000, /* 0878 */ 0x1e30, 0x0000, /* 087a */ 0x1e33, 0x0000, /* 087c */ 0x1e32, 0x0000, /* 087e */ 0x1e35, 0x0000, /* 0880 */ 0x1e34, 0x0000, /* 0882 */ 0x1e37, 0x0000, /* 0884 */ 0x1e36, 0x0000, /* 0886 */ 0x1e39, 0x0000, /* 0888 */ 0x1e38, 0x0000, /* 088a */ 0x1e3b, 0x0000, /* 088c */ 0x1e3a, 0x0000, /* 088e */ 0x1e3d, 0x0000, /* 0890 */ 0x1e3c, 0x0000, /* 0892 */ 0x1e3f, 0x0000, /* 0894 */ 0x1e3e, 0x0000, /* 0896 */ 0x1e41, 0x0000, /* 0898 */ 0x1e40, 0x0000, /* 089a */ 0x1e43, 0x0000, /* 089c */ 0x1e42, 0x0000, /* 089e */ 0x1e45, 0x0000, /* 08a0 */ 0x1e44, 0x0000, /* 08a2 */ 0x1e47, 0x0000, /* 08a4 */ 0x1e46, 0x0000, /* 08a6 */ 0x1e49, 0x0000, /* 08a8 */ 0x1e48, 0x0000, /* 08aa */ 0x1e4b, 0x0000, /* 08ac */ 0x1e4a, 0x0000, /* 08ae */ 0x1e4d, 0x0000, /* 08b0 */ 0x1e4c, 0x0000, /* 08b2 */ 0x1e4f, 0x0000, /* 08b4 */ 0x1e4e, 0x0000, /* 08b6 */ 0x1e51, 0x0000, /* 08b8 */ 0x1e50, 0x0000, /* 08ba */ 0x1e53, 0x0000, /* 08bc */ 0x1e52, 0x0000, /* 08be */ 0x1e55, 0x0000, /* 08c0 */ 0x1e54, 0x0000, /* 08c2 */ 0x1e57, 0x0000, /* 08c4 */ 0x1e56, 0x0000, /* 08c6 */ 0x1e59, 0x0000, /* 08c8 */ 0x1e58, 0x0000, /* 08ca */ 0x1e5b, 0x0000, /* 08cc */ 0x1e5a, 0x0000, /* 08ce */ 0x1e5d, 0x0000, /* 08d0 */ 0x1e5c, 0x0000, /* 08d2 */ 0x1e5f, 0x0000, /* 08d4 */ 0x1e5e, 0x0000, /* 08d6 */ 0x1e61, 0x0000, /* 08d8 */ 0x1e60, 0x0000, /* 08da */ 0x1e63, 0x0000, /* 08dc */ 0x1e62, 0x0000, /* 08de */ 0x1e65, 0x0000, /* 08e0 */ 0x1e64, 0x0000, /* 08e2 */ 0x1e67, 0x0000, /* 08e4 */ 0x1e66, 0x0000, /* 08e6 */ 0x1e69, 0x0000, /* 08e8 */ 0x1e68, 0x0000, /* 08ea */ 0x1e6b, 0x0000, /* 08ec */ 0x1e6a, 0x0000, /* 08ee */ 0x1e6d, 0x0000, /* 08f0 */ 0x1e6c, 0x0000, /* 08f2 */ 0x1e6f, 0x0000, /* 08f4 */ 0x1e6e, 0x0000, /* 08f6 */ 0x1e71, 0x0000, /* 08f8 */ 0x1e70, 0x0000, /* 08fa */ 0x1e73, 0x0000, /* 08fc */ 0x1e72, 0x0000, /* 08fe */ 0x1e75, 0x0000, /* 0900 */ 0x1e74, 0x0000, /* 0902 */ 0x1e77, 0x0000, /* 0904 */ 0x1e76, 0x0000, /* 0906 */ 0x1e79, 0x0000, /* 0908 */ 0x1e78, 0x0000, /* 090a */ 0x1e7b, 0x0000, /* 090c */ 0x1e7a, 0x0000, /* 090e */ 0x1e7d, 0x0000, /* 0910 */ 0x1e7c, 0x0000, /* 0912 */ 0x1e7f, 0x0000, /* 0914 */ 0x1e7e, 0x0000, /* 0916 */ 0x1e81, 0x0000, /* 0918 */ 0x1e80, 0x0000, /* 091a */ 0x1e83, 0x0000, /* 091c */ 0x1e82, 0x0000, /* 091e */ 0x1e85, 0x0000, /* 0920 */ 0x1e84, 0x0000, /* 0922 */ 0x1e87, 0x0000, /* 0924 */ 0x1e86, 0x0000, /* 0926 */ 0x1e89, 0x0000, /* 0928 */ 0x1e88, 0x0000, /* 092a */ 0x1e8b, 0x0000, /* 092c */ 0x1e8a, 0x0000, /* 092e */ 0x1e8d, 0x0000, /* 0930 */ 0x1e8c, 0x0000, /* 0932 */ 0x1e8f, 0x0000, /* 0934 */ 0x1e8e, 0x0000, /* 0936 */ 0x1e91, 0x0000, /* 0938 */ 0x1e90, 0x0000, /* 093a */ 0x1e93, 0x0000, /* 093c */ 0x1e92, 0x0000, /* 093e */ 0x1e95, 0x0000, /* 0940 */ 0x1e94, 0x0000, /* 0942 */ 0x0048, 0x0331, 0x0000, /* 0945 */ 0x0068, 0x0331, 0x0000, /* 0948 */ 0x0054, 0x0308, 0x0000, /* 094b */ 0x0074, 0x0308, 0x0000, /* 094e */ 0x0057, 0x030a, 0x0000, /* 0951 */ 0x0077, 0x030a, 0x0000, /* 0954 */ 0x0059, 0x030a, 0x0000, /* 0957 */ 0x0079, 0x030a, 0x0000, /* 095a */ 0x0041, 0x02be, 0x0000, /* 095d */ 0x0061, 0x02be, 0x0000, /* 0960 */ 0x1e60, 0x0000, /* 0962 */ 0x1e61, 0x0000, /* 0964 */ 0x00df, 0x0000, /* 0966 */ 0x0073, 0x0073, 0x0000, /* 0969 */ 0x1ea1, 0x0000, /* 096b */ 0x1ea0, 0x0000, /* 096d */ 0x1ea3, 0x0000, /* 096f */ 0x1ea2, 0x0000, /* 0971 */ 0x1ea5, 0x0000, /* 0973 */ 0x1ea4, 0x0000, /* 0975 */ 0x1ea7, 0x0000, /* 0977 */ 0x1ea6, 0x0000, /* 0979 */ 0x1ea9, 0x0000, /* 097b */ 0x1ea8, 0x0000, /* 097d */ 0x1eab, 0x0000, /* 097f */ 0x1eaa, 0x0000, /* 0981 */ 0x1ead, 0x0000, /* 0983 */ 0x1eac, 0x0000, /* 0985 */ 0x1eaf, 0x0000, /* 0987 */ 0x1eae, 0x0000, /* 0989 */ 0x1eb1, 0x0000, /* 098b */ 0x1eb0, 0x0000, /* 098d */ 0x1eb3, 0x0000, /* 098f */ 0x1eb2, 0x0000, /* 0991 */ 0x1eb5, 0x0000, /* 0993 */ 0x1eb4, 0x0000, /* 0995 */ 0x1eb7, 0x0000, /* 0997 */ 0x1eb6, 0x0000, /* 0999 */ 0x1eb9, 0x0000, /* 099b */ 0x1eb8, 0x0000, /* 099d */ 0x1ebb, 0x0000, /* 099f */ 0x1eba, 0x0000, /* 09a1 */ 0x1ebd, 0x0000, /* 09a3 */ 0x1ebc, 0x0000, /* 09a5 */ 0x1ebf, 0x0000, /* 09a7 */ 0x1ebe, 0x0000, /* 09a9 */ 0x1ec1, 0x0000, /* 09ab */ 0x1ec0, 0x0000, /* 09ad */ 0x1ec3, 0x0000, /* 09af */ 0x1ec2, 0x0000, /* 09b1 */ 0x1ec5, 0x0000, /* 09b3 */ 0x1ec4, 0x0000, /* 09b5 */ 0x1ec7, 0x0000, /* 09b7 */ 0x1ec6, 0x0000, /* 09b9 */ 0x1ec9, 0x0000, /* 09bb */ 0x1ec8, 0x0000, /* 09bd */ 0x1ecb, 0x0000, /* 09bf */ 0x1eca, 0x0000, /* 09c1 */ 0x1ecd, 0x0000, /* 09c3 */ 0x1ecc, 0x0000, /* 09c5 */ 0x1ecf, 0x0000, /* 09c7 */ 0x1ece, 0x0000, /* 09c9 */ 0x1ed1, 0x0000, /* 09cb */ 0x1ed0, 0x0000, /* 09cd */ 0x1ed3, 0x0000, /* 09cf */ 0x1ed2, 0x0000, /* 09d1 */ 0x1ed5, 0x0000, /* 09d3 */ 0x1ed4, 0x0000, /* 09d5 */ 0x1ed7, 0x0000, /* 09d7 */ 0x1ed6, 0x0000, /* 09d9 */ 0x1ed9, 0x0000, /* 09db */ 0x1ed8, 0x0000, /* 09dd */ 0x1edb, 0x0000, /* 09df */ 0x1eda, 0x0000, /* 09e1 */ 0x1edd, 0x0000, /* 09e3 */ 0x1edc, 0x0000, /* 09e5 */ 0x1edf, 0x0000, /* 09e7 */ 0x1ede, 0x0000, /* 09e9 */ 0x1ee1, 0x0000, /* 09eb */ 0x1ee0, 0x0000, /* 09ed */ 0x1ee3, 0x0000, /* 09ef */ 0x1ee2, 0x0000, /* 09f1 */ 0x1ee5, 0x0000, /* 09f3 */ 0x1ee4, 0x0000, /* 09f5 */ 0x1ee7, 0x0000, /* 09f7 */ 0x1ee6, 0x0000, /* 09f9 */ 0x1ee9, 0x0000, /* 09fb */ 0x1ee8, 0x0000, /* 09fd */ 0x1eeb, 0x0000, /* 09ff */ 0x1eea, 0x0000, /* 0a01 */ 0x1eed, 0x0000, /* 0a03 */ 0x1eec, 0x0000, /* 0a05 */ 0x1eef, 0x0000, /* 0a07 */ 0x1eee, 0x0000, /* 0a09 */ 0x1ef1, 0x0000, /* 0a0b */ 0x1ef0, 0x0000, /* 0a0d */ 0x1ef3, 0x0000, /* 0a0f */ 0x1ef2, 0x0000, /* 0a11 */ 0x1ef5, 0x0000, /* 0a13 */ 0x1ef4, 0x0000, /* 0a15 */ 0x1ef7, 0x0000, /* 0a17 */ 0x1ef6, 0x0000, /* 0a19 */ 0x1ef9, 0x0000, /* 0a1b */ 0x1ef8, 0x0000, /* 0a1d */ 0x1efb, 0x0000, /* 0a1f */ 0x1efa, 0x0000, /* 0a21 */ 0x1efd, 0x0000, /* 0a23 */ 0x1efc, 0x0000, /* 0a25 */ 0x1eff, 0x0000, /* 0a27 */ 0x1efe, 0x0000, /* 0a29 */ 0x1f08, 0x0000, /* 0a2b */ 0x1f09, 0x0000, /* 0a2d */ 0x1f0a, 0x0000, /* 0a2f */ 0x1f0b, 0x0000, /* 0a31 */ 0x1f0c, 0x0000, /* 0a33 */ 0x1f0d, 0x0000, /* 0a35 */ 0x1f0e, 0x0000, /* 0a37 */ 0x1f0f, 0x0000, /* 0a39 */ 0x1f00, 0x0000, /* 0a3b */ 0x1f01, 0x0000, /* 0a3d */ 0x1f02, 0x0000, /* 0a3f */ 0x1f03, 0x0000, /* 0a41 */ 0x1f04, 0x0000, /* 0a43 */ 0x1f05, 0x0000, /* 0a45 */ 0x1f06, 0x0000, /* 0a47 */ 0x1f07, 0x0000, /* 0a49 */ 0x1f18, 0x0000, /* 0a4b */ 0x1f19, 0x0000, /* 0a4d */ 0x1f1a, 0x0000, /* 0a4f */ 0x1f1b, 0x0000, /* 0a51 */ 0x1f1c, 0x0000, /* 0a53 */ 0x1f1d, 0x0000, /* 0a55 */ 0x1f10, 0x0000, /* 0a57 */ 0x1f11, 0x0000, /* 0a59 */ 0x1f12, 0x0000, /* 0a5b */ 0x1f13, 0x0000, /* 0a5d */ 0x1f14, 0x0000, /* 0a5f */ 0x1f15, 0x0000, /* 0a61 */ 0x1f28, 0x0000, /* 0a63 */ 0x1f29, 0x0000, /* 0a65 */ 0x1f2a, 0x0000, /* 0a67 */ 0x1f2b, 0x0000, /* 0a69 */ 0x1f2c, 0x0000, /* 0a6b */ 0x1f2d, 0x0000, /* 0a6d */ 0x1f2e, 0x0000, /* 0a6f */ 0x1f2f, 0x0000, /* 0a71 */ 0x1f20, 0x0000, /* 0a73 */ 0x1f21, 0x0000, /* 0a75 */ 0x1f22, 0x0000, /* 0a77 */ 0x1f23, 0x0000, /* 0a79 */ 0x1f24, 0x0000, /* 0a7b */ 0x1f25, 0x0000, /* 0a7d */ 0x1f26, 0x0000, /* 0a7f */ 0x1f27, 0x0000, /* 0a81 */ 0x1f38, 0x0000, /* 0a83 */ 0x1f39, 0x0000, /* 0a85 */ 0x1f3a, 0x0000, /* 0a87 */ 0x1f3b, 0x0000, /* 0a89 */ 0x1f3c, 0x0000, /* 0a8b */ 0x1f3d, 0x0000, /* 0a8d */ 0x1f3e, 0x0000, /* 0a8f */ 0x1f3f, 0x0000, /* 0a91 */ 0x1f30, 0x0000, /* 0a93 */ 0x1f31, 0x0000, /* 0a95 */ 0x1f32, 0x0000, /* 0a97 */ 0x1f33, 0x0000, /* 0a99 */ 0x1f34, 0x0000, /* 0a9b */ 0x1f35, 0x0000, /* 0a9d */ 0x1f36, 0x0000, /* 0a9f */ 0x1f37, 0x0000, /* 0aa1 */ 0x1f48, 0x0000, /* 0aa3 */ 0x1f49, 0x0000, /* 0aa5 */ 0x1f4a, 0x0000, /* 0aa7 */ 0x1f4b, 0x0000, /* 0aa9 */ 0x1f4c, 0x0000, /* 0aab */ 0x1f4d, 0x0000, /* 0aad */ 0x1f40, 0x0000, /* 0aaf */ 0x1f41, 0x0000, /* 0ab1 */ 0x1f42, 0x0000, /* 0ab3 */ 0x1f43, 0x0000, /* 0ab5 */ 0x1f44, 0x0000, /* 0ab7 */ 0x1f45, 0x0000, /* 0ab9 */ 0x03a5, 0x0313, 0x0000, /* 0abc */ 0x03c5, 0x0313, 0x0000, /* 0abf */ 0x1f59, 0x0000, /* 0ac1 */ 0x03a5, 0x0313, 0x0300, 0x0000, /* 0ac5 */ 0x03c5, 0x0313, 0x0300, 0x0000, /* 0ac9 */ 0x1f5b, 0x0000, /* 0acb */ 0x03a5, 0x0313, 0x0301, 0x0000, /* 0acf */ 0x03c5, 0x0313, 0x0301, 0x0000, /* 0ad3 */ 0x1f5d, 0x0000, /* 0ad5 */ 0x03a5, 0x0313, 0x0342, 0x0000, /* 0ad9 */ 0x03c5, 0x0313, 0x0342, 0x0000, /* 0add */ 0x1f5f, 0x0000, /* 0adf */ 0x1f51, 0x0000, /* 0ae1 */ 0x1f53, 0x0000, /* 0ae3 */ 0x1f55, 0x0000, /* 0ae5 */ 0x1f57, 0x0000, /* 0ae7 */ 0x1f68, 0x0000, /* 0ae9 */ 0x1f69, 0x0000, /* 0aeb */ 0x1f6a, 0x0000, /* 0aed */ 0x1f6b, 0x0000, /* 0aef */ 0x1f6c, 0x0000, /* 0af1 */ 0x1f6d, 0x0000, /* 0af3 */ 0x1f6e, 0x0000, /* 0af5 */ 0x1f6f, 0x0000, /* 0af7 */ 0x1f60, 0x0000, /* 0af9 */ 0x1f61, 0x0000, /* 0afb */ 0x1f62, 0x0000, /* 0afd */ 0x1f63, 0x0000, /* 0aff */ 0x1f64, 0x0000, /* 0b01 */ 0x1f65, 0x0000, /* 0b03 */ 0x1f66, 0x0000, /* 0b05 */ 0x1f67, 0x0000, /* 0b07 */ 0x1fba, 0x0000, /* 0b09 */ 0x1fbb, 0x0000, /* 0b0b */ 0x1fc8, 0x0000, /* 0b0d */ 0x1fc9, 0x0000, /* 0b0f */ 0x1fca, 0x0000, /* 0b11 */ 0x1fcb, 0x0000, /* 0b13 */ 0x1fda, 0x0000, /* 0b15 */ 0x1fdb, 0x0000, /* 0b17 */ 0x1ff8, 0x0000, /* 0b19 */ 0x1ff9, 0x0000, /* 0b1b */ 0x1fea, 0x0000, /* 0b1d */ 0x1feb, 0x0000, /* 0b1f */ 0x1ffa, 0x0000, /* 0b21 */ 0x1ffb, 0x0000, /* 0b23 */ 0x1f88, 0x0000, /* 0b25 */ 0x1f08, 0x0399, 0x0000, /* 0b28 */ 0x1f00, 0x03b9, 0x0000, /* 0b2b */ 0x1f89, 0x0000, /* 0b2d */ 0x1f09, 0x0399, 0x0000, /* 0b30 */ 0x1f01, 0x03b9, 0x0000, /* 0b33 */ 0x1f8a, 0x0000, /* 0b35 */ 0x1f0a, 0x0399, 0x0000, /* 0b38 */ 0x1f02, 0x03b9, 0x0000, /* 0b3b */ 0x1f8b, 0x0000, /* 0b3d */ 0x1f0b, 0x0399, 0x0000, /* 0b40 */ 0x1f03, 0x03b9, 0x0000, /* 0b43 */ 0x1f8c, 0x0000, /* 0b45 */ 0x1f0c, 0x0399, 0x0000, /* 0b48 */ 0x1f04, 0x03b9, 0x0000, /* 0b4b */ 0x1f8d, 0x0000, /* 0b4d */ 0x1f0d, 0x0399, 0x0000, /* 0b50 */ 0x1f05, 0x03b9, 0x0000, /* 0b53 */ 0x1f8e, 0x0000, /* 0b55 */ 0x1f0e, 0x0399, 0x0000, /* 0b58 */ 0x1f06, 0x03b9, 0x0000, /* 0b5b */ 0x1f8f, 0x0000, /* 0b5d */ 0x1f0f, 0x0399, 0x0000, /* 0b60 */ 0x1f07, 0x03b9, 0x0000, /* 0b63 */ 0x1f80, 0x0000, /* 0b65 */ 0x1f08, 0x0399, 0x0000, /* 0b68 */ 0x1f00, 0x03b9, 0x0000, /* 0b6b */ 0x1f81, 0x0000, /* 0b6d */ 0x1f09, 0x0399, 0x0000, /* 0b70 */ 0x1f01, 0x03b9, 0x0000, /* 0b73 */ 0x1f82, 0x0000, /* 0b75 */ 0x1f0a, 0x0399, 0x0000, /* 0b78 */ 0x1f02, 0x03b9, 0x0000, /* 0b7b */ 0x1f83, 0x0000, /* 0b7d */ 0x1f0b, 0x0399, 0x0000, /* 0b80 */ 0x1f03, 0x03b9, 0x0000, /* 0b83 */ 0x1f84, 0x0000, /* 0b85 */ 0x1f0c, 0x0399, 0x0000, /* 0b88 */ 0x1f04, 0x03b9, 0x0000, /* 0b8b */ 0x1f85, 0x0000, /* 0b8d */ 0x1f0d, 0x0399, 0x0000, /* 0b90 */ 0x1f05, 0x03b9, 0x0000, /* 0b93 */ 0x1f86, 0x0000, /* 0b95 */ 0x1f0e, 0x0399, 0x0000, /* 0b98 */ 0x1f06, 0x03b9, 0x0000, /* 0b9b */ 0x1f87, 0x0000, /* 0b9d */ 0x1f0f, 0x0399, 0x0000, /* 0ba0 */ 0x1f07, 0x03b9, 0x0000, /* 0ba3 */ 0x1f98, 0x0000, /* 0ba5 */ 0x1f28, 0x0399, 0x0000, /* 0ba8 */ 0x1f20, 0x03b9, 0x0000, /* 0bab */ 0x1f99, 0x0000, /* 0bad */ 0x1f29, 0x0399, 0x0000, /* 0bb0 */ 0x1f21, 0x03b9, 0x0000, /* 0bb3 */ 0x1f9a, 0x0000, /* 0bb5 */ 0x1f2a, 0x0399, 0x0000, /* 0bb8 */ 0x1f22, 0x03b9, 0x0000, /* 0bbb */ 0x1f9b, 0x0000, /* 0bbd */ 0x1f2b, 0x0399, 0x0000, /* 0bc0 */ 0x1f23, 0x03b9, 0x0000, /* 0bc3 */ 0x1f9c, 0x0000, /* 0bc5 */ 0x1f2c, 0x0399, 0x0000, /* 0bc8 */ 0x1f24, 0x03b9, 0x0000, /* 0bcb */ 0x1f9d, 0x0000, /* 0bcd */ 0x1f2d, 0x0399, 0x0000, /* 0bd0 */ 0x1f25, 0x03b9, 0x0000, /* 0bd3 */ 0x1f9e, 0x0000, /* 0bd5 */ 0x1f2e, 0x0399, 0x0000, /* 0bd8 */ 0x1f26, 0x03b9, 0x0000, /* 0bdb */ 0x1f9f, 0x0000, /* 0bdd */ 0x1f2f, 0x0399, 0x0000, /* 0be0 */ 0x1f27, 0x03b9, 0x0000, /* 0be3 */ 0x1f90, 0x0000, /* 0be5 */ 0x1f28, 0x0399, 0x0000, /* 0be8 */ 0x1f20, 0x03b9, 0x0000, /* 0beb */ 0x1f91, 0x0000, /* 0bed */ 0x1f29, 0x0399, 0x0000, /* 0bf0 */ 0x1f21, 0x03b9, 0x0000, /* 0bf3 */ 0x1f92, 0x0000, /* 0bf5 */ 0x1f2a, 0x0399, 0x0000, /* 0bf8 */ 0x1f22, 0x03b9, 0x0000, /* 0bfb */ 0x1f93, 0x0000, /* 0bfd */ 0x1f2b, 0x0399, 0x0000, /* 0c00 */ 0x1f23, 0x03b9, 0x0000, /* 0c03 */ 0x1f94, 0x0000, /* 0c05 */ 0x1f2c, 0x0399, 0x0000, /* 0c08 */ 0x1f24, 0x03b9, 0x0000, /* 0c0b */ 0x1f95, 0x0000, /* 0c0d */ 0x1f2d, 0x0399, 0x0000, /* 0c10 */ 0x1f25, 0x03b9, 0x0000, /* 0c13 */ 0x1f96, 0x0000, /* 0c15 */ 0x1f2e, 0x0399, 0x0000, /* 0c18 */ 0x1f26, 0x03b9, 0x0000, /* 0c1b */ 0x1f97, 0x0000, /* 0c1d */ 0x1f2f, 0x0399, 0x0000, /* 0c20 */ 0x1f27, 0x03b9, 0x0000, /* 0c23 */ 0x1fa8, 0x0000, /* 0c25 */ 0x1f68, 0x0399, 0x0000, /* 0c28 */ 0x1f60, 0x03b9, 0x0000, /* 0c2b */ 0x1fa9, 0x0000, /* 0c2d */ 0x1f69, 0x0399, 0x0000, /* 0c30 */ 0x1f61, 0x03b9, 0x0000, /* 0c33 */ 0x1faa, 0x0000, /* 0c35 */ 0x1f6a, 0x0399, 0x0000, /* 0c38 */ 0x1f62, 0x03b9, 0x0000, /* 0c3b */ 0x1fab, 0x0000, /* 0c3d */ 0x1f6b, 0x0399, 0x0000, /* 0c40 */ 0x1f63, 0x03b9, 0x0000, /* 0c43 */ 0x1fac, 0x0000, /* 0c45 */ 0x1f6c, 0x0399, 0x0000, /* 0c48 */ 0x1f64, 0x03b9, 0x0000, /* 0c4b */ 0x1fad, 0x0000, /* 0c4d */ 0x1f6d, 0x0399, 0x0000, /* 0c50 */ 0x1f65, 0x03b9, 0x0000, /* 0c53 */ 0x1fae, 0x0000, /* 0c55 */ 0x1f6e, 0x0399, 0x0000, /* 0c58 */ 0x1f66, 0x03b9, 0x0000, /* 0c5b */ 0x1faf, 0x0000, /* 0c5d */ 0x1f6f, 0x0399, 0x0000, /* 0c60 */ 0x1f67, 0x03b9, 0x0000, /* 0c63 */ 0x1fa0, 0x0000, /* 0c65 */ 0x1f68, 0x0399, 0x0000, /* 0c68 */ 0x1f60, 0x03b9, 0x0000, /* 0c6b */ 0x1fa1, 0x0000, /* 0c6d */ 0x1f69, 0x0399, 0x0000, /* 0c70 */ 0x1f61, 0x03b9, 0x0000, /* 0c73 */ 0x1fa2, 0x0000, /* 0c75 */ 0x1f6a, 0x0399, 0x0000, /* 0c78 */ 0x1f62, 0x03b9, 0x0000, /* 0c7b */ 0x1fa3, 0x0000, /* 0c7d */ 0x1f6b, 0x0399, 0x0000, /* 0c80 */ 0x1f63, 0x03b9, 0x0000, /* 0c83 */ 0x1fa4, 0x0000, /* 0c85 */ 0x1f6c, 0x0399, 0x0000, /* 0c88 */ 0x1f64, 0x03b9, 0x0000, /* 0c8b */ 0x1fa5, 0x0000, /* 0c8d */ 0x1f6d, 0x0399, 0x0000, /* 0c90 */ 0x1f65, 0x03b9, 0x0000, /* 0c93 */ 0x1fa6, 0x0000, /* 0c95 */ 0x1f6e, 0x0399, 0x0000, /* 0c98 */ 0x1f66, 0x03b9, 0x0000, /* 0c9b */ 0x1fa7, 0x0000, /* 0c9d */ 0x1f6f, 0x0399, 0x0000, /* 0ca0 */ 0x1f67, 0x03b9, 0x0000, /* 0ca3 */ 0x1fb8, 0x0000, /* 0ca5 */ 0x1fb9, 0x0000, /* 0ca7 */ 0x1fba, 0x0345, 0x0000, /* 0caa */ 0x1fba, 0x0399, 0x0000, /* 0cad */ 0x1f70, 0x03b9, 0x0000, /* 0cb0 */ 0x1fbc, 0x0000, /* 0cb2 */ 0x0391, 0x0399, 0x0000, /* 0cb5 */ 0x03b1, 0x03b9, 0x0000, /* 0cb8 */ 0x0386, 0x0345, 0x0000, /* 0cbb */ 0x0386, 0x0399, 0x0000, /* 0cbe */ 0x03ac, 0x03b9, 0x0000, /* 0cc1 */ 0x0391, 0x0342, 0x0000, /* 0cc4 */ 0x03b1, 0x0342, 0x0000, /* 0cc7 */ 0x0391, 0x0342, 0x0345, 0x0000, /* 0ccb */ 0x0391, 0x0342, 0x0399, 0x0000, /* 0ccf */ 0x03b1, 0x0342, 0x03b9, 0x0000, /* 0cd3 */ 0x1fb0, 0x0000, /* 0cd5 */ 0x1fb1, 0x0000, /* 0cd7 */ 0x1f70, 0x0000, /* 0cd9 */ 0x1f71, 0x0000, /* 0cdb */ 0x1fb3, 0x0000, /* 0cdd */ 0x0391, 0x0399, 0x0000, /* 0ce0 */ 0x03b1, 0x03b9, 0x0000, /* 0ce3 */ 0x0399, 0x0000, /* 0ce5 */ 0x03b9, 0x0000, /* 0ce7 */ 0x1fca, 0x0345, 0x0000, /* 0cea */ 0x1fca, 0x0399, 0x0000, /* 0ced */ 0x1f74, 0x03b9, 0x0000, /* 0cf0 */ 0x1fcc, 0x0000, /* 0cf2 */ 0x0397, 0x0399, 0x0000, /* 0cf5 */ 0x03b7, 0x03b9, 0x0000, /* 0cf8 */ 0x0389, 0x0345, 0x0000, /* 0cfb */ 0x0389, 0x0399, 0x0000, /* 0cfe */ 0x03ae, 0x03b9, 0x0000, /* 0d01 */ 0x0397, 0x0342, 0x0000, /* 0d04 */ 0x03b7, 0x0342, 0x0000, /* 0d07 */ 0x0397, 0x0342, 0x0345, 0x0000, /* 0d0b */ 0x0397, 0x0342, 0x0399, 0x0000, /* 0d0f */ 0x03b7, 0x0342, 0x03b9, 0x0000, /* 0d13 */ 0x1f72, 0x0000, /* 0d15 */ 0x1f73, 0x0000, /* 0d17 */ 0x1f74, 0x0000, /* 0d19 */ 0x1f75, 0x0000, /* 0d1b */ 0x1fc3, 0x0000, /* 0d1d */ 0x0397, 0x0399, 0x0000, /* 0d20 */ 0x03b7, 0x03b9, 0x0000, /* 0d23 */ 0x1fd8, 0x0000, /* 0d25 */ 0x1fd9, 0x0000, /* 0d27 */ 0x0399, 0x0308, 0x0300, 0x0000, /* 0d2b */ 0x03b9, 0x0308, 0x0300, 0x0000, /* 0d2f */ 0x0399, 0x0308, 0x0301, 0x0000, /* 0d33 */ 0x03b9, 0x0308, 0x0301, 0x0000, /* 0d37 */ 0x0399, 0x0342, 0x0000, /* 0d3a */ 0x03b9, 0x0342, 0x0000, /* 0d3d */ 0x0399, 0x0308, 0x0342, 0x0000, /* 0d41 */ 0x03b9, 0x0308, 0x0342, 0x0000, /* 0d45 */ 0x1fd0, 0x0000, /* 0d47 */ 0x1fd1, 0x0000, /* 0d49 */ 0x1f76, 0x0000, /* 0d4b */ 0x1f77, 0x0000, /* 0d4d */ 0x1fe8, 0x0000, /* 0d4f */ 0x1fe9, 0x0000, /* 0d51 */ 0x03a5, 0x0308, 0x0300, 0x0000, /* 0d55 */ 0x03c5, 0x0308, 0x0300, 0x0000, /* 0d59 */ 0x03a5, 0x0308, 0x0301, 0x0000, /* 0d5d */ 0x03c5, 0x0308, 0x0301, 0x0000, /* 0d61 */ 0x03a1, 0x0313, 0x0000, /* 0d64 */ 0x03c1, 0x0313, 0x0000, /* 0d67 */ 0x1fec, 0x0000, /* 0d69 */ 0x03a5, 0x0342, 0x0000, /* 0d6c */ 0x03c5, 0x0342, 0x0000, /* 0d6f */ 0x03a5, 0x0308, 0x0342, 0x0000, /* 0d73 */ 0x03c5, 0x0308, 0x0342, 0x0000, /* 0d77 */ 0x1fe0, 0x0000, /* 0d79 */ 0x1fe1, 0x0000, /* 0d7b */ 0x1f7a, 0x0000, /* 0d7d */ 0x1f7b, 0x0000, /* 0d7f */ 0x1fe5, 0x0000, /* 0d81 */ 0x1ffa, 0x0345, 0x0000, /* 0d84 */ 0x1ffa, 0x0399, 0x0000, /* 0d87 */ 0x1f7c, 0x03b9, 0x0000, /* 0d8a */ 0x1ffc, 0x0000, /* 0d8c */ 0x03a9, 0x0399, 0x0000, /* 0d8f */ 0x03c9, 0x03b9, 0x0000, /* 0d92 */ 0x038f, 0x0345, 0x0000, /* 0d95 */ 0x038f, 0x0399, 0x0000, /* 0d98 */ 0x03ce, 0x03b9, 0x0000, /* 0d9b */ 0x03a9, 0x0342, 0x0000, /* 0d9e */ 0x03c9, 0x0342, 0x0000, /* 0da1 */ 0x03a9, 0x0342, 0x0345, 0x0000, /* 0da5 */ 0x03a9, 0x0342, 0x0399, 0x0000, /* 0da9 */ 0x03c9, 0x0342, 0x03b9, 0x0000, /* 0dad */ 0x1f78, 0x0000, /* 0daf */ 0x1f79, 0x0000, /* 0db1 */ 0x1f7c, 0x0000, /* 0db3 */ 0x1f7d, 0x0000, /* 0db5 */ 0x1ff3, 0x0000, /* 0db7 */ 0x03a9, 0x0399, 0x0000, /* 0dba */ 0x03c9, 0x03b9, 0x0000, /* 0dbd */ 0x03c9, 0x0000, /* 0dbf */ 0x006b, 0x0000, /* 0dc1 */ 0x00e5, 0x0000, /* 0dc3 */ 0x214e, 0x0000, /* 0dc5 */ 0x2132, 0x0000, /* 0dc7 */ 0x2170, 0x0000, /* 0dc9 */ 0x2171, 0x0000, /* 0dcb */ 0x2172, 0x0000, /* 0dcd */ 0x2173, 0x0000, /* 0dcf */ 0x2174, 0x0000, /* 0dd1 */ 0x2175, 0x0000, /* 0dd3 */ 0x2176, 0x0000, /* 0dd5 */ 0x2177, 0x0000, /* 0dd7 */ 0x2178, 0x0000, /* 0dd9 */ 0x2179, 0x0000, /* 0ddb */ 0x217a, 0x0000, /* 0ddd */ 0x217b, 0x0000, /* 0ddf */ 0x217c, 0x0000, /* 0de1 */ 0x217d, 0x0000, /* 0de3 */ 0x217e, 0x0000, /* 0de5 */ 0x217f, 0x0000, /* 0de7 */ 0x2160, 0x0000, /* 0de9 */ 0x2161, 0x0000, /* 0deb */ 0x2162, 0x0000, /* 0ded */ 0x2163, 0x0000, /* 0def */ 0x2164, 0x0000, /* 0df1 */ 0x2165, 0x0000, /* 0df3 */ 0x2166, 0x0000, /* 0df5 */ 0x2167, 0x0000, /* 0df7 */ 0x2168, 0x0000, /* 0df9 */ 0x2169, 0x0000, /* 0dfb */ 0x216a, 0x0000, /* 0dfd */ 0x216b, 0x0000, /* 0dff */ 0x216c, 0x0000, /* 0e01 */ 0x216d, 0x0000, /* 0e03 */ 0x216e, 0x0000, /* 0e05 */ 0x216f, 0x0000, /* 0e07 */ 0x2184, 0x0000, /* 0e09 */ 0x2183, 0x0000, /* 0e0b */ 0x24d0, 0x0000, /* 0e0d */ 0x24d1, 0x0000, /* 0e0f */ 0x24d2, 0x0000, /* 0e11 */ 0x24d3, 0x0000, /* 0e13 */ 0x24d4, 0x0000, /* 0e15 */ 0x24d5, 0x0000, /* 0e17 */ 0x24d6, 0x0000, /* 0e19 */ 0x24d7, 0x0000, /* 0e1b */ 0x24d8, 0x0000, /* 0e1d */ 0x24d9, 0x0000, /* 0e1f */ 0x24da, 0x0000, /* 0e21 */ 0x24db, 0x0000, /* 0e23 */ 0x24dc, 0x0000, /* 0e25 */ 0x24dd, 0x0000, /* 0e27 */ 0x24de, 0x0000, /* 0e29 */ 0x24df, 0x0000, /* 0e2b */ 0x24e0, 0x0000, /* 0e2d */ 0x24e1, 0x0000, /* 0e2f */ 0x24e2, 0x0000, /* 0e31 */ 0x24e3, 0x0000, /* 0e33 */ 0x24e4, 0x0000, /* 0e35 */ 0x24e5, 0x0000, /* 0e37 */ 0x24e6, 0x0000, /* 0e39 */ 0x24e7, 0x0000, /* 0e3b */ 0x24e8, 0x0000, /* 0e3d */ 0x24e9, 0x0000, /* 0e3f */ 0x24b6, 0x0000, /* 0e41 */ 0x24b7, 0x0000, /* 0e43 */ 0x24b8, 0x0000, /* 0e45 */ 0x24b9, 0x0000, /* 0e47 */ 0x24ba, 0x0000, /* 0e49 */ 0x24bb, 0x0000, /* 0e4b */ 0x24bc, 0x0000, /* 0e4d */ 0x24bd, 0x0000, /* 0e4f */ 0x24be, 0x0000, /* 0e51 */ 0x24bf, 0x0000, /* 0e53 */ 0x24c0, 0x0000, /* 0e55 */ 0x24c1, 0x0000, /* 0e57 */ 0x24c2, 0x0000, /* 0e59 */ 0x24c3, 0x0000, /* 0e5b */ 0x24c4, 0x0000, /* 0e5d */ 0x24c5, 0x0000, /* 0e5f */ 0x24c6, 0x0000, /* 0e61 */ 0x24c7, 0x0000, /* 0e63 */ 0x24c8, 0x0000, /* 0e65 */ 0x24c9, 0x0000, /* 0e67 */ 0x24ca, 0x0000, /* 0e69 */ 0x24cb, 0x0000, /* 0e6b */ 0x24cc, 0x0000, /* 0e6d */ 0x24cd, 0x0000, /* 0e6f */ 0x24ce, 0x0000, /* 0e71 */ 0x24cf, 0x0000, /* 0e73 */ 0x2c30, 0x0000, /* 0e75 */ 0x2c31, 0x0000, /* 0e77 */ 0x2c32, 0x0000, /* 0e79 */ 0x2c33, 0x0000, /* 0e7b */ 0x2c34, 0x0000, /* 0e7d */ 0x2c35, 0x0000, /* 0e7f */ 0x2c36, 0x0000, /* 0e81 */ 0x2c37, 0x0000, /* 0e83 */ 0x2c38, 0x0000, /* 0e85 */ 0x2c39, 0x0000, /* 0e87 */ 0x2c3a, 0x0000, /* 0e89 */ 0x2c3b, 0x0000, /* 0e8b */ 0x2c3c, 0x0000, /* 0e8d */ 0x2c3d, 0x0000, /* 0e8f */ 0x2c3e, 0x0000, /* 0e91 */ 0x2c3f, 0x0000, /* 0e93 */ 0x2c40, 0x0000, /* 0e95 */ 0x2c41, 0x0000, /* 0e97 */ 0x2c42, 0x0000, /* 0e99 */ 0x2c43, 0x0000, /* 0e9b */ 0x2c44, 0x0000, /* 0e9d */ 0x2c45, 0x0000, /* 0e9f */ 0x2c46, 0x0000, /* 0ea1 */ 0x2c47, 0x0000, /* 0ea3 */ 0x2c48, 0x0000, /* 0ea5 */ 0x2c49, 0x0000, /* 0ea7 */ 0x2c4a, 0x0000, /* 0ea9 */ 0x2c4b, 0x0000, /* 0eab */ 0x2c4c, 0x0000, /* 0ead */ 0x2c4d, 0x0000, /* 0eaf */ 0x2c4e, 0x0000, /* 0eb1 */ 0x2c4f, 0x0000, /* 0eb3 */ 0x2c50, 0x0000, /* 0eb5 */ 0x2c51, 0x0000, /* 0eb7 */ 0x2c52, 0x0000, /* 0eb9 */ 0x2c53, 0x0000, /* 0ebb */ 0x2c54, 0x0000, /* 0ebd */ 0x2c55, 0x0000, /* 0ebf */ 0x2c56, 0x0000, /* 0ec1 */ 0x2c57, 0x0000, /* 0ec3 */ 0x2c58, 0x0000, /* 0ec5 */ 0x2c59, 0x0000, /* 0ec7 */ 0x2c5a, 0x0000, /* 0ec9 */ 0x2c5b, 0x0000, /* 0ecb */ 0x2c5c, 0x0000, /* 0ecd */ 0x2c5d, 0x0000, /* 0ecf */ 0x2c5e, 0x0000, /* 0ed1 */ 0x2c00, 0x0000, /* 0ed3 */ 0x2c01, 0x0000, /* 0ed5 */ 0x2c02, 0x0000, /* 0ed7 */ 0x2c03, 0x0000, /* 0ed9 */ 0x2c04, 0x0000, /* 0edb */ 0x2c05, 0x0000, /* 0edd */ 0x2c06, 0x0000, /* 0edf */ 0x2c07, 0x0000, /* 0ee1 */ 0x2c08, 0x0000, /* 0ee3 */ 0x2c09, 0x0000, /* 0ee5 */ 0x2c0a, 0x0000, /* 0ee7 */ 0x2c0b, 0x0000, /* 0ee9 */ 0x2c0c, 0x0000, /* 0eeb */ 0x2c0d, 0x0000, /* 0eed */ 0x2c0e, 0x0000, /* 0eef */ 0x2c0f, 0x0000, /* 0ef1 */ 0x2c10, 0x0000, /* 0ef3 */ 0x2c11, 0x0000, /* 0ef5 */ 0x2c12, 0x0000, /* 0ef7 */ 0x2c13, 0x0000, /* 0ef9 */ 0x2c14, 0x0000, /* 0efb */ 0x2c15, 0x0000, /* 0efd */ 0x2c16, 0x0000, /* 0eff */ 0x2c17, 0x0000, /* 0f01 */ 0x2c18, 0x0000, /* 0f03 */ 0x2c19, 0x0000, /* 0f05 */ 0x2c1a, 0x0000, /* 0f07 */ 0x2c1b, 0x0000, /* 0f09 */ 0x2c1c, 0x0000, /* 0f0b */ 0x2c1d, 0x0000, /* 0f0d */ 0x2c1e, 0x0000, /* 0f0f */ 0x2c1f, 0x0000, /* 0f11 */ 0x2c20, 0x0000, /* 0f13 */ 0x2c21, 0x0000, /* 0f15 */ 0x2c22, 0x0000, /* 0f17 */ 0x2c23, 0x0000, /* 0f19 */ 0x2c24, 0x0000, /* 0f1b */ 0x2c25, 0x0000, /* 0f1d */ 0x2c26, 0x0000, /* 0f1f */ 0x2c27, 0x0000, /* 0f21 */ 0x2c28, 0x0000, /* 0f23 */ 0x2c29, 0x0000, /* 0f25 */ 0x2c2a, 0x0000, /* 0f27 */ 0x2c2b, 0x0000, /* 0f29 */ 0x2c2c, 0x0000, /* 0f2b */ 0x2c2d, 0x0000, /* 0f2d */ 0x2c2e, 0x0000, /* 0f2f */ 0x2c61, 0x0000, /* 0f31 */ 0x2c60, 0x0000, /* 0f33 */ 0x026b, 0x0000, /* 0f35 */ 0x1d7d, 0x0000, /* 0f37 */ 0x027d, 0x0000, /* 0f39 */ 0x023a, 0x0000, /* 0f3b */ 0x023e, 0x0000, /* 0f3d */ 0x2c68, 0x0000, /* 0f3f */ 0x2c67, 0x0000, /* 0f41 */ 0x2c6a, 0x0000, /* 0f43 */ 0x2c69, 0x0000, /* 0f45 */ 0x2c6c, 0x0000, /* 0f47 */ 0x2c6b, 0x0000, /* 0f49 */ 0x0251, 0x0000, /* 0f4b */ 0x0271, 0x0000, /* 0f4d */ 0x0250, 0x0000, /* 0f4f */ 0x0252, 0x0000, /* 0f51 */ 0x2c73, 0x0000, /* 0f53 */ 0x2c72, 0x0000, /* 0f55 */ 0x2c76, 0x0000, /* 0f57 */ 0x2c75, 0x0000, /* 0f59 */ 0x023f, 0x0000, /* 0f5b */ 0x0240, 0x0000, /* 0f5d */ 0x2c81, 0x0000, /* 0f5f */ 0x2c80, 0x0000, /* 0f61 */ 0x2c83, 0x0000, /* 0f63 */ 0x2c82, 0x0000, /* 0f65 */ 0x2c85, 0x0000, /* 0f67 */ 0x2c84, 0x0000, /* 0f69 */ 0x2c87, 0x0000, /* 0f6b */ 0x2c86, 0x0000, /* 0f6d */ 0x2c89, 0x0000, /* 0f6f */ 0x2c88, 0x0000, /* 0f71 */ 0x2c8b, 0x0000, /* 0f73 */ 0x2c8a, 0x0000, /* 0f75 */ 0x2c8d, 0x0000, /* 0f77 */ 0x2c8c, 0x0000, /* 0f79 */ 0x2c8f, 0x0000, /* 0f7b */ 0x2c8e, 0x0000, /* 0f7d */ 0x2c91, 0x0000, /* 0f7f */ 0x2c90, 0x0000, /* 0f81 */ 0x2c93, 0x0000, /* 0f83 */ 0x2c92, 0x0000, /* 0f85 */ 0x2c95, 0x0000, /* 0f87 */ 0x2c94, 0x0000, /* 0f89 */ 0x2c97, 0x0000, /* 0f8b */ 0x2c96, 0x0000, /* 0f8d */ 0x2c99, 0x0000, /* 0f8f */ 0x2c98, 0x0000, /* 0f91 */ 0x2c9b, 0x0000, /* 0f93 */ 0x2c9a, 0x0000, /* 0f95 */ 0x2c9d, 0x0000, /* 0f97 */ 0x2c9c, 0x0000, /* 0f99 */ 0x2c9f, 0x0000, /* 0f9b */ 0x2c9e, 0x0000, /* 0f9d */ 0x2ca1, 0x0000, /* 0f9f */ 0x2ca0, 0x0000, /* 0fa1 */ 0x2ca3, 0x0000, /* 0fa3 */ 0x2ca2, 0x0000, /* 0fa5 */ 0x2ca5, 0x0000, /* 0fa7 */ 0x2ca4, 0x0000, /* 0fa9 */ 0x2ca7, 0x0000, /* 0fab */ 0x2ca6, 0x0000, /* 0fad */ 0x2ca9, 0x0000, /* 0faf */ 0x2ca8, 0x0000, /* 0fb1 */ 0x2cab, 0x0000, /* 0fb3 */ 0x2caa, 0x0000, /* 0fb5 */ 0x2cad, 0x0000, /* 0fb7 */ 0x2cac, 0x0000, /* 0fb9 */ 0x2caf, 0x0000, /* 0fbb */ 0x2cae, 0x0000, /* 0fbd */ 0x2cb1, 0x0000, /* 0fbf */ 0x2cb0, 0x0000, /* 0fc1 */ 0x2cb3, 0x0000, /* 0fc3 */ 0x2cb2, 0x0000, /* 0fc5 */ 0x2cb5, 0x0000, /* 0fc7 */ 0x2cb4, 0x0000, /* 0fc9 */ 0x2cb7, 0x0000, /* 0fcb */ 0x2cb6, 0x0000, /* 0fcd */ 0x2cb9, 0x0000, /* 0fcf */ 0x2cb8, 0x0000, /* 0fd1 */ 0x2cbb, 0x0000, /* 0fd3 */ 0x2cba, 0x0000, /* 0fd5 */ 0x2cbd, 0x0000, /* 0fd7 */ 0x2cbc, 0x0000, /* 0fd9 */ 0x2cbf, 0x0000, /* 0fdb */ 0x2cbe, 0x0000, /* 0fdd */ 0x2cc1, 0x0000, /* 0fdf */ 0x2cc0, 0x0000, /* 0fe1 */ 0x2cc3, 0x0000, /* 0fe3 */ 0x2cc2, 0x0000, /* 0fe5 */ 0x2cc5, 0x0000, /* 0fe7 */ 0x2cc4, 0x0000, /* 0fe9 */ 0x2cc7, 0x0000, /* 0feb */ 0x2cc6, 0x0000, /* 0fed */ 0x2cc9, 0x0000, /* 0fef */ 0x2cc8, 0x0000, /* 0ff1 */ 0x2ccb, 0x0000, /* 0ff3 */ 0x2cca, 0x0000, /* 0ff5 */ 0x2ccd, 0x0000, /* 0ff7 */ 0x2ccc, 0x0000, /* 0ff9 */ 0x2ccf, 0x0000, /* 0ffb */ 0x2cce, 0x0000, /* 0ffd */ 0x2cd1, 0x0000, /* 0fff */ 0x2cd0, 0x0000, /* 1001 */ 0x2cd3, 0x0000, /* 1003 */ 0x2cd2, 0x0000, /* 1005 */ 0x2cd5, 0x0000, /* 1007 */ 0x2cd4, 0x0000, /* 1009 */ 0x2cd7, 0x0000, /* 100b */ 0x2cd6, 0x0000, /* 100d */ 0x2cd9, 0x0000, /* 100f */ 0x2cd8, 0x0000, /* 1011 */ 0x2cdb, 0x0000, /* 1013 */ 0x2cda, 0x0000, /* 1015 */ 0x2cdd, 0x0000, /* 1017 */ 0x2cdc, 0x0000, /* 1019 */ 0x2cdf, 0x0000, /* 101b */ 0x2cde, 0x0000, /* 101d */ 0x2ce1, 0x0000, /* 101f */ 0x2ce0, 0x0000, /* 1021 */ 0x2ce3, 0x0000, /* 1023 */ 0x2ce2, 0x0000, /* 1025 */ 0x2cec, 0x0000, /* 1027 */ 0x2ceb, 0x0000, /* 1029 */ 0x2cee, 0x0000, /* 102b */ 0x2ced, 0x0000, /* 102d */ 0x2cf3, 0x0000, /* 102f */ 0x2cf2, 0x0000, /* 1031 */ 0x10a0, 0x0000, /* 1033 */ 0x10a1, 0x0000, /* 1035 */ 0x10a2, 0x0000, /* 1037 */ 0x10a3, 0x0000, /* 1039 */ 0x10a4, 0x0000, /* 103b */ 0x10a5, 0x0000, /* 103d */ 0x10a6, 0x0000, /* 103f */ 0x10a7, 0x0000, /* 1041 */ 0x10a8, 0x0000, /* 1043 */ 0x10a9, 0x0000, /* 1045 */ 0x10aa, 0x0000, /* 1047 */ 0x10ab, 0x0000, /* 1049 */ 0x10ac, 0x0000, /* 104b */ 0x10ad, 0x0000, /* 104d */ 0x10ae, 0x0000, /* 104f */ 0x10af, 0x0000, /* 1051 */ 0x10b0, 0x0000, /* 1053 */ 0x10b1, 0x0000, /* 1055 */ 0x10b2, 0x0000, /* 1057 */ 0x10b3, 0x0000, /* 1059 */ 0x10b4, 0x0000, /* 105b */ 0x10b5, 0x0000, /* 105d */ 0x10b6, 0x0000, /* 105f */ 0x10b7, 0x0000, /* 1061 */ 0x10b8, 0x0000, /* 1063 */ 0x10b9, 0x0000, /* 1065 */ 0x10ba, 0x0000, /* 1067 */ 0x10bb, 0x0000, /* 1069 */ 0x10bc, 0x0000, /* 106b */ 0x10bd, 0x0000, /* 106d */ 0x10be, 0x0000, /* 106f */ 0x10bf, 0x0000, /* 1071 */ 0x10c0, 0x0000, /* 1073 */ 0x10c1, 0x0000, /* 1075 */ 0x10c2, 0x0000, /* 1077 */ 0x10c3, 0x0000, /* 1079 */ 0x10c4, 0x0000, /* 107b */ 0x10c5, 0x0000, /* 107d */ 0x10c7, 0x0000, /* 107f */ 0x10cd, 0x0000, /* 1081 */ 0xa641, 0x0000, /* 1083 */ 0xa640, 0x0000, /* 1085 */ 0xa643, 0x0000, /* 1087 */ 0xa642, 0x0000, /* 1089 */ 0xa645, 0x0000, /* 108b */ 0xa644, 0x0000, /* 108d */ 0xa647, 0x0000, /* 108f */ 0xa646, 0x0000, /* 1091 */ 0xa649, 0x0000, /* 1093 */ 0xa648, 0x0000, /* 1095 */ 0xa64b, 0x0000, /* 1097 */ 0xa64a, 0x0000, /* 1099 */ 0xa64d, 0x0000, /* 109b */ 0xa64c, 0x0000, /* 109d */ 0xa64f, 0x0000, /* 109f */ 0xa64e, 0x0000, /* 10a1 */ 0xa651, 0x0000, /* 10a3 */ 0xa650, 0x0000, /* 10a5 */ 0xa653, 0x0000, /* 10a7 */ 0xa652, 0x0000, /* 10a9 */ 0xa655, 0x0000, /* 10ab */ 0xa654, 0x0000, /* 10ad */ 0xa657, 0x0000, /* 10af */ 0xa656, 0x0000, /* 10b1 */ 0xa659, 0x0000, /* 10b3 */ 0xa658, 0x0000, /* 10b5 */ 0xa65b, 0x0000, /* 10b7 */ 0xa65a, 0x0000, /* 10b9 */ 0xa65d, 0x0000, /* 10bb */ 0xa65c, 0x0000, /* 10bd */ 0xa65f, 0x0000, /* 10bf */ 0xa65e, 0x0000, /* 10c1 */ 0xa661, 0x0000, /* 10c3 */ 0xa660, 0x0000, /* 10c5 */ 0xa663, 0x0000, /* 10c7 */ 0xa662, 0x0000, /* 10c9 */ 0xa665, 0x0000, /* 10cb */ 0xa664, 0x0000, /* 10cd */ 0xa667, 0x0000, /* 10cf */ 0xa666, 0x0000, /* 10d1 */ 0xa669, 0x0000, /* 10d3 */ 0xa668, 0x0000, /* 10d5 */ 0xa66b, 0x0000, /* 10d7 */ 0xa66a, 0x0000, /* 10d9 */ 0xa66d, 0x0000, /* 10db */ 0xa66c, 0x0000, /* 10dd */ 0xa681, 0x0000, /* 10df */ 0xa680, 0x0000, /* 10e1 */ 0xa683, 0x0000, /* 10e3 */ 0xa682, 0x0000, /* 10e5 */ 0xa685, 0x0000, /* 10e7 */ 0xa684, 0x0000, /* 10e9 */ 0xa687, 0x0000, /* 10eb */ 0xa686, 0x0000, /* 10ed */ 0xa689, 0x0000, /* 10ef */ 0xa688, 0x0000, /* 10f1 */ 0xa68b, 0x0000, /* 10f3 */ 0xa68a, 0x0000, /* 10f5 */ 0xa68d, 0x0000, /* 10f7 */ 0xa68c, 0x0000, /* 10f9 */ 0xa68f, 0x0000, /* 10fb */ 0xa68e, 0x0000, /* 10fd */ 0xa691, 0x0000, /* 10ff */ 0xa690, 0x0000, /* 1101 */ 0xa693, 0x0000, /* 1103 */ 0xa692, 0x0000, /* 1105 */ 0xa695, 0x0000, /* 1107 */ 0xa694, 0x0000, /* 1109 */ 0xa697, 0x0000, /* 110b */ 0xa696, 0x0000, /* 110d */ 0xa723, 0x0000, /* 110f */ 0xa722, 0x0000, /* 1111 */ 0xa725, 0x0000, /* 1113 */ 0xa724, 0x0000, /* 1115 */ 0xa727, 0x0000, /* 1117 */ 0xa726, 0x0000, /* 1119 */ 0xa729, 0x0000, /* 111b */ 0xa728, 0x0000, /* 111d */ 0xa72b, 0x0000, /* 111f */ 0xa72a, 0x0000, /* 1121 */ 0xa72d, 0x0000, /* 1123 */ 0xa72c, 0x0000, /* 1125 */ 0xa72f, 0x0000, /* 1127 */ 0xa72e, 0x0000, /* 1129 */ 0xa733, 0x0000, /* 112b */ 0xa732, 0x0000, /* 112d */ 0xa735, 0x0000, /* 112f */ 0xa734, 0x0000, /* 1131 */ 0xa737, 0x0000, /* 1133 */ 0xa736, 0x0000, /* 1135 */ 0xa739, 0x0000, /* 1137 */ 0xa738, 0x0000, /* 1139 */ 0xa73b, 0x0000, /* 113b */ 0xa73a, 0x0000, /* 113d */ 0xa73d, 0x0000, /* 113f */ 0xa73c, 0x0000, /* 1141 */ 0xa73f, 0x0000, /* 1143 */ 0xa73e, 0x0000, /* 1145 */ 0xa741, 0x0000, /* 1147 */ 0xa740, 0x0000, /* 1149 */ 0xa743, 0x0000, /* 114b */ 0xa742, 0x0000, /* 114d */ 0xa745, 0x0000, /* 114f */ 0xa744, 0x0000, /* 1151 */ 0xa747, 0x0000, /* 1153 */ 0xa746, 0x0000, /* 1155 */ 0xa749, 0x0000, /* 1157 */ 0xa748, 0x0000, /* 1159 */ 0xa74b, 0x0000, /* 115b */ 0xa74a, 0x0000, /* 115d */ 0xa74d, 0x0000, /* 115f */ 0xa74c, 0x0000, /* 1161 */ 0xa74f, 0x0000, /* 1163 */ 0xa74e, 0x0000, /* 1165 */ 0xa751, 0x0000, /* 1167 */ 0xa750, 0x0000, /* 1169 */ 0xa753, 0x0000, /* 116b */ 0xa752, 0x0000, /* 116d */ 0xa755, 0x0000, /* 116f */ 0xa754, 0x0000, /* 1171 */ 0xa757, 0x0000, /* 1173 */ 0xa756, 0x0000, /* 1175 */ 0xa759, 0x0000, /* 1177 */ 0xa758, 0x0000, /* 1179 */ 0xa75b, 0x0000, /* 117b */ 0xa75a, 0x0000, /* 117d */ 0xa75d, 0x0000, /* 117f */ 0xa75c, 0x0000, /* 1181 */ 0xa75f, 0x0000, /* 1183 */ 0xa75e, 0x0000, /* 1185 */ 0xa761, 0x0000, /* 1187 */ 0xa760, 0x0000, /* 1189 */ 0xa763, 0x0000, /* 118b */ 0xa762, 0x0000, /* 118d */ 0xa765, 0x0000, /* 118f */ 0xa764, 0x0000, /* 1191 */ 0xa767, 0x0000, /* 1193 */ 0xa766, 0x0000, /* 1195 */ 0xa769, 0x0000, /* 1197 */ 0xa768, 0x0000, /* 1199 */ 0xa76b, 0x0000, /* 119b */ 0xa76a, 0x0000, /* 119d */ 0xa76d, 0x0000, /* 119f */ 0xa76c, 0x0000, /* 11a1 */ 0xa76f, 0x0000, /* 11a3 */ 0xa76e, 0x0000, /* 11a5 */ 0xa77a, 0x0000, /* 11a7 */ 0xa779, 0x0000, /* 11a9 */ 0xa77c, 0x0000, /* 11ab */ 0xa77b, 0x0000, /* 11ad */ 0x1d79, 0x0000, /* 11af */ 0xa77f, 0x0000, /* 11b1 */ 0xa77e, 0x0000, /* 11b3 */ 0xa781, 0x0000, /* 11b5 */ 0xa780, 0x0000, /* 11b7 */ 0xa783, 0x0000, /* 11b9 */ 0xa782, 0x0000, /* 11bb */ 0xa785, 0x0000, /* 11bd */ 0xa784, 0x0000, /* 11bf */ 0xa787, 0x0000, /* 11c1 */ 0xa786, 0x0000, /* 11c3 */ 0xa78c, 0x0000, /* 11c5 */ 0xa78b, 0x0000, /* 11c7 */ 0x0265, 0x0000, /* 11c9 */ 0xa791, 0x0000, /* 11cb */ 0xa790, 0x0000, /* 11cd */ 0xa793, 0x0000, /* 11cf */ 0xa792, 0x0000, /* 11d1 */ 0xa7a1, 0x0000, /* 11d3 */ 0xa7a0, 0x0000, /* 11d5 */ 0xa7a3, 0x0000, /* 11d7 */ 0xa7a2, 0x0000, /* 11d9 */ 0xa7a5, 0x0000, /* 11db */ 0xa7a4, 0x0000, /* 11dd */ 0xa7a7, 0x0000, /* 11df */ 0xa7a6, 0x0000, /* 11e1 */ 0xa7a9, 0x0000, /* 11e3 */ 0xa7a8, 0x0000, /* 11e5 */ 0x0266, 0x0000, /* 11e7 */ 0x0046, 0x0066, 0x0000, /* 11ea */ 0x0046, 0x0046, 0x0000, /* 11ed */ 0x0066, 0x0066, 0x0000, /* 11f0 */ 0x0046, 0x0069, 0x0000, /* 11f3 */ 0x0046, 0x0049, 0x0000, /* 11f6 */ 0x0066, 0x0069, 0x0000, /* 11f9 */ 0x0046, 0x006c, 0x0000, /* 11fc */ 0x0046, 0x004c, 0x0000, /* 11ff */ 0x0066, 0x006c, 0x0000, /* 1202 */ 0x0046, 0x0066, 0x0069, 0x0000, /* 1206 */ 0x0046, 0x0046, 0x0049, 0x0000, /* 120a */ 0x0066, 0x0066, 0x0069, 0x0000, /* 120e */ 0x0046, 0x0066, 0x006c, 0x0000, /* 1212 */ 0x0046, 0x0046, 0x004c, 0x0000, /* 1216 */ 0x0066, 0x0066, 0x006c, 0x0000, /* 121a */ 0x0053, 0x0074, 0x0000, /* 121d */ 0x0053, 0x0054, 0x0000, /* 1220 */ 0x0073, 0x0074, 0x0000, /* 1223 */ 0x0053, 0x0074, 0x0000, /* 1226 */ 0x0053, 0x0054, 0x0000, /* 1229 */ 0x0073, 0x0074, 0x0000, /* 122c */ 0x0544, 0x0576, 0x0000, /* 122f */ 0x0544, 0x0546, 0x0000, /* 1232 */ 0x0574, 0x0576, 0x0000, /* 1235 */ 0x0544, 0x0565, 0x0000, /* 1238 */ 0x0544, 0x0535, 0x0000, /* 123b */ 0x0574, 0x0565, 0x0000, /* 123e */ 0x0544, 0x056b, 0x0000, /* 1241 */ 0x0544, 0x053b, 0x0000, /* 1244 */ 0x0574, 0x056b, 0x0000, /* 1247 */ 0x054e, 0x0576, 0x0000, /* 124a */ 0x054e, 0x0546, 0x0000, /* 124d */ 0x057e, 0x0576, 0x0000, /* 1250 */ 0x0544, 0x056d, 0x0000, /* 1253 */ 0x0544, 0x053d, 0x0000, /* 1256 */ 0x0574, 0x056d, 0x0000, /* 1259 */ 0xff41, 0x0000, /* 125b */ 0xff42, 0x0000, /* 125d */ 0xff43, 0x0000, /* 125f */ 0xff44, 0x0000, /* 1261 */ 0xff45, 0x0000, /* 1263 */ 0xff46, 0x0000, /* 1265 */ 0xff47, 0x0000, /* 1267 */ 0xff48, 0x0000, /* 1269 */ 0xff49, 0x0000, /* 126b */ 0xff4a, 0x0000, /* 126d */ 0xff4b, 0x0000, /* 126f */ 0xff4c, 0x0000, /* 1271 */ 0xff4d, 0x0000, /* 1273 */ 0xff4e, 0x0000, /* 1275 */ 0xff4f, 0x0000, /* 1277 */ 0xff50, 0x0000, /* 1279 */ 0xff51, 0x0000, /* 127b */ 0xff52, 0x0000, /* 127d */ 0xff53, 0x0000, /* 127f */ 0xff54, 0x0000, /* 1281 */ 0xff55, 0x0000, /* 1283 */ 0xff56, 0x0000, /* 1285 */ 0xff57, 0x0000, /* 1287 */ 0xff58, 0x0000, /* 1289 */ 0xff59, 0x0000, /* 128b */ 0xff5a, 0x0000, /* 128d */ 0xff21, 0x0000, /* 128f */ 0xff22, 0x0000, /* 1291 */ 0xff23, 0x0000, /* 1293 */ 0xff24, 0x0000, /* 1295 */ 0xff25, 0x0000, /* 1297 */ 0xff26, 0x0000, /* 1299 */ 0xff27, 0x0000, /* 129b */ 0xff28, 0x0000, /* 129d */ 0xff29, 0x0000, /* 129f */ 0xff2a, 0x0000, /* 12a1 */ 0xff2b, 0x0000, /* 12a3 */ 0xff2c, 0x0000, /* 12a5 */ 0xff2d, 0x0000, /* 12a7 */ 0xff2e, 0x0000, /* 12a9 */ 0xff2f, 0x0000, /* 12ab */ 0xff30, 0x0000, /* 12ad */ 0xff31, 0x0000, /* 12af */ 0xff32, 0x0000, /* 12b1 */ 0xff33, 0x0000, /* 12b3 */ 0xff34, 0x0000, /* 12b5 */ 0xff35, 0x0000, /* 12b7 */ 0xff36, 0x0000, /* 12b9 */ 0xff37, 0x0000, /* 12bb */ 0xff38, 0x0000, /* 12bd */ 0xff39, 0x0000, /* 12bf */ 0xff3a, 0x0000 }; const wchar_t *t3_to_lower(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].lower; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_title(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].title; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_upper(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].upper; return ofs == 0 ? 0 : &case_expansion[ofs]; } const wchar_t *t3_to_fold(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].fold; return ofs == 0 ? 0 : &case_expansion[ofs]; } wchar_t t3_simple_case_fold(wchar_t ch) { case_table *e = case_tab[ch >> 7]; if (e == 0) return 0; int ofs = e[ch & 127].lower; return ofs == 0 ? ch : case_expansion[ofs + 1] != 0 ? ch : case_expansion[ofs]; } /* total static data size (16-bit pointers) = 75138 bytes */ frobtads-1.2.3/tads3/vminitim.cpp0000644000175000001440000000227212145504453016070 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminitim.cpp - VM initialization - in-memory pool implementation Function Notes Modified 07/21/99 MJRoberts - Creation */ #include "t3std.h" #include "vminit.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Initialize the VM with in-memory page pools */ void vm_init_in_mem(vm_globals **vmg, const vm_init_options *opts) { /* initialize the base VM structures */ vm_init_base(vmg, opts); /* * assign the global pointer to the special vmg__ local for * globals-on-stack configuration */ vm_globals *vmg__ = *vmg; /* * explicitly mark vmg__ as reference, in case we don't happen to use * it in this build configuration */ (void)vmg__; /* create the in-memory pools */ VM_IF_ALLOC_PRE_GLOBAL(G_code_pool = new CVmPoolInMem()); VM_IF_ALLOC_PRE_GLOBAL(G_const_pool = new CVmPoolInMem()); } frobtads-1.2.3/tads3/tcpnbase.h0000644000175000001440000005610212012526233015472 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCPNBASE.H,v 1.3 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcpn.h - Parse Node - base class Function Defines the target-independent base class for parse nodes Notes All expression parse nodes are derived from the target-specific subclass of this class. The target-independent base class is CTcPrsNodeBase; the target-specific class is CTcPrsNode. Modified 05/10/99 MJRoberts - Creation */ #ifndef TCPN_H #define TCPN_H #include "vmhash.h" /* ------------------------------------------------------------------------ */ /* * Parse Tree Allocation Object. This is a base class that can be used * for tree objects that are to be allocated from the parser node pool. */ class CTcPrsAllocObj { public: /* * Override operator new() - allocate all parse node objects out of * the parse node pool. */ void *operator new(size_t siz); }; /* ------------------------------------------------------------------------ */ /* * Parse Tree Expression Node - base class. As we parse an expression, * we build a tree of these objects to describe the source code. * * This class is subclassed for each type of parsing node: each type of * statement has a node type, some statements have helper node types for * parts of statements, and each expression operator has a node type. * These subclasses contain the information specific to the type of * parsing construct represented. * * Each parsing subclass is then further subclassed for each target * architecture. This final subclass contains the code generator for * the node in the target architecture. * * The target-independent base version of each subclass is called * CTPNXxxBase. The target-specific subclass derived from this base * class is CTPNXxx. For example, the final subclass for constant * nodes, which is derived from the target-independent base class * CTPNConstBase, is CTPNConst. (Note that each target uses the same * name for the final subclass, so we can only link one target * architecture into a given build of the compiler. Each additional * target requires a separate compiler executable with the appropriate * CTPNConst classes linked in.) */ class CTcPrsNodeBase: public CTcPrsAllocObj { public: /* * Generate code for the expression for the target architecture. * This method is defined only by the final target-specific * subclasses. * * This method is used to generate code to evaluate the expression * as an rvalue. * * If 'discard' is true, it indicates that any value yielded by the * expression will not be used, in which case the generated code * need not leave the result of the expression on the stack. We can * generate code more efficiently for certain types of expressions * when we know that we're evaluating them only for side effects. * For example, an assignment expression has a result value, but * this value need not be pushed onto the stack if it will simply be * discarded. Also, an operator like "+" that has no side effects * of its own can merely evaluate its operands for their side * effects, but need not compute its own result if that result would * simply be discarded. * * If 'for_condition' is true, it indicates that the result of the * expression will be used directly for a conditional of some kind * (for a "?:" operator, an "if" statement, a "while" statement, or * the like). In some cases, we can avoid extra conversions to some * values when they're going to be used directly for a comparison; * for example, the "&&" operator must return a true/nil value, but * the code generator may be able to avoid the extra conversion when * the value will be used for an "if" statement's conditional value. */ virtual void gen_code(int discard, int for_condition) = 0; /* * Get the constant value of the parse node, if available. Most * parse nodes have no constant value, so by default this returns * null. Only constant parse nodes can provide a constant value, so * they should override this. */ virtual class CTcConstVal *get_const_val() { return 0; } /* determine if the node has a constant value */ int is_const() { return get_const_val() != 0; } /* determine if I have a given constant integer value */ int is_const_int(int val) { return (is_const() && get_const_val()->get_type() == TC_CVT_INT && get_const_val()->get_val_int() == val); } /* * Set the constant value of the parse node from that of another * node. The caller must already have checked that this node and * the value being assigned are both valid constant values. */ void set_const_val(class CTcPrsNode *src) { /* set my constant value from the source's constant value */ get_const_val()->set(((CTcPrsNodeBase *)src)->get_const_val()); } /* * Is this value guaranteed to be a boolean value? Certain operators * produce boolean results regardless of the operands. In particular, * the comparison operators (==, !=, >, <, >=, <=) and the logical NOT * operator (!) always produce boolean results. */ virtual int is_bool() const { return FALSE; } /* * Check to see if this expression can possibly be a valid lvalue. * Return true if so, false if not. This check is made before * symbol resolution; when it is not certain whether or not a symbol * expression can be an lvalue, assume it can be at this point. By * default, we'll return false; operator nodes whose result can be * used as an lvalue should override this to return true. */ virtual int check_lvalue() const { return FALSE; } /* * Check to see if this expression is an valid lvalue, after * resolving symbols in the given scope. Returns true if so, false * if not. */ virtual int check_lvalue_resolved(class CTcPrsSymtab *symtab) const { return FALSE; } /* * Check to see if this expression can possibly be a valid address * value, so that the address-of ("&") operator can be applied. * Returns true if it is possible, false if not. The only type of * expression whose address can be taken is a simple symbol. The * address of a symbol can be taken only if the symbol is a function * or property name, but we won't know this at parse time, so we'll * indicate that any symbol is acceptable. By default, this returns * false, since the address of most expressions cannot be taken. */ virtual int has_addr() const { return FALSE; } /* * Check to see if this expression is an address expression of some * kind (i.e., of class CTPNAddrBase, or of a class derived from * CTPNAddrBase). Returns true if so, false if not. */ virtual int is_addr() const { return FALSE; } /* * Determine if this node is of type double-quoted string (dstring). * Returns true if so, false if not. By default, we return false. */ virtual int is_dstring() const { return FALSE; } /* * Determine if this node contains a dstring expression. This returns * true if the node is a dstring, a dstring embedding, or a comma node * containing a dstring and/or a dstring embedding. */ virtual int is_dstring_expr() const { return FALSE; } /* * Determine if this is a simple assignment operator node. Returns * true if so, false if not. By default, we return false. */ virtual int is_simple_asi() const { return FALSE; } /* * Determine if this node yields a value when evaluated. Returns * true if so, false if not. When it cannot be determined at * compile-time whether or not the node has a value (for example, * for a call to a pointer to a function whose return type is not * declared), this should indicate that a value is returned. * * Most nodes yield a value when executed, so we'll return true by * default. */ virtual int has_return_value() const { return TRUE; } /* * Determine if this node yields a return value when called as a * function. We assume by default that it does. */ virtual int has_return_value_on_call() const { return TRUE; } /* * Get the text of the symbol for this node, if any. If the node is * not some kind of symbol node, this returns null. */ virtual const textchar_t *get_sym_text() const { return 0; } virtual size_t get_sym_text_len() const { return 0; } /* check for a match to the given symbol text */ virtual int sym_text_matches(const char *str, size_t len) const { return FALSE; } /* * Fold constant expressions, given a finished symbol table. We do * most of our constant folding during the initial parsing, but some * constant folding must wait until the symbol table is finished; in * particular, we can't figure out what to do with symbols until we * know what the symbols mean. * * For most nodes, this function should merely recurse into subnodes * and fold constants. Nodes that are affected by symbol * resolution, directly or indirectly, should override this. * * For example, a list can change from unknown to constant during * this operation. If the list contains a symbol, the list will * initially be set to unknown, since the symbol could turn out to * be a property evaluation, which would be non-constant, or an * object name, which would be constant. * * Returns the folded version of the node, or simply 'this' if no * folding takes place. */ virtual class CTcPrsNode *fold_constants(class CTcPrsSymtab *symtab) = 0; /* * generate a constant value node for the address of this node; * returns null if the symbol has no address */ virtual class CTcPrsNode *fold_addr_const(class CTcPrsSymtab *) { /* by default, we have no address */ return 0; } /* * Adjust the expression for use as a dynamic (run-time) compilation * expression expression. Dynamic expressions include debugger * expression evaluations and the run-time "eval()" facility. * * Code generation for dynamic expressions is somewhat different than * for normal expressions. This routine should allocate and return a * new node, if necessary, for dynamic use. If no changes are * necessary, simply return 'this'. * * If 'speculative' is true, the expression is being evaluated * speculatively by the debugger. This means that the user hasn't * explicitly asked for the expression to be evaluated, but rather the * debugger is making a guess that the expression might be of interest * to the user and is making an unsolicited attempt to offer it to the * user. Because the debugger is only guessing that the expression is * interesting, the expression must not be evaluated if it has any side * effects at all. */ virtual class CTcPrsNode *adjust_for_dyn(const tcpn_dyncomp_info *info); }; /* ------------------------------------------------------------------------ */ /* * Symbol Table Entry. Each symbol has an entry in one of the symbol * tables: * * - The global symbol table contains object, property, and built-in * functions from the default function set. * * - Local symbol tables contain local variables and parameters. Local * tables have block-level scope. * * - Label symbol tables contain code labels (for "goto" statements). * Label tables have function-level or method-level scope. */ /* * Basic symbol table entry. The target */ class CTcSymbolBase: public CVmHashEntryCS { public: CTcSymbolBase(const char *str, size_t len, int copy, tc_symtype_t typ) : CVmHashEntryCS(str, len, copy) { typ_ = typ; } /* allocate symbol entries from the parser memory pool */ void *operator new(size_t siz); /* get the symbol type */ tc_symtype_t get_type() const { return typ_; } /* get the symbol text and length */ const char *get_sym() const { return getstr(); } size_t get_sym_len() const { return getlen(); } /* * Generate a constant value node for this symbol, if possible; * returns null if the symbol does not evaluate to a compile-time * constant value. An object name, for example, evaluates to a * compile-time constant equal to the object reference; a property * name, in contrast, is (when not qualified by another operator) an * invocation of the property, hence must be executed at run time, * hence is not a compile-time constant. */ virtual class CTcPrsNode *fold_constant() { /* by default, a symbol's value is not a constant */ return 0; } /* * generate a constant value node for the address of this symbol; * returns null if the symbol has no address */ virtual class CTcPrsNode *fold_addr_const() { /* by default, a symbol has no address */ return 0; } /* determine if this symbol can be used as an lvalue */ virtual int check_lvalue() const { return FALSE; } /* determine if this symbol can have its address taken */ virtual int has_addr() const { return FALSE; } /* determine if I have a return value when evaluated */ virtual int has_return_value_on_call() const { return TRUE; } /* * Write the symbol to a symbol export file. By default, we'll * write the type and symbol name to the file. Some subclasses * might wish to override this to write additional data, or to write * something different or nothing at all (for example, built-in * function symbols are not written to a symbol export file). * * When a subclass does override this, it must write the type as a * UINT2 value as the first thing written to the file. The generic * file reader switches on this type code to determine what to call * to load the entry, then calls the subclass-specific loader to do * the actual work. * * Returns true if we wrote the symbol to the file, false if not. * (False doesn't indicate an error - it indicates that we chose not * to store the symbol because the symbol is not of a type that we * want to put in the export file.) */ virtual int write_to_sym_file(class CVmFile *fp); /* write the symbol name (with a UINT2 length prefix) to a file */ int write_name_to_file(class CVmFile *fp); /* * Write the symbol to an object file. By default, we'll write the * type and symbol name to the file. Some subclasses might wish to * override this to write additional data, or to write something * different or nothing at all (for example, built-in function * symbols are not written to an object file). * * When a subclass does override this, it must write the type as a * UINT2 value as the first thing written to the file. The generic * file reader switches on this type code to determine what to call * to load the entry, then calls the subclass-specific loader to do * the actual work. * * Returns true if we wrote the symbol to the file, false if not. * (False doesn't indicate an error - it indicates that we chose not * to store the symbol because the symbol is not of a type that we * want to put in the export file.) */ virtual int write_to_obj_file(class CVmFile *fp); /* * Write the symbol's cross references to the object file. This can * write references to other symbols by storing the other symbol's * index in the object file. Most symbols don't have any cross * references, so this does nothing by default. * * If this writes anything, the first thing written must be a UINT4 * giving the object file index of this symbol. On loading, we'll * read this and look up the loaded symbol. */ virtual int write_refs_to_obj_file(class CVmFile *) { return FALSE; } /* * perform basic writing to a file - this performs common work that * can be used for object or symbol files */ int write_to_file_gen(CVmFile *fp); /* * Read a symbol from a symbol file, returning the new symbol */ static class CTcSymbol *read_from_sym_file(class CVmFile *fp); /* * Load a symbol from an object file. Stores the symbol in the * global symbol table, and fills in the appropriate translation * mapping table when necessary. Returns zero on success; logs * error messages and return non-zero on failure. */ static int load_from_obj_file(class CVmFile *fp, const textchar_t *fname, tctarg_obj_id_t *obj_xlat, tctarg_prop_id_t *prop_xlat, ulong *enum_xlat); /* * Load references from the object file - reads the information that * write_refs_to_obj_file() wrote, except that the caller will have * read the first UINT4 giving the symbol's object file index before * calling this routine. */ virtual void load_refs_from_obj_file(class CVmFile *, const textchar_t * /*obj_fname*/, tctarg_obj_id_t * /*obj_xlat*/, tctarg_prop_id_t * /*prop_xlat*/) { /* by default, do nothing */ } /* * Log an object file loading conflict with this symbol. The given * type is the new type found in the object file of the given name. */ void log_objfile_conflict(const textchar_t *fname, tc_symtype_t new_type) const; /* * Get a pointer to the head of the fixup list for this symbol. * Symbols such as functions that keep a list of fixups for * references to the symbol must override this to provide a fixup * list head; by default, symbols keep no fixup list, so we'll just * return null. */ virtual struct CTcAbsFixup **get_fixup_list_anchor() { return 0; } /* * Set my code stream anchor object. By default, symbols don't keep * track of any stream anchors. Symbols that refer to code or data * stream locations directly must keep an anchor, since they must * keep track of their fixup list in order to fix up generated * references to the symbol. This must be overridden by any * subclasses that keep anchors. */ virtual void set_anchor(struct CTcStreamAnchor *) { } /* * Determine if this symbol is external and unresolved. By default, * a symbol cannot be external at all, so this will return false. * Subclasses for symbol types that can be external should override * this to return true if the symbol is an unresolved external * reference. */ virtual int is_unresolved_extern() const { return FALSE; } /* * Mark the symbol as referenced. Some symbol types keep track of * whether they've been referenced or not; those types can override * this to keep track. This method is called each time the symbol * is found in the symbol table via the find() or find_or_def() * methods. By default, we do nothing. */ virtual void mark_referenced() { } /* * Apply internal fixups. If the symbol keeps its own internal * fixup information, it can translate the fixups here. By default, * this does nothing. */ virtual void apply_internal_fixups() { } /* * Build dictionary entries for this symbol. Most symbols do * nothing here; objects which can have associated vocabulary words * should insert their vocabulary into the dictionary. */ virtual void build_dictionary() { } /* * Create a new "context variable" version of this symbol for use in * an anonymous function. This is only needed for symbols that can * exist in a local scope. */ virtual class CTcSymbol *new_ctx_var() const { return 0; } /* * Apply context variable conversion. If this symbol has not been * referenced, this should simply remove the symbol from the symbol * table. Otherwise, this should apply the necessary conversions to * the original symbol from which this symbol was created to ensure * that the original and this symbol share a context variable slot. * * Returns true if a conversion was performed (i.e., the symbol was * referenced), false if not. */ virtual int apply_ctx_var_conv(class CTcPrsSymtab *, class CTPNCodeBody *) { return FALSE; } /* * Finalize context variable conversion. This should do nothing if * the variable hasn't already been notified that it's a context * variable (how this happens varies by symbol type - see locals in * particular). This is called with the variable's own scope active * in the parser, so the final variable assignments for the symbol * can be made. */ virtual void finish_ctx_var_conv() { } /* * Check for local references. For variables that can exist in * local scope, such as locals, this will be called when all of the * code for the scope has been parsed; this should check to see if * the symbol has been referenced in the scope, and display an * appropriate warning message if not. */ virtual void check_local_references() { } /* * Add an entry for this symbol to a "runtime symbol table," which is * a symbol table that we can pass to the interpreter. This must be * overridden by each symbol type for each target architecture, * because the nature of the runtime symbol table varies by target * architecture. * * By default, this does nothing. Symbol types that don't need to * generate runtime symbol table entries don't need to override this. */ virtual void add_runtime_symbol(class CVmRuntimeSymbols *) { } protected: /* * Base routine to read from a symbol file - reads the symbol name. * Returns a pointer to the symbol name (stored in tokenizer memory * that will remain valid throughout the compilation) on success; on * failure, logs an error and returns null. */ static const char *base_read_from_sym_file(class CVmFile *fp); /* symbol type */ tc_symtype_t typ_; }; #endif /* TCPN_H */ frobtads-1.2.3/tads3/vmprof.h0000644000175000001440000001202111321366202015174 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmprof.h - T3 VM Profiler Function Definitions for the execution profiler, which captures information on the time the program spends in each function. Profiling information is useful for performance optimization, because it identifies the areas where the program is spending all of its time. Profiling can be optionally included when the VM is compiled. Builds of the VM intended for use by end users should normally not include the profiler, because they will have no use for profiling, and including the profiler in the build enlarges the code size and adds run-time overhead. Normally, the profiler should be included only in builds that include the interactive debugger. Notes Modified 08/03/02 MJRoberts - Creation */ #ifndef VMPROF_H #define VMPROF_H #include "vmtype.h" #include "vmprofty.h" /* ------------------------------------------------------------------------ */ /* * Enable the profiler when compiling the VM by #defining VM_PROFILER on * the compiler command line (usually done through the makefile's CFLAGS * compiler options variable or equivalent). When VM_PROFILE is not * defined at compile-time, the profiling code will be omitted, making for * a smaller executable and less run-time overhead. */ #ifdef VM_PROFILER /* include profiler-only code, and do not include non-profiler-only code */ #define VM_IF_PROFILER(x) x #define VM_IF_NOT_PROFILER(x) #else /* VM_PROFILER */ /* do not include profiler code; do include non-profiler code */ #define VM_IF_PROFILER(x) #define VM_IF_NOT_PROFILER(x) x #endif /* VM_PROFILER */ /* ------------------------------------------------------------------------ */ /* * Profiler function call record. * * We keep a stack of these records alongside the regular VM stack, to * track the amount of time spent in a function. A record in this stack * corresponds to an activation frame in the regular VM stack. * * We also keep a master table of these entries indexed by obj.prop/func * identifier. These records keep the running totals for each function. */ struct vm_profiler_rec { /* the object.property of the function, for a method */ vm_obj_id_t obj; vm_prop_id_t prop; /* the entrypoint of the method */ const uchar *func; /* the cumulative time DIRECTLY in this function (not in children) */ vm_prof_time sum_direct; /* * the cumulative time in this function's children (but not directly in * the function itself) */ vm_prof_time sum_chi; /* * invocation count (this is used in the running total records only, * not in the stack) */ unsigned long call_cnt; }; /* ------------------------------------------------------------------------ */ /* * OS-specific profiler functions. These must be provided by the OS * implementation. */ /* * Get the current high-precision timer, filling in *t with the current * time. This indicates the time that has elapsed since an arbitrary zero * point, defined by the OS code. We only care about deltas from one time * to another, so the zero point doesn't matter to us, as long as all times * have a consistent zero time and can thus be subtracted to find elapsed * time values. The units are arbitrary; because this routine will be * called very frequently, it should be as fast as possible, and thus * should return the time in the native units the OS uses. */ void os_prof_curtime(vm_prof_time *t); /* * Convert the elapsed time value to microseconds and return it as an * unsigned long. This should simply multiply the time value by the number * of microseconds per OS-dependent unit that os_prof_curtime() returns, * and return the value as a 32-bit unsigned long. * * We use a separate routine to convert the OS time units to microseconds * so that we don't have to perform the conversion every time we note the * current time, which we must do very frequently; instead, we defer the * conversions until we're finished with a profiler run, at which point we * have a set of summary data that is typically much smaller than the raw * set we collect throughout execution. * * Note that our return precision of 32 bits can only represent time values * up to about 4000 seconds, or about 70 minutes. This is obviously not * enough for complete generality, but it's a fairly long time by profiler * standards, since the values we're measuring are the cumulative elapsed * time spent in particular functions. Given how much easier it is to work * with 32-bit values than anything longer, and given that 70 minutes is a * fairly generous ceiling for this particular application, we accept the * loss of generality in exchange for the simplifaction. */ unsigned long os_prof_time_to_ms(const vm_prof_time *t); #endif /* VMPROF_H */ frobtads-1.2.3/tads3/vmhttpsrv.cpp0000644000175000001440000003223511677361652016326 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpsrv.cpp - CVmObjHTTPServer object Function Notes Modified 04/22/10 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmhttpsrv.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "utf8.h" #include "vmimport.h" #include "vmpredef.h" #include "vmhost.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_httpsrv_ext *vm_httpsrv_ext::alloc_ext( VMG_ CVmObjHTTPServer *self) { /* calculate how much space we need */ size_t siz = sizeof(vm_httpsrv_ext); /* allocate the memory */ vm_httpsrv_ext *ext = (vm_httpsrv_ext *) G_mem->get_var_heap()->alloc_mem(siz, self); /* we don't have a listener or binding address yet */ ext->l = 0; ext->addr = 0; /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer object statics */ /* metaclass registration object */ static CVmMetaclassHTTPServer metaclass_reg_obj; CVmMetaclass *CVmObjHTTPServer::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjHTTPServer::*CVmObjHTTPServer::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjHTTPServer::getp_undef, &CVmObjHTTPServer::getp_shutdown, &CVmObjHTTPServer::getp_get_addr, &CVmObjHTTPServer::getp_get_ip, &CVmObjHTTPServer::getp_get_port }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPServer intrinsic class implementation */ /* * construction */ CVmObjHTTPServer::CVmObjHTTPServer(VMG_ int) { /* allocate our extension structure */ ext_ = (char *)vm_httpsrv_ext::alloc_ext(vmg_ this); } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjHTTPServer::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; const char *errmsg; /* * parse arguments - new HTTPServer(addr?, port?) */ if (argc > 3) err_throw(VMERR_WRONG_NUM_OF_ARGS); /* check for the address - use "localhost" by default */ char addr[256] = "localhost"; if (argc >= 1) { /* get the address - this must be a string or nil */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else CVmBif::pop_str_val_buf(vmg_ addr, sizeof(addr)); } /* check for a port - use 0 by default */ int port = 0; if (argc >= 2) { /* get the port number - this must be an integer or nil */ if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else port = CVmBif::pop_int_val(vmg0_); } /* check for an upload size limit */ int32_t ulim; if (argc >= 3) { if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else ulim = CVmBif::pop_long_val(vmg0_); } /* get the network safety level, to determine if this is allowed */ int client_level, server_level; G_host_ifc->get_net_safety(&client_level, &server_level); /* check the network server safety level */ switch (server_level) { case VM_NET_SAFETY_MINIMUM: /* all access allowed - proceed */ break; case VM_NET_SAFETY_LOCALHOST: /* * localhost only - if the host isn't 'localhost' or '127.0.0.1', * don't allow it */ if (stricmp(addr, "localhost") != 0 && stricmp(addr, "127.0.0.1") != 0) goto access_error; /* allow it */ break; case VM_NET_SAFETY_MAXIMUM: access_error: /* no access allowed */ errmsg = "prohibited network access"; G_interpreter->push_string(vmg_ errmsg); G_interpreter->throw_new_class(vmg_ G_predef->net_safety_exception, 1, errmsg); AFTER_ERR_THROW(break;) } /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, FALSE, FALSE); /* http servers are inherently transient */ G_obj_table->set_obj_transient(id); /* create the object */ CVmObjHTTPServer *l = new (vmg_ id) CVmObjHTTPServer(vmg_ TRUE); /* get the extension */ vm_httpsrv_ext *ext = l->get_ext(); /* create the listener thread object */ TadsHttpListenerThread *ht = new TadsHttpListenerThread( id, G_net_queue, ulim); /* launch the listener */ ext->l = TadsListener::launch(addr, (ushort)port, ht); /* we're done with the thread object (the listener took it over) */ ht->release_ref(); /* if the launch failed, throw an error */ if (ext->l == 0) { /* throw a network error */ const char *errmsg = "Unable to start HTTP server"; G_interpreter->push_string(vmg_ errmsg); G_interpreter->throw_new_class(vmg_ G_predef->net_exception, 1, errmsg); } /* remember my requested binding address */ ext->addr = lib_copy_str(addr); /* return the new ID */ return id; } /* * notify of deletion */ void CVmObjHTTPServer::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ vm_httpsrv_ext *ext = get_ext(); if (ext != 0) { /* shut down the listener */ if (ext->l != 0) { /* tell it to terminate */ ext->l->shutdown(); /* * Notify the listener thread that we're being deleted, so that * it can drop its reference on us. The listener thread object * keeps a reference on us, but it isn't itself a * garbage-collected object, so its reference on us won't * prevent us from being deleted. We therefore need to * manually remove its reference on us when we're about to be * deleted. */ ((TadsHttpListenerThread *)ext->l->get_thread()) ->detach_server_obj(); /* we're done with the listener object */ ext->l->release_ref(); } /* delete our address string */ lib_free_str(ext->addr); /* delete the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* * set a property */ void CVmObjHTTPServer::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjHTTPServer::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * load from an image file */ void CVmObjHTTPServer::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjHTTPServer::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjHTTPServer::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ notify_delete(vmg_ FALSE); /* allocate the extension */ vm_httpsrv_ext *ext = vm_httpsrv_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; } /* * save to a file */ void CVmObjHTTPServer::save_to_file(VMG_ class CVmFile *fp) { /* we're inherently transient, so we should never do this */ assert(FALSE); } /* * restore from a file */ void CVmObjHTTPServer::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* http servers are always transient, so we should never do this */ } /* * Shut down the server */ int CVmObjHTTPServer::getp_shutdown(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* check arguments */ int wait = FALSE; if (argc >= 1) wait = CVmBif::pop_bool_val(vmg0_); /* if we have a listener, tell it to shut down */ vm_httpsrv_ext *ext = get_ext(); if (ext != 0 && ext->l != 0) { /* add a reference on the listener while we're using it */ ext->l->add_ref(); /* shut down the server */ ext->l->shutdown(); /* if we're waiting, wait for the listener thread to exit */ if (wait) ext->l->get_thread()->wait(); /* * Return true if the thread is done, nil if it's still running. * The main listener thread waits for all of its launched server * threads to exit before it itself exits, so once the listener * thread is done, we can be sure that all of its launched threads * are done as well. */ retval->set_logical(ext->l->get_thread()->test()); /* done with the listener */ ext->l->release_ref(); } else { /* there is no server, so it's already shut down */ retval->set_true(); } /* handled */ return TRUE; } /* * Internal get-port interface */ int CVmObjHTTPServer::get_listener_addr( char *&addr, char *&ip, int &port) const { /* if we have a listener, get its port number */ vm_httpsrv_ext *ext = get_ext(); if (ext != 0 && ext->l != 0 && ext->addr != 0 && ext->l->get_thread()->get_local_addr(ip, port)) { /* got the IP and port - also return the constructor address string */ addr = lib_copy_str(ext->addr); return TRUE; } else { /* the address isn't available */ return FALSE; } } /* * Get the port number */ int CVmObjHTTPServer::getp_get_port(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* return the port number */ char *addr, *ip; int port; if (get_listener_addr(addr, ip, port)) { retval->set_int(port); lib_free_str(addr); lib_free_str(ip); } else retval->set_nil(); /* handled */ return TRUE; } /* * Get the listener address */ int CVmObjHTTPServer::getp_get_addr(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* if we have an address string saved, return it */ vm_httpsrv_ext *ext = get_ext(); if (ext != 0 && ext->addr != 0) { /* got it - return the string value */ G_interpreter->push_string(vmg_ ext->addr); G_stk->pop(retval); } else { /* no address value - return nil */ retval->set_nil(); } /* handled */ return TRUE; } /* * Get the listener IP address */ int CVmObjHTTPServer::getp_get_ip(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* return the IP address */ char *ip, *addr; int port; if (get_listener_addr(addr, ip, port)) { /* got it - return the string value */ G_interpreter->push_string(vmg_ ip); G_stk->pop(retval); /* free the source strings */ lib_free_str(addr); lib_free_str(ip); } else { /* no IP address - return nil */ retval->set_nil(); } /* handled */ return TRUE; } frobtads-1.2.3/tads3/vmfunc.h0000644000175000001440000004350511336557150015206 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmfunc.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmfunc.h - T3 VM Function Header definitions Function Defines the layout of a function header, which is the block of data immediately preceding the first byte code instruction in every function. The function header is stored in binary portable format, so that image files can be loaded directly into memory and executed without translation. Notes Modified 11/20/98 MJRoberts - Creation */ #ifndef VMFUNC_H #define VMFUNC_H #include "t3std.h" #include "vmtype.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * FUNCTION HEADER */ /* * The function header is a packed array of bytes, with each element stored * in a canonical binary format for binary portability. No padding is * present except where otherwise specified. The fields in the function * header, starting at offset zero, are: * * UBYTE argc - the number of parameters the function expects to receive. * If the high-order bit is set (i.e., (argc & 0x80) != 0), then the * function takes a variable number of parameters, with a minimum of (argc * & 0x7f) and no maximum. If the high-order bit is clear, argc gives the * exact number of parameters required. * * UBYTE optional_argc - number of additional optional parameters the * function can receive, beyond the fixed arguments given by the 'argc' * field. * * UINT2 locals - the number of local variables the function uses. This * does not count the implicit argument counter local variable, which is * always pushed by the VM after setting up a new activation frame. * * UINT2 total_stack - the total number of stack slots required by the * function, including local variables, intermediate results of * calculations, and actual parameters to functions invoked by this code. * * UINT2 exception_table_ofs - the byte offset from the start of this * method header of the function's exception table. This value is zero if * the function has no exception table. * * UINT2 debug_ofs - the byte offset from the start of this method header * of the function's debugger records. This value is zero if the function * has no debugger records. */ /* minimum function header size supported by this version of the VM */ const size_t VMFUNC_HDR_MIN_SIZE = 10; class CVmFuncPtr { public: CVmFuncPtr() { p_ = 0; } CVmFuncPtr(VMG_ pool_ofs_t ofs); CVmFuncPtr(const char *p) { p_ = (const uchar *)p; } CVmFuncPtr(const uchar *p) { p_ = p; } CVmFuncPtr(VMG_ const vm_val_t *val) { set(vmg_ val); } /* initialize with a pointer to the start of the function */ void set(const uchar *p) { p_ = p; } /* set from a vm_val_t; returns true on success, false on failure */ int set(VMG_ const vm_val_t *val); /* get the header pointer */ const uchar *get() const { return p_; } /* copy from another function pointer */ void copy_from(const CVmFuncPtr *fp) { p_ = fp->p_; } /* get a vm_val_t pointer to this function */ int get_fnptr(VMG_ vm_val_t *v); /* get the minimum argument count */ int get_min_argc() const { /* get the argument count, but mask out the varargs bit */ return (int)(get_argc() & 0x7f); } /* get the maximum argument count, not counting varargs */ int get_max_argc() const { return get_min_argc() + (int)get_opt_argc(); } /* is this a varargs function? */ int is_varargs() const { return ((get_argc() & 0x80) != 0); } /* * check an actual parameter count for correctness; returns true if * the count is correct for this function, false if not */ int argc_ok(int argc) const { /* check for match to the min-max range */ if (argc >= get_min_argc() && argc <= get_max_argc()) { /* we have an exact match, so we're fine */ return TRUE; } else if (is_varargs() && argc > get_min_argc()) { /* * we have variable arguments, and we have at least the * minimum, so we're okay */ return TRUE; } else { /* * either we don't have variable arguments, or we don't have * the minimum varargs count - in either case, we have an * argument count mistmatch */ return FALSE; } } /* * Get the internal argument count. This has the high bit set for a * varargs function, and the low-order seven bits give the nominal * argument count. If this is a varargs function, the nominal * argument count is the minimum count: any actual number of arguments * at least the nominal count is valid. If this is not a varargs * function, the nominal count is the exact count: the actual number * of arguments must match the nominal count. */ uchar get_argc() const { return *(p_ + 0); } /* get the additional optional argument count */ uint get_opt_argc() const { return *(p_ + 1); } /* get the number of locals */ uint get_local_cnt() const { return osrp2(p_ + 2); } /* get the total stack slots required by the function */ uint get_stack_depth() const { return osrp2(p_ + 4); } /* get the exception table offset */ uint get_exc_ofs() const { return osrp2(p_ + 6); } /* get the debugger records table offset */ uint get_debug_ofs() const { return osrp2(p_ + 8); } /* * Set up an exception table pointer for this function. Returns * true if successful, false if there's no exception table. */ int set_exc_ptr(class CVmExcTablePtr *exc_ptr) const; /* * Set up a debug table pointer for this function. Returns true if * successful, false if there's no debug table. */ int set_dbg_ptr(class CVmDbgTablePtr *dbg_ptr) const; private: /* pointer to the method header */ const uchar *p_; }; /* ------------------------------------------------------------------------ */ /* * EXCEPTION TABLE */ /* * The exception table starts with a count indicating how many elements * are in the table, followed by the table entries. Each entry in the * table specifies the handler for one protected range of code. We * search the table in forward order, so the handlers must be stored in * order of precedence. * * Each table entry contains: * * UINT2 start_ofs - the starting offset (as a byte offset from the * start of the function) of the protected range for this handler. * * UINT2 end_ofs - the ending offset (as a byte offset from the start of * the function) of the protected range for this handler. The range is * inclusive of this offset, so a one-byte range would have start_ofs * and end_ofs set to the same value. * * UINT4 exception_class - the object ID of the class of exception * handled by this handler. * * UINT2 handler_ofs - the handler offset (as a byte offset from the * start of the function). This is the offset of the first instruction * of the code to be invoked to handle this exception. */ /* * Exception Table Entry Pointer */ class CVmExcEntryPtr { /* let CVmExcTablePtr initialize us */ friend class CVmExcTablePtr; public: /* get the starting/ending offset from this entry */ uint get_start_ofs() const { return osrp2(p_); } uint get_end_ofs() const { return osrp2(p_ + 2); } /* get the exception class's object ID */ vm_obj_id_t get_exception() const { return (vm_obj_id_t)t3rp4u(p_ + 4); } /* get the handler byte code offset */ uint get_handler_ofs() const { return osrp2(p_ + 8); } /* increment the pointer to the next entry in the table */ void inc(VMG0_) { p_ += G_exc_entry_size; } private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Exception Table Pointer */ class CVmExcTablePtr { public: /* * Initialize with a pointer to the start of the function -- we'll * read the exception table offset out of the method header. * Returns true if the function has an exception table, false if * there is no exception table defined in the function. */ int set(const uchar *p) { CVmFuncPtr func; /* set up the function pointer */ func.set(p); /* if there's no exception table, simply return this information */ if (func.get_exc_ofs() == 0) return FALSE; /* set up our pointer by reading from the header */ p_ = p + func.get_exc_ofs(); /* indicate that there is a valid exception table */ return TRUE; } /* get the number of entries in the table */ size_t get_count() const { return osrp2(p_); } /* initialize a CVmExcEntryPtr with the entry at the given index */ void set_entry_ptr(VMG_ CVmExcEntryPtr *entry, size_t idx) const { entry->set(p_ + 2 + (idx * G_exc_entry_size)); } private: /* pointer to the first byte of the exception table */ const uchar *p_; }; /* ------------------------------------------------------------------------ */ /* * DEBUGGER RECORDS TABLE */ /* * The debugger table consists of three sections. The first section is * the header, with general information on the method or function. The * second section is the line records, which give the code boundaries of * the source lines. The third section is the frame records, giving the * local symbol tables. */ /* * Debugger source line entry pointer */ class CVmDbgLinePtr { /* let CVmDbgTablePtr initialize us */ friend class CVmDbgTablePtr; public: /* * get the byte-code offset (from method start) of the start of the * byte-code for this line */ uint get_start_ofs() const { return osrp2(p_); } /* get the source file ID (an index into the global source file list) */ uint get_source_id() const { return osrp2(p_ + 2); } /* get the line number in the source file */ ulong get_source_line() const { return t3rp4u(p_ + 4); } /* get the frame ID (a 1-based index into our local frame table) */ uint get_frame_id() const { return osrp2(p_ + 8); } /* increment the pointer to the next entry in the table */ void inc(VMG0_) { p_ += G_line_entry_size; } /* set from another line pointer */ void copy_from(const CVmDbgLinePtr *p) { p_ = p->p_; } private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger frame symbol entry pointer */ class CVmDbgFrameSymPtr { /* let CVmDbgFramePtr initialize us */ friend class CVmDbgFramePtr; public: /* get the local/parameter number */ uint get_var_num() const { return osrp2(p_); } /* get the context array index (for context locals) */ vm_prop_id_t get_ctx_arr_idx() const { return (vm_prop_id_t)osrp2(p_ + 4); } /* determine if I'm a local or a parameter */ int is_local() const { return (get_flags() & 1) == 0; } int is_param() const { return (((get_flags() & 1) != 0) && !is_ctx_local()); } /* determine if I'm a context local */ int is_ctx_local() const { return (get_flags() & 2) != 0; } /* get the length of my name string */ uint get_sym_len(VMG0_) const { return osrp2(get_symptr(vmg0_)); } /* get a pointer to my name string - this is not null-terminated */ const char *get_sym(VMG0_) const { return get_symptr(vmg0_) + 2; } /* increment this pointer to point to the next symbol in the frame */ void inc(VMG0_) { /* skip the in-line symbol, or the pointer */ if (is_sym_inline()) p_ += get_sym_len(vmg0_) + 2; else p_ += 4; /* skip the header */ p_ += G_dbg_lclsym_hdr_size; } /* is my symbol in-line or in the constant pool? */ int is_sym_inline() const { return !(get_flags() & 0x0004); } /* * Set up a vm_val_t for the symbol. For an in-line symbol, this * creates a new string object; for a constant pool element, this * returns a VM_SSTR pointer to it. */ void get_str_val(VMG_ vm_val_t *val) const; private: /* get my flags value */ uint get_flags() const { return osrp2(p_ + 2); } /* * get a pointer to my symbol data - this points to a UINT2 length * prefix followed by the bytes of the UTF-8 string */ const char *get_symptr(VMG0_) const; /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger frame entry pointer */ class CVmDbgFramePtr { /* let CVmDbgTablePtr initialize us */ friend class CVmDbgTablePtr; public: /* copy from another frame pointer */ void copy_from(const CVmDbgFramePtr *frame) { /* copy the original frame's pointer */ p_ = frame->p_; } /* get the ID of the enclosing frame */ uint get_enclosing_frame() const { return osrp2(p_); } /* get the number of symbols in the frame */ uint get_sym_count() const { return osrp2(p_ + 2); } /* set up a pointer to the first symbol */ void set_first_sym_ptr(VMG_ CVmDbgFrameSymPtr *entry) { entry->set(p_ + G_dbg_frame_size); } /* get the bytecode range covered by the frame */ uint get_start_ofs(VMG0_) { return G_dbg_frame_size >= 8 ? osrp2(p_ + 4) : 0; } uint get_end_ofs(VMG0_) { return G_dbg_frame_size >= 8 ? osrp2(p_ + 6) : 0; } /* is this frame nested in the given frame? */ int is_nested_in(VMG_ const class CVmDbgTablePtr *dp, int i); private: /* initialize with a pointer to the first byte of our entry */ void set(const uchar *p) { p_ = p; } /* pointer to the first byte of our entry */ const uchar *p_; }; /* * Debugger Records Table Pointer */ class CVmDbgTablePtr { public: /* * Initialize with a pointer to the start of the function -- we'll * read the debugger table offset out of the method header. Returns * true if the function has debugger records, false if there is no * debugger table defined in the function. */ int set(const uchar *p) { CVmFuncPtr func; /* if the pointer is null, there's obviously no function pointer */ if (p == 0) return FALSE; /* set up the function pointer */ func.set(p); /* if there's no debugger table, simply return this information */ if (func.get_debug_ofs() == 0) return FALSE; /* set up our pointer by reading from the header */ p_ = p + func.get_debug_ofs(); /* indicate that there is a valid debugger records table */ return TRUE; } /* copy from another debug table pointer */ void copy_from(const CVmDbgTablePtr *table) { /* copy the other table's location */ p_ = table->p_; } /* get the number of source line entries in the table */ size_t get_line_count(VMG0_) const { return osrp2(p_ + G_dbg_hdr_size); } /* get the number of frame entries in the table */ size_t get_frame_count(VMG0_) const { return osrp2(p_ + get_frame_ofs(vmg0_)); } /* initialize a CVmDbgLinePtr with the entry at the given index */ void set_line_ptr(VMG_ CVmDbgLinePtr *entry, size_t idx) const { entry->set(p_ + G_dbg_hdr_size + 2 + (idx * G_line_entry_size)); } /* initialize a CVmDbgFramePtr with the entry at the given index */ void set_frame_ptr(VMG_ CVmDbgFramePtr *entry, size_t idx) const { size_t index_ofs; size_t frame_ofs; /* * Compute the location of the index table entry - note that * 'idx' is a one-based index value, so we must decrement it * before performing our offset arithmetic. Note also that we * must add two bytes to get past the count field at the start * of the frame table. Each index entry is two bytes long. * * (If we were clever, we would distribute the multiply-by-two, * which would yield a constant subtraction of two, which would * cancel the constant addition of two. Let's hope the C++ * catches on to this, because we would rather not be clever and * instead write it explicitly for greater clarity.) */ index_ofs = get_frame_ofs(vmg0_) + 2 + (2 * (idx - 1)); /* read the frame offset from the entry */ frame_ofs = osrp2(p_ + index_ofs); /* * the frame offset in the table is relative to the location of * the table location containing the entry, so add the index * offset to the frame offset to get the actual location of the * frame entry */ entry->set(p_ + index_ofs + frame_ofs); } private: /* get the offset to the start of the frame table */ size_t get_frame_ofs(VMG0_) const { /* * the frame table follows the line records, which follow the * debug table header and the line counter, plus another two * bytes for the post-frame offset pointer */ return (G_dbg_hdr_size + 2 + (get_line_count(vmg0_) * G_line_entry_size) + 2); } /* pointer to the first byte of the debugger records table */ const uchar *p_; }; #endif /* VMFUNC_H */ frobtads-1.2.3/tads3/vminit.h0000644000175000001440000001072010151070636015200 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMINIT.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vminit.h - initialize and terminate a VM session Function Notes VM initialization is configurable via the linker. To select a configuration, link in exactly one of the vmcfgxxx.cpp object files. Modified 04/06/99 MJRoberts - Creation */ #ifndef VMINIT_H #define VMINIT_H #include "vmglob.h" /* * Generic initialization function. Client code calls this function to * initialize the VM. The actual implementation of this function is * selected at link time from the several provided configurations. * * 'charset' is the name of the display character mapping that we should * use. If 'charset' is null, we'll use the default character set * obtained from the OS layer. */ void vm_initialize(struct vm_globals **vmg, const struct vm_init_options *opts); /* * Initialization options - this structure is passed to vm_initialize to * specify the configuration. (We pass this as a structure, rather than as * individual parameters, because we internally have several different * implementations of the initialization function that we choose from based * on the VM link-time configuration. Packaging these arguments as a * structure generally lets us avoid changes to the various implementations * when we add new configuration parameters.) */ struct vm_init_options { vm_init_options(class CVmHostIfc *hi, class CVmMainClientIfc *ci, const char *cs = 0, const char *lcs = 0) { hostifc = hi; clientifc = ci; charset = cs; log_charset = lcs; } /* the host interface */ class CVmHostIfc *hostifc; /* the client interface */ class CVmMainClientIfc *clientifc; /* the display character set (or null to use the OS default) */ const char *charset; /* the log file character set (or null to use the OS default) */ const char *log_charset; }; /* * Initialization, phase 2 - just before loading the image file */ void vm_init_before_load(VMG_ const char *image_filename); /* * Initialization, phase 3 - just after loading the image file */ void vm_init_after_load(VMG0_); /* * Terminate the VM. Deletes all objects created by vm_init(). This * can be used to clean up memory after a program has finished * executing. */ void vm_terminate(struct vm_globals *vmg, class CVmMainClientIfc *clientifc); /* ------------------------------------------------------------------------ */ /* * Private interface. Client code does not call any of the following * routines; they're for use internally by the initialization mechanism * only. */ /* * Initialize the VM with in-memory pools. Creates all global objects * necessary for the VM to run. */ void vm_init_in_mem(struct vm_globals **vmg, const vm_init_options *opts); /* initialize the VM with the "flat" memory pool manager */ void vm_init_flat(struct vm_globals **vmg, const vm_init_options *opts); /* * Initialize the VM with swapping pools with the given maximum number * of pages in memory. */ void vm_init_swap(struct vm_globals **vmg, size_t max_mem_pages, const vm_init_options *opts); /* * Internal base initialization routine. Clients do not call this * routine directly; it's for use by vm_init_in_mem(), vm_init_swap(), * and any other vm_init_xxx routines. */ void vm_init_base(struct vm_globals **vmg, const vm_init_options *opts); /* * Initialize debugger-related objects. For non-debug versions, this * doesn't need to do anything. */ void vm_init_debugger(VMG0_); /* * Get the amount of stack space to allocate as a reserve for handling * stack overflows in the debugger. For non-debug versions, we don't * normally use a reserve, since the reserve is only useful for manually * recovering from stack overflows in the debugger. */ size_t vm_init_stack_reserve(); /* * Termination - shut down the debugger. Notifies the debugger UI that * we're terminating the executable. */ void vm_terminate_debug_shutdown(VMG0_); /* * termination - delete debugger-related objects, if any were allocated * in vm_init_debugger() */ void vm_terminate_debug_delete(VMG0_); #endif /* VMINIT_H */ frobtads-1.2.3/tads3/vmbif.h0000644000175000001440000003670511725134652015017 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmbif.h,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */ /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbif.h - built-in function interface Function Provides the interface to built-in function sets. The host application environment may provide more than one set of built-in functions. Each function set is identified by a universally unique identifier; the program image specifies one or more function sets using these identifiers and maps each set to an image-specific index. At run-time, the byte-code program calls built-in functions by specifying a function set index and a function index within the set. A particular function set may use more than one universal identifier, because of version evolution. If new functions are added to a function set, the new function set can continue to use its original universal identifier, as long as the function set continues to provide the original set of functions as a subset of its new vector, and as long as the functions in the original set are at the same index positions in the modified function set. Notes Modified 12/05/98 MJRoberts - Creation */ #ifndef VMBIF_H #define VMBIF_H #include "t3std.h" #include "vmglob.h" #include "vmtype.h" #include "vmfunc.h" /* ------------------------------------------------------------------------ */ /* * Image function set table. We maintain one global function set table, * which we create when we load the image file. */ class CVmBifTable { public: /* * Create a table with a given number of initial entries. The table * may be expanded in the future if necessary, but if the caller can * predict the maximum number of entries required, we can * preallocate the table at its final size and thus avoid the * overhead and memory fragmentation of expanding the table. */ CVmBifTable(size_t init_entries); /* delete the table */ ~CVmBifTable(); /* clear all entries from the table */ void clear(VMG0_); /* * Add an entry to the table, given the function set identifier (a * string giving the universally unique name for the function set). * Fills in the next available slot. Throws an error if the * function set is not present in the system. A function set may * not be present because it's a newer version than this * implementation provides, or because this particular host * application environment does not provide the function set. */ void add_entry(VMG_ const char *func_set_id); /* get the total number of entries in the table */ size_t get_count() const { return count_; } /* get the descriptor for a function; returns null if it's invalid */ const struct vm_bif_desc *get_desc(uint set_index, uint func_index); /* validate an entry */ int validate_entry(uint set_index, uint func_index); /* get the entry at the given index */ const struct vm_bif_entry_t *get_entry(uint idx) { return idx < count_ ? table_[idx] : 0; } /* * Look up an entry by function set name. If the name has a "/nnnnnn" * version suffix, we'll return null if the loaded version is older * than the requested version. */ const struct vm_bif_entry_t *get_entry(const char *name); /* call the given function from the given function set */ void call_func(VMG_ uint set_index, uint func_index, uint argc); private: /* * Ensure we have space for a given number of entries, allocating * more if necessary. If we must allocate more space, we'll * increase the current allocation size by at least the given * increment (more if necessary to bring it up to the required * size). */ void ensure_space(size_t entries, size_t increment); /* * Add an unresolved function set to the table, or throw an error if * this isn't allowed. If we require resolution of function sets at * load time, this should simply throw an error; otherwise, this * should make a null entry in the function set table so that we can * recognize the missing function set if one of its functions is * ever invoked. */ void add_entry_unresolved(VMG_ const char *func_set_id); /* the table array - we keep an array of pointers */ struct vm_bif_entry_t **table_; /* * Name array - this is a list of the global function set names; * each entry corresponds to the entry of the table_ array at the * same index. We use this only for call-time resolution, so that * we can report the name of an unavailable function set when a * function from the unavailable set is invoked. */ char **names_; /* number of entries defined in the table */ ushort count_; /* number of entries allocated for the table */ size_t alloc_; }; /* ------------------------------------------------------------------------ */ /* * Built-in function descriptor. This tells us how to call a function, and * provides reflection data on the function. */ struct vm_bif_desc { /* * The function entrypoint. * * Each function has a common C interface, because the functions take * arguments and return values on the VM stack. The order of the * functions within the vector is defined by the function set * specification; a function set which uses a particular universal * identifier must always conform to the specification for that * universal identifier. * * For each function, 'argc' is the number of actual parameters passed * to the function by the caller. The function receives its parameters * from the VM stack; the first argument is at the top of the stack, * the second argument is the next item on the stack, and so on. The * function must remove all of the arguments from the stack before * returning, and must push a return value if appropriate. */ void (*func)(VMG_ uint argc); /* minimum number of arguments required */ int min_argc; /* additional optional arguments */ int opt_argc; /* are additional variadic arguments allowed? */ int varargs; /* * VM_BIFPTR value for this function. During image file loading, we * fill this in for each function, so that the function can create a * pointer to itself during execution as needed. */ vm_val_t bifptr; /* * Synthetic method header. We build this during the initialization * process to mimic a byte-code method header. This makes bif pointers * fit in with the rest of the type system by letting CVmFuncPtr pull * out reflection information using the same format as for byte-code * methods. */ char synth_hdr[VMFUNC_HDR_MIN_SIZE]; /* fill in the synthetic header */ void init_synth_hdr() { /* set everything to zeros to start with */ memset(synth_hdr, 0, sizeof(synth_hdr)); /* * Fill in the base arguments field. This is an 8-bit field * containing the minimum number of arguments, with the high bit * (0x80) set if it's varargs. */ synth_hdr[0] = (char)(min_argc | (varargs ? 0x80 : 0)); /* set the optional argument count field */ synth_hdr[1] = (char)opt_argc; /* * We can leave all of the other fields zeroed out: * * - locals, total_stack: the caller doesn't allocate a standard * stack frame for a built-in, so these are unused * * - exception_table_ofs: we don't have any bytecode, so we * obviously don't have a bytecode exception table; leave it zeroed * out to indicate the absence of a table * * - debug_ofs: we don't have any debugger information since we * don't have any bytecode; zero indicates no records */ } }; /* ------------------------------------------------------------------------ */ /* * Function set table entry. This contains information on the function * set at one index in the table. */ struct vm_bif_entry_t { /* * Function set identifier - a string of 7-bit ASCII characters, one * byte per character, in the range 32 to 126 inclusive, of length 1 * to 255 characters, null-terminated. */ const char *func_set_id; /* number of functions in the function set */ size_t func_count; /* the function vector */ vm_bif_desc *func; /* "attach" function for the set - perform static initialization */ void (*attach)(VMG0_); /* "detach" function for the set - perform static cleanup */ void (*detach)(VMG0_); /* link to the image file */ void link_to_image(VMG_ ushort set_idx) { /* initialize the function set */ (*attach)(vmg0_); /* link each function entry */ for (ushort i = 0 ; i < func_count ; ++i) { /* set up the bifptr value for the function */ func[i].bifptr.set_bifptr(set_idx, i); /* generate the synthetic method header for the function */ func[i].init_synth_hdr(); } } /* termination */ void unload_image(VMG_ int set_idx) { /* perform static cleanup */ (*detach)(vmg0_); } }; /* ------------------------------------------------------------------------ */ /* * Base class for function set collections. This class provides some * utility functions that intrinsics might find useful. */ class CVmBif { public: /* * Attach to the function set. The image file loader calls this during * program load for each function set linked into the image file. This * gives the function set a chance to to any static initialization. We * only call this when the function set is actually going to be used, * which avoids unnecessarily allocation resources when the function * set isn't needed. * * Note that this is a static function, but it's kind of like a "class * virtual" in that the loader explicitly calls the suitable * Subclass::attach() and Subclass::detach() for each CVmBif subclass * linked from the image file. If you don't define an overriding * static in a subclass, the compiler will simply link to this base * class method. */ static void attach(VMG0_) { } /* * Detach from the function set. The system calls this during program * termination to let the function set do any static cleanup of * resources allocated during attach(). */ static void detach(VMG0_) { } /* * check arguments; throws an error if the argument count doesn't * match the given value */ static void check_argc(VMG_ uint argc, uint needed_argc); /* * check arguments; throws an error if the argument count is outside * of the given range */ static void check_argc_range(VMG_ uint argc, uint argc_min, uint argc_max); /* pop an integer/long value */ static int pop_int_val(VMG0_); static int32_t pop_long_val(VMG0_); /* pop an integer value, or use a default if the argument is nil */ static int pop_int_or_nil(VMG_ int defval); /* pop a true/nil logical value */ static int pop_bool_val(VMG0_); /* pop an object ID value */ static vm_obj_id_t pop_obj_val(VMG0_); /* pop a property ID value */ static vm_prop_id_t pop_propid_val(VMG0_); /* * Pop a string or list value, returning a pointer to the * string/list data. If the value is a constant string or constant * list, as appropriate, we'll return the constant pool pointer; if * the value is an object of metaclass String or List, as * appropriate, we'll return the metaclass data. Throws an error if * the value is of any other type. */ static const char *pop_str_val(VMG0_); static const char *pop_list_val(VMG0_); /* get a value as a string */ static const char *get_str_val(VMG_ const vm_val_t *val); /* * Pop a null-terminated string into the given buffer. If the * string is too long for the buffer, we'll truncate it to the given * size. */ static void pop_str_val_buf(VMG_ char *buf, size_t buflen); static void get_str_val_buf(VMG_ char *buf, size_t buflen, const vm_val_t *val); /* * Pop a string value, returning the result as a filename in the given * buffer. The returned filename is null-terminated and converted to * the local filename character set. The source value must be a * string. */ static void pop_str_val_fname(VMG_ char *buf, size_t buflen); static void get_str_val_fname(VMG_ char *buf, size_t buflen, const char *strp); /* * Pop a filename value, returning the result as a filename in the * given buffer. The returned filename is null-terminated and * converted to the local filename character set. The source value can * be a string or FileName object. */ static void pop_fname_val(VMG_ char *buf, size_t buflen); static void get_fname_val(VMG_ char *buf, size_t buflen, const vm_val_t *val); /* * Pop a null-terminated string into the given buffer, converting the * string to the UI character set. Null-terminates the resulting * string. If the given buffer is null, we'll allocate a buffer with * t3malloc() and return it; the caller is responsible for freeing the * buffer with t3free(). In any case, returns the buffer into which we * store the results. */ static char *pop_str_val_ui(VMG_ char *buf, size_t buflen); /* create a string object from a C-style string in the UI character set */ static vm_obj_id_t str_from_ui_str(VMG_ const char *str); static vm_obj_id_t str_from_ui_str(VMG_ const char *str, size_t len); /* * Get/pop a CharacterSet argument from the stack. If the argument is * an object, it must be a CharacterSet object. If it's a string, * we'll create a CharacterSet object based on the given character set * name. We'll also allow nil if desired. */ static vm_obj_id_t pop_charset_obj(VMG0_); static vm_obj_id_t get_charset_obj(VMG_ int stk_idx); /* get a character set object from a value */ static vm_obj_id_t get_charset_obj(VMG_ const vm_val_t *val); /* * Return a value */ static void retval(VMG_ const struct vm_val_t *val); static void retval_nil(VMG0_); static void retval_true(VMG0_); static void retval_bool(VMG_ int val); static void retval_int(VMG_ long val); static void retval_obj(VMG_ vm_obj_id_t obj); static void retval_prop(VMG_ vm_prop_id_t prop); static void retval_str(VMG_ const char *str); static void retval_str(VMG_ const char *str, size_t len); static void retval_ui_str(VMG_ const char *str); static void retval_ui_str(VMG_ const char *str, size_t len); static void retval_fnptr(VMG_ pool_ofs_t func); static void retval_bifptr(VMG_ ushort set_idx, ushort func_idx); /* return the value at top of stack */ static void retval_pop(VMG0_); }; #endif /* VMBIF_H */ frobtads-1.2.3/tads3/tcmain.h0000644000175000001440000002344207665437106015171 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCMAIN.H,v 1.3 1999/07/11 00:46:53 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmain.h - TADS 3 Compiler - main compiler driver Function Notes Modified 04/22/99 MJRoberts - Creation */ #ifndef TCMAIN_H #define TCMAIN_H #include #include "t3std.h" #include "tcerr.h" /* * error display option flags */ #define TCMAIN_ERR_VERBOSE 0x00000001 /* use verbose messages */ #define TCMAIN_ERR_NUMBERS 0x00000002 /* include numeric error codes */ #define TCMAIN_ERR_WARNINGS 0x00000004 /* show warnings */ #define TCMAIN_ERR_PEDANTIC 0x00000008 /* show pendantic warnings */ #define TCMAIN_ERR_TESTMODE 0x00000010 /* testing mode */ #define TCMAIN_ERR_FNAME_QU 0x00000020 /* quote filenames */ /* * main compiler driver class */ class CTcMain { public: /* Initialize the compiler. Creates all global objects. */ static void init(class CTcHostIfc *hostifc, class CResLoader *res_loader, const char *default_charset); /* Terminate the compiler. Deletes all global objects. */ static void terminate(); /* initialize/terminate the error subsystem */ static void tc_err_init(size_t param_stack_size, class CResLoader *res_loader); static void tc_err_term(); /* log an error - varargs arguments - static routine */ static void S_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err, ...) { va_list args; /* pass through to our va_list-style handler */ va_start(args, err); S_v_log_error(linedesc, linenum, err_counter, warn_counter, 0, 0, options, suppress_list, suppress_cnt, severity, err, args); va_end(args); } /* log an error - arguments from a CVmException object */ static void S_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, struct CVmException *exc); /* show or don't show numeric error codes */ void set_show_err_numbers(int show) { if (show) err_options_ |= TCMAIN_ERR_NUMBERS; else err_options_ &= ~TCMAIN_ERR_NUMBERS; } /* set verbosity for error messages */ void set_verbosity(int verbose) { if (verbose) err_options_ |= TCMAIN_ERR_VERBOSE; else err_options_ &= ~TCMAIN_ERR_VERBOSE; } /* turn all warnings on or off */ void set_warnings(int show) { if (show) err_options_ |= TCMAIN_ERR_WARNINGS; else err_options_ &= ~TCMAIN_ERR_WARNINGS; } /* turn pedantic warnings on or off */ void set_pedantic(int show) { if (show) err_options_ |= TCMAIN_ERR_PEDANTIC; else err_options_ &= ~TCMAIN_ERR_PEDANTIC; } /* * Get/set regression test reporting mode. In test mode, we'll only * show the root name of each file in our status reports and error * messages. This is useful when running regression tests, because it * allows us to diff the compiler output against a reference log * without worrying about the local directory structure. */ int get_test_report_mode() const { return (err_options_ & TCMAIN_ERR_TESTMODE) != 0; } void set_test_report_mode(int flag) { if (flag) err_options_ |= TCMAIN_ERR_TESTMODE; else err_options_ &= ~TCMAIN_ERR_TESTMODE; } /* * Set the quoted filenames option. If this option is set, we'll quote * the filenames we report in our error messages; this makes it easier * for automated tools to parse the error messages, because it ensures * that the contents of a filename won't be mistaken for part of the * error message format. */ void set_quote_filenames(int flag) { if (flag) err_options_ |= TCMAIN_ERR_FNAME_QU; else err_options_ &= ~TCMAIN_ERR_FNAME_QU; } /* * Set an array of warning codes to suppress. We will suppress any * warnings and pedantic warnings whose error numbers appear in this * array; errors of greater severity will be shown even when they * appear here. * * The memory of this list is managed by the caller. We merely keep a * reference to the caller's array. The caller is responsible for * ensuring that the memory remains valid for as long as we're around. */ void set_suppress_list(const int *lst, size_t cnt) { /* remember the list and its size */ suppress_list_ = lst; suppress_cnt_ = cnt; } /* log an error - va_list-style arguments - static routine */ static void S_v_log_error(class CTcTokFileDesc *linedesc, long linenum, int *err_counter, int *warn_counter, int *first_error, int *first_warning, unsigned long options, const int *suppress_list, size_t suppress_cnt, tc_severity_t severity, int err, va_list args); /* log an error - varargs */ void log_error(class CTcTokFileDesc *linedesc, long linenum, tc_severity_t severity, int err, ...) { va_list args; /* pass through to our va_list-style handler */ va_start(args, err); v_log_error(linedesc, linenum, severity, err, args); va_end(args); } void v_log_error(class CTcTokFileDesc *linedesc, long linenum, tc_severity_t severity, int err, va_list args) { /* call our static routine */ S_v_log_error(linedesc, linenum, &error_count_, &warning_count_, &first_error_, &first_warning_, err_options_, suppress_list_, suppress_cnt_, severity, err, args); /* if we've exceeded the maximum error limit, throw a fatal error */ check_error_limit(); } /* get the error/warning count */ int get_error_count() const { return error_count_; } int get_warning_count() const { return warning_count_; } /* reset the error and warning counters */ void reset_error_counts() { /* clear the counters */ error_count_ = 0; warning_count_ = 0; /* clear the error/warning memories */ first_error_ = 0; first_warning_ = 0; } /* * get the first compilation error/warning code - we keep track of * these for times when we have limited error reporting capabilities * and can only report a single error, such as when we're compiling * an expression in the debugger */ int get_first_error() const { return first_error_; } int get_first_warning() const { return first_warning_; } /* * check the error count against the error limit, and throw a fatal * error if we've exceeded it */ void check_error_limit(); /* get the console output character mapper */ static class CCharmapToLocal *get_console_mapper() { return console_mapper_; } private: CTcMain(class CResLoader *res_loader, const char *default_charset); ~CTcMain(); /* error and warning count */ int error_count_; int warning_count_; /* first error/warning code we've encountered */ int first_error_; int first_warning_; /* * Maximum error limit - if we encounter more than this many errors * in a single compilation unit, we'll abort the compilation with a * fatal error. This at least limits the amount of garbage we'll * display if we run into a really bad cascading parsing error * situation where we just can't resynchronize. */ int max_error_count_; /* error options - this is a combination of TCMAIN_ERR_xxx flags */ unsigned long err_options_; /* * An array of warning messages to suppress. We'll only use this to * suppress warnings and pedantic warnings; errors of greater severity * will be displayed even if they appear in this list. The memory used * for this list is managed by our client; we just keep a reference to * the client's list. */ const int *suppress_list_; size_t suppress_cnt_; /* resource loader */ class CResLoader *res_loader_; /* default character set name */ char *default_charset_; /* * flag: we have tried loading an external compiler error message * file and failed; if we fail once during a session, we won't try * again, to avoid repeated searches for a message file during * compilations of multiple files */ static int err_no_extern_messages_; /* count of references to the error subsystem */ static int err_refs_; /* * The console character mapper. We use this to map error messages * to the console character set. This is a static so that we can * access it from the static error message printer routines. */ static class CCharmapToLocal *console_mapper_; }; #endif /* TCMAIN_H */ frobtads-1.2.3/tads3/vmregex.h0000644000175000001440000012332011777377130015366 0ustar realncusers/* * Copyright (c) 1998, 2012 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmregex.h - regular expression parser for T3 Function Notes Adapted from the TADS 2 regular expression parser. This version uses UTF-8 strings rather than simple single-byte character strings, and is organized into a C++ class. Modified 04/11/99 CNebel - Fix warnings. 10/07/98 MJRoberts - Creation */ #ifndef VMREGEX_H #define VMREGEX_H #include #include "t3std.h" #include "vmuni.h" #include "utf8.h" #include "vmerr.h" #include "vmerrnum.h" /* state ID */ typedef int re_state_id; /* invalid state ID - used to mark null machines */ #define RE_STATE_INVALID ((re_state_id)-1) /* first valid state ID */ #define RE_STATE_FIRST_VALID ((re_state_id)0) /* ------------------------------------------------------------------------ */ /* * forward declarations */ typedef struct regex_scan_frame regex_scan_frame; /* ------------------------------------------------------------------------ */ /* * Group register structure. Each register keeps track of the starting * and ending offset of the group's text within the original search * string. */ struct re_group_register { int start_ofs; int end_ofs; }; /* maximum number of group registers we keep */ #define RE_GROUP_REG_CNT 10 /* maximum group nesting depth */ #define RE_GROUP_NESTING_MAX 20 /* * the maximum number of separate loop variables we need is the same as * the group nesting level, since we only need one loop variable per * nested group */ #define RE_LOOP_VARS_MAX RE_GROUP_NESTING_MAX /* ------------------------------------------------------------------------ */ /* * Recognizer types. */ enum re_recog_type { /* invalid/uninitialized */ RE_INVALID, /* literal (single) character recognizer */ RE_LITERAL, /* literal string recognizer */ RE_LITSTR, /* aliased literal string (points to string allocated in another state) */ RE_LITSTRA, /* "epsilon" recognizer - match without consuming anything */ RE_EPSILON, /* wildcard character */ RE_WILDCARD, /* beginning and end of text */ RE_TEXT_BEGIN, RE_TEXT_END, /* * same position as parent lookback assertion - this is used for * lookback assertions to assert that the assertion ends at the point * where the assertion occurs in the match string */ RE_LOOKBACK_POS, /* start and end of a word */ RE_WORD_BEGIN, RE_WORD_END, /* word-char and non-word-char */ RE_WORD_CHAR, RE_NON_WORD_CHAR, /* word-boundary and non-word-boundary */ RE_WORD_BOUNDARY, RE_NON_WORD_BOUNDARY, /* a character range/exclusion range */ RE_RANGE, RE_RANGE_EXCL, /* group entry/exit transition */ RE_GROUP_ENTER, RE_GROUP_EXIT, /* * group matcher - the character code has the group number (0 for * group 0, etc) rather than a literal to match */ RE_GROUP_MATCH, /* any alphabetic character */ RE_ALPHA, /* any digit */ RE_DIGIT, RE_NON_DIGIT, /* any upper-case alphabetic */ RE_UPPER, /* any lower-case alphabetic */ RE_LOWER, /* any alphanumeric */ RE_ALPHANUM, /* space character */ RE_SPACE, RE_NON_SPACE, /* punctuation character */ RE_PUNCT, /* newline character */ RE_NEWLINE, /* null character (used in range recognizers) */ RE_NULLCHAR, /* positive look-ahead assertion */ RE_ASSERT_POS, /* negative look-ahead assertion */ RE_ASSERT_NEG, /* positive look-back assertion */ RE_ASSERT_BACKPOS, /* negative look-back assertion */ RE_ASSERT_BACKNEG, /* loop entry: zero the associated loop variable */ RE_ZERO_VAR, /* loop branch: inspect loop criteria and branch accordingly */ RE_LOOP_BRANCH, /* vertical whitespace */ RE_VSPACE, RE_NON_VSPACE }; /* ------------------------------------------------------------------------ */ /* * Denormalized state transition tuple. Each tuple represents the * complete set of transitions out of a particular state. A particular * state can have one character transition, or two epsilon transitions. * Note that we don't need to store the state ID of the tuple itself in * the tuple, because the state ID is the index of the tuple in an array * of state tuples. */ struct re_tuple { /* recognizer type */ re_recog_type typ; /* the character we must match to transition to the target state */ union { /* * if this is a character transition, this is the character (used * as the character literal in RE_LITERAL, and as the group ID in * RE_GROUP_MATCH and in RE_EPSILON nodes with the group flag set) */ wchar_t ch; /* * If this is a character string transition, this is the string; * it's stored as a null-terminated string of wchar_t's, allocated * with new wchar_t[]. 'src' is the original state number for an * aliased string that points to another state's original. */ struct { wchar_t *str; re_state_id src; } str; /* * if this has a sub-machine, this is the start and end info (used * for the assertion entry states: RE_ASSERT_POS, RE_ASSERT_NEG, * RE_ASSERT_BACKPOS, RE_ASSERT_BACKNEG) */ struct { /* sub-machine start/end states */ re_state_id init; re_state_id final; /* for look-back assertions, the match length range */ int minlen; int maxlen; } sub; /* * if this is a loop, the loop parameters (used for RE_ZERO_VAR, * RE_LOOP_BRANCH) */ struct { int loop_min; int loop_max; int loop_var; } loop; /* * Character range match table - this is used if the recognizer * type is RE_RANGE or RE_RANGE_EXCL; for other recognizer types, * this is not used. * * If used, this is an array of pairs of characters. In each pair, * the first is the low end of the range, and the second is the * high end of the range, both ends inclusive. A single character * takes up two entries, both identical, to specify a range of only * one character. * * If the first character is '\0', then neither wchar_t is a * character in the ordinary sense described above. Instead, the * second wchar_t is actually one of the recognizer type codes * (re_recog_type) for a character class (RE_ALPHA, RE_DIGIT, etc). * The pair in this case is to be taken to match (or exclude) the * entire class. * * To represent a match for '\0', use '\0' for the first wchar_t * and RE_NULLCHAR for the second wchar_t. Note that the special * meaning of '\0' in the first character of a pair makes it * impossible to represent a range including a null byte with a * single pair; instead, representing a range like [\000-\017] * requires two pairs: the first pair is ('\0', RE_NULLCHAR), and * the second pair is ('\001', '\017'). */ struct { wchar_t *char_range; size_t char_range_cnt; } range; } info; /* the target states */ re_state_id next_state_1; re_state_id next_state_2; /* flags */ unsigned char flags; }; /* * Tuple flags */ /* this state is being tested for a cycle */ #define RE_STATE_CYCLE_TEST 0x08 /* * for branching states: take the shortest, rather than longest, branch * when both branches are successful */ #define RE_STATE_SHORTEST 0x10 /* ------------------------------------------------------------------------ */ /* * A "machine" description. A machines is fully described by its initial * and final state ID's. */ struct re_machine { /* the machine's initial state */ re_state_id init; /* the machine's final state */ re_state_id final; }; /* ------------------------------------------------------------------------ */ /* * Compiled pattern description. This is not a complete compiled pattern, * since the tuple array is separate; this is just a description of the * compiled pattern that can be combined with the tuple array to form a * full compiled pattern. */ struct re_compiled_pattern_base { /* the pattern's machine description */ re_machine machine; /* the number of tuples in the tuple array */ re_state_id tuple_cnt; /* number of capturing groups in the expression */ int group_cnt; /* maximum number of looping variables in the expression */ int loop_var_cnt; /* * or mode. If this flag is clear, the search is not * case-sensitive, so alphabetic characters in the pattern are matched * without regard to case. */ unsigned int case_sensitive : 1; /* is the case sensitivity explicit or defaulted? */ unsigned int case_sensitivity_specified : 1; /* * or match mode -- if this flag is set, we match the * longest string in case of ambiguity; otherwise we match the * shortest. */ unsigned int longest_match : 1; /* * or match mode -- if this flag is set, we * match (in a search) the string that starts first in case of * ambiguity; otherwise, we match the string that ends first */ unsigned int first_begin : 1; }; /* * Compiled pattern object. This is a pattern compiled and saved for use * in searches and matches. This is a compiled pattern description * coupled with its tuple array, which in combination provide a complete * compiled pattern. */ struct re_compiled_pattern: re_compiled_pattern_base { /* * the tuple array (the structure is overallocated to make room for * tuple_cnt entries in this array) */ re_tuple tuples[1]; }; /* ------------------------------------------------------------------------ */ /* * Status codes */ typedef enum { /* success */ RE_STATUS_SUCCESS = 0, /* compilation error - group nesting too deep */ RE_STATUS_GROUP_NESTING_TOO_DEEP } re_status_t; /* ------------------------------------------------------------------------ */ /* * Regular expression compilation context structure. This tracks the * state of the compilation and stores the resources associated with the * compiled expression. */ class CRegexParser { friend class CRegexSearcher; friend class CRegexSearcherSimple; public: /* initialize */ CRegexParser(); /* delete */ ~CRegexParser(); /* * Compile an expression and create a compiled pattern object, filling * in *pattern with a pointer to the newly-allocated pattern object. * The caller is responsible for freeing the pattern by calling * free_pattern(pattern). */ re_status_t compile_pattern(const char *expr_str, size_t exprlen, re_compiled_pattern **pattern); /* free a pattern previously created with compile_pattern() */ static void free_pattern(re_compiled_pattern *pattern); protected: /* reset the parser */ void reset(); /* allocate a new state ID */ re_state_id alloc_state(); /* set a transition from a state to a given destination state */ void set_trans(re_state_id id, re_state_id dest_id, re_recog_type typ, wchar_t ch); /* initialize a new machine, setting up the initial and final state */ void init_machine(struct re_machine *machine); /* build a character recognizer */ void build_char(struct re_machine *machine, wchar_t ch); /* build a special recognizer */ void build_special(struct re_machine *machine, re_recog_type typ, wchar_t ch); /* build a character range recognizer */ void build_char_range(struct re_machine *machine, int exclusion); /* build a group recognizer */ void build_group_matcher(struct re_machine *machine, int group_num); /* build a concatenation recognizer */ void build_concat(struct re_machine *new_machine, struct re_machine *lhs, struct re_machine *rhs); /* build a group machine */ void build_group(struct re_machine *new_machine, struct re_machine *sub_machine, int group_id); /* build a positive or negative assertion machine */ void build_assert(struct re_machine *new_machine, struct re_machine *sub_machine, int is_negative, int is_lookback); /* build an alternation recognizer */ void build_alter(struct re_machine *new_machine, struct re_machine *lhs, struct re_machine *rhs); /* build a closure recognizer */ void build_closure(struct re_machine *new_machine, struct re_machine *sub, wchar_t specifier, int shortest); /* build an interval matcher */ void build_interval(struct re_machine *new_machine, struct re_machine *sub, int min_val, int max_val, int var_id, int shortest); /* build a null machine */ void build_null_machine(struct re_machine *machine); /* determine if a machine is null */ int is_machine_null(struct re_machine *machine); /* concate the second machine onto the first machine */ void concat_onto(struct re_machine *dest, struct re_machine *rhs); /* alternate the second machine onto the first */ void alternate_onto(struct re_machine *dest, struct re_machine *rhs); /* compile an expression */ re_status_t compile(const char *expr_str, size_t exprlen, re_compiled_pattern_base *pat); /* compile a character class or class range expression */ int compile_char_class_expr(utf8_ptr *expr, size_t *exprchars, re_machine *result_machine); /* parse an integer value */ int parse_int(utf8_ptr *p, size_t *chars_rem); /* add a character to our range buffer */ void add_range_char(wchar_t ch) { add_range_char(ch, ch); } void add_range_char(wchar_t ch_lo, wchar_t ch_hi); /* add a character class to our range buffer */ void add_range_class(re_recog_type cl); /* ensure space in the range buffer for another entry */ void ensure_range_buf_space(); /* break any infinite loops in the machine */ void break_loops(re_machine *machine); /* get the match length for a state */ void get_match_length(re_state_id init, re_state_id final, int *minlen, int *maxlen, regex_scan_frame *stack); /* find an infinite loop back to the given state */ int break_loops(re_state_id init, re_state_id final, regex_scan_frame *stack); /* optimize away meaningless branch-to-branch transitions */ void remove_branch_to_branch(re_machine *machine); void optimize_transition(const re_machine *machine, re_state_id *trans); /* consolidate runs of characters into strings */ void consolidate_strings(re_machine *machine); /* next available state ID */ re_state_id next_state_; /* * The array of transition tuples. We'll allocate this array and * expand it as necessary. */ re_tuple *tuple_arr_; /* number of transition tuples allocated in the array */ int tuples_alloc_; /* buffer for building range exprssions */ wchar_t *range_buf_; /* current number of entries in range buffer */ size_t range_buf_cnt_; /* maximum number of entries in range buffer */ size_t range_buf_max_; }; /* ------------------------------------------------------------------------ */ /* * Pattern recognizer state stack. Each time we need to process a * sub-state (a two-way epsilon, or a nested assertion), we stack the * current state so that we can backtrack when we're done with the * sub-expression. The state we store consists of: * * - backtrack type - this is an arbitrary uchar identifier that the * pattern matcher uses to identify where to go when we pop the state * * - the current state ID * * - the current offset in the string being matched * * - the state ID of the terminating state of the machine * * - saved group registers; we only store the ones we've actually * modified, to avoid unnecessary copying * * - saved loop variables; we only store the ones we've actually modified, * to avoid unnecessary copying */ enum regex_frame_type { ST_EPS1 = 1, /* doing first branch of two-branch epsilon */ ST_EPS2 = 2, /* doing second branch of two-branch epsilon */ ST_ASSERT = 3 /* doing an assertion */ }; /* the base structure for a stacked state */ struct regex_stack_entry { /* the backtrack type identifier */ regex_frame_type typ; /* the starting offset in the string */ int start_ofs; /* the current offset in the string */ int str_ofs; /* the length of the search portion of the string */ int curlen; /* the pattern state */ re_state_id state; /* the final state of the machine */ re_state_id final; /* the return value for this state */ int retval; /* * The look-back iteration. For a look-back assertion, we have to * check each starting point over the range of possible match lengths, * until we've either found a success condition or exhausted the range * of possible match lengths. This keeps track of where we are in the * iteration. */ int iter; /* stack offset of previous frame */ int prv_sp; }; /* saved group/loop entry */ struct regex_stack_var { /* * The ID - this is in the range 0..RE_GROUP_REG_CNT-1 for group * registers, RE_GROUP_REG_CNT..RE_GROUP_REG_CNT+RE_LOOP_VARS_MAX-1 * for loop variables. In other words, a loop variable is identified * by its loop variable number plus RE_GROUP_REG_CNT. The special ID * value -1 indicates the integer 'retval' value (a saved return value * for the stack state). */ int id; /* the value */ union { re_group_register group; short loopvar; int retval; } val; }; /* state stack class */ class CRegexStack { friend class reg_deltas; public: CRegexStack() { /* allocate the initial state buffer */ bufsiz_ = 8192; buf_ = (char *)t3malloc(bufsiz_); /* we don't have anything on the stack yet */ sp_ = -1; used_ = 0; } ~CRegexStack() { /* delete the stack buffer */ t3free(buf_); } /* reset the stack */ void reset() { /* empty the stack */ sp_ = -1; used_ = 0; } /* push a new state */ void push(regex_frame_type typ, int start_ofs, int str_ofs, int curlen, re_state_id state, re_state_id final, int iter) { /* * Ensure we have enough space for the base state structure plus a * full complement of group registers, loop variables, and return * value. We might not actually need all of the registers and * loop variables, so we won't commit all of this space yet, but * check in advance to make sure we have it so that we don't have * to check again when and if we get around to consuming * group/loop slots. */ ensure_space(sizeof(regex_stack_entry) + ((RE_GROUP_REG_CNT + RE_LOOP_VARS_MAX + 1) *sizeof(regex_stack_var))); /* allocate the base stack frame */ regex_stack_entry *fp = (regex_stack_entry *)alloc_space(sizeof(regex_stack_entry)); /* set it up */ fp->typ = typ; fp->start_ofs = start_ofs; fp->str_ofs = str_ofs; fp->curlen = curlen; fp->state = state; fp->final = final; fp->iter = iter; /* push it onto the stack */ fp->prv_sp = sp_; sp_ = (char *)fp - buf_; } /* save a group register */ void save_group_reg(int id, const re_group_register *regs) { /* allocate a new slot if needed and save the value */ regex_stack_var *var; if (sp_ != -1 && (var = new_reg_or_var(id)) != 0) var->val.group = regs[id]; } /* save a loop variable */ void save_loop_var(int id, const short *loop_vars) { /* * allocate a new slot if needed and save the value; note that * loop variables are identified by the loop variable ID plus the * base index RE_GROUP_REG_CNT */ regex_stack_var *var; if (sp_ != -1 && (var = new_reg_or_var(id + RE_GROUP_REG_CNT)) != 0) var->val.loopvar = loop_vars[id]; } /* propagate a group register or loop variable from another frame */ void propagate(const regex_stack_var *src) { regex_stack_var *dst; if (sp_ != -1 && (dst = new_reg_or_var(src->id)) != 0) memcpy(dst, src, sizeof(*dst)); } /* * get the type of the state at top of stack; if there is no state, * returns -1 */ int get_top_type() { /* * if there's nothing on the stack, so indicate, otherwise get the * type from the top stack element */ if (sp_ == -1) return -1; else return ((regex_stack_entry *)(buf_ + sp_))->typ; } /* get the stack frame at the given depth (0 is top of stack) */ regex_stack_entry *get_frame(int depth) { /* traverse the given number of frames from the top of the stack */ regex_stack_entry *fp; for (fp = (regex_stack_entry *)(buf_ + sp_) ; depth != 0 && fp != 0 ; --depth, fp = get_parent_frame(fp)) ; /* return the frame pointer */ return fp; } /* * Get the parent lookback assertion match position. This scans up the * stack for the nearest assertion frame, and returns its match * position. */ int get_lookback_pos() { /* scan up the stack for a lookback frame */ for (regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_) ; fp != 0 ; fp = get_parent_frame(fp)) { /* if this is an assert frame, return the position */ if (fp->typ == ST_ASSERT) return fp->str_ofs; } /* didn't find it */ return -1; } /* get the parent stack entry */ regex_stack_entry *get_parent_frame(regex_stack_entry *fp) { if (fp->prv_sp < 0) return 0; else return (regex_stack_entry *)(buf_ + fp->prv_sp); } /* pop a state */ void pop(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars, int *iter) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* restore the string offset and state ID */ *start_ofs = fp->start_ofs; *str_ofs = fp->str_ofs; *curlen = fp->curlen; *state = fp->state; *final = fp->final; *iter = fp->iter; /* run through the saved registers/variables in the state */ for (regex_stack_var *var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* sense the type */ if (var->id < RE_GROUP_REG_CNT) { /* it's a group register */ regs[var->id] = var->val.group; } else { /* it's a loop variable */ loop_vars[var->id - RE_GROUP_REG_CNT] = var->val.loopvar; } } /* we're done with the stop stack frame, so discard it */ discard(); } /* discard the top stack state */ void discard() { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* unwind the stack */ used_ = (size_t)sp_; sp_ = fp->prv_sp; } /* * Swap the current state with the state at the top of the stack, and * push a second copy of the restored state. This is used to traverse * the second branch of a two-branch epsilon: we first have to save the * results of the first branch, including the return value and its * registers, and we then have to restore the initial register/loop * state as it was before the first branch. * * We save the final state and restore the initial state by swapping * the group registers in the saved state with those in the current * state. This brings back the initial conditions to the current * machine state, while saving everything that's changed in the current * machine state in the stack frame. We'll likewise swap the machine * state and string offset. Later, this same final machine state can * be restored by first restoring the machine state to the initial * state, then popping this frame. * * On return, the stack frame that was active on entry will be set to * contain the current machine state, and the current machine state * will be replaced with what was in that stack frame. In addition, * we'll have pushed a new stack frame for the new current machine * state. */ void swap_and_push(int retval, regex_frame_type typ, int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars) { /* swap the current state wtih the top of stack */ swap(start_ofs, str_ofs, curlen, state, final, regs, loop_vars); /* save the return value from the outgoing state */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); fp->retval = retval; /* push a copy of the restored state */ push(typ, *start_ofs, *str_ofs, *curlen, *state, *final, 0); } /* * Swap the current active state with the top of stack, then pop the * frame. */ void swap_and_pop(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars, int *iter) { /* swap the current state wtih the top of stack */ swap(start_ofs, str_ofs, curlen, state, final, regs, loop_vars); /* save the return value from the outgoing state */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); *iter = fp->iter; /* discard the frame */ discard(); } /* * Swap the current state with the top stack frame */ void swap(int *start_ofs, int *str_ofs, size_t *curlen, re_state_id *state, re_state_id *final, re_group_register *regs, short *loop_vars) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* swap the string offset */ int tmp_ofs = *str_ofs; *str_ofs = fp->str_ofs; fp->str_ofs = tmp_ofs; /* swap the starting offset */ tmp_ofs = *start_ofs; *start_ofs = fp->start_ofs; fp->start_ofs = tmp_ofs; /* swap the search length */ size_t tmp_len = *curlen; *curlen = fp->curlen; fp->curlen = tmp_len; /* swap the current machine state */ re_state_id tmp_id = *state; *state = fp->state; fp->state = tmp_id; /* swap the final machine state */ tmp_id = *final; *final = fp->final; fp->final = tmp_id; /* swap all group and loop registers with the current state */ for (regex_stack_var *var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* sense the type */ if (var->id < RE_GROUP_REG_CNT) { re_group_register tmp; /* it's a group register */ tmp = regs[var->id]; regs[var->id] = var->val.group; var->val.group = tmp; } else { short tmp; /* it's a loop variable */ tmp = loop_vars[var->id - RE_GROUP_REG_CNT]; loop_vars[var->id - RE_GROUP_REG_CNT] = var->val.loopvar; var->val.loopvar = tmp; } } } protected: /* * allocate a new register or group variable in the stack frame; if we * find an existing copy of the same variable, we'll return null to * indicate that we don't have to save it again */ regex_stack_var *new_reg_or_var(int id) { /* get the stack pointer */ regex_stack_entry *fp = (regex_stack_entry *)(buf_ + sp_); /* scan the frame for a register/variable with this ID */ regex_stack_var *var; for (var = (regex_stack_var *)(fp + 1) ; var < (regex_stack_var *)(buf_ + used_) ; ++var) { /* if this is the one, we don't need to save it again */ if (var->id == id) return 0; } /* we didn't find it, so return a new entry with the given ID */ var = (regex_stack_var *)alloc_space(sizeof(regex_stack_var)); var->id = id; return var; } /* ensure space in our stack buffer */ void ensure_space(size_t siz) { /* if it's within range, we're fine */ if (used_ + siz <= bufsiz_) return; /* expand */ bufsiz_ += 8192; /* if it's too large, throw an error */ if (bufsiz_ > OSMALMAX) err_throw(VMERR_OUT_OF_MEMORY); /* reallocate at the new size */ buf_ = (char *)t3realloc(buf_, bufsiz_); /* make sure we're not out of memory */ if (buf_ == 0) err_throw(VMERR_OUT_OF_MEMORY); } /* * allocate space - the caller must have already checked that space is * available */ char *alloc_space(size_t siz) { /* figure out where the new object goes */ char *ret = buf_ + used_; /* consume the space */ used_ += siz; /* return the allocated space */ return ret; } /* the stack buffer */ char *buf_; size_t bufsiz_; /* offset of current stack frame */ int sp_; /* number of bytes used so far */ size_t used_; }; /* ------------------------------------------------------------------------ */ /* * Regular Expression Searcher/Matcher. This object encapsulates the * group registers associated with a search. */ class CRegexSearcher { public: CRegexSearcher(); ~CRegexSearcher(); /* * Search for a compiled pattern. Returns the byte offset of the * match, or -1 if no match was found. *result_len is filled in with * the byte length of the match if we found one. Note that the * returned index and result_len values are byte lengths, not * character lengths. * * The caller is responsible for providing a set of group registers, * which must be an array of registers of size RE_GROUP_REG_CNT. The * caller also must save the original search string if it will be * necessary to extract substrings based on the group registers. */ int search_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs); /* * Search backwards for a compiled pattern. This is similar to * search_for_pattern(), but searches backwards from the starting * position. The match must end BEFORE the character at the starting * position; the match can't overlap or include that character. * * On success, the return value is non-negative, and gives the number * of bytes before the starting position where the match begins. To * get the starting byte pointer, subtract the return value from the * 'searchstr' pointer. On failure, returns -1. */ int search_back_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len, re_group_register *regs); /* * Check for a match to a previously compiled expression. Returns the * length of the match if we found a match, -1 if we found no match. * This is not a search function; we merely match the leading * substring of the given string to the given pattern. Note that the * returned length is a byte length, not a character length. * * The caller is responsible for providing a set of group registers, * which must be an array of registers of size RE_GROUP_REG_CNT. The * caller also must save the original search string if it will be * necessary to extract substrings based on the group registers. */ int match_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, re_group_register *regs); /* * Get/set the default case sensititivy for searching and matching. * This controls the case sensitivity for patterns that don't include * explicit or flags. Explicit flags in the pattern * override this default. */ int get_default_case_sensitive() const { return default_case_sensitive_; } void set_default_case_sensitive(int f) { default_case_sensitive_ = f; } protected: /* match a string to a compiled expression */ int match(const char *entire_str, size_t entire_len, const char *str, size_t origlen, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, short *loop_vars); /* search for a regular expression within a string */ int search(const char *entire_str, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, int *result_len); /* search backwards for a regular expression within a string */ int search_back(const char *entire_str, const char *str, size_t len, const re_compiled_pattern_base *pattern, const re_tuple *tuple_arr, const struct re_machine *machine, re_group_register *regs, int *result_len); /* clear a set of group registers */ void clear_group_regs(re_group_register *regs) { /* set the start and end offsets for all registers to -1 */ int i; re_group_register *r; for (r = regs, i = 0 ; i < RE_GROUP_REG_CNT ; ++i, ++r) r->start_ofs = r->end_ofs = -1; } /* * Determine if a character is part of a word. We consider letters * and numbers to be word characters. */ static int is_word_char(wchar_t c) { return (t3_is_alpha(c) || t3_is_digit(c)); } /* match state stack */ CRegexStack stack_; /* default case sensitivity, for patterns that don't specify it */ unsigned int default_case_sensitive_ : 1; }; /* * Simplified Searcher - this class provides some high-level methods that * simplify one-off searches that combine compilation and searching into * one step. */ class CRegexSearcherSimple: public CRegexSearcher { public: CRegexSearcherSimple(class CRegexParser *parser) { /* remember my parser */ parser_ = parser; } ~CRegexSearcherSimple() { } /* clear out our group registers */ void clear_group_regs() { /* clear the last match register */ match_.start_ofs = match_.end_ofs = -1; /* clear the actual group registers */ CRegexSearcher::clear_group_regs(regs_); } /* search for a pattern, using our internal group registers */ int search_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::search_for_pattern( pattern, entirestr, searchstr, searchlen, result_len, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = m; match_.end_ofs = m + *result_len; } /* return the result */ return m; } /* search backwards for a pattern, using internal group registers */ int search_back_for_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::search_back_for_pattern( pattern, entirestr, searchstr, searchlen, result_len, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr - m; match_.end_ofs = match_.start_ofs + *result_len; } /* return the result */ return m; } /* match a pattern, using our internal group registers */ int match_pattern(const re_compiled_pattern *pattern, const char *entirestr, const char *searchstr, size_t searchlen) { /* remember the group count from the compiled pattern */ group_cnt_ = pattern->group_cnt; /* clear the group registers */ clear_group_regs(); /* search for the compiled pattern using our group register */ int m = CRegexSearcher::match_pattern( pattern, entirestr, searchstr, searchlen, regs_); /* save the match information on success */ if (m >= 0) { match_.start_ofs = searchstr - entirestr; match_.end_ofs = match_.start_ofs + m; } /* return the result */ return m; } /* * Compile an expression and search for a match within the given * string. Returns the byte offset of the match, or -1 if no match * was found. *result_len is filled in with the byte length of the * match if we found one. Note that the returned index and result_len * values are byte lengths, not character lengths. */ int compile_and_search(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len); /* compile and search backwards */ int compile_and_search_back(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen, int *result_len); /* * Compile an expression and check for a match. Returns the byte * length of the match if we found a match, -1 if we found no match. * This is not a search function; we merely match the leading * substring of the given string to the given pattern. Note that the * returned length is a byte length, not a character length. */ int compile_and_match(const char *pattern, size_t patlen, const char *entirestr, const char *searchstr, size_t searchlen); /* * Get a group register. 0 refers to the first group; groups are * numbered in left-to-right order by their opening parenthesis. */ const re_group_register *get_group_reg(int i) const { return ®s_[i]; } /* get the last match */ const re_group_register *get_last_match() const { return &match_; } /* get the number of groups in the last pattern we searched */ int get_group_cnt() const { return group_cnt_; } /* copy group registers from another search */ void copy_group_regs(CRegexSearcherSimple *s) { memcpy(regs_, s->regs_, sizeof(regs_)); group_cnt_ = s->group_cnt_; } protected: /* group registers */ re_group_register regs_[RE_GROUP_REG_CNT]; /* last match location */ re_group_register match_; /* number of groups in last pattern we searched */ int group_cnt_; /* my regular expression parser */ class CRegexParser *parser_; }; #endif /* VMREGEX_H */ frobtads-1.2.3/tads3/vmstrcmp.h0000644000175000001440000002763611215237703015565 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmstrcmp.h - T3 String Comparator intrinsic class Function Defines the String Comparator intrinsic class, which provides native code that performs complex, parameterized string comparisons. We offer the following customizable options for our comparisons: - We can match exactly on case, or without regard to case. - We can optionally match a value to a truncated reference value (which allows user input to use abbreviated forms of dictionary words, for example). The minimum truncation length is a settable option. - We can use equivalence mappings that allow a given character in a reference string to match different characters in value strings. For example, we could specify that an "a" with an acute accent in a reference string matches an unaccented "a" in a value string. Each such mapping can specify result flag bits, so a caller can determine if particular equivalence mappings were used in making a match. This class implements the generic "comparator" interface, by providing a hash value calculator method and a value comparison method, so a String Comparator instance can be used as a Dictionary's comparator object. StringComparator objects are immutable; all of our parameters are set in the constructor. This is desirable because it allows the object to be installed in a Dictionary (or any other hash table-based structure) without any danger that the hash table will need to be rebuilt as long as the same comparator is installed. Notes Modified 09/01/02 MJRoberts - Creation */ #ifndef VMSTRCMP_H #define VMSTRCMP_H #include #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* ------------------------------------------------------------------------ */ /* * Our serialized data stream, in both the image file and a saved file, * consists of: * * UINT2 truncation_length *. UINT2 flags *. UINT2 equivalence_mapping_count *. UINT2 equivalence_total_value_chars *. equivalence_mappings * * The 'flags' value consists of the following combination of bit fields: * * 0x0001 - the match is case-sensitive * * The 'equivalence_total_value_chars' gives the sum total of the value * string characters in ALL of the equivalence mappings. This value is * stored simply to make it easier to calculate the memory allocation * needs when loading this object. * * Each 'equivalence_mapping' entry is arranged like this: * * UINT2 reference_char *. UBYTE value_char_count *. UINT4 uc_result_flags *. UINT4 lc_result_flags *. UINT2 value_char[value_char_count] * * Each character is given as a 16-bit Unicode value. These values map * directly to the corresponding vmobj_strcmp_equiv structure entries. */ /* ------------------------------------------------------------------------ */ /* * Our in-memory extension. */ struct vmobj_strcmp_ext { /* * The truncation length for reference strings, or zero if no * truncation is allowed. This is the minimum length that we must * match when the value string is shorter than the reference string. */ size_t trunc_len; /* * Case sensitivity. If this is true, then our matches are sensitive * to case, which means that we must match each character exactly on * case. If this is false, then our matches are insensitive to case, * so we can match an upper-case letter to the corresponding * lower-case letter. */ int case_sensitive; /* * Equivalence mapping table, giving the mapping for each "reference" * string character. This is a two-tiered array: the first tier is * indexed by the high-order 8 bits of a reference character, and * gives a pointer to the second tier array, or a null pointer if * there is no mapping for any character with the given high-order 8 * bits. The second tier is indexed by the low-order 8 bits, and * gives a pointer to the equivalence mapping structure for the * character, or a null pointer if there is no mapping for the * character. */ struct vmobj_strcmp_equiv **equiv[256]; }; /* * Equivalence mapping entry. Note that we don't store the reference * character in a mapping structure, because we can only reach these * mapping structures by indexing the mapping array with the reference * character, and thus must always already know the reference character * before we can even reach one of these. */ struct vmobj_strcmp_equiv { /* string of value characters matching this reference character */ size_t val_ch_cnt; wchar_t *val_ch; /* * Additive result flags for upper-case input matches: this value is * bitwise-OR'd into the result code when this equivalence mapping is * used to match the value to an upper-case input letter. */ unsigned long uc_result_flags; /* additive result flags for lower-case input matches */ unsigned long lc_result_flags; }; /* ------------------------------------------------------------------------ */ /* * String Comparator intrinsic class */ class CVmObjStrComp: public CVmObject { friend class CVmMetaclassStrComp; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* am I a StringComparator object? */ static int is_strcmp_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { /* defer to our base class */ return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* undo operations - we are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* we reference no other objects */ void mark_refs(VMG_ uint) { } void remove_stale_weak_refs(VMG0_) { } /* load from an image file */ void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixup); /* * Direct Interface. These functions correspond to methods we expose * through the get_prop() interface, but can be called directly from * the C++ code of other intrinsic classes (such as Dictionary) to * avoid the overhead of going through the get_prop() mechanism. * These are virtual to allow derived intrinsic classes to override * the implementation of the public VM-visible interface. */ /* calculate a hash value for a constant string */ virtual unsigned int calc_str_hash(const char *str, size_t len); /* match two strings */ virtual unsigned long match_strings(const char *valstr, size_t vallen, const char *refstr, size_t reflen); /* * Check for an approximation match to a given character. This checks * the given input string for a match to the approximation for a given * reference character. Returns the number of characters in the match, * or zero if there's no match. */ virtual size_t match_chars(const wchar_t *valstr, size_t valcnt, wchar_t refchar); /* truncation length, in characters; 0 means no truncation length */ virtual size_t trunc_len() const; protected: /* create with no extension */ CVmObjStrComp() { ext_ = 0; } /* delete my extension */ void delete_ext(VMG0_); /* get my extension data */ vmobj_strcmp_ext *get_ext() const { return (vmobj_strcmp_ext *)ext_; } /* load from an abstact stream object */ void load_from_stream(VMG_ class CVmStream *str); /* * Write to an abstract stream object. Returns the number of bytes * actually needed to store the object. * * If 'bytes_avail' is non-null, it indicates the maximum number of * bytes available for writing; if we need more than this amount, we * won't write anything at all, but will simply return the number of * bytes we actually need. */ ulong write_to_stream(VMG_ class CVmStream *str, ulong *bytes_avail); /* allocate and initialize our extension */ void alloc_ext(VMG_ size_t trunc_len, int case_sensitive, size_t equiv_cnt, size_t total_chars, class CVmObjStrCompMapReader *reader); /* count of equivalence mappings */ void count_equiv_mappings(size_t *equiv_cnt, size_t *total_ch_cnt); /* property evaluator - undefined property */ int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } /* property evaluator - calculate a hash value */ int getp_calc_hash(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluator - match two values */ int getp_match_values(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc); /* property evaluation function table */ static int (CVmObjStrComp::*func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassStrComp: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "string-comparator/030000"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStrComp(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjStrComp(); G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjStrComp::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjStrComp::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMSTRCMP_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjStrComp) frobtads-1.2.3/tads3/vmini_nd.cpp0000644000175000001440000000275007753150544016047 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmini_nd.cpp - VM initialization - non-debug version Function Stub routines for initialization related to the debugger. This module should be linked into non-debug builds of the interpreter, so that debugger-related files are omitted from the executable. Notes Modified 12/03/99 MJRoberts - Creation */ #include "t3std.h" #include "vminit.h" #include "vmparam.h" /* * Initialize the debugger */ void vm_init_debugger(VMG0_) { /* non-debug version - nothing to do */ } /* * Initialization, phase 2: just before loading image file */ void vm_init_before_load(VMG_ const char *image_filename) { /* non-debug version - nothing to do */ } /* * Initialization, phase 3: just after loading the image file */ void vm_init_after_load(VMG0_) { /* non-debug version - nothing to do */ } /* * Termination - shut down the debugger */ void vm_terminate_debug_shutdown(VMG0_) { /* non-debug version - nothing to do */ } /* * Termination - delete debugger objects */ void vm_terminate_debug_delete(VMG0_) { /* non-debug version - nothing to do */ } /* * In the non-debug version, use the basic stack reserve parameter. */ size_t vm_init_stack_reserve() { return VM_STACK_RESERVE; } frobtads-1.2.3/tads3/vmmain.h0000644000175000001440000005030712145504453015172 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmain.h - main entrypoint to run a T3 image file Function Notes Modified 10/07/99 MJRoberts - Creation */ #ifndef VMMAIN_H #define VMMAIN_H #include "vmglob.h" /* * Parse a command line to determine the name of the game file specified by * the arguments. If we can find a game file specification, we'll fill in * 'buf' with the filename and return true; if there's no file name * specified, we'll return false. * * Note that our parsing will work for TADS 2 or TADS 3 interpreter command * lines, so this routine can be used to extract the filename from an * ambiguous command line in order to check the file for its type and * thereby resolve which interpreter to use. * * Note that the filename might not come directly from the command * arguments, since it might be implied. If there's no game file directly * specified, but there is an explicit "-r" option to restore a saved game, * we'll pull the game filename out of the saved game file if possible. * Saved game files in both TADS 2 and TADS 3 can store the original game * file name that was being executed at the time the game was saved. */ int vm_get_game_arg(int argc, const char *const *argv, char *buf, size_t buflen, int *engine_type); /* * Given a game file argument, determine which engine (TADS 2 or TADS 3) * should be used to run the game. * * We'll first check to see if the given file exists. If it does, we'll * read header information from the file to try to identify the game. If * the file does not exist, we will proceed to check default suffixes, if * the suffix arguments are non-null. * * If defexts is not null, it gives an array of default filename suffix * strings, suitable for use with os_defext(). When the name as given * doesn't refer to an existing file, we'll try looking for files with * these suffixes, one at a time. * * Returns one of the VM_GGT_xxx codes. * * If the return value is 2 or 3, we'll fill in the actual_filename buffer * with the full name of the file; if we added a default suffix, the * suffix will be included in this result. */ int vm_get_game_type(const char *filename, char *actual_filename, size_t actual_filename_buffer_length, const char *const *defexts, size_t defext_count); /* * Returns codes for vm_get_game_type() */ /* game type is TADS 2 */ #define VM_GGT_TADS2 2 /* game type is TADS 3 */ #define VM_GGT_TADS3 3 /* game file not found (even after trying default extensions) */ #define VM_GGT_NOT_FOUND (-1) /* game file exists but isn't a valid tads 2 or tads 3 game */ #define VM_GGT_INVALID (-2) /* * ambiguous filename - the exact filename doesn't exist, and more than * one default suffix version exists */ #define VM_GGT_AMBIG (-3) /* * determine if a VM_GGT_xxx code refers to a valid engine version: * returns true if the code is an engine version, false if the code is an * error indication */ #define vm_ggt_is_valid(code) ((code) > 0) /* * Parameters structure for vm_run_image. vm_run_image() essentially takes * the parsed version of the execution parameters, and loads and executes * the .t3 file accordingly. The caller is responsible for parsing the * command-line arguments or equivalent. You can use vm_run_image_main() * if you have a standard Unix-style argv/argc and want to do the standard * t3run parsing. If you don't have a standard argv/argc (e.g., you get * your parameters from a GUI dialog, an external config file), you can * bypass the normal t3run argv parsing by setting up this structure from * your parameters and calling vm_run_image(). You can also use this * approach if you want override the standard t3run option syntax and do * your own argv parsing. */ struct vm_run_image_params { vm_run_image_params(class CVmMainClientIfc *clientifc, class CVmHostIfc *hostifc, const char *image_file_name) { /* set the required parameters */ this->clientifc = clientifc; this->hostifc = hostifc; this->image_file_name = image_file_name; /* assume no program arguments */ prog_argv = 0; prog_argc = 0; /* assume no script or log files */ script_file = 0; log_file = 0; cmd_log_file = 0; script_quiet = FALSE; /* assume we'll use [More] mode for interactive output */ more_mode = TRUE; /* assume we're loading from a separate .t3 file */ load_from_exe = FALSE; /* assume we won't show the VM banner */ show_banner = FALSE; /* assume we will seed the RNG */ seed_rand = TRUE; /* use the default character sets from the system */ charset = 0; log_charset = 0; /* assume we're not loading a saved state file */ saved_state = 0; /* use default directories */ res_dir = 0; file_dir = 0; sandbox_dir = 0; /* assume no network configuration */ netconfig = 0; } /* * the client interface - this lets the host application environment * provide a custom implementation of the main console interface for * the game */ class CVmMainClientIfc *clientifc; /* * the host interface - this lets the host application environment * customize certain preference settings and data sources */ class CVmHostIfc *hostifc; /* the image file name */ const char *image_file_name; /* * The program command-line parameters - these are argv-style parameter * strings to pass to the .t3 program's main(). (Note that these * aren't the arguments to the interpreter - that's what this entire * structure is about, as it contains the pre-digested results of * parsing those parameters. These are instead unparsed parameters * that we're to pass along to the .t3 program.) */ const char *const *prog_argv; int prog_argc; /* * The input script file. If this is not null, the interpreter will * read console input from the given file and feed it to the .t3 * program via inputLine(), etc. If script_quiet is true, we'll read * this input silently, without echoing it or program output to the * console for the duration of the script input. */ const char *script_file; int script_quiet; /* * The log file. If this is not null, we'll log console output to the * given file. */ const char *log_file; /* * Command log file. If this is not null, we'll log input lines read * from the console to this file. */ const char *cmd_log_file; /* * In [More] mode, we pause and await a keystroke after each screenful * of text to give the user a chance to read text before it scrolls off * the screen. This is the default, but sometimes users will want to * run in batch/stdio mode, where there's no pausing. */ int more_mode; /* * Flag: load from the application executable file. If this is true, * the given image filename is actually the name of the native * application executable file that we're running (i.e., the main * program's argv[0] or equivalent), and it contains an embedded or * attached copy of the .t3 file, embedded via some OS-specific * mechanism. */ int load_from_exe; /* flag: show the VM version/copyright banner at startup */ int show_banner; /* * flag: seed the random number generator automatically at startup; * this uses some OS-specific source of true randomness to generate an * initial random state for the RNG */ int seed_rand; /* * Character set name for the main console. If this is not null, we'll * use this charater set for text displayed to and read from the * interactive console. */ const char *charset; /* log file character set (if not null) */ const char *log_charset; /* * Saved state file to restore on startup. If this is not null, we'll * restore this saved state file immediately after loading the .t3 * file. */ const char *saved_state; /* resource directory */ const char *res_dir; /* * Default working directory for File operations. If this is not null, * the File class will use this as the working directory for file * operations when file names are given as relative paths. If this is * null, the folder containing the .t3 file is the default. */ const char *file_dir; /* * Sandbox directory for file safety enforcement. If this is not null, * the File class uses this as the sandbox directory for file safety * purposes. If this is null, the default sandbox is the file_dir if * specified, otherwise the folder containing the .t3 file. */ const char *sandbox_dir; /* * Network configuration object. If not null, we're running in web * host mode, with the network parameters specified in this object. */ class TadsNetConfig *netconfig; }; /* * Execute an image file. We'll return zero on success, or a VM error code * on failure. * * If 'load_from_exe' is true, the image filename given is actually the * name of the native executable file that we're running, and we should * load the image file that's attached to the native executable file via * the system-specific os_exeseek() mechanism. * * If 'script_file' is not null, we'll read console input from the given * file. If 'log_file' is not null, we'll log console output to the given * file. If 'cmd_log_file' is not null, we'll log each line we read from * the console to the given command logging file. * * 'charset' optionally selects a character set to use for text displayed * to or read from the user interface. If this is null, we'll use the * current system character set as indicated by the osifc layer. 'charset' * should usually be null unless explicitly specified by the user. */ int vm_run_image(const vm_run_image_params *params); /* * Execute an image file using argc/argv conventions. We'll parse the * command line and invoke the program. * * The 'executable_name' is the name of the host program; this is used to * prepare "usage" messages. * * If 'defext' is true, we'll try adding a default extension ("t3", * formerly "t3x") to the name of the image file we find if the given * filename doesn't exist. We'll always check to see if the file exists * with the exact given name before we do this, so that we don't add an * extension where none is needed. If the caller doesn't want us to try * adding an extension at all, pass in 'defext' as false. * * If 'test_mode' is true, we'll make some small changes to the program * invocation protocol appropriate to running system tests. In * particular, we'll build the program argument list with only the root * name of the image file, not the full path - this allows the program to * display the argument list without any dependencies on local path name * conventions or the local directory structure, allowing for more easily * portable test scripts. * * If 'hostifc' is null, we'll provide our own default interface. The * caller can provide a custom host interface by passing in a non-null * 'hostifc' value. */ int vm_run_image_main(class CVmMainClientIfc *clientifc, const char *executable_name, int argc, char **argv, int defext, int test_mode, class CVmHostIfc *hostifc); /* * Show the interface report. This writes an XML listing of the * metaclasses and intrinsic function sets to the standard output. */ void vm_interface_report(class CVmMainClientIfc *cli, const char *fname); /* * Get the IFID from the GameInfo resource. This looks for the * gameinfo.txt resource in the image file or resource directory, then * scans the contents for its IFID field. If we find an IFID, we return an * allocated buffer containing the IFID string (or the first IFID string, * if the GameInfo contains multiple IFIDs); otherwise we return null. The * caller is responsible for freeing the returned buffer with * lib_free_str(). * * Note that the host interface's resource map for the image file is * generally initialized when the image file is loaded, so this routine * must be called AFTER the image file has been loaded through the regular * loader. If you want a more general-purpose GameInfo extractor that * doesn't rely on the interpreter's image loader, you should use the Babel * API tools, which parse the image file directly. This routine is * intended as a lightweight alternative to the Babel tools for use in the * interpreter, where we're certainly already going to invoke the full * image loader anyway. */ char *vm_get_ifid(class CVmHostIfc *hostifc); /* ------------------------------------------------------------------------ */ /* * VM Main client services interface. Callers of the vm_run_image * functions must provide an implementation of this interface. */ class CVmMainClientIfc { public: virtual ~CVmMainClientIfc() { } /* * Set "plain" mode. This should set the console to plain ASCII output * mode, if appropriate. Note that this can be called before * client_init(), and no globals are generally present at this point. * * In most cases, this can make a call to os_plain() to set the * OS-level console to plain mode. Non-console applications generally * need not do anything here at all. */ virtual void set_plain_mode() = 0; /* * Create the main system console, if desired. This is called during * VM initialization, so it is called prior to client_init(). Returns * the main console object, if desired. If no main console is desired * for this application, return null. */ virtual class CVmConsoleMain *create_console( struct vm_globals *globals) = 0; /* * Delete the console, if we created one. This is called during VM * termination, so it's called after client_terminate(). If * create_console() doesn't create a console, this routine need do * nothing. */ virtual void delete_console(struct vm_globals *globals, class CVmConsoleMain *console) = 0; /* * Initialization - we'll invoke this immediately after initializing * the VM (via vm_initialize), so the client can perform any global * initialization desired. The globals are valid at this point because * we have completed VM initialization. * * If script_file is non-null, it gives the name of a file to use as * the source of console input. The client implementation should set * up accordingly; if the standard console (G_console) is being used, * the client can simply use G_console->open_script_file() to set up * scripting. * * If log_file is non-null, it gives the name of a file to use to log * console output. The client shoudl set up logging; if the standard * console if being used, G_console->open_log_file() will do the trick. * * If cmd_log_file is non-null, it gives the name of a file to use to * log commands read from the input (i.e., only command input should be * logged, not other console output). If the standard console is being * used, G_console->open_command_log() will set things up properly. * * If banner_str is non-null, it gives a VM banner string that should * be displayed to the user. If the standard console is being used, * this can be displayed using G_console->format_text(). * * The parameters (script_file, log_file, cmd_log_file, and the * presence or absence of banner_str) are taken from the startup * parameters. For a command-line version, for example, these come * from command line options. So, these are necessarily passed down in * some form from the client to begin with; so a client that never * passes these to vm_run_image() or vm_run_image_main() doesn't need * to handle these parameters at all here. */ virtual void client_init(struct vm_globals *globals, const char *script_file, int script_quiet, const char *log_file, const char *cmd_log_file, const char *banner_str, int more_mode) = 0; /* * Termination - we'll invoke this immediately before terminating the * VM (via vm_terminate). Globals are still valid at this point, but * will be destroyed after this returns. */ virtual void client_terminate(struct vm_globals *globals) = 0; /* * pre-execution notification - we'll invoke this function just before * starting execution in the loaded image */ virtual void pre_exec(struct vm_globals *globals) = 0; /* * terminate - we'll invoke this just after execution in the loaded * image terminates */ virtual void post_exec(struct vm_globals *globals) = 0; /* * Terminate with error - we'll invoke this upon catching an * exception that the image file doesn't handle and which thus * terminates execution. Note that if this is called, post_exec() * will not be called; however, if post_exec() itself throws an * exception, we'll invoke this routine. */ virtual void post_exec_err(struct vm_globals *globals) = 0; /* * Display an error message. We'll call this with a complete error * message to display. Note that we won't add a newline at the end of * the message, so if the message is to be displayed on a stdio-style * terminal, this routine should display a newline after the message. * * If the implementation normally writes the text to the main output * console (G_console), it must take into the account the possibility * that we have not opened a system console at all (i.e., G_console * could be null), or have not allocated any globals at all (i.e., * 'globals' could be null). * * If 'add_blank_line' is true, the implementation should add a blank * line after the error, if appropriate for the display device. If * we're displaying the message in an alert box on a GUI, for example, * this can be ignored. */ virtual void display_error(struct vm_globals *globals, const struct CVmException *exc, const char *msg, int add_blank_line) = 0; }; /* ------------------------------------------------------------------------ */ /* * Very basic client interface implementation using stdio */ class CVmMainClientIfcStdio: public CVmMainClientIfc { public: virtual void set_plain_mode() { } virtual class CVmConsoleMain *create_console(struct vm_globals *) { return 0; } virtual void delete_console(struct vm_globals *, class CVmConsoleMain *) { } virtual void client_init(struct vm_globals *, const char *, int, const char *, const char *, const char *, int) { } virtual void client_terminate(struct vm_globals *) { } virtual void pre_exec(struct vm_globals *) { } virtual void post_exec(struct vm_globals *) { } virtual void post_exec_err(struct vm_globals *) { } virtual void display_error(struct vm_globals *, const struct CVmException *, const char *msg, int add_blank_line) { printf(add_blank_line ? "%s\n" : "%s", msg); } }; #endif /* VMMAIN_H */ frobtads-1.2.3/tads3/vmbiftad.h0000644000175000001440000001711611777373377015523 0ustar realncusers/* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbiftad.h - function set definition - TADS function set Function Notes Modified 12/06/98 MJRoberts - Creation */ #ifndef VMBIFTAD_H #define VMBIFTAD_H #include "os.h" #include "vmbif.h" #include "utf8.h" /* ------------------------------------------------------------------------ */ /* * Include headers for our selectable Random Number Generator algorithms. */ #include "vmisaac.h" #include "vmmersenne.h" /* ------------------------------------------------------------------------ */ /* * TADS function set built-in functions */ class CVmBifTADS: public CVmBif { public: /* function vector */ static vm_bif_desc bif_table[]; /* * General functions */ static void datatype(VMG_ uint argc); static void getarg(VMG_ uint argc); static void firstobj(VMG_ uint argc); static void nextobj(VMG_ uint argc); static void randomize(VMG_ uint argc); static void rand(VMG_ uint argc); static void toString(VMG_ uint argc); static void toInteger(VMG_ uint argc); static void toNumber(VMG_ uint argc); static void gettime(VMG_ uint argc); static void re_match(VMG_ uint argc); static void re_search(VMG_ uint argc); static void re_group(VMG_ uint argc); static void re_replace(VMG_ uint argc); static void savepoint(VMG_ uint argc); static void undo(VMG_ uint argc); static void save(VMG_ uint argc); static void restore(VMG_ uint argc); static void restart(VMG_ uint argc); static void get_max(VMG_ uint argc); static void get_min(VMG_ uint argc); static void make_string(VMG_ uint argc); static void get_func_params(VMG_ uint argc); static void sprintf(VMG_ uint argc); static void make_list(VMG_ uint argc); static void get_abs(VMG_ uint argc); static void get_sgn(VMG_ uint argc); static void concat(VMG_ uint argc); static void re_search_back(VMG_ uint argc); /* internal toString interface */ static void toString(VMG_ vm_val_t *retval, const vm_val_t *srcval, int radix, int flags); /* format a date-and-time list per getTime(GetDateAndTime) */ static vm_obj_id_t format_datetime_list(VMG_ os_time_t timer); protected: /* common handler for re_search() and re_search_back() */ template inline static void re_search_common(VMG_ uint argc); /* enumerate objects (common handler for firstobj and nextobj) */ static void enum_objects(VMG_ uint argc, vm_obj_id_t start_obj); /* common handler for toInteger and toNumber */ static void toIntOrNum(VMG_ uint argc, int int_only); }; /* ------------------------------------------------------------------------ */ /* * Random Number Generator ID values for randomize(id, ...) */ #define VMBT_RNGID_ISAAC 1 #define VMBT_RNGID_LCG 2 #define VMBT_RNGID_MT19937 3 #define VMBT_RNGID_BITSHIFT 4 /* ------------------------------------------------------------------------ */ /* * Global information for the TADS intrinsics. We allocate this * structure with the VM global variables - G_bif_tads_globals contains * the structure. */ class CVmBifTADSGlobals { public: /* creation */ CVmBifTADSGlobals(VMG0_); /* deletion */ ~CVmBifTADSGlobals(); /* regular expression parser and searcher */ class CRegexParser *rex_parser; class CRegexSearcherSimple *rex_searcher; /* * global variable for the last regular expression search string (we * need to hold onto this because we might need to extract group-match * substrings from it) */ struct vm_globalvar_t *last_rex_str; /* the currently active RNG ID (VMBT_RNGID_xxx) */ int rng_id; /* -------------------------------------------------------------------- */ /* * Linear Congruential Random Number Generator state - this is just a * 32-bit seed value. */ int32_t lcg_rand_seed; /* -------------------------------------------------------------------- */ /* * Bit-Shift Random Number Generator state */ #ifdef VMBIFTADS_RNG_BITSHIFT /* bit-shift generator seed value */ int32_t bits_rand_seed; #endif /* VMBIFTADS_RNG_BITSHIFT */ /* -------------------------------------------------------------------- */ /* * ISAAC Random Number Generator state */ struct isaacctx *isaac_ctx; /* -------------------------------------------------------------------- */ /* * Mersenne Twister MT19937 Random Number Generator state */ class CVmMT19937 *mt_ctx; }; /* end of section protected against multiple inclusion */ #endif /* VMBIFTAD_H */ /* ------------------------------------------------------------------------ */ /* * TADS function set vector. Define this only if VMBIF_DEFINE_VECTOR has * been defined, so that this file can be included for the prototypes alone * without defining the function vector. * * Note that this vector is specifically defined outside of the section of * the file protected against multiple inclusion. */ #ifdef VMBIF_DEFINE_VECTOR /* TADS general data manipulation functions */ vm_bif_desc CVmBifTADS::bif_table[] = { { &CVmBifTADS::datatype, 1, 0, FALSE }, /* 0 */ { &CVmBifTADS::getarg, 1, 0, FALSE }, /* 1 */ { &CVmBifTADS::firstobj, 0, 2, FALSE }, /* 2 */ { &CVmBifTADS::nextobj, 1, 2, FALSE }, /* 3 */ { &CVmBifTADS::randomize, 0, 0, FALSE }, /* 4 */ { &CVmBifTADS::rand, 0, 0, TRUE }, /* 5 */ { &CVmBifTADS::toString, 1, 2, FALSE }, /* 6 */ { &CVmBifTADS::toInteger, 1, 1, FALSE }, /* 7 */ { &CVmBifTADS::gettime, 0, 1, FALSE }, /* 8 */ { &CVmBifTADS::re_match, 2, 1, FALSE }, /* 9 */ { &CVmBifTADS::re_search, 2, 1, FALSE }, /* 10 */ { &CVmBifTADS::re_group, 1, 0, FALSE }, /* 11 */ { &CVmBifTADS::re_replace, 3, 2, FALSE }, /* 12 */ { &CVmBifTADS::savepoint, 0, 0, FALSE }, /* 13 */ { &CVmBifTADS::undo, 0, 0, FALSE }, /* 14 */ { &CVmBifTADS::save, 1, 0, FALSE }, /* 15 */ { &CVmBifTADS::restore, 1, 0, FALSE }, /* 16 */ { &CVmBifTADS::restart, 0, 0, FALSE }, /* 17 */ { &CVmBifTADS::get_max, 1, 0, TRUE }, /* 18 */ { &CVmBifTADS::get_min, 1, 0, TRUE }, /* 19 */ { &CVmBifTADS::make_string, 1, 1, FALSE }, /* 20 */ { &CVmBifTADS::get_func_params, 1, 0, FALSE }, /* 21 */ { &CVmBifTADS::toNumber, 1, 1, FALSE }, /* 23 */ { &CVmBifTADS::sprintf, 1, 0, TRUE }, /* 24 */ { &CVmBifTADS::make_list, 1, 1, FALSE }, /* 25 */ { &CVmBifTADS::get_abs, 1, 0, FALSE }, /* 26 */ { &CVmBifTADS::get_sgn, 1, 0, FALSE }, /* 27 */ { &CVmBifTADS::concat, 0, 0, TRUE }, /* 28 */ { &CVmBifTADS::re_search_back, 2, 1, FALSE } /* 29 */ }; #endif /* VMBIF_DEFINE_VECTOR */ frobtads-1.2.3/tads3/tctok.h0000644000175000001440000024512112145504453015027 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/tctok.h,v 1.5 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctok.h - TADS3 compiler tokenizer and preprocessor Function Notes The tokenizer is layered with the preprocessor, so that the preprocessor can deal with include files, macro expansion, and preprocessor directives. Modified 04/12/99 MJRoberts - Creation */ #ifndef TCTOK_H #define TCTOK_H #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "vmhash.h" #include "vmerr.h" #include "tcerr.h" #include "tcerrnum.h" /* ------------------------------------------------------------------------ */ /* * Constants */ /* maximum length of a symbol name, in characters */ const size_t TOK_SYM_MAX_LEN = 80; /* * Maximum buffer required to hold a symbol, in bytes. Each UTF-8 * character may take up three bytes, plus we need a null terminator * byte. */ const size_t TOK_SYM_MAX_BUFFER = (3*TOK_SYM_MAX_LEN + 1); /* maximum #if nesting level */ const size_t TOK_MAX_IF_NESTING = 100; /* maximum number of parameters per macro */ const int TOK_MAX_MACRO_ARGS = 128; /* * Special token flag characters - these are a characters that can't * occur in an input file (we guarantee this by converting any * occurrences of this character to a space on reading input). We use * these to flag certain special properties of tokens in the input * buffer. * * We use ASCII characters in the control range (0x01 (^A) through 0x1A * (^Z), excluding 0x09 (tab), 0x0A (LF), 0x0D (CR), and 0x0C (Page * Feed); a well-formed source file would never use any of these * characters in input. Even if it does, we won't get confused, since * we'll always translate these to a space if we find them in input; but * choosing characters that *should* never occur in valid input will * ensure that we never alter the meaning of valid source by this * translation. */ /* * macro parameter flag - we use this in the internal storage of a * #define expansion to flag where the formal parameters are mentioned, * so that we can substitute the actuals when expanding the macro */ const char TOK_MACRO_FORMAL_FLAG = 0x01; /* * Token fully expanded flag. Whenever we detect that a particular * token has been fully expanded in the course of a particular macro * expansion, we'll insert this byte before the token; on subsequent * re-scans, whenever we see this flag, we'll realize that the token * needs no further consideration of expansion. */ const char TOK_FULLY_EXPANDED_FLAG = 0x02; /* * Macro substitution end marker. Each time we expand a macro, we'll * insert immediately after the macro expansion a special pseudo-token, * consisting of this flag followed by a pointer to the symbol table * entry for the symbol expanded. As we expand macros, we'll check to * see if any of these special flags appear in the buffer after the * macro about to be expanded. If we find such a flag matching the * symbol about to be expanded, we'll know the symbol has already been * fully expanded on a previous scan and thus must not be expanded * again. */ const char TOK_MACRO_EXP_END = 0x03; /* * End-of-line flag. This serves as a local end-of-file marker for * preprocessor lines. Because preprocessor lines must be considered in * isolation, we need some way when parsing one to tell the tokenizer * not to try to read another line when it reaches the end of the * current line. This flag serves this purpose: when the tokenizer * encounters one of these flags, it will simply return end-of-file * until the caller explicitly reads a new source line. */ const char TOK_END_PP_LINE = 0x04; /* * "#foreach" marker flag. This marks the presence of a #foreach token in * a macro's expansion. We leave the text of the expansion area intact, * but we replace the #foreach token with this marker character. */ const char TOK_MACRO_FOREACH_FLAG = 0x05; /* * "#argcount" marker flag. This marks the presence of a #argcount token * in a macro's expansion. */ const char TOK_MACRO_ARGCOUNT_FLAG = 0x06; /* * "#ifempty" and #ifnempty" marker flags */ const char TOK_MACRO_IFEMPTY_FLAG = 0x07; const char TOK_MACRO_IFNEMPTY_FLAG = 0x08; /* * Macro format version number. The compiler sets up a predefined macro * (__TADS_MACRO_FORMAT_VERSION) with this information. Since 3.1, the * macro table is visible to user code via t3GetGlobalSymbols() (using the * T3PreprocMacros selector), and this information includes the parsed * format with the embedded flag codes. The macro information can be used * in DynamicFunc compilation at run-time. If we ever make incompatible * changes to the internal format, future interpreters will have to * recognize older versions so that they can make the necessary * translations. By embedding the version information in the table, we * make this recognition possible. */ #define TCTOK_MACRO_FORMAT_VERSION 1 /* ------------------------------------------------------------------------ */ /* * Macro table. This is a virtualized version of our basic hash table, to * allow specialized versions that provide views on top of other table * structures. */ class CTcMacroTable { public: virtual ~CTcMacroTable() { } /* add an entry */ virtual void add(CVmHashEntry *entry) = 0; /* remove an entry */ virtual void remove(CVmHashEntry *entry) = 0; /* find an entry */ virtual class CVmHashEntry *find(const char *str, size_t len) = 0; /* enumerate entries */ virtual void enum_entries( void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx) = 0; /* dump the hash table for debugging purposes */ virtual void debug_dump() = 0; }; /* ------------------------------------------------------------------------ */ /* * #if state */ enum tok_if_t { TOKIF_NONE, /* not in a #if block at all */ TOKIF_IF_YES, /* processing a true #if branch */ TOKIF_IF_NO, /* processing a false #if branch */ TOKIF_IF_DONE, /* done with true #if/#elif; skip #elif's and #else */ TOKIF_ELSE_YES, /* processing a true #else branch */ TOKIF_ELSE_NO /* processing a false #else branch */ }; /* * #if stack entry */ struct tok_if_info_t { /* state */ tok_if_t state; /* file descriptor and line number of starting #if */ class CTcTokFileDesc *desc; long linenum; }; /* ------------------------------------------------------------------------ */ /* * Token Types */ enum tc_toktyp_t { TOKT_INVALID, /* invalid token */ TOKT_NULLTOK, /* null token - caller should read another token */ TOKT_EOF, /* end of file */ TOKT_MACRO_FORMAL, /* formal parameter replacement placeholder */ TOKT_MACRO_FOREACH, /* macro varargs #foreach placeholder */ TOKT_MACRO_ARGCOUNT, /* macro varargs #argcount placeholder */ TOKT_MACRO_IFEMPTY, /* #ifempty macro placeholder */ TOKT_MACRO_IFNEMPTY, /* #ifnempty macro placeholder */ TOKT_SYM, /* symbolic name */ TOKT_INT, /* integer */ TOKT_SSTR, /* single-quoted string */ TOKT_SSTR_START, /* start of an sstring with embedding - '...<< */ TOKT_SSTR_MID, /* middle of an sstring with embedding - >>...<< */ TOKT_SSTR_END, /* end of an sstring with embedding - >>...' */ TOKT_DSTR, /* double-quoted string */ TOKT_DSTR_START, /* start of a dstring with embedding - "...<< */ TOKT_DSTR_MID, /* middle of a dstring with embedding - >>...<< */ TOKT_DSTR_END, /* end of a dstring with embedding - >>..." */ TOKT_RESTR, /* regular expression string - R'...' or R"..." */ TOKT_LPAR, /* left paren '(' */ TOKT_RPAR, /* right paren ')' */ TOKT_COMMA, /* comma ',' */ TOKT_DOT, /* period '.' */ TOKT_LBRACE, /* left brace '{' */ TOKT_RBRACE, /* right brace '}' */ TOKT_LBRACK, /* left square bracket '[' */ TOKT_RBRACK, /* right square bracket ']' */ TOKT_EQ, /* equals sign '=' */ TOKT_EQEQ, /* double-equals sign '==' */ TOKT_ASI, /* colon-equals assignment operator ':=' */ TOKT_PLUS, /* plus sign '+' */ TOKT_MINUS, /* minus sign '-' */ TOKT_TIMES, /* multiplication symbol '*' */ TOKT_DIV, /* division symbol '/' */ TOKT_MOD, /* modulo '%' */ TOKT_GT, /* greater-than sign '>' */ TOKT_LT, /* less-than sign '<' */ TOKT_GE, /* greater-or-equal sign '>=' */ TOKT_LE, /* less-or-equal sign '<=' */ TOKT_NE, /* not-equals sign '!=' or '<>' */ TOKT_ARROW, /* arrow symbol '->' */ TOKT_COLON, /* colon ':' */ TOKT_SEM, /* semicolon ';' */ TOKT_AND, /* bitwise AND '&' */ TOKT_ANDAND, /* logical AND '&&' */ TOKT_OR, /* bitwise OR '|' */ TOKT_OROR, /* logical OR '||' */ TOKT_XOR, /* bitwise XOR '^' */ TOKT_SHL, /* shift left '<<' */ TOKT_ASHR, /* arithmetic shift right '>>' */ TOKT_LSHR, /* logical shift right '>>>' */ TOKT_INC, /* increment '++' */ TOKT_DEC, /* decrement '--' */ TOKT_PLUSEQ, /* plus-equals '+=' */ TOKT_MINEQ, /* minus-equals '-=' */ TOKT_TIMESEQ, /* times-equals '*=' */ TOKT_DIVEQ, /* divide-equals '/=' */ TOKT_MODEQ, /* mod-equals '%=' */ TOKT_ANDEQ, /* and-equals '&=' */ TOKT_OREQ, /* or-equals '|=' */ TOKT_XOREQ, /* xor-equals '^=' */ TOKT_SHLEQ, /* shift-left-and-assign '<<=' */ TOKT_ASHREQ, /* arithmetic shift-right-and-assign '>>=' */ TOKT_LSHREQ, /* logical shift-right-and-assign '>>>=' */ TOKT_NOT, /* logical not '!' */ TOKT_BNOT, /* bitwise not '~' */ TOKT_POUND, /* pound '#' */ TOKT_POUNDPOUND, /* double-pound '##' */ TOKT_POUNDAT, /* pound-at '#@' */ TOKT_ELLIPSIS, /* ellipsis '...' */ TOKT_QUESTION, /* question mark '?' */ TOKT_QQ, /* double question mark '??' */ TOKT_COLONCOLON, /* double-colon '::' */ TOKT_FLOAT, /* floating-point number */ TOKT_BIGINT, /* an integer promoted to a float due to overflow */ TOKT_AT, /* at-sign */ TOKT_DOTDOT, /* range marker '..' */ TOKT_FMTSPEC, /* sprintf format spec for <<%fmt expr>> */ /* keywords */ TOKT_SELF, TOKT_INHERITED, TOKT_ARGCOUNT, TOKT_IF, TOKT_ELSE, TOKT_FOR, TOKT_WHILE, TOKT_DO, TOKT_SWITCH, TOKT_CASE, TOKT_DEFAULT, TOKT_GOTO, TOKT_BREAK, TOKT_CONTINUE, TOKT_FUNCTION, TOKT_RETURN, TOKT_LOCAL, TOKT_OBJECT, TOKT_NIL, TOKT_TRUE, TOKT_PASS, TOKT_EXTERNAL, TOKT_EXTERN, TOKT_FORMATSTRING, TOKT_CLASS, TOKT_REPLACE, TOKT_MODIFY, TOKT_NEW, TOKT_DELETE, TOKT_THROW, TOKT_TRY, TOKT_CATCH, TOKT_FINALLY, TOKT_INTRINSIC, TOKT_DICTIONARY, TOKT_GRAMMAR, TOKT_ENUM, TOKT_TEMPLATE, TOKT_STATIC, TOKT_FOREACH, TOKT_EXPORT, TOKT_DELEGATED, TOKT_TARGETPROP, TOKT_PROPERTYSET, TOKT_TARGETOBJ, TOKT_DEFININGOBJ, TOKT_TRANSIENT, TOKT_REPLACED, TOKT_PROPERTY, TOKT_OPERATOR, TOKT_METHOD, TOKT_INVOKEE /* type names - formerly reserved but later withdrawn */ // TOKT_VOID, // TOKT_INTKW, // TOKT_STRING, // TOKT_LIST, // TOKT_BOOLEAN, // TOKT_ANY }; /* ------------------------------------------------------------------------ */ /* * Source Block. As we read the source file, we need to keep quoted * strings and symbol names around for later reference, in case they're * needed after reading more tokens and flushing the line buffer. We'll * copy needed text into our source blocks, which we keep in memory * throughout the compilation, so that we can be certain we can * reference these strings at any time. */ /* size of a source block */ const size_t TCTOK_SRC_BLOCK_SIZE = 50000; /* source block class */ class CTcTokSrcBlock { public: CTcTokSrcBlock() { /* no next block yet */ nxt_ = 0; } ~CTcTokSrcBlock() { /* delete the next block in line */ if (nxt_ != 0) delete nxt_; } /* get/set the next block */ CTcTokSrcBlock *get_next() const { return nxt_; } void set_next(CTcTokSrcBlock *blk) { nxt_ = blk; } /* get a pointer to the block's buffer */ char *get_buf() { return buf_; } private: /* the next block in the list */ CTcTokSrcBlock *nxt_; /* bytes of the list entry */ char buf_[TCTOK_SRC_BLOCK_SIZE]; }; /* ------------------------------------------------------------------------ */ /* * String Buffer. We use these buffers for reading input lines and * expanding macros. */ class CTcTokString { public: CTcTokString() { /* no buffer yet */ buf_ = 0; buf_len_ = 0; buf_size_ = 0; } virtual ~CTcTokString() { /* delete our buffer */ if (buf_ != 0) t3free(buf_); } /* ensure that a given amount of space if available */ virtual void ensure_space(size_t siz) { /* make sure there's room for the requested size plus a null byte */ if (buf_size_ < siz + 1) { /* increase to the next 4k increment */ buf_size_ = (siz + 4095 + 1) & ~4095; /* allocate or re-allocate the buffer */ if (buf_ == 0) buf_ = (char *)t3malloc(buf_size_); else buf_ = (char *)t3realloc(buf_, buf_size_); /* throw an error if that failed */ if (buf_ == 0) err_throw(TCERR_NO_STRBUF_MEM); } } /* expand the buffer */ void expand() { /* expand to the next 4k increment */ ensure_space(buf_size_ + 4096); } /* get the text and the length of the text */ const char *get_text() const { return buf_; } size_t get_text_len() const { return buf_len_; } /* get the end of the text */ const char *get_text_end() const { return buf_ + buf_len_; } /* append text to the buffer */ virtual void append(const char *p) { append(p, strlen(p)); } virtual void append(const char *p, size_t len) { /* make sure we have space available */ ensure_space(buf_len_ + len); /* copy the text onto the end of our buffer */ memcpy(buf_ + buf_len_, p, len); /* add it to the length of the text */ buf_len_ += len; /* null-terminte it */ buf_[buf_len_] = '\0'; } /* prepend text */ virtual void prepend(const char *p) { prepend(p, strlen(p)); } virtual void prepend(const char *p, size_t len) { /* make sure we have enough space */ ensure_space(buf_len_ + len); /* * move the existing text (including the null terminator) up in the * buffer to make room for the prepended text */ memmove(buf_ + len, buf_, buf_len_ + 1); /* copy the new text to the start of the buffer */ memcpy(buf_, p, len); /* count the new size */ buf_len_ += len; } /* * Append a string to the buffer, enclosing the text in single or * double quote (as given by 'qu', which must be either '"' or '\'') * and backslash-escaping any occurrences of the same quote character * found within the string. */ void append_qu(char qu, const char *p) { append_qu(qu, p, strlen(p)); } void append_qu(char qu, const char *p, size_t len) { const char *start; /* append the open quote */ append(&qu, 1); /* scan for quotes we'll need to escape */ while (len != 0) { size_t rem; /* skip to the next quote */ for (start = p, rem = len ; rem != 0 && *p != qu ; ++p, --rem) ; /* insert the chunk up to the quote */ if (p != start) append(start, p - start); /* if we did find a quote, append it with a backslash escape */ if (rem != 0) { /* append the backslash and the quote */ append("\\", 1); append(&qu, 1); /* skip the quote in the source */ ++p; --rem; } /* we now only have 'rem' left to consider */ len = rem; } /* finally, append the closing quote */ append(&qu, 1); } /* insert text into the buffer at the given offset */ virtual void insert(int ofs, const char *p, size_t len) { /* check to see if there's anything after the insertion point */ if ((size_t)ofs >= buf_len_) { /* * there's nothing after the insertion point, so this is simply * equivalent to 'append' - go do the append, and we're done */ append(p, len); return; } /* ensure there's space for the added text */ ensure_space(buf_len_ + len); /* * Move the existing text after the insertion point just far enough * to make room for the new text. Include the null terminator. */ memmove(buf_ + ofs + len, buf_ + ofs, buf_len_ - ofs + 1); /* copy the new text in at the given offset */ memcpy(buf_ + ofs, p, len); /* include the new text in our length */ buf_len_ += len; } /* copy text into the buffer, replacing existing text */ virtual void copy(const char *p, size_t len) { /* ensure we have enough space */ ensure_space(len); /* copy the text */ memcpy(buf_, p, len); /* set our length */ buf_len_ = len; /* null-terminate it */ buf_[buf_len_] = '\0'; } /* clear any existing text */ virtual void clear_text() { /* zero the length */ buf_len_ = 0; /* put a null terminator at the start of the buffer if possible */ if (buf_size_ > 0) buf_[0] = '\0'; } /* get the buffer, for copying text directly into it */ virtual char *get_buf() const { return buf_; } size_t get_buf_size() const { return buf_size_; } /* * Set the text length - use this after copying directly into the * buffer to set the length, excluding the null terminator. We'll * add a null terminator at the given length. */ virtual void set_text_len(size_t len) { /* set the new length */ buf_len_ = len; /* add a null terminator after the new length */ if (len < buf_size_) buf_[len] = '\0'; } protected: /* buffer */ char *buf_; /* size of the buffer */ size_t buf_size_; /* length of the text in the buffer (excluding trailing null) */ size_t buf_len_; }; /* * String buffer subclass for a non-allocated string that merely * references another buffer. This can be used anywhere a CTcString is * required, but does not require any allocation. * * These objects can only be used in 'const' contexts: the underlying * buffer cannot be changed or expanded, since we do not own the * underlying buffer. */ class CTcTokStringRef: public CTcTokString { public: CTcTokStringRef() { /* we have no referenced buffer yet */ buf_ = 0; buf_size_ = 0; buf_len_ = 0; } ~CTcTokStringRef() { /* we don't own the underlying buffer, so simply forget about it */ buf_ = 0; } /* we can't make any changes to the underlying buffer */ void ensure_space(size_t) { } void append(const char *) { assert(FALSE); } void append(const char *, size_t) { assert(FALSE); } void prepend(const char *) { assert(FALSE); } void prepend(const char *, size_t) { assert(FALSE); } void insert(int, const char *, size_t) { assert(FALSE); } void copy(const char *, size_t) { assert(FALSE); } void clear_text() { assert(FALSE); } char *get_buf() const { assert(FALSE); return 0; } void set_text_len(size_t) { assert(FALSE); } /* set my underlying buffer */ void set_buffer(const char *buf, size_t len) { buf_ = (char *)buf; buf_size_ = len + 1; buf_len_ = len; } }; /* ------------------------------------------------------------------------ */ /* * String embedding context. This keeps track of the token structure for * embedded expressions within strings using << >>. */ struct tok_embed_level { /* parenthesis depth within the expression */ int parens; /* token type to switch back to on ending the string */ tc_toktyp_t endtok; /* the quote character for the enclosing string */ wchar_t qu; /* true -> the enclosing string is a triple-quoted string */ int triple; void enter(wchar_t qu, int triple) { this->parens = 0; this->endtok = (qu == '"' ? TOKT_DSTR_END : TOKT_SSTR_END); this->qu = qu; this->triple = triple; } }; struct tok_embed_ctx { tok_embed_ctx() { reset(); } void reset() { level = 0; s = 0; } void start_expr(wchar_t qu, int triple, int report); void end_expr() { if (level > 0) --level; if (level == 0) s = 0; else if (level < countof(stk)) s = stk + level - 1; } /* are we in an embedded expression? */ int in_expr() const { return level != 0; } /* paren nesting at current level */ int parens() const { return in_expr() ? s->parens : 0; } /* inc/dec paren nesting level */ void parens(int inc) { if (in_expr()) { if ((s->parens += inc) < 0) s->parens = 0; } } /* ending token type at current level */ tc_toktyp_t endtok() const { return in_expr() ? s->endtok : TOKT_INVALID; } /* ending quote at current level */ wchar_t qu() const { return in_expr() ? s->qu : 0; } /* ending quote is triple quote at current level */ int triple() const { return in_expr() ? s->triple : FALSE; } /* nesting level */ int level; /* stack pointer */ tok_embed_level *s; /* stack */ tok_embed_level stk[10]; }; /* ------------------------------------------------------------------------ */ /* * Token */ class CTcToken { public: CTcToken() { } CTcToken(tc_toktyp_t typ) : typ_(typ) { } /* get/set the token type */ tc_toktyp_t gettyp() const { return typ_; } void settyp(tc_toktyp_t typ) { typ_ = typ; } /* get/set the fully-expanded flag */ int get_fully_expanded() const { return fully_expanded_; } void set_fully_expanded(int flag) { fully_expanded_ = flag; } /* get/set the text pointer */ const char *get_text() const { return text_; } size_t get_text_len() const { return text_len_; } void set_text(const char *txt, size_t len) { text_ = txt; text_len_ = len; } /* get/set the integer value */ ulong get_int_val() const { return int_val_; } void set_int_val(ulong val) { typ_ = TOKT_INT; int_val_ = val; } /* * compare the text to the given string - returns true if the text * matches, false if not */ int text_matches(const char *txt, size_t len) const { return (len == text_len_ && memcmp(txt, text_, len) == 0); } int text_matches(const char *txt) const { return text_matches(txt, txt != 0 ? strlen(txt) : 0); } /* copy from a another token */ void set(const CTcToken &tok) { typ_ = tok.typ_; text_ = tok.text_; text_len_ = tok.text_len_; int_val_ = tok.int_val_; fully_expanded_ = tok.fully_expanded_; } private: /* token type */ tc_toktyp_t typ_; /* * Pointer to the token's text. This is a pointer into the * tokenizer's symbol table or into the token list itself, so this * pointer is valid as long as the tokenizer and its token list are * valid. */ const char *text_; size_t text_len_; /* integer value - valid when the token type is TOKT_INT */ ulong int_val_; /* * flag: the token has been fully expanded, and should not be * expanded further on any subsequent rescan for macros */ uint fully_expanded_ : 1; }; /* * Token list entry. This is a generic linked list element containing a * token. */ class CTcTokenEle: public CTcToken { public: CTcTokenEle() { nxt_ = prv_ = 0; } /* get/set the next element */ CTcTokenEle *getnxt() const { return nxt_; } void setnxt(CTcTokenEle *nxt) { nxt_ = nxt; } /* get/set the previous element */ CTcTokenEle *getprv() const { return prv_; } void setprv(CTcTokenEle *prv) { prv_ = prv; } protected: /* next/previous token in list */ CTcTokenEle *nxt_; CTcTokenEle *prv_; }; /* ------------------------------------------------------------------------ */ /* * Macro Expansion Resource object. This object is a collection of * resources that are needed for a macro expansion. To avoid frequent * allocating and freeing of these resources, we keep a pool of these * objects around so that we can re-use them as needed. We'll * dynamically expand the pool as necessary, so this doesn't impose any * pre-set limits; it simply avoids lots of memory allocation activity. */ class CTcMacroRsc { public: CTcMacroRsc() { /* we're not in any lists yet */ next_avail_ = 0; next_ = 0; } /* buffer for expansion of the whole line */ CTcTokString line_exp_; /* buffer for expansion of current macro on line */ CTcTokString macro_exp_; /* buffer for expansion of an actual parameter value */ CTcTokString actual_exp_buf_; /* next resource object in the "available" list */ CTcMacroRsc *next_avail_; /* next resource object in the master list */ CTcMacroRsc *next_; }; /* ------------------------------------------------------------------------ */ /* * Abstract token source interface. This is used to allow external code * to inject their own substreams into the main token stream. */ class CTcTokenSource { public: virtual ~CTcTokenSource() { } /* * Get the next token from the source. Returns null if there are no * more tokens. */ virtual const CTcToken *get_next_token() = 0; /* set the enclosing external token source and current token */ void set_enclosing_source(CTcTokenSource *src, const CTcToken *tok) { /* remember the enclosing source */ enclosing_src_ = src; /* remember the current token */ enclosing_curtok_ = *tok; } /* get the enclosing external token source */ CTcTokenSource *get_enclosing_source() const { return enclosing_src_; } /* get the token that was current when this source was inserted */ const CTcToken *get_enclosing_curtok() const { return &enclosing_curtok_; } protected: /* the enclosing external token source */ CTcTokenSource *enclosing_src_; /* * the current token in effect enclosing this source - this is the * token that comes immediately after the source's tokens, because a * source is inserted before the current token */ CTcToken enclosing_curtok_; }; /* ------------------------------------------------------------------------ */ /* * Newline spacing modes. The newline spacing mode controls how line * breaks are handled within strings. */ enum newline_spacing_mode_t { /* delete: a newline and immediately following whitespace are deleted */ NEWLINE_SPACING_DELETE = 0, /* * collapse: a newline and immediately following whitespace are * replaced with a single space character */ NEWLINE_SPACING_COLLAPSE = 1, /* * preserve: newlines and subsequent whitespace are preserved exactly * as written in the source code */ NEWLINE_SPACING_PRESERVE = 2 }; /* ------------------------------------------------------------------------ */ /* * Tokenizer. This object reads a file and constructs a representation * of the file as a token list in memory. The tokenizer interprets * preprocessor directives and expands macros. */ class CTcTokenizer { friend class CTcHashEntryPpDefine; public: /* * Create the tokenizer and start reading from the given file. The * default character set is generally specified by the user (on the * compiler command line, for example), or obtained from the * operating system. */ CTcTokenizer(class CResLoader *res_loader, const char *default_charset); /* destroy the tokenizer */ ~CTcTokenizer(); /* * Reset the tokenizer. Deletes the current source object and all * saved token text. This can be used after compilation of a unit * is completed and the intermediate parser state can be completely * discarded. */ void reset(); /* * Set the source file. 'src_filename' is the fully-resolved local * filename of the source file; 'orig_name' is the original name as * given on the command line, in the makefile, or wherever it came * from. We keep track of the original name so that we can pass * information to the debugger indicating the name as it was originally * given; this is more useful than the resolved filename, because we * might want to run the debugger on another machine with a different * local directory structure. */ int set_source(const char *src_filename, const char *orig_name); /* set the source to a memory buffer */ void set_source_buf(const char *buf) { set_source_buf(buf, buf != 0 ? strlen(buf) : 0); } void set_source_buf(const char *buf, size_t len); /* * Add a #include directory to the include path. We search the * include path in the order in which they were defined. */ void add_inc_path(const char *path); /* * Set preprocess-only mode. In this mode, we'll retain * preprocessor directives that will be needed if the preprocessed * result is itself compiled; for example, we'll retain #line, * #pragma C, #error, and #pragma message directives. */ void set_mode_pp_only(int flag) { pp_only_mode_ = flag; } /* * Set list-includes mode. In this mode, we'll simply scan source * files and write to the standard output a list of the names of all * of the #include files. */ void set_list_includes_mode(int flag) { list_includes_mode_ = flag; } /* * Get/set the test-report mode. In this mode, we'll expand __FILE__ * macros with the root name only. */ int get_test_report_mode() const { return test_report_mode_; } void set_test_report_mode(int flag) { test_report_mode_ = flag; } /* enable or disable preprocessing directives */ void enable_pp(int enable) { allow_pp_ = enable; } /* get the type of the current token */ tc_toktyp_t cur() const { return curtok_.gettyp(); } /* get the next token, reading a new line of source if necessary */ tc_toktyp_t next(); /* * Un-get the current token and back up to the previous token. The * previous token becomes the current token, and the current token is * saved on an internal stack to be fetched again after the returned * token. * * With no arguments, this backs up to the internally stored previous * token. This allows callers to look ahead by one token without * making a list of saved tokens. * * With an argument, this backs up to a prior token saved by the * caller. This can be used to back up arbitrarily many tokens - it's * up to the caller to save ungettable tokens and push them back into * the stream. Tokens must be ungotten in reverse order (e.g., get A, * B, C, D, unget D, C, B, A). Note that the argument is the PREVIOUS * token to restore, not the current token. * * Tokens un-got with this routine are accessible only to next(), not * to any of the lower-level token readers. */ void unget(); void unget(const CTcToken *prv); /* * Push a token into the token stream. The given token becomes the * next token to be retrieved. This doesn't affect the current token. */ void push(const CTcToken *tok); /* get the current token */ const class CTcToken *getcur() const { return &curtok_; } /* * Copy the current token. This makes a copy of the token's text in * tokenizer source memory, to ensure that the reference to the text * buffer the caller is keeping will remain valid forever. */ const class CTcToken *copycur(); /* make a safely storable copy of a given token */ void copytok(class CTcToken *dst, const class CTcToken *src); /* check to see if the current token matches the given text */ int cur_tok_matches(const char *txt, size_t len); int cur_tok_matches(const char *txt) { return cur_tok_matches(txt, strlen(txt)); } /* * Try looking ahead to match a pair of symbols. If the current token * is a TOKT_SYM token matching sym1, read the next token to see if * it's another TOKT_SYM matching sym2. If so, skip the second token * and return true. If not, unget tthe second (so the original token * is current again) and return false. */ int look_ahead(const char *sym1, const char *sym2); /* * peek ahead - same as look_ahead, but doesn't skip anything even on * successfully matching the token pair */ int peek_ahead(const char *sym1, const char *sym2); /* * Set an external token source. We'll read tokens from this source * until it is exhausted, at which point we'll revert to the enclosing * source. * * The new source is inserted before the current token, so the current * token will become current once again when this source is exhausted. * We'll automatically advance to the next token, which (unless we * have an ungotten token stashed) will go to the first token in the * new source. */ void set_external_source(CTcTokenSource *src) { /* * store the old source in the new source, so we can restore the * old source when we have exhausted the new source */ src->set_enclosing_source(ext_src_, &curtok_); /* set the new external source */ ext_src_ = src; /* skip to the next token */ next(); } /* clear all external sources, returning to the real token stream */ void clear_external_sources(); /* * assume that we should have found '>>' sequence after an embedded * expression in a string - used by parsers to resynchronize after * an apparent syntax error */ void assume_missing_str_cont(); /* define a macro */ void add_define(const char *sym, size_t len, const char *expansion, size_t expan_len); void add_define(const char *sym, const char *expansion, size_t expan_len) { add_define(sym, strlen(sym), expansion, expan_len); } void add_define(const char *sym, const char *expansion) { add_define(sym, strlen(sym), expansion, strlen(expansion)); } /* add a macro, given the symbol entry */ void add_define(class CTcHashEntryPp *entry); /* undefine a previously defined macro */ void undefine(const char *sym, size_t len); void undefine(const char *sym) { undefine(sym, strlen(sym)); } /* find a #define symbol */ class CTcHashEntryPp *find_define(const char *sym, size_t len) const; /* find an #undef symbol */ class CTcHashEntryPp *find_undef(const char *sym, size_t len) const; /* enumerate all of the #define symbols through a callback */ void enum_defines(void (*func)(void *ctx, class CTcHashEntryPp *entry), void *ctx); /* read the next line and handle preprocessor directives */ int read_line_pp(); /* get the file descriptor and line number of the last line read */ class CTcTokFileDesc *get_last_desc() const { return last_desc_; } long get_last_linenum() const { return last_linenum_; } void get_last_pos(class CTcTokFileDesc **desc, long *linenum) const { *desc = last_desc_; *linenum = last_linenum_; } /* * set the current file descriptor and line number -- this can be * used to force the line position to a previously-saved value * (during code generation, for example) for error-reporting and * debug-record purposes */ void set_line_info(class CTcTokFileDesc *desc, long linenum) { last_desc_ = desc; last_linenum_ = linenum; } /* * Parse a preprocessor constant expression. We always parse out of * the macro expansion buffer (expbuf_), but the caller must set p_ * to point to the starting point on the expansion line prior to * calling this routine. * * If 'read_first' is true, we'll read a token into curtok_ before * parsing; otherwise, we'll assume the caller has already primed * the pump by reading the first token. * * If 'last_on_line' is true, we'll flag an error if anything is * left on the line after we finish parsing the expression. * * If 'add_line_ending' is true, we'll add an end-of-line marker to * the expansion buffer, so that the tokenizer won't attempt to read * past the end of the line. Since a preprocessor expression must * be contained entirely on a single logical line, we must never try * to read past the end of the current line when parsing a * preprocessor expression. */ int pp_parse_expr(class CTcConstVal *result, int read_first, int last_on_line, int add_line_ending); /* log an error, optionally with parameters */ static void log_error(int errnum, ...); /* * log an error with the current token text as the parameter, * suitable for a "%.*s" format list entry (hence we'll provide two * parameters: an integer with the length of the token text, and a * pointer to the token text string) */ void log_error_curtok(int errnum); /* log a warning, optionally with parameters */ static void log_warning(int errnum, ...); /* log a pedantic warning */ static void log_pedantic(int errnum, ...); /* log a warning with the current token as the parameter */ void log_warning_curtok(int errnum); /* log a warning or error for the current token */ void log_error_or_warning_curtok(tc_severity_t sev, int errnum); /* log a warning or error for a given token */ void log_error_or_warning_with_tok(tc_severity_t sev, int errnum, const CTcToken *tok); /* * log then throw a fatal error (this is different from an internal * error in that it indicates an unrecoverable error in the input; * an internal error indicates that something is wrong with the * compiler itself) */ static void throw_fatal_error(int errnum, ...); /* * log then throw an internal error (internal errors are always * fatal: these indicate that something has gone wrong in the * compiler, and are equivalent to an assert failure) */ static void throw_internal_error(int errnum, ...); /* display a string/number value */ void msg_str(const char *str, size_t len) const; void msg_long(long val) const; /* get the current line */ const char *get_cur_line() const { return linebuf_.get_text(); } size_t get_cur_line_len() const { return linebuf_.get_text_len(); } /* get/set the #define hash table */ class CTcMacroTable *get_defines_table() const { return defines_; } class CTcMacroTable *set_defines_table(class CTcMacroTable *tab) { CTcMacroTable *old_tab = defines_; defines_ = tab; return old_tab; } /* * look up a token as a keyword; returns true and fills in 'kw' with * the keyword token ID if the token is in fact a keyword, or * returns false if it's not a keyword */ int look_up_keyword(const CTcToken *tok, tc_toktyp_t *kw); /* * Get the next token on the line, filling in the token object. * Advances the pointer to the character immediately following the * token. * * If the token is a string, and the string contains backslash * sequences, we'll modify the source string by translating each * backslash sequences; for example, a "\n" sequence is changed into an * ASCII 10. * * 'expanding' indicates whether or not we're in the initial macro * expansion pass. If this is true, we'll suppress error messages * during this pass, as we'll encounter the same tokens again when we * parse the expanded form of the line. */ static tc_toktyp_t next_on_line(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding); /* * Get the text of an operator token. Returns a pointer to a * constant, static, null-terminated string, suitable for use in * error messages. */ static const char *get_op_text(tc_toktyp_t op); /* * Store text in the source list. Text stored here is available * throughout compilation. This routine automatically reserves the * space needed, so do not call 'reserve' or 'commit' separately. */ const char *store_source(const char *txt, size_t len); /* reserve space for text in the source list */ void reserve_source(size_t len); /* * Store a piece of text into pre-reserved space in the source list. * This can be used to build up a string from several pieces. You must * call 'reserve' first to allocate the space, and you must explicitly * add a null terminator at the end of the string. Do not call * 'commit'; this automatically commits the space as each substring is * added. */ const char *store_source_partial(const char *txt, size_t len); /* * Get the index of the next source file descriptor that will be * created. The linker can use this information to fix up * references to file descriptors in an object file when loading * multiple object files. */ int get_next_filedesc_index() const { return next_filedesc_id_; } /* get the number of source file descriptors in the master list */ int get_filedesc_count() const { return next_filedesc_id_; } /* get the file descriptor at the given (0-based) index */ class CTcTokFileDesc *get_filedesc(size_t idx) const { /* return the array entry at the index, if the index is valid */ return (idx < desc_list_cnt_ ? desc_list_[idx] : 0); } /* get the head of the master source file descriptor list */ class CTcTokFileDesc *get_first_filedesc() const { return desc_head_; } /* * Create a new file descriptor and add it to the master list. This * creates the new descriptor unconditionally, even if a descriptor * for the same source file already exists. */ class CTcTokFileDesc *create_file_desc(const char *fname, size_t len) { return get_file_desc(fname, len, TRUE, fname, len); } /* * Set the string capture file. Once this is set, we'll write the * contents of each string token that we encounter to this file, * with a newline after each token. */ void set_string_capture(osfildef *fp); /* write macros to a file, for debugger use */ void write_macros_to_file_for_debug(class CVmFile *fp); /* * Load macros from a file. If any errors occur, we'll flag them * through the error handler object and return a non-zero value. * Returns zero on success. */ int load_macros_from_file(class CVmStream *fp, class CTcTokLoadMacErr *err_handler); /* receive notification that the compiler is done with all parsing */ void parsing_done() { /* forget any input file position */ set_line_info(0, 0); } /* * Stuff text into the tokenizer source stream. The new text is * inserted at the current read pointer, so that the next token we * fetch will come from the start of the inserted text. If 'expand' is * true, we'll expand macros in the text; if not, we'll insert the text * exactly as is with no macro expansion. */ void stuff_text(const char *txt, size_t len, int expand); private: /* skip whitespace and token markers */ static void skip_ws_and_markers(utf8_ptr *p); /* * get the next token on the line; if we go past the end of the * string buffer, we'll return EOF */ static tc_toktyp_t next_on_line(const CTcTokString *srcbuf, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding); /* * get the next token on the current line, updating the internal * character position pointer to point just past the token, and filling * in the internal current token object with the toen data */ tc_toktyp_t next_on_line() { return next_on_line(&p_, &curtok_, 0, FALSE); } /* get the next token on the line, with string translation */ tc_toktyp_t next_on_line_xlat(tok_embed_ctx *ec) { return next_on_line_xlat(&p_, &curtok_, ec); } /* * get the next token, translating strings and storing string and * symbol text in the source block list */ tc_toktyp_t next_on_line_xlat_keep(); /* * get the next token on the line, translating strings to internal * format */ tc_toktyp_t next_on_line_xlat(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec); /* * translate a string to internal format by converting escape * sequences; overwrites the original buffer */ tc_toktyp_t xlat_string(utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec); /* * translate a string into a given buffer; if 'force_embed_end' is * true, we'll act as though we're continuing the string after the * '>>' after an embedded expression, no matter what the actual * input looks like */ tc_toktyp_t xlat_string_to(char *dst, utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int force_embed_end); /* * Translate a string, saving the translated version in the source * block list. If 'force_end_embed' is true, we'll act as though we * were looking at '>>' (or, more precisely, we'll act as though * '>>' immediately preceded the current input), regardless of what * the actual input looks like. */ tc_toktyp_t xlat_string_to_src(tok_embed_ctx *ec, int force_end_embed); /* * is the given token type "safe" (i.e., stored in the tokenizer source * list, so that its text pointer is permanently valid; non-safe tokens * have valid text pointers only until the next token fetch) */ int is_tok_safe(tc_toktyp_t typ); /* initialize the source block list */ void init_src_block_list(); /* delete current source file, including all including parents */ void delete_source(); /* * Read the next line; processes comments, but does not expand macros * or parse preprocessor directives. This always reads into linebuf_; * the return value is the offset within linebuf_ of the new text. A * return value of -1 indicates that we're at end of file. */ int read_line(int append); /* * Set the source read pointer to the start of a new line, given the * CTcTokString object containing the buffer, and the offset within * that buffer. */ void start_new_line(CTcTokString *str, int ofs) { /* remember the buffer we're reading out of */ curbuf_ = str; /* set the read pointer to the start of the new line's text */ p_.set((char *)str->get_text() + ofs); } /* unsplice text from the current line and make it the next line */ void unsplice_line(const char *new_line_start); /* * Commit space in the source list - this is used when text is directly * stored after reserving space. The size reserved may be greater than * the size committed, because it is sometimes more efficient to make a * guess that may overestimate the amount we actually end up needing. */ void commit_source(size_t len); /* parse a string */ static tc_toktyp_t tokenize_string( utf8_ptr *p, CTcToken *tok, tok_embed_ctx *ec, int expanding_macros); /* process comments */ void process_comments(size_t start_ofs); /* splice lines for a string that runs across multiple lines */ void splice_string(); /* scan a sprintf format spec */ void scan_sprintf_spec(utf8_ptr *p); /* translate a \ escape sequence */ void xlat_escape(utf8_ptr *dst, utf8_ptr *p, wchar_t qu, int triple); /* skip an ordinary character or \ escape sequence */ void skip_escape(utf8_ptr *p); /* translate escape sequences in a string segment */ void xlat_escapes(utf8_ptr *dstp, const utf8_ptr *srcp, const utf8_ptr *endp); /* expand macros in the current line */ int expand_macros_curline(int read_more, int allow_defined, int append_to_expbuf); /* * Expand the macros in the given text, filling in the given * CTcTokString with the results. The expansion will clear out any * existing text in the result buffer. Returns zero on success, or * non-zero on error. */ int expand_macros(class CTcTokString *dest, const char *str, size_t len) { CTcTokStringRef srcbuf; /* set up a CTcTokString for the source */ srcbuf.set_buffer(str, len); /* go expand macros */ return expand_macros(&srcbuf, 0, dest, FALSE, FALSE, FALSE); } /* expand all of the macros in the given text */ int expand_macros(class CTcTokString *srcbuf, utf8_ptr *src, class CTcTokString *expbuf, int read_more, int allow_defined, int append); /* expand the macro at the current token on the current line */ int expand_macro(class CTcMacroRsc *res, class CTcTokString *expbuf, const class CTcTokString *srcbuf, utf8_ptr *src, size_t macro_srcbuf_ofs, CTcHashEntryPp *entry, int read_more, int allow_defined, int *expanded); /* * Remove our special expansion flags from an expanded macro buffer. * This can be called after all expansion has been completed to clean * up the buffer for human consumption. */ void remove_expansion_flags(CTcTokString *buf); /* scan for a prior expansion of a macro within the current context */ static int scan_for_prior_expansion(utf8_ptr src, const char *src_end, const class CTcHashEntryPp *entry); /* remove end-of-macro-expansion flags from a buffer */ static void remove_end_markers(class CTcTokString *buf); /* change a buffer to use individual token full-expansion markers */ void mark_full_exp_tokens(CTcTokString *dstbuf, const class CTcTokString *srcbuf, int append) const; /* allocate a macro expansion resource */ class CTcMacroRsc *alloc_macro_rsc(); /* release a macro expansion resource */ void release_macro_rsc(class CTcMacroRsc *rsc); /* * Parse the actual parameters to a macro. Fills in argofs[] and * arglen[] with the offsets (from srcbuf->get_buf()) and lengths, * respectively, of each actual parameter's text. */ int parse_macro_actuals(const class CTcTokString *srcbuf, utf8_ptr *src, const CTcHashEntryPp *macro_entry, size_t argofs[TOK_MAX_MACRO_ARGS], size_t arglen[TOK_MAX_MACRO_ARGS], int read_more, int *found_actuals); /* splice the next line for reading more macro actuals */ tc_toktyp_t actual_splice_next_line(const CTcTokString *srcbuf, utf8_ptr *src, CTcToken *tok); /* substitute the actual parameters in a macro's expansion */ int substitute_macro_actuals(class CTcMacroRsc *rsc, class CTcTokString *subexp, CTcHashEntryPp *macro_entry, const class CTcTokString *srcbuf, const size_t *argofs, const size_t *arglen, int allow_defined); /* stringize a macro actual parameter into an expansion buffer */ void stringize_macro_actual(class CTcTokString *expbuf, const char *actual_val, size_t actual_len, char quote_char, int add_open_quote, int add_close_quote); /* skip a delimited macro expansion area (#foreach, #ifempty, etc) */ void skip_delimited_group(utf8_ptr *p, int parts_to_skip); /* expand a defined() preprocessor operator */ int expand_defined(class CTcTokString *subexp, const class CTcTokString *srcbuf, utf8_ptr *src); /* add a file to the list of files to be included only once */ void add_include_once(const char *fname); /* find a file in the list of files to be included only once */ int find_include_once(const char *fname); /* process a #pragma directive */ void pp_pragma(); /* process a #charset directive */ void pp_charset(); /* process a #include directive */ void pp_include(); /* process a #define directive */ void pp_define(); /* process a #if directive */ void pp_if(); /* process a #ifdef directive */ void pp_ifdef(); /* process a #ifdef directive */ void pp_ifndef(); /* process a #ifdef or #ifndef */ void pp_ifdef_or_ifndef(int sense); /* process a #else directive */ void pp_else(); /* process a #elif directive */ void pp_elif(); /* process a #endif directive */ void pp_endif(); /* process a #error directive */ void pp_error(); /* process a #undef directive */ void pp_undef(); /* process a #line directive */ void pp_line(); /* get a lone identifier for a preprocessor directive */ int pp_get_lone_ident(char *buf, size_t bufl); /* process a #pragma C directive */ // void pragma_c(); - not currently used /* process a #pragma once directive */ void pragma_once(); /* process a #pragma all_once directive */ void pragma_all_once(); /* process a #pragma message directive */ void pragma_message(); /* process a #pragma newline_spacing(on/off) directive */ void pragma_newline_spacing(); /* process a #pragma sourceTextGroup directive */ void pragma_source_text_group(); /* * Determine if we're in a false #if branch. If we're inside a #if * block, and the state is either IF_NO, IF_DONE, or ELSE_NO, or * we're inside a #if nested within any negative branch, we're in a * not-taken branch of a #if block. */ int in_false_if() const { return (if_sp_ != 0 && (if_false_level_ != 0 || if_stack_[if_sp_ - 1].state == TOKIF_IF_NO || if_stack_[if_sp_ - 1].state == TOKIF_IF_DONE || if_stack_[if_sp_ - 1].state == TOKIF_ELSE_NO)); } /* push a new #if level with the given state */ void push_if(tok_if_t state); /* get the current #if state */ tok_if_t get_if_state() const { if (if_sp_ == 0) return TOKIF_NONE; else return if_stack_[if_sp_ - 1].state; } /* switch the current #if level to the given state */ void change_if_state(tok_if_t state) { if (if_sp_ != 0) if_stack_[if_sp_ - 1].state = state; } /* pop the current #if level */ void pop_if(); /* * Find or create a descriptor for the given filename. 'fname' is * the full file system path specifying the file. 'orig_fname' is * the filename as originally specified by the user, if different; * in the case of #include files, this indicates the name that was * specified in the directive itself, whereas 'fname' is the actual * filename that resulted from searching the include path for the * given name. */ class CTcTokFileDesc *get_file_desc(const char *fname, size_t fname_len, int always_create, const char *orig_fname, size_t orig_fname_len); /* clear the line buffer */ void clear_linebuf(); /* flag: ALL_ONCE mode - we include each file only once */ unsigned int all_once_ : 1; /* flag: warn on ignoring a redundant #include file */ unsigned int warn_on_ignore_incl_ : 1; /* * Flag: in preprocess-only mode. In this mode, we'll leave certain * preprocessor directives intact in the source, since they'll be * needed in a subsequent compilation of the preprocessed source. * For example, we'll leave #line directives, #pragma C, #error, and * #pragma message directives in the preprocessed result. */ unsigned int pp_only_mode_ : 1; /* * Flag: in test reporting mode. In this mode, we'll expand __FILE__ * macros with the root name only. */ unsigned int test_report_mode_ : 1; /* * Flag: in preprocess-for-includes mode. In this mode, we'll do * nothing except run the preprocessor and generate a list of the * header files that are included, along with header files they * include, and so on. */ unsigned int list_includes_mode_ : 1; /* * flag: we're parsing a preprocessor constant expression (for a * #if, for example; this doesn't apply to simple macro expansion) */ unsigned int in_pp_expr_ : 1; /* mode for handling newlines in strings */ newline_spacing_mode_t string_newline_spacing_; /* resource loader */ class CResLoader *res_loader_; /* * name of our default character set - this is generally specified * by the user (on the compiler command line, for example), or * obtained from the operating system */ char *default_charset_; /* input (to unicode) character mapper for the default character set */ class CCharmapToUni *default_mapper_; /* head of list of previously-included files */ struct tctok_incfile_t *prev_includes_; /* head and tail of include path list */ struct tctok_incpath_t *incpath_head_; struct tctok_incpath_t *incpath_tail_; /* file descriptor and line number of last line read */ class CTcTokFileDesc *last_desc_; long last_linenum_; /* file descriptor and line number of last line appended */ class CTcTokFileDesc *appended_desc_; long appended_linenum_; /* current input stream */ class CTcTokStream *str_; /* master list of file descriptors */ class CTcTokFileDesc *desc_head_; class CTcTokFileDesc *desc_tail_; /* * array of file descriptors (we keep the list in both an array and * a linked list, since we need both sequential and indexed access; * this isn't a lot of trouble since we never need to remove an * entry from the list) */ class CTcTokFileDesc **desc_list_; /* number of entries in desc_list_ */ size_t desc_list_cnt_; /* number of slots allocated in desc_list_ array */ size_t desc_list_alo_; /* next file descriptor ID to be assigned */ int next_filedesc_id_; /* pointer to current position in current line */ utf8_ptr p_; /* * The CTcTokString object containing the current line. This is the * buffer object we're currently reading from, and will be either * linebuf_ or expbuf_. p_ always points into this buffer. */ CTcTokString *curbuf_; /* raw file input buffer */ CTcTokString linebuf_; /* * unsplice buffer - we'll put any unspliced text into this buffer, * then read it back at the next read_line() */ CTcTokString unsplicebuf_; /* macro expansion buffer */ CTcTokString expbuf_; /* macro definition parsing buffer */ CTcTokString defbuf_; /* * Flag: in a string. If this is '\0', we're not in a string; * otherwise, this is the quote character that ends the string. */ wchar_t in_quote_; /* flag: the in_quote_ string we're in is triple quoted */ int in_triple_; /* embedded expression context in comment */ tok_embed_ctx comment_in_embedding_; /* embedded expression context within a macro expansion */ tok_embed_ctx macro_in_embedding_; /* embedded expression context for the main token stream */ tok_embed_ctx main_in_embedding_; /* * #if state stack. if_sp_ is the index of the next nesting slot; * if if_sp_ is zero, it means that we're not in a #if at all. * * Separately, the if_false_level_ is the level of #if's contained * within a false #if branch. This is separate because, once we're * in a false #if branch, everything within it is false. */ int if_sp_; tok_if_info_t if_stack_[TOK_MAX_IF_NESTING]; int if_false_level_; /* source block list head */ CTcTokSrcBlock *src_head_; /* current (and last) source block */ CTcTokSrcBlock *src_cur_; /* pointer to next available byte in the current source block */ char *src_ptr_; /* number of bytes remaining in the current source block */ size_t src_rem_; /* current token */ CTcToken curtok_; /* previous token (for unget) */ CTcToken prvtok_; /* head of allocated list of unget token slots */ CTcTokenEle *unget_head_; /* * Last unget token. This is a pointer into the unget list, to the * element containing the last ungotten token. The next next() will * reinstate this token and move the pointer back into the list; the * next unget() will move the pointer forward and fill in the next * element. This is null if there are no ungotten tokens. */ CTcTokenEle *unget_cur_; /* the external token source, if any */ CTcTokenSource *ext_src_; /* symbol table for #define symbols */ class CTcMacroTable *defines_; /* * symbol table for symbols explicitly undefined; we keep track of * these so that we can exclude anything ever undefined from the debug * macro records, since only static global macros can be handled in the * debug records */ class CVmHashTable *undefs_; /* symbol table for TADS keywords */ class CVmHashTable *kw_; /* head of macro resource pool list */ class CTcMacroRsc *macro_res_head_; /* head of list of available macro resources */ class CTcMacroRsc *macro_res_avail_; /* * string capture file - if this is non-null, we'll capture all of * the strings we read to this file, one string per line */ class CVmDataSource *string_fp_; /* character mapper for writing to the string capture file */ class CCharmapToLocal *string_fp_map_; /* true -> allow preprocessor directives */ unsigned int allow_pp_; }; /* ------------------------------------------------------------------------ */ /* * Error handler interface. Callers of load_macros_from_file() in * CTcTokenizer must provide an implementation of this interface to handle * errors that occur while loading macros. */ class CTcTokLoadMacErr { public: /* * Flag an error. The error codes are taken from the following list: * * 1 - a macro name symbol in the file is too long (it exceeds the * maximum symbol length for the preprocessor) * * 2 - a formal parameter name is too long */ virtual void log_error(int err) = 0; }; /* ------------------------------------------------------------------------ */ /* * Tokenizer File Descriptor. Each unique source file has a separate * file descriptor, which keeps track of the file's name. */ class CTcTokFileDesc { public: /* create a file descriptor */ CTcTokFileDesc(const char *fname, size_t fname_len, int index, CTcTokFileDesc *orig_desc, const char *orig_fname, size_t orig_fname_len); /* delete the descriptor */ ~CTcTokFileDesc(); /* get the filename */ const char *get_fname() const { return fname_; } /* get the original filename string */ const char *get_orig_fname() const { return orig_fname_; } /* * get the filename as a double-quoted string (backslashes and * double-quotes will be escaped with backslashes) */ const char *get_dquoted_fname() const { return dquoted_fname_; } /* * get the root filename (i.e., with no path prefix) as a * double-quoted string */ const char *get_dquoted_rootname() const { return dquoted_rootname_; } /* get the filename as a single-quoted string */ const char *get_squoted_fname() const { return squoted_fname_; } /* get the root filename as a single-quoted string */ const char *get_squoted_rootname() const { return squoted_rootname_; } /* get/set the next file descriptor in the descriptor chain */ CTcTokFileDesc *get_next() const { return next_; } void set_next(CTcTokFileDesc *nxt) { next_ = nxt; } /* get my index in the master list */ int get_index() const { return index_; } /* get the original descriptor for this file in the list */ CTcTokFileDesc *get_orig() const { return orig_; } /* * get the list index of the original entry (returns my own list * index if I am the original entry) */ int get_orig_index() const { return orig_ == 0 ? index_ : orig_->get_index(); } /* * Add a source line position to our list. We keep an index of the * byte-code address for each executable source line, so that * debuggers can find the compiled code corresponding to a source * location. The image builder gives us this information during the * linking process. The address is the absolute location in the * image file of the executable code for the given source line (the * first line in the file is numbered 1). */ void add_source_line(ulong linenum, ulong line_addr); /* * Enumerate the source lines, calling the callback for each one. * We will only enumerate source lines which actually have an * associated code location - source lines that generated no * executable code are skipped. We'll enumerate the lines in * ascending order of line number, and each line number will appear * only once. */ void enum_source_lines(void (*cbfunc)(void *ctx, ulong linenum, ulong byte_code_addr), void *cbctx); private: /* index in the master list */ int index_; /* filename string - this is the actual file system filename */ char *fname_; /* * original filename string, if different from fname_ - this is the * filename as specified by the user, before it was adjusted with * include paths or other extra location information */ char *orig_fname_; /* double-quoted version of the filename */ char *dquoted_fname_; /* single-quoted version of the filename */ char *squoted_fname_; /* single-quoted version of the root filename */ char *squoted_rootname_; /* double-quoted version of the root filename */ char *dquoted_rootname_; /* next descriptor in the master descriptor list */ CTcTokFileDesc *next_; /* * The original file descriptor with the same filename. If we * create multiple descriptors for the same filename (because, for * example, the same header is included in several different object * files), we'll keep track of the original descriptor for the file * in all of the copies. */ CTcTokFileDesc *orig_; /* source line pages */ struct CTcTokSrcPage **src_pages_; /* number of source line page slots allocated */ size_t src_pages_alo_; }; /* ------------------------------------------------------------------------ */ /* * Tokenizer Input Stream */ class CTcTokStream { public: /* create a token stream */ CTcTokStream(class CTcTokFileDesc *desc, class CTcSrcObject *src, CTcTokStream *parent, int charset_error, int init_if_level); /* delete the stream */ ~CTcTokStream(); /* get/set the associated file descriptor */ class CTcTokFileDesc *get_desc() const { return desc_; } void set_desc(class CTcTokFileDesc *desc) { desc_ = desc; } /* get the underlying source file */ class CTcSrcObject *get_src() const { return src_; } /* get the line number of the next line to be read */ long get_next_linenum() const { return next_linenum_; } /* set the next line number */ void set_next_linenum(long l) { next_linenum_ = l; } /* get the enclosing stream */ CTcTokStream *get_parent() const { return parent_; } /* count having read a line */ void count_line() { ++next_linenum_; } /* was there a #charset error when opening the file? */ int get_charset_error() const { return charset_error_; } /* get/set the in-comment status */ int is_in_comment() const { return in_comment_; } void set_in_comment(int f) { in_comment_ = f; } /* get/set the pragma C mode */ // int is_pragma_c() const { return pragma_c_; } // void set_pragma_c(int f) { pragma_c_ = f; } /* get/set if nesting level at the start of the file */ int get_init_if_level() const { return init_if_level_; } void set_init_if_level(int level) { init_if_level_ = level; } /* get/set the newline spacing mode */ newline_spacing_mode_t get_newline_spacing() const { return newline_spacing_; } void set_newline_spacing(newline_spacing_mode_t f) { newline_spacing_ = f; } private: /* file descriptor associated with this file */ class CTcTokFileDesc *desc_; /* the underlying source reader */ class CTcSrcObject *src_; /* * the enclosing stream - this is the stream that #include'd the * current stream */ CTcTokStream *parent_; /* line number of next line to be read */ ulong next_linenum_; /* #if nesting level at the start of the file */ int init_if_level_; /* newline_spacing mode when the stream was stacked */ newline_spacing_mode_t newline_spacing_; /* flag: we were unable to load the map in the #charset directive */ uint charset_error_ : 1; /* the stream is in a multi-line comment */ uint in_comment_ : 1; /* flag: we're in #pragma C+ mode */ // uint pragma_c_ : 1; - #pragma C is not currently used }; /* ------------------------------------------------------------------------ */ /* * Keyword Hash Table Entry */ class CTcHashEntryKw: public CVmHashEntryCS { public: CTcHashEntryKw(const textchar_t *str, tc_toktyp_t tokid) : CVmHashEntryCS(str, strlen(str), FALSE) { /* save the token ID for the keyword */ tokid_ = tokid; } /* get the token ID */ tc_toktyp_t get_tok_id() const { return tokid_; } private: /* our token ID */ tc_toktyp_t tokid_; }; /* ------------------------------------------------------------------------ */ /* * basic #define symbol table entry */ class CTcHashEntryPp: public CVmHashEntryCS { public: CTcHashEntryPp(const textchar_t *str, size_t len, int copy) : CVmHashEntryCS(str, len, copy) { /* by default, we have no arguments */ has_args_ = FALSE; has_varargs_ = FALSE; argc_ = 0; argv_ = 0; params_table_ = 0; } /* get the expansion text */ virtual const char *get_expansion() const = 0; virtual size_t get_expan_len() const = 0; /* get the original expansion text, before parsing */ virtual const char *get_orig_expansion() const { return get_expansion(); } virtual size_t get_orig_expan_len() const { return get_expan_len(); } /* certain special macros (__LINE__, __FILE__) aren't undef'able */ virtual int is_undefable() const { return TRUE; } /* * most macros are real symbols, created by #define's, but some are * special pseudo-macros, like __LINE__ and __FILE__, that the * preprocessor provides */ virtual int is_pseudo() const { return FALSE; } /* does the macro have an argument list? */ int has_args() const { return has_args_; } /* get the number of arguments */ int get_argc() const { return argc_; } /* do we have a variable number of arguments? */ int has_varargs() const { return has_varargs_; } /* * get the minimum number of allowed arguments - if we have varargs, * this is one less than the number of formals listed, since the last * formal can correspond to any number of actuals, including zero */ int get_min_argc() const { return has_varargs_ ? argc_ - 1 : argc_; } /* get the name of an argument by position (0 = first argument) */ const char *get_arg_name(int idx) const { return argv_[idx]; } /* get the parameter hash table entry for the parameter */ class CTcHashEntryPpArg *get_arg_entry(int idx) const { return arg_entry_[idx]; } /* get the parameters hash table */ const CVmHashTable *get_params_table() const { return params_table_; } protected: /* argument list */ char **argv_; /* list of parameter hash entries */ class CTcHashEntryPpArg **arg_entry_; /* parameter hash table */ CVmHashTable *params_table_; /* argument count */ int argc_; /* flag: the macro has a parameter list */ uint has_args_ : 1; /* * flag: the parameter list takes a variable number of arguments; if * this is set, then argc_ is one greater than the minimum number of * arguments required, and the last formal receives the varying part * of the actual parameter list, which can contain zero or more * actuals */ uint has_varargs_ : 1; }; /* * #define symbol hash table entry */ class CTcHashEntryPpDefine: public CTcHashEntryPp { public: /* * Create the hash entry. argc is the number of arguments to the * macro, and argv is an array of pointers to null-terminated * strings with the argument names, in the order defined in the * macro. * * If has_args is false, the macro does not take a parameter list at * all. Note that it is possible for has_args to be true and argc * to be zero, because a macro can be defined to take an argument * list with no arguments (i.e., empty parens). A macro with an * empty argument list is distinct from a macro with no argument * list: in the former case, the empty parens are required, and are * removed from the input stream and replaced with the macro's * expansion. * * We'll make a copy of the argument list vector, strings, and * expansion text, so the caller is free to forget all of that after * creating the entry instance. */ CTcHashEntryPpDefine(const textchar_t *str, size_t len, int copy, int has_args, int argc, int has_varargs, const char **argv, const size_t *argvlen, const char *expansion, size_t expan_len); ~CTcHashEntryPpDefine(); /* * Get the expansion text and its length. This is the parsed version * of the expansion, with occurrences of the formal parameters replaced * by TOK_MACRO_FORMAL_FLAG sequences, and the various '#' sequences * replaced by their corresponding flags. */ const char *get_expansion() const { return expan_; } size_t get_expan_len() const { return expan_len_; } /* get the original expansion - the original text before parsing */ const char *get_orig_expansion() const { return orig_expan_; } size_t get_orig_expan_len() const { return orig_expan_len_; } private: /* parse the expansion */ void parse_expansion(const size_t *argvlen); /* expansion (parsed version) */ char *expan_; size_t expan_len_; /* original expansion (before parsing) */ char *orig_expan_; size_t orig_expan_len_; }; /* * Hash table entry for __FILE__ and __LINE__ */ class CTcHashEntryPpSpecial: public CTcHashEntryPp { public: CTcHashEntryPpSpecial(CTcTokenizer *tok, const char *str) : CTcHashEntryPp(str, strlen(str), FALSE) { /* remember my tokenizer */ tok_ = tok; } /* these special macros are not undef'able */ virtual int is_undefable() const { return FALSE; } /* special macros are pseudo-macros provided by the preprocessor */ virtual int is_pseudo() const { return TRUE; } protected: /* my tokenizer */ CTcTokenizer *tok_; }; class CTcHashEntryPpFILE: public CTcHashEntryPpSpecial { public: CTcHashEntryPpFILE(CTcTokenizer *tok) : CTcHashEntryPpSpecial(tok, "__FILE__") { } /* our expansion is the current filename, in single quotes */ const char *get_expansion() const { return get_base_text(); } size_t get_expan_len() const { return strlen(get_base_text()); } private: /* get our expansion base text */ const char *get_base_text() const { /* * if we're in test-report mode, use the root name only; * otherwise, use the full name with path */ if (tok_->get_last_desc() == 0) return ""; else if (tok_->get_test_report_mode()) return tok_->get_last_desc()->get_squoted_rootname(); else return tok_->get_last_desc()->get_squoted_fname(); } }; class CTcHashEntryPpLINE: public CTcHashEntryPpSpecial { public: CTcHashEntryPpLINE(CTcTokenizer *tok) : CTcHashEntryPpSpecial(tok, "__LINE__") { } /* our expansion is the line number as a decimal string */ const char *get_expansion() const { gen_expansion(tok_); return buf_; } size_t get_expan_len() const { gen_expansion(tok_); return strlen(buf_); } private: /* generate the expansion text into our internal buffer */ static void gen_expansion(CTcTokenizer *tok) { sprintf(buf_, "%ld", tok->get_last_linenum()); } /* internal buffer */ static char buf_[20]; }; /* * Hash entry for preprocessor arguments */ class CTcHashEntryPpArg: public CVmHashEntryCS { public: CTcHashEntryPpArg(const char *str, size_t len, int copy, int argnum) : CVmHashEntryCS(str, len, copy) { /* remember the argument number */ argnum_ = argnum; } /* get my argument number */ int get_argnum() const { return argnum_; } private: /* argument number */ int argnum_; }; /* ------------------------------------------------------------------------ */ /* * Previously-included file list entry. Each time we include a file, * we'll add an entry to a list of files; in the future, we'll consult * this list to ensure that we don't include the same file again. */ struct tctok_incfile_t { /* next entry in the list of previously-included files */ tctok_incfile_t *nxt; /* name of this file (we'll allocate memory to hold the name) */ char fname[1]; }; /* ------------------------------------------------------------------------ */ /* * Include path list entry. This structure defines one include path; we * maintain a list of these structures. */ struct tctok_incpath_t { /* next entry in the list */ tctok_incpath_t *nxt; /* path */ char path[1]; }; #endif /* TCTOK_H */ frobtads-1.2.3/tads3/tcmake.h0000644000175000001440000007147711725241127015161 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcmake.h - TADS 3 Compiler "Make" Engine Function The "Make" engine doesn't have a main program entrypoint; instead, this class is meant to make it easy to write a main entrypoint. The program main function must parse command line arguments, read a config file, get the parameters from a dialog, or use whatever other OS-specific mechanism it desires to obtain the compilation parameters. Given the parameters, this class makes it easy to compile the program. To compile a TADS 3 program, create a CTcMake object, then perform the following steps: - set the build options (source/symbol/object paths, debug mode, error options, etc) - add a module object for each source file - set the image file name - invoke build() Notes Modified 07/11/99 MJRoberts - Creation */ #ifndef TCMAKE_H #define TCMAKE_H #include "t3std.h" /* ------------------------------------------------------------------------ */ /* * String buffer object */ class CTcMakeStr { public: CTcMakeStr() { buf_ = 0; } ~CTcMakeStr() { lib_free_str(buf_); } /* get the string */ const textchar_t *get() const { return (buf_ != 0 ? buf_ : ""); } /* set the string */ void set(const textchar_t *str) { set(str, str == 0 ? 0 : strlen(str)); } void set(const textchar_t *str, size_t len) { lib_free_str(buf_); buf_ = lib_copy_str(str, len); } /* check to see if the string has been set - returns true if so */ int is_set() const { return buf_ != 0; } private: /* our string buffer */ textchar_t *buf_; }; /* ------------------------------------------------------------------------ */ /* * Module source types. This tells us how we resolved the given name of * the module to a filename. */ enum tcmod_source_t { /* "normal" - the module is found on the ordinary search path */ TCMOD_SOURCE_NORMAL = 1, /* the module comes from the system library */ TCMOD_SOURCE_SYSLIB = 2, /* the module comes from a specific user library */ TCMOD_SOURCE_LIB = 3 }; /* * Module list entry. Each module is represented by one of these * entries. * * A module defines a source file and its directly derived files: a * symbol file, and an object file. */ class CTcMakeModule { public: CTcMakeModule() { /* not in a list yet */ nxt_ = 0; /* presume we won't need recompilation */ needs_sym_recompile_ = FALSE; needs_obj_recompile_ = FALSE; /* by default, include all modules in the compilation */ exclude_ = FALSE; /* presume the module is from the ordinary search path */ source_type_ = TCMOD_SOURCE_NORMAL; /* we don't have a sequence number yet */ seqno_ = 0; } ~CTcMakeModule() { } /* get/set the next list entry */ CTcMakeModule *get_next() const { return nxt_; } void set_next(CTcMakeModule *nxt) { nxt_ = nxt; } /* get/set the sequence number */ int get_seqno() const { return seqno_; } void set_seqno(int n) { seqno_ = n; } /* * Set the module name. This will fill in the source, symbol, and * object filenames with names derived from the source name if those * names are not already defined. If any of the names are already * explicitly defined, this won't affect them. */ void set_module_name(const textchar_t *modname); /* * Get/set the original module name. The original module name is the * name as it was given on the command line, in the library source, or * wherever else it was originally specified. This is the name before * conversion to local filename conventions and before resolving to a * specific directory; this is useful in debugging records because it * can be more portable than the resolved filename. */ const textchar_t *get_orig_name() const { return orig_name_.get(); } void set_orig_name(const textchar_t *name) { orig_name_.set(name); } /* get/set the source filename */ const textchar_t *get_source_name() const { return src_.get(); } void set_source_name(const textchar_t *fname) { src_.set(fname); } /* * Get/set the "search" source filename. This is the filename we use * when we're trying to find the file in a search path. In some cases, * this might not be identical to the given filename; for example, when * we assume a relative path for a file, we won't use the assumed * relative path in the search name, since we only assumed the relative * path because we thought that would tell us where the file is. */ const textchar_t *get_search_source_name() const { /* * if there's an explicit search name, use it; otherwise, simply * use the normal source name */ if (search_src_.get() != 0 && search_src_.get()[0] != '\0') return search_src_.get(); else return src_.get(); } void set_search_source_name(const textchar_t *fname) { search_src_.set(fname); } /* get/set the symbol filename */ const textchar_t *get_symbol_name() const { return sym_.get(); } void set_symbol_name(const textchar_t *fname) { sym_.set(fname); } /* get/set the object filename */ const textchar_t *get_object_name() const { return obj_.get(); } void set_object_name(const textchar_t *fname) { obj_.set(fname); } /* get/set the symbol file recompilation flag */ int get_needs_sym_recompile() const { return needs_sym_recompile_; } void set_needs_sym_recompile(int flag) { needs_sym_recompile_ = flag; } /* get/set the object file recompilation flag */ int get_needs_obj_recompile() const { return needs_obj_recompile_; } void set_needs_obj_recompile(int flag) { needs_obj_recompile_ = flag; } /* get/set the exclusion flag */ int is_excluded() const { return exclude_; } void set_excluded(int exc) { exclude_ = exc; } /* get/set the library member URL */ const char *get_url() const { return url_.get(); } void set_url(const char *url) { url_.set(url); } /* get the module's source type */ tcmod_source_t get_source_type() const { return source_type_; } /* set the module's source to the system library */ void set_from_syslib() { source_type_ = TCMOD_SOURCE_SYSLIB; } /* * set the module's source to the given library, specified via * URL-style library path (so a module in library 'bar' that was in * turn in library 'foo' will have library path 'foo/bar') */ void set_from_lib(const textchar_t *libname, const textchar_t *url) { /* save the library's name and URL */ lib_name_.set(libname); lib_url_.set(url); /* remember that we're from a library */ source_type_ = TCMOD_SOURCE_LIB; } /* get our library URL */ const textchar_t *get_from_lib() const { return lib_name_.get(); } protected: /* next entry in the list */ CTcMakeModule *nxt_; /* source filename */ CTcMakeStr src_; /* * sequence number - this is simply an ordinal giving our position in * the list of modules making up the build */ int seqno_; /* path-searching source filename */ CTcMakeStr search_src_; /* the original name, before local file path resolution */ CTcMakeStr orig_name_; /* symbol filename */ CTcMakeStr sym_; /* object filename */ CTcMakeStr obj_; /* flag: requires recompilation of symbol/object file */ int needs_sym_recompile_; int needs_obj_recompile_; /* * flag: module is excluded from compilation (this is set when a * module is included by a library and then explicitly excluded from * the build) */ int exclude_; /* * Library member URL - this is the URL-style string naming the module * if it is a member of a library. This is not used except for * library members. This value is formed by adding the library prefix * for each enclosing sublibrary (not including the top-level library, * included from the command line or equivalent) to the "source:" * variable value that included this module from its library. A * library prefix is formed by adding the library prefix for each * enclosing sublibrary to the "library:" variable name that included * the library, plus a terminating "/". */ CTcMakeStr url_; /* the name of the enclosing library */ CTcMakeStr lib_name_; /* * library URL - this is the URL to our enclosing library, not * including the module name itself */ CTcMakeStr lib_url_; /* the source of the module */ enum tcmod_source_t source_type_; }; /* ------------------------------------------------------------------------ */ /* * Search path list entry - each entry in this list is a directory that * we'll search for a certain type of file (#include files, source files) */ class CTcMakePath { public: CTcMakePath(const textchar_t *path) { /* remember the path */ path_.set(path); /* we're not in a list yet */ nxt_ = 0; } /* get/set the path */ const textchar_t *get_path() const { return path_.get(); } void set_path(const textchar_t *path) { path_.set(path); } /* get/set the next list entry */ CTcMakePath *get_next() const { return nxt_; } void set_next(CTcMakePath *nxt) { nxt_ = nxt; } protected: /* our path string */ CTcMakeStr path_; /* next include path in the list */ CTcMakePath *nxt_; }; /* ------------------------------------------------------------------------ */ /* * Preprocessor symbol definition or undefinition item */ class CTcMakeDef { public: CTcMakeDef(const textchar_t *sym, const textchar_t *expan, int is_def) { /* remember the symbol and its expansion */ sym_.set(sym); expan_.set(expan); /* remember whether it's a definition or un-definition */ is_def_ = (is_def != 0); /* not in a list yet */ nxt_ = 0; } /* get my symbol */ const textchar_t *get_sym() const { return sym_.get(); } /* get my expansion text */ const textchar_t *get_expan() const { return expan_.get(); } /* get my define/undefine flag */ int is_def() const { return is_def_ != 0; } /* get/set the next list entry */ CTcMakeDef *get_next() const { return nxt_; } void set_next(CTcMakeDef *nxt) { nxt_ = nxt; } protected: /* next in the list */ CTcMakeDef *nxt_; /* the symbol to define or undefine */ CTcMakeStr sym_; /* the expansion text */ CTcMakeStr expan_; /* flag: true -> define the symbol, false -> undefine it */ unsigned int is_def_ : 1; }; /* ------------------------------------------------------------------------ */ /* * The program maintenance facility class. The program main entrypoint * constructs one of these objects, sets its parameters, then calls the * "build()" entrypoint to carry out the build instructions. * * The caller should not initialize or use compiler globals. This * object owns the compiler globals, and will create and destroy * compiler globals in the course of its processing. */ class CTcMake { public: CTcMake(); ~CTcMake(); /* * Set the default source file character set. Source and header * files that don't specify a character set (using a #charset * directive at the very start of the file) will be read using this * character set. If this isn't specified, we'll use the default * character set obtained from the OS. */ void set_source_charset(const textchar_t *charset) { /* delete any previous character set string */ lib_free_str(source_charset_); /* store the new character set name */ source_charset_ = lib_copy_str(charset); } /* * turn on/off source-level debugging - we'll generate the extra * information necessary for debugging the program */ void set_debug(int debug) { debug_ = debug; } /* set preprocess-only mode */ void set_pp_only(int pp_only) { pp_only_ = pp_only; } /* set list-include-files mode */ void set_list_includes(int f) { list_includes_mode_ = f; } /* set "clean" mode */ void set_clean_mode(int f) { clean_mode_ = f; } /* * set test reporting mode - in this mode, we suppress path names in * filenames in progress reports, so that the output is independent of * local path conventions */ void set_test_report_mode(int flag) { test_report_mode_ = flag; } /* * set status percentage mode - in this mode, we'll output special * status update lines with a percent-done indication, for use by * container environments such as Workbench */ void set_status_pct_mode(int flag) { status_pct_mode_ = flag; } /* set quoted filenames mode for error messages */ void set_err_quoted_fnames(int flag) { quoted_fname_mode_ = flag; } /* * turn on/off linking - by default, we'll compile and link, but the * linking phase can be turned off so we just compile sources to * object files */ void set_do_link(int do_link) { do_link_ = do_link; } /* * turn on preinit mode - by default, we'll run preinit if and only * if we're not in debug mode */ void set_preinit(int preinit) { preinit_ = preinit; explicit_preinit_ = TRUE; } /* turn sourceTextGroup property generation on or off */ void set_source_text_group_mode(int f) { src_group_mode_ = f; } /* turn verbose error messages on or off */ void set_verbose(int verbose) { verbose_ = verbose; } /* turn error number display on or off */ void set_show_err_numbers(int show) { show_err_numbers_ = show; } /* turn all warning messages on or off */ void set_warnings(int show) { show_warnings_ = show; } /* treat warnings as errors mode */ void set_warnings_as_errors(int f) { warnings_as_errors_ = f; } /* turn pedantic messages on or off */ void set_pedantic(int show) { pedantic_ = show; } /* * Set the list of warning messages to suppress. The caller is * responsible for maintaining this memory, keeping it valid as long as * we have a reference to it and deleting the memory when it's no * longer needed. We merely keep a reference to the caller's memory. */ void set_suppress_list(const int *lst, size_t cnt) { /* remember the caller's suppress list */ suppress_list_ = lst; suppress_cnt_ = cnt; } /* set the constant pool data XOR mask */ void set_data_xor_mask(uchar mask) { data_xor_mask_ = mask; } /* add a #include path entry */ void add_include_path(const textchar_t *path); /* add a #include path entry, if it's not already in our list */ void maybe_add_include_path(const textchar_t *path); /* add a source file path */ void add_source_path(const textchar_t *dir); /* * add a system include/source file path - system paths are always * searched after all of the regular paths, so effectively the items * in these lists come after all items in the add_include_path and * add_source_path lists, respectively */ class CTcMakePath *add_sys_include_path(const textchar_t *dir); class CTcMakePath *add_sys_source_path(const textchar_t *dir); /* * Set the "create directories" flag. This is false by default. If * set, we'll create the directories named in the various output * file/path options if they don't already exist. Specifically, we'll * create the directories named in set_symbol_dir(), set_object_dir(), * and set_image_file() options. */ void set_create_dirs(int flag) { create_dirs_ = flag; } /* * Set the symbol file directory. Any symbol file that doesn't have * an absolute path will default to this directory. */ void set_symbol_dir(const textchar_t *dir) { symdir_.set(dir); } /* * Set the object file directory. Any object file that doesn't have * an absolute path will default to this directory. */ void set_object_dir(const textchar_t *dir) { objdir_.set(dir); } /* set the assembly listing file */ void set_assembly_listing(osfildef *fp) { assembly_listing_fp_ = fp; } /* * Set the image file name */ void set_image_file(const textchar_t *fname) { image_fname_.set(fname); } /* get the image filename */ const char *get_image_file() const { return image_fname_.get(); } /* * Add a module (a module defines a source file and its directly * derived files: a symbol file and an object file). * * Once a module has been added to our list, we own the module. * We'll delete the module object when we're deleted. */ void add_module(CTcMakeModule *mod); void add_module_first(CTcMakeModule *mod); /* get the head and tail of the module list */ CTcMakeModule *get_first_module() const { return mod_head_; } CTcMakeModule *get_last_module() const { return mod_tail_; } /* * Add a module by filename. src_name must be specified, but * sym_name and obj_name can be null, in which case we'll use the * standard algorithm to derive these names from the source file * name. */ CTcMakeModule *add_module(const char *src_name, const char *sym_name, const char *obj_name) { return add_module(src_name, sym_name, obj_name, FALSE); } /* add a module at the head of the module list */ CTcMakeModule *add_module_first(const char *src_name, const char *sym_name, const char *obj_name) { return add_module(src_name, sym_name, obj_name, TRUE); } /* add a module at either the start or end of the module list */ CTcMakeModule *add_module(const char *src_name, const char *sym_name, const char *obj_name, int first); /* * Add a preprocessor symbol definition. If the expansion text is * null, we'll set the expansion text to "1" by default. */ void def_pp_sym(const textchar_t *sym, const textchar_t *expan); /* * Un-define a preprocessor symbol. */ void undef_pp_sym(const textchar_t *sym); /* look up a preprocessor symbol definition */ const char *look_up_pp_sym(const textchar_t *sym, size_t sym_len); /* * Build the program. This can be invoked once the options are set * and all of the modules have been added. This routine checks * dependencies and performs the build: * * - for each source file, generates a symbol file if the symbol file * is not up to date * * - after creating all symbol files: for each source file, compiles * the source to an object file if the object file isn't up to date * * - after creating all object files: links the object files to create * an image file, if the image file isn't up to date * * If any errors occur, we'll stop after the step in which the errors * occur. For example, if errors occur compiling an object file, * we'll stop after finishing with the object file and will not * proceed to any additional object file compilations. * * The 'host_interface' object must be provided by the caller. This * tells the compiler how to report error messages and otherwise * interact with the host environment. * * Fills in '*error_count' and '*warning_count' with the number of * errors and warnings, respectively, that occur during the * compilation. * * If 'force_build' is set, we'll build all derived files (symbols, * objects, and image), regardless of whether it appears necessary * based on file times. * * 'argv0' is the main program's argv[0], if available; a null pointer * can be passed in if argv[0] is not available (for example, if we're * not running in a command-line environment and the program's * executable filename is not available) */ void build(class CTcHostIfc *host_interface, int *error_count, int *warning_count, int force_build, int force_link, class CRcResList *res_list, const textchar_t *argv0); /* * Write our build configuration information to a symbol file. The * symbol file builder calls this to give us a chance to insert our * build configuration information into the symbol file. We include * all of the information we will need when re-loading the symbol file * to determine if the symbol file is up-to-date, so that we can * determine if we must rebuild the symbol file from the source or can * use it without rebuilding. */ void write_build_config_to_sym_file(class CVmFile *fp); /* * Compare our build configuration to the information saved in an * symbol file. Returns true if the configuration in the symbol file * matches our current configuration, false if not. A false return * indicates that recompilation is necessary, because something in the * configuration has changed. */ int compare_build_config_from_sym_file(const char *sym_fname, class CVmFile *fp); /* * Set the string capture file. If this is set, we'll write each * string token in the compiled text to this file, one string per * line. */ void set_string_capture(osfildef *fp) { string_fp_ = fp; } /* * Derive the source/symbol/object filenames for a module. Fills in * the buffer with the derived name. If the filenames don't have * paths explicitly specified, we'll use our default path settings to * build the full filename. * * The buffers are assumed to be OSFNMAX characters long, which should * be large enough for any valid filename. */ void get_srcfile(textchar_t *dst, CTcMakeModule *mod); void get_symfile(textchar_t *dst, CTcMakeModule *mod); void get_objfile(textchar_t *dst, CTcMakeModule *mod); /* * Create a directory if it doesn't already exist. If 'is_file' is * true, the path includes both a directory path and a filename, in * which case we'll create the directory containing the file as * specified in the path. If 'is_file' is false, the path specifies a * directory name directly, with no filename attached. If an error * occurs, we'll generate a message and count it in the error count. */ void create_dir(class CTcHostIfc *hostifc, const char *path, int is_file, int *errcnt); private: /* scan all modules for name collisions with other modules */ void check_all_module_collisions(class CTcHostIfc *hostifc, class CResLoader *res_loader, int *err_cnt, int *warn_cnt); /* check the given module for name collisions with other modules */ void check_module_collision(CTcMakeModule *mod); /* * read and compare a configuration string; returns true if the * string matches what's stored in the file, false if not */ int read_and_compare_config_str(CVmFile *fp, const textchar_t *str); /* preprocess a source file */ void preprocess_source(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build a symbol file */ void build_symbol_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, const textchar_t *sym_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build an object file */ void build_object_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *src_fname, const textchar_t *obj_fname, CTcMakeModule *src_mod, int *error_count, int *warning_count); /* build the image file */ void build_image_file(class CTcHostIfc *hostifc, class CResLoader *res_loader, const textchar_t *image_fname, int *error_count, int *warning_count, class CVmRuntimeSymbols *runtime_symtab, class CVmRuntimeSymbols *runtime_macros, const char tool_data[4]); /* symbol enumeration callback: build runtime symbol table */ static void build_runtime_symtab_cb(void *ctx, class CTcSymbol *sym); /* symbol enumeration callback: build runtime macro table */ static void build_runtime_macro_cb(void *ctx, class CVmHashEntry *e); /* set compiler options in the G_tcmain object */ void set_compiler_options(); /* add a preprocessor symbol definition or undefinition */ void add_pp_def(const textchar_t *sym, const textchar_t *expan, int is_def); /* * Get the string to report for a filename in a progress report. If * we're in test reporting mode, we'll return the root name so that we * suppress all paths in progress reports; otherwise, we'll just return * the name as given. If we're quoting filenames in messages, we'll * quote the filename. */ const char *get_step_fname(char *buf, const char *fname); /* default source file character set */ char *source_charset_; /* flag: create output directories if they don't exist */ int create_dirs_; /* symbol file directory */ CTcMakeStr symdir_; /* object file diretory */ CTcMakeStr objdir_; /* assembly listing file */ osfildef *assembly_listing_fp_; /* image file name */ CTcMakeStr image_fname_; /* head and tail of module list */ CTcMakeModule *mod_head_; CTcMakeModule *mod_tail_; /* head and tail of source path list */ CTcMakePath *src_head_; CTcMakePath *src_tail_; /* * tail of regular source path list - this is where we insert regular * source path entries, which come before all system path entries */ CTcMakePath *nonsys_src_tail_; /* head and tail of #include path list */ CTcMakePath *inc_head_; CTcMakePath *inc_tail_; /* tail of regular (non-system) include path list */ CTcMakePath *nonsys_inc_tail_; /* head and tail of preprocessor symbol list */ CTcMakeDef *def_head_; CTcMakeDef *def_tail_; /* string capture file */ osfildef *string_fp_; /* true -> generate sourceTextGroup properties */ int src_group_mode_; /* true -> show verbose error messages */ int verbose_; /* true -> show error numbers with error messages */ int show_err_numbers_; /* true -> show warnings, false -> suppress all warnings */ int show_warnings_; /* true -> treat warnings as errors */ int warnings_as_errors_; /* true -> show "pedantic" warning messages */ int pedantic_; /* list of warning numbers to suppress */ const int *suppress_list_; size_t suppress_cnt_; /* true -> debug mode */ int debug_; /* true -> preprocess only */ int pp_only_; /* true -> #include list mode */ int list_includes_mode_; /* true -> "clean" mode */ int clean_mode_; /* true -> do linking after compiling */ int do_link_; /* true -> preinit mode */ int preinit_; /* * true -> obey 'preinit_' setting; otherwise, use default based on * 'debug_' setting */ int explicit_preinit_; /* * data pool XOR mask - we'll mask each byte of each constant data * pool with this byte when writing the image file, to obscure any * text strings in the constant data */ uchar data_xor_mask_; /* * true -> test reporting mode: suppress all paths from filenames * displayed in progress reports, in order to make the output * independent of local path name conventions */ int test_report_mode_; /* true -> percent-done reporting mode */ int status_pct_mode_; /* true -> use quoted filenames in error messages */ int quoted_fname_mode_; }; #endif /* TCMAKE_H */ frobtads-1.2.3/tads3/askf_tx3.cpp0000644000175000001440000000340711444673777016000 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name askf_tx.cpp - formatted text implementation of askfile Function Implements file dialog using text prompts. Notes Only one of askf_tx.c or askf_os.c should be included in a given executable. For a text-only version, include askf_tx. For a version where os_askfile() provides a file dialog, use askf_os instead. We provide a choice of tio_askfile() implementations in the portable code (rather than only through the OS code) so that we can call the formatted text output routines in this version. An OS-layer implementation could not call the formatted output routines (it would have to call os_printf directly), which would result in poor prompt formatting any time a prompt exceeded a single line of text. Modified 09/27/99 MJRoberts - Creation */ #include "os.h" #include "t3std.h" #include "vmglob.h" #include "vmconsol.h" /* * formatted text-only file prompt */ int CVmConsole::askfile(VMG_ const char *prompt, size_t prompt_len, char *reply, size_t replen, int /*dialog_type*/, os_filetype_t /*file_type*/, int bypass_script) { /* show the prompt */ format_text(vmg_ prompt, prompt_len); format_text(vmg_ " >"); /* ask for the filename */ if (read_line(vmg_ reply, replen, bypass_script)) return OS_AFE_FAILURE; /* * if they entered an empty line, return "cancel"; otherwise, return * success */ return (reply[0] == '\0' ? OS_AFE_CANCEL : OS_AFE_SUCCESS); } frobtads-1.2.3/tads3/portnote.htm0000644000175000001440000001312607510550324016112 0ustar realncusers Porting TADS 3

Porting TADS 3

TADS 3 is designed for easy portability to a wide variety of platforms. The source code is written in ANSI C++, and is very conservative in its use of C++ languages features; the code does not use templates, exceptions, or run-time type information, and limits itself to the standard C run-time library.

A Note on Newlines

Different operating systems have a tendency to disagree on the most trivial details. One of these pointless variations that causes a lot of trouble is "newline" conventions: each OS has its own way of signaling the end of a line of text in files such as C source and header files. In almost every case, the end of a line of text is indicated by some combination of the special characters with ASCII values 10 and 13, known respectively as "line feed" or "LF", and "carriage return" or "CR". MS-DOS uses the sequence CR-LF; Unix uses just LF; Macintosh uses just CR; and some system somewhere probably uses LF-CR.

In many cases, systems are tolerant of "foreign" conventions, but sometimes the wrong conventions will confuse compilers and other tools. If you run into any mysterious compiler errors, or your text editor or other tools have problems reading or displaying the TADS source files, you might want to try converting the files to your system's newline conventions. One of the easiest ways to do this is usually to use your UNZIP tool's "auto-convert text files" option when you unpack the TADS source archive - on command-line systems, this is usually the "unzip -a" option.

Portable and OS Layer Code

The TADS 3 source code uses the same technique to separate code into portable and OS layers that TADS 2 used. All of the code in the TADS 3 source distribution is portable, except for the files in system-specific subdirectories (such as "win32"). You should be able to use all of the portable code without making any changes to it. Any required changes in the portable code are porting exceptions; you should report them back to me so that I can find a way to merge the changes to correct the problem. There should be no platform-specific #if's or #ifdef's in the portable code - the source in the portable files should be 100% common to all platforms.

OS Layer Implementation

TADS 3 uses the identical OS interface layer that TADS 2 used. So, the first thing you need to do to port TADS 3 to a new platform is to obtain the TADS 2 OS interface implementation for that platform. TADS 2 has been widely ported, so there is a good chance that you can use an existing OS layer implementation for your platform. If you're porting to a platform that doesn't have a TADS 2 port, you must create your own TADS 2 OS interface implementation for the platform. Please obtain a copy of the TADS 2 source distribution for full details on how to go about this.

TADS 3 is designed to share the same object files from the TADS 2 OS layer, so you will not need to create a new copy of the OS files. You should instead simply set up your new TADS 3 makefile so that it includes headers from your TADS 2 OS include directory, and links in the "osxxx" object files from your TADS 2 object file directory.

Creating a Makefile

Refer to win32/makefile.vc5 in the source distribution for an example makefile. This makefile is set up for Microsoft Visual C++ on Windows (it'll work on VC++ versions 5 and 6, at least). This is not any kind of scary project file or other machine-generated monstronsity; it's simply a makefile, similar to Unix-style makefiles. The "vc5" extension, by the way, is just my own naming convention to indicate that it's the Visual C++ version of the makefile.

The Windows makefile defines the groups of object files that make up the various executables. In most cases, you will want to configure your own executables in roughly the same way; the only differences should be that you must substitute your OS layer implementation files for the Windows versions - these are the files whose names start with "os".

Makefiles are pretty nasty to read, so I'll provide some pointers on what to look for. The first piece of information you want from the makefile is simply the group of object files that make up each executable. This much is pretty easy to read - look for names of executables followed immediately by a colon - search for "t3make.exe:", for example, to find the compiler executable's build rule. Following this is a long list of object files. These are the object files that make up the executable. All of the object files prefixed with "$(OBJDIR)" are portable executables. The ones prefixed by "$(TADS2OBJDIR)" are the TADS 2 OS layer object files - these are not part of the TADS 3 source distribution because you can get them from the TADS 2 distribution. The object file list ends at the line that starts with "$(LD) $(LDFLAGS)" - this line starts the command specification for the "link" command that builds the executable.

Many of the executables listed in the makefile are for testing purposes only. You don't need to build these (but you certainly can if you want to run some more in-depth testing on your port).

The source file dependencies are mostly straightforward - these follow the executable rules. However, a few source files have special compilation rules - look at the end of the source-to-object dependency rules for the rules that have explicit "$(CC)" command lines listed under the dependencies. For example, the object module "vmrun_d.obj" is compiled from "vmrun.cpp" with a special compile command (the command sets the #define symbol VM_DEBUGGER). frobtads-1.2.3/tads3/vmlog.cpp0000644000175000001440000000417612145504453015365 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2011 by Michael J. Roberts. All Rights Reserved. */ /* Name vmlog.cpp - system log file Function Notes Modified 12/01/11 MJRoberts - Creation */ #include #include "t3std.h" #include "vmglob.h" #include "vmlog.h" #include "vmfile.h" #include "vmdatasrc.h" #include "charmap.h" /* ------------------------------------------------------------------------ */ /* * System log file */ /* * Log a message with sprintf-style formatting */ void vm_log_fmt(VMG_ const char *fmt, ...) { /* format the message */ va_list args; va_start(args, fmt); char *str = t3vsprintf_alloc(fmt, args); va_end(args); /* log the message */ vm_log(vmg_ str, strlen(str)); /* done with the string */ t3free(str); } /* * Log a string with a given length */ void vm_log(VMG_ const char *str, size_t len) { /* open the system log file */ osfildef *fp = osfoprwt(G_syslogfile, OSFTTEXT); if (fp != 0) { /* wrap it in a data source */ CVmFileSource ds(fp); /* get a printable timestamp */ os_time_t timer = os_time(0); struct tm *tblk = os_localtime(&timer); char *tmsg = asctime(tblk); /* remove the trailing '\n' from the asctime message */ size_t tmsgl = strlen(tmsg); if (tmsgl > 0 && tmsg[tmsgl-1] == '\n') tmsg[--tmsgl] = '\0'; /* build the full message: [] */ char *msg = t3sprintf_alloc("[%s] %.*s\n", tmsg, (int)len, str); size_t msglen = strlen(msg); /* seek to the end of the file */ ds.seek(0, OSFSK_END); /* if we can convert to a local character set, do so */ if (G_cmap_to_log != 0) { /* write the message in the local character set */ G_cmap_to_file->write_file(&ds, msg, msglen); } else { /* write the message with no character set conversion */ (void)osfwb(fp, msg, msglen); } /* done with the formatted text string */ t3free(msg); } } frobtads-1.2.3/tads3/vmbifregn.cpp0000644000175000001440000000550411704144663016217 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmbifreg.cpp,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmbifreg.cpp - built-in function set registry Function Defines the built-in functions that are linked in to this implementation of the VM. This file is dependent on the host application environment configuration. This particular file includes a table for the base set of T3 VM built-in functions. Some host application environments may provide additional function sets; if you're building a host system with its own extra function sets, do the following: 1. Make a copy of this file (DO NOT MODIFY THE ORIGINAL). 2. Remove this file (and/or derived files, such as object files) from your makefile, and add your modified version instead. 3. In the table below, add an entry for each of your extra function sets. (Of course, for each of your added function sets, you must implement the function set and link the implementation into your host application executable.) Notes Modified 12/05/98 MJRoberts - Creation */ #include "vmbifreg.h" /* ------------------------------------------------------------------------ */ /* * Include the function set vector definitions. Define * VMBIF_DEFINE_VECTOR so that the headers all generate vector * definitions. */ #define VMBIF_DEFINE_VECTOR #include "vmbiftad.h" #include "vmbiftio.h" #include "vmbift3.h" #include "vmbifnet.h" // !!! INCLUDE HOST-SPECIFIC FUNCTION SET HEADERS HERE ("vmbifxxx.h") /* done with the vector definition */ #undef VMBIF_DEFINE_VECTOR #define MAKE_ENTRY(entry_name, cls) \ { entry_name, countof(cls::bif_table), cls::bif_table, \ &cls::attach, &cls::detach } /* ------------------------------------------------------------------------ */ /* * The function set registration table. Each entry in the table * provides the definition of one function set, keyed by the function * set's universally unique identifier. */ vm_bif_entry_t G_bif_reg_table[] = { /* T3 VM system function set, v1 */ MAKE_ENTRY("t3vm/010006", CVmBifT3), /* T3 VM Testing interface, v1 */ MAKE_ENTRY("t3vmTEST/010000", CVmBifT3Test), /* TADS generic data manipulation functions */ MAKE_ENTRY("tads-gen/030008", CVmBifTADS), /* TADS input/output functions */ MAKE_ENTRY("tads-io/030007", CVmBifTIO), /* TADS input/output functions */ MAKE_ENTRY("tads-net/030001", CVmBifNet), // !!! ADD ANY HOST-SPECIFIC FUNCTION SETS HERE /* end of table marker */ { 0, 0, 0 } }; /* we don't need the MAKE_ENTRY macro any longer */ #undef MAKE_ENTRY frobtads-1.2.3/tads3/vmtype.cpp0000644000175000001440000004332111773415247015570 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMTYPE.CPP,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmtype.cpp - VM types Function Notes Modified 11/18/98 MJRoberts - Creation */ #include "t3std.h" #include "vmtype.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmpool.h" /* ------------------------------------------------------------------------ */ /* * Compare this value to another value to determine if the two values * are equal. */ int vm_val_t::equals(VMG_ const vm_val_t *v, int depth) const { /* * if the second value is an object and the first isn't, use the object * comparison of the second */ if (v->typ == VM_OBJ && typ != VM_OBJ) return vm_objp(vmg_ v->val.obj) ->equals(vmg_ v->val.obj, this, depth); /* figure out what to do based on my type */ switch(typ) { case VM_NIL: case VM_TRUE: /* we match only if the other value is the same boolean value */ return (v->typ == typ); case VM_STACK: case VM_CODEPTR: /* * we match only if the other value has the same type and its * pointer value matches */ return (v->typ == typ && v->val.ptr == this->val.ptr); case VM_OBJ: /* use the object's polymorphic equality test routine */ return vm_objp(vmg_ this->val.obj) ->equals(vmg_ this->val.obj, v, depth); case VM_PROP: /* we match if the other value is the same property ID */ return (v->typ == VM_PROP && v->val.prop == this->val.prop); case VM_INT: /* we match if the other value is the same integer */ return (v->typ == VM_INT && v->val.intval == this->val.intval); case VM_BIFPTR: case VM_BIFPTRX: /* we match if the other value refers to the same function */ return (v->typ == VM_BIFPTR && v->val.bifptr.set_idx == this->val.bifptr.set_idx && v->val.bifptr.func_idx == this->val.bifptr.func_idx); case VM_ENUM: return (v->typ == VM_ENUM && v->val.enumval == this->val.enumval); case VM_SSTRING: /* use the standard string comparison routine */ return CVmObjString:: const_equals(vmg_ G_const_pool->get_ptr(this->val.ofs), v); case VM_LIST: /* use the standard list comparison routine */ return CVmObjList:: const_equals(vmg_ this, G_const_pool->get_ptr(this->val.ofs), v, depth); case VM_OBJX: /* match if it's the same object */ return (v->typ == typ && v->val.obj == this->val.obj); case VM_CODEOFS: case VM_FUNCPTR: /* we match if the other value is the same code offset */ return (v->typ == typ && v->val.ofs == this->val.ofs); case VM_EMPTY: /* empty never matches anything */ return FALSE; case VM_DSTRING: /* dstrings have no value, and are thus never equal to anything */ return FALSE; default: /* other types are not recognized */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Calculate a hash value */ uint vm_val_t::calc_hash(VMG_ int depth) const { /* see what we have */ switch(typ) { case VM_NIL: /* this is rather arbitrary */ return 0; case VM_TRUE: /* this is arbitrary, but at least make it different from nil */ return 1; case VM_EMPTY: /* also arbitrary */ return 2; case VM_CODEOFS: case VM_FUNCPTR: /* use a 16-bit hash of the code address */ return (uint)((val.ofs & 0xffff) ^ ((val.ofs & 0xffff0000) >> 16)); case VM_OBJX: /* use a 16-bit hash of the object ID */ return (uint)((val.obj & 0xffff) ^ ((val.obj & 0xffff0000) >> 16)); case VM_PROP: /* use the property ID as the hash */ return (uint)val.prop; case VM_INT: case VM_BIFPTR: case VM_BIFPTRX: /* * the set index and function index both tend to be small integers; * multiply them and keep the low-order 16 bits */ return (uint)(val.bifptr.set_idx * val.bifptr.func_idx) & 0xffff; case VM_ENUM: /* use a 16-bit hash of the enum value */ return (uint)((val.enumval & 0xffff) ^ ((val.enumval & 0xffff0000) >> 16)); case VM_OBJ: /* ask the object to calculate its hash value */ return vm_objp(vmg_ val.obj)->calc_hash(vmg_ val.obj, depth); break; case VM_SSTRING: /* get the hash of the constant string */ return CVmObjString:: const_calc_hash(G_const_pool->get_ptr(val.ofs)); break; case VM_LIST: /* get the hash of the constant list */ return CVmObjList:: const_calc_hash(vmg_ this, G_const_pool->get_ptr(val.ofs), depth); default: /* return an arbitrary value for any other type */ return 3; } } /* ------------------------------------------------------------------------ */ /* * Compare this value to the given value. Returns a positive value if * this value is greater than 'val', a negative value if this value is * less than 'val', and 0 if the two values are equal. Throws an error * if a magnitude comparison is not meaningful for the involved types. */ int vm_val_t::gen_compare_to(VMG_ const vm_val_t *v) const { /* the comparison depends on my type */ switch(typ) { case VM_OBJ: /* let the object perform the comparison */ return vm_objp(vmg_ this->val.obj) ->compare_to(vmg_ this->val.obj, v); case VM_SSTRING: /* compare the string */ return CVmObjString:: const_compare(vmg_ G_const_pool->get_ptr(this->val.ofs), v); case VM_INT: if (v->typ == VM_INT) { /* comparing two integers */ int32_t a = this->val.intval, b = v->val.intval; return a - b; } else if (v->typ == VM_OBJ) { /* the other value is an object, so let it do the comparison */ return -(vm_objp(vmg_ v->val.obj)-> compare_to(vmg_ v->val.obj, this)); } else { /* other types can't be compared to integer */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } default: /* if the other value is an object, let it do the comparison */ if (v->typ == VM_OBJ) { return -(vm_objp(vmg_ v->val.obj)-> compare_to(vmg_ v->val.obj, this)); } else { /* other types cannot be compared for magnitude */ err_throw(VMERR_INVALID_COMPARISON); AFTER_ERR_THROW(return 0;) } } } /* ------------------------------------------------------------------------ */ /* * Is this a numeric value? Returns true for integers and BigNumber * values. */ int vm_val_t::nonint_is_numeric(VMG0_) const { /* some objects represent numeric types (e.g., BigNumber) */ if (typ == VM_OBJ) return vm_objp(vmg_ val.obj)->is_numeric(); /* we shouldn't be called with an int, but just in case */ if (typ == VM_INT) return TRUE; /* other types are non-numeric */ return FALSE; } /* * Convert a numeric type to integer */ int32_t vm_val_t::nonint_num_to_int(VMG0_) const { /* if it's an object, ask the object for its integer value */ long l; if (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_as_int(&l)) return l; /* we shouldn't be called with an int type, but just in case */ if (typ == VM_INT) return val.intval; /* other types are not convertible */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0); } /* * Convert a numeric type to doule */ double vm_val_t::nonint_num_to_double(VMG0_) const { /* if it's an object, ask the object for its integer value */ double d; if (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_as_double(vmg_ &d)) return d; /* we shouldn't be called with an int type, but just in case */ if (typ == VM_INT) return (double)val.intval; /* other types are not convertible */ err_throw(VMERR_NUM_VAL_REQD); AFTER_ERR_THROW(return 0.); } /* * Cast to integer */ int32_t vm_val_t::nonint_cast_to_int(VMG0_) const { /* check the type */ switch (typ) { case VM_TRUE: return 1; case VM_NIL: return 0; case VM_INT: /* we shouldn't ever be called with an int value, but just in case */ return val.intval; case VM_SSTRING: { /* get the string constant */ const char *p = G_const_pool->get_ptr(val.ofs); /* parse it as an integer */ vm_val_t i; CVmObjString::parse_num_val( vmg_ &i, p + VMB_LEN, vmb_get_len(p), 10, TRUE); /* return the integer */ return i.val.intval; } case VM_OBJ: /* ask the object to do the conversion */ return vm_objp(vmg_ val.obj)->cast_to_int(vmg0_); default: /* there's no integer conversion */ err_throw(VMERR_NO_INT_CONV); AFTER_ERR_THROW(return 0;) } } /* * Cast to numeric (integer or BigNumber) */ void vm_val_t::cast_to_num(VMG_ vm_val_t *retval) const { /* check the type */ switch (typ) { case VM_TRUE: retval->set_int(1); break; case VM_NIL: retval->set_int(0); break; case VM_INT: /* it's already numeric */ retval->set_int(val.intval); break; case VM_SSTRING: { /* get the string constant */ const char *p = G_const_pool->get_ptr(val.ofs); /* parse it as an integer */ CVmObjString::parse_num_val( vmg_ retval, p + VMB_LEN, vmb_get_len(p), 10, FALSE); } break; case VM_OBJ: /* ask the object to do the conversion */ vm_objp(vmg_ val.obj)->cast_to_num(vmg_ retval, val.obj); break; default: /* there's no numeric conversion */ err_throw(VMERR_NO_NUM_CONV); } } /* * Promote an integer to match my type */ void vm_val_t::promote_int(VMG_ vm_val_t *val) const { if (typ == VM_OBJ) { /* ask the object to perform the promotion */ vm_objp(vmg_ this->val.obj)->promote_int(vmg_ val); } else { /* * we can't promote integers to other types; this is a "numeric * value required" error, since we only try to promote integers * when performing arithmetic involving an int and another type */ err_throw(VMERR_NUM_VAL_REQD); } } /* ------------------------------------------------------------------------ */ /* * Cast to string */ const char *vm_val_t::cast_to_string(VMG_ vm_val_t *new_str) const { /* presume the value is just our own value */ *new_str = *this; switch (typ) { case VM_SSTRING: /* return the string as is */ return get_as_string(vmg0_); case VM_OBJ: /* cast the object to string */ return vm_objp(vmg_ val.obj)->cast_to_string(vmg_ val.obj, new_str); case VM_INT: /* create a printable decimal representation of the string */ { /* format the number into a temporary buffer */ char buf[20]; sprintf(buf, "%ld", (long)val.intval); /* create a new string object for the number */ new_str->set_obj( CVmObjString::create(vmg_ FALSE, buf, strlen(buf))); /* return the string buffer */ return new_str->get_as_string(vmg0_); } case VM_NIL: /* return an empty string */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, 0)); return new_str->get_as_string(vmg0_); case VM_TRUE: /* return 'true' */ new_str->set_obj(CVmObjString::create(vmg_ FALSE, "true", 4)); return new_str->get_as_string(vmg0_); case VM_LIST: /* join the list into a string, separating items with commas */ CVmObjList::join(vmg_ new_str, this, ",", 1); return new_str->get_as_string(vmg0_); default: err_throw(VMERR_NO_STR_CONV); AFTER_ERR_THROW(return 0;) } } /* * Get the underlying string constant value. */ const char *vm_val_t::get_as_string(VMG0_) const { /* check my type */ if (typ == VM_SSTRING) { /* it's a constant string - return its text from the constant pool */ return G_const_pool->get_ptr(val.ofs); } else if (typ == VM_OBJ) { /* it's an object - ask for its underlying string, if any */ return vm_objp(vmg_ val.obj)->get_as_string(vmg0_); } else { /* other types do not have underlying strings */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get as a code pointer */ const uchar *vm_val_t::get_as_codeptr(VMG0_) const { /* check my type */ switch (typ) { case VM_CODEOFS: case VM_FUNCPTR: /* * these types contain code pool offsets - translate the offset to * a physical pointer */ return (const uchar *)G_code_pool->get_ptr(val.ofs); case VM_CODEPTR: /* it's already a physical code pointer */ return (const uchar *)val.ptr; default: /* other types do not contain code pointers */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Get the underlying list constant value. */ const char *vm_val_t::get_as_list(VMG0_) const { /* check my type */ if (typ == VM_LIST) { /* it's a constant list - return its data from the constant pool */ return G_const_pool->get_ptr(val.ofs); } else if (typ == VM_OBJ) { /* it's an object - ask for its underlying list, if any */ return vm_objp(vmg_ val.obj)->get_as_list(); } else { /* other types do not have underlying lists */ return 0; } } /* ------------------------------------------------------------------------ */ /* * Is this indexable as a list? */ int vm_val_t::is_listlike(VMG0_) const { return (get_as_list(vmg0_) != 0 || (typ == VM_OBJ && (vm_objp(vmg_ val.obj)->get_as_list() || vm_objp(vmg_ val.obj)->is_listlike(vmg_ val.obj)))); } /* * Get the number of elements in a list-like object */ int vm_val_t::ll_length(VMG0_) const { /* try it as a regular list first */ const char *p; if ((p = get_as_list(vmg0_)) != 0) return vmb_get_len(p); /* try it as an object */ if (typ == VM_OBJ) return vm_objp(vmg_ val.obj)->ll_length(vmg_ val.obj); /* we don't have a length */ return -1; } /* * Get an indexed value */ void vm_val_t::ll_index(VMG_ vm_val_t *ret, const vm_val_t *idx) const { /* try it as a regular list first, then as an object */ const char *p; if ((p = get_as_list(vmg0_)) != 0) { /* get the index as an integer value */ int i = idx->cast_to_int(vmg0_); /* check the range */ if (i < 1 || i > (int)vmb_get_len(p)) err_throw(VMERR_INDEX_OUT_OF_RANGE); /* it's in range - retrieve the element */ vmb_get_dh(p + VMB_LEN + (size_t)((i - 1) * VMB_DATAHOLDER), ret); } else if (typ == VM_OBJ) { /* use the object method, allowing operator overloading */ vm_objp(vmg_ val.obj)->index_val_ov(vmg_ ret, val.obj, idx); } else if ((p = get_as_string(vmg0_)) != 0) { /* string - parse the string as an integer in decimal format */ CVmObjString::parse_num_val( vmg_ ret, p + VMB_LEN, vmb_get_len(p), 10, TRUE); } else { /* no value */ ret->set_nil(); } } /* ------------------------------------------------------------------------ */ /* * Is this an object of the given class? */ int vm_val_t::is_instance_of(VMG_ vm_obj_id_t cls) const { return (typ == VM_OBJ && cls != VM_INVALID_OBJ && vm_objp(vmg_ val.obj)->is_instance_of(vmg_ cls)); } /* ------------------------------------------------------------------------ */ /* * Set to an integer giving the datatype code for the given value */ void vm_val_t::set_datatype(VMG_ const vm_val_t *v) { /* check for an object */ if (v->typ == VM_OBJ) { /* check for List and String values */ if (v->get_as_string(vmg0_) != 0) { /* treat this the same as a string constant */ set_int((int)VM_SSTRING); } else if (v->get_as_list(vmg0_) != 0) { /* treat this the same as a list constant */ set_int((int)VM_LIST); } else { /* any other type of object is simply an object */ set_int((int)VM_OBJ); } } else { /* for any other type, return our internal type code */ set_int((int)v->typ); } } /* ------------------------------------------------------------------------ */ /* * Is this a function pointer of some kind? */ int vm_val_t::is_func_ptr(VMG0_) const { return (typ == VM_FUNCPTR || typ == VM_BIFPTR || (typ == VM_OBJ && vm_objp(vmg_ val.obj)->get_invoker(vmg_ 0))); } frobtads-1.2.3/tads3/vmhash.h0000644000175000001440000002015711652514703015172 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/vmhash.h,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $ */ /* * Copyright (c) 1997, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhash.h - hash table implementation Function Notes Modified 10/25/97 MJRoberts - Creation */ #ifndef VMHASH_H #define VMHASH_H #ifndef STD_H #include "t3std.h" #endif /* ------------------------------------------------------------------------ */ /* * Hash function interface class. Hash table clients must implement an * appropriate hash function to use with the hash table; this abstract * class provides the necessary interface. */ class CVmHashFunc { public: virtual ~CVmHashFunc() { } virtual unsigned int compute_hash(const char *str, size_t len) const = 0; }; /* ------------------------------------------------------------------------ */ /* * Hash table symbol entry. This is an abstract class; subclasses must * provide a symbol-matching method. */ class CVmHashEntry { public: /* * Construct the hash entry. 'copy' indicates whether we should * make a private copy of the value; if not, the caller must keep * the original string around as long as this hash entry is around. * If 'copy' is true, we'll make a private copy of the string * immediately, so the caller need not keep it around after * constructing the entry. */ CVmHashEntry(const char *str, size_t len, int copy); virtual ~CVmHashEntry(); /* determine if this entry matches a given string */ virtual int matches(const char *str, size_t len) const = 0; /* list link */ CVmHashEntry *nxt_; /* get the string pointer and length */ const char *getstr() const { return str_; } size_t getlen() const { return len_; } protected: const char *str_; size_t len_; int copy_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Hash table */ class CVmHashTable { public: /* * Construct a hash table. If own_hash_func is true, the hash table * object takes ownership of the hash function object, so the hash * table object will delete the hash function object when the table * is deleted. */ CVmHashTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func) { /* initialize, allocating our own hash array */ init(hash_table_size, hash_function, own_hash_func, 0); } /* * Construct a hash table, using memory allocated and owned by the * caller for the hash array. */ CVmHashTable(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array) { /* initialize, using the provided hash array */ init(hash_table_size, hash_function, own_hash_func, hash_array); } /* delete the hash table */ ~CVmHashTable(); /* * Add a symbol. If 'copy' is true, it means that we need to make * a private copy of the string; otherwise, the caller must ensure * that the string remains valid as long as the hash table entry * remains valid, since we'll just store a pointer to the original * string. IMPORTANT: the hash table takes over ownership of the * hash table entry; the hash table will delete this object when the * hash table is deleted, so the client must not delete the entry * once it's been added to the table. */ void add(CVmHashEntry *entry); /* * Remove an object from the cache. This routine does not delete * the object. */ void remove(CVmHashEntry *entry); /* * Delete all entries in the table */ void delete_all_entries(); /* * Find an entry in the table matching the given string */ CVmHashEntry *find(const char *str, size_t len) const; /* enumerate all entries that match the given string's hash value */ void enum_hash_matches(const char *str, size_t len, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx); /* enumerate all entries with the given hash value */ void enum_hash_matches(uint hash, void (*cb)(void *cbctx, CVmHashEntry *entry), void *cbctx); /* * Find an entry that matches the longest leading substring of the * given string. (For this routine, we find a match where the * dictionary word is SHORTER than the given word.) */ CVmHashEntry *find_leading_substr(const char *str, size_t len); /* * Enumerate all entries, invoking a callback for each entry in the * table */ void enum_entries(void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx); /* * Enumerate all entries safely. This does the same thing as * enum_entries(), but this version is safe to use regardless of any * changes to the table that the callback makes. */ void safe_enum_entries(void (*func)(void *ctx, class CVmHashEntry *entry), void *ctx); /* * Move all of the entries in this hash table to a new hash table. * After this is finished, this hash table will be empty, and the new * hash table will be populated with all of the entries that were * formerly in this table. We don't reallocate any of the entries - * they simply are unlinked from this table and moved into the new * table. This can be used to rebuild a hash table with a new bucket * count or hash function. */ void move_entries_to(CVmHashTable *new_tab); /* dump information on the hash table to stderr for debugging */ void debug_dump() const; /* compute the hash value for an entry/a string */ unsigned int compute_hash(CVmHashEntry *entry) const; unsigned int compute_hash(const char *str, size_t len) const; private: /* adjust a hash to the table size */ unsigned int adjust_hash(unsigned int hash) const { return hash & (table_size_ - 1); } /* initialize */ void init(int hash_table_size, CVmHashFunc *hash_function, int own_hash_func, CVmHashEntry **hash_array); /* internal service routine for checking hash table sizes for validity */ int is_power_of_two(int n); /* the table of hash entries */ CVmHashEntry **table_; size_t table_size_; /* hash function */ CVmHashFunc *hash_function_; /* flag: I own the hash function and must delete it when done */ unsigned int own_hash_func_ : 1; /* flag: I own the hash table array and must delete it when done */ unsigned int own_hash_table_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Simple case-insensitive hash function */ class CVmHashFuncCI: public CVmHashFunc { public: unsigned int compute_hash(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Simple case-sensitive hash function implementation */ class CVmHashFuncCS: public CVmHashFunc { public: unsigned int compute_hash(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-insensitive * symbol match implementation */ class CVmHashEntryCI: public CVmHashEntry { public: CVmHashEntryCI(const char *str, size_t len, int copy) : CVmHashEntry(str, len, copy) { } virtual int matches(const char *str, size_t len) const; }; /* ------------------------------------------------------------------------ */ /* * Concrete subclass of CVmHashEntry providing a case-sensitive symbol * match implementation */ class CVmHashEntryCS: public CVmHashEntry { public: CVmHashEntryCS(const char *str, size_t len, int copy) : CVmHashEntry(str, len, copy) { } virtual int matches(const char *str, size_t len) const; }; #endif /* VMHASH_H */ frobtads-1.2.3/tads3/tct3int.h0000644000175000001440000002420111465242051015262 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3INT.H,v 1.3 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3int.h - T3-specific intermediate parse node classes Function Notes Modified 05/12/99 MJRoberts - Creation */ #ifndef TCT3INT_H #define TCT3INT_H /* include our T3-specific base classes */ #include "tct3base.h" /* include the target-independent intermediate classes */ #include "tcpnint.h" /* ------------------------------------------------------------------------ */ /* * Unary Operator Expression node */ class CTPNUnary: public CTPNUnaryBase { public: CTPNUnary(CTcPrsNode *subexpr) : CTPNUnaryBase(subexpr) { } /* * Generate code for a unary operator with no side effects. We'll * generate code for the subexpression, then generate the given * opcode. * * If 'discard' is true, we won't generate the opcode. We know the * opcode has no side effects, so if the result of the calculation * isn't going to be used, there's no point in calculating it in the * first place. */ void gen_unary(uchar opc, int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Binary Operator Expression node */ class CTPNBin: public CTPNBinBase { public: CTPNBin(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNBinBase(lhs, rhs) { } /* * Generate code for a binary operator with no side effects. We'll * generate code for the left operand, then for the right operand, * then generate the opcode. * * If 'discard' is true, we won't generate the opcode. We know that * the opcode has no side effects, so if the result of the * calculation isn't needed, we need not apply the opcode; we simply * want to evaluate the subexpressions for any side effects they * might have. */ void gen_binary(uchar opc, int discard, int for_condition); /* * Generate code for a binary operator as part of a compound * assignment. For example, this is used to generate the '+' portion * of '+='. The caller must already have generated code to push the * left operand; we generate the right operand plus the opcode to apply * the operator. */ void gen_binary_ca(uchar opc); }; /* ------------------------------------------------------------------------ */ /* * generic statement */ class CTPNStm: public CTPNStmBase { public: /* initialize at the tokenizer's current source file position */ CTPNStm() : CTPNStmBase() { } /* initialize at the given source position */ CTPNStm(class CTcTokFileDesc *file, long linenum) : CTPNStmBase(file, linenum) { } /* * Generate code for a labeled 'continue'. These are called when * this statement is labeled, and the same label is used in a * 'continue' statement. * * Returns true if successful, false if not. This should return * false if the statement is not suitable for a 'continue'. By * default, we simply return false, since most statements cannot be * used as the target of a 'continue'. */ virtual int gen_code_labeled_continue() { return FALSE; } }; /* ------------------------------------------------------------------------ */ /* * Enclosing Statement class: this is the base class for statements that * have special needs for exiting due to break, continue, goto, and * return. Refer to tcpnint.h for details. */ class CTPNStmEnclosing: public CTPNStm { public: CTPNStmEnclosing(CTPNStmEnclosing *enclosing) { /* remember the statement that encloses me */ enclosing_ = enclosing; } /* get the enclosing statement */ CTPNStmEnclosing *get_enclosing() const { return enclosing_; } /* * Generate code for a 'break' to a given label, or an unlabeled * 'break' if lbl is null. Returns true if the break was fully * processed, false if not. If any work is necessary to unwind the * exception stack to break out of this block, that should be done * here. If this node doesn't actually accomplish the break, it * should recursively invoke the next enclosing statement's routine * to do the work. * * By default, we'll simply invoke the enclosing handler, if there * is one, or return failure (i.e., false) if not. */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { if (enclosing_) return enclosing_->gen_code_break(lbl, lbl_len); else return 0; } /* * Generate code for a 'continue' to a given label, or an unlabeled * 'continue' if lbl is null. Same rules as gen_code_break(). * * By default, we'll simply invoke the enclosing handler, if there * is one, or return failure (i.e., false) if not. */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { if (enclosing_) return enclosing_->gen_code_continue(lbl, lbl_len); else return 0; } /* * Generate the code necessary to unwind the stack for returning * through this enclosing statement. This should generate any code * necessary (such as a call to a 'finally' block, for a 'try' * statement), then call the next enclosing statement to do the same * thing. By default, this does nothing except invoke the enclosing * statement, since most enclosing statements don't need to generate * any code to handle a 'return' through them. */ virtual void gen_code_unwind_for_return() { /* if there's an enclosing statement, ask it to unwind itself */ if (enclosing_ != 0) enclosing_->gen_code_unwind_for_return(); } /* * Determine if we'll generate any code to unwind the stack for * returning through this enclosing statement. This should return * true if any code will be generated by a call to * gen_code_unwind_for_return(). By default, we'll return whatever * our enclosing statement does. */ virtual int will_gen_code_unwind_for_return() const { /* * return what the enclosing statement does, or false if we're * the outermost statement */ return (enclosing_ != 0 ? enclosing_->will_gen_code_unwind_for_return() : FALSE); } /* * Generate the code necessary to unwind the stack for executing a * 'goto' to the given label statement. We first determine if the * target label is contained within this statement; if it is, * there's nothing we need to do, because we're transferring control * within the same enclosing statement. * * If control is being transferred outside of this statement (i.e., * the label is not contained within this statement), we must * generate the necessary code to leave the block; for example, a * 'try' block must generate a call to its 'finally' block. We must * then invoke this same function on our enclosing statement to let * it do the same work. */ void gen_code_unwind_for_goto(class CTPNStmGoto *goto_stm, class CTPNStmLabel *target); /* * Generate code for transferring control out of this statement. We * invoke this from gen_code_unwind_for_goto() when we determine * that the 'goto' target is not enclosed within this statement. By * default we do nothing; statements such as 'try' that must do work * on transferring control must override this to generate the * appropriate code. */ virtual void gen_code_for_transfer_out() { } /* * Check to see if we are allowed to transfer in to this block via * 'goto' statements. If so, simply return TRUE. If we don't allow * this type of transfer, we should log an appropriate error, then * return FALSE. Note that the function should both log an error * and return FALSE if the transfer isn't allowed; the return value * allows the caller to ensure that only one error is displayed for * this type of problem even if the target label is nested within * several levels of blocks that don't allow transfers in. * * By default, we'll simply return TRUE, since most block types * allow this type of transfer. */ virtual int check_enter_by_goto(class CTPNStmGoto * /*goto_stm*/, class CTPNStmLabel * /*target*/) { return TRUE; } protected: /* * generate code for a break - this can be used as a service routine * by loop/switch statements */ int gen_code_break_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len); /* * generate code for a continue - this can be used as a service * routine by loop/switch statements */ int gen_code_continue_loop(CTcCodeLabel *code_label, const textchar_t *lbl, size_t lbl_len); /* the block enclosing this block */ CTPNStmEnclosing *enclosing_; }; /* ------------------------------------------------------------------------ */ /* * Base class for 'for' statement 'var in collection' and 'var in from..to' * expressions. We keep a separate list of these nodes for a 'for' * statement, for code generation purposes. These expression nodes reside * in the initializer clause of the 'for', but they implicitly generate * code in the condition and reinit phases as well. */ class CTPNForIn: public CTPNForInBase { public: CTPNForIn() { } /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl) = 0; /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit() = 0; protected: }; #endif /* TCT3INT_H */ frobtads-1.2.3/tads3/core.h0000644000175000001440000000114707627507676014654 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name core.h - sample T3 source code defining an intrinsic function set for the 'vmcore.cpp' sample Function Notes Modified 04/06/02 MJRoberts - Creation */ #ifndef CORE_H #define CORE_H intrinsic 'core-sample/010000' { /* display the given string */ displayText(str); /* read a line of text from the keyboard, and return it as a string */ readText(); } #endif /* CORE_H */ frobtads-1.2.3/tads3/tct3drv.h0000644000175000001440000020541012012524030015253 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3DRV.H,v 1.4 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3drv.h - derived final T3-specific parse node classes Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCT3DRV_H #define TCT3DRV_H #include /* include our T3-specific intermediate classes */ #include "tct3int.h" /* include the target-independent derived classes */ #include "tcpndrv.h" /* ------------------------------------------------------------------------ */ /* * "self" */ class CTPNSelf: public CTPNSelfBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * "replaced" */ class CTPNReplaced: public CTPNReplacedBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a function call expression */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* ------------------------------------------------------------------------ */ /* * "targetprop" */ class CTPNTargetprop: public CTPNTargetpropBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "targetobj" */ class CTPNTargetobj: public CTPNTargetobjBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "definingobj" */ class CTPNDefiningobj: public CTPNDefiningobjBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "invokee" */ class CTPNInvokee: public CTPNInvokeeBase { public: /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * "inherited" */ class CTPNInh: public CTPNInhBase { public: /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); protected: /* generate a multi-method inherited() call */ void gen_code_mminh(CTcSymFunc *func, int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * "inherited" with explicit superclass */ class CTPNInhClass: public CTPNInhClassBase { public: CTPNInhClass(const char *sym, size_t len) : CTPNInhClassBase(sym, len) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * "delegated" */ class CTPNDelegated: public CTPNDelegatedBase { public: CTPNDelegated(CTcPrsNode *delegatee) : CTPNDelegatedBase(delegatee) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* ------------------------------------------------------------------------ */ /* * "argcount" node */ class CTPNArgc: public CTPNArgcBase { public: void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * constant node */ class CTPNConst: public CTPNConstBase { public: CTPNConst(const CTcConstVal *val) : CTPNConstBase(val) { } /* generate code for the constant */ void gen_code(int discard, int for_condition); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* generate code for assigning to this expression */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate a function call expression */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); /* generate code to push an integer constant */ static void s_gen_code_int(long intval); /* generate code to push a string constant */ static void s_gen_code_str(const CTcConstVal *val); static void s_gen_code_str(const char *str, size_t len); /* generate code to push a symbol's name as a string constant */ static void s_gen_code_str(const class CTcSymbol *sym); /* * generate code to push a string, using the correct code for the * current compilation mode (static vs dynamic) */ static void s_gen_code_str_by_mode(const class CTcSymbol *sym); }; /* * Debugger constant node */ class CTPNDebugConst: public CTPNConst { public: CTPNDebugConst(CTcConstVal *val) : CTPNConst(val) { } /* debugger constants aren't actual constants - they require code gen */ CTcConstVal *get_const_val() { return 0; } /* generate code for the constant */ void gen_code(int discard, int for_condition); /* generate a constant string */ static void s_gen_code_str(const char *str, size_t len); }; /* ------------------------------------------------------------------------ */ /* * Unary Operators */ /* bitwise NOT */ CTPNUnary_def(CTPNBNot); /* arithmetic positive */ CTPNUnary_def(CTPNPos); /* arithmetic negative */ CTPNUnary_def(CTPNNeg); /* pre-increment */ CTPNUnary_side_def(CTPNPreInc); void CTPNPreInc_gen_code(class CTcPrsNode *sub, int discard); /* pre-decrement */ CTPNUnary_side_def(CTPNPreDec); /* post-increment */ CTPNUnary_side_def(CTPNPostInc); /* post-decrement */ CTPNUnary_side_def(CTPNPostDec); /* delete */ CTPNUnary_side_def(CTPNDelete); /* boolean-ize */ CTPNUnary_def(CTPNBoolize); /* ------------------------------------------------------------------------ */ /* * NEW operator */ class CTPNNew: public CTPNUnary { public: CTPNNew(class CTcPrsNode *sub, int is_transient) : CTPNUnary(sub) { /* remember 'transient' status */ transient_ = is_transient; } /* generate code */ void gen_code(int discard, int for_condition); /* note that we have side effects */ virtual int has_side_effects() const { return TRUE; } protected: /* flag: it's a 'new transient' operator */ int transient_; }; /* ------------------------------------------------------------------------ */ /* * NOT operator */ class CTPNNot: public CTPNNotBase { public: CTPNNot(CTcPrsNode *sub) : CTPNNotBase(sub) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Binary Operators */ class CTPNComma: public CTPNCommaBase { public: CTPNComma(class CTcPrsNode *lhs, class CTcPrsNode *rhs) : CTPNCommaBase(lhs, rhs) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* bitwise OR */ CTPNBin_def(CTPNBOr); /* bitwise AND */ CTPNBin_def(CTPNBAnd); /* bitwise XOR */ CTPNBin_def(CTPNBXor); /* greater than */ CTPNBin_cmp_def(CTPNGt); /* greater or equal */ CTPNBin_cmp_def(CTPNGe); /* less than */ CTPNBin_cmp_def(CTPNLt); /* less or equal */ CTPNBin_cmp_def(CTPNLe); /* bit shift left */ CTPNBin_def(CTPNShl); /* arithmetic shift right */ CTPNBin_def(CTPNAShr); /* logical shift right */ CTPNBin_def(CTPNLShr); /* multiply */ CTPNBin_def(CTPNMul); /* divide */ CTPNBin_def(CTPNDiv); /* modulo */ CTPNBin_def(CTPNMod); /* ------------------------------------------------------------------------ */ /* * Addition */ class CTPNAdd: public CTPNAddBase { public: CTPNAdd(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAddBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Subtraction */ class CTPNSub: public CTPNSubBase { public: CTPNSub(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNSubBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Equality Comparison */ class CTPNEq: public CTPNEqBase { public: CTPNEq(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNEqBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Inequality Comparison */ class CTPNNe: public CTPNNeBase { public: CTPNNe(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNNeBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'is in' */ class CTPNIsIn: public CTPNIsInBase { public: CTPNIsIn(class CTcPrsNode *left, class CTPNArglist *right) : CTPNIsInBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'not in' */ class CTPNNotIn: public CTPNNotInBase { public: CTPNNotIn(class CTcPrsNode *left, class CTPNArglist *right) : CTPNNotInBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Logical AND (short-circuit logic) */ class CTPNAnd: public CTPNAndBase { public: CTPNAnd(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAndBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a condition */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); }; /* ------------------------------------------------------------------------ */ /* * Logical OR (short-circuit logic) */ class CTPNOr: public CTPNOrBase { public: CTPNOr(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNOrBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate a condition */ virtual void gen_code_cond(struct CTcCodeLabel *then_label, struct CTcCodeLabel *else_label); }; /* ------------------------------------------------------------------------ */ /* * Assignment Operators */ /* simple assignment */ class CTPNAsi: public CTPNAsiBase { public: CTPNAsi(class CTcPrsNode *left, class CTcPrsNode *right) : CTPNAsiBase(left, right) { } /* generate code */ void gen_code(int discard, int for_condition); }; /* add and assign */ CTPNBin_side_def(CTPNAddAsi); void CTPNAddAsi_gen_code(class CTcPrsNode *left, class CTcPrsNode *right, int discard); /* subtract and assign */ CTPNBin_side_def(CTPNSubAsi); /* multiple and assign */ CTPNBin_side_def(CTPNMulAsi); /* divide and assign */ CTPNBin_side_def(CTPNDivAsi); /* modulo and assign */ CTPNBin_side_def(CTPNModAsi); /* bitwise AND and assign */ CTPNBin_side_def(CTPNBAndAsi); /* bitwise OR and assign */ CTPNBin_side_def(CTPNBOrAsi); /* bitwise XOR and assign */ CTPNBin_side_def(CTPNBXorAsi); /* bit shift left and assign */ CTPNBin_side_def(CTPNShlAsi); /* arithmetic shift right and assign */ CTPNBin_side_def(CTPNAShrAsi); /* logical shift right and assign */ CTPNBin_side_def(CTPNLShrAsi); /* ------------------------------------------------------------------------ */ /* * Array/list Subscript */ class CTPNSubscript: public CTPNSubscriptBase { public: CTPNSubscript(CTcPrsNode *lhs, CTcPrsNode *rhs) : CTPNSubscriptBase(lhs, rhs) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* ------------------------------------------------------------------------ */ /* * Address-of Operator */ class CTPNAddr: public CTPNAddrBase { public: CTPNAddr(CTcPrsNode *sub) : CTPNAddrBase(sub) { } /* generate code */ void gen_code(int discard, int for_condition) { /* * if we're not discarding the value, tell the subnode to * generate its address; if the value is being discarded, do * nothing, since taking an address has no side effects */ if (!discard) sub_->gen_code_addr(); } /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args) { /* * let the subnode do the work, since "&obj" is the same as * "obj" in any context */ sub_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs, named_args); } /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr) { /* * let the subexpression handle it, so that we treat "x.&prop" * the same as "x.prop" */ return sub_->gen_code_propid(check_only, is_expr); } /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* * let the subexpression handle it, since "x.&prop" is the same * as "x.prop" */ return sub_->gen_code_obj_predot(is_self); } }; /* ------------------------------------------------------------------------ */ /* * If-nil operator ?? */ class CTPNIfnil: public CTPNIfnilBase { public: CTPNIfnil(class CTcPrsNode *first, class CTcPrsNode *second) : CTPNIfnilBase(first, second) { } /* generate code for the conditional */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Conditional Operator */ class CTPNIf: public CTPNIfBase { public: CTPNIf(class CTcPrsNode *first, class CTcPrsNode *second, class CTcPrsNode *third) : CTPNIfBase(first, second, third) { } /* generate code for the conditional */ void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Symbol node */ class CTPNSym: public CTPNSymBase { public: CTPNSym(const char *sym, size_t len) : CTPNSymBase(sym, len) { } /* generate code for the symbol evaluated as an rvalue */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate code to take the address of the symbol */ void gen_code_addr(); /* generate code to call the symbol */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * Resolved symbol */ class CTPNSymResolved: public CTPNSymResolvedBase { public: CTPNSymResolved(CTcSymbol *sym) : CTPNSymResolvedBase(sym) { } /* generate code for the symbol evaluated as an rvalue */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate code to take the address of the symbol */ void gen_code_addr(); /* generate code to call the symbol */ void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); }; /* ------------------------------------------------------------------------ */ /* * Resolved debugger local variable symbol */ class CTPNSymDebugLocal: public CTPNSymDebugLocalBase { public: CTPNSymDebugLocal(const struct tcprsdbg_sym_info *info) : CTPNSymDebugLocalBase(info) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code for assigning into this node */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* ------------------------------------------------------------------------ */ /* * double-quoted string expression */ class CTPNDstr: public CTPNDstrBase { public: CTPNDstr(const char *str, size_t len) : CTPNDstrBase(str, len) { } void gen_code(int discard, int for_condition); }; /* * debug version of string */ class CTPNDebugDstr: public CTPNDstr { public: CTPNDebugDstr(const char *str, size_t len) : CTPNDstr(str, len) { } void gen_code(int discard, int for_condition); }; /* * double-quoted string embedding */ class CTPNDstrEmbed: public CTPNDstrEmbedBase { public: CTPNDstrEmbed(CTcPrsNode *sub); void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Embedded <> list in a string */ class CTPNStrOneOf: public CTPNStrOneOfBase { public: CTPNStrOneOf(int dstr, class CTPNList *lst, class CTcSymObj *state_obj) : CTPNStrOneOfBase(dstr, lst, state_obj) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Argument List */ class CTPNArglist: public CTPNArglistBase { public: CTPNArglist(int argc, class CTPNArg *list_head) : CTPNArglistBase(argc, list_head) { } void gen_code(int, int) { /* * this isn't used - we always generate explicitly via * gen_code_arglist() */ assert(FALSE); } /* generate code for an argument list */ void gen_code_arglist(int *varargs, struct CTcNamedArgs &named_args); }; /* * Argument List Entry */ class CTPNArg: public CTPNArgBase { public: CTPNArg(CTcPrsNode *expr) : CTPNArgBase(expr) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Function/method call */ class CTPNCall: public CTPNCallBase { public: CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist); /* generate code */ void gen_code(int discard, int for_condition); /* generate code for operator 'new' applied to this expression */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int from_call, int is_transient); /* generate code for a call to 'rand' */ int gen_code_rand(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Member expression with no arguments */ class CTPNMember: public CTPNMemberBase { public: CTPNMember(CTcPrsNode *lhs, CTcPrsNode *rhs, int rhs_is_expr) : CTPNMemberBase(lhs, rhs, rhs_is_expr) { } /* generate code */ void gen_code(int discard, int for_condition); /* generate code to assign a value to the symbol */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* * Member evaluation with argument list */ class CTPNMemArg: public CTPNMemArgBase { public: CTPNMemArg(CTcPrsNode *obj_expr, CTcPrsNode *prop_expr, int prop_is_expr, class CTPNArglist *arglist) : CTPNMemArgBase(obj_expr, prop_expr, prop_is_expr, arglist) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * List */ class CTPNList: public CTPNListBase { public: void gen_code(int discard, int for_condition); }; /* * List Element */ class CTPNListEle: public CTPNListEleBase { public: CTPNListEle(CTcPrsNode *expr) : CTPNListEleBase(expr) { } void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Derived T3-specific symbol classes */ /* * undefined symbol */ class CTcSymUndef: public CTcSymUndefBase { public: CTcSymUndef(const char *str, size_t len, int copy) : CTcSymUndefBase(str, len, copy) { } /* * generate code to evaluate the symbol; since it's undefined, this * doesn't generate any code at all */ virtual void gen_code(int) { } /* * Generate code to assign to the symbol. The symbol is undefined, * so we can't generate any code; but we'll indicate to the caller * that the assignment is fully processed anyway, since there's no * need for the caller to try generating any code either. */ virtual int gen_code_asi(int, int phase, tc_asitype_t, const char *, class CTcPrsNode *, int, int, void **) { return TRUE; } /* * Generate code for taking the address of the symbol. The symbol * has no address, so this generates no code. */ virtual void gen_code_addr() { } /* * generate code to call the symbol - we can't generate any code for * this, but don't bother issuing an error since we will have shown * an error for the undefined symbol in the first place */ virtual void gen_code_call(int, int, int, struct CTcNamedArgs *) { } /* * generate code for operator 'new' applied to this expression - we * can't generate any code, but suppress errors as usual */ void gen_code_new(int, int, int, struct CTcNamedArgs *, int) { } /* generate a member expression */ void gen_code_member(int, class CTcPrsNode *, int, int, int, struct CTcNamedArgs *) { } /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { *is_self = FALSE; return VM_INVALID_OBJ; } }; /* * function */ class CTcSymFunc: public CTcSymFuncBase { public: CTcSymFunc(const char *str, size_t len, int copy, int argc, int opt_argc, int varargs, int has_retval, int is_multimethod, int is_mm_base, int is_extern, int has_proto) : CTcSymFuncBase(str, len, copy, argc, opt_argc, varargs, has_retval, is_multimethod, is_mm_base, is_extern, has_proto) { /* * we don't have a valid absolute address - by default, we use * the anchor */ abs_addr_valid_ = FALSE; abs_addr_ = 0; } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* * Get my code pool address. This is valid only after the linking * phase has been completed and all fixups have been applied. (The * normal use for this is to generate image file elements not * subject to fixup, such as the entrypoint record. Normally, * references to the symbol that require the code pool address would * simply generate a fixup rather than asking for the true address, * since a fixup can be generated at any time during code * generation.) */ ulong get_code_pool_addr() const; /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* * Set the absolute code pool address. This can only be used when * the symbol is loaded from a fully-linked image file; in most * cases, this routine is not needed, because the function's address * will be kept with its associated anchor to allow for relocation * of the function during the linking process. */ void set_abs_addr(uint32_t addr) { /* remember the address */ abs_addr_ = addr; /* note that the absolute address is valid */ abs_addr_valid_ = TRUE; } /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); protected: /* * Absolute code pool address, and a flag indicating whether the * address is valid. When we load a function symbol from a * fully-linked image file (specifically, when we load the symbol * from the debug records of an image file), the address of the * function is fully resolved and doesn't require fixups via the * anchor mechanism. In these cases, we'll simply remember the * resolved address here. */ ulong abs_addr_; uint abs_addr_valid_ : 1; }; /* * metaclass */ class CTcSymMetaclass: public CTcSymMetaclassBase { public: CTcSymMetaclass(const char *str, size_t len, int copy, int meta_idx, tctarg_obj_id_t class_obj) : CTcSymMetaclassBase(str, len, copy, meta_idx, class_obj) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate code for operator 'new' */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* * fix up the inheritance chain in the modifier objects - each * object file has its own independent inheritance list, so we can * only build the complete and connected list after loading them all */ void fix_mod_obj_sc_list(); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * object */ class CTcSymObj: public CTcSymObjBase { public: CTcSymObj(const char *str, size_t len, int copy, vm_obj_id_t obj_id, int is_extern, tc_metaclass_t meta, class CTcDictEntry *dict) : CTcSymObjBase(str, len, copy, obj_id, is_extern, meta, dict) { } /* we have a valid object value */ virtual vm_obj_id_t get_val_obj() const { return get_obj_id(); } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* generate code for operator 'new' */ void gen_code_new(int discard, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* static code to generate code for 'new' */ static void s_gen_code_new(int discard, vm_obj_id_t obj_id, tc_metaclass_t meta, int argc, int varargs, struct CTcNamedArgs *named_args, int is_transient); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* static code to generate a member expression */ static void s_gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, vm_obj_id_t obj_id, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self); /* delete a property from our modified base classes */ virtual void delete_prop_from_mod_base(tctarg_prop_id_t prop_id); /* mark the compiled data for the object as a 'class' object */ virtual void mark_compiled_as_class(); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* build my dictionary */ virtual void build_dictionary(); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * Function-like object symbol. This is an object that's invokable as a * function, such as an anonymous function object or a DynamicFunc * instance. These occur in the dynamic compiler only. */ class CTcSymFuncObj: public CTcSymObj { public: CTcSymFuncObj(const char *str, size_t len, int copy, vm_obj_id_t obj_id, int is_extern, tc_metaclass_t meta, class CTcDictEntry *dict) : CTcSymObj(str, len, copy, obj_id, is_extern, meta, dict) { } /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); }; /* * property */ class CTcSymProp: public CTcSymPropBase { public: CTcSymProp(const char *str, size_t len, int copy, tctarg_prop_id_t prop) : CTcSymPropBase(str, len, copy, prop) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* we can take the address of this symbol type */ virtual void gen_code_addr(); /* assign to the property */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * Enumerator */ class CTcSymEnum: public CTcSymEnumBase { public: CTcSymEnum(const char *str, size_t len, int copy, ulong id, int is_token) : CTcSymEnumBase(str, len, copy, id, is_token) { } /* generate code */ virtual void gen_code(int discard); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * local variable/parameter */ class CTcSymLocal: public CTcSymLocalBase { public: CTcSymLocal(const char *str, size_t len, int copy, int is_param, int var_num) : CTcSymLocalBase(str, len, copy, is_param, var_num) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate code to store the value at top of stack in the variable */ void gen_code_setlcl_stk() { s_gen_code_setlcl_stk(var_num_, is_param_); } /* generate code to get a local with a given ID */ static void s_gen_code_getlcl(int local_id, int is_param); /* generate code to store a local with a given ID */ static void s_gen_code_setlcl_stk(int local_id, int is_param); /* generate code to store the value at top of stack in the local */ void gen_code_setlcl(); /* assign to the variable */ virtual int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); /* * generate a call - invoking a local as a function assumes that the * local contains a method or function pointer */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* evaluate a property ID */ vm_prop_id_t gen_code_propid(int check_only, int is_expr); /* generate a member expression */ void gen_code_member(int discard, class CTcPrsNode *prop_expr, int prop_is_expr, int argc, int varargs, struct CTcNamedArgs *named_args); /* get the object value for a '.' expression */ vm_obj_id_t gen_code_obj_predot(int *is_self) { /* simply generate our code as normal */ gen_code(FALSE); /* indicate that the caller must use generated code */ *is_self = FALSE; return VM_INVALID_OBJ; } /* write the symbol to a debug frame */ virtual int write_to_debug_frame(int test_only); }; /* * Run-time dynamic code local variable symbol. This is the symbol type * for a local accessed via a StackFrameRef object, for use in DynamicFunc * compilations. */ class CTcSymDynLocal: public CTcSymDynLocalBase { public: CTcSymDynLocal(const char *str, size_t len, int copy, tctarg_obj_id_t fref, int varnum, int ctxidx) : CTcSymDynLocalBase(str, len, copy, fref, varnum, ctxidx) { } /* generate code */ void gen_code(int discard); /* generate code for assigning into this node */ int gen_code_asi(int discard, int phase, tc_asitype_t typ, const char *op, class CTcPrsNode *rhs, int ignore_errors, int xplicit, void **ctx); }; /* * built-in function */ class CTcSymBif: public CTcSymBifBase { public: CTcSymBif(const char *str, size_t len, int copy, ushort func_set_id, ushort func_idx, int has_retval, int min_argc, int max_argc, int varargs) : CTcSymBifBase(str, len, copy, func_set_id, func_idx, has_retval, min_argc, max_argc, varargs) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* generate code for an address '&' operator */ virtual void gen_code_addr(); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); /* add a runtime symbol table entry */ void add_runtime_symbol(class CVmRuntimeSymbols *symtab); }; /* * external function */ class CTcSymExtfn: public CTcSymExtfnBase { public: CTcSymExtfn(const char *str, size_t len, int copy, int argc, int varargs, int has_retval) : CTcSymExtfnBase(str, len, copy, argc, varargs, has_retval) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); /* generate a call */ virtual void gen_code_call(int discard, int argc, int varargs, struct CTcNamedArgs *named_args); /* write the symbol to an image file's global symbol table */ int write_to_image_file_global(class CVmImageWriter *image_writer); }; /* * code label */ class CTcSymLabel: public CTcSymLabelBase { public: CTcSymLabel(const char *str, size_t len, int copy) : CTcSymLabelBase(str, len, copy) { } /* generate code to evaluate the symbol */ virtual void gen_code(int discard); }; /* ------------------------------------------------------------------------ */ /* * Anonymous function */ class CTPNAnonFunc: public CTPNAnonFuncBase { public: CTPNAnonFunc(class CTPNCodeBody *code_body, int has_retval, int is_method) : CTPNAnonFuncBase(code_body, has_retval, is_method) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * Code Body */ class CTPNCodeBody: public CTPNCodeBodyBase { public: CTPNCodeBody(class CTcPrsSymtab *lcltab, class CTcPrsSymtab *gototab, class CTPNStm *stm, int argc, int opt_argc, int varargs, int varargs_list, class CTcSymLocal *varargs_list_local, int local_cnt, int self_valid, struct CTcCodeBodyRef *enclosing_code_body) : CTPNCodeBodyBase(lcltab, gototab, stm, argc, opt_argc, varargs, varargs_list, varargs_list_local, local_cnt, self_valid, enclosing_code_body) { /* presume I'm not a constructor */ is_constructor_ = FALSE; /* presume I won't need a 'finally' return value holder local */ allocated_fin_ret_lcl_ = FALSE; fin_ret_lcl_ = 0; /* presume it won't be static */ is_static_ = FALSE; /* no nested symbol table list yet */ first_nested_symtab_ = 0; /* no named argument tables yet */ named_arg_tables_ = 0; } /* * Mark the code body as belonging to a constructor. A constructor * must return its 'self' object as the return value. */ void set_constructor(int f) { is_constructor_ = f; } /* * mark the code body as static - this indicates that it is a static * initializer */ void set_static() { is_static_ = TRUE; } /* generate code */ virtual void gen_code(int discard, int for_condition); /* check locals */ virtual void check_locals(); /* * Allocate a local variable for temporarily saving the expression * value of a 'return' statement while calling the enclosing * 'finally' block(s). Returns the variable ID. Only one such * local is needed per code body, so if we've already allocated one * for another 'return' statement, we'll just return the same one we * allocated previously. */ uint alloc_fin_ret_lcl() { /* if we haven't allocated it yet, do so now */ if (!allocated_fin_ret_lcl_) { /* use the next available local */ fin_ret_lcl_ = local_cnt_++; /* note that we've allocated it */ allocated_fin_ret_lcl_ = TRUE; } /* return the local ID */ return fin_ret_lcl_; } /* * Add a named argument table to the code body. Each call that has * named arguments requires one of these tables. We keep a list of * tables, and generate them all at the end of the method. Returns a * label that can be used to write a forward reference offset to the * table's offset in the code stream. */ struct CTcCodeLabel *add_named_arg_tab( const struct CTcNamedArgs *named_args); protected: /* * callback for enumerating local frame symbol table entries - write * each entry to the code stream as a debug record */ static void write_local_to_debug_frame(void *ctx, class CTcSymbol *sym); /* * enumerator for checking for parameter symbols that belong in the * local context */ static void enum_for_param_ctx(void *ctx, class CTcSymbol *sym); /* enumerator for generating named parameter bindings */ static void enum_for_named_params(void *ctx, class CTcSymbol *sym); /* write the debug information table to the code stream */ void build_debug_table(ulong start_ofs, int include_lines); /* show disassembly */ void show_disassembly(unsigned long start_ofs, unsigned long code_start_ofs, unsigned long code_end_ofs); /* head of list of nested symbol tables */ class CTcPrsSymtab *first_nested_symtab_; /* head of list of named argument tables */ struct CTcNamedArgTab *named_arg_tables_; /* * ID of local variable for temporarily storing the expression value * of a 'return' statement while calling the enclosing 'finally' * block(s); valid only when allocated_fin_ret_lcl_ is true */ uint fin_ret_lcl_; /* flag: I'm a constructor */ uint is_constructor_ : 1; /* * flag: I've allocated a local for saving the expression value of a * 'return' statement while calling the enclosing 'finally' block(s) */ uint allocated_fin_ret_lcl_ : 1; /* flag: I'm a static property initializer */ uint is_static_ : 1; }; /* ------------------------------------------------------------------------ */ /* * Compound Statement */ class CTPNStmComp: public CTPNStmCompBase { public: CTPNStmComp(class CTPNStm *first_stm, class CTcPrsSymtab *symtab) : CTPNStmCompBase(first_stm, symtab) { } }; /* ------------------------------------------------------------------------ */ /* * Null statement */ class CTPNStmNull: public CTPNStmNullBase { public: CTPNStmNull() { } }; /* ------------------------------------------------------------------------ */ /* * Expression statement */ class CTPNStmExpr: public CTPNStmExprBase { public: CTPNStmExpr(class CTcPrsNode *expr) : CTPNStmExprBase(expr) { } }; /* ------------------------------------------------------------------------ */ /* * Static property initializer statement */ class CTPNStmStaticPropInit: public CTPNStmStaticPropInitBase { public: CTPNStmStaticPropInit(class CTcPrsNode *expr, tctarg_prop_id_t prop) : CTPNStmStaticPropInitBase(expr, prop) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'if' statement */ class CTPNStmIf: public CTPNStmIfBase { public: CTPNStmIf(class CTcPrsNode *cond_expr, class CTPNStm *then_part, class CTPNStm *else_part) : CTPNStmIfBase(cond_expr, then_part, else_part) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'for' statement */ class CTPNStmFor: public CTPNStmForBase { public: CTPNStmFor(class CTcPrsNode *init_expr, class CTcPrsNode *cond_expr, class CTcPrsNode *reinit_expr, class CTPNForIn *in_exprs, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm) : CTPNStmForBase(init_expr, cond_expr, reinit_expr, in_exprs, symtab, enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * ' in ' node, for 'for' statements */ class CTPNVarIn: public CTPNVarInBase { public: CTPNVarIn(class CTcPrsNode *lval, class CTcPrsNode *expr, int iter_local_id) : CTPNVarInBase(lval, expr, iter_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl); /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit(); /* * Generate the iterator initializer. This is a static method so that * it can be shared by for..in and foreach..in. 'kw' is the keyword * for the statement type that we're generating ("for" or "foreach"), * so that we can show the appropriate statement type in any error * messages. */ static void gen_iter_init(class CTcPrsNode *coll_expr, int iter_local_id, const char *kw); /* * Generate the iterator condition. This is a static method so that it * can be shared by for..in and foreach..in. */ static void gen_iter_cond(class CTcPrsNode *lval, int iter_local_id, struct CTcCodeLabel *&endlbl, const char *kw); }; /* * ' in .. ' node, for 'for' statements */ class CTPNVarInRange: public CTPNVarInRangeBase { public: CTPNVarInRange(class CTcPrsNode *lval, class CTcPrsNode *from_expr, class CTcPrsNode *to_expr, class CTcPrsNode *step_expr, int to_local_id, int step_local_id) : CTPNVarInRangeBase(lval, from_expr, to_expr, step_expr, to_local_id, step_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate the condition part of the 'for' */ virtual void gen_forstm_cond(struct CTcCodeLabel *endlbl); /* generate the reinit part of the 'for' */ virtual void gen_forstm_reinit(); }; /* ------------------------------------------------------------------------ */ /* * 'for' statement */ class CTPNStmForeach: public CTPNStmForeachBase { public: CTPNStmForeach(class CTcPrsNode *iter_expr, class CTcPrsNode *coll_expr, class CTcPrsSymtab *symtab, class CTPNStmEnclosing *enclosing_stm, int iter_local_id) : CTPNStmForeachBase(iter_expr, coll_expr, symtab, enclosing_stm, iter_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'break' statement */ class CTPNStmBreak: public CTPNStmBreakBase { public: CTPNStmBreak() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'continue' statement */ class CTPNStmContinue: public CTPNStmContinueBase { public: CTPNStmContinue() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'while' statement */ class CTPNStmWhile: public CTPNStmWhileBase { public: CTPNStmWhile(class CTcPrsNode *cond_expr, class CTPNStmEnclosing *enclosing_stm) : CTPNStmWhileBase(cond_expr, enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'do-while' statement */ class CTPNStmDoWhile: public CTPNStmDoWhileBase { public: CTPNStmDoWhile(class CTPNStmEnclosing *enclosing_stm) : CTPNStmDoWhileBase(enclosing_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { return gen_code_break_loop(break_lbl_, lbl, lbl_len); } /* generate code for 'continue' */ virtual int gen_code_labeled_continue() { return gen_code_continue_loop(cont_lbl_, 0, 0); } /* generate code for a labeled 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len) { return gen_code_continue_loop(cont_lbl_, lbl, lbl_len); } protected: /* our 'break' label */ struct CTcCodeLabel *break_lbl_; /* our 'continue' label */ struct CTcCodeLabel *cont_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'return' statement */ class CTPNStmReturn: public CTPNStmReturnBase { public: CTPNStmReturn(class CTcPrsNode *expr) : CTPNStmReturnBase(expr) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'switch' statement */ class CTPNStmSwitch: public CTPNStmSwitchBase { public: CTPNStmSwitch(class CTPNStmEnclosing *enclosing_stm) : CTPNStmSwitchBase(enclosing_stm) { /* the 'case' and 'default' slot pointers aren't valid yet */ case_slot_ofs_ = 0; default_slot_ofs_ = 0; } /* generate code */ virtual void gen_code(int discard, int for_condition); /* * Get the next case slot offset, and consume the slot. Each call * consumes one slot. This is valid only during code generation in * the switch body; 'case' labels use this to figure out where to * write their data. */ ulong alloc_case_slot() { ulong ret; /* note the value to return */ ret = case_slot_ofs_; /* move our internal counter to the next slot */ case_slot_ofs_ += VMB_DATAHOLDER + VMB_UINT2; /* return the allocated slot */ return ret; } /* get the code offset of the 'default' case slot */ ulong get_default_slot() const { return default_slot_ofs_; } /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len) { /* * if there's no label, and we don't have a 'break' label * ourselves, it must mean that we're processing an unreachable * 'break' - we can simply do nothing in this case and pretend we * succeeded */ if (lbl == 0 && break_lbl_ == 0) return TRUE; /* break to our break label */ return gen_code_break_loop(break_lbl_, lbl, lbl_len); } protected: /* * code offset of the next 'case' slot to be allocated from the * switch's case table - this is only valid during generation of the * body, because we set this up during generation of the switch * itself */ ulong case_slot_ofs_; /* code offset of the 'default' slot */ ulong default_slot_ofs_; /* our 'break' label */ struct CTcCodeLabel *break_lbl_; }; /* ------------------------------------------------------------------------ */ /* * code label statement */ class CTPNStmLabel: public CTPNStmLabelBase { public: CTPNStmLabel(class CTcSymLabel *lbl, class CTPNStmEnclosing *enclosing); /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len); /* generate code for 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len); /* * Generate code for a labeled 'continue'. We'll simply generate * code for a labeled continue in our enclosed statement. * * (This is normally a routine that *we* call on our contained * statement in response to a 'continue' being targeted at this * label. This can be invoked on us, however, when a statement has * multiple labels, and the outer label is mentioned in the 'break' * -- the outer label will call this routine on the inner label, * which is us, so we simply need to pass it along to the enclosed * statement.) */ virtual int gen_code_labeled_continue() { return (stm_ != 0 ? stm_->gen_code_labeled_continue() : FALSE); } /* get my 'goto' label */ struct CTcCodeLabel *get_goto_label(); protected: /* the byte label to jump to this statement */ struct CTcCodeLabel *goto_label_; /* the byte label to break from this statement */ struct CTcCodeLabel *break_label_; }; /* ------------------------------------------------------------------------ */ /* * 'case' label statement */ class CTPNStmCase: public CTPNStmCaseBase { public: CTPNStmCase() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'default' label statement */ class CTPNStmDefault: public CTPNStmDefaultBase { public: CTPNStmDefault() { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'try' block */ class CTPNStmTry: public CTPNStmTryBase { public: CTPNStmTry(class CTPNStmEnclosing *enclosing) : CTPNStmTryBase(enclosing) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for 'break' */ virtual int gen_code_break(const textchar_t *lbl, size_t lbl_len); /* generate code for 'continue' */ virtual int gen_code_continue(const textchar_t *lbl, size_t lbl_len); /* * Generate the code necessary to unwind for returning. We must * generate a call to our 'finally' code, then unwind any enclosing * statements. */ virtual void gen_code_unwind_for_return() { /* call 'finally' on the way out */ gen_jsr_finally(); /* inherit default to unwind enclosing statements */ CTPNStmTryBase::gen_code_unwind_for_return(); } /* determine if we'll generate code for unwinding from a return */ virtual int will_gen_code_unwind_for_return() const { /* we'll generate a call to our 'finally' block if we have one */ if (finally_stm_ != 0) return TRUE; /* we'll also generate code if any enclosing statement does */ return CTPNStmTryBase::will_gen_code_unwind_for_return(); } /* generate the code necessary to transfer out of the block */ virtual void gen_code_for_transfer_out() { /* call 'finally' on the way out */ gen_jsr_finally(); } /* * generate a local subroutine call to our 'finally' block, if we * have a 'finally' block - this should be invoked whenever code * within the protected block or a 'catch' block executes a break, * continue, return, or goto out of the protected region, or when * merely falling off the end of the protected block or a 'catch' * block. */ void gen_jsr_finally(); /* get my 'finally' label */ struct CTcCodeLabel *get_finally_lbl() const { return finally_lbl_; } protected: /* our 'finally' label */ struct CTcCodeLabel *finally_lbl_; }; /* ------------------------------------------------------------------------ */ /* * 'catch' */ class CTPNStmCatch: public CTPNStmCatchBase { public: CTPNStmCatch() { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for the 'catch' clause */ virtual void gen_code_catch(ulong protected_start_ofs, ulong protected_end_ofs); }; /* ------------------------------------------------------------------------ */ /* * 'finally' */ class CTPNStmFinally: public CTPNStmFinallyBase { public: CTPNStmFinally(CTPNStmEnclosing *enclosing, int exc_local_id, int jsr_local_id) : CTPNStmFinallyBase(enclosing, exc_local_id, jsr_local_id) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for the 'finally' clause */ virtual void gen_code_finally(ulong protected_start_ofs, ulong protected_end_ofs, class CTPNStmTry *try_stm); /* check for transfer in via 'goto' statements */ virtual int check_enter_by_goto(class CTPNStmGoto *goto_stm, class CTPNStmLabel *target); }; /* ------------------------------------------------------------------------ */ /* * 'throw' statement */ class CTPNStmThrow: public CTPNStmThrowBase { public: CTPNStmThrow(class CTcPrsNode *expr) : CTPNStmThrowBase(expr) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'goto' statement */ class CTPNStmGoto: public CTPNStmGotoBase { public: CTPNStmGoto(const textchar_t *lbl, size_t lbl_len) : CTPNStmGotoBase(lbl, lbl_len) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* ------------------------------------------------------------------------ */ /* * 'dictionary' statement */ class CTPNStmDict: public CTPNStmDictBase { public: CTPNStmDict(class CTcDictEntry *dict) : CTPNStmDictBase(dict) { } /* generate code */ virtual void gen_code(int, int) { } }; /* ------------------------------------------------------------------------ */ /* * Object Definition */ class CTPNStmObject: public CTPNStmObjectBase { public: CTPNStmObject(class CTcSymObj *obj_sym, int is_class) : CTPNStmObjectBase(obj_sym, is_class) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* check locals */ virtual void check_locals(); /* * given the stream offset for the start of the object data in the * stream, get information on the object from the stream data: * *. - get the number of properties in the stream data *. - set the number of properties in the stream data, updating the * stored data size in the metaclass header in the stream; returns * the new data size *. - get the number of superclasses in the stream data *. - get a superclass object ID *. - get the offset of the first property in the stream data *. - get the offset of the property at the given index *. - get the ID of the object's property at the given index *. - set the ID of the object's property at the given index *. - get the object flags from the tads-object header *. - set the object flags in the tads-object heade */ static uint get_stream_prop_cnt(class CTcDataStream *stream, ulong obj_ofs); static size_t set_stream_prop_cnt(class CTcDataStream *stream, ulong obj_ofs, uint prop_cnt); static uint get_stream_sc_cnt(class CTcDataStream *stream, ulong obj_ofs); static vm_obj_id_t get_stream_sc(class CTcDataStream *stream, ulong obj_ofs, uint sc_idx); static void set_stream_sc(class CTcDataStream *stream, ulong obj_ofs, uint sc_idx, vm_obj_id_t new_sc); static ulong get_stream_first_prop_ofs(class CTcDataStream *stream, ulong obj_ofs); static ulong get_stream_prop_ofs(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static vm_prop_id_t get_stream_prop_id(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static enum vm_datatype_t get_stream_prop_type(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static ulong get_stream_prop_val_ofs(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx); static void set_stream_prop_id(class CTcDataStream *stream, ulong obj_ofs, uint prop_idx, vm_prop_id_t new_id); static uint get_stream_obj_flags(class CTcDataStream *stream, ulong obj_ofs); static void set_stream_obj_flags(class CTcDataStream *stream, ulong obj_ofs, uint flags); }; /* * Inline object definition (defined within an expression) */ class CTPNInlineObject: public CTPNInlineObjectBase { public: /* generate code */ virtual void gen_code(int discard, int for_condition); }; /* * Property Value list entry */ class CTPNObjProp: public CTPNObjPropBase { public: CTPNObjProp(class CTPNObjDef *objdef, class CTcSymProp *prop_sym, class CTcPrsNode *expr, class CTPNCodeBody *code_body, class CTPNAnonFunc *inline_method, int is_static) : CTPNObjPropBase( objdef, prop_sym, expr, code_body, inline_method, is_static) { } /* generate code */ virtual void gen_code(int discard, int for_condition); /* generate code for an inline object instantiation */ void gen_code_inline_obj(); /* check locals */ void check_locals(); protected: /* generate a setMethod() call for inline object creation */ void gen_setMethod(); }; /* * Implicit constructor */ class CTPNStmImplicitCtor: public CTPNStmImplicitCtorBase { public: CTPNStmImplicitCtor(class CTPNStmObject *obj_stm) : CTPNStmImplicitCtorBase(obj_stm) { } /* generate code */ virtual void gen_code(int discard, int for_condition); }; #endif /* TCT3DRV_H */ frobtads-1.2.3/tads3/vmhttpreq.cpp0000644000175000001440000027233012145504453016272 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhttpreq.cpp - CVmObjHTTPRequest object Function Notes Modified MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmhttpreq.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmrun.h" #include "vmstr.h" #include "vmstrbuf.h" #include "vmlst.h" #include "vmlookup.h" #include "utf8.h" #include "vmnet.h" #include "vmpredef.h" #include "vmbytarr.h" #include "charmap.h" #include "vmfilobj.h" #include "vmdatasrc.h" #include "vmcset.h" #include "vmimport.h" /* ------------------------------------------------------------------------ */ /* * Allocate an extension structure */ vm_httpreq_ext *vm_httpreq_ext::alloc_ext(VMG_ CVmObjHTTPRequest *self) { /* calculate how much space we need */ size_t siz = sizeof(vm_httpreq_ext); /* allocate the memory */ vm_httpreq_ext *ext = (vm_httpreq_ext *)G_mem->get_var_heap()->alloc_mem( siz, self); /* we don't have a system message object yet */ ext->req = 0; /* we have no reply cookies yet */ ext->cookies = 0; /* return the new extension */ return ext; } /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest object statics */ /* metaclass registration object */ static CVmMetaclassHTTPRequest metaclass_reg_obj; CVmMetaclass *CVmObjHTTPRequest::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjHTTPRequest::*CVmObjHTTPRequest::func_table_[])( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjHTTPRequest::getp_undef, &CVmObjHTTPRequest::getp_getServer, &CVmObjHTTPRequest::getp_getVerb, &CVmObjHTTPRequest::getp_getQuery, &CVmObjHTTPRequest::getp_parseQuery, &CVmObjHTTPRequest::getp_getQueryParam, &CVmObjHTTPRequest::getp_getHeaders, &CVmObjHTTPRequest::getp_getCookie, &CVmObjHTTPRequest::getp_getCookies, &CVmObjHTTPRequest::getp_getFormFields, &CVmObjHTTPRequest::getp_getBody, &CVmObjHTTPRequest::getp_getClientAddress, &CVmObjHTTPRequest::getp_setCookie, &CVmObjHTTPRequest::getp_sendReply, &CVmObjHTTPRequest::getp_startChunkedReply, &CVmObjHTTPRequest::getp_sendReplyChunk, &CVmObjHTTPRequest::getp_endChunkedReply, &CVmObjHTTPRequest::getp_sendReplyAsync }; /* ------------------------------------------------------------------------ */ /* * CVmObjHTTPRequest intrinsic class implementation */ /* * construction */ CVmObjHTTPRequest::CVmObjHTTPRequest(VMG_ int) { /* allocate our extension structure */ ext_ = (char *)vm_httpreq_ext::alloc_ext(vmg_ this); } /* * create */ vm_obj_id_t CVmObjHTTPRequest::create( VMG_ int in_root_set, TadsHttpRequest *req, vm_obj_id_t server) { /* create the object - we have no gc-obj refs of any kind */ vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE); /* requests are inherently transient */ G_obj_table->set_obj_transient(id); /* create the C++ object and get its extension */ CVmObjHTTPRequest *r = new (vmg_ id) CVmObjHTTPRequest(vmg_ TRUE); vm_httpreq_ext *ext = r->get_ext(); /* save the system request object */ if ((ext->req = req) != 0) req->add_ref(); /* save the server object */ ext->server = server; /* return the new object ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjHTTPRequest::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { /* * we can't be created with 'new' - we can only be created via the * getNetEvent() intrinsic function */ err_throw(VMERR_BAD_DYNAMIC_NEW); /* not reached, but the compiler might not know that */ AFTER_ERR_THROW(return VM_INVALID_OBJ;) } /* * notify of deletion */ void CVmObjHTTPRequest::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ vm_httpreq_ext *ext = get_ext(); if (ext != 0) { /* release the message object */ if (ext->req != 0) ext->req->release_ref(); /* delete the cookies */ if (ext->cookies != 0) delete ext->cookies; /* delete the extension */ G_mem->get_var_heap()->free_mem(ext_); } } /* * Mark references */ void CVmObjHTTPRequest::mark_refs(VMG_ uint state) { /* if we have a server object, mark it */ vm_httpreq_ext *ext = get_ext(); if (ext->server != VM_INVALID_OBJ) G_obj_table->mark_all_refs(ext->server, state); } /* * set a property */ void CVmObjHTTPRequest::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjHTTPRequest::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* translate the property into a function vector index */ uint func_idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * load from an image file */ void CVmObjHTTPRequest::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjHTTPRequest::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjHTTPRequest::load_image_data(VMG_ const char *ptr, size_t siz) { /* free our existing extension, if we have one */ notify_delete(vmg_ FALSE); /* allocate the extension */ vm_httpreq_ext *ext = vm_httpreq_ext::alloc_ext(vmg_ this); ext_ = (char *)ext; } /* * save to a file */ void CVmObjHTTPRequest::save_to_file(VMG_ class CVmFile *fp) { /* we're inherently transient, so we should never do this */ assert(FALSE); } /* * restore from a file */ void CVmObjHTTPRequest::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { /* we're transient, so this should never happen */ } /* * Create a new validated ASCII string. This converts any 8-bit characters * to '?' to ensure that the result is well-formed UTF-8. */ static void new_ascii_string(VMG_ vm_val_t *ret, const char *str, size_t len) { /* create and return a string for it */ vm_obj_id_t strid = CVmObjString::create(vmg_ FALSE, str, len); ret->set_obj(strid); /* * We expected a plain ASCII string as input, so any character outside * of the 7-bit ASCII range (0-127) is invalid. Scan the string and * convert any invalid characters to '?' characters. If we didn't do * this, any 8-bit characters could potentially trigger VM crashes, * since we'd try to interpret them as multi-byte UTF-8 character * components even though they might not form valid UTF-8 sequences. * Eliminating any 8-bit characters up front thus protects us against * errant or malicious clients by ensuring that we have a well-formed * UTF-8 string in all cases (since any string containing only plain * ASCII characters is inherently a well-formed UTF-8 string). */ CVmObjString *strp = (CVmObjString *)vm_objp(vmg_ strid); for (char *p = strp->cons_get_buf() ; len != 0 ; ++p, --len) { /* convert anything outside of plain ASCII (0-127) to '?' */ if (*(uchar *)p > 127) *p = '?'; } } static void new_ascii_string(VMG_ vm_val_t *ret, const char *str) { return new_ascii_string(vmg_ ret, str, strlen(str)); } /* * get the server (the HTTPServer object) */ int CVmObjHTTPRequest::getp_getServer(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* return the server object */ retval->set_obj_or_nil(get_ext()->server); /* handled */ return TRUE; } /* * get the HTTP verb */ int CVmObjHTTPRequest::getp_getVerb(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* return a validated ASCII string for the verb in the request */ new_ascii_string(vmg_ retval, get_ext()->req->verb->get()); /* handled */ return TRUE; } /* * get the query string */ int CVmObjHTTPRequest::getp_getQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* create a validated ASCII string for the query string */ new_ascii_string(vmg_ retval, get_ext()->req->resource_name->get()); /* handled */ return TRUE; } /* encoding modes for new_parsed_str */ #define ENCMODE_NONE 0 /* no encoding */ #define ENCMODE_URL 1 /* URL encoding - translate %xx codes */ #define ENCMODE_FORM 2 /* form-data: translate %xx, and + to space */ #define ENCMODE_MIME 3 /* MIME header: translate ?= strings */ /* * Create a parsed URL string value. This converts %xx sequences to bytes, * and ensures that the results are well-formed UTF-8 characters. */ static void new_parsed_str(VMG_ vm_val_t *ret, const char *str, size_t len, int encmode) { char *src, *dst; /* create the raw string */ ret->set_obj(CVmObjString::create(vmg_ FALSE, str, len)); CVmObjString *s = (CVmObjString *)vm_objp(vmg_ ret->val.obj); /* get the string buffer */ char *buf = s->cons_get_buf(); char *bufend = buf + len; /* * if desired, decode url-encoding: run through the string, converting * '%xx' sequences to bytes, and '+' characters to spaces */ if (encmode == ENCMODE_URL || encmode == ENCMODE_FORM) { for (src = dst = buf ; src < bufend ; ) { /* if this is a '%xx' sequence, convert it */ if (*src == '%' && src + 2 < bufend && is_xdigit(*(src+1)) && is_xdigit(*(src+2))) { /* generate the character value */ int c = (value_of_xdigit(*(src+1)) << 4) | value_of_xdigit(*(src+2)); /* skip the three-character %xx sequence */ src += 3; /* * if this is an encoded CR, and an LF follows, decode the * whole sequence to a single '\n' */ if (c == '\r' && src + 2 < bufend && memicmp(src, "%0a", 3) == 0) { /* change to '\n' */ c = '\n'; /* skip the in the source */ src += 3; } /* set the character in the output */ *dst++ = (char)(unsigned char)c; } else if (*src == '+' && (encmode == ENCMODE_FORM || encmode == ENCMODE_URL)) { /* for form data, translate + to space */ *dst++ = ' '; ++src; } else if ((*src == '\r' || *src == '\n') && encmode == ENCMODE_FORM) { /* for form data, strip out newlines */ ++src; } else { /* ordinary character - copy as-is */ *dst++ = *src++; } } /* * update the length - the string might have contracted, since * '%xx' sequences might have turned into single bytes */ len = dst - buf; bufend = dst; s->cons_set_len(len); } else if (encmode == ENCMODE_MIME) { // $$$ decode the MIME ?= encoding } /* ensure that the buffer has only well-formed UTF-8 characters */ CCharmapToUni::validate(buf, bufend - buf); } /* add a parsed query string element to a lookup table */ static void add_parsed_val(VMG_ CVmObjLookupTable *tab, const vm_val_t *key, const char *str, size_t len, int encmode) { /* stack the key for gc protection */ G_stk->push(key); /* create a parsed string value to add to the table */ vm_val_t val; new_parsed_str(vmg_ &val, str, len, encmode); G_stk->push(&val); /* add or extend the table entry */ vm_val_t oldval; const char *oldstr; if (tab->index_check(vmg_ &oldval, key) && (oldstr = oldval.get_as_string(vmg0_)) != 0) { /* * it's already in the table - make it a comma-separated list by * appending ", val" to the existing key */ /* get the parsed string we're adding */ const char *newstr = val.get_as_string(vmg0_); /* allocate the combined string as len1 + len2 + 2 (for the ", ") */ vm_val_t cval; size_t clen = vmb_get_len(oldstr) + vmb_get_len(newstr) + 2; cval.set_obj(CVmObjString::create(vmg_ FALSE, clen)); CVmObjString *cstr = (CVmObjString *)vm_objp(vmg_ cval.val.obj); /* build the new string */ t3sprintf(cstr->cons_get_buf(), clen, "%.*s, %.*s", (int)vmb_get_len(oldstr), oldstr + VMB_LEN, (int)vmb_get_len(newstr), newstr + VMB_LEN); /* use the combined string as the value */ G_stk->discard(); val = cval; G_stk->push(&val); } /* it's not in the table yet - add it */ tab->set_or_add_entry(vmg_ key, &val); /* discard the gc protection */ G_stk->discard(2); } /* add a parsed query string element under an integer key */ static void add_parsed_val(VMG_ CVmObjLookupTable *tab, int key, const char *str, size_t len, int encmode) { /* set up a vm_val_t with the integer key value */ vm_val_t keyval; keyval.set_int(key); /* add the value to the table */ add_parsed_val(vmg_ tab, &keyval, str, len, encmode); } /* add a parsed query string element under a string key */ static void add_parsed_val(VMG_ CVmObjLookupTable *tab, const char *keystr, size_t keylen, const char *str, size_t len, int encmode) { /* set up the key as a parsed string */ vm_val_t key; new_parsed_str(vmg_ &key, keystr, keylen, encmode); /* add the value */ add_parsed_val(vmg_ tab, &key, str, len, encmode); } /* * Parse query parameters. This can be used for the part of a URL query * string after the first '?', or for a message body with content-type * application/x-www-form-urlencoded, since they have identical formats. */ static void parse_query_params(VMG_ CVmObjLookupTable *tab, const char *p, int encmode) { /* parse the arguments */ while (*p != '\0') { /* the end of this argument is at the next '&' */ const char *amp = strchr(p, '&'); if (amp == 0) amp = p + strlen(p); /* find the '=', if any */ const char *eq = strchr(p, '='); if (eq == 0 || eq > amp) eq = amp; /* add the resource name under the name key */ add_parsed_val(vmg_ tab, p, eq - p, eq + 1, eq == amp ? 0 : amp - eq - 1, encmode); /* move on to the next delimiter */ p = amp; if (*p != '\0') ++p; } } /* * parse the query string into the resource name and parameter list */ int CVmObjHTTPRequest::getp_parseQuery(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the query string from the request */ TadsHttpRequest *req = get_ext()->req; const char *q = req->resource_name->get(); size_t ql = strlen(q); /* create a lookup table to hold the result; push it for gc protection */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 16, 32)); G_stk->push(retval); CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ retval->val.obj); /* * the resource name is the part of the query up to the first '?', or * the entire string if there's no '?' */ const char *qq = strchr(q, '?'); if (qq == 0) qq = q + ql; /* add the resource name under key [1] */ add_parsed_val(vmg_ tab, 1, q, qq - q, ENCMODE_URL); /* parse the query parameters, starting after the '?' */ if (*qq == '?') parse_query_params(vmg_ tab, qq + 1, ENCMODE_URL); /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * parse the query string and return a specified parametre */ int CVmObjHTTPRequest::getp_getQueryParam(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the target parameter name */ const char *paramName = G_stk->get(0)->get_as_string(vmg0_); if (paramName == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and buffer from the string */ size_t paramLen = vmb_get_len(paramName); paramName += VMB_LEN; /* retrieve the query string from the request */ TadsHttpRequest *req = get_ext()->req; const char *q = req->resource_name->get(); size_t ql = strlen(q); /* presume we won't find the parameter we're looking for */ retval->set_nil(); /* scan the parameters, if any */ for (const char *qq = strchr(q, '?') ; qq != 0 && *qq != '\0' ; ) { /* skip this separator character */ ++qq; /* find the end of this parameter */ const char *amp = strchr(qq, '&'); if (amp == 0) amp = q + ql; /* find the value portion, if present */ const char *eq = strchr(qq, '='); if (eq == 0 || eq > amp) eq = amp; /* * Compare this to the name we're looking for. Note that URLs are * either plain ASCII or UTF-8, so we can just do the comparison on * a byte-by-byte basis as our paramName string is also UTF-8. */ if ((size_t)(eq - qq) == paramLen && memcmp(qq, paramName, paramLen) == 0) { /* this is the one - create the value string */ new_parsed_str(vmg_ retval, eq + 1, eq == amp ? 0 : amp - eq - 1, ENCMODE_URL); /* done with our search */ break; } /* move on to the next separator */ qq = amp; } /* discard arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* * get the headers */ int CVmObjHTTPRequest::getp_getHeaders(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the headers from the request */ TadsHttpRequest *req = get_ext()->req; TadsHttpRequestHeader *hdrs = req->hdr_list; /* create a lookup table to hold the result; push it for gc protection */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); G_stk->push(retval); CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ retval->val.obj); /* * The first item in the list is the special "request line," with the * HTTP verb and resource name; it's not really a header, as it doesn't * have a name/value pair, so we file it under the key [1]. */ add_parsed_val(vmg_ tab, 1, hdrs->name, strlen(hdrs->name), ENCMODE_NONE); /* add the rest as key/value pairs */ for (hdrs = hdrs->nxt ; hdrs != 0 ; hdrs = hdrs->nxt) add_parsed_val(vmg_ tab, hdrs->name, strlen(hdrs->name), hdrs->value, strlen(hdrs->value), ENCMODE_NONE); /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * get an individual cookie */ int CVmObjHTTPRequest::getp_getCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the headers from the request */ TadsHttpRequest *req = get_ext()->req; TadsHttpRequestHeader *hdrs = req->hdr_list; /* get the cookie name argument */ const char *cname = G_stk->get(0)->get_as_string(vmg0_); if (cname == 0) err_throw(VMERR_BAD_TYPE_BIF); /* presume we won't find a match */ retval->set_nil(); /* scan for "Cookie" headers */ for (hdrs = hdrs->nxt ; hdrs != 0 ; hdrs = hdrs->nxt) { /* if this is a Cookie: header, process it */ if (stricmp(hdrs->name, "cookie") == 0) { /* scan the list of cookie name=value pairs */ for (const char *p = hdrs->value ; *p != '\0' ; ) { /* skip leading whitespace */ for ( ; isspace(*p) ; ++p) ; /* the cookie name ends at the '=', ';', ',', or null */ const char *name = p; for ( ; *p != '\0' && strchr("=;,", *p) == 0 ; ++p) ; size_t namelen = p - name; /* if we found the '=', scan the value */ const char *val = ""; size_t vallen = 0; if (*p == '=') { /* skip the '=' and scan for the end of the cookie */ for (val = ++p ; *p != '\0' && strchr(" \t;,", *p) == 0 ; ++p) ; vallen = p - val; } /* if this matches the name we're looking for, return it */ if (namelen == vmb_get_len(cname) && memcmp(name, cname + VMB_LEN, namelen) == 0) { /* it's a match - return the value as a string */ new_parsed_str(vmg_ retval, val, vallen, ENCMODE_NONE); /* stop looking */ goto done; } /* scan ahead to the next non-delimiter */ for ( ; *p != '\0' && strchr(" \t;,", *p) != 0 ; ++p) ; } } } done: /* done with the arguments */ G_stk->discard(argc); /* handled */ return TRUE; } /* * get the cookies */ int CVmObjHTTPRequest::getp_getCookies(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the headers from the request */ TadsHttpRequest *req = get_ext()->req; TadsHttpRequestHeader *hdrs = req->hdr_list; /* create a lookup table to hold the result; push it for gc protection */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); G_stk->push(retval); CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ retval->val.obj); /* scan for "Cookie" headers */ for (hdrs = hdrs->nxt ; hdrs != 0 ; hdrs = hdrs->nxt) { /* if this is a Cookie: header, process it */ if (stricmp(hdrs->name, "cookie") == 0) { /* scan the list of cookie name=value pairs */ for (const char *p = hdrs->value ; *p != '\0' ; ) { /* skip leading whitespace */ for ( ; isspace(*p) ; ++p) ; /* the cookie name ends at the '=', ';', ',', or null */ const char *name = p; for ( ; *p != '\0' && strchr("=;,", *p) == 0 ; ++p) ; size_t namelen = p - name; /* if we found the '=', scan the value */ const char *val = ""; size_t vallen = 0; if (*p == '=') { /* skip the '=' and scan for the end of the cookie */ for (val = ++p ; *p != '\0' && strchr(" \t;,", *p) == 0 ; ++p) ; vallen = p - val; } /* add this cookie to the table */ add_parsed_val(vmg_ tab, name, namelen, val, vallen, ENCMODE_NONE); /* scan ahead to the next non-delimiter */ for ( ; *p != '\0' && strchr(" \t;,", *p) != 0 ; ++p) ; } } } /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Find an attribute token in a content-type or similar header string. An * attribute token is separated from preceding text by a ';', and has the * form "name=value". The value can be in quotes, or can just be a token. */ static int parse_attr_token(const char *str, const char *attr_name, const char *&val, size_t &vallen) { /* assume we won't find anything */ val = 0; vallen = 0; /* note the name length */ size_t attr_name_len = strlen(attr_name); /* scan the string for attribute tokens, starting at the first ';' */ for (const char *p = strchr(str, ';') ; p != 0 ; p = strchr(p, ';')) { /* skip spaces */ for (++p ; isspace(*p) ; ++p) ; /* check to see if this is the desired attribute name */ if (memicmp(p, attr_name, attr_name_len) != 0) continue; /* skip the name and any subsequent spaces, and look for the '=' */ for (p += attr_name_len ; isspace(*p) ; ++p) ; if (*p != '=') continue; /* skip the '=' and subsequent spaces */ for (++p ; isspace(*p) ; ++p) ; /* * Okay, we're at the attribute value. If there's a quote mark, * the value is everything from here to the close quote. Otherwise * it's everything up to the next space or semicolon. */ if (*p == '"') { /* skip the open quote, and find the close quote */ for (val = ++p ; *p != '\0' && *p != '"' ; ++p) ; } else { /* it's from here to the next separator character */ val = p; for ( ; *p != '\0' && strchr(" \t;,", *p) == 0 ; ++p) ; } /* note the length, and return success */ vallen = p - val; return TRUE; } /* didn't find it - return failure */ return FALSE; } /* * Match two attribute tokens */ static int match_attrs(const char *a, size_t alen, const char *b, size_t blen) { /* if the lengths don't match, it's not a match */ if (alen != blen) return FALSE; /* if both are empty strings, it's a match */ if (alen == 0 && blen == 0) return TRUE; /* compare the strings */ return memcmp(a, b, alen) == 0; } /* * send a cookie header */ int vm_httpreq_cookie::send(TadsServerThread *t) { /* send the Set-Cookie header */ return (t->send("Set-Cookie: ") && t->send(name) && t->send("=") && t->send(val) && t->send("\r\n")); } /* * set a cookie in the reply */ int CVmObjHTTPRequest::getp_setCookie(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(2); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the cookie name and value */ const char *cname = G_stk->get(0)->get_as_string(vmg0_); const char *cval = G_stk->get(1)->get_as_string(vmg0_); /* check that we got string values */ if (cname == 0 || cval == 0) err_throw(VMERR_BAD_TYPE_BIF); /* get the name and value length and buffer */ size_t cname_len = vmb_get_len(cname), cval_len = vmb_get_len(cval); cname += VMB_LEN; cval += VMB_LEN; /* get a null-terminated copy of the value, and parse out the 'path' */ char *cbuf = lib_copy_str(cval, cval_len); const char *cpath, *cdom; size_t cpathlen, cdomlen; parse_attr_token(cbuf, "path", cpath, cpathlen); parse_attr_token(cbuf, "domain", cdom, cdomlen); /* search for an existing cookie with this name */ vm_httpreq_cookie *cookie; for (cookie = get_ext()->cookies ; cookie != 0 ; cookie = cookie->nxt) { /* check for a match */ if (strlen(cookie->name) == cname_len && memcmp(cookie->name, cname, cname_len) == 0) { /* parse the current value's path and domain attributes */ const char *vpath, *vdom; size_t vpathlen, vdomlen; parse_attr_token(cookie->val, "path", vpath, vpathlen); parse_attr_token(cookie->val, "domain", vdom, vdomlen); /* * Check to see if the path and domain match. If not, this * counts as a distinct cookie. */ if (!match_attrs(vpath, vpathlen, cpath, cpathlen) || !match_attrs(vdom, vdomlen, cdom, cdomlen)) continue; /* it's already defined - redefine it with the new value */ lib_free_str(cookie->val); cookie->val = lib_copy_str(cval, cval_len); break; } } /* done with the allocated value copy */ lib_free_str(cbuf); /* if we didn't replace an existing entry, create a new one */ if (cookie == 0) { /* create the entry */ cookie = new vm_httpreq_cookie(cname, cname_len, cval, cval_len); /* link it into our list */ cookie->nxt = get_ext()->cookies; get_ext()->cookies = cookie; } /* done with the arguments */ G_stk->discard(argc); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Find the charset property in a Content-Type header. Fills in 'cs' with * a mapper object if we find a text content type, otherwise nil. In the * nil case we set cs->val.obj = VM_INVALID_OBJ, so you can use that field * where a vm_obj_id_t is required in either case. We return the file mode * to use for a file opened on this content: VMOBJFILE_MODE_TEXT if we find * a character set, VMOBJFILE_MODE_RAW if not. */ static int get_charset_map(VMG_ const char *ct, vm_val_t *cs) { /* presume we won't create a mapper */ cs->set_nil(); cs->val.obj = VM_INVALID_OBJ; /* get the Content-Type header */ if (ct != 0 && (memcmp(ct, "text/", 5) == 0 || memcmp(ct, "application/x-www-form-urlencoded", 33) == 0)) { /* * It's a text content type, so we'll use a text mapping. Scan the * content-type string for a "charset" parameter. */ const char *cname; size_t cnamelen; if (parse_attr_token(ct, "charset", cname, cnamelen)) { /* got it - try loading the character mapper for the set name */ cs->set_obj(CVmObjCharSet::create(vmg_ FALSE, cname, cnamelen)); } /* if we didn't create a mapper, or it's not valid, use ASCII */ if (cs->typ == VM_NIL || ((CVmObjCharSet *)vm_objp(vmg_ cs->val.obj)) ->get_to_uni(vmg0_) == 0) { /* create a us-ascii mapper */ cs->set_obj(CVmObjCharSet::create(vmg_ FALSE, "us-ascii", 8)); } /* indicate that they should use text mode on the file */ return VMOBJFILE_MODE_TEXT; } else { /* it's not a text content type - use raw mode */ return VMOBJFILE_MODE_RAW; } } /* * StringRef data source. This is a simple subclass of the basic string * data source; the only specialization is that we keep a reference to the * StringRef object, and release it on close. */ class CVmStringRefSource: public CVmStringSource { public: CVmStringRefSource(StringRef *s) : CVmStringSource(s->get(), s->getlen()) { /* keep a reference on the source */ this->s = s; s->add_ref(); } CVmStringRefSource(StringRef *s, long start_ofs, long len) : CVmStringSource(s->get() + start_ofs, len) { /* keep a reference on the source */ this->s = s; s->add_ref(); } virtual CVmDataSource *clone(VMG_ const char * /*mode*/) { return new CVmStringRefSource(s, mem - s->get(), memlen); } ~CVmStringRefSource() { /* release our reference on the underlying source */ s->release_ref(); } protected: /* our underlying StringRef object */ StringRef *s; }; /* * throw a network error */ static void throw_net_err(VMG_ const char *msg, int sock_err) { /* * Determine the exception class, based on the error code: * *. - ECONNRESET -> SocketDisconnectException *. - everything else -> the base NetException */ vm_obj_id_t cl = VM_INVALID_OBJ; switch (sock_err) { case OS_ECONNRESET: case OS_ECONNABORTED: cl = G_predef->net_reset_exception; break; } /* * if we didn't pick a special class for the error, or the class we * selected wasn't imported, use the base NetException class by default */ if (cl == VM_INVALID_OBJ) cl = G_predef->net_exception; /* throw new exceptionClass(msg, errno) */ int argc = 1; if (sock_err != 0) { G_stk->push_int(vmg_ sock_err); ++argc; } G_interpreter->push_string(vmg_ msg); G_interpreter->throw_new_class(vmg_ cl, argc, msg); } /* * Seek the next multi-part boundary */ static const char *next_boundary(const char *startp, const char *endp, const char *b, size_t blen, int need_crlf) { /* scan for the next boundary */ for (const char *p = startp ; p + 2 + blen <= endp ; ++p) { /* check for a match */ if (p[0] != '-' || p[1] != '-' || memcmp(p+2, b, blen) != 0) continue; /* if we need a newline prefix, check that it's there */ if (need_crlf && (p < startp + 2 || p[-2] != '\r' || p[-1] != '\n')) continue; /* it's a match - return the part after the '--' prefix */ return p + 2; } /* didn't find it */ return 0; } /* * Parse multi-part data. */ static void parse_multipart(VMG_ CVmObjLookupTable *tab, StringRef *body, const char *ct) { /* * find the "boundary" attribute in the content-type string; if it's * not present, we can't go on, since this is required to parse the * content sections */ const char *bndry; size_t bndry_len; if (!parse_attr_token(ct, "boundary", bndry, bndry_len)) return; /* scan the sections */ const char *p = body->get(); const char *endp = p + body->getlen(); for (p = next_boundary(p, endp, bndry, bndry_len, FALSE) ; p != 0 ; ) { /* no error for the section yet */ const char *errmsg = 0; /* skip ahead to the end of the boundary string */ p += bndry_len; /* if we're at a '--', we're done */ if (*p == '-' && *(p+1) == '-') break; /* otherwise, we should be at a newline; if not, give up */ for ( ; *p == ' ' || *p == '\t' ; ++p) ; if (*p++ != '\r' || *p++ != '\n') break; /* * Parse the headers for this section. To do this, we'll need to * make a separate writable copy of the header section - scan ahead * for the double CR-LF. */ const char *hdr_start = p; if ((p = strstr(p, "\r\n\r\n")) == 0) break; /* skip the double CR-LF to get to the section body */ const char *sbody = (p += 4); /* find the boundary at the end of this section */ if ((p = next_boundary(p, endp, bndry, bndry_len, TRUE)) == 0) break; /* * Note the length of the body - if exclude. The body excludes the * CR-LF and the "--" preceding the boundary marker, so deduct four * bytes from the current position. */ long sbody_len = p - sbody - 4; /* make a copy of the header section */ StringRef *hdr_copy = new StringRef(hdr_start, sbody - 4 - hdr_start); /* parse the headers */ TadsHttpRequestHeader *hdrs = 0, *hdrs_tail = 0; TadsHttpRequestHeader::parse_headers( hdrs, hdrs_tail, TRUE, hdr_copy, 0); /* get the content-disposition and content-type */ const char *scd = hdrs->find("content-disposition"); const char *sct = hdrs->find("content-type"); const char *fldname, *filename; size_t fldname_len, filename_len; vm_val_t fldval, keyval; int has_fname; /* * Use text/plain as the default content type. Since we send text * out in utf-8 mode, assume that responses come back in utf-8 * mode. */ if (sct == 0) sct = "text/plain; charset=utf-8"; /* make sure the content-disposition is "form-data" */ if (scd == 0 || memcmp(scd, "form-data", 9) != 0) goto section_done; /* * Get the "name" attribute - this is field's NAME value. * This is the table key for this section, so if it's not set we * can't do anything with this section. */ if (!parse_attr_token(scd, "name", fldname, fldname_len)) goto section_done; /* * Get the "filename" attribute. If this is an uploaded file, this * gives the name of the client-side file being uploaded. If it's * not present, this is a data field rather than a file * upload, so the content body is simply the field value. * * If the filename is empty and the file content has zero length, * it means that the user didn't select a file in this field. In * this case, use nil in place of the FileUpload object. */ has_fname = parse_attr_token(scd, "filename", filename, filename_len); if (has_fname && filename_len != 0 && sbody_len != 0) { /* * There's a "filename" attribute, so this is a file upload. * We'll represent this as a FileUpload object, with a * string-sourced File representing the body. * * If it's a text content type, we'll open the file in Text * mode with the character mapping specifed in the content type * parameter. Otherwise, we'll open it in raw mode. Start by * checking the mode implied by the content type parameter. */ vm_val_t charset; int mode = get_charset_map(vmg_ sct, &charset); int textmode = (mode == VMOBJFILE_MODE_TEXT); /* save the character set for gc protection */ G_stk->push(&charset); /* create the memory data source for the file */ CVmStringRefSource *ds = new CVmStringRefSource( body, sbody - body->get(), sbody_len); /* create the file object */ vm_val_t file; file.set_obj(CVmObjFile::create( vmg_ FALSE, 0, textmode ? charset.val.obj : VM_INVALID_OBJ, ds, mode, VMOBJFILE_ACCESS_READ, textmode)); /* * discard the gc protection for the character set (it's safely * ensconced in the file now), and push the new file object */ G_stk->discard(); G_stk->push(&file); /* make sure the FileUpload is defined */ if (G_predef->file_upload_cl == VM_INVALID_OBJ) { errmsg = "Parsing form fields: FileUpload class is missing"; goto section_done; } /* call FileUpload(file, contentType, filename) */ G_interpreter->push_string(vmg_ filename, filename_len); G_interpreter->push_string(vmg_ sct); G_interpreter->push(&file); vm_objp(vmg_ G_predef->file_upload_cl)->create_instance( vmg_ G_predef->file_upload_cl, 0, 3); /* the new object is in R0 */ fldval = *G_interpreter->get_r0(); /* discard the earlier gc protection; push the field value */ G_stk->discard(2); G_stk->push(&fldval); } else if (has_fname) { /* * We have a filename attribute, but it's empty, and there's no * content body. Use nil as the value. */ fldval.set_nil(); G_stk->push(&fldval); } else { /* * There's no filename attribute, so this is a simple data * field, with the section content body as the field value. */ new_parsed_str(vmg_ &fldval, sbody, sbody_len, ENCMODE_NONE); G_stk->push(&fldval); } /* create the field name string as the table key */ new_parsed_str(vmg_ &keyval, fldname, fldname_len, ENCMODE_MIME); G_stk->push(&keyval); /* save the key/value pair in the table */ tab->set_or_add_entry(vmg_ &keyval, &fldval); /* discard gc protection */ G_stk->discard(2); section_done: /* free the headers */ delete hdrs; hdr_copy->release_ref(); /* if there's an error, throw it */ if (errmsg != 0) throw_net_err(vmg_ errmsg, 0); } } /* * get the posted form fields */ int CVmObjHTTPRequest::getp_getFormFields(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the headers from the request */ TadsHttpRequest *req = get_ext()->req; TadsHttpRequestHeader *hdrs = req->hdr_list; StringRef *body = req->body; /* if the body was too large, return 'overflow' */ if (req->overflow) { retval->set_obj(CVmObjString::create(vmg_ FALSE, "overflow", 8)); return TRUE; } /* if there's no message body, there are no form fields */ if (body == 0) { retval->set_nil(); return TRUE; } /* find the content type header */ const char *ct = hdrs->find("content-type"); int typ; if (ct != 0) { /* check what we have */ if (memicmp(ct, "application/x-www-form-urlencoded", 33) == 0) typ = 0; else if (memicmp(ct, "multipart/form-data", 19) == 0) typ = 1; else { /* it's not a posted format we recognize - return nil */ retval->set_nil(); return TRUE; } } /* create a lookup table to hold the result; push it for gc protection */ retval->set_obj(CVmObjLookupTable::create(vmg_ FALSE, 32, 64)); G_stk->push(retval); CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ retval->val.obj); /* parse the content type that we found */ if (typ == 0) { /* * URL-encoded form data. This is the same format as a URL query * string, so we can just apply that parser to the message body. */ parse_query_params(vmg_ tab, body->get(), ENCMODE_FORM); } else if (typ == 1) { /* * Multipart form-data. Invoke our generic multipart parser. */ parse_multipart(vmg_ tab, body, ct); } /* done with the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * get the request body */ int CVmObjHTTPRequest::getp_getClientAddress(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the request */ TadsHttpRequest *req = get_ext()->req; TadsServerThread *t = req->thread; /* set up the return list, using address information from the thread */ G_interpreter->push_int(vmg_ t->get_client_port()); G_interpreter->push_string(vmg_ t->get_client_ip()); retval->set_obj(CVmObjList::create_from_stack(vmg_ 0, 2)); /* handled */ return TRUE; } /* * get the request body */ int CVmObjHTTPRequest::getp_getBody(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* retrieve the headers from the request */ TadsHttpRequest *req = get_ext()->req; TadsHttpRequestHeader *hdrs = req->hdr_list; StringRef *body = req->body; /* if the body was too large, return 'overflow' */ if (req->overflow) { retval->set_obj(CVmObjString::create(vmg_ FALSE, "overflow", 8)); return TRUE; } /* if there's no body, return nil */ if (body == 0) { retval->set_nil(); return TRUE; } /* create a data source for the body */ CVmStringRefSource *sbody = new CVmStringRefSource(body); /* for a text content type, get the character mapper */ vm_val_t charset; int mode = get_charset_map(vmg_ hdrs->find("content-type"), &charset); G_stk->push(&charset); /* create a File object to access the body data source */ retval->set_obj(CVmObjFile::create( vmg_ FALSE, 0, charset.val.obj, sbody, mode, VMOBJFILE_ACCESS_READ, TRUE)); /* discard the gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * look up a standard status code by number */ static const char *look_up_status(int n, size_t &len) { /* message table for the standard status codes */ static struct { int n; const char *msg; } msgs[] = { { 100, "100 Continue" }, { 101, "101 Switching Protocols" }, { 102, "102 Processing" }, { 200, "200 OK" }, { 201, "201 Created" }, { 202, "202 Accepted" }, { 203, "203 Non-Authoritative Information" }, { 204, "204 No Content" }, { 205, "205 Reset Content" }, { 206, "206 Partial Content" }, { 207, "207 Multi-Status" }, { 300, "300 Multiple Choices" }, { 301, "301 Moved Permanently" }, { 302, "302 Found" }, { 303, "303 See Other" }, { 304, "304 Not Modified" }, { 305, "305 Use Proxy" }, { 306, "306 Switch Proxy" }, { 307, "307 Temporary Redirect" }, { 400, "400 Bad Request" }, { 401, "401 Unauthorized" }, { 402, "402 Payment Required" }, { 403, "403 Forbidden" }, { 404, "404 Not Found" }, { 405, "405 Method Not Allowed" }, { 406, "406 Not Acceptable" }, { 407, "407 Proxy Authentication Required" }, { 408, "408 Request Timeout" }, { 409, "409 Conflict" }, { 410, "410 Gone" }, { 411, "411 Length Required" }, { 412, "412 Precondition Failed" }, { 413, "413 Request Entity Too Large" }, { 414, "414 Request-URI Too Long" }, { 415, "415 Unsupported Media Type" }, { 416, "416 Requested Range Not Satisfiable" }, { 417, "417 Expectation Failed" }, { 418, "418 I'm a teapot" }, { 421, "421 There are too many connections from your internet address" }, { 422, "422 Unprocessable Entity" }, { 423, "423 Locked" }, { 424, "424 Failed Dependency" }, { 425, "425 Unordered Collection" }, { 426, "426 Upgrade Required" }, { 449, "449 Retry With" }, { 450, "450 Blocked by Windows Parental Controls" }, { 500, "500 Internal Server Error" }, { 501, "501 Not Implemented" }, { 502, "502 Bad Gateway" }, { 503, "503 Service Unavailable" }, { 504, "504 Gateway Timeout" }, { 505, "505 HTTP Version Not Supported" }, { 506, "506 Variant Also Negotiates" }, { 507, "507 Insufficient Storage" }, { 509, "509 Bandwidth Limit Exceeded" }, { 510, "510 Not Extended" }, { 530, "530 User access denied" } }; /* search the list */ for (int i = 0 ; i < countof(msgs) ; ++i) { /* check for a match to the given status code */ if (msgs[i].n == n) { /* it's a match - set the length and return the message */ len = strlen(msgs[i].msg); return msgs[i].msg; } } /* didn't find a match - throw an error */ err_throw(VMERR_BAD_VAL_BIF); AFTER_ERR_THROW(return 0); } /* * look up an HTTP status message by code number */ static const char *get_status_arg(VMG_ int body_status_code, int argn, int argc, size_t &len) { /* if a body status code was given, it overrides the status argument */ if (body_status_code != 0) return look_up_status(body_status_code, len); /* if the argument isn't present, use the default "200 OK" */ if (argn >= argc) { len = 6; return "200 OK"; } /* check the type of the argument value */ const vm_val_t *argp = G_stk->get(argn); const char *status; switch (argp->typ) { case VM_INT: /* it's an integer - look up the full message */ return look_up_status(argp->val.intval, len); case VM_NIL: /* nil - use the default 200 */ len = 6; return "200 OK"; default: /* it's not an integer, so it has to be a string */ if ((status = argp->get_as_string(vmg0_)) == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and return the buffer pointer */ len = vmb_get_len(status); return status + VMB_LEN; } } /* * send headers */ static void send_custom_headers(VMG_ TadsServerThread *t, const vm_val_t *headers) { /* send each header */ int cnt = headers->ll_length(vmg0_); for (int i = 1 ; i <= cnt ; ++i) { /* get this list element */ vm_val_t ele; headers->ll_index(vmg_ &ele, i); /* it has to be a string */ const char *h = ele.get_as_string(vmg0_); if (h == 0) err_throw(VMERR_STRING_VAL_REQD); /* send the header string and the CR-LF */ if (!t->send(h + VMB_LEN, vmb_get_len(h)) || !t->send("\r\n")) throw_net_err(vmg_ "HTTPRequest: error sending headers", t->last_error()); } } /* error info for HTTPReplySender::send() */ struct http_reply_err { /* OS_Exxx error number (see osifcnet.h) */ int sock_err; /* descriptive message (as a static const string - not allocated) */ const char *msg; /* set the error - returns FALSE status code for caller to return */ int set(int err, const char *msg) { this->sock_err = err; this->msg = msg; return FALSE; } int set(TadsServerThread *t, const char *msg) { return set(t->last_error(), msg); } }; /* * Body argument descriptor. This handles the various different source * types we handle for body values - strings, ByteArrays, StringBuffers. */ struct bodyArg { /* load from the stack */ bodyArg(VMG_ int argn) { /* first clear everything out */ none = FALSE; str = 0; stralo = FALSE; strbuf = 0; bytarr = 0; len = 0; status_code = 0; init_err = 0; datasource = 0; gv = 0; /* get the argument value pointer out of the stack */ srcval = *G_stk->get(argn); /* try the various types */ if ((str = srcval.get_as_string(vmg0_)) != 0) { /* it's a string - get the length and buffer pointer */ len = vmb_get_len(str); str += VMB_LEN; } else if (srcval.typ == VM_OBJ && CVmObjByteArray::is_byte_array(vmg_ srcval.val.obj)) { /* it's a byte array */ bytarr = (CVmObjByteArray *)vm_objp(vmg_ srcval.val.obj); len = bytarr->get_element_count(); } else if (srcval.typ == VM_OBJ && CVmObjStringBuffer::is_string_buffer_obj( vmg_ srcval.val.obj)) { /* it's a string buffer object */ strbuf = (CVmObjStringBuffer *)vm_objp(vmg_ srcval.val.obj); len = strbuf->utf8_length(); } else if (srcval.typ == VM_OBJ && CVmObjFile::is_file_obj(vmg_ srcval.val.obj)) { /* it's a file object - cast it */ file = (CVmObjFile *)vm_objp(vmg_ srcval.val.obj); /* note the size */ len = file->get_file_size(vmg0_); /* * Get the data source and file spec. Use the file spec as the * source object, not the File itself; it's the spec that we * want to keep around, since we're directly accessing a data * source based on the spec rather than the File. */ datasource = file->get_datasource(); file->get_filespec(&srcval); /* it's an error if the file isn't open or valid */ if (len < 0) init_err = VMERR_READ_FILE; } else if (srcval.typ == VM_INT) { /* * Integer value - treat this as an HTTP status code. The * content body is a generated HTML page for the status code. */ status_code = (int)srcval.val.intval; /* generate the HTML page */ size_t len; const char *msg = look_up_status(status_code, len); /* make sure it's a valid status code */ if (len == 0) err_throw(VMERR_BAD_VAL_BIF); /* generate the page */ str = t3sprintf_alloc("%s" "

%s

", msg, strchr(msg, ' ') + 1); stralo = TRUE; } else if (srcval.typ == VM_NIL) { /* no content body */ none = TRUE; } else { /* it's a type we can't handle */ init_err = VMERR_BAD_TYPE_BIF; } } ~bodyArg() { /* free the string, if we allocated it */ if (stralo) lib_free_str((char *)str); } /* is this a text resource? */ int is_text(VMG0_) { /* check the type */ if (str != 0 || strbuf != 0) { /* strings and string buffers are always sent as text */ return TRUE; } else if (bytarr != 0) { /* byte arrays are always sent as binary */ return FALSE; } else if (file != 0) { /* files are sent according to their mode */ return file->get_file_mode(vmg0_) == VMOBJFILE_MODE_TEXT; } else { /* there shouldn't be anything else, but just in case... */ return FALSE; } } /* * prepare for async use */ void init_async(VMG0_) { /* if we have a data source, clone it */ if (datasource != 0) datasource = datasource->clone(vmg_ "rb"); /* * If we have a string buffer or byte array, make a private copy. * These objects aren't designed to be thread-safe, so we can't * access them from the background thread. */ if (strbuf != 0 || bytarr != 0) { /* allocate the string buffer */ char *buf = lib_alloc_str(len); /* extract the contents into the buffer */ int32_t ofs = 0; extract(buf, ofs, len); /* use this as the string buffer */ str = buf; stralo = TRUE; /* forget the original object */ strbuf = 0; bytarr = 0; /* we don't even need gc protection for the original any more */ srcval.set_nil(); } /* * if the source value is an object, create a VM global for gc * protection as long as the background thread is running */ if (srcval.typ == VM_OBJ) { gv = G_obj_table->create_global_var(); gv->val = srcval; } } /* clean up from async use */ void term_async(VMG0_) { /* delete our data source */ if (datasource != 0) delete datasource; /* delete our VM global */ if (gv != 0) G_obj_table->delete_global_var(gv); } /* * Send the body data */ int send(TadsServerThread *t, http_reply_err *err) { /* send the data according to the source type */ if (str != 0) { /* send the string's contents */ if (!t->send(str, (size_t)len)) return err->set(t, "error sending content body"); } else if (bytarr != 0 || strbuf != 0) { /* send the byte array or string buffer contents */ char buf[1024]; int32_t ofs = 0; for (;;) { /* get the next chunk */ int32_t len = sizeof(buf); const char *p = extract(buf, ofs, len); /* if we're out of material, we're done */ if (len == 0) break; /* send this chunk */ if (!t->send(p, (size_t)len)) return err->set(t, "error sending content body"); } } else if (datasource != 0) { /* seek to the start of the file */ datasource->seek(0, OSFSK_SET); /* copy the file to the socket */ for (;;) { /* get the next chunk */ char buf[1024]; int32_t len = sizeof(buf); len = datasource->readc(buf, len); /* if we're out of material, we're done */ if (len == 0) break; /* send this chunk */ if (!t->send(buf, (size_t)len)) return err->set(t, "error sending content body"); } } /* success */ return TRUE; } /* * Extract a portion of the contents. If possible, we'll simply return * a direct pointer to the buffer containing the data; if this isn't * possible, we'll copy the data to the caller's buffer. * * On input, 'ofs' is the starting offset of the extraction. The * meaning varies according to the underlying data type, so this should * always be either 0 (for the start of the string) or the output value * of a previous call to this routine. 'ofs' is an in-out variable: on * return, we've updated it to reflect the starting offset of the next * character after the chunk we extract here. * * 'len' is also an in-out variable. On input, this is the size in * bytes of the buffer. On output, it reflects the actual number of * bytes copied to the buffer. This might be smaller than the * requested size (never larger), because (a) we won't copy past the * end of the data source, and (b) some data sources will stop short of * the end of the buffer if filling the buffer exactly would require * copying a fractional utf-8 character. Note, however, that we make * no guarantee that a fractional character won't be copied: some data * sources won't do this, but others will. */ const char *extract(char *buf, int32_t &ofs, int32_t &len) { /* assume we'll have to copy into the caller's buffer */ const char *ret = buf; /* determine what to do based on the source type */ if (str != 0) { /* limit the length to the actual source length */ if (len > this->len) len = this->len; /* get a pointer to the requested substring */ ret = str + ofs; /* move the offset past the requested portion */ ofs += len; } else if (strbuf != 0) { /* * for a string buffer, we have to translate to utf8 - this * updates 'idx' and 'len' per our interface */ strbuf->to_utf8(buf, ofs, len); } else if (bytarr != 0) { /* * It's a byte array - copy bytes to the caller's buffer. Note * that we use 0-based offsets, and the byte array uses 1-based * offsets, so adjust accordingly. */ len = bytarr->copy_to_buf((unsigned char *)buf, ofs + 1, len); /* adjust the offset for the actual copied size */ ofs += len; } else if (file != 0) { /* * It's a File object - read bytes from the file into the * caller's buffer. */ datasource->seek(ofs, OSFSK_SET); len = datasource->readc(buf, len); /* adjust the offset for the actual copied size */ ofs += len; } else { /* didn't read anything */ len = 0; } /* return the result pointer */ return ret; } /* true -> no content body */ int none; /* construction error code, if applicable */ int init_err; /* if it's a string, the string */ const char *str; /* flag: we allocated the string */ int stralo; /* if it's a string buffer, the string buffer */ CVmObjStringBuffer *strbuf; /* if it's a byte array, the byte array */ CVmObjByteArray *bytarr; /* if it's a File object, the File object and data source */ CVmObjFile *file; CVmDataSource *datasource; /* source value */ vm_val_t srcval; /* if it's an HTTP status code, the status code; otherwise 0 */ int status_code; /* length in bytes of the underlying data source */ int32_t len; /* VM global, for async use */ vm_globalvar_t *gv; }; /* * Compare two strings, skipping spaces in the source string. */ static int eq_skip_sp(const char *src, size_t len, const char *ref) { /* skip leading spaces */ utf8_ptr srcp((char *)src); for ( ; len != 0 && is_space(srcp.getch()) ; srcp.inc(&len)) ; /* keep going until we run out of one string or the other */ for (utf8_ptr srcp((char *)src) ; len != 0 && *ref != '\0' ; ++ref) { /* * if this source character is a space, match any run of * whitespace; otherwise we must match exactly */ wchar_t c = to_lower(srcp.getch()); if (*ref == ' ' && is_space(c)) { /* * we have a whitespace match - skip any additional whitespace * in the source string */ for (srcp.inc(&len) ; len != 0 && is_space(srcp.getch()) ; srcp.inc(&len)) ; } else if (*ref == c) { /* we have a match - skip one source character */ srcp.inc(&len); } else { /* this character doesn't match, so the whole match fails */ return FALSE; } } /* * if we ran out of the reference string, we have a match; otherwise we * must have run out of the source string first, in which case we don't * match because we have additional unmatched reference characters */ return (*ref == '\0'); } /* * send the cookie headers */ static void send_cookies(VMG_ TadsServerThread *t, vm_httpreq_cookie *c) { /* send each cookie in the list */ for ( ; c != 0 ; c = c->nxt) { if (!c->send(t)) { /* failed - throw an error */ throw_net_err(vmg_ "error sending cookies", t->last_error()); } } } /* * send a reply */ int CVmObjHTTPRequest::getp_sendReply( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { return common_sendReply(vmg_ self, retval, oargc, TRUE); } /* * send a reply asynchronously */ int CVmObjHTTPRequest::getp_sendReplyAsync( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { return common_sendReply(vmg_ self, retval, oargc, FALSE); } /* reply sender thread */ class HTTPReplySenderThread: public OS_Thread { public: HTTPReplySenderThread(class HTTPReplySender *sender); ~HTTPReplySenderThread(); virtual void thread_main(); class HTTPReplySender *sender; }; /* custom header list */ struct http_header { static http_header *create(VMG_ const vm_val_t *lstval) { /* start with an empty list */ http_header *head = 0, **tailp = &head; /* parse the value list */ int cnt = lstval->ll_length(vmg0_); for (int i = 1 ; i <= cnt ; ++i) { /* get this list element */ vm_val_t ele; lstval->ll_index(vmg_ &ele, i); /* it has to be a string */ const char *h = ele.get_as_string(vmg0_); if (h == 0) err_throw(VMERR_STRING_VAL_REQD); /* add a new header */ *tailp = new http_header(h + VMB_LEN, vmb_get_len(h)); tailp = &(*tailp)->nxt; } /* return the list head */ return head; } http_header(const char *str, size_t len) { /* allocate the string, with two extra bytes for the CR-LF */ this->len = len + 2; this->str = lib_alloc_str(this->len); /* copy the string */ memcpy(this->str, str, len); /* add the CR-LF */ this->str[len] = '\r'; this->str[len+1] = '\n'; /* we're not in a list yet */ nxt = 0; } ~http_header() { lib_free_str(str); if (nxt != 0) delete nxt; } /* string and length */ char *str; size_t len; /* next in list */ http_header *nxt; }; /* * Asynchronous HTTP reply result event */ class TadsHttpReplyResult: public TadsEventMessage { public: TadsHttpReplyResult(VMG_ TadsHttpRequest *req, vm_globalvar_t *reqvar, bodyArg *body, int sock_err, const char *msg) : TadsEventMessage(0) { /* save the VM globals for use in the main thread */ this->vmg = VMGLOB_ADDR; /* remember our parameters */ this->req = req; this->reqvar = reqvar; this->body = body; this->sock_err = sock_err; this->msg = lib_copy_str(msg); } ~TadsHttpReplyResult() { /* establish global variable access */ VMGLOB_PTR(vmg); /* done with the request object */ if (req != 0) req->release_ref(); /* done with the request object ID global */ if (reqvar != 0) G_obj_table->delete_global_var(reqvar); /* done with 'body' - clean up from async mode and delete it */ if (body != 0) { body->term_async(vmg0_); delete body; } /* free the message text */ lib_free_str(msg); } /* prepare a bytecode event object when we're read from the queue */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type) { /* push the socket error */ G_stk->push()->set_int(sock_err); /* push the result message */ G_stk->push_string(vmg_ msg, msg != 0 ? strlen(msg) : 0); /* push the request object ID */ if (reqvar != 0) G_stk->push(&reqvar->val); else G_stk->push()->set_nil(); /* relay our added argument count to the caller */ *argc = 3; /* set the event type to NetEvReplyDone (6 - see include/tadsnet.h) */ *evt_type = 6; /* the event subclass is NetReplyDoneEvent */ return G_predef->net_reply_done_event; } /* VM globals, for use in main thread only */ vm_globals *vmg; /* the request object for the request we replied to */ TadsHttpRequest *req; /* VM global for the request object value */ vm_globalvar_t *reqvar; /* the content body of the reply */ bodyArg *body; /* socket error from reply data transfer */ int sock_err; /* result message text */ char *msg; }; /* * Reply sender object. This encapsulates a reply's data and the code to * send the reply. This can be used to send a reply synchronously or * asynchronously. */ class HTTPReplySender: public CVmRefCntObj { friend class HTTPReplySenderThread; public: HTTPReplySender() { vmg = 0; queue = 0; reqid = VM_INVALID_OBJ; req = 0; reqvar = 0; status = 0; status_len = 0; headers = 0; body = 0; cont_type = 0; cont_type_len = 0; cookies = 0; async = FALSE; } void set_params(VMG_ vm_obj_id_t reqid, TadsHttpRequest *req, const char *status, size_t status_len, const vm_val_t *headers, bodyArg *body, const char *cont_type, size_t cont_type_len, vm_httpreq_cookie *cookies) { /* * Save the values. For reference types (string buffers, etc), * just keep pointers to the originals; for synchronous replies, * the caller will block on the send, so these will stay around for * the send. For asynchronous (threaded) replies, we'll make * private copies when we start the new thread. */ this->reqid = reqid; this->req = req; this->status = status; this->status_len = status_len; this->headers = 0; this->body = body; this->cont_type = cont_type; this->cont_type_len = cont_type_len; this->cookies = cookies; /* parse the headers list */ if (headers != 0) this->headers = http_header::create(vmg_ headers); /* add a reference on the request object */ req->add_ref(); } ~HTTPReplySender() { /* if we're in async mode, delete our private parameter data */ if (async) { lib_free_str((char *)status); lib_free_str((char *)cont_type); if (cookies != 0) delete cookies; } /* delete the headers (we always copy these) */ if (headers != 0) delete headers; /* release the net event queue */ if (queue != 0) queue->release_ref(); /* done with the request object and VM global */ if (req != 0) req->release_ref(); if (reqvar != 0) { VMGLOB_PTR(vmg); G_obj_table->delete_global_var(reqvar); } /* done with the body object */ if (body != 0) delete body; } /* * send the reply; returns true on success, false on failure (and fills * in '*err' with error information on failure) */ int send(http_reply_err *err) { /* presume success */ int ok = TRUE; /* * We've gathered all of the information, so send the reply. Start * with the status code. */ TadsServerThread *t = req->thread; if (!t->send("HTTP/1.1 ") || !t->send(status, status_len) || !t->send("\r\n")) ok = err->set(t, "error sending status"); /* send the custom headers, if any */ if (ok && headers != 0) { for (http_header *h = headers ; ok && h != 0 ; h = h->nxt) { if (!t->send(h->str, h->len)) ok = err->set(t, "error sending headers"); } } /* if there's a body, send Content-Type and Content-Length headers */ if (ok && !body->none) { /* if it's a "text/" type, add a utf-8 "charset" parameter */ const char *charset = ""; if (cont_type_len > 5 && memicmp(cont_type, "text/", 5) == 0) charset = "; charset=utf-8"; /* send the standard headers: Content-Type, Content-Length */ char hbuf[256]; t3sprintf(hbuf, sizeof(hbuf), "Content-Type: %.*s%s\r\n" "Content-Length: %lu\r\n", (int)cont_type_len, cont_type, charset, (ulong)body->len); if (!t->send(hbuf)) ok = err->set(t, "error sending headers"); } /* send the cookies */ for (vm_httpreq_cookie *c = cookies ; ok && c != 0 ; c = c->nxt) { if (!c->send(t)) ok = err->set(t, "error sending cookies"); } /* send the blank line at the end of the headers */ if (ok && !t->send("\r\n")) ok = err->set(t, "error sending headers"); /* send the reply body */ if (ok && !body->send(t, err)) ok = FALSE; /* mark the request as completed */ req->complete(); /* return the status indication */ return ok; } /* reply error codes */ static const int ErrThread = -8; /* thread launch failed */ /* start a thread to send the reply asynchronously */ void start_thread(VMG0_) { /* save the globals for use in the 'done' event */ vmg = VMGLOB_ADDR; /* remember the net event queue, and record our reference on it */ if ((queue = G_net_queue) != 0) queue->add_ref(); /* * remove the original parametres from our member variables before * we set async mode, in case we throw an error in the course of * making copies - we wouldn't want to delete the originals, as * those are owned by the main thread */ const char *ostatus = status; status = 0; const char *ocont_type = cont_type; cont_type = 0; vm_httpreq_cookie *ocookies = cookies; cookies = 0; /* note that we're now in async mode */ async = TRUE; /* install copies in our member variables */ status = lib_copy_str(ostatus, status_len); cont_type = lib_copy_str(ocont_type, cont_type_len); if (ocookies != 0) cookies = ocookies->clone(); /* initialize async mode in the body object */ body->init_async(vmg0_); /* * create a global variable for the request object ID, to keep it * around until the request is completed */ reqvar = G_obj_table->create_global_var(); reqvar->val.set_obj(reqid); /* create and launch a thread to handle the data transfer */ HTTPReplySenderThread *t = new HTTPReplySenderThread(this); if (!t->launch()) { /* * the thread failed - post a 'finished' event with an error, * since the thread can't do this on its own if it never runs */ post_done_event(0, "unable to launch thread"); } /* we're done with the thread */ t->release_ref(); } protected: /* handle the main thread action */ void thread_main() { /* send the reply */ http_reply_err err; if (send(&err)) { /* post the successful 'finished' event */ post_done_event(0, 0); } else { /* post the error */ post_done_event(err.sock_err, err.msg); } } /* post the "finished" event for an asynchronous reply */ void post_done_event(int sock_err, const char *msg) { /* establish the global context */ VMGLOB_PTR(vmg); /* create the result event */ TadsHttpReplyResult *evt = new TadsHttpReplyResult( vmg_ req, reqvar, body, sock_err, msg); /* queue the event (this transfers ownership to the queue) */ queue->post(evt); /* forget the items that the event object took ownership of */ req = 0; body = 0; reqvar = 0; } /* * VM globals (we need these for the "finished" event object) - note * that the background thread isn't allowed to access VM globals since * they're not thread safe and thus are restricted to the main thread */ vm_globals *vmg; /* network event queue */ TadsMessageQueue *queue; /* request object ID (an HTTPRequest object) */ vm_obj_id_t reqid; vm_globalvar_t *reqvar; /* the underlying network request object */ TadsHttpRequest *req; /* status string */ const char *status; size_t status_len; /* headers */ http_header *headers; /* content body */ bodyArg *body; /* content type string */ const char *cont_type; size_t cont_type_len; /* cookie list head */ vm_httpreq_cookie *cookies; /* * Are we sending the reply asynchronously? If this is true, the * parameters that point to memory buffers are private copies. */ int async; }; HTTPReplySenderThread::HTTPReplySenderThread(class HTTPReplySender *sender) { (this->sender = sender)->add_ref(); } HTTPReplySenderThread::~HTTPReplySenderThread() { sender->release_ref(); } void HTTPReplySenderThread::thread_main() { sender->thread_main(); } /* * Common handler for sendReply and sendReplyAsync. */ int CVmObjHTTPRequest::common_sendReply( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc, int wait) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 3); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the request */ TadsHttpRequest *req = get_ext()->req; /* if the request is already completed, this is an error */ if (req->completed) throw_net_err(vmg_ "request already completed", 0); /* get the 'body' argument (leave it on the stack for gc protection) */ bodyArg *body = new bodyArg(vmg_ 0); /* no reply sender yet */ HTTPReplySender *sender = 0; err_try { /* get the content type, if present */ const char *cont_type = 0; size_t cont_type_len = 0; if (body->init_err != 0) { err_throw(body->init_err); } else if (body->status_code != 0) { /* we're using a generated HTML reply, so it's always text/html */ cont_type = "text/html"; cont_type_len = 9; } else if (body->none) { /* no body -> no content type */ cont_type = 0; cont_type_len = 0; } else if (argc >= 2 && G_stk->get(1)->typ != VM_NIL) { /* retrieve the content type - this must be a string */ cont_type = G_stk->get(1)->get_as_string(vmg0_); if (cont_type == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and buffer pointer */ cont_type_len = vmb_get_len(cont_type); cont_type += VMB_LEN; } else { /* * There's no content type argument, so we need to infer the * type from the 'body' argument. First, extract the initial * section of the content to check for common signatures. */ char buf[512]; int32_t ofs = 0, len = sizeof(buf); const char *bufp = body->extract(buf, ofs, len); /* check to see if the body is text or binary */ if (body->is_text(vmg0_)) { /* * it's text data, so check for tell-tale signs of HTML and * XML, after skipping leading whitespace and HTML-style * comments */ utf8_ptr p((char *)bufp); size_t rem = (size_t)len; while (rem != 0) { /* if we're at a comment starter, skip the comment */ if (rem >= 4 && memcmp(p.getptr(), "", 3) == 0) { /* skip it and stop looking */ p.inc(&rem); p.inc(&rem); p.inc(&rem); break; } } /* continue scanning */ continue; } /* if we're at any whitespace character, just skip it */ if (t3_is_whitespace(p.getch())) { p.inc(&rem); continue; } /* otherwise stop scanning */ break; } /* check for 5 && memicmp(p.getptr(), " 5 && memicmp(p.getptr(), " 10 && (unsigned char)bufp[0] == 0xff && (unsigned char)bufp[1] == 0xd8 && memcmp(bufp+6, "JFIF", 4) == 0) { /* it's a JPEG image */ cont_type = "image/jpeg"; } else if (len > 6 && (memcmp(bufp, "GIF87a", 6) == 0 || memcmp(bufp, "GIF89a", 6) == 0)) { /* it's a GIF image */ cont_type = "image/gif"; } else if (len > 6 && memcmp(bufp, "\211PNG\r\n\032\n", 6) == 0) { /* it's a PNG image */ cont_type = "image/png"; } else if (len > 3 && memcmp(bufp, "ID3", 3) == 0) { /* MP3 file */ cont_type = "audio/mpeg"; } else if (len > 14 && memcmp( bufp, "OggS\000\002\000\000\000\000\000\000\000\000", 14) == 0) { /* Ogg Vorbis audio */ cont_type = "application/ogg"; } else if (len > 4 && memcmp(bufp, "MThD", 4) == 0) { /* MIDI */ cont_type = "audio/midi"; } else if (len > 8 && (memcmp(bufp, "FWS", 3) == 0 || memcmp(bufp, "CWS", 3) == 0)) { /* shockwave flash */ cont_type = "application/x-shockwave-flash"; } else { /* we don't recognize it; use generic binary */ cont_type = "application/octet-stream"; } } /* get the length of the content type string we selected */ cont_type_len = strlen(cont_type); } /* get the result code, if present */ size_t status_len; const char *status = get_status_arg(vmg_ body->status_code, 2, argc, status_len); /* get the headers argument */ vm_val_t *headers = 0; if (argc >= 4 && G_stk->get(3)->typ != VM_NIL) { /* get the headers value */ headers = G_stk->get(3); /* make sure it's a list */ if (!headers->is_listlike(vmg0_)) err_throw(VMERR_LIST_VAL_REQD); } /* set up a reply sender object */ sender = new HTTPReplySender(); sender->set_params( vmg_ self, req, status, status_len, headers, body, cont_type, cont_type_len, get_ext()->cookies); /* the sender now owns our 'body' object */ body = 0; /* send the reply, either directly or in a new thread */ if (wait) { /* waiting for completion - send in the current thread */ http_reply_err err; if (!sender->send(&err)) throw_net_err(vmg_ err.msg, err.sock_err); } else { /* sending asynchronously - send in a new thread */ sender->start_thread(vmg0_); } } err_finally { /* done with the body */ if (body != 0) delete body; /* done with the reply sender */ if (sender != 0) sender->release_ref(); } err_end; /* discard arguments */ G_stk->discard(argc); /* no result */ retval->set_nil(); /* handled */ return TRUE; } /* * start a chunked reply */ int CVmObjHTTPRequest::getp_startChunkedReply( VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1, 3); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the request */ TadsHttpRequest *req = get_ext()->req; /* if the request is already completed, this is an error */ if (req->completed) throw_net_err(vmg_ "request already completed", 0); /* get the content type argument */ const char *cont_type = G_stk->get(0)->get_as_string(vmg0_); if (cont_type == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and buffer pointer */ size_t cont_type_len = vmb_get_len(cont_type); cont_type += VMB_LEN; /* get the result code, if present */ size_t status_len; const char *status = get_status_arg(vmg_ 0, 1, argc, status_len); /* get the headers argument */ const vm_val_t *headers = 0; if (argc >= 3) { /* get the headers value */ headers = G_stk->get(2); /* make sure it's a list */ if (!headers->is_listlike(vmg0_)) err_throw(VMERR_LIST_VAL_REQD); } /* * Send the headers, starting with the status code */ TadsServerThread *t = req->thread; if (!t->send("HTTP/1.1 ") || !t->send(status, status_len) || !t->send("\r\n")) throw_net_err(vmg_ "error sending status", t->last_error()); /* send the custom headers, if any */ if (headers != 0) send_custom_headers(vmg_ t, headers); /* send the cookies, if any */ send_cookies(vmg_ t, get_ext()->cookies); /* send the standard headers: Content-Type, Transfer-Encoding */ char hbuf[256]; t3sprintf(hbuf, sizeof(hbuf), "Content-Type: %.*s\r\n" "Transfer-Encoding: chunked\r\n" "\r\n", (int)cont_type_len, cont_type); if (!t->send(hbuf)) throw_net_err(vmg_ "error sending headers", t->last_error()); /* discard arguments */ G_stk->discard(argc); /* no result */ retval->set_nil(); /* handled */ return TRUE; } /* * send a piece of a chunked reply */ int CVmObjHTTPRequest::getp_sendReplyChunk(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the request and thread */ TadsHttpRequest *req = get_ext()->req; TadsServerThread *t = req->thread; /* if the request is already completed, this is an error */ if (req->completed) throw_net_err(vmg_ "request already completed", 0); /* retrieve the 'body' argument */ bodyArg *body = new bodyArg(vmg_ 0); err_try { /* make sure we initialized the body correctly */ if (body->init_err != 0) err_throw(body->init_err); /* send the length prefix */ char lbuf[20]; t3sprintf(lbuf, sizeof(lbuf), "%lx\r\n", (long)body->len); if (!t->send(lbuf)) throw_net_err(vmg_ "error sending length prefix", t->last_error()); /* send the body */ http_reply_err err; if (!body->send(t, &err)) throw_net_err(vmg_ err.msg, err.sock_err); /* send the CR-LF suffix */ if (!t->send("\r\n")) throw_net_err(vmg_ "error sending suffix", t->last_error()); } err_finally { /* done with the body */ delete body; } err_end; /* discard arguments */ G_stk->discard(argc); /* no result */ retval->set_nil(); /* handled */ return TRUE; } /* * finish a chunked reply */ int CVmObjHTTPRequest::getp_endChunkedReply(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *oargc) { uint argc = (oargc != 0 ? *oargc : 0); static CVmNativeCodeDesc desc(0, 1); if (get_prop_check_argc(retval, oargc, &desc)) return TRUE; /* get the request and the thread */ TadsHttpRequest *req = get_ext()->req; TadsServerThread *t = req->thread; /* if the request is already completed, this is an error */ if (req->completed) throw_net_err(vmg_ "request already completed", 0); /* send the "0" length prefix to indicate we're done */ if (!t->send("0\r\n")) throw_net_err(vmg_ "error sending end-of-stream", t->last_error()); /* get the headers argument */ const vm_val_t *headers = 0; if (argc >= 1) { /* get the headers value */ headers = G_stk->get(0); /* make sure it's a list */ if (!headers->is_listlike(vmg0_)) err_throw(VMERR_LIST_VAL_REQD); /* send the headers */ send_custom_headers(vmg_ t, headers); } /* send the closing CR-LF */ if (!t->send("\r\n")) throw_net_err(vmg_ "error sending suffix", t->last_error()); /* mark the request as completed */ req->complete(); /* discard arguments */ G_stk->discard(argc); /* no result */ retval->set_nil(); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * HTTP request object */ /* * Prepare the event object. This creates an HTTPRequest object (the * intrinsic class representing an incoming HTTP request), and sets up a * NetRequestEvent (the byte-code object representing a request) to wrap * it. */ vm_obj_id_t TadsHttpRequest::prep_event_obj(VMG_ int *argc, int *evt_type) { /* get the HTTPServer object that created the listener thread */ vm_obj_id_t srv_obj = thread->get_listener()->get_server_obj(); /* create and push the HTTPRequest object */ G_interpreter->push_obj( vmg_ CVmObjHTTPRequest::create(vmg_ FALSE, this, srv_obj)); /* we added one argument to the constructor */ *argc = 1; /* the event type is NetEvRequest (1 - see include/tadsnet.h) */ *evt_type = 1; /* the event subclass is NetRequestEvent */ return G_predef->net_request_event; } frobtads-1.2.3/tads3/vmmeta.cpp0000644000175000001440000005257211745521623015540 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/VMMETA.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $"; #endif /* * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmeta.cpp - metaclass dependency table Function Notes Modified 12/01/98 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmtype.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmmeta.h" #include "vmobj.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmintcls.h" /* ------------------------------------------------------------------------ */ /* * Create the metaclass dependency table with a given number of initial * entries */ CVmMetaTable::CVmMetaTable(size_t init_entries) { vm_meta_reg_t *entry; uint cnt; uint i; /* allocate space for our entries */ if (init_entries != 0) { /* allocate the space */ table_ = (vm_meta_entry_t *) t3malloc(init_entries * sizeof(table_[0])); } else { /* we have no entries */ table_ = 0; } /* no entries are defined yet */ count_ = 0; /* remember the allocation size */ alloc_ = init_entries; /* count the number of registered metaclasses */ for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ; ++entry, ++cnt) ; /* * allocate the reverse dependency table -- this table lets us get * the dependency index of a metaclass given its registration table * index */ reverse_map_ = (int *)t3malloc(cnt * sizeof(reverse_map_[0])); /* * set each element of the table to -1, since we have no dependency * table entries yet */ for (i = 0 ; i < cnt ; ++i) reverse_map_[i] = -1; } /* ------------------------------------------------------------------------ */ /* * Delete the table */ CVmMetaTable::~CVmMetaTable() { /* clear the table */ clear(); /* free the table, if we ever allocated one */ if (table_ != 0) t3free(table_); /* free the reverse map */ t3free(reverse_map_); } /* ------------------------------------------------------------------------ */ /* * Clear all entries from the table */ void CVmMetaTable::clear() { size_t i; /* delete all of the property tables in the entries */ for (i = 0 ; i < count_ ; ++i) table_[i].release_mem(); /* * Reset the entry counter. Note that this doesn't affect any * allocation; we keep a separate count of the number of table slots * we have allocated. Table slots don't have any additional * associated memory, so we don't need to worry about cleaning * anything up at this point. */ count_ = 0; } /* ------------------------------------------------------------------------ */ /* * Ensure that we have space for a given number of entries */ void CVmMetaTable::ensure_space(size_t entries, size_t increment) { /* if we don't have enough space, allocate more */ if (entries >= alloc_) { size_t new_size; /* increase the allocation size by the given increment */ alloc_ += increment; /* if it's still too small, bump it up to the required size */ if (alloc_ < entries) alloc_ = entries; /* compute the new size */ new_size = alloc_ * sizeof(table_[0]); /* * if we have a table already, reallocate it at the larger size; * otherwise, allocate a new table */ if (table_ != 0) table_ = (vm_meta_entry_t *)t3realloc(table_, new_size); else table_ = (vm_meta_entry_t *)t3malloc(new_size); } } /* ------------------------------------------------------------------------ */ /* * Add an entry to the table */ void CVmMetaTable::add_entry(const char *metaclass_id, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { vm_meta_reg_t *entry; uint idx; const char *vsn; size_t name_len; /* find the version suffix in the metaclass name, if any */ vsn = lib_find_vsn_suffix(metaclass_id, '/', "000000", &name_len); /* ensure we have space for one more entry */ ensure_space(count_ + 1, 5); /* look up the metaclass by name */ for (idx = 0, entry = G_meta_reg_table ; entry->meta != 0 ; ++entry, ++idx) { const char *entry_vsn; size_t entry_name_len; /* find the version number in this entry */ entry_vsn = lib_find_vsn_suffix((*entry->meta)->get_meta_name(), '/', "000000", &entry_name_len); /* see if this is a match */ if (name_len == entry_name_len && memcmp(metaclass_id, (*entry->meta)->get_meta_name(), name_len) == 0) { /* * make sure the version provided in the VM is at least as * high as the requested version */ if (strcmp(vsn, entry_vsn) > 0) { /* * throw the error; this is a version mismatch error, so * flag it as a version error and include the metaclass * dependency information */ err_throw_a(VMERR_METACLASS_TOO_OLD, 4, ERR_TYPE_TEXTCHAR, metaclass_id, ERR_TYPE_TEXTCHAR, entry_vsn, ERR_TYPE_METACLASS, metaclass_id, ERR_TYPE_VERSION_FLAG); } /* add this entry */ add_entry(metaclass_id, idx, func_cnt, min_prop, max_prop); /* we're done */ return; } } /* * We didn't find it. This is probably an out-of-date VM issue, so * flag it as a version error, and include the metaclass dependency * information. */ err_throw_a(VMERR_UNKNOWN_METACLASS, 3, ERR_TYPE_TEXTCHAR, metaclass_id, ERR_TYPE_METACLASS, metaclass_id, ERR_TYPE_VERSION_FLAG); } /* ------------------------------------------------------------------------ */ /* * Add an entry to the table for a given registration table index */ void CVmMetaTable::add_entry(const char *metaclass_id, uint idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { /* get the registration table entry from the index */ vm_meta_reg_t *entry = &G_meta_reg_table[idx]; /* remember the defining class object */ table_[count_].meta_ = *entry->meta; /* save the name */ table_[count_].set_meta_name(metaclass_id); /* calculate the number of entries in the table */ size_t prop_xlat_cnt; if (min_prop == VM_INVALID_PROP || max_prop == VM_INVALID_PROP) prop_xlat_cnt = 0; else prop_xlat_cnt = max_prop - min_prop + 1; /* set the property count */ table_[count_].min_prop_ = min_prop; table_[count_].prop_xlat_cnt_ = prop_xlat_cnt; /* set the function count */ table_[count_].func_xlat_cnt_ = func_cnt; /* we don't know the intrinsic class's representative object yet */ table_[count_].class_obj_ = VM_INVALID_OBJ; /* allocate space for the property table, if we have any translations */ if (prop_xlat_cnt != 0) { size_t siz; /* calculate the table size */ siz = prop_xlat_cnt * sizeof(table_[count_].prop_xlat_[0]); /* allocate the table */ table_[count_].prop_xlat_ = (unsigned short *)t3malloc(siz); /* * initialize each entry in the table to zero - this will ensure * that each entry that is never set to a valid function index * will properly yield function index zero, which is defined as * the "not found" invalid function index */ memset(table_[count_].prop_xlat_, 0, siz); } else { /* there are no properties to translate, so we don't need a table */ table_[count_].prop_xlat_ = 0; } /* allocate the function-to-property translation table */ if (func_cnt != 0) { size_t i; vm_prop_id_t *prop_ptr; /* allocate the table */ table_[count_].func_xlat_ = (vm_prop_id_t *)t3malloc(func_cnt * sizeof(vm_prop_id_t)); /* clear the table - mark each entry as an invalid property ID */ for (i = 0, prop_ptr = table_[count_].func_xlat_ ; i < func_cnt ; ++i, ++prop_ptr) *prop_ptr = VM_INVALID_PROP; } else { /* no properties, so we don't need a table */ table_[count_].func_xlat_ = 0; } /* * store the reverse mapping for this entry, so that we can find the * dependency entry given the registration index */ reverse_map_[idx] = count_; /* count the new entry */ ++count_; } /* ------------------------------------------------------------------------ */ /* * Add an entry to the dependency table only if it doesn't already * appear in the dependency table. We add the entry based on its * registration table index. */ void CVmMetaTable::add_entry_if_new(uint reg_table_idx, size_t func_cnt, vm_prop_id_t min_prop, vm_prop_id_t max_prop) { /* * if the entry already has a valid dependency table index, as * obtained from the reverse mapping, we need not add it again */ if (reverse_map_[reg_table_idx] != -1) return; /* add the entry */ add_entry((*G_meta_reg_table[reg_table_idx].meta)->get_meta_name(), reg_table_idx, func_cnt, min_prop, max_prop); } /* ------------------------------------------------------------------------ */ /* * Look up an entry by external ID */ vm_meta_entry_t *CVmMetaTable::get_entry_by_id(const char *id) const { /* find the version suffix in the metaclass name, if any */ size_t name_len; const char *vsn = lib_find_vsn_suffix(id, '/', "000000", &name_len); /* look up the metaclass by name */ size_t i; vm_meta_entry_t *entry; for (i = 0, entry = table_ ; i < count_ ; ++entry, ++i) { /* find the version number in this entry */ size_t entry_name_len; const char *entry_vsn = lib_find_vsn_suffix( entry->image_meta_name_, '/', "000000", &entry_name_len); /* see if this is a match */ if (name_len == entry_name_len && memcmp(id, entry->image_meta_name_, name_len) == 0) { /* * we found a match to the name; make sure the version is at * least as high as requested */ if (strcmp(vsn, entry_vsn) <= 0) { /* the loaded version is at least as new, so return it */ return entry; } else { /* it's too old, so it's effectively not available */ return 0; } } } /* didn't find a match */ return 0; } /* ------------------------------------------------------------------------ */ /* * Invoke the VM-stack-based constructor for the metaclass at the given * index */ vm_obj_id_t CVmMetaTable::create_from_stack(VMG_ const uchar **pc_ptr, uint idx, uint argc) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* invoke the appropriate constructor */ return table_[idx].meta_->create_from_stack(vmg_ pc_ptr, argc); } /* ------------------------------------------------------------------------ */ /* * Call a static property in the metaclass at the given index */ int CVmMetaTable::call_static_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint idx, uint *argc, vm_prop_id_t prop) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* invoke the appropriate static property evaluator */ return table_[idx].meta_->call_stat_prop(vmg_ result, pc_ptr, argc, prop); } /* ------------------------------------------------------------------------ */ /* * Create an object with the given ID and load the object from the image * file. */ void CVmMetaTable::create_from_image(VMG_ uint idx, vm_obj_id_t id, const char *ptr, size_t siz) { /* make sure the entry is defined */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* create the object table entry in the memory manager */ G_obj_table->alloc_obj_with_id(id, TRUE); /* invoke the appropriate constructor */ table_[idx].meta_->create_for_image_load(vmg_ id); /* load the object */ vm_objp(vmg_ id)->load_from_image(vmg_ id, ptr, siz); } /* * Create an object of the given metaclass with the given ID, in * preparation for restoring the object's data from saved state * information. This doesn't fill in the object with the saved state * data, but merely creates the object. * * The caller is responsible for having allocated the object ID before * calling this function. */ void CVmMetaTable::create_for_restore(VMG_ uint idx, vm_obj_id_t id) { /* make sure the entry is defined in our table of metaclasses */ if (idx >= count_) err_throw(VMERR_BAD_METACLASS_INDEX); /* * Invoke the appropriate constructor. Note that the caller must * already have allocated the object ID, so we simply use the given ID * without further consideration. */ table_[idx].meta_->create_for_restore(vmg_ id); } /* ------------------------------------------------------------------------ */ /* * Write the table to a file. */ void CVmMetaTable::write_to_file(CVmFile *fp) { size_t i; /* write the number of entries */ fp->write_uint2(get_count()); /* write each entry */ for (i = 0 ; i < get_count() ; ++i) { const char *nm; const vm_meta_entry_t *entry; ushort j; /* get the entry */ entry = get_entry(i); /* get this entry's name */ nm = entry->image_meta_name_; /* write the length of the name, followed by the name */ fp->write_uint2(strlen(nm)); fp->write_bytes(nm, strlen(nm)); /* write our associated IntrinsicClass object's ID */ fp->write_uint4(entry->class_obj_); /* * Write the property table information - write the number of * function entries, and the minimum and maximum property ID's. */ fp->write_uint2(entry->func_xlat_cnt_); fp->write_uint2(entry->min_prop_); fp->write_uint2(entry->min_prop_ + entry->prop_xlat_cnt_); /* * Write out the property translation table. The function * translation table will always be smaller than (at worst, it * will be the same size as) the property translation table; * both tables contain the same information, so since we only * need to write out one or the other, write out the smaller of * the two. * * Note that xlat_func() requires a 1-based function index, so * run our counter from 1 to the function table count. */ for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j) fp->write_uint2(entry->xlat_func(j)); } } /* * Read the table from a file. */ int CVmMetaTable::read_from_file(CVmFile *fp) { size_t cnt; size_t i; /* clear the existing table */ clear(); /* read the number of entries */ cnt = fp->read_uint2(); /* read the entries */ for (i = 0 ; i < cnt ; ++i) { char buf[256]; size_t len; vm_prop_id_t min_prop; vm_prop_id_t max_prop; ushort func_cnt; ushort j; vm_meta_entry_t *entry; vm_obj_id_t class_obj; /* read the length of this entry, and make sure it's valid */ len = fp->read_uint2(); if (len > sizeof(buf) - 1) return VMERR_SAVED_META_TOO_LONG; /* read the name and null-terminate it */ fp->read_bytes(buf, len); buf[len] = '\0'; /* read the associated IntrinsicClass object */ class_obj = (vm_obj_id_t)fp->read_uint4(); /* read the property table description */ func_cnt = (ushort)fp->read_uint2(); min_prop = (vm_prop_id_t)fp->read_uint2(); max_prop = (vm_prop_id_t)fp->read_uint2(); /* add this entry */ add_entry(buf, func_cnt, min_prop, max_prop); /* get the new entry's record */ entry = get_entry(i); /* set the class ID */ entry->class_obj_ = class_obj; /* * Read the property mappings. We stored the function table * entries, so we simply need to load those entries. Note that * the function table has a 1-based index, so run our counter * from 1 to the function count. */ for (j = 1 ; j <= func_cnt ; ++j) entry->add_prop_xlat((short)fp->read_int2(), j); } /* success */ return 0; } #if 0 // moved inline, since it's small and used a lot /* * Get the function vector index for a property given the metaclass's * registration table index. */ int CVmMetaTable::prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop) { /* get the entry for the registration table index */ vm_meta_entry_t *entry = get_entry_from_reg(reg_table_idx); /* * if there's no entry for this registration index, there's * obviously no metaclass function mapping for the property */ if (entry == 0) return 0; /* return the property translation */ return entry->xlat_prop(prop); } #endif /* * Create an IntrinsicClass object for each metaclass that doesn't * already have one. */ void CVmMetaTable::create_intrinsic_class_instances(VMG0_) { size_t idx; vm_meta_entry_t *entry; /* go through our table */ for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry) { /* if this entry has no associated class object, create one for it */ if (entry->class_obj_ == VM_INVALID_OBJ) { /* * create the class object - it will automatically register * itself to fill in the entry */ CVmObjClass::create_dyn(vmg_ idx); } /* * Add this object to the machine globals, if it's not a root * object. Any dynamically-created intrinsic class instance must * be made a machine global because it will always be reachable as * the class object for the metaclass, but the metclass won't trace * the object during garbage collection, so we have to set up a * global to make sure the gc knows it's always reachable. */ G_obj_table->add_to_globals(entry->class_obj_); } } /* * Forget the IntrinsicClass objects we created dynamically in * create_intrinsic_class_instances(). This simply drops our references * to those objects from the metaclass table. */ void CVmMetaTable::forget_intrinsic_class_instances(VMG0_) { size_t idx; vm_meta_entry_t *entry; /* go through our table and do class-level initializations */ for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry) entry->class_obj_ = VM_INVALID_OBJ; } /* ------------------------------------------------------------------------ */ /* * Build a translation table from run-time metaclass index to compiler * TC_META_xxx ID. */ tc_metaclass_t *CVmMetaTable::build_runtime_to_compiler_id_table(VMG0_) { vm_meta_reg_t *entry; int i, cnt; /* count the metaclass registration table size */ for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ; ++entry, ++cnt) ; /* * allocate the translation table - this is simply an array indexed by * metaclass registration index and yielding the corresponding compiler * metaclass ID */ tc_metaclass_t *xlat = (tc_metaclass_t *)t3malloc(cnt * sizeof(xlat[0])); /* scan the registration table for compiler-recognized classes */ for (entry = G_meta_reg_table, i = 0 ; entry->meta != 0 ; ++entry, ++i) { /* get its name */ const char *nm = (*entry->meta)->get_meta_name(); /* scan for the version number suffix - we'll ignore it if found */ const char *p; for (p = nm ; *p != '\0' && *p != '/' ; ++p) ; /* note the length up to the version suffix delimiter */ size_t len = p - nm; /* check for the known names */ if (len == 11 && memcmp(nm, "tads-object", 11) == 0) xlat[i] = TC_META_TADSOBJ; else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0) xlat[i] = TC_META_DICT; else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0) xlat[i] = TC_META_GRAMPROD; else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0) xlat[i] = TC_META_ICMOD; else xlat[i] = TC_META_UNKNOWN; } /* return the translation table */ return xlat; } frobtads-1.2.3/tads3/tct3ty.h0000644000175000001440000000160507627507712015144 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3TY.H,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3ty.h - T3 Target-specific Type definitions Function Notes Modified 07/09/99 MJRoberts - Creation */ #ifndef TCT3TY_H #define TCT3TY_H #include "vmtype.h" /* ------------------------------------------------------------------------ */ /* * target type specifications */ /* global object ID */ typedef vm_obj_id_t tctarg_obj_id_t; /* global property ID */ typedef vm_prop_id_t tctarg_prop_id_t; /* invalid object/property ID's */ const tctarg_obj_id_t TCTARG_INVALID_OBJ = VM_INVALID_OBJ; const tctarg_prop_id_t TCTARG_INVALID_PROP = VM_INVALID_PROP; #endif /* TCT3TY_H */ frobtads-1.2.3/tads3/tccmdutl.h0000644000175000001440000000524307627507702015532 0ustar realncusers/* * Copyright (c) 2002 by Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tccmdutl.cpp - TADS 3 Compiler command-line parsing utilities Function Defines some utility functions for command-line parsing. Notes Modified 04/03/02 MJRoberts - Creation */ #ifndef TCCMDUTL_H #define TCCMDUTL_H #include /* * Command utilities class. We use a class to group these functions into * a class namespace */ class CTcCommandUtil { public: /* get an option argument */ static char *get_opt_arg(int argc, char **argv, int *curarg, int optlen); /* parse an options file */ static int parse_opt_file(osfildef *fp, char **argv, class CTcOptFileHelper *helper); }; /* * Helper interface for parse_opt_file helper. An implementation of this * interface must be provided to parse_opt_file(). */ class CTcOptFileHelper { public: /* * Allocate an option string. Allocates the given number of bytes of * memory. Strings allocated with this must be freed with * free_opt_file_str(). */ virtual char *alloc_opt_file_str(size_t len) = 0; /* free a string allocated with alloc_opt_file_str() */ virtual void free_opt_file_str(char *str) = 0; /* * Process a comment line. We call this for each line that we find * starting with "#" and for each blank line. */ virtual void process_comment_line(const char *str) = 0; /* process a non-comment line */ virtual void process_non_comment_line(const char *str) = 0; /* * Process a configuration line. Once we see a configuration flag (a * line reading "[Config:xxx]"), we'll process all subsequent text in * the file through this function. * * 'config_id' is the 'xxx' value in the [Config:xxx] configuration * flag line that started the section. 'is_id_line' is true if we're * processing the "[Config:xxx]" line itself, false if it's any other * line within the [Config:xxx] section. * * Note that this routine receives ALL lines within a configuration * section, including comment lines. Once we're inside a configuration * block, the entire contents are opaque to the generic processor, * since even the comment format is up to the configuration section's * owner to define. */ virtual void process_config_line(const char *config_id, const char *str, int is_id_line) = 0; }; #endif /* TCCMDUTL_H */ frobtads-1.2.3/tads3/tct3img.cpp0000644000175000001440000037154612145504453015623 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCT3IMG.CPP,v 1.1 1999/07/11 00:46:57 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3.cpp - TADS 3 Compiler - T3 VM Code Generator - image writing functions Function Image writing routines for the T3-specific code generator Notes Modified 05/08/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "os.h" #include "tcprs.h" #include "tct3.h" #include "tcgen.h" #include "vmtype.h" #include "vmwrtimg.h" #include "vmgram.h" #include "vmfile.h" #include "tcmain.h" #include "tcerr.h" #include "tcmake.h" #include "tctok.h" /* ------------------------------------------------------------------------ */ /* * Object file signature. The numerical suffix in the first part is the * format version number: whenever we make an incompatible change to the * format, we'll increment this number so that the linker will recognize an * incompatible file format and require a full rebuild. */ static const char obj_file_sig[] = "TADS3.Object.0011\n\r\032"; /* ------------------------------------------------------------------------ */ /* * Write an object file. The object file contains the raw byte streams * with the generated code; the fixup lists for the streams; the global * symbol table; and the function and metaclass dependency lists. */ void CTcGenTarg::write_to_object_file(CVmFile *fp, CTcMake *) { ulong flags; /* write the signature */ fp->write_bytes(obj_file_sig, sizeof(obj_file_sig) - 1); /* compute the object file flags */ flags = 0; if (G_debug) flags |= TCT3_OBJHDR_DEBUG; /* write the flags */ fp->write_uint4(flags); /* write the constant and code pool indivisible object maxima */ fp->write_uint4(max_str_len_); fp->write_uint4(max_list_cnt_); fp->write_uint4(max_bytecode_len_); /* * Write the maximum object and property ID's. When we load this * object file, we'll need to generate a translated ID number for * each object ID and for each property ID, to translate from the * numbering system in the object file to the final image file * numbering system. It is helpful if we know early on how many of * each there are, so that we can allocate table space accordingly. */ fp->write_uint4(next_obj_); fp->write_uint4(next_prop_); fp->write_uint4(G_prs->get_enum_count()); /* write the function set dependency table */ write_funcdep_to_object_file(fp); /* * write the metaclass dependency table - note that we must do this * before writing the global symbol table, because upon loading the * object file, we must have the dependency table loaded before we * can load the symbols (so that any metaclass symbols can be * resolved to their dependency table indices) */ write_metadep_to_object_file(fp); /* write the global symbol table */ G_prs->write_to_object_file(fp); /* write the main code stream and its fixup list */ G_cs_main->write_to_object_file(fp); /* write the static code stream and its fixup list */ G_cs_static->write_to_object_file(fp); /* write the data stream and its fixup list */ G_ds->write_to_object_file(fp); /* write the object stream and its fixup list */ G_os->write_to_object_file(fp); /* write the intrinsic class modifier stream */ G_icmod_stream->write_to_object_file(fp); /* write the BigNumber stream and its fixup list */ G_bignum_stream->write_to_object_file(fp); /* write the RexPattern stream and its fixup list */ G_rexpat_stream->write_to_object_file(fp); /* write the static initializer ID stream */ G_static_init_id_stream->write_to_object_file(fp); /* write the local variable symbol stream */ G_lcl_stream->write_to_object_file(fp); /* write the object ID fixup list */ CTcIdFixup::write_to_object_file(fp, G_objfixup); /* write the property ID fixup list */ CTcIdFixup::write_to_object_file(fp, G_propfixup); /* write the enumerator ID fixup list */ CTcIdFixup::write_to_object_file(fp, G_enumfixup); /* write debugging information if we're compiling for the debugger */ if (G_debug) { tct3_debug_line_page *pg; /* write the source file list */ write_sources_to_object_file(fp); /* * Write the pointers to the debug line records in the code * stream, so that we can fix up the line records on re-loading * the object file (they'll need to be adjusted for the new * numbering system for the source file descriptors). First, * write the total number of pointers. */ fp->write_uint4(debug_line_cnt_); /* now write the pointers, one page at a time */ for (pg = debug_line_head_ ; pg != 0 ; pg = pg->nxt) { size_t pgcnt; /* * if this is the last entry, it might only be partially * full; otherwise, it's completely full, because we always * fill a page before allocating a new one */ if (pg->nxt == 0) pgcnt = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE); else pgcnt = TCT3_DEBUG_LINE_PAGE_SIZE; /* * Write the data - we prepared it in portable format, so we * can just copy it directly to the file. Note that each * entry is four bytes. */ fp->write_bytes((char *)pg->line_ofs, pgcnt * TCT3_DEBUG_LINE_REC_SIZE); } } /* write the #define symbols */ G_tok->write_macros_to_file_for_debug(fp); } /* ------------------------------------------------------------------------ */ /* * Write the function-set dependency table to an object file */ void CTcGenTarg::write_funcdep_to_object_file(CVmFile *fp) { tc_fnset_entry *cur; /* write the count */ fp->write_uint2(fnset_cnt_); /* write the entries */ for (cur = fnset_head_ ; cur != 0 ; cur = cur->nxt) { size_t len; len = strlen(cur->nm); fp->write_uint2(len); fp->write_bytes(cur->nm, len); } } /* * Write the metaclass dependency table to an object file */ void CTcGenTarg::write_metadep_to_object_file(CVmFile *fp) { tc_meta_entry *cur; /* write the count */ fp->write_uint2(meta_cnt_); /* write the entries */ for (cur = meta_head_ ; cur != 0 ; cur = cur->nxt) { size_t len; len = strlen(cur->nm); fp->write_uint2(len); fp->write_bytes(cur->nm, len); } } /* ------------------------------------------------------------------------ */ /* * Error handler for CTcTokenizer::load_macros_from_file() */ class MyLoadMacErr: public CTcTokLoadMacErr { public: MyLoadMacErr(const char *fname) { fname_ = fname; } /* log an error */ virtual void log_error(int err) { /* check the error code */ switch(err) { case 1: case 2: G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_MACRO_SYM_TOO_LONG, fname_); break; } } private: /* the name of the object file we're loading */ const char *fname_; }; /* ------------------------------------------------------------------------ */ /* * Load an object file. We'll read the file, load its data into memory * (creating global symbol table entries and writing to the code and * data streams), fix up the fixups to the new base offsets in the * streams, and translate object and property ID values from the object * file numbering system to our in-memory numbering system (which will * usually differ after more than one object file is loaded, because the * numbering systems in the different files must be reconciled). * * Returns zero on success; logs errors and returns non-zero on error. */ int CTcGenTarg::load_object_file(CVmFile *fp, const textchar_t *fname) { char buf[128]; ulong obj_cnt; ulong prop_cnt; ulong enum_cnt; vm_obj_id_t *obj_xlat = 0; vm_prop_id_t *prop_xlat = 0; ulong *enum_xlat = 0; int err; ulong hdr_flags; ulong siz; ulong main_cs_start_ofs; ulong static_cs_start_ofs; /* * Before loading anything from the file, go through all of the * streams and set their object file base offset. All stream * offsets that we read from the object file will be relative to the * these values, since the object file stream data will be loaded in * after any data currently in the streams. */ G_cs_main->set_object_file_start_ofs(); G_cs_static->set_object_file_start_ofs(); G_ds->set_object_file_start_ofs(); G_os->set_object_file_start_ofs(); G_icmod_stream->set_object_file_start_ofs(); G_bignum_stream->set_object_file_start_ofs(); G_rexpat_stream->set_object_file_start_ofs(); G_static_init_id_stream->set_object_file_start_ofs(); G_lcl_stream->set_object_file_start_ofs(); /* * note the main code stream's start offset, since we'll need this * later in order to process the debug line records; likewise, note * the static stream's start offset */ main_cs_start_ofs = G_cs_main->get_ofs(); static_cs_start_ofs = G_cs_static->get_ofs(); /* read the signature, and make sure it matches */ fp->read_bytes(buf, sizeof(obj_file_sig) - 1); if (memcmp(buf, obj_file_sig, sizeof(obj_file_sig) - 1) != 0) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_SIG); return 1; } /* read the file header flags */ hdr_flags = fp->read_uint4(); /* * If we're linking with debugging information, but this object file * wasn't compiled with debugging information, we won't be able to * produce a complete debuggable image - log an error to that * effect. */ if (G_debug && (hdr_flags & TCT3_OBJHDR_DEBUG) == 0) G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_NO_DBG, fname); /* * Read the constant and code pool indivisible object maxima. Note * each maximum that exceeds the current maximum, since we must keep * track of the largest indivisible object of each type in the * entire program. */ /* read and note the maximum string constant length */ siz = fp->read_uint4(); if (siz > max_str_len_) max_str_len_ = siz; /* read and note the maximum list size */ siz = fp->read_uint4(); if (siz > max_list_cnt_) max_list_cnt_ = siz; /* read and note the maximum code pool object size */ siz = fp->read_uint4(); if (siz > max_bytecode_len_) max_bytecode_len_ = siz; /* * read the object, property, and enumerator ID counts from the file * - these give the highest ID values that are assigned anywhere in * the object file's numbering system */ obj_cnt = fp->read_uint4(); prop_cnt = fp->read_uint4(); enum_cnt = fp->read_uint4(); /* * Allocate object and property ID translation tables. These are * simply arrays of ID's. Each element of an array gives the global * ID number assigned to the object whose local ID is the array * index. So, obj_xlat[local_id] = global_id. We need one element * in the object ID translation array for each local ID in the * object file, which is obj_cnt; likewise for properties and * prop_cnt. * * We're being a bit lazy here by using flat arrays. This could be * a problem for very large object files on 16-bit machines: if a * single object file has more than 16k object ID's (which means * that it defines and imports more than 16k unique objects), or * more than 32k property ID's, we'll go over the 64k allocation * limit on these machines. This seems unlikely to become a problem * in practice, but to ensure a graceful failure in such cases, * check the allocation requirement to make sure we don't go over * the present platform's architectural limits. */ if (obj_cnt * sizeof(obj_xlat[0]) > OSMALMAX || prop_cnt * sizeof(prop_xlat[0]) > OSMALMAX || enum_cnt * sizeof(enum_xlat[0]) > OSMALMAX) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_TOO_MANY_IDS); return 2; } /* allocate the translation arrays */ obj_xlat = (vm_obj_id_t *) t3malloc((size_t)(obj_cnt * sizeof(obj_xlat[0]))); prop_xlat = (vm_prop_id_t *) t3malloc((size_t)(prop_cnt * sizeof(prop_xlat[0]))); enum_xlat = (ulong *) t3malloc((size_t)(enum_cnt * sizeof(enum_xlat[0]))); err_try { /* check to make sure we got the memory */ if (obj_xlat == 0 || prop_xlat == 0 || enum_xlat == 0) { /* log an error and return failure */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_OUT_OF_MEM); err = 3; goto done; } /* * Clear out the translation arrays initially. We should, in the * course of loading the symbol table, assign a translation value * for every entry. If anything is left at zero (which is invalid * as an object or property ID), something must be wrong. */ memset(obj_xlat, 0, (size_t)(obj_cnt * sizeof(obj_xlat[0]))); memset(prop_xlat, 0, (size_t)(prop_cnt * sizeof(prop_xlat[0]))); memset(enum_xlat, 0, (size_t)(enum_cnt * sizeof(enum_xlat[0]))); /* read the function set dependency table */ load_funcdep_from_object_file(fp, fname); /* read the metaclass dependency table */ load_metadep_from_object_file(fp, fname); /* * Read the symbol table. This will create translation entries for * the object and property names found in the symbol table. */ if ((err = G_prs->load_object_file(fp, fname, obj_xlat, prop_xlat, enum_xlat)) != 0) { /* that failed - abort the load */ goto done; } /* read the main code stream and its fixup list */ G_cs_main->load_object_file(fp, fname); /* read the static code stream and its fixup list */ G_cs_static->load_object_file(fp, fname); /* read the data stream and its fixup list */ G_ds->load_object_file(fp, fname); /* read the object data stream and its fixup list */ G_os->load_object_file(fp, fname); /* read the intrinsic class modifier stream */ G_icmod_stream->load_object_file(fp, fname); /* read the BigNumber stream and its fixup list */ G_bignum_stream->load_object_file(fp, fname); /* read the RexPattern stream and its fixup list */ G_rexpat_stream->load_object_file(fp, fname); /* read the static initializer ID stream */ G_static_init_id_stream->load_object_file(fp, fname); /* read the local variable symbol stream */ G_lcl_stream->load_object_file(fp, fname); /* read the object ID fixup list */ CTcIdFixup::load_object_file( fp, obj_xlat, obj_cnt, TCGEN_XLAT_OBJ, 4, fname, G_keep_objfixups ? &G_objfixup : 0); /* read the property ID fixup list */ CTcIdFixup::load_object_file( fp, prop_xlat, prop_cnt, TCGEN_XLAT_PROP, 2, fname, G_keep_propfixups ? &G_propfixup : 0); /* read the enum ID fixup list */ CTcIdFixup::load_object_file( fp, enum_xlat, enum_cnt, TCGEN_XLAT_ENUM, 2, fname, G_keep_enumfixups ? &G_enumfixup : 0); /* if the object file contains debugging information, read that */ if ((hdr_flags & TCT3_OBJHDR_DEBUG) != 0) { /* load the debug records */ load_debug_records_from_object_file( fp, fname, main_cs_start_ofs, static_cs_start_ofs); } /* read the macro definitions */ { CVmFileStream str(fp); MyLoadMacErr err_handler(fname); G_tok->load_macros_from_file(&str, &err_handler); } done: ; } err_finally { /* * free the ID translation arrays - we no longer need them after * loading the object file, because we translate everything in the * course of loading, so what's left in memory when we're done uses * the new global numbering system */ if (obj_xlat != 0) t3free(obj_xlat); if (prop_xlat != 0) t3free(prop_xlat); if (enum_xlat != 0) t3free(enum_xlat); } err_end; /* return the result */ return err; } /* ------------------------------------------------------------------------ */ /* * Add a debug line table to our list */ void CTcGenTarg::add_debug_line_table(ulong ofs) { size_t idx; uchar *p; /* calculate the index of the next free entry on its page */ idx = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE); /* * if we've completely filled the last page, allocate a new one - we * know we've exhausted the page if we're at the start of a new page * (i.e., the index is zero) */ if (idx == 0) { tct3_debug_line_page *pg; /* allocate the new page */ pg = (tct3_debug_line_page *)t3malloc(sizeof(*pg)); /* link it in at the end of the list */ pg->nxt = 0; if (debug_line_tail_ == 0) debug_line_head_ = pg; else debug_line_tail_->nxt = pg; debug_line_tail_ = pg; } /* get a pointer to the entry */ p = debug_line_tail_->line_ofs + (idx * TCT3_DEBUG_LINE_REC_SIZE); /* * set this entry - one byte for the code stream ID, then a UINT4 with * the offset in the stream */ *p = G_cs->get_stream_id(); oswp4(p + 1, ofs); /* count it */ ++debug_line_cnt_; } /* * Load the debug records from an object file */ void CTcGenTarg::load_debug_records_from_object_file( CVmFile *fp, const textchar_t *fname, ulong main_cs_start_ofs, ulong static_cs_start_ofs) { int first_filedesc; ulong line_table_cnt; /* * Note the starting number of our file descriptors - in the file, * we started numbering them at zero, but if we have already loaded * other object files before this one, we'll be numbering ours after * the ones previously loaded. So, we'll need to fix up the * references to the file descriptor indices accordingly. */ first_filedesc = G_tok->get_next_filedesc_index(); /* read the source file list */ read_sources_from_object_file(fp); /* * Read the line record pointers. For each line record table, we * must fix up the line records to reflect the file descriptor * numbering system. */ for (line_table_cnt = fp->read_uint4() ; line_table_cnt != 0 ; --line_table_cnt) { uchar stream_id; ulong ofs; CTcCodeStream *cs; ulong start_ofs; /* read the stream ID */ stream_id = fp->read_byte(); /* find the appropriate code stream */ cs = (CTcCodeStream *) CTcDataStream::get_stream_from_id(stream_id, fname); /* get the appropriate starting offset */ start_ofs = (cs == G_cs_main ? main_cs_start_ofs : static_cs_start_ofs); /* * Read the next line table offset - this is the offset in the * code stream of the next debug line table. Add our starting * offset to get the true offset. */ ofs = fp->read_uint4() + start_ofs; /* update this table */ fix_up_debug_line_table(cs, ofs, first_filedesc); } } /* * Fix up a debug line record table for the current object file */ void CTcGenTarg::fix_up_debug_line_table(CTcCodeStream *cs, ulong line_table_ofs, int first_filedesc) { uint cnt; ulong ofs; /* read the number of line records in the table */ cnt = cs->readu2_at(line_table_ofs); /* adjust each entry */ for (ofs = line_table_ofs + 2 ; cnt != 0 ; --cnt, ofs += G_sizes.dbg_line) { uint filedesc; /* read the old file descriptor ID */ filedesc = cs->readu2_at(ofs + 2); /* adjust it to the new numbering system */ filedesc += first_filedesc; /* write it back */ cs->write2_at(ofs + 2, filedesc); } } /* ------------------------------------------------------------------------ */ /* * Load a function set dependency table from the object file. We can * add to the existing set of functions, but if we have N function sets * defined already, the first N in the file must match the ones we have * loaded exactly. */ void CTcGenTarg::load_funcdep_from_object_file(class CVmFile *fp, const textchar_t *fname) { int cnt; tc_fnset_entry *cur; /* read the count */ cnt = fp->read_int2(); /* read the entries */ for (cur = fnset_head_ ; cnt != 0 ; --cnt) { char buf[128]; size_t len; /* read this entry */ len = fp->read_uint2(); if (len + 1 > sizeof(buf)) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_FN_OR_META, fname); return; } /* read the name and null-terminate it */ fp->read_bytes(buf, len); buf[len] = '\0'; /* * if we are still scanning existing entries, make sure it * matches; otherwise, add it */ if (cur != 0) { const char *vsn; char *ent_vsn; size_t name_len; size_t ent_name_len; /* get the version suffixes, if any */ vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len); ent_vsn = (char *) lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len); /* if it doesn't match, it's an error */ if (name_len != ent_name_len || memcmp(cur->nm, buf, name_len) != 0) G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_FNSET_CONFLICT, buf, fname); /* * if the new version string is higher than the old version * string, keep the new version string */ if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0 && strlen(vsn) <= strlen(ent_vsn)) { /* * the new version is newer than the version in the * table - overwrite the table version with the new * version, so that the table keeps the newest version * mentioned anywhere (newer versions are upwardly * compatible with older versions, so the code that uses * the older version will be equally happy with the * newer version) */ strcpy(ent_vsn, vsn); } /* move on to the next one */ cur = cur->nxt; } else { /* we're past the existing list - add the new function set */ add_fnset(buf, len); } } } /* * Load a metaclass dependency table from the object file. We can add * to the existing set of metaclasses, but if we have N metaclasses * defined already, the first N in the file must match the ones we have * loaded exactly. */ void CTcGenTarg::load_metadep_from_object_file(class CVmFile *fp, const textchar_t *fname) { int cnt; tc_meta_entry *cur; /* read the count */ cnt = fp->read_int2(); /* read the entries */ for (cur = meta_head_ ; cnt != 0 ; --cnt) { char buf[128]; size_t len; /* read this entry */ len = fp->read_uint2(); if (len + 1 > sizeof(buf)) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INV_FN_OR_META, fname); return; } /* read the name and null-terminate it */ fp->read_bytes(buf, len); buf[len] = '\0'; /* * if we are still scanning existing entries, make sure it * matches; otherwise, add it */ if (cur != 0) { const char *vsn; char *ent_vsn; size_t name_len; size_t ent_name_len; /* find the version suffix, if any */ vsn = lib_find_vsn_suffix(buf, '/', 0, &name_len); /* find the version suffix in this entry's name */ ent_vsn = (char *) lib_find_vsn_suffix(cur->nm, '/', 0, &ent_name_len); /* if it doesn't match the entry name, it's an error */ if (name_len != ent_name_len || memcmp(cur->nm, buf, name_len) != 0) { /* log a mis-matched metaclass error */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_META_CONFLICT, buf, fname); } /* * if the new version string is higher than the old version * string, keep the new version string */ if (vsn != 0 && ent_vsn != 0 && strcmp(vsn, ent_vsn) > 0 && strlen(vsn) <= strlen(ent_vsn)) { /* * the new version is newer than the version in the * table - overwrite the table version with the new * version, so that the table keeps the newest version * mentioned anywhere (newer versions are upwardly * compatible with older versions, so the code that uses * the older version will be equally happy with the * newer version) */ strcpy(ent_vsn, vsn); } /* move on to the next one */ cur = cur->nxt; } else { /* we're past the existing list - add the new metaclass */ add_meta(buf, len, 0); } } } /* ------------------------------------------------------------------------ */ /* * Write the source file list to an object file */ void CTcGenTarg::write_sources_to_object_file(CVmFile *fp) { CTcTokFileDesc *desc; /* write the number of entries */ fp->write_uint2(G_tok->get_filedesc_count()); /* write the entries */ for (desc = G_tok->get_first_filedesc() ; desc != 0 ; desc = desc->get_next()) { size_t len; const char *fname; /* get the filename - use the resolved local filename */ fname = desc->get_fname(); /* write the length of the filename */ len = strlen(fname); fp->write_uint2(len); /* write the filename */ fp->write_bytes(fname, len); } } /* * Read a source file list from an object file */ void CTcGenTarg::read_sources_from_object_file(CVmFile *fp) { uint cnt; uint i; /* read the number of entries */ cnt = fp->read_uint2(); /* read the entries */ for (i = 0 ; i < cnt ; ++i) { size_t len; char fname[OSFNMAX]; /* read the length of the entry */ len = fp->read_uint2(); /* see if it fits in our buffer */ if (len <= sizeof(fname)) { /* read it */ fp->read_bytes(fname, len); } else { /* it's too long - truncate to the buffer size */ fp->read_bytes(fname, sizeof(fname)); /* skip the rest */ fp->set_pos(fp->get_pos() + len - sizeof(fname)); /* note the truncated length */ len = sizeof(fname); } /* * Add it to the tokenizer list. Always create a new entry, * rather than re-using an existing entry. When loading * multiple object files, this might result in the same file * appearing as multiple different descriptors, but it's a small * price to pay (it doesn't add too much redundant space to the * image file, and in any case the information is only retained * when we're compiling for debugging) for a big gain in * simplicity (the source references in the object file can be * fixed up simply by adding the object file's base index to all * of the reference indices). */ G_tok->create_file_desc(fname, len); } } /* ------------------------------------------------------------------------ */ /* * Calculate pool layouts. This is called at the start of the link * phase: at this point, we know the sizes of the largest constant pool * and code pool objects, so we can figure the layouts of the pools. */ void CTcGenTarg::calc_pool_layouts(size_t *first_static_page) { size_t max_str; size_t max_list; size_t max_item; /* * We've parsed the entire program, so we now know the lengths of * the longest string constant and the longest list constant. From * this, we can figure the size of our constant pool pages: since * each list or string must be contained entirely in a single page, * the minimum page size is the length of the longest string or list. * * We must pick a power of two for our page size. We don't want to * make the page size too small; each page requires a small amount * of overhead, hence the more pages for a given total constant pool * size, the more overhead. We also don't want to make the page * size too large, because smaller page sizes will give us better * performance on small machines that will have to swap pages in and * out (the smaller a page, the less time it will take to load a * page). * * Start at 2k, which is big enough that the data part will * overwhelm the per-page overhead, but small enough that it can be * loaded quickly on a small machine. If that's at least twice the * length of the longest string or list, use it; otherwise, double * it and try again. */ /* * find the length in bytes of the longest string - we require the * length prefix in addition to the bytes of the string */ max_str = max_str_len_ + VMB_LEN; /* * find the length in bytes of the longest list - we require one * data holder per element, plus the length prefix */ max_list = (max_list_cnt_ * VMB_DATAHOLDER) + VMB_LEN; /* get the larger of the two - this will be our minimum size */ max_item = max_str; if (max_list > max_item) max_item = max_list; /* * if the maximum item size is under 16k, look for a size that will * hold twice the maximum item size; otherwise, relax this * requirement, since the pages are getting big, and look for * something that merely fits the largest element */ if (max_item < 16*1024) max_item <<= 1; /* calculate the constant pool layout */ const_layout_.calc_layout(G_ds, max_item, TRUE); /* calculate the main code pool layout */ code_layout_.calc_layout(G_cs_main, max_bytecode_len_, TRUE); /* note the number of pages of regular code */ *first_static_page = code_layout_.page_cnt_; /* * add the static pool into the code pool layout, since we'll * ultimately write the static code as part of the plain code pages */ code_layout_.calc_layout(G_cs_static, max_bytecode_len_, FALSE); } /* ------------------------------------------------------------------------ */ /* * Write the image file */ void CTcGenTarg::write_to_image(CVmFile *fp, uchar data_xor_mask, const char tool_data[4]) { tc_meta_entry *meta; CTcSymbol *sym; unsigned long main_ofs; vm_prop_id_t construct_prop = VM_INVALID_PROP; vm_prop_id_t finalize_prop = VM_INVALID_PROP; vm_prop_id_t graminfo_prop = VM_INVALID_PROP; vm_prop_id_t gramtag_prop = VM_INVALID_PROP; vm_prop_id_t miscvocab_prop = VM_INVALID_PROP; tc_fnset_entry *fnset; CVmImageWriter *image_writer; int bignum_idx; int rexpat_idx; int int_class_idx; CTcPrsExport *exp; CTcDataStream *cs_list[2]; size_t first_static_code_page; /* * if we have any BigNumber data, get the BigNumber metaclass index * (or define it, if the program didn't do so itself) */ if (G_bignum_stream->get_ofs() != 0) bignum_idx = find_or_add_meta("bignumber", 9, 0); /* if we have any RexPattern data, get the RexPattern metaclass index */ if (G_rexpat_stream->get_ofs() != 0) rexpat_idx = find_or_add_meta("regex-pattern", 13, 0); /* apply internal object/property ID fixups in the symbol table */ G_prs->apply_internal_fixups(); /* build the grammar productions */ G_prs->build_grammar_productions(); /* * Build the dictionaries. We must wait until after applying the * internal fixups to build the dictionaries, so that we have the * final, fully-resolved form of each object's vocabulary list before * we build the dictionaries. We must also wait until after we build * the grammar productions, because the grammar productions can add * dictionary entries for their literal token matchers. */ G_prs->build_dictionaries(); /* * Build the multi-method static initializers. Note: this must be done * before we generate the intrinsic class objects, because we might add * intrinsic class modifiers in the course of building the mm * initializers. */ build_multimethod_initializers(); /* make sure the the IntrinsicClass intrinsic class is itself defined */ int_class_idx = find_or_add_meta("intrinsic-class", 15, 0); /* build the IntrinsicClass objects */ build_intrinsic_class_objs(G_int_class_stream); /* * Apply fixups for the local variable stream. This stream isn't * written to the image file, but we need to do the layout in order to * apply its fixups. */ CTcStreamLayout vpl; vpl.calc_layout(G_lcl_stream, 65535, TRUE); /* * Build the local symbol records. We need to do this before * calculating the code pool layout, because this writes more data to * the constant pool. We need to build the symbol records separately * for the regular and static code pools, since they haven't been * merged yet. */ CVmHashTable *lcltab = new CVmHashTable(1024, new CVmHashFuncCS(), TRUE); build_local_symbol_records(G_cs_main, lcltab); build_local_symbol_records(G_cs_static, lcltab); /* we're done with the local symbol hash table */ delete lcltab; /* calculate the final pool layouts */ calc_pool_layouts(&first_static_code_page); /* build the source line location maps, if debugging */ if (G_debug) build_source_line_maps(); /* look up the "_main" symbol in the global symbol table */ sym = G_prs->get_global_symtab()->find("_main"); /* * if there's no "_main" symbol, or it's not a function, it's an * error */ if (sym == 0) { /* "_main" isn't defined - log an error and abort */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_DEFINED); return; } else if (sym->get_type() != TC_SYM_FUNC) { /* "_main" isn't a function - log an error and abort */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MAIN_NOT_FUNC); return; } else { /* * Get the "_main" symbol's code pool address - this is the * program's entrypoint. We can ask for this information at * this point because we don't start writing the image file * until after the final fixup pass, which is where this address * is finally calculated. */ main_ofs = ((CTcSymFunc *)sym)->get_code_pool_addr(); } /* get the constructor and finalizer property ID's */ construct_prop = (tctarg_prop_id_t)G_prs->get_constructor_prop(); finalize_prop = (tctarg_prop_id_t)G_prs->get_finalize_prop(); /* get the special generated GrammarProd property IDs */ graminfo_prop = (tctarg_prop_id_t)G_prs->get_grammarInfo_prop(); gramtag_prop = (tctarg_prop_id_t)G_prs->get_grammarTag_prop(); miscvocab_prop = (tctarg_prop_id_t)G_prs->get_miscvocab_prop(); /* create our image writer */ image_writer = new CVmImageWriter(fp); /* prepare the image file - use file format version 1 */ image_writer->prepare(1, tool_data); /* write the entrypoint offset and data structure parameters */ image_writer->write_entrypt(main_ofs, G_sizes.mhdr, G_sizes.exc_entry, G_sizes.dbg_line, G_sizes.dbg_hdr, G_sizes.lcl_hdr, G_sizes.dbg_frame, G_sizes.dbg_fmt_vsn); /* begin writing the symbolic items */ image_writer->begin_sym_block(); /* run through the list of exports in the parser */ for (exp = G_prs->get_exp_head() ; exp != 0 ; exp = exp->get_next()) { CTcPrsExport *exp2; int dup_err_cnt; /* * if this one's external name is null, it means that we've * previously encountered it as a duplicate and marked it as such * - in this case, simply skip it */ if (exp->get_ext_name() == 0) continue; /* make sure it's not one of our special ones */ if (exp->ext_name_matches("LastProp") || exp->ext_name_matches("Constructor") || exp->ext_name_matches("Destructor") || exp->ext_name_starts_with("operator ")) { /* it's a reserved export - flag an error */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_RESERVED_EXPORT, (int)exp->get_ext_len(), exp->get_ext_name()); } /* look up the symbol, defining as a property if undefined */ sym = G_prs->get_global_symtab() ->find_or_def_prop(exp->get_sym(), exp->get_sym_len(), FALSE); /* * Scan the rest of the export list for duplicates. If we find * the symbol external name exported with a different value, it's * an error. */ for (dup_err_cnt = 0, exp2 = exp->get_next() ; exp2 != 0 ; exp2 = exp2->get_next()) { /* if this one has already been marked as a duplicate, skip it */ if (exp2->get_ext_name() == 0) continue; /* check for a match of the external name */ if (exp->ext_name_matches(exp2)) { /* * This one matches, so it's a redundant export for the * same name. If it's being exported as the same internal * symbol as the other one, this is fine; otherwise it's * an error, since the same external name can't be given * two different meanings. */ if (!exp->sym_matches(exp2)) { /* * It doesn't match - log an error. If we've already * logged an error, show a continuation error; * otherwise show the first error for the symbol. */ ++dup_err_cnt; if (dup_err_cnt == 1) { /* it's the first error - show the long form */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_DUP_EXPORT, (int)exp->get_ext_len(), exp->get_ext_name(), (int)exp->get_sym_len(), exp->get_sym(), (int)exp2->get_sym_len(), exp2->get_sym()); } else { /* it's a follow-up error */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_DUP_EXPORT_AGAIN, (int)exp->get_ext_len(), exp->get_ext_name(), (int)exp2->get_sym_len(), exp2->get_sym()); } } /* * Regardless of whether this one matches or not, remove * it from the list by setting its external name to null - * we only want to include each symbol in the export list * once. */ exp2->set_extern_name(0, 0); } } /* write it out according to its type */ switch(sym->get_type()) { case TC_SYM_OBJ: /* write the object symbol */ image_writer->write_sym_item_objid( exp->get_ext_name(), exp->get_ext_len(), ((CTcSymObj *)sym)->get_obj_id()); break; case TC_SYM_PROP: /* write the property symbol */ image_writer->write_sym_item_propid( exp->get_ext_name(), exp->get_ext_len(), ((CTcSymProp *)sym)->get_prop()); break; case TC_SYM_FUNC: /* write the function symbol */ image_writer->write_sym_item_func( exp->get_ext_name(), exp->get_ext_len(), ((CTcSymFunc *)sym)->get_code_pool_addr()); break; default: /* can't export other types */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_INVALID_TYPE_FOR_EXPORT, (int)exp->get_sym_len(), exp->get_sym()); break; } } /* * write the last property ID - this is a special synthetic export * that we provide automatically */ image_writer->write_sym_item_propid("LastProp", next_prop_); /* write our Constructor and Destructor property ID's */ if (construct_prop != VM_INVALID_PROP) image_writer->write_sym_item_propid("Constructor", construct_prop); if (finalize_prop != VM_INVALID_PROP) image_writer->write_sym_item_propid("Destructor", finalize_prop); /* the compiler generates grammarTag and grammarInfo properties */ if (graminfo_prop != VM_INVALID_PROP) image_writer->write_sym_item_propid( "GrammarProd.grammarInfo", graminfo_prop); if (gramtag_prop != VM_INVALID_PROP) image_writer->write_sym_item_propid( "GrammarProd.grammarTag", gramtag_prop); /* likewise miscVocab */ if (miscvocab_prop != VM_INVALID_PROP) image_writer->write_sym_item_propid( "GrammarProd.miscVocab", miscvocab_prop); /* write the operator properties */ write_op_export(image_writer, G_prs->ov_op_add_); write_op_export(image_writer, G_prs->ov_op_sub_); write_op_export(image_writer, G_prs->ov_op_mul_); write_op_export(image_writer, G_prs->ov_op_div_); write_op_export(image_writer, G_prs->ov_op_mod_); write_op_export(image_writer, G_prs->ov_op_xor_); write_op_export(image_writer, G_prs->ov_op_shl_); write_op_export(image_writer, G_prs->ov_op_ashr_); write_op_export(image_writer, G_prs->ov_op_lshr_); write_op_export(image_writer, G_prs->ov_op_bnot_); write_op_export(image_writer, G_prs->ov_op_bor_); write_op_export(image_writer, G_prs->ov_op_band_); write_op_export(image_writer, G_prs->ov_op_neg_); write_op_export(image_writer, G_prs->ov_op_idx_); write_op_export(image_writer, G_prs->ov_op_setidx_); /* done with the symbolic names */ image_writer->end_sym_block(); /* write the function-set dependency table */ image_writer->begin_func_dep(fnset_cnt_); for (fnset = fnset_head_ ; fnset != 0 ; fnset = fnset->nxt) image_writer->write_func_dep_item(fnset->nm); image_writer->end_func_dep(); /* start the metaclass dependency table */ image_writer->begin_meta_dep(meta_cnt_); /* write the metaclass dependency items */ for (meta = meta_head_ ; meta != 0 ; meta = meta->nxt) { /* write the dependency item */ image_writer->write_meta_dep_item(meta->nm); /* if there's an associated symbol, write the property list */ if (meta->sym != 0) { CTcSymMetaProp *prop; /* scan the list of properties and write each one */ for (prop = meta->sym->get_prop_head() ; prop != 0 ; prop = prop->nxt_) { /* write this item's property */ image_writer->write_meta_item_prop(prop->prop_->get_prop()); } } } /* end the metaclass dependency table */ image_writer->end_meta_dep(); /* write the code pool streams (don't bother masking the code bytes) */ cs_list[0] = G_cs_main; cs_list[1] = G_cs_static; code_layout_.write_to_image(cs_list, 2, image_writer, 1, 0); /* * write the constant pool (applying the constant pool data mask to * obscure any text strings in the data) */ const_layout_.write_to_image(&G_ds, 1, image_writer, 2, data_xor_mask); /* write the "TADS object" data */ write_tads_objects_to_image(G_os, image_writer, TCT3_METAID_TADSOBJ); /* write the intrinsic class modifier object data */ write_tads_objects_to_image(G_icmod_stream, image_writer, TCT3_METAID_ICMOD); /* write the dictionary data - this is a stream of dictionary objects */ write_nontads_objs_to_image(G_dict_stream, image_writer, TCT3_METAID_DICT, TRUE); /* write the grammar data - this is a stream of production objects */ write_nontads_objs_to_image(G_gramprod_stream, image_writer, TCT3_METAID_GRAMPROD, TRUE); /* if we have any BigNumber data, write it out */ if (G_bignum_stream->get_ofs() != 0) write_nontads_objs_to_image(G_bignum_stream, image_writer, bignum_idx, FALSE); /* if we have any RexPattern data, write it out */ if (G_rexpat_stream->get_ofs() != 0) write_nontads_objs_to_image(G_rexpat_stream, image_writer, rexpat_idx, FALSE); /* if we have any IntrinsicClass data, write it out */ if (G_int_class_stream->get_ofs() != 0) write_nontads_objs_to_image(G_int_class_stream, image_writer, int_class_idx, FALSE); /* write the static initializer list */ write_static_init_list(image_writer, first_static_code_page * code_layout_.page_size_); /* write debug information if desired */ if (G_debug) { /* write the source file table */ write_sources_to_image(image_writer); /* write the global symbol table to the image file */ write_global_symbols_to_image(image_writer); /* write the method header list */ write_method_list_to_image(image_writer); /* write the macro records */ write_macros_to_image(image_writer); } /* finish the image file */ image_writer->finish(); /* delete our image writer */ delete image_writer; image_writer = 0; } /* ------------------------------------------------------------------------ */ /* * Write an export record for an operator overload property */ void CTcGenTarg::write_op_export(CVmImageWriter *image_writer, CTcSymProp *prop) { if (prop != 0 && prop->get_prop() != VM_INVALID_PROP) { image_writer->write_sym_item_propid( prop->get_sym(), prop->get_sym_len(), prop->get_prop()); } } /* ------------------------------------------------------------------------ */ /* * Write the static initializer ID list */ void CTcGenTarg::write_static_init_list(CVmImageWriter *image_writer, ulong main_cs_size) { ulong rem; ulong ofs; ulong init_cnt; /* * calculate the number of initializers - this is simply the size of * the stream divided by the size of each record (4 bytes for object * ID, 2 bytes for property ID) */ init_cnt = G_static_init_id_stream->get_ofs() / 6; /* add the multi-method initializer object, if there is one */ if (mminit_obj_ != VM_INVALID_OBJ) init_cnt += 1; /* start the block */ image_writer->begin_sini_block(main_cs_size, init_cnt); /* write the multi-method initializer object, if applicable */ if (mminit_obj_ != VM_INVALID_OBJ) { /* write the object data */ char buf[6]; oswp4(buf, mminit_obj_); /* the object ID */ oswp2(buf+4, 1); /* our arbitrary initializer property ID */ image_writer->write_bytes(buf, 6); } /* write the bytes */ for (ofs = 0, rem = G_static_init_id_stream->get_ofs() ; rem != 0 ; ) { const char *ptr; ulong cur; /* get the next chunk */ ptr = G_static_init_id_stream->get_block_ptr(ofs, rem, &cur); /* write this chunk */ image_writer->write_bytes(ptr, cur); /* advance past this chunk */ ofs += cur; rem -= cur; } /* end the block */ image_writer->end_sini_block(); } /* ------------------------------------------------------------------------ */ /* * Build synthesized code. This is called after all of the object files * are loaded and before we generate the final image file, to give the * linker a chance to generate any automatically generated code. We use * this to generate the stub base functions for multi-methods. */ struct mmstub_ctx { mmstub_ctx() { mmc = 0; cnt = 0; } /* _multiMethodCall function symbol */ CTcSymFunc *mmc; /* number of multi-method stubs we generated */ int cnt; }; void CTcGenTarg::build_synthesized_code() { mmstub_ctx ctx; /* look up the _multiMethodCall function */ ctx.mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find( "_multiMethodCall", 16); /* * our generated code isn't part of any object file - flag a new object * file so that we don't get confused into thinking this came from the * last object file loaded */ G_cs_static->set_object_file_start_ofs(); G_os->set_object_file_start_ofs(); /* build out the stubs for the multi-method base functions */ G_prs->get_global_symtab()->enum_entries(&multimethod_stub_cb, &ctx); /* * if we generated any stubs, we definitely need _multiMethodCall to be * defined - if it's not, it's an error */ if (ctx.cnt != 0 && (ctx.mmc == 0 || ctx.mmc->get_type() != TC_SYM_FUNC)) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, "_multiMethodCall"); } } /* callback context - build multi-method base function stubs */ void CTcGenTarg::multimethod_stub_cb(void *ctx0, CTcSymbol *sym) { mmstub_ctx *ctx = (mmstub_ctx *)ctx0; /* if this is a function, check to see if it's a multi-method stub */ if (sym->get_type() == TC_SYM_FUNC) { CTcSymFunc *fsym = (CTcSymFunc *)sym; /* * It's a base function if it's marked as a multi-method and it * doesn't have a '*' in its name. (If there's a '*', it's a * concrete multi-method rather than a base function.) */ if (fsym->is_multimethod()) { /* it's marked as a multi-method - check for a decorated name */ const char *p = sym->getstr(); size_t rem = sym->getlen(); for ( ; rem != 0 && *p != '*' ; ++p, --rem) ; if (rem == 0) { tct3_method_gen_ctx gen_ctx; /* * It's a multi-method base function - build out its stub. * The stub function is a varargs function with no fixed * parameters - i.e., funcName(...). Its body simply calls * _multiMethodCall with a pointer to itself as the base * function. */ G_cg->open_method(G_cs_main, fsym, fsym->get_fixup_list_anchor(), 0, 0, 0, 0, TRUE, FALSE, FALSE, FALSE, &gen_ctx); /* set the anchor in the function symbol */ fsym->set_anchor(gen_ctx.anchor); /* * turn the arguments into a list, leaving this on the * stack as the second argument for _multiMethodCall */ G_cg->write_op(OPC_PUSHPARLST); G_cs->write(0); G_cg->note_push(); /* push the function address argument */ CTcConstVal funcval; funcval.set_funcptr(fsym); CTPNConst cfunc(&funcval); cfunc.gen_code(FALSE, FALSE); G_cg->note_push(); /* * call _multiMethodCall, if it's defined (if not, the * caller will flag it as an error, so we don't need to * worry about that here - just skip generating the call) */ if (ctx->mmc != 0) ctx->mmc->gen_code_call(FALSE, 2, FALSE, FALSE); /* return the result */ G_cg->write_op(OPC_RETVAL); G_cg->note_pop(); /* finish the method */ G_cg->close_method(0, 0, 0, 0, &gen_ctx, 0); G_cg->close_method_cleanup(&gen_ctx); /* the stub symbol now has a definition */ fsym->set_extern(FALSE); /* count it */ ctx->cnt += 1; } } } } /* ------------------------------------------------------------------------ */ /* * Start a OBJS header for a TadsObject to a given stream. This only * writes the fixed part; the caller must then write the superclass list * and the property table. After the contents have been written, call * close_tadsobj() to finalize the header data. */ void CTcGenTarg::open_tadsobj(tct3_tadsobj_ctx *ctx, CTcDataStream *stream, vm_obj_id_t obj_id, int sc_cnt, int prop_cnt, unsigned int internal_flags, unsigned int vm_flags) { /* remember the stream in the context */ ctx->stream = stream; /* write the internal header */ stream->write2(internal_flags); /* note the start of the VM object data */ ctx->obj_ofs = stream->get_ofs(); /* write the fixed header data */ stream->write_obj_id(obj_id); /* object ID */ stream->write2(0); /* byte size placeholder - we'll fix up at "close" */ stream->write2(sc_cnt); /* superclass count */ stream->write2(prop_cnt); /* property count */ stream->write2(vm_flags); /* object flags */ } /* * Close a TadsObject header. This must be called after the object's * contents have been written so that we can fix up the header with the * actual data size. */ void CTcGenTarg::close_tadsobj(tct3_tadsobj_ctx *ctx) { /* fix up the object size data */ ctx->stream->write2_at(ctx->obj_ofs + 4, ctx->stream->get_ofs() - ctx->obj_ofs - 6); } /* ------------------------------------------------------------------------ */ /* * Linker support: ensure that the given intrinsic class has a modifier * object. If there's no modifier, we'll create one and add the code for * it to the object stream. * * This should only be called during the linking phase, after code * generation is completed. If you want to create a modifier during * compilation, you should instead use CTcParser::find_or_def_obj(), since * that creates the necessary structures for object file generation and * later linking. */ void CTcGenTarg::linker_ensure_mod_obj(CTcSymMetaclass *mc) { /* if there's no modifier object, create one */ if (mc->get_mod_obj() == 0) { /* create a modifier object */ CTcSymObj *mod_sym = CTcSymObj::synthesize_modified_obj_sym(FALSE); /* set it to be an IntrinsicClassModifier object */ mod_sym->set_metaclass(TC_META_ICMOD); /* link the modifier to the metaclass */ mc->set_mod_obj(mod_sym); /* * generate the object data - this is simply an empty object with * no superclasses, and it goes in the intrinsic class modifier * stream */ tct3_tadsobj_ctx obj_ctx; G_cg->open_tadsobj( &obj_ctx, G_icmod_stream, mod_sym->get_obj_id(), 0, 0, 0, 0); G_cg->close_tadsobj(&obj_ctx); } } /* * Ensure that the given intrinsic class has a modifier object, by name. */ void CTcGenTarg::linker_ensure_mod_obj(const char *name, size_t len) { /* look up the symbol */ CTcSymMetaclass *mc = (CTcSymMetaclass *)G_prs->get_global_symtab() ->find(name, len); /* if we found the metaclass symbol, add a modifier if needed */ if (mc != 0 && mc->get_type() == TC_SYM_METACLASS) linker_ensure_mod_obj(mc); } /* ------------------------------------------------------------------------ */ /* * Build the multi-method initializers */ /* enumerator callback context */ struct mminit_ctx { mminit_ctx() { mmr = 0; cnt = 0; } /* _multiMethodRegister function symbol */ CTcSymFunc *mmr; /* number of multi-method registrations we generated */ int cnt; }; /* main initializer builder */ void CTcGenTarg::build_multimethod_initializers() { tct3_method_gen_ctx genctx; mminit_ctx ctx; /* look up the _multiMethodRegister function */ ctx.mmr = (CTcSymFunc *)G_prs->get_global_symtab()->find( "_multiMethodRegister", 20); /* * open the method - it's a static initializer, so write it to the * static stream */ G_cg->open_method(G_cs_static, 0, 0, 0, 0, 0, 0, FALSE, FALSE, FALSE, FALSE, &genctx); /* scan the symbol table for multimethods and generate initializers */ G_prs->get_global_symtab()->enum_entries(&multimethod_init_cb, &ctx); /* * if we found any multi-methods, generate a call to * _multiMethodBuildBindings */ if (ctx.cnt != 0) { /* look up the function - it's an error if it's not defined */ CTcSymFunc *mmb = (CTcSymFunc *)G_prs->get_global_symtab()->find( "_multiMethodBuildBindings", 25); if (mmb == 0 || mmb->get_type() != TC_SYM_FUNC) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, "_multiMethodBuildBindings"); return; } /* write the call instruction */ G_cg->write_op(OPC_CALL); G_cs->write(0); /* argument count */ mmb->add_abs_fixup(G_cs); /* function address fixup */ G_cs->write4(0); /* fixup placeholder */ } /* close the method and clean up */ G_cg->close_method(0, 0, 0, 0, &genctx, 0); G_cg->close_method_cleanup(&genctx); /* * if we generated any registrations, create the initializer object - * this will go in the static initializer block to trigger invocation * of the registration routine at load time */ if (ctx.cnt != 0) { /* we have multi-methods, so we definitely need _multiMethodRegister */ if (ctx.mmr == 0 || ctx.mmr->get_type() != TC_SYM_FUNC) { G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_MISSING_MMREG, "_multiMethodRegister"); return; } /* create an anonymous object to hold the initializer code */ mminit_obj_ = G_cg->new_obj_id(); /* add a debugging symbol for it */ G_prs->get_global_symtab()->add_entry(new CTcSymObj( "", 17, FALSE, mminit_obj_, TRUE, TC_META_TADSOBJ, 0)); /* write the object header: no superclasses, 1 property */ tct3_tadsobj_ctx obj_ctx; open_tadsobj(&obj_ctx, G_os, mminit_obj_, 0, 1, 0, 0); /* write the static initializer property */ G_os->write2(1); /* arbitrary property ID */ G_os->write(VM_CODEOFS); /* offset of the code we just generated */ CTcAbsFixup::add_abs_fixup( &genctx.anchor->fixup_info_.internal_fixup_head_, G_os, G_os->get_ofs()); G_os->write4(0); /* placeholder */ /* fix up the object size data */ close_tadsobj(&obj_ctx); } /* switch back to the main stream */ G_cs = G_cs_main; } /* callback context - build multi-method registration calls */ void CTcGenTarg::multimethod_init_cb(void *ctx0, CTcSymbol *sym) { mminit_ctx *ctx = (mminit_ctx *)ctx0; /* if this is a function, check to see if it's a multi-method instance */ if (sym->get_type() == TC_SYM_FUNC) { CTcSymFunc *fsym = (CTcSymFunc *)sym; /* * multi-method instances have names of the form * "name*type1,type2", so check the name to see if it fits the * pattern */ const char *p = sym->getstr(); size_t rem = sym->getlen(); int is_mm = FALSE; for ( ; rem != 0 ; ++p, --rem) { /* * if we found a '*', it's a multimethod; otherwise, if it's * anything other than a symbol character, it's not a * multimethod */ if (*p == '*') { is_mm = TRUE; break; } else if (!is_sym(*p)) break; } /* * If it's a multi-method symbol, build the initializer. If it's * the base function for a multi-method, build out the stub * function. */ if (is_mm) { int argc; /* note the function base name - it's the part up to the '*' */ const char *funcname = sym->getstr(); size_t funclen = (size_t)(p - funcname); /* look up the base function symbol */ CTcSymFunc *base_sym = (CTcSymFunc *)G_prs->get_global_symtab() ->find(funcname, funclen); /* if it's not defined as a function, ignore it */ if (base_sym == 0 || base_sym->get_type() != TC_SYM_FUNC) return; /* * skip to the end of the string, and remove the '*' from the * length count */ p += rem; --rem; /* * Run through the decorated name and look up each mentioned * class. We need to push the parameters onto the stack in * reverse order to match the VM calling conventions. */ for (argc = 0 ; rem != 0 ; ++argc) { /* remember where the current name starts */ size_t plen; /* skip the terminator for this item */ --p, --rem; /* scan backwards to the previous delimiter */ for (plen = 0 ; rem != 0 && *(p-1) != ';' ; --p, --rem, ++plen) ; /* look up this name */ if (plen == 0) { /* * empty name - this slot accepts any type; represent * this in the run-time formal list with 'nil' */ G_cg->write_op(OPC_PUSHNIL); /* * An untyped slot is implicitly an Object slot, so we * need to make sure that Object can participate in the * binding property system by ensuring that it has a * modifier object. */ G_cg->linker_ensure_mod_obj("Object", 6); } else if (plen == 3 && memcmp(p, "...", 3) == 0) { /* * varargs indicator - represent this in the list with * the literal string '...' */ CTcConstVal val; val.set_sstr("...", 3); CTPNConst cval(&val); cval.gen_code(FALSE, FALSE); /* * a varargs slot is implicitly an Object slot, so make * sure Object has a modifier object */ G_cg->linker_ensure_mod_obj("Object", 6); } else { /* class name - look it up */ CTcSymbol *cl = G_prs->get_global_symtab()->find(p, plen); CTcConstVal val; /* * if it's missing, unresolved, or not an object, flag * an error */ if (cl == 0 || (cl->get_type() == TC_SYM_OBJ && ((CTcSymObj *)cl)->is_extern())) { G_tcmain->log_error( 0, 0, TC_SEV_ERROR, TCERR_UNDEF_SYM, (int)plen, p); return; } else if (cl->get_type() == TC_SYM_OBJ) { /* get the object information */ CTcSymObj *co = (CTcSymObj *)cl; val.set_obj(co->get_obj_id(), co->get_metaclass()); } else if (cl->get_type() == TC_SYM_METACLASS) { /* get the metaclass information */ CTcSymMetaclass *cm = (CTcSymMetaclass *)cl; val.set_obj(cm->get_class_obj(), TC_META_UNKNOWN); /* * If this metaclass doesn't have a modifier * object, create one for it. This is needed * because the run-time library's multi-method * implementation stores the method binding * information in properties of the parameter * objects. Since we're using this metaclass as a * parameter type, we'll need to write at least one * property to it. We can only write properties to * intrinsic class objects when they're equipped * with modifier objects. * * The presence of a modifier object has no effect * at all on performance for ordinary method call * operations on the intrinsic class, and the * modifier itself is just a bare object, so the * cost of creating this extra object is trivial. */ G_cg->linker_ensure_mod_obj(cm); } else { /* it's not a valid object type */ G_tcmain->log_error( 0, 0, TC_SEV_ERROR, TCERR_MMPARAM_NOT_OBJECT, (int)plen, p, (int)funclen, funcname); return; } /* * represent the object or class in the parameter list * with the object reference */ CTPNConst cval(&val); cval.gen_code(FALSE, FALSE); } /* note the value we pushed */ G_cg->note_push(); } /* build and push the list from the parameters */ if (argc <= 255) { G_cg->write_op(OPC_NEW1); G_cs->write((char)argc); } else { G_cg->write_op(OPC_NEW2); G_cs->write2(argc); } G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); G_cg->write_op(OPC_GETR0); G_cg->note_pop(argc); G_cg->note_push(); /* push the function pointer argument */ fsym->gen_code(FALSE); /* push the base function pointer argument */ base_sym->gen_code(FALSE); /* * call _multiMethodRegister, if it's available (if it's not, * our caller will flag this as an error, so just skip the code * generation here) */ if (ctx->mmr != 0) { G_cg->write_op(OPC_CALL); G_cs->write(3); /* argument count */ ctx->mmr->add_abs_fixup(G_cs); /* function address fixup */ G_cs->write4(0); /* fixup placeholder */ } /* the 3 arguments will be gone on return */ G_cg->note_pop(3); /* count the registration */ ctx->cnt += 1; } } } /* ------------------------------------------------------------------------ */ /* * Build the IntrinsicClass objects */ void CTcGenTarg::build_intrinsic_class_objs(CTcDataStream *str) { tc_meta_entry *meta; uint idx; /* * run through the dependency table, and create an IntrinsicClass * object for each entry */ for (idx = 0, meta = meta_head_ ; meta != 0 ; meta = meta->nxt, ++idx) { /* * if we have a symbol for this class, add the object to the * intrinsic class stream */ if (meta->sym != 0) { /* write the OBJS header */ str->write4(meta->sym->get_class_obj()); str->write2(8); /* * write the data - the data length (8), followed by the * intrinsic class index that this object is associated * with, followed by the modifier object */ str->write2(8); str->write2(idx); str->write4(meta->sym->get_mod_obj() == 0 ? VM_INVALID_OBJ : meta->sym->get_mod_obj()->get_obj_id()); /* * fix up the inheritance chain in the modifier objects, if * necessary */ meta->sym->fix_mod_obj_sc_list(); } } } /* ------------------------------------------------------------------------ */ /* * symbol table entry for a local variable debug entry */ class dbglcl: public CVmHashEntryCS { public: dbglcl(const char *str, size_t len, CTcStreamAnchor *anchor, ulong ofs) : CVmHashEntryCS(str, len, TRUE) { this->anchor = anchor; this->ofs = ofs; } CTcStreamAnchor *anchor; ulong ofs; }; /* * Build the local symbol records. */ void CTcGenTarg::build_local_symbol_records(CTcCodeStream *cs, CVmHashTable *tab) { CTcStreamAnchor *anchor; /* this only applies for debug format 2+ */ if (G_sizes.dbg_fmt_vsn < 2) return; /* go through the list of anchors in the code stream */ for (anchor = cs->get_first_anchor() ; anchor != 0 ; anchor = anchor->nxt_) { ulong start_ofs; ulong dbg_ofs; uint cnt; ulong ofs; /* get the anchor's stream offset */ start_ofs = anchor->get_ofs(); /* read the debug table offset from the method header */ dbg_ofs = start_ofs + cs->readu2_at(start_ofs + 8); /* if there's no debug table for this method, go on to the next */ if (dbg_ofs == start_ofs) continue; /* read the number of line entries */ ofs = dbg_ofs + G_sizes.dbg_hdr; cnt = cs->readu2_at(ofs); ofs += 2; /* skip past the line entries */ ofs += cnt * G_sizes.dbg_line; /* skip the end offset */ ofs += 2; /* read the frame count */ uint frame_cnt = cs->readu2_at(ofs); ofs += 2; /* skip the frame index */ ofs += 2 * frame_cnt; /* go through the individual frames */ for (uint fi = 0 ; fi < frame_cnt ; ++fi) { /* get the number of entries */ cnt = cs->readu2_at(ofs + 2); /* skip to the first entry */ ofs += G_sizes.dbg_frame; /* parse each entry */ for (uint i = 0 ; i < cnt ; ++i) { /* read the flags */ uint flags = cs->readu2_at(ofs + 2); ofs += G_sizes.lcl_hdr; /* * If this is an out-of-line entry, we currently have a * pointer to the local variable stream, and we need to * move it to the constant pool instead. If it's in-line, * there's nothing to do - we just skip the record. */ if (flags & 0x0004) { /* * It's a constant pool entry. Read the name from the * local variable pool. */ ulong lofs = cs->readu4_at(ofs); size_t nlen = G_lcl_stream->readu2_at(lofs); if (nlen <= TOK_SYM_MAX_LEN) { /* read the name */ char nbuf[TOK_SYM_MAX_LEN + 1]; G_lcl_stream->copy_to_buf(nbuf, lofs + 2, nlen); /* * Look up the name in our symbol table, to see if * we've already defined it. If so, re-use the * same name. A few local variable names tend to * be used over and over, so it saves a lot of * space to share one copy for each instance of a * reused name. */ dbglcl *l = (dbglcl *)tab->find(nbuf, nlen); if (l == 0) { /* add an anchor for the constant pool entry */ CTcStreamAnchor *anchor = G_ds->add_anchor(0, 0); /* * It's not already defined. Add a new symbol * table entry for it. The entry will go at * the current constant pool stream offset. */ l = new dbglcl(nbuf, nlen, anchor, G_ds->get_ofs()); tab->add(l); /* copy the name to the constant pool stream */ G_ds->write2(nlen); G_ds->write(nbuf, nlen); } /* add a fixup for this pointer in the code stream */ CTcAbsFixup::add_abs_fixup( l->anchor->fixup_list_head_, cs, ofs); /* * Overwrite the local stream offset with the * constant pool offset of the symbol. */ cs->write4_at(ofs, l->ofs); } /* skip the record */ ofs += 4; } else { /* it's in-line - just skip to the next record */ ofs += cs->readu2_at(ofs) + 2; } } } } } /* ------------------------------------------------------------------------ */ /* * Build the source file line maps. These maps provide listings from * the source location to the executable location, so the debugger can * do things such as set a breakpoint at a given source file location. */ void CTcGenTarg::build_source_line_maps() { CTcStreamAnchor *anchor; /* go through the list of anchors in the code stream */ for (anchor = G_cs->get_first_anchor() ; anchor != 0 ; anchor = anchor->nxt_) { ulong start_ofs; ulong start_addr; ulong dbg_ofs; uint cnt; ulong ofs; /* get the anchor's stream offset */ start_ofs = anchor->get_ofs(); /* get the anchor's absolute address in the image file */ start_addr = anchor->get_addr(); /* read the debug table offset from the method header */ dbg_ofs = start_ofs + G_cs->readu2_at(start_ofs + 8); /* if there's no debug table for this method, go on to the next */ if (dbg_ofs == start_ofs) continue; /* read the number of line entries */ cnt = G_cs->readu2_at(dbg_ofs + G_sizes.dbg_hdr); /* go through the individual line entries */ for (ofs = dbg_ofs + G_sizes.dbg_hdr + 2 ; cnt != 0 ; --cnt, ofs += G_sizes.dbg_line) { uint file_id; ulong linenum; uint method_ofs; ulong line_addr; CTcTokFileDesc *file_desc; /* * get the file position, and the byte-code offset from the * start of the method of the executable code for the line */ method_ofs = G_cs->readu2_at(ofs); file_id = G_cs->readu2_at(ofs + 2); linenum = G_cs->readu4_at(ofs + 4); /* calculate the absolute address of the line in the image file */ line_addr = start_addr + method_ofs; /* find the given file descriptor */ file_desc = G_tok->get_filedesc(file_id); /* * get the original file descriptor, since we always want to * add to the original, not to the duplicates, if the file * appears more than once (because this is a one-way mapping * from file to byte-code location - we thus require a * single index) */ if (file_desc->get_orig() != 0) file_desc = file_desc->get_orig(); /* add this line to the file descriptor */ file_desc->add_source_line(linenum, line_addr); } } } /* ------------------------------------------------------------------------ */ /* * Callback to write enumerated source lines to an image file */ static void write_source_lines_cb(void *ctx, ulong linenum, ulong code_addr) { CVmImageWriter *image_writer; /* get the image writer */ image_writer = (CVmImageWriter *)ctx; /* write the data */ image_writer->write_srcf_line_entry(linenum, code_addr); } /* * Write the list of source file descriptors to an image file */ void CTcGenTarg::write_sources_to_image(CVmImageWriter *image_writer) { CTcTokFileDesc *desc; /* write the block prefix */ image_writer->begin_srcf_block(G_tok->get_filedesc_count()); /* write the entries */ for (desc = G_tok->get_first_filedesc() ; desc != 0 ; desc = desc->get_next()) { const char *fname; /* * Get the filename. Use the fully resolved local filename, so * that the debugger can correlate the resolved file back to the * project configuration. This ties the debug records to the local * directory structure, but the only drawback of this is that the * program must be recompiled wherever it's to be used with the * debugger. */ fname = desc->get_fname(); /* * if we're in test reporting mode, write only the root name, not * the full name - this insulates test logs from the details of * local pathname conventions and the local directory structure, * allowing for more portable test logs */ if (G_tcmain->get_test_report_mode()) fname = os_get_root_name((char *)fname); /* begin this entry */ image_writer->begin_srcf_entry(desc->get_orig_index(), fname); /* write the source lines */ desc->enum_source_lines(write_source_lines_cb, image_writer); /* end this entry */ image_writer->end_srcf_entry(); } /* end the block */ image_writer->end_srcf_block(); } /* * Write the method header list to the image file */ void CTcGenTarg::write_method_list_to_image(CVmImageWriter *image_writer) { CTcStreamAnchor *anchor; /* begin the method header list block in the image file */ image_writer->begin_mhls_block(); /* go through the list of anchors in the code stream */ for (anchor = G_cs->get_first_anchor() ; anchor != 0 ; anchor = anchor->nxt_) { /* write this entry's code pool address */ image_writer->write_mhls_entry(anchor->get_addr()); } /* end the block */ image_writer->end_mhls_block(); } /* * Write the preprocessor macros to the image file, for debugger use */ void CTcGenTarg::write_macros_to_image(CVmImageWriter *image_writer) { /* begin the macro block */ image_writer->begin_macr_block(); /* * ask the tokenizer to dump the data directly to the file underlying * the image writer */ G_tok->write_macros_to_file_for_debug(image_writer->get_fp()); /* end the macro block */ image_writer->end_macr_block(); } /* ------------------------------------------------------------------------ */ /* * Callback context for global symbol table writer */ struct write_sym_to_image_cb { /* number of symbols written */ ulong count; /* the image writer */ CVmImageWriter *image_writer; }; /* * Callback for writing the global symbol table to an object file */ static void write_sym_to_image(void *ctx0, CTcSymbol *sym) { write_sym_to_image_cb *ctx; /* cast the context */ ctx = (write_sym_to_image_cb *)ctx0; /* * If the symbol's name starts with a period, don't write it - the * compiler constructs certain private symbol names for its own * internal use, and marks them as such by starting the name with a * period. These symbols cannot be used to evaluate expressions, so * they're of no use in teh global symbol table in the image file. */ if (sym->get_sym()[0] == '.') return; /* ask the symbol to do the work */ if (sym->write_to_image_file_global(ctx->image_writer)) { /* we wrote the symbol - count it */ ++(ctx->count); } } /* * Write the global symbol table to an object file */ void CTcGenTarg::write_global_symbols_to_image(CVmImageWriter *image_writer) { write_sym_to_image_cb ctx; /* set up the callback context */ ctx.count = 0; ctx.image_writer = image_writer; /* start the block */ image_writer->begin_gsym_block(); /* ask the symbol table to enumerate itself through our symbol writer */ G_prs->get_global_symtab()->enum_entries(&write_sym_to_image, &ctx); /* end the block */ image_writer->end_gsym_block(ctx.count); } /* ------------------------------------------------------------------------ */ /* * Look up a property */ vm_prop_id_t CTcGenTarg::look_up_prop(const char *propname, int required, int err_if_undef, int err_if_not_prop) { /* look up the symbol */ CTcSymbol *sym = G_prs->get_global_symtab()->find(propname); /* check to see if it's defined and of the proper type */ if (sym == 0) { /* log the 'undefined' error */ G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC, err_if_undef); } else if (sym->get_type() != TC_SYM_PROP) { /* log the 'not a property' error */ G_tcmain->log_error(0, 0, required ? TC_SEV_ERROR : TC_SEV_PEDANTIC, err_if_not_prop); } else { /* return the property ID */ return ((CTcSymProp *)sym)->get_prop(); } /* if we got here, we didn't find a valid property */ return VM_INVALID_PROP; } /* ------------------------------------------------------------------------ */ /* * Write a TADS object stream to the image file. We'll write blocks of * size up to somewhat less than 64k, to ensure that the file is usable on * 16-bit machines. */ void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os, CVmImageWriter *image_writer, int meta_idx) { /* write the persistent (non-transient) objects */ write_tads_objects_to_image(os, image_writer, meta_idx, FALSE); /* write the transient objects */ write_tads_objects_to_image(os, image_writer, meta_idx, TRUE); } /* * Write the TADS object stream to the image file, writing only persistent * or transient objects. */ void CTcGenTarg::write_tads_objects_to_image(CTcDataStream *os, CVmImageWriter *image_writer, int meta_idx, int trans) { ulong start_ofs; /* keep going until we've written the whole file */ for (start_ofs = 0 ; start_ofs < os->get_ofs() ; ) { ulong ofs; uint siz; uint cnt; uint block_size; /* * Scan the stream. Each entry in the stream is a standard * object record, which means that it starts with the object ID * (UINT4) and the length (UINT2) of the metaclass-specific * data, which is then followed by the metaclass data. Skip as * many objects as we can while staying within our approximately * 64k limit. */ for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; ) { uint flags; ulong rem_len; size_t orig_prop_cnt; size_t write_prop_cnt; size_t write_size; ulong next_ofs; ulong orig_ofs; /* if we've reached the end of the stream, we're done */ if (ofs >= os->get_ofs()) break; /* remember the starting offset */ orig_ofs = ofs; /* read our internal flags */ flags = os->readu2_at(ofs + TCT3_OBJ_INTERNHDR_FLAGS_OFS); /* * get the size of this block - this is the * metaclass-specific data size at offset 4 in the T3 * metaclass header, plus the size of the T3 metaclass * header, plus the size of our internal header */ siz = TCT3_OBJ_INTERNHDR_SIZE + TCT3_META_HEADER_SIZE + os->readu2_at(ofs + TCT3_META_HEADER_OFS + 4); /* * Calculate the offset of the next block. Note that this is * the current offset plus the original block size; the amount * of data we end up writing might be less than the original * block size because we might have deleted property slots * when we sorted and compressed the property table. */ next_ofs = ofs + siz; /* if this object was deleted, skip it */ if ((flags & TCT3_OBJ_REPLACED) != 0) { ofs = next_ofs; continue; } /* * if this object is of the wrong persistent/transient type, * skip it */ if (((flags & TCT3_OBJ_TRANSIENT) != 0) != (trans != 0)) { ofs = next_ofs; continue; } /* * if this would push us over the limit, stop here and start a * new block */ if (block_size + siz > 64000L) break; /* * We must sort the property table, in order of ascending * property ID, before we write the image file. We had to * wait until now to do this, because the final property ID * assignments aren't made until link time. */ write_prop_cnt = sort_object_prop_table(os, ofs); /* note the original property count */ orig_prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, ofs); /* * Then temporarily pdate the property count in the stream, in * case we changed it in the sorting process. * * Calculate the new size of the data to write. Note that we * must add in the size of the T3 metaclass header, since this * isn't reflected in the data size. */ write_size = CTPNStmObject::set_stream_prop_cnt(os, ofs, write_prop_cnt) + TCT3_META_HEADER_SIZE; /* * if this is the first object in this block, write the * block header */ if (cnt == 0) image_writer->begin_objs_block(meta_idx, FALSE, trans); /* * skip past our internal header - we don't want to write * our internal header to the image file, since this was * purely for our own use in the compiler and linker */ ofs += TCT3_OBJ_INTERNHDR_SIZE; /* * write the object data; write the size returned from * sorting the property table, which might be different than * the original block data size in the stream, because we * might have compressed the property table */ for (rem_len = write_size ; rem_len != 0 ; ) { const char *p; ulong avail_len; /* get the next block */ p = os->get_block_ptr(ofs, rem_len, &avail_len); /* write it out */ image_writer->write_objs_bytes(p, avail_len); /* move past this block */ ofs += avail_len; rem_len -= avail_len; } /* count the object */ ++cnt; /* restore the original stream property count */ CTPNStmObject::set_stream_prop_cnt(os, orig_ofs, orig_prop_cnt); /* move on to the next block */ ofs = next_ofs; } /* if we wrote any objects, end the block */ if (cnt != 0) image_writer->end_objs_block(cnt); /* move on to the next block */ start_ofs = ofs; } } /* ------------------------------------------------------------------------ */ /* * Write an object stream of non-TADS objects to the image file */ void CTcGenTarg::write_nontads_objs_to_image(CTcDataStream *os, CVmImageWriter *image_writer, int meta_idx, int large_objs) { /* keep going until we've written the whole file */ for (ulong start_ofs = 0 ; start_ofs < os->get_ofs() ; ) { ulong ofs; uint siz; uint cnt; uint block_size; /* * Scan the stream. Each entry in the stream is either a small or * large object record, which means that it starts with the object * ID (UINT4) and the length (UINT2 for small, UINT4 for large) of * the metaclass-specific data, which is then followed by the * metaclass data. * * Include as many objects as we can while staying within our * approximately 64k limit, if this is a small-format block; fill * the block without limit if this is a large-format block. */ for (block_size = 0, ofs = start_ofs, cnt = 0 ; ; ) { ulong rem_len; ulong next_ofs; /* if we've reached the end of the stream, we're done */ if (ofs >= os->get_ofs()) break; /* * get the size of this block - this is the * metaclass-specific data size at offset 4 in the T3 * metaclass header, plus the size of the T3 metaclass * header */ if (large_objs) { /* * Get the 32-bit size value. Note that we don't worry * about limiting the overall block size to 64k when we're * writing a "large" object block. */ siz = (ulong)os->readu4_at(ofs + 4) + TCT3_LARGE_META_HEADER_SIZE; } else { /* get the 16-bit size value */ siz = (ulong)os->read2_at(ofs + 4) + TCT3_META_HEADER_SIZE; /* * Since this is a small-object block, limit the aggregate * size of the entire block to 64k. So, if this block * would push us over the 64k aggregate for the block, * start a new OBJS block with this object. */ if (cnt != 0 && block_size + siz > 64000L) break; } /* * if this is the first object in this block, write the * block header - the dictionary uses large object headers, * so note that */ if (cnt == 0) image_writer->begin_objs_block(meta_idx, large_objs, FALSE); /* calculate the offset of the next block */ next_ofs = ofs + siz; /* write the object data */ for (rem_len = siz ; rem_len != 0 ; ) { const char *p; ulong avail_len; /* get the next block */ p = os->get_block_ptr(ofs, rem_len, &avail_len); /* write it out */ image_writer->write_objs_bytes(p, avail_len); /* move past this block */ ofs += avail_len; rem_len -= avail_len; } /* count the object */ ++cnt; /* move on to the next block */ ofs = next_ofs; } /* if we wrote any objects, end the block */ if (cnt != 0) image_writer->end_objs_block(cnt); /* move on to the next block */ start_ofs = ofs; } } /* ------------------------------------------------------------------------ */ /* * Property comparison callback function for qsort() when invoked from * sort_object_prop_table() */ //extern "C" int prop_compare(const void *p1, const void *p2); extern "C" { static int prop_compare(const void *p1, const void *p2) { uint id1, id2; /* get the ID's */ id1 = osrp2(p1); id2 = osrp2(p2); /* compare them and return the result */ return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1); } } /* * Sort an object's property table. This puts the property table into * order of ascending property ID, and deletes any unused properties from * the table. * * Note that we do NOT update the stream to indicate the reduced number of * properties if we delete any properties. Instead, we simply return the * new number of properties. */ size_t CTcGenTarg::sort_object_prop_table(CTcDataStream *os, ulong start_ofs) { /* read the number of properties from the header */ uint prop_cnt = CTPNStmObject::get_stream_prop_cnt(os, start_ofs); /* calculate the property table size */ uint prop_table_size = prop_cnt * TCT3_TADSOBJ_PROP_SIZE; /* get the offset of the first property */ ulong prop_ofs = CTPNStmObject::get_stream_first_prop_ofs(os, start_ofs); /* reallocate the sort buffer if necessary */ if (prop_table_size > sort_buf_size_) { /* increase the sort buffer size to the next 4k increment */ sort_buf_size_ = (prop_table_size + 4095) & ~4096; /* reallocate the buffer */ sort_buf_ = (char *)t3realloc(sort_buf_, sort_buf_size_); if (sort_buf_ == 0 || sort_buf_size_ < prop_table_size) G_tok->throw_internal_error(TCERR_CODEGEN_NO_MEM); } /* extract the table into our buffer */ os->copy_to_buf(sort_buf_, prop_ofs, prop_table_size); /* * Compress the table by removing any properties that have been * marked as deleted -- if we had any 'modify + replace' properties * that we resolved at link time, we will have marked those * properties for deletion by setting their property ID's to zero in * the table. Scan the table for any such properties and remove * them now. */ size_t src, dst; for (src = dst = 0, prop_cnt = 0 ; src < prop_table_size ; src += TCT3_TADSOBJ_PROP_SIZE) { /* if this property isn't marked for deletion, keep it */ if (osrp2(sort_buf_ + src) != VM_INVALID_PROP) { /* * we're keeping it - if we can move it to a lower table * position, copy the data to the new position, otherwise * leave it alone */ if (src != dst) memcpy(sort_buf_ + dst, sort_buf_ + src, TCT3_TADSOBJ_PROP_SIZE); /* * advance the destination pointer past this slot, since * we're going to keep the data in the slot */ dst += TCT3_TADSOBJ_PROP_SIZE; /* count this property, since we're keeping it */ ++prop_cnt; } } /* sort the table */ qsort(sort_buf_, prop_cnt, TCT3_TADSOBJ_PROP_SIZE, &prop_compare); /* add back any unused slots after all of the sorted slots */ for ( ; dst < prop_table_size ; dst += TCT3_TADSOBJ_PROP_SIZE) oswp2(sort_buf_ + dst, VM_INVALID_PROP); /* put the sorted table back in the buffer */ os->write_at(prop_ofs, sort_buf_, prop_table_size); /* return the (possibly reduced) number of properties */ return prop_cnt; } /* * callback context for enumerating a dictionary */ struct enum_dict_ctx { /* number of entries written so far */ uint cnt; }; /* * Generate code for a dictionary object */ void CTcGenTarg::gen_code_for_dict(CTcDictEntry *dict) { long size_ofs; long entry_cnt_ofs; long end_ofs; enum_dict_ctx ctx; /* * Write the OBJS header - object ID plus byte count for * metaclass-specific data (use a placeholder length for now) */ G_dict_stream->write4(dict->get_sym()->get_obj_id()); size_ofs = G_dict_stream->get_ofs(); G_dict_stream->write4(0); /* * Write the metaclass-specific data for the 'dictionary' metaclass */ /* write a nil comparator object initially */ G_dict_stream->write4(0); /* write a placeholder for the entry count */ entry_cnt_ofs = G_dict_stream->get_ofs(); G_dict_stream->write2(0); /* write the dictionary entries */ ctx.cnt = 0; dict->get_hash_table()->enum_entries(&enum_dict_gen_cb, &ctx); /* remember the ending offset of the table */ end_ofs = G_dict_stream->get_ofs(); /* go back and fix up the total size of the object data */ G_dict_stream->write4_at(size_ofs, end_ofs - size_ofs - 4); /* fix up the dictionary entry count */ G_dict_stream->write2_at(entry_cnt_ofs, ctx.cnt); } /* * Callback - enumerate dictionary entries for code generation */ void CTcGenTarg::enum_dict_gen_cb(void *ctx0, CVmHashEntry *entry0) { enum_dict_ctx *ctx = (enum_dict_ctx *)ctx0; CVmHashEntryPrsDict *entry = (CVmHashEntryPrsDict *)entry0; char buf[255]; size_t len; char *p; size_t rem; uint cnt; CTcPrsDictItem *item; /* count this entry */ ++(ctx->cnt); /* limit the key length to 255 bytes */ len = entry->getlen(); if (len > 255) len = 255; /* copy the entry to our buffer */ memcpy(buf, entry->getstr(), len); /* apply the XOR obfuscation to the key text */ for (p = buf, rem = len ; rem != 0 ; ++p, --rem) *p ^= 0xBD; /* write the length of the key followed by the key string */ G_dict_stream->write((uchar)len); G_dict_stream->write(buf, len); /* count the items in this entry */ for (cnt = 0, item = entry->get_list() ; item != 0 ; ++cnt, item = item->nxt_) ; /* write the number of entries */ G_dict_stream->write2(cnt); /* write the entries */ for (item = entry->get_list() ; item != 0 ; item = item->nxt_) { /* write the object ID and property ID of this entry */ G_dict_stream->write4(item->obj_); G_dict_stream->write2(item->prop_); } } /* * Generate code for a grammar production */ void CTcGenTarg::gen_code_for_gramprod(CTcGramProdEntry *prod) { long size_ofs; long end_ofs; uint cnt; CTcGramProdAlt *alt; CTcDataStream *str = G_gramprod_stream; /* * write the OBJS header - object ID plus byte count for * metaclass-specific data (use a placeholder length for now) */ str->write4(prod->get_prod_sym()->get_obj_id()); size_ofs = str->get_ofs(); str->write4(0); /* * Write the metaclass-specific data for the 'grammar-production' * metaclass */ /* count the alternatives */ for (cnt = 0, alt = prod->get_alt_head() ; alt != 0 ; ++cnt, alt = alt->get_next()) ; /* * If this production has no alternatives and was not explicitly * declared, flag an error indicating that the production is * undeclared. We treat this as an error because there's a good chance * that the an alternative referring to the production misspelled the * name. If the production was explicitly declared, then we have * sufficient confirmation that the name is correct, so no error is * indicated. */ if (cnt == 0 && !prod->is_declared()) G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_GRAMPROD_HAS_NO_ALTS, (int)prod->get_prod_sym()->get_sym_len(), prod->get_prod_sym()->get_sym()); /* * The count has to fit in 16 bits; it's surprisingly easy to exceed * this by using the power of permutation (with nested '|' * alternatives), so check for overflow and flag an error. Even though * it's not hard to exceed the limit, it's not desirable to create so * many permutations, so the limit isn't really in need of being * raised; it's better to rewrite a rule with a huge number of * permutations using sub-productions. */ if (cnt > 65535) G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_GRAMPROD_TOO_MANY_ALTS, (int)prod->get_prod_sym()->get_sym_len(), prod->get_prod_sym()->get_sym()); /* write the number of alternatives */ str->write2(cnt); /* write the alternatives */ for (alt = prod->get_alt_head() ; alt != 0 ; alt = alt->get_next()) { CTcGramProdTok *tok; /* write the score and badness for the alternative */ str->write2(alt->get_score()); str->write2(alt->get_badness()); /* write the processor object ID for this alternative */ str->write4(alt->get_processor_obj()->get_obj_id()); /* count the tokens in this alternative */ for (cnt = 0, tok = alt->get_tok_head() ; tok != 0 ; ++cnt, tok = tok->get_next()) ; /* write the token count */ str->write2(cnt); /* write the tokens */ for (tok = alt->get_tok_head() ; tok != 0 ; tok = tok->get_next()) { size_t idx; /* write the property association */ str->write2((uint)tok->get_prop_assoc()); /* write the token data */ switch(tok->get_type()) { case TCGRAM_PROD: /* write the type */ str->write((uchar)VMGRAM_MATCH_PROD); /* write the sub-production object ID */ str->write4((ulong)tok->getval_prod()->get_obj_id()); break; case TCGRAM_PART_OF_SPEECH: /* write the type */ str->write((uchar)VMGRAM_MATCH_SPEECH); /* write the part-of-speech property */ str->write2((uint)tok->getval_part_of_speech()); break; case TCGRAM_PART_OF_SPEECH_LIST: /* write the type */ str->write((uchar)VMGRAM_MATCH_NSPEECH); /* write the number of elements in the property list */ str->write2((uint)tok->getval_part_list_len()); /* write each element */ for (idx = 0 ; idx < tok->getval_part_list_len() ; ++idx) str->write2((uint)tok->getval_part_list_ele(idx)); /* done */ break; case TCGRAM_LITERAL: /* write the type */ str->write((uchar)VMGRAM_MATCH_LITERAL); /* write the string length prefix */ str->write2(tok->getval_literal_len()); /* write the string text */ str->write(tok->getval_literal_txt(), tok->getval_literal_len()); /* * add the word to the dictionary that was active when the * alternative was defined */ if (alt->get_dict() != 0) { /* * there's a dictionary - add the word, associating it * with the production object and with the parser's * miscVocab property */ alt->get_dict()->add_word( tok->getval_literal_txt(), tok->getval_literal_len(), FALSE, prod->get_prod_sym()->get_obj_id(), G_prs->get_miscvocab_prop()); } break; case TCGRAM_TOKEN_TYPE: /* write the type */ str->write((uchar)VMGRAM_MATCH_TOKTYPE); /* write the enum ID of the token */ str->write4(tok->getval_token_type()); break; case TCGRAM_STAR: /* write the type - there's no additional data */ str->write((uchar)VMGRAM_MATCH_STAR); break; default: assert(FALSE); break; } } } /* remember the ending offset of the object data */ end_ofs = str->get_ofs(); /* go back and fix up the total size of the object data */ str->write4_at(size_ofs, end_ofs - size_ofs - 4); } /* ------------------------------------------------------------------------ */ /* * Data Stream Layout Manager */ /* * calculate the size of the pool pages, given the size of the largest * single item */ void CTcStreamLayout::calc_layout(CTcDataStream *ds, ulong max_len, int is_first) { ulong rem; ulong free_ofs; CTcStreamAnchor *anchor; /* if this is the first page, handle some things specially */ if (is_first) { ulong pgsiz; /* * Starting at 2k, look for a page size that will fit the * desired minimum size. */ for (pgsiz = 2048 ; pgsiz < max_len ; pgsiz <<= 1) ; /* remember our selected page size */ page_size_ = pgsiz; /* start at the bottom of the first page */ rem = pgsiz; free_ofs = 0; page_cnt_ = 1; } else { /* * this isn't the first page - if there are no anchors, don't * bother adding anything */ if (ds->get_first_anchor() == 0) return; /* * start at the end of the last existing page - this will ensure * that everything added from the new stream will go onto a * brand new page after everything from the previous stream */ rem = 0; free_ofs = page_size_ * page_cnt_; } /* * Run through the list of stream anchors and calculate the layout. * For each item, assign its final pool address and apply its * fixups. */ for (anchor = ds->get_first_anchor() ; anchor != 0 ; anchor = anchor->nxt_) { ulong len; /* * if this anchor has been marked as replaced, don't include it * in our calculations, because we don't want to include this * block in the image file */ if (anchor->is_replaced()) continue; /* * if this item fits on the current page, assign it the next * sequential address; otherwise, go to the next page * * if this anchor is at the dividing point, put it on the next * page, unless we just started a new page */ len = anchor->get_len(ds); if (len > rem) { /* * we must start the next page - skip to the next page by * moving past the remaining free space on this page */ free_ofs += rem; /* count the new page */ ++page_cnt_; /* the whole next page is available to us now */ rem = page_size_; } /* * set the anchor's final address, which will apply fixups for * the object's fixup list */ anchor->set_addr(free_ofs); /* advance past this block */ free_ofs += len; rem -= len; } /* if there's no data at all, we have zero pages */ if (free_ofs == 0) page_cnt_ = 0; } /* * Write our stream to an image file */ void CTcStreamLayout::write_to_image(CTcDataStream **ds_arr, size_t ds_cnt, CVmImageWriter *image_writer, int pool_id, uchar xor_mask) { CTcStreamAnchor *anchor; ulong free_ofs; ulong next_page_start; int pgnum; /* write the constant pool definition block - the pool's ID is 2 */ image_writer->write_pool_def(pool_id, page_cnt_, page_size_, TRUE); /* * start out before the first page - the next page starts with the * item at offset zero */ pgnum = 0; next_page_start = 0; /* run through each stream */ for ( ; ds_cnt != 0 ; ++ds_arr, --ds_cnt) { CTcDataStream *ds; /* get the current stream */ ds = *ds_arr; /* run through the anchor list for this stream */ for (anchor = ds->get_first_anchor() ; anchor != 0 ; anchor = anchor->nxt_) { ulong len; ulong stream_ofs; ulong addr; /* * if this anchor is marked as replaced, skip it entirely - * we omit replaced blocks from the image file, because * they're completely unreachable */ if (anchor->is_replaced()) continue; /* * if this item's assigned address is on the next page, move * to the next page */ len = anchor->get_len(ds); addr = anchor->get_addr(); if (addr == next_page_start) { /* if this isn't the first page, close the previous page */ if (pgnum != 0) image_writer->end_pool_page(); /* start the new page */ image_writer->begin_pool_page(pool_id, pgnum, TRUE, xor_mask); /* this item is at the start of the new page */ free_ofs = next_page_start; /* count the new page */ ++pgnum; /* calculate the address of the start of the next page */ next_page_start += page_size_; } /* advance past this block */ free_ofs += len; /* * write the data from the stream to the image file - we * must iterate over the chunks the code stream returns, * since it might not be able to return the entire block in * a single operation */ for (stream_ofs = anchor->get_ofs() ; len != 0 ; ) { ulong cur; const char *ptr; /* get the pointer to this chunk */ ptr = ds->get_block_ptr(stream_ofs, len, &cur); /* write this chunk */ image_writer->write_pool_page_bytes(ptr, cur, xor_mask); /* advance our pointers past this chunk */ len -= cur; stream_ofs += cur; } } } /* if we started a page, end it */ if (pgnum != 0) image_writer->end_pool_page(); } /* ------------------------------------------------------------------------ */ /* * Object Symbol subclass - image-file functions */ /* * mark the compiled data for the object as a 'class' object */ void CTcSymObj::mark_compiled_as_class() { uint flags; CTcDataStream *str; /* get the appropriate stream for generating the data */ str = get_stream(); /* get my original object flags */ flags = CTPNStmObject::get_stream_obj_flags(str, stream_ofs_); /* add in the 'class' flag */ flags |= TCT3_OBJFLG_CLASS; /* set the updated flags */ CTPNStmObject::set_stream_obj_flags(str, stream_ofs_, flags); } /* * Delete a property from our modified base classes */ void CTcSymObj::delete_prop_from_mod_base(tctarg_prop_id_t prop_id) { uint prop_cnt; uint i; CTcDataStream *str; /* get the correct data stream */ str = get_stream(); /* get the number of properties in the object */ prop_cnt = CTPNStmObject::get_stream_prop_cnt(str, stream_ofs_); /* find the property in our property table */ for (i = 0 ; i < prop_cnt ; ++i) { /* if this property ID matches, delete it */ if (CTPNStmObject::get_stream_prop_id(str, stream_ofs_, i) == prop_id) { /* delete the object by setting its ID to 'invalid' */ CTPNStmObject::set_stream_prop_id(str, stream_ofs_, i, VM_INVALID_PROP); /* * there's no need to look any further - a property can * occur only once in an object */ break; } } } /* * Build the dictionary */ void CTcSymObj::build_dictionary() { uint prop_cnt; uint i; /* * Inherit the default handling - this will explicitly add all * superclass dictionary data into my own internal dictionary list, * so that we don't have to worry at all about superclasses here. * This will also add our words to my associated dictionary object. */ CTcSymObjBase::build_dictionary(); /* if I'm not a regular tads object, there's nothing to do here */ if (metaclass_ != TC_META_TADSOBJ) return; /* * Examine my properties. Each time we find a property whose value * is set to vocab-list, replace it with an actual list of strings * for my vocabulary words associated with the property. */ /* get the number of properties in the object */ prop_cnt = CTPNStmObject::get_stream_prop_cnt(G_os, stream_ofs_); /* find the property in our property table */ for (i = 0 ; i < prop_cnt ; ++i) { CTcConstVal val; vm_datatype_t prop_type; /* get this property value */ prop_type = CTPNStmObject::get_stream_prop_type(G_os, stream_ofs_, i); /* * if it's a vocabulary list placeholder, replace it with the * actual list of vocabulary strings */ if (prop_type == VM_VOCAB_LIST) { vm_prop_id_t prop_id; CTcVocabEntry *entry; CTPNList *lst; ulong prop_val_ofs; /* get the property ID */ prop_id = CTPNStmObject::get_stream_prop_id(G_os, stream_ofs_, i); /* get the value offset of this property */ prop_val_ofs = CTPNStmObject:: get_stream_prop_val_ofs(G_os, stream_ofs_, i); /* create a list */ lst = new CTPNList(); /* * scan my internal vocabulary list and add the entries * associated with this property */ for (entry = vocab_ ; entry != 0 ; entry = entry->nxt_) { /* if this one matches our property, add it */ if (entry->prop_ == prop_id) { CTcConstVal str_val; CTcPrsNode *ele; /* create a string element */ str_val.set_sstr(entry->txt_, entry->len_); ele = new CTPNConst(&str_val); /* add it to the list */ lst->add_element(ele); } } /* * Overwrite the original property value with the new list. * If the list is empty, this object doesn't define or * inherit any vocabulary of this property at all, so we can * clear the property entirely. */ if (lst->get_count() == 0) { /* * delete the property from the object by setting its * property ID to 'invalid' */ CTPNStmObject:: set_stream_prop_id(G_os, stream_ofs_, i, VM_INVALID_PROP); } else { /* write the list value to the property */ val.set_list(lst); G_cg->write_const_as_dh(G_os, prop_val_ofs, &val); } } } } /* ------------------------------------------------------------------------ */ /* * Symbol table entry routines for writing a symbol to the global symbol * table in the debug records in the image file */ /* * write the symbol to an image file's global symbol table */ int CTcSymFunc::write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* build our extra data buffer */ oswp4(buf, get_code_pool_addr()); oswp2(buf + 4, get_argc()); buf[6] = (is_varargs() != 0); buf[7] = (has_retval() != 0); oswp2(buf + 8, get_opt_argc()); /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_FUNC, buf, 10); /* we wrote the symbol */ return TRUE; } /* * write the symbol to an image file's global symbol table */ int CTcSymObj::write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* store our object ID in the extra data buffer */ oswp4(buf, obj_id_); /* add our modifying object ID, if we have a modifying object */ if (get_modifying_sym() != 0) oswp4(buf + 4, get_modifying_sym()->get_obj_id()); else oswp4(buf + 4, 0); /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_OBJ, buf, 8); /* we wrote the symbol */ return TRUE; } /* * write the symbol to an image file's global symbol table */ int CTcSymProp::write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* build our extra data buffer */ oswp2(buf, (uint)get_prop()); /* build our flags byte */ buf[2] = 0; if (vocab_) buf[2] |= 0x01; /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_PROP, buf, 3); /* we wrote the symbol */ return TRUE; } /* * write the symbol to an image file's global symbol table */ int CTcSymEnum::write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* build our extra data buffer */ oswp4(buf, get_enum_id()); /* build our flags */ buf[4] = 0; if (is_token_) buf[4] |= 0x01; /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_ENUM, buf, 5); /* we wrote the symbol */ return TRUE; } /* * write the symbol to an image file's global symbol table */ int CTcSymMetaclass:: write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* build our extra data buffer */ oswp2(buf, meta_idx_); oswp4(buf + 2, class_obj_); /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_METACLASS, buf, 6); /* we wrote the symbol */ return TRUE; } /* * Fix up the inheritance chain in the modifier objects */ void CTcSymMetaclass::fix_mod_obj_sc_list() { CTcSymObj *obj; CTcSymObj *obj_base; /* * go through our chain of modifier objects, and make sure the * stream data for each object points to its correct superclass */ for (obj = mod_obj_ ; obj != 0 ; obj = obj_base) { CTcDataStream *str; /* get the correct data stream */ str = obj->get_stream(); /* get the base object for this symbol */ obj_base = obj->get_mod_base_sym(); /* * if there's no base object, there's no superclass entry to * adjust for this object */ if (obj_base == 0) break; /* * set the superclass in this object to point to this base * object */ CTPNStmObject::set_stream_sc(str, obj->get_stream_ofs(), 0, obj_base->get_obj_id()); } } /* * write the symbol to an image file's global symbol table */ int CTcSymBif::write_to_image_file_global(class CVmImageWriter *image_writer) { char buf[128]; /* build our extra data buffer */ oswp2(buf, get_func_idx()); oswp2(buf + 2, get_func_set_id()); buf[4] = (has_retval() != 0); oswp2(buf + 5, get_min_argc()); oswp2(buf + 7, get_max_argc()); buf[9] = (is_varargs() != 0); /* write the data */ image_writer->write_gsym_entry(get_sym(), get_sym_len(), (int)TC_SYM_BIF, buf, 10); /* we wrote the symbol */ return TRUE; } /* * write the symbol to an image file's global symbol table */ int CTcSymExtfn::write_to_image_file_global(class CVmImageWriter *iw) { //$$$ to be implemented assert(FALSE); return FALSE; } /* ------------------------------------------------------------------------ */ /* * Object properties */ /* * Check locals */ void CTPNObjProp::check_locals() { /* check locals in our code body */ if (code_body_ != 0) code_body_->check_locals(); } frobtads-1.2.3/tads3/vmmcnet.h0000644000175000001440000000142711430454777015364 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/VMMCCORE.H,v 1.2 1999/05/17 02:52:28 MJRoberts Exp $ */ /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmmcnet.h - Networking Metaclass Registrations Function Notes Modified 12/01/98 MJRoberts - Creation */ /* * NOTE - this file is INTENTIONALLY not protected against multiple * inclusion. Because of the funny business involved in buildling the * registration tables, we must include this file more than once in * different configurations. Therefore, there's no #ifndef * VMMCCORE_INCLUDED test at all in this file. */ #include "vmhttpsrv.h" #include "vmhttpreq.h" frobtads-1.2.3/tads3/std.cpp0000644000175000001440000011466412145504453015037 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/STD.CPP,v 1.3 1999/07/11 00:46:52 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name std.cpp - T3 library functions Function Notes Modified 04/16/99 MJRoberts - Creation */ #include #include #include #include "os.h" #include "t3std.h" #include "utf8.h" #include "osifcnet.h" /* ------------------------------------------------------------------------ */ /* * Allocate space for a string of a given length. We'll add in space * for the null terminator. */ char *lib_alloc_str(size_t len) { char *buf; /* allocate the space */ buf = (char *)t3malloc(len + 1); /* make sure it's initially null-terminated */ buf[0] = '\0'; /* return the space */ return buf; } /* * Allocate space for a string of known length, and save a copy of the * string. The length does not include a null terminator, and in fact * the string does not need to be null-terminated. The copy returned, * however, is null-terminated. */ char *lib_copy_str(const char *str, size_t len) { /* if the source string is null, just return null as the result */ if (str == 0) return 0; /* allocate space */ char *buf = lib_alloc_str(len); /* if that succeeded, make a copy */ if (buf != 0) { /* copy the string */ memcpy(buf, str, len); /* null-terminate it */ buf[len] = '\0'; } /* return the buffer */ return buf; } /* * allocate and copy a null-terminated string */ char *lib_copy_str(const char *str) { return (str == 0 ? 0 : lib_copy_str(str, strlen(str))); } /* * Free a string previously allocated with lib_copy_str() */ void lib_free_str(char *buf) { if (buf != 0) t3free(buf); } /* ------------------------------------------------------------------------ */ /* * Utility routine: compare spaces, collapsing whitespace */ int lib_strequal_collapse_spaces(const char *a, size_t a_len, const char *b, size_t b_len) { const char *a_end; const char *b_end; utf8_ptr ap, bp; /* calculate where the strings end */ a_end = a + a_len; b_end = b + b_len; /* keep going until we run out of strings */ for (ap.set((char *)a), bp.set((char *)b) ; ap.getptr() < a_end && bp.getptr() < b_end ; ) { /* check to see if we have whitespace in both strings */ if (is_space(ap.getch()) && is_space(bp.getch())) { /* skip all whitespace in both strings */ for (ap.inc() ; ap.getptr() < a_end && is_space(ap.getch()) ; ap.inc()) ; for (bp.inc() ; bp.getptr() < b_end && is_space(bp.getch()) ; bp.inc()) ; /* keep going */ continue; } /* if the characters here don't match, we don't have a match */ if (ap.getch() != bp.getch()) return FALSE; /* move on to the next character of each string */ ap.inc(); bp.inc(); } /* * if both strings ran out at the same time, we have a match; * otherwise, they're not the same */ return (ap.getptr() == a_end && bp.getptr() == b_end); } /* ------------------------------------------------------------------------ */ /* * Utility routine: do a case-insensitive comparison of two UTF-8 strings. * Returns strcmp-style results: negative if a < b, 0 if a == b, positive * if a > b. * * If 'bmatchlen' is null, it means that the two strings must have the same * number of characters. Otherwise, we'll return 0 (equal) if 'a' is a * leading substring of 'b', and fill in '*bmatchlen' with the length in * bytes of the 'b' string that we matched. This might differ from the * length of the 'a' string because of case folding. To match as a leading * substring, we have to match to a character boundary. E.g., we won't * match "weis" as a leading substring of "weiß": while "weis" is indeed a * leading substring of "weiss", which is the case-folded version of * "weiß", it doesn't end at a character boundary in the original. */ int t3_compare_case_fold( const char *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen) { /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a, alen), bp(b, blen); /* scan until we find a mismatch or run out of one string */ while(ap.more() && bp.more()) { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } /* * if 'a' ran out first, and we have a 'bmatchlen' pointer, then we're * being asked if 'a' is a leading substring of 'b', which it is - fill * in '*bmatchlen' with the length of 'b' that we matched, and return * ture */ if (bmatchlen != 0 && !ap.more() && bp.at_boundary()) { *bmatchlen = bp.getptr() - b; return 0; } /* which ran out first was shorter, so sorts first */ return ap.more() ? 1 : bp.more() ? -1 : 0; } /* * compare a wchar_t string against a utf-8 string with case folding */ int t3_compare_case_fold( const wchar_t *a, size_t alen, const char *b, size_t blen, size_t *bmatchlen) { /* set up folded-case string readers for the two strings */ CVmCaseFoldStr ap(a, alen); Utf8FoldStr bp(b, blen); /* scan until we find a mismatch or run out of one string */ while (ap.more() && bp.more()) { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } /* * if 'a' ran out first, and we have a 'bmatchlen' pointer, then we're * being asked if 'a' is a leading substring of 'b', which it is - fill * in '*bmatchlen' with the length of 'b' that we matched, and return * ture */ if (bmatchlen != 0 && !ap.more() && bp.at_boundary()) { *bmatchlen = bp.getptr() - b; return 0; } /* which ran out first was shorter, so sorts first */ return ap.more() ? 1 : bp.more() ? -1 : 0; } /* ------------------------------------------------------------------------ */ /* * Compare the minimum number of characters in each string with case * folding. */ int t3_compare_case_fold_min( utf8_ptr &a, size_t &alen, utf8_ptr &b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a.getptr(), alen), bp(b.getptr(), blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we made it to a boundary in each string without finding a * difference, we have a match. Advance each string past the matched * text. */ size_t ainc = ap.getptr() - a.getptr(); alen -= ainc; a.inc_bytes(ainc); size_t binc = bp.getptr() - b.getptr(); blen -= binc; b.inc_bytes(binc); /* return "equal" */ return 0; } int t3_compare_case_fold_min( utf8_ptr &a, size_t &alen, const wchar_t *&b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ Utf8FoldStr ap(a.getptr(), alen); CVmCaseFoldStr bp(b, blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we made it to a boundary in each string without finding a * difference, we have a match. Advance each string past the matched * text. */ size_t ainc = ap.getptr() - a.getptr(); alen -= ainc; a.inc_bytes(ainc); blen -= bp.getptr() - b; b = bp.getptr(); /* return "equal" */ return 0; } int t3_compare_case_fold_min( const wchar_t* &a, size_t &alen, const wchar_t* &b, size_t &blen) { /* if either is empty, there can be no match (unless both are empty) */ if (alen == 0 || blen == 0) return alen - blen; /* set up folded-case string readers for the two strings */ CVmCaseFoldStr ap(a, alen), bp(b, blen); /* * Scan until we're at a boundary in both strings. Note that we start * at a boundary, so always compare at least one character. */ do { /* get the next character of each string */ wchar_t ach = ap.getch(), bch = bp.getch(); /* if they're different, return the sign difference */ if (ach != bch) return ach - bch; } while (!ap.at_boundary() || !bp.at_boundary()); /* * If we got this far, we made it to a boundary in each string without * finding a difference, so we have a match. Advance each string past * the matched text. */ alen -= ap.getptr() - a; a = ap.getptr(); blen -= bp.getptr() - b; b = bp.getptr(); /* return "equal" */ return 0; } /* ------------------------------------------------------------------------ */ /* * Limited-length atoi */ int lib_atoi(const char *str, size_t len) { /* parse the sign, if present */ int s = 1; if (len >= 1 && *str == '-') s = -1, ++str, --len; else if (len >= 1 && *str == '+') ++str, --len; /* scan digits */ int acc; for (acc = 0 ; len > 0 && is_digit(*str) ; acc *= 10, acc += value_of_digit(*str), ++str, --len) ; /* apply the sign and return the result */ return s * acc; } /* * Limited-length atoi, with auto-advance of the string */ int lib_atoi_adv(const char *&str, size_t &len) { /* parse the sign, if present */ int s = 1; if (len >= 1 && *str == '-') s = -1, ++str, --len; else if (len >= 1 && *str == '+') ++str, --len; /* scan digits */ int acc; for (acc = 0 ; len > 0 && is_digit(*str) ; acc *= 10, acc += value_of_digit(*str), ++str, --len) ; /* apply the sign and return the result */ return s * acc; } /* ------------------------------------------------------------------------ */ /* * Find a version suffix in an identifier string. A version suffix * starts with the given character. If we don't find the character, * we'll return the default version suffix. In any case, we'll set * name_len to the length of the name portion, excluding the version * suffix and its leading separator. * * For example, with a '/' suffix, a versioned name string would look * like "tads-gen/030000" - the name is "tads_gen" and the version is * "030000". */ const char *lib_find_vsn_suffix(const char *name_string, char suffix_char, const char *default_vsn, size_t *name_len) { const char *vsn; /* find the suffix character, if any */ for (vsn = name_string ; *vsn != '\0' && *vsn != suffix_char ; ++vsn); /* note the length of the name portion */ *name_len = vsn - name_string; /* * skip the separator if we found one, to point vsn at the start of * the suffix string itself - it we didn't find the separator * character, use the default version string */ if (*vsn == suffix_char) ++vsn; else vsn = default_vsn; /* return the version string */ return vsn; } /* ------------------------------------------------------------------------ */ /* * allocating sprintf implementation */ char *t3sprintf_alloc(const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); char *str = t3vsprintf_alloc(fmt, args); va_end(args); /* return the allocated string */ return str; } /* * allocating vsprintf implementation */ char *t3vsprintf_alloc(const char *fmt, va_list args) { /* measure the required space - add in a byte for null termination */ size_t len = t3vsprintf(0, 0, fmt, args) + 1; /* allocate space */ char *buf = (char *)t3malloc(len); if (buf == 0) return 0; /* do the actual formatting */ t3vsprintf(buf, len, fmt, args); /* return the allocated buffer */ return buf; } /* * buffer-checked sprintf implementation */ size_t t3sprintf(char *buf, size_t buflen, const char *fmt, ...) { /* package the arguments as a va_list and invoke our va_list version */ va_list args; va_start(args, fmt); size_t len = t3vsprintf(buf, buflen, fmt, args); va_end(args); /* return the length */ return len; } /* check for 'th' suffix in a format code */ static const char *check_nth(const char *&fmt, int ival) { /* presume no suffix */ const char *nth = ""; /* check for the 'th' suffix in the format code */ if (fmt[1] == 't' && fmt[2] == 'h') { /* skip the extra format characters */ fmt += 2; /* 'th' suffix applies to most numbers */ nth = "th"; /* * check for 1st, 2nd, 3rd, 21st, 22nd, 23rd, etc; but note that * the the teens are all th's */ if (ival % 100 < 10 || ival % 100 > 20) { switch (ival % 10) { case 1: nth = "st"; break; case 2: nth = "nd"; break; case 3: nth = "rd"; break; } } } /* return the suffix we figured */ return nth; } /* * buffer-checked vsprintf implementation */ size_t t3vsprintf(char *buf, size_t buflen, const char *fmt, va_list args0) { size_t rem; size_t need = 0; char *dst; /* * make a private copy of the arguments, to ensure that we don't modify * the caller's copy (on some platforms, va_list is a reference type; * the caller might want to reuse their argument pointer for a two-pass * operation, such as a pre-format pass to measure how much space is * needed) */ va_list args; os_va_copy(args, args0); /* scan the buffer */ for (dst = buf, rem = buflen ; *fmt != '\0' ; ++fmt) { /* check for a format specifier */ if (*fmt == '%') { const char *fmt_start = fmt; const char *nth = ""; const char *txt; size_t txtlen; char buf[20]; int fld_wid = -1; int fld_prec = -1; char lead_char = ' '; int plus = FALSE; int approx = FALSE; int add_ellipsis = FALSE; int left_align = FALSE; size_t i; /* skip the '%' */ ++fmt; /* check for the "approximation" flag */ if (*fmt == '~') { approx = TRUE; ++fmt; } /* check for an explicit sign */ if (*fmt == '+') { plus = TRUE; ++fmt; } /* check for left alignment */ if (*fmt == '-') { left_align = TRUE; ++fmt; } /* if leading zeros are desired, note it */ if (*fmt == '0') lead_char = '0'; /* check for a field width specifier */ if (is_digit(*fmt)) { /* scan the digits */ for (fld_wid = 0 ; is_digit(*fmt) ; ++fmt) { fld_wid *= 10; fld_wid += value_of_digit(*fmt); } } else if (*fmt == '*') { /* the value is an integer taken from the arguments */ fld_wid = va_arg(args, int); /* skip the '*' */ ++fmt; } /* check for a precision specifier */ if (*fmt == '.') { /* skip the '.' */ ++fmt; /* check what we have */ if (*fmt == '*') { /* the value is an integer taken from the arguments */ fld_prec = va_arg(args, int); /* skip the '*' */ ++fmt; } else { /* scan the digits */ for (fld_prec = 0 ; is_digit(*fmt) ; ++fmt) { fld_prec *= 10; fld_prec += value_of_digit(*fmt); } } } /* check what follows */ switch (*fmt) { case '%': /* it's a literal '%' */ txt = "%"; txtlen = 1; break; case 's': /* the value is a (char *) */ txt = va_arg(args, char *); /* if it's null, show "(null)" */ if (txt == 0) txt = "(null)"; /* get the length, limiting it to the precision */ { const char *p; /* * Scan until we reach a null terminator, or until our * length reaches the maximum given by the precision * qualifier. */ for (txtlen = 0, p = txt ; *p != '\0' && (fld_prec == -1 || txtlen < (size_t)fld_prec) ; ++p, ++txtlen) ; } /* * if the 'approximation' flag is specified, limit the * string to 60 characters or the field width, whichever * is less */ if (approx) { size_t lim; /* the default limit is 60 characters */ lim = 60; /* * if a field width is specified, it overrides the * default - but take out three characters for the * '...' suffix */ if (fld_wid != -1 && (size_t)fld_wid > lim) lim = fld_wid - 3; /* if we're over the limit, shorten the string */ if (txtlen > lim) { /* shorten the string to the limit */ txtlen = lim; /* add an ellipsis to indicate the truncation */ add_ellipsis = TRUE; } } /* * if a field width was specified and we have the default * right alignment, pad to the left with spaces if the * width is greater than the actual length */ if (fld_wid != -1 && !left_align) { for ( ; (size_t)fld_wid > txtlen ; --fld_wid) { ++need; if (rem > 1) --rem, *dst++ = ' '; } } break; case 'P': /* * URL parameter - this is a (char*) value with url * encoding applied */ txt = va_arg(args, char *); /* if it's null, use an empty string */ if (txt == 0) txt = ""; /* use the field precision if given, or the string length */ txtlen = (fld_prec >= 0 ? fld_prec : strlen(txt)); /* encode the parameter and add it to the buffer */ for ( ; txtlen != 0 ; --txtlen, ++txt) { switch (*txt) { case '!': case '*': case '\'': case '(': case ')': case ';': case ':': case '@': case '&': case '=': case '+': case '$': case ',': case '/': case '?': case '#': case '[': case ']': case ' ': /* use % encoding for special characters */ sprintf(buf, "%%%02x", (unsigned)(uchar)*txt); need += 3; for (i = 0 ; i < 3 ; ++i) { if (rem > 1) *dst++ = buf[i], --rem; } break; default: /* copy anything else as-is */ need += 1; if (rem > 1) *dst++ = *txt, --rem; break; } } break; case 'c': /* the value is a (char) */ buf[0] = (char)va_arg(args, int); txt = buf; txtlen = 1; break; case 'd': /* the value is an int, formatted in decimal */ { int ival = va_arg(args, int); sprintf(buf, "%d", ival); /* check for '%dth' notation (1st, 2nd, etc) */ nth = check_nth(fmt, ival); } /* fall through to num_common: */ num_common: /* use the temporary buffer where we formatted the value */ txt = buf; txtlen = strlen(buf); /* * Pad with leading spaces or zeros if the requested * field width exceeds the actual size and we have the * default left alignment. */ if (fld_wid != -1 && !left_align && (size_t)fld_wid > txtlen) { /* * if we're showing an explicit sign, and we don't have * a leading '-' in the results, add it */ if (plus && txt[0] != '-') { /* add the '+' at the start of the output */ ++need; if (rem > 1) --rem, *dst++ = '+'; } /* * if we're showing leading zeros, and we have a * negative number, show the '-' first */ if (lead_char == '0' && txt[0] == '-') { /* add the '-' at the start of the output */ ++need; if (rem > 1) --rem, *dst++ = '-'; /* we've shown the '-', so skip it in the number */ ++txt; --txtlen; } /* add the padding */ for ( ; (size_t)fld_wid > txtlen ; --fld_wid) { ++need; if (rem > 1) --rem, *dst++ = lead_char; } } /* done */ break; case 'u': /* the value is an int, formatted as unsigned decimal */ sprintf(buf, "%u", va_arg(args, int)); goto num_common; case 'x': /* the value is an int, formatted in hex */ sprintf(buf, "%x", va_arg(args, int)); goto num_common; case 'o': /* the value is an int, formatted in hex */ sprintf(buf, "%o", va_arg(args, int)); goto num_common; case 'l': /* it's a 'long' value - get the second specifier */ switch (*++fmt) { case 'd': /* it's a long, formatted in decimal */ sprintf(buf, "%ld", va_arg(args, long)); goto num_common; case 'u': /* it's a long, formatted as unsigned decimal */ sprintf(buf, "%lu", va_arg(args, long)); goto num_common; case 'x': /* it's a long, formatted in hex */ sprintf(buf, "%lx", va_arg(args, long)); goto num_common; case 'o': /* it's a long, formatted in octal */ sprintf(buf, "%lo", va_arg(args, long)); goto num_common; default: /* bad specifier - show the literal text */ txt = "%"; txtlen = 1; fmt = fmt_start; break; } break; default: /* bad specifier - show the literal text */ txt = "%"; txtlen = 1; fmt = fmt_start; break; } /* add the text to the buffer */ for (i = txtlen ; i != 0 ; --i, ++txt) { ++need; if (rem > 1) --rem, *dst++ = *txt; } /* add the 'nth' suffix, if applicable */ for ( ; *nth != 0 ; ++nth) { ++need; if (rem > 1) --rem, *dst++ = *nth; } /* add an ellipsis if desired */ if (add_ellipsis) { /* add three '.' characters */ for (i = 3 ; i != 0 ; --i) { ++need; if (rem > 1) --rem, *dst++ = '.'; } /* for padding purposes, count the ellipsis */ txtlen += 3; } /* * if we have left alignment and the actual length is less * than the field width, pad to the right with spaces */ if (left_align && fld_wid != -1 && (size_t)fld_wid > txtlen) { /* add spaces to pad out to the field width */ for (i = fld_wid ; i > txtlen ; --i) { ++need; if (rem > 1) --rem, *dst++ = ' '; } } } else { /* it's not a format specifier - just copy it literally */ ++need; if (rem > 1) --rem, *dst++ = *fmt; } } /* add the trailing null */ if (rem != 0) *dst = '\0'; /* bracket the va_copy at the top of the function */ os_va_copy_end(args); /* return the needed length */ return need; } /* ------------------------------------------------------------------------ */ /* * Convert string to lower case */ void t3strlwr(char *p) { for ( ; *p != '\0' ; ++p) *p = (char)to_lower(*p); } /* ------------------------------------------------------------------------ */ /* * Debugging routines for memory management */ #ifdef T3_DEBUG #include #include #define T3_DEBUG_MEMGUARD #ifdef T3_DEBUG_MEMGUARD #define MEM_GUARD_PREFIX char guard[sizeof(mem_prefix_t *)]; #define MEM_GUARD_POST_BYTES sizeof(mem_prefix_t *) #else /* T3_DEBUG_MEMGUARD */ #define MEM_GUARD_PREFIX #define MEM_GUARD_BYTES 0 #define mem_check_guard(blk) #define mem_set_guard(blk) #endif /* T3_DEBUG_MEMGUARD */ /* * memory block prefix - each block we allocate has this prefix attached * just before the pointer that we return to the program */ struct mem_prefix_t { long id; size_t siz; mem_prefix_t *nxt; mem_prefix_t *prv; int alloc_type; OS_MEM_PREFIX MEM_GUARD_PREFIX }; #ifdef T3_DEBUG_MEMGUARD static void mem_make_guard(char *b, const mem_prefix_t *blk) { memcpy(b, &blk, sizeof(blk)); const static unsigned char x[] = { 0xcf, 0xfd, 0xef, 0xfb, 0xbf, 0xfc, 0xdf, 0xfe }; for (size_t i = 0 ; i < sizeof(blk) ; ++i) b[i] ^= x[i % countof(x)]; } static void mem_set_guard(mem_prefix_t *blk) { char b[sizeof(mem_prefix_t *)]; mem_make_guard(b, blk); memcpy(blk->guard, b, sizeof(b)); memcpy((char *)(blk + 1) + blk->siz, b, sizeof(b)); } static void mem_check_guard(const mem_prefix_t *blk) { char b[sizeof(mem_prefix_t *)]; mem_make_guard(b, blk); if (memcmp(blk->guard, b, sizeof(b)) != 0) fprintf(stderr, "pre guard bytes corrupted: addr=%lx, id=%ld, siz=%lu " OS_MEM_PREFIX_FMT "\n", (long)(blk + 1), blk->id, (unsigned long)blk->siz OS_MEM_PREFIX_FMT_VARS(blk)); if (memcmp((char *)(blk + 1) + blk->siz, b, sizeof(b)) != 0) fprintf(stderr, "post guard bytes corrupted: addr=%lx, id=%ld, siz=%lu " OS_MEM_PREFIX_FMT "\n", (long)(blk + 1), blk->id, (unsigned long)blk->siz OS_MEM_PREFIX_FMT_VARS(blk)); } #endif /* T3_DEBUG_MEMGUARD */ /* head and tail of memory allocation linked list */ static mem_prefix_t *mem_head = 0; static mem_prefix_t *mem_tail = 0; /* mutex for protecting the memory tracker list */ static OS_Mutex mem_mutex; /* * Check the integrity of the heap: traverse the entire list, and make * sure the forward and backward pointers match up. */ static void t3_check_heap() { mem_prefix_t *p; /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* scan from the front */ for (p = mem_head ; p != 0 ; p = p->nxt) { /* * If there's a backwards pointer, make sure it matches up. If * there's no backwards pointer, make sure we're at the head of * the list. If this is the end of the list, make sure it * matches the tail pointer. */ if ((p->prv != 0 && p->prv->nxt != p) || (p->prv == 0 && p != mem_head) || (p->nxt == 0 && p != mem_tail)) fprintf(stderr, "\n--- heap corrupted ---\n"); } /* done with the list */ mem_mutex.unlock(); } /* * Allocate a block, storing it in a doubly-linked list of blocks and * giving the block a unique ID. */ void *t3malloc(size_t siz, int alloc_type) { static long id; static int check = 0; mem_prefix_t *mem; /* make sure the size doesn't overflow when we add the prefix */ if (siz + sizeof(mem_prefix_t) < siz) return 0; /* allocate the memory, including its prefix */ mem = (mem_prefix_t *)malloc(siz + sizeof(mem_prefix_t) + MEM_GUARD_POST_BYTES); /* if that failed, return failure */ if (mem == 0) return 0; /* set the guard bytes */ mem->alloc_type = alloc_type; mem->siz = siz; mem_set_guard(mem); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* set up the prefix */ mem->id = id++; mem->prv = mem_tail; os_mem_prefix_set(mem); mem->nxt = 0; if (mem_tail != 0) mem_tail->nxt = mem; else mem_head = mem; mem_tail = mem; /* done with the list */ mem_mutex.unlock(); /* check the heap for corruption if desired */ if (check) t3_check_heap(); /* return the caller's block, which immediately follows the prefix */ return (void *)(mem + 1); } /* * reallocate a block - to simplify, we'll allocate a new block, copy * the old block up to the smaller of the two block sizes, and delete * the old block */ void *t3realloc(void *oldptr, size_t newsiz) { void *newptr; size_t oldsiz; /* allocate a new block */ newptr = t3malloc(newsiz, T3MALLOC_TYPE_MALLOC); /* copy the old block into the new block */ oldsiz = (((mem_prefix_t *)oldptr) - 1)->siz; memcpy(newptr, oldptr, (oldsiz <= newsiz ? oldsiz : newsiz)); /* free the old block */ t3free(oldptr); /* return the new block */ return newptr; } /* free a block, removing it from the allocation block list */ void t3free(void *ptr, int alloc_type) { /* statics for debugging */ static int check = 0; static int double_check = 0; static int check_heap = 0; static long ckblk[] = { 0xD9D9D9D9, 0xD9D9D9D9, 0xD9D9D9D9 }; /* ignore freeing null */ if (ptr == 0) return; /* check the integrity of the entire heap if desired */ if (check_heap) t3_check_heap(); /* get the prefix */ mem_prefix_t *mem = ((mem_prefix_t *)ptr) - 1; size_t siz; /* * check that the call type matches the allocating call type (malloc, * new, or new[]) */ if (mem->alloc_type != alloc_type) fprintf(stderr, "\n--- memory block freed with wrong call type: " "block=%lx, size=%lu, id=%lu, alloc type=%d, free type=%d " "---\n", (unsigned long)ptr, (unsigned long)mem->siz, mem->id, mem->alloc_type, alloc_type); /* check for a pre-freed block */ if (memcmp(mem, ckblk, sizeof(ckblk)) == 0) { fprintf(stderr, "\n--- memory block freed twice: %lx ---\n", (unsigned long)ptr); return; } /* check the guard bytes for overwrites */ mem_check_guard(mem); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* if desired, check to make sure the block is in our list */ if (check) { mem_prefix_t *p; for (p = mem_head ; p != 0 ; p = p->nxt) { if (p == mem) break; } if (p == 0) fprintf(stderr, "\n--- memory block not found in t3free: %lx ---\n", (unsigned long)ptr); } /* unlink the block from the list */ if (mem->prv != 0) mem->prv->nxt = mem->nxt; else mem_head = mem->nxt; if (mem->nxt != 0) mem->nxt->prv = mem->prv; else mem_tail = mem->prv; /* * if we're being really cautious, check to make sure the block is * no longer in the list */ if (double_check) { mem_prefix_t *p; for (p = mem_head ; p != 0 ; p = p->nxt) { if (p == mem) break; } if (p != 0) fprintf(stderr, "\n--- memory block still in list after " "t3free ---\n"); } /* done with the list */ mem_mutex.unlock(); /* make it obvious that the memory is invalid */ siz = mem->siz; memset(mem, 0xD9, siz + sizeof(mem_prefix_t)); /* free the memory with the system allocator */ free((void *)mem); } /* * Default display lister callback */ static void fprintf_stderr(const char *msg) { fprintf(stderr, "%s", msg); } /* * Diagnostic routine to display the current state of the heap. This * can be called just before program exit to display any memory blocks * that haven't been deleted yet; any block that is still in use just * before program exit is a leaked block, so this function can be useful * to help identify and remove memory leaks. */ void t3_list_memory_blocks(void (*cb)(const char *)) { mem_prefix_t *mem; int cnt; char buf[128]; /* if there's no callback, use our own standard display lister */ if (cb == 0) cb = fprintf_stderr; /* display introductory message */ (*cb)("\n(T3VM) Memory blocks still in use:\n"); /* lock the memory mutex while accessing the memory block list */ mem_mutex.lock(); /* display the list of undeleted memory blocks */ for (mem = mem_head, cnt = 0 ; mem ; mem = mem->nxt, ++cnt) { sprintf(buf, " addr=%lx, id=%ld, siz=%lu" OS_MEM_PREFIX_FMT "\n", (long)(mem + 1), mem->id, (unsigned long)mem->siz OS_MEM_PREFIX_FMT_VARS(mem)); (*cb)(buf); } /* done with the mutex */ mem_mutex.unlock(); /* display totals */ sprintf(buf, "\nTotal blocks in use: %d\n", cnt); (*cb)(buf); } #ifdef T_WIN32 /* * Windows-specific additions to the memory header. We'll track the first * couple of return addresses from the stack, to make it easier to track * down where the allocation request came from. */ void os_mem_prefix_set(mem_prefix_t *mem) { /* * Trace back the call stack. In the standard Intel stack arrangement, * BP is the base pointer for the frame, and points to the enclosing * frame pointer. Just above BP is the return address. We're not * interested in our own return address, so skip the first frame. */ DWORD bp_; __asm mov bp_, ebp; bp_ = IsBadReadPtr((DWORD *)bp_, sizeof(bp_)) ? 0 : *(DWORD *)bp_; for (size_t i = 0 ; i < countof(mem->stk) && !IsBadReadPtr((DWORD *)bp_, sizeof(bp_)) && bp_ < *(DWORD *)bp_ ; ++i, bp_ = *(DWORD *)bp_) mem->stk[i].return_addr = ((DWORD *)bp_)[1]; } #endif /* T_WIN32 */ #endif /* T3_DEBUG */ frobtads-1.2.3/tads3/vmbifnet.cpp0000644000175000001440000011772512145504453016060 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* Copyright (c) 2005 by Michael J. Roberts. All Rights Reserved. */ /* Name vmbifnet.cpp - TADS networking function set Function Notes Modified 04/20/2010 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "os.h" #include "utf8.h" #include "charmap.h" #include "vmbifnet.h" #include "vmtype.h" #include "vmstack.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmglob.h" #include "vmpool.h" #include "vmobj.h" #include "vmstr.h" #include "vmlst.h" #include "vmrun.h" #include "vmhost.h" #include "vmconsol.h" #include "vmvec.h" #include "vmnet.h" #include "vmimport.h" #include "vmpredef.h" #include "vmhttpsrv.h" #include "vmdatasrc.h" #include "vmbytarr.h" #include "vmfilobj.h" #include "vmcset.h" #include "vmlookup.h" #include "vmtobj.h" #include "vmlog.h" /* ------------------------------------------------------------------------ */ /* * NetEvent.evType values */ #define VMBN_EVT_REQUEST 1 #define VMBN_EVT_TIMEOUT 2 #define VMBN_EVT_DBGBRK 3 /* ------------------------------------------------------------------------ */ /* * Static initialization */ void CVmBifNet::attach(VMG0_) { /* initialize the network layer */ os_net_init(G_net_config); /* create the network message queue */ G_net_queue = new TadsMessageQueue(0); } /* * Static cleanup */ void CVmBifNet::detach(VMG0_) { /* done with the message queue - flush it and release our reference */ G_net_queue->flush(); G_net_queue->release_ref(); G_net_queue = 0; } /* ------------------------------------------------------------------------ */ /* * Connect to the Web UI */ void CVmBifNet::connect_ui(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 2); /* get the server object */ vm_val_t *srv = G_stk->get(0); if (srv->typ != VM_OBJ || !CVmObjHTTPServer::is_httpsrv_obj(vmg_ srv->val.obj)) err_throw(VMERR_BAD_TYPE_BIF); /* get the object pointer properly cast */ CVmObjHTTPServer *srvp = (CVmObjHTTPServer *)vm_objp(vmg_ srv->val.obj); /* get the URL path */ const char *path = G_stk->get(1)->get_as_string(vmg0_); if (path == 0) err_throw(VMERR_BAD_TYPE_BIF); /* make a null-terminated copy of the path */ char *pathb = lib_copy_str(path + VMB_LEN, vmb_get_len(path)); /* get the server network address information */ char *errmsg = 0; char *addr = 0, *ip = 0; int port; int ok = srvp->get_listener_addr(addr, ip, port); /* * If we don't have a network configuration yet, create one. This * notifies other subsystems that we have an active web UI; for * example, the presence of a network UI disables the regular console * UI, since all UI actions have to go through the web UI once it's * established. * * The interpreter startup creates a network configuration if it * receives command-line information telling it that the game was * launched by a Web server in response to an incoming client request. * When the user launches the game in stand-alone mode directly from * the operating system shell, there's no Web server launch * information, so the startup code doesn't create a net config object. * So, if we get here and find we don't have this object, it means that * we're running in local standalone mode. */ if (G_net_config == 0) G_net_config = new TadsNetConfig(); /* connect */ if (ok) { /* connect */ ok = osnet_connect_webui(vmg_ addr, port, pathb, &errmsg); } else { /* couldn't get the server address */ errmsg = lib_copy_str( "No address information available for HTTPServer"); } /* free strings */ lib_free_str(pathb); lib_free_str(addr); lib_free_str(ip); /* if there's an error, throw it */ if (!ok) { G_interpreter->push_string(vmg_ errmsg); lib_free_str(errmsg); G_interpreter->throw_new_class(vmg_ G_predef->net_exception, 1, "Error connecting to Web UI"); } /* no return value */ retval_nil(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Get a network event */ void CVmBifNet::get_event(VMG_ uint oargc) { /* check arguments */ check_argc_range(vmg_ oargc, 0, 1); /* get the timeout, if present */ long timeout = OS_FOREVER; if (oargc >= 1) { if (G_stk->get(0)->typ == VM_NIL) G_stk->discard(); else timeout = pop_int_val(vmg0_); } /* we don't know the NetEvent subclass yet, so presume the base class */ vm_obj_id_t ev_cl = G_predef->net_event; int argc; /* get the next message */ TadsMessage *msg = 0; int wret = G_net_queue->wait(vmg_ timeout, &msg); /* cast the message */ TadsEventMessage *evtmsg = cast_tads_message(TadsEventMessage, msg); /* make sure we release the message before exiting */ err_try { /* check the wait result */ const char *err; switch (wret) { case OSWAIT_EVENT + 0: /* we got a message */ if (evtmsg != 0) { /* ask the message to set up a new NetRequestEvent */ int evt_code; ev_cl = evtmsg->prep_event_obj(vmg_ &argc, &evt_code); /* add the event type argument */ G_interpreter->push_int(vmg_ evt_code); ++argc; } else { /* unrecognized message type */ err = "getNetEvent(): unexpected message type in queue"; goto evt_error; } break; case OSWAIT_EVENT + 1: /* 'quit' event - return an error */ err = "getNetEvent(): queue terminated"; goto evt_error; case OSWAIT_EVENT + 2: /* debug break - return a NetEvent with the interrupt code */ argc = 1; G_interpreter->push_int(vmg_ VMBN_EVT_DBGBRK); break; case OSWAIT_TIMEOUT: /* the timeout expired - return a NetTimeoutEvent */ ev_cl = G_predef->net_timeout_event; argc = 1; G_interpreter->push_int(vmg_ VMBN_EVT_TIMEOUT); break; case OSWAIT_ERROR: default: /* the wait failed */ err = "getNetEvent(): error waiting for request message"; evt_error: /* on error, throw a NetException describing the problem */ G_interpreter->push_string(vmg_ err); G_interpreter->throw_new_class( vmg_ G_predef->net_exception, 1, err); AFTER_ERR_THROW(break;) } /* * if the specific event type subclass isn't defined, fall back on * the base NetEvent class */ if (ev_cl == VM_INVALID_OBJ) ev_cl = G_predef->net_event; /* * construct the NetEvent subclass, or a generic list if we don't * even have the NetEvent class defined */ if (ev_cl != VM_INVALID_OBJ) vm_objp(vmg_ ev_cl)->create_instance(vmg_ ev_cl, 0, argc); else retval_obj(vmg_ CVmObjList::create_from_stack(vmg_ 0, argc)); } err_finally { /* we're done with the message object */ if (msg != 0) msg->release_ref(); } err_end; } /* ------------------------------------------------------------------------ */ /* * Get the local host name */ void CVmBifNet::get_hostname(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 0); /* check the network safety levels */ int client_level, server_level; G_host_ifc->get_net_safety(&client_level, &server_level); /* * If the network safety level doesn't allow outside network access for * either client or server, return "localhost". If they don't allow * any network access at all, return nil. * * If there's a host name defined in the web configuration, return that * host name. Otherwise, return the default host name from the OS. */ if (client_level >= VM_NET_SAFETY_MAXIMUM && server_level >= VM_NET_SAFETY_MAXIMUM) { /* no network access is allowed - return nil */ retval_nil(vmg0_); } else if (client_level >= VM_NET_SAFETY_LOCALHOST && server_level >= VM_NET_SAFETY_LOCALHOST) { /* localhost access only - return "localhost" */ retval_str(vmg_ "localhost"); } else { /* * the network safety level allows outside network access, so we * can return the actual host name - get it from the system */ char buf[256]; if (os_get_hostname(buf, sizeof(buf))) { /* got it - return the string */ retval_ui_str(vmg_ buf); } else { /* no host name available - return nil */ retval_nil(vmg0_); } } } /* ------------------------------------------------------------------------ */ /* * Get the local host IP address */ void CVmBifNet::get_host_ip(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 0); /* check the network safety levels */ int client_level, server_level; G_host_ifc->get_net_safety(&client_level, &server_level); /* * If the network safety level doesn't allow outside network access for * either client or server, return "localhost". If they don't allow * any network access at all, return nil. */ if (client_level >= VM_NET_SAFETY_MAXIMUM && server_level >= VM_NET_SAFETY_MAXIMUM) { /* no network access is allowed - return nil */ retval_nil(vmg0_); } else if (client_level >= VM_NET_SAFETY_LOCALHOST && server_level >= VM_NET_SAFETY_LOCALHOST) { /* localhost access only - return the standard localhost IP address */ retval_str(vmg_ "127.0.0.1"); } else { /* retrieve the host IP address for the default host */ char buf[256]; if (os_get_local_ip(buf, sizeof(buf), 0)) { /* got it - return the string */ retval_ui_str(vmg_ buf); } else { /* no host name available - return nil */ retval_nil(vmg0_); } } } /* ------------------------------------------------------------------------ */ /* * Get the network storage server URL. */ /* get the storage server URL */ void CVmBifNet::get_storage_url(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 1); /* set a default nil return in case we can't build the path */ retval_nil(vmg0_); /* get the resource name */ const char *page = G_stk->get(0)->get_as_string(vmg0_); if (page == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the resource name length and buffer pointer */ size_t pagelen = vmb_get_len(page); page += VMB_LEN; /* if there's a network configuration, build the resource path */ const char *host = 0, *rootpath = 0; if (G_net_config != 0) { /* get the storage server host name and root path */ host = G_net_config->get("storage.domain"); rootpath = G_net_config->get("storage.rootpath", "/"); } /* we must have a host name to proceed */ if (host != 0) { /* build the full string */ G_interpreter->push_stringf(vmg_ "http://%s%s%.*s", host, rootpath, (int)pagelen, page); /* pop it into R0 */ G_stk->pop(G_interpreter->get_r0()); } /* discard arguments */ G_stk->discard(); } /* ------------------------------------------------------------------------ */ /* * Get the launching host address */ void CVmBifNet::get_launch_host_addr(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 0); /* get the launch host name from the network configuration */ const char *host = (G_net_config != 0 ? G_net_config->get("hostname") : 0); /* if there's a host name, return it, otherwise return nil */ if (host != 0) retval_str(vmg_ host); else retval_nil(vmg0_); } /* ------------------------------------------------------------------------ */ /* * Get a configuration variable */ #if 0 // leave this out for now void CVmBifNet::get_net_config(VMG_ uint oargc) { /* check arguments */ check_argc(vmg_ oargc, 1); /* get the variable name */ char name[256]; pop_str_val_buf(vmg_ name, sizeof(name)); /* look up the name */ const char *val = 0; if (G_net_config != 0) val = G_net_config->get(name); /* if we found a value, return it; otherwise return nil */ if (val != 0) retval_str(vmg_ val); else retval_nil(vmg0_); } #endif /* ------------------------------------------------------------------------ */ /* * Send a network request - result event. */ class TadsHttpReqResult: public TadsEventMessage { public: TadsHttpReqResult(VMG_ vm_globalvar_t *idg, int status, CVmDataSource *reply, char *hdrs, char *loc) : TadsEventMessage(0) { /* save the VM globals */ vmg = VMGLOB_ADDR; /* save the ID global */ this->idg = idg; /* remember the status */ this->status = status; /* take ownership of the reply, headers, and location objects */ this->reply = reply; this->hdrs = hdrs; this->loc = loc; } ~TadsHttpReqResult() { /* establish the global context */ VMGLOB_PTR(vmg); /* delete our ID global */ G_obj_table->delete_global_var(idg); /* delete the reply object */ if (reply != 0) delete reply; /* free the reply headers */ if (hdrs != 0) delete [] hdrs; /* free the redirect location string, if we have one */ if (loc != 0) lib_free_str(loc); } /* prepare a bytecode event object when we're read from the queue */ virtual vm_obj_id_t prep_event_obj(VMG_ int *argc, int *evt_type) { /* push the location string (or nil) */ G_interpreter->push_string(vmg_ loc, loc != 0 ? strlen(loc) : 0); /* push the header string (or nil) */ G_interpreter->push_string(vmg_ hdrs, hdrs != 0 ? strlen(hdrs) : 0); /* if we have reply data, push it as a File object */ if (reply != 0) { /* * Look for a content-type header, to determine the format of * the reply body. If the header starts with "text/", it's a * text type; otherwise it's a binary type. */ int mode = VMOBJFILE_MODE_RAW; const char *charset = "ISO-8859-1"; size_t cslen = 10; const char *ct = find_header("content-type"); if (ct != 0 && memcmp(ct, "text/", 5) == 0) { /* it's a textual type */ mode = VMOBJFILE_MODE_TEXT; /* look for a 'charset' attribute */ for (;;) { /* scan ahead to the next ';' before end of line */ for ( ; *ct != '\0' && *ct != '\r' && *ct != '\n' && *ct != ';' ; ++ct) ; if (*ct != ';') break; /* skip the ';' and spaces */ for (ct += 1 ; *ct != '\0' && is_space(*ct) ; ++ct) ; /* check for "charset" */ if (memcmp(ct, "charset=", 8) == 0) { /* note the "charset" attribute, and find the end */ for (ct += 8, charset = ct ; *ct != '\r' && *ct != '\n' && *ct != ';' && !is_space(*ct) ; ++ct) ; /* note the character set name length */ cslen = ct - charset; /* look no further */ break; } } } /* if it's a text-mode file, create a character mapper */ vm_obj_id_t csobj = VM_INVALID_OBJ; if (mode == VMOBJFILE_MODE_TEXT) csobj = CVmObjCharSet::create(vmg_ FALSE, charset, cslen); /* seek to the beginning of the reply stream */ reply->seek(0, OSFSK_SET); /* create a File object from the Data Source */ vm_obj_id_t fileobj = CVmObjFile::create( vmg_ FALSE, 0, csobj, reply, mode, VMOBJFILE_ACCESS_READ, TRUE); /* push the file object */ G_interpreter->push_obj(vmg_ fileobj); /* * the File object owns the data source, which owns the reply * stream, so forget about the reply stream */ reply = 0; } else { /* no reply data - push nil */ G_interpreter->push_nil(vmg0_); } /* push the network status code */ G_interpreter->push_int(vmg_ status); /* push the caller's ID value */ G_stk->push(&idg->val); /* relay our added argument count to the caller */ *argc = 5; /* set the event type to NetEvReply (5 - see include/tadsnet.h) */ *evt_type = 5; /* the event subclass is NetReplyEvent */ return G_predef->net_reply_event; } protected: /* find a header */ const char *find_header(const char *name) { /* remember the name length */ size_t namelen = strlen(name); /* scan the headers */ for (const char *p = hdrs ; p != 0 ; p = next_header(p)) { /* skip spaces */ for ( ; *p != '\0' && is_space(*p) ; ++p) ; /* check for a match */ if (memicmp(p, name, namelen) == 0) { /* skip the name and any subsequent spaces */ for (p += namelen ; *p != '\0' && is_space(*p) ; ++p) ; /* make sure we have a ':' */ if (*p == ':') { /* skip the ':' and subsequent spaces */ for (p += 1 ; *p != '\0' && is_space(*p) ; ++p) ; /* this is the start of the header value */ return p; } } } /* didn't find it */ return 0; } /* find the next header */ const char *next_header(const char *p) { /* scan ahead to the next \r\n sequence */ for ( ; *p != '\0' ; ++p) { /* if this is a CR-LF sequence, it's the end of this header */ if (*p == '\r' && *(p+1) == '\n') { /* skip spaces */ for (p += 2 ; *p != '\0' && is_space(*p) ; ++p) ; /* if this was a blank line, we're out of headers */ if (*p == '\0' || (*p == '\r' && *(p+1) == '\n')) return 0; /* this is the next header */ return p; } } /* no more headers */ return 0; } /* VM globals */ vm_globals *vmg; /* * VM global variable for the ID value. We need to hang on to the ID * value until the request completes, since we have to pass it back to * the program via the NetEvent we generate at completion. Since the * thread isn't a garbage-collected object, we have to explicitly * register our reference on the ID value via a VM global variable. */ vm_globalvar_t *idg; /* HTTP status (or OS_HttpClient::ErrXxx code, if negative) */ int status; /* the reply content body */ CVmDataSource *reply; /* reply headers */ char *hdrs; /* redirect location */ char *loc; }; /* ------------------------------------------------------------------------ */ /* * Request option flags */ #define NetReqNoRedirect 0x0001 /* form body lookup table iterator context */ struct iter_body_table_ctx { iter_body_table_ctx(class HttpReqThread *self, const vm_rcdesc *rc) : self(self), rc(rc) { } class HttpReqThread *self; const vm_rcdesc *rc; }; /* ------------------------------------------------------------------------ */ /* * Send a network request - HTTP client thread. This is the worker thread * we launch to asynchronously send an HTTP request to a remote server. */ class HttpReqThread: public OS_Thread { public: /* * Set up the thread. All string parameters are provided in internal * string format, with VMB_LEN length prefixes. */ HttpReqThread(VMG_ const vm_val_t *id, const char *url, const char *verb, int32_t options, const char *hdrs, vm_val_t *body, const char *body_type, const vm_rcdesc *rc) { /* save the VM globals */ vmg = VMGLOB_ADDR; /* add a reference on the net event queue */ if ((queue = G_net_queue) != 0) queue->add_ref(); /* create a global for the ID value */ idg = G_obj_table->create_global_var(); idg->val = *id; /* save the option flags */ this->options = options; /* set up to parse the URL */ size_t urll = vmb_get_len(url); url += VMB_LEN; /* note whether the scheme is plain http or https */ https = (urll >= 8 && memcmp(url, "https://", 8) == 0); /* we don't have any URL pieces yet */ host = 0; resource = 0; port = (https ? 443 : 80); /* skip the scheme prefix */ size_t pfx = (urll >= 7 && memcmp(url, "http://", 7) == 0 ? 7 : urll >= 8 && memcmp(url, "https://", 8) == 0 ? 8 : 0); url += pfx, urll -= pfx; /* the host name is the part up to the ':', '/', or end of string */ const char *start; for (start = url ; urll > 0 && *url != ':' && *url != '/' ; ++url, --urll) ; /* save the host name */ host = lib_copy_str(start, url - start); /* parse the port, if present */ if (urll > 0 && *url == ':') { /* parse the port number string */ for (--urll, start = ++url, port = 0 ; urll > 0 && *url != '/' ; ++url, --urll) { if (is_digit(*url)) port = port*10 + value_of_digit(*url); } } /* * The rest (including the '/') is the resource string. If there's * nothing left, the resource is implicitly '/'. */ if (urll > 0) resource = lib_copy_str(url, urll); else resource = lib_copy_str("/"); /* make null-terminated copies of the various strings */ this->verb = lib_copy_str(verb + VMB_LEN, vmb_get_len(verb)); this->hdrs = (hdrs == 0 ? 0 : lib_copy_str(hdrs + VMB_LEN, vmb_get_len(hdrs))); /* if there's a content body, set up the payload */ this->body = 0; if (body != 0) { /* set up an empty payload */ this->body = new OS_HttpPayload(); /* * Convert the body to a stream payload, if it's a string or * ByteArray. If it's a LookupTable, it's a set of form * fields. */ if (body->typ == VM_OBJ && CVmObjLookupTable::is_lookup_table_obj(vmg_ body->val.obj)) { /* * The body is a LookupTable, so we have a set of form * fields to encode as HTML form data. */ /* cast the object value */ CVmObjLookupTable *tab = (CVmObjLookupTable *)vm_objp(vmg_ body->val.obj); /* add each key in the table as a form field */ iter_body_table_ctx ctx(this, rc); tab->for_each(vmg_ iter_body_table, &ctx); } else { /* * It's not a lookup table, so it must be bytes to upload, * as a string or ByteArray. */ const char *def_mime_type = 0; CVmDataSource *s = val_to_ds(vmg_ body, &def_mime_type); /* presume we're going to use the default mime type */ const char *mime_type = def_mime_type; size_t mime_type_len = (mime_type != 0 ? strlen(mime_type) : 0); /* if the caller specified a mime type, use it instead */ if (body_type != 0) { mime_type = body_type + VMB_LEN; mime_type_len = vmb_get_len(body_type); } /* add the stream */ this->body->add( "", 0, "", 0, mime_type, mime_type_len, s); } } } ~HttpReqThread() { /* establish the global context */ VMGLOB_PTR(vmg); /* delete copied strings */ lib_free_str(host); lib_free_str(resource); lib_free_str(verb); lib_free_str(hdrs); /* delete the content body */ if (body != 0) delete body; /* release the message queue */ if (queue != 0) queue->release_ref(); /* delete our ID global, if we still own it */ if (idg != 0) G_obj_table->delete_global_var(idg); } /* main thread entrypoint */ void thread_main() { /* if they want redirections, set up to receive the location */ char *loc = 0; char **locp = ((options & NetReqNoRedirect) != 0 ? &loc : 0); /* set up a memory stream object to hold the reply */ CVmMemorySource *reply = new CVmMemorySource(128); /* no reply headers yet */ char *reply_hdrs = 0; /* figure the request option flags */ int rflags = 0; if (https) rflags |= OS_HttpClient::OptHTTPS; /* get the length of the custom headers, if we have any */ size_t hdrs_len = (hdrs != 0 ? strlen(hdrs) : 0); /* send the request */ int status = OS_HttpClient::request( rflags, host, port, verb, resource, hdrs, hdrs_len, body, reply, &reply_hdrs, locp, 0); /* * if the status is negative, a system-level error occurred, so * there's no reply data - delete and forget the reply stream */ if (status < 0) { delete reply; reply = 0; } /* * Always post a result event, even on failure, since the event is * the way we transmit the status information back to the caller. * Note that the event object takes ownership of the reply stream, * headers string, and location string, and will free the * underlying memory when the event object is deleted. */ post_result(status, reply, reply_hdrs, loc); } /* post the result */ void post_result(int status, CVmDataSource *reply, char *hdrs, char *loc) { /* establish the global context */ VMGLOB_PTR(vmg); /* create the result event */ TadsHttpReqResult *evt = new TadsHttpReqResult( vmg_ idg, status, reply, hdrs, loc); /* post the event (this transfers ownership to the queue) */ queue->post(evt); /* the result event now owns our ID global 'idg', so forget it */ idg = 0; } protected: /* * Convert a string or ByteArray object to a data stream */ static CVmDataSource *val_to_ds(VMG_ const vm_val_t *val, const char **def_mime_type) { /* try it as a string */ const char *bstr = val->get_as_string(vmg0_); if (bstr != 0) { /* assume this is text/plain */ *def_mime_type = "text/plain; charset=utf-8"; /* create a stream with a private copy of the string */ return new CVmPrivateStringSource( bstr + VMB_LEN, vmb_get_len(bstr)); } else if (val->typ == VM_OBJ && CVmObjByteArray::is_byte_array(vmg_ val->val.obj)) { /* assume this is binary data */ *def_mime_type = "application/octet-stream"; /* cast the ByteArray object value */ CVmObjByteArray *barr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj); /* create a stream copy of the byte array's contents */ unsigned long arrlen = barr->get_element_count(); CVmDataSource *s = new CVmMemorySource(arrlen); /* copy the array's contents to the stream */ const size_t BLOCKLEN = 1024; char block[BLOCKLEN]; for (unsigned long idx = 1 ; idx <= arrlen ; idx += BLOCKLEN) { /* read as much as remains */ unsigned long rem = arrlen - idx + 1; size_t cur = (rem < BLOCKLEN ? (size_t)rem : BLOCKLEN); /* copy the data */ barr->copy_to_buf((unsigned char *)block, idx, cur); s->write(block, cur); } /* * rewind the stream to the start, so that the network code * reads the entire contents from the beginning */ s->seek(0, OSFSK_SET); /* return the stream */ return s; } else { /* we can't convert other types */ return 0; } } /* * Lookup Table for_each callback for building a form payload. When * the caller specifies the body as a lookup table, we'll iterate over * the table's contents using this callback, which adds each field to * the payload object. */ static void iter_body_table( VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx0) { /* get the context */ iter_body_table_ctx *ctx = (iter_body_table_ctx *)ctx0; /* the key must be a string giving the field name */ const char *field_name = key->get_as_string(vmg0_); if (field_name == 0) return; /* * the value must be either a string giving the simple form field * value, or a FileUpload object giving a file to upload */ if (val->is_instance_of(vmg_ G_predef->file_upload_cl)) { /* * It's a FileUpload object. The 'file' property is the * content object, which must be provided as a string or * ByteArray; the 'filename' and 'contentType' properties are * strings giving us details on how to treat the content data. */ /* retrieve the property values we're interested in */ vm_val_t file_val, ctype_val, fname_val; G_interpreter->get_prop( vmg_ &file_val, val, G_predef->file_upload_fileObj, 0, ctx->rc); G_interpreter->get_prop( vmg_ &ctype_val, val, G_predef->file_upload_contentType, 0, ctx->rc); G_interpreter->get_prop( vmg_ &fname_val, val, G_predef->file_upload_filename, 0, ctx->rc); /* wrap the 'file' value in a datasource */ const char *def_ctype = 0; CVmDataSource *src = val_to_ds(vmg_ &file_val, &def_ctype); /* if we have a contentType string, override the default */ const char *ctype = ctype_val.get_as_string(vmg0_); size_t ctype_len; if (ctype != 0) { /* they specified a content type - use their value */ ctype_len = vmb_get_len(ctype); ctype += VMB_LEN; } else { /* use the default */ ctype = def_ctype; ctype_len = strlen(def_ctype); } /* get the filename string, if they provided one */ const char *fname = fname_val.get_as_string(vmg0_); size_t fname_len = (fname != 0 ? vmb_get_len(fname) : 0); fname += (fname != 0 ? VMB_LEN : 0); /* add the field */ ctx->self->body->add( field_name + VMB_LEN, vmb_get_len(field_name), fname, fname_len, ctype, ctype_len, src); } else { /* get a string interpretation of the value */ vm_val_t new_str; const char *field_val = val->cast_to_string(vmg_ &new_str); if (field_val == 0) return; /* add the field to the payload */ ctx->self->body->add_tstr(field_name, field_val); } } /* VM globals */ vm_globals *vmg; /* event message queue */ TadsMessageQueue *queue; /* option flags */ int32_t options; /* parsed URL information */ int https; char *host; char *resource; unsigned short port; /* the verb */ char *verb; /* custom headers */ char *hdrs; /* the content body */ OS_HttpPayload *body; /* * VM global variable for the ID value. We need to hang on to the ID * value until the request completes, since we have to pass it back to * the program via the NetEvent we generate at completion. Since the * thread isn't a garbage-collected object, we have to explicitly * register our reference on the ID value via a VM global variable. */ vm_globalvar_t *idg; }; /* ------------------------------------------------------------------------ */ /* * Match a domain name in a URL to the given string */ static int match_url_domain(const char *url, size_t urll, const char *domain) { /* note the length of the domain name we're matching */ size_t domainl = strlen(domain); /* scan to the end of the scheme name */ for ( ; urll > 0 ; ++url, --urll) { /* the scheme name ends in "://" */ if (urll > 3 && memcmp(url, "://", 3) == 0) { /* skip the :// sequence */ url += 3; urll -= 3; /* * we have a match if the domain name follows, and either it's * the whole rest of the string, or the next character after * the domain name is '/' or ':' */ return (urll >= domainl && memicmp(url, domain, domainl) == 0 && (urll == domainl || url[domainl] == ':' || url[domainl] == '/')); } } /* didn't find the end of the scheme name - fail */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Send a network request */ void CVmBifNet::send_net_request(VMG_ uint argc) { /* check arguments - we need (id, url), and after that it varies */ check_argc_range(vmg_ argc, 2, 32767); /* * set up a recursive VM context, in case we need it (we might, for * evaluating FileUpload properties in a form body lookup table) */ vm_rcdesc rc(vmg_ "sendNetRequest", CVmBifNet::bif_table, 6, G_stk->get(0), argc); /* get the network safety levels */ int client_level, server_level; G_host_ifc->get_net_safety(&client_level, &server_level); /* if the client safety level doesn't allow client operations, fail */ if (client_level >= VM_NET_SAFETY_MAXIMUM) err_throw(VMERR_NETWORK_SAFETY); /* retrieve the ID value - any type is allowed here */ vm_val_t *id = G_stk->get(0); /* retrieve the URL - this must be a string */ const char *url = get_str_val(vmg_ G_stk->get(1)); size_t urll = vmb_get_len(url); url += VMB_LEN; /* get the image URL, for logging purposes */ const char *image_url = ""; if (G_net_config != 0) image_url = G_net_config->get("image.url", ""); /* map the URL to the local file character set */ char *lurl; size_t lurll = G_cmap_to_file->map_utf8_alo(&lurl, url, urll); /* log the request */ vm_log_fmt(vmg_ "client request to %.*s by %s", (int)lurll, lurl, image_url); /* done with the local version of the URL */ t3free(lurl); /* check the protocol */ if (urll >= 7 && (memcmp(url, "http://", 7) == 0 || memcmp(url, "https://", 8) == 0)) { /* HTTP or HTTPS */ /* * if we're restricted to local access, verify that the domain is * "localhost" or "127.0.0.1" */ if (client_level == VM_NET_SAFETY_LOCALHOST) { /* get a pointer to the domain */ if (!match_url_domain(url, urll, "localhost") && !match_url_domain(url, urll, "127.0.0.1")) err_throw(VMERR_NETWORK_SAFETY); } /* get the verb - this must be a string */ const char *verb = get_str_val(vmg_ G_stk->get(2)); /* get the option flags, if present */ int32_t options = 0; if (argc >= 4 && G_stk->get(3)->typ != VM_NIL) options = G_stk->get(3)->num_to_int(vmg0_); /* get the headers, if present */ const char *hdrs = 0; if (argc >= 5 && G_stk->get(4)->typ != VM_NIL) hdrs = get_str_val(vmg_ G_stk->get(4)); /* get the content body, if present */ vm_val_t *body = 0; if (argc >= 6 && G_stk->get(5)->typ != VM_NIL) { /* get the body value */ body = G_stk->get(5); /* * verify the body type - this must be a string, byte array, or * lookup table */ if (body->get_as_string(vmg0_) != 0 || (body->typ == VM_OBJ && (CVmObjByteArray::is_byte_array(vmg_ body->val.obj) || CVmObjLookupTable::is_lookup_table_obj( vmg_ body->val.obj)))) { /* acceptable body type */ } else { /* invalid body type */ err_throw(VMERR_BAD_TYPE_BIF); } } /* get the body's MIME type, if present */ const char *body_type = 0; if (argc >= 7 && G_stk->get(6)->typ != VM_NIL) body_type = get_str_val(vmg_ G_stk->get(6)); /* create the request processor thread */ HttpReqThread *th = new HttpReqThread( vmg_ id, url - VMB_LEN, verb, options, hdrs, body, body_type, &rc); /* launch the thread */ if (!th->launch()) { /* * We couldn't launch the thread - post a result event * indicating failure. We have to post the result event * ourselves because (a) a result event always has to be * posted, even when the request fails, as it's the way that we * transmit all status information back to the caller, and (b) * the thread would normally post the result, but it won't be * doing so because it never got started. */ th->post_result(OS_HttpClient::ErrThread, 0, 0, 0); } /* done with the thread object */ th->release_ref(); } else { /* unknown protocol - it's an error */ err_throw(VMERR_BAD_VAL_BIF); } /* pop the arguments */ G_stk->discard(argc); /* there's no return value, but return nil to clear out any old data */ retval_nil(vmg0_); } frobtads-1.2.3/tads3/vmlookup.cpp0000644000175000001440000020045611745032163016113 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmlookup.cpp - LookupTable metaclass implementation Function Notes Modified 02/06/01 MJRoberts - Creation */ #include #include #include #include "t3std.h" #include "vmlookup.h" #include "vmerr.h" #include "vmerrnum.h" #include "vmtype.h" #include "vmglob.h" #include "vmobj.h" #include "vmundo.h" #include "vmmcreg.h" #include "vmfile.h" #include "vmbif.h" #include "vmmeta.h" #include "vmstack.h" #include "vmiter.h" #include "vmrun.h" #include "vmlst.h" #include "vmvec.h" /* ------------------------------------------------------------------------ */ /* * LookupTable undo record. We attach this record to the standard undo * record via the 'ptrval' field. */ struct lookuptab_undo_rec { /* action type */ enum lookuptab_undo_action action; /* key value */ vm_val_t key; }; /* ------------------------------------------------------------------------ */ /* * Allocate a new extension structure */ vm_lookup_ext *vm_lookup_ext::alloc_ext(VMG_ CVmObjLookupTable *self, uint bucket_cnt, uint value_cnt) { size_t siz; vm_lookup_ext *ext; /* calculate how much space we need */ siz = sizeof(vm_lookup_ext) + (bucket_cnt - 1) * sizeof(ext->buckets[0]) + value_cnt * sizeof(vm_lookup_val); /* allocate the memory */ ext = (vm_lookup_ext *)G_mem->get_var_heap()->alloc_mem(siz, self); /* remember the table sizes */ ext->bucket_cnt = bucket_cnt; ext->value_cnt = value_cnt; /* set the default value to nil initially */ ext->default_value.set_nil(); /* return the new extension */ return ext; } /* * initialize the buckets and free list */ void vm_lookup_ext::init_ext() { uint i; vm_lookup_val **bucketp; vm_lookup_val *valp; /* all of the buckets are initially empty */ for (i = bucket_cnt, bucketp = buckets ; i != 0 ; --i, ++bucketp) *bucketp = 0; /* * Initialize the value free list. We suballocate the value entry * structures out of our main allocation block; the available memory * for the structures comes immediately after the last bucket, so * bucketp points to the first value entry. */ valp = (vm_lookup_val *)(void *)bucketp; first_free = valp; for (first_free = valp, i = value_cnt ; i != 0 ; --i, ++valp) { /* link it into the free list */ valp->nxt = valp + 1; /* mark it as empty */ valp->key.set_empty(); valp->val.set_empty(); } /* terminate the free list at the last element */ (valp - 1)->nxt = 0; } /* * Reallocate an extension structure, adding more value entries. */ vm_lookup_ext *vm_lookup_ext::expand_ext(VMG_ CVmObjLookupTable *self, vm_lookup_ext *old_ext, uint new_value_cnt) { vm_lookup_ext *new_ext; /* allocate a new extension structure of the requested size */ new_ext = alloc_ext(vmg_ self, old_ext->bucket_cnt, new_value_cnt); /* copy the old extension data into the new extension */ new_ext->copy_ext_from(old_ext); /* delete the old memory */ G_mem->get_var_heap()->free_mem(old_ext); /* return the new extension */ return new_ext; } /* * Copy extension data */ void vm_lookup_ext::copy_ext_from(vm_lookup_ext *old_ext) { vm_lookup_val **oldbp; vm_lookup_val **newbp; vm_lookup_val *oldval; vm_lookup_val *newval; char *oldbase; char *newbase; uint i; /* the bucket counts must be the same in both extensions */ assert(bucket_cnt == old_ext->bucket_cnt); /* we must have at least as many entries as the old one had */ assert(value_cnt >= old_ext->value_cnt); /* copy the default value */ default_value = old_ext->default_value; /* get the new and old base values for faster pointer arithmetic */ oldbase = (char *)old_ext->idx_to_val(0); newbase = (char *)idx_to_val(0); /* copy the hash buckets from the old extension to the new extension */ for (i = old_ext->bucket_cnt, oldbp = old_ext->buckets, newbp = buckets ; i != 0 ; --i, ++newbp, ++oldbp) { /* copy the bucket pointer, adjusting to our local pointer scheme */ *newbp = (*oldbp == 0 ? 0 : (vm_lookup_val *)(((char *)*oldbp - oldbase) + newbase)); } /* copy the values from the old extension to the new extension */ for (i = old_ext->value_cnt, oldval = old_ext->idx_to_val(0), newval = idx_to_val(0) ; i != 0 ; --i, ++oldval, ++newval) { /* copy this entry */ newval->key = oldval->key; newval->val = oldval->val; newval->nxt = (oldval->nxt == 0 ? 0 : (vm_lookup_val *)(((char *)oldval->nxt - oldbase) + newbase)); } /* link all of the remaining free values into the free list */ first_free = newval; for (i = value_cnt - old_ext->value_cnt ; i != 0 ; --i, ++newval) { /* link this value into the free list */ newval->nxt = newval + 1; /* mark this free value as empty */ newval->key.set_empty(); newval->val.set_empty(); } /* terminate the free list */ (newval - 1)->nxt = 0; } /* ------------------------------------------------------------------------ */ /* * LookupTable object statics */ /* metaclass registration object */ static CVmMetaclassLookupTable metaclass_reg_obj; CVmMetaclass *CVmObjLookupTable::metaclass_reg_ = &metaclass_reg_obj; /* function table */ int (CVmObjLookupTable:: *CVmObjLookupTable::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjLookupTable::getp_undef, /* 0 */ &CVmObjLookupTable::getp_key_present, /* 1 */ &CVmObjLookupTable::getp_remove_entry, /* 2 */ &CVmObjLookupTable::getp_apply_all, /* 3 */ &CVmObjLookupTable::getp_for_each, /* 4 */ &CVmObjLookupTable::getp_count_buckets, /* 5 */ &CVmObjLookupTable::getp_count_entries, /* 6 */ &CVmObjLookupTable::getp_for_each_assoc, /* 7 */ &CVmObjLookupTable::getp_keys_to_list, /* 8 */ &CVmObjLookupTable::getp_vals_to_list, /* 9 */ &CVmObjLookupTable::getp_get_def_val, /* 10 */ &CVmObjLookupTable::getp_set_def_val, /* 11 */ &CVmObjLookupTable::getp_nthKey, /* 12 */ &CVmObjLookupTable::getp_nthVal /* 13 */ }; /* ------------------------------------------------------------------------ */ /* * Lookup Table metaclass implementation */ /* * create a lookup table with a given hash table size and the given * initial entry table size */ CVmObjLookupTable::CVmObjLookupTable(VMG_ size_t bucket_cnt, size_t val_cnt) { vm_val_t empty; /* set up an empty value */ empty.set_empty(); /* allocate our extension structure from the variable heap */ ext_ = (char *)vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); /* initialize the extension */ get_ext()->init_ext(); } /* * create */ vm_obj_id_t CVmObjLookupTable::create(VMG_ int in_root_set, uint bucket_count, uint init_capacity) { vm_obj_id_t id; /* allocate the object ID */ id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); /* create the object */ new (vmg_ id) CVmObjLookupTable(vmg_ bucket_count, init_capacity); /* return the new ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjLookupTable::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; size_t bucket_count; size_t init_capacity; vm_val_t src_obj; /* parse the arguments */ get_constructor_args(vmg_ argc, &bucket_count, &init_capacity, &src_obj); /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* create the object */ new (vmg_ id) CVmObjLookupTable(vmg_ bucket_count, init_capacity); /* populate it with the source list if desired */ ((CVmObjLookupTable *)vm_objp(vmg_ id))-> populate_from_list(vmg_ &src_obj); /* discard the source object gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* * get and check constructor arguments */ void CVmObjLookupTable::get_constructor_args(VMG_ uint argc, size_t *bucket_count, size_t *init_capacity, vm_val_t *src_obj) { /* presume no source object */ src_obj->set_nil(); /* check arguments */ if (argc == 0) { /* no arguments - they want default parameters */ *bucket_count = 32; *init_capacity = 64; } else if (argc == 1) { int cnt; /* * One argument - they want to create from a list. Retrieve the * list or vector object. */ G_stk->pop(src_obj); /* make sure it's a list or vector */ if (!src_obj->is_listlike(vmg0_) || (cnt = src_obj->ll_length(vmg0_)) < 0) err_throw(VMERR_BAD_VAL_BIF); /* use 1.5x the pair count as the bucket count and capacity */ *bucket_count = *init_capacity = cnt*3 / 2; } else if (argc == 2) { /* * two arguments - they specified the bucket count and initial * capacity; pop the parameters */ *bucket_count = (size_t)CVmBif::pop_long_val(vmg0_); *init_capacity = (size_t)CVmBif::pop_long_val(vmg0_); } else { /* invalid arguments */ err_throw(VMERR_WRONG_NUM_OF_ARGS); } /* re-push the source object to protect it from gc */ G_stk->push(src_obj); /* make sure both are positive */ if (*bucket_count <= 0 || *init_capacity <= 0) err_throw(VMERR_BAD_VAL_BIF); } /* * Populate the table from a source list or vector. This should only be * used during construction, because it doesn't save undo. (It's not * necessary to save undo during construction because the whole object * creation and construction is atomic for undo purposes: if we undo any of * this, we undo the whole creation of the object, so there's no need to * worry about saving old state internal to the object.) */ void CVmObjLookupTable::populate_from_list(VMG_ const vm_val_t *src) { /* copy elements from the source object */ int cnt = src->ll_length(vmg0_); int i; for (i = 1 ; i + 1 <= cnt ; ) { vm_val_t key, val; /* get the next two elements - they're always key, value pairs */ src->ll_index(vmg_ &key, i++); src->ll_index(vmg_ &val, i++); /* set them in the table */ set_or_add_entry(vmg_ &key, &val); } /* if there's a single remaining entry, it's the default value */ if (i <= cnt) src->ll_index(vmg_ &get_ext()->default_value, i); } /* * Create a copy of this object */ vm_obj_id_t CVmObjLookupTable::create_copy(VMG0_) { vm_obj_id_t id; CVmObjLookupTable *new_obj; /* allocate the object ID */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* allocate a new object */ new_obj = new (vmg_ id) CVmObjLookupTable(); /* * set up the new object with our same sizes, but don't initialize it * - we're going to blast all of our data directly into the new * extension, so we don't need to waste time setting up an empty * initial state */ new_obj->ext_ = (char *)vm_lookup_ext::alloc_ext( vmg_ new_obj, get_bucket_count(), get_entry_count()); /* copy my data to the next object */ new_obj->get_ext()->copy_ext_from(get_ext()); /* return the new object's id */ return id; } /* * notify of deletion */ void CVmObjLookupTable::notify_delete(VMG_ int /*in_root_set*/) { /* free our additional data, if we have any */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * set a property */ void CVmObjLookupTable::set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val) { /* no settable properties - throw an error */ err_throw(VMERR_INVALID_SETPROP); } /* * get a property */ int CVmObjLookupTable::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { uint func_idx; /* translate the property into a function vector index */ func_idx = G_meta_table ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from our base class */ return CVmObjCollection:: get_prop(vmg_ prop, retval, self, source_obj, argc); } /* * apply an undo record */ void CVmObjLookupTable::apply_undo(VMG_ struct CVmUndoRecord *undo_rec) { if (undo_rec->id.ptrval != 0) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* check the action in the record */ switch(rec->action) { case LOOKUPTAB_UNDO_NULL: /* * null record, which means that it had a weak reference to an * object that has been deleted - there's no way to reinstate * such records, so ignore it */ break; case LOOKUPTAB_UNDO_ADD: /* we added the entry, so we must now delete it */ del_entry(vmg_ &rec->key); break; case LOOKUPTAB_UNDO_DEL: /* we deleted the entry, so we must now add it */ add_entry(vmg_ &rec->key, &undo_rec->oldval); break; case LOOKUPTAB_UNDO_MOD: /* we modified the entry, so we must change it back */ mod_entry(vmg_ &rec->key, &undo_rec->oldval); break; case LOOKUPTAB_UNDO_DEFVAL: /* we modified the default value; change it back */ get_ext()->default_value = undo_rec->oldval; break; } /* discard the private record */ t3free(rec); /* clear the pointer in the main record so we know it's gone */ undo_rec->id.ptrval = 0; } } /* * discard extra undo information */ void CVmObjLookupTable::discard_undo(VMG_ CVmUndoRecord *rec) { /* delete our extra information record */ if (rec->id.ptrval != 0) { /* free the record */ t3free((lookuptab_undo_rec *)rec->id.ptrval); /* clear the pointer so we know it's gone */ rec->id.ptrval = 0; } } /* * Mark undo references. */ void CVmObjLookupTable:: mark_undo_ref(VMG_ struct CVmUndoRecord *undo_rec) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* if the key in the record is an object, mark it referenced */ if (rec->key.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->key.val.obj, VMOBJ_REACHABLE); /* if the value in the record is an object, mark it as well */ if (undo_rec->oldval.typ == VM_OBJ) G_obj_table->mark_all_refs(undo_rec->oldval.val.obj, VMOBJ_REACHABLE); } /* * Mark references. Keys (but not values) are strongly referenced. */ void CVmObjLookupTable::mark_refs(VMG_ uint state) { /* get my extension */ vm_lookup_ext *ext = get_ext(); /* mark the default value */ const vm_val_t *val = &ext->default_value; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); /* run through my buckets */ vm_lookup_val **bp = ext->buckets; size_t i = ext->bucket_cnt; for ( ; i != 0 ; ++bp, --i) { /* run through all entries attached to this bucket */ for (const vm_lookup_val *entry = *bp ; entry != 0 ; entry = entry->nxt) { /* if the key is an object, mark it as referenced */ val = &entry->key; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); /* if the entry is an object, mark it as referenced */ val = &entry->val; if (val->typ == VM_OBJ) G_obj_table->mark_all_refs(val->val.obj, state); } } } /* * load from an image file */ void CVmObjLookupTable::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from the image file */ void CVmObjLookupTable::reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* load our image data */ load_image_data(vmg_ ptr, siz); } /* * load or reload data from the image */ void CVmObjLookupTable::load_image_data(VMG_ const char *ptr, size_t siz) { uint bucket_cnt; uint val_cnt; uint i; const char *ibp; vm_lookup_val **ebp; vm_lookup_val *eval; vm_lookup_ext *ext; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the bucket and value counts from the header */ bucket_cnt = osrp2(ptr); val_cnt = osrp2(ptr + 2); /* allocate space for a copy of the image data */ ext = vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); ext_ = (char *)ext; /* initialize the free list pointer, given as a 1-based entry index */ ext->first_free = ext->img_idx_to_val(osrp2(ptr + 4)); /* initialize the table from the load image data */ for (i = bucket_cnt, ibp = ptr + 6, ebp = ext->buckets ; i != 0 ; --i, ibp += 2, ++ebp) { /* translate the image file index to a value pointer */ *ebp = ext->img_idx_to_val(osrp2(ibp)); } /* initialize the value entries */ for (i = val_cnt, eval = ext->idx_to_val(0) ; i != 0 ; --i, ibp += VMLOOKUP_VALUE_SIZE, ++eval) { /* read the key and value */ vmb_get_dh(ibp, &eval->key); vmb_get_dh(ibp + VMB_DATAHOLDER, &eval->val); /* remember the next pointer, which is given as a 1-based index */ eval->nxt = ext->img_idx_to_val(osrp2(ibp + VMB_DATAHOLDER*2)); } /* if a default value is included, read it */ ext->default_value.set_nil(); if (ibp + VMB_DATAHOLDER <= ptr + siz) { /* read the default value and skip it in the source data */ vmb_get_dh(ibp, &ext->default_value); ibp += VMB_DATAHOLDER; } } /* * save to a file */ void CVmObjLookupTable::save_to_file(VMG_ class CVmFile *fp) { vm_lookup_ext *ext = get_ext(); uint i; vm_lookup_val **bp; vm_lookup_val *val; char buf[VMLOOKUP_VALUE_SIZE]; /* write our bucket count, value count, and first-free index */ fp->write_uint2(ext->bucket_cnt); fp->write_uint2(ext->value_cnt); fp->write_uint2(ext->val_to_img_idx(ext->first_free)); /* * If the image file was compiled for version 030003 or later of the * LookupTable object, save the default value. The default value * wasn't added until 030003, so source code compiled against earlier * versions of the metaclass spec can't change it from the 'nil' * default. Furthermore, these image files could be loaded on VMs that * have older versions of the metaclass that won't know to look for a * default value in the loaded data, so to be compatible with those * readers we must omit the value entirely. If the image file was * compiled with 030003 or later, it can only be loaded by VMs with * LookupTable implementations that know to read the value. */ if (image_file_version_ge(vmg_ "030003")) { vmb_put_dh(buf, &ext->default_value); fp->write_bytes(buf, VMB_DATAHOLDER); } /* write the buckets */ for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp) fp->write_uint2(ext->val_to_img_idx(*bp)); /* write the values */ for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val) { uint idx; /* store the key, value, and index */ vmb_put_dh(buf, &val->key); vmb_put_dh(buf + VMB_DATAHOLDER, &val->val); idx = ext->val_to_img_idx(val->nxt); oswp2(buf + VMB_DATAHOLDER*2, idx); /* write the data */ fp->write_bytes(buf, VMLOOKUP_VALUE_SIZE); } } /* * restore from a file */ void CVmObjLookupTable::restore_from_file(VMG_ vm_obj_id_t self, CVmFile *fp, CVmObjFixup *fixups) { vm_lookup_ext *ext; char buf[64]; size_t bucket_cnt; size_t val_cnt; size_t i; vm_lookup_val **bp; vm_lookup_val *entry; uint idx; /* free our existing extension, if we have one */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); /* read the fixed fields */ fp->read_bytes(buf, 6); bucket_cnt = vmb_get_uint2(buf); val_cnt = vmb_get_uint2(buf + 2); /* allocate the extension structure */ ext = vm_lookup_ext::alloc_ext(vmg_ this, bucket_cnt, val_cnt); ext_ = (char *)ext; /* store the free pointer */ ext->first_free = ext->img_idx_to_val(vmb_get_uint2(buf + 4)); /* * if the saver was using version 030003 or later, there's a default * value; otherwise the default value is fixed at 'nil' */ ext->default_value.set_nil(); if (image_file_version_ge(vmg_ "030003")) { /* read the default value */ fp->read_bytes(buf, VMB_DATAHOLDER); /* apply fixups */ fixups->fix_dh(vmg_ buf); /* decode it into the default value in the extension */ vmb_get_dh(buf, &ext->default_value); } /* read the buckets */ for (i = bucket_cnt, bp = ext->buckets ; i != 0 ; ++bp, --i) { /* read this bucket pointer and store it */ idx = fp->read_uint2(); *bp = ext->img_idx_to_val(idx); } /* read the value entries */ for (i = val_cnt, entry = ext->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* read this value entry */ fp->read_bytes(buf, VMLOOKUP_VALUE_SIZE); /* fix up the key and value dataholders */ fixups->fix_dh_array(vmg_ buf, 2); /* store the key and value */ vmb_get_dh(buf, &entry->key); vmb_get_dh(buf + VMB_DATAHOLDER, &entry->val); /* store the next pointer */ idx = osrp2(buf + VMB_DATAHOLDER*2); entry->nxt = ext->img_idx_to_val(idx); } } /* * create an iterator */ void CVmObjLookupTable::new_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { vm_val_t copy_val; /* save a copy of myself for protection against garbage collection */ G_stk->push(self_val); /* * create a copy of my object, so that we can iterate over a snapshot * of our state even if we subsequently change our state */ copy_val.set_obj(create_copy(vmg0_)); /* set up a new lookup table iterator object on the copy */ retval->set_obj(CVmObjIterLookupTable::create_for_coll(vmg_ ©_val)); /* done with the gc protection */ G_stk->discard(); } /* * create a live iterator */ void CVmObjLookupTable::new_live_iterator(VMG_ vm_val_t *retval, const vm_val_t *self_val) { /* set up a new lookup table iterator directly on myself */ retval->set_obj(CVmObjIterLookupTable::create_for_coll(vmg_ self_val)); } /* * add an undo record */ void CVmObjLookupTable::add_undo_rec(VMG_ vm_obj_id_t self, lookuptab_undo_action action, const vm_val_t *key, const vm_val_t *old_entry_val) { lookuptab_undo_rec *rec; vm_val_t nil_val; /* allocate our record extension */ rec = (lookuptab_undo_rec *)t3malloc(sizeof(lookuptab_undo_rec)); /* if either the key or old value are null pointers, use a nil value */ nil_val.set_nil(); if (key == 0) key = &nil_val; if (old_entry_val == 0) old_entry_val = &nil_val; /* set up the record */ rec->action = action; rec->key = *key; /* add the record to the global undo stream */ if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, old_entry_val)) { /* * we didn't add an undo record, so our extra undo information * isn't actually going to be stored in the undo system - hence we * must delete our extra information */ t3free(rec); } } /* * Add an entry; does not generate undo. */ void CVmObjLookupTable::add_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { uint hashval; vm_lookup_val *entry; /* push the key and value as gc protection for a moment */ G_stk->push(key); G_stk->push(val); /* calculate the hash value for the key */ hashval = calc_key_hash(vmg_ key); /* allocate an entry */ entry = alloc_new_entry(vmg0_); /* link this entry into the chain at this hash value */ entry->nxt = get_ext()->buckets[hashval]; get_ext()->buckets[hashval] = entry; /* set the key and value for this entry */ entry->key = *key; entry->val = *val; /* discard the gc protection */ G_stk->discard(2); } /* * Add an entry, generating undo */ void CVmObjLookupTable::add_entry_undo(VMG_ vm_obj_id_t self, const vm_val_t *key, const vm_val_t *val) { /* * Add undo for the change. Since we're adding a new record, there's * no old value for the record. */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_ADD, key, 0); /* add the entry */ add_entry(vmg_ key, val); } /* * Delete an entry; does not generate undo. */ void CVmObjLookupTable::del_entry(VMG_ const vm_val_t *key) { uint hashval; vm_lookup_val *entry; vm_lookup_val *prv_entry; /* find the entry for the key */ entry = find_entry(vmg_ key, &hashval, &prv_entry); /* if we found it, unlink it */ if (entry != 0) unlink_entry(vmg_ entry, hashval, prv_entry); } /* * Unlink an entry */ void CVmObjLookupTable::unlink_entry(VMG_ vm_lookup_val *entry, uint hashval, vm_lookup_val *prv_entry) { vm_lookup_ext *ext = get_ext(); /* * set the previous item's next pointer, or the head of the chain, as * appropriate */ if (prv_entry == 0) ext->buckets[hashval] = entry->nxt; else prv_entry->nxt = entry->nxt; /* link the entry into the free list */ entry->nxt = ext->first_free; ext->first_free = entry; /* set this entry's key and value to 'empty' to indicate that it's free */ entry->key.set_empty(); entry->val.set_empty(); } /* * Modify an entry; does not generate undo. */ void CVmObjLookupTable::mod_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { vm_lookup_val *entry; /* find the entry for the key */ entry = find_entry(vmg_ key, 0, 0); /* if we found it, change the value to the given value */ if (entry != 0) entry->val = *val; } /* * Set an entry's value, generating undo */ void CVmObjLookupTable::set_entry_val_undo(VMG_ vm_obj_id_t self, vm_lookup_val *entry, const vm_val_t *val) { /* generate undo for the change */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_MOD, &entry->key, &entry->val); /* update the entry */ entry->val = *val; } /* * Find an entry for a given key. Returns the entry index, and fills in * the hash value and previous-entry output variables if provided. * Returns zero if there is no such key. */ vm_lookup_val *CVmObjLookupTable::find_entry(VMG_ const vm_val_t *key, uint *hashval_p, vm_lookup_val **prv_entry_p) { vm_lookup_ext *ext = get_ext(); uint hashval; vm_lookup_val *entry; vm_lookup_val *prv_entry; /* compute the hash value (and return it to the caller if desired) */ hashval = calc_key_hash(vmg_ key); if (hashval_p != 0) *hashval_p = hashval; /* scan the hash chain for this entry */ for (prv_entry = 0, entry = ext->buckets[hashval] ; entry != 0 ; prv_entry = entry, entry = entry->nxt) { /* if it matches the key we're looking for, this is the one */ if (entry->key.equals(vmg_ key)) break; } /* if the caller wanted to know the previous entry, tell them */ if (prv_entry_p != 0) *prv_entry_p = prv_entry; /* return the entry where we found the key, if indeed we found it */ return entry; } /* * Calculate a hash value for a key */ uint CVmObjLookupTable::calc_key_hash(VMG_ const vm_val_t *key) { uint hash; /* calculate the raw hash for the value */ hash = key->calc_hash(vmg0_); /* reduce the hash value modulo the hash table size */ hash %= get_bucket_count(); /* return the hash value */ return hash; } /* * Allocate a new entry */ vm_lookup_val *CVmObjLookupTable::alloc_new_entry(VMG0_) { /* if necessary, add more entry slots to the table */ expand_if_needed(vmg0_); /* get the first entry from the free list */ vm_lookup_val *entry = get_first_free(); /* unlink this entry from the free list */ set_first_free(entry->nxt); entry->nxt = 0; /* return the free entry */ return entry; } /* * Check for free space, and expand the table if necessary. */ void CVmObjLookupTable::expand_if_needed(VMG0_) { /* if we have any free entries left, we need do nothing */ if (get_first_free() != 0) return; /* increase the entry count by 50%, or 16 slots, whichever is more */ uint new_entry_cnt = get_entry_count() + (get_entry_count() >> 1); if (new_entry_cnt < get_entry_count() + 16) new_entry_cnt = get_entry_count() + 16; /* reallocate the extension at the new size */ ext_ = (char *)vm_lookup_ext::expand_ext( vmg_ this, get_ext(), new_entry_cnt); } /* ------------------------------------------------------------------------ */ /* * Get a value by index */ int CVmObjLookupTable::index_val_q(VMG_ vm_val_t *result, vm_obj_id_t self, const vm_val_t *index_val) { vm_lookup_val *entry; /* find the entry */ entry = find_entry(vmg_ index_val, 0, 0); /* if we found it, return it; otherwise, return nil */ if (entry != 0) { /* get the value from the entry and hand it back as the result */ *result = entry->val; } else { /* not found - return the default value */ *result = get_ext()->default_value; } /* handled */ return TRUE; } /* * Get a value by index, with an indication of whether or not the value * exists. */ int CVmObjLookupTable::index_check(VMG_ vm_val_t *result, const vm_val_t *idx) { /* find the entry */ vm_lookup_val *entry = find_entry(vmg_ idx, 0, 0); /* if we found it, return it; otherwise, return nil */ if (entry != 0) { /* get the indexed value */ *result = entry->val; /* indicate that the key is present is in the table */ return TRUE; } else { /* * not found - set a nil result value and indicate that the key * isn't in the table */ result->set_nil(); return FALSE; } } /* * Set or add an entry, with no undo */ void CVmObjLookupTable::set_or_add_entry(VMG_ const vm_val_t *key, const vm_val_t *val) { vm_lookup_val *entry; /* look for an existing entry with this key */ entry = find_entry(vmg_ key, 0, 0); /* if we found an existing entry, modify it; otherwise, add a new one */ if (entry != 0) { /* set the new value for this entry */ entry->val = *val; } else { /* add a new entry with the given key */ add_entry(vmg_ key, val); } } /* * Set a value by index */ int CVmObjLookupTable::set_index_val_q(VMG_ vm_val_t *new_container, vm_obj_id_t self, const vm_val_t *index_val, const vm_val_t *new_val) { vm_lookup_val *entry; /* look for an existing entry with this key */ entry = find_entry(vmg_ index_val, 0, 0); /* if we found an existing entry, modify it; otherwise, add a new one */ if (entry != 0) { /* set the new value for this entry */ set_entry_val_undo(vmg_ self, entry, new_val); } else { /* add a new entry with the given key */ add_entry_undo(vmg_ self, index_val, new_val); } /* we can be modified, hence the new container is the original one */ new_container->set_obj(self); /* handled */ return TRUE; } /* ------------------------------------------------------------------------ */ /* * Property evaluator - determine if the given key is in the table */ int CVmObjLookupTable::getp_key_present(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_lookup_val *entry; vm_val_t key; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the key */ G_stk->pop(&key); /* find the entry for this key */ entry = find_entry(vmg_ &key, 0, 0); /* return true if we found it, nil if not */ retval->set_logical(entry != 0); /* handled */ return TRUE; } /* * Property evaluator - remove an entry given a key */ int CVmObjLookupTable::getp_remove_entry(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_lookup_val *entry; vm_val_t key; uint hashval; vm_lookup_val *prv_entry; static CVmNativeCodeDesc desc(1); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the key */ G_stk->pop(&key); /* find the entry for this key */ entry = find_entry(vmg_ &key, &hashval, &prv_entry); /* if we found the entry, delete it */ if (entry != 0) { /* the return value is the value stored at this key */ *retval = entry->val; /* generate undo for the change */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_DEL, &key, retval); /* unlink the entry */ unlink_entry(vmg_ entry, hashval, prv_entry); } else { /* not found - the return value is nil */ retval->set_nil(); } /* handled */ return TRUE; } /* * Property evaluator - apply a callback to each entry in the table */ int CVmObjLookupTable::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { vm_val_t *fn; vm_lookup_val *entry; static CVmNativeCodeDesc desc(1); uint i; vm_rcdesc rc(vmg_ "LookupTable.applyAll", self, 3, G_stk->get(0), argc); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the function to invoke is the top argument */ fn = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* iterate over each entry */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* * if the entry is in use (it is if the key is non-empty), invoke * the callback on it */ if (entry->key.typ != VM_EMPTY) { /* push the arguments - (key, val) - push in reverse order */ G_stk->push(&entry->val); G_stk->push(&entry->key); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ fn, 2, &rc, 0); /* replace this element with the result */ set_entry_val_undo(vmg_ self, entry, G_interpreter->get_r0()); } } /* discard the arguments and gc protection */ G_stk->discard(2); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Property evaluator - invoke a callback for each entry in the table */ int CVmObjLookupTable::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* call the general processor */ vm_rcdesc rc(vmg_ "LookupTable.forEach", self, 4, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, FALSE, &rc); } /* * Property evaluator - invoke a callback for each entry in the table */ int CVmObjLookupTable::getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* call the general processor */ vm_rcdesc rc(vmg_ "List.forEachAssoc", self, 7, G_stk->get(0), argc); return for_each_gen(vmg_ self, retval, argc, TRUE, &rc); } /* * general processor for forEach/forEachAssoc */ int CVmObjLookupTable::for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int pass_key_to_cb, vm_rcdesc *rc) { vm_val_t *fn; vm_lookup_val *entry; static CVmNativeCodeDesc desc(1); uint i; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* the function to invoke is the top argument */ fn = G_stk->get(0); /* push a self-reference for gc protection */ G_stk->push()->set_obj(self); /* iterate over each bucket */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* * if it's in use (i.e., if the key is non-empty), invoke the * callback for the entry */ if (entry->key.typ != VM_EMPTY) { /* push the current value argument */ G_stk->push(&entry->val); /* push the key if desired */ if (pass_key_to_cb) G_stk->push(&entry->key); /* invoke the callback */ G_interpreter->call_func_ptr(vmg_ fn, pass_key_to_cb ? 2 : 1, rc, 0); } } /* discard the arguments and gc protection */ G_stk->discard(2); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * iterate over the table's contents through a callback */ void CVmObjLookupTable::for_each( VMG_ void (*cb)(VMG_ const vm_val_t *key, const vm_val_t *val, void *ctx), void *ctx) { vm_lookup_val *entry; uint i; /* iterate over each bucket */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* * if it's in use (i.e., if the key is non-empty), invoke the * callback for the entry */ if (entry->key.typ != VM_EMPTY) (*cb)(vmg_ &entry->key, &entry->val, ctx); } } /* * Property evaluator - get the number of buckets */ int CVmObjLookupTable::getp_count_buckets(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the number of buckets */ retval->set_int(get_bucket_count()); /* handled */ return TRUE; } /* * Property evaluator - get the number of entries in use */ int CVmObjLookupTable::getp_count_entries(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); uint i; int cnt; vm_lookup_val *entry; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* run through the table and count in-use entries */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* if the entry is not marked as free, count it as used */ if (entry->key.typ != VM_EMPTY) ++cnt; } /* return the number of in-use entries we counted */ retval->set_int(cnt); /* handled */ return TRUE; } /* * Property evaluator - make a list of all of the keys in the table */ int CVmObjLookupTable::getp_keys_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general list maker, listing only the keys */ return getp_make_list(vmg_ self, retval, argc, TRUE); } /* * Property evaluator - make a list of all of the values in the table */ int CVmObjLookupTable::getp_vals_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* use the general list maker, listing only the values */ return getp_make_list(vmg_ self, retval, argc, FALSE); } /* * General handler for keys_to_list and vals_to_list */ int CVmObjLookupTable::getp_make_list(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc, int store_keys) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* push self while we're working, for gc protection */ G_stk->push()->set_obj(self); /* make the list */ make_list(vmg_ retval, store_keys, 0); /* discard our gc protection */ G_stk->discard(); /* handled */ return TRUE; } /* * Low-level list maker. Creates a list of all of the keys or values in * the table. */ void CVmObjLookupTable::make_list(VMG_ vm_val_t *retval, int store_keys, int (*filter)(VMG_ const vm_val_t *, const vm_val_t *)) { vm_lookup_val *entry; uint i; int cnt; CVmObjList *lst; /* run through the table and count in-use entries */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* skip empties */ if (entry->key.typ == VM_EMPTY) continue; /* skip values that the filter function rejects */ if (filter != 0 && !filter(vmg_ &entry->key, &entry->val)) continue; /* count it */ ++cnt; } /* allocate a list to store the results */ retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt)); /* get the list object */ lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); lst->cons_clear(); /* populate the list */ for (cnt = 0, i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; ++entry, --i) { /* if the entry is not marked as free, count it as used */ if (entry->key.typ != VM_EMPTY) { /* skip values that the filter function rejects */ if (filter != 0 && !filter(vmg_ &entry->key, &entry->val)) continue; /* store the key or value, as appropriate */ if (store_keys) { /* store the key */ lst->cons_set_element(cnt, &entry->key); } else { /* store the value */ lst->cons_set_element(cnt, &entry->val); } /* update the destination index */ ++cnt; } } } /* * Property evaluator: get the default value */ int CVmObjLookupTable::getp_get_def_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* return the default value */ *retval = get_ext()->default_value; return TRUE; } /* get the default value - internal API version */ void CVmObjLookupTable::get_default_val(vm_val_t *val) { *val = get_ext()->default_value; } /* * Property evaluator: set the default value */ int CVmObjLookupTable::getp_set_def_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* save undo */ add_undo_rec(vmg_ self, LOOKUPTAB_UNDO_DEFVAL, 0, &get_ext()->default_value); /* set the default value to the argument value */ G_stk->pop(&get_ext()->default_value); /* return self */ retval->set_obj(self); return TRUE; } /* * Property evaluator: get the nth key */ int CVmObjLookupTable::getp_nthKey(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the nth key value */ get_nth_ele(vmg_ retval, 0, self, CVmBif::pop_long_val(vmg0_)); /* handled */ return TRUE; } /* * Property evaluator: get the nth value */ int CVmObjLookupTable::getp_nthVal(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(1); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the nth value */ get_nth_ele(vmg_ 0, retval, self, CVmBif::pop_long_val(vmg0_)); /* handled */ return TRUE; } /* * Get the nth element's key and/or value */ void CVmObjLookupTable::get_nth_ele(VMG_ vm_val_t *key, vm_val_t *val, vm_obj_id_t self, long idx) { uint i; vm_lookup_val *entry; /* * if the index is zero, and we're asking for the value, return the * default value */ if (idx == 0) { if (key != 0) key->set_nil(); if (val != 0) *val = get_ext()->default_value; return; } /* iterate over our buckets */ for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ; i != 0 ; --i, ++entry) { /* if it's in use (i.e., the key isn't empty), count it */ if (entry->key.typ != VM_EMPTY) { /* count it */ --idx; /* if this is the one we're looking for, return it */ if (idx == 0) { /* fill in the key and/or value elements */ if (key != 0) *key = entry->key; if (val != 0) *val = entry->val; /* done */ return; } } } /* we didn't find it - the index is out of range */ err_throw(VMERR_INDEX_OUT_OF_RANGE); } /* ------------------------------------------------------------------------ */ /* * WeakRefLookupTable */ /* * statics */ /* metaclass registration object */ static CVmMetaclassWeakRefLookupTable weak_metaclass_reg_obj; CVmMetaclass *CVmObjWeakRefLookupTable::metaclass_reg_ = &weak_metaclass_reg_obj; /* * create */ vm_obj_id_t CVmObjWeakRefLookupTable:: create(VMG_ int in_root_set, uint bucket_count, uint init_capacity) { vm_obj_id_t id; /* allocate the object ID */ id = vm_new_id(vmg_ in_root_set, TRUE, TRUE); /* create the object */ new (vmg_ id) CVmObjWeakRefLookupTable(vmg_ bucket_count, init_capacity); /* return the new ID */ return id; } /* * create dynamically using stack arguments */ vm_obj_id_t CVmObjWeakRefLookupTable::create_from_stack( VMG_ const uchar **pc_ptr, uint argc) { vm_obj_id_t id; size_t bucket_count; size_t init_capacity; vm_val_t src_obj; /* parse the arguments */ get_constructor_args(vmg_ argc, &bucket_count, &init_capacity, &src_obj); /* * allocate the object ID - this type of construction never creates a * root object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* create the object */ new (vmg_ id) CVmObjWeakRefLookupTable(vmg_ bucket_count, init_capacity); /* populate it with the source list if desired */ ((CVmObjLookupTable *)vm_objp(vmg_ id))-> populate_from_list(vmg_ &src_obj); /* discard the source object gc protection */ G_stk->discard(); /* return the new ID */ return id; } /* * Mark undo references. Keys are strongly referenced, so mark the key in * the record, if present. */ void CVmObjWeakRefLookupTable:: mark_undo_ref(VMG_ struct CVmUndoRecord *undo_rec) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* if the key in the record is an object, mark it referenced */ if (rec->key.typ == VM_OBJ) G_obj_table->mark_all_refs(rec->key.val.obj, VMOBJ_REACHABLE); } /* * Mark references. Keys (but not values) are strongly referenced. */ void CVmObjWeakRefLookupTable::mark_refs(VMG_ uint state) { vm_lookup_val **bp; uint i; /* note that the default value is weakly referenced, so don't mark it */ /* run through my buckets */ for (i = get_ext()->bucket_cnt, bp = get_ext()->buckets ; i != 0 ; --i, ++bp) { const vm_lookup_val *entry; /* run through all entries attached to this bucket */ for (entry = *bp ; entry != 0 ; entry = entry->nxt) { /* if the key is an object, mark it as referenced */ if (entry->key.typ == VM_OBJ) G_obj_table->mark_all_refs(entry->key.val.obj, state); /* * note that we only reference our value weakly, so do not * mark it here */ } } } /* * Remove stale weak references from an undo record. Value entries are * weakly referenced. */ void CVmObjWeakRefLookupTable:: remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *undo_rec) { int forget_record; /* presume we won't want to forget the record */ forget_record = FALSE; /* * if the old value in the undo record refers to an object, forget * about it */ if (G_obj_table->is_obj_deletable(&undo_rec->oldval)) { /* * the object is being deleted - since we only weakly reference * our objects, we must forget about this object now; this also * means we can forget about the record entirely, since there is * no need to apply it in the future now that it doesn't do * anything */ undo_rec->oldval.set_nil(); forget_record = TRUE; } /* delete our extended record if we're forgetting the record */ if (undo_rec->id.ptrval != 0 && forget_record) { lookuptab_undo_rec *rec; /* get our private record from the standard undo record */ rec = (lookuptab_undo_rec *)undo_rec->id.ptrval; /* we don't care about the key value in this record any more */ rec->key.set_nil(); /* mark the record as forgotten */ rec->action = LOOKUPTAB_UNDO_NULL; } } /* * Remove stale weak references. Our values are weak references, so * remove any entries that have values that are about to be deleted. */ void CVmObjWeakRefLookupTable::remove_stale_weak_refs(VMG0_) { vm_lookup_val **bucket; uint i; uint hashval; /* remove the default value if it's gone stale */ if (G_obj_table->is_obj_deletable(&get_ext()->default_value)) get_ext()->default_value.set_nil(); /* run through each bucket */ for (hashval = 0, bucket = get_ext()->buckets, i = get_bucket_count() ; i != 0 ; ++bucket, --i, ++hashval) { vm_lookup_val *entry; vm_lookup_val *prv; vm_lookup_val *nxt; /* run through all entries attached to this bucket */ for (prv = 0, entry = *bucket ; entry != 0 ; entry = nxt) { /* remember the next entry, in case we delete this one */ nxt = entry->nxt; /* * if the value has an object reference that has become stale, * delete the entry */ if (G_obj_table->is_obj_deletable(&entry->val)) { /* it's deletable, so delete the entire record */ unlink_entry(vmg_ entry, hashval, prv); /* * note that we do NOT advance the prv pointer - we've * deleted this entry, so the one preceding it is now the * previous for the one following this one */ } else { /* we're keeping this entry - make it the new previous */ prv = entry; } } } } /* ------------------------------------------------------------------------ */ /* * LookupTable Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIterLookupTable iter_metaclass_reg_obj; CVmMetaclass *CVmObjIterLookupTable::metaclass_reg_ = &iter_metaclass_reg_obj; /* create a list with no initial contents */ vm_obj_id_t CVmObjIterLookupTable::create_for_coll(VMG_ const vm_val_t *coll) { vm_obj_id_t id; /* push the collection object reference for gc protection */ G_stk->push(coll); /* create a non-root-set object */ id = vm_new_id(vmg_ FALSE, TRUE, TRUE); /* instantiate the iterator */ new (vmg_ id) CVmObjIterLookupTable(vmg_ coll); /* done with the gc protection */ G_stk->discard(); /* return the new object's ID */ return id; } /* * constructor */ CVmObjIterLookupTable::CVmObjIterLookupTable(VMG_ const vm_val_t *coll) { /* allocate space for our extension data */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERLOOKUPTABLE_EXT_SIZE, this); /* save the collection value */ vmb_put_dh(ext_, coll); /* clear the flags */ set_flags(0); /* * set up at index zero - we're not at a valid entry until initialized * with getNext */ set_entry_index(0); } /* * Find the first valid entry, starting at the given entry index. If the * given index refers to a valid entry, we return it. If there is no * valid next entry, we return zero. */ uint CVmObjIterLookupTable::find_first_valid_entry(VMG_ uint entry) const { CVmObjLookupTable *ltab; vm_val_t coll; /* get my lookup table object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* if we're already past the last item, we have nothing to find */ if (entry > ltab->get_entry_count()) return 0; /* * scan entries until we find one with a non-empty key (an empty key * value indicates that the entry is free) */ for ( ; entry <= ltab->get_entry_count() ; ++entry) { vm_lookup_val *entryp; /* translate the 1-based index to an entry pointer */ entryp = ltab->get_ext()->idx_to_val(entry - 1); /* if it's non-empty, this is the one we want */ if (entryp->key.typ != VM_EMPTY) return entry; } /* we didn't find a valid entry */ return 0; } /* * notify of deletion */ void CVmObjIterLookupTable::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * property evaluator - get the next item */ int CVmObjIterLookupTable::getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { uint entry; static CVmNativeCodeDesc desc(0); CVmObjLookupTable *ltab; vm_val_t coll; /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get my collection object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* advance to the next valid index after the current one */ entry = find_first_valid_entry(vmg_ get_entry_index() + 1); /* * if we're past the end of the table, set the index to zero to * indicate that we're done */ if (entry == 0) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the current entry */ *retval = ltab->get_ext()->idx_to_val(entry - 1)->val; /* update our internal state, saving undo */ set_entry_index_undo(vmg_ self, entry); /* handled */ return TRUE; } /* * property evaluator - get current value */ int CVmObjIterLookupTable::getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the entry's value */ get_cur_entry(vmg_ retval, 0); /* handled */ return TRUE; } /* * property evaluator - get current key */ int CVmObjIterLookupTable::getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the entry's key */ get_cur_entry(vmg_ 0, retval); /* handled */ return TRUE; } /* * Get the current entry index, value, and key for the current entry. Any * of the output pointers can be null if the corresponding value is not * needed by the caller. */ void CVmObjIterLookupTable::get_cur_entry(VMG_ vm_val_t *valp, vm_val_t *keyp) const { uint entry; vm_val_t coll; CVmObjLookupTable *ltab; vm_lookup_val *entryp; /* get my collection object */ get_coll_val(&coll); ltab = (CVmObjLookupTable *)vm_objp(vmg_ coll.val.obj); /* make sure the index is in range */ entry = get_entry_index(); if (entry < 1 || entry > ltab->get_entry_count()) err_throw(VMERR_OUT_OF_RANGE); /* translate our 1-based index to an entry pointer */ entryp = ltab->get_ext()->idx_to_val(entry - 1); /* if the current entry has been deleted, we have no valid current item */ if (entryp->key.typ == VM_EMPTY) err_throw(VMERR_OUT_OF_RANGE); /* retrieve this entry's value and key for the caller, as desired */ if (valp != 0) *valp = entryp->val; if (keyp != 0) *keyp = entryp->key; } /* * property evaluator - is next value available? */ int CVmObjIterLookupTable::getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { uint entry; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* find the index of the next valid entry */ entry = find_first_valid_entry(vmg_ get_entry_index() + 1); /* return true if we have a valid index, false if not */ retval->set_logical(entry != 0); /* handled */ return TRUE; } /* * property evaluator - reset to first item */ int CVmObjIterLookupTable::getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set our entry index back to zero, saving undo */ set_entry_index_undo(vmg_ self, 0); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set the next index value, saving undo if necessary */ void CVmObjIterLookupTable::set_entry_index_undo( VMG_ vm_obj_id_t self, uint entry_idx) { /* save undo if necessary */ if (G_undo != 0 && !(get_flags() & VMOBJITERLOOKUPTABLE_UNDO)) { vm_val_t val; /* * Add the undo record. Store the entry index as the key (it's * not really a key, but it'll do as a place to store the data). * We don't need anything in the payload, so store a dummy nil * value. */ val.set_nil(); G_undo->add_new_record_int_key(vmg_ self, get_entry_index(), &val); /* * set the undo bit so we don't save redundant undo for this * savepoint */ set_flags(get_flags() | VMOBJITERLOOKUPTABLE_UNDO); } /* set the index */ set_entry_index(entry_idx); } /* * apply undo */ void CVmObjIterLookupTable::apply_undo(VMG_ CVmUndoRecord *rec) { /* * the integer key in the undo record is my saved index value (and is * the only thing in our state that can ever change) */ set_entry_index((uint)rec->id.intval); } /* * mark references */ void CVmObjIterLookupTable::mark_refs(VMG_ uint state) { vm_val_t coll; /* my collection is always an object - mark it as referenced */ get_coll_val(&coll); G_obj_table->mark_all_refs(coll.val.obj, state); } /* * load from an image file */ void CVmObjIterLookupTable::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * Allocate a new extension. Make sure it's at least as large as the * current standard extension size. */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(siz < VMOBJITERLOOKUPTABLE_EXT_SIZE ? VMOBJITERLOOKUPTABLE_EXT_SIZE : siz, this); /* copy the image data to our extension */ memcpy(ext_, ptr, siz); /* * save our image data pointer in the object table, so that we can * access it (without storing it ourselves) during a reload */ G_obj_table->save_image_pointer(self, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* * re-load from an image file */ void CVmObjIterLookupTable::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* re-copy the image data */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } /* * save */ void CVmObjIterLookupTable::save_to_file(VMG_ CVmFile *fp) { /* write my extension */ fp->write_bytes(ext_, VMOBJITERLOOKUPTABLE_EXT_SIZE); } /* * restore */ void CVmObjIterLookupTable::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *fixups) { /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate a new extension */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERLOOKUPTABLE_EXT_SIZE, this); /* read my extension */ fp->read_bytes(ext_, VMOBJITERLOOKUPTABLE_EXT_SIZE); /* fix up my collection object reference */ fixups->fix_dh(vmg_ ext_); } frobtads-1.2.3/tads3/vmiter.cpp0000644000175000001440000003002411652356662015547 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmiter.cpp - T3 iterator metaclass Function Notes Modified 04/22/00 MJRoberts - Creation */ #include #include "vmtype.h" #include "vmobj.h" #include "vmiter.h" #include "vmglob.h" #include "vmmeta.h" #include "vmstack.h" #include "vmundo.h" #include "vmlst.h" #include "vmfile.h" /* ------------------------------------------------------------------------ */ /* * Base Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIter iter_metaclass_reg_obj; CVmMetaclass *CVmObjIter::metaclass_reg_ = &iter_metaclass_reg_obj; /* function table */ int (CVmObjIter:: *CVmObjIter::func_table_[])(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) = { &CVmObjIter::getp_undef, &CVmObjIter::getp_get_next, &CVmObjIter::getp_is_next_avail, &CVmObjIter::getp_reset_iter, &CVmObjIter::getp_get_cur_key, &CVmObjIter::getp_get_cur_val }; /* * get a property */ int CVmObjIter::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc) { /* translate the property into a function vector index */ uint func_idx = G_meta_table->prop_to_vector_idx( metaclass_reg_->get_reg_idx(), prop); /* call the appropriate function */ if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) { *source_obj = metaclass_reg_->get_class_obj(vmg0_); return TRUE; } /* inherit default handling from the base object class */ return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc); } /* ------------------------------------------------------------------------ */ /* * Indexed Iterator metaclass */ /* * statics */ /* metaclass registration object */ static CVmMetaclassIterIdx idx_metaclass_reg_obj; CVmMetaclass *CVmObjIterIdx::metaclass_reg_ = &idx_metaclass_reg_obj; /* create a list with no initial contents */ vm_obj_id_t CVmObjIterIdx::create_for_coll(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index) { vm_obj_id_t id; /* push the collection object reference for gc protection */ G_stk->push(coll); /* create a non-root-set object */ id = vm_new_id(vmg_ FALSE, TRUE, FALSE); /* instantiate the iterator */ new (vmg_ id) CVmObjIterIdx(vmg_ coll, first_valid_index, last_valid_index); /* done with the gc protection */ G_stk->discard(); /* return the new object's ID */ return id; } /* * constructor */ CVmObjIterIdx::CVmObjIterIdx(VMG_ const vm_val_t *coll, long first_valid_index, long last_valid_index) { /* allocate space for our extension data */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERIDX_EXT_SIZE, this); /* save the collection value */ vmb_put_dh(ext_, coll); /* * set the current index to the first index minus 1, so that we start * with the first element when we make our first call to getNext() */ set_cur_index_no_undo(first_valid_index - 1); /* remember the first and last valid index values */ set_first_valid(first_valid_index); set_last_valid(last_valid_index); /* clear the flags */ set_flags(0); } /* * notify of deletion */ void CVmObjIterIdx::notify_delete(VMG_ int) { /* free our extension */ if (ext_ != 0) G_mem->get_var_heap()->free_mem(ext_); } /* * property evaluator - get the current item's value */ int CVmObjIterIdx::getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { long idx; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the current index */ idx = get_cur_index(); /* if the current value is out of range, throw an error */ if (idx < 1 || idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the value for this index */ get_indexed_val(vmg_ idx, retval); /* handled */ return TRUE; } /* * property evaluator - get the current item's key */ int CVmObjIterIdx::getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { long idx; static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the current index */ idx = get_cur_index(); /* if the current value is out of range, throw an error */ if (idx < 1 || idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* return the index */ retval->set_int(idx); /* handled */ return TRUE; } /* * property evaluator - get the next item */ int CVmObjIterIdx::getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* get the next index, which is one higher than the current index */ long idx = get_cur_index() + 1; /* if the current value is out of range, throw an error */ if (idx > get_last_valid()) err_throw(VMERR_OUT_OF_RANGE); /* retrieve the value */ get_indexed_val(vmg_ idx, retval); /* save the current index in our internal state */ set_cur_index(vmg_ self, idx); /* handled */ return TRUE; } /* * get the next iteration item */ int CVmObjIterIdx::iter_next(VMG_ vm_obj_id_t self, vm_val_t *val) { /* get the next index, which is one higher than the current index */ long idx = get_cur_index() + 1; /* if the current value is out of range, throw an error */ if (idx <= get_last_valid()) { /* retrieve the value */ get_indexed_val(vmg_ idx, val); /* save the current index in our internal state */ set_cur_index(vmg_ self, idx); /* we got a value */ return TRUE; } else { /* no more values available */ return FALSE; } } /* * Retrieve an indexed value from my collection */ void CVmObjIterIdx::get_indexed_val(VMG_ long idx, vm_val_t *retval) { vm_val_t coll; vm_val_t idx_val; /* get my collection value and the next index value */ get_coll_val(&coll); /* check to see if we have an object or a constant list */ switch(coll.typ) { case VM_LIST: /* it's a constant list - index the constant value */ CVmObjList::index_list(vmg_ retval, coll.get_as_list(vmg0_), idx); break; case VM_OBJ: /* it's an object - index it */ idx_val.set_int(idx); vm_objp(vmg_ coll.val.obj) ->index_val_ov(vmg_ retval, coll.val.obj, &idx_val); break; default: /* * Anything else is an error. We really should never be able to * get here, since the only way to instantiate an iterator should * be via the collection object's createIter() method; so if we * get here it must be an internal error, hence we could probably * assert failure here. Nonetheless, just throw an error, since * this will make for a more pleasant indication of the problem. */ err_throw(VMERR_CANNOT_INDEX_TYPE); break; } } /* * property evaluator - is next value available? */ int CVmObjIterIdx::getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { /* check arguments */ static CVmNativeCodeDesc desc(0); if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* * if the current index plus one is less than or equal to the last * valid index, another item is available */ retval->set_logical(get_cur_index() + 1 <= get_last_valid()); /* handled */ return TRUE; } /* * property evaluator - reset to first item */ int CVmObjIterIdx::getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) { static CVmNativeCodeDesc desc(0); /* check arguments */ if (get_prop_check_argc(retval, argc, &desc)) return TRUE; /* set the index to the first valid index minus one */ set_cur_index(vmg_ self, get_first_valid() - 1); /* no return value */ retval->set_nil(); /* handled */ return TRUE; } /* * Set the current index value, saving undo if necessary */ void CVmObjIterIdx::set_cur_index(VMG_ vm_obj_id_t self, long idx) { /* save undo if necessary */ if (G_undo != 0 && !(get_flags() & VMOBJITERIDX_UNDO)) { vm_val_t dummy; /* * Add the undo record. Note that the only information we need * to store is the index value, so we can store this as the key * value - supply a dummy payload, since we have no use for it. */ dummy.set_nil(); G_undo->add_new_record_int_key(vmg_ self, get_cur_index(), &dummy); /* * set the undo bit so we don't save redundant undo for this * savepoint */ set_flags(get_flags() | VMOBJITERIDX_UNDO); } /* set the index */ set_cur_index_no_undo(idx); } /* * apply undo */ void CVmObjIterIdx::apply_undo(VMG_ CVmUndoRecord *rec) { /* * the integer key in the undo record is my saved index value (and * is the only thing in an indexed iterator that can ever change) */ set_cur_index_no_undo(rec->id.intval); } /* * mark references */ void CVmObjIterIdx::mark_refs(VMG_ uint state) { vm_val_t coll; /* if my collection is an object, mark it as referenced */ get_coll_val(&coll); if (coll.typ == VM_OBJ) G_obj_table->mark_all_refs(coll.val.obj, state); } /* * load from an image file */ void CVmObjIterIdx::load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* if we already have memory allocated, free it */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* * Allocate a new extension. Make sure it's at least as large as * the current standard extension size. */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(siz < VMOBJITERIDX_EXT_SIZE ? VMOBJITERIDX_EXT_SIZE : siz, this); /* copy the image data to our extension */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERIDX_UNDO); /* save our image data pointer, so we can use it for reloading */ G_obj_table->save_image_pointer(self, ptr, siz); } /* * reload from an image file */ void CVmObjIterIdx::reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz) { /* copy the image data over our data */ memcpy(ext_, ptr, siz); /* clear the undo flag */ set_flags(get_flags() & ~VMOBJITERIDX_UNDO); } /* * save */ void CVmObjIterIdx::save_to_file(VMG_ CVmFile *fp) { /* write my extension */ fp->write_bytes(ext_, VMOBJITERIDX_EXT_SIZE); } /* * restore */ void CVmObjIterIdx::restore_from_file(VMG_ vm_obj_id_t, CVmFile *fp, CVmObjFixup *fixups) { /* free any existing extension */ if (ext_ != 0) { G_mem->get_var_heap()->free_mem(ext_); ext_ = 0; } /* allocate a new extension */ ext_ = (char *)G_mem->get_var_heap() ->alloc_mem(VMOBJITERIDX_EXT_SIZE, this); /* read my extension */ fp->read_bytes(ext_, VMOBJITERIDX_EXT_SIZE); /* fix up my collection object reference */ fixups->fix_dh(vmg_ ext_); } frobtads-1.2.3/tads3/vmwrtimg.cpp0000644000175000001440000006177211705667360016132 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/vmwrtimg.cpp,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmwrtimg.cpp - T3 Image File Writer utility functions Function Provides functions to write an image file Notes Modified 04/04/99 MJRoberts - Creation */ #include #include #include "t3std.h" #include "vmfile.h" #include "vmwrtimg.h" #include "vmimage.h" /* ------------------------------------------------------------------------ */ /* * initialize */ CVmImageWriter::CVmImageWriter(CVmFile *fp) { /* remember the underlying file */ fp_ = fp; /* no block is currently open */ block_start_ = 0; } /* * delete */ CVmImageWriter::~CVmImageWriter() { } /* ------------------------------------------------------------------------ */ /* * get the current seek position in the underlying file */ long CVmImageWriter::get_pos() const { return fp_->get_pos(); } /* ------------------------------------------------------------------------ */ /* * Prepare the file - write the header. */ void CVmImageWriter::prepare(uint vsn, const char tool_data[4]) { /* write the signature */ fp_->write_bytes(VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1); /* write the version number */ fp_->write_uint2(vsn); /* write the 28 reserved bytes, setting all to zero */ char buf[32]; memset(buf, 0, 28); fp_->write_bytes(buf, 28); /* write the additional 4 bytes reserved for tool use */ fp_->write_bytes(tool_data, 4); /* write the compilation timestamp */ os_time_t timer = os_time(NULL); struct tm *tblock = os_localtime(&timer); fp_->write_bytes(asctime(tblock), 24); } /* ------------------------------------------------------------------------ */ /* * Begin a block */ void CVmImageWriter::begin_block(const char *block_id, int mandatory) { char buf[10]; uint flags; /* if there's a block currently open, close it */ end_block(); /* remember the seek location of the start of the block */ block_start_ = fp_->get_pos(); /* store the type string */ memcpy(buf, block_id, 4); /* store four bytes of zeros as a placeholder for the size */ memset(buf+4, 0, 4); /* compute the flags */ flags = 0; if (mandatory) flags |= VMIMAGE_DBF_MANDATORY; /* store the flags */ oswp2(buf+8, flags); /* write the header */ fp_->write_bytes(buf, 10); } /* ------------------------------------------------------------------------ */ /* * End the current block. If no block is open, this does nothing. */ void CVmImageWriter::end_block() { long end_pos; uint32_t siz; /* if there's no block open, there's nothing we need to do */ if (block_start_ == 0) return; /* * note the current file position - this will let us compute the * size of the block, and we'll need to seek back here when we're * done updating the block header */ end_pos = fp_->get_pos(); /* * Since the block is finished, we can now compute its size. The * size of the data block is the end seek position minus the * starting seek position. 'block_start_' contains the seek * position of the block header, which takes up ten bytes; we want * to store the size of the block's data, excluding the header, * which is (end_pos - block_header_pos - 10). */ siz = (uint32_t)(end_pos - block_start_ - 10); /* * Seek back to the location of the size field in the block header; * this is four bytes into the block header. Then, update the size * field with the size of the block's data. */ fp_->set_pos(block_start_ + 4); fp_->write_uint4(siz); /* * seek back to the end of the block, so we can resume writing data * following the block */ fp_->set_pos(end_pos); /* the block is now closed, so forget about it */ block_start_ = 0; } /* ------------------------------------------------------------------------ */ /* * Write raw bytes to the file */ void CVmImageWriter::write_bytes(const char *ptr, uint32_t siz) { /* write in 64k chunks, to accommodate 16-bit platforms */ while (siz != 0) { size_t cur; /* get the next 64k, or the remainder if less than 64k is left */ cur = 65535; if (siz < cur) cur = (size_t)siz; /* write this chunk */ fp_->write_bytes(ptr, cur); /* advance past this chunk */ ptr += cur; siz -= cur; } } /* ------------------------------------------------------------------------ */ /* * Finish the file. Closes the current block if one is open, and writes * the end-of-file marker to the file. */ void CVmImageWriter::finish() { /* if there's a block open, close it */ end_block(); /* * write the EOF block - the block contains no data, so simply begin * and end it */ begin_block("EOF ", TRUE); end_block(); } /* ------------------------------------------------------------------------ */ /* * Write an entrypoint (ENTP) block */ void CVmImageWriter::write_entrypt(uint32_t entry_ofs, size_t method_hdr_size, size_t exc_entry_size, size_t line_entry_size, size_t dbg_hdr_size, size_t dbg_lclsym_hdr_size, size_t dbg_frame_hdr_size, int dbg_vsn_id) { char buf[32]; /* prepare the block's contents */ oswp4(buf, entry_ofs); oswp2(buf+4, method_hdr_size); oswp2(buf+6, exc_entry_size); oswp2(buf+8, line_entry_size); oswp2(buf+10, dbg_hdr_size); oswp2(buf+12, dbg_lclsym_hdr_size); oswp2(buf+14, dbg_vsn_id); oswp2(buf+16, dbg_frame_hdr_size); /* open the block, write the data, and close the block */ begin_block("ENTP", TRUE); fp_->write_bytes(buf, 18); end_block(); } /* ------------------------------------------------------------------------ */ /* * Write a function set dependency block */ void CVmImageWriter::write_func_dep(const char **funcset_names, int count) { /* write a FNSD block */ write_dep_block("FNSD", funcset_names, count); } /* ------------------------------------------------------------------------ */ /* * begin a metaclass dependency block */ void CVmImageWriter::begin_meta_dep(int count) { /* begin the dependency block */ begin_dep_block("MCLD", count); /* we're not in a property list yet */ mcld_propcnt_pos_ = 0; } /* * Write a metaclass dependency block */ void CVmImageWriter::write_meta_dep(const char **meta_names, int count) { /* write a MCLD block */ write_dep_block("MCLD", meta_names, count); } /* * write a metaclass dependency block item */ void CVmImageWriter::write_meta_dep_item(const char *metaclass_name) { /* if we didn't end the previous item's property list, end it now */ end_meta_prop_list(); /* write a placeholder next record offset */ mcld_ofs_pos_ = fp_->get_pos(); fp_->write_uint2(0); /* write the metaclass name */ write_dep_block_item(metaclass_name); /* write a placeholder property vector count */ mcld_propcnt_pos_ = fp_->get_pos(); fp_->write_uint2(0); /* write the property record size (2 bytes) */ fp_->write_uint2(2); /* no properties yet */ mcld_prop_cnt_ = 0; } /* * write a metaclass dependency property list item */ void CVmImageWriter::write_meta_item_prop(uint prop_id) { /* write the property ID */ fp_->write_uint2(prop_id); /* count it */ ++mcld_prop_cnt_; } /* * end a metaclass prop list */ void CVmImageWriter::end_meta_prop_list() { /* if we have a count pending, go write it */ if (mcld_propcnt_pos_ != 0) { long pos; /* remember the current position */ pos = fp_->get_pos(); /* go back and write the property count */ fp_->set_pos(mcld_propcnt_pos_); fp_->write_uint2(mcld_prop_cnt_); /* we no longer have a property count fixup to apply */ mcld_propcnt_pos_ = 0; /* go back and write the next-record offset */ fp_->set_pos(mcld_ofs_pos_); fp_->write_int2((int)(pos - mcld_ofs_pos_)); /* go back to the end of the record */ fp_->set_pos(pos); } } /* * end a metaclass dependency block */ void CVmImageWriter::end_meta_dep() { /* end the last metaclass item */ end_meta_prop_list(); /* end the dependency block */ end_dep_block(); } /* ------------------------------------------------------------------------ */ /* * Begin a dependency block */ void CVmImageWriter::begin_dep_block(const char *block_id, int count) { char buf[4]; /* open the block */ begin_block(block_id, TRUE); /* write the number of entries */ oswp2(buf, count); fp_->write_bytes(buf, 2); } /* * Write a dependency block item */ void CVmImageWriter::write_dep_block_item(const char *nm) { size_t len; char buf[4]; /* get the length of this name, and truncate to 255 bytes */ len = strlen(nm); if (len > 255) len = 255; /* write the length, followed by the name */ buf[0] = (char)(uchar)len; fp_->write_bytes(buf, 1); fp_->write_bytes(nm, len); } /* * End a dependency block */ void CVmImageWriter::end_dep_block() { /* end the block */ end_block(); } /* ------------------------------------------------------------------------ */ /* * Write a generic dependency list block */ void CVmImageWriter::write_dep_block(const char *block_id, const char **names, int count) { /* open the block */ begin_dep_block(block_id, count); /* write each entry */ for ( ; count > 0 ; ++names, --count) write_dep_block_item(*names); /* end the block */ end_dep_block(); } /* ------------------------------------------------------------------------ */ /* * Write a constant pool definition block */ void CVmImageWriter::write_pool_def(uint pool_id, uint32_t page_count, uint32_t page_size, int mandatory) { char buf[16]; /* prepare the block's data */ oswp2(buf, pool_id); oswp4(buf+2, page_count); oswp4(buf+6, page_size); /* open the block, write the data, and end the block */ begin_block("CPDF", mandatory); fp_->write_bytes(buf, 10); end_block(); } /* * Fix up a pool definition block with the actual page count */ void CVmImageWriter::fix_pool_def(long def_seek_ofs, uint32_t page_count) { long old_pos; char buf[4]; /* note the file position at entry */ old_pos = fp_->get_pos(); /* * seek to the original definition block location, plus the size of * the block header (10 bytes), plus the offset within the block of * the pool page count (it starts 2 bytes into the block data) */ fp_->set_pos(def_seek_ofs + 10 + 2); /* write the page count */ oswp4(buf, page_count); fp_->write_bytes(buf, 4); /* seek back to our location at entry */ fp_->set_pos(old_pos); } /* ------------------------------------------------------------------------ */ /* * Write a constant pool page */ void CVmImageWriter::write_pool_page(uint pool_id, uint32_t page_index, const char *page_data, uint32_t page_data_size, int mandatory, uchar xor_mask) { char buf[16]; /* begin the block */ begin_block("CPPG", mandatory); /* prepare the prefix */ oswp2(buf, pool_id); oswp4(buf+2, page_index); buf[6] = xor_mask; /* write the prefix */ fp_->write_bytes(buf, 7); /* write the page data, XOR'ing the data with the mask byte */ xor_and_write_bytes(page_data, page_data_size, xor_mask); /* end the block */ end_block(); } /* ------------------------------------------------------------------------ */ /* * Begin writing a constant pool page. This constructs the header and * prepares for writing the bytes making up the page. */ void CVmImageWriter::begin_pool_page(uint pool_id, uint32_t page_index, int mandatory, uchar xor_mask) { char buf[16]; /* begin the block */ begin_block("CPPG", mandatory); /* prepare the prefix */ oswp2(buf, pool_id); oswp4(buf+2, page_index); buf[6] = xor_mask; /* write the prefix */ fp_->write_bytes(buf, 7); } /* * write bytes to a pool page under construction */ void CVmImageWriter::write_pool_page_bytes(const char *buf, uint32_t siz, uchar xor_mask) { /* write the page data, XOR'ing the data with the mask byte */ xor_and_write_bytes(buf, siz, xor_mask); } /* * XOR and write a block of data - we will XOR each byte of the data * with the given mask byte before writing it to the file */ void CVmImageWriter::xor_and_write_bytes(const char *mem, uint32_t siz, uchar xor_mask) { /* * if there's no mask, simply write the data directly - anything XOR * zero equals the original value */ if (xor_mask == 0) { /* write the data to the page */ fp_->write_bytes(mem, siz); } else { /* * copy the data in chunks into our buffer, XOR it with the * mask, and write the results */ while (siz != 0) { char buf[1024]; size_t cur; size_t rem; char *dst; /* * limit this chunk to the buffer size or the remainder of * the input, whichever is smaller */ cur = sizeof(buf); if (cur > siz) cur = (size_t)siz; /* copy this chunk, xor'ing each byte with the mask */ for (dst = buf, rem = cur ; rem != 0 ; --rem, ++dst, ++mem) *dst = *mem ^ xor_mask; /* write out this chunk */ fp_->write_bytes(buf, cur); /* subtract this chunk from the length remaining */ siz -= cur; } } } /* * finish writing a pool page */ void CVmImageWriter::end_pool_page() { /* end the block */ end_block(); } /* ------------------------------------------------------------------------ */ /* * Begin a symbolic names block */ void CVmImageWriter::begin_sym_block() { char buf[4]; /* begin the block */ begin_block("SYMD", FALSE); /* remember where our placeholder goes */ symd_prefix_ = fp_->get_pos(); /* prepare the placeholder prefix, using a zero count for now */ oswp2(buf, 0); /* write the prefix */ fp_->write_bytes(buf, 2); /* we haven't written any symbolic name items yet */ symd_cnt_ = 0; } /* * write a symbolic name for an object ID */ void CVmImageWriter::write_sym_item_objid(const char *nm, size_t len, ulong obj_id) { vm_val_t val; /* set up the object ID value */ val.set_obj((vm_obj_id_t)obj_id); /* write it out */ write_sym_item(nm, len, &val); } /* * write a symbolic name for a property ID */ void CVmImageWriter::write_sym_item_propid(const char *nm, size_t len, uint prop_id) { vm_val_t val; /* set up the property ID value */ val.set_propid((vm_prop_id_t)prop_id); /* write it out */ write_sym_item(nm, len, &val); } /* * write a symbolic name for a function */ void CVmImageWriter::write_sym_item_func(const char *nm, size_t len, ulong code_ofs) { vm_val_t val; /* set up the property ID value */ val.set_fnptr((pool_ofs_t)code_ofs); /* write it out */ write_sym_item(nm, len, &val); } /* * write a symbolic name item */ void CVmImageWriter::write_sym_item(const char *nm, size_t len, const vm_val_t *val) { char buf[VMB_DATAHOLDER + 1]; /* prepare the data holder in the prefix */ vmb_put_dh(buf, val); /* limit the length to 255 bytes */ if (len > 255) len = 255; /* add the length to the prefix */ buf[VMB_DATAHOLDER] = (char)len; /* write the prefix */ fp_->write_bytes(buf, VMB_DATAHOLDER + 1); /* write the string */ fp_->write_bytes(nm, len); /* count it */ ++symd_cnt_; } /* * end a symbolic names block */ void CVmImageWriter::end_sym_block() { long old_pos; char buf[4]; /* end the block */ end_block(); /* * Go back and fix the header with the number of items we wrote. * First, remember our current position, then seek back to the count * prefix. */ old_pos = fp_->get_pos(); fp_->set_pos(symd_prefix_); /* prepare the prefix, and write it out */ oswp2(buf, symd_cnt_); fp_->write_bytes(buf, 2); /* restore the file position */ fp_->set_pos(old_pos); } /* ------------------------------------------------------------------------ */ /* * Begin an object static data block */ void CVmImageWriter::begin_objs_block(uint metaclass_idx, int large_objects, int trans) { char buf[16]; uint flags; /* begin the block */ begin_block("OBJS", TRUE); /* prepare the flags */ flags = 0; if (large_objects) flags |= 1; if (trans) flags |= 2; /* remember where the prefix goes so we can fix it up later */ objs_prefix_ = fp_->get_pos(); /* * write a placeholder object count, the metaclass dependency table * index, and the OBJS flags */ oswp2(buf, 0); oswp2(buf + 2, metaclass_idx); oswp2(buf + 4, flags); /* write the prefix */ fp_->write_bytes(buf, 6); } /* * Write bytes to an OBJS (object static data) block */ void CVmImageWriter::write_objs_bytes(const char *buf, uint32_t siz) { /* write the buffer */ fp_->write_bytes(buf, siz); } /* * end an object static data block */ void CVmImageWriter::end_objs_block(uint object_count) { long pos; /* remember the current file write position for a moment */ pos = fp_->get_pos(); /* go back and fix up the object count in the header */ fp_->set_pos(objs_prefix_); fp_->write_uint2(object_count); /* seek back to the original position */ fp_->set_pos(pos); /* end the block */ end_block(); } /* ------------------------------------------------------------------------ */ /* * SRCF blocks - source file descriptors */ /* * begin a SRCF block */ void CVmImageWriter::begin_srcf_block(int count) { /* * begin the block - SRCF blocks are always optional, since they're * purely for debugging purposes */ begin_block("SRCF", FALSE); /* write the number of entries */ fp_->write_uint2(count); /* each source line record is 8 bytes in the current format */ fp_->write_uint2(8); } /* * begin a SRCF file entry */ void CVmImageWriter::begin_srcf_entry(int orig_index, const char *fname) { size_t len; /* remember where this entry starts, so we can fix up the size later */ srcf_entry_pos_ = fp_->get_pos(); /* write a placeholder size entry */ fp_->write_uint4(0); /* write the original index */ fp_->write_uint2(orig_index); /* write the length of the name */ len = get_strlen(fname); fp_->write_uint2(len); /* write the filename */ fp_->write_bytes(fname, len); /* we have no line record yet, so write a placeholder count */ srcf_line_pos_ = fp_->get_pos(); fp_->write_uint4(0); srcf_line_cnt_ = 0; } /* * write a SRCF line record entry */ void CVmImageWriter::write_srcf_line_entry(ulong linenum, ulong addr) { /* write the line number and address */ fp_->write_uint4(linenum); fp_->write_uint4(addr); /* count it */ ++srcf_line_cnt_; } /* * end a SRCF file entry */ void CVmImageWriter::end_srcf_entry() { ulong pos; /* go back and fix up the line record count */ pos = fp_->get_pos(); fp_->set_pos(srcf_line_pos_); fp_->write_uint4(srcf_line_cnt_); /* go back and fix up the total entry size record */ fp_->set_pos(srcf_entry_pos_); fp_->write_uint4(pos - srcf_entry_pos_); /* seek back to the end of the block */ fp_->set_pos(pos); } /* * end a SRCF block */ void CVmImageWriter::end_srcf_block() { /* end the block using the generic mechanism */ end_block(); } /* ------------------------------------------------------------------------ */ /* * MACR blocks - global preprocess macro symbol table */ /* * begin a MACR block */ void CVmImageWriter::begin_macr_block() { /* * write the header - it's an optional block since it's for the * debugger's use only */ begin_block("MACR", FALSE); } /* * end a MACR block */ void CVmImageWriter::end_macr_block() { /* end the block using the generic mechanism */ end_block(); } /* ------------------------------------------------------------------------ */ /* * GSYM blocks - global symbol table */ /* * begin a GSYM block */ void CVmImageWriter::begin_gsym_block() { /* * begin the block - GSYM blocks are always optional, since they're * purely for debugging purposes */ begin_block("GSYM", FALSE); /* remember where the prefix goes so we can fix it up later */ gsym_prefix_ = fp_->get_pos(); /* write a placehodler object count and the metaclass index */ fp_->write_uint4(0); } /* * write a GSYM entry */ void CVmImageWriter::write_gsym_entry(const char *sym, size_t sym_len, int type_id, const char *dat, size_t dat_len) { /* * write the length of the symbol, length of the extra data, and the * symbol type */ fp_->write_uint2(sym_len); fp_->write_uint2(dat_len); fp_->write_uint2(type_id); /* write the symbol name */ fp_->write_bytes(sym, sym_len); /* write the extra data */ fp_->write_bytes(dat, dat_len); } /* * end a GSYM block */ void CVmImageWriter::end_gsym_block(ulong cnt) { long pos; /* remember the current file write position for a moment */ pos = fp_->get_pos(); /* go back and fix up the count in the header */ fp_->set_pos(gsym_prefix_); fp_->write_uint4(cnt); /* seek back to the original position */ fp_->set_pos(pos); /* end the block using the generic mechanism */ end_block(); } /* ------------------------------------------------------------------------ */ /* * MHLS blocks - method header list */ /* * begin an MHLS block */ void CVmImageWriter::begin_mhls_block() { /* * begin the block - MHLS blocks are always optional, since they're * purely for debugging purposes */ begin_block("MHLS", FALSE); /* remember where the count goes so we can fix it up later */ mhls_cnt_pos_ = fp_->get_pos(); /* write a placehodler count */ fp_->write_uint4(0); /* there are no entries yet */ mhls_cnt_ = 0; } /* * write an MHLS entry */ void CVmImageWriter::write_mhls_entry(ulong addr) { /* write the address */ fp_->write_uint4(addr); /* count the entry */ ++mhls_cnt_; } /* * end an MHLS block */ void CVmImageWriter::end_mhls_block() { long pos; /* remember the current file write position for a moment */ pos = fp_->get_pos(); /* go back and fix up the count in the header */ fp_->set_pos(mhls_cnt_pos_); fp_->write_uint4(mhls_cnt_); /* seek back to the original position */ fp_->set_pos(pos); /* end the block using the generic mechanism */ end_block(); } /* ------------------------------------------------------------------------ */ /* * SINI block - static initializer list */ /* * begin an SINI block */ void CVmImageWriter::begin_sini_block(ulong static_cs_ofs, ulong init_cnt) { /* * begin the block - SINI blocks are mandatory, since the program * depends upon static initializers being evaluated immediately * after compilation */ begin_block("SINI", TRUE); /* * write the size of our header (including the size prefix); this * serves as a simple versioning flag so we can tell if fields added * at a later date are part of a given image file's data or not (if * the header is too small to contain them, they're not present) */ fp_->write_uint4(12); /* write the starting static code segment offset */ fp_->write_uint4(static_cs_ofs); /* write the initializer count */ fp_->write_uint4(init_cnt); } /* * end an SINI block */ void CVmImageWriter::end_sini_block() { /* end the block using the generic mechanism */ end_block(); } frobtads-1.2.3/tads3/vmsa.cpp0000644000175000001440000001336111724416446015211 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header$"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmsa.cpp - stand-alone interpreter entrypoints Function Defines certain entrypoints that are not needed in a normal stand-alone version of the interpreter. The entrypoints defined here are dummy implementations that should never be called. Certain functions are needed only in special VM versions that support, for example, rebuilding of an image file after running 'preinit'. Notes Modified 07/21/99 MJRoberts - Creation */ #include #include "t3std.h" #include "vmobj.h" #include "vmglob.h" #include "vmdict.h" #include "vmstr.h" #include "vmlst.h" #include "vmbignum.h" #include "vmtobj.h" #include "vmgram.h" #include "vmmeta.h" #include "vmintcls.h" #include "vmiter.h" #include "vmvec.h" #include "vmlookup.h" #include "vmbytarr.h" #include "vmcset.h" #include "vmfilobj.h" #include "vmtmpfil.h" #include "vmfilnam.h" #include "vmpat.h" #include "vmstrcmp.h" #include "vmstrbuf.h" #include "vmdynfunc.h" #include "vmfref.h" #include "vmdate.h" #include "vmtzobj.h" #ifdef TADSNET #include "vmhttpsrv.h" #include "vmhttpreq.h" #endif ulong CVmObjTads::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjString::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjList::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjDict::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } void CVmMetaTable::rebuild_image(class CVmImageWriter *) { } ulong CVmObjBigNum::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjClass::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjIterIdx::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjVector::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjLookupTable::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjStringBuffer::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjIterLookupTable::convert_to_const_data( VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjByteArray::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjCharSet::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFile::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjTemporaryFile::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjFileName::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjPattern::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *, vm_obj_id_t) { assert(FALSE); } ulong CVmObjStrComp::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmDynamicFunc::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFrameDesc::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjFrameRef::rebuild_image(VMG_ char *, ulong) { assert(FALSE); return 0; } ulong CVmObjDate::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjTimeZone::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } /* ------------------------------------------------------------------------ */ /* * networking support, if present */ #ifdef TADSNET ulong CVmObjHTTPServer::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } ulong CVmObjHTTPRequest::rebuild_image(VMG_ char *buf, ulong buflen) { assert(FALSE); return 0; } #endif frobtads-1.2.3/tads3/tcprs.cpp0000644000175000001440000101551612024607261015372 0ustar realncusers#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/tads3/TCPRS.CPP,v 1.5 1999/07/11 00:46:53 MJRoberts Exp $"; #endif /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcprs.cpp - TADS 3 Compiler Parser Function This parser module contains code required for any parser usage, both for a full compiler and for a debugger. Notes Modified 04/30/99 MJRoberts - Creation */ #include #include #include #include #include #include "os.h" #include "t3std.h" #include "tcprs.h" #include "tctarg.h" #include "tcgen.h" #include "vmhash.h" #include "tcmain.h" #include "vmfile.h" #include "tctok.h" #include "vmbignum.h" /* ------------------------------------------------------------------------ */ /* * Expression Operator parser objects. These objects are linked * together in a network that defines the order of precedence for * expression operators. * * These objects use static storage. This is acceptable, even though * these objects aren't all "const" qualified, because the compiler uses * a single global parser object; since there's only the one parser, * there only needs to be a single network of these objects. */ /* unary operator parser */ static CTcPrsOpUnary S_op_unary; /* factor group */ static const CTcPrsOpMul S_op_mul; static const CTcPrsOpDiv S_op_div; static const CTcPrsOpMod S_op_mod; static const CTcPrsOpBin *const S_grp_factor[] = { &S_op_mul, &S_op_div, &S_op_mod, 0 }; static const CTcPrsOpBinGroup S_op_factor(&S_op_unary, &S_op_unary, S_grp_factor); /* term group */ static const CTcPrsOpAdd S_op_add; static const CTcPrsOpSub S_op_sub; static const CTcPrsOpBin *const S_grp_term[] = { &S_op_add, &S_op_sub, 0 }; static const CTcPrsOpBinGroup S_op_term(&S_op_factor, &S_op_factor, S_grp_term); /* shift group */ static const CTcPrsOpShl S_op_shl; static const CTcPrsOpAShr S_op_ashr; static const CTcPrsOpLShr S_op_lshr; static const CTcPrsOpBin *const S_grp_shift[] = { &S_op_shl, &S_op_ashr, &S_op_lshr, 0 }; static const CTcPrsOpBinGroup S_op_shift(&S_op_term, &S_op_term, S_grp_shift); /* magnitude comparisons group */ static const CTcPrsOpGt S_op_gt; static const CTcPrsOpGe S_op_ge; static const CTcPrsOpLt S_op_lt; static const CTcPrsOpLe S_op_le; static const CTcPrsOpBin *const S_grp_relcomp[] = { &S_op_gt, &S_op_ge, &S_op_lt, &S_op_le, 0 }; static const CTcPrsOpBinGroup S_op_relcomp(&S_op_shift, &S_op_shift, S_grp_relcomp); /* * Equality/inequality comparison group. Note that the equality operator * is non-const because we want the option to change the operator on the * fly based on syntax mode - '==' in C-mode, '=' in TADS traditional mode. * (This was a feature of tads 2, but we have since deprecated it in tads * 3, so this ability is actually just vestigial at this point. No harm in * keeping around the code internally for it, though, since it's pretty * simple.) * * Note also that this is a special binary group - this one recognizes the * non-keyword operators 'is in' and 'not in'. */ static CTcPrsOpEq S_op_eq; static const CTcPrsOpNe S_op_ne; static const CTcPrsOpBin *const S_grp_eqcomp[] = { &S_op_eq, &S_op_ne, 0 }; static const CTcPrsOpBinGroupCompare S_op_eqcomp(&S_op_relcomp, &S_op_relcomp, S_grp_eqcomp); /* bitwise AND operator */ static const CTcPrsOpBAnd S_op_band(&S_op_eqcomp, &S_op_eqcomp); /* bitwise XOR operator */ static const CTcPrsOpBXor S_op_bxor(&S_op_band, &S_op_band); /* bitwise OR operator */ static const CTcPrsOpBOr S_op_bor(&S_op_bxor, &S_op_bxor); /* logical AND operator */ static const CTcPrsOpAnd S_op_and(&S_op_bor, &S_op_bor); /* logical OR operator */ static const CTcPrsOpOr S_op_or(&S_op_and, &S_op_and); /* if-nil operator ?? */ static const CTcPrsOpIfnil S_op_ifnil; /* conditional operator */ static const CTcPrsOpIf S_op_if; /* * assignment operator - note that this is non-const, because we must be * able to change the operator - '=' in C-mode, and ':=' in TADS * traditional mode */ static CTcPrsOpAsi S_op_asi; /* comma operator */ static const CTcPrsOpComma S_op_comma(&S_op_asi, &S_op_asi); /* ------------------------------------------------------------------------ */ /* * Embedded expression token list. We capture the tokens of an embedded * expression in a private list for a first scan, to compare against the * list of special embedding templates. If we determine that the embedding * is a simple expression, or that a portion of it is an expression, we'll * push the captured tokens back into the token stream via 'unget'. */ class CTcEmbedTokenList { public: CTcEmbedTokenList() { /* create the initial list entry */ wrt = head = new CTcTokenEle(); cnt = 0; } ~CTcEmbedTokenList() { /* delete the token list */ while (head != 0) { CTcTokenEle *nxt = head->getnxt(); delete head; head = nxt; } } /* reset the list */ void reset() { /* set the read and write pointers to the allocated list head */ wrt = head; cnt = 0; } /* get the number of tokens remaining */ int getcnt() const { return cnt; } /* get the head elemenet */ const CTcToken *get_head() const { return head; } /* remove the head element */ void unlink_head(CTcToken *tok) { if (head != 0) { /* copy the token to the caller's buffer */ G_tok->copytok(tok, head); /* unlink it */ CTcTokenEle *h = head; head = head->getnxt(); --cnt; /* delete it */ delete h; } else { /* no token available - return EOF */ tok->settyp(TOKT_EOF); } } /* add a token to the list */ void add_tok(const CTcToken *tok) { /* fill in the write token */ G_tok->copytok(wrt, tok); /* if this is the last item in the list, add another */ if (wrt->getnxt() == 0) { CTcTokenEle *ele = new CTcTokenEle(); ele->setprv(wrt); wrt->setnxt(ele); } /* advance the write pointer */ wrt = wrt->getnxt(); /* count it */ ++cnt; } /* * Unget the captured tokens, skipping the first n tokens and the last * m tokens. */ void unget(int n = 0, int m = 0) { /* skip the last 'm' tokens */ CTcTokenEle *ele; int i; for (ele = wrt->getprv(), i = cnt ; m != 0 && ele != 0 ; ele = ele->getprv(), --m, --i) ; /* unget tokens until we're down to the first 'n' */ for ( ; ele != 0 && i > n ; ele = ele->getprv(), --i) G_tok->unget(ele); } /* match a string of space-delimited tokens */ int match(const char *txt) { /* count the tokens */ int argn = 0; const char *p; for (p = txt ; *p != '\0' ; ++argn) { /* skip leading spaces */ for ( ; is_space(*p) ; ++p) ; /* scan to the next space */ for ( ; *p != '\0' && !is_space(*p) ; ++p) ; } /* if we don't have enough tokens to match the input, we can't match */ if (cnt < argn) return FALSE; /* check the arguments against the list */ CTcTokenEle *ele; int tokn; for (tokn = cnt, ele = head, p = txt ; tokn != 0 && argn != 0 ; ele = ele->getnxt(), --tokn, --argn) { /* get this argument */ for ( ; is_space(*p) ; ++p) ; const char *arg = p; /* find the end of the argument */ for ( ; *p != '\0' && !is_space(*p) ; ++p) ; size_t len = p - arg; /* check for special arguments */ if (arg[0] == '*') { /* * '*' - this matches one or more tokens in the token list * up to the last remaining arguments after the '*'. Skip * arguments until the token list and remaining argument * list are the same length. */ for ( ; tokn > argn ; ele = ele->getnxt(), --tokn) ; } else { /* it's a literal - check for a match */ if (!ele->text_matches(arg, len)) return FALSE; } } /* if the lists ran out at the same time, it's a match */ return (tokn == 0 && argn == 0); } /* match a string template definition */ int match(CTcStrTemplate *tpl) { /* if we don't have enough tokens to match, don't bother looking */ if (cnt < tpl->cnt) return FALSE; /* check the arguments against the list */ CTcTokenEle *ele, *tele; int tokn, tpln; for (tokn = cnt, tpln = tpl->cnt, ele = head, tele = tpl->head ; tokn != 0 && tpln != 0 ; ele = ele->getnxt(), tele = tele->getnxt(), --tokn, --tpln) { /* check for special arguments */ if (tele->text_matches("*", 1)) { /* * '*' - this matches one or more tokens in the token list * up to the last remaining arguments after the '*'. Skip * arguments until the token list and remaining argument * list are the same length. */ for ( ; tokn > tpln ; ele = ele->getnxt(), --tokn) ; } else { /* it's a literal - check for a match */ if (!ele->text_matches(tele->get_text(), tele->get_text_len())) return FALSE; } } /* if the lists ran out at the same time, it's a match */ return (tokn == 0 && tpln == 0); } /* * Reduce our token list to include only the tokens matching the '*' in * a template. This presumes that the template actually does match. */ void reduce(CTcStrTemplate *tpl) { /* find the first token in our list matching '*' in the template */ CTcTokenEle *src, *tele; int rem, trem; for (src = head, tele = tpl->head, rem = cnt, trem = tpl->cnt ; src != 0 && tele != 0 ; src = src->getnxt(), tele = tele->getnxt(), --rem, --trem) { /* stop when we reach '*' in the template */ if (tele->text_matches("*", 1)) break; } /* skip the '*' in the template */ trem -= 1; /* * The number of tokens matching '*' is the number left in our * list, minus the number left in the token list after the '*'. */ rem -= trem; cnt = rem; /* if we had any leading fixed tokens to remove, remove them */ if (src != head) { CTcTokenEle *dst; for (dst = head ; rem != 0 && src != 0 ; src = src->getnxt(), dst = dst->getnxt(), --rem) { /* copy this token */ G_tok->copytok(dst, src); } } /* move the write pointer to the proper position */ for (wrt = head, rem = cnt ; rem != 0 ; wrt = wrt->getnxt(), --rem) ; } protected: /* head of the allocated list */ CTcTokenEle *head; /* current write pointer */ CTcTokenEle *wrt; /* number of tokens currently in the list */ int cnt; }; /* ------------------------------------------------------------------------ */ /* * Main Parser */ /* * initialize the parser */ CTcParser::CTcParser() { size_t i; /* we don't have any module information yet */ module_name_ = 0; module_seqno_ = 0; /* start out in normal mode */ pp_expr_mode_ = FALSE; src_group_mode_ = FALSE; /* create the global symbol table */ global_symtab_ = new CTcPrsSymtab(0); /* no enclosing statement yet */ enclosing_stm_ = 0; /* no source location yet */ cur_desc_ = 0; cur_linenum_ = 0; /* no dictionaries yet */ dict_cur_ = 0; dict_head_ = dict_tail_ = 0; /* no object file dictionary list yet */ obj_dict_list_ = 0; obj_file_dict_idx_ = 0; /* no dictionary properties yet */ dict_prop_head_ = 0; /* no grammar productions yet */ gramprod_head_ = gramprod_tail_ = 0; /* no object file symbol list yet */ obj_sym_list_ = 0; obj_file_sym_idx_ = 0; /* no object templates yet */ template_head_ = template_tail_ = 0; /* allocate some initial template parsing space */ template_expr_max_ = 16; template_expr_ = (CTcObjTemplateInst *)G_prsmem-> alloc(sizeof(template_expr_[0]) * template_expr_max_); /* no string templates yet */ str_template_head_ = str_template_tail_ = 0; /* no locals yet */ local_cnt_ = max_local_cnt_ = 0; /* no local or goto symbol table yet */ local_symtab_ = 0; enclosing_local_symtab_ = 0; goto_symtab_ = 0; /* no debugger local symbol table yet */ debug_symtab_ = 0; /* not in a preprocessor constant expression */ pp_expr_mode_ = FALSE; /* assume we're doing full compilation */ syntax_only_ = FALSE; /* no nested top-level statements yet */ nested_stm_head_ = 0; nested_stm_tail_ = 0; /* no anonymous objects yet */ anon_obj_head_ = 0; anon_obj_tail_ = 0; /* no non-symbol objects yet */ nonsym_obj_head_ = 0; nonsym_obj_tail_ = 0; /* allocate an initial context variable property array */ ctx_var_props_size_ = 50; ctx_var_props_ = (tctarg_prop_id_t *) t3malloc(ctx_var_props_size_ * sizeof(tctarg_prop_id_t)); /* no context variable properties assigned yet */ ctx_var_props_cnt_ = 0; ctx_var_props_used_ = 0; /* * no context variable indices assigned yet - start at one higher * than the index at which we always store 'self' */ next_ctx_arr_idx_ = TCPRS_LOCAL_CTX_METHODCTX + 1; /* 'self' isn't valid yet */ self_valid_ = FALSE; /* start at enum ID 1 (let 0 serve as an invalid value) */ next_enum_id_ = 1; /* the '+' property is not yet defined */ plus_prop_ = 0; /* no exported symbols yet */ exp_head_ = exp_tail_ = 0; /* allocate an initial '+' stack */ plus_stack_alloc_ = 32; plus_stack_ = (CTPNStmObject **) t3malloc(plus_stack_alloc_ * sizeof(*plus_stack_)); /* clear out the stack */ for (i = 0 ; i < plus_stack_alloc_ ; ++i) plus_stack_[i] = 0; /* there's no current code body (function/method body) yet */ cur_code_body_ = 0; /* nothing in the local context has been referenced yet */ self_referenced_ = FALSE; local_ctx_needs_self_ = FALSE; full_method_ctx_referenced_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; /* create the embedded expression list object */ embed_toks_ = new CTcEmbedTokenList(); } /* * Add a special built-in property symbol */ CTcSymProp *CTcParser::def_special_prop(int def, const char *name, tc_prop_id *idp) { /* we haven't created or found the property yet */ CTcSymProp *propsym = 0; /* define or look up the property, as required */ if (def) { /* allocate the ID */ tctarg_prop_id_t id = G_cg->new_prop_id(); /* create the symbol */ propsym = new CTcSymProp(name, strlen(name), FALSE, id); /* mark it as referenced, since the compiler itself uses it */ propsym->mark_referenced(); /* add it to the global symbol table */ global_symtab_->add_entry(propsym); } else { /* find the entry */ CTcSymbol *sym = global_symtab_->find(name, strlen(name)); /* check to see if we found a property symbol */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { /* got it - use the definition we found */ propsym = (CTcSymProp *)sym; } else { /* not found - create a dummy symbol for it */ propsym = new CTcSymProp(name, strlen(name), FALSE, TCTARG_INVALID_PROP); } } /* hand the property ID back to the caller if they want it */ if (idp != 0) *idp = (propsym != 0 ? propsym->get_prop() : TCTARG_INVALID_PROP); /* return the symbol */ return propsym; } /* * Initialize. This must be called after the code generator is set up. */ void CTcParser::init() { /* define the special properties */ cache_special_props(TRUE); } /* * Define or look up the special properties. If 'def' is true, we'll * create definitions; otherwise we'll look up the definitions in the * existing symbol table. The former case is for normal initialization of * a new compiler; the latter is for use in dynamic compilation, where the * global symbol table is provided by the running program. */ void CTcParser::cache_special_props(int def) { tc_prop_id propid; /* add a "construct" property for constructors */ constructor_sym_ = def_special_prop(def, "construct", &constructor_prop_); /* add a "finalize" property for finalizers */ def_special_prop(def, "finalize", &finalize_prop_); /* add some properties for grammar production match objects */ graminfo_prop_ = def_special_prop(def, "grammarInfo"); gramtag_prop_ = def_special_prop(def, "grammarTag"); /* add a "miscVocab" property for miscellaneous vocabulary words */ def_special_prop(def, "miscVocab", &propid); miscvocab_prop_ = (tc_prop_id)propid; /* add a "lexicalParent" property for a nested object's parent */ lexical_parent_sym_ = def_special_prop(def, "lexicalParent"); /* add a "sourceTextOrder" property */ src_order_sym_ = def_special_prop(def, "sourceTextOrder"); /* start the sourceTextOrder index at 1 */ src_order_idx_ = 1; /* add a "sourceTextGroup" property */ src_group_sym_ = def_special_prop(def, "sourceTextGroup"); /* we haven't created the sourceTextGroup referral object yet */ src_group_id_ = TCTARG_INVALID_OBJ; /* add a "sourceTextGroupName" property */ src_group_mod_sym_ = def_special_prop(def, "sourceTextGroupName"); /* add a "sourceTextGroupOrder" property */ src_group_seq_sym_ = def_special_prop(def, "sourceTextGroupOrder"); /* define the operator overload properties */ ov_op_add_ = def_special_prop(def, "operator +"); ov_op_sub_ = def_special_prop(def, "operator -"); ov_op_mul_ = def_special_prop(def, "operator *"); ov_op_div_ = def_special_prop(def, "operator /"); ov_op_mod_ = def_special_prop(def, "operator %"); ov_op_xor_ = def_special_prop(def, "operator ^"); ov_op_shl_ = def_special_prop(def, "operator <<"); ov_op_ashr_ = def_special_prop(def, "operator >>"); ov_op_lshr_ = def_special_prop(def, "operator >>>"); ov_op_bnot_ = def_special_prop(def, "operator ~"); ov_op_bor_ = def_special_prop(def, "operator |"); ov_op_band_ = def_special_prop(def, "operator &"); ov_op_neg_ = def_special_prop(def, "operator negate"); ov_op_idx_ = def_special_prop(def, "operator []"); ov_op_setidx_ = def_special_prop(def, "operator []="); } /* get the grammarTag property ID */ tc_prop_id CTcParser::get_grammarTag_prop() const { return gramtag_prop_ != 0 ? gramtag_prop_->get_prop() : TCTARG_INVALID_PROP; } /* get the grammarInfo property ID */ tc_prop_id CTcParser::get_grammarInfo_prop() const { return graminfo_prop_ != 0 ? graminfo_prop_->get_prop() : TCTARG_INVALID_PROP; } /* * destroy the parser */ CTcParser::~CTcParser() { /* * Note that we don't have to delete certain objects, because we * allocated them out of the parser memory pool and will be * automatically deleted when the pool is deleted. For example, we * don't have to delete any symbol tables, including the global * symbol table. */ /* delete the module name, if it's known */ lib_free_str(module_name_); /* delete the object file symbol fixup list, if present */ if (obj_sym_list_ != 0) t3free(obj_sym_list_); /* delete the object file dictionary fixup list, if present */ if (obj_dict_list_ != 0) t3free(obj_dict_list_); /* delete the context variable property list */ if (ctx_var_props_ != 0) t3free(ctx_var_props_); /* delete the export list */ while (exp_head_ != 0) { CTcPrsExport *nxt; /* remember the next entry, since we're deleting our pointer to it */ nxt = exp_head_->get_next(); /* delete this entry */ delete exp_head_; /* move on to the next */ exp_head_ = nxt; } /* delete the '+' stack */ t3free(plus_stack_); /* delete the embedding look-ahead list */ delete embed_toks_; } /* ------------------------------------------------------------------------ */ /* * Set the module information */ void CTcParser::set_module_info(const char *name, int seqno) { /* if we have a name stored already, delete the old one */ lib_free_str(module_name_); /* store the new name and sequence number */ module_name_ = lib_copy_str(name); module_seqno_ = seqno; } /* * Change the #pragma C mode. On changing this mode, we'll change the * assignment operator and equality operator tokens. If 'mode' is true, * we're in C mode; otherwise, we're in traditional TADS mode. * * #pragma C+: assignment is '=', equality is '==' *. #pragma C-: assignment is ':=', equality is '='. */ void CTcParser::set_pragma_c(int mode) { /* set the assignment operator */ S_op_asi.set_asi_op(mode ? TOKT_EQ : TOKT_ASI); /* set the equality comparison operator */ S_op_eq.set_eq_op(mode ? TOKT_EQEQ : TOKT_EQ); } /* * Parse an expression. This parses a top-level comma expression. */ CTcPrsNode *CTcParser::parse_expr() { /* parse a comma expression */ return S_op_comma.parse(); } /* * Parse a condition expression. Warns if the outermost operator is a * simple assignment. */ CTcPrsNode *CTcParser::parse_cond_expr() { CTcPrsNode *cond; /* parse the expression */ cond = parse_expr(); /* * if the outermost operator is a simple assignment, display an * error */ if (cond != 0 && cond->is_simple_asi() && !G_prs->get_syntax_only()) G_tok->log_warning(TCERR_ASI_IN_COND); /* return the result */ return cond; } /* * Parse an assignment expression. */ CTcPrsNode *CTcParser::parse_asi_expr() { /* parse an assignment expression */ return S_op_asi.parse(); } /* * Parse an expression or a double-quoted string expression */ CTcPrsNode *CTcParser::parse_expr_or_dstr(int allow_comma_expr) { /* * parse the appropriate kind of expression - if a comma expression is * allowed, parse that, otherwise parse an assignment expression (as * that's the next thing down the hierarchy from the comma operator) */ return (allow_comma_expr ? S_op_comma.parse() : S_op_asi.parse()); } /* * Parse a required semicolon */ int CTcParser::parse_req_sem() { const char eof_str[] = ""; /* check to see if we found the semicolon */ if (G_tok->cur() == TOKT_SEM) { /* success - skip the semicolon and tell the caller to proceed */ G_tok->next(); return 0; } /* * check what we have; the type of error we want to log depends on * what we find next */ switch(G_tok->cur()) { case TOKT_RPAR: /* log the extra ')' error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* * we're probably in an expression that ended before the user * thought it should; skip the extraneous material up to the * next semicolon */ return skip_to_sem(); case TOKT_RBRACK: /* log the error */ G_tok->log_error(TCERR_EXTRA_RBRACK); /* skip up to the next semicolon */ return skip_to_sem(); case TOKT_EOF: /* * missing semicolon at end of file - log the missing-semicolon * error and tell the caller not to proceed, since there's * nothing left to parse */ G_tok->log_error(TCERR_EXPECTED_SEMI, (int)sizeof(eof_str)-1, eof_str); return 1; default: /* * the source is probably just missing a semicolon; log the * error, and tell the caller to proceed from the current * position */ G_tok->log_error_curtok(TCERR_EXPECTED_SEMI); return 0; } } /* * Skip to the next semicolon */ int CTcParser::skip_to_sem() { /* keep going until we find a semicolon or some other reason to stop */ for (;;) { /* see what we have next */ switch(G_tok->cur()) { case TOKT_EOF: /* end of file - tell the caller not to proceed */ return 1; case TOKT_SEM: /* * it's the semicolon at last - skip it and tell the caller * to proceed */ G_tok->next(); return 0; case TOKT_LBRACE: case TOKT_RBRACE: /* * Don't skip past braces - the caller probably simply left * out a semicolon at the end of a statement, and we've now * reached the next block start or end. Stop here and tell * the caller to proceed. */ return 0; default: /* skip anything else */ G_tok->next(); break; } } } /* * Parse an operator name. Call this when the current token is 'operator' * and an operator name is expected. We'll fill in 'tok' with the pseudo * property name of the operator ("operator +", etc). */ int CTcParser::parse_op_name(CTcToken *tok, int *op_argp) { const char *propname; int ok = TRUE; int op_args = 0; /* get the actual operator */ switch (G_tok->next()) { case TOKT_SYM: /* check for named operators */ if (G_tok->getcur()->text_matches("negate")) { propname = "operator negate"; op_args = 1; } else { /* unknown symbolic operator name */ G_tok->log_error_curtok(TCERR_BAD_OP_OVERLOAD); ok = FALSE; propname = "unknown_operator"; } break; case TOKT_PLUS: propname = "operator +"; op_args = 2; break; case TOKT_MINUS: propname = "operator -"; op_args = 2; break; case TOKT_TIMES: propname = "operator *"; op_args = 2; break; case TOKT_DIV: propname = "operator /"; op_args = 2; break; case TOKT_MOD: propname = "operator %"; op_args = 2; break; case TOKT_XOR: propname = "operator ^"; op_args = 2; break; case TOKT_SHL: propname = "operator <<"; op_args = 2; break; case TOKT_ASHR: propname = "operator >>"; op_args = 2; break; case TOKT_LSHR: propname = "operator >>>"; op_args = 2; break; case TOKT_BNOT: propname = "operator ~"; op_args = 1; break; case TOKT_OR: propname = "operator |"; op_args = 2; break; case TOKT_AND: propname = "operator &"; op_args = 2; break; case TOKT_LBRACK: /* we need at least a ']', and a '=' can follow */ if (G_tok->next() != TOKT_RBRACK) G_tok->log_error_curtok(TCERR_EXPECTED_RBRACK_IN_OP); /* check what follows that */ if (G_tok->next() == TOKT_EQ) { /* it's the assign-to-index operator []= */ propname = "operator []="; op_args = 3; } else { /* it's just the regular index operator [] */ propname = "operator []"; op_args = 2; /* put back our peek-ahead token */ G_tok->unget(); } break; default: /* it's not an operator we can override */ G_tok->log_error_curtok(TCERR_BAD_OP_OVERLOAD); propname = "unknown_operator"; ok = FALSE; break; } /* copy the property name to the token */ tok->set_text(propname, strlen(propname)); /* set the caller's argument counter if desired */ if (op_argp != 0) *op_argp = op_args; /* return the success/error indication */ return ok; } /* * Create a symbol node */ CTcPrsNode *CTcParser::create_sym_node(const textchar_t *sym, size_t sym_len) { /* * First, look up the symbol in local scope. Local scope symbols * can always be resolved during parsing, because the language * requires that local scope items be declared before their first * use. */ CTcPrsSymtab *symtab; CTcSymbol *entry = local_symtab_->find(sym, sym_len, &symtab); /* if we found it in local scope, return a resolved symbol node */ if (entry != 0 && symtab != global_symtab_) return new CTPNSymResolved(entry); /* if there's a debugger local scope, look it up there */ if (debug_symtab_ != 0) { /* look it up in the debug symbol table */ tcprsdbg_sym_info info; if (debug_symtab_->find_symbol(sym, sym_len, &info)) { /* found it - return a debugger local variable */ return new CTPNSymDebugLocal(&info); } } /* * We didn't find it in local scope, so the symbol cannot be resolved * until code generation - return an unresolved symbol node. Note a * possible implicit self-reference, since this could be a property of * 'self'. */ set_self_referenced(TRUE); return new CTPNSym(sym, sym_len); } /* * Add a nested top-level statement to our list */ void CTcParser::add_nested_stm(CTPNStmTop *stm) { /* link it into our list */ if (nested_stm_tail_ != 0) nested_stm_tail_->set_next_stm_top(stm); else nested_stm_head_ = stm; nested_stm_tail_ = stm; } /* * Add an anonymous object to our list */ void CTcParser::add_anon_obj(CTcSymObj *sym) { /* link it into our list */ if (anon_obj_tail_ != 0) anon_obj_tail_->nxt_ = sym; else anon_obj_head_ = sym; anon_obj_tail_ = sym; /* it's the last one */ sym->nxt_ = 0; /* mark the symbol as anonymous */ sym->set_anon(TRUE); } /* * Add a non-symbolic object to our list */ void CTcParser::add_nonsym_obj(tctarg_obj_id_t id) { tcprs_nonsym_obj *obj; /* allocate a link structure */ obj = new (G_prsmem) tcprs_nonsym_obj(id); /* link it into our list */ if (nonsym_obj_tail_ != 0) nonsym_obj_tail_->nxt_ = obj; else nonsym_obj_head_ = obj; nonsym_obj_tail_ = obj; } /* * Basic routine to read a length-prefixed symbol. Uses the given * temporary buffer, then stores the text in tokenizer memory (which * remains valid and available throughout compilation). If the length * exceeds the temporary buffer length, we'll flag the given error and * return null. The length return pointer can be null if the caller wants * the results null-terminated rather than returned with a counted length. * If the length pointer is given, the result will not be null-terminated. * */ const char *CTcParser::read_len_prefix_str (CVmFile *fp, char *tmp_buf, size_t tmp_buf_len, size_t *ret_len, int err_if_too_long) { size_t read_len; size_t alloc_len; /* read the length to read from the file */ read_len = (size_t)fp->read_uint2(); /* if we need null termination, add a byte to the allocation length */ alloc_len = read_len + (ret_len == 0 ? 1 : 0); /* if it won't fit in the temporary buffer, it's an error */ if (alloc_len > tmp_buf_len) { /* log the error and return failure */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); return 0; } /* read the bytes into the temporary buffer */ fp->read_bytes(tmp_buf, read_len); /* add null termination if required, or set the return length if not */ if (ret_len == 0) tmp_buf[read_len] = '\0'; else *ret_len = read_len; /* store the result in the tokenizer's text list and return the result */ return G_tok->store_source(tmp_buf, alloc_len); } /* * Read a length prefixed string into a given buffer. Returns zero on * success, non-zero on failure. */ int CTcParser::read_len_prefix_str(CVmFile *fp, char *buf, size_t buf_len, int err_if_too_long) { size_t read_len; size_t alloc_len; /* read the length to read from the file */ read_len = (size_t)fp->read_uint2(); /* add a byte for null termination */ alloc_len = read_len + 1; /* if it won't fit in the temporary buffer, it's an error */ if (alloc_len > buf_len) { /* log the error and return failure */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, err_if_too_long); return 1; } /* read the bytes into the caller's buffer */ fp->read_bytes(buf, read_len); /* add null termination */ buf[read_len] = '\0'; /* success */ return 0; } /* ------------------------------------------------------------------------ */ /* * Add a generated object */ CTcSymObj *CTcParser::add_gen_obj(const char *clsname) { /* look up the class symbol */ CTcSymObj *cls = (CTcSymObj *)global_symtab_->find( clsname, strlen(clsname)); /* make sure we found an object symbol */ if (cls != 0 && cls->get_type() == TC_SYM_OBJ) { /* got it - go create the object */ return add_gen_obj(cls); } else { /* not an object - return failure */ return 0; } } /* * Add a generated object. */ CTcSymObj *CTcParser::add_gen_obj(CTcSymObj *cls) { /* check for dynamic compilation */ if (G_vmifc != 0) { /* create a live object in the VM, and wrap it in an anon symbol */ tctarg_obj_id_t clsid = (cls != 0 ? cls->get_obj_id() : TCTARG_INVALID_OBJ); return new CTcSymObj(".anon", 5, FALSE, G_vmifc->new_obj(clsid), FALSE, TC_META_TADSOBJ, 0); } else { /* static mode */ return add_gen_obj_stat(cls); } } /* * Add a constant property value to a generated object */ void CTcParser::add_gen_obj_prop( CTcSymObj *obj, const char *propn, const CTcConstVal *val) { /* look up the property - this counts as an explicit definition */ CTcSymbol *sym = G_prs->get_global_symtab() ->find_or_def_prop_explicit(propn, strlen(propn), FALSE); /* make sure we found it, and that it's a property symbol */ if (sym != 0 && sym->get_type() == TC_SYM_PROP) { /* it's a property symbol - cast it */ CTcSymProp *prop = (CTcSymProp *)sym; /* check for dynamic compilation */ if (G_vmifc != 0) { /* dynamic mode - add it to the live object in the VM */ G_vmifc->set_prop(obj->get_obj_id(), prop->get_prop(), val); } else { /* static compilation - add the value to the object statement */ add_gen_obj_prop_stat(obj, prop, val); } } } /* * add an integer property value to a generated object */ void CTcParser::add_gen_obj_prop(CTcSymObj *obj, const char *prop, int val) { /* set up a constant structure for the value */ CTcConstVal c; c.set_int(val); /* set the property */ add_gen_obj_prop(obj, prop, &c); } /* * add a string property value to a generated object */ void CTcParser::add_gen_obj_prop(CTcSymObj *obj, const char *prop, const char *val) { /* set up a constant structure for the value */ CTcConstVal c; c.set_sstr(val, strlen(val)); /* set the property */ add_gen_obj_prop(obj, prop, &c); } /* ------------------------------------------------------------------------ */ /* * Constant Value */ /* * set a string value */ void CTcConstVal::set_sstr(const char *val, size_t len) { /* store the type */ typ_ = TC_CVT_SSTR; /* store a pointer to the string */ val_.strval_.strval_ = val; val_.strval_.strval_len_ = len; /* for image file layout purposes, record the length of this string */ G_cg->note_str(len); } void CTcConstVal::set_sstr(const CTcToken *tok) { /* get the string from the token */ if (tok != 0) set_sstr(tok->get_text(), tok->get_text_len()); else set_sstr("", 0); } void CTcConstVal::set_sstr(uint32_t ofs) { typ_ = TC_CVT_SSTR; val_.strval_.strval_ = 0; val_.strval_.strval_len_ = 0; val_.strval_.pool_ofs_ = ofs; } /* * Set a regex string value */ void CTcConstVal::set_restr(const CTcToken *tok) { typ_ = TC_CVT_RESTR; val_.strval_.strval_ = tok->get_text(); val_.strval_.strval_len_ = tok->get_text_len(); val_.strval_.pool_ofs_ = 0; } /* * BigNumber string formatter buffer allocator. Allocates a buffer of the * required size from the parser memory pool. */ class CBigNumStringBufPrsAlo: public IBigNumStringBuf { public: virtual char *get_buf(size_t need) { return new (G_prsmem) char[need]; } }; /* set a floating point value from a token string */ void CTcConstVal::set_float(const char *str, size_t len, int promoted) { /* set the float */ typ_ = TC_CVT_FLOAT; val_.floatval_.txt_ = str; val_.floatval_.len_ = len; promoted_ = promoted; } /* set a floating point value from a vbignum_t */ void CTcConstVal::set_float(const vbignum_t *val, int promoted) { /* format the value */ CBigNumStringBufPrsAlo alo; const char *buf = val->format(&alo); /* * read the length from the buffer - vbignum_t::format() fills in the * buffer using the TADS String format, with a two-byte little-endian * (VMB_LEN) length prefix */ size_t len = vmb_get_len(buf); buf += VMB_LEN; /* store it */ set_float(buf, len, promoted); } /* set a floating point value from a promoted integer value */ void CTcConstVal::set_float(ulong i) { /* set up the bignum value */ vbignum_t b(i); /* store it as a promoted integer value */ set_float(&b, TRUE); } /* * Try demoting a float back to an int. This can be used after a * constant-folding operation to turn a previously promoted float back to * an int if the result of the calculation now fits the int type. */ void CTcConstVal::demote_float() { /* if this is a promoted integer, see if we can demote it back to int */ if (typ_ == TC_CVT_FLOAT && promoted_) { /* get the BigNumber value */ vbignum_t b(val_.floatval_.txt_, val_.floatval_.len_, 0); /* convert it to an integer */ int ov; int32_t i = b.to_int(ov); /* if it didn't overflow, demote the constant back to an int */ if (!ov) set_int(i); } } /* * set a list value */ void CTcConstVal::set_list(CTPNList *lst) { /* set the type */ typ_ = TC_CVT_LIST; /* remember the list */ val_.listval_.l_ = lst; /* for image file layout purposes, record the length of this list */ G_cg->note_list(lst->get_count()); } void CTcConstVal::set_list(uint32_t ofs) { typ_ = TC_CVT_LIST; val_.listval_.l_ = 0; val_.listval_.pool_ofs_ = ofs; } /* * Convert a value to a string */ const char *CTcConstVal::cvt_to_str(char *buf, size_t bufl, size_t *result_len) { /* check my type */ switch(typ_) { case TC_CVT_NIL: /* the result is "nil" */ if (bufl < 4) return 0; strcpy(buf, "nil"); *result_len = 3; return buf; case TC_CVT_TRUE: /* the result is "true" */ if (bufl < 5) return 0; strcpy(buf, "true"); *result_len = 4; return buf; case TC_CVT_SSTR: /* it's already a string */ *result_len = get_val_str_len(); return get_val_str(); case TC_CVT_INT: /* convert our signed integer value */ if (bufl < 12) return 0; sprintf(buf, "%ld", get_val_int()); *result_len = strlen(buf); return buf; case TC_CVT_FLOAT: /* we store these as strings */ *result_len = get_val_float_len(); return get_val_float(); default: /* can't convert other types */ return 0; } } /* is this a numeric value equal to zero? */ int CTcConstVal::equals_zero() const { /* check for integer zero */ if (typ_ == TC_CVT_INT && get_val_int() == 0) return TRUE; /* check for a float zero */ if (typ_ == TC_CVT_FLOAT) { vbignum_t v(get_val_float(), get_val_float_len(), 0); return v.is_zero(); } /* not zero */ return FALSE; } /* * Compare for equality to another constant value */ int CTcConstVal::is_equal_to(const CTcConstVal *val) const { CTPNListEle *ele1; CTPNListEle *ele2; /* check for float-int comparisons */ if (typ_ == TC_CVT_INT && val->get_type() == TC_CVT_FLOAT) { vbignum_t a(get_val_int()); vbignum_t b(val->get_val_float(), val->get_val_float_len(), 0); return a.compare(b) == 0; } if (typ_ == TC_CVT_FLOAT && val->get_type() == TC_CVT_INT) { vbignum_t a(get_val_float(), get_val_float_len(), 0); vbignum_t b(val->get_val_int()); return a.compare(b) == 0; } /* * if the types aren't equal, the values are not equal; otherwise, * check the various types */ if (typ_ != val->get_type()) { /* the types aren't equal, so the values are not equal */ return FALSE; } /* the types are the same; do the comparison based on the type */ switch(typ_) { case TC_CVT_UNK: /* unknown type; unknown values can never be equal */ return FALSE; case TC_CVT_TRUE: case TC_CVT_NIL: /* * nil==nil and true==true; since we know the types are the * same, the values are the same */ return TRUE; case TC_CVT_INT: /* compare the integers */ return (get_val_int() == val->get_val_int()); case TC_CVT_FLOAT: { vbignum_t a(get_val_float(), get_val_float_len(), 0); vbignum_t b(val->get_val_float(), val->get_val_float_len(), 0); return a.compare(b) == 0; } case TC_CVT_SSTR: /* compare the strings */ return (get_val_str_len() == val->get_val_str_len() && memcmp(get_val_str(), val->get_val_str(), get_val_str_len()) == 0); case TC_CVT_LIST: /* * if the lists don't have the same number of elements, they're * not equal */ if (get_val_list()->get_count() != val->get_val_list()->get_count()) return FALSE; /* * compare each element of each list; if they're all the same, * the values are the same */ ele1 = get_val_list()->get_head(); ele2 = val->get_val_list()->get_head(); for ( ; ele1 != 0 && ele2 != 0 ; ele1 = ele1->get_next(), ele2 = ele2->get_next()) { /* if these elements aren't equal, the lists aren't equal */ if (!ele1->get_expr()->get_const_val() ->is_equal_to(ele2->get_expr()->get_const_val())) return FALSE; } /* we didn't find any differences, so the lists are equal */ return TRUE; case TC_CVT_OBJ: /* if the object values are the same, the values match */ return (get_val_obj() == val->get_val_obj()); case TC_CVT_PROP: /* if the property values are the same, the values match */ return (get_val_prop() == val->get_val_prop()); case TC_CVT_FUNCPTR: /* * if both symbols are the same, the values match; otherwise, * they refer to different functions */ return (get_val_funcptr_sym() == val->get_val_funcptr_sym()); default: /* unknown type; return unequal */ return FALSE; } } /* ------------------------------------------------------------------------ */ /* * Operator Parsers */ /* ------------------------------------------------------------------------ */ /* * Parse a left-associative binary operator */ CTcPrsNode *CTcPrsOpBin::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find our operator */ for (;;) { /* check my operator */ if (G_tok->cur() == get_op_tok()) { /* skip the matching token */ G_tok->next(); /* parse the right-hand side */ CTcPrsNode *rhs = right_->parse(); if (rhs == 0) return 0; /* try folding our subnodes into a constant value, if possible */ CTcPrsNode *const_tree = eval_constant(lhs, rhs); /* * if we couldn't calculate a constant value, build the tree * normally */ if (const_tree == 0) { /* * Build my tree, then proceed to parse any additional * occurrences of our operator, with the result of * applying this occurrence of the operator as the * left-hand side of the new operator. */ lhs = build_tree(lhs, rhs); } else { /* we got a constant value - use it as the result directly */ lhs = const_tree; } } else { /* * it's not my operator - return what we thought might have * been our left-hand side */ return lhs; } } } /* ------------------------------------------------------------------------ */ /* * Parse a group of left-associative binary operators at the same * precedence level */ CTcPrsNode *CTcPrsOpBinGroup::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find one of our operators */ while (find_and_apply_op(&lhs)) ; /* return the expression tree */ return lhs; } /* * Find an apply one of our operators to the already-parsed left-hand * side. Returns true if we found an operator, false if not. */ int CTcPrsOpBinGroup::find_and_apply_op(CTcPrsNode **lhs) const { /* check each operator at this precedence level */ for (const CTcPrsOpBin *const *op = ops_ ; *op != 0 ; ++op) { /* check this operator's token */ if (G_tok->cur() == (*op)->get_op_tok()) { /* skip the operator token */ G_tok->next(); /* parse the right-hand side */ CTcPrsNode *rhs = right_->parse(); if (rhs == 0) { /* error - cancel the entire expression */ *lhs = 0; return FALSE; } /* try folding our subnodes into a constant value */ CTcPrsNode *const_tree = (*op)->eval_constant(*lhs, rhs); /* * if we couldn't calculate a constant value, build the tree * normally */ if (const_tree == 0) { /* * build my tree, replacing the original left-hand side * with the new expression */ *lhs = (*op)->build_tree(*lhs, rhs); } else { /* we got a constant value - use it as the result */ *lhs = const_tree; } /* * Tell the caller to proceed to parse any additional * occurrences of our operator - this will apply the next * occurrence of the operator as the left-hand side of the * new operator. */ return TRUE; } } /* * if we got here, we didn't find an operator - tell the caller that * we've reached the end of this operator's possible span */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Comparison operator group */ CTcPrsNode *CTcPrsOpBinGroupCompare::parse() const { /* parse our left side - if that fails, return failure */ CTcPrsNode *lhs = left_->parse(); if (lhs == 0) return 0; /* keep going as long as we find one of our operators */ for (;;) { /* * try one of our regular operators - if we find it, go back for * another round to see if there's another operator following * the next expression */ if (find_and_apply_op(&lhs)) continue; /* * check for the 'is in' operator - 'is' and 'in' aren't * keywords, so we must check for symbol tokens with the text of * these context-sensitive keywords */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("is", 2)) { /* we have 'is' - get the next token and check if it's 'in' */ if (G_tok->next() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* scan the expression list */ CTPNArglist *rhs = parse_inlist(); if (rhs == 0) return 0; /* build the node */ lhs = new CTPNIsIn(lhs, rhs); /* * we've applied the 'is in' operator - go back for * another operator from the comparison group */ continue; } else { /* it's not 'is in' - throw back the token and keep looking */ G_tok->unget(); } } /* * Check for the 'not in' operator */ if (G_tok->cur() == TOKT_SYM && G_tok->getcur()->text_matches("not", 3)) { /* we have 'is' - get the next token and check if it's 'in' */ if (G_tok->next() == TOKT_SYM && G_tok->getcur()->text_matches("in", 2)) { /* scan the expression list */ CTPNArglist *rhs = parse_inlist(); if (rhs == 0) return 0; /* build the node */ lhs = new CTPNNotIn(lhs, rhs); /* * we've applied the 'is in' operator - go back for * another operator from the comparison group */ continue; } else { /* it's not 'is in' - throw back the token and keep looking */ G_tok->unget(); } } /* we didn't find any of our operators - we're done */ break; } /* return the expression */ return lhs; } /* * parse the list for the right-hand side of an 'is in' or 'not in' * expression */ CTPNArglist *CTcPrsOpBinGroupCompare::parse_inlist() const { int argc; CTPNArg *arg_head; CTPNArg *arg_tail; /* skip the second keyword token, and check for an open paren */ if (G_tok->next() == TOKT_LPAR) { /* skip the paren */ G_tok->next(); } else { /* * log an error, and keep going on the assumption that it was * merely omitted and the rest of the list is well-formed */ G_tok->log_error_curtok(TCERR_IN_REQ_LPAR); } /* keep going until we find the close paren */ for (argc = 0, arg_head = arg_tail = 0 ;; ) { CTcPrsNode *expr; CTPNArg *arg_cur; /* if this is the close paren, we're done */ if (G_tok->cur() == TOKT_RPAR) break; /* parse this expression */ expr = S_op_asi.parse(); if (expr == 0) return 0; /* count the argument */ ++argc; /* create a new argument node */ arg_cur = new CTPNArg(expr); /* * link the new node at the end of our list (this preserves the * order of the original list) */ if (arg_tail != 0) arg_tail->set_next_arg(arg_cur); else arg_head = arg_cur; arg_tail = arg_cur; /* we need to be looking at a comma or right paren */ if (G_tok->cur() == TOKT_RPAR) { /* that's the end of the list */ break; } else if (G_tok->cur() == TOKT_COMMA) { /* skip the comma and parse the next argument */ G_tok->next(); } else { /* * If we're at the end of the file, there's no point * proceding, so return failure. If we've reached something * that looks like a statement separator (semicolon, curly * brace), also return failure, since the problem is clearly * a missing right paren. Otherwise, assume that a comma * was missing and continue as though we have another * argument. */ switch(G_tok->cur()) { default: /* log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_IN_COMMA); /* * if we're at the end of file, return what we have so * far; otherwise continue, assuming that they merely * left out a comma between two argument expressions */ if (G_tok->cur() == TOKT_EOF) return new CTPNArglist(argc, arg_head); break; case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * we're apparently at the end of the statement; flag * the error as a missing right paren, and return what * we have so far */ G_tok->log_error_curtok(TCERR_EXPECTED_IN_RPAR); return new CTPNArglist(argc, arg_head); } } } /* skip the closing paren */ G_tok->next(); /* create and return the argument list descriptor */ return new CTPNArglist(argc, arg_head); } /* ------------------------------------------------------------------------ */ /* * Comma Operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpComma::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* * if both sides are constants, the result is the constant on the * right side; we can't simply fold down to a right-side constant if * the left side is not constant, though, because we must still * evaluate the left side at run-time for any possible side effects */ if (left->is_const() && right->is_const()) { /* both are constants - simply return the right constant value */ return right; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build a subtree for the comma operator */ CTcPrsNode *CTcPrsOpComma::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNComma(left, right); } /* ------------------------------------------------------------------------ */ /* * logical OR operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpOr::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const()) { CTcPrsNode *ret; /* * Check for constants. If the first expression is constant, * the result will always be either 'true' (if the first * expression's constant value is true), or the value of the * second expression (if the first expression's constant value * is 'nil'). * * Note that it doesn't matter whether or not the right side is * a constant. If the left is true, the right will never be * executed because of the short-circuit logic; if the left is * nil, the result will always be the result of the right value. */ if (left->get_const_val()->get_val_bool()) { /* * the left is true, so the result is always true, and the * right never gets executed */ ret = left; } else { /* the left is nil, so the result is the right value */ ret = right; } /* ensure the result is a boolean value */ if (ret->is_const()) { /* make it a true/nil constant value */ ret->get_const_val() ->set_bool(ret->get_const_val()->get_val_bool()); } else { /* boolean-ize the value at run-time as needed */ ret = new CTPNBoolize(ret); } /* return the result */ return ret; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build the subtree */ CTcPrsNode *CTcPrsOpOr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNOr(left, right); } /* ------------------------------------------------------------------------ */ /* * logical AND operator */ /* * try to evaluate a constant expression */ CTcPrsNode *CTcPrsOpAnd::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* * Check for constants. If the first expression is constant, the * result will always be either 'nil' (if the first expression's * constant value is nil), or the value of the second expression (if * the first expression's constant value is 'true'). * * Note that it doesn't matter whether or not the right side is a * constant. If the left is nil, the right will never be executed * because of the short-circuit logic; if the left is true, the * result will always be the result of the right value. */ if (left->is_const()) { CTcPrsNode *ret; /* * The left value is a constant, so we can eliminate the &&. If * the left value is nil, the result is nil; otherwise, it's the * right half. */ if (left->get_const_val()->get_val_bool()) { /* the left side is true - the result is the right side */ ret = right; } else { /* * The left side is nil - the result is nil, and the right * side never gets executed. */ ret = left; } /* ensure the result is a boolean value */ if (ret->is_const()) { /* make it a true/nil constant value */ ret->get_const_val() ->set_bool(ret->get_const_val()->get_val_bool()); } else { /* boolean-ize the value at run-time as needed */ ret = new CTPNBoolize(ret); } /* return the result */ return ret; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* * build the subtree */ CTcPrsNode *CTcPrsOpAnd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAnd(left, right); } /* ------------------------------------------------------------------------ */ /* * Generic Comparison Operator parser base class */ /* * evaluate a constant expression */ CTcPrsNode *CTcPrsOpRel::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { /* get the types */ CTcConstVal *c1 = left->get_const_val(); CTcConstVal *c2 = right->get_const_val(); tc_constval_type_t typ1 = c1->get_type(); tc_constval_type_t typ2 = c2->get_type(); /* determine what we're comparing */ int sense; if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) { /* get the values */ long val1 = c1->get_val_int(); long val2 = c2->get_val_int(); /* calculate the sense of the integer comparison */ sense = (val1 < val2 ? -1 : val1 == val2 ? 0 : 1); } else if (typ1 == TC_CVT_SSTR && typ2 == TC_CVT_SSTR) { /* compare the string values */ sense = strcmp(c1->get_val_str(), c2->get_val_str()); } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_FLOAT) { /* both are floats - get as BigNumber values */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); sense = a.compare(b); } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_INT) { /* float vs int */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_int()); sense = a.compare(b); } else if (typ1 == TC_CVT_INT && typ2 == TC_CVT_FLOAT) { vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); sense = a.compare(b); } else { /* these types are incomparable */ G_tok->log_error(TCERR_CONST_BAD_COMPARE, G_tok->get_op_text(get_op_tok())); return 0; } /* set the result in the left value */ c1->set_bool(get_bool_val(sense)); /* return the updated left value */ return left; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * greater-than operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpGt::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNGt(left, right); } /* ------------------------------------------------------------------------ */ /* * less-than operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLt::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLt(left, right); } /* ------------------------------------------------------------------------ */ /* * greater-or-equal operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpGe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNGe(left, right); } /* ------------------------------------------------------------------------ */ /* * less-or-equal operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLe(left, right); } /* ------------------------------------------------------------------------ */ /* * General equality/inequality operators base class */ /* * evaluate a constant expression */ CTcPrsNode *CTcPrsOpEqComp::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { int ops_equal; /* check for constants */ if (left->is_const() && right->is_const()) { /* both sides are constants - determine if they're equal */ ops_equal = left->get_const_val() ->is_equal_to(right->get_const_val()); /* set the result in the left value */ left->get_const_val()->set_bool(get_bool_val(ops_equal)); /* return the updated left value */ return left; } else if (left->is_addr() && right->is_addr()) { CTcConstVal cval; int comparable; /* * both sides are addresses - if they're both addresses of the * same subexpression, then the values are comparable as * compile-time constants */ ops_equal = ((CTPNAddr *)left) ->is_addr_eq((CTPNAddr *)right, &comparable); /* if they're not comparable, we can't fold this as a constant */ if (!comparable) return 0; /* generate the appropriate boolean result for the comparison */ cval.set_bool(get_bool_val(ops_equal)); /* return a new constant node with the result */ return new CTPNConst(&cval); } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * equality operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpEq::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNEq(left, right); } /* ------------------------------------------------------------------------ */ /* * inequality operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpNe::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNNe(left, right); } /* ------------------------------------------------------------------------ */ /* * 'is in' operator */ /* * construct */ CTPNIsInBase::CTPNIsInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) : CTPNBin(lhs, rhs) { /* presume we don't have a constant value */ const_true_ = FALSE; } /* * fold constants */ CTcPrsNode *CTPNIsInBase::fold_binop() { CTPNArglist *lst; CTPNArg *arg; CTPNArg *prv; CTPNArg *nxt; /* if the left-hand side isn't constant, there's nothing to do */ if (!left_->is_const()) return this; /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* look for the value in the arguments */ for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) { /* remember the next argument, in case we eliminate this one */ nxt = arg->get_next_arg(); /* check to see if this argument is a constant */ if (arg->is_const()) { /* * This one's a constant, so check to see if we found the * left side value. If the left side equals this value, * note that we found the value. */ if (left_->get_const_val()->is_equal_to(arg->get_const_val())) { /* * The values are equal, so the result of the expression * is definitely 'true'. */ const_true_ = TRUE; /* * Because the 'is in' operator only evaluates operands * from the 'in' list until it finds one that matches, * any remaining operands will simply never be * evaluated. We can thus discard the rest of the * argument list. */ nxt = 0; } /* * We now know whether the left side equals this constant * list element. This is never going to change because both * values are constant, so there's no point in making this * same comparison over and over again at run-time. We can * thus eliminate this argument from the list. */ lst->set_argc(lst->get_argc() - 1); if (prv == 0) lst->set_arg_list_head(nxt); else prv->set_next_arg(nxt); } } /* * If the argument list is now completely empty, the result of the * expression is a constant. */ if (lst->get_arg_list_head() == 0) { /* set the left operand's value to our result */ left_->get_const_val()->set_bool(const_true_); /* return the constant value in place of the entire expression */ return left_; } /* we're not a constant, to return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * 'not in' operator */ /* * construct */ CTPNNotInBase::CTPNNotInBase(CTcPrsNode *lhs, class CTPNArglist *rhs) : CTPNBin(lhs, rhs) { /* presume we don't have a constant value */ const_false_ = FALSE; } /* * fold constants for binary operator */ CTcPrsNode *CTPNNotInBase::fold_binop() { CTPNArglist *lst; CTPNArg *arg; CTPNArg *prv; CTPNArg *nxt; /* if the left-hand side isn't constant, there's nothing to do */ if (!left_->is_const()) return this; /* the right side is always an argument list */ lst = (CTPNArglist *)right_; /* look for the value in the arguments */ for (prv = 0, arg = lst->get_arg_list_head() ; arg != 0 ; arg = nxt) { /* remember the next argument, in case we eliminate this one */ nxt = arg->get_next_arg(); /* check to see if this argument is a constant */ if (arg->is_const()) { /* * This one's a constant, so check to see if we found the * left side value. If the left side equals this value, * note that we found the value. */ if (left_->get_const_val()->is_equal_to(arg->get_const_val())) { /* * The values are equal, so the result of the expression * is definitely 'nil'. */ const_false_ = TRUE; /* * Because the 'not in' operator only evaluates operands * from the 'in' list until it finds one that matches, * any remaining operands will simply never be * evaluated. We can thus discard the rest of the * argument list. */ nxt = 0; } /* * We now know whether the left side equals this constant * list element. This is never going to change because both * values are constant, so there's no point in making this * same comparison over and over again at run-time. We can * thus eliminate this argument from the list. */ lst->set_argc(lst->get_argc() - 1); if (prv == 0) lst->set_arg_list_head(nxt); else prv->set_next_arg(nxt); } } /* * If the argument list is now completely empty, the result of the * expression is a constant. */ if (lst->get_arg_list_head() == 0) { /* set the left operand's value to our result */ left_->get_const_val()->set_bool(!const_false_); /* return the constant value in place of the entire expression */ return left_; } /* we're not a constant, to return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * General arithmetic operator base class */ /* * evaluate constant value */ CTcPrsNode *CTcPrsOpArith::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { CTcConstVal *c1 = left->get_const_val(); CTcConstVal *c2 = right->get_const_val(); tc_constval_type_t typ1 = c1->get_type(); tc_constval_type_t typ2 = c2->get_type(); /* check for ints, floats, or a combination */ if (typ1 == TC_CVT_INT && typ2 == TC_CVT_INT) { /* calculate the int result */ int ov; long r = calc_result(c1->get_val_int(), c2->get_val_int(), ov); /* on overflow, recalculate using BigNumbers */ if (ov) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_int()); vbignum_t *c = calc_result(a, b); /* if successful, assign it back to the left operand */ if (c != 0) { c1->set_float(c, TRUE); delete c; } } else { /* success - assign the folded int to the left operand */ c1->set_int(r); } } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_FLOAT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c1->is_promoted() && c2->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else if (typ1 == TC_CVT_FLOAT && typ2 == TC_CVT_INT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_float(), c1->get_val_float_len(), 0); vbignum_t b(c2->get_val_int()); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c1->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else if (typ1 == TC_CVT_INT && typ2 == TC_CVT_FLOAT) { /* calculate the BigNumber result */ vbignum_t a(c1->get_val_int()); vbignum_t b(c2->get_val_float(), c2->get_val_float_len(), 0); vbignum_t *c = calc_result(a, b); /* assign it back to the left operand */ if (c != 0) { /* save the value and delete the calculation result */ c1->set_float(c, c2->is_promoted()); delete c; /* check for possible demotion back to int */ c1->demote_float(); } } else { /* incompatible types - log an error */ G_tok->log_error(TCERR_CONST_BINARY_REQ_NUM, G_tok->get_op_text(get_op_tok())); return 0; } /* return the updated left value */ return left; } else { /* * one or the other is non-constant, so we can't fold the * expression - return null to so indicate */ return 0; } } /* ------------------------------------------------------------------------ */ /* * bitwise OR operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBOr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBOr(left, right); } /* ------------------------------------------------------------------------ */ /* * bitwise AND operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBAnd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBAnd(left, right); } /* ------------------------------------------------------------------------ */ /* * bitwise XOR operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpBXor::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNBXor(left, right); } /* ------------------------------------------------------------------------ */ /* * shift left operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpShl::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNShl(left, right); } /* ------------------------------------------------------------------------ */ /* * arithmetic shift right operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpAShr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAShr(left, right); } /* ------------------------------------------------------------------------ */ /* * logical shift right operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpLShr::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNLShr(left, right); } /* ------------------------------------------------------------------------ */ /* * multiplication operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpMul::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNMul(left, right); } /* * calculate a constant float result */ vbignum_t *CTcPrsOpMul::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a * b; } /* ------------------------------------------------------------------------ */ /* * division operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpDiv::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNDiv(left, right); } /* * evaluate constant integer result */ long CTcPrsOpDiv::calc_result(long a, long b, int &ov) const { /* check for divide-by-zero */ if (b == 0) { /* log a divide-by-zero error */ G_tok->log_error(TCERR_CONST_DIV_ZERO); /* the result isn't really meaningful, but return something anyway */ return 1; } /* * check for overflow - there's only one way that integer division can * overflow, which is to divide INT32MINVAL by -1 */ ov = (a == INT32MINVAL && b == 1); /* return the result */ return a / b; } /* * calculate a constant float result */ vbignum_t *CTcPrsOpDiv::calc_result( const vbignum_t &a, const vbignum_t &b) const { /* check for division by zero */ if (b.is_zero()) { /* log an error, and just return the left operand */ G_tok->log_error(TCERR_CONST_DIV_ZERO); return new vbignum_t(1L); } else { /* calculate the result */ return a / b; } } /* ------------------------------------------------------------------------ */ /* * modulo operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpMod::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNMod(left, right); } /* * evaluate constant result */ long CTcPrsOpMod::calc_result(long a, long b, int &ov) const { /* there's no way for integer modulo to overflow */ ov = FALSE; /* check for divide-by-zero */ if (b == 0) { /* log a divide-by-zero error */ G_tok->log_error(TCERR_CONST_DIV_ZERO); /* the result isn't really meaningful, but return something anyway */ return 1; } else { /* return the result */ return a % b; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpMod::calc_result( const vbignum_t &a, const vbignum_t &b) const { /* check for division by zero */ if (b.is_zero()) { /* log an error, and just return the left operand */ G_tok->log_error(TCERR_CONST_DIV_ZERO); return new vbignum_t(1L); } else { /* calculate the result */ return a % b; } } /* ------------------------------------------------------------------------ */ /* * subtraction operator */ /* * build the subtree */ CTcPrsNode *CTcPrsOpSub::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNSub(left, right); } /* * evaluate a constant value */ CTcPrsNode *CTcPrsOpSub::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { if (left->is_const() && right->is_const()) { tc_constval_type_t typ1, typ2; /* get the types */ typ1 = left->get_const_val()->get_type(); typ2 = right->get_const_val()->get_type(); /* check our types */ if ((typ1 == TC_CVT_INT || typ1 == TC_CVT_FLOAT) && (typ2 == TC_CVT_INT || typ2 == TC_CVT_FLOAT)) { /* int/float minus int/float - use the inherited handling */ return CTcPrsOpArith::eval_constant(left, right); } else if (typ1 == TC_CVT_LIST) { /* get the original list */ CTPNList *lst = left->get_const_val()->get_val_list(); /* * if the right side is a list, remove each element of that * list from the list on the left; otherwise, remove the * value on the right from the list on the left */ if (typ2 == TC_CVT_LIST) { /* remove each element of the rhs list from the lhs list */ CTPNListEle *ele; /* scan the list, adding each element */ for (ele = right->get_const_val() ->get_val_list()->get_head() ; ele != 0 ; ele = ele->get_next()) { /* add this element's underlying expression value */ lst->remove_element(ele->get_expr()->get_const_val()); } } else { /* remove the rhs value from the lhs list */ lst->remove_element(right->get_const_val()); } } else if (typ1 == TC_CVT_OBJ) { /* * assume it's an overloaded operator, in which case constant * evaluation isn't possible, but it could still be a legal * expression */ return 0; } else { /* these types are incompatible - log an error */ G_tok->log_error(TCERR_CONST_BINMINUS_INCOMPAT); return 0; } /* return the updated left side */ return left; } else { /* they're not constant - we can't generate a constant result */ return 0; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpSub::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a - b; } /* ------------------------------------------------------------------------ */ /* * addition operator */ /* * evaluate constant value */ CTcPrsNode *CTcPrsOpAdd::eval_constant(CTcPrsNode *left, CTcPrsNode *right) const { /* check for constants */ if (left->is_const() && right->is_const()) { tc_constval_type_t typ1, typ2; /* get the types */ typ1 = left->get_const_val()->get_type(); typ2 = right->get_const_val()->get_type(); /* check our types */ if ((typ1 == TC_CVT_INT || typ1 == TC_CVT_FLOAT) && (typ2 == TC_CVT_INT || typ2 == TC_CVT_FLOAT)) { /* int/float plus int/float - use the inherited handling */ return CTcPrsOpArith::eval_constant(left, right); } else if (typ1 == TC_CVT_LIST) { /* get the original list */ CTPNList *lst = left->get_const_val()->get_val_list(); /* * if the right side is also a list, concatenate it onto the * left list; otherwise, just add the right side as a new * element to the existing list */ if (typ2 == TC_CVT_LIST) { CTPNListEle *ele; /* scan the list, adding each element */ for (ele = right->get_const_val() ->get_val_list()->get_head() ; ele != 0 ; ele = ele->get_next()) { /* add this element's underlying expression value */ lst->add_element(ele->get_expr()); } } else { /* add a new list element for the right side */ lst->add_element(right); } /* * this list is longer than the original(s); tell the parser * about it in case it's the longest list yet */ G_cg->note_list(lst->get_count()); } else if (typ1 == TC_CVT_SSTR || typ2 == TC_CVT_SSTR) { char buf1[128]; char buf2[128]; const char *str1, *str2; size_t len1, len2; char *new_str; /* if the second value is a list, we can't make a constant */ if (typ2 == TC_CVT_LIST) return 0; /* convert both values to strings if they're not already */ str1 = left->get_const_val() ->cvt_to_str(buf1, sizeof(buf1), &len1); str2 = right->get_const_val() ->cvt_to_str(buf2, sizeof(buf2), &len2); /* treat nil as an empty string */ if (typ2 == TC_CVT_NIL) str1 = "", len2 = 0; /* * if we couldn't convert one or the other, leave the result * non-constant */ if (str1 == 0 || str2 == 0) return 0; /* * allocate space in the node pool for the concatenation of * the two strings - if that fails, don't bother with the * concatenation */ new_str = (char *)G_prsmem->alloc(len1 + len2 + 1); if (new_str == 0) return 0; /* copy the two string values into the new space */ memcpy(new_str, str1, len1); memcpy(new_str + len1, str2, len2); new_str[len1 + len2] = '\0'; /* set the new value in the left node */ left->get_const_val()->set_sstr(new_str, len1 + len2); } else if (typ1 == TC_CVT_OBJ) { /* * assume it's an overloaded operator, in which case constant * evaluation isn't possible, but it could still be a legal * expression */ return 0; } else { /* these types are incompatible - log an error */ G_tok->log_error(TCERR_CONST_BINPLUS_INCOMPAT); return 0; } /* return the updated left value */ return left; } else { /* the values aren't constant, so the result isn't constant */ return 0; } } /* * calculate a constant float result */ vbignum_t *CTcPrsOpAdd::calc_result( const vbignum_t &a, const vbignum_t &b) const { return a + b; } /* * build the subtree */ CTcPrsNode *CTcPrsOpAdd::build_tree(CTcPrsNode *left, CTcPrsNode *right) const { return new CTPNAdd(left, right); } /* ------------------------------------------------------------------------ */ /* * Assignment Operator Group */ /* * parse an assignment expression */ CTcPrsNode *CTcPrsOpAsi::parse() const { /* start by parsing a conditional subexpression */ CTcPrsNode *lhs = S_op_if.parse(); if (lhs == 0) return 0; /* get the next operator */ tc_toktyp_t curtyp = G_tok->cur(); /* check to see if it's an assignment operator of some kind */ switch(curtyp) { case TOKT_PLUSEQ: case TOKT_MINEQ: case TOKT_TIMESEQ: case TOKT_DIVEQ: case TOKT_MODEQ: case TOKT_ANDEQ: case TOKT_OREQ: case TOKT_XOREQ: case TOKT_SHLEQ: case TOKT_ASHREQ: case TOKT_LSHREQ: /* it's an assignment operator - process it */ break; default: /* check against the current simple-assignment operator */ if (curtyp == asi_op_) { /* it's an assignment operator - process it */ break; } else { /* * it's not an assignment - return the original * subexpression with no further elaboration */ return lhs; } } /* check for a valid lvalue */ if (!lhs->check_lvalue()) { /* log an error but continue parsing */ G_tok->log_error(TCERR_INVALID_LVALUE, G_tok->get_op_text(G_tok->cur())); } /* skip the assignment operator */ G_tok->next(); /* * Recursively parse an assignment subexpression. Do this * recursively rather than iteratively, because assignment operators * group right-to-left. By recursively parsing an assignment, our * right-hand side will contain all remaining assignment expressions * incorporated into it. */ CTcPrsNode *rhs = parse(); if (rhs == 0) return 0; /* build and return the result based on the operator type */ switch(curtyp) { case TOKT_PLUSEQ: lhs = new CTPNAddAsi(lhs, rhs); break; case TOKT_MINEQ: lhs = new CTPNSubAsi(lhs, rhs); break; case TOKT_TIMESEQ: lhs = new CTPNMulAsi(lhs, rhs); break; case TOKT_DIVEQ: lhs = new CTPNDivAsi(lhs, rhs); break; case TOKT_MODEQ: lhs = new CTPNModAsi(lhs, rhs); break; case TOKT_ANDEQ: lhs = new CTPNBAndAsi(lhs, rhs); break; case TOKT_OREQ: lhs = new CTPNBOrAsi(lhs, rhs); break; case TOKT_XOREQ: lhs = new CTPNBXorAsi(lhs, rhs); break; case TOKT_SHLEQ: lhs = new CTPNShlAsi(lhs, rhs); break; case TOKT_ASHREQ: lhs = new CTPNAShrAsi(lhs, rhs); break; case TOKT_LSHREQ: lhs = new CTPNLShrAsi(lhs, rhs); break; default: /* plain assignment operator */ lhs = new CTPNAsi(lhs, rhs); break; } /* return the result */ return lhs; } /* ------------------------------------------------------------------------ */ /* * Ifnil operator ?? */ CTcPrsNode *CTcPrsOpIfnil::parse() const { /* parse the conditional part */ CTcPrsNode *first = S_op_or.parse(); if (first == 0) return 0; /* if we're not looking at the '??' operator, we're done */ if (G_tok->cur() != TOKT_QQ) return first; /* skip the '??' operator */ G_tok->next(); /* * parse the second part, which can be any expression except ',', since * we have higher precedence than ',' */ CTcPrsNode *second = G_prs->parse_expr_or_dstr(FALSE); if (second == 0) return 0; /* if the first expression is constant, we can fold immediately */ if (first->is_const()) { /* if it's nil, yield the second value, otherwise yield the first */ return (first->get_const_val()->get_type() == TC_CVT_NIL ? second : first); } else { /* it's not a constant value - return a new if-nil node */ return new CTPNIfnil(first, second); } } /* ------------------------------------------------------------------------ */ /* * Tertiary Conditional Operator */ CTcPrsNode *CTcPrsOpIf::parse() const { /* parse the conditional part */ CTcPrsNode *first = S_op_ifnil.parse(); if (first == 0) return 0; /* if we're not looking at the '?' operator, we're done */ if (G_tok->cur() != TOKT_QUESTION) return first; /* skip the '?' operator */ G_tok->next(); /* * parse the second part, which can be any expression, including a * double-quoted string expression or a comma expression (even though * the '?:' operator overall has higher precedence than ',', we can't * steal away operands from a ',' before our ':' because that would * leave the ':' with nothing to go with) */ CTcPrsNode *second = G_prs->parse_expr_or_dstr(TRUE); if (second == 0) return 0; /* make sure we have the ':' after the second part */ if (G_tok->cur() != TOKT_COLON) { /* * log the error, but continue parsing as though we found the * ':' - if the ':' is simply missing, this will allow us to * recover and continue parsing the rest of the expression */ G_tok->log_error(TCERR_QUEST_WITHOUT_COLON); /* if we're at the end of file, there's no point in continuing */ if (G_tok->cur() == TOKT_EOF) return 0; } /* skip the ':' */ G_tok->next(); /* * parse the third part, which can be any other expression, including a * double-quoted string expression - but not a comma expression, since * we have higher precedence than ',' */ CTcPrsNode *third = G_prs->parse_expr_or_dstr(FALSE); if (third == 0) return 0; /* * If the condition is constant, we can choose the second or third * expression directly. It doesn't matter whether or not the second * and/or third parts are themselves constant, because a constant * condition means that we'll always execute only one of the * alternatives. */ if (first->is_const()) { /* * evaluate the conditional value as a true/false value, and * return the second part's constant if the condition is true, * or the third part's constant if the condition is false */ return (first->get_const_val()->get_val_bool() ? second : third); } else { /* it's not a constant value - return a new conditional node */ return new CTPNIf(first, second, third); } } /* ------------------------------------------------------------------------ */ /* * Bmbedded string expressions. */ /* * Embedded string tree generator. This abstracts the tree construction * for an embedded string, so that the same parser can be used for single * and double quoted strings. */ struct CTcEmbedBuilder { /* is this a double-quoted string builder? */ virtual int is_double() const = 0; /* create the initial node for the leading fixed part of the string */ virtual CTcPrsNode *lead_node() = 0; /* * Finish the tree. If we skipped creating a tree because we had an * empty string in the lead position, and we haven't added anything to * it, this should create a non-null node to represent the final tree, * so we know it's not an error. */ virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) = 0; /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() = 0; /* * Add an embedding: create a node combining the tree before an * expression, an embedded expression, and the current token * representing the continuation of the string after the expression. */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) = 0; /* add a string segment (a "mid" or "end" segment) */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) = 0; /* string mid/end token types */ virtual tc_toktyp_t mid_tok() = 0; virtual tc_toktyp_t end_tok() = 0; }; /* * Embedded string tree generator for double-quoted strings. Double-quoted * strings with embeddings are converted to a series of comma expressions: * *. "one <> three <> five" *. -> "one ", say(two), " three ", say(four), " five"; * * Note that the embedded expressions are wrapped in "say()" calls, since * we have to make sure they generate output. Some embeddings will * generate output as a side effect rather than as a value result, but * that's fine; in that case they'll perform their side effect (i.e., * printing text) and then return nil, so the say() will add nothing to the * output stream. */ struct CTcEmbedBuilderDbl: CTcEmbedBuilder { /* we're the builder for double-quoted strings */ virtual int is_double() const { return TRUE; } /* string mid/end token types */ virtual tc_toktyp_t mid_tok() { return TOKT_DSTR_MID; } virtual tc_toktyp_t end_tok() { return TOKT_DSTR_END; } /* build the leading node */ virtual CTcPrsNode *lead_node() { /* * Create a node for the initial part of the string. This is just * an ordinary double-quoted string node. If the initial part of * the string is zero-length, don't create an initial node at all, * since this would just generate do-nothing code. */ if (G_tok->getcur()->get_text_len() != 0) { /* create the node for the initial part of the string */ return new CTPNDstr(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); } else { /* * the initial part of the string is empty, so we don't need a * node for this portion */ return 0; } } /* finish the tree */ virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) { return (cur != 0 ? cur : new CTPNDstr("", 0)); } /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() { /* * an expression embedded in a dstring can be any value expression, * or another dstring expression */ return G_prs->parse_expr_or_dstr(TRUE); } /* add another embedding */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) { /* wrap the expresion in a "say()" embedding node */ sub = new CTPNDstrEmbed(sub); /* * Build a node representing everything so far: do this by * combining the sub-expression with everything preceding, using a * comma operator. This isn't necessary if there's nothing * preceding the sub-expression, since this means the * sub-expression itself is everything so far. */ if (cur != 0) return new CTPNComma(cur, sub); else return sub; } /* add a string segment */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) { /* * Combine the part so far with the next string segment, using a * comma operator. If the next string segment is empty, there's no * need to add anything for it. */ size_t len = G_tok->getcur()->get_text_len(); if (len != 0) { /* create a node for the new string segment */ CTcPrsNode *newstr = new CTPNDstr( G_tok->getcur()->get_text(), len); /* combine it into the part so far with a comma operator */ cur = new CTPNComma(cur, newstr); } /* return the new combined node */ return cur; } }; /* * Embedded string tree generator for single-quoted strings. * * Single-quoted strings with embeddings are translated as follows: * *. local a = 'one <> three <> five'; *. -> local a = ('one ' + (two) + ' three ' + (four) + ' five'); */ struct CTcEmbedBuilderSgl: CTcEmbedBuilder { /* we're the builder for single-quoted strings */ virtual int is_double() const { return FALSE; } /* string mid/end token types */ virtual tc_toktyp_t mid_tok() { return TOKT_SSTR_MID; } virtual tc_toktyp_t end_tok() { return TOKT_SSTR_END; } /* build the leading node */ virtual CTcPrsNode *lead_node() { /* * Create a string constant node for the first part of the string. * Note that we need this even if the string is empty, to guarantee * that the overall expression is treated as a string. */ return string_node(G_tok->getcur()); } virtual CTcPrsNode *finish_tree(CTcPrsNode *cur) { return (cur != 0 ? cur : string_node(0)); } /* create a constant string node from the current token */ CTcPrsNode *string_node(const CTcToken *tok) { CTcConstVal cval; cval.set_sstr(tok); return new CTPNConst(&cval); } /* parse an embedded expression */ virtual CTcPrsNode *parse_expr() { /* in a single-quoted string, we need a value expression */ return G_prs->parse_expr(); } /* add another embedding */ virtual CTcPrsNode *add_embedding(CTcPrsNode *cur, CTcPrsNode *sub) { /* * Combine the sub-expression with the preceding portion using an * addition operator. This will be interpreted as concatenation at * run-time since the left side is definitively a string. */ return new CTPNAdd(cur, sub); } /* add a string segment */ virtual CTcPrsNode *add_segment(CTcPrsNode *cur) { /* * Add the next string segment. We can omit it if it's zero * length, as this will add nothing to the result. */ if (G_tok->getcur()->get_text_len() != 0) { CTcConstVal cval; cval.set_sstr(G_tok->getcur()); cur = new CTPNAdd(cur, new CTPNConst(&cval)); } /* return the new combined node */ return cur; } }; /* * Embedding stack entry. Each nesting level keeps one of these structures * to track the enclosing constructs. */ struct CTcEmbedLevel { /* set up the level, linking to our parent */ CTcEmbedLevel(int typ, CTcEmbedLevel *parent) : typ(typ), parent(parent) { } /* level type */ int typ; /* level types */ static const int Outer = 0; static const int If = 1; static const int OneOf = 2; static const int FirstTime = 3; /* parent level */ CTcEmbedLevel *parent; /* find a type among my parents */ int is_in(int typ) { /* if this is my type or a parent's type, we're in this type */ return (typ == this->typ || (parent != 0 && parent->is_in(typ))); } }; /* * Parse an embedded expression in a string. This handles embedding for * both single-quoted and double-quoted strings, using the tree builder * object to handle the differences. The two have the same parsing syntax, * but they do generate different parse trees. */ CTcPrsNode *CTcPrsOpUnary::parse_embedding(CTcEmbedBuilder *b) { /* keep going until we reach the end of the string */ for (;;) { /* parse a series of embeddings */ int eos; CTcEmbedLevel level(CTcEmbedLevel::Outer, 0); CTcPrsNode *n = parse_embedding_list(b, eos, &level); /* if we encountered an error or the end of the string, we're done */ if (n == 0 || eos) return n; /* * We stopped parsing without reaching the end of the string, so we * must have encountered a token that terminates a nested structure * - <>, <>, <>, etc. Capture the current * embedding so we can check which type of end token it is. */ const char *open_kw; parse_embedded_end_tok(b, &level, &open_kw); G_tok->log_error(TCERR_BAD_EMBED_END_TOK, (int)G_tok->getcur()->get_text_len(), G_tok->getcur()->get_text(), open_kw); } } /* * Parse a series of embeddings */ CTcPrsNode *CTcPrsOpUnary::parse_embedding_list( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* we haven't reached the end of the string yet */ eos = FALSE; /* create the node for the leading string */ tc_toktyp_t curtyp = G_tok->cur(); CTcPrsNode *cur = b->lead_node(); G_tok->next(); /* * If we're starting with the final segment of the string, there's no * need to parse any further. We can enter with the final segment when * parsing sub-constructs such as <>. */ if (curtyp == b->end_tok()) { /* tell the caller we're at the end of the string */ eos = TRUE; /* return the lead node */ return b->finish_tree(cur); } /* keep going until we find the end of the string */ for (;;) { /* note the current error count */ int nerr = G_tcmain->get_error_count(); /* * Capture tokens up to the next string segment, so that we can * compare the tokens to the embedding templates. */ CTcEmbedTokenList *tl = G_prs->embed_toks_; capture_embedded(b, tl); /* * First, check the special built-in syntax: * *. <> *. <> *. <> */ CTcPrsNode *sub = 0; const char *open_kw = 0; int check_end = TRUE; if (tl->match("if *")) { /* parse the "if" list */ tl->unget(1, 0); sub = parse_embedded_if(b, FALSE, eos, parent); } else if (tl->match("unless *")) { /* parse the "unless" (which is just an inverted "if") */ tl->unget(1); sub = parse_embedded_if(b, TRUE, eos, parent); } else if (tl->match("one of")) { /* parse the "one of" list */ sub = parse_embedded_oneof(b, eos, parent); } else if (tl->match("first time")) { /* parse the "first time" */ sub = parse_embedded_firsttime(b, eos, parent); } else if (parse_embedded_end_tok(tl, parent, &open_kw)) { /* * We're at a terminator for a multi-part embedding construct. * Stop here and return what we have, with the token left at * the terminator. The caller is presumably parsing this * construct recursively, so it'll take it from here. */ tl->unget(); return b->finish_tree(cur); } else if (tl->getcnt() == 0) { /* * empty embedding, or we got here via error recovery; proceed * with the next segment without any embedding */ sub = 0; } else { /* * No special syntax, so treat it as an expression. First, * check for a sprintf format spec - e.g., <<%2d x>> */ CTcToken fmttok(TOKT_INVALID); if (tl->get_head()->gettyp() == TOKT_FMTSPEC) tl->unlink_head(&fmttok); /* parse the embedded expression */ sub = parse_embedded_expr(b, tl); if (sub == 0) return 0; /* we can't have an end token immediately after an expression */ check_end = FALSE; /* * if we have a format spec, wrap the expression in a * sprintf node as sprintf(fmtspec, sub) */ if (fmttok.gettyp() == TOKT_FMTSPEC) { /* * set up the argument list - fmtspec string, sub (note * that arg lists are built in reverse order) */ CTcConstVal fmtcv; fmtcv.set_sstr(&fmttok); CTPNArg *args = new CTPNArg(sub); args->set_next_arg(new CTPNArg(new CTPNConst(&fmtcv))); /* set up the sprintf call */ sub = new CTPNCall(new CTPNSym("sprintf", 7), new CTPNArglist(2, args)); } } /* if we have an embedding, add it */ if (sub != 0) cur = b->add_embedding(cur, sub); /* * after the expression, we must find either another string segment * with another embedded expression following, or the final string * segment; anything else is an error */ if (eos) { /* * We reached the end of the string in a sub-construct; there's * nothing more to add to the main string. Just return what we * have to the caller. */ return b->finish_tree(cur); } else if ((curtyp = G_tok->cur()) == b->mid_tok()) { /* * It's a string with yet another embedded expression. Add * this segment to the expression and proceed to the next * embedding. */ cur = b->add_segment(cur); G_tok->next(); } else if (curtyp == b->end_tok()) { /* * It's the last segment of the string. Add the final segment * to the string. */ cur = b->add_segment(cur); G_tok->next(); /* tell the caller we've reached the end, and return our tree */ eos = TRUE; return b->finish_tree(cur); } else if (check_end && parse_embedded_end_tok(b, parent, &open_kw)) { /* * We parsed a recursive list for <>, <>, etc. End * tokens are implied when missing, so an end token at one * level can implicitly end multiple levels. We stopped at * such a token without consuming it, so it's for our caller to * digest. Simply return what we have and let the caller take * it from here. */ return b->finish_tree(cur); } else if (G_tcmain->get_error_count() != nerr) { /* * We logged an error within a sub-expression, so it's not * surprising that we're not at a valid continuation token at * this point. Try syncing up with the source: look for a * continuation token or something that probably ends the * statement, such as a semicolon or right brace. */ for (;;) { /* if we're at a statement ender, stop here */ if (curtyp == TOKT_SEM || curtyp == TOKT_RBRACE) return b->finish_tree(cur); /* if at eof, return failure */ if (curtyp == TOKT_EOF) return 0; /* if we're at a continuation token, resume parsing */ if (curtyp == b->mid_tok() || curtyp == b->end_tok()) break; /* skip this token and keep looking for a sync point */ curtyp = G_tok->next(); } } else { /* anything else is invalid - log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_STR_CONT); /* * Skip ahead until we find the next string segment or * something that looks like the end of the statement. */ tl->reset(); capture_embedded(b, tl); /* * If we stopped at the next segment, carry on. Otherwise, * assume they simply left off the end of the string and return * what we have. */ if (G_tok->cur() != b->mid_tok() && G_tok->cur() != b->end_tok()) return (cur != 0 ? cur : b->finish_tree(sub)); } } } /* * Parse a single embedded expression */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_expr( CTcEmbedBuilder *b, CTcEmbedTokenList *tl) { /* search for a template for the expression */ for (CTcStrTemplate *st = G_prs->get_str_template_head() ; st != 0 ; st = st->nxt) { /* check for a match */ if (tl->match(st)) { /* * This template matches. Generate the code as a call to the * template's processor function. */ CTPNSymResolved *func = new CTPNSymResolved(st->func); /* * If the template has a '*', the tokens matching the star are * a sub-expression that we evaluate as the argument to the * function. Otherwise we call it with no arguments. */ CTPNArglist *arglist; if (st->star) { /* * If the template has any fixed tokens, reparse the * remaining tokens to see if they refer to a new template. * If the template was just '*', skip this, since we'd just * find the same match again. */ CTcPrsNode *sub; if (st->cnt > 1) { /* remove the tokens that matched the fixed part */ tl->reduce(st); /* reparse as a new embedded expression */ sub = parse_embedded_expr(b, tl); } else { /* the template is just '*', so parse a raw expression */ tl->unget(); sub = b->parse_expr(); } /* if we failed to parse a sub-expression, return failure */ if (sub == 0) return 0; /* create the argument list */ arglist = new CTPNArglist(1, new CTPNArg(sub)); } else { /* there's no argument list */ arglist = new CTPNArglist(0, 0); } /* create and return the call to the template function */ return new CTPNCall(func, arglist); } } /* * There's no template, so process it as an ordinary expression. Put * the captured token list back into the token stream, and parse an * expression node. */ tl->unget(); return b->parse_expr(); } /* * Capture an embedded expression to an embedded token list */ void CTcPrsOpUnary::capture_embedded(CTcEmbedBuilder *b, CTcEmbedTokenList *tl) { /* reset the list */ tl->reset(); /* we don't have any nested braces or parens yet */ int braces = 0, parens = 0, bracks = 0; /* run through the list */ for (;;) { /* check what we have */ switch (G_tok->cur()) { case TOKT_LBRACE: ++braces; break; case TOKT_RBRACE: /* * if we find an unbalanced close brace, assume that we've * reached the end of the statement without properly * terminating the string */ if (--braces < 0) return; break; case TOKT_LPAR: ++parens; break; case TOKT_RPAR: --parens; break; case TOKT_LBRACK: ++bracks; break; case TOKT_RBRACK: --bracks; break; case TOKT_SEM: /* * if we find a semicolon outside of braces, assume that we've * reached the end of the statement without properly * terminating the string */ if (braces == 0) return; break; case TOKT_EOF: return; default: /* stop if we've reached the next string segment */ if (G_tok->cur() == b->end_tok() || G_tok->cur() == b->mid_tok()) return; break; } /* add the token to the capture list and move on to the next */ tl->add_tok(G_tok->getcur()); G_tok->next(); } } /* * Parse an embedded "if" or "unless" construct. The two are the same, * except the "unless" inverts the condition. */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_if( CTcEmbedBuilder *b, int unless, int &eos, CTcEmbedLevel *parent) { /* set up our embedding stack level */ CTcEmbedLevel level(CTcEmbedLevel::If, parent); /* parse the condition expression */ CTcPrsNode *cond = G_prs->parse_cond_expr(); if (cond == 0) return 0; /* if this is an "unless", invert the condition */ if (unless) cond = new CTPNNot(cond); /* parse the "then" list */ CTcPrsNode *then = parse_embedding_list(b, eos, &level); if (then == 0) return 0; /* * create our top-level 'if' node - leave the 'else' branch empty for * now; we'll build this out if we find an 'else' */ CTPNIf *top = new CTPNIf(cond, then, 0); /* * as we build out the 'else if' branches, we'll add elses to the tail * of the tree; this is currently the top node */ CTPNIf *tail = top; /* parse zero or more "else if" branches */ int found_else = FALSE; while (!eos && (G_tok->cur() == TOKT_ELSE || G_tok->cur_tok_matches("otherwise"))) { /* skip the "else"/"otherwise", and check for another "if" */ if (G_tok->next() == TOKT_IF || G_tok->cur_tok_matches("unless")) { /* * <> or <>. Note which sense * of the test we're using. */ unless = (G_tok->cur() != TOKT_IF); /* skip the "else"/"otherwise" and parse the condition */ G_tok->next(); cond = G_prs->parse_cond_expr(); if (cond == 0) return 0; /* if it's 'unless', invert the condition */ if (unless) cond = new CTPNNot(cond); /* parse the "then" list for this new branch */ then = parse_embedding_list(b, eos, &level); if (then == 0) return 0; /* add the new 'if' node to the 'else' of the tail of the tree */ CTPNIf *sub = new CTPNIf(cond, then, 0); tail->set_else(sub); /* this is the new tail, for adding the next 'else' */ tail = sub; } else { /* it's the final else/otherwise - parse the branch */ CTcPrsNode *sub = parse_embedding_list(b, eos, &level); if (sub == 0) return 0; /* add it as the else branch of the tail node of the tree */ tail->set_else(sub); /* no more clauses of the "if" can follow an "else" */ found_else = TRUE; break; } } /* * if we ended without an "else", add an implicit "nil" as the final * "else" branch */ if (!found_else) tail->set_else(b->finish_tree(0)); /* * If we're at an "end" token, this is the explicit close of the "if". * Otherwise, the "if" ended implicitly at the end of the string or at * a closing token for a containing structure. */ if (!eos && G_tok->cur_tok_matches("end")) G_tok->next(); /* return the condition tree */ return top; } /* * "one of" enders */ struct one_of_option { /* ending phrase */ const char *endph; /* list attribute string */ const char *attr; }; static one_of_option one_of_list[] = { { "purely at random", "rand" }, { "then purely at random", "seq,rand" }, { "at random" , "rand-norpt" }, { "then at random", "seq,rand-norpt" }, { "sticky random", "rand,stop" }, { "as decreasingly likely outcomes", "rand-wt" }, { "shuffled", "shuffle" }, { "then shuffled", "seq,shuffle" }, { "half shuffled", "shuffle2" }, { "then half shuffled", "seq,shuffle2" }, { "cycling", "seq" }, { "stopping", "seq,stop" } }; /* * Parse an embedded "one of" construct */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_oneof( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* get the embedding token list capturer */ CTcEmbedTokenList *tl = G_prs->embed_toks_; /* set up our stack level */ CTcEmbedLevel level(CTcEmbedLevel::OneOf, parent); /* create a list node to hold the items */ CTPNList *lst = new CTPNList(); /* keep going until we get to the end of the list */ for (;;) { /* parse the next string or embedded expression */ CTcPrsNode *ele = parse_embedding_list(b, eos, &level); if (ele == 0) return 0; /* add this element to the list */ lst->add_element(ele); /* capture the ending token list */ tl->reset(); capture_embedded(b, tl); /* if we're not at an OR, this is the end of the list */ if (!tl->match("or") && !tl->match("||")) break; } /* * Search for a match to one of our endings. If we don't find one, use * "purely at random" as the default. */ const char *attrs = 0; for (size_t i = 0 ; i < countof(one_of_list) ; ++i) { /* check for a match */ if (tl->match(one_of_list[i].endph)) { /* got it - set this attribute and stop searching */ attrs = one_of_list[i].attr; break; } } /* * if we didn't find a match for the ending tokens, we must have the * ending for an enclosing structure; put the tokens back for the * enclosing structure to parse, and use "purely at random" as the * default */ if (attrs == 0) { tl->unget(); attrs = "rand"; } /* create the one-of list */ return create_oneof_node(b, lst, attrs); } /* * Create a one-of list node */ CTcPrsNode *CTcPrsOpUnary::create_oneof_node( CTcEmbedBuilder *b, CTPNList *lst, const char *attrs) { /* * If we're actually compiling (not just doing a symbol extraction * pass), create our list state object. This is an anonymous * TadsObject of class OneOfIndexGen, with the following properties: * *. numItems = number of items in the list (integer) *. listAttrs = our 'attrs' list attributes value (string) */ CTcSymObj *obj = 0; if (!G_prs->get_syntax_only()) { /* geneate our anonymous OneOfIndexGen instance */ obj = G_prs->add_gen_obj("OneOfIndexGen"); if (obj != 0) { G_prs->add_gen_obj_prop(obj, "numItems", lst->get_count()); G_prs->add_gen_obj_prop(obj, "listAttrs", attrs); } else { /* check to see if OneOfIndexGen is undefined */ CTcSymbol *cls = G_prs->get_global_symtab()->find( "OneOfIndexGen", 13); if (cls == 0 || cls->get_type() != TC_SYM_OBJ) G_tok->log_error(TCERR_ONEOF_REQ_GENCLS); } } /* return the <> node */ return new CTPNStrOneOf(b->is_double(), lst, obj); } /* * Parse an embedded "first time" structure */ CTcPrsNode *CTcPrsOpUnary::parse_embedded_firsttime( CTcEmbedBuilder *b, int &eos, CTcEmbedLevel *parent) { /* get the embedding token list capturer */ CTcEmbedTokenList *tl = G_prs->embed_toks_; /* set up our stack level */ CTcEmbedLevel level(CTcEmbedLevel::FirstTime, parent); /* parse the embedding */ CTcPrsNode *ele = parse_embedding_list(b, eos, &level); if (ele == 0) return 0; /* capture the ending token list */ tl->reset(); capture_embedded(b, tl); /* * check for our "only" list - if we don't match it, put the tokens * back, since they must be the ending list for an enclosing structure */ if (!tl->match("only")) tl->unget(); /* create this as equivalent to <>our item<><> */ CTPNList *lst = new CTPNList(); lst->add_element(ele); CTcConstVal cv; cv.set_sstr("", 0); lst->add_element(new CTPNConst(&cv)); /* create the <> node */ return create_oneof_node(b, lst, "seq,stop"); } /* * Check for an end token in an embedded expression construct. Reads from * the token stream, but restores the tokens when done. */ int CTcPrsOpUnary::parse_embedded_end_tok(CTcEmbedBuilder *b, CTcEmbedLevel *parent, const char **open_kw) { /* capture the current expression's token list */ CTcEmbedTokenList *tl = G_prs->embed_toks_; tl->reset(); capture_embedded(b, tl); /* parse the end token */ int ret = parse_embedded_end_tok(tl, parent, open_kw); /* un-get the captured tokens */ tl->unget(); /* return the result */ return ret; } /* * Check for an end token in an embedded expression construct. Compares * tokens in the given capture list. */ int CTcPrsOpUnary::parse_embedded_end_tok(CTcEmbedTokenList *tl, CTcEmbedLevel *parent, const char **open_kw) { /* presume we won't find a closing keyword */ *open_kw = "unknown"; /* * consider ending keywords to always be significant, regardless of * context, since these are unambiguous with valid expressions */ if (tl->match("else") || tl->match("else if *") || tl->match("else unless *")) { *open_kw = "<> or <>"; return TRUE; } /* check for "if" ending tokens - else, end */ if (tl->match("end") || tl->match("otherwise") || tl->match("otherwise if *") || tl->match("otherwise unless *")) { *open_kw = "<> or <>"; if (parent->is_in(CTcEmbedLevel::If)) return TRUE; } /* check for an "or", which delimits branches of a "one of" */ if (tl->match("or") || tl->match("||")) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::OneOf)) return TRUE; } /* check for "one of" ending tokens */ for (size_t i = 0 ; i < countof(one_of_list) ; ++i) { if (tl->match(one_of_list[i].endph)) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::OneOf)) return TRUE; break; } } /* check for an "only", which ends a "first time" */ if (tl->match("only")) { *open_kw = "<>"; if (parent->is_in(CTcEmbedLevel::FirstTime)) return TRUE; } /* didn't find a close token */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * Embedded <> list in a string */ CTcPrsNode *CTPNStrOneOfBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { lst_ = (CTPNList *)lst_->adjust_for_dyn(info); return this; } CTcPrsNode *CTPNStrOneOfBase::fold_constants(CTcPrsSymtab *symtab) { lst_ = (CTPNList *)lst_->fold_constants(symtab); return this; } /* ------------------------------------------------------------------------ */ /* * Unary Operator Parser */ CTcPrsNode *CTcPrsOpUnary::parse() const { /* get the current token, which may be a prefix operator */ tc_toktyp_t op = G_tok->cur(); /* check for prefix operators */ switch(op) { case TOKT_AND: /* skip the '&' */ G_tok->next(); /* parse the address expression */ return parse_addr(); case TOKT_NOT: case TOKT_BNOT: case TOKT_PLUS: case TOKT_MINUS: case TOKT_INC: case TOKT_DEC: case TOKT_DELETE: { /* skip the operator */ G_tok->next(); /* * recursively parse the unary expression to which to apply the * operator */ CTcPrsNode *sub = parse(); if (sub == 0) return 0; /* apply the operator */ switch(op) { case TOKT_NOT: /* apply the NOT operator */ return parse_not(sub); case TOKT_BNOT: /* apply the bitwise NOT operator */ return parse_bnot(sub); case TOKT_PLUS: /* apply the unary positive operator */ return parse_pos(sub); case TOKT_MINUS: /* apply the unary negation operator */ return parse_neg(sub); case TOKT_INC: /* apply the pre-increment operator */ return parse_inc(TRUE, sub); case TOKT_DEC: /* apply the pre-decrement operator */ return parse_dec(TRUE, sub); case TOKT_DELETE: /* apply the deletion operator */ return parse_delete(sub); default: /* * we shouldn't ever get here, since this switch should * contain every case that brought us into it in the first * place */ return 0; } } default: /* it's not a unary prefix operator - parse a postfix expression */ return parse_postfix(TRUE, TRUE); } } /* * parse a unary NOT expression */ CTcPrsNode *CTcPrsOpUnary::parse_not(CTcPrsNode *subexpr) { /* try folding a constant value */ CTcPrsNode *ret = eval_const_not(subexpr); /* * if we got a constant result, return it; otherwise, create a NOT * node for code generation */ if (ret != 0) return ret; else return new CTPNNot(subexpr); } /* * evaluate a constant NOT expression */ CTcPrsNode *CTcPrsOpUnary::eval_const_not(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* set the new value */ subexpr->get_const_val() ->set_bool(!subexpr->get_const_val()->get_val_bool()); /* return the modified constant value */ return subexpr; } /* the result is not constant */ return 0; } /* * parse a unary bitwise NOT expression */ CTcPrsNode *CTcPrsOpUnary::parse_bnot(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* we need an integer - log an error if it's not */ if (subexpr->get_const_val()->get_type() != TC_CVT_INT) G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_BNOT)); else subexpr->get_const_val() ->set_int(~subexpr->get_const_val()->get_val_int()); /* return the updated value */ return subexpr; } /* create the bitwise NOT node */ return new CTPNBNot(subexpr); } /* * parse a unary address expression */ CTcPrsNode *CTcPrsOpUnary::parse_addr() const { /* * if it's a simple symbol, create an unresolved symbol node for it; * otherwise parse the entire expression */ CTcPrsNode *subexpr; if (G_tok->cur() == TOKT_SYM) { /* * create an unresolved symbol node - we'll resolve this during * code generation */ const CTcToken *tok = G_tok->getcur(); subexpr = new CTPNSym(tok->get_text(), tok->get_text_len()); /* * If the symbol is undefined, assume it's a property, but make the * definition "weak". Starting in 3.1, &x can be a property ID, a * function pointer, or a built-in function pointer, so it's no * longer safe to just assume it's a property. If we don't see the * symbol used in any other context, we'll assume it's a property, * but mark it as "weak" if it's not already defined. This will * allow any conflicting definition to override the property * assumption without complaint. */ G_prs->get_global_symtab()->find_or_def_prop_weak( tok->get_text(), tok->get_text_len(), FALSE); /* skip the symbol */ G_tok->next(); } else if (G_tok->cur() == TOKT_OPERATOR) { /* operator property - parse the operator name */ CTcToken proptok; if (G_prs->parse_op_name(&proptok)) { /* skip the last token of the operator name */ G_tok->next(); } /* create a symbol for it */ subexpr = new CTPNSym(proptok.get_text(), proptok.get_text_len()); } else { /* parse an expression */ subexpr = parse(); if (subexpr == 0) return 0; } /* * The underlying expression must be something that has an address; * if it's not, it's an error. */ if (!subexpr->has_addr()) { /* * can't take the address of the subexpression - log an error, * but continue parsing the expression anyway */ G_tok->log_error(TCERR_NO_ADDRESS); } /* create the address node */ return new CTPNAddr(subexpr); } /* * parse a unary arithmetic positive expression */ CTcPrsNode *CTcPrsOpUnary::parse_pos(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* if it's a float, a unary '+' has no effect at all */ if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) return subexpr; /* we need an integer - log an error if it's not */ if (subexpr->get_const_val()->get_type() != TC_CVT_INT) G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_PLUS)); /* * positive-ing a value doesn't change the value, so return the * original constant */ return subexpr; } /* create the unary positive node */ return new CTPNPos(subexpr); } /* * parse a unary arithmetic negation expression */ CTcPrsNode *CTcPrsOpUnary::parse_neg(CTcPrsNode *subexpr) { /* * if the underlying expression is a constant value, apply the * operator */ if (subexpr->is_const()) { /* we need an integer or float */ CTcConstVal *cval = subexpr->get_const_val(); if (cval->get_type() == TC_CVT_INT) { /* * Negating an integer. There's a special overflow case to * check for: if we're negating INT32MINVAL, the result doesn't * fit in a signed integer, so we need to promote it to float. */ long l = cval->get_val_int(); if (l <= INT32MINVAL) { /* overflow - promote to BigNumber */ vbignum_t b((ulong)2147483648U); cval->set_float(&b, TRUE); } else { /* set the value negative in the subexpression */ cval->set_int(-l); } } else if (subexpr->get_const_val()->get_type() == TC_CVT_FLOAT) { /* get the original value */ const char *ctxt = cval->get_val_float(); size_t clen = cval->get_val_float_len(); /* if the old value is negative, simply remove the sign */ if (clen > 0 && ctxt[0] == '-') { /* keep the old value, removing the sign */ cval->set_float(ctxt + 1, clen - 1, cval->is_promoted()); } else { /* allocate new buffer for the float text plus a '-' */ char *new_txt = (char *)G_prsmem->alloc(clen + 1); /* insert the minus sign */ new_txt[0] = '-'; /* add the original string */ memcpy(new_txt + 1, ctxt, clen); /* update the subexpression's constant value to the new text */ cval->set_float(new_txt, clen + 1, cval->is_promoted()); } /* check for possible demotion back to int */ cval->demote_float(); } else { /* log the error */ G_tok->log_error(TCERR_CONST_UNARY_REQ_NUM, G_tok->get_op_text(TOKT_MINUS)); } /* return the modified constant value */ return subexpr; } /* create the unary negation node */ return new CTPNNeg(subexpr); } /* * parse a pre-increment expression */ CTcPrsNode *CTcPrsOpUnary::parse_inc(int pre, CTcPrsNode *subexpr) { /* require an lvalue */ if (!subexpr->check_lvalue()) { /* log an error, but continue parsing */ G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, G_tok->get_op_text(TOKT_INC)); } /* apply the increment operator */ if (pre) return new CTPNPreInc(subexpr); else return new CTPNPostInc(subexpr); } /* * parse a pre-decrement expression */ CTcPrsNode *CTcPrsOpUnary::parse_dec(int pre, CTcPrsNode *subexpr) { /* require an lvalue */ if (!subexpr->check_lvalue()) { /* log an error, but continue parsing */ G_tok->log_error(TCERR_INVALID_UNARY_LVALUE, G_tok->get_op_text(TOKT_DEC)); } /* apply the pre-increment operator */ if (pre) return new CTPNPreDec(subexpr); else return new CTPNPostDec(subexpr); } /* * parse a unary allocation expression */ CTcPrsNode *CTcPrsOpUnary::parse_new(CTcPrsNode *subexpr, int is_transient) { /* create the allocation node */ return new CTPNNew(subexpr, is_transient); } /* * parse a unary deletion expression */ CTcPrsNode *CTcPrsOpUnary::parse_delete(CTcPrsNode *subexpr) { /* the delete operator is obsolete in TADS 3 - warn about it */ if (!G_prs->get_syntax_only()) G_tok->log_warning(TCERR_DELETE_OBSOLETE); /* create the deletion node */ return new CTPNDelete(subexpr); } /* * parse a postfix expression */ CTcPrsNode *CTcPrsOpUnary::parse_postfix(int allow_member_expr, int allow_call_expr) { /* parse a primary expression */ CTcPrsNode *sub = parse_primary(); if (sub == 0) return 0; /* keep going as long as we find postfix operators */ for (;;) { /* check for a postfix operator */ tc_toktyp_t op = G_tok->cur(); switch(op) { case TOKT_LPAR: /* left paren - function or method call */ if (allow_call_expr) { /* parse the function call expression */ sub = parse_call(sub); } else { /* call expressions aren't allowed - stop here */ return sub; } break; case TOKT_LBRACK: /* left square bracket - subscript */ sub = parse_subscript(sub); break; case TOKT_DOT: /* * Dot - member selection. If a member expression is allowed * by the caller, parse it; otherwise, just return the * expression up to this point. */ if (allow_member_expr) { /* * it's allowed - parse it and continue to look for other * postfix expressions following the member expression */ sub = parse_member(sub); } else { /* * member expressions aren't allowed - stop here, * returning the expression up to this point */ return sub; } break; case TOKT_INC: /* post-increment */ G_tok->next(); sub = parse_inc(FALSE, sub); break; case TOKT_DEC: /* post-decrement */ G_tok->next(); sub = parse_dec(FALSE, sub); break; default: /* it's not a postfix operator - return the result */ return sub; } /* if the last parse failed, return failure */ if (sub == 0) return 0; } } /* * Parse an argument list */ CTPNArglist *CTcPrsOpUnary::parse_arg_list() { /* skip the open paren */ G_tok->next(); /* keep going until we find the close paren */ int argc; CTPNArg *arg_head; for (argc = 0, arg_head = 0 ;; ) { /* presume it's not a named argument */ CTcToken param_name; param_name.settyp(TOKT_INVALID); /* if this is the close paren, we're done */ if (G_tok->cur() == TOKT_RPAR) break; /* check for a named argument: "symbol: expression" */ if (G_tok->cur() == TOKT_SYM) { /* remember the symbol, in case it is in fact a named argument */ param_name = *G_tok->getcur(); /* peek ahead - if a colon follows, it's a named argument */ if (G_tok->next() == TOKT_COLON) { /* * it's a named argument - we already have the name stashed * away for when we create the argument node, so simply * skip the colon so we can parse the argument value * expression */ G_tok->next(); } else { /* * No colon - it's a regular parameter expression. Put * back the token we peeked at, clear out the name token, * and keep parsing as normal. */ G_tok->unget(); param_name.settyp(TOKT_INVALID); } } /* * parse this argument expression - this is an 'assignment' * expression, since a comma is an argument separator in this * context rather than an operator */ CTcPrsNode *expr = S_op_asi.parse(); if (expr == 0) return 0; /* if this is a positional argument (not named), count it */ if (param_name.gettyp() != TOKT_SYM) ++argc; /* create a new argument node */ CTPNArg *arg_cur = new CTPNArg(expr); /* if it's a named argument, store the name in the argument node */ arg_cur->set_name(¶m_name); /* check to see if the argument is followed by an ellipsis */ if (G_tok->cur() == TOKT_ELLIPSIS) { /* this isn't allowed for a named argument */ if (param_name.gettyp() == TOKT_SYM) G_tok->log_error(TCERR_NAMED_ARG_NO_ELLIPSIS); /* skip the ellipsis */ G_tok->next(); /* mark the argument as a list-to-varargs parameter */ arg_cur->set_varargs(TRUE); } /* * Link the new node in at the beginning of our list - this will * ensure that the list is built in reverse order, which is the * order in which we push the arguments onto the stack. */ arg_cur->set_next_arg(arg_head); arg_head = arg_cur; /* we need to be looking at a comma, right paren, or ellipsis */ if (G_tok->cur() == TOKT_RPAR) { /* that's the end of the list */ break; } else if (G_tok->cur() == TOKT_COMMA) { /* skip the comma and parse the next argument */ G_tok->next(); } else { /* * If we're at the end of the file, there's no point * proceding, so return failure. If we've reached something * that looks like a statement separator (semicolon, curly * brace), also return failure, since the problem is clearly * a missing right paren. Otherwise, assume that a comma * was missing and continue as though we have another * argument. */ switch(G_tok->cur()) { default: /* log an error */ G_tok->log_error_curtok(TCERR_EXPECTED_ARG_COMMA); /* * if we're at the end of file, return what we have so * far; otherwise continue, assuming that they merely * left out a comma between two argument expressions */ if (G_tok->cur() == TOKT_EOF) return new CTPNArglist(argc, arg_head); break; case TOKT_SEM: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * we're apparently at the end of the statement; flag * the error as a missing right paren, and return what * we have so far */ G_tok->log_error_curtok(TCERR_EXPECTED_ARG_RPAR); return new CTPNArglist(argc, arg_head); } } } /* skip the closing paren */ G_tok->next(); /* create and return the argument list descriptor */ return new CTPNArglist(argc, arg_head); } /* * Parse a function call expression */ CTcPrsNode *CTcPrsOpUnary::parse_call(CTcPrsNode *lhs) { /* parse the argument list */ CTPNArglist *arglist = parse_arg_list(); if (arglist == 0) return 0; /* check for the special "defined()" syntax */ if (lhs != 0 && lhs->sym_text_matches("defined", 7)) { /* make sure we have one argument that's a symbol */ CTcPrsNode *arg; if (arglist->get_argc() == 1 && (arg = arglist->get_arg_list_head()->get_arg_expr()) ->get_sym_text() != 0) { /* look up the symbol */ CTcSymbol *sym = G_prs->get_global_symtab()->find( arg->get_sym_text(), arg->get_sym_text_len()); /* * The result is a constant 'true' or 'nil' node, depending on * whether the symbol is defined. Note that this is a "compile * time constant" expression, not a true constant - flag it as * such so that we don't generate a warning if this value is * used as the conditional expression of an if, while, or for. */ CTcConstVal cval; cval.set_bool(sym != 0 && sym->get_type() != TC_SYM_UNKNOWN); cval.set_ctc(TRUE); return new CTPNConst(&cval); } else { /* invalid syntax */ G_tok->log_error(TCERR_DEFINED_SYNTAX); } } /* check for the special "__objref" syntax */ if (lhs != 0 && lhs->sym_text_matches("__objref", 8)) { /* assume we won't generate an error or warning if it's undefined */ int errhandling = 0; /* get the rightmost argument */ CTPNArg *curarg = arglist->get_arg_list_head(); /* if we have other than two arguments, it's an error */ if (arglist->get_argc() > 2) { G_tok->log_error(TCERR___OBJREF_SYNTAX); return lhs; } /* if we have two arguments, get the warning/error mode */ if (arglist->get_argc() == 2) { /* get the argument, and skip to the next to the left */ CTcPrsNode *arg = curarg->get_arg_expr(); curarg = curarg->get_next_arg(); /* it has to be "warn" or "error" */ if (arg->sym_text_matches("warn", 4)) { errhandling = 1; } else if (arg->sym_text_matches("error", 5)) { errhandling = 2; } else { G_tok->log_error(TCERR___OBJREF_SYNTAX); return lhs; } } /* the first argument must be a symbol */ CTcPrsNode *arg = curarg->get_arg_expr(); if (arg->get_sym_text() != 0) { /* look up the symbol */ CTcSymbol *sym = G_prs->get_global_symtab()->find( arg->get_sym_text(), arg->get_sym_text_len()); /* * The result is the object reference value if the symbol is * defined as an object, or nil if it's undefined or something * other than an object. */ CTcConstVal cval; if (sym != 0 && sym->get_type() == TC_SYM_OBJ) { /* it's an object - the result is the object symbol */ return arg; } else { /* log a warning or error, if applicable */ if (errhandling != 0 && !G_prs->get_syntax_only()) { /* note whether it's undefined or otherwise defined */ int errcode = (sym == 0 ? TCERR_UNDEF_SYM : TCERR_SYM_NOT_OBJ); /* log an error or warning, as desired */ if (errhandling == 1) G_tok->log_warning( errcode, (int)arg->get_sym_text_len(), arg->get_sym_text()); else G_tok->log_error( errcode, (int)arg->get_sym_text_len(), arg->get_sym_text()); } /* not defined or non-object - the result is nil */ cval.set_bool(FALSE); cval.set_ctc(TRUE); return new CTPNConst(&cval); } } else { /* invalid syntax */ G_tok->log_error(TCERR___OBJREF_SYNTAX); } } /* build and return the function call node */ return new CTPNCall(lhs, arglist); } /* * Parse a subscript expression */ CTcPrsNode *CTcPrsOpUnary::parse_subscript(CTcPrsNode *lhs) { /* skip the '[' */ G_tok->next(); /* parse the expression within the brackets */ CTcPrsNode *subscript = S_op_comma.parse(); if (subscript == 0) return 0; /* check for the matching ']' */ if (G_tok->cur() == TOKT_RBRACK) { /* got it - skip it */ G_tok->next(); } else { /* log an error, and forgive the missing ']' */ G_tok->log_error_curtok(TCERR_EXPECTED_SUB_RBRACK); } /* try folding constants */ CTcPrsNode *cval = eval_const_subscript(lhs, subscript); /* * if that worked, use the result; otherwise, build an expression * node to generate code for the subscript operator */ if (cval != 0) return cval; else return new CTPNSubscript(lhs, subscript); } /* * Evaluate a constant subscript value */ CTcPrsNode *CTcPrsOpUnary::eval_const_subscript( CTcPrsNode *lhs, CTcPrsNode *subscript) { /* assume we won't be able to evaluate this as a constant */ CTcPrsNode *c = 0; /* * if we're subscripting a constant list by a constant index value, * we can evaluate a constant result */ if (lhs->is_const() && subscript->is_const()) { /* check the type of value we're indexing */ switch (lhs->get_const_val()->get_type()) { case TC_CVT_LIST: /* * It's a constant list. Lists can only be indexed by integer * values. */ if (subscript->get_const_val()->get_type() == TC_CVT_INT) { /* * it's an integer - index the constant list by the * constant subscript to get the element value, which * replaces the entire list-and-index expression */ c = lhs->get_const_val()->get_val_list()->get_const_ele( subscript->get_const_val()->get_val_int()); } else { /* a list index must be an integer */ G_tok->log_error(TCERR_CONST_IDX_NOT_INT); } break; case TC_CVT_SSTR: case TC_CVT_OBJ: case TC_CVT_FUNCPTR: case TC_CVT_ANONFUNCPTR: case TC_CVT_FLOAT: /* * these types don't define indexing as a native operator, but * it's possible for this to be meaningful at run-time via * operator overloading; simply leave the constant expression * unevaluated so that we generate code to perform the index * operation at run-time */ break; default: /* other types definitely cannot be indexed */ G_tok->log_error(TCERR_CONST_IDX_INV_TYPE); break; } } /* return the constant result, if any */ return c; } /* * Parse a member selection ('.') expression. If no '.' is actually * present, then '.targetprop' is implied. */ CTcPrsNode *CTcPrsOpUnary::parse_member(CTcPrsNode *lhs) { CTcPrsNode *rhs; int rhs_is_expr; /* * If a '.' is present, skip it; otherwise, '.targetprop' is implied. */ if (G_tok->cur() == TOKT_DOT) { /* we have an explicit property expression - skip the '.' */ G_tok->next(); /* assume the property will be a simple symbol, not an expression */ rhs_is_expr = FALSE; /* we could have a symbol or a parenthesized expression */ switch(G_tok->cur()) { case TOKT_SYM: /* * It's a simple property name - create a symbol node. Note * that we must explicitly create an unresolved symbol node, * since we want to ignore any local variable with the same * name and look only in the global symbol table for a * property; we must hence defer resolving the symbol until * code generation. */ rhs = new CTPNSym(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol token */ G_tok->next(); /* proceed to check for an argument list */ break; case TOKT_LPAR: /* * It's a parenthesized expression - parse it. First, skip * the open paren - we don't want the sub-expression to go * beyond the close paren (if we didn't skip the open paren, * the open paren would be part of the sub-expression, hence * any postfix expression after the close paren would be * considered part of the sub-expression; this would be * invalid, since we might want to find a postfix actual * parameter list). */ G_tok->next(); /* remember that it's an expression */ rhs_is_expr = TRUE; /* parse the sub-expression */ rhs = S_op_comma.parse(); if (rhs == 0) return 0; /* require the close paren */ if (G_tok->cur() == TOKT_RPAR) { /* skip the closing paren */ G_tok->next(); } else { /* log the error */ G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); /* * if we're at a semicolon or end of file, we must be on * to the next statement - stop trying to parse this one * if so; otherwise, continue on the assumption that they * merely left out the close paren and what follows is * more expression for us to process */ if (G_tok->cur() == TOKT_SEM || G_tok->cur() == TOKT_EOF) return lhs; } break; case TOKT_TARGETPROP: /* * it's an unparenthesized "targetprop" expression - skip the * keyword */ G_tok->next(); /* * the property value is the result of evaluating * "targetprop", which is an expression */ rhs = new CTPNTargetprop(); rhs_is_expr = TRUE; /* note the reference to the extended method context */ G_prs->set_full_method_ctx_referenced(TRUE); /* go parse the rest */ break; default: /* anything else is invalid - log an error */ G_tok->log_error_curtok(TCERR_INVALID_PROP_EXPR); /* skip the errant token so we don't loop on it */ G_tok->next(); /* return what we have so far */ return lhs; } } else { /* there's no property specified, so '.targetprop' is implied */ rhs = new CTPNTargetprop(); rhs_is_expr = TRUE; /* * note the reference to the full method context (since * 'targetprop' is part of the extended method context beyond * 'self') */ G_prs->set_full_method_ctx_referenced(TRUE); } /* check for an argument list */ if (G_tok->cur() == TOKT_LPAR) { CTPNArglist *arglist; /* parse the argument list */ arglist = parse_arg_list(); if (arglist == 0) return 0; /* create and return a member-with-arguments node */ return new CTPNMemArg(lhs, rhs, rhs_is_expr, arglist); } else { /* * there's no argument list - create and return a simple member * node */ return new CTPNMember(lhs, rhs, rhs_is_expr); } } /* * Parse a primary expression */ CTcPrsNode *CTcPrsOpUnary::parse_primary() { CTcPrsNode *sub; CTcConstVal cval; tc_toktyp_t t; CTcToken tok, tok2; /* keep going until we find something interesting */ for (;;) { /* determine what we have */ switch(G_tok->cur()) { case TOKT_LBRACE: /* short form of anonymous function */ return parse_anon_func(TRUE, FALSE); case TOKT_METHOD: /* parse an anonymous method */ return parse_anon_func(FALSE, TRUE); case TOKT_FUNCTION: /* parse an anonymous function */ return parse_anon_func(FALSE, FALSE); case TOKT_OBJECT: /* in-line object definition */ return parse_inline_object(FALSE); case TOKT_NEW: /* skip the operator and check for 'function' or 'method' */ if ((t = G_tok->next()) == TOKT_FUNCTION) { /* it's an anonymous function definition - go parse it */ sub = parse_anon_func(FALSE, FALSE); } else if (t == TOKT_METHOD) { /* anonymous method */ sub = parse_anon_func(FALSE, TRUE); } else { /* check for the 'transient' keyword */ int trans = (G_tok->cur() == TOKT_TRANSIENT); if (trans) G_tok->next(); /* * it's an ordinary 'new' expression - parse the primary * making up the name */ sub = parse_primary(); /* if there's an argument list, parse the argument list */ if (G_tok->cur() == TOKT_LPAR) sub = parse_call(sub); /* create the 'new' node */ sub = parse_new(sub, trans); } return sub; case TOKT_LPAR: /* left parenthesis - skip it */ G_tok->next(); /* parse the expression */ sub = S_op_comma.parse(); if (sub == 0) return 0; /* require the matching right parenthesis */ if (G_tok->cur() == TOKT_RPAR) { /* skip the right paren */ G_tok->next(); } else { /* * log an error; assume that the paren is simply * missing, so continue with our work */ G_tok->log_error_curtok(TCERR_EXPR_MISSING_RPAR); } /* return the parenthesized expression */ return sub; case TOKT_NIL: /* nil - the result is the constant value nil */ cval.set_nil(); return_constant: /* skip the token */ G_tok->next(); /* return a constant node */ return new CTPNConst(&cval); case TOKT_TRUE: /* true - the result is the constant value true */ cval.set_true(); goto return_constant; case TOKT_INT: /* integer constant value */ cval.set_int(G_tok->getcur()->get_int_val()); goto return_constant; case TOKT_FLOAT: /* floating point constant value */ cval.set_float(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), FALSE); goto return_constant; case TOKT_BIGINT: /* an integer constant promoted to float due to overflow */ cval.set_float(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), TRUE); goto return_constant; case TOKT_SSTR: handle_sstring: /* single-quoted string - the result is a constant string value */ cval.set_sstr(G_tok->getcur()); goto return_constant; case TOKT_DSTR: /* * if we're in preprocessor expression mode, treat this the * same as a single-quoted string */ if (G_prs->get_pp_expr_mode()) goto handle_sstring; /* * a string implicitly references 'self', because we could run * through the default output method on the current object */ G_prs->set_self_referenced(TRUE); /* build a double-quoted string node */ sub = new CTPNDstr(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the string */ G_tok->next(); /* return the new node */ return sub; case TOKT_RESTR: /* regular expression string */ cval.set_restr(G_tok->getcur()); goto return_constant; case TOKT_DSTR_START: /* a string implicitly references 'self' */ G_prs->set_self_referenced(TRUE); /* parse the embedding expression */ { CTcEmbedBuilderDbl builder; return parse_embedding(&builder); } case TOKT_SSTR_START: /* parse the embedded expression */ { CTcEmbedBuilderSgl builder; return parse_embedding(&builder); } case TOKT_LBRACK: /* parse the list */ return parse_list(); case TOKT_SYM: /* * symbol - the meaning of the symbol is not yet known, so * create an unresolved symbol node */ sub = G_prs->create_sym_node(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* skip the symbol token */ G_tok->next(); /* return the new node */ return sub; case TOKT_SELF: /* note the explicit self-reference */ G_prs->set_self_referenced(TRUE); /* generate the "self" node */ G_tok->next(); return new CTPNSelf(); case TOKT_REPLACED: /* generate the "replaced" node */ G_tok->next(); return new CTPNReplaced(); case TOKT_TARGETPROP: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "targetprop" node */ G_tok->next(); return new CTPNTargetprop(); case TOKT_TARGETOBJ: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "targetobj" node */ G_tok->next(); return new CTPNTargetobj(); case TOKT_DEFININGOBJ: /* note the explicit extended method context reference */ G_prs->set_full_method_ctx_referenced(TRUE); /* generate the "definingobj" node */ G_tok->next(); return new CTPNDefiningobj(); case TOKT_INVOKEE: /* generate the "invokee" node */ G_tok->next(); return new CTPNInvokee(); case TOKT_ARGCOUNT: /* generate the "argcount" node */ G_tok->next(); return new CTPNArgc(); case TOKT_INHERITED: /* parse the "inherited" operation */ return parse_inherited(); case TOKT_DELEGATED: /* parse the "delegated" operation */ return parse_delegated(); case TOKT_RPAR: /* extra right paren - log an error */ G_tok->log_error(TCERR_EXTRA_RPAR); /* skip it and go back for more */ G_tok->next(); break; case TOKT_RBRACK: /* extra right square bracket - log an error */ G_tok->log_error(TCERR_EXTRA_RBRACK); /* skip it and go back for more */ G_tok->next(); break; case TOKT_DSTR_MID: case TOKT_DSTR_END: case TOKT_SEM: case TOKT_RBRACE: /* * this looks like the end of the statement, but we expected * an operand - note the error and end the statement */ G_tok->log_error_curtok(TCERR_EXPECTED_OPERAND); /* * Synthesize a constant zero as the operand value. Do not * skip the current token, because it's almost certainly not * meant to be part of the expression; we want to stay put * so that the caller can resynchronize on this token. */ cval.set_int(G_tok->getcur()->get_int_val()); return new CTPNConst(&cval); case TOKT_POUND: /* * When evaluating a debugger expression, we accept a special * notation for certain types that can't always be represented * in ordinary source code: * *. #__obj n - object number n (n is in decimal) *. #__prop n - property number n *. #__sstr ofs - s-string at offset ofs (in decimal) *. #__list ofs - list at offset ofs *. #__enum n - enum n *. #__bifptr s i - built-in function pointer, set s, index i *. #__invalid - invalid type */ tok = *G_tok->copycur(); if (G_prs->is_debug_expr() && G_tok->next() == TOKT_SYM && ((tok2 = *G_tok->copycur()).text_matches("__obj", 5) || tok2.text_matches("__obj", 5) || tok2.text_matches("__prop", 6) || tok2.text_matches("__sstr", 6) || tok2.text_matches("__list", 6) || tok2.text_matches("__func", 6) || tok2.text_matches("__enum", 6) || tok2.text_matches("__bifptr", 8))) { /* get the integer value */ if (G_tok->next() == TOKT_INT) { /* get and skip the integer value */ long i = G_tok->getcur()->get_int_val(); G_tok->next(); /* check which symbol we have */ if (tok2.text_matches("__obj", 5)) { /* generate an object expression */ cval.set_obj(i, TC_META_UNKNOWN); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__prop", 6)) { /* generate a property expression */ cval.set_prop(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__sstr", 6)) { /* generate a constant-pool string expression */ cval.set_sstr(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__list", 6)) { /* generate a constant-pool list expression */ cval.set_list(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__func", 6)) { /* generate a fake symbol for the function */ CTcSymFunc *sym = new CTcSymFunc( ".anon", 5, FALSE, 0, 0, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE); /* set the absolute address */ sym->set_abs_addr(i); /* generate a code-pool function pointer expression */ cval.set_funcptr(sym); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__enum", 6)) { /* generate an enum expression */ cval.set_enum(i); return new CTPNDebugConst(&cval); } else if (tok2.text_matches("__bifptr", 8)) { /* get and skip the function index token */ long j = G_tok->getcur()->get_int_val(); G_tok->next(); /* set up a fake symbol for the function */ CTcSymBif *sym = new CTcSymBif( ".anon", 5, FALSE, (ushort)i, (ushort)j, FALSE, 0, 0, FALSE); /* generate a built-in function pointer expression */ cval.set_bifptr(sym); return new CTPNDebugConst(&cval); } } else { /* it's not a special sequence - put back the tokens */ G_tok->unget(&tok2); G_tok->unget(&tok); } } else { /* it's not one of our symbols - put back the '#' */ G_tok->unget(&tok); } /* * if we got this far, we didn't get a valid special debugger * value - complain about the '#' */ goto bad_primary; default: bad_primary: /* invalid primary expression - log the error */ G_tok->log_error_curtok(TCERR_BAD_PRIMARY_EXPR); /* * Skip the token that's causing the problem; this will * ensure that we don't loop indefinitely trying to figure * out what this token is about, and return a constant zero * value as the primary. */ G_tok->next(); /* synthesize a constant zero as the operand value */ cval.set_int(G_tok->getcur()->get_int_val()); goto return_constant; } } } /* * Parse an "inherited" expression */ CTcPrsNode *CTcPrsOpUnary::parse_inherited() { CTcPrsNode *lhs; /* skip the "inherited" keyword and check what follows */ switch(G_tok->next()) { case TOKT_SYM: /* * it's an "inherited superclass..." expression - set up the * "inherited superclass" node */ lhs = new CTPNInhClass(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len()); /* this passes method context information to the inheritor */ G_prs->set_full_method_ctx_referenced(TRUE); /* skip the superclass token */ G_tok->next(); /* parse the member expression portion normally */ break; case TOKT_LT: /* * '<' - this is the start of a multi-method type list. Parse the * list: type1 ',' type2 ',' ... '>', then the argument list to the * 'inherited' call. */ { /* create the formal type list */ CTcFormalTypeList *tl = new (G_prsmem)CTcFormalTypeList(); /* skip the '<' */ G_tok->next(); /* parse each list element */ for (int done = FALSE ; !done ; ) { switch (G_tok->cur()) { case TOKT_GT: /* end of the list - skip the '>', and we're done */ G_tok->next(); done = TRUE; break; case TOKT_ELLIPSIS: /* '...' */ tl->add_ellipsis(); /* this has to be the end of the list */ if (G_tok->next() == TOKT_GT) G_tok->next(); else G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); /* assume the list ends here in any case */ done = TRUE; break; case TOKT_SYM: /* a type token - add it to the list */ tl->add_typed_param(G_tok->getcur()); finish_type: /* skip the type token */ switch (G_tok->next()) { case TOKT_COMMA: /* another type follows */ G_tok->next(); break; case TOKT_GT: G_tok->next(); done = TRUE; break; case TOKT_SYM: case TOKT_ELLIPSIS: case TOKT_TIMES: /* probably just a missing comma */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); break; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_COMMA); G_tok->next(); break; } break; case TOKT_TIMES: /* '*' indicates an untyped parameter */ tl->add_untyped_param(); goto finish_type; case TOKT_LPAR: /* probably a missing '>' */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_GT); done = TRUE; break; case TOKT_COMMA: /* probably a missing type */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); G_tok->next(); break; case TOKT_SEM: case TOKT_RPAR: case TOKT_EOF: /* all of these indicate a premature end of the list */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); return 0; default: /* anything else is an error */ G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_TYPE); G_prs->skip_to_sem(); return 0; } } /* the left-hand side is an "inherited" node, with the arg list */ lhs = new CTPNInh(); ((CTPNInh *)lhs)->set_typelist(tl); /* an inherited<> expression must have an argument list */ if (G_tok->cur() != TOKT_LPAR) { G_tok->log_error_curtok(TCERR_MMINH_MISSING_ARG_LIST); G_prs->skip_to_sem(); return 0; } } break; default: /* * There's no explicit superclass name listed, so the left-hand * side of the '.' expression is the simple "inherited" node. */ lhs = new CTPNInh(); /* * Since we don't have an explicit superclass, we'll need the * method context at run-time to establish the next class in * inheritance order. Flag the need for the full method context. */ G_prs->set_full_method_ctx_referenced(TRUE); /* parse the member expression portion normally */ break; } /* parse and return the member expression */ return parse_member(lhs); } /* * Parse a "delegated" expression */ CTcPrsNode *CTcPrsOpUnary::parse_delegated() { /* 'delegated' always references 'self' */ G_prs->set_self_referenced(TRUE); /* skip the "delegated" keyword */ G_tok->next(); /* * Parse a postfix expression giving the delegatee. Don't allow * nested member subexpressions (unless they're enclosed in * parentheses, of course) - our implicit '.' postfix takes * precedence. Also, don't allow call subexpressions (unless enclosed * in parens), since a postfix argument list binds to the 'delegated' * expression, not to a subexpression involving a function/method * call. */ CTcPrsNode *target = parse_postfix(FALSE, FALSE); /* set up the "delegated" node */ CTcPrsNode *lhs = new CTPNDelegated(target); /* return the rest as a normal member expression */ return parse_member(lhs); } /* * Parse a list */ CTcPrsNode *CTcPrsOpUnary::parse_list() { /* assume this isn't a lookup table (with "key->val" pairs) */ int is_lookup_table = FALSE; /* we're not at the default value for a lookup table ("*->val") */ int at_def_val = FALSE; /* set up a nil value for adding placeholders (for error recovery) */ CTcConstVal nilval; nilval.set_nil(); /* skip the opening '[' */ G_tok->next(); /* * create the list expression -- we'll add elements to the list as * we parse the elements */ CTPNList *lst = new CTPNList(); /* scan all list elements */ for (;;) { /* check what we have */ switch(G_tok->cur()) { case TOKT_RBRACK: /* * that's the end of the list - skip the closing bracket and * return the finished list */ G_tok->next(); goto done; case TOKT_EOF: case TOKT_RBRACE: case TOKT_SEM: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * these would all seem to imply that the closing ']' was * missing from the list; flag the error and end the list * now */ G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); goto done; case TOKT_RPAR: /* * extra right paren - log an error, but then skip the paren * and try to keep parsing */ G_tok->log_error(TCERR_LIST_EXTRA_RPAR); G_tok->next(); break; case TOKT_TIMES: /* * If a -> follows, this is the default value for a lookup * table list. */ if (G_tok->next() == TOKT_ARROW) { /* skip the arrow and get to the value */ G_tok->next(); /* * if this is a non-lookup table list with other entries, * this is an error */ if (!is_lookup_table && lst->get_count() != 0) { /* log the error, but keep parsing the list */ G_tok->log_error(TCERR_ARROW_IN_LIST, lst->get_count()); } else { /* it's definitely a lookup list now */ is_lookup_table = TRUE; /* we're now on the default value */ at_def_val = TRUE; } } else { /* it's not *-> - put back the next token */ G_tok->unget(); } default: /* it must be the next element expression */ break; } /* * Attempt to parse another list element expression. Parse just * below a comma expression, because commas can be used to * separate list elements. */ CTcPrsNode *ele = S_op_asi.parse(); if (ele == 0) return 0; /* add the element to the list */ lst->add_element(ele); /* if this was the default value, the list should end here */ if (at_def_val && G_tok->cur() != TOKT_RBRACK) { /* log an error */ G_tok->log_error_curtok(TCERR_LOOKUP_LIST_EXPECTED_END_AT_DEF); /* we've left the default value */ at_def_val = FALSE; /* * if we're at a comma, add a nil value to the list to * resynchronize - they must want to add more Key->Value pairs */ if (G_tok->cur() == TOKT_COMMA) lst->add_element(new CTPNConst(&nilval)); } /* check what follows the element */ switch(G_tok->cur()) { case TOKT_COMMA: /* skip the comma introducing the next element */ G_tok->next(); /* * If this is a lookup table list, commas are only allowed at * even elements: [ODD -> EVEN, ODD -> EVEN...]. */ if (is_lookup_table && (lst->get_count() & 1) != 0) { /* log an error */ G_tok->log_error(TCERR_LOOKUP_LIST_EXPECTED_ARROW, (lst->get_count()+1)/2); /* * Add an implied nil element as the value. This will help * resynchronize with the source code in the most likely * case that they simply left out a ->value item, so that * we don't log alternating "expected comma" and "expected * arrow" messages on every subsequent delimiter. */ lst->add_element(new CTPNConst(&nilval)); } /* if a close bracket follows, we seem to have an extra comma */ if (G_tok->cur() == TOKT_RBRACK) { /* * log an error about the missing element, then end the * list here */ G_tok->log_error_curtok(TCERR_LIST_EXPECT_ELEMENT); goto done; } break; case TOKT_ARROW: /* skip the arrow */ G_tok->next(); /* * '->' indicates a lookup table key->value list. This is an * all-or-nothing proposition: it has to be on every pair of * elements, or on none of them. */ if (lst->get_count() == 1) { /* first element: this is now a lookup table list */ is_lookup_table = TRUE; } else if (!is_lookup_table) { /* * it's not the first element, and we haven't seen arrows * before, so this list shouldn't have arrows at all */ G_tok->log_error(TCERR_ARROW_IN_LIST, lst->get_count()); } else if (is_lookup_table && (lst->get_count() & 1) == 0) { /* * We have an even number of elements, so an arrow is not * allowed here under any circumstances. */ G_tok->log_error(TCERR_MISPLACED_ARROW_IN_LIST, lst->get_count()/2); /* * They probably just left out the key value accidentally, * so add a nil value as the key. This will avoid a * cascade of errors for subsequent delimiters if they did * just leave out one item. */ lst->add_element(new CTPNConst(&nilval)); } break; case TOKT_RBRACK: /* * we're done with the list - skip the bracket and return * the finished list */ G_tok->next(); /* * If this is a lookup table list, make sure we ended on an * even number - if not, we're missing a ->Value entry. The * exception is that if the last value was a default, we end on * an odd item. */ if (is_lookup_table && (lst->get_count() & 1) != 0 && !at_def_val) { /* log the error */ G_tok->log_error(TCERR_LOOKUP_LIST_EXPECTED_ARROW, (lst->get_count()+1)/2); } goto done; case TOKT_EOF: case TOKT_LBRACE: case TOKT_RBRACE: case TOKT_SEM: case TOKT_DSTR_MID: case TOKT_DSTR_END: /* * these would all seem to imply that the closing ']' was * missing from the list; flag the error and end the list * now */ G_tok->log_error_curtok(TCERR_LIST_MISSING_RBRACK); goto done; default: /* * Anything else is an error - note that we expected a * comma, then proceed with parsing from the current token * as though we had found the comma (in all likelihood, the * comma was accidentally omitted). If we've reached the * end of the file, return what we have so far, since it's * pointless to keep looping. */ G_tok->log_error_curtok(TCERR_LIST_EXPECT_COMMA); /* give up on end of file, otherwise keep going */ if (G_tok->cur() == TOKT_EOF) goto done; break; } } done: /* tell the parser to note this list, in case it's the longest yet */ G_cg->note_list(lst->get_count()); /* if it's a lookup table list, mark it as such */ if (is_lookup_table) lst->set_lookup_table(); /* return the list */ return lst; } /* ------------------------------------------------------------------------ */ /* * Parse Allocation Object */ /* * memory allocator for parse nodes */ void *CTcPrsAllocObj::operator new(size_t siz) { /* allocate the space out of the node pool */ return G_prsmem->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * Parse Tree space manager */ /* * create */ CTcPrsMem::CTcPrsMem() { /* we have no blocks yet */ head_ = tail_ = 0; /* allocate our first block */ alloc_block(); } CTcPrsMem::~CTcPrsMem() { /* delete all objects in our pool */ delete_all(); } /* * Save state, for later resetting */ void CTcPrsMem::save_state(tcprsmem_state_t *state) { /* save the pool information in the state structure */ state->tail = tail_; state->free_ptr = free_ptr_; state->rem = rem_; } /* * Reset to initial state */ void CTcPrsMem::reset() { /* delete all blocks */ delete_all(); /* re-allocate the initial block */ alloc_block(); /* fixups are allocated in parser memory, so they're gone now */ G_objfixup = 0; G_propfixup = 0; G_enumfixup = 0; } /* * Reset. This deletes all objects allocated out of the parser pool * since the state was saved. */ void CTcPrsMem::reset(const tcprsmem_state_t *state) { tcprsmem_blk_t *cur; /* * delete all of the blocks that were allocated after the last block * that existed when the state was saved */ for (cur = state->tail->next_ ; cur != 0 ; ) { tcprsmem_blk_t *nxt; /* remember the next block */ nxt = cur->next_; /* delete this block */ t3free(cur); /* move on to the next one */ cur = nxt; } /* re-establish the saved last block */ tail_ = state->tail; /* make sure the list is terminated at the last block */ tail_->next_ = 0; /* re-establish the saved allocation point in the last block */ free_ptr_ = state->free_ptr; rem_ = state->rem; } /* * Delete all parser memory. This deletes all objects allocated out of * parser memory, so the caller must be sure that all of these objects * are unreferenced. */ void CTcPrsMem::delete_all() { /* free all blocks */ while (head_ != 0) { tcprsmem_blk_t *nxt; /* remember the next block after this one */ nxt = head_->next_; /* free this block */ t3free(head_); /* move on to the next one */ head_ = nxt; } /* there's no tail now */ tail_ = 0; } /* * allocate a block */ void CTcPrsMem::alloc_block() { tcprsmem_blk_t *blk; /* * block size - pick a size that's large enough that we won't be * unduly inefficient (in terms of having tons of blocks), but still * friendly to 16-bit platforms (i.e., under 64k) */ const size_t BLOCK_SIZE = 65000; /* allocate space for the block */ blk = (tcprsmem_blk_t *)t3malloc(sizeof(tcprsmem_blk_t) + BLOCK_SIZE - 1); /* if that failed, throw an error */ if (blk == 0) err_throw(TCERR_NO_MEM_PRS_TREE); /* link in the block at the end of our list */ blk->next_ = 0; if (tail_ != 0) tail_->next_ = blk; else head_ = blk; /* the block is now the last block in the list */ tail_ = blk; /* * Set up to allocate out of our block. Make sure the free pointer * starts out on a worst-case alignment boundary; normally, the C++ * compiler will properly align our "buf_" structure member on a * worst-case boundary, so this calculation won't actually change * anything, but this will help ensure portability even to weird * compilers. */ free_ptr_ = (char *)osrndpt((unsigned char *)blk->buf_); /* * get the amount of space remaining in the block (in the unlikely * event that worst-case alignment actually moved the free pointer * above the start of the buffer, we'll have lost a little space in * the buffer for the alignment offset) */ rem_ = BLOCK_SIZE - (free_ptr_ - blk->buf_); } /* * Allocate space */ void *CTcPrsMem::alloc(size_t siz) { char *ret; size_t space_used; /* if there's not enough space available, allocate a new block */ if (siz > rem_) { /* allocate a new block */ alloc_block(); /* * if there's still not enough room, the request must exceed the * largest block we can allocate */ if (siz > rem_) G_tok->throw_internal_error(TCERR_PRS_BLK_TOO_BIG, (ulong)siz); } /* return the free pointer */ ret = free_ptr_; /* advance the free pointer past the space, rounding for alignment */ free_ptr_ = (char *)osrndpt((unsigned char *)free_ptr_ + siz); /* deduct the amount of space we consumed from the available space */ space_used = free_ptr_ - ret; if (space_used > rem_) rem_ = 0; else rem_ -= space_used; /* return the allocated space */ return ret; } /* ------------------------------------------------------------------------ */ /* * parse node base class */ /* * Make adjustments for dynamic evaluation */ CTcPrsNode *CTcPrsNodeBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* by default, a node can't be used in dynamic evaluation */ err_throw(VMERR_INVAL_DBG_EXPR); AFTER_ERR_THROW(return 0;) } /* ------------------------------------------------------------------------ */ /* * constant node */ /* * adjust for debugger use */ CTcPrsNode *CTPNConstBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* convert to a debugger-constant */ return new CTPNDebugConst(&val_); } /* ------------------------------------------------------------------------ */ /* * List parse node */ /* * add an element to a list */ void CTPNListBase::add_element(CTcPrsNode *expr) { CTPNListEle *ele; /* create a list element object for the new element */ ele = new CTPNListEle(expr); /* count the new entry */ ++cnt_; /* add the element to our linked list */ ele->set_prev(tail_); if (tail_ != 0) tail_->set_next(ele); else head_ = ele; tail_ = ele; /* * if the new element does not have a constant value, the list no * longer has a constant value (if it did before) */ if (!expr->is_const()) is_const_ = FALSE; } /* * remove each occurrence of a given constant value from the list */ void CTPNListBase::remove_element(const CTcConstVal *val) { CTPNListEle *cur; /* scan the list */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* * if this element is constant, compare it to the value to be * removed; if it matches, remove it */ if (cur->get_expr()->is_const() && cur->get_expr()->get_const_val()->is_equal_to(val)) { /* set the previous element's forward pointer */ if (cur->get_prev() == 0) head_ = cur->get_next(); else cur->get_prev()->set_next(cur->get_next()); /* set the next element's back pointer */ if (cur->get_next() == 0) tail_ = cur->get_prev(); else cur->get_next()->set_prev(cur->get_prev()); /* decrement our element counter */ --cnt_; } } } /* * Get the constant value of the element at the given index. Logs an * error and returns null if there's no such element. */ CTcPrsNode *CTPNListBase::get_const_ele(int index) { CTPNListEle *ele; /* if the index is negative, it's out of range */ if (index < 1) { /* log the error and return failure */ G_tok->log_error(TCERR_CONST_IDX_RANGE); return 0; } /* scan the list for the given element */ for (ele = head_ ; ele != 0 && index > 1 ; ele = ele->get_next(), --index) ; /* if we ran out of elements, the index is out of range */ if (ele == 0 || index != 1) { G_tok->log_error(TCERR_CONST_IDX_RANGE); return 0; } /* return the element's constant value */ return ele->get_expr(); } /* * Fold constants */ CTcPrsNode *CTPNListBase::fold_constants(CTcPrsSymtab *symtab) { CTPNListEle *cur; int all_const; /* * if the list is already constant, there's nothing extra we need to * do */ if (is_const_) return this; /* presume the result will be all constants */ all_const = TRUE; /* run through my list and fold each element */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* fold this element */ cur->fold_constants(symtab); /* * if this element is not a constant, the whole list cannot be * constant */ if (!cur->get_expr()->is_const()) all_const = FALSE; } /* if every element was a constant, the overall list is constant */ if (all_const) is_const_ = TRUE; /* return myself */ return this; } /* * Adjust for dynamic compilation */ CTcPrsNode *CTPNListBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { CTPNListEle *cur; /* run through my list and adjust each element */ for (cur = head_ ; cur != 0 ; cur = cur->get_next()) { /* adjust this element */ cur->adjust_for_dyn(info); } /* * force the list to be non-constant - in debugger mode, we have to * build the value we push as a dynamic object, never as an actual * constant, to ensure that the generated code can be deleted * immediately after being executed */ is_const_ = FALSE; /* return myself */ return this; } /* ------------------------------------------------------------------------ */ /* * If-nil operator ?? node base class */ /* * fold constants */ CTcPrsNode *CTPNIfnilBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the subnodes */ first_ = first_->fold_constants(symtab); second_ = second_->fold_constants(symtab); /* * if the first is now a constant, we can fold this entire expression * node by choosing the first or second node. Otherwise return myself * unchanged. */ if (first_->is_const()) { /* * if the first expression is nil, return the second, otherwise * return the first */ return (first_->get_const_val()->get_type() == TC_CVT_NIL ? second_ : first_); } else { /* we can't fold this node any further - return it unchanged */ return this; } } /* ------------------------------------------------------------------------ */ /* * conditional operator node base class */ /* * fold constants */ CTcPrsNode *CTPNIfBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the subnodes */ first_ = first_->fold_constants(symtab); second_ = second_->fold_constants(symtab); third_ = third_->fold_constants(symtab); /* * if the first is now a constant, we can fold this entire * expression node by choosing the second or third based on its * value; otherwise, return myself unchanged */ if (first_->is_const()) { /* * The condition is a constant - the result is the 'then' or 'else' * part, based on the condition's value. */ return (first_->get_const_val()->get_val_bool() ? second_ : third_); } else { /* we can't fold this node any further - return it unchanged */ return this; } } /* ------------------------------------------------------------------------ */ /* * Double-quoted string node - base class */ /* * create a double-quoted string node */ CTPNDstrBase::CTPNDstrBase(const char *str, size_t len) { /* remember the string */ str_ = str; len_ = len; /* * note the length in the parser, in case it's the longest string * we've seen so far */ G_cg->note_str(len); } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNDstrBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* * don't allow dstring evaluation in speculative mode, since we * can't execute anything with side effects in this mode */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* return a debugger dstring node */ return new CTPNDebugDstr(str_, len_); } /* ------------------------------------------------------------------------ */ /* * Address-of parse node */ /* * fold constants */ CTcPrsNode *CTPNAddrBase::fold_constants(CTcPrsSymtab *symtab) { CTcPrsNode *ret; /* ask the symbol to generate a constant expression for its address */ ret = get_sub_expr()->fold_addr_const(symtab); /* * if we got a constant value, return it; otherwise, return myself * unchanged */ return (ret != 0 ? ret : this); } /* * determine if my address equals that of another node */ int CTPNAddrBase::is_addr_eq(const CTPNAddr *node, int *comparable) const { /* * If both sides are symbols, the addresses are equal if and only if * the symbols are identical. One symbol has exactly one meaning in * a given context, and no two symbols can have the same meaning. * (It's important that we be able to state this for all symbols, * because we can't necessarily know during parsing the meaning of a * given symbol, since the symbol could be a forward reference.) */ if (get_sub_expr()->get_sym_text() != 0 && node->get_sub_expr()->get_sym_text() != 0) { CTcPrsNode *sym1; CTcPrsNode *sym2; /* they're both symbols, so they're comparable */ *comparable = TRUE; /* they're the same if both symbols have the same text */ sym1 = get_sub_expr(); sym2 = node->get_sub_expr(); return (sym1->get_sym_text_len() == sym2->get_sym_text_len() && memcmp(sym1->get_sym_text(), sym2->get_sym_text(), sym1->get_sym_text_len()) == 0); } /* they're not comparable */ *comparable = FALSE; return FALSE; } /* ------------------------------------------------------------------------ */ /* * Symbol parse node base class */ /* * fold constants */ CTcPrsNode *CTPNSymBase::fold_constants(CTcPrsSymtab *symtab) { CTcSymbol *sym; CTcPrsNode *ret; /* * Look up my symbol. At this stage, don't assume a definition; * merely look to see if it's already known. We don't have enough * information to determine how we should define the symbol, so * leave it undefined until code generation if it's not already * known. */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* ask the symbol to do the folding */ ret = sym->fold_constant(); /* if that succeeded, return it; otherwise, return unchanged */ return (ret != 0 ? ret : this); } else { /* not defined - return myself unchanged */ return this; } } /* * Fold my address to a constant node. If I have no address value, I'll * simply return myself unchanged. Note that it's an error if I have no * constant value, but we'll count on the code generator to report the * error, and simply ignore it for now. */ CTcPrsNode *CTPNSymBase::fold_addr_const(CTcPrsSymtab *symtab) { CTcSymbol *sym; /* look up my symbol; if we don't find it, don't define it */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* we got a symbol - ask it to do the folding */ return sym->fold_addr_const(); } else { /* undefined symbol - there's no constant address value */ return 0; } } /* * Determine if I have a return value when called */ int CTPNSymBase::has_return_value_on_call() const { CTcSymbol *sym; /* try resolving my symbol */ sym = G_prs->get_global_symtab()->find(sym_, len_); /* * if we found a symbol, let it resolve the call; otherwise, assume * that we do have a return value */ if (sym != 0) return sym->has_return_value_on_call(); else return TRUE; } /* * Determine if I am a valid lvalue */ int CTPNSymBase::check_lvalue_resolved(class CTcPrsSymtab *symtab) const { CTcSymbol *sym; /* look up the symbol in the given scope */ sym = symtab->find(get_sym_text(), get_sym_text_len()); if (sym != 0) { /* ask the symbol what it thinks */ return sym->check_lvalue(); } else { /* it's undefined - can't be an lvalue */ return FALSE; } } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNSymBase::adjust_for_dyn(const tcpn_dyncomp_info *) { /* * If this symbol isn't defined in the global symbol table, we can't * evaluate this expression in the debugger - new symbols can never * be defined in the debugger, so there's no point in trying to hold * a forward reference as we normally would for an undefined symbol. * We need look only in the global symbol table because local * symbols will already have been resolved. */ if (G_prs->get_global_symtab()->find(sym_, len_) == 0) { /* log the error, to generate an appropriate message */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len_, sym_); /* throw the error as well */ err_throw_a(TCERR_UNDEF_SYM, 2, ERR_TYPE_INT, (int)len_, ERR_TYPE_TEXTCHAR, sym_); } /* return myself unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Resolved Symbol parse node base class */ /* * fold constants */ CTcPrsNode *CTPNSymResolvedBase::fold_constants(CTcPrsSymtab *symtab) { CTcPrsNode *ret; /* ask the symbol to generate the folded constant value */ ret = sym_->fold_constant(); /* if that succeeded, return it; otherwise, return myself unchanged */ return (ret != 0 ? ret : this); } /* * Fold my address to a constant node. If I have no address value, I'll * simply return myself unchanged. Note that it's an error if I have no * constant value, but we'll count on the code generator to report the * error, and simply ignore it for now. */ CTcPrsNode *CTPNSymResolvedBase::fold_addr_const(CTcPrsSymtab *symtab) { /* ask my symbol to generate the folded constant value */ return sym_->fold_addr_const(); } /* ------------------------------------------------------------------------ */ /* * Debugger local variable resolved symbol */ /* * construction */ CTPNSymDebugLocalBase::CTPNSymDebugLocalBase(const tcprsdbg_sym_info *info) { /* save the type information */ switch(info->sym_type) { case TC_SYM_LOCAL: var_id_ = info->var_id; ctx_arr_idx_ = info->ctx_arr_idx; frame_idx_ = info->frame_idx; is_param_ = FALSE; break; case TC_SYM_PARAM: var_id_ = info->var_id; ctx_arr_idx_ = 0; frame_idx_ = info->frame_idx; is_param_ = TRUE; break; default: /* other types are invalid */ assert(FALSE); break; } } /* ------------------------------------------------------------------------ */ /* * Argument List parse node base class */ /* * fold constants */ CTcPrsNode *CTPNArglistBase::fold_constants(CTcPrsSymtab *symtab) { /* fold each list element */ CTPNArg *cur; for (cur = get_arg_list_head() ; cur != 0 ; cur = cur->get_next_arg()) cur->fold_constants(symtab); /* return myself with no further folding */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNArglistBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust each argument list member */ CTPNArg *arg; for (arg = list_ ; arg != 0 ; arg = arg->get_next_arg()) { /* * adjust this argument; assume the argument node itself isn't * affected */ arg->adjust_for_dyn(info); } /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Argument List Entry parse node base class */ /* * fold constants */ CTcPrsNode *CTPNArgBase::fold_constants(CTcPrsSymtab *symtab) { /* fold my argument expression */ arg_expr_ = arg_expr_->fold_constants(symtab); /* return myself unchanged */ return this; } /* * Set the parameter name */ void CTPNArgBase::set_name(const CTcToken *tok) { /* remember the name token */ name_ = *tok; } /* ------------------------------------------------------------------------ */ /* * Member with no arguments */ /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNMemberBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* adjust the object and property expressions */ obj_expr_ = obj_expr_->adjust_for_dyn(info); prop_expr_ = prop_expr_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Member with Arguments parse node base class */ /* * fold constants */ CTcPrsNode *CTPNMemArgBase::fold_constants(CTcPrsSymtab *symtab) { /* fold constants in the object and property expressions */ obj_expr_ = obj_expr_->fold_constants(symtab); prop_expr_ = prop_expr_->fold_constants(symtab); /* * fold constants in the argument list; an argument list node never * changes to a new node type during constant folding, so we don't * need to update the member */ arglist_->fold_constants(symtab); /* return myself with no further evaluation */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNMemArgBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* don't allow in speculative mode due to possible side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* * adjust my object expression, property expression, and argument * list */ obj_expr_ = obj_expr_->adjust_for_dyn(info); prop_expr_ = prop_expr_->adjust_for_dyn(info); arglist_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Function/Method Call parse node base class */ /* * fold constants */ CTcPrsNode *CTPNCallBase::fold_constants(CTcPrsSymtab *symtab) { /* fold my function expression */ func_ = func_->fold_constants(symtab); /* fold my argument list */ arglist_->fold_constants(symtab); /* return myself unchanged */ return this; } /* * adjust for dynamic (run-time) compilation */ CTcPrsNode *CTPNCallBase::adjust_for_dyn(const tcpn_dyncomp_info *info) { /* don't allow in speculative mode because of side effects */ if (info->speculative) err_throw(VMERR_BAD_SPEC_EVAL); /* adjust the function expression */ func_ = func_->adjust_for_dyn(info); /* adjust the argument list (assume it doesn't change) */ arglist_->adjust_for_dyn(info); /* return myself otherwise unchanged */ return this; } /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table implementation */ /* static hash function */ CVmHashFunc *CTcPrsSymtab::hash_func_ = 0; /* * allocate parser symbol tables out of the parser memory pool */ void *CTcPrsSymtab::operator new(size_t siz) { return G_prsmem->alloc(siz); } /* * static initialization */ void CTcPrsSymtab::s_init() { /* create our static hash function */ if (hash_func_ == 0) hash_func_ = new CVmHashFuncCS(); } /* * static termination */ void CTcPrsSymtab::s_terminate() { /* delete our static hash function */ if (hash_func_ != 0) { delete hash_func_; hash_func_ = 0; } } /* * initialize */ CTcPrsSymtab::CTcPrsSymtab(CTcPrsSymtab *parent_scope) { size_t hash_table_size; /* * Create the hash table. If we're at global scope (parent_scope == * 0), create a large hash table, since we'll probably add lots of * symbols; otherwise, it's just a local table, which probably won't * have many entries, so create a small table. * * Note that we always use the static hash function object, hence * the table doesn't own the object. */ hash_table_size = (parent_scope == 0 ? 512 : 32); hashtab_ = new (G_prsmem) CVmHashTable(hash_table_size, hash_func_, FALSE, new (G_prsmem) CVmHashEntry *[hash_table_size]); /* remember the parent scope */ parent_ = parent_scope; /* we're not in a debugger frame list yet */ list_index_ = 0; list_next_ = 0; /* we don't have a generated byte code range yet */ start_ofs_ = end_ofs_ = 0; } /* * delete */ CTcPrsSymtab::~CTcPrsSymtab() { /* delete our underlying hash table */ delete hashtab_; } /* * Find a symbol, marking it as referenced if we find it. */ CTcSymbol *CTcPrsSymtab::find(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab) { CTcSymbol *entry; /* find the symbol */ entry = find_noref(sym, len, symtab); /* if we found an entry, mark it as referenced */ if (entry != 0) entry->mark_referenced(); /* return the result */ return entry; } /* * Find a symbol. This base version does not affect the 'referenced' * status of the symbol we look up. */ CTcSymbol *CTcPrsSymtab::find_noref(const textchar_t *sym, size_t len, CTcPrsSymtab **symtab) { /* * Look for the symbol. Start in the current symbol table, and work * outwards to the outermost enclosing table. */ for (CTcPrsSymtab *curtab = this ; curtab != 0 ; curtab = curtab->get_parent()) { /* look for the symbol in this table */ CTcSymbol *entry = curtab->find_direct(sym, len); if (entry != 0) { /* * found it - if the caller wants to know about the symbol * table in which we actually found the symbol, return that * information */ if (symtab != 0) *symtab = curtab; /* return the symbol table entry we found */ return entry; } } /* we didn't find the symbol - return failure */ return 0; } /* * Find a symbol directly in this table */ CTcSymbol *CTcPrsSymtab::find_direct(const textchar_t *sym, size_t len) { /* return the entry from our hash table */ return (CTcSymbol *)get_hashtab()->find(sym, len); } /* * Add a symbol to the table */ void CTcPrsSymtab::add_entry(CTcSymbol *sym) { /* add it to the table */ get_hashtab()->add(sym); } /* * remove a symbol from the table */ void CTcPrsSymtab::remove_entry(CTcSymbol *sym) { /* remove it from the underyling hash table */ get_hashtab()->remove(sym); } /* * Add a formal parameter symbol */ CTcSymLocal *CTcPrsSymtab::add_formal(const textchar_t *sym, size_t len, int formal_num, int copy_str) { CTcSymLocal *lcl; /* * Make sure it's not already defined in our own symbol table - if * it is, log an error and ignore the redundant definition. (We * only care about our own scope, not enclosing scopes, since it's * perfectly fine to hide variables in enclosing scopes.) */ if (find_direct(sym, len) != 0) { /* log an error */ G_tok->log_error(TCERR_FORMAL_REDEF, (int)len, sym); /* don't add the symbol again */ return 0; } /* create the symbol entry */ lcl = new CTcSymLocal(sym, len, copy_str, TRUE, formal_num); /* add it to the table */ add_entry(lcl); /* return the new symbol */ return lcl; } /* * Add the current token as a local variable symbol, initially unreferenced * and uninitialized */ CTcSymLocal *CTcPrsSymtab::add_local(int local_num) { return add_local(G_tok->getcur()->get_text(), G_tok->getcur()->get_text_len(), local_num, FALSE, FALSE, FALSE); } /* * Add a local variable symbol */ CTcSymLocal *CTcPrsSymtab::add_local(const textchar_t *sym, size_t len, int local_num, int copy_str, int init_assigned, int init_referenced) { CTcSymLocal *lcl; /* * make sure the symbol isn't already defined in this scope; if it * is, log an error */ if (find_direct(sym, len) != 0) { /* log the error */ G_tok->log_error(TCERR_LOCAL_REDEF, (int)len, sym); /* don't create the symbol again - return the original definition */ return 0; } /* create the symbol entry */ lcl = new CTcSymLocal(sym, len, copy_str, FALSE, local_num); /* * if the symbol is initially to be marked as referenced or * assigned, mark it now */ if (init_assigned) lcl->set_val_assigned(TRUE); if (init_referenced) lcl->set_val_used(TRUE); /* add it to the table */ add_entry(lcl); /* return the new local */ return lcl; } /* * Add a code label ('goto') symbol */ CTcSymLabel *CTcPrsSymtab::add_code_label(const textchar_t *sym, size_t len, int copy_str) { CTcSymLabel *lbl; /* * make sure the symbol isn't already defined in this scope; if it * is, log an error */ if (find_direct(sym, len) != 0) { /* log the error */ G_tok->log_error(TCERR_CODE_LABEL_REDEF, (int)len, sym); /* don't create the symbol again - return the original definition */ return 0; } /* create the symbol entry */ lbl = new CTcSymLabel(sym, len, copy_str); /* add it to the table */ add_entry(lbl); /* return the new label */ return lbl; } /* * Find a symbol; if the symbol isn't defined, add a new entry according * to the action flag. Because we add a symbol entry if the symbol * isn't defined, this *always* returns a valid symbol object. */ CTcSymbol *CTcPrsSymtab::find_or_def(const textchar_t *sym, size_t len, int copy_str, tcprs_undef_action action) { /* * Look for the symbol. Start in the current symbol table, and work * outwards to the outermost enclosing table. */ for (CTcPrsSymtab *curtab = this ; ; curtab = curtab->get_parent()) { /* look for the symbol in this table */ CTcSymbol *entry = (CTcSymbol *)curtab->find_direct(sym, len); if (entry != 0) { /* mark the entry as referenced */ entry->mark_referenced(); /* * if this is a non-weak property definition, and this is a * property symbol, remove any existing weak flag from the * property symbol */ if (entry->get_type() == TC_SYM_PROP && (action == TCPRS_UNDEF_ADD_PROP || action == TCPRS_UNDEF_ADD_PROP_NO_WARNING)) ((CTcSymProp *)entry)->set_weak(FALSE); /* found it - return the symbol */ return entry; } /* * If there's no parent symbol table, the symbol is undefined. * Add a new symbol according to the action parameter. Note * that we always add the new symbol at global scope, hence we * add it to 'curtab', not 'this'. */ if (curtab->get_parent() == 0) { /* check which action we're being asked to perform */ switch(action) { case TCPRS_UNDEF_ADD_UNDEF: /* add an "undefined" entry - log an error */ G_tok->log_error(TCERR_UNDEF_SYM, (int)len, sym); /* create a new symbol of type undefined */ entry = new CTcSymUndef(sym, len, copy_str); /* finish up */ goto add_entry; case TCPRS_UNDEF_ADD_PROP: case TCPRS_UNDEF_ADD_PROP_NO_WARNING: case TCPRS_UNDEF_ADD_PROP_WEAK: { /* create a new symbol of type property */ CTcSymProp *prop = new CTcSymProp( sym, len, copy_str, G_cg->new_prop_id()); /* mark it as "weak" if desired */ if (action == TCPRS_UNDEF_ADD_PROP_WEAK) prop->set_weak(TRUE); /* use it as the new table entry */ entry = prop; } /* show a warning if desired */ if (action == TCPRS_UNDEF_ADD_PROP) G_tok->log_warning(TCERR_ASSUME_SYM_PROP, (int)len, sym); /* finish up */ goto add_entry; add_entry: /* add the new entry to the global table */ add_to_global_symtab(curtab, entry); /* return the new entry */ return entry; } } } } /* * Find a symbol. If the symbol is already defined as a "weak" property, * delete the existing symbol to make way for the overriding strong * definition. */ CTcSymbol *CTcPrsSymtab::find_delete_weak(const char *name, size_t len) { /* look up the symbol */ CTcSymbol *sym = find(name, len); /* if it's a weak property definition, delete it */ if (sym != 0 && sym->get_type() == TC_SYM_PROP && ((CTcSymProp *)sym)->is_weak()) { /* delete it and forget it */ remove_entry(sym); sym = 0; } /* return what we found */ return sym; } /* * Enumerate the entries in a symbol table */ void CTcPrsSymtab::enum_entries(void (*func)(void *, CTcSymbol *), void *ctx) { /* * Ask the hash table to perform the enumeration. We know that all * of our entries in the symbol table are CTcSymbol objects, so we * can coerce the callback function to the appropriate type without * danger. */ get_hashtab()->enum_entries((void (*)(void *, CVmHashEntry *))func, ctx); } /* * Scan the symbol table for unreferenced local variables */ void CTcPrsSymtab::check_unreferenced_locals() { /* skip the check if we're only parsing for syntax */ if (!G_prs->get_syntax_only()) { /* run the symbols through our unreferenced local check callback */ enum_entries(&unref_local_cb, this); } } /* * Enumeration callback to check for unreferenced locals */ void CTcPrsSymtab::unref_local_cb(void *, CTcSymbol *sym) { /* check the symbol */ sym->check_local_references(); } /* ------------------------------------------------------------------------ */ /* * Comma node */ /* * fold constants */ CTcPrsNode *CTPNCommaBase::fold_binop() { /* use the normal addition folder */ return S_op_comma.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Addition parse node */ /* * Fold constants. We override the default fold_constants() for * addition nodes because addition constancy can be affected by symbol * resolution. In particular, if we resolve symbols in a list, the list * could turn constant, which could in turn make the result of an * addition operator with the list as an operand turn constant. */ CTcPrsNode *CTPNAddBase::fold_binop() { /* use the normal addition folder */ return S_op_add.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Subtraction parse node */ /* * Fold constants. We override the default fold_constants() for the * subtraction node because subtraction constancy can be affected by * symbol resolution. In particular, if we resolve symbols in a list, * the list could turn constant, which could in turn make the result of * a subtraction operator with the list as an operand turn constant. */ CTcPrsNode *CTPNSubBase::fold_binop() { /* use the normal addition folder */ return S_op_sub.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Equality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNEqBase::fold_binop() { /* use the normal addition folder */ return S_op_eq.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Inequality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNNeBase::fold_binop() { /* use the normal addition folder */ return S_op_ne.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Logical AND parse node */ /* * fold constants */ CTcPrsNode *CTPNAndBase::fold_binop() { /* use the normal addition folder */ return S_op_and.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Logical OR parse node */ /* * fold constants */ CTcPrsNode *CTPNOrBase::fold_binop() { /* use the normal addition folder */ return S_op_or.eval_constant(left_, right_); } /* ------------------------------------------------------------------------ */ /* * NOT parse node */ /* * fold constants */ CTcPrsNode *CTPNNotBase::fold_unop() { /* use the normal addition folder */ return S_op_unary.eval_const_not(sub_); } /* ------------------------------------------------------------------------ */ /* * Subscript parse node */ /* * Fold constants. We override the default fold_constants() for * subscript nodes because subscript constancy can be affected by symbol * resolution. In particular, if we resolve symbols in a list, the list * could turn constant, which could in turn make the result of a * subscript operator with the list as an operand turn constant. */ /* ------------------------------------------------------------------------ */ /* * Equality Comparison parse node */ /* * fold constants */ CTcPrsNode *CTPNSubscriptBase::fold_binop() { /* use the normal addition folder */ return S_op_unary.eval_const_subscript(left_, right_); } /* ------------------------------------------------------------------------ */ /* * Parser Symbol Table Entry base class */ /* * Allocate symbol objects from the parse pool, since these objects have * all of the lifespan characteristics of pool objects. */ void *CTcSymbolBase::operator new(size_t siz) { return G_prsmem->alloc(siz); } /* ------------------------------------------------------------------------ */ /* * function symbol entry base */ /* * fold function name into a function address */ CTcPrsNode *CTcSymFuncBase::fold_constant() { CTcConstVal cval; /* set up the function pointer constant */ cval.set_funcptr((CTcSymFunc *)this); /* return a constant node with the function pointer */ return new CTPNConst(&cval); } /* * add an absolute fixup to my list */ void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(&fixups_, ds, ofs); } /* * add an absolute fixup at the current stream offset */ void CTcSymFuncBase::add_abs_fixup(CTcDataStream *ds) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(&fixups_, ds, ds->get_ofs()); } /* ------------------------------------------------------------------------ */ /* * local variable symbol entry base */ /* * initialize */ CTcSymLocalBase::CTcSymLocalBase(const char *str, size_t len, int copy, int is_param, int var_num) : CTcSymbol(str, len, copy, (is_param ? TC_SYM_PARAM : TC_SYM_LOCAL)) { /* remember the information */ var_num_ = var_num; param_index_ = 0; is_param_ = is_param; is_named_param_ = FALSE; is_opt_param_ = FALSE; is_list_param_ = FALSE; /* presume it's a regular stack variable (not a context local) */ is_ctx_local_ = FALSE; ctx_orig_ = 0; ctx_var_num_ = 0; ctx_level_ = 0; ctx_var_num_set_ = FALSE; /* presume there's no default value expression */ defval_expr_ = 0; /* so far, the value isn't used anywhere */ val_used_ = FALSE; val_assigned_ = FALSE; /* the symbol has not been referenced so far */ referenced_ = FALSE; /* remember where the symbol is defined in the source file */ G_tok->get_last_pos(&src_desc_, &src_linenum_); } /* * Mark the value of the variable as used */ void CTcSymLocalBase::set_val_used(int f) { /* note the new status */ val_used_ = f; /* if we have now assigned the value, propagate to the original */ if (f && ctx_orig_ != 0) ctx_orig_->set_val_used(TRUE); } /* * Mark the value of the variable as assigned */ void CTcSymLocalBase::set_val_assigned(int f) { /* note the new status */ val_assigned_ = f; /* if we have now assigned the value, propagate to the original */ if (f && ctx_orig_ != 0) ctx_orig_->set_val_assigned(TRUE); } /* * Check for references to this local */ void CTcSymLocalBase::check_local_references() { int err; tc_severity_t sev = TC_SEV_WARNING; /* * if this isn't an original, but is simply a copy of a variable * inherited from an enclosing scope (such as into an anonymous * function), don't bother even checking for errors - we'll let the * original symbol take care of reporting its own errors */ if (ctx_orig_ != 0) return; /* * Note if this is some kind of parameter. Some parameters are * represented as locals: varargs-list parameters, optional parameters, * and named parameters. We need to check for those kinds of * parameters specifically because they otherwise look like regular * local variables. */ int param = (is_param() || is_list_param() || is_opt_param() || is_named_param()); /* * If it's unreferenced or unassigned (or both), log an error; note * that a formal parameter is always assigned, since the value is * assigned by the caller. */ if (!get_val_used() && !get_val_assigned() && !param) { /* this is a regular local that was never assigned or referenced */ err = TCERR_UNREFERENCED_LOCAL; } else if (!get_val_used()) { if (param) { /* it's a parameter - generate only a pendantic error */ sev = TC_SEV_PEDANTIC; err = TCERR_UNREFERENCED_PARAM; } else { /* * this is a regular local with a value that was assigned and * never used */ err = TCERR_UNUSED_LOCAL_ASSIGNMENT; } } else if (!get_val_assigned() && !param) { /* it's used but never assigned */ err = TCERR_UNASSIGNED_LOCAL; } else { /* no error */ return; } /* * display the warning message, showing the error location as the * source line where the local was defined */ G_tcmain->log_error(get_src_desc(), get_src_linenum(), sev, err, (int)get_sym_len(), get_sym()); } /* * create a new context variable copy of this symbol */ CTcSymbol *CTcSymLocalBase::new_ctx_var() const { /* create a new local with the same name */ CTcSymLocal *lcl = new CTcSymLocal( get_sym(), get_sym_len(), FALSE, FALSE, 0); /* refer the copy back to the original (i.e., me) */ lcl->set_ctx_orig((CTcSymLocal *)this); /* set up the context variable information */ if (!is_ctx_local_) { /* * The original is a true local - we're at the first context * level, and we don't yet have a property assigned, since we * don't know if this variable is actually going to be * referenced. */ lcl->set_ctx_level(1); } else { /* * The original was already a context variable - we're at one * higher context level in this function, and we use the same * context property as the original did. */ lcl->set_ctx_level(ctx_level_ + 1); } /* return the new symbol */ return lcl; } /* * Apply context variable conversion */ int CTcSymLocalBase::apply_ctx_var_conv( CTcPrsSymtab *symtab, CTPNCodeBody *code_body) { /* * if this symbol isn't referenced, simply delete it from the table, * so that it doesn't get entered in the debug records; and there's * no need to propagate it back to the enclosing scope as a context * variable, since it's not referenced from this enclosed scope */ if (!referenced_) { /* remove the symbol from the table */ symtab->remove_entry(this); /* this variable doesn't need to be converted */ return FALSE; } /* * convert the symbol in the enclosing scope to a context local, if * it's not already */ if (ctx_orig_ != 0) { /* convert the original to a context symbol */ ctx_orig_->convert_to_ctx_var(get_val_used(), get_val_assigned()); /* * ask the code body for the context object's local variable for * our recursion level */ ctx_var_num_ = code_body->get_or_add_ctx_var_for_level(ctx_level_); /* note that we've set our context variable ID */ ctx_var_num_set_ = TRUE; /* this variable was converted */ return TRUE; } /* this variable wasn't converted */ return FALSE; } /* * convert this variable to a context variable */ void CTcSymLocalBase::convert_to_ctx_var(int val_used, int val_assigned) { /* if I'm not already a context local, mark me as a context local */ if (!is_ctx_local_) { /* mark myself as a context local */ is_ctx_local_ = TRUE; /* * we haven't yet assigned our local context variable, since * we're still processing the inner scope at this point; just * store placeholders for now so we know to come back and fix * this up */ ctx_var_num_ = 0; ctx_arr_idx_ = 0; } /* note that I've been referenced */ mark_referenced(); /* propagate the value-used and value-assigned flags */ if (val_used) set_val_used(TRUE); if (val_assigned) set_val_assigned(TRUE); /* propagate the conversion to the original symbol */ if (ctx_orig_ != 0) ctx_orig_->convert_to_ctx_var(val_used, val_assigned); } /* * finish the context variable conversion */ void CTcSymLocalBase::finish_ctx_var_conv() { /* * If this isn't already marked as a context variable, there's * nothing to do - this variable must not have been referenced from * an anonymous function yet, and hence can be kept in the stack. * * Similarly, if my context local variable number has been assigned * already, there's nothing to do - we must have been set to refer * back to a context variable in an enclosing scope (this can happen * in a nested anonymous function). */ if (!is_ctx_local_ || ctx_var_num_set_) return; /* * tell the parser to create a local context for this scope, if it * hasn't already */ G_prs->init_local_ctx(); /* use the local context variable specified by the parser */ ctx_var_num_ = G_prs->get_local_ctx_var(); ctx_var_num_set_ = TRUE; /* assign our array index */ if (ctx_arr_idx_ == 0) ctx_arr_idx_ = G_prs->alloc_ctx_arr_idx(); } /* * Get my context variable array index */ int CTcSymLocalBase::get_ctx_arr_idx() const { /* * if I'm based on an original symbol from another scope, use the * same property ID as the original symbol */ if (ctx_orig_ != 0) return ctx_orig_->get_ctx_arr_idx(); /* return my context property */ return ctx_arr_idx_; } /* ------------------------------------------------------------------------ */ /* * object symbol entry base */ /* * fold the symbol as a constant */ CTcPrsNode *CTcSymObjBase::fold_constant() { /* if it's not a TadsObject symbol, we can't fold it into a constant */ if (get_metaclass() != TC_META_TADSOBJ) return 0; /* set up the object constant */ CTcConstVal cval; cval.set_obj(get_obj_id(), get_metaclass()); /* return a constant node */ return new CTPNConst(&cval); } /* * Add a superclass name entry. */ void CTcSymObjBase::add_sc_name_entry(const char *txt, size_t len) { /* create the entry object */ CTPNSuperclass *entry = new CTPNSuperclass(txt, len); /* link it into our list */ if (sc_name_tail_ != 0) sc_name_tail_->nxt_ = entry; else sc_name_head_ = entry; sc_name_tail_ = entry; } /* * Check to see if I have a given superclass. */ int CTcSymObjBase::has_superclass(class CTcSymObj *sc_sym) const { /* * Scan my direct superclasses. For each one, check to see if my * superclass matches the given superclass, or if my superclass * inherits from the given superclass. */ for (CTPNSuperclass *entry = sc_name_head_ ; entry != 0 ; entry = entry->nxt_) { /* look up this symbol */ CTcSymObj *entry_sym = (CTcSymObj *)G_prs->get_global_symtab()->find( entry->get_sym_txt(), entry->get_sym_len()); /* * if the entry's symbol doesn't exist or isn't an object symbol, * skip it */ if (entry_sym == 0 || entry_sym->get_type() != TC_SYM_OBJ) continue; /* * if it matches the given superclass, we've found the given * superclass among our direct superclasses, so we definitely have * the given superclass */ if (entry_sym == sc_sym) return TRUE; /* * ask the symbol if the given class is among its direct or * indirect superclasses - if it's a superclass of my superclass, * it's also my superclass */ if (entry_sym->has_superclass(sc_sym)) return TRUE; } /* * we didn't find the given class anywhere among our superclasses or * their superclasses, so it must not be a superclass of ours */ return FALSE; } /* * Add a deleted property entry */ void CTcSymObjBase::add_del_prop_to_list(CTcObjPropDel **list_head, CTcSymProp *prop_sym) { /* create the new entry */ CTcObjPropDel *entry = new CTcObjPropDel(prop_sym); /* link it into my list */ entry->nxt_ = *list_head; *list_head = entry; } /* * Add a self-reference fixup */ void CTcSymObjBase::add_self_ref_fixup(CTcDataStream *stream, ulong ofs) { /* add a fixup to our list */ CTcIdFixup::add_fixup(&fixups_, stream, ofs, obj_id_); } /* * Add a word to my vocabulary */ void CTcSymObjBase::add_vocab_word(const char *txt, size_t len, tctarg_prop_id_t prop) { /* create a new vocabulary entry */ CTcVocabEntry *entry = new (G_prsmem) CTcVocabEntry(txt, len, prop); /* link it into my list */ entry->nxt_ = vocab_; vocab_ = entry; } /* * Delete a vocabulary property from my list (for 'replace') */ void CTcSymObjBase::delete_vocab_prop(tctarg_prop_id_t prop) { /* scan my list and delete each word defined for the given property */ for (CTcVocabEntry *prv = 0, *entry = vocab_, *nxt = 0 ; entry != 0 ; entry = nxt) { /* remember the next entry */ nxt = entry->nxt_; /* if this entry is for the given property, unlink it */ if (entry->prop_ == prop) { /* * it matches - unlink it from the list (note that we don't * have to delete the entry, because it's allocated in * parser memory and thus will be deleted when the parser is * deleted) */ if (prv != 0) prv->nxt_ = nxt; else vocab_ = nxt; /* * this entry is no longer in any list (we don't really have * to clear the 'next' pointer here, since nothing points to * 'entry' any more, but doing so will make it obvious that * the entry was removed from the list, which could be handy * during debugging from time to time) */ entry->nxt_ = 0; } else { /* * this entry is still in the list, so it's now the previous * entry for our scan */ prv = entry; } } } /* * Add my words to the dictionary, associating the words with the given * object. This can be used to add my own words to the dictionary or to * add my words to a subclass's dictionary. */ void CTcSymObjBase::inherit_vocab() { /* * if I've already inherited my superclass vocabulary, there's * nothing more we need to do */ if (vocab_inherited_) return; /* make a note that I've inherited my superclass vocabulary */ vocab_inherited_ = TRUE; /* inherit words from each superclass */ for (CTcObjScEntry *sc = sc_ ; sc != 0 ; sc = sc->nxt_) { /* make sure this superclass has built its inherited list */ sc->sym_->inherit_vocab(); /* add this superclass's words to my list */ sc->sym_->add_vocab_to_subclass((CTcSymObj *)this); } } /* * Add my vocabulary words to the given subclass's vocabulary list */ void CTcSymObjBase::add_vocab_to_subclass(CTcSymObj *sub) { /* add each of my words to the subclass */ for (CTcVocabEntry *entry = vocab_ ; entry != 0 ; entry = entry->nxt_) { /* add this word to my dictionary */ sub->add_vocab_word(entry->txt_, entry->len_, entry->prop_); } } /* * Set my base 'modify' object. This tells us the object that we're * modifying. */ void CTcSymObjBase::set_mod_base_sym(CTcSymObj *sym) { /* remember the object I'm modifying */ mod_base_sym_ = sym; /* * set the other object's link back to me, so it knows that I'm the * object that's modifying it */ if (sym != 0) sym->set_modifying_sym((CTcSymObj *)this); } /* * Get the appropriate stream for a given metaclass */ CTcDataStream *CTcSymObjBase::get_stream_from_meta(tc_metaclass_t meta) { switch(meta) { case TC_META_TADSOBJ: /* it's the regular object stream */ return G_os; case TC_META_ICMOD: /* intrinsic class modifier stream */ return G_icmod_stream; default: /* other metaclasses have no stream */ return 0; } } /* * Add a class-specific template */ void CTcSymObjBase::add_template(CTcObjTemplate *tpl) { /* link it in at the tail of our list */ if (template_tail_ != 0) template_tail_->nxt_ = tpl; else template_head_ = tpl; template_tail_ = tpl; } /* ------------------------------------------------------------------------ */ /* * metaclass symbol */ /* * add a property */ void CTcSymMetaclassBase::add_prop( const char *txt, size_t len, const char *obj_fname, int is_static) { /* see if this property is already defined */ CTcSymProp *prop_sym = (CTcSymProp *)G_prs->get_global_symtab()->find(txt, len); if (prop_sym != 0) { /* it's already defined - make sure it's a property */ if (prop_sym->get_type() != TC_SYM_PROP) { /* * it's something other than a property - log the * appropriate type of error, depending on whether we're * loading this from an object file or from source code */ if (obj_fname == 0) { /* creating from source - note the code location */ G_tok->log_error_curtok(TCERR_REDEF_AS_PROP); } else { /* loading from an object file */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_REDEF_SYM_TYPE, (int)len, txt, "property", obj_fname); } /* forget the symbol - it's not a property */ prop_sym = 0; } } else { /* add the property definition */ prop_sym = new CTcSymProp(txt, len, FALSE, G_cg->new_prop_id()); G_prs->get_global_symtab()->add_entry(prop_sym); } /* * if we found a valid property symbol, add it to the metaclass * property list */ if (prop_sym != 0) { /* * mark the symbol as referenced - even if we don't directly * make use of it, the metaclass table references this symbol */ prop_sym->mark_referenced(); /* add the property to the metaclass list */ add_prop(prop_sym, is_static); } } /* * add a property */ void CTcSymMetaclassBase::add_prop(class CTcSymProp *prop, int is_static) { /* create a new list entry for the property */ CTcSymMetaProp *entry = new (G_prsmem) CTcSymMetaProp(prop, is_static); /* link it at the end of our list */ if (prop_tail_ != 0) prop_tail_->nxt_ = entry; else prop_head_ = entry; prop_tail_ = entry; /* count the addition */ ++prop_cnt_; } /* * get the nth property in our table */ CTcSymMetaProp *CTcSymMetaclassBase::get_nth_prop(int n) const { /* traverse the list to the desired index */ CTcSymMetaProp *prop; for (prop = prop_head_ ; prop != 0 && n != 0 ; prop = prop->nxt_, --n) ; /* return the property */ return prop; } /* ------------------------------------------------------------------------ */ /* * property symbol entry base */ /* * fold an address constant */ CTcPrsNode *CTcSymPropBase::fold_addr_const() { /* set up the property pointer constant */ CTcConstVal cval; cval.set_prop(get_prop()); /* return a constant node */ return new CTPNConst(&cval); } /* ------------------------------------------------------------------------ */ /* * Enumerator symbol base */ /* * fold the symbol as a constant */ CTcPrsNode *CTcSymEnumBase::fold_constant() { /* set up the enumerator constant */ CTcConstVal cval; cval.set_enum(get_enum_id()); /* return a constant node */ return new CTPNConst(&cval); } /* ------------------------------------------------------------------------ */ /* * Parser dictionary hash table entry */ /* * add an item to my list of object associations */ void CVmHashEntryPrsDict::add_item(tc_obj_id obj, tc_prop_id prop) { /* search my list for an existing association to the same obj/prop */ CTcPrsDictItem *item; for (item = list_ ; item != 0 ; item = item->nxt_) { /* if it matches, we don't need to add this one again */ if (item->obj_ == obj && item->prop_ == prop) return; } /* not found - create a new item */ item = new (G_prsmem) CTcPrsDictItem(obj, prop); /* link it into my list */ item->nxt_ = list_; list_ = item; } /* ------------------------------------------------------------------------ */ /* * Code Body Parse Node */ /* * instantiate */ CTPNCodeBodyBase::CTPNCodeBodyBase( CTcPrsSymtab *lcltab, CTcPrsSymtab *gototab, CTPNStm *stm, int argc, int opt_argc, int varargs, int varargs_list, CTcSymLocal *varargs_list_local, int local_cnt, int self_valid, CTcCodeBodyRef *enclosing_code_body) { /* remember the data in the code body */ lcltab_ = lcltab; gototab_ = gototab; stm_ = stm; argc_ = argc; opt_argc_ = opt_argc; varargs_ = varargs; varargs_list_ = varargs_list; varargs_list_local_ = varargs_list_local; local_cnt_ = local_cnt; self_valid_ = self_valid; self_referenced_ = FALSE; full_method_ctx_referenced_ = FALSE; op_overload_ = FALSE; is_anon_method_ = FALSE; is_dyn_func_ = FALSE; is_dyn_method_ = FALSE; /* remember the enclosing code body */ enclosing_code_body_ = enclosing_code_body; /* presume we won't need a local context object */ has_local_ctx_ = FALSE; local_ctx_arr_size_ = 0; ctx_head_ = ctx_tail_ = 0; local_ctx_needs_self_ = FALSE; local_ctx_needs_full_method_ctx_ = FALSE; /* presume we have an internal fixup list */ fixup_owner_sym_ = 0; fixup_list_anchor_ = &internal_fixups_; /* no internal fixups yet */ internal_fixups_ = 0; /* we haven't been replaced yet */ replaced_ = FALSE; /* leave it up to the caller to set the starting location */ start_desc_ = 0; start_linenum_ = 0; /* * remember the source location of the closing brace, which should * be the current location when we're instantiated */ end_desc_ = G_tok->get_last_desc(); end_linenum_ = G_tok->get_last_linenum(); } /* * fold constants */ CTcPrsNode *CTPNCodeBodyBase::fold_constants(class CTcPrsSymtab *) { /* * fold constants in our compound statement, in the scope of our * local symbol table */ if (stm_ != 0) stm_->fold_constants(lcltab_); /* we are not directly changed by this operation */ return this; } /* * Check for unreferenced labels */ void CTPNCodeBodyBase::check_unreferenced_labels() { /* * enumerate our labels - skip this check if we're only parsing the * program for syntax */ if (gototab_ != 0 && !G_prs->get_syntax_only()) gototab_->enum_entries(&unref_label_cb, this); } /* * Callback for enumerating labels for checking for unreferenced labels */ void CTPNCodeBodyBase::unref_label_cb(void *, CTcSymbol *sym) { /* if it's a label, check it out */ if (sym->get_type() == TC_SYM_LABEL) { CTcSymLabel *lbl = (CTcSymLabel *)sym; /* * get its underlying statement, and make sure it has a * control-flow flag for goto, continue, or break */ if (lbl->get_stm() != 0) { ulong flags; /* * get the explicit control flow flags for this statement -- * these flags indicate the use of the label in a goto, * break, or continue statement */ flags = lbl->get_stm()->get_explicit_control_flow_flags(); /* * if the flags aren't set for at least one of the explicit * label uses, the label is unreferenced */ if ((flags & (TCPRS_FLOW_GOTO | TCPRS_FLOW_BREAK | TCPRS_FLOW_CONT)) == 0) lbl->get_stm()->log_warning(TCERR_UNREFERENCED_LABEL, (int)lbl->get_sym_len(), lbl->get_sym()); } } } /* * add an absolute fixup to my list */ void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds, ulong ofs) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ofs); } /* * add an absolute fixup at the current stream offset */ void CTPNCodeBodyBase::add_abs_fixup(CTcDataStream *ds) { /* ask the code body to add the fixup */ CTcAbsFixup::add_abs_fixup(fixup_list_anchor_, ds, ds->get_ofs()); } /* * Get the context variable for a given level */ int CTPNCodeBodyBase::get_or_add_ctx_var_for_level(int level) { /* scan our list to see if the level is already assigned */ CTcCodeBodyCtx *ctx; for (ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) { /* if we've already set up this level, return its variable */ if (ctx->level_ == level) return ctx->var_num_; } /* we didn't find it - allocate a new level structure */ ctx = new (G_prsmem) CTcCodeBodyCtx(); /* set up its level and allocate a new variable and property for it */ ctx->level_ = level; ctx->var_num_ = G_prs->alloc_ctx_holder_var(); /* * allocating a new variable probably increased the maximum local * variable count - update our information from the parser */ local_cnt_ = G_prs->get_max_local_cnt(); /* link it into our list */ ctx->prv_ = ctx_tail_; ctx->nxt_ = 0; if (ctx_tail_ != 0) ctx_tail_->nxt_ = ctx; else ctx_head_ = ctx; ctx_tail_ = ctx; /* return the variable for the new level */ return ctx->var_num_; } /* * Find a local context for a given level */ int CTPNCodeBodyBase::get_ctx_var_for_level(int level, int *varnum) { /* if they want level zero, it's our local context */ if (level == 0) { /* set the variable ID to our local context variable */ *varnum = local_ctx_var_; /* return true only if we actually have a local context */ return has_local_ctx_; } /* scan our list to see if the level is already assigned */ for (CTcCodeBodyCtx *ctx = ctx_head_ ; ctx != 0 ; ctx = ctx->nxt_) { /* if we've already set up this level, return its variable */ if (ctx->level_ == level) { /* set the caller's variable number */ *varnum = ctx->var_num_; /* indicate that we found it */ return TRUE; } } /* didn't find it */ return FALSE; } /* * Get the immediately enclosing code body */ CTPNCodeBody *CTPNCodeBodyBase::get_enclosing() const { /* * if we have no enclosing code body reference, we have no enclosing * code body */ if (enclosing_code_body_ == 0) return 0; /* get the code body from my enclosing code body reference object */ return enclosing_code_body_->ptr; } /* * Get the outermost enclosing code body */ CTPNCodeBody *CTPNCodeBodyBase::get_outermost_enclosing() const { CTPNCodeBody *cur; CTPNCodeBody *nxt; /* * scan each enclosing code body until we find one without an enclosing * code body */ for (cur = 0, nxt = get_enclosing() ; nxt != 0 ; cur = nxt, nxt = nxt->get_enclosing()) ; /* return what we found */ return cur; } /* * Get the base function symbol for a code body defining a modified * function (i.e., 'modify ...'). This is the function to which * 'replaced' refers within this code body and within nested code bodies. */ class CTcSymFunc *CTPNCodeBodyBase::get_replaced_func() const { CTcSymFunc *b; CTPNCodeBody *enc; /* if we have an associated function symbol, it's the base function */ if ((b = get_func_sym()) != 0) return b->get_mod_base(); /* * if we have an enclosing code body, then 'replaced' here means the * same thing it does there, since we don't explicitly replace anything * here */ if ((enc = get_enclosing()) != 0) return enc->get_replaced_func(); /* if we haven't found anything yet, we don't have a base function */ return 0; } /* ------------------------------------------------------------------------ */ /* * Anonymous function */ /* * mark as replaced/obsolete */ void CTPNAnonFuncBase::set_replaced(int flag) { if (code_body_ != 0) code_body_->set_replaced(flag); } /* ------------------------------------------------------------------------ */ /* * Generic statement node */ /* * initialize at the tokenizer's current source file position */ CTPNStmBase::CTPNStmBase() { /* get the current source location from the parser */ init(G_prs->get_cur_desc(), G_prs->get_cur_linenum()); } /* * log an error at this statement's source file position */ void CTPNStmBase::log_error(int errnum, ...) const { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(file_, linenum_, TC_SEV_ERROR, errnum, marker); va_end(marker); } /* * log a warning at this statement's source file position */ void CTPNStmBase::log_warning(int errnum, ...) const { va_list marker; /* display the message */ va_start(marker, errnum); G_tcmain->v_log_error(file_, linenum_, TC_SEV_WARNING, errnum, marker); va_end(marker); } /* * Generate code for a sub-statement */ void CTPNStmBase::gen_code_substm(CTPNStm *substm) { /* set the error reporting location to refer to the sub-statement */ G_tok->set_line_info(substm->get_source_desc(), substm->get_source_linenum()); /* generate code for the sub-statement */ substm->gen_code(TRUE, TRUE); /* restore the error reporting location to the main statement */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); } /* ------------------------------------------------------------------------ */ /* * superclass record */ /* * get my symbol */ CTcSymbol *CTPNSuperclass::get_sym() const { /* if we know the symbol already, return it directly */ if (sym_ != 0) return sym_; /* look up my symbol by name in the global symbol table */ return G_prs->get_global_symtab()->find(sym_txt_, sym_len_); } /* * am I a subclass of the given class? */ int CTPNSuperclass::is_subclass_of(const CTPNSuperclass *other) const { /* * if my name matches, we're a subclass (we are a subclass of * ourselves) */ if (other->sym_len_ == sym_len_ && memcmp(other->sym_txt_, sym_txt_, sym_len_) == 0) return TRUE; /* * We're a subclass if any of our superclasses are subclasses of the * given object. Get my object symbol, and make sure it's really a * tads-object - if it's not, we're definitely not a subclass of * anything. */ CTcSymObj *sym = (CTcSymObj *)get_sym(); if (sym == 0 || sym->get_type() != TC_SYM_OBJ || sym->get_metaclass() != TC_META_TADSOBJ) return FALSE; /* scan our symbol's superclass list for a match */ for (CTPNSuperclass *sc = sym->get_sc_name_head() ; sc != 0 ; sc = sc->nxt_) { /* * if this one's a subclass of the given class, we're a subclass * as well, since we're a subclass of this superclass */ if (sc->is_subclass_of(other)) return TRUE; } /* * we didn't find any superclass that's a subclass of the given * class, so we're not a subclass of the given class */ return FALSE; } /* ------------------------------------------------------------------------ */ /* * 'return' statement */ /* * fold constants */ CTcPrsNode *CTPNStmReturnBase::fold_constants(CTcPrsSymtab *symtab) { /* set our location for any errors that occur */ G_tok->set_line_info(get_source_desc(), get_source_linenum()); /* fold constants in the expression, if we have one */ if (expr_ != 0) expr_ = expr_->fold_constants(symtab); /* we are not directly changed by this operation */ return this; } /* ------------------------------------------------------------------------ */ /* * Formal type list */ /* * add a typed parameter to the list - 'tok' is the symbol giving the type * name */ void CTcFormalTypeList::add_typed_param(const CTcToken *tok) { add(new (G_prsmem) CTcFormalTypeEle(tok->get_text(), tok->get_text_len())); } /* add an untyped parameter to the list */ void CTcFormalTypeList::add_untyped_param() { add(new (G_prsmem) CTcFormalTypeEle()); } /* add a list element */ void CTcFormalTypeList::add(CTcFormalTypeEle *ele) { /* link it into our list */ if (tail_ != 0) tail_->nxt_ = ele; else head_ = ele; tail_ = ele; ele->nxt_ = 0; } /* * create a decorated name token for the multi-method defined by the given * function name and our type list */ void CTcFormalTypeList::decorate_name(CTcToken *decorated_name, const CTcToken *func_base_name) { CTcFormalTypeEle *ele; size_t len; const char *p; /* figure out how much space we need for the decorated name */ for (len = func_base_name->get_text_len() + 1, ele = head_ ; ele != 0 ; ele = ele->nxt_) { /* add this type name's length, if there's a name */ if (ele->name_ != 0) len += ele->name_len_; /* add a semicolon after the type */ len += 1; } /* add "...;" if it's varargs */ if (varargs_) len += 4; /* allocate space for the name */ G_tok->reserve_source(len); /* start with the function name */ p = G_tok->store_source_partial(func_base_name->get_text(), func_base_name->get_text_len()); /* add a "*" separator for the multi-method indicator */ G_tok->store_source_partial("*", 1); /* add each type name */ for (ele = head_ ; ele != 0 ; ele = ele->nxt_) { /* add the type, if it has one (if not, leave the type empty) */ if (ele->name_ != 0) G_tok->store_source_partial(ele->name_, ele->name_len_); /* add a semicolon to terminate the parameter name */ G_tok->store_source_partial(";", 1); } /* add the varargs indicator ("...;"), if applicable */ if (varargs_) G_tok->store_source_partial("...;", 4); /* null-terminate it */ G_tok->store_source_partial("\0", 1); /* set the decorated token name */ decorated_name->settyp(TOKT_SYM); decorated_name->set_text(p, len); } /* formal list element - construction */ CTcFormalTypeEle::CTcFormalTypeEle(const char *name, size_t len) { name_ = new (G_prsmem) char[len + 1]; memcpy(name_, name, len); name_len_ = len; } frobtads-1.2.3/tads3/tcunas.h0000644000175000001440000000600612145504453015175 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCUNAS.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tcunas.h - TADS 3 Compiler Unassembler Function Notes Modified 05/10/99 MJRoberts - Creation */ #ifndef TCUNAS_H #define TCUNAS_H #include #include #include "t3std.h" #include "tcgen.h" /* ------------------------------------------------------------------------ */ /* * byte-code source for unassembler */ class CTcUnasSrc { public: virtual ~CTcUnasSrc() { } /* * read the next byte; returns zero on success, non-zero at the end * of the byte stream */ virtual int next_byte(char *ch) = 0; /* get the current offset */ virtual ulong get_ofs() const = 0; }; /* * code stream implementation of byte code source */ class CTcUnasSrcCodeStr: public CTcUnasSrc { public: CTcUnasSrcCodeStr(CTcCodeStream *str) { /* remember our underlying code stream */ str_ = str; /* start at the first byte of the code stream */ ofs_ = 0; } /* read from the code stream */ int next_byte(char *ch) { /* if there's anything left, return the byte and bump the pointer */ if (ofs_ < str_->get_ofs()) { *ch = str_->get_byte_at(ofs_); ++ofs_; return 0; } /* indicate end of file */ return 1; } /* get the current offset */ ulong get_ofs() const { return ofs_; } protected: /* underlying code stream object */ CTcCodeStream *str_; /* current read offset in code stream */ ulong ofs_; }; /* ------------------------------------------------------------------------ */ /* * output stream for unassembler */ class CTcUnasOut { public: virtual ~CTcUnasOut() { } /* write a line of text to the output, printf-style */ virtual void print(const char *fmt, ...) = 0; }; /* * stdio implementation of output stream - writes data to standard * output */ class CTcUnasOutStdio: public CTcUnasOut { public: void print(const char *fmt, ...) { va_list va; /* display the data on the standard output */ va_start(va, fmt); vprintf(fmt, va); va_end(va); } }; /* * Text file (osfildef) implementation of output stream. The file handle * is managed by the caller. */ class CTcUnasOutFile: public CTcUnasOut { public: CTcUnasOutFile(osfildef *fp) { fp_ = fp; } void print(const char *fmt, ...) { char buf[1024]; va_list va; /* format the text */ va_start(va, fmt); t3vsprintf(buf, sizeof(buf), fmt, va); va_end(va); /* write the formatted text to the file */ os_fprintz(fp_, buf); } protected: /* our file handle */ osfildef *fp_; }; #endif /* TCUNAS_H */ frobtads-1.2.3/tads3/vmhostsi.h0000644000175000001440000000516311713617232015557 0ustar realncusers/* $Header$ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name vmhostsi.h - simple stdio-based VM host application environment Function Provides a simple implementation of the VM host application interface. This implementation is suitable for simple command-line tools with minimal user interface; more complete implementations should be used for most applications that embed the VM. Notes Modified 07/29/99 MJRoberts - Creation */ #ifndef VMHOSTSI_H #define VMHOSTSI_H #include "vmhost.h" #include "vmhosttx.h" class CVmHostIfcStdio: public CVmHostIfcText { public: /* create */ CVmHostIfcStdio(const char *argv0); /* delete */ virtual ~CVmHostIfcStdio(); /* get the I/O safety level */ virtual int get_io_safety_read() { return io_safety_read_; } virtual int get_io_safety_write() { return io_safety_write_; } /* set I/O safety level */ virtual void set_io_safety(int read_level, int write_level) { io_safety_read_ = read_level; io_safety_write_ = write_level; } /* get the network safety level */ virtual void get_net_safety(int *client_level, int *server_level) { *client_level = net_client_safety_; *server_level = net_server_safety_; } /* set the network safety level */ virtual void set_net_safety(int client_level, int server_level) { net_client_safety_ = client_level; net_server_safety_ = server_level; } /* get the system resource loader */ virtual class CResLoader *get_sys_res_loader() { return sys_res_loader_; } /* get the resource path */ virtual const char *get_res_path() { return 0; } /* get an image file name */ virtual vmhost_gin_t get_image_name(char *, size_t) { return VMHOST_GIN_IGNORED; } /* get a special file system path */ virtual void get_special_file_path(char *buf, size_t buflen, int id) { os_get_special_path(buf, buflen, argv0_, id); } protected: /* * the original main program's argv[0] - we need to remember this * because it's sometimes needed to resolve special file system paths * on the local system */ char *argv0_; /* system resource loader (character maps, etc) */ class CResLoader *sys_res_loader_; /* current I/O safety levels */ int io_safety_read_; int io_safety_write_; /* current network safety levels */ int net_client_safety_; int net_server_safety_; }; #endif /* VMHOSTSI_H */ frobtads-1.2.3/tads3/tctarg.h0000644000175000001440000000762611146543321015172 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCTARG.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tctarg.h - TADS 3 Compiler Target Selector Function Notes Modified 04/30/99 MJRoberts - Creation */ #ifndef TCTARG_H #define TCTARG_H /* ------------------------------------------------------------------------ */ /* * Target Selection. * * SEE ALSO: tctargty.h - target type header selector * * As the parser scans the source stream, it constructs a parse tree * that represent the parsed form of the source. After the parsing * pass, the parse tree contains all of the information necessary to * generate code for the translation. * * The parse tree objects are actually the code generators. So, as the * scanner parses the source file, it must create parse tree objects for * the appropriate target architecture. However, we want to keep the * scanner independent of the target architecture -- we want the same * scanner to be usable for any target architecture for which we can * provide a code generator. * * To accomplish this, we define a base class for parse tree nodes; the * scanner is only interested in this base class interface. Then, for * each target architecture, we create a subclass of each parse tree * node that contains the code generator for that node type for the * target. * * However, the scanner must still know enough to create the appropriate * subclass of each parse tree node. This file contains the target * selection switch that mediates the independence of the scanner from * the target code generator, but still allows the scanner to create the * correct type of parse tree nodes for the desired target. For each * parse tree node type that the scanner must create, we #define a * generic symbol to a target-specific subclass. The scanner uses the * generic symbol, but we expand the macro when compiling the compiler * to the correct target. * * Because the target selection is made through macros, the target * architecture is fixed at compile time. However, the same scanner * source code can be compiled into multiple target compilers. */ /* * Before including this file, #define the appropriate target type. * This should usually be done in the makefile, since this is a * compile-time selection. * * To add a new code generator, define the appropriate subclasses in a * new file. Add a new #ifdef-#include sequence below that includes the * subclass definitions for your code generator. * * Note that each target uses the same names for the final subclasses. * We choose the target at link time when building the compiler * executable, so we must compile and link only a single target * architecture into a given build of the compiler. */ /* * make sure TC_TARGET_DEFINED__ isn't defined, so we can use it to * sense whether we defined a code generator or not */ #undef TCTARG_TARGET_DEFINED__ /* ------------------------------------------------------------------------ */ /* * T3 Virtual Machine Code Generator */ #ifdef TC_TARGET_T3 #include "tct3.h" #define TCTARG_TARGET_DEFINED__ #endif /* ------------------------------------------------------------------------ */ /* * JavaScript code generator */ #ifdef TC_TARGET_JS #include "tcjs.h" #define TCTARG_TARGET_DEFINED__ #endif /* ------------------------------------------------------------------------ */ /* * ensure that a code generator was defined - if not, the compilation * cannot proceed */ #ifndef TCTARG_TARGET_DEFINED__ #error No code generator target is defined. A code generator must be defined in your makefile. See tctarg.h for details. #endif #endif /* TCTARG_H */ frobtads-1.2.3/tads3/vmintcls.h0000644000175000001440000002641712007540750015544 0ustar realncusers/* $Header$ */ /* Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. */ /* Name vmintcls.h - T3 metaclass - intrinsic class Function Notes Modified 03/08/00 MJRoberts - Creation */ #ifndef VMINTCLS_H #define VMINTCLS_H #include #include "vmtype.h" #include "vmobj.h" #include "vmglob.h" /* * An IntrinsicClass object represents the class of an instance of an * intrinsic class. For example, if we create a BigNumber instance, then * ask for its class, the result is the IntrinsicClass object associated * with the BigNumber intrinsic class: * * Each metaclass in the metaclass dependency table will be associated with * an IntrinsicClass object. * * The image file format for an IntrinsicClass object consists of the * following: * * UINT2 byte_count (currently, this is always 8) *. UINT2 metaclass_dependency_table_index *. UINT4 modifier_object_id *. DATAHOLDER class_state * * The 'class_state' entry is a generic data holder value for use by the * intrinsic class for storing static class state. We save and restore * this value automatically and can keep undo information for it. This can * be used to implement static class-level setprop/getprop to store special * values associated with the class object. */ /* intrinsic class extension */ struct vm_intcls_ext { /* metaclass dependency table index */ int meta_idx; /* modifier object */ vm_obj_id_t mod_obj; /* class state */ vm_val_t class_state; /* has the class state value changed since load time? */ int changed; }; /* * intrinsic class object */ class CVmObjClass: public CVmObject { friend class CVmMetaclassClass; public: /* metaclass registration object */ static class CVmMetaclass *metaclass_reg_; class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } /* am I of the given metaclass? */ virtual int is_of_metaclass(class CVmMetaclass *meta) const { /* try my own metaclass and my base class */ return (meta == metaclass_reg_ || CVmObject::is_of_metaclass(meta)); } /* is the given object an IntrinsicClass object? */ static int is_intcls_obj(VMG_ vm_obj_id_t obj) { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); } /* * get my metaclass registration table index - this can be compared to * the metaclass_reg_ element for a given C++ intrinsic class * implementation to determine if this intrinsic class object is the * intrinsic class object for a given C++ intrinsic class */ uint get_meta_idx() const { return get_ext()->meta_idx; } /* get my metaclass table entry */ struct vm_meta_entry_t *get_meta_entry(VMG0_) const; /* * Get the class state value. This is for use by the class * implementation to store static (class) state. */ const vm_val_t *get_class_state() { return &get_ext()->class_state; } /* Set the class state value, without saving undo. */ void set_class_state(const vm_val_t *val) { get_ext()->class_state = *val; get_ext()->changed = TRUE; } /* * Set the class state value with undo. This is unimplemented for now, * since no one needs it. In principle it could be useful to be able * to store a scalar value in the class state slot, in which case we'd * need to have special code here in order to undo changes. So far, * though, we always store an object reference in class_state, such as * a Vector or TadsObject, since we need to be able to store more than * just a single scalar value. Since all of the state data are then * stored within those container objects, and the container classes we * use all handle undo themselves, there's no need for any undo action * at the class_state level. */ // Unimplemented! void set_class_state_undo(VMG_ class CVmUndo *undo, const vm_val_t *val); /* create dynamically using stack arguments */ static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc); /* create for a given dependency table index */ static vm_obj_id_t create_dyn(VMG_ uint meta_idx); /* * call a static property - we don't have any of our own, so simply * "inherit" the base class handling */ static int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop); /* determine if I'm an instance of the given object */ virtual int is_instance_of(VMG_ vm_obj_id_t obj); /* get superclass information */ int get_superclass_count(VMG_ vm_obj_id_t self) const; vm_obj_id_t get_superclass(VMG_ vm_obj_id_t self, int idx) const; /* * Determine if this is a class object. All intrinsic class objects * indicate true. */ virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const { return TRUE; } /* reserve constant data */ virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other data and cannot be converted to constant * data ourselves, so there's nothing to do here */ } /* convert to constant data */ virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, vm_obj_id_t self) { /* * we reference no other data and cannot be converted to constant * data ourselves, so there's nothing to do here */ } /* create with no initial contents */ static vm_obj_id_t create(VMG_ int in_root_set); /* notify of deletion */ void notify_delete(VMG_ int in_root_set); /* set a property */ void set_prop(VMG_ class CVmUndo *undo, vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); /* get a property */ int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* build a list of my properties */ void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval); /* undo operations - classes are immutable and hence keep no undo */ void notify_new_savept() { } void apply_undo(VMG_ struct CVmUndoRecord *) { } void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } /* mark references */ void mark_refs(VMG_ uint state); /* remove weak references - we have none */ void remove_stale_weak_refs(VMG0_) { } /* have we changed since load time? */ int is_changed_since_load() const { return get_ext()->changed; } /* load/reload from an image file */ void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); void reload_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* rebuild for image file */ virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); /* save to a file */ void save_to_file(VMG_ class CVmFile *fp); /* restore from a file */ void restore_from_file(VMG_ vm_obj_id_t self, class CVmFile *fp, class CVmObjFixup *fixups); /* get the user modifier object for the intrinsic class */ vm_obj_id_t get_mod_obj() const { return get_ext()->mod_obj; } /* * find the intrinsic class for the given modifier object, searching * myself and my intrinsic superclasses */ vm_obj_id_t find_mod_src_obj(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj); /* static method: isIntrinsicClass(x) */ static int s_getp_is_int_class(VMG_ vm_val_t *result, uint *argc); protected: /* create with no initial contents */ CVmObjClass() { ext_ = 0; } /* create with a given dependency table index */ CVmObjClass(VMG_ int in_root_set, uint meta_idx, vm_obj_id_t self); /* allocate or reallocate my extension */ vm_intcls_ext *alloc_ext(VMG0_); /* get my extension */ vm_intcls_ext *get_ext() const { return (vm_intcls_ext *)ext_; } /* load/reload the image data */ void load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); /* list our intrinsic class's properties */ size_t list_class_props(VMG_ vm_obj_id_t self, struct vm_meta_entry_t *entry, class CVmObjList *lst, size_t starting_idx, int static_only); /* * Find the intrinsic class for the given modifier object. We override * this because we want an intrinsic class's effective intrinsic class * to be its intrinsic superclass, not its metaclass. The metaclass of * an intrinsic class object is always IntrinsicClass; instead, we want * to see, for example, List->Collection->Object, which is the * intrinsic superclass hierarchy. */ vm_obj_id_t find_intcls_for_mod(VMG_ vm_obj_id_t self, vm_obj_id_t mod_obj) { /* * The implementation is very simple: just look for a modifier * object attached to this object or one of its intrinsic * superclasses. The difference between this and the regular * CVmObject implementation is that the CVmObject implementation * looks in the object's metaclass; we simply look in our intrinsic * superclasses directly, since, for reflection purposes, we are * our own metaclass. */ return find_mod_src_obj(vmg_ self, mod_obj); } /* * search for a property among our modifiers, searching our own * modifier and modifiers for our superclasses */ int get_prop_from_mod(VMG_ vm_prop_id_t prop, vm_val_t *val, vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); /* register myself with the dependency table */ void register_meta(VMG_ vm_obj_id_t self); }; /* ------------------------------------------------------------------------ */ /* * Registration table object */ class CVmMetaclassClass: public CVmMetaclass { public: /* get the global name */ const char *get_meta_name() const { return "intrinsic-class/030001"; } /* create from image file */ void create_for_image_load(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjClass(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create from restoring from saved state */ void create_for_restore(VMG_ vm_obj_id_t id) { new (vmg_ id) CVmObjClass(); G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); } /* create dynamically using stack arguments */ vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) { return CVmObjClass::create_from_stack(vmg_ pc_ptr, argc); } /* call a static property */ int call_stat_prop(VMG_ vm_val_t *result, const uchar **pc_ptr, uint *argc, vm_prop_id_t prop) { return CVmObjClass::call_stat_prop(vmg_ result, pc_ptr, argc, prop); } }; #endif /* VMINTCLS_H */ /* * Register the class */ VM_REGISTER_METACLASS(CVmObjClass) frobtads-1.2.3/tads3/tct3.h0000644000175000001440000012113411774034131014553 0ustar realncusers/* $Header: d:/cvsroot/tads/tads3/TCT3.H,v 1.5 1999/07/11 00:46:55 MJRoberts Exp $ */ /* * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name tct3.h - TADS 3 compiler - T3 Virtual Machine Code Generator Function Notes Modified 04/30/99 MJRoberts - Creation */ #ifndef TCT3_H #define TCT3_H #include "t3std.h" #include "tcprs.h" #include "vmop.h" #include "vmtype.h" #include "tct3ty.h" /* ------------------------------------------------------------------------ */ /* * include the T3-specific CVmRuntimeSymbols class definition */ #include "vmrunsym.h" /* ------------------------------------------------------------------------ */ /* * include the T3-specific final parse node classes */ #include "tct3drv.h" /* ------------------------------------------------------------------------ */ /* * Define some internal compiler datatypes - any type * VM_FIRST_INVALID_TYPE or higher is not used by the VM and can thus be * used for our own internal types. */ const vm_datatype_t VM_VOCAB_LIST = VM_MAKE_INTERNAL_TYPE(0); /* ------------------------------------------------------------------------ */ /* * Data structure sizes. These are the sizes of various data structures * that we write to the image file; these values are global to the * entire image file, and are constants of the current file format. */ /* method header size */ #define TCT3_METHOD_HDR_SIZE 10 /* exception table entry size */ #define TCT3_EXC_ENTRY_SIZE 10 /* debugger line record entry size */ #define TCT3_LINE_ENTRY_SIZE 10 /* debug table header size */ #define TCT3_DBG_HDR_SIZE 0 /* debugger local symbol record size */ #define TCT3_DBG_LCLSYM_HDR_SIZE 6 /* debugger frame record header size */ #define TCT3_DBG_FRAME_SIZE 8 /* debugger record format version */ #define TCT3_DBG_FMT_VSN 2 /* ------------------------------------------------------------------------ */ /* * Object file header flags. */ #define TCT3_OBJHDR_DEBUG 0x0001 /* ------------------------------------------------------------------------ */ /* * Object Stream prefix flags. Each object we write to the object * stream starts with a prefix byte that we use to store some extra flag * information about the object. */ /* object has been replaced - do not write to image file */ #define TCT3_OBJ_REPLACED 0x0001 /* object has been modified */ #define TCT3_OBJ_MODIFIED 0x0002 /* object is transient */ #define TCT3_OBJ_TRANSIENT 0x0004 /* ------------------------------------------------------------------------ */ /* * T3 metaclass object stream header sizes */ /* * internal header size - this is an extra header the compiler adds for * each object in an object stream * * UINT2 compiler_flags */ const size_t TCT3_OBJ_INTERNHDR_SIZE = 2; /* offset of the internal header from the start of the object data */ const size_t TCT3_OBJ_INTERNHDR_OFS = 0; /* offset of internal header flags */ const size_t TCT3_OBJ_INTERNHDR_FLAGS_OFS = TCT3_OBJ_INTERNHDR_OFS; /* * T3 generic metaclass header - this is the header on every metaclass * in a T3 image file 'OBJS' block. * * UINT4 object_id *. UINT2 metaclass_specific_byte_count */ const size_t TCT3_META_HEADER_SIZE = 6; /* * large metaclass header size - this is the same as the standard * header, but uses a 32-bit size field rather than the 16-bit size * field */ const size_t TCT3_LARGE_META_HEADER_SIZE = 8; /* offset of generic metaclass header from the start of an object's data */ const size_t TCT3_META_HEADER_OFS = TCT3_OBJ_INTERNHDR_OFS + TCT3_OBJ_INTERNHDR_SIZE; /* ------------------------------------------------------------------------ */ /* * tads-object metaclass object stream header sizes */ /* * tads-object header - each object with metaclass tads-object defines * this header * *. UINT2 superclass_count *. UINT2 property_count *. UINT2 object_flags */ const size_t TCT3_TADSOBJ_HEADER_SIZE = 6; /* offset of the tads-object header from the start of an object's data */ const size_t TCT3_TADSOBJ_HEADER_OFS = TCT3_META_HEADER_OFS + TCT3_META_HEADER_SIZE; /* * offset to the superclass table, which immediately follows the * tads-object header */ const size_t TCT3_TADSOBJ_SC_OFS = TCT3_TADSOBJ_HEADER_OFS + TCT3_TADSOBJ_HEADER_SIZE; /* * size of a property table entry * *. UINT2 property_ID *. DATAHOLDER value */ const size_t TCT3_TADSOBJ_PROP_SIZE = 2 + VMB_DATAHOLDER; /* * T3 object flags */ /* class flag - object is a class, not an instance */ #define TCT3_OBJFLG_CLASS 0x0001 /* ------------------------------------------------------------------------ */ /* * Metaclass List Entry. This list keeps track of the metaclasses that * the image file is dependent upon, and the dynamic link mapping * between metaclass ID in the image file and the universally unique * metaclass name. */ struct tc_meta_entry { /* next entry in the list */ tc_meta_entry *nxt; /* * metaclass symbol object, if present - we get the property list * from the metaclass symbol */ class CTcSymMetaclass *sym; /* external (universally unique) metaclass name */ char nm[1]; }; /* * Fixed System Metaclasses. The compiler must generate code for these * metaclasses directly, so it pre-loads the metaclass dependency table * with these metaclasses at initialization. Because these entries are * always loaded into the table in the same order, they have fixed table * indices that we can define as constants here. */ /* TADS Object Metaclass */ const int TCT3_METAID_TADSOBJ = 0; /* list metaclass */ const int TCT3_METAID_LIST = 1; /* dictionary metaclass */ const int TCT3_METAID_DICT = 2; /* grammar production metaclass */ const int TCT3_METAID_GRAMPROD = 3; /* vector metaclass */ const int TCT3_METAID_VECTOR = 4; /* anonymous function pointer */ const int TCT3_METAID_ANONFN = 5; /* intrinsic class modifiers */ const int TCT3_METAID_ICMOD = 6; /* lookup table */ const int TCT3_METAID_LOOKUP_TABLE = 7; /* * IMPORTANT!!! When adding new entries to this list of pre-defined * metaclasses, you must: * * - update the 'last' constant below * * - add the new entry to CTcGenTarg::load_image_file_meta_table() */ /* last metaclass ID - adjust when new entries are added */ const int TCT3_METAID_LAST = 7; /* ------------------------------------------------------------------------ */ /* * Function set dependency list entry */ struct tc_fnset_entry { /* next entry in the list */ tc_fnset_entry *nxt; /* external (universally unique) function set name */ char nm[1]; }; /* ------------------------------------------------------------------------ */ /* * Exception Table builder. This object keeps track of the entries in * an exception table under construction, so that the exception table * for a function can be written to the code stream after all of the * code in the function has been generated. */ class CTcT3ExcTable { public: CTcT3ExcTable(); ~CTcT3ExcTable() { /* if we've allocated a table, delete it */ if (table_ != 0) t3free(table_); } /* * Set the current function or method's start offset. The code * generator for the function body should set this to the code * stream offset of the start of the method header; this allows us * to calculate the offsets of protected code and 'catch' blocks. * * Important: this is the code stream offset (G_cs->get_ofs()), not * the final code pool address. We need only relative offsets, so * the code stream offset suffices (and is available much earlier in * the code generation process). */ void set_method_ofs(ulong ofs) { method_ofs_ = ofs; } /* * Add a 'catch' entry. The offsets are all code stream offsets * (G_cs->get_ofs() values). */ void add_catch(ulong protected_start_ofs, ulong protected_end_ofs, ulong exc_obj_id, ulong catch_block_ofs); /* * write our exception table to the code stream - writes to the G_cs * global code stream object */ void write_to_code_stream(); /* get the number of entries */ size_t get_entry_count() const { return exc_used_; } /* clear the exception table - remove all entries */ void clear_table() { exc_used_ = 0; } protected: /* the starting offset of this method header */ ulong method_ofs_; /* exception table */ struct CTcT3ExcEntry *table_; /* number of entries used/allocated in our table */ size_t exc_used_; size_t exc_alloced_; }; /* * Exception table entry */ struct CTcT3ExcEntry { /* start/end offset (from start of method header) of protected code */ ulong start_ofs; ulong end_ofs; /* object ID of exception class caught */ ulong exc_obj_id; /* 'catch' block starting offset (from start of method header) */ ulong catch_ofs; }; /* ------------------------------------------------------------------------ */ /* * Data Stream Page Layout Manager. This works with a CTcDataStream * object (such as the constant pool or the code pool) to divide the * stream into pages for the image file. */ class CTcStreamLayout { public: CTcStreamLayout() { /* we don't know anything about our layout yet */ page_size_ = 0; page_cnt_ = 0; } /* * Calculate my layout, given the maximum object size. This can be * called once the entire stream has been generated, hence the size * of the largest indivisible item in the stream is known. This * will apply all fixups throughout the stream. * * If this is the first stream for this layout, is_first is true. * If we're adding more pages, is_first is false, and max_len is * ignored (so the caller must ensure that the max_len provided on * laying out the first stream for this page set is adequate for all * streams added to this layout). */ void calc_layout(class CTcDataStream *ds, ulong max_len, int is_first); /* * Write the stream(s) to an image file. We'll write the pool * definition block and the pool pages. This cannot be called until * after calc_layout() has been called for all streams, because we * must apply all fixups throughout the entire image before we can * write out anything. */ void write_to_image(class CTcDataStream **ds_array, size_t ds_cnt, class CVmImageWriter *image_writer, int pool_id, uchar xor_mask); /* page size */ ulong page_size_; /* number of pages used */ size_t page_cnt_; }; /* ------------------------------------------------------------------------ */ /* * Debug line list page. We keep a linked list of these pages, and * allocate new entries out of the last page. We keep going until the * last page is filled up, then allocate a new page. */ const size_t TCT3_DEBUG_LINE_PAGE_SIZE = 1024; const size_t TCT3_DEBUG_LINE_REC_SIZE = 5; struct tct3_debug_line_page { /* next page in list */ tct3_debug_line_page *nxt; /* * Entries on this page (each entry is a debug line record offset in * the code stream). Each entry consists of one byte for the code * stream identifier (TCGEN_xxx_STREAM) and four bytes for a * portable UINT4 with the offset in the stream. */ uchar line_ofs[TCT3_DEBUG_LINE_PAGE_SIZE * TCT3_DEBUG_LINE_REC_SIZE]; }; /* ------------------------------------------------------------------------ */ /* * T3-specific code generator helper class. This class provides a set * of static functions that are useful for T3 code generation. */ class CTcGenTarg { public: /* initialize the code generator */ CTcGenTarg(); /* destroy the code generator object */ ~CTcGenTarg(); /* * Set the run-time metaclass dependency table index for a given * metaclass, identified by 'name' (a string of length 'len'). 'idx' * is the run-time metaclass dependency index. * * When we're operating as part of an interactive debugger, the image * loader must call this for each entry in the metaclass dependency * table loaded from the image file. This allows us to fix up our * internal notion of the metaclass indices so that we generate code * compatible with the actual loaded image file. * * The protocol is as follows: call start_image_file_meta_table(), then * call load_image_file_meta_table() on each entry in the table, then * call end_image_file_meta_table(). */ void start_image_file_meta_table(); void load_image_file_meta_table(const char *nm, size_t len, int idx); void end_image_file_meta_table(); /* * Allocate a new global property ID. */ tctarg_prop_id_t new_prop_id() { return next_prop_++; } /* * Allocate a new global object ID. */ tctarg_obj_id_t new_obj_id() { return next_obj_++; } /* * add a metaclass to the dependency table - returns the index of * the metaclass in the table */ int add_meta(const char *meta_extern_name, size_t len, class CTcSymMetaclass *sym); int add_meta(const char *nm, class CTcSymMetaclass *sym) { return add_meta(nm, strlen(nm), sym); } int add_meta(const char *nm) { return add_meta(nm, strlen(nm), 0); } /* * Find a metaclass entry, adding it if it's not already there. If * the metaclass is already defined, and it has an associated * symbol, we will not change the associated symbol - this will let * the caller detect that the metaclass has been previously defined * for a different symbol, which is usually an error. */ int find_or_add_meta(const char *nm, size_t len, class CTcSymMetaclass *sym); /* * Get the property ID for the given method table index in the given * metaclass. The metaclass ID is given as the internal metaclass ID * (without the "/version" suffix), not as the external class name. * Returns a parse node for the property, or null if it's not found. */ CTcPrsNode *get_metaclass_prop(const char *name, ushort idx) const; /* get a metaclass symbol by the metaclass's global identifier */ class CTcSymMetaclass *find_meta_sym(const char *nm, size_t len); /* * Find the metaclass table entry for a given global identifier. If * update_vsn is true, we'll update the entry stored in the table to * the given version number if the given name's version number is * higher than the one in the table. If we find an entry, we'll * fill in *entry_idx with the entry's index. */ tc_meta_entry *find_meta_entry(const char *nm, size_t len, int update_vsn, int *entry_idx); /* get/set the symbol for a given metaclass */ class CTcSymMetaclass *get_meta_sym(int meta_idx); void set_meta_sym(int meta_idx, class CTcSymMetaclass *sym); /* get the number of metaclasses */ int get_meta_cnt() const { return meta_cnt_; } /* get the external (universally unique) name for the given metaclass */ const char *get_meta_name(int idx) const; /* * Get the dependency table index for the given pre-defined metaclass, * specified by the TCT3_METAID_xxx value. */ int get_predef_meta_idx(int id) const { return predef_meta_idx_[id]; } /* * Add a function set to the dependency table - returns the index of * the function set in the table */ ushort add_fnset(const char *fnset_extern_name, size_t len); ushort add_fnset(const char *fnset_extern_name) { return add_fnset(fnset_extern_name, strlen(fnset_extern_name)); } /* get the name of a function set given its index */ const char *get_fnset_name(int idx) const; /* get the number of defined function sets */ int get_fnset_cnt() const { return fnset_cnt_; } /* * Notify the code generator that parsing is finished. This should * be called after parsing and before code generation begins. */ void parsing_done(); /* * Note a string value's length. This should be invoked during the * parsing phase for each constant string value. We'll keep track * of the largest constant data in the file, so that after parsing * is finished, we'll know the minimum size we need for each * constant pool page. This doesn't actually allocate any space in * the constant pool; this merely keeps track of the longest string * we'll eventually need to store. */ void note_str(size_t len); /* * Note number of elements in a constant list value. This is the * list equivalent of note_str(). */ void note_list(size_t element_count); /* * Note the length of a code block's byte code. This should be * invoked during code generation for each code block; we'll keep * track of the longest byte code block, so that after code * generation is complete, we'll know the minimum size we need for * each code pool page. */ void note_bytecode(ulong len); /* * Notify the code generator that we're replacing an object (via the * "replace" statement) at the given stream offset. We'll mark the * data in the stream as deleted so that we don't write it to the * image file. */ void notify_replace_object(ulong stream_ofs); /* * Write to an object file. The compiler calls this after all * parsing and code generation are completed to write an object * file, which can then be linked with other object files to create * an image file. */ void write_to_object_file(class CVmFile *object_fp, class CTcMake *make_obj); /* * Load an object file. Returns zero on success, non-zero on error. */ int load_object_file(CVmFile *fp, const textchar_t *fname); /* * Write the image file. The compiler calls this after all parsing * and code generation are completed to write an image file. We * must apply all fixups, assign the code and constant pool layouts, * and write the data to the image file. */ void write_to_image(class CVmFile *image_fp, uchar data_xor_mask, const char tool_data[4]); /* generate synthesized code during linking */ void build_synthesized_code(); /* generate code for a dictionary object */ void gen_code_for_dict(class CTcDictEntry *dict); /* generate code for a grammar production object */ void gen_code_for_gramprod(class CTcGramProdEntry *prod); /* get the maximum string/list/bytecode lengths */ size_t get_max_str_len() const { return max_str_len_; } size_t get_max_list_cnt() const { return max_list_cnt_; } size_t get_max_bytecode_len() const { return max_bytecode_len_; } /* * Add a debug line record. If we're in debug mode, this will clear * the peephole optimizer to ensure that the line record doesn't get * confused due to compression of opcodes. */ void add_line_rec(class CTcTokFileDesc *file, long linenum); /* write an opcode to the output stream */ void write_op(uchar opc); /* write a CALLPROP instruction */ void write_callprop(int argc, int varargs, vm_prop_id_t prop); /* * Determine if we can skip an opcode for peephole optimization. * We'll look at the previous opcode to determine if this opcode is * reachable, and we'll indicate that we should suppress the new * opcode if not. */ int can_skip_op(); /* * Add a string to the constant pool, and create a fixup for the * item for a reference from the given stream at the given offset. */ void add_const_str(const char *str, size_t len, class CTcDataStream *ds, ulong ofs); /* * Add a list to the constant pool, and create a fixup for the item * for a reference from the given stream at the given offset. */ void add_const_list(class CTPNList *lst, class CTcDataStream *ds, ulong ofs); /* * Write a constant value (in the compiler's internal * representation, a CTcConstVal structure) to a given buffer in T3 * image file DATA_HOLDER format. Write at a given offset, or at * the current write offset. */ void write_const_as_dh(class CTcDataStream *ds, ulong ofs, const class CTcConstVal *src); void write_const_as_dh(class CTcDataStream *ds, const class CTcConstVal *src); /* * Clear the peephole optimizer state. This must be invoked * whenever a jump label is defined. We can't combine an * instruction at a jump destination with anything previous: the * instruction at a jump destination must be generated as-is, rather * than being combined with the preceding instruction, since someone * could jump directly to it. */ void clear_peephole() { last_op_ = OPC_NOP; second_last_op_ = OPC_NOP; } /* get the last opcode we generated */ uchar get_last_op() const { return last_op_; } /* * Remove the last JMP instruction. This is used when we detect * that we just generated a JMP ahead to the very next instruction, * in which case we can eliminate the JMP, since it has no effect. */ void remove_last_jmp(); /* * Stack depth counting. While we're generating code for a code * block (a function or method), we'll keep track of our stack push * and pop operations, so that we can monitor the maximum stack * depth. In order for the stack depth to be calculable at compile * time, the code generator must take care that each individual * statement is stack-neutral (i.e, the stack comes out of each * statement at the same depth as when it entered the statement), so * that jumps, iterations, and other variables we can't analyze * statically can be ignored. */ /* * reset the stack depth counters - call this at the start * generating of each code block */ void reset_sp_depth() { sp_depth_ = max_sp_depth_ = 0; } /* * get the maximum stack depth for the current function - use this * when finished generating a code block to determine the maximum * stack space needed by the code block */ int get_max_sp_depth() const { return max_sp_depth_; } /* get the current stack depth */ int get_sp_depth() const { return sp_depth_; } /* record a push - increments the current stack depth */ void note_push() { note_push(1); } /* record a push of a given number of stack elements */ void note_push(int cnt) { sp_depth_ += cnt; if (sp_depth_ > max_sp_depth_) max_sp_depth_ = sp_depth_; } /* record a pop - decrements the current stack depth */ void note_pop() { note_pop(1); } /* record a pop of a given number of stack elements */ void note_pop(int cnt) { sp_depth_ -= cnt; } /* record a full stack reset back to function entry conditions */ void note_rst() { sp_depth_ = 0; } /* * do post-call cleanup: generate a named argument table pointer if * needed, remove the named arguments from the stack */ void post_call_cleanup(const struct CTcNamedArgs *named_args); /* * Open/close a method/function. "Open" generates a placeholder method * header and sets up our generator globals to prepare for a new * method. "Close" goes back and fills in the final method header * based on the code generated since "Open". */ void open_method(class CTcCodeStream *stream, class CTcSymbol *fixup_owner_sym, struct CTcAbsFixup **fixup_list_head, class CTPNCodeBody *code_body, class CTcPrsSymtab *goto_tab, int argc, int opt_argc, int varargs, int is_constructor, int is_op_overload, int is_self_available, struct tct3_method_gen_ctx *ctx); void close_method(int local_cnt, class CTcPrsSymtab *local_symtab, class CTcTokFileDesc *end_desc, long end_linenum, struct tct3_method_gen_ctx *ctx, struct CTcNamedArgTab *named_arg_tab_head); void close_method_cleanup(struct tct3_method_gen_ctx *ctx); /* * Generate a TadsObject header to a data stream */ void open_tadsobj(struct tct3_tadsobj_ctx *ctx, CTcDataStream *stream, vm_obj_id_t obj_id, int sc_cnt, int prop_cnt, unsigned int internal_flags, unsigned int vm_flags); void close_tadsobj(struct tct3_tadsobj_ctx *ctx); /* * Linker support: ensure that the given intrinsic class has a modifier * object. If there's no modifier, we'll create one and add code for * it to the intrinsic class modifier stream. */ void linker_ensure_mod_obj(CTcSymMetaclass *mc_sym); void linker_ensure_mod_obj(const char *name, size_t len); /* * get my exception table object - this is used to construct a * method's exception table during code generation, and to write the * table to the code stream */ CTcT3ExcTable *get_exc_table() { return &exc_table_; } /* determine if we're compiling a constructor */ int is_in_constructor() const { return in_constructor_; } void set_in_constructor(int f) { in_constructor_ = f; } /* determine if we're compiling an operator overload method */ int is_in_op_overload() const { return in_op_overload_; } void set_in_op_overload(int f) { in_op_overload_ = f; } /* * set the method offset - the code body object calls this when it's * about to start generating code to let us know the offset of the * current method */ void set_method_ofs(ulong ofs); /* * Add a debug line table to our list. We keep track of all of the * debug line record tables in the program, so that we can store the * list in the object file. We need this information in the object * file because each debug line record table in an object file must * be fixed up at link time after loading the object file. */ void add_debug_line_table(ulong ofs); /* * Set dynamic (run-time) compilation mode. This mode must be used for * code compiled during program execution, such as for an "eval()" * facility. In dynamic compilation, all pool addresses are already * resolved from the loaded program, and we can't add anything to any * of the constant or code pools. */ void set_dyn_eval() { eval_for_dyn_ = TRUE; } /* * Set debug evaluation mode. If 'speculative' is true, it means * that we're generating an expression for speculative evaluation, * in which case the evaluation must fail if it would have any side * effects (such as calling a method, displaying a string, or * assigning a value). 'stack_level' is the enclosing stack level * at which to evaluate the expression; 0 is the last active * non-debug stack level, 1 is the first enclosing level, and so on. */ void set_debug_eval(int speculative, int level) { /* note that we're evaluating for the debugger */ eval_for_debug_ = TRUE; /* this is a special type of run-time dynamic compilation */ eval_for_dyn_ = TRUE; /* note the speculative mode */ speculative_ = speculative; /* note the stack level */ debug_stack_level_ = level; } /* set normal evaluation mode */ void set_normal_eval() { eval_for_debug_ = eval_for_dyn_ = FALSE; } /* determine if we're in dynamic/debugger evaluation mode */ int is_eval_for_dyn() const { return eval_for_dyn_; } int is_eval_for_debug() const { return eval_for_debug_; } /* determine if we're in speculative evaluation mode */ int is_speculative() const { return eval_for_debug_ && speculative_; } /* get the active debugger stack level */ int get_debug_stack_level() const { return debug_stack_level_; } /* * Generate a BigNumber object, returning the object ID. The input * text gives the source representation of the number. */ vm_obj_id_t gen_bignum_obj(const char *txt, size_t len, int promoted); /* generate a RexPattern object */ vm_obj_id_t gen_rexpat_obj(const char *txt, size_t len); private: /* eliminate jump-to-jump sequences */ void remove_jumps_to_jumps(class CTcCodeStream *str, ulong start_ofs); /* * Calculate pool layouts. This is called after all code generation * is completed; at this point, the T3 code generator can determine * how the code pages will be laid out, since we now know the size * of the largest single chunk of code. * * We'll fill in *first_static_page with the page number in the code * pool of the first page of code containing static initializers. * We group all of the static initializer code together at the end * of the code pool to allow the pre-initialization re-writer to * omit all of the static code pages from the final image file. */ void calc_pool_layouts(size_t *first_static_page); /* * Write a TADS object stream to the image file. This routine will * fix up the property table in each object to put the table in * sorted order. */ void write_tads_objects_to_image(class CTcDataStream *obj_stream, class CVmImageWriter *image_writer, int metaclass_idx); /* * write the TADS objects of one particular type - transient or * persistent - to the image file */ void write_tads_objects_to_image(CTcDataStream *os, CVmImageWriter *image_writer, int meta_idx, int trans); /* * Write an object stream of non-TADS objects to the image file. * This writes the objects as-is, without looking into their * contents at all. */ void write_nontads_objs_to_image(class CTcDataStream *obj_stream, class CVmImageWriter *image_writer, int metaclass_idx, int large_obs); /* * Sort an object's property table, and compress the table to remove * deleted properties. Returns the final size of the object data to * write to the image file, which could differ from the original * size, because we might remove property slots from the property * data. If we do change the size of the property table, we'll * update the stream data to reflect the new property count and * metaclass data size. */ size_t sort_object_prop_table(class CTcDataStream *obj_stream, ulong start_ofs); /* write the function-set dependency table to an object file */ void write_funcdep_to_object_file(class CVmFile *fp); /* write the metaclass dependency table to an object file */ void write_metadep_to_object_file(class CVmFile *fp); /* load the function set dependency table from an object file */ void load_funcdep_from_object_file(class CVmFile *fp, const textchar_t *fname); /* load the metaclass dependency table from an object file */ void load_metadep_from_object_file(class CVmFile *fp, const textchar_t *fname); /* look up a required or optional property by name */ vm_prop_id_t look_up_prop(const char *propname, int required, int err_if_undef, int err_if_not_prop); /* build the IntrinsicClass instances */ void build_intrinsic_class_objs(CTcDataStream *str); /* build the source file line maps */ void build_source_line_maps(); /* build the local symbol records */ void build_local_symbol_records(class CTcCodeStream *cs, class CVmHashTable *tab); /* build the multi-method initializer list */ void build_multimethod_initializers(); /* symbol table enumerator callback for the multi-method initializers */ static void multimethod_init_cb(void *ctx, CTcSymbol *sym); /* symbol table enumerator callback for the multi-method stubs */ static void multimethod_stub_cb(void *ctx, CTcSymbol *sym); /* write an overloaded operator property export */ void write_op_export(CVmImageWriter *image_writer, class CTcSymProp *prop); /* write the static initializer list to the image file */ void write_static_init_list(CVmImageWriter *image_writer, ulong main_cs_size); /* write the list of source file descriptors to an image file */ void write_sources_to_image(class CVmImageWriter *image_writer); /* write the global symbol table to an object file */ void write_global_symbols_to_image(class CVmImageWriter *image_writer); /* write the method header list to the image file */ void write_method_list_to_image(class CVmImageWriter *image_writer); /* write macro definitions to the image file */ void write_macros_to_image(class CVmImageWriter *image_writer); /* write the list of source file descriptors to an object file */ void write_sources_to_object_file(class CVmFile *fp); /* * read the list of sources from an object file, adding the sources * to the tokenizer's internal list */ void read_sources_from_object_file(class CVmFile *fp); /* load debug records from an object file */ void load_debug_records_from_object_file(class CVmFile *fp, const textchar_t *fname, ulong main_cs_start_ofs, ulong static_cs_start_ofs); /* fix up a debug line record table for the object file */ void fix_up_debug_line_table(class CTcCodeStream *cs, ulong line_table_ofs, int first_filedesc); /* hash table enumerator callback - generate dictionary code */ static void enum_dict_gen_cb(void *ctx, class CVmHashEntry *entry); /* most recent opcodes we've written, for peephole optimization */ uchar last_op_; uchar second_last_op_; /* maximum constant string length seen during parsing */ size_t max_str_len_; /* maximum list element count seen during parsing */ size_t max_list_cnt_; /* maximum byte code block generated during code generation */ size_t max_bytecode_len_; /* head and tail of metaclass list */ tc_meta_entry *meta_head_; tc_meta_entry *meta_tail_; /* number of entries in metaclass list so far */ int meta_cnt_; /* head and tail of function set list */ tc_fnset_entry *fnset_head_; tc_fnset_entry *fnset_tail_; /* number of function sets in the list */ int fnset_cnt_; /* next available global property ID */ vm_prop_id_t next_prop_; /* next available global object ID */ vm_obj_id_t next_obj_; /* current stack depth */ int sp_depth_; /* maximum stack depth in current code block */ int max_sp_depth_; /* exception table for current code block */ CTcT3ExcTable exc_table_; /* constant pool layout manager */ CTcStreamLayout const_layout_; /* code pool layout manager */ CTcStreamLayout code_layout_; /* first/last page of debug line list */ tct3_debug_line_page *debug_line_head_; tct3_debug_line_page *debug_line_tail_; /* total number of debug line list entries used so far */ ulong debug_line_cnt_; /* * Object ID of the multi-method static initializer object. This will * be set by build_multimethod_initializers() if we end up creating any * registration code. */ vm_obj_id_t mminit_obj_; /* * property sorting buffer - this is space we allocate to copy an * object's property table for sorting */ char *sort_buf_; size_t sort_buf_size_; /* flag: we're currently compiling a constructor */ uint in_constructor_ : 1; /* flag: we're currently compiling an operator overload method */ uint in_op_overload_ : 1; /* flag: we're generating code for dynamic (run-time) compilation */ uint eval_for_dyn_ : 1; /* flag: we're generating an expression for debugger use */ uint eval_for_debug_ : 1; /* flag: we're generating a debugger speculative evaluation expression */ uint speculative_ : 1; /* * debugger active stack context level - valid when eval_for_debug_ * is true */ int debug_stack_level_; /* * String interning hash table. This is a table of short string * literals that we've generated into the data segment. Whenever we * generate a new string literal, we'll check this table to see if * we've previously stored the identical string. If so, we'll simply * use the original string rather than generating a new copy. This can * reduce the size of the object file by eliminating duplication of * common short strings. * * Note that string interning has the potential to change run-time * semantics in some other languages, but not in TADS. There are two * common issues in other languages. First, in C/C++, a string * constant is technically a writable buffer. This means that two * distinct string literals in the source code really need to be * treated as distinct memory locations, even if they have identical * contents, because the program might write to one buffer and expect * the other to remain unchanged. This doesn't apply to TADS because * string literals are strictly read-only. Second, strings in many * languages are objects with reference semantics for comparisons; two * distinct source-code strings must therefore have distinct reference * identities. String comparisons in TADS are always by value, so * there's no need for distinct references if two strings have * identical contents. */ class CVmHashTable *strtab_; /* * Static table of image file metaclass dependency indices for the * pre-defined metaclasses (i.e., the metaclasses that the compiler * specifically generates code for). When we compile a program, these * are determined by the compiler simply according to the order in * which it builds its own initial table of the known metaclasses. * When we're debugging, we need to get these values from the image * file. * * This is a simple translation table - we translate from our internal * index (a TCT3_METAID_xxx value) to the corresponding dependency * index in the actual image file. So, we just need as many of these * as there TCT3_METAID_xxx indices. We never need these for any * additional metaclasses that might exist - we only care about the * ones that the compiler specifically knows about in advance. */ int predef_meta_idx_[TCT3_METAID_LAST + 1]; }; /* ------------------------------------------------------------------------ */ /* * Method generator context */ struct tct3_method_gen_ctx { /* output code stream */ class CTcCodeStream *stream; /* stream anchor */ struct CTcStreamAnchor *anchor; /* method header offset in stream */ ulong method_ofs; /* starting and ending code offset in stream */ ulong code_start_ofs; ulong code_end_ofs; /* enclosing code body */ class CTPNCodeBody *old_code_body; }; /* ------------------------------------------------------------------------ */ /* * TadsObject header writer context */ struct tct3_tadsobj_ctx { /* start of object header in data stream */ ulong obj_ofs; /* data stream to which we're writing the object */ CTcDataStream *stream; }; /* ------------------------------------------------------------------------ */ /* * Named arguments information. This keeps track of the named arguments * for a generated call. */ struct CTcNamedArgs { /* number of named arguments */ int cnt; /* argument list */ class CTPNArglist *args; }; #endif /* TCT3_H */ frobtads-1.2.3/tads3/lib/0000755000175000001440000000000012145614112014265 5ustar realncusersfrobtads-1.2.3/tads3/lib/dynfunc.t0000644000175000001440000001575211427502132016132 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2001, 2006 Michael J. Roberts * * This file is part of TADS 3. * * This module defines classes related to the DynamicFunc intrinsic * class. You should include this source file in your build if you're * using the DynamicFunc class. */ #include #include /* ------------------------------------------------------------------------ */ /* * Compiler: This object provides a simplified interface to the dynamic * compiler. The methods here can be used instead of manually creating * DynamicFunc instances. * * The main advantage of using this object to compile code is that it * automatically provides access to the global symbol table that was used * to compile the current program, for use in dynamic code. Without the * global symbol table, dynamic code won't have access to object names, * property names, function names, and so on. That doesn't stop you from * compiling code that only depends upon its own function parameters and * local variables, but for most purposes the global symbols are useful * to have around. * * Note that including this object in a project will automatically save * the global symbol table in the compiled .t3 file. This increases the * size of the .t3 file, as well as memory usage during execution. If * you're concerned about minimizing the .t3 file size or the run-time * memory footprint, *and* you don't need global symbols for dynamic code * (or you don't use the dynamic compiler at all), you can save some * space by omitting this whole module from the build. */ Compiler: PreinitObject /* * Compile an expression or function. 'str' is a string giving the * code to compile. This can be a simple value expression, such as * 'Me.location' or 'new BigNumber(12345).sqrt()'. Or, it can be a * complete unnamed function definition, using this syntax: * *. 'function(x, y, z) { ...body of function... }' * * The body of the function can contain any executable code that you * could write in a regular function in static code: if, while, * switch, return, etc. * * The return value is a DynamicFunc containing the compiled * expression or function. You call it by using the return value as * though it were a function: * *. local f = Compiler.compile('Me.location'); *. local loc = f(); * * If the source string was just an expression, it acts like a * function that takes zero arguments, and returns the computed value * of the expression. The expression is evaluated anew each time you * invoke it, so you'll get the "live" value of an expression that * refers to object properties or other external data. In the * example above, we'd get the current value of Me.location every * time we call f(). * * The source string is actually compiled immediately when you call * this function. This means it's checked for errors, such as syntax * errors and unknown symbol names. If the code contains any errors, * this method throws a CompilerException describing the problem. */ compile(str, locals?) { /* compile the string, using our saved global symbol table */ return new DynamicFunc(str, symtab_, locals, macros_); } /* * Compile a dynamic function string, and add it to the global symbol * table as a function with the given name. This effectively creates * a new named function that you can call from other dynamic code * objects. */ defineFunc(name, str, locals?) { /* * compile the string, and add it to our symbol table under the * given function name */ symtab_[name] = new DynamicFunc(str, symtab_, locals, macros_); } /* * Evaluate an expression. 'str' is a string giving code to compile. * In most cases, this is simply a simple value expression, although * it's also acceptable to use the 'function()' syntax to create a * function that takes no arguments. * * This method compiles the source string and immediately calls the * resulting compiled code. The return value is the value returned * from the compiled code itself. This method thus provides a quick * way to evaluate an expression. * * If the string contains any syntax errors or other compilation * errors, the method throws a CompilerException. In addition, it's * possible for the compiled code to throw exceptions of its own; * this method doesn't catch those, leaving it up to the caller to * handle them. * * If you expect to evaluate the same expression repeatedly, you're * better off using compile() to get the compiled representation of * the expression, and then call that compiled code each time the * value is needed. That's more efficient than using eval() each * time, since eval() to recompile the expression on every call, * which is a fairly complex process. */ eval(str, locals?) { /* * compile the string, call the resulting function, and return * the result from the function */ return (new DynamicFunc(str, symtab_, locals, macros_))(); } /* * During preinit, save a reference to the program's global symbol * table in a property of self. The VM always makes the global * symbols available during preinit, but by default it discards the * table after that because most programs don't need it. That means * that the symbols aren't available by default during normal * execution. However, saving a reference here prevents the garbage * collector from discarding the table when preinit finishes, which * forces it to be saved in the final .t3 file and thus makes it * available permanently. */ execute() { /* save the global symbol table */ symtab_ = t3GetGlobalSymbols(T3GlobalSymbols); macros_ = t3GetGlobalSymbols(T3PreprocMacros); } /* a saved reference to the global symbol table */ symtab_ = nil /* a saved referenced to the preprocessor macro table */ macros_ = nil ; /* ------------------------------------------------------------------------ */ /* * Compiler Exception. 'new DynamicFunc()' throws an exception of this * class if an error occurs compiling the source code of the new object. */ class CompilerException: Exception construct(msg) { errmsg_ = msg; } displayException() { if (errmsg_ != nil) "<>"; else "Source code compilation error"; } /* the error message from the compiler */ errmsg_ = nil ; /* export the compiler exception for use by the intrinsic class */ export CompilerException 'DynamicFunc.CompilerException'; frobtads-1.2.3/tads3/lib/multmeth.t0000644000175000001440000007720211465246012016325 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2008 Michael J. Roberts. All Rights Reserved. * * This module provides the run-time component of "multi-methods" in TADS * 3. This works with the compiler to implement a multiple-dispatch * system. * * Multi-methods are essentially a combination of regular object methods * and "overloaded functions" in languages like C++. Like a regular * object method, multi-methods are polymorphic: you can define several * incarnations of the same function name, with different parameter * types, the system picks the right binding for each invocation * dynamically, based on the actual argument values at run-time. Unlike * regular methods, though, the selection is made on ALL of the argument * types, not just a special "self" argument. In that respect, * multi-methods are like overloaded functions in C++; but multi-methods * differ from C++ overloading in that the selection of which method to * call is made dynamically at run-time, not at compile time. * * There are two main uses for multi-methods. * * First, most obviously, multi-methods provide what's known as "multiple * dispatch" semantics. There are some situations (actually, quite a * few) where the ordinary Object Oriented notion of polymorphism - * selecting a method based on a single target object - doesn't quite do * the trick, because what you really want to do is select a particular * method based on the *combination* of objects involved in an operation. * Some canonical examples are calculating intersections of shapes in a * graphics program, where you want to select a specialized "Rectangle + * Circle" routine in one case and a "Line + Polygon" routine in another; * or performing file format conversions, where you want to select, say, * a specialized "JPEG to PNG" routine. In an IF context, the obvious * use is for carrying out multi-object verbs, where you might want a * special routine for PUT (liquid) IN (vessel), and another for PUT * (object) IN (container). * * Second, multi-methods offer a way of extending a class without having * to change the class's source code. Since a multi-method is defined * externally to any classes it refers to, you can create a method that's * polymorphic on class type - just like a regular method - but as a * syntactically stand-alone function. This feature isn't as important * in TADS as in some other languages, since TADS lets you do essentially * the same thing with the "modify" syntax; but for some purposes the * multi-method approach might be preferable aesthetically, since it's * wholly external to the class rather than a sort of lexically separate * continuation of the class's code. (However, as a practical matter, * it's not all that different; our implementation of multi-methods does * in fact modify the original class object, since we store the binding * information in the class objects.) */ #include /* ------------------------------------------------------------------------ */ /* * Invoke a multi-method function. For an expression of the form * *. f(a, b, ...) * * where 'f' has been declared as a multi-method, the compiler will * actually generate code that invokes this function, like so: * *. _multiMethodCall(baseFunc, params); * * 'baseFunc' is a function pointer giving the base function; this is a * pointer to the common stub function that the compiler generates to * identify all of the multi-methods with a given name. 'params' is a * list giving the actual parameter values for invoking the function. * * Our job is to find the actual run-time binding for the function given * the actual parameters, and invoke it. */ _multiMethodCall(baseFunc, args) { /* get the function binding lookup information */ local info = _multiMethodRegistry.boundFuncTab_[baseFunc]; /* it's an error if there's no binding */ if (info == nil) throw new UnboundMultiMethod(baseFunc, args); /* * Look up the function binding based on the arguments. To ensure * that we match a function with the correct number of argument, we * have to explicitly add the last-argument placeholder to the list. */ local func = _multiMethodSelect(info, args + _multiMethodEndOfList); /* if we found a binding, invoke it; otherwise throw an error */ if (func == nil) throw new UnboundMultiMethod(baseFunc, args); else return (func)(args...); } /* * Invoke the base multi-method inherited from the given multi-method. * 'fromFunc' is a pointer to a multi-method, presumably the one * currently running; we look up the next in line in inheritance order * and invoke it with the given argument list. */ _multiMethodCallInherited(fromFunc, [args]) { #ifdef MULTMETH_STATIC_INHERITED /* static mode - get the cached inheritance information */ local inh = _multiMethodRegistry.inhTab_[fromFunc]; #else /* dynamic mode - get the base function binding */ local info = _multiMethodRegistry.boundFuncTab_[ _multiMethodRegistry.baseFuncTab_[fromFunc]]; /* it's an error if it doesn't exist */ if (info == nil) throw new UnboundInheritedMultiMethod(fromFunc, args); /* look up the inherited function based on the actual parameters */ local inh = _multiMethodInherit( fromFunc, info, args + _multiMethodEndOfList); #endif /* it's an error if there's no inherited binding */ if (inh == nil) throw new UnboundInheritedMultiMethod(fromFunc, args); /* call it */ return (inh)(args...); } /* ------------------------------------------------------------------------ */ /* * Get a pointer to a resolved multi-method function. This takes a * pointer to the base function for the multi-method and a list of actual * argument values, and returns a function pointer to the specific * version of the multi-method that would be invoked if you called the * multi-method with that argument list. * * For example, if you want to get a pointer to the function that would * be called if you were to call foo(x, y, z), you'd use: * *. local func = getMultiMethodPointer(foo, x, y, z); * * We return a pointer to the individual multi-method function that * matches the argument list, or nil if there's no matching multi-method. */ getMultiMethodPointer(baseFunc, [args]) { /* get the function binding lookup information */ local info = _multiMethodRegistry.boundFuncTab_[baseFunc]; /* if there's no binding information, return failure */ if (info == nil) return nil; /* look up and return the function binding based on the arguments */ return _multiMethodSelect(info, args + _multiMethodEndOfList); } /* ------------------------------------------------------------------------ */ /* * Resolve a multi-method binding. This function takes a binding * property ID (the property we assign during the registration process to * generate the binding tables) and a "remaining" argument list. This * function invokes itself recursively to traverse the arguments from * left to right, so at each recursive invocation, we lop off the * leftmost argument (the one we're working on currently) and pass in the * remaining arguments in the list. * * We look up the binding property on the first argument in the remaining * argument list. This can yield one of three things: * * - The trivial result is nil, which means that this binding property * has no definition on the first argument. This doesn't necessarily * mean that the whole function is undefined on the arguments; it only * means that the current inheritance level we're looking at for the * previous argument(s) has no binding. If we get this result we simply * return nil to tell the caller that it must look at an inherited * binding for the previous argument. * * - If the result is a function pointer, it's the bound function. This * is the final result for the recursion, so we simply return it. * * - Otherwise, the result will be a new property ID, giving the property * that resolves the binding for the *next* argument. In this case, we * use this property to resolve the next argument in the list by a * recursive invocation. If that recursive call succeeds (i.e., returns * a non-nil value), we're done - we simply return the recursive result * as though it were our own. If it fails, it means that there's no * binding for the particular subclass we're currently working on for the * first argument - however, there could still be a binding for a parent * class of the first argument. So, we iterate up to any inherited * binding for the first argument, and if we find one, we try again with * the same recursive call. We continue up our first argument's class * tree until we either find a binding (in which case we return it) or * exhaust the class tree (in which case we return nil). */ _multiMethodSelect(prop, args) { local obj, binding; /* * Get the first argument from the remaining arguments. If it's not * an object, use the placeholder object for non-object parameter * bindings. */ local orig = args[1]; if (dataType(orig) not in (TypeObject, TypeList, TypeSString)) orig = _multiMethodNonObjectBindings; /* get the remaining arguments */ args = args.sublist(2); /* * Look up the initial binding - this is simply the value of the * binding property for the first argument. In order to process the * inheritance tree later, we'll need to know where we got this * definition from, so look up the specific defining object. * * If the initial binding property isn't defined, or its value is * explicitly nil, the function isn't bound (or, in the case of nil, * is explicitly unbound). Inheritance won't help in these cases, so * we can immediately return nil to indicate that we don't have a * binding. */ if ((obj = orig.propDefined(prop, PropDefGetClass)) == nil || (binding = obj.(prop)) == nil) return nil; /* * If there are no more arguments, but we didn't just find a final * function binding, we don't have enough arguments to match the * current multi-method path. Return failure. */ if (args.length() == 0 && dataType(binding) != TypeFuncPtr) return nil; /* * starting at our current defining object for the first argument, * scan up its superclass tree until we find a binding */ for (;;) { local ret; /* if we have a function pointer, we've found our binding */ if (dataType(binding) == TypeFuncPtr) return binding; /* * Recursively bind the binding for the remaining arguments. If * we find a binding, we're done - simply return it. */ if ((ret = _multiMethodSelect(binding, args)) != nil) return ret; /* * We didn't find a binding for the remaining arguments, so we * must have chosen too specific a binding for the first * argument. Look for an inherited value of the binding property * in the next superclass of the object where we found the last * binding value. */ obj = orig.propInherited(prop, orig, obj, PropDefGetClass); if (obj == nil) return nil; /* we found an inherited value, so retrieve it from the superclass */ binding = obj.(prop); } } /* * Select the INHERITED version of a multi-method. This takes a * particular version of the multi-method, and finds the next version in * inheritance order. * * This is basically a copy of _multiMethodSelect(), with a small amount * of extra logic. This code repetition isn't good maintenance-wise, and * the two functions could in principle be merged into one. However, * doing so would have an efficiency cost to _multiMethodSelect(), which * we want to keep as lean as possible. */ _multiMethodInherit(fromFunc, prop, args) { return _multiMethodInheritMain( new _MultiMethodInheritCtx(), fromFunc, prop, args); } class _MultiMethodInheritCtx: object foundFromFunc = nil ; _multiMethodInheritMain(ctx, fromFunc, prop, args) { local obj, binding; /* * Get the first argument from the remaining arguments. If it's not * an object, use the placeholder object for non-object parameter * bindings. */ local orig = args[1]; if (dataType(orig) not in (TypeObject, TypeList, TypeSString)) orig = _multiMethodNonObjectBindings; /* get the remaining arguments */ args = args.sublist(2); /* * Look up the initial binding - this is simply the value of the * binding property for the first argument. In order to process the * inheritance tree later, we'll need to know where we got this * definition from, so look up the specific defining object. * * If the initial binding property isn't defined, or its value is * explicitly nil, the function isn't bound (or, in the case of nil, * is explicitly unbound). Inheritance won't help in these cases, so * we can immediately return nil to indicate that we don't have a * binding. */ if ((obj = orig.propDefined(prop, PropDefGetClass)) == nil || (binding = obj.(prop)) == nil) return nil; /* * If there are no more arguments, but we didn't just find a final * function binding, we don't have enough arguments to match the * current multi-method path. Return failure. */ if (args.length() == 0 && dataType(binding) != TypeFuncPtr) return nil; /* * starting at our current defining object for the first argument, * scan up its superclass tree until we find a binding */ for (;;) { /* we haven't found a function binding yet */ local ret = nil; /* * we either have a function pointer, in which case it's the * actual binding, or a property, in which case it's the next * binding level */ if (dataType(binding) == TypeFuncPtr) { /* this is the binding */ ret = binding; } else { /* if there are no more arguments, return failure */ if (args.length() == 0) return nil; /* * Recursively bind the binding for the remaining arguments. * If we find a binding, we're done - simply return it. */ ret = _multiMethodInheritMain(ctx, fromFunc, binding, args); } /* check to see if we found a binding */ if (ret != nil) { /* * We found a binding. If we've already found the inheriting * version, return the first thing we find, since that's the * next inheriting level. Otherwise, if this is the * inheriting version, note that we've found it, but keep * looking, since we want to find the next one after that. * Otherwise, just keep looking, since we haven't even * reached the overriding version yet. */ if (ctx.foundFromFunc) return ret; else if (ret == fromFunc) ctx.foundFromFunc = true; } /* * We didn't find a binding for the remaining arguments, so we * must have chosen too specific a binding for the first * argument. Look for an inherited value of the binding property * in the next superclass of the object where we found the last * binding value. */ obj = orig.propInherited(prop, orig, obj, PropDefGetClass); if (obj == nil) return nil; /* we found an inherited value, so retrieve it from the superclass */ binding = obj.(prop); } } /* ------------------------------------------------------------------------ */ /* * Unbound multi-method exception. This is thrown when a call to resolve * a multi-method fails to find a binding, meaning that there's no * definition of the method that matches the types of the arguments. */ class UnboundMultiMethod: Exception construct(func, args) { /* note the function name and argument list */ func_ = func; args_ = args; /* look up the function's name */ name_ = _multiMethodRegistry.funcNameTab_[func]; } /* display an error message describing the exception */ displayException() { "Unbound multi-method \"<>\" (<> argument(s))"; } /* the base function pointer */ func_ = nil /* the symbol name of the base function */ name_ = '' /* the number of arguments */ args_ = 0 ; class UnboundInheritedMultiMethod: UnboundMultiMethod displayException() { "No inherited multi-method for \"<>\" (<> arguments(s))"; } ; /* ------------------------------------------------------------------------ */ /* * Base class for our internal placeholder objects for argument list * matching. */ class _MultiMethodPlaceholder: object ; /* * A placeholder object for bindings for non-object arguments. Whenever * we have an actual argument value that's not an object, we'll look here * for bindings for that parameter. When registering a function, we'll * register a binding here for any parameter that doesn't have a type * specification. */ _multiMethodNonObjectBindings: _MultiMethodPlaceholder ; /* * A placeholder object for end-of-list bindings. When we're matching an * argument list, we'll use this to represent the end of the list so that * we can match the "..." in any varargs functions in the multi-method * set that we're matching against. */ _multiMethodEndOfList: _MultiMethodPlaceholder ; /* ------------------------------------------------------------------------ */ /* * Register a multi-method. * * The compiler automatically generates a call to this function during * pre-initialization for each defined multi-method. 'baseFunc' is a * pointer to the "base" function - this is a stub function that the * compiler generates to refer to the whole collection of multi-methods * with a given name. 'func' is the pointer to the specific multi-method * we're registering; this is the actual function defined in the code * with a given set of parameter types. 'params' is a list of the * parameter type values; each parameter type in the list is given as a * class object (meaning that the parameter matches that class), nil * (meaning that the parameter matches ANY type of value), or the string * '...' (meaning that this is a "varargs" function, and any number of * additional parameters can be supplied at this point in the parameters; * this is always the last parameter in the list if it's present). */ _multiMethodRegister(baseFunc, func, params) { /* if there's no hash entry for the function yet, add one */ local tab = _multiMethodRegistry.funcTab_; if (tab[baseFunc] == nil) tab[baseFunc] = new Vector(10); /* add the entry to the list of variants for this function */ tab[baseFunc].append([func, params]); /* add the mapping from the function to the base function */ _multiMethodRegistry.baseFuncTab_[func] = baseFunc; /* also add the function to the direct parameter table */ _multiMethodRegistry.funcParamTab_[func] = params; } /* * Build the method bindings. The compiler generates a call to this * after all methods have been registered; we run through the list of * registered methods and generate the binding properties in the * referenced objects. */ _multiMethodBuildBindings() { /* no errors yet */ local errs = []; /* * build a lookup table that maps function pointers to symbol names, * so we can look up our function names for diagnostic purposes */ local nameTab = new LookupTable(128, 256); t3GetGlobalSymbols().forEachAssoc(function(key, val) { /* if it's a function, store a value-to-name association */ if (dataType(val) == TypeFuncPtr) nameTab[val] = key; }); /* run through each entry in the method table */ _multiMethodRegistry.funcTab_.forEachAssoc(function(baseFunc, val) { /* look up the base function's name */ local name = nameTab[baseFunc]; /* add this to the saved name table */ _multiMethodRegistry.funcNameTab_[baseFunc] = name; /* note the number of registered instances of this function */ local funcCnt = val.length(); /* * Assign the initial binding property for this function. This * is the property that gives us the binding for the first * variant argument. Each unique multi-method (which is defined * as a multi-method with a given name and a given number of * parameters) has a single initial binding property. */ local initProp = t3AllocProp(); /* * store the binding starter information for the function - to * find the binding on invocation, we'll need the initial binding * property so that we can trace the argument list */ _multiMethodRegistry.boundFuncTab_[baseFunc] = initProp; /* build the argument binding tables */ for (local i = 1 ; i <= funcCnt ; i++) { /* get the function binding */ local func = val[i][1]; /* get the formal parameter type list for this function */ local params = val[i][2]; local paramCnt = params.length(); /* * If the last formal isn't a varargs placeholder, then we * must explicitly find the end of the list in the actual * parameters in order to match a call. To match the end of * the list, add the special End-Of-List placeholder to the * formals list. * * This isn't necessary when there's a varargs placeholder * because the placeholder can match zero or more - so it * doesn't matter where the list ends as long as we get to * the varargs slot. */ if (paramCnt == 0 || params[paramCnt] != '...') { params += _multiMethodEndOfList; ++paramCnt; } /* start at the initial binding property */ local prop = initProp; /* run through the parameters and build the bindings */ for (local j = 1 ; j <= paramCnt ; j++) { /* get this parameter type */ local origTyp = params[j], typ = origTyp; /* * If the type is nil, it means that this parameter slot * can accept any type. So, map the slot to the generic * Object type - this will catch everything, since we * handle non-objects by mapping them to the * _multiMethodNonObjectBindings placeholder object, * which like all objects inherits from Object. This * means we'll match argument values that are objects or * non-objects, thus fulfilling our requirement to match * all values. * * If the type is the string '...', it means that this is * a varargs placeholder argument. In this case, we need * to set up a match for the generic Object, in case we * have one or more actual arguments for the varargs * portion. This will also automatically match the case * where we have no extra arguments, because in this case * the matcher will try to match the End-Of-List * placeholder object _multiMethodEndOfList, which (as * above) inherits from Object and thus picks up the * any-type binding. * * The one tricky bit is that when we have a parameter * explicitly bound to Object, or an explicit End-Of-List * flag object, we'll get an undesired side effect of * this otherwise convenient arrangement: we'll * effectively bind non-object types to the Object by * virtue of the inheritance. To deal with this, we'll * explicitly set the placeholders' binding to nil in * this situation - this makes non-object types * explicitly *not* bound to the function, overriding any * binding we'd otherwise inherit from Object. */ if (typ == nil || typ == '...') typ = Object; /* * Figure the binding. * * - If this is the last parameter, it's the end of the * line, so bind directly to the function pointer. * * - If this isn't the variant parameter, the binding is * the next binding property. At invocation, we'll * continue on to the next argument value, evaluating * this next property to get its binding in the context * established by the current argument and property. The * next binding property is specific to the current class * in the current position, so we might already have * assigned a property for it from another version of * this function. Look it up, or create a new one if we * haven't assigned it already. */ local binding; if (j == paramCnt) { /* end of the line - the binding is the actual function */ binding = func; /* * if this type is already bound to a different * definition for this function, we have a * conflicting definition */ if (typ.propDefined(prop, PropDefDirectly) && typ.(prop) != binding) errs += [baseFunc, func, params, name]; } else { /* check for an existing binding property here */ if (typ.propDefined(prop, PropDefDirectly)) { /* we already have a forward binding here - use it */ binding = typ.(prop); } else { /* it's not defined here, so create a new property */ binding = t3AllocProp(); } } /* set the binding */ typ.(prop) = binding; /* * As we mentioned above, if the original type is * explicitly Object, we *don't* want to allow non-object * types (int, true, nil, property pointers, etc) and * End-Of-List placeholders to match - without some kind * of explicit intervention here, the placeholders would * match by inheritance because the placeholders are just * objects themselves. To handle this properly, set the * non-object placeholder bindings explicitly to nil. */ if (origTyp == Object) _MultiMethodPlaceholder.(prop) = nil; /* * if there's another argument, this binding is the * binding property for the next argument */ prop = binding; } } }); #ifdef MULTMETH_STATIC_INHERITED /* * If we're operating in static inheritance mode, cache the * next-override information for inherited() calls. Since we're * using static inheritance, we can figure this at startup and just * look up the cached information whenever we need to perform an * inherited() call. */ _multiMethodRegistry.funcTab_.forEachAssoc(function(baseFunc, val) { /* get the binding property for the base function */ local prop = _multiMethodRegistry.boundFuncTab_[baseFunc]; /* run through the functions registered under this function name */ for (local i = 1 ; i <= val.length() ; i++) { /* get this function binding and the type list */ local func = val[i][1]; local params = val[i][2]; local paramCnt = params.length(); /* * Add the end-of-list marker if applicable. For a varargs * function, add one generic Object parameter in place of the * variable list - but we'll check later to make sure that * any match we find is really varargs, since a varargs * function can only inherit from another varargs function. * (This is because, in order to actually invoke the * inherited function from an overrider, the callee must be * varargs to be able to handle varargs from the caller.) */ local varargs = (paramCnt != 0 && params[paramCnt] == '...'); if (varargs) params[paramCnt] = Object; else params += _multiMethodEndOfList; /* look up the inherited version of the method */ local inh = _multiMethodInherit(func, prop, params); /* varargs can only inherit from varargs */ if (inh != nil && varargs) { /* look up the inherited parameters */ local inhParams = _multiMethodRegistry.funcParamTab_[inh]; local inhParamCnt = inhParams.length(); /* make sure it's varargs, too */ if (inhParamCnt == 0 || inhParams[inhParamCnt] != '...') inh = nil; } /* remember the inherited function */ _multiMethodRegistry.inhTab_[func] = inh; } }); #endif /* MULTMETH_STATIC_INHERITED */ /* we're done with the source bindings - discard them to save memory */ _multiMethodRegistry.funcTab_ = nil; _multiMethodRegistry.funcParamTab_ = nil; } /* * Multi-method registry. This is where we keep the registry information * that we build during initialization. */ _multiMethodRegistry: object /* table of registered functions, indexed by base function */ funcTab_ = static new LookupTable(128, 256) /* table of function parameter lists, indexed by function */ funcParamTab_ = static new LookupTable(128, 256) /* function name table */ funcNameTab_ = static new LookupTable(64, 128) /* base function -> initial binding property */ boundFuncTab_ = static new LookupTable(64, 128) /* function -> base function */ baseFuncTab_ = static new LookupTable(64, 128) #ifdef MULTMETH_STATIC_INHERITED /* table of cached inherited() information, indexed by function */ inhTab_ = static new LookupTable(64, 128) #endif ; frobtads-1.2.3/tads3/lib/adv3/0000755000175000001440000000000012145614112015122 5ustar realncusersfrobtads-1.2.3/tads3/lib/adv3/parser.t0000644000175000001440000076457312145504453016637 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: parser * * This modules defines the language-independent parts of the command * parser. * * Portions based on xiny.t, copyright 2002 by Steve Breslin and * incorporated by permission. */ #include "adv3.h" #include "tok.h" #include #include #include /* ------------------------------------------------------------------------ */ /* * property we add to ResolveInfo to store the remaining items from an * ambiguous list */ property extraObjects; /* ------------------------------------------------------------------------ */ /* * ResolveResults - an instance of this class is passed to the * resolveNouns() routine to receive the results of the resolution. * * This class's main purpose is to virtualize the handling of error or * warning conditions during the resolution process. The ResolveResults * object is created by the initiator of the resolution, so it allows * the initiator to determine how errors are to be handled without * having to pass flags down through the match tree. */ class ResolveResults: object /* * Instances must provide the following methods: * * noVocabMatch(action, txt) - there are no objects in scope matching * the given noun phrase vocabulary. This is "worse" than noMatch(), * in the sense that this indicates that the unqualified noun phrase * simply doesn't refer to any objects in scope, whereas noMatch() * means that some qualification applied to the vocabulary ruled out * any matches. 'txt' is the original text of the noun phrase. * * noMatch(action, txt) - there are no objects in scope matching the * noun phrase. This is used in cases where we eliminate all * possible matches because of qualifications or constraints, so it's * possible that the noun phrase vocabulary, taken by itself, does * match some object; it's just that when the larger noun phrase * context is considered, there's nothing that matches. * * noMatchPossessive(action, txt) - same as noMatch, but the * unmatched phrase is qualified with a possessive phrase. For * ranking matches, the possessive version ranks ahead of treating * the possessive words as raw vocabulary when neither matches, since * it's more informative to report that we can't even match the * underlying qualified noun phrase, let alone the whole phrase with * qualification. * * noMatchForAll() - there's nothing matching "all" * * noMatchForAllBut() - there's nothing matching "all except..." * (there might be something matching all, but there's nothing left * when the exception list is applied) * * noMatchForListBut() - there's nothing matching " except..." * * noteEmptyBut() - a "but" (or "except") list matches nothing. We * don't consider this an error, but we rate an interpretation with a * non-empty "but" list and complete exclusion of the "all" or "any" * phrase whose objects are excluded by the "but" higher than one * with an empty "but" list and a non-empty all/any list - we do this * because it probably means that we came up with an incorrect * interpretation of the "but" phrase in the empty "but" list case * and failed to exclude things we should have excluded. * * noMatchForPronoun(typ, txt) - there's nothing matching a pronoun. * 'typ' is one of the PronounXxx constants, and 'txt' is the text of * the word used in the command. * * allNotAllowed() - the command contained the word ALL (or a * synonym), but the verb doesn't allow ALL to be used in its noun * phrases. * * reflexiveNotAllowed(typ, txt) - the reflexive pronoun isn't * allowed in this context. This usually means that the pronoun was * used with an intransitive or single-object action. 'typ' is one * of the PronounXxx constants, and 'txt' is the text of the word * used. * * wrongReflexive(typ, txt) - the reflexive pronoun doesn't agree * with its referent in gender, number, or some other way. * * noMatchForPossessive(owner, txt) - there's nothing matching the * phrase 'txt' owned by the resolved possessor object 'owner'. Note * that 'owner' is a list, since we can have plural possessive * qualifier phrases. * * noMatchForLocation(loc, txt) - there's nothing matching 'txt' in * the location object 'loc'. This is used when a noun phrase is * explicitly qualified by location ("the book on the table"). * * noteBadPrep() - we have a noun phrase or other phrase * incorporating an invalid prepositional phrase structure. This is * called from "badness" rules that are set up to match phrases with * embedded prepositions, as a last resort when no valid * interpretation can be found. * * nothingInLocation(loc) - there's nothing in the given location * object. This is used when we try to select the one object or all * of the objects in a given container, but the container doesn't * actually have any contents. * * ambiguousNounPhrase(keeper, asker, txt, matchLst, fullMatchList, * scopeList, requiredNum, resolver) - an ambiguous noun phrase was * entered: the noun phrase matches multiple objects that are all * equally qualified, but we only want the given exact number of * matches. 'asker' is a ResolveAsker object that we'll use to * generate any prompts; if no customization is required, simply pass * the base ResolveAsker. 'txt' is the original text of the noun * list in the command, which the standard prompt messages can use to * generate their questions. 'matchLst' is a list of the qualified * objects matching the phrase, with only one object included for * each set of equivalents in the original full list; 'fullMatchList' * is the full list of matches, including each copy of equivalents; * 'scopeList' is the list of everything in scope that matched the * original phrase, including illogical items. If it's desirable to * interact with the user at this point, prompt the user to resolve * the list, and return a new list with the results. If no prompting * is desired, the original list can be returned. If it isn't * possible to determine the final set of objects, and a final set of * objects is required (this is up to the subclass to determine), a * parser exception should be thrown to stop further processing of * the command. 'keeper' is an AmbigResponseKeeper object, which is * usually simply the production object itself; each time we parse an * interactive response (if we are interactive at all), we'll call * addAmbigResponse() on the calling production object to add it to * the saved list of responses, and we'll call getAmbigResponses() to * find previous answers to the same question, in case of * re-resolving the phrase with 'again' or the like. * * unknownNounPhrase(match, resolver) - a noun phrase that doesn't * match any known noun phrase syntax was entered. 'match' is the * production match tree object for the unknown phrase. Returns a * list of the resolved objects for the noun phrase, if possible. If * it is not possible to resolve the phrase, and a resolution is * required (this is up to the subclass to determine), a parser * exception should be thrown. * * getImpliedObject(np, resolver) - a noun phrase was left out * entirely. 'np' is the noun phrase production standing in for the * missing noun phrase; this is usually an EmptyNounPhraseProd or a * subclass. If an object is implicit in the command, or a * reasonable default can be assumed, return the implicit or default * object or objects. If not, the routine can return nil or can * throw an error. The result is a ResolveInfo list. * * askMissingObject(asker, resolver, responseProd) - a noun phrase * was left out entirely, and no suitable default can be found * (getImpliedObject has already been called, and that returned nil). * If it is possible to ask the player interactively to fill in the * missing object, ask the player. If it isn't possible to resolve * an object, an error can be thrown, or an empty list can be * returned. 'asker' is a ResolveAsker object, which can be used to * customize the prompt (if any) that we show; pass the base * ResolveAsker if no customization is needed. 'responseProd' is the * production to use to parse the response. The return value is the * root match tree object of the player's interactive response, with * its 'resolvedObjects' property set to the ResolveInfo list from * resolving the player's response. (The routine returns the match * tree for the player's response so that, if we must run resolution * again on another pass, we can re-resolve the same response without * asking the player the same question again.) * * noteLiteral(txt) - note the text of a literal phrase. When * selecting among alternative interpretations of a phrase, we'll * favor shorter literals, since treating fewer tokens as literals * means that we're actually interpreting more tokens. * * askMissingLiteral(action, which) - a literal phrase was left out * entirely. If possible, prompt interactively for a player response * and return the result. If it's not possible to ask for a * response, an error can be thrown, or nil can be returned. The * return value is simply the text string the player enters. * * emptyNounPhrase(resolver) - a noun phrase involving only * qualifiers was entered ('take the'). In most cases, an exception * should be thrown. If the empty phrase can be resolved to an * object or list of objects, the resolved list should be returned. * * zeroQuantity(txt) - a noun phrase referred to a zero quantity of * something ("take zero books"). * * insufficientQuantity(txt, matchList, requiredNumber) - a noun * phrase is quantified with a number exceeding the number of objects * available: "take five books" when only two books are in scope. * * uniqueObjectRequired(txt, matchList) - a noun phrase yields more * than one object, but only one is allowed. For example, we'll call * this if the user attempts to use more than one result for a * single-noun phrase (such as by answering a disambiguation question * with 'all'). * * singleObjectRequired(txt) - a noun phrase list was used where a * single noun phrase is required. * * noteAdjEnding() - a noun phrase ends in an adjective. This isn't * normally an error, but is usually less desirable than interpreting * the same noun phrase as ending in a noun (in other words, if a * word can be used as both an adjective and a noun, it is usually * better to interpret the word as a noun rather than as an adjective * when the word appears at the end of a noun phrase, as long as the * noun interpretation matches an object in scope). * * noteIndefinite() - a noun phrase is phrased as an indefinite ("any * book", "one book"), meaning that we can arbitrarily choose any * matching object in case of ambiguity. Sometimes, an object will * have explicit vocabulary that could be taken to be indefinite: a * button labeled "1" could be a "1 button", for example, or a subway * might have an "A train". By noting the indefinite interpretation, * we can give priority to the alternative definite interpretation. * * noteMatches(matchList) - notifies the results object that the * given list of objects is being matched. This allows the results * object to inspect the object list for its strength: for example, * by noting the presence of truncated words. This should only be * called after the nouns have been resolved to the extent possible, * so any disambiguation or selection that is to be performed should * be performed before this routine is called. * * noteMiscWordList(txt) - a noun phrase is made up of miscellaneous * words. A miscellaneous word list as a noun phrase has non-zero * badness, so we will never use a misc word list unless we can't * find any more structured interpretation. In most cases, a * miscellaneous word list indicates an invalid phrasing, but in some * cases objects might use this unstructured type of noun phrase in * conjunction with matchName() to perform dynamic or special-case * parsing. * * notePronoun() - a noun phrase is matching as a pronoun. In * general, we prefer a match to an object's explicit vocabulary * words to a match to a pronoun phrase: if the game goes to the * trouble of including a word explicitly among an object's * vocabulary, that's a better match than treating the same word as a * generic pronoun. * * notePlural() - a plural phrase is matching. In some cases we * require a single object match, in which case a plural phrase is * undesirable. (A plural phrase might still match just one object, * though, so it can't be ruled out on structural grounds alone.) * * beginSingleObjSlot() and endSingleObjSlot() are used to bracket * resolution of a noun phrase that needs to be resolved as a single * object. We use these to explicitly lower the ranking for plural * structural phrasings within these slots. * * beginTopicSlot() and endTopicSlot() are used to bracket resolution * of a noun phrase that's being used as a conversation topic. These * are of the *form* of single nouns, but singular and plural words * are equivalent, so when we're in a topic we don't consider a * plural to be a minus the way we would for an ordinary object noun * phrase. * * incCommandCount() - increment the command counter. This is used * to keep track of how many subcommands are in a command tree. * * noteActorSpecified() - note that the command is explicitly * addressed to an actor. * * noteNounSlots(cnt) - note the number of "noun slots" in the verb * phrase. This is the number of objects the verb takes: an * intransitive verb has no noun slots; a transitive verb with a * direct object only has one; a verb with a direct and indirect * object has two. Note this has nothing to do with the number of * objects specified in a noun list - TAKE BOX, BOOK, AND BELL has * only one noun slot (a direct object) even though the slot is * occupied by a list with three objects. * * noteWeakPhrasing(level) - note that the phrasing is "weak," with * the given weakness level - higher is weaker. The exact meaning of * the weakness levels is up to the language module to define. The * English module, for example, considers VERB IOBJ DOBJ phrasing * (with no preposition, as in GIVE BOB BOOK) to be weak when the * DOBJ part doesn't have a grammatical marker that clarifies that * it's really a separate noun phrase (an article serves this purpose * in English: GIVE BOB THE BOOK). * * allowActionRemapping - returns true if we can remap the action * during noun phrase resolution. Remapping is usually allowed only * during the actual execution phase, not during the ranking phase. * * allowEquivalentFiltering - returns true if we can filter an * ambiguous resolution list by making an arbitrary choice among * equivalent objects. This is normally allowed only during a final * resolution phase, not during a tentative resolution phase. */ ; /* ------------------------------------------------------------------------ */ /* * Noun phrase resolver "asker." This type of object can be passed to * certain ResolveResults methods in order to customize the messages * that the parser generates for interactive prompting. */ class ResolveAsker: object /* * Ask for help disambiguating a noun phrase. This asks which of * several possible matching objects was intended. This method has * the same parameter list as the equivalent message object method. */ askDisambig(targetActor, promptTxt, curMatchList, fullMatchList, requiredNum, askingAgain, dist) { /* let the target actor's parser message object handle it */ targetActor.getParserMessageObj().askDisambig( targetActor, promptTxt, curMatchList, fullMatchList, requiredNum, askingAgain, dist); } /* * Ask for a missing object. This prompts for an object that's * structurally required for an action, but which was omitted from * the player's command. */ askMissingObject(targetActor, action, which) { /* let the target actor's parser message object handle it */ targetActor.getParserMessageObj().askMissingObject( targetActor, action, which); } ; /* ------------------------------------------------------------------------ */ /* * The resolveNouns() method returns a list of ResolveInfo objects * describing the objects matched to the noun phrase. */ class ResolveInfo: object construct(obj, flags, np = nil) { /* remember the members */ obj_ = obj; flags_ = flags; np_ = np; } /* * Look for a ResolveInfo item in a list of ResolveInfo items that * is equivalent to us. Returns true if we find such an item, nil * if not. * * Another ResolveInfo is equivalent to us if it refers to the same * underlying game object that we do, or if it refers to a game * object that is indistinguishable from our underlying game object. */ isEquivalentInList(lst) { /* * if we can find our exact item in the list, or we can find an * equivalent object, we have an equivalent */ return (lst.indexWhich({x: x.obj_ == obj_}) != nil || lst.indexWhich( {x: x.obj_.isVocabEquivalent(obj_)}) != nil); } /* * Look for a ResolveInfo item in a list of ResolveInfo items that * is equivalent to us according to a particular Distinguisher. */ isDistEquivInList(lst, dist) { /* * if we can find our exact item in the list, or we can find an * equivalent object, we have an equivalent */ return (lst.indexWhich({x: x.obj_ == obj_}) != nil || lst.indexWhich( {x: !dist.canDistinguish(x.obj_, obj_)}) != nil); } /* the object matched */ obj_ = nil /* flags describing how we matched the object */ flags_ = 0 /* * By default, each ResolveInfo counts as one object, for the * purposes of a quantity specifier (as in "five coins" or "both * hats"). However, in some cases, a single resolved object might * represent a collection of discrete objects and thus count as more * than one for the purposes of the quantifier. */ quant_ = 1 /* * The possessive ranking, if applicable. If this object is * qualified by a possessive phrase ("my books"), we'll set this to * a value indicating how strongly the possession applies to our * object: 2 indicates that the object is explicitly owned by the * object indicated in the possessive phrase, 1 indicates that it's * directly held by the named possessor but not explicitly owned, * and 0 indicates all else. In cases where there's no posessive * qualifier, this will simply be zero. */ possRank_ = 0 /* the pronoun type we matched, if any (as a PronounXxx enum) */ pronounType_ = nil /* the noun phrase we parsed to come up with this object */ np_ = nil /* * The pre-calculated multi-object announcement text for this object. * When we iterate over the object list in a command with multiple * direct or indirect objects (TAKE THE BOOK, BELL, AND CANDLE), we * calculate the little announcement messages ("book:") for the * objects BEFORE we execute the actual commands. We then use the * pre-calculated announcements during our iteration. This ensures * consistency in the basis for choosing the names, which is * important in cases where the names include state-dependent * information for the purposes of distinguishing one object from * another. The relevant state can change over the course of * executing the command on the objects in the iteration, so if we * calculated the names on the fly we could end up with inconsistent * naming. The user thinks of the objects in terms of their state at * the start of the command, so the pre-calculation approach is not * only more internally consistent, but is also more consistent with * the user's perspective. */ multiAnnounce = nil ; /* * Intersect two resolved noun lists, returning a list consisting only * of the unique objects from the two lists. */ intersectNounLists(lst1, lst2) { /* we don't have any results yet */ local ret = []; /* * run through each element of the first list to see if it has a * matching object in the second list */ foreach(local cur in lst1) { local other; /* * if this element's object occurs in the second list, we can * include it in the result list */ if ((other = lst2.valWhich({x: x.obj_ == cur.obj_})) != nil) { /* if one or the other has a noun phrase, keep it */ local np = cur.np_ ?? other.np_; /* * include this one in the result list, with the combined * flags from the two original entries */ ret += new ResolveInfo(cur.obj_, cur.flags_ | other.flags_, np); } } /* return the result list */ return ret; } /* * Extract the objects from a list obtained with resolveNouns(). * Returns a list composed only of the objects in the resolution * information list. */ getResolvedObjects(lst) { /* * return a list composed only of the objects from the ResolveInfo * structures */ return lst.mapAll({x: x.obj_}); } /* ------------------------------------------------------------------------ */ /* * The basic production node base class. We'll use this as the base * class for all of our grammar rule match objects. */ class BasicProd: object /* get the original text of the command for this match */ getOrigText() { /* if we have no token list, return an empty string */ if (tokenList == nil) return ''; /* build the string based on my original token list */ return cmdTokenizer.buildOrigText(getOrigTokenList()); } /* get my original token list, in canonical tokenizer format */ getOrigTokenList() { /* * return the subset of the full token list from my first token * to my last token */ return nilToList(tokenList).sublist( firstTokenIndex, lastTokenIndex - firstTokenIndex + 1); } /* * Set my original token list. This replaces the actual token list * we matched from the parser with a new token list provided by the * caller. */ setOrigTokenList(toks) { tokenList = toks; firstTokenIndex = 1; lastTokenIndex = toks.length(); } /* * Can this object match tree resolve to the given object? We'll * resolve the phrase as though it were a topic phrase, then look for * the object among the matches. */ canResolveTo(obj, action, issuingActor, targetActor, which) { /* set up a topic resolver */ local resolver = new TopicResolver( action, issuingActor, targetActor, self, which, action.createTopicQualifierResolver(issuingActor, targetActor)); /* * set up a results object - use a tentative results object, * since we're only looking at the potential matches, not doing a * full resolution pass */ local results = new TentativeResolveResults(issuingActor, targetActor); /* resolve the phrase as a topic */ local match = resolveNouns(resolver, results); /* * make sure it's packaged in the canonical topic form, as a * ResolvedTopic object */ match = resolver.packageTopicList(match, self); /* if the topic can match it, it's a possible resolution */ return (match != nil && match.length() > 0 && match[1].obj_.canMatchObject(obj)); } /* * Is this match a match to the special syntax for a custom missing * object query? This returns true if the match has a wording that * strongly distinguishes it from an ordinary new command. In the * English parser, for example, this returns true for the * PrepSingleTopicProd matches (e.g., inSingleNoun) if the phrase * starts with the preposition for the match. * * This property is used when we ask a missing object question: * *. >dig *. What do you want to dig in? *. *. >in the dirt * * In English, the DIG command sets up to receive a response phrased * as "in " - that's done by setting the response * production to inSingleNoun. In this case, "in the dirt" would * return true, since that's pretty clearly a match to the expected * inSingleNoun syntax. In contrast, "the dirt" would return false, * since that's just a noun phrase without the special wording for * this particular verb. */ isSpecialResponseMatch = nil /* * Grammar match objects that come from a GrammarProd.parseTokens() * call will always have a set of properties indicating which tokens * from the input matched the grammar rule. However, we sometimes * synthesize match trees internally rather than getting them from * parser input; for synthesized trees, the parser obviously won't * supply those properties for us, so we need to define suitable * defaults that synthesized match tree nodes can inherit. */ firstTokenIndex = 0 lastTokenIndex = 0 ; /* ------------------------------------------------------------------------ */ /* * Basic disambiguation production class */ class DisambigProd: BasicProd /* * Remove the "ambiguous" flags from a result list. This can be * used to mark the response to a disambiguation query as no longer * ambiguous. */ removeAmbigFlags(lst) { /* remove the "unclear disambig" flag from each result item */ foreach (local cur in lst) cur.flags_ &= ~UnclearDisambig; /* return the list */ return lst; } ; /* ------------------------------------------------------------------------ */ /* * Base class for "direction" productions. Each direction (the compass * directions, the vertical directions, the shipboard directions, and so * on) must have an associated grammar rule, which must produce one of * these. This should be subclassed with grammar rules like this: * * grammar directionName: 'north' | 'n' : DirectionProd *. dir = northDirection *. ; */ class DirectionProd: BasicProd /* * Each direction-specific grammar rule subclass must set this * property to the associated direction object (northDirection, * etc). */ dir = nil ; /* ------------------------------------------------------------------------ */ /* * The base class for commands. A command is the root of the grammar * match tree for a single action. A command line can consist of a * number of commands joined with command separators; in English, * command separators are things like periods, semicolons, commas, and * the words "and" and "then". */ class CommandProd: BasicProd hasTargetActor() { /* * By default, a command production does not include a * specification of a target actor. Command phrases that * contain syntax specifically directing the command to a target * actor should return true here. */ return nil; } /* * Get the match tree for the target actor phrase, if any. By * default, we have no target actor phrase, so just return nil. */ getActorPhrase = nil /* * "Execute" the actor phrase. This lets us know that the parser * has decided to use our phrasing to specify the target actor. * We're not required to do anything here; it's just a notification * for subclass use. Since we don't have a target actor phrase at * all, we obviously don't need to do anything here. */ execActorPhrase(issuingActor) { } ; /* * A first-on-line command. The first command on a command line can * optionally start with an actor specification, to give orders to the * actor. */ class FirstCommandProd: CommandProd countCommands(results) { /* count commands in the underlying command */ cmd_.countCommands(results); } getTargetActor() { /* * we have no actor specified explicitly, so it's the current * player character */ return libGlobal.playerChar; } /* * The tokens of the entire command except for the target actor * specification. By default, we take all of the tokens starting * with the first command's first token and running to the end of * the token list. This assumes that the target actor is specified * at the beginning of the command - languages that use some other * word ordering must override this accordingly. */ getCommandTokens() { return tokenList.sublist(cmd_.firstTokenIndex); } /* * Resolve my first action. This returns an instance of a subclass * of Action that represents the resolved action. We'll ask our * first subcommand to resolve its action. */ resolveFirstAction(issuingActor, targetActor) { return cmd_.resolveFirstAction(issuingActor, targetActor); } /* resolve nouns in the command */ resolveNouns(issuingActor, targetActor, results) { /* resolve nouns in the underlying command */ cmd_.resolveNouns(issuingActor, targetActor, results); /* count our commands */ countCommands(results); } /* * Does this command end a sentence? The exact meaning of a * sentence may vary by language; in English, a sentence ends with * certain punctuation marks (a period, a semicolon, an exclamation * point). */ isEndOfSentence() { /* ask the underlying command phrase */ return cmd_.isEndOfSentence(); } /* * Get the token index of the first command separator token. This * is the first token that is not part of the underlying command. */ getCommandSepIndex() { /* get the separator index from the underlying command */ return cmd_.getCommandSepIndex(); } /* * get the token index of the next command - this is the index of * the next token after our conjunction if we have one, or after our * command if we don't have a conjunction */ getNextCommandIndex() { /* get the next command index from the underlying command */ return cmd_.getNextCommandIndex(); } ; /* * Define the simplest grammar rule for a first-on-line command phrase, * which is just an ordinary command phrase. The language-specific * grammar must define any other alternatives; specifically, the * language might provide an "actor, command" syntax to direct a command * to a particular actor. */ grammar firstCommandPhrase(commandOnly): commandPhrase->cmd_ : FirstCommandProd ; /* * A command with an actor specification. This should be instantiated * with grammar rules in a language-specific module. * * Instantiating grammar rules should set property actor_ to the actor * match tree, and cmd_ to the underlying 'commandPhrase' production * match tree. */ class CommandProdWithActor: CommandProd hasTargetActor() { /* this command explicitly specifies an actor */ return true; } getTargetActor() { /* return my resolved actor object */ return resolvedActor_; } getActorPhrase() { /* return the actor phrase production */ return actor_; } /* * Execute the target actor phrase. This is a notification, for use * by subclasses; we don't have anything we need to do in this base * class implementation. */ execActorPhrase(issuingActor) { } /* * Resolve nouns. We'll resolve only the nouns in the target actor * phrase; we do not resolve nouns in the command phrase, because we * must re-parse the command phrase after we've finished resolving * the actor phrase, and thus we can't resolve nouns in the command * phrase until after the re-parse is completed. */ resolveNouns(issuingActor, targetActor, results) { local lst; /* * Resolve the actor, then we're done. Note that we do not * attempt to resolve anything in the underlying command yet, * because we can only resolve the command after we've fully * processed the actor clause. */ lst = actor_.resolveNouns(getResolver(issuingActor), results); /* * there are no noun phrase slots, since we're not looking at the * verb */ results.noteNounSlots(0); /* * if we have an object in the list, use it - note that our * actor phrase is a single-noun production, and hence should * never yield more than a single entry in its resolution list */ if (lst.length() > 0) { /* remember the resolved actor */ resolvedActor_ = lst[1].obj_; /* note in the results that an actor is specified */ results.noteActorSpecified(); } /* count our commands */ countCommands(results); } /* get or create my actor resolver */ getResolver(issuingActor) { /* if I don't already have a resolver, create one and cache it */ if (resolver_ == nil) resolver_ = new ActorResolver(issuingActor); /* return our cached resolver */ return resolver_; } /* my resolved actor object */ resolvedActor_ = nil /* my actor resolver object */ resolver_ = nil ; /* * First-on-line command with target actor. As with * CommandProdWithActor, instantiating grammar rules must set the * property actor_ to the actor match tree, and cmd_ to the underlying * commandPhrase match. */ class FirstCommandProdWithActor: CommandProdWithActor, FirstCommandProd ; /* ------------------------------------------------------------------------ */ /* * The 'predicate' production is the grammar rule for all individual * command phrases. We don't define anything about the predicate * grammar here, since it is completely language-dependent, but we do * *use* the predicate production as a sub-production of our * commandPhrase rules. * * The language-dependent implementation of the 'predicate' production * must provide the following methods: * * resolveAction(issuingActor, targetActor): This method returns a * newly-created instance of the Action subclass that the command refers * to. This method must generally interpret the phrasing of the command * to determine what noun phrases are present and what roles they serve, * and what verb phrase is present, then create an appropriate Action * subclass to represent the verb phrase as applied to the noun phrases * in their determined roles. * * resolveNouns(issuingActor, targetActor, results): This method * resolves all of the noun phrases in the predicate, using the * 'results' object (an object of class ResolveResults) to store * information about the status of the resolution. Generally, this * routine should collect information about the roles of the noun phrase * production matches, plug these into the Action via appropriate * properties (dobjMatch for the direct object of a transitive verb, for * example), resolve the Action, and then call the Action to do the * resolution. This method has no return value, since the Action is * responsible for keeping track of the results of the resolution. * * For languages like English which encode most information about the * phrase roles in word ordering, a good way to design a predicate * grammar is to specify the syntax of each possible verb phrase as a * 'predicate' rule, and base the match object for that rule on the * corresponding Action subclass. For example, the English grammar has * this rule to define the syntax for the verb 'take': * * VerbRule *. ('take' | 'pick' 'up' | 'get') dobjList *. | 'pick' dobjList 'up' *. : TakeAction *. ; * * Since English encodes everything in this command positionally, it's * straightforward to write grammar rules for the possible syntax * variations of a given action, hence it's easy to associate command * syntax directly with its associated action. When each 'predicate' * grammar maps to an Action subclass for its match object, * resolveAction() is especially easy to implement: it simply returns * 'self', since the grammar match and the Action are the same object. * It's also easy to plug each noun phrase into its appropriate property * slot in the Action subclass: the parser can be told to plug in each * noun phrase directly using the "->" notation in the grammar. * * Many languages encode the roles of noun phrases with case markers or * other syntactic devices, and as a result do not impose such strict * rules as English does on word order. For such languages, it is * usually better to construct the 'predicate' grammar separately from * any single action, so that the various acceptable phrase orderings * are enumerated, and the verb phrase is just another phrase that plugs * into these top-level orderings. In such grammars, the predicate must * do some programmatic work in resolveAction(): it must figure out * which Action subclass is involved based on the verb phrase sub-match * object and the noun phrases present, then must create an instance of * that Action subclass. Furthermore, since we can't plug the noun * phrases into the Action using the "->" notation in the grammar, the * resolveAction() routine must pick out the sub-match objects * representing the noun phrases and plug them into the Action itself. * * Probably the easiest way to accomplish all of this is by designing * each verb phrase match object so that it is associated with one or * more Action subclasses, according to the specific noun phrases * present. The details of this mapping are obviously specific to each * language, but it should be possible in most cases to build a base * class for all verb phrases that uses parameters to create the Action * and noun phrase associations. For example, each verb phrase grammar * match object might have a list of possible Action matches, such as * this example from a purely hypothetical English grammar based on this * technique * * grammar verbPhrase: 'take' | 'pick' 'up': VerbProd *. actionMap = *. [ *. [TakeAction, BasicProd, &dobjMatch], *. [TakeWithAction, BasicProd, &dobjMatch, WithProd, &iobjMatch] *. ] *. ; * * The idea is that the base VerbProd class looks through the list given * by the actionMap property for a template that matches the number and * type of noun phrases present in the predicate; when it finds a match, * it creates an instance of the Action subclass given in the template, * then plugs the noun phrases into the listed properties of the new * Action instance. * * Note that our verbPhrase example above is not an example of actual * working code, because there is no such thing in the * language-independent library as a VerbProd base class that reads * actionMap properites - this is a purely hypothetical bit of code to * illustrate how such a construction might work in a language that * requires it, and it is up to the language-specific module to define * such a mechanism for its own use. */ /* ------------------------------------------------------------------------ */ /* * Base classes for grammar matches for full commands. * * There are two kinds of command separators: ambiguous and unambiguous. * Unambiguous separators are those that can separate only commands, such * as "then" and periods. Ambiguous separators are those that can * separate nouns in a noun list as well as commands; for example "and" * and commas. * * First, CommandProdWithDefiniteConj, which is for a single full * command, unambiguously terminated with a separator that can only mean * that a new command follows. We parse one command at a time when a * command line includes several commands, since we can only resolve * objects - and thus can only choose structural interpretations - when * we reach the game state in which a given command is to be executed. * * Second, CommandProdWithAmbiguousConj, which is for multiple commands * separated by a conjunction that does not end a sentence, but could * just as well separate two noun phrases. In this case, we parse both * commands, to ensure that we actually have a well-formed command * following the conjunction; this allows us to try interpreting the part * after the conjunction as a command and also as a noun phrase, to see * which interpretation looks better. * * When we find an unambiguous command separator, we can simply use the * '*' grammar match to put off parsing everything after the separator * until later, reducing the complexity of the grammar tree. When we * find an ambiguous separator, we can't put off parsing the rest with * '*', because we need to know if the part after the separator can * indeed be construed as a new command. During resolution, we'll take a * noun list interpretation of 'and' over a command list whenever doing * so would give us resolvable noun phrases. */ /* * Match class for a command phrase that is separated from anything that * follows with an unambiguous conjunction. * * Grammar rules based on this match class must set property cmd_ to the * underlying 'predicate' production. */ class CommandProdWithDefiniteConj: CommandProd resolveNouns(issuingActor, targetActor, results) { /* resolve nouns */ cmd_.resolveNouns(issuingActor, targetActor, results); /* count commands */ countCommands(results); } countCommands(results) { /* we have only one subcommand */ if (cmdCounted_++ == 0) results.incCommandCount(); } /* counter: have we counted our command in the results object yet? */ cmdCounted_ = 0 /* resolve my first action */ resolveFirstAction(issuingActor, targetActor) { return cmd_.resolveAction(issuingActor, targetActor); } /* does this command end a sentence */ isEndOfSentence() { /* * it's the end of the sentence if our predicate encompasses all * of the tokens in the command, or the conjunction is a * sentence-ending conjunction */ return conj_ == nil || conj_.isEndOfSentence(); } /* * Get the token index of the first command separator token. This * is the first token that is not part of the underlying command. */ getCommandSepIndex() { /* * if we have a conjunction, return the first token index of the * conjunction; otherwise, return the index of the next token * after the command itself */ if (conj_ != nil) return conj_.firstTokenIndex; else return cmd_.lastTokenIndex + 1; } /* * get the token index of the next command - this is the index of * the next token after our conjunction if we have one, or after our * command if we don't have a conjunction */ getNextCommandIndex() { return (conj_ == nil ? cmd_ : conj_).lastTokenIndex + 1; } ; /* * Match class for two command phrases separated by an ambiguous * conjunction (i.e., a conjunction that could also separate two noun * phrases). Grammar rules based on this class must set the properties * 'cmd1_' to the underlying 'predicate' production match of the first * command, and 'cmd2_' to the underlying 'commandPhrase' production * match of the second command. */ class CommandProdWithAmbiguousConj: CommandProd resolveNouns(issuingActor, targetActor, results) { /* * Resolve nouns in the first subcommand only. Do NOT resolve * nouns in any of the additional subcommands (there might be * more than one, since cmd2_ can be a list of subcommands, not * just a single subcommand), because we cannot assume that the * current scope will continue to be valid after executing the * first subcommand - the first command could take us to a * different location, or change the lighting conditions, or add * or remove objects from the location, or any number of other * things that would invalidate the current scope. */ cmd1_.resolveNouns(issuingActor, targetActor, results); /* count our commands */ countCommands(results); } countCommands(results) { /* count our first subcommand (cmd1_) */ if (cmdCounted_++ == 0) results.incCommandCount(); /* count results in the rest of the list (cmd2_ and its children) */ cmd2_.countCommands(results); } /* counter: have we counted our command in the results object yet? */ cmdCounted_ = 0 /* resolve my first action */ resolveFirstAction(issuingActor, targetActor) { return cmd1_.resolveAction(issuingActor, targetActor); } /* does this command end a sentence */ isEndOfSentence() { /* * it's the end of the sentence if the conjunction is a * sentence-ending conjunction */ return conj_.isEndOfSentence(); } /* * Get the token index of the first command separator token. This * is the first token that is not part of the underlying command. */ getCommandSepIndex() { /* return the conjunction's first token index */ return conj_.firstTokenIndex; } /* * get the token index of the next command - this is simply the * starting index for our second subcommand tree */ getNextCommandIndex() { return cmd2_.firstTokenIndex; } ; /* * The basic grammar rule for an unambiguous end-of-sentence command. * This matches either a predicate with nothing following, or a predicate * with an unambiguous command-only conjunction following. */ grammar commandPhrase(definiteConj): predicate->cmd_ | predicate->cmd_ commandOnlyConjunction->conj_ * : CommandProdWithDefiniteConj ; /* * the basic grammar rule for a pair of commands with an ambiguous * separator (i.e., a separator that could separate commands or * conjunctions) */ grammar commandPhrase(ambiguousConj): predicate->cmd1_ commandOrNounConjunction->conj_ commandPhrase->cmd2_ : CommandProdWithAmbiguousConj ; /* ------------------------------------------------------------------------ */ /* * We leave almost everything about the grammatical rules of noun * phrases to the language-specific implementation, but we provide a set * of base classes for implementing several conceptual structures that * have equivalents in most languages. */ /* * Basic noun phrase production class. */ class NounPhraseProd: BasicProd /* * Determine whether this kind of noun phrase prefers to keep a * collective or the collective's individuals when filtering. If * this is true, we'll keep a collective and discard its individuals * when filtering a resolution list; otherwise, we'll drop the * collective and keep the individuals. */ filterForCollectives = nil /* * Filter a "verify" result list for the results we'd like to keep * in the final resolution of the noun phrase. This is called after * we've run through the verification process on the list of * candidate matches, so 'lst' is a list of ResolveInfo objects, * sorted in descending order of logicalness. * * By default, we keep only the items that are equally as logical as * the best item in the results. Since the items are sorted in * descending order of goodness, the best item is always the first * item. */ getVerifyKeepers(results) { local best; /* * Reduce the list to the most logical elements - in other * words, keep only the elements that are exactly as logical as * the first element, which we know to have the best logicalness * ranking in the list by virtue of having sorted the list in * descending order of logicalness. */ best = results[1]; return results.subset({x: x.compareTo(best) == 0}); } /* * Filter a match list of results for truncated matches. If we have * a mix of truncated matches and exact matches, we'll keep only the * exact matches. If we have only truncated matches, though, we'll * return the list unchanged, as we don't have a better offer going. */ filterTruncations(lst, resolver) { local exactLst; /* * If we're in "global scope," it means that we're resolving * something like a topic phrase, and we're not limited to the * current physical scope but can choose objects from the entire * game world. In this case, don't apply any truncation * filtering. The reason is that we could have several unrelated * objects in the game with vocabulary that differs only in * truncation, but the user has forgotten about the ones with the * longer names and is thinking of something she's referred to * with truncation in the past, when the longer-named objects * weren't around. We're aware of all of those longer-named * objects, but it's not helpful to the player, since they might * currently be out of sight. */ if (resolver.isGlobalScope) return lst; /* get the list of exact (i.e., untruncated) matches */ exactLst = lst.subset( {x: (x.flags_ & (VocabTruncated | PluralTruncated)) == 0}); /* * if we have any exact matches, use only the exact matches; if * we don't, use the full list as is */ return (exactLst.length() != 0 ? exactLst : lst); } ; /* * Basic noun phrase list production class. */ class NounListProd: BasicProd ; /* * Basic "layered" noun phrase production. It's often useful to define * a grammar rule that simply defers to an underlying grammar rule; we * make this simpler by defining this class that automatically delegates * resolveNouns to the underlying noun phrase given by the property np_. */ class LayeredNounPhraseProd: NounPhraseProd resolveNouns(resolver, results) { /* let the underlying match tree object do the work */ return np_.resolveNouns(resolver, results); } ; /* ------------------------------------------------------------------------ */ /* * A single noun is sometimes required where, structurally, a list is * not allowed. Single nouns should not be used to prohibit lists where * there is no structural reason for the prohibition - these should be * used only where it doesn't make sense to use a list structurally. */ class SingleNounProd: NounPhraseProd resolveNouns(resolver, results) { /* tell the results object we're resolving a single-object slot */ results.beginSingleObjSlot(); /* resolve the underlying noun phrase */ local lst = np_.resolveNouns(resolver, results); /* tell the results object we're done with the single-object slot */ results.endSingleObjSlot(); /* make sure the list has only one element */ if (lst.length() > 1) results.uniqueObjectRequired(getOrigText(), lst); /* return the results */ return lst; } ; /* * A user could attempt to use a noun list where a single noun is * required. This is not a grammatical error, so we accept it * grammatically; however, for disambiguation purposes we score it lower * than a singleNoun production with only one noun phrase, and if we try * to resolve it, we'll fail with an error. */ class SingleNounWithListProd: NounPhraseProd resolveNouns(resolver, results) { /* note the problem */ results.singleObjectRequired(getOrigText()); /* * In case we have any unknown words or other detrimental * aspects, resolve the underlying noun list as well, so we can * score even lower. Note that doing this after noting our * single-object-required problem will avoid any interactive * resolution (such as asking for disambiguation information), * since once we get to the point where we're ready to do * anything interactive, the single-object-required results * notification will fail with an error, so we won't make it to * this point during interactive resolution. */ np_.resolveNouns(resolver, results); /* we have no resolution */ return []; } ; /* * A topic is a noun phrase used in commands like "ask about * ." For our purposes, this works as an ordinary single noun * production. */ class TopicProd: SingleNounProd /* get the original text and tokens from the underlying phrase */ getOrigTokenList() { return np_.getOrigTokenList(); } getOrigText() { return np_.getOrigText(); } resolveNouns(resolver, results) { /* note that we're entering a topic slot */ results.beginTopicSlot(); /* do the inherited work */ local ret = inherited(resolver, results); /* we're leaving the topic slot */ results.endTopicSlot(); /* return the resolved list */ return ret; } ; /* * A literal is a string enclosed in quotes. */ class LiteralProd: BasicProd ; /* * Basic class for pronoun phrases. The specific pronouns are * language-dependent; each instance should define its pronounType * property to an appropriate PronounXxx constant. */ class PronounProd: NounPhraseProd resolveNouns(resolver, results) { local lst; /* * check for a valid anaphoric binding (i.e., a back-reference to * an object mentioned earlier in the current command: ASK BOB * ABOUT HIMSELF) */ lst = checkAnaphoricBinding(resolver, results); /* * if we didn't get an anaphoric binding, find an antecedent from * a previous command */ if (lst == nil) { /* ask the resolver to get the antecedent */ lst = resolver.resolvePronounAntecedent( pronounType, self, results, isPossessive); /* if there are no results, note the error */ if (lst == []) results.noMatchForPronoun(pronounType, getOrigText().toLower()); /* remember the pronoun type in each ResolveInfo */ lst.forEach({x: x.pronounType_ = pronounType}); /* * If the pronoun is singular, but we got multiple potential * antecedents, it means that the previous command had * multiple noun slots (as in UNLOCK DOOR WITH KEY) and * didn't want to decide a priori which one is the antecedent * for future pronouns. So, we have to decide now which of * the potential antecedents to use as the actual antecedent. * Choose the most logical, if there's a clearly more logical * one. */ if (!isPlural && lst.length() > 1) { /* filter the phrase using the normal disambiguation */ lst = resolver.filterAmbiguousNounPhrase(lst, 1, self); /* * if that leaves more than one item, pick the first one * and mark it as unclearly disambiguated */ if (lst.length() > 1) { /* arbitrarily keep the first item only */ lst = lst.sublist(1, 1); /* mark it as an arbitrary choice */ lst[1].flags_ |= UnclearDisambig; } } } /* note that we have phrase involving a pronoun */ results.notePronoun(); /* return the result list */ return lst; } /* * our pronoun specifier - this must be set in each rule instance to * one of the PronounXxx constants to specify which pronoun to use * when resolving the pronoun phrase */ pronounType = nil /* is this a possessive usage? */ isPossessive = nil /* * Is this pronoun a singular or a plural? A pronoun like "it" or * "he" is singular, because it refers to a single antecedent; "them" * is plural. Language modules that define their own custom pronoun * subclasses should override this as needed. */ isPlural = nil /* * Check for an anaphoric binding. Returns a list (which is allowed * to be empty) if this can refer back to an earlier noun phrase in * the same command, nil if not. By default, we consider pronouns * to be non-anaphoric, meaning they refer to something from a * previous sentence, not something in this same sentence. In most * languages, pronouns don't refer to objects in other noun phrases * within the same predicate unless they're reflexive. */ checkAnaphoricBinding(resolver, results) { return nil; } ; /* * For simplicity, define subclasses of PronounProd for the basic set of * pronouns found in most languages. Language-specific grammar * definitions can choose to use these or not, and can add their own * extra subclasses as needed for types of pronouns we don't define * here. */ class ItProd: PronounProd pronounType = PronounIt ; class ThemProd: PronounProd pronounType = PronounThem isPlural = true ; class HimProd: PronounProd pronounType = PronounHim ; class HerProd: PronounProd pronounType = PronounHer ; class MeProd: PronounProd pronounType = PronounMe ; class YouProd: PronounProd pronounType = PronounYou ; /* * Third-person anaphoric reflexive pronouns. These refer to objects * that appeared earlier in the sentence: "ask bob about himself." */ class ReflexivePronounProd: PronounProd resolveNouns(resolver, results) { /* ask the resolver for the reflexive pronoun binding */ local lst = resolver.getReflexiveBinding(pronounType); /* * if the result is empty, the verb will provide the binding * later, on a second pass */ if (lst == []) return lst; /* * If the result is nil, the verb is saying that the reflexive * pronoun makes no sense internally within the predicate * structure. In this case, or if we did get a list that * doesn't agree with the pronoun type (in number or gender, for * example), consider the reflexive to refer back to the actor, * for a construct such as TELL BOB TO HIT HIMSELF. However, * only do this if the issuer and target actor aren't the same, * since we generally don't refer to the PC in the third person. */ if ((lst == nil || !checkAgreement(lst)) && resolver.actor_ != resolver.issuer_) { /* try treating the actor as the reflexive pronoun */ lst = [new ResolveInfo(resolver.actor_, 0, self)]; } /* if the list is nil, it means reflexives aren't allowed here */ if (lst == nil) { results.reflexiveNotAllowed(pronounType, getOrigText.toLower()); return []; } /* * Check the list for agreement (in gender, number, and so on). * Don't bother if the list is empty, as this is the action's * way of telling us that it doesn't have a binding for us yet * but will provide one on a subsequent attempt at re-resolving * this phrase. */ if (!checkAgreement(lst)) results.wrongReflexive(pronounType, getOrigText().toLower()); /* return the result list */ return lst; } /* * Check that the binding we found for our reflexive pronoun agrees * with the pronoun in gender, number, and anything else that it has * to agree with. This must be defined by each concrete subclass. * Note that language-specific subclasses can and *should* override * this to test agreement for the local language's rules. * * This should return true if we agree, nil if not. */ checkAgreement(lst) { return true; } ; class ItselfProd: ReflexivePronounProd pronounType = PronounIt ; class ThemselvesProd: ReflexivePronounProd pronounType = PronounThem ; class HimselfProd: ReflexivePronounProd pronounType = PronounHim ; class HerselfProd: ReflexivePronounProd pronounType = PronounHer ; /* * The special 'all' constructions are full noun phrases. */ class EverythingProd: BasicProd resolveNouns(resolver, results) { local lst; /* check to make sure 'all' is allowed */ if (!resolver.allowAll()) { /* it's not - flag an error and give up */ results.allNotAllowed(); return []; } /* get the 'all' list */ lst = resolver.getAll(self); /* * set the "always announce" flag for each item - the player * didn't name these items specifically, so always show what we * chose */ foreach (local cur in lst) cur.flags_ |= AlwaysAnnounce | MatchedAll; /* make sure there's something in it */ if (lst.length() == 0) results.noMatchForAll(); /* return the list */ return lst; } /* match Collective objects instead of their individuals */ filterForCollectives = true ; /* ------------------------------------------------------------------------ */ /* * Basic exclusion list ("except the silver one") production base class. */ class ExceptListProd: BasicProd ; /* * Basic "but" rule, which selects a list of plurals minus a list of * specifically excepted objects. This can be used to construct more * specific production classes for things like "everything but the book" * and "all books except the red ones". * * In each grammar rule based on this class, the 'except_' property must * be set to a suitable noun phrase for the exception list. We'll * resolve this list and remove the objects in it from our main list. */ class ButProd: NounPhraseProd resolveNouns(resolver, results) { local mainList; local butList; local butRemapList; local action; local role; local remapProp; /* get our main list of items to include */ mainList = getMainList(resolver, results); /* filter out truncated matches if we have any exact matches */ mainList = filterTruncations(mainList, resolver); /* * resolve the 'except' list - use an 'except' resolver based on * our list so that we resolve these objects in the scope of our * main list */ butList = except_.resolveNouns( new ExceptResolver(mainList, getOrigText(), resolver), new ExceptResults(results)); /* if the exception list is empty, tell the results about it */ if (butList == []) results.noteEmptyBut(); /* * Get the remapping property for this object role for this * action. This property applies to each of the objects we're * resolving, and tells us if the resolved object is going to * remap its handling of this action when the object is used in * this role. For example, the Take action's remapping property * for the direct object would usually be remapDobjTake, so * book.remapDobjTake would tell us if TAKE BOOK were going to * be remapped. */ action = resolver.getAction(); role = resolver.whichObject; remapProp = action.getRemapPropForRole(role); /* get the list of simple synonym remappings for the 'except' list */ butRemapList = butList.mapAll( {x: action.getSimpleSynonymRemap(x.obj_, role, remapProp)}); /* * scan the 'all' list, and remove each item that appears in the * 'except' list */ for (local i = 1, local len = mainList.length() ; i <= len ; ++i) { local curRemap; /* get the current 'all' list element */ local cur = mainList[i].obj_; /* get the simple synonym remapping for this item, if any */ curRemap = action.getSimpleSynonymRemap(cur, role, remapProp); /* * If this item appears in the 'except' list, remove it. * * Similarly, if this item is remapped to something that * appears in the 'except' list, remove it. * * Similarly, if something in the 'except' list is remapped * to this item, remove this item. */ if (butList.indexWhich({x: x.obj_ == cur}) != nil || butRemapList.indexWhich({x: x == cur}) != nil || butList.indexWhich({x: x.obj_ == curRemap}) != nil) { /* remove it and adjust our loop counters accordingly */ mainList = mainList.removeElementAt(i); --i; --len; } } /* if that doesn't leave anything, it's an error */ if (mainList == []) flagAllExcepted(resolver, results); /* perform the final filtering on the list for our subclass */ mainList = filterFinalList(mainList); /* add any flags to the result list that our subclass indicates */ foreach (local cur in mainList) cur.flags_ |= addedFlags; /* note the matched objects in the results */ results.noteMatches(mainList); /* return whatever we have left after the exclusions */ return mainList; } /* get my main list, which is the list of items to include */ getMainList(resolver, results) { return []; } /* flag an error - everything has been excluded by the 'but' list */ flagAllExcepted(resolver, results) { } /* filter the final list - by default we just return the same list */ filterFinalList(lst) { return lst; } /* by default, add no extra flags to our resolved object list */ addedFlags = 0 ; /* ------------------------------------------------------------------------ */ /* * Base class for "all but" rules, which select everything available * except for objects in a specified list of exceptions; for example, in * English, "take everything but the book". */ class EverythingButProd: ButProd /* our main list is given by the "all" list */ getMainList(resolver, results) { /* check to make sure 'all' is allowed */ if (!resolver.allowAll()) { /* it's not - flag an error and give up */ results.allNotAllowed(); return []; } /* return the 'all' list */ return resolver.getAll(self); } /* flag an error - our main list has been completely excluded */ flagAllExcepted(resolver, results) { results.noMatchForAllBut(); } /* * set the "always announce" flag for each item - the player didn't * name the selected items specifically, so always show what we * chose */ addedFlags = AlwaysAnnounce /* match Collective objects instead of their individuals */ filterForCollectives = true ; /* * Base class for "list but" rules, which select everything in an * explicitly provided list minus a set of exceptions; for example, in * English, "take all of the books except the red ones". * * Subclasses defining grammar rules must set the 'np_' property to the * main noun list; we'll resolve this list to find the objects to be * included before exclusions are applied. */ class ListButProd: ButProd /* our main list is given by the 'np_' subproduction */ getMainList(resolver, results) { return np_.resolveNouns(resolver, results); } /* flag an error - everything has been excluded */ flagAllExcepted(resolver, results) { results.noMatchForListBut(); } /* * set the "unclear disambig" flag in our results, so we provide an * indication of which object we chose */ addedFlags = UnclearDisambig ; /* ------------------------------------------------------------------------ */ /* * Pre-resolved phrase production. This isn't normally used in any * actual grammar; instead, this is for use when building actions * programmatically. This allows us to fill in an action tree when we * already know the object we want to resolve. */ class PreResolvedProd: BasicProd construct(obj) { /* if it's not a collection, we need to make it a list */ if (!obj.ofKind(Collection)) { /* if it's not already a ResolveInfo, wrap it in a ResolveInfo */ if (!obj.ofKind(ResolveInfo)) obj = new ResolveInfo(obj, 0, self); /* the resolved object list is simply the one ResolveInfo */ obj = [obj]; } /* store the new ResolveInfo list */ obj_ = obj; } /* resolve the nouns: this is easy, since we started out resolved */ resolveNouns(resolver, results) { /* return our pre-resolved object */ return obj_; } /* * Our pre-resolved object result. This is a list containing a * single ResolveInfo representing our resolved object, since this is * the form required by callers of resolveNouns. */ obj_ = nil ; /* * A pre-resolved *ambiguous* noun phrase. This is used when the game * or library wants to suggest a specific set of objects for a new * action, then ask which one to use. */ class PreResolvedAmbigProd: DefiniteNounProd construct(objs, asker, phrase) { /* remember my list of possible objects as a resolved object list */ objs_ = objs.mapAll({x: new ResolveInfo(x, 0, nil)}); /* remember the ResolveAsker to use */ asker_ = asker; /* remember the noun phrase to use in disambiguation questions */ phrase_ = phrase; } resolveNouns(resolver, results) { /* resolve our list using definite-phrase rules */ return resolveDefinite(asker_, phrase_, objs_, self, resolver, results); } /* my pre-resolved list of ambiguous objects */ objs_ = nil /* the noun phrase to use in disambiguation questions */ phrase_ = nil /* the ResolveAsker to use when prompting for the selection */ asker_ = nil ; /* * Pre-resolved literal phrase production */ class PreResolvedLiteralProd: BasicProd construct(txt) { /* * If the argument is a ResolveInfo, assume its obj_ property * gives the literal string, and retrieve the string. */ if (txt.ofKind(ResolveInfo)) txt = txt.obj_; /* save the text */ text_ = txt; } /* get the text */ getLiteralText(results, action, which) { return text_; } getTentativeLiteralText() { return text_; } /* our underlying text */ text_ = nil ; /* * A token list production is an internally generated placeholder when we * synthesize a production rather than matching grammar, and we want to * keep track of the token list that triggered the node. */ class TokenListProd: BasicProd construct(toks) { tokenList = toks; firstTokenIndex = 1; lastTokenIndex = toks.length(); } /* the token list */ tokenList = nil ; /* ------------------------------------------------------------------------ */ /* * Mix-in class for noun phrase productions that use * ResolveResults.ambiguousNounPhrase(). This mix-in provides the * methods that ambiguousNounPhrase() uses to keep track of past * responses to the disambiguation question. */ class AmbigResponseKeeper: object addAmbigResponse(resp) { /* add an ambiguous response to our list */ ambigResponses_ += resp; } getAmbigResponses() { /* return our list of past interactive disambiguation responses */ return ambigResponses_; } /* our list of saved interactive disambiguation responses */ ambigResponses_ = [] ; /* ------------------------------------------------------------------------ */ /* * Base class for noun phrase productions with definite articles. */ class DefiniteNounProd: NounPhraseProd, AmbigResponseKeeper resolveNouns(resolver, results) { /* resolve our underlying noun phrase using definite rules */ return resolveDefinite(ResolveAsker, np_.getOrigText(), np_.resolveNouns(resolver, results), self, resolver, results); } /* * Resolve an underlying phrase using definite noun phrase rules. */ resolveDefinite(asker, origText, lst, responseKeeper, resolver, results) { local scopeList; local fullList; /* filter the list to remove truncations if we have exact matches */ lst = filterTruncations(lst, resolver); /* * Remember the current list, before filtering for logical * matches and before filtering out equivalents, as our full * scope list. If we have to ask for clarification, this is the * scope of the clarification. */ scopeList = lst; /* filter for possessive qualification strength */ lst = resolver.filterPossRank(lst, 1); /* filter out the obvious mismatches */ lst = resolver.filterAmbiguousNounPhrase(lst, 1, self); /* * remember the current list as the full match list, before * filtering equivalents */ fullList = lst; /* * reduce the list to include only one of each set of * equivalents; do this only if the results object allows this * kind of filtering */ if (results.allowEquivalentFiltering) lst = resolver.filterAmbiguousEquivalents(lst, self); /* do any additional subclass-specific filtering on the list */ lst = reduceDefinite(lst, resolver, results); /* * This is (explicitly or implicitly) a definite noun phrase, so * we must resolve to exactly one object. If the list has more * than one object, we must disambiguate it. */ if (lst.length() == 0) { /* there are no objects matching the phrase */ results.noMatch(resolver.getAction(), origText); } else if (lst.length() > 1) { /* * the noun phrase is ambiguous - pass in the full list * (rather than the list with the redundant equivalents * weeded out) so that we can display the appropriate * choices for multiple equivalent items */ lst = results.ambiguousNounPhrase( responseKeeper, asker, origText, lst, fullList, scopeList, 1, resolver); } /* note the matched objects in the results */ results.noteMatches(lst); /* return the results */ return lst; } /* * Do any additional subclass-specific filtering to further reduce * the list before we decide whether or not we have sufficient * specificity. We call this just before deciding whether or not to * prompt for more information ("which book do you mean...?"). By * default, this simply returns the same list unchanged; subclasses * that have some further way of narrowing down the options can use * this opportunity to apply that extra narrowing. */ reduceDefinite(lst, resolver, results) { return lst; } ; /* * Base class for a plural production */ class PluralProd: NounPhraseProd /* * Basic plural noun resolution. We'll retrieve the matching objects * and filter them using filterPluralPhrase. */ basicPluralResolveNouns(resolver, results) { local lst; /* resolve the underlying noun phrase */ lst = np_.resolveNouns(resolver, results); /* if there's nothing in it, it's an error */ if (lst.length() == 0) results.noMatch(resolver.getAction(), np_.getOrigText()); /* filter out truncated matches if we have any exact matches */ lst = filterTruncations(lst, resolver); /* filter the plurals for the logical subset and return the result */ lst = resolver.filterPluralPhrase(lst, self); /* if we have a list, sort it by pluralOrder */ if (lst != nil) lst = lst.sort(SortAsc, {a, b: a.obj_.pluralOrder - b.obj_.pluralOrder}); /* note the matches */ results.noteMatches(lst); /* note the plural in the results */ results.notePlural(); /* return the result */ return lst; } /* * Get the verify "keepers" for a plural phrase. * * If the "filter plural matches" configuration flag is set to true, * we'll return the subset of items which are logical for this * command. If the filter flag is nil, we'll simply return the full * set of vocabulary matches without any filtering. */ getVerifyKeepers(results) { /* check the global "filter plural matches" configuration flag */ if (gameMain.filterPluralMatches) { local sub; /* get the subset of items that don't exclude plurals */ sub = results.subset({x: !x.excludePluralMatches}); /* * if that's non-empty, use it as the result; otherwise, just * use the original list */ return (sub.length() != 0 ? sub : results); } else { /* we don't want any filtering */ return results; } } ; /* * A plural phrase that explicitly selects all of matching objects. For * English, this would be a phrase like "all of the marbles". This type * of phrase matches all of the objects that match the name in the * plural, except that if we have a collective object and we also have * objects that are part of the collective (such as a bag of marbles and * some individual marbles), we'll omit the collective, and match only * the individual items. * * Grammar rule instantiations in language modules should set property * np_ to the plural phrase match tree. */ class AllPluralProd: PluralProd resolveNouns(resolver, results) { /* return the basic plural resolution */ return basicPluralResolveNouns(resolver, results); } /* * since the player explicitly told us to use ALL of the matching * objects, keep everything in the verify list, logical or not */ getVerifyKeepers(results) { return results; } /* prefer to keep individuals over collectives */ filterForCollectives = nil ; /* * A plural phrase qualified by a definite article ("the books"). This * type of phrasing doesn't specify anything about the specific number of * items involved, except that they number more than one. * * In most cases, we take this to imply that all of the matching objects * are intended to be included, with one exception: when we have an * object that can serve as a collective for some of the other objects, * we match only the collective but not the other objects. For example, * if we type "take marbles," and we have five marbles and a bag of * marbles that serves as a collective object for three of the five * marbles, we'll match the bag and two marbles not in the bag, but NOT * the marbles that are in the bag. This is usually desirable when * there's a collective object, since it applies the command to the * object standing in for the group rather than applying the command one * by one to each of the individuals in the group. */ class DefinitePluralProd: PluralProd resolveNouns(resolver, results) { /* return the basic plural resolution */ return basicPluralResolveNouns(resolver, results); } /* prefer to keep collectives instead of their individuals */ filterForCollectives = true ; /* * Quantified plural phrase. */ class QuantifiedPluralProd: PluralProd /* * Resolve the main noun phrase. By default, we simply resolve np_, * but we make this separately overridable to allow this class to be * subclassed for quantifying other types of plural phrases. * * If this is unable to resolve the list, it can flag an appropriate * error via the results object and return nil. If this routine * returns nil, our main resolver will simply return an empty list * without further flagging of any errors. */ resolveMainPhrase(resolver, results) { /* resolve the main noun phrase */ return np_.resolveNouns(resolver, results); } /* * get the quantity specified - by default, this comes from the * quantifier phrase in "quant_" */ getQuantity() { return quant_.getval(); } /* resolve the noun phrase */ resolveNouns(resolver, results) { local lst; local num; /* resolve the underlying noun phrase */ if ((lst = resolveMainPhrase(resolver, results)) == nil) return []; /* filter out truncated matches if we have any exact matches */ lst = filterTruncations(lst, resolver); /* sort the list in ascending order of pluralOrder */ if (lst != nil) lst = lst.sort(SortAsc, {a, b: a.obj_.pluralOrder - b.obj_.pluralOrder}); /* get the quantity desired */ num = getQuantity(); /* * if we have at least the desired number, arbitrarily choose * the desired number; otherwise, it's an error */ if (num == 0) { /* zero objects makes no sense */ results.zeroQuantity(np_.getOrigText()); } else { local qsum; /* if we have too many, disambiguate */ if (lst.length() >= num) { local scopeList; /* remember the list of everything in scope that matches */ scopeList = lst; /* filter for possessive qualifier strength */ lst = resolver.filterPossRank(lst, num); /* * Use the normal disambiguation ranking to find the best * set of possibilities. */ lst = resolver.filterAmbiguousNounPhrase(lst, num, self); /* * If that left us with more than we're looking for, call * our selection routine to select the subset. If it * left us with too few, note it in the results. */ if (lst.length() > num) { /* select the desired exact count */ lst = selectExactCount(lst, num, scopeList, resolver, results); } } /* * Check to make sure we have enough items matching. Go by * the 'quant_' property of the ResolveInfo entries, since we * might have a single ResolveInfo object that represents a * quantity of objects from the player's perspective. */ qsum = 0; lst.forEach({x: qsum += x.quant_}); if (qsum < num) { /* note in the results that there aren't enough matches */ results.insufficientQuantity(np_.getOrigText(), lst, num); } } /* note the matched objects in the results */ results.noteMatches(lst); /* return the results */ return lst; } /* * Select the desired number of matches from what the normal * disambiguation filtering leaves us with. * * Note that this will never be called with 'num' larger than the * number in the current list. This is only called to select a * smaller subset than we currently have. * * By default, we'll simply select an arbitrary subset, since we * simply want any 'num' of the matches. This can be overridden if * other behaviors are needed. */ selectExactCount(lst, num, scopeList, resolver, results) { /* * If we want less than what we actually got, arbitrarily pick * the first 'num' elements; otherwise, return what we have. */ if (lst.length() > num) return lst.sublist(1, num); else return lst; } /* * Since the player explicitly told us to use a given number of * matching objects, keep the required number, logical or not. */ getVerifyKeepers(results) { /* get the quantity desired */ local num = getQuantity(); /* * if we have at least the number required, arbitrarily choose * the initial subset of the desired length; otherwise, use them * all */ if (results.length() > num) return results.sublist(1, num); else return results; } ; /* * Exact quantified plural phrase. This is similar to the normal * quantified plural, but has the additional requirement of matching an * unambiguous set of the exact given number ("the five books" means * that we expect to find exactly five books matching the phrase - no * fewer, and no more). */ class ExactQuantifiedPluralProd: QuantifiedPluralProd, AmbigResponseKeeper /* * Select the desired number of matches. Since we want an exact set * of matches, we'll run disambiguation on the set. */ selectExactCount(lst, num, scopeList, resolver, results) { local fullList; /* remember the list before filtering for redundant equivalents */ fullList = lst; /* reduce the list by removing redundant equivalents, if allowed */ if (results.allowEquivalentFiltering) lst = resolver.filterAmbiguousEquivalents(lst, self); /* * if the reduced list has only one element, everything in the * original list must have been equivalent, so arbitrarily pick * the desired number of items from the original list */ if (lst.length() == 1) return fullList.sublist(1, num); /* we still have too many items, so disambiguate the results */ return results.ambiguousNounPhrase( self, ResolveAsker, np_.getOrigText(), lst, fullList, scopeList, num, resolver); } /* get the keepers in the verify stage */ getVerifyKeepers(results) { /* * keep everything: we want an exact quantity, so we want the * keepers to match the required quantity on their own, without * any arbitrary paring down */ return results; } ; /* * Noun phrase with an indefinite article */ class IndefiniteNounProd: NounPhraseProd /* * resolve the main phrase - this is separately overridable to allow * subclassing */ resolveMainPhrase(resolver, results) { /* by default, resolve the main noun phrase */ return np_.resolveNouns(resolver, results); } resolveNouns(resolver, results) { local lst; local allEquiv = nil; /* resolve the underlying list */ if ((lst = resolveMainPhrase(resolver, results)) == nil) return []; /* filter out truncated matches if we have any exact matches */ lst = filterTruncations(lst, resolver); /* see what we found */ if (lst.length() == 0) { /* it turned up nothing - note the problem */ results.noMatch(resolver.getAction(), np_.getOrigText()); } else if (lst.length() > 1) { /* * There are multiple objects, but the phrase is indefinite, * which means that it doesn't refer to a specific matching * object but could refer to any of them. */ /* start by noting if the choices are all equivalent */ allEquiv = areAllEquiv(lst); /* * Filter using possessive qualifier strength and then normal * disambiguation ranking to find the best set of * possibilities, then pick which we want. */ lst = resolver.filterPossRank(lst, 1); lst = resolver.filterAmbiguousNounPhrase(lst, 1, self); lst = selectFromList(resolver, results, lst); } /* * Set the "unclear disambiguation" flag on the item we picked - * our selection was arbitrary, so it's polite to let the player * know which we chose. However, don't do this if the possible * matches were all equivalent to start with, as the player's * input must already have been as specific as we can be in * reporting the choice. */ if (lst.length() == 1 && !allEquiv) lst[1].flags_ |= UnclearDisambig; /* note the matched objects in the results */ results.noteMatches(lst); /* note that this is an indefinite phrasing */ results.noteIndefinite(); /* return the results */ return lst; } /* are all of the items in the resolve list equivalents? */ areAllEquiv(lst) { local first = lst[1].obj_; /* check each item to see if it's equivalent to the first */ for (local i = 2, local cnt = lst.length() ; i <= cnt ; ++i) { /* * if this one isn't equivalent to the first, then they're * not all equivalent */ if (!first.isVocabEquivalent(lst[i].obj_)) return nil; } /* we didn't find any non-equivalents, so they're all equivalents */ return true; } /* * Select an item from the list of potential matches, given the list * sorted from most likely to least likely (according to the * resolver's ambiguous match filter). We'll ask the resolver to * make the selection, because indefinite noun phrases can mean * different things in different contexts. */ selectFromList(resolver, results, lst) { /* ask the resolver to select */ return resolver.selectIndefinite(results, lst, 1); } ; /* * Noun phrase explicitly asking us to choose an object arbitrarily * (with a word like "any"). This is similar to the indefinite noun * phrase, but differs in that this phrase is *explicitly* arbitrary, * rather than merely indefinite. */ class ArbitraryNounProd: IndefiniteNounProd /* * Select an object from a list of potential matches. Since the * choice is explicitly arbitrary, we simply choose the first * (they're in order from most likely to least likely, so this will * choose the most likely). */ selectFromList(resolver, results, lst) { /* simply select the first item */ return lst.sublist(1, 1); } ; /* * Noun phrase with an indefinite article and an exclusion ("any of the * books except the red one") */ class IndefiniteNounButProd: ButProd /* resolve our main phrase */ resolveMainPhrase(resolver, results) { /* note that this is an indefinite phrasing */ results.noteIndefinite(); /* by default, simply resolve the underlying noun phrase */ return np_.resolveNouns(resolver, results); } /* get our main list */ getMainList(resolver, results) { local lst; /* resolve the underlying list */ if ((lst = resolveMainPhrase(resolver, results)) == nil) return []; /* filter for possessive qualifier strength */ lst = resolver.filterPossRank(lst, 1); /* filter it to pick the most likely objects */ lst = resolver.filterAmbiguousNounPhrase(lst, 1, self); /* return the filtered list */ return lst; } /* flag an error - everything has been excluded */ flagAllExcepted(resolver, results) { results.noMatchForListBut(); } /* filter the final list */ filterFinalList(lst) { /* we want to keep only one item - arbitrarily take the first one */ return (lst.length() == 0 ? [] : lst.sublist(1, 1)); } /* * set the "unclear disambig" flag in our results, so we provide an * indication of which object we chose */ addedFlags = UnclearDisambig ; /* * A qualified plural phrase explicitly including two objects (such as, * in English, "both books"). */ class BothPluralProd: ExactQuantifiedPluralProd /* the quantity specified by a "both" phrase is 2 */ getQuantity() { return 2; } ; /* ------------------------------------------------------------------------ */ /* * Possessive adjectives */ class PossessivePronounAdjProd: PronounProd /* * Possessive pronouns can refer to the earlier noun phrases of the * same predicate, which is to say that they're anaphoric. For * example, in GIVE BOB HIS BOOK, 'his' refers to Bob. */ checkAnaphoricBinding(resolver, results) { local lst; /* if we simply can't be an anaphor, there's no binding */ if (!canBeAnaphor) return nil; /* ask the resolver for the reflexive binding, if any */ lst = resolver.getReflexiveBinding(pronounType); /* * If there's no binding from the verb, or it doesn't match in * number and gender, try an anaphoric binding from the actor. */ if (lst == nil || (lst != [] && !checkAnaphorAgreement(lst))) { /* get the actor's anaphoric possessive binding */ local obj = resolver.actor_.getPossAnaphor(pronounType); /* if we got an object or a list, make a resolve list */ if (obj != nil) { if (obj.ofKind(Collection)) lst = obj.mapAll({x: new ResolveInfo(x, 0, self)}); else lst = [new ResolveInfo(obj, 0, self)]; } } /* * If we got a binding, make sure we agree in number and gender; * if not, don't use the anaphoric form. This isn't an error; * it just means we're falling back on the regular antecedent * binding. If we have an empty list, it means the action isn't * ready to tell us the binding yet, so we can't verify it yet. */ if (lst != nil && (lst == [] || checkAnaphorAgreement(lst))) return lst; /* don't use an anaphoric binding */ return nil; } /* this is a possessive usage of the pronoun */ isPossessive = true /* * Can we be an anaphor? By default, we consider third-person * possessive pronouns to be anaphoric, and others to be * non-anaphoric. For example, in GIVE BOB MY BOOK, MY always refers * to the speaker, so it's clearly not anaphoric within the sentence. */ canBeAnaphor = true /* * Check agreement to a given anaphoric pronoun binding. The * language module should override this for each pronoun type to * ensure that the actual contents of the list agree in number and * gender with this type of pronoun. If so, return true; if not, * return nil. It's not an error or a ranking demerit if we don't * agree; it just means that we'll fall back on the regular pronoun * antecedent rather than trying to use an anaphoric binding. */ checkAnaphorAgreement(lst) { return true; } /* * By default, the "main text" of a possessive pronoun is the same as * the actual token text. Languages can override this as needed> */ getOrigMainText() { return getOrigText(); } ; class ItsAdjProd: PossessivePronounAdjProd pronounType = PronounIt ; class HisAdjProd: PossessivePronounAdjProd pronounType = PronounHim ; class HerAdjProd: PossessivePronounAdjProd pronounType = PronounHer ; class TheirAdjProd: PossessivePronounAdjProd pronounType = PronounThem ; class YourAdjProd: PossessivePronounAdjProd pronounType = PronounYou canBeAnaphor = nil ; class MyAdjProd: PossessivePronounAdjProd pronounType = PronounMe canBeAnaphor = nil ; /* * Possessive nouns */ class PossessivePronounNounProd: PronounProd /* this is a possessive usage of the pronoun */ isPossessive = true ; class ItsNounProd: PossessivePronounNounProd pronounType = PronounIt ; class HisNounProd: PossessivePronounNounProd pronounType = PronounHim ; class HersNounProd: PossessivePronounNounProd pronounType = PronounHer ; class TheirsNounProd: PossessivePronounNounProd pronounType = PronounThem ; class YoursNounProd: PossessivePronounNounProd pronounType = PronounYou ; class MineNounProd: PossessivePronounNounProd pronounType = PronounMe ; /* * Basic possessive phrase. The grammar rules for these phrases must map * the possessive qualifier phrase to poss_, and the noun phrase being * qualified to np_. We are based on DefiniteNounProd because we resolve * the possessive qualifier as though it had a definite article. * * The possessive production object poss_ must define the method * getOrigMainText() to return the text of its noun phrase in a format * suitable for disambiguation prompts or error messages. In English, * for example, this means that the getOrigMainText() must omit the * apostrophe-S suffix if present. */ class BasicPossessiveProd: DefiniteNounProd /* * To allow this class to be mixed with other classes that have * mixed-in ambiguous response keepers, create a separate object to * hold our ambiguous response keeper for the possessive phrase. We * will never use our own ambiguous response keeper properties, so * those are available to any other production class we're mixed * into. */ construct() { /* create an AmbigResponseKeeper for the possessive phrase */ npKeeper = new AmbigResponseKeeper; } /* * Resolve the possessive, and perform preliminary resolution of the * qualified noun phrase. We find the owner object and reduce the * resolved objects for the qualified phrase to those owned by the * owner. * * If we fail, we return nil. Otherwise, we return a list of the * tentatively resolved objects. The caller can further resolve * this list as needed. */ resolvePossessive(resolver, results, num) { /* resolve the underlying noun phrase being qualified */ local lst = np_.resolveNouns(resolver, results); /* if we found no matches for the noun phrase, so note */ if (lst.length() == 0) { results.noMatchPossessive(resolver.getAction(), np_.getOrigText()); return nil; } /* * resolve the possessive phrase and reduce the list to select * only the items owned by the possessor */ lst = selectWithPossessive(resolver, results, lst, np_.getOrigText(), num); /* return the tentative resolution list for the qualified phrase */ return lst; } /* * Resolve the possessive, and reduce the given match list by * selecting only those items owned by the resolution of the * possessive phrase. * * 'num' is the number of objects we want to select. If the noun * phrase being qualified is singular, this will be 1; if it's * plural, this will be nil, to indicate that there's no specific * target quantity; if the phrase is something like "bob's five * books," the the number will be the qualifying quantity (5, in this * case). */ selectWithPossessive(resolver, results, lst, lstOrigText, num) { /* * Create the possessive resolver. Note that we resolve the * possessive phrase in the context of the resolver's indicated * qualifier resolver, which might not be the same as the * resolver for the overall phrase. */ local possResolver = resolver.getPossessiveResolver(); /* enter a single-object slot for the possessive phrase */ results.beginSingleObjSlot(); /* resolve the underlying possessive */ local possLst = poss_.resolveNouns(possResolver, results); /* perform the normal resolve list filtering */ possLst = resolver.getAction().finishResolveList( possLst, resolver.whichObject, self, nil); /* done with the single-object slot */ results.endSingleObjSlot(); /* * If that resolved to an empty list, return now with an empty * list. The underlying possessive resolver will have noted an * error if this is indeed an error; if it's not an error, it * means that we're pending resolution of the other noun phrase * to resolve an anaphor in the possessive phrase. */ if (possLst == []) { /* * we must have a pending anaphor to resolve - simply return * the current list without any possessive filtering */ return lst; } /* * If the possessive phrase itself is singular, treat the * possessive phrase as a definite phrase, requiring an * unambiguous referent. If it's plural ("the men's books"), * leave it as it is, taking it to mean that we want to select * things that are owned by any/all of the possessors. * * Note that the possessive phrase has no qualifier - any * qualifier applies to the noun phrase our possessive is also * qualifying, not to the possessive phrase itself. */ local owner; if (poss_.isPluralPossessive) { /* * The possessive phrase is plural, so don't reduce its match * to a single object; instead, select all of the objects * owned by any of the possessors. The owner is anyone in * the list. */ owner = possLst.mapAll({x: x.obj_}); } else { /* * the possessive phrase is singular, so resolve the * possessive qualifier as a definite noun */ possLst = resolveDefinite( ResolveAsker, poss_.getOrigMainText(), possLst, npKeeper, possResolver, results); /* * if we didn't manage to find a single resolution to the * possessive phrase, we can't resolve the rest of the phrase * yet */ if (possLst.length() != 1) { /* if we got more than one object, it's a separate error */ if (possLst.length() > 1) results.uniqueObjectRequired(poss_.getOrigMainText(), possLst); /* we can't go on */ return []; } /* get the resolved owner object */ owner = [possLst[1].obj_]; } /* select the objects owned by any of the owners */ local newLst = lst.subset({x: owner.indexWhich( {y: x.obj_.isOwnedBy(y)}) != nil}); /* * If that didn't leave any results, try one more thing: if the * owner is itself in the list of possessed objects, keep the * owner. This allows for sequences like this: * * >take book *. Which book? *. >the man's *. Which man, Bob or Dave? *. >bob's * * In this case, the qualified object list will be [bob,dave], * and the owner will be [bob], so we want to keep [bob] as the * result list. */ if (newLst == []) newLst = lst.subset({x: owner.indexOf(x.obj_) != nil}); /* use the new list we found */ lst = newLst; /* * Give each item an ownership priority ranking, since the items * are being qualified by owner: explicitly owned items have the * highest ranking, items directly held but not explicitly owned * have the second ranking, and items merely held have the * lowest ranking. If we deem the list to be ambiguous later, * we'll apply the ownership priority ranking first in trying to * disambiguate. */ foreach (local cur in lst) { /* * give the item a ranking: explicitly owned, directly held, * or other */ if (owner.indexOf(cur.obj_.owner) != nil) cur.possRank_ = 2; else if (owner.indexWhich({x: cur.obj_.isDirectlyIn(x)}) != nil) cur.possRank_ = 1; } /* if we found nothing, mention it */ if (lst.length() == 0) { results.noMatchForPossessive(owner, lstOrigText); return []; } /* return the reduced list */ return lst; } /* our ambiguous response keeper */ npKeeper = nil ; /* * Possessive phrase + singular noun phrase. The language grammar rule * must map poss_ to the possessive production and np_ to the noun * phrase being qualified. */ class PossessiveNounProd: BasicPossessiveProd resolveNouns(resolver, results) { local lst; /* * perform the initial qualification; if that fails, give up now * and return an empty list */ if ((lst = resolvePossessive(resolver, results, 1)) == nil) return []; /* now resolve the underlying list definitely */ return resolveDefinite(ResolveAsker, np_.getOrigText(), lst, self, resolver, results); } /* our AmbigResponseKeeper for the qualified noun phrase */ npKeeper = nil ; /* * Possessive phrase + plural noun phrase. The grammar rule must set * poss_ to the possessive and np_ to the plural. */ class PossessivePluralProd: BasicPossessiveProd resolveNouns(resolver, results) { local lst; /* perform the initial qualifier resolution */ if ((lst = resolvePossessive(resolver, results, nil)) == nil) return []; /* filter out truncated matches if we have any exact matches */ lst = filterTruncations(lst, resolver); /* filter the plurals for the logical subset */ lst = resolver.filterPluralPhrase(lst, self); /* if we have a list, sort it by pluralOrder */ if (lst != nil) lst = lst.sort(SortAsc, {a, b: a.obj_.pluralOrder - b.obj_.pluralOrder}); /* note the matched objects in the results */ results.noteMatches(lst); /* we want everything in the list, so return what we found */ return lst; } ; /* * Possessive plural with a specific quantity that must be exact */ class ExactQuantifiedPossessivePluralProd: ExactQuantifiedPluralProd, BasicPossessiveProd /* * resolve the main noun phrase - this is the possessive-qualified * plural phrase */ resolveMainPhrase(resolver, results) { return resolvePossessive(resolver, results, getQuantity()); } ; /* * Possessive noun used in an exclusion list. This is for things like * the "mine" in a phrase like "take keys except mine". */ class ButPossessiveProd: BasicPossessiveProd resolveNouns(resolver, results) { /* * resolve the possessive phrase, and use it to reduce the * resolver's main list (this is the list before the "except" * from which are choosing items to exclude) to those items * owned by the object indicated in the possessive phrase */ return selectWithPossessive(resolver, results, resolver.mainList, resolver.mainListText, nil); } ; /* * Possessive phrase production for disambiguation. This base class can * be used for grammar productions that match possessive phrases in * disambiguation prompt ("which book do you mean...?") responses. */ class DisambigPossessiveProd: BasicPossessiveProd, DisambigProd resolveNouns(resolver, results) { local lst; /* * Remember the original qualified list (this is the list of * objects from which we're trying to choose on the basis of the * possessive phrase we're resolving now). We can feed the * qualified-object list back into the selection process for the * qualifier itself, because we're looking for a qualifier that * makes sense when combined with one of the qualified objects. */ qualifiedList_ = resolver.matchList; /* select from the match list using the possessive phrase */ lst = selectWithPossessive(resolver, results, resolver.matchList, resolver.matchText, 1); /* * if the list has more than one entry, treat the result as still * ambiguous - a simple possessive response to a disambiguation * query is implicitly definite, so we must select a single * object */ if (lst != nil && lst.length() > 1) lst = results.ambiguousNounPhrase( self, ResolveAsker, resolver.matchText, lst, resolver.fullMatchList, lst, 1, resolver); /* * if we failed to resolve it, return an empty list; otherwise * return the list */ return (lst == nil ? [] : lst); } /* * Do extra filter during disambiguation. Since we have a list of * objects we're trying to qualify, we can look at that list to see * if some of the possible matches for the qualifier phrase are * owners of things in the qualified list. */ reduceDefinite(lst, resolver, results) { local newLst; /* * try reducing the list to owners of objects that appear in the * qualified object list */ newLst = lst.subset({x: qualifiedList_.indexWhich( {y: y.obj_.isOwnedBy(x.obj_)}) != nil}); /* if there's anything in that list, keep only the subset */ if (newLst.length() > 0) lst = newLst; /* return the result */ return lst; } /* * the list of objects being qualified - this is the list of books, * for example, in "bob's books" */ qualifiedList_ = [] ; /* ------------------------------------------------------------------------ */ /* * A noun phrase with explicit containment. Grammar rules based on this * class must set the property np_ to the main noun phrase, and cont_ to * the noun phrase giving the container. * * We're based on the definite noun phrase production, because we need * to resolve the underlying container phrase to a singe, unambiguous * object. */ class ContainerNounPhraseProd: DefiniteNounProd resolveNouns(resolver, results) { local lst; local cRes; local cLst; local cont; /* * We have two separate noun phrases to resolve: the qualified * noun phrase in np_, and the locational qualifier in cont_. * We then want to filter the object matches for np_ to select * the subset that is contained in cont_. * * We must resolve cont_ to a single, unambiguous object. * However, we want to be smart about it by limiting the range * of choices to objects that actually contain something that * could match the possible resolutions of np_. So, tentatively * resolve np_ first, to get the range of possible matches. */ lst = np_.resolveNouns(resolver, results); /* * We have the tentative resolution of the main noun phrase, so * we can now resolve the locational qualifier phrase. Use our * special container resolver for this step, since this will try * to pick objects that contain something in the tentative * results for the main list. Resolve the container as a * definite noun phrase, since we want a single, unambiguous * match. * * Note that we must base our special container resolver on the * qualifier resolver, not on the main resolver. The location is * a qualifying phrase, so it's resolved in the scope of any * other qualifying phrase. * * The container phrase has to be a single object, so note in the * results that we're working on a single-object slot. */ results.beginSingleObjSlot(); cRes = new ContainerResolver(lst, np_.getOrigText(), resolver.getQualifierResolver()); cLst = resolveDefinite(ResolveAsker, cont_.getOrigText(), cont_.resolveNouns(cRes, results), self, cRes, results); results.endSingleObjSlot(); /* * We need a single object in the container list. If we have * no objects, or more than one object, it's an error. */ if (cLst.length() != 1) { /* it's a separate error if we got more than one object */ if (cLst.length() > 1) results.uniqueObjectRequired(cont_.getOrigText(), cLst); /* we can't go on */ return []; } /* we have a unique item, so it's the container */ cont = cLst[1].obj_; /* reduce the list to those objects inside the container */ lst = lst.subset({x: x.obj_.isNominallyIn(cont)}); /* * If we have some objects directly in the container, and other * objects indirectly in the container, filter the list to * include only the directly contained items. */ if (lst.indexWhich({x: x.obj_.isDirectlyIn(cont)}) != nil) lst = lst.subset({x: x.obj_.isDirectlyIn(cont)}); /* if that leaves nothing, mention it */ if (lst.length() == 0) { results.noMatchForLocation(cont, np_.getOrigText()); return []; } /* return the list */ return lst; } ; /* * Basic container resolver. This is a common subclass for the standard * container resolver and the "vague" container resolver. */ class BasicContainerResolver: ProxyResolver /* we're a sub-phrase resolver */ isSubResolver = true /* resolve any qualifiers in the main scope */ getQualifierResolver() { return origResolver; } /* filter an ambiguous noun phrase */ filterAmbiguousNounPhrase(lst, requiredNum, np) { local outer; local lcl; /* do the normal filtering first */ lst = inherited(lst, requiredNum, np); /* * get the subset that includes only local objects - that is, * objects within the same outermost room as the target actor */ outer = actor_.getOutermostRoom(); lcl = lst.subset({x: x.obj_.isIn(outer)}); /* if there's a local subset, take the subset */ if (lcl.length() != 0) lst = lcl; /* return the result */ return lst; } ; /* * Container Resolver. This is a proxy for the main qualifier resolver * that prefers to match objects that are plausible in the sense that * they contain something in the tentative resolution of the main list. */ class ContainerResolver: BasicContainerResolver construct(mainList, mainText, origResolver) { /* inherit base handling */ inherited(origResolver); /* remember my tentative main match list */ self.mainList = mainList; self.mainListText = mainText; } /* filter ambiguous equivalents */ filterAmbiguousEquivalents(lst, np) { local vec; /* * Check to see if any of the objects in the list are plausible * containers for objects in our main list. If we can find any * plausible entries, keep only the plausible ones. */ vec = new Vector(lst.length()); foreach (local cur in lst) { /* if this item is plausible, add it to our result vector */ if (mainList.indexWhich( {x: x.obj_.isNominallyIn(cur.obj_)}) != nil) vec.append(cur); } /* * if we found anything plausible, return only the plausible * subset; otherwise, return the full original list, since * they're all equally implausible */ if (vec.length() != 0) return vec.toList(); else return lst; } /* the tentative match list for the main phrase we're qualifying */ mainList = nil /* the text of the main phrase we're qualifying */ mainListText = nil ; /* ------------------------------------------------------------------------ */ /* * A "vague" container noun phrase. This is a phrase that specifies a * container but nothing else: "the one in the box", "the ones in the * box", "everything in the box". */ class VagueContainerNounPhraseProd: DefiniteNounProd resolveNouns(resolver, results) { local cRes; local cLst; local lst; local cont; /* resolve the container as a single-object slot */ results.beginSingleObjSlot(); /* * Resolve the container. Use a special resolver that prefers * objects that have any contents. */ cRes = new VagueContainerResolver(resolver.getQualifierResolver()); cLst = resolveDefinite(ResolveAsker, cont_.getOrigText(), cont_.resolveNouns(cRes, results), self, cRes, results); /* done with the single-object slot */ results.endSingleObjSlot(); /* make sure we resolved to a unique container */ if (cLst.length() != 1) { /* it's a separate error if we got more than one object */ if (cLst.length() > 1) results.uniqueObjectRequired(cont_.getOrigText(), cLst); /* we can't go on */ return []; } /* we have a unique item, so it's the container */ cont = cLst[1].obj_; /* * If the container is the nominal drop destination for the * actor's location, then use the actor's location as the actual * container. This way, if we try to get "all on floor" or the * like, we'll correctly get objects that are directly in the * room. */ if (cont == resolver.actor_.location.getNominalDropDestination()) cont = resolver.actor_.location; /* get the contents */ lst = cont.getAllForTakeFrom(resolver.getScopeList()); /* keep only visible objects */ lst = lst.subset({x: resolver.actor_.canSee(x)}); /* map the contents to a resolved object list */ lst = lst.mapAll({x: new ResolveInfo(x, 0, self)}); /* make sure the list isn't empty */ if (lst.length() == 0) { /* there's nothing in this container */ results.nothingInLocation(cont); return []; } /* make other subclass-specific checks on the list */ lst = checkContentsList(resolver, results, lst, cont); /* note the matches */ results.noteMatches(lst); /* return the list */ return lst; } /* check a contents list */ checkContentsList(resolver, results, lst, cont) { /* by default, just return the list */ return lst; } ; /* * "All in container" */ class AllInContainerNounPhraseProd: VagueContainerNounPhraseProd /* check a contents list */ checkContentsList(resolver, results, lst, cont) { /* keep only items that aren't hidden from "all" */ lst = lst.subset({x: !x.obj_.hideFromAll(resolver.getAction())}); /* set the "matched all" and "always announce as multi" flags */ foreach (local cur in lst) cur.flags_ |= AlwaysAnnounce | MatchedAll; /* if that emptied the list, so note */ if (lst.length() == 0) results.nothingInLocation(cont); /* return the result list */ return lst; } ; /* * A definite vague container phrase. This selects a single object in a * given container ("the one in the box"). If more than one object is * present, we'll try to disambiguate it. * * Grammar rules instantiating this class must set the property * 'mainPhraseText' to the text to display for a disambiguation prompt * involving the main phrase. */ class VagueContainerDefiniteNounPhraseProd: VagueContainerNounPhraseProd construct() { /* create a disambiguator for the main phrase */ npKeeper = new AmbigResponseKeeper(); } /* check a contents list */ checkContentsList(resolver, results, lst, cont) { /* * This production type requires a single object in the * container (since it's for phrases like "the one in the box"). * If we have more than one object, try disambiguating. */ if (lst.length() > 1) { local scopeList; local fullList; /* * There's more than one object in this container. First, * try filtering it by possessive qualifier strength and * then the normal disambiguation ranking. */ scopeList = lst; lst = resolver.filterPossRank(lst, 1); lst = resolver.filterAmbiguousNounPhrase(lst, 1, self); /* try removing redundant equivalents */ fullList = lst; if (results.allowEquivalentFiltering) lst = resolver.filterAmbiguousEquivalents(lst, self); /* if we still have too many objects, it's ambiguous */ if (lst.length() > 1) { /* ask the results object to handle the ambiguous phrase */ lst = results.ambiguousNounPhrase( npKeeper, ResolveAsker, mainPhraseText, lst, fullList, scopeList, 1, resolver); } } /* return the contents of the container */ return lst; } /* our disambiguation result keeper */ npKeeper = nil ; /* * An indefinite vague container phrase. This selects a single object, * choosing arbitrarily if multiple objects are in the container. */ class VagueContainerIndefiniteNounPhraseProd: VagueContainerNounPhraseProd /* check a contents list */ checkContentsList(resolver, results, lst, cont) { /* choose one object arbitrarily */ if (lst.length() > 1) lst = lst.sublist(1, 1); /* return the (possibly trimmed) list */ return lst; } ; /* * Container Resolver for vaguely-specified containment phrases. We'll * select for objects that have contents, but that's about as much as we * can do, since the main phrase is bounded only by the container in * vague containment phrases (and thus provides no information that * would help us narrow down the container itself). */ class VagueContainerResolver: BasicContainerResolver /* filter ambiguous equivalents */ filterAmbiguousEquivalents(lst, np) { local vec; /* prefer objects with contents */ vec = new Vector(lst.length()); foreach (local cur in lst) { /* if this item has any contents, add it to the new list */ if (cur.obj_.contents.length() > 0) vec.append(cur); } /* * if we found anything plausible, return only the plausible * subset; otherwise, return the full original list, since * they're all equally implausible */ if (vec.length() != 0) return vec.toList(); else return lst; } ; /* ------------------------------------------------------------------------ */ /* * Noun phrase with vocabulary resolution. This is a base class for the * various noun phrases that match adjective, noun, and plural tokens. * This class provides dictionary resolution of a vocabulary word into a * list of objects. * * In non-declined languages such as English, the parts of speech of our * words are usually simply 'adjective' and 'noun'. A language * "declines" its noun phrases if the words in a noun phrase have * different forms that depend on the function of the noun phrase in a * sentence; for example, in German, adjectives take suffixes that * depend upon the gender of the noun being modified and the function of * the noun phrase in the sentence (subject, direct object, etc). In a * declined language, it might be desirable to use separate parts of * speech for separate declined adjective and noun forms. */ class NounPhraseWithVocab: NounPhraseProd /* * Get a list of the matches in the main dictionary for the given * token as the given part of speech (&noun, &adjective, &plural, or * others as appropriate for the local language) that are in scope * according to the resolver. */ getWordMatches(tok, partOfSpeech, resolver, flags, truncFlags) { local lst; /* start with the dictionary matches */ lst = cmdDict.findWord(tok, partOfSpeech); /* filter it to eliminate redundant matches */ lst = filterDictMatches(lst); /* add the objects that match the full dictionary word */ lst = inScopeMatches(lst, flags, flags | truncFlags, resolver); /* return the combined results */ return lst; } /* * Combine two word match lists. This simply adds each entry from * the second list that doesn't already have a corresponding entry * in the first list, returning the combined list. */ combineWordMatches(aLst, bLst) { /* create a vector copy of the original 'a' list */ aLst = new Vector(aLst.length(), aLst); /* * add each entry from the second list whose object isn't * already represented in the first list */ foreach (local b in bLst) { local ia; /* look for an existing entry for this object in the 'a' list */ ia = aLst.indexWhich({aCur: aCur.obj_ == b.obj_}); /* * If this 'b' entry isn't already in the 'a' list, simply * add the 'b' entry. If both lists have this entry, combine * the flags into the existing entry. */ if (ia == nil) { /* it's not already in the 'a' list, so simply add it */ aLst += b; } else { /* * Both lists have the entry, so keep the existing 'a' * entry, but merge the flags. Note that the * original 'a' might still be interesting to the * caller separately, so create a copy of it before * modifying it. */ local a = aLst[ia]; aLst[ia] = a = a.createClone(); combineWordMatchItems(a, b); } } /* return the combined list */ return aLst; } /* * Combine the given word match entries. We'll merge the flags of * the two entries to produce a single merged entry in 'a'. */ combineWordMatchItems(a, b) { /* * If one item was matched with truncation and the other wasn't, * remove the truncation flag entirely. This will make the * combined entry reflect the fact that we were able to match the * word without any truncation. The fact that we also matched it * with truncation isn't relevant, since matching without * truncation is the stronger condition. */ if ((b.flags_ & VocabTruncated) == 0) a.flags_ &= ~VocabTruncated; /* likewise for plural truncation */ if ((b.flags_ & PluralTruncated) == 0) a.flags_ &= ~PluralTruncated; /* * Other flags generally are set for the entire list of matching * objects for a production, rather than at the level of * individual matching objects, so we don't have to worry about * combining them - they'll naturally be the same at this point. */ } /* * Filter a dictionary match list. This is called to clean up the * raw match list returned from looking up a vocabulary word in the * dictionary. * * The main purpose of this routine is to eliminate unwanted * redundancy from the dictionary matches; in particular, the * dictionary might have multiple matches for a given word at a given * object, due to truncation, upper/lower folding, accent removal, * and so on. In general, we want to keep only the single strongest * match from the dictionary for a given word matching a given * object. * * The meaning of "stronger" and "exact" matches is * language-dependent, so we abstract these with the separate methods * dictMatchIsExact() and dictMatchIsStronger(). * * Keep in mind that the raw dictionary match list has alternating * entries: object, comparator flags, object, comparator flags, etc. * The return list should be in the same format. */ filterDictMatches(lst) { local ret; /* set up a vector to hold the result */ ret = new Vector(lst.length()); /* * check each inexact element of the list for another entry with * a stronger match; keep only the ones without a stronger match */ truncScan: /* scan for a stronger match */ for (local i = 1, local len = lst.length() ; i < len ; i += 2) { /* get the current object and its flags */ local curObj = lst[i]; local curFlags = lst[i+1]; /* if it's not an exact match, check for a stronger match */ if (!dictMatchIsExact(curFlags)) { /* scan for an equal or stronger match later in the list */ for (local j = i + 2 ; j < len ; j += 2) { /* * if entry j is stronger than or identical to the * current entry, omit the current entry in favor of * entry j */ local jObj = lst[j], jFlags = lst[j+1]; if (jObj == curObj && (jFlags == curFlags || dictMatchIsStronger(jFlags, curFlags))) { /* there's a better entry; omit the current one */ continue truncScan; } } } /* keep this entry - add it to the result vector */ ret.append(curObj); ret.append(curFlags); } /* return the result vector */ return ret; } /* * Check a dictionary match's string comparator flags to see if the * match is "exact." The exact meaning of "exact" is dependent on * the language's lexical rules; this generic base version considers * a match to be exact if it doesn't have any string comparator flags * other than the base "matched" flag and the case-fold flag. This * should be suitable for most languages, as (1) case folding usually * doesn't improve match strength, and (2) any additional comparator * flags usually indicate some kind of inexact match status. * * A language that depends on upper/lower case as a marker of match * strength will need to override this to consider the case-fold flag * as significant in determining match exactness. In addition, a * language that uses additional string comparator flags to indicate * better (rather than worse) matches will have to override this to * require the presence of those flags. */ dictMatchIsExact(flags) { /* * the match is exact if it doesn't have any qualifier flags * other than the basic "yes I matched" flag and the case-fold * flag */ return ((flags & ~(StrCompMatch | StrCompCaseFold)) == 0); } /* * Compare two dictionary matches for the same object and determine * if the first one is stronger than the second. Both are for the * same object; the only difference is the string comparator flags. * * Language modules might need to override this to supplement the * filtering with their own rules. This generic base version * considers truncation: an untruncated match is stronger than a * truncated match. Non-English languages might want to consider * other lexical factors in the match strength, such as whether we * matched the exact accented characters or approximated with * unaccented equivalents - this information will, of course, need to * be coordinated with the dictionary's string comparator, and * reflected in the comparator match flags. It's the comparator * match flags that we're looking at here. */ dictMatchIsStronger(flags1, flags2) { /* * if the first match (flags1) indicates no truncation, and the * second (flags2) was truncated, then the first match is * stronger; otherwise, there's no distinction as far as we're * concerned */ return ((flags1 & StrCompTrunc) == 0 && (flags2 & StrCompTrunc) != 0); } /* * Get a list of the matches in the main dictionary for the given * token, intersecting the resulting list with the given list. */ intersectWordMatches(tok, partOfSpeech, resolver, flags, truncFlags, lst) { local newList; /* get the matches to the given word */ newList = getWordMatches(tok, partOfSpeech, resolver, flags, truncFlags); /* intersect the result with the other list */ newList = intersectNounLists(newList, lst); /* return the combined results */ return newList; } /* * Given a list of dictionary matches to a given word, construct a * list of ResolveInfo objects for the matches that are in scope. * For regular resolution, "in scope" means the resolver thinks the * object is in scope. */ inScopeMatches(dictionaryMatches, flags, truncFlags, resolver) { local ret; /* set up a vector to hold the results */ ret = new Vector(dictionaryMatches.length()); /* * Run through the list and include only the words that are in * scope. Note that the list of dictionary matches has * alternating objects and match flags, so we must scan it two * elements at a time. */ for (local i = 1, local cnt = dictionaryMatches.length() ; i <= cnt ; i += 2) { /* get this object */ local cur = dictionaryMatches[i]; /* if it's in scope, add a ResolveInfo for this object */ if (resolver.objInScope(cur)) { local curResults; local curFlags; /* get the comparator match results for this object */ curResults = dictionaryMatches[i+1]; /* * If the results indication truncation, use the the * truncated flags; otherwise use the ordinary flags. */ if (dataType(curResults) == TypeInt && (curResults & StrCompTrunc) != 0) curFlags = truncFlags; else curFlags = flags; /* add the item to the results */ ret.append(new ResolveInfo(cur, curFlags, self)); } } /* return the results as a list */ return ret.toList(); } /* * Resolve the objects. */ resolveNouns(resolver, results) { local matchList; local starList; /* * get the preliminary match list - this is simply the set of * objects that match all of the words in the noun phrase */ matchList = getVocabMatchList(resolver, results, 0); /* * get a list of any in-scope objects that match '*' for nouns - * these are objects that want to do all of their name parsing * themselves */ starList = inScopeMatches(cmdDict.findWord('*', &noun), 0, 0, resolver); /* combine the lists */ matchList = combineWordMatches(matchList, starList); /* run the results through matchName */ return resolveNounsMatchName(results, resolver, matchList); } /* * Run a set of resolved objects through matchName() or a similar * routine. Returns the filtered results. */ resolveNounsMatchName(results, resolver, matchList) { local origTokens; local adjustedTokens; local objVec; local ret; /* get the original token list for the command */ origTokens = getOrigTokenList(); /* get the adjusted token list for the command */ adjustedTokens = getAdjustedTokens(); /* set up to receive about the same number of results as inputs */ objVec = new Vector(matchList.length()); /* consider each preliminary match */ foreach (local cur in matchList) { /* ask this object if it wants to be included */ local newObj = resolver.matchName( cur.obj_, origTokens, adjustedTokens); /* check the result */ if (newObj == nil) { /* * it's nil - this means it's not a match for the name * after all, so leave it out of the results */ } else if (newObj.ofKind(Collection)) { /* * it's a collection of some kind - add each element to * the result list, using the same flags as the original */ foreach (local curObj in newObj) objVec.append(new ResolveInfo(curObj, cur.flags_, self)); } else { /* * it's a single object - add it ito the result list, * using the same flags as the original */ objVec.append(new ResolveInfo(newObj, cur.flags_, self)); } } /* convert the result vector to a list */ ret = objVec.toList(); /* if our list is empty, note it in the results */ if (ret.length() == 0) { /* * If the adjusted token list contains any tokens of type * "miscWord", send the phrase to the results object for * further consideration. */ if (adjustedTokens.indexOf(&miscWord) != nil) { /* * we have miscWord tokens, so this is a miscWordList * match - let the results object process it specially. */ ret = results.unknownNounPhrase(self, resolver); } /* * if the list is empty, note that we have a noun phrase * whose vocabulary words don't match anything in the game */ if (ret.length() == 0) results.noVocabMatch(resolver.getAction(), getOrigText()); } /* return the result list */ return ret; } /* * Each subclass must override getAdjustedTokens() to provide the * appropriate set of tokens used to match the object. This is * usually simply the original set of tokens, but in some cases it * may differ; for example, spelled-out numbers normally adjust to * the numeral form of the number. * * For each adjusted token, the list must have two entries: the * first is a string giving the token text, and the second is the * property giving the part of speech for the token. */ getAdjustedTokens() { return nil; } /* * Get the vocabulary match list. This is simply the set of objects * that match all of the words in the noun phrase. Each rule * subclass must override this to return an appropriate list. Note * that subclasses should use getWordMatches() and * intersectWordMatches() to build the list. */ getVocabMatchList(resolver, results, extraFlags) { return nil; } ; /* ------------------------------------------------------------------------ */ /* * An empty noun phrase production is one that matches, typically with * non-zero badness value, as a placeholder when a command is missing a * noun phrase where one is required. * * Each grammar rule instance of this rule class must define the * property 'responseProd' to be the production that should be used to * parse any response to an interactive prompt for the missing object. */ class EmptyNounPhraseProd: NounPhraseProd /* customize the way we generate the prompt and parse the response */ setPrompt(response, asker) { /* remember the new response production and ResolveAsker */ responseProd = response; asker_ = asker; } /* resolve the empty phrase */ resolveNouns(resolver, results) { /* * if we've filled in our missing phrase already, return the * resolution of that list */ if (newMatch != nil) return newMatch.resolveNouns(resolver, results); /* * The noun phrase was left out entirely, so try to get an * implied object. */ local match = getImpliedObject(resolver, results); /* if that succeeded, return the result */ if (match != nil) return match; /* * Parse the next input with our own (subclassed) responseProd if * we have one. If we don't (i.e., it's nil), get one from the * action. */ local rp = responseProd; if (rp == nil && resolver.getAction() != nil) rp = resolver.getAction().getObjResponseProd(resolver); /* as a last resort, use the fallback response */ if (rp == nil) rp = fallbackResponseProd; /* * There is no implied object, so try to get a result * interactively. Use the production that our rule instance * specifies via the responseProd property to parse the * interactive response. */ match = results.askMissingObject(asker_, resolver, rp); /* if we didn't get a match, we have nothing to return */ if (match == nil) return []; /* we got a match - remember it as a proxy for our noun phrase */ newMatch = match; /* return the resolved noun phrase from the proxy match */ return newMatch.resolvedObjects; } /* * Get an implied object to automatically fill in for the missing * noun phrase. By default, we simply ask the 'results' object for * the missing object. */ getImpliedObject(resolver, results) { /* ask the 'results' object for the information */ return results.getImpliedObject(self, resolver); } /* * Get my tokens. If I have a new match tree, return the tokens * from the new match tree. Otherwise, we don't have any tokens, * since we're empty. */ getOrigTokenList() { return (newMatch != nil ? newMatch.getOrigTokenList() : []); } /* * Get my original text. If I have a new match tree, return the * text from the new match tree. Otherwise, we have no original * text, since we're an empty phrase. */ getOrigText() { return (newMatch != nil ? newMatch.getOrigText() : ''); } /* * I'm an empty noun phrase, unless I already have a new match * object. */ isEmptyPhrase { return newMatch == nil; } /* * the new match, when we get an interactive response to a query for * the missing object */ newMatch = nil /* * Our "response" production - this is the production we use to parse * the player's input in response to our disambiguation prompt. A * subclass can leave this as nil, in which case we'll attempt to get * the appropriate response production from the action. */ responseProd = nil /* * Our fallback response production - if responseProd is nil, this * must be supplied for cases where we can't get the production from * the action. This is ignored if responseProd is non-nil. */ fallbackResponseProd = nil /* * The ResolveAsker we use to generate our prompt. Use the base * ResolveAsker by default; this can be overridden when the prompt * is to be customized. */ asker_ = ResolveAsker ; /* * An empty noun phrase production for verb phrasings that imply an * actor, but don't actually include one by name. * * This is similar to EmptyNounPhraseProd, but has an important * difference: if the actor carrying out the command has a current or * implied conversation partner, then we choose the conversation partner * as the implied object. This is important in that we don't count the * noun phrase as technically missing in this case, for the purposes of * command ranking. This is useful for phrasings that inherently imply * an actor strongly enough that there should be no match-strength * penalty for leaving it out. */ class ImpliedActorNounPhraseProd: EmptyNounPhraseProd /* get my implied object */ getImpliedObject(resolver, results) { /* * If the actor has a default interlocutor, use that, bypassing * the normal implied object search. */ local actor = resolver.actor_.getDefaultInterlocutor(); if (actor != nil) return [new ResolveInfo(actor, DefaultObject, nil)]; /* ask the 'results' object for the information */ return results.getImpliedObject(self, resolver); } ; /* * Empty literal phrase - this serves a purpose similar to that of * EmptyNounPhraseProd, but can be used where literal phrases are * required. */ class EmptyLiteralPhraseProd: LiteralProd getLiteralText(results, action, which) { local toks; local prods; /* if we already have an interactive response, return it */ if (newText != nil) return newText; /* * ask for the missing phrase and remember it for the next time * we're asked for our text */ newText = results.askMissingLiteral(action, which); /* * Parse the text (if any) as a literal phrase. In most cases, * anything can be parsed as a literal phrase, so this might * seem kind of pointless; however, this is useful when the * language-specific library defines rules for things like * removing quotes from a quoted string. */ if (newText != nil) { try { /* tokenize the input */ toks = cmdTokenizer.tokenize(newText); } catch (TokErrorNoMatch exc) { /* note the token error */ gLibMessages.invalidCommandToken(exc.curChar_.htmlify()); /* treat the command as empty */ throw new ReplacementCommandStringException(nil, nil, nil); } /* parse the input as a literal phrase */ prods = literalPhrase.parseTokens(toks, cmdDict); /* if we got a match, use it */ if (prods.length() > 0) { /* * we got a match - this will be a LiteralProd, so ask * the matching LiteralProd for its literal text, and * use that as our new literal text */ newText = prods[1].getLiteralText(results, action, which); } } /* return the text */ return newText; } /* * Tentatively get my literal text. This is used for pre-resolution * when we have another phrase we want to resolve first, but we want * to provide a tentative form of the text in the meantime. We won't * attempt to ask for more information interactively, but we'll * return any information we do have. */ getTentativeLiteralText() { /* * if we have a result from a previous interaactive request, * return it; otherwise we have no tentative text */ return newText; } /* I'm an empty phrase, unless I already have a text response */ isEmptyPhrase { return newText == nil; } /* the response to a previous interactive query */ newText = nil ; /* * Empty topic phrase production. This is the topic equivalent of * EmptyNounPhraseProd. */ class EmptyTopicPhraseProd: TopicProd resolveNouns(resolver, results) { local match; /* * if we've filled in our missing phrase already, return the * resolution of that list */ if (newMatch != nil) return newMatch.resolveNouns(resolver, results); /* ask for a topic interactively, using our responseProd */ match = results.askMissingObject(asker_, resolver, responseProd); /* if we didn't get a match, we have nothing to return */ if (match == nil) return []; /* we got a match - remember it as a proxy for our noun phrase */ newMatch = match; /* return the resolved noun phrase from the proxy match */ return newMatch.resolvedObjects; } /* we're an empty phrase if we don't have a new topic yet */ isEmptyPhrase { return newMatch = nil; } /* get my tokens - use the underlying new match tree if we have one */ getOrigTokenList() { return (newMatch != nil ? newMatch.getOrigTokenList() : inherited()); } /* get my original text - use the new match tree if we have one */ getOrigText() { return (newMatch != nil ? newMatch.getOrigText() : inherited()); } /* our new underlying topic phrase */ newMatch = nil /* * by default, parse our interactive response as an ordinary topic * phrase */ responseProd = topicPhrase /* our ResolveAsker object - this is for customizing the prompt */ asker_ = ResolveAsker ; /* ------------------------------------------------------------------------ */ /* * Look for an undefined word in a list of tokens, and give the player a * chance to correct a typo with "OOPS" if appropriate. * * If we find an unknown word and we can prompt for interactive * resolution, we'll do so, and we'll throw an appropriate exception to * handle the response. If we can't resolve the missing word * interactively, we'll throw a parse failure exception. * * If there are no undefined words in the command, we'll simply return. * * tokList is the list of tokens under examination; this is a subset of * the full command token list. cmdTokenList is the full command token * list, in the usual tokenizer format. firstTokenIndex is the index of * the first token in tokList within cmdTokenList. * * cmdType is an rmcXxx code giving the type of input we're reading. */ tryOops(tokList, issuingActor, targetActor, firstTokenIndex, cmdTokenList, cmdType) { /* run the main "oops" processor in the player character sense context */ callWithSenseContext(nil, sight, {: tryOopsMain(tokList, issuingActor, targetActor, firstTokenIndex, cmdTokenList, cmdType) }); } /* main "oops" processor */ tryOopsMain(tokList, issuingActor, targetActor, firstTokenIndex, cmdTokenList, cmdType) { local str; local unknownIdx; local oopsMatch; local toks; local w; /* * Look for a word not in the dictionary. */ for (unknownIdx = nil, local i = 1, local len = tokList.length() ; i <= len ; ++i) { local cur; local typ; /* get the token value for this word */ cur = getTokVal(tokList[i]); typ = getTokType(tokList[i]); /* check to see if this word is defined in the dictionary */ if (typ == tokWord && !cmdDict.isWordDefined(cur)) { /* note that we found an unknown word */ unknownIdx = i; /* no need to look any further */ break; } } /* * if we didn't find an unknown word, there's no need to offer the * user a chance to correct a typo - simply return without any * further processing */ if (unknownIdx == nil) return; /* * We do have an unknown word, but check one more thing: if we were * asking some kind of follow-up question, such as a missing-object * or disambiguation query, check to see if the new entry would parse * successfully as a new command. It's possible that the new entry * is a brand new command rather than a response to our question, and * that the unknown word is valid in the context of a new command - * it could be part of a literal-phrase, for example. */ if (cmdType != rmcCommand) { /* parse the command */ local lst = firstCommandPhrase.parseTokens(cmdTokenList, cmdDict); /* resolve actions */ lst = lst.subset( {x: x.resolveFirstAction(issuingActor, targetActor) != nil}); /* if we managed to match something, treat it as a new command */ if (lst.length() != 0) throw new ReplacementCommandStringException( cmdTokenizer.buildOrigText(cmdTokenList), nil, nil); } /* get the unknown word, in presentable form */ w = getTokOrig(tokList[unknownIdx]).htmlify(); /* * Give them a chance to correct a typo via OOPS if the player * issued the command. If the command came from an actor other than * the player character, simply fail the command. */ if (!issuingActor.isPlayerChar()) { /* we can't do this interactively - treat it as a failure */ throw new ParseFailureException(&wordIsUnknown, w); } /* * tell the player about the unknown word, implicitly asking for * an OOPS to fix it */ targetActor.getParserMessageObj().askUnknownWord(targetActor, w); /* * Prompt for a new command. We'll use the main command prompt, * because we want to pretend that we're asking for a brand new * command, which we'll accept. However, if the player enters * an OOPS command, we'll process it specially. */ getResponse: str = readMainCommandTokens(rmcOops); /* re-enable the transcript, if we have one */ if (gTranscript) gTranscript.activate(); /* * if the command reader fully processed the input via preparsing, * we have nothing more to do here - simply throw a replace-command * exception with the nil string */ if (str == nil) throw new ReplacementCommandStringException(nil, nil, nil); /* extract the tokens and string from the result */ toks = str[2]; str = str[1]; /* try parsing it as an "oops" command */ oopsMatch = oopsCommand.parseTokens(toks, cmdDict); /* * if we found a match, process the OOPS command; otherwise, * treat it as a brand new command */ if (oopsMatch.length() != 0) { local badIdx; /* * if they typed in just OOPS without any tokens, show an error * and ask again */ if (oopsMatch[1].getNewTokens() == nil) { /* tell them they need to supply the missing word */ gLibMessages.oopsMissingWord; /* go back and try reading another response */ goto getResponse; } /* * Build a new token list by removing the errant token, and * splicing the new tokens into the original token list to * replace the errant one. * * Note that we'll arbitrarily take the first "oops" match, * even if there are several. There should be no ambiguity * in the "oops" grammar rule, but even if there is, it * doesn't matter, since ultimately all we care about is the * list of tokens after the "oops". */ badIdx = firstTokenIndex + unknownIdx - 1; cmdTokenList = spliceList(cmdTokenList, badIdx, oopsMatch[1].getNewTokens()); /* * Turn the token list back into a string. Since we've edited * the original text, we want to start over with the new input, * including running pre-parsing on the text. */ str = cmdTokenizer.buildOrigText(cmdTokenList); /* * run it through pre-parsing as the same kind of input the * caller was reading */ str = StringPreParser.runAll(str, cmdType); /* * if it came back nil, it means the preparser fully handled it; * in this case, simply throw a nil replacement command string */ if (str == nil) throw new ReplacementCommandStringException(nil, nil, nil); /* re-tokenize the result of pre-parsing */ cmdTokenList = cmdTokenizer.tokenize(str); /* retry parsing the edited token list */ throw new RetryCommandTokensException(cmdTokenList); } else { /* * they didn't enter something that looks like an "OOPS", so * it must be a brand new command - parse it as a new * command by throwing a replacement command exception with * the new string */ throw new ReplacementCommandStringException(str, nil, nil); } } /* * splice a new sublist into a list, replacing the item at 'idx' */ spliceList(lst, idx, newItems) { return (lst.sublist(1, idx - 1) + newItems + lst.sublist(idx + 1)); } /* ------------------------------------------------------------------------ */ /* * Try reading a response to a missing object question. If we * successfully read a noun phrase that matches the given production * rule, we'll resolve it, stash the resolved list in the * resolvedObjects_ property of the match tree, and return the match * tree. If they enter something that doesn't look like a response to * the question at all, we'll throw a new-command exception to process * it. */ tryAskingForObject(issuingActor, targetActor, resolver, results, responseProd) { /* * Prompt for a new command. We'll use the main command prompt, * because we want to pretend that we're asking for a brand new * command, which we'll accept. However, if the player enters * something that looks like a response to the missing-object query, * we'll handle it as an answer rather than as a new command. */ local str = readMainCommandTokens(rmcAskObject); /* re-enable the transcript, if we have one */ if (gTranscript) gTranscript.activate(); /* * if it came back nil, it means that the preparser fully processed * the input, which means we have nothing more to do here - simply * treat this is a nil replacement command */ if (str == nil) throw new ReplacementCommandStringException(nil, nil, nil); /* extract the input line and tokens */ local toks = str[2]; str = str[1]; /* keep going as long as we get replacement token lists */ for (;;) { /* try parsing it as an object list */ local matchList = responseProd.parseTokens(toks, cmdDict); /* * if we didn't find any match at all, it's probably a brand new * command - go process it as a replacement for the current * command */ if (matchList == []) { /* * they didn't enter something that looks like a valid * response, so assume it's a brand new command - parse it * as a new command by throwing a replacement command * exception with the new string */ throw new ReplacementCommandStringException(str, nil, nil); } /* if we're in debug mode, show the interpretations */ dbgShowGrammarList(matchList); /* create an interactive sub-resolver for resolving the response */ local ires = new InteractiveResolver(resolver); /* * rank them using our response ranker - use the original * resolver to resolve the object list */ local rankings = MissingObjectRanking.sortByRanking(matchList, ires); /* * If the best item has unknown words, try letting the user * correct typos with OOPS. */ if (rankings[1].nonMatchCount != 0 && rankings[1].unknownWordCount != 0) { try { /* * complain about the unknown word and look for an OOPS * reply */ tryOops(toks, issuingActor, targetActor, 1, toks, rmcAskObject); } catch (RetryCommandTokensException exc) { /* get the new token list */ toks = exc.newTokens_; /* replace the string as well */ str = cmdTokenizer.buildOrigText(toks); /* go back for another try at parsing the response */ continue; } } /* * If the best item we could find has no matches, check to see * if it has miscellaneous noun phrases - if so, it's probably * just a new command, since it doesn't have anything we * recognize as a noun phrase. */ if (rankings[1].nonMatchCount != 0 && rankings[1].miscWordListCount != 0) { /* * it's probably not an answer at all - treat it as a new * command */ throw new ReplacementCommandStringException(str, nil, nil); } /* the highest ranked object is the winner */ local match = rankings[1].match; /* * Check to see if this looks like an ordinary new command as * well as a noun phrase. For example, "e" looks like the noun * phrase "east wall," in that "e" is a synonym for the adjective * "east". But "e" also looks like an ordinary new command. * * We'd have to be able to read the user's mind to know which * they mean in such cases, so we have to make some assumptions * to deal with the ambiguity. In particular, if the phrasing * has special syntax that makes it look like a particularly * close match to the query phrase, assume it's a query response; * otherwise, assume it's a new command. For example: * *. >dig *. What do you want to dig in? [sets up for "in " reply] * * If the user answers "IN THE DIRT", we have a match to the * special syntax of the reply (the "in " phrasing), so we * will assume this is a reply to the query, even though it also * matches a valid new command phrasing for ENTER DIRT. If the * user answers "E" or "EAST", we *don't* have a match to the * special syntax, but merely an ordinary noun phrase match, so * we'll assume this is an ordinary GO EAST command. */ local cmdMatchList = firstCommandPhrase.parseTokens(toks, cmdDict); if (cmdMatchList != []) { /* * The phrasing looks like it's a valid new command as well * as a noun phrase reply. Check the query reply match for * special syntax that would distinguish it from a new * command; if it doesn't match any special syntax, assume * that it is indeed a new command instead of a query reply. */ if (!match.isSpecialResponseMatch) throw new ReplacementCommandStringException(str, nil, nil); } /* show our winning interpretation */ dbgShowGrammarWithCaption('Missing Object Winner', match); /* * actually resolve the response to objects, using the original * results and resolver objects */ local objList = match.resolveNouns(ires, results); /* stash the resolved object list in a property of the match tree */ match.resolvedObjects = objList; /* return the match tree */ return match; } } /* * a property we use to hold the resolved objects of a match tree in * tryAskingForObject */ property resolvedObjects; /* * Missing-object response ranker. */ class MissingObjectRanking: CommandRanking /* * missing-object responses have no verb, so they won't count any * noun slots; we just need to give these an arbitrary value so that * we can compare them (and find them equal) */ nounSlotCount = 0 ; /* ------------------------------------------------------------------------ */ /* * An implementation of ResolveResults for normal noun resolution. */ class BasicResolveResults: ResolveResults /* * The target and issuing actors for the command being resolved. */ targetActor_ = nil issuingActor_ = nil /* set up the actor parameters */ setActors(target, issuer) { targetActor_ = target; issuingActor_ = issuer; } /* * Results gatherer methods */ noVocabMatch(action, txt) { /* indicate that we couldn't match the phrase */ throw new ParseFailureException(&noMatch, action, txt.toLower().htmlify()); } noMatch(action, txt) { /* show an error */ throw new ParseFailureException(&noMatch, action, txt.toLower().htmlify()); } noMatchPossessive(action, txt) { /* use the basic noMatch handling */ noMatch(action, txt); } allNotAllowed() { /* show an error */ throw new ParseFailureException(&allNotAllowed); } noMatchForAll() { /* show an error */ throw new ParseFailureException(&noMatchForAll); } noteEmptyBut() { /* this isn't an error - ignore it */ } noMatchForAllBut() { /* show an error */ throw new ParseFailureException(&noMatchForAllBut); } noMatchForListBut() { /* show an error */ throw new ParseFailureException(&noMatchForListBut); } noMatchForPronoun(typ, txt) { /* show an error */ throw new ParseFailureException(&noMatchForPronoun, typ, txt.toLower().htmlify()); } reflexiveNotAllowed(typ, txt) { /* show an error */ throw new ParseFailureException(&reflexiveNotAllowed, typ, txt.toLower().htmlify()); } wrongReflexive(typ, txt) { /* show an error */ throw new ParseFailureException(&wrongReflexive, typ, txt.toLower().htmlify()); } noMatchForPossessive(owner, txt) { /* if the owner is a list, it's a plural owner */ if (owner.length() > 1) { /* it's a plural owner */ throw new ParseFailureException(&noMatchForPluralPossessive, txt); } else { /* let the (singular) owner object generate the error */ owner[1].throwNoMatchForPossessive(txt.toLower().htmlify()); } } noMatchForLocation(loc, txt) { /* let the location object generate the error */ loc.throwNoMatchForLocation(txt.toLower().htmlify()); } nothingInLocation(loc) { /* let the location object generate the error */ loc.throwNothingInLocation(); } noteBadPrep() { /* * bad prepositional phrase structure - assume that the * preposition was intended as part of the verb phrase, so * indicate that the entire command is not understood */ throw new ParseFailureException(&commandNotUnderstood); } /* * Service routine - determine if we can interactively resolve a * need for more information. If the issuer is the player, and the * target actor can talk to the player, then we can resolve a * question interactively; otherwise, we cannot. * * We can't interactively resolve a question if the issuer isn't the * player, because there's no interactive user to prompt for more * information. * * We can't interactively resolve a question if the target actor * can't talk to the player, because the question to the player * would be coming out of thin air. */ canResolveInteractively(action) { /* * If this is an implied action, and the target actor is in * "NPC" mode, we can't resolve interactively. Note that if * there's no action, it can't be implicit (we won't have an * action if we're resolving the actor to whom the action is * targeted). */ if (action != nil && action.isImplicit && targetActor_.impliedCommandMode() == ModeNPC) return nil; /* * if the issuer is the player, and either the target is the * player or the target can talk to the player, we can resolve * interactively */ return (issuingActor_.isPlayerChar() && (targetActor_ == issuingActor_ || targetActor_.canTalkTo(issuingActor_))); } /* * Handle an ambiguous noun phrase. We'll first check to see if we * can find a Distinguisher that can tell at least some of the * matches apart; if we fail to do that, we'll just pick the required * number of objects arbitrarily, since we have no way to distinguish * any of them. Once we've chosen a Distinguisher, we'll ask the * user for help interactively if possible. */ ambiguousNounPhrase(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver) { /* put the match list in disambigPromptOrder order */ matchList = matchList.sort( SortAsc, {a, b: (a.obj_.disambigPromptOrder - b.obj_.disambigPromptOrder) }); /* * Get the token list for the original phrase. Since the whole * point is to disambiguate a phrase that matched multiple * objects, all of the matched objects came from the same phrase, * so we can just grab the tokens from the first item. */ local np = matchList[1].np_; local npToks = np ? np.getOrigTokenList() : []; /* for the prompt, use the lower-cased version of the input text */ local promptTxt = txt.toLower().htmlify(); /* ask the response keeper for its list of past responses, if any */ local pastResponses = keeper.getAmbigResponses(); /* * set up a results object - use the special disambiguation * results object instead of the basic resolver type */ local disambigResults = new DisambigResults(self); /* * start out with an empty still-to-resolve list - we have only * the one list to resolve so far */ local stillToResolve = []; /* we have nothing in the result list yet */ local resultList = []; /* determine if we can ask for clarification interactively */ if (!canResolveInteractively(resolver.getAction())) { /* * don't attempt to resolve this interactively - just treat * it as a resolution failure */ throw new ParseFailureException( &ambiguousNounPhrase, txt.htmlify(), matchList, fullMatchList); } /* we're asking for the first time */ local everAsked = nil; local askingAgain = nil; /* * Keep going until we run out of things left to resolve. Each * time an answer looks valid but doesn't completely * disambiguate the results, we'll queue it for another question * iteration, so we must continue until we run out of queued * questions. */ queryLoop: for (local pastIdx = 1 ;; ) { local str; local toks; local dist; local curMatchList = []; /* * Find the first distinguisher that can tell one or more of * these objects apart. Work through the distinguishers in * priority order, which is the order they appear in an * object's 'distinguishers' property. * * If these objects aren't all equivalents, then we'll * immediately find that the basic distinguisher can tell * them all apart. Every object's first distinguisher is * the basic distinguisher. If these objects are all * equivalents, then they'll all have the same set of * distinguishers. In either case, we don't have to worry * about what set of objects we have, because we're * guaranteed to have a suitable set of distinguishers in * common - so just arbitrarily pick an object and work * through its distinguishers. */ foreach (dist in matchList[1].obj_.distinguishers) { /* filter the match list using this distinguisher only */ curMatchList = filterWithDistinguisher(matchList, dist); /* * if this list has more than the required number of * results, then this distinguisher is capable of * telling apart enough of these objects, so use this * distinguisher for now */ if (curMatchList.length() > requiredNum) break; } /* * If we didn't find any distinguishers that could tell * apart enough of the objects, then we've exhausted our * ability to choose among these objects, so return an * arbitrary set of the desired size. */ if (curMatchList.length() <= requiredNum) return fullMatchList.sublist(1, requiredNum); /* * If we have past responses, try reusing the past responses * before asking for new responses. */ if (pastIdx <= pastResponses.length()) { /* we have another past response - use the next one */ str = pastResponses[pastIdx++]; /* tokenize the new command */ toks = cmdTokenizer.tokenize(str); } else { /* * Try filtering the options with the basic * distinguisher, to see if all of the remaining options * are basic equivalents - if they are, then we can * refer to them by the object name, since every one has * the same object name. */ local basicDistList = filterWithDistinguisher( matchList, basicDistinguisher); /* * if we filtered to one object, everything remaining is * a basic equivalent of that one object, so they all * have the same name, so we can use that name */ if (basicDistList.length() == 1) promptTxt = basicDistList[1].obj_.disambigEquivName; /* * let the distinguisher know we're prompting for more * information based on this distinguisher */ dist.notePrompt(curMatchList); /* * We don't have any special insight into which object * the user might have intended, and we have no past * responses to re-use, so simply ask for clarification. * Ask using the full match list, so that we correctly * indicate where there are multiple equivalents. */ asker.askDisambig(targetActor_, promptTxt, curMatchList, fullMatchList, requiredNum, everAsked && askingAgain, dist); /* ask for a new command */ str = readMainCommandTokens(rmcDisambig); /* re-enable the transcript, if we have one */ if (gTranscript) gTranscript.activate(); /* note that we've asked for input interactively */ everAsked = true; /* * if it came back nil, the input was fully processed by * the preparser, so throw a nil replacement string * exception */ if (str == nil) throw new ReplacementCommandStringException( nil, nil, nil); /* extract the tokens and the string result */ toks = str[2]; str = str[1]; } /* presume we won't have to ask again */ askingAgain = nil; /* * Add the new tokens to the noun phrase token list, at the * end, enclosed in parentheses. The effect is that each * time we answer a disambiguation question, the answer * appears as a parenthetical at the end of the phrase: "take * the box (cardboard) (the big one)". */ npToks = npToks.append(['(', tokPunct, '(']); npToks += toks; npToks = npToks.append([')', tokPunct, ')']); retryParse: /* * Check for narrowing syntax. If the command doesn't * appear to match a narrowing syntax, then treat it as an * entirely new command. */ local prodList = mainDisambigPhrase.parseTokens(toks, cmdDict); /* * if we didn't get any structural matches for a * disambiguation response, it must be an entirely new * command */ if (prodList == []) throw new ReplacementCommandStringException(str, nil, nil); /* if we're in debug mode, show the interpretations */ dbgShowGrammarList(prodList); /* create a disambiguation response resolver */ local disResolver = new DisambigResolver( txt, matchList, fullMatchList, fullMatchList, resolver, dist); /* * create a disambiguation resolver that uses the full scope * list - we'll use this as a fallback if we can't match any * objects with the narrowed possibility list */ local scopeDisResolver = new DisambigResolver( txt, matchList, fullMatchList, scopeList, resolver, dist); /* * Run the alternatives through the disambiguation response * ranking process. */ local rankings = DisambigRanking.sortByRanking(prodList, disResolver); /* * If the best item has unknown words, try letting the user * correct typos with OOPS. */ if (rankings[1].nonMatchCount != 0 && rankings[1].unknownWordCount != 0) { try { /* * complain about the unknown word and look for an * OOPS reply */ tryOops(toks, issuingActor_, targetActor_, 1, toks, rmcDisambig); } catch (RetryCommandTokensException exc) { /* get the new token list */ toks = exc.newTokens_; /* replace the string as well */ str = cmdTokenizer.buildOrigText(toks); /* go back for another try at parsing the response */ goto retryParse; } } /* * If the best item we could find has no matches, check to * see if it has miscellaneous noun phrases - if so, it's * probably just a new command, since it doesn't have * anything we recognize as a noun phrase. */ if (rankings[1].nonMatchCount != 0 && rankings[1].miscWordListCount != 0) { /* * it's probably not an answer to our disambiguation * question, so treat it as a whole new command - * abandon the current command and start over with the * new string */ throw new ReplacementCommandStringException(str, nil, nil); } /* if we're in debug mode, show the winning intepretation */ dbgShowGrammarWithCaption('Disambig Winner', rankings[1].match); /* get the response list */ local respList = rankings[1].match.getResponseList(); /* * Select the objects for each response in our winning list. * The user can select more than one of the objects we * offered, so simply take each one they specify here * separately. */ foreach (local resp in respList) { try { try { /* * select the objects for this match, and add * them into the matches so far */ local newObjs = resp.resolveNouns( disResolver, disambigResults); /* set the new token list in the new matches */ for (local n in newObjs) { if (n.np_ != nil) n.np_.setOrigTokenList(npToks); } /* add them to the matches so far */ resultList += newObjs; } catch (UnmatchedDisambigException udExc) { /* * The response didn't match anything in the * narrowed list. Try again with the full scope * list, in case they actually wanted to apply * the command to something that's in scope but * which didn't make the cut for the narrowed * list we originally offered. */ resultList += resp.resolveNouns(scopeDisResolver, disambigResults); } } catch (StillAmbiguousException saExc) { /* * Get the new "reduced" list from the exception. * Use our original full list, and reduce it to * include only the elements in the reduced list - * this will ensure that the items in the new list * are in the same order as they were in the * original list, which keeps the next iteration's * question in the same order. */ local newList = new Vector(saExc.matchList_.length()); foreach (local cur in fullMatchList) { /* * If this item from the original list has an * equivalent in the reduced list, include it in * the new match list. */ if (cur.isDistEquivInList(saExc.matchList_, dist)) newList.append(cur); } /* convert it to a list */ newList = newList.toList(); /* * If that left us with nothing, just keep the * original list unchanged. This can occasionally * happen, such as when a sub-phrase (a locational * qualifier, for example) is itself ambiguous. */ if (newList == []) newList = matchList; /* * Generate the new "full" list. This is a list of * all of the items from our current "full" list * that either are directly in the new match list, * or are indistinguishable from items in the new * reduced list. * * The exception thrower is not capable of providing * us with a new full list, because it only knows * about the reduced list, as the reduced list is * its scope for narrowing the results. */ local newFullList = new Vector(fullMatchList.length()); foreach (local cur in fullMatchList) { /* * if this item is in the new reduced list, or * has an equivalent in the new match list, * include it in the new full list */ if (cur.isDistEquivInList(newList, dist)) { /* * we have this item or something * indistinguishable - include it in the * revised full match list */ newFullList.append(cur); } } /* convert it to a list */ newFullList = newFullList.toList(); /* * They answered, but with insufficient specificity. * Add the given list to the still-to-resolve list, * so that we try again with this new list. */ stillToResolve += new StillToResolveItem(newList, newFullList, saExc.origText_); } catch (DisambigOrdinalOutOfRangeException oorExc) { /* * Explain the problem (note that if we didn't want * to offer another chance here, we could simply * throw this message as a ParseFailureException * instead of continuing with the query loop). * * If we've never asked interactively for input * (because we've obtained all of our input from * past responses to a command we're repeating), * don't show this message, because it makes no * sense when they haven't answered any questions in * the first place. */ if (everAsked) targetActor_.getParserMessageObj(). disambigOrdinalOutOfRange( targetActor_, oorExc.ord_, txt.htmlify()); /* go back to the outer loop to ask for a new response */ askingAgain = true; continue queryLoop; } catch (UnmatchedDisambigException udExc) { local newList; /* * They entered something that looked like a * disambiguation response, but didn't refer to any * of our objects. Try parsing the input as though * it were a new command, and if it matches any * command syntax, treat it as a new command. */ newList = firstCommandPhrase.parseTokens(toks, cmdDict); if (newList.length() != 0) { /* * it appears syntactically to be a new command * - treat it as such by throwing a command * replacement exception */ throw new ReplacementCommandStringException( str, nil, nil); } /* * Explain the problem (note that if we didn't want * to continue with the query loop, we could simply * throw this message as a ParseFailureException). * * Don't show any error if we've never asked * interactively on this turn (which can only be * because we're using interactive responses from a * previous turn that we're repeating), because it * makes no sense when we haven't apparently asked * for anything yet. */ if (everAsked) targetActor_.getParserMessageObj(). noMatchDisambig(targetActor_, txt.htmlify(), udExc.resp_); /* go back to the outer loop to ask for a new response */ askingAgain = true; continue queryLoop; } } /* * If we got this far, the last input we parsed was actually * a response to our question, as opposed to a new command * or something unintelligible. * * If this was an interactive response, add the response to * the response keeper's list of past responses. This will * allow the production to re-use the same list if it has to * re-resolve the phrase in the future, without asking the * user to answer the same questions again */ if (everAsked) keeper.addAmbigResponse(str); /* * if there's nothing left in the still-to-resolve list, we * have nothing left to do, so we can stop working */ if (stillToResolve.length() == 0) break; /* * set up for the next iteration with the first item in the * still-to-resolve list */ matchList = stillToResolve[1].matchList; fullMatchList = stillToResolve[1].fullMatchList; txt = stillToResolve[1].origText; /* remove the first item, since we're going to process it now */ stillToResolve = stillToResolve.sublist(2); } /* success - return the final match list */ return resultList; } /* * filter a match list with a specific Distinguisher */ filterWithDistinguisher(lst, dist) { local result; /* create a vector for the result list */ result = new Vector(lst.length()); /* scan the list */ foreach (local cur in lst) { /* * if we have no equivalent of the current item (for the * purposes of our Distinguisher) in the result list, add * the current item to the result list */ if (!cur.isDistEquivInList(result, dist)) result.append(cur); } /* return the result list */ return result.toList(); } /* * handle a noun phrase that doesn't match any legal grammar rules * for noun phrases */ unknownNounPhrase(match, resolver) { local wordList; local ret; /* * ask the resolver to handle it - if it gives us a resolved * object list, simply return it */ wordList = match.getOrigTokenList(); if ((ret = resolver.resolveUnknownNounPhrase(wordList)) != nil) return ret; /* * The resolver doesn't handle unknown words, so we can't * resolve the phrase. Look for an undefined word and give the * player a chance to correct typos with OOPS. */ tryOops(wordList, issuingActor_, targetActor_, match.firstTokenIndex, match.tokenList, rmcCommand); /* * If we didn't find any unknown words, it means that they used * a word that's in the dictionary in a way that makes no sense * to us. Simply return an empty list and let the resolver * proceed with its normal handling for unmatched noun phrases. */ return []; } getImpliedObject(np, resolver) { /* ask the resolver to supply an implied default object */ return resolver.getDefaultObject(np); } askMissingObject(asker, resolver, responseProd) { /* if we can't resolve this interactively, fail with an error */ if (!canResolveInteractively(resolver.getAction())) { /* interactive resolution isn't allowed - fail */ throw new ParseFailureException(&missingObject, resolver.getAction(), resolver.whichMessageObject); } /* have the action show objects already defaulted */ resolver.getAction().announceAllDefaultObjects(nil); /* ask for a default object */ asker.askMissingObject(targetActor_, resolver.getAction(), resolver.whichMessageObject); /* try reading an object response */ return tryAskingForObject(issuingActor_, targetActor_, resolver, self, responseProd); } noteLiteral(txt) { /* * there's nothing to do with a literal at this point, since * we're not ranking anything */ } askMissingLiteral(action, which) { local ret; /* if we can't resolve this interactively, fail with an error */ if (!canResolveInteractively(action)) { /* interactive resolution isn't allowed - fail */ throw new ParseFailureException(&missingLiteral, action, which); } /* ask for the missing literal */ targetActor_.getParserMessageObj(). askMissingLiteral(targetActor_, action, which); /* read the response */ ret = readMainCommand(rmcAskLiteral); /* re-enable the transcript, if we have one */ if (gTranscript) gTranscript.activate(); /* return the response */ return ret; } emptyNounPhrase(resolver) { /* abort with an error */ throw new ParseFailureException(&emptyNounPhrase); } zeroQuantity(txt) { /* abort with an error */ throw new ParseFailureException(&zeroQuantity, txt.toLower().htmlify()); } insufficientQuantity(txt, matchList, requiredNum) { /* abort with an error */ throw new ParseFailureException( &insufficientQuantity, txt.toLower().htmlify(), matchList, requiredNum); } uniqueObjectRequired(txt, matchList) { /* abort with an error */ throw new ParseFailureException( &uniqueObjectRequired, txt.toLower().htmlify(), matchList); } singleObjectRequired(txt) { /* abort with an error */ throw new ParseFailureException( &singleObjectRequired, txt.toLower().htmlify()); } noteAdjEnding() { /* we don't care about adjective-ending noun phrases at this point */ } noteIndefinite() { /* we don't care about indefinites at this point */ } noteMiscWordList(txt) { /* we don't care about unstructured noun phrases at this point */ } notePronoun() { /* we don't care about pronouns right now */ } noteMatches(matchList) { /* we don't care about the matches just now */ } notePlural() { /* we don't care about these right now */ } beginSingleObjSlot() { } endSingleObjSlot() { } beginTopicSlot() { } endTopicSlot() { } incCommandCount() { /* we don't care about how many subcommands there are */ } noteActorSpecified() { /* * we don't care about this during execution - it only matters * for determining the strength of the command during the * ranking process */ } noteNounSlots(cnt) { /* * we don't care about this during execution; it only matters * for the ranking process */ } noteWeakPhrasing(level) { /* ignore this during execution; it only matters during ranking */ } /* allow remapping the action */ allowActionRemapping = true /* allow making an arbitrary choice among equivalents */ allowEquivalentFiltering = true ; /* * List entry for the still-to-resolve list */ class StillToResolveItem: object construct(lst, fullList, txt) { /* remember the equivalent-reduced and full match lists */ matchList = lst; fullMatchList = fullList; /* note the text */ origText = txt; } /* the reduced (equivalent-eliminated) match list */ matchList = [] /* full (equivalent-inclusive) match list */ fullMatchList = [] /* the original command text being disambiguated */ origText = '' ; /* ------------------------------------------------------------------------ */ /* * Specialized noun-phrase resolution results gatherer for resolving a * command actor (i.e., the target actor of a command). */ class ActorResolveResults: BasicResolveResults construct() { /* do the inherited work */ inherited(); /* * set the initial actor context to the PC - this type of * resolver is set up to determine the actor context, so we don't * usually know the actual actor context yet when setting up this * resolver */ targetActor_ = issuingActor_ = gPlayerChar; } getImpliedObject(np, resolver) { /* * there's no default for the actor - it's usually simply a * syntax error when the actor is omitted */ throw new ParseFailureException(&missingActor); } uniqueObjectRequired(txt, matchList) { /* an actor phrase must address a single actor */ throw new ParseFailureException(&singleActorRequired); } singleObjectRequired(txt) { /* an actor phrase must address a single actor */ throw new ParseFailureException(&singleActorRequired); } /* don't allow action remapping while resolving the actor */ allowActionRemapping = nil ; /* * A results object for resolving an actor in a command with an unknown * word or invalid phrasing in the predicate. For this type of * resolution, we're trying to interpret the actor portion of the command * as a noun phrase referring to an actor, but it could also just be * another command. E.g., we could have "bob, asdf" or "east, asdf". * Since we're only tentatively interpreting the phrase as a noun phrase, * to see if that interpretation goes anywhere, we don't want to throw * any errors on failures; instead we simply allow empty match lists. */ class TryAsActorResolveResults: ResolveResults noVocabMatch(action, txt) { } noMatch(action, txt) { } noMatchPossessive(action, txt) { } noMatchForAll() { } noMatchForAllBut() { } noMatchForListBut() { } noteEmptyBut() { } noMatchForPronoun() { } allNotAllowed() { } reflexiveNotAllowed() { } wrongReflexive(typ, txt) { } noMatchForPossessive(owner, txt) { } noMatchForLocation(loc, txt) { } noteBadPrep() { } nothingInLocation(loc) { } ambiguousNounPhrase(keeper, askwer, txt, matchLst, fullMatchLst, scopeList, requiredNum, resolver) { } unknownNounPhrase(match, resolver) { } getImpliedObject(np, resolver) { return []; } askMissingObject(asker, resolver, responseProd) { return []; } noteLiteral(txt) { } askMissingLiteral(action, which) { return nil; } emptyNounPhrase(resolver) { } zeroQuantity(txt) { } insufficientQuantity(txt, matchList, requiredNum) { } uniqueObjectRequired(txt, matchList) { } singleObjectRequired(txt) { } noteAdjEnding() { } noteIndefinite() { } noteMatches(matchList) { } noteMiscWord(txt) { } notePronoun() { } notePlural() { } beginSingleObjSlot() { } endSingleObjSlot() { } beginTopicSlot() { } endTopicSlot() { } incCommandCount() { } noteActorSpecified() { } noteNounSlots(cnt) { } noteWeakPhrasing() { } allowActionRemapping = true allowEquivalentFiltering = true ; /* ------------------------------------------------------------------------ */ /* * Command ranking criterion. This is used by the CommandRanking class * to represent one criterion for comparing two parse trees. * * Rankings are performed in two passes. The first pass is the rough, * qualitative pass, meant to determine if one parse tree has big, * obvious differences from another. In most cases, this means that one * tree has a particular type of problem or special advantage that the * other doesn't have at all. * * The second pass is the fine-grained pass. We only reach the second * pass if we can't find any coarse differences on the first rough pass. * In most cases, the second pass compares the magnitude of problems or * advantages to determine if one tree is slightly better than the other. */ class CommandRankingCriterion: object /* * Compare two CommandRanking objects on the basis of this criterion, * for the first, coarse-grained pass. Returns a positive number if * a is better than b, 0 if they're indistinguishable, or -1 if a is * worse than b. */ comparePass1(a, b) { return 0; } /* compare two rankings for the second, fine-grained pass */ comparePass2(a, b) { return 0; } ; /* * A command ranking criterion that measures a "problem" by a count of * occurrences stored in a property of the CommandRanking object. For * example, we could count the number of noun phrases that don't resolve * to any objects. * * On the first, coarse-grained pass, we measure only the presence or * absence of our problem. That is, if one parse tree has zero * occurrences of the problem and the other has a non-zero number of * occurrences of the problem (as measured by our counting property), * then we'll prefer the one with zero occurrences. If both have no * occurrences, or both have a non-zero number of occurrences, we'll * consider the two equivalent for the first pass, since we only care * about the presence or absence of the problem. * * On the second, fine-grained pass, we measure the actual number of * occurrences of the problem, and choose the parse tree with the lower * number. */ class CommandRankingByProblem: CommandRankingCriterion /* * our ranking property - this is a property of the CommandRanking * object that gives us a count of the number of times our "problem" * has occurred in the ranking object's parse tree */ prop_ = nil /* first pass - compare by presence or absence of the problem */ comparePass1(a, b) { local acnt = a.(self.prop_); local bcnt = b.(self.prop_); /* if b has the problem but a doesn't, a is better */ if (acnt == 0 && bcnt != 0) return 1; /* if a has the problem but b doesn't, b is better */ if (acnt != 0 && bcnt == 0) return -1; /* we can't tell the difference at this stage */ return 0; } /* second pass - compare by number of occurrences of the problem */ comparePass2(a, b) { /* * Return the difference in the problem counts. We want to * return >0 if a has fewer problems, <0 if b has fewer problems: * so compute (a-b) and negate it, which is the same as computing * (a-b). */ return b.(self.prop_) - a.(self.prop_); } ; /* * A "weakness" criterion. This is similar to the rank-by-problem * criterion, but rather than ranking on an actual structural problem, it * ranks on a structural weakness. This is suitable for things like * adjective endings and truncations, where the weakness isn't on the * same order as a "problem" but where we'd still rather avoid the * weakness if we can. * * The point of the separate "weakness" criterion is that we only allow * weaknesses to come into play on pass 2, after we've already * discriminated based on problems. If we can discriminate based on * problems, we'll do so in pass 1 and won't even get to pass 2; we'll * only discriminate based on weakness if we can't tell the difference * based on real problems. */ class CommandRankingByWeakness: CommandRankingCriterion /* on pass 1, ignore weaknesses */ comparePass1(a, b) { return 0; } /* on pass 2, compare based on weaknesses */ comparePass2(a, b) { return b.(self.prop_) - a.(self.prop_); } /* our command-ranking property */ prop_ = nil ; /* * command-ranking-by-problem and by-weakness objects for the pre-defined * ranking criteria */ rankByVocabNonMatch: CommandRankingByProblem prop_ = &vocabNonMatchCount; rankByNonMatch: CommandRankingByProblem prop_ = &nonMatchCount; rankByInsufficient: CommandRankingByProblem prop_ = &insufficientCount; rankByListForSingle: CommandRankingByProblem prop_ = &listForSingle; rankByEmptyBut: CommandRankingByProblem prop_ = &emptyButCount; rankByAllExcluded: CommandRankingByProblem prop_ = &allExcludedCount; rankByActorSpecified: CommandRankingByProblem prop_ = &actorSpecifiedCount; rankByMiscWordList: CommandRankingByProblem prop_ = &miscWordListCount; rankByPluralTrunc: CommandRankingByWeakness prop_ = &pluralTruncCount; rankByEndAdj: CommandRankingByWeakness prop_ = &endAdjCount; rankByIndefinite: CommandRankingByProblem prop_ = &indefiniteCount; rankByTrunc: CommandRankingByWeakness prop_ = &truncCount; rankByMissing: CommandRankingByProblem prop_ = &missingCount; rankByPronoun: CommandRankingByWeakness prop_ = &pronounCount; rankByWeakness: CommandRankingByWeakness prop_ = &weaknessLevel; rankByUnwantedPlural: CommandRankingByProblem prop_ = &unwantedPluralCount; /* * Rank by unmatched possessive-qualified phrases. If we have two * unknown phrases, one with a possessive qualifier and one without, and * other things being equal, prefer the one with the possessive * qualifier. * * We prefer the qualified version because it lets us report a smaller * phrase that we can't match. For example, in X BOB'S WALLET, if we * can't match WALLET all by itself, it's more useful to report that "you * see no wallet" than to report that you see no "bob's wallet", because * the latter incorrectly implies that there might still be a wallet in * scope as long as it's not Bob's we're looking for. */ rankByNonMatchPoss: CommandRankingCriterion /* * ignore on pass 1 - this only counts if other factors are equal, so * we want to consider all of the other factors on pass 1 before * taking this criterion into account */ comparePass1(a, b) { return 0; } /* pass 2 - more possessives are better */ comparePass2(a, b) { /* * if we don't have the same underlying non-match count, * possessive qualification is irrelevant */ if (a.nonMatchCount != b.nonMatchCount) return 0; /* more possessives are better */ return a.nonMatchPossCount - b.nonMatchPossCount; } ; /* * Command ranking by literal phrase length. We prefer interpretations * that treat less text as uninterpreted literal text. By "less text," * we simply mean that one has a shorter string treated as literal text * than the other. (We prefer shorter literals because when the parser * matches a string of literal text, it's essentially throwing up its * hands and admitting it can't parse the text; so the less text is * contained in literals, the more text the parser is actually parsing, * and more parsed is better.) */ rankByLiteralLength: CommandRankingCriterion /* first pass */ comparePass1(a, b) { /* * Compare our lengths. We want to return >0 if a is shorter and * <0 if a is longer (and, of course, 0 if they're the same * length). So, we can just compute (a-b) and negate the result, * which is the same as computing (b-a). * * The CommandRanking objects keep track of the length of text in * literals in their literalLength properties. */ return b.literalLength - a.literalLength; } /* * Second pass - we use our full powers of discrimination on the * first pass, so if we make it to the second pass, we couldn't tell * a difference on the first pass and thus can't tell a difference * now. So, just inherit the default implementation, which simply * returns 0 to indicate that there's no difference. */ ; /* * Command ranking by subcommand count: we prefer the match with fewer * subcommands. If one has fewer subcommands than the other, it means * that we were able to interpret ambiguous conjunctions (such as "and") * as noun phrase conjunctions rather than as command conjunctions; other * things being equal, we'd rather take the interpretation that gives us * noun phrases than the one that involves more separate commands. */ rankBySubcommands: CommandRankingCriterion /* first pass - compare subcommand counts */ comparePass1(a, b) { /* * if a has fewer subcommands, return <0, and if b has fewer * subcommands, return >0: so we can just return the negative of * (a-b), or (b-a) */ return b.commandCount - a.commandCount; } /* second pass - do nothing, as we do all of our work on the first pass */ ; /* * Rank by token count. Other things being equal, we'd rather pick a * longer match. If one match is shorter than the other in terms of the * number of tokens it encompasses, then it means that the shorter match * left more tokens at the end of the command to be interpreted as * separate commands. If we have an interpretation that can take more of * those tokens and parse them as part of the current command, that * interpretation is probably better. */ rankByTokenCount: CommandRankingCriterion /* first pass - compare token counts */ comparePass1(a, b) { /* choose the one that matched more tokens */ return a.tokCount - b.tokCount; } /* first pass - we do all our work on the first pass */ ; /* * Rank by "verb structure." This gives more weight to an * interpretation that has more structural noun phrases in the verb. * For example, "DETACH dobj FROM iobj" is given more weight than * "DETACH dobj", because the former has two structural noun phrases * whereas the latter has only one. This will make us prefer to treat * DETACH WIRE FROM BOX as a two-object action, for example, even if we * could treat WIRE FROM BOX as a single "locational" noun phrase. */ rankByVerbStructure: CommandRankingCriterion comparePass2(a, b) { /* take the one with more structural noun slots in the verb phrase */ return a.nounSlotCount - b.nounSlotCount; } ; /* * Rank by ambiguous noun phrases. We apply this criterion on the second * pass only, because it's a weak test: we might end up narrowing things * down through automatic "logicalness" tests during the noun resolution * process, so ambiguity at this stage in the parsing process doesn't * necessarily indicate that there's real ambiguity in the command. * However, if we can already tell that one interpretation is unambiguous * and another is ambiguous, and the two interpretations are otherwise * equally good, pick the one that's already unambiguous: the ambiguous * interpretation might or might not stay ambiguous, but the unambiguous * interpretation will definitely stay unambiguous. */ rankByAmbiguity: CommandRankingCriterion /* * Do nothing on the first pass, because we want any first-pass * criterion to prevail over our weak test. Instead, check for a * difference in ambiguity only on the second pass. */ comparePass2(a, b) { /* the one with lower ambiguity is better */ return b.ambigCount - a.ambigCount; } ; /* * Production match ranking object. We create one of these objects for * each match tree that we wish to rank. * * This class is generally not instantiated by client code - instead, * clients use the sortByRanking() class method to rank a list of * production matches. */ class CommandRanking: ResolveResults /* * Sort a list of productions, as returned from * GrammarProd.parseTokens(), in descending order of command * strength. We return a list of CommandRanking objects whose first * element is the best command interpretation. * * Note that this can be used as a class-level method. */ sortByRanking(lst, [resolveArguments]) { /* * create a vector to hold the ranking information - we * need one ranking item per match */ local rankings = new Vector(lst.length()); /* get the ranking information for each command */ foreach(local cur in lst) { local curRank; /* create a ranking item for the entry */ curRank = self.createInstance(cur); /* rank this entry */ curRank.calcRanking(resolveArguments); /* add this to our ranking list */ rankings.append(curRank); } /* sort the entries by descending ranking, and return the results */ return rankings.sort(SortDesc, {x, y: x.compareRanking(y)}); } /* create a new entry */ construct(match) { /* remember the match object */ self.match = match; /* remember the number of tokens in the match */ tokCount = match.lastTokenIndex - match.firstTokenIndex + 1; } /* calculate my ranking */ calcRanking(resolveArguments) { /* * Ask the match tree to resolve nouns, using this ranking * object as the resolution results receiver - when an error or * warning occurs during resolution, we'll merely note the * condition rather than say anything about it. * * Note that 'self' is the results object, because the point of * this resolution pass is to gather statistics into this * results object. */ match.resolveNouns(resolveArguments..., self); } /* * Compare two production list entries for ranking purposes. Returns * a negative number if this one ranks worse than the other, 0 if * they have the same ranking, or a positive number if this one ranks * better than the other one. * * This routine is designed to run entirely off of our * rankingCriteria property. In most cases, subclasses should be * able to customize the ranking system simply by overriding the * rankingCriteria property to provide a customized list of criteria * objects. */ compareRanking(other) { local ret; /* * Run through our ranking criteria and apply the first pass to * each one. Return the indication of the first criterion that * can tell a difference. */ foreach (local cur in rankingCriteria) { /* if the rankings differ in this criterion, return the result */ if ((ret = cur.comparePass1(self, other)) != 0) return ret; } /* * We couldn't tell any difference on the first pass, so try * again with the finer-grained second pass. */ foreach (local cur in rankingCriteria) { /* run the second pass */ if ((ret = cur.comparePass2(self, other)) != 0) return ret; } /* we couldn't tell any difference between the two */ return 0; } /* * Our list of ranking criteria. This is a list of * CommandRankingCriterion objects. The list is given in order of * importance: the first criterion is the most important, so if it * can discriminate the two match trees, we use its result; if the * first criterion can't tell any difference, then we move on to the * second criterion; and so on through the list. * * The most important thing is whether or not we have irresolvable * noun phrases (vocabNonMatchCount). If one of us has a noun phrase * that refers to nothing anywhere in the game, it's not as good as a * phrase that at least matches something somewhere. * * Next, if one of us has noun phrases that cannot be resolved to * something in scope (nonMatchCount), and the other can successfully * resolve its noun phrases, the one that can resolve the phrases is * preferred. * * Next, check for insufficient numbers of matches to counted phrases * (insufficientCount). * * Next, check for noun lists in single-noun-only slots * (listForSingle). * * Next, if we have an empty "but" list in one but not the other, * take the one with the non-empty "but" list (emptyButCount). We * prefer a non-empty "but" list with an empty "all" even to a * non-empty "all" list with an empty "but", because in the latter * case we probably failed to exclude anything because we * misinterpreted the noun phrase to be excluded. * * Next, if we have an empty "all" or "any" phrase due to "but" * exclusion, take the one that's not empty (allExcludedCount). * * Next, prefer a command that addresses an actor * (actorSpecifiedCount) - if the actor name looks like a command (we * have someone named "Open Bob," maybe?), we'd prefer to interpret * the name appearing as a command prefix as an actor name. * * Next, prefer no unstructured word lists as noun phrases * (miscWordList phrases) (miscWordListCount). * * Next, prefer interpretations that treat less text as uninterpreted * literal text. By "less text," we simply mean that one has a * shorter string treated as a literal than the other. * * Prefer no indefinite noun phrases (indefiniteCount). * * Prefer no truncated plurals (pluralTruncCount). * * Prefer no noun phrases ending in adjectives (endAdjCount). * * Prefer no truncated words of any kind (truncCount). * * Prefer fewer pronouns. If we have an interpretation that matches * a word to explicit vocabulary, take it over matching a word as a * pronoun: if a word is given explicitly as vocabulary for an * object, use it if possible. * * Prefer no missing phrases (missingCount). * * Prefer the one with fewer subcommands - if one has fewer * subcommands than the other, it means that we were able to * interpret ambiguous conjunctions (such as "and") as noun phrase * conjunctions rather than as command conjunctions; since we know by * now that we both either have or don't have unresolved noun * phrases, we'd rather take the interpretation that gives us noun * phrases than the one that involves more separate commands. * * Prefer the tree that matches more tokens. * * Prefer the one with more structural noun phrases in the verb. For * example, if we have one interpretation that's DETACH (X FROM Y) * (where X FROM Y is a 'locational' phrase that we treat as the * direct object), and one that's DETACH X FROM Y (where X is the * direct object and Y is in the indirect object), prefer the latter, * because it has both direct and indirect object phrases, whereas * the former has only a direct object phrase. English speakers * almost always try to put prepositions into a structural role in * the verb phrase like this when they could be either in the verb * phrase or part of a noun phrase. * * If all else fails, prefer the one that is initially less * ambiguous. Ambiguity is a weak test at this point, since we might * end up narrowing things down through automatic "logicalness" tests * later, but it's slightly better to have the match be less * ambiguous now, all other things being equal. */ rankingCriteria = [rankByVocabNonMatch, rankByNonMatch, rankByNonMatchPoss, rankByInsufficient, rankByListForSingle, rankByEmptyBut, rankByAllExcluded, rankByActorSpecified, rankByUnwantedPlural, rankByMiscWordList, rankByWeakness, rankByLiteralLength, rankByIndefinite, rankByPluralTrunc, rankByEndAdj, rankByTrunc, rankByPronoun, rankByMissing, rankBySubcommands, rankByTokenCount, rankByVerbStructure, rankByAmbiguity] /* the match tree I'm ranking */ match = nil /* the number of tokens my match tree consumes */ tokCount = 0 /* * Ranking information. calcRanking() fills in these members, and * compareRanking() uses these to calculate the relative ranking. */ /* * The number of structural "noun phrase slots" in the verb. An * intransitive verb has no noun phrase slots; a transitive verb * with a direct object has one; a verb with a direct and indirect * object has two slots. */ nounSlotCount = nil /* number of noun phrases matching nothing anywhere in the game */ vocabNonMatchCount = 0 /* number of noun phrases matching nothing in scope */ nonMatchCount = 0 /* * Number of possessive-qualified noun phrases matching nothing in * scope. For example, "bob's desk" when there's no desk in scope * (Bob's or otherwise). */ nonMatchPossCount = 0 /* number of phrases requiring quantity higher than can be fulfilled */ insufficientCount = 0 /* number of noun lists in single-noun slots */ listForSingle = 0 /* number of empty "but" lists */ emptyButCount = 0 /* number of "all" or "any" lists totally excluded by "but" */ allExcludedCount = 0 /* missing phrases (structurally omitted, as in "put book") */ missingCount = 0 /* number of truncated plurals */ pluralTruncCount = 0 /* number of phrases ending in adjectives */ endAdjCount = 0 /* number of phrases with indefinite noun phrase structure */ indefiniteCount = 0 /* number of miscellaneous word lists as noun phrases */ miscWordListCount = 0 /* number of truncated words overall */ truncCount = 0 /* number of ambiguous noun phrases */ ambigCount = 0 /* number of subcommands in the command */ commandCount = 0 /* an actor is specified */ actorSpecifiedCount = 0 /* unknown words */ unknownWordCount = 0 /* total character length of literal text phrases */ literalLength = 0 /* number of pronoun phrases */ pronounCount = 0 /* weakness level (for noteWeakPhrasing) */ weaknessLevel = 0 /* number of plural phrases encountered in single-object slots */ unwantedPluralCount = 0 /* -------------------------------------------------------------------- */ /* * ResolveResults implementation. We use this results receiver when * we're comparing the semantic strengths of multiple structural * matches, so we merely note each error condition without showing * any message to the user or asking the user for any input. Once * we've ranked all of the matches, we'll choose the one with the * best attributes and then resolve it for real, at which point if * we chose one with any errors, we'll finally get around to showing * the errors to the user. */ noVocabMatch(action, txt) { /* note the unknown phrase */ ++vocabNonMatchCount; } noMatch(action, txt) { /* note that we have a noun phrase that matches nothing */ ++nonMatchCount; } noMatchPossessive(action, txt) { /* note that we have an unmatched possessive-qualified noun phrase */ ++nonMatchCount; ++nonMatchPossCount; } allNotAllowed() { /* treat this as a non-matching noun phrase */ ++nonMatchCount; } noMatchForAll() { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } noteEmptyBut() { /* note it */ ++emptyButCount; } noMatchForAllBut() { /* count the total exclusion */ ++allExcludedCount; } noMatchForListBut() { /* treat this as any other noun phrase that matches nothing */ ++allExcludedCount; } noMatchForPronoun(typ, txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } reflexiveNotAllowed(typ, txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } wrongReflexive(typ, txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } noMatchForPossessive(owner, txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } noMatchForLocation(loc, txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } noteBadPrep() { /* don't do anything at this point */ } nothingInLocation(txt) { /* treat this as any other noun phrase that matches nothing */ ++nonMatchCount; } ambiguousNounPhrase(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver) { local lst; /* note the ambiguity */ ++ambigCount; /* * There's no need to disambiguate the list at this stage, since * we're only testing the strength of the structure. * * As a tentative approximation of the results, return a list * consisting of the required number only, but stash away the * remainder of the full list as a property of the first element * of the return list so we can find the full list again later. */ lst = matchList.sublist(1, requiredNum); if (matchList.length() > requiredNum && lst.length() >= 1) lst[1].extraObjects = matchList.sublist(requiredNum + 1); /* return the abbreviated list */ return lst; } unknownNounPhrase(match, resolver) { local wordList; local ret; /* * if the resolver can handle this set of unknown words, treat * it as a good noun phrase; otherwise, treat it as an unmatched * noun phrase */ wordList = match.getOrigTokenList(); if ((ret = resolver.resolveUnknownNounPhrase(wordList)) == nil) { /* count the unmatchable phrase */ ++nonMatchCount; /* count the unknown word */ ++unknownWordCount; /* * since this is only a ranking pass, resolve to an empty * list for now */ ret = []; } /* return the results */ return ret; } getImpliedObject(np, resolver) { /* count the missing object phrase */ ++missingCount; return nil; } askMissingObject(asker, resolver, responseProd) { /* * no need to do anything here - we'll count the missing object * in getImpliedObject, and we don't want to ask for anything * interactively at this point */ return nil; } noteLiteral(txt) { /* add the length of this literal to the total literal length */ literalLength += txt.length(); } emptyNounPhrase(resolver) { /* treat this as a non-matching noun phrase */ ++nonMatchCount; return []; } zeroQuantity(txt) { /* treat this as a non-matching noun phrase */ ++nonMatchCount; } insufficientQuantity(txt, matchList, requiredNum) { /* treat this as a non-matching noun phrase */ ++insufficientCount; } singleObjectRequired(txt) { /* treat this as a non-matching noun phrase */ ++listForSingle; } uniqueObjectRequired(txt, matchList) { /* * ignore this for now - we might get a unique object via * disambiguation during the execution phase */ } noteAdjEnding() { /* count it */ ++endAdjCount; } noteIndefinite() { /* count it */ ++indefiniteCount; } noteMiscWordList(txt) { /* note the presence of an unstructured noun phrase */ ++miscWordListCount; /* count this as a literal as well */ noteLiteral(txt); } notePronoun() { /* note the presence of a pronoun */ ++pronounCount; } noteMatches(matchList) { /* * Run through the match list and note each weak flag. Note * that each element of the match list is a ResolveInfo * instance. */ foreach (local cur in matchList) { /* if this object was matched with a truncated word, note it */ if ((cur.flags_ & VocabTruncated) != 0) ++truncCount; /* if this object was matched with a truncated plural, note it */ if ((cur.flags_ & PluralTruncated) != 0) ++pluralTruncCount; } } beginSingleObjSlot() { ++inSingleObjSlot; } endSingleObjSlot() { --inSingleObjSlot; } inSingleObjSlot = 0 beginTopicSlot() { ++inTopicSlot; } endTopicSlot() { --inTopicSlot; } inTopicSlot = 0 notePlural() { /* * If we're resolving a single-object slot, we want to avoid * plurals, since they could resolve to multiple objects as * though we'd typed a list of objects here. This isn't a * problem for topics, though, since a topic slot isn't iterated * for execution. */ if (inSingleObjSlot && !inTopicSlot) ++unwantedPluralCount; } incCommandCount() { /* increase our subcommand counter */ ++commandCount; } noteActorSpecified() { /* note it */ ++actorSpecifiedCount; } noteNounSlots(cnt) { /* * If this is the first noun slot count we've received, remember * it. If we already have a count, ignore the new one - we only * want to consider the first verb phrase if there are multiple * verb phrases, since we'll reconsider the next verb phrase when * we're ready to execute it. */ if (nounSlotCount == nil) nounSlotCount = cnt; } noteWeakPhrasing(level) { /* note the weak phrasing level */ weaknessLevel = level; } /* don't allow action remapping while ranking */ allowActionRemapping = nil ; /* * Another preliminary results gatherer that does everything the way the * CommandRanking results object does, except that we perform * interactive resolution of unknown words via OOPS. */ class OopsResults: CommandRanking construct(issuingActor, targetActor) { /* remember the actors */ issuingActor_ = issuingActor; targetActor_ = targetActor; } /* * handle a phrase with unknown words */ unknownNounPhrase(match, resolver) { /* * if the resolver can handle this set of unknown words, treat * it as a good noun phrase */ local wordList = match.getOrigTokenList(); local ret = resolver.resolveUnknownNounPhrase(wordList); if (ret != nil) return ret; /* * we still can't resolve it; try prompting for a correction of * any misspelled words in the phrase */ tryOops(wordList, issuingActor_, targetActor_, match.firstTokenIndex, match.tokenList, rmcCommand); /* * if we got this far, we still haven't resolved it; resolve to * an empty phrase */ return []; } /* the command's issuing actor */ issuingActor_ = nil /* the command's target actor */ targetActor_ = nil ; /* ------------------------------------------------------------------------ */ /* * Exception list resolver. We use this type of resolution for noun * phrases in the "but" list of an "all but" construct. * * We scope the "all but" list to the objects in the "all" list, since * there's no point in excluding objects that aren't in the "all" list. * In addition, if a phrase in the exclusion list matches more than one * object in the "all" list, we consider it a match to all of those * objects, even if it's a definite phrase - this means that items in * the "but" list are never ambiguous. */ class ExceptResolver: ProxyResolver construct(mainList, mainListText, resolver) { /* invoke the base class constructor */ inherited(resolver); /* remember the main list, from which we're excluding items */ self.mainList = mainList; self.mainListText = mainListText; } /* we're a sub-phrase resolver */ isSubResolver = true /* * match an object's name - we'll use the disambiguation name * resolver, so that they can give us partial names just like in * answer to a disambiguation question */ matchName(obj, origTokens, adjustedTokens) { return obj.matchNameDisambig(origTokens, adjustedTokens); } /* * Resolve qualifiers in the enclosing main scope, since qualifier * phrases are not part of the narrowed list - qualifiers apply to * the main phrase from which we're excluding, not to the exclusion * list itself. */ getQualifierResolver() { return origResolver; } /* * determine if an object is in scope - it's in scope if it's in the * original main list */ objInScope(obj) { return mainList.indexWhich({x: x.obj_ == obj}) != nil; } /* for 'all', simply return the whole original list */ getAll(np) { return mainList; } /* filter ambiguous equivalents */ filterAmbiguousEquivalents(lst, np) { /* * keep all of the equivalent items in an exception list, * because we want to exclude all of the equivalent items from * the main list */ return lst; } /* filter an ambiguous noun list */ filterAmbiguousNounPhrase(lst, requiredNum, np) { /* * noun phrases in an exception list are never ambiguous, * because they implicitly refer to everything they match - * simply return the full matching list */ return lst; } /* filter a plural noun list */ filterPluralPhrase(lst, np) { /* return all of the original plural matches */ return lst; } /* the main list from which we're excluding things */ mainList = nil /* the original text for the main list */ mainListText = '' /* the original underlying resolver */ origResolver = nil ; /* * Except list results object */ class ExceptResults: object construct(results) { /* remember the original results object */ origResults = results; } /* * ignore failed matches in the exception list - if they try to * exclude something that's not in the original list, the object is * excluded to begin with */ noMatch(action, txt) { } noMatchPoss(action, txt) { } noVocabMatch(action, txt) { } /* ignore failed matches for possessives in the exception list */ noMatchForPossessive(owner, txt) { } /* ignore failed matches for location in the exception list */ noMatchForLocation(loc, txt) { } /* ignore failed matches for location in the exception list */ nothingInLocation(loc) { } /* * in case of ambiguity, simply keep everything and treat it as * unambiguous - if they say "take coin except copper", we simply * want to treat "copper" as unambiguously excluding every copper * coin in the original list */ ambiguousNounPhrase(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver) { /* return the full match list - exclude everything that matches */ return fullMatchList; } /* proxy anything we don't override to the underlying results object */ propNotDefined(prop, [args]) { return origResults.(prop)(args...); } /* my original underlying results object */ origResults = nil ; /* ------------------------------------------------------------------------ */ /* * Base class for parser exceptions */ class ParserException: Exception ; /* * Terminate Command exception - when the parser encounters an error * that makes it impossible to go any further processing a command, we * throw this error to abandon the current command and proceed to the * next. This indicates a syntax error or semantic resolution error * that renders the command meaningless or makes it impossible to * proceed. * * When this exception is thrown, all processing of the current command * termintes immediately. No further action processing is performed; we * don't continue iterating the command on any additional objects for a * multi-object command; and we discard any remaining commands on the * same command line. */ class TerminateCommandException: ParserException ; /* * Cancel Command Line exception. This is used to cancel any *remaining* * commands on a command line after finishing execution of one command on * the line. For example, if the player types "TAKE BOX AND GO NORTH", * the handler for TAKE BOX can throw this exception to cancel everything * later on the command line (in this case, the GO NORTH part). * * This is handled almost identically to TerminateCommandException. The * only difference is that some games might want to alert the player with * an explanation that extra commands are being ignored. */ class CancelCommandLineException: TerminateCommandException ; /* * Parsing failure exception. This exception is parameterized with * message information describing the failure, and can be used to route * the failure notification to the issuing actor. */ class ParseFailureException: ParserException construct(messageProp, [args]) { /* remember the message property and the parameters */ message_ = messageProp; args_ = args; } /* notify the issuing actor of the problem */ notifyActor(targetActor, issuingActor) { /* * Tell the target actor to notify the issuing actor. We route * the notification from the target to the issuer in keeping * with conversation we're modelling: the issuer asked the * target to do something, so the target is now replying with * information explaining why the target can't do as asked. */ targetActor.notifyParseFailure(issuingActor, message_, args_); } displayException() { "Parse failure exception"; } /* the message property ID */ message_ = nil /* the (varargs) parameters to the message */ args_ = nil ; /* * Exception: Retry a command with new tokens. In some cases, the * parser processes a command by replacing the command with a new one * and processing the new one instead of the original. When this * happens, the parser will throw this exception, filling in newTokens_ * with the replacement token list. * * Note that this is meant to replace the current command only - this * exception effectively *edits* the current command. Any pending * tokens after the current command should be retained when this * exception is thrown. */ class RetryCommandTokensException: ParserException construct(lst) { /* remember the new token list */ newTokens_ = lst; } /* * The replacement token list. Note that this is in the same format * as the token list returned from the tokenizer, so this is a list * consisting of two sublists - one for the token strings, the other * for the corresponding token types. */ newTokens_ = [] ; /* * Replacement command string exception. Abort any current command * line, and start over with a brand new input string. Note that any * pending, unparsed tokens on the previous command line should be * discarded. */ class ReplacementCommandStringException: ParserException construct(str, issuer, target) { /* remember the new command string */ newCommand_ = str; /* * note the issuing actor; if the caller specified this as nil, * use the current player character as the default */ issuingActor_ = (issuer == nil ? libGlobal.playerChar : issuer); /* note the default target actor, defaulting to the player */ targetActor_ = (target == nil ? libGlobal.playerChar : target); } /* the new command string */ newCommand_ = '' /* the actor issuing the command */ issuingActor_ = nil /* the default target actor of the command */ targetActor_ = nil ; /* ------------------------------------------------------------------------ */ /* * Parser debugging helpers */ #ifdef PARSER_DEBUG /* * Show a list of match trees */ showGrammarList(matchList) { /* show the list only if we're in debug mode */ if (libGlobal.parserDebugMode) { /* show each match tree in the list */ foreach (local cur in matchList) { "\n----------\n"; showGrammar(cur, 0); } } } /* * Show a winning match tree */ showGrammarWithCaption(headline, match) { /* show the list only if we're in debug mode */ if (libGlobal.parserDebugMode) { "\n----- <> -----\n"; showGrammar(match, 0); } } /* * Show a grammar tree */ showGrammar(prod, indent) { local info; /* if we're not in parser debug mode, do nothing */ if (!libGlobal.parserDebugMode) return; /* indent to the requested level */ for (local i = 0 ; i < indent ; ++i) "\ \ "; /* check for non-production objects */ if (prod == nil) { /* this tree element isn't used - skip it */ return; } else if (dataType(prod) == TypeSString) { /* show the item literally, and we're done */ "'<>'\n"; return; } /* get the information for this item */ info = prod.grammarInfo(); /* if it's nil, there's nothing more to do */ if (info == nil) { "\n"; return; } /* show the name */ "<> [<>]\n"; /* show the subproductions */ for (local i = 2 ; i <= info.length ; ++i) showGrammar(info[i], indent + 1); } #endif /* PARSER_DEBUG */ frobtads-1.2.3/tads3/lib/adv3/exec.t0000644000175000001440000021625612145504453016255 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: command execution * * This module defines functions that perform command execution. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Execute a command line, as issued by the given actor and as given as a * list of tokens. * * If 'firstInSentence' is true, we're at the start of a "sentence." The * meaning and effect of this may vary by language. In English, a * sentence ends with certain punctuation marks (a period, semicolon, * exclamation mark, or question mark), so anything after one of these * punctuation marks is the start of a new sentence. Also in English, we * can address a command to an explicit target actor using the "actor," * prefix syntax, which we can't use except at the start of a sentence. * * If the command line consists of multiple commands, we will only * actually execute the first command before returning. We'll schedule * any additional commands for later execution by putting them into the * target actor's pending command queue before we return, but we won't * actually execute them. */ executeCommand(targetActor, issuingActor, toks, firstInSentence) { local actorPhrase; local actorSpecified; /* * Turn on sense caching while we're working, until execution * begins. The parsing and resolution phases of command processing * don't involve any changes to game state, so we can safely cache * sense information; caching sense information during these phases * is desirable because these steps (noun resolution in particular) * involve repeated inspection of the current sensory environment, * which can require expensive calculations. */ libGlobal.enableSenseCache(); /* we don't have an explicit actor phrase yet */ actorPhrase = nil; /* presume an actor will not be specified */ actorSpecified = nil; /* * If this is the start of a new sentence, and the issuing actor * wants to cancel any target actor designation at the end of each * sentence, change the target actor back to the issuing actor. */ if (firstInSentence && issuingActor != targetActor && issuingActor.revertTargetActorAtEndOfSentence) { /* switch to the target actor */ targetActor = issuingActor; /* switch to the issuer's sense context */ senseContext.setSenseContext(issuingActor, sight); } /* * Keep going until we've processed the command. This might take * several iterations, because we might have replacement commands to * execute. */ parseTokenLoop: for (;;) { local lst; local action; local match; local nextIdx; local nextCommandTokens; local extraIdx; local extraTokens; /* * Catch any errors that occur while executing the command, * since some of them are signals to us that we should reparse * some new text read or generated deep down inside the command * processing. */ try { local rankings; /* we have no extra tokens yet */ extraTokens = []; /* * Parse the token list. If this is the first command on * the command line, allow an actor prefix. Otherwise, just * look for a command. */ lst = (firstInSentence ? firstCommandPhrase : commandPhrase) .parseTokens(toks, cmdDict); /* * As a first cut at reducing the list of possible matches * to those that make sense, eliminate from this list any * matches which do not have valid actions. In grammars for * "scrambling" languages (i.e., languages with flexible * word ordering), it is possible to construct commands that * fit the grammatical rules of sentence construction but * which make no sense because of the specific constraints * of the verbs involved; we can filter out such nonsense * interpretations immediately by keeping only those * structural interpretations that can resolve to valid * actions. */ lst = lst.subset( {x: x.resolveFirstAction(issuingActor, targetActor) != nil}); /* if we have no matches, the command isn't understood */ if (lst.length() == 0) { /* * If this is a first-in-sentence phrase, try looking for * a target actor phrase. If we can find one, we can * direct the 'oops' to that actor, to allow the game to * customize messages more specifically. */ if (firstInSentence) { local i; /* try parsing an "actor, " phrase */ lst = actorBadCommandPhrase.parseTokens(toks, cmdDict); /* if we got any matches, try to resolve actors */ lst = lst.mapAll({x: x.resolveNouns( issuingActor, issuingActor, new TryAsActorResolveResults())}); /* drop any that didn't yield any results */ lst = lst.subset({x: x != nil && x.length() != 0}); /* * * if anything's left, and one of the entries * resolves to an actor, arbitrarily pick the * first such entry use the resolved actor as the * new target actor */ if (lst.length() != 0 && (i = lst.indexWhich( {x: x[1].obj_.ofKind(Actor)})) != nil) targetActor = lst[i][1].obj_; } /* * We don't understand the command. Check for unknown * words - if we have any, give them a chance to use * OOPS to correct a typo. */ tryOops(toks, issuingActor, targetActor, 1, toks, rmcCommand); /* * try running it by the SpecialTopic history to see if * they're trying to use a special topic in the wrong * context - if so, explain that they can't use the * command right now, rather than claiming that the * command is completely invalid */ if (specialTopicHistory.checkHistory(toks)) { /* the special command is not currently available */ targetActor.notifyParseFailure( issuingActor, &specialTopicInactive, []); } else { /* tell the issuer we didn't understand the command */ targetActor.notifyParseFailure( issuingActor, &commandNotUnderstood, []); } /* * we're done with this command, and we want to abort * any subsequent commands on the command line */ return; } /* show the matches if we're in debug mode */ dbgShowGrammarList(lst); /* * Perform a tentative resolution on each alternative * structural interpretation of the command, and rank the * interpretations in order of "goodness" as determined by * our ranking criteria encoded in CommandRanking. * * Note that we perform the tentative resolution and ranking * even if we have only one interpretation, because the * tentative resolution is often useful for the final * resolution pass. */ rankings = CommandRanking .sortByRanking(lst, issuingActor, targetActor); /* * Take the interpretation that came up best in the rankings * (or, if we have multiple at the same ranking, arbitrarily * pick the one that happened to come up first in the list) * - they're ranked in descending order of goodness, so take * the first one. */ match = rankings[1].match; /* if we're in debug mode, show the winner */ dbgShowGrammarWithCaption('Winner', match); /* * Get the token list for the rest of the command after what * we've parsed so far. * * Note that we'll start over and parse these tokens anew, * even though we might have parsed them already into a * subcommand on the previous iteration. Even if we already * parsed these tokens, we want to parse them again, because * we did not have a suitable context for evaluating the * semantic strengths of the possible structural * interpretations this command on the previous iteration; * for example, it was not possible to resolve noun phrases * in this command because we could not guess what the scope * would be when we got to this point. So, we'll simply * discard the previous match tree, and start over, treating * the new tokens as a brand new command. */ nextIdx = match.getNextCommandIndex(); nextCommandTokens = toks.sublist(nextIdx); /* if the pending command list is empty, make it nil */ if (nextCommandTokens.length() == 0) nextCommandTokens = nil; /* * Get the part of the token list that the match doesn't use * at all. These are the tokens following the tokens we * matched. */ extraIdx = match.tokenList.length() + 1; extraTokens = toks.sublist(extraIdx); /* * We now have the best match for the command tree. * * If the command has an actor clause, resolve the actor, so * that we can direct the command to that actor. If the * command has no actor clause, the command is to the actor * who issued the command. * * Do NOT process the actor clause if we've already done so. * If we edit the token list and retry the command after * this point, there is no need to resolve the actor part * again. Doing so could be bad - if resolving the actor * required player interaction, such as asking for help with * an ambiguous noun phrase, we do not want to go through * the same interaction again just because we have to edit * and retry a later part of the command. */ if (match.hasTargetActor()) { local actorResults; /* * If we haven't yet explicitly specified a target * actor, and the default target actor is different from * the issuing actor, then this is really a new command * from the issuing actor. * * First, an actor change mid-command is allowed only if * the issuing actor waits for NPC commands to be * carried out; if not, then we can't change the actor * mid-command. * * Second, since the command comes from the issuing * actor, not from the default target actor, we want to * issue the orders on the issuing actor's turn, so put * the command into the queue for the issuer, and let * the issuer re-parse the command on the issuer's next * turn. */ if (!actorSpecified && issuingActor != targetActor) { /* * don't allow the command if the issuing actor * doesn't wait for orders to be completed */ if (!issuingActor.issueCommandsSynchronously) { /* turn off any sense capturing */ senseContext.setSenseContext(nil, sight); /* show the error */ issuingActor.getParserMessageObj() .cannotChangeActor(); /* done */ return; } /* put the command into the issuer's queue */ issuingActor.addFirstPendingCommand( firstInSentence, issuingActor, toks); /* done */ return; } /* create an actor-specialized results object */ actorResults = new ActorResolveResults(); /* set up the actors in the results object */ actorResults.setActors(targetActor, issuingActor); /* resolve the actor object */ match.resolveNouns(issuingActor, targetActor, actorResults); /* get the target actor from the command */ targetActor = match.getTargetActor(); /* pull out the phrase specifying the actor */ actorPhrase = match.getActorPhrase(); /* * Copy antecedents from the issuing actor to the target * actor. Since the issuer is giving us a new command * here, pronouns will be given from the issuer's * perspective. */ targetActor.copyPronounAntecedentsFrom(issuingActor); /* let the actor phrase know we're actually using it */ match.execActorPhrase(issuingActor); /* * Ask the target actor if it's interested in the * command at all. This only applies when the actor was * actually specified - if an actor wasn't specified, * the command is either directed to the issuer itself, * in which case the command will always be accepted; or * the command is a continuation of a command line * previously accepted. */ if (!targetActor.acceptCommand(issuingActor)) { /* * the command was immediately rejected - abandon * the command and any subsequent commands on the * same line */ return; } /* note that an actor was specified */ actorSpecified = true; /* * Pull out the rest of the command (other than the * target actor specification) and start over with a * fresh parse of the whole thing. We must do this * because our tentative resolution pass that we used to * pick the best structural interpretation of the * command couldn't go far enough - since we didn't know * the actor involved, we weren't able to resolve nouns * in the rest of the command. Now that we know the * actor, we can start over and resolve everything in * the rest of the command, and thus choose the right * structural match for the command. */ toks = match.getCommandTokens(); /* what follows obviously isn't first in the sentence */ firstInSentence = nil; /* go back to parse the rest */ continue parseTokenLoop; } /* pull out the first action from the command */ action = match.resolveFirstAction(issuingActor, targetActor); /* * If the winning interpretation had any unknown words, run * a resolution pass to resolve those interactively, if * possible. We want to do this before doing any other * interactive resolution because OOPS has the unique * property of forcing us to reparse the command; if we * allowed any other interactive resolution to happen before * processing an OOPS, we'd likely have to repeat the other * resolution on the reparse, which would confuse and * irritate users by asking the same question more than once * for what is apparently the same command. */ if (rankings[1].unknownWordCount != 0) { /* * resolve using the OOPS results gatherer - this runs * essentially the same preliminary resolution process * as the ranking results gatherer, but does perform * interactive resolution of unknown words via OOPS */ match.resolveNouns( issuingActor, targetActor, new OopsResults(issuingActor, targetActor)); } /* * If the command is directed to a different actor than the * issuer, change to the target actor's sense context. * * On the other hand, if this is a conversational command * (e.g., BOB, YES or BOB, GOODBYE), execute it within the * sense context of the issuer, even when a target is * specified. Target actors in conversational commands * designate the interlocutor rather than the performing * actor: BOB, YES doesn't ask Bob to say "yes", but rather * means that the player character (the issuer) is saying * "yes" to Bob. */ if (action != nil && action.isConversational(issuingActor)) senseContext.setSenseContext(issuingActor, sight); else if (actorSpecified && targetActor != issuingActor) senseContext.setSenseContext(targetActor, sight); /* set up a transcript to receive the command results */ withCommandTranscript(CommandTranscript, function() { /* * Execute the action. * * If a target actor was specified, and it's not the same * as the issuing actor, this counts as a turn for the * issuing actor. */ executeAction(targetActor, actorPhrase, issuingActor, actorSpecified && issuingActor != targetActor, action); }); /* * If we have anything remaining on the command line, insert * the remaining commands at the start of the target actor's * command queue, since we want the target actor to continue * with the rest of this command line as its next operation. * * Since the remainder of the line represents part of the * work the actor pulled out of the queue in order to call * us, put the remainder back in at the START of the actor's * queue - it was before anything else that might be in the * actor's queue before, so it should stay ahead of anything * else now. */ if (nextCommandTokens != nil) { /* * Prepend the remaining commands to the actor's queue. * The next command is the start of a new sentence if * the command we just executed ends the sentence. */ targetActor.addFirstPendingCommand( match.isEndOfSentence(), issuingActor, nextCommandTokens); } /* * If the command was directed from the issuer to a * different target actor, and the issuer wants to wait for * the full set of issued commands to complete before * getting another turn, tell the issuer to begin waiting. */ if (actorSpecified && issuingActor != targetActor) issuingActor.waitForIssuedCommand(targetActor); /* we're done */ return; } catch (ParseFailureException rfExc) { /* * Parsing failed in such a way that we cannot proceed. * Tell the target actor to notify the issuing actor. */ rfExc.notifyActor(targetActor, issuingActor); /* * the command cannot proceed, so abandon any remaining * tokens on the command line */ return; } catch (CancelCommandLineException cclExc) { /* * if there are any tokens remaining, the game might want to * show an explanation */ if (nextCommandTokens != nil) targetActor.getParserMessageObj().explainCancelCommandLine(); /* stop now, abandoning the rest of the command line */ return; } catch (TerminateCommandException tcExc) { /* * the command cannot proceed - we can't do any more * processing of this command, so simply return, abandoning * any additional tokens we have */ return; } catch (RetryCommandTokensException rctExc) { /* * We want to replace the current command's token list with * the new token list - get the new list, and then go back * and start over processing it. * * Note that we must retain any tokens beyond those that the * match tree used. The exception only edits the current * match tree's matched tokens, since it doesn't have access * to any of the original tokens beyond those, so we must * now add back in any tokens beyond the originals. */ toks = rctExc.newTokens_ + extraTokens; /* go back and process the command again with the new tokens */ continue parseTokenLoop; } catch (ReplacementCommandStringException rcsExc) { local str; /* retrieve the new command string from the exception */ str = rcsExc.newCommand_; /* * if the command string is nil, it means that the command * has been fully handled already, so we simply return * without any further work */ if (str == nil) return; /* * Replace the entire command string with the one from the * exception - this cancels any previous command that we * had. */ toks = cmdTokenizer.tokenize(str); /* * we have a brand new command line, so we're starting a * brand new sentence */ firstInSentence = true; /* set the issuing and target actor according to the exception */ issuingActor = rcsExc.issuingActor_; targetActor = rcsExc.targetActor_; /* * Put this work into the target actor's work queue, so that * the issuer will carry out the command at the next * opportunity. This is a brand new command line, so it * starts a new sentence. */ targetActor.addPendingCommand(true, issuingActor, toks); /* we're done processing this command */ return; } } } /* ------------------------------------------------------------------------ */ /* * GlobalRemapping makes it possible to transform one action into another * globally - as opposed to the remapTo mechanism, which lets an object * involved in the command perform a remapping. The key difference * between global remappings and remapTo is that the latter can't happen * until after the objects are resolved (for fairly obvious reasons: each * remapTo mapping is associated with an object, so you can't know which * mapping to apply until you know which object is involved). In * contrast, global remappings are performed *before* object resolution - * this is possible because the mappings don't depend on the objects * involved in the action. * * Whenever an action is about to be executed, the parser runs through * all of the defined global remappings, and gives each one a chance to * remap the command. If any remapping succeeds, we replace the original * command with the remapped version, then repeat the scan of the global * remapping list from the start - we do another complete scan of the * list in case there's another global mapping that applies to the * remapped version of the command. We repeat this process until we make * it through the whole list without finding a remapping. * * GlobalRemapping instances are added to the master list of mappings * automatically at pre-init time, and any time you construct one * dynamically with 'new'. */ class GlobalRemapping: PreinitObject /* * Check for and apply a remapping. This method must be implemented * in each GlobalRemapping instance to perform the actual remapping * work. * * This routine should first check to see if the command is relevant * to this remapping. In most cases, this means checking that the * command matches some template, such as having a particular action * (verb) and combination of potential objects. Note that the * objects aren't fully resolved during global remapping - the whole * point of global remapping is to catch certain phrasings before we * get to the noun resolution phase - but the *phrases* involved will * be known, so the range of potential matches is knowable. * * If the routine decides that the action isn't relevant to this * remapping, it should simply return nil. * * If the action decides to remap the action, it must create a new * Action object representing the replacement version of the command. * Then, return a list, [targetActor, action], giving the new target * actor and the new action. You don't have to change the target * actor, of course, but it's included in the result so that you can * change it if you want to. For example, you could use this to * remap a command of the form "X, GIVE ME Y" to "ME, ASK X FOR Y" - * note that the target actor changes from X to ME. */ getRemapping(issuingActor, targetActor, action) { /* * this must be overridden to perform the actual remapping; by * default, simply return nil to indicate that we don't want to * remap this action */ return nil; } /* * Remapping order - the parser applies global remappings in * ascending order of this value. In most cases, the order shouldn't * matter, since most remappings should be narrow enough that a given * command will only be subject to one remapping rule. However, in * some cases you might need to define rules that overlap, so the * ordering lets you specify which one goes first. In most cases * you'll want to apply the more specific rule first. */ remappingOrder = 100 /* * Static class method: look for a remapping. This runs through the * master list of mappings, looking for a mapping that applies to the * given command. If we find one, we'll replace the command with the * remapped version, then start over with a fresh scan of the entire * list to see if there's a remapping for the *new* version of the * command. We repeat this until we get through the whole list * without finding a remapping. * * The return value is a list, [targetActor, action], giving the * resulting target actor and new action object. If we don't find * any remapping, this will simply be the original values passed in * as our arguments; if we do find a remapping, this will be the new * version of the command. */ findGlobalRemapping(issuingActor, targetActor, action) { /* get the global remapping list */ local v = GlobalRemapping.allGlobalRemappings; local cnt = v.length(); /* if necessary, sort the list */ if (GlobalRemapping.listNeedsSorting) { /* sort it by ascending remappingOrder value */ v.sort(SortAsc, {a, b: a.remappingOrder - b.remappingOrder}); /* note that it's now sorted */ GlobalRemapping.listNeedsSorting = nil; } /* * iterate through the list repeatedly, until we make it all the * way through without finding a mapping */ for (local done = nil ; !done ; ) { /* presume we won't find a remapping on this iteration */ done = true; /* run through the list, looking for a remapping */ for (local i = 1 ; i <= cnt ; ++i) { local rm; /* check for a remapping */ rm = v[i].getRemapping(issuingActor, targetActor, action); if (rm != nil) { /* found a remapping - apply it */ targetActor = rm[1]; action = rm[2]; /* * we found a remapping, so we have to repeat the * scan of the whole list - note that we're not done, * and break out of the current scan so that we start * over with a fresh scan */ done = nil; break; } } } /* return the final version of the command */ return [targetActor, action]; } /* pre-initialization: add each instance to the master list */ execute() { /* add me to the master list */ registerGlobalRemapping(); } /* construction: add myself to the master list */ construct() { /* add me to the master list */ registerGlobalRemapping(); } /* register myself with the global list, making this an active mapping */ registerGlobalRemapping() { /* add myself to the global list */ GlobalRemapping.allGlobalRemappings.append(self); /* note that a sort is required the next time we run */ GlobalRemapping.listNeedsSorting = true; } /* * unregister - this removes me from the global list, making this * mapping inactive: after being unregistered, the parser won't apply * this mapping to new commands */ unregisterGlobalRemapping() { GlobalRemapping.allGlobalRemappings.removeElement(self); } /* * Static class property: the master list of remappings. We build * this automatically at preinit time, and manipulate it via our * constructor. */ allGlobalRemappings = static new Vector(10) /* * static class property: the master list needs to be sorted; this is * set to true each time we update the list, so that the list scanner * knows to sort it before doing its scan */ listNeedsSorting = nil ; /* ------------------------------------------------------------------------ */ /* * Execute an action, as specified by an Action object. We'll resolve * the nouns in the action, then perform the action. */ executeAction(targetActor, targetActorPhrase, issuingActor, countsAsIssuerTurn, action) { local rm, results; startOver: /* check for a global remapping */ rm = GlobalRemapping.findGlobalRemapping( issuingActor, targetActor, action); targetActor = rm[1]; action = rm[2]; /* create a basic results object to handle the resolution */ results = new BasicResolveResults(); /* set up the actors in the results object */ results.setActors(targetActor, issuingActor); /* catch any "remap" signals while resolving noun phrases */ try { /* resolve noun phrases */ action.resolveNouns(issuingActor, targetActor, results); } catch (RemapActionSignal sig) { /* mark the new action as remapped */ sig.action_.setRemapped(action); /* get the new action from the signal */ action = sig.action_; /* start over with the new action */ goto startOver; } /* * Check to see if we should create an undo savepoint for the * command. If the action is not marked for inclusion in the undo * log, there is no need to log a savepoint for it. * * Don't save undo for nested commands. A nested command is part of * a main command, and we only want to save undo for the main * command, not for its individual sub-commands. */ if (action.includeInUndo && action.parentAction == nil && (targetActor.isPlayerChar() || (issuingActor.isPlayerChar() && countsAsIssuerTurn))) { /* * Remember the command we're about to perform, so that if we * undo to here we'll be able to report what we undid. Note that * we do this *before* setting the savepoint, because we want * after the undo to know the command we were about to issue at * the savepoint. */ libGlobal.lastCommandForUndo = action.getOrigText(); libGlobal.lastActorForUndo = (targetActorPhrase == nil ? nil : targetActorPhrase.getOrigText()); /* * set a savepoint here, so that we on 'undo' we'll restore * conditions to what they were just before we executed this * command */ savepoint(); } /* * If this counts as a turn for the issuer, adjust the issuer's busy * time. * * However, this doesn't apply if the command is conversational (that * is, it's something like "BOB, HELLO"). A conversational command * is conceptually carried out by the issuer, not the target actor, * since the action consists of the issuer actually saying something * to the target actor. The normal turn accounting in Action will * count a conversational command this way, so we don't have to do * the extra bookkeeping for such a command here. */ if (countsAsIssuerTurn && !action.isConversational(issuingActor)) { /* * note in the issuer that the target is the most recent * conversational partner */ issuingActor.lastInterlocutor = targetActor; /* make the issuer busy for the order-giving interval */ issuingActor.addBusyTime(nil, issuingActor.orderingTime(targetActor)); /* notify the target that this will be a non-idle turn */ targetActor.nonIdleTurn(); } /* * If the issuer is directing the command to a different actor, and * it's not a conversational command, check with the target actor to * see if it wants to accept the command. Don't check * conversational commands, since these aren't of the nature of * orders to be obeyed. */ if (issuingActor != targetActor && !action.isConversational(issuingActor) && !targetActor.obeyCommand(issuingActor, action)) { /* * if the issuing actor's "ordering time" is zero, make this take * up a turn anyway, just for the refusal */ if (issuingActor.orderingTime(targetActor) == 0) issuingActor.addBusyTime(nil, 1); /* * Since we're aborting the command, we won't get into the normal * execution for it. However, we might still want to save it for * an attempted re-issue with AGAIN, so do so explicitly now. */ action.saveActionForAgain(issuingActor, countsAsIssuerTurn, targetActor, targetActorPhrase); /* * This command was rejected, so don't process it any further, * and give up on processing any remaining commands on the same * command line. */ throw new TerminateCommandException(); } /* execute the action */ action.doAction(issuingActor, targetActor, targetActorPhrase, countsAsIssuerTurn); } /* ------------------------------------------------------------------------ */ /* * Try an implicit action. * * Returns true if the action was attempted, whether or not it * succeeded, nil if the command was not even attempted. We will not * attempt an implied command that verifies as "dangerous," since this * means that it should be obvious to the player character that such a * command should not be performed lightly. */ _tryImplicitAction(issuingActor, targetActor, msgProp, actionClass, [objs]) { local action; /* create an instance of the desired action class */ action = actionClass.createActionInstance(); /* mark the action as implicit */ action.setImplicit(msgProp); /* install the resolved objects in the action */ action.setResolvedObjects(objs...); /* * For an implicit action, we must check the objects involved to make * sure they're in scope. If any of the objects aren't in scope, * there is no way the actor would know to perform the command, so * the command would not be implied in the first place. Simply fail * without trying the command. */ if (!action.resolvedObjectsInScope()) return nil; /* * catch the abort-implicit signal, so we can turn it into a result * code for our caller instead of an exception */ try { /* in NPC mode, add a command separator before each implied action */ if (targetActor.impliedCommandMode() == ModeNPC) gTranscript.addCommandSep(); /* execute the action */ action.doAction(issuingActor, targetActor, nil, nil); /* * if the actor is in "NPC" mode for implied commands, do some * extra work */ if (targetActor.impliedCommandMode() == ModeNPC) { /* * we're in NPC mode, so if the implied action failed, then * act as though the command had never been attempted */ if (gTranscript.actionFailed(action)) { /* the implied command failed - act like we didn't even try */ return nil; } /* * In "NPC" mode, we display the results from implied * commands as though they had been explicitly entered as * separate actions. So, add visual separation after the * results from the implied command. */ gTranscript.addCommandSep(); } /* tell the caller we at least tried to execute the command */ return true; } catch (AbortImplicitSignal sig) { /* tell the caller we didn't execute the command at all */ return nil; } catch (ParseFailureException exc) { /* * Parse failure. If the actor is in NPC mode, we can't have * asked for a new command, so this must be some failure in * processing the implied command itself; most likely, we tried * to resolve a missing object or the like and found that we * couldn't perform interactive resolution (because of the NPC * mode). In this case, simply treat this as a failure of the * implied command itself, and act as though we didn't even try * the implied command. * * If the actor is in player mode, then we *can* perform * interactive resolution, so we won't have thrown a parser * failure before trying to solve the problem interactively. The * failure must therefore be in an interactive response. In this * case, simply re-throw the failure so that it reaches the main * parser. */ if (targetActor.impliedCommandMode() == ModeNPC) { /* NPC mode - the implied command itself failed */ return nil; } else { /* player mode - interactive resolution failed */ throw exc; } } } /* ------------------------------------------------------------------------ */ /* * Run a replacement action. */ _replaceAction(actor, actionClass, [objs]) { /* run the replacement action as a nested action */ _nestedAction(true, actor, actionClass, objs...); /* the invoking command is done */ exit; } /* ------------------------------------------------------------------------ */ /* * Resolve and execute a replacement action. This differs from the * normal replacement action execution in that the action we execute * requires resolution before execution. */ resolveAndReplaceAction(newAction) { /* prepare the replacement action */ prepareNestedAction(true, nil, newAction); /* * resolve and execute the new action, using the same target and * issuing actors as the original action */ executeAction(gActor, nil, gIssuingActor, nil, newAction); /* the invoking command has been replaced, so it's done */ exit; } /* ------------------------------------------------------------------------ */ /* * Run an action as a new turn. Returns the CommandTranscript describing * the action's results. */ _newAction(transcriptClass, issuingActor, targetActor, actionClass, [objs]) { local action; /* create an instance of the desired action class */ action = actionClass.createActionInstance(); /* execute the command with the action instance */ return newActionObj(transcriptClass, issuingActor, targetActor, action, objs...); } /* * Run an action as a new turn. This is almost the same as _newAction, * but should be used when the caller has already explicitly created an * instance of the Action to be performed. * * If issuingActor is nil, we'll use the current global issuing actor; if * that's also nil, we'll use the target actor. * * Returns a CommandTranscript object describing the result of the * action. */ newActionObj(transcriptClass, issuingActor, targetActor, actionObj, [objs]) { /* create the results object and install it as the global transcript */ return withCommandTranscript(transcriptClass, function() { /* install the resolved objects in the action */ actionObj.setResolvedObjects(objs...); /* * if the issuing actor isn't specified, use the current global * issuing actor; if that's also not set, use the target actor */ if (issuingActor == nil) issuingActor = gIssuingActor; if (issuingActor == nil) issuingActor = targetActor; /* * Execute the given action. Because this is a new action, * execute the action in a new sense context for the given actor. */ callWithSenseContext(targetActor.isPlayerChar() ? nil : targetActor, sight, {: actionObj.doAction(issuingActor, targetActor, nil, nil)}); /* return the current global transcript object */ return gTranscript; }); } /* ------------------------------------------------------------------------ */ /* * Run a nested action. 'isReplacement' has the same meaning as in * execNestedAction(). */ _nestedAction(isReplacement, actor, actionClass, [objs]) { local action; /* create an instance of the desired action class */ action = actionClass.createActionInstance(); /* install the resolved objects in the action */ action.setResolvedObjects(objs...); /* execute the new action */ execNestedAction(isReplacement, nil, actor, action); } /* * Execute a fully-constructed nested action. * * 'isReplacement' indicates whether the action is a full replacement or * an ordinary nested action. If it's a replacement, then we use the * game time taken by the replacement, and set the enclosing action * (i.e., the current gAction) to take zero time. If it's an ordinary * nested action, then we consider the nested action to take zero time, * using the current action's time as the overall command time. * * 'isRemapping' indicates whether or not this is a remapped action. If * we're remapping from one action to another, this will be true; for * any other kind of nested or replacement action, this should be nil. */ execNestedAction(isReplacement, isRemapping, actor, action) { /* prepare the nested action */ prepareNestedAction(isReplacement, isRemapping, action); /* execute the new action in the actor's sense context */ callWithSenseContext( actor.isPlayerChar() ? nil : actor, sight, {: action.doAction(gIssuingActor, actor, nil, nil) }); } /* * Prepare a nested or replacement action for execution. */ prepareNestedAction(isReplacement, isRemapping, action) { /* * if the original action is an implicit command, make the new * command implicit as well */ if (gAction.isImplicit) { /* * make the new action implicit, but don't describe it as a * separate implicit command - it's effectively part of the * original implicit command */ action.setImplicit(nil); } /* mark the new action as nested */ action.setNested(); /* a nested action is part of the enclosing action */ action.setOriginalAction(gAction); /* if this is a remapping, mark it as such */ if (isRemapping) action.setRemapped(gAction); /* * Set either the nested action's time or the enclosing (current) * action's time to zero - we want to count only the time of one * command or the other. * * If we're running an ordinary nested command, set the nested * command's time to zero, since we want to consider it just a part * of the enclosing command and thus to take no time of its own. * * If we're running a full replacement command, and we're replacing * something other than an implied command, don't consider the * enclosing command to take any time, since the enclosing command is * carrying out its entire function via the replacement and thus * requires no time of its own. If we're replacing an implied * command, this doesn't apply, since the implied command defers to * its enclosing command for timing. If we're replacing a command * that already has zero action time, this also doesn't apply, since * we're presumably replacing a command that's itself nested. */ if (isReplacement && !gAction.isImplicit && gAction.actionTime != 0) gAction.zeroActionTime(); else action.actionTime = 0; } /* ------------------------------------------------------------------------ */ /* * Run a previously-executed command as a nested action, re-resolving * all of its objects to ensure they are still valid. */ nestedActionAgain(action) { /* reset the any cached information for the new command context */ action.resetAction(); /* mark the action as nested */ action.setNested(); action.setOriginalAction(gAction); /* * do not count any time for the nested action, since it's merely * part of the main turn and doesn't count as a separate turn of its * own */ action.actionTime = 0; /* execute the command */ executeAction(gActor, nil, gIssuingActor, nil, action); } /* ------------------------------------------------------------------------ */ /* * Run some code in a simulated Action environment. We'll create a dummy * instance of the given Action class, and set up a command transcript, * then invoke the function. This is useful for writing daemon code that * needs to invoke other code that's set up to expect a normal action * processing environment. */ withActionEnv(actionClass, actor, func) { local oldAction, oldActor; /* remember the old globals */ oldAction = gAction; oldActor = gActor; try { /* set up a dummy action */ gAction = actionClass.createInstance(); /* use the player character as the actor */ gActor = actor; /* * execute the function with a command transcript active; obtain * and return the return value of the function */ return withCommandTranscript(CommandTranscript, func); } finally { /* restore globals on the way out */ gAction = oldAction; gActor = oldActor; } } /* ------------------------------------------------------------------------ */ /* * Exit signal. This signal indicates that we're finished with the * entire command execution sequence for an action; the remainder of the * command execution sequence is to be skipped for the action. Throw * this from within the command execution sequence in order to skip * directly to the end-of-turn processing. This skips everything * remaining in the action, including after-action notification and the * like. This signal skips directly past the 'afterAction' phase of the * command. * * Note that this doesn't prevent further processing of the same command * if there are multiple objects involved, and it doesn't affect * processing of additional commands on the same command line. If you * want to cancel further iteration of the same command for additional * objects, call gAction.cancelIteration(). */ class ExitSignal: Exception ; /* * Exit Action signal. This signal indicates that we're finished with * the execAction portion of processing the command, but we still want * to proceed with the rest of the command as normal. This can be used * when a step in the action processing wants to preempt any of the more * default processing that would normally follow. This skips directly * to the 'afterAction' phase of the command. * * Note that this doesn't prevent further processing of the same command * if there are multiple objects involved, and it doesn't affect * processing of additional commands on the same command line. If you * want to cancel further iteration of the same command for additional * objects, call gAction.cancelIteration(). */ class ExitActionSignal: Exception ; /* * Abort implicit command signal. This exception indicates that we are * aborting an implicit command without having tried to execute the * command at all. This is thrown when an implied command is to be * aborted before it's even attempted, such as when verification shows * the command is obviously dangerous and thus should never be attempted * without the player having explicitly requesting it. */ class AbortImplicitSignal: Exception ; /* ------------------------------------------------------------------------ */ /* * Action Remap signal. This signal can be thrown only during the noun * phrase resolution phase of execution, and indicates that we want to * remap the action to a different action, specified in the signal. * * This is useful when an object is always used in a special way, so * that a generic verb used with the object must be mapped to a more * specific verb on the object. For example, a game with a generic USE * verb might convert USE PAINTBRUSH ON WALL to PAINT WALL WITH * PAINTBRUSH by remapping the UseWith action to a PaintWith action * instead. */ class RemapActionSignal: Exception construct(action) { /* remember the new action */ action_ = action; } /* the new action that should replace the original action */ action_ = nil ; /* * Remap a 'verify' method for a remapped action. This is normally * invoked through the remapTo() macro. */ remapVerify(oldRole, resultSoFar, remapInfo) { local newAction; local objs; local idx; local newRole; /* extract new action's object list from the remapping info list */ objs = remapInfo.sublist(2); /* * Create a new action object. We only perform verification * remapping during the resolution phase of the command processing, * because once we've finished resolving, we'll actually replace the * action with the remapped action and thus won't have to remap * verification (or anything else) at that point. So, pass true for * the in-resolve flag to the action creation routine. */ newAction = remapActionCreate(true, oldRole, remapInfo); /* * Find the object that's given as a resolved object, rather than as * a DirectObject (etc) identifier - the one given as a specific * object is the one that corresponds to the original object. */ idx = objs.indexWhich({x: dataType(x) == TypeObject}); /* get the role identifier (DirectObject, etc) for the slot position */ newRole = newAction.getRoleFromIndex(idx); /* if we don't yet have a result list object, create one */ if (resultSoFar == nil) resultSoFar = new VerifyResultList(); /* if we found a remapping, verify it */ if (idx != nil) { /* * Remember the remapped object in the result list. Note that we * do this first, before calling the remapped verification * property, so that our call to the remapped verification * property will overwrite this setting if it does further * remapping. We want the ultimate target object represented * here, after all remappings are finished. */ resultSoFar.remapAction_ = newAction; resultSoFar.remapTarget_ = objs[idx]; resultSoFar.remapRole_ = newRole; /* install the new action as the current action while verifying */ local oldAction = gAction; gAction = newAction; try { /* call verification on the new object in the new role */ return newAction.callVerifyProp( objs[idx], newAction.getVerifyPropForRole(newRole), newAction.getPreCondPropForRole(newRole), newAction.getRemapPropForRole(newRole), resultSoFar, newRole); } finally { /* restore the old gAction on the way out */ gAction = oldAction; } } else { /* there's no remapping, so there's nothing to verify */ return resultSoFar; } } /* * Perform a remapping to a new action. This is normally invoked * through the remapTo() macro. */ remapAction(inResolve, oldRole, remapInfo) { local newAction; /* get the new action */ newAction = remapActionCreate(inResolve, oldRole, remapInfo); /* * replace the current action, using the appropriate mechanism * depending on the current processing phase */ if (inResolve) { /* * we're still resolving the objects, so we must use a signal to * start the resolution process over for the new action */ throw new RemapActionSignal(newAction); } else { /* * We've finished resolving everything, so we can simply use the * new action as a replacement action. */ execNestedAction(true, true, gActor, newAction); /* * the remapped action replaces the original action, so * terminate the original action */ exit; } } /* * Create a new action object for the given remapped action. */ remapActionCreate(inResolve, oldRole, remapInfo) { local newAction; local newObjs; local newActionClass; local objs; /* get the new action class and object list from the remap info */ newActionClass = remapInfo[1]; objs = remapInfo.sublist(2); /* * create a new instance of the replacement action, carrying forward * the properties of the original (current) action */ newAction = newActionClass.createActionFrom(gAction); /* remember the original action we're remapping */ newAction.setOriginalAction(gAction); /* set up an empty vector for the match trees for the new action */ newObjs = new Vector(objs.length()); /* remap according to the phase of the execution */ if (inResolve) { /* translate the object mappings */ foreach (local cur in objs) { /* check what we have to translate */ if (dataType(cur) == TypeEnum) { /* * it's an object role - if it's the special OtherObject * designator, get the other role of a two-object * command */ if (cur == OtherObject) cur = gAction.getOtherObjectRole(oldRole); /* * get the match tree for this role from the old action * and add it to our list */ newObjs.append(gAction.getMatchForRole(cur)); } else { /* append the new ResolveInfo to the new object list */ newObjs.append(gAction.getResolveInfo(cur, oldRole)); } } /* set the object matches in the new action */ newAction.setObjectMatches(newObjs.toList()...); } else { /* translate the object mappings */ foreach (local cur in objs) { /* check what we have to translate */ if (dataType(cur) != TypeEnum) { /* it's an explicit object - use it directly */ newObjs.append(cur); } else { /* it's a role - translate OtherObject if needed */ if (cur == OtherObject) cur = gAction.getOtherObjectRole(oldRole); /* get the resolved object for this role */ newObjs.append(gAction.getObjectForRole(cur)); } } /* set the resolved objects in the new action */ newAction.setResolvedObjects(newObjs.toList()...); } /* return the new action */ return newAction; } /* ------------------------------------------------------------------------ */ /* * Result message object. This is used for verification results and * main command reports, which must keep track of messages to display. */ class MessageResult: object /* * Construct given literal message text, or alternatively a property * of the current actor's verb messages object. In either case, * we'll expand the message immediately to allow the message to be * displayed later with any parameters fixed at the time the message * is constructed. */ construct(msg, [params]) { /* if we're based on an existing object, copy its characteristics */ if (dataType(msg) == TypeObject && msg.ofKind(MessageResult)) { /* base it on the existing object */ messageText_ = msg.messageText_; messageProp_ = msg.messageProp_; return; } /* * if the message was given as a property, remember the property * for identification purposes */ if (dataType(msg) == TypeProp) messageProp_ = msg; /* * Resolve the message and store the text. Use the action's * objects (the direct object, indirect object, etc) as the * sources for message overrides - this makes it easy to override * messages on a per-object basis without having to rewrite the * whole verify/check/action routines. */ messageText_ = resolveMessageText(gAction.getCurrentObjects(), msg, params); } /* * Static method: resolve a message. If the message is given as a * property, we'll look up the message in the given source objects * and in the actor's "action messages" object. We'll return the * resolved message string. */ resolveMessageText(sources, msg, params) { /* * If we have more than one source object, it means that the * command has more than one object slot (such as a TIAction, * which has direct and indirect objects). Rearrange the list so * that the nearest caller is the first object in the list. If * one of these source objects provides an override, we generally * want to get the message from the immediate caller rather than * the other object. Note that we only care about the *first* * source object we find in the stack trace, because we only care * about the actual message generator call; enclosing calls * aren't relevant to the message priority because they don't * necessarily have anything to do with the messaging. */ if (sources.length() > 1) { /* look through the stack trace for a 'self' in the source list */ local tr = t3GetStackTrace(); for (local i = 1, local trCnt = tr.length() ; i <= trCnt ; ++i) { /* check this 'self' */ local s = tr[i].self_; local sIdx = sources.indexOf(s); if (sIdx != nil) { /* * it's a match - move this object to the head of the * list so that we give its message bindings priority */ if (sIdx != 1) sources = [s] + (sources - s); /* no need to look any further */ break; } } } /* * The message can be given either as a string or as a property * of the actor's verb message object. If it's the latter, look * up the text of the property from the appropriate object. */ findTextSource: if (dataType(msg) == TypeProp) { local msgObj; /* * Presume that we'll read the message from the current * actor's "action message object." This is typically * playerActionMessages or npcActionMessages, but it's up to * the actor to specify which object we get our messages * from. */ msgObj = gActor.getActionMessageObj(); /* * First, look up the message property in the action's * objects (the direct object, indirect object, etc). This * makes it easy to override messages on a per-object basis * without having to rewrite the whole verify/check/action * routine. */ foreach (local cur in sources) { /* check to see if this object defines the message property */ if (cur != nil && cur.propDefined(msg)) { local res; /* * This object defines the property, so check what * we have. */ switch (cur.propType(msg)) { case TypeProp: /* * It's another property, so we're being * directed back to the player action message * object. The object does override the * message, but the override points to another * message property in the action object message * set. Simply redirect 'msg' to point to the * new property, and use the same action message * object we already assumed we'd use. */ msg = cur.(msg); break; case TypeSString: /* it's a simple string - retrieve it */ msg = cur.(msg); /* * since it's just a string, we're done finding * the message text - there's no need to do any * further property lookup, since we've obviously * reached the end of that particular line */ break findTextSource; case TypeCode: /* * Check the parameter count - we'll allow this * method to take the full set of parameters, or * no parameters at all. We allow the no-param * case for convenience in cases where the method * simply wants to return a string or property ID * from a short method that doesn't need to know * the parameters; in these cases, it's * syntactically a lot nicer looking to write it * as a "prop = (expresion)" than to write the * full method-with-params syntax. */ if (cur.getPropParams(msg) == [0, 0, nil]) res = cur.(msg); else res = cur.(msg)(params...); /* * If that returned nil, ignore it entirely and * keep scanning the remaining source objects. * The object must have decided it didn't want to * provide the message override in this case * after all. */ if (res == nil) continue; /* we didn't get nil, so use the result */ msg = res; /* * if we got a string, we've fully resolved the * message text, so we can stop searching for it */ if (dataType(msg) == TypeSString) break findTextSource; /* * It's not nil and it's not a string, so it must * be a property ID. In this case, the property * ID is a property to evaluate in the normal * action message object. Simply proceed to * evaluate the new message property as normal. */ break; case TypeNil: /* * it's explicitly nil, which simply means to * ignore this definition; keep scanning other * source objects */ continue; default: /* * In any other case, this must simply be the * message we're to use. For this case, the * source of the message is this object, so * forget about the normal action message object * and instead use the current object. Then * proceed to evaluate the message property as * normal, which will fetch it from the current * object. */ msgObj = cur; break; } /* * we found a definition, so we don't need to look * at any of the other objects involved in the * action - we just use the first override we find */ break; } } /* look up the message in the actor's message generator */ msg = msgObj.(msg)(params...); } /* * format the string and remember the result - do the formatting * immediately, because we want to make sure we expand any * substitution parameters in the context of the current * command, since the parameters might change (and thus alter * the meaning of the message) by the time it's displayed */ msg = langMessageBuilder.generateMessage(msg); /* * "quote" the message text - it's fully expanded now, so * there's no need to further expand anything that might by * coincidence look like substitution parameters in its text */ msg = langMessageBuilder.quoteMessage(msg); /* return the resolved message string */ return msg; } /* * set a new message, given the same type of information as we'd use * to construct the object */ setMessage(msg, [params]) { /* simply invoke the constructor to re-fill the message data */ construct(msg, params...); } /* * Display a message describing why the command isn't allowed. */ showMessage() { /* show our message string */ say(messageText_); } /* the text of our result message */ messageText_ = nil /* the message property, if we have one */ messageProp_ = nil ; frobtads-1.2.3/tads3/lib/adv3/lister.t0000644000175000001440000024523711702410572016630 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - Lister class * * This module defines the "Lister" class, which generates formatted * lists of objects, and several subclasses of Lister that generate * special kinds of lists. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Lister. This is the base class for formatting of lists of objects. * * The external interface consists of the showList() method, which * displays a formatted list of objects according to the rules of the * lister subclass. * * The rest of the methods are an internal interface which lister * subclasses can override to customize the way that a list is shown. * Certain of these methods are meant to be overridden by virtually all * listers, such as the methods that show the prefix and suffix * messages. The remaining methods are designed to allow subclasses to * customize detailed aspects of the formatting, so they only need to be * overridden when something other than the default behavior is needed. */ class Lister: object /* * Show a list, showing all items in the list as though they were * fully visible, regardless of their actual sense status. */ showListAll(lst, options, indent) { /* create a sense information table with each item in full view */ local infoTab = new LookupTable(16, 32); foreach (local cur in lst) { /* add a plain view sensory description to the info list */ infoTab[cur] = new SenseInfo(cur, transparent, nil, 3); } /* show the list from the current global point of view */ showList(getPOV(), nil, lst, options, indent, infoTab, nil); } /* * Display a list of items, grouping according to the 'listWith' * associations of the items. We will only list items for which * isListed() returns true. * * 'pov' is the point of view of the listing, which is usually an * actor (and usually the player character actor). * * 'parent' is the parent (container) of the list being shown. This * should be nil if the listed objects are not all within a single * object. * * 'lst' is the list of items to display. * * 'options' gives a set of ListXxx option flags. * * 'indent' gives the indentation level. This is used only for * "tall" lists (specified by including ListTall in the options * flags). An indentation level of zero indicates no indentation. * * 'infoTab' is a lookup table of SenseInfo objects for all of the * objects that can be sensed from the perspective of the actor * performing the action that's causing the listing. This is * normally the table returned from Thing.senseInfoTable() for the * actor from whose point of view the list is being generated. (We * take this as a parameter rather than generating ourselves for two * reasons. First, it's often the case that the same information * table will be needed for a series of listings, so we can save the * compute time of recalculating the same table repeatedly by having * the caller obtain the table and pass it to each lister. Second, * in some cases the caller will want to synthesize a special sense * table rather than using the actual sense information; taking this * as a parameter allows the caller to easily customize the table.) * * 'parentGroup' is the ListGroup object that is showing this list. * We will not group the objects we list into the parent group, or * into any group more general than the parent group. * * This routine is not usually overridden in lister subclasses. * Instead, this method calls a number of other methods that * determine the listing style in more detail; usually those other, * simpler methods are customized in subclasses. */ showList(pov, parent, lst, options, indent, infoTab, parentGroup) { /* remember the original list */ local origLst = lst; /* filter the list to get only the items we actually will list */ lst = getFilteredList(lst, infoTab); /* create a lookup table to keep track of the groups we've seen */ local groupTab = new LookupTable(); local groups = new Vector(10); /* set up a vector to keep track of the singles */ local singles = new Vector(10); /* figure the groupings */ local itemCount = getListGrouping(groupTab, groups, singles, lst, parentGroup); /* * Now that we've figured out what's in the list and how it's * arranged into groups, show the list. */ showArrangedList(pov, parent, lst, options, indent, infoTab, itemCount, singles, groups, groupTab, origLst); /* * If the list is recursive, mention the contents of any items * that weren't listed in the main list, and of any contents * that are specifically to be listed out-of-line. Don't do * this if we're already recursively showing such a listing, * since if we did so we could show items at recursive depths * more than once; if we're already doing a recursive listing, * our caller will itself recurse through all levels of the * tree, so we don't have to recurse any further ourselves. */ if ((options & ListRecurse) != 0 && indent == 0 && (options & ListContents) == 0) { /* show the contents of each object we didn't list */ showSeparateContents(pov, origLst, options | ListContents, infoTab); } } /* * Filter a list to get only the elements we actually want to show. * Returns a new list consisting only of the items that (1) pass the * isListed() test, and (2) are represented in the sense information * table (infoTab). If infoTab is nil, no sense filtering is * applied. */ getFilteredList(lst, infoTab) { /* narrow the list down based on the isListed criteria */ lst = lst.subset({x: isListed(x)}); /* * If we have an infoTab, build a new list consisting only of * the items in 'lst' that have infoTab entries - we can't sense * anything that doesn't have an infoTab entry, so we don't want * to show any such objects. */ if (infoTab != nil) { /* create a vector to contain the new filtered list */ local filteredList = new Vector(lst.length()); /* * run through our original list and confirm that each one * is in the infoTab */ foreach (local cur in lst) { /* * if this item has an infoTab entry, add this item to * the filtered list */ if (infoTab[cur] != nil) filteredList.append(cur); } /* forget the original list, and use the filtered list instead */ lst = filteredList; } /* return the filtered list */ return lst; } /* * Get the groupings for a given listing. * * 'groupTab' is an empty LookupTable, and 'groups' is an empty * Vector; we'll populate these with the grouping information. * 'singles' is an empty Vector that we'll populate with the single * items not part of any group. */ getListGrouping(groupTab, groups, singles, lst, parentGroup) { local cur; local i, cnt; /* * First, scan the list to determine how we're going to group * the objects. */ for (i = 1, cnt = lst.length() ; i <= cnt ; ++i) { local curGroups; local parentIdx; /* get this object into a local for easy reference */ cur = lst[i]; /* if the item isn't part of this listing, skip it */ if (!isListed(cur)) continue; /* get the list of groups with which this object is listed */ curGroups = listWith(cur); /* if there are no groups, we can move on to the next item */ if (curGroups == nil) continue; /* * If we have a parent group, and it appears in the list of * groups for this item, eliminate everything in the item's * group list up to and including the parent group. If * we're showing this list as part of a group to begin with, * we obviously don't want to show this list grouped into * the same group, and we also don't want to group it into * anything broader than the parent group. Groups are * listed from most general to most specific, so we can * eliminate anything up to and including the parent group. */ if (parentGroup != nil && (parentIdx = curGroups.indexOf(parentGroup)) != nil) { /* eliminate everything up to and including the parent */ curGroups = curGroups.sublist(parentIdx + 1); } /* if this item has no groups, skip it */ if (curGroups.length() == 0) continue; /* * This item has one or more group associations that we must * consider. */ foreach (local g in curGroups) { local itemsInGroup; /* find the group table entry for this group */ itemsInGroup = groupTab[g]; /* if there's no entry for this group, create a new one */ if (itemsInGroup == nil) { /* create a new group table entry */ itemsInGroup = groupTab[g] = new Vector(10); /* add it to the group vector */ groups.append(g); } /* * add this item to the list of items that want to be * grouped with this group */ itemsInGroup.append(cur); } } /* * We now have the set of all of the groups that could possibly * be involved in this list display. We must now choose the * single group we'll use to display each grouped object. * * First, eliminate any groups with insufficient membership. * (Most groups require at least two members, but this can vary * by group.) */ for (i = 1, cnt = groups.length() ; i <= cnt ; ++i) { /* if this group has only one member, drop it */ if (groupTab[groups[i]].length() < groups[i].minGroupSize) { /* remove this group from the group list */ groups.removeElementAt(i); /* * adjust the list count, and back up to try the element * newly at this index on the next iteration */ --cnt; --i; } } /* * Next, scan for groups with identical member lists, and for * groups with subset member lists. For each pair of identical * elements we find, eliminate the more general of the two. */ for (i = 1, cnt = groups.length() ; i <= cnt ; ++i) { local g1; local mem1; /* get the current group and its membership list */ g1 = groups[i]; mem1 = groupTab[g1]; /* look for matching items in the list after this one */ for (local j = i + 1 ; j <= cnt ; ++j) { local g2; local mem2; /* get the current item and its membership list */ g2 = groups[j]; mem2 = groupTab[g2]; /* * Compare the membership lists for the two items. Note * that we built these membership lists all in the same * order of objects, so if two membership lists have all * the same members, those members will be in the same * order in the two lists; hence, we can simply compare * the two lists to determine the membership order. */ if (mem1 == mem2) { local ordList; /* * The groups have identical membership, so * eliminate the more general group. Groups are * ordered from most general to least general, so * keep the one with the higher index in the group * list for an object in the membership list. Note * that we assume that each member has the same * ordering for the common groups, so we can pick a * member arbitrarily to find the way a member * orders the groups. */ ordList = listWith(mem1[1]); if (ordList.indexOf(g1) > ordList.indexOf(g2)) { /* * group g1 is more specific than group g2, so * keep g1 and discard g2 - remove the 'j' * element from the list, and back up in the * inner loop so we reconsider the element newly * at this index on the next iteration */ groups.removeElementAt(j); --cnt; --j; } else { /* * group g2 is more specific, so discard g1 - * remove the 'i' element from the list, back up * in the outer loop, and break out of the inner * loop, since the outer loop element is no * longer there for us to consider in comparing * more elements in the inner loop */ groups.removeElementAt(i); --cnt; --i; break; } } } } /* * Scan for subsets. For each group whose membership list is a * subset of another group in our list, eliminate the subset, * keeping only the larger group. The group lister will be able * to show the subgroup as grouped within its larger list. */ for (local i = 1, cnt = groups.length() ; i <= cnt ; ++i) { local g1; local mem1; /* get the current group and its membership list */ g1 = groups[i]; mem1 = groupTab[g1]; /* look at the other elements to see if we have any subsets */ for (local j = 1 ; j <= cnt ; ++j) { local g2; local mem2; /* don't bother checking the same element */ if (j == i) continue; /* get the current item and its membership list */ g2 = groups[j]; mem2 = groupTab[g2]; /* * if g2's membership is a subset, eliminate g2 from the * group list */ if (isListSubset(mem2, mem1)) { /* remove g2 from the list */ groups.removeElementAt(j); /* adjust the loop counters for the removal */ --cnt; --j; /* * adjust the outer loop counter if it's affected - * the outer loop is affected if it's already past * this point in the list, which means that its * index is higher than the inner loop index */ if (i > j) --i; } } } /* * We now have a final accounting of the groups that we will * consider using. Reset the membership list for each group in * the surviving list. */ foreach (local g in groups) { local itemsInList; /* get this group's membership list vector */ itemsInList = groupTab[g]; /* clear the vector */ itemsInList.removeRange(1, itemsInList.length()); } /* * Now, run through our item list again, and assign each item to * the surviving group that comes earliest in the item's group * list. */ for (i = 1, cnt = lst.length() ; i <= cnt ; ++i) { local curGroups; local winningGroup; /* get this object into a local for easy reference */ cur = lst[i]; /* if the item isn't part of this listing, skip it */ if (!isListed(cur)) continue; /* get the list of groups with which this object is listed */ curGroups = listWith(cur); if (curGroups == nil) curGroups = []; /* * find the first element in the group list that is in the * surviving group list */ winningGroup = nil; foreach (local g in curGroups) { /* if this group is in the surviving list, it's the one */ if (groups.indexOf(g) != nil) { winningGroup = g; break; } } /* * if we have a group, add this item to the group's * membership; otherwise, add it to the singles list */ if (winningGroup != nil) groupTab[winningGroup].append(cur); else singles.append(cur); } /* eliminate any surviving group with too few members */ for (i = 1, cnt = groups.length() ; i <= cnt ; ++i) { local mem; /* get this group's membership list */ mem = groupTab[groups[i]]; /* * if this group's membership is too small, eliminate the * group and add the member into the singles pile */ if (mem.length() < groups[i].minGroupSize) { /* put the item(s) into the singles list */ singles.appendAll(mem); /* eliminate this item from the group list */ groups.removeElementAt(i); /* adjust the loop counters */ --cnt; --i; } } /* return the cardinality of the arranged list */ return getArrangedListCardinality(singles, groups, groupTab); } /* * Show the list. This is called after we've figured out which items * we intend to display, and after we've arranged the items into * groups. In rare cases, listers might want to override this, to * customize the way the way the list is displayed based on the * internal arrangement of the list. */ showArrangedList(pov, parent, lst, options, indent, infoTab, itemCount, singles, groups, groupTab, origLst) { /* * We now know how many items we're listing (grammatically * speaking), so we're ready to display the list prefix. If * we're displaying nothing at all, just display the "empty" * message, and we're done. */ if (itemCount == 0) { /* show the empty list */ showListEmpty(pov, parent); } else { local i; local cnt; local sublists; local origOptions = options; local itemOptions; local groupOptions; local listCount; local dispCount; local cur; /* * Check to see if we have one or more group sublists - if * we do, we must use the "long" list format for our overall * list, otherwise we can use the normal "short" list * format. The long list format uses semicolons to separate * items. */ for (i = 1, cnt = groups.length(), sublists = nil ; i <= cnt ; ++i) { /* * if this group's lister item displays a sublist, we * must use the long format */ if (groups[i].groupDisplaysSublist) { /* note that we are using the long format */ sublists = true; /* * one is enough to make us use the long format, so * we need not look any further */ break; } } /* generate the prefix message if we're in a 'tall' listing */ if ((options & ListTall) != 0) { /* indent the prefix */ showListIndent(options, indent); /* * Show the prefix. If this is a contents listing, and * it's not at the top level, show the contents prefix; * otherwise show the full list prefix. Note that we can * have a contents listing at the top level, since some * lists are broken out for separate listing. */ if ((options & ListContents) != 0 && indent != 0) showListContentsPrefixTall(itemCount, pov, parent); else showListPrefixTall(itemCount, pov, parent); /* go to a new line for the list contents */ "\n"; /* indent the items one level now, since we showed a prefix */ ++indent; } else { /* show the prefix */ showListPrefixWide(itemCount, pov, parent, lst: lst); } /* * regardless of whether we're adding long formatting to the * main list, display the group sublists with whatever * formatting we were originally using */ groupOptions = options; /* show each item with our current set of options */ itemOptions = options; /* * if we're using sublists, show "long list" separators in * the main list */ if (sublists) itemOptions |= ListLong; /* * calculate the number of items we'll show in the list - * each group shows up as one list entry, so the total * number of list entries is the number of single items plus * the number of groups */ listCount = singles.length() + groups.length(); /* * Show the items. Run through the (filtered) original * list, so that we show everything in the original sorting * order. */ dispCount = 0; foreach (cur in lst) { local group; local displayedCur; /* presume we'll display this item */ displayedCur = true; /* * Figure out how to show this item: if it's in the * singles list, show it as a single item; if it's in * the group list, show its group; if it's in a group * we've previously shown, show nothing, as we showed * the item when we showed its group. */ if (singles.indexOf(cur) != nil) { /* * It's in the singles list, so show it as a single * item. * * If the item has contents that we'll display in * 'tall' mode, show the item with its contents - we * don't need to show the item separately, since it * will provide a 'tall' list prefix showing itself. * Otherwise, show the item singly. */ if ((options & ListTall) != 0 && (options & ListRecurse) != 0 && contentsListed(cur) && !contentsListedSeparately(cur) && getListedContents(cur, infoTab) != []) { /* show the item with its contents */ showContentsList(pov, cur, origOptions | ListContents, indent, infoTab); } else { /* show the list indent if necessary */ showListIndent(itemOptions, indent); /* show the item */ showListItem(cur, itemOptions, pov, infoTab); /* * if we're in wide recursive mode, show the * item's contents as an in-line parenthetical */ if ((options & ListTall) == 0 && (options & ListRecurse) != 0 && contentsListed(cur) && !contentsListedSeparately(cur)) { /* show the item's in-line contents */ showInlineContentsList(pov, cur, origOptions | ListContents, indent + 1, infoTab); } } } else if ((group = groups.valWhich( {g: groupTab[g].indexOf(cur) != nil})) != nil) { /* show the list indent if necessary */ showListIndent(itemOptions, indent); /* we found the item in a group, so show its group */ group.showGroupList(pov, self, groupTab[group], groupOptions, indent, infoTab); /* * Forget this group - we only need to show each * group once, since the group shows every item it * contains. Since we'll encounter the groups other * members as we continue to scan the main list, we * want to make sure we don't show the group again * when we reach the other items. */ groups.removeElement(group); } else { /* * We didn't find the item in the singles list or in * a group - it must be part of a group that we * already showed previously, so we don't need to * show it again now. Simply make a note that we * didn't display it. */ displayedCur = nil; } /* if we displayed the item, show a suitable separator */ if (displayedCur) { /* count another list entry displayed */ ++dispCount; /* show an appropriate separator */ showListSeparator(itemOptions, dispCount, listCount); } } /* * if we're in 'wide' mode, finish the listing (note that if * this is a 'tall' listing, we're already done, because a * tall listing format doesn't make provisions for anything * after the item list) */ if ((options & ListTall) == 0) { /* show the wide-mode list suffix */ showListSuffixWide(itemCount, pov, parent); } } } /* * Get the cardinality of an arranged list. Returns the number of * items that will appear in the list, for grammatical agreement. */ getArrangedListCardinality(singles, groups, groupTab) { local cnt; /* start with a count of zero; we'll add to it as we go */ cnt = 0; /* * Add up the cardinality of the single items. Some individual * items in the singles list might count as multiple items * grammatically - in particular, if an item has a plural name, * we need a plural verb to agree with it. */ foreach (local s in singles) { /* add the grammatical cardinality of this single item */ cnt += listCardinality(s); } /* add in the cardinality of each group */ foreach (local g in groups) { /* add the grammatical cardinality of this group */ cnt += g.groupCardinality(self, groupTab[g]); } /* return the total */ return cnt; } /* * Get the number of noun phrase elements in a list. This differs * from the cardinality in that we only count noun phrases, not the * cardinality of each noun phrase. So, for example, "five coins" * has cardinality five, but has only one noun phrase. */ getArrangedListNounPhraseCount(singles, groups, groupTab) { local cnt; /* each single item counts as one noun phrase */ cnt = singles.length(); /* add in the noun phrases from each group */ foreach (local g in groups) cnt += g.groupNounPhraseCount(self, groupTab[g]); /* return the total */ return cnt; } /* * Service routine: show the separately-listed contents of the items * in the given list, and their separately-listed contents, and so * on. This routine is not normally overridden in subclasses, and is * not usually called except from the Lister implementation. * * For each item in the given list, we show the item's contents if * the item is either marked as unlisted, or it's marked as showing * its contents separately. In the former case, we know that we * cannot have shown the item's contents in-line in the main list, * since we didn't show the item at all in the main list. In the * latter case, we know that we didn't show the item's contents in * the main list because it's specifically marked as showing its * contents out-of-line. */ showSeparateContents(pov, lst, options, infoTab) { /* * show the separate contents list for each item in the list * which isn't itself listable or which has its contents listed * separately despite its being listed */ foreach (local cur in lst) { /* only show the contents if the contents are listed */ if (contentsListed(cur)) { /* * if we didn't list this item, or if it specifically * wants its contents listed out-of-line, show its * listable contents */ if (!isListed(cur) || contentsListedSeparately(cur)) { /* * Show the item's contents. Note that even though * we're showing this list recursively, it's actually * a new top-level list, so show it at indent level * zero. */ showContentsList(pov, cur, options, 0, infoTab); } /* recursively do the same thing with its contents */ showSeparateContents(pov, getContents(cur), options, infoTab); } } } /* * Show a list indent if necessary. If ListTall is included in the * options, we'll indent to the given level; otherwise we'll do * nothing. */ showListIndent(options, indent) { /* show the indent only if we're in "tall" mode */ if ((options & ListTall) != 0) { for (local i = 0 ; i < indent ; ++i) "\t"; } } /* * Show a newline after a list item if we're in a tall list; does * nothing for a wide list. */ showTallListNewline(options) { if ((options & ListTall) != 0) "\n"; } /* * Show a simple list, recursing into contents lists if necessary. * We pay no attention to grouping; we just show the items * individually. * * 'prevCnt' is the number of items already displayed, if anything * has already been displayed for this list. This should be zero if * this will display the entire list. */ showListSimple(pov, lst, options, indent, prevCnt, infoTab) { local i; local cnt; local dispCount; local totalCount; /* calculate the total number of items in the lis t*/ totalCount = prevCnt + lst.length(); /* display the items */ for (i = 1, cnt = lst.length(), dispCount = prevCnt ; i <= cnt ; ++i) { local cur; /* get the item */ cur = lst[i]; /* * If the item has contents that we'll display in 'tall' * mode, show the item with its contents - we don't need to * show the item separately, since it will provide a 'tall' * list prefix showing itself. Otherwise, show the item * singly. */ if ((options & ListTall) != 0 && (options & ListRecurse) != 0 && contentsListed(cur) && !contentsListedSeparately(cur) && getListedContents(cur, infoTab) != []) { /* show the item with its contents */ showContentsList(pov, cur, options | ListContents, indent + 1, infoTab); } else { /* show the list indent if necessary */ showListIndent(options, indent); /* show the item */ showListItem(cur, options, pov, infoTab); /* * if we're in wide recursive mode, show the item's * contents as an in-line parenthetical */ if ((options & ListTall) == 0 && (options & ListRecurse) != 0 && contentsListed(cur) && !contentsListedSeparately(cur)) { /* show the item's in-line contents */ showInlineContentsList(pov, cur, options | ListContents, indent + 1, infoTab); } } /* count the item displayed */ ++dispCount; /* show the list separator */ showListSeparator(options, dispCount, totalCount); } } /* * List the contents of an item. * * 'pov' is the point of view, which is usually an actor (and * usually the player character actor). * * 'obj' is the item whose contents we are to display. * * 'options' is the set of flags that we'll pass to showList(), and * has the same meaning as for that function. * * 'infoTab' is a lookup table of SenseInfo objects giving the sense * information for all of the objects that the actor to whom we're * showing the contents listing can sense. */ showContentsList(pov, obj, options, indent, infoTab) { /* * List the item's contents. By default, use the contentsLister * property of the object whose contents we're showing to obtain * the lister for the contents. */ obj.showObjectContents(pov, obj.contentsLister, options, indent, infoTab); } /* * Determine if an object's contents are listed separately from its * own list entry for the purposes of our type of listing. If this * returns true, then we'll list the object's contents in a separate * listing (a separate sentence following the main listing sentence, * or a separate tree when in 'tall' mode). * * Note that this only matters for objects listed in the top-level * list. We'll always show the contents separately for an object * that isn't listed in the top-level list (i.e., an object for which * isListed(obj) returns nil). */ contentsListedSeparately(obj) { return obj.contentsListedSeparately; } /* * Show an "in-line" contents list. This shows the item's contents * list as a parenthetical, as part of a recursive listing. This is * pretty much the same as showContentsList(), but uses the object's * in-line contents lister instead of its regular contents lister. */ showInlineContentsList(pov, obj, options, indent, infoTab) { /* show the item's contents using its in-line contents lister */ obj.showObjectContents(pov, obj.inlineContentsLister, options, indent, infoTab); } /* * Show the prefix for a 'wide' listing - this is a message that * appears just before we start listing the objects. 'itemCount' is * the number of items to be listed; the items might be grouped in * the listing, so a list that comes out as "three boxes and two * books" will have an itemCount of 5. (The purpose of itemCount is * to allow the message to have grammatical agreement in number.) * * 'lst' is the entire list, which some languages need for * grammatical agreement. This is passed as a named argument, so an * overrider can omit it from the parameter list if it's not needed. * * This will never be called with an itemCount of zero, because we * will instead use showListEmpty() to display an empty list. */ showListPrefixWide(itemCount, pov, parent, lst:) { } /* * show the suffix for a 'wide' listing - this is a message that * appears just after we finish listing the objects */ showListSuffixWide(itemCount, pov, parent) { } /* * Show the list prefix for a 'tall' listing. Note that there is no * list suffix for a tall listing, because the format doesn't allow * it. */ showListPrefixTall(itemCount, pov, parent) { } /* * Show the list prefix for the contents of an object in a 'tall' * listing. By default, we just show our usual tall list prefix. */ showListContentsPrefixTall(itemCount, pov, parent) { showListPrefixTall(itemCount, pov, parent); } /* * Show an empty list. If the list to be displayed has no items at * all, this is called instead of the prefix/suffix routines. This * can be left empty if no message is required for an empty list, or * can display the complete message appropriate for an empty list * (such as "You are empty-handed"). */ showListEmpty(pov, parent) { } /* * Is this item to be listed in room descriptions? Returns true if * so, nil if not. By default, we'll use the object's isListed * method to make this determination. We virtualize this into the * lister interface to allow for different inclusion rules for the * same object depending on the type of list we're generating. */ isListed(obj) { return obj.isListed(); } /* * Get the grammatical cardinality of this listing item. This should * return the number of items that this item appears to be * grammatically, for noun-verb agreement purposes. */ listCardinality(obj) { return obj.listCardinality(self); } /* * Are this item's contents listable? */ contentsListed(obj) { return obj.contentsListed; } /* * Get all contents of this item. */ getContents(obj) { return obj.contents; } /* * Get the listed contents of an object. 'infoTab' is the sense * information table for the enclosing listing. By default, we call * the object's getListedContents() method, but this is virtualized * in the lister interface to allow for listing other hierarchies * besides ordinary contents. */ getListedContents(obj, infoTab) { return obj.getListedContents(self, infoTab); } /* * Get the list of grouping objects for listing the item. By * default, we return the object's listWith result. Subclasses can * override this to specify different groupings for the same object * depending on the type of list we're generating. * * The group list returned is in order from most general to most * specific. For example, if an item is grouped with coins in * general and silver coins in particular, the general coins group * would come first, then the silver coin group, because the silver * coin group is more specific. */ listWith(obj) { return obj.listWith; } /* show an item in a list */ showListItem(obj, options, pov, infoTab) { obj.showListItem(options, pov, infoTab); } /* * Show a set of equivalent items as a counted item ("three coins"). * The listing mechanism itself never calls this directly; instead, * this is provided so that ListGroupEquivalent can ask the lister * how to describe its equivalent sets, so that different listers * can customize the display of equivalent items. * * 'lst' is the full list of equivalent items. By default, we pick * one of these arbitrarily to show, since they're presumably all * the same for the purposes of the list. */ showListItemCounted(lst, options, pov, infoTab) { /* * by defualt, show the counted name for one of the items * (chosen arbitrarily, since they're all the same) */ lst[1].showListItemCounted(lst, options, pov, infoTab); } /* * Show a list separator after displaying an item. curItemNum is * the number of the item just displayed (1 is the first item), and * totalItems is the total number of items that will be displayed in * the list. * * This generic routine is further parameterized by properties for * the individual types of separators. This default implementation * distinguishes the following separators: the separator between the * two items in a list of exactly two items; the separator between * adjacent items other than the last two in a list of more than two * items; and the separator between the last two elements of a list * of more than two items. */ showListSeparator(options, curItemNum, totalItems) { local useLong = ((options & ListLong) != 0); /* if this is a tall list, the separator is simply a newline */ if ((options & ListTall) != 0) { "\n"; return; } /* if that was the last item, there are no separators */ if (curItemNum == totalItems) return; /* check to see if the next item is the last */ if (curItemNum + 1 == totalItems) { /* * We just displayed the penultimate item in the list, so we * need to use the special last-item separator. If we're * only displaying two items total, we use an even more * special separator. */ if (totalItems == 2) { /* use the two-item separator */ if (useLong) longListSepTwo; else listSepTwo; } else { /* use the normal last-item separator */ if (useLong) longListSepEnd; else listSepEnd; } } else { /* in the middle of the list - display the normal separator */ if (useLong) longListSepMiddle; else listSepMiddle; } } /* * Show the specific types of list separators for this list. By * default, these will use the generic separators defined in the * library messages object (gLibMessages). For English, these are * commas and semicolons for short and long lists, respectively; the * word "and" for a list with only two items; and a comma or * semicolon and the word "and" between the last two items in a list * with more than two items. */ /* * normal and "long list" separator between the two items in a list * with exactly two items */ listSepTwo { gLibMessages.listSepTwo; } longListSepTwo { gLibMessages.longListSepTwo; } /* * normal and long list separator between items in list with more * than two items */ listSepMiddle { gLibMessages.listSepMiddle; } longListSepMiddle { gLibMessages.longListSepMiddle; } /* * normal and long list separator between second-to-last and last * items in a list with more than two items */ listSepEnd { gLibMessages.listSepEnd; } longListSepEnd { gLibMessages.longListSepEnd; } /* * Get my "top-level" lister. For a sub-lister, this will return * the parent lister's top-level lister. The default lister is a * top-level lister, so we just return ourself. */ getTopLister() { return self; } /* * The last custom flag defined by this class. Lister and each * subclass are required to define this so that each subclass can * allocate its own custom flags in a manner that adapts * automatically to future additions of flags to base classes. As * the base class, we allocate our flags statically with #define's, * so we simply use the fixed #define'd last flag value here. */ nextCustomFlag = ListCustomFlag ; /* ------------------------------------------------------------------------ */ /* * A SimpleLister provides simplified interfaces for creating formatted * lists. */ class SimpleLister: Lister /* * Show a formatted list given a list of items. This lets you create * a formatted list from an item list without worrying about * visibility or other factors that affect the full Lister * interfaces. */ showSimpleList(lst) { showListAll(lst, 0, 0); } /* by default, everything given to a simple lister is listed */ isListed(obj) { return true; } /* * Format a simple list, but rather than displaying the result, * return it as a string. This simply displays the list as normal, * but captures the output as a string and returns it. */ makeSimpleList(lst) { return mainOutputStream.captureOutput({: showSimpleList(lst) }); } ; /* * objectLister is a concrete SimpleLister for listing simulation * objects. */ objectLister: SimpleLister ; /* * stringLister is a concrete SimpleLister for formatting lists of * strings. To use this lister, pass lists of single-quoted strings * (instead of simulation objects) to showSimpleList(), etc. */ stringLister: SimpleLister /* show a list item - list items are strings, so simply 'say' them */ showListItem(str, options, pov, infoTab) { say(str); } /* * get the cardinality of an arranged list (we need to override this * because our items are strings, which don't have the normal object * properties that would let us count cardinality the usual way) */ getArrangedListCardinality(singles, groups, groupTab) { return singles.length(); } ; /* ------------------------------------------------------------------------ */ /* * Plain lister - this lister doesn't show anything for an empty list, * and doesn't show a list suffix or prefix. */ plainLister: Lister /* show the prefix/suffix in wide mode */ showListPrefixWide(itemCount, pov, parent) { } showListSuffixWide(itemCount, pov, parent) { } /* show the tall prefix */ showListPrefixTall(itemCount, pov, parent) { } ; /* * Sub-lister for listing the contents of a group. This lister shows a * simple list with no prefix or suffix, and otherwise uses the * characteristics of the parent lister. */ class GroupSublister: object construct(parentLister, parentGroup) { /* remember the parent lister and group objects */ self.parentLister = parentLister; self.parentGroup = parentGroup; } /* no prefix or suffix */ showListPrefixWide(itemCount, pov, parent) { } showListSuffixWide(itemCount, pov, parent) { } showListPrefixTall(itemCount, pov, parent) { } /* show nothing when empty */ showListEmpty(pov, parent) { } /* * Show an item in the list. Rather than going through the parent * lister directly, we go through the parent group, so that it can * customize the display of items in the group. */ showListItem(obj, options, pov, infoTab) { /* ask the parent group to handle it */ parentGroup.showGroupItem(parentLister, obj, options, pov, infoTab); } /* * Show a counted item in the group. As with showListItem, we ask * the parent group to do the work, so that it can customize the * display if desired. */ showListItemCounted(lst, options, pov, infoTab) { /* ask the parent group to handle it */ parentGroup.showGroupItemCounted( parentLister, lst, options, pov, infoTab); } /* delegate everything we don't explicitly handle to our parent lister */ propNotDefined(prop, [args]) { return delegated (getTopLister()).(prop)(args...); } /* get the top-level lister - returns my parent's top-level lister */ getTopLister() { return parentLister.getTopLister(); } /* my parent lister */ parentLister = nil /* my parent list group */ parentGroup = nil ; /* * Paragraph lister: this shows its list items separated by paragraph * breaks, with a paragraph break before the first item. */ class ParagraphLister: Lister /* start the list with a paragraph break */ showListPrefixWide(itemCount, pov, parent) { "<.p>"; } /* we show no list separators */ showListSeparator(options, curItemNum, totalItems) { /* add a paragraph separator between items */ if (curItemNum != totalItems) "<.p>"; } ; /* * Lister for objects in a room description with special descriptions. * Each special description gets its own paragraph, so this is based on * the paragraph lister. */ specialDescLister: ParagraphLister /* list everything */ isListed(obj) { return true; } /* show a list item */ showListItem(obj, options, pov, infoTab) { /* show the object's special description */ obj.showSpecialDescWithInfo(infoTab[obj], pov); } /* use the object's special description grouper */ listWith(obj) { return obj.specialDescListWith; } ; /* * Special description lister for the contents of an item being examined. * This is similar to the regular specialDescLister, but shows the * special descriptions of the contents of an object being described with * "examine" or "look in," rather than of the entire location. */ class SpecialDescContentsLister: ParagraphLister construct(cont) { /* remember the containing object being described */ cont_ = cont; } /* list everything */ isListed(obj) { return true; } /* show a list item */ showListItem(obj, options, pov, infoTab) { /* show the object's special description in our container */ obj.showSpecialDescInContentsWithInfo(infoTab[obj], pov, cont_); } /* use the object's special description grouper */ listWith(obj) { return obj.specialDescListWith; } /* the containing object we're examining */ cont_ = nil ; /* * Plain lister for actors. This is the same as an ordinary * plainLister, but ignores each object's isListed flag and lists it * anyway. */ plainActorLister: plainLister isListed(obj) { return true; } ; /* * Grouper for actors in a common posture and in a common location. We * create one of these per room per posture when we discover actors in * the room during "look around" (or "examine" on a nested room). This * grouper lets us group actors like so: "Dan and Jane are sitting on * the couch." */ class RoomActorGrouper: ListGroup construct(location, posture) { self.location = location; self.posture = posture; } showGroupList(pov, lister, lst, options, indent, infoTab) { local cont; local outer; /* if the location isn't in the sense table, skip the whole list */ if (infoTab[location] == nil) return; /* get the nominal posture container, if it's visible */ cont = location.getNominalActorContainer(posture); if (cont != nil && !pov.canSee(cont)) cont = nil; /* get the outermost visible enclosing location */ outer = location.getOutermostVisibleRoom(pov); /* * Only mention the outermost location if it's remote and it's * not the same as the nominal container. (If the remote outer * room is the same as the nominal container, it would be * redundant to mention it as both the nominal and remote * container.) */ if (outer == cont || pov.isIn(outer)) outer = nil; /* create a sub-lister for the group */ lister = createGroupSublister(lister); /* * show the list prefix message - use the nominal container if * we can see it, otherwise generate a generic message */ if (cont != nil) cont.actorInGroupPrefix(pov, posture, outer, lst); else if (outer != nil) gLibMessages.actorThereGroupPrefix(pov, posture, outer, lst); else gLibMessages.actorHereGroupPrefix(posture, lst); /* list the actors' names as a plain list */ plainActorLister.showList(pov, location, lst, options, indent, infoTab, self); /* add the suffix message */ if (cont != nil) cont.actorInGroupSuffix(pov, posture, outer, lst); else if (outer != nil) gLibMessages.actorThereGroupSuffix(pov, posture, outer, lst); else gLibMessages.actorHereGroupSuffix(posture, lst); } ; /* * Base class for inventory listers. This lister uses a special listing * method to show the items, so that items can be shown with special * notations in an inventory list that might not appear in other types * of listings. */ class InventoryLister: Lister /* list items in inventory according to their isListedInInventory */ isListed(obj) { return obj.isListedInInventory; } /* * Show list items using the inventory name, which might differ from * the regular nmae of the object. */ showListItem(obj, options, pov, infoTab) { obj.showInventoryItem(options, pov, infoTab); } showListItemCounted(lst, options, pov, infoTab) { lst[1].showInventoryItemCounted(lst, options, pov, infoTab); } /* * Show contents of the items in the inventory. We customize this * so that we can differentiate inventory contents lists from other * contents lists. */ showContentsList(pov, obj, options, indent, infoTab) { /* list the item's contents */ obj.showInventoryContents(pov, obj.contentsLister, options, indent, infoTab); } /* * Show the contents in-line, for an inventory listing. */ showInlineContentsList(pov, obj, options, indent, infoTab) { /* list the item's contents using its in-line lister */ obj.showInventoryContents(pov, obj.inlineContentsLister, options, indent, infoTab); } ; /* * Base class for worn-inventory listers. This lister uses a special * listing method to show the items, so that items being worn are shown * *without* the special '(being worn)' notation that might otherwise * appear in inventory listings. */ class WearingLister: InventoryLister /* show the list item using the "worn listing" name */ showListItem(obj, options, pov, infoTab) { obj.showWornItem(options, pov, infoTab); } showListItemCounted(lst, options, pov, infoTab) { lst[1].showWornItemCounted(lst, options, pov, infoTab); } ; /* * "Divided" inventory lister. In 'wide' mode, this shows inventory in * two parts: the items being carried, and the items being worn. (We use * the standard single tree-style listing in 'tall' mode.) */ class DividedInventoryLister: InventoryLister /* * Show the list. We completely override the main lister method so * that we can show our two lists. */ showList(pov, parent, lst, options, indent, infoTab, parentGroup) { /* * If this is a 'tall' listing, use the normal listing style; for * a 'wide' listing, use our special segregated style. If we're * being invoked recursively to show a contents listing, we * similarly want to use the base handling. */ if ((options & (ListTall | ListContents)) != 0) { /* inherit the standard behavior */ inherited(pov, parent, lst, options, indent, infoTab, parentGroup); } else { local carryingLst, wearingLst; local carryingStr, wearingStr; /* divide the lists into 'carrying' and 'wearing' sublists */ carryingLst = new Vector(32); wearingLst = new Vector(32); foreach (local cur in lst) (cur.isWornBy(parent) ? wearingLst : carryingLst).append(cur); /* generate and capture the 'carried' listing */ carryingStr = outputManager.curOutputStream.captureOutput({: carryingLister.showList(pov, parent, carryingLst, options, indent, infoTab, parentGroup)}); /* generate and capture the 'worn' listing */ wearingStr = outputManager.curOutputStream.captureOutput({: wearingLister.showList(pov, parent, wearingLst, options, indent, infoTab, parentGroup)}); /* generate the combined listing */ showCombinedInventoryList(parent, carryingStr, wearingStr); /* * Now show the out-of-line contents for the whole list, if * appropriate. We save this until after showing both parts * of the list, to keep the direct inventory parts together * at the beginning of the output. */ if ((options & ListRecurse) != 0 && indent == 0 && (options & ListContents) == 0) { /* show the contents of each object we didn't list */ showSeparateContents(pov, lst, options | ListContents, infoTab); } } } /* * Show the combined listing. This must be provided by each * language-specific subclass. The inputs are the results (strings) * of the captured output of the sublistings of the items being * carried and the items being worn. These will be "raw" listings, * without any prefix or suffix text. This routine's job is to * display the final output, adding the framing text. */ showCombinedInventoryList(parent, carrying, wearing) { } /* * The recommended maximum number of number of noun phrases to show * in the single-sentence format. This should be used by the * showCombinedInventoryList() method to decide whether to display * the combined listing as a single sentence or as two separate * sentences. */ singleSentenceMaxNouns = 7 /* * Our associated sub-listers for items begin carried and worn, * respectively. We'll use these to list our sublist of items being * worn. */ carryingLister = actorCarryingSublister wearingLister = actorWearingSublister ; /* * Base class for the inventory sub-lister for items being carried. This * is a minor specialization of the basic inventory lister; in this * version, we omit any prefix, suffix, or empty messages, since we'll * rely on the caller to combine our raw listing with the raw 'wearing' * listing to form the full results. * * This type of lister should normally only be used from within an * inventory lister. This type of lister assumes that it's part of a * larger listing controlled externally; for example, we don't show * out-of-line contents, since we assume the caller will be doing this. */ class InventorySublister: InventoryLister /* don't show any prefix, suffix, or 'empty' messages */ showListPrefixWide(itemCount, pov, parent) { } showListSuffixWide(itemCount, pov, parent) { } showListEmpty(pov, parent) { } /* don't show out-of-line contents */ showSeparateContents(pov, lst, options, infoTab) { } ; /* * Base class for the inventory sub-lister for items being worn. We use * a special listing method to show these items, so that items being * shown explicitly in a worn list can be shown differently from the way * they would in a normal inventory list. (For example, a worn item in a * normal inventory list might show a "(worn)" indication, whereas it * would not want to show a similar indication in a list of objects * explicitly being worn.) * * This type of lister should normally only be used from within an * inventory lister. This type of lister assumes that it's part of a * larger listing controlled externally; for example, we don't show * out-of-line contents, since we assume the caller will be doing this. */ class WearingSublister: WearingLister /* don't show any prefix, suffix, or 'empty' messages */ showListPrefixWide(itemCount, pov, parent) { } showListSuffixWide(itemCount, pov, parent) { } showListEmpty(pov, parent) { } /* don't show out-of-line contents */ showSeparateContents(pov, lst, options, infoTab) { } ; /* * The standard inventory sublisters. */ actorCarryingSublister: InventorySublister; actorWearingSublister: WearingSublister; /* * Base class for contents listers. This is used to list the contents * of the objects that appear in top-level lists (a top-level list is * the list of objects directly in a room that appears in a room * description, or the list of items being carried in an INVENTORY * command, or the direct contents of an object being examined). */ class ContentsLister: Lister ; /* * Base class for description contents listers. This is used to list * the contents of an object when we examine the object, or when we * explicitly LOOK IN the object. */ class DescContentsLister: Lister /* * Use the explicit look-in flag for listing contents. We might * list items within an object on explicit examination of the item * that we wouldn't list in a room or inventory list containing the * item. */ isListed(obj) { return obj.isListedInContents; } ; /* * Base class for sense listers, which list the items that can be sensed * for a command like "listen" or "smell". */ class SenseLister: ParagraphLister /* show everything we're asked to list */ isListed(obj) { return true; } /* show a counted list item */ showListItemCounted(lst, options, pov, infoTab) { /* * simply show one item, without the count - non-visual senses * don't distinguish numbers of items that are equivalent */ showListItem(lst[1], options, pov, infoTab); } ; /* * Room contents lister for things that can be heard. */ roomListenLister: SenseLister /* list an item in a room if its isSoundListedInRoom is true */ isListed(obj) { return obj.isSoundListedInRoom; } /* list an item */ showListItem(obj, options, pov, infoTab) { /* show the "listen" list name for the item */ obj.soundHereDesc(); } ; /* * Lister for explicit "listen" action */ listenActionLister: roomListenLister /* list everything in response to an explicit general LISTEN command */ isListed(obj) { return true; } /* show an empty list */ showListEmpty(pov, parent) { mainReport(¬hingToHearMsg); } ; /* * Room contents lister for things that can be smelled. */ roomSmellLister: SenseLister /* list an item in a room if its isSmellListedInRoom is true */ isListed(obj) { return obj.isSmellListedInRoom; } /* list an item */ showListItem(obj, options, pov, infoTab) { /* show the "smell" list name for the item */ obj.smellHereDesc(); } ; /* * Lister for explicit "smell" action */ smellActionLister: roomSmellLister /* list everything in response to an explicit general SMELL command */ isListed(obj) { return true; } /* show an empty list */ showListEmpty(pov, parent) { mainReport(¬hingToSmellMsg); } ; /* * Inventory lister for things that can be heard. */ inventoryListenLister: SenseLister /* list an item */ showListItem(obj, options, pov, infoTab) { /* show the "listen" list name for the item */ obj.soundHereDesc(); } ; /* * Inventory lister for things that can be smelled. */ inventorySmellLister: SenseLister /* list an item */ showListItem(obj, options, pov, infoTab) { /* show the "smell" list name for the item */ obj.smellHereDesc(); } ; /* ------------------------------------------------------------------------ */ /* * List Group Interface. An instance of this object is created for each * set of objects that are to be grouped together. */ class ListGroup: object /* * Show a list of items from this group. All of the items in the * list will be members of this list group; we are to display a * sentence fragment showing the items in the list, suitable for * embedding in a larger list. * * 'options', 'indent', and 'infoTab' have the same meaning as they * do for showList(). * * Note that we are not to display any separator before or after our * list; the caller is responsible for that. */ showGroupList(pov, lister, lst, options, indent, infoTab) { } /* * Show an item in the group's sublist. The sublister calls this to * display each item in the group when the group calls the sublister * to display the group list. By default, we simply let the * sublister handle the request, which gives items in our group * sublist the same appearance they would have had in the sublist to * begin with. We can customize this behavior to give our list * items a different appearance special to the group sublist. * * Note that the same customization could be accomplished by * creating a specialized subclass of GroupSublister in * createGroupSublister(), and overriding showListItem() in the * specialized GroupSublister subclass. We use this mechanism as a * convenience, so that a separate group sublister class doesn't * have to be created simply to customize the display of group * items. */ showGroupItem(sublister, obj, options, pov, infoTab) { /* by default, list using the regular sublister */ sublister.showListItem(obj, options, pov, infoTab); } /* * Show a counted item in our group list. This is the counted item * equivalent of showGroupItem. */ showGroupItemCounted(sublister, lst, options, pov, infoTab) { /* by default, list using the regular sublister */ sublister.showListItemCounted(lst, options, pov, infoTab); } /* * Determine if showing the group list will introduce a sublist into * an enclosing list. This should return true if we will show a * sublist without some kind of grouping, so that the caller knows * to introduce some extra grouping into its enclosing list. This * should return nil if the sublist we display will be clearly set * off in some way (for example, by being enclosed in parentheses). */ groupDisplaysSublist = true /* * The minimum number of elements for which we should retain the * group in a listing. By default, we need two elements to display a * group; any group with only one element is discarded, and the * single element is moved into the 'singles' list. This can be * overridden to allow single-element groups to be retained. In most * cases, it's undesirable to retain single-element groups, but when * grouping is used to partition a list into two or more fixed * portions, single-element groups become desirable. */ minGroupSize = 2 /* * Determine the cardinality of the group listing, grammatically * speaking. This is the number of items that the group seems to be * for the purposes of grammatical agreement. For example, if the * group is displayed as "$1.38 in change", it would be singular for * grammatical agreement, hence would return 1 here; if it displays * "five coins (two copper, three gold)," it would count as five * items for grammatical agreement. * * For languages (like English) that grammatically distinguish * number only between singular and plural, it is sufficient for * this to return 1 for singular and anything higher for plural. * For the sake of languages that make more elaborate number * distinctions for grammatical agreement, though, this should * return as accurate a count as is possible. * * By default, we return the number of items to be displayed in the * list group. This should be overridden when necessary, such as * when the group message is singular in usage even if the list has * multiple items (as in "$1.38 in change"). */ groupCardinality(lister, lst) { return lst.length(); } /* * Get the number of noun phrases this group will display. This * differs from the cardinality in that it doesn't matter how many * *objects* the phrases represent; it only matters how many phrases * are displayed. For example, "five coins" has cardinality 5 but * only displays one noun phrase. * * By default, we simply return the number of items in the group, * since most groups individually list their items. */ groupNounPhraseCount(lister, lst) { return lst.length(); } /* * Create the group sublister. * * In most cases, when a group displays a list of the items in the * group as a sublist, it will not want to use the same lister that * was used to show the enclosing group, because the enclosing lister * will usually have different prefix/suffix styles than the sublist. * However, the group list and the enclosing list might share many * other attributes, such as the style of name to use when displaying * items in the list. The default sublister we create, * GroupSublister, is a hybrid that uses the enclosing lister's * attributes except for a few, such as the prefix and suffix, that * usually need to be changed for the sublist. * * This can be overridden to use a completely customized Lister * object for the group list, if desired. */ createGroupSublister(parentLister) { /* create the standard group sublister by default */ return new GroupSublister(parentLister, self); } ; /* * A "custom" List Group implementation. This type of lister uses a * completely custom message to show the group, without a need to * recursively invoke a lister to list the individual elements. The main * difference between this and the base ListGroup is that the interface * to the custom message generator is very simple - we can dispense with * most of the numerous arguments that the base group message receives, * since most of those arguments are there to allow recursive listing of * the group list. * * This group type is intended mainly for cases where you want to display * some sort of collective description of the group, rather than listing * its members individually. The whole point of the simple interface is * that we don't pass the normal big pile of parameters because we won't * be invoking a full sublisting. Since we assume that this group won't * itself look like a sublist, we set groupDisplaysSublist to nil by * default. This means that our presence in the overall list won't * trigger the "long list" format (usually, this uses semicolons instead * of commas) in the enclosing list. If your custom group message does * indeed look like a sublist (that is, it displays multiple items in a * comma-separated list), you might want to change groupDisplaysSublist * back to true so that the overall list is shown in the "long" format. */ class ListGroupCustom: ListGroup showGroupList(pov, lister, lst, options, indent, infoTab) { /* simply show the custom message for the list */ showGroupMsg(lst); } /* show the custom group message - subclasses should override */ showGroupMsg(lst) { } /* assume our listing message doesn't look like a sublist */ groupDisplaysSublist = nil ; /* * Sorted group list. This is a list that simply displays its members * in a specific sorting order. */ class ListGroupSorted: ListGroup /* * Show the group list */ showGroupList(pov, lister, lst, options, indent, infoTab) { /* put the list in sorted order */ lst = sortListGroup(lst); /* create a sub-lister for the group */ lister = createGroupSublister(lister); /* show the list */ lister.showList(pov, nil, lst, options & ~ListContents, indent, infoTab, self); } /* * Sort the group list. By default, if we have a * compareGroupItems() method defined, we'll sort the list using * that method; otherwise, we'll just return the list unchanged. */ sortListGroup(lst) { /* * if we have a compareGroupItems method, use it to sort the * list; otherwise, just return the list in its current order */ if (propDefined(&compareGroupItems, PropDefAny)) return lst.sort(SortAsc, {a, b: compareGroupItems(a, b)}); else return lst; } /* * Compare a pair of items from the group to determine their relative * sorting order. This should return 0 if the two items are at the * same sorting order, a positive integer if the first item sorts * after the second item, or a negative integer if the first item * sorts before the second item. * * Note that we don't care about the return value beyond whether it's * positive, negative, or zero. This makes it especially easy to * implement this method if the sorting order is determined by a * property on each object that has an integer value: in this case * you simply return the difference of the two property values, as in * a.prop - b.prop. This will have the effect of sorting the objects * in ascending order of their 'prop' property values. To sort in * descending order of the same property, simply reverse the * subtraction: b.prop - a.prop. * * When no implementation of this method is defined in the group * object, sortListGroup won't bother sorting the list at all. * * By default, we don't implement this method. Subclasses that want * to impose a sorting order must implement the method. */ // compareGroupItems(a, b) { return a > b ? 1 : a == b ? 0 : -1; } ; /* * List Group implementation: parenthesized sublist. Displays the * number of items collectively, then displays the list of items in * parentheses. * * Note that this is a ListGroupSorted subclass. If our subclass * defines a compareGroupItems() method, we'll show the list in the * order specified by compareGroupItems(). */ class ListGroupParen: ListGroupSorted /* * show the group list */ showGroupList(pov, lister, lst, options, indent, infoTab) { /* sort the list group, if we have an ordering method defined */ lst = sortListGroup(lst); /* create a sub-lister for the group */ lister = createGroupSublister(lister); /* show the collective count of the object */ showGroupCountName(lst); /* show the tall or wide sublist */ if ((options & ListTall) != 0) { /* tall list - show the items as a sublist */ "\n"; lister.showList(pov, nil, lst, options & ~ListContents, indent, infoTab, self); } else { /* wide list - add a space and a paren for the sublist */ " ("; /* show the sublist */ lister.showList(pov, nil, lst, options & ~ListContents, indent, infoTab, self); /* end the sublist */ ")"; } } /* * Show the collective count for the list of objects. By default, * we'll simply display the countName of the first item in the list, * on the assumption that each object has the same plural * description. However, in most cases this should be overridden to * provide a more general collective name for the group. */ showGroupCountName(lst) { /* show the first item's countName */ say(lst[1].countName(lst.length())); } /* we don't add a sublist, since we enclose our list in parentheses */ groupDisplaysSublist = nil ; /* * List Group implementation: simple prefix/suffix lister. Shows a * prefix message, then shows the list, then shows a suffix message. * * Note that this is a ListGroupSorted subclass. If our subclass * defines a compareGroupItems() method, we'll show the list in the * order specified by compareGroupItems(). */ class ListGroupPrefixSuffix: ListGroupSorted showGroupList(pov, lister, lst, options, indent, infoTab) { /* sort the list group, if we have an ordering method defined */ lst = sortListGroup(lst); /* create a sub-lister for the group */ lister = createGroupSublister(lister); /* show the prefix */ showGroupPrefix(pov, lst); /* if we're in tall mode, start a new line */ lister.showTallListNewline(options); /* show the list */ lister.showList(pov, nil, lst, options & ~ListContents, indent + 1, infoTab, self); /* show the suffix */ showGroupSuffix(pov, lst); } /* show the prefix - we just show the groupPrefix message by default */ showGroupPrefix(pov, lst) { groupPrefix; } /* show the suffix - we just show the groupSuffix message by default */ showGroupSuffix(pov, lst) { groupSuffix; } /* * The prefix and suffix messages. The showGroupPrefix and * showGroupSuffix methods simply show these message properties. We * go through this two-step procedure for convenience: if the * subclass doesn't need the POV and list parameters, it's less * typing to just override these parameterless properties. If the * subclass needs to vary the message according to the POV or what's * in the list, it can override the showGroupXxx methods instead. */ groupPrefix = "" groupSuffix = "" ; /* * Equivalent object list group. This is the default listing group for * equivalent items. The Thing class creates an instance of this class * during initialization for each set of equivalent items. */ class ListGroupEquivalent: ListGroup showGroupList(pov, lister, lst, options, indent, infoTab) { /* show a count of the items */ lister.showListItemCounted(lst, options, pov, infoTab); } /* * An equivalence group displays only a single noun phrase to cover * the entire group. */ groupNounPhraseCount(lister, lst) { return 1; } /* we display as a single item, so there's no sublist */ groupDisplaysSublist = nil ; frobtads-1.2.3/tads3/lib/adv3/banner.t0000644000175000001440000013133511475735040016573 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: banner manager * * This module defines the banner manager, which provides high-level * services to create and manipulate banner windows. * * A "banner" is an independent window shown within the interpreter's * main application display frame (which might be the entire screen on a * character-mode terminal, or could be a window in a GUI system). The * game can control the creation and destruction of banner windows, and * can control their placement and size. * * This implementation is based in part on Steve Breslin's banner * manager, used by permission. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * A BannerWindow corresponds to an on-screen banner. For each banner * window a game wants to display, the game must create an object of this * class. * * Note that merely creating a BannerWindow object doesn't actually * display a banner window. Once a BannerWindow is created, the game * must call the object's showBanner() method to create the on-screen * window for the banner. * * BannerWindow instances are intended to be persistent (not transient). * The banner manager keeps track of each banner window that's actually * being displayed separately via an internal transient object; the game * doesn't need to worry about these tracking objects, since the banner * manager automatically handles them. */ class BannerWindow: OutputStreamWindow /* * Construct the object. * * 'id' is a globally unique identifying string for the banner. When * we dynamically create a banner object, we have to provide a unique * identifying string, so that we can correlate transient on-screen * banners with the banners in a saved state when restoring the saved * state. * * Note that no ID string is needed for BannerWindow objects defined * statically at compile-time, because the object itself ('self') is * a suitably unique and stable identifier. */ construct(id) { /* remember my unique identifier */ id_ = id; } /* * Show the banner. The game should call this method when it first * wants to display the banner. * * 'parent' is the parent banner; this is an existing BannerWindow * object. If 'parent' is nil, then the parent is the main game * text window. The new window's display space is obtained by * carving space out of the parent's area, according to the * alignment and size values specified. * * 'where' and 'other' give the position of the banner among the * children of the given parent. 'where' is one of the constants * BannerFirst, BannerLast, BannerBefore, or BannerAfter. If * 'where' is BannerBefore or BannerAfter, 'other' gives the * BannerWindow object to be used as the reference point in the * parent's child list; 'other' is ignored in other cases. Note * that 'other' must always be another child of the same parent; if * it's not, then we act as though 'where' were given as BannerLast. * * 'windowType' is a BannerTypeXxx constant giving the new window's * type. * * 'align' is a BannerAlignXxx constant giving the alignment of the * new window. 'size' is an integer giving the size of the banner, * in units specified by 'sizeUnits', which is a BannerSizeXxx * constant. If 'size' is nil, it indicates that the caller doesn't * care about the size, usually because the caller will be resizing * the banner soon anyway; the banner will initially have zero size * in this case if we create a new window, or will retain the * existing size if there's already a system window. * * 'styleFlags' is a combination of BannerStyleXxx constants * (combined with the bitwise OR operator, '|'), giving the requested * display style of the new banner window. * * Note that if we already have a system banner window, and the * existing banner window has the same characteristics as the new * creation parameters, we'll simply re-use the existing window * rather than closing and re-creating it; this reduces unnecessary * redrawing in cases where the window isn't changing. If the caller * explicitly wants to create a new window even if we already have a * window, the caller should simply call removeBanner() before * calling this routine. */ showBanner(parent, where, other, windowType, align, size, sizeUnits, styleFlags) { local parentID; local otherID; /* note the ID's of the parent window and the insertion point */ parentID = (parent != nil ? parent.getBannerID() : nil); otherID = (other != nil ? other.getBannerID() : nil); /* * if we have an 'other' specified, its parent must match our * proposed parent; otherwise, ignore 'other' and insert at the * end of the parent list */ if (other != nil && other.parentID_ != parentID) { other = nil; where = BannerLast; } /* if we already have an existing banner window, check for a match */ if (handle_ != nil) { local t; local match; /* presume we won't find an exact match */ match = nil; /* we already have a window - get the UI tracker object */ if ((t = bannerUITracker.getTracker(self)) != nil) { /* check the placement, window type, alignment, and style */ match = (t.windowType_ == windowType && t.parentID_ == parentID && t.align_ == align && t.styleFlags_ == styleFlags && bannerUITracker.orderMatches(t, where, otherID)); } /* * if it doesn't match the existing window, close it, so that * we will open a brand new window with the new * characteristics */ if (!match) removeBanner(); } /* if the system-level banner doesn't already exist, create it */ if (handle_ == nil) { /* create my system-level banner window */ if (!createSystemBanner(parent, where, other, windowType, align, size, sizeUnits, styleFlags)) { /* we couldn't create the system banner - give up */ return nil; } /* create our output stream */ createOutputStream(); /* add myself to the UI tracker's active banner list */ bannerUITracker.addBanner(handle_, outputStream_, getBannerID(), parentID, where, other, windowType, align, styleFlags); } else { /* * Our system-level window already exists, so we don't need * to create a new one. However, our size could be * different, so explicitly set the requested size if it * doesn't match our recorded size. If the size is given as * nil, leave the size as it is; a nil size indicates that * the caller doesn't care about the size (probably because * the caller is going to change the size shortly anyway), * so we can avoid unnecessary redrawing by leaving the size * as it is for now. */ if (size != nil && (size != size_ || sizeUnits != sizeUnits_)) bannerSetSize(handle_, size, sizeUnits, nil); } /* * remember the creation parameters, so that we can re-create the * banner with the same characteristics in the future if we * should need to restore the banner from a saved position */ parentID_ = parentID; windowType_ = windowType; align_ = align; size_ = size; sizeUnits_ = sizeUnits; styleFlags_ = styleFlags; /* * Add myself to the persistent banner tracker's active list. Do * this even if we already had a system handle, since we might be * initializing the window as part of a persistent restore * operation, in which case the persistent tracking object might * not yet exist. (This seems backwards: if we're restoring a * persistent state, surely the persistent tracker would already * exist. In fact, the case we're really handling is where the * window is open in the transient UI, because it was already * open in the ongoing session; but the persistent state we're * restoring doesn't include the window. This is most likely to * occur after a RESTART, since we could have a window that is * always opened immediately at start-up and thus will be in the * transient state up to and through the RESTART, but is only * created as part of the initialization process.) */ bannerTracker.addBanner(self, parent, where, other); /* indicate success */ return true; } /* * Remove the banner. This removes the banner's on-screen window. * The BannerWindow object itself remains valid, but after this * method returns, the BannerWindow no longer has an associated * display window. * * Note that any child banners of ours will become undisplayable * after we're gone. A child banner depends upon its parent to * obtain display space, so once the parent is gone, its children no * longer have any way to obtain any display space. Our children * remain valid objects even after we're closed, but they won't be * visible on the display. */ removeBanner() { /* if I don't have a system-level handle, there's nothing to do */ if (handle_ == nil) return; /* remove my system-level banner window */ bannerDelete(handle_); /* our system-level window is gone, so forget its handle */ handle_ = nil; /* we only need an output stream when we're active */ outputStream_ = nil; /* remove myself from the UI trackers's active list */ bannerUITracker.removeBanner(getBannerID()); /* remove myself from the persistent banner tracker's active list */ bannerTracker.removeBanner(self); } /* write the given text to the banner */ writeToBanner(txt) { /* write the text to our underlying output stream */ outputStream_.writeToStream(txt); } /* flush any pending output to the banner */ flushBanner() { bannerFlush(handle_); } /* * Set the banner window to a specific size. 'size' is the new * size, in units given by 'sizeUnits', which is a BannerSizeXxx * constant. * * 'isAdvisory' is true or nil; if true, it indicates that the size * setting is purely advisory, and that a sizeToContents() call will * eventually follow to set the actual size. When 'isAdvisory is * true, the interpreter is free to ignore the request if * sizeToContents() */ setSize(size, sizeUnits, isAdvisory) { /* set the underlying system window size */ bannerSetSize(handle_, size, sizeUnits, isAdvisory); /* * remember my new size in case we have to re-create the banner * from a saved state */ size_ = size; sizeUnits_ = sizeUnits; } /* * Size the banner to its current contents. Note that some systems * do not support this operation, so callers should always make an * advisory call to setSize() first to set a size based on the * expected content size. */ sizeToContents() { /* size our system-level window to our contents */ bannerSizeToContents(handle_); } /* * Clear my banner window. This clears out all of the contents of * our on-screen display area. */ clearWindow() { /* clear our system-level window */ bannerClear(handle_); } /* set the text color in the banner */ setTextColor(fg, bg) { bannerSetTextColor(handle_, fg, bg); } /* set the screen color in the banner window */ setScreenColor(color) { bannerSetScreenColor(handle_, color); } /* * Move the cursor to the given row/column position. This can only * be used with text-grid banners; for ordinary text banners, this * has no effect. */ cursorTo(row, col) { bannerGoTo(handle_, row, col); } /* * Get the banner identifier. If our 'id_' property is set to nil, * we'll assume that we're a statically-defined object, in which case * 'self' is a suitable identifier. Otherwise, we'll return the * identifier string. */ getBannerID() { return id_ != nil ? id_ : self; } /* * Restore this banner. This is called after a RESTORE or UNDO * operation that finds that this banner was being displayed at the * time the state was saved but is not currently displayed in the * active UI. We'll show the banner using the characteristics saved * persistently. */ showForRestore(parent, where, other) { /* show myself, using my saved characteristics */ showBanner(parent, where, other, windowType_, align_, size_, sizeUnits_, styleFlags_); /* update my contents */ updateForRestore(); } /* * Create the system-level banner window. This can be customized as * needed, although this default implementation should be suitable * for most instances. * * Returns true if we are successful in creating the system window, * nil if we fail. */ createSystemBanner(parent, where, other, windowType, align, size, sizeUnits, styleFlags) { /* create the system-level window */ handle_ = bannerCreate(parent != nil ? parent.handle_ : nil, where, other != nil ? other.handle_ : nil, windowType, align, size, sizeUnits, styleFlags); /* if we got a valid handle, we succeeded */ return (handle_ != nil); } /* create our banner output stream */ createOutputStreamObj() { return new transient BannerOutputStream(handle_); } /* * Update my contents after being restored. By default, this does * nothing; instances might want to override this to refresh the * contents of the banner if the banner is normally updated only in * response to specific events. Note that it's not necessary to do * anything here if the banner will soon be updated automatically as * part of normal processing; for example, the status line banner is * updated at each new command line via a prompt-daemon, so there's * no need for the status line banner to do anything here. */ updateForRestore() { /* do nothing by default; subclasses can override as needed */ } /* * Initialize the banner window. This is called during * initialization (when first starting the game, or when resetting * with RESTART). If the banner is to be displayed from the start of * the game, this can set up the on-screen display. * * Note that we might already have an on-screen handle when this is * called. This indicates that we're restarting an ongoing session, * and that this banner already existed in the session before the * RESTART operation. If desired, we can attach ourselves to the * existing on-screen banner, avoiding the redrawing that would occur * if we created a new window. * * If this window depends upon another window for its layout order * placement (i.e., we'll call showBanner() with another BannerWindow * given as the 'other' parameter), then this routine should call the * other window's initBannerWindow() method before creating its own * window, to ensure that the other window has a system window and * thus will be meaningful to establish the layout order. * * Overriding implementations should check the 'inited_' property. * If this property is true, then it can be assumed that we've * already been initialized and don't require further initialization. * This routine can be called multiple times because dependent * windows might call us directly, before we're called for our * regular initialization. */ initBannerWindow() { /* by default, simply note that we've been initialized */ inited_ = true; } /* flag: this banner has been initialized with initBannerWindow() */ inited_ = nil /* * The creator-assigned ID string to identify the banner * persistently. This is only needed for banners created * dynamically; for BannerWindow objects defined statically at * compile time, simply leave this value as nil, and we'll use the * object itself as the identifier. */ id_ = nil /* the handle to my system-level banner window */ handle_ = nil /* * Creation parameters. We store these when we create the banner, * and update them as needed when the banner's display attributes * are changed. */ parentID_ = nil windowType_ = nil align_ = nil size_ = nil sizeUnits_ = nil styleFlags_ = nil ; /* ------------------------------------------------------------------------ */ /* * Banner Output Stream. This is a specialization of OutputStream that * writes to a banner window. */ class BannerOutputStream: OutputStream /* construct */ construct(handle) { /* inherit base class constructor */ inherited(); /* remember my banner window handle */ handle_ = handle; } /* execute preinitialization */ execute() { /* * We shouldn't need to do anything during pre-initialization, * since we should always be constructed dynamically by a * BannerWindow. Don't even inherit the base class * initialization, since it could clear out state that we want to * keep through a restart, restore, etc. */ } /* write text from the stream to the interpreter I/O system */ writeFromStream(txt) { /* write the text to the underlying system banner window */ bannerSay(handle_, txt); } /* our system-level banner window handle */ handle_ = nil ; /* ------------------------------------------------------------------------ */ /* * The banner UI tracker. This object keeps track of the current user * interface display state; this object is transient because the * interpreter's user interface is not part of the persistence * mechanism. */ transient bannerUITracker: object /* add a banner to the active display list */ addBanner(handle, ostr, id, parentID, where, other, windowType, align, styleFlags) { local uiWin; local parIdx; local idx; /* create a transient BannerUIWindow object to track the banner */ uiWin = new transient BannerUIWindow(handle, ostr, id, parentID, windowType, align, styleFlags); /* * Find the parent in the list. If there's no parent, the * parent is the main window; consider it to be at imaginary * index zero in the list. */ parIdx = (parentID == nil ? 0 : activeBanners_.indexWhich({x: x.id_ == parentID})); /* insert the banner at the proper point in our list */ switch(where) { case BannerFirst: /* * insert as the first child of the parent - put it * immediately after the parent in the list */ activeBanners_.insertAt(parIdx + 1, uiWin); break; case BannerLast: ins_last: /* * Insert as the last child of the parent: insert * immediately after the last window that descends from the * parent. */ activeBanners_.insertAt(skipDescendants(parIdx), uiWin); break; case BannerBefore: case BannerAfter: /* find the reference point ID in our list */ idx = activeBanners_.indexWhich( {x: x.id_ == other.getBannerID()}); /* * if we didn't find the reference point, or the reference * point item doesn't have the same parent as the new item, * then ignore the reference point and instead insert at the * end of the parent's child list */ if (idx == nil || activeBanners_[idx].parentID_ != parentID) goto ins_last; /* * if inserting after, skip the reference item and all * of its descendants */ if (where == BannerAfter) idx = skipDescendants(idx); /* insert at the position we found */ activeBanners_.insertAt(idx, uiWin); break; } } /* * Given an index in our list of active windows, skip the given item * and all items whose windows are descended from this window. * We'll leave the index positioned on the next entry in the list * that isn't a descendant of the window at the given index. Note * that this skips not only children but grandchildren (and so on) * as well. */ skipDescendants(idx) { local parentID; /* * if the index is zero, it's the main window; all windows are * children of the root window, so return the next index after * the last item */ if (idx == 0) return activeBanners_.length() + 1; /* note ID of the parent item */ parentID = activeBanners_[idx].id_; /* skip the parent item */ ++idx; /* keep going as long as we see children of the parent */ while (idx <= activeBanners_.length() && activeBanners_[idx].parentID_ == parentID) { /* * This is a child of the given parent, so we must skip it; * we must also skip its descendants, since they're all * indirectly descendants of the original parent. So, * simply skip this item and its descendants with a * recursive call to this routine. */ idx = skipDescendants(idx); } /* return the new index */ return idx; } /* remove a banner from the active display list */ removeBanner(id) { local idx; /* find the entry with the given ID, and remove it */ if ((idx = activeBanners_.indexWhich({x: x.id_ == id})) != nil) { local lastIdx; /* * After removing an item, its children are no longer * displayable, because a child obtains display space from * its parent. So, we must remove any children of this item * at the same time we remove the item itself. Find the * index of the next item after all of our descendants, so * that we can remove the item and its children all at once. * An item and its descendants are always contiguous in our * list, since we store children immediately after their * parents, so we can simply remove the range of items from * the specified item to its last descendant. * * Note that skipDescendants() returns the index of the * first item that is NOT a descendant; so, decrement the * result so that we end up with the index of the last * descendant. */ lastIdx = skipDescendants(idx) - 1; /* remove the item and all of its children */ activeBanners_.removeRange(idx, lastIdx); } } /* get the BannerUIWindow tracker object for a given BannerWindow */ getTracker(win) { local id; /* get the window's ID */ id = win.getBannerID(); /* return the tracker with the same ID as the given BannerWindow */ return activeBanners_.valWhich({x: x.id_ == id}); } /* check a BannerUIWindow to see if it matches the given layout order */ orderMatches(uiWin, where, otherID) { local idx; local otherIdx; local parentID; local parIdx; /* get the list index of the given window */ idx = activeBanners_.indexOf(uiWin); /* get the list index of the reference point window */ otherIdx = (otherID != nil ? activeBanners_.indexWhich({x: x.id_ == otherID}) : nil); /* * find the parent item (using imaginary index zero for the * root, which we can think of as being just before the first * item in the list) */ parentID = uiWin.parentID_; parIdx = (parentID == nil ? 0 : activeBanners_.indexWhich({x: x.id_ == parentID})); /* * if 'other' is specified, it has to have our same parent; if * it has a different parent, it's not a match */ if (otherID != nil && parentID != activeBanners_[otherIdx].parentID_) return nil; /* * if there's no such window in the list, it can't match the * given placement no matter what the given placement is, as it * has no placement */ if (idx == nil) return nil; /* check the requested layout order */ switch (where) { case BannerFirst: /* make sure it's immediately after the parent */ return idx == parIdx + 1; case BannerLast: /* * Make sure it's the last child of the parent. To do this, * make sure that the next item after this item's last * descendant is the same as the next item after the * parent's last descendant. */ return skipDescendants(idx) == skipDescendants(parIdx); case BannerBefore: /* * we want this item to come before 'other', so make sure * the next item after all of this item's descendants is * 'other' */ return skipDescendants(idx) == otherIdx; case BannerAfter: /* * we want this item to come just after 'other', so make * sure that the next item after all of the descendants of * 'other' is this item */ return skipDescendants(otherIdx) == idx; default: /* other layout orders are invalid */ return nil; } } /* * The vector of banners currently on the screen. This is a list of * transient BannerUIWindow objects, stored in the same order as the * banner layout list. */ activeBanners_ = static new transient Vector(32) ; /* * A BannerUIWindow object. This keeps track of the transient UI state * of a banner window while it appears on the screen. We create only * transient instances of this class, since it tracks what's actually * displayed at any given time. */ class BannerUIWindow: object /* construct */ construct(handle, ostr, id, parentID, windowType, align, styleFlags) { /* remember the banner's data */ handle_ = handle; outputStream_ = ostr; id_ = id; parentID_ = parentID; windowType_ = windowType; align_ = align; styleFlags_ = styleFlags; } /* the system-level banner handle */ handle_ = nil /* the banner's ID */ id_ = nil /* the parent banner's ID (nil if this is a top-level banner) */ parentID_ = nil /* * The banner's output stream. Output streams are always transient, * so hang on to each active banner's stream so that we can plug it * back in on restore. */ outputStream_ = nil /* creation parameters of the banner */ windowType_ = nil align_ = nil styleFlags_ = nil /* * Scratch-pad for our association to our BannerWindow object. We * only use this during the RESTORE process, to tie the transient * object back to the proper persistent object. */ win_ = nil ; /* * The persistent banner tracker. This keeps track of the active banner * windows persistently. Whenever we save or restore the game's state, * this object will be saved or restored along with the state. When we * restore a previously saved state, we can look at this object to * determine which banners were active at the time the state was saved, * and use this information to restore the same active banners in the * user interface. * * This is a post-restore and post-undo object, so we're notified via our * execute() method whenever we restore a saved state using RESTORE or * UNDO. When we restore a saved state, we'll restore the banner display * conditions as they existed in the saved state. */ bannerTracker: PostRestoreObject, PostUndoObject /* add a banner to the active display list */ addBanner(win, parent, where, other) { local parIdx; local otherIdx; /* * Don't add it if it's already in the list. If we're restoring * the banner from persistent state, it'll already be in the * active list, since the active list is the set of windows * we're restoring in the first place. */ if (activeBanners_.indexOf(win) != nil) return; /* find the parent among the existing windows */ parIdx = (parent == nil ? 0 : activeBanners_.indexOf(parent)); /* note the index of 'other' */ otherIdx = (other == nil ? nil : activeBanners_.indexOf(other)); /* insert the banner at the proper point in our list */ switch(where) { case BannerFirst: /* insert immediately after the parent */ activeBanners_.insertAt(parIdx + 1, win); break; case BannerLast: ins_last: /* insert after the parent's last descendant */ activeBanners_.insertAt(skipDescendants(parIdx), win); break; case BannerBefore: case BannerAfter: /* * if we didn't find the reference point, insert at the end * of the parent's child list */ if (otherIdx == nil) goto ins_last; /* * if inserting after, skip the reference item and all of * its descendants */ if (where == BannerAfter) otherIdx = skipDescendants(otherIdx); /* insert at the position we found */ activeBanners_.insertAt(otherIdx, win); break; } } /* * Skip all descendants of the window at the given index. */ skipDescendants(idx) { local parentID; /* index zero is the root item, so skip the entire list */ if (idx == 0) return activeBanners_.length() + 1; /* note the parent item */ parentID = activeBanners_[idx].getBannerID(); /* skip the parent item */ ++idx; /* keep going as long as we see children of the parent */ while (idx < activeBanners_.length() && activeBanners_[idx].parentID_ == parentID) { /* this is a child, so skip it and all of its descendants */ idx = skipDescendants(idx); } /* return the new index */ return idx; } /* remove a banner from the active list */ removeBanner(win) { local idx; local lastIdx; /* get the index of the item to remove */ idx = activeBanners_.indexOf(win); /* if we didn't find it, ignore the request */ if (idx == nil) return; /* find the index of its last descendant */ lastIdx = skipDescendants(idx) - 1; /* * remove the item and all of its descendants - child items * cannot be displayed once their parents are gone, so we can * remove all of this item's children, all of their children, * and so on, as they are becoming undisplayable */ activeBanners_.removeRange(idx, lastIdx); } /* * The list of active banners. This is a list of BannerWindow * objects, stored in banner layout list order. */ activeBanners_ = static new Vector(32) /* receive RESTORE/UNDO notification */ execute() { /* restore the display state for a non-initial state */ restoreDisplayState(nil); } /* * Restore the saved banner display state, so that the banner layout * looks the same as it did when we saved the persistent state. This * should be called after restoring a saved state, undoing to a * savepoint, or initializing (when first starting the game or when * restarting). */ restoreDisplayState(initing) { local uiVec; local uiIdx; local origActive; /* get the list of banners active in the UI */ uiVec = bannerUITracker.activeBanners_; /* * First, go through all of the persistent BannerWindow objects. * For each one whose ID shows up in the active UI display list, * tell the BannerWindow object its current UI handle. */ forEachInstance(BannerWindow, function(cur) { local uiCur; /* find this banner in the active UI list */ uiCur = uiVec.valWhich({x: x.id_ == cur.getBannerID()}); /* * if the window exists in the active UI list, note the * current system handle for the window; otherwise, we have * no system window, so set the handle to nil */ if (uiCur != nil) { /* note the current system banner handle */ cur.handle_ = uiCur.handle_; /* re-establish the banner's active output stream */ cur.outputStream_ = uiCur.outputStream_; /* tie the transient record to the current 'cur' */ uiCur.win_ = cur; } else { /* it's not shown, so it has no system banner handle */ cur.handle_ = nil; /* it has no output stream */ cur.outputStream_ = nil; } }); /* * * 'initing' indicates whether we're initializing (startup or * RESTART) or doing something else (RESTORE, UNDO). When * initializing, if there are any banners on-screen, we'll give * their associated BannerWindow objects (if any) a chance to set * up their initial conditions; this allows us to avoid * unnecessary redrawing if we have banners that we'd immediately * set up to the same conditions anyway, since we can just keep * the existing banners rather than removing and re-creating * them. * * So, if we're initializing, tell each banner that it's time to * set up its initial display. */ if (initing) forEachInstance(BannerWindow, {cur: cur.initBannerWindow()}); /* * scan the active UI list, and close each window that isn't * still open in the saved state */ foreach (local uiCur in uiVec) { /* if this window isn't in the active list, close it */ if (activeBanners_.indexWhich( {x: x.getBannerID() == uiCur.id_}) == nil) { /* * There's no banner in the persistent list with this * ID, so this window is not part of the state we're * restoring. Close the window. If we have an * associated BannerWindow object, close through the * window object; otherwise, close the system handle * directly. */ if (uiCur.win_ != nil) { /* we have a BannerWindow - close it */ uiCur.win_.removeBanner(); } else { /* there's no BannerWindow - close the system window */ bannerDelete(uiCur.handle_); /* remove the UI tracker object */ uiVec.removeElement(uiCur); } } } /* start at the first banner actually displayed right now */ uiIdx = 1; /* * make a copy of the original active list - we might modify the * actual active list in the course of restoring things, so make * a copy that we can refer to as we reconstruct the original * list */ origActive = activeBanners_.toList(); /* * Scan the saved list of banners, and restore each one. Note * that by restoring windows in the order in which they appear * in the list, we ensure that we always restore a parent before * restoring any of its children, since a child always follows * its parent in the list. */ for (local curIdx = 1, local aLen = origActive.length() ; curIdx <= aLen ; ++curIdx) { local redisp; local cur; /* get the current item */ cur = origActive[curIdx]; /* presume we will have to redisplay this banner */ redisp = true; /* * If this banner matches the current banner in the active * UI display list, and the characteristics match, we need * do nothing, as we're already displaying this banner * properly. If the current active UI banner doesn't match, * then we need to insert this saved banner at the current * active UI position. */ if (uiVec.length() >= uiIdx) { local uiCur; /* get this current UI display item (a BannerUIWindow) */ uiCur = uiVec[uiIdx]; /* check for a match to 'cur' */ if (uiCur.id_ == cur.getBannerID() && uiCur.parentID_ == cur.parentID_ && uiCur.windowType_ == cur.windowType_ && uiCur.align_ == cur.align_ && uiCur.styleFlags_ == cur.styleFlags_) { /* * This saved banner ('cur') exactly matches the * active UI banner ('uiCur') at the same position * in the layout list. Therefore, we do not need to * redisplay 'cur'. */ redisp = nil; } } /* if we need to redisplay 'cur', do so */ if (redisp) { local prvIdx; local where; local other; local parent; /* * If 'cur' is already being displayed, we must remove * it before showing it anew. This is the only way to * ensure that we display it with the proper * characteristics, since the characteristics of the * current instance of its window don't match up to what * we want to restore. */ if (cur.handle_ != nil) cur.removeBanner(); /* * Figure out how to specify this window's display list * position. A display list position is always * specified relative to the parent's child list, so * figure out where we go in our parent's list. Scan * backwards in the active list for the nearest previous * window with the same parent. If we find one, insert * the new window after that prior sibling; otherwise, * insert as the first child of our parent. Presume * that we'll fail to find a prior sibling, then search * for it and search for our parent. */ where = BannerFirst; other = nil; for (prvIdx = curIdx - 1 ; prvIdx > 0 ; --prvIdx) { local prv; /* note this item */ prv = origActive[prvIdx]; /* * If this item has our same parent, and we haven't * already found a prior sibling, this is our most * recent prior sibling, so note it. */ if (where == BannerFirst && prv.parentID_ == cur.parentID_) { /* insert after this prior sibling */ where = BannerAfter; other = prv; } /* if this is our parent, note it */ if (prv.getBannerID() == cur.parentID_) { /* * note the parent BannerWindow object - we'll * need it to specify our window display * position */ parent = prv; /* * Children of a given parent always come after * the parent in the display list, so there's no * possibility of finding another sibling. * There's also obviously no possibility of * finding another parent. So, our work here is * done; we can stop scanning. */ break; } } /* show the window */ cur.showForRestore(parent, where, other); } else { /* * the banner is already showing, so we don't need to * redisplay it; simply notify it that a Restore * operation has taken place so that it can do any * necessary updates */ cur.updateForRestore(); } /* * 'cur' should now be displayed, but we might have failed * to re-create it. If we did show the window, we can * advance to the next slot in the UI list, since this * window will necessarily be at the current spot in the UI * list. */ if (cur.handle_ != nil) { /* * We know that this window is now the entry in the * active UI list at the current index we're looking at. * Move on to the next position in the active list for * the next saved window. */ ++uiIdx; } } } ; /* * Initialization object - this will be called when we start the game the * first time or RESTART within a session. We'll restore the display * state to the initial conditions. */ bannerInit: InitObject execute() { /* restore banner displays to their initial conditions */ bannerTracker.restoreDisplayState(true); } ; frobtads-1.2.3/tads3/lib/adv3/verify.t0000644000175000001440000006300510667223710016626 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: verification * * This module defines classes related to "verification," which is the * phase of command execution where the parser attempts to determine how * logical a command is. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Verification result class. Verification routines return a * verification result describing whether or not an action is allowed, * and how much sense the command seems to make. When a verification * fails, it must include a message describing why the command isn't * allowed. * * It is important to understand that the purpose of verification * results is to guess what's in the player's mind, not to reflect the * full internal state of the game. We use verification results to * figure out what a player means with a command, so if we were to rely * on information the player doesn't have, we would not correctly guess * the player's intentions. So, in choosing a verification result, only * information that ought to be obvious to the player should be * consdidered. * * For example, suppose we have a closed door; suppose further that the * door happens to be locked, but that there's no way for the player to * see that just by looking at the door. Now, if the player types * "close door," we should return "currently illogical" - common sense * tells the player that the door is something that can be opened and * closed, so we wouldn't return "always illogical," but the player can * plainly see that the door is already closed and thus would know that * it makes no sense to close it again. In other words, the player * would conclude looking at the door that closing it is currently * illogical, so that's the result we should generate. * * What if the player types "open door," though? In this case, should * we return "currently illogical" as well, because the door is locked? * The answer is no. We know that the command won't succeed because we * know from looking at the internal game state that the door is locked, * but that doesn't matter - it's what the *player* knows that's * important, not what the internal game state tells us. So, what * should we return here? It might seem strange, but the correct result * is "logical" - as far as the player is concerned, the door is * something that can be opened and closed, and it is currently closed, * so it makes perfect sense to open it. */ class VerifyResult: MessageResult /* * Is the action allowed? This returns true if the command can be * allowed to proceed on the basis of the verification, nil if not. */ allowAction = true /* * Is the action allowed as an implicit action? This returns true * if the command can be allowed to proceed AND the command can be * undertaken simply because it's implied by another command, even * though the player never explicitly entered the command. We * distinguish this from allowAction so that we can prevent certain * actions from being undertaken implicitly; we might want to * disallow an implicit action when our best guess is that a player * should know better than to perform an action because it's * obviously dangerous. */ allowImplicit { /* * by default, any allowable action is also allowed as an * implicit action */ return allowAction; } /* * Am I worse than another result? Returns true if this result is * more disapproving than the other. */ isWorseThan(other) { /* I'm worse if my result ranking is lower */ return (resultRank < other.resultRank); } /* * compare to another: negative if I'm worse than the other, zero if * we're the same, positive if I'm better */ compareTo(other) { /* compare based on result rankings */ return resultRank - other.resultRank; } /* * Determine if I should appear in a result list before the given * result object. By default, this is true if I'm worse than the * given result, but some types of results use special sorting * orders. */ shouldInsertBefore(other) { /* * by default, I come before the other in a result list if I'm * worse than the other, because we keep result lists in order * from worst to best */ return compareTo(other) < 0; } /* * Determine if I'm identical to another result. Note that it's * possible for two items to compare the same but not be identical - * compareTo() is concerned only with logicalness ranking, but * identicalTo() determines if the two items are exactly the same. * Some subclasses (such as LogicalVerifyResult) distinguish among * items that compare the same but have different reasons for their * rankings. */ identicalTo(other) { /* by default, I'm identical if my comparison shows I rank the same */ return compareTo(other) == 0; } /* * Our result ranking relative to other results. Each result class * defines a ranking level so that we can determine whether one * result is better (more approving) or worse (more disapproving) * than another. * * To allow easy insertion of new library extension result types or * game-specific result types, we assign widely spaced rankings to * the pre-defined results. This is arbitrary; the only thing that * matters in comparing two results is the order of the rank values. */ resultRank = nil /* * Should we exclude plurals from being matched, when this type of * result is present? By default, we don't; some illogical types * might want to exclude plurals because the result types indicate * such obvious illogicalities. */ excludePluralMatches = nil ; /* * Verification result - command is logical and allowed. * * This can provide additional information ranking the likelihood of the * command intepretation, which can be useful to distinguish among * logical but not equally likely possibilities. For example, if the * command is "take book," and the actor has a book inside his or her * backpack, and there is also a book on a table in the actor's * location, it would make sense to take either book, but the game might * prefer to take the book on the table because it's not already being * carried. The likelihood level can be used to rank these * alternatives: if the object is being carried indirectly, a lower * likelihood ranking would be returned than if the object were not * already somewhere in the actor's inventory. */ class LogicalVerifyResult: VerifyResult construct(likelihoodRank, key, ord) { /* remember my likelihood ranking */ likelihood = likelihoodRank; /* remember my key value */ keyVal = key; /* remember my list order */ listOrder = ord; } /* am I worse than the other result? */ isWorseThan(other) { /* * I'm worse if my result ranking is lower; or, if we are both * LogicalVerifyResult objects, I'm worse if my likelihood is * lower. */ if (resultRank == other.resultRank) return likelihood < other.likelihood; else return inherited(other); } /* compare to another result */ compareTo(other) { /* * if we're not both of the same rank (i.e., 'logical'), inherit * the default comparison */ if (resultRank != other.resultRank) return inherited(other); /* * we're both 'logical' results, so compare based on our * respective likelihoods */ return likelihood - other.likelihood; } /* determine if I go in a result list before the given result */ shouldInsertBefore(other) { /* if we're not both of the same rank, use the default handling */ if (resultRank != other.resultRank) return inherited(other); /* * we're both 'logical' results, so order in the list based on * our priority ordering; if we're of the same priority, use the * default ordering */ if (listOrder != other.listOrder) return listOrder < other.listOrder; else return inherited(other); } /* determine if I'm identical to another result */ identicalTo(other) { /* * I'm identical if I compare the same and my key value is the * same. */ return compareTo(other) == 0 && keyVal == other.keyVal; } /* * The likelihood of the command - the higher the number, the more * likely. We use 100 as the default, so that there's plenty of * room for specific rankings above or below the default. Particular * actions might want to rank likelihoods based on action-specific * factors. */ likelihood = 100 /* * Our list ordering. This establishes how we are entered into the * master results list relative to other 'logical' results. Results * are entered into the master list in ascending list order, so a * lower order number means an earlier place in the list. * * The list ordering is more important than the likelihood ranking. * Suppose we have two items: one is at list order 10 and has * likelihood 100, and the other is at list order 20 and has * likelihood 50. The order of the likelihoods stored in the list * will be (100, 50). This is inverted from the normal ordering, * which would put the worst item first. * * The point of this ordering is to allow for logical results with * higher or lower importances in establishing the likelihood. The * library uses the following list order values: * * 100 - the default ranking. This is used in most cases. * * 150 - secondary ranking. This is used for rankings that aren't * of great importance but which can be useful to distinguish * objects in cases where no more important rankings are present. * The library uses this for precondition verification rankings. */ listOrder = 100 /* * my key value, to distinguish among different results with the * same likelihood ranking */ keyVal = '' /* result rank - we're the most approving kind of result */ resultRank = 100 ; /* * Verification result - command is logical and allowed, but is * dangerous. As with all verify results, this should reflect our best * guess as to the player's intentions, so this should only be used when * it is meant to be obvious to the player that the action is dangerous. */ class DangerousVerifyResult: VerifyResult /* * don't allow dangerous actions to be undertaken implicitly - we do * allow these actions, but only when explicitly requested */ allowImplicit = nil /* result rank - we're only slightly less approving than 'logical' */ resultRank = 90 /* this result indicates danger */ isDangerous = true ; /* * Verification result - command is currently illogical due to the state * of the object, but might be logically applied to the object at other * times. For example, "open door" on a door that's already open is * illogical at the moment, but makes more sense than opening something * that has no evident way to be opened or closed to begin with. */ class IllogicalNowVerifyResult: VerifyResult /* the command isn't allowed */ allowAction = nil /* result rank */ resultRank = 40 ; /* * Verification result - command is currently illogical, because the * state that the command seeks to impose already obtains. For example, * we're trying to open a door that's already open, or drop an object * that we're not carrying. * * This is almost exactly the same as an "illogical now" result, so this * is a simple subclass of that result type. We act almost the same as * an "illogical now" result; the only reason to distinguish this type is * that it's an especially obvious kind of condition, so we might want to * use it to exclude some vocabulary matches that we wouldn't normally * exclude for the more general "illogical now" result type. */ class IllogicalAlreadyVerifyResult: IllogicalNowVerifyResult /* exclude plural matches when this result type is present */ excludePluralMatches = true ; /* * Verification result - command is always illogical, regardless of the * state of the object. "Close fish" might fall into this category. */ class IllogicalVerifyResult: VerifyResult /* the command isn't allowed */ allowAction = nil /* result rank - this is the most disapproving of the disapprovals */ resultRank = 30 ; /* * Verification result - command is always illogical, because it's trying * to use an object on itself in some invalid way, as in PUT BOX IN BOX. * * This is almost identical to a regular always-illogical result, so * we're a simple subclass of that result type. We distinguish these * from the basic always-illogical type because it's especially obvious * that the "self" kind is illogical, so we might in some cases want to * exclude a vocabulary match for the "self" kind that we wouldn't * exclude for the basic kind. */ class IllogicalSelfVerifyResult: IllogicalVerifyResult /* exclude plural matches when this result type is present */ excludePluralMatches = true ; /* * Verification result - command is logical and allowed, but is * non-obvious on this object. This should be used when the command is * logical, but should not be obvious to the player. When this * verification result is present, the command is allowed when performed * explicitly but will never be taken as a default. * * In cases of ambiguity, a non-obvious object is equivalent to an * always-illogical object. A non-obvious object *appears* to be * illogical at first glance, so we want to treat it the same as an * ordinarily illogical object if we're trying to choose among ambiguous * objects. */ class NonObviousVerifyResult: VerifyResult /* * don't allow non-obvious actions to be undertaken implicitly - we * allow these actions, but only when explicitly requested */ allowImplicit = nil /* * non-obvious objects are illogical at first glance, so rank them * the same as objects that are actually illogical */ resultRank = (IllogicalVerifyResult.resultRank) ; /* * Verification result - object is inaccessible. This should be used * when a command is applied to an object that is not accessibile in a * sense required for the command; for example, "look at" requires that * its target object be visible, so a "look at" command in the dark * would fail with this type of result. */ class InaccessibleVerifyResult: VerifyResult /* the command isn't allowed */ allowAction = nil /* * This ranks below any illogical result - inaccessibility is a * stronger disapproval than mere illogicality. */ resultRank = 10 ; /* * Default 'logical' verify result. If a verification result list * doesn't have an explicitly set result, this is the default value. */ defaultLogicalVerifyResult: LogicalVerifyResult showMessage() { /* the default logical result has no message */ } keyVal = 'default' ; /* * Verification result list. */ class VerifyResultList: object construct() { /* initialize the results vector */ results_ = new Vector(5); } /* * Add a result to our result list. */ addResult(result) { local i; /* * Find the insertion point. We want to keep the results sorted * in order from worst to best, so insert this result before the * first item in our list that's better than this item. */ for (i = 1 ; i <= results_.length() ; ++i) { /* * if it's exactly the same as this item, don't add it - * keep only one of each unique result */ if (result.identicalTo(results_[i])) return; /* * If the new result is to be inserted before the result at * the current index, insert the new result at the current * index. */ if (result.shouldInsertBefore(results_[i])) break; } /* add the result to our list at the index we found */ results_.insertAt(i, result); } /* * Is the action allowed? We return true if we have no results; * otherwise, we allow the action if *all* of our results allow it, * nil if even one disapproves. */ allowAction() { /* approve if the effective result approves */ return results_.indexWhich({x: !x.allowAction}) == nil; } /* * Do we exclude plural matches? We do if we have at least one * result that excludes plural matches. */ excludePluralMatches() { /* exclude plural matches if we have any result that says to */ return results_.indexWhich({x: x.excludePluralMatches}) != nil; } /* * Is the action allowed as an implicit action? Returns true if we * have no results; otherwise, returns true if *all* of our results * allow the implicit action, nil if even one disapproves. */ allowImplicit() { /* search for disapprovals; if we find none, allow it */ return results_.indexWhich({x: !x.allowImplicit}) == nil; } /* * Show the message. If I have any results, we'll show the message * for the effective (i.e., most disapproving) result; otherwise we * show nothing. */ showMessage() { local res; /* * Find the first result that disapproves. Only disapprovers * will have messages, so we need to find a disapprover. * Entries are in ascending order of approval, and we want the * most disapproving disapprover, so take the first one we find. */ if ((res = results_.valWhich({x: !x.allowAction})) != nil) res.showMessage(); } /* * Get my effective result object. If I have no explicitly-set * result object, my effective result is the defaut logical result. * Otherwise, we return the most disapproving result in our list. */ getEffectiveResult() { /* if our list is empty, return the default logical result */ if (results_.length() == 0) return defaultLogicalVerifyResult; /* * return the first item in the list - we keep the list sorted * from worst to best, so the first item is the most * disapproving result we have */ return results_[1]; } /* * Compare my cumulative result (i.e., my most disapproving result) * to that of another result list's cumulative result. Returns a * value suitable for sorting: -1 if I'm worse than the other one, 0 * if we're the same, and 1 if I'm better than the other one. This * can be used to compare the cumulative verification results for * two objects to determine which object is more logical. */ compareTo(other) { local lst1; local lst2; local idx; /* get private copies of the two lists */ lst1 = results_.toList(); lst2 = other.results_.toList(); /* keep going until we find differing items or run out of items */ for (idx = 1 ; idx <= lst1.length() || idx <= lst2.length(); ++idx) { local a, b; local diff; /* * Get the current item from each list. If we're past the * end of one or the other list, use the default logical * result as the current item from that list. */ a = idx <= lst1.length() ? lst1[idx] : defaultLogicalVerifyResult; b = idx <= lst2.length() ? lst2[idx] : defaultLogicalVerifyResult; /* * If the two items have distinct rankings, simply return * the sense of the ranking. */ if ((diff = a.compareTo(b)) != 0) return diff; /* * The two items at the current position have equivalent * rankings, so ignore them for the purposes of comparing * these two items. Simply proceed to the next item in each * list. Before we do, though, check to see if we can * eliminate that current item from our own list - if we * have an identical item (not just ranked the same, but * actually identical) in the other list, throw the item out * of both lists. */ for (local j = 1 ; j < lst2.length() ; ++j) { /* * if this item in the other list is identical to the * current item from our list, throw out both items */ if (lst2[j].identicalTo(a)) { /* remove the items from both lists */ lst1 -= a; lst2 -= lst2[j]; /* consider the new current item at this position */ --idx; /* no need to scan any further */ break; } } } /* * We've run out of items in both lists, so everything must have * been identical in both lists. Since we have no 'verify' basis * for preferring one object over the other, fall back on our * intrinsic vocabLikelihood values as a last resort. */ return obj_.obj_.vocabLikelihood - other.obj_.obj_.vocabLikelihood; } /* * Determine if we match another verify result list after remapping. * This determines if the other verify result is equivalent to us * after considering the effects of remapping. We'll return true if * all of the following are true: * * - compareTo returns zero, indicating that we have the same * weighting in the verification results * * - we refer to the same object after remapping; the effective * object after remapping is our original resolved object, if we're * not remapped, or our remap target if we are * * - we use the object for the same action and in the same role * * Note: this can only be called on remapped results. Results can * only be combined in the first place when remapped, so there's no * need to ever call this on an unremapped result. */ matchForCombineRemapped(other, action, role) { /* if our verification values aren't identical, we can't combine */ if (compareTo(other) != 0) return nil; /* * check the other - how we check depends on whether it's been * remapped or not */ if (other.remapTarget_ == nil) { /* * The other one hasn't been remapped, so our remapped * object, action, and role must match the other's original * object, action, and role. Note that to compare the two * actions, we compare their baseActionClass properties, * since this property gives us the identifying canonical * base class for the actions. */ return (remapTarget_ == other.obj_.obj_ && remapAction_.baseActionClass == action.baseActionClass && remapRole_ == role); } else { /* * The other one has been remapped as well, so our remapped * object, action, and role must match the other's remapped * object, action, and role. */ return (remapTarget_ == other.remapTarget_ && remapAction_.baseActionClass == other.remapAction_.baseActionClass && remapRole_ == other.remapRole_); } } /* * The remapped target object. This will filled in during * verification if we decide that we want to remap the nominal * object of the command to a different object. This should be set * to the ultimate target object after all remappings. */ remapTarget_ = nil /* the action and role of the remapped action */ remapAction_ = nil remapRole_ = nil /* our list of results */ results_ = [] /* * The ResolveInfo for the object being verified. Note that this * isn't saved until AFTER the verification is completed. */ obj_ = nil /* * The original list index for this result. We use this when sorting * a list of results to preserve the original ordering of otherwise * equivalent items. */ origOrder = 0 ; frobtads-1.2.3/tads3/lib/adv3/status.t0000644000175000001440000004737111441461404016650 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - Status Line * * This module defines the framework for displaying the status line, * which is the area conventionally displayed at the top of the screen * showing information such as the current location, score (if scoring is * used at all), and number of turns. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * In case the 'score' module isn't included, make sure we can refer to * totalScore as a property. Likewise for the banner API and Web UI * frame API. */ property totalScore; property showBanner, setSize, sizeToContents; property flushWin, resize; /* ------------------------------------------------------------------------ */ /* * A special OutputStream for the tag contents. This is really * just part of the main output stream, but we use a separate output * stream object so that we have our own separate stream state variables * (for paragraph breaking and so forth). */ transient statusTagOutputStream: OutputStream /* * We're really part of the main window's output stream as far as the * underlying interpreter I/O system is concerned, so we have to * coordinate with the main game window's input manager. */ myInputManager = inputManager /* we sit atop the system-level main console output stream */ writeFromStream(txt) { /* write the text directly to the main output stream */ tadsSay(txt); } ; /* * A special OutputStream for the left half of the status line (the * short description area) in text mode. We use a separate stream for * this because we must write the text using the output mode switching * for the status line. * * We only use this stream when we use the old-style text-mode status * line interface, which explicitly separates the status line into a * left part and a right part. When we have the banner API available in * the interpreter, we'll use banners instead, since banners give us * much more flexibility. */ transient statusLeftOutputStream: OutputStream /* we sit atop the system-level main console output stream */ writeFromStream(txt) { /* write the text directly to the main output stream */ tadsSay(txt); } ; /* * A special OutputStream for the right half of the status line (the * score/turn count area) in text mode. We use a separate stream for * this because we have to write this text with the special * statusRight() intrinsic in text mode. * * We only use this stream when we use the old-style text-mode status * line interface, which explicitly separates the status line into a * left part and a right part. When we have the banner API available in * the interpreter, we'll use banners instead, since banners give us * much more flexibility. */ transient statusRightOutputStream: OutputStream /* * Write from the stream. We simply buffer up text until we're * asked to display the final data. */ writeFromStream(txt) { /* buffer the text */ buf_ += txt; } /* * Flush the buffer. This writes whatever we've buffered up to the * right half of the text-mode status line. */ flushStream() { /* write the text to the system console */ statusRight(buf_); /* we no longer have anything buffered */ buf_ = ''; } /* our buffered text */ buf_ = '' ; /* ------------------------------------------------------------------------ */ /* * Statusline modes. We have three different ways to display the * statusline, depending on the level of support in the interpreter. * * StatusModeApi - use the banner API. This is preferred method, because * it gives us uniform capabilities on text and graphical interpreters, * and provides an output stream for the statusline that is fully * independent on the main game window's output stream. * * StatusModeTag - use the tag. This is the method we must use * for HTML-enabled interpreters that don't support the banner API. This * gives us the full formatting capabilities of HTML, but isn't as good * as StatusModeApi because we have to share our output stream with the * main game window. * * StatusModeText - use the old-style dedicated statusline in a text-only * interpreter. This is the least desirable method, because it gives us * a rigid format for the statusline (exactly one line high, no control * over colors, and with the strict left/right bifurcation). We'll only * use this method if we're on a text-only interpreter that doesn't * support the banner API. * * StatusModeBrowser - use the Web UI. This is similar to API mode, but * instead of using banner windows we use Web UI windows, which are * implemented as IFRAMEs in the browser. */ enum StatusModeApi, StatusModeTag, StatusModeText, StatusModeBrowser; /* ------------------------------------------------------------------------ */ /* * Status line - this is an abstract object that controls the status line * display. * * We provide two main methods: showStatusHtml, which shows the status * line in HTML format, and showStatusText, which shows the status line * in plain text mode. To display the status line, we invoke one or the * other of these methods, according to the current mode, to display the * statusline. The default implementations of these methods generate the * appropriate formatting codes for a statusline with a left part and a * right part, calling showStatusLeft and showStatusRight, respectively, * to display the text for the parts. * * Games can customize the statusline at two levels. At the simpler * level, a game can modify showStatusLeft and/or showStatusRight to * change the text displayed on the left and/or right of the statusline. * Since these two methods are used regardless of the statusline style of * the underlying interpreter, games don't have to worry about the * different modes when overriding these. * * At the more complex level, a game can modify showStatusHtml and/or * showStatusText. Modifying these routines provides complete control * over the formatting of the entir status line. If a game wants to use * something other than the traditional left/right display, it must * modify these methods. * * This object is transient, because the statusline style is a function * of the interpreter we're currently running on, and thus isn't suitable * for saving persistently. */ transient statusLine: object /* * Show the status line, in HTML or text mode, as appropriate. By * default, the library sets this up as a "prompt daemon," which * means that this will be called automatically just before each * command line is read. */ showStatusLine() { local oldStr; /* if the status line isn't active, or there's no PC, skip this */ if (statusDispMode == nil || gPlayerChar == nil) return; /* * showing the status line doesn't normally change any game * state, so we can turn on the sense cache while generating the * display */ libGlobal.enableSenseCache(); /* * Enter status-line mode. This will do whatever is required for * our current status-line display style to prepare the output * manager so that any text we display to the default output * stream is displayed on the status line. */ oldStr = beginStatusLine(); /* make sure we restore statusline mode before we're done */ try { /* * Generate a text or HTML status line, as appropriate. If * we're in tag mode or banner API mode, use HTML to * format the contents of the status line; if we're using the * old-style text mode, use plain text, since the formatting * is rigidly defined in this mode. */ switch (statusDispMode) { case StatusModeTag: case StatusModeApi: case StatusModeBrowser: /* show the HTML status line */ showStatusHtml(); break; case StatusModeText: /* show the status line in plain text mode */ showStatusText(); break; } } finally { /* end status-line mode */ endStatusLine(oldStr); /* turn off sense caching */ libGlobal.disableSenseCache(); } } /* prompt-daemon showing the status line */ showStatusLineDaemon() { /* show the status line as normal */ showStatusLine(); /* * Explicitly flush the status line if it's in a banner window. * This will ensure that we'll redraw the status line on each * turn if we're reading an input script, which is nice because * it provides a visual indication that something's happening. */ if (statusDispMode is in (StatusModeApi, StatusModeBrowser)) statuslineBanner.flushBanner(); } /* * Show the status line in HTML format. Our default implementation * shows the traditional two-part (left/right) status line, using * showStatusLeft() and showStatusRight() to display the parts. */ showStatusHtml() { /* * start the left half, and write the to hyperlink the * location name to a "look around" command */ "<><< aHref(gLibMessages.commandLookAround, nil, nil, AHREF_Plain)>>"; /* show the left part of the status line */ showStatusLeft(); /* * end the left portion and start the right portion, then * generate the to link the score to a FULL SCORE * command */ "<./a><><< aHref(gLibMessages.commandFullScore, nil, nil, AHREF_Plain)>>"; /* show the right part of the status line */ showStatusRight(); /* end the score link, and end the right half wrapper */ "<./a><>"; /* add the status-line exit list, if desired */ if (gPlayerChar.location != nil) gPlayerChar.location.showStatuslineExits(); } /* * Get the estimated HTML-style banner height, in lines of text. * This is used to set the status line banner size for platforms * where sizing to the exact height of the rendered contents isn't * supported. * * If showStatusHtml() is overridden to display more or fewer lines * of text than the basic implementation here, then this routine must * be overridden as well to reflect the new height. */ getEstimatedHeightHtml() { local ht; /* * we need one line for the basic display (the location name and * score/turn count) */ ht = 1; /* add in the estimated height of the exits display, if appropriate */ if (gPlayerChar.location != nil) ht += gPlayerChar.location.getStatuslineExitsHeight(); /* return the result */ return ht; } /* * Show the statusline in text mode. Our default implementation * shows the traditional two-part (left/right) status line, using * showStatusLeft() and showStatusRight() to display the parts. */ showStatusText() { /* show the left part of the display */ showStatusLeft(); /* switch to the right-side status stream */ outputManager.setOutputStream(statusRightOutputStream); /* show the right-half text */ showStatusRight(); /* flush the right-side stream */ statusRightOutputStream.flushStream(); } /* * Show the left part of a standard left/right statusline. By * default, we'll show the player character's location, by calling * statusName() on the PC's immediate container. */ showStatusLeft() { local actor; /* get the player character actor */ actor = gPlayerChar; "<.statusroom>"; /* show the actor's location's status name */ if (actor != nil && actor.location != nil) actor.location.statusName(actor); "<./statusroom>"; } /* * Show the right part of a standard left/right statusline. By * default, we'll show the current score, a slash, and the number of * turns. */ showStatusRight() { local s; /* if there's a score object, show the score */ if ((s = libGlobal.scoreObj) != nil) { /* show the score and the number of turns so far */ "<.statusscore><>/<< libGlobal.totalTurns>><./statusscore>"; } } /* * Set up the status line's color scheme. This is called each time * we redraw the status line to set the background and text colors. * We call the statusline banner window to do the work, since the * mechanism is different between the traditional and Web UIs. */ setColorScheme() { /* call the banner window to do the work */ statuslineBanner.setColorScheme(); } /* * Begin status-line mode. This sets up the output manager so that * text written to the default output stream is displayed on the * status line. Returns the original output stream. */ beginStatusLine() { local oldStr; /* check what kind of statusline display we're using */ switch(statusDispMode) { case StatusModeApi: /* * We have a banner API window. Start by clearing the * window, so we can completely replace everything in it. */ statuslineBanner.clearWindow(); /* * If the platform doesn't support size-to-contents, then set * the height to our best estimate for the size. * * If we do support size-to-contents, we'll set the height to * the exact rendered size when we're done, so we don't need * to worry about setting an estimate; indicate this to the * interpreter by setting the is-advisory flag to true. */ statuslineBanner.setSize(getEstimatedHeightHtml(), BannerSizeAbsolute, true); /* switch to the banner's output stream */ oldStr = statuslineBanner.setOutputStream(); /* set up the statusline color in the window */ setColorScheme(); /* done */ break; case StatusModeBrowser: /* browser UI - clear the window */ statuslineBanner.clearWindow(); /* switch to its output stream */ oldStr = statuslineBanner.setOutputStream(); break; case StatusModeTag: /* * We're using tags. Switch to our statusline * output stream. */ oldStr = outputManager.setOutputStream(statusTagOutputStream); /* set up the tag */ ""; /* set up the color scheme */ setColorScheme(); /* done */ break; case StatusModeText: /* flush the main window */ flushOutput(); /* plain text mode - enter text status mode */ statusMode(StatModeStatus); /* switch to the status-left output stream */ oldStr = outputManager.setOutputStream(statusLeftOutputStream); /* done */ break; } /* return the original output stream */ return oldStr; } /* end statusline display */ endStatusLine(oldStr) { /* restore the old default output stream */ outputManager.setOutputStream(oldStr); /* check the type of statusline we're generating */ switch (statusDispMode) { case StatusModeApi: /* banner API mode - end the last line */ statuslineBanner.writeToBanner('\n'); /* * Size the window to its current contents. This doesn't * work everywhere - on a few platforms, it does nothing - * but this will give us the optimal size where it's * supported. On platforms that don't support this, it'll do * nothing, which means we'll simply be left with the * "advisory" size we established earlier. */ statuslineBanner.sizeToContents(); break; case StatusModeBrowser: /* browser mode - flush the window and update the content size */ statuslineBanner.flushWin(); break; case StatusModeTag: /* HTML mode - end the tag */ statusTagOutputStream.writeToStream(''); break; case StatusModeText: /* plain text statusline - end status mode */ statusMode(StatModeNormal); } } /* * Initialize the banner window, given the BannerWindow object * representing the status line banner API window. */ initBannerWindow(win) { /* * Try showing the banner API window. If that succeeds, use the * banner API window, since it's the most portable and flexible * way to show the status line. If we can't create the banner * API window, it means we're on an interpreter that doesn't * support the banner API, so fall back on one of the older, less * flexible mechanisms; which older mechanism we choose depends * on what kind of interpreter we're on. * * Since we create the status line banner during initialization * and normally leave it as the first item in the display list at * all times, we can attach to an existing status line banner * window if there is one. This will avoid unnecessary redrawing * on RESTART. */ if (win.showBanner(nil, BannerFirst, nil, BannerTypeText, BannerAlignTop, nil, nil, BannerStyleBorder | BannerStyleTabAlign)) { /* * we successfully created the banner window - use the banner * API to show the status line */ statusDispMode = StatusModeApi; } else if (outputManager.htmlMode) { /* * We failed to create a banner API window, and we're running * on a full HTML interpreter, so use tags to * produce the status line. */ statusDispMode = StatusModeTag; } else { /* * We failed to create a banner API window, and we're running * on a text-only interpreter - use the old-style * fixed-format status line mechanism. */ statusDispMode = StatusModeText; } } /* * The status mode we're using. If this is nil, it means we haven't * chosen a mode yet. */ statusDispMode = nil ; frobtads-1.2.3/tads3/lib/adv3/disambig.t0000644000175000001440000005113311541620101017063 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: disambiguation * * This module defines classes related to resolving ambiguity in noun * phrases in command input. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Distinguisher. This object encapsulates logic that determines * whether or not we can tell two objects apart. * * Each game object has a list of distinguishers. For most objects, the * distinguisher list contains only BasicDistinguisher, since most game * objects are unique and thus are inherently distinguishable from all * other objects. */ class Distinguisher: object /* can we distinguish the given two objects? */ canDistinguish(a, b) { return true; } /* * Note that we're showing a prompt to the player asking for help in * narrowing the object list, based on this distinguisher. 'lst' is * the list of ResolveInfo objects which we're mentioning in the * prompt. * * By default, we do nothing. Some types of distinguishers might * want to do something special here. For example, an ownership * distinguisher might want to set pronoun antecedents based on the * owners mentioned in the disambiguation prompt, so that the * player's response can refer anaphorically to the nouns in the * prompt. */ notePrompt(lst) { } /* * Is the object in scope for the purposes of the disambiguation * reply from the player? By default, any object in the full match * list is in scope. * * Distinguishers that can use related objects to qualify the name * should add those related objects to the scope by returning true * here. For example, the locational distinguisher can use the * location name as a qualifying phrase, so the location name is in * scope. */ objInScope(obj, matchList, fullMatchList) { /* it's in scope if it's in the full match list */ return fullMatchList.indexWhich({x: x.obj_ == obj}) != nil; } /* * Try matching an object to a noun phrase in a disambiguation reply * from the player (that is, the player's response to a "Which foo * did you mean" question). By default, we call the object's * matchNameDisambig() method to let it try to match its * disambiguation name. * * Subclasses can override this to check for additional phrasing * specific to the subclass. For example, the locational * distinguisher checks for a match to the container or owner name, * so that the player can simply respond to the question with the * location name rather than typing in a whole locational phrase. * Note that subclasses will usually want to inherit the default * handling if they don't find a match to their own special phrasing, * because the player might respond with a simple adjective * pertaining to the base object even if there's some external * distinguishing characteristic handled by the subclass. */ matchName(obj, origTokens, adjustedTokens, matchList, fullMatchList) { /* try matching the object's disambiguation name */ return obj.matchNameDisambig(origTokens, adjustedTokens); } ; /* * A "null" distinguisher. This can tell two objects apart if they have * different names (so it's inherently language-specific). */ nullDistinguisher: Distinguisher ; /* * "Basic" Distinguisher. This distinguisher can tell two objects apart * if one or the other object is not marked as isEquivalent, OR if the * two objects don't have an identical superclass list. This * distinguisher thus can tell apart objects unless they're "basic * equivalents," marked with isEquivalent and having the same equivalence * keys. */ basicDistinguisher: Distinguisher canDistinguish(a, b) { /* * If the two objects are both marked isEquivalent, and they have * the same equivalence key, they are basic equivalents, so we * cannot distinguish them. Otherwise, we consider them * distinguishable. */ return !(a.isEquivalent && b.isEquivalent && a.equivalenceKey == b.equivalenceKey); } ; /* * Ownership Distinguisher. This distinguisher can tell two objects * apart if they have different owners. "Unowned" objects are * identified by their immediate containers instead of their owners. * * Note that while location *can* distinguish items with this * distinguisher, ownership takes priority: if an object has an owner, * the owner is the distinguishing feature. The reason location is a * factor at all is that we need something parallel to ownership for the * purposes of phrasing distinguishing descriptions of unowned objects. * The best-sounding phrasing, at least in English, is to refer to the * unowned objects by location. */ ownershipDistinguisher: Distinguisher canDistinguish(a, b) { local aOwner; local bOwner; /* get the nominal owner of each object */ aOwner = a.getNominalOwner(); bOwner = b.getNominalOwner(); /* * If neither object is owned, we can't tell them apart on the * basis of ownership, so check to see if we can tell them apart * on the basis of their immediate locations. */ if (aOwner == nil && bOwner == nil) { /* * neither is owned - we can tell them apart only if they * have different immediate containers */ return a.location != b.location; } /* * One or both objects are owned, so we can tell them apart if * and only if they have different owners. */ return aOwner != bOwner; } objInScope(obj, matchList, fullMatchList) { /* it's in scope if it's an owner of an object in the base list */ if (matchList.indexWhich(function(m) { /* get the owner, or the location if there's no owner */ m = m.obj_; local l = m.getNominalOwner(); if (l == nil) l = m.location; /* if obj matches the owner/location, consider it in scope */ return obj == l; }) != nil) return true; /* otherwise, use the inherited handling */ return inherited(obj, matchList, fullMatchList); } matchName(obj, origTokens, adjustedTokens, matchList, fullMatchList) { /* if the name matches, consider ownership relationships */ if (obj.matchName(origTokens, adjustedTokens)) { /* * Look for objects in the original list owned by 'obj'. We * might be matching an owner or location name rather than an * object from the original list, in which case we want to act * like we're matching the original list object(s) instead. */ local owned = matchList.mapAll({m: m.obj_}) .subset(function(m) { /* get the owner or location */ local o = m.getNominalOwner(); if (o == nil) o = m.location; /* if the owner/location is 'obj', keep it */ return o == obj; }); /* if we found any matches, return them all */ if (owned.length() > 0) return owned; } /* no match to the owner; inherit the default handling */ return inherited(obj, origTokens, adjustedTokens, matchList, fullMatchList); } ; /* * Location Distinguisher. This distinguisher identifies objects purely * by their immediate locations. */ locationDistinguisher: Distinguisher canDistinguish(a, b) { /* we tell the objects apart by their immediate locations */ return a.location != b.location; } objInScope(obj, matchList, fullMatchList) { /* it's in scope if it's a location of an object in the base list */ if (matchList.indexWhich({m: m.obj_.location == obj}) != nil) return true; /* otherwise, use the inherited handling */ return inherited(obj, matchList, fullMatchList); } matchName(obj, origTokens, adjustedTokens, matchList, fullMatchList) { /* if the name matches, consider location relationships */ if (obj.matchName(origTokens, adjustedTokens)) { /* look for objects in the original list contained in 'obj' */ local cont = matchList.mapAll({m: m.obj_}) .subset({m: m.location == obj}); /* if we found any matches, return them all */ if (cont.length() > 0) return cont; } /* no match to the owner; inherit the default handling */ return inherited(obj, origTokens, adjustedTokens, matchList, fullMatchList); } ; /* * Lit/unlit Distinguisher. This distinguisher can tell two objects * apart if one is lit (i.e., its isLit property is true) and the other * isn't. */ litUnlitDistinguisher: Distinguisher canDistinguish(a, b) { /* we can tell them apart if one is lit and the other isn't */ return a.isLit != b.isLit; } ; /* ------------------------------------------------------------------------ */ /* * A command ranking criterion for comparing by the number of ordinal * phrases ("first", "the second one") we find in a result. */ rankByDisambigOrdinals: CommandRankingByProblem prop_ = &disambigOrdinalCount ; /* * Disambiguation Ranking. This is a special version of the command * ranker that we use to rank the intepretations of a disambiguation * response. */ class DisambigRanking: CommandRanking /* * Add the ordinal count ranking criterion at the end of the * inherited list of ranking criteria. If we can't find any * differences on the basis of the other criteria, choose the * interpretation that uses fewer ordinal phrases. (We prefer an * non-ordinal interpretation, because this will prefer matches to * explicit vocabulary for objects over matches for generic * ordinals.) * * Insert the 'ordinal' rule just before the 'indefinite' rule - * avoiding an ordinal match is more important. */ rankingCriteria = static (inherited().insertAt( inherited().indexOf(rankByIndefinite), rankByDisambigOrdinals)) /* * note the an ordinal response is out of range */ noteOrdinalOutOfRange(ord) { /* count it as a non-matching entry */ ++nonMatchCount; } /* * note a list ordinal (i.e., "the first one" to refer to the first * item in the ambiguous list) - we take list ordinals as less * desirable than treating ordinal words as adjectives or nouns */ noteDisambigOrdinal() { /* count it as an ordinal entry */ ++disambigOrdinalCount; } /* number of list ordinals in the match */ disambigOrdinalCount = 0 /* * disambiguation commands have no verbs, so there's no verb * structure to rank; so just use an arbitrary noun slot count */ nounSlotCount = 0 ; /* ------------------------------------------------------------------------ */ /* * Base class for resolvers used when answering interactive questions. * This class doesn't do anything in the library directly, but it * provides a structured point for language extensions to hook in as * needed with 'modify'. */ class InteractiveResolver: ProxyResolver ; /* ------------------------------------------------------------------------ */ /* * Disambiguation Resolver. This is a special resolver that we use for * resolving disambiguation responses. */ class DisambigResolver: InteractiveResolver construct(matchText, ordinalMatchList, matchList, fullMatchList, resolver, dist) { /* inherit the base class constructor */ inherited(resolver); /* remember the original match text and lists */ self.matchText = matchText; self.ordinalMatchList = ordinalMatchList; self.matchList = matchList; self.fullMatchList = fullMatchList; self.distinguisher = dist; } /* * Match an object's name. We'll send this to the distinguisher for * handling. */ matchName(obj, origTokens, adjustedTokens) { return distinguisher.matchName(obj, origTokens, adjustedTokens, matchList, fullMatchList); } /* * Resolve qualifiers in the enclosing main scope, since qualifier * phrases in responses are not part of the narrowed list being * disambiguated. */ getQualifierResolver() { return origResolver; } /* * Determine if an object is in scope. We pass this to the * distinguisher to decide. */ objInScope(obj) { return distinguisher.objInScope(obj, matchList, fullMatchList); } /* * we allow ALL in interactive disambiguation responses, regardless * of the verb, because we're just selecting from the list presented * in the prompt in these cases */ allowAll = true /* for 'all', use the full current full match list */ getAll(np) { return fullMatchList; } /* filter an ambiguous noun list */ filterAmbiguousNounPhrase(lst, requiredNum, np) { /* * we're doing disambiguation, so we're only narrowing the * original match list, which we've already filtered as well as * we can - just return the list unchanged */ return lst; } /* filter a plural noun list */ filterPluralPhrase(lst, np) { /* * we're doing disambiguation, so we're only narrowing the * original match list, which we've already filtered as well as * we can - just return the list unchanged */ return lst; } /* * Select the match for an indefinite noun phrase. In interactive * disambiguation, an indefinite noun phrase simply narrows the * list, rather than selecting any match, so treat this as still * ambiguous. */ selectIndefinite(results, lst, requiredNumber) { /* note the ambiguous list in the results */ return results.ambiguousNounPhrase(nil, ResolveAsker, '', lst, lst, lst, requiredNumber, self); } /* the text of the phrase we're disambiguating */ matchText = '' /* * The "ordinal" match list: this includes the exact list offered as * interactive choices in the same order as they were shown in the * prompt. This list can be used to correlate ordinal responses to * the prompt list, since it contains exactly the items listed in * the prompt. Note that this list will only contain one of each * indistinguishable object. */ ordinalMatchList = [] /* * the original match list we are disambiguating, which includes all * of the objects offered as interactive choices, and might include * indistinguishable equivalents of offered items */ matchList = [] /* * the full original match list, which might include items in scope * beyond those offered as interactive choices */ fullMatchList = [] /* * The distinguisher that was used to generate the prompt. Some * distinguishers can tell objects apart by other characteristics * than just their names, so when parsing we want to be able to give * the distinguisher a look at the input to see if the player is * referring to one of the distinguishing characteristics rather than * the object's own name. */ distinguisher = nil ; /* ------------------------------------------------------------------------ */ /* * General class for disambiguation exceptions */ class DisambigException: Exception ; /* * Still Ambiguous Exception - this is thrown when the user answers a * disambiguation question with insufficient specificity, so that we * still have an ambiguous list. */ class StillAmbiguousException: DisambigException construct(matchList, origText) { /* remember the new match list and text */ matchList_ = matchList; origText_ = origText; } /* the narrowed, but still ambiguous, match list */ matchList_ = [] /* the text of the new phrasing */ origText_ = '' ; /* * Unmatched disambiguation - we throw this when the user answers a * disambiguation question with a syntactically valid response that * doesn't refer to any of the objects in the list of choices offered. */ class UnmatchedDisambigException: DisambigException construct(resp) { /* remember the response text */ resp_ = resp; } /* the response text */ resp_ = nil ; /* * Disambiguation Ordinal Out Of Range - this is thrown when the user * answers a disambiguation question with an ordinal, but the ordinal is * outside the bounds of the offered list (for example, we ask "which * book do you mean, the red book, or the blue book?", and the user * answers "the fourth one"). */ class DisambigOrdinalOutOfRangeException: DisambigException construct(ord) { /* remember the ordinal word */ ord_ = ord; } /* a string giving the ordinal word entered by the user */ ord_ = '' ; /* ------------------------------------------------------------------------ */ /* * A disambiguation results gatherer object. We use this to manage the * results of resolution of a disambiguation response. */ class DisambigResults: BasicResolveResults construct(parent) { /* copy the actor information from the parent resolver */ setActors(parent.targetActor_, parent.issuingActor_); } ambiguousNounPhrase(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver) { /* if we're resolving a sub-phrase, inherit the standard handling */ if (resolver.isSubResolver) return inherited(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver); /* * Before giving up, try filtering by possessive rank, in case we * qualified by a possessive phrase. */ matchList = resolver.filterPossRank(matchList, requiredNum); /* * Our disambiguation response itself requires further * disambiguation. Do not handle it recursively, since doing so * could allow the user to blow the stack simply by answering * with the same response over and over. Instead, throw a * "still ambiguous" exception - the original disambiguation * loop will note the situation and iterate on the resolution * list, ensuring that we can run forever without blowing the * stack, if that's the game the user wants to play. */ throw new StillAmbiguousException(matchList, txt.toLower().htmlify()); } /* * note the an ordinal response is out of range */ noteOrdinalOutOfRange(ord) { /* this is an error */ throw new DisambigOrdinalOutOfRangeException(ord); } /* * show a message on not matching an object - for a disambiguation * response, failing to match means that the combination of the * disambiguation response plus the original text doesn't name any * objects, not that the object in the response itself isn't present */ noMatch(action, txt) { /* throw an error indicating the problem */ throw new UnmatchedDisambigException(txt.toLower().htmlify()); } noMatchPoss(action, txt) { throw new UnmatchedDisambigException(txt.toLower().htmlify()); } noVocabMatch(action, txt) { /* throw an error indicating the problem */ throw new UnmatchedDisambigException(txt.toLower().htmlify()); } noMatchForPossessive(owner, txt) { /* throw an error indicating the problem */ throw new UnmatchedDisambigException(txt.toLower().htmlify()); } ; frobtads-1.2.3/tads3/lib/adv3/action.t0000644000175000001440000072742211763123552016612 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: Actions. * * This module defines the Action classes. An Action is an abstract * object representing a command to be performed. */ #include "adv3.h" #include "tok.h" /* ------------------------------------------------------------------------ */ /* * Object associations lists. We use this object to store some lookup * tables that we build during preinitialization to relate object usages * (DirectObject, IndirectObject) to certain properties. */ objectRelations: PreinitObject /* preinitialization - build the lookup tables */ execute() { /* build the default pre-condition properties table */ preCondDefaultProps[DirectObject] = &preCondDobjDefault; preCondDefaultProps[IndirectObject] = &preCondIobjDefault; /* build the catch-all pre-conditions properties table */ preCondAllProps[DirectObject] = &preCondDobjAll; preCondAllProps[IndirectObject] = &preCondIobjAll; /* build the default verification properties table */ verifyDefaultProps[DirectObject] = &verifyDobjDefault; verifyDefaultProps[IndirectObject] = &verifyIobjDefault; /* build the catch-all verification properties table */ verifyAllProps[DirectObject] = &verifyDobjAll; verifyAllProps[IndirectObject] = &verifyIobjAll; /* build the default check properties table */ checkDefaultProps[DirectObject] = &checkDobjDefault; checkDefaultProps[IndirectObject] = &checkIobjDefault; /* build the catch-all check properties table */ checkAllProps[DirectObject] = &checkDobjAll; checkAllProps[IndirectObject] = &checkIobjAll; /* build the default action properties table */ actionDefaultProps[DirectObject] = &actionDobjDefault; actionDefaultProps[IndirectObject] = &actionIobjDefault; /* build the catch-all check properties table */ actionAllProps[DirectObject] = &actionDobjAll; actionAllProps[IndirectObject] = &actionIobjAll; } /* lookup table for default precondition properties */ preCondDefaultProps = static new LookupTable() /* lookup table for catch-all precondition properties */ preCondAllProps = static new LookupTable() /* lookup table for default verification properties */ verifyDefaultProps = static new LookupTable() /* lookup table for catch-all verification properties */ verifyAllProps = static new LookupTable() /* lookup table for default check properties */ checkDefaultProps = static new LookupTable() /* lookup table for catch-all check properties */ checkAllProps = static new LookupTable() /* lookup table for default action properties */ actionDefaultProps = static new LookupTable() /* lookup table for catch-all action properties */ actionAllProps = static new LookupTable() ; /* ------------------------------------------------------------------------ */ /* * Invoke the given function with the given values for the parser global * variables gActor and gAction. */ withParserGlobals(issuer, actor, action, func) { local oldIssuer; local oldActor; local oldAction; /* remember the old action and actor, so we can restore them later */ oldIssuer = gIssuingActor; oldActor = gActor; oldAction = gAction; /* establish our actor and action as the global settings */ gIssuingActor = issuer; gActor = actor; gAction = action; /* make sure we restore globals on the way out */ try { /* invoke the callback and return the result */ return (func)(); } finally { /* restore the globals we changed */ gActor = oldActor; gIssuingActor = oldIssuer; gAction = oldAction; } } /* ------------------------------------------------------------------------ */ /* * Pre-condition Descriptor. This object encapsulates a precondition * object and the argument we want to pass to its condition check method. */ class PreCondDesc: object construct(cond, arg) { /* remember the condition and the check argument */ cond_ = cond; arg_ = arg; } /* check the precondition */ checkPreCondition(allowImplicit) { /* call the precondition's check method with the argument we stored */ return cond_.checkPreCondition(arg_, allowImplicit); } /* the precondition object */ cond_ = nil /* the check argument */ arg_ = nil /* our list sorting index */ index_ = 0 ; /* ------------------------------------------------------------------------ */ /* * Basic Action class. An Action is the language-independent definition * of the abstract action of a command. */ class Action: BasicProd /* * Are we the given kind of action? By default, this simply returns * true if we're of the given action class. */ actionOfKind(cls) { return ofKind(cls); } /* * Reset the action in preparation for re-execution. This should * discard any scoped context from any past execution of the * command, such as cached scope information. */ resetAction() { /* forget any past successful verification passes */ verifiedOkay = []; } /* * Repeat the action, for an AGAIN command. */ repeatAction(lastTargetActor, lastTargetActorPhrase, lastIssuingActor, countsAsIssuerTurn) { /* execute the command */ executeAction(lastTargetActor, lastTargetActorPhrase, lastIssuingActor, countsAsIssuerTurn, self); } /* * Cancel iteration of the action. This can be called during the * 'check' or 'action' phases of executing this action. It tells * the action that we want to stop executing the action when we're * finished with the current object. * * Note that this doesn't cause a jump out of the current code, so * it's not like 'exit' or the other termination signals. Instead, * this simply tells the action to proceed normally for the * remainder of the processing for the current object, and then act * as though there were no more objects to iterate over, ending the * command normally. If you want to cut off the remainder of the * execution cycle for the current object, you can use 'exit' (for * example) immediately after calling this method. */ cancelIteration() { iterationCanceled = true; } /* internal flag: object iteration has been canceled */ iterationCanceled = nil /* * Create an instance of this action, for use by a recursive or * programmatically-generated command. * * The generic actions defined in the library are always subclassed * by language-specific library modules, because the language * modules have to define the grammar rules for the verbs - we can't * define the grammar rules generically because the verbs wouldn't * be reusable for non-English translations if we did. As a result, * library code referring to one of the library verbs by name, say * TakeAction, doesn't get a language-specific subclass of the verb, * but just gets the language-independent base class. * * However, to make full use of an Action object in a recursive * command, we do need a final language-specific subclass - without * this, we won't be able to generate text describing the command, * for example. This method bridges this gap by finding a suitable * language-specific subclass of the given action, then creating an * instance of that subclass rather than an instance of the base * class. * * By default, we'll take any subclass of this action that is itself * a class. However, if any subclass has the property * defaultForRecursion set to true, we'll use that class * specifically - this lets the language module designate a * particular subclass to use as the default for recursive commands, * which might be desirable in cases where the language module * defines more than one subclass of an action. */ createActionInstance() { local found; /* * Iterate over our subclasses. Initialize 'found' to this base * class, so that if we fail to find any subclasses, we'll at * least be able to create an instance of the generic base * class. */ for (local cur = firstObj(self, ObjClasses), found = self ; cur != nil ; cur = nextObj(cur, self, ObjClasses)) { /* * if this one is marked as a default for recursion, and the * last one we found isn't, choose this one over the last * one we found */ if (cur.defaultForRecursion && !found.defaultForRecursion) found = cur; /* * If this one is a subclass of the last one we found, pick * it instead of the last one. We always want a final * subclass here, never an intermediate class, so if we find * a subclass of the one we've tentatively picked, we know * that the tentative selection isn't final after all. */ if (cur.ofKind(found)) found = cur; } /* return a new instance of what we found */ return found.createInstance(); } /* * Create an instance of this action based on another action. We'll * copy the basic properties of the original action. */ createActionFrom(orig) { local action; /* create a new instance of this action */ action = createActionInstance(); /* copy the token list information to the new action */ action.tokenList = orig.tokenList; action.firstTokenIndex = orig.firstTokenIndex; action.lastTokenIndex = orig.lastTokenIndex; /* the new action is implicit if the original was */ if (orig.isImplicit) action.setImplicit(orig.implicitMsg); /* return the new action */ return action; } /* * Mark the command as implicit. 'msgProp' is the property (of * gLibMessages) to use to announce the implicit command. */ setImplicit(msgProp) { /* mark ourselves as implicit */ isImplicit = true; /* do not show default reports for implicit commands */ showDefaultReports = nil; /* all implicit commands are nested */ setNested(); /* * do not include implicit commands in undo - since an implicit * command is only a subpart of an explicit command, an implicit * command is not undone individually but only as part of the * enclosing explicit command */ includeInUndo = nil; /* remember the implicit command announcement message property */ implicitMsg = msgProp; } /* * Mark the command as nested, noting the parent action (which we * take as the global current action). */ setNested() { /* remember the parent action */ parentAction = gAction; } /* * Determine if I'm nested within the given action. Returns true if * the given action is my parent action, or if my parent action is * nested within the given action. */ isNestedIn(action) { /* if my parent action is the given action, I'm nested in it */ if (parentAction == action) return true; /* * if I have a parent action, and it's nested in the given * action, then I'm nested in the given action because I'm * nested in anything my parent is nested in */ if (parentAction != nil && parentAction.isNestedIn(action)) return true; /* we're not nested in the given action */ return nil; } /* * Set the "original" action. An action with an original action is * effectively part of the original action for the purposes of its * reported results. * * An action has an original action if it's a nested or replacement * action for an action. */ setOriginalAction(action) { /* remember my original action */ originalAction = action; } /* * Get the "original" action. If I'm a replacement or nested action, * this returns the original main action of which I'm a part, for * reporting pruposes. * * It's important to note that an implied action does NOT count as a * nested or replacement action for the purposes of this method. * That is, if a command A triggers an implied action B, which * triggers a nested action C, and then after that command A itself * triggers a nested action D, then * * A.getOriginalAction -> A *. B.getOriginalAction -> B (B is implied, not nested) *. C.getOriginalAction -> C (C is nested within B) *. D.getOriginalAction -> A (D is nested within A) * * The purpose of the original action is to tell us, mainly for * reporting purposes, what we're really trying to do with the * action. This allows reports to hide the internal details of how * the action is carried out, and instead say what the action was * meant to do from the player's perspective. */ getOriginalAction() { /* if I have no other original action, I'm the original action */ if (originalAction == nil) return self; /* return my original action's original action */ return originalAction.getOriginalAction(); } /* * Determine if this action is "part of" the given action. I'm part * of the given action if I am the given action, or the given action * is my "original" action, or my original action is part of the * given action. */ isPartOf(action) { /* if I'm the same as the given action, I'm obviously part of it */ if (action == self) return true; /* if my original action is part of the action, I'm part of it */ if (originalAction != nil && originalAction.isPartOf(action)) return true; /* I'm not part of the given action */ return nil; } /* * Mark the action as "remapped." This indicates that the action * was explicitly remapped to a different action during the remap() * phase. */ setRemapped(orig) { remappedFrom = gAction; } /* determine if I'm remapped, and get the original action if so */ isRemapped() { return remappedFrom != nil; } getRemappedFrom() { return remappedFrom; } /* * Get the "simple synonym" remapping for one of our objects, if * any. 'obj' is the resolved object to remap, and 'role' is the * object role identifier (DirectObject, IndirectObject, etc). * 'remapProp' is the remapping property for the role; this is * simply the result of our getRemapPropForRole(role), but we ask * the caller to pass this in so that it can be pre-computed in * cases where we'll called in a loop. * * A simple synonym remapping is a remapTo that applies the same * verb to a new object in the same role. For example, if we remap * OPEN DESK to OPEN DRAWER, then the drawer is the simple synonym * remapping for the desk in an OPEN command. A remapping is * considered a simple synonym remapping only if we're remapping to * the same action, AND the new object is in the same action role as * the original object was. * * If there's no simple synonym remapping, we'll return nil. */ getSimpleSynonymRemap(obj, role, remapProp) { local mapping; local remapIdx; /* if the object isn't remapped at all, there's no remapping */ if ((mapping = obj.(remapProp)) == nil) return nil; /* if the mapping isn't to the same action, it's not a synonym */ if (!ofKind(mapping[1])) return nil; /* * Find the specific (non-role) object in the remap vector - * this is the entry that's actually an object rather than a * role identifier. (The remapping vector is required to have * exactly one such entry. Look from the right end of the list, * since the first entry is always the new action, which is * itself an object, but not the object we're looking for.) */ remapIdx = mapping.lastIndexWhich({x: dataType(x) == TypeObject}); if (remapIdx == nil) return nil; /* * Determine if the object plays the same role in the new action * as the original object did in the original action. It does * if it's at the index in the mapping vector of our object role * in the action. Note that the mapping vector has slot 1 * filled with the action, so its objects are actually one slot * higher than they are in the action itself. * * If the new object isn't in the same role, then this isn't a * simple synonym remapping. */ if (getRoleFromIndex(remapIdx - 1) != role) return nil; /* * We have the same action applied to a new object in the same * role as the original object, so this is a simple synonym * remapping. Return the new object we're mapping to. */ return mapping[remapIdx]; } /* * the defaultForRecursion flag must be explicitly set in subclasses * when desired - by default we'll use any language-specific * subclass of an Action for recursive commands */ defaultForRecursion = nil /* * Flag: the command is implicit. An implicit command is one that * is performed as an implied enabling step of another command - for * example, if an actor wants to throw something, the actor must be * holding the object, so will implicitly try to take the object. */ isImplicit = nil /* * The parent action. If the command is performed programmatically * in the course of executing another command, this is set to the * enclosing action. * * Note that while all implicit commands are nested, not all nested * commands are implicit. A nested command may simply be a * component of another command, or another command may be handled * entirely by running a different command as a nested command. In * any case, a nested but non-implicit command does not appear to * the player as a separate command; it is simply part of the * original command. */ parentAction = nil /* * The original action we were remapped from. This is valid when * the action was explicitly remapped during the remap() phase to a * different action. */ remappedFrom = nil /* * the original action - we are effectively part of the original * action for reporting purposes */ originalAction = nil /* the libMessage property, if any, to announce the implicit command */ implicitMsg = nil /* * Flag: we are to show default reports for this action. In most * cases we will do so, but for some types of commands (such as * implicit commands), we suppress default reports. */ showDefaultReports = true /* * Get a message parameter object for the action. Each action * subclass defines this to return its objects according to its own * classifications. The default action has no objects, but * recognizes 'actor' as the current command's actor. */ getMessageParam(objName) { switch(objName) { case 'pc': /* return the player character */ return gPlayerChar; case 'actor': /* return the current actor */ return gActor; default: /* * if we have an extra message parameters table, look up the * parameter name in the table */ if (extraMessageParams != nil) return extraMessageParams[objName]; /* we don't recognize other names */ return nil; } } /* * Define an extra message-specific parameter. Message processors * can use this to add their own special parameters, so that they * can refer to parameters that aren't involved directly in the * command. For example, a message for "take " might want to * refer to the object containing the direct object. */ setMessageParam(objName, obj) { /* * if we don't yet have an extra message parameters table, * create a small lookup table for it */ if (extraMessageParams == nil) extraMessageParams = new LookupTable(8, 8); /* add the parameter to the table, indexing by the parameter name */ extraMessageParams[objName.toLower()] = obj; } /* * For convenience, this method allows setting any number of * name/value pairs for message parameters. */ setMessageParams([lst]) { /* set each pair from the argument list */ for (local i = 1, local len = lst.length() ; i+1 <= len ; i += 2) setMessageParam(lst[i], lst[i+1]); } /* * Synthesize a global message parameter name for the given object. * We'll store the association and return the synthesized name. */ synthMessageParam(obj) { local nm; /* synthesize a name */ nm = 'synth' + toString(synthParamID++); /* store the association */ setMessageParam(nm, obj); /* return the synthesized name */ return nm; } /* synthesized message object parameter serial number */ synthParamID = 1 /* * Extra message parameters. If a message processor wants to add * special message parameters of its own, we'll create a lookup * table for the extra parameters. Message processors might want to * add their own special parameters to allow referring to objects * other than the main objects of the command. */ extraMessageParams = nil /* * Flag: this command is repeatable with 'again'. Before executing * a command, we'll save it for use by the 'again' command if this * flag is true. */ isRepeatable = true /* * Flag: this command should be included in the undo records. This * is true for almost every command, but a few special commands * (undo, save) are not subject to undo. */ includeInUndo = true /* * Flag: this is a "conversational" command, as opposed to an order. * When this type of command is addressed to another character, it * doesn't ask the other character to do anything, but rather engages * the other character in conversation. Examples: * *. Bob, hello *. Bob, goodbye *. Bob, tell me about the planet *. Bob, yes *. Bob, no * * ("Tell me about..." is a little different from the others. We * treat it as conversational because it means the same thing as "ask * Bob about...", which we consider conversational because it would * be rendered in real life as a question. In other words, the * action involves the issuing actor stating the question, which * means that issuing actor is the one doing the physical work of the * action. "Tell me about..." could be seen as an order, but it * seems more appropriate to view it as simply an alternative way of * phrasing a question.) * * The issuing actor is passed as a parameter because some actions * are conversational only in some cases; "tell me about the planet" * is conversational, but "tell Bill about the planet" isn't, since * the latter doesn't ask Bob a question but orders Bob to talk to * Bill. * * When the issuing actor and target actor are the same, this is * irrelevant. The purpose of this is to distinguish orders given to * another character from conversational overtures directed to the * other character, so if the command is coming from the player and * bound for the player character, there's obviously no conversation * going on. * * Note also that, contrary to what one might think at first glance, * a command like ASK ABOUT is NOT conversational; it's a command to * ask someone about something, and isn't itself a conversational * overture. The act of asking is itself a conversational overture, * but the asking is the *result* of the command, not the command * itself. An action is only conversational if the action itself is * a conversational overture. So, "BOB, HELLO" is conversational; * "BOB, ASK BILL ABOUT COMPUTER" is not, because it orders Bob to do * something. */ isConversational(issuingActor) { return nil; } /* * Get the actual verb phrase the player typed in to generate this * Action, expressed in a canonical format. The canonical format * consists of the lower-case version of all of the actual text the * player typed, but with each noun phrase replaced by a standard * placeholder token describing the slot. The placeholder tokens are * '(dobj)' for the direct object, '(iobj)' for the indirect object, * '(literal)' for a literal text phrase, and '(topic)' for a topic * phrase. * * For example, if the player typed PUT BOOK AND PENCIL IN BOX, the * canonical phrasing we return would be "put (dobj) in (iobj)". */ getEnteredVerbPhrase() { local orig; local txt; /* * If there's an original action, let the original action do the * work. If we've been remapped, or if this is an implied * action, we won't necessarily have been constructed from the * actual player input, so we need to go back to the original * action for this information. */ if (getOriginalAction() != self) return getOriginalAction.getEnteredVerbPhrase(); /* start with the original token list for the predicate */ orig = getPredicate().getOrigTokenList(); /* add each token to the result text */ for (txt = '', local i = 1, local len = orig.length() ; i <= len ; ++i) { local foundNp; /* add a space if this isn't the first element */ if (i > 1) txt += ' '; /* * Check to see if we're entering one of the noun-phrase * slots. We are if we've reached the first token of one of * the predicate noun phrases. */ foundNp = nil; foreach (local npProp in predicateNounPhrases) { local match; /* check to see if we're at this noun phrase */ if ((match = self.(npProp)) != nil && i == match.firstTokenIndex) { /* * we're entering this noun phrase - add the generic * placeholder token for the noun phrase */ txt += (npProp == &dobjMatch ? '(dobj)' : npProp == &iobjMatch ? '(iobj)' : npProp == &topicMatch ? '(topic)' : npProp == &literalMatch ? '(literal)' : '(other)'); /* skip the entire run of tokens for the noun phrase */ i = match.lastTokenIndex; /* note that we found a noun phrase */ foundNp = true; /* stop looking for a noun phrase */ break; } } /* * if we didn't find a noun phrase, this token is a literal * part of the predicate grammar, so add it as-is */ if (!foundNp) txt += getTokVal(orig[i]); } /* return the phrase, with everything converted to lower-case */ return txt.toLower(); } /* * Get the grammar match tree object for the predicate that was used * to enter this command. By default, if we have an original action, * we return the original action; otherwise we just return 'self'. * * Language libraries must override this to return the original match * tree object if Actions are separate from predicate match trees. * * (The 'predicate' is simply the grammar match tree object for the * entire verb phrase from the player's actual command entry text * that matched this Action. For example, in "BOB, TAKE BOX", the * predicate is the match tree for the "TAKE BOX" part. In "BOB, * TAKE BOX AND GO NORTH", the predicate for the Take action is still * the "TAKE BOX" part. For "BOB, TAKE BOX AND BOOK AND GO NORTH", * the predicate for the Take action is "TAKE BOX AND BOOK" - the * full verb phrase includes any multiple-object lists in the * original command.) */ getPredicate() { /* * By default, we just return the original Action - we assume * that the language library defines Action subclasses as the * actual match tree objects for predicate grammars. Language * modules must override this if they use separate object types * for the predicate match tree objects. */ return getOriginalAction(); } /* * Get the noun-phrase information for my predicate grammar. This * returns a list of the match-tree properties for the noun-phrase * sub-productions in our predicate grammar. The properties * generally include &dobjMatch, &iobjMatch, &literalMatch, and * &topicMatch. The order of the slots isn't important; they simply * tell us which ones we should find in our predicate grammar match. * * The base Action is intransitive, so it doesn't have any * noun-phrase slots, hence this is an empty list. */ predicateNounPhrases = [] /* * Get the object "role" identifier for the given index. This * returns the identifier (DirectObject, IndirectObject, etc.) for * the object at the given slot index, as used in * setResolvedObjects(). The first object is at index 1. */ getRoleFromIndex(idx) { return nil; } /* * Get the "other object" role - this is the complement of the given * object role for a two-object action, and is used to determine the * real role of the special OtherObject placeholder in a remapTo(). * This is only meaningful with actions of exactly two objects. */ getOtherObjectRole(role) { return nil; } /* get the resolved object in a given role */ getObjectForRole(role) { return nil; } /* get the match tree for the noun phrase in the given role */ getMatchForRole(role) { return nil; } /* get the 'verify' property for a given object role */ getVerifyPropForRole(role) { return nil; } /* get the 'preCond' property for a given object role */ getPreCondPropForRole(role) { return nil; } /* get the 'remap' property for a given object role */ getRemapPropForRole(role) { return nil; } /* * Get the ResolveInfo for the given object in the current command. * Since we don't have any objects at all, we'll simply return a new * ResolveInfo wrapping the given object. 'cur' is the object we're * looking for, and 'oldRole' is the role the object previously * played in the action. */ getResolveInfo(obj, oldRole) { return new ResolveInfo(obj, 0, nil); } /* * Explicitly set the resolved objects. This should be overridden * in each subclass for the number of objects specific to the action * (a simple transitive action takes one argument, an action with * both a direct and indirect object takes two arguments, and so * on). The base action doesn't have any objects at all, so this * takes no arguments. * * This method is used to set up an action to be performed * programmatically, rather than based on parsed input. Since * there's no parsed input in such cases, the objects are specified * directly by the programmatic caller. */ setResolvedObjects() { } /* * Explicitly set the object match trees. This sets the pre-resolved * production match trees. The arguments are given in the order of * their roles in the action, using the same order that * setResolvedObjects() uses. * * The arguments to this routine can either be match tree objects, * which we'll plug into our grammar tree in the respective roles * exactly as given; or they can be ResolveInfo objects giving the * desired resolutions, in which case we'll build the appropriate * kind of PreResolvedProd for each one. The types can be freely * mixed. */ setObjectMatches() { } /* * Check that the resolved objects are all in scope. Returns true if * so, nil if not. * * This routine is meant for use only for actions built * programmatically using setResolvedObjects(). In particular, we * assume that we have only one object in each slot. For normal * parser-built actions, this check isn't necessary, because the * parser only resolves objects that are in scope in the first place. */ resolvedObjectsInScope() { /* we have no objects at all, so there is nothing out of scope */ return true; } /* * Get the list of bindings for an anaphoric pronoun - this is a * pronoun that refers back to an earlier noun phrase in the same * sentence, such as HIMSELF in ASK BOB ABOUT HIMSELF. These * obviously make no sense for verbs that take one (or zero) * objects, since there's no earlier phrase to refer back to; these * should return nil to indicate that an anaphoric pronoun is simply * nonsensical in such a context. Actions of two or more objects * should return a list of objects for the preceding noun phrase. * * Actions of two or more objects should set this if possible to the * resolved object list for the previous noun phrase, as a * ResolveInfo list. * * The tricky part is that some actions will resolve noun phrases in * a different order than they appear in the actual command grammar; * similarly, it's also possible that some non-English languages use * cataphoric pronouns (i.e., pronouns that refer to noun phrases * that appear later in the sentence). To allow for these cases, * return an empty list here if a binding is grammatically possible * but not yet available because of the resolution order, and note * internally that the parser asked us for an anaphoric binding. * Afterwards, the action's resolver method must go back and perform * *another* resolve pass on the noun phrase production that * requested the anaphor binding. * * 'typ' is the PronounType specifier for the corresponding ordinary * pronoun. For 'himself', for example, typ will be PronounHim. */ getAnaphoricBinding(typ) { return nil; } /* * Set a special pronoun override. This creates a temporary pronoun * definition, which lasts as long as this action (and any nested * actions). The temporary definition overrides the normal meaning * of the pronoun. * * One example of where this is useful is in global action remapping * cases where the target actor changes in the course of the * remapping. For example, if we remap BOB, GIVE ME YOUR BOOK to ASK * BOB FOR YOUR BOOK, the YOUR qualifier should still refer to Bob * even though the command is no longer addressing Bob directly. * This routine can be used in this case to override the meaning of * 'you' so that it refers to Bob. */ setPronounOverride(typ, val) { /* if we don't have an override table yet, create one */ if (pronounOverride == nil) pronounOverride = new LookupTable(5, 10); /* add it to the table */ pronounOverride[typ] = val; } /* * Get any special pronoun override in effect for the action, as set * via setPronounOverride(). This looks in our own override table * for a definition; then, if we have no override of our own, we ask * our parent action if we have one, then our original action. */ getPronounOverride(typ) { local pro; /* check our own table */ if (pronounOverride != nil && (pro = pronounOverride[typ]) != nil) return pro; /* we don't have anything in our own table; check our parent */ if (parentAction != nil && (pro = parentAction.getPronounOverride(typ)) != nil) return pro; /* if still nothing, check with the original action */ if (originalAction != nil && originalAction != parentAction && (pro = originalAction.getPronounOverride(typ)) != nil) return pro; /* we didn't find an override */ return nil; } /* * the pronoun override table - this is nil by default, which means * that no overrides have been defined yet; we create a LookupTable * upon adding the first entry to the table */ pronounOverride = nil /* wrap an object with a ResolveInfo */ makeResolveInfo(val) { /* * if it's already a ResolveInfo object, return it as-is; * otherwise, create a new ResolveInfo wrapper for it */ if (dataType(val) == TypeObject && val.ofKind(ResolveInfo)) return val; else return new ResolveInfo(val, 0, nil); } /* * Convert an object or list of objects to a ResolveInfo list */ makeResolveInfoList(val) { /* if we have a non-list collection, make it a list */ if (dataType(val) == TypeObject && val.ofKind(Collection)) val = val.toList(); /* if it's nil or an empty list, return an empty list */ if (val == nil || val == []) return []; /* see what we have */ if (dataType(val) == TypeList) { /* it's a list - make a ResolveInfo for each item */ return val.mapAll({x: makeResolveInfo(x)}); } else { /* it's not a list - return a one-element ResolveInfo list */ return [makeResolveInfo(val)]; } } /* * If the command is repeatable, save it for use by 'again'. */ saveActionForAgain(issuingActor, countsAsIssuerTurn, targetActor, targetActorPhrase) { /* * Check to see if the command is repeatable. It's repeatable if * the base action is marked as repeatable, AND it's not nested, * AND it's issued by the player character, AND it's either a PC * command or it counts as an issuer turn. * * Nested commands are never repeatable with 'again', since no * one ever typed them in. * * "Again" is strictly for the player's use, so it's repeatable * only if this is the player's turn, as opposed to a scripted * action by an NPC. This is the player's turn only if the * command was issued by the player character (which means it * came from the player), and either it's directed to the player * character OR it counts as a turn for the player character. */ if (isRepeatable && parentAction == nil && (issuingActor.isPlayerChar() && (targetActor.isPlayerChar() || countsAsIssuerTurn))) AgainAction.saveForAgain(issuingActor, targetActor, targetActorPhrase, self); } /* * Perform this action. Throughout execution of the action, the * global parser variables that specify the current actor and action * are valid. */ doAction(issuingActor, targetActor, targetActorPhrase, countsAsIssuerTurn) { local oldActor; local oldIssuer; local oldAction; local oldResults; /* * save the current parser globals, for restoration when we * finish this command - if this command is nested within * another, this will let us ensure that everything is restored * properly when we finish with this command */ oldActor = gActor; oldIssuer = gIssuingActor; oldAction = gAction; oldResults = gVerifyResults; /* * set the new globals (note that there are no verification * results or command reports objects yet - these are valid only * while we're running the corresponding command phases) */ gActor = targetActor; gIssuingActor = issuingActor; gAction = self; gVerifyResults = nil; /* make sure we restore globals on our way out */ try { local pc; /* if applicable, save the command for AGAIN */ saveActionForAgain(issuingActor, countsAsIssuerTurn, targetActor, targetActorPhrase); /* start a new command visually if this isn't a nested action */ if (parentAction == nil) gTranscript.addCommandSep(); /* have the player character note initial conditions */ pc = gPlayerChar; pc.noteConditionsBefore(); /* run the before routine for the entire action */ beforeActionMain(); /* run the subclass-specific processing */ doActionMain(); /* run the after routine for the entire action */ afterActionMain(); /* * If this is a top-level action, show the command results. * Don't show results for a nested action, since we want to * wait and let the top-level action show the results after * it has the full set of results. */ if (parentAction == nil) { /* * If the player character didn't change, ask the player * character to note any condition changes. If the * player character did change to a new actor, * presumably the command will have displayed a specific * message, since this would be an unusual development * for which we can generate no generic message. */ if (gPlayerChar == pc) pc.noteConditionsAfter(); } } finally { /* restore the parser globals to how we found them */ gActor = oldActor; gIssuingActor = oldIssuer; gAction = oldAction; gVerifyResults = oldResults; } } /* * Perform processing before running the action. This is called * just once per action, even if the action will be iterated for a * list of objects. */ beforeActionMain() { } /* * Perform processing after running the entire action. This is * called just once per action, even if the action was iterated for * a list of objects. */ afterActionMain() { /* call each registered after-action handler */ if (afterActionMainList != nil) { foreach (local cur in afterActionMainList) cur.afterActionMain(); } /* * Mark ourselves as busy for the amount of time this action * takes. Don't count the time taken for implied actions, * though, since these are meant to be zero-time sub-actions * performed as part of the main action and thus don't have a * separate time cost. * * Note that we add our busy time in the main after-action * processing because we only want to count our time cost once * for the whole command, even if we're performing the command on * multiple objects. */ if (!isImplicit) { local actor; /* * If the command is conversational, the turn counts as an * issuer turn; otherwise, it counts as a turn for the target * actor. Conversational commands are effectively carried * out by the issuer, even though in form they're directed to * another actor (as in "BOB, HELLO"), so we need to count * the time they take as the issuer's time. */ actor = (isConversational(gIssuingActor) ? gIssuingActor : gActor); /* add the busy time to the actor */ actor.addBusyTime(self, actionTime); } /* * If the command failed, and this is a top-level (not nested) * action, check to see if the game wants to cancel remaining * commands on the line. */ if (gTranscript.isFailure && parentAction == nil && gameMain.cancelCmdLineOnFailure) { /* * the command failed, and they want to cancel remaining * commands on failure - throw a 'cancel command line' * exception to cancel any remaining tokens */ throw new CancelCommandLineException(); } } /* * Register an object for afterActionMain invocation. After we've * finished with the entire action - including all iterated objects * involved in the action - we'll invoke each registered object's * afterActionMain() method. This registration is only meaningful * for the current action instance, and can only be set up before * the action has been finished (i.e., before the current gAction * invokes its own afterActionMain() method). * * Each object is only registered once. If a caller attempts to * register the same object repeatedly, we'll simply ignore the * repeated requests. * * This is a convenient way to implement a collective follow-up to * the parts of an iterated action. Since repeated registrations * are ignored, each handler for an iterated object (such as a * "dobjFor" action() handler) can register its follow-up handler * without worrying about redundant registration. Then, when the * overall action is completed for each iterated object involved, * the follow-up handler will be invoked, and it can do any final * work for the overall action. For example, the follow-up handler * could display a message summarizing the iterated parts of the * action; or, it could even scan the transcript for particular * messages and replace them with a summary. */ callAfterActionMain(obj) { /* if we don't have an after-action list yet, create one */ if (afterActionMainList == nil) afterActionMainList = new Vector(8); /* if this item isn't already in the list, add it */ if (afterActionMainList.indexOf(obj) == nil) afterActionMainList.append(obj); } /* list of methods to invoke after we've completed the action */ afterActionMainList = nil /* * the amount of time on the game clock that the action consumes - * by default, each action consumes one unit, but actions can * override this to consume more or less game time */ actionTime = 1 /* * Zero the action time in this action and any parent actions. This * should be used when a nested replacement action is to completely * take over the time-keeping responsibility for the entire turn; all * containing actions in this case are to take zero time, since the * nested action is the only part of the turn that will count for * timing. */ zeroActionTime() { /* clear my action time */ actionTime = 0; /* if we have a parent action, zero it and its parents */ if (parentAction != nil) parentAction.zeroActionTime(); } /* * Execute the action for a single set of objects. This runs the * full execution sequence for the current set of objects. * * Subclasses generally won't override this method, but will instead * override the methods that implement the individual steps in the * execution sequence. */ doActionOnce() { /* * Perform the sequence of operations to execute the action. If * an ExitSignal is thrown during the sequence, skip to the * end-of-turn processing. */ try { local result; local impReport; /* * Before doing any actual execution, check the command for * remapping. If we end up doing any remapping, the * remapping routine will simply replace the current command, * so we the remapping call will terminate the current action * with 'exit' and thus never return here. */ checkRemapping(); /* * If this is an implicit action, check for danger: we never * try a command implicitly when the command is obviously * dangerous. */ if (isImplicit) { /* * verify the action for an implicit command, checking * for actions that aren't allowe implicitly - we never * try a command implicitly when the command is (or * should be) obviously dangerous or is simply * non-obvious */ result = verifyAction(); /* * If the action can be performed, but can't be performed * implicitly, abort. Note that we only silently ignore * the action if it is allowed normally but not * implicitly: if it's not even allowed normally, we'll * simply fail later with the normal failure message, * since there's no harm in trying. */ if (result != nil && result.allowAction && !result.allowImplicit) abortImplicit; } /* * If this is an implicit command, display a message * indicating that we're performing the command. */ impReport = maybeAnnounceImplicit(); /* * Make one or two passes through verifications and * preconditions. If any precondition performs an implicit * command, we must run everything a second time to ensure * that the implicit command or commands did not invalidate * any earlier precondition or a verification. * * Run verifications before preconditions, because there * would be no point in applying implicit commands from * preconditions if the command verifies as illogical in the * first place. */ for (local iter = 1 ; iter <= 2 ; ++iter) { /* verify the action */ result = verifyAction(); /* * if verification doesn't allow the command to proceed, * show the reason and end the command */ if (result != nil && !result.allowAction) { /* show the result message */ result.showMessage(); /* mark the command as a failure */ gTranscript.noteFailure(); /* * if we have an implicit report, mark it as a mere * attempt, since the action can't be completed */ if (impReport != nil) impReport.noteJustTrying(); /* terminate the command */ exit; } /* * Check preconditions of the action. If we don't invoke * any implicit commands, we can stop here: nothing in * the game state will have changed, so there is no need * to re-verify or re-check preconditions. * * Only allow implicit actions on the first pass. Do not * perform implicit actions on the second pass, because * if we did so we could get into an infinite loop of * conflicting preconditions, where each pass would * reverse the state from the last pass. */ if (!checkPreConditions(iter == 1)) break; } /* * Disable sense caching once we start the action phase - * once we start making changes to game state, it's too much * work to figure out when to invalidate the cache, so simply * turn off caching entirely. * * Note that the sense cache will already be disabled if we * executed any implied commands, because the first implied * command will have disabled the cache as soon as it reached * its execution phase, and no one will have turned caching * back on. It does no harm to disable it again here. */ libGlobal.disableSenseCache(); /* if desired, run the "before" notifications before "check" */ if (gameMain.beforeRunsBeforeCheck) runBeforeNotifiers(); /* * Invoke the action's execution method. Catch any "exit * action" exceptions - these indicate that the action is * finished but that the rest of the command processing is to * proceed as normal. */ try { /* notify the actor of what we're about to do */ gActor.actorAction(); /* check the action */ checkAction(); /* if desired, run the "before" notifications after "check" */ if (!gameMain.beforeRunsBeforeCheck) runBeforeNotifiers(); /* execute the action */ execAction(); } catch (ExitActionSignal eaSig) { /* * an exit action signal was thrown - since we've now * skipped past any remaining action processing, simply * continue with the rest of the command processing as * normal */ } /* call afterAction for each object in the notify list */ notifyBeforeAfter(&afterAction); /* notify the actor's containers of the completed action */ gActor.forEachContainer(callRoomAfterAction); /* run the after-action processing */ afterAction(); } catch (ExitSignal exc) { /* the execution sequence is finished - simply stop here */ } } /* * Run the "before" notifiers: this calls beforeAction on everything * in scope, and calls roomBeforeAction on the actor's containers. */ runBeforeNotifiers() { /* run the before-action processing */ beforeAction(); /* * notify the actor's containers that an action is about * to take place within them */ gActor.forEachContainer(callRoomBeforeAction); /* call beforeAction for each object in the notify list */ notifyBeforeAfter(&beforeAction); } /* * Reset the message generation context for a sense change. This * can be called when something substantial happens in the midst of * a command, and we might need different message generation rules * before and after the change. For example, this is used when a * non-player character moves from one location to another, because * the NPC might want to generate leaving and arriving messages * differently in the two locations. */ recalcSenseContext() { /* tell the sense context capturer to recalculate the context */ senseContext.recalcSenseContext(); } /* * Maybe announce the action as an implied action. */ maybeAnnounceImplicit() { /* * if we're a remapped action, we'll actually want to announce * the original, not the remapping */ if (remappedFrom != nil) return remappedFrom.maybeAnnounceImplicit(); /* * if we're implicit, and we have an implicit announcement * message, announce the implicit action */ if (isImplicit && implicitMsg != nil) return gTranscript.announceImplicit(self, implicitMsg); /* we don't need to announce the implied action */ return nil; } /* * Announce the object of an action. This should be used for each * iteration of a command that takes objects to announce the objects * on this iteration. * * We announce an object under several circumstances: * * - If we are iterating through multiple objects, we'll show the * current object to show the player the individual step in the * command being performed. * * - If 'all' was used to specify the object, we'll announce it even * if only one object is involved, to make it clear to the player * exactly what we chose as a match. * * - If we are executing the command on a single object, and the * object was chosen through disambiguation of a set of ambiguous * choices, and some of the discarded possibilities were logical but * less so than the chosen object, we'll show the assumption we * made. In such cases, our assumption is not necessarily correct, * so we'll tell the user about our choice explicitly by way of * confirmation - this gives the user a better chance of noticing * quickly if our assumption was incorrect. * * - If we supplied a default for a missing noun phrase in the * player's command, we'll show what we chose. Since the player * didn't say what they meant, we'll make it plain that we're * providing an assumption about what we thought they must have * meant. * * 'info' is the ResolveInfo object describing this resolved object, * and 'numberInList' is the total number of objects we're iterating * over for this object function (direct object, indirect object, * etc). 'whichObj' is one of the object function constants * (DirectObject, IndirectObject, etc) describing which object we're * mentioning; the language-specific message generator might use * this in conjunction with the action to include a preposition with * the displayed phrase, for example, or choose an appropriate * inflection. */ announceActionObject(info, numberInList, whichObj) { /* * Show prefix announcements only if the player character is * performing the action. For NPC's, we don't use the prefix * format, because it doesn't work as well for NPC result * reports; instead, the NPC versions of the library messages * tend to use sufficiently detailed reports that the prefix * isn't required (for example, "Bob takes the iron key" rather * than just "Taken"). */ if (gActor.isPlayerChar) { /* check for the various announcements */ if (maybeAnnounceMultiObject(info, numberInList, whichObj)) { /* we've done a multi announcement, so we're now done */ } else if ((info.flags_ & UnclearDisambig) != 0 || ((info.flags_ & ClearDisambig) != 0 && gameMain.ambigAnnounceMode == AnnounceClear)) { /* show the object, since we're not certain it's right */ gTranscript.announceAmbigActionObject(info.obj_, whichObj); } else if ((info.flags_ & DefaultObject) != 0 && !(info.flags_ & AnnouncedDefaultObject)) { /* * Show the object, since we supplied it as a default. * At this stage in the command, we have resolved * everything. */ gTranscript.announceDefaultObject( info.obj_, whichObj, self, true); /* note that we've announced this object */ info.flags_ |= AnnouncedDefaultObject; } } } /* announce a multi-action object, if appropriate */ maybeAnnounceMultiObject(info, numberInList, whichObj) { /* * announce if we have more than one object, or we're set to * announce this object in any case */ if (numberInList > 1 || (info.flags_ & AlwaysAnnounce) != 0) { /* show the current object of a multi-object action */ gTranscript.announceMultiActionObject( info.multiAnnounce, info.obj_, whichObj); /* tell the caller we made an announcement */ return true; } /* tell the caller we didn't make an announcement */ return nil; } /* * Pre-calculate the multi-object announcement text for each object. * This is important because these announcements might choose a form * for the name that distinguishes it from the other objects in the * iteration, and the basis for distinction might be state-dependent * (such as the object's current owner or location), and the relevant * state might change as we iterate over the objects. From the * user's perspective, they're referring to the objects based on the * state at the start of the command, so the user will expect to see * names based on the that state. */ cacheMultiObjectAnnouncements(lst, whichObj) { /* run through the list and cache each object's announcement */ foreach (local cur in lst) { /* calculate and cache this object's multi-object announcement */ cur.multiAnnounce = libMessages.announceMultiActionObject( cur.obj_, whichObj, self); } } /* get the list of resolved objects in the given role */ getResolvedObjList(which) { /* * the base action doesn't have any objects in any roles, so just * return nil; subclasses need to override this */ return nil; } /* * Announce a defaulted object list, if appropriate. We'll announce * the object if we have a single object in the given resolution * list, it was defaulted, and it hasn't yet been announced. */ maybeAnnounceDefaultObject(lst, which, allResolved) { /* * if the list has exactly one element, and it's marked as a * defaulted object, and it hasn't yet been announced, announce * it */ if (lst != nil && lst.length() == 1 && (lst[1].flags_ & DefaultObject) != 0 && !(lst[1].flags_ & AnnouncedDefaultObject)) { /* announce the object */ gTranscript.announceDefaultObject( lst[1].obj_, which, self, allResolved); /* * we've now announced the object; mark it as announced so * we don't show the same announcement again */ lst[1].flags_ |= AnnouncedDefaultObject; } } /* * "Pre-announce" a common object for a command that might involve * iteration over other objects. For example, in "put all in box", * the box is common to all iterations of the command, so we would * want to preannounce it, if it needs to be announced at all, * before the iterations of the command. * * We'll announce the object only if it's marked as defaulted or * unclearly disambiguated, and then only if the other list will be * announcing its objects as multi-action objects. However, we do * not pre-announce anything for a remapped action, because we'll * show the full action description for each individually announced * object, so we don't need or want a separate announcement for the * group. * * Returns true if we did any pre-announcing, nil otherwise. If we * return true, the caller should not re-announce this object during * the iteration, since our pre-announcement is common to all * iterations. */ preAnnounceActionObject(info, mainList, whichObj) { /* do not pre-announce anything for a remapped action */ if (isRemapped()) return nil; /* * determine if the main list will be announcing its objects - * it will if it has more than one object, or if its one object * is marked as "always announce" */ if (mainList.length() > 1 || (mainList[1].flags_ & AlwaysAnnounce) != 0) { /* * we will be announcing the main list object or objects, so * definitely pre-announce this object if appropriate */ announceActionObject(info, 1, whichObj); /* tell the caller we pre-announced the object */ return true; } /* we didn't pre-announce the object */ return nil; } /* * Run our action-specific pre-processing. By default, we do * nothing here. */ beforeAction() { } /* * Check the action. This runs the 'check' phase, and must be * overridden for each subclass. * * Intransitive actions don't generally have to do anything in the * 'check' phase, since they can simply do any necessary checks in * the 'execute' phase that runs immediately after 'check'. This * phase is separated out from 'execute' mostly for the benefit of * transitive actions, where the 'check' phase gives the involved * objects a chance to object to the action. */ checkAction() { } /* * Execute the action. This must be overridden by each subclass. * * Intransitive actions must do all of their work in this routine. * In most cases, transitive actions will delegate processing to one * or more of the objects involved in the command - for example, * most single-object commands will call a method in the direct * object to carry out the command. */ execAction() { /* by default, just show the 'no can do' message */ mainReport(&cannotDoThatMsg); } /* * Run our action-specific post-processing. By default, we do * nothing here. */ afterAction() { } /* * Verify the action. Action subclasses with one or more objects * should call object verification routines here. Returns a * VerifyResultList with the results, or nil if there are no * verification results at all. A nil return should be taken as * success, not failure, because it means that we found no objection * to the command. */ verifyAction() { /* * there are no objects in the default action, but we might have * pre-condition verifiers */ return callVerifyPreCond(nil); } /* * Initialize tentative resolutions for other noun phrases besides * the one indicated. */ initTentative(issuingActor, targetActor, whichObj) { /* by default, we have no noun phrases to tentatively resolve */ } /* * Check for remapping the action. This should check with each * resolved object involved in the command to see if the object wants * to remap the action to a new action; if it does, the object must * replace the current action (using replaceAction or equivalent). * Note that replacing the action must use 'exit' to terminate the * original action, so this will never return if remapping actually * does occur. */ checkRemapping() { /* by default, we have no objects, so we do nothing here */ } /* * Invoke a callback with a verify results list in gVerifyResults, * using the existing results list or creating a new one if there is * no existing one. Returns the results list used. */ withVerifyResults(resultsSoFar, obj, func) { local oldResults; /* if we don't already have a result list, create one */ if (resultsSoFar == nil) resultsSoFar = new VerifyResultList(); /* remember the old global results list */ oldResults = gVerifyResults; /* install the new one */ gVerifyResults = resultsSoFar; /* make sure we restore the old result list on the way out */ try { /* invoke the callback */ (func)(); } finally { /* restore the old result list */ gVerifyResults = oldResults; } /* return the result list */ return resultsSoFar; } /* * Verify the non-object-related pre-conditions. This runs * verification on each of the pre-condition objects defined for the * action. */ callVerifyPreCond(resultSoFar) { /* look at each of our preconditions */ foreach (local cond in preCond) { /* * If this precondition defines a verifier, call it. Check * to see if we have a verifier defined first so that we can * avoid creating a result list if we won't have any use for * it. */ if (cond.propDefined(&verifyPreCondition)) { /* * invoke the pre-condition verifier - this is an * action-level verifier, so there's no object involved */ resultSoFar = withVerifyResults(resultSoFar, nil, {: cond.verifyPreCondition(nil) }); } } /* return the latest result list */ return resultSoFar; } /* * Call a catch-all property on the given object. * * actionProp is the custom per-object/per-action property that we * normally invoke to process the action. For example, if we're * processing verification for the direct object of Take, this would * be &verifyDobjTake. * * defProp is the default property that corresponds to actionProp. * This is the per-object/default-action property that we invoke * when the object doesn't provide a "more specialized" version of * actionProp - that is, if the object doesn't define or inherit * actionProp at a point in its class hierarchy that is more * specialized than the point at which it defines defProp, we'll * call defProp. If there is a more specialized definition of * actionProp for the object, it effectively overrides the default * handler, so we do not invoke the default handler. * * allProp is the catch-all property corresponding to actionProp. * We invoke this property in all cases. * * Returns true if there is indeed a Default property that overrides * the action, nil if not. */ callCatchAllProp(obj, actionProp, defProp, allProp) { /* always invoke the catch-all property first */ obj.(allProp)(); /* * invoke the default property only if the object doesn't have a * "more specialized" version of actionProp */ if (!obj.propHidesProp(actionProp, defProp)) { /* the Default overrides the action-specific method */ obj.(defProp)(); /* tell the caller the Default routine handled it */ return true; } else { /* tell the caller to call the action-specific property */ return nil; } } /* * Call a verification routine. This creates a results object and * makes it active, then invokes the given verification routine on * the given object. * * We call verification directly on the object, and we also call * verification on the object's preconditions. * * If resultSoFar is non-nil, it is a VerifyResultList that has the * results so far - this can be used for multi-object verifications * to gather all of the verification results for all of the objects * into a single result list. If resultSoFar is nil, we'll create a * new result list. */ callVerifyProp(obj, verProp, preCondProp, remapProp, resultSoFar, whichObj) { local remapInfo; /* check for remapping */ if ((remapInfo = obj.(remapProp)()) != nil) { /* call the remapped verify */ resultSoFar = remapVerify(whichObj, resultSoFar, remapInfo); /* * If the original object has a verify that effectively * "overrides" the remap - i.e., defined by an object that * inherits from the object where the remap is defined - then * run it by the overriding verify as well. */ local remapSrc = obj.propDefined(remapProp, PropDefGetClass); if (obj.propDefined(verProp) && overrides(obj, remapSrc, verProp)) { resultSoFar = withVerifyResults( resultSoFar, obj, function() { obj.(verProp)(); }); } /* return the results */ return resultSoFar; } /* initialize tentative resolutions for other noun phrases */ initTentative(gIssuingActor, gActor, whichObj); /* * run the verifiers in the presence of a results list, and * return the result list */ return withVerifyResults(resultSoFar, obj, function() { local lst; /* * check the object for a default or catch-all verifier, and * call it if present; if there isn't a default that * overrides the action-specific verifier, continue to the * action-specific verifer */ if (!callCatchAllProp( obj, verProp, objectRelations.verifyDefaultProps[whichObj], objectRelations.verifyAllProps[whichObj])) { /* * invoke the action-specific verifier method - this * will update the global verification results object * with the appropriate status for this action being * performed on this object */ obj.(verProp)(); } /* * Check the pre-conditions defined for this action on this * object. For each one that has a verifier, invoke the * verifier. */ lst = getObjPreConditions(obj, preCondProp, whichObj); if (lst != nil) foreach (local cur in lst) cur.verifyPreCondition(obj); }); } /* * Get the precondition list for an object. whichObj is the object * role of the object whose preconditions we're retrieving; this is * nil if we're looking for action-level preconditions. */ getObjPreConditions(obj, preCondProp, whichObj) { local allPreProp; local defPreProp; local pre; /* * if we're looking for action preconditions, there are no * default or catch-all properties, so simply get the * preconditions from the action */ if (whichObj == nil) return obj.(preCondProp); /* get the default-action and catch-all precondition properties */ defPreProp = objectRelations.preCondDefaultProps[whichObj]; allPreProp = objectRelations.preCondAllProps[whichObj]; /* * Check for an "overriding" default-action handler. If we have * a default-action handler that hides the specific handler for * this action, use the default handler's precondition list * instead. Otherwise, use the specific action preconditions. */ if (obj.propHidesProp(defPreProp, preCondProp)) pre = obj.(defPreProp); else pre = obj.(preCondProp); /* if we have catch-all preconditions, add them to the list */ if (obj.propDefined(allPreProp)) { /* get the catch-all preconditions */ local allPre = obj.(allPreProp); /* add them to the list so far (if any) */ pre = (pre == nil ? allPre : pre + allPre); } /* return the precondition list */ return pre; } /* * Verify that some sort of handling for this action is defined on * at least one of the given objects. If we have no handlers at all * definfed, we'll add an "illogical" status to the verification * results to indicate that the action is not defined on this * object. This check provides a last resort for verbs with no * handling at all defined on the involved objects, to ensure that * the verb won't go completely unprocessed. * * Each entry in objList is an object involved in the action. For * each entry in objList, there must be *THREE* entries in propList: * a verify property, a check property, and an action property. If * any of these three properties is defined on the corresponding * object, we'll allow the command to proceed. If we can find none * of the given handler properties on any of our objects, we'll add * an "illogical" verify result. */ verifyHandlersExist(objList, propList, result) { /* * check each object to see if any define their corresponding * action property */ for (local i = 1, local j = 1, local len = objList.length() ; i <= len ; ++i, j += 3) { /* check each of the associated handler properties */ for (local k = 0 ; k < 3 ; ++k) { /* check this handler property */ if (objList[i].propDefined(propList[j + k])) { /* we've found a handler, so we can proceed */ return result; } } } /* * None of the objects defines an appropriate action method, so * this verifies as illogical. If there's no result list so * far, create one. */ if (result == nil) result = new VerifyResultList(); /* add an "illogical" status to the results */ result.addResult(new IllogicalVerifyResult(&cannotDoThatMsg)); /* return the result */ return result; } /* * Call the beforeAction or afterAction method for each object in * the notification list. */ notifyBeforeAfter(prop) { local lst; /* get a table of potential notification receivers */ lst = getNotifyTable(); /* go through the table and notify each object */ lst.forEachAssoc({obj, val: obj.(prop)()}); } /* * Get the list of objects to notify before or after the action has * been performed. */ getNotifyTable() { local tab; local curObjs; local actor; /* stash the current actor in a local for faster reference */ actor = gActor; /* start with everything connected by containment to the actor */ tab = actor.connectionTable(); /* add the actor's explicitly registered notification list */ foreach (local cur in actor.getActorNotifyList()) tab[cur] = true; /* add the items explicitly registered in the actor's location(s) */ actor.forEachContainer( {loc: loc.getRoomNotifyList().forEach( {obj: tab[obj] = true}) }); /* get the list of objects directly involved in the command */ curObjs = getCurrentObjects(); /* * add any objects explicitly registered with the objects * directly involved in the command */ foreach (local cur in curObjs) { /* add each item in the object's notify list */ foreach (local ncur in cur.getObjectNotifyList()) tab[ncur] = true; } /* * Remove from the list all of the actor's containers. These * will be notified via the more specific room notification * methods, so we don't want to send them generic notifiers as * well. */ tab.forEachAssoc(function(obj, val) { if (actor.isIn(obj)) tab.removeElement(obj); }); /* if we have any explicitly registered objects, add them */ if (beforeAfterObjs != nil) { /* add each object in the list of explicitly registered objects */ foreach (local cur in beforeAfterObjs) tab[cur] = true; } /* return the final table */ return tab; } /* * Register an object for explicit inclusion in beforeAction and * afterAction notifications. This can be used to register objects * that might not be connected by containment or otherwise * notifiable by normal means. If this is called after the * beforeAction notification loop has already started, then the * object will only be sent an afterAction notification. */ addBeforeAfterObj(obj) { /* if we don't yet have a before/after list, create one */ if (beforeAfterObjs == nil) beforeAfterObjs = new Vector(16); /* add the object to the list */ beforeAfterObjs.append(obj); } /* vector of objects requiring explicit before/after notification */ beforeAfterObjs = nil /* * Get the list of all of the objects (direct object, indirect * object, and any additional objects for actions with three or more * object roles) involved in the current execution. This is valid * only during a call to doActionOnce(), since that's the only time * a particular set of objects are selected for the action. * * By default, an action has no objects roles at all, so we'll just * return an empty list. */ getCurrentObjects() { return []; } /* * Set the current objects. This takes a list of the same form * returned by getCurrentObjects(). */ setCurrentObjects(lst) { } /* * Check any pre-conditions for the action. * * This should check all of the conditions that must be met for the * action to proceed. If any pre-condition can be met by running an * implicit command first, that implicit command should be executed * here. If any pre-condition cannot be met, this routine should * notify the actor and throw an ExitSignal. * * Returns true if any implicit commands are executed, nil if not. * Implicit commands can only be attempted if allowImplicit is true; * if this is nil, a precondition must simply fail (by displaying an * appropriate failure report and using 'exit') without attempting * an implicit command if its assertion does not hold. */ checkPreConditions(allowImplicit) { /* check each condition in our action preconditions */ return callPreConditions(getPreCondDescList(), allowImplicit); } /* * Get the precondition descriptor list for the action. For the base * intransitive action type, this simply returns the list of * conditions for the action itself. */ getPreCondDescList() { /* get the action-level preconditions */ return getObjPreCondDescList(self, &preCond, nil, nil); } /* * Get a precondition descriptor list for the given object. This * returns a list of PreCondDesc objects that wrap the preconditions * for the given object in the given role for this action. */ getObjPreCondDescList(obj, preCondProp, checkArg, whichObj) { local lst; /* get the list of preconditions for the given object in its role */ lst = getObjPreConditions(obj, preCondProp, whichObj); /* if there are no preconditions, return an empty list */ if (lst == nil) return []; /* * wrap the precondition objects in descriptors, so that we can * keep track of the check argument that goes with these * preconditions */ return lst.mapAll({x: new PreCondDesc(x, checkArg)}); } /* * Call a method on all of the precondition objects in the * precondition list obtained from the given property of the given * object. */ callPreConditions(lst, allowImplicit) { local ret; local i; /* presume we won't call any implicit commands */ ret = nil; /* * note the original descriptor list order, so that we can retain * the current ordering when the execution order doesn't require * changes */ i = 0; lst.forEach({x: x.index_ = i++}); /* sort the precondition list by execution order */ lst = lst.sort(SortAsc, function(a, b) { local delta; /* if the execution orders differ, sort by execution order */ delta = a.cond_.preCondOrder - b.cond_.preCondOrder; if (delta != 0) return delta; /* otherwise, retain the original list order */ return a.index_ - b.index_; }); /* catch any 'exit' signals within the preconditions */ try { /* invoke the check method for each condition in the list */ foreach (local cur in lst) { /* * call this precondition; if it runs an implicit * command, note that we have run an implicit command */ if (cur.checkPreCondition(allowImplicit)) ret = true; } } catch (ExitSignal es) { /* * any 'exit' that occurs within a precondition is a failure * for the enclosing action */ gTranscript.noteFailure(); /* re-throw the 'exit' to cancel the enclosing action */ throw es; } /* return true if any implicit commands were executed */ return ret; } /* * Our list of action-level pre-condition objects. These are the * conditions that apply to the overall action, not to the * individual objects involved. (Object-level pre-conditions are * attached to the objects, not to the action.) */ preCond = [] /* * Finish the result list for a resolved noun phrase. This is used * just before disambiguation. We'll give each object in the list a * chance to filter the list with filterResolveList, and we'll note * the noun phrase we matched in each resolved object. */ finishResolveList(lst, whichObj, np, requiredNum) { /* give each object a chance to filter the list */ foreach (local cur in lst) lst = cur.obj_.filterResolveList( lst, self, whichObj, np, requiredNum); /* stash the noun phrase in each object in the list */ foreach (local cur in lst) cur.np_ = np; /* return the list */ return lst; } /* * Get a list of verification results for the given ResolveInfo * objects, sorted from best to worst. Each entry in the returned * list will be a VerifyResultList object whose obj_ property is set * to the ResolveInfo object for which it was generated. */ getSortedVerifyResults(lst, verProp, preCondProp, remapProp, whichObj, np, requiredNum) { local results; local idx; /* if there's nothing in the list, we're done */ if (lst == []) return lst; /* * Before we run verification, give each object in the list a * chance to do its own filtering on the entire list. This form * of filtering allows an object to act globally on the list, so * that it can take special action according to the presence or * absence of other objects, and can affect the presence of other * objects. */ lst = finishResolveList(lst, whichObj, np, requiredNum); /* create a vector to hold the verification results */ results = new Vector(lst.length()); /* * Call the given verifier method on each object, noting each * result. */ idx = 0; foreach (local cur in lst) { local curResult; /* call the verifier method and note the current result */ curResult = callVerifyProp(cur.obj_, verProp, preCondProp, remapProp, nil, whichObj); /* * save the ResolveInfo in the verify result list object, so * that we can figure out later (after sorting the results) * which original ResolveInfo this verification result * applies to */ curResult.obj_ = cur; /* remember the original list order */ curResult.origOrder = idx++; /* add this verify result to our result vector */ results.append(curResult); } /* * Sort the results in descending order of logicalness, and * return the sorted list. When results are equivalently * logical, keep the results in their existing order. */ return results.toList().sort(SortDesc, function(x, y) { /* compare the logicalness */ local c = x.compareTo(y); /* * if it's the same, keep in ascending pluralOrder - note * that we must reverse the sense of this comparison, since * we're sorting the overall list in descending order */ if (c == 0) c = (y.obj_.obj_.pluralOrder - x.obj_.obj_.pluralOrder); /* if they're otherwise the same, preserve the original order */ if (c == 0) c = (y.origOrder - x.origOrder); /* return the result */ return c; }); } /* * Combine any remapped verify results in the given verify result * list. We will remove any result that was remapped to a different * object if and only if the target of the remapping appears in the * list and has the same results as the remapped original. When * objects are remapped in this fashion, they become effectively * equivalent for the purposes of this command, so we don't have to * distinguish between them for disambiguation or defaulting * purposes. */ combineRemappedVerifyResults(lst, role) { /* scan each element in the list */ for (local i = 1, local len = lst.length() ; i <= len ; ++i) { local cur = lst[i]; /* if this element has been remapped, consider it further */ if (cur.remapTarget_ != nil) { local other; /* * scan for another entry in the list that matches us * for remapping purposes */ other = lst.indexWhich( {x: x != cur && cur.matchForCombineRemapped(x, self, role)}); /* * if we found another entry, delete the other entry, * since it is indistinguishable from this entry */ if (other != nil) { /* remove this element from the list */ lst -= cur; /* adjust our list counters for the deletion */ --i; --len; } } } /* return the updated list */ return lst; } /* * Filter an ambiguous object list using the given verification * method. We call the given verification method on each object, * noting the result, then find the best (most logical) result in * the list. We reduce the set to the objects that all have the * same best value - everything else in the list is less logical, so * we discard it. This gives us a set of objects that are all of * equivalent likelihood and all of the best likelihood of all the * objects. * * This is the typical way that we disambiguate a list of objects, * but this is merely a service routine, so individual actions can * choose to use this or other mechanisms as appropriate. */ filterAmbiguousWithVerify(lst, requiredNum, verProp, preCondProp, remapProp, whichObj, np) { local results; local discards; local keepers; local bestResult; local uniqueKeepers; /* if there's nothing in the list, there's nothing to do */ if (lst == []) return lst; /* first, filter out redundant facets */ lst = filterFacets(lst); /* * Call the verifier method on each object, and sort the results * from best to worst. */ results = getSortedVerifyResults(lst, verProp, preCondProp, remapProp, whichObj, np, requiredNum); /* note the best result value */ bestResult = results[1]; /* * ask the noun phrase to filter this list down to the ones it * wants to keep */ keepers = np.getVerifyKeepers(results); /* * Count the number of unique keepers - this is the number of * items in the keepers list that don't have any equivalents in * the keepers list. * * To calculate this number, start with the total number of * items in the list, and reduce it by one for each item with an * earlier equivalent in the list. Note that we only ignore * items with unique equivalents *earlier* in the list so that * we keep exactly one of each equivalent - if we ignored every * element with a unique equivalent elsewhere in the list, we'd * ignore every equivalent item, so we'd only count the items * with no equivalents at all. */ uniqueKeepers = keepers.length(); for (local i = 1, local cnt = keepers.length() ; i <= cnt ; ++i) { local eqIdx; /* check for a unique equivalent earlier in the list */ eqIdx = keepers.indexWhich( {x: x.obj_.obj_.isVocabEquivalent(keepers[i].obj_.obj_)}); if (eqIdx != nil && eqIdx < i) { /* * this one has an earlier equivalent, so don't include * it in the unique item count */ --uniqueKeepers; } } /* * If we found more items to keep than were required by the * caller, we were not able to reduce the set to an unambiguous * subset. In this case, keep *all* of the items that are * logical. */ if (uniqueKeepers > requiredNum) { local allAllowed; /* filter so that we keep all of the logical results */ allAllowed = results.subset({x: x.allowAction}); /* if that list is non-empty, use it as the keepers */ if (allAllowed.length() != 0) keepers = allAllowed; } /* * Get the list of discards - this is the balance of the * original result list after removing the best ones. */ discards = results - keepers; /* * We now have the set of objects we want, but the entries in * the list are all VerifyResultList instances, and we just want * the objects - pull out a list of just the ResolveInfo, and * return that. */ keepers = keepers.mapAll({x: x.obj_}); /* * Check to see if we should set flags in the results. If we * eliminated any objects, flag the remaining objects as having * been selected through disambiguation. If the best of the * discarded objects were logical, flag the survivors as * "unclear," because we only selected them as better than the * discards, and not because they were the only possible * choices. */ if (discards != []) { local addedFlags; /* * We have reduced the set. If the best of the discards was * ranked as highly as the survivors at the coarsest level, * flag the survivors as having been "unclearly" * disambiguated; otherwise, mark them as "clearly" * disambiguated. */ if (keepers.indexOf(bestResult.obj_) != nil && (discards[1].getEffectiveResult().resultRank == bestResult.getEffectiveResult().resultRank)) { /* * we had to make a choice that discarded possibilities * that were valid, though not as good as the one we * chose - mark the objects as being not perfectly clear */ addedFlags = UnclearDisambig; /* * if the keepers and the rejects are all basic * equivalents, don't bother flagging this as unclear, * since there's no point in mentioning that we chose * one basic equivalent over another, as they all have * the same name */ if (BasicResolveResults.filterWithDistinguisher( keepers + discards.mapAll({x: x.obj_}), basicDistinguisher).length() == 1) { /* they're all basic equivalents - mark as clear */ addedFlags = ClearDisambig; } } else { /* the choice is clear */ addedFlags = ClearDisambig; } /* add the flags to each survivor */ foreach (local cur in keepers) cur.flags_ |= addedFlags; } /* return the results */ return keepers; } /* * Filter a plural list with a verification method. We'll reduce * the list to the subset of objects that verify as logical, if * there are any. If there are no logical objects in the list, * we'll simply return the entire original list. */ filterPluralWithVerify(lst, verProp, preCondProp, remapProp, whichObj, np) { local results; /* if there's nothing in the list, there's nothing to do */ if (lst == []) return lst; /* first, filter out redundant facets */ lst = filterFacets(lst); /* * Call the verifier method on each object, and sort the results * from best to worst. */ results = getSortedVerifyResults(lst, verProp, preCondProp, remapProp, whichObj, np, nil); /* * If the best (and thus first) result allows the action, filter * the list to keep only the "keepers," as determined by the noun * phrase. Otherwise, there are no allowed results, so return * the original list. */ if (results[1].allowAction) results = np.getVerifyKeepers(results); /* return the resolve results objects from the list */ return results.mapAll({x: x.obj_}); } /* * Filter out redundant facets of the same object. The various * facets of an object are equivalent to the parser. An object that * has multiple facets is meant to appear to be one game world * object from the perspective of a character - the multiple facet * objects are an internal implementation detail. */ filterFacets(lst) { local result; local actor = gActor; /* * create a vector for the result list, presuming we'll keep all * of the original list elements */ result = new Vector(lst.length(), lst); /* check for facets */ foreach (local cur in lst) { local allFacets; /* if this item has any facets, check for them in our results */ allFacets = cur.obj_.getFacets() + cur.obj_; if (allFacets.length() != 0) { local inScopeFacets; local best; /* make a new list of the facets that appear in the results */ inScopeFacets = allFacets.subset( {f: result.indexWhich({r: r.obj_ == f}) != nil}); /* pick the best of those facets */ best = findBestFacet(actor, inScopeFacets); /* * remove all of the facets besides the best one from the * result list - this will ensure that we have only the * single best facet in the final results */ foreach (local r in result) { /* * if this result list item is in the facet list, and * it's not the best facet, delete it from the result * list */ if (r.obj_ != best && inScopeFacets.indexOf(r.obj_) != nil) result.removeElement(r); } } } /* return the result list */ return result.toList(); } /* * Get a default object using the given verification method. We'll * start with the 'all' list, then use the verification method to * reduce the list to the most likely candidates. If we find a * unique most likely candidate, we'll return a ResolveInfo list * with that result; otherwise, we'll return nothing, since there is * no suitable default. */ getDefaultWithVerify(resolver, verProp, preCondProp, remapProp, whichObj, np) { local lst; local results; local bestResult; /* * Start with the 'all' list for this noun phrase. This is the * set of every object that we consider obviously suitable for * the command, so it's a good starting point to guess at a * default object. */ lst = resolver.getAllDefaults(); /* eliminate objects that can't be defaults for this action */ lst = lst.subset({x: !x.obj_.hideFromDefault(self)}); /* * reduce equivalent items to a single instance of each * equivalent - if we have several equivalent items that are * equally good as defaults, we can pick just one */ lst = resolver.filterAmbiguousEquivalents(lst, np); /* if we have no entries in the list, there is no default */ if (lst == []) return nil; /* * get the verification results for these objects, sorted from * best to worst */ results = getSortedVerifyResults(lst, verProp, preCondProp, remapProp, whichObj, np, nil); /* eliminate redundant remapped objects */ results = combineRemappedVerifyResults(results, whichObj); /* note the best result */ bestResult = results[1]; /* * if the best item is not allowed as an implied object, there * is no default */ if (!bestResult.allowImplicit) return nil; /* * The best item must be uniquely logical in order to be a * default - if more than one item is equally good, it makes no * sense to assume anything about what the user meant. So, if * we have more than one item, and the second item is equally as * logical as the first item, we cannot supply a default. (The * second item cannot be better than the first item, because of * the sorting - at most, it can be equally good.) */ if (results.length() != 1 && bestResult.compareTo(results[2]) == 0) return nil; /* * We have a uniquely logical item, so we can assume the user * must have been referring to this item. Return the * ResolveInfo for this item (which the verify result sorter * stashed in the obj_ property of the verify result object). * * Before returning the list, clear the 'all' flag in the result * (getAll() set that flag), and replace it with the 'default' * flag, since the object is an implied default. */ bestResult.obj_.flags_ &= ~MatchedAll; bestResult.obj_.flags_ |= DefaultObject; /* return the result list */ return [bestResult.obj_]; } /* * A synthesized Action (one that's generated by something other than * parsing a command line, such as an event action or nested action) * won't have a parser token list attached to it. If we're asked to * get the token list, we need to check for this possibility. If we * don't have a token list, but we do have a parent action, we'll * defer to the parent action. Otherwise, we'll simply return nil. */ getOrigTokenList() { /* if we don't have a token list, look elsewhere */ if (tokenList == nil) { /* if we have a parent action, defer to it */ if (parentAction != nil) return parentAction.getOrigTokenList(); /* we have nowhere else to look, so return an empty list */ return []; } /* inherit the standard handling from BasicProd */ return inherited(); } /* * Create a topic qualifier resolver. This type of resolver is used * for qualifier phrases (e.g., possessives) within topic phrases * within objects of this verb. Topics *usually* only apply to * TopicActionBase subclasses, but not exclusively: action remappings * can sometimes require a topic phrase from one action to be * resolved in the context of another action that wouldn't normally * involve a topic phrase. That's why this is needed on the base * Action class. */ createTopicQualifierResolver(issuingActor, targetActor) { /* create and return a topic qualifier object resolver */ return new TopicQualifierResolver(self, issuingActor, targetActor); } /* * List of objects that verified okay on a prior pass. This is a * scratch-pad for use by verifier routines, to keep track of work * they've already done. A few verifiers use this as a way to * detect when an implicit action actually finished the entire job, * which would in many cases result in a verify failure if not * checked (because a command that effects conditions that already * hold is normally illogical); by tracking that the verification * previously succeeded, the verifier can know that the action * should be allowed to proceed and do nothing. */ verifiedOkay = [] /* * Get the missing object response production for a given resolver * role. (The base action doesn't have any objects, so there's no * such thing as a missing object query.) */ getObjResponseProd(resolver) { return nil; } ; /* * Call the roomBeforeAction method on a given room's containing rooms, * then on the room itself. */ callRoomBeforeAction(room) { /* first, call roomBeforeAction on the room's containers */ room.forEachContainer(callRoomBeforeAction); /* call roomBeforeAction on this room */ room.roomBeforeAction(); } /* * Call the roomAfterAction method on a given room, then on the room's * containing rooms. */ callRoomAfterAction(room) { /* first, call roomAfterAction on this room */ room.roomAfterAction(); /* next, call roomAfterAction on the room's containers */ room.forEachContainer(callRoomAfterAction); } /* ------------------------------------------------------------------------ */ /* * Intransitive Action class - this is an action that takes no objects. * In general, each subclass should implement its action handling in its * execAction() method. */ class IAction: Action /* * resolve my noun phrases to objects */ resolveNouns(issuingActor, targetActor, results) { /* * We have no objects to resolve. The only thing we have to do * is note in the results our number of structural noun slots * for the verb, which is zero, since we have no objects at all. */ results.noteNounSlots(0); } /* * Execute the action. */ doActionMain() { /* * we have no objects to iterate, so simply run through the * execution sequence once */ doActionOnce(); } ; /* ------------------------------------------------------------------------ */ /* * "All" and "Default" are a pseudo-actions used for dobjFor(All), * iobjFor(Default), and similar catch-all handlers. These are never * executed as actual actions, but we define them so that dobjFor(All) * and the like won't generate any warnings for undefined actions. */ class AllAction: object ; class DefaultAction: object ; /* ------------------------------------------------------------------------ */ /* * Transitive Action class - this is an action that takes a direct * object. * * For simplicity, this object is its own object resolver - we really * don't need a separate resolver object because we have only one object * list for this verb. (In contrast, an action with both a direct and * indirect object might need separate resolution rules for the two * objects, and hence would need separate resolver objects for the two.) * * The advantage of implementing the Resolver behavior in this object, * rather than using a separate object, is that it's less trouble to * override object resolution rules - simply override the resolver * methods in the subclass where you define the grammar rule for the * action. */ class TAction: Action, Resolver construct() { /* inherit only the Action constructor */ inherited Action.construct(); } resetAction() { /* inherit default handling */ inherited(); /* discard our cached resolver */ dobjResolver_ = nil; } /* * Create an action for retrying an original action with changes. */ createForRetry(orig) { local action; /* create the new action based on the original action */ action = createActionFrom(orig); /* * do not include the new command in undo, since it's merely * part of the enclosing explicit command */ action.includeInUndo = nil; /* mark the action as nested */ action.setNested(); /* * Even if the original action is implicit, don't announce this * action as implicit, because it's good enough to have * announced the original implicit action. Now, this new * command actually still is implicit if the original was - we * simply don't want to announce it as such. To suppress the * extra announcement while still retaining the rest of the * desired implicitness, simply set the implicitMsg property of * the new action to nil; when there's no implicitMsg, there's * no announcement. */ action.implicitMsg = nil; /* return the new action */ return action; } /* * Retry an intransitive action as a single-object action. We'll * obtain a indirect object using the normal means (first looking * for a default, then prompting the player if we can't find a * suitable default). 'orig' is the original zero-object action. * * This routine terminates with 'exit' if it doesn't throw some * other error. */ retryWithMissingDobj(orig, asker) { /* resolve and execute the replacement action */ resolveAndReplaceAction(createForMissingDobj(orig, asker)); } /* * Retry an action as a single-object action with an ambiguous * direct object. We'll ask which of the given possible objects is * intended. */ retryWithAmbiguousDobj(orig, objs, asker, objPhrase) { local action; local resolver; /* create a missing-direct-object replacement action */ action = createForMissingDobj(orig, asker); /* reduce the object list to the objects in scope */ resolver = action.getDobjResolver(gIssuingActor, gActor, true); objs = objs.subset({x: resolver.objInScope(x)}); /* plug in the ambiguous direct object list */ action.dobjMatch = new PreResolvedAmbigProd(objs, asker, objPhrase); /* resolve and execute the replacement action */ resolveAndReplaceAction(action); } /* * Test to see if askForDobj() would find a default direct object. * Returns true if there's a default, nil if not. If this returns * true, then askForDobj() will simply take the default and proceed; * otherwise, it will have to actually ask the user for the missing * information. */ testRetryDefaultDobj(orig) { /* create the new action for checking for a direct object */ local action = createForMissingDobj(orig, ResolveAsker); /* get the default dobj */ local def = action.getDefaultDobj( action.dobjMatch, action.getDobjResolver(gIssuingActor, gActor, nil)); /* if there's exactly one result, then we have a default */ return (def != nil && def.length() == 1); } /* * Create an instance of this action for retrying a given * single-object action with a missing direct object. */ createForMissingDobj(orig, asker) { /* create the action for a retry */ local action = createForRetry(orig); /* use an empty noun phrase for the new action's direct object */ action.dobjMatch = new EmptyNounPhraseProd(); /* set our custom response production and ResolveAsker */ action.dobjMatch.setPrompt(action.askDobjResponseProd, asker); /* initialize the new action with any pre-resolved parts */ action.initForMissingDobj(orig); /* return the new action */ return action; } /* * Initialize this action in preparation for retrying with a missing * direct object. This routine must copy any phrases from the * original action that have already been resolved. This base * TAction implementation obviously can't have anything pre-resolved * in the original, since the original must simply be an IAction. * Subclasses must override as appropriate for the kinds of base * actions from which they can be retried. */ initForMissingDobj(orig) { } /* * The root production to use to parse missing direct object * responses. By default, this is nounList, but individual actions * can override this as appropriate. * * Note that language modules might want to override this to allow * for special responses. For example, in English, some verbs might * want to override this with a specialized production that allows * the appropriate preposition in the response. */ askDobjResponseProd = nounList /* get the missing object response production for a given role */ getObjResponseProd(resolver) { /* if it's the direct object, return the dobj response prod */ if (resolver.whichObject == DirectObject) return askDobjResponseProd; /* otherwise use the default handling */ return inherited(resolver); } /* * Can the direct object potentially resolve to the given simulation * object? This only determines if the object is a *syntactic* * match, meaning that it can match at a vocabulary and grammar * level. This doesn't test it for logicalness or check that it's an * otherwise valid resolution. */ canDobjResolveTo(obj) { /* check our dobj match tree to see if it can resolve to 'obj' */ return dobjMatch.canResolveTo( obj, self, issuer_, actor_, DirectObject); } /* * Resolve objects. This is called at the start of command * execution to resolve noun phrases in the command to specific * objects. */ resolveNouns(issuingActor, targetActor, results) { /* note that we have a single noun slot (the direct object) */ results.noteNounSlots(1); /* * Ask the direct object noun phrase list to resolve itself, and * store the resulting object list. Since we're starting a * resolution pass through our objects, reset the resolver if * we're reusing it. */ dobjList_ = dobjMatch.resolveNouns( getDobjResolver(issuingActor, targetActor, true), results); } /* a transitive action has one noun phrase: the direct object */ predicateNounPhrases = [&dobjMatch] /* get the role of an object */ getRoleFromIndex(idx) { /* we only take a single object role - the direct object */ return (idx == 1 ? DirectObject : inherited(idx)); } /* get the resolved object in a given role */ getObjectForRole(role) { /* return the direct object if requested */ return (role == DirectObject ? getDobj() : inherited(role)); } /* get the match tree for the noun phrase in the given role */ getMatchForRole(role) { /* return the direct object match tree if requested */ return (role == DirectObject ? dobjMatch : inherited(role)); } /* get the 'verify' property for a given object role */ getVerifyPropForRole(role) { return (role == DirectObject ? verDobjProp : inherited(role)); } /* get the 'preCond' property for a given object role */ getPreCondPropForRole(role) { return (role == DirectObject ? preCondDobjProp : inherited(role)); } /* get the 'remap' property for a given object role */ getRemapPropForRole(role) { return (role == DirectObject ? remapDobjProp : inherited(role)); } /* get the ResolveInfo for the given object */ getResolveInfo(obj, oldRole) { local info = nil; /* scan our resolved direct object list for the given object */ if (dobjList_ != nil) info = dobjList_.valWhich({x: x.obj_ == obj}); /* if we didn't find one, create one from scratch */ if (info == nil) { /* * if there's anything in the old dobj role, copy the flags * and noun phrase to the new role */ if (dobjList_.length() > 0) { /* get the flags and noun phrase from the old role */ info = new ResolveInfo( obj, dobjList_[1].flags_, dobjList_[1].np_); } else { /* there's no old role, so start from scratch */ info = new ResolveInfo(obj, 0, nil); } } /* return what we found (or created) */ return info; } /* get the list of resolved objects in the given role */ getResolvedObjList(which) { return (which == DirectObject ? getResolvedDobjList() : inherited(which)); } /* get the list of resolved direct objects */ getResolvedDobjList() { /* * if we have a direct object list, return the objects from it; * otherwise, return an empty list */ return (dobjList_ == nil ? nil : dobjList_.mapAll({x: x.obj_})); } /* manually set the resolved objects - we'll set the direct object */ setResolvedObjects(dobj) { /* set the resolved direct object */ setResolvedDobj(dobj); } /* set the resolved direct object */ setResolvedDobj(dobj) { /* * set the direct object tree to a fake grammar tree that * resolves to our single direct object, in case we're asked to * resolve explicitly */ dobjMatch = new PreResolvedProd(dobj); /* * Set the resolved direct object list to the single object or * to the list of objects, depending on what we received. */ dobjList_ = makeResolveInfoList(dobj); /* set the current object as well */ dobjCur_ = (dobjList_.length() > 0 ? dobjList_[1].obj_ : nil); dobjInfoCur_ = (dobjList_.length() > 0 ? dobjList_[1] : nil); } /* manually set the unresolved object noun phrase match trees */ setObjectMatches(dobj) { /* * if it's a ResolveInfo, handle it as a resolved object; * otherwise handle it as a match tree to be resolved */ if (dobj.ofKind(ResolveInfo)) setResolvedDobj(dobj); else dobjMatch = dobj; } /* check that the resolved objects are in scope */ resolvedObjectsInScope() { /* check the direct object */ return getDobjResolver(gIssuingActor, gActor, true) .objInScope(dobjList_[1].obj_); } /* * Get a message parameter object for the action. We define 'dobj' * as the direct object, in addition to any inherited targets. */ getMessageParam(objName) { switch(objName) { case 'dobj': /* return the current direct object */ return dobjCur_; default: /* inherit default handling */ return inherited(objName); } } /* * Execute the action. We'll run through the execution sequence * once for each resolved direct object. */ doActionMain() { /* * Set the direct object list as the antecedent, using the * language-specific pronoun setter. Don't set pronouns for a * nested command, because the player didn't necessarily refer to * the objects in a nested command. */ if (parentAction == nil) gActor.setPronoun(dobjList_); /* we haven't yet canceled the iteration */ iterationCanceled = nil; /* pre-calculate the multi-object announcement text for each dobj */ cacheMultiObjectAnnouncements(dobjList_, DirectObject); /* run through the sequence once for each direct object */ for (local i = 1, local len = dobjList_.length() ; i <= len && !iterationCanceled ; ++i) { /* make this object our current direct object */ dobjCur_ = dobjList_[i].obj_; dobjInfoCur_ = dobjList_[i]; /* announce the object if appropriate */ announceActionObject(dobjList_[i], len, whichMessageObject); /* run the execution sequence for the current direct object */ doActionOnce(); /* if we're top-level, count the iteration in the transcript */ if (parentAction == nil) gTranscript.newIter(); } } /* get the precondition descriptor list for the action */ getPreCondDescList() { /* * return the inherited preconditions plus the conditions that * apply to the direct object */ return inherited() + getObjPreCondDescList(dobjCur_, preCondDobjProp, dobjCur_, DirectObject); } /* * Get the list of active objects. We have only a direct object, so * we'll return a list with the current direct object. */ getCurrentObjects() { return [dobjCur_]; } /* set the current objects */ setCurrentObjects(lst) { dobjCur_ = lst[1]; dobjInfoCur_ = nil; } /* * Verify the action. */ verifyAction() { local result; /* invoke any general (non-object) pre-condition verifiers */ result = callVerifyPreCond(nil); /* * invoke the verifier routine ("verXxx") on the current direct * object and return the result */ result = callVerifyProp(dobjCur_, verDobjProp, preCondDobjProp, remapDobjProp, result, DirectObject); /* verify that the action routine ("doXxx") exists on the object */ return verifyHandlersExist( [dobjCur_], [verDobjProp, actionDobjProp, checkDobjProp], result); } /* initialize tentative resolutions for other noun phrases */ initTentative(issuingActor, targetActor, whichObj) { /* * we have only one noun phrase, so there's nothing else to * tentatively resolve */ } /* * Check for remapping */ checkRemapping() { local remapInfo; /* check for remapping in the direct object */ if ((remapInfo = dobjCur_.(remapDobjProp)) != nil) { /* * we have a remapping, so apply it - note that this won't * return, since this will completely replace the command * and thus terminate the old command with 'exit' */ remapAction(nil, DirectObject, remapInfo); } } /* * Check the command. * * For a single-object transitive action, this runs the catch-all * 'check' properties (the dobjFor(Default) and dobjFor(All) 'check' * methods) on the direct object, then calls the individual 'check' * routine for this specific action. */ checkAction() { try { /* call the catch-all 'check' properties */ if (!callCatchAllProp(dobjCur_, checkDobjProp, &checkDobjDefault, &checkDobjAll)) { /* * the action-specific check routine overrides any * Default catch-all, so call the direct object's check * routine (its "checkXxx") method */ dobjCur_.(checkDobjProp)(); } } catch (ExitSignal es) { /* mark the action as a failure in the transcript */ gTranscript.noteFailure(); /* re-throw the signal */ throw es; } } /* * Execute the command. */ execAction() { /* call the catch-all 'action' properties */ if (!callCatchAllProp(dobjCur_, actionDobjProp, &actionDobjDefault, &actionDobjAll)) { /* * the verb-specific 'action' handler overrides any Default * action handler, so call the verb-specific 'action' * routine in the direct object (the "doXxx" method) */ dobjCur_.(actionDobjProp)(); } } /* * The direct object preconditions, verifier, remapper, check, and * action methods for this action. Each concrete action must define * these appropriately. By convention, the methods are named like * so: * *. preconditions: preCondDobjAction *. verifier: verDobjAction *. remap: remapDobjAction *. check: checkDobjAction *. action: actionDobjAction * * where the 'Action' suffix is replaced by the name of the action. * The DefineTAction macro applies this convention, so in most cases * library and game authors will never have to create all of those * property names manually. */ verDobjProp = nil preCondDobjProp = nil remapDobjProp = nil checkDobjProp = nil actionDobjProp = nil /* * Get my direct object resolver. If I don't already have one, * create one and cache it; if I've already cached one, return it. * Note that we cache the resolver because it can sometimes take a * bit of work to set one up (the scope list can in some cases be * complicated to calculate). We use the resolver only during the * object resolution phase; since game state can't change during * this phase, it's safe to keep a cached copy. */ getDobjResolver(issuingActor, targetActor, reset) { /* create a new resolver if we don't already have one cached */ if (dobjResolver_ == nil) dobjResolver_ = createDobjResolver(issuingActor, targetActor); /* reset the resolver if desired */ if (reset) dobjResolver_.resetResolver(); /* return it */ return dobjResolver_; } /* * Create a resolver for the direct object. By default, we are our * own resolver. Some actions might want to override this to create * and return a specialized resolver instance if special resolution * rules are needed. */ createDobjResolver(issuingActor, targetActor) { /* initialize myself as a resolver */ initResolver(issuingActor, targetActor); /* return myself */ return self; } /* * Does this action allow "all" to be used in noun phrases? By * default, we allow it or not according to a gameMain property. * * Note that the inventory management verbs (TAKE, TAKE FROM, DROP, * PUT IN, PUT ON) override this to allow "all" to be used, so * disallowing "all" here (or via gameMain) won't disable "all" for * those verbs. */ actionAllowsAll = (gameMain.allVerbsAllowAll) /* * Resolve 'all' for the direct object, given a list of everything * in scope. By default, we'll simply return everything in scope; * some actions might want to override this to return a more * specific list of objects suitable for 'all'. */ getAllDobj(actor, scopeList) { return scopeList; } /* filter an ambiguous direct object noun phrase */ filterAmbiguousDobj(lst, requiredNum, np) { /* filter using the direct object verifier method */ return filterAmbiguousWithVerify(lst, requiredNum, verDobjProp, preCondDobjProp, remapDobjProp, DirectObject, np); } /* filter a plural phrase */ filterPluralDobj(lst, np) { /* filter using the direct object verifier method */ return filterPluralWithVerify(lst, verDobjProp, preCondDobjProp, remapDobjProp, DirectObject, np); } /* get the default direct object */ getDefaultDobj(np, resolver) { /* get a default direct object using the verify method */ return getDefaultWithVerify(resolver, verDobjProp, preCondDobjProp, remapDobjProp, DirectObject, np); } /* get the current direct object of the command */ getDobj() { return dobjCur_; } /* get the full ResolveInfo associated with the current direct object */ getDobjInfo() { return dobjInfoCur_; } /* get the object resolution flags for the direct object */ getDobjFlags() { return dobjInfoCur_ != nil ? dobjInfoCur_.flags_ : 0; } /* get the number of direct objects */ getDobjCount() { return dobjList_ != nil ? dobjList_.length() : 0; } /* get the original token list of the current direct object phrase */ getDobjTokens() { return dobjInfoCur_ != nil && dobjInfoCur_.np_ != nil ? dobjInfoCur_.np_.getOrigTokenList() : nil; } /* get the original words (as a list of strings) of the current dobj */ getDobjWords() { local l = getDobjTokens(); return l != nil ? l.mapAll({x: getTokOrig(x)}) : nil; } /* the predicate must assign the direct object production tree here */ dobjMatch = nil /* my resolved list of direct objects */ dobjList_ = [] /* * The resolved direct object on which we're currently executing the * command. To execute the command, we iterate through the direct * object list, calling the execution sequence for each object in * the list. We set this to the current object in each iteration. */ dobjCur_ = nil /* the full ResolveInfo associated with dobjCur_ */ dobjInfoCur_ = nil /* my cached direct object resolver */ dobjResolver_ = nil /* -------------------------------------------------------------------- */ /* * Resolver interface implementation - for the moment, we don't need * any special definitions here, since the basic Resolver * implementation (which we inherit) is suitable for a single-object * action. */ /* -------------------------------------------------------------------- */ /* * private Resolver implementation details */ /* * Initialize me as a resolver. */ initResolver(issuingActor, targetActor) { /* remember the actors */ issuer_ = issuingActor; actor_ = targetActor; /* I'm the action as well as the resolver */ action_ = self; /* cache the actor's default scope list */ cacheScopeList(); } /* issuing actor */ issuer_ = nil /* target actor */ actor_ = nil /* * By default, our direct object plays the direct object role in * generated messages. Subclasses can override this if the resolved * object is to play a different role. Note that this only affects * generated messages; for parsing purposes, our object is always in * the DirectObject role. */ whichMessageObject = DirectObject ; /* ------------------------------------------------------------------------ */ /* * "Tentative" noun resolver results gather. This type of results * gatherer is used to perform a tentative pre-resolution of an object * of a multi-object action. * * Consider what happens when we resolve a two-object action, such as * "put in ". Since we have two objects, we obviously must * resolve one object or the other first; but this means that we must * resolve one object with no knowledge of the resolution of the other * object. This often makes it very difficult to resolve that first * object intelligently, because we'd really like to know something * about the other object. For example, if we first resolve the iobj of * "put in ", it would be nice to know which dobj we're * talking about, since we could reduce the likelihood that the iobj is * the dobj's present container. * * Tentative resolution addresses this need by giving us some * information about a later-resolved object while resolving an * earlier-resolved object, even though we obviously can't have fully * resolved the later-resolved object. In tentative resolution, we * perform the resolution of the later-resolved object, completely in * the dark about the earlier-resolved object(s), and come up with as * much information as we can. The important thing about this stage of * resolution is that we don't ask any interactive questions and we * don't count anything for ranking purposes - we simply do the best we * can and note the results, leaving any ranking or interaction for the * true resolution phase that we'll perform later. */ class TentativeResolveResults: ResolveResults construct(target, issuer) { setActors(target, issuer); } /* * ignore most resolution problems, since this is only a tentative * resolution pass */ noMatch(action, txt) { } noMatchPoss(action, txt) { } noVocabMatch(action, txt) { } noMatchForAll() { } noteEmptyBut() { } noMatchForAllBut() { } noMatchForListBut() { } noMatchForPronoun(typ, txt) { } reflexiveNotAllowed(typ, txt) { } wrongReflexive(typ, txt) { } noMatchForPossessive(owner, txt) { } noMatchForLocation(loc, txt) { } noteBadPrep() { } nothingInLocation(loc) { } unknownNounPhrase(match, resolver) { return []; } noteLiteral(txt) { } emptyNounPhrase(resolver) { return []; } zeroQuantity(txt) { } insufficientQuantity(txt, matchList, requiredNum) { } uniqueObjectRequired(txt, matchList) { } noteAdjEnding() { } noteIndefinite() { } noteMiscWordList(txt) { } notePronoun() { } noteMatches(matchList) { } incCommandCount() { } noteActorSpecified() { } noteNounSlots(cnt) { } noteWeakPhrasing(level) { } allowActionRemapping = nil /* * during the tentative phase, keep all equivalents - we don't want * to make any arbitrary choices among equivalents during this * phase, because doing so could improperly force a choice among * otherwise ambiguous resolutions to the other phrase */ allowEquivalentFiltering = nil /* * for ambiguous results, don't attempt to narrow things down - just * keep the entire list */ ambiguousNounPhrase(keeper, asker, txt, matchList, fullMatchList, scopeList, requiredNum, resolver) { return matchList; } /* * no interaction is allowed, so return nothing if we need to ask * for a missing object */ askMissingObject(asker, resolver, responseProd) { /* note that we have a missing noun phrase */ npMissing = true; /* return nothing */ return nil; } /* * no interaction is allowed, so return no tokens if we need to ask * for a literal */ askMissingLiteral(action, which) { return []; } /* no interaction is allowed during tentative resolution */ canResolveInteractively() { return nil; } /* * flag: the noun phrase we're resolving is a missing noun phrase, * which means that we'll ask for it to be filled in when we get * around to resolving it for real */ npMissing = nil ; /* * A dummy object that we use for the *tentative* resolution of a noun * phrase when the noun phrase doesn't match anything. This lets us * distinguish cases where we have a noun phrase that has an error from a * noun phrase that's simply missing. */ dummyTentativeObject: object ; dummyTentativeInfo: ResolveInfo obj_ = dummyTentativeObject flags_ = 0 ; /* ------------------------------------------------------------------------ */ /* * Transitive-with-indirect Action class - this is an action that takes * both a direct and indirect object. We subclass the basic one-object * action to add the indirect object handling. */ class TIAction: TAction resetAction() { /* inherit defaulting handling */ inherited(); /* discard our cached iobj resolver */ iobjResolver_ = nil; } /* * Retry a single-object action as a two-object action. We'll treat * the original action's direct object list as our direct object * list, and obtain an indirect object using the normal means (first * looking for a default, then prompting the player if we can't find * a suitable default). 'orig' is the original single-object action. * * This routine terminates with 'exit' if it doesn't throw some * other error. */ retryWithMissingIobj(orig, asker) { /* resolve and execute the replacement action */ resolveAndReplaceAction(createForMissingIobj(orig, asker)); } /* * Retry an action as a two-object action with an ambiguous indirect * object. We'll ask which of the given possible objects is * intended. */ retryWithAmbiguousIobj(orig, objs, asker, objPhrase) { /* create a missing-indirect-object replacement action */ local action = createForMissingIobj(orig, asker); /* reduce the object list to the objects in scope */ local resolver = action.getIobjResolver(gIssuingActor, gActor, true); objs = objs.subset({x: resolver.objInScope(x)}); /* plug in the ambiguous indirect object list */ action.iobjMatch = new PreResolvedAmbigProd(objs, asker, objPhrase); /* resolve and execute the replacement action */ resolveAndReplaceAction(action); } /* * Test to see if askForIobj() would find a default indirect object. * Returns true if there's a default, nil if not. If this returns * true, then askForIobj() will simply take the default and proceed; * otherwise, it will have to actually ask the user for the missing * information. */ testRetryDefaultIobj(orig) { local action; local def; /* create the new action for checking for an indirect object */ action = createForMissingIobj(orig, ResolveAsker); /* get the default iobj */ def = action.getDefaultIobj( action.iobjMatch, action.getIobjResolver(gIssuingActor, gActor, nil)); /* if there's exactly one result, then we have a default */ return (def != nil && def.length() == 1); } /* * Create an instance of this action for retrying a given * single-object action with a missing indirect object. */ createForMissingIobj(orig, asker) { /* create the new action based on the original action */ local action = createForRetry(orig); /* use an empty noun phrase for the new action's indirect object */ action.iobjMatch = new EmptyNounPhraseProd(); /* set our custom response production and ResolveAsker */ action.iobjMatch.setPrompt(action.askIobjResponseProd, asker); /* copy what we've resolved so far */ action.initForMissingIobj(orig); /* return the action */ return action; } /* * Initialize the action for retrying with a missing direct object. * * If we're trying a TIAction, we can only be coming from a TAction * (since that's the only kind of original action that can turn into * a two-object, at least in the base library). That means the * original action already has a direct object. Now, since we're * asking for a MISSING direct object, the only possibility is that * the original action's direct object is our INDIRECT object. For * example: SWEEP WITH BROOM is turning into SWEEP WITH * BROOM. */ initForMissingDobj(orig) { local origDobj = orig.getDobj(); /* * Set the indirect object in the new action to the direct object * from the original action. If there's no individual direct * object yet, we must be retrying the overall command, before we * started iterating through the individual dobjs, so copy the * entire dobj list from the original. */ iobjMatch = new PreResolvedProd(origDobj != nil ? origDobj : orig.dobjList_ ); } /* * Initialize the action for retrying with a missing indirect object. * * We can only be coming from a TAction, so the TAction will have a * direct object already. Simply copy that as our own direct * object. For example: UNLOCK DOOR is turning into UNLOCK DOOR * WITH . */ initForMissingIobj(orig) { local origDobj = orig.getDobj(); /* * Copy the direct object from the original. If there's no * individual direct object yet, we must be retrying the overall * command, before we started iterating through the individual * dobjs, so copy the entire dobj list. */ dobjMatch = new PreResolvedProd(origDobj != nil ? origDobj : orig.dobjList_); } /* * The root production to use to parse missing indirect object * responses. By default, this is singleNoun, but individual * actions can override this as appropriate. * * Note that language modules might want to override this to allow * for special responses. For example, in English, most verbs will * want to override this with a specialized production that allows * the appropriate preposition in the response. */ askIobjResponseProd = singleNoun /* get the missing object response production for a given role */ getObjResponseProd(resolver) { /* if it's the indirect object, return the iobj response prod */ if (resolver.whichObject == IndirectObject) return askIobjResponseProd; /* otherwise use the default handling */ return inherited(resolver); } /* * Resolution order - returns DirectObject or IndirectObject to * indicate which noun phrase to resolve first in resolveNouns(). * By default, we'll resolve the indirect object first, but * individual actions can override this to resolve in a non-default * order. */ resolveFirst = IndirectObject /* * Empty phrase resolution order. This is similar to the standard * resolution order (resolveFirst), but is used only when both the * direct and indirect objects are empty phrases. * * When both phrases are empty, we will either use a default or * prompt interactively for the missing phrase. In most cases, it * is desirable to prompt interactively for a missing direct object * first, regardless of the usual resolution order. */ resolveFirstEmpty = DirectObject /* * Determine which object to call first for action processing. By * default, we execute in the same order as we resolve, but this can * be overridden if necessary. */ execFirst = (resolveFirst) /* * Can the indirect object potentially resolve to the given * simulation object? This only determines if the object is a * *syntactic* match, meaning that it can match at a vocabulary and * grammar level. This doesn't test it for logicalness or check that * it's an otherwise valid resolution. */ canIobjResolveTo(obj) { /* check our iobj match tree to see if it can resolve to 'obj' */ return iobjMatch.canResolveTo( obj, self, issuer_, actor_, IndirectObject); } /* * resolve our noun phrases to objects */ resolveNouns(issuingActor, targetActor, results) { local first; local objMatch1, objMatch2; local objList1, objList2; local getResolver1, getResolver2; local objCur1; local remapProp; local reResolveFirst; /* * note in the results that we have two noun slots (the direct * and indirect objects) */ results.noteNounSlots(2); /* we have no current known direct or indirect object yet */ dobjCur_ = nil; iobjCur_ = nil; /* * presume we won't have to re-resolve the first noun phrase, * and clear out our record of anaphor bindings */ reResolveFirst = nil; needAnaphoricBinding_ = nil; lastObjList_ = nil; /* * Determine which object we want to resolve first. If both * phrases are empty, use the special all-empty ordering; * otherwise, use the standard ordering for this verb. */ if (dobjMatch.isEmptyPhrase && iobjMatch.isEmptyPhrase) { /* both phrases are empty - use the all-empty ordering */ first = resolveFirstEmpty; } else { /* * we have at least one non-empty phrase, so use our * standard ordering */ first = resolveFirst; } /* * The resolution process is symmetrical for the two possible * resolution orders (direct object first or indirect object * first); all we need to do is to set up the parameters we'll * need according to which order we're using. * * This parameterized approach makes the code further below a * little mind-boggling, because it's using so much indirection. * But the alternative is worse: the alternative is to * essentially make two copies of the code below (one for the * dobj-first case and one for the iobj-first case), which is * prone to maintenance problems because of the need to keep the * two copies synchronized when making any future changes. */ if (first == DirectObject) { objMatch1 = dobjMatch; objMatch2 = iobjMatch; objList1 = &dobjList_; objList2 = &iobjList_; getResolver1 = &getDobjResolver; getResolver2 = &getIobjResolver; objCur1 = &dobjCur_; remapProp = remapDobjProp; } else { objMatch1 = iobjMatch; objMatch2 = dobjMatch; objList1 = &iobjList_; objList2 = &dobjList_; getResolver1 = &getIobjResolver; getResolver2 = &getDobjResolver; objCur1 = &iobjCur_; remapProp = remapIobjProp; } /* * Get the unfiltered second-resolved object list - this will * give the first-resolved object resolver access to some * minimal information about the possible second-resolved object * or objects. * * Note that we're not *really* resolving the second object here * - it is the second-resolved object, after all. What we're * doing is figuring out the *potential* set of objects that * could resolve to the second phrase, with minimal * disambiguation, so that the first object resolver is not * totally in the dark about the second object's potential * resolution. */ initTentative(issuingActor, targetActor, first); /* resolve the first-resolved noun phrase */ self.(objList1) = objMatch1.resolveNouns( self.(getResolver1)(issuingActor, targetActor, true), results); /* * If the first-resolved noun phrase asked for an anaphoric * binding, we'll need to go back and re-resolve that noun * phrase after we resolve our other noun phrase, since the * first-resolved phrase refers to the second-resolved phrase. */ reResolveFirst = needAnaphoricBinding_; /* * if the second-resolved phrase uses an anaphoric pronoun, the * anaphor can be taken as referring to the first-resolved * phrase's results */ lastObjList_ = self.(objList1); /* * if the first-resolved phrase resolves to just one object, we * can immediately set the current resolved object, so that we * can use it while resolving the second-resolved object list */ if (self.(objList1).length() == 1) { /* set the current first-resolved object */ self.(objCur1) = self.(objList1)[1].obj_; /* if remapping is allowed at this point, look for a remapping */ if (results.allowActionRemapping) { withParserGlobals(issuingActor, targetActor, self, function() { local remapInfo; /* check for a remapping */ if ((remapInfo = self.(objCur1).(remapProp)) != nil) { /* * we have a remapping, so apply it - note that * we're still in the process of resolving noun * phrases (since we've only resolved one of our * two noun phrases so far), so pass 'true' for * the inResolve parameter */ remapAction(true, first, remapInfo); } }); } } /* resolve the second-resolved object */ self.(objList2) = objMatch2.resolveNouns( self.(getResolver2)(issuingActor, targetActor, true), results); /* * if we have to re-resolve the first noun phrase due to an * anaphoric pronoun, go back and do that now */ if (reResolveFirst) { /* * use the second-resolved noun phrase as the referent of * the anaphoric pronoun(s) in the first-resolved phrase */ lastObjList_ = self.(objList2); /* re-resolve the first object list */ self.(objList1) = objMatch1.resolveNouns( self.(getResolver1)(issuingActor, targetActor, true), results); } } /* we have a direct and indirect object */ predicateNounPhrases = [&dobjMatch, &iobjMatch] /* get an object role */ getRoleFromIndex(idx) { /* * the second object is always our indirect object; for other * roles, defer to the inherited behavior */ return (idx == 2 ? IndirectObject : inherited(idx)); } /* get the resolved object in a given role */ getObjectForRole(role) { /* return the indirect object if requested; otherwise inherit */ return (role == IndirectObject ? getIobj() : inherited(role)); } /* get the ResolveInfo for the given resolved object */ getResolveInfo(obj, oldRole) { local info; /* scan our resolved direct object list for the given object */ if (dobjList_ != nil) info = dobjList_.valWhich({x: x.obj_ == obj}); /* if we didn't find it there, try the indirect object list */ if (info == nil && iobjList_ != nil) info = iobjList_.valWhich({x: x.obj_ == obj}); /* if we didn't find one, create one from scratch */ if (info == nil) { /* get the list for the old role */ local lst = (oldRole == DirectObject ? dobjList_ : iobjList_); /* * if there's anything in the old role, copy its flags and * noun phrase to the new role */ if (lst.length() > 0) { /* copy the old role's attributes */ info = new ResolveInfo(obj, lst[1].flags_, lst[1].np_); } else { /* nothing in the old role - start from scratch */ info = new ResolveInfo(obj, 0, nil); } } /* return what we found (or created) */ return info; } /* get the OtherObject role for the given role */ getOtherObjectRole(role) { /* the complementary roles are DirectObject and IndirectObject */ return (role == IndirectObject ? DirectObject : IndirectObject); } /* get the match tree for the noun phrase in the given role */ getMatchForRole(role) { /* return the indirect object match tree if requested; else inherit */ return (role == IndirectObject ? iobjMatch : inherited(role)); } /* get the 'verify' property for a given object role */ getVerifyPropForRole(role) { return (role == IndirectObject ? verIobjProp : inherited(role)); } /* get the 'preCond' property for a given object role */ getPreCondPropForRole(role) { return (role == IndirectObject ? preCondIobjProp : inherited(role)); } /* get the 'remap' property for a given object role */ getRemapPropForRole(role) { return (role == IndirectObject ? remapIobjProp : inherited(role)); } /* get the list of resolved objects in the given role */ getResolvedObjList(which) { return (which == IndirectObject ? getResolvedIobjList() : inherited(which)); } /* get the list of resolved indirect objects */ getResolvedIobjList() { /* * if we have an indirect object list, return the objects from * it; otherwise, return an empty list */ return (iobjList_ == nil ? nil : iobjList_.mapAll({x: x.obj_})); } /* * Manually set the resolved objects. We'll set our direct and * indirect objects. */ setResolvedObjects(dobj, iobj) { /* inherit the base class handling to set the direct object */ inherited(dobj); /* set the resolved iobj */ setResolvedIobj(iobj); } /* set a resolved iobj */ setResolvedIobj(iobj) { /* build a pre-resolved production for the indirect object */ iobjMatch = new PreResolvedProd(iobj); /* set the resolved indirect object */ iobjList_ = makeResolveInfoList(iobj); /* set the current indirect object as well */ iobjCur_ = (iobjList_.length() > 0 ? iobjList_[1].obj_ : nil); iobjInfoCur_ = (iobjList_.length() > 0 ? iobjList_[1] : nil); } /* manually set the unresolved object noun phrase match trees */ setObjectMatches(dobj, iobj) { /* inherit default handling */ inherited(dobj); /* * if the iobj is a ResolveInfo, set it as a resolved object; * otherwise set it as an unresolved match tree */ if (iobj.ofKind(ResolveInfo)) setResolvedIobj(iobj); else iobjMatch = iobj; } /* * Get the anaphoric binding for the noun phrase we're currently * resolving. */ getAnaphoricBinding(typ) { /* if we've resolved a prior noun phrase, return it */ if (lastObjList_ != nil) return lastObjList_; /* * we don't have a prior noun phrase - make a note that we have * to come back and re-resolve the current noun phrase */ needAnaphoricBinding_ = true; /* * return an empty list to indicate that the anaphor is * acceptable in this context but has no binding yet */ return []; } /* * The last object list we resolved. We keep track of this so that * we can provide it as the anaphoric binding, if an anaphor binding * is requested. */ lastObjList_ = nil /* * Flag: we have been asked for an anaphoric binding, but we don't * have a binding available. We'll check this after resolving the * first-resolved noun phrase so that we can go back and re-resolve * it again after resolving the other noun phrase. */ needAnaphoricBinding_ = nil /* check that the resolved objects are in scope */ resolvedObjectsInScope() { /* * check the indirect object, and inherit the base class handling * to check the direct object */ return (inherited() && (getIobjResolver(gIssuingActor, gActor, true) .objInScope(iobjList_[1].obj_))); } /* * get our indirect object resolver, or create one if we haven't * already cached one */ getIobjResolver(issuingActor, targetActor, reset) { /* if we don't already have one cached, create a new one */ if (iobjResolver_ == nil) iobjResolver_ = createIobjResolver(issuingActor, targetActor); /* reset the resolver if desired */ if (reset) iobjResolver_.resetResolver(); /* return the cached resolver */ return iobjResolver_; } /* * Create our indirect object resolver. By default, we'll use a * basic indirect object resolver. */ createIobjResolver(issuingActor, targetActor) { /* create and return a new basic indirect object resolver */ return new IobjResolver(self, issuingActor, targetActor); } /* * Resolve 'all' for the indirect object. By default, we'll return * everything in the scope list. */ getAllIobj(actor, scopeList) { return scopeList; } /* filter an ambiguous indirect object noun phrase */ filterAmbiguousIobj(lst, requiredNum, np) { /* filter using the indirect object verifier method */ return filterAmbiguousWithVerify(lst, requiredNum, verIobjProp, preCondIobjProp, remapIobjProp, IndirectObject, np); } /* filter a plural phrase */ filterPluralIobj(lst, np) { /* filter using the direct object verifier method */ return filterPluralWithVerify(lst, verIobjProp, preCondIobjProp, remapIobjProp, IndirectObject, np); } /* get the default indirect object */ getDefaultIobj(np, resolver) { /* get a default indirect object using the verify method */ return getDefaultWithVerify(resolver, verIobjProp, preCondIobjProp, remapIobjProp, IndirectObject, np); } /* * Execute the action. We'll run through the execution sequence * once for each resolved object in our direct or indirect object * list, depending on which one is the list and which one is the * singleton. */ doActionMain() { local lst; local preAnnouncedDobj; local preAnnouncedIobj; /* * Get the list of resolved objects for the multiple object. If * neither has multiple objects, it doesn't matter which is * iterated, since we'll just do the command once anyway. */ lst = (iobjList_.length() > 1 ? iobjList_ : dobjList_); /* * Set the pronoun antecedents, using the game-specific pronoun * setter. Don't set an antecedent for a nested command. */ if (parentAction == nil) { /* * Set both direct and indirect objects as potential * antecedents. Rather than trying to figure out right now * which one we might want to refer to in the future, remember * both - we'll decide which one is the logical antecedent * when we find a pronoun to resolve in a future command. */ gActor.setPronounMulti(dobjList_, iobjList_); /* * If one or the other object phrase was specified in the * input as a pronoun, keep the meaning of that pronoun the * same, overriding whatever we just did. Note that the * order we use here doesn't matter: if a given pronoun * appears in only one of the two lists, then the list where * it's not set has no effect on the pronoun, hence it * doesn't matter which comes first; if a pronoun appears in * both lists, it will have the same value in both lists, so * we'll just do the same thing twice, so, again, order * doesn't matter. */ setPronounByInput(dobjList_); setPronounByInput(iobjList_); } /* * pre-announce the non-list object if appropriate - this will * provide a common pre-announcement if we iterate through * several announcements of the main list objects */ if (lst == dobjList_) { /* pre-announce the single indirect object if needed */ preAnnouncedIobj = preAnnounceActionObject( iobjList_[1], dobjList_, IndirectObject); /* we haven't announced the direct object yet */ preAnnouncedDobj = nil; /* pre-calculate the multi-object announcements */ cacheMultiObjectAnnouncements(dobjList_, DirectObject); } else { /* pre-announce the single direct object if needed */ preAnnouncedDobj = preAnnounceActionObject( dobjList_[1], iobjList_, DirectObject); /* we haven't announced the indirect object yet */ preAnnouncedIobj = nil; /* pre-calculate the multi-object announcements */ cacheMultiObjectAnnouncements(iobjList_, IndirectObject); } /* we haven't yet canceled the iteration */ iterationCanceled = nil; /* iterate over the resolved list for the multiple object */ for (local i = 1, local len = lst.length() ; i <= len && !iterationCanceled ; ++i) { local dobjInfo; local iobjInfo; /* * make the current list item the direct or indirect object, * as appropriate */ if (lst == dobjList_) { /* the direct object is the multiple object */ dobjInfo = dobjInfoCur_ = lst[i]; iobjInfo = iobjInfoCur_ = iobjList_[1]; } else { /* the indirect object is the multiple object */ dobjInfo = dobjInfoCur_ = dobjList_[1]; iobjInfo = iobjInfoCur_ = lst[i]; } /* get the current dobj and iobj from the resolve info */ dobjCur_ = dobjInfo.obj_; iobjCur_ = iobjInfo.obj_; /* * if the action was remapped, and we need to announce * anything, announce the entire action */ if (isRemapped()) { /* * We were remapped. The entire phrasing of the new * action might have changed from what the player typed, * so it might be nonsensical to show the objects as we * usually would, as sentence fragments that are meant * to combine with what the player actually typed. So, * instead of showing the usual sentence fragments, show * the entire phrasing of the command. * * Only show the announcement if we have a reason to: we * have unclear disambiguation in one of the objects, or * one of the objects is defaulted. * * If we don't want to announce the remapped action, * still consider showing a multi-object announcement, * if we would normally need to do so. */ if (needRemappedAnnouncement(dobjInfo) || needRemappedAnnouncement(iobjInfo)) { /* show the remapped announcement */ gTranscript.announceRemappedAction(); } else { /* announce the multiple dobj if necessary */ if (!preAnnouncedDobj) maybeAnnounceMultiObject( dobjInfo, dobjList_.length(), DirectObject); /* announce the multiple iobj if necessary */ if (!preAnnouncedIobj) maybeAnnounceMultiObject( iobjInfo, iobjList_.length(), IndirectObject); } } else { /* announce the direct object if appropriate */ if (!preAnnouncedDobj) announceActionObject(dobjInfo, dobjList_.length(), DirectObject); /* announce the indirect object if appropriate */ if (!preAnnouncedIobj) announceActionObject(iobjInfo, iobjList_.length(), IndirectObject); } /* run the execution sequence for the current direct object */ doActionOnce(); /* if we're top-level, count the iteration in the transcript */ if (parentAction == nil) gTranscript.newIter(); } } /* * Set the pronoun according to the pronoun type actually used in * the input. For example, if we said PUT BOX ON IT, we want IT to * continue referring to whatever IT referred to before this command * - we specifically do NOT want IT to refer to the BOX in this * case. */ setPronounByInput(lst) { local objs; /* get the subset of the list that was specified by pronoun */ lst = lst.subset({x: x.pronounType_ != nil}); /* * Get a list of the unique objects in the list. This will * ensure that we can distinguish THEM from IT AND IT AND IT: if * we have the same pronoun appearing with multiple objects, * we'll know that it's because the pronoun was actually plural * (THEM) rather than a singular pronoun that was repeated (IT * AND IT AND IT). */ objs = lst.mapAll({x: x.obj_}).getUnique(); /* * Now retain one 'lst' element for each 'objs' element. This * is a bit tricky: we're mapping each element in 'objs' to pick * out one element of 'lst' where the 'lst' element points to * the 'objs' element. */ lst = objs.mapAll({o: lst.valWhich({l: l.obj_ == o})}); /* * Now we can set the pronouns. Go through the list, and set * each different pronoun type that appears. Set it to the * subset of the list with that matches that pronoun type, and * then remove that subset and keep iterating to pick up the * remaining pronoun types. */ while (lst.length() != 0) { local cur; local typ; /* on this iteration, handle the first pronoun type in the list */ typ = lst[1].pronounType_; /* pick out the subset with the current pronoun type */ cur = lst.subset({x: x.pronounType_ == typ}); /* set the current pronoun to the current list */ gActor.setPronounByType(typ, cur); /* remove the subset we just handled from the remaining list */ lst -= cur; } } /* * Determine if we need to announce this action when the action was * remapped, based on the resolution information for one of our * objects. We need to announce a remapped action when either * object had unclear disambiguation or was defaulted. */ needRemappedAnnouncement(info) { /* * if it's a defaulted object that hasn't been announced, or it * was unclearly disambiguated, we need an announcement */ return (((info.flags_ & DefaultObject) != 0 && (info.flags_ & AnnouncedDefaultObject) == 0) || (info.flags_ & UnclearDisambig) != 0); } /* * Verify the action. */ verifyAction() { local result; /* invoke any general (non-object) pre-condition verifiers */ result = callVerifyPreCond(nil); /* check the direct object */ result = callVerifyProp(dobjCur_, verDobjProp, preCondDobjProp, remapDobjProp, result, DirectObject); /* * Check the indirect object, combining the results with the * direct object results. We combine the results for the two * objects because we're simply looking for any reason that we * can't perform the command. */ result = callVerifyProp(iobjCur_, verIobjProp, preCondIobjProp, remapIobjProp, result, IndirectObject); /* * check that the action method ("doXxx") is defined on at least * one of the objects */ return verifyHandlersExist( [dobjCur_, iobjCur_], [verDobjProp, checkDobjProp, actionDobjProp, verIobjProp, checkIobjProp, actionIobjProp], result); } /* initialize tentative resolutions for other noun phrases */ initTentative(issuingActor, targetActor, whichObj) { local tRes; local ti, td; /* * remember the old tentative direct and indirect objects, then * set them to empty lists - this will ensure that we don't * recursively try to get a tentative resolution for the object * we're working on right now, which would cause infinite * recursion */ td = tentativeDobj_; ti = tentativeIobj_; /* set them to empty lists to indicate they don't need resolving */ tentativeDobj_ = []; tentativeIobj_ = []; /* make sure our built-in dobj resolver is initialized */ issuer_ = issuingActor; actor_ = targetActor; /* make sure we set things back when we're done */ try { /* initialize the other noun phrase */ if (whichObj == DirectObject && ti == nil) { /* tentatively resolve the indirect object */ tRes = new TentativeResolveResults(targetActor, issuingActor); ti = iobjMatch.resolveNouns( getIobjResolver(issuingActor, targetActor, true), tRes); /* * if the list is empty, and we didn't have a missing * noun phrase, use a dummy object as the tentative * resolution - this distinguishes erroneous noun phrases * from those that are simply missing */ if (ti == [] && !tRes.npMissing) ti = [dummyTentativeInfo]; } else if (whichObj == IndirectObject && td == nil) { /* tentatively resolve the direct object */ tRes = new TentativeResolveResults(targetActor, issuingActor); td = dobjMatch.resolveNouns( getDobjResolver(issuingActor, targetActor, true), tRes); /* use a dummy object if appropriate */ if (td == [] && !tRes.npMissing) td = [dummyTentativeInfo]; } } finally { /* set the original (or updated) tentative lists */ tentativeDobj_ = td; tentativeIobj_ = ti; } } /* * Check for remapping */ checkRemapping() { local remapInfo; local role; /* presume we'll find remapping for the first-resolved object */ role = resolveFirst; /* check remapping for each object, in the resolution order */ if (resolveFirst == DirectObject) { /* the direct object is resolved first, so try it first */ if ((remapInfo = dobjCur_.(remapDobjProp)) == nil) { remapInfo = iobjCur_.(remapIobjProp); role = IndirectObject; } } else { /* the indirect object is resolved first, so remap it first */ if ((remapInfo = iobjCur_.(remapIobjProp)) == nil) { remapInfo = dobjCur_.(remapDobjProp); role = DirectObject; } } /* if we found a remapping, apply it */ if (remapInfo != nil) remapAction(nil, role, remapInfo); } /* * Check the command. * * For a two-object action, this first calls the catch-all 'check' * methods (the dobjFor(Default) and dobjFor(All) methods) on the two * objects (indirect object first), then calls the 'check' methods * for this specific action (direct object first). */ checkAction() { try { /* invoke the catch-all 'check' methods on each object */ local defIo = callCatchAllProp( iobjCur_, checkIobjProp, &checkIobjDefault, &checkIobjAll); local defDo = callCatchAllProp( dobjCur_, checkDobjProp, &checkDobjDefault, &checkDobjAll); /* * invoke the 'check' method on each object, as long as it * overrides any corresponding Default 'check' handler */ if (!defDo) dobjCur_.(checkDobjProp)(); if (!defIo) iobjCur_.(checkIobjProp)(); } catch (ExitSignal es) { /* mark the action as a failure in the transcript */ gTranscript.noteFailure(); /* re-throw the signal */ throw es; } } /* * Execute the command. */ execAction() { local defIo, defDo; /* invoke the catch-all 'action' method on each object */ defIo = callCatchAllProp(iobjCur_, actionIobjProp, &actionIobjDefault, &actionIobjAll); defDo = callCatchAllProp(dobjCur_, actionDobjProp, &actionDobjDefault, &actionDobjAll); /* * Invoke the action method on each object, starting with the * non-list object. Call these only if the corresponding * default 'action' methods didn't override the verb-specific * methods. */ if (execFirst == DirectObject) { if (!defDo) dobjCur_.(actionDobjProp)(); if (!defIo) iobjCur_.(actionIobjProp)(); } else { if (!defIo) iobjCur_.(actionIobjProp)(); if (!defDo) dobjCur_.(actionDobjProp)(); } } /* get the precondition descriptor list for the action */ getPreCondDescList() { /* * return the inherited preconditions plus the conditions that * apply to the indirect object */ return inherited() + getObjPreCondDescList(iobjCur_, preCondIobjProp, iobjCur_, IndirectObject); } /* * Get a message parameter object for the action. We define 'dobj' * as the direct object and 'iobj' as the indirect object, in * addition to any inherited targets. */ getMessageParam(objName) { switch(objName) { case 'iobj': /* return the current indirect object */ return iobjCur_; default: /* inherit default handling */ return inherited(objName); } } /* get the current indirect object being executed */ getIobj() { return iobjCur_; } /* get the full ResolveInfo associated with the current indirect object */ getIobjInfo() { return iobjInfoCur_; } /* get the object resolution flags for the indirect object */ getIobjFlags() { return iobjInfoCur_ != nil ? iobjInfoCur_.flags_ : 0; } /* get the number of direct objects */ getIobjCount() { return iobjList_ != nil ? iobjList_.length() : 0; } /* get the original token list of the current indirect object phrase */ getIobjTokens() { return iobjInfoCur_ != nil && iobjInfoCur_.np_ != nil ? iobjInfoCur_.np_.getOrigTokenList() : nil; } /* get the original words (as a list of strings) of the current iobj */ getIobjWords() { local l = getIobjTokens(); return l != nil ? l.mapAll({x: getTokOrig(x)}) : nil; } /* * Get the list of active objects. We have a direct and indirect * object. */ getCurrentObjects() { return [dobjCur_, iobjCur_]; } /* set the current objects */ setCurrentObjects(lst) { /* remember the current direct and indirect objects */ dobjCur_ = lst[1]; iobjCur_ = lst[2]; /* we don't have ResolveInfo objects for this call */ dobjInfoCur_ = iobjInfoCur_ = nil; } /* * Copy one tentative object list to the other. This is useful when * an object's verifier for one TIAction wants to forward the call * to the other object verifier for a different TIAction. */ copyTentativeObjs() { /* copy whichever tentative list is populated to the other slot */ if (tentativeDobj_ != nil) tentativeDobj_ = tentativeIobj_; else tentativeIobj_ = tentativeDobj_; } /* * Get the tentative direct/indirect object resolution lists. A * tentative list is available for the later-resolved object while * resolving the earlier-resolved object. */ getTentativeDobj() { return tentativeDobj_; } getTentativeIobj() { return tentativeIobj_; } /* * the predicate grammar must assign the indirect object production * tree to iobjMatch */ iobjMatch = nil /* the indirect object list */ iobjList_ = [] /* current indirect object being executed */ iobjCur_ = nil /* the full ResolveInfo associated with iobjCur_ */ iobjInfoCur_ = nil /* my cached indirect object resolver */ iobjResolver_ = nil /* * The tentative direct and indirect object lists. A tentative list * is available for the later-resolved object while resolving the * earlier-resolved object. */ tentativeDobj_ = nil tentativeIobj_ = nil /* * Verification and action properties for the indirect object. By * convention, the verification method for the indirect object of a * two-object action is verIobjXxx; the check method is * checkIobjXxx; and the action method is actionIobjXxx. */ verIobjProp = nil preCondIobjProp = nil checkIobjProp = nil actionIobjProp = nil /* * Action-remap properties for the indirect object. By convention, * the remapper properties are named remapDobjAction and * remapIobjAction, for the direct and indirect objects, * respectively, where Action is replaced by the root name of the * action. */ remapIobjProp = nil ; /* ------------------------------------------------------------------------ */ /* * Common base class for actions involving literal phrases. This is a * mix-in class that can be combined with Action subclasses to create * specific kinds of literal actions. */ class LiteralActionBase: object /* * Get a message parameter. We define 'literal' as the text of the * literal phrase, in addition to inherited targets. */ getMessageParam(objName) { switch(objName) { case 'literal': /* return the text of the literal phrase */ return text_; default: /* inherit default handling */ return inherited(objName); } } /* manually set the resolved objects */ setResolvedObjects(txt) { /* remember the literal text */ text_ = txt; } /* manually set the pre-resolved match trees */ setObjectMatches(lit) { /* if it's not already a PreResolvedLiteralProd, wrap it */ if (!lit.ofKind(PreResolvedLiteralProd)) { /* save the literal text */ text_ = lit; /* wrap it in a match tree */ lit = new PreResolvedLiteralProd(lit); } /* note the new literal match tree */ literalMatch = lit; } /* get the current literal text */ getLiteral() { return text_; } /* the text of the literal phrase */ text_ = nil ; /* ------------------------------------------------------------------------ */ /* * An action with a literal phrase as its only object, such as "say ". We'll accept anything as the literal phrase - a number, a * quoted string, or arbitrary words - and treat them all simply as text. * * The grammar rules that produce these actions must set literalMatch to * the literal phrase's match tree. * * Because we don't have any actual resolved objects, we're based on * IAction. Subclasses that implement particular literal actions should * override execAction() to carry out the action; this method can call * the getLiteral() method of self to get a string giving the literal * text. */ class LiteralAction: LiteralActionBase, IAction /* * Resolve objects. We don't actually have any objects to resolve, * but we do have to get the text for the literal phrase. */ resolveNouns(issuingActor, targetActor, results) { /* the literal phrase counts as one noun slot */ results.noteNounSlots(1); /* * "Resolve" our literal phrase. The literal phrase doesn't * resolve to an object list the way a regular noun phrase would, * but rather just resolves to a text string giving the original * literal contents of the phrase. */ literalMatch.resolveLiteral(results); /* retrieve the text of the phrase, exactly as the player typed it */ text_ = literalMatch.getLiteralText(results, self, DirectObject); } /* we have a literal phrase as our only noun phrase */ predicateNounPhrases = [&literalMatch] ; /* ------------------------------------------------------------------------ */ /* * An action with a direct object and a literal, such as "turn dial to * " or "type on keypad". We'll accept anything as the * literal phrase - a number, a quoted string, or arbitrary words - and * treat them all simply as text. * * The grammar rules that produce these actions must set dobjMatch to the * resolvable object of the command, and must set literalMatch to the * literal phrase's match tree. Note that we use dobjMatch as the * resolvable object even if the object serves grammatically as the * indirect object - this is a simplification, and the true grammatical * purpose of the object isn't important since there's only one true * object in the command. * * When referring to objects by role (such as in remapTo), callers should * ALWAYS refer to the resolvable object as DirectObject, and the literal * phrase as IndirectObject. * * Each subclass must set the property whichMessageLiteral to the * grammatical role (DirectObject, IndirectObject) the literal phrase * plays for message generation purposes. This only affects messages; it * doesn't affect anything else; in particular, regardless of the * whichMessageLiteral setting, callers should always refer to the * literal as IndirectObject when calling getObjectForRole() and the * like, and should always call getDobj() to get the resolved version of * the resolvable object phrase. */ class LiteralTAction: LiteralActionBase, TAction /* * Resolve objects. */ resolveNouns(issuingActor, targetActor, results) { /* * the literal phrase counts as one noun slot, and the direct * object as another */ results.noteNounSlots(2); /* * If the literal phrase serves as the direct object in generated * messages, ask for its literal text first, so that we ask for * it interactively first. Otherwise, get the tentative literal * text, in case it's useful to have in resolving the other * object. */ if (whichMessageLiteral == DirectObject) text_ = literalMatch.getLiteralText(results, self, DirectObject); else text_ = literalMatch.getTentativeLiteralText(); /* resolve the direct object */ dobjList_ = dobjMatch.resolveNouns( getDobjResolver(issuingActor, targetActor, true), results); /* * "Resolve" the literal, ignoring the result - we call this so * that the literal can do any scoring it wants to do during * resolution; but since we're treating it literally, it * obviously has no actual resolved value. */ literalMatch.resolveLiteral(results); /* the literal phrase resolves to the text only */ text_ = literalMatch.getLiteralText(results, self, whichMessageLiteral); } /* we have a direct object and a literal phrase */ predicateNounPhrases = [&dobjMatch, &literalMatch] /* get an object role */ getRoleFromIndex(idx) { /* * index 2 is the literal object, which is always in the indirect * object role; for others, inherit the default handling */ return (idx == 2 ? IndirectObject : inherited(idx)); } /* get the OtherObject role for the given role */ getOtherObjectRole(role) { /* the complementary roles are DirectObject and IndirectObject */ return (role == DirectObject ? IndirectObject : DirectObject); } /* get the resolved object in a given role */ getObjectForRole(role) { /* * the literal is in the IndirectObject role; inherit the default * for others */ return (role == IndirectObject ? text_ : inherited(role)); } /* get the match tree for the given role */ getMatchForRole(role) { /* * the literal is in the IndirectObject role; inherit the default * for anything else */ return (role == IndirectObject ? literalMatch : inherited(role)); } /* manually set the resolved objects */ setResolvedObjects(dobj, txt) { /* inherit default TAction handling for the direct object */ inherited TAction(dobj); /* inherit the LiteralActionBase handling for the literal text */ inherited LiteralActionBase(txt); } /* manually set the pre-resolved match trees */ setObjectMatches(dobj, lit) { /* inherit default TAction handling for the direct object */ inherited TAction(dobj); /* inherit the default LiteralActionBase handling for the literal */ inherited LiteralActionBase(lit); } /* * Get a list of the current objects. We include only the direct * object here, since the literal text is not a resolved object but * simply literal text. */ getCurrentObjects() { return [dobjCur_]; } /* set the current objects */ setCurrentObjects(lst) { dobjCur_ = lst[1]; dobjInfoCur_ = nil; } /* * Retry a single-object action as an action taking both an object * and a literal phrase. We'll treat the original action's direct * object list as our direct object list, and obtain a literal * phrase interactively. * * This routine terminates with 'exit' if it doesn't throw some * other error. */ retryWithMissingLiteral(orig) { local action; /* create the new action based on the original action */ action = createForRetry(orig); /* use an empty literal phrase for the new action */ action.literalMatch = new EmptyLiteralPhraseProd(); /* initialize for the missing literal phrase */ action.initForMissingLiteral(orig); /* resolve and execute the replacement action */ resolveAndReplaceAction(action); } /* initialize with a missing direct object phrase */ initForMissingDobj(orig) { /* * Since we have a missing direct objet, we must be coming from * a LiteralAction. The LiteralAction will already have a * literal phrase, so copy it. For example: WRITE HELLO -> * WRITE HELLO ON . */ literalMatch = new PreResolvedLiteralProd(orig.getLiteral()); } /* initialize for a missing literal phrase */ initForMissingLiteral(orig) { local origDobj = orig.getDobj(); /* * Since we have a missing literal, we must be coming from a * TAction. The TAction will already have a direct object, * which we'll want to keep as our own direct object. For * example: WRITE ON SLATE -> WRITE ON SLATE. */ dobjMatch = new PreResolvedProd(origDobj != nil ? origDobj : orig.dobjList_); } /* object role played by the literal phrase */ whichMessageLiteral = nil /* -------------------------------------------------------------------- */ /* * Direct Object Resolver implementation. We serve as our own * direct object resolver, so we define any special resolver * behavior here. */ /* * the true grammatical role of the resolved object is always the * direct object */ whichObject = DirectObject /* * What we call our direct object might actually be playing the * grammatical role of the indirect object - in order to inherit * easily from TAction, we call our resolved object our direct * object, regardless of which grammatical role it actually plays. * For the most part it doesn't matter which is which; but for the * purposes of our resolver, we actually do care about its real role. * So, override the resolver method whichMessageObject so that it * returns whichever role is NOT served by the topic object. */ whichMessageObject = (whichMessageLiteral == DirectObject ? IndirectObject : DirectObject) ; /* ------------------------------------------------------------------------ */ /* * Base class for actions that include a "topic" phrase. This is a * mix-in class that can be used in different types of topic actions. In * all cases, the topic phrase must be assigned to the 'topicMatch' * property in grammar rules based on this class. */ class TopicActionBase: object /* * Resolve the topic phrase. This resolves the match tree in out * 'topicMatch' property, and stores the result in topicList_. This * is for use in resolveNouns(). */ resolveTopic(issuingActor, targetActor, results) { /* get the topic resolver */ local resolver = getTopicResolver(issuingActor, targetActor, true); /* resolve the topic match tree */ topicList_ = topicMatch.resolveNouns(resolver, results); /* make sure it's properly packaged as a ResolvedTopic */ topicList_ = resolver.packageTopicList(topicList_, topicMatch); } /* * Set the resolved topic to the given object list. This is for use * in setResolvedObjects(). */ setResolvedTopic(topic) { /* if the topic isn't given as a ResolvedTopic, wrap it in one */ if (!topic.ofKind(ResolvedTopic)) topic = ResolvedTopic.wrapObject(topic); /* use the match object from the ResolvedTopic */ topicMatch = topic.topicProd; /* finally, make a ResolveInfo list out of the ResolvedTopic */ topicList_ = makeResolveInfoList(topic); } /* * Set the topic match tree. This is for use in setObjectMatches(). */ setTopicMatch(topic) { /* * If it's given as a ResolveInfo, wrap it in a PreResolvedProd. * Otherwise, if it's not a topic match (a TopicProd object), and * it has tokens, re-parse it as a topic match to make sure we * get the grammar optimized for global scope. */ if (topic.ofKind(ResolveInfo)) { /* it's a resolved object, so wrap it in a fake match tree */ topic = new PreResolvedProd(topic); } else if (!topic.ofKind(TopicProd)) { /* * Re-parse it as a topic phrase. If our dobj resolver knows * the actors, use those; otherwise default to the player * character. */ local issuer = (issuer_ != nil ? issuer_ : gPlayerChar); local target = (actor_ != nil ? actor_ : gPlayerChar); topic = reparseMatchAsTopic(topic, issuer, target); } /* save the topic match */ topicMatch = topic; } /* * Re-parse a match tree as a topic phrase. Returns a TopicProd * match tree, if possible. */ reparseMatchAsTopic(topic, issuingActor, targetActor) { local toks; /* we can only proceed if the match tree has a token list */ if ((toks = topic.getOrigTokenList()) != nil && toks.length() > 0) { /* parse it as a topic phrase */ local match = topicPhrase.parseTokens(toks, cmdDict); /* * if we parsed it successfully, and we have more than one * match, get the best of the bunch */ if (match != nil && match.length() > 1) { /* set up a topic resolver for the match */ local resolver = getTopicResolver( issuingActor, targetActor, true); /* sort it and return the best match */ return CommandRanking.sortByRanking(match, resolver)[1].match; } else if (match != nil && match.length() > 0) { /* we found exactly one match, so use it */ return match[1]; } } /* * if we make it this far, we couldn't parse it as a topic, so * just return the original */ return topic; } /* * get the topic resolver */ getTopicResolver(issuingActor, targetActor, reset) { /* create one if we don't already have one */ if (topicResolver_ == nil) topicResolver_ = createTopicResolver(issuingActor, targetActor); /* reset the resolver if desired */ if (reset) topicResolver_.resetResolver(); /* return the cached resolver */ return topicResolver_; } /* * Create the topic resolver. */ createTopicResolver(issuingActor, targetActor) { return new TopicResolver( self, issuingActor, targetActor, topicMatch, whichMessageTopic, getTopicQualifierResolver(issuingActor, targetActor, nil)); } /* * Get a message parameter by name. We'll return the topic for the * keyword 'topic', and inherit the default handling for anything * else. */ getMessageParam(objName) { switch(objName) { case 'topic': /* return the topic */ return getTopic(); default: /* inherit default handling */ return inherited(objName); } } /* get the current topic */ getTopic() { /* * Because the topic list always contains one entry (a * ResolvedTopic object encapsulating the topic information), * our current topic is always simply the first and only element * of the topic list. */ return topicList_[1].obj_; } /* * Get the resolver for qualifiers we find in the topic phrase * (qualifiers that might need resolution include things like * possessive adjectives and locational phrases). */ getTopicQualifierResolver(issuingActor, targetActor, reset) { /* if we don't already have a qualifier resolver, create one */ if (topicQualResolver_ == nil) topicQualResolver_ = createTopicQualifierResolver( issuingActor, targetActor); /* reset it if desired */ if (reset) topicQualResolver_.resetResolver(); /* return it */ return topicQualResolver_; } /* the topic qualifier resolver */ topicQualResolver_ = nil /* the resolved topic object list */ topicList_ = nil /* my cached topic resolver */ topicResolver_ = nil ; /* ------------------------------------------------------------------------ */ /* * An action with a topic phrase as its only object, such as "think about * ". * * The grammar rules that produce these actions must set topicMatch to * the topic match tree. * * Because we don't have any actual resolved objects, we're based on * IAction. Subclasses that implement particular topic actions should * override execAction() to carry out the action; this method can call * the getTopic() method of self to get a string giving the topic. */ class TopicAction: TopicActionBase, IAction /* * Resolve objects. We don't actually have any normal objects to * resolve, but we do have to get the resolved topic phrase. */ resolveNouns(issuingActor, targetActor, results) { /* the topic phrase counts as one noun slot */ results.noteNounSlots(1); /* resolve the topic */ resolveTopic(issuingActor, targetActor, results); } /* manually set the resolved objects */ setResolvedObjects(topic) { /* set the resolved topic */ setResolvedTopic(topic); } /* manually set the pre-resolved match trees */ setObjectMatches(topic) { /* note the new topic match tree */ setTopicMatch(topic); } /* we have a topic noun phrase */ predicateNounPhrases = [&topicMatch] ; /* ------------------------------------------------------------------------ */ /* * An Action with a direct object and a topic, such as "ask about * ". Topics differ from ordinary noun phrases in scope: rather * than resolving to simulation objects based on location, we resolve * these based on the actor's knowledge. * * The grammar rules that produce these actions must set dobjMatch to the * resolvable object of the command (the in "ask about * "), and must set topicMatch to the topic match tree object, * which must be a TopicProd object. Note that, in some cases, the * phrasing might make the dobjMatch the indirect object, grammatically * speaking: "type on "; even in such cases, use * dobjMatch for the resolvable object. * * When we resolve the topic, we will always resolve it to a single * object of class ResolvedTopic. This contains the literal tokens of * the original command plus a list of simulation objects matching the * topic name, ordered from best to worst. This is different from the * way most commands work, since we do not resolve the topic to a simple * game world object. We keep all of this extra information because we * don't want to perform disambiguation in the normal fashion, but * instead resolve as much as we can with what we're given, and then give * the specialized action code as much information as we can to let the * action code figure out how to respond to the topic. */ class TopicTAction: TopicActionBase, TAction /* * reset the action */ resetAction() { /* reset the inherited state */ inherited(); /* forget our topic resolver */ topicResolver_ = nil; } /* * resolve our noun phrases to objects */ resolveNouns(issuingActor, targetActor, results) { local dobjRes; /* * the topic phrase counts as one noun slot, and the direct * object as another */ results.noteNounSlots(2); /* resolve the direct object, if we have one */ if (dobjMatch != nil) { /* presume we won't find an unbound anaphor */ needAnaphoricBinding_ = nil; /* * if the direct object phrase is an empty phrase, resolve * the topic first */ if (dobjMatch.isEmptyPhrase) resolveTopic(issuingActor, targetActor, results); /* get the direct object resolver */ dobjRes = getDobjResolver(issuingActor, targetActor, true); /* resolve the direct object */ dobjList_ = dobjMatch.resolveNouns(dobjRes, results); /* * if that turned up an anaphor (LOOK UP BOOK IN ITSELF), * resolve the topic phrase as though it were the direct * object */ if (needAnaphoricBinding_ && topicMatch != nil) dobjList_ = topicMatch.resolveNouns(dobjRes, results); } /* resolve the topic */ resolveTopic(issuingActor, targetActor, results); } /* * Filter the resolved topic. This is called by our * TActionTopicResolver, which refers the resolution back to us. */ filterTopic(lst, np, resolver) { /* by default, simply put everything in an undifferentiated list */ return new ResolvedTopic(lst, [], [], topicMatch); } /* * Retry a single-object action as an action taking both an object * and a topic phrase. We'll treat the original action's direct * object list as our direct object list, and we'll obtain a topic * phrase interactively. * * This routine terminates with 'exit' if it doesn't throw some other * error. */ retryWithMissingTopic(orig) { local action; /* create the new action based on the original action */ action = createForRetry(orig); /* use an empty topic phrase for the new action */ action.topicMatch = new EmptyTopicPhraseProd(); /* initialize for the missing topic */ action.initForMissingTopic(orig); /* resolve and execute the replacement action */ resolveAndReplaceAction(action); } /* initialize a new action we're retrying for a missing direct object */ initForMissingDobj(orig) { /* * The original action must have been a TopicAction, so we * already have a topic. Simply copy the original topic to use * as our own topic. For example: ASK ABOUT BOOK -> ASK * ABOUT BOOK. */ topicMatch = new PreResolvedProd(orig.getTopic()); } /* initialize for retrying with a missing topic phrase */ initForMissingTopic(orig) { local origDobj = orig.getDobj(); /* * The original action must have been a TAction, so we already * have a direct object. Simply copy the original direct object * for use as our own. For example: ASK BOB -> ASK BOB ABOUT * . */ dobjMatch = new PreResolvedProd(origDobj != nil ? origDobj : orig.dobjList_); } /* create our TAction topic resolver */ createTopicResolver(issuingActor, targetActor) { return new TActionTopicResolver( self, issuingActor, targetActor, topicMatch, whichMessageTopic, getTopicQualifierResolver(issuingActor, targetActor, nil)); } /* * In the topic phrase, we can use an anaphoric pronoun to refer * back to the direct object. Since we resolve the direct object * phrase first, we can simply return the direct object list as the * binding. If the direct object isn't resolved yet, make a note to * come back and re-bind the anaphor. */ getAnaphoricBinding(typ) { /* if we've already resolved the direct object phrase, return it */ if (dobjList_ not in (nil, [])) return dobjList_; /* no dobj yet - make a note that we need to re-bind the anaphor */ needAnaphoricBinding_ = true; /* * return an empty list for now to indicate that the anaphor is * acceptable but has no binding yet */ return []; } /* * Flag: we have been asked for an anaphoric binding, but we don't * have a binding available. We'll check this after resolving the * first-resolved noun phrase so that we can go back and re-resolve * it again after resolving the other noun phrase. */ needAnaphoricBinding_ = nil /* we have a direct object and a topic phrase */ predicateNounPhrases = [&dobjMatch, &topicMatch] /* get an object role */ getRoleFromIndex(idx) { /* * index 2 is the topic object, which is always in the indirect * object role; for others, inherit the default handling */ return (idx == 2 ? IndirectObject : inherited(idx)); } /* get the OtherObject role for the given role */ getOtherObjectRole(role) { /* the complementary roles are DirectObject and IndirectObject */ return (role == DirectObject ? IndirectObject : DirectObject); } /* get the resolved object in a given role */ getObjectForRole(role) { /* * the topic is in the IndirectObject role; inherit the default * for others */ return (role == IndirectObject ? getTopic() : inherited(role)); } /* get the match tree for the given role */ getMatchForRole(role) { /* * the topic is in the IndirectObject role; inherit the default * for anything else */ return (role == IndirectObject ? topicMatch : inherited(role)); } /* * Manually set the resolved objects. We'll set our direct and * indirect objects. */ setResolvedObjects(dobj, topic) { /* inherit default handling for the direct object */ inherited(dobj); /* set the resolved topic */ setResolvedTopic(topic); } /* manually set the pre-resolved match trees */ setObjectMatches(dobj, topic) { /* inherit default handling for the direct object */ inherited(dobj); /* note the new topic match tree */ setTopicMatch(topic); } /* * Get the list of active objects. We return only our direct * object, since our topic isn't actually a simulation object. */ getCurrentObjects() { return [dobjCur_]; } /* set the current objects */ setCurrentObjects(lst) { dobjCur_ = lst[1]; dobjInfoCur_ = nil; } /* the resolved topic object list */ topicList_ = nil /* my cached topic resolver */ topicResolver_ = nil /* grammatical role played by topic phrase in generated messages */ whichMessageTopic = nil /* -------------------------------------------------------------------- */ /* * Direct Object Resolver implementation. We serve as our own * direct object resolver, so we define any special resolver * behavior here. */ /* the true role of the resolved object is always as the direct object */ whichObject = DirectObject /* * What we call our direct object might actually be playing the * grammatical role of the indirect object - in order to inherit * easily from TAction, we call our resolved object our direct * object, regardless of which grammatical role it actually plays. * For the most part it doesn't matter which is which; but for the * purposes of our resolver, we actually do care about its real role. * So, override the resolver method whichMessageObject so that it * returns whichever role is NOT served by the topic object. */ whichMessageObject = (whichMessageTopic == DirectObject ? IndirectObject : DirectObject) ; /* * "Conversation" TopicTAction. Many TopicTAction verbs involve * conversation with an actor, who's specified as the direct object: ASK * ABOUT , TELL ABOUT , ASK FOR * . For these common cases, the most likely default direct * object is the last interlocutor of the actor performing the command - * that is, ASK ABOUT BOOK should by default be directed to whomever we * were speaking to last. * * This subclass is suitable for such verbs. When asked for a default * direct object, we'll check for a current interlocutor, and use it as * the default if available. If no interlocutor is available, we'll * inherit the standard default handling. */ class ConvTopicTAction: TopicTAction getDefaultDobj(np, resolver) { /* * check to see if the actor has a default interlocutor; if so, * use it as the default actor to be addressed here, otherwise * use the default handling */ local obj = resolver.getTargetActor().getCurrentInterlocutor(); if (obj != nil) return [new ResolveInfo(obj, 0, nil)]; else return inherited(np, resolver); } /* * Create the topic resolver. Use a conversational topic resolver * for this type of action. */ createTopicResolver(issuingActor, targetActor) { return new ConvTopicResolver( self, issuingActor, targetActor, topicMatch, whichMessageTopic, getTopicQualifierResolver(issuingActor, targetActor, nil)); } ; /* ------------------------------------------------------------------------ */ /* * A conversational intransitive action. This class is for actions such * as Hello, Goodbye, Yes, No - conversational actions that don't involve * any topics or other objects, but whose expression is entirely * contained in the verb. */ class ConvIAction: IAction /* this action is conversational */ isConversational(issuer) { return true; } ; /* ------------------------------------------------------------------------ */ /* * Resolved Topic object. The topic of a TopicTAction always resolves to * one of these objects. */ class ResolvedTopic: object construct(inScope, likely, others, prod) { /* * Remember our lists of objects. We keep three separate lists, * so that the action knows how we've classified the objects * matching our phrase. We keep a list of objects that are in * scope; a list of objects that aren't in scope but which the * actor thinks are likely topics; and a list of all of the other * matches. * * We keep only the simulation objects in these retained lists - * we don't keep the full ResolveInfo data here. */ inScopeList = inScope.mapAll({x: x.obj_}); likelyList = likely.mapAll({x: x.obj_}); otherList = others.mapAll({x: x.obj_}); /* keep the production match tree */ topicProd = prod; /* * But it might still be interesting to have the ResolveInfo * data, particularly for information on the vocabulary match * strength. Keep that information in a separate lookup table * indexed by simulation object. */ local rt = new LookupTable(); others.forEach({x: rt[x.obj_] = x}); likely.forEach({x: rt[x.obj_] = x}); inScope.forEach({x: rt[x.obj_] = x}); /* save the ResolvedInfo lookup table */ resInfoTab = rt; } /* * Static method: create a ResolvedTopic to represent an object * that's already been resolved to a game object for the current * action. 'role' is the object role to wrap (DirectObject, * IndirectObject, etc). */ wrapActionObject(role) { local rt; /* create a ResolvedTopic with just the match tree */ rt = new ResolvedTopic([], [], [], gAction.getMatchForRole(role)); /* * add the current resolved object in the role as the one * in-scope match */ rt.inScopeList += gAction.getObjectForRole(role); /* return the ResolvedTopic wrapper */ return rt; } /* * Static method: create a ResolvedTopic to represent the given * object. */ wrapObject(obj) { local rt; /* create a ResolvedTopic with a PreResolvedProd for the object */ rt = new ResolvedTopic([], [], [], new PreResolvedProd(obj)); /* add the given object or objects as the in-scope match list */ rt.inScopeList += obj; /* return the ResolvedTopic wrapper */ return rt; } /* * Get the best object match to the topic. This is a default * implementation that can be changed by game authors or library * extensions to implement different topic-matching strategies. This * implementation simply picks an object arbitrarily from the * "strongest" of the three lists we build: if there's anything in * the inScopeList, we choose an object from that list; otherwise, if * there's anything in the likelyList, we choose an object from that * list; otherwise we choose an object from the otherList. */ getBestMatch() { /* if there's an in-scope match, pick one */ if (inScopeList.length() != 0) return inScopeList[1]; /* nothing's in scope, so try to pick a likely match */ if (likelyList.length() != 0) return likelyList[1]; /* * there's not even anything likely, so pick an "other", if * there's anything in that list */ if (otherList.length() != 0) return otherList[1]; /* there are no matches at all - return nil */ return nil; } /* * Is the given object among the possible matches for the topic? */ canMatchObject(obj) { return inScopeList.indexOf(obj) != nil || likelyList.indexOf(obj) != nil || otherList.indexOf(obj) != nil; } /* get the original text of the topic phrase */ getTopicText() { return topicProd.getOrigText(); } /* * Are we allowed to match the topic text literally, for parsing * purposes? If this is true, it means that we can match the literal * text the player entered against strings, regular expressions, * etc.; for example, we can match a TopicMatchTopic's matchPattern * regular expression. If this is nil, it means that we can only * interpret the meaning of the resolved topic by looking at the * various topic match lists (inScopeList, likelyList, otherList). * * By default, we simply return true. Note that the base library * never has any reason of its own to disallow literal matching of * topic text; this property is purely for the use of language * modules, to handle language-specific input that parses at a high * level as a topic phrase but which has some idiomatic or * grammatical function that makes it in appropriate to try to * extract the meaning of the resolved topic from the literal text of * the topic phrase in isolation. This case doesn't seem to arise in * English, but does occur in other languages: Michel Nizette cites * "parlez-en a Bob" as an example in French, where "en" is * essentially a particle modifying the verb, not a full-fledged * phrase that we can interpret separately as a topic. */ canMatchLiterally = true /* * get the original tokens of the topic phrase, in canonical * tokenizer format */ getTopicTokens() { return topicProd.getOrigTokenList(); } /* * get the original word strings of the topic phrase - this is * simply a list of the original word strings (in their original * case), without any of the extra information of the more * complicated canonical tokenizer format */ getTopicWords() { /* return just the original text strings from the token list */ return topicProd.getOrigTokenList().mapAll({x: getTokOrig(x)}); } /* * Get the parser ResolveInfo object for a given matched object. * This recovers the ResolveInfo describing the parsing result for * any object in the resolved object lists (inScopeList, etc). */ getResolveInfo(obj) { return resInfoTab[obj]; } /* * Our lists of resolved objects matching the topic phrase, * separated by classification. */ inScopeList = nil likelyList = nil otherList = nil /* * The production match tree object that matched the topic phrase in * the command. This can be used to obtain the original tokens of * the command or the original text of the phrase. */ topicProd = nil /* * ResolveInfo table for the resolved objects. This is a lookup * table indexed by simulation object. Each entry in the resolved * object lists (inScopeList, etc) has have a key in this table, with * the ResolveInfo object as the value for the key. This can be used * to recover the ResolveInfo object describing the parser results * for this object. */ resInfoTab = nil ; /* * A special topic for "nothing." It's occasionally useful to be able * to construct a TopicTAction with an empty topic phrase. For the * topic phrase to be well-formed, we need a valid ResolvedTopic object, * even though it won't refer to anything. */ resolvedTopicNothing: ResolvedTopic inScopeList = [] likelyList = [] otherList = [] getTopicText() { return ''; } getTopicTokens() { return []; } getTopicWords() { return []; } ; /* * Topic Resolver */ class TopicResolver: Resolver construct(action, issuingActor, targetActor, prod, which, qualifierResolver) { /* inherit the base class constructor */ inherited(action, issuingActor, targetActor); /* the topic phrase doesn't have a true resolved role */ whichObject = nil; /* remember the grammatical role of our object in the command */ whichMessageObject = which; /* remember our topic match tree */ topicProd = prod; /* remember the resolver for qualifier phrases */ qualifierResolver_ = qualifierResolver; } resetResolver() { /* inherit the default handling */ inherited(); /* reset our qualifier resolver as well */ qualifierResolver_.resetResolver(); } /* * package a resolved topic list - if it's not already represented as * a ResolvedTopic object, we'll apply that wrapping */ packageTopicList(lst, match) { /* * if the topic is other than a single ResolvedTopic object, run * it through the topic resolver's ambiguous noun phrase filter * to get it into that canonical form */ if (lst != nil && (lst.length() > 1 || (lst.length() == 1 && !lst[1].obj_.ofKind(ResolvedTopic)))) { /* it's not in canonical form, so get it in canonical form now */ lst = filterAmbiguousNounPhrase(lst, 1, match); } /* return the result */ return lst; } /* our qualifier resolver */ qualifierResolver_ = nil /* get our qualifier resolver */ getQualifierResolver() { return qualifierResolver_; } getPossessiveResolver() { return qualifierResolver_; } /* * Determine if the object is in scope. We consider any vocabulary * match to be in scope for the purposes of a topic phrase, since the * subject matter of topics is mere references to things, not the * things themselves; we can, for example, ASK ABOUT things that * aren't physically present, or even about completely abstract * ideas. */ objInScope(obj) { return true; } /* * our scope is global, because we don't limit the scope to the * physical senses */ isGlobalScope = true /* * Determine if an object is in physical scope. We'll accept * anything that's in physical scope, and we'll also accept any topic * object that the actor knows about. * * Note that this isn't part of the basic Resolver interface. It's * instead provided as a service routine for our subclasses, so that * they can easily determine the physical scope of an object if * needed. */ objInPhysicalScope(obj) { /* * it's in physical scope, or if the actor knows about it as a * topic, it's in scope */ return (scope_.indexOf(obj) != nil); } /* * Filter an ambiguous noun phrase list using the strength of * possessive qualification, if any. For a topic phrase, we want to * keep all of the possibilities. */ filterPossRank(lst, num) { return lst; } /* * Filter an ambiguous noun list. * * It is almost always undesirable from a user interface perspective * to ask for help disambiguating a topic phrase. In the first * place, since all topics tend to be in scope all the time, we * might reveal too much about the inner model of the story if we * were to enumerate all of the topic matches to a phrase. In the * second place, topics are used in conversational contexts, so it * almost never make sense for the parser to ask for clarification - * the other member of the conversation might ask, but not the * parser. So, we'll always filter the list to the required number, * even if it means we choose arbitrarily. * * As a first cut, we prefer objects that are physically in scope to * those not in scope: if the player is standing next to a control * panel and types "ask bob about control panel," it makes little * sense to consider any other control panels in the simulation. * * As a second cut, we'll ask the actor to filter the list. Games * that keep track of the actor's knowledge can use this to filter * according to topics the actor is likely to know about. */ filterAmbiguousNounPhrase(lst, requiredNum, np) { /* ask the action to create the ResolvedTopic */ local rt = resolveTopic(lst, requiredNum, np); /* wrap the ResolvedTopic in the usual ResolveInfo list */ return [new ResolveInfo(rt, 0, np)]; } /* * Resolve the topic phrase. This returns a ResolvedTopic object * encapsulating the resolution of the phrase. * * This default base class implementation simply creates a resolved * topic list with the whole set of possible matches * undifferentiated. Subclasses for specialized actions might want * to differentiate the items in the list, based on things like the * actor's knowledge so far or what's in physical scope. */ resolveTopic(lst, requiredNum, np) { /* return a ResolvedTopic that lumps everything in the main list */ return new ResolvedTopic(lst, [], [], topicProd); } /* * Resolve an unknown phrase. We allow unknown words to be used in * topics; we simply return a ResolvedTopic that doesn't refer to * any simulation objects at all. */ resolveUnknownNounPhrase(tokList) { /* * Create our ResolvedTopic object for the results. We have * words we don't know, so we're not referring to any objects, * so our underlying simulation object list is empty. */ local rt = new ResolvedTopic([], [], [], topicProd); /* return a resolved topic object with the empty list */ return [new ResolveInfo(rt, 0, new TokenListProd(tokList))]; } /* filter a plural */ filterPluralPhrase(lst, np) { /* * Handle this the same way we handle an ambiguous noun phrase, * so that we yield only one object. Topics are inherently * singular; we'll allow asking about a grammatically plural * term, but we'll turn it into a single topic result. */ return filterAmbiguousNounPhrase(lst, 1, np); } /* get a default object */ getDefaultObject(np) { /* there is never a default for a topic */ return nil; } /* it's fine not to match a topic phrase */ noVocabMatch(action, txt) { } noMatch(action, txt) { } noMatchPoss(action, txt) { } /* we don't allow ALL or provide defaults */ getAll(np) { return []; } getAllDefaults() { return []; } /* the production match tree for the topic phrase we're resolving */ topicProd = nil ; /* * A topic resolver specialized for TopicTActions - actions involving a * topic and a physical object, such as CONSULT ABOUT. For these topics, * we'll let the action handle the resolution. */ class TActionTopicResolver: TopicResolver resolveTopic(lst, requiredNum, np) { /* let the action handle it */ return action_.filterTopic(lst, np, self); } ; /* * A topic resolver specialized for conversational actions (ASK ABOUT, * TELL ABOUT, etc). When we resolve the topic, we'll differentiate the * resolution to differentiate based on the knowledge of the actor who's * performing the command. */ class ConvTopicResolver: TopicResolver /* * Resolve the topic phrase. We'll break up the vocabulary matches * into three sublists: the objects that are either in physical scope * or known to the actor performing the command; objects that the * actor considers likely topics; and everything else. */ resolveTopic(lst, requiredNum, np) { local inScope; local actorPrefs; /* * First, get the subset of items that are in conversational * scope - we'll consider this the best set of matches. */ inScope = lst.subset({x: objInConvScope(x.obj_)}); /* * eliminate the in-scope items from the list, so we can * consider only what remains */ lst -= inScope; /* * ask the actor to pick out the most likely set of topics from * the ones that remain */ actorPrefs = lst.subset({x: actor_.isLikelyTopic(x.obj_)}); /* eliminate those items from the list */ lst -= actorPrefs; /* create our ResolvedTopic object and return it */ return new ResolvedTopic(inScope, actorPrefs, lst, topicProd); } /* * Determine if an object is in "conversational" scope - this returns * true if the object is in physical scope or it's known to the actor * performing the command. */ objInConvScope(obj) { /* * if it's in physical scope, or the actor knows about it, it's * in conversation scope */ return (objInPhysicalScope(obj) || actor_.knowsTopic(obj)); } ; /* ------------------------------------------------------------------------ */ /* * System action. These actions are for out-of-game meta-verbs (save, * restore, undo). These verbs take no objects, must be performed by * the player (thus by the player character, not an NPC), and consume no * game clock time. */ class SystemAction: IAction /* execute the action */ execAction() { /* * Conceptually, system actions are performed by the player * directly, not by any character in the game (not even the * player character). However, we don't distinguish in the * command-entry user interface between a command the player is * performing and a command to the player character, hence we * can merely ensure that the command is not directed to a * non-player character. */ if (!gActor.isPlayerChar) { gLibMessages.systemActionToNPC(); exit; } /* * system actions sometimes need to prompt for interactive * responses, so deactivate the report list - this will allow * interactive prompts to be shown immediately, not treated as * reports to be deferred until the command is finished */ gTranscript.showReports(true); gTranscript.clearReports(); /* perform our specific action */ execSystemAction(); /* re-activate the transcript for the next command */ gTranscript.activate(); } /* each subclass must override this to perform its actual action */ execSystemAction() { } /* * Ask for an input file. We call the input manager, which freezes * the real-time clock, displays the appropriate local file selector * dialog, and restarts the clock. */ getInputFile(prompt, dialogType, fileType, flags) { return inputManager.getInputFile(prompt, dialogType, fileType, flags); } /* system actions consume no game time */ actionTime = 0 ; /* ------------------------------------------------------------------------ */ /* * An exception class for remappings that we can't handle. */ class ActionRemappingTooComplexError: Exception ; frobtads-1.2.3/tads3/lib/adv3/objects.t0000644000175000001440000065724711702413654016773 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. *. Portions based on work by Kevin Forchione, used by permission. * * TADS 3 Library - objects * * This module defines the basic physical simulation objects (apart from * Thing, the base class for most game objects, which is so large that * it's defined in its own separate module for convenience). We define * such basic classes as containers, surfaces, fixed-in-place objects, * openables, and lockables. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * LocateInParent - this is a mix-in superclass that defines the location * of the object as the object's lexical parent. This is useful for * nested object definitions where the next object should be located * within the enclosing object. * * When this class is mixed with Thing or its subclasses, LocateInParent * should go first, so that the location we define here takes precedence. */ class LocateInParent: object location = (lexicalParent) ; /* ------------------------------------------------------------------------ */ /* * Intangible - this is an object that represents something that can be * sensed but which has no tangible existence, such as a ray of light, a * sound, or an odor. */ class Intangible: Thing /* * The base intangible object has no presence in any sense, * including sight. Subclasses should override these as appropriate * for the senses in which the object can be sensed. */ sightPresence = nil soundPresence = nil smellPresence = nil touchPresence = nil /* intangibles aren't included in regular room/inventory/contents lists */ isListed = nil isListedInInventory = nil isListedInContents = nil /* hide intangibles from 'all' for all actions by default */ hideFromAll(action) { return true; } /* don't hide from defaults, though */ hideFromDefault(action) { return nil; } /* * Essentially all verbs are meaningless on intangibles. Each * subclass should re-enable verbs that are meaningful for that * specific type of intangible; to re-enable an action, just define * a verify() handler for the action. * * Note that the verbs we handle via the Default handlers have no * preconditions; since these verbs don't do anything anyway, * there's no need to apply any preconditions to them. */ dobjFor(Default) { preCond = [] verify() { illogical(¬WithIntangibleMsg, self); } } iobjFor(Default) { preCond = [] verify() { illogical(¬WithIntangibleMsg, self); } } ; /* * A "vaporous" object is a visible but intangible object: something * visible, and possibly with an odor and a sound, but not something that * can be touched or otherwise physically manipulated. Fire, smoke, and * fog are examples of this kind of object. */ class Vaporous: Intangible /* we have a sight presence */ sightPresence = true /* * EXAMINE ALL, LISTEN TO ALL, and SMELL ALL apply to us, but hide * from ALL for other actions, as not much else makes sense on us */ hideFromAll(action) { return !(action.ofKind(ExamineAction) || action.ofKind(ListenToAction) || action.ofKind(SmellAction)); } /* * We can examine, smell, and listen to these objects, as normal for * any Thing. To make these verbs work as normal for Thing, we need * to explicitly override the corresponding verifiers, so that we * bypass the dobjFor(Default) verifier in Intangible. We don't need * to do anything special in the overrides, so just inherit the * default handling; what's important is that we do override the * methods at all. */ dobjFor(Examine) { verify() { inherited(); } } dobjFor(Smell) { verify() { inherited(); } } dobjFor(ListenTo) { verify() { inherited(); } } /* * look in, look through, look behind, look under, search: since * vaporous objects are usually essentially transparent, these * commands reveal nothing interesting */ lookInDesc { mainReport(&lookInVaporousMsg, self); } /* * downgrade the likelihood of these slightly, and map everything to * LOOK IN */ dobjFor(LookIn) { verify() { logicalRank(70, 'look in vaporous'); } } dobjFor(LookThrough) asDobjFor(LookIn) dobjFor(LookBehind) asDobjFor(LookIn) dobjFor(LookUnder) asDobjFor(LookIn) dobjFor(Search) asDobjFor(LookIn) /* the message we display for commands we disallow */ notWithIntangibleMsg = ¬WithVaporousMsg ; /* * A sensory emanation. This is an intangible object that represents a * sound, odor, or the like. */ class SensoryEmanation: Intangible /* * Are we currently emanating our sensory information? This can be * used as an on/off switch to control when we're active. */ isEmanating = true /* * The description shown when the *source* is examined (with "listen * to", "smell", or whatever verb is appropriate to the type of sense * the subclass involves). This will also be appended to the regular * "examine" description, if we're not marked as ambient. */ sourceDesc = "" /* our description, with and without being able to see the source */ descWithSource = "" descWithoutSource = "" /* * Our "I am here" message, with and without being able to see the * source. These are displayed in room descriptions, inventory * descriptions, and by the daemon that schedules background messages * for sensory emanations. * * If different messages are desired as the emanation is mentioned * repeatedly while the emanation remains continuously within sense * range of the player character ("A phone is ringing", "The phone is * still ringing", etc), you can do one of two things. The easier * way is to use a Script object; each time we need to show a * message, we'll invoke the script. The other way, which is more * manual but gives you greater control, is to write a method that * checks the displayCount property of self to determine which * iteration of the message is being shown. displayCount is set to 1 * the first time a message is displayed for the object when the * object can first be sensed, and is incremented each we invoke one * of these display routines. Note that displayCount resets to nil * when the object leaves sense scope, so the sequence of messages * will automatically start over each time the object comes back into * scope. * * The manual way (writing a method that checks the displayCount) * might be desirable if you want the emanation to fade into the * background gradually as the player character stays in the same * location repeatedly. This mimics human perception: we notice a * noise or odor most when we first hear it, but if it continues for * an extended period without changing, we'll eventually stop * noticing it. */ hereWithSource = "" hereWithoutSource = "" /* * A message to display when the emanation ceases to be within sense * range. In most cases, this displays nothing at all, but some * emanations might want to note explicitly when the noise/etc * stops. */ noLongerHere = "" /* * Flag: I'm an "ambient" emanation. This means we essentially are * part of the background, and are not worth mentioning in our own * right. If this is set to true, then we won't mention this * emanation at all when it first becomes reachable in its sense. * This should be used for background noises and the like: we won't * ever make an unsolicited mention of them, but they'll still show * up in explicit 'listen' commands and so on. */ isAmbient = nil /* * The schedule for displaying messages about the emanation. This * is a list of intervals between messages, in game clock times. * When the player character can repeatedly sense this emanation for * multiple consecutive turns, we'll use this schedule to display * messages periodically about the noise/odor/etc. * * Human sensory perception tends to be "edge-sensitive," which * means that we tend to perceive sensory input most acutely when * something changes. When a sound or odor is continually present * without variation for an extended period, it tends to fade into * the background of our awareness, so that even though it remains * audible, we gradually stop noticing it. This message display * schedule mechanism is meant to approximate this perceptual model * by allowing the sensory emanation to specify how noticeable the * emanation remains during continuous exposure. Typically, a * continuous emanation would have relatively frequent messages * (every two turns, say) for a couple of iterations, then would * switch to infrequent messages. Emanations that are analogous to * white noise would probably not be mentioned at all after the * first couple of messages, because the human senses are especially * given to treating such input as background. * * We use this list by applying each interval in the list once and * then moving to the next entry in the list. The first entry in * the list is the interval between first sensing the emanation and * displaying the first "still here" message. When we reach the end * of the list, we simply repeat the last interval in the list * indefinitely. If the last entry in the list is nil, though, we * simply never produce another message. */ displaySchedule = [nil] /* * Show our "I am here" description. This is the description shown * as part of our room's description. We show our hereWithSource or * hereWithoutSource message, according to whether or not we can see * the source object. */ emanationHereDesc() { local actor; local prop; /* if we're not currently emanating, there's nothing to do */ if (!isEmanating) return; /* note that we're mentioning the emanation */ noteDisplay(); /* * get the actor driving the description - if there's a command * active, use the command's actor; otherwise use the player * character */ if ((actor = gActor) == nil) actor = gPlayerChar; /* our display varies according to our source's visibility */ prop = (canSeeSource(actor) ? &hereWithSource : &hereWithoutSource); /* * if it's a Script object, invoke the script; otherwise, just * invoke the property */ if (propType(prop) == TypeObject && self.(prop).ofKind(Script)) self.(prop).doScript(); else self.(prop); } /* * Show a message describing that we cannot see the source of this * emanation because the given obstructor is in the way. This * should be overridden for each subclass. */ cannotSeeSource(obs) { } /* * Get the source of the noise/odor/whatever, as perceived by the * current actor. This is the object we appear to be coming from. * By default, an emanation is generated by its direct container, * and by default this is apparent to actors, so we'll simply return * our direct container. * * If the source is not apparent, this should simply return nil. */ getSource() { return location; } /* determine if our source is apparent and visible */ canSeeSource(actor) { local src; /* get our source */ src = getSource(); /* * return true if we have an apparent source, and the apparent * source is visible to the current actor */ return src != nil && actor.canSee(src); } /* * Note that we're displaying a message about the emanation. This * method should be called any time a message about the emanation is * displayed, either by an explicit action or by our background * daemon. * * We'll adjust our next display time so that we wait the full * interval at the current point in the display schedule before we * show any background message about this object. Note we do not * advance through the schedule list; instead, we merely delay any * further message by the interval at the current point in the * schedule list. */ noteDisplay() { /* calculate our next display time */ calcNextDisplayTime(); /* count the display */ if (displayCount == nil) displayCount = 1; else ++displayCount; } /* * Note an indirect message about the emanation. This can be used * when we don't actually display a message ourselves, but another * object (usually our source object) describes the emanation; for * example, if our source object mentions the noise it's making when * it is examined, it should call this method to let us know we have * been described indirectly. This method advances our next display * time, just as noteDisplay() does, but this method doesn't count * the display as a direct display. */ noteIndirectDisplay() { /* calculate our next display time */ calcNextDisplayTime(); } /* * Begin the emanation. This is called from the sense change daemon * when the item first becomes noticeable to the player character - * for example, when the player character first enters the room * containing the emanation, or when the emanation is first * activated. */ startEmanation() { /* if we're an ambient emanation only, don't mention it */ if (isAmbient) return; /* * if we've already initialized our scheduling, we must have * been explicitly mentioned, such as by a room description - in * this case, act as though we're continuing our emanation */ if (scheduleIndex != nil) { continueEmanation(); return; } /* show our message */ emanationHereDesc; } /* * Continue the emanation. This is called on each turn in which the * emanation remains continuously within sense range of the player * character. */ continueEmanation() { /* * if we are not to run again, our next display time will be set * to zero - do nothing in this case */ if (nextDisplayTime == 0 || nextDisplayTime == nil) return; /* if we haven't yet reached our next display time, do nothing */ if (Schedulable.gameClockTime < nextDisplayTime) return; /* * Advance to the next schedule interval, if we have one. If * we're already on the last schedule entry, simply repeat it * forever. */ if (scheduleIndex < displaySchedule.length()) ++scheduleIndex; /* show our description */ emanationHereDesc; } /* * End the emanation. This is called when the player character can * no longer sense the emanation. */ endEmanation() { /* show our "no longer here" message */ noLongerHere; /* uninitialize the display scheduling */ scheduleIndex = nil; nextDisplayTime = nil; /* reset the display count */ displayCount = nil; } /* * Calculate our next display time. The caller must set our * scheduleIndex to the correct index prior to calling this. */ calcNextDisplayTime() { local delta; /* if our scheduling isn't initialized, set it up now */ if (scheduleIndex == nil) { /* start at the first display schedule interval */ scheduleIndex = 1; } /* get the next display interval from the schedule list */ delta = displaySchedule[scheduleIndex]; /* * if the current display interval is nil, it means that we're * never to display another message */ if (delta == nil) { /* * we're not to display again - simply set the next display * time to zero and return */ nextDisplayTime = 0; return; } /* * our next display time is the current game clock time plus the * interval */ nextDisplayTime = Schedulable.gameClockTime + delta; } /* * Internal counters that keep track of our display scheduling. * scheduleIndex is the index in the displaySchedule list of the * interval we're waiting to expire; nextDisplayTime is the game * clock time of our next display. noiseList and odorList are lists * of senseInfo entries for the sound and smell senses, * respectively, indicating which objects were within sense range on * the last turn. displayCount is the number of times in a row * we've displayed a message already. */ scheduleIndex = nil nextDisplayTime = nil noiseList = nil odorList = nil displayCount = nil /* * Class method implementing the sensory change daemon. This runs * on each turn to check for changes in the set of objects the * player can hear and smell, and to generate "still here" messages * for objects continuously within sense range for multiple turns. */ noteSenseChanges() { /* emanations don't change anything, so turn on caching */ libGlobal.enableSenseCache(); /* note sound changes */ noteSenseChangesFor(sound, &noiseList, Noise); /* note odor changes */ noteSenseChangesFor(smell, &odorList, Odor); /* done with sense caching */ libGlobal.disableSenseCache(); } /* * Note sense changes for a particular sense. 'listProp' is the * property of SensoryEmanation giving the list of SenseInfo entries * for the sense on the previous turn. 'sub' is a subclass of ours * (such as Noise) giving the type of sensory emanation used for * this sense. */ noteSenseChangesFor(sense, listProp, sub) { local newInfo; local oldInfo; /* get the old table of SenseInfo entries for the sense */ oldInfo = self.(listProp); /* * Get the new table of items we can reach in the given sense, * and reduce it to include only emanations of the subclass of * interest. */ newInfo = gPlayerChar.senseInfoTable(sense); newInfo.forEachAssoc(function(obj, info) { /* * remove this item if it's not of the subclass of interest, * or if it's not currently emanating */ if (!obj.ofKind(sub) || !obj.isEmanating) newInfo.removeElement(obj); }); /* run through the new list and note each change */ newInfo.forEachAssoc(function(obj, info) { /* treat this as a new command visually */ "<.commandsep>"; /* * Check to see whether the item is starting anew or was * already here on the last turn. If the item was in our * list from the previous turn, it was already here. */ if (oldInfo == nil || oldInfo[obj] == nil) { /* * the item wasn't in sense range on the last turn, so * it is becoming newly noticeable */ obj.startEmanation(); } else { /* the item was already here - continue its emanation */ obj.continueEmanation(); } }); /* run through the old list and note each item no longer sensed */ if (oldInfo != nil) { oldInfo.forEachAssoc(function(obj, info) { /* if this item isn't in the new list, note its departure */ if (newInfo[obj] == nil) { /* treat this as a new command visually */ "<.commandsep>"; /* note the departure */ obj.endEmanation(); } }); } /* store the current list for comparison the next time we run */ self.(listProp) = newInfo; } /* * Examine the sensory emanation. We'll show our descWithSource or * descWithoutSource, according to whether or not we can see the * source object. */ dobjFor(Examine) { verify() { inherited(); } action() { /* note that we're displaying a message about us */ noteDisplay(); /* display our sound description */ if (canSeeSource(gActor)) { /* we can see the source */ descWithSource; } else { local src; /* show the unseen-source version of the description */ descWithoutSource; /* * If we have a source, find out what's keeping us from * seeing the source; in other words, find the opaque * visual obstructor on the sense path to the source. */ if ((src = getSource()) != nil) { local obs; /* get the visual obstructor */ obs = gActor.findVisualObstructor(src); /* * If we found an obstructor, and we can see it, add * a message describing the obstruction. If we * can't see the obstructor, we can't localize the * sensory emanation at all. */ if (obs != nil && gActor.canSee(obs)) cannotSeeSource(obs); } } } } ; /* * Noise - this is an intangible object representing a sound. * * A Noise object is generally placed directly within the object that is * generating the noise. This will ensure that the noise is * automatically in scope whenever the object is in scope (or, more * precisely, whenever the object's contents are in scope) and with the * same sense attributes. * * By default, when a noise is specifically examined via "listen to", * and the container is visible, we'll mention that the noise is coming * from the container. */ class Noise: SensoryEmanation /* * by default, we have a definite presence in the sound sense if * we're emanating our noise */ soundPresence = (isEmanating) /* * By default, a noise is listed in a room description (i.e., on LOOK * or entry to a room) unless it's an ambient background noise.. Set * this to nil to omit the noise from the room description, while * still allowing it to be heard in an explicit LISTEN command. */ isSoundListedInRoom = (!isAmbient && isEmanating) /* show our description as part of a room description */ soundHereDesc() { emanationHereDesc(); } /* explain that we can't see the source because of the obstructor */ cannotSeeSource(obs) { obs.cannotSeeSoundSource(self); } /* treat "listen to" the same as "examine" */ dobjFor(ListenTo) asDobjFor(Examine) /* "examine" requires that the object is audible */ dobjFor(Examine) { preCond = [objAudible] } ; /* * Odor - this is an intangible object representing an odor. */ class Odor: SensoryEmanation /* * by default, we have a definite presence in the smell sense if * we're currently emanating our odor */ smellPresence = (isEmanating) /* * By default, an odor is listed in a room description (i.e., on LOOK * or entry to a room) unless it's an ambient background odor. Set * this to nil to omit the odor from the room description, while * still allowing it to be listed in an explicit SMELL command. */ isSmellListedInRoom = (!isAmbient && isEmanating) /* mention the odor as part of a room description */ smellHereDesc() { emanationHereDesc(); } /* explain that we can't see the source because of the obstructor */ cannotSeeSource(obs) { obs.cannotSeeSmellSource(self); } /* handle "smell" using our "examine" handler */ dobjFor(Smell) asDobjFor(Examine) /* "examine" requires that the object is smellable */ dobjFor(Examine) { preCond = [objSmellable] } ; /* * SimpleNoise is for cases where a noise is an ongoing part of a * location, so (1) it's not necessary to distinguish source and * sourceless versions of the description, and (2) there are no * scheduled reports for the noise. For these cases, all of the * messages default to the basic 'desc' property. Note that we make * this type of noise "ambient" by default, which means that we won't * automatically include it in room descriptions. */ class SimpleNoise: Noise isAmbient = true sourceDesc { desc; } descWithSource { desc; } descWithoutSource { desc; } hereWithSource { desc; } hereWithoutSource { desc; } ; /* SimpleOdor is the olfactory equivalent of SimpleNoise */ class SimpleOdor: Odor isAmbient = true sourceDesc { desc; } descWithSource { desc; } descWithoutSource { desc; } hereWithSource { desc; } hereWithoutSource { desc; } ; /* ------------------------------------------------------------------------ */ /* * Sensory Event. This is an object representing a transient event, * such as a sound, visual display, or odor, to which some objects * observing the event might react. * * A sensory event differs from a sensory emanation in that an emanation * is ongoing and passive, while an event is isolated in time and * actively notifies observers. */ class SensoryEvent: object /* * Trigger the event. This routine must be called at the time when * the event is to occur. We'll notify every interested observer * capable of sensing the event that the event is occurring, so * observers can take appropriate action in response to the event. * * 'source' is the source object - this is the physical object in * the simulation that is causing the event. For example, if the * event is the sound of a phone ringing, the phone would probably * be the source object. The source is used to determine which * observers are capable of detecting the event: an observer must be * able to sense the source object in the appropriate sense to be * notified of the event. */ triggerEvent(source) { /* * Run through all objects connected to the source object by * containment, and notify any that are interested and can * detect the event. Containment is the only way sense * information can propagate, so we can limit our search * accordingly. * * Connection by containment is no guarantee of a sense * connection: it's a necessary, but not sufficient, condition. * Because it's a necessary condition, though, we can use it to * limit the number of objects we have to test with a more * expensive sense path calculation. */ source.connectionTable().forEachAssoc(function(cur, val) { /* * If this object defines the observer notification method, * then it might be interested in the event. If the object * doesn't define this method, then there's no way it could * be interested. (We make this test before checking the * sense path because checking to see if an object defines a * property is fast and simple, while the sense path * calculation could be expensive.) */ if (cur.propDefined(notifyProp, PropDefAny)) { local info; /* * This object might be interested in the event, so * check to see if the object can sense the event. If * this object can sense the source object at all (i.e., * the sense path isn't 'opaque'), then notify the * object of the event. */ info = cur.senseObj(sense, source); if (info.trans != opaque) { /* * this observer object can sense the source of the * event, so notify it of the event */ cur.(notifyProp)(self, source, info); } } }); } /* the sense in which the event is observable */ sense = nil /* * the notification property - this is the property we'll invoke on * each observer to notify it of the event */ notifyProp = nil ; /* * Visual event */ class SightEvent: SensoryEvent sense = sight notifyProp = ¬ifySightEvent ; /* * Visual event observer. This is a mix-in that can be added to any * other classes. */ class SightObserver: object /* * Receive notification of a sight event. This routine is called * whenever a SightEvent occurs within view of this object. * * 'event' is the SightEvent object; 'source' is the physical * simulation object that is making the visual display; and 'info' * is a SenseInfo object describing the viewing conditions from this * object to the source object. */ notifySightEvent(event, source, info) { } ; /* * Sound event */ class SoundEvent: SensoryEvent sense = sound notifyProp = ¬ifySoundEvent ; /* * Sound event observer. This is a mix-in that can be added to any * other classes. */ class SoundObserver: object /* * Receive notification of a sound event. This routine is called * whenever a SoundEvent occurs within hearing range of this object. */ notifySoundEvent(event, source, info) { } ; /* * Smell event */ class SmellEvent: SensoryEvent sense = smell notifyProp = ¬ifySmellEvent ; /* * Smell event observer. This is a mix-in that can be added to any * other classes. */ class SmellObserver: object /* * Receive notification of a smell event. This routine is called * whenever a SmellEvent occurs within smelling range of this * object. */ notifySmellEvent(event, source, info) { } ; /* ------------------------------------------------------------------------ */ /* * Hidden - this is an object that's present but not visible to any * actors. The object will simply not be visible in the 'sight' sense * until discovered. */ class Hidden: Thing /* we can't be seen until discovered */ canBeSensed(sense, trans, ambient) { /* * If the sense is sight, and we haven't been discovered yet, we * cannot be sensed. Otherwise, inherit the normal handling. */ if (sense == sight && !discovered) return nil; else return inherited(sense, trans, ambient); } /* * Have we been discovered yet? * * Note that this should be a simple property value, not a method. * It's risky to make this a method because it's evaluated from * within some of the low-level scope/sense calculations, and those * calculations depend upon certain global variables. If you make * this property into a method, you could indirectly call another * method that changes some of the same globals, which could disrupt * the main scope/sense calculations and cause other, seemingly * unrelated objects to mysteriously appear or disappear at the wrong * times. If you need to calculate this value dynamically, you could * explicitly assign the property a new value in something like a * daemon or an afterAction() method. * * (The warning above is a bit more conservative than is strictly * necessary. It actually is safe to make 'discovered' a method, * *provided* that the method doesn't ever call anything that's * involved in the scope/sense calculations. For example, never call * methods like senseObj(), senseAmbientMax(), or * sensePresenceList(), or anything that calls those. In most cases, * it's safe to call non-sense-related methods, like isOpen() or * isIn().) */ discovered = nil /* mark the object as discovered */ discover() { local pc; /* note that we've been discovered */ discovered = true; /* mark me and my contents as having been seen */ if ((pc = gPlayerChar).canSee(self)) { /* mark me as seen */ pc.setHasSeen(self); /* mark my visible contents as see */ setContentsSeenBy(pc.visibleInfoTable(), pc); } } ; /* ------------------------------------------------------------------------ */ /* * Collective - this is an object that can be used to refer to a group of * other (usually equivalent) objects collectively. In most cases, this * object will be a separate game object that contains or can contain the * individuals: a bag of marbles can be a collective for the marbles, or * a book of matches can be a collective for the matchsticks. * * A collective object is usually given the same plural vocabulary as its * individuals. When we use that plural vocabulary, we will filter for * or against the collective, as determined by the noun phrase * production, when the player uses the collective term. * * This is a mix-in class, intended to be used along with other (usually * Thing-derived) superclasses. */ class Collective: object filterResolveList(lst, action, whichObj, np, requiredNum) { /* scan for my matching individuals */ foreach (local cur in lst) { /* if this one's a matching individual, decide what to do */ if (isCollectiveFor(cur.obj_)) { /* * We're a collective for this object. If the noun * phrase production wants us to filter for collectives, * remove the individual and keep me (the collective); * otherwise, keep the individual and remove me. */ if (np.filterForCollectives) { /* * we want to keep the collective, so remove this * individual item */ lst -= cur; } else { /* * we want to keep individuals, so remove the * collective (i.e., myself) */ lst -= lst.valWhich({x: x.obj_ == self}); /* * we can only be in the list once, so there's no * need to keep looking - if we found another item * for which we're a collective, all we'd do is try * to remove myself again, which would be pointless * since I'm already gone */ break; } } } /* return the result */ return lst; } /* * Determine if I'm a collective object for the given object. * * In order to be a collective for some objects, an object must have * vocubulary for the plural name, and must return true from this * method for the collected objects. */ isCollectiveFor(obj) { return nil; } ; /* * A "collective group" object. This is an abstract object: the player * doesn't think of this as a physically separate object, but rather as a * collection of a bunch of individual objects. For example, if you had * a group of floor-number buttons in an elevator, you might create a * CollectiveGroup to represent the buttons as a collection - from the * player's perspective, there's not a separate physical object called * "the buttons," but it might nonetheless be handy to refer to "the * buttons" collectively as a single entity in commands. CollectiveGroup * is designed for such situations. * * There are two ways to use CollectiveGroup: as a non-physical, * non-simulation object whose only purpose is to field a few specific * commands; or as a physical simulation object that shows up separately * as an object in its own right. * * First: you can use a CollectiveGroup as a non-physical object, which * essentially means it has a nil 'location'. The group object doesn't * actually appear in any location. Instead, it'll be brought into the * sensory system automatically by its individuals, and it'll have the * same effective sensory status as the most visible/audible/etc of its * individuals. This choice is appropriate when the individuals are * mobile, so they might be scattered around the game map, hence the * group object might need to be invoked anywhere. With this option, you * normally won't want to make the CollectiveGroup handle very many * commands, because you'll have to completely customize each command you * want it to handle, in order to properly account for the possible * scattering of the individuals. For example, if you want the group * object to handle the TAKE command, you'll have to figure out which * individuals are in reach, and specially program the procedure for * taking each of the available individuals. * * Second: you can use CollectiveGroup as a simulation object, and * actually set its 'location' to the location of its individuals. The * group object in this case shows up in the simulation alongside its * individuals. This is a good choice if the individuals are fixed in * place, all in one place, because you can simply put the group object * in the same location as the individuals without worrying that the * individuals will move around the game later on. This is much easier * to handle than the first case above, mostly because commands that * physically manipulate the individuals (such as TAKE) aren't a factor. * In this set-up, you can easily let the group object handle many * actions, since it won't have to do much apart from showing the default * failure messages that a Fixed would generate in any other situation. * Note that if you use this approach, the CollectiveGroup should *also* * inherit from Fixture or the like, so that the group object is fixed in * place just like its corresponding individuals. * * The parser will substitute a CollectiveGroup object for its * individuals when (1) any of the individuals are in scope, (2) the * CollectiveGroup has vocabulary that matches a noun phrase in the * player's input, and (3) the conditions for substitution, defined by * isCollectiveQuant and isCollectiveAction, are met. * * (The substitution itself is handled in two steps. First, an * individual will add the group object to the sense connection list * whenever the individual is in the connection list, which will bring * the object into scope, so the parser will be able to match the * vocabulary from the group object any time an individual is in scope. * Once the group object is matched, its filterResolveList method will * throw out either the group object or all of the individuals, depending * on whether or not the isCollectiveQuant and isCollectiveAction tests * are met.) * * For example, we might have a bunch of coins and paper bills in a game, * and give them all a plural word 'money'. We then also create a * collective group object with plural word 'money'. We set the * collectiveGroup property of each coin and bill object to refer to the * collective group object. Whenever the player uses 'money' in a * command, the individual coins and bills will initially match, and the * group object will also match. The group object will then either throw * itself out, keeping only the individuals, or will throw out the * individuals. If the group object decides to field the command, it * will be the only matching object, so a command like "examine money" * will be directed to the single collective group object, rather than * directed to the matching individuals one at a time. This allows the * game to present simpler, more elegant responses to commands on the * individuals as a group. * * By default, the only action we handle is Examine. Each instance must * provide a suitable description so that when the collective is * examined, we describe the group of individuals appropriately. */ class CollectiveGroup: Thing /* collective group objects are usually named in plural terms */ isPlural = true /* * Filter a noun phrase resolution list. * * If there are any objects in the resolution list for which we're a * collective, we'll check to see whether we want to the collective * or keep the individuals. We want to keep the collective if the * action is one we can handle collectively; otherwise, we want to * drop the collective and let the individuals handle the action * instead. * * Note that, when any of our individuals are in scope, we're in * scope. This means that the collective is always in the * resolution list, along with the individuals, if (1) any * individuals are in scope, and (2) the vocabulary used in the noun * phrase matches the collective object. If the vocabulary doesn't * match the collective, the parser simply won't include the * collective in the resolution list by virtue of the normal * vocabulary selection mechanism, so we'll never reach this point. * * By default, the collective object will be ignored if a specific * number of objects is required. When the player explicitly * specifies a quantity (by a phrase like "the five coins" or "both * coins"), we'll assume they want to iterate over individuals * rather than operate on the collection. */ filterResolveList(lst, action, whichObj, np, requiredNum) { /* * If we want to use the collective for the current action and * the required quantity, keep the collective; otherwise, if * there are any individuals, keep the individuals and filter * out the collective group. If there are no matching * individuals, keep the collective group object, since there's * nothing to replace it. */ if (isCollectiveQuant(np, requiredNum) && isCollectiveAction(action, whichObj)) { /* * We can handle the action collectively, so keep myself, and * get rid of the individuals. We want to discard the * individuals because we want the entire action to be * handled by the collective object, rather than iterating * over the individuals. So, discard each object that has * 'self' as a collectiveGroup (which is to say, keep each * object that *doesn't* have collectiveGroup 'self'). */ lst = lst.subset({x: !x.obj_.hasCollectiveGroup(self)}); } else if (lst.indexWhich({x: x.obj_.hasCollectiveGroup(self)}) != nil) { /* * We can't handle the action collectively, and the list * includes at least one of our individuals, so let the * individuals handle it. Simply remove myself from the * list. */ lst = lst.removeElementAt(lst.indexWhich({x: x.obj_ == self})); } /* return the updated list */ return lst; } /* * "Unfilter" a pronoun antecedent list. We'll restore the * individuals to the list so that we can choose anew, for the new * command, whether to select the group object or the individuals. * * For example, suppose there's a CollectiveGroup for a set of * elevator buttons that handles the Examine command, but no other * commands. Now suppose the player types in these commands: * *. >examine buttons *. >push them * * On the first command, the CollectiveGroup object will filter out * the individual buttons in filterResolveList, because the group * object handles the Examine command on behalf of the individuals. * This will set the pronoun antecedent for IT and THEM to the group * object, because that's the program object that handled the * action. On the second command, if the player had typed simply * PUSH BUTTONS, the collective group object would have filtered * *itself* out, keeping the individuals. However, the raw pronoun * binding for THEM is the group object; if we did nothing to change * this, we'd get a different response for PUSH THEM than we'd get * for PUSH BUTTONS. That's where this routine comes in: by * restoring the individuals, we let filterResolveList() make the * decision about what to keep anew for the pronoun. */ expandPronounList(typ, lst) { /* restore our individuals to the list */ forEachInstance(Thing, function(obj) { if (obj.hasCollectiveGroup(self)) lst += obj; }); /* return the list */ return lst; } /* * Check the action to determine if it's one that we want to handle * collectively. If so, return true; if not, return nil. */ isCollectiveAction(action, whichObj) { /* we handle 'Examine' */ if (action.ofKind(ExamineAction)) return true; /* it's not one of ours */ return nil; } /* * Check to see if we're a collective for the given quantity. By * default, we return true only when no quantity is specified. */ isCollectiveQuant(np, requiredNum) { /* if no quantity was specified, use the collective */ return (requiredNum == nil); } /* * Get a list of the individuals that can be sensed, given the * information table for the desired sense (for visible items, this * can be obtained by calling gActor.visibleInfoTable()). This is a * service routine that can be useful for purposes such as writing a * description routine for the collective. For example, a "money" * collective object might want to count up the sum of money visible * and show that. * * Note that it's possible for this to return an empty list. The * caller can deal with this in a description, for example, by * indicating that the collection cannot be seen. */ getVisibleIndividuals(tab) { /* keep only those items that are individuals of this collective */ tab.forEachAssoc(function(key, val) { /* remove this item if it's not an individual of mine */ if (!key.hasCollectiveGroup(self)) tab.removeElement(key); }); /* return a list of the objects (i.e., the table's keys) */ return tab.keysToList(); } /* * When we have no location, we're an abstract object without any * physical presence in the game world. However, we still want to * show up in the senses to the same extent our individuals do. To * do this, we override this method so that we use the same sense * data as the most visible (or whatever) of our individuals. */ addToSenseInfoTable(sense, tab) { /* if we have no location, mimic our best individual */ if (location == nil && !ofKind(BaseMultiLoc)) { /* check everything in the connection table */ tab.forEachAssoc(function(cur, val) { /* if this is one of our individuals, check it */ if (cur.hasCollectiveGroup(self)) { local t; /* * If it's the best or only one so far, adopt its * sense status. Consider it the best if it has a * more transparent transparency than the best so * far, or its transparency is the same and it has a * high ambient level. */ t = transparencyCompare(cur.tmpTrans_, tmpTrans_); if (t > 0 || (t == 0 && cur.tmpAmbient_ > tmpAmbient_)) { /* it's better than our settings; mimic it */ tmpTrans_ = cur.tmpTrans_; tmpAmbient_ = cur.tmpAmbient_; tmpObstructor_ = cur.tmpObstructor_; } } }); } /* inherit the standard handling */ inherited(sense, tab); } /* * When we have no location, we want to create our own special * containment path, just as we create our own special SenseInfo. */ specialPathFrom(src, vec) { /* if we have a location, use the normal handling */ if (location != nil || ofKind(BaseMultiLoc)) inherited(src, vec); /* look for an individual among the source object's connections */ src.connectionTable().forEachAssoc(function(cur, val) { /* if this is one of our individuals, check it */ if (cur.hasCollectiveGroup(self)) { /* add this individual's paths to the vector */ vec.appendAll(src.getAllPathsTo(cur)); } }); } /* * CollectiveGroup objects are not normally listable in any * situations. Since a collective group is merely a parser stand-in * for its individuals, we don't want it to appear as a separate * object in the game. */ isListedInContents = nil isListedInInventory = nil ; /* * An "itemizing" collective group is like a regular collective group, * but the Examine action itemizes the individual visible items making up * the group. We itemize the individuals instead of showing the 'desc' * for the overall group object, as the basic collective group class * does. */ class ItemizingCollectiveGroup: CollectiveGroup /* * Override the main Examine handling. By default, we'll list the * individuals that are visible, and separately list those that are * being carried by the actor. If none of our individuals are * visible, simply say so. */ mainExamine() { local info; local vis; local carried, here; /* get the visible info table */ info = gActor.visibleInfoTable(); /* get the list of visible individuals */ vis = getVisibleIndividuals(info); /* if any individuals are visible, list them */ if (vis.length() != 0) { /* separate out the individuals being carried */ carried = vis.subset({x: x.isIn(gActor)}); here = vis - carried; /* show the items that are here but not being carried, if any */ if (here.length() != 0) { /* get the room contents lister */ local lister = gActor.location.roomContentsLister; /* get the subset that the room contents lister won't list */ local xlist = here.subset({x: !lister.isListed(x)}); /* show the list through the room contents lister */ lister.showList(gActor, nil, here, 0, 0, info, nil); /* Examine any objects not part of the room description */ foreach (local x in xlist) examineUnlisted(x); /* * if that showed anything, add a paragraph break before * the carried list */ if (xlist.length() != 0 && carried.length() != 0) "<.p>"; } /* separately, show the items being carried, if any */ if (carried.length() != 0) gActor.inventoryLister.showList( gActor, gActor, carried, 0, 0, info, nil); } else { /* * None are visible. If it's dark in the location, simply * say so; otherwise, say that we can't see any of me. */ if (!gActor.isLocationLit()) reportFailure(&tooDarkMsg); else reportFailure(&mustBeVisibleMsg, self); } } /* * Examine an unlisted individual object. This will be called for * each object in the room that's not listable via the room contents * lister. */ examineUnlisted(x) { "<.p>"; nestedAction(Examine, x); } ; /* ------------------------------------------------------------------------ */ /* * A readable object. Any ordinary object will show its normal full * description when read, but an object that is explicitly readable will * have elevated logicalness for the "read" action, and can optionally * show a separate description when read. */ class Readable: Thing /* * Show my special reading desription. By default, we set this to * nil to indicate that we should use our default "examine" * description; objects can override this to show a special message * for reading the object as desired. */ readDesc = nil /* our reading description when obscured */ obscuredReadDesc() { gLibMessages.obscuredReadDesc(self); } /* our reading description in dim light */ dimReadDesc() { gLibMessages.dimReadDesc(self); } /* "Read" action */ dobjFor(Read) { verify() { /* give slight preference to an object being held */ if (!isIn(gActor)) logicalRank(80, 'not held'); } action() { /* * if we have a special reading description defined, show * it; otherwise, use the same handling as "examine" */ if (propType(&readDesc) != TypeNil) { local info; /* * Reading requires a transparent sight path and plenty * of light; in the absence of either of these, we can't * make out the details. */ info = gActor.bestVisualInfo(self); if (info.trans != transparent) obscuredReadDesc; else if (info.ambient < 3) dimReadDesc; else readDesc; } else { /* * we have no special reading description, so use the * default "examine" handling */ actionDobjExamine(); } } } ; /* ------------------------------------------------------------------------ */ /* * A "consultable" object. This is an inanimate object that can be * consulted about various topics, almost the way an actor can be asked * about topics. Examples include individual objects that contain * voluminous information, such as books, phone directories, and maps, as * well as collections of individual information-carrying objects, such * as file cabinets or bookcases. * * A consultable keeps a database of TopicEntry objects; this works in * much the same way as the topic database system that actors use. * Create one or more ConsultTopic objects and place them inside the * Consultable (using the 'location' property, or using the '+' syntax). * When an actor consults the object about a topic, we'll search our * database for a ConsultTopic object that matches the topic and is * currently active, and show the response for the best one we can find. * * From an IF design perspective, consultables have two nice properties. * * First, they hide the boundaries of implementation, by letting the game * *suggest* that there's an untold wealth of information in a particular * book (or whatever) without the need to actually implement all of it. * We only have to show the entries the player specifically asks for, so * the game never has to admit when it's run out of things to show, and * the player can never know for sure that there's not more to find. Be * careful, though, because this is a double-edge sword, design-wise; * it's easy to abuse this property to hide information gratuitously from * the player. * * Second, consultables help "match impedances" between the narrative * level of detail and the underlying world model. At the narrative * level, we paint in fairly broad strokes: when we visit a new location, * we describe the *important* features of the setting, not every last * detail. If the player wants to examine something in closer detail, we * zoom in on that detail, assuming we've implemented it, but it's up to * the player to determine where the attention is focused. Consultable * objects give us the same capability for books and the like. With a * consultable, we can describe the way a book looks without immediately * dumping the literal contents of the book onto the screen; but when the * player chooses some aspect of the book to read in detail, we can zoom * in on that page or chapter and show that literal content, if we * choose. * * Also, note that we assume that consultables convey their information * through visual information, such as printed text or a display screen. * Because of this, we by default require that the object be visible to * be consulted. This might not be appropriate in some cases, such as * Braille books or talking PDA's; to remove the visual condition, * override the pre-condition for the Consult action. */ class Consultable: Thing, TopicDatabase /* * If they consult us without a topic, just ask for a topic. Treat * it as logical, but rank it as improbable, in case there's * anything else around that can be consulted without any topic * specified. */ dobjFor(Consult) { preCond = [touchObj, objVisible] verify() { logicalRank(50, 'need a topic'); } action() { askForTopic(ConsultAbout); } } /* consult about a topic */ dobjFor(ConsultAbout) { verify() { } action() { /* remember that we're the last object the actor consulted */ gActor.noteConsultation(self); /* try handling the topic through our topic database */ if (!handleTopic(gActor, gTopic, consultConvType, nil)) topicNotFound(); } } /* show the default response for a topic we couldn't find */ topicNotFound() { /* * Report the absence of the topic. Note that we use an * ordinary, successful report, not a failure report, because * the consultation really did succeed in the sense of the * physical action of consulting: we successfully flipped * through the book, scanned the file cabinet, or whatever. We * didn't find what we were looking for, but in terms of the * physical action undertaken, we successfully did exactly what * we were asked to do. */ mainReport(&cannotFindTopicMsg); } /* * Resolve the topic phrase for a CONSULT ABOUT command. The CONSULT * ABOUT action refers this to the direct object of the action, so * that the direct object can filter the topic match according to * what makes sense for the consultable. * * By default, we resolve the topic phrase a little differently than * we would for conversational commands, such as ASK ABOUT. By * default, we don't differentiate objects at all based on physical * scope or actor knowledge when deciding on a match for a topic * phrase. For example, if you create a Consultable representing a * phone book, and the player enters a command like FIND BOB IN PHONE * BOOK, the topic BOB will be found even if the 'bob' object isn't * known to the player character. The reason for this difference * from ASK ABOUT et al is that consultables are generally the kinds * of objects where, in real life, a person could browse through the * object and come across entries whether or not the person knew * enough to look for them. For example, you could go through a * phone book and find an entry for "Bob" even if you didn't know * anyone named Bob. * * 'lst' is the list of ResolveInfo objects giving the full set of * matches for the vocabulary words; 'np' is the grammar production * object for the topic phrase; and 'resolver' is the TopicResolver * that's resolving the topic phrase. Note that 'lst' contains * ResolveInfo objects, so to get the game-world object for a given * list entry, use lst[i].obj_. * * We return a ResolvedTopic object that encapsulates the matching * objects. * * Note that the resolver object can be used to get certain useful * information. The resolver's getAction() method returns the action * (which you should use instead of gAction, since this routine is * called during the resolution process, not during command * execution); its getTargetActor() method returns the actor * performing the action; and its objInPhysicalScope(obj) method lets * you determine if an object is in physical scope for the actor. */ resolveConsultTopic(lst, np, resolver) { /* * by default, simply return an undifferentiated list with * everything given equal weight, whether known or not, and * whether in scope or not */ return new ResolvedTopic(lst, [], [], np); } /* * Our topic entry database for consultatation topics. This will be * automatically built during initialization from the set of * ConsultTopic objects located within me, so there's usually no * need to initialize this manually. */ consultTopics = nil ; /* * A consultation topic. You can place one or more of these inside a * Consultable object (using the 'location' property, or the '+' * notation), to create a database of topics that can be looked up in * the consultable. */ class ConsultTopic: TopicMatchTopic /* include in the consultation list */ includeInList = [&consultTopics] /* * don't set any pronouns for the topic - the consultable itself * should be the pronoun antecedent */ setTopicPronouns(fromActor, obj) { } ; /* * A default topic entry for a consultable. You can include one (or * more) of these in a consultable's database to provide a topic of last * resort that answers to any topics that aren't in the database * themselves. */ class DefaultConsultTopic: DefaultTopic includeInList = [&consultTopics] setTopicPronouns(fromActor, obj) { } ; /* ------------------------------------------------------------------------ */ /* * A common, abstract base class for things that cannot be moved. You * shouldn't use this class to create game objects directly; you should * always use one of the concrete subclasses, such as Fixture or * Immovable. This base class doesn't provide the full behavior * necessary to make an object immovable; it's just here as a * programming abstraction for the common elements of all immovable * objects. * * This class has two purposes. First, it defines some behavior common * to all non-portable objects. Second, you can test an object to see * if it's based on this class to determine whether it's a portable or * unportable type of Thing. */ class NonPortable: Thing /* * An immovable objects is not listed in room or container contents * listings. Since the object is immovable, it's in effect a * permanent feature of its location, so it should be described as * such: either directly as part of its location's description text, * or via its own specialDesc. */ isListed = nil isListedInContents = nil isListedInInventory = nil /* * By default, if the object's contents would be listed in a direct * examination, then also list them when showing an inventory list, * or describing the enclosing room or an enclosing object. */ contentsListed = (contentsListedInExamine) /* * Are my contents within a fixed item that is within the given * location? Since we're fixed in place, our contents are certainly * within a fixed item, so we merely need to check if we're fixed in * place within the given location. We are if we're in the given * location or we ourselves are fixed in place in the given location. */ contentsInFixedIn(loc) { return isDirectlyIn(loc) || isInFixedIn(loc); } /* * Since non-portables aren't carried, their weight and bulk are * largely irrelevant. Even so, when a non-portable is a component * of another object, or otherwise contained in another object, its * weight and/or bulk can affect the behavior of the parent object. * So, it's simplest to use a default of zero for these so that there * are no surprises about the parent's behavior. */ weight = 0 bulk = 0 /* * Non-portable objects can't be held, since they can't be carried. * However, in some cases, it's useful to include non-portable * objects within an actor, such as when creating component parts of * an actor (hands, say). In these cases, the non-portables aren't * held, but rather are components or similar. */ isHeldBy(actor) { return nil; } /* * We're not being held, but if our location is an actor, then we're * as good as held because we're effectively part of the actor. */ meetsObjHeld(actor) { return actor == location; } /* * showing an immovable to someone simply requires that it be in * sight: we're not holding it up to show it, we're simply pointing * it out */ dobjFor(ShowTo) { preCond = [objVisible] } /* * Thing decreases the likelihood that we want to examine an object * when the object isn't being held. That's fine for portable * objects, but nonportables can never be held, so we don't want that * decrease in logicalness. */ dobjFor(Examine) { /* override Thing's likelihood downgrade for un-held items */ verify() { } } ; /* ------------------------------------------------------------------------ */ /* * A "fixture," which is something that's obviously a part of the room. * These objects cannot be removed from their containers. This class is * meant for permanent features of rooms that obviously cannot be moved * to a new container, such as walls, floors, doors, built-in bookcases, * light switches, buildings, and the like. * * The important feature of a Fixture is that it's *obvious* that it's * part of its container, so it should be safe to assume that a character * normally wouldn't even try to take it or move it. For objects that * might appear portable but turn out to be immovable, other classes are * more appropriate: use Heavy for objects that are immovable simply * because they're very heavy, for example, or Immovable for objects that * are immovable for some non-obvious reason. */ class Fixture: NonPortable /* * Hide fixtures from "all" for certain commands. Fixtures are * obviously part of the location, so a reaonable person wouldn't * even consider trying to do things like take them or move them. */ hideFromAll(action) { return (action.ofKind(TakeAction) || action.ofKind(DropAction) || action.ofKind(PutInAction) || action.ofKind(PutOnAction)); } /* don't hide from defaults, though */ hideFromDefault(action) { return nil; } /* a fixed item can't be moved by an actor action */ verifyMoveTo(newLoc) { /* it's never possible to do this */ illogical(cannotMoveMsg); } /* * a fixed item can't be taken - this would be caught by * verifyMoveTo anyway, but provide a more explicit message when a * fixed item is explicitly taken */ dobjFor(Take) { verify() { illogical(cannotTakeMsg); }} dobjFor(TakeFrom) { verify() { illogical(cannotTakeMsg); }} /* fixed objects can't be put anywhere */ dobjFor(PutIn) { verify() { illogical(cannotPutMsg); }} dobjFor(PutOn) { verify() { illogical(cannotPutMsg); }} dobjFor(PutUnder) { verify() { illogical(cannotPutMsg); }} dobjFor(PutBehind) { verify() { illogical(cannotPutMsg); }} /* fixed objects can't be pushed, pulled, or moved */ dobjFor(Push) { verify() { illogical(cannotMoveMsg); }} dobjFor(Pull) { verify() { illogical(cannotMoveMsg); }} dobjFor(Move) { verify() { illogical(cannotMoveMsg); }} dobjFor(MoveWith) { verify() { illogical(cannotMoveMsg); }} dobjFor(MoveTo) { verify() { illogical(cannotMoveMsg); }} dobjFor(PushTravel) { verify() { illogical(cannotMoveMsg); }} dobjFor(ThrowAt) { verify() { illogical(cannotMoveMsg); }} dobjFor(ThrowDir) { verify() { illogical(cannotMoveMsg); }} /* * The messages to use for illogical messages. These can be * overridden with new properties (of playerActionMessages and the * like), or simply with single-quoted strings to display. */ cannotTakeMsg = &cannotTakeFixtureMsg cannotMoveMsg = &cannotMoveFixtureMsg cannotPutMsg = &cannotPutFixtureMsg /* * A component can be said to be owned by its location's owner or by * its location. */ isOwnedBy(obj) { /* * if I'm owned by the object under the normal rules, then we * won't say otherwise */ if (inherited(obj)) return true; /* * we can be said to be owned by our location, since we're a * direct and permanent part of the location */ if (obj == location) return true; /* * if my location is owned by the given object, consider * ourselves owned by it as well, as we're an extension of our * location */ if (location != nil && location.isOwnedBy(obj)) return true; /* we didn't find anything that establishes ownership */ return nil; } ; /* * A component object. These objects cannot be removed from their * containers because they are permanent features of other objects, which * may themselves be portable: the hands of a watch, a tuning dial on a * radio. This class behaves essentially the same way as Fixture, but * its messages are more suitable for objects that are component parts of * other objects rather than fixed features of rooms. */ class Component: Fixture /* a component cannot be removed from its container by an actor action */ verifyMoveTo(newLoc) { /* it's never possible to do this */ illogical(&cannotMoveComponentMsg, location); } /* * Hide components from EXAMINE ALL, as well as any commands hidden * from ALL for ordinary fixtures. Components are small parts of * larger objects, so when we EXAMINE ALL, it's enough to examine the * larger objects of which we're a part; we don't want components to * show up separately in these cases. */ hideFromAll(action) { /* hide from EXAMINE ALL, plus anything the base class hides */ return (action.ofKind(ExamineAction) || inherited(action)); } /* * We are a component of our direct cotnainer, and we're indirectly a * component of anything that it's a component of. */ isComponentOf(obj) { return (obj == location || (location != nil && location.isComponentOf(obj))); } /* * Consider ourself to be held by the given actor if we're a * component of the actor. */ meetsObjHeld(actor) { return isComponentOf(actor); } /* a component cannot be taken separately */ dobjFor(Take) { verify() { illogical(&cannotTakeComponentMsg, location); }} dobjFor(TakeFrom) { verify() { illogical(&cannotTakeComponentMsg, location); }} /* a component cannot be separately put somewhere */ dobjFor(PutIn) { verify() { illogical(&cannotPutComponentMsg, location); }} dobjFor(PutOn) { verify() { illogical(&cannotPutComponentMsg, location); }} dobjFor(PutUnder) { verify() { illogical(&cannotPutComponentMsg, location); }} dobjFor(PutBehind) { verify() { illogical(&cannotPutComponentMsg, location); }} ; /* * A "secret fixture" is a kind of fixture that we use for internal * implementation purposes, and which we don't intend to be visible to * the player. Objects of this type usually have no vocabulary, since we * don't want the player to be able to refer to them. */ class SecretFixture: Fixture /* * this kind of object is internal to the game's implementation, so * we don't want it to show up in "all" lists */ hideFromAll(action) { return true; } ; /* * A fixture that uses the same custom message for taking, moving, and * putting. In many cases, it's useful to customize the message for a * fixture, using the same custom message for all sorts of moving. Just * override cannotTakeMsg, and the other messages will copy it. */ class CustomFixture: Fixture cannotMoveMsg = (cannotTakeMsg) cannotPutMsg = (cannotTakeMsg) ; /* ------------------------------------------------------------------------ */ /* * An Immovable is an object that can't be moved, but not because it's * obviously a fixture or component of another object. This class is * suitable for things like furniture, which are in principle portable * but which actors aren't actually allowed to pick up or move around. * * Note that Immovable is a lot like Fixture. The difference is that * Fixture is for objects that are *obviously* fixed in place by their * very nature, whereas Immovable is for objects that common sense would * tell us are portable, but which the game doesn't in fact allow the * player to move. * * The practical difference between Immovable and Fixture is that Fixture * considers taking or moving to be illogical actions, whereas Immovable * considers these actions logical but simply doesn't allow them. To be * more specific, Fixture disallows taking and moving in the verify() * methods for those actions, while Immovable disallows the actions in * the check() methods. This means, for example, that Fixture objects * will be removed from consideration during the noun resolution phase * when there are more logical choices. */ class Immovable: NonPortable /* an Immovable can't be taken */ dobjFor(Take) { check() { failCheck(cannotTakeMsg); }} /* Immovables can't be put anywhere */ dobjFor(PutIn) { check() { failCheck(cannotPutMsg); }} dobjFor(PutOn) { check() { failCheck(cannotPutMsg); }} dobjFor(PutUnder) { check() { failCheck(cannotPutMsg); }} dobjFor(PutBehind) { check() { failCheck(cannotPutMsg); }} /* Immovables can't be pushed, pulled, or otherwise moved */ dobjFor(Drop) { action() { reportFailure(cannotMoveMsg); }} dobjFor(Push) { action() { reportFailure(cannotMoveMsg); }} dobjFor(Pull) { action() { reportFailure(cannotMoveMsg); }} dobjFor(Move) { action() { reportFailure(cannotMoveMsg); }} dobjFor(MoveWith) { check() { failCheck(cannotMoveMsg); }} dobjFor(MoveTo) { check() { failCheck(cannotMoveMsg); }} dobjFor(PushTravel) { action() { reportFailure(cannotMoveMsg); }} dobjFor(ThrowAt) { verify() { illogical(cannotMoveMsg); }} dobjFor(ThrowDir) { verify() { illogical(cannotMoveMsg); }} dobjFor(Turn) { verify() { logicalRank(50, 'turn heavy'); } action() { reportFailure(cannotMoveMsg); } } /* * The messages to use for the failure messages. These can be * overridden with new properties (of playerActionMessages and the * like), or simply with single-quoted strings to display. */ cannotTakeMsg = &cannotTakeImmovableMsg cannotMoveMsg = &cannotMoveImmovableMsg cannotPutMsg = &cannotPutImmovableMsg ; /* * An immovable that uses the same custom message for taking, moving, and * putting. In many cases, it's useful to customize the message for an * immovable, using the same custom message for all sorts of moving. * Just override cannotTakeMsg, and the other messages will copy it. */ class CustomImmovable: Immovable cannotMoveMsg = (cannotTakeMsg) cannotPutMsg = (cannotTakeMsg) ; /* * Heavy: an object that's immovable because it's very heavy. This is * suitable for things like large boulders, heavy furniture, or the like: * things that aren't nailed down, but nonetheless are too heavy to be * carried or otherwise move. * * This is a simple specialization of Immovable; the only thing we change * is the messages we use to describe why the object can't be moved. */ class Heavy: Immovable cannotTakeMsg = &cannotTakeHeavyMsg cannotMoveMsg = &cannotMoveHeavyMsg cannotPutMsg = &cannotPutHeavyMsg ; /* ------------------------------------------------------------------------ */ /* * Decoration. This is an object that is included for scenery value but * which has no other purpose, and which the author wants to make clear * is not important. We use the catch-all action routine to respond to * any command on this object with a flat "that's not important" * message, so that the player can plainly see that there's no point * wasting any time trying to manipulate this object. * * We use the "default" catch-all verb verify handling to report our * "that's not important" message, so a decoration can be made * responsive to specific verbs simply by defining an action handler for * those verbs. */ class Decoration: Fixture /* don't include decorations in 'all' */ hideFromAll(action) { return true; } /* don't hide from defaults */ hideFromDefault(action) { return nil; } /* * use the default response "this object isn't important" when we're * used as either a direct or indirect object */ dobjFor(Default) { verify() { illogical(notImportantMsg, self); } } iobjFor(Default) { verify() { illogical(notImportantMsg, self); } } /* use the standard not-important message for decorations */ notImportantMsg = &decorationNotImportantMsg /* * The catch-all Default verifier makes all actions illogical, but we * can override this to allow specific actions by explicitly defining * them here so that they hide the Default verify handlers. In * addition, give decorations a reduced logical rank, so that any * in-scope non-decoration object with similar vocabulary will be * matched for an Examine command ahead of a decoration. */ dobjFor(Examine) { verify() { inherited(); logicalRank(70, 'decoration'); } } /* * likewise for LISTEN TO and SMELL, which are the auditory and * olfactory equivalents of EXAMINE */ dobjFor(ListenTo) { verify() { inherited(); logicalRank(70, 'decoration'); } } dobjFor(Smell) { verify() { inherited(); logicalRank(70, 'decoration'); } } /* likewise for READ */ dobjFor(Read) { verify() { inherited(); logicalRank(70, 'decoration'); } } /* likewise for LOOK IN and SEARCH */ dobjFor(LookIn) { verify() { inherited(); logicalRank(70, 'decoration'); } } dobjFor(Search) { verify() { inherited(); logicalRank(70, 'decoration'); } } /* the default LOOK IN response is our standard "that's not important" */ lookInDesc { mainReport(¬ImportantMsg, self); } ; /* ------------------------------------------------------------------------ */ /* * An "unthing" is an object that represents the *absence* of an object. * It's occasionally useful to respond specially when the player mentions * an object that isn't present, especially when the player is likely to * assume that something is present. * * An unthing is essentially a decoration, but we use a customized * message that says "that isn't here" rather than "that isn't * important". */ class Unthing: Decoration /* * The message to display when the player refers to this object. * This can be a library message property, or a single-quoted string. * This message will probably always be overridden in practice, since * the point of this class is to provide a more specific explanation * of why the object isn't here. */ notHereMsg = &unthingNotHereMsg /* an Unthing shouldn't be picked as a default */ hideFromDefault(action) { return true; } /* * by default, use our 'not here' message for our descriptions (in * all of the standard senses) */ basicExamine() { mainReport(notHereMsg, self); } basicExamineListen(explicit) { if (explicit) mainReport(notHereMsg, self); } basicExamineSmell(explicit) { if (explicit) mainReport(notHereMsg, self); } /* use our custom message for the inherited Decoration responses */ notImportantMsg = (notHereMsg) /* * Because we're not actually here, use custom error messages when * we're used as a possessive or locational qualifier. The standard * messages say things like "Bob doesn't appear to have that" or "You * don't see that in the box," but these don't make sense for an * Unthing - we're not actually here, so we can't "appear" or "seem" * to own or contain anything. Instead, we need to indicate that the * qualifying object itself (i.e., 'self') isn't here at all. */ throwNoMatchForPossessive(txt) { throwUnthingAsQualifier(); } throwNoMatchForLocation(txt) { throwUnthingAsQualifier(); } throwNothingInLocation() { throwUnthingAsQualifier(); } /* * throw a generic message when we're used as a qualifier - we'll * simply get our "not here" message and display that */ throwUnthingAsQualifier() { local msg; /* * resolve our "not here" message to a string - we need to do * this here, since we're too early in the parsing sequence for * the normal "mainResult" type of processing */ msg = MessageResult.resolveMessageText([self], ¬HereMsg, [self]); /* throw a parser exception that will display this literal text */ throw new ParseFailureException(&parserErrorString, msg); } /* * if there's anything at all in a resolve list other than me, always * remove me */ filterResolveList(lst, action, whichObj, np, requiredNum) { /* if the list has anything else in it, remove myself */ if (lst.length() != 1) lst = lst.removeElementAt(lst.indexWhich({x: x.obj_ == self})); /* return the list */ return lst; } /* * trying to given an order to an Unthing acts the same way as any * other kind of interaction */ acceptCommand(issuingActor) { mainReport(notHereMsg, self); } ; /* ------------------------------------------------------------------------ */ /* * Distant item. This is an object that's too far away to manipulate, * but can be seen. This is useful for scenery objects that are at a * great distance within a large location. * * A Distant item is essentially just like a decoration, but the default * message is different. Note that this class is based on Fixture, which * means that it should be *obvious* that the object is too far away to * take or move. */ class Distant: Fixture /* don't include in 'all' */ hideFromAll(action) { return true; } dobjFor(Default) { verify() { illogical(&tooDistantMsg, self); } } iobjFor(Default) { verify() { illogical(&tooDistantMsg, self); } } /* * Explicitly allow examining and listening to a Distant item. To * do this, override the 'verify' methods explicitly; we only need * to inherit the base class handling, but we need to explicitly do * so to 'override' the catch-all default handlers. */ dobjFor(Examine) { verify { inherited() ; } } dobjFor(ListenTo) { verify() { inherited(); } } /* similarly, allow showing a distant item */ dobjFor(ShowTo) { verify() { inherited(); } } ; /* * Out Of Reach - this is a special mix-in that can be used to create an * object that places its *contents* out of reach under customizable * conditions, and can optionally place itself out of reach as well. */ class OutOfReach: object checkTouchViaPath(obj, dest, op) { /* check how we're traversing the object */ if (op == PathTo) { /* * we're reaching from outside for this object itself - * check to see if the source can reach me */ if (!canObjReachSelf(obj)) return new CheckStatusFailure( cannotReachFromOutsideMsg(dest), dest); } else if (op == PathIn) { /* * we're reaching in to touch one of my contents - check to * see if the source object is within reach of my contents */ if (!canObjReachContents(obj)) return new CheckStatusFailure( cannotReachFromOutsideMsg(dest), dest); } else if (op == PathOut) { local ok; /* * We're reaching out. If we're reaching for the object * itself, check to see if we're reachable from within; * otherwise, check to see if we can reach objects outside * us from within. */ if (dest == self) ok = canReachSelfFromInside(obj); else ok = canReachFromInside(obj, dest); /* if we can't reach the object, say so */ if (!ok) return new CheckStatusFailure( cannotReachFromInsideMsg(dest), dest); } /* if we didn't find a problem, allow the operation */ return checkStatusSuccess; } /* * The message to use to indicate that we can't reach an object, * because the actor is outside me and the target is inside, or vice * versa. Each of these can return a property ID giving an actor * action message property, or can simply return a string with the * message text. */ cannotReachFromOutsideMsg(dest) { return &tooDistantMsg; } cannotReachFromInsideMsg(dest) { return &tooDistantMsg; } /* * Determine if the given object can reach my contents. 'obj' is * the object (usually an actor) attempting to reach my contents * from outside of me. * * By default, we'll return nil, so that nothing within me can be * reached from anyone outside. This can be overridden to allow my * contents to become reachable from some external locations but not * others; for example, a high shelf could allow an actor standing * on a chair to reach my contents. */ canObjReachContents(obj) { return nil; } /* * Determine if the given object can reach me. 'obj' is the object * (usually an actor) attempting to reach this object. * * By default, make this object subject to the same rules as its * contents. */ canObjReachSelf(obj) { return canObjReachContents(obj); } /* * Determine if the given object outside of me is reachable from * within me. 'obj' (usually an actor) is attempting to reach * 'dest'. * * By default, we return nil, so nothing outside of me is reachable * from within me. This can be overridden as needed. This should * usually behave symmetrically with canObjReachContents(). */ canReachFromInside(obj, dest) { return nil; } /* * Determine if we can reach this object itself from within. This * is used when 'obj' tries to touch this object when 'obj' is * located within this object. * * By default, we we use the same rules as we use to reach an * external object from within. */ canReachSelfFromInside(obj) { return canReachFromInside(obj, self); } /* * We cannot implicitly remove this obstruction, so simply return * nil when asked. */ tryImplicitRemoveObstructor(sense, obj) { return nil; } ; /* ------------------------------------------------------------------------ */ /* * A Fill Medium - this is the class of object returned from * Thing.fillMedium(). */ class FillMedium: Thing /* * Get the transparency sensing through this medium. */ senseThru(sense) { /* * if I have a meterial, use its transparency; otherwise, we're * transparent */ return (material != nil ? material.senseThru(sense) : transparent); } /* my material */ material = nil ; /* ------------------------------------------------------------------------ */ /* * Base multi-location item with automatic initialization. This is the * base class for various multi-located object classes. * * We provide four ways of initializing a multi-located object's set of * locations. * * First, the object can simply enumerate the locations explicitly, by * setting the 'locationList' property to the list of locations. * * Second, the object can indicate that it's located in every object of a * given class, by setting the 'initialLocationClass' property to the * desired class. * * Third, the object can define a rule that specifies which objects are * its initial locations, by defining the 'isInitiallyIn(obj)' method to * return true if 'obj' is an initial location, nil if not. This can be * combined with the 'initialLocationClass' mechanism: if * 'initialLocationClass' is non-nil, then only objects of the given * class will be tested with 'isInitiallyIn()'; if 'initialLocationClass' * is nil, then every object in the entire game will be tested. * * Fourth, you can override the method buildLocationList() to build an * return the initial list of locations. You can use this approach if * you have a complex set of rules for determining the initial location * list, and none of the above approaches are flexible enough to * implement it. If you override buildLocationList(), simply compute and * return the list of initial locations; the library will automatically * call the method during pre-initialization. * * If you don't define any of these, then the object simply has no * initial locations by default. */ class BaseMultiLoc: object /* * The location list. Instances can override this to manually * enumerate our initial locations. By default, we'll call * buildLocationList() the first time this is invoked, and store the * result. */ locationList = perInstance(buildLocationList()) /* * The class of our initial locations. If this is nil, then our * default buildLocationList() method will test every object in the * entire game with our isInitiallyIn() method; otherwise, we'll test * only objects of the given class. */ initialLocationClass = nil /* * Test an object for inclusion in our initial location list. By * default, we'll simply return true to include every object. We * return true by default so that an instance can merely specify a * value for initialLocationClass in order to place this object in * every instance of the given class. */ isInitiallyIn(obj) { return true; } /* * Build my list of locations, and return the list. This default * implementation looks for an 'initialLocationClass' property value, * and if one is found, looks at every object of that class; * otherwise, it looks at every object in the entire game. In either * case, each object is then passed to our isInitiallyIn() method, * and is included in our result list if isInitiallyIn() returns * true. */ buildLocationList() { /* * If the object doesn't define any of the standard rules, which * it would do by overriding initialLocationClass and/or * isInitiallyIn(), then simply return an empty list. We take * the absence of overrides for any of the rules to mean that the * object simply has no initial locations. */ if (initialLocationClass == nil && !overrides(self, BaseMultiLoc, &isInitiallyIn)) return []; /* start with an empty list */ local lst = new Vector(16); /* * if initialLocationClass is defined, loop over all objects of * that class; otherwise, loop over all objects */ if (initialLocationClass != nil) { /* loop over all instances of the given class */ for (local obj = firstObj(initialLocationClass) ; obj != nil ; obj = nextObj(obj, initialLocationClass)) { /* if the object passes the test, include it */ if (isInitiallyIn(obj)) lst.append(obj); } } else { /* loop over all objects */ for (local obj = firstObj() ; obj != nil ; obj = nextObj(obj)) { /* if the object passes the test, include it */ if (isInitiallyIn(obj)) lst.append(obj); } } /* return the list of locations */ return lst.toList(); } /* determine if I'm in a given object, directly or indirectly */ isIn(obj) { /* first, check to see if I'm directly in the given object */ if (isDirectlyIn(obj)) return true; /* * Look at each object in my location list. For each location * object, if the location is within the object, I'm within the * object. */ return locationList.indexWhich({loc: loc.isIn(obj)}) != nil; } /* determine if I'm directly in the given object */ isDirectlyIn(obj) { /* * we're directly in the given object only if the object is in * my list of immediate locations */ return (locationList.indexOf(obj) != nil); } /* * Determine if I'm to be listed within my immediate container. As a * multi-location object, we have multiple immediate containers, so * we need to know which direct container we're talking about. * Thing.examineListContents() passes this down via "cont:", a named * parameter. Other callers might not always provide this argument, * though, so if it's not present simply base this on whether we have * a special description in any context. */ isListedInContents(examinee:?) { return (examinee != nil ? !useSpecialDescInContents(examinee) : !useSpecialDesc()); } /* Am I either inside 'obj', or equal to 'obj'? */ isOrIsIn(obj) { return self == obj || isIn(obj); } ; /* ------------------------------------------------------------------------ */ /* * MultiLoc: this class can be multiply inherited by any object that * must exist in more than one place at a time. To use this class, put * it BEFORE Thing (or any subclass of Thing) in the object's superclass * list, to ensure that we override the default containment * implementation for the object. * * Note that a MultiLoc object appears *in its entirety* in each of its * locations. This means that MultiLoc is most suitable for a couple of * specific situations: * * - several locations overlap slightly so that they include a common * object: a large statue at the center of a public square, for example; * * - an object forms a sense connection among its location: a window; * * - a distant object that is seen in its entirety from several * locations: the moon, say, or a mountain range. * * Note that MultiLoc is NOT suitable for cases where an object spans * several locations but isn't contained entirely in any one of them: * it's not good for something like a rope or a river, for example. * MultiLoc also isn't good for cases where you simply want to avoid * creating a bunch of repeated decorations in different locations. * MultiLoc isn't good for these cases because a MultiLoc is treated as * though it exists ENTIRELY and SIMULTANEOUSLY in all of its locations, * which means that all of its sense information and internal state is * shared among all of its locations. * * MultiInstance is better than MultiLoc for cases where you want to * share a decoration object across several locations. MultiInstance is * better because it creates individual copies of the object in the * different locations, so each copy has its own separate sense * information and its own separate identity. * * MultiFaceted is better for objects that span several locations, such * as a river or a long rope. Like MultiInstance, MultiFaceted creates * a separate copy in each location; in addition, MultiFaceted relates * the copies together as "facets" of the same object, so that the * parser knows they're all actually parts of one larger object. */ class MultiLoc: BaseMultiLoc /* * Initialize my location's contents list - add myself to my * container during initialization */ initializeLocation() { /* add myself to each of my container's contents lists */ locationList.forEach({loc: loc.addToContents(self)}); } /* * Re-initialize the location list. This calls buildLocationList() * to re-evaluate the location rules, then updates the locationList * to match the new results. We'll remove the MultiLoc from any old * locations that are no longer part of the location list, and we'll * add it to any new locations that weren't previously in the * location list. You can call this at any time to update the * MutliLoc's presence to reflect applying our location rules to the * current game state. * * Note that this doesn't trigger any moveInto notifications. This * routine is a re-initialization rather than an in-game action, so * it's not meant to behave as though an actor in the game were * walking around moving the MultiLoc around; thus no notifications * are sent. Note also that we attempt to minimize our work by * computing the "delta" from the old state - hence we only move the * MultiLoc into containers it wasn't in previously, and we only * remove it from existing containers that it's no longer in. */ reInitializeLocation() { local newList; /* build the new location list */ newList = buildLocationList(); /* * Update any containers that are not in the intersection of the * two lists. Note that we don't simply move ourselves out of * the old list and into the new list, because the two lists * could have common members; to avoid unnecessary work that * might result from removing ourselves from a container and * then adding ourselves right back in to the same container, we * only notify containers when we're actually moving out or * moving in. */ /* * For each item in the old list, if it's not in the new list, * notify the old container that we're being removed. */ foreach (local loc in locationList) { /* if it's not in the new list, remove me from the container */ if (newList.indexOf(loc) == nil) loc.removeFromContents(self); } /* * for each item in the new list, if we weren't already in this * location, add ourselves to the location */ foreach (local loc in newList) { /* if it's not in the old list, add me to the new container */ if (!isDirectlyIn(loc) == nil) loc.addToContents(self); } /* make the new location list current */ locationList = newList; } /* * Note that we don't need to override any of the contents * management methods, since we provide special handling for our * location relationships, not for our contents relationships. */ /* save my location for later restoration */ saveLocation() { /* return my list of locations */ return locationList; } /* restore a previously saved location */ restoreLocation(oldLoc) { /* remove myself from each current location not in the saved list */ foreach (local cur in locationList) { /* * if this present location isn't in the saved list, remove * myself from the location */ if (oldLoc.indexOf(cur) == nil) cur.removeFromContents(self); } /* add myself to each saved location not in the current list */ foreach (local cur in oldLoc) { /* if I'm not already in this location, add me to it */ if (locationList.indexOf(cur) == nil) cur.addToContents(self); } /* set my own list to the original list */ locationList = oldLoc; } /* * Basic routine to move this object into a given single container. * Removes the object from all of its other containers. Performs no * notifications. */ baseMoveInto(newContainer) { /* remove myself from all of my current contents */ locationList.forEach({loc: loc.removeFromContents(self)}); /* set my location list to include only the new location */ if (newContainer != nil) { /* set my new location */ locationList = [newContainer]; /* add myself to my new container's contents */ newContainer.addToContents(self); } else { /* we have no new locations */ locationList = []; } } /* * Add this object to a new location - base version that performs no * notifications. */ baseMoveIntoAdd(newContainer) { /* add the new container to my list of locations */ locationList += newContainer; /* add myself to my new container's contents */ newContainer.addToContents(self); } /* * Add this object to a new location. */ moveIntoAdd(newContainer) { /* notify my new container that I'm about to be added */ if (newContainer != nil) newContainer.sendNotifyInsert(self, newContainer, ¬ifyInsert); /* perform base move-into-add operation */ baseMoveIntoAdd(newContainer); /* note that I've been moved */ moved = true; } /* * Base routine to move myself out of a given container. Performs * no notifications. */ baseMoveOutOf(cont) { /* remove myself from this container's contents list */ cont.removeFromContents(self); /* remove this container from my location list */ locationList -= cont; } /* * Remove myself from a given container, leaving myself in any other * containers. */ moveOutOf(cont) { /* if I'm not actually directly in this container, do nothing */ if (!isDirectlyIn(cont)) return; /* * notify this container (and only this container) that we're * being removed from it */ cont.sendNotifyRemove(obj, nil, ¬ifyRemove); /* perform base operation */ baseMoveOutOf(cont); /* note that I've been moved */ moved = true; } /* * Call a function on each container. We'll invoke the function as * follows for each container 'cont': * * (func)(cont, args...) */ forEachContainer(func, [args]) { /* call the function for each location in our list */ foreach(local cur in locationList) (func)(cur, args...); } /* * Call a function on each connected container. By default, we * don't connect our containers for sense purposes, so we do nothing * here. */ forEachConnectedContainer(func, ...) { } /* * get a list of my connected containers; by default, we don't * connect our containers, so this is an empty list */ getConnectedContainers = [] /* * Clone this object's contents for inclusion in a MultiInstance's * contents tree. A MultiLoc is capable of being in multiple places * at once, so we can just use our original contents tree as is. */ cloneMultiInstanceContents(loc) { } /* * Create a clone of this object for inclusion in a MultiInstance's * contents tree. We don't actually need to make a copy of the * object, because a MultiLoc can be in several locations * simultaneously; all we need to do is add ourselves to the new * location. */ cloneForMultiInstanceContents(loc) { /* add myself into the new container */ baseMoveIntoAdd(loc); } /* * Add the direct containment connections for this item to a lookup * table. * * A MultiLoc does not, by default, connect its multiple locations * together. This means that if we're traversing in from a point of * view outside the MultiLoc object, we don't add any of our other * containers to the connection table. However, the MultiLoc * itself, and its contents, *can* see out to all of its locations; * so if we're traversing from a point of view inside self, we will * add all of our containers to the connection list. */ addDirectConnections(tab) { /* add myself */ tab[self] = true; /* add my CollectiveGroup objects */ foreach (local cur in collectiveGroups) tab[cur] = true; /* add my contents */ foreach (local cur in contents) { if (tab[cur] == nil) cur.addDirectConnections(tab); } /* * If we're traversing from the outside in, don't connect any of * our other containers. However, if we're traversing from our * own point of view, or from a point of view inside us, we do * get to see out to all of our containers. */ if (senseTmp.pointOfView == self || senseTmp.pointOfView.isIn(self)) { /* add my locations */ foreach (local cur in locationList) { if (tab[cur] == nil) cur.addDirectConnections(tab); } } } /* * Transmit ambient energy to my location or locations. Note that * even though we don't by default shine light from one of our * containers to another, we still shine light from within me to * each of our containers. */ shineOnLoc(sense, ambient, fill) { /* shine on each of my containers and their immediate children */ foreach (local cur in locationList) cur.shineFromWithin(self, sense, ambient, fill); } /* * Build a sense path to my location or locations. Note that even * though we don't by default connect our different containers * together, we still build a sense path from within to outside, * because we can see from within out to all of our containers. */ sensePathToLoc(sense, trans, obs, fill) { /* build a path to each of my containers and their children */ foreach (local cur in locationList) cur.sensePathFromWithin(self, sense, trans, obs, fill); } /* * Get the drop destination. The default implementation in Thing * won't work for us, because it delegates to its location to find * the drop destination; we can't do that because we could have * several locations. To figure out which of our multiple locations * to delegate to, we'll look for 'self' in the supplied sense path; * if we can find it, and the previous path element is a container or * peer of ours, then we'll delegate to that container, because it's * the "side" we approached from. If there's no path, or if we're * not preceded in the path by a container of ours, we'll arbitrarily * delegate to our first container. * * Note that when we don't have a path, or there's no container of * ours preceding us in the path, the object being dropped must be * starting inside us. It would be highly unusual for this to happen * with a multi-location object, because MutliLoc isn't designed for * use as a "nested room" or the like. However, it's not an * impossible situation; if the game does want to create such a * scenario, then the game simply needs to override this routine so * that it does whatever makes sense in the game scenario. There's * no general way to handle such situations, but it should be * possible to determine the correct handling for specific scenarios. */ getDropDestination(obj, path) { local idx; /* * if there's no path, get the ordinary "touch" path from the * current actor to us, since this is how the actor would reach * out and touch this object */ if (path == nil) path = gActor.getTouchPathTo(self); /* * if there's a path, check to see if we're in it; if so, and * we're not the first element, and the preceding element is a * container or peer of ours, delegate to the preceding element */ if (path != nil && (idx = path.indexOf(self)) != nil && idx >= 3 && path[idx - 1] is in (PathIn, PathPeer)) { /* * we're preceded in the path by a container or peer of ours, * so we know that we're approaching from that "side" - * delegate to that container, since we're coming from that * direction */ return path[idx - 2].getDropDestination(obj, path); } /* * We either don't have a path, or we're not preceded in the path * by one of our containers or peers, so we don't have any idea * which "side" we're approaching from. This means we have no * good basis for deciding where the object being dropped will * fall. Arbitrarily delegate to our first container, if we have * one. */ return locationList.length() > 0 ? locationList[1].getDropDestination(obj, path) : nil; } ; /* ------------------------------------------------------------------------ */ /* * A "multi-instance" object is a simple way of creating copies of an * object in several places. This is often useful for decorations and * other features that recur in a whole group of rooms. * * You define a multi-instance object in two parts. * * First, you define a MultiInstance object, which is just a hollow * shell of an object that sets up the location relationships. This * shell object doesn't have any presence in the game world; it's just a * programming abstraction. * * Second, as part of the shell object, you define an example of the * object that will actually show up in the game in each of the multiple * locations. You do this by defining a nested object under the * 'instanceObject' property of the shell object. This is otherwise a * perfectly ordinary object. In most cases, you'll want to make this a * Decoration, Fixture, or some other non-portable object class, since * the "cloned" nature of these objects means that you usually won't * want them moving around (if they did, you might run into situations * where you had several of them in the same place, leading to * disambiguation headaches for the player). * * Here's an example of how you set up a multi-instance object: * * trees: MultiInstance *. locationList = [forest1, forest2, forest3] *. instanceObject: Fixture { 'tree/trees' 'trees' *. "Many tall, old trees grow here. " *. isPlural = true *. } *. ; * * Note that the instanceObject itself has no location, because it * doesn't appear in the game-world model itself - it's just a template * for the real objects. * * During initialization, the library will automatically create several * instances (i.e., subclasses) of the example object - one instance per * location, to be exact. These instances are the real objects that * show up in the game world. * * MultiInstance has one more helpful feature: it lets you dynamically * change the set of locations where the instances appear. You do this * using the same interface that you use to move around MultiLoc objects * - moveInto(), moveIntoAdd(), moveOutOf(). When you call these * routines on the MultiInstance shell object, it will add and remove * object instances as needed to keep everything consistent. Thanks to * a little manipulation we do on the instance objects when we set them up, * you can also move the instance objects around directly using * moveInto(), and they'll update the MultiInstance parent to keep its * location list consistent. */ class MultiInstance: BaseMultiLoc /* the template object */ instanceObject = nil /* initialize my locations */ initializeLocation() { /* create a copy of our template object for each of our locations */ locationList.forEach({loc: addInstance(loc)}); } /* * Move the MultiInstance into the given location. This removes us * from any other existing locations and adds us (if we're not * already there) to the given location. */ moveInto(loc) { /* remove all instances that aren't in the new location */ foreach (local cur in instanceList) { /* if this instance isn't directly in 'loc', remove it */ if (!cur.isDirectlyIn(loc)) cur.moveInto(nil); } /* * If I don't have an instance object in the new location, add * one. Since I've dropped every other instance already, we * either have exactly one location now, which is in the new * location, or we have no locations at all; so we need only * check to see if we have any instances and add one in the new * location if not. */ if (loc != nil && locationList.length() == 0) addInstance(loc); } /* * Add the new location to our set of locations. Any existing * locations are unaffected. */ moveIntoAdd(loc) { /* if I'm not already in the location, add an instance there */ if (locationList.indexOf(loc) == nil) addInstance(loc); } /* * Remove me from the given location. Other locations are * unaffected. */ moveOutOf(loc) { local inst; /* find our instance that's in the given location */ inst = getInstanceIn(loc); /* if we found it, remove this instance from its location */ if (inst != nil) inst.moveInto(nil); } /* get our instance object (if any) that's in the given location */ getInstanceIn(loc) { return instanceList.valWhich({x: x.isDirectlyIn(loc)}); } /* internal service routine - add an instance for a given location */ addInstance(loc) { local inst; /* * Create an instance of the template object, mixing in our * special instance superclass using multiple inheritance. The * MultiInstanceInstance superclass overrides the location * manipulation methods so that we keep the MultiInstance parent * (i.e., us) synchronized if we move around the instance object * directly (by calling its moveInto() method directly, for * example). */ inst = TadsObject.createInstanceOf( [instanceMixIn, self], [instanceObject]); /* add it to our list of active instances */ instanceList.append(inst); /* move the instance into its new location */ inst.moveInto(loc); } /* * If any contents are added to the MultiInstance object, they must * be contents of the template object, so add them to the template * object instead of the MultiInstance parent. */ addToContents(obj) { instanceObject.addToContents(obj); } /* * remove an object from our contents - we'll delegate this to our * template object just like we delegate addToContents */ removeFromContents(obj) { instanceObject.removeFromContents(obj); } /* the mix-in superclass for our instance objects */ instanceMixIn = MultiInstanceInstance /* our vector of active instance objects */ instanceList = perInstance(new Vector(5)) ; /* * An instance of a MultiInstance object. This is a mix-in class that * we add (using mutiple inheritance) to each instance. This overrides * the location manipulation methods, to ensure that we keep the * MultiInstance parent object in sync with any changes made directly to * the instance objects. * * IMPORTANT - the library adds this class to each instance object * *automatically*. Game code shouldn't ever have to use this class * directly. */ class MultiInstanceInstance: object construct(parent) { /* remember our MultiInstance parent object */ miParent = parent; /* * clone my contents tree for the new instance, so that we have a * private copy of any components within the instance */ cloneMultiInstanceContents(); } /* move to a new location */ baseMoveInto(newCont) { /* * if we currently have a location, take the location out of our * MultiInstance parent's location list */ if (location != nil) miParent.locationList -= location; /* inherit the standard behavior */ inherited(newCont); /* * if we have a new location, add the new location to our * MultiInstance parent's location list; otherwise, drop out of * the parent's instance list */ if (newCont != nil) { /* * add the new location to the parent's location list, if * we're not already there */ if (miParent.locationList.indexOf(newCont) == nil) miParent.locationList += newCont; } else { /* * we're being removed from the game world, so remove this * instance from the parent's instance list */ miParent.instanceList.removeElement(self); } } /* * All instances of a given MultiInstance are equivalent to one * another, for parsing purposes. */ isEquivalent = true /* our MultiInstance parent */ miParent = nil ; /* ------------------------------------------------------------------------ */ /* * A "multi-faceted" object is similar to a MultiInstance object, with * the addition that the instance objects are "facets" of one another. * This means that they have the same identity, from the perspective of * a character in the scenario: all of the instance objects are part of * the same conceptual object, not separate objects. * * This is especially useful for large objects that span multiple * locations, such as a river or a long rope. * * You define a multi-faceted object the same way you set up a * MultiInstance: definfe a MultiFaceted shell object, and as part of * the shell, define the facet object using the instanceObject property. * Here's an example: * * river: MultiFaceted *. locationList = [riverBank, meadow, canyon] *. instanceObject: Fixture { 'river' 'river' *. "The river meanders by. " *. } *. ; * * The main difference between MultiInstance and MultiFaceted is that * the "facet" objects of a MultiFaceted are related as facets of a * common object from the parser's perspective. For example, if a * player refers to one facet, then travels to another location that * contains a different facet, then refers to "it", the parser will * realize that the pronoun refers to the new facet in the new location. */ class MultiFaceted: MultiInstance /* our instance objects represent our facets for parsing purposes */ getFacets() { return instanceList; } /* the mix-in superclass for our instance objects */ instanceMixIn = MultiFacetedFacet ; /* * The mix-in superclass for MultiFaceted facet instances. * * IMPORTANT - the library adds this class to each instance object * *automatically*. Game code shouldn't ever have to use this class * directly. */ class MultiFacetedFacet: MultiInstanceInstance /* * Get our other facets for parsing purposes - our parent maintains * the list of all of its facets, so simply return that list. (Note * that we'll be in the list as well, but that's harmless, so don't * bother removing us.) */ getFacets() { return miParent.getFacets(); } ; /* ------------------------------------------------------------------------ */ /* * A "linkable" object is one that can participate in a master/slave * relationship. This kind of relationship means that the state of both * objects in the pair is controlled by one of the objects, called the * master; the other object defers to the other to get and set all of * its linkable state. * * Note that this base class doesn't provide for the management of any * of the actual linked state. Subclasses are responsible for doing * this. The general pattern is to create a getter/setter method pair * for each bit of linked state, and in these methods refer to * masterObject.xxx rather than just self.xxx. * * This is useful for objects such as doors that have two separate * objects representing the two sides of the door. The two sides are * always linked for things like open/closed and locked/unlocked state; * this can be handled by linking the two sides, and managing all state * of both sides in one side designated as the master. */ class Linkable: object /* * Get the master object, which holds our state. By default, this * is simply 'self', but some objects might want to override this. * For example, doors are usually implemented with two separate * objects, representing the two sides of the door, which share * common state; in such cases, one of the pair can be designated as * the master, which holds the common state of the door, and this * method can be overridden so that all state operations on the lock * are performed on the master side of the door. * * We return self by default so that a linkable object can stand * alone if desired. That is, a linkable object doesn't have to be * part of a pair; it can just as well be a single object. */ masterObject() { /* * inherit from the next superclass, if possible; otherwise, use * 'self' as the default master object */ if (canInherit()) return inherited(); else return self; } /* * We're normally mixed into a Thing; do some extra work in * initialization. */ initializeThing() { /* inherit the default handling */ inherited(); /* * If we're tied to a separate master object, check the master * object to see if it's tied back to us as its master object. * Only one can be the master; if each says the other is the * master, we'll get stuck in infinite loops as each tries to * defer to the other. To avoid this, break the loop by * arbitrarily choosing one or the other as the master. Note * that we don't have to worry about the other object making a * different decision and breaking the relationship, because if * we detect the loop, it means we're going first - if the other * object had gone first then it would have detected and broken * the loop itself, and we wouldn't be finding the loop now. */ if (masterObject != self && masterObject.masterObject == self) { /* * We're tied together in a loop - break the loop by * arbitrarily electing myself as the master object. * Because these relationships are symmetric, it shouldn't * matter which we choose. */ masterObject = self; } } ; /* ------------------------------------------------------------------------ */ /* * A "basic openable" is an object that keeps open/closed status, and * which can be linked to another object to maintain that status. This * basic class doesn't handle any special commands; it's purely for * keeping track of internal open/closed state. */ class BasicOpenable: Linkable /* * Initial open/closed setting. Set this to true to make the object * open initially. If this object is linked to another object (as * in the two sides of a door), you only need to set this property * in the *master* object - the other side will automatically link * up to the master object during initialization. */ initiallyOpen = nil /* * Flag: door is open. Travel is only possible when the door is * open. Return the master's status. */ isOpen() { /* * If we're the master, simply use our isOpen_ property; * otherwise, call our master's isOpen method. This way, if the * master has a different way of calculating isOpen, we'll defer * to its different handling. */ if (masterObject == self) return isOpen_; else return masterObject.isOpen(); } /* * Make the object open or closed. By default, we'll simply set the * isOpen flag to the new status. Objects can override this to * apply side effects of opening or closing the object. */ makeOpen(stat) { /* * if we're the master, simply set our isOpen_ property; * otherwise, defer to the master */ if (masterObject == self) isOpen_ = stat; else masterObject.makeOpen(stat); /* inherit the next superclass's handling */ inherited(stat); } /* * Open status name. This is an adjective describing whether the * object is opened or closed. In English, this will return "open" * or "closed." */ openDesc = (isOpen ? gLibMessages.openMsg(self) : gLibMessages.closedMsg(self)) /* initialization */ initializeThing() { /* inherit the default handling */ inherited(); /* if we're the master, set our initial open/closed state */ if (masterObject == self) isOpen_ = initiallyOpen; } /* * If we're obstructing a sense path, it must be because we're * closed. Try implicitly opening. */ tryImplicitRemoveObstructor(sense, obj) { /* * If I'm not already open, try opening me. As usual for 'try' * routines, we return true if we attempt a command, nil if not. * * Note that we might be creating an obstruction despite already * being open; in this case, we don't want to do anything, since * an implied 'open' won't help when we're already open. */ return isOpen ? nil : tryImplicitAction(Open, self); } /* * if we can't reach or move something through the container, it * must be because we're closed */ cannotTouchThroughMsg = &cannotTouchThroughClosedMsg cannotMoveThroughMsg = &cannotMoveThroughClosedMsg /* * Internal open/closed status. Do not use this for initialization * - set initiallyOpen in the master object instead. */ isOpen_ = nil ; /* ------------------------------------------------------------------------ */ /* * Openable: a mix-in class that can be combined with an object's other * superclasses to make the object respond to the verbs "open" and * "close." We also add some extra features for other related verbs, * such as a must-be-open precondition "look in" and "board". */ class Openable: BasicOpenable /* * Describe our contents using a special version of the contents * lister, so that we add our open/closed status to the listing. The * message we add is given by our openStatus method, so if all you * want to change is the "it's open" status message, you can just * override openStatus rather than providing a whole new lister. */ descContentsLister = openableDescContentsLister /* * Contents lister to use when we're opening the object. This * lister shows the items that are newly revealed when the object is * opened. */ openingLister = openableOpeningLister /* * Get our "open status" message - this is a complete sentence saying * that we're open or closed. By default, in English, we just say * "it's open" (adjusted for number and gender, of course). * * Note that this message has to be a stand-alone independent clause. * In particular note that we don't put any spacing after it, since * we need to be able to add sentence-ending or clause-ending * punctuation immediately after it. */ openStatus() { return gLibMessages.openStatusMsg(self); } /* * By default, an Openable that's also a Lockable must be closed to * be locked. This means that when it's open, the object is * implicitly unlocked, in which case "It's unlocked" isn't worth * mentioning when the description says "It's open." */ lockStatusReportable = (!isOpen) /* * Action handlers */ dobjFor(Open) { verify() { /* it makes no sense to open something that's already open */ if (isOpen) illogicalAlready(&alreadyOpenMsg); } action() { local trans; /* * note the effect we have currently, while still closed, on * sensing from outside into our contents */ trans = transSensingIn(sight); /* make it open */ makeOpen(true); /* * make the default report - if we make a non-default * report, the default will be ignored, so we don't need to * worry about whether or not we'll make a non-default * report now */ defaultReport(&okayOpenMsg); /* * If the actor is outside me, and we have any listable * contents, and our sight transparency is now better than it * was before we were open, reveal the new contents. * Otherwise, just show our default 'opened' message. * * As a special case, if we're running as an implied command * within a LookIn or Search action on this same object, * don't bother showing this result. Doing so would be * redundant with the explicit examination of the contents * that we'll be doing anyway with the main action. */ if (!gActor.isIn(self) && transparencyCompare(transSensingIn(sight), trans) > 0 && !(gAction.isImplicit && (gAction.parentAction.ofKind(LookInAction) || gAction.parentAction.ofKind(SearchAction)) && gAction.parentAction.getDobj() == self)) { local tab; /* get the table of visible objects */ tab = gActor.visibleInfoTable(); /* show my contents list, if I have any */ openingLister.showList(gActor, self, contents, ListRecurse, 0, tab, nil); /* mark my contents as having been seen */ setContentsSeenBy(tab, gActor); /* show any special contents as well */ examineSpecialContents(); } } } dobjFor(Close) { verify() { /* it makes no sense to close something that's already closed */ if (!isOpen) illogicalAlready(&alreadyClosedMsg); } action() { /* make it closed */ makeOpen(nil); /* show the default report */ defaultReport(&okayCloseMsg); } } dobjFor(LookIn) { /* * to look in an openable object, we must be open, unless the * object is transparent or the actor is inside us */ preCond { local lst; /* get the inherited preconditions */ lst = nilToList(inherited()); /* * if I'm not transparent looking in, and the actor isn't * already inside me, try opening me */ if (transSensingIn(sight) != transparent && !gActor.isIn(self)) lst += objOpen; /* return the result */ return lst; } } dobjFor(Search) { /* * To search an openable object, we must be open - unlike LOOK * IN, this applies even if the object is transparent, since * SEARCH is inherently more aggressive than LOOK IN, and implies * physically picking through the contents. This doesn't apply * if the actor is already inside me. */ preCond { /* get the inherited preconditions */ local lst = nilToList(inherited()); /* if the actor isn't in me, make sure I'm open */ if (!gActor.isIn(self)) lst += objOpen; /* * searching implies physically sifting through the contents, * so we need to be able to touch the object */ lst += touchObj; /* return the updated list */ return lst; } } /* * Generate a precondition to make sure gActor can reach the interior * of the container. We consider the inside reachable if either the * actor is located inside the container, or the actor is outside and * the container is open. */ addInteriorReachableCond(lst) { /* * If the actor's inside us, they can reach our interior whether * we're open or not, so there's no need for any additional * condition. If not, we need to be open for the actor to be * able to reach our interior. */ if (!gActor.isIn(self)) lst = nilToList(lst) + objOpen; /* return the result */ return lst; } iobjFor(PutIn) { /* make sure that our interior is reachable */ preCond { return addInteriorReachableCond(inherited()); } } iobjFor(PourInto) { /* make sure that our interior is reachable */ preCond { return addInteriorReachableCond(inherited()); } } /* can't lock an openable that isn't closed */ dobjFor(Lock) { preCond { return nilToList(inherited()) + objClosed; } } dobjFor(LockWith) { preCond { return nilToList(inherited()) + objClosed; } } /* must be open to get out of a nested room */ dobjFor(GetOutOf) { preCond() { return nilToList(inherited()) + new ObjectPreCondition(self, objOpen); } } /* must be open to get into a nested room */ dobjFor(Board) { preCond() { return nilToList(inherited()) + new ObjectPreCondition(self, objOpen); } } ; /* ------------------------------------------------------------------------ */ /* * Lockable: a mix-in class that can be combined with an object's other * superclasses to make the object respond to the verbs "lock" and * "unlock." A Lockable requires no key. * * Note that Lockable should usually go BEFORE a Thing-derived class in * the superclass list. */ class Lockable: Linkable /* * Our initial locked state (i.e., at the start of the game). By * default, we start out locked. */ initiallyLocked = true /* * Current locked state. Use our isLocked_ status if we're the * master, otherwise defer to the master. */ isLocked() { if (masterObject == self) return isLocked_; else return masterObject.isLocked(); } /* * Make the object locked or unlocked. Objects can override this to * apply side effects of locking or unlocking. By default, if we're * the master, we'll simply set our isLocked_ property to the new * status, and otherwise defer to the master object. */ makeLocked(stat) { /* apply to self or the master object, as appropriate */ if (masterObject == self) isLocked_ = stat; else masterObject.makeLocked(stat); /* inherit the next superclass's handling */ inherited(stat); } /* show our status */ examineStatus() { /* inherit the default handling */ inherited(); /* * if our lock status is visually apparent, and we want to * mention the lock status in our current state, show the lock * status */ if (lockStatusObvious && lockStatusReportable) say(isLocked ? gLibMessages.currentlyLocked : gLibMessages.currentlyUnlocked); } /* * Description of the object's current locked state. In English, * this simply returns one of 'locked' or 'unlocked'. (Note that * this is provided as a convenience to games, for generating * messages about the object that include its state. The library * doesn't use this message itself, so overriding this won't change * any library messages - in particular, it won't change the * examineStatus message.) */ lockedDesc = (isLocked() ? gLibMessages.lockedMsg(self) : gLibMessages.unlockedMsg(self)) /* * Is our 'locked' status obvious? This should be set to true for an * object whose locked/unlocked status can be visually observed, nil * for an object whose status is not visuall apparent. For example, * you can usually tell from the inside that a door is locked by * looking at the position of the lock's paddle, but on the outside * of a door there's usually no way to see the status. * * By default, since we can be locked and unlocked with simple LOCK * and UNLOCK commands, we assume the status is as obvious as the * mechanism must be to allow such simple commands. */ lockStatusObvious = true /* * Is our 'locked' status reportable in our current state? This is * similar to lockStatusObvious, but serves a separate purpose: this * tells us if we wish to report the lock status for aesthetic * reasons. * * This property is primarily of interest to mix-ins. To allow * mix-ins to get a say, regardless of the order of superclasses, * we'll by default defer to any inherited value if there is in fact * an inherited value. If there's no inherited value, we'll simply * return true. * * We use this in the library for one case in particular: when we're * mixed with Openable, we don't want to report the lock status for * an open object because an Openable must by default be closed to be * locked. That is, when an Openable is open, it's always unlocked, * so reporting that it's unlocked is essentially redundant * information. */ lockStatusReportable = (canInherit() ? inherited() : true) /* * Internal locked state. Do not use this to set the initial state * - set initiallyLocked in the master object instead. */ isLocked_ = nil /* initialization */ initializeThing() { /* inherit the default handling */ inherited(); /* if we're the master, set our initial state */ if (masterObject == self) isLocked_ = initiallyLocked; } /* * Action handling */ /* "lock" */ dobjFor(Lock) { preCond = (nilToList(inherited()) + [touchObj]) verify() { /* if we're already locked, there's no point in locking us */ if (isLocked) illogicalAlready(&alreadyLockedMsg); } action() { /* make it locked */ makeLocked(true); /* make the default report */ defaultReport(&okayLockMsg); } } /* "unlock" */ dobjFor(Unlock) { preCond = (nilToList(inherited()) + [touchObj]) verify() { /* if we're already unlocked, there's no point in doing this */ if (!isLocked) illogicalAlready(&alreadyUnlockedMsg); } action() { /* make it unlocked */ makeLocked(nil); /* make the default report */ defaultReport(&okayUnlockMsg); } } /* "lock with" */ dobjFor(LockWith) { preCond = (nilToList(inherited()) + [touchObj]) verify() { illogical(&noKeyNeededMsg); } } /* "unlock with" */ dobjFor(UnlockWith) { preCond = (nilToList(inherited()) + [touchObj]) verify() { illogical(&noKeyNeededMsg); } } /* * Should we automatically unlock this door on OPEN? By default, we * do this only if the lock status is obvious. */ autoUnlockOnOpen = (lockStatusObvious) /* * A locked object can't be opened - apply a precondition and a check * for "open" that ensures that we unlock this object before we can * open it. * * If the lock status isn't obvious, don't try to unlock the object * as a precondition. Instead, test to make sure it's unlocked in * the 'check' routine, and fail. */ dobjFor(Open) { preCond() { /* start with the inherited preconditions */ local ret = nilToList(inherited()); /* automatically unlock on open, if appropriate */ if (autoUnlockOnOpen) ret += objUnlocked; /* return the result */ return ret; } check() { /* make sure we're unlocked */ if (isLocked) { /* let them know we're locked */ reportFailure(&cannotOpenLockedMsg); /* set 'it' to me, so UNLOCK IT works */ gActor.setPronounObj(self); /* we cannot proceed */ exit; } /* inherit the default handling */ inherited(); } } ; /* ------------------------------------------------------------------------ */ /* * A lockable that can't be locked and unlocked by direct action. The * LOCK and UNLOCK commands cannot be used with this kind of lockable. * * This is useful for a couple of situations. First, it's useful when we * want to create a locked object that simply can't be unlocked, such as * a locked door that forms a permanent boundary of the map. Second, * it's useful for locked objects that must be unlocked by some other * means, such as manipulating an external mechanism (pulling a lever, * say). In these cases, the trick is to figure out the separate means * of unlocking the door, so we don't want the LOCK and UNLOCK commands * to work directly. */ class IndirectLockable: Lockable dobjFor(Lock) { check() { reportFailure(cannotLockMsg); exit; } } dobjFor(LockWith) asDobjFor(Lock) dobjFor(Unlock) { check() { reportFailure(cannotUnlockMsg); exit; } } dobjFor(UnlockWith) asDobjFor(Unlock) /* * Since we can't be locked and unlocked with simple LOCK and UNLOCK * commands, presume that the lock status isn't obvious. If the * alternative mechanism that locks and unlocks the object makes the * current status readily apparent, this should be overridden and set * to true. */ lockStatusObvious = nil /* the message we display in response to LOCK/UNLOCK */ cannotLockMsg = &unknownHowToLockMsg cannotUnlockMsg = &unknownHowToUnlockMsg ; /* ------------------------------------------------------------------------ */ /* * LockableWithKey: a mix-in class that can be combined with an object's * other superclasses to make the object respond to the verbs "lock" and * "unlock," with a key as an indirect object. A LockableWithKey cannot * be locked or unlocked except with the keys listed in the keyList * property. * * Note that LockableWithKey should usually go BEFORE a Thing-derived * class in the superclass list. */ class LockableWithKey: Lockable /* * Determine if the key fits this lock. Returns true if so, nil if * not. By default, we'll return true if the key is in my keyList. * This can be overridden to use other key selection criteria. */ keyFitsLock(key) { return keyList.indexOf(key) != nil; } /* * Determine if the key is plausibly of the right type for this * lock. This doesn't check to see if the key actually fits the * lock - rather, this checks to see if the key is generally the * kind of object that might plausibly be used with this lock. * * The point of this routine is to make this class concerned only * with the abstract notion of objects that serve to lock and unlock * other objects, without requiring that the key objects resemble * little notched metal sticks or that the lock objects resemble * cylinders with pins - or, more specifically, without requiring * that all of the kinds of keys in a game remotely resemble one * another. * * For example, one kind of "key" in a game might be a plastic card * with a magnetic stripe, and the corresponding lock would be a * card slot; another kind of key might the traditional notched * metal stick. Clearly, no one would ever think to use a plastic * card with a conventional door lock, nor would one try to put a * house key into a card slot (not with the expectation that it * would actually work, anyway). This routine is meant to * facilitate this kind of distinction: the card slot can use this * routine to indicate that only plastic card objects are plausible * as keys, and door locks can indicate that only metal keys are * plausible. * * This routine can be used for disambiguation and other purposes * when we must programmatically select a key that is not specified * or is only vaguely specified. For example, the keyring searcher * uses it so that, when we're searching for a key on a keyring to * open this lock, we implicitly try only the kinds of keys that * would be plausibly useful for this kind of lock. * * By default, we'll simply return true. Subclasses specific to a * game (such as the "card reader" base class or the "door lock" * base class) can override this to discriminate among the * game-specific key classes. */ keyIsPlausible(key) { return true; } /* the list of objects that can serve as keys for this object */ keyList = [] /* * The list of keys which the player knows will fit this lock. This * is used to make key disambiguation automatic once the player * knows the correct key for a lock. */ knownKeyList = [] /* * Get my known key list. This simply returns the known key list * from the known key owner. */ getKnownKeyList() { return getKnownKeyOwner().knownKeyList; } /* * Get the object that own our known key list. If we explicitly have * our own non-empty known key list, we own the key list; otherwise, * our master object owns the list, as long as it has a non-nil key * list at all. */ getKnownKeyOwner() { /* * if we have a non-empty key list, or our master object doesn't * have a key list at all, use our list; otherwise, use our * master object's list so use our list */ if (knownKeyList.length() != 0 || masterObject.knownKeyList == nil) return self; else return masterObject; } /* * Flag: remember my keys after they're successfully used. If this * is true, whenever a key is successfully used to lock or unlock * this object, we'll add the key to our known key list; * subsequently, whenever we try to use a key in this lock, we will * automatically disambiguate the key based on the keys known to * work previously. * * Some authors might prefer not to assume that the player should * remember which keys operate which locks, so this property can be * changed to nil to eliminate this memory feature. By default we * set this to true, since it shouldn't generally give away any * secrets or puzzles for the game to assume that a key that was * used successfully once with a given lock is the one to be used * subsequently with the same lock. */ rememberKnownKeys = true /* * Determine if the player knows that the given key operates this * lock. Returns true if the key is in our known key list, nil if * not. */ isKeyKnown(key) { return getKnownKeyList().indexOf(key) != nil; } /* * By default, the locked/unlocked status of a keyed lockable is nil. * In most cases, an object that's locked and unlocked using a key * doesn't have a visible indication of the status; for example, you * usually can't tell just by looking at it from the outside whether * or not an exterior door to a building is locked. Usually, the * only way to tell from the outside that an exterior door is locked * is to try opening it and see if it opens. */ lockStatusObvious = nil /* * Should we automatically unlock on OPEN? We will if our inherited * handling says so, OR if the current actor is carrying a key * that's known to work with this object. We automatically unlock * when a known key is present as a convenience: if we have a known * key, then there's no mystery in unlocking this object, and thus * for playability we want to make its operation fully automatic. */ autoUnlockOnOpen() { return (inherited() || getKnownKeyList.indexWhich({x: x.isIn(gActor)}) != nil); } /* * Action handling */ dobjFor(Lock) { preCond { /* * remove any objClosed from our precondition - since we * won't actually do any locking but will instead merely ask * for an indirect object, we don't want to apply the normal * closed precondition here */ return inherited() - objClosed; } verify() { /* if we're already locked, there's no point in locking us */ if (isLocked) illogicalAlready(&alreadyLockedMsg); } action() { /* ask for an indirect object to use as the key */ askForIobj(LockWith); } } /* "unlock" */ dobjFor(Unlock) { verify() { /* if we're not locked, there's no point in unlocking us */ if (!isLocked) illogicalAlready(&alreadyUnlockedMsg); } action() { /* * We need a key. If we're running as an implied action, the * player hasn't specifically proposed unlocking the object, * so it's a little weird to ask a follow-up question about * what key to use. So, if the action is implicit and * there's no default key, don't proceed; simply fail with an * explanation. */ if (gAction.isImplicit && !UnlockWithAction.testRetryDefaultIobj(gAction)) { /* explain that we need a key, and we're done */ reportFailure(&unlockRequiresKeyMsg); return; } /* ask for a key */ askForIobj(UnlockWith); } } /* * perform the action processing for LockWith or UnlockWith - these * are highly symmetrical, in that the only thing that varies is the * new lock state we establish */ lockOrUnlockAction(lock) { /* * If it's a keyring, let the keyring's action handler do the * work. Otherwise, if it's my key, lock/unlock; it's not a * key, fail. */ if (gIobj.ofKind(Keyring)) { /* * do nothing - let the indirect object action handler do * the work */ } else if (keyFitsLock(gIobj)) { local ko; /* * get the object (us or our master object) that owns the * known key list */ ko = getKnownKeyOwner(); /* * if the key owner remembers known keys, and it doesn't know * about this working key yet, remember this in the list of * known keys */ if (ko.rememberKnownKeys && ko.knownKeyList.indexOf(gIobj) == nil) ko.knownKeyList += gIobj; /* set my new state and issue a default report */ makeLocked(lock); defaultReport(lock ? &okayLockMsg : &okayUnlockMsg); } else { /* the key doesn't work in this lock */ reportFailure(&keyDoesNotFitLockMsg); } } /* "lock with" */ dobjFor(LockWith) { verify() { /* if we're already locked, there's no point in locking us */ if (isLocked) illogicalAlready(&alreadyLockedMsg); } action() { /* perform the generic lock/unlock action processing */ lockOrUnlockAction(true); } } /* "unlock with" */ dobjFor(UnlockWith) { verify() { /* if we're not locked, there's no point in unlocking us */ if (!isLocked) illogicalAlready(&alreadyUnlockedMsg); } action() { /* perform the generic lock/unlock action processing */ lockOrUnlockAction(nil); } } ; /* ------------------------------------------------------------------------ */ /* * The common base class for containers and surfaces: things that have * limited bulk capacities. This class isn't usually used directly; * subclasses such as Surface and Container are usually used instead. */ class BulkLimiter: Thing /* * A container can limit the cumulative amount of bulk of its * contents, and the maximum bulk of any one object, using * bulkCapacity and maxSingleBulk. We count the cumulative and * single-item limits separately, since we want to allow modelling * some objects as so large that they won't fit in this container at * all, even if the container is carrying nothing else, without * limiting the number of small items we can carry. * * By default, we set bulkCapacity to a very large number, making * the total capacity of the object essentially unlimited. However, * we set maxSingleBulk to a relatively low number - this way, if an * author wants to designate certain objects as especially large and * thus unable to fit in ordinary containers, the author merely * needs to set the bulk of those large items to something greater * than 10. On the other hand, if an author doesn't want to worry * about bulk and limited carrying capacities and simply uses * library defaults for everything, we will be able to contain * anything and everything. * * In a game that models bulk realistically, a container's bulk * should generally be equal to or slightly greater than its * bulkCapacity, because a container shouldn't be smaller on the * outside than on the inside. If bulkCapacity exceeds bulk, the * player can work around a holding bulk limit by piling objects * into the container, thus "hiding" the bulks of the contents * behind the smaller bulk of the container. */ bulkCapacity = 10000 maxSingleBulk = 10 /* * receive notification that we're about to insert an object into * this container */ notifyInsert(obj, newCont) { /* if I'm the new direct container, check our bulk limit */ if (newCont == self) { /* * do a 'what if' test to see what would happen to our * contained bulk if we moved this item into me */ obj.whatIf({: checkBulkInserted(obj)}, &moveInto, self); } /* inherit base class handling */ inherited(obj, newCont); } /* * Check to see if a proposed insertion - already tentatively in * effect when this routine is called - would overflow our bulk * limits. Reports failure and exits if the inserted object would * exceed our capacity. */ checkBulkInserted(insertedObj) { local objBulk; /* get the bulk of the inserted object itself */ objBulk = insertedObj.getBulk(); /* * Check the object itself to see if it fits by itself. If it * doesn't, we can report the simple fact that the object is too * big for the container. */ if (objBulk > maxSingleBulk || objBulk > bulkCapacity) { reportFailure(&tooLargeForContainerMsg, insertedObj, self); exit; } /* * If our contained bulk is over our maximum, don't allow it. * Note that we merely need to check our current bulk within, * since this routine is called with the insertion already * tentatively in effect. */ if (getBulkWithin() > bulkCapacity) { reportFailure(tooFullMsg, insertedObj, self); exit; } } /* * the message property to use when we're too full to hold a new * object (i.e., the object's bulk would push us over our bulk * capacity limit) */ tooFullMsg = &containerTooFullMsg /* * the message property to use when doing something to one of our * contents would make it too large to fit all by itself into this * container (that is, it would cause that object's bulk to exceed * our maxSingleBulk) */ becomingTooLargeMsg = &becomingTooLargeForContainerMsg /* * the message property to use when doing something to one of our * contents would cause our overall contents to exceed our capacity */ becomingTooFullMsg = &containerBecomingTooFullMsg /* * Check a bulk change of one of my direct contents. */ checkBulkChangeWithin(obj) { local objBulk; /* get the object's new bulk */ objBulk = obj.getBulk(); /* * if this change would cause the object to exceed our * single-item bulk limit, don't allow it */ if (objBulk > maxSingleBulk || objBulk > bulkCapacity) { reportFailure(becomingTooLargeMsg, obj, self); exit; } /* * If our total carrying capacity is exceeded with this change, * don't allow it. Note that 'obj' is already among our * contents when this routine is called, so we can simply check * our current total bulk within. */ if (getBulkWithin() > bulkCapacity) { reportFailure(becomingTooFullMsg, obj, self); exit; } } /* * Adjust a THROW destination. Since we only allow a limited amount * of bulk within our contents, we need to make sure the thrown * object would fit if it landed here. If it doesn't, we'll redirect * the landing site to our container. */ adjustThrowDestination(thrownObj, path) { local thrownBulk = thrownObj.getBulk(); local newBulk; local dest; /* * do a 'what if' test to test our total bulk with the projectile * added to my contents */ newBulk = thrownObj.whatIf({: getBulkWithin()}, &moveInto, self); /* * If that exceeds our maximum bulk, or the object's bulk * individually is over our limit, we can't be the landing site. * In this case, defer to our location's drop destination, if it * has one. */ if ((newBulk > bulkCapacity || thrownBulk > bulkCapacity || thrownBulk > maxSingleBulk) && location != nil && (dest = location.getDropDestination(thrownObj, path)) != nil) { /* * It won't fit, so defer to our container's drop * destination. Give the new destination a chance to further * adjust the destination. */ return dest.adjustThrowDestination(thrownObj, path); } /* * the projectile fits, or we just can't find a container to * defer to; use the original destination, i.e., self */ return self; } /* * Examine my interior. This can be used to handle the action() for * LOOK IN, or for other commands appropriate to the subclass. */ examineInterior() { /* examine the interior with our normal look-in lister */ examineInteriorWithLister(lookInLister); /* * Anything that the an overriding caller (a routine that called * us with 'inherited') wants to add is an addendum to our * description, so add a transcript marker to indicate that the * main description is now finished. * * The important thing about this is that any message that an * overriding caller wants to add is not considered part of the * description, in the sense that we don't want it to suppress * any default description we've already generated. One of the * transformations we apply to the transcript is to suppress any * default descriptive text if there's any more specific * descriptive text following (for example, we suppress "It's an * ordinary " if we also are going to say "it's open" or * "it contains three coins"). If we have an overriding caller * who's going to add anything, then we must assume that what the * caller's adding is something about the act of examining the * object, rather than a description of the object, so we don't * want it to suppress a default description. */ gTranscript.endDescription(); } /* examine my interior, listing the contents with the given lister */ examineInteriorWithLister(lister) { local tab; /* if desired, reveal any "Hidden" items concealed within */ if (revealHiddenItems) { /* scan our contents and reveal each Hidden item */ foreach (local cur in contents) { /* if it's a Hidden item, reveal it */ if (cur.ofKind(Hidden)) cur.discover(); } } /* get my visible sense info */ tab = gActor.visibleInfoTable(); /* show my contents, if I have any */ lister.showList(gActor, self, contents, ListRecurse, 0, tab, nil); /* mark my contents as having been seen */ setContentsSeenBy(tab, gActor); /* examine my special contents */ examineSpecialContents(); } /* * Verify putting something new in my interior. This is suitable * for use as a verify() method for a command like PutIn or PutOn. * Note that this routine assumes and requires that gDobj be the * object to be added, and gIobj be self. */ verifyPutInInterior() { /* * if we haven't resolved the direct object yet, we can at least * check to see if all of the potential direct objects are * already in me, and rule out this indirect object as illogical * if so */ if (gDobj == nil) { /* * check the tentative direct objects to see if (1) all of * them are directly inside me already, or (2) all of them * are at least indirectly inside me already */ if (gTentativeDobj.indexWhich( {x: !x.obj_.isDirectlyIn(self)}) == nil) { /* * All of the potential direct objects are already * directly inside me. This makes this object * illogical, since there's no need to move any of these * objects into me. */ illogicalAlready(&alreadyPutInMsg); } else if (gTentativeDobj.indexWhich( {x: !x.obj_.isIn(self)}) == nil) { /* * All of the potential direct objects are already in * me, at least indirectly. This makes this object * somewhat less likely, since we're more likely to want * to put something in here that wasn't already within. * Note that this isn't actually illogical, though, * since we could be moving something from deeper inside * me to directly inside me. */ logicalRank(50, 'dobjs already inside'); } } else { /* * We can't put myself in myself, obviously. We also can't * put something into any component of itself, so the command * is illogical if we're a component of the direct object. */ if (gDobj == self || isComponentOf(gDobj)) illogicalSelf(&cannotPutInSelfMsg); /* if it's already directly inside me, this is illogical */ if (gDobj.isDirectlyIn(self)) illogicalAlready(&alreadyPutInMsg); } /* * if I'm not held by the actor, give myself a slightly lower * ranking than fully logical, so that objects being held are * preferred */ if (!isIn(gActor)) logicalRank(60, 'not indirectly held'); else if (!isHeldBy(gActor)) logicalRank(70, 'not held'); } /* * Flag: reveal any hidden items contained directly within me when * my interior is explicitly examined, via a command such as LOOK IN * . By default, we reveal our hidden contents on * examination; hidden objects are in most cases meant to be more * inconspicuous than actually camouflaged, so a careful, explicit * examination would normally reveal them. If our hidden objects * are so concealed that even explicit examination of our interior * wouldn't reveal them, set this to nil. */ revealHiddenItems = true ; /* ------------------------------------------------------------------------ */ /* * A basic container is an object that can enclose its contents. This is * the core of the Container type, but this class only has the bare-bones * sense-related enclosing features, without any action implementation. * This can be used for cases where an object isn't meant to have its * contents be manipulable by the player (so we don't want to allow "put * in" and so on), but where we do want the ability to conceal our * contents when we're closed. */ class BasicContainer: BulkLimiter /* * My current open/closed state. By default, this state never * changes, but is fixed in the object's definition; for example, a * box without a lid would always be open, while a hollow glass cube * would always be closed. Our default state is open. */ isOpen = true /* the material that we're made of */ material = adventium /* prepositional phrase for objects being put into me */ putDestMessage = &putDestContainer /* * Determine if I can move an object via a path through this * container. */ checkMoveViaPath(obj, dest, op) { /* * if we're moving the object in or out of me, we must consider * our openness and whether or not the object fits through our * opening */ if (op is in (PathIn, PathOut)) { /* if we're closed, we can't move anything in or out */ if (!isOpen) return new CheckStatusFailure(cannotMoveThroughMsg, obj, self); /* if it doesn't fit through our opening, don't allow it */ if (!canFitObjThruOpening(obj)) return new CheckStatusFailure(op == PathIn ? &cannotFitIntoOpeningMsg : &cannotFitOutOfOpeningMsg, obj, self); } /* in any other cases, allow the operation */ return checkStatusSuccess; } /* * The message property we use when we can't move an object through * the containment boundary. This is a playerActionMessages * property. */ cannotMoveThroughMsg = &cannotMoveThroughContainerMsg /* * Determine if an actor can touch an object via a path through this * container. */ checkTouchViaPath(obj, dest, op) { /* * if we're reaching from inside directly to me, allow it - * treat this as touching our interior, which we allow from * within regardless of our open/closed status */ if (op == PathOut && dest == self) return checkStatusSuccess; /* * if we're reaching in or out of me, consider our openness and * whether or not the actor's hand fits through our opening */ if (op is in (PathIn, PathOut)) { /* if we're closed, we can't reach into/out of the container */ if (!isOpen) return new CheckStatusFailure(cannotTouchThroughMsg, obj, self); /* * if the object's "hand" doesn't fit through our opening, * don't allow it */ if (!canObjReachThruOpening(obj)) return new CheckStatusFailure(op == PathIn ? &cannotReachIntoOpeningMsg : &cannotReachOutOfOpeningMsg, obj, self); } /* in any other cases, allow the operation */ return checkStatusSuccess; } /* * Library message (in playerActionMessages) explaining why we can't * touch an object through this container. This is used when an * actor on the outside tries to reach something on the inside, or * vice versa. */ cannotTouchThroughMsg = &cannotTouchThroughContainerMsg /* * Determine if the given object fits through our opening. This is * only called when we're open; this determines if the object can be * moved in or out of this container. By default, we'll return * true; some objects might want to override this to disallow * objects over a certain size from being moved in or out of this * container. * * Note that this method doesn't care whether or not the object can * actually fit inside the container once through the opening; we * only care about whether or not the object can fit through the * opening itself. This allows for things like narrow-mouthed * bottles which have greater capacity within than in their * openings. */ canFitObjThruOpening(obj) { return true; } /* * Determine if the given object can "reach" through our opening, * for the purposes of touching an object on the other side of the * opening. This is used to determine if the object, which is * usually an actor, can its "hand" (or whatever appendange 'obj' * uses to reach things) through our opening. This is only called * when we're open. By default, we'll simply return true. * * This differs from canFitObjThruOpening() in that we don't care if * all of 'obj' is able to fit through the opening; we only care * whether obj's hand (or whatever it uses for reaching) can fit. */ canObjReachThruOpening(obj) { return true; } /* * Determine how a sense passes to my contents. If I'm open, the * sense passes through directly, since there's nothing in the way. * If I'm closed, the sense must pass through my material. */ transSensingIn(sense) { if (isOpen) { /* I'm open, so the sense passes through without interference */ return transparent; } else { /* I'm closed, so the sense must pass through my material */ return material.senseThru(sense); } } /* * Get my fill medium. If I'm open, inherit my parent's medium, * assuming that the medium behaves like fog or smoke and naturally * disperses to fill any nested open containers. If I'm closed, I * am by default filled with no medium. */ fillMedium() { if (isOpen && location != nil) { /* I'm open, so return my location's medium */ return location.fillMedium(); } else { /* * I'm closed, so we're cut off from the parent - assume * we're filled with nothing */ return nil; } } /* * Display a message explaining why we are obstructing a sense path * to the given object. */ cannotReachObject(obj) { /* * We must be obstructing by containment. Show an appropriate * message depending on whether the object is inside me or not - * if not, then the actor trying to reach the object must be * inside me. */ if (obj.isIn(self)) gLibMessages.cannotReachContents(obj, self); else gLibMessages.cannotReachOutside(obj, self); } /* explain why we can't see the source of a sound */ cannotSeeSoundSource(obj) { /* we must be obstructing by containment */ if (obj.isIn(self)) gLibMessages.soundIsFromWithin(obj, self); else gLibMessages.soundIsFromWithout(obj, self); } /* explain why we can't see the source of an odor */ cannotSeeSmellSource(obj) { /* we must be obstructing by containment */ if (obj.isIn(self)) gLibMessages.smellIsFromWithin(obj, self); else gLibMessages.smellIsFromWithout(obj, self); } /* message when an object is too large (all by itself) to fit in me */ tooLargeForContainerMsg = &tooLargeForContainerMsg ; /* ------------------------------------------------------------------------ */ /* * Container: an object that can have other objects placed within it. */ class Container: BasicContainer /* * Our fixed "look in" description, if any. This is shown on LOOK * IN before our normal listing of our portable contents; it can be * used to describe generally what the interior looks like, for * example. By default, we show nothing here. */ lookInDesc = nil /* * Show our status for "examine". This shows our open/closed status, * and lists our contents. */ examineStatus() { /* show any special container-specific status */ examineContainerStatus(); /* inherit the default handling to show my contents */ inherited(); } /* * mention my open/closed status for Examine processing */ examineContainerStatus() { /* * By default, show nothing extra. This can be overridden by * subclasses as needed to show any extra status before our * contents list. */ } /* * Try putting an object into me when I'm serving as a bag of * holding. For a container, this simply does a "put obj in bag". */ tryPuttingObjInBag(target) { /* if the object won't fit all by itself, don't even try */ if (target.getBulk() > maxSingleBulk) return nil; /* if we can't fit the object with other contents, don't try */ if (target.whatIf({: getBulkWithin() > bulkCapacity}, &moveInto, self)) return nil; /* we're a container, so use "put in" to get the object */ return tryImplicitActionMsg(&announceMoveToBag, PutIn, target, self); } /* * Try moving an object into this container. For a container, this * performs a PUT IN command to move the object into self. */ tryMovingObjInto(obj) { return tryImplicitAction(PutIn, obj, self); } /* -------------------------------------------------------------------- */ /* * "Look in" */ dobjFor(LookIn) { verify() { } check() { /* * If I'm closed, and I can't see my contents when closed, we * can't go on. Unless, of course, the actor is inside us, * in which case our external boundary isn't relevant. */ if (!isOpen && transSensingIn(sight) == opaque && !gActor.isIn(self)) { /* we can't see anything because we're closed */ reportFailure(&cannotLookInClosedMsg); exit; } } action() { /* show our fixed "look in" description, if any */ lookInDesc; /* examine my interior */ examineInterior(); } } /* * "Search". This is mostly like Open, except that the actor has to * be able to reach into the object, not just see into it - searching * implies a more thorough sort of examination, usually including * physically poking through the object's contents. */ dobjFor(Search) { preCond = (nilToList(inherited()) + [touchObj]) check() { /* * if I'm closed, and the actor isn't inside me, make sure my * contents are reachable from the outside */ if (!isOpen && transSensingIn(touch) != transparent && !gActor.isIn(self)) { /* we can't search an object that we can't reach into */ reportFailure(&cannotTouchThroughMsg, gActor, self); exit; } } } /* -------------------------------------------------------------------- */ /* * Put In processing. A container can accept new contents. */ iobjFor(PutIn) { verify() { /* use the standard verification for adding new contents */ verifyPutInInterior(); } action() { /* move the direct object into me */ gDobj.moveInto(self); /* issue our default acknowledgment of the command */ defaultReport(&okayPutInMsg); } } ; /* * A "restricted holder" is a generic mix-in class for various container * types (Containers, Surfaces, Undersides, RearContainers, RearSurfaces) * that adds a restriction to what can be contained. */ class RestrictedHolder: object /* * A list of acceptable items for the container. This list can be * used to identify the objects that can be put in the container (or * on the surface, under the underside, or behind the rear container * or surface). */ validContents = [] /* * Is the given object allowed to go in this container (or * on/under/behind it, as appropriate for the type)? Returns true if * so, nil if not. By default, we'll return true if the object is * found in our validContents list, nil if not. This can be * overridden if a subclass wants to determine which objects are * acceptable with some other kind of per-object test; for example, a * subclass might accept only objects of a given class as contents, * or might accept only contents with some particular attribute. */ canPutIn(obj) { return validContents.indexOf(obj) != nil; } /* * Check a PUT IN/ON/UNDER/BEHIND action to ensure that the direct * object is in our approved-contents list. */ checkPutDobj(msgProp) { /* validate the direct object */ if (!canPutIn(gDobj)) { /* explain the problem */ reportFailure(self.(msgProp)(gDobj)); /* terminate the command */ exit; } } ; /* * A special kind of container that only accepts specific contents. The * acceptable contents can be specified by a list of enumerated items, * or by a method that indicates whether or not an item is allowed. */ class RestrictedContainer: RestrictedHolder, Container /* * A message that explains why the direct object can't be put in this * container. In most cases, the rather generic default message * should be overridden to provide a specific reason that the dobj * can't be put in this object. The rejected object is provided as a * parameter in case the message needs to vary by object, but we * ignore this and just use a single blanket failure message by * default. */ cannotPutInMsg(obj) { return &cannotPutInRestrictedMsg; } /* override PutIn to enforce our contents restriction */ iobjFor(PutIn) { check() { checkPutDobj(&cannotPutInMsg); } } ; /* * A single container is a special kind of container that can only * contain a single item. If another object is put into this container, * we'll remove any current contents. */ class SingleContainer: Container /* override PutIn to enforce our single-contents rule */ iobjFor(PutIn) { preCond { return inherited() + objEmpty; } } ; /* ------------------------------------------------------------------------ */ /* * OpenableContainer: an object that can contain things, and which can * be opened and closed. */ class OpenableContainer: Openable, Container ; /* ------------------------------------------------------------------------ */ /* * LockableContainer: an object that can contain things, and that can be * opened and closed as well as locked and unlocked. */ class LockableContainer: Lockable, OpenableContainer ; /* ------------------------------------------------------------------------ */ /* * KeyedContainer: an openable container that can be locked and * unlocked, but only with a specified key. */ class KeyedContainer: LockableWithKey, OpenableContainer ; /* ------------------------------------------------------------------------ */ /* * Surface: an object that can have other objects placed on top of it. * A surface is essentially the same as a regular container, but the * contents of a surface behave as though they are on the surface's top * rather than contained within the object. */ class Surface: BulkLimiter /* * Our fixed LOOK IN description. This is shown in response to LOOK * IN before we list our portable contents; it can be used to show * generally what the surface looks like. By default, we say * nothing here. */ lookInDesc = nil /* my contents lister */ contentsLister = surfaceContentsLister descContentsLister = surfaceDescContentsLister lookInLister = surfaceLookInLister inlineContentsLister = surfaceInlineContentsLister /* * we're a surface, so taking something from me that's not among my * contents shows the message as "that's not on the iobj" */ takeFromNotInMessage = &takeFromNotOnMsg /* * my message indicating that another object x cannot be put into me * because I'm already in x */ circularlyInMessage = &circularlyOnMsg /* message phrase for objects put into me */ putDestMessage = &putDestSurface /* message when we're too full for another object */ tooFullMsg = &surfaceTooFullMsg /* * Try moving an object into this container. For a surface, this * performs a PUT ON command to move the object onto self. */ tryMovingObjInto(obj) { return tryImplicitAction(PutOn, obj, self); } /* -------------------------------------------------------------------- */ /* * Put On processing */ iobjFor(PutOn) { verify() { /* use the standard put-in verification */ verifyPutInInterior(); } action() { /* move the direct object onto me */ gDobj.moveInto(self); /* issue our default acknowledgment */ defaultReport(&okayPutOnMsg); } } /* * Looking "in" a surface simply shows the surface's contents. */ dobjFor(LookIn) { verify() { } action() { /* show our fixed lookInDesc */ lookInDesc; /* show our contents */ examineInterior(); } } /* use the PUT ON forms of the verifier messages */ cannotPutInSelfMsg = &cannotPutOnSelfMsg alreadyPutInMsg = &alreadyPutOnMsg ; /* * A special kind of surface that only accepts specific contents. */ class RestrictedSurface: RestrictedHolder, Surface /* * A message that explains why the direct object can't be put on this * surface. In most cases, the rather generic default message should * be overridden to provide a specific reason that the dobj can't be * put on this surface. The rejected object is provided as a * parameter in case the message needs to vary by object, but we * ignore this and just use a single blanket failure message by * default. */ cannotPutOnMsg(obj) { return &cannotPutOnRestrictedMsg; } /* override PutOn to enforce our contents restriction */ iobjFor(PutOn) { check() { checkPutDobj(&cannotPutOnMsg); } } ; /* ------------------------------------------------------------------------ */ /* * Food - something you can eat. By default, when an actor eats a food * item, the item disappears. */ class Food: Thing dobjFor(Taste) { /* tasting food is perfectly logical */ verify() { } } dobjFor(Eat) { verify() { } action() { /* describe the consumption */ defaultReport(&okayEatMsg); /* the object disappears */ moveInto(nil); } } ; /* ------------------------------------------------------------------------ */ /* * OnOffControl - a generic control that can be turned on and off. We * keep track of an internal on/off state, and recognize the commands * "turn on" and "turn off". */ class OnOffControl: Thing /* * The current on/off setting. We'll start in the 'off' position by * default. */ isOn = nil /* * On/off status name. This returns the appropriate name ('on' or * 'off' in English) for our current status. */ onDesc = (isOn ? gLibMessages.onMsg(self) : gLibMessages.offMsg(self)) /* * Change our on/off setting. Subclasses can override this to apply * any side effects of changing the value. */ makeOn(val) { /* remember the new value */ isOn = val; } dobjFor(TurnOn) { verify() { /* if it's already on, complain */ if (isOn) illogicalAlready(&alreadySwitchedOnMsg); } action() { /* set to 'on' and generate a default report */ makeOn(true); defaultReport(&okayTurnOnMsg); } } dobjFor(TurnOff) { verify() { /* if it's already off, complain */ if (!isOn) illogicalAlready(&alreadySwitchedOffMsg); } action() { /* set to 'off' and generate a default report */ makeOn(nil); defaultReport(&okayTurnOffMsg); } } ; /* * Switch - a simple extension of the generic on/off control that can be * used with a "switch" command without specifying "on" or "off", and * treats "flip" synonymously. */ class Switch: OnOffControl /* "switch" with no specific new setting - reverse our setting */ dobjFor(Switch) { verify() { } action() { /* reverse our setting and generate a report */ makeOn(!isOn); defaultReport(isOn ? &okayTurnOnMsg : &okayTurnOffMsg); } } /* "flip" is the same as "switch" for our purposes */ dobjFor(Flip) asDobjFor(Switch) ; /* ------------------------------------------------------------------------ */ /* * Settable - an abstract class for things you can set to different * settings; the settings can be essentially anything, such as numbers * (or other markers) on a dial, or stops on a sliding switch. */ class Settable: Thing /* * Our current setting. This is an arbitrary string value. The * value initially assigned here is our initial setting; we'll * update this whenever we're set to another setting. */ curSetting = '1' /* * Canonicalize a proposed setting. This ensures that the setting is * in a specific primary format when there are superficially * different ways of expressing the same value. For example, if the * setting is numeric, this could do things like trim off leading * zeros; for a text value, it could ensure the value is in the * proper case. */ canonicalizeSetting(val) { /* * by default, we don't have any special canonical format, so * just return the value as it is */ return val; } /* * Change our setting. This is always called with the canonical * version of the new setting, as returned by canonicalizeSetting(). * Subclasses can override this routine to apply any side effects of * changing the value. */ makeSetting(val) { /* remember the new value */ curSetting = val; } /* * Is the given text a valid setting? Returns true if so, nil if * not. This should not display any messages; simply indicate * whether or not the setting is valid. * * This is always called with the *canonical* value of the proposed * new setting, as returned by canonicalizeSetting(). */ isValidSetting(val) { /* * By default, allow anything; subclasses should override to * enforce our valid set of values. */ return true; } /* * "set " action */ dobjFor(Set) { verify() { logicalRank(150, 'settable'); } action() { askForLiteral(SetTo); } } /* * "set to " action */ dobjFor(SetTo) { preCond = [touchObj] verify() { local txt; /* * If we already know our literal text, and it's not valid, * reduce the logicalness. Don't actually make it * illogical, as it's probably still more logical to set a * settable to an invalid setting than to set something that * isn't settable at all. */ if ((txt = gAction.getLiteral()) != nil && !isValidSetting(canonicalizeSetting(txt))) logicalRank(50, 'invalid setting'); } check() { /* if the setting is not valid, don't allow it */ if (!isValidSetting(canonicalizeSetting(gAction.getLiteral()))) { /* there is no such setting */ reportFailure(setToInvalidMsgProp); exit; } } action() { /* set the new value */ makeSetting(canonicalizeSetting(gAction.getLiteral())); /* remark on the change */ defaultReport(okaySetToMsgProp, curSetting); } } /* our message property for an invalid setting */ setToInvalidMsgProp = &setToInvalidMsg /* our message property for acknowledging a new setting */ okaySetToMsgProp = &okaySetToMsg ; /* * Dial - something you can turn to different settings. Note that dials * are usually used as components of larger objects; since our base * class is the basic Settable, component dials should be created to * inherit multiply from Dial and Component, in that order. * * This is almost hte same as a regular Settable; the only thing we add * is that we make "turn to " equivalent to "set * to ", as this is the verb most people would use to set a * dial. */ class Dial: Settable /* "turn" with no destination - indicate that we need a setting */ dobjFor(Turn) { verify() { illogical(&mustSpecifyTurnToMsg); } } /* treat "turn to " the same as "set to" */ dobjFor(TurnTo) asDobjFor(SetTo) /* refer to setting the dial as turning it in our messages */ setToInvalidMsgProp = &turnToInvalidMsg okaySetToMsgProp = &okayTurnToMsg ; /* * Numbered Dial - something you can turn to a range of numeric values. */ class NumberedDial: Dial /* * The range of settings - the dial can be set to values from the * minimum to the maximum, inclusive. */ minSetting = 1 maxSetting = 10 /* * Canonicalize a proposed setting value. For numbers, strip off any * leading zeros, since these don't change the meaning of the value. */ canonicalizeSetting(val) { local num; /* try parsing it as a digit string or a spelled-out number */ if ((num = parseInt(val)) != nil) { /* * we parsed it successfully - return the string * representation of the numeric value */ return toString(num); } /* it didn't parse as a number, so just return it as-is */ return val; } /* * Check a setting for validity. A setting is valid only if it's a * number within the allowed range for the dial. */ isValidSetting(val) { local num; /* if it doesn't look like a number, it's not valid */ if (rexMatch('+', val) != val.length()) return nil; /* get the numeric value */ num = toInteger(val); /* it's valid if it's within range */ return num >= minSetting && num <= maxSetting; } ; /* * Labeled Dial - something you can turn to a set of arbitrary text * labels. */ class LabeledDial: Dial /* * The list of valid settings. Each entry in this list should be a * string value. We ignore the case of these labels (we convert * everything to upper-case when comparing labels). */ validSettings = [] /* * Canonicalize the setting. We consider case insignificant in * matching our labels, but the canonical version of a setting is the * one that appears in the validSettings list - so if the player * types in SET DIAL TO EXTRA LOUD, and the validSettings list * contains 'Extra Loud', we'll want to convert the 'EXTRA LOUD' to * the capitalization of the validSettings entry. */ canonicalizeSetting(val) { local txt; /* * convert it to upper-case, so that we can compare it to our * valid labels without regard to case */ txt = val.toUpper(); /* * if we find a match in the validSettings list, return the match * from the list, since that's the canonical format */ if ((txt = validSettings.valWhich({x: x.toUpper() == txt})) != nil) return txt; /* we didn't find a match, so leave the original value unchanged */ return val; } /* * Check a setting for validity. A setting is valid only if it * appears in the validSettings list for this dial. */ isValidSetting(val) { /* * If the given value appears in our validSettings list, it's a * valid setting; otherwise, it's not valid. Ignore case when * comparing values by converting the valid labels to upper case; * we've already converted the value we're testing to upper case, * so the case mix won't matter in our comparison. * * Note that we're handed a canonical setting value, so we don't * have to worry about case differences. */ return validSettings.indexOf(val) != nil; } ; /* ------------------------------------------------------------------------ */ /* * Button - something you can push to activate, as a control for a * mechanical device. */ class Button: Thing dobjFor(Push) { verify() { } action() { /* * individual buttons should override this to carry out any * special action for the button; by default, we'll just * show a simple acknowledgment */ defaultReport(&okayPushButtonMsg); } } ; /* ------------------------------------------------------------------------ */ /* * Lever - something you can push, pull, or move, generally as a control * for a mechanical device. Our basic lever has two states, "pushed" * and "pulled". */ class Lever: Thing /* * The current state. We have two states: "pushed" and "pulled". * We start in the pushed state, so the lever can initially be * pulled, since "pull" is the verb most people would first think to * apply to a lever. */ isPulled = nil /* * Set the state. This can be overridden to apply side effects as * needed. */ makePulled(pulled) { /* note the new state */ isPulled = pulled; } /* * Action handlers. We handle push and pull, and we treat "move" as * equivalent to whichever of push or pull is appropriate to reverse * the current state. */ dobjFor(Push) { verify() { /* if it's already pushed, pushing it again makes no sense */ if (!isPulled) illogicalAlready(&alreadyPushedMsg); } action() { /* set the new state to pushed (i.e., not pulled) */ makePulled(nil); /* make the default report */ defaultReport(&okayPushLeverMsg); } } dobjFor(Pull) { verify() { /* if it's already pulled, pulling it again makes no sense */ if (isPulled) illogicalAlready(&alreadyPulledMsg); } action() { /* set the new state to pulled */ makePulled(true); /* make the default report */ defaultReport(&okayPullLeverMsg); } } dobjFor(Move) { verify() { } check() { /* run the check for pushing or pulling, as appropriate */ if (isPulled) checkDobjPush(); else checkDobjPull(); } action() { /* if we're pulled, push the lever; otherwise pull it */ if (isPulled) actionDobjPush(); else actionDobjPull(); } } ; /* * A spring-loaded lever is a lever that bounces back to its starting * position after being pulled. This is essentially equivalent in terms * of functionality to a button, but can at least provide superficial * variety. */ class SpringLever: Lever dobjFor(Pull) { action() { /* * Individual objects should override this to perform the * appropriate action when the lever is pulled. By default, * we'll do nothing except show a default report. */ defaultReport(&okayPullSpringLeverMsg); } } ; /* ------------------------------------------------------------------------ */ /* * An item that can be worn */ class Wearable: Thing /* is the item currently being worn? */ isWorn() { /* it's being worn if the wearer is non-nil */ return wornBy != nil; } /* * make the item worn by the given actor; if actor is nil, the item * isn't being worn by anyone */ makeWornBy(actor) { /* remember who's wearing the item */ wornBy = actor; } /* * An item being worn is not considered to be held in the wearer's * hands. */ isHeldBy(actor) { if (isWornBy(actor)) { /* it's being worn by the actor, so it's not also being held */ return nil; } else { /* * it's not being worn by this actor, so use the default * interpretation of being held */ return inherited(actor); } } /* * A wearable is not considered held by an actor when it is being * worn, so we must do a what-if test for removing the item if the * actor is currently wearing the item. If the actor isn't wearing * the item, we can use the default test of moving the item into the * actor's inventory. */ whatIfHeldBy(func, newLoc) { /* * If the article is being worn, and it's already in the same * location we're moving it to, simply test with the article no * longer being worn. Otherwise, inherit the default handling. */ if (location == newLoc && wornBy != nil) return whatIf(func, &wornBy, nil); else return inherited(func, newLoc); } /* * Try making the current command's actor hold me. If I'm already * directly in the actor's inventory and I'm being worn, we'll try a * 'doff' command; otherwise, we'll use the default handling. */ tryHolding() { /* * Try an implicit 'take' command. If the actor is carrying the * object indirectly, make the command "take from" instead, * since what we really want to do is take the object out of its * container. */ if (location == gActor && isWornBy(gActor)) return tryImplicitAction(Doff, self); else return inherited(); } /* * The object wearing this object, if any; if I'm not being worn, * this is nil. The wearer should always be a container (direct or * indirect) of this object - in order to wear something, you must * be carrying it. In most cases, the wearer should be the direct * container of the object. * * The reason we keep track of who's wearing the object (rather than * simply keeping track of whether it's being worn) is to allow for * cases where an actor is carrying another actor. Since this * object will be (indirectly) inside both actors in such cases, we * would have to inspect intermediate containers to determine * whether or not the outer actor was wearing the object if we * didn't keep track of the wearer directly. */ wornBy = nil /* am I worn by the given object? */ isWornBy(actor) { return wornBy == actor; } /* * An article of clothing that is being worn by an actor does not * typically encumber the actor at all, so by default we'll return * zero if we're being worn by the actor, and our normal bulk * otherwise. */ getEncumberingBulk(actor) { /* * if we're being worn by the actor, we create no encumbrance at * all; otherwise, return our normal bulk */ return isWornBy(actor) ? 0 : getBulk(); } /* * An article of clothing typically encumbers an actor with the same * weight whether or not the actor is wearing the item. However, * this might not apply to all objects; a suit of armor, for * example, might be slightly less encumbering in terms of weight * when worn than it is when held because the distribution of weight * is more manageable when worn. By default, we simply return our * normal weight, whether worn or not; subclasses can override as * needed to differentiate. */ getEncumberingWeight(actor) { return getWeight(); } /* get my state */ getState = (isWorn() ? wornState : unwornState) /* my list of possible states */ allStates = [wornState, unwornState] /* -------------------------------------------------------------------- */ /* * Action processing */ dobjFor(Wear) { preCond = [objHeld] verify() { /* make sure the actor isn't already wearing the item */ if (isWornBy(gActor)) illogicalAlready(&alreadyWearingMsg); } action() { /* make the item worn and describe what happened */ makeWornBy(gActor); defaultReport(&okayWearMsg); } } dobjFor(Doff) { preCond = [roomToHoldObj] verify() { /* * Make sure the actor is actually wearing the item. If * they're not, it's illogical, but if they are, it's an * especially likely thing to remove. */ if (!isWornBy(gActor)) illogicalAlready(¬WearingMsg); else logicalRank(150, 'worn'); } action() { /* un-wear the item and describe what happened */ makeWornBy(nil); defaultReport(&okayDoffMsg); } } /* "remove " is the same as "doff " */ dobjFor(Remove) asDobjFor(Doff) /* * if a wearable is being worn, showing it off to someone doesn't * require holding it */ dobjFor(ShowTo) { preCond() { /* get the standard handling */ local lst = inherited(); /* if we're being worn, don't require us to be held */ if (isWornBy(gActor)) lst -= objHeld; /* return the result */ return lst; } } ; /* ------------------------------------------------------------------------ */ /* * An item that can provide light. * * Any Thing can provide light, but this class should be used for * objects that explicitly serve as light sources from the player's * perspective. Objects of this class display a "providing light" * status message in inventory listings, and can be turned on and off * via the isLit property. */ class LightSource: Thing /* is the light source currently turned on? */ isLit = true /* * Turn the light source on or off. Note that we don't have to make * any special check for a change to the light level, because the * main action handler always checks for a change in light/dark * status over the course of the turn. */ makeLit(lit) { /* change the status */ isLit = lit; } /* * We can distinguish light sources according to their isLit status. * Give the lit/unlit distinction higher priority than the normal * ownership/containment distinction. */ distinguishers = [basicDistinguisher, litUnlitDistinguisher, ownershipDistinguisher, locationDistinguisher] /* the brightness that the object has when it is on and off */ brightnessOn = 3 brightnessOff = 0 /* * return the appropriate on/off brightness, depending on whether or * not we're currently lit */ brightness { return isLit ? brightnessOn : brightnessOff; } /* get our current state: lit or unlit */ getState = (brightness > 1 ? lightSourceStateOn : lightSourceStateOff) /* get our set of possible states */ allStates = [lightSourceStateOn, lightSourceStateOff] ; /* * A Flashlight is a special kind of light source that can be switched * on and off. * * To create a limited-use flashlight (with a limited battery life, for * example), you can combine this class with FueledLightSource. The * flashlight's on/off switch status is a separate property from its * lit/unlit light-source status, so combining Flashlight with * FueledLightSource will actually allow the two to become decoupled: a * flashlight can be on without providing light, when the battery is * dead. For this reason, you might want to override the decription, * and possibly the TurnOn action() handler, to customize the messages * for the case when the flashlight is switched on but out of power. */ class Flashlight: LightSource, Switch /* our switch status - start in the 'off' position */ isOn = nil /* * Change the on/off status. Note that switching the flashlight on * or off should always be done via makeOn - the makeLit inherited * from the LightSource should never be called directly on a * Flashlight object, because it doesn't keep the switch on/off and * flashlight lit/unlit status in sync. This routine is the one to * call because it keeps everything properly synchronized. */ makeOn(stat) { /* inherit the default handling */ inherited(stat); /* * Set the 'lit' status to track the on/off status. Note that * we don't simply do this by deriving isLit from isOn because * we want to invoke the side effects of changing the status by * calling makeLit explicitly. We also want to allow the two to * be decoupled when necessary, such as might happen when the * flashlight's bulb is burned out, or its battery has run down. */ makeLit(stat); } /* initialize */ initializeThing() { /* inherit default handling */ inherited(); /* * Make sure our initial isLit setting (for the LightSource) * matches our initial isOn steting (for the Switch). The * switch status drives the light source status, so initialize * the latter from the former. */ isLit = isOn; } /* treat 'light' and 'extinguish' as 'turn on' and 'turn off' */ dobjFor(Light) asDobjFor(TurnOn) dobjFor(Extinguish) asDobjFor(TurnOff) /* if we turn on the flashlight, but it doesn't light, mention this */ dobjFor(TurnOn) { action() { /* do the normal work */ inherited(); /* * If we're now on but not lit, mention this. This can * happen when we run out of power in the battery, or our * bulb is missing or burned out, or we're simply broken. */ if (isOn && !isLit) mainReport(&flashlightOnButDarkMsg); } } ; frobtads-1.2.3/tads3/lib/adv3/exits.t0000644000175000001440000003710610472355561016465 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2002, 2006 by Michael J. Roberts * * Based on exitslister.t, copyright 2002 by Steve Breslin and * incorporated by permission. * * TADS 3 Library - Exits Lister * * This module provides an automatic exit lister that shows the apparent * exits from the player character's location. The automatic exit lister * can optionally provide these main features: * * - An "exits" verb lets the player explicitly show the list of apparent * exits, along with the name of the room to which each exit connects. * * - Exits can be shown automatically as part of the room description. * This extra information can be controlled by the player through the * "exits on" and "exits off" command. * * - Exits can be shown automatically when an actor tries to go in a * direction where no exit exists, as a helpful reminder of which * directions are valid. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * The main exits lister. */ exitLister: PreinitObject /* preinitialization */ execute() { /* install myself as the global exit lister object */ gExitLister = self; } /* * Flag: use "verbose" listing style for exit lists in room * descriptions. When this is set to true, we'll show a * sentence-style list of exits ("Obvious exits lead east to the * living room, south, and up."). When this is set to nil, we'll use * a terse style, enclosing the message in the default system * message's brackets ("[Obvious exits: East, West]"). * * Verbose-style room descriptions tend to fit well with a room * description's prose, but at the expense of looking redundant with * the exit list that's usually built into each room's custom * descriptive text to begin with. Some authors prefer the terse * style precisely because it doesn't look like more prose * description, but looks like a separate bit of information being * offered. * * This is an author-configured setting; the library does not provide * a command to let the player control this setting. */ roomDescVerbose = nil /* * Flag: show automatic exit listings on attempts to move in * directions that don't allow travel. Enable this by default, * since most players appreciate having the exit list called out * separately from the room description (where any mention of exits * might be buried in lots of other text) in place of an unspecific * "you can't go that way". * * This is an author-configured setting; the library does not provide * a command to let the player control this setting. */ enableReminder = true /* * Flag: enable the automatic exit reminder even when the room * description exit listing is enabled. When this is nil, we will * NOT show a reminder with "can't go that way" messages when the * room description exit list is enabled - this is the default, * because it can be a little much to have the list of exits shown so * frequently. Some authors might prefer to show the reminder * unconditionally, though, so this option is offered. * * This is an author-configured setting; the library does not provide * a command to let the player control this setting. */ enableReminderAlways = nil /* * Flag: use hyperlinks in the directions mentioned in room * description exit lists, so that players can click on the direction * name in the listing to enter the direction command. */ enableHyperlinks = true /* flag: we've explained how the exits on/off command works */ exitsOnOffExplained = nil /* * Determine if the "reminder" is enabled. The reminder is the list * of exits we show along with a "can't go that way" message, to * reminder the player of the valid exits when an invalid one is * attempted. */ isReminderEnabled() { /* * The reminder is enabled if enableReminderAlways is true, OR if * enableReminder is true AND exitsMode.inRoomDesc is nil. */ return (enableReminderAlways || (enableReminder && !exitsMode.inRoomDesc)); } /* * Get the exit lister we use for room descriptions. */ getRoomDescLister() { /* use the verbose or terse lister, according to the configuration */ return roomDescVerbose ? lookAroundExitLister : lookAroundTerseExitLister; } /* perform the "exits" command to show exits on explicit request */ showExitsCommand() { /* show exits for the current actor */ showExits(gActor); /* * if we haven't explained how to turn exit listing on and off, * do so now */ if (!exitsOnOffExplained) { gLibMessages.explainExitsOnOff; exitsOnOffExplained = true; } } /* * Perform an EXITS ON/OFF/STATUS/LOOK command. 'stat' indicates * whether we're turning on (true) or off (nil) the statusline exit * listing; 'look' indicates whether we're turning the room * description listing on or off. */ exitsOnOffCommand(stat, look) { /* set the new status */ exitsMode.inStatusLine = stat; exitsMode.inRoomDesc = look; /* confirm the new status */ gLibMessages.exitsOnOffOkay(stat, look); /* * If we haven't already explained how the EXITS ON/OFF command * works, don't bother explaining it now, since they obviously * know how it works if they've actually used it. */ exitsOnOffExplained = true; } /* show the list of exits from an actor's current location */ showExits(actor) { /* show exits from the actor's location */ showExitsFrom(actor, actor.location); } /* show an exit list display in the status line, if desired */ showStatuslineExits() { /* if statusline exit displays are enabled, show the exit list */ if (exitsMode.inStatusLine) showExitsWithLister(gPlayerChar, gPlayerChar.location, statuslineExitLister, gPlayerChar.location .wouldBeLitFor(gPlayerChar)); } /* * Calculate the contribution of the exits list to the height of the * status line, in lines of text. If we're not configured to display * the exits list in the status line, then the contribution is zero; * otherwise, we'll estimate how much space we need to display the * exit list. */ getStatuslineExitsHeight() { /* * if we're enabled, our standard display takes up one line; if * we're disabled, we don't contribute anything to the status * line's vertical extent */ if (exitsMode.inStatusLine) return 1; else return 0; } /* show exits as part of a room description */ lookAroundShowExits(actor, loc, illum) { /* if room exit displays are enabled, show the exits */ if (exitsMode.inRoomDesc) showExitsWithLister(actor, loc, getRoomDescLister, illum); } /* show exits as part of a "cannot go that way" error */ cannotGoShowExits(actor, loc) { /* if we want to show the reminder, show it */ if (isReminderEnabled()) showExitsWithLister(actor, loc, explicitExitLister, loc.wouldBeLitFor(actor)); } /* show the list of exits from a given location for a given actor */ showExitsFrom(actor, loc) { /* show exits with our standard lister */ showExitsWithLister(actor, loc, explicitExitLister, loc.wouldBeLitFor(actor)); } /* * Show the list of exits using a specific lister. * * 'actor' is the actor for whom the display is being generated. * 'loc' is the location whose exit list is to be shown; this need * not be the same as the actor's current location. 'lister' is the * Lister object that will show the list of DestInfo objects that we * create to represent the exit list. * * 'locIsLit' indicates whether or not the ambient illumination, for * the actor's visual senses, is sufficient that the actor would be * able to see if the actor were in the new location. We take this * as a parameter so that we don't have to re-compute the * information if the caller has already computed it for other * reasons (such as showing a room description). If the caller * hasn't otherwise computed the value, it can be easily computed as * loc.wouldBeLitFor(actor). */ showExitsWithLister(actor, loc, lister, locIsLit) { local destList; local showDest; local options; /* * Ask the lister if it shows the destination names. We need to * know because we want to consolidate exits that go to the same * place if and only if we're going to show the destination in * the listing; if we're not showing the destination, there's no * reason to consolidate. */ showDest = lister.listerShowsDest; /* we have no option flags for the lister yet */ options = 0; /* run through all of the directions used in the game */ destList = new Vector(Direction.allDirections.length()); foreach (local dir in Direction.allDirections) { local conn; /* * If the actor's location has a connector in this * direction, and the connector is apparent, add it to the * list. * * If the actor is in the dark, we can only see the * connector if the connector is visible in the dark. If * the actor isn't in the dark, we can show all of the * connectors. */ if ((conn = loc.getTravelConnector(dir, actor)) != nil && conn.isConnectorApparent(loc, actor) && conn.isConnectorListed && (locIsLit || conn.isConnectorVisibleInDark(loc, actor))) { local dest; local destName = nil; local destIsBack; /* * We have an apparent connection in this direction, so * add it to our list. First, check to see if we know * the destination. */ dest = conn.getApparentDestination(loc, actor); /* note if this is the "back to" connector for the actor */ destIsBack = (conn == actor.lastTravelBack); /* * If we know the destination, and they want to include * destination names where possible, get the name. If * there's a name to show, include the name. */ if (dest != nil && showDest && (destName = dest.getDestName(actor, loc)) != nil) { local orig; /* * we are going to show a destination name for this * item, so set the special option flag to let the * lister know that this is the case */ options |= ExitLister.hasDestNameFlag; /* * if this is the back-to connector, note that we * know the name of the back-to location */ if (destIsBack) options |= ExitLister.hasBackNameFlag; /* * If this destination name already appears in the * list, don't include this one in the list. * Instead, add this direction to the 'others' list * for the existing entry, so that we will show each * known destination only once. */ orig = destList.valWhich({x: x.dest_ == dest}); if (orig != nil) { /* * this same destination name is already present * - add this direction to the existing entry's * list of other directions going to the same * place */ orig.others_ += dir; /* * if this is the back-to connector, note it in * the original destination item */ if (destIsBack) orig.destIsBack_ = true; /* * don't add this direction to the main list, * since we don't want to list the destination * redundantly */ continue; } } /* add it to our list */ destList.append(new DestInfo(dir, dest, destName, destIsBack)); } } /* show the list */ lister.showListAll(destList.toList(), options, 0); } ; /* * A destination tracker. This keeps track of a direction and the * apparent destination in that direction. */ class DestInfo: object construct(dir, dest, destName, destIsBack) { /* remember the direction, destination, and destination name */ dir_ = dir; dest_ = dest; destName_ = destName; destIsBack_ = destIsBack; } /* the direction of travel */ dir_ = nil /* the destination room object */ dest_ = nil /* the name of the destination */ destName_ = nil /* flag: this is the "back to" destination */ destIsBack_ = nil /* list of other directions that go to our same destination */ others_ = [] ; /* * Settings item - show defaults in status line */ exitsMode: SettingsItem /* our ID */ settingID = 'adv3.exits' /* show our description */ settingDesc = (gLibMessages.currentExitsSettings(inStatusLine, inRoomDesc)) /* convert to text */ settingToText() { /* just return the two binary variables */ return (inStatusLine ? 'on' : 'off') + ',' + (inRoomDesc ? 'on' : 'off'); } settingFromText(str) { /* parse out our format */ if (rexMatch('*(+)*,*(+)', str.toLower()) != nil) { /* pull out the two variables from the regexp groups */ inStatusLine = (rexGroup(1)[3] == 'on'); inRoomDesc = (rexGroup(2)[3] == 'on'); } } /* * Our value is in two parts. inStatusLine controls whether or not * we show the exit list in the status line; inRoomDesc controls the * exit listing in room descriptions. */ inStatusLine = true inRoomDesc = nil ; frobtads-1.2.3/tads3/lib/adv3/precond.t0000644000175000001440000012114711465246012016753 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: Pre-Conditions. * * This module defines the library pre-conditions. A pre-condition is an * abstract object that encapsulates a condition that is required to * apply before a command can be executed, and optionally an implied * command that can bring the condition into effect. Pre-conditions can * be associated with actions or with the objects of an action. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * An action pre-condition object. Each condition of an action is * represented by a subclass of this class. */ class PreCondition: object /* * Check the condition on the given object (which may be nil, if * this condition doesn't apply specifically to one of the objects * in the command). If it is possible to meet the condition with an * implicit command, and allowImplicit is true, try to execute the * command. If the condition cannot be met, report a failure and * use 'exit' to terminate the command. * * If allowImplicit is nil, an implicit command may not be * attempted. In this case, if the condition is not met, we must * simply report a failure and use 'exit' to terminate the command. */ checkPreCondition(obj, allowImplicit) { } /* * Verify the condition. This is called during the object * verification step so that the pre-condition can add verifications * of its own. This can be used, for example, to add likelihood to * objects that already meet the condition. Note that it is * generally not desirable to report illogical for conditions that * checkPreCondition() enforces, because doing so will prevent * checkPreCondition() from ever being reached and thus will prevent * checkPreCondition() from attempting to carry out implicit actions * to meet the condition. * * 'obj' is the object being checked. Note that because this is * called during verification, the explicitly passed-in object must * be used in the check rather than the current object in the global * current action. */ verifyPreCondition(obj) { } /* * Precondition execution order. When we execute preconditions for a * given action, we'll sort the list of all applicable preconditions * in ascending execution order. * * For the most part, the relative order of two preconditions is * arbitrary. In some unusual cases, though, the order is important, * such as when applying one precondition can destroy the conditions * that the other would try to create but not vice versa. When the * order doesn't matter, this can be left at the default setting. */ preCondOrder = 100 ; /* ------------------------------------------------------------------------ */ /* * A pre-condition that applies to a specific, pre-determined object, * rather than the direct/indirect object of the command. */ class ObjectPreCondition: PreCondition construct(obj, cond) { /* * remember the specific object I act upon, and the underlying * precondition to apply to that object */ obj_ = obj; cond_ = cond; } /* route our check to the pre-condition using our specific object */ checkPreCondition(obj, allowImplicit) { /* check the precondition */ return cond_.checkPreCondition(obj_, allowImplicit); } /* route our verification check to the pre-condition */ verifyPreCondition(obj) { cond_.verifyPreCondition(obj_); } /* use the same order as our underlying condition */ preCondOrder = (cond_.preCondOrder) /* the object we check with the condition */ obj_ = nil /* the pre-condition we check */ cond_ = nil ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: object must be visible. This condition doesn't * attempt any implied command to make the object visible, but merely * enforces visibility before allowing the command. * * This condition is useful for commands that rely on visibly inspecting * the object, such as "examine" or "look in". It is possible for an * object to be in scope without being visible, since an object can be * in scope by way of a non-visual sense. * * We enforce visibility with a verification test, not a precondition * check. */ objVisible: PreCondition verifyPreCondition(obj) { /* if the object isn't visible, disallow the command */ if (obj != nil && !gActor.canSee(obj)) { /* * If the actor is in the dark, that must be the problem. * Otherwise, if the object can be heard or smelled but not * seen, say so. In any other case, issue a generic message * that we can't see the object. */ if (!gActor.isLocationLit()) inaccessible(&tooDarkMsg); else if (obj.soundPresence && gActor.canHear(obj)) inaccessible(&heardButNotSeenMsg, obj); else if (obj.smellPresence && gActor.canSmell(obj)) inaccessible(&smelledButNotSeenMsg, obj); else inaccessible(&mustBeVisibleMsg, obj); } } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: object must be audible; that is, it must be within * hearing range of the actor. This condition doesn't attempt any * implied command to make the object audible, but merely enforces * audibility before allowing the command. * * It is possible for an object to be in scope without being audible, * since an object can be inside a container that is transparent to * light but blocks all sound. * * We enforce this condition with a verification test. */ objAudible: PreCondition verifyPreCondition(obj) { /* if the object isn't audible, disallow the command */ if (obj != nil && !gActor.canHear(obj)) inaccessible(&cannotHearMsg, obj); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: object must be within smelling range of the actor. * This condition doesn't attempt any implied command to make the object * smellable, but merely enforces the condition before allowing the * command. * * It is possible for an object to be in scope without being smellable, * since an object can be inside a container that is transparent to * light but blocks all odors. * * We enforce this condition with a verification test. */ objSmellable: PreCondition verifyPreCondition(obj) { /* if the object isn't within sense range, disallow the command */ if (obj != nil && !gActor.canSmell(obj)) inaccessible(&cannotSmellMsg, obj); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: actor must be standing. This is useful for travel * commands to ensure that the actor is free of any entanglements from * nested rooms prior to travel. */ actorStanding: PreCondition checkPreCondition(obj, allowImplicit) { /* check to see if the actor is standing - if so, we're done */ if (gActor.posture == standing) return nil; /* the actor isn't standing - try a "stand up" command */ if (allowImplicit && tryImplicitAction(Stand)) { /* * make sure that leaves the actor standing - if not, * exit silently, since the reason for failure will have * been reported by the "stand up" action */ if (gActor.posture != standing) exit; /* indicate that we executed an implicit command */ return true; } /* we can't stand up implicitly - report the problem and exit */ reportFailure(&mustBeStandingMsg); exit; } ; /* * Pre-condition: actor must be "travel ready." The exact meaning of * "travel ready" is provided by the actor's immediately container. The * 'obj' argument is always the travel connector to be traversed. */ actorTravelReady: PreCondition checkPreCondition(obj, allowImplicit) { local loc = gActor.location; /* check to see if the actor is standing - if so, we're done */ if (loc.isActorTravelReady(obj)) return nil; /* the actor isn't standing - try a "stand up" command */ if (allowImplicit && gActor.location.tryMakingTravelReady(obj)) { /* * make sure that the actor really is travel-ready now - if * not, exit silently, since the reason for failure will have * been reported by the implicit action */ if (!loc.isActorTravelReady(obj)) exit; /* indicate that we executed an implicit command */ return true; } /* we can't make the actor travel-ready - report failure and exit */ reportFailure(loc.notTravelReadyMsg); exit; } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the traveler is directly in the given room. This will * attempt to remove the traveler from any nested rooms within the given * room, but cannot perform travel between rooms not related by * containment. * * Note that the traveler is not necessarily the actor, because the actor * could be in a vehicle. * * This is a class, because it has to be instantiated with more * parameters than just a single 'obj' passed by default when evaluating * preconditions. In particular, we need to know the actor performing * the travel, the connector being traversed, and the room we need to be * directly in. */ class TravelerDirectlyInRoom: PreCondition construct(actor, conn, loc) { /* remember the actor, connector, and room */ actor_ = actor; conn_ = conn; loc_ = loc; } checkPreCondition(obj, allowImplicit) { /* ask the traveler to do the work */ return actor_.getTraveler(conn_) .checkDirectlyInRoom(loc_, allowImplicit); } /* the actor doing the travel */ actor_ = nil /* the connector being traversed */ conn_ = nil /* the room we need to be directly in */ loc_ = nil ; /* * Pre-condition: the actor is directly in the given room. This differs * from TravelerDirectlyInRoom in that this operates directly on the * actor, regardless of whether the actor is in a vehicle. */ actorDirectlyInRoom: PreCondition checkPreCondition(obj, allowImplicit) { /* ask the actor to do the work */ return gActor.checkDirectlyInRoom(obj, allowImplicit); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: actor is ready to enter a nested location. This is * useful for commands that cause travel within a location, such as "sit * on chair": this ensures that the actor is either already in the given * nested location, or is in the main location; and that the actor is * standing. We simply call the actor to do the work. */ actorReadyToEnterNestedRoom: PreCondition checkPreCondition(obj, allowImplicit) { /* ask the actor to make the determination */ return gActor.checkReadyToEnterNestedRoom(obj, allowImplicit); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the target actor must be able to talk to the object. * This is useful for actions that require communications, such as ASK * ABOUT, TELL ABOUT, and TALK TO. */ canTalkToObj: PreCondition checkPreCondition(obj, allowImplicit) { /* * if the current actor can't talk to the given object, disallow * the command */ if (obj != nil && !gActor.canTalkTo(obj)) { reportFailure(&objCannotHearActorMsg, obj); exit; } /* we don't perform any implicit commands */ return nil; } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: object must be held. This condition requires that an * object of a command must be held by the actor. If it is not, we will * attempt a recursive "take" command on the object. * * This condition is useful for commands where the object is to be * manipulated in some way, or used to manipulate some other object. * For example, the key in "unlock door with key" would normally have to * be held. */ objHeld: PreCondition checkPreCondition(obj, allowImplicit) { /* if the object is already held, there's nothing we need to do */ if (obj == nil || obj.meetsObjHeld(gActor)) return nil; /* the object isn't being held - try an implicit 'take' command */ if (allowImplicit && obj.tryHolding()) { /* * we successfully executed the command; check to make sure * it worked, and if not, abort the command without further * comment (if the command failed, presumably the command * showed an explanation as to why) */ if (!obj.meetsObjHeld(gActor)) exit; /* tell the caller we executed an implicit command */ return true; } /* it's not held and we can't take it - fail */ reportFailure(&mustBeHoldingMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } /* lower the likelihood rating for anything not being held */ verifyPreCondition(obj) { /* if the object isn't being held, reduce its likelihood rating */ if (obj != nil && !obj.meetsObjHeld(gActor)) logicalRankOrd(80, 'implied take', 150); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: a given source object must be able to touch the * object. This requires that the source object (given by our property * 'sourceObj') has a clear 'touch' path to the target object. * * This is a base class for arbitrary object-to-object touch conditions. * In most cases, you'll want to use the more specific touchObj, which * tests that the current actor can touch the current object. */ class TouchObjCondition: PreCondition /* construct with a given source object */ construct(src) { sourceObj = src; } /* * the source object - this is the object that is attempting to * touch the target object */ sourceObj = nil /* check the condition */ checkPreCondition(obj, allowImplicit) { local pastObs; /* * If we can touch the object, we can proceed with no implicit * actions. */ if (sourceObj.canTouch(obj)) return nil; /* we haven't tried removing any obstructors yet */ pastObs = new Vector(8); /* * Repeatedly look for and attempt to remove obstructions. * There could be multiple things in the way, so try to remove * each one we find until either we fail to remove an * obstruction or we run out of obstructions. */ for (;;) { local stat; local path; local result; local obs; /* get the path for reaching out and touching the object */ path = sourceObj.getTouchPathTo(obj); /* if we have a path, look for an obstructor */ if (path != nil) { /* traverse the path to find what blocks our touch */ stat = sourceObj.traversePath(path, function(ele, op) { /* * If we can continue the reach via this path element, * simply keep going. Otherwise, stop the reach here. */ result = ele.checkTouchViaPath(sourceObj, obj, op); if (result.isSuccess) { /* no objection here - keep going */ return true; } else { /* stop here, noting the obstruction */ obs = ele; return nil; } }); /* * if we now have a clear path, we're done - simply return * true to indicate that we ran one or more implicit * commands */ if (stat) return true; } else { /* * we have no path, so the object must be in an * unconnected location; we don't know the obstructor in * this case */ obs = nil; } /* * 'result' is a CheckStatus object explaining why we can't * reach past 'obs', which is the first object that * obstructs our reach. * * If the obstructor is not visible or we couldn't find one, * we can't do anything to try to remove it; simply report * that we can't reach the target object and give up. */ if (obs == nil || !gActor.canSee(obs)) { reportFailure(&cannotReachObjectMsg, obj); exit; } /* * Ask the obstructor to get out of the way if possible. * * If we've already tried to remove this same obstructor on * a past iteration, don't try again, as there's no reason * to think an implicit command will work any better this * time. */ if (pastObs.indexOf(obs) != nil || !allowImplicit || !obs.tryImplicitRemoveObstructor(touch, obj)) { /* * We can't remove the obstruction - either we've tried * an implicit command on this same obstructor and * failed, or we can't try an implicit command at all. * In any case, use the explanation of the problem from * the CheckStatus result object. */ reportFailure(result.msgProp, result.msgParams...); exit; } /* * if the implied command failed, simply give up now - * there's no need to go on, since the implied command will * have already explained why it failed */ if (gTranscript.currentActionHasReport({x: x.isFailure})) exit; /* * We've tried an implied command to remove this obstructor, * but that isn't guaranteed to make the target touchable, * as there could be further obstrutions, or the implied * command could have failed to actually remove the * obstruction. Keep iterating. To avoid looping forever * in the event the implicit command we just tried isn't * good enough to remove this obstruction, make a note of * the obstruction we just tried to remove; if we find it * again on a subsequent iteration, we'll know that we've * tried before to remove it and failed, and thus we'll know * to give up without making the same doomed attempt again. */ pastObs.append(obs); } } verifyPreCondition(obj) { /* * If there's no source object, do nothing at this point. We can * have a nil source object when we're resolving nouns for a * two-object action, and we have a cross-object condition (for * example, we require that the indirect object can touch the * direct object). In these cases, when we're resolving the * first-resolved noun phrase, the second-resolved noun phrase * won't be known yet. The only purpose of verification at times * like these is to improve our guess about an ambiguous match, * but we have nothing to add at such times, so we can simply * return without doing anything. */ if (sourceObj == nil) return; /* if we can't touch the object, make it less likely */ if (!sourceObj.canTouch(obj)) { /* * If we can't see the object, we must be able to sense it * by some means other than sight, so it must have a * sufficiently distinctive sound or odor to put it in * scope. Explain this: "you can hear it but you can't see * it", or the like. */ if (gActor.canSee(obj)) { local info; /* * It's visible but cannot be reached from here, so it * must be too far away, inside a closed but transparent * container, or something like that. * * If it's at a distance, rule it illogical, since * there's not usually anything automatic we can do to * remove the distance obstruction. * * If it's not distant, don't rule it illogical, but do * reduce the likelihood ranking, so that we'll prefer a * different object that can readily be touched. Since * we can see where the object is, we might know how to * remove the obstruction to reachability. */ info = gActor.bestVisualInfo(obj); if (info != nil && info.trans == distant) { /* it's distant - assume we can't fix this */ inaccessible(&tooDistantMsg, obj); } else { /* * it's not distant; rank it logical (since we might * be able to clear the obstruction with an implied * action), but at reduced likelihood (in case * there's something that doesn't need any prior * implied action to reach) */ logicalRankOrd(80, 'unreachable but visible', 150); } } else { /* * if it has a sound presence, then "you can hear it but * you can't see it"; if it has a smell presence, then * "you can smell it but you can't see it"; otherwise, * you simply can't see it */ if (obj.soundPresence && gActor.canHear(obj)) { /* it can be heard but not seen */ inaccessible(&heardButNotSeenMsg, obj); } else if (obj.smellPresence && gActor.canSmell(obj)) { /* it can be smelled but not seen */ inaccessible(&smelledButNotSeenMsg, obj); } else if (!gActor.isLocationLit()) { /* it's too dark to see the object */ inaccessible(&tooDarkMsg); } else { /* it simply cannot be seen */ inaccessible(&mustBeVisibleMsg, obj); } } } } /* * This condition tends to be fragile, in the sense that other * preconditions for the same action have the potential to undo any * implicit action that we perform to make an object touchable. This * is most likely to happen when we implicitly move the actor (moving * in or out of a nested room, for example) to put the actor within * reach of the target object. To reduce the likelihood that this * fragility will be visible to a player, try to execute this * condition after other conditions. Most other preconditions tend * to be "stickier" - less likely to be undone by subsequent * preconditions. */ preCondOrder = 200 ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: actor must be able to touch the object. This doesn't * require that the actor is actually holding the object, but the actor * must be able to physically touch the object. This ensures that the * actor and object are not, for example, separated by a transparent * barrier. * * If there is a transparent barrier, we will attempt to remove the * barrier by calling the barrier object's tryImplicitRemoveObstructor * method. Objects that can be opened in an obvious fashion will * perform an implicit recursive "open" command, and other types of * objects can provide customized behavior as appropriate. */ touchObj: TouchObjCondition /* we want to test reaching from the current actor to the target object */ sourceObj = (gActor) ; /* * Pre-condition: the indirect object must be able to touch the target * object. This can be used for actions where the direct object is going * to be manipulated by an "agent" of the action (i.e., the indirect * object), rather than directly by the actor: MOVE X WITH Y, for * example. * * Note that the target object of this condition should be the direct * object in most cases, so this condition should usually be used like * this: * * dobjFor(MoveWith) { preCond = [iobjTouchObj] } * * In other words, this is a precondition that we apply in most cases to * the *direct* object. */ iobjTouchObj: TouchObjCondition /* the indirect object has to be able to touch the target object */ sourceObj = (gIobj) ; /* * Pre-condition: the direct object can touch the target object. This * is useful for situations where the direct object is being manipulated * directly and the indirect object is more of a passive participant in * the action, such as PLUG CORD INTO OUTLET. */ dobjTouchObj: TouchObjCondition /* the direct object has to be able to touch the target object */ sourceObj = (gDobj) ; /* ------------------------------------------------------------------------ */ /* * A precondition ensuring that the target object is in the same * immediate location as a given object. */ class SameLocationCondition: PreCondition /* * construct dynamically, setting the other object whose location we * must match */ construct(obj) { sourceObj = obj; } /* the object whose location we must match */ sourceObj = nil /* check the condition */ checkPreCondition(obj, allowImplicit) { local moveObj; local targetLoc; /* if we're in the same container, we're fine */ if (obj.location == sourceObj.location) return nil; /* * Pick an object to move. By default, pick the target object; * but if the target object is non-portable, then trying to move * it will fail, so pick the source object. */ if (obj.ofKind(NonPortable)) { /* 'obj' is unportable, so try moving the source object */ moveObj = sourceObj; targetLoc = obj.location; } else { /* 'obj' is portable, so try moving it by default */ moveObj = obj; targetLoc = sourceObj.location; } /* try moving the object, and return the result */ if (allowImplicit && targetLoc.tryMovingObjInto(moveObj)) { /* if it didn't work, abort the action */ if (obj.location != sourceObj.location) exit; /* tell the caller we executed an implied action */ return true; } /* we can't move it - report the failure and abort the action */ targetLoc.mustMoveObjInto(moveObj); exit; } ; /* * require that the target object be in the same immediate location as * the direct object */ sameLocationAsDobj: SameLocationCondition sourceObj = (gDobj) ; /* * require that the target object be in the same immediate location as * the indirect object */ sameLocationAsIobj: SameLocationCondition sourceObj = (gIobj) ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: actor must have room to hold the object directly (such * as in the actor's hands). We'll let the actor do the work. */ roomToHoldObj: PreCondition checkPreCondition(obj, allowImplicit) { /* let the actor check the precondition */ return gActor.tryMakingRoomToHold(obj, allowImplicit); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the actor must not be wearing the object. If the * actor is currently wearing the object, we'll try asking the actor to * doff the object. * * Note that this pre-condition never needs to be combined with objHeld, * because an object being worn is not considered to be held, and * Wearable implicitly doffs an article when it must be held. */ objNotWorn: PreCondition checkPreCondition(obj, allowImplicit) { /* if the object isn't being worn, we have nothing to do */ if (obj == nil || !obj.isWornBy(gActor)) return nil; /* try an implicit 'doff' command */ if (allowImplicit && tryImplicitAction(Doff, obj)) { /* * we executed the command - make sure it worked, and abort * if it didn't */ if (obj.isWornBy(gActor)) exit; /* tell the caller we executed an implicit command */ return true; } /* report the problem and terminate the command */ reportFailure(&cannotBeWearingMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } /* lower the likelihood rating for anything being worn */ verifyPreCondition(obj) { /* if the object is being worn, reduce its likelihood rating */ if (obj != nil && obj.isWornBy(gActor)) logicalRankOrd(80, 'implied doff', 150); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the object is open. */ class ObjOpenCondition: PreCondition checkPreCondition(obj, allowImplicit) { /* if the object is already open, we're already done */ if (obj == nil || obj.isOpen) return nil; /* try an implicit 'open' command on the object */ if (allowImplicit && tryImplicitAction(Open, obj)) { /* * we executed the command - make sure it worked, and abort * if it didn't */ if (!obj.isOpen) exit; /* tell the caller we executed an implied command */ return true; } /* can't open it implicitly - report the failure */ conditionFailed(obj); exit; } /* * The condition failed - report the failure and give up. We * separate this to allow subclasses to report failure differently * for specialized types of opening. */ conditionFailed(obj) { /* can't open it implicitly - report failure and give up */ reportFailure(&mustBeOpenMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); } /* reduce the likelihood rating for anything that isn't already open */ verifyPreCondition(obj) { /* if the object is closed, reduce its likelihood rating */ if (obj != nil && !obj.isOpen) logicalRankOrd(80, 'implied open', 150); } ; /* * The basic object-open condition */ objOpen: ObjOpenCondition; /* * Pre-condition: a door must be open. This differs from the regular * objOpen condition only in that we use a customized version of the * failure report. */ doorOpen: ObjOpenCondition conditionFailed(obj) { /* * We can generate implicit open-door commands as a result of * travel, which means that the actor issuing the command might * never have explicitly referred to the door. (This is not the * case for most preconditions, which refer to objects directly * used in the command and thus within the actor's awareness, at * least initially.) So, if the door isn't visible to the * actor, don't tell the actor they have to open the door; * instead, just show the standard no-travel message for the * door. */ if (gActor.canSee(obj)) { /* they can see the door, so tell them they need to open it */ reportFailure(&mustOpenDoorMsg, obj); /* set this as the pronoun antecedent */ gActor.setPronounObj(obj); } else { /* * they can't see the door - call the door's routine to * indicate that travel is not possible */ obj.cannotTravel(); } } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the object is closed. */ objClosed: PreCondition checkPreCondition(obj, allowImplicit) { /* if the object is already closed, we're already done */ if (obj == nil || !obj.isOpen) return nil; /* try an implicit 'close' command on the object */ if (allowImplicit && tryImplicitAction(Close, obj)) { /* * we executed the command - make sure it worked, and abort * if it didn't */ if (obj.isOpen) exit; /* tell the caller we executed an implied command */ return true; } /* can't close it implicitly - report failure and give up */ reportFailure(&mustBeClosedMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } /* reduce the likelihood rating for anything that isn't already closed */ verifyPreCondition(obj) { /* if the object is closed, reduce its likelihood rating */ if (obj != nil && obj.isOpen) logicalRankOrd(80, 'implied close', 150); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the object is unlocked. */ objUnlocked: PreCondition checkPreCondition(obj, allowImplicit) { /* if the object is already unlocked, we're already done */ if (obj == nil || !obj.isLocked) return nil; /* try an implicit 'unlock' command on the object */ if (allowImplicit && tryImplicitAction(Unlock, obj)) { /* * we executed the command - make sure it worked, and abort * if it didn't */ if (obj.isLocked) exit; /* tell the caller we executed an implied command */ return true; } /* can't unlock it implicitly - report failure and give up */ reportFailure(&mustBeUnlockedMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } /* reduce the likelihood rating for anything that's locked */ verifyPreCondition(obj) { /* if the object is locked, reduce its likelihood rating */ if (obj != nil && obj.isLocked) logicalRankOrd(80, 'implied unlock', 150); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: destination for "drop" is an outermost room. If the * drop destination is a nested room, we'll try returning the actor to * the outermost room via an implicit command. */ dropDestinationIsOuterRoom: PreCondition checkPreCondition(obj, allowImplicit) { local dest; /* * if the actor's location's drop location is the outermost * room, we don't need to do anything special */ dest = gActor.getDropDestination(obj, nil); if (dest.getOutermostRoom() == dest) return nil; /* * the default drop destination is not an outermost room; try an * implicit command to return the actor to an outermost room */ return actorDirectlyInRoom.checkPreCondition( dest.getOutermostRoom(), allowImplicit); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: object is burning. This can be used for matches, * candles, and the like. If the object's isLit is nil, we'll attempt a * "burn" command on the object. */ objBurning: PreCondition checkPreCondition(obj, allowImplicit) { /* if it's already burning, there's nothing to do */ if (obj == nil || obj.isLit) return nil; /* try an implicit 'burn' command */ if (allowImplicit && tryImplicitAction(Burn, obj)) { /* we executed a 'burn' - give up if it didn't work */ if (!obj.isLit) exit; /* tell the caller we executed an implied command */ return true; } /* we can't burn it implicitly - report failure and give up */ reportFailure(&mustBeBurningMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } verifyPreCondition(obj) { /* if the object is not already burning, reduce its likelihood */ if (obj != nil && !obj.isLit) logicalRankOrd(80, 'implied burn', 150); } ; /* ------------------------------------------------------------------------ */ /* * Pre-condition: the object is empty. This ensures that the object * does not contain any other objects. * * Note that we unconditionally try to remove all objects. If a * container needs to have some objects that can be removed and others * that can't (such as components within the container), then the * container will have to be implemented as a ComplexContainer - the * non-removable components should be made contents of the enclosing * ComplexContainer, and the secret inner container should be the one * subject to this precondition. */ objEmpty: PreCondition checkPreCondition(obj, allowImplicit) { local chi; /* * if there's no object, or the object already has no contents, * there's nothing to do */ if (obj == nil || obj.contents.length() == 0) return nil; /* * Try an implicit 'take x' on the object's first child. * * Note that we only try this on the first object, because the * precondition mechanism automatically re-applies all * preconditions after any one of them performs an implied * command. If we have multiple objects that must be removed, * that basic loop will ensure that we'll come back here as many * times as necessary. */ chi = obj.contents[1]; if (allowImplicit && tryImplicitAction(TakeFrom, chi, obj)) { /* make sure it worked */ if (chi.isIn(obj)) exit; /* tell the caller we tried an implied command */ return true; } /* we can't remove the objects implicitly, so give up */ reportFailure(&mustBeEmptyMsg, obj); /* make it the pronoun */ gActor.setPronounObj(obj); /* abort the command */ exit; } ; frobtads-1.2.3/tads3/lib/adv3/sense.t0000644000175000001440000007326111465246012016441 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - senses * * This module defines objects and functions related to senses. This * file is language-independent. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Material: the base class for library objects that specify the way * senses pass through objects. */ class Material: object /* * Determine how a sense passes through the material. We'll return * a transparency level. (Individual materials should not need to * override this method, since it simply dispatches to the various * xxxThru methods.) */ senseThru(sense) { /* dispatch to the xxxThru method for the sense */ return self.(sense.thruProp); } /* * For each sense, each material must define an appropriate xxxThru * property that returns the transparency level for that sense * through the material. Any xxxThru property not defined in an * individual material defaults to opaque. */ seeThru = opaque hearThru = opaque smellThru = opaque touchThru = opaque ; /* * Adventium is the basic stuff of the game universe. This is the * default material for any object that doesn't specify a different * material. This type of material is opaque to all senses. */ adventium: Material seeThru = opaque hearThru = opaque smellThru = opaque touchThru = opaque ; /* * Paper is opaque to sight and touch, but allows sound and smell to * pass. */ paper: Material seeThru = opaque hearThru = transparent smellThru = transparent touchThru = opaque ; /* * Glass is transparent to light, but opaque to touch, sound, and smell. */ glass: Material seeThru = transparent hearThru = opaque smellThru = opaque touchThru = opaque ; /* * Fine Mesh is transparent to all senses except touch. */ fineMesh: Material seeThru = transparent hearThru = transparent smellThru = transparent touchThru = opaque ; /* * Coarse Mesh is transparent to all senses, including touch, but * doesn't allow large objects to pass through. */ coarseMesh: Material seeThru = transparent hearThru = transparent smellThru = transparent touchThru = transparent ; /* ------------------------------------------------------------------------ */ /* * Sense: the basic class for senses. */ class Sense: object /* * Each sense must define the property thruProp as a property * pointer giving the xxxThru property for the sense. The xxxThru * property is the property of a material which determines how the * sense passes through that material. */ thruProp = nil /* * Each sense must define the property sizeProp as a property * pointer giving the xxxSize property for the sense. The xxxSize * property is the property of a Thing which determines how "large" * the object is with respect to the sense. For example, sightSize * indicates how large the object is visually, while soundSize * indicates how loud the object is. * * The purpose of an object's size in a given sense is to determine * how well the object can be sensed through an obscuring medium or * at a distance. */ sizeProp = nil /* * Each sense must define the property presenceProp as a property * pointer giving the xxxPresence property for the sense. The * xxxPresence property is the property of a Thing which determines * whether or not the object has a "presence" in this sense, which is * to say whether or not the object is emitting any detectable * sensory data for the sense. For example, soundPresence indicates * whether or not a Thing is making any noise. * * The sensory presence is used to determine if an object is in * scope. An object with a detectable sensory presence is normally * in scope. Note that sounds and smells emitted by a tangible * object are frequently represented as additional intangible * objects, and in these cases the intangible object (the sensory * emanation) is usually the object with a sensory presence, rather * than the tangible object making the noise/odor. However, it is * sometimes obvious that a particular sound or odor is coming from a * particular kind of object, so the presence of the sound or odor * implies the presence of the source object and thus places the * source object in scope. In such cases, it is desirable for the * source object to have a sensory presence of its own, in addition * to the sensory presence of the intangible sensory emanation * object. * * Note that the "presence" doesn't have any effect on whether or not * an object can be sensed. Only the sense path matters for that: an * object without a presence can still be sensed if there's a * non-opaque sense path to the object. Presence only determines * whether or not an object is *actively* calling attention to * itself. */ presenceProp = nil /* * Each sense can define this property to specify a property pointer * used to define a Thing's "ambient" energy emissions. Senses * which do not use ambient energy should define this to nil. * * Some senses work only on directly emitted sensory data; human * hearing, for example, has no (at least effectively no) use for * reflected sound, and can sense objects only by the sounds they're * actually emitting. Sight, on the other hand, can make use not * only of light emitted by an object but of light reflected by the * object. So, sight defines an ambience property, whereas hearing, * touch, and smell do not. */ ambienceProp = nil /* * Determine if, in general, the given object can be sensed under * the given conditions. Returns true if so, nil if not. By * default, if the ambient level is zero, we'll return nil; * otherwise, if the transparency level is 'transparent', we'll * return true; otherwise, we'll consult the object's size: * * - Small objects cannot be sensed under less than transparent * conditions. * * - Medium or large objects can be sensed in any conditions other * than opaque. */ canObjBeSensed(obj, trans, ambient) { /* * if we use "reflected" energy, and the ambient energy level is * zero, we can't sense it */ if (ambienceProp != nil && ambient == 0) return nil; /* check the transparency level */ switch(trans) { case transparent: case attenuated: /* * we can always sense under transparent or attenuated * conditions */ return true; case distant: case obscured: /* * we can only sense medium and large objects under less * than transparent conditions */ return obj.(self.sizeProp) != small; default: /* we can never sense under other conditions */ return nil; } } ; /* * The senses. We define sight, sound, smell, and touch. We do not * define a separate sense for taste, since it would add nothing to our * model: you can taste something if and only if you can touch it. * * To add a new sense, you must do the following: * * - Define the sense object itself, in parallel to the senses defined * below. * * - Modify class Material to set the default transparency level for * this sense by defining the property xxxThru - for most senses, the * default transparency level is 'opaque', but you must decide on the * appropriate default for your new sense. * * - Modify class Thing to set the default xxxSize setting, if desired. * * - Modify class Thing to set the default xxxPresence setting, if * desired. * * - Modify each instance of class 'Material' that should have a * non-default transparency for the sense by defining the property * xxxThru for the material. * * - Modify class Actor to add the sense to the default mySenses list; * this is only necessary if the sense is one that all actors should * have by default. */ sight: Sense thruProp = &seeThru sizeProp = &sightSize presenceProp = &sightPresence ambienceProp = &brightness ; sound: Sense thruProp = &hearThru sizeProp = &soundSize presenceProp = &soundPresence ; smell: Sense thruProp = &smellThru sizeProp = &smellSize presenceProp = &smellPresence ; touch: Sense thruProp = &touchThru sizeProp = &touchSize presenceProp = &touchPresence /* * Override canObjBeSensed for touch. Unlike other senses, touch * requires physical contact with an object, so it cannot operate at * a distance, regardless of the size of an object. */ canObjBeSensed(obj, trans, ambient) { /* if it's distant, we can't sense the object no matter how large */ if (trans == distant) return nil; /* for other cases, inherit the default handling */ return inherited(obj, trans, ambient); } ; /* ------------------------------------------------------------------------ */ /* * "Add" two transparency levels, yielding a new transparency level. * This function can be used to determine the result of passing a sense * through multiple layers of material. */ transparencyAdd(a, b) { /* transparent + x -> x for all x */ if (a == transparent) return b; if (b == transparent) return a; /* opaque + x -> opaque for all x */ if (a == opaque || b == opaque) return opaque; /* * any other combinations yield opaque - we can't have two levels of * attenuation or obscuration without losing all detail and energy * transmission */ return opaque; } /* ------------------------------------------------------------------------ */ /* * Compare two transparency levels to determine which one is more * transparent. Returns 0 if the two levels are equally transparent, 1 * if the first one is more transparent, and -1 if the second one is * more transparent. The comparison follows this rule: * * transparent > attenuated > distant == obscured > opaque */ transparencyCompare(a, b) { /* * for the purposes of the comparison, consider obscured to be * identical to distant */ if (a == obscured) a = distant; if (b == obscured) b = distant; /* if they're the same, return zero to so indicate */ if (a == b) return 0; /* * We know they're not equal, so if one is transparent, then the * other one isn't. Thus, if either one is transparent, it's the * winner. */ if (a == transparent) return 1; if (b == transparent) return -1; /* * We know they're not equal and we know neither is transparent, so * if one is attenuated then the other is worse, and the attenuated * one is the winner. */ if (a == attenuated) return 1; if (b == attenuated) return -1; /* * We now know neither one is transparent or attenuated, and we've * already transformed obscured into distant, so the only possible * values remaining are distant and opaque. We know also they're * not equal, because we would have already returned if that were * the case. So, we can conclude that one must be distant and the * other must be opaque. Hence, the one that's opaque is the less * transparent one. */ if (a == opaque) return -1; else return 1; } /* ------------------------------------------------------------------------ */ /* * Given a brightness level and a transparency level, compute the * brightness as modified by the transparency level. */ adjustBrightness(br, trans) { switch(trans) { case transparent: case distant: /* * Transparent medium or distance - this doesn't modify * brightness at all. (Technically, distance would reduce * brightness somewhat, but the typical scale of an IF setting * isn't usually large enough that brightness should * significantly diminish.) */ return br; case attenuated: case obscured: /* * Distant, obscured, or attenuated. We reduce self-illuminating * light (level 1) and dim light (level 2) to nothing (level 0), * we leave nothing as nothing (obviously), and we reduce all * other levels one step. So, everything below level 3 goes to * 0, and everything at or above level 3 gets decremented by 1. */ return (br >= 3 ? br - 1 : 0); case opaque: /* opaque medium - nothing makes it through */ return 0; default: /* shouldn't get to other cases */ return nil; } } /* ------------------------------------------------------------------------ */ /* * SenseConnector: an object that can pass senses across room * boundaries. This is a mix-in class: add it to the superclass list of * the object before Thing (or a Thing subclass). * * A SenseConnector acts as a sense conduit across all of its locations, * so to establish a connection between locations, simply place a * SenseConnector in each location. Since a SenseConnector is useful * only when placed placed in multiple locations, SenseConnector is * based on MultiLoc. */ class SenseConnector: MultiLoc /* * A SenseConnector's material generally determines how senses pass * through the connection. */ connectorMaterial = adventium /* * Determine how senses pass through this connection. By default, * we simply use the material's transparency. */ transSensingThru(sense) { return connectorMaterial.senseThru(sense); } /* * Add the direct containment connections for this item to a lookup * table. * * Since we provide a sense connection among all of our containers, * add each of our containers to the list. */ addDirectConnections(tab) { /* add myself */ tab[self] = true; /* add my CollectiveGroup objects */ foreach (local cur in collectiveGroups) tab[cur] = true; /* add my contents */ foreach (local cur in contents) { if (tab[cur] == nil) cur.addDirectConnections(tab); } /* add my containers */ foreach (local cur in locationList) { if (tab[cur] == nil) cur.addDirectConnections(tab); } } /* * Transmit energy from a container onto me. */ shineFromWithout(fromParent, sense, ambient, fill) { /* if this increases my ambient level, accept the new level */ if (ambient > tmpAmbient_) { local levelThru; /* remember the new level and fill material to this point */ tmpAmbient_ = ambient; tmpAmbientFill_ = fill; /* transmit to my contents */ shineOnContents(sense, ambient, fill); /* * We must transmit this energy to each of our other * parents, possibly reduced for traversing our connector. * Calculate the new level after traversing our connector. */ levelThru = adjustBrightness(ambient, transSensingThru(sense)); /* * if there's anything left, transmit it to the other * containers */ if (levelThru >= 2) { /* transmit to each container except the source */ foreach (local cur in locationList) { /* if this isn't the sender, transmit to it */ if (cur != fromParent) cur.shineFromWithin(self, sense, levelThru, fill); } } } } /* * Build a sense path from a container to me */ sensePathFromWithout(fromParent, sense, trans, obs, fill) { /* * if there's better transparency along this path than along any * previous path we've used to visit this item, take this path */ if (transparencyCompare(trans, tmpTrans_) > 0) { local transThru; /* remember the new path to this point */ tmpTrans_ = trans; tmpObstructor_ = obs; /* we're coming to this object from outside */ tmpPathIsIn_ = true; /* transmit to my contents */ sensePathToContents(sense, trans, obs, fill); /* * We must transmit this energy to each of our other * parents, possibly reduced for traversing our connector. * Calculate the new level after traversing our connector. */ transThru = transparencyAdd(trans, transSensingThru(sense)); /* if we changed the transparency, we're the obstructor */ if (transThru != trans) obs = self; /* * if there's anything left, transmit it to the other * containers */ if (transThru != opaque) { /* transmit to each container except the source */ foreach (local cur in locationList) { /* if this isn't the sender, transmit to it */ if (cur != fromParent) cur.sensePathFromWithin(self, sense, transThru, obs, fill); } } } } /* * Call a function on each connected container. Since we provide a * sense path connection among our containers, we must iterate over * each of our containers. */ forEachConnectedContainer(func, [args]) { forEachContainer(func, args...); } /* * Return a list of my connected containers. We connect to all of * our containers, so simply return my location list. */ getConnectedContainers = (locationList) /* * Check moving an object through me. This is called when we try to * move an object from one of our containers to another of our * containers through me. By default, we don't allow it. */ checkMoveThrough(obj, dest) { /* return an error - cannot move through */ return new CheckStatusFailure(&cannotMoveThroughMsg, obj, self); } /* * Check touching an object through me. This is called when an * actor tries to reach from one of my containers through me into * another of my containers. By default, we don't allow it. */ checkTouchThrough(obj, dest) { /* return an error - cannot reach through */ return new CheckStatusFailure(&cannotReachThroughMsg, dest, self); } /* * Check throwing an object through me. This is called when an actor * tries to throw a projectile 'obj' at 'dest' via a path that * includes 'self'. By default, we don't allow it. */ checkThrowThrough(obj, dest) { return new CheckStatusFailure(&cannotThrowThroughMsg, dest, self); } /* check for moving via a path */ checkMoveViaPath(obj, dest, op) { /* if moving through us, run the separate Move check */ if (op == PathThrough) return checkMoveThrough(obj, dest); /* if we can inherit, do so */ if (canInherit()) return inherited(obj, dest, op); /* return success by default */ return checkStatusSuccess; } /* check for touching via a path */ checkTouchViaPath(obj, dest, op) { /* if reaching through us, run the separate Touch check */ if (op == PathThrough) return checkTouchThrough(obj, dest); /* if we can inherit, do so */ if (canInherit()) return inherited(obj, dest, op); /* return success by default */ return checkStatusSuccess; } /* check for throwing via a path */ checkThrowViaPath(obj, dest, op) { /* if throwing through us, run the separate Throw check */ if (op == PathThrough) return checkThrowThrough(obj, dest); /* if we can inherit, do so */ if (canInherit()) return inherited(obj, dest, op); /* return success by default */ return checkStatusSuccess; } ; /* * Occluder: this is a mix-in class that can be used with multiple * inheritance to combine with other classes (such as SenseConnector, or * Thing subclasses), to create an "occluded view." This lets you * exclude certain objects from view, and you can make the exclusion vary * according to the point of view. * * This class is useful for situations where the view from one location * to another is partially obstructed. For example, suppose we have two * rooms, connected by a window between them. The window is the sense * connector that connects the two top-level locations, and it makes * objects in one room visible from the point of view of the other room. * Suppose that one room contains a bookcase, with its back to the * window. From the point of view of the other room, we can't see * anything inside the bookcase. This class allows for such special * situations. * * Note that occlusion rules are applied "globally" within a room - that * is, anything that an Occluder occludes will be removed from view, even * if it's visible from another, non-occluding connector. Hence, * occlusion always takes precedence over "inclusion" - if an object is * occluded just once, then it won't be in view, no matter how many times * it's added back into view by other connectors. This comes from the * order in which the occlusion rules are considered. Occlusion rules * are always run last, and they can't distinguish the connector that * added an object to view. So, we first run around and collect up * everything that can be seen, by considering all of the different paths * to seeing those things. Then, we go through all of the occlusion * rules that apply to the room, and we remove from view everything that * the occluding connectors want to occlude. */ class Occluder: object /* * Do we occlude the given object, in the given sense and from the * given point of view? This returns true if the object is occluded, * nil if not. By default, we simply ask the object whether it's * occluded by this occluder from the given POV. */ occludeObj(obj, sense, pov) { /* by default, simply ask the object what it thinks */ return obj.isOccludedBy(self, sense, pov); } /* * When we initialize for the sense path calculation, register to * receive notification after we've finished building the sense * table. We'll use the notification to remove any occluded objects * from the sense table. */ clearSenseInfo() { /* do the normal work */ inherited(); /* register for notification after we've built the table */ senseTmp.notifyList.append(self); } /* * Receive notification that the sense path calculation is now * finished. 'objs' is a LookupTable containing all of the objects * involved in the sense path calculation (the objects are the keys * in the table). Each object in the table now has its tmpXxx_ * properties set to the sense path data we've calculated for that * object - tmpTrans_ is the transparency to the object, tmpAmbient_ * is the ambient light level at the object, and so on. * * Since our job is to occlude certain objects from view, we'll run * through the table and test each object using our occlusion rule. * If we find that we do occlude an object, we'll set its * transparency to 'opaque' to indicate that it cannot be seen. */ finishSensePath(objs, sense) { /* get the point of view of the calculation */ local pov = senseTmp.pointOfView; /* run through the table, and apply our rule to each object */ objs.forEachAssoc(function(key, val) { /* if this object is occluded, set its path to opaque */ if (occludeObj(key, sense, pov)) { /* set this object to opaque */ key.tmpTrans_ = key.tmpTransWithin_ = opaque; /* we're the obstructor for the object */ key.tmpObstructor_ = key.tmpObstructorWithin_ = self; } }); } ; /* * DistanceConnector: a special type of SenseConnector that connects its * locations with distance. This can be used for things like divided * rooms, where a single physical location is modeled with two or more * Room objects - the north and south end of a large cave, for example. * This is also useful for cases where two rooms are separate but open to * one another, such as a balcony overlooking a courtyard. * * Note that this inherits from both SenseConnector and Intangible. * Intangible is included as a base class because each instance will need * to derive from Thing, so that it fits into the normal sense model, but * will virtually never need any other physical presence in the game * world; Intangible fills both of these needs. */ class DistanceConnector: SenseConnector, Intangible /* all senses are connected through us, but at a distance */ transSensingThru(sense) { return distant; } /* * When checking for reaching through this connector, specialize the * failure message to indicate that distance is the specific problem. * (Without this specialization, we'd get a generic message when * trying to reach through the connector, such as "you can't reach * that through ." */ checkTouchThrough(obj, dest) { /* we can't touch through this connector due to the distance */ return new CheckStatusFailure(&tooDistantMsg, dest); } /* * When checking for throwing through this container, specialize the * failure message to indicate that distance is the specific problem. */ checkThrowThrough(obj, dest) { return new CheckStatusFailure(&tooDistanceMsg, dest); } /* * Do allow moving an object through a distance connector. This * should generally only be involved at all when we're moving an * object programmatically, in which case we should already have * decided that the movement is allowable. Any command that tries to * move an object through a distance connector will almost certainly * have a suitable set of preconditions that checks for reachability, * which will in most cases disallow the action anyway before we get * to the point of wanting to move anything. */ checkMoveThrough(obj, dest) { return checkStatusSuccess; } /* * Report the reason that we stopped a thrown projectile from hitting * its intended target. This is called when we're along the path * between the thrower and the intended target, AND 'self' objects to * the action. * * The default version of this method in Thing reports that the * projectile hits 'self', as though self were a physical obstruction * like a fence or wall. In the case of a distance connector, * though, the reason isn't usually obstruction, but simply that the * connector imposes such a distance that the actor can't throw the * projectile far enough to reach the intended target. We therefore * override the Thing version to report that the projectile fell * short of the target. * * Note that if you do want to allow throwing a projectile across the * distance represented by this connector, you can override * checkThrowThrough() to return checkStatusSuccess. */ throwTargetHitWith(projectile, path) { /* * figure out where we fall to when we hit this object, then send * the object being thrown to that location */ getHitFallDestination(projectile, path) .receiveDrop(projectile, new DropTypeShortThrow(self, path)); } ; /* * A drop-type descriptor for a "short throw," which occurs when the * target is too far away to reach with our throw (i.e., the thrown * object falls short of the target). */ class DropTypeShortThrow: DropTypeThrow construct(target, path) { /* inherit the default handling */ inherited(target, path); /* we care about the *intended* target, not the distance connector */ target_ = path[path.length()]; } standardReport(obj, dest) { /* show the short-throw report */ mainReport(&throwFallShortMsg, obj, target_, dest.getNominalDropDestination()); } getReportPrefix(obj, dest) { /* return the short-throw prefix */ return gActor.getActionMessageObj().throwShortMsg(obj, target_); } ; frobtads-1.2.3/tads3/lib/adv3/adv3web.tl0000644000175000001440000000135411476200607017027 0ustar realncusersname: TADS 3 Adventure Library (Web UI, language-independent) source: action source: actions source: actor source: browser source: disambig source: events source: exec source: exits source: extras source: footnote source: hintsys source: input source: lister source: menusys source: menuweb source: misc source: modid source: numbers source: objects source: output source: parser source: pov source: precond source: report source: resolver source: score source: sense source: settings source: status source: tips source: thing source: travel source: verify needmacro: LANGUAGE this selects the language the library uses in its messages and parsing, using ISO 639/3166 language/country codes: US English is "en_us" library: $(LANGUAGE)/$(LANGUAGE) frobtads-1.2.3/tads3/lib/adv3/menucon.t0000644000175000001440000011126511476200541016764 0ustar realncusers#charset "us-ascii" /* * TADS 3 Library - Menu System, console edition * * This implements the menusys user interface for the traditional * console-mode interpreters. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Banner windows. For the console version, we display the menu * components in banner windows. */ /* * The very top banner of the menu, which holds its title and * instructions. */ topMenuBanner: BannerWindow ; /* * The actual menu contents banner window. This displays the list of * menu items to choose from. */ contentsMenuBanner: BannerWindow ; /* * The long topic banner. This takes over the screen when we're * displaying a long topic item. */ longTopicBanner: BannerWindow ; /* ------------------------------------------------------------------------ */ /* * Menu Item - user interface implementation for the console */ modify MenuItem /* * Call menu.display when you're ready to show the menu. This * should be called on the top-level menu; we run the entire menu * display process, and return when the user exits from the menu * tree. */ display() { local oldStr; local flags; /* make sure the main window is flushed before we get going */ flushOutput(); /* set up with the top menu banner in place of the status line */ removeStatusLine(); showTopMenuBanner(self); /* * display the menu using the same mode that the statusline * has decided to use */ switch (statusLine.statusDispMode) { case StatusModeApi: /* use a border, unless we're taking over the whole screen */ flags = (fullScreenMode ? 0 : BannerStyleBorder); /* * use a scrollbar if possible; keep the text scrolled into * view as we show it */ flags |= BannerStyleVScroll | BannerStyleAutoVScroll; /* banner API mode - show our banner window */ contentsMenuBanner.showBanner(nil, BannerLast, nil, BannerTypeText, BannerAlignTop, nil, nil, flags); /* make the banner window the default output stream */ oldStr = contentsMenuBanner.setOutputStream(); /* make sure we restore the default output stream when done */ try { /* display and run our menu in HTML mode */ showMenuHtml(self); } finally { /* restore the original default output stream */ outputManager.setOutputStream(oldStr); /* remove the menu banner */ contentsMenuBanner.removeBanner(); } break; case StatusModeTag: /* HTML tag mode - just show our HTML contents */ showMenuHtml(self); /* remove the banner for the menu display */ ""; break; case StatusModeText: /* display and run our menu in text mode */ showMenuText(self); break; } /* we're done, so remove the top menu banner */ removeTopMenuBanner(); } /* * Display the menu in plain text mode. This is used when the * interpreter only supports the old tads2-style text-mode * single-line status area. * * Returns true if we should return to the parent menu, nil if the * user selected QUIT to exit the menu system entirely. */ showMenuText(topMenu) { local i, selection, len, key = '', loc; /* remember the key list */ curKeyList = topMenu.keyList; /* bring our contents up to date, as needed */ updateContents(); /* keep going until the player exits this menu level */ do { /* * For text mode, print the title, then show the menu * options as a numbered list, then ask the player to make a * selection. */ /* get the number of items in the menu */ len = contents.length(); /* show the menu heading */ "\n<>\b"; /* show the contents as a numbered list */ for (i = 1; i <= len; i++) { /* leave room for two-digit numeric labels if needed */ if (len > 9 && i <= 10) "\ "; /* show the item's number and title */ "<>.\ <>\n"; } /* show the main prompt */ gLibMessages.textMenuMainPrompt(topMenu.keyList); /* main input loop */ do { /* * Get a key, and convert any alphabetics to lower-case. * Do not allow real-time interruptions, as menus are * meta-game interactions. */ key = inputManager.getKey(nil, nil).toLower(); /* check for a command key */ loc = topMenu.keyList.indexWhich({x: x.indexOf(key) != nil}); /* also check for a numeric selection */ selection = toInteger(key); } while ((selection < 1 || selection > len) && loc != M_QUIT && loc != M_PREV); /* * show the selection if it's an ordinary key (an ordinary * key is represented by a single character; if we have more * than one character, it's one of the '[xxx]' special key * representations) */ if (key.length() == 1) "<>"; /* add a blank line */ "\b"; /* * If the selection is a number, then the player selected * that menu option. Call that submenu or topic's display * routine. If the routine returns nil, the player selected * QUIT, so we should quit as well. */ while (selection != 0 && selection <= contents.length()) { /* invoke the child menu */ loc = contents[selection].showMenuText(topMenu); /* * Check the result. If it's nil, it means QUIT; if it's * 'next', it means we're to proceed directly to our next * sub-menu. If the user didn't select QUIT, then * refresh our menu contents, as we'll be displaying our * menu again and its contents could have been affected * by the sub-menu invocation. */ switch(loc) { case M_QUIT: /* they want to quit - leave the submenu loop */ selection = 0; break; case M_UP: /* they want to go to the previous menu directly */ --selection; break; case M_DOWN: /* they want to go to the next menu directly */ ++selection; break; case M_PREV: /* * they want to show this menu again - update our * contents so that we account for any changes made * while running the submenu, then leave the submenu * loop */ updateContents(); selection = 0; /* * forget the 'prev' command - we don't want to back * up any further just yet, since the submenu just * wanted to get back to this point */ loc = nil; break; } } } while (loc != M_QUIT && loc != M_PREV); /* return the desired next action */ return loc; } /* * Show the menu using HTML. Return nil when the user selects QUIT * to exit the menu entirely. */ showMenuHtml(topMenu) { local len, selection = 1, loc; local refreshTitle = true; /* remember the key list */ curKeyList = topMenu.keyList; /* update the menu contents, as needed */ updateContents(); /* keep going until the user exits this menu level */ do { /* refresh our title in the instructions area if necessary */ if (refreshTitle) { refreshTopMenuBanner(topMenu); refreshTitle = nil; } /* get the number of items in the menu */ len = contents.length(); /* check whether we're in banner API or tag mode */ if (statusLine.statusDispMode == StatusModeApi) { /* banner API mode - clear our window */ contentsMenuBanner.clearWindow(); /* advise the interpreter of our best guess for our size */ if (fullScreenMode) contentsMenuBanner.setSize(100, BannerSizePercent, nil); else contentsMenuBanner.setSize(len + 1, BannerSizeAbsolute, true); /* set up our desired color scheme */ "> text=<> >"; } else { /* * tag mode - set up our tag. In full-screen * mode, set our height to 100% immediately; otherwise, * leave the height unspecified so that we'll use the * size of our contents. Use a border only if we're not * taking up the full screen. */ "> >> text=<> >"; } /* display our contents as a table */ "
> > "; for (local i = 1; i <= len; i++) { /* * To get the alignment right, we have to print '>' on * each and every line. However, we print it in the * background color to make it invisible everywhere but * in front of the current selection. */ if (selection != i) "> >>"; else ">"; /* make each selection a plain (i.e. unhilighted) HREF */ "> ><>
"; } /* end the table */ "
"; /* finish our display as appropriate */ if (statusLine.statusDispMode == StatusModeApi) { /* banner API - size the window to its contents */ if (!fullScreenMode) contentsMenuBanner.sizeToContents(); } else { /* tag - just close the tag */ ""; } /* main input loop */ do { local key, events; /* * Read an event - don't allow real-time interruptions, * since menus are meta-game interactions. Read an * event rather than just a keystroke, because we want * to let the user click on a menu item's HREF. */ events = inputManager.getEvent(nil, nil); /* check the event type */ switch (events[1]) { case InEvtHref: /* * the HREF's value is the selection number, or a * 'previous' command */ if (events[2] == 'previous') loc = M_PREV; else { selection = toInteger(events[2]); loc = M_SEL; } break; case InEvtKey: /* keystroke - convert any alphabetic to lower case */ key = events[2].toLower(); /* scan for a valid command key */ loc = topMenu.keyList.indexWhich( {x: x.indexOf(key) != nil}); break; } /* handle arrow keys */ if (loc == M_UP) { selection--; if (selection < 1) selection = len; } else if (loc == M_DOWN) { selection++; if (selection > len) selection = 1; } } while (loc == nil); /* if the player selected a sub-menu, invoke the selection */ while (loc == M_SEL && selection != 0 && selection <= contents.length()) { /* * Invoke the sub-menu, checking for a QUIT result. If * the user isn't quitting, we'll display our own menu * again; in this case, update it now, in case something * in the sub-menu changed our own contents. */ loc = contents[selection].showMenuHtml(topMenu); /* see what we have */ switch (loc) { case M_UP: /* they want to go directly to the previous menu */ loc = M_SEL; --selection; break; case M_DOWN: /* they want to go directly to the next menu */ loc = M_SEL; ++selection; break; case M_PREV: /* they want to return to this menu level */ loc = nil; /* update our contents */ updateContents(); /* make sure we refresh the title area */ refreshTitle = true; break; } } } while (loc != M_QUIT && loc != M_PREV); /* return the next status */ return loc; } /* * showTopMenuBanner creates the banner for the menu using the * banner API. The banner contains the title of the menu on the * left and the navigation keys on the right. */ showTopMenuBanner(topMenu) { /* do not show the top banner if we're in text mode */ if (statusLine.statusDispMode == StatusModeText) return; /* * Since the status line has already figured out the terp's * capabilities, piggyback off of what it learned. If we're * using banner API mode, show our banner window. */ if (statusLine.statusDispMode == StatusModeApi) { /* banner API mode - show our banner window */ topMenuBanner.showBanner(nil, BannerFirst, nil, BannerTypeText, BannerAlignTop, nil, nil, BannerStyleBorder | BannerStyleTabAlign); /* advise the terp that we need two lines */ topMenuBanner.setSize(2, BannerSizeAbsolute, true); } /* show our contents */ refreshTopMenuBanner(topMenu); } /* * Refresh the contents of the top bar with the instructions */ refreshTopMenuBanner(topMenu) { local oldStr; /* clear our old contents using the appropriate mode */ switch (statusLine.statusDispMode) { case StatusModeApi: /* clear the window */ topMenuBanner.clearWindow(); /* set the default output stream to our menu window */ oldStr = topMenuBanner.setOutputStream(); /* set our color scheme */ "> text=<> >"; break; case StatusModeTag: /* start a new tag */ "> text=<> >"; break; } /* show our heading */ say(heading); /* show our keyboard assignments */ gLibMessages.menuInstructions(topMenu.keyList, prevMenuLink); /* finish up according to our mode */ switch (statusLine.statusDispMode) { case StatusModeApi: /* banner API mode - restore the old output stream */ outputManager.setOutputStream(oldStr); /* size the window to the actual content size */ topMenuBanner.sizeToContents(); break; case StatusModeTag: /* close the tag */ ""; break; } } /* * Remove the top banner window */ removeTopMenuBanner() { /* remove the window according to the banner mode */ switch (statusLine.statusDispMode) { case StatusModeApi: /* banner API mode - remove the banner window */ topMenuBanner.removeBanner(); break; case StatusModeTag: /* banner tag mode - remove our banner tag */ ""; } } /* * Remove the status line banner prior to displaying the menu */ removeStatusLine() { local oldStr; /* remove the banner according to our banner display mode */ switch (statusLine.statusDispMode) { case StatusModeApi: /* * banner API mode - simply set the banner window to zero * size, which will effectively make it invisible */ statuslineBanner.setSize(0, BannerSizeAbsolute, nil); break; case StatusModeTag: /* tag mode - remove the statusline banner */ oldStr = outputManager.setOutputStream(statusTagOutputStream); ""; outputManager.setOutputStream(oldStr); break; case StatusModeText: /* tads2-style statusline - there's no way to remove it */ break; } } ; /* ------------------------------------------------------------------------ */ /* * Menu topic item - console UI implementation */ modify MenuTopicItem /* * Display and run our menu in text mode. */ showMenuText(topMenu) { local i, len, loc; /* remember the key list */ curKeyList = topMenu.keyList; /* update our contents, as needed */ updateContents(); /* get the number of items in our list */ len = menuContents.length(); /* show our heading and instructions */ "\n<>"; gLibMessages.textMenuTopicPrompt(); /* * Show all of the items up to and including the last one we * displayed on any past invocation. Append "[#/#]" to each * item to show where we are in the overall list. */ for (i = 1 ; i <= lastDisplayed ; ++i) { /* display this item */ displaySubItem(i, i == lastDisplayed, '\b'); } /* main input loop */ for (;;) { local key; /* read a keystroke */ key = inputManager.getKey(nil, nil).toLower(); /* look it up in the key list */ loc = topMenu.keyList.indexWhich({x: x.indexOf(key) != nil}); /* check to see if they want to quit the menu system */ if (loc == M_QUIT) return M_QUIT; /* * check to see if they want to return to the previous menu; * if we're out of items to show, return to the previous * menu on any other keystrok as well */ if (loc == M_PREV || self.lastDisplayed == len) return M_PREV; /* for any other keystroke, just show the next item */ lastDisplayed++; displaySubItem(lastDisplayed, lastDisplayed == len, '\b'); } } /* * Display and run our menu in HTML mode. */ showMenuHtml(topMenu) { local len; local topIdx; /* remember the key list */ curKeyList = topMenu.keyList; /* refresh the top instructions bar with our heading */ refreshTopMenuBanner(topMenu); /* update our contents, as needed */ updateContents(); /* get the number of items in our list */ len = menuContents.length(); /* * initially show the first item at the top of the window (we * might scroll the list later to show a later item at the top, * if we're limiting the number of items we can show at once) */ topIdx = 1; /* main interaction loop */ for (;;) { local lastIdx; /* redraw the window with the current top item */ lastIdx = redrawWinHtml(topIdx); /* process input */ for (;;) { local events; local loc; local key; /* read an event */ events = inputManager.getEvent(nil, nil); switch(events[1]) { case InEvtHref: /* check for a 'next' or 'previous' command */ switch(events[2]) { case 'next': /* we want to go to the next item */ loc = M_SEL; break; case 'previous': /* we want to go to the previous menu */ loc = M_PREV; break; default: /* ignore other hyperlinks */ loc = nil; } break; case InEvtKey: /* get the key, converting alphabetic to lower case */ key = events[2].toLower(); /* look up the keystroke in our key mappings */ loc = topMenu.keyList.indexWhich( {x: x.indexOf(key) != nil}); break; } /* * if they're quitting or returning to the previous * menu, we're done */ if (loc == M_QUIT || loc == M_PREV) return loc; /* advance to the next item if desired */ if (loc == M_SEL) { /* * if the last item we showed is the last item in * our entire list, then the normal selection keys * simply return to the previous menu */ if (lastIdx == len) return M_PREV; /* * If we haven't yet reached the last revealed item, * it means we're limited by the chunk size, so show * the next chunk. Otherwise, reveal the next item. */ if (lastIdx < lastDisplayed) { /* advance to the next chunk */ topIdx += chunkSize; } else { /* reveal the next item */ ++lastDisplayed; /* * if we're not in full-screen mode, and we've * already filled the window, scroll down a line * by advancing the index of the item at the top * of the window */ if (!fullScreenMode && lastIdx == topIdx + chunkSize - 1) ++topIdx; } /* done processing input */ break; } } } } /* * redraw the window in HTML mode, starting with the given item at * the top of the window */ redrawWinHtml(topIdx) { local len; local idx; /* get the number of items in our list */ len = menuContents.length(); /* check the banner mode (based on the statusline mode) */ if (statusLine.statusDispMode == StatusModeApi) { /* banner API mode - clear the window */ contentsMenuBanner.clearWindow(); /* * Advise the terp of our best guess at our size: assume one * line per item, and max out at either our actual number of * items or our maximum chunk size, whichever is lower. If * we're in full-screen mode, though, simply size to 100% of * the available space. */ if (fullScreenMode) contentsMenuBanner.setSize(100, BannerSizePercent, nil); else contentsMenuBanner.setSize(chunkSize < len ? chunkSize : len, BannerSizeAbsolute, true); /* set up our color scheme */ "> text=<> >"; } else { /* tag mode - open our tag */ "> >> text=<> >"; } /* start a table to show the items */ "
> > "; /* show the items */ for (idx = topIdx ; ; ++idx) { local isLast; /* * Note if this is the last item we're going to show just * now. It's the last item we're showing if it's the last * item in the list, or it's the 'lastDisplayed' item, or * we've filled out the chunk size. */ isLast = (idx == len || (!fullScreenMode && idx == topIdx + chunkSize - 1) || idx == lastDisplayed); /* display the next item */ displaySubItem(idx, isLast, '
'); /* if that was the last item, we're done */ if (isLast) break; } /* finish the table */ "
"; /* finish the window */ switch(statusLine.statusDispMode) { case StatusModeApi: /* if we're not in full-screen mode, set the final size */ if (!fullScreenMode) contentsMenuBanner.sizeToContents(); break; case StatusModeTag: /* end the banner tag */ "
"; break; } /* return the index of the last item displayed */ return idx; } /* * Display an item from our list. 'idx' is the index in our list of * the item to display. 'lastBeforeInput' indicates whether or not * this is the last item we're going to show before pausing for user * input. 'eol' gives the newline sequence to display at the end of * the line. */ displaySubItem(idx, lastBeforeInput, eol) { local item; /* get the item from our list */ item = menuContents[idx]; /* * show the item: if it's a simple string, just display it; * otherwise, assume it's an object, and call its getItemText * method to get its text (and possibly trigger any needed * side-effects) */ say(dataType(item) == TypeSString ? item : item.getItemText()); /* add the [n/m] indicator */ gLibMessages.menuTopicProgress(idx, menuContents.length()); /* * if this is the last item we're going to display before asking * for input, and it's not the last item in the list overall, * and we're in HTML mode, show a hyperlink for advancing to the * next item */ if (lastBeforeInput && idx != menuContents.length()) " <>"; /* show the desired line-ending separator */ say(eol); /* if it's the last item, add the end-of-list marker */ if (idx == menuContents.length()) "<>\n"; } ; /* ------------------------------------------------------------------------ */ /* * Long topic item */ modify MenuLongTopicItem /* display and run our menu in text mode */ showMenuText(topMenu) { local ret; /* remember the key list */ curKeyList = topMenu.keyList; /* take over the entire screen */ cls(); /* use the common handling */ ret = showMenuCommon(topMenu); /* we're done, so clear the screen again */ cls(); /* return the result from the common handler */ return ret; } /* display and run our menu in HTML mode */ showMenuHtml(topMenu) { local ret; local oldStr; /* remember the key list */ curKeyList = topMenu.keyList; /* update our contents, as needed */ updateContents(); /* hide the two menu system banners */ if (statusLine.statusDispMode == StatusModeApi) { local flags; /* * Our banner window might already be showing, because we * could be coming here directly from a prior chapter. If it * is, we don't need to show it again. If it isn't showing, * show it now. */ if (longTopicBanner.handle_ != nil) { /* simply clear our existing window */ longTopicBanner.clearWindow(); } else { /* hide the top menu banner */ topMenuBanner.setSize(0, BannerSizeAbsolute, nil); /* figure our flags */ flags = (fullScreenMode ? 0 : BannerStyleBorder) | BannerStyleVScroll | BannerStyleMoreMode | BannerStyleAutoVScroll; /* banner API mode - show the long-topic banner */ longTopicBanner.showBanner(contentsMenuBanner, BannerLast, nil, BannerTypeText, BannerAlignTop, 100, BannerSizePercent, flags); } /* use its output stream */ oldStr = longTopicBanner.setOutputStream(); /* set up our color scheme in the new banner */ "> text=<> >"; } else { /* * use the main game window output stream for printing this * text (we need to switch back to it explicitly, because * HTML-mode menus normally run in the context of the menu's * banner output stream) */ oldStr = outputManager.setOutputStream(mainOutputStream); /* we're using the main window, so clear out the game text */ cls(); } try { /* show our contents using the normal text display */ ret = showMenuCommon(topMenu); } finally { local chapter; /* restore the original output stream */ outputManager.setOutputStream(oldStr); /* * If we're going directly to the next or previous "chapter," * and the next menu is itself a long-topic item, don't clean * up the screen: simply leave it in place for the next item. * First, check for a next/previous chapter return code, and * get the menu object for the next/previous chapter. */ if (ret == M_DOWN) chapter = location.getNextMenu(self); else if (ret == M_UP) chapter = location.getPrevMenu(self); /* * if we have a next/previous chapter, and it's a long-topic * menu, we don't need cleanup; otherwise we do */ if (isChapterMenu && chapter != nil && chapter.ofKind(MenuLongTopicItem)) { /* we don't need any cleanup */ } else { /* clean up the window */ if (statusLine.statusDispMode == StatusModeApi) { /* API mode - remove our long-topic banner */ longTopicBanner.removeBanner(); } else { /* tag mode - we used the main game window, so clear it */ cls(); } /* restore the top menu banner window */ topMenu.showTopMenuBanner(topMenu); } } /* return the quit/continue indication */ return ret; } /* show our contents - common handler for text and HTML modes */ showMenuCommon(topMenu) { local evt, key, loc, nxt; /* update our contents, as needed */ updateContents(); /* show our heading, centered */ "
<>
\b"; /* show our contents */ "<>\b"; /* check to see if we should offer chapter navigation */ nxt = (isChapterMenu ? location.getNextMenu(self) : nil); /* if there's a next chapter, show how we can navigate to it */ if (nxt != nil) { /* show the navigation */ gLibMessages.menuNextChapter(topMenu.keyList, nxt.title, 'next', 'menu'); } else { /* no chaptering - just print the ending message */ "<>"; } /* wait for an event */ for (;;) { evt = inputManager.getEvent(nil, nil); switch(evt[1]) { case InEvtHref: /* check for a 'next' or 'prev' command */ if (evt[2] == 'next') return M_DOWN; else if (evt[2] == 'prev') return M_UP; else if (evt[2] == 'menu') return M_PREV; break; case InEvtKey: /* get the key */ key = evt[2].toLower(); /* * if we're in plain text mode, add a blank line after * the key input */ if (statusLine.statusDispMode == StatusModeText) "\b"; /* look up the command key */ loc = topMenu.keyList.indexWhich({x: x.indexOf(key) != nil}); /* * if it's 'next', either proceed to the next menu or * return to the previous menu, depending on whether * we're in chapter mode or not */ if (loc == M_SEL) return (nxt == nil ? M_PREV : M_DOWN); /* if it's 'prev', return to the previous menu */ if (loc == M_PREV || loc == M_QUIT) return loc; /* ignore other keys */ break; } } } ; frobtads-1.2.3/tads3/lib/adv3/hintsys.t0000644000175000001440000006360010772161205017021 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 by Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - Hint System * * This module provides a hint system framework. Games can use this * framework to define context-sensitive hints for players. * * This module depends on the menus module to display the user interface. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * We refer to some properties defined primarily in score.t - that's an * optional module, though, so make sure the compiler has heard of these. */ property scoreCount; /* ------------------------------------------------------------------------ */ /* * A basic hint menu object. This is an abstract base class that * encapsulates some behavior common to different hint menu classes. */ class HintMenuObject: object /* * The topic order. When we're about to show a list of open topics, * we'll sort the list in ascending order of this property, then in * ascending order of title. By default, we set this order value to * 1000; if individual goals don't override this, then they'll * simply be sorted lexically by topic name. This can be used if * there's some basis other than alphabetical order for sorting the * list. */ topicOrder = 1000 /* * Compare this goal to another, for the purposes of sorting a list * of topics. Returns a positive number if this goal sorts after * the other one, a negative number if this goal sorts before the * other one, 0 if the relative order is arbitrary. * * By default, we'll sort by topicOrder if the topicOrder values are * different, otherwise alphabetically by title. */ compareForTopicSort(other) { /* if the topicOrder values are different, sort by topicOrder */ if (topicOrder != other.topicOrder) return topicOrder - other.topicOrder; /* the topicOrder values are the same, so sort by title */ if (title > other.title) return 1; else if (title < other.title) return -1; else return 0; } ; /* * A Goal represents an open task: something that the player is trying * to achieve. A Goal is an abstract object, not part of the simulated * world of the game. * * Each goal is associated with a hint topic (usually shown as a * question, such as "How do I get past the guard?") and an ordered list * of hints. The hints are usually ordered from most general to most * specific. The idea is to let the player control how big a hint they * get; we start with a small nudge and work towards giving away the * puzzle completely, so the player can stop as soon as they see * something that helps. * * At any given time, a goal can be in one of three states: * * - Open: this means that the player is (or ought to be) aware of the * goal, but the goal hasn't yet been achieved. Determining this * awareness is up to the goal. In some cases, a goal is opened as soon * as the player has seen a particular object or entered a particular * area; in other cases, a goal might be opened by a scripted event, * such as a speech by an NPC telling the player they have to accomplish * something. A goal could even be opened by viewing a hint for another * goal, because that hint could explain a gating goal that the player * might not otherwise been able to know about. * * - Undiscovered: this means that the player doesn't yet have any * reason to know about the goal. * * - Closed: this means that the player has accomplished the goal, or in * some cases that the goal has become irrelevant. * * The hint system only shows goals that are Open. We don't show Closed * goals because the player presumably has no need of them any longer; * we don't show Undiscovered goals to avoid giving away developments * later in the game before they become relevant. */ enum OpenGoal, ClosedGoal, UndiscoveredGoal; class Goal: MenuTopicItem, HintMenuObject /* * The topic question associated with the goal. The hint system * shows a list of the topics for the goals that are currently open, * so that the player can decide what area they want help on. */ title = '' /* * Our parent menu - this is usually a HintMenu object. In very * simple hint systems, this could simply be a top-level hint menu * container; more typically, the hint system will be structured * into a menu tree that organizes the hint topics into several * different submenus, for easier navigatino. */ location = nil /* * The list of hints for this topic. This should be ordered from * most general to most specific; we offer the hints in the order * they appear in this list, so the earlier hints should give away * as little as possible, while the later hints should get * progressively closer to just outright giving away the answer. * * Each entry in the list can be a simple (single-quoted) string, or * it can be a Hint object. In most cases, a string will do. A * Hint object is only needed when displaying the hint has some side * effect, such as opening a new Goal. */ menuContents = [] /* * An optional object that, when seen by the player character, opens * this goal. It's often convenient to declare a goal open as soon * as the player enters a particular area or has encountered a * particular object. For such cases, simply set this property to * the room or object that opens the goal, and we'll automatically * mark the goal as Open the next time the player asks for a hint * after seeing the referenced object. */ openWhenSeen = nil /* * An option object that, when seen by the player character, closes * this goal. Many goals will be things like "how do I find the * X?", in which case it's nice to close the goal when the X is * found. */ closeWhenSeen = nil /* * this is like openWhenSeen, but opens the topic when the given * object is described (with EXAMINE) */ openWhenDescribed = nil /* close the goal when the given object is described */ closeWhenDescribed = nil /* * An optional Achievement object that opens this goal. This goal * will be opened automatically once the goal is achieved, if the * goal was previously undiscovered. This makes it easy to set up a * hint topic that becomes available after a particular puzzle is * solved, which is useful when a new puzzle only becomes known to * the player after a gating puzzle has been solved. */ openWhenAchieved = nil /* * An optional Achievement object that closes this goal. Once the * achievement is completed, this goal's state will automatically be * set to Closed. This makes it easy to associate the goal with a * puzzle: once the puzzle is solved, there's no need to show hints * for the goal any more. */ closeWhenAchieved = nil /* * An optional Topic or Thing that opens this goal when the object * becomes "known" to the player character. This will open the goal * as soon as gPlayerChar.knowsAbout(openWhenKnown) returns true. * This makes it easy to open a goal as soon as the player comes * across some information in the game. */ openWhenKnown = nil /* an optional Topic or Thing that closes this goal when known */ closeWhenKnown = nil /* * An optional <.reveal> tag name that opens this goal. If this is * set to a non-nil string, we'll automatically open this goal when * the tag has been revealed via <.reveal> (or gReveal()). */ openWhenRevealed = nil /* an optional <.reveal> tag that closes this goal when revealed */ closeWhenRevealed = nil /* * An optional arbitrary check that opens the goal. If this returns * true, we'll open the goal. This check is made in addition to the * other checks (openWhenSeen, openWhenDescribed, etc). This can be * used for any custom check that doesn't fit into one of the * standard openWhenXxx properties. */ openWhenTrue = nil /* an optional general-purpose check that closes the goal */ closeWhenTrue = nil /* * Determine if there's any condition that should open this goal. * This checks openWhenSeen, openWhenDescribed, and all of the other * openWhenXxx conditions; if any of these return true, then we'll * return true. * * Note that this should generally NOT be overridden in individual * instances; normally, instances would define openWhenTrue instead. * However, some games might find that they use the same special * condition over and over in many goals, often enough to warrant * adding a new openWhenXxx property to Goal. In these cases, you * can use 'modify Goal' to override openWhen to add the new * condition: simply define openWhen as (inherited || newCondition), * where 'newCondition' is the new special condition you want to * add. */ openWhen = ( (openWhenSeen != nil && gPlayerChar.hasSeen(openWhenSeen)) || (openWhenDescribed != nil && openWhenDescribed.described) || (openWhenAchieved != nil && openWhenAchieved.scoreCount != 0) || (openWhenKnown != nil && gPlayerChar.knowsAbout(openWhenKnown)) || (openWhenRevealed != nil && gRevealed(openWhenRevealed)) || openWhenTrue) /* * Determine if there's any condition that should close this goal. * We'll check closeWhenSeen, closeWhenDescribed, and all of the * other closeWhenXxx conditions; if any of these return true, then * we'll return true. */ closeWhen = ( (closeWhenSeen != nil && gPlayerChar.hasSeen(closeWhenSeen)) || (closeWhenDescribed != nil && closeWhenDescribed.described) || (closeWhenAchieved != nil && closeWhenAchieved.scoreCount != 0) || (closeWhenKnown != nil && gPlayerChar.knowsAbout(closeWhenKnown)) || (closeWhenRevealed != nil && gRevealed(closeWhenRevealed)) || closeWhenTrue) /* * Has this goal been fully displayed? The hint system automatically * sets this to true when the last item in our hint list is * displayed. * * You can use this, for example, to automatically remove the hint * from the hint menu after it's been fully displayed. (You might * want to do this with a hint for a red herring, for example. After * the player has learned that the red herring is a red herring, they * probably won't need to see that particular line of hints again, so * you can remove the clutter in the menu by closing the hint after * it's been fully displayed.) To do this, simply add this to the * Goal object: * *. closeWhenTrue = (goalFullyDisplayed) */ goalFullyDisplayed = nil /* * Check our menu state and update it if necessary. Each time our * parent menu is about to display, it'll call this on its sub-items * to let them update their current states. This method can promote * the state to Open or Closed if the necessary conditions for the * goal have been met. * * Sometimes it's more convenient to set a goal's state explicitly * from a scripted event; for example, if the goal is associated * with a scored achievement, awarding the goal's achievement will * set the goal's state to Closed. In these cases, there's no need * to use this method, since you're managing the goal's state * explicitly. The purpose of this method is to make it easy to * catch goal state changes that can be reached by several different * routes; in these cases, you can just write a single test for * those conditions in this method rather than trying to catch every * possible route to the new conditions and writing code in all of * those. * * The default implementation looks at our openWhenSeen property. * If this property is not nil, then we'll check the object * referenced in this property; if our current state is * Undiscovered, and the object referenced by openWhenSeen has been * seen by the player character, then we'll change our state to * Open. We'll make the corresponding check for openWhenDescribed. */ updateContents() { /* * If we're currently Undiscovered, and our openWhenSeen object * has been seen by the player charater, change our state to * Open. Likewise, if our gating achievement has been scored, * open the goal. */ if (goalState == UndiscoveredGoal && openWhen) { /* * the player has encountered our gating object, so open * this goal */ goalState = OpenGoal; } /* * if we're currently Undiscovered or Open, and our Achievement * has been scored, then change our state to Closed - once the * goal has been achieved, there's no need to offer hints on the * topic any longer */ if (goalState is in (UndiscoveredGoal, OpenGoal) && closeWhen) { /* the goal has been achieved, so close it */ goalState = ClosedGoal; } } /* display a sub-item, keeping track of when we've shown them all */ displaySubItem(idx, lastBeforeInput, eol) { /* do the inherited work */ inherited(idx, lastBeforeInput, eol); /* if we just displayed the last item, note it */ if (idx == menuContents.length()) goalFullyDisplayed = true; } /* we're active in our parent menu if our goal state is Open */ isActiveInMenu = (goalState == OpenGoal) /* * This goal's current state. We'll start off undiscovered. When a * goal should be open from the very start of the game, this should * be overridden and set to OpenGoal. */ goalState = UndiscoveredGoal ; /* * A Hint encapsulates one hint from a topic. In many cases, hints can * be listed in a topic simply as strings, rather than using Hint * objects. Hint objects provide a little more control, though; in * particular, a Hint object can specify some additional code to run * when the hint is shown, so that it can apply any side effects of * showing the hint (for example, when a hint is shown, it could mark * another Goal object as Open, which might be desirable if the hint * refers to another topic that the player might not yet have * encountered). */ class Hint: MenuTopicSubItem /* the hint text */ hintText = '' /* * A list of other Goal objects that this hint references. By * default, when we show this hint for the first time, we'll promote * each goal in this list from Undiscovered to Open. * * Sometimes, it's necessary to solve one puzzle before another can * be solved. In these cases, some hints for the first puzzle * (which depends on the second), especially the later, more * specific hints, might need to refer to the other puzzle. This * would make the player aware of the other puzzle even if they * weren't already. In such cases, it's a good idea to make sure * that we make hints for the other puzzle available immediately, * since otherwise the player might be confused by the absence of * hints about it. */ referencedGoals = [] /* * Get my hint text. By default, we mark as Open any goals listed * in our referencedGoals list, then return our hintText string. * Individual Hint objects can override this as desired to apply any * additional side effects. */ getItemText() { /* scan the referenced goals list */ foreach (local cur in referencedGoals) { /* if this goal is not yet discovered, open it */ if (cur.goalState == UndiscoveredGoal) cur.goalState = OpenGoal; } /* return our hint text */ return hintText; } ; /* * A hint menu. This same class can be used for the top-level hints * menu and for sub-menus within the hints menu. * * The typical hint menu system will be structured into a top-level hint * menu that contains a set of sub-menus for the main areas of the game; * each sub-menu will have a series of Goal items, each Goal providing a * set of answers to a particular question. Something like this: * * topHintMenu: TopHintMenu 'Hints'; *. + HintMenu 'General Questions'; *. ++ Goal 'What am I supposed to be doing?' [answer, answer, answer]; *. ++ Goal 'Amusing things to try' [thing, thing, thing]; *. + HintMenu 'First Area'; *. ++ Goal 'How do I get past the shark?' [answer, answer, answer]; *. ++ Goal 'How do I open the fish tank?' [answer, answer, answer]; *. + HintMenu 'Second Area'; *. ++ Goal 'Where is the gold key?' [answer, answer, answer]; *. ++ Goal 'How do I unlock the gold door?' [answer, answer, answer]; * * Note that there's no requirement that the hint menu tree takes * exactly this shape. A very small game could dispense with the * submenus and simply put all of the goals directly in the top hint * menu. A very large game with lots of goals could add more levels of * sub-menus to make it easier to navigate the large number of topics. */ class HintMenu: MenuItem, HintMenuObject /* the menu's title */ title = '' /* update our contents */ updateContents() { local vec = new Vector(16); /* * First, run through all of our sub-items, and update their * contents. We only want to show our active contents, so we * need to check with each item to find out which is active. */ foreach (local cur in allContents) cur.updateContents(); /* create a vector containing all of our active items */ foreach (local cur in allContents) { /* if this item is active, add it to the active vector */ if (cur.isActiveInMenu) vec.append(cur); } /* set our contents list to the list of active items */ contents = vec; } /* we're active in a menu if we have any active contents */ isActiveInMenu = (contents.length() != 0) /* add a sub-item to our contents */ addToContents(obj) { /* * add the sub-item to our allContents list rather than our * active contents */ allContents += obj; } /* initialize our contents list */ initializeContents() { /* sort our allContents list in the object-defined sorting order */ allContents = allContents.sort( SortAsc, {a, b: a.compareForTopicSort(b)}); } /* * our list of all of our sub-items (some of which may not be * active, in which case they'll appear in this list but not in our * 'contents' list, which contains only active contents) */ allContents = [] ; /* * A hint menu version of the long topic menu. */ class HintLongTopicItem: MenuLongTopicItem, HintMenuObject /* * presume these are always active - they're usually used for things * like hint system instructions that should always be available */ isActiveInMenu = true ; /* * Top-level hint menu. As a convenience, an object defined of this * class will automatically register itself as the top-level hint menu * during pre-initialization. */ class TopHintMenu: HintMenu, PreinitObject /* register as the top-level hint menu during pre-initialization */ execute() { hintManager.topHintMenuObj = self; } ; /* ------------------------------------------------------------------------ */ /* * The default hint system user interface implementation. All of the * hint-related verbs operate by calling methods in the object stored in * the global variable gHintSystem, which we'll by default initialize * with a reference to this object. Games can replace this with their * own implementations if desired. */ hintManager: PreinitObject /* during pre-initialization, register as the global hint manager */ execute() { gHintManager = self; } /* * Disable hints - this is invoked by the HINTS OFF action. * * Some users don't like on-line hint systems because they find them * to be too much of a temptation. To address this concern, we * provide this HINTS OFF command. Players who want to ensure that * their will-power won't crumble later on in the face of a * difficult puzzle can type HINTS OFF early on, before the going * gets rough; this will disable hints for the rest of the session. * It's kind of like giving your credit card to a friend before * going to the mall, making the friend promise that they won't let * you spend more than such and such an amount, no matter how much * you beg and plead. */ disableHints() { /* * Remember that hints have been disabled. Keep this * information in the transient session object, since we want * the disabled status to last for the rest of this session, * even if we restore or restart later. */ sessionHintStatus.hintsDisabled = true; /* acknowledge it */ mainReport(gLibMessages.hintsDisabled); } /* * The top-level hint menu. This must be provided by the game, and * should be set during initialization. If this is nil, hints won't * be available. * * We don't provide a default top-level hint menu because we want to * give the game maximum flexibility in defining this object exactly * as it wants. For convenience, an object of class TopHintMenu * will automatically register itself during pre-initialization - * but note that there should be only one such object in the entire * game, since if there are more than one, only one will be * arbitrarily chosen as the registered object. */ topHintMenuObj = nil /* * Show hints - invoke the hint system. */ showHints() { /* if there is no top-level hint menu, no hints are available */ if (topHintMenuObj == nil) { mainReport(gLibMessages.hintsNotPresent); return; } /* if hints are disabled, reject the request */ if (sessionHintStatus.hintsDisabled) { mainReport(gLibMessages.sorryHintsDisabled); return; } /* bring the hint menu tree up to date */ topHintMenuObj.updateContents(); /* if there are no hints available, say so and give up */ if (topHintMenuObj.contents.length() == 0) { mainReport(gLibMessages.currentlyNoHints); return; } /* if we haven't warned about hints, do so now */ if (!showHintWarning()) return; /* display the hint menu */ topHintMenuObj.display(); /* all done */ mainReport(gLibMessages.hintsDone); } /* * Show a warning before showing any hints. By default, we'll show * this at most once per session or once per saved game. Returns * true if we are to proceed to the hints, nil if not. */ showHintWarning() { /* * If we have previously warned in this session, or if we've * warned in a previous session and the same game was later * saved and restored, don't warn again. The transient session * object tells us if we've asked in this session; the normal * persistent object tells us if we've asked in a previous * session that we've since saved and restored. */ if (!sessionHintStatus.hintWarning && !gameHintStatus.hintWarning) { /* * we haven't asked yet in either the session or the game, * so show the warning now */ gLibMessages.showHintWarning(); /* note that we've shown the warning */ sessionHintStatus.hintWarning = true; gameHintStatus.hintWarning = true; /* don't proceed to hints now; let them ask again */ return nil; } /* * They've already seen the warning before. It's possible that * they've seen it in a past session with the game and not * otherwise during this session, but now that we're accessing * the hint system once, don't bother with another warning for * the rest of this session. */ sessionHintStatus.hintWarning = true; /* proceed to the hints */ return true; } ; /* * We keep several pieces of information about the status of the hint * system. Some of it pertains to the current session, independently of * any saving/restoring/restarting, so we keep this information in a * transient object. Some pertains to the present game, so we keep it * in an ordinary persistent object, so that it's saved and restored * along with the game. */ transient sessionHintStatus: object /* flag: we've warned about the hint system in this session */ hintWarning = nil /* flag: we've disabled hints for this session */ hintsDisabled = nil ; gameHintStatus: object /* flag: we've warned about the hint system in this session */ hintWarning = nil ; frobtads-1.2.3/tads3/lib/adv3/footnote.t0000644000175000001440000002117711035466341017162 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - footnotes * * This module defines objects related to footnotes. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Footnote - this allows footnote references to be generated in * displayed text, and the user to retrieve the contents of the footnote * on demand. * * Create an instance of Footnote for each footnote. For each footnote * object, define the "desc" property as a double-quoted string (or * method) displaying the footnote's contents. * * To display a footnote reference in a passage of text, call * <>, where x is the footnote object in question. That's all * you have to do - we'll automatically assign the footnote a sequential * number (so that footnote references are always seen by the player in * ascending order, regardless of the order in which the player * encounters the sources of the footnotes), and the NOTE command will * automatically figure out which footnote object is involved for a given * footnote number. * * This class also serves as a daemon notification object to receive * per-command daemon calls. The first time we show a footnote * reference, we'll show an explanation of how footnotes work. */ class Footnote: object /* * Display the contents of the footnote - this will be called when * the user asks to show the footnote with the "NOTE n" command. * Each instance must provide suitable text here. */ desc = "" /* * Get a reference to the footnote for use in a passage of text. * This returns a single-quoted string to display as a reference to * the footnote. */ noteRef { /* * If the sensory context is blocking output, do not consider * this a reference to the footnote at all, since the player * won't see it. */ if (senseContext.isBlocking) return ''; /* * if we haven't already assigned a number to this footnote, * assign one now */ if (footnoteNum == nil) { /* * Allocate a new footnote number and remember it as our * own. Note that we want the last footnote number for all * footnotes, so use the Footnote class property * lastFootnote. */ footnoteNum = ++(Footnote.lastFootnote); /* * add myself to the class's list of numbered notes, so we * can find this footnote easily again given its number */ Footnote.numberedFootnotes.append(self); /* note that we've generated a footnote reference */ Footnote.everShownFootnote = true; } /* * If we're allowed to show footnotes, return the library * message text to display given the note number. If all * footnotes are being hidden, or if we're only showing new * footnotes and we've already read this one, return an empty * string. */ switch(footnoteSettings.showFootnotes) { case FootnotesFull: /* we're showing all footnotes unconditionally */ return gLibMessages.footnoteRef(footnoteNum); case FootnotesMedium: /* we're only showing unread footnotes */ return footnoteRead ? '' : gLibMessages.footnoteRef(footnoteNum); case FootnotesOff: /* we're hiding all footnotes unconditionally */ return ''; } /* * in case the status is invalid and we fall through, return an * empty string as a last resort */ return ''; } /* * Display a footnote given its number. If there is no such * footnote, we'll display an error message saying so. (This is a * class method, so it should be called directly on Footnote, not on * instances of Footnote.) */ showFootnote(num) { /* * if there's a footnote for this number, display it; otherwise, * display an error explaining that the footnote number is * invalid */ if (num >= 1 && num <= lastFootnote) { local fn; /* * it's a valid footnote number - get the footnote object * from our vector of footnotes, simply using the footnote * number as an index into the vector */ fn = numberedFootnotes[num]; /* show its description by calling 'desc' method */ fn.desc; /* note that this footnote text has been read */ fn.footnoteRead = true; } else { /* there is no such footnote */ gLibMessages.noSuchFootnote(num); } } /* SettingsItem tracking our current status */ footnoteSettings = footnoteSettingsItem /* * my footnote number - this is assigned the first time I'm * referenced; initially we have no number, since we don't want to * assign a number until the note is first referenced */ footnoteNum = nil /* * Flag: this footnote's full text has been displayed. This refers * to the text of the footnote itself, not the reference, so this is * only set when the "FOOTNOTE n" command is used to read this * footnote. */ footnoteRead = nil /* * Static property: the highest footnote number currently in use. * We start this at zero, because zero is never a valid footnote * number. */ lastFootnote = 0 /* * Static property: a vector of all footnotes which have had numbers * assigned. We use this to find a footnote object given its note * number. */ numberedFootnotes = static new Vector(20) /* static property: we've never shown a footnote reference before */ everShownFootnote = nil /* static property: per-command-prompt daemon entrypoint */ checkNotification() { /* * If we've ever shown a footnote, show the footnote * notification now. Note that we know we've never shown a * notification before simply because we're still running - we * remove this daemon as soon as it shows its notification. */ if (everShownFootnote) { /* show the first footnote notification */ gLibMessages.firstFootnote(); /* * We only want to show this notification once in the whole * game, so we can cancel this daemon now. Since we're the * event that's running, we can just tell the event manager * to remove the current event from receiving further * notifications. */ eventManager.removeCurrentEvent(); } } ; /* our FOOTNOTES settings item */ footnoteSettingsItem: SettingsItem /* our current status - the factory default is "medium" */ showFootnotes = FootnotesMedium /* our config file variable ID */ settingID = 'adv3.footnotes' /* show our short description */ settingDesc = (gLibMessages.shortFootnoteStatus(showFootnotes)) /* get the setting's external file string representation */ settingToText() { switch(showFootnotes) { case FootnotesMedium: return 'medium'; case FootnotesFull: return 'full'; default: return 'off'; } } settingFromText(str) { /* convert to lower-case and strip off spaces */ if (rexMatch('*(+)', str.toLower()) != nil) str = rexGroup(1)[3]; /* check the keyword */ switch (str) { case 'off': showFootnotes = FootnotesOff; break; case 'medium': showFootnotes = FootnotesMedium; break; case 'full': showFootnotes = FootnotesFull; break; } } ; /* pre-initialization - set up the footnote explanation daemon */ PreinitObject execute() { /* since we're available, register as the global footnote handler */ libGlobal.footnoteClass = Footnote; /* initialize the footnote notification daemon */ new PromptDaemon(Footnote, &checkNotification); } ; frobtads-1.2.3/tads3/lib/adv3/adv3.tl0000644000175000001440000000136311476200611016324 0ustar realncusersname: TADS 3 Adventure Library (language-independent) source: action source: actions source: actor source: banner source: console source: disambig source: events source: exec source: exits source: extras source: footnote source: hintsys source: input source: lister source: menusys source: menucon source: misc source: modid source: numbers source: objects source: output source: parser source: pov source: precond source: report source: resolver source: score source: sense source: settings source: status source: tips source: thing source: travel source: verify needmacro: LANGUAGE this selects the language the library uses in its messages and parsing, using ISO 639/3166 language/country codes: US English is "en_us" library: $(LANGUAGE)/$(LANGUAGE) frobtads-1.2.3/tads3/lib/adv3/output.t0000644000175000001440000021432111747340413016661 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - output formatting * * This module defines the framework for displaying output text. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * The standard library output function. We set this up as the default * display function (for double-quoted strings and for "<< >>" * embeddings). Code can also call this directly to display items. */ say(val) { /* send output to the active output stream */ outputManager.curOutputStream.writeToStream(val); } /* ------------------------------------------------------------------------ */ /* * Generate a string for showing quoted text. We simply enclose the * text in a ... tag sequence and return the result. */ withQuotes(txt) { return '<>'; } /* ------------------------------------------------------------------------ */ /* * Output Manager. This object contains global code for displaying text * on the console. * * The output manager is transient because we don't want its state to be * saved and restored; the output manager state is essentially part of * the intepreter user interface, which is not affected by save and * restore. */ transient outputManager: object /* * Switch to a new active output stream. Returns the previously * active output stream, so that the caller can easily restore the * old output stream if the new output stream is to be established * only for a specific duration. */ setOutputStream(ostr) { local oldStr; /* remember the old stream for a moment */ oldStr = curOutputStream; /* set the new output stream */ curOutputStream = ostr; /* * return the old stream, so the caller can restore it later if * desired */ return oldStr; } /* * run the given function, using the given output stream as the * active default output stream */ withOutputStream(ostr, func) { /* establish the new stream */ local oldStr = setOutputStream(ostr); /* make sure we restore the old active stream on the way out */ try { /* invoke the callback */ (func)(); } finally { /* restore the old output stream */ setOutputStream(oldStr); } } /* the current output stream - start with the main text stream */ curOutputStream = mainOutputStream /* * Is the UI running in HTML mode? This tells us if we have a full * HTML UI or a text-only UI. Full HTML mode applies if we're * running on a Multimedia TADS interpreter, or we're using the Web * UI, which runs in a separate browser and is thus inherently * HTML-capable. * * (The result can't change during a session, since it's a function * of the game and interpreter capabilities, so we store the result * on the first evaluation to avoid having to recompute it on each * query. Since 'self' is a static object, we'll recompute this each * time we run the program, which is important because we could save * the game on one interpreter and resume the session on a different * interpreter with different capabilities.) */ htmlMode = (self.htmlMode = checkHtmlMode()) ; /* ------------------------------------------------------------------------ */ /* * Output Stream. This class provides a stream-oriented interface to * displaying text on the console. "Stream-oriented" means that we write * text as a sequential string of characters. * * Output streams are always transient, since they track the system user * interface in the interpreter. The interpreter does not save its UI * state with a saved position, so objects such as output streams that * track the UI state should not be saved either. */ class OutputStream: PreinitObject /* * Write a value to the stream. If the value is a string, we'll * display the text of the string; if it's nil, we'll ignore it; if * it's anything else, we'll try to convert it to a string (with the * toString() function) and display the resulting text. */ writeToStream(val) { /* convert the value to a string */ switch(dataType(val)) { case TypeSString: /* * it's a string - no conversion is needed, but if it's * empty, it doesn't count as real output (so don't notify * anyone, and don't set any output flags) */ if (val == '') return; break; case TypeNil: /* nil - don't display anything for this */ return; case TypeInt: case TypeObject: /* convert integers and objects to strings */ val = toString(val); break; } /* run it through our output filters */ val = applyFilters(val); /* * if, after filtering, we're not writing anything at all, * there's nothing left to do */ if (val == nil || val == '') return; /* write the text to our underlying system stream */ writeFromStream(val); } /* * Watch the stream for output. It's sometimes useful to be able to * call out to some code and determine whether or not the code * generated any text output. This routine invokes the given * callback function, monitoring the stream for output; if any * occurs, we'll return true, otherwise we'll return nil. */ watchForOutput(func) { local mon; /* set up a monitor filter on the stream */ addOutputFilter(mon = new MonitorFilter()); /* catch any exceptions so we can remove our filter before leaving */ try { /* invoke the callback */ (func)(); /* return the monitor's status, indicating if output occurred */ return mon.outputFlag; } finally { /* remove our monitor filter */ removeOutputFilter(mon); } } /* * Call the given function, capturing all text output to this stream * in the course of the function call. Return a string containing * the captured text. */ captureOutput(func, [args]) { /* install a string capture filter */ local filter = new StringCaptureFilter(); addOutputFilter(filter); /* make sure we don't leave without removing our capturer */ try { /* invoke the function */ (func)(args...); /* return the text that we captured */ return filter.txt_; } finally { /* we're done with our filter, so remove it */ removeOutputFilter(filter); } } /* my associated input manager, if I have one */ myInputManager = nil /* dynamic construction */ construct() { /* * Set up filter list. Output streams are always transient, so * make our filter list transient as well. */ filterList_ = new transient Vector(10); } /* execute pre-initialization */ execute() { /* do the same set-up we would do for dynamic construction */ construct(); } /* * Write text out from this stream; this writes to the lower-level * stream underlying this stream. This routine is intended to be * called only from within this class. * * Each output stream is conceptually "stacked" on top of another, * lower-level stream. At the bottom of the stack is usually some * kind of physical device, such as the display, or a file on disk. * * This method must be defined in each subclass to write to the * appropriate underlying stream. Most subclasses are specifically * designed to sit atop a system-level stream, such as the display * output stream, so most implementations of this method will call * directly to a system-level output function. */ writeFromStream(txt) { } /* * The list of active filters on this stream, in the order in which * they are to be called. This should normally be initialized to a * Vector in each instance. */ filterList_ = [] /* * Add an output filter. The argument is an object of class * OutputFilter, or any object implementing the filterText() method. * * Filters are always arranged in a "stack": the last output filter * added is the first one called during output. This method thus * adds the new filter at the "top" of the stack. */ addOutputFilter(filter) { /* add the filter to the end of our list */ filterList_.append(filter); } /* * Add an output filter at a given point in the filter stack: add * the filter so that it is "below" the given existing filter in the * stack. This means that the new filter will be called just after * the existing filter during output. * * If 'existingFilter' isn't in the stack of existing filters, we'll * add the new filter at the "top" of the stack. */ addOutputFilterBelow(newFilter, existingFilter) { /* find the existing filter in our list */ local idx = filterList_.indexOf(existingFilter); /* * If we found the old filter, add the new filter below the * existing filter in the stack, which is to say just before the * old filter in our vector of filters (since we call the * filters in reverse order of the list). * * If we didn't find the existing filter, simply add the new * filter at the top of the stack, by appending the new filter * at the end of the list. */ if (idx != nil) filterList_.insertAt(idx, newFilter); else filterList_.append(newFilter); } /* * Remove an output filter. Since filters are arranged in a stack, * only the LAST output filter added may be removed. It's an error * to remove a filter other than the last one. */ removeOutputFilter(filter) { /* get the filter count */ local len = filterList_.length(); /* make sure it's the last filter */ if (len == 0 || filterList_[len] != filter) t3DebugTrace(T3DebugBreak); /* remove the filter from my list */ filterList_.removeElementAt(len); } /* call the filters */ applyFilters(val) { /* * Run through the list, applying each filter in turn. We work * backwards through the list from the last element, because the * filter list is a stack: the last element added is the topmost * element of the stack, so it must be called first. */ for (local i in filterList_.length()..1 step -1 ; val != nil ; ) val = filterList_[i].filterText(self, val); /* return the result of all of the filters */ return val; } /* * Apply the current set of text transformation filters to a string. * This applies only the non-capturing filters; we skip any capture * filters. */ applyTextFilters(val) { /* run through the filter stack from top to bottom */ for (local i in filterList_.length()..1 step -1 ; val != nil ; ) { /* skip capturing filters */ local f = filterList_[i]; if (f.ofKind(CaptureFilter)) continue; /* apply the filter */ val = f.filterText(self, val); } /* return the result */ return val; } /* * Receive notification from the input manager that we have just * ended reading a line of input from the keyboard. */ inputLineEnd() { /* an input line ending doesn't look like a paragraph */ justDidPara = nil; } /* * Internal state: we just wrote a paragraph break, and there has * not yet been any intervening text. By default, we set this to * true initially, so that we suppress any paragraph breaks at the * very start of the text. */ justDidPara = true /* * Internal state: we just wrote a character that suppresses * paragraph breaks that immediately follow. In this state, we'll * suppress any paragraph marker that immediately follows, but we * won't suppress any other characters. */ justDidParaSuppressor = nil ; /* * The OutputStream for the main text area. * * This object is transient because the output stream state is * effectively part of the interpreter user interface, which is not * affected by save and restore. */ transient mainOutputStream: OutputStream /* * The main text area is the same place where we normally read * command lines from the keyboard, so associate this output stream * with the primary input manager. */ myInputManager = inputManager /* the current command transcript */ curTranscript = nil /* we sit atop the system-level main console output stream */ writeFromStream(txt) { /* if an input event was interrupted, cancel the event */ inputManager.inputEventEnd(); /* write the text to the console */ aioSay(txt); } ; /* ------------------------------------------------------------------------ */ /* * Paragraph manager. We filter strings as they're about to be sent to * the console to convert paragraph markers (represented in the source * text using the "style tag" format, <.P>) into a configurable display * rendering. * * We also process the zero-spacing paragraph, <.P0>. This doesn't * generate any output, but otherwise acts like a paragraph break in that * it suppresses any paragraph breaks that immediately follow. * * The special marker <./P0> cancels the effect of a <.P0>. This can be * used if you want to ensure that a newline or paragraph break is * displayed, even if a <.P0> was just displayed. * * Our special processing ensures that paragraph tags interact with one * another and with other display elements specially: * * - A run of multiple consecutive paragraph tags is treated as a single * paragraph tag. This property is particularly important because it * allows code to write out a paragraph marker without having to worry * about whether preceding code or following code add paragraph markers * of their own; if redundant markers are found, we'll filter them out * automatically. * * - We can suppress paragraph markers following other specific * sequences. For example, if the paragraph break is rendered as a blank * line, we might want to suppress an extra blank line for a paragraph * break after an explicit blank line. * * - We can suppress other specific sequences following a paragraph * marker. For example, if the paragraph break is rendered as a newline * plus a tab, we could suppress whitespace following the paragraph * break. * * The paragraph manager should always be instantiated with transient * instances, because this object's state is effectively part of the * interpreter user interface, which doesn't participate in save and * restore. */ class ParagraphManager: OutputFilter /* * Rendering - this is what we display on the console to represent a * paragraph break. By default, we'll display a blank line. */ renderText = '\b' /* * Flag: show or hide paragraph breaks immediately after input. By * default, we do not show paragraph breaks after an input line. */ renderAfterInput = nil /* * Preceding suppression. This is a regular expression that we * match to individual characters. If the character immediately * preceding a paragraph marker matches this expression, we'll * suppress the paragraph marker in the output. By default, we'll * suppress a paragraph break following a blank line, because the * default rendering would add a redundant blank line. */ suppressBefore = static new RexPattern('\b') /* * Following suppression. This is a regular expression that we * match to individual characters. If the character immediately * following a paragraph marker matches this expression, we'll * suppress the character. We'll apply this to each character * following a paragraph marker in turn until we find one that does * not match; we'll suppress all of the characters that do match. * By default, we suppress additional blank lines after a paragraph * break. */ suppressAfter = static new RexPattern('[\b\n]') /* pre-compile some regular expression patterns we use a lot */ leadingMultiPat = static new RexPattern('([pP]0?)+') leadingSinglePat = static new RexPattern( '([pP]0?|/[pP]0)') /* process a string that's about to be written to the console */ filterText(ostr, txt) { local ret; /* we don't have anything in our translated string yet */ ret = ''; /* keep going until we run out of string to process */ while (txt != '') { local len; local match; local p0; local unp0; /* * if we just wrote a paragraph break, suppress any * character that matches 'suppressAfter', and suppress any * paragraph markers that immediately follow */ if (ostr.justDidPara) { /* check for any consecutive paragraph markers */ if ((len = rexMatch(leadingMultiPat, txt)) != nil) { /* discard the consecutive <.P>'s, and keep going */ txt = txt.substr(len + 1); continue; } /* check for a match to the suppressAfter pattern */ if (rexMatch(suppressAfter, txt) != nil) { /* discard the suppressed character and keep going */ txt = txt.substr(2); continue; } } /* * we have a character other than a paragraph marker, so we * didn't just scan a paragraph marker */ ostr.justDidPara = nil; /* * if we just wrote a suppressBefore character, discard any * leading paragraph markers */ if (ostr.justDidParaSuppressor && (len = rexMatch(leadingMultiPat, txt)) != nil) { /* remove the paragraph markers */ txt = txt.substr(len + 1); /* * even though we're not rendering the paragraph, note * that a logical paragraph just started */ ostr.justDidPara = true; /* keep going */ continue; } /* presume we won't find a <.p0> or <./p0> */ p0 = unp0 = nil; /* find the next paragraph marker */ match = rexSearch(leadingSinglePat, txt); if (match == nil) { /* * there are no more paragraph markers - copy the * remainder of the input string to the output */ ret += txt; txt = ''; /* we just did something other than a paragraph */ ostr.justDidPara = nil; } else { /* add everything up to the paragraph break to the output */ ret += txt.substr(1, match[1] - 1); /* get the rest of the string following the paragraph mark */ txt = txt.substr(match[1] + match[2]); /* note if we found a <.p0> or <./p0> */ p0 = (match[3] is in ('<.p0>', '<.P0>')); unp0 = (match[3] is in ('<./p0>', '<./P0>')); /* * note that we just found a paragraph marker, unless * this is a <./p0> */ ostr.justDidPara = !unp0; } /* * If the last character we copied out is a suppressBefore * character, note for next time that we have a suppressor * pending. Likewise, if we found a <.p0> rather than a * <.p>, this counts as a suppressor. */ ostr.justDidParaSuppressor = (p0 || rexMatch(suppressBefore, ret.substr(ret.length(), 1)) != nil); /* * if we found a paragraph marker, and we didn't find a * leading suppressor character just before it, add the * paragraph rendering */ if (ostr.justDidPara && !ostr.justDidParaSuppressor) ret += renderText; } /* return the translated string */ return ret; } ; /* the paragraph manager for the main output stream */ transient mainParagraphManager: ParagraphManager ; /* ------------------------------------------------------------------------ */ /* * Output Filter */ class OutputFilter: object /* * Apply the filter - this should be overridden in each filter. The * return value is the result of filtering the string. * * 'ostr' is the OutputStream to which the text is being written, * and 'txt' is the original text to be displayed. */ filterText(ostr, txt) { return txt; } ; /* ------------------------------------------------------------------------ */ /* * Output monitor filter. This is a filter that leaves the filtered * text unchanged, but keeps track of whether any text was seen at all. * Our 'outputFlag' is true if we've seen any output, nil if not. */ class MonitorFilter: OutputFilter /* filter text */ filterText(ostr, val) { /* if the value is non-empty, note the output */ if (val != nil && val != '') outputFlag = true; /* return the input value unchanged */ return val; } /* flag: has any output occurred for this monitor yet? */ outputFlag = nil ; /* ------------------------------------------------------------------------ */ /* * Capture Filter. This is an output filter that simply captures all of * the text sent through the filter, sending nothing out to the * underlying stream. * * The default implementation simply discards the incoming text. * Subclasses can keep track of the text in memory, in a file, or * wherever desired. */ class CaptureFilter: OutputFilter /* * Filter the text. We simply discard the text, passing nothing * through to the underlying stream. */ filterText(ostr, txt) { /* leave nothing for the underlying stream */ return nil; } ; /* * "Switchable" capture filter. This filter can have its blocking * enabled or disabled. When blocking is enabled, we capture * everything, leaving nothing to the underlying stream; when disabled, * we pass everything through to the underyling stream unchanged. */ class SwitchableCaptureFilter: CaptureFilter /* filter the text */ filterText(ostr, txt) { /* * if we're blocking output, return nothing to the underlying * stream; if we're disabled, return the input unchanged */ return (isBlocking ? nil : txt); } /* * Blocking enabled: if this is true, we'll capture all text passed * through us, leaving nothing to the underyling stream. Blocking * is enabled by default. */ isBlocking = true ; /* * String capturer. This is an implementation of CaptureFilter that * saves the captured text to a string. */ class StringCaptureFilter: CaptureFilter /* filter text */ filterText(ostr, txt) { /* add the text to my captured text so far */ addText(txt); } /* add to my captured text */ addText(txt) { /* append the text to my string of captured text */ txt_ += txt; } /* my captured text so far */ txt_ = '' ; /* ------------------------------------------------------------------------ */ /* * Style tag. This defines an HTML-like tag that can be used in output * text to display an author-customizable substitution string. * * Each StyleTag object defines the name of the tag, which can be * invoked in output text using the syntax "<.name>" - we require the * period after the opening angle-bracket to plainly distinguish the * sequence as a style tag, not a regular HTML tag. * * Each StyleTag also defines the text string that should be substituted * for each occurrence of the "<.name>" sequence in output text, and, * optionally, another string that is substituted for occurrences of the * "closing" version of the tag, invoked with the syntax "<./name>". */ class StyleTag: object /* name of the tag - the tag appears in source text in <.xxx> notation */ tagName = '' /* * opening text - this is substituted for each instance of the tag * without a '/' prefix */ openText = '' /* * Closing text - this is substituted for each instance of the tag * with a '/' prefix (<./xxx>). Note that non-container tags don't * have closing text at all. */ closeText = '' ; /* * HtmlStyleTag - this is a subclass of StyleTag that provides different * rendering depending on whether the interpreter is in HTML mode or not. * In HTML mode, we display our htmlOpenText and htmlCloseText; when not * in HTML mode, we display our plainOpenText and plainCloseText. */ class HtmlStyleTag: StyleTag openText = (outputManager.htmlMode ? htmlOpenText : plainOpenText) closeText = (outputManager.htmlMode ? htmlCloseText : plainCloseText) /* our HTML-mode opening and closing text */ htmlOpenText = '' htmlCloseText = '' /* our plain (non-HTML) opening and closing text */ plainOpenText = '' plainCloseText = '' ; /* * Define our default style tags. We name all of these StyleTag objects * so that authors can easily change the expansion text strings at * compile-time with the 'modify' syntax, or dynamically at run-time by * assigning new strings to the appropriate properties of these objects. */ /* * <.roomname> - we use this to display the room's name in the * description of a room (such as in a LOOK AROUND command, or when * entering a new location). By default, we display the room name in * boldface on a line by itself. */ roomnameStyleTag: StyleTag 'roomname' '\n' '
\n'; /* <.roomdesc> - we use this to display a room's long description */ roomdescStyleTag: StyleTag 'roomdesc' '' ''; /* * <.roompara> - we use this to separate paragraphs within a room's long * description */ roomparaStyleTag: StyleTag 'roompara' '<.p>\n'; /* * <.inputline> - we use this to display the text actually entered by the * user on a command line. Note that this isn't used for the prompt text * - it's used only for the command-line text itself. */ inputlineStyleTag: HtmlStyleTag 'inputline' /* in HTML mode, switch in and out of TADS-Input font */ htmlOpenText = '' htmlCloseText = '' /* in plain mode, do nothing */ plainOpenText = '' plainCloseText = '' ; /* * <.a> (named in analogy to the HTML tag) - we use this to display * hyperlinked text. Note that this goes *inside* an HTML tag - this * doesn't do the actual linking (the true tag does that), but rather * allows customized text formatting for hyperlinked text. */ hyperlinkStyleTag: HtmlStyleTag 'a' ; /* <.statusroom> - style for the room name in a status line */ statusroomStyleTag: HtmlStyleTag 'statusroom' htmlOpenText = '' htmlCloseText = '' ; /* <.statusscore> - style for the score in a status line */ statusscoreStyleTag: HtmlStyleTag 'statusscore' htmlOpenText = '' htmlCloseText = '' ; /* * <.parser> - style for messages explicitly from the parser. * * By default, we do nothing special with these messages. Many games * like to use a distinctive notation for parser messages, to make it * clear that the messages are "meta" text that's not part of the story * but rather specific to the game mechanics; one common convention is * to put parser messages in [square brackets]. * * If the game defines a special appearance for parser messages, for * consistency it might want to use the same appearance for notification * messages displayed with the <.notification> tag (see * notificationStyleTag). */ parserStyleTag: StyleTag 'parser' openText = '' closeText = '' ; /* * <.notification> - style for "notification" messages, such as score * changes and messages explaining how facilities (footnotes, exit * lists) work the first time they come up. * * By default, we'll put notifications in parentheses. Games that use * [square brackets] for parser messages (i.e., for the <.parser> tag) * might want to use the same notation here for consistency. */ notificationStyleTag: StyleTag 'notification' openText = '(' closeText = ')' ; /* * <.assume> - style for "assumption" messages, showing an assumption * the parser is making. This style is used for showing objects used by * default when not specified in a command, objects that the parser * chose despite some ambiguity, and implied commands. */ assumeStyleTag: StyleTag 'assume' openText = '(' closeText = ')' ; /* * <.announceObj> - style for object announcement messages. The parser * shows an object announcement for each object when a command is applied * to multiple objects (TAKE ALL, DROP KEYS AND WALLET). The * announcement simply shows the object's name and a colon, to let the * player know that the response text that follows applies to the * announced object. */ announceObjStyleTag: StyleTag 'announceObj' openText = '' closeText = '' ; /* ------------------------------------------------------------------------ */ /* * "Style tag" filter. This is an output filter that expands our * special style tags in output text. */ styleTagFilter: OutputFilter, PreinitObject /* pre-compile our frequently-used tag search pattern */ tagPattern = static new RexPattern( '%.(/?[a-z][a-z0-9]*)') /* filter for a style tag */ filterText(ostr, val) { local idx; /* search for our special '<.xxx>' tags, and expand any we find */ idx = rexSearch(tagPattern, val); while (idx != nil) { local xlat; local afterOfs; local afterStr; /* ask the formatter to translate it */ xlat = translateTag(rexGroup(1)[3]); /* get the part of the string that follows the tag */ afterOfs = idx[1] + idx[2]; afterStr = val.substr(idx[1] + idx[2]); /* * if we got a translation, replace it; otherwise, leave the * original text intact */ if (xlat != nil) { /* replace the tag with its translation */ val = val.substr(1, idx[1] - 1) + xlat + afterStr; /* * figure the offset of the remainder of the string in * the replaced version of the string - this is the * length of the original part up to the replacement * text plus the length of the replacement text */ afterOfs = idx[1] + xlat.length(); } /* * search for the next tag, considering only the part of * the string following the replacement text - we do not * want to re-scan the replacement text for tags */ idx = rexSearch(tagPattern, afterStr); /* * If we found it, adjust the starting index of the match to * its position in the actual string. Note that we do this * by adding the OFFSET of the remainder of the string, * which is 1 less than its INDEX, because idx[1] is already * a string index. (An offset is one less than an index * because the index of the first character is 1.) */ if (idx != nil) idx[1] += afterOfs - 1; } /* return the filtered value */ return val; } /* * Translate a tag */ translateTag(tag) { local isClose; local styleTag; /* if it's a close tag, so note and remove the leading slash */ isClose = tag.startsWith('/'); if (isClose) tag = tag.substr(2); /* look up the tag object in our table */ styleTag = tagTable[tag]; /* * if we found it, return the open or close text, as * appropriate; otherwise return nil */ return (styleTag != nil ? (isClose ? styleTag.closeText : styleTag.openText) : nil); } /* preinitialization */ execute() { /* create a lookup table for our style table */ tagTable = new LookupTable(); /* * Populate the table with all of the StyleTag instances. Key * by tag name, storing the tag object as the value for each * key. This will let us efficiently look up the StyleTag * object given a tag name string. */ forEachInstance(StyleTag, { tag: tagTable[tag.tagName] = tag }); } /* * Our tag translation table. We'll initialize this during preinit * to a lookup table with all of the defined StyleTag objects. */ tagTable = nil ; /* ------------------------------------------------------------------------ */ /* * MessageBuilder - this object provides a general text substitution * mechanism. Text to be substituted is enclosed in {curly braces}. * Within the braces, we have the substitution parameter name, which can * be in the following formats: * * id *. id obj *. id1/id2 obj *. id1 obj/id2 * * The ID string gives the type of substitution to perform. The ID's * all come from a table, which is specified by the language-specific * subclass, so the ID's can vary by language (to allow for natural * template-style parameter names for each language). If the ID is in * two pieces (id1 and id2), we concatenate the two pieces together with * a slash between to form the name we seek in the table - so {the/he * dobj} and {the dobj/he} are equivalent, and both look up the * identifier 'the/he'. If a two-part identifier is given, and the * identifier isn't found in the table, we'll try looking it up with the * parts reversed: if we see {he/the dobj}, we'll first try finding * 'he/the', and if that fails we'll look for 'the/he'. * * If 'obj' is present, it specificies the target object providing the * text to be substitutued; this is a string passed to the current * Action, and is usually something like 'actor', 'dobj', or 'iobj'. * * One instance of this class, called langMessageBuilder, should be * created by the language-specific library. */ class MessageBuilder: OutputFilter, PreinitObject /* pre-compile some regular expressions we use a lot */ patUpper = static new RexPattern('') patAllCaps = static new RexPattern('') patIdObjSlashId = static new RexPattern( '(<^space|/>+)+(<^space|/>+)(/<^space|/>+)') patIdObj = static new RexPattern('(<^space>+)+(<^space>+)') patIdSlash = static new RexPattern('([^/]+)/([^/]+)') /* * Given a source string with substitution parameters, generate the * expanded message with the appropriate text in place of the * parameters. */ generateMessage(orig) { local result; /* we have nothing in the result string so far */ result = ''; /* keep going until we run out of substitution parameters */ for (;;) { local idx; local paramStr; local paramName; local paramObj; local info; local initCap, allCaps; local targetObj; local newText; local prop; /* get the position of the next brace */ idx = orig.find('{'); /* * if there are no braces, the rest of the string is simply * literal text; add the entire remainder of the string to * the result, and we're done */ if (idx == nil) { result += processResult(genLiteral(orig)); break; } /* add everything up to the brace to the result string */ result += processResult(genLiteral(orig.substr(1, idx - 1))); /* * lop off everything up to and including the brace from the * source string, since we're done with the part up to the * brace now */ orig = orig.substr(idx + 1); /* * if the brace was the last thing in the source string, or * it's a stuttered brace, add it literally to the result */ if (orig.length() == 0) { /* * nothing follows - add a literal brace to the result, * and we're done */ result += processResult('{'); break; } else if (orig.substr(1, 1) == '{') { /* * it's a stuttered brace - add a literal brace to the * result */ result += processResult('{'); /* remove the second brace from the source */ orig = orig.substr(2); /* we're finished processing this brace - go back for more */ continue; } /* find the closing brace */ idx = orig.find('}'); /* * if there is no closing brace, include the brace and * whatever follows as literal text */ if (idx == nil) { /* add the literal brace to the result */ result += processResult('{'); /* we're done with the brace - go back for more */ continue; } /* * Pull out everything up to the brace as the parameter * text. */ paramStr = orig.substr(1, idx - 1); /* assume for now that we will have no parameter object */ paramObj = nil; /* * drop everything up to and including the closing brace, * since we've pulled it out for processing now */ orig = orig.substr(idx + 1); /* * Note the capitalization of the first two letters. If * they're both lower-case, we won't adjust the case of the * substitution text at all. If the first is a capital and * the second isn't, we'll capitalize the first letter of * the replacement text. If they're both capitals, we'll * capitalize the entire replacement text. */ initCap = (rexMatch(patUpper, paramStr) != nil); allCaps = (rexMatch(patAllCaps, paramStr) != nil); /* lower-case the entire parameter string for matching */ local origParamStr = paramStr; paramStr = paramStr.toLower(); /* perform any language-specific rewriting on the string */ paramStr = langRewriteParam(paramStr); /* * Figure out which format we have. The allowable formats * are: * * id * obj *. id obj *. id1/id2 *. id1/id2 obj *. id1 obj/id2 */ if (rexMatch(patIdObjSlashId, paramStr) != nil) { /* we have the id1 obj/id2 format */ paramName = rexGroup(1)[3] + rexGroup(3)[3]; paramObj = rexGroup(2)[3]; } else if (rexMatch(patIdObj, paramStr) != nil) { /* we have 'id obj' or 'id1/id2 obj' */ paramName = rexGroup(1)[3]; paramObj = rexGroup(2)[3]; } else { /* we have no spaces, so we have no target object */ paramName = paramStr; paramObj = nil; } /* look up our parameter name */ info = paramTable_[paramName]; /* * If we didn't find it, and the parameter name contains a * slash ('/'), try reversing the order of the parts before * and after the slash. */ if (info == nil && rexMatch(patIdSlash, paramName) != nil) { /* * rebuild the name with the order of the parts * reversed, and look up the result */ info = paramTable_[rexGroup(2)[3] + '/' + rexGroup(1)[3]]; } /* * If we didn't find a match, simply put the entire thing in * the result stream literally, including the braces. */ if (info == nil) { /* * We didn't find it, so try treating it as a string * parameter object. Try getting the string from the * action. */ newText = (gAction != nil ? gAction.getMessageParam(paramName) : nil); /* check what we found */ if (dataType(newText) == TypeSString) { /* * It's a valid string parameter. Simply add the * literal text to the result. If we're in html * mode, translate the string to ensure that any * markup-significant characters are properly quoted * so that they aren't taken as html themselves. */ result += processResult(newText.htmlify()); } else { /* * the parameter is completely undefined; simply add * the original text, including the braces */ result += processResult('{<>}'); } /* * we're done with this substitution string - go back * for more */ continue; } /* * If we have no target object specified in the substitution * string, and the parameter name has an associated implicit * target object, use the implied object. */ if (paramObj == nil && info[3] != nil) paramObj = info[3]; /* * If we have a target object name, ask the current action * for the target object value. Otherwise, use the same * target object as the previous expansion. */ if (paramObj != nil) { /* check for a current action */ if (gAction != nil) { /* get the target object by name through the action */ targetObj = gAction.getMessageParam(paramObj); } else { /* there's no action, so we don't have a value yet */ targetObj = nil; } /* * if we didn't find a value, look up the name in our * global name table */ if (targetObj == nil) { /* look up the name */ targetObj = nameTable_[paramObj]; /* * if we found it, and the result is a function * pointer or an anonymous function, invoke the * function to get the result */ if (dataTypeXlat(targetObj) == TypeFuncPtr) { /* evaluate the function */ targetObj = (targetObj)(); } } /* * remember this for next time, in case the next * substitution string doesn't include a target object */ lastTargetObj_ = targetObj; lastParamObj_ = paramObj; } else { /* * there's no implied or explicit target - use the same * one as last time */ targetObj = lastTargetObj_; paramObj = lastParamObj_; } /* * if the target object wasn't found, treat the whole thing * as a failure - put the entire parameter string back in * the result stream literally */ if (targetObj == nil) { /* add it to the output literally, and go back for more */ result += processResult('{' + paramStr + '}'); continue; } /* get the property to call on the target */ prop = getTargetProp(targetObj, paramObj, info); /* evaluate the parameter's associated property on the target */ newText = targetObj.(prop); /* apply the appropriate capitalization to the result */ if (allCaps) newText = newText.toUpper(); else if (initCap) newText = newText.substr(1, 1).toUpper() + newText.substr(2); /* * append the new text to the output result so far, and * we're finished with this round */ result += processResult(newText); } /* return the result string */ return result; } /* * Get the property to invoke on the target object for the given * parameter information entry. By default, we simply return * info[2], which is the standard property to call on the target. * This can be overridden by the language-specific subclass to * provide a different property if appropriate. * * 'targetObj' is the target object, and 'paramObj' is the parameter * name of the target object. For example, 'paramObj' might be the * string 'dobj' to represent the direct object, in which case * 'targetObj' will be the gDobj object. * * The English version, for example, uses this routine to supply a * reflexive instead of the default entry when the target object * matches the subject of the sentence. */ getTargetProp(targetObj, paramObj, info) { /* return the standard property mapping from the parameter info */ return info[2]; } /* * Process result text. This takes some result text that we're * about to add and returns a processed version. This is called for * all text as we add it to the result string. * * The text we pass to this method has already had all parameter * text fully expanded, so this routine does not need to worry about * { } sequences - all { } sequences will have been removed and * replaced with the corresponding expansion text before this is * called. * * This routine is called piecewise: the routine will be called once * for each parameter replacement text and once for each run of text * between parameters, and is called in the order in which the text * appears in the original string. * * By default we do nothing with the result text; we simply return * the original text unchanged. The language-specific subclass can * override this as desired to further modify the text for special * language-specific parameterization outside of the { } mechanism. * The subclass can also use this routine to maintain internal state * that depends on sentence structure. For example, the English * version looks for sentence-ending punctuation so that it can * reset its internal notion of the subject of the sentence when a * sentence appears to be ending. */ processResult(txt) { return txt; } /* * "Quote" a message - double each open or close brace, so that braces in * the message will be taken literally when run through the substitution * replacer. This can be used when a message is expanded prior to being * displayed to ensure that braces in the result won't be mistaken as * substitution parameters requiring further expansion. * * Note that only open braces need to be quoted, since lone close braces * are ignored in the substitution process. */ quoteMessage(str) { return str.findReplace(['{', '}'], ['{{', '}}'], ReplaceAll); } /* * Internal routine - generate the literal text for the given source * string. We'll remove any stuttered close braces. */ genLiteral(str) { /* replace all '}}' sequences with '}' sequences */ return str.findReplace('}}', '}', ReplaceAll); } /* * execute pre-initialization */ execute() { /* create a lookup table for our parameter names */ paramTable_ = new LookupTable(); /* add each element of our list to the table */ foreach (local cur in paramList_) paramTable_[cur[1]] = cur; /* create a lookup table for our global names */ nameTable_ = new LookupTable(); /* * Add an entry for 'actor', which resolves to gActor if there is * a gActor when evaluated, or the current player character if * not. Note that using a function ensures that we evaluate the * current gActor or gPlayerChar each time we need the 'actor' * value. */ nameTable_['actor'] = {: gActor != nil ? gActor : gPlayerChar }; } /* * Our output filter method. We'll run each string written to the * display through our parameter substitution method. */ filterText(ostr, txt) { /* substitute any parameters in the string and return the result */ return generateMessage(txt); } /* * The most recent target object. Each time we parse a substitution * string, we'll remember the target object here; when a * substitution string doesn't imply or specify a target object, * we'll use the previous one by default. */ lastTargetObj_ = nil /* the parameter name of the last target object ('dobj', 'actor', etc) */ lastParamObj_ = nil /* our parameter table - a LookupTable that we set up during preinit */ paramTable_ = nil /* our global name table - a LookupTable we set up during preinit */ nameTable_ = nil /* * Rewrite the parameter string for any language-specific rules. By * default, we'll return the original parameter string unchanged; * the language-specific instance can override this to provide any * special syntax extensions to the parameter string syntax desired * by the language-specific library. The returned string must be in * one of the formats recognized by the generic handler. */ langRewriteParam(paramStr) { /* by default, return the original unchanged */ return paramStr; } /* * our parameter list - this should be initialized in the * language-specific subclass to a list like this: * * [entry1, entry2, entry3, ...] * * Each entry is a list like this: * * [paramName, &prop, impliedTargetName, ] * * paramName is a string giving the substitution parameter name; * this can be one word or two ('the' or 'the obj', for example). * * prop is a property identifier. This is the property invoked on * the target object to obtain the substitution text. * * impliedTargetName is a string giving the target object name to * use. When this is supplied, the paramName is normally used in * message text with no object name. This should be nil for * parameters that do not imply a particular target. * * is any number of additional parameters for the * language-specific subclass. The generic code ignores these extra * parameters, but the langague-specific subclass can use them if it * requires additional information. * * Here's an example: * * paramList_ = [ *. ['you', &theDesc, nil, 'actor'], *. ['the obj' &theObjDesc, &itReflexive, nil] *. ] * * The first item specifies a substitution name of 'you', which is * expanded by evaluating the property theDesc on the target object, * and specifies an implied target object of 'actor'. When this is * expanded, we'll call the current action to get the meaning of * 'actor', then evaulate property theDesc on the result. * * The second item specifies a substitution name of 'the obj', * expanded by evaluating property theObjDesc on the target object. * This one doesn't have an implied object, so the target object is * the one explicitly given in the message source text or is the * previous target object if one isn't specified in the message * text. */ paramList_ = [] ; /* ------------------------------------------------------------------------ */ /* * Command Sequencer Filter. This is an output filter that handles the * special <.commandsep> tag for visual command separation. This tag has * the form of a style tag, but must be processed specially. * * <.commandsep> shows an appropriate separator between commands. Before * the first command output or after the last command output, this has no * effect. A run of multiple consecutive <.commandsep> tags is treated * as a single tag. * * Between commands, we show gLibMessages.commandResultsSeparator. After * an input line and before the first command result text, we show * gLibMessages.commandResultsPrefix. After the last command result text * before a new input line, we show gLibMessages.commandResultsSuffix. * If we read two input lines, and there is no intervening text output at * all, we show gLibMessages.commandResultsEmpty. * * The input manager should write a <.commandbefore> tag whenever it * starts reading a command line, and a <.commandafter> tag whenever it * finishes reading a command line. */ enum stateReadingCommand, stateBeforeCommand, stateBeforeInterruption, stateInCommand, stateBetweenCommands, stateWriteThrough, stateNoCommand; transient commandSequencer: OutputFilter /* * Force the sequencer into mid-command mode. This can be used to * defeat the resequencing into before-results mode that occurs if * any interactive command-line input must be read in the course of * a command's execution. */ setCommandMode() { state_ = stateInCommand; } /* * Internal routine: write the given text directly through us, * skipping any filtering we'd otherwise apply. */ writeThrough(txt) { local oldState; /* remember our old state */ oldState = state_; /* set our state to write-through */ state_ = stateWriteThrough; /* make sure we reset things on the way out */ try { /* write the text */ say(txt); } finally { /* restore our old state */ state_ = oldState; } } /* pre-compile our tag sequence pattern */ patNextTag = static new RexPattern( '' + 'command(sep|int|before|after|none|mid)' + '') /* * Apply our filter */ filterText(ostr, txt) { local ret; /* * if we're in write-through mode, simply pass the text through * unchanged */ if (state_ == stateWriteThrough) return txt; /* scan for tags */ for (ret = '' ; txt != '' ; ) { local match; local cur; local tag; /* search for our next special tag sequence */ match = rexSearch(patNextTag, txt); /* check to see if we found a tag */ if (match == nil) { /* no more tags - the rest of the text is plain text */ cur = txt; txt = ''; tag = nil; } else { /* found a tag - get the plain text up to the tag */ cur = txt.substr(1, match[1] - 1); txt = txt.substr(match[1] + match[2]); /* get the tag name */ tag = rexGroup(1)[3]; } /* process the plain text up to the tag, if any */ if (cur != '') { /* check our state */ switch(state_) { case stateReadingCommand: case stateWriteThrough: case stateInCommand: case stateNoCommand: /* we don't need to add anything in these states */ break; case stateBeforeCommand: /* * We're waiting for the first command output, and * we've now found it. Write the command results * prefix separator. */ ret += gLibMessages.commandResultsPrefix; /* we're now inside some command result text */ state_ = stateInCommand; break; case stateBeforeInterruption: /* * An editing session has been interrupted, and we're * showing new output. First, switch to normal * in-command mode - do this before doing anything * else, since we might recursively show some more * text in the course of canceling the input line. */ state_ = stateInCommand; /* * Now tell the input manager that we're canceling * the input line that was under construction. Don't * reset the input editor state, though, since we * might be able to resume editing the same line * later. */ inputManager.cancelInputInProgress(nil); /* insert the command interruption prefix */ ret += gLibMessages.commandInterruptionPrefix; break; case stateBetweenCommands: /* * We've been waiting for a new command to start * after seeing a <.commandsep> tag. We now have * some text for the new command, so show a command * separator. */ ret += gLibMessages.commandResultsSeparator; /* we're now inside some command result text */ state_ = stateInCommand; break; } /* add the plain text */ ret += cur; } /* if we found the tag, process it */ switch(tag) { case 'none': /* switching to no-command mode */ state_ = stateNoCommand; break; case 'mid': /* switching back to mid-command mode */ state_ = stateInCommand; break; case 'sep': /* command separation - check our state */ switch(state_) { case stateReadingCommand: case stateBeforeCommand: case stateBetweenCommands: case stateWriteThrough: /* in these states, <.commandsep> has no effect */ break; case stateInCommand: /* * We're inside some command text. <.commandsep> * tells us that we've reached the end of one * command's output, so any subsequent output text * belongs to a new command and thus must be visually * separated from the preceding text. Don't add any * separation text yet, because we don't know for * sure that there will ever be any more output text; * instead, switch our state to between-commands, so * that any subsequent text will trigger addition of * a separator. */ state_ = stateBetweenCommands; break; } break; case 'int': /* * we've just interrupted reading a command line, due to * an expired timeout event - switch to the * before-interruption state */ state_ = stateBeforeInterruption; break; case 'before': /* we're about to start reading a command */ switch (state_) { case stateBeforeCommand: /* * we've shown nothing since the last command; show * the empty command separator */ writeThrough(gLibMessages.commandResultsEmpty()); break; case stateBetweenCommands: case stateInCommand: /* * we've written at least one command result, so * show the after-command separator */ writeThrough(gLibMessages.commandResultsSuffix()); break; default: /* do nothing in other modes */ break; } /* switch to reading-command mode */ state_ = stateReadingCommand; break; case 'after': /* * We've just finished reading a command. If we're * still in reading-command mode, switch to * before-command-results mode. Don't switch if we're * in another state, since we must have switched to * another state already by a different route, in which * case we can ignore this notification. */ if (state_ == stateReadingCommand) state_ = stateBeforeCommand; break; } } /* return the results */ return ret; } /* our current state - start out in before-command mode */ state_ = stateBeforeCommand ; /* ------------------------------------------------------------------------ */ /* * Log Console output stream. This is a simple wrapper for the system * log console, which allows console-style output to be captured to a * file, with full processing (HTML expansion, word wrapping, etc) but * without displaying anything to the game window. * * This class should always be instantiated with transient instances, * since the underlying system object doesn't participate in save/restore * operations. */ class LogConsole: OutputStream /* * Utility method: create a log file, set up to capture all console * output to the log file, run the given callback function, and then * close the log file and restore the console output. This can be * used as a simple means of creating a file that captures the output * of a command. */ captureToFile(filename, charset, width, func) { local con; /* set up a log console to do the capturing */ con = new LogConsole(filename, charset, width); /* capture to the console and run our command */ outputManager.withOutputStream(con, func); /* done with the console */ con.closeConsole(); } /* create a log console */ construct(filename, charset, width) { /* inherit base class handling */ inherited(); /* create the system log console object */ handle_ = logConsoleCreate(filename, charset, width); /* install the standard output filters */ addOutputFilter(typographicalOutputFilter); addOutputFilter(new transient ParagraphManager()); addOutputFilter(styleTagFilter); addOutputFilter(langMessageBuilder); } /* * Close the console. This closes the underlying system log console, * which closes the operating system file. No further text can be * written to the console after it's closed. */ closeConsole() { /* close our underlying system console */ logConsoleClose(handle_); /* * forget our handle, since it's no longer valid; setting the * handle to nil will make it more obvious what's going on if * someone tries to write more text after we've been closed */ handle_ = nil; } /* low-level stream writer - write to our system log console */ writeFromStream(txt) { logConsoleSay(handle_, txt); } /* our system log console handle */ handle_ = nil ; /* ------------------------------------------------------------------------ */ /* * Output stream window. * * This is an abstract base class for UI widgets that have output * streams, such as Banner Windows and Web UI windows. This base class * essentially handles the interior of the window, and leaves the details * of the window's layout in the broader UI to subclasses. */ class OutputStreamWindow: object /* * Invoke the given callback function, setting the default output * stream to the window's output stream for the duration of the call. * This allows invoking any code that writes to the current default * output stream and displaying the result in the window. */ captureOutput(func) { /* make my output stream the global default */ local oldStr = outputManager.setOutputStream(outputStream_); /* make sure we restore the default output stream on the way out */ try { /* invoke the callback function */ (func)(); } finally { /* restore the original default output stream */ outputManager.setOutputStream(oldStr); } } /* * Make my output stream the default in the output manager. Returns * the previous default output stream; the caller can note the return * value and use it later to restore the original output stream via a * call to outputManager.setOutputStream(), if desired. */ setOutputStream() { /* set my stream as the default */ return outputManager.setOutputStream(outputStream_); } /* * Create our output stream. We'll create the appropriate output * stream subclass and set it up with our default output filters. * Subclasses can override this as needed to customize the filters. */ createOutputStream() { /* create a banner output stream */ outputStream_ = createOutputStreamObj(); /* set up the default filters */ outputStream_.addOutputFilter(typographicalOutputFilter); outputStream_.addOutputFilter(new transient ParagraphManager()); outputStream_.addOutputFilter(styleTagFilter); outputStream_.addOutputFilter(langMessageBuilder); } /* * Create the output stream object. Subclasses can override this to * create the appropriate stream subclass. Note that the stream * should always be created as a transient object. */ createOutputStreamObj() { return new transient OutputStream(); } /* * My output stream - this is a transient OutputStream instance. * Subclasses must create this explicitly by calling * createOutputStream() when the underlying UI window is first * created. */ outputStream_ = nil ; frobtads-1.2.3/tads3/lib/adv3/samples/0000755000175000001440000000000012145614112016566 5ustar realncusersfrobtads-1.2.3/tads3/lib/adv3/samples/starta3.t0000644000175000001440000001013612145513566020350 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 1999, 2002 by Michael J. Roberts. Permission is * granted to anyone to copy and use this file for any purpose. * * This is a starter TADS 3 source file. This is a complete TADS game * that you can compile and run. * * To compile this game in TADS Workbench, open the "Build" menu and * select "Compile for Debugging." To run the game, after compiling it, * open the "Debug" menu and select "Go." * * This is the "advanced" starter game - it has only the minimum set of * definitions needed for a working game. If you would like some more * examples, create a new game, and choose the "introductory" version * when asked for the type of starter game to create. */ /* * Include the main header for the standard TADS 3 adventure library. * Note that this does NOT include the entire source code for the * library; this merely includes some definitions for our use here. The * main library must be "linked" into the finished program by including * the file "adv3.tl" in the list of modules specified when compiling. * In TADS Workbench, simply include adv3.tl in the "Source Files" * section of the project. * * Also include the US English definitions, since this game is written * in English. */ #include #include /* * Our game credits and version information. This object isn't required * by the system, but our GameInfo initialization above needs this for * some of its information. * * You'll have to customize some of the text below, as marked: the name * of your game, your byline, and so on. */ versionInfo: GameID IFID = '$IFID$' name = '$TITLE$' byline = 'by $AUTHOR$' htmlByline = 'by $AUTHOR$' version = '1' authorEmail = '$AUTHOR$ <$EMAIL$>' desc = '$DESC$' htmlDesc = '$HTMLDESC$' ; /* * The "gameMain" object lets us set the initial player character and * control the game's startup procedure. Every game must define this * object. For convenience, we inherit from the library's GameMainDef * class, which defines suitable defaults for most of this object's * required methods and properties. */ gameMain: GameMainDef /* the initial player character is 'me' */ initialPlayerChar = me ; /* * Starting location - we'll use this as the player character's initial * location. The name of the starting location isn't important to the * library, but note that it has to match up with the initial location * for the player character, defined in the "me" object below. * * Our definition defines two strings. The first string, which must be * in single quotes, is the "name" of the room; the name is displayed on * the status line and each time the player enters the room. The second * string, which must be in double quotes, is the "description" of the * room, which is a full description of the room. This is displayed when * the player types "look around," when the player first enters the room, * and any time the player enters the room when playing in VERBOSE mode. * * The name "startRoom" isn't special - you can change this any other * name you'd prefer. The player character's starting location is simply * the location where the "me" actor is initially located. */ startRoom: Room 'Start Room' "This is the starting room. " ; /* * Define the player character. The name of this object is not * important, but it MUST match the name we use to initialize * gameMain.initialPlayerChar above. * * Note that we aren't required to define any vocabulary or description * for this object, because the class Actor, defined in the library, * automatically provides the appropriate definitions for an Actor when * the Actor is serving as the player character. Note also that we don't * have to do anything special in this object definition to make the * Actor the player character; any Actor can serve as the player * character, and we'll establish this one as the PC in main(), below. */ + me: Actor ; frobtads-1.2.3/tads3/lib/adv3/samples/starti3.t0000644000175000001440000004133512145513566020365 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 1999, 2002 by Michael J. Roberts. Permission is * granted to anyone to copy and use this file for any purpose. * * This is a starter TADS 3 source file. This is a complete TADS game * that you can compile and run. * * To compile this game in TADS Workbench, open the "Build" menu and * select "Compile for Debugging." To run the game, after compiling it, * open the "Debug" menu and select "Go." * * Please note that this file contains considerably more than the * minimal set of definitions necessary to create a working game; this * file has numerous examples meant to help you start making progress on * your game more quickly, by giving you a few concrete examples that * you can copy and modify. As you flesh out your game, you should * modify the objects we define here, or simply remove them when you no * longer need them in your game. * * If you want a truly minimal set of definitions, create another new * game in TADS Workbench, and choose the "advanced" version when asked * for the type of starter game to create. */ /* * Include the main header for the standard TADS 3 adventure library. * Note that this does NOT include the entire source code for the * library; this merely includes some definitions for our use here. The * main library must be "linked" into the finished program by including * the file "adv3.tl" in the list of modules specified when compiling. * In TADS Workbench, simply include adv3.tl in the "Source Files" * section of the project. * * Also include the US English definitions, since this game is written * in English. */ #include #include /* * Our game credits and version information. This object isn't required * by the system, but our GameInfo initialization above needs this for * some of its information. * * IMPORTANT - You should customize some of the text below, as marked: * the name of your game, your byline, and so on. */ versionInfo: GameID IFID = '$IFID$' name = '$TITLE$' byline = 'by $AUTHOR$' htmlByline = 'by $AUTHOR$' version = '1' authorEmail = '$AUTHOR$ <$EMAIL$>' desc = '$DESC$' htmlDesc = '$HTMLDESC$' /* * other bibliographic tags you might want to set include: * *. headline = 'An Interactive Sample' *. seriesName = 'The Sample Trilogy' *. seriesNumber = '1' *. genreName = 'Sample Games' *. forgivenessLevel = 'Polite' *. gameUrl = 'http://mysite.com/mygame.htm' *. firstPublished = '2006' *. languageCode = 'en-US' *. licenseType = 'Freeware' *. copyingRules = 'Nominal cost only; compilations allowed' *. presentationProfile = 'Default' */ showCredit() { /* show our credits */ "Put credits for the game here. "; /* * The game credits are displayed first, but the library will * display additional credits for library modules. It's a good * idea to show a blank line after the game credits to separate * them visually from the (usually one-liner) library credits * that follow. */ "\b"; } showAbout() { "Put information for players here. Many authors like to mention any unusual commands here, along with background information on the game (for example, the author might mention that the game was created as an entry for a particular competition). "; } ; /* * Entryway location. * * We use the class "Room" to define the location. Room is a class, * defined in the library, that can be used for most of the locations in * the game. * * Our definition defines two strings. The first string, which must be * in single quotes, is the "name" of the room; the name is displayed on * the status line and each time the player enters the room. The second * string, which must be in double quotes, is the "description" of the * room, which is a full description of the room. This is displayed * when the player types "look around," when the player first enters the * room, and any time the player enters the room when playing in VERBOSE * mode. */ entryway: Room 'Entryway' "This large, formal entryway is slightly intimidating: the walls are lined with somber portraits of gray-haired men from decades past; a medieval suit of armor<> towers over a single straight-backed wooden chair. The front door leads back outside to the south. A hallway leads north. " /* * In the description text above, we embedded the expression * "describeAxe". Whenever the description text is displayed, it * will call evaluate that expression, which will in turn call this * method, where we'll generate some additional text to describe the * axe if it's still part of the suit of armor. */ describeAxe { if (axe.isIn(suitOfArmor)) ", posed with a battle axe at the ready,"; } /* * To the north is the hallway. Set the "north" property to the * destination room object. Other direction properties that we * could set: east, west, north, up, down, plus the diagonals: * northeast, northwest, southeast, southwest. We can also set "in" * and "out", and the shipboard directions port, starboard, fore, * and aft. */ north = hallway /* * To the south is the front door. A travel direction link can * point directly to another room, but it can also point to * something like a door. */ south = frontDoor /* * The "out" direction is the same as south, since going south leads * outside */ out = frontDoor ; /* * Define the front door. The "+" sign in front of the definition means * that the object is located within the most recently defined room, * which in this case is 'entryway' as defined above. * * We start this object definition with two strings, both in single * quotes, and a third in double quotes. The first is the vocabulary * list for the object, which tells us how the player can refer to this * object. The second string is the name, which is how the game refers * to the object in generated messages. The third is the full * description of the object, which is displayed when the player * examines this object. * * The vocabulary list consists of any number of words separated by * spaces. Every word is an adjective except the last, which is a noun. * You can specify more than one noun by listing several nouns separated * by slash characters ("/"). The player can use any of the words * defined here to refer to the object - the player doesn't have to use * all of the words, or use them in the same order that we define them * here, except that adjectives and nouns must be in the grammatically * correct order (in English, this means that adjectives must precede * nouns). */ + frontDoor: Door 'front door' 'front door' "It's a heavy wooden door, currently closed. " /* the door is initially closed */ initiallyOpen = nil /* * Doors can usually be opened, but we don't want to allow this one * to be opened. The library by default allows a door to be opened * and closed at will. To change this, we must override the "direct * object" handler for the Open action on this object. Since we * don't want anything to happen when the player tries to open the * door, we can simply override the action handler and display a * message indicating why we can't open the door. */ dobjFor(Open) { action() { "You'd rather stay in the house for now. "; } } ; /* * Define the chair. We use the Chair class for this. Note that the * default Chair class defines a moveable object; we don't want our chair * going anywhere, so make it use the Immovable class as well. * * We don't need to refer to the chair anywhere, so we don't bother * giving it a name. This saves us a little typing and saves us the * trouble of thinking of a name for the object. */ + Chair, Immovable 'straight-backed wooden chair' 'wooden chair' "It looks like one of those formal chairs that looks elegant but is incredibly uncomfortable to sit on. " ; /* * Define the suit of armor. It can't be moved because it's very heavy, * so make it a Heavy object. Note that we do need to refer to this * object (in the 'entryway' object), so we need to give it an object * name. * * Note that we define both "suit" and "armor" as nouns in our vocabulary * list, because we want to be able to refer to it as "suit of armor"; in * the phrasing "x of y", both x and y are noun phrases. */ + suitOfArmor: Heavy 'medieval plate-mail suit/armor' 'suit of armor' "It's a suit of plate-mail armor that looks suitable for a very tall knight. <> " /* * as we did in entryway's description, we've embedded a call to our * describeAxe method, so that we can add a description of the axe * if appropriate */ describeAxe { if (axe.isIn(self)) "The armor is posed with a huge battle-axe held at the ready. "; } ; /* * The battle axe, initially posed with the suit of armor. We make this * a Thing, because we want it to be something the player can pick up * and manipulate. * * This definition starts with two "+" signs, to indicate that it is * initially inside the last object defined with one "+" sign, which is * the suit of armor. * * Note that we define a bunch of vocabulary words that aren't really * synonyms for "axe," but are for things we describe as parts of the * axe (the blade, the dried blood on the blade). Those parts aren't * worth defining as separate objects, but we can at least recognize * them as vocabulary words that simply refer to the axe itself. */ ++ axe: Thing 'large steel battle dried ax/axe/blade/edge/blood' 'battle axe' "It's a large steel battle axe. A little bit of dried blood on the edge of the blade makes the authenticity of the equipment quite credible. " /* * When we're located in the suit of armor, the suit of armor and * the room containing the suit of armor describe us specially. * This means that we do not want to display our name among the * miscellaneous items listed in the room's description. To prevent * being listed in the ordinary description, indicate that we have a * "special" description any time we're located in the suit of * armor, and then make this special desription show nothing - it's * not necessary to show anything because the room and suit of armor * both already show something special for us. */ useSpecialDesc = (isIn(suitOfArmor)) specialDesc = "" ; /* * Define the portraits. We don't want to define several individual * portraits, because they're not important enough, so define a single * object that refers to the portraits collectively. * * Because the library normally allows the player to abbreviate any word * to its first six or more letters, note that we don't have to provide * separate vocabulary words for "portrait" and "portraits", or for * "picture" and "pictures" - "portrait" is an acceptable abbreviation * for "portraits". */ + Fixture 'somber gray-haired portraits/pictures/men/man' 'portraits' "The men in the portraits look like bankers or businessmen, all serious faces and old-fashioned suits. " /* * this object has a plural name, so we must set the isPlural flag * to let the library know how to use its name in messages */ isPlural = true ; /* * The hallway, north of the entryway. */ hallway: Room 'Hallway' "This broad, dimly-lit corridor runs north and south. " south = entryway north = kitchen ; /* * The kitchen. */ kitchen: Room 'Kitchen' "This is a surprisingly cramped kitchen, equipped with antique accoutrements: the stove is a huge black iron contraption, and in place of a refrigerator is an actual icebox. A hallway lies to the south. " south = hallway ; /* * The stove is a Fixture, since we don't want the player to be able to * move it. It's also an OpenableContainer, because we want the player * to be able to open and close it and put things in it. * * Note that we define 'stove' as both an adjective and as a noun, * because we want the player to be able to refer to it not only as a * "stove" but also as a "stove door". * * Because we're an OpenableContainer, the library will automatically add * to our description text an open/closed indication and a listing of any * contents when we're open. */ + Fixture, OpenableContainer 'huge black iron stove stove/oven/contraption/door' 'stove' "It's a huge black iron cube, with a front door that swings open sideways. " /* it's initially closed */ initiallyOpen = nil ; /* * Put a loaf of bread in the stove. It's edible, so use the library * class Food. */ ++ Food 'fresh golden-brown brown loaf/bread/crust' 'loaf of bread' "It's a fresh loaf with a golden-brown crust. " /* * we want to provide a special message when we eat the bread, so * override the direct object action handler for the Eat action; * inherit the default handling, but also display our special * message, which will automatically override the default message * that the base class produces */ dobjFor(Eat) { action() { /* inherit the default handling */ inherited(); /* show our special description */ "You tear off a piece and eat it; it's delicious. You tear off a little more, then a little more, and before long the whole loaf is gone. "; } } ; /* * The icebox is similar to the stove. */ + Fixture, OpenableContainer 'ice box/icebox' 'icebox' "Before there were refrigerators, people had these: it's just a big insulated box, into which one would put perishables along with enough ice to keep the perishables chilled for a few days. " /* * when looking in the icebox, explicitly point out that it contains * no ice; do this by overriding the LookIn action handler, * inheriting the default handling and adding our own message */ dobjFor(LookIn) { action() { /* show the default description */ inherited(); /* add a note that there's no ice, after a paragraph break */ "<.p>It's been a long time since any ice was in there. "; } } ; /* * Define the player character. The name of this object is not * important, but note that it has to match up with the name we use in * the main() routine to initialize the game, below. * * Note that we aren't required to define any vocabulary or description * for this object, because the class Actor, defined in the library, * automatically provides the appropriate definitions for an Actor when * the Actor is serving as the player character. Note also that we * don't have to do anything special in this object definition to make * the Actor the player character; any Actor can serve as the player * character, and we'll establish this one as the PC in main(), below. */ me: Actor /* the initial location is the entryway */ location = entryway ; /* * The "gameMain" object lets us set the initial player character and * control the game's startup procedure. Every game must define this * object. For convenience, we inherit from the library's GameMainDef * class, which defines suitable defaults for most of this object's * required methods and properties. */ gameMain: GameMainDef /* the initial player character is 'me' */ initialPlayerChar = me /* * Show our introductory message. This is displayed just before the * game starts. Most games will want to show a prologue here, * setting up the situation for the player, and show the title of the * game. */ showIntro() { "Welcome to the TADS 3 Starter Game!\b"; } /* * Show the "goodbye" message. This is displayed on our way out, * after the user quits the game. You don't have to display anything * here, but many games display something here to acknowledge that * the player is ending the session. */ showGoodbye() { "<.p>Thanks for playing!\b"; } ; frobtads-1.2.3/tads3/lib/adv3/tips.t0000644000175000001440000001605111465246012016275 0ustar realncusers#charset "us-ascii" /* * TADS 3 Tips, by Krister Fundin (fundin@yahoo.com). Provides a uniform * way of providing one-time only tips to the player (and especially to * inexperienced players) when certain things happen in the game. */ #include /* ---------------------------------------------------------------------- */ /* * The tip manager keeps track of which tips we have shown. Since we don't * want to unnecessarily show any tips more than once, we store this * information both transiently (in the tipManager) and persistently (in * the tip objects themselves). This should make sure that we at least * cover these two types of cases: * * - The player sees a tip, then restarts, undos or restores to an earlier * position. * - The player sees a tip, saves, then resumes play at some later time. */ transient tipManager: InitObject, PostRestoreObject, PostUndoObject /* * Show pending tips. This is called by a PromptDaemon before each new * round of input. */ showTips() { /* * Go through our vector of pending tips. Use a 'for' loop instead * a 'foreach' loop, in case showing one tip triggers another one. */ for (local i = 1 ; i <= pendingTips.length ; i++) { /* show the description of the current tip */ pendingTips[i].showTipDesc(); } /* clear the vector of pending tips */ pendingTips.setLength(0); } /* update tip information after a restore, restart or undo */ execute() { /* go through all tips */ forEachInstance(Tip, function(tip) { /* * see if this one has been shown, according to its own * persistent memory */ if (tip.shown) { /* * It says that it has been shown. If it's not in our list * of shown tips, then add it. */ if (shownTips.indexOf(tip) == nil) shownTips += tip; } else { /* * It says that it hasn't been shown. If it's in our list * of shown tips, then it must have been shown after all. */ if (shownTips.indexOf(tip) != nil) tip.shown = true; } }); } /* a vector of tips to be displayed before the next prompt */ pendingTips = static new Vector(2) /* * A transient list of shown tips. Note that this must be a list and * not a vector. When updating a list, we actually replace it with a * new list, since lists are immutable. This is a transient change - * it affects only the value of the shownTips property. Updating a * vector, however, modifies the vector itself and leaves the property * with the same reference. A vector itself is always persistent, so * this change would be lost after E.G. a restore. */ shownTips = [] ; /* * The Tip class. Each actual tip should be represented by an instance of * this class. To show the tip, just call tipName.showTip(). If the tip * has already been shown, or if the tips have been turned off completely, * then nothing will be displayed. */ class Tip: object /* * The actual text to display when this tip is shown. We'll wrap it in * <.tip> tags automatically, and also add a paragraph break before * it. */ desc = "" /* show this tip */ showTip() { /* see if we should be shown */ if (!shouldShowTip) return; /* * defer the actual displaying of the tip until just before the * next command prompt */ tipManager.pendingTips.append(self); /* note that we have been shown */ makeShown(); } /* display our tip description, I.E. the actual tip */ showTipDesc() { /* display a pargraph break and an opening <.tip> style tag */ "<.p><.tip>"; /* show our description */ desc(); /* close the <.tip> tag */ "<./tip>"; } /* should we show this tip when asked to? */ shouldShowTip() { /* * We'll show it as long as it hasn't been shown before and we * haven't turned the tips off completely. Certain tips might want * to be displayed even when all tips are turned off, if they * contain information specific to a certain story or an extension * that it uses. If so, then this method could be overridden. */ return (!shown && tipMode.isOn); } /* * Mark this tip as shown. This method can be called by outside code * before the tip has been triggered. If the tip informs the player of * a certain command, for instance, then it would become redundant if * the player has already used that command. */ makeShown() { /* set our shown flag */ shown = true; /* also add us to the transient list of shown tips */ tipManager.shownTips += self; } /* flag: has this tip been shown before? */ shown = nil ; /* * A style tag that we enclose tips with. By default, we just use plain * parentheses, just like for notifications and parser messages, but this * could be overridden if we wanted to display something fancier. */ tipStyleTag: StyleTag 'tip' openText = '(' closeText = ')' ; /* * During pre-init, create a PromptDaemon for displaying tips. We don't * want to display them directly when the showTip() method is called, to * allow tips to be triggered from pretty much anywhere without having to * worry about them showing up in the middle of some text. */ PreinitObject execute() { /* * Give this daemon a higher-than-average eventOrder, in case * another PromptDaemon wants to display a tip. The standard tip * about turning of score notification is triggered this way. */ new PromptDaemon(tipManager, &showTips).eventOrder = 200; } ; /* ---------------------------------------------------------------------- */ /* * Next, we want to allow turning all tips on and off during the game. It * should also be possible to turn the tips off for ALL games that use * them, and thus we define a SettingsItem for this purpose. This means * that the player can turn the tips off and then save this setting as the * default. */ tipMode: BinarySettingsItem /* we show tips by default */ isOn = true /* the ID string to use in the configuration file */ settingID = 'tips.showtips' /* show our description */ settingDesc = (libMessages.tipStatusShort(isOn)) ; /* * Define a system action for turning tip mode on/off. */ DefineSystemAction(TipMode) execSystemAction() { /* set the new tip mode */ tipMode.isOn = stat_; /* acknowledge the change */ libMessages.acknowledgeTipStatus(stat_); } ; frobtads-1.2.3/tads3/lib/adv3/thing.t0000644000175000001440000135251112145504453016436 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 by Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - Thing * * This module defines Thing, the base class for physical objects in the * simulation. We also define some utility classes that Thing uses * internally. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * this property is defined in the 'exits' module, but declare it here * in case we're not including the 'exits' module */ property lookAroundShowExits; /* ------------------------------------------------------------------------ */ /* * Sense Information entry. Thing.senseInfoTable() returns a list of * these objects to provide full sensory detail on the objects within * range of a sense. */ class SenseInfo: object construct(obj, trans, obstructor, ambient) { /* set the object being described */ self.obj = obj; /* remember the transparency and obstructor */ self.trans = trans; self.obstructor = obstructor; /* * set the energy level, as seen from the point of view - adjust * the level by the transparency */ self.ambient = (ambient != nil ? adjustBrightness(ambient, trans) : nil); } /* the object being sensed */ obj = nil /* the transparency from the point of view to this object */ trans = nil /* the obstructor that introduces a non-transparent value of trans */ obstructor = nil /* the ambient sense energy level at this object */ ambient = nil /* * compare this SenseInfo object's transparency to the other one; * returns a number greater than zero if 'self' is more transparent, * zero if they're equally transparent, or a negative number if * 'self' is less transparent */ compareTransTo(other) { return transparencyCompare(trans, other.trans); } /* * Return the more transparent of two SenseInfo objects. Either * argument can be nil, in which case we'll return the non-nil one; * if both are nil, we'll return nil. If they're equal, we'll return * the first one. */ selectMoreTrans(a, b) { /* if one or the other is nil, return the non-nil one */ if (a == nil) return b; else if (b == nil) return a; else if (a.compareTransTo(b) >= 0) return a; else return b; } ; /* * Can-Touch information. This object keeps track of whether or not a * given object is able to reach out and touch another object. */ class CanTouchInfo: object /* construct, given the touch path */ construct(path) { touchPath = path; } /* the full reach-and-touch path from the source to the target */ touchPath = nil /* * if we have calculated whether or not the source can touch the * target, we'll set the property canTouch to nil or true * accordingly; if this property is left undefined, this information * has never been calculated */ // canTouch = nil ; /* * Given a sense information table (a LookupTable returned from * Thing.senseInfoTable()), return a vector of only those objects in the * table that match the given criteria. * * 'func' is a function that takes two arguments, func(obj, info), where * 'obj' is a simulation object and 'info' is the corresponding * SenseInfo object. This function is invoked for each object in the * sense info table; if 'func' returns true, then 'obj' is part of the * list that we return. * * The return value is a simple vector of game objects. (Note that * SenseInfo objects are not returned - just the simulation objects.) */ senseInfoTableSubset(senseTab, func) { local vec; /* set up a vector for the return list */ vec = new Vector(32); /* scan the table for objects matching criteria given by 'func' */ senseTab.forEachAssoc(function(obj, info) { /* if the function accepts this object, include it in the vector */ if ((func)(obj, info)) vec.append(obj); }); /* return the result vector */ return vec; } /* * Sense calculation scratch-pad globals. Many of the sense * calculations involve recursive descents of portions of the * containment tree. In the course of these calculations, it's * sometimes useful to have information about the entire operation in * one of the recursive calls. We could pass the information around as * extra parameters, but that adds overhead, and performance is critical * in the sense routines (because they tend to get invoked a *lot*). To * reduce the overhead, particularly for information that's not needed * very often, we stuff some information into this global object rather * than passing it around through parameters. * * Note that this object is transient because this information is useful * only during the course of a single tree traversal, and so doesn't * need to be saved or undone. */ transient senseTmp: object /* * The point of view of the sense calculation. This is the starting * point of a sense traversal; it's the object that's viewing the * other objects. */ pointOfView = nil /* post-calculation notification list */ notifyList = static new Vector(16) ; /* ------------------------------------------------------------------------ */ /* * Command check status object. This is an abstract object that we use * in to report results from a check of various kinds. * * The purpose of this object is to consolidate the code for certain * kinds of command checks into a single routine that can be used for * different purposes - verification, selection from multiple * possibilities (such as multiple paths), and command action * processing. This object encapsulates a status - success or failure - * and, when the status is failure, a message giving the reason for the * failure. */ class CheckStatus: object /* did the check succeed or fail? */ isSuccess = nil /* * the message property or string, and parameters, for failure - * this is for use with reportFailure or the like */ msgProp = nil msgParams = [] ; /* * Success status object. Note that this is a single object, not a * class - there's no distinct information per success indicator, so we * only need this single success indicator for all uses. */ checkStatusSuccess: CheckStatus isSuccess = true ; /* * Failure status object. Unlike the success indicator, this is a * class, because we need to keep track of the separate failure message * for each kind of failure. */ class CheckStatusFailure: CheckStatus construct(prop, [params]) { isSuccess = nil; msgProp = prop; msgParams = params; } ; /* ------------------------------------------------------------------------ */ /* * Equivalent group state information. This keeps track of a state and * the number of items in that state when we're listing a group of * equivalent items in different states. */ class EquivalentStateInfo: object construct(st, obj, nameProp) { /* remember the state object and the name property to display */ stateObj = st; stateNameProp = nameProp; /* this is the first one in this state */ stateVec = new Vector(8); stateVec.append(obj); } /* add an object to the list of equivalent objects in this state */ addEquivObj(obj) { stateVec.append(obj); } /* get the number of equivalent items in the same state */ getEquivCount() { return stateVec.length(); } /* get the list of equivalent items in the same state */ getEquivList() { return stateVec; } /* get the name to use for listing purposes */ getName() { return stateObj.(stateNameProp)(stateVec); } /* the ThingState object describing the state */ stateObj = nil /* the property to evaluate to get the name for listing purposes */ stateNameProp = nil /* list of items in this same state */ stateVec = nil ; /* ------------------------------------------------------------------------ */ /* * Drop Descriptor. This is passed to the receiveDrop() method of a * "drop destination" when an object is discarded via commands such as * DROP or THROW. The purpose of the descriptor is to identify the type * of command being performed, so that the receiveDrop() method can * generate an appropriate report message. */ class DropType: object /* * Generate the standard report message for the action. The drop * destination's receiveDrop() method can call this if the standard * message is adequate to describe the result of the action. * * 'obj' is the object being dropped, and 'dest' is the drop * destination. */ // standardReport(obj, dest) { /* subclasses must override */ } /* * Get a short report describing the action without saying where the * object ended up. This is roughly the same as the standard report, * but omits any information on where the object lands, so that the * caller can show a separate message explaining that part. * * The report must be worded such that the object being dropped is * the logical antecedent for any subsequent text. This means that * callers can use a pronoun to refer back to the object dropped, * allowing for more natural sequences to be constructed. (It * usually sounds stilted to repeat the full name: "You drop the box. * The box falls into the chasm." It's better if we can use a * pronoun in the second sentence: "You drop the box. It falls into * the chasm.") * * 'obj' is the object being dropped, and 'dest' is the drop * destination. */ // getReportPrefix(obj, dest) { return ''; } ; /* * A drop-type descriptor for the DROP command. Since we have no need to * include any varying parameters in this object, we simply provide this * singleton instance. */ dropTypeDrop: DropType standardReport(obj, dest) { /* show the default "Dropped" response */ defaultReport(&okayDropMsg); } getReportPrefix(obj, dest) { /* return the standard "You drop the " message */ return gActor.getActionMessageObj().droppingObjMsg(obj); } ; /* * A drop-type descriptor for the THROW command. This object keeps track * of the target (the object that was hit by the projectile) and the * projectile's path to the target. The projectile is simply the direct * object. */ class DropTypeThrow: DropType construct(target, path) { /* remember the target and path */ target_ = target; path_ = path; } standardReport(obj, dest) { local nominalDest; /* get the nominal drop destination */ nominalDest = dest.getNominalDropDestination(); /* * if the actual target and the nominal destination are the same, * just say that it lands on the destination; otherwise, say that * it bounces off the target and falls to the nominal destination */ if (target_ == nominalDest) mainReport(&throwFallMsg, obj, target_); else mainReport(&throwHitFallMsg, obj, target_, nominalDest); } getReportPrefix(obj, dest) { /* return the standard "The hits the " message */ return gActor.getActionMessageObj().throwHitMsg(obj, target_); } /* the object that was hit by the projectile */ target_ = nil /* the path the projectile took to reach the target */ path_ = nil ; /* ------------------------------------------------------------------------ */ /* * Bag Affinity Info - this class keeps track of the affinity of a bag * of holding for an object it might contain. We use this class in * building bag affinity lists. */ class BagAffinityInfo: object construct(obj, bulk, aff, bag) { /* save our parameters */ obj_ = obj; bulk_ = bulk; aff_ = aff; bag_ = bag; } /* * Compare this item to another item, for affinity ranking purposes. * Returns positive if I should rank higher than the other item, * zero if we have equal ranking, negative if I rank lower than the * other item. */ compareAffinityTo(other) { /* * if this object is the indirect object of 'take from', treat * it as having the lowest ranking */ if (gActionIs(TakeFrom) && gIobj == obj_) return -1; /* if we have different affinities, sort according to affinity */ if (aff_ != other.aff_) return aff_ - other.aff_; /* we have the same affinity, so put the higher bulk item first */ if (bulk_ != other.bulk_) return bulk_ - other.bulk_; /* * We have the same affinity and same bulk; rank according to * how recently the items were picked up. Put away the oldest * items first, so the lower holding (older) index has the * higher ranking. (Note that because lower holding index is * the higher ranking, we return the negative of the holding * index comparison.) */ return other.obj_.holdingIndex - obj_.holdingIndex; } /* * given a vector of affinities, remove the most recent item (as * indicated by holdingIndex) and return the BagAffinityInfo object */ removeMostRecent(vec) { local best = vec[1]; /* find the most recent item */ foreach (local cur in vec) { /* if this is better than the best so far, remember it */ if (cur.obj_.holdingIndex > best.obj_.holdingIndex) best = cur; } /* remove the best item from the vector */ vec.removeElement(best); /* return the best item */ return best; } /* the object the bag wants to contain */ obj_ = nil /* the object's bulk */ bulk_ = nil /* the bag that wants to contain the object */ bag_ = nil /* affinity of the bag for the object */ aff_ = nil ; /* ------------------------------------------------------------------------ */ /* * "State" of a Thing. This is an object abstractly describing the * state of an object that can assume different states. * * The 'listName', 'inventoryName', and 'wornName' give the names of * state as displayed in room/contents listings, inventory listings, and * listings of items being worn by an actor. This state name is * displayed along with the item name (usually parenthetically after the * item name, but the exact nature of the display is controlled by the * language-specific part of the library). * * The 'listingOrder' is an integer giving the listing order of this * state relative to other states of the same kind of object. When we * show a list of equivalent items in different states, we'll order the * state names in ascending order of listingOrder. */ class ThingState: object /* * The name of the state to use in ordinary room/object contents * listings. If the name is nil, no extra state information is shown * in a listing for an object in this state. (It's often desirable * to leave the most ordinary state an object can be in unnamed, to * avoid belaboring the obvious. For example, a match that isn't * burning would probably not want to mention "(not lit)" every time * it's listed.) * * 'lst' is a list of the objects being listed in this state. If * we're only listing a single object, this will be a list with one * element giving the object being listed. If we're listing a * counted set of equivalent items all in this same state, this will * be the list of items. Everything in 'lst' will be equivalent (in * the isEquivalent sense). */ listName(lst) { return nil; } /* * The state name to use in inventory lists. By default, we just use * the base name. 'lst' has the same meaning as in listName(). */ inventoryName(lst) { return listName(lst); } /* * The state name to use in listings of items being worn. By * default, we just use the base name. 'lst' has the same meaning as * in listName(). */ wornName(lst) { return listName(lst); } /* the relative listing order */ listingOrder = 0 /* * Match the name of an object in this state. 'obj' is the object * to be matched; 'origTokens' and 'adjustedTokens' have the same * meanings they do for Thing.matchName; and 'states' is a list of * all of the possible states the object can assume. * * Implementation of this is always language-specific. In most * cases, this should do something along the lines of checking for * the presence (in the token list) of words that only apply to * other states, rejecting the match if any such words are found. * For example, the ThingState object representing the unlit state * of a light source might check for the presence of 'lit' as an * adjective, and reject the object if it's found. */ matchName(obj, origTokens, adjustedTokens, states) { /* by default, simply match the object */ return obj; } ; /* ------------------------------------------------------------------------ */ /* * Object with vocabulary. This is the base class for any object that * can define vocabulary words. */ class VocabObject: object /* * Match a name as used in a noun phrase in a player's command to * this object. The parser calls this routine to test this object * for a match to a noun phrase when all of the following conditions * are true: * * - this object is in scope; * * - our vocabulary matches the noun phrase, which means that ALL of * the words in the player's noun phrase are associated with this * object with the corresponding parts of speech. Note the special * wildcard vocabulary words: '#' as an adjective matches any number * used as an adjective; '*' as a noun matches any word used as any * part of speech. * * 'origTokens' is the list of the original input words making up * the noun phrase, in canonical tokenizer format. Each element of * this list is a sublist representing one token. * * 'adjustedTokens' is the "adjusted" token list, which provides * more information on how the parser is analyzing the phrase but * may not contain the exact original tokens of the command. In the * adjusted list, the tokens are represented by pairs of values in * the list: the first value of each pair is a string giving the * adjusted token text, and the second value of the pair is a * property ID giving the part of speech of the parser's * interpretation of the phrase. For example, if the noun phrase is * "red book", the list might look like ['red', &adjective, 'book', * &noun]. * * The adjusted token list in some cases contains different tokens * than the original input. For example, when the command contains * a spelled-out number, the parser will translate the spelled-out * number to a numeral format and provide the numeral string in the * adjusted token list: 'a hundred and thirty-four' will become * '134' in the adjusted token list. * * If this object does not match the noun phrase, this routine * returns nil. If the object is a match, the routine returns * 'self'. The routine can also return a different object, or even * a list of objects - in this case, the parser will consider the * noun phrase to have matched the returned object or objects rather * than this original match. * * Note that it isn't necessary to check that the input tokens match * our defined vocabulary words, because the parser will already * have done that for us. This routine is only called after the * parser has already determined that all of the noun phrase's words * match ours. * * By default, we do two things. First, we check to see if ALL of * our tokens are "weak" tokens, and if they are, we indicate that * we do NOT match the phrase. Second, if we pass the "weak token" * test, we'll invoke the common handling in matchNameCommon(), and * return the result. * * In most cases, games will want to override matchNameCommon() * instead of this routine. matchNameCommon() is the common handler * for both normal and disambiguation-reply matching, so overriding * that one routine will take care of both kinds of matching. Games * will only need to override matchName() separately in cases where * they need to differentiate normal matching and disambiguation * matching. */ matchName(origTokens, adjustedTokens) { /* if we have a weak-token list, check the tokens */ weakTest: if (weakTokens != nil) { local sc = languageGlobals.dictComparator; /* check to see if all of our tokens are "weak" */ for (local i = 1, local len = adjustedTokens.length() ; i <= len ; i += 2) { /* get the current token and its type */ local tok = adjustedTokens[i]; local typ = adjustedTokens[i+1]; /* * if this is a miscWord token, skip it - these aren't * vocabulary matches, so we won't find them in either * our weak or strong vocabulary lists */ if (typ == &miscWord) continue; /* if this token isn't in our weak list, it's not weak */ if (weakTokens.indexWhich({x: sc.matchValues(tok, x) != 0}) == nil) { /* * It's not in the weak list, so this isn't a weak * token; therefore the phrase isn't weak, so we do * not wish to veto the match. There's no need to * keep looking; simply break out of the weak test * entirely, since we've now passed. */ break weakTest; } } /* * If we get here, it means we got through the loop without * finding any non-weak tokens. This means the entire * phrase is weak, which means that we don't match it. * Return nil to indicate that this is not a match. */ return nil; } /* invoke the common handling */ return matchNameCommon(origTokens, adjustedTokens); } /* * Match a name in a disambiguation response. This is similar to * matchName(), but is called for each object in an ambiguous object * list for which a disambiguation response was provided. As with * matchName(), we only call this routine for objects that match the * dictionary vocabulary. * * This routine is separate from matchName() because a * disambiguation response usually only contains a partial name. * For example, the exchange might go something like this: * * >take box *. Which box do you mean, the cardboard box, or the wood box? * >cardboard * * Note that it is not safe to assume that the disambiguation * response can be prepended to the original noun phrase to make a * complete noun phrase; if this were safe, we'd simply concatenate * the two strings and call matchName(). This would work for the * example above, since we'd get "cardboard box" as the new noun * phrase, but it wouldn't work in general. Consider these examples: * * >open post office box *. Which post office box do you mean, box 100, box 101, or box 102? * * >take jewel *. Which jewel do you mean, the emerald, or the diamond? * * There's no general way of assembling the disambiguation response * and the original noun phrase together into a new noun phrase, so * rather than trying to use matchName() for both purposes, we * simply use a separate routine to match the disambiguation name. * * Note that, when this routine is called, this object will have * been previously matched with matchName(), so there is no question * that this object matches the original noun phrase. The only * question is whether or not this object matches the response to * the "which one do you mean" question. * * The return value has the same meaning as for matchName(). * * By default, we simply invoke the common handler. Note that games * will usually want to override matchNameCommon() instead of this * routine, since matchNameCommon() provides common handling for the * main match and disambiguation match cases. Games should only * override this routine when they need to do something different * for normal vs disambiguation matching. */ matchNameDisambig(origTokens, adjustedTokens) { /* by default, use the same common processing */ return matchNameCommon(origTokens, adjustedTokens); } /* * Common handling for the main matchName() and the disambiguation * handler matchNameDisambig(). By default, we'll check with our * state object if we have a state object; if not, we'll simply * return 'self' to indicate that we do indeed match the given * tokens. * * In most cases, when a game wishes to customize name matching for * an object, it can simply override this routine. This routine * provides common handling for matchName() and matchNameDisambig(), * so overriding this routine will take care of both the normal and * disambiguation matching cases. In cases where a game needs to * customize only normal matching or only disambiguation matching, * it can override one of those other routines instead. */ matchNameCommon(origTokens, adjustedTokens) { local st; /* * if we have a state, ask our state object to check for words * applying only to other states */ if ((st = getState()) != nil) return st.matchName(self, origTokens, adjustedTokens, allStates); /* by default, accept the parser's determination that we match */ return self; } /* * Plural resolution order. When a command contains a plural noun * phrase, we'll sort the items that match the plural phrase in * ascending order of this property value. * * In most cases, the plural resolution order doesn't matter. Once * in a while, though, a set of objects will be named as "first," * "second," "third," and so on; in these cases, it's nice to have * the order of resolution match the nominal ordering. * * Note that the sorting order only applies within the matches for a * particular plural phrase, not globally throughout the entire list * of objects in a command. For example, if the player types TAKE * BOXES AND BOOKS, we'll sort the boxes in one group, and then we'll * sort the books as a separate group - but all of the boxes will * come before any of the books, regardless of the plural orders. * * >take books and boxes *. first book: Taken. *. second book: Taken. *. third book: Taken. *. left box: Taken. *. middle box: Taken. *. right box: Taken. */ pluralOrder = 100 /* * Disambiguation prompt order. When we interactively prompt for * help resolving an ambiguous noun phrase, we'll put the list of * ambiguous matches in ascending order of this property value. * * In most cases, the prompt order doesn't matter, so most objects * won't have to override the default setting. Sometimes, though, a * set of objects will be identified in the game as "first", * "second", "third", etc., and in these cases it's desirable to have * the objects presented in the same order as the names indicate: * * Which door do you mean, the first door, the second door, or the * third door? * * By default, we use the same value as our pluralOrder, since the * plural order has essentially the same purpose. */ disambigPromptOrder = (pluralOrder) /* * Intrinsic parsing likelihood. During disambiguation, if the * parser finds two objects with equivalent logicalness (as * determined by the 'verify' process for the particular Action being * performed), it will pick the one with the higher intrinsic * likelihood value. The default value is zero, which makes all * objects equivalent by default. Set a higher value to make the * parser prefer this object in cases of ambiguity. */ vocabLikelihood = 0 /* * Filter an ambiguous noun phrase resolution list. The parser calls * this method for each object that matches an ambiguous noun phrase * or an ALL phrase, to allow the object to modify the resolution * list. This method allows the object to act globally on the entire * list, so that the filtering can be sensitive to the presence or * absence in the list of other objects, and can affect the presence * of other objects. * * 'lst' is a list of ResolveInfo objects describing the tentative * resolution of the noun phrase. 'action' is the Action object * representing the command. 'whichObj' is the object role * identifier of the object being resolved (DirectObject, * IndirectObject, etc). 'np' is the noun phrase production that * we're resolving; this is usually a subclass of NounPhraseProd. * 'requiredNum' is the number of objects required, when an exact * quantity is specified; this is nil in cases where the quantity is * unspecified, as in 'all' or an unquantified plural ("take coins"). * * The result is a new list of ResolveInfo objects, which need not * contain any of the objects of the original list, and can add new * objects not in the original list, as desired. * * By default, we simply return the original list. */ filterResolveList(lst, action, whichObj, np, requiredNum) { /* return the original list unchanged */ return lst; } /* * Expand a pronoun list. This is essentially complementary to * filterResolveList: the function is to "unfilter" a pronoun binding * that contains this object so that it restores any objects that * would have been filtered out by filterResolveList from the * original noun phrase binding. * * This routine is called whenever the parser is called upon to * resolve a pronoun ("TAKE THEM"). This routine is called for each * object in the "raw" pronoun binding, which is simply the list of * objects that was stored by the previous command as the antecedent * for the pronoun. After this routine has been called for each * object in the raw pronoun binding, the final list will be passed * through filterResolveList(). * * 'lst' is the raw pronoun binding so far, which might reflect * changes made by this method called on previous objects in the * list. 'typ' is the pronoun type (PronounIt, PronounThem, etc) * describing the pronoun phrase being resolved. The return value is * the new pronoun binding list; if this routine doesn't need to make * any changes, it should simply return 'lst'. * * In some cases, filterResolveList chooses which of two or more * possible ways to bind a noun phrase, with the binding dependent * upon other conditions, such as the current action. In these * cases, it's often desirable for a subsequent pronoun reference to * make the same decision again, choosing from the full set of * possible bindings. This routine facilitates that by letting the * object put back objects that were filtered out, so that the * filtering can once again run on the full set of possible bindings * for the pronoun reference. * * This base implementation just returns the original list unchanged. * See CollectiveGroup for an override that uses this. */ expandPronounList(typ, lst) { return lst; } /* * Our list of "weak" tokens. This is a token that is acceptable in * our vocabulary, but which we can only use in combination with one * or more "strong" tokens. (A token is strong if it's not weak, so * we need only keep track of one or the other kind. Weak tokens * are much less common than strong tokens, so it takes a lot less * space if we store the weak ones instead of the strong ones.) * * The purpose of weak tokens is to allow players to use more words * to refer to some objects without creating ambiguity. For * example, if we have a house, and a front door of the house, we * might want to allow the player to call the front door "front door * of house." If we just defined the door's vocabulary thus, * though, we'd create ambiguity if the player tried to refer to * "house," even though this obviously doesn't create any ambiguity * to a human reader. Weak tokens fix the problem: we define * "house" as a weak token for the front door, which allows the * player to refer to the front door as "front door of house", but * prevents the front door from matching just "house". * * By default, this is nil to indicate that we don't have any weak * tokens to check. If the object has weak tokens, this should be * set to a list of strings giving the weak tokens. */ weakTokens = nil /* * By default, every object can be used as the resolution of a * possessive qualifier phrase (e.g., "bob" in "bob's book"). If * this property is set to nil for an object, that object can never * be used as a possessive. Note that has nothing to do with * establishing ownership relationships between objects; it controls * only the resolution of possessive phrases during parsing to * concrete game objects. */ canResolvePossessive = true /* * Throw an appropriate parser error when this object is used in a * player command as a possessive qualifier (such as when 'self' is * the "bob" in "take bob's key"), and we don't own anything matching * the object name that we qualify. This is only called when 'self' * is in scope. By default, we throw the standard parser error ("Bob * doesn't appear to have any such thing"). 'txt' is the * lower-cased, HTMLified text that of the qualified object name * ("key" in "bob's key"). */ throwNoMatchForPossessive(txt) { throw new ParseFailureException(&noMatchForPossessive, self, txt); } /* * Throw an appropriate parser error when this object is used in a * player command to locationally qualify another object (such as * when we're the box in "examine the key in the box"), and there's * no object among our contents with the given name. By default, we * throw the standard parser error ("You see no key in the box"). */ throwNoMatchForLocation(txt) { throw new ParseFailureException(&noMatchForLocation, self, txt); } /* * Throw an appropriate parser error when this object is used in a * player command to locationally qualify "all" (such as when we're * the box in "examine everything in the box"), and we have no * contents. By default, we throw the standard parser error ("You * see nothing unusual in the box"). */ throwNothingInLocation() { throw new ParseFailureException(¬hingInLocation, self); } /* * Get a list of the other "facets" of this object. A facet is * another program object that to the player looks like the same or * part of the same physical object. For example, it's often * convenient to represent a door using two game objects - one for * each side - but the two really represent the same door from the * player's perspective. * * The parser uses an object's facets to resolve a pronoun when the * original antecedent goes out of scope. In our door example, if * we refer to the door, then walk through it to the other side, * then refer to 'it', the parser will realize from the facet * relationship that 'it' now refers to the other side of the door. */ getFacets() { return []; } /* * Ownership: a vocab-object can be marked as owned by a given Thing. * This allows command input to refer to the owned object using * possessive syntax (such as, in English, "x's y"). * * This method returns true if 'self' is owned by 'obj'. The parser * generally tests for ownership in this direction, as opposed to * asking for obj's owner, because a given object might have multiple * owners, and might not be able to enumerate them all (or, at least, * might not be able to enumerate them efficiently). It's usually * efficient to determine whether a given object qualifies as an * owner, and from the parser's persepctive that's the question * anyway, since it wants to determine if the "x" in "x's y" * qualifies as my owner. * * By default, we simply return true if 'obj' matches our 'owner' * property (and is not nil). */ isOwnedBy(obj) { return owner != nil && owner == obj; } /* * Get our nominal owner. This is the owner that we report for this * object if we're asked to distinguish this object from another * object in a disambiguation prompt. The nominal owner isn't * necessarily the only owner. Note that if getNominalOwner() * returns a non-nil value, isOwnedBy(getNominalOwner()) should * always return true. * * By default, we'll simply return our 'owner' property. */ getNominalOwner() { return owner; } /* our explicit owner, if any */ owner = nil ; /* ------------------------------------------------------------------------ */ /* * Find the best facet from the given list of facets, from the * perspective of the given actor. We'll find the facet that has the * best visibility, or, visibilities being equal, the best touchability. */ findBestFacet(actor, lst) { local infoList; local best; /* * if the list has no elements, there's no result; if it has only one * element, it's obviously the best one */ if (lst.length() == 0) return nil; if (lst.length() == 1) return lst[1]; /* make a list of the visibilities of the given facets */ infoList = lst.mapAll({x: new SightTouchInfo(actor, x)}); /* scan the list and find the entry with the best results */ best = nil; foreach (local cur in infoList) best = SightTouchInfo.selectBetter(best, cur); /* return the best result */ return best.obj_; } /* * A small data structure class recording SenseInfo objects for sight and * touch. We use this to pick the best facet from a list of facets. */ class SightTouchInfo: object construct(actor, obj) { /* remember our object */ obj_ = obj; /* get the visual SenseInfo in the actor's best sight-like sense */ visInfo = actor.bestVisualInfo(obj); /* get the 'touch' SenseInfo for the object */ touchInfo = actor.senseObj(touch, obj); } /* the object we're associated with */ obj_ = nil /* our SenseInfo objects for sight and touch */ visInfo = nil touchInfo = nil /* * Class method: select the "better" of two SightTouchInfo's. * Returns the one with the more transparent visual status, or, * visual transparencies being equal, the one with the more * transparent touch status. */ selectBetter(a, b) { local d; /* if one or the other is nil, return the non-nil one */ if (a == nil) return b; if (b == nil) return a; /* if the visual transparencies differ, compare visually */ d = a.visInfo.compareTransTo(b.visInfo); if (d > 0) return a; else if (d < 0) return b; else { /* the visual status it the same, so compare by touch */ d = a.touchInfo.compareTransTo(b.touchInfo); if (d >= 0) return a; else return b; } } ; /* ------------------------------------------------------------------------ */ /* * Thing: the basic class for game objects. An object of this class * represents a physical object in the simulation. */ class Thing: VocabObject /* * on constructing a new Thing, initialize it as we would a * statically instantiated Thing */ construct() { /* * If we haven't already been through here for this object, do * the static initialization. (Because many library classes and * game objects inherit from multiple Thing subclasses, we'll * sometimes inherit this constructor multiple times in the * course of creating a single new object. The set-up work we do * isn't meant to be repeated and can create internal * inconsistencies if it is.) */ if (!isThingConstructed) { /* inherit base class construction */ inherited(); /* initialize the Thing properties */ initializeThing(); /* note that we've been through this constructor now */ isThingConstructed = true; } } /* * Have we been through Thing.construct() yet for this object? Note * that this will only be set for dynamically created instances * (i.e., objects created with 'new'). */ isThingConstructed = nil /* * My global parameter name. This is a name that can be used in * {xxx} parameters in messages to refer directly to this object. * This is nil by default, which means we have no global parameter * name. Define this to a single-quoted string to set a global * parameter name. * * Global parameter names can be especially useful for objects whose * names change in the course of the game, such as actors who are * known by one name until introduced, after which they're known by * another name. It's a little nicer to write "{He/the bob}" than * "<>". We can do this with a global parameter name, * because it allows us to use {bob} as a message parameter, even * when the actor isn't involved directly in any command. * * Note that these parameter names are global, so no two objects are * allowed to have the same name. These names are also subordinate * to the parameter names in the current Action, so names that the * actions define, such as 'dobj' and 'actor', should usually be * avoided. */ globalParamName = nil /* * Set the global parameter name dynamically. If you need to add a * new global parameter name at run-time, call this rather than * setting the property directly, to ensure that the name is added to * the message builder's name table. You can also use this to delete * an object's global parameter name, by passing nil for the new * name. * * (You only need to use this method if you want to add or change a * name dynamically at run-time, because the library automatically * initializes the table for objects with globalParamName settings * defined at compile-time.) */ setGlobalParamName(name) { /* if we already had a name in the table, remove our old name */ if (globalParamName != nil && langMessageBuilder.nameTable_[globalParamName] == self) langMessageBuilder.nameTable_.removeElement(globalParamName); /* remember our name locally */ globalParamName = name; /* add the name to the message builder's table */ if (name != nil) langMessageBuilder.nameTable_[name] = self; } /* * "Special" description. This is the generic place to put a * description of the object that should appear in the containing * room's full description. If the object defines a special * description, the object is NOT listed in the basic contents list * of the room, because listing it with the contents would be * redundant with the special description. * * By default, we have no special description. If a special * description is desired, define this to a double-quoted string * containing the special description, or to a method that displays * the special description. */ specialDesc = nil /* * The special descriptions to use under obscured and distant * viewing conditions. By default, these simply display the normal * special description, but these can be overridden if desired to * show different messages under these viewing conditions. * * Note that if you override these alternative special descriptions, * you MUST also provide a base specialDesc. The library figures * out whether or not to show any sort of specialDesc first, based * on the presence of a non-nil specialDesc; only then does the * library figure out which particular variation to use. * * Note that remoteSpecialDesc takes precedence over these methods. * That is, when 'self' is in a separate top-level room from the * point-of-view actor, we'll use remoteSpecialDesc to generate our * description, even if the sense path to 'self' is distant or * obscured. */ distantSpecialDesc = (specialDesc) obscuredSpecialDesc = (specialDesc) /* * The "remote" special description. This is the special * description that will be used when this object is not in the * point-of-view actor's current top-level room, but is visible in a * connected room. For example, if two top-level rooms are * connected by a window, so that an actor in one room can see the * objects in the other room, this method will be used to generate * the description of the object when the actor is in the other * room, viewing this object through the window. * * By default, we just use the special description. It's usually * better to customize this to describe the object from the given * point of view. 'actor' is the point-of-view actor. */ remoteSpecialDesc(actor) { distantSpecialDesc; } /* * List order for the special description. Whenever there's more * than one object showing a specialDesc at the same time (in a * single room description, for example), we'll use this to order the * specialDesc displays. We'll display in ascending order of this * value. By default, we use the same value for everything, so * listing order is arbitrary; when one specialDesc should appear * before or after another, this property can be used to control the * relative ordering. */ specialDescOrder = 100 /* * Special description phase. We list special descriptions for a * room's full description in two phases: one phase before we show * the room's portable contents list, and another phase after we show * the contents list. This property controls the phase in which we * show this item's special description. This only affects special * descriptions that are shown with room descriptions; in other * cases, such as "examine" descriptions of objects, all of the * special descriptions are usually shown together. * * By default, we show our special description (if any) before the * room's contents listing, because most special descriptions act * like extensions of the room's main description and thus should be * grouped directly with the room's descriptive text. Objects with * special descriptions that are meant to indicate more ephemeral * properties of the location can override this to be listed after * the room's portable contents. * * One situation where you usually will want to list a special * description after contents is when the special description applies * to an item that's contained in a portable item. Since the * container will be listed with the room contents, as it's portable, * we'll usually want the special description of this child item to * show up after the contents listing, so that it shows up after its * container is mentioned. * * Note that the specialDescOrder is secondary to this phase * grouping, because we essentially list special items in two * separate groups. */ specialDescBeforeContents = true /* * Show my special description, given a SenseInfo object for the * visual sense path from the point of view of the description. */ showSpecialDescWithInfo(info, pov) { local povActor = getPOVActorDefault(gActor); /* * Determine what to show, based on the point-of-view location * and the sense path. If the point of view isn't in the same * top-level location as 'self', OR the actor isn't the same as * the POV, use the remote special description; otherwise, select * the obscured, distant, or basic special description according * to the transparency of the sense path. */ if (getOutermostRoom() != povActor.getOutermostRoom() || pov != povActor) { /* * different top-level rooms, or different actor and POV - * use the remote description */ showRemoteSpecialDesc(povActor); } else if (info.trans == obscured) { /* we're obscured, so show our obscured special description */ showObscuredSpecialDesc(); } else if (info.trans == distant) { /* we're at a distance, so use our distant special description */ showDistantSpecialDesc(); } else if (canDetailsBeSensed(sight, info, pov)) { /* * we're not obscured or distant, and our details can be * sensed, so show our fully-visible special description */ showSpecialDesc(); } } /* * Show the special description, if we have one. If we are using * our initial description, we'll show that; otherwise, if we have a * specialDesc property, we'll show that. * * Note that the initial description overrides the specialDesc * property whenever useInitSpecialDesc() returns true. This allows * an object to have both an initial description that is used until * the object is moved, and a separate special description used * thereafter. */ showSpecialDesc() { /* * if we are to use our initial description, show that; * otherwise, show our special description */ if (useInitSpecialDesc()) initSpecialDesc; else specialDesc; } /* show the special description under obscured viewing conditions */ showObscuredSpecialDesc() { if (useInitSpecialDesc()) obscuredInitSpecialDesc; else obscuredSpecialDesc; } /* show the special description under distant viewing conditions */ showDistantSpecialDesc() { if (useInitSpecialDesc()) distantInitSpecialDesc; else distantSpecialDesc; } /* show the remote special description */ showRemoteSpecialDesc(actor) { if (useInitSpecialDesc()) remoteInitSpecialDesc(actor); else remoteSpecialDesc(actor); } /* * Determine if we should use a special description. By default, we * have a special description if we have either a non-nil * specialDesc property, or we have an initial description. */ useSpecialDesc() { /* * if we have a non-nil specialDesc, or we are to use an initial * description, we have a special description */ return propType(&specialDesc) != TypeNil || useInitSpecialDesc(); } /* * Determine if we should use our special description in the given * room's LOOK AROUND description. By default, this simply returns * useSpecialDesc(). */ useSpecialDescInRoom(room) { return useSpecialDesc(); } /* * Determine if we should use our special description in the given * object's contents listings, for the purposes of "examine " * or "look in ". By default, we'll use our special * description for a given container if we'd use our special * description in general, AND we're actually inside the container * being examined. */ useSpecialDescInContents(cont) { /* * if we would otherwise use a special description, and we are * contained within the given object, use our special description * in the container */ return useSpecialDesc() && self.isIn(cont); } /* * Show our special description as part of a parent's full * description. */ showSpecialDescInContentsWithInfo(info, pov, cont) { local povActor = getPOVActorDefault(gActor); /* determine what to show, based on the location and sense path */ if (getOutermostRoom() != povActor.getOutermostRoom() || pov != povActor) showRemoteSpecialDescInContents(povActor, cont); else if (info.trans == obscured) showObscuredSpecialDescInContents(povActor, cont); else if (info.trans == distant) showDistantSpecialDescInContents(povActor, cont); else if (canDetailsBeSensed(sight, info, pov)) showSpecialDescInContents(povActor, cont); } /* * Show the special description in contents listings under various * sense conditions. By default, we'll use the corresponding special * descriptions for full room descriptions. These can be overridden * to show special versions of the description when we're examining * particular containers, if desired. 'actor' is the actor doing the * looking. */ showSpecialDescInContents(actor, cont) { showSpecialDesc(); } showObscuredSpecialDescInContents(actor, cont) { showObscuredSpecialDesc(); } showDistantSpecialDescInContents(actor, cont) { showDistantSpecialDesc(); } showRemoteSpecialDescInContents(actor, cont) { showRemoteSpecialDesc(actor); } /* * If we define a non-nil initSpecialDesc, this property will be * called to describe the object in room listings as long as the * object is in its "initial" state (as determined by isInInitState: * this is usually true until the object is first moved to a new * location). By default, objects don't have initial descriptions. * * If this is non-nil, and the object is portable, this will be used * (as long as the object is in its initial state) instead of * showing the object in an ordinary room-contents listing. This * can be used to give the object a special mention in its initial * location in the game, rather than letting the ordinary * room-contents lister lump it in with all of the other portable * object lying around. */ initSpecialDesc = nil /* * The initial descriptions to use under obscured and distant * viewing conditions. By default, these simply show the plain * initSpecialDesc; these can be overridden, if desired, to show * alternative messages when viewing conditions are less than ideal. * * Note that in order for one of these alternative initial * descriptions to be shown, the regular initSpecialDesc MUST be * defined, even if it's never actually used. We make the decision * to display these other descriptions based on the existence of a * non-nil initSpecialDesc, so always define initSpecialDesc * whenever these are defined. */ obscuredInitSpecialDesc = (initSpecialDesc) distantInitSpecialDesc = (initSpecialDesc) /* the initial remote special description */ remoteInitSpecialDesc(actor) { distantInitSpecialDesc; } /* * If we define a non-nil initDesc, and the object is in its initial * description state (as indicated by isInInitState), then we'll use * this property instead of "desc" to describe the object when * examined. This can be used to customize the description the * player sees in parallel to initSpecialDesc. */ initDesc = nil /* * Am I in my "initial state"? This is used to determine if we * should show the initial special description (initSpecialDesc) and * initial examine description (initDesc) when describing the * object. By default, we consider the object to be in its initial * state until the first time it's moved. * * You can override this to achieve other effects. For example, if * you want to use the initial description only the first time the * object is examined, and then revert to the ordinary description, * you could override this to return (!described). */ isInInitState = (!moved) /* * Determine whether or not I should be mentioned in my containing * room's description (on LOOK AROUND) using my initial special * description (initSpecialDesc). This returns true if I have an * initial description that isn't nil, and I'm in my initial state. * If this returns nil, the object should be described in room * descriptions using the ordinary generated message (either the * specialDesc, if we have one, or the ordinary mention in the list * of portable room contents). */ useInitSpecialDesc() { return isInInitState && propType(&initSpecialDesc) != TypeNil; } /* * Determine if I should be described on EXAMINE using my initial * examine description (initDesc). This returns true if I have an * initial examine desription that isn't nil, and I'm in my initial * state. If this returns nil, we'll show our ordinary description * (given by the 'desc' property). */ useInitDesc() { return isInInitState && propType(&initDesc) != TypeNil; } /* * Flag: I've been moved out of my initial location. Whenever we * move the object to a new location, we'll set this to true. */ moved = nil /* * Flag: I've been seen by the player character. This is nil by * default; we set this to true whenever we show a room description * from the player character's perspective, and the object is * visible. The object doesn't actually have to be mentioned in the * room description to be marked as seen - it merely has to be * visible to the player character. * * Note that this is only the DEFAULT 'seen' property, which all * Actor objects use by default. The ACTUAL property that a given * Actor uses depends on the actor's seenProp, which allows a game to * keep track separately of what each actor has seen by using * different 'seen' properties for different actors. */ seen = nil /* * Flag: suppress the automatic setting of the "seen" status for this * object in room and object descriptions. Normally, we'll * automatically mark every visible object as seen (by calling * gActor.setHasSeen()) whenever we do a LOOK AROUND. We'll also * automatically mark as seen every visible object within an object * examined explicitly (such as with EXAMINE, LOOK IN, LOOK ON, or * OPEN). This property can override this automatic status change: * when this property is true, we will NOT mark this object as seen * in any of these cases. When this property is true, the game must * explicitly mark the object as seen, if and when desired, by * calling actor.setHasSeen(). * * Sometimes, an object is not meant to be immediately obvious. For * example, a puzzle box might have a hidden button that can't be * seen on casual examination. In these cases, you can use * suppressAutoSeen to ensure that the object won't be marked as seen * merely by virtue of its being visible at the time of a LOOK AROUND * or EXAMINE command. * * Note that this property does NOT affect the object's actual * visibility or other sensory attributes. This property merely * controls the automatic setting of the "seen" status of the object. */ suppressAutoSeen = nil /* * Flag: I've been desribed. This is nil by default; we set this to * true whenever the player explicitly examines the object. */ described = nil /* * Mark all visible contents of 'self' as having been seen. * 'infoTab' is a LookupTable of sight information, as returned by * visibleInfoTable(). This should be called any time an object is * examined in such a way that its contents should be considered to * have been seen. * * We will NOT mark as seen any objects that have suppressAutoSeen * set to true. */ setContentsSeenBy(infoTab, actor) { /* * run through the table of visible objects, and mark each one * that's contained within 'self' as having been seen */ infoTab.forEachAssoc(function(obj, info) { if (obj.isIn(self) && !obj.suppressAutoSeen) actor.setHasSeen(obj); }); } /* * Mark everything visible as having been seen. 'infoTab' is a * LookupTable of sight information, as returned by * visibleInfoTable(). We'll mark everything visible in the table * as having been seen by the actor, EXCEPT objects that have * suppressAutoSeen set to true. */ setAllSeenBy(infoTab, actor) { /* mark everything as seen, except suppressAutoSeen items */ infoTab.forEachAssoc(function(obj, info) { if (!obj.suppressAutoSeen) actor.setHasSeen(obj); }); } /* * Note that I've been seen by the given actor, setting the given * "seen" property. This routine notifies the object that it's just * been observed by the given actor, allowing it to take any special * action it wants to take in such cases. */ noteSeenBy(actor, prop) { /* by default, simply set the given "seen" property to true */ self.(prop) = true; } /* * Flag: this object is explicitly "known" to actors in the game, * even if it's never been seen. This allows the object to be * resolved as a topic in ASK ABOUT commands and the like. * Sometimes, actors know about an object even before it's been seen * - they might simply know about it from background knowledge, or * they might hear about it from another character, for example. * * Like the 'seen' property, this is merely the DEFAULT 'known' * property that we use for actors. Each actor can individually use * a separate property to track its own knowledge if it prefers; it * can do this simply by overriding its isKnownProp property. */ isKnown = nil /* * Hide from 'all' for a given action. If this returns true, this * object will never be included in 'all' lists for the given action * class. This should generally be set only for objects that serve * some sort of internal purpose and don't represent physical * objects in the model world. By default, objects are not hidden * from 'all' lists. * * Note that returning nil doesn't put the object into an 'all' * list. Rather, it simply *leaves* it in any 'all' list it should * happen to be in. Each action controls its own selection criteria * for 'all', and different verbs use different criteria. No matter * how an action chooses its 'all' list, though, an item will always * be excluded if hideFromAll() returns true for the item. */ hideFromAll(action) { return nil; } /* * Hide from defaulting for a given action. By default, we're * hidden from being used as a default object for a given action if * we're hidden from the action for 'all'. */ hideFromDefault(action) { return hideFromAll(action); } /* * Determine if I'm to be listed at all in my room's description, * and in descriptions of objects containing my container. * * Most objects should be listed normally, but some types of objects * should be suppressed from the normal room listing. For example, * fixed-in-place scenery objects are generally described in the * custom message for the containing room, so these are normally * omitted from the listing of the room's contents. * * By default, we'll return the same thing as isListedInContents - * that is, if this object is to be listed when its *direct* * container is examined, it'll also be listed by default when any * further enclosing container (including the enclosing room) is * described. Individual objects can override this to use different * rules. * * Why would we want to be able to list an object when examining in * its direct container, but not when examining an enclosing * container, or the enclosing room? The most common reason is to * control the level of detail, to avoid overloading the broad * description of the room and the main things in it with every * detail of every deeply-nested container. */ isListed { return isListedInContents; } /* * Determine if I'm listed in explicit "examine" and "look in" * descriptions of my direct container. * * By default, we return true as long as we're not using our special * description in this particular context. Examining or looking in a * container will normally show special message for any contents of * the container, so we don't want to list the items with special * descriptions in the ordinary list as well. */ isListedInContents { return !useSpecialDescInContents(location); } /* * Determine if I'm listed in inventory listings. By default, we * include every item in an inventory list. */ isListedInInventory { return true; } /* * by default, regular objects are not listed when they arrive * aboard vehicles (only actors are normally listed in this fashion) */ isListedAboardVehicle = nil /* * Determine if I'm listed as being located in the given room part. * We'll first check to make sure the object is nominally contained * in the room part; if not, it's not listed in the room part. We'll * then ask the room part itself to make the final determination. */ isListedInRoomPart(part) { /* * list me if I'm nominally contained in the room part AND the * room part wants me listed */ return (isNominallyInRoomPart(part) && part.isObjListedInRoomPart(self)); } /* * Determine if we're nominally in the given room part (floor, * ceiling, wall, etc). This returns true if we *appear* to be * located directly in/on the given room part object. * * In most cases, a portable object might start out with a special * initial room part location, but once moved and then dropped * somewhere, ends up nominally in the nominal drop destination of * the location where it was dropped. For example, a poster might * start out being nominally attached to a wall, or a light bulb * might be nominally hanging from the ceiling; if these objects are * taken and then dropped somewhere, they'll simply end up on the * floor. * * Our default behavior models this. If we've never been moved, * we'll indicate that we're in our initial room part location, * given by initNominalRoomPartLocation. Once we've been moved (or * if our initNominalRoomPartLocation is nil), we'll figure out what * the nominal drop destination is for our current container, and * then see if we appear to be in that nominal drop destination. */ isNominallyInRoomPart(part) { /* * If we're not directly in the apparent container of the given * room part, then we certainly are not even nominally in the * room part. We only appear to be in a room part when we're * actually in the room directly containing the room part. */ if (location == nil || location.getRoomPartLocation(part) != location) return nil; /* if we've never been moved, use our initial room part location */ if (!moved && initNominalRoomPartLocation != nil) return (part == initNominalRoomPartLocation); /* if we have an explicit special room part location, use that */ if (specialNominalRoomPartLocation != nil) return (part == specialNominalRoomPartLocation); /* * We don't seem to have an initial or special room part * location, but there's still one more possibility: if the room * part is the nominal drop destination, then we are by default * nominally in that room part, because that's where we * otherwise end up in the absence of any special room part * location setting. */ if (location.getNominalDropDestination() == part) return true; /* we aren't nominally in the given room part */ return nil; } /* * Determine if I should show my special description with the * description of the given room part (floor, ceiling, wall, etc). * * By default, we'll include our special description with a room * part's description if either (1) we are using our initial * description, and our initNominalRoomPartLocation is the given * part; or (2) we are using our special description, and our * specialNominalRoomPartLocation is the given part. * * Note that, by default, we do NOT use our special description for * the "default" room part location - that is, for the nominal drop * destination for our containing room, which is where we end up by * default, in the absence of an initial or special room part * location setting. We don't use our special description in this * default location because special descriptions are most frequently * used to describe an object that is specially situated, and hence * we don't want to assume a default situation. */ useSpecialDescInRoomPart(part) { /* if we're not nominally in the part, rule it out */ if (!isNominallyInRoomPart(part)) return nil; /* * if we're using our initial description, and we are explicitly * in the given room part initially, use the special description * for the room part */ if (useInitSpecialDesc() && initNominalRoomPartLocation == part) return true; /* * if we're using our special description, and we are explicitly * in the given room part as our special location, use the * special description */ if (useSpecialDesc() && specialNominalRoomPartLocation == part) return true; /* do not use the special description in the room part */ return nil; } /* * Our initial room part location. By default, we set this to nil, * which means that we'll use the nominal drop destination of our * actual initial location when asked. If desired, this can be set * to another part; for example, if a poster is initially described * as being "on the north wall," this should set to the default north * wall object. */ initNominalRoomPartLocation = nil /* * Our "special" room part location. By default, we set this to * nil, which means that we'll use the nominal drop destination of * our actual current location when asked. * * This property has a function similar to * initNominalRoomPartLocation, but is used to describe the nominal * room part container of the object has been moved (and even before * it's been moved, if initNominalRoomPartLocation is nil). * * It's rare for an object to have a special room part location * after it's been moved, because most games simply don't provide * commands for things like re-attaching a poster to a wall or * re-hanging a fan from the ceiling. When it is possible to move * an object to a new special location, though, this property can be * used to flag its new special location. */ specialNominalRoomPartLocation = nil /* * Determine if my contents are to be listed when I'm shown in a * listing (in a room description, inventory list, or a description * of my container). * * Note that this doesn't affect listing my contents when I'm * *directly* examined - use contentsListedInExamine to control that. * * By default, we'll list our contents in these situations if (1) we * have some kind of presence in the description of whatever it is * the player is looking at, either by being listed in the ordinary * way (that is, isListed is true) or in a custom way (via a * specialDesc), and (2) contentsListedInExamine is true. The * reasoning behind this default is that if the object itself shows * up in the description, then we usually want its contents to be * mentioned as well, but if the object itself isn't mentioned, then * its contents probably shouldn't be, either. And in any case, if * the contents aren't listed even when we *directly* examine the * object, then we almost certainly don't want its contents mentioned * when we only indirectly mention the object in the course of * describing something enclosing it. */ contentsListed = (contentsListedInExamine && (isListed || useSpecialDesc())) /* * Determine if my contents are to be listed when I'm directly * examined (with an EXAMINE/LOOK AT command). By default, we * *always* list our contents when we're directly examined. */ contentsListedInExamine = true /* * Determine if my contents are listed separately from my own list * entry. If this is true, then my contents will be listed in a * separate sentence from my own listing; for example, if 'self' is a * box, we'll be listed like so: * *. You are carrying a box. The box contains a key and a book. * * By default, this is nil, which means that we list our contents * parenthetically after our name: * *. You are carrying a box (which contains a key and a book). * * Using a separate listing is sometimes desirable for an object that * will routinely contain listable objects that in turn have listable * contents of their own, because it can help break up a long listing * that would otherwise use too many nested parentheticals. * * Note that this only applies to "wide" listings; "tall" listings * will show contents in the indented tree format regardless of this * setting. */ contentsListedSeparately = nil /* * The language-dependent part of the library must provide a couple * of basic descriptor methods that we depend upon. We provide * several descriptor methods that we base on the basic methods. * The basic methods that must be provided by the language extension * are: * * listName - return a string giving the "list name" of the object; * that is, the name that should appear in inventory and room * contents lists. This is called with the visual sense info set * according to the point of view. * * countName(count) - return a string giving the "counted name" of * the object; that is, the name as it should appear with the given * quantifier. In English, this might return something like 'one * box' or 'three books'. This is called with the visual sense info * set according to the point of view. */ /* * Show this item as part of a list. 'options' is a combination of * ListXxx flags indicating the type of listing. 'infoTab' is a * lookup table of SenseInfo objects giving the sense information * for the point of view. */ showListItem(options, pov, infoTab) { /* show the list, using the 'listName' of the state */ showListItemGen(options, pov, infoTab, &listName); } /* * General routine to show the item as part of a list. * 'stateNameProp' is the property to use in any listing state * object to obtain the state name. */ showListItemGen(options, pov, infoTab, stateNameProp) { local info; local st; local stName; /* get my visual information from the point of view */ info = infoTab[self]; /* show the item's list name */ say(withVisualSenseInfo(pov, info, &listName)); /* * If we have a list state with a name, show it. Note that to * obtain the state name, we have to pass a list of the objects * being listed to the stateNameProp method of the state object; * we're the only object we're showing for this particular * display list element, so we simply pass a single-element list * containing 'self'. */ if ((st = getStateWithInfo(info, pov)) != nil && (stName = st.(stateNameProp)([self])) != nil) { /* we have a state with a name - show it */ gLibMessages.showListState(stName); } } /* * Show this item as part of a list, grouped with a count of * list-equivalent items. */ showListItemCounted(lst, options, pov, infoTab) { /* show the item using the 'listName' property of the state */ showListItemCountedGen(lst, options, pov, infoTab, &listName); } /* * General routine to show this item as part of a list, grouped with * a count of list-equivalent items. 'stateNameProp' is the * property of any list state object that we should use to obtain * the name of the listing state. */ showListItemCountedGen(lst, options, pov, infoTab, stateNameProp) { local info; local stateList; /* get the visual information from the point of view */ info = infoTab[self]; /* show our counted name */ say(countListName(lst.length(), pov, info)); /* check for list states */ stateList = new Vector(10); foreach (local cur in lst) { local st; /* get this item's sense information from the list */ info = infoTab[cur]; /* * if this item has a list state with a name, include it in * our list of states to show */ if ((st = cur.getStateWithInfo(info, pov)) != nil && st.(stateNameProp)(lst) != nil) { local stInfo; /* * if this state is already in our list, simply * increment the count of items in this state; * otherwise, add the new state to the list */ stInfo = stateList.valWhich({x: x.stateObj == st}); if (stInfo != nil) { /* it's already in the list - count the new item */ stInfo.addEquivObj(cur); } else { /* it's not in the list - add it */ stateList.append( new EquivalentStateInfo(st, cur, stateNameProp)); } } } /* if the state list is non-empty, show it */ if (stateList.length() != 0) { /* put the state list in its desired order */ stateList.sort(SortAsc, {a, b: (a.stateObj.listingOrder - b.stateObj.listingOrder)}); /* * If there's only one item in the state list, and all of * the objects are in that state, then show the "all in * state" message. Otherwise, show the list of states with * counts. * * (Note that it's possible to have just one state in the * list without having all of the objects in this state, * because we could have some of the objects in an unnamed * state, in which case they wouldn't contribute to the list * at all.) */ if (stateList.length() == 1 && stateList[1].getEquivCount() == lst.length()) { /* everything is in the same state */ gLibMessages.allInSameListState(stateList[1].getEquivList(), stateList[1].getName()); } else { /* list the state items */ equivalentStateLister.showListAll(stateList.toList(), 0, 0); } } } /* * Single-item counted listing description. This is used to display * an item with a count of equivalent items ("four gold coins"). * 'info' is the sense information from the current point of view * for 'self', which we take to be representative of the sense * information for all of the equivalent items. */ countListName(equivCount, pov, info) { return withVisualSenseInfo(pov, info, &countName, equivCount); } /* * Show this item as part of an inventory list. By default, we'll * show the regular list item name. */ showInventoryItem(options, pov, infoTab) { /* show the item, using the inventory state name */ showListItemGen(options, pov, infoTab, &inventoryName); } showInventoryItemCounted(lst, options, pov, infoTab) { /* show the item, using the inventory state name */ showListItemCountedGen(lst, options, pov, infoTab, &inventoryName); } /* * Show this item as part of a list of items being worn. */ showWornItem(options, pov, infoTab) { /* show the item, using the worn-listing state name */ showListItemGen(options, pov, infoTab, &wornName); } showWornItemCounted(lst, options, pov, infoTab) { /* show the item, using the worn-listing state name */ showListItemCountedGen(lst, options, pov, infoTab, &wornName); } /* * Get the "listing state" of the object, given the visual sense * information for the object from the point of view for which we're * generating the listing. This returns a ThingState object * describing the object's state for the purposes of listings. This * should return nil if the object doesn't have varying states for * listings. * * By default, we return a list state if the visual sense path is * transparent or attenuated, or we have large visual scale. In * other cases, we assume that the details of the object are not * visible under the current sense conditions; since the list state * is normally a detail of the object, we don't return a list state * when the details of the object are not visible. */ getStateWithInfo(info, pov) { /* * if our details can be sensed, return the list state; * otherwise, return nil, since the list state is a detail */ if (canDetailsBeSensed(sight, info, pov)) return getState(); else return nil; } /* * Get our state - returns a ThingState object describing the state. * By default, we don't have varying states, so we simply return * nil. */ getState = nil /* * Get a list of all of our possible states. For an object that can * assume varying states as represented by getState, this should * return the list of all possible states that the object can * assume. */ allStates = [] /* * The default long description, which is displayed in response to * an explicit player request to examine the object. We'll use a * generic library message; most objects should override this to * customize the object's desription. * * Note that we show this as a "default descriptive report," because * this default message indicates merely that there's nothing * special to say about the object. If we generate any additional * description messages, such as status reports ("it's open" or "it * contains a gold key") or special descriptions for things inside, * we clearly *do* have something special to say about the object, * so we'll want to suppress the nothing-special message. To * accomplish this suppression, all we have to do is report our * generic description as a default descriptive report, and the * transcript will automatically filter it out if there are any * other reports for this same action. * * Note that any time this is overridden by an object with any sort * of actual description, the override should NOT use * defaultDescReport. Instead, simply set this to display the * descriptive message directly: * * desc = "It's a big green box. " */ desc { defaultDescReport(&thingDescMsg, self); } /* * Our LOOK IN description. This is shown when we explicitly LOOK IN * the object. By default, we just report that there's nothing * unusual inside. */ lookInDesc { mainReport(¬hingInsideMsg); } /* * The default "distant" description. If this is defined for an * object, then we evaluate it to display the description when an * actor explicitly examines this object from a point of view where * we have a "distant" sight path to the object. * * If this property is left undefined for an object, then we'll * describe this object when it's distant in one of two ways. If * the object has its 'sightSize' property set to 'large', we'll * display the ordinary 'desc', because its large visual size makes * its details visible at a distance. If the 'sightSize' is * anything else, we'll instead display the default library message * indicating that the object is too far away to see any details. * * To display a specific description when the object is distant, set * this to a double-quoted string, or to a method that displays a * message. */ // distantDesc = "the distant description" /* the default distant description */ defaultDistantDesc { gLibMessages.distantThingDesc(self); } /* * The "obscured" description. If this is defined for an object, * then we call it to display the description when an actor * explicitly examines this object from a point of view where we * have an "obscured" sight path to the object. * * If this property is left undefined for an object, then we'll * describe this object when it's obscured in one of two ways. If * the object has its 'sightSize' property set to 'large', we'll * display the ordinary 'desc', because its large visual size makes * its details visible even when the object is obscured. If the * 'sightSize' is anything else, we'll instead display the default * library message indicating that the object is too obscured to see * any details. * * To display a specific description when the object is visually * obscured, override this to a method that displays your message. * 'obs' is the object that's obstructing the view - this will be * something on our sense path, such as a dirty window, that the * actor has to look through to see 'self'. */ // obscuredDesc(obs) { "the obscured description"; } /* * The "remote" description. If this is defined for an object, then * we call it to display the description when an actor explicitly * examines this object from a separate top-level location. That * is, when the actor's outermost enclosing room is different from * our own outermost enclosing room, we'll use this description. * * If this property is left undefined, then we'll describe this * object when it's distant as though it were in the same room. So, * we'll select the obscured, distant, or ordinary description, * according to the sense path. */ // remoteDesc(pov) { "the remote description" } /* the default obscured description */ defaultObscuredDesc(obs) { gLibMessages.obscuredThingDesc(self, obs); } /* * The "sound description," which is the description displayed when * an actor explicitly listens to the object. This is used when we * have a transparent sense path and no associated "emanation" * object; when we have an associated emanation object, we use its * description instead of this one. */ soundDesc { defaultDescReport(&thingSoundDescMsg, self); } /* distant sound description */ distantSoundDesc { gLibMessages.distantThingSoundDesc(self); } /* obscured sound description */ obscuredSoundDesc(obs) { gLibMessages.obscuredThingSoundDesc(self, obs); } /* * The "smell description," which is the description displayed when * an actor explicitly smells the object. This is used when we have * a transparent sense path to the object, and we have no * "emanation" object; when we have an associated emanation object, * we use its description instead of this one. */ smellDesc { defaultDescReport(&thingSmellDescMsg, self); } /* distant smell description */ distantSmellDesc { gLibMessages.distantThingSmellDesc(self); } /* obscured smell description */ obscuredSmellDesc(obs) { gLibMessages.obscuredThingSmellDesc(self, obs); } /* * The "taste description," which is the description displayed when * an actor explicitly tastes the object. Note that, unlike sound * and smell, we don't distinguish levels of transparency or * distance with taste, because tasting an object requires direct * physical contact with it. */ tasteDesc { gLibMessages.thingTasteDesc(self); } /* * The "feel description," which is the description displayed when * an actor explicitly feels the object. As with taste, we don't * distinguish transparency or distance. */ feelDesc { gLibMessages.thingFeelDesc(self); } /* * Show the smell/sound description for the object as part of a room * description. These are displayed when the object is in the room * and it has a presence in the corresponding sense. By default, * these show nothing. * * In most cases, regular objects don't override these, because most * regular objects have no direct sensory presence of their own. * Instead, a Noise or Odor is created and added to the object's * direct contents, and the Noise or Odor provides the object's * sense presence. */ smellHereDesc() { } soundHereDesc() { } /* * "Equivalence" flag. If this flag is set, then all objects with * the same immediate superclass will be considered interchangeable; * such objects will be listed collectively in messages (so we would * display "five coins" rather than "a coin, a coin, a coin, a coin, * and a coin"), and will be treated as equivalent in resolving noun * phrases to objects in user input. * * By default, this property is nil, since we want most objects to * be treated as unique. */ isEquivalent = nil /* * My Distinguisher list. This is a list of Distinguisher objects * that can be used to distinguish this object from other objects. * * Distinguishers are listed in order of priority. The * disambiguation process looks for distinguishers capable of telling * objects apart, starting with the first in the list. The * BasicDistinguisher is generally first in every object's list, * because any two objects can be told apart if they come from * different classes. * * By default, each object has the "basic" distinguisher, which tells * objects apart on the basis of the "isEquivalent" property and * their superclasses; the ownership distinguisher, which tells * objects apart based on ownership; and the location distinguisher, * which identifies objects by their immediate containers. */ distinguishers = [basicDistinguisher, ownershipDistinguisher, locationDistinguisher] /* * Get the distinguisher to use for printing this object's name in an * action announcement (such as a multi-object, default object, or * vague-match announcement). We check the global option setting to * see if we should actually use distinguishers for this; if so, we * call getInScopeDistinguisher() to find the correct distinguisher, * otherwise we use the "null" distinguisher, which simply lists * objects by their base names. * * 'lst' is the list of other objects from which we're trying to * differentiate 'self'. The reason 'lst' is given is that it lets * us choose the simplest name for each object that usefully * distinguishes it; to do this, we need to know exactly what we're * distinguishing it from. */ getAnnouncementDistinguisher(lst) { return (gameMain.useDistinguishersInAnnouncements ? getBestDistinguisher(lst) : nullDistinguisher); } /* * Get a distinguisher that differentiates me from all of the other * objects in scope, if possible, or at least from some of the other * objects in scope. */ getInScopeDistinguisher() { /* return the best distinguisher for the objects in scope */ return getBestDistinguisher(gActor.scopeList()); } /* * Get a distinguisher that differentiates me from all of the other * objects in the given list, if possible, or from as many of the * other objects as possible. */ getBestDistinguisher(lst) { local bestDist, bestCnt; /* remove 'self' from the list */ lst -= self; /* * Try the "null" distinguisher, which distinguishes objects * simply by their base names - if that can tell us apart from * the other objects, there's no need for anything more * elaborate. So, calculate the list of indistinguishable * objects using the null distinguisher, and if it's empty, we * can just use the null distinguisher as our result. */ if (lst.subset({obj: !nullDistinguisher.canDistinguish(self, obj)}) .length() == 0) return nullDistinguisher; /* * Reduce the list to the set of objects that are basic * equivalents of mine - these are the only ones we care about * telling apart from us, since all of the other objects can be * distinguished by their disambig name. */ lst = lst.subset( {obj: !basicDistinguisher.canDistinguish(self, obj)}); /* if the basic distinguisher can tell us apart, just use it */ if (lst.length() == 0) return basicDistinguisher; /* as a last resort, fall back on the basic distinguisher */ bestDist = basicDistinguisher; bestCnt = lst.countWhich({obj: bestDist.canDistinguish(self, obj)}); /* * run through my distinguisher list looking for the * distinguisher that can tell me apart from the largest number * of my vocab equivalents */ foreach (local dist in distinguishers) { /* if this is the default, skip it */ if (dist == bestDist) continue; /* check to see how many objects 'dist' can distinguish me from */ local cnt = lst.countWhich({obj: dist.canDistinguish(self, obj)}); /* * if it can distinguish me from every other object, use this * one - it uniquely identifies me */ if (cnt == lst.length()) return dist; /* * that can't distinguish us from everything else here, but * if it's the best so far, remember it; we'll fall back on * the best that we find if we fail to find a perfect * distinguisher */ if (cnt > bestCnt) { bestDist = dist; bestCnt = cnt; } } /* * We didn't find any distinguishers that can tell me apart from * every other object, so choose the one that can tell me apart * from the most other objects. */ return bestDist; } /* * Determine if I'm equivalent, for the purposes of command * vocabulary, to given object. * * We'll run through our list of distinguishers and check with each * one to see if it can tell us apart from the other object. If we * can find at least one distinguisher that can tell us apart, we're * not equivalent. If we have no distinguisher that can tell us * apart from the other object, we're equivalent. */ isVocabEquivalent(obj) { /* * Check each distinguisher - if we can find one that can tell * us apart from the other object, we're not equivalent. If * there are no distinguishers that can tell us apart, we're * equivalent. */ return (distinguishers.indexWhich( {cur: cur.canDistinguish(self, obj)}) == nil); } /* * My associated "collective group" objects. A collective group is * an abstract object, not part of the simulation (i.e, not directly * manipulable by characters as a separate object) that can stand in * for an entire group of related objects in some actions. For * example, we might have a collective "money" object that stands in * for any group of coins and paper bills for "examine" commands, so * that when the player says something like "look at money" or "count * money," we use the single collective money object to handle the * command rather than running the command iteratively on each of the * individual coins and bills present. * * The value is a list because this object can be associated with * more than one collective group. For example, a diamond could be * in a "treasure" group as well as a "jewelry" group. * * The associated collective objects are normally of class * CollectiveGroup. By default, if our 'collectiveGroup' property is * not nil, our list consists of that one item; otherwise we just use * an empty list. */ collectiveGroups = (collectiveGroup != nil ? [collectiveGroup] : []) /* * Our collective group. Note - this property is obsolescent; it's * supported only for compatibility with old code. New games should * use 'collectiveGroups' instead. */ collectiveGroup = nil /* * Are we associated with the given collective group? We do if it's * in our collectiveGroups list. */ hasCollectiveGroup(g) { return collectiveGroups.indexOf(g) != nil; } /* * "List Group" objects. This specifies a list of ListGroup objects * that we use to list this object in object listings, such as * inventory lists and room contents lists. * * An object can be grouped in more than one way. When multiple * groups are specified here, the order is significant: * * - To the extent two groups entirely overlap, which is to say that * one of the pair entirely contains the other (for example, if * every coin is a kind of money, then the "money" listing group * would contain every object in the "coin" group, plus other * objects as well: the coin group is a subset of the money group), * the groups must be listed from most general to most specific (for * our money/coin example, then, money would come before coin in the * group list). * * - When two groups do not overlap, then the earlier one in our * list is given higher priority. * * By default, we return an empty list. */ listWith = [] /* * Get the list group for my special description. This works like * the ordinary listWith, but is used to group me with other objects * showing special descriptions, rather than in ordinary listings. */ specialDescListWith = [] /* * Our interior room name. This is the status line name we display * when an actor is within this object and can't see out to the * enclosing room. Since we can't rely on the enclosing room's * status line name if we can't see the enclosing room, we must * provide one of our own. * * By default, we'll use our regular name. */ roomName = (name) /* * Show our interior description. We use this to generate the long * "look" description for the room when an actor is within this * object and cannot see the enclosing room. * * Note that this is used ONLY when the actor cannot see the * enclosing room - when the enclosing room is visible (because the * nested room is something like a chair that doesn't enclose the * actor, or can enclose the actor but is open or transparent), then * we'll simply use the description of the enclosing room instead, * adding a note to the short name shown at the start of the room * description indicating that the actor is in the nested room. * * By default, we'll show the appropriate "actor here" description * for the posture, so we'll say something like "You are sitting on * the red chair" or "You are in the phone booth." Instances can * override this to customize the description with something more * detailed, if desired. */ roomDesc { gActor.listActorPosture(getPOVActorDefault(gActor)); } /* * Show our first-time room description. This is the description we * show when the actor is seeing our interior description for the * first time. By default, we just show the ordinary room * description, but this can be overridden to provide a special * description the first time the actor sees the room. */ roomFirstDesc { roomDesc; } /* * Show our remote viewing description. This is used when we show a * description of a room, via lookAroundWithin, and the actor who's * viewing the room is remote. Note that the library never does this * itself, since there's no command in the basic library that lets a * player view a remote room. However, a game might want to generate * such a description to handle a command like LOOK THROUGH KEYHOLE, * so we provide this extra description method for the game's use. * * By default, we simply show the normal room description. You'll * probably want to override this any time you actually use it, * though, to describe what the actor sees from the remote point of * view. */ roomRemoteDesc(actor) { roomDesc; } /* our interior room name when we're in the dark */ roomDarkName = (gLibMessages.roomDarkName) /* show our interior description in the dark */ roomDarkDesc { gLibMessages.roomDarkDesc; } /* * Describe an actor in this location either from the point of view * of a separate top-level room, or at a distance. This is called * when we're showing a room description (for LOOK AROUND), and the * given actor is in a remote room or at a distance visually from * the actor doing the looking, and the given actor is contained * within this room. * * By default, if we have a location, and the actor doing the * looking can see out into the enclosing room, we'll defer to the * location. Otherwise, we'll show a default library message ("The * actor is nearby"). */ roomActorThereDesc(actor) { local pov = getPOV(); /* * if we have an enclosing location, and the POV actor can see * out to our location, defer to our location; otherwise, show * the default library message */ if (location != nil && pov != nil && pov.canSee(location)) location.roomActorThereDesc(actor); else gLibMessages.actorInRemoteRoom(actor, self, pov); } /* * Look around from the point of view of this object and on behalf of * the given actor. This can be used to generate a description as * seen from this object by the given actor, and is suitable for * cases where the actor can use this object as a sensing device. We * simply pass this through to lookAroundPov(), passing 'self' as the * point-of-view object. * * 'verbose' is a combination of LookXxx flags (defined in adv3.h) * indicating what style of description we want. This can also be * 'true', in which case we'll show the standard verbose listing * (LookRoomName | LookRoomDesc | LookListSpecials | * LookListPortables); or 'nil', in which case we'll use the standard * terse listing (LookRoomName | LookListSpecials | * LookListPortables). */ lookAround(actor, verbose) { /* look around from my point of view */ lookAroundPov(actor, self, verbose); } /* * Look around from within this object, looking from the given point * of view and on behalf of the given actor. * * 'actor' is the actor doing the looking, and 'pov' is the point of * view of the description. These are usually the same, but need not * be. For example, an actor could be looking at a room through a * hole in the wall, in which case the POV would be the object * representing the "side" of the hole in the room being described. * Or, the actor could be observing a remote room via a * closed-circuit television system, in which case the POV would be * the camera in the remote room. (The POV is the physical object * receiving the photons in the location being described, so it's the * camera, not the TV monitor that the actor's looking at.) * * 'verbose' has the same meaning as it does in lookAround(). * * This routine checks to see if 'self' is the "look-around ceiling," * which is for most purposes the outermost visible container of this * object; see isLookAroundCeiling() for more. If this object is the * look-around ceiling, then we'll call lookAroundWithin() to * generate the description of the interior of 'self'; otherwise, * we'll recursively defer to our immediate container so that it can * make the same test. In most cases, the outermost visible * container that actually generates the description will be a Room * or a NestedRoom. */ lookAroundPov(actor, pov, verbose) { /* * If we're the "look around ceiling," generate the description * within this room. Otherwise, we have an enclosing location * that provides the interior description. */ if (isLookAroundCeiling(actor, pov)) { /* we're the "ceiling," so describe our interior */ fromPOV(actor, pov, &lookAroundWithin, actor, pov, verbose); } else { /* our location provides the interior description */ location.lookAroundPov(actor, pov, verbose); } } /* * Are we the "look around ceiling"? If so, it means that a LOOK * AROUND for an actor within this location (or from a point of view * within this location) will use our own interior description, via * lookAroundWithin(). If we're not the ceiling, then we defer to * our location, letting it describe its interior. * * By default, we're the ceiling if we're a top-level room (that is, * we have no enclosing location), OR it's not possible to see out to * our location. In either of these cases, we can't see anything * outside of this room, so we have to generate our own interior * description. However, if we do have a location that we can see, * then we'll assume that this object just represents a facet of its * enclosing location, so the enclosing location provides the room * interior description. * * In some cases, a room might want to provide its own LOOK AROUND * interior description directly even if its location is visible. * For example, if the player's inside a wooden booth with a small * window that can see out to the enclosing location, LOOK AROUND * should probably describe the interior of the booth rather than the * enclosing location: even though the exterior is technically * visible, the booth clearly dominates the view, from the player's * perspective. In this case, we'd want to override this routine to * indicate that we're the LOOK AROUND ceiling, despite our * location's visibility. */ isLookAroundCeiling(actor, pov) { /* * if we don't have a location, or we can't see our location, * then we have to provide our own description, so we're the LOOK * AROUND "ceiling" */ return (location == nil || !actor.canSee(location)); } /* * Provide a "room description" of the interior of this object. This * routine is primarily intended as a subroutine of lookAround() and * lookAroundPov() - most game code shouldn't need to call this * routine directly. Note that if you *do* call this routine * directly, you *must* set the global point-of-view variables, which * you can do most easily by calling this routine indirectly through * the fromPOV() method. * * The library calls this method when an actor performs a "look * around" command, and the actor is within this object, and the * actor can't see anything outside of this object; this can happen * simply because we're a top-level room, but it can also happen when * we're a closed opaque container or there's not enough light to see * the enclosing location. * * The parameters have the same meaning as they do in * lookAroundPov(). * * Note that this method must be overridden if a room overrides the * standard mechanism for representing its contents list (i.e., it * doesn't store its complete set of direct contents in its * 'contents' list property). * * In most cases, this routine will only be called in Room and * NestedRoom objects, because actors can normally only enter those * types of objects. However, it is possible to try to describe the * interior of other types of objects if (1) the game allows actors * to enter other types of objects, or (2) the game provides a * non-actor point-of-view object, such as a video camera, that can * be placed in ordinary containers and which transmit what they see * for remote viewing. */ lookAroundWithin(actor, pov, verbose) { local illum; local infoTab; local info; local specialList; local specialBefore, specialAfter; /* check for the special 'true' and 'nil' settings for 'verbose' */ if (verbose == true) { /* true -> show the standard verbose description */ verbose = (LookRoomName | LookRoomDesc | LookListSpecials | LookListPortables); } else if (verbose == nil) { /* nil -> show the standard terse description */ verbose = (LookRoomName | LookListSpecials | LookListPortables); } /* * if we've never seen the room before, include the room * description, even if it wasn't specifically requested */ if (!actor.hasSeen(self)) verbose |= LookRoomDesc; /* * get a table of all of the objects that the viewer can sense * in the location using sight-like senses */ infoTab = actor.visibleInfoTableFromPov(pov); /* get the ambient illumination at the point of view */ info = infoTab[pov]; if (info != nil) { /* get the ambient illumination from the info list item */ illum = info.ambient; } else { /* the actor's not in the list, so it must be completely dark */ illum = 0; } /* * adjust the list of described items (usually, this removes the * point-of-view object from the list, to ensure that we don't * list it among the room's contents) */ adjustLookAroundTable(infoTab, pov, actor); /* if desired, display the room name */ if ((verbose & LookRoomName) != 0) { "<.roomname>"; lookAroundWithinName(actor, illum); "<./roomname>"; } /* if we're in verbose mode, display the full description */ if ((verbose & LookRoomDesc) != 0) { /* display the full room description */ "<.roomdesc>"; lookAroundWithinDesc(actor, illum); "<./roomdesc>"; } /* show the initial special descriptions, if desired */ if ((verbose & LookListSpecials) != 0) { local plst; /* * Display any special description messages for visible * objects, other than those carried by the actor. These * messages are part of the verbose description rather than * the portable item listing, because these messages are * meant to look like part of the room's full description * and thus should not be included in a non-verbose listing. * * Note that we only want to show objects that are currently * using special descriptions AND which aren't contained in * the actor doing the looking, so subset the list * accordingly. */ specialList = specialDescList( infoTab, {obj: obj.useSpecialDescInRoom(self) && !obj.isIn(actor)}); /* * partition the special list into the parts to be shown * before and after the portable contents list */ plst = partitionList(specialList, {obj: obj.specialDescBeforeContents}); specialBefore = plst[1]; specialAfter = plst[2]; /* * at this point, describe only the items that are to be * listed specially before the room's portable contents list */ specialContentsLister.showList(pov, nil, specialBefore, 0, 0, infoTab, nil); } /* show the portable items, if desired */ if ((verbose & LookListPortables) != 0) { /* * Describe each visible object directly contained in the * room that wants to be listed - generally, we list any * mobile objects that don't have special descriptions. We * list items in all room descriptions, verbose or not, * because the portable item list is more of a status display * than it is a part of the full description. * * Note that the infoTab has sense data that will ensure that * we don't show anything we shouldn't if the room is dark. */ lookAroundWithinContents(actor, illum, infoTab); } /* * If we're showing special descriptions, show the special descs * for any items that want to be listed after the room's portable * contents. */ if ((verbose & LookListSpecials) != 0) { /* show the subset that's listed after the room contents */ specialContentsLister.showList(pov, nil, specialAfter, 0, 0, infoTab, nil); } /* show all of the sounds we can hear */ lookAroundWithinSense(actor, pov, sound, roomListenLister); /* show all of the odors we can smell */ lookAroundWithinSense(actor, pov, smell, roomSmellLister); /* show the exits, if desired */ lookAroundWithinShowExits(actor, illum); /* * If we're not in the dark, note that we've been seen. Don't * count this as having seen the location if we're in the dark, * since we won't normally describe any detail in the dark. */ if (illum > 1) actor.setHasSeen(self); } /* * Display the "status line" name of the room. This is normally a * brief, single-line description. * * By long-standing convention, each location in a game usually has a * distinctive name that's displayed here. Players usually find * these names helpful in forming a mental map of the game. * * By default, if we have an enclosing location, and the actor can * see the enclosing location, we'll defer to the location. * Otherwise, we'll display our roo interior name. */ statusName(actor) { /* * use the enclosing location's status name if there is an * enclosing location and its visible; otherwise, show our * interior room name */ if (location != nil && actor.canSee(location)) location.statusName(actor); else lookAroundWithinName(actor, actor.getVisualAmbient()); } /* * Adjust the sense table a "look around" command. This is called * after we calculate the sense info table, but before we start * describing any of the room's contents, to give us a chance to * remove any items we don't want described (or, conceivably, to add * any items we do want described but which won't show up with the * normal sense calculations). * * By default, we simply remove the point-of-view object from the * list, to ensure that it's not included among the objects mentioned * as being in the room. We don't want to mention the point of view * because the POV object because a POV isn't normally in its own * field of view. */ adjustLookAroundTable(tab, pov, actor) { /* remove the POV object from the table */ tab.removeElement(pov); /* give the actor a chance to adjust the table as well */ if (self not in (actor, pov)) { /* let the POV object adjust the table */ pov.adjustLookAroundTable(tab, pov, actor); /* if the actor differs from the POV, let it adjust the table */ if (actor != pov) actor.adjustLookAroundTable(tab, pov, actor); } } /* * Show my name for a "look around" command. This is used to display * the location name when we're providing the room description. * 'illum' is the ambient visual sense level at the point of view. * * By default, we show our interior room name or interior dark room * name, as appropriate to the ambient light level at the point of * view. */ lookAroundWithinName(actor, illum) { /* * if there's light, show our name; otherwise, show our * in-the-dark name */ if (illum > 1) { /* we can see, so show our interior description */ "\^<>"; } else { /* we're in the dark, so show our default dark name */ say(roomDarkName); } /* show the actor's posture as part of the description */ actor.actorRoomNameStatus(self); } /* * Show our "look around" long description. This is used to display * the location's full description when we're providing the room * description - that is, when the actor doing the looking is inside * me. This displays only the room-specific portion of the room's * description; it does not display the status line or anything * about the room's dynamic contents. */ lookAroundWithinDesc(actor, illum) { /* * check for illumination - we must have at least dim ambient * lighting (level 2) to see the room's long description */ if (illum > 1) { local pov = getPOVDefault(actor); /* * Display the normal description of the room - use the * roomRemoteDesc if the actor isn't in the same room OR the * point of view isn't the same as the actor; use the * firstDesc if this is the first time in the room; and * otherwise the basic roomDesc. */ if (!actor.isIn(self) || actor != pov) { /* we're viewing the room remotely */ roomRemoteDesc(actor); } else if (actor.hasSeen(self)) { /* we've seen it already - show the basic room description */ roomDesc; } else { /* * we've never seen this location before - show the * first-time description */ roomFirstDesc; } } else { /* display the in-the-dark description of the room */ roomDarkDesc; } } /* * Show my room contents for a "look around" description within this * object. */ lookAroundWithinContents(actor, illum, infoTab) { local lst; local lister; local remoteLst; local recurse; /* get our outermost enclosing room */ local outer = getOutermostRoom(); /* mark everything visible from the room as having been seen */ setAllSeenBy(infoTab, actor); /* * if the illumination is less than 'dim' (level 2), display * only self-illuminating items */ if (illum != nil && illum < 2) { /* * We're in the dark - list only those objects that the actor * can sense via the sight-like senses, which will be the * list of self-illuminating objects. Don't include items * being carried by the actor, though, since those don't * count as part of the actor's surroundings. (To produce * this list, simply make a list consisting of all of the * objects in the sense info table that aren't contained in * the actor; since the table naturally includes only objects * that are illuminated, the entire contents of the table * will give us the visible objects.) */ lst = senseInfoTableSubset( infoTab, {obj, info: !obj.isIn(actor)}); /* use my dark contents lister */ lister = darkRoomContentsLister; /* there are no remote-room lists to generate */ remoteLst = nil; /* * Since we can't see what we're looking at, we can't see the * positional relationships among the things we're looking * at, so we can't show the contents tree recursively. We * just want to show our flat list of what's visible on * account of self-illumination. */ recurse = nil; } else { /* start with my contents list */ lst = contents; /* always remove the actor from the list */ lst -= actor; /* * Use the normal (lighted) lister. Note that if the actor * isn't in the same room, or the point of view isn't the * same as the actor, we want to generate remote * descriptions, so use the remote contents lister in these * cases. */ lister = (actor.isIn(self) && actor == getPOVDefault(actor) ? roomContentsLister : remoteRoomContentsLister(self)); /* * Generate a list of all of the *other* top-level rooms * with contents visible from here. To do this, build a * list of all of the unique top-level rooms, other than our * own top-level room, that contain items in the visible * information table. */ remoteLst = new Vector(5); infoTab.forEachAssoc(function(obj, info) { /* if this object isn't in our top-level room, note it */ if (obj != outer && !obj.isIn(outer)) { local objOuter; /* * it's not in our top-level room, so find its * top-level room */ objOuter = obj.getOutermostRoom(); /* if this one isn't in our list yet, add it */ if (remoteLst.indexOf(objOuter) == nil) remoteLst.append(objOuter); } }); /* * since we can see what we're looking at, show the * relationships among the contents by listing recursively */ recurse = true; } /* add a paragraph before the room's contents */ "<.p>"; /* show the contents */ lister.showList(actor, self, lst, (recurse ? ListRecurse : 0), 0, infoTab, nil, examinee: self); /* * if we can see anything in remote top-level rooms, show the * contents of each other room with visible contents */ if (remoteLst != nil) { /* show each remote room's contents */ for (local i = 1, local len = remoteLst.length() ; i <= len ; ++i) { local cur; local cont; /* get this item */ cur = remoteLst[i]; /* start with the direct contents of this remote room */ cont = cur.contents; /* * Remove any objects that are also contents of the * local room or of any other room we've already listed. * It's possible that we have a MultiLoc that's in one * or more of the visible top-level locations, so we * need to make sure we only list each one once. */ cont = cont.subset({x: !x.isIn(outer)}); for (local j = 1 ; j < i ; ++j) cont = cont.subset({x: !x.isIn(remoteLst[j])}); /* * list this remote room's contents using our remote * lister for this remote room */ outer.remoteRoomContentsLister(cur).showList( actor, cur, cont, ListRecurse, 0, infoTab, nil, examinee: self); } } } /* * Add to the room description a list of things we notice through a * specific sense. This is used to add things we can hear and smell * to a room description. */ lookAroundWithinSense(actor, pov, sense, lister) { local infoTab; local presenceList; /* * get the information table for the desired sense from the * given point of view */ infoTab = pov.senseInfoTable(sense); /* * Get the list of everything with a presence in this sense. * Include only items that aren't part of the actor's inventory, * since inventory items aren't part of the room and thus * shouldn't be described as part of the room. */ presenceList = senseInfoTableSubset(infoTab, {obj, info: obj.(sense.presenceProp) && !obj.isIn(actor)}); /* mark each item in the presence list as known */ foreach (local cur in presenceList) actor.setKnowsAbout(cur); /* list the items */ lister.showList(pov, nil, presenceList, 0, 0, infoTab, nil, examinee: self); } /* * Show the exits from this room as part of a description of the * room, if applicable. By default, if we have an exit lister * object, we'll invoke it to show the exits. */ lookAroundWithinShowExits(actor, illum) { /* if we have an exit lister, have it show a list of exits */ if (gExitLister != nil) gExitLister.lookAroundShowExits(actor, self, illum); } /* * Get my lighted room contents lister - this is the Lister object * that we use to display the room's contents when the room is lit. * We'll return the default library room lister. */ roomContentsLister { return roomLister; } /* * Get my lister for the contents of the given remote room. A * remote room is a separate top-level room that an actor can see * from its current location; for example, if two top-level rooms * are connected by a window, so that an actor standing in one room * can see into the other room, then the other room is the remote * room, from the actor's perspective. * * This is called on the actor's outermost room to get the lister * for objects visible in the given remote room. This lets each * room customize the way it describes the objects in adjoining * rooms as seen from its point of view. * * We provide a simple default lister that yields decent results in * most cases. However, it's often desirable to customize this, to * be more specific about how we see the items: "Through the window, * you see..." or "Further down the hall, you see...". An easy way * to provide such custom listings is to return a new * CustomRoomLister, supplying the prefix and suffix strings as * parameters: * *. return new CustomRoomLister( *. 'Further down the hall, {you/he} see{s}', '.'); */ remoteRoomContentsLister(other) { return new RemoteRoomLister(other); } /* * Get my dark room contents lister - this is the Lister object we'll * use to display the room's self-illuminating contents when the room * is dark. */ darkRoomContentsLister { return darkRoomLister; } /* * Get the outermost containing room. We return our container, if * we have one, or self if not. */ getOutermostRoom() { /* return our container's outermost room, if we have one */ return (location != nil ? location.getOutermostRoom() : self); } /* get the outermost room that's visible from the given point of view */ getOutermostVisibleRoom(pov) { local enc; /* * Get our outermost enclosing visible room - this is the * outermost enclosing room that the POV can see. If we manage * to find an enclosing visible room, return that, since it's * "more outer" than we are. */ if (location != nil && (enc = location.getOutermostVisibleRoom(pov)) != nil) return enc; /* * We either don't have any enclosing rooms, or none of them are * visible. So, our outermost enclosing room is one of two * things: if we're visible, it's us; if we're not even visible * ourselves, there's no visible room at all, so return nil. */ return (pov.canSee(self) ? self : nil); } /* * Get the location traveler - this is the object that's actually * going to change location when a traveler within this location * performs a travel command to travel via the given connector. By * default, we'll indicate what our containing room indicates. (The * enclosing room might be a vehicle or an ordinary room; in any * case, it'll know what to do, so we merely have to ask it.) * * We defer to our enclosing room by default because this allows for * things like a seat in a car: the actor is sitting in the seat and * starts traveling in the car, so the seat calls the enclosing room, * which is the car, and the car returns itself, since it's the car * that will be traveling. */ getLocTraveler(trav, conn) { /* * if we have a location, defer to it; otherwise, just return the * proposed traveler */ return (location != nil ? location.getLocTraveler(trav, conn) : trav); } /* * Get the "location push traveler" - this is the object that's going * to travel for a push-travel action performed by a traveler within * this location. This is called by a traveler within this location * to find out if the location wants to be involved in the travel, as * a vehicle might be. By default, we let our location handle it, if * we have one. */ getLocPushTraveler(trav, obj) { /* * defer to our location if we have one, otherwise just use the * proposed traveler */ return (location != nil ? location.getLocPushTraveler(trav, obj) : trav); } /* * Get the travel preconditions for an actor in this location. For * ordinary objects, we'll just defer to our container, if we have * one. */ roomTravelPreCond() { /* if we have a location, defer to it */ if (location != nil) return location.roomTravelPreCond(); /* * we have no location, and we impose no conditions of our by * default, so simply return an empty list */ return []; } /* * Determine if the current gActor, who is directly in this location, * is "travel ready." This means that the actor is ready, as far as * this location is concerned, to traverse the given connector. By * default, we'll defer to our location. * * Note that if you override this to return nil, you should also * provide a custom 'notTravelReadyMsg' property that explains the * objection. */ isActorTravelReady(conn) { /* if we have a location, defer to it */ if (location != nil) return location.isActorTravelReady(conn); /* * we have no location, and we impose no conditions of our own by * default, so we have no objection */ return true; } /* explain the reason isActorTravelReady() returned nil */ notTravelReadyMsg = (location != nil ? location.notTravelReadyMsg : nil) /* show a list of exits as part of failed travel - defer to location */ cannotGoShowExits(actor) { if (location != nil) location.cannotGoShowExits(actor); } /* show exits in the statusline - defer to our location */ showStatuslineExits() { if (location != nil) location.showStatuslineExits(); } /* get the status line exit lister height - defer to our location */ getStatuslineExitsHeight() { return location != nil ? location.getStatuslineExitsHeight() : 0; } /* * Get the travel connector from this location in the given * direction. * * Map all of the directional connections to their values in the * enclosing location, except for any explicitly defined in this * object. (In most cases, ordinary objects won't define any * directional connectors directly, since those connections usually * apply only to top-level rooms.) * * If 'actor' is non-nil, we'll limit our search to enclosing * locations that the actor can currently see. If 'actor' is nil, * we'll consider any connector in any enclosing location, whether * the actor can see the enclosing location or not. It's useful to * pass in a nil actor in cases where we're interested in the * structure of the map and not actual travel, such as when * constructing an automatic map or computing possible courses * between locations. */ getTravelConnector(dir, actor) { local conn; /* * if we explicitly define the link property, and the result is a * non-nil value, use that definition */ if (propDefined(dir.dirProp) && (conn = self.(dir.dirProp)) != nil) return conn; /* * We don't define the direction link explicitly. If the actor * can see our container, or there's no actor involved, retrieve * the link from our container; otherwise, stop here, and simply * return the default link for the direction. */ if (location != nil && (actor == nil || actor.canSee(location))) { /* * the actor can see out to our location (or there's no actor * involved at all), so get the connector for the location */ return location.getTravelConnector(dir, actor); } else { /* * the actor can't see our location, so there's no way to * travel this way - return the default connector for the * direction */ return dir.defaultConnector(self); } } /* * Search our direction list for a connector that will lead the * given actor to the given destination. */ getConnectorTo(actor, dest) { local conn; /* search all of our directions */ foreach (local dir in Direction.allDirections) { /* get the connector for this direction */ conn = getTravelConnector(dir, actor); /* ask the connector for a connector to the given destination */ if (conn != nil) conn = conn.connectorGetConnectorTo(self, actor, dest); /* if we found a valid connector, return it */ if (conn != nil) return conn; } /* didn't find a match */ return nil; } /* * Find a direction linked to a given connector, for travel by the * given actor. Returns the first direction (as a Direction object) * we find linked to the connector, or nil if we don't find any * direction linked to it. */ directionForConnector(conn, actor) { /* search all known directions */ foreach (local dir in Direction.allDirections) { /* if this direction is linked to the connector, return it */ if (self.getTravelConnector(dir, actor) == conn) return dir; } /* we didn't find any directions linked to the connector */ return nil; } /* * Find the "local" direction link from this object to the given * travel connector. We'll only consider our own local direction * links; we won't consider enclosing objects. */ localDirectionLinkForConnector(conn) { /* scan all direction links */ foreach (local dir in Direction.allDirections) { /* * if we define this direction property to point to this * connector, we've found our required location */ if (self.(dir.dirProp) == conn) return dir; } /* we didn't find a direction linked to the given connector */ return nil; } /* * Check that a traveler is directly in this room. By default, a * Thing is not a room, so a connector within a Thing actually * requires the traveler to be in the Thing's container, not in the * Thing itself. So, defer to our container. */ checkTravelerDirectlyInRoom(traveler, allowImplicit) { /* defer to our location */ return location.checkTravelerDirectlyInRoom(traveler, allowImplicit); } /* * Check, using pre-condition rules, that the actor is removed from * this nested location and moved to its exit destination. * * This is called when the actor is attempting to move to an object * outside of this object while the actor is within this object (for * example, if we have a platform containing a box containing a * chair, 'self' is the box, and the actor is in the chair, an * attempt to move to the platform will trigger a call to * box.checkActorOutOfNested). * * By default, we're not a nested location, but we could *contain* * nested locations. So what we need to do is defer to the child * object that actually contains the actor, to let it figure out what * it means to move the actor out of itself. */ checkActorOutOfNested(allowImplicit) { /* find the child containing the actor, and ask it to do the work */ foreach (local cur in contents) { if (gActor.isIn(cur)) return cur.checkActorOutOfNested(allowImplicit); } /* didn't find a child containing the actor; just fail */ reportFailure(&cannotDoFromHereMsg); exit; } /* * Get my "room location." If 'self' is capable of holding actors, * this should return 'self'; otherwise, it should return the * nearest enclosing container that's a room location. By default, * we simply return our location's room location. */ roomLocation = (location != nil ? location.roomLocation : nil) /* * Get the apparent location of one of our room parts (the floor, * the ceiling, etc). By default, we'll simply ask our container * about it, since a nested room by default doesn't have any of the * standard room parts. */ getRoomPartLocation(part) { if (location != nil) return location.getRoomPartLocation(part); else return nil; } /* * By default, an object containing the player character will forward * the roomDaemon() call up to the container. */ roomDaemon() { if (location != nil) location.roomDaemon(); } /* * Our room atmospheric message list. This can be set to an * EventList that provides messages to be displayed while the player * character is within this object. * * By default, if our container is visible to us, we'll use our * container's atmospheric messages. This can be overridden to * provide our own atmosphere list when the player character is in * this object. */ atmosphereList() { if (location != nil && gPlayerChar.canSee(location)) return location.atmosphereList; else return nil; } /* * Get the notification list actions performed by actors within this * object. Ordinary objects don't have room notification lists, but * we might be part of a nested set of objects that includes rooms, * so simply look to our container for the notification list. */ getRoomNotifyList() { local lst = []; /* get the notification lists for our immediate containers */ forEachContainer( {cont: lst = lst.appendUnique(cont.getRoomNotifyList())}); /* return the result */ return lst; } /* * Are we aboard a ship? By default, we'll return our location's * setting for this property; if we have no location, we'll return * nil. In general, this should be overridden in shipboard rooms to * return true. * * The purpose of this property is to indicate to the travel system * that shipboard directions (fore, aft, port, starboard) make sense * here. When this returns true, and an actor attempts travel in a * shipboard direction that doesn't allow travel here, we'll use the * standard noTravel message. When this returns nil, attempting to * move in a shipboard direction will show a different response that * indicates that such directions are not meaningful when not aboard * a ship of some kind. * * Note that we look to our location unconditionally - we don't * bother to check to see if we're open, transparent, or anything * like that, so we'll check with our location even if the actor * can't see the location. The idea is that, no matter how nested * within opaque containers we are, we should still know that we're * aboard a ship. This might not always be appropriate; if the * actor is magically transported into an opaque container that * happens to be aboard a ship somewhere, the actor probably * shouldn't think of the location as shipboard until escaping the * container, unless they'd know because of the rocking of the ship * or some other sensory factor that would come through the opaque * container. When the shipboard nature of an interior location * should not be known to the actor, this should simply be * overridden to return nil. */ isShipboard() { /* if we have a location, use its setting; otherwise return nil */ return (location != nil ? location.isShipboard() : nil); } /* * When an actor takes me, the actor will assign me a holding index * value, which is simply a serial number indicating the order in * which I was picked up. This lets the actor determine which items * have been held longest: the item with the lowest holding index * has been held the longest. */ holdingIndex = 0 /* * An object has "weight" and "bulk" specifying how heavy and how * large the object is. These are in arbitrary units, and by * default everything has a weight of 1 and a bulk of 1. */ weight = 1 bulk = 1 /* * Calculate the bulk contained within this object. By default, * we'll simply add up the bulks of all of our contents. */ getBulkWithin() { local total; /* add up the bulks of our contents */ total = 0; foreach (local cur in contents) total += cur.getBulk(); /* return the total */ return total; } /* * get my "destination name," for travel purposes; by default, we * simply defer to our location, if we have one */ getDestName(actor, origin) { if (location != nil) return location.getDestName(actor, origin); else return ''; } /* * Get the effective location of an actor directly within me, for the * purposes of a "follow" command. To follow someone, we must have * the same effective follow location that the target had when we * last observed the target leaving. * * For most objects, we simply defer to the location. */ effectiveFollowLocation = (location != nil ? location.effectiveFollowLocation : self) /* * Get the destination for an object dropped within me. Ordinary * objects can't contain actors, so an actor can't directly drop * something within a regular Thing, but the same effect could occur * if an actor throws a projectile, since the projectile might reach * either the target or an intermediate obstruction, then bounce off * and land in whatever contains the object hit. * * By default, objects dropped within us won't stay within us, but * will land in our container's drop destination. * * 'obj' is the object being dropped. In some cases, the drop * destination might differ according to the object; for example, if * the floor is a metal grating, a large object might land on the * grating when dropped, but a small object such as a coin might drop * through the grating to the room below. Note that 'obj' can be * nil, if the caller simply wants to determine the generic * destination where a *typical* object would land if dropped - this * routine must therefore not assume that 'obj' is non-nil. * * 'path' is the sense path (as returned by selectPathTo and the * like) traversed by the operation that's seeking the drop * destination. For ordinary single-location objects, the path is * irrelevant, but this information is sometimes useful for * multi-location objects to let them know which "side" we approached * them from. Note that the path can be nil, so this routine must * not depend upon a path being supplied; if the path is nil, the * routine should assume that the ordinary "touch" sense path from * the current actor to 'self' is to be used, because the actor is * effectively reaching out and placing an object on self. This * means that when calling this routine, you can supply a nil path * any time the actor is simply dropping an object. */ getDropDestination(obj, path) { /* * by default, the object lands in our container's drop * destination; if we don't have a container, drop it here */ return location != nil ? location.getDropDestination(obj, path) : self; } /* * Adjust a drop destination chosen in a THROW. This is called on * the object that was chosen as the preliminary landing location for * the thrown object, as calculated by getHitFallDestination(), to * give the preliminary destination a chance to redirect the landing * to another object. For example, if the preliminary destination * simply isn't a suitable surface or container where a projectile * could land, or it's not big enough to hold the projectile, the * preliminary destination could override this method to redirect the * landing to a more suitable object. * * By default, we don't need to make any adjustment, so we simply * return 'self' to indicate that this can be used as the actual * destination. */ adjustThrowDestination(thrownObj, path) { return self; } /* * Receive a dropped object. This is called when we are the actual * (not nominal) drop destination for an object being dropped, * thrown, or otherwise discarded. This routine is responsible for * carrying out the drop operation, and reporting the result of the * command. * * 'desc' is a "drop descriptor": an object of class DropType, which * describes how the object is being discarded. This can be a * DropTypeDrop for a DROP command, a DropTypeThrow for a THROW * command, or custom types defined by the game or by library * extensions. * * In most cases, the actual *result* of dropping the object will * always be the same for a given location, regardless of which * command initiated the drop, so this routine won't usually have to * look at 'desc' at all to figure out what happens. * * However, the *message* we generate does usually vary according to * the drop type, because this is the report for the entire command. * There are three main ways of handling this variation. * * - First, you can check what kind of descriptor it is (using * ofKind, for example), and generate a custom message for each kind * of descriptor. Be aware that any library extensions you're using * might have added new DropType subclasses. * * - Second, experienced programmers might prefer the arguably * cleaner OO "visitor" pattern: treat 'desc' as a visitor, calling a * single method that you define for your custom message. You'll * have to define that custom method in each DropType subclass, of * course. * * - Third, you can build a custom message by combining the standard * "message fragment" prefix provided by the drop descriptor with * your own suffix. The prefix will always be the start of a * sentence describing what the player did: "You drop the box", "The * ball hits the wall and bounces off", and so on. Since the * fragment always has the form of the start of a sentence, you can * always add your own suffix: ", and falls to the floor", for * example, or ", and falls into the chasm". Note that if you're * using one of the other approaches above, you'll probably want to * combine that approach with this one to handle cases where you * don't recognize the drop descriptor type. * * By default, we simply move the object into self and display the * standard message using the descriptor (we use the "visitor" * pattern to display that standard message). This can be overridden * in cases where it's necessary to move the object to a different * location, or to invoke a side effect. */ receiveDrop(obj, desc) { /* simply move the object into self */ obj.moveInto(self); /* generate the standard message */ desc.standardReport(obj, self); } /* * Get the nominal destination for an object dropped into this * object. This is used to report where an object ends up when the * object is moved into me by some indirect route, such as throwing * the object. * * By default, most objects simply return themselves, so we'll * report something like "obj lands {in/on} self". Some objects * might want to report a different object as the destination; for * example, an indoor room might want to report objects as falling * onto the floor. */ getNominalDropDestination() { return self; } /* * Announce myself as a default object for an action. By default, * we'll show the standard library message announcing a default. */ announceDefaultObject(whichObj, action, resolvedAllObjects) { /* use the standard library message for the announcement */ return gLibMessages.announceDefaultObject( self, whichObj, action, resolvedAllObjects); } /* * Calculate my total weight. Weight is generally inclusive of the * weights of contents or components, because anything inside an * object normally contributes to the object's total weight. */ getWeight() { local total; /* start with my own intrinsic weight */ total = weight; /* * add the weights of my direct contents, recursively * calculating their total weights */ foreach (local cur in contents) total += cur.getWeight(); /* return the total */ return total; } /* * Calculate my total bulk. Most objects have a fixed external * shape (and thus bulk) that doesn't vary as contents are added or * removed, so our default implementation simply returns our * intrinsic bulk without considering any contents. * * Some objects do change shape as contents are added. Such objects * can override this routine to provide the appropriate behavior. * (We don't try to provide a parameterized total bulk calculator * here because there are too many possible ways a container's bulk * could be affected by its contents or by other factors.) * * If an object's bulk depends on its contents, the object should * override notifyInsert() so that it calls checkBulkChange() - this * will ensure that an object won't suddenly become too large for * its container (or holding actor). */ getBulk() { /* by default, simply return my intrinsic bulk */ return bulk; } /* * Calculate the amount of bulk that this object contributes towards * encumbering an actor directly holding the item. By default, this * simply returns my total bulk. * * Some types of objects will override this to cause the object to * contribute more or less bulk to an actor holding the object. For * example, an article of clothing being worn by an actor typically * contributes no bulk at all to the actor's encumbrance, because an * actor wearing an item typically doesn't also have to hold on to * the item. Or, a small animal might encumber an actor more than * its normal bulk because it's squirming around trying to get free. */ getEncumberingBulk(actor) { /* by default, return our normal total bulk */ return getBulk(); } /* * Calculate the amount of weight that this object contributes * towards encumbering an actor directly holding the item. By * default, this simply returns my total weight. * * Note that we don't recursively sum the encumbering weights of our * contents - we simply sum the actual weights. We do it this way * because items within a container aren't being directly held by * the actor, so any difference between the encumbering and actual * weights should not apply, even though the actor is indirectly * holding the items. If a subclass does want the sum of the * encumbering weights, it should override this to make that * calculation. */ getEncumberingWeight(actor) { /* by default, return our normal total weight */ return getWeight(); } /* * "What if" test. Make the given changes temporarily, bypassing * any side effects that would normally be associated with the * changes; invokes the given callback; then remove the changes. * Returns the result of calling the callback function. * * The changes are expressed as pairs of argument values. The first * value in a pair is a property, and the second is a new value for * the property. For each pair, we'll set the given property to the * given value. The setting is direct - we don't invoke any method, * because we don't want to cause any side effects at this point; * we're interested only in what the world would look like if the * given changes were to go into effect. * * A special property value of 'moveInto' can be used to indicate * that the object should be moved into another object for the test. * In this case, the second element of the pair is not a value to be * stored into the moveInto property, but instead the value is a new * location for the object. We'll call the baseMoveInto method to * move the object to the given new location. * * In any case, after making the changes, we'll invoke the given * callback function, which we'll call with no arguments. * * Finally, on our way out, we'll restore the properties we changed * to their original values. We once again do this without any side * effects. Note that we restore the old values even if we exit * with an exception. */ whatIf(func, [changes]) { local oldList; local cnt; local i; /* * Allocate a vector with one slot per change, so that we have a * place to store the old values of the properties we're * changing. Note that the argument list has two entries (prop, * value) for each change. */ cnt = changes.length(); oldList = new Vector(cnt / 2); /* run through the change list and make each change */ for (i = 1 ; i <= cnt ; i += 2) { local curProp; local newVal; /* get the current property and new value */ curProp = changes[i]; newVal = changes[i+1]; /* see what we have */ switch(curProp) { case &moveInto: /* * It's the special moveInto property. This indicates * that we are to move self into the given new * location. First save the current location, then * use baseMoveInto to make the change without * triggering any side effects. Note that if this * would create circular containment, we won't make * the move. */ oldList.append(saveLocation()); if (newVal != self && !newVal.isIn(self)) baseMoveInto(newVal); break; default: /* * Anything else is a property to which we want to * assign the given new value. Remember the old value * in our original values vector, then set the new value * from the argument. */ oldList.append(self.(curProp)); self.(curProp) = newVal; break; } } /* * the changes are now in effect - invoke the given callback * function to check things out under the new conditions, but * protect the code so that we are sure to restore things on the * way out */ try { /* invoke the given callback, returning the result */ return (func)(); } finally { /* run through the change list and undo each change */ for (i = 1, local j = 1 ; i <= cnt ; i += 2, ++j) { local curProp; local oldVal; /* get the current property and old value */ curProp = changes[i]; oldVal = oldList[j]; /* see what we have */ switch(curProp) { case &moveInto: /* restore the location that we saved */ restoreLocation(oldVal); break; default: /* restore the property value */ self.(curProp) = oldVal; break; } } } } /* * Run a what-if test to see what would happen if this object were * being held directly by the given actor. */ whatIfHeldBy(func, actor) { /* * by default, simply run the what-if test with this object * moved into the actor's inventory */ return whatIf(func, &moveInto, actor); } /* * Check a proposed change in my bulk. When this is called, the new * bulk should already be in effect (the best way to do this when * just making a check is via whatIf). * * This routine can be called during the 'check' or 'action' stages * of processing a command to determine if a change in my bulk would * cause a problem. If so, we'll add a failure report and exit the * command. * * By default, notify our immediate container of the change to see * if there's any objection. A change in an object's bulk typically * only aaffects its container or containers. * * The usual way to invoke this routine in a 'check' or 'action' * routine is with something like this: * * whatIf({: checkBulkChange()}, &inflated, true); * * This checks to see if the change in my bulk implied by changing * self.inflated to true would present a problem for my container, * terminating with a reportFailure+exit if so. */ checkBulkChange() { /* * Notify each of our containers of a change in our bulk; if * any container has a problem with our new bulk, it can * report a failure and exit. */ forEachContainer( {loc: loc.checkBulkChangeWithin(self)}); } /* * Check a bulk change of one of my direct contents, given by obj. * When this is called, 'obj' will be (tentatively) set to reflect * its proposed new bulk; if this routine doesn't like the new bulk, * it should issue a failure report and exit the command, which will * cancel the command that would have caused the change and will * prevent the proposed change from taking effect. * * By default, we'll do nothing; subclasses that are sensitive to * the bulks of their contents should override this. */ checkBulkChangeWithin(changingObj) { } /* * Get "bag of holding" affinities for the given list of objects. * For each item in the list, we'll get the item's bulk, and get * each bag's affinity for containing the item. We'll note the bag * with the highest affinity. Once we have the list, we'll put it * in descending order of affinity, and return the result as a * vector. */ getBagAffinities(lst) { local infoVec; local bagVec; /* * First, build a list of all of the bags of holding within me. * We start with the list of bags because we want to check each * object to see which bag we might want to put it in. */ bagVec = new Vector(10); getBagsOfHolding(bagVec); /* if there are no bags of holding, there will be no affinities */ if (bagVec.length() == 0) return []; /* create a vector to hold information on each child item */ infoVec = new Vector(lst.length()); /* look at each child item */ foreach (local cur in lst) { local maxAff; local bestBag; /* find the bag with the highest affinity for this item */ maxAff = 0; bestBag = nil; foreach (local bag in bagVec) { local aff; /* get this bag's affinity for this item */ aff = bag.affinityFor(cur); /* * If this is the best so far, note it. If this bag has * an affinity of zero for this item, it means it * doesn't want it at all, so don't even consider it in * this case. */ if (aff != 0 && (bestBag == nil || aff > maxAff)) { /* this is the best so far - note it */ maxAff = aff; bestBag = bag; } } /* * if we found a bag that wants this item, add it to our * list of results */ if (bestBag != nil) infoVec.append(new BagAffinityInfo( cur, cur.getEncumberingBulk(self), maxAff, bestBag)); } /* sort the list in descending order of affinity */ infoVec = infoVec.sort(SortDesc, {a, b: a.compareAffinityTo(b)}); /* * Go through the list and ensure that any item in the list * that's destined to go inside another item that's also in the * list gets put in the other item before the second item gets * put in its bag. For example, if we have a credit card that * goes in a wallet, and a wallet that goes in a pocket, we want * to ensure that we put the credit card in the wallet before we * put the wallet in the pocket. * * The point of this reordering is that, in some cases, we might * not be able to move the first item once the second item has * been moved. In our credit-card/wallet/pocket example, the * wallet might have to be closed to go in the pocket, at which * point we'd have to take it out again to put the credit card * into it, defeating the purpose of having put it away in the * first place. We avoid these kinds of circular traps by * making sure the list is ordered such that we dispose of the * credit card first, then the wallet. * * To avoid looping forever in cases of circular containment, * don't allow any more moves than there are items in the list. */ for (local i = 1, local len = infoVec.length(), local moves = 0 ; i <= len && moves <= len ; ++i) { /* get this item and its destination */ local cur = infoVec[i]; local dest = cur.bag_; /* * check to see if the destination itself appears in the * list as an item to be bagged */ local idx = infoVec.indexWhich({x: x.obj_ == dest}); /* * if the destination item appears in the list before the * current item, move the current item before its * destination */ if (idx != nil && idx < i) { /* remove the current item from the list */ infoVec.removeElementAt(i); /* re-insert it just before its destination item */ infoVec.insertAt(idx, cur); /* count the move */ ++moves; /* * Now, back up and resume the scan from the item just * after the item that moves our destination bag. This * will ensure that any items destined to go in the bag * we just moved are caught. (Although note that we * start just after the destination item, rather than at * the destination item, because the destination item * can't be legitimately moved at this point: if it is, * it's because we have a circular containment plan. We * obviously can't resolve circular containment into a * linear ordering, so there's no point in even looking * at the destination item again at this point. If we * have a circular chain that's several elements long, * this won't avoid getting stuck; for that, we have to * count on our 'moves' limit.) */ i = idx + 1; } } /* return the result */ return infoVec; } /* * Find all of the bags of holding contained within this object and * add them to the given vector. By default, we'll simply recurse * into our children so they can add their bags of holding. */ getBagsOfHolding(vec) { /* * if my contents can't be touched from outside, there's no * point in traversing our children */ if (transSensingIn(touch) != transparent) return; /* add each of my children's bags to the list */ foreach (local cur in contents) cur.getBagsOfHolding(vec); } /* * The strength of the light the object is giving off, if indeed it * is giving off light. This value should be one of the following: * * 0: The object is giving off no light at all. * * 1: The object is self-illuminating, but doesn't give off enough * light to illuminate any other objects. This is suitable for * something like an LED digital clock. * * 2: The object gives off dim light. This level is bright enough to * illuminate nearby objects, but not enough to go through obscuring * media, and not enough for certain activities requiring strong * lighting, such as reading. * * 3: The object gives off medium light. This level is bright enough * to illuminate nearby objects, and is enough for most activities, * including reading and the like. Traveling through an obscuring * medium reduces this level to dim (2). * * 4: The object gives off strong light. This level is bright enough * to illuminate nearby objects, and travel through an obscuring * medium reduces it to medium light (3). * * Note that the special value -1 is reserved as an invalid level, * used to flag certain events (such as the need to recalculate the * ambient light level from a new point of view). * * Most objects do not give off light at all. */ brightness = 0 /* * Sense sizes of the object. Each object has an individual size for * each sense. By default, objects are medium for all senses; this * allows them to be sensed from a distance or through an obscuring * medium, but doesn't allow their details to be sensed. */ sightSize = medium soundSize = medium smellSize = medium touchSize = medium /* * Determine whether or not the object has a "presence" in each * sense. An object has a presence in a sense if an actor * immediately adjacent to the object could detect the object by the * sense alone. For example, an object has a "hearing presence" if * it is making some kind of noise, and does not if it is silent. * * Presence in a given sense is an intrinsic (which does not imply * unchanging) property of the object, in that presence is * independent of the relationship to any given actor. If an alarm * clock is ringing, it has a hearing presence, unconditionally; it * doesn't matter if the alarm clock is sealed inside a sound-proof * box, because whether or not a given actor has a sense path to the * object is a matter for a different computation. * * Note that presence doesn't control access: an actor might have * access to an object for a sense even if the object has no * presence in the sense. Presence indicates whether or not the * object is actively emitting sensory data that would make an actor * aware of the object without specifically trying to apply the * sense to the object. * * By default, an object is visible and touchable, but does not emit * any sound or odor. */ sightPresence = true soundPresence = nil smellPresence = nil touchPresence = true /* * My "contents lister." This is a Lister object that we use to * display the contents of this object for room descriptions, * inventories, and the like. */ contentsLister = thingContentsLister /* * the Lister to use when showing my contents as part of my own * description (i.e., for Examine commands) */ descContentsLister = thingDescContentsLister /* * the Lister to use when showing my contents in response to a * LookIn command */ lookInLister = thingLookInLister /* * my "in-line" contents lister - this is the Lister object that * we'll use to display my contents parenthetically as part of my * list entry in a second-level contents listing */ inlineContentsLister = inlineListingContentsLister /* * My "special" contents lister. This is the Lister to use to * display the special descriptions for objects that have special * descriptions when we're showing a room description for this * object. */ specialContentsLister = specialDescLister /* * Determine if I can be sensed under the given conditions. Returns * true if the object can be sensed, nil if not. If this method * returns nil, this object will not be considered in scope for the * current conditions. * * By default, we return nil if the ambient energy level for the * object is zero. If the ambient level is non-zero, we'll return * true in 'transparent' conditions, nil for 'opaque', and we'll let * the sense decide via its canObjBeSensed() method for any other * transparency conditions. Note that 'ambient' as given here is the * ambient level *at the object*, not as seen along my sense path - * so this should NOT be given as the ambient value from a SenseInfo, * which has already been adjusted for the sense path. */ canBeSensed(sense, trans, ambient) { /* * adjust the ambient level for the transparency path, if the * sense uses ambience at all */ if (sense.ambienceProp != nil) { /* * adjust the ambient level for the transparency - if that * leaves a level of zero, the object can't be sensed */ if (adjustBrightness(ambient, trans) == 0) return nil; } /* check the viewing conditions */ switch(trans) { case transparent: case attenuated: /* * under transparent or attenuated conditions, I appear as * myself */ return true; case obscured: case distant: /* * ask the sense to determine if I can be sensed under these * conditions */ return sense.canObjBeSensed(self, trans, ambient); default: /* for any other conditions, I can't be sensed at all */ return nil; } } /* * Determine if I can be sensed IN DETAIL in the given sense, with * the given SenseInfo description. By default, an object's details * can be sensed if the sense path is 'transparent' or 'attenuated', * OR the object's scale in the sense is 'large'. */ canDetailsBeSensed(sense, info, pov) { /* if the sense path is opaque, we can be sensed */ if (info == nil || info.trans == opaque) return nil; /* * if we have 'large' scale in the sense, our details can be * sensed under any conditions */ if (self.(sense.sizeProp) == large) return true; /* * we don't have 'large' scale in the sense, so we can be sensed * in detail only if the sense path is 'transparent' or * 'attenuated' */ return (info.trans is in (transparent, attenuated)); } /* * Call a method on this object from the given point of view. We'll * push the current point of view, call the method, then restore the * enclosing point of view. */ fromPOV(actor, pov, propToCall, [args]) { /* push the new point of view */ pushPOV(actor, pov); /* make sure we pop the point of view no matter how we leave */ try { /* call the method */ self.(propToCall)(args...); } finally { /* restore the enclosing point of view on the way out */ popPOV(); } } /* * Every Thing has a location, which is the Thing that contains this * object. A Thing's location can only be a simple object * reference, or nil; it cannot be a list, and it cannot be a method. * * If the location is nil, the object does not exist anywhere in the * simulation's physical model. A nil location can be used to * remove an object from the game world, temporarily or permanently. * * In general, the 'location' property should be declared for each * statically defined object (explicitly or implicitly via the '+' * syntax). 'location' is a private property - it should never be * evaluated or changed by any subclass or by any other object. * Only Thing methods may evaluate or change the 'location' * property. So, you can declare a 'location' property when * defining an object, but you should essentially never refer to * 'location' directly in any other context; instead, use the * location and containment methods (isIn, etc) when you want to * know an object's containment relationship to another object. */ location = nil /* * Get the direct container we have in common with the given object, * if any. Returns at most one common container. Returns nil if * there is no common location. */ getCommonDirectContainer(obj) { /* we haven't found one yet */ local found = nil; /* scan each of our containers for one in common with 'loc' */ forEachContainer(function(loc) { /* * If this location of ours is a direct container of the * other object, it is a common location, so note it. */ if (obj.isDirectlyIn(loc)) found = loc; }); /* return what we found */ return found; } /* * Get the container (direct or indirect) we have in common with the * object, if any. */ getCommonContainer(obj) { /* we haven't found one yet */ local found = nil; /* scan each of our containers for one in common with 'loc' */ forEachContainer(function(loc) { /* * If this location of ours is a direct container of the * other object, it is a common location, so note it. */ if (obj.isIn(loc)) found = loc; }); /* if we found a common container, return it */ if (found != nil) return found; /* * we didn't find obj's container among our direct containers, * so try our direct container's containers */ forEachContainer(function(loc) { local cur; /* try finding for a common container of this container */ cur = loc.getCommonContainer(obj); /* if we found it, note it */ if (cur != nil) found = cur; }); /* return what we found */ return found; } /* * Get our "identity" object. This is the object that we appear to * be an inseparable part of. * * In most cases, an object is its own identity object. However, * there are times when the object that a player sees isn't the same * as the object that the parser resolves, because we're modeling a * single physical object with several programming objects. For * example, a complex container has one or more secret internal * program objects representing the different sub-containers; as far * as the player is concerned, all of the sub-containers are just * aspects of the parent complex container, not separate object, so * the sub-containers all have the identity of the parent complex * container. * * By default, this is simply 'self'. Objects that take their * effective identities from other objects must override this * accordingly. */ getIdentityObject() { return self; } /* * Am I a component of the given object? The base Thing is not a * component of anything, so we'll simply return nil. */ isComponentOf(obj) { return nil; } /* * General initialization - this will be called during preinit so * that we can set up the initial values of any derived internal * properties. */ initializeThing() { /* initialize our location settings */ initializeLocation(); /* * if we're marked 'equivalent', it means we're interchangeable * in parser input with other objects with the same name; set up * our equivalence group listing if so */ if (isEquivalent) initializeEquivalent(); /* if we have a global parameter name, add it to the global table */ if (globalParamName != nil) langMessageBuilder.nameTable_[globalParamName] = self; } /* * Initialize my location's contents list - add myself to my * container during initialization */ initializeLocation() { if (location != nil) location.addToContents(self); } /* * Initialize this class object for listing its instances that are * marked with isEquivalent. We'll initialize a list group that * lists our equivalent instances as a group. * * Objects are grouped by their equivalence key - each set of objects * with the same key is part of the same group. The key is * determined by the language module, but is usually just the basic * disambiguation name (the 'disambigName' property in English, for * example). */ initializeEquivalent() { /* * If we're already in an equivalence group (because our key has * changed, for example, or due to inheritance), remove the * existing group from the group list. If the group isn't * changing after all, we'll just add it back, so removing it * should be harmless. */ if (equivalentGrouper != nil) listWith -= equivalentGrouper; /* look up our equivalence key in the global table of groupers */ equivalentGrouper = equivalentGrouperTable[equivalenceKey]; /* if there's no grouper for our key, create one */ if (equivalentGrouper == nil) { /* create a new grouper */ equivalentGrouper = equivalentGrouperClass.createInstance(); /* add it to the table */ equivalentGrouperTable[equivalenceKey] = equivalentGrouper; } /* * Add it to our list, as long as our listWith isn't already * defined as a method. Note that we intentionally add it as the * last element, because an equivalent group is the most specific * kind of group possible. */ if (propType(&listWith) != TypeCode) listWith += equivalentGrouper; } /* * A static game-wide table of equivalence groups. This has a table * of ListGroupEquivalent-derived objects, keyed by equivalence name. * Each group of objects with the same equivalence name is listed in * the same group and so has the same grouper object. */ equivalentGrouperTable = static (new LookupTable(32, 64)) /* * my equivalence grouper class - when we initialize, we'll create a * grouper of this class and store it in equivalentGrouper */ equivalentGrouperClass = ListGroupEquivalent /* * Our equivalent item grouper. During initialization, we will * create an equivalent grouper and store it in this property for * each class object that has instances marked with isEquivalent. * Note that this is stored with the class, because we want each of * our equivalent instances to share the same grouper object so that * they are listed together as a group. */ equivalentGrouper = nil /* * My contents. This is a list of the objects that this object * directly contains. */ contents = [] /* * Get my associated noise object. By default, this looks for an * item of class Noise directly within me. */ getNoise() { /* * Look for a Noise object among my direct contents. Only look * for a noise with an active sound presence. */ return contents.valWhich( {obj: obj.ofKind(Noise) && obj.soundPresence}); } /* * Get my associated odor object. By default, this looks for an * item of class Odor directly within me. */ getOdor() { /* * Look for an Odor object among my direct contents. Only look * for an odor with an active smell presence. */ return contents.valWhich( {obj: obj.ofKind(Odor) && obj.smellPresence}); } /* * Get a vector of all of my contents, recursively including * contents of contents. */ allContents() { local vec; /* start with an empty vector */ vec = new Vector(32); /* add all of my contents to the vector */ addAllContents(vec); /* return the result */ return vec; } /* * Get a list of objects suitable for matching ALL in TAKE ALL FROM * . By default, this simply returns the list of everything in * scope that's directly inside 'self'. */ getAllForTakeFrom(scopeList) { /* * include only objects contained within 'self' that aren't * components of 'self' */ return scopeList.subset( {x: x != self && x.isDirectlyIn(self) && !x.isComponentOf(self)}); } /* * add myself and all of my contents, recursively including contents * of contents, to a vector */ addAllContents(vec) { /* visit everything in my contents */ foreach (local cur in contents) { /* * if this item is already in the vector, skip it - we've * already visited it and its contents */ if (vec.indexOf(cur) != nil) continue; /* add this item */ vec.append(cur); /* add this item's contents recursively */ cur.addAllContents(vec); } } /* * Show the contents of this object, as part of a recursive listing * generated as part of the description of our container, our * container's container, or any further enclosing container. * * If the object has any contents, we'll display a listing of the * contents. This is used to display the object's contents as part * of the description of a room ("look around"), of an object * ("examine box"), or of an object's contents ("look in box"). * * 'options' is the set of flags that we'll pass to showList(), and * has the same meaning as for that function. * * 'infoTab' is a lookup table of SenseInfo objects for the objects * that the actor to whom we're showing the contents listing can see * via the sight-like senses. * * This method should be overridden by any object that doesn't store * its contents using a simple 'contents' list property. */ showObjectContents(pov, lister, options, indent, infoTab) { /* get my listable contents */ local cont = lister.getListedContents(self, infoTab); /* if the surviving list isn't empty, show it */ if (cont != []) lister.showList(pov, self, cont, options, indent, infoTab, nil); } /* * Show the contents of this object as part of an inventory listing. * By default, we simply use the same listing we do for the normal * contents listing. */ showInventoryContents(pov, lister, options, indent, infoTab) { /* by default, use the normal room/object contents listing */ showObjectContents(pov, lister, options, indent, infoTab); } /* * Get my listed contents for recursive object descriptions. This * returns the list of the direct contents that we'll mention when * we're examining our container's container, a further enclosing * container, or the enclosing room. * * By default, we'll return the same list we'll display on direct * examination of this object. */ getListedContents(lister, infoTab) { /* * return the contents we'd list when directly examining self, * limited to the listable contents */ return getContentsForExamine(lister, infoTab) .subset({x: lister.isListed(x)}); } /* * Get the listed contents in a direct examination of this object. * By default, this simply returns a list of all of our direct * contents that are included in the info table. */ getContentsForExamine(lister, infoTab) { /* * return only my direct contents that are found in the infoTab, * since these are the objects that can be sensed from the * actor's point of view */ return contents.subset({x: infoTab[x] != nil}); } /* * Is this a "top-level" location? A top-level location is an * object which doesn't have another container, so its 'location' * property is nil, but which is part of the game universe anyway. * In most cases, a top-level location is simply a Room, since the * network of rooms makes up the game's map. * * If an object has no location and is not itself a top-level * location, then the object is not part of the game world. It's * sometimes useful to remove objects from the game world, such as * when they're destroyed within the context of the game. */ isTopLevel = nil /* * Determine if I'm is inside another Thing. Returns true if this * object is contained within 'obj', directly or indirectly (that * is, this returns true if my immediate container is 'obj', OR my * immediate container is in 'obj', recursively defined). * * isIn(nil) returns true if this object is "outside" the game * world, which means that the object is not reachable from anywhere * in the game map and is thus not part of the simulation. This is * the case if our outermost container is NOT a top-level object, as * indicated by its isTopLevel property. If we're inside an object * marked as a top-level object, or we're inside an object that's * inside a top-level object (and so on), then we're part of the * game world, so isIn(nil) will return nil. If our outermost * container is has a nil isTopLevel property, isIn(nil) will return * true. * * Note that our notion of "in" is not limited to enclosing * containment, because the same containment hierarchy is used to * represent all types of containment relationships, including * things being "on" other things and part of other things. */ isIn(obj) { local loc = location; /* if we have no location, we're not inside any object */ if (loc == nil) { /* * We have no container, so there are two possibilities: * either we're not part of the game world at all, or we're * a top-level object, which is an object that's explicitly * part of the game world and at the top of the containment * tree. Our 'isTopLevel' property determines which it is. * * If they're asking us if we're inside 'nil', then what * they want to know is if we're outside the game world. If * we're not a top-level object, we are outside the game * world, so isIn(nil) is true; if we are a top-level * object, then we're explicitly part of the game world, so * isIn(nil) is false. * * If they're asking us if we're inside any non-nil object, * then they simply want to know if we're inside that * object. So, if 'obj' is not nil, we must return nil: * we're not in any object, so we can't be in 'obj'. */ if (obj == nil) { /* * they want to know if we're outside the simulation * entirely: return true if we're NOT a top-level * object, nil otherwise */ return !isTopLevel; } else { /* * they want to know if we're inside some specific * object; we can't be, because we're not in any object */ return nil; } } /* if obj is my immediate container, I'm obviously in it */ if (loc == obj) return true; /* I'm in obj if my container is in obj */ return loc.isIn(obj); } /* * Determine if I'm directly inside another Thing. Returns true if * this object is contained directly within obj. Returns nil if * this object isn't directly within obj, even if it is indirectly * in obj (i.e., its container is directly or indirectly in obj). */ isDirectlyIn(obj) { /* I'm directly in obj only if it's my immediate container */ return location == obj; } /* * Determine if I'm "nominally" inside the given Thing. Returns true * if the object is actually within the given object, OR the object * is a room part and I'm nominally in the room part. */ isNominallyIn(obj) { /* if I'm actually in the given object, I'm nominally in it as well */ if (isIn(obj)) return true; /* * if the object is a room part, and we're nominally in the room * part, then we're nominally in the object */ if (obj.ofKind(RoomPart) && isNominallyInRoomPart(obj)) return true; /* we're not nominally in the object */ return nil; } /* * Determine if this object is contained within an item fixed in * place within the given location. We'll check our container to * see if its contents are within an object fixed in place in the * given location. * * This is a rather specific check that might seem a bit odd, but * for some purposes it's useful to treat objects within fixed * containers in a location as though they were in the location * itself, because fixtures of a location are to some extent parts * of the location. */ isInFixedIn(loc) { /* return true if my location's contents are in fixed things */ return location != nil && location.contentsInFixedIn(loc); } /* Am I either inside 'obj', or equal to 'obj'? */ isOrIsIn(obj) { return self == obj || isIn(obj); } /* * Are my contents within a fixed item that is within the given * location? By default, we return nil because we are not ourselves * fixed. */ contentsInFixedIn(loc) { return nil; } /* * Determine if I'm "held" by an actor, for the purposes of being * manipulated in an action. In most cases, an object is considered * held by an actor if it's directly within the actor's inventory, * because the actor's direct inventory represents the contents of * the actors hands (or equivalent). * * Some classes might override this to change the definition of * "held" to include things not directly in the actor's inventory or * exclude things directly in the inventory. For example, an item * being worn is generally not considered held even though it might * be in the direct inventory, and a key on a keyring is considered * held if the keyring is being held. */ isHeldBy(actor) { /* * by default, an object is held if and only if it's in the * direct inventory of the actor */ return isDirectlyIn(actor); } /* * Are we being held by the given actor for the purposes of the * objHeld precondition? By default, and in almost all cases, this * simply returns isHeldBy(). In some rare cases, it might make * sense to consider an object to meet the objHeld condition even if * isHeldBy returns nil; for example, an actor's hands and other * body parts can't be considered to be held, but they also don't * need to be for any command operating on them. */ meetsObjHeld(actor) { return isHeldBy(actor); } /* * Add to a vector all of my contents that are directly held when * I'm being directly held. This is used to add the direct contents * of an item to scope when the item itself is being directly held. * * In most cases, we do nothing. Certain types of objects override * this because they consider their contents to be held if they're * held. For example, a keyring considers all of its keys to be * held if the keyring itself is held, because the keys are attached * to the keyring rather than contained within it. */ appendHeldContents(vec) { /* by default, do nothing */ } /* * Determine if I'm "owned" by another object. By default, if we * have an explicit owner, then we are owned by 'obj' if and only if * 'obj' is our explicit owner; otherwise, if 'obj' is our immediate * location, and our immediate location can own us (as reported by * obj.canOwn(self)), then 'obj' owns us; otherwise, 'obj' owns us * if our immediate location CANNOT own us AND our immediate * location is owned by 'obj'. This last case is tricky: it means * that if we're inside something other than 'obj' that can own us, * such as another actor, then 'obj' doesn't own us because our * immediate location does; it also means that if we're inside an * object that has an explicit owner rather than an owner based on * location, we have the same explicit owner, so a dollar bill * inside Bob's wallet which is in turn being carried by Charlie is * owned by Bob, not Charlie. * * This is used to determine ownership for the purpose of * possessives in commands. Ownership is not always exclusive: it * is possible for a given object to have multiple owners in some * cases. For example, if Bob and Bill are both sitting on a couch, * the couch could be referred to as "bob's couch" or "bill's * couch", so the couch is owned by both Bob and Bill. It is also * possible for an object to be unowned. * * In most cases, ownership is a function of location (possession is * nine-tenths of the law, as they say), but not always; in some * cases, an object has a particular owner regardless of its * location, such as "bob's wallet". This default implementation * allows for ownership by location, as well as explicit ownership, * with explicit ownership (as indicated by the self.owner property) * taking precedence. */ isOwnedBy(obj) { local cont; /* * if I have an explicit owner, then obj is my owner if it * matches my explicit owner */ if (owner != nil) return owner == obj; /* * Check my immediate container to see if it's the owner in * question. */ if (isDirectlyIn(obj)) { /* * My immediate container is the owner of interest. If this * container can own me, then I'm owned by it because we * didn't find any other owners first. Otherwise, I'm not * owned by it because it can't own me in the first place. */ return obj.canOwn(self); } /* presume we have a single-location container */ cont = location; /* check to see if we have no single location */ if (cont == nil) { /* * I have no location, so either I'm not inside anything at * all, or I have multiple locations. If we're indirectly * in 'obj', then proceed up the containment tree branch by * which 'obj' contains us. If we're not even indirectly in * 'obj', then there's no containment relationship that * establishes ownership. */ if (isIn(obj)) { /* * we're indirectly in 'obj', so get the containment * branch on which we're contained by 'obj' */ forEachContainer(function(x) { if (x == obj || x.isIn(obj)) cont = x; }); } else { /* * we're not in 'obj', so there's no containment * relationship that establishes ownership */ return nil; } } /* * My immediate container is not the object of interest. If * this container can own me, then I'm owned by this container * and NOT by obj. */ if (cont.canOwn(self)) return nil; /* * My container can't own me, so it's not my owner, so our owner * is my container's owner - thus, I'm owned by obj if my * location is owned by obj. */ return cont.isOwnedBy(obj); } /* * My explicit owner. By default, objects do not have explicit * owners, which means that the owner at any given time is * determined by the object's location - my innermost container that * can own me is my owner. * * However, in some cases, an object is inherently owned by a * particular other object (usually an actor), and this is invariant * even when the object moves to a new location not within the * owner. For such cases, this property can be set to the explicit * owner object, which will cause self.isOwnedBy(obj) to return true * if and only if obj == self.owner. */ owner = nil /* * Get the "nominal owner" of this object. This is the owner that * we report for the object if asked to distinguish this object from * another via the OwnershipDistinguisher. Note that the nominal * owner is not necessarily the only owner, because an object can * have multiple owners in some cases; however, the nominal owner * must always be an owner, in that isOwnedBy(getNominalOwner()) * should always return true. * * By default, if we have an explicit owner, we'll return that. * Otherwise, if our immediate container can own us, we'll return * our immediate container. Otherwise, we'll return our immediate * container's nominal owner. Note that this last case means that a * dollar bill inside Bob's wallet will be Bob's dollar bill, even * if Bob's wallet is currently being carried by another actor. */ getNominalOwner() { /* if we have an explicit owner, return that */ if (owner != nil) return owner; /* if we have no location, we have no owner */ if (location == nil) return nil; /* if our immediate location can own us, return it */ if (location.canOwn(self)) return location; /* return our immediate location's owner */ return location.getNominalOwner(); } /* * Can I own the given object? By default, objects cannot own other * objects. This can be overridden when ownership is desired. * * This doesn't determine that we *do* own the given object, but * only that we *can* own the given object. */ canOwn(obj) { return nil; } /* * Get the carrying actor. This is the nearest enclosing location * that's an actor. */ getCarryingActor() { /* if I don't have a location, there's no carrier */ if (location == nil) return nil; /* if my location is an actor, it's the carrying actor */ if (location.isActor) return location; /* return my location's carrying actor */ return location.getCarryingActor(); } /* * Try making the current command's actor hold me. By default, * we'll simply try a "take" command on the object. */ tryHolding() { /* * Try an implicit 'take' command. If the actor is carrying the * object indirectly, make the command "take from" instead, * since what we really want to do is take the object out of its * container. */ if (isIn(gActor)) return tryImplicitAction(TakeFrom, self, location); else return tryImplicitAction(Take, self); } /* * Try moving the given object into this object, with an implied * command. By default, since an ordinary Thing doesn't have a way * of adding new contents by a player command, this does nothing. * Containers and other objects that can hold new contents can * override this as appropriate. */ tryMovingObjInto(obj) { return nil; } /* * Report a failure of the condition that tryMovingObjInto tries to * bring into effect. By default, this simply says that the object * must be in 'self'. Some objects might want to override this when * they describe containment specially; for example, an actor might * want to say that the actor "must be carrying" the object. */ mustMoveObjInto(obj) { reportFailure(&mustBeInMsg, obj, self); } /* * Add an object to my contents. * * Note that this should NOT be overridden to cause side effects - * if side effects are desired when inserting a new object into my * contents, use notifyInsert(). This routine is not allowed to * cause side effects because it is sometimes necessary to bypass * side effects when moving an item. */ addToContents(obj) { /* add the object to my contents list */ if (contents.indexOf(obj) == nil) contents += obj; } /* * Remove an object from my contents. * * Do NOT override this routine to cause side effects. If side * effects are desired when removing an object, use notifyRemove(). */ removeFromContents(obj) { /* remove the object from my contents list */ contents -= obj; } /* * Save my location for later restoration. Returns a value suitable * for passing to restoreLocation. */ saveLocation() { /* * I'm an ordinary object with only one location, so simply * return the location */ return location; } /* * Restore a previously saved location. Does not trigger any side * effects. */ restoreLocation(oldLoc) { /* move myself without side effects into my old container */ baseMoveInto(oldLoc); } /* * Move this object to a new container. Before the move is actually * performed, we notify the items in the movement path of the * change, then we send notifyRemove and notifyInsert messages to * the old and new containment trees, respectively. * * All notifications are sent before the object is actually moved. * This means that the current game state at the time of the * notifications reflects the state before the move. */ moveInto(newContainer) { /* notify the path */ moveIntoNotifyPath(newContainer); /* perform the main moveInto operations */ mainMoveInto(newContainer); } /* * Move this object to a new container as part of travel. This is * almost the same as the regular moveInto(), but does not attempt * to calculate and notify the sense path to the new location. We * omit the path notification because travel is done via travel * connections, which are not necessarily the same as sense * connections. */ moveIntoForTravel(newContainer) { /* * perform the main moveInto operations, omitting the path * notification */ mainMoveInto(newContainer); } /* * Main moveInto - this is the mid-level containment changer; this * routine sends notifications to the old and new container, but * doesn't notify anything along the connecting sense path. */ mainMoveInto(newContainer) { /* notify my container that I'm being removed */ sendNotifyRemove(self, newContainer, ¬ifyRemove); /* notify my new container that I'm about to be added */ if (newContainer != nil) newContainer.sendNotifyInsert(self, newContainer, ¬ifyInsert); /* notify myself that I'm about to be moved */ notifyMoveInto(newContainer); /* perform the basic containment change */ baseMoveInto(newContainer); /* note that I've been moved */ moved = true; } /* * Base moveInto - this is the low-level containment changer; this * routine does not send any notifications to any containers, and * does not mark the object as moved. This form should be used only * for internal library-initiated state changes, since it bypasses * all of the normal side effects of moving an object to a new * container. */ baseMoveInto(newContainer) { /* if I have a container, remove myself from its contents list */ if (location != nil) location.removeFromContents(self); /* remember my new location */ location = newContainer; /* * if I'm not being moved into nil, add myself to the * container's contents */ if (location != nil) location.addToContents(self); } /* * Notify each element of the move path of a moveInto operation. */ moveIntoNotifyPath(newContainer) { local path; /* calculate the path; if there isn't one, there's nothing to do */ if ((path = getMovePathTo(newContainer)) == nil) return; /* * We must fix up the path's final element, depending on whether * we're moving into the new container from the outside or from * the inside. */ if (isIn(newContainer)) { /* * We're already in the new container, so we're moving into * the new container from the inside. The final element of * the path is the new container, but since we're stopping * within the new container, we don't need to traverse out * of it. Simply remove the final two elements of the path, * since we're not going to make this traversal when moving * the object. */ path = path.sublist(1, path.length() - 2); } else { /* * We're moving into the new container from the outside. * Since we calculated the path to the container, we must * now add a final element to traverse into the container; * the final object in the path doesn't matter, since it's * just a placeholder for the new item inside the container */ path += [PathIn, nil]; } /* traverse the path, sending a notification to each element */ traversePath(path, function(target, op) { /* notify this path element */ target.notifyMoveViaPath(self, newContainer, op); /* continue the traversal */ return true; }); } /* * Call a function on each container. If we have no location, we * won't invoke the function at all. We'll invoke the function as * follows: * * (func)(location, args...) */ forEachContainer(func, [args]) { /* call the function on our location, if we have one */ if (location != nil) (func)(location, args...); } /* * Call a function on each *connected* container. For most objects, * this is the same as forEachContainer, but objects that don't * connect their containers for sense purposes would do nothing * here. */ forEachConnectedContainer(func, [args]) { /* by default, use the standard forEachContainer handling */ forEachContainer(func, args...); } /* * Get a list of all of my connected containers. This simply returns * the list that forEachConnectedContainer() iterates over. */ getConnectedContainers() { local loc; /* * if I have a non-nil location, return a list containing it; * otherwise, simply return an empty list */ return ((loc = location) == nil ? [] : [loc]); } /* * Clone this object for inclusion in a MultiInstance's contents * tree. When we clone an instance object for a MultiInstance, we'll * also clone its contents, and their contents, and so on. This * routine creates a private copy of all of our contents. */ cloneMultiInstanceContents() { local origContents; /* * Remember my current contents list, then clear it out. We want * a private copy of everything in our contents list, so we want * to forget our references to the template contents and start * from scratch. */ origContents = contents; contents = []; /* clone each entry in our contents list */ foreach (local cur in origContents) cur.cloneForMultiInstanceContents(self); } /* * Create a clone for inclusion in MultiInstance contents. We'll * recursively clone our own contents. */ cloneForMultiInstanceContents(loc) { /* create a new instance of myself */ local cl = createInstance(); /* move it into the new location */ cl.baseMoveInto(loc); /* clone its contents */ cl.cloneMultiInstanceContents(); } /* * Determine if I'm a valid staging location for the given nested * room destination 'dest'. This is called when the actor is * attempting to enter the nested room 'dest', and the travel * handlers find that we're the staging location for the room. (A * "staging location" is the location the actor is required to * occupy immediately before moving into the destination.) * * If this object is a valid staging location, the routine should * simply do nothing. If this object isn't valid as a staging * location, this routine should display an appropriate message and * terminate the command with 'exit'. * * An arbitrary object can't be a staging location, simply because * an actor can't enter an arbitrary object. So, by default, we'll * explain that we can't enter this object. If the destination is * contained within us, we'll provide a more specific explanation * indicating that the problem is that the destination is within us. */ checkStagingLocation(dest) { /* * if the destination is within us, explain specifically that * this is the problem */ if (dest.isIn(self)) reportFailure(&invalidStagingContainerMsg, self, dest); else reportFailure(&invalidStagingLocationMsg, self); /* terminate the command */ exit; } /* * by default, objects don't accept commands */ acceptCommand(issuingActor) { /* report that we don't accept commands */ gLibMessages.cannotTalkTo(self, issuingActor); /* tell the caller we don't accept commands */ return nil; } /* * by default, most objects are not logical targets for commands */ isLikelyCommandTarget = nil /* * Get my extra scope items. This is a list of any items that we * want to add to command scope (i.e., the set of all items to which * an actor is allowed to refer with noun phrases) when we are * ourselves in the command scope. Returns a list of the items to * add (or just [] if there are no items to add). * * By default, we add nothing. */ getExtraScopeItems(actor) { return []; } /* * Generate a lookup table of all of the objects connected by * containment to this object. This table includes all containment * connections, even through closed containers and the like. * * The table is keyed by object; the associated values are * meaningless, as all that matters is whether or not an object is * in the table. */ connectionTable() { local tab; local cache; /* if we already have a cached connection list, return it */ if ((cache = libGlobal.connectionCache) != nil && (tab = cache[self]) != nil) return tab; /* remember the point of view globally, in case anyone needs it */ senseTmp.pointOfView = self; /* create a lookup table for the results */ tab = new LookupTable(32, 64); /* add everything connected to me */ addDirectConnections(tab); /* cache the list, if we caching is active */ if (cache != nil) cache[self] = tab; /* return the table */ return tab; } /* * Add this item and its direct containment connections to the lookup * table. This must recursively add all connected objects that * aren't already in the table. */ addDirectConnections(tab) { local cur; /* add myself */ tab[self] = true; /* * Add my CollectiveGroup objects, if any. We don't have to * traverse into the CollectiveGroup objects, since they don't * connect us or itself to anything else; our connection to a * collective group is one-way. */ foreach (cur in collectiveGroups) tab[cur] = true; /* * Add my contents to the table. Loop over our contents list, * and add each item that's not already in the table, then * recursively everything connected to that item. * * Note that we use a C-style 'for' loop to iterate over the list * by index, rather than a more modern tads-style 'foreach' loop, * purely for performance reasons: this code is called an awful * lot, and the iteration by loop index is very slightly faster. * A 'foreach' requires calling a couple of methods of an * iterator object each time through the loop, whereas we can do * the index-based 'for' loop entirely with cached local * variables. The performance difference is negligible for one * time through any given loop; it only matters to us here * because this routine tends to be invoked *extremely* * frequently (an average of roughly 500 times per turn in the * library sample game, for example). Game authors are strongly * advised to consider 'foreach' and 'for' equivalent in * performance for most purposes - just choose the clearest * construct for your situation. */ for (local clst = contents, local i = 1, local len = clst.length() ; i <= len ; ++i) { /* get the current item */ cur = clst[i]; /* if it's not already in the table, add it (recursively) */ if (tab[cur] == nil) cur.addDirectConnections(tab); } /* add my container if it's not already in the table */ if ((cur = location) != nil && tab[cur] == nil) cur.addDirectConnections(tab); } /* * Try an implicit action that would remove this object as an * obstructor to 'obj' from the perspective of the current actor in * the given sense. This is invoked when this object is acting as * an obstructor between the current actor and 'obj' for the given * sense, and the caller wants to perform a command that requires a * clear sense path to the given object in the given sense. * * If it is possible to perform an implicit command that would clear * the obstruction, try performing the command, and return true. * Otherwise, simply return nil. The usual implied command rules * should be followed (which can be accomplished simply by using * tryImplictAction() to execute any implied command). * * The particular type of command that would remove this obstructor * can vary by obstructor class. For a container, for example, an * "open" command is the usual remedy. */ tryImplicitRemoveObstructor(sense, obj) { /* by default, we have no way of clearing our obstruction */ return nil; } /* * Display a message explaining why we are obstructing a sense path * to the given object. */ cannotReachObject(obj) { /* * Default objects have no particular obstructive capabilities, * so we can't do anything but show the default message. This * is a last resort that should rarely be used; normally, we * will be able to identify a specific obstructor that overrides * this to explain precisely what kind of obstruction is * involved. */ gLibMessages.cannotReachObject(obj); } /* * Display a message explaining that the source of a sound cannot be * seen because I am visually obstructing it. By default, we show * nothing at all; subclasses can override this to provide a better * explanation when possible. */ cannotSeeSoundSource(obj) { } /* explain why we cannot see the source of an odor */ cannotSeeSmellSource(obj) { } /* * Get the path for this object reaching out and touching the given * object. This can be used to determine whether or not an actor * can touch the given object. */ getTouchPathTo(obj) { local path; local key = [self, obj]; local cache; local info; /* if we have a cache, try finding the data in the cache */ if ((cache = libGlobal.canTouchCache) != nil && (info = cache[key]) != nil) { /* we have a cache entry - return the path from the cache */ return info.touchPath; } /* select the path from here to the target object */ path = selectPathTo(obj, &canTouchViaPath); /* if caching is enabled, add a cache entry for the path */ if (cache != nil) cache[key] = new CanTouchInfo(path); /* return the path */ return path; } /* * Determine if I can touch the given object. By default, we can * always touch our immediate container; otherwise, we can touch * anything with a touch path that we can traverse. */ canTouch(obj) { local path; local result; local key = [self, obj]; local cache; local info; /* if we have a cache, check the cache for the desired data */ if ((cache = libGlobal.canTouchCache) != nil && (info = cache[key]) != nil) { /* if we cached the canTouch result, return it */ if (info.propDefined(&canTouch)) return info.canTouch; /* * we didn't calculate the canTouch result, but we at least * have a cached path that we can avoid recalculating */ path = info.touchPath; } else { /* we don't have a cached path, so calculate it anew */ path = getTouchPathTo(obj); } /* if there's no 'touch' path, we can't touch the object */ if (path == nil) { /* there's no path */ result = nil; } else { /* we have a path - check to see if we can reach along it */ result = traversePath(path, {ele, op: ele.canTouchViaPath(self, obj, op)}); } /* if caching is active, cache our result */ if (cache != nil) { /* if we don't already have a cache entry, create one */ if (info == nil) cache[key] = info = new CanTouchInfo(path); /* save our canTouch result in the cache entry */ info.canTouch = result; } /* return the result */ return result; } /* * Find the object that prevents us from touching the given object. */ findTouchObstructor(obj) { /* cache 'touch' sense path information */ cacheSenseInfo(connectionTable(), touch); /* return the opaque obstructor for the sense of touch */ return findOpaqueObstructor(touch, obj); } /* * Get the path for moving this object from its present location to * the given new container. */ getMovePathTo(newLoc) { /* * select the path from here to the new location, using the * canMoveViaPath method to discriminate among different path * possibilities. */ return selectPathTo(newLoc, &canMoveViaPath); } /* * Get the path for throwing this object from its present location * to the given target object. */ getThrowPathTo(newLoc) { /* * select the path from here to the target, using the * canThrowViaPath method to discriminate among different paths */ return selectPathTo(newLoc, &canThrowViaPath); } /* * Determine if we can traverse self for moving the given object in * the given manner. This is used to determine if a containment * connection path can be used to move an object to a new location. * Returns a CheckStatus object indicating success if we can * traverse self, failure if not. * * By default, we'll simply return a success indicator. Subclasses * might want to override this for particular conditions. For * example, containers would normally override this to return nil * when attempting to move an object in or out of a closed * container. Some special containers might also want to override * this to allow moving an object in or out only if the object is * below a certain size threshold, for example. * * 'obj' is the object being moved, and 'dest' is the destination of * the move. * * 'op' is one of the pathXxx operations - PathIn, PathOut, PathPeer * - specifying what kind of movement is being attempted. PathIn * indicates that we're moving 'obj' from outside self to inside * self; PathOut indicates the opposite. PathPeer indicates that * we're moving 'obj' entirely within self - this normally means * that we've moved the object out of one of our contents and will * move it into another of our contents. */ checkMoveViaPath(obj, dest, op) { return checkStatusSuccess; } /* * Determine if we can traverse this object for throwing the given * object in the given manner. By default, this returns the same * thing as canMoveViaPath, since throwing is in most cases the same * as ordinary movement. Objects can override this when throwing an * object through this path element should be treated differently * from ordinary movement. */ checkThrowViaPath(obj, dest, op) { return checkMoveViaPath(obj, dest, op); } /* * Determine if we can traverse self in the given manner for the * purposes of 'obj' touching another object. 'obj' is usually an * actor; this determines if 'obj' is allowed to reach through this * path element on the way to touching another object. * * By default, this returns the same thing as canMoveViaPath, since * touching is in most cases the same as ordinary movement of an * object from one location to another. Objects can overridet his * when touching an object through this path element should be * treated differently from moving an object. */ checkTouchViaPath(obj, dest, op) { return checkMoveViaPath(obj, dest, op); } /* * Determine if we can traverse this object for moving the given * object via a path. Calls checkMoveViaPath(), and returns true if * checkMoveViaPath() indicates success, nil if it indicates failure. * * Note that this method should generally not be overridden; only * checkMoveViaPath() should usually need to be overridden. */ canMoveViaPath(obj, dest, op) { return checkMoveViaPath(obj, dest, op).isSuccess; } /* determine if we can throw an object via this path */ canThrowViaPath(obj, dest, op) { return checkThrowViaPath(obj, dest, op).isSuccess; } /* determine if we can reach out and touch an object via this path */ canTouchViaPath(obj, dest, op) { return checkTouchViaPath(obj, dest, op).isSuccess; } /* * Check moving an object through this container via a path. This * method is called during moveInto to notify each element along a * move path that the movement is about to occur. We call * checkMoveViaPath(); if it indicates failure, we'll report the * failure encoded in the status object and terminate the command * with 'exit'. * * Note that this method should generally not be overridden; only * checkMoveViaPath() should usually need to be overridden. */ notifyMoveViaPath(obj, dest, op) { local stat; /* check the move */ stat = checkMoveViaPath(obj, dest, op); /* if it's a failure, report the failure message and terminate */ if (!stat.isSuccess) { /* report the failure */ reportFailure(stat.msgProp, stat.msgParams...); /* terminate the command */ exit; } } /* * Choose a path from this object to a given object. If no paths * are available, returns nil. If any paths exist, we'll find the * shortest usable one, calling the given property on each object in * the path to determine if the traversals are allowed. * * If we can find a path, but there are no good paths, we'll return * the shortest unusable path. This can be useful for explaining * why the traversal is impossible. */ selectPathTo(obj, traverseProp) { local allPaths; local goodPaths; local minPath; /* get the paths from here to the given object */ allPaths = getAllPathsTo(obj); /* if we found no paths, the answer is obvious */ if (allPaths.length() == 0) return nil; /* start off with an empty vector for the good paths */ goodPaths = new Vector(allPaths.length()); /* go through the paths and find the good ones */ for (local i = 1, local len = allPaths.length() ; i <= len ; ++i) { local path = allPaths[i]; local ok; /* * traverse the path, calling the traversal check property * on each point in the path; if any check property returns * nil, it means that the traversal isn't allowed at that * point, so we can't use the path */ ok = true; traversePath(path, function(target, op) { /* * Invoke the check property on this target. If it * doesn't allow the traversal, the path is unusable. */ if (target.(traverseProp)(self, obj, op)) { /* we're still okay - continue the path traversal */ return true; } else { /* failed - note that the path is no good */ ok = nil; /* there's no need to continue the path traversal */ return nil; } }); /* * if we didn't find any objections to the path, add this to * the list of good paths */ if (ok) goodPaths.append(path); } /* if there are no good paths, take the shortest bad path */ if (goodPaths.length() == 0) goodPaths = allPaths; /* find the shortest of the paths we're still considering */ minPath = nil; for (local i = 1, local len = goodPaths.length() ; i <= len ; ++i) { /* get the current path */ local path = goodPaths[i]; /* if this is the best so far, note it */ if (minPath == nil || path.length() < minPath.length()) minPath = path; } /* return the shortest good path */ return minPath; } /* * Traverse a containment connection path, calling the given * function for each element. In each call to the callback, 'obj' * is the container object being traversed, and 'op' is the * operation being used to traverse it. * * At each stage, the callback returns true to continue the * traversal, nil if we are to stop the traversal. * * Returns nil if any callback returns nil, true if all callbacks * return true. */ traversePath(path, func) { local len; local target; /* * if there's no path at all, there's nothing to do - simply * return true in this case, because no traversal callback can * fail when there are no traversal callbacks to begin with */ if (path == nil || (len = path.length()) == 0) return true; /* traverse from the path's starting point */ if (path[1] != nil && !(func)(path[1], PathFrom)) return nil; /* run through this path and see if the traversals are allowed */ for (target = nil, local i = 2 ; i <= len ; i += 2) { local op; /* get the next traversal operation */ op = path[i]; /* check the next traversal to see if it's allowed */ switch(op) { case PathIn: /* * traverse in - notify the previous object, since it's * the container we're entering */ target = path[i-1]; break; case PathOut: /* * traversing out of the current container - tell the * next object, since it's the object we're leaving */ target = path[i+1]; break; case PathPeer: /* * traversing from one object to a containment peer - * notify the container in common to both peers */ target = path[i-1].getCommonDirectContainer(path[i+1]); break; case PathThrough: /* * traversing through a multi-location connector (the * previous and next object will always be the same in * this case, so it doesn't really matter which we * choose) */ target = path[i-1]; break; } /* call the traversal callback */ if (target != nil && !(func)(target, op)) { /* the callback told us not to continue */ return nil; } } /* * Traverse to the path's ending point, if we haven't already. * Some operations do traverse to the right-hand element * (PathOut in particular), in which case we'll already have * reached the target. Most operations traverse the left-hand * element, though, so in these cases we need to visit the last * element explicitly. */ if (path[len] != nil && path[len] != target && !(func)(path[len], PathTo)) return nil; /* all callbacks told us to continue */ return true; } /* * Build a vector containing all of the possible paths we can * traverse to get from me to the given object. The return value is * a vector of paths; each path is a list of containment operations * needed to get from here to there. * * Each path item in the vector is a list arranged like so: * * [obj, op, obj, op, obj] * * Each 'obj' is an object, and each 'op' is an operation enum * (PathIn, PathOut, PathPeer) that specifies how to get from the * preceding object to the next object. The first object in the * list is always the starting object (i.e., self), and the last is * always the target object ('obj'). */ getAllPathsTo(obj) { local vec; /* create an empty vector to hold the return set */ vec = new Vector(10); /* look along each connection from me */ buildContainmentPaths(vec, [self], obj); /* if there's no path, check the object for a special path */ if (obj != nil && vec.length() == 0) obj.specialPathFrom(self, vec); /* return the vector */ return vec; } /* * Get a "special" path from the given starting object to me. * src.getAllPathsTo(obj) calls this on 'obj' when it can't find any * actual containment path from 'src' to 'obj'. If desired, this * method should add the path or paths to the vector 'vec'. * * By default, we do nothing at all. The purpose of this routine is * to allow special objects that exist outside the normal containment * model to insinuate themselves into the sense model under special * conditions of their choosing. */ specialPathFrom(src, vec) { } /* * Service routine for getAllPathsTo: build a vector of the * containment paths starting with this object. */ buildContainmentPaths(vec, pathHere, obj) { local i, len; local cur; /* scan each of our contents */ for (i = 1, len = contents.length() ; i <= len ; ++i) { /* get the current item */ cur = contents[i]; /* * If this item is the target, we've found what we're * looking for - simply add the path to here to the return * vector. * * Otherwise, if this item isn't already in the path, * traverse into it; if it's already in the path, there's no * need to look at it because we've already looked at it to * get here */ if (cur == obj) { /* * The path to here is the path to the target. Append * the target object itself with an 'in' operation. * Before adding the path to the result set, normalize * it. */ vec.append(normalizePath(pathHere + [PathIn, obj])); } else if (pathHere.indexOf(cur) == nil) { /* * look at this item, adding it to the path with an * 'enter child' traversal */ cur.buildContainmentPaths(vec, pathHere + [PathIn, cur], obj); } } /* scan each of our locations */ for (local clst = getConnectedContainers, i = 1, len = clst.length() ; i <= len ; ++i) { /* get the current item */ cur = clst[i]; /* * If this item is the target, we've found what we're * looking for. Otherwise, if this item isn't already in * the path, traverse into it */ if (cur == obj) { /* * We have the path to the target. Add the traversal to * the container to the path, normalize the path, and * add the path to the result set. */ vec.append(normalizePath(pathHere + [PathOut, cur])); } else if (pathHere.indexOf(cur) == nil) { /* * Look at this container, adding it to the path with an * 'exit to container' traversal. */ cur.buildContainmentPaths(vec, pathHere + [PathOut, cur], obj); } } } /* * "Normalize" a containment path to remove redundant containment * traversals. * * First, we expand any sequence of in+out operations that take us * out of one root-level containment tree and into another to * include a "through" operation for the multi-location object being * traversed. For example, if 'a' and 'c' do not share a common * container, then we will turn this: * * [a PathIn b PathOut c] * * into this: * * [a PathIn b PathThrough b PathOut c] * * This will ensure that when we traverse the path, we will * explicitly traverse through the connector material of 'b'. * * Second, we replace any sequence of out+in operations through a * common container with "peer" operations across the container's * contents directly. For example, a path that looks like this * * [a PathOut b PathIn c] * * will be normalized to this: * * [a PathPeer c] * * This means that we go directly from a to c, traversing through * the fill medium of their common container 'b' but not actually * traversing out of 'b' and back into it. */ normalizePath(path) { /* * Traverse the path looking for items to normalize with the * (in-out)->(through) transformation. Start at the second * element, which is the first path operation code. */ for (local i = 2 ; i <= path.length() ; i += 2) { /* * If we're on an 'in' operation, and an 'out' operation * immediately follows, and the object before the 'in' and * the object after the 'out' do not share a common * container, we must add an explicit 'through' step for the * multi-location connector being traversed. */ if (path[i] == PathIn && i + 2 <= path.length() && path[i+2] == PathOut && path[i-1].getCommonDirectContainer(path[i+3]) == nil) { /* we need to add a 'through' operation */ path = path.sublist(1, i + 1) + PathThrough + path.sublist(i + 1); } } /* * make another pass, this time applying the (out-in)->peer * transformation */ for (local i = 2 ; i <= path.length() ; i += 2) { /* * If we're on an 'out' operation, and an 'in' operation * immediately follows, we can collapse the out+in sequence * to a single 'peer' operation. */ if (path[i] == PathOut && i + 2 <= path.length() && path[i+2] == PathIn) { /* * this sequence can be collapsed to a single 'peer' * operation - rewrite the path accordingly */ path = path.sublist(1, i - 1) + PathPeer + path.sublist(i + 3); } } /* return the normalized path */ return path; } /* * Get the visual sense information for this object from the current * global point of view. If we have explicit sense information set * with setSenseInfo, we'll return that; otherwise, we'll calculate * the current sense information for the given point of view. * Returns a SenseInfo object giving the information. */ getVisualSenseInfo() { local infoTab; /* if we have explicit sense information already set, use it */ if (explicitVisualSenseInfo != nil) return explicitVisualSenseInfo; /* calculate the sense information for the point of view */ infoTab = getPOVDefault(gActor).visibleInfoTable(); /* return the information on myself from the table */ return infoTab[self]; } /* * Call a description method with explicit point-of-view and the * related point-of-view sense information. 'pov' is the point of * view object, which is usually an actor; 'senseInfo' is a * SenseInfo object giving the sense information for this object, * which we'll use instead of dynamically calculating the sense * information for the duration of the routine called. */ withVisualSenseInfo(pov, senseInfo, methodToCall, [args]) { local oldSenseInfo; /* push the sense information */ oldSenseInfo = setVisualSenseInfo(senseInfo); /* push the point of view */ pushPOV(pov, pov); /* make sure we restore the old value no matter how we leave */ try { /* * call the method with the given arguments, and return the * result */ return self.(methodToCall)(args...); } finally { /* restore the old point of view */ popPOV(); /* restore the old sense information */ setVisualSenseInfo(oldSenseInfo); } } /* * Set the explicit visual sense information; if this is not nil, * getVisualSenseInfo() will return this rather than calculating the * live value. Returns the old value, which is a SenseInfo or nil. */ setVisualSenseInfo(info) { local oldInfo; /* remember the old value */ oldInfo = explicitVisualSenseInfo; /* remember the new value */ explicitVisualSenseInfo = info; /* return the original value */ return oldInfo; } /* current explicit visual sense information overriding live value */ explicitVisualSenseInfo = nil /* * Determine how accessible my contents are to a sense. Any items * contained within a Thing are considered external features of the * Thing, hence they are transparently accessible to all senses. */ transSensingIn(sense) { return transparent; } /* * Determine how accessible peers of this object are to the contents * of this object, via a given sense. This has the same meaning as * transSensingIn(), but in the opposite direction: whereas * transSensingIn() determines how accessible my contents are from * the outside, this determines how accessible the outside is from * the contents. * * By default, we simply return the same thing as transSensingIn(), * since most containers are symmetrical for sense passing from * inside to outside or outside to inside. However, we distinguish * this as a separate method so that asymmetrical containers can * have different effects in the different directions; for example, * a box made of one-way mirrors might be transparent when looking * from the inside to the outside, but opaque in the other * direction. */ transSensingOut(sense) { return transSensingIn(sense); } /* * Get my "fill medium." This is an object of class FillMedium that * permeates our interior, such as fog or smoke. This object * affects the transmission of senses from one of our children to * another, or between our interior and exterior. * * Note that the FillMedium object is usually a regular object in * scope, so that the player can refer to the fill medium. For * example, if a room is filled with fog, the player might want to * be able to refer to the fog in a command. * * By default, our medium is the same as our parent's medium, on the * assumption that fill media diffuse throughout the location's * interior. Note, though, that Container overrides this so that a * closed Container is isolated from its parent's fill medium - * think of a closed bottle within a room filled with smoke. * However, a fill medium doesn't expand from a child into its * containers - it only diffuses into nested containers, never out. * * An object at the outermost containment level has no fill medium * by default, so we return nil if our location is nil. * * Note that, unlike the "surface" material, the fill medium is * assumed to be isotropic - that is, it has the same sense-passing * characteristics regardless of the direction in which the energy * is traversing the medium. Since we don't have any information in * our containment model about the positions of our objects relative * to one another, we have no way to express anisotropy in the fill * medium among our children anyway. * * Note further that energy going in or out of this object must * traverse both the fill medium and the surface of the object * itself. Since we have no other information on the relative * positions of our contents, we can only assume that they're * uniformly distributed through our interior, so it is necessary to * traverse the same amount of fill material to go from one child to * any other or from a child to our inner surface. * * As a sense is transmitted, several consecutive traversals of a * single fill material (i.e., a single object reference) will be * treated as a single traversal of the material. Since we don't * have a notion of distance in our containment model, we can't * assume that we cover a certain amount of distance just because we * traverse a certain number of containment levels. So, if we have * three nested containment levels all inheriting a single fill * material from their outermost parent, traversing from the inner * container to the outer container will count as a single traversal * of the material. */ fillMedium() { local loc; return ((loc = location) != nil ? loc.fillMedium() : nil); } /* * Can I see/hear/smell the given object? By default, an object can * "see" (or "hear", etc) another if there's a clear path in the * corresponding basic sense to the other object. Note that actors * override this, because they have a subjective view of the senses: * an actor might see in a special infrared vision sense rather than * (or in addition to) the normal 'sight' sense, for example. */ canSee(obj) { return senseObj(sight, obj).trans != opaque; } canHear(obj) { return senseObj(sound, obj).trans != opaque; } canSmell(obj) { return senseObj(smell, obj).trans != opaque; } /* can the given actor see/hear/smell/touch me? */ canBeSeenBy(actor) { return actor.canSee(self); } canBeHeardBy(actor) { return actor.canHear(self); } canBeSmelledBy(actor) { return actor.canSmell(self); } canBeTouchedBy(actor) { return actor.canTouch(self); } /* can the player character see/hear/smell/touch me? */ canBeSeen = (canBeSeenBy(gPlayerChar)) canBeHeard = (canBeHeardBy(gPlayerChar)) canBeSmelled = (canBeSmelledBy(gPlayerChar)) canBeTouched = (canBeTouchedBy(gPlayerChar)) /* * Am I occluded by the given Occluder when viewed in the given sense * from the given point of view? The default Occluder implementation * calls this on each object involved in the sense path to determine * if it should occlude the object. Returns true if we're occluded * by the given Occluder, nil if not. By default, we simply return * nil to indicate that we're not occluded. */ isOccludedBy(occluder, sense, pov) { return nil; } /* * Determine how well I can sense the given object. Returns a * SenseInfo object describing the sense path from my point of view * to the object. * * Note that, because 'distant', 'attenuated', and 'obscured' * transparency levels always compound (with one another and with * themselves) to opaque, there will never be more than a single * obstructor in a path, because any path with two or more * obstructors would be an opaque path, and hence not a path at all. */ senseObj(sense, obj) { local info; /* get the sense information from the table */ info = senseInfoTable(sense)[obj]; /* if we couldn't find the object, return an 'opaque' indication */ if (info == nil) info = new SenseInfo(obj, opaque, nil, 0); /* return the sense data descriptor */ return info; } /* * Find an opaque obstructor. This can be called immediately after * calling senseObj() when senseObj() indicates that the object is * opaquely obscured. We will find the nearest (by containment) * object where the sense status is non-opaque, and we'll return * that object. * * senseObj() by itself does not determine the obstructor when the * sense path is opaque, because doing so requires extra work. The * sense path calculator that senseObj() uses cuts off its search * whenever it reaches an opaque point, because beyond that point * nothing can be sensed. * * This can only be called immediately after calling senseObj() * because we re-use the same cached sense path information that * senseObj() uses. */ findOpaqueObstructor(sense, obj) { local path; /* get all of the paths from here to there */ path = getAllPathsTo(obj); /* * if there are no paths, we won't be able to find a specific * obstructor - the object simply isn't connected to us at all */ if (path == nil) return nil; /* * Arbitrarily take the first path - there must be an opaque * obstructor on every path or we never would have been called * in the first place. One opaque obstructor is as good as any * other for our purposes. */ path = path[1]; /* * The last thing in the list that can be sensed is the opaque * obstructor. Note that the path entries alternate between * objects and traversal operations. */ for (local i = 3, local len = path.length() ; i <= len ; i += 2) { local obj; local trans; local ambient; /* get this object */ obj = path[i]; /* * get the appropriate sense direction - if the path takes * us out, look at the interior sense data for the object; * otherwise look at its exterior sense data */ if (path[i-1] == PathOut) { /* we're looking outward, so use the interior data */ trans = obj.tmpTransWithin_; ambient = obj.tmpAmbientWithin_; } else { /* we're looking inward, so use the exterior data */ trans = obj.tmpTrans_; ambient = obj.tmpAmbient_; } /* * if this item cannot be sensed, the previous item is the * opaque obstructor */ if (!obj.canBeSensed(sense, trans, ambient)) { /* can't sense it - the previous item is the obstructor */ return path[i-2]; } } /* we didn't find any obstructor */ return nil; } /* * Build a list of full information on all of the objects reachable * from me through the given sense, along with full information for * each object's sense characteristics. * * We return a lookup table of each object that can be sensed (in * the given sense) from the point of view of 'self'. The key for * each entry in the table is an object, and the corresponding value * is a SenseInfo object describing the sense conditions for the * object. */ senseInfoTable(sense) { local objs; local tab; local siz; local cache; local key = [self, sense]; /* if we have cached sense information, simply return it */ if ((cache = libGlobal.senseCache) != nil && (tab = cache[key]) != nil) return tab; /* * get the list of objects connected to us by containment - * since the only way senses can travel between objects is via * containment relationships, this is the complete set of * objects that could be connected to us by any senses */ objs = connectionTable(); /* we're the point of view for this path calculation */ senseTmp.pointOfView = self; /* cache the sensory information for all of these objects */ cacheSenseInfo(objs, sense); /* build a table of all of the objects we can reach */ siz = objs.getEntryCount(); tab = new LookupTable(32, siz == 0 ? 32 : siz); objs.forEachAssoc(function(cur, val) { /* add this object's sense information to the table */ cur.addToSenseInfoTable(sense, tab); }); /* add the information vector to the sense cache */ if (cache != nil) cache[key] = tab; /* return the result table */ return tab; } /* * Add myself to a sense info table. This is called by * senseInfoTable() for each object connected by containment to the * source object 'src', after we've fully traversed the containment * tree to initialize our current-sense properties (tmpAmbient_, * tmpTrans_, etc). * * Our job is to figure out if 'src' can sense us, and add a * SenseInfo entry to the LookupTable 'tab' (which is keyed by * object, hence our key is simply 'self') if 'src' can indeed sense * us. * * Note that an object that wants to set up its own special sensory * data can do so by overriding this. This routine will only be * called on objects connected to 'src' by containment, though, so if * an object overrides this in order to implement a special sensory * system that's outside of the normal containment model, it must * somehow ensure that it gets included in the containment connection * table in the first place. */ addToSenseInfoTable(sense, tab) { local trans; local ambient; local obs; /* * consider the appropriate set of data, depending on whether the * source is looking out from within me, or in from outside of me */ if (tmpPathIsIn_) { /* we're looking in from without, so use our exterior data */ trans = tmpTrans_; ambient = tmpAmbient_; obs = tmpObstructor_; } else { /* we're looking out from within, so use our interior data */ trans = tmpTransWithin_; ambient = tmpAmbientWithin_; obs = tmpObstructorWithin_; } /* if the source can sense us, add ourself to the data */ if (canBeSensed(sense, trans, ambient)) tab[self] = new SenseInfo(self, trans, obs, ambient); } /* * Build a list of the objects reachable from me through the given * sense and with a presence in the sense. */ sensePresenceList(sense) { local infoTab; /* get the full sense list */ infoTab = senseInfoTable(sense); /* * return only the subset of items that have a presence in this * sense, and return only the items themselves, not the * SenseInfo objects */ return senseInfoTableSubset(infoTab, {obj, info: obj.(sense.presenceProp)}); } /* * Determine the highest ambient sense level at this object for any * of the given senses. * * Note that this method changes certain global variables used during * sense and scope calculations. Because of this, be careful not to * call this method *during* sense or scope calculations. In * particular, don't call this from an object's canBeSensed() method * or anything it calls. For example, don't call this from a * Hidden.discovered method. */ senseAmbientMax(senses) { local objs; local maxSoFar; /* we don't have any level so far */ maxSoFar = 0; /* get the table of connected objects */ objs = connectionTable(); /* go through each sense */ for (local i = 1, local lst = senses, local len = lst.length() ; i <= len ; ++i) { /* * cache the ambient level for this sense for everything * connected by containment */ cacheAmbientInfo(objs, lst[i]); /* * if our cached level for this sense is the highest so far, * remember it */ if (tmpAmbient_ > maxSoFar) maxSoFar = tmpAmbient_; } /* return the highest level we found */ return maxSoFar; } /* * Cache sensory information for all objects in the given list from * the point of view of self. This caches the ambient energy level * at each object, if the sense uses ambient energy, and the * transparency and obstructor on the best path in the sense to the * object. 'objs' is the connection table, as generated by * connectionTable(). */ cacheSenseInfo(objs, sense) { /* clear out the end-of-calculation notification list */ local nlst = senseTmp.notifyList; if (nlst.length() != 0) nlst.removeRange(1, nlst.length()); /* first, calculate the ambient energy level at each object */ cacheAmbientInfo(objs, sense); /* next, cache the sense path from here to each object */ cacheSensePath(sense); /* notify each object in the notification list */ for (local i = 1, local len = nlst.length() ; i <= len ; ++i) nlst[i].finishSensePath(objs, sense); } /* * Cache the ambient energy level at each object in the table. The * list must include everything connected by containment. */ cacheAmbientInfo(objs, sense) { local aprop; /* * if this sense has ambience, transmit energy from sources to * all reachable objects; otherwise, just clear out the sense * data */ if ((aprop = sense.ambienceProp) != nil) { local sources; /* create a vector to hold the set of ambient energy sources */ sources = new Vector(16); /* * Clear out any cached sensory information from past * calculations, and note objects that have ambient energy to * propagate. */ objs.forEachAssoc(function(cur, val) { /* clear old sensory information for this object */ cur.clearSenseInfo(); /* if it's an energy source, note it */ if (cur.(aprop) != 0) sources.append(cur); }); /* * Calculate the ambient energy level at each object. To do * this, start at each energy source and transmit its energy * to all objects within reach of the sense. */ for (local i = 1, local len = sources.length() ; i <= len ; ++i) { /* get this item */ local cur = sources[i]; /* if this item transmits energy, process it */ if (cur.(aprop) != 0) cur.transmitAmbient(sense); } } else { /* * this sense doesn't use ambience - all we need to do is * clear out any old sense data for this object */ objs.forEachAssoc({cur, val: cur.clearSenseInfo()}); } } /* * Transmit my radiating energy to everything within reach of the * sense. */ transmitAmbient(sense) { local ambient; /* get the energy level I'm transmitting */ ambient = self.(sense.ambienceProp); /* if this is greater than my ambient level so far, take it */ if (ambient > tmpAmbient_) { /* * remember the new settings: start me with my own ambience, * and with no fill medium in the way (there's nothing * between me and myself, so I shine on myself with full * force and with no intervening fill medium) */ tmpAmbient_ = ambient; tmpAmbientFill_ = nil; /* * if the level is at least 2, transmit to adjacent objects * (level 1 is self-illumination only, so we don't transmit * to anything else) */ if (ambient >= 2) { /* transmit to my containers */ shineOnLoc(sense, ambient, nil); /* shine on my contents */ shineOnContents(sense, ambient, nil); } } /* * Apply our interior self-illumination if necessary. If we're * self-illuminating, and our interior surface doesn't already * have higher ambient energy from another source, then the * ambient energy at our inner surface is simply 1, since we * self-illuminate inside as well as outside. */ if (ambient == 1 && ambient > tmpAmbientWithin_) tmpAmbientWithin_ = ambient; } /* * Transmit ambient energy to my location or locations. */ shineOnLoc(sense, ambient, fill) { /* * shine on my container, if I have one, and its immediate * children */ if (location != nil) location.shineFromWithin(self, sense, ambient, fill); } /* * Shine ambient energy at my surface onto my contents. */ shineOnContents(sense, ambient, fill) { local levelWithin; /* * Figure the level of energy to transmit to my contents. To * reach my contents, the ambient energy here must traverse our * surface. Since we want to know what the ambient light at our * surface looks like when viewed from our interior, we must use * the sensing-out transparency. */ levelWithin = adjustBrightness(ambient, transSensingOut(sense)); /* * Remember our ambient level at our inner surface, if the * adjusted transmission is higher than our tentative ambient * level already set from another source or path. Note that the * tmpAmbientWithin_ caches the ambient level at our inner * surface, which comes before we adjust for our fill medium * (because our fill medium is enclosed by our inner surface). */ if (levelWithin >= 2 && levelWithin > tmpAmbientWithin_) { local fillWithin; /* note my new interior ambience */ tmpAmbientWithin_ = levelWithin; /* * If there's a new fill material in my interior that the * ambient energy here hasn't already just traversed, we must * further adjust the ambient level in my interior by the * fill transparency. */ fillWithin = tmpFillMedium_; if (fillWithin != fill && fillWithin != nil) { /* * we're traversing a new fill material - adjust the * brightness further for the fill material */ levelWithin = adjustBrightness(levelWithin, fillWithin.senseThru(sense)); } /* if that leaves any ambience in my interior, transmit it */ if (levelWithin >= 2) { /* shine on each object directly within me */ for (local clst = contents, local i = 1, local len = clst.length() ; i <= len ; ++i) { /* transmit the ambient energy to this child item */ clst[i].shineFromWithout(self, sense, levelWithin, fillWithin); } } } } /* * Transmit ambient energy from an object within me. This transmits * to my outer surface, and also to my own immediate children - in * other words, to the peers of the child shining on us. We need to * transmit to the source's peers right now, because it might * degrade the ambient energy to go out through our surface. */ shineFromWithin(fromChild, sense, ambient, fill) { local levelWithout; local levelWithin; local fillWithin; /* * Calculate the change in energy as the sense makes its way to * our "inner surface," and to peers of the sender - in both * cases, the energy must traverse our fill medium to get to the * next object. * * As always, energy must never traverse a single fill medium * more than once consecutively, so if the last fill material is * the same as the fill material here, no further adjustment is * necessary for another traversal of the same material. */ levelWithin = ambient; fillWithin = tmpFillMedium_; if (fillWithin != fill && fillWithin != nil) { /* adjust the brightness for the fill traversal */ levelWithin = adjustBrightness(levelWithin, fillWithin.senseThru(sense)); } /* if there's no energy left to transmit, we're done */ if (levelWithin < 2) return; /* * Since we're transmitting the energy from within us, calculate * any attenuation as the energy goes from our inner surface to * our outer surface - this is the energy that makes it through * to our exterior and thus is the new ambient level at our * surface. We must calculate the attenuation that a viewer * from outside sees looking at an energy source within us, so * we must use the sensing-in transparency. * * Note that we start here with the level within that we've * already calculated: we assume that the energy from our child * must first traverse our interior medium before reaching our * "inner surface," at which point it must then further traverse * our surface material to reach our "outer surface," at which * point it's the ambient level at our exterior. */ levelWithout = adjustBrightness(levelWithin, transSensingIn(sense)); /* * The level at our outer surface is the new ambient level for * this object. The last fill material traversed is the fill * material within me. If it's the best yet, take it. */ if (levelWithout > tmpAmbient_) { /* it's the best so far - cache it */ tmpAmbient_ = levelWithout; tmpAmbientFill_ = fillWithin; /* transmit to our containers */ shineOnLoc(sense, levelWithout, fillWithin); } /* transmit the level within to each peer of the sender */ if (levelWithin > tmpAmbientWithin_) { /* note our level within */ tmpAmbientWithin_ = levelWithin; /* transmit to each of our children */ for (local i = 1, local clst = contents, local len = clst.length() ; i <= len ; ++i) { /* get the current item */ local cur = clst[i]; /* if it's not the source, shine on it */ if (cur != fromChild) cur.shineFromWithout(self, sense, levelWithin, fillWithin); } } } /* * Transmit ambient energy from an object immediately containing me. */ shineFromWithout(fromParent, sense, level, fill) { /* if this is the best level yet, take it and transmit it */ if (level > tmpAmbient_) { /* cache this new best level */ tmpAmbient_ = level; tmpAmbientFill_ = fill; /* transmit it down to my children */ shineOnContents(sense, level, fill); } } /* * Cache the sense path for each object reachable from this point of * view. Fills in tmpTrans_ and tmpObstructor_ for each object with * the best transparency path from the object to me. */ cacheSensePath(sense) { /* the view from me to myself is unobstructed */ tmpPathIsIn_ = true; tmpTrans_ = transparent; tmpTransWithin_ = transparent; tmpObstructor_ = nil; tmpObstructorWithin_ = nil; /* build a path to my containers */ sensePathToLoc(sense, transparent, nil, nil); /* build a path to my contents */ sensePathToContents(sense, transparent, nil, nil); } /* * Build a path to my location or locations */ sensePathToLoc(sense, trans, obs, fill) { /* * proceed to my container, if I have one, and its immediate * children */ if (location != nil) location.sensePathFromWithin(self, sense, trans, obs, fill); } /* * Build a sense path to my contents */ sensePathToContents(sense, trans, obs, fill) { local transWithin; local obsWithin; local fillWithin; /* * Figure the transparency to my contents. To reach my * contents, we must look in through our surface. If we change * the transparency, we're the new obstructor. */ transWithin = transparencyAdd(trans, transSensingIn(sense)); obsWithin = (trans == transWithin ? obs : self); /* * If there's a new fill material in my interior that we haven't * already just traversed, we must further adjust the * transparency by the fill transparency. */ fillWithin = tmpFillMedium_; if (fillWithin != fill && fillWithin != nil) { local oldTransWithin = transWithin; /* we're traversing a new fill material */ transWithin = transparencyAdd(transWithin, fillWithin.senseThru(sense)); if (transWithin != oldTransWithin) obsWithin = fill; } /* if the path isn't opaque, proceed to my contents */ if (transWithin != opaque) { /* build a path to each child */ for (local clst = contents, local i = 1, local len = clst.length() ; i <= len ; ++i) { /* build a path to this child */ clst[i].sensePathFromWithout(self, sense, transWithin, obsWithin, fillWithin); } } } /* * Build a path from an object within me. */ sensePathFromWithin(fromChild, sense, trans, obs, fill) { local transWithin; local fillWithin; local transWithout; local obsWithout; /* * Calculate the transparency change along the path from the * child to our "inner surface" and to peers of the sender - in * both cases, we must traverse the fill material. * * As always, energy must never traverse a single fill medium * more than once consecutively, so if the last fill material is * the same as the fill material here, no further adjustment is * necessary for another traversal of the same material. */ transWithin = trans; fillWithin = tmpFillMedium_; if (fillWithin != fill && fillWithin != nil) { /* adjust for traversing a new fill material */ transWithin = transparencyAdd(transWithin, fillWithin.senseThru(sense)); if (transWithin != trans) obs = fillWithin; } /* if we're opaque at this point, we're done */ if (transWithin == opaque) return; /* * Calculate the transparency going from our inner surface to * our outer surface - we must traverse our own material to * travel this segment. */ transWithout = transparencyAdd(transWithin, transSensingOut(sense)); obsWithout = (transWithout != transWithin ? self : obs); /* * We now have the path to our outer surface. The last fill * material traversed is the fill material within me. If this * is the best yet, remember it. */ if (transparencyCompare(transWithout, tmpTrans_) > 0) { /* it's the best so far - cache it */ tmpTrans_ = transWithout; tmpObstructor_ = obsWithout; /* we're coming to this object from within */ tmpPathIsIn_ = nil; /* transmit to our containers */ sensePathToLoc(sense, transWithout, obsWithout, fillWithin); } /* * if this is the best interior transparency yet, build a path * to each peer of the sender */ if (transparencyCompare(transWithin, tmpTransWithin_) > 0) { /* it's the best so far - cache it */ tmpTransWithin_ = transWithin; tmpObstructorWithin_ = obs; /* we're coming to this object from within */ tmpPathIsIn_ = nil; /* build a path to each peer of the sender */ for (local i = 1, local clst = contents, local len = clst.length() ; i <= len ; ++i) { /* get this item */ local cur = clst[i]; /* if it's not the source, build a path to it */ if (cur != fromChild) cur.sensePathFromWithout(self, sense, transWithin, obs, fillWithin); } } } /* * Build a path from an object immediately containing me. */ sensePathFromWithout(fromParent, sense, trans, obs, fill) { /* if this is the best level yet, take it and keep going */ if (transparencyCompare(trans, tmpTrans_) > 0) { /* remember this new best level */ tmpTrans_ = trans; tmpObstructor_ = obs; /* we're coming to this object from outside */ tmpPathIsIn_ = true; /* build a path down into my children */ sensePathToContents(sense, trans, obs, fill); } } /* * Clear the sensory scratch-pad properties, in preparation for a * sensory calculation pass. */ clearSenseInfo() { tmpPathIsIn_ = true; tmpAmbient_ = 0; tmpAmbientWithin_ = 0; tmpAmbientFill_ = nil; tmpTrans_ = opaque; tmpTransWithin_ = opaque; tmpObstructor_ = nil; tmpObstructorWithin_ = nil; /* pre-calculate my fill medium */ tmpFillMedium_ = fillMedium(); } /* * Scratch-pad for calculating ambient energy level - valid only * after calcAmbience and until the game state changes in any way. * This is for internal use within the sense propagation methods * only. */ tmpAmbient_ = 0 /* * Last fill material traversed by the ambient sense energy in * tmpAmbient_. We must keep track of this so that we can treat * consecutive traversals of the same fill material as equivalent to * a single traversal. */ tmpAmbientFill_ = nil /* * Scrach-pad for the best transparency level to this object from * the current point of view. This is used during cacheSenseInfo to * keep track of the sense path to this object. */ tmpTrans_ = opaque /* * Scratch-pad for the obstructor that contributed to a * non-transparent path to this object in tmpTrans_. */ tmpObstructor_ = nil /* * Scratch-pads for the ambient level, best transparency, and * obstructor to our *interior* surface. We keep track of these * separately from the exterior data so that we can tell what we * look like from the persepctive of an object within us. */ tmpAmbientWithin_ = 0 tmpTransWithin_ = opaque tmpObstructorWithin_ = nil /* * Scratch-pad for the sense path direction at this object. If this * is true, the sense path is pointing inward - that is, the path * from the source object to here is entering from outside me. * Otherwise, the sense path is pointing outward. */ tmpPathIsIn_ = true /* * My fill medium. We cache this during each sense path * calculation, since the fill medium calculation often requires * traversing several containment levels. */ tmpFillMedium_ = nil /* * Merge two senseInfoTable tables. Merges the second table into * the first. If an object appears only in the first table, the * entry is left unchanged; if an object appears only in the second * table, the entry is added to the first table. If an object * appears in both tables, we'll keep the one with better detail or * brightness, adding it to the first table if it's the one in the * second table. */ mergeSenseInfoTable(a, b) { /* if either table is nil, return the other table */ if (a == nil) return b; else if (b == nil) return a; /* * iterate over the second table, merging each item from the * second table into the corresponding item in the first table */ b.forEachAssoc({obj, info: a[obj] = mergeSenseInfo(a[obj], info)}); /* return the merged first table */ return a; } /* * Merge two SenseInfo items. Chooses the "better" of the two items * and returns it, where "better" is defined as more transparent, * or, transparencies being equal, brighter in ambient energy. */ mergeSenseInfo(a, b) { /* if one or the other is nil, return the non-nil one */ if (a == nil) return b; if (b == nil) return a; /* * Both items are non-nil, so keep the better of the two. If * the transparencies aren't equal, keep the one that's more * transparent. Otherwise, keep the one with higher ambient * energy. */ if (a.trans == b.trans) { /* * The transparencies are equal, so choose the one with * higher ambient energy. If those are the same, * arbitrarily keep the first one. */ if (a.ambient >= b.ambient) return a; else return b; } else { /* * The transparencies are unequal, so pick the one with * better transparency. */ if (transparencyCompare(a.trans, b.trans) < 0) return b; else return a; } } /* * Receive notification that a command is about to be performed. * This is called on each object connected by containment with the * actor performing the command, and on any objects explicitly * registered with the actor, the actor's location and its locations * up to the outermost container, or the directly involved objects. */ beforeAction() { /* by default, do nothing */ } /* * Receive notification that a command has just been performed. * This is called by the same rules as beforeAction(), but under the * conditions prevailing after the command has been completed. */ afterAction() { /* by default, do nothing */ } /* * Receive notification that a traveler (an actor or a vehicle, for * example) is about to depart via travelerTravelTo(), OR that an * actor is about to move among nested locations via travelWithin(). * This notification is sent to each object connected to the traveler * by containment, just before the traveler departs. * * If the traveler is traveling between top-level locations, * 'connector' is the TravelConnector object being traversed. If an * actor is merely moving between nested locations, 'connector' will * be nil. */ beforeTravel(traveler, connector) { /* do nothing by default */ } /* * Receive notification that a traveler has just arrived via * travelerTravelTo(). This notification is sent to each object * connected to the traveler by containment, in its new location, * just after the travel completes. */ afterTravel(traveler, connector) { /* do nothing by default */ } /* * Get my notification list - this is a list of objects on which we * must call beforeAction and afterAction when this object is * involved in a command as the direct object, indirect object, or * any addition object (other than as the actor performing the * command). * * The general notification mechanism always includes in the * notification list all of the objects connected by containment to * the actor; this method allows for explicit registration of * additional objects that must be notified when commands are * performed on this object even when the other objects are nowhere * nearby. */ getObjectNotifyList() { /* return our registration list */ return objectNotifyList; } /* * Add an item to our registered notification list for actions * involving this object as the direct object, indirect object, and * so on. * * Items can be added here if they must be notified of actions * involving this object regardless of the physical proximity of * this item and the notification item. */ addObjectNotifyItem(obj) { objectNotifyList += obj; } /* remove an item from the registered notification list */ removeObjectNotifyItem(obj) { objectNotifyList -= obj; } /* our list of registered notification items */ objectNotifyList = [] /* -------------------------------------------------------------------- */ /* * Verify a proposed change of location of this object from its * current container hierarchy to the given new container. We'll * verify removal from each container up to but not including a * parent that's in common with the new container - we stop upon * reaching the common parent because the object isn't leaving the * common parent, but merely repositioned around within it. We'll * also verify insertion into each new parent from the first * non-common parent on down to the immediate new container. * * This routine is called any time an actor action would cause this * object to be moved to a new container, so it is the common point * at which to intercept any action that would attempt to move the * object. */ verifyMoveTo(newLoc) { /* check removal up to the common parent */ sendNotifyRemove(self, newLoc, &verifyRemove); /* check insertion into parents up to the common parent */ if (newLoc != nil) newLoc.sendNotifyInsert(self, newLoc, &verifyInsert); } /* * Send the given notification to each direct parent, each of their * direct parents, and so forth, stopping when we reach parents that * we have in common with our new location. We don't notify parents * in common with new location (or their parents) because we're not * actually removing the object from the common parents. */ sendNotifyRemove(obj, newLoc, msg) { /* send notification to each container, as appropriate */ forEachContainer(function(loc) { /* * If this container contains the new location, don't send it * (or its parents) notification, since we're not leaving it. * Otherwise, send the notification and proceed to its * parents. */ if (newLoc == nil || (loc != newLoc && !newLoc.isIn(loc))) { /* notify this container of the removal */ loc.(msg)(obj); /* recursively notify this container's containers */ loc.sendNotifyRemove(obj, newLoc, msg); } }); } /* * Send the given notification to each direct parent, each of their * direct parents, and so forth, stopping when we reach parents that * we have in common with our new location. We don't notify parents * in common with new location (or their parents). * * This should always be called *before* a change of location is * actually put into effect, so that we will still be in our old * container when this is called. 'obj' is the object being * inserted, and 'newCont' is the new direct container. */ sendNotifyInsert(obj, newCont, msg) { /* * If the object is already in me, there's no need to notify * myself of the insertion; otherwise, send the notification. * * If the object is already inside me indirectly, and we're * moving it directly in me, still notify myself, since we're * still picking up a new direct child. */ if (!obj.isIn(self)) { /* * before we notify ourselves, notify my own parents - this * sends the notifications from the outside in */ forEachContainer({loc: loc.sendNotifyInsert(obj, newCont, msg)}); /* notify this potential container of the insertion */ self.(msg)(obj, newCont); } else if (newCont == self && !obj.isDirectlyIn(self)) { /* notify myself of the new direct insertion */ self.(msg)(obj, self); } } /* * Verify removal of an object from my contents or a child object's * contents. By default we allow the removal. This is to be called * during verification only, so gVerifyResult is valid when this is * called. */ verifyRemove(obj) { } /* * Verify insertion of an object into my contents. By default we * allow it, unless I'm already inside the other object. This is to * be called only during verification. */ verifyInsert(obj, newCont) { /* * If I'm inside the other object, don't allow it, since this * would create circular containment. */ if (isIn(obj)) illogicalNow(obj.circularlyInMessage, newCont, obj); } /* * my message indicating that another object x cannot be put into me * because I'm already in x */ circularlyInMessage = &circularlyInMsg /* * Receive notification that we are about to remove an object from * this container. This is normally called during the action() * phase. * * When an object is about to be moved via moveInto(), the library * calls notifyRemove on the old container tree, then notifyInsert on * the new container tree, then notifyMoveInto on the object being * moved. Any of these routines can cancel the operation by * displaying an explanatory message and calling 'exit'. */ notifyRemove(obj) { } /* * Receive notification that we are about to insert a new object into * this container. 'obj' is the object being moved, and 'newCont' is * the new direct container (which might be a child of ours). This * is normally called during the action() phase. * * During moveInto(), this is called on the new container tree after * notifyRemove has been called on the old container tree. This * routine can cancel the move by displaying an explanatory message * and calling 'exit'. */ notifyInsert(obj, newCont) { } /* * Receive notification that I'm about to be moved to a new * container. By default, we do nothing; subclasses can override * this to do any special processing when this object is moved. This * is normally called during the action() phase. * * During moveInto(), this routine is called after notifyRemove() and * notifyInsert() have been called on the old and new container trees * (respectively). This routine can cancel the move by displaying an * explanatory message and calling 'exit'. * * This routine is the last in the notification sequence, so if this * routine doesn't cancel the move, then the move will definitely * happen (at least to the extent that we'll definitely call * baseMoveInto() to carry out the move). */ notifyMoveInto(newCont) { } /* -------------------------------------------------------------------- */ /* * Determine if one property on this object effectively "hides" * another. This is a sort of override check for two distinct * properties. * * We look at the object to determine where prop1 and prop2 are * defined in the class hierarchy. If prop1 isn't defined, it * definitely doesn't hide prop2. If prop2 isn't defined, prop1 * definitely hides it. If both are defined, then prop1 hides prop2 * if and only if it is defined at a point in the class hierarchy * that is "more specialized" than prop2. That is, for prop1 to * hide prop2, the class that defines prop1 must either be the same * as the class that defines prop2, or the class where prop1 is * defined must inherit from the class that defines prop2, or the * class where prop1 is defined must be earlier in a multiple * inheritance list than the class defining prop2. */ propHidesProp(prop1, prop2) { local definer1; local definer2; /* * get the classes in our hierarchy where the two properties are * defined */ definer1 = propDefined(prop1, PropDefGetClass); definer2 = propDefined(prop2, PropDefGetClass); /* if prop1 isn't defined, it definitely doesn't hide prop2 */ if (definer1 == nil) return nil; /* if prop1 isn't defined, prop1 definitely hides it */ if (definer2 == nil) return true; /* * They're both defined. If definer1 inherits from definer2, * then prop1 definitely hides prop2. */ if (definer1.ofKind(definer2)) return true; /* * if definer2 inherits from definer1, then prop2 hides prop1, * so prop1 doesn't hide prop2 */ if (definer2.ofKind(definer1)) return nil; /* * The two classes don't have an inheritance relation among * themselves, but they might still be related in our own * hierarchy by multiple inheritance. In particular, if there * is some point in the hierarchy where we have multiple base * classes, and one class among the multiple bases inherits from * definer1 and the other from definer2, then the one that's * earlier in the multiple inheritance list is the hider. */ return superHidesSuper(definer1, definer2); } /* * Determine if a given superclass of ours hides another superclass * of ours, by being inherited (directly or indirectly) in our class * list ahead of the other. */ superHidesSuper(s1, s2) { local lst; local idx1, idx2; /* get our superclass list */ lst = getSuperclassList(); /* if we have no superclass, there is no hiding */ if (lst.length() == 0) return nil; /* * if we have only one element, there's obviously no hiding * going on at this level, so simply traverse into the * superclass to see if the hiding happens there */ if (lst.length() == 1) return lst[1].superHidesSuper(s1, s2); /* * Scan the superclass list to determine the first superclass to * which each of the two superclasses is related. Stop looking * when we find both superclasses or exhaust our list. */ for (local i = 1, idx1 = idx2 = nil ; i <= lst.length() && (idx1 == nil || idx2 == nil) ; ++i) { /* * if we haven't found s1 yet, and this superclass of ours * inherits from s1, this is the earliest at which we've * found s1 */ if (idx1 == nil && lst[i].ofKind(s1)) idx1 = i; /* likewise for the other superclass */ if (idx2 == nil && lst[i].ofKind(s2)) idx2 = i; } /* * if we found the two superclasses at different points in our * hierarchy, the one that comes earlier hides the other; so, if * idx1 is less than idx2, s1 hides s2, and if idx1 is greater * than idx2, s2 hides s1 (equivalently, s1 doesn't hide s2) */ if (idx1 != idx2) return idx1 < idx2; /* * We found both superclasses at the same point in our * hierarchy, so they must be multiply inherited by this base * class or one of its classes. Keep looking up the hierarchy * for our answer. */ return lst[idx1].superHidesSuper(s1, s2); } /* * Generic "check" failure routine. This displays the given failure * message, then performs an 'exit' to cancel the current command. * 'msg' is the message to display - it can be a single-quoted string * with the message text, or a property pointer. If 'msg' is a * property pointer, any necessary message parameters can be supplied * as additional 'params' arguments. */ failCheck(msg, [params]) { reportFailure(msg, params...); exit; } /* -------------------------------------------------------------------- */ /* * "Examine" action */ dobjFor(Examine) { preCond = [objVisible] verify() { /* give slight preference to an object being held */ if (!isIn(gActor)) logicalRank(80, 'not held'); } action() { /* * call our mainExamine method from the current actor's point * of view */ fromPOV(gActor, gActor, &mainExamine); } } /* * Main examination processing. This is called with the current * global POV set to the actor performing the 'examine' command. */ mainExamine() { /* perform the basic 'examine' action */ basicExamine(); /* * listen to and smell the object, but only show a message if we * have an explicitly associated Noise/Odor object */ basicExamineListen(nil); basicExamineSmell(nil); /* show special descriptions for any contents */ examineSpecialContents(); } /* * Perform the basic 'examine' action. This shows either the normal * or initial long description (the latter only if the object hasn't * been moved yet, and it has a special initial examine * description), and marks the object as having been described at * least once. */ basicExamine() { /* check the transparency to viewing this object */ local info = getVisualSenseInfo(); local t = info.trans; /* * If the viewing conditions are 'obscured' or 'distant', use * the special, custom messages for those conditions. * Otherwise, if the details are visible (which will be the case * even for a distant or obscured object if its sightSize is set * to 'large'), use the ordinary 'desc' description. Otherwise, * use an appropriate default description indicating that the * object's details aren't visible. */ if (getOutermostRoom() != getPOVDefault(gActor).getOutermostRoom() && propDefined(&remoteDesc)) { /* we're remote, so show our remote description */ remoteDesc(getPOVDefault(gActor)); } else if (t == obscured && propDefined(&obscuredDesc)) { /* we're obscured, so show our special obscured description */ obscuredDesc(info.obstructor); } else if (t == distant && propDefined(&distantDesc)) { /* we're distant, so show our special distant description */ distantDesc; } else if (canDetailsBeSensed(sight, info, getPOVDefault(gActor))) { /* * Viewing conditions and/or scale are suitable for showing * full details, so show my normal long description. Use * the initDesc if desired, otherwise show the normal "desc" * description. */ if (useInitDesc()) initDesc; else desc; /* note that we've examined it */ described = true; /* show any subclass-specific status */ examineStatus(); } else if (t == obscured) { /* * we're obscured, and we're not large enough to see the * details anyway, and we don't have a special description * for when we're obscured; show the default description of * an obscured object */ defaultObscuredDesc(info.obstructor); } else if (t == distant) { /* * we're distant, and we're not large enough to see the * details anyway, and we don't have a special distant * description; show the default distant description */ defaultDistantDesc; } } /* * Show any status associated with the object as part of the full * description. This is shown after the basicExamine() message, and * is only displayed if we can see full details of the object * according to the viewing conditions. * * By default, we simply show our contents. Even though this base * class isn't set up as a container from the player's perspective, * we could contain components which are themselves containers. So, * to ensure that we properly describe any contents of our contents, * we need to list our children. */ examineStatus() { /* list my contents */ examineListContents(); } /* * List my contents as part of Examine processing. We'll recursively * list contents of contents; this will ensure that even if we have * no listable contents, we'll list any listable contents of our * contents. */ examineListContents() { /* show our contents with our normal contents lister */ examineListContentsWith(descContentsLister); } /* list my contents for an "examine", using the given lister */ examineListContentsWith(lister) { /* get the actor's visual sense information */ local tab = gActor.visibleInfoTable(); /* mark my contents as having been seen */ setContentsSeenBy(tab, gActor); /* if we don't list our contents on Examine, do nothing */ if (!contentsListedInExamine) return; /* get my listed contents for 'examine' */ local lst = getContentsForExamine(lister, tab); /* show my listable contents, if I have any */ lister.showList(gActor, self, lst, ListRecurse, 0, tab, nil, examinee: self); } /* * Basic examination of the object for sound. If the object has an * associated noise object, we'll describe it. * * If 'explicit' is true, we'll show our soundDesc if we have no * associated Noise object; otherwise, we'll show nothing at all * unless we have a Noise object. */ basicExamineListen(explicit) { /* get my associated Noise object, if we have one */ local obj = getNoise(); /* * If this is not an explicit LISTEN command, only add the sound * description if we have a Noise object that is not marked as * "ambient." */ if (!explicit && (obj == nil || obj.isAmbient)) return; /* get our sensory information from the actor's point of view */ local info = getPOVDefault(gActor).senseObj(sound, self); local t = info.trans; /* * If we have a transparent path to the object, or we have * 'large' sound scale, show full details. Otherwise, show the * appropriate non-detailed message for the listening conditions. */ if (canDetailsBeSensed(sound, info, getPOVDefault(gActor))) { /* * We can hear full details. If we have a Noise object, show * its "listen to source" description; otherwise, show our * own default sound description. */ if (obj != nil) { /* note the explicit display of the Noise description */ obj.noteDisplay(); /* show the examine-source description of the Noise */ obj.sourceDesc; } else { /* we have no Noise, so use our own description */ soundDesc; } } else if (t == obscured) { /* show our 'obscured' description */ obscuredSoundDesc(info.obstructor); } else if (t == distant) { /* show our 'distant' description */ distantSoundDesc; } /* * If this is an explicit LISTEN TO directed at this object, also * mention any sounds we can hear from objects inside me, other * than the Noise object we've explicitly mentioned, that have a * sound presence. */ if (explicit) { local senseTab; local presenceList; /* get the set of objects we can hear */ senseTab = getPOVDefault(gActor).senseInfoTable(sound); /* get the subset with a sound presence that are inside me */ presenceList = senseInfoTableSubset( senseTab, {x, info: x.soundPresence && x.isIn(self) && x != obj}); /* show the soundHereDesc for each of these */ foreach (local cur in presenceList) cur.soundHereDesc(); } } /* * Basic examination of the object for odor. If the object has an * associated odor object, we'll describe it. */ basicExamineSmell(explicit) { local obj; local info; local t; /* get our associated Odor object, if any */ obj = getOdor(); /* * If this is not an explicit SMELL command, only add the odor * description if we have an Odor object that is not marked as * "ambient." */ if (!explicit && (obj == nil || obj.isAmbient)) return; /* get our sensory information from the actor's point of view */ info = getPOVDefault(gActor).senseObj(smell, self); t = info.trans; /* * If we have a transparent path to the object, or we have * 'large' sound scale, show full details. Otherwise, show the * appropriate non-detailed message for the listening conditions. */ if (canDetailsBeSensed(smell, info, getPOVDefault(gActor))) { /* if we have a Noise object, show its "listen to source" */ if (obj != nil) { /* note the explicit display of the Odor description */ obj.noteDisplay(); /* show the examine-source description of the Odor */ obj.sourceDesc; } else { /* we have no associated Odor; show our default description */ smellDesc; } } else if (t == obscured) { /* show our 'obscured' description */ obscuredSmellDesc(info.obstructor); } else if (t == distant) { /* show our 'distant' description */ distantSmellDesc; } /* * If this is an explicit SMELL command directed at this object, * also mention any odors we can detect from objects inside me, * other than the Odor object we've explicitly mentioned, that * have an odor presence. */ if (explicit) { local senseTab; local presenceList; /* get the set of objects we can smell */ senseTab = getPOVDefault(gActor).senseInfoTable(smell); /* get the subset with a smell presence that are inside me */ presenceList = senseInfoTableSubset( senseTab, {x, info: x.smellPresence && x.isIn(self) && x != obj}); /* show the smellHereDesc for each of these */ foreach (local cur in presenceList) cur.smellHereDesc(); } } /* * Basic examination of an object for taste. Unlike the * smell/listen examination routines, we don't bother using a * separate sensory emanation object for tasting, as tasting is * always an explicit action, never passive. Furthermore, since * tasting requires direct physical contact with the object, we * don't differentiate levels of transparency or distance. */ basicExamineTaste() { /* simply show our taste description */ tasteDesc; } /* * Basic examination of an object for touch. As with the basic * taste examination, we don't use an emanation object or * distinguish transparency levels, because feeling an object * requires direct physical contact. */ basicExamineFeel() { /* simply show our touch description */ feelDesc; } /* * Show the special descriptions of any contents. We'll run through * the visible information list for the location; for any visible * item inside me that is using its special description, we'll * display the special description as a separate paragraph. */ examineSpecialContents() { /* get the actor's table of visible items */ local infoTab = gActor.visibleInfoTable(); /* * get the objects using special descriptions and contained * within this object */ local lst = specialDescList( infoTab, {obj: obj.useSpecialDescInContents(self)}); /* show the special descriptions */ (new SpecialDescContentsLister(self)).showList( gActor, nil, lst, 0, 0, infoTab, nil); } /* * Given a visible object info table (from Actor.visibleInfoTable()), * get the list of objects, filtered by the given condition and * sorted by specialDescOrder. */ specialDescList(infoTab, cond) { local lst; /* * get a list of all of the objects in the table - the objects * are the keys, so we just want a list of the keys */ lst = infoTab.keysToList(); /* subset the list for the given condition */ lst = lst.subset(cond); /* * Sort the list in ascending order of specialDescOrder, and * return the result. Where the specialDescOrder is the same, * and one object is inside another, put the outer object earlier * in the list; this will ensure that we'll generally list * objects before their contents, except when the special desc * list order specifically says otherwise. */ return lst.sort(SortAsc, function(a, b) { /* if the list orders are different, go by list order */ if (a.specialDescOrder != b.specialDescOrder) return a.specialDescOrder - b.specialDescOrder; /* * the list order is the same; if one is inside the other, * list the outer one first */ if (a.isIn(b)) return 1; else if (b.isIn(a)) return -1; else return 0; }); } /* -------------------------------------------------------------------- */ /* * "Read" */ dobjFor(Read) { preCond = [objVisible] verify() { /* * reduce the likelihood that they want to read an ordinary * item, but allow it */ logicalRank(50, 'not readable'); /* give slight preference to an object being held */ if (!isIn(gActor)) logicalRank(80, 'not held'); } action() { /* simply show the ordinary description */ actionDobjExamine(); } } /* -------------------------------------------------------------------- */ /* * "Look in" */ dobjFor(LookIn) { preCond = [objVisible] verify() { /* give slight preference to an object being held */ if (!isIn(gActor)) logicalRank(80, 'not held'); } action() { /* show our LOOK IN description */ lookInDesc; } } /* -------------------------------------------------------------------- */ /* * "Search". By default, we make "search obj" do the same thing as * "look in obj". */ dobjFor(Search) asDobjFor(LookIn) /* -------------------------------------------------------------------- */ /* * "Look under" */ dobjFor(LookUnder) { preCond = [objVisible] verify() { } action() { mainReport(¬hingUnderMsg); } } /* -------------------------------------------------------------------- */ /* * "Look behind" */ dobjFor(LookBehind) { preCond = [objVisible] verify() { } action() { mainReport(¬hingBehindMsg); } } /* -------------------------------------------------------------------- */ /* * "Look through" */ dobjFor(LookThrough) { verify() { } action() { mainReport(¬hingThroughMsg); } } /* -------------------------------------------------------------------- */ /* * "Listen to" */ dobjFor(ListenTo) { preCond = [objAudible] verify() { } action() { /* show our "listen" description explicitly */ fromPOV(gActor, gActor, &basicExamineListen, true); } } /* -------------------------------------------------------------------- */ /* * "Smell" */ dobjFor(Smell) { preCond = [objSmellable] verify() { } action() { /* * show our 'smell' description, explicitly showing our * default description if we don't have an Odor association */ fromPOV(gActor, gActor, &basicExamineSmell, true); } } /* -------------------------------------------------------------------- */ /* * "Taste" */ dobjFor(Taste) { /* to taste an object, we have to be able to touch it */ preCond = [touchObj] verify() { /* you *can* taste anything, but for most things it's unlikely */ logicalRank(50, 'not edible'); } action() { /* show our "taste" description */ fromPOV(gActor, gActor, &basicExamineTaste); } } /* -------------------------------------------------------------------- */ /* * "Feel" */ dobjFor(Feel) { /* to feel an object, we have to be able to touch it */ preCond = [touchObj] verify() { } action() { /* show our "feel" description */ fromPOV(gActor, gActor, &basicExamineFeel); } } /* -------------------------------------------------------------------- */ /* * "Take" action */ dobjFor(Take) { preCond = [touchObj, objNotWorn, roomToHoldObj] verify() { /* * if the object is already being held by the actor, it * makes no sense at the moment to take it */ if (isDirectlyIn(gActor)) { /* I'm already holding it, so this is not logical */ illogicalAlready(&alreadyHoldingMsg); } else { local carrier; /* * If the object isn't being held, it's logical to take * it. However, rank objects being carried as less * likely than objects not being carried, because in an * ambiguous situation, it's more likely that an actor * would want to take something not being carried at all * than something already being carried inside another * object. */ if (isIn(gActor)) logicalRank(70, 'already in'); /* * If the object is being carried by another actor, * reduce the likelihood, since taking something from * another actor is usually less likely than taking * something out of one's own possessions or from the * location. */ carrier = getCarryingActor(); if (carrier != nil && carrier != gActor) logicalRank(60, 'other owner'); } /* * verify transfer from the current container hierarchy to * the new container hierarchy */ verifyMoveTo(gActor); } action() { /* move me into the actor's direct contents */ moveInto(gActor); /* issue our default acknowledgment of the command */ defaultReport(&okayTakeMsg); } } /* -------------------------------------------------------------------- */ /* * "Remove" processing. We'll treat "remove dobj" as meaning "take * dobj from ", where is elided and must be * determined. * * This should be overridden in certain cases. For clothing, * "remove dobj" should be the same as "doff dobj". For removable * components, this should imply removing the component from its * container. */ dobjFor(Remove) { preCond = [touchObj, objNotWorn, roomToHoldObj] verify() { /* if we're already held, there's nothing to remove me from */ if (isHeldBy(gActor)) illogicalNow(&cannotRemoveHeldMsg); } action() { askForIobj(TakeFrom); } } /* -------------------------------------------------------------------- */ /* * "Take from" processing */ dobjFor(TakeFrom) { preCond = [touchObj, objNotWorn, roomToHoldObj] verify() { /* * we can only take something from something else if the thing * is inside the other thing */ if (gIobj != nil && !self.isIn(gIobj)) illogicalAlready(gIobj.takeFromNotInMessage); /* treat this otherwise like a regular "take" */ verifyDobjTake(); } check() { /* apply the same checks as for a regular "take" */ checkDobjTake(); } action() { /* we've passed our checks, so process it as a regular "take" */ replaceAction(Take, self); } } iobjFor(TakeFrom) { verify() { /* check what we know about the dobj */ if (gDobj == nil) { /* * We haven't yet resolved the direct object; check the * tentative direct object list, and count us as * illogical if none of the possible direct objects are * in me. */ if (gTentativeDobj.indexWhich({x: x.obj_.isIn(self)}) == nil) illogicalAlready(takeFromNotInMessage); else if (gTentativeDobj.indexWhich( {x: x.obj_.isDirectlyIn(self)}) != nil) logicalRank(150, 'directly in'); } else if (!gDobj.isIn(self)) { /* * the dobj isn't in me, so it's obviously not logical * to take the dobj out of me */ illogicalAlready(takeFromNotInMessage); } else if (gDobj.isDirectlyIn(self)) { /* * it's slightly more likely that they want to remove * the object from its direct container */ logicalRank(150, 'directly in'); } } } /* general message for "take from" when an object isn't in me */ takeFromNotInMessage = &takeFromNotInMsg /* -------------------------------------------------------------------- */ /* * "Drop" verb processing */ dobjFor(Drop) { preCond = [objHeld] verify() { /* the object must be held by the actor, at least indirectly */ if (isIn(gActor)) { /* * it's being held, so dropping it makes sense; verify * transfer from the current container hierarchy to the * new one */ verifyMoveTo(gActor.getDropDestination(self, nil)); } else { /* it's not being held, so this is simply not logical */ illogicalAlready(¬CarryingMsg); } } action() { /* send the object to the actor's drop destination */ gActor.getDropDestination(self, nil) .receiveDrop(self, dropTypeDrop); } } /* -------------------------------------------------------------------- */ /* * "Put In" verb processing. Default objects cannot contain other * objects, but they can be put in arbitrary containers. */ dobjFor(PutIn) { preCond = [objHeld] verify() { /* * It makes no sense to put us in a container we're already * directly in. (It's fine to put it in something it's * indirectly in, though - doing so takes it out of the * intermediate container and moves it directly into the * indirect object.) */ if (gIobj != nil && isDirectlyIn(gIobj)) illogicalAlready(&alreadyPutInMsg); /* can't put in self, obviously */ if (gIobj == self) illogicalSelf(&cannotPutInSelfMsg); /* verify the transfer */ verifyMoveTo(gIobj); } } iobjFor(PutIn) { preCond = [touchObj] verify() { /* by default, objects cannot be put in this object */ illogical(¬AContainerMsg); } } /* -------------------------------------------------------------------- */ /* * "Put On" processing. Default objects cannot have other objects * put on them, but they can be put on surfaces. */ dobjFor(PutOn) { preCond = [objHeld] verify() { /* it makes no sense to put us on a surface we're already on */ if (gIobj != nil && isDirectlyIn(gIobj)) illogicalAlready(&alreadyPutOnMsg); /* can't put on self, obviously */ if (gIobj == self) illogicalSelf(&cannotPutOnSelfMsg); /* verify the transfer */ verifyMoveTo(gIobj); } } iobjFor(PutOn) { preCond = [touchObj] verify() { /* by default, objects cannot be put on this object */ illogical(¬ASurfaceMsg); } } /* -------------------------------------------------------------------- */ /* * "PutUnder" action */ dobjFor(PutUnder) { preCond = [objHeld] verify() { } } iobjFor(PutUnder) { preCond = [touchObj] verify() { illogical(&cannotPutUnderMsg); } } /* -------------------------------------------------------------------- */ /* * "PutBehind" action */ dobjFor(PutBehind) { preCond = [objHeld] verify() { } } iobjFor(PutBehind) { preCond = [touchObj] verify() { illogical(&cannotPutBehindMsg); } } /* -------------------------------------------------------------------- */ /* * "Wear" action */ dobjFor(Wear) { verify() { illogical(¬WearableMsg); } } /* -------------------------------------------------------------------- */ /* * "Doff" action */ dobjFor(Doff) { verify() { illogical(¬DoffableMsg); } } /* -------------------------------------------------------------------- */ /* * "Kiss" */ dobjFor(Kiss) { preCond = [touchObj] verify() { logicalRank(50, 'not kissable'); } action() { mainReport(&cannotKissMsg); } } /* -------------------------------------------------------------------- */ /* * "Ask for" action */ dobjFor(AskFor) { verify() { illogical(¬AddressableMsg, self); } } /* -------------------------------------------------------------------- */ /* * "Talk to" */ dobjFor(TalkTo) { verify() { illogical(¬AddressableMsg, self); } } /* -------------------------------------------------------------------- */ /* * "Give to" action */ dobjFor(GiveTo) { preCond = [objHeld] verify() { /* * if the intended recipient already has the object, there's * no point in trying this */ if (gIobj != nil && isHeldBy(gIobj)) illogicalAlready(&giveAlreadyHasMsg); /* inherit any further processing */ inherited(); } } iobjFor(GiveTo) { preCond = [touchObj] verify() { illogical(&cannotGiveToMsg); } } /* -------------------------------------------------------------------- */ /* * "Show to" action */ dobjFor(ShowTo) { preCond = [objHeld] verify() { /* it's more likely that we want to show something held */ if (isHeldBy(gActor)) { /* I'm being held - use the default logical ranking */ } else if (isIn(gActor)) { /* * the actor isn't hold me, but I am in the actor's * inventory, so reduce the likelihood only slightly */ logicalRank(80, 'not held'); } else { /* * the actor isn't even carrying me, so reduce our * likelihood even more */ logicalRank(70, 'not carried'); } } check() { /* * The direct object must be visible to the indirect object * in order for the indirect object to be shown the direct * object. */ if (!gIobj.canSee(self)) { reportFailure(&actorCannotSeeMsg, gIobj, self); exit; } /* * The actor performing the showing must also be visible to * the indirect object, otherwise the actor wouldn't be able * to attract the indirect object's attention to do the * showing. */ if (!gIobj.canSee(gActor)) { reportFailure(&actorCannotSeeMsg, gIobj, gActor); exit; } } } iobjFor(ShowTo) { verify() { illogical(&cannotShowToMsg); } } /* -------------------------------------------------------------------- */ /* * "Ask about" action */ dobjFor(AskAbout) { verify() { illogical(¬AddressableMsg, self); } } /* -------------------------------------------------------------------- */ /* * "Tell about" action */ dobjFor(TellAbout) { verify() { illogical(¬AddressableMsg, self); } } /* -------------------------------------------------------------------- */ /* * Vague "ask" and "tell" - these are for syntactically incorrect ASK * and TELL phrasings, so that we can provide better error feedback. */ dobjFor(AskVague) { verify() { illogical(&askVagueMsg); } } dobjFor(TellVague) { verify() { illogical(&tellVagueMsg); } } /* -------------------------------------------------------------------- */ /* * "Follow" action */ dobjFor(Follow) { verify() { /* make sure I'm followable to start with */ if (!verifyFollowable()) return; /* it makes no sense to follow myself */ if (gActor == gDobj) illogical(&cannotFollowSelfMsg); /* ask the actor to verify following the object */ gActor.actorVerifyFollow(self); } action() { /* ask the actor to carry out the follow */ gActor.actorActionFollow(self); } } /* * Verify that I'm a followable object. By default, it's not * logical to follow an arbitrary object. If I'm not followable, * this routine will generate an appropriate illogical() explanation * and return nil. If I'm followable, we'll return true. */ verifyFollowable() { illogical(¬FollowableMsg); return nil; } /* -------------------------------------------------------------------- */ /* * "Attack" action. */ dobjFor(Attack) { preCond = [touchObj] verify() { } action() { mainReport(&uselessToAttackMsg); } } /* -------------------------------------------------------------------- */ /* * "Attack with" action - attack with (presumably) a weapon. */ dobjFor(AttackWith) { preCond = [touchObj] /* * it makes as much sense to attack any object as any other, but * by default attacking an object has no effect */ verify() { } action() { mainReport(&uselessToAttackMsg); } } iobjFor(AttackWith) { preCond = [objHeld] verify() { illogical(¬AWeaponMsg); } } /* -------------------------------------------------------------------- */ /* * "Throw" action. By default, we'll simply re-route this to a * "throw at" action. Objects that can meaningfully be thrown * without any specific target can override this. * * Note that we don't apply an preconditions or verification, since * we don't really do anything with the action ourselves. If an * object overrides this, it should add any preconditions and * verifications that are appropriate. */ dobjFor(Throw) { verify() { } action() { askForIobj(ThrowAt); } } /* -------------------------------------------------------------------- */ /* * "Throw ". By default, we simply reject this and * explain that the command to use is THROW AT. With one exception: * we treat THROW as equivalent to THROW AT FLOOR, and use the * default library message for that command instead. */ dobjFor(ThrowDir) { verify() { if (gAction.getDirection() == downDirection) illogicalAlready(&shouldNotThrowAtFloorMsg); } action() { /* * explain that we should use THROW AT (or DROP, in the case * of THROW DOWN) */ reportFailure(gAction.getDirection() == downDirection ? &shouldNotThrowAtFloorMsg : &dontThrowDirMsg); } } /* -------------------------------------------------------------------- */ /* * "Throw at" action */ dobjFor(ThrowAt) { preCond = [objHeld] verify() { /* by default, we can throw something if we can drop it */ verifyMoveTo(gActor.getDropDestination(self, nil)); /* can't throw something at itself */ if (gIobj == self) illogicalSelf(&cannotThrowAtSelfMsg); /* can't throw dobj at iobj if iobj is in dobj */ if (gIobj != nil && gIobj.isIn(self)) illogicalNow(&cannotThrowAtContentsMsg); } action() { /* * process a 'throw' operation, finishing with hitting the * target if we get that far */ processThrow(gIobj, &throwTargetHitWith); } } iobjFor(ThrowAt) { /* by default, anything can be a target */ verify() { } } /* * Process a 'throw' command. This is common handling that can be * used for any sort of throwing (throw at, throw to, throw in, * etc). The projectile is self, and 'target' is the thing we're * throwing at or to. 'hitProp' is the property to call on 'target' * if we reach the target. */ processThrow(target, hitProp) { local path; local stat; /* get the throw path */ path = getThrowPathTo(target); /* traverse the path with throwViaPath */ stat = traversePath( path, {obj, op: throwViaPath(obj, op, target, path)}); /* * If we made it all the way through the path without complaint, * process hitting the target. If something along the path * finished the traversal (by returning nil), we'll consider the * action complete - the object along that path that canceled * the traversal is responsible for having displayed an * appropriate message. */ if (stat) target.(hitProp)(self, path); } /* * Carry out a 'throw' operation along a path. 'self' is the * projectile; 'obj' is the path element being traversed, and 'op' is * the operation being used to traverse the element. 'target' is the * object we're throwing 'self' at. 'path' is the projectile's full * path (in getThrowPathTo format). * * By default, we'll use the standard canThrowViaPath handling (which * invokes the even more basic checkThrowViaPath) to determine if we * can make this traversal. If so, we'll proceed with the throw; * otherwise, we'll stop the throw by calling stopThrowViaPath() and * returning the result. */ throwViaPath(obj, op, target, path) { /* * By default, if we can throw the object through self, return * true to allow the caller to proceed; otherwise, describe the * object as hitting this element and falling to the appropriate * point. */ if (obj.canThrowViaPath(self, target, op)) { /* no objection - allow the traversal to proceed */ return true; } else { /* can't do it - stop the throw and return the result */ return obj.stopThrowViaPath(self, path); } } /* * Process the effect of throwing the object 'projectile' at the * target 'self'. By default, we'll move the projectile to the * target's drop location, and display a message saying that there * was no effect other than the projectile dropping to the floor (or * whatever it drops to). 'path' is the path we took to reach the * target, as returned from getThrowPathTo(); this lets us determine * how we approached the target. */ throwTargetHitWith(projectile, path) { /* * figure out where we fall to when we hit this object, then send * the object being thrown to that location */ getHitFallDestination(projectile, path) .receiveDrop(projectile, new DropTypeThrow(self, path)); } /* * Stop a 'throw' operation along a path. 'self' is the object in * the path that is impassable by 'projectile' according to * canThrowViaPath(), and 'path' is the getThrowPathTo-style path of * objects traversed in the projectile's trajectory. * * The return value is taken as a path traversal continuation * indicator: nil means to stop the traversal, which is to say that * the 'throw' command finishes here. If we don't really want to * stop the traversal, we can return 'true' to let the traversal * continue. * * By default, we'll stop the throw by doing the same thing we would * have done if we had successfully thrown the object at 'self' - the * whole reason we're stopping the throw is that we're in the way, so * the effect is the same as though we were the intended target to * begin with. This is the normal handling when we can't throw * through 'obj' because 'obj' is a closed container or is otherwise * impassable by self when thrown. This can be overridden to provide * different handling if needed. */ stopThrowViaPath(projectile, path) { /* we've been hit */ throwTargetHitWith(projectile, path); /* tell the caller to stop the traversal */ return nil; } /* * Get the "hit-and-fall" destination for a thrown object. This is * called when we interrupt a thrown object's trajectory because * we're in the way of its trajectory. * * For example, if the actor is inside a cage, and tries to throw a * projectile at an object outside the cage, and the cage blocks the * projectile's passage, then this routine is called on the cage to * determine where the projectile ends up. The projectile's ultimate * destination is the hit-and-fall destination for the cage: it's * where the project ends up when it hits me and then falls to the * ground, its trajectory cut short. * * 'thrownObj' is the projectile thrown, 'self' is the target object, * and 'path' is the path the projectile took to reach us. The path * is of the form returned by getThrowPathTo(). Note that the path * could extend beyond 'self', because the original target might have * been a different object - we could simply have interrupted the * projectile's course. */ getHitFallDestination(thrownObj, path) { local prvCont; local prvOp; local idx; local dest; local common; /* find myself in the path */ idx = path.indexOf(self); /* * get the container traversed just before us in the path (it's * two positions before us in the path list, because the path * consists of alternating objects and operators) */ prvCont = path[idx - 2]; /* get the operation that got from the last container to us */ prvOp = path[idx - 1]; /* * If the previous container is within us, we're throwing from * inside to the outside, so the object falls within me; * otherwise, the object bounces off the outside and falls * outside me. * * If the projectile traversed *inward* from the previous item in * the path, then we should simply land in the drop destination * of the previous item itself, since we're coming in through * that item. If the previous item is a peer of ours, though, we * traversed *across* the item, so land in our common direct * container. * * Note that in certain cases, the previous "container" will be * the projectile itself; this happens when the thrower is * throwing the object at itself (as in "throw it at me"). In * these cases, we'll assume that the the thrower is *holding* * rather than containing the object, in which case the object * isn't actually inside the thrower, in which case we want to * use the location's drop destination. */ if (prvCont == thrownObj) { /* throwing object at self - land in my location's drop dest */ dest = (location != nil ? location.getDropDestination(thrownObj, path) : self); } else if (prvCont.isIn(self)) { /* throwing from within - land in my own drop destination */ dest = getDropDestination(thrownObj, path); } else if (prvOp == PathPeer && (common = getCommonDirectContainer(prvCont)) != nil) { /* * We're coming over from a peer, and we found a container in * common with the peer. Land in the common container's drop * destination. */ dest = common.getDropDestination(thrownObj, path); } else { /* * Either we're coming in from a container, or we weren't * able to find a container in common with the peer. In * either case, the projectile lands in the 'drop' * destination of the previous object in the path. */ dest = prvCont.getDropDestination(thrownObj, path); } /* * Whatever we found, give the destination itself a chance to * make any necessary adjustments. */ return dest.adjustThrowDestination(thrownObj, path); } /* -------------------------------------------------------------------- */ /* * "Throw to" action */ dobjFor(ThrowTo) { preCond = [objHeld] verify() { /* * by default, we can throw an object to someone if we can * throw it at them */ verifyDobjThrowAt(); } action() { /* * process a 'throw' operation, finishing with the target * trying to catch the object if we get that far */ processThrow(gIobj, &throwTargetCatch); } } iobjFor(ThrowTo) { verify() { /* by default, we don't want to catch anything */ illogical(&cannotThrowToMsg); } } /* * Process the effect of throwing the object 'obj' to the catcher * 'self'. By default, we'll simply move the projectile into self. */ throwTargetCatch(obj, path) { /* take the object */ obj.moveInto(self); /* generate the default message if we successfully took the object */ if (obj.isDirectlyIn(self)) mainReport(&throwCatchMsg, obj, self); } /* -------------------------------------------------------------------- */ /* * "Dig" action - by default, simply re-reoute to dig-with, since we * generally need a digging implement to dig in anything. Some * objects might want to override this to allow digging without any * implement; a sandy beach, for example, might allow digging in the * sand without a shovel. */ dobjFor(Dig) { preCond = [touchObj] verify() { } action() { askForIobj(DigWith); } } /* -------------------------------------------------------------------- */ /* * "DigWith" action */ dobjFor(DigWith) { preCond = [touchObj] verify() { illogical(&cannotDigMsg); } } iobjFor(DigWith) { preCond = [objHeld] verify() { illogical(&cannotDigWithMsg); } } /* -------------------------------------------------------------------- */ /* * "jump over" */ dobjFor(JumpOver) { verify() { illogical(&cannotJumpOverMsg); } } /* -------------------------------------------------------------------- */ /* * "jump off" */ dobjFor(JumpOff) { verify() { illogical(&cannotJumpOffMsg); } } /* -------------------------------------------------------------------- */ /* * "Push" action */ dobjFor(Push) { preCond = [touchObj] verify() { logicalRank(50, 'not pushable'); } action() { reportFailure(&pushNoEffectMsg); } } /* -------------------------------------------------------------------- */ /* * "Pull" action */ dobjFor(Pull) { preCond = [touchObj] verify() { logicalRank(50, 'not pullable'); } action() { reportFailure(&pullNoEffectMsg); } } /* -------------------------------------------------------------------- */ /* * "Move" action */ dobjFor(Move) { preCond = [touchObj] verify() { logicalRank(50, 'not movable'); } action() { reportFailure(&moveNoEffectMsg); } } /* -------------------------------------------------------------------- */ /* * "MoveWith" action */ dobjFor(MoveWith) { preCond = [iobjTouchObj] verify() { logicalRank(50, 'not movable'); } action() { reportFailure(&moveNoEffectMsg); } } iobjFor(MoveWith) { preCond = [objHeld] verify() { illogical(&cannotMoveWithMsg); } } /* -------------------------------------------------------------------- */ /* * "MoveTo" action */ dobjFor(MoveTo) { preCond = [touchObj] verify() { logicalRank(50, 'not movable'); } action() { reportFailure(&moveToNoEffectMsg); } } /* -------------------------------------------------------------------- */ /* * "Turn" action */ dobjFor(Turn) { preCond = [touchObj] verify() { illogical(&cannotTurnMsg); } } /* -------------------------------------------------------------------- */ /* * "Turn to" action */ dobjFor(TurnTo) { preCond = [touchObj] verify() { illogical(&cannotTurnMsg); } } /* -------------------------------------------------------------------- */ /* * "TurnWith" action */ dobjFor(TurnWith) { preCond = [iobjTouchObj] verify() { illogical(&cannotTurnMsg); } } iobjFor(TurnWith) { preCond = [objHeld] verify() { illogical(&cannotTurnWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Set" action */ dobjFor(Set) { verify() { } action() { askForIobj(PutOn); } } /* -------------------------------------------------------------------- */ /* * "SetTo" action */ dobjFor(SetTo) { preCond = [touchObj] verify() { illogical(&cannotSetToMsg); } } /* -------------------------------------------------------------------- */ /* * "Consult" action */ dobjFor(Consult) { preCond = [touchObj] verify() { illogical(&cannotConsultMsg); } } dobjFor(ConsultAbout) { preCond = [touchObj] verify() { illogical(&cannotConsultMsg); } } /* -------------------------------------------------------------------- */ /* * "Type on" action */ dobjFor(TypeOn) { preCond = [touchObj] verify() { illogical(&cannotTypeOnMsg); } /* * if the verifier is overridden to allow typing on this object, * by default just ask for a missing literal phrase, since we * need something to type on this object */ action() { askForLiteral(TypeLiteralOn); } } /* * "Type on" action */ dobjFor(TypeLiteralOn) { preCond = [touchObj] verify() { illogical(&cannotTypeOnMsg); } } /* -------------------------------------------------------------------- */ /* * "Enter on" action */ dobjFor(EnterOn) { preCond = [touchObj] verify() { illogical(&cannotEnterOnMsg); } } /* -------------------------------------------------------------------- */ /* * "Switch" action */ dobjFor(Switch) { preCond = [touchObj] verify() { illogical(&cannotSwitchMsg); } } /* -------------------------------------------------------------------- */ /* * "Flip" action */ dobjFor(Flip) { preCond = [touchObj] verify() { illogical(&cannotFlipMsg); } } /* -------------------------------------------------------------------- */ /* * "TurnOn" action */ dobjFor(TurnOn) { preCond = [touchObj] verify() { illogical(&cannotTurnOnMsg); } } /* -------------------------------------------------------------------- */ /* * "TurnOff" action */ dobjFor(TurnOff) { preCond = [touchObj] verify() { illogical(&cannotTurnOffMsg); } } /* -------------------------------------------------------------------- */ /* * "Light" action. By default, we treat this as equivalent to * "burn". */ dobjFor(Light) asDobjFor(Burn) /* -------------------------------------------------------------------- */ /* * "Burn". By default, we ask for something to use to burn the * object, since most objects are not self-igniting. */ dobjFor(Burn) { preCond = [touchObj] verify() { /* * although we can in principle burn anything, most things * are unlikely choices for burning */ logicalRank(50, 'not flammable'); } action() { /* rephrase this as a "burn with" command */ askForIobj(BurnWith); } } /* -------------------------------------------------------------------- */ /* * "Burn with" */ dobjFor(BurnWith) { preCond = [touchObj] verify() { illogical(&cannotBurnMsg); } } iobjFor(BurnWith) { preCond = [objHeld] verify() { illogical(&cannotBurnWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Extinguish" */ dobjFor(Extinguish) { preCond = [touchObj] verify() { illogical(&cannotExtinguishMsg); } } /* -------------------------------------------------------------------- */ /* * "AttachTo" action */ dobjFor(AttachTo) { preCond = [touchObj] verify() { illogical(&cannotAttachMsg); } } iobjFor(AttachTo) { preCond = [touchObj] verify() { illogical(&cannotAttachToMsg); } } /* -------------------------------------------------------------------- */ /* * "DetachFrom" action */ dobjFor(DetachFrom) { preCond = [touchObj] verify() { illogical(&cannotDetachMsg); } } iobjFor(DetachFrom) { preCond = [touchObj] verify() { illogical(&cannotDetachFromMsg); } } /* -------------------------------------------------------------------- */ /* * "Detach" action */ dobjFor(Detach) { preCond = [touchObj] verify() { illogical(&cannotDetachMsg); } } /* -------------------------------------------------------------------- */ /* * "Break" action */ dobjFor(Break) { preCond = [touchObj] verify() { illogical(&shouldNotBreakMsg); } } /* -------------------------------------------------------------------- */ /* * "Cut with" action */ dobjFor(CutWith) { preCond = [touchObj] verify() { logicalRank(50, 'not cuttable'); } action() { reportFailure(&cutNoEffectMsg); } } iobjFor(CutWith) { preCond = [touchObj] verify() { illogical(&cannotCutWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Climb", "climb up", and "climb down" actions */ dobjFor(Climb) { preCond = [touchObj] verify() { illogical(&cannotClimbMsg); } } dobjFor(ClimbUp) { preCond = [touchObj] verify() { illogical(&cannotClimbMsg); } } dobjFor(ClimbDown) { preCond = [touchObj] verify() { illogical(&cannotClimbMsg); } } /* -------------------------------------------------------------------- */ /* * "Open" action */ dobjFor(Open) { preCond = [touchObj] verify() { illogical(&cannotOpenMsg); } } /* -------------------------------------------------------------------- */ /* * "Close" action */ dobjFor(Close) { preCond = [touchObj] verify() { illogical(&cannotCloseMsg); } } /* -------------------------------------------------------------------- */ /* * "Lock" action */ dobjFor(Lock) { preCond = [touchObj] verify() { illogical(&cannotLockMsg); } } /* -------------------------------------------------------------------- */ /* * "Unlock" action */ dobjFor(Unlock) { preCond = [touchObj] verify() { illogical(&cannotUnlockMsg); } } /* -------------------------------------------------------------------- */ /* * "LockWith" action */ dobjFor(LockWith) { preCond = [touchObj] verify() { illogical(&cannotLockMsg); } } iobjFor(LockWith) { preCond = [objHeld] verify() { illogical(&cannotLockWithMsg); } } /* -------------------------------------------------------------------- */ /* * "UnlockWith" action */ dobjFor(UnlockWith) { preCond = [touchObj] verify() { illogical(&cannotUnlockMsg); } } iobjFor(UnlockWith) { preCond = [objHeld] verify() { illogical(&cannotUnlockWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Eat" action */ dobjFor(Eat) { /* * generally, an object must be held to be eaten; this can be * overridden on an object-by-object basis as desired */ preCond = [objHeld] verify() { illogical(&cannotEatMsg); } } /* -------------------------------------------------------------------- */ /* * "Drink" action */ dobjFor(Drink) { preCond = [objHeld] verify() { illogical(&cannotDrinkMsg); } } /* -------------------------------------------------------------------- */ /* * "Pour" */ dobjFor(Pour) { preCond = [touchObj] verify() { illogical(&cannotPourMsg); } } /* -------------------------------------------------------------------- */ /* * "Pour into" */ dobjFor(PourInto) { preCond = [touchObj] verify() { illogical(&cannotPourMsg); } } iobjFor(PourInto) { preCond = [touchObj] verify() { illogical(&cannotPourIntoMsg); } } /* -------------------------------------------------------------------- */ /* * "Pour onto" */ dobjFor(PourOnto) { preCond = [touchObj] verify() { illogical(&cannotPourMsg); } } iobjFor(PourOnto) { preCond = [touchObj] verify() { illogical(&cannotPourOntoMsg); } } /* -------------------------------------------------------------------- */ /* * "Clean" action */ dobjFor(Clean) { preCond = [touchObj] verify() { illogical(&cannotCleanMsg); } } /* -------------------------------------------------------------------- */ /* * "CleanWith" action */ dobjFor(CleanWith) { preCond = [touchObj] verify() { illogical(&cannotCleanMsg); } } iobjFor(CleanWith) { preCond = [objHeld] verify() { illogical(&cannotCleanWithMsg); } } /* -------------------------------------------------------------------- */ /* * "SitOn" action */ dobjFor(SitOn) { preCond = [touchObj] verify() { illogical(&cannotSitOnMsg); } } /* -------------------------------------------------------------------- */ /* * "LieOn" action */ dobjFor(LieOn) { preCond = [touchObj] verify() { illogical(&cannotLieOnMsg); } } /* -------------------------------------------------------------------- */ /* * "StandOn" action */ dobjFor(StandOn) { preCond = [touchObj] verify() { illogical(&cannotStandOnMsg); } } /* -------------------------------------------------------------------- */ /* * "Board" action */ dobjFor(Board) { preCond = [touchObj] verify() { illogical(&cannotBoardMsg); } } /* -------------------------------------------------------------------- */ /* * "Get out of" (unboard) action */ dobjFor(GetOutOf) { verify() { illogical(&cannotUnboardMsg); } } /* * "Get off of" action */ dobjFor(GetOffOf) { verify() { illogical(&cannotGetOffOfMsg); } } /* -------------------------------------------------------------------- */ /* * "Fasten" action */ dobjFor(Fasten) { preCond = [touchObj] verify() { illogical(&cannotFastenMsg); } } /* -------------------------------------------------------------------- */ /* * "Fasten to" action */ dobjFor(FastenTo) { preCond = [touchObj] verify() { illogical(&cannotFastenMsg); } } iobjFor(FastenTo) { preCond = [touchObj] verify() { illogical(&cannotFastenToMsg); } } /* -------------------------------------------------------------------- */ /* * "Unfasten" action */ dobjFor(Unfasten) { preCond = [touchObj] verify() { illogical(&cannotUnfastenMsg); } } /* -------------------------------------------------------------------- */ /* * "Unfasten from" action */ dobjFor(UnfastenFrom) { preCond = [touchObj] verify() { illogical(&cannotUnfastenMsg); } } iobjFor(UnfastenFrom) { preCond = [touchObj] verify() { illogical(&cannotUnfastenFromMsg); } } /* -------------------------------------------------------------------- */ /* * "PlugIn" action */ dobjFor(PlugIn) { preCond = [touchObj] verify() { illogical(&cannotPlugInMsg); } } /* -------------------------------------------------------------------- */ /* * "PlugInto" action */ dobjFor(PlugInto) { preCond = [touchObj] verify() { illogical(&cannotPlugInMsg); } } iobjFor(PlugInto) { preCond = [touchObj] verify() { illogical(&cannotPlugInToMsg); } } /* -------------------------------------------------------------------- */ /* * "Unplug" action */ dobjFor(Unplug) { preCond = [touchObj] verify() { illogical(&cannotUnplugMsg); } } /* -------------------------------------------------------------------- */ /* * "UnplugFrom" action */ dobjFor(UnplugFrom) { preCond = [touchObj] verify() { illogical(&cannotUnplugMsg); } } iobjFor(UnplugFrom) { preCond = [touchObj] verify() { illogical(&cannotUnplugFromMsg); } } /* -------------------------------------------------------------------- */ /* * "Screw" action */ dobjFor(Screw) { preCond = [touchObj] verify() { illogical(&cannotScrewMsg); } } /* -------------------------------------------------------------------- */ /* * "ScrewWith" action */ dobjFor(ScrewWith) { preCond = [iobjTouchObj] verify() { illogical(&cannotScrewMsg); } } iobjFor(ScrewWith) { preCond = [objHeld] verify() { illogical(&cannotScrewWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Unscrew" action */ dobjFor(Unscrew) { preCond = [touchObj] verify() { illogical(&cannotUnscrewMsg); } } /* -------------------------------------------------------------------- */ /* * "UnscrewWith" action */ dobjFor(UnscrewWith) { preCond = [iobjTouchObj] verify() { illogical(&cannotUnscrewMsg); } } iobjFor(UnscrewWith) { preCond = [objHeld] verify() { illogical(&cannotUnscrewWithMsg); } } /* -------------------------------------------------------------------- */ /* * "Enter" */ dobjFor(Enter) { preCond = [touchObj] verify() { illogical(&cannotEnterMsg); } } /* * "Go through" */ dobjFor(GoThrough) { preCond = [touchObj] verify() { illogical(&cannotGoThroughMsg); } } /* -------------------------------------------------------------------- */ /* * Push in Direction action - this is for commands like "push * boulder north" or "drag sled into cave". */ dobjFor(PushTravel) { preCond = [touchObj] verify() { illogical(&cannotPushTravelMsg); } } /* * For all of the two-object forms, map these using our general * push-travel mapping. We do all of this mapping here, rather than * in the action definition, so that individual objects can change * the meanings of these verbs for special cases as appropriate. */ mapPushTravelHandlers(PushTravelThrough, GoThrough) mapPushTravelHandlers(PushTravelEnter, Enter) mapPushTravelHandlers(PushTravelGetOutOf, GetOutOf) mapPushTravelHandlers(PushTravelClimbUp, ClimbUp) mapPushTravelHandlers(PushTravelClimbDown, ClimbDown) ; frobtads-1.2.3/tads3/lib/adv3/en_us/0000755000175000001440000000000012145614112016233 5ustar realncusersfrobtads-1.2.3/tads3/lib/adv3/en_us/en_us.tl0000644000175000001440000000034607602357740017725 0ustar realncusersname: TADS 3 English Language Definitions source: en_us source: instruct needmacro: MESSAGESTYLE this selects the style to use for library messages: use "neu" for the standard "neutral narrator" style source: msg_$(MESSAGESTYLE) frobtads-1.2.3/tads3/lib/adv3/en_us/msg_neu.t0000644000175000001440000060142111765663244020102 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - "neutral" messages for US English * * This module provides standard library messages with a parser/narrator * that's as invisible (neutral) as possible. These messages are designed * to reduce the presence of the computer as mediator in the story, to * give the player the most direct contact that we can with the scenario. * * The parser almost always refers to itself in the third person (by * calling itself something like "this story") rather than in the first * person, and, whenever possible, avoids referring to itself in the first * place. Our ideal phrasing is either second-person, describing things * directly in terms of the player character's experience, or "no-person," * simply describing things without mentioning the speaker or listener at * all. For example, rather than saying "I don't see that here," we say * "you don't see that here," or "that's not here." We occasionally stray * from this ideal where achieving it would be too awkward. * * In the earliest days of adventure games, the parser was usually a * visible presence: the early parsers frequently reported things in the * first person, and some even had specific personalities. This * conspicuous parser style has become less prevalent in modern games, * though, and authors now usually prefer to treat the parser as just * another part of the user interface, which like all good UI's is best * when the user doesn't notice it. */ #include "adv3.h" #include "en_us.h" /* ------------------------------------------------------------------------ */ /* * Build a message parameter string with the given parameter type and * name. * * This is useful when we have a name from a variable, and we need to * build the message substitution string for embedding in a larger * string. We can't just embed the name variable using <>, because * that would process the output piecewise - the output filter needs to * see the whole {typ var} expression in one go. So, instead of writing * this: * *. {The/he <>} {is} ... * * write this: * *. <> {is} ... */ buildParam(typeString, nm) { return '{' + typeString + ' ' + nm + '}'; } /* * Synthesize a message parameter, and build it into a parameter string * with the given substitution type. * * For example, buildSynthParam('abc', obj) returns '{abc xxx}', where * 'xxx' is a synthesized message parameter name (created using * gSynthMessageParam) for the object obj. */ buildSynthParam(typeString, obj) { return '{' + typeString + ' ' + gSynthMessageParam(obj) + '}'; } /* ------------------------------------------------------------------------ */ /* * Library Messages */ libMessages: MessageHelper /* * The pronoun to use for the objective form of the personal * interrogative pronoun. Strictly speaking, the right word for * this usage is "whom"; but regardless of what the grammar books * say, most American English speakers these days use "who" for both * the subjective and objective cases; to many ears, "whom" sounds * old-fashioned, overly formal, or even pretentious. (Case in * point: a recent television ad tried to make a little kid look * ultra-sophisticated by having him answer the phone by asking * "*whom* may I ask is calling?", with elaborate emphasis on the * "whom." Of course, the correct usage in this case is "who," so * the ad only made the kid look pretentious. It goes to show that, * at least in the mind of the writer of the ad, "whom" is just the * snooty, formal version of "who" that serves only to signal the * speaker's sophistication.) * * By default, we distinguish "who" and "whom." Authors who prefer * to use "who" everywhere can do so by changing this property's * value to 'who'. */ whomPronoun = 'whom' /* * Flag: offer an explanation of the "OOPS" command when it first * comes up. We'll only show this the first time the player enters * an unknown word. If you never want to offer this message at all, * simply set this flag to nil initially. * * See also oopsNote() below. */ offerOopsNote = true /* * some standard commands for insertion into tags - these are in * the messages so they can translated along with the command set */ commandLookAround = 'look around' commandFullScore = 'full score' /* announce a completely remapped action */ announceRemappedAction(action) { return '<./p0>\n<.assume>' + action.getParticiplePhrase() + '<./assume>\n'; } /* * Get a string to announce an implicit action. This announces the * current global action. 'ctx' is an ImplicitAnnouncementContext * object describing the context in which the message is being * displayed. */ announceImplicitAction(action, ctx) { /* build the announcement from the basic verb phrase */ return ctx.buildImplicitAnnouncement(action.getImplicitPhrase(ctx)); } /* * Announce a silent implied action. This allows an implied action * to work exactly as normal (including the suppression of a default * response message), but without any announcement of the implied * action. */ silentImplicitAction(action, ctx) { return ''; } /* * Get a string to announce that we're implicitly moving an object to * a bag of holding to make room for taking something new. If * 'trying' is true, it means we want to phrase the message as merely * trying the action, not actually performing it. */ announceMoveToBag(action, ctx) { /* build the announcement, adding an explanation */ return ctx.buildImplicitAnnouncement( action.getImplicitPhrase(ctx) + ' to make room'); } /* show a library credit (for a CREDITS listing) */ showCredit(name, byline) { "<> <>"; } /* show a library version number (for a VERSION listing) */ showVersion(name, version) { "<> version <>"; } /* there's no "about" information in this game */ noAboutInfo = "<.parser>This story has no ABOUT information.<./parser> " /* * Show a list state name - this is extra state information that we * show for an object in a listing involving the object. For * example, a light source might add a state like "(providing * light)". We simply show the list state name in parentheses. */ showListState(state) { " (<>)"; } /* a set of equivalents are all in a given state */ allInSameListState(lst, stateName) { " (<> <>)"; } /* generic long description of a Thing from a distance */ distantThingDesc(obj) { gMessageParams(obj); "{It's obj} too far away to make out any detail. "; } /* generic long description of a Thing under obscured conditions */ obscuredThingDesc(obj, obs) { gMessageParams(obj, obs); "{You/he} {can\'t} make out any detail through {the obs/him}. "; } /* generic "listen" description of a Thing at a distance */ distantThingSoundDesc(obj) { "{You/he} {can\'t} hear any detail from this distance. "; } /* generic obscured "listen" description */ obscuredThingSoundDesc(obj, obs) { gMessageParams(obj, obs); "{You/he} {can\'t} hear any detail through {the obs/him}. "; } /* generic "smell" description of a Thing at a distance */ distantThingSmellDesc(obj) { "{You/he} {can\'t} smell much at this distance. "; } /* generic obscured "smell" description */ obscuredThingSmellDesc(obj, obs) { gMessageParams(obj, obs); "{You/he} {can\'t} smell much through {the obs/him}. "; } /* generic "taste" description of a Thing */ thingTasteDesc(obj) { gMessageParams(obj); "{It/he obj} taste{s/d} much as {you/he} would {|have} expect{|ed}. "; } /* generic "feel" description of a Thing */ thingFeelDesc(obj) { "{You/he} {feel[s]|felt} nothing out of the ordinary. "; } /* obscured "read" description */ obscuredReadDesc(obj) { gMessageParams(obj); "{You/he} {can\'t} see {that obj/him} well enough to read {it/him}. "; } /* dim light "read" description */ dimReadDesc(obj) { gMessageParams(obj); "There{’s| was} not enough light to read {that obj/him}. "; } /* lit/unlit match description */ litMatchDesc(obj) { "\^<> lit. "; } unlitMatchDesc(obj) { "\^<> an ordinary match. "; } /* lit candle description */ litCandleDesc(obj) { "\^<> lit. "; } /* * Prepositional phrases for putting objects into different types of * objects. */ putDestContainer(obj) { return 'into ' + obj.theNameObj; } putDestSurface(obj) { return 'onto ' + obj.theNameObj; } putDestUnder(obj) { return 'under ' + obj.theNameObj; } putDestBehind(obj) { return 'behind ' + obj.theNameObj; } putDestFloor(obj) { return 'to ' + obj.theNameObj; } putDestRoom(obj) { return 'into ' + obj.theNameObj; } /* the list separator character in the middle of a list */ listSepMiddle = ", " /* the list separator character for a two-element list */ listSepTwo = " and " /* the list separator for the end of a list of at least three elements */ listSepEnd = ", and " /* * the list separator for the middle of a long list (a list with * embedded lists not otherwise set off, such as by parentheses) */ longListSepMiddle = "; " /* the list separator for a two-element list of sublists */ longListSepTwo = ", and " /* the list separator for the end of a long list */ longListSepEnd = "; and " /* show the basic score message */ showScoreMessage(points, maxPoints, turns) { "In <> move<>, you have scored <> of a possible <> point<< maxPoints == 1 ? '' : 's'>>. "; } /* show the basic score message with no maximum */ showScoreNoMaxMessage(points, turns) { "In <> move<>, you have scored <> point<>. "; } /* show the full message for a given score rank string */ showScoreRankMessage(msg) { "This makes you <>. "; } /* * show the list prefix for the full score listing; this is shown on * a line by itself before the list of full score items, shown * indented and one item per line */ showFullScorePrefix = "Your score consists of:" /* * show the item prefix, with the number of points, for a full score * item - immediately after this is displayed, we'll display the * description message for the achievement */ fullScoreItemPoints(points) { "<> point<> for "; } /* score change - first notification */ firstScoreChange(delta) { scoreChange(delta); scoreChangeTip.showTip(); } /* score change - notification other than the first time */ scoreChange(delta) { "<.commandsep><.notification><< basicScoreChange(delta)>><./notification> "; } /* * basic score change notification message - this is an internal * service routine for scoreChange and firstScoreChange */ basicScoreChange(delta) { "Your <> has just < 0 ? 'in' : 'de'>>creased by < 0 ? delta : -delta)>> point<>."; } /* acknowledge turning tips on or off */ acknowledgeTipStatus(stat) { "<.parser>Tips are now turned <>.<./parser> "; } /* describe the tip mode setting */ tipStatusShort(stat) { "TIPS <>"; } /* get the string to display for a footnote reference */ footnoteRef(num) { /* set up a hyperlink for the note that enters the "note n" command */ return '[<>]'; } /* first footnote notification */ firstFootnote() { footnotesTip.showTip(); } /* there is no such footnote as the given number */ noSuchFootnote(num) { "<.parser>The story has never referred to any such footnote.<./parser> "; } /* show the current footnote status */ showFootnoteStatus(stat) { "The current setting is FOOTNOTES "; switch(stat) { case FootnotesOff: "OFF, which hides all footnote references. Type <> to show references to footnotes except those you’ve already seen, or <> to show all footnote references. "; break; case FootnotesMedium: "MEDIUM, which shows references to unread footnotes, but hides references to those you’ve already read. Type <> to hide footnote references entirely, or <> to show every reference, even to notes you’ve already read. "; break; case FootnotesFull: "FULL, which shows every footnote reference, even to notes you’ve already read. Type <> to show only references to notes you haven’t yet read, or << aHref('footnotes off', 'FOOTNOTES OFF', 'Turn off footnotes')>> to hide footnote references entirely. "; break; } } /* acknowledge a change in the footnote status */ acknowledgeFootnoteStatus(stat) { "<.parser>The setting is now <>.<./parser> "; } /* show the footnote status, in short form */ shortFootnoteStatus(stat) { "FOOTNOTES << stat == FootnotesOff ? 'OFF' : stat == FootnotesMedium ? 'MEDIUM' : 'FULL' >>"; } /* * Show the main command prompt. * * 'which' is one of the rmcXxx phase codes indicating what kind of * command we're reading. This default implementation shows the * same prompt for every type of input, but games can use the * 'which' value to show different prompts for different types of * queries, if desired. */ mainCommandPrompt(which) { "\b>"; } /* * Show a pre-resolved error message string. This simply displays * the given string. */ parserErrorString(actor, msg) { say(msg); } /* show the response to an empty command line */ emptyCommandResponse = "<.parser>I beg your pardon?<./parser> " /* invalid token (i.e., punctuation) in command line */ invalidCommandToken(ch) { "<.parser>The story doesn’t know how to use the character ‘<>’ in a command.<./parser> "; } /* * Command group prefix - this is displayed after a command line and * before the first command results shown after the command line. * * By default, we'll show the "zero-space paragraph" marker, which * acts like a paragraph break in that it swallows up immediately * following paragraph breaks, but doesn't actually add any space. * This will ensure that we don't add any space between the command * input line and the next text. */ commandResultsPrefix = '<.p0>' /* * Command "interruption" group prefix. This is displayed after an * interrupted command line - a command line editing session that * was interrupted by a timeout event - just before the text that * interrupted the command line. * * By default, we'll show a paragraph break here, to set off the * interrupting text from the command line under construction. */ commandInterruptionPrefix = '<.p>' /* * Command separator - this is displayed after the results from a * command when another command is about to be executed without any * more user input. That is, when a command line contains more than * one command, this message is displayed between each successive * command, to separate the results visually. * * This is not shown before the first command results after a * command input line, and is not shown after the last results * before a new input line. Furthermore, this is shown only between * adjacent commands for which output actually occurs; if a series * of commands executes without any output, we won't show any * separators between the silent commands. * * By default, we'll just start a new paragraph. */ commandResultsSeparator = '<.p>' /* * "Complex" result separator - this is displayed between a group of * messages for a "complex" result set and adjoining messages. A * command result list is "complex" when it's built up out of * several generated items, such as object identification prefixes * or implied command prefixes. We use additional visual separation * to set off these groups of messages from adjoining messages, * which is especially important for commands on multiple objects, * where we would otherwise have several results shown together. By * default, we use a paragraph break. */ complexResultsSeparator = '<.p>' /* * Internal results separator - this is displayed to visually * separate the results of an implied command from the results for * the initiating command, which are shown after the results from * the implied command. By default, we show a paragraph break. */ internalResultsSeparator = '<.p>' /* * Command results suffix - this is displayed just before a new * command line is about to be read if any command results have been * shown since the last command line. * * By default, we'll show nothing extra. */ commandResultsSuffix = '' /* * Empty command results - this is shown when we read a command line * and then go back and read another without having displaying * anything. * * By default, we'll return a message indicating that nothing * happened. */ commandResultsEmpty = ('Nothing obvious happen' + tSel('s', 'ed') + '.<.p>') /* * Intra-command report separator. This is used to separate report * messages within a single command's results. By default, we show * a paragraph break. */ intraCommandSeparator = '<.p>' /* * separator for "smell" results - we ordinarily show each item's * odor description as a separate paragraph */ smellDescSeparator() { "<.p>"; } /* * separator for "listen" results */ soundDescSeparator() { "<.p>"; } /* a command was issued to a non-actor */ cannotTalkTo(targetActor, issuingActor) { "\^<> not something <> <> talk to. "; } /* greeting actor while actor is already talking to us */ alreadyTalkingTo(actor, greeter) { "\^<> already <> <> attention. "; } /* no topics to suggest when we're not talking to anyone */ noTopicsNotTalking = "<.parser>{You're} not currently talking to anyone.<./parser> " /* * Show a note about the OOPS command. This is, by default, added * to the "I don't know that word" error the first time that error * occurs. */ oopsNote() { oopsTip.showTip(); } /* can't use OOPS right now */ oopsOutOfContext = "<.parser>You can only use OOPS to correct a misspelling immediately after the story points out a word it doesn’t know.<./parser> " /* OOPS in context, but without the word to correct */ oopsMissingWord = "<.parser>To use OOPS to correct a misspelling, put the corrected word after OOPS, as in OOPS BOOK.<./parser> " /* acknowledge setting VERBOSE mode (true) or TERSE mode (nil) */ acknowledgeVerboseMode(verbose) { if (verbose) "<.parser>VERBOSE mode is now selected.<./parser> "; else "<.parser>TERSE mode is now selected.<./parser> "; } /* show the current VERBOSE setting, in short form */ shortVerboseStatus(stat) { "<> mode"; } /* show the current score notify status */ showNotifyStatus(stat) { "<.parser>Score notifications are currently <>.<./parser> "; } /* show the current score notify status, in short form */ shortNotifyStatus(stat) { "NOTIFY <>"; } /* acknowledge a change in the score notification status */ acknowledgeNotifyStatus(stat) { "<.parser>Score notifications are now <>.<./parser> "; } /* * Announce the current object of a set of multiple objects on which * we're performing an action. This is used to tell the player * which object we're acting upon when we're iterating through a set * of objects specified in a command targeting multiple objects. */ announceMultiActionObject(obj, whichObj, action) { /* * get the display name - we only need to differentiate this * object from the other objects in the iteration */ local nm = obj.getAnnouncementDistinguisher( action.getResolvedObjList(whichObj)).name(obj); /* build the announcement */ return '<./p0>\n<.announceObj>' + nm + ':<./announceObj> <.p0>'; } /* * Announce a singleton object that we selected from a set of * ambiguous objects. This is used when we disambiguate a command * and choose an object over other objects that are also logical but * are less likely. In such cases, it's courteous to tell the * player what we chose, because it's possible that the user meant * one of the other logical objects - announcing this type of choice * helps reduce confusion by making it immediately plain to the * player when we make a choice other than what they were thinking. */ announceAmbigActionObject(obj, whichObj, action) { /* * get the display name - distinguish the object from everything * else in scope, since we chose from a set of ambiguous options */ local nm = obj.getAnnouncementDistinguisher(gActor.scopeList()) .theName(obj); /* announce the object in "assume" style, ending with a newline */ return '<.assume>' + nm + '<./assume>\n'; } /* * Announce a singleton object we selected as a default for a * missing noun phrase. * * 'resolvedAllObjects' indicates where we are in the command * processing: this is true if we've already resolved all of the * other objects in the command, nil if not. We use this * information to get the phrasing right according to the situation. */ announceDefaultObject(obj, whichObj, action, resolvedAllObjects) { /* * put the action's default-object message in "assume" style, * and start a new line after it */ return '<.assume>' + action.announceDefaultObject(obj, whichObj, resolvedAllObjects) + '<./assume>\n'; } /* 'again' used with no prior command */ noCommandForAgain() { "<.parser>There’s nothing to repeat.<./parser> "; } /* 'again' cannot be directed to a different actor */ againCannotChangeActor() { "<.parser>To repeat a command like turtle, go north, just say again, not turtle, again.<./parser> "; } /* 'again': can no longer talk to target actor */ againCannotTalkToTarget(issuer, target) { "\^<> <> repeat that command. "; } /* the last command cannot be repeated in the present context */ againNotPossible(issuer) { "That command cannot be repeated now. "; } /* system actions cannot be directed to non-player characters */ systemActionToNPC() { "<.parser>This command cannot be directed to another character in the story.<./parser> "; } /* confirm that we really want to quit */ confirmQuit() { "Do you really want to quit?\ (<> is affirmative) >\ "; } /* * QUIT message. We display this to acknowledge an explicit player * command to quit the game. This is the last message the game * displays on the way out; there is no need to offer any options at * this point, because the player has decided to exit the game. * * By default, we show nothing; games can override this to display an * acknowledgment if desired. Note that this isn't a general * end-of-game 'goodbye' message; the library only shows this to * acknowledge an explicit QUIT command from the player. */ okayQuitting() { } /* * "not terminating" confirmation - this is displayed when the * player doesn't acknowledge a 'quit' command with an affirmative * response to our confirmation question */ notTerminating() { "<.parser>Continuing the story.<./parser> "; } /* confirm that they really want to restart */ confirmRestart() { "Do you really want to start over?\ (<> is affirmative) >\ "; } /* "not restarting" confirmation */ notRestarting() { "<.parser>Continuing the story.<./parser> "; } /* * Show a game-finishing message - we use the conventional "*** You * have won! ***" format that text games have been using since the * dawn of time. */ showFinishMsg(msg) { "<.p>*** <>\ ***<.p>"; } /* standard game-ending messages for the common outcomes */ finishDeathMsg = '{YOU/HE pc} {[HAVE]|} DIED' finishVictoryMsg = ('YOU ' + tSel('HAVE ', '') + 'WON') finishFailureMsg = ('YOU ' + tSel('HAVE ', '') + 'FAILED') finishGameOverMsg = 'GAME OVER' /* * Get the save-game file prompt. Note that this must return a * single-quoted string value, not display a value itself, because * this prompt is passed to inputFile(). */ getSavePrompt = 'Save game to file' /* get the restore-game prompt */ getRestorePrompt = 'Restore game from file' /* successfully saved */ saveOkay() { "<.parser>Saved.<./parser> "; } /* save canceled */ saveCanceled() { "<.parser>Canceled.<./parser> "; } /* saved failed due to a file write or similar error */ saveFailed(exc) { "<.parser>Failed; your computer might be running low on disk space, or you might not have the necessary permissions to write this file.<./parser> "; } /* save failed due to storage server request error */ saveFailedOnServer(exc) { "<.parser>Failed, because of a problem accessing the storage server: <><./parser>"; } /* * make an error message into a sentence, by capitalizing the first * letter and adding a period at the end if it doesn't already have * one */ makeSentence(msg) { return rexReplace( ['^*[a-z]', '(?<=[^.?! ])*$'], msg, [{m: m.toUpper()}, '.']); } /* note that we're restoring at startup via a saved-position launch */ noteMainRestore() { "<.parser>Restoring saved game...<./parser>\n"; } /* successfully restored */ restoreOkay() { "<.parser>Restored.<./parser> "; } /* restore canceled */ restoreCanceled() { "<.parser>Canceled.<./parser> "; } /* restore failed due to storage server request error */ restoreFailedOnServer(exc) { "<.parser>Failed, because of a problem accessing the storage server: <><./parser>"; } /* restore failed because the file was not a valid saved game file */ restoreInvalidFile() { "<.parser>Failed: this is not a valid saved position file.<./parser> "; } /* restore failed because the file was corrupted */ restoreCorruptedFile() { "<.parser>Failed: this saved state file appears to be corrupted. This can occur if the file was modified by another program, or the file was copied between computers in a non-binary transfer mode, or the physical media storing the file were damaged.<./parser> "; } /* restore failed because the file was for the wrong game or version */ restoreInvalidMatch() { "<.parser>Failed: the file was not saved by this story (or was saved by an incompatible version of the story).<./parser> "; } /* restore failed for some reason other than those distinguished above */ restoreFailed(exc) { "<.parser>Failed: the position could not be restored.<./parser> "; } /* error showing the input file dialog (or character-mode equivalent) */ filePromptFailed() { "<.parser>A system error occurred asking for a filename. Your computer might be running low on memory, or might have a configuration problem.<./parser> "; } /* error showing the input file dialog, with a system error message */ filePromptFailedMsg(msg) { "<.parser>Failed: <><./parser> "; } /* Web UI inputFile error: uploaded file is too large */ webUploadTooBig = 'The file you selected is too large to upload.' /* PAUSE prompt */ pausePrompt() { "<.parser>The story is now paused. Please press the space bar when you are ready to resume the story, or press the ‘S’ key to save the current position.<./parser><.p>"; } /* saving from within a pause */ pauseSaving() { "<.parser>Saving the story...<./parser><.p>"; } /* PAUSE ended */ pauseEnded() { "<.parser>Resuming the story.<./parser> "; } /* acknowledge starting an input script */ inputScriptOkay(fname) { "<.parser>Reading commands from << File.getRootName(fname).htmlify()>>...<./parser>\n "; } /* error opening input script */ inputScriptFailed = "<.parser>Failed; the script input file could not be opened.<./parser> " /* exception opening input script */ inputScriptFailedException(exc) { "<.parser>Failed; <><./parser> "; } /* get the scripting inputFile prompt message */ getScriptingPrompt = 'Please select a name for the new script file' /* acknowledge scripting on */ scriptingOkay() { "<.parser>The transcript will be saved to the file. Type <> to discontinue scripting.<./parser> "; } scriptingOkayWebTemp() { "<.parser>The transcript will be saved. Type <> to discontinue scripting and download the saved transcript.<./parser> "; } /* scripting failed */ scriptingFailed = "<.parser>Failed; an error occurred opening the script file.<./parser> " /* scripting failed with an exception */ scriptingFailedException(exc) { "<.parser>Failed; <><./parser>"; } /* acknowledge cancellation of script file dialog */ scriptingCanceled = "<.parser>Canceled.<./parser> " /* acknowledge scripting off */ scriptOffOkay = "<.parser>Scripting ended.<./parser> " /* SCRIPT OFF ignored because we're not in a script file */ scriptOffIgnored = "<.parser>No script is currently being recorded.<./parser> " /* get the RECORD prompt */ getRecordingPrompt = 'Please select a name for the new command log file' /* acknowledge recording on */ recordingOkay = "<.parser>Commands will now be recorded. Type <> to stop recording commands.<.parser> " /* recording failed */ recordingFailed = "<.parser>Failed; an error occurred opening the command recording file.<./parser> " /* recording failed with exception */ recordingFailedException(exc) { "<.parser>Failed; <><./parser> "; } /* acknowledge cancellation */ recordingCanceled = "<.parser>Canceled.<./parser> " /* recording turned off */ recordOffOkay = "<.parser>Command recording ended.<./parser> " /* RECORD OFF ignored because we're not recording commands */ recordOffIgnored = "<.parser>No command recording is currently being made.<./parser> " /* REPLAY prompt */ getReplayPrompt = 'Please select the command log file to replay' /* REPLAY file selection canceled */ replayCanceled = "<.parser>Canceled.<./parser> " /* undo command succeeded */ undoOkay(actor, cmd) { "<.parser>Taking back one turn: "; /* show the target actor prefix, if an actor was specified */ if (actor != nil) "<>, "; /* show the command */ "<>.<./parser><.p>"; } /* undo command failed */ undoFailed() { "<.parser>No more undo information is available.<./parser> "; } /* comment accepted, with or without transcript recording in effect */ noteWithScript = "<.parser>Comment recorded.<./parser> " noteWithoutScript = "<.parser>Comment not recorded.<./parser> " /* on the first comment without transcript recording, warn about it */ noteWithoutScriptWarning = "<.parser>Comment not recorded. Use <> if you want to start recording the transcript.<./parser> " /* invalid finishGame response */ invalidFinishOption(resp) { "\bThat isn’t one of the options. "; } /* acknowledge new "exits on/off" status */ exitsOnOffOkay(stat, look) { if (stat && look) "<.parser>The list of exits will now be shown in both the status line and in each room description.<./parser> "; else if (!stat && !look) "<.parser>The list of exits will no longer be shown in either the status line or room description.<./parser> "; else "<.parser>The list of exits <> be shown in the status line, and <> be included in room descriptions.<./parser> "; } /* explain how to turn exit display on and off */ explainExitsOnOff() { exitsTip.showTip(); } /* describe the current EXITS settings */ currentExitsSettings(statusLine, roomDesc) { "EXITS "; if (statusLine && roomDesc) "ON"; else if (statusLine) "STATUSLINE"; else if (roomDesc) "LOOK"; else "OFF"; } /* acknowledge HINTS OFF */ hintsDisabled = '<.parser>Hints are now disabled.<./parser> ' /* rebuff a request for hints when they've been previously disabled */ sorryHintsDisabled = '<.parser>Sorry, but hints have been disabled for this session, as you requested. If you’ve changed your mind, you’ll have to save your current position, exit the TADS interpreter, and start a new interpreter session.<./parser> ' /* this game has no hints */ hintsNotPresent = '<.parser>Sorry, this story doesn’t have any built-in hints.<./parser> ' /* there are currently no hints available (but there might be later) */ currentlyNoHints = '<.parser>Sorry, no hints are currently available. Please check back later.<./parser> ' /* show the hint system warning */ showHintWarning = "<.notification>Warning: Some people don’t like built-in hints, since the temptation to ask for help prematurely can become overwhelming when hints are so close at hand. If you’re worried that your willpower won’t hold up, you can disable hints for the rest of this session by typing <>. If you still want to see the hints now, type <>.<./notification> " /* done with hints */ hintsDone = '<.parser>Done.<./parser> ' /* optional command is not supported in this game */ commandNotPresent = "<.parser>That command isn’t needed in this story.<./parser> " /* this game doesn't use scoring */ scoreNotPresent = "<.parser>This story doesn’t use scoring.<./parser> " /* mention the FULL SCORE command */ mentionFullScore() { fullScoreTip.showTip(); } /* SAVE DEFAULTS successful */ savedDefaults() { "<.parser>Your current settings have been stored as the default settings for new games. The saved settings are: "; /* show all of the settings */ settingsUI.showAll(); ". Most newer games will apply these settings automatically whenever you start (or RESTART) the game, but note that older games will not.<./parser> "; } /* RESTORE DEFAULTS successful */ restoredDefaults() { "<.parser>The saved default settings have been put into effect. The new settings are: "; /* show all of the settings */ settingsUI.showAll(); ".<./parser> "; } /* show a separator for the settingsUI.showAll() list */ settingsItemSeparator = "; " /* SAVE/RESTORE DEFAULTS not supported (old interpreter version) */ defaultsFileNotSupported = "<.parser>Sorry, but the version of the TADS interpreter you’re using doesn’t support saving or restoring defaults. You must install a more recent version in order to use this feature.<./parser> " /* RESTORE DEFAULTS file open/read error */ defaultsFileReadError(exc) { "<.parser>An error occurred reading the default settings file. The global defaults can't be restored.<./parser> "; } /* SAVE DEFAULTS file creation error */ defaultsFileWriteError = "<.parser>An error occurred writing the default settings file. The defaults have not been saved. You might be out of disk space, or you might not have the necessary permissions to write the file.<./parser> " /* * Command key list for the menu system. This uses the format * defined for MenuItem.keyList in the menu system. Keys must be * given as lower-case in order to match input, since the menu * system converts input keys to lower case before matching keys to * this list. * * Note that the first item in each list is what will be given in * the navigation menu, which is why the fifth list contains 'ENTER' * as its first item, even though this will never match a key press. */ menuKeyList = [ ['q'], ['p', '[left]', '[bksp]', '[esc]'], ['u', '[up]'], ['d', '[down]'], ['ENTER', '\n', '[right]', ' '] ] /* link title for 'previous menu' navigation link */ prevMenuLink = 'Previous' /* link title for 'next topic' navigation link in topic lists */ nextMenuTopicLink = 'Next' /* * main prompt text for text-mode menus - this is displayed each * time we ask for a keystroke to navigate a menu in text-only mode */ textMenuMainPrompt(keylist) { "\bSelect a topic number, or press ‘<< keylist[M_PREV][1]>>’ for the previous menu or ‘<>’ to quit:\ "; } /* prompt text for topic lists in text-mode menus */ textMenuTopicPrompt() { "\bPress the space bar to display the next line, ‘P’ to go to the previous menu, or ‘Q’ to quit.\b"; } /* * Position indicator for topic list items - this is displayed after * a topic list item to show the current item number and the total * number of items in the list, to give the user an idea of where * they are in the overall list. */ menuTopicProgress(cur, tot) { " [<>/<>]"; } /* * Message to display at the end of a topic list. We'll display * this after we've displayed all available items from a * MenuTopicItem's list of items, to let the user know that there * are no more items available. */ menuTopicListEnd = '[The End]' /* * Message to display at the end of a "long topic" in the menu * system. We'll display this at the end of the long topic's * contents. */ menuLongTopicEnd = '[The End]' /* * instructions text for banner-mode menus - this is displayed in * the instructions bar at the top of the screen, above the menu * banner area */ menuInstructions(keylist, prevLink) { "\^<>=Quit \^<< keylist[M_PREV][1]>>=Previous Menu
<> \^<>=Up \^<< keylist[M_DOWN][1]>>=Down \^<< keylist[M_SEL][1]>>=Select
"; } /* show a 'next chapter' link */ menuNextChapter(keylist, title, hrefNext, hrefUp) { "Next: <>; \^<>=<>"; } /* * cannot reach (i.e., touch) an object that is to be manipulated in * a command - this is a generic message used when we cannot * identify the specific reason that the object is in scope but * cannot be touched */ cannotReachObject(obj) { "{You/he} {cannot} reach <>. "; } /* * cannot reach an object, because the object is inside the given * container */ cannotReachContents(obj, loc) { gMessageParams(obj, loc); return '{You/he} {cannot} reach {the obj/him} through ' + '{the loc/him}. '; } /* cannot reach an object because it's outisde the given container */ cannotReachOutside(obj, loc) { gMessageParams(obj, loc); return '{You/he} {cannot} reach {the obj/him} through ' + '{the loc/him}. '; } /* sound is coming from inside/outside a container */ soundIsFromWithin(obj, loc) { "\^<> appear<> to be coming from inside <>. "; } soundIsFromWithout(obj, loc) { "\^<> appear<> to be coming from outside <>. "; } /* odor is coming from inside/outside a container */ smellIsFromWithin(obj, loc) { "\^<> appear<> to be coming from inside <>. "; } smellIsFromWithout(obj, loc) { "\^<> appear<> to be coming from outside <>. "; } /* default description of the player character */ pcDesc(actor) { "\^<> look<> the same as usual. "; } /* * Show a status line addendum for the actor posture, without * mentioning the actor's location. We won't mention standing, since * this is the default posture. */ roomActorStatus(actor) { /* mention any posture other than standing */ if (actor.posture != standing) " (<>)"; } /* show a status line addendum: standing in/on something */ actorInRoomStatus(actor, room) { " (<> <>)"; } /* generic short description of a dark room */ roomDarkName = 'In the dark' /* generic long description of a dark room */ roomDarkDesc = "It{’s| was} pitch black. " /* * mention that an actor is here, without mentioning the enclosing * room, as part of a room description */ roomActorHereDesc(actor) { "\^<> <> <>. "; } /* * mention that an actor is visible at a distance or remotely, * without mentioning the enclosing room, as part of a room * description */ roomActorThereDesc(actor) { "\^<> <> nearby. "; } /* * Mention that an actor is in a given local room, as part of a room * description. This is used as a default "special description" for * an actor. */ actorInRoom(actor, cont) { "\^<> <> <>. "; } /* * Describe an actor as standing/sitting/lying on something, as part * of the actor's EXAMINE description. This is additional * information added to the actor's description, so we refer to the * actor with a pronoun ("He's standing here"). */ actorInRoomPosture(actor, room) { "\^<> <> <>. "; } /* * Describe an actor's posture, as part of an actor's "examine" * description. If the actor is standing, don't bother mentioning * anything, as standing is the trivial default condition. */ roomActorPostureDesc(actor) { if (actor.posture != standing) "\^<> <>. "; } /* * mention that the given actor is visible, at a distance or * remotely, in the given location; this is used in room * descriptions when an NPC is visible in a remote or distant * location */ actorInRemoteRoom(actor, room, pov) { /* say that the actor is in the room, using its remote in-name */ "\^<> <> <>. "; } /* * mention that the given actor is visible, at a distance or * remotely, in the given nested room within the given outer * location; this is used in room descriptions */ actorInRemoteNestedRoom(actor, inner, outer, pov) { /* * say that the actor is in the nested room, in the current * posture, and add then add that we're in the outer room as * well */ "\^<> <>, <> <>. "; } /* * Prefix/suffix messages for listing actors in a room description, * for cases when the actors are in the local room in a nominal * container that we want to mention: "Bob and Bill are sitting on * the couch." */ actorInGroupPrefix(posture, cont, lst) { "\^"; } actorInGroupSuffix(posture, cont, lst) { " <> <> <>. "; } /* * Prefix/suffix messages for listing actors in a room description, * for cases when the actors are inside a nested room that's inside * a remote location: "Bob and Bill are in the courtyard, sitting on * the bench." */ actorInRemoteGroupPrefix(pov, posture, cont, remote, lst) { "\^"; } actorInRemoteGroupSuffix(pov, posture, cont, remote, lst) { " <> <>, <> <>. "; } /* * Prefix/suffix messages for listing actors in a room description, * for cases when the actors' nominal container cannot be seen or is * not to be stated: "Bob and Bill are standing here." * * Note that we don't always want to state the nominal container, * even when it's visible. For example, when actors are standing on * the floor, we don't bother saying that they're on the floor, as * that's stating the obvious. The container will decide whether or * not it wants to be included in the message; containers that don't * want to be mentioned will use this form of the message. */ actorHereGroupPrefix(posture, lst) { "\^"; } actorHereGroupSuffix(posture, lst) { " <> <> <>. "; } /* * Prefix/suffix messages for listing actors in a room description, * for cases when the actors' immediate container cannot be seen or * is not to be stated, and the actors are in a remote location: * "Bob and Bill are in the courtyard." */ actorThereGroupPrefix(pov, posture, remote, lst) { "\^"; } actorThereGroupSuffix(pov, posture, remote, lst) { " <> <> <>. "; } /* a traveler is arriving, but not from a compass direction */ sayArriving(traveler) { "\^<> enter<> <>. "; } /* a traveler is departing, but not in a compass direction */ sayDeparting(traveler) { "\^<> <> <>. "; } /* * a traveler is arriving locally (staying within view throughout the * travel, and coming closer to the PC) */ sayArrivingLocally(traveler, dest) { "\^<> enter<> <>. "; } /* * a traveler is departing locally (staying within view throughout * the travel, and moving further away from the PC) */ sayDepartingLocally(traveler, dest) { "\^<> <> <>. "; } /* * a traveler is traveling remotely (staying within view through the * travel, and moving from one remote top-level location to another) */ sayTravelingRemotely(traveler, dest) { "\^<> <> to <>. "; } /* a traveler is arriving from a compass direction */ sayArrivingDir(traveler, dirName) { "\^<> enter<> <> from the <>. "; } /* a traveler is leaving in a given compass direction */ sayDepartingDir(traveler, dirName) { local nm = traveler.travelerRemoteLocName; "\^<> <> to the <><>. "; } /* a traveler is arriving from a shipboard direction */ sayArrivingShipDir(traveler, dirName) { "\^<> enter<> <> from <>. "; } /* a traveler is leaving in a given shipboard direction */ sayDepartingShipDir(traveler, dirName) { local nm = traveler.travelerRemoteLocName; "\^<> <> to <><>. "; } /* a traveler is going aft */ sayDepartingAft(traveler) { local nm = traveler.travelerRemoteLocName; "\^<> <> aft<>. "; } /* a traveler is going fore */ sayDepartingFore(traveler) { local nm = traveler.travelerRemoteLocName; "\^<> <> forward<>. "; } /* a shipboard direction was attempted while not onboard a ship */ notOnboardShip = "That direction {is|was}n’t meaningful {|t}here. " /* a traveler is leaving via a passage */ sayDepartingThroughPassage(traveler, passage) { "\^<> <> <> through <>. "; } /* a traveler is arriving via a passage */ sayArrivingThroughPassage(traveler, passage) { "\^<> enter<> <> through <>. "; } /* a traveler is leaving via a path */ sayDepartingViaPath(traveler, passage) { "\^<> <> <> via <>. "; } /* a traveler is arriving via a path */ sayArrivingViaPath(traveler, passage) { "\^<> enter<> <> via <>. "; } /* a traveler is leaving up a stairway */ sayDepartingUpStairs(traveler, stairs) { "\^<> <> up <>. "; } /* a traveler is leaving down a stairway */ sayDepartingDownStairs(traveler, stairs) { "\^<> <> down <>. "; } /* a traveler is arriving by coming up a stairway */ sayArrivingUpStairs(traveler, stairs) { local nm = traveler.travelerRemoteLocName; "\^<> <> up <><>. "; } /* a traveler is arriving by coming down a stairway */ sayArrivingDownStairs(traveler, stairs) { local nm = traveler.travelerRemoteLocName; "\^<> <> down <><>. "; } /* acompanying another actor on travel */ sayDepartingWith(traveler, lead) { "\^<> <> with <>. "; } /* * Accompanying a tour guide. Note the seemingly reversed roles: * the lead actor is the one initiating the travel, and the tour * guide is the accompanying actor. So, the lead actor is * effectively following the accompanying actor. It seems * backwards, but really it's not: the tour guide merely shows the * lead actor where to go, but it's up to the lead actor to actually * initiate the travel. */ sayDepartingWithGuide(guide, lead) { "\^<> let<> <> lead the way. "; } /* note that a door is being opened/closed remotely */ sayOpenDoorRemotely(door, stat) { "Someone <> <> from the other side. "; } /* * open/closed status - these are simply adjectives that can be used * to describe the status of an openable object */ openMsg(obj) { return 'open'; } closedMsg(obj) { return 'closed'; } /* object is currently open/closed */ currentlyOpen = '{It\'s dobj} currently open. ' currentlyClosed = '{It\'s dobj} currently closed. ' /* stand-alone independent clause describing current open status */ openStatusMsg(obj) { return obj.itIsContraction + ' ' + obj.openDesc; } /* locked/unlocked status - adjectives describing lock states */ lockedMsg(obj) { return 'locked'; } unlockedMsg(obj) { return 'unlocked'; } /* object is currently locked/unlocked */ currentlyLocked = '{It\'s dobj} currently locked. ' currentlyUnlocked = '{It\'s dobj} currently unlocked. ' /* * on/off status - these are simply adjectives that can be used to * describe the status of a switchable object */ onMsg(obj) { return 'on'; } offMsg(obj) { return 'off'; } /* daemon report for burning out a match */ matchBurnedOut(obj) { gMessageParams(obj); "{The obj/he} finish{es/ed} burning, and disappear{s/ed} into a cloud of ash. "; } /* daemon report for burning out a candle */ candleBurnedOut(obj) { gMessageParams(obj); "{The obj/he} burn{s/ed} down too far to stay lit, and {goes} out. "; } /* daemon report for burning out a generic fueled light source */ objBurnedOut(obj) { gMessageParams(obj); "{The obj/he} {goes} out. "; } /* * Standard dialog titles, for the Web UI. These are shown in the * title bar area of the Web UI dialog used for inputDialog() calls. * These correspond to the InDlgIconXxx icons. The conventional * interpreters use built-in titles when titles are needed at all, * but in the Web UI we have to generate these ourselves. */ dlgTitleNone = 'Note' dlgTitleWarning = 'Warning' dlgTitleInfo = 'Note' dlgTitleQuestion = 'Question' dlgTitleError = 'Error' /* * Standard dialog button labels, for the Web UI. These are built in * to the conventional interpreters, but in the Web UI we have to * generate these ourselves. */ dlgButtonOk = 'OK' dlgButtonCancel = 'Cancel' dlgButtonYes = 'Yes' dlgButtonNo = 'No' /* web UI alert when a new user has joined a multi-user session */ webNewUser(name) { "\b[<> has joined the session.]\n"; } /* * Warning prompt for inputFile() warnings generated when reading a * script file, for the Web UI. The interpreter normally displays * these warnings directly, but in Web UI mode, the program is * responsible, so we need localized messages. */ inputFileScriptWarning(warning, filename) { /* remove the two-letter error code at the start of the string */ warning = warning.substr(3); /* build the message */ return warning + ' Do you wish to proceed?'; } inputFileScriptWarningButtons = [ '&Yes, use this file', '&Choose another file', '&Stop the script'] ; /* ------------------------------------------------------------------------ */ /* * Player Character messages. These messages are generated when the * player issues a regular command to the player character (i.e., * without specifying a target actor). */ playerMessages: libMessages /* invalid command syntax */ commandNotUnderstood(actor) { "<.parser>The story doesn’t understand that command.<./parser> "; } /* a special topic can't be used right now, because it's inactive */ specialTopicInactive(actor) { "<.parser>That command can’t be used right now.<./parser> "; } /* no match for a noun phrase */ noMatch(actor, action, txt) { action.noMatch(self, actor, txt); } /* * No match message - we can't see a match for the noun phrase. This * is the default for most verbs. */ noMatchCannotSee(actor, txt) { "{You/he} {sees} no <> {|t}here. "; } /* * No match message - we're not aware of a match for the noun phrase. * Some sensory actions, such as LISTEN TO and SMELL, use this * variation instead of the normal version; the things these commands * refer to tend to be intangible, so "you can't see that" tends to * be nonsensical. */ noMatchNotAware(actor, txt) { "{You/he} {are} not aware of any <> {|t}here. "; } /* 'all' is not allowed with the attempted action */ allNotAllowed(actor) { "<.parser>All cannot be used with that verb.<./parser> "; } /* no match for 'all' */ noMatchForAll(actor) { "<.parser>{You/he} {sees} nothing suitable {|t}here.<./parser> "; } /* nothing left for 'all' after removing 'except' items */ noMatchForAllBut(actor) { "<.parser>{You/he} {sees} nothing else {|t}here.<./parser> "; } /* nothing left in a plural phrase after removing 'except' items */ noMatchForListBut(actor) { noMatchForAllBut(actor); } /* no match for a pronoun */ noMatchForPronoun(actor, typ, pronounWord) { /* show the message */ "<.parser>The word <> doesn’t refer to anything right now.<./parser> "; } /* * Ask for a missing object - this is called when a command is * completely missing a noun phrase for one of its objects. */ askMissingObject(actor, action, which) { reportQuestion('<.parser>\^' + action.whatObj(which) + ' do you want ' + (actor.referralPerson == ThirdPerson ? actor.theName : '') + ' to ' + action.getQuestionInf(which) + '?<./parser> '); } /* * An object was missing - this is called under essentially the same * circumstances as askMissingObject, but in cases where interactive * resolution is impossible and we simply wish to report the problem * and do not wish to ask for help. */ missingObject(actor, action, which) { "<.parser>You need to be more specific about <> you want <> to <>.<./parser> "; } /* * Ask for a missing literal phrase. */ askMissingLiteral(actor, action, which) { /* use the standard missing-object message */ askMissingObject(actor, action, which); } /* * Show the message for a missing literal phrase. */ missingLiteral(actor, action, which) { "<.parser>Please be more specific about <> to <>. Try, for example, <> something.<./parser> "; } /* reflexive pronoun not allowed */ reflexiveNotAllowed(actor, typ, pronounWord) { "<.parser>The story doesn’t understand how to use the word <> like that.<./parser> "; } /* * a reflexive pronoun disagrees in gender, number, or something * else with its referent */ wrongReflexive(actor, typ, pronounWord) { "<.parser>The story doesn’t understand what the word <> refers to.<./parser> "; } /* no match for a possessive phrase */ noMatchForPossessive(actor, owner, txt) { "<.parser>\^<> not appear to have any such thing.<./parser> "; } /* no match for a plural possessive phrase */ noMatchForPluralPossessive(actor, txt) { "<.parser>\^They <> not appear to have any such thing.<./parser> "; } /* no match for a containment phrase */ noMatchForLocation(actor, loc, txt) { "<.parser>\^<> no <>.<./parser> "; } /* nothing in a container whose contents are specifically requested */ nothingInLocation(actor, loc) { "<.parser>\^<> <>.<./parser> "; } /* no match for the response to a disambiguation question */ noMatchDisambig(actor, origPhrase, disambigResponse) { /* * show the message, leaving the <.parser> tag mode open - we * always show another disambiguation prompt after this message, * so we'll let the prompt close the <.parser> mode */ "<.parser>That was not one of the choices. "; } /* empty noun phrase ('take the') */ emptyNounPhrase(actor) { "<.parser>You seem to have left out some words.<./parser> "; } /* 'take zero books' */ zeroQuantity(actor, txt) { "<.parser>\^<> <> do that to zero of something.<./parser> "; } /* insufficient quantity to meet a command request ('take five books') */ insufficientQuantity(actor, txt, matchList, requiredNum) { "<.parser>\^<>n’t see that many <> <>.<./parser> "; } /* a unique object is required, but multiple objects were specified */ uniqueObjectRequired(actor, txt, matchList) { "<.parser>You can’t use multiple objects there.<./parser> "; } /* a single noun phrase is required, but a noun list was used */ singleObjectRequired(actor, txt) { "<.parser>Multiple objects aren’t allowed with that command.<./parser> "; } /* * The answer to a disambiguation question specifies an invalid * ordinal ("the fourth one" when only three choices were offered). * * 'ordinalWord' is the ordinal word entered ('fourth' or the like). * 'originalText' is the text of the noun phrase that caused the * disambiguation question to be asked in the first place. */ disambigOrdinalOutOfRange(actor, ordinalWord, originalText) { /* leave the <.parser> tag open, for the re-prompt that will follow */ "<.parser>There were not that many choices. "; } /* * Ask the canonical disambiguation question: "Which x do you * mean...?". 'matchList' is the list of ambiguous objects with any * redundant equivalents removed; and 'fullMatchList' is the full * list, including redundant equivalents that were removed from * 'matchList'. * * If askingAgain is true, it means that we're asking the question * again because we got an invalid response to the previous attempt * at the same prompt. We will have explained the problem, and now * we're going to give the user another crack at the same response. * * To prevent interactive disambiguation, do this: * * throw new ParseFailureException(&ambiguousNounPhrase, *. originalText, matchList, fullMatchList); */ askDisambig(actor, originalText, matchList, fullMatchList, requiredNum, askingAgain, dist) { /* mark this as a question report with a dummy report */ reportQuestion(''); /* * Open the "<.parser>" tag, if we're not "asking again." If we * are asking again, we will already have shown a message * explaining why we're asking again, and that message will have * left us in <.parser> tag mode, so we don't need to open the * tag again. */ if (!askingAgain) "<.parser>"; /* * the question varies depending on whether we want just one * object or several objects in the final result */ if (requiredNum == 1) { /* * One object needed - use the original text in the query. * * Note that if we're "asking again," we will have shown an * additional message first explaining *why* we're asking * again, and that message will have left us in <.parser> * tag mode; so we need to close the <.parser> tag in this * case, but we don't need to show a new one. */ if (askingAgain) "Which did you mean, <>?"; else "Which <> do you mean, <>?"; } else { /* * Multiple objects required - ask by number, since we can't * easily guess what the plural might be given the original * text. * * As above, we only need to *close* the <.parser> tag if * we're asking again, because we will already have shown a * prompt that opened the tag in this case. */ if (askingAgain) "Which <> (of <>) did you mean?"; else "Which <> (of <>) do you mean?"; } /* close the <.parser> tag */ "<./parser> "; } /* * we found an ambiguous noun phrase, but we were unable to perform * interactive disambiguation */ ambiguousNounPhrase(actor, originalText, matchList, fullMatchList) { "<.parser>The story doesn’t know which <> you mean.<./parser> "; } /* the actor is missing in a command */ missingActor(actor) { "<.parser>You must be more specific about << whomPronoun>> you want<> to address.<./parser> "; } /* only a single actor can be addressed at a time */ singleActorRequired(actor) { "<.parser>You can only address one person at a time.<./parser> "; } /* cannot change actor mid-command */ cannotChangeActor() { "<.parser>You cannot address more than one character on a single command line in this story.<./parser> "; } /* * tell the user they entered a word we don't know, offering the * chance to correct it with "oops" */ askUnknownWord(actor, txt) { /* start the message */ "<.parser>The word <> is not necessary in this story.<./parser> "; /* mention the OOPS command, if appropriate */ oopsNote(); } /* * tell the user they entered a word we don't know, but don't offer * an interactive way to fix it (i.e., we can't use OOPS at this * point) */ wordIsUnknown(actor, txt) { "<.parser>The story doesn’t understand that command.<./parser> "; } /* the actor refuses the command because it's busy with something else */ refuseCommandBusy(targetActor, issuingActor) { "\^<> busy. "; } /* cannot speak to multiple actors */ cannotAddressMultiple(actor) { "<.parser>\^<> <> address multiple people at once.<./parser> "; } /* * Remaining actions on the command line were aborted due to the * failure of the current action. This is just a hook for the game's * use, if it wants to provide an explanation; by default, we do * nothing. Note that games that override this will probably want to * use a flag property so that they only show this message once - * it's really only desirable to explain the the mechanism, not to * flag it every time it's used. */ explainCancelCommandLine() { } ; /* ------------------------------------------------------------------------ */ /* * Non-Player Character (NPC) messages - parser-mediated format. These * versions of the NPC messages report errors through the * parser/narrator. * * Note that a separate set of messages can be selected to report * messages in the voice of the NPC - see npcMessagesDirect below. */ /* * Standard Non-Player Character (NPC) messages. These messages are * generated when the player issues a command to a specific non-player * character. */ npcMessages: playerMessages /* the target cannot hear a command we gave */ commandNotHeard(actor) { "\^<> not respond. "; } /* no match for a noun phrase */ noMatchCannotSee(actor, txt) { "\^<> no <>. "; } noMatchNotAware(actor, txt) { "\^<> not aware of any <>. "; } /* no match for 'all' */ noMatchForAll(actor) { "<.parser>\^<>n’t see anything suitable.<./parser> "; } /* nothing left for 'all' after removing 'except' items */ noMatchForAllBut(actor) { "<.parser>\^<> nothing else.<./parser> "; } /* insufficient quantity to meet a command request ('take five books') */ insufficientQuantity(actor, txt, matchList, requiredNum) { "<.parser>\^<>n’t see that many <>.<./parser> "; } /* * we found an ambiguous noun phrase, but we were unable to perform * interactive disambiguation */ ambiguousNounPhrase(actor, originalText, matchList, fullMatchList) { "<.parser>\^<>n’t know which <> you mean<>.<./parser> "; } /* * Missing object query and error message templates */ askMissingObject(actor, action, which) { reportQuestion('<.parser>\^' + action.whatObj(which) + ' do you want ' + actor.theNameObj + ' to ' + action.getQuestionInf(which) + '?<./parser> '); } missingObject(actor, action, which) { "<.parser>You must be more specific about <> you want <> to <>.<./parser> "; } /* missing literal phrase query and error message templates */ missingLiteral(actor, action, which) { "<.parser>You must be more specific about <> you want <> to <>. For example: <>, <> something.<./parser> "; } ; /* * Deferred NPC messages. We use this to report deferred messages from * an NPC to the player. A message is deferred when a parsing error * occurs, but the NPC can't talk to the player because there's no sense * path to the player. When this happens, the NPC queues the message * for eventual delivery; when a sense path appears later that lets the * NPC talk to the player, we deliver the message through this object. * Since these messages describe conditions that occurred in the past, * we use the past tense to phrase the messages. * * This default implementation simply doesn't report deferred errors at * all. The default message voice is the parser/narrator character, and * there is simply no good way for the parser/narrator to say that a * command failed in the past for a given character: "Bob looks like he * didn't know which box you meant" just doesn't work. So, we'll simply * not report these errors at all. * * To report messages in the NPC's voice directly, modify the NPC's * Actor object, or the Actor base class, to return * npcDeferredMessagesDirect rather than this object from * getParserMessageObj(). */ npcDeferredMessages: object ; /* ------------------------------------------------------------------------ */ /* * NPC messages, reported directly in the voice of the NPC. These * messages are not selected by default, but a game can use them instead * of the parser-mediated versions by modifying the actor object's * getParserMessageObj() to return these objects. */ /* * Standard Non-Player Character (NPC) messages. These messages are * generated when the player issues a command to a specific non-player * character. */ npcMessagesDirect: npcMessages /* no match for a noun phrase */ noMatchCannotSee(actor, txt) { "\^<> around. I don’t see any <>. "; } noMatchNotAware(actor, txt) { "I’m not aware of any <>, <>. "; } /* no match for 'all' */ noMatchForAll(actor) { "\^<>, I don’t see anything suitable. "; } /* nothing left for 'all' after removing 'except' items */ noMatchForAllBut(actor) { "\^<>, I see nothing else here. "; } /* 'take zero books' */ zeroQuantity(actor, txt) { "\^<>, I can’t do that to zero of something. "; } /* insufficient quantity to meet a command request ('take five books') */ insufficientQuantity(actor, txt, matchList, requiredNum) { "\^<>, I don’t see that many <> here. "; } /* a unique object is required, but multiple objects were specified */ uniqueObjectRequired(actor, txt, matchList) { "\^<>, I can’t use multiple objects like that. "; } /* a single noun phrase is required, but a noun list was used */ singleObjectRequired(actor, txt) { "\^<>, I can’t use multiple objects like that. "; } /* no match for the response to a disambiguation question */ noMatchDisambig(actor, origPhrase, disambigResponse) { /* leave the quote open for the re-prompt */ "\^<>, That was not one of the choices. "; } /* * The answer to a disambiguation question specifies an invalid * ordinal ("the fourth one" when only three choices were offered). * * 'ordinalWord' is the ordinal word entered ('fourth' or the like). * 'originalText' is the text of the noun phrase that caused the * disambiguation question to be asked in the first place. */ disambigOrdinalOutOfRange(actor, ordinalWord, originalText) { /* leave the quote open for the re-prompt */ "\^<>, There weren’t that many choices. "; } /* * Ask the canonical disambiguation question: "Which x do you * mean...?". 'matchList' is the list of ambiguous objects with any * redundant equivalents removed, and 'fullMatchList' is the full * list, including redundant equivalents that were removed from * 'matchList'. * * To prevent interactive disambiguation, do this: * * throw new ParseFailureException(&ambiguousNounPhrase, *. originalText, matchList, fullMatchList); */ askDisambig(actor, originalText, matchList, fullMatchList, requiredNum, askingAgain, dist) { /* mark this as a question report */ reportQuestion(''); /* the question depends on the number needed */ if (requiredNum == 1) { /* one required - ask with the original text */ if (!askingAgain) "\^<>, "; "Which <> do you mean, << askDisambigList(matchList, fullMatchList, nil, dist)>>? "; } else { /* * more than one required - we can't guess at the plural * given the original text, so just use the number */ if (!askingAgain) "\^<>, "; "Which <> (of << askDisambigList(matchList, fullMatchList, true, dist)>>) do you mean? "; } } /* * we found an ambiguous noun phrase, but we were unable to perform * interactive disambiguation */ ambiguousNounPhrase(actor, originalText, matchList, fullMatchList) { "\^<>, I don’t know which <> you mean. "; } /* * Missing object query and error message templates */ askMissingObject(actor, action, which) { reportQuestion('\^' + actor.nameSays + ', \^' + action.whatObj(which) + ' do you want me to ' + action.getQuestionInf(which) + '? '); } missingObject(actor, action, which) { "\^<>, I don’t know <> you want me to <>. "; } missingLiteral(actor, action, which) { /* use the same message we use for a missing ordinary object */ missingObject(actor, action, which); } /* tell the user they entered a word we don't know */ askUnknownWord(actor, txt) { "\^<>, I don’t know the word <>. "; } /* tell the user they entered a word we don't know */ wordIsUnknown(actor, txt) { "\^<>, You used a word I don’t know. "; } ; /* * Deferred NPC messages. We use this to report deferred messages from * an NPC to the player. A message is deferred when a parsing error * occurs, but the NPC can't talk to the player because there's no sense * path to the player. When this happens, the NPC queues the message * for eventual delivery; when a sense path appears later that lets the * NPC talk to the player, we deliver the message through this object. * Since these messages describe conditions that occurred in the past, * we use the past tense to phrase the messages. * * Some messages will never be deferred: * * commandNotHeard - if a command is not heard, it will never enter an * actor's command queue; the error is given immediately in response to * the command entry. * * refuseCommandBusy - same as commandNotHeard * * noMatchDisambig - interactive disambiguation will not happen in a * deferred response situation, so it is impossible to have an * interactive disambiguation failure. * * disambigOrdinalOutOfRange - for the same reason noMatchDisambig can't * be deferred. * * askDisambig - if we couldn't display a message, we definitely * couldn't perform interactive disambiguation. * * askMissingObject - for the same reason that askDisambig can't be * deferred * * askUnknownWord - for the same reason that askDisambig can't be * deferred. */ npcDeferredMessagesDirect: npcDeferredMessages commandNotUnderstood(actor) { "\^<>, I didn’t understand what you meant. "; } /* no match for a noun phrase */ noMatchCannotSee(actor, txt) { "\^<>, I didn’t see any <>. "; } noMatchNotAware(actor, txt) { "\^<>, I wasn’t aware of any <>. "; } /* no match for 'all' */ noMatchForAll(actor) { "\^<>, I didn’t see anything suitable. "; } /* nothing left for 'all' after removing 'except' items */ noMatchForAllBut(actor) { "\^<>, I didn’t see what you meant. "; } /* empty noun phrase ('take the') */ emptyNounPhrase(actor) { "\^<>, You left some words out. "; } /* 'take zero books' */ zeroQuantity(actor, txt) { "\^<>, I didn’t understand what you meant. "; } /* insufficient quantity to meet a command request ('take five books') */ insufficientQuantity(actor, txt, matchList, requiredNum) { "\^<>, I didn’t see enough <>. "; } /* a unique object is required, but multiple objects were specified */ uniqueObjectRequired(actor, txt, matchList) { "\^<>, I didn’t understand what you meant. "; } /* a unique object is required, but multiple objects were specified */ singleObjectRequired(actor, txt) { "\^<>, I didn’t understand what you meant. "; } /* * we found an ambiguous noun phrase, but we were unable to perform * interactive disambiguation */ ambiguousNounPhrase(actor, originalText, matchList, fullMatchList) { "\^<>, I couldn’t tell which <> you meant. "; } /* an object phrase was missing */ askMissingObject(actor, action, which) { reportQuestion('\^' + actor.nameSays + ', I didn’t know ' + action.whatObj(which) + ' you wanted me to ' + action.getQuestionInf(which) + '. '); } /* tell the user they entered a word we don't know */ wordIsUnknown(actor, txt) { "\^<>, You used a word I don’t know. "; } ; /* ------------------------------------------------------------------------ */ /* * Verb messages for standard library verb implementations for actions * performed by the player character. These return strings suitable for * use in VerifyResult objects as well as for action reports * (defaultReport, mainReport, and so on). * * Most of these messages are generic enough to be used for player and * non-player character alike. However, some of the messages either are * too terse (such as the default reports) or are phrased awkwardly for * NPC use, so the NPC verb messages override those. */ playerActionMessages: MessageHelper /* * generic "can't do that" message - this is used when verification * fails because an object doesn't define the action ("doXxx") * method for the verb */ cannotDoThatMsg = '{You/he} {can\'t} do that. ' /* must be holding something before a command */ mustBeHoldingMsg(obj) { gMessageParams(obj); return '{You/he} {must} be holding {the obj/him} to do that. '; } /* it's too dark to do that */ tooDarkMsg = 'It{’s| was} too dark to do that. ' /* object must be visible */ mustBeVisibleMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} see {that obj/him}. '; } /* object can be heard but not seen */ heardButNotSeenMsg(obj) { gMessageParams(obj); return '{You/he} {can} hear {an obj/him}, but {you/he} {can\'t} see {it obj/him}. '; } /* object can be smelled but not seen */ smelledButNotSeenMsg(obj) { gMessageParams(obj); return '{You/he} {can} smell {an obj/him}, but {you/he} {can\'t} see {it obj/him}. '; } /* cannot hear object */ cannotHearMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} hear {that obj/him}. '; } /* cannot smell object */ cannotSmellMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} smell {that obj/him}. '; } /* cannot taste object */ cannotTasteMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} taste {that obj/him}. '; } /* must remove an article of clothing before a command */ cannotBeWearingMsg(obj) { gMessageParams(obj); return '{You/he} {must} take off {the obj/him} before {it actor/he} {can} do that. '; } /* all contents must be removed from object before doing that */ mustBeEmptyMsg(obj) { gMessageParams(obj); return '{You/he} {must} take everything out of {the obj/him} before {it actor/he} {can} do that. '; } /* object must be opened before doing that */ mustBeOpenMsg(obj) { gMessageParams(obj); return '{You/he} {must} open {the obj/him} before {it actor/he} {can} do that. '; } /* object must be closed before doing that */ mustBeClosedMsg(obj) { gMessageParams(obj); return '{You/he} {must} close {the obj/him} before {it actor/he} {can} do that. '; } /* object must be unlocked before doing that */ mustBeUnlockedMsg(obj) { gMessageParams(obj); return '{You/he} {must} unlock {the obj/him} before {it actor/he} {can} do that. '; } /* no key is needed to lock or unlock this object */ noKeyNeededMsg = '{The dobj/he} {does} not appear to take a key. ' /* actor must be standing before doing that */ mustBeStandingMsg = '{You/he} {must} stand up before {it actor/he} {can} do that. ' /* must be sitting on/in chair */ mustSitOnMsg(obj) { gMessageParams(obj); return '{You/he} {must} sit {in obj} first. '; } /* must be lying on/in object */ mustLieOnMsg(obj) { gMessageParams(obj); return '{You/he} {must} lie {in obj} first. '; } /* must get on/in object */ mustGetOnMsg(obj) { gMessageParams(obj); return '{You/he} {must} get {in obj} first. '; } /* object must be in loc before doing that */ mustBeInMsg(obj, loc) { gMessageParams(obj, loc); return '{The obj/he} {must} be {in loc} before {you/he} {can} do that. '; } /* actor must be holding the object before we can do that */ mustBeCarryingMsg(obj, actor) { gMessageParams(obj, actor); return '{The actor/he} {must} be holding {the obj/him} before {it actor/he} {can} do that. '; } /* generic "that's not important" message for decorations */ decorationNotImportantMsg(obj) { gMessageParams(obj); return '{The obj/he} {is}n’t important. '; } /* generic "you don't see that" message for "unthings" */ unthingNotHereMsg(obj) { gMessageParams(obj); return '{You/he} {does}n’t see {that obj/him} {|t}here. '; } /* generic "that's too far away" message for Distant items */ tooDistantMsg(obj) { gMessageParams(obj); return '{The obj/he} {is} too far away. '; } /* generic "no can do" message for intangibles */ notWithIntangibleMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} do that to {an obj/him}. '; } /* generic failure message for varporous objects */ notWithVaporousMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} do that to {an obj/him}. '; } /* look in/look under/look through/look behind/search vaporous */ lookInVaporousMsg(obj) { gMessageParams(obj); return '{You/he} just {sees} {the obj/him}. '; } /* * cannot reach (i.e., touch) an object that is to be manipulated in * a command - this is a generic message used when we cannot * identify the specific reason that the object is in scope but * cannot be touched */ cannotReachObjectMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} reach {the obj/him}. '; } /* cannot reach an object through an obstructor */ cannotReachThroughMsg(obj, loc) { gMessageParams(obj, loc); return '{You/he} {cannot} reach {the obj/him} through ' + '{the loc/him}. '; } /* generic long description of a Thing */ thingDescMsg(obj) { gMessageParams(obj); return '{You/he} {sees} nothing unusual about ' + '{it obj/him}. '; } /* generic LISTEN TO description of a Thing */ thingSoundDescMsg(obj) { return '{You/he} hear{s/d} nothing out of the ordinary. '; } /* generic "smell" description of a Thing */ thingSmellDescMsg(obj) { return '{You/he} smell{s/ed} nothing out of the ordinary. '; } /* default description of a non-player character */ npcDescMsg(npc) { gMessageParams(npc); return '{You/he} {sees} nothing unusual about ' + '{the npc/him}. '; } /* generic messages for looking prepositionally */ nothingInsideMsg = 'There{’s| was} nothing unusual in {the dobj/him}. ' nothingUnderMsg = '{You/he} {sees} nothing unusual under {the dobj/him}. ' nothingBehindMsg = '{You/he} {sees} nothing unusual behind {the dobj/him}. ' nothingThroughMsg = '{You/he} {can} see nothing through {the dobj/him}. ' /* this is an object we can't look behind/through */ cannotLookBehindMsg = '{You/he} {can\'t} look behind {the dobj/him}. ' cannotLookUnderMsg = '{You/he} {can\'t} look under {the dobj/him}. ' cannotLookThroughMsg = '{You/he} {can\'t} see through {the dobj/him}. ' /* looking through an open passage */ nothingThroughPassageMsg = '{You/he} {can\'t} see much through {the dobj/him} from {|t}here. ' /* there's nothing on the other side of a door we just opened */ nothingBeyondDoorMsg = 'Opening {the dobj/him} reveal{s|ed} nothing unusual. ' /* there's nothing here with a specific odor */ nothingToSmellMsg = '{You/he} smell{s/ed} nothing out of the ordinary. ' /* there's nothing here with a specific noise */ nothingToHearMsg = '{You/he} hear{s/d} nothing out of the ordinary. ' /* a sound appears to be coming from a source */ noiseSourceMsg(src) { return '{The dobj/he} seem{s/ed} to be coming from ' + src.theNameObj + '. '; } /* an odor appears to be coming from a source */ odorSourceMsg(src) { return '{The dobj/he} seem{s/ed} to be coming from ' + src.theNameObj + '. '; } /* an item is not wearable */ notWearableMsg = '{That dobj/he} {is}n’t something {you/he} {can} wear. ' /* doffing something that isn't wearable */ notDoffableMsg = '{That dobj/he} {is}n’t something {you/he} {can} remove. ' /* already wearing item */ alreadyWearingMsg = '{You\'re} already wearing {it dobj/him}. ' /* not wearing (item being doffed) */ notWearingMsg = '{You\'re} not wearing {that dobj/him}. ' /* default response to 'wear obj' */ okayWearMsg = 'Okay, {you\'re} now wearing {the dobj/him}. ' /* default response to 'doff obj' */ okayDoffMsg = 'Okay, {you\'re} no longer wearing {the dobj/him}. ' /* default response to open/close */ okayOpenMsg = shortTMsg( 'Opened. ', '{You/he} open{s/ed} {the dobj/him}. ') okayCloseMsg = shortTMsg( 'Closed. ', '{You/he} close{s/d} {the dobj/him}. ') /* default response to lock/unlock */ okayLockMsg = shortTMsg( 'Locked. ', '{You/he} lock{s/ed} {the dobj/him}. ') okayUnlockMsg = shortTMsg( 'Unlocked. ', '{You/he} unlock{s/ed} {the dobj/him}. ') /* cannot dig here */ cannotDigMsg = '{You/he} {have} no reason to dig in {that dobj/him}. ' /* not a digging implement */ cannotDigWithMsg = '{You/he} {sees} no way to use {that iobj/him} as a shovel. ' /* taking something already being held */ alreadyHoldingMsg = '{You/he} {are} already carrying {the dobj/him}. ' /* actor taking self ("take me") */ takingSelfMsg = '{You/he} {can\'t} take {yourself}. ' /* dropping an object not being carried */ notCarryingMsg = '{You\'re} not carrying {that dobj/him}. ' /* actor dropping self */ droppingSelfMsg = '{You/he} {can\'t} drop {yourself}. ' /* actor putting self in something */ puttingSelfMsg = '{You/he} {can\'t} do that to {yourself}. ' /* actor throwing self */ throwingSelfMsg = '{You/he} {can\'t} throw {yourself}. ' /* we can't put the dobj in the iobj because it's already there */ alreadyPutInMsg = '{The dobj/he} {is} already in {the iobj/him}. ' /* we can't put the dobj on the iobj because it's already there */ alreadyPutOnMsg = '{The dobj/he} {is} already on {the iobj/him}. ' /* we can't put the dobj under the iobj because it's already there */ alreadyPutUnderMsg = '{The dobj/he} {is} already under {the iobj/him}. ' /* we can't put the dobj behind the iobj because it's already there */ alreadyPutBehindMsg = '{The dobj/he} {is} already behind {the iobj/him}. ' /* * trying to move a Fixture to a new container by some means (take, * drop, put in, put on, etc) */ cannotMoveFixtureMsg = '{The dobj/he} {cannot} be moved. ' /* trying to take a Fixture */ cannotTakeFixtureMsg = '{You/he} {can\'t} take {that dobj/him}. ' /* trying to put a Fixture in something */ cannotPutFixtureMsg = '{You/he} {can\'t} put {the dobj/him} anywhere. ' /* trying to take/move/put an Immovable object */ cannotTakeImmovableMsg = '{You/he} {can\'t} take {that dobj/him}. ' cannotMoveImmovableMsg = '{The dobj/he} {cannot} be moved. ' cannotPutImmovableMsg = '{You/he} {can\'t} put {the dobj/him} anywhere. ' /* trying to take/move/put a Heavy object */ cannotTakeHeavyMsg = '{The dobj/he} {is} too heavy. ' cannotMoveHeavyMsg = '{The dobj/he} {is} too heavy. ' cannotPutHeavyMsg = '{The dobj/he} {is} too heavy. ' /* trying to move a component object */ cannotMoveComponentMsg(loc) { return '{The dobj/he} {is} part of ' + loc.theNameObj + '. '; } /* trying to take a component object */ cannotTakeComponentMsg(loc) { return '{You/he} {can\'t} have {that/him dobj}; ' + '{it\'s dobj} part of ' + loc.theNameObj + '. '; } /* trying to put a component in something */ cannotPutComponentMsg(loc) { return '{You/he} {can\'t} put {that/him dobj} anywhere; ' + '{it\'s dobj} part of ' + loc.theNameObj + '. '; } /* specialized Immovable messages for TravelPushables */ cannotTakePushableMsg = '{You/he} {can\'t} take {that/him dobj}, but {it actor/he} might {be|have been} able to push it somewhere. ' cannotMovePushableMsg = 'It wouldn’t {|have} accomplish{|ed} anything to move {the dobj/him} around aimlessly, but {it actor/he} might {be|have been} able to push {it dobj/him} in a specific direction. ' cannotPutPushableMsg = '{You/he} {can\'t} put {that/him dobj} anywhere, but {it actor/he} might {be|have been} able to push it somewhere. ' /* can't take something while occupying it */ cannotTakeLocationMsg = '{You/he} {can\'t} take {that/him dobj} while {you\'re} occupying {it/him dobj}. ' /* can't REMOVE something that's being held */ cannotRemoveHeldMsg = 'There{\'s| was} nothing to remove {the dobj/him} from. ' /* default 'take' response */ okayTakeMsg = shortTMsg( 'Taken. ', '{You/he} {take[s]|took} {the dobj/him}. ') /* default 'drop' response */ okayDropMsg = shortTMsg( 'Dropped. ', '{You/he} drop{s/ped} {the dobj/him}. ') /* dropping an object */ droppingObjMsg(dropobj) { gMessageParams(dropobj); return '{You/he} drop{s/ped} {the dropobj/him}. '; } /* default receiveDrop suffix for floorless rooms */ floorlessDropMsg(dropobj) { gMessageParams(dropobj); return '{It dropobj/he} {fall[s]|fell} out of sight below. '; } /* default successful 'put in' response */ okayPutInMsg = shortTIMsg( 'Done. ', '{You/he} put{[s]|} {the dobj/him} in {the iobj/him}. ') /* default successful 'put on' response */ okayPutOnMsg = shortTIMsg( 'Done. ', '{You/he} put{[s]|} {the dobj/him} on {the iobj/him}. ') /* default successful 'put under' response */ okayPutUnderMsg = shortTIMsg( 'Done. ', '{You/he} put{[s]|} {the dobj/him} under {the iobj/him}. ') /* default successful 'put behind' response */ okayPutBehindMsg = shortTIMsg( 'Done. ', '{You/he} put{[s]|} {the dobj/him} behind {the iobj/him}. ') /* try to take/move/put/taste an untakeable actor */ cannotTakeActorMsg = '{The dobj/he} {won\'t} let {you/him} do that. ' cannotMoveActorMsg = '{The dobj/he} {won\'t} let {you/him} do that. ' cannotPutActorMsg = '{The dobj/he} {won\'t} let {you/him} do that. ' cannotTasteActorMsg = '{The dobj/he} {won\'t} let {you/him} do that. ' /* trying to take/move/put/taste a person */ cannotTakePersonMsg = '{The dobj/he} probably wouldn’t {|have} like{|d} that. ' cannotMovePersonMsg = '{The dobj/he} probably wouldn’t {|have} like{|d} that. ' cannotPutPersonMsg = '{The dobj/he} probably wouldn’t {|have} like{|d} that. ' cannotTastePersonMsg = '{The dobj/he} probably wouldn’t {|have} like{|d} that. ' /* cannot move obj through obstructor */ cannotMoveThroughMsg(obj, obs) { gMessageParams(obj, obs); return '{You/he} {can\'t} move {that obj/him} through ' + '{the obs/him}. '; } /* cannot move obj in our out of container cont */ cannotMoveThroughContainerMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {can\'t} move {that obj/him} through ' + '{the cont/him}. '; } /* cannot move obj because cont is closed */ cannotMoveThroughClosedMsg(obj, cont) { gMessageParams(cont); return '{You/he} {can\'t} do that because {the cont/he} {is} ' + 'closed. '; } /* cannot fit obj into cont through cont's opening */ cannotFitIntoOpeningMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {can\'t} do that because {the obj/he} {is} too big to put into {the cont/him}. '; } /* cannot fit obj out of cont through cont's opening */ cannotFitOutOfOpeningMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {can\'t} do that because {the obj/he} {is} too big to take out of {the cont/him}. '; } /* actor 'obj' cannot reach in our out of container 'cont' */ cannotTouchThroughContainerMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {can\'t} reach anything through ' + '{the cont/him}. '; } /* actor 'obj' cannot reach through cont because cont is closed */ cannotTouchThroughClosedMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {can\'t} do that because {the cont/he} {is} closed. '; } /* actor cannot fit hand into cont through cont's opening */ cannotReachIntoOpeningMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {can\'t} fit {its/her} hand into ' + '{the cont/him}. '; } /* actor cannot fit hand into cont through cont's opening */ cannotReachOutOfOpeningMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {can\'t} fit {its/her} hand through {the cont/him}. '; } /* the object is too large for the actor to hold */ tooLargeForActorMsg(obj) { gMessageParams(obj); return '{The obj/he} {is} too large for {you/him} to hold. '; } /* the actor doesn't have room to hold the object */ handsTooFullForMsg(obj) { return '{Your} hands {are|were} too full to hold ' + obj.theNameObj + '. '; } /* the object is becoming too large for the actor to hold */ becomingTooLargeForActorMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} do that because {the obj/he} would {|have} become too large for {you/him} to hold. '; } /* the object is becoming large enough that the actor's hands are full */ handsBecomingTooFullForMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} do that because {its/her} hands would {|have} become too full to hold {the obj/him}. '; } /* the object is too heavy (all by itself) for the actor to hold */ tooHeavyForActorMsg(obj) { gMessageParams(obj); return '{The obj/he} {is} too heavy for {you/him} to pick up. '; } /* * the object is too heavy (in combination with everything else * being carried) for the actor to pick up */ totalTooHeavyForMsg(obj) { gMessageParams(obj); return '{The obj/he} {is} too heavy; {you/he} {will} have to put something else down first. '; } /* object is too large for container */ tooLargeForContainerMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {is} too large for {the cont/him}. '; } /* object is too large to fit under object */ tooLargeForUndersideMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {is} too large to put under {the cont/him}. '; } /* object is too large to fit behind object */ tooLargeForRearMsg(obj, cont) { gMessageParams(obj, cont); return '{The obj/he} {is} too large to put behind {the cont/him}. '; } /* container doesn't have room for object */ containerTooFullMsg(obj, cont) { gMessageParams(obj, cont); return '{The cont/he} {is} already too full to hold {the obj/him}. '; } /* surface doesn't have room for object */ surfaceTooFullMsg(obj, cont) { gMessageParams(obj, cont); return 'There{’s| was} no room for {the obj/him} on ' + '{the cont/him}. '; } /* underside doesn't have room for object */ undersideTooFullMsg(obj, cont) { gMessageParams(obj, cont); return 'There{’s| was} no room for {the obj/him} under ' + '{the cont/him}. '; } /* rear surface/space doesn't have room for object */ rearTooFullMsg(obj, cont) { gMessageParams(obj, cont); return 'There{’s |was} no room for {the obj/him} behind ' + '{the cont/him}. '; } /* the current action would make obj too large for its container */ becomingTooLargeForContainerMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because it would {make|have made} {the obj/him} too large for {the cont/him}. '; } /* * the current action would increase obj's bulk so that container is * too full */ containerBecomingTooFullMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because {the obj/he} would no longer {|have} fit{|ted} in {the cont/him}. '; } /* trying to put an object in a non-container */ notAContainerMsg = '{You/he} {can\'t} put anything in {the iobj/him}. ' /* trying to put an object on a non-surface */ notASurfaceMsg = 'There{’s| was} no good surface on {the iobj/him}. ' /* can't put anything under iobj */ cannotPutUnderMsg = '{You/he} {can\'t} put anything under {that iobj/him}. ' /* nothing can be put behind the given object */ cannotPutBehindMsg = '{You/he} {can\'t} put anything behind {the iobj/him}. ' /* trying to put something in itself */ cannotPutInSelfMsg = '{You/he} {can\'t} put {the dobj/him} in {itself}. ' /* trying to put something on itself */ cannotPutOnSelfMsg = '{You/he} {can\'t} put {the dobj/him} on {itself}. ' /* trying to put something under itself */ cannotPutUnderSelfMsg = '{You/he} {can\'t} put {the dobj/him} under {itself}. ' /* trying to put something behind itself */ cannotPutBehindSelfMsg = '{You/he} {can\'t} put {the dobj/him} behind {itself}. ' /* can't put something in/on/etc a restricted container/surface/etc */ cannotPutInRestrictedMsg = '{You/he} {can\'t} put {that dobj/him} in {the iobj/him}. ' cannotPutOnRestrictedMsg = '{You/he} {can\'t} put {that dobj/him} on {the iobj/him}. ' cannotPutUnderRestrictedMsg = '{You/he} {can\'t} put {that dobj/him} under {the iobj/him}. ' cannotPutBehindRestrictedMsg = '{You/he} {can\'t} put {that dobj/him} behind {the iobj/him}. ' /* trying to return something to a remove-only dispenser */ cannotReturnToDispenserMsg = '{You/he} {can\'t} put {a dobj/him} back in {the iobj/him}. ' /* wrong item type for dispenser */ cannotPutInDispenserMsg = '{You/he} {can\'t} put {a dobj/him} in {the iobj/him}. ' /* the dobj doesn't fit on this keyring */ objNotForKeyringMsg = '{The dobj/he} {does}n’t fit on {the iobj/him}. ' /* the dobj isn't on the keyring */ keyNotOnKeyringMsg = '{The dobj/he} {is} not attached to {the iobj/him}. ' /* can't detach key (with no iobj specified) because it's not on a ring */ keyNotDetachableMsg = '{The dobj/he} {is}n’t attached to anything. ' /* we took a key and attached it to a keyring */ takenAndMovedToKeyringMsg(keyring) { gMessageParams(keyring); return '{You/he} pick{s/ed} up {the dobj/him} and attach{es/ed actor} {it dobj/him} to {the keyring/him}. '; } /* we attached a key to a keyring automatically */ movedKeyToKeyringMsg(keyring) { gMessageParams(keyring); return '{You/he} attach{es/ed} {the dobj/him} to {the keyring/him}. '; } /* we moved several keys to a keyring automatically */ movedKeysToKeyringMsg(keyring, keys) { gMessageParams(keyring); return '{You/he} attach{es/ed} {your/his} loose key' + (keys.length() > 1 ? 's' : '') + ' to {the keyring/him}. '; } /* putting y in x when x is already in y */ circularlyInMsg(x, y) { gMessageParams(x, y); return '{You/he} {can\'t} do that while {the x/he} {is} in {the y/him}. '; } /* putting y in x when x is already on y */ circularlyOnMsg(x, y) { gMessageParams(x, y); return '{You/he} {can\'t} do that while {the x/he} {is} on {the y/him}. '; } /* putting y in x when x is already under y */ circularlyUnderMsg(x, y) { gMessageParams(x, y); return '{You/he} {can\'t} do that while {the x/he} {is} under {the y/him}. '; } /* putting y in x when x is already behind y */ circularlyBehindMsg(x, y) { gMessageParams(x, y); return '{You/he} {can\'t} do that while {the x/he} {is} behind {the y/him}. '; } /* taking dobj from iobj, but dobj isn't in iobj */ takeFromNotInMsg = '{The dobj/he} {is}n’t in {that iobj/him}. ' /* taking dobj from surface, but dobj isn't on iobj */ takeFromNotOnMsg = '{The dobj/he} {is}n’t on {that iobj/him}. ' /* taking dobj from under something, but dobj isn't under iobj */ takeFromNotUnderMsg = '{The dobj/he} {is}n’t under {that iobj/him}. ' /* taking dobj from behind something, but dobj isn't behind iobj */ takeFromNotBehindMsg = '{The dobj/he} {is}n’t behind {that iobj/him}. ' /* taking dobj from an actor, but actor doesn't have iobj */ takeFromNotInActorMsg = '{The iobj/he} {does}n’t have {that dobj/him}. ' /* actor won't let go of a possession */ willNotLetGoMsg(holder, obj) { gMessageParams(holder, obj); return '{The holder/he} {won\'t} let {you/him} have {that obj/him}. '; } /* must say which way to go */ whereToGoMsg = 'You’ll have to say which way to go. ' /* travel attempted in a direction with no exit */ cannotGoThatWayMsg = '{You/he} {can\'t} go that way. ' /* travel attempted in the dark in a direction with no exit */ cannotGoThatWayInDarkMsg = 'It{’s| was} too dark; {you/he} {can\'t} see where {you\'re} going. ' /* we don't know the way back for a GO BACK */ cannotGoBackMsg = '{You/he} {does}n’t know how to return from {|t}here. ' /* cannot carry out a command from this location */ cannotDoFromHereMsg = '{You/he} {can\'t} do that from {|t}here. ' /* can't travel through a close door */ cannotGoThroughClosedDoorMsg(door) { gMessageParams(door); return '{You/he} {can\'t} do that, because {the door/he} {is} ' + 'closed. '; } /* cannot carry out travel while 'dest' is within 'cont' */ invalidStagingContainerMsg(cont, dest) { gMessageParams(cont, dest); return '{You/he} {can\'t} do that while {the dest/he} {is} {in cont}. '; } /* cannot carry out travel while 'cont' (an actor) is holding 'dest' */ invalidStagingContainerActorMsg(cont, dest) { gMessageParams(cont, dest); return '{You/he} {can\'t} do that while {the cont/he} {is} holding {the dest/him}. '; } /* can't carry out travel because 'dest' isn't a valid staging location */ invalidStagingLocationMsg(dest) { gMessageParams(dest); return '{You/he} {can\'t} get {in dest}. '; } /* destination is too high to enter from here */ nestedRoomTooHighMsg(obj) { gMessageParams(obj); return '{The obj/he} {is} too high to reach from {|t}here. '; } /* enclosing room is too high to reach by GETTING OUT OF here */ nestedRoomTooHighToExitMsg(obj) { return 'It{’s| was} too long a drop to do that from {|t}here. '; } /* cannot carry out a command from a nested room */ cannotDoFromMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} do that from {the obj/him}. '; } /* cannot carry out a command from within a vehicle in a nested room */ vehicleCannotDoFromMsg(obj) { local loc = obj.location; gMessageParams(obj, loc); return '{You/he} {can\'t} do that while {the obj/he} {is} {in loc}. '; } /* cannot go that way in a vehicle */ cannotGoThatWayInVehicleMsg(traveler) { gMessageParams(traveler); return '{You/he} {can\'t} do that {in traveler}. '; } /* cannot push an object that way */ cannotPushObjectThatWayMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} go that way pushing {the obj/him}. '; } /* cannot push an object to a nested room */ cannotPushObjectNestedMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} push {the obj/him} there. '; } /* cannot enter an exit-only passage */ cannotEnterExitOnlyMsg(obj) { gMessageParams(obj); return '{You/he} {can\'t} enter {the obj/him} from {|t}here. '; } /* must open door before going that way */ mustOpenDoorMsg(obj) { gMessageParams(obj); return '{You/he} {must} open {the obj/him} first. '; } /* door closes behind actor during travel through door */ doorClosesBehindMsg(obj) { gMessageParams(obj); return '<.p>After {you/he} {goes} through {the obj/him}, {it/he} close{s/d} behind {it actor/him}. '; } /* the stairway does not go up/down */ stairwayNotUpMsg = '{The dobj/he} only {goes} down from {|t}here. ' stairwayNotDownMsg = '{The dobj/he} only {goes} up from {|t}here. ' /* "wait" */ timePassesMsg = 'Time pass{es|ed}... ' /* "hello" with no target actor */ sayHelloMsg = (addressingNoOneMsg) /* "goodbye" with no target actor */ sayGoodbyeMsg = (addressingNoOneMsg) /* "yes"/"no" with no target actor */ sayYesMsg = (addressingNoOneMsg) sayNoMsg = (addressingNoOneMsg) /* an internal common handler for sayHelloMsg, sayGoodbyeMsg, etc */ addressingNoOneMsg { return '{You/he} {must} be more specific about ' + gLibMessages.whomPronoun + ' {it actor/he} want{s/ed} to talk to. '; } /* "yell" */ okayYellMsg = '{You/he} scream{s/ed} as loud as {it actor/he} {can}. ' /* "jump" */ okayJumpMsg = '{You/he} jump{s/ed} a little, and land{s/ed} back where {it actor/he} {|had} started. ' /* cannot jump over object */ cannotJumpOverMsg = '{You/he} {can\'t} jump over {that dobj/him}. ' /* cannot jump off object */ cannotJumpOffMsg = '{You/he} {can\'t} jump off {that dobj/him}. ' /* cannot jump off (with no direct object) from here */ cannotJumpOffHereMsg = 'There{’s| was} nowhere to jump from {|t}here. ' /* failed to find a topic in a consultable object */ cannotFindTopicMsg = '{You/he} {can\'t} seem to find that in {the dobj/him}. ' /* an actor doesn't accept a command from another actor */ refuseCommand(targetActor, issuingActor) { gMessageParams(targetActor, issuingActor); return '{The targetActor/he} refuse{s/d} {your/his} request. '; } /* cannot talk to an object (because it makes no sense to do so) */ notAddressableMsg(obj) { gMessageParams(obj); return '{You/he} {cannot} talk to {that obj/him}. '; } /* actor won't respond to a request or other communicative gesture */ noResponseFromMsg(other) { gMessageParams(other); return '{The other/he} {does} not respond. '; } /* trying to give something to someone who already has the object */ giveAlreadyHasMsg = '{The iobj/he} already {has} {that/him dobj}. ' /* can't talk to yourself */ cannotTalkToSelfMsg = 'Talking to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't ask yourself about anything */ cannotAskSelfMsg = 'Talking to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't ask yourself for anything */ cannotAskSelfForMsg = 'Talking to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't tell yourself about anything */ cannotTellSelfMsg = 'Talking to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't give yourself something */ cannotGiveToSelfMsg = 'Giving {the dobj/him} to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't give something to itself */ cannotGiveToItselfMsg = 'Giving {the dobj/him} to {itself} {won’t|wouldn’t} accomplish anything. ' /* can't show yourself something */ cannotShowToSelfMsg = 'Showing {the dobj/him} to {yourself/himself} {won’t|wouldn’t} accomplish anything. ' /* can't show something to itself */ cannotShowToItselfMsg = 'Showing {the dobj/him} to {itself} {won’t|wouldn’t} accomplish anything. ' /* can't give/show something to a non-actor */ cannotGiveToMsg = '{You/he} {cannot} give anything to {an iobj/him}. ' cannotShowToMsg = '{You/he} {cannot} show anything to {an iobj/him}. ' /* actor isn't interested in something being given/shown */ notInterestedMsg(actor) { return '\^' + actor.nameDoes + ' not appear interested. '; } /* vague ASK/TELL (for ASK/TELL syntax errors) */ askVagueMsg = '<.parser>The story doesn’t understand that command. Please use ASK ACTOR ABOUT TOPIC (or just A TOPIC).<./parser> ' tellVagueMsg = '<.parser>The story doesn’t understand that command. Please use TELL ACTOR ABOUT TOPIC (or just T TOPIC).<./parser> ' /* object cannot hear actor */ objCannotHearActorMsg(obj) { return '\^' + obj.nameDoes + ' not appear to hear {you/him}. '; } /* actor cannot see object being shown to actor */ actorCannotSeeMsg(actor, obj) { return '\^' + actor.nameDoes + ' not appear to be able to see ' + obj.theNameObj + '. '; } /* not a followable object */ notFollowableMsg = '{You/he} {cannot} follow {that dobj/him}. ' /* cannot follow yourself */ cannotFollowSelfMsg = '{You/he} {cannot} follow {yourself}. ' /* following an object that's in the same location as the actor */ followAlreadyHereMsg = '{The dobj/he} {is} right {|t}here. ' /* * following an object that we *think* is in our same location (in * other words, we're already in the location where we thought we * last saw the object go), but it's too dark to see if that's * really true */ followAlreadyHereInDarkMsg = '{The dobj/he} should {be|have been} right {|t}here, but {you/he} {can\'t} see {it dobj/him}. ' /* trying to follow an object, but don't know where it went from here */ followUnknownMsg = '{You\'re} not sure where {the dobj/he} {went|had gone} from {|t}here. ' /* * we're trying to follow an actor, but we last saw the actor in the * given other location, so we have to go there to follow */ cannotFollowFromHereMsg(srcLoc) { return 'The last place {you/he} {saw|had seen} {the dobj/him} was ' + srcLoc.getDestName(gActor, gActor.location) + '. '; } /* acknowledge a 'follow' for a target that was in sight */ okayFollowInSightMsg(loc) { return '{You/he} follow{s/ed} {the dobj/him} ' + loc.actorIntoName + '. '; } /* obj is not a weapon */ notAWeaponMsg = '{You/he} {can\'t} attack anything with {the iobj/him}. ' /* no effect attacking obj */ uselessToAttackMsg = '{You/he} {cannot} attack {that dobj/him}. ' /* pushing object has no effect */ pushNoEffectMsg = 'Pushing {the dobj/him} {has|had} no effect. ' /* default 'push button' acknowledgment */ okayPushButtonMsg = 'Click. ' /* lever is already in pushed state */ alreadyPushedMsg = '{It\'s dobj} already pushed as far as {it dobj/he} {will} go. ' /* default acknowledgment to pushing a lever */ okayPushLeverMsg = '{You/he} push{es/ed} {the dobj/him} to {its/her dobj} stop. ' /* pulling object has no effect */ pullNoEffectMsg = 'Pulling {the dobj/him} {has|had} no effect. ' /* lever is already in pulled state */ alreadyPulledMsg = '{It\'s dobj} already pulled as far as {it dobj/he} {will} go. ' /* default acknowledgment to pulling a lever */ okayPullLeverMsg = '{You/he} pull{s/ed} {the dobj/him} to {its/her dobj} stop. ' /* default acknowledgment to pulling a spring-loaded lever */ okayPullSpringLeverMsg = '{You/he} pull{s/ed} {the dobj/him}, which spr{ing[s]|ung} back to {its/her} starting position as soon as {you/he} let{[s]|} go of {it dobj/him}. ' /* moving object has no effect */ moveNoEffectMsg = 'Moving {the dobj/him} {has|had} no effect. ' /* cannot move object to other object */ moveToNoEffectMsg = 'This would {|have} accomplish{|ed} nothing. ' /* cannot push an object through travel */ cannotPushTravelMsg = 'This would {|have} accomplish{|ed} nothing. ' /* acknowledge pushing an object through travel */ okayPushTravelMsg(obj) { return '<.p>{You/he} push{es/ed} ' + obj.theNameObj + ' into the area. '; } /* cannot use object as an implement to move something */ cannotMoveWithMsg = '{You/he} {cannot} move anything with {the iobj/him}. ' /* cannot set object to setting */ cannotSetToMsg = '{You/he} {cannot} set {that dobj/him} to anything. ' /* invalid setting for generic Settable */ setToInvalidMsg = '{The dobj/he} {has} no such setting. ' /* default 'set to' acknowledgment */ okaySetToMsg(val) { return 'Okay, {the dobj/he} {is} now set to ' + val + '. '; } /* cannot turn object */ cannotTurnMsg = '{You/he} {cannot} turn {that dobj/him}. ' /* must specify setting to turn object to */ mustSpecifyTurnToMsg = '{You/he} {must} specify the setting to turn {it dobj/him} to. ' /* cannot turn anything with object */ cannotTurnWithMsg = '{You/he} {cannot} turn anything with {that iobj/him}. ' /* invalid setting for dial */ turnToInvalidMsg = '{The dobj/he} {has} no such setting. ' /* default 'turn to' acknowledgment */ okayTurnToMsg(val) { return 'Okay, {the dobj/he} {is} now set to ' + val + '. '; } /* switch is already on/off */ alreadySwitchedOnMsg = '{The dobj/he} {is} already on. ' alreadySwitchedOffMsg = '{The dobj/he} {is} already off. ' /* default acknowledgment for switching on/off */ okayTurnOnMsg = 'Okay, {the dobj/he} {is} now on. ' okayTurnOffMsg = 'Okay, {the dobj/he} {is} now off. ' /* flashlight is on but doesn't light up */ flashlightOnButDarkMsg = '{You/he} turn{s/ed} on {the dobj/him}, but nothing seem{s|ed} to happen. ' /* default acknowledgment for eating something */ okayEatMsg = '{You/he} {eat[s]|ate} {the dobj/him}. ' /* object must be burning before doing that */ mustBeBurningMsg(obj) { return '{You/he} {must} light ' + obj.theNameObj + ' before {it actor/he} {can} do that. '; } /* match not lit */ matchNotLitMsg = '{The dobj/he} {is}n’t lit. ' /* lighting a match */ okayBurnMatchMsg = '{You/he} str{ike[s]|uck} {the dobj/him}, igniting a small flame. ' /* extinguishing a match */ okayExtinguishMatchMsg = '{You/he} put{[s]|} out {the dobj/him}, which disappear{s/ed} into a cloud of ash. ' /* trying to light a candle with no fuel */ candleOutOfFuelMsg = '{The dobj/he} {is} too burned down; {it/he} {cannot} be lit. ' /* lighting a candle */ okayBurnCandleMsg = '{You/he} {light[s]|lit} {the dobj/him}. ' /* extinguishing a candle that isn't lit */ candleNotLitMsg = '{The dobj/he} {is} not lit. ' /* extinguishing a candle */ okayExtinguishCandleMsg = 'Done. ' /* cannot consult object */ cannotConsultMsg = '{That dobj/he} {is} not something {you/he} {can} consult. ' /* cannot type anything on object */ cannotTypeOnMsg = '{You/he} {cannot} type anything on {that dobj/him}. ' /* cannot enter anything on object */ cannotEnterOnMsg = '{You/he} {cannot} enter anything on {that dobj/him}. ' /* cannot switch object */ cannotSwitchMsg = '{You/he} {cannot} switch {that dobj/him}. ' /* cannot flip object */ cannotFlipMsg = '{You/he} {cannot} flip {that dobj/him}. ' /* cannot turn object on/off */ cannotTurnOnMsg = '{That dobj/he} {is}n’t something {you/he} {can} turn on. ' cannotTurnOffMsg = '{That dobj/he} {is}n’t something {you/he} {can} turn off. ' /* cannot light */ cannotLightMsg = '{You/he} {cannot} light {that dobj/him}. ' /* cannot burn */ cannotBurnMsg = '{That dobj/he} {is} not something {you/he} {can} burn. ' cannotBurnWithMsg = '{You/he} {cannot} burn anything with {that iobj/him}. ' /* cannot burn this specific direct object with this specific iobj */ cannotBurnDobjWithMsg = '{You/he} {cannot} light {the dobj/him} with {the iobj/him}. ' /* object is already burning */ alreadyBurningMsg = '{The dobj/he} {is} already burning. ' /* cannot extinguish */ cannotExtinguishMsg = '{You/he} {cannot} extinguish {that dobj/him}. ' /* cannot pour/pour in/pour on */ cannotPourMsg = '{That dobj/he} {is} not something {you/he} {can} pour. ' cannotPourIntoMsg = '{You/he} {cannot} pour anything into {that iobj/him}. ' cannotPourOntoMsg = '{You/he} {cannot} pour anything onto {that iobj/him}. ' /* cannot attach object to object */ cannotAttachMsg = '{You/he} {cannot} attach {that dobj/him} to anything. ' cannotAttachToMsg = '{You/he} {cannot} attach anything to {that iobj/him}. ' /* cannot attach to self */ cannotAttachToSelfMsg = '{You/he} {cannot} attach {the dobj/him} to {itself}. ' /* cannot attach because we're already attached to the given object */ alreadyAttachedMsg = '{The dobj/he} {is} already attached to {the iobj/him}. ' /* * dobj and/or iobj can be attached to certain things, but not to * each other */ wrongAttachmentMsg = '{You/he} {can\'t} attach {that dobj/him} to {the iobj/him}. ' /* dobj and iobj are attached, but they can't be taken apart */ wrongDetachmentMsg = '{You/he} {can\'t} detach {that dobj/him} from {the iobj/him}. ' /* must detach the object before proceeding */ mustDetachMsg(obj) { gMessageParams(obj); return '{You/he} {must} detach {the obj/him} before {it actor/he} {can} do that. '; } /* default message for successful Attachable attachment */ okayAttachToMsg = 'Done. ' /* default message for successful Attachable detachment */ okayDetachFromMsg = 'Done. ' /* cannot detach object from object */ cannotDetachMsg = '{You/he} {cannot} detach {that dobj/him}. ' cannotDetachFromMsg = '{You/he} {cannot} detach anything from {that iobj/him}. ' /* no obvious way to detach a permanent attachment */ cannotDetachPermanentMsg = 'There{’s| was} no obvious way to detach {that dobj/him}. ' /* dobj isn't attached to iobj */ notAttachedToMsg = '{The dobj/he} {is}n’t attached to {that iobj/him}. ' /* breaking object would serve no purpose */ shouldNotBreakMsg = 'Breaking {that dobj/him} would {|have} serve{|d} no purpose. ' /* cannot cut that */ cutNoEffectMsg = '{The iobj/he} {can\'t} seem to cut {the dobj/him}. ' /* can't use iobj to cut anything */ cannotCutWithMsg = '{You/he} {can\'t} cut anything with {the iobj/him}. ' /* cannot climb object */ cannotClimbMsg = '{That dobj/he} {is} not something {you/he} {can} climb. ' /* object is not openable/closable */ cannotOpenMsg = '{That dobj/he} {is} not something {you/he} {can} open. ' cannotCloseMsg = '{That dobj/he} {is} not something {you/he} {can} close. ' /* already open/closed */ alreadyOpenMsg = '{The dobj/he} {is} already open. ' alreadyClosedMsg = '{The dobj/he} {is} already closed. ' /* already locked/unlocked */ alreadyLockedMsg = '{The dobj/he} {is} already locked. ' alreadyUnlockedMsg = '{The dobj/he} {is} already unlocked. ' /* cannot look in container because it's closed */ cannotLookInClosedMsg = '{The dobj/he} {is} closed. ' /* object is not lockable/unlockable */ cannotLockMsg = '{That dobj/he} {is} not something {you/he} {can} lock. ' cannotUnlockMsg = '{That dobj/he} {is} not something {you/he} {can} unlock. ' /* attempting to open a locked object */ cannotOpenLockedMsg = '{The dobj/he} seem{s/ed} to be locked. ' /* object requires a key to unlock */ unlockRequiresKeyMsg = '{You/he} seem{s/ed} to need a key to unlock {the dobj/him}. ' /* object is not a key */ cannotLockWithMsg = '{The iobj/he} {does}n’t look suitable for locking that. ' cannotUnlockWithMsg = '{The iobj/he} {does}n’t look suitable for unlocking that. ' /* we don't know how to lock/unlock this */ unknownHowToLockMsg = 'It{’s| was} not clear how to lock {the dobj/him}. ' unknownHowToUnlockMsg = 'It{’s| was} not clear how to unlock {the dobj/him}. ' /* the key (iobj) does not fit the lock (dobj) */ keyDoesNotFitLockMsg = '{The iobj/he} {does}n’t fit the lock. ' /* found key on keyring */ foundKeyOnKeyringMsg(ring, key) { gMessageParams(ring, key); return '{You/he} tr{ies/ied} each key on {the ring/him}, and {find[s actor]|found} that {the key/he} fit{s/ted} the lock. '; } /* failed to find a key on keyring */ foundNoKeyOnKeyringMsg(ring) { gMessageParams(ring); return '{You/he} tr{ies/ied} each key on {the ring/him}, but {you/he} {can\'t} find anything that fit{s|ted} the lock. '; } /* not edible/drinkable */ cannotEatMsg = '{The dobj/he} {does} not appear to be edible. ' cannotDrinkMsg = '{That dobj/he} {does} not appear to be something {you/he} {can} drink. ' /* cannot clean object */ cannotCleanMsg = '{You/he} wouldn’t {|have} know{|n} how to clean {that dobj/him}. ' cannotCleanWithMsg = '{You/he} {can\'t} clean anything with {that iobj/him}. ' /* cannot attach key (dobj) to (iobj) */ cannotAttachKeyToMsg = '{You/he} {cannot} attach {the dobj/him} to {that iobj/him}. ' /* actor cannot sleep */ cannotSleepMsg = '{You/he} {does}n’t need to sleep right now. ' /* cannot sit/lie/stand/get on/get out of */ cannotSitOnMsg = '{That dobj/he} {is}n’t something {you/he} {can} sit on. ' cannotLieOnMsg = '{That dobj/he} {is}n’t something {you/he} {can} lie on. ' cannotStandOnMsg = '{You/he} {can\'t} stand on {that dobj/him}. ' cannotBoardMsg = '{You/he} {can\'t} board {that dobj/him}. ' cannotUnboardMsg = '{You/he} {can\'t} get out of {that dobj/him}. ' cannotGetOffOfMsg = '{You/he} {can\'t} get off of {that dobj/him}. ' /* standing on a PathPassage */ cannotStandOnPathMsg = 'If {you/he} want{s/ed} to follow {the dobj/him}, just say so. ' /* cannot sit/lie/stand on something being held */ cannotEnterHeldMsg = '{You/he} {can\'t} do that while {you\'re} holding {the dobj/him}. ' /* cannot get out (of current location) */ cannotGetOutMsg = '{You\'re} not in anything {you/he} {can} disembark. ' /* actor is already in a location */ alreadyInLocMsg = '{You\'re} already {in dobj}. ' /* actor is already standing/sitting on/lying on */ alreadyStandingMsg = '{You\'re} already standing. ' alreadyStandingOnMsg = '{You\'re} already standing {on dobj}. ' alreadySittingMsg = '{You\'re} already sitting down. ' alreadySittingOnMsg = '{You\'re} already sitting {on dobj}. ' alreadyLyingMsg = '{You\'re} already lying down. ' alreadyLyingOnMsg = '{You\'re} already lying {on dobj}. ' /* getting off something you're not on */ notOnPlatformMsg = '{You\'re} not {on dobj}. ' /* no room to stand/sit/lie on dobj */ noRoomToStandMsg = 'There{’s| was} no room for {you/him} to stand {on dobj}. ' noRoomToSitMsg = 'There{’s| was} no room for {you/him} to sit {on dobj}. ' noRoomToLieMsg = 'There{’s| was} no room for {you/him} to lie {on dobj}. ' /* default report for standing up/sitting down/lying down */ okayPostureChangeMsg(posture) { return 'Okay, {you\'re} now ' + posture.participle + '. '; } /* default report for standing/sitting/lying in/on something */ roomOkayPostureChangeMsg(posture, obj) { gMessageParams(obj); return 'Okay, {you\'re} now ' + posture.participle + ' {on obj}. '; } /* default report for getting off of a platform */ okayNotStandingOnMsg = 'Okay, {you\'re} no longer {on dobj}. ' /* cannot fasten/unfasten */ cannotFastenMsg = '{You/he} {cannot} fasten {the dobj/him}. ' cannotFastenToMsg = '{You/he} {cannot} fasten anything to {the iobj/him}. ' cannotUnfastenMsg = '{You/he} {cannot} unfasten {the dobj/him}. ' cannotUnfastenFromMsg = '{You/he} {cannot} unfasten anything from {the iobj/him}. ' /* cannot plug/unplug */ cannotPlugInMsg = '{You/he} {sees} no way to plug in {the dobj/him}. ' cannotPlugInToMsg = '{You/he} {sees} no way to plug anything into {the iobj/him}. ' cannotUnplugMsg = '{You/he} {sees} no way to unplug {the dobj/him}. ' cannotUnplugFromMsg = '{You/he} {sees} no way to unplug anything from {the iobj/him}. ' /* cannot screw/unscrew */ cannotScrewMsg = '{You/he} {sees} no way to screw {the dobj/him}. ' cannotScrewWithMsg = '{You/he} {cannot} screw anything with {the iobj/him}. ' cannotUnscrewMsg = '{You/he} {sees} no way to unscrew {the dobj/him}. ' cannotUnscrewWithMsg = '{You/he} {cannot} unscrew anything with {the iobj/him}. ' /* cannot enter/go through */ cannotEnterMsg = '{That/he dobj} {is} not something {you/he} {can} enter. ' cannotGoThroughMsg = '{That/he dobj} {is} not something {you/he} {can} go through. ' /* can't throw something at itself */ cannotThrowAtSelfMsg = '{You/he} {can\'t} throw {that dobj/him} at {itself}. ' /* can't throw something at an object inside itself */ cannotThrowAtContentsMsg = '{You/he} {must} remove {the iobj/him} from {the dobj/him} before {it actor/he} {can} do that. ' /* can't throw through a sense connector */ cannotThrowThroughMsg(target, loc) { gMessageParams(target, loc); return '{You/he} {cannot} throw anything through {the loc/him}. '; } /* shouldn't throw something at the floor */ shouldNotThrowAtFloorMsg = '{You/he} should just {|have} put {it dobj/him} down instead. ' /* THROW isn't supported; use THROW AT instead */ dontThrowDirMsg = ('<.parser>You need to be more specific about what ' + (gActor.referralPerson == ThirdPerson ? 'you want {the actor/him}' : '') + ' to throw {the dobj/him} at.<./parser> ') /* thrown object bounces off target (short report) */ throwHitMsg(projectile, target) { gMessageParams(projectile, target); return '{The projectile/he} hit{[s]|} {the target/him} without any obvious effect. '; } /* thrown object lands on target */ throwFallMsg(projectile, target) { gMessageParams(projectile, target); return '{The projectile/he} land{s/ed} on {the target/him}. '; } /* thrown object bounces off target and falls to destination */ throwHitFallMsg(projectile, target, dest) { gMessageParams(projectile, target); return '{The projectile/he} hit{[s]|} {the target/him} without any obvious effect, and {fall[s projectile]|fell} ' + dest.putInName + '. '; } /* thrown object falls short of distant target (sentence prefix only) */ throwShortMsg(projectile, target) { gMessageParams(projectile, target); return '{The projectile/he} {fall[s]|fell} well short of ' + '{the target/him}. '; } /* thrown object falls short of distant target */ throwFallShortMsg(projectile, target, dest) { gMessageParams(projectile, target); return '{The projectile/he} {fall[s]|fell} ' + dest.putInName + ' well short of {the target/him}. '; } /* target catches object */ throwCatchMsg(obj, target) { return '\^' + target.theName + ' ' + tSel('catch' + target.verbEndingEs, 'caught') + ' ' + obj.theNameObj + '. '; } /* we're not a suitable target for THROW TO (because we're not an NPC) */ cannotThrowToMsg = '{You/he} {cannot} throw anything to {an iobj/him}. ' /* target does not want to catch anything */ willNotCatchMsg(catcher) { return '\^' + catcher.nameDoes + 'n’t look like ' + catcher.itNom + ' want' + catcher.verbEndingSEd + ' to catch anything. '; } /* cannot kiss something */ cannotKissMsg = 'Kissing {the dobj/him} {has|had} no obvious effect. ' /* person uninterested in being kissed */ cannotKissActorMsg = '{The dobj/he} probably wouldn’t {|have} like{|d} that. ' /* cannot kiss yourself */ cannotKissSelfMsg = '{You/he} {cannot} kiss {yourself}. ' /* it is now dark at actor's location */ newlyDarkMsg = 'It {is|was} now pitch black. ' ; /* * Non-player character verb messages. By default, we inherit all of * the messages defined for the player character, but we override some * that must be rephrased slightly to make sense for NPC's. */ npcActionMessages: playerActionMessages /* "wait" */ timePassesMsg = '{You/he} wait{s/ed}... ' /* trying to move a Fixture/Immovable */ cannotMoveFixtureMsg = '{You/he} {cannot} move {that dobj/him}. ' cannotMoveImmovableMsg = '{You/he} {cannot} move {that dobj/him}. ' /* trying to take/move/put a Heavy object */ cannotTakeHeavyMsg = '{That dobj/he} {is} too heavy for {you/him} to take. ' cannotMoveHeavyMsg = '{That dobj/he} {is} too heavy for {you/him} to move. ' cannotPutHeavyMsg = '{That dobj/he} {is} too heavy for {you/him} to move. ' /* trying to move a component object */ cannotMoveComponentMsg(loc) { return '{You/he} {cannot} do that because {the dobj/he} {is} part of ' + loc.theNameObj + '. '; } /* default successful 'take' response */ okayTakeMsg = '{You/he} {take[s]|took} {the dobj/him}. ' /* default successful 'drop' response */ okayDropMsg = '{You/he} put{[s]|} down {the dobj/him}. ' /* default successful 'put in' response */ okayPutInMsg = '{You/he} put{[s]|} {the dobj/him} in {the iobj/him}. ' /* default successful 'put on' response */ okayPutOnMsg = '{You/he} put{[s]|} {the dobj/him} on {the iobj/him}. ' /* default successful 'put under' response */ okayPutUnderMsg = '{You/he} put{[s]|} {the dobj/him} under {the iobj/him}. ' /* default successful 'put behind' response */ okayPutBehindMsg = '{You/he} put{[s]|} {the dobj/him} behind {the iobj/him}. ' /* default succesful response to 'wear obj' */ okayWearMsg = '{You/he} put{[s]|} on {the dobj/him}. ' /* default successful response to 'doff obj' */ okayDoffMsg = '{You/he} {take[s]|took} off {the dobj/him}. ' /* default successful responses to open/close */ okayOpenMsg = '{You/he} open{s/ed} {the dobj/him}. ' okayCloseMsg = '{You/he} close{s/d} {the dobj/him}. ' /* default successful responses to lock/unlock */ okayLockMsg = '{You/he} lock{s/ed} {the dobj/him}. ' okayUnlockMsg = '{You/he} unlock{s/ed} {the dobj/him}. ' /* push/pull/move with no effect */ pushNoEffectMsg = '{You/he} tr{ies/ied} to push {the dobj/him}, with no ' + 'obvious effect. ' pullNoEffectMsg = '{You/he} tr{ies/ied} to pull {the dobj/him}, with no ' + 'obvious effect. ' moveNoEffectMsg = '{You/he} tr{ies/ied} to move {the dobj/him}, with no ' + 'obvious effect. ' moveToNoEffectMsg = '{You/he} {leaves} {the dobj/he} where {it/he} {is}. ' whereToGoMsg = 'You’ll have to say which way {you/he} should {|have} go{|ne}. ' /* object is too large for container */ tooLargeForContainerMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because {the obj/he} {is} too large for {the cont/him}. '; } /* object is too large for underside */ tooLargeForUndersideMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because {the obj/he} {won\'t} fit under {the cont/him}. '; } /* object is too large to fit behind something */ tooLargeForRearMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because {the obj/he} {won\'t} fit behind {the cont/him}. '; } /* container doesn't have room for object */ containerTooFullMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because {the cont/he} {is} already too full to hold {the obj/him}. '; } /* surface doesn't have room for object */ surfaceTooFullMsg(obj, cont) { gMessageParams(obj, cont); return '{You/he} {cannot} do that because there{\'s| was} no room for {the obj/him} on {the cont/him}. '; } /* the dobj doesn't fit on this keyring */ objNotForKeyringMsg = '{You/he} {cannot} do that because {that dobj/he} {does}n’t fit on {the iobj/him}. ' /* taking dobj from iobj, but dobj isn't in iobj */ takeFromNotInMsg = '{You/he} {cannot} do that because {the dobj/he} {is}n’t in {that iobj/him}. ' /* taking dobj from surface, but dobj isn't on iobj */ takeFromNotOnMsg = '{You/he} {cannot} do that because {the dobj/he} {is}n’t on {that iobj/him}. ' /* taking dobj under something, but dobj isn't under iobj */ takeFromNotUnderMsg = '{You/he} {cannot} do that because {the dobj/he} {is}n’t under {that iobj/him}. ' /* taking dobj from behind something, but dobj isn't behind iobj */ takeFromNotBehindMsg = '{You/he} {cannot} do that because {the dobj/he} {is}n’t behind {that iobj/him}. ' /* cannot jump off (with no direct object) from here */ cannotJumpOffHereMsg = 'There{’s| was} nowhere for {you/him} to jump. ' /* should not break object */ shouldNotBreakMsg = '{You/he} {does}n’t want to break {that dobj/him}. ' /* report for standing up/sitting down/lying down */ okayPostureChangeMsg(posture) { return '{You/he} ' + posture.msgVerbI + '. '; } /* report for standing/sitting/lying in/on something */ roomOkayPostureChangeMsg(posture, obj) { gMessageParams(obj); return '{You/he} ' + posture.msgVerbT + ' {on obj}. '; } /* report for getting off a platform */ okayNotStandingOnMsg = '{You/he} {get[s]|got} {offof dobj}. ' /* default 'turn to' acknowledgment */ okayTurnToMsg(val) { return '{You/he} turn{s/ed} {the dobj/him} to ' + val + '. '; } /* default 'push button' acknowledgment */ okayPushButtonMsg = '{You/he} push{es/ed} {the dobj/him}. ' /* default acknowledgment for switching on/off */ okayTurnOnMsg = '{You/he} turn{s/ed} {the dobj/him} on. ' okayTurnOffMsg = '{You/he} turn{s/ed} {the dobj/him} off. ' /* the key (iobj) does not fit the lock (dobj) */ keyDoesNotFitLockMsg = '{You/he} tr{ies/ied} {the iobj/he}, but {it iobj/he} {does}n’t fit the lock. ' /* acknowledge entering "follow" mode */ okayFollowModeMsg = 'Okay, I will follow {the dobj/him}. ' /* note that we're already in "follow" mode */ alreadyFollowModeMsg = 'I\'m already following {the dobj/him}. ' /* extinguishing a candle */ okayExtinguishCandleMsg = '{You/he} extinguish{es/ed} {the dobj/him}. ' /* acknowledge attachment */ okayAttachToMsg = '{You/he} attach{es/ed} {the dobj/him} to {the iobj/him}. ' /* acknowledge detachment */ okayDetachFromMsg = '{You/he} detach{es/ed} {the dobj/him} from {the iobj/him}. ' /* * the PC's responses to conversational actions applied to oneself * need some reworking for NPC's */ cannotTalkToSelfMsg = '{You/he} {won\'t} accomplish anything talking to {yourself/himself}. ' cannotAskSelfMsg = '{You/he} {won\'t} accomplish anything talking to {yourself/himself}. ' cannotAskSelfForMsg = '{You/he} {won\'t} accomplish anything talking to {yourself/himself}. ' cannotTellSelfMsg = '{You/he} {won\'t} accomplish anything talking to {yourself/himself}. ' cannotGiveToSelfMsg = '{You/he} {won\'t} accomplish anything giving {the dobj/him} to {yourself/himself}. ' cannotShowToSelfMsg = '{You/he} {won\'t} accomplish anything showing {the dobj/him} to {yourself/himself}. ' ; /* ------------------------------------------------------------------------ */ /* * Standard tips */ scoreChangeTip: Tip "If you’d prefer not to be notified about score changes in the future, type <>." ; footnotesTip: Tip "A number in [square brackets] like the one above refers to a footnote, which you can read by typing FOOTNOTE followed by the number: <>, for example. Footnotes usually contain added background information that might be interesting but isn’t essential to the story. If you’d prefer not to see footnotes at all, you can control their appearance by typing <>." ; oopsTip: Tip "If this was an accidental misspelling, you can correct it by typing OOPS followed by the corrected word now. Any time the story points out an unknown word, you can correct a misspelling using OOPS as your next command." ; fullScoreTip: Tip "To see a detailed accounting of your score, type <>." ; exitsTip: Tip "You can control the exit listings with the EXITS command. <> shows the exit list in the status line, <> shows a full exit list in each room description, <> shows both, and <> turns off both kinds of exit lists." ; undoTip: Tip "If this didn't turn out quite the way you expected, note that you can always take back a turn by typing <>. You can even use UNDO repeatedly to take back several turns in a row. " ; /* ------------------------------------------------------------------------ */ /* * Listers */ /* * The basic "room lister" object - this is the object that we use by * default with showList() to construct the listing of the portable * items in a room when displaying the room's description. */ roomLister: Lister /* show the prefix/suffix in wide mode */ showListPrefixWide(itemCount, pov, parent) { "{You/he} {sees} "; } showListSuffixWide(itemCount, pov, parent) { " {|t}here. "; } /* show the tall prefix */ showListPrefixTall(itemCount, pov, parent) { "{You/he} {sees}:"; } ; /* * The basic room lister for dark rooms */ darkRoomLister: Lister showListPrefixWide(itemCount, pov, parent) { "In the darkness {you/he} {can} see "; } showListSuffixWide(itemCount, pov, parent) { ". "; } showListPrefixTall(itemCount, pov, parent) { "In the darkness {you/he} {sees}:"; } ; /* * A "remote room lister". This is used to describe the contents of an * adjoining room. For example, if an actor is standing in one room, * and can see into a second top-level room through a window, we'll use * this lister to describe the objects the actor can see through the * window. */ class RemoteRoomLister: Lister construct(room) { remoteRoom = room; } showListPrefixWide(itemCount, pov, parent) { "\^<>, {you/he} {sees} "; } showListSuffixWide(itemCount, pov, parent) { ". "; } showListPrefixTall(itemCount, pov, parent) { "\^<>, {you/he} {sees}:"; } /* the remote room we're viewing */ remoteRoom = nil ; /* * A simple customizable room lister. This can be used to create custom * listers for things like remote room contents listings. We act just * like any ordinary room lister, but we use custom prefix and suffix * strings provided during construction. */ class CustomRoomLister: Lister construct(prefix, suffix) { prefixStr = prefix; suffixStr = suffix; } showListPrefixWide(itemCount, pov, parent) { "<> "; } showListSuffixWide(itemCount, pov, parent) { "<> "; } showListPrefixTall(itemCount, pov, parent) { "<>:"; } /* our prefix and suffix strings */ prefixStr = nil suffixStr = nil ; /* * Single-list inventory lister. This shows the inventory listing as a * single list, with worn items mixed in among the other inventory items * and labeled "(being worn)". */ actorSingleInventoryLister: InventoryLister showListPrefixWide(itemCount, pov, parent) { "<> {is} carrying "; } showListSuffixWide(itemCount, pov, parent) { ". "; } showListPrefixTall(itemCount, pov, parent) { "<> {is} carrying:"; } showListContentsPrefixTall(itemCount, pov, parent) { "<>, who {is} carrying:"; } showListEmpty(pov, parent) { "<> {is} empty-handed. "; } ; /* * Standard inventory lister for actors - this will work for the player * character and NPC's as well. This lister uses a "divided" format, * which segregates the listing into items being carried and items being * worn. We'll combine the two lists into a single sentence if the * overall list is short, otherwise we'll show two separate sentences for * readability. */ actorInventoryLister: DividedInventoryLister /* * Show the combined inventory listing, putting together the raw * lists of the items being carried and the items being worn. */ showCombinedInventoryList(parent, carrying, wearing) { /* if one or the other sentence is empty, the format is simple */ if (carrying == '' && wearing == '') { /* the parent is completely empty-handed */ showInventoryEmpty(parent); } else if (carrying == '') { /* the whole list is being worn */ showInventoryWearingOnly(parent, wearing); } else if (wearing == '') { /* the whole list is being carried */ showInventoryCarryingOnly(parent, carrying); } else { /* * Both listings are populated. Count the number of * comma-separated or semicolon-separated phrases in each * list. This will give us an estimate of the grammatical * complexity of each list. If we have very short lists, a * single sentence will be easier to read; if the lists are * long, we'll show the lists in separate sentences. */ if (countPhrases(carrying) + countPhrases(wearing) <= singleSentenceMaxNouns) { /* short enough: use a single-sentence format */ showInventoryShortLists(parent, carrying, wearing); } else { /* long: use a two-sentence format */ showInventoryLongLists(parent, carrying, wearing); } } } /* * Count the noun phrases in a string. We'll count the number of * elements in the list as indicated by commas and semicolons. This * might not be a perfect count of the actual number of noun phrases, * since we could have commas setting off some other kind of clauses, * but it nonetheless will give us a good estimate of the overall * complexity of the text, which is what we're really after. The * point is that we want to break up the listings if they're long, * but combine them into a single sentence if they're short. */ countPhrases(txt) { local cnt; /* if the string is empty, there are no phrases at all */ if (txt == '') return 0; /* a non-empty string has at least one phrase */ cnt = 1; /* scan for commas and semicolons */ for (local startIdx = 1 ;;) { local idx; /* find the next phrase separator */ idx = rexSearch(phraseSepPat, txt, startIdx); /* if we didn't find it, we're done */ if (idx == nil) break; /* count it */ ++cnt; /* continue scanning after the separator */ startIdx = idx[1] + idx[2]; } /* return the count */ return cnt; } phraseSepPat = static new RexPattern(',(?! and )|;| and |') /* * Once we've made up our mind about the format, we'll call one of * these methods to show the final sentence. These are all separate * methods so that the individual formats can be easily tweaked * without overriding the whole combined-inventory-listing method. */ showInventoryEmpty(parent) { /* empty inventory */ "<> {is} empty-handed. "; } showInventoryWearingOnly(parent, wearing) { /* we're carrying nothing but wearing some items */ "<> {is} carrying nothing, and {is} wearing <>. "; } showInventoryCarryingOnly(parent, carrying) { /* we have only carried items to report */ "<> {is} carrying <>. "; } showInventoryShortLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* short lists - combine carried and worn in a single sentence */ "<> {is} carrying <>, and <>{subj} wearing <>. "; } showInventoryLongLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* long lists - show carried and worn in separate sentences */ "<> {is} carrying <>. <> wearing <>. "; } /* * For 'tall' listings, we'll use the standard listing style, so we * need to provide the framing messages for the tall-mode listing. */ showListPrefixTall(itemCount, pov, parent) { "<> {is} carrying:"; } showListContentsPrefixTall(itemCount, pov, parent) { "<>, who {is} carrying:"; } showListEmpty(pov, parent) { "<> {is} empty-handed. "; } ; /* * Special inventory lister for non-player character descriptions - long * form lister. This is used to display the inventory of an NPC as part * of the full description of the NPC. * * This long form lister is meant for actors with lengthy descriptions. * We start the inventory listing on a new line, and use the actor's * full name in the list preface. */ actorHoldingDescInventoryListerLong: actorInventoryLister showInventoryEmpty(parent) { /* empty inventory - saying nothing in an actor description */ } showInventoryWearingOnly(parent, wearing) { /* we're carrying nothing but wearing some items */ "<.p><> {is} wearing <>. "; } showInventoryCarryingOnly(parent, carrying) { /* we have only carried items to report */ "<.p><> {is} carrying <>. "; } showInventoryShortLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* short lists - combine carried and worn in a single sentence */ "<.p><> {is} carrying <>, and <>{subj} wearing <>. "; } showInventoryLongLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* long lists - show carried and worn in separate sentences */ "<.p><> {is} carrying <>. <> wearing <>. "; } ; /* short form of non-player character description inventory lister */ actorHoldingDescInventoryListerShort: actorInventoryLister showInventoryEmpty(parent) { /* empty inventory - saying nothing in an actor description */ } showInventoryWearingOnly(parent, wearing) { /* we're carrying nothing but wearing some items */ "<> {is} wearing <>. "; } showInventoryCarryingOnly(parent, carrying) { /* we have only carried items to report */ "<> {is} carrying <>. "; } showInventoryShortLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* short lists - combine carried and worn in a single sentence */ "<> {is} carrying <>, and <>{subj} wearing <>. "; } showInventoryLongLists(parent, carrying, wearing) { local nm = gSynthMessageParam(parent); /* long lists - show carried and worn in separate sentences */ "<> {is} carrying <>. <> wearing <>. "; } ; /* * Base contents lister for things. This is used to display the contents * of things shown in room and inventory listings; we subclass this for * various purposes */ class BaseThingContentsLister: Lister showListPrefixWide(itemCount, pov, parent) { "\^<> "; } showListSuffixWide(itemCount, pov, parent) { ". "; } showListPrefixTall(itemCount, pov, parent) { "\^<>:"; } showListContentsPrefixTall(itemCount, pov, parent) { "<>, which contain<>:"; } ; /* * Contents lister for things. This is used to display the second-level * contents lists for things listed in the top-level list for a room * description, inventory listing, or object description. */ thingContentsLister: ContentsLister, BaseThingContentsLister ; /* * Contents lister for descriptions of things - this is used to display * the contents of a thing as part of the long description of the thing * (in response to an "examine" command); it differs from a regular * thing contents lister in that we use a pronoun to refer to the thing, * rather than its full name, since the full name was probably just used * in the basic long description. */ thingDescContentsLister: DescContentsLister, BaseThingContentsLister showListPrefixWide(itemCount, pov, parent) { "\^<> "; } ; /* * Contents lister for openable things. */ openableDescContentsLister: thingDescContentsLister showListEmpty(pov, parent) { "\^<>. "; } showListPrefixWide(itemCount, pov, parent) { "\^<>, and contain<> "; } ; /* * Base contents lister for "LOOK " commands (LOOK IN, LOOK UNDER, * LOOK BEHIND, etc). This can be subclasses for the appropriate LOOK * command matching the container type - LOOK UNDER for * undersides, LOOK BEHIND for rear containers, etc. To use this class, * combine it via multiple inheritance with the appropriate * BaseContentsLister for the preposition type. */ class LookWhereContentsLister: DescContentsLister showListEmpty(pov, parent) { /* show a default message indicating the surface is empty */ gMessageParams(parent); defaultDescReport('{You/he} {sees} nothing ' + parent.objInPrep + ' {the parent/him}. '); } ; /* * Contents lister for descriptions of things whose contents are * explicitly inspected ("look in"). This differs from a regular * contents lister in that we explicitly say that the object is empty if * it's empty. */ thingLookInLister: LookWhereContentsLister, BaseThingContentsLister showListEmpty(pov, parent) { /* * Indicate that the list is empty, but make this a default * descriptive report. This way, if we report any special * descriptions for items contained within the object, we'll * suppress this default description that there's nothing to * describe, which is clearly wrong if there are * specially-described contents. */ gMessageParams(parent); defaultDescReport('{You/he} {sees} nothing unusual in {the parent/him}. '); } ; /* * Default contents lister for newly-revealed objects after opening a * container. */ openableOpeningLister: BaseThingContentsLister showListEmpty(pov, parent) { } showListPrefixWide(itemCount, pov, parent) { /* * This list is, by default, generated as a replacement for the * default "Opened" message in response to an OPEN command. We * therefore need different messages for PC and NPC actions, * since this serves as the description of the entire action. * * Note that if you override the Open action response for a given * object, you might want to customize this lister as well, since * the message we generate (especially for an NPC action) * presumes that we'll be the only response the command. */ gMessageParams(pov, parent); if (pov.isPlayerChar()) "Opening {the parent/him} reveal{s|ed} "; else "{The pov/he} open{s/ed} {the parent/him}, revealing "; } ; /* * Base contents lister. This class handles contents listings for most * kinds of specialized containers - Surfaces, RearConainers, * RearSurfaces, and Undersides. The main variation in the contents * listings for these various types of containers is simply the * preposition that's used to describe the relationship between the * container and the contents, and for this we can look to the objInPrep * property of the container. */ class BaseContentsLister: Lister showListPrefixWide(itemCount, pov, parent) { "\^<> <> <> "; } showListSuffixWide(itemCount, pov, parent) { ". "; } showListPrefixTall(itemCount, pov, parent) { "\^<> <> <>:"; } showListContentsPrefixTall(itemCount, pov, parent) { "<>, <> which <>:"; } ; /* * Base class for contents listers for a surface */ class BaseSurfaceContentsLister: BaseContentsLister ; /* * Contents lister for a surface */ surfaceContentsLister: ContentsLister, BaseSurfaceContentsLister ; /* * Contents lister for explicitly looking in a surface */ surfaceLookInLister: LookWhereContentsLister, BaseSurfaceContentsLister ; /* * Contents lister for a surface, used in a long description. */ surfaceDescContentsLister: DescContentsLister, BaseSurfaceContentsLister ; /* * Contents lister for room parts */ roomPartContentsLister: surfaceContentsLister isListed(obj) { /* list the object if it's listed in the room part */ return obj.isListedInRoomPart(part_); } /* the part I'm listing */ part_ = nil ; /* * contents lister for room parts, used in a long description */ roomPartDescContentsLister: surfaceDescContentsLister isListed(obj) { /* list the object if it's listed in the room part */ return obj.isListedInRoomPart(part_); } part_ = nil ; /* * Look-in lister for room parts. Most room parts act like surfaces * rather than containers, so base this lister on the surface lister. */ roomPartLookInLister: surfaceLookInLister isListed(obj) { /* list the object if it's listed in the room part */ return obj.isListedInRoomPart(part_); } part_ = nil ; /* * Base class for contents listers for an Underside. */ class BaseUndersideContentsLister: BaseContentsLister ; /* basic contents lister for an Underside */ undersideContentsLister: ContentsLister, BaseUndersideContentsLister ; /* contents lister for explicitly looking under an Underside */ undersideLookUnderLister: LookWhereContentsLister, BaseUndersideContentsLister ; /* contents lister for moving an Underside and abandoning its contents */ undersideAbandonContentsLister: undersideLookUnderLister showListEmpty(pov, parent) { } showListPrefixWide(itemCount, pov, parent) { "Moving <> reveal<> "; } showListSuffixWide(itemCount, pov, parent) { " underneath. "; } showListPrefixTall(itemCount, pov, parent) { "Moving <> reveal<>:"; } ; /* contents lister for an Underside, used in a long description */ undersideDescContentsLister: DescContentsLister, BaseUndersideContentsLister showListPrefixWide(itemCount, pov, parent) { "Under <> <> "; } ; /* * Base class for contents listers for an RearContainer or RearSurface */ class BaseRearContentsLister: BaseContentsLister ; /* basic contents lister for a RearContainer or RearSurface */ rearContentsLister: ContentsLister, BaseRearContentsLister ; /* contents lister for explicitly looking behind a RearContainer/Surface */ rearLookBehindLister: LookWhereContentsLister, BaseRearContentsLister ; /* lister for moving a RearContainer/Surface and abandoning its contents */ rearAbandonContentsLister: undersideLookUnderLister showListEmpty(pov, parent) { } showListPrefixWide(itemCount, pov, parent) { "Moving <> reveal<> "; } showListSuffixWide(itemCount, pov, parent) { " behind <>. "; } showListPrefixTall(itemCount, pov, parent) { "Moving <> reveal<>:"; } ; /* long-description contents lister for a RearContainer/Surface */ rearDescContentsLister: DescContentsLister, BaseRearContentsLister showListPrefixWide(itemCount, pov, parent) { "Behind <> <> "; } ; /* * Base class for specialized in-line contents listers. This shows the * list in the form "( which is...)", with the preposition obtained * from the container's objInPrep property. */ class BaseInlineContentsLister: ContentsLister showListEmpty(pov, parent) { } showListPrefixWide(cnt, pov, parent) { " (<> which << cnt == 1 ? tSel('is', 'was') : tSel('are', 'were')>> "; } showListSuffixWide(itemCount, pov, parent) { ")"; } ; /* * Contents lister for a generic in-line list entry. We customize the * wording slightly here: rather than saying "(in which...)" as the base * class would, we use the slightly more readable "(which contains...)". */ inlineListingContentsLister: BaseInlineContentsLister showListPrefixWide(cnt, pov, parent) { " (which contain<> "; } ; /* in-line contents lister for a surface */ surfaceInlineContentsLister: BaseInlineContentsLister ; /* in-line contents lister for an Underside */ undersideInlineContentsLister: BaseInlineContentsLister ; /* in-line contents lister for a RearContainer/Surface */ rearInlineContentsLister: BaseInlineContentsLister ; /* * Contents lister for keyring list entry. This is used to display a * keyring's contents in-line with the name of the keyring, * parenthetically. */ keyringInlineContentsLister: inlineListingContentsLister showListPrefixWide(cnt, pov, parent) { " (with "; } showListSuffixWide(cnt, pov, parent) { " attached)"; } ; /* * Contents lister for "examine " */ keyringExamineContentsLister: DescContentsLister showListEmpty(pov, parent) { "\^<> empty. "; } showListPrefixWide(cnt, pov, parent) { "Attached to <> <> "; } showListSuffixWide(itemCount, pov, parent) { ". "; } ; /* * Lister for actors aboard a traveler. This is used to list the actors * on board a vehicle when the vehicle arrives or departs a location. */ aboardVehicleLister: Lister showListPrefixWide(itemCount, pov, parent) { " (carrying "; } showListSuffixWide(itemCount, pov, parent) { ")"; } /* list anything whose isListedAboardVehicle returns true */ isListed(obj) { return obj.isListedAboardVehicle; } ; /* * A simple lister to show the objects to which a given Attachable * object is attached. This shows the objects which have symmetrical * attachment relationships to the given parent object, or which are * "major" items to which the parent is attached. */ class SimpleAttachmentLister: Lister construct(parent) { parent_ = parent; } showListEmpty(pov, parent) { /* say nothing when there are no attachments */ } showListPrefixWide(cnt, pov, parent) { "<.p>\^<> attached to "; } showListSuffixWide(cnt, pov, parent) { ". "; } /* ask the parent if we should list each item */ isListed(obj) { return parent_.isListedAsAttachedTo(obj); } /* * the parent object - this is the object whose attachments are being * listed */ parent_ = nil ; /* * The "major" attachment lister. This lists the objects which are * attached to a given parent Attachable, and for which the parent is * the "major" item in the relationship. The items in the list are * described as being attached to the parent. */ class MajorAttachmentLister: SimpleAttachmentLister showListPrefixWide(cnt, pov, parent) { "<.p>\^"; } showListSuffixWide(cnt, pov, parent) { " <> attached to <>. "; } /* ask the parent if we should list each item */ isListed(obj) { return parent_.isListedAsMajorFor(obj); } ; /* * Finish Options lister. This lister is used to offer the player * options in finishGame(). */ finishOptionsLister: Lister showListPrefixWide(cnt, pov, parent) { "<.p>Would you like to "; } showListSuffixWide(cnt, pov, parent) { /* end the question, add a blank line, and show the ">" prompt */ "?\b>"; } isListed(obj) { return obj.isListed; } listCardinality(obj) { return 1; } showListItem(obj, options, pov, infoTab) { /* obj is a FinishOption object; show its description */ obj.desc; } showListSeparator(options, curItemNum, totalItems) { /* * for the last separator, show "or" rather than "and"; for * others, inherit the default handling */ if (curItemNum + 1 == totalItems) { if (totalItems == 2) " or "; else ", or "; } else inherited(options, curItemNum, totalItems); } ; /* * Equivalent list state lister. This shows a list of state names for a * set of otherwise indistinguishable items. We show the state names in * parentheses, separated by commas only (i.e., no "and" separating the * last two items); we use this less verbose format so that we blend * into the larger enclosing list more naturally. * * The items to be listed are EquivalentStateInfo objects. */ equivalentStateLister: Lister showListPrefixWide(cnt, pov, parent) { " ("; } showListSuffixWide(cnt, pov, parent) { ")"; } isListed(obj) { return true; } listCardinality(obj) { return 1; } showListItem(obj, options, pov, infoTab) { "<> <>"; } showListSeparator(options, curItemNum, totalItems) { if (curItemNum < totalItems) ", "; } ; /* in case the exits module isn't included in the build */ property destName_, destIsBack_, others_, enableHyperlinks; /* * Basic room exit lister. This shows a list of the apparent exits from * a location. * * The items to be listed are DestInfo objects. */ class ExitLister: Lister showListPrefixWide(cnt, pov, parent) { if (cnt == 1) "The only obvious exit {leads|led} "; else "Obvious exits {lead|led} "; } showListSuffixWide(cnt, pov, parent) { ". "; } isListed(obj) { return true; } listCardinality(obj) { return 1; } showListItem(obj, options, pov, infoTab) { /* * Show the back-to-direction prefix, if we don't know the * destination name but this is the back-to direction: "back to * the north" and so on. */ if (obj.destIsBack_ && obj.destName_ == nil) say(obj.dir_.backToPrefix + ' '); /* show the direction */ showListItemDirName(obj, nil); /* if the destination is known, show it as well */ if (obj.destName_ != nil) { /* * if we have a list of other directions going to the same * place, show it parenthetically */ otherExitLister.showListAll(obj.others_, 0, 0); /* * Show our destination name. If we know the "back to" * destination name, show destination names in the format * "east, to the living room" so that they are consistent * with "west, back to the dining room". Otherwise, just * show "east to the living room". */ if ((options & hasBackNameFlag) != 0) ","; /* if this is the way back, say so */ if (obj.destIsBack_) " back"; /* show the destination */ " to <>"; } } showListSeparator(options, curItemNum, totalItems) { /* * if we have a "back to" name, use the "long" notation - this is * important because we'll use commas in the directions with * known destination names */ if ((options & hasBackNameFlag) != 0) options |= ListLong; /* * for a two-item list, if either item has a destination name, * show a comma or semicolon (depending on 'long' vs 'short' list * mode) before the "and"; for anything else, use the default * handling */ if (curItemNum == 1 && totalItems == 2 && (options & hasDestNameFlag) != 0) { if ((options & ListLong) != 0) "; and "; else ", and "; } else inherited(options, curItemNum, totalItems); } /* show a direction name, hyperlinking it if appropriate */ showListItemDirName(obj, initCap) { local dirname; /* get the name */ dirname = obj.dir_.name; /* capitalize the first letter, if desired */ if (initCap) dirname = dirname.substr(1,1).toUpper() + dirname.substr(2); /* show the name with a hyperlink or not, as configured */ if (libGlobal.exitListerObj.enableHyperlinks) say(aHref(dirname, dirname)); else say(dirname); } /* this lister shows destination names */ listerShowsDest = true /* * My special options flag: at least one object in the list has a * destination name. The caller should set this flag in our options * if applicable. */ hasDestNameFlag = ListerCustomFlag(1) hasBackNameFlag = ListerCustomFlag(2) nextCustomFlag = ListerCustomFlag(3) ; /* * Show a list of other exits to a given destination. We'll show the * list parenthetically, if there's a list to show. The objects to be * listed are Direction objects. */ otherExitLister: Lister showListPrefixWide(cnt, pov, parent) { " (or "; } showListSuffixWide(cnt, pov, parent) { ")"; } isListed(obj) { return true; } listCardinality(obj) { return 1; } showListItem(obj, options, pov, infoTab) { if (libGlobal.exitListerObj.enableHyperlinks) say(aHref(obj.name, obj.name)); else say(obj.name); } showListSeparator(options, curItemNum, totalItems) { /* * simply show "or" for all items (these lists are usually * short, so omit any commas) */ if (curItemNum != totalItems) " or "; } ; /* * Show room exits as part of a room description, using the "verbose" * sentence-style notation. */ lookAroundExitLister: ExitLister showListPrefixWide(cnt, pov, parent) { /* add a paragraph break before the exit listing */ "<.roompara>"; /* inherit default handling */ inherited(cnt, pov, parent); } ; /* * Show room exits as part of a room description, using the "terse" * notation. */ lookAroundTerseExitLister: ExitLister showListPrefixWide(cnt, pov, parent) { "<.roompara><.parser>Obvious exits: "; } showListItem(obj, options, pov, infoTab) { /* show the direction name */ showListItemDirName(obj, true); } showListSuffixWide(cnt, pov, parent) { "<./parser> "; } showListSeparator(options, curItemNum, totalItems) { /* just show a comma between items */ if (curItemNum != totalItems) ", "; } /* this lister does not show destination names */ listerShowsDest = nil ; /* * Show room exits in response to an explicit request (such as an EXITS * command). */ explicitExitLister: ExitLister showListEmpty(pov, parent) { "There {are|were} no obvious exits. "; } ; /* * Show room exits in the status line (used in HTML mode only) */ statuslineExitLister: ExitLister showListEmpty(pov, parent) { "<>Exits: None<>"; } showListPrefixWide(cnt, pov, parent) { "<>Exits: "; } showListSuffixWide(cnt, pov, parent) { "<>"; } showListItem(obj, options, pov, infoTab) { "<>"; } showListSeparator(options, curItemNum, totalItems) { /* just show a space between items */ if (curItemNum != totalItems) "   "; } /* this lister does not show destination names */ listerShowsDest = nil ; /* * Implied action announcement grouper. This takes a list of * ImplicitActionAnnouncement reports and returns a single message string * describing the entire list of actions. */ implicitAnnouncementGrouper: object /* * Configuration option: keep all failures in a list of implied * announcements. If this is true, then we'll write things like * "trying to unlock the door and then open it"; if nil, we'll * instead write simply "trying to unlock the door". * * By default, we keep only the first of a group of failures. A * group of failures is always recursively related, so the first * announcement refers to the command that actually failed; the rest * of the announcements are for the enclosing actions that triggered * the first action. All of the enclosing actions failed as well, * but only because the first action failed. * * Announcing all of the actions is too verbose for most tastes, * which is why we set the default here to nil. The fact that the * first action in the group failed means that we necessarily won't * carry out any of the enclosing actions, so the enclosing * announcements don't tell us much. All they really tell us is why * we're running the action that actually failed, but that's almost * always obvious, so suppressing them is usually fine. */ keepAllFailures = nil /* build the composite message */ compositeMessage(lst) { local txt; local ctx = new ListImpCtx(); /* add the text for each item in the list */ for (txt = '', local i = 1, local len = lst.length() ; i <= len ; ++i) { local curTxt; /* get this item */ local cur = lst[i]; /* we're not in a 'try' or 'ask' sublist yet */ ctx.isInSublist = nil; /* set the underlying context according to this item */ ctx.setBaseCtx(cur); /* * Generate the announcement for this element. Generate the * announcement from the message property for this item using * our running list context. */ curTxt = cur.getMessageText( cur.getAction().getOriginalAction(), ctx); /* * If this one is an attempt only, and it's followed by one * or more other attempts, the attempts must all be * recursively related (in other words, the first attempt was * an implied action required by the second attempt, which * was required by the third, and so on). They have to be * recursively related, because otherwise we wouldn't have * kept trying things after the first failed attempt. * * To make the series of failed attempts sound more natural, * group them into a single "trying to", and keep only the * first failure: rather than "trying to unlock the door, * then trying to open the door", write "trying to unlock the * door and then open it". * * An optional configuration setting makes us keep only the * first failed operation, so we'd instead write simply * "trying to unlock the door". * * Do the same grouping for attempts interrupted for an * interactive question. */ while ((cur.justTrying && i < len && lst[i+1].justTrying) || (cur.justAsking && i < len && lst[i+1].justAsking)) { local addTxt; /* * move on to the next item - we're processing it here, * so we don't need to handle it again in the main loop */ ++i; cur = lst[i]; /* remember that we're in a try/ask sublist */ ctx.isInSublist = true; /* add the list entry for this action, if desired */ if (keepAllFailures) { /* get the added text */ addTxt = cur.getMessageText( cur.getAction().getOriginalAction(), ctx); /* * if both the text so far and the added text are * non-empty, string them together with 'and then'; * if one or the other is empty, use the non-nil one */ if (addTxt != '' && curTxt != '') curTxt += ' and then ' + addTxt; else if (addTxt != '') curTxt = addTxt; } } /* add a separator before this item if it isn't the first */ if (txt != '' && curTxt != '') txt += ', then '; /* add the current item's text */ txt += curTxt; } /* if we ended up with no text, the announcement is silent */ if (txt == '') return ''; /* wrap the whole list in the usual full standard phrasing */ return standardImpCtx.buildImplicitAnnouncement(txt); } ; /* * Suggested topic lister. */ class SuggestedTopicLister: Lister construct(asker, askee, explicit) { /* remember the actors */ askingActor = asker; targetActor = askee; /* remember whether this is explicit or implicit */ isExplicit = explicit; /* cache the actor's scope list */ scopeList = asker.scopeList(); } showListPrefixWide(cnt, pov, parent) { /* add the asking and target actors as global message parameters */ gMessageParams(askingActor, targetActor); /* show the prefix; include a paren if not in explicit mode */ "<>{You askingActor/he} could "; } showListSuffixWide(cnt, pov, parent) { /* end the sentence; include a paren if not in explicit mode */ ".<> "; } showListEmpty(pov, parent) { /* * say that the list is empty if it was explicitly requested; * say nothing if the list is being added by the library */ if (isExplicit) { gMessageParams(askingActor, targetActor); "<>{You askingActor/he} {have} nothing specific in mind {right now|just then} to discuss with {the targetActor/him}.<> "; } } showListSeparator(options, curItemNum, totalItems) { /* use "or" as the conjunction */ if (curItemNum + 1 == totalItems) ", or "; else inherited(options, curItemNum, totalItems); } /* list suggestions that are currently active */ isListed(obj) { return obj.isSuggestionActive(askingActor, scopeList); } /* each item counts as one item grammatically */ listCardinality(obj) { return 1; } /* suggestions have no contents */ contentsListed(obj) { return nil; } /* get the list group */ listWith(obj) { return obj.suggestionGroup; } /* mark as seen - nothing to do for suggestions */ markAsSeen(obj, pov) { } /* show the item - show the suggestion's theName */ showListItem(obj, options, pov, infoTab) { /* note that we're showing the suggestion */ obj.noteSuggestion(); /* show the name */ say(obj.fullName); } /* don't use semicolons, even in long lists */ longListSepTwo { listSepTwo; } longListSepMiddle { listSepMiddle; } longListSepEnd { listSepEnd; } /* flag: this is an explicit listing (i.e., a TOPICS command) */ isExplicit = nil /* the actor who's asking for the topic list (usually the PC) */ askingActor = nil /* the actor we're talking to */ targetActor = nil /* our cached scope list for the actor */ scopeList = nil ; /* ASK/TELL suggestion list group base class */ class SuggestionListGroup: ListGroupPrefixSuffix showGroupItem(sublister, obj, options, pov, infoTab) { /* * show the short name of the item - the group prefix will have * shown the appropriate long name */ say(obj.name); } ; /* ASK ABOUT suggestion list group */ suggestionAskGroup: SuggestionListGroup groupPrefix = "ask {it targetActor/him} about " ; /* TELL ABOUT suggestion list group */ suggestionTellGroup: SuggestionListGroup groupPrefix = "tell {it targetActor/him} about " ; /* ASK FOR suggestion list group */ suggestionAskForGroup: SuggestionListGroup groupPrefix = "ask {it targetActor/him} for " ; /* GIVE TO suggestions list group */ suggestionGiveGroup: SuggestionListGroup groupPrefix = "give {it targetActor/him} " ; /* SHOW TO suggestions */ suggestionShowGroup: SuggestionListGroup groupPrefix = "show {it targetActor/him} " ; /* YES/NO suggestion group */ suggestionYesNoGroup: SuggestionListGroup showGroupList(pov, lister, lst, options, indent, infoTab) { /* * if we have one each of YES and NO responses, make the entire * list "say yes or no"; otherwise, use the default behavior */ if (lst.length() == 2 && lst.indexWhich({x: x.ofKind(SuggestedYesTopic)}) != nil && lst.indexWhich({x: x.ofKind(SuggestedNoTopic)}) != nil) { /* we have a [yes, no] group - use the simple message */ "say yes or no"; } else { /* inherit the default behavior */ inherited(pov, lister, lst, options, indent, infoTab); } } groupPrefix = "say"; ; frobtads-1.2.3/tads3/lib/adv3/en_us/en_us.h0000644000175000001440000001566510772162202017534 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - US English header * * This file defines common properties, macros, and other identifiers used * throughout the US English modules of the TADS 3 library. */ /* ------------------------------------------------------------------------ */ /* * Vocabulary properties for parts of speech we use in the English parser. * * These properties are defined in the English-specific header because * other languages might classify their parts of speech differently; for * example, a French parser might distinguish nouns according to gender, * and thus require masculineNoun and feminineNoun properties instead of a * single noun property. * * adjApostS is a special adjective form for adjectives that are * explicitly defined in vocabulary with a "'s" suffix. In most cases, * it's not necessary to explicitly define possessive words explicitly in * an object's vocabulary, because the parser's possessive qualifier * phrase grammar allows any valid noun phrase to be used in a 's * possessive form to indicate ownership. However, this only works when * ownership is actually modeled in the game; that is, this only works * when one object has an ownership relation with another object, using * the owner/isOwnedBy mechanism in Thing. Sometimes, a possessive word * is just a literal part of an object's name, and the nominal owner * doesn't actually exist as an object in the game: "Mom's Old-Fashioned * Robot Oil," for example. In these cases, the object can include the * possessive word *without* the 's suffix ("Mom") as a vocabulary word * for the object, using the adjApostS property. * * A literalAdjective is like an adjective, but behaves a little * differently in the grammar. A literal adjective can be optionally * enclosed in quotes (for example, we could refer to an elevator button * labeled "G" as "'G' button" or simply "G button"). In addition, a * literal adjective can be placed after the noun it modifies (hence * "button 'G'" or "button G"). */ dictionary property noun, adjective, plural, adjApostS, literalAdjective; /* * Numeric parts of speech: * * digitWord - a name for a digit (zero through nine) * * teenWord - a name for a 'teen' (ten through nineteen) - we distinguish * these because there's no general grammatical rule for constructing * names for these numbers out of smaller lexical units. We include ten * in this set, rather than in the teenWord set, because ten cannot be * combined with digit words to form other numbers. * * tensWord - a name for a multiple of ten (twenty, thirty, ..., ninety). * The words in this set can be used to construct the numbers from 20 to * 99 by combining them with digit words using a hyphen (as in * "twenty-two"). * * ordinalWord - an ordinal, such as '1st' or 'first'. */ dictionary property digitWord, teenWord, tensWord; dictionary property ordinalWord; /* ------------------------------------------------------------------------ */ /* * Templates for the basic vocabulary object */ VocabObject template 'vocabWords'; /* * Define some templates that apply to ordinary objects (descendants of * Thing). */ Thing template 'vocabWords' 'name' @location? "desc"?; /* * For rooms, we normally have no vocabulary words, but we do have a name * and description, and optionally a "destination name" to use to describe * connectors from adjoining rooms. */ Room template 'roomName' 'destName'? 'name'? "desc"?; /* * For actors, we generally override the npcDesc or pcDesc rather than the * base desc. For the standard templates, set the npcDesc, since most * actors are NPC's rather than the player character. */ Actor template 'vocabWords' 'name' @location? "npcDesc"; /* * For passages, allow special syntax to point to the master side of the * passage. */ Passage template ->masterObject inherited; /* * For one-way room connectors, provide special syntax to point to the * destination room. */ OneWayRoomConnector template ->destination; /* * For enterables, allow special syntax to point to the connector which an * actor uses to traverse into the enterable. */ Enterable template ->connector inherited; Exitable template ->connector inherited; /* * For TravelMessage connectors, provide special syntax to specify the * message and point to the destination. */ TravelMessage template ->destination "travelDesc" | [eventList]; /* * For connectors that don't go anywhere but do show a message on a travel * attempt, we just need to specify the travel message. */ NoTravelMessage template "travelDesc" | [eventList]; DeadEndConnector template 'apparentDestName'? "travelDesc" | [eventList]; /* * Unthings are defined pretty much like Things, except they have no use * for a desc, and frequently want a custom notHereMsg. */ Unthing template 'vocabWords' 'name' @location? 'notHereMsg'?; /* ThingState objects */ ThingState template 'listName_' +listingOrder?; /* ------------------------------------------------------------------------ */ /* * Convenience macros for defining command grammar. * * A command's grammar is defined via a 'grammar' rule definition for the * 'predicate' production; the VerbRule macro can be used for better * readability. * * Within a command grammar rule, there are several commonly-used object * roles assigned to single-noun or noun-list phrases. We provide the * singleDobj, dobjList, singleIobj, and iobjList macros to make these * assignments more readable. In addition, number, topic, literal, and * direction phrases can be assigned with singleNumber, singleTopic, * singleLiteral, and singleDir, respectively. */ #define VerbRule(tag) grammar predicate(tag): #define singleDobj singleNoun->dobjMatch #define singleIobj singleNoun->iobjMatch #define dobjList nounList->dobjMatch #define iobjList nounList->iobjMatch #define singleNumber numberPhrase->numMatch #define singleTopic topicPhrase->topicMatch #define singleLiteral literalPhrase->literalMatch #define singleDir directionName->dirMatch /* ------------------------------------------------------------------------ */ /* * Convenience macros for controlling the narrative tense. */ /* * Set the current narrative tense. Use val = true for past and * val = nil for present. */ #define setPastTense(val) (gameMain.usePastTense = (val)) /* * Shorthand macro for selecting one of two values depending on the * current narrative tense. */ #define tSel(presVal, pastVal) \ (gameMain.usePastTense ? (pastVal) : (presVal)) /* * Temporarily override the current narrative tense and invoke a callback * function. */ #define withPresent(callback) (withTense(nil, (callback))) #define withPast(callback) (withTense(true, (callback))) frobtads-1.2.3/tads3/lib/adv3/en_us/instruct.t0000644000175000001440000014042011477673622020316 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: Instructions for new players * * This module defines the INSTRUCTIONS command, which provides the * player with an overview of how to play IF games in general. These * instructions are especially designed as an introduction to IF for * inexperienced players. The instructions given here are meant to be * general enough to apply to most games that follow the common IF * conventions. * * This module defines the English version of the instructions. * * In most cases, each author should customize these general-purpose * instructions at least a little for the specific game. We provide a * few hooks for some specific parameter-driven customizations that don't * require modifying the original text in this file. Authors should also * feel free to make more extensive customizations as needed to address * areas where the game diverges from the common conventions described * here. * * One of the most important things you should do to customize these * instructions for your game is to add a list of any special verbs or * command phrasings that your game uses. Of course, you might think * you'll be spoiling part of the challenge for the player if you do * this; you might worry that you'll give away a puzzle if you don't keep * a certain verb secret. Be warned, though, that many players - maybe * even most - don't think "guess the verb" puzzles are good challenges; * a lot of players feel that puzzles that hinge on finding the right * verb or phrasing are simply bad design that make a game less * enjoyable. You should think carefully about exactly why you don't * want to disclose a particular verb in the instructions. If you want * to withhold a verb because the entire puzzle is to figure out what * command to use, then you have created a classic guess-the-verb puzzle, * and most everyone in the IF community will feel this is simply a bad * puzzle that you should omit from your game. If you want to withhold a * verb because it's too suggestive of a particular solution, then you * should at least make sure that a more common verb - one that you are * willing to disclose in the instructions, and one that will make as * much sense to players as your secret verb - can achieve the same * result. You don't have to disclose every *accepted* verb or phrasing * - as long as you disclose every *required* verb *and* phrasing, you * will have a defense against accusations of using guess-the-verb * puzzles. * * You might also want to mention the "cruelty" level of the game, so * that players will know how frequently they should save the game. It's * helpful to point out whether or not it's possible for the player * character to be killed; whether it's possible to get into situations * where the game becomes "unwinnable"; and, if the game can become * unwinnable, whether or not this will become immediately clear. The * kindest games never kill the PC and are always winnable, no matter * what actions the player takes; it's never necessary to save these * games except to suspend a session for later resumption. The cruelest * games kill the PC without warning (although if they offer an UNDO * command from a "death" prompt, then even this doesn't constitute true * cruelty), and can become unwinnable in ways that aren't readily and * immediately apparent to the player, which means that the player could * proceed for quite some time (and thus invest substantial effort) after * the game is already effectively lost. Note that unwinnable situations * can often be very subtle, and might not even be intended by the * author; for example, if the player needs a candle to perform an * exorcism at some point, but the candle can also be used for * illumination in dark areas, the player could make the game unwinnable * simply by using up the candle early on while exploring some dark * tunnels, and might not discover the problem until much further into * the game. */ #include "adv3.h" #include "en_us.h" /* * The INSTRUCTIONS command. Make this a "system" action, because it's * a meta-action outside of the story. System actions don't consume any * game time. */ DefineSystemAction(Instructions) /* * This property tells us how complete the verb list is. By default, * we'll assume that the instructions fail to disclose every required * verb in the game, because the generic set we use here doesn't even * try to anticipate the special verbs that most games include. If * you provide your own list of game-specific verbs, and your custom * list (taken together with the generic list) discloses every verb * required to complete the game, you should set this property to * true; if you set this to true, the instructions will assure the * player that they will not need to think of any verbs besides the * ones listed in the instructions. Authors are strongly encouraged * to disclose a list of verbs that is sufficient by itself to * complete the game, and to set this property to true once they've * done so. */ allRequiredVerbsDisclosed = nil /* * A list of custom verbs. Each game should set this to a list of * single-quoted strings; each string gives an example of a verb to * display in the list of sample verbs. Something like this: * * customVerbs = ['brush my teeth', 'pick the lock'] */ customVerbs = [] /* * Verbs relating specifically to character interaction. This is in * the same format as customVerbs, and has essentially the same * purpose; however, we call these out separately to allow each game * not only to supplement the default list we provide but to replace * our default list. This is desirable for conversation-related * commands in particular because some games will not use the * ASK/TELL conversation system at all and will thus want to remove * any mention of the standard set of verbs. */ conversationVerbs = [ 'ASK WIZARD ABOUT WAND', 'ASK WIZARD FOR POTION', 'TELL WIZARD ABOUT DUSTY TOME', 'SHOW SCROLL TO WIZARD', 'GIVE WAND TO WIZARD', 'YES (or NO)' ] /* conversation verb abbreviations */ conversationAbbr = "\n\tASK ABOUT (topic) can be abbreviated to A (topic) \n\tTELL ABOUT (topic) can be entered as T (topic)" /* * Truncation length. If the game's parser allows words to be * abbreviated to some minimum number of letters, this should * indicate the minimum length. The English parser uses a truncation * length of 6 letters by default. * * Set this to nil if the game doesn't allow truncation at all. */ truncationLength = 6 /* * This property should be set on a game-by-game basis to indicate * the "cruelty level" of the game, which is a rough estimation of * how likely it is that the player will encounter an unwinnable * position in the game. * * Level 0 is "kind," which means that the player character can * never be killed, and it's impossible to make the game unwinnable. * When this setting is used, the instructions will reassure the * player that saving is necessary only to suspend the session. * * Level 1 is "standard," which means that the player character can * be killed, and/or that unwinnable positions are possible, but * that there are no especially bad unwinnable situations. When * this setting is selected, we'll warn the player that they should * save every so often. * * (An "especially bad" situation is one in which the game becomes * unwinnable at some point, but this won't become apparent to the * player until much later. For example, suppose the first scene * takes place in a location that can never be reached again after * the first scene, and suppose that there's some object you can * obtain in this scene. This object will be required in the very * last scene to win the game; if you don't have the object, you * can't win. This is an "especially bad" unwinnable situation: if * you leave the first scene without getting the necessary object, * the game is unwinnable from that point forward. In order to win, * you have to go back and play almost the whole game over again. * Saved positions are almost useless in a case like this, since * most of the saved positions will be after the fatal mistake; no * matter how often you saved, you'll still have to go back and do * everything over again from near the beginning.) * * Level 2 is "cruel," which means that the game can become * unwinnable in especially bad ways, as described above. If this * level is selected, we'll warn the player more sternly to save * frequently. * * We set this to 1 ("standard") by default, because even games that * aren't intentionally designed to be cruel often have subtle * situations where the game becomes unwinnable, because of things * like the irreversible loss of an object, or an unrepeatable event * sequence; it almost always takes extra design work to ensure that * a game is always winnable. */ crueltyLevel = 1 /* * Does this game have any real-time features? If so, set this to * true. By default, we'll explain that game time passes only in * response to command input. */ isRealTime = nil /* * Conversation system description. Several different conversation * systems have come into relatively widespread use, so there isn't * any single convention that's generic enough that we can assume it * holds for all games. In deference to this variability, we * provide this hook to make it easy to replace the instructions * pertaining to the conversation system. If the game uses the * standard ASK/TELL system, it can leave this list unchanged; if * the game uses a different system, it can replace this with its * own instructions. * * We'll include information on the TALK TO command if there are any * in-conversation state objects in the game; if not, we'll assume * there's no need for this command. * * We'll mention the TOPICS command if there are any SuggestedTopic * instances in the game; if not, then the game will never have * anything to suggest, so the TOPICS command isn't needed. * * We'll include information on special topics if there are any * SpecialTopic objects defined. */ conversationInstructions = "You can talk to other characters by asking or telling them about things in the story. For example, you might ASK WIZARD ABOUT WAND or TELL GUARD ABOUT ALARM. You should always use the ASK ABOUT or TELL ABOUT phrasing; the story won’t be able to understand other formats, so you don’t have to worry about thinking up complicated questions like ask guard how to open the window. In most cases, you’ll get the best results by asking about specific objects or other characters you’ve encountered in the story, rather than about abstract topics such as MEANING OF LIFE; however, if something in the story leads you to believe you should ask about some particular abstract topic, it can’t hurt to try. \bIf you’re asking or telling the same person about several topics in succession, you can save some typing by abbreviating ASK ABOUT to A, and TELL ABOUT to T. For example, once you’re talking to the wizard, you can abbreviate ASK WIZARD ABOUT AMULET to simply A AMULET. This addresses the question to the same character as in the last ASK or TELL. <> <> <> \bYou can also interact with other characters using physical objects. For example, you might be able to give something to another character, as in GIVE MONEY TO CLERK, or show an object to someone, as in SHOW IDOL TO PROFESSOR. You might also be able to fight other characters, as in ATTACK TROLL WITH SWORD or THROW AXE AT DWARF. \bIn some cases, you can tell a character to do something for you. You do this by typing the character’s name, then a comma, then the command you want the character to perform, using the same wording you’d use for a command to your own character. For example: \b\t>ROBOT, GO NORTH \bKeep in mind, though, that there’s no guarantee that other characters will always obey your orders. Most characters have minds of their own and won’t automatically do whatever you ask. " /* execute the command */ execSystemAction() { local origElapsedTime; /* * note the elapsed game time on the real-time clock before we * start, so that we can reset the game time when we're done; we * don't want the instructions display to consume any real game * time */ origElapsedTime = realTimeManager.getElapsedTime(); /* show the instructions */ showInstructions(); /* reset the real-time game clock */ realTimeManager.setElapsedTime(origElapsedTime); } #ifdef INSTRUCTIONS_MENU /* * Show the instructions, using a menu-based table of contents. */ showInstructions() { /* run the instructions menu */ topInstructionsMenu.display(); /* show an acknowledgment */ "Done. "; } #else /* INSTRUCTIONS_MENU */ /* * Show the instructions as a standard text display. Give the user * the option of turning on a SCRIPT file to capture the text. */ showInstructions() { local startedScript; /* presume we won't start a new script file */ startedScript = nil; /* show the introductory message */ "The story is about to show a full set of instructions, designed especially for people who aren’t already familiar with interactive fiction. The instructions are lengthy"; /* * Check to see if we're already scripting. If we aren't, offer * to save the instructions to a file. */ if (scriptStatus.scriptFile == nil) { local str; /* * they're not already logging; ask if they'd like to start * doing so */ ", so you might want to capture them to a file (so that you can print them out, for example). Would you like to proceed? \n(<> is affirmative, or type <> to capture to a file) > "; /* ask for input */ str = inputManager.getInputLine(nil, nil); /* if they want to capture them to a file, set up scripting */ if (rexMatch('*s(c(r(i(pt?)?)?)?)?*', str) == str.length()) { /* try setting up a scripting file */ ScriptAction.setUpScripting(nil); /* if that failed, don't proceed */ if (scriptStatus.scriptFile == nil) return; /* note that we've started a script file */ startedScript = true; } else if (rexMatch('*y.*', str) != str.length()) { "Canceled. "; return; } } else { /* * they're already logging; just confirm that they want to * see the instructions */ "; would you like to proceed? \n(Y is affirmative) > "; /* stop if they don't want to proceed */ if (!yesOrNo()) { "Canceled. "; return; } } /* make sure we have something for the next "\b" to skip from */ "\ "; /* show each chapter in turn */ showCommandsChapter(); showAbbrevChapter(); showTravelChapter(); showObjectsChapter(); showConversationChapter(); showTimeChapter(); showSaveRestoreChapter(); showSpecialCmdChapter(); showUnknownWordsChapter(); showAmbiguousCmdChapter(); showAdvancedCmdChapter(); showTipsChapter(); /* if we started a script file, close it */ if (startedScript) ScriptOffAction.turnOffScripting(nil); } #endif /* INSTRUCTIONS_MENU */ /* Entering Commands chapter */ showCommandsChapter() { "\bEntering Commands\b You’ve probably already noticed that you interact with the story by typing a command whenever you see the prompt, which usually looks like this: \b"; gLibMessages.mainCommandPrompt(rmcCommand); "\bKnowing this much, you’re probably thinking one of two things: Great, I can type absolutely anything I want, in plain English, and the story will do my bidding, or Great, now I have to figure out yet another heinously complex command language for a computer program; I think I’ll go play Minesweeper. Well, neither extreme is quite true. \bIn actual play, you’ll only need a fairly small set of commands, and the commands are mostly in ordinary English, so there’s not very much you’ll have to learn or remember. Even though that command prompt can look intimidating, don’t let it scare you off — there are just a few simple things you have to know. \bFirst, you’ll almost never have to refer to anything that isn’t directly mentioned in the story; this is a story, after all, not a guessing game where you have to think of everything that goes together with some random object. For example, if you’re wearing a jacket, you might assume that the jacket has pockets, or buttons, or a zipper — but if the story never mentions those things, you shouldn’t have to worry about them. \bSecond, you won’t have to think of every conceivable action you could perform. The point of the game isn’t to make you guess at verbs. Instead, you’ll only have to use a relatively small number of simple, ordinary actions. To give you an idea of what we mean, here are some of the commands you can use:"; "\b \n\t LOOK AROUND \n\t INVENTORY \n\t GO NORTH (or EAST, SOUTHWEST, and so on, or UP, DOWN, IN, OUT) \n\t WAIT \n\t TAKE THE BOX \n\t DROP THE DISK \n\t LOOK AT THE DISK \n\t READ THE BOOK \n\t OPEN BOX \n\t CLOSE BOX \n\t LOOK IN THE BOX \n\t LOOK THROUGH WINDOW \n\t PUT FLOPPY DISK INTO BOX \n\t PUT BOX ON TABLE \n\t WEAR THE CONICAL HAT \n\t TAKE OFF HAT \n\t TURN ON LANTERN \n\t LIGHT MATCH \n\t LIGHT CANDLE WITH MATCH \n\t PUSH BUTTON \n\t PULL LEVER \n\t TURN KNOB \n\t TURN DIAL TO 11 \n\t EAT COOKIE \n\t DRINK MILK \n\t THROW PIE AT CLOWN \n\t ATTACK TROLL WITH SWORD \n\t UNLOCK DOOR WITH KEY \n\t LOCK DOOR WITH KEY \n\t CLIMB THE LADDER \n\t GET IN THE CAR \n\t SIT ON THE CHAIR \n\t STAND ON THE TABLE \n\t STAND IN FLOWER BED \n\t LIE ON THE BED \n\t TYPE HELLO ON COMPUTER \n\t LOOK UP BOB IN PHONE BOOK"; /* show the conversation-related verbs */ foreach (local cur in conversationVerbs) "\n\t <>"; /* show the custom verbs */ foreach (local cur in customVerbs) "\n\t <>"; /* * if the list is exhaustive, say so; otherwise, mention that * there might be some other verbs to find */ if (allRequiredVerbsDisclosed) "\bThat’s it — every verb and every command phrasing you need to complete the story is shown above. If you ever get stuck and feel like the story is expecting you to think of something completely out of the blue, remember this: whatever it is you have to do, you can do it with one or more of the commands shown above. Don’t ever worry that you have to start trying lots of random commands to hit upon the magic phrasing, because everything you need is shown plainly above. "; else "\bMost of the verbs you’ll need to complete the story are shown above; there might be a few additional commands you’ll need from time to time as well, but they’ll follow the same simple format as the commands above."; "\bA few of these commands deserve some more explanation. LOOK AROUND (which you abbreviate to LOOK, or even just L) shows the description of the current location. You can use this if you want to refresh your memory of your character’s surroundings. INVENTORY (or just I) shows a list of everything your character is carrying. WAIT (or Z) just lets a little time pass in the story."; } /* Abbreviations chapter */ showAbbrevChapter() { "\bAbbreviations \bYou’ll probably use a few commands quite a lot, so to save typing, you can abbreviate some of the most frequently-used commands: \b \n\t LOOK AROUND can be shortened to LOOK, or merely L \n\t INVENTORY can be simply I \n\t GO NORTH can be written NORTH, or even just N (likewise E, W, S, NE, SE, NW, SW, U for UP and D for DOWN) \n\t LOOK AT THE DISK can be entered as EXAMINE DISK or simply X DISK \n\t WAIT can be shortened to Z <> \bA Few More Command Details \bWhen you’re entering commands, you can use capital or small letters in any mixture. You can use words such as THE and A when they’re appropriate, but you can omit them if you prefer. "; if (truncationLength != nil) { "You can abbreviate any word to its first << spellInt(truncationLength)>> letters, but if you choose not to abbreviate, the story will pay attention to everything you actually type; this means, for example, that you can abbreviate SUPERCALIFRAGILISTICEXPIALIDOCIOUS to << 'SUPERCALIFRAGILISTICEXPIALIDOCIOUS'.substr(1, truncationLength) >> or << 'SUPERCALIFRAGILISTICEXPIALIDOCIOUS'.substr(1, truncationLength+2) >>, but not to << 'SUPERCALIFRAGILISTICEXPIALIDOCIOUS'.substr(1, truncationLength) >>SDF. "; } } /* Travel chapter */ showTravelChapter() { "\bTravel \bAt any given time in the story, your character is in a location. The story describes the location when your character first enters, and again any time you type LOOK. Each location usually has a short name that’s displayed just before its full description; the name is useful when drawing a map, and the short name can help jog your memory as you’re finding your way around. \bEach location is a separate room, or a large outdoor area, or the like. (Sometimes a single physical room is so large that it comprises several locations in the game, but that’s fairly rare.) For the most part, going to a location is as specific as you have to get about travel; once your character is in a location, the character can usually see and reach everything within the location, so you don’t have to worry about where exactly your character is standing within the room. Once in a while, you might find that something is out of reach, perhaps because it’s on a high shelf or on the other side of a moat; in these cases, it’s sometimes useful to be more specific about your character’s location, such as by standing on something (STAND ON TABLE, for example). \bTraveling from one location to another is usually done using a direction command: GO NORTH, GO NORTHEAST, GO UP, and so on. (You can abbreviate the cardinal and vertical directions to one letter each — N, S, E, W, U, D — and the diagonal directions to two: NE, NW, SE, SW.) The story should always tell you the directions that you can go when it describes a location, so you should never have to try all the unmentioned directions to see if they go anywhere. \bIn most cases, backtracking (by reversing the direction you took from one location to another) will take you back to where you started, although some passages might have turns. \bMost of the time, when the story describes a door or passageway, you won’t need to open the door to go through the passage, as the story will do this for you. Only when the story specifically states that a door is blocking your way will you usually have to deal with the door explicitly."; } /* Objects chapter */ showObjectsChapter() { "\bManipulating Objects \bYou might find objects in the story that your character can carry or otherwise manipulate. If you want to carry something, type TAKE and the object’s name: TAKE BOOK. If you want to drop something your character is carrying, DROP it. \bYou usually won’t have to be very specific about exactly how your character is supposed to carry or hold something, so you shouldn’t have to worry about which hand is holding which item or anything like that. It might sometimes be useful to put one object inside or on top of another, though; for example, PUT BOOK IN SHOPPING BAG or PUT VASE ON TABLE. If your character’s hands get full, it might help to put some items being carried into a container, much as in real life you can carry more stuff if it’s in a bag or a box. \bYou can often get a lot of extra information (and sometimes vital clues) by examining objects, which you can do with the LOOK AT command (or, equivalently, EXAMINE, which you can abbreviate to simply X, as in X PAINTING). Most experienced players get in the habit of examining everything in a new location right away. "; } /* show the Conversation chapter */ showConversationChapter() { "\bInteracting with Other Characters \bYour character may encounter other people or creatures in the story. You can sometimes interact with these characters.\b"; /* show the customizable conversation instructions */ conversationInstructions; } /* Time chapter */ showTimeChapter() { "\bTime"; if (isRealTime) { "\bTime passes in the story in response to the commands you type. In addition, some parts of the story might unfold in real time, which means that things can happen even while you’re thinking about your next move. \bIf you want to stop the clock while you’re away from your computer for a moment (or you just need some time to think), you can type PAUSE."; } else { "\bTime passes in the story only in response to commands you type. This means that nothing happens while the story is waiting for you to type something. Each command takes about the same amount of time in the story. If you specifically want to let some extra time pass within the story, because you think something is about to happen, you can type WAIT (or just Z). "; } } /* Saving, Restoring, and Undo chapter */ showSaveRestoreChapter() { "\bSaving and Restoring \bYou can save a snapshot of your current position in the story at any time, so that you can later restore the story to the same position. The snapshot will be saved to a file on your computer’s disk, and you can save as many different snapshots as you’d like (to the extent you have space on your disk, anyway).\b"; switch (crueltyLevel) { case 0: "In this story, your character will never be killed, and you’ll never find yourself in a situation where it’s impossible to complete the story. Whatever happens to your character, you’ll always be able to find a way to complete the story. So, unlike in many text games, you don’t have to worry about saving positions to protect yourself against getting stuck in impossible situations. Of course, you can still save as often as you’d like, to suspend your session and return to it later, or to save positions that you think you might want to revisit."; break; case 1: case 2: "It might be possible for your character to be killed in the story, or for you to find your character in an impossible situation where you won’t be able to complete the story. So, you should be sure to save your position <> so that you won’t have to go back too far if this should happen. "; if (crueltyLevel == 2) "(You should make a point of keeping all of your old saved positions, too, because you might not always realize immediately when a situation has become impossible. You might find at times that you need to go back further than just the last position that you thought was safe.)"; break; } "\bTo save your position, type SAVE at the command prompt. The story will ask you for the name of a disk file to use to store snapshot. You’ll have to specify a filename suitable for your computer system, and the disk will need enough free space to store the file; you’ll be told if there’s any problem saving the file. You should use a filename that doesn’t already exist on your machine, because the new file will overwrite any existing file with the same name. \bYou can restore a previously saved position by typing RESTORE at any prompt. The story will ask you for the name of the file to restore. After the computer reads the file, everything in the story will be exactly as it was when you saved that file."; "\bUndo \bEven if you haven’t saved your position recently, you can usually take back your last few commands with the UNDO command. Each time you type UNDO, the story reverses the effect of one command, restoring the story as it was before you typed that command. UNDO is limited to taking back the last few commands, so this isn’t a substitute for SAVE/RESTORE, but it’s very handy if you find your character unexpectedly in a dangerous situation, or you make a mistake you want to take back."; } /* Other Special Commands chapter */ showSpecialCmdChapter() { "\bSome Other Special Commands \bThe story understands a few other special commands that you might find useful. \bAGAIN (or just G): Repeats the last command. (If your last input line had several commands, only the last single command on the line is repeated.) \bINVENTORY (or just I): Shows what your character is carrying. \bLOOK (or just L): Shows the full description of your character’s current location."; /* if the exit lister module is active, mention the EXITS command */ if (gExitLister != nil) "\bEXITS: Shows the list of obvious exits from the current location. \bEXITS ON/OFF/STATUS/LOOK: Controls the way the game displays exit lists. EXITS ON puts a list of exits in the status line and also lists exits in each room description. EXITS OFF turns off both of these listings. EXITS STATUS turns on just the status line exit list, and EXITS ROOM turns on only the room description lists."; "\bOOPS: Corrects a single misspelled word in a command, without retyping the entire command. You can only use OOPS immediately after the story tells you it didn’t recognize a word in your previous command. Type OOPS followed by the corrected word. \bQUIT (or just Q): Terminates the story. \bRESTART: Starts the story over from the beginning. \bRESTORE: Restores a position previously saved with SAVE. \bSAVE: Saves the current position in a disk file. \bSCRIPT: Starts making a transcript of your story session, saving all of the displayed text to a disk file, so that you can peruse it later or print it out. \bSCRIPT OFF: Ends a transcript that you started with SCRIPT. \bUNDO: Takes back the last command. \bSAVE DEFAULTS: Saves your current settings for things like NOTIFY, EXITS, and FOOTNOTES as defaults. This means that your settings will be restored automatically the next time you start a new game, or RESTART this one. \bRESTORE DEFAULTS: Explicitly restores the option settings you previously saved with SAVE DEFAULTS. "; } /* Unknown Words chapter */ showUnknownWordsChapter() { "\bUnknown Words \bThe story doesn’t pretend to know every word in the English language. The story might even occasionally use words in its own descriptions that it doesn’t understand in commands. If you type a command with a word the story doesn’t know, the story will tell you which word it didn’t recognize. If the story doesn’t know a word for something it described, and it doesn’t know any synonyms for that thing, you can probably assume that the object is just there as a detail of the setting, and isn’t important to the story. "; } /* Ambiguous Commands chapter */ showAmbiguousCmdChapter() { "\bAmbiguous Commands \bIf you type a command that leaves out some important information, the story will try its best to figure out what you mean. Whenever it’s reasonably obvious from context what you mean, the story will make an assumption about the missing information and proceed with the command. The story will point out what it’s assuming in these cases, to avoid any confusion from a mismatch between the story’s assumptions and yours. For example: \b \n\t >TIE THE ROPE \n\t (to the hook) \n\t The rope is now tied to the hook. The end of the \n\t rope nearly reaches the floor of the pit below. \bIf the command is ambiguous enough that the story can’t safely make an assumption, you’ll be asked for more information. You can answer these questions by typing the missing information. \b \n\t >UNLOCK THE DOOR \n\t What do you want to unlock it with? \b \n\t >KEY \n\t Which key do you mean, the gold key, or the silver key? \b \n\t >GOLD \n\t Unlocked. \bIf the story asks you one of these questions, and you decide that you don’t want to proceed with the original command after all, you can just type in a brand new command instead of answering the question."; } /* Advance Command Formats chapter */ showAdvancedCmdChapter() { "\bAdvanced Command Formats \bOnce you get comfortable with entering commands, you might be interested to know about some more complex command formats that the story will accept. These advanced commands are all completely optional, because you can always do the same things with the simpler formats we’ve talked about so far, but experienced players often like the advanced formats because they can save you a little typing. \bUsing Multiple Objects at Once \bIn most commands, you can operate on multiple objects in a single command. Use the word AND, or a comma, to separate one object from another: \b \n\t TAKE THE BOX, THE FLOPPY DISK, AND THE ROPE \n\t PUT DISK AND ROPE IN BOX \n\t DROP BOX AND ROPE \bYou can use the words ALL and EVERYTHING to refer to everything applicable to your command, and you can use EXCEPT or BUT (right after the word ALL) to exclude specific objects: \b \n\t TAKE ALL \n\t PUT ALL BUT DISK AND ROPE INTO BOX \n\t TAKE EVERYTHING OUT OF THE BOX \n\t TAKE ALL OFF SHELF \bALL refers to everything that makes sense for your command, excluding things inside other objects matching the ALL. For example, if you’re carrying a box and a rope, and the box contains a floppy disk, typing DROP ALL will drop the box and the rope, and the floppy will remain in the box. \bIt and Them \bYou can use IT and THEM to refer to the last object or objects that you used in a command: \b \n\t TAKE THE BOX \n\t OPEN IT \n\t TAKE THE DISK AND THE ROPE \n\t PUT THEM IN THE BOX \bMultiple Commands At Once \bYou can put multiple commands on a single input line by separating the commands with periods or the word THEN, or with a comma or AND. For example: \b \n\t TAKE THE DISK AND PUT IT IN THE BOX \n\t TAKE BOX. OPEN IT. \n\t UNLOCK THE DOOR WITH THE KEY. OPEN IT, THEN GO NORTH. \b If the story doesn’t understand one of the commands, it will tell you what it couldn’t understand, and ignore everything after that on the line."; } /* General Tips chapter */ showTipsChapter() { "\bA Few Tips \bNow that you know the technical details of entering commands, you might be interested in some strategy pointers. Here are a few techniques that experienced interactive fiction players use when approaching a story. \bEXAMINE everything, especially when you enter a new location for the first time. Looking at objects will often reveal details that aren’t mentioned in the broader description of the location. If examining an object mentions detailed parts of the object, examine them as well. \bMake a map, if the story has more than a few locations. When you encounter a new location for the first time, note all of its exits; this will make it easy to see at a glance if there are any exits you haven’t explored yet. If you get stuck, you can scan your map for any unexplored areas, where you might find what you’re looking for. \bIf the story doesn’t recognize a word or any of its synonyms, the object you’re trying to manipulate probably isn’t important to the story. If you try manipulating something, and the story responds with something like that isn’t important, you can probably just ignore the object; it’s most likely just there as scenery, to make the setting more detailed. \bIf you’re trying to accomplish something, and nothing you do seems to work, pay attention to what’s going wrong. If everything you try is met with utter dismissal (nothing happens or that’s not something you can open), you might simply be on the wrong track; step back and think about other ways of approaching the problem. If the response is something more specific, it might be a clue. The guard says you can’t open that here!\ and snatches the box from your hands — this might indicate that you have to get the guard to leave, or that you should take the box somewhere else before you open it, for example. \bIf you get completely stuck, you might try setting aside the current puzzle and working on a different problem for a while. You might even want to save your position and take a break from the game. The solution to the problem that’s been thwarting your progress might come to you in a flash of insight when you least expect it, and those moments of insight can be incredibly rewarding. Some stories are best appreciated when played over a period of days, weeks, or even months; if you’re enjoying the story, why rush through it? \bFinally, if all else fails, you can try asking for help. A great place to go for hints is the Usenet newsgroup
rec.games.int-fiction. "; "\n"; } /* INSTRUCTIONS doesn't affect UNDO or AGAIN */ isRepeatable = nil includeInUndo = nil ; /* ------------------------------------------------------------------------ */ /* * define the INSTRUCTIONS command's grammar */ VerbRule(instructions) 'instructions' : InstructionsAction ; /* ------------------------------------------------------------------------ */ /* * The instructions, rendered in menu form. The menu format helps break * up the instructions by dividing the instructions into chapters, and * displaying a menu that lists the chapters. This way, players can * easily go directly to the chapters they're most interested in, but * can also still read through the whole thing fairly easily. * * To avoid creating an unnecessary dependency on the menu subsystem for * games that don't want the menu-style instructions, we'll only define * the menu objects if the preprocessor symbol INSTRUCTIONS_MENU is * defined. So, if you want to use the menu-style instructions, just * add -D INSTRUCTIONS_MENU to your project makefile. */ #ifdef INSTRUCTIONS_MENU /* a base class for the instruction chapter menus */ class InstructionsMenu: MenuLongTopicItem /* * present the instructions in "chapter" format, so that we can * navigate from one chapter directly to the next */ isChapterMenu = true /* the InstructionsAction property that we invoke to show our chapter */ chapterProp = nil /* don't use a heading, as we provide our own internal chapter titles */ heading = '' /* show our chapter text */ menuContents = (InstructionsAction.(self.chapterProp)()) ; InstructionsMenu template 'title' ->chapterProp; /* * The main instructions menu. This can be used as a top-level menu; * just call the 'display()' method on this object to display the menu. * This can also be used as a sub-menu of any other menu, simply by * inserting this menu into a parent menu's 'contents' list. */ topInstructionsMenu: MenuItem 'How to Play Interactive Fiction'; /* the chapter menus */ + MenuLongTopicItem isChapterMenu = true title = 'Introduction' heading = nil menuContents() { "\bIntroduction \bWelcome! If you’ve never played Interactive Fiction before, these instructions are designed to help you get started. If you already know how to play this kind of game, you can probably skip the full instructions, but you might want to type ABOUT at the command prompt for a summary of the special features of this story. \b To make the instructions easier to navigate, they’re broken up into chapters. "; if (curKeyList != nil && curKeyList.length() > 0) "At the end of each chapter, just press <> to proceed to the next chapter, or <> to return to the chapter list. "; else "To flip through the chapters, click on the links or use the Left/Right arrow keys. "; } ; + InstructionsMenu 'Entering Commands' ->(&showCommandsChapter); + InstructionsMenu 'Command Abbreviations' ->(&showAbbrevChapter); + InstructionsMenu 'Travel' ->(&showTravelChapter); + InstructionsMenu 'Manipulating Objects' ->(&showObjectsChapter); + InstructionsMenu 'Interacting with Other Characters' ->(&showConversationChapter); + InstructionsMenu 'Time' ->(&showTimeChapter); + InstructionsMenu 'Saving and Restoring' ->(&showSaveRestoreChapter); + InstructionsMenu 'Other Special Commands' ->(&showSpecialCmdChapter); + InstructionsMenu 'Unknown Words' ->(&showUnknownWordsChapter); + InstructionsMenu 'Ambiguous Commands' ->(&showAmbiguousCmdChapter); + InstructionsMenu 'Advanced Command Formats' ->(&showAdvancedCmdChapter); + InstructionsMenu 'A Few Tips' ->(&showTipsChapter); #endif /* INSTRUCTIONS_MENU */ frobtads-1.2.3/tads3/lib/adv3/en_us/en_us.t0000644000175000001440000131531711766067740017565 0ustar realncusers#charset "us-ascii" /* * Copyright 2000, 2006 Michael J. Roberts. All Rights Reserved. *. Past-tense extensions written by Michel Nizette, and incorporated by * permission. * * TADS 3 Library - English (United States variant) implementation * * This defines the parts of the TADS 3 library that are specific to the * English language as spoken (and written) in the United States. * * We have attempted to isolate here the parts of the library that are * language-specific, so that translations to other languages or dialects * can be created by replacing this module, without changing the rest of * the library. * * In addition to this module, a separate set of US English messages are * defined in the various msg_xxx.t modules. Those modules define * messages in English for different stylistic variations. For a given * game, the author must select one of the message modules - but only * one, since they all define variations of the same messages. To * translate the library, a translator must create at least one module * defining those messages as well; only one message module is required * per language. * * The past-tense system was contributed by Michel Nizette. * *. ----- * * "Watch an immigrant struggling with a second language or a stroke * patient with a first one, or deconstruct a snatch of baby talk, or try * to program a computer to understand English, and ordinary speech * begins to look different." * *. Stephen Pinker, "The Language Instinct" */ #include "tads.h" #include "tok.h" #include "adv3.h" #include "en_us.h" #include #include #include #include /* ------------------------------------------------------------------------ */ /* * Fill in the default language for the GameInfo metadata class. */ modify GameInfoModuleID languageCode = 'en-US' ; /* ------------------------------------------------------------------------ */ /* * Simple yes/no confirmation. The caller must display a prompt; we'll * read a command line response, then return true if it's an affirmative * response, nil if not. */ yesOrNo() { /* switch to no-command mode for the interactive input */ "<.commandnone>"; /* * Read a line of input. Do not allow real-time event processing; * this type of prompt is used in the middle of a command, so we * don't want any interruptions. Note that the caller must display * any desired prompt, and since we don't allow interruptions, we * won't need to redisplay the prompt, so we pass nil for the prompt * callback. */ local str = inputManager.getInputLine(nil, nil); /* switch back to mid-command mode */ "<.commandmid>"; /* * If they answered with something starting with 'Y', it's * affirmative, otherwise it's negative. In reading the response, * ignore any leading whitespace. */ return rexMatch('*[yY]', str) != nil; } /* ------------------------------------------------------------------------ */ /* * During start-up, install a case-insensitive truncating comparator in * the main dictionary. */ PreinitObject execute() { /* set up the main dictionary's comparator */ languageGlobals.setStringComparator( new StringComparator(gameMain.parserTruncLength, nil, [])); } /* * Make sure we run BEFORE the main library preinitializer, so that * we install the comparator in the dictionary before we add the * vocabulary words to the dictionary. This doesn't make any * difference in terms of the correctness of the dictionary, since * the dictionary will automatically rebuild itself whenever we * install a new comparator, but it makes the preinitialization run * a tiny bit faster by avoiding that rebuild step. */ execAfterMe = [adv3LibPreinit] ; /* ------------------------------------------------------------------------ */ /* * Language-specific globals */ languageGlobals: object /* * Set the StringComparator object for the parser. This sets the * comparator that's used in the main command parser dictionary. */ setStringComparator(sc) { /* remember it globally, and set it in the main dictionary */ dictComparator = sc; cmdDict.setComparator(sc); } /* * The character to use to separate groups of digits in large * numbers. US English uses commas; most Europeans use periods. * * Note that this setting does not affect system-level BigNumber * formatting, but this information can be passed when calling * BigNumber formatting routines. */ digitGroupSeparator = ',' /* * The decimal point to display in floating-point numbers. US * English uses a period; most Europeans use a comma. * * Note that this setting doesn't affect system-level BigNumber * formatting, but this information can be passed when calling * BigNumber formatting routines. */ decimalPointCharacter = '.' /* the main dictionary's string comparator */ dictComparator = nil ; /* ------------------------------------------------------------------------ */ /* * Language-specific extension of the default gameMain object * implementation. */ modify GameMainDef /* * Option setting: the parser's truncation length for player input. * As a convenience to the player, we can allow the player to * truncate long words, entering only the first, say, 6 characters. * For example, rather than typing "x flashlight", we could allow the * player to simply type "x flashl" - truncating "flashlight" to six * letters. * * We use a default truncation length of 6, but games can change this * by overriding this property in gameMain. We use a default of 6 * mostly because that's what the old Infocom games did - many * long-time IF players are accustomed to six-letter truncation from * those games. Shorter lengths are superficially more convenient * for the player, obviously, but there's a trade-off, which is that * shorter truncation lengths create more potential for ambiguity. * For some games, a longer length might actually be better for the * player, because it would reduce spurious ambiguity due to the * parser matching short input against long vocabulary words. * * If you don't want to allow the player to truncate long words at * all, set this to nil. This will require the player to type every * word in its entirety. * * Note that changing this property dynamicaly will have no effect. * The library only looks at it once, during library initialization * at the very start of the game. If you want to change the * truncation length dynamically, you must instead create a new * StringComparator object with the new truncation setting, and call * languageGlobals.setStringComparator() to select the new object. */ parserTruncLength = 6 /* * Option: are we currently using a past tense narrative? By * default, we aren't. * * This property can be reset at any time during the game in order to * switch between the past and present tenses. The macro * setPastTense can be used for this purpose: it just provides a * shorthand for setting gameMain.usePastTense directly. * * Authors who want their game to start in the past tense can achieve * this by overriding this property on their gameMain object and * giving it a value of true. */ usePastTense = nil ; /* ------------------------------------------------------------------------ */ /* * Language-specific modifications for ThingState. */ modify ThingState /* * Our state-specific tokens. This is a list of vocabulary words * that are state-specific: that is, if a word is in this list, the * word can ONLY refer to this object if the object is in a state * with that word in its list. * * The idea is that you set up the object's "static" vocabulary with * the *complete* list of words for all of its possible states. For * example: * *. + Matchstick 'lit unlit match'; * * Then, you define the states: in the "lit" state, the word 'lit' is * in the stateTokens list; in the "unlit" state, the word 'unlit' is * in the list. By putting the words in the state lists, you * "reserve" the words to their respective states. When the player * enters a command, the parser will limit object matches so that the * reserved state-specific words can only refer to objects in the * corresponding states. Hence, if the player refers to a "lit * match", the word 'lit' will only match an object in the "lit" * state, because 'lit' is a reserved state-specific word associated * with the "lit" state. * * You can re-use a word in multiple states. For example, you could * have a "red painted" state and a "blue painted" state, along with * an "unpainted" state. */ stateTokens = [] /* * Match the name of an object in this state. We'll check the token * list for any words that apply only to *other* states the object * can assume; if we find any, we'll reject the match, since the * phrase must be referring to an object in a different state. */ matchName(obj, origTokens, adjustedTokens, states) { /* scan each word in our adjusted token list */ for (local i = 1, local len = adjustedTokens.length() ; i <= len ; i += 2) { /* get the current token */ local cur = adjustedTokens[i]; /* * If this token is in our own state-specific token list, * it's acceptable as a match to this object. (It doesn't * matter whether or not it's in any other state's token list * if it's in our own, because its presence in our own makes * it an acceptable matching word when we're in this state.) */ if (stateTokens.indexWhich({t: t == cur}) != nil) continue; /* * It's not in our own state-specific token list. Check to * see if the word appears in ANOTHER state's token list: if * it does, then this word CAN'T match an object in this * state, because the token is special to that other state * and thus can't refer to an object in a state without the * token. */ if (states.indexWhich( {s: s.stateTokens.indexOf(cur) != nil}) != nil) return nil; } /* we didn't find any objection, so we can match this phrase */ return obj; } /* * Check a token list for any tokens matching any of our * state-specific words. Returns true if we find any such words, * nil if not. * * 'toks' is the *adjusted* token list used in matchName(). */ findStateToken(toks) { /* * Scan the token list for a match to any of our state-specific * words. Since we're using the adjusted token list, every * other entry is a part of speech, so work through the list in * pairs. */ for (local i = 1, local len = toks.length() ; i <= len ; i += 2) { /* * if this token matches any of our state tokens, indicate * that we found a match */ if (stateTokens.indexWhich({x: x == toks[i]}) != nil) return true; } /* we didn't find a match */ return nil; } /* get our name */ listName(lst) { return listName_; } /* * our list name setting - we define this so that we can be easily * initialized with a template (we can't initialize listName() * directly in this manner because it's a method, but we define the * listName() method to simply return this property value, which we * can initialize with a template) */ listName_ = nil ; /* ------------------------------------------------------------------------ */ /* * Language-specific modifications for VocabObject. */ modify VocabObject /* * The vocabulary initializer string for the object - this string * can be initialized (most conveniently via a template) to a string * of this format: * * 'adj adj adj noun/noun/noun*plural plural plural' * * The noun part of the string can be a hyphen, '-', in which case * it means that the string doesn't specify a noun or plural at all. * This can be useful when nouns and plurals are all inherited from * base classes, and only adjectives are to be specified. (In fact, * any word that consists of a single hyphen will be ignored, but * this is generally only useful for the adjective-only case.) * * During preinitialization, we'll parse this string and generate * dictionary entries and individual vocabulary properties for the * parts of speech we find. * * Note that the format described above is specific to the English * version of the library. Non-English versions will probably want * to use different formats to conveniently encode appropriate * language-specific information in the initializer string. See the * comments for initializeVocabWith() for more details. * * You can use the special wildcard # to match any numeric * adjective. This only works as a wildcard when it stands alone, * so a string like "7#" is matched as that literal string, not as a * wildcard. If you want to use a pound sign as a literal * adjective, just put it in double quotes. * * You can use the special wildcard "\u0001" (include the double * quotes within the string) to match any literal adjective. This * is the literal adjective equivalent of the pound sign. We use * this funny character value because it is unlikely ever to be * interesting in user input. * * If you want to match any string for a noun and/or adjective, you * can't do it with this property. Instead, just add the property * value noun='*' to the object. */ vocabWords = '' /* * On dynamic construction, initialize our vocabulary words and add * them to the dictionary. */ construct() { /* initialize our vocabulary words from vocabWords */ initializeVocab(); /* add our vocabulary words to the dictionary */ addToDictionary(&noun); addToDictionary(&adjective); addToDictionary(&plural); addToDictionary(&adjApostS); addToDictionary(&literalAdjective); } /* add the words from a dictionary property to the global dictionary */ addToDictionary(prop) { /* if we have any words defined, add them to the dictionary */ if (self.(prop) != nil) cmdDict.addWord(self, self.(prop), prop); } /* initialize the vocabulary from vocabWords */ initializeVocab() { /* inherit vocabulary from this class and its superclasses */ inheritVocab(self, new Vector(10)); } /* * Inherit vocabulary from this class and its superclasses, adding * the words to the given target object. 'target' is the object to * which we add our vocabulary words, and 'done' is a vector of * classes that have been visited so far. * * Since a class can be inherited more than once in an inheritance * tree (for example, a class can have multiple superclasses, each * of which have a common base class), we keep a vector of all of * the classes we've visited. If we're already in the vector, we'll * skip adding vocabulary for this class or its superclasses, since * we must have already traversed this branch of the tree from * another subclass. */ inheritVocab(target, done) { /* * if we're in the list of classes handled already, don't bother * visiting me again */ if (done.indexOf(self) != nil) return; /* add myself to the list of classes handled already */ done.append(self); /* * add words from our own vocabWords to the target object (but * only if it's our own - not if it's only inherited, as we'll * pick up the inherited ones explicitly in a bit) */ if (propDefined(&vocabWords, PropDefDirectly)) target.initializeVocabWith(vocabWords); /* add vocabulary from each of our superclasses */ foreach (local sc in getSuperclassList()) sc.inheritVocab(target, done); } /* * Initialize our vocabulary from the given string. This parses the * given vocabulary initializer string and adds the words defined in * the string to the dictionary. * * Note that this parsing is intentionally located in the * English-specific part of the library, because it is expected that * other languages will want to define their own vocabulary * initialization string formats. For example, a language with * gendered nouns might want to use gendered articles in the * initializer string as an author-friendly way of defining noun * gender; languages with inflected (declined) nouns and/or * adjectives might want to encode inflected forms in the * initializer. Non-English language implementations are free to * completely redefine the format - there's no need to follow the * conventions of the English format in other languages where * different formats would be more convenient. */ initializeVocabWith(str) { local sectPart; local modList = []; /* start off in the adjective section */ sectPart = &adjective; /* scan the string until we run out of text */ while (str != '') { local len; local cur; /* * if it starts with a quote, find the close quote; * otherwise, find the end of the current token by seeking * the next delimiter */ if (str.startsWith('"')) { /* find the close quote */ len = str.find('"', 2); } else { /* no quotes - find the next delimiter */ len = rexMatch('<^space|star|/>*', str); } /* if there's no match, use the whole rest of the string */ if (len == nil) len = str.length(); /* if there's anything before the delimiter, extract it */ if (len != 0) { /* extract the part up to but not including the delimiter */ cur = str.substr(1, len); /* * if we're in the adjectives, and either this is the * last token or the next delimiter is not a space, this * is implicitly a noun */ if (sectPart == &adjective && (len == str.length() || str.substr(len + 1, 1) != ' ')) { /* move to the noun section */ sectPart = &noun; } /* * if the word isn't a single hyphen (in which case it's * a null word placeholder, not an actual vocabulary * word), add it to our own appropriate part-of-speech * property and to the dictionary */ if (cur != '-') { /* * by default, use the part of speech of the current * string section as the part of speech for this * word */ local wordPart = sectPart; /* * Check for parentheses, which indicate that the * token is "weak." This doesn't affect anything * about the token or its part of speech except that * we must include the token in our list of weak * tokens. */ if (cur.startsWith('(') && cur.endsWith(')')) { /* it's a weak token - remove the parens */ cur = cur.substr(2, cur.length() - 2); /* * if we don't have a weak token list yet, * create the list */ if (weakTokens == nil) weakTokens = []; /* add the token to the weak list */ weakTokens += cur; } /* * Check for special formats: quoted strings, * apostrophe-S words. These formats are mutually * exclusive. */ if (cur.startsWith('"')) { /* * It's a quoted string, so it's a literal * adjective. */ /* remove the quote(s) */ if (cur.endsWith('"')) cur = cur.substr(2, cur.length() - 2); else cur = cur.substr(2); /* change the part of speech to 'literal adjective' */ wordPart = &literalAdjective; } else if (cur.endsWith('\'s')) { /* * It's an apostrophe-s word. Remove the "'s" * suffix and add the root word using adjApostS * as the part of speech. The grammar rules are * defined to allow this part of speech to be * used exclusively with "'s" suffixes in input. * Since the tokenizer always pulls the "'s" * suffix off of a word in the input, we have to * store any vocabulary words with "'s" suffixes * the same way, with the "'s" suffixes removed. */ /* change the part of speech to adjApostS */ wordPart = &adjApostS; /* remove the "'s" suffix from the string */ cur = cur.substr(1, cur.length() - 2); } /* add the word to our own list for this part of speech */ if (self.(wordPart) == nil) self.(wordPart) = [cur]; else self.(wordPart) += cur; /* add it to the dictionary */ cmdDict.addWord(self, cur, wordPart); if (cur.endsWith('.')) { local abbr; /* * It ends with a period, so this is an * abbreviated word. Enter the abbreviation * both with and without the period. The normal * handling will enter it with the period, so we * only need to enter it specifically without. */ abbr = cur.substr(1, cur.length() - 1); self.(wordPart) += abbr; cmdDict.addWord(self, abbr, wordPart); } /* note that we added to this list */ if (modList.indexOf(wordPart) == nil) modList += wordPart; } } /* if we have a delimiter, see what we have */ if (len + 1 < str.length()) { /* check the delimiter */ switch(str.substr(len + 1, 1)) { case ' ': /* stick with the current part */ break; case '*': /* start plurals */ sectPart = &plural; break; case '/': /* start alternative nouns */ sectPart = &noun; break; } /* remove the part up to and including the delimiter */ str = str.substr(len + 2); /* skip any additional spaces following the delimiter */ if ((len = rexMatch('+', str)) != nil) str = str.substr(len + 1); } else { /* we've exhausted the string - we're done */ break; } } /* uniquify each word list we updated */ foreach (local p in modList) self.(p) = self.(p).getUnique(); } ; /* ------------------------------------------------------------------------ */ /* * Language-specific modifications for Thing. This class contains the * methods and properties of Thing that need to be replaced when the * library is translated to another language. * * The properties and methods defined here should generally never be used * by language-independent library code, because everything defined here * is specific to English. Translators are thus free to change the * entire scheme defined here. For example, the notions of number and * gender are confined to the English part of the library; other language * implementations can completely replace these attributes, so they're * not constrained to emulate their own number and gender systems with * the English system. */ modify Thing /* * Flag that this object's name is rendered as a plural (this * applies to both a singular noun with plural usage, such as * "pants" or "scissors," and an object used in the world model to * represent a collection of real-world objects, such as "shrubs"). */ isPlural = nil /* * Flag that this is object's name is a "mass noun" - that is, a * noun denoting a continuous (effectively infinitely divisible) * substance or material, such as water, wood, or popcorn; and * certain abstract concepts, such as knowledge or beauty. Mass * nouns are never rendered in the plural, and use different * determiners than ordinary ("count") nouns: "some popcorn" vs "a * kernel", for example. */ isMassNoun = nil /* * Flags indicating that the object should be referred to with * gendered pronouns (such as 'he' or 'she' rather than 'it'). * * Note that these flags aren't mutually exclusive, so it's legal * for the object to have both masculine and feminine usage. This * can be useful when creating collective objects that represent * more than one individual, for example. */ isHim = nil isHer = nil /* * Flag indicating that the object can be referred to with a neuter * pronoun ('it'). By default, this is true if the object has * neither masculine nor feminine gender, but it can be overridden * so that an object has both gendered and ungendered usage. This * can be useful for collective objects, as well as for cases where * gendered usage varies by speaker or situation, such as animals. */ isIt { /* by default, we're an 'it' if we're not a 'him' or a 'her' */ return !(isHim || isHer); } /* * Test to see if we can match the pronouns 'him', 'her', 'it', and * 'them'. By default, these simply test the corresponding isXxx * flags (except 'canMatchThem', which tests 'isPlural' to see if the * name has plural usage). */ canMatchHim = (isHim) canMatchHer = (isHer) canMatchIt = (isIt) canMatchThem = (isPlural) /* can we match the given PronounXxx pronoun type specifier? */ canMatchPronounType(typ) { /* check the type, and return the appropriate indicator property */ switch (typ) { case PronounHim: return canMatchHim; case PronounHer: return canMatchHer; case PronounIt: return canMatchIt; case PronounThem: return canMatchThem; default: return nil; } } /* * The grammatical cardinality of this item when it appears in a * list. This is used to ensure verb agreement when mentioning the * item in a list of items. ("Cardinality" is a fancy word for "how * many items does this look like"). * * English only distinguishes two degrees of cardinality in its * grammar: one, or many. That is, when constructing a sentence, the * only thing the grammar cares about is whether an object is * singular or plural: IT IS on the table, THEY ARE on the table. * Since English only distinguishes these two degrees, two is the * same as a hundred is the same as a million for grammatical * purposes, so we'll consider our cardinality to be 2 if we're * plural, 1 otherwise. * * Some languages don't express cardinality at all in their grammar, * and others distinguish cardinality in greater detail than just * singular-vs-plural, which is why this method has to be in the * language-specific part of the library. */ listCardinality(lister) { return isPlural ? 2 : 1; } /* * Proper name flag. This indicates that the 'name' property is the * name of a person or place. We consider proper names to be fully * qualified, so we don't add articles for variations on the name * such as 'theName'. */ isProperName = nil /* * Qualified name flag. This indicates that the object name, as * given by the 'name' property, is already fully qualified, so * doesn't need qualification by an article like "the" or "a" when * it appears in a sentence. By default, a name is considered * qualified if it's a proper name, but this can be overridden to * mark a non-proper name as qualified when needed. */ isQualifiedName = (isProperName) /* * The name of the object - this is a string giving the object's * short description, for constructing sentences that refer to the * object by name. Each instance should override this to define the * name of the object. This string should not contain any articles; * we use this string as the root to generate various forms of the * object's name for use in different places in sentences. */ name = '' /* * The name of the object, for the purposes of disambiguation * prompts. This should almost always be the object's ordinary * name, so we return self.name by default. * * In rare cases, it might be desirable to override this. In * particular, if a game has two objects that are NOT defined as * basic equivalents of one another (which means that the parser * will always ask for disambiguation when the two are ambiguous * with one another), but the two nonetheless have identical 'name' * properties, this property should be overridden for one or both * objects to give them different names. This will ensure that we * avoid asking questions of the form "which do you mean, the coin, * or the coin?". In most cases, non-equivalent objects will have * distinct 'name' properties to begin with, so this is not usually * an issue. * * When overriding this method, take care to override * theDisambigName, aDisambigName, countDisambigName, and/or * pluralDisambigName as needed. Those routines must be overridden * only when the default algorithms for determining articles and * plurals fail to work properly for the disambigName (for example, * the indefinite article algorithm fails with silent-h words like * "hour", so if disambigName is "hour", aDisambigName must be * overridden). In most cases, the automatic algorithms will * produce acceptable results, so the default implementations of * these other routines can be used without customization. */ disambigName = (name) /* * The "equivalence key" is the value we use to group equivalent * objects. Note that we can only treat objects as equivalent when * they're explicitly marked with isEquivalent=true, so the * equivalence key is irrelevant for objects not so marked. * * Since the main point of equivalence is to allow creation of groups * of like-named objects that are interchangeable in listings and in * command input, we use the basic disambiguation name as the * equivalence key. */ equivalenceKey = (disambigName) /* * The definite-article name for disambiguation prompts. * * By default, if the disambiguation name is identical to the * regular name (i.e, the string returned by self.disambigName is * the same as the string returned by self.name), then we simply * return self.theName. Since the base name is the same in either * case, presumably the definite article names should be the same as * well. This way, if the object overrides theName to do something * special, then we'll use the same definite-article name for * disambiguation prompts. * * If the disambigName isn't the same as the regular name, then * we'll apply the same algorithm to the base disambigName that we * normally do to the regular name to produce the theName. This * way, if the disambigName is overridden, we'll use the overridden * disambigName to produce the definite-article version, using the * standard definite-article algorithm. * * Note that there's an aspect of this conditional approach that * might not be obvious. It might look as though the test is * redundant: if name == disambigName, after all, and the default * theName returns theNameFrom(name), then this ought to be * identical to returning theNameFrom(disambigName). The subtlety * is that theName could be overridden to produce a custom result, * in which case returning theNameFrom(disambigName) would return * something different, which probably wouldn't be correct: the * whole reason theName would be overridden is that the algorithmic * determination (theNameFrom) gets it wrong. So, by calling * theName directly when disambigName is the same as name, we are * assured that we pick up any override in theName. * * Note that in rare cases, neither of these default approaches will * produce the right result; this will happen if the object uses a * custom disambigName, but that name doesn't fit the normal * algorithmic pattern for applying a definite article. In these * cases, the object should simply override this method to specify * the custom name. */ theDisambigName = (name == disambigName ? theName : theNameFrom(disambigName)) /* * The indefinite-article name for disambiguation prompts. We use * the same logic here as in theDisambigName. */ aDisambigName = (name == disambigName ? aName : aNameFrom(disambigName)) /* * The counted name for disambiguation prompts. We use the same * logic here as in theDisambigName. */ countDisambigName(cnt) { return (name == disambigName && pluralName == pluralDisambigName ? countName(cnt) : countNameFrom(cnt, disambigName, pluralDisambigName)); } /* * The plural name for disambiguation prompts. We use the same * logic here as in theDisambigName. */ pluralDisambigName = (name == disambigName ? pluralName : pluralNameFrom(disambigName)) /* * The name of the object, for the purposes of disambiguation prompts * to disambiguation among this object and basic equivalents of this * object (i.e., objects of the same class marked with * isEquivalent=true). * * This is used in disambiguation prompts in place of the actual text * typed by the user. For example, suppose the user types ">take * coin", then we ask for help disambiguating, and the player types * ">gold". This narrows things down to, say, three gold coins, but * they're in different locations so we need to ask for further * disambiguation. Normally, we ask "which gold do you mean", * because the player typed "gold" in the input. Once we're down to * equivalents, we don't have to rely on the input text any more, * which is good because the input text could be fragmentary (as in * our present example). Since we have only equivalents, we can use * the actual name of the objects (they're all the same, after all). * This property gives the name we use. * * For English, this is simply the object's ordinary disambiguation * name. This property is separate from 'name' and 'disambigName' * for the sake of languages that need to use an inflected form in * this context. */ disambigEquivName = (disambigName) /* * Single-item listing description. This is used to display the * item when it appears as a single (non-grouped) item in a list. * By default, we just show the indefinite article description. */ listName = (aName) /* * Return a string giving the "counted name" of the object - that is, * a phrase describing the given number of the object. For example, * for a red book, and a count of 5, we might return "five red * books". By default, we use countNameFrom() to construct a phrase * from the count and either our regular (singular) 'name' property * or our 'pluralName' property, according to whether count is 1 or * more than 1. */ countName(count) { return countNameFrom(count, name, pluralName); } /* * Returns a string giving a count applied to the name string. The * name must be given in both singular and plural forms. */ countNameFrom(count, singularStr, pluralStr) { /* if the count is one, use 'one' plus the singular name */ if (count == 1) return 'one ' + singularStr; /* * Get the number followed by a space - spell out numbers below * 100, but use numerals to denote larger numbers. Append the * plural name to the number and return the result. */ return spellIntBelowExt(count, 100, 0, DigitFormatGroupSep) + ' ' + pluralStr; } /* * Get the 'pronoun selector' for the various pronoun methods. This * returns: * *. - singular neuter = 1 *. - singular masculine = 2 *. - singular feminine = 3 *. - plural = 4 */ pronounSelector = (isPlural ? 4 : isHer ? 3 : isHim ? 2 : 1) /* * get a string with the appropriate pronoun for the object for the * nominative case, objective case, possessive adjective, possessive * noun */ itNom { return ['it', 'he', 'she', 'they'][pronounSelector]; } itObj { return ['it', 'him', 'her', 'them'][pronounSelector]; } itPossAdj { return ['its', 'his', 'her', 'their'][pronounSelector]; } itPossNoun { return ['its', 'his', 'hers', 'theirs'][pronounSelector]; } /* get the object reflexive pronoun (itself, etc) */ itReflexive { return ['itself', 'himself', 'herself', 'themselves'] [pronounSelector]; } /* demonstrative pronouns ('that' or 'those') */ thatNom { return ['that', 'he', 'she', 'those'][pronounSelector]; } thatIsContraction { return thatNom + tSel(isPlural ? ' are' : '’s', ' ' + verbToBe); } thatObj { return ['that', 'him', 'her', 'those'][pronounSelector]; } /* * get a string with the appropriate pronoun for the object plus the * correct conjugation of 'to be' */ itIs { return itNom + ' ' + verbToBe; } /* get a pronoun plus a 'to be' contraction */ itIsContraction { return itNom + tSel(isPlural ? '’re' : '’s', ' ' + verbToBe); } /* * get a string with the appropriate pronoun for the object plus the * correct conjugation of the given regular verb for the appropriate * person */ itVerb(verb) { return itNom + ' ' + conjugateRegularVerb(verb); } /* * Conjugate a regular verb in the present or past tense for our * person and number. * * In the present tense, this is pretty easy: we add an 's' for the * third person singular, and leave the verb unchanged for plural (it * asks, they ask). The only complication is that we must check some * special cases to add the -s suffix: -y -> -ies (it carries), -o -> * -oes (it goes). * * In the past tense, we can equally easily figure out when to use * -d, -ed, or -ied. However, we have a more serious problem: for * some verbs, the last consonant of the verb stem should be repeated * (as in deter -> deterred), and for others it shouldn't (as in * gather -> gathered). To figure out which rule applies, we would * sometimes need to know whether the last syllable is stressed, and * unfortunately there is no easy way to determine that * programmatically. * * Therefore, we do *not* handle the case where the last consonant is * repeated in the past tense. You shouldn't use this method for * this case; instead, treat it as you would handle an irregular * verb, by explicitly specifying the correct past tense form via the * tSel macro. For example, to generate the properly conjugated form * of the verb "deter" for an object named "thing", you could use an * expression such as: * * 'deter' + tSel(thing.verbEndingS, 'red') * * This would correctly generate "deter", "deters", or "deterred" * depending on the number of the object named "thing" and on the * current narrative tense. */ conjugateRegularVerb(verb) { /* * Which tense are we currently using? */ if (gameMain.usePastTense) { /* * We want the past tense form. * * If the last letter is 'e', simply add 'd'. */ if (verb.endsWith('e')) return verb + 'd'; /* * Otherwise, if the verb ending would become 'ies' in the * third-person singular present, then it becomes 'ied' in * the past. */ else if (rexMatch(iesEndingPat, verb)) return verb.substr(1, verb.length() - 1) + 'ied'; /* * Otherwise, use 'ed' as the ending. Don't try to determine * if the last consonant should be repeated: that's too * complicated. We'll just ignore the possibility. */ else return verb + 'ed'; } else { /* * We want the present tense form. * * Check our number and person. */ if (isPlural) { /* * We're plural, so simply use the base verb form ("they * ask"). */ return verb; } else { /* * Third-person singular, so we must add the -s suffix. * Check for special spelling cases: * * '-y' changes to '-ies', unless the 'y' is preceded by * a vowel * * '-sh', '-ch', and '-o' endings add suffix '-es' */ if (rexMatch(iesEndingPat, verb)) return verb.substr(1, verb.length() - 1) + 'ies'; else if (rexMatch(esEndingPat, verb)) return verb + 'es'; else return verb + 's'; } } } /* verb-ending patterns for figuring out which '-s' ending to add */ iesEndingPat = static new RexPattern('.*[^aeiou]y$') esEndingPat = static new RexPattern('.*(o|ch|sh)$') /* * Get the name with a definite article ("the box"). By default, we * use our standard definite article algorithm to apply an article * to self.name. * * The name returned must be in the nominative case (which makes no * difference unless the name is a pronoun, since in English * ordinary nouns don't vary according to how they're used in a * sentence). */ theName = (theNameFrom(name)) /* * theName in objective case. In most cases, this is identical to * the normal theName, so we use that by default. This must be * overridden if theName is a pronoun (which is usually only the * case for player character actors; see our language-specific Actor * modifications for information on that case). */ theNameObj { return theName; } /* * Generate the definite-article name from the given name string. * If my name is already qualified, don't add an article; otherwise, * add a 'the' as the prefixed definite article. */ theNameFrom(str) { return (isQualifiedName ? '' : 'the ') + str; } /* * theName as a possessive adjective (Bob's book, your book). If the * name's usage is singular (i.e., isPlural is nil), we'll simply add * an apostrophe-S. If the name is plural, and it ends in an "s", * we'll just add an apostrophe (no S). If it's plural and doesn't * end in "s", we'll add an apostrophe-S. * * Note that some people disagree about the proper usage for * singular-usage words (especially proper names) that end in 's'. * Some people like to use a bare apostrophe for any name that ends * in 's' (so Chris -> Chris'); other people use apostrophe-s for * singular words that end in an "s" sound and a bare apostrophe for * words that end in an "s" that sounds like a "z" (so Charles * Dickens -> Charles Dickens'). However, most usage experts agree * that proper names take an apostrophe-S in almost all cases, even * when ending with an "s": "Chris's", "Charles Dickens's". That's * what we do here. * * Note that this algorithm doesn't catch all of the special * exceptions in conventional English usage. For example, Greek * names ending with "-es" are usually written with the bare * apostrophe, but we don't have a property that tells us whether the * name is Greek or not, so we can't catch this case. Likewise, some * authors like to possessive-ize words that end with an "s" sound * with a bare apostrophe, as in "for appearance' sake", and we don't * attempt to catch these either. For any of these exceptions, you * must override this method for the individual object. */ theNamePossAdj { /* add apostrophe-S, unless it's a plural ending with 's' */ return theName + (isPlural && theName.endsWith('s') ? '’' : '’s'); } /* * TheName as a possessive noun (that is Bob's, that is yours). We * simply return the possessive adjective name, since the two forms * are usually identical in English (except for pronouns, where they * sometimes differ: "her" for the adjective vs "hers" for the noun). */ theNamePossNoun = (theNamePossAdj) /* * theName with my nominal owner explicitly stated, if we have a * nominal owner: "your backpack," "Bob's flashlight." If we have * no nominal owner, this is simply my theName. */ theNameWithOwner() { local owner; /* * if we have a nominal owner, show with our owner name; * otherwise, just show our regular theName */ if ((owner = getNominalOwner()) != nil) return owner.theNamePossAdj + ' ' + name; else return theName; } /* * Default preposition to use when an object is in/on this object. * By default, we use 'in' as the preposition; subclasses can * override to use others (such as 'on' for a surface). */ objInPrep = 'in' /* * Default preposition to use when an actor is in/on this object (as * a nested location), and full prepositional phrase, with no article * and with an indefinite article. By default, we use the objInPrep * for actors as well. */ actorInPrep = (objInPrep) /* preposition to use when an actor is being removed from this location */ actorOutOfPrep = 'out of' /* preposition to use when an actor is being moved into this location */ actorIntoPrep { if (actorInPrep is in ('in', 'on')) return actorInPrep + 'to'; else return actorInPrep; } /* * describe an actor as being in/being removed from/being moved into * this location */ actorInName = (actorInPrep + ' ' + theNameObj) actorInAName = (actorInPrep + ' ' + aNameObj) actorOutOfName = (actorOutOfPrep + ' ' + theNameObj) actorIntoName = (actorIntoPrep + ' ' + theNameObj) /* * A prepositional phrase that can be used to describe things that * are in this room as seen from a remote point of view. This * should be something along the lines of "in the airlock", "at the * end of the alley", or "on the lawn". * * 'pov' is the point of view from which we're seeing this room; * this might be * * We use this phrase in cases where we need to describe things in * this room when viewed from a point of view outside of the room * (i.e., in a different top-level room). By default, we'll use our * actorInName. */ inRoomName(pov) { return actorInName; } /* * Provide the prepositional phrase for an object being put into me. * For a container, for example, this would say "into the box"; for * a surface, it would say "onto the table." By default, we return * our library message given by our putDestMessage property; this * default is suitable for most cases, but individual objects can * customize as needed. When customizing this, be sure to make the * phrase suitable for use in sentences like "You put the book * <>" and "The book falls <>" - the phrase * should be suitable for a verb indicating active motion by the * object being received. */ putInName() { return gLibMessages.(putDestMessage)(self); } /* * Get a description of an object within this object, describing the * object's location as this object. By default, we'll append "in * " to the given object name. */ childInName(childName) { return childInNameGen(childName, theName); } /* * Get a description of an object within this object, showing the * owner of this object. This is similar to childInName, but * explicitly shows the owner of the containing object, if any: "the * flashlight in bob's backpack". */ childInNameWithOwner(childName) { return childInNameGen(childName, theNameWithOwner); } /* * get a description of an object within this object, as seen from a * remote location */ childInRemoteName(childName, pov) { return childInNameGen(childName, inRoomName(pov)); } /* * Base routine for generating childInName and related names. Takes * the name to use for the child and the name to use for me, and * combines them appropriately. * * In most cases, this is the only one of the various childInName * methods that needs to be overridden per subclass, since the others * are defined in terms of this one. Note also that if the only * thing you need to do is change the preposition from 'in' to * something else, you can just override objInPrep instead. */ childInNameGen(childName, myName) { return childName + ' ' + objInPrep + ' ' + myName; } /* * Get my name (in various forms) distinguished by my owner or * location. * * If the object has an owner, and either we're giving priority to * the owner or our immediate location is the same as the owner, * we'll show using a possessive form with the owner ("bob's * flashlight"). Otherwise, we'll show the name distinguished by * our immediate container ("the flashlight in the backpack"). * * These are used by the ownership and location distinguishers to * list objects according to owners in disambiguation lists. The * ownership distinguisher gives priority to naming by ownership, * regardless of the containment relationship between owner and * self; the location distinguisher gives priority to naming by * location, showing the owner only if the owner is the same as the * location. * * We will presume that objects with proper names are never * indistinguishable from other objects with proper names, so we * won't worry about cases like "Bob's Bill". This leaves us free * to use appropriate articles in all cases. */ aNameOwnerLoc(ownerPriority) { local owner; /* show in owner or location format, as appropriate */ if ((owner = getNominalOwner()) != nil && (ownerPriority || isDirectlyIn(owner))) { local ret; /* * we have an owner - show as "one of Bob's items" (or just * "Bob's items" if this is a mass noun or a proper name) */ ret = owner.theNamePossAdj + ' ' + pluralName; if (!isMassNoun && !isPlural) ret = 'one of ' + ret; /* return the result */ return ret; } else { /* we have no owner - show as "an item in the location" */ return location.childInNameWithOwner(aName); } } theNameOwnerLoc(ownerPriority) { local owner; /* show in owner or location format, as appropriate */ if ((owner = getNominalOwner()) != nil && (ownerPriority || isDirectlyIn(owner))) { /* we have an owner - show as "Bob's item" */ return owner.theNamePossAdj + ' ' + name; } else { /* we have no owner - show as "the item in the location" */ return location.childInNameWithOwner(theName); } } countNameOwnerLoc(cnt, ownerPriority) { local owner; /* show in owner or location format, as appropriate */ if ((owner = getNominalOwner()) != nil && (ownerPriority || isDirectlyIn(owner))) { /* we have an owner - show as "Bob's five items" */ return owner.theNamePossAdj + ' ' + countName(cnt); } else { /* we have no owner - show as "the five items in the location" */ return location.childInNameWithOwner('the ' + countName(cnt)); } } /* * Note that I'm being used in a disambiguation prompt by * owner/location. If we're showing the owner, we'll set the * antecedent for the owner's pronoun, if the owner is a 'him' or * 'her'; this allows the player to refer back to our prompt text * with appropriate pronouns. */ notePromptByOwnerLoc(ownerPriority) { local owner; /* show in owner or location format, as appropriate */ if ((owner = getNominalOwner()) != nil && (ownerPriority || isDirectlyIn(owner))) { /* we are showing by owner - let the owner know about it */ owner.notePromptByPossAdj(); } } /* * Note that we're being used in a prompt question with our * possessive adjective. If we're a 'him' or a 'her', set our * pronoun antecedent so that the player's response to the prompt * question can refer back to the prompt text by pronoun. */ notePromptByPossAdj() { if (isHim) gPlayerChar.setHim(self); if (isHer) gPlayerChar.setHer(self); } /* * My name with an indefinite article. By default, we figure out * which article to use (a, an, some) automatically. * * In rare cases, the automatic determination might get it wrong, * since some English spellings defy all of the standard * orthographic rules and must simply be handled as special cases; * for example, the algorithmic determination doesn't know about * silent-h words like "hour". When the automatic determination * gets it wrong, simply override this routine to specify the * correct article explicitly. */ aName = (aNameFrom(name)) /* the indefinite-article name in the objective case */ aNameObj { return aName; } /* * Apply an indefinite article ("a box", "an orange", "some lint") * to the given name. We'll try to figure out which indefinite * article to use based on what kind of noun phrase we use for our * name (singular, plural, or a "mass noun" like "lint"), and our * spelling. * * By default, we'll use the article "a" if the name starts with a * consonant, or "an" if it starts with a vowel. * * If the name starts with a "y", we'll look at the second letter; * if it's a consonant, we'll use "an", otherwise "a" (hence "an * yttrium block" but "a yellow brick"). * * If the object is marked as having plural usage, we will use * "some" as the article ("some pants" or "some shrubs"). * * Some objects will want to override the default behavior, because * the lexical rules about when to use "a" and "an" are not without * exception. For example, silent-"h" words ("honor") are written * with "an", and "h" words with a pronounced but weakly stressed * initial "h" are sometimes used with "an" ("an historian"). Also, * some 'y' words might not follow the generic 'y' rule. * * 'U' words are especially likely not to follow any lexical rule - * any 'u' word that sounds like it starts with 'y' should use 'a' * rather than 'an', but there's no good way to figure that out just * looking at the spelling (consider "a universal symbol" and "an * unimportant word", or "a unanimous decision" and "an unassuming * man"). We simply always use 'an' for a word starting with 'u', * but this will have to be overridden when the 'u' sounds like 'y'. */ aNameFrom(str) { /* remember the original source string */ local inStr = str; /* * The complete list of unaccented, accented, and ligaturized * Latin vowels from the Unicode character set. (The Unicode * database doesn't classify characters as vowels or the like, * so it seems the only way we can come up with this list is * simply to enumerate the vowels.) * * These are all lower-case letters; all of these are either * exclusively lower-case or have upper-case equivalents that * map to these lower-case letters. * * (Note an implementation detail: the compiler will append all * of these strings together at compile time, so we don't have * to perform all of this concatenation work each time we * execute this method.) * * Note that we consider any word starting with an '8' to start * with a vowel, since 'eight' and 'eighty' both take 'an'. */ local vowels = '8aeiou\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6' + '\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF' + '\u00F2\u00F3\u00F4\u00F5\u00F6\u00F8\u00F9\u00FA' + '\u00FB\u00FC\u0101\u0103\u0105\u0113\u0115\u0117' + '\u0119\u011B\u0129\u012B\u012D\u012F\u014D\u014F' + '\u0151\u0169\u016B\u016D\u016F\u0171\u0173\u01A1' + '\u01A3\u01B0\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8' + '\u01DA\u01DC\u01DF\u01E1\u01E3\u01EB\u01ED\u01FB' + '\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B' + '\u020D\u020F\u0215\u0217\u0254\u025B\u0268\u0289' + '\u1E01\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E2D\u1E2F' + '\u1E4D\u1E4F\u1E51\u1E53\u1E73\u1E75\u1E77\u1E79' + '\u1E7B\u1E9A\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB' + '\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB' + '\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB' + '\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB' + '\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB' + '\u1EED\u1EEF\u1EF1\uFF41\uFF4F\uFF55'; /* * A few upper-case vowels in unicode don't have lower-case * mappings - consider them separately. */ local vowelsUpperOnly = '\u0130\u019f'; /* * the various accented forms of the letter 'y' - these are all * lower-case versions; the upper-case versions all map to these */ local ys = 'y\u00FD\u00FF\u0177\u01B4\u1E8F\u1E99\u1EF3' + '\u1EF5\u1EF7\u1EF9\u24B4\uFF59'; /* if the name is already qualified, don't add an article at all */ if (isQualifiedName) return str; /* if it's plural or a mass noun, use "some" as the article */ if (isPlural || isMassNoun) { /* use "some" as the article */ return 'some ' + str; } else { local firstChar; local firstCharLower; /* if it's empty, just use "a" */ if (inStr == '') return 'a'; /* get the first character of the name */ firstChar = inStr.substr(1, 1); /* skip any leading HTML tags */ if (rexMatch(patTagOrQuoteChar, firstChar) != nil) { /* * Scan for tags. Note that this pattern isn't quite * perfect, as it won't properly ignore close-brackets * that are inside quoted material, but it should be good * enough for nearly all cases in practice. In cases too * complex for this pattern, the object will simply have * to override aDesc. */ local len = rexMatch(patLeadingTagOrQuote, inStr); /* if we got a match, strip out the leading tags */ if (len != nil) { /* strip off the leading tags */ inStr = inStr.substr(len + 1); /* re-fetch the first character */ firstChar = inStr.substr(1, 1); } } /* get the lower-case version of the first character */ firstCharLower = firstChar.toLower(); /* * if the first word of the name is only one letter long, * treat it specially */ if (rexMatch(patOneLetterWord, inStr) != nil) { /* * We have a one-letter first word, such as "I-beam" or * "M-ray sensor", or just "A". Choose the article based * on the pronunciation of the letter as a letter. */ return (rexMatch(patOneLetterAnWord, inStr) != nil ? 'an ' : 'a ') + str; } /* * look for the first character in the lower-case and * upper-case-only vowel lists - if we find it, it takes * 'an' */ if (vowels.find(firstCharLower) != nil || vowelsUpperOnly.find(firstChar) != nil) { /* it starts with a vowel */ return 'an ' + str; } else if (ys.find(firstCharLower) != nil) { local secondChar; /* get the second character, if there is one */ secondChar = inStr.substr(2, 1); /* * It starts with 'y' - if the second letter is a * consonant, assume the 'y' is a vowel sound, hence we * should use 'an'; otherwise assume the 'y' is a * diphthong 'ei' sound, which means we should use 'a'. * If there's no second character at all, or the second * character isn't alphabetic, use 'a' - "a Y" or "a * Y-connector". */ if (secondChar == '' || rexMatch(patIsAlpha, secondChar) == nil || vowels.find(secondChar.toLower()) != nil || vowelsUpperOnly.find(secondChar) != nil) { /* * it's just one character, or the second character * is non-alphabetic, or the second character is a * vowel - in any of these cases, use 'a' */ return 'a ' + str; } else { /* the second character is a consonant - use 'an' */ return 'an ' + str; } } else if (rexMatch(patElevenEighteen, inStr) != nil) { /* * it starts with '11' or '18', so it takes 'an' ('an * 11th-hour appeal', 'an 18-hole golf course') */ return 'an ' + str; } else { /* it starts with a consonant */ return 'a ' + str; } } } /* pre-compile some regular expressions for aName */ patTagOrQuoteChar = static new RexPattern('[<"\']') patLeadingTagOrQuote = static new RexPattern( '(<^rangle>+|"|\')+') patOneLetterWord = static new RexPattern('(<^alpha>|$)') patOneLetterAnWord = static new RexPattern('[aefhilmnorsx]') patIsAlpha = static new RexPattern('') patElevenEighteen = static new RexPattern('1[18](<^digit>|$)') /* * Get the default plural name. By default, we'll use the * algorithmic plural determination, which is based on the spelling * of the name. * * The algorithm won't always get it right, since some English * plurals are irregular ("men", "children", "Attorneys General"). * When the name doesn't fit the regular spelling patterns for * plurals, the object should simply override this routine to return * the correct plural name string. */ pluralName = (pluralNameFrom(name)) /* * Get the plural form of the given name string. If the name ends in * anything other than 'y', we'll add an 's'; otherwise we'll replace * the 'y' with 'ies'. We also handle abbreviations and individual * letters specially. * * This can only deal with simple adjective-noun forms. For more * complicated forms, particularly for compound words, it must be * overridden (e.g., "Attorney General" -> "Attorneys General", * "man-of-war" -> "men-of-war"). Likewise, names with irregular * plurals ('child' -> 'children', 'man' -> 'men') must be handled * with overrides. */ pluralNameFrom(str) { local len; local lastChar; local lastPair; /* * if it's marked as having plural usage, just use the ordinary * name, since it's already plural */ if (isPlural) return str; /* check for a 'phrase of phrase' format */ if (rexMatch(patOfPhrase, str) != nil) { local ofSuffix; /* * Pull out the two parts - the part up to the 'of' is the * part we'll actually pluralize, and the rest is a suffix * we'll stick on the end of the pluralized part. */ str = rexGroup(1)[3]; ofSuffix = rexGroup(2)[3]; /* * now pluralize the part up to the 'of' using the normal * rules, then add the rest back in at the end */ return pluralNameFrom(str) + ofSuffix; } /* if there's no short description, return an empty string */ len = str.length(); if (len == 0) return ''; /* * If it's only one character long, handle it specially. If it's * a lower-case letter, add an apostrophe-S. If it's a capital * A, E, I, M, U, or V, we'll add apostrophe-S (because these * could be confused with words or common abbreviations if we * just added "s": As, Es, Is, Ms, Us, Vs). If it's anything * else (any other capital letter, or any non-letter character), * we'll just add an "s". */ if (len == 1) { if (rexMatch(patSingleApostropheS, str) != nil) return str + '’s'; else return str + 's'; } /* get the last character of the name, and the last pair of chars */ lastChar = str.substr(len, 1); lastPair = (len == 1 ? lastChar : str.substr(len - 1, 2)); /* * If the last letter is a capital letter, assume it's an * abbreviation without embedded periods (CPA, PC), in which case * we just add an "s" (CPAs, PCs). Likewise, if it's a number, * just add "s": "the 1940s", "the low 20s". */ if (rexMatch(patUpperOrDigit, lastChar) != nil) return str + 's'; /* * If the last character is a period, it must be an abbreviation * with embedded periods (B.A., B.S., Ph.D.). In these cases, * add an apostrophe-S. */ if (lastChar == '.') return str + '’s'; /* * If it ends in a non-vowel followed by 'y', change -y to -ies. * (This doesn't apply if a vowel precedes a terminal 'y'; in * such cases, we'll use the normal '-s' ending instead: "survey" * -> "surveys", "essay" -> "essays", "day" -> "days".) */ if (rexMatch(patVowelY, lastPair) != nil) return str.substr(1, len - 1) + 'ies'; /* if it ends in s, x, z, or h, add -es */ if ('sxzh'.find(lastChar) != nil) return str + 'es'; /* for anything else, just add -s */ return str + 's'; } /* some pre-compiled patterns for pluralName */ patSingleApostropheS = static new RexPattern('') patUpperOrDigit = static new RexPattern('') patVowelY = static new RexPattern('[^aeoiu]y') patOfPhrase = static new RexPattern( '(.+?)(+of+.+)') /* get my name plus a being verb ("the box is") */ nameIs { return theName + ' ' + verbToBe; } /* get my name plus a negative being verb ("the box isn't") */ nameIsnt { return nameIs + 'n’t'; } /* * My name with the given regular verb in agreement: in the present * tense, if my name has singular usage, we'll add 's' to the verb, * otherwise we won't. In the past tense, we'll add 'd' (or 'ed'). * This can't be used with irregular verbs, or with regular verbs * that have the last consonant repeated before the past -ed ending, * such as "deter". */ nameVerb(verb) { return theName + ' ' + conjugateRegularVerb(verb); } /* being verb agreeing with this object as subject */ verbToBe { return tSel(isPlural ? 'are' : 'is', isPlural ? 'were' : 'was'); } /* past tense being verb agreeing with object as subject */ verbWas { return tSel(isPlural ? 'were' : 'was', 'had been'); } /* 'have' verb agreeing with this object as subject */ verbToHave { return tSel(isPlural ? 'have' : 'has', 'had'); } /* * A few common irregular verbs and name-plus-verb constructs, * defined for convenience. */ verbToDo = (tSel('do' + verbEndingEs, 'did')) nameDoes = (theName + ' ' + verbToDo) verbToGo = (tSel('go' + verbEndingEs, 'went')) verbToCome = (tSel('come' + verbEndingS, 'came')) verbToLeave = (tSel('leave' + verbEndingS, 'left')) verbToSee = (tSel('see' + verbEndingS, 'saw')) nameSees = (theName + ' ' + verbToSee) verbToSay = (tSel('say' + verbEndingS, 'said')) nameSays = (theName + ' ' + verbToSay) verbMust = (tSel('must', 'had to')) verbCan = (tSel('can', 'could')) verbCannot = (tSel('cannot', 'could not')) verbCant = (tSel('can’t', 'couldn’t')) verbWill = (tSel('will', 'would')) verbWont = (tSel('won’t', 'wouldn’t')) /* * Verb endings for regular '-s' verbs, agreeing with this object as * the subject. We define several methods each of which handles the * past tense differently. * * verbEndingS doesn't try to handle the past tense at all - use it * only in places where you know for certain that you'll never need * the past tense form, or in expressions constructed with the tSel * macro: use verbEndingS as the macro's first argument, and specify * the past tense ending explicitly as the second argument. For * example, you could generate the correctly conjugated form of the * verb "to fit" for an object named "key" with an expression such * as: * * 'fit' + tSel(key.verbEndingS, 'ted') * * This would generate 'fit', 'fits', or 'fitted' according to number * and tense. * * verbEndingSD and verbEndingSEd return 'd' and 'ed' respectively in * the past tense. * * verbEndingSMessageBuilder_ is for internal use only: it assumes * that the correct ending to be displayed in the past tense is * stored in langMessageBuilder.pastEnding_. It is used as part of * the string parameter substitution mechanism. */ verbEndingS { return isPlural ? '' : 's'; } verbEndingSD = (tSel(verbEndingS, 'd')) verbEndingSEd = (tSel(verbEndingS, 'ed')) verbEndingSMessageBuilder_ = (tSel(verbEndingS, langMessageBuilder.pastEnding_)) /* * Verb endings (present or past) for regular '-es/-ed' and * '-y/-ies/-ied' verbs, agreeing with this object as the subject. */ verbEndingEs { return tSel(isPlural ? '' : 'es', 'ed'); } verbEndingIes { return tSel(isPlural ? 'y' : 'ies', 'ied'); } /* * Dummy name - this simply displays nothing; it's used for cases * where messageBuilder substitutions want to refer to an object (for * internal bookkeeping) without actually showing the name of the * object in the output text. This should always simply return an * empty string. */ dummyName = '' /* * Invoke a property (with an optional argument list) on this object * while temporarily switching to the present tense, and return the * result. */ propWithPresent(prop, [args]) { return withPresent({: self.(prop)(args...)}); } /* * Method for internal use only: invoke on this object the property * stored in langMessageBuilder.fixedTenseProp_ while temporarily * switching to the present tense, and return the result. This is * used as part of the string parameter substitution mechanism. */ propWithPresentMessageBuilder_ { return propWithPresent(langMessageBuilder.fixedTenseProp_); } /* * For the most part, "strike" has the same meaning as "hit", so * define this as a synonym for "attack" most objects. There are a * few English idioms where "strike" means something different, as * in "strike match" or "strike tent." */ dobjFor(Strike) asDobjFor(Attack) ; /* ------------------------------------------------------------------------ */ /* * An object that uses the same name as another object. This maps all of * the properties involved in supplying the object's name, number, and * other usage information from this object to a given target object, so * that all messages involving this object use the same name as the * target object. This is a mix-in class that can be used with any other * class. * * Note that we map only the *reported* name for the object. We do NOT * give this object any vocabulary from the other object; in other words, * we don't enter this object into the dictionary with the other object's * vocabulary words. */ class NameAsOther: object /* the target object - we'll use the same name as this object */ targetObj = nil /* map our naming and usage properties to the target object */ isPlural = (targetObj.isPlural) isMassNoun = (targetObj.isMassNoun) isHim = (targetObj.isHim) isHer = (targetObj.isHer) isIt = (targetObj.isIt) isProperName = (targetObj.isProperName) isQualifiedName = (targetObj.isQualifiedName) name = (targetObj.name) /* map the derived name properties as well, in case any are overridden */ disambigName = (targetObj.disambigName) theDisambigName = (targetObj.theDisambigName) aDisambigName = (targetObj.aDisambigName) countDisambigName(cnt) { return targetObj.countDisambigName(cnt); } disambigEquivName = (targetObj.disambigEquivName) listName = (targetObj.listName) countName(cnt) { return targetObj.countName(cnt); } /* map the pronoun properites, in case any are overridden */ itNom = (targetObj.itNom) itObj = (targetObj.itObj) itPossAdj = (targetObj.itPossAdj) itPossNoun = (targetObj.itPossNoun) itReflexive = (targetObj.itReflexive) thatNom = (targetObj.thatNom) thatObj = (targetObj.thatObj) thatIsContraction = (targetObj.thatIsContraction) itIs = (targetObj.itIs) itIsContraction = (targetObj.itIsContraction) itVerb(verb) { return targetObj.itVerb(verb); } conjugateRegularVerb(verb) { return targetObj.conjugateRegularVerb(verb); } theName = (targetObj.theName) theNameObj = (targetObj.theNameObj) theNamePossAdj = (targetObj.theNamePossAdj) theNamePossNoun = (targetObj.theNamePossNoun) theNameWithOwner = (targetObj.theNameWithOwner) aNameOwnerLoc(ownerPri) { return targetObj.aNameOwnerLoc(ownerPri); } theNameOwnerLoc(ownerPri) { return targetObj.theNameOwnerLoc(ownerPri); } countNameOwnerLoc(cnt, ownerPri) { return targetObj.countNameOwnerLoc(cnt, ownerPri); } notePromptByOwnerLoc(ownerPri) { targetObj.notePromptByOwnerLoc(ownerPri); } notePromptByPossAdj() { targetObj.notePromptByPossAdj(); } aName = (targetObj.aName) aNameObj = (targetObj.aNameObj) pluralName = (targetObj.pluralName) nameIs = (targetObj.nameIs) nameIsnt = (targetObj.nameIsnt) nameVerb(verb) { return targetObj.nameVerb(verb); } verbToBe = (targetObj.verbToBe) verbWas = (targetObj.verbWas) verbToHave = (targetObj.verbToHave) verbToDo = (targetObj.verbToDo) nameDoes = (targetObj.nameDoes) verbToGo = (targetObj.verbToGo) verbToCome = (targetObj.verbToCome) verbToLeave = (targetObj.verbToLeave) verbToSee = (targetObj.verbToSee) nameSees = (targetObj.nameSees) verbToSay = (targetObj.verbToSay) nameSays = (targetObj.nameSays) verbMust = (targetObj.verbMust) verbCan = (targetObj.verbCan) verbCannot = (targetObj.verbCannot) verbCant = (targetObj.verbCant) verbWill = (targetObj.verbWill) verbWont = (targetObj.verbWont) verbEndingS = (targetObj.verbEndingS) verbEndingSD = (targetObj.verbEndingSD) verbEndingSEd = (targetObj.verbEndingSEd) verbEndingEs = (targetObj.verbEndingEs) verbEndingIes = (targetObj.verbEndingIes) ; /* * Name as Parent - this is a special case of NameAsOther that uses the * lexical parent of a nested object as the target object. (The lexical * parent is the enclosing object in a nested object definition; in other * words, it's the object in which the nested object is embedded.) */ class NameAsParent: NameAsOther targetObj = (lexicalParent) ; /* * ChildNameAsOther is a mix-in class that can be used with NameAsOther * to add the various childInXxx naming to the mapped properties. The * childInXxx names are the names generated when another object is * described as located within this object; by mapping these properties * to our target object, we ensure that we use exactly the same phrasing * as we would if the contained object were actually contained by our * target rather than by us. * * Note that this should always be used in combination with NameAsOther: * * myObj: NameAsOther, ChildNameAsOther, Thing ... * * You can also use it the same way in combination with a subclass of * NameAsOther, such as NameAsParent. */ class ChildNameAsOther: object objInPrep = (targetObj.objInPrep) actorInPrep = (targetObj.actorInPrep) actorOutOfPrep = (targetObj.actorOutOfPrep) actorIntoPrep = (targetObj.actorIntoPrep) childInName(childName) { return targetObj.childInName(childName); } childInNameWithOwner(childName) { return targetObj.childInNameWithOwner(childName); } childInNameGen(childName, myName) { return targetObj.childInNameGen(childName, myName); } actorInName = (targetObj.actorInName) actorOutOfName = (targetObj.actorOutOfName) actorIntoName = (targetObj.actorIntoName) actorInAName = (targetObj.actorInAName) ; /* ------------------------------------------------------------------------ */ /* * Language modifications for the specialized container types */ modify Surface /* * objects contained in a Surface are described as being on the * Surface */ objInPrep = 'on' actorInPrep = 'on' actorOutOfPrep = 'off of' ; modify Underside objInPrep = 'under' actorInPrep = 'under' actorOutOfPrep = 'from under' ; modify RearContainer objInPrep = 'behind' actorInPrep = 'behind' actorOutOfPrep = 'from behind' ; /* ------------------------------------------------------------------------ */ /* * Language modifications for Actor. * * An Actor has a "referral person" setting, which determines how we * refer to the actor; this is almost exclusively for the use of the * player character. The typical convention is that we refer to the * player character in the second person, but a game can override this on * an actor-by-actor basis. */ modify Actor /* by default, use my pronoun for my name */ name = (itNom) /* * Pronoun selector. This returns an index for selecting pronouns * or other words based on number and gender, taking into account * person, number, and gender. The value returned is the sum of the * following components: * * number/gender: *. - singular neuter = 1 *. - singular masculine = 2 *. - singular feminine = 3 *. - plural = 4 * * person: *. - first person = 0 *. - second person = 4 *. - third person = 8 * * The result can be used as a list selector as follows (1=first * person, etc; s=singular, p=plural; n=neuter, m=masculine, * f=feminine): * * [1/s/n, 1/s/m, 1/s/f, 1/p, 2/s/n, 2/s/m, 2/s/f, 2/p, *. 3/s/n, 3/s/m, 3/s/f, 3/p] */ pronounSelector { return ((referralPerson - FirstPerson)*4 + (isPlural ? 4 : isHim ? 2 : isHer ? 3 : 1)); } /* * get the verb form selector index for the person and number: * * [1/s, 2/s, 3/s, 1/p, 2/p, 3/p] */ conjugationSelector { return (referralPerson + (isPlural ? 3 : 0)); } /* * get an appropriate pronoun for the object in the appropriate * person for the nominative case, objective case, possessive * adjective, possessive noun, and objective reflexive */ itNom { return ['I', 'I', 'I', 'we', 'you', 'you', 'you', 'you', 'it', 'he', 'she', 'they'][pronounSelector]; } itObj { return ['me', 'me', 'me', 'us', 'you', 'you', 'you', 'you', 'it', 'him', 'her', 'them'][pronounSelector]; } itPossAdj { return ['my', 'my', 'my', 'our', 'your', 'your', 'your', 'your', 'its', 'his', 'her', 'their'][pronounSelector]; } itPossNoun { return ['mine', 'mine', 'mine', 'ours', 'yours', 'yours', 'yours', 'yours', 'its', 'his', 'hers', 'theirs'][pronounSelector]; } itReflexive { return ['myself', 'myself', 'myself', 'ourselves', 'yourself', 'yourself', 'yourself', 'yourselves', 'itself', 'himself', 'herself', 'themselves'][pronounSelector]; } /* * Demonstrative pronoun, nominative case. We'll use personal a * personal pronoun if we have a gender or we're in the first or * second person, otherwise we'll use 'that' or 'those' as we would * for an inanimate object. */ thatNom { return ['I', 'I', 'I', 'we', 'you', 'you', 'you', 'you', 'that', 'he', 'she', 'those'][pronounSelector]; } /* demonstrative pronoun, objective case */ thatObj { return ['me', 'me', 'me', 'us', 'you', 'you', 'you', 'you', 'that', 'him', 'her', 'those'][pronounSelector]; } /* demonstrative pronoun, nominative case with 'is' contraction */ thatIsContraction { return thatNom + tSel(['’m', '’re', '’s', '’re', '’re', ' are'][conjugationSelector], ' ' + verbToBe); } /* * We don't need to override itIs: the base class handling works for * actors too. */ /* get my pronoun with a being verb contraction ("the box's") */ itIsContraction { return itNom + tSel( '’' + ['m', 're', 's', 're', 're', 're'][conjugationSelector], ' ' + verbToBe); } /* * Conjugate a regular verb in the present or past tense for our * person and number. * * In the present tense, this is pretty easy: we add an 's' for the * third person singular, and leave the verb unchanged for every * other case. The only complication is that we must check some * special cases to add the -s suffix: -y -> -ies, -o -> -oes. * * In the past tense, we use the inherited handling since the past * tense ending doesn't vary with person. */ conjugateRegularVerb(verb) { /* * If we're in the third person or if we use the past tense, * inherit the default handling; otherwise, use the base verb * form regardless of number (regular verbs use the same * conjugated forms for every case but third person singular: I * ask, you ask, we ask, they ask). */ if (referralPerson != ThirdPerson && !gameMain.usePastTense) { /* * we're not using the third-person or the past tense, so the * conjugation is the same as the base verb form */ return verb; } else { /* * we're using the third person or the past tense, so inherit * the base class handling, which conjugates these forms */ return inherited(verb); } } /* * Get the name with a definite article ("the box"). If the * narrator refers to us in the first or second person, use a * pronoun rather than the short description. */ theName { return (referralPerson == ThirdPerson ? inherited : itNom); } /* theName in objective case */ theNameObj { return (referralPerson == ThirdPerson ? inherited : itObj); } /* theName as a possessive adjective */ theNamePossAdj { return (referralPerson == ThirdPerson ? inherited : itPossAdj); } /* theName as a possessive noun */ theNamePossNoun { return (referralPerson == ThirdPerson ? inherited : itPossNoun); } /* * Get the name with an indefinite article. Use the same rules of * referral person as for definite articles. */ aName { return (referralPerson == ThirdPerson ? inherited : itNom); } /* aName in objective case */ aNameObj { return (referralPerson == ThirdPerson ? inherited : itObj); } /* being verb agreeing with this object as subject */ verbToBe { return tSel(['am', 'are', 'is', 'are', 'are', 'are'], ['was', 'were', 'was', 'were', 'were', 'were']) [conjugationSelector]; } /* past tense being verb agreeing with this object as subject */ verbWas { return tSel(['was', 'were', 'was', 'were', 'were', 'were'] [conjugationSelector], 'had been'); } /* 'have' verb agreeing with this object as subject */ verbToHave { return tSel(['have', 'have', 'has', 'have', 'have', 'have'] [conjugationSelector], 'had'); } /* * verb endings for regular '-s' and '-es' verbs, agreeing with this * object as the subject */ verbEndingS { return ['', '', 's', '', '', ''][conjugationSelector]; } verbEndingEs { return tSel(['', '', 'es', '', '', ''][conjugationSelector], 'ed'); } verbEndingIes { return tSel(['y', 'y', 'ies', 'y', 'y', 'y'][conjugationSelector], 'ied'); } /* "I'm not" doesn't fit the regular "+n't" rule */ nameIsnt { return conjugationSelector == 1 && !gameMain.usePastTense ? 'I’m not' : inherited; } /* * Show my name for an arrival/departure message. If we've been seen * before by the player character, we'll show our definite name, * otherwise our indefinite name. */ travelerName(arriving) { say(gPlayerChar.hasSeen(self) ? theName : aName); } /* * Test to see if we can match the third-person pronouns. We'll * match these if our inherited test says we match them AND we can * be referred to in the third person. */ canMatchHim = (inherited && canMatch3rdPerson) canMatchHer = (inherited && canMatch3rdPerson) canMatchIt = (inherited && canMatch3rdPerson) canMatchThem = (inherited && canMatch3rdPerson) /* * Test to see if we can match a third-person pronoun ('it', 'him', * 'her', 'them'). We can unless we're the player character and the * player character is referred to in the first or second person. */ canMatch3rdPerson = (!isPlayerChar || referralPerson == ThirdPerson) /* * Set a pronoun antecedent to the given list of ResolveInfo objects. * Pronoun handling is language-specific, so this implementation is * part of the English library, not the generic library. * * If only one object is present, we'll set the object to be the * antecedent of 'it', 'him', or 'her', according to the object's * gender. We'll also set the object as the single antecedent for * 'them'. * * If we have multiple objects present, we'll set the list to be the * antecedent of 'them', and we'll forget about any antecedent for * 'it'. * * Note that the input is a list of ResolveInfo objects, so we must * pull out the underlying game objects when setting the antecedents. */ setPronoun(lst) { /* if the list is empty, ignore it */ if (lst == []) return; /* * if we have multiple objects, the entire list is the antecedent * for 'them'; otherwise, it's a singular antecedent which * depends on its gender */ if (lst.length() > 1) { local objs = lst.mapAll({x: x.obj_}); /* it's 'them' */ setThem(objs); /* forget any 'it' */ setIt(nil); } else if (lst.length() == 1) { /* * We have only one object, so set it as an antecedent * according to its gender. */ setPronounObj(lst[1].obj_); } } /* * Set a pronoun to refer to multiple potential antecedents. This is * used when the verb has multiple noun slots - UNLOCK DOOR WITH KEY. * For verbs like this, we have no way of knowing in advance whether * a future pronoun will refer back to the direct object or the * indirect object (etc) - we could just assume that 'it' will refer * to the direct object, but this won't always be what the player * intended. In natural English, pronoun antecedents must often be * inferred from context at the time of use - so we use the same * approach. * * Pass an argument list consisting of ResolveInfo lists - that is, * pass one argument per noun slot in the verb, and make each * argument a list of ResolveInfo objects. In other words, you call * this just as you would setPronoun(), except you can pass more than * one list argument. * * We'll store the multiple objects as antecedents. When we need to * resolve a future singular pronoun, we'll figure out which of the * multiple antecedents is the most logical choice in the context of * the pronoun's usage. */ setPronounMulti([args]) { local lst, subLst; local gotThem; /* * If there's a plural list, it's 'them'. Arbitrarily use only * the first plural list if there's more than one. */ if ((lst = args.valWhich({x: x.length() > 1})) != nil) { /* set 'them' to the plural list */ setPronoun(lst); /* note that we had a clear 'them' */ gotThem = true; } /* from now on, consider only the sublists with exactly one item */ args = args.subset({x: x.length() == 1}); /* get a list of the singular items from the lists */ lst = args.mapAll({x: x[1].obj_}); /* * Set 'it' to all of the items that can match 'it'; do likewise * with 'him' and 'her'. If there are no objects that can match * a given pronoun, leave that pronoun unchanged. */ if ((subLst = lst.subset({x: x.canMatchIt})).length() > 0) setIt(subLst); if ((subLst = lst.subset({x: x.canMatchHim})).length() > 0) setHim(subLst); if ((subLst = lst.subset({x: x.canMatchHer})).length() > 0) setHer(subLst); /* * set 'them' to the potential 'them' matches, if we didn't * already find a clear plural list */ if (!gotThem && (subLst = lst.subset({x: x.canMatchThem})).length() > 0) setThem(subLst); } /* * Set a pronoun antecedent to the given ResolveInfo list, for the * specified type of pronoun. We don't have to worry about setting * other types of pronouns to this antecedent - we specifically want * to set the given pronoun type. This is language-dependent * because we still have to figure out the number (i.e. singular or * plural) of the pronoun type. */ setPronounByType(typ, lst) { /* check for singular or plural pronouns */ if (typ == PronounThem) { /* it's plural - set a list antecedent */ setPronounAntecedent(typ, lst.mapAll({x: x.obj_})); } else { /* it's singular - set an individual antecedent */ setPronounAntecedent(typ, lst[1].obj_); } } /* * Set a pronoun antecedent to the given simulation object (usually * an object descended from Thing). */ setPronounObj(obj) { /* * Actually use the object's "identity object" as the antecedent * rather than the object itself. In some cases, we use multiple * program objects to represent what appears to be a single * object in the game; in these cases, the internal program * objects all point to the "real" object as their identity * object. Whenever we're manipulating one of these internal * program objects, we want to make sure that its the * player-visible object - the identity object - that appears as * the antecedent for subsequent references. */ obj = obj.getIdentityObject(); /* * Set the appropriate pronoun antecedent, depending on the * object's gender. * * Note that we'll set an object to be the antecedent for both * 'him' and 'her' if the object has both masculine and feminine * usage. */ /* check for masculine usage */ if (obj.canMatchHim) setHim(obj); /* check for feminine usage */ if (obj.canMatchHer) setHer(obj); /* check for neuter usage */ if (obj.canMatchIt) setIt(obj); /* check for third-person plural usage */ if (obj.canMatchThem) setThem([obj]); } /* set a possessive anaphor */ setPossAnaphorObj(obj) { /* check for each type of usage */ if (obj.canMatchHim) possAnaphorTable[PronounHim] = obj; if (obj.canMatchHer) possAnaphorTable[PronounHer] = obj; if (obj.canMatchIt) possAnaphorTable[PronounIt] = obj; if (obj.canMatchThem) possAnaphorTable[PronounThem] = [obj]; } ; /* ------------------------------------------------------------------------ */ /* * Give the postures some additional attributes */ modify Posture /* * Intransitive and transitive forms of the verb, for use in library * messages. Each of these methods simply calls one of the two * corresponding fixed-tense properties, depending on the current * tense. */ msgVerbI = (tSel(msgVerbIPresent, msgVerbIPast)) msgVerbT = (tSel(msgVerbTPresent, msgVerbTPast)) /* * Fixed-tense versions of the above properties, to be defined * individually by each instance of the Posture class. */ /* our present-tense intransitive form ("he stands up") */ // msgVerbIPresent = 'stand{s} up' /* our past-tense intransitive form ("he stood up") */ // msgVerbIPast = 'stood up' /* our present-tense transitive form ("he stands on the chair") */ // msgVerbTPresent = 'stand{s}' /* our past-tense transitive form ("he stood on the chair") */ // msgVerbTPast = 'stood' /* our participle form */ // participle = 'standing' ; modify standing msgVerbIPresent = 'stand{s} up' msgVerbIPast = 'stood up' msgVerbTPresent = 'stand{s}' msgVerbTPast = 'stood' participle = 'standing' ; modify sitting msgVerbIPresent = 'sit{s} down' msgVerbIPast = 'sat down' msgVerbTPresent = 'sit{s}' msgVerbTPast = 'sat' participle = 'sitting' ; modify lying msgVerbIPresent = 'lie{s} down' msgVerbIPast = 'lay down' msgVerbTPresent = 'lie{s}' msgVerbTPast = 'lay' participle = 'lying' ; /* ------------------------------------------------------------------------ */ /* * For our various topic suggestion types, we can infer the full name * from the short name fairly easily. */ modify SuggestedAskTopic fullName = ('ask {it targetActor/him} about ' + name) ; modify SuggestedTellTopic fullName = ('tell {it targetActor/him} about ' + name) ; modify SuggestedAskForTopic fullName = ('ask {it targetActor/him} for ' + name) ; modify SuggestedGiveTopic fullName = ('give {it targetActor/him} ' + name) ; modify SuggestedShowTopic fullName = ('show {it targetActor/him} ' + name) ; modify SuggestedYesTopic name = 'yes' fullName = 'say yes' ; modify SuggestedNoTopic name = 'no' fullName = 'say no' ; /* ------------------------------------------------------------------------ */ /* * Provide custom processing of the player input for matching * SpecialTopic patterns. When we're trying to match a player's command * to a set of active special topics, we'll run the input through this * processing to produce the string that we actually match against the * special topics. * * First, we'll remove any punctuation marks. This ensures that we'll * still match a special topic, for example, if the player puts a period * or a question mark at the end of the command. * * Second, if the user's input starts with "A" or "T" (the super-short * forms of the ASK ABOUT and TELL ABOUT commands), remove the "A" or "T" * and keep the rest of the input. Some users might think that special * topic suggestions are meant as ask/tell topics, so they might * instinctively try these as A/T commands. * * Users *probably* won't be tempted to do the same thing with the full * forms of the commands (e.g., ASK BOB ABOUT APOLOGIZE, TELL BOB ABOUT * EXPLAIN). It's more a matter of habit of using A or T for interaction * that would tempt a user to phrase a special topic this way; once * you're typing out the full form of the command, it generally won't be * grammatical, as special topics generally contain the sense of a verb * in their phrasing. */ modify specialTopicPreParser processInputStr(str) { /* * remove most punctuation from the string - we generally want to * ignore these, as we mostly just want to match keywords */ str = rexReplace(punctPat, str, '', ReplaceAll); /* if it starts with "A" or "T", strip off the leading verb */ if (rexMatch(aOrTPat, str) != nil) str = rexGroup(1)[3]; /* return the processed result */ return str; } /* pattern for string starting with "A" or "T" verbs */ aOrTPat = static new RexPattern( '*[at]+(<^space>.*)$') /* pattern to eliminate punctuation marks from the string */ punctPat = static new RexPattern('[.?!,;:]'); ; /* * For SpecialTopic matches, treat some strings as "weak": if the user's * input consists of just one of these weak strings and nothing else, * don't match the topic. */ modify SpecialTopic matchPreParse(str, procStr) { /* if it's one of our 'weak' strings, don't match */ if (rexMatch(weakPat, str) != nil) return nil; /* it's not a weak string, so match as usual */ return inherited(str, procStr); } /* * Our "weak" strings - 'i', 'l', 'look': these are weak because a * user typing one of these strings by itself is probably actually * trying to enter the command of the same name, rather than entering * a special topic. These come up in cases where the special topic * is something like "say I don't know" or "tell him you'll look into * it". */ weakPat = static new RexPattern('*(i|l|look)*$') ; /* ------------------------------------------------------------------------ */ /* * English-specific Traveler changes */ modify Traveler /* * Get my location's name, from the PC's perspective, for describing * my arrival to or departure from my current location. We'll * simply return our location's destName, or "the area" if it * doesn't have one. */ travelerLocName() { /* get our location's name from the PC's perspective */ local nm = location.getDestName(gPlayerChar, gPlayerChar.location); /* if there's a name, return it; otherwise, use "the area" */ return (nm != nil ? nm : 'the area'); } /* * Get my "remote" location name, from the PC's perspective. This * returns my location name, but only if my location is remote from * the PC's perspective - that is, my location has to be outside of * the PC's top-level room. If we're within the PC's top-level * room, we'll simply return an empty string. */ travelerRemoteLocName() { /* * if my location is outside of the PC's outermost room, we're * remote, so return my location name; otherwise, we're local, * so we don't need a remote name at all */ if (isIn(gPlayerChar.getOutermostRoom())) return ''; else return travelerLocName; } ; /* ------------------------------------------------------------------------ */ /* * English-specific Vehicle changes */ modify Vehicle /* * Display the name of the traveler, for use in an arrival or * departure message. */ travelerName(arriving) { /* * By default, start with the indefinite name if we're arriving, * or the definite name if we're leaving. * * If we're leaving, presumably they've seen us before, since we * were already in the room to start with. Since we've been * seen before, the definite is appropriate. * * If we're arriving, even if we're not being seen for the first * time, we haven't been seen yet in this place around this * time, so the indefinite is appropriate. */ say(arriving ? aName : theName); /* show the list of actors aboard */ aboardVehicleListerObj.showList( libGlobal.playerChar, nil, allContents(), 0, 0, libGlobal.playerChar.visibleInfoTable(), nil); } ; /* ------------------------------------------------------------------------ */ /* * English-specific PushTraveler changes */ modify PushTraveler /* * When an actor is pushing an object from one room to another, show * its name with an additional clause indicating the object being * moved along with us. */ travelerName(arriving) { "<>, pushing <>,"; } ; /* ------------------------------------------------------------------------ */ /* * English-specific travel connector changes */ modify PathPassage /* treat "take path" the same as "enter path" or "go through path" */ dobjFor(Take) maybeRemapTo( gAction.getEnteredVerbPhrase() == 'take (dobj)', TravelVia, self) dobjFor(Enter) { verify() { logicalRank(50, 'enter path'); } } dobjFor(GoThrough) { verify() { logicalRank(50, 'enter path'); } } ; modify AskConnector /* * This is the noun phrase we'll use when asking disambiguation * questions for this travel connector: "Which *one* do you want to * enter..." */ travelObjsPhrase = 'one' ; /* ------------------------------------------------------------------------ */ /* * English-specific changes for various nested room types. */ modify BasicChair /* by default, one sits *on* a chair */ objInPrep = 'on' actorInPrep = 'on' actorOutOfPrep = 'off of' ; modify BasicPlatform /* by default, one stands *on* a platform */ objInPrep = 'on' actorInPrep = 'on' actorOutOfPrep = 'off of' ; modify Booth /* by default, one is *in* a booth */ objInPrep = 'in' actorInPrep = 'in' actorOutOfPrep = 'out of' ; /* ------------------------------------------------------------------------ */ /* * Language modifications for Matchstick */ modify Matchstick /* "strike match" means "light match" */ dobjFor(Strike) asDobjFor(Burn) /* "light match" means "burn match" */ dobjFor(Light) asDobjFor(Burn) ; /* * Match state objects. We show "lit" as the state for a lit match, * nothing for an unlit match. */ matchStateLit: ThingState 'lit' stateTokens = ['lit'] ; matchStateUnlit: ThingState stateTokens = ['unlit'] ; /* ------------------------------------------------------------------------ */ /* * English-specific modifications for Room. */ modify Room /* * The ordinary 'name' property is used the same way it's used for * any other object, to refer to the room when it shows up in * library messages and the like: "You can't take the hallway." * * By default, we derive the name from the roomName by converting * the roomName to lower case. Virtually every room will need a * custom room name setting, since the room name is used mostly as a * title for the room, and good titles are hard to generate * mechanically. Many times, converting the roomName to lower case * will produce a decent name to use in messages: "Ice Cave" gives * us "You can't eat the ice cave." However, games will want to * customize the ordinary name separately in many cases, because the * elliptical, title-like format of the room name doesn't always * translate well to an object name: "West of Statue" gives us the * unworkable "You can't eat the west of statue"; better to make the * ordinary name something like "plaza". Note also that some rooms * have proper names that want to preserve their capitalization in * the ordinary name: "You can't eat the Hall of the Ancient Kings." * These cases need to be customized as well. */ name = (roomName.toLower()) /* * The "destination name" of the room. This is primarily intended * for use in showing exit listings, to describe the destination of * a travel connector leading away from our current location, if the * destination is known to the player character. We also use this * as the default source of the name in similar contexts, such as * when we can see this room from another room connected by a sense * connector. * * The destination name usually mirrors the room name, but we use * the name in prepositional phrases involving the room ("east, to * the alley"), so this name should include a leading article * (usually definite - "the") unless the name is proper ("east, to * Dinsley Plaza"). So, by default, we simply use the "theName" of * the room. In many cases, it's better to specify a custom * destName, because this name is used when the PC is outside of the * room, and thus can benefit from a more detailed description than * we'd normally use for the basic name. For example, the ordinary * name might simply be something like "hallway", but since we want * to be clear about exactly which hallway we're talking about when * we're elsewhere, we might want to use a destName like "the * basement hallway" or "the hallway outside the operating room". */ destName = (theName) /* * For top-level rooms, describe an object as being in the room by * describing it as being in the room's nominal drop destination, * since that's the nominal location for the objects directly in the * room. (In most cases, the nominal drop destination for a room is * its floor.) * * If the player character isn't in the same outermost room as this * container, use our remote name instead of the nominal drop * destination. The nominal drop destination is usually something * like the floor or the ground, so it's only suitable when we're in * the same location as what we're describing. */ childInName(childName) { /* if the PC isn't inside us, we're viewing this remotely */ if (!gPlayerChar.isIn(self)) return childInRemoteName(childName, gPlayerChar); else return getNominalDropDestination().childInName(childName); } childInNameWithOwner(chiName) { /* if the PC isn't inside us, we're viewing this remotely */ if (!gPlayerChar.isIn(self)) return inherited(chiName); else return getNominalDropDestination().childInNameWithOwner(chiName); } ; /* ------------------------------------------------------------------------ */ /* * English-specific modifications for the default room parts. */ modify Floor childInNameGen(childName, myName) { return childName + ' on ' + myName; } objInPrep = 'on' actorInPrep = 'on' actorOutOfPrep = 'off of' ; modify defaultFloor noun = 'floor' 'ground' name = 'floor' ; modify defaultGround noun = 'ground' 'floor' name = 'ground' ; modify DefaultWall noun='wall' plural='walls' name='wall'; modify defaultCeiling noun='ceiling' 'roof' name='ceiling'; modify defaultNorthWall adjective='n' 'north' name='north wall'; modify defaultSouthWall adjective='s' 'south' name='south wall'; modify defaultEastWall adjective='e' 'east' name='east wall'; modify defaultWestWall adjective='w' 'west' name='west wall'; modify defaultSky noun='sky' name='sky'; /* ------------------------------------------------------------------------ */ /* * The English-specific modifications for directions. */ modify Direction /* describe a traveler arriving from this direction */ sayArriving(traveler) { /* show the generic arrival message */ gLibMessages.sayArriving(traveler); } /* describe a traveler departing in this direction */ sayDeparting(traveler) { /* show the generic departure message */ gLibMessages.sayDeparting(traveler); } ; /* * The English-specific modifications for compass directions. */ modify CompassDirection /* describe a traveler arriving from this direction */ sayArriving(traveler) { /* show the generic compass direction description */ gLibMessages.sayArrivingDir(traveler, name); } /* describe a traveler departing in this direction */ sayDeparting(traveler) { /* show the generic compass direction description */ gLibMessages.sayDepartingDir(traveler, name); } ; /* * The English-specific definitions for the compass direction objects. * In addition to modifying the direction objects to define the name of * the direction, we add a 'directionName' grammar rule. */ #define DefineLangDir(root, dirNames, backPre) \ grammar directionName(root): dirNames: DirectionProd \ dir = root##Direction \ ; \ \ modify root##Direction \ name = #@root \ backToPrefix = backPre DefineLangDir(north, 'north' | 'n', 'back to the'); DefineLangDir(south, 'south' | 's', 'back to the'); DefineLangDir(east, 'east' | 'e', 'back to the'); DefineLangDir(west, 'west' | 'w', 'back to the'); DefineLangDir(northeast, 'northeast' | 'ne', 'back to the'); DefineLangDir(northwest, 'northwest' | 'nw', 'back to the'); DefineLangDir(southeast, 'southeast' | 'se', 'back to the'); DefineLangDir(southwest, 'southwest' | 'sw', 'back to the'); DefineLangDir(up, 'up' | 'u', 'back'); DefineLangDir(down, 'down' | 'd', 'back'); DefineLangDir(in, 'in', 'back'); DefineLangDir(out, 'out', 'back'); /* * The English-specific shipboard direction modifications. Certain of * the ship directions have no natural descriptions for arrival and/or * departure; for example, there's no good way to say "arriving from * fore." Others don't fit any regular pattern: "he goes aft" rather * than "he departs to aft." As a result, these are a bit irregular * compared to the compass directions and so are individually defined * below. */ DefineLangDir(port, 'port' | 'p', 'back to') sayArriving(trav) { gLibMessages.sayArrivingShipDir(trav, 'the port direction'); } sayDeparting(trav) { gLibMessages.sayDepartingShipDir(trav, 'port'); } ; DefineLangDir(starboard, 'starboard' | 'sb', 'back to') sayArriving(trav) { gLibMessages.sayArrivingShipDir(trav, 'starboard'); } sayDeparting(trav) { gLibMessages.sayDepartingShipDir(trav, 'starboard'); } ; DefineLangDir(aft, 'aft' | 'a', 'back to') sayArriving(trav) { gLibMessages.sayArrivingShipDir(trav, 'aft'); } sayDeparting(trav) { gLibMessages.sayDepartingAft(trav); } ; DefineLangDir(fore, 'fore' | 'forward' | 'f', 'back to') sayArriving(trav) { gLibMessages.sayArrivingShipDir(trav, 'forward'); } sayDeparting(trav) { gLibMessages.sayDepartingFore(trav); } ; /* ------------------------------------------------------------------------ */ /* * Some helper routines for the library messages. */ class MessageHelper: object /* * Show a list of objects for a disambiguation query. If * 'showIndefCounts' is true, we'll show the number of equivalent * items for each equivalent item; otherwise, we'll just show an * indefinite noun phrase for each equivalent item. */ askDisambigList(matchList, fullMatchList, showIndefCounts, dist) { /* show each item */ for (local i = 1, local len = matchList.length() ; i <= len ; ++i) { local equivCnt; local obj; /* get the current object */ obj = matchList[i].obj_; /* * if this isn't the first, add a comma; if this is the * last, add an "or" as well */ if (i == len) ", or "; else if (i != 1) ", "; /* * Check to see if more than one equivalent of this item * appears in the full list. */ for (equivCnt = 0, local j = 1, local fullLen = fullMatchList.length() ; j <= fullLen ; ++j) { /* * if this item is equivalent for the purposes of the * current distinguisher, count it */ if (!dist.canDistinguish(obj, fullMatchList[j].obj_)) { /* it's equivalent - count it */ ++equivCnt; } } /* show this item with the appropriate article */ if (equivCnt > 1) { /* * we have multiple equivalents - show either with an * indefinite article or with a count, depending on the * flags the caller provided */ if (showIndefCounts) { /* a count is desired for each equivalent group */ say(dist.countName(obj, equivCnt)); } else { /* no counts desired - show with an indefinite article */ say(dist.aName(obj)); } } else { /* there's only one - show with a definite article */ say(dist.theName(obj)); } } } /* * For a TAction result, select the short-form or long-form message, * according to the disambiguation status of the action. This is for * the ultra-terse default messages, such as "Taken" or "Dropped", * that sometimes need more descriptive variations. * * If there was no disambiguation involved, we'll use the short * version of the message. * * If there was unclear disambiguation involved (meaning that there * was more than one logical object matching a noun phrase, but the * parser was able to decide based on likelihood rankings), we'll * still use the short version, because we assume that the parser * will have generated a parenthetical announcement to point out its * choice. * * If there was clear disambiguation involved (meaning that more than * one in-scope object matched a noun phrase, but there was only one * choice that passed the logicalness tests), AND the announcement * mode (in gameMain.ambigAnnounceMode) is DescribeClear, we'll * choose the long-form message. */ shortTMsg(short, long) { /* check the disambiguation flags and the announcement mode */ if ((gAction.getDobjFlags() & (ClearDisambig | AlwaysAnnounce)) == ClearDisambig && gAction.getDobjCount() == 1 && gameMain.ambigAnnounceMode == DescribeClear) { /* clear disambig and DescribeClear mode - use the long message */ return long; } else { /* in other cases, use the short message */ return short; } } /* * For a TIAction result, select the short-form or long-form message. * This works just like shortTIMsg(), but takes into account both the * direct and indirect objects. */ shortTIMsg(short, long) { /* check the disambiguation flags and the announcement mode */ if (((gAction.getDobjFlags() & (ClearDisambig | AlwaysAnnounce)) == ClearDisambig || (gAction.getIobjFlags() & (ClearDisambig | AlwaysAnnounce)) == ClearDisambig) && gAction.getDobjCount() == 1 && gAction.getIobjCount() == 1 && gameMain.ambigAnnounceMode == DescribeClear) { /* clear disambig and DescribeClear mode - use the long message */ return long; } else { /* in other cases, use the short message */ return short; } } ; /* ------------------------------------------------------------------------ */ /* * Custom base resolver */ modify Resolver /* * Get the default in-scope object list for a given pronoun. We'll * look for a unique object in scope that matches the desired * pronoun, and return a ResolveInfo list if we find one. If there * aren't any objects in scope that match the pronoun, or multiple * objects are in scope, there's no default. */ getPronounDefault(typ, np) { local map = [PronounHim, &canMatchHim, PronounHer, &canMatchHer, PronounIt, &canMatchIt]; local idx = map.indexOf(typ); local filterProp = (idx != nil ? map[idx + 1] : nil); local lst; /* if we couldn't find a filter for the pronoun, ignore it */ if (filterProp == nil) return []; /* * filter the list of all possible defaults to those that match * the given pronoun */ lst = getAllDefaults.subset({x: x.obj_.(filterProp)}); /* * if the list contains exactly one element, then there's a * unique default; otherwise, there's either nothing here that * matches the pronoun or the pronoun is ambiguous, so there's * no default */ if (lst.length() == 1) { /* * we have a unique object, so they must be referring to it; * because this is just a guess, though, mark it as vague */ lst[1].flags_ |= UnclearDisambig; /* return the list */ return lst; } else { /* * the pronoun doesn't have a unique in-scope referent, so * we can't guess what they mean */ return []; } } ; /* ------------------------------------------------------------------------ */ /* * Custom interactive resolver. This is used for responses to * disambiguation questions and prompts for missing noun phrases. */ modify InteractiveResolver /* * Resolve a pronoun antecedent. We'll resolve a third-person * singular pronoun to the target actor if the target actor matches * in gender, and the target actor isn't the PC. This allows * exchanges like this: * *. >bob, examine *. What do you want Bob to look at? *. *. >his book * * In the above exchange, we'll treat "his" as referring to Bob, the * target actor of the action, because we have referred to Bob in * the partial command (the "BOB, EXAMINE") that triggered the * interactive question. */ resolvePronounAntecedent(typ, np, results, poss) { local lst; /* try resolving with the target actor as the antecedent */ if ((lst = resolvePronounAsTargetActor(typ)) != nil) return lst; /* use the inherited result */ return inherited(typ, np, results, poss); } /* * Get the reflexive third-person pronoun binding (himself, herself, * itself, themselves). If the target actor isn't the PC, and the * gender of the pronoun matches, we'll consider this as referring * to the target actor. This allows exchanges of this form: * *. >bob, examine *. What do you want Bob to examine? *. *. >himself */ getReflexiveBinding(typ) { local lst; /* try resolving with the target actor as the antecedent */ if ((lst = resolvePronounAsTargetActor(typ)) != nil) return lst; /* use the inherited result */ return inherited(typ); } /* * Try matching the given pronoun type to the target actor. If it * matches in gender, and the target actor isn't the PC, we'll * return a resolve list consisting of the target actor. If we * don't have a match, we'll return nil. */ resolvePronounAsTargetActor(typ) { /* * if the target actor isn't the player character, and the * target actor can match the given pronoun type, resolve the * pronoun as the target actor */ if (actor_.canMatchPronounType(typ) && !actor_.isPlayerChar()) { /* the match is the target actor */ return [new ResolveInfo(actor_, 0, nil)]; } /* we didn't match it */ return nil; } ; /* * Custom disambiguation resolver. */ modify DisambigResolver /* * Perform special resolution on pronouns used in interactive * responses. If the pronoun is HIM or HER, then look through the * list of possible matches for a matching gendered object, and use * it as the result if we find one. If we find more than one, then * use the default handling instead, treating the pronoun as * referring back to the simple antecedent previously set. */ resolvePronounAntecedent(typ, np, results, poss) { /* if it's a non-possessive HIM or HER, use our special handling */ if (!poss && typ is in (PronounHim, PronounHer)) { local prop; local sub; /* get the gender indicator property for the pronoun */ prop = (typ == PronounHim ? &canMatchHim : &canMatchHer); /* * Scan through the match list to find the objects that * match the gender of the pronoun. Note that if the player * character isn't referred to in the third person, we'll * ignore the player character for the purposes of matching * this pronoun - if we're calling the PC 'you', then we * wouldn't expect the player to refer to the PC as 'him' or * 'her'. */ sub = matchList.subset({x: x.obj_.(prop)}); /* if the list has a single entry, then use it as the match */ if (sub.length() == 1) return sub; /* * if it has more than one entry, it's still ambiguous, but * we might have narrowed it down, so throw a * still-ambiguous exception and let the interactive * disambiguation ask for further clarification */ results.ambiguousNounPhrase(nil, ResolveAsker, 'one', sub, matchList, matchList, 1, self); return []; } /* * if we get this far, it means we didn't use our special * handling, so use the inherited behavior */ return inherited(typ, np, results, poss); } ; /* ------------------------------------------------------------------------ */ /* * Distinguisher customizations for English. * * Each distinguisher must provide a method that gets the name of an item * for a disamgiguation query. Since these are inherently * language-specific, they're defined here. */ /* * The null distinguisher tells objects apart based strictly on the name * string. When we list objects, we simply show the basic name - since * we can tell apart our objects based on the base name, there's no need * to resort to other names. */ modify nullDistinguisher /* we can tell objects apart if they have different base names */ canDistinguish(a, b) { return a.name != b.name; } name(obj) { return obj.name; } aName(obj) { return obj.aName; } theName(obj) { return obj.theName; } countName(obj, cnt) { return obj.countName(cnt); } ; /* * The basic distinguisher can tell apart objects that are not "basic * equivalents" of one another. Thus, we need make no distinctions when * listing objects apart from showing their names. */ modify basicDistinguisher name(obj) { return obj.disambigName; } aName(obj) { return obj.aDisambigName; } theName(obj) { return obj.theDisambigName; } countName(obj, cnt) { return obj.countDisambigName(cnt); } ; /* * The ownership distinguisher tells objects apart based on who "owns" * them, so it shows the owner or location name when listing the object. */ modify ownershipDistinguisher name(obj) { return obj.theNameOwnerLoc(true); } aName(obj) { return obj.aNameOwnerLoc(true); } theName(obj) { return obj.theNameOwnerLoc(true); } countName(obj, cnt) { return obj.countNameOwnerLoc(cnt, true); } /* note that we're prompting based on this distinguisher */ notePrompt(lst) { /* * notify each object that we're referring to it by * owner/location in a disambiguation prompt */ foreach (local cur in lst) cur.obj_.notePromptByOwnerLoc(true); } ; /* * The location distinguisher tells objects apart based on their * containers, so it shows the location name when listing the object. */ modify locationDistinguisher name(obj) { return obj.theNameOwnerLoc(nil); } aName(obj) { return obj.aNameOwnerLoc(nil); } theName(obj) { return obj.theNameOwnerLoc(nil); } countName(obj, cnt) { return obj.countNameOwnerLoc(cnt, nil); } /* note that we're prompting based on this distinguisher */ notePrompt(lst) { /* notify the objects of their use in a disambiguation prompt */ foreach (local cur in lst) cur.obj_.notePromptByOwnerLoc(nil); } ; /* * The lit/unlit distinguisher tells apart objects based on whether * they're lit or unlit, so we list objects as lit or unlit explicitly. */ modify litUnlitDistinguisher name(obj) { return obj.nameLit; } aName(obj) { return obj.aNameLit; } theName(obj) { return obj.theNameLit; } countName(obj, cnt) { return obj.pluralNameLit; } ; /* ------------------------------------------------------------------------ */ /* * Enligh-specific light source modifications */ modify LightSource /* provide lit/unlit names for litUnlitDistinguisher */ nameLit = ((isLit ? 'lit ' : 'unlit ') + name) aNameLit() { /* * if this is a mass noun or a plural name, just use the name * with lit/unlit; otherwise, add "a" */ if (isPlural || isMassNoun) return (isLit ? 'lit ' : 'unlit ') + name; else return (isLit ? 'a lit ' : 'an unlit ') + name; } theNameLit = ((isLit ? 'the lit ' : 'the unlit ') + name) pluralNameLit = ((isLit ? 'lit ' : 'unlit ') + pluralName) /* * Allow 'lit' and 'unlit' as adjectives - but even though we define * these as our adjectives in the dictionary, we'll only accept the * one appropriate for our current state, thanks to our state * objects. */ adjective = 'lit' 'unlit' ; /* * Light source list states. An illuminated light source shows its * status as "providing light"; an unlit light source shows no extra * status. */ lightSourceStateOn: ThingState 'providing light' stateTokens = ['lit'] ; lightSourceStateOff: ThingState stateTokens = ['unlit'] ; /* ------------------------------------------------------------------------ */ /* * Wearable states - a wearable item can be either worn or not worn. */ /* "worn" */ wornState: ThingState 'being worn' /* * In listings of worn items, don't bother mentioning our 'worn' * status, as the entire list consists of items being worn - it * would be redundant to point out that the items in a list of items * being worn are being worn. */ wornName(lst) { return nil; } ; /* * "Unworn" state. Don't bother mentioning the status of an unworn item, * since this is the default for everything. */ unwornState: ThingState; /* ------------------------------------------------------------------------ */ /* * Typographical effects output filter. This filter looks for certain * sequences in the text and converts them to typographical equivalents. * Authors could simply write the HTML for the typographical markups in * the first place, but it's easier to write the typewriter-like * sequences and let this filter convert to HTML. * * We perform the following conversions: * * '---' -> &zwnbsp;— *. '--' -> &zwnbsp;– *. sentence-ending punctuation -> same +   * * Since this routine is called so frequently, we hard-code the * replacement strings, rather than using properties, for slightly faster * performance. Since this routine is so simple, games that want to * customize the replacement style should simply replace this entire * routine with a new routine that applies the customizations. * * Note that we define this filter in the English-specific part of the * library, because it seems almost certain that each language will want * to customize it for local conventions. */ typographicalOutputFilter: OutputFilter filterText(ostr, val) { /* * Look for sentence-ending punctuation, and put an 'en' space * after each occurrence. Recognize ends of sentences even if we * have closing quotes, parentheses, or other grouping characters * following the punctuation. Do this before the hyphen * substitutions so that we can look for ordinary hyphens rather * than all of the expanded versions. */ val = rexReplace(eosPattern, val, '%1\u2002', ReplaceAll); /* undo any abbreviations we mistook for sentence endings */ val = rexReplace(abbrevPat, val, '%1. ', ReplaceAll); /* * Replace dashes with typographical hyphens. Three hyphens in a * row become an em-dash, and two in a row become an en-dash. * Note that we look for the three-hyphen sequence first, because * if we did it the other way around, we'd incorrectly find the * first two hyphens of each '---' sequence and replace them with * an en-dash, causing us to miss the '---' sequences entirely. * * We put a no-break marker (\uFEFF) just before each hyphen, and * an okay-to-break marker (\u200B) just after, to ensure that we * won't have a line break between the preceding text and the * hyphen, and to indicate that a line break is specifically * allowed if needed to the right of the hyphen. */ val = val.findReplace(['---', '--'], ['\uFEFF—\u200B', '\uFEFF–\u200B']); /* return the result */ return val; } /* * The end-of-sentence pattern. This looks a bit complicated, but * all we're looking for is a period, exclamation point, or question * mark, optionally followed by any number of closing group marks * (right parentheses or square brackets, closing HTML tags, or * double or single quotes in either straight or curly styles), all * followed by an ordinary space. * * If a lower-case letter follows the space, though, we won't * consider it a sentence ending. This applies most commonly after * quoted passages ending with what would normally be sentence-ending * punctuation: "'Who are you?' he asked." In these cases, the * enclosing sentence isn't ending, so we don't want the extra space. * We can tell the enclosing sentence isn't ending because a * non-capital letter follows. * * Note that we specifically look only for ordinary spaces. Any * sentence-ending punctuation that's followed by a quoted space or * any typographical space overrides this substitution. */ eosPattern = static new RexPattern( '' + '(' + '[.!?]' + '(' + '' + '|<^rangle>*' + ')*' + ')' + ' +(?![-a-z])' ) /* pattern for abbreviations that were mistaken for sentence endings */ abbrevPat = static new RexPattern( '%<(' + abbreviations + ')\u2002') /* * Common abbreviations. These are excluded from being treated as * sentence endings when they appear with a trailing period. * * Note that abbrevPat must be rebuilt manually if you change this on * the fly - abbrevPat is static, so it picks up the initial value of * this property at start-up, and doesn't re-evaluate it while the * game is running. */ abbreviations = 'mr|mrs|ms|dr|prof' ; /* ------------------------------------------------------------------------ */ /* * The English-specific message builder. */ langMessageBuilder: MessageBuilder /* * The English message substitution parameter table. * * Note that we specify two additional elements for each table entry * beyond the standard language-independent complement: * * info[4] = reflexive property - this is the property to invoke * when the parameter is used reflexively (in other words, its * target object is the same as the most recent target object used * in the nominative case). If this is nil, the parameter has no * reflexive form. * * info[5] = true if this is a nominative usage, nil if not. We use * this to determine which target objects are used in the nominative * case, so that we can remember those objects for subsequent * reflexive usages. */ paramList_ = [ /* parameters that imply the actor as the target object */ ['you/he', &theName, 'actor', nil, true], ['you/she', &theName, 'actor', nil, true], ['you\'re/he\'s', &itIsContraction, 'actor', nil, true], ['you\'re/she\'s', &itIsContraction, 'actor', nil, true], ['you\'re', &itIsContraction, 'actor', nil, true], ['you/him', &theNameObj, 'actor', &itReflexive, nil], ['you/her', &theNameObj, 'actor', &itReflexive, nil], ['your/her', &theNamePossAdj, 'actor', nil, nil], ['your/his', &theNamePossAdj, 'actor', nil, nil], ['your', &theNamePossAdj, 'actor', nil, nil], ['yours/hers', &theNamePossNoun, 'actor', nil, nil], ['yours/his', &theNamePossNoun, 'actor', nil, nil], ['yours', &theNamePossNoun, 'actor', nil, nil], ['yourself/himself', &itReflexive, 'actor', nil, nil], ['yourself/herself', &itReflexive, 'actor', nil, nil], ['yourself', &itReflexive, 'actor', nil, nil], /* parameters that don't imply any target object */ ['the/he', &theName, nil, nil, true], ['the/she', &theName, nil, nil, true], ['the/him', &theNameObj, nil, &itReflexive, nil], ['the/her', &theNameObj, nil, &itReflexive, nil], ['the\'s/her', &theNamePossAdj, nil, &itPossAdj, nil], ['the\'s/hers', &theNamePossNoun, nil, &itPossNoun, nil], /* * Verb 's' endings. In most cases, you should use 's/d', 's/ed', * or 's/?ed' rather than 's', except in places where you know you * will never need the past tense form, because 's' doesn't handle * the past tense. Don't use 's/?ed' with a literal question * mark; put a consonant in place of the question mark instead. */ ['s', &verbEndingS, nil, nil, true], ['s/d', &verbEndingSD, nil, nil, true], ['s/ed', &verbEndingSEd, nil, nil, true], ['s/?ed', &verbEndingSMessageBuilder_, nil, nil, true], ['es', &verbEndingEs, nil, nil, true], ['es/ed', &verbEndingEs, nil, nil, true], ['ies', &verbEndingIes, nil, nil, true], ['ies/ied', &verbEndingIes, nil, nil, true], ['is', &verbToBe, nil, nil, true], ['are', &verbToBe, nil, nil, true], ['was', &verbWas, nil, nil, true], ['were', &verbWas, nil, nil, true], ['has', &verbToHave, nil, nil, true], ['have', &verbToHave, nil, nil, true], ['does', &verbToDo, nil, nil, true], ['do', &verbToDo, nil, nil, true], ['goes', &verbToGo, nil, nil, true], ['go', &verbToGo, nil, nil, true], ['comes', &verbToCome, nil, nil, true], ['come', &verbToCome, nil, nil, true], ['leaves', &verbToLeave, nil, nil, true], ['leave', &verbToLeave, nil, nil, true], ['sees', &verbToSee, nil, nil, true], ['see', &verbToSee, nil, nil, true], ['says', &verbToSay, nil, nil, true], ['say', &verbToSay, nil, nil, true], ['must', &verbMust, nil, nil, true], ['can', &verbCan, nil, nil, true], ['cannot', &verbCannot, nil, nil, true], ['can\'t', &verbCant, nil, nil, true], ['will', &verbWill, nil, nil, true], ['won\'t', &verbWont, nil, nil, true], ['a/he', &aName, nil, nil, true], ['an/he', &aName, nil, nil, true], ['a/she', &aName, nil, nil, true], ['an/she', &aName, nil, nil, true], ['a/him', &aNameObj, nil, &itReflexive, nil], ['an/him', &aNameObj, nil, &itReflexive, nil], ['a/her', &aNameObj, nil, &itReflexive, nil], ['an/her', &aNameObj, nil, &itReflexive, nil], ['it/he', &itNom, nil, nil, true], ['it/she', &itNom, nil, nil, true], ['it/him', &itObj, nil, &itReflexive, nil], ['it/her', &itObj, nil, &itReflexive, nil], /* * note that we don't have its/his, because that leaves * ambiguous whether we want an adjective or noun form - so we * only use the feminine pronouns with these, to make the * meaning unambiguous */ ['its/her', &itPossAdj, nil, nil, nil], ['its/hers', &itPossNoun, nil, nil, nil], ['it\'s/he\'s', &itIsContraction, nil, nil, true], ['it\'s/she\'s', &itIsContraction, nil, nil, true], ['it\'s', &itIsContraction, nil, nil, true], ['that/he', &thatNom, nil, nil, true], ['that/she', &thatNom, nil, nil, true], ['that/him', &thatObj, nil, &itReflexive, nil], ['that/her', &thatObj, nil, &itReflexive, nil], ['that\'s', &thatIsContraction, nil, nil, true], ['itself', &itReflexive, nil, nil, nil], ['itself/himself', &itReflexive, nil, nil, nil], ['itself/herself', &itReflexive, nil, nil, nil], /* default preposition for standing in/on something */ ['on', &actorInName, nil, nil, nil], ['in', &actorInName, nil, nil, nil], ['outof', &actorOutOfName, nil, nil, nil], ['offof', &actorOutOfName, nil, nil, nil], ['onto', &actorIntoName, nil, nil, nil], ['into', &actorIntoName, nil, nil, nil], /* * The special invisible subject marker - this can be used to * mark the subject in sentences that vary from the * subject-verb-object structure that most English sentences * take. The usual SVO structure allows the message builder to * see the subject first in most sentences naturally, but in * unusual sentence forms it is sometimes useful to be able to * mark the subject explicitly. This doesn't actually result in * any output; it's purely for marking the subject for our * internal book-keeping. * * (The main reason the message builder wants to know the subject * in the first place is so that it can use a reflexive pronoun * if the same object ends up being used as a direct or indirect * object: "you can't open yourself" rather than "you can't open * you.") */ ['subj', &dummyName, nil, nil, true] ] /* * Add a hook to the generateMessage method, which we use to * pre-process the source string before expanding the substitution * parameters. */ generateMessage(orig) { return inherited(processOrig(orig)); } /* * Pre-process a source string containing substitution parameters, * before generating the expanded message from it. * * We use this hook to implement the special tense-switching syntax * {|}. Although it superficially looks like an * ordinary substitution parameter, we actually can't use the normal * parameter substitution framework for that, because we want to * allow the and substrings themselves to contain * substitution parameters, and the normal framework doesn't allow * for recursive substitution. * * We simply replace every sequence of the form {|} * with either or , depending on the current * narrative tense. We then substitute braces for square brackets in * the resulting string. This allows treating every bracketed tag * inside the tense-switching sequence as a regular substitution * parameter. * * For example, the sequence "{take[s]|took}" appearing in the * message string would be replaced with "take{s}" if the current * narrative tense is present, and would be replaced with "took" if * the current narrative tense is past. The string "take{s}", if * selected, would in turn be expanded to either "take" or "takes", * depending on the grammatical person of the subject, as per the * regular substitution mechanism. */ processOrig(str) { local idx = 1; local len; local match; local replStr; /* * Keep searching the string until we run out of character * sequences with a special meaning (specifically, we look for * substrings enclosed in braces, and stuttered opening braces). */ for (;;) { /* * Find the next special sequence. */ match = rexSearch(patSpecial, str, idx); /* * If there are no more special sequence, we're done * pre-processing the string. */ if (match == nil) break; /* * Remember the starting index and length of the special * sequence. */ idx = match[1]; len = match[2]; /* * Check if this special sequence matches our tense-switching * syntax. */ if (rexMatch(patTenseSwitching, str, idx) == nil) { /* * It doesn't, so forget about it and continue searching * from the end of this special sequence. */ idx += len; continue; } /* * Extract either the first or the second embedded string, * depending on the current narrative tense. */ match = rexGroup(tSel(1, 2)); replStr = match[3]; /* * Convert all square brackets to braces in the extracted * string. */ replStr = replStr.findReplace('[', '{', ReplaceAll); replStr = replStr.findReplace(']', '}', ReplaceAll); /* * In the original string, replace the tense-switching * sequence with the extracted string. */ str = str.substr(1, idx - 1) + replStr + str.substr(idx + len); /* * Move the index at the end of the substituted string. */ idx += match[2]; } /* * We're done - return the result. */ return str; } /* * Pre-compiled regular expression pattern matching any sequence with * a special meaning in a message string. * * We match either a stuttered opening brace, or a single opening * brace followed by any sequence of characters that doesn't contain * a closing brace followed by a closing brace. */ patSpecial = static new RexPattern ('|(?!)((?:<^rbrace>)*)') /* * Pre-compiled regular expression pattern matching our special * tense-switching syntax. * * We match a single opening brace, followed by any sequence of * characters that doesn't contain a closing brace or a vertical bar, * followed by a vertical bar, followed by any sequence of characters * that doesn't contain a closing brace or a vertical bar, followed * by a closing brace. */ patTenseSwitching = static new RexPattern ( '(?!)((?:<^rbrace|vbar>)*)' + '((?:<^rbrace|vbar>)*)' ) /* * The most recent target object used in the nominative case. We * note this so that we can supply reflexive mappings when the same * object is re-used in the objective case. This allows us to map * things like "you can't take you" to the better-sounding "you * can't take yourself". */ lastSubject_ = nil /* the parameter name of the last subject ('dobj', 'actor', etc) */ lastSubjectName_ = nil /* * Get the target object property mapping. If the target object is * the same as the most recent subject object (i.e., the last object * used in the nominative case), and this parameter has a reflexive * form property, we'll return the reflexive form property. * Otherwise, we'll return the standard property mapping. * * Also, if there was an exclamation mark at the end of any word in * the tag, we'll return a property returning a fixed-tense form of * the property for the tag. */ getTargetProp(targetObj, paramObj, info) { local ret; /* * If this target object matches the last subject, and we have a * reflexive rendering, return the property for the reflexive * rendering. * * Only use the reflexive rendering if the parameter name is * different - if the parameter name is the same, then presumably * the message will have been written with a reflexive pronoun or * not, exactly as the author wants it. When the author knows * going in that these two objects are structurally the same, * they want the exact usage they wrote. */ if (targetObj == lastSubject_ && paramObj != lastSubjectName_ && info[4] != nil) { /* use the reflexive rendering */ ret = info[4]; } else { /* no special handling; inherit the default handling */ ret = inherited(targetObj, paramObj, info); } /* if this is a nominative usage, note it as the last subject */ if (info[5]) { lastSubject_ = targetObj; lastSubjectName_ = paramObj; } /* * If there was an exclamation mark at the end of any word in the * parameter string (which we remember via the fixedTenseProp_ * property), store the original target property in * fixedTenseProp_ and use &propWithPresentMessageBuilder_ as the * target property instead. propWithPresentMessageBuilder_ acts * as a wrapper for the original target property, which it * invokes after temporarily switching to the present tense. */ if (fixedTenseProp_) { fixedTenseProp_ = ret; ret = &propWithPresentMessageBuilder_; } /* return the result */ return ret; } /* end-of-sentence match pattern */ patEndOfSentence = static new RexPattern('[.;:!?]<^alphanum>') /* * Process result text. */ processResult(txt) { /* * If the text contains any sentence-ending punctuation, reset * our internal memory of the subject of the sentence. We * consider the sentence to end with a period, semicolon, colon, * question mark, or exclamation point followed by anything * other than an alpha-numeric. (We require the secondary * character so that we don't confuse things like "3:00" or * "7.5" to contain sentence-ending punctuation.) */ if (rexSearch(patEndOfSentence, txt) != nil) { /* * we have a sentence ending in this run of text, so any * saved subject object will no longer apply after this text * - forget our subject object */ lastSubject_ = nil; lastSubjectName_ = nil; } /* return the inherited processing */ return inherited(txt); } /* some pre-compiled search patterns we use a lot */ patIdObjSlashIdApostS = static new RexPattern( '(<^space>+)(+<^space>+)\'s(/<^space>+)$') patIdObjApostS = static new RexPattern( '(?!<^space>+\'s)(<^space>+)(+<^space>+)\'s$') patParamWithExclam = static new RexPattern('.*(!)(?:.*|/.*|$)') patSSlashLetterEd = static new RexPattern( 's/(ed)$|(ed)/s$') /* * Rewrite a parameter string for a language-specific syntax * extension. * * For English, we'll handle the possessive apostrophe-s suffix * specially, by allowing the apostrophe-s to be appended to the * target object name. If we find an apostrophe-s on the target * object name, we'll move it to the preceding identifier name: * * the dobj's -> the's dobj *. the dobj's/he -> the's dobj/he *. he/the dobj's -> he/the's dobj * * We also use this method to check for the presence of an * exclamation mark at the end of any word in the parameter string * (triggering the fixed-tense handling), and to detect a parameter * string matching the {s/?ed} syntax, where ? is any letter, and * rewrite it literally as 's/?ed' literally. */ langRewriteParam(paramStr) { /* * Check for an exclamation mark at the end of any word in the * parameter string, and remember the result of the test. */ local exclam = rexMatch(patParamWithExclam, paramStr); fixedTenseProp_ = exclam; /* * Remove the exclamation mark, if any. */ if (exclam) { local exclamInd = rexGroup(1)[1]; paramStr = paramStr.substr(1, exclamInd - 1) + paramStr.substr(exclamInd + 1); } /* look for "id obj's" and "id1 obj's/id2" */ if (rexMatch(patIdObjSlashIdApostS, paramStr) != nil) { /* rewrite with the "'s" moved to the preceding parameter name */ paramStr = rexGroup(1)[3] + '\'s' + rexGroup(2)[3] + rexGroup(3)[3]; } else if (rexMatch(patIdObjApostS, paramStr) != nil) { /* rewrite with the "'s" moved to the preceding parameter name */ paramStr = rexGroup(1)[3] + '\'s' + rexGroup(2)[3]; } /* * Check if this parameter matches the {s/?ed} or {?ed/s} syntax. */ if (rexMatch(patSSlashLetterEd, paramStr)) { /* * It does - remember the past verb ending, and rewrite the * parameter literally as 's/?ed'. */ pastEnding_ = rexGroup(1)[3]; paramStr = 's/?ed'; } /* return our (possibly modified) result */ return paramStr; } /* * This property is used to temporarily store the past-tense ending * of a verb to be displayed by Thing.verbEndingSMessageBuilder_. * It's for internal use only; game authors shouldn't have any reason * to access it directly. */ pastEnding_ = nil /* * This property is used to temporarily store either a boolean value * indicating whether the last encountered parameter string had an * exclamation mark at the end of any word, or a property to be * invoked by Thing.propWithPresentMessageBuilder_. This field is * for internal use only; authors shouldn't have any reason to access * it directly. */ fixedTenseProp_ = nil ; /* ------------------------------------------------------------------------ */ /* * Temporarily override the current narrative tense and invoke a callback * function. */ withTense(usePastTense, callback) { /* * Remember the old value of the usePastTense flag. */ local oldUsePastTense = gameMain.usePastTense; /* * Set the new value. */ gameMain.usePastTense = usePastTense; /* * Invoke the callback (remembering the return value) and restore the * usePastTense flag on our way out. */ local ret; try { ret = callback(); } finally { gameMain.usePastTense = oldUsePastTense; } /* * Return the result. */ return ret; } /* ------------------------------------------------------------------------ */ /* * Functions for spelling out numbers. These functions take a numeric * value as input, and return a string with the number spelled out as * words in English. For example, given the number 52, we'd return a * string like 'fifty-two'. * * These functions obviously have language-specific implementations. * Note also that even their interfaces might vary by language. Some * languages might need additional information in the interface; for * example, some languages might need to know the grammatical context * (such as part of speech, case, or gender) of the result. * * Note that some of the spellIntXxx flags might not be meaningful in all * languages, because most of the flags are by their very nature * associated with language-specific idioms. Translations are free to * ignore flags that indicate variations with no local equivalent, and to * add their own language-specific flags as needed. */ /* * Spell out an integer number in words. Returns a string with the * spelled-out number. * * Note that this simple version of the function uses the default * options. If you want to specify non-default options with the * SpellIntXxx flags, you can call spellIntExt(). */ spellInt(val) { return spellIntExt(val, 0); } /* * Spell out an integer number in words, but only if it's below the given * threshold. It's often awkward in prose to spell out large numbers, * but exactly what constitutes a large number depends on context, so * this routine lets the caller specify the threshold. * * If the absolute value of val is less than (not equal to) the threshold * value, we'll return a string with the number spelled out. If the * absolute value is greater than or equal to the threshold value, we'll * return a string representing the number in decimal digits. */ spellIntBelow(val, threshold) { return spellIntBelowExt(val, threshold, 0, 0); } /* * Spell out an integer number in words if it's below a threshold, using * the spellIntXxx flags given in spellFlags to control the spelled-out * format, and using the DigitFormatXxx flags in digitFlags to control * the digit format. */ spellIntBelowExt(val, threshold, spellFlags, digitFlags) { local absval; /* compute the absolute value */ absval = (val < 0 ? -val : val); /* check the value to see whether to spell it or write it as digits */ if (absval < threshold) { /* it's below the threshold - spell it out in words */ return spellIntExt(val, spellFlags); } else { /* it's not below the threshold - write it as digits */ return intToDecimal(val, digitFlags); } } /* * Format a number as a string of decimal digits. The DigitFormatXxx * flags specify how the number is to be formatted.` */ intToDecimal(val, flags) { local str; local sep; /* perform the basic conversion */ str = toString(val); /* add group separators as needed */ if ((flags & DigitFormatGroupComma) != 0) { /* explicitly use a comma as a separator */ sep = ','; } else if ((flags & DigitFormatGroupPeriod) != 0) { /* explicitly use a period as a separator */ sep = '.'; } else if ((flags & DigitFormatGroupSep) != 0) { /* use the current languageGlobals separator */ sep = languageGlobals.digitGroupSeparator; } else { /* no separator */ sep = nil; } /* if there's a separator, add it in */ if (sep != nil) { local i; local len; /* * Insert the separator before each group of three digits. * Start at the right end of the string and work left: peel off * the last three digits and insert a comma. Then, move back * four characters through the string - another three-digit * group, plus the comma we inserted - and repeat. Keep going * until the amount we'd want to peel off the end is as long or * longer than the entire remaining string. */ for (i = 3, len = str.length() ; len > i ; i += 4) { /* insert this comma */ str = str.substr(1, len - i) + sep + str.substr(len - i + 1); /* note the new length */ len = str.length(); } } /* return the result */ return str; } /* * Spell out an integer number - "extended" interface with flags. The * "flags" argument is a (bitwise-OR'd) combination of SpellIntXxx * values, specifying the desired format of the result. */ spellIntExt(val, flags) { local str; local trailingSpace; local needAnd; local powers = [1000000000, ' billion ', 1000000, ' million ', 1000, ' thousand ', 100, ' hundred ']; /* start with an empty string */ str = ''; trailingSpace = nil; needAnd = nil; /* if it's zero, it's a special case */ if (val == 0) return 'zero'; /* * if the number is negative, note it in the string, and use the * absolute value */ if (val < 0) { str = 'negative '; val = -val; } /* do each named power of ten */ for (local i = 1 ; val >= 100 && i <= powers.length() ; i += 2) { /* * if we're in teen-hundreds mode, do the teen-hundreds - this * only works for values from 1,100 to 9,999, since a number like * 12,000 doesn't work this way - 'one hundred twenty hundred' is * no good */ if ((flags & SpellIntTeenHundreds) != 0 && val >= 1100 && val < 10000) { /* if desired, add a comma if there was a prior power group */ if (needAnd && (flags & SpellIntCommas) != 0) str = str.substr(1, str.length() - 1) + ', '; /* spell it out as a number of hundreds */ str += spellIntExt(val / 100, flags) + ' hundred '; /* take off the hundreds */ val %= 100; /* note the trailing space */ trailingSpace = true; /* we have something to put an 'and' after, if desired */ needAnd = true; /* * whatever's left is below 100 now, so there's no need to * keep scanning the big powers of ten */ break; } /* if we have something in this power range, apply it */ if (val >= powers[i]) { /* if desired, add a comma if there was a prior power group */ if (needAnd && (flags & SpellIntCommas) != 0) str = str.substr(1, str.length() - 1) + ', '; /* add the number of multiples of this power and the power name */ str += spellIntExt(val / powers[i], flags) + powers[i+1]; /* take it out of the remaining value */ val %= powers[i]; /* * note that we have a trailing space in the string (all of * the power-of-ten names have a trailing space, to make it * easy to tack on the remainder of the value) */ trailingSpace = true; /* we have something to put an 'and' after, if one is desired */ needAnd = true; } } /* * if we have anything left, and we have written something so far, * and the caller wanted an 'and' before the tens part, add the * 'and' */ if ((flags & SpellIntAndTens) != 0 && needAnd && val != 0) { /* add the 'and' */ str += 'and '; trailingSpace = true; } /* do the tens */ if (val >= 20) { /* anything above the teens is nice and regular */ str += ['twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'][val/10 - 1]; val %= 10; /* if it's non-zero, we'll add the units, so add a hyphen */ if (val != 0) str += '-'; /* we no longer have a trailing space in the string */ trailingSpace = nil; } else if (val >= 10) { /* we have a teen */ str += ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'][val - 9]; /* we've finished with the number */ val = 0; /* there's no trailing space */ trailingSpace = nil; } /* if we have a units value, add it */ if (val != 0) { /* add the units name */ str += ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'][val]; /* we have no trailing space now */ trailingSpace = nil; } /* if there's a trailing space, remove it */ if (trailingSpace) str = str.substr(1, str.length() - 1); /* return the string */ return str; } /* * Return a string giving the numeric ordinal representation of a number: * 1st, 2nd, 3rd, 4th, etc. */ intOrdinal(n) { local s; /* start by getting the string form of the number */ s = toString(n); /* now add the appropriate suffix */ if (n >= 10 && n <= 19) { /* the teens all end in 'th' */ return s + 'th'; } else { /* * for anything but a teen, a number whose last digit is 1 * always has the suffix 'st' (for 'xxx-first', as in '141st'), * a number whose last digit is 2 always ends in 'nd' (for * 'xxx-second', as in '532nd'), a number whose last digit is 3 * ends in 'rd' (for 'xxx-third', as in '53rd'), and anything * else ends in 'th' */ switch(n % 10) { case 1: return s + 'st'; case 2: return s + 'nd'; case 3: return s + 'rd'; default: return s + 'th'; } } } /* * Return a string giving a fully spelled-out ordinal form of a number: * first, second, third, etc. */ spellIntOrdinal(n) { return spellIntOrdinalExt(n, 0); } /* * Return a string giving a fully spelled-out ordinal form of a number: * first, second, third, etc. This form takes the same flag values as * spellIntExt(). */ spellIntOrdinalExt(n, flags) { local s; /* get the spelled-out form of the number itself */ s = spellIntExt(n, flags); /* * If the number ends in 'one', change the ending to 'first'; 'two' * becomes 'second'; 'three' becomes 'third'; 'five' becomes * 'fifth'; 'eight' becomes 'eighth'; 'nine' becomes 'ninth'. If * the number ends in 'y', change the 'y' to 'ieth'. 'Zero' becomes * 'zeroeth'. For everything else, just add 'th' to the spelled-out * name */ if (s == 'zero') return 'zeroeth'; if (s.endsWith('one')) return s.substr(1, s.length() - 3) + 'first'; else if (s.endsWith('two')) return s.substr(1, s.length() - 3) + 'second'; else if (s.endsWith('three')) return s.substr(1, s.length() - 5) + 'third'; else if (s.endsWith('five')) return s.substr(1, s.length() - 4) + 'fifth'; else if (s.endsWith('eight')) return s.substr(1, s.length() - 5) + 'eighth'; else if (s.endsWith('nine')) return s.substr(1, s.length() - 4) + 'ninth'; else if (s.endsWith('y')) return s.substr(1, s.length() - 1) + 'ieth'; else return s + 'th'; } /* ------------------------------------------------------------------------ */ /* * Parse a spelled-out number. This is essentially the reverse of * spellInt() and related functions: we take a string that contains a * spelled-out number and return the integer value. This uses the * command parser's spelled-out number rules, so we can parse anything * that would be recognized as a number in a command. * * If the string contains numerals, we'll treat it as a number in digit * format: for example, if it contains '789', we'll return 789. * * If the string doesn't parse as a number, we return nil. */ parseInt(str) { try { /* tokenize the string */ local toks = cmdTokenizer.tokenize(str); /* parse it */ return parseIntTokens(toks); } catch (Exception exc) { /* * on any exception, just return nil to indicate that we couldn't * parse the string as a number */ return nil; } } /* * Parse a spelled-out number that's given as a token list (as returned * from Tokenizer.tokenize). If we can successfully parse the token list * as a number, we'll return the integer value. If not, we'll return * nil. */ parseIntTokens(toks) { try { /* * if the first token contains digits, treat it as a numeric * string value rather than a spelled-out number */ if (toks.length() != 0 && rexMatch('+', getTokOrig(toks[1])) != nil) return toInteger(getTokOrig(toks[1])); /* parse it using the spelledNumber production */ local lst = spelledNumber.parseTokens(toks, cmdDict); /* * if we got a match, return the integer value; if not, it's not * parseable as a number, so return nil */ return (lst.length() != 0 ? lst[1].getval() : nil); } catch (Exception exc) { /* * on any exception, just return nil to indicate that it's not * parseable as a number */ return nil; } } /* ------------------------------------------------------------------------ */ /* * Additional token types for US English. */ /* special "apostrophe-s" token */ enum token tokApostropheS; /* special apostrophe token for plural possessives ("the smiths' house") */ enum token tokPluralApostrophe; /* special abbreviation-period token */ enum token tokAbbrPeriod; /* special "#nnn" numeric token */ enum token tokPoundInt; /* * Command tokenizer for US English. Other language modules should * provide their own tokenizers to allow for differences in punctuation * and other lexical elements. */ cmdTokenizer: Tokenizer rules_ = static [ /* skip whitespace */ ['whitespace', new RexPattern('+'), nil, &tokCvtSkip, nil], /* certain punctuation marks */ ['punctuation', new RexPattern('[.,;:?!]'), tokPunct, nil, nil], /* * We have a special rule for spelled-out numbers from 21 to 99: * when we see a 'tens' word followed by a hyphen followed by a * digits word, we'll pull out the tens word, the hyphen, and * the digits word as separate tokens. */ ['spelled number', new RexPattern('(twenty|thirty|forty|fifty|sixty|' + 'seventy|eighty|ninety)-' + '(one|two|three|four|five|six|seven|eight|nine)' + '(?!)'), tokWord, &tokCvtSpelledNumber, nil], /* * Initials. We'll look for strings of three or two initials, * set off by periods but without spaces. We'll look for * three-letter initials first ("G.H.W. Billfold"), then * two-letter initials ("X.Y. Zed"), so that we find the longest * sequence that's actually in the dictionary. Note that we * don't have a separate rule for individual initials, since * we'll pick that up with the regular abbreviated word rule * below. * * Some games could conceivably extend this to allow strings of * initials of four letters or longer, but in practice people * tend to elide the periods in longer sets of initials, so that * the initials become an acronym, and thus would fit the * ordinary word token rule. */ ['three initials', new RexPattern(''), tokWord, &tokCvtAbbr, &acceptAbbrTok], ['two initials', new RexPattern(''), tokWord, &tokCvtAbbr, &acceptAbbrTok], /* * Abbbreviated word - this is a word that ends in a period, * such as "Mr.". This rule comes before the ordinary word rule * because we will only consider the period to be part of the * word (and not a separate token) if the entire string * including the period is in the main vocabulary dictionary. */ ['abbreviation', new RexPattern('*'), tokWord, &tokCvtAbbr, &acceptAbbrTok], /* * A word ending in an apostrophe-s. We parse this as two * separate tokens: one for the word and one for the * apostrophe-s. */ ['apostrophe-s word', new RexPattern('*[sS]'), tokWord, &tokCvtApostropheS, nil], /* * A plural word ending in an apostrophe. We parse this as two * separate tokens: one for the word and one for the apostrophe. */ ['plural possessive word', new RexPattern('*' + '(?!)'), tokWord, &tokCvtPluralApostrophe, nil], /* * Words - note that we convert everything to lower-case. A word * must start with an alphabetic character, a hyphen, or an * ampersand; after the initial character, a word can contain * alphabetics, digits, hyphens, ampersands, and apostrophes. */ ['word', new RexPattern('*'), tokWord, nil, nil], /* an abbreviation word starting with a number */ ['abbreviation with initial digit', new RexPattern('(?=*)' + '*'), tokWord, &tokCvtAbbr, &acceptAbbrTok], /* * A word can start with a number, as long as there's something * other than numbers in the string - if it's all numbers, we * want to treat it as a numeric token. */ ['word with initial digit', new RexPattern('(?=*)' + '*'), tokWord, nil, nil], /* strings with ASCII "straight" quotes */ ['string ascii-quote', new RexPattern('([`\'"])(.*)%1(?!)'), tokString, nil, nil], /* some people like to use single quotes like `this' */ ['string back-quote', new RexPattern('`(.*)\'(?!)'), tokString, nil, nil], /* strings with Latin-1 curly quotes (single and double) */ ['string curly single-quote', new RexPattern('\u2018(.*)\u2019'), tokString, nil, nil], ['string curly double-quote', new RexPattern('\u201C(.*)\u201D'), tokString, nil, nil], /* * unterminated string - if we didn't just match a terminated * string, but we have what looks like the start of a string, * match to the end of the line */ ['string unterminated', new RexPattern('([`\'"\u2018\u201C](.*)'), tokString, nil, nil], /* integer numbers */ ['integer', new RexPattern('[0-9]+'), tokInt, nil, nil], /* numbers with a '#' preceding */ ['integer with #', new RexPattern('#[0-9]+'), tokPoundInt, nil, nil] ] /* * Handle an apostrophe-s word. We'll return this as two separate * tokens: one for the word preceding the apostrophe-s, and one for * the apostrophe-s itself. */ tokCvtApostropheS(txt, typ, toks) { local w; local s; /* * pull out the part up to but not including the apostrophe, and * pull out the apostrophe-s part */ w = txt.substr(1, txt.length() - 2); s = txt.substr(-2); /* add the part before the apostrophe as the main token type */ toks.append([w, typ, w]); /* add the apostrophe-s as a separate special token */ toks.append([s, tokApostropheS, s]); } /* * Handle a plural apostrophe word ("the smiths' house"). We'll * return this as two tokens: one for the plural word, and one for * the apostrophe. */ tokCvtPluralApostrophe(txt, typ, toks) { local w; local s; /* * pull out the part up to but not including the apostrophe, and * separately pull out the apostrophe */ w = txt.substr(1, txt.length() - 1); s = txt.substr(-1); /* add the part before the apostrophe as the main token type */ toks.append([w, typ, w]); /* add the apostrophe-s as a separate special token */ toks.append([s, tokPluralApostrophe, s]); } /* * Handle a spelled-out hyphenated number from 21 to 99. We'll * return this as three separate tokens: a word for the tens name, a * word for the hyphen, and a word for the units name. */ tokCvtSpelledNumber(txt, typ, toks) { /* parse the number into its three parts with a regular expression */ rexMatch(patAlphaDashAlpha, txt); /* add the part before the hyphen */ toks.append([rexGroup(1)[3], typ, rexGroup(1)[3]]); /* add the hyphen */ toks.append(['-', typ, '-']); /* add the part after the hyphen */ toks.append([rexGroup(2)[3], typ, rexGroup(2)[3]]); } patAlphaDashAlpha = static new RexPattern('(+)-(+)') /* * Check to see if we want to accept an abbreviated token - this is * a token that ends in a period, which we use for abbreviated words * like "Mr." or "Ave." We'll accept the token only if it appears * as given - including the period - in the dictionary. Note that * we ignore truncated matches, since the only way we'll accept a * period in a word token is as the last character; there is thus no * way that a token ending in a period could be a truncation of any * longer valid token. */ acceptAbbrTok(txt) { /* look up the word, filtering out truncated results */ return cmdDict.isWordDefined( txt, {result: (result & StrCompTrunc) == 0}); } /* * Process an abbreviated token. * * When we find an abbreviation, we'll enter it with the abbreviated * word minus the trailing period, plus the period as a separate * token. We'll mark the period as an "abbreviation period" so that * grammar rules will be able to consider treating it as an * abbreviation -- but since it's also a regular period, grammar * rules that treat periods as regular punctuation will also be able * to try to match the result. This will ensure that we try it both * ways - as abbreviation and as a word with punctuation - and pick * the one that gives us the best result. */ tokCvtAbbr(txt, typ, toks) { local w; /* add the part before the period as the ordinary token */ w = txt.substr(1, txt.length() - 1); toks.append([w, typ, w]); /* add the token for the "abbreviation period" */ toks.append(['.', tokAbbrPeriod, '.']); } /* * Given a list of token strings, rebuild the original input string. * We can't recover the exact input string, because the tokenization * process throws away whitespace information, but we can at least * come up with something that will display cleanly and produce the * same results when run through the tokenizer. */ buildOrigText(toks) { local str; /* start with an empty string */ str = ''; /* concatenate each token in the list */ for (local i = 1, local len = toks.length() ; i <= len ; ++i) { /* add the current token to the string */ str += getTokOrig(toks[i]); /* * if this looks like a hyphenated number that we picked * apart into two tokens, put it back together without * spaces */ if (i + 2 <= len && rexMatch(patSpelledTens, getTokVal(toks[i])) != nil && getTokVal(toks[i+1]) == '-' && rexMatch(patSpelledUnits, getTokVal(toks[i+2])) != nil) { /* * it's a hyphenated number, all right - put the three * tokens back together without any intervening spaces, * so ['twenty', '-', 'one'] turns into 'twenty-one' */ str += getTokOrig(toks[i+1]) + getTokOrig(toks[i+2]); /* skip ahead by the two extra tokens we're adding */ i += 2; } else if (i + 1 <= len && getTokType(toks[i]) == tokWord && getTokType(toks[i+1]) is in (tokApostropheS, tokPluralApostrophe)) { /* * it's a word followed by an apostrophe-s token - these * are appended together without any intervening spaces */ str += getTokOrig(toks[i+1]); /* skip the extra token we added */ ++i; } /* * If another token follows, and the next token isn't a * punctuation mark, and the previous token wasn't an open * paren, add a space before the next token. */ if (i != len && rexMatch(patPunct, getTokVal(toks[i+1])) == nil && getTokVal(toks[i]) != '(') str += ' '; } /* return the result string */ return str; } /* some pre-compiled regular expressions */ patSpelledTens = static new RexPattern( 'twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety') patSpelledUnits = static new RexPattern( 'one|two|three|four|five|six|seven|eight|nine') patPunct = static new RexPattern('[.,;:?!)]') ; /* ------------------------------------------------------------------------ */ /* * Grammar Rules */ /* * Command with explicit target actor. When a command starts with an * actor's name followed by a comma followed by a verb, we take it as * being directed to the actor. */ grammar firstCommandPhrase(withActor): singleNounOnly->actor_ ',' commandPhrase->cmd_ : FirstCommandProdWithActor /* "execute" the target actor phrase */ execActorPhrase(issuingActor) { /* flag that the actor's being addressed in the second person */ resolvedActor_.commandReferralPerson = SecondPerson; } ; grammar firstCommandPhrase(askTellActorTo): ('ask' | 'tell' | 'a' | 't') singleNounOnly->actor_ 'to' commandPhrase->cmd_ : FirstCommandProdWithActor /* "execute" the target actor phrase */ execActorPhrase(issuingActor) { /* * Since our phrasing is TELL TO , the * actor clearly becomes the antecedent for a subsequent * pronoun. For example, in TELL BOB TO READ HIS BOOK, the word * HIS pretty clearly refers back to BOB. */ if (resolvedActor_ != nil) { /* set the possessive anaphor object to the actor */ resolvedActor_.setPossAnaphorObj(resolvedActor_); /* flag that the actor's being addressed in the third person */ resolvedActor_.commandReferralPerson = ThirdPerson; /* * in subsequent commands carried out by the issuer, the * target actor is now the pronoun antecedent (for example: * after TELL BOB TO GO NORTH, the command FOLLOW HIM means * to follow Bob) */ issuingActor.setPronounObj(resolvedActor_); } } ; /* * An actor-targeted command with a bad command phrase. This is used as * a fallback if we fail to match anything on the first attempt at * parsing the first command on a line. The point is to at least detect * the target actor phrase, if that much is valid, so that we better * customize error messages for the rest of the command. */ grammar actorBadCommandPhrase(main): singleNounOnly->actor_ ',' miscWordList | ('ask' | 'tell' | 'a' | 't') singleNounOnly->actor_ 'to' miscWordList : FirstCommandProdWithActor /* to resolve nouns, we merely resolve the actor */ resolveNouns(issuingActor, targetActor, results) { /* resolve the underlying actor phrase */ return actor_.resolveNouns(getResolver(issuingActor), results); } ; /* * Command-only conjunctions. These words and groups of words can * separate commands from one another, and can't be used to separate noun * phrases in a noun list. */ grammar commandOnlyConjunction(sentenceEnding): '.' | '!' : BasicProd /* these conjunctions end the sentence */ isEndOfSentence() { return true; } ; grammar commandOnlyConjunction(nonSentenceEnding): 'then' | 'and' 'then' | ',' 'then' | ',' 'and' 'then' | ';' : BasicProd /* these conjunctions do not end a sentence */ isEndOfSentence() { return nil; } ; /* * Command-or-noun conjunctions. These words and groups of words can be * used to separate commands from one another, and can also be used to * separate noun phrases in a noun list. */ grammar commandOrNounConjunction(main): ',' | 'and' | ',' 'and' : BasicProd /* these do not end a sentence */ isEndOfSentence() { return nil; } ; /* * Noun conjunctions. These words and groups of words can be used to * separate noun phrases from one another. Note that these do not need * to be exclusive to noun phrases - these can occur as command * conjunctions as well; this list is separated from * commandOrNounConjunction in case there are conjunctions that can never * be used as command conjunctions, since such conjunctions, which can * appear here, would not appear in commandOrNounConjunctions. */ grammar nounConjunction(main): ',' | 'and' | ',' 'and' : BasicProd /* these conjunctions do not end a sentence */ isEndOfSentence() { return nil; } ; /* ------------------------------------------------------------------------ */ /* * Noun list: one or more noun phrases connected with conjunctions. This * kind of noun list can end in a terminal noun phrase. * * Note that a single noun phrase is a valid noun list, since a list can * simply be a list of one. The separate production nounMultiList can be * used when multiple noun phrases are required. */ /* * a noun list can consist of a single terminal noun phrase */ grammar nounList(terminal): terminalNounPhrase->np_ : NounListProd resolveNouns(resolver, results) { /* resolve the underlying noun phrase */ return np_.resolveNouns(resolver, results); } ; /* * a noun list can consist of a list of a single complete (non-terminal) * noun phrase */ grammar nounList(nonTerminal): completeNounPhrase->np_ : NounListProd resolveNouns(resolver, results) { /* resolve the underlying noun phrase */ return np_.resolveNouns(resolver, results); } ; /* * a noun list can consist of a list with two or more entries */ grammar nounList(list): nounMultiList->lst_ : NounListProd resolveNouns(resolver, results) { /* resolve the underlying list */ return lst_.resolveNouns(resolver, results); } ; /* * An empty noun list is one with no words at all. This is matched when * a command requires a noun list but the player doesn't include one; * this construct has "badness" because we only want to match it when we * have no choice. */ grammar nounList(empty): [badness 500] : EmptyNounPhraseProd responseProd = nounList ; /* ------------------------------------------------------------------------ */ /* * Noun Multi List: two or more noun phrases connected by conjunctions. * This is almost the same as the basic nounList production, but this * type of production requires at least two noun phrases, whereas the * basic nounList production more generally defines its list as any * number - including one - of noun phrases. */ /* * a multi list can consist of a noun multi- list plus a terminal noun * phrase, separated by a conjunction */ grammar nounMultiList(multi): nounMultiList->lst_ nounConjunction terminalNounPhrase->np_ : NounListProd resolveNouns(resolver, results) { /* return a list of all of the objects from both underlying lists */ return np_.resolveNouns(resolver, results) + lst_.resolveNouns(resolver, results); } ; /* * a multi list can consist of a non-terminal multi list */ grammar nounMultiList(nonterminal): nonTerminalNounMultiList->lst_ : NounListProd resolveNouns(resolver, results) { /* resolve the underlying list */ return lst_.resolveNouns(resolver, results); } ; /* ------------------------------------------------------------------------ */ /* * A non-terminal noun multi list is a noun list made up of at least two * non-terminal noun phrases, connected by conjunctions. * * This is almost the same as the regular non-terminal noun list * production, but this production requires two or more underlying noun * phrases, whereas the basic non-terminal noun list matches any number * of underlying phrases, including one. */ /* * a non-terminal multi-list can consist of a pair of complete noun * phrases separated by a conjunction */ grammar nonTerminalNounMultiList(pair): completeNounPhrase->np1_ nounConjunction completeNounPhrase->np2_ : NounListProd resolveNouns(resolver, results) { /* return the combination of the two underlying noun phrases */ return np1_.resolveNouns(resolver, results) + np2_.resolveNouns(resolver, results); } ; /* * a non-terminal multi-list can consist of another non-terminal * multi-list plus a complete noun phrase, connected by a conjunction */ grammar nonTerminalNounMultiList(multi): nonTerminalNounMultiList->lst_ nounConjunction completeNounPhrase->np_ : NounListProd resolveNouns(resolver, results) { /* return the combination of the sublist and the noun phrase */ return lst_.resolveNouns(resolver, results) + np_.resolveNouns(resolver, results); } ; /* ------------------------------------------------------------------------ */ /* * "Except" list. This is a noun list that can contain anything that's * in a regular noun list plus some things that only make sense as * exceptions, such as possessive nouns (e.g., "mine"). */ grammar exceptList(single): exceptNounPhrase->np_ : ExceptListProd resolveNouns(resolver, results) { return np_.resolveNouns(resolver, results); } ; grammar exceptList(list): exceptNounPhrase->np_ nounConjunction exceptList->lst_ : ExceptListProd resolveNouns(resolver, results) { /* return a list consisting of all of our objects */ return np_.resolveNouns(resolver, results) + lst_.resolveNouns(resolver, results); } ; /* * An "except" noun phrase is a normal "complete" noun phrase or a * possessive noun phrase that doesn't explicitly qualify another phrase * (for example, "all the coins but bob's" - the "bob's" is just a * possessive noun phrase without another noun phrase attached, since it * implicitly qualifies "the coins"). */ grammar exceptNounPhrase(singleComplete): completeNounPhraseWithoutAll->np_ : ExceptListProd resolveNouns(resolver, results) { return np_.resolveNouns(resolver, results); } ; grammar exceptNounPhrase(singlePossessive): possessiveNounPhrase->poss_ : ButPossessiveProd ; /* ------------------------------------------------------------------------ */ /* * A single noun is sometimes required where, structurally, a list is not * allowed. Single nouns should not be used to prohibit lists where * there is no structural reason for the prohibition - these should be * used only where it doesn't make sense to use a list structurally. */ grammar singleNoun(normal): singleNounOnly->np_ : LayeredNounPhraseProd ; /* * An empty single noun is one with no words at all. This is matched * when a command requires a noun list but the player doesn't include * one; this construct has "badness" because we only want to match it * when we have no choice. */ grammar singleNoun(empty): [badness 500] : EmptyNounPhraseProd /* use a nil responseProd, so that we get the phrasing from the action */ responseProd = nil /* the fallback responseProd, if we can't get one from the action */ fallbackResponseProd = singleNoun ; /* * A user could attempt to use a noun list with more than one entry (a * "multi list") where a single noun is required. This is not a * grammatical error, so we accept it grammatically; however, for * disambiguation purposes we score it lower than a singleNoun production * with only one noun phrase, and if we try to resolve it, we'll fail * with an error. */ grammar singleNoun(multiple): nounMultiList->np_ : SingleNounWithListProd ; /* * A *structural* single noun phrase. This production is for use where a * single noun phrase (not a list of nouns) is required grammatically. */ grammar singleNounOnly(main): terminalNounPhrase->np_ | completeNounPhrase->np_ : SingleNounProd ; /* ------------------------------------------------------------------------ */ /* * Prepositionally modified single noun phrases. These can be used in * indirect object responses, so allow for interactions like this: * * >unlock door *. What do you want to unlock it with? * * >with the key * * The entire notion of prepositionally qualified noun phrases in * interactive indirect object responses is specific to English, so this * is implemented in the English module only. However, the general * notion of specialized responses to interactive indirect object queries * is handled in the language-independent library in some cases, in such * a way that the language-specific library can customize the behavior - * see TIAction.askIobjResponseProd. */ class PrepSingleNounProd: SingleNounProd resolveNouns(resolver, results) { return np_.resolveNouns(resolver, results); } /* * If the response starts with a preposition, it's pretty clearly a * response to the special query rather than a new command. */ isSpecialResponseMatch() { return (np_ != nil && np_.firstTokenIndex > 1); } ; /* * Same thing for a Topic phrase */ class PrepSingleTopicProd: TopicProd resolveNouns(resolver, results) { return np_.resolveNouns(resolver, results); } ; grammar inSingleNoun(main): singleNoun->np_ | ('in' | 'into' | 'in' 'to') singleNoun->np_ : PrepSingleNounProd ; grammar forSingleNoun(main): singleNoun->np_ | 'for' singleNoun->np_ : PrepSingleNounProd ; grammar toSingleNoun(main): singleNoun->np_ | 'to' singleNoun->np_ : PrepSingleNounProd ; grammar throughSingleNoun(main): singleNoun->np_ | ('through' | 'thru') singleNoun->np_ : PrepSingleNounProd ; grammar fromSingleNoun(main): singleNoun->np_ | 'from' singleNoun->np_ : PrepSingleNounProd ; grammar onSingleNoun(main): singleNoun->np_ | ('on' | 'onto' | 'on' 'to') singleNoun->np_ : PrepSingleNounProd ; grammar withSingleNoun(main): singleNoun->np_ | 'with' singleNoun->np_ : PrepSingleNounProd ; grammar atSingleNoun(main): singleNoun->np_ | 'at' singleNoun->np_ : PrepSingleNounProd ; grammar outOfSingleNoun(main): singleNoun->np_ | 'out' 'of' singleNoun->np_ : PrepSingleNounProd ; grammar aboutTopicPhrase(main): topicPhrase->np_ | 'about' topicPhrase->np_ : PrepSingleTopicProd ; /* ------------------------------------------------------------------------ */ /* * Complete noun phrase - this is a fully-qualified noun phrase that * cannot be modified with articles, quantifiers, or anything else. This * is the highest-level individual noun phrase. */ grammar completeNounPhrase(main): completeNounPhraseWithAll->np_ | completeNounPhraseWithoutAll->np_ : LayeredNounPhraseProd ; /* * Slightly better than a purely miscellaneous word list is a pair of * otherwise valid noun phrases connected by a preposition that's * commonly used in command phrases. This will match commands where the * user has assumed a command with a prepositional structure that doesn't * exist among the defined commands. Since we have badness, we'll be * ignored any time there's a valid command syntax with the same * prepositional structure. */ grammar completeNounPhrase(miscPrep): [badness 100] completeNounPhrase->np1_ ('with' | 'into' | 'in' 'to' | 'through' | 'thru' | 'for' | 'to' | 'onto' | 'on' 'to' | 'at' | 'under' | 'behind') completeNounPhrase->np2_ : NounPhraseProd resolveNouns(resolver, results) { /* note that we have an invalid prepositional phrase structure */ results.noteBadPrep(); /* resolve the underlying noun phrases, for scoring purposes */ np1_.resolveNouns(resolver, results); np2_.resolveNouns(resolver, results); /* return nothing */ return []; } ; /* * A qualified noun phrase can, all by itself, be a full noun phrase */ grammar completeNounPhraseWithoutAll(qualified): qualifiedNounPhrase->np_ : LayeredNounPhraseProd ; /* * Pronoun rules. A pronoun is a complete noun phrase; it does not allow * further qualification. */ grammar completeNounPhraseWithoutAll(it): 'it' : ItProd; grammar completeNounPhraseWithoutAll(them): 'them' : ThemProd; grammar completeNounPhraseWithoutAll(him): 'him' : HimProd; grammar completeNounPhraseWithoutAll(her): 'her' : HerProd; /* * Reflexive second-person pronoun, for things like "bob, look at * yourself" */ grammar completeNounPhraseWithoutAll(yourself): 'yourself' | 'yourselves' | 'you' : YouProd ; /* * Reflexive third-person pronouns. We accept these in places such as * the indirect object of a two-object verb. */ grammar completeNounPhraseWithoutAll(itself): 'itself' : ItselfProd /* check agreement of our binding */ checkAgreement(lst) { /* the result is required to be singular and ungendered */ return (lst.length() == 1 && lst[1].obj_.canMatchIt); } ; grammar completeNounPhraseWithoutAll(themselves): 'themself' | 'themselves' : ThemselvesProd /* check agreement of our binding */ checkAgreement(lst) { /* * For 'themselves', allow anything; we could balk at this * matching a single object that isn't a mass noun, but that * would be overly picky, and it would probably reject at least * a few things that really ought to be acceptable. Besides, * 'them' is the closest thing English has to a singular * gender-neutral pronoun, and some people intentionally use it * as such. */ return true; } ; grammar completeNounPhraseWithoutAll(himself): 'himself' : HimselfProd /* check agreement of our binding */ checkAgreement(lst) { /* the result is required to be singular and masculine */ return (lst.length() == 1 && lst[1].obj_.canMatchHim); } ; grammar completeNounPhraseWithoutAll(herself): 'herself' : HerselfProd /* check agreement of our binding */ checkAgreement(lst) { /* the result is required to be singular and feminine */ return (lst.length() == 1 && lst[1].obj_.canMatchHer); } ; /* * First-person pronoun, for referring to the speaker: "bob, look at me" */ grammar completeNounPhraseWithoutAll(me): 'me' | 'myself' : MeProd; /* * "All" and "all but". * * "All" is a "complete" noun phrase, because there's nothing else needed * to make it a noun phrase. We make this a special kind of complete * noun phrase because 'all' is not acceptable as a complete noun phrase * in some contexts where any of the other complete noun phrases are * acceptable. * * "All but" is a "terminal" noun phrase - this is a special kind of * complete noun phrase that cannot be followed by another noun phrase * with "and". "All but" is terminal because we want any and's that * follow it to be part of the exception list, so that we interpret "take * all but a and b" as excluding a and b, not as excluding a but then * including b as a separate list. */ grammar completeNounPhraseWithAll(main): 'all' | 'everything' : EverythingProd ; grammar terminalNounPhrase(allBut): ('all' | 'everything') ('but' | 'except' | 'except' 'for') exceptList->except_ : EverythingButProd ; /* * Plural phrase with an exclusion list. This is a terminal noun phrase * because it ends in an exclusion list. */ grammar terminalNounPhrase(pluralExcept): (qualifiedPluralNounPhrase->np_ | detPluralNounPhrase->np_) ('except' | 'except' 'for' | 'but' | 'but' 'not') exceptList->except_ : ListButProd ; /* * Qualified singular with an exception */ grammar terminalNounPhrase(anyBut): 'any' nounPhrase->np_ ('but' | 'except' | 'except' 'for' | 'but' 'not') exceptList->except_ : IndefiniteNounButProd ; /* ------------------------------------------------------------------------ */ /* * A qualified noun phrase is a noun phrase with an optional set of * qualifiers: a definite or indefinite article, a quantifier, words such * as 'any' and 'all', possessives, and locational specifiers ("the box * on the table"). * * Without qualification, a definite article is implicit, so we read * "take box" as equivalent to "take the box." * * Grammar rule instantiations in language-specific modules should set * property np_ to the underlying noun phrase match tree. */ /* * A qualified noun phrase can be either singular or plural. The number * is a feature of the overall phrase; the phrase might consist of * subphrases of different numbers (for example, "bob's coins" is plural * even though it contains a singular subphrase, "bob"; and "one of the * coins" is singular, even though its subphrase "coins" is plural). */ grammar qualifiedNounPhrase(main): qualifiedSingularNounPhrase->np_ | qualifiedPluralNounPhrase->np_ : LayeredNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * Singular qualified noun phrase. */ /* * A singular qualified noun phrase with an implicit or explicit definite * article. If there is no article, a definite article is implied (we * interpret "take box" as though it were "take the box"). */ grammar qualifiedSingularNounPhrase(definite): ('the' | 'the' 'one' | 'the' '1' | ) indetSingularNounPhrase->np_ : DefiniteNounProd ; /* * A singular qualified noun phrase with an explicit indefinite article. */ grammar qualifiedSingularNounPhrase(indefinite): ('a' | 'an') indetSingularNounPhrase->np_ : IndefiniteNounProd ; /* * A singular qualified noun phrase with an explicit arbitrary * determiner. */ grammar qualifiedSingularNounPhrase(arbitrary): ('any' | 'one' | '1' | 'any' ('one' | '1')) indetSingularNounPhrase->np_ : ArbitraryNounProd ; /* * A singular qualified noun phrase with a possessive adjective. */ grammar qualifiedSingularNounPhrase(possessive): possessiveAdjPhrase->poss_ indetSingularNounPhrase->np_ : PossessiveNounProd ; /* * A singular qualified noun phrase that arbitrarily selects from a * plural set. This is singular, even though the underlying noun phrase * is plural, because we're explicitly selecting one item. */ grammar qualifiedSingularNounPhrase(anyPlural): 'any' 'of' explicitDetPluralNounPhrase->np_ : ArbitraryNounProd ; /* * A singular object specified only by its containment, with a definite * article. */ grammar qualifiedSingularNounPhrase(theOneIn): 'the' 'one' ('that' ('is' | 'was') | 'that' tokApostropheS | ) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : VagueContainerDefiniteNounPhraseProd /* * our main phrase is simply 'one' (so disambiguation prompts will * read "which one do you mean...") */ mainPhraseText = 'one' ; /* * A singular object specified only by its containment, with an * indefinite article. */ grammar qualifiedSingularNounPhrase(anyOneIn): ('anything' | 'one') ('that' ('is' | 'was') | 'that' tokApostropheS | ) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : VagueContainerIndefiniteNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * An "indeterminate" singular noun phrase is a noun phrase without any * determiner. A determiner is a phrase that specifies the phrase's * number and indicates whether or not it refers to a specific object, * and if so fixes which object it refers to; determiners include * articles ("the", "a") and possessives. * * Note that an indeterminate phrase is NOT necessarily an indefinite * phrase. In fact, in most cases, we assume a definite usage when the * determiner is omitted: we take TAKE BOX as meaning TAKE THE BOX. This * is more or less the natural way an English speaker would interpret * this ill-formed phrasing, but even more than that, it's the * Adventurese convention, taking into account that most players enter * commands telegraphically and are accustomed to noun phrases being * definite by default. */ /* an indetermine noun phrase can be a simple noun phrase */ grammar indetSingularNounPhrase(basic): nounPhrase->np_ : LayeredNounPhraseProd ; /* * An indetermine noun phrase can specify a location for the object(s). * The location must be a singular noun phrase, but can itself be a fully * qualified noun phrase (so it can have possessives, articles, and * locational qualifiers of its own). * * Note that we take 'that are' even though the noun phrase is singular, * because what we consider a singular noun phrase can have plural usage * ("scissors", for example). */ grammar indetSingularNounPhrase(locational): nounPhrase->np_ ('that' ('is' | 'was') | 'that' tokApostropheS | 'that' ('are' | 'were') | ) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : ContainerNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * Plural qualified noun phrase. */ /* * A simple unqualified plural phrase with determiner. Since this form * of plural phrase doesn't have any additional syntax that makes it an * unambiguous plural, we can only accept an actual plural for the * underlying phrase here - we can't accept an adjective phrase. */ grammar qualifiedPluralNounPhrase(determiner): ('any' | ) detPluralOnlyNounPhrase->np_ : LayeredNounPhraseProd ; /* plural phrase qualified with a number and optional "any" */ grammar qualifiedPluralNounPhrase(anyNum): ('any' | ) numberPhrase->quant_ indetPluralNounPhrase->np_ | ('any' | ) numberPhrase->quant_ 'of' explicitDetPluralNounPhrase->np_ : QuantifiedPluralProd ; /* plural phrase qualified with a number and "all" */ grammar qualifiedPluralNounPhrase(allNum): 'all' numberPhrase->quant_ indetPluralNounPhrase->np_ | 'all' numberPhrase->quant_ 'of' explicitDetPluralNounPhrase->np_ : ExactQuantifiedPluralProd ; /* plural phrase qualified with "both" */ grammar qualifiedPluralNounPhrase(both): 'both' detPluralNounPhrase->np_ | 'both' 'of' explicitDetPluralNounPhrase->np_ : BothPluralProd ; /* plural phrase qualified with "all" */ grammar qualifiedPluralNounPhrase(all): 'all' detPluralNounPhrase->np_ | 'all' 'of' explicitDetPluralNounPhrase->np_ : AllPluralProd ; /* vague plural phrase with location specified */ grammar qualifiedPluralNounPhrase(theOnesIn): ('the' 'ones' ('that' ('are' | 'were') | ) | ('everything' | 'all') ('that' ('is' | 'was') | 'that' tokApostropheS | )) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : AllInContainerNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * A plural noun phrase with a determiner. The determiner can be * explicit (such as an article or possessive) or it can implied (the * implied determiner is the definite article, so "take boxes" is * understood as "take the boxes"). */ grammar detPluralNounPhrase(main): indetPluralNounPhrase->np_ | explicitDetPluralNounPhrase->np_ : LayeredNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * A determiner plural phrase with an explicit underlying plural (i.e., * excluding adjective phrases with no explicitly plural words). */ grammar detPluralOnlyNounPhrase(main): implicitDetPluralOnlyNounPhrase->np_ | explicitDetPluralOnlyNounPhrase->np_ : LayeredNounPhraseProd ; /* * An implicit determiner plural phrase is an indeterminate plural phrase * without any extra determiner - i.e., the determiner is implicit. * We'll treat this the same way we do a plural explicitly determined * with a definite article, since this is the most natural interpretation * in English. * * (This might seem like a pointless extra layer in the grammar, but it's * necessary for the resolution process to have a layer that explicitly * declares the phrase to be determined, even though the determiner is * implied in the structure. This extra layer is important because it * explicitly calls results.noteMatches(), which is needed for rankings * and the like.) */ grammar implicitDetPluralOnlyNounPhrase(main): indetPluralOnlyNounPhrase->np_ : DefinitePluralProd ; /* ------------------------------------------------------------------------ */ /* * A plural noun phrase with an explicit determiner. */ /* a plural noun phrase with a definite article */ grammar explicitDetPluralNounPhrase(definite): 'the' indetPluralNounPhrase->np_ : DefinitePluralProd ; /* a plural noun phrase with a definite article and a number */ grammar explicitDetPluralNounPhrase(definiteNumber): 'the' numberPhrase->quant_ indetPluralNounPhrase->np_ : ExactQuantifiedPluralProd ; /* a plural noun phrase with a possessive */ grammar explicitDetPluralNounPhrase(possessive): possessiveAdjPhrase->poss_ indetPluralNounPhrase->np_ : PossessivePluralProd ; /* a plural noun phrase with a possessive and a number */ grammar explicitDetPluralNounPhrase(possessiveNumber): possessiveAdjPhrase->poss_ numberPhrase->quant_ indetPluralNounPhrase->np_ : ExactQuantifiedPossessivePluralProd ; /* ------------------------------------------------------------------------ */ /* * A plural noun phrase with an explicit determiner and only an * explicitly plural underlying phrase. */ grammar explicitDetPluralOnlyNounPhrase(definite): 'the' indetPluralOnlyNounPhrase->np_ : AllPluralProd ; grammar explicitDetPluralOnlyNounPhrase(definiteNumber): 'the' numberPhrase->quant_ indetPluralNounPhrase->np_ : ExactQuantifiedPluralProd ; grammar explicitDetPluralOnlyNounPhrase(possessive): possessiveAdjPhrase->poss_ indetPluralOnlyNounPhrase->np_ : PossessivePluralProd ; grammar explicitDetPluralOnlyNounPhrase(possessiveNumber): possessiveAdjPhrase->poss_ numberPhrase->quant_ indetPluralNounPhrase->np_ : ExactQuantifiedPossessivePluralProd ; /* ------------------------------------------------------------------------ */ /* * An indeterminate plural noun phrase. * * For the basic indeterminate plural phrase, allow an adjective phrase * anywhere a plural phrase is allowed; this makes possible the * short-hand of omitting a plural word when the plural number is * unambiguous from context. */ /* a simple plural noun phrase */ grammar indetPluralNounPhrase(basic): pluralPhrase->np_ | adjPhrase->np_ : LayeredNounPhraseProd ; /* * A plural noun phrase with a locational qualifier. Note that even * though the overall phrase is plural (and so the main underlying noun * phrase is plural), the location phrase itself must always be singular. */ grammar indetPluralNounPhrase(locational): (pluralPhrase->np_ | adjPhrase->np_) ('that' ('are' | 'were') | ) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : ContainerNounPhraseProd ; /* * An indetermine plural noun phrase with only explicit plural phrases. */ grammar indetPluralOnlyNounPhrase(basic): pluralPhrase->np_ : LayeredNounPhraseProd ; grammar indetPluralOnlyNounPhrase(locational): pluralPhrase->np_ ('that' ('are' | 'were') | ) ('in' | 'inside' | 'inside' 'of' | 'on' | 'from') completeNounPhraseWithoutAll->cont_ : ContainerNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * Noun Phrase. This is the basic noun phrase, which serves as a * building block for complete noun phrases. This type of noun phrase * can be qualified with articles, quantifiers, and possessives, and can * be used to construct possessives via the addition of "'s" at the end * of the phrase. * * In most cases, custom noun phrase rules should be added to this * production, as long as qualification (with numbers, articles, and * possessives) is allowed. For a custom noun phrase rule that cannot be * qualified, a completeNounPhrase rule should be added instead. */ grammar nounPhrase(main): compoundNounPhrase->np_ : LayeredNounPhraseProd ; /* * Plural phrase. This is the basic plural phrase, and corresponds to * the basic nounPhrase for plural forms. */ grammar pluralPhrase(main): compoundPluralPhrase->np_ : LayeredNounPhraseProd ; /* ------------------------------------------------------------------------ */ /* * Compound noun phrase. This is one or more noun phrases connected with * 'of', as in "piece of paper". The part after the 'of' is another * compound noun phrase. * * Note that this general rule does not allow the noun phrase after the * 'of' to be qualified with an article or number, except that we make an * exception to allow a definite article. Other cases ("a piece of four * papers") do not generally make sense, so we won't attempt to support * them; instead, games can add as special cases new nounPhrase rules for * specific literal sequences where more complex grammar is necessary. */ grammar compoundNounPhrase(simple): simpleNounPhrase->np_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { return np_.getVocabMatchList(resolver, results, extraFlags); } getAdjustedTokens() { return np_.getAdjustedTokens(); } ; grammar compoundNounPhrase(of): simpleNounPhrase->np1_ 'of'->of_ compoundNounPhrase->np2_ | simpleNounPhrase->np1_ 'of'->of_ 'the'->the_ compoundNounPhrase->np2_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local lst1; local lst2; /* resolve the two underlying lists */ lst1 = np1_.getVocabMatchList(resolver, results, extraFlags); lst2 = np2_.getVocabMatchList(resolver, results, extraFlags); /* * the result is the intersection of the two lists, since we * want the list of objects with all of the underlying * vocabulary words */ return intersectNounLists(lst1, lst2); } getAdjustedTokens() { local ofLst; /* generate the 'of the' list from the original words */ if (the_ == nil) ofLst = [of_, &miscWord]; else ofLst = [of_, &miscWord, the_, &miscWord]; /* return the full list */ return np1_.getAdjustedTokens() + ofLst + np2_.getAdjustedTokens(); } ; /* ------------------------------------------------------------------------ */ /* * Compound plural phrase - same as a compound noun phrase, but involving * a plural part before the 'of'. */ /* * just a single plural phrase */ grammar compoundPluralPhrase(simple): simplePluralPhrase->np_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { return np_.getVocabMatchList(resolver, results, extraFlags); } getAdjustedTokens() { return np_.getAdjustedTokens(); } ; /* * of */ grammar compoundPluralPhrase(of): simplePluralPhrase->np1_ 'of'->of_ compoundNounPhrase->np2_ | simplePluralPhrase->np1_ 'of'->of_ 'the'->the_ compoundNounPhrase->np2_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local lst1; local lst2; /* resolve the two underlying lists */ lst1 = np1_.getVocabMatchList(resolver, results, extraFlags); lst2 = np2_.getVocabMatchList(resolver, results, extraFlags); /* * the result is the intersection of the two lists, since we * want the list of objects with all of the underlying * vocabulary words */ return intersectNounLists(lst1, lst2); } getAdjustedTokens() { local ofLst; /* generate the 'of the' list from the original words */ if (the_ == nil) ofLst = [of_, &miscWord]; else ofLst = [of_, &miscWord, the_, &miscWord]; /* return the full list */ return np1_.getAdjustedTokens() + ofLst + np2_.getAdjustedTokens(); } ; /* ------------------------------------------------------------------------ */ /* * Simple noun phrase. This is the most basic noun phrase, which is * simply a noun, optionally preceded by one or more adjectives. */ /* * just a noun */ grammar simpleNounPhrase(noun): nounWord->noun_ : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { return noun_.getVocabMatchList(resolver, results, extraFlags); } getAdjustedTokens() { return noun_.getAdjustedTokens(); } ; /* * (this allows any number of adjectives * to be applied) */ grammar simpleNounPhrase(adjNP): adjWord->adj_ simpleNounPhrase->np_ : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* * return the list of objects in scope matching our adjective * plus the list from the underlying noun phrase */ return intersectNounLists( adj_.getVocabMatchList(resolver, results, extraFlags), np_.getVocabMatchList(resolver, results, extraFlags)); } getAdjustedTokens() { return adj_.getAdjustedTokens() + np_.getAdjustedTokens(); } ; /* * A simple noun phrase can also include a number or a quoted string * before or after a noun. A number can be spelled out or written with * numerals; we consider both forms equivalent in meaning. * * A number in this type of usage is grammatically equivalent to an * adjective - it's not meant to quantify the rest of the noun phrase, * but rather is simply an adjective-like modifier. For example, an * elevator's control panel might have a set of numbered buttons which we * want to refer to as "button 1," "button 2," and so on. It is * frequently the case that numeric adjectives are equally at home before * or after their noun: "push 3 button" or "push button 3". In addition, * we accept a number by itself as a lone adjective, as in "push 3". */ /* * just a numeric/string adjective (for things like "push 3", "push #3", * 'push "G"') */ grammar simpleNounPhrase(number): literalAdjPhrase->adj_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* * note that this counts as an adjective-ending phrase, since we * don't have a noun involved */ results.noteAdjEnding(); /* pass through to the underlying literal adjective phrase */ local lst = adj_.getVocabMatchList(resolver, results, extraFlags | EndsWithAdj); /* if in global scope, also try a noun interpretation */ if (resolver.isGlobalScope) lst = adj_.addNounMatchList(lst, resolver, results, extraFlags); /* return the result */ return lst; } getAdjustedTokens() { /* pass through to the underlying literal adjective phrase */ return adj_.getAdjustedTokens(); } ; /* * (for things like "board 44 bus" or 'push * "G" button') */ grammar simpleNounPhrase(numberAndNoun): literalAdjPhrase->adj_ nounWord->noun_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local nounList; local adjList; /* get the list of objects matching the rest of the noun phrase */ nounList = noun_.getVocabMatchList(resolver, results, extraFlags); /* get the list of objects matching the literal adjective */ adjList = adj_.getVocabMatchList(resolver, results, extraFlags); /* intersect the two lists and return the results */ return intersectNounLists(nounList, adjList); } getAdjustedTokens() { return adj_.getAdjustedTokens() + noun_.getAdjustedTokens(); } ; /* * (for things like "press button 3" or 'put * tab "A" in slot "B"') */ grammar simpleNounPhrase(nounAndNumber): nounWord->noun_ literalAdjPhrase->adj_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local nounList; local adjList; /* get the list of objects matching the rest of the noun phrase */ nounList = noun_.getVocabMatchList(resolver, results, extraFlags); /* get the literal adjective matches */ adjList = adj_.getVocabMatchList(resolver, results, extraFlags); /* intersect the two lists and return the results */ return intersectNounLists(nounList, adjList); } getAdjustedTokens() { return noun_.getAdjustedTokens() + adj_.getAdjustedTokens(); } ; /* * A simple noun phrase can also end in an adjective, which allows * players to refer to objects using only their unique adjectives rather * than their full names, which is sometimes more convenient: just "take * gold" rather than "take gold key." * * When a particular phrase can be interpreted as either ending in an * adjective or ending in a noun, we will always take the noun-ending * interpretation - in such cases, the adjective-ending interpretation is * probably a weak binding. For example, "take pizza" almost certainly * refers to the pizza itself when "pizza" and "pizza box" are both * present, but it can also refer just to the box when no pizza is * present. * * Equivalent to a noun phrase ending in an adjective is a noun phrase * ending with an adjective followed by "one," as in "the red one." */ grammar simpleNounPhrase(adj): adjWord->adj_ : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* note in the results that we end in an adjective */ results.noteAdjEnding(); /* generate a list of objects matching the adjective */ local lst = adj_.getVocabMatchList( resolver, results, extraFlags | EndsWithAdj); /* if in global scope, also try a noun interpretation */ if (resolver.isGlobalScope) lst = adj_.addNounMatchList(lst, resolver, results, extraFlags); /* return the result */ return lst; } getAdjustedTokens() { /* return the adjusted token list for the adjective */ return adj_.getAdjustedTokens(); } ; grammar simpleNounPhrase(adjAndOne): adjective->adj_ 'one' : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* * This isn't exactly an adjective ending, but consider it as * such anyway, since we're not matching 'one' to a vocabulary * word - we're just using it as a grammatical marker that we're * not providing a real noun. If there's another match for * which 'one' is a noun, that one is definitely preferred to * this one; the adj-ending marking will ensure that we choose * the other one. */ results.noteAdjEnding(); /* generate a list of objects matching the adjective */ return getWordMatches(adj_, &adjective, resolver, extraFlags | EndsWithAdj, VocabTruncated); } getAdjustedTokens() { return [adj_, &adjective]; } ; /* * In the worst case, a simple noun phrase can be constructed from * arbitrary words that don't appear in our dictionary. */ grammar simpleNounPhrase(misc): [badness 200] miscWordList->lst_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* get the match list from the underlying list */ local lst = lst_.getVocabMatchList(resolver, results, extraFlags); /* * If there are no matches, note in the results that we have an * arbitrary word list. Note that we do this only if there are * no matches, because we might match non-dictionary words to an * object with a wildcard in its vocabulary words, in which case * this is a valid, matching phrase after all. */ if (lst == nil || lst.length() == 0) results.noteMiscWordList(lst_.getOrigText()); /* return the match list */ return lst; } getAdjustedTokens() { return lst_.getAdjustedTokens(); } ; /* * If the command has qualifiers but omits everything else, we can have * an empty simple noun phrase. */ grammar simpleNounPhrase(empty): [badness 600] : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* we have an empty noun phrase */ return results.emptyNounPhrase(resolver); } getAdjustedTokens() { return []; } ; /* ------------------------------------------------------------------------ */ /* * An AdjPhraseWithVocab is an English-specific subclass of * NounPhraseWithVocab, specifically for noun phrases that contain * entirely adjectives. */ class AdjPhraseWithVocab: NounPhraseWithVocab /* the property for the adjective literal - this is usually adj_ */ adjVocabProp = &adj_ /* * Add the vocabulary matches that we'd get if we were treating our * adjective as a noun. This combines the noun interpretation with a * list of matches we got for the adjective version. */ addNounMatchList(lst, resolver, results, extraFlags) { /* get the word matches with a noun interpretation of our adjective */ local nLst = getWordMatches( self.(adjVocabProp), &noun, resolver, extraFlags, VocabTruncated); /* combine the lists and return the result */ return combineWordMatches(lst, nLst); } ; /* ------------------------------------------------------------------------ */ /* * A "literal adjective" phrase is a number or string used as an * adjective. */ grammar literalAdjPhrase(number): numberPhrase->num_ | poundNumberPhrase->num_ : AdjPhraseWithVocab adj_ = (num_.getStrVal()) getVocabMatchList(resolver, results, extraFlags) { local numList; /* * get the list of objects matching the numeral form of the * number as an adjective */ numList = getWordMatches(num_.getStrVal(), &adjective, resolver, extraFlags, VocabTruncated); /* add the list of objects matching the special '#' wildcard */ numList += getWordMatches('#', &adjective, resolver, extraFlags, VocabTruncated); /* return the combined lists */ return numList; } getAdjustedTokens() { return [num_.getStrVal(), &adjective]; } ; grammar literalAdjPhrase(string): quotedStringPhrase->str_ : AdjPhraseWithVocab adj_ = (str_.getStringText().toLower()) getVocabMatchList(resolver, results, extraFlags) { local strList; local wLst; /* * get the list of objects matching the string with the quotes * removed */ strList = getWordMatches(str_.getStringText().toLower(), &literalAdjective, resolver, extraFlags, VocabTruncated); /* add the list of objects matching the literal-adjective wildcard */ wLst = getWordMatches('\u0001', &literalAdjective, resolver, extraFlags, VocabTruncated); strList = combineWordMatches(strList, wLst); /* return the combined lists */ return strList; } getAdjustedTokens() { return [str_.getStringText().toLower(), &adjective]; } ; /* * In many cases, we might want to write what is semantically a literal * string qualifier without the quotes. For example, we might want to * refer to an elevator button that's labeled "G" as simply "button G", * without any quotes around the "G". To accommodate these cases, we * provide the literalAdjective part-of-speech. We'll match these parts * of speech the same way we'd match them if they were quoted. */ grammar literalAdjPhrase(literalAdj): literalAdjective->adj_ : AdjPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local lst; /* get a list of objects in scope matching our literal adjective */ lst = getWordMatches(adj_, &literalAdjective, resolver, extraFlags, VocabTruncated); /* if the scope is global, also include ordinary adjective matches */ if (resolver.isGlobalScope) { /* get the ordinary adjective bindings */ local aLst = getWordMatches(adj_, &adjective, resolver, extraFlags, VocabTruncated); /* global scope - combine the lists */ lst = combineWordMatches(lst, aLst); } /* return the result */ return lst; } getAdjustedTokens() { return [adj_, &literalAdjective]; } ; /* ------------------------------------------------------------------------ */ /* * A noun word. This can be either a simple 'noun' vocabulary word, or * it can be an abbreviated noun with a trailing abbreviation period. */ class NounWordProd: NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { local w; local nLst; /* get our word text */ w = getNounText(); /* get the list of matches as nouns */ nLst = getWordMatches(w, &noun, resolver, extraFlags, VocabTruncated); /* * If the resolver indicates that we're in a "global" scope, * *also* include any additional matches as adjectives. * * Normally, when we're operating in limited, local scopes, we * use the structure of the phrasing to determine whether to * match a noun or adjective; if we have a match for a given word * as a noun, we'll treat it only as a noun. This allows us to * take PIZZA to refer to the pizza (for which 'pizza' is defined * as a noun) rather than to the PIZZA BOX (for which 'pizza' is * a mere adjective) when both are in scope. It's obvious which * the player means in such cases, so we can be smart about * choosing the stronger match. * * In cases of global scope, though, it's much harder to guess * about the player's intentions. When the player types PIZZA, * they might be thinking of the box even though there's a pizza * somewhere else in the game. Since the two objects might be in * entirely different locations, both out of view, we can't * assume that one or the other is more likely on the basis of * which is closer to the player's senses. So, it's better to * allow both to match for now, and decide later, based on the * context of the command, which was actually meant. */ if (resolver.isGlobalScope) { /* get the list of matching adjectives */ local aLst = getWordMatches(w, &adjective, resolver, extraFlags, VocabTruncated); /* combine it with the noun list */ nLst = combineWordMatches(nLst, aLst); } /* return the match list */ return nLst; } getAdjustedTokens() { /* the noun includes the period as part of the literal text */ return [getNounText(), &noun]; } /* the actual text of the noun to match to the dictionary */ getNounText() { return noun_; } ; grammar nounWord(noun): noun->noun_ : NounWordProd ; grammar nounWord(nounAbbr): noun->noun_ tokAbbrPeriod->period_ : NounWordProd /* * for dictionary matching purposes, include the text of our noun * with the period attached - the period is part of the dictionary * entry for an abbreviated word */ getNounText() { return noun_ + period_; } ; /* ------------------------------------------------------------------------ */ /* * An adjective word. This can be either a simple 'adjective' vocabulary * word, or it can be an 'adjApostS' vocabulary word plus a 's token. */ grammar adjWord(adj): adjective->adj_ : AdjPhraseWithVocab /* generate a list of resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* return a list of objects in scope matching our adjective */ return getWordMatches(adj_, &adjective, resolver, extraFlags, VocabTruncated); } getAdjustedTokens() { return [adj_, &adjective]; } ; grammar adjWord(adjApostS): adjApostS->adj_ tokApostropheS->apost_ : AdjPhraseWithVocab /* generate a list of resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* return a list of objects in scope matching our adjective */ return getWordMatches(adj_, &adjApostS, resolver, extraFlags, VocabTruncated); } getAdjustedTokens() { return [adj_, &adjApostS]; } ; grammar adjWord(adjAbbr): adjective->adj_ tokAbbrPeriod->period_ : AdjPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* * return the list matching our adjective *with* the period * attached; the period is part of the dictionary entry for an * abbreviated word */ return getWordMatches(adj_ + period_, &adjective, resolver, extraFlags, VocabTruncated); } getAdjustedTokens() { /* the adjective includes the period as part of the literal text */ return [adj_ + period_, &adjective]; } ; /* ------------------------------------------------------------------------ */ /* * Possessive phrase. This is a noun phrase expressing ownership of * another object. * * Note that all possessive phrases that can possibly be ambiguous must * define getOrigMainText() to return the "main noun phrase" text. In * English, this means that we must omit any "'s" suffix. This is needed * only when the phrase can be ambiguous, so pronouns don't need it since * they are inherently unambiguous. */ grammar possessiveAdjPhrase(its): 'its' : ItsAdjProd /* we only agree with a singular ungendered noun */ checkAnaphorAgreement(lst) { return lst.length() == 1 && lst[1].obj_.canMatchIt; } ; grammar possessiveAdjPhrase(his): 'his' : HisAdjProd /* we only agree with a singular masculine noun */ checkAnaphorAgreement(lst) { return lst.length() == 1 && lst[1].obj_.canMatchHim; } ; grammar possessiveAdjPhrase(her): 'her' : HerAdjProd /* we only agree with a singular feminine noun */ checkAnaphorAgreement(lst) { return lst.length() == 1 && lst[1].obj_.canMatchHer; } ; grammar possessiveAdjPhrase(their): 'their' : TheirAdjProd /* we only agree with a single noun that has plural usage */ checkAnaphorAgreement(lst) { return lst.length() == 1 && lst[1].obj_.isPlural; } ; grammar possessiveAdjPhrase(your): 'your' : YourAdjProd /* we are non-anaphoric */ checkAnaphorAgreement(lst) { return nil; } ; grammar possessiveAdjPhrase(my): 'my' : MyAdjProd /* we are non-anaphoric */ checkAnaphorAgreement(lst) { return nil; } ; grammar possessiveAdjPhrase(npApostropheS): ('the' | ) nounPhrase->np_ tokApostropheS->apost_ : LayeredNounPhraseProd /* get the original text without the "'s" suffix */ getOrigMainText() { /* return just the basic noun phrase part */ return np_.getOrigText(); } ; grammar possessiveAdjPhrase(ppApostropheS): ('the' | ) pluralPhrase->np_ (tokApostropheS->apost_ | tokPluralApostrophe->apost_) : LayeredNounPhraseProd /* get the original text without the "'s" suffix */ getOrigMainText() { /* return just the basic noun phrase part */ return np_.getOrigText(); } resolveNouns(resolver, results) { /* note that we have a plural phrase, structurally speaking */ results.notePlural(); /* inherit the default handling */ return inherited(resolver, results); } /* the possessive phrase is plural */ isPluralPossessive = true ; /* * Possessive noun phrases. These are similar to possessive phrases, but * are stand-alone phrases that can act as nouns rather than as * qualifiers for other noun phrases. For example, for a first-person * player character, "mine" would be a possessive noun phrase referring * to an object owned by the player character. * * Note that many of the words used for possessive nouns are the same as * for possessive adjectives - for example "his" is the same in either * case, as are "'s" words. However, we make the distinction internally * because the two have different grammatical uses, and some of the words * do differ ("her" vs "hers", for example). */ grammar possessiveNounPhrase(its): 'its': ItsNounProd; grammar possessiveNounPhrase(his): 'his': HisNounProd; grammar possessiveNounPhrase(hers): 'hers': HersNounProd; grammar possessiveNounPhrase(theirs): 'theirs': TheirsNounProd; grammar possessiveNounPhrase(yours): 'yours' : YoursNounProd; grammar possessiveNounPhrase(mine): 'mine' : MineNounProd; grammar possessiveNounPhrase(npApostropheS): ('the' | ) (nounPhrase->np_ tokApostropheS->apost_ | pluralPhrase->np (tokApostropheS->apost_ | tokPluralApostrophe->apost_)) : LayeredNounPhraseProd /* get the original text without the "'s" suffix */ getOrigMainText() { /* return just the basic noun phrase part */ return np_.getOrigText(); } ; /* ------------------------------------------------------------------------ */ /* * Simple plural phrase. This is the most basic plural phrase, which is * simply a plural noun, optionally preceded by one or more adjectives. * * (English doesn't have any sort of adjective declension in number, so * there's no need to distinguish between plural and singular adjectives; * this equivalent rule in languages with adjective-noun agreement in * number would use plural adjectives here as well as plural nouns.) */ grammar simplePluralPhrase(plural): plural->plural_ : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { local lst; /* get the list of matching plurals */ lst = getWordMatches(plural_, &plural, resolver, extraFlags, PluralTruncated); /* get the list of matching 'noun' definitions */ local nLst = getWordMatches(plural_, &noun, resolver, extraFlags, VocabTruncated); /* get the combined list */ local comboLst = combineWordMatches(lst, nLst); /* * If we're in global scope, add in the matches for just plain * 'noun' properties as well. This is important because we'll * sometimes want to define a word that's actually a plural * usage (in terms of the real-world English) under the 'noun' * property. This occurs particularly when a single game-world * object represents a multiplicity of real-world objects. When * the scope is global, it's hard to anticipate all of the * possible interactions with vocabulary along these lines, so * it's easiest just to include the 'noun' matches. */ if (resolver.isGlobalScope) { /* keep the combined list */ lst = comboLst; } else if (comboLst.length() > lst.length()) { /* * ordinary scope, so don't include the noun matches; but * since we found extra items to add, at least mark the * plural matches as potentially ambiguous */ lst.forEach({x: x.flags_ |= UnclearDisambig}); } /* return the result list */ return lst; } getAdjustedTokens() { return [plural_, &plural]; } ; grammar simplePluralPhrase(adj): adjWord->adj_ simplePluralPhrase->np_ : NounPhraseWithVocab /* resolve my object list */ getVocabMatchList(resolver, results, extraFlags) { /* * return the list of objects in scope matching our adjective * plus the list from the underlying noun phrase */ return intersectNounLists( adj_.getVocabMatchList(resolver, results, extraFlags), np_.getVocabMatchList(resolver, results, extraFlags)); } getAdjustedTokens() { return adj_.getAdjustedTokens() + np_.getAdjustedTokens(); } ; grammar simplePluralPhrase(poundNum): poundNumberPhrase->num_ simplePluralPhrase->np_ : NounPhraseWithVocab /* resolve my object list */ getVocabMatchList(resolver, results, extraFlags) { local baseList; local numList; /* get the base list for the rest of the phrase */ baseList = np_.getVocabMatchList(resolver, results, extraFlags); /* get the numeric matches, including numeric wildcards */ numList = getWordMatches(num_.getStrVal(), &adjective, resolver, extraFlags, VocabTruncated) + getWordMatches('#', &adjective, resolver, extraFlags, VocabTruncated); /* return the intersection of the lists */ return intersectNounLists(numList, baseList); } getAdjustedTokens() { return [num_.getStrVal(), &adjective] + np_.getAdjustedTokens(); } ; /* * A simple plural phrase can end with an adjective and "ones," as in * "the red ones." */ grammar simplePluralPhrase(adjAndOnes): adjective->adj_ 'ones' : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* generate a list of objects matching the adjective */ return getWordMatches(adj_, &adjective, resolver, extraFlags | EndsWithAdj, VocabTruncated); } getAdjustedTokens() { return [adj_, &adjective]; } ; /* * If the command has qualifiers that require a plural, but omits * everything else, we can have an empty simple noun phrase. */ grammar simplePluralPhrase(empty): [badness 600] : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* we have an empty noun phrase */ return results.emptyNounPhrase(resolver); } getAdjustedTokens() { return []; } ; /* * A simple plural phrase can match unknown words as a last resort. */ grammar simplePluralPhrase(misc): [badness 300] miscWordList->lst_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* get the match list from the underlying list */ local lst = lst_.getVocabMatchList(resolver, results, extraFlags); /* * if there are no matches, note in the results that we have an * arbitrary word list that doesn't correspond to any object */ if (lst == nil || lst.length() == 0) results.noteMiscWordList(lst_.getOrigText()); /* return the vocabulary match list */ return lst; } getAdjustedTokens() { return lst_.getAdjustedTokens(); } ; /* ------------------------------------------------------------------------ */ /* * An "adjective phrase" is a phrase made entirely of adjectives. */ grammar adjPhrase(adj): adjective->adj_ : AdjPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* note the adjective ending */ results.noteAdjEnding(); /* return the match list */ local lst = getWordMatches(adj_, &adjective, resolver, extraFlags | EndsWithAdj, VocabTruncated); /* if in global scope, also try a noun interpretation */ if (resolver.isGlobalScope) lst = addNounMatchList(lst, resolver, results, extraFlags); /* return the result */ return lst; } getAdjustedTokens() { return [adj_, &adjective]; } ; grammar adjPhrase(adjAdj): adjective->adj_ adjPhrase->ap_ : NounPhraseWithVocab /* generate a list of my resolved objects */ getVocabMatchList(resolver, results, extraFlags) { /* * return the list of objects in scope matching our adjective * plus the list from the underlying adjective phrase */ return intersectWordMatches( adj_, &adjective, resolver, extraFlags, VocabTruncated, ap_.getVocabMatchList(resolver, results, extraFlags)); } getAdjustedTokens() { return [adj_, &adjective] + ap_.getAdjustedTokens(); } ; /* ------------------------------------------------------------------------ */ /* * A "topic" is a special type of noun phrase used in commands like "ask * about ." We define a topic as simply an ordinary * single-noun phrase. We distinguish this in the grammar to allow games * to add special syntax for these. */ grammar topicPhrase(main): singleNoun->np_ : TopicProd ; /* * Explicitly match a miscellaneous word list as a topic. * * This might seem redundant with the ordinary topicPhrase that accepts a * singleNoun, because singleNoun can match a miscellaneous word list. * The difference is that singleNoun only matches a miscWordList with a * "badness" value, whereas we match a miscWordList here without any * badness. We want to be more tolerant of unrecognized input in topic * phrases than in ordinary noun phrases, because it's in the nature of * topic phrases to go outside of what's implemented directly in the * simulation model. At a grammatical level, we don't want to treat * topic phrases that we can resolve to the simulation model any * differently than we treat those we can't resolve, so we must add this * rule to eliminate the badness that singleNoun associated with a * miscWordList match. * * Note that we do prefer resolvable noun phrase matches to miscWordList * matches, but we handle this preference with the resolver's scoring * mechanism rather than with badness. */ grammar topicPhrase(misc): miscWordList->np_ : TopicProd resolveNouns(resolver, results) { /* note in the results that we have an arbitrary word list */ results.noteMiscWordList(np_.getOrigText()); /* inherit the default TopicProd behavior */ return inherited(resolver, results); } ; /* ------------------------------------------------------------------------ */ /* * A "quoted string" phrase is a literal enclosed in single or double * quotes. * * Note that this is a separate production from literalPhrase. This * production can be used when *only* a quoted string is allowed. The * literalPhrase production allows both quoted and unquoted text. */ grammar quotedStringPhrase(main): tokString->str_ : LiteralProd /* * get my string, with the quotes trimmed off (so we return simply * the contents of the string) */ getStringText() { return stripQuotesFrom(str_); } ; /* * Service routine: strip quotes from a *possibly* quoted string. If the * string starts with a quote, we'll remove the open quote. If it starts * with a quote and it ends with a corresponding close quote, we'll * remove that as well. */ stripQuotesFrom(str) { local hasOpen; local hasClose; /* presume we won't find open or close quotes */ hasOpen = hasClose = nil; /* * Check for quotes. We'll accept regular ASCII "straight" single * or double quotes, as well as Latin-1 curly single or double * quotes. The curly quotes must be used in their normal */ if (str.startsWith('\'') || str.startsWith('"')) { /* single or double quote - check for a matching close quote */ hasOpen = true; hasClose = (str.length() > 2 && str.endsWith(str.substr(1, 1))); } else if (str.startsWith('`')) { /* single in-slanted quote - check for either type of close */ hasOpen = true; hasClose = (str.length() > 2 && (str.endsWith('`') || str.endsWith('\''))); } else if (str.startsWith('\u201C')) { /* it's a curly double quote */ hasOpen = true; hasClose = str.endsWith('\u201D'); } else if (str.startsWith('\u2018')) { /* it's a curly single quote */ hasOpen = true; hasClose = str.endsWith('\u2019'); } /* trim off the quotes */ if (hasOpen) { if (hasClose) str = str.substr(2, str.length() - 2); else str = str.substr(2); } /* return the modified text */ return str; } /* ------------------------------------------------------------------------ */ /* * A "literal" is essentially any phrase. This can include a quoted * string, a number, or any set of word tokens. */ grammar literalPhrase(string): quotedStringPhrase->str_ : LiteralProd getLiteralText(results, action, which) { /* get the text from our underlying quoted string */ return str_.getStringText(); } getTentativeLiteralText() { /* * our result will never change, so our tentative text is the * same as our regular literal text */ return str_.getStringText(); } resolveLiteral(results) { /* flag the literal text */ results.noteLiteral(str_.getOrigText()); } ; grammar literalPhrase(miscList): miscWordList->misc_ : LiteralProd getLiteralText(results, action, which) { /* get my original text */ local txt = misc_.getOrigText(); /* * if our underlying miscWordList has only one token, strip * quotes, in case that token is a quoted string token */ if (misc_.getOrigTokenList().length() == 1) txt = stripQuotesFrom(txt); /* return the text */ return txt; } getTentativeLiteralText() { /* our regular text is permanent, so simply use it now */ return misc_.getOrigText(); } resolveLiteral(results) { /* * note the length of our literal phrase - when we have a choice * of interpretations, we prefer to choose shorter literal * phrases, since this means that we'll have more of our tokens * being fully interpreted rather than bunched into an * uninterpreted literal */ results.noteLiteral(misc_.getOrigText()); } ; /* * In case we have a verb grammar rule that calls for a literal phrase, * but the player enters a command with nothing in that slot, match an * empty token list as a last resort. Since this phrasing has a badness, * we won't match it unless we don't have any better structural match. */ grammar literalPhrase(empty): [badness 400]: EmptyLiteralPhraseProd resolveLiteral(results) { } ; /* ------------------------------------------------------------------------ */ /* * An miscellaneous word list is a list of one or more words of any kind: * any word, any integer, or any apostrophe-S token will do. Note that * known and unknown words can be mixed in an unknown word list; we care * only that the list is made up of tokWord, tokInt, tokApostropheS, * and/or abbreviation-period tokens. * * Note that this kind of phrase is often used with a 'badness' value. * However, we don't assign any badness here, because a miscellaneous * word list might be perfectly valid in some contexts; instead, any * productions that include a misc word list should specify badness as * desired. */ grammar miscWordList(wordOrNumber): tokWord->txt_ | tokInt->txt_ | tokApostropheS->txt_ | tokPluralApostrophe->txt_ | tokPoundInt->txt_ | tokString->txt_ | tokAbbrPeriod->txt_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* we don't match anything directly with our vocabulary */ return []; } getAdjustedTokens() { /* our token type is the special miscellaneous word type */ return [txt_, &miscWord]; } ; grammar miscWordList(list): (tokWord->txt_ | tokInt->txt_ | tokApostropheS->txt_ | tokPluralApostrophe->txt_ | tokAbbrPeriod->txt_ | tokPoundInt->txt_ | tokString->txt_) miscWordList->lst_ : NounPhraseWithVocab getVocabMatchList(resolver, results, extraFlags) { /* we don't match anything directly with our vocabulary */ return []; } getAdjustedTokens() { /* our token type is the special miscellaneous word type */ return [txt_, &miscWord] + lst_.getAdjustedTokens(); } ; /* ------------------------------------------------------------------------ */ /* * A main disambiguation phrase consists of a disambiguation phrase, * optionally terminated with a period. */ grammar mainDisambigPhrase(main): disambigPhrase->dp_ | disambigPhrase->dp_ '.' : BasicProd resolveNouns(resolver, results) { return dp_.resolveNouns(resolver, results); } getResponseList() { return dp_.getResponseList(); } ; /* * A "disambiguation phrase" is a phrase that answers a disambiguation * question ("which book do you mean..."). * * A disambiguation question can be answered with several types of * syntax: * *. all/everything/all of them *. both/both of them *. any/any of them *. *. the ones *. the former/the latter * * Note that we assign non-zero badness to all of the ordinal * interpretations, so that we will take an actual vocabulary * interpretation instead of an ordinal interpretation whenever possible. * For example, if an object's name is actually "the third button," this * will give us greater affinity for using "third" as an adjective than * as an ordinal in our own list. */ grammar disambigPhrase(all): 'all' | 'everything' | 'all' 'of' 'them' : DisambigProd resolveNouns(resolver, results) { /* they want everything we proposed - return the whole list */ return removeAmbigFlags(resolver.getAll(self)); } /* there's only me in the response list */ getResponseList() { return [self]; } ; grammar disambigPhrase(both): 'both' | 'both' 'of' 'them' : DisambigProd resolveNouns(resolver, results) { /* * they want two items - return the whole list (if it has more * than two items, we'll simply act as though they wanted all of * them) */ return removeAmbigFlags(resolver.getAll(self)); } /* there's only me in the response list */ getResponseList() { return [self]; } ; grammar disambigPhrase(any): 'any' | 'any' 'of' 'them' : DisambigProd resolveNouns(resolver, results) { local lst; /* they want any item - arbitrarily pick the first one */ lst = resolver.matchList.sublist(1, 1); /* * add the "unclear disambiguation" flag to the item we picked, * to indicate that the selection was arbitrary */ if (lst.length() > 0) lst[1].flags_ |= UnclearDisambig; /* return the result */ return lst; } /* there's only me in the response list */ getResponseList() { return [self]; } ; grammar disambigPhrase(list): disambigList->lst_ : DisambigProd resolveNouns(resolver, results) { return removeAmbigFlags(lst_.resolveNouns(resolver, results)); } /* there's only me in the response list */ getResponseList() { return lst_.getResponseList(); } ; grammar disambigPhrase(ordinalList): disambigOrdinalList->lst_ 'ones' | 'the' disambigOrdinalList->lst_ 'ones' : DisambigProd resolveNouns(resolver, results) { /* return the list with the ambiguity flags removed */ return removeAmbigFlags(lst_.resolveNouns(resolver, results)); } /* the response list consists of my single ordinal list item */ getResponseList() { return [lst_]; } ; /* * A disambig list consists of one or more disambig list items, connected * by noun phrase conjunctions. */ grammar disambigList(single): disambigListItem->item_ : DisambigProd resolveNouns(resolver, results) { return item_.resolveNouns(resolver, results); } /* the response list consists of my single item */ getResponseList() { return [item_]; } ; grammar disambigList(list): disambigListItem->item_ commandOrNounConjunction disambigList->lst_ : DisambigProd resolveNouns(resolver, results) { return item_.resolveNouns(resolver, results) + lst_.resolveNouns(resolver, results); } /* my response list consists of each of our list items */ getResponseList() { return [item_] + lst_.getResponseList(); } ; /* * Base class for ordinal disambiguation items */ class DisambigOrdProd: DisambigProd resolveNouns(resolver, results) { /* note the ordinal match */ results.noteDisambigOrdinal(); /* select the result by the ordinal */ return selectByOrdinal(ord_, resolver, results); } selectByOrdinal(ordTok, resolver, results) { local idx; local matchList = resolver.ordinalMatchList; /* * look up the meaning of the ordinal word (note that we assume * that each ordinalWord is unique, since we only create one of * each) */ idx = cmdDict.findWord(ordTok, &ordinalWord)[1].numval; /* * if it's the special value -1, it indicates that we should * select the *last* item in the list */ if (idx == -1) idx = matchList.length(); /* if it's outside the limits of the match list, it's an error */ if (idx > matchList.length()) { /* note the problem */ results.noteOrdinalOutOfRange(ordTok); /* no results */ return []; } /* return the selected item as a one-item list */ return matchList.sublist(idx, 1); } ; /* * A disambig vocab production is the base class for disambiguation * phrases that involve vocabulary words. */ class DisambigVocabProd: DisambigProd ; /* * A disambig list item consists of: * *. first/second/etc *. the first/second/etc *. first one/second one/etc *. the first one/the second one/etc *. *. possessive */ grammar disambigListItem(ordinal): ordinalWord->ord_ | ordinalWord->ord_ 'one' | 'the' ordinalWord->ord_ | 'the' ordinalWord->ord_ 'one' : DisambigOrdProd ; grammar disambigListItem(noun): completeNounPhraseWithoutAll->np_ | terminalNounPhrase->np_ : DisambigVocabProd resolveNouns(resolver, results) { /* get the matches for the underlying noun phrase */ local lst = np_.resolveNouns(resolver, results); /* note the matches */ results.noteMatches(lst); /* return the match list */ return lst; } ; grammar disambigListItem(plural): pluralPhrase->np_ : DisambigVocabProd resolveNouns(resolver, results) { local lst; /* * get the underlying match list; since we explicitly have a * plural, the result doesn't need to be unique, so simply * return everything we find */ lst = np_.resolveNouns(resolver, results); /* * if we didn't get anything, it's an error; otherwise, take * everything, since we explicitly wanted a plural usage */ if (lst.length() == 0) results.noMatch(resolver.getAction(), np_.getOrigText()); else results.noteMatches(lst); /* return the list */ return lst; } ; grammar disambigListItem(possessive): possessiveNounPhrase->poss_ : DisambigPossessiveProd ; /* * A disambig ordinal list consists of two or more ordinal words * separated by noun phrase conjunctions. Note that there is a minimum * of two entries in the list. */ grammar disambigOrdinalList(tail): ordinalWord->ord1_ ('and' | ',') ordinalWord->ord2_ : DisambigOrdProd resolveNouns(resolver, results) { /* note the pair of ordinal matches */ results.noteDisambigOrdinal(); results.noteDisambigOrdinal(); /* combine the selections of our two ordinals */ return selectByOrdinal(ord1_, resolver, results) + selectByOrdinal(ord2_, resolver, results); } ; grammar disambigOrdinalList(head): ordinalWord->ord_ ('and' | ',') disambigOrdinalList->lst_ : DisambigOrdProd resolveNouns(resolver, results) { /* note the ordinal match */ results.noteDisambigOrdinal(); /* combine the selections of our ordinal and the sublist */ return selectByOrdinal(ord_, resolver, results) + lst_.resolveNouns(resolver, results); } ; /* ------------------------------------------------------------------------ */ /* * Ordinal words. We define a limited set of these, since we only use * them in a few special contexts where it would be unreasonable to need * even as many as define here. */ #define defOrdinal(str, val) object ordinalWord=#@str numval=val defOrdinal(former, 1); defOrdinal(first, 1); defOrdinal(second, 2); defOrdinal(third, 3); defOrdinal(fourth, 4); defOrdinal(fifth, 5); defOrdinal(sixth, 6); defOrdinal(seventh, 7); defOrdinal(eighth, 8); defOrdinal(ninth, 9); defOrdinal(tenth, 10); defOrdinal(eleventh, 11); defOrdinal(twelfth, 12); defOrdinal(thirteenth, 13); defOrdinal(fourteenth, 14); defOrdinal(fifteenth, 15); defOrdinal(sixteenth, 16); defOrdinal(seventeenth, 17); defOrdinal(eighteenth, 18); defOrdinal(nineteenth, 19); defOrdinal(twentieth, 20); defOrdinal(1st, 1); defOrdinal(2nd, 2); defOrdinal(3rd, 3); defOrdinal(4th, 4); defOrdinal(5th, 5); defOrdinal(6th, 6); defOrdinal(7th, 7); defOrdinal(8th, 8); defOrdinal(9th, 9); defOrdinal(10th, 10); defOrdinal(11th, 11); defOrdinal(12th, 12); defOrdinal(13th, 13); defOrdinal(14th, 14); defOrdinal(15th, 15); defOrdinal(16th, 16); defOrdinal(17th, 17); defOrdinal(18th, 18); defOrdinal(19th, 19); defOrdinal(20th, 20); /* * the special 'last' ordinal - the value -1 is special to indicate the * last item in a list */ defOrdinal(last, -1); defOrdinal(latter, -1); /* ------------------------------------------------------------------------ */ /* * A numeric production. These can be either spelled-out numbers (such * as "fifty-seven") or numbers entered in digit form (as in "57"). */ class NumberProd: BasicProd /* get the numeric (integer) value */ getval() { return 0; } /* * Get the string version of the numeric value. This should return * a string, but the string should be in digit form. If the * original entry was in digit form, then the original entry should * be returned; otherwise, a string should be constructed from the * integer value. By default, we'll do the latter. */ getStrVal() { return toString(getval()); } ; /* * A quantifier is simply a number, entered with numerals or spelled out. */ grammar numberPhrase(digits): tokInt->num_ : NumberProd /* get the numeric value */ getval() { return toInteger(num_); } /* * get the string version of the numeric value - since the token was * an integer to start with, return the actual integer value */ getStrVal() { return num_; } ; grammar numberPhrase(spelled): spelledNumber->num_ : NumberProd /* get the numeric value */ getval() { return num_.getval(); } ; /* * A number phrase preceded by a pound sign. We distinguish this kind of * number phrase from plain numbers, since this kind has a somewhat more * limited set of valid contexts. */ grammar poundNumberPhrase(main): tokPoundInt->num_ : NumberProd /* * get the numeric value - a tokPoundInt token has a pound sign * followed by digits, so the numeric value is the value of the * substring following the '#' sign */ getval() { return toInteger(num_.substr(2)); } /* * get the string value - we have a number token following the '#', * so simply return the part after the '#' */ getStrVal() { return num_.substr(2); } ; /* * Number literals. We'll define a set of special objects for numbers: * each object defines a number and a value for the number. */ #define defDigit(num, val) object digitWord=#@num numval=val #define defTeen(num, val) object teenWord=#@num numval=val #define defTens(num, val) object tensWord=#@num numval=val defDigit(one, 1); defDigit(two, 2); defDigit(three, 3); defDigit(four, 4); defDigit(five, 5); defDigit(six, 6); defDigit(seven, 7); defDigit(eight, 8); defDigit(nine, 9); defTeen(ten, 10); defTeen(eleven, 11); defTeen(twelve, 12); defTeen(thirteen, 13); defTeen(fourteen, 14); defTeen(fifteen, 15); defTeen(sixteen, 16); defTeen(seventeen, 17); defTeen(eighteen, 18); defTeen(nineteen, 19); defTens(twenty, 20); defTens(thirty, 30); defTens(forty, 40); defTens(fifty, 50); defTens(sixty, 60); defTens(seventy, 70); defTens(eighty, 80); defTens(ninety, 90); grammar spelledSmallNumber(digit): digitWord->num_ : NumberProd getval() { /* * Look up the units word - there should be only one in the * dictionary, since these are our special words. Return the * object's numeric value property 'numval', which gives the * number for the name. */ return cmdDict.findWord(num_, &digitWord)[1].numval; } ; grammar spelledSmallNumber(teen): teenWord->num_ : NumberProd getval() { /* look up the dictionary word for the number */ return cmdDict.findWord(num_, &teenWord)[1].numval; } ; grammar spelledSmallNumber(tens): tensWord->num_ : NumberProd getval() { /* look up the dictionary word for the number */ return cmdDict.findWord(num_, &tensWord)[1].numval; } ; grammar spelledSmallNumber(tensAndUnits): tensWord->tens_ '-'->sep_ digitWord->units_ | tensWord->tens_ digitWord->units_ : NumberProd getval() { /* look up the words, and add up the values */ return cmdDict.findWord(tens_, &tensWord)[1].numval + cmdDict.findWord(units_, &digitWord)[1].numval; } ; grammar spelledSmallNumber(zero): 'zero' : NumberProd getval() { return 0; } ; grammar spelledHundred(small): spelledSmallNumber->num_ : NumberProd getval() { return num_.getval(); } ; grammar spelledHundred(hundreds): spelledSmallNumber->hun_ 'hundred' : NumberProd getval() { return hun_.getval() * 100; } ; grammar spelledHundred(hundredsPlus): spelledSmallNumber->hun_ 'hundred' spelledSmallNumber->num_ | spelledSmallNumber->hun_ 'hundred' 'and'->and_ spelledSmallNumber->num_ : NumberProd getval() { return hun_.getval() * 100 + num_.getval(); } ; grammar spelledHundred(aHundred): 'a' 'hundred' : NumberProd getval() { return 100; } ; grammar spelledHundred(aHundredPlus): 'a' 'hundred' 'and' spelledSmallNumber->num_ : NumberProd getval() { return 100 + num_.getval(); } ; grammar spelledThousand(thousands): spelledHundred->thou_ 'thousand' : NumberProd getval() { return thou_.getval() * 1000; } ; grammar spelledThousand(thousandsPlus): spelledHundred->thou_ 'thousand' spelledHundred->num_ : NumberProd getval() { return thou_.getval() * 1000 + num_.getval(); } ; grammar spelledThousand(thousandsAndSmall): spelledHundred->thou_ 'thousand' 'and' spelledSmallNumber->num_ : NumberProd getval() { return thou_.getval() * 1000 + num_.getval(); } ; grammar spelledThousand(aThousand): 'a' 'thousand' : NumberProd getval() { return 1000; } ; grammar spelledThousand(aThousandAndSmall): 'a' 'thousand' 'and' spelledSmallNumber->num_ : NumberProd getval() { return 1000 + num_.getval(); } ; grammar spelledMillion(millions): spelledHundred->mil_ 'million': NumberProd getval() { return mil_.getval() * 1000000; } ; grammar spelledMillion(millionsPlus): spelledHundred->mil_ 'million' (spelledThousand->nxt_ | spelledHundred->nxt_) : NumberProd getval() { return mil_.getval() * 1000000 + nxt_.getval(); } ; grammar spelledMillion(aMillion): 'a' 'million' : NumberProd getval() { return 1000000; } ; grammar spelledMillion(aMillionAndSmall): 'a' 'million' 'and' spelledSmallNumber->num_ : NumberProd getval() { return 1000000 + num_.getval(); } ; grammar spelledMillion(millionsAndSmall): spelledHundred->mil_ 'million' 'and' spelledSmallNumber->num_ : NumberProd getval() { return mil_.getval() * 1000000 + num_.getval(); } ; grammar spelledNumber(main): spelledHundred->num_ | spelledThousand->num_ | spelledMillion->num_ : NumberProd getval() { return num_.getval(); } ; /* ------------------------------------------------------------------------ */ /* * "OOPS" command syntax */ grammar oopsCommand(main): oopsPhrase->oops_ | oopsPhrase->oops_ '.' : BasicProd getNewTokens() { return oops_.getNewTokens(); } ; grammar oopsPhrase(main): 'oops' miscWordList->lst_ | 'oops' ',' miscWordList->lst_ | 'o' miscWordList->lst_ | 'o' ',' miscWordList->lst_ : BasicProd getNewTokens() { return lst_.getOrigTokenList(); } ; grammar oopsPhrase(missing): 'oops' | 'o' : BasicProd getNewTokens() { return nil; } ; /* ------------------------------------------------------------------------ */ /* * finishGame options. We provide descriptions and keywords for the * option objects here, because these are inherently language-specific. * * Note that we provide hyperlinks for our descriptions when possible. * When we're in plain text mode, we can't show links, so we'll instead * show an alternate form with the single-letter response highlighted in * the text. We don't highlight the single-letter response in the * hyperlinked version because (a) if the user wants a shortcut, they can * simply click the hyperlink, and (b) most UI's that show hyperlinks * show a distinctive appearance for the hyperlink itself, so adding even * more highlighting within the hyperlink starts to look awfully busy. */ modify finishOptionQuit desc = "<QUIT', 'Leave the story')>>" responseKeyword = 'quit' responseChar = 'q' ; modify finishOptionRestore desc = "<RESTORE', 'Restore a saved position')>> a saved position" responseKeyword = 'restore' responseChar = 'r' ; modify finishOptionRestart desc = "<START', 'Start the story over from the beginning')>> the story" responseKeyword = 'restart' responseChar = 's' ; modify finishOptionUndo desc = "<UNDO', 'Undo the last move')>> the last move" responseKeyword = 'undo' responseChar = 'u' ; modify finishOptionCredits desc = "see the <CREDITS', 'Show credits')>>" responseKeyword = 'credits' responseChar = 'c' ; modify finishOptionFullScore desc = "see your <FULL SCORE', 'Show full score')>>" responseKeyword = 'full score' responseChar = 'f' ; modify finishOptionAmusing desc = "see some <AMUSING', 'Show some amusing things to try')>> things to try" responseKeyword = 'amusing' responseChar = 'a' ; modify restoreOptionStartOver desc = "<START', 'Start from the beginning')>> the game from the beginning" responseKeyword = 'start' responseChar = 's' ; modify restoreOptionRestoreAnother desc = "<RESTORE', 'Restore a saved position')>> a different saved position" ; /* ------------------------------------------------------------------------ */ /* * Context for Action.getVerbPhrase(). This keeps track of pronoun * antecedents in cases where we're stringing together a series of verb * phrases. */ class GetVerbPhraseContext: object /* get the objective form of an object, using a pronoun as appropriate */ objNameObj(obj) { /* * if it's the pronoun antecedent, use the pronoun form; * otherwise, use the full name */ if (obj == pronounObj) return obj.itObj; else return obj.theNameObj; } /* are we showing the given object pronomially? */ isObjPronoun(obj) { return (obj == pronounObj); } /* set the pronoun antecedent */ setPronounObj(obj) { pronounObj = obj; } /* the pronoun antecedent */ pronounObj = nil ; /* * Default getVerbPhrase context. This can be used when no other context * is needed. This context instance has no state - it doesn't track any * antecedents. */ defaultGetVerbPhraseContext: GetVerbPhraseContext /* we don't remember any antecedents */ setPronounObj(obj) { } ; /* ------------------------------------------------------------------------ */ /* * Implicit action context. This is passed to the message methods that * generate implicit action announcements, to indicate the context in * which the message is to be used. */ class ImplicitAnnouncementContext: object /* * Should we use the infinitive form of the verb, or the participle * form for generating the announcement? By default, use use the * participle form: "(first OPENING THE BOX)". */ useInfPhrase = nil /* is this message going in a list? */ isInList = nil /* * Are we in a sublist of 'just trying' or 'just asking' messages? * (We can only have sublist groupings one level deep, so we don't * need to worry about what kind of sublist we're in.) */ isInSublist = nil /* our getVerbPhrase context - by default, don't use one */ getVerbCtx = nil /* generate the announcement message given the action description */ buildImplicitAnnouncement(txt) { /* if we're not in a list, make it a full, stand-alone message */ if (!isInList) txt = '<./p0>\n<.assume>first ' + txt + '<./assume>\n'; /* return the result */ return txt; } ; /* the standard implicit action announcement context */ standardImpCtx: ImplicitAnnouncementContext; /* the "just trying" implicit action announcement context */ tryingImpCtx: ImplicitAnnouncementContext /* * The action was merely attempted, so use the infinitive phrase in * the announcement: "(first trying to OPEN THE BOX)". */ useInfPhrase = true /* build the announcement */ buildImplicitAnnouncement(txt) { /* * If we're not in a list of 'trying' messages, add the 'trying' * prefix message to the action description. This isn't * necessary if we're in a 'trying' list, since the list itself * will have the 'trying' part. */ if (!isInSublist) txt = 'trying to ' + txt; /* now build the message into the full text as usual */ return inherited(txt); } ; /* * The "asking question" implicit action announcement context. By * default, we generate the message exactly the same way we do for the * 'trying' case. */ askingImpCtx: tryingImpCtx; /* * A class for messages appearing in a list. Within a list, we want to * keep track of the last direct object, so that we can refer to it with * a pronoun later in the list. */ class ListImpCtx: ImplicitAnnouncementContext, GetVerbPhraseContext /* * Set the appropriate base context for the given implicit action * announcement report (an ImplicitActionAnnouncement object). */ setBaseCtx(ctx) { /* * if this is a failed attempt, use a 'trying' context; * otherwise, use a standard context */ if (ctx.justTrying) baseCtx = tryingImpCtx; else if (ctx.justAsking) baseCtx = askingImpCtx; else baseCtx = standardImpCtx; } /* we're in a list */ isInList = true /* we are our own getVerbPhrase context */ getVerbCtx = (self) /* delegate the phrase format to our underlying announcement context */ useInfPhrase = (delegated baseCtx) /* build the announcement using our underlying context */ buildImplicitAnnouncement(txt) { return delegated baseCtx(txt); } /* our base context - we delegate some unoverridden behavior to this */ baseCtx = nil ; /* ------------------------------------------------------------------------ */ /* * Language-specific Action modifications. */ modify Action /* * In the English grammar, all 'predicate' grammar definitions * (which are usually made via the VerbRule macro) are associated * with Action match tree objects; in fact, each 'predicate' grammar * match tree is the specific Action subclass associated with the * grammar for the predicate. This means that the Action associated * with a grammar match is simply the grammar match object itself. * Hence, we can resolve the action for a 'predicate' match simply * by returning the match itself: it is the Action as well as the * grammar match. * * This approach ('grammar predicate' matches are based on Action * subclasses) works well for languages like English that encode the * role of each phrase in the word order of the sentence. * * Languages that encode phrase roles using case markers or other * devices tend to be freer with word order. As a result, * 'predicate' grammars for such languages should generally not * attempt to capture all possible word orderings for a given * action, but should instead take the complementary approach of * capturing the possible overall sentence structures independently * of verb phrases, and plug in a verb phrase as a component, just * like noun phrases plug into the English grammar. In these cases, * the match objects will NOT be Action subclasses; the Action * objects will instead be buried down deeper in the match tree. * Hence, resolveAction() must be defined on whatever class is used * to construct 'predicate' grammar matches, instead of on Action, * since Action will not be a 'predicate' match. */ resolveAction(issuingActor, targetActor) { return self; } /* * Return the interrogative pronoun for a missing object in one of * our object roles. In most cases, this is simply "what", but for * some actions, "whom" is more appropriate (for example, the direct * object of "ask" is implicitly a person, so "whom" is most * appropriate for this role). */ whatObj(which) { /* intransitive verbs have no objects, so there's nothing to show */ } /* * Translate an interrogative word for whatObj. If the word is * 'whom', translate to the library message for 'whom'; this allows * authors to use 'who' rather than 'whom' as the objective form of * 'who', which sounds less stuffy to many people. */ whatTranslate(txt) { /* * if it's 'whom', translate to the library message for 'whom'; * otherwise, just show the word as is */ return (txt == 'whom' ? gLibMessages.whomPronoun : txt); } /* * Return a string with the appropriate pronoun (objective form) for * a list of object matches, with the given resolved cardinality. * This list is a list of ResolveInfo objects. */ objListPronoun(objList) { local himCnt, herCnt, themCnt; local FirstPersonCnt, SecondPersonCnt; local resolvedNumber; /* if there's no object list at all, just use 'it' */ if (objList == nil || objList == []) return 'it'; /* note the number of objects in the resolved list */ resolvedNumber = objList.length(); /* * In the tentatively resolved object list, we might have hidden * away ambiguous matches. Expand those back into the list so * we have the full list of in-scope matches. */ foreach (local cur in objList) { /* * if this one has hidden ambiguous objects, add the hidden * objects back into our list */ if (cur.extraObjects != nil) objList += cur.extraObjects; } /* * if the desired cardinality is plural and the object list has * more than one object, simply say 'them' */ if (objList.length() > 1 && resolvedNumber > 1) return 'them'; /* * singular cardinality - count masculine and feminine objects, * and count the referral persons */ himCnt = herCnt = themCnt = 0; FirstPersonCnt = SecondPersonCnt = 0; foreach (local cur in objList) { /* if it's masculine, count it */ if (cur.obj_.isHim) ++himCnt; /* if it's feminine, count it */ if (cur.obj_.isHer) ++herCnt; /* if it has plural usage, count it */ if (cur.obj_.isPlural) ++themCnt; /* if it's first person usage, count it */ if (cur.obj_.referralPerson == FirstPerson) ++FirstPersonCnt; /* if it's second person usage, count it */ if (cur.obj_.referralPerson == SecondPerson) ++SecondPersonCnt; } /* * if they all have plural usage, show "them"; if they're all of * one gender, show "him" or "her" as appropriate; if they're * all neuter, show "it"; otherwise, show "them" */ if (themCnt == objList.length()) return 'them'; else if (FirstPersonCnt == objList.length()) return 'myself'; else if (SecondPersonCnt == objList.length()) return 'yourself'; else if (himCnt == objList.length() && herCnt == 0) return 'him'; else if (herCnt == objList.length() && himCnt == 0) return 'her'; else if (herCnt == 0 && himCnt == 0) return 'it'; else return 'them'; } /* * Announce a default object used with this action. * * 'resolvedAllObjects' indicates where we are in the command * processing: this is true if we've already resolved all of the * other objects in the command, nil if not. We use this * information to get the phrasing right according to the situation. */ announceDefaultObject(obj, whichObj, resolvedAllObjects) { /* * the basic action class takes no objects, so there can be no * default announcement */ return ''; } /* * Announce all defaulted objects in the action. By default, we * show nothing. */ announceAllDefaultObjects(allResolved) { } /* * Return a phrase describing the action performed implicitly, as a * participle phrase. 'ctx' is an ImplicitAnnouncementContext object * describing the context in which we're generating the phrase. * * This comes in two forms: if the context indicates we're only * attempting the action, we'll return an infinitive phrase ("open * the box") for use in a larger participle phrase describing the * attempt ("trying to..."). Otherwise, we'll be describing the * action as actually having been performed, so we'll return a * present participle phrase ("opening the box"). */ getImplicitPhrase(ctx) { /* * Get the phrase. Use the infinitive or participle form, as * indicated in the context. */ return getVerbPhrase(ctx.useInfPhrase, ctx.getVerbCtx); } /* * Get the infinitive form of the action. We are NOT to include the * infinitive complementizer (i.e., "to") as part of the result, * since the complementizer isn't used in all contexts in which we * might want to use the infinitive; for example, we don't want a * "to" in phrases involving an auxiliary verb, such as "he can open * the box." */ getInfPhrase() { /* return the verb phrase in infinitive form */ return getVerbPhrase(true, nil); } /* * Get the root infinitive form of our verb phrase as part of a * question in which one of the verb's objects is the "unknown" of * the interrogative. 'which' is one of the role markers * (DirectObject, IndirectObject, etc), indicating which object is * the subject of the interrogative. * * For example, for the verb UNLOCK WITH , if the * unknown is the direct object, the phrase we'd return would be * "unlock": this would plug into contexts such as "what do you want * to unlock." If the indirect object is the unknown for the same * verb, the phrase would be "unlock it with", which would plug in as * "what do you want to unlock it with". * * Note that we are NOT to include the infinitive complementizer * (i.e., "to") as part of the phrase we generate, since the * complementizer isn't used in some contexts where the infinitive * conjugation is needed (for example, "what should I "). */ getQuestionInf(which) { /* * for a verb without objects, this is the same as the basic * infinitive */ return getInfPhrase(); } /* * Get a string describing the full action in present participle * form, using the current command objects: "taking the watch", * "putting the book on the shelf" */ getParticiplePhrase() { /* return the verb phrase in participle form */ return getVerbPhrase(nil, nil); } /* * Get the full verb phrase in either infinitive or participle * format. This is a common handler for getInfinitivePhrase() and * getParticiplePhrase(). * * 'ctx' is a GetVerbPhraseContext object, which lets us keep track * of antecedents when we're stringing together multiple verb * phrases. 'ctx' can be nil if the verb phrase is being used in * isolation. */ getVerbPhrase(inf, ctx) { /* * parse the verbPhrase into the parts before and after the * slash, and any additional text following the slash part */ rexMatch('(.*)/(+)(.*)', verbPhrase); /* return the appropriate parts */ if (inf) { /* * infinitive - we want the part before the slash, plus the * extra prepositions (or whatever) after the switched part */ return rexGroup(1)[3] + rexGroup(3)[3]; } else { /* participle - it's the part after the slash */ return rexGroup(2)[3] + rexGroup(3)[3]; } } /* * Show the "noMatch" library message. For most verbs, we use the * basic "you can't see that here". Verbs that are mostly used with * intangible objects, such as LISTEN TO and SMELL, might want to * override this to use a less visually-oriented message. */ noMatch(msgObj, actor, txt) { msgObj.noMatchCannotSee(actor, txt); } /* * Verb flags - these are used to control certain aspects of verb * formatting. By default, we have no special flags. */ verbFlags = 0 /* add a space prefix/suffix to a string if the string is non-empty */ spPrefix(str) { return (str == '' ? str : ' ' + str); } spSuffix(str) { return (str == '' ? str : str + ' '); } ; /* * English-specific additions for single-object verbs. */ modify TAction /* return an interrogative word for an object of the action */ whatObj(which) { /* * Show the interrogative for our direct object - this is the * last word enclosed in parentheses in our verbPhrase string. */ rexSearch('.*?(+)', verbPhrase); return whatTranslate(rexGroup(1)[3]); } /* announce a default object used with this action */ announceDefaultObject(obj, whichObj, resolvedAllObjects) { /* * get any direct object preposition - this is the part inside * the "(what)" specifier parens, excluding the last word */ rexSearch('(.*+)?+', verbPhrase); local prep = (rexGroup(1) == nil ? '' : rexGroup(1)[3]); /* do any verb-specific adjustment of the preposition */ if (prep != nil) prep = adjustDefaultObjectPrep(prep, obj); /* * get the object name - we need to distinguish from everything * else in scope, since we considered everything in scope when * making our pick */ local nm = obj.getAnnouncementDistinguisher( gActor.scopeList()).theName(obj); /* show the preposition (if any) and the object */ return (prep == '' ? nm : prep + nm); } /* * Adjust the preposition. In some cases, the verb will want to vary * the preposition according to the object. This method can return a * custom preposition in place of the one in the verbPhrase. By * default, we just use the fixed preposition from the verbPhrase, * which is passed in to us in 'prep'. */ adjustDefaultObjectPrep(prep, obj) { return prep; } /* announce all defaulted objects */ announceAllDefaultObjects(allResolved) { /* announce a defaulted direct object if appropriate */ maybeAnnounceDefaultObject(dobjList_, DirectObject, allResolved); } /* show the verb's basic infinitive form for an interrogative */ getQuestionInf(which) { /* * Show the present-tense verb form (removing the participle * part - the "/xxxing" part). Include any prepositions * attached to the verb itself or to the direct object (inside * the "(what)" parens). */ rexSearch('(.*)/+(.*?)+' + '(.*?)*?+', verbPhrase); return rexGroup(1)[3] + spPrefix(rexGroup(2)[3]) + spPrefix(rexGroup(3)[3]); } /* get the verb phrase in infinitive or participle form */ getVerbPhrase(inf, ctx) { local dobj; local dobjText; local dobjIsPronoun; local ret; /* use the default pronoun context if one wasn't supplied */ if (ctx == nil) ctx = defaultGetVerbPhraseContext; /* get the direct object */ dobj = getDobj(); /* note if it's a pronoun */ dobjIsPronoun = ctx.isObjPronoun(dobj); /* get the direct object name */ dobjText = ctx.objNameObj(dobj); /* get the phrasing */ ret = getVerbPhrase1(inf, verbPhrase, dobjText, dobjIsPronoun); /* set the pronoun antecedent to my direct object */ ctx.setPronounObj(dobj); /* return the result */ return ret; } /* * Given the text of the direct object phrase, build the verb phrase * for a one-object verb. This is a class method that can be used by * other kinds of verbs (i.e., non-TActions) that use phrasing like a * single object. * * 'inf' is a flag indicating whether to use the infinitive form * (true) or the present participle form (nil); 'vp' is the * verbPhrase string; 'dobjText' is the direct object phrase's text; * and 'dobjIsPronoun' is true if the dobj text is rendered as a * pronoun. */ getVerbPhrase1(inf, vp, dobjText, dobjIsPronoun) { local ret; local dprep; local vcomp; /* * parse the verbPhrase: pick out the 'infinitive/participle' * part, the complementizer part up to the '(what)' direct * object placeholder, and any preposition within the '(what)' * specifier */ rexMatch('(.*)/(+)(.*) ' + '(.*?)*?+(.*)', vp); /* start off with the infinitive or participle, as desired */ if (inf) ret = rexGroup(1)[3]; else ret = rexGroup(2)[3]; /* get the prepositional complementizer */ vcomp = rexGroup(3)[3]; /* get the direct object preposition */ dprep = rexGroup(4)[3]; /* do any verb-specific adjustment of the preposition */ if (dprep != nil) dprep = adjustDefaultObjectPrep(dprep, getDobj()); /* * if the direct object is not a pronoun, put the complementizer * BEFORE the direct object (the 'up' in "PICKING UP THE BOX") */ if (!dobjIsPronoun) ret += spPrefix(vcomp); /* add the direct object preposition */ ret += spPrefix(dprep); /* add the direct object, using the pronoun form if applicable */ ret += ' ' + dobjText; /* * if the direct object is a pronoun, put the complementizer * AFTER the direct object (the 'up' in "PICKING IT UP") */ if (dobjIsPronoun) ret += spPrefix(vcomp); /* * if there's any suffix following the direct object * placeholder, add it at the end of the phrase */ ret += rexGroup(5)[3]; /* return the complete phrase string */ return ret; } ; /* * English-specific additions for two-object verbs. */ modify TIAction /* * Flag: omit the indirect object in a query for a missing direct * object. For many verbs, if we already know the indirect object * and we need to ask for the direct object, the query sounds best * when it includes the indirect object: "what do you want to put in * it?" or "what do you want to take from it?". This is the * default phrasing. * * However, the corresponding query for some verbs sounds weird: * "what do you want to dig in with it?" or "whom do you want to ask * about it?". For such actions, this property should be set to * true to indicate that the indirect object should be omitted from * the queries, which will change the phrasing to "what do you want * to dig in", "whom do you want to ask", and so on. */ omitIobjInDobjQuery = nil /* * For VerbRules: does this verb rule have a prepositional or * structural phrasing of the direct and indirect object slots? That * is, are the object slots determined by a prepositional marker, or * purely by word order? For most English verbs with two objects, * the indirect object is marked by a preposition: GIVE BOOK TO BOB, * PUT BOOK IN BOX. There are a few English verbs that don't include * any prespositional markers for the objects, though, and assign the * noun phrase roles purely by the word order: GIVE BOB BOOK, SHOW * BOB BOOK, THROW BOB BOOK. We define these phrasings with separate * verb rules, which we mark with this property. * * We use this in ranking verb matches. Non-prepositional verb * structures are especially prone to matching where they shouldn't, * because we can often find a way to pick out words to fill the * slots in the absence of any marker words. For example, GIVE GREEN * BOOK could be interpreted as GIVE BOOK TO GREEN, where GREEN is * assumed to be an adjective-ending noun phrase; but the player * probably means to give the green book to someone who they assumed * would be filled in as a default. So, whenever we find an * interpretation that involves a non-prespositional phrasing, we'll * use this flag to know we should be suspicious of it and try * alternative phrasing first. * * Most two-object verbs in English use prepositional markers, so * we'll set this as the default. Individual VerbRules that use * purely structural phrasing should override this. */ isPrepositionalPhrasing = true /* resolve noun phrases */ resolveNouns(issuingActor, targetActor, results) { /* * If we're a non-prepositional phrasing, it means that we have * the VERB IOBJ DOBJ word ordering (as in GIVE BOB BOX or THROW * BOB COIN). For grammar match ranking purposes, give these * phrasings a lower match probability when the dobj phrase * doesn't have a clear qualifier. If the dobj phrase starts * with 'the' or a qualifier word like that (GIVE BOB THE BOX), * then it's pretty clear that the structural phrasing is right * after all; but if there's no qualifier, we could reading too * much into the word order. We could have something like GIVE * GREEN BOX, where we *could* treat this as two objects, but we * could just as well have a missing indirect object phrase. */ if (!isPrepositionalPhrasing) { /* * If the direct object phrase starts with 'a', 'an', 'the', * 'some', or 'any', the grammar is pretty clearly a good * match for the non-prepositional phrasing. Otherwise, it's * suspect, so rank it accordingly. */ if (rexMatch('(a|an|the|some|any)', dobjMatch.getOrigText()) == nil) { /* note this as weak phrasing level 100 */ results.noteWeakPhrasing(100); } } /* inherit the base handling */ inherited(issuingActor, targetActor, results); } /* get the interrogative for one of our objects */ whatObj(which) { switch (which) { case DirectObject: /* * the direct object interrogative is the first word in * parentheses in our verbPhrase string */ rexSearch('.*?(+)', verbPhrase); break; case IndirectObject: /* * the indirect object interrogative is the second * parenthesized word in our verbPhrase string */ rexSearch('.*.*?(+)', verbPhrase); break; } /* show the group match */ return whatTranslate(rexGroup(1)[3]); } /* announce a default object used with this action */ announceDefaultObject(obj, whichObj, resolvedAllObjects) { /* presume we won't have a verb or preposition */ local verb = ''; local prep = ''; /* * Check the full phrasing - if we're showing the direct object, * but an indirect object was supplied, use the verb's * participle form ("asking bob") in the default string, since * we must clarify that we're not tagging the default string on * to the command line. Don't include the participle form if we * don't know all the objects yet, since in this case we are in * fact tagging the default string onto the command so far, as * there's nothing else in the command to get in the way. */ if (whichObj == DirectObject && resolvedAllObjects) { /* * extract the verb's participle form (including any * complementizer phrase) */ rexSearch('/(<^lparen>+) ', verbPhrase); verb = rexGroup(1)[3] + ' '; } /* get the preposition to use, if any */ switch(whichObj) { case DirectObject: /* use the preposition in the first "(what)" phrase */ rexSearch('(.*?)*+', verbPhrase); prep = rexGroup(1)[3]; break; case IndirectObject: /* use the preposition in the second "(what)" phrase */ rexSearch('.*(.*?)*+', verbPhrase); prep = rexGroup(1)[3]; break; } /* * get the object name - we need to distinguish from everything * else in scope, since we considered everything in scope when * making our pick */ local nm = obj.getAnnouncementDistinguisher( gActor.scopeList()).theName(obj); /* build and return the complete phrase */ return spSuffix(verb) + spSuffix(prep) + nm; } /* announce all defaulted objects */ announceAllDefaultObjects(allResolved) { /* announce a defaulted direct object if appropriate */ maybeAnnounceDefaultObject(dobjList_, DirectObject, allResolved); /* announce a defaulted indirect object if appropriate */ maybeAnnounceDefaultObject(iobjList_, IndirectObject, allResolved); } /* show the verb's basic infinitive form for an interrogative */ getQuestionInf(which) { local ret; local vcomp; local dprep; local iprep; local pro; /* * Our verb phrase can one of three formats, depending on which * object role we're asking about (the part in * is the part we're responsible for generating). In these * formats, 'verb' is the verb infinitive; 'comp' is the * complementizer, if any (e.g., the 'up' in 'pick up'); 'dprep' * is the direct object preposition (the 'in' in 'dig in x with * y'); and 'iprep' is the indirect object preposition (the * 'with' in 'dig in x with y'). * * asking for dobj: verb vcomp dprep iprep it ('what do you want * to ?', '', ''). * * asking for dobj, but suppressing the iobj part: verb vcomp * dprep ('what do you want to ?', '?', '') * * asking for iobj: verb dprep it vcomp iprep ('what do you want * to ', '', '' */ /* parse the verbPhrase into its component parts */ rexMatch('(.*)/+(?:+(<^lparen>*))?' + '+(.*?)*+' + '+(.*?)*+', verbPhrase); /* pull out the verb */ ret = rexGroup(1)[3]; /* pull out the verb complementizer */ vcomp = (rexGroup(2) == nil ? '' : rexGroup(2)[3]); /* pull out the direct and indirect object prepositions */ dprep = rexGroup(3)[3]; iprep = rexGroup(4)[3]; /* get the pronoun for the other object phrase */ pro = getOtherMessageObjectPronoun(which); /* check what we're asking about */ if (which == DirectObject) { /* add the part in all cases */ ret += spPrefix(vcomp) + spPrefix(dprep); /* add the part if we want the indirect object part */ if (!omitIobjInDobjQuery && pro != nil) ret += spPrefix(iprep) + ' ' + pro; } else { /* add the part if appropriate */ if (pro != nil) ret += spPrefix(dprep) + ' ' + pro; /* add the part */ ret += spPrefix(vcomp) + spPrefix(iprep); } /* return the result */ return ret; } /* * Get the pronoun for the message object in the given role. */ getOtherMessageObjectPronoun(which) { local lst; /* * Get the resolution list (or tentative resolution list) for the * *other* object, since we want to show a pronoun representing * the other object. If we don't have a fully-resolved list for * the other object, use the tentative resolution list, which * we're guaranteed to have by the time we start resolving * anything (and thus by the time we need to ask for objects). */ lst = (which == DirectObject ? iobjList_ : dobjList_); if (lst == nil || lst == []) lst = (which == DirectObject ? tentativeIobj_ : tentativeDobj_); /* if we found an object list, use the pronoun for the list */ if (lst != nil && lst != []) { /* we got a list - return a suitable pronoun for this list */ return objListPronoun(lst); } else { /* there's no object list, so there's no pronoun */ return nil; } } /* get the verb phrase in infinitive or participle form */ getVerbPhrase(inf, ctx) { local dobj, dobjText, dobjIsPronoun; local iobj, iobjText; local ret; /* use the default context if one wasn't supplied */ if (ctx == nil) ctx = defaultGetVerbPhraseContext; /* get the direct object information */ dobj = getDobj(); dobjText = ctx.objNameObj(dobj); dobjIsPronoun = ctx.isObjPronoun(dobj); /* get the indirect object information */ iobj = getIobj(); iobjText = (iobj != nil ? ctx.objNameObj(iobj) : nil); /* get the phrasing */ ret = getVerbPhrase2(inf, verbPhrase, dobjText, dobjIsPronoun, iobjText); /* * Set the antecedent for the next verb phrase. Our direct * object is normally the antecedent; however, if the indirect * object matches the current antecedent, keep the current * antecedent, so that 'it' (or whatever) remains the same for * the next verb phrase. */ if (ctx.pronounObj != iobj) ctx.setPronounObj(dobj); /* return the result */ return ret; } /* * Get the verb phrase for a two-object (dobj + iobj) phrasing. This * is a class method, so that it can be reused by unrelated (i.e., * non-TIAction) classes that also use two-object syntax but with * other internal structures. This is the two-object equivalent of * TAction.getVerbPhrase1(). */ getVerbPhrase2(inf, vp, dobjText, dobjIsPronoun, iobjText) { local ret; local vcomp; local dprep, iprep; /* parse the verbPhrase into its component parts */ rexMatch('(.*)/(+)(?:+(<^lparen>*))?' + '+(.*?)*+' + '+(.*?)*+', vp); /* start off with the infinitive or participle, as desired */ if (inf) ret = rexGroup(1)[3]; else ret = rexGroup(2)[3]; /* get the complementizer */ vcomp = (rexGroup(3) == nil ? '' : rexGroup(3)[3]); /* get the direct and indirect object prepositions */ dprep = rexGroup(4)[3]; iprep = rexGroup(5)[3]; /* * add the complementizer BEFORE the direct object, if the * direct object is being shown as a full name ("PICK UP BOX") */ if (!dobjIsPronoun) ret += spPrefix(vcomp); /* * add the direct object and its preposition, using a pronoun if * applicable */ ret += spPrefix(dprep) + ' ' + dobjText; /* * add the complementizer AFTER the direct object, if the direct * object is shown as a pronoun ("PICK IT UP") */ if (dobjIsPronoun) ret += spPrefix(vcomp); /* if we have an indirect object, add it with its preposition */ if (iobjText != nil) ret += spPrefix(iprep) + ' ' + iobjText; /* return the result phrase */ return ret; } ; /* * English-specific additions for verbs taking a literal phrase as the * sole object. */ modify LiteralAction /* provide a base verbPhrase, in case an instance leaves it out */ verbPhrase = 'verb/verbing (what)' /* get an interrogative word for an object of the action */ whatObj(which) { /* use the same processing as TAction */ return delegated TAction(which); } getVerbPhrase(inf, ctx) { /* handle this as though the literal were a direct object phrase */ return TAction.getVerbPhrase1(inf, verbPhrase, gLiteral, nil); } getQuestionInf(which) { /* use the same handling as for a regular one-object action */ return delegated TAction(which); } ; /* * English-specific additions for verbs of a direct object and a literal * phrase. */ modify LiteralTAction announceDefaultObject(obj, whichObj, resolvedAllObjects) { /* * Use the same handling as for a regular two-object action. We * can only default the actual object in this kind of verb; the * actual object always fills the DirectObject slot, but in * message generation it might use a different slot, so use the * message generation slot here. */ return delegated TIAction(obj, whichMessageObject, resolvedAllObjects); } whatObj(which) { /* use the same handling we use for a regular two-object action */ return delegated TIAction(which); } getQuestionInf(which) { /* * use the same handling as for a two-object action (but note * that we override getMessageObjectPronoun(), which will affect * the way we present the verb infinitive in some cases) */ return delegated TIAction(which); } /* * When we want to show a verb infinitive phrase that involves a * pronoun for the literal phrase, refer to the literal as 'that' * rather than 'it' or anything else. */ getOtherMessageObjectPronoun(which) { /* * If we're asking about the literal phrase, then the other * pronoun is for the resolved object: so, return the pronoun * for the direct object phrase, because we *always* store the * non-literal in the direct object slot, regardless of the * actual phrasing of the action. * * If we're asking about the resolved object (i.e., not the * literal phrase), then return 'that' as the pronoun for the * literal phrase. */ if (which == whichMessageLiteral) { /* * we're asking about the literal, so the other pronoun is * for the resolved object, which is always in the direct * object slot (so the 'other' slot is effectively the * indirect object) */ return delegated TIAction(IndirectObject); } else { /* * We're asking about the resolved object, so the other * pronoun is for the literal phrase: always use 'that' to * refer to the literal phrase. */ return 'that'; } } getVerbPhrase(inf, ctx) { local dobj, dobjText, dobjIsPronoun; local litText; local ret; /* use the default context if one wasn't supplied */ if (ctx == nil) ctx = defaultGetVerbPhraseContext; /* get the direct object information */ dobj = getDobj(); dobjText = ctx.objNameObj(dobj); dobjIsPronoun = ctx.isObjPronoun(dobj); /* get our literal text */ litText = gLiteral; /* * Use the standard two-object phrasing. The order of the * phrasing depends on whether our literal phrase is in the * direct or indirect object slot. */ if (whichMessageLiteral == DirectObject) ret = TIAction.getVerbPhrase2(inf, verbPhrase, litText, nil, dobjText); else ret = TIAction.getVerbPhrase2(inf, verbPhrase, dobjText, dobjIsPronoun, litText); /* use the direct object as the antecedent for the next phrase */ ctx.setPronounObj(dobj); /* return the result */ return ret; } ; /* * English-specific additions for verbs taking a topic phrase as the sole * object. */ modify TopicAction /* get an interrogative word for an object of the action */ whatObj(which) { /* use the same processing as TAction */ return delegated TAction(which); } getVerbPhrase(inf, ctx) { /* handle this as though the topic text were a direct object phrase */ return TAction.getVerbPhrase1( inf, verbPhrase, getTopic().getTopicText().toLower(), nil); } getQuestionInf(which) { /* use the same handling as for a regular one-object action */ return delegated TAction(which); } ; /* * English-specific additions for verbs with topic phrases. */ modify TopicTAction announceDefaultObject(obj, whichObj, resolvedAllObjects) { /* * Use the same handling as for a regular two-object action. We * can only default the actual object in this kind of verb; the * actual object always fills the DirectObject slot, but in * message generation it might use a different slot, so use the * message generation slot here. */ return delegated TIAction(obj, whichMessageObject, resolvedAllObjects); } whatObj(which) { /* use the same handling we use for a regular two-object action */ return delegated TIAction(which); } getQuestionInf(which) { /* use the same handling as for a regular two-object action */ return delegated TIAction(which); } getOtherMessageObjectPronoun(which) { /* * If we're asking about the topic, then the other pronoun is * for the resolved object, which is always in the direct object * slot. If we're asking about the resolved object, then return * a pronoun for the topic. */ if (which == whichMessageTopic) { /* * we want the pronoun for the resolved object, which is * always in the direct object slot (so the 'other' slot is * effectively the indirect object) */ return delegated TIAction(IndirectObject); } else { /* return a generic pronoun for the topic */ return 'that'; } } getVerbPhrase(inf, ctx) { local dobj, dobjText, dobjIsPronoun; local topicText; local ret; /* use the default context if one wasn't supplied */ if (ctx == nil) ctx = defaultGetVerbPhraseContext; /* get the direct object information */ dobj = getDobj(); dobjText = ctx.objNameObj(dobj); dobjIsPronoun = ctx.isObjPronoun(dobj); /* get our topic phrase */ topicText = getTopic().getTopicText().toLower(); /* * Use the standard two-object phrasing. The order of the * phrasing depends on whether our topic phrase is in the direct * or indirect object slot. */ if (whichMessageTopic == DirectObject) ret = TIAction.getVerbPhrase2(inf, verbPhrase, topicText, nil, dobjText); else ret = TIAction.getVerbPhrase2(inf, verbPhrase, dobjText, dobjIsPronoun, topicText); /* use the direct object as the antecedent for the next phrase */ ctx.setPronounObj(dobj); /* return the result */ return ret; } ; /* ------------------------------------------------------------------------ */ /* * Verbs. * * The actual body of each of our verbs is defined in the main * language-independent part of the library. We only define the * language-specific grammar rules here. */ VerbRule(Take) ('take' | 'pick' 'up' | 'get') dobjList | 'pick' dobjList 'up' : TakeAction verbPhrase = 'take/taking (what)' ; VerbRule(TakeFrom) ('take' | 'get') dobjList ('from' | 'out' 'of' | 'off' | 'off' 'of') singleIobj | 'remove' dobjList 'from' singleIobj : TakeFromAction verbPhrase = 'take/taking (what) (from what)' ; VerbRule(Remove) 'remove' dobjList : RemoveAction verbPhrase = 'remove/removing (what)' ; VerbRule(Drop) ('drop' | 'put' 'down' | 'set' 'down') dobjList | ('put' | 'set') dobjList 'down' : DropAction verbPhrase = 'drop/dropping (what)' ; VerbRule(Examine) ('examine' | 'inspect' | 'x' | 'look' 'at' | 'l' 'at' | 'look' | 'l') dobjList : ExamineAction verbPhrase = 'examine/examining (what)' ; VerbRule(Read) 'read' dobjList : ReadAction verbPhrase = 'read/reading (what)' ; VerbRule(LookIn) ('look' | 'l') ('in' | 'inside') dobjList : LookInAction verbPhrase = 'look/looking (in what)' ; VerbRule(Search) 'search' dobjList : SearchAction verbPhrase = 'search/searching (what)' ; VerbRule(LookThrough) ('look' | 'l') ('through' | 'thru' | 'out') dobjList : LookThroughAction verbPhrase = 'look/looking (through what)' ; VerbRule(LookUnder) ('look' | 'l') 'under' dobjList : LookUnderAction verbPhrase = 'look/looking (under what)' ; VerbRule(LookBehind) ('look' | 'l') 'behind' dobjList : LookBehindAction verbPhrase = 'look/looking (behind what)' ; VerbRule(Feel) ('feel' | 'touch') dobjList : FeelAction verbPhrase = 'touch/touching (what)' ; VerbRule(Taste) 'taste' dobjList : TasteAction verbPhrase = 'taste/tasting (what)' ; VerbRule(Smell) ('smell' | 'sniff') dobjList : SmellAction verbPhrase = 'smell/smelling (what)' /* * use the "not aware" version of the no-match message - the object * of SMELL is often intangible, so the default "you can't see that" * message is often incongruous for this verb */ noMatch(msgObj, actor, txt) { msgObj.noMatchNotAware(actor, txt); } ; VerbRule(SmellImplicit) 'smell' | 'sniff' : SmellImplicitAction verbPhrase = 'smell/smelling' ; VerbRule(ListenTo) ('hear' | 'listen' 'to' ) dobjList : ListenToAction verbPhrase = 'listen/listening (to what)' /* * use the "not aware" version of the no-match message - the object * of LISTEN TO is often intangible, so the default "you can't see * that" message is often incongruous for this verb */ noMatch(msgObj, actor, txt) { msgObj.noMatchNotAware(actor, txt); } ; VerbRule(ListenImplicit) 'listen' | 'hear' : ListenImplicitAction verbPhrase = 'listen/listening' ; VerbRule(PutIn) ('put' | 'place' | 'set') dobjList ('in' | 'into' | 'in' 'to' | 'inside' | 'inside' 'of') singleIobj : PutInAction verbPhrase = 'put/putting (what) (in what)' askIobjResponseProd = inSingleNoun ; VerbRule(PutOn) ('put' | 'place' | 'drop' | 'set') dobjList ('on' | 'onto' | 'on' 'to' | 'upon') singleIobj | 'put' dobjList 'down' 'on' singleIobj : PutOnAction verbPhrase = 'put/putting (what) (on what)' askIobjResponseProd = onSingleNoun ; VerbRule(PutUnder) ('put' | 'place' | 'set') dobjList 'under' singleIobj : PutUnderAction verbPhrase = 'put/putting (what) (under what)' ; VerbRule(PutBehind) ('put' | 'place' | 'set') dobjList 'behind' singleIobj : PutBehindAction verbPhrase = 'put/putting (what) (behind what)' ; VerbRule(PutInWhat) [badness 500] ('put' | 'place') dobjList : PutInAction verbPhrase = 'put/putting (what) (in what)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = inSingleNoun; } ; VerbRule(Wear) ('wear' | 'don' | 'put' 'on') dobjList | 'put' dobjList 'on' : WearAction verbPhrase = 'wear/wearing (what)' ; VerbRule(Doff) ('doff' | 'take' 'off') dobjList | 'take' dobjList 'off' : DoffAction verbPhrase = 'take/taking off (what)' ; VerbRule(Kiss) 'kiss' singleDobj : KissAction verbPhrase = 'kiss/kissing (whom)' ; VerbRule(AskFor) ('ask' | 'a') singleDobj 'for' singleTopic | ('ask' | 'a') 'for' singleTopic 'from' singleDobj : AskForAction verbPhrase = 'ask/asking (whom) (for what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun askIobjResponseProd = forSingleNoun ; VerbRule(AskWhomFor) ('ask' | 'a') 'for' singleTopic : AskForAction verbPhrase = 'ask/asking (whom) (for what)' omitIobjInDobjQuery = true construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = singleNoun; } ; VerbRule(AskAbout) ('ask' | 'a') singleDobj 'about' singleTopic : AskAboutAction verbPhrase = 'ask/asking (whom) (about what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun ; VerbRule(AskAboutImplicit) 'a' singleTopic : AskAboutAction verbPhrase = 'ask/asking (whom) (about what)' omitIobjInDobjQuery = true construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = singleNoun; } ; VerbRule(AskAboutWhat) [badness 500] 'ask' singleDobj : AskAboutAction verbPhrase = 'ask/asking (whom) (about what)' askDobjResponseProd = singleNoun omitIobjInDobjQuery = true construct() { /* set up the empty topic phrase */ topicMatch = new EmptyNounPhraseProd(); topicMatch.responseProd = aboutTopicPhrase; } ; VerbRule(TellAbout) ('tell' | 't') singleDobj 'about' singleTopic : TellAboutAction verbPhrase = 'tell/telling (whom) (about what)' askDobjResponseProd = singleNoun omitIobjInDobjQuery = true ; VerbRule(TellAboutImplicit) 't' singleTopic : TellAboutAction verbPhrase = 'tell/telling (whom) (about what)' omitIobjInDobjQuery = true construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = singleNoun; } ; VerbRule(TellAboutWhat) [badness 500] 'tell' singleDobj : TellAboutAction verbPhrase = 'tell/telling (whom) (about what)' askDobjResponseProd = singleNoun omitIobjInDobjQuery = true construct() { /* set up the empty topic phrase */ topicMatch = new EmptyNounPhraseProd(); topicMatch.responseProd = aboutTopicPhrase; } ; VerbRule(AskVague) [badness 500] 'ask' singleDobj singleTopic : AskVagueAction verbPhrase = 'ask/asking (whom)' ; VerbRule(TellVague) [badness 500] 'tell' singleDobj singleTopic : AskVagueAction verbPhrase = 'tell/telling (whom)' ; VerbRule(TalkTo) ('greet' | 'say' 'hello' 'to' | 'talk' 'to') singleDobj : TalkToAction verbPhrase = 'talk/talking (to whom)' askDobjResponseProd = singleNoun ; VerbRule(TalkToWhat) [badness 500] 'talk' : TalkToAction verbPhrase = 'talk/talking (to whom)' askDobjResponseProd = singleNoun construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = onSingleNoun; } ; VerbRule(Topics) 'topics' : TopicsAction verbPhrase = 'show/showing topics' ; VerbRule(Hello) ('say' | ) ('hello' | 'hallo' | 'hi') : HelloAction verbPhrase = 'say/saying hello' ; VerbRule(Goodbye) ('say' | ()) ('goodbye' | 'good-bye' | 'good' 'bye' | 'bye') : GoodbyeAction verbPhrase = 'say/saying goodbye' ; VerbRule(Yes) 'yes' | 'affirmative' | 'say' 'yes' : YesAction verbPhrase = 'say/saying yes' ; VerbRule(No) 'no' | 'negative' | 'say' 'no' : NoAction verbPhrase = 'say/saying no' ; VerbRule(Yell) 'yell' | 'scream' | 'shout' | 'holler' : YellAction verbPhrase = 'yell/yelling' ; VerbRule(GiveTo) ('give' | 'offer') dobjList 'to' singleIobj : GiveToAction verbPhrase = 'give/giving (what) (to whom)' askIobjResponseProd = toSingleNoun ; VerbRule(GiveToType2) ('give' | 'offer') singleIobj dobjList : GiveToAction verbPhrase = 'give/giving (what) (to whom)' askIobjResponseProd = toSingleNoun /* this is a non-prepositional phrasing */ isPrepositionalPhrasing = nil ; VerbRule(GiveToWhom) ('give' | 'offer') dobjList : GiveToAction verbPhrase = 'give/giving (what) (to whom)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new ImpliedActorNounPhraseProd(); iobjMatch.responseProd = toSingleNoun; } ; VerbRule(ShowTo) 'show' dobjList 'to' singleIobj : ShowToAction verbPhrase = 'show/showing (what) (to whom)' askIobjResponseProd = toSingleNoun ; VerbRule(ShowToType2) 'show' singleIobj dobjList : ShowToAction verbPhrase = 'show/showing (what) (to whom)' askIobjResponseProd = toSingleNoun /* this is a non-prepositional phrasing */ isPrepositionalPhrasing = nil ; VerbRule(ShowToWhom) 'show' dobjList : ShowToAction verbPhrase = 'show/showing (what) (to whom)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new ImpliedActorNounPhraseProd(); iobjMatch.responseProd = toSingleNoun; } ; VerbRule(Throw) ('throw' | 'toss') dobjList : ThrowAction verbPhrase = 'throw/throwing (what)' ; VerbRule(ThrowAt) ('throw' | 'toss') dobjList 'at' singleIobj : ThrowAtAction verbPhrase = 'throw/throwing (what) (at what)' askIobjResponseProd = atSingleNoun ; VerbRule(ThrowTo) ('throw' | 'toss') dobjList 'to' singleIobj : ThrowToAction verbPhrase = 'throw/throwing (what) (to whom)' askIobjResponseProd = toSingleNoun ; VerbRule(ThrowToType2) 'throw' singleIobj dobjList : ThrowToAction verbPhrase = 'throw/throwing (what) (to whom)' askIobjResponseProd = toSingleNoun /* this is a non-prepositional phrasing */ isPrepositionalPhrasing = nil ; VerbRule(ThrowDir) ('throw' | 'toss') dobjList ('to' ('the' | ) | ) singleDir : ThrowDirAction verbPhrase = ('throw/throwing (what) ' + dirMatch.dir.name) ; /* a special rule for THROW DOWN */ VerbRule(ThrowDirDown) 'throw' ('down' | 'd') dobjList : ThrowDirAction verbPhrase = ('throw/throwing (what) down') /* the direction is fixed as 'down' for this phrasing */ getDirection() { return downDirection; } ; VerbRule(Follow) 'follow' singleDobj : FollowAction verbPhrase = 'follow/following (whom)' askDobjResponseProd = singleNoun ; VerbRule(Attack) ('attack' | 'kill' | 'hit' | 'kick' | 'punch') singleDobj : AttackAction verbPhrase = 'attack/attacking (whom)' askDobjResponseProd = singleNoun ; VerbRule(AttackWith) ('attack' | 'kill' | 'hit' | 'kick' | 'punch' | 'strike') singleDobj 'with' singleIobj : AttackWithAction verbPhrase = 'attack/attacking (whom) (with what)' askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(Inventory) 'i' | 'inventory' | 'take' 'inventory' : InventoryAction verbPhrase = 'take/taking inventory' ; VerbRule(InventoryTall) 'i' 'tall' | 'inventory' 'tall' : InventoryTallAction verbPhrase = 'take/taking "tall" inventory' ; VerbRule(InventoryWide) 'i' 'wide' | 'inventory' 'wide' : InventoryWideAction verbPhrase = 'take/taking "wide" inventory' ; VerbRule(Wait) 'z' | 'wait' : WaitAction verbPhrase = 'wait/waiting' ; VerbRule(Look) 'look' | 'look' 'around' | 'l' | 'l' 'around' : LookAction verbPhrase = 'look/looking around' ; VerbRule(Quit) 'quit' | 'q' : QuitAction verbPhrase = 'quit/quitting' ; VerbRule(Again) 'again' | 'g' : AgainAction verbPhrase = 'repeat/repeating the last command' ; VerbRule(Footnote) ('footnote' | 'note') singleNumber : FootnoteAction verbPhrase = 'show/showing a footnote' ; VerbRule(FootnotesFull) 'footnotes' 'full' : FootnotesFullAction verbPhrase = 'enable/enabling all footnotes' ; VerbRule(FootnotesMedium) 'footnotes' 'medium' : FootnotesMediumAction verbPhrase = 'enable/enabling new footnotes' ; VerbRule(FootnotesOff) 'footnotes' 'off' : FootnotesOffAction verbPhrase = 'hide/hiding footnotes' ; VerbRule(FootnotesStatus) 'footnotes' : FootnotesStatusAction verbPhrase = 'show/showing footnote status' ; VerbRule(TipsOn) ('tips' | 'tip') 'on' : TipModeAction stat_ = true verbPhrase = 'turn/turning tips on' ; VerbRule(TipsOff) ('tips' | 'tip') 'off' : TipModeAction stat_ = nil verbPhrase = 'turn/turning tips off' ; VerbRule(Verbose) 'verbose' : VerboseAction verbPhrase = 'enter/entering VERBOSE mode' ; VerbRule(Terse) 'terse' | 'brief' : TerseAction verbPhrase = 'enter/entering BRIEF mode' ; VerbRule(Score) 'score' | 'status' : ScoreAction verbPhrase = 'show/showing score' ; VerbRule(FullScore) 'full' 'score' | 'fullscore' | 'full' : FullScoreAction verbPhrase = 'show/showing full score' ; VerbRule(Notify) 'notify' : NotifyAction verbPhrase = 'show/showing notification status' ; VerbRule(NotifyOn) 'notify' 'on' : NotifyOnAction verbPhrase = 'turn/turning on score notification' ; VerbRule(NotifyOff) 'notify' 'off' : NotifyOffAction verbPhrase = 'turn/turning off score notification' ; VerbRule(Save) 'save' : SaveAction verbPhrase = 'save/saving' ; VerbRule(SaveString) 'save' quotedStringPhrase->fname_ : SaveStringAction verbPhrase = 'save/saving' ; VerbRule(Restore) 'restore' : RestoreAction verbPhrase = 'restore/restoring' ; VerbRule(RestoreString) 'restore' quotedStringPhrase->fname_ : RestoreStringAction verbPhrase = 'restore/restoring' ; VerbRule(SaveDefaults) 'save' 'defaults' : SaveDefaultsAction verbPhrase = 'save/saving defaults' ; VerbRule(RestoreDefaults) 'restore' 'defaults' : RestoreDefaultsAction verbPhrase = 'restore/restoring defaults' ; VerbRule(Restart) 'restart' : RestartAction verbPhrase = 'restart/restarting' ; VerbRule(Pause) 'pause' : PauseAction verbPhrase = 'pause/pausing' ; VerbRule(Undo) 'undo' : UndoAction verbPhrase = 'undo/undoing' ; VerbRule(Version) 'version' : VersionAction verbPhrase = 'show/showing version' ; VerbRule(Credits) 'credits' : CreditsAction verbPhrase = 'show/showing credits' ; VerbRule(About) 'about' : AboutAction verbPhrase = 'show/showing story information' ; VerbRule(Script) 'script' | 'script' 'on' : ScriptAction verbPhrase = 'start/starting scripting' ; VerbRule(ScriptString) 'script' quotedStringPhrase->fname_ : ScriptStringAction verbPhrase = 'start/starting scripting' ; VerbRule(ScriptOff) 'script' 'off' | 'unscript' : ScriptOffAction verbPhrase = 'end/ending scripting' ; VerbRule(Record) 'record' | 'record' 'on' : RecordAction verbPhrase = 'start/starting command recording' ; VerbRule(RecordString) 'record' quotedStringPhrase->fname_ : RecordStringAction verbPhrase = 'start/starting command recording' ; VerbRule(RecordEvents) 'record' 'events' | 'record' 'events' 'on' : RecordEventsAction verbPhrase = 'start/starting event recording' ; VerbRule(RecordEventsString) 'record' 'events' quotedStringPhrase->fname_ : RecordEventsStringAction verbPhrase = 'start/starting command recording' ; VerbRule(RecordOff) 'record' 'off' : RecordOffAction verbPhrase = 'end/ending command recording' ; VerbRule(ReplayString) 'replay' ('quiet'->quiet_ | 'nonstop'->nonstop_ | ) (quotedStringPhrase->fname_ | ) : ReplayStringAction verbPhrase = 'replay/replaying command recording' /* set the appropriate option flags */ scriptOptionFlags = ((quiet_ != nil ? ScriptFileQuiet : 0) | (nonstop_ != nil ? ScriptFileNonstop : 0)) ; VerbRule(ReplayQuiet) 'rq' (quotedStringPhrase->fname_ | ) : ReplayStringAction scriptOptionFlags = ScriptFileQuiet ; VerbRule(VagueTravel) 'go' | 'walk' : VagueTravelAction verbPhrase = 'go/going' ; VerbRule(Travel) 'go' singleDir | singleDir : TravelAction verbPhrase = ('go/going ' + dirMatch.dir.name) ; /* * Create a TravelVia subclass merely so we can supply a verbPhrase. * (The parser looks for subclasses of each specific Action class to find * its verb phrase, since the language-specific Action definitions are * always in the language module's 'grammar' subclasses. We don't need * an actual grammar rule, since this isn't an input-able verb, so we * merely need to create a regular subclass in order for the verbPhrase * to get found.) */ class EnTravelVia: TravelViaAction verbPhrase = 'use/using (what)' ; VerbRule(Port) 'go' 'to' ('port' | 'p') : PortAction dirMatch: DirectionProd { dir = portDirection } verbPhrase = 'go/going to port' ; VerbRule(Starboard) 'go' 'to' ('starboard' | 'sb') : StarboardAction dirMatch: DirectionProd { dir = starboardDirection } verbPhrase = 'go/going to starboard' ; VerbRule(In) 'enter' : InAction dirMatch: DirectionProd { dir = inDirection } verbPhrase = 'enter/entering' ; VerbRule(Out) 'exit' | 'leave' : OutAction dirMatch: DirectionProd { dir = outDirection } verbPhrase = 'exit/exiting' ; VerbRule(GoThrough) ('walk' | 'go' ) ('through' | 'thru') singleDobj : GoThroughAction verbPhrase = 'go/going (through what)' askDobjResponseProd = singleNoun ; VerbRule(Enter) ('enter' | 'in' | 'into' | 'in' 'to' | ('walk' | 'go') ('to' | 'in' | 'in' 'to' | 'into')) singleDobj : EnterAction verbPhrase = 'enter/entering (what)' askDobjResponseProd = singleNoun ; VerbRule(GoBack) 'back' | 'go' 'back' | 'return' : GoBackAction verbPhrase = 'go/going back' ; VerbRule(Dig) ('dig' | 'dig' 'in') singleDobj : DigAction verbPhrase = 'dig/digging (in what)' askDobjResponseProd = inSingleNoun ; VerbRule(DigWith) ('dig' | 'dig' 'in') singleDobj 'with' singleIobj : DigWithAction verbPhrase = 'dig/digging (in what) (with what)' omitIobjInDobjQuery = true askDobjResponseProd = inSingleNoun askIobjResponseProd = withSingleNoun ; VerbRule(Jump) 'jump' : JumpAction verbPhrase = 'jump/jumping' ; VerbRule(JumpOffI) 'jump' 'off' : JumpOffIAction verbPhrase = 'jump/jumping off' ; VerbRule(JumpOff) 'jump' 'off' singleDobj : JumpOffAction verbPhrase = 'jump/jumping (off what)' askDobjResponseProd = singleNoun ; VerbRule(JumpOver) ('jump' | 'jump' 'over') singleDobj : JumpOverAction verbPhrase = 'jump/jumping (over what)' askDobjResponseProd = singleNoun ; VerbRule(Push) ('push' | 'press') dobjList : PushAction verbPhrase = 'push/pushing (what)' ; VerbRule(Pull) 'pull' dobjList : PullAction verbPhrase = 'pull/pulling (what)' ; VerbRule(Move) 'move' dobjList : MoveAction verbPhrase = 'move/moving (what)' ; VerbRule(MoveTo) ('push' | 'move') dobjList ('to' | 'under') singleIobj : MoveToAction verbPhrase = 'move/moving (what) (to what)' askIobjResponseProd = toSingleNoun omitIobjInDobjQuery = true ; VerbRule(MoveWith) 'move' singleDobj 'with' singleIobj : MoveWithAction verbPhrase = 'move/moving (what) (with what)' askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun omitIobjInDobjQuery = true ; VerbRule(Turn) ('turn' | 'twist' | 'rotate') dobjList : TurnAction verbPhrase = 'turn/turning (what)' ; VerbRule(TurnWith) ('turn' | 'twist' | 'rotate') singleDobj 'with' singleIobj : TurnWithAction verbPhrase = 'turn/turning (what) (with what)' askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(TurnTo) ('turn' | 'twist' | 'rotate') singleDobj 'to' singleLiteral : TurnToAction verbPhrase = 'turn/turning (what) (to what)' askDobjResponseProd = singleNoun omitIobjInDobjQuery = true ; VerbRule(Set) 'set' dobjList : SetAction verbPhrase = 'set/setting (what)' ; VerbRule(SetTo) 'set' singleDobj 'to' singleLiteral : SetToAction verbPhrase = 'set/setting (what) (to what)' askDobjResponseProd = singleNoun omitIobjInDobjQuery = true ; VerbRule(TypeOn) 'type' 'on' singleDobj : TypeOnAction verbPhrase = 'type/typing (on what)' ; VerbRule(TypeLiteralOn) 'type' singleLiteral 'on' singleDobj : TypeLiteralOnAction verbPhrase = 'type/typing (what) (on what)' askDobjResponseProd = singleNoun ; VerbRule(TypeLiteralOnWhat) [badness 500] 'type' singleLiteral : TypeLiteralOnAction verbPhrase = 'type/typing (what) (on what)' construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = onSingleNoun; } ; VerbRule(EnterOn) 'enter' singleLiteral ('on' | 'in' | 'in' 'to' | 'into' | 'with') singleDobj : EnterOnAction verbPhrase = 'enter/entering (what) (on what)' askDobjResponseProd = singleNoun ; VerbRule(EnterOnWhat) 'enter' singleLiteral : EnterOnAction verbPhrase = 'enter/entering (what) (on what)' construct() { /* * ENTER is a little special, because it could mean ENTER * ON , or it could mean GO INTO . It's * hard to tell which based on the grammar alone, so we have to * do some semantic analysis to make a good decision about it. * * We'll start by assuming it's the ENTER ON form * of the command, and we'll look for a suitable default object * to serve as the iobj. If we can't find a suitable default, we * won't prompt for the missing object as we usually would. * Instead, we'll try re-parsing the command as GO INTO. To do * this, use our custom "asker" - this won't actually prompt for * the missing object, but will instead retry the command as a GO * INTO command. */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.setPrompt(onSingleNoun, enterOnWhatAsker); } ; /* our custom "asker" for the missing iobj in an "ENTER " command */ enterOnWhatAsker: ResolveAsker askMissingObject(targetActor, action, which) { /* * This method is called when the resolver has failed to find a * suitable default for the missing indirect object of ENTER * ON . * * Instead of issuing the prompt that we'd normally issue under * these circumstances, assume that we're totally wrong about the * way we've been interpreting the command: assume that it's not * meant as ENTER ON after all, but was actually * meant as GO IN . So, rephrase the command as such and * start over with the new phrasing. */ throw new ReplacementCommandStringException( 'get in ' + action.getLiteral(), gIssuingActor, gActor); } ; VerbRule(Consult) 'consult' singleDobj : ConsultAction verbPhrase = 'consult/consulting (what)' askDobjResponseProd = singleNoun ; VerbRule(ConsultAbout) 'consult' singleDobj ('on' | 'about') singleTopic | 'search' singleDobj 'for' singleTopic | (('look' | 'l') ('up' | 'for') | 'find' | 'search' 'for' | 'read' 'about') singleTopic 'in' singleDobj | ('look' | 'l') singleTopic 'up' 'in' singleDobj : ConsultAboutAction verbPhrase = 'consult/consulting (what) (about what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun ; VerbRule(ConsultWhatAbout) (('look' | 'l') ('up' | 'for') | 'find' | 'search' 'for' | 'read' 'about') singleTopic | ('look' | 'l') singleTopic 'up' : ConsultAboutAction verbPhrase = 'look/looking up (what) (in what)' whichMessageTopic = DirectObject construct() { /* set up the empty direct object phrase */ dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = inSingleNoun; } ; VerbRule(Switch) 'switch' dobjList : SwitchAction verbPhrase = 'switch/switching (what)' ; VerbRule(Flip) 'flip' dobjList : FlipAction verbPhrase = 'flip/flipping (what)' ; VerbRule(TurnOn) ('activate' | ('turn' | 'switch') 'on') dobjList | ('turn' | 'switch') dobjList 'on' : TurnOnAction verbPhrase = 'turn/turning on (what)' ; VerbRule(TurnOff) ('deactivate' | ('turn' | 'switch') 'off') dobjList | ('turn' | 'switch') dobjList 'off' : TurnOffAction verbPhrase = 'turn/turning off (what)' ; VerbRule(Light) 'light' dobjList : LightAction verbPhrase = 'light/lighting (what)' ; DefineTAction(Strike); VerbRule(Strike) 'strike' dobjList : StrikeAction verbPhrase = 'strike/striking (what)' ; VerbRule(Burn) ('burn' | 'ignite' | 'set' 'fire' 'to') dobjList : BurnAction verbPhrase = 'light/lighting (what)' ; VerbRule(BurnWith) ('light' | 'burn' | 'ignite' | 'set' 'fire' 'to') singleDobj 'with' singleIobj : BurnWithAction verbPhrase = 'light/lighting (what) (with what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(Extinguish) ('extinguish' | 'douse' | 'put' 'out' | 'blow' 'out') dobjList | ('blow' | 'put') dobjList 'out' : ExtinguishAction verbPhrase = 'extinguish/extinguishing (what)' ; VerbRule(Break) ('break' | 'ruin' | 'destroy' | 'wreck') dobjList : BreakAction verbPhrase = 'break/breaking (what)' ; VerbRule(CutWithWhat) [badness 500] 'cut' singleDobj : CutWithAction verbPhrase = 'cut/cutting (what) (with what)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = withSingleNoun; } ; VerbRule(CutWith) 'cut' singleDobj 'with' singleIobj : CutWithAction verbPhrase = 'cut/cutting (what) (with what)' askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(Eat) ('eat' | 'consume') dobjList : EatAction verbPhrase = 'eat/eating (what)' ; VerbRule(Drink) ('drink' | 'quaff' | 'imbibe') dobjList : DrinkAction verbPhrase = 'drink/drinking (what)' ; VerbRule(Pour) 'pour' dobjList : PourAction verbPhrase = 'pour/pouring (what)' ; VerbRule(PourInto) 'pour' dobjList ('in' | 'into' | 'in' 'to') singleIobj : PourIntoAction verbPhrase = 'pour/pouring (what) (into what)' askIobjResponseProd = inSingleNoun ; VerbRule(PourOnto) 'pour' dobjList ('on' | 'onto' | 'on' 'to') singleIobj : PourOntoAction verbPhrase = 'pour/pouring (what) (onto what)' askIobjResponseProd = onSingleNoun ; VerbRule(Climb) 'climb' singleDobj : ClimbAction verbPhrase = 'climb/climbing (what)' askDobjResponseProd = singleNoun ; VerbRule(ClimbUp) ('climb' | 'go' | 'walk') 'up' singleDobj : ClimbUpAction verbPhrase = 'climb/climbing (up what)' askDobjResponseProd = singleNoun ; VerbRule(ClimbUpWhat) [badness 200] ('climb' | 'go' | 'walk') 'up' : ClimbUpAction verbPhrase = 'climb/climbing (up what)' askDobjResponseProd = singleNoun construct() { dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = onSingleNoun; } ; VerbRule(ClimbDown) ('climb' | 'go' | 'walk') 'down' singleDobj : ClimbDownAction verbPhrase = 'climb/climbing (down what)' askDobjResponseProd = singleNoun ; VerbRule(ClimbDownWhat) [badness 200] ('climb' | 'go' | 'walk') 'down' : ClimbDownAction verbPhrase = 'climb/climbing (down what)' askDobjResponseProd = singleNoun construct() { dobjMatch = new EmptyNounPhraseProd(); dobjMatch.responseProd = onSingleNoun; } ; VerbRule(Clean) 'clean' dobjList : CleanAction verbPhrase = 'clean/cleaning (what)' ; VerbRule(CleanWith) 'clean' dobjList 'with' singleIobj : CleanWithAction verbPhrase = 'clean/cleaning (what) (with what)' askIobjResponseProd = withSingleNoun omitIobjInDobjQuery = true ; VerbRule(AttachTo) ('attach' | 'connect') dobjList 'to' singleIobj : AttachToAction askIobjResponseProd = toSingleNoun verbPhrase = 'attach/attaching (what) (to what)' ; VerbRule(AttachToWhat) [badness 500] ('attach' | 'connect') dobjList : AttachToAction verbPhrase = 'attach/attaching (what) (to what)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = toSingleNoun; } ; VerbRule(DetachFrom) ('detach' | 'disconnect') dobjList 'from' singleIobj : DetachFromAction verbPhrase = 'detach/detaching (what) (from what)' askIobjResponseProd = fromSingleNoun ; VerbRule(Detach) ('detach' | 'disconnect') dobjList : DetachAction verbPhrase = 'detach/detaching (what)' ; VerbRule(Open) 'open' dobjList : OpenAction verbPhrase = 'open/opening (what)' ; VerbRule(Close) ('close' | 'shut') dobjList : CloseAction verbPhrase = 'close/closing (what)' ; VerbRule(Lock) 'lock' dobjList : LockAction verbPhrase = 'lock/locking (what)' ; VerbRule(Unlock) 'unlock' dobjList : UnlockAction verbPhrase = 'unlock/unlocking (what)' ; VerbRule(LockWith) 'lock' singleDobj 'with' singleIobj : LockWithAction verbPhrase = 'lock/locking (what) (with what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(UnlockWith) 'unlock' singleDobj 'with' singleIobj : UnlockWithAction verbPhrase = 'unlock/unlocking (what) (with what)' omitIobjInDobjQuery = true askDobjResponseProd = singleNoun askIobjResponseProd = withSingleNoun ; VerbRule(SitOn) 'sit' ('on' | 'in' | 'down' 'on' | 'down' 'in') singleDobj : SitOnAction verbPhrase = 'sit/sitting (on what)' askDobjResponseProd = singleNoun /* use the actorInPrep, if there's a direct object available */ adjustDefaultObjectPrep(prep, obj) { return (obj != nil ? obj.actorInPrep + ' ' : prep); } ; VerbRule(Sit) 'sit' ( | 'down') : SitAction verbPhrase = 'sit/sitting down' ; VerbRule(LieOn) 'lie' ('on' | 'in' | 'down' 'on' | 'down' 'in') singleDobj : LieOnAction verbPhrase = 'lie/lying (on what)' askDobjResponseProd = singleNoun /* use the actorInPrep, if there's a direct object available */ adjustDefaultObjectPrep(prep, obj) { return (obj != nil ? obj.actorInPrep + ' ' : prep); } ; VerbRule(Lie) 'lie' ( | 'down') : LieAction verbPhrase = 'lie/lying down' ; VerbRule(StandOn) ('stand' ('on' | 'in' | 'onto' | 'on' 'to' | 'into' | 'in' 'to') | 'climb' ('on' | 'onto' | 'on' 'to')) singleDobj : StandOnAction verbPhrase = 'stand/standing (on what)' askDobjResponseProd = singleNoun /* use the actorInPrep, if there's a direct object available */ adjustDefaultObjectPrep(prep, obj) { return (obj != nil ? obj.actorInPrep + ' ' : prep); } ; VerbRule(Stand) 'stand' | 'stand' 'up' | 'get' 'up' : StandAction verbPhrase = 'stand/standing up' ; VerbRule(GetOutOf) ('out' 'of' | 'get' 'out' 'of' | 'climb' 'out' 'of' | 'leave' | 'exit') singleDobj : GetOutOfAction verbPhrase = 'get/getting (out of what)' askDobjResponseProd = singleNoun /* use the actorOutOfPrep, if there's a direct object available */ adjustDefaultObjectPrep(prep, obj) { return (obj != nil ? obj.actorOutOfPrep + ' ' : prep); } ; VerbRule(GetOffOf) 'get' ('off' | 'off' 'of' | 'down' 'from') singleDobj : GetOffOfAction verbPhrase = 'get/getting (off of what)' askDobjResponseProd = singleNoun /* use the actorOutOfPrep, if there's a direct object available */ adjustDefaultObjectPrep(prep, obj) { return (obj != nil ? obj.actorOutOfPrep + ' ' : prep); } ; VerbRule(GetOut) 'get' 'out' | 'get' 'off' | 'get' 'down' | 'disembark' | 'climb' 'out' : GetOutAction verbPhrase = 'get/getting out' ; VerbRule(Board) ('board' | ('get' ('in' | 'into' | 'in' 'to' | 'on' | 'onto' | 'on' 'to')) | ('climb' ('in' | 'into' | 'in' 'to'))) singleDobj : BoardAction verbPhrase = 'get/getting (in what)' askDobjResponseProd = singleNoun ; VerbRule(Sleep) 'sleep' : SleepAction verbPhrase = 'sleep/sleeping' ; VerbRule(Fasten) ('fasten' | 'buckle' | 'buckle' 'up') dobjList : FastenAction verbPhrase = 'fasten/fastening (what)' ; VerbRule(FastenTo) ('fasten' | 'buckle') dobjList 'to' singleIobj : FastenToAction verbPhrase = 'fasten/fastening (what) (to what)' askIobjResponseProd = toSingleNoun ; VerbRule(Unfasten) ('unfasten' | 'unbuckle') dobjList : UnfastenAction verbPhrase = 'unfasten/unfastening (what)' ; VerbRule(UnfastenFrom) ('unfasten' | 'unbuckle') dobjList 'from' singleIobj : UnfastenFromAction verbPhrase = 'unfasten/unfastening (what) (from what)' askIobjResponseProd = fromSingleNoun ; VerbRule(PlugInto) 'plug' dobjList ('in' | 'into' | 'in' 'to') singleIobj : PlugIntoAction verbPhrase = 'plug/plugging (what) (into what)' askIobjResponseProd = inSingleNoun ; VerbRule(PlugIntoWhat) [badness 500] 'plug' dobjList : PlugIntoAction verbPhrase = 'plug/plugging (what) (into what)' construct() { /* set up the empty indirect object phrase */ iobjMatch = new EmptyNounPhraseProd(); iobjMatch.responseProd = inSingleNoun; } ; VerbRule(PlugIn) 'plug' dobjList 'in' | 'plug' 'in' dobjList : PlugInAction verbPhrase = 'plug/plugging (what)' ; VerbRule(UnplugFrom) 'unplug' dobjList 'from' singleIobj : UnplugFromAction verbPhrase = 'unplug/unplugging (what) (from what)' askIobjResponseProd = fromSingleNoun ; VerbRule(Unplug) 'unplug' dobjList : UnplugAction verbPhrase = 'unplug/unplugging (what)' ; VerbRule(Screw) 'screw' dobjList : ScrewAction verbPhrase = 'screw/screwing (what)' ; VerbRule(ScrewWith) 'screw' dobjList 'with' singleIobj : ScrewWithAction verbPhrase = 'screw/screwing (what) (with what)' omitIobjInDobjQuery = true askIobjResponseProd = withSingleNoun ; VerbRule(Unscrew) 'unscrew' dobjList : UnscrewAction verbPhrase = 'unscrew/unscrewing (what)' ; VerbRule(UnscrewWith) 'unscrew' dobjList 'with' singleIobj : UnscrewWithAction verbPhrase = 'unscrew/unscrewing (what) (with what)' omitIobjInDobjQuery = true askIobjResponseProd = withSingleNoun ; VerbRule(PushTravelDir) ('push' | 'pull' | 'drag' | 'move') singleDobj singleDir : PushTravelDirAction verbPhrase = ('push/pushing (what) ' + dirMatch.dir.name) ; VerbRule(PushTravelThrough) ('push' | 'pull' | 'drag' | 'move') singleDobj ('through' | 'thru') singleIobj : PushTravelThroughAction verbPhrase = 'push/pushing (what) (through what)' ; VerbRule(PushTravelEnter) ('push' | 'pull' | 'drag' | 'move') singleDobj ('in' | 'into' | 'in' 'to') singleIobj : PushTravelEnterAction verbPhrase = 'push/pushing (what) (into what)' ; VerbRule(PushTravelGetOutOf) ('push' | 'pull' | 'drag' | 'move') singleDobj 'out' ('of' | ) singleIobj : PushTravelGetOutOfAction verbPhrase = 'push/pushing (what) (out of what)' ; VerbRule(PushTravelClimbUp) ('push' | 'pull' | 'drag' | 'move') singleDobj 'up' singleIobj : PushTravelClimbUpAction verbPhrase = 'push/pushing (what) (up what)' omitIobjInDobjQuery = true ; VerbRule(PushTravelClimbDown) ('push' | 'pull' | 'drag' | 'move') singleDobj 'down' singleIobj : PushTravelClimbDownAction verbPhrase = 'push/pushing (what) (down what)' ; VerbRule(Exits) 'exits' : ExitsAction verbPhrase = 'exits/showing exits' ; VerbRule(ExitsMode) 'exits' ('on'->on_ | 'all'->on_ | 'off'->off_ | 'none'->off_ | ('status' ('line' | ) | 'statusline') 'look'->on_ | 'look'->on_ ('status' ('line' | ) | 'statusline') | 'status'->stat_ ('line' | ) | 'statusline'->stat_ | 'look'->look_) : ExitsModeAction verbPhrase = 'turn/turning off exits display' ; VerbRule(HintsOff) 'hints' 'off' : HintsOffAction verbPhrase = 'disable/disabling hints' ; VerbRule(Hint) 'hint' | 'hints' : HintAction verbPhrase = 'show/showing hints' ; VerbRule(Oops) ('oops' | 'o') singleLiteral : OopsAction verbPhrase = 'oops/correcting (what)' ; VerbRule(OopsOnly) ('oops' | 'o') : OopsIAction verbPhrase = 'oops/correcting' ; /* ------------------------------------------------------------------------ */ /* * "debug" verb - special verb to break into the debugger. We'll only * compile this into the game if we're compiling a debug version to begin * with, since a non-debug version can't be run under the debugger. */ #ifdef __DEBUG VerbRule(Debug) 'debug' : DebugAction verbPhrase = 'debug/debugging' ; #endif /* __DEBUG */ frobtads-1.2.3/tads3/lib/adv3/report.t0000644000175000001440000020730311465246012016633 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: command reports * * This module defines the "command report" classes, which the command * execution engine uses to keep track of the status of a command. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Command report objects. The library uses these to control how the * text from a command is displayed. Game code can also use report * objects to show and control command results, but this isn't usually * necessary; game code can usually simply display messages directly. * * Reports are divided into two broad classes: "default" and "full" * reports. * * A "default" report is one that simply confirms that an action was * performed, and provides little additional information. The library * uses default reports for simple commands whose full implications * should normally be obvious to a player typing such commands: take, * drop, put in, and the like. The library's default reports are * usually quite terse: "Taken", "Dropped", "Done". * * A "full" report is one that gives the player more information than a * simple confirmation. These reports typically describe either the * changes to the game state caused by a command or surprising side * effects of a command. For example, if the command is "push button," * and pushing the button opens the door next to the button, a full * report would describe the door opening. * * Note that a full report is warranted any time a command describes * anything beyond a simple confirmation. In our door-opening button * example, opening the door by pushing the button always warrants a * full report, even if the player has already seen the effects of the * button a hundred times before, and even if the button is labeled * "push to open door." It doesn't matter whether or not the * consequences of the command ought to be obvious to the player; what * matters is that the command warrants a report beyond a simple * confirmation. Any time a report is more than a simple confirmation, * it is a full report, no matter how obvious to the player the effects * of the action. * * Full reports are further divided into three subcategories by time * ordering: "main," "before," and "after." "Before" and "after" * reports are ordered before and after (respectively) a main report. */ class CommandReport: object construct() { /* * remember the action with which we're associated, unless a * subclass already specifically set the action */ if (action_ == nil) action_ = gAction; } /* get/set my action */ getAction() { return action_; } setAction(action) { action_ = action; } /* check to see if my action is implicit */ isActionImplicit() { return action_ != nil && action_.isImplicit; } /* check to see if my action is nested in the other report's action */ isActionNestedIn(other) { return (action_ != nil && other.getAction() != nil && action_.isNestedIn(other.getAction())); } /* * Flag: if this property is true, this report indicates a failure. * By default, a report does not indicate failure. */ isFailure = nil /* * Flag: if this property is true, this report indicates an * interruption for interactive input. */ isQuestion = nil /* iteration number current when we were added to the transcript */ iter_ = nil /* the action I'm associated with */ action_ = nil /* * Am I part of the same action as the given report? Returns true if * this action is part of the same iteration and part of the same * action as the other report. */ isPartOf(report) { /* * if I don't have an action, or the other report doesn't have an * action, we're not related */ if (action_ == nil || report.action_ == nil) return nil; /* if our iterations don't match, we're not related */ if (iter_ != report.iter_) return nil; /* check if I'm part of the other report's action */ return action_.isPartOf(report.action_); } ; /* * Group separator. This simply displays separation between groups of * messages - that is, between one set of messages associated with a * single action and a set of messages associated with a different * action. */ class GroupSeparatorMessage: CommandReport construct(report) { /* use the same action and iteration as the given report */ action_ = report.getAction(); iter_ = report.iter_; } /* show the normal command results separator */ showMessage() { say(gLibMessages.complexResultsSeparator); } ; /* * Internal separator. This displays separation within a group of * messages for a command, to visually separate the results from an * implied command from the results for the enclosing command. */ class InternalSeparatorMessage: CommandReport construct(report) { /* use the same action and iteration as the given report */ action_ = report.getAction(); iter_ = report.iter_; } /* show the normal command results separator */ showMessage() { say(gLibMessages.internalResultsSeparator); } ; /* * Report boundary marker. This is a pseudo-report that doesn't display * anything; its purpose is to allow a caller to identify a block of * reports (the reports between two markers) for later removal or * reordering. */ class MarkerReport: CommandReport showMessage() { } ; /* * End-of-description marker. This serves as a marker in the transcript * stream to let us know where the descriptive reports for a given * action end. */ class EndOfDescReport: MarkerReport ; /* * Simple MessageResult-based command report */ class CommandReportMessage: CommandReport, MessageResult construct([params]) { /* invoke our base class constructors */ inherited CommandReport(); inherited MessageResult(params...); } ; /* * default report */ class DefaultCommandReport: CommandReportMessage ; /* * extra information report */ class ExtraCommandReport: CommandReportMessage ; /* * default descriptive report */ class DefaultDescCommandReport: CommandReportMessage ; /* * cosmetic spacing report */ class CosmeticSpacingCommandReport: CommandReportMessage ; /* * base class for all "full" reports */ class FullCommandReport: CommandReportMessage /* * a full report has a sequence number that tells us where the * report goes relative to the others - the higher this number, the * later the report goes */ seqNum = nil ; /* * "before" report - these come before the main report */ class BeforeCommandReport: FullCommandReport seqNum = 1 ; /* * main report */ class MainCommandReport: FullCommandReport seqNum = 2 ; /* * failure report */ class FailCommandReport: FullCommandReport seqNum = 2 isFailure = true ; /* * failure marker - this is a silent report that marks an action as * having failed without actually generating any message text */ class FailCommandMarker: MarkerReport isFailure = true ; /* * "after" report - these come after the main report */ class AfterCommandReport: FullCommandReport seqNum = 3 ; /* * An interruption for interactive input. This is used to report a * prompt for more information that's needed before the command can * proceed, such as a prompt for a missing object, or a disambiguation * prompt. */ class QuestionCommandReport: MainCommandReport isQuestion = true; ; /* * A conversation begin/end report. This is a special marker we insert * into the transcript to flag the boundaries of an NPC's conversational * message. */ class ConvBoundaryReport: CommandReport construct(id) { actorID = id; } /* the actor's ID number, as assigned by the ConversationManager */ actorID = nil ; class ConvBeginReport: ConvBoundaryReport showMessage() { say('<.convbegin ' + actorID + '>'); } ; class ConvEndReport: ConvBoundaryReport construct(id, node) { inherited(id); defConvNode = node; } showMessage() { if (actorID != nil) say('<.convend ' + actorID + ' ' + (defConvNode == nil ? '' : defConvNode) + '>'); } /* the default new ConvNode for the actor */ defConvNode = nil ; /* ------------------------------------------------------------------------ */ /* * Announcements. We use these to track announcements to be made as * part of an action's results. */ class CommandAnnouncement: CommandReport construct([params]) { /* inherit default handling */ inherited(); /* remember our text */ messageText_ = getMessageText(params...); } /* * Get our message text. By default, we simply get the gLibMessages * message given by the property. */ getMessageText([params]) { /* get the library message */ return gLibMessages.(messageProp_)(params...); } /* * Show our message. Our default implementation shows the library * message given by our messageProp_ property, using the parameters * we stored in our constructor. */ showMessage() { /* call gLibMessages to show our message */ say(messageText_); } /* our gLibMessages property */ messageProp_ = nil /* our message text */ messageText_ = '' ; /* * Multiple-object announcement. When the player applies a single * command to a series of objects (as in "take the book and the folder" * or "take all"), we'll show one of these announcements for each object, * just before we execute the command for that object. This announcement * usually just shows the object's name plus suitable punctuation (in * English, a colon), and helps the player see which results go with * which objects. */ class MultiObjectAnnouncement: CommandAnnouncement construct(preCalcMsg, obj, whichObj, action) { /* do the inherited work */ inherited(obj, whichObj, action); /* * if we have a pre-calculated message, use it instead of the * message we just generated - this lets the caller explicitly * set the message as desired */ if (preCalcMsg != nil) messageText_ = preCalcMsg; } /* show the announceMultiActionObject message */ messageProp_ = &announceMultiActionObject ; /* * Default object announcement. We display this announcement whenever * the player leaves out a required object from a command, but the parser * is able to infer which object they must have meant. The parser infers * that an object was intended when a verb requires an object that the * player didn't specify, and there's only one logical choice for the * missing object. We announce our assumption to put it out in the open, * to ensure that the player is immediately alerted if they had something * else in mind. * * In English, this type of announcement conventionally consists of * simply the name of the assumed object, in parenthesis and on a line by * itself. In cases where the object role involves a prepositional * phrase in the verb structure, we generally show the preposition before * the object name. This format usually reads intuitively, by combining * with the text just above of the player's own command: * *. >open *. (the door> *. You try opening the door, but it seems to be locked. *. *. >unlock the door * (with the key) */ class DefaultObjectAnnouncement: CommandAnnouncement construct(obj, whichObj, action, allResolved) { /* remember our object */ obj_ = obj; /* remember the message parameters */ whichObj_ = whichObj; allResolved_ = allResolved; /* remember my action */ action_ = action; /* inherit default handling */ inherited(); } /* get our message text */ getMessageText() { /* get the announcement message from our object */ return obj_.announceDefaultObject(whichObj_, action_, allResolved_); } /* our defaulted object */ obj_ = nil /* our message parameters */ whichObj_ = nil allResolved_ = nil ; /* * Ambiguous object announcement. We display this when the parser * manages to resolve a noun phrase to an object (or objects) from an * ambiguous set of possibilities, without having to ask the player for * help but also without absolute certainty that the objects selected are * the ones the player meant. This happens when more than enough objects * are logical possibilities for selection, but some objects are more * logical choices than others. The parser picks the most logical of the * available options, but since other logical choices are present, the * parser can't be certain that it chose the ones the player actually * meant. Because of this uncertainty, we generate one of these * announcements each time this happens. This report lets the player * know exactly which object we chose, which will immediately alert the * player when our selection is different from what they had in mind. * * In form, this type of announcement usually looks just like a default * object announcement. */ class AmbigObjectAnnouncement: CommandAnnouncement /* show the announceAmbigObject announcement */ messageProp_ = &announceAmbigActionObject ; /* * Remapped action announcement. This is used when we need to mention a * defaulted or disambiguated object, but the player's original input was * remapped to a different action that rearranges the object roles. In * these cases, rather than just announcing the defaulted object name, we * announce the entire remapped action; we show the full action * description because rearrangement of the object roles usually makes * the standard object-only announcement confusing to read, since it * doesn't naturally fit in as a continuation of what the user typed. * * In English, this message is usually shown with the entire verb phrase, * in present participle form ("opening the door"), enclosed in * parentheses and on a line by itself. */ class RemappedActionAnnouncement: CommandAnnouncement construct() { /* use the action as the message parameter */ inherited(gAction); } messageProp_ = &announceRemappedAction ; /* * Each language module must define a class called * ImplicitAnnouncementContext, and three instances of the class, for use * by the generic library. The language module can define other * instances of the context class as needed. We minimally need the * following instances to be defined by the language module: * * standardImpCtx: this is the standard context, which indicates that we * want the default format for the implicit action announcement. * * tryingImpCtx: this is the "trying" context, which indicates that we * want the announcement to phrase the action to indicate that we're only * trying the action, not actually performing it. We use this when the * implicit action has failed, in which case we want our announcement to * say that we're merely attempting the action; the announcement * shouldn't imply that the action has actually been performed. * * askingImpCtx: this is the "asking" context, which indicates that the * action was interrupted with an interactive question, such as a prompt * for a missing direct object. * * We leave it up to the language module to define the class and these * two instances. This lets the language module represent the context * types any way it likes. */ /* * Implicit action announcement. This is displayed when we perform a * command implicitly, which we usually do to fulfill a precondition of * an action. * * In English, we usually show an implied action as the verb participle * phrase ("opening the door"), prefixed with "first", and enclosed in * parentheses on a line by itself (hence, "(first opening the door)"). */ class ImplicitActionAnnouncement: CommandAnnouncement construct(action, msg) { /* use the given message property */ messageProp_ = msg; /* * Inherit default. The first message parameter is the action; * the second is our standard implicit action context object, * indicating that we want the normal context. */ inherited(action, standardImpCtx); } /* * Make this announcement silent. This eliminates any announcement * for this action, but makes it otherwise behave like a normal * implied action. */ makeSilent() { /* clear my message text */ messageText_ = ''; /* * use the silent announcement message if we have to regenerate * our text for another context */ messageProp_ = &silentImplicitAction; } /* * Note that the action we're attempting is merely an attempt that * failed. This will change our report to indicate that we're only * trying the action, rather than suggesting that we actually carried * it out. */ noteJustTrying() { /* note that we're just trying the action */ justTrying = true; /* change our message to the "trying" form */ messageText_ = getMessageText(getAction(), tryingImpCtx); } /* * Note that the action we're attempting is incomplete, as it was * interupted for interactive input (such as asking for a missing * object). */ noteQuestion() { /* note that the action was interrupted with a question */ justAsking = true; /* change our message to the "asking" form */ messageText_ = getMessageText(getAction(), askingImpCtx); } /* * Flag: we're just attempting the action; this is set when we * determine that the implicit action has failed, in which case we * want an announcement indicating that we're merely attempting the * action, not actually performing it. Presume that we're actually * going to perform the action; the action can change this if * necessary. */ justTrying = nil /* flag: the action was interrupted with an interactive question */ justAsking = nil ; class CommandSepAnnouncement: CommandAnnouncement construct() { /* we're not associated with an iteration or action */ action_ = nil; iter_ = 0; } showMessage() { /* show a command separator */ "<.commandsep>"; } ; /* ------------------------------------------------------------------------ */ /* * Command Transcript. This is a "semantic transcript" of the results of * a command. This provides a list of CommandReport objects describing * the results of the command. */ class CommandTranscript: OutputFilter construct() { /* set up a vector to hold the reports */ reports_ = new Vector(5); } /* * flag: the command has failed (i.e., at least one failure report * has been generated) */ isFailure = nil /* * Note that the current action has failed. This is equivalent to * adding a reportFailure() message to the transcript. */ noteFailure() { /* add an empty reportFailure message */ reportFailure(''); } /* * Did the given action fail? This scans the transcript to determine * if there are any failure messages associated with the given * action. */ actionFailed(action) { /* * scan the transcript for failure messages that are associated * with the given action */ return reports_.indexWhich( {x: x.isPartOf(action) && x.isFailure}) != nil; } /* * flag: I'm active; when this is nil, we'll pass text through our * filter routine unchanged */ isActive = true /* * Summarize the current action's reports. This allows a caller to * turn a series of iterated reports into a single report for the * entire action. For example, we could change something like this: * * gold coin: Bob accepts the gold coin. *. gold coin: Bob accepts the gold coin. *. gold coin: Bob accepts the gold coin. * * into this: * * Bob accepts the three gold coins. * * This function runs through the reports for the current action, * submitting each one to the 'cond' callback to see if it's of * interest to the summary. For each consecutive run of two or more * reports that can be summarized, we'll remove the reports that * 'cond' accepted, and we'll remove the multiple-object announcement * reports associated with them, and we'll insert a new report with * the message returned by the 'report' callback. * * 'cond' is called as cond(x), where 'x' is a report object. This * callback returns true if the report can be summarized for the * caller's purposes, nil if not. * * 'report' is called as report(vec), where 'vec' is a Vector * consisting of all of the consecutive report objects that we're now * summarizing. This function returns a string giving the message to * use in place of the reports we're removing. This should be a * summary message, standing in for the set of individual reports * we're removing. * * There's an important subtlety to note. If the messages you're * summarizing are conversational (that is, if they're generated by * TopicEntry responses), you should take care to generate the full * replacement text in the 'report' part, rather than doing so in * separate code that you run after summarizeAction() returns. This * is important because it ensures that the Conversation Manager * knows that your replacement message is part of the same * conversation. If you wait until after summarizeAction() returns * to generate more response text, the conversation manager won't * realize that the additional text is part of the same conversation. */ summarizeAction(cond, report) { local vec = new Vector(8); local rpt = reports_; local cnt = rpt.length(); local i; /* find the first report for the current action */ for (i = 1 ; i <= cnt && rpt[i].getAction() != gAction ; ++i) ; /* iterate over the transcript for the current action */ for ( ; ; ++i) { local ok; /* presume we won't find a summarizable item */ ok = nil; /* if we're still in range, check what we have */ if (i <= cnt) { /* get the current item */ local cur = rpt[i]; /* if this one is of interest, note it in the vector */ if (cond(cur)) { /* add it to our vector of summarizable reports */ vec.append(cur); /* note that we're okay on this round */ ok = true; } else if (cur.ofKind(ImplicitActionAnnouncement) || cur.ofKind(MultiObjectAnnouncement) || cur.ofKind(DefaultCommandReport) || cur.ofKind(ConvBoundaryReport)) { /* we can keep these in summaries */ ok = true; } } /* * If this item isn't summarizable, or we've reached the last * item, generate a summary of any we have so far. (We need * to generate the summary on reaching the last item because * there are no further items that could go in the summary.) */ if (!ok || i == cnt) { /* if we have two or more items, generate a summary */ if (vec.length() > 1) { local insIdx; local txt; /* remove each item in the vector */ foreach (local cur in vec) { local idx; /* get the index of the current item */ idx = rpt.indexOf(cur); /* * we're summarizing this item, so remove the * individual item - subsume it into the summary */ rpt.removeElementAt(idx); --i; --cnt; /* insert the summary here */ insIdx = idx; /* * skip any implicit action announcements, * default command announcements, and * conversational boundary markers */ for (--idx ; idx > 0 && (rpt[idx].ofKind(ImplicitActionAnnouncement) || rpt[idx].ofKind(DefaultCommandReport) || rpt[idx].ofKind(ConvBoundaryReport)) ; --idx) ; /* * if the preceding element is a multi-object * announcement, remove it - let the summary * mention the individual objects if it wants to */ if (idx > 0 && rpt[idx].ofKind(MultiObjectAnnouncement)) { /* remove this element and adjust the counters */ rpt.removeElementAt(idx); --i; --cnt; --insIdx; /* * If this leaves us with a <.convbegin> * preceded directly by a <.convend> for the * same actor, the two cancel each other out. * Simply remove both of them. */ if (idx <= rpt.length() && idx > 1 && rpt[idx].ofKind(ConvBeginReport) && rpt[idx-1].ofKind(ConvEndReport) && rpt[idx].actorID == rpt[idx-1].actorID) { /* * we have a canceling <.convend> + * <.convbegin> pair - simply remove them * both and adjust the counters * accordingly */ rpt.removeRange(idx - 1, idx); i -= 2; cnt -= 2; insIdx -= 2; } } } /* ask the caller for the summary */ txt = report(vec); /* insert it */ rpt.insertAt(insIdx, new MainCommandReport(txt)); ++cnt; ++i; } /* clear the vector */ if (vec.length() > 0) vec.removeRange(1, vec.length()); } /* if we've reached the end of the list, we're done */ if (i > cnt) break; } } /* activate - set up to capture output */ activate() { /* make myself active */ isActive = true; } /* deactivate - stop capturing output */ deactivate() { /* make myself inactive */ isActive = nil; } /* * Count an iteration. An Action should call this once per iteration * if it's a top-level (non-nested) command. */ newIter() { ++iter_; } /* * Flush the transcript in preparation for reading input. This shows * all pending reports, clears the backlog of reports (so that we * don't show them again in the future), and deactivates the * transcript's capture feature so that subsequent output goes * directly to the output stream. * * We return the former activation status - that is, we return true * if the transcript was activated before the call, nil if not. */ flushForInput() { /* show our reports, and deactivate output capture */ local wasActive = showReports(true); /* clear the reports, since we've now shown them all */ clearReports(); /* return the previous activation status */ return wasActive; } /* * Show our reports. Returns true if the transcript was previously * active, nil if not. */ showReports(deact) { local wasActive; /* * remember whether we were active or not originally, then * deactivate (maybe just temporarily) so that we can write out * our reports without recursively intercepting them */ wasActive = isActive; deactivate(); /* first, apply all defined transformations to our transcript */ applyTransforms(); /* * Temporarily cancel any sense context message blocking. We * have already taken into account for each report whether or * not the report was visible when it was generated, so we can * display each report that made it past that check without any * further conditions. */ callWithSenseContext(nil, nil, function() { /* show the reports */ foreach (local cur in reports_) { /* if we're allowed to show this report, show it */ if (canShowReport(cur)) cur.showMessage(); } }); /* * if we were active and we're not being asked to deactivate, * re-activate now that we're finished showing our reports */ if (wasActive && !deact) activate(); /* return the former activation status */ return wasActive; } /* * Add a report. */ addReport(report) { /* check for a failure report */ if (report.isFailure) { /* set the failure flag for the entire command */ isFailure = true; /* * If this is an implied command, and the actor is in "NPC * mode", suppress the report. When an implied action fails * in NPC mode, we act as though we never attempted the * action. */ if (gAction.isImplicit && gActor.impliedCommandMode() == ModeNPC) { /* add a failure marker, not the message report */ reports_.append(new FailCommandMarker()); /* that's all we need to add */ return; } } /* * Do not queue reports made while the sense context is blocking * output because the player character cannot sense the locus of * the action. Note that this check comes before we queue the * report, but after we've noted any effect on the status of the * overall action; even if we're not going to show the report, * its status effects are still valid. */ if (senseContext.isBlocking) return; /* * If the new report's iteration ID hasn't been set already, note * the current iteration in the report. Some types of reports * will have already set a specific iteration before we get here, * so set the iteration ID only if the report hasn't done so * already. */ if (report.iter_ == nil) report.iter_ = iter_; /* append the report */ reports_.append(report); } /* get the last report added */ getLastReport() { local cnt = reports_.length(); return (cnt == 0 ? nil : reports_[cnt]); } /* delete the last report added */ deleteLastReport() { local cnt = reports_.length(); if (cnt != 0) reports_.removeElementAt(cnt); } /* * Add a marker report. This adds a marker to the report stream, * and returns the marker object. The marker doesn't show any * message in the final display, but callers can use a pair of * markers to identify a range of reports for later reordering or * removal. */ addMarker() { /* create the new report */ local marker = new MarkerReport(); /* add it to the stream */ addReport(marker); /* return the new report */ return marker; } /* delete the reports between two markers */ deleteRange(marker1, marker2) { local idx1, idx2; /* find the indices of the two markers */ idx1 = reports_.indexOf(marker1); idx2 = reports_.indexOf(marker2); /* if we found both, delete the range */ if (idx1 != nil && idx2 != nil) reports_.removeRange(idx1, idx2); } /* * Pull out the reports between two markers, and reinsert them at * the end of the transcript. */ moveRangeAppend(marker1, marker2) { local idx1, idx2; /* find the indices of the two markers */ idx1 = reports_.indexOf(marker1); idx2 = reports_.indexOf(marker2); /* if we didn't find both, ignore the request */ if (idx1 == nil || idx2 == nil) return; /* append each item in the range to the end of the report list */ for (local i = idx1 ; i <= idx2 ; ++i) reports_.append(reports_[i]); /* delete the original copies */ reports_.removeRange(idx1, idx2); } /* * Perform a callback on all of the reports in the transcript. * We'll invoke the given callback function func(rpt) once for each * report, with the report object as the parameter. */ forEachReport(func) { reports_.forEach(func); } /* * End the description section of the report. This adds a marker * report that indicates that anything following (and part of the * same action) is no longer part of the description; this can be * important when we apply the default description suppression * transformation, because it tells us not to consider the * non-descriptive messages following this marker when, for example, * suppressing default descriptive messages. */ endDescription() { /* add an end-of-description report */ addReport(new EndOfDescReport()); } /* * Announce that the action is implicit */ announceImplicit(action, msgProp) { /* * If the actor performing the command is not in "player" mode, * save an implicit action announcement; for NPC mode, we treat * implicit command results like any other results, so we don't * want a separate announcement. */ if (gActor.impliedCommandMode() == ModePlayer) { /* create the new report */ local report = new ImplicitActionAnnouncement(action, msgProp); /* add it to the transcript */ addReport(report); /* return it */ return report; } else { /* no need for a report */ return nil; } } /* * Announce a remapped action */ announceRemappedAction() { /* save a remapped-action announcement */ addReport(new RemappedActionAnnouncement()); } /* * Announce one of a set of objects to a multi-object action. We'll * record this announcement for display with our report list. */ announceMultiActionObject(preCalcMsg, obj, whichObj) { /* save a multi-action object announcement */ addReport(new MultiObjectAnnouncement( preCalcMsg, obj, whichObj, gAction)); } /* * Announce an object that was resolved with slight ambiguity. */ announceAmbigActionObject(obj, whichObj) { /* save an ambiguous object announcement */ addReport(new AmbigObjectAnnouncement(obj, whichObj, gAction)); } /* * Announce a default object. */ announceDefaultObject(obj, whichObj, action, allResolved) { /* save the default object announcement */ addReport(new DefaultObjectAnnouncement( obj, whichObj, action, allResolved)); } /* * Add a command separator. */ addCommandSep() { /* add a command separator announcement */ addReport(new CommandSepAnnouncement()); } /* * clear our reports */ clearReports() { /* forget all of the reports in the main list */ if (reports_.length() != 0) reports_.removeRange(1, reports_.length()); } /* * Can we show a given report? By default, we always return true, * but subclasses might want to override this to suppress certain * types of reports. */ canShowReport(report) { return true; } /* * Filter text. If we're active, we'll turn the text into a command * report and add it to our report list, blocking the text from * reaching the underlying stream; otherwise, we'll pass it through * unchanged. */ filterText(ostr, txt) { /* if we're inactive, pass text through unchanged */ if (!isActive) return txt; /* * If the current sense context doesn't allow any messages to be * generated, block the generated text entirely. We want to * block text or not according to the sense context in effect * now; so we must note it now rather than wait until we * actually display the report, since the context could be * different by then. */ if (senseContext.isBlocking) return nil; /* add a main report to our list if the text is non-empty */ if (txt != '') addReport(new MainCommandReport(txt)); /* capture the text - send nothing to the underlying stream */ return nil; } /* apply transformations */ applyTransforms() { /* apply each defined transformation */ foreach (local cur in transforms_) cur.applyTransform(self, reports_); } /* * check to see if the current action has a report matching the given * criteria */ currentActionHasReport(func) { /* check to see if we can find a matching report */ return (findCurrentActionReport(func) != nil); } /* find a report in the current action that matches the given criteria */ findCurrentActionReport(func) { /* * Find an action that's part of the current iteration and which * matches the given function's criteria. Return the first match * we find. */ return reports_.valWhich({x: x.iter_ == iter_ && (func)(x)}); } /* * iteration number - for an iterated top-level command, this helps * us keep the results for a particular iteration grouped together */ iter_ = 1 /* our vector of reports */ reports_ = nil /* our list of transformations */ transforms_ = [defaultReportTransform, implicitGroupTransform, reportOrderTransform, complexMultiTransform] ; /* ------------------------------------------------------------------------ */ /* * Transcript Transform. */ class TranscriptTransform: object /* * Apply our transform to the transcript vector. By default, we do * nothing; each subclass must override this to manipulate the vector * to make the change it wants to make. */ applyTransform(trans, vec) { } ; /* ------------------------------------------------------------------------ */ /* * Transcript Transform: set before/main/after report order. We'll look * for any before/after reports that are out of order with respect to * their main reports, and move them into the appropriate positions. */ reportOrderTransform: TranscriptTransform applyTransform(trans, vec) { /* scan for before/after reports */ for (local i = 1, local len = vec.length() ; i <= len ; ++i) { /* get this item */ local cur = vec[i]; /* if this is a before/after report, consider moving it */ if (cur.ofKind(FullCommandReport) && cur.seqNum != nil) { local idx; /* * This item cares about its sequencing, so it could be * out of order with respect to other items from the same * sequence. Find the first item with a higher sequence * number from the same group, and make sure this item is * before the first such item. */ for (idx = 1 ; idx < i ; ++idx) { local x; /* get this item */ x = vec[idx]; /* if x should come after cur, we need to move cur */ if (x.ofKind(FullCommandReport) && x.seqNum > cur.seqNum && x.isPartOf(cur)) { /* remove cur and reinsert it before x */ vec.removeElementAt(i); vec.insertAt(idx, cur); /* adjust our scan index for the removal */ --i; /* no need to look any further */ break; } } } } } ; /* ------------------------------------------------------------------------ */ /* * Transcript Transform: remove unnecessary default reports. We'll scan * the transcript for default reports for actions which also have * implicit announcements or non-default reports, and remove those * default reports. We'll also remove default descriptive reports which * also have non-default reports in the same action. */ defaultReportTransform: TranscriptTransform applyTransform(trans, vec) { /* scan for default reports */ for (local i = 1, local len = vec.length() ; i <= len ; ++i) { local cur; /* get this item */ cur = vec[i]; /* * if this is a default report, check to see if we want to * keep it */ if (cur.ofKind(DefaultCommandReport)) { /* * check for a main report or an implicit announcement * associated with the same action; if we find anything, * we don't need to keep the default report */ if (vec.indexWhich( {x: (x != cur && cur.isPartOf(x) && (x.ofKind(FullCommandReport) || x.ofKind(ImplicitActionAnnouncement))) }) != nil) { /* we don't need this default report */ vec.removeElementAt(i); /* adjust our scan index for the removal */ --i; --len; } } /* * if this is a default descriptive report, check to see if * we want to keep it */ if (cur.ofKind(DefaultDescCommandReport)) { local fullIdx; /* * check for a main report associated with the same * action */ fullIdx = vec.indexWhich( {x: (x != cur && cur.isPartOf(x) && x.ofKind(FullCommandReport))}); /* * if we found another report, check to see if it comes * before or after any 'end of description' for the same * action */ if (fullIdx != nil) { local endIdx; /* find the 'end of description' report, if any */ endIdx = vec.indexWhich( {x: (x != cur && cur.isPartOf(x) && x.ofKind(EndOfDescReport))}); /* * if we found a full report before the * end-of-description report, then the full report is * part of the description and thus should suppress * the default report; otherwise, the description * portion includes only the default report and the * default report should thus remain */ if (endIdx == nil || fullIdx < endIdx) { /* don't keep the default descriptive report */ vec.removeElementAt(i); /* adjust our indices for the removal */ --i; --len; } } } } } ; /* ------------------------------------------------------------------------ */ /* * Transcript Transform: group implicit announcements. We'll find any * runs of consecutive implicit command announcements, and group each run * into a single announcement listing all of the implied actions. For * example, we'll turn this: * *. >go south *. (first opening the door) *. (first unlocking the door) * * this into: * *. >go south *. (first opening the door and unlocking the door) * * In addition, if we find an implicit announcement in the middle of a * set of regular command reports, and it's for an action nested within * the action generating the regular reports, we'll start a new paragraph * before the implicit announcement. */ implicitGroupTransform: TranscriptTransform applyTransform(trans, vec) { /* * Scan for implicit announcements whose actions failed, and mark * the implicit actions as such. This allows us to phrase the * implicit announcements as attempts rather than as actual * actions, which sounds a little better because it doesn't clash * with the failure report that immediately follows. */ for (local i = 1, local len = vec.length() ; i <= len ; ++i) { local sub; /* get this item */ local cur = vec[i]; /* * If this is an implicit action announcement, and its * corresponding action (or any nested action) failed, mark * the implicit announcement as a mere attempt. Likewise, if * we're interrupting the action for interactive input, it's * likewise just an incomplete attempt. */ if (cur.ofKind(ImplicitActionAnnouncement) && (sub = vec.valWhich( {x: ((x.isFailure || x.isQuestion) && (x.isPartOf(cur) || x.isActionNestedIn(cur)))})) != nil) { /* * it's either a failed attempt or an interruption for a * question - note which one */ if (sub.isFailure) cur.noteJustTrying(); else cur.noteQuestion(); } } /* * Scan for implicit announcement groups. Since we're only * scanning for runs of two or more announcements, we can stop * scanning one short of the end of the list - there's no need to * check the last item because it can't possibly be followed by * another item. Thus, scan while i < len. */ for (local i = 1, local len = vec.length() ; i < len ; ++i) { local origI = i; /* get this item */ local cur = vec[i]; /* * If it's an implied action announcement, and the next one * qualifies for group inclusion, build a group. Note that * because we only loop until we reach the second-to-last * item, we know for sure there is indeed a next item to * index here. */ if (cur.ofKind(ImplicitActionAnnouncement) && canGroupWith(cur, vec[i+1])) { local j; local groupVec; /* create a vector to hold the re-sorted group listing */ groupVec = new Vector(16); /* * Scan items for grouping. This time, we want to scan * to the last (not second-to-last) item in the main * list, since we could conceivably group everything * remaining. */ for (j = i ; j <= len ; ) { /* get this item */ cur = vec[j]; /* unstack any recursive grouping */ j = unstackRecursiveGroup(groupVec, vec, j); /* * if we've used now everything in the list, or the * next item can't be grouped with the current item, * we're done */ if (j > len || !canGroupWith(cur, vec[j])) break; } /* process default object announcements */ processDefaultAnnouncements(groupVec); /* build the composite message for the entire group */ vec[i].messageText_ = implicitAnnouncementGrouper .compositeMessage(groupVec); /* * Clear the messages in the second through last grouped * announcements. Leave the report objects themselves * intact, so that our internal structural record of the * transcript remains as it was, but make them silent in * the displayed text, since these messages are now * subsumed into the combined first message. */ for (++i ; i < j ; ++i) vec[i].messageText_ = ''; /* * continue the main loop from the next element after the * last one we included in the group */ i = j - 1; } /* * If this is an implied action or default object * announcement that interrupts a set of regular command * reports, and it's for an action nested within the action * generating the reports, add a paragraph spacer before the * implicit announcement. */ if ((cur.ofKind(ImplicitActionAnnouncement) || cur.ofKind(DefaultObjectAnnouncement)) && cur.messageText_ != '') { local j; /* scan back for the nearest announcement with text */ for (j = origI - 1 ; j >= 1 && vec[j].messageText_ == '' ; --j) ; /* * if it's a regular command report, and our implied or * default announcement is nested within it, add a * paragraph spacer */ if (j >= 1 && vec[j].ofKind(FullCommandReport) && cur.isActionNestedIn(vec[j])) { /* * insert a paragraph spacer before the announcement * - this will make the implied action and its * results stand out as separate actions, rather than * running everything together without spacing */ vec.insertAt(origI, new GroupSeparatorMessage(cur)); /* adjust our indices for the insertion */ ++i; ++len; } } } } /* * "Unstack" a recursive group of nested announcements. Adds the * recursive group to the output group vector in chronological order, * and returns the index of the next item after the recursive group. * * A recursive group is a set of nested implicit commands, where one * implicit command triggered another, which triggered another, and * so on. The innermost of the nested set is the one that's actually * executed first chronologically, since an implied command must be * carried out before its enclosing command can proceed. For * example: * *. >go south *. (first opening the door) *. (first unlocking the door) *. (first taking the key out of the bag) * * Going south implies opening the door, but before we can open the * door, we must unlock it, and before we can unlock it we must be * holding the key. In report order, the innermost command is listed * last, since it's nested within the enclosing commands. * Chronologically, though, the innermost command is actually * executed first. The purpose of this routine is to unstack these * nested sets, rearranging them into chronological order. */ unstackRecursiveGroup(groupVec, vec, idx) { local cur; /* remember the item we're tasked to work on */ cur = vec[idx]; /* skip the current item */ ++idx; /* * Scan for items nested within vec[idx]. Process each child * item first. An item is nested within us if can be grouped * with us, and its action is a child of our action. */ for (local len = vec.length() ; idx <= len ; ) { /* if the next item is nested within 'cur', process it */ if (canGroupWith(cur, vec[idx]) && vec[idx].getAction().isNestedIn(cur.getAction())) { /* * It's nested with us - process it recursively. Since * our goal is to unstack these reports into * chronological order, we must process our children * first, so that they get added to the group vector * first, since children chronologically predede their * parents. */ idx = unstackRecursiveGroup(groupVec, vec, idx); } else { /* it's not nested within us, so we're done */ break; } } /* add our item to the result vector */ groupVec.append(cur); /* * return the index of the next item; this is simply the current * 'idx' value, since we've advanced it past each item we've * processed */ return idx; } /* * Process default object announcements in a grouped message vector. * * Default object announcements come in two flavors: with and without * message text. Those without message text are present purely to * retain a structural record of the default object in the internal * transcript; we can simply remove these, since the actions that * created them didn't even want default messages. For those that do * include message text, remove them as well, but also use their * actions to replace the corresponding parent actions, so that the * parent actions reflect what actually happened with the final * defaulted objects. */ processDefaultAnnouncements(vec) { /* scan the vector for default announcements */ for (local i = 1, local len = vec.length() ; i <= len ; ++i) { local cur = vec[i]; /* if this is a default announcement, process it */ if (cur.ofKind(DefaultObjectAnnouncement)) { /* * If it has a message, use its action to replace the * parent action. The only way an implied command can * have a defaulted object is for the implied command to * have been stated with too few objects, so that an * askForIobj (for example) occurred. In such cases, the * default announcement will be a child action of the * original underspecified action, so we can simply find * the original action and replace it with the defaulted * action. */ if (cur.messageText_ != '') { /* * Scan for the parent announcement. * * Note that the implicit announcement containing the * parent action will follow the default announcement * in the result list, since the default announcement * is a child of the parent. */ for (local j = i + 1 ; j <= len ; ++j) { /* if this is the parent action, replace it */ if (vec[j].getAction() == cur.getAction().parentAction) { /* this is it - replace the action */ vec[j].setAction(cur.getAction()); /* no need to look any further */ break; } } } /* remove the default announcement from the list */ vec.removeElementAt(i); /* adjust our list index and length for the deletion */ --i; --len; } } } /* * Can we group the second item with the first? Returns true if the * second item is also an implicit action announcement, or it's a * default object announcement whose parent action is the first * item's action. */ canGroupWith(a, b) { /* if 'b' is also an implicit announcement, we can include it */ if (b.ofKind(ImplicitActionAnnouncement)) return true; /* * if 'b' is a default object announcement, and has the same * parent action as 'a', then we can group it; otherwise we can't */ return (b.ofKind(DefaultObjectAnnouncement) && b.getAction().parentAction == a.getAction()); } ; /* ------------------------------------------------------------------------ */ /* * Transcript Transform: Complex Multi-object Separation. If we have an * action that's being applied to one of a bunch of iterated objects, and * the action has any implied command announcements associated with it, * we'll set off the result for this command from its preceding and * following commands by a paragraph separator. */ complexMultiTransform: TranscriptTransform applyTransform(trans, vec) { /* scan the list for multi-object announcements */ foreach (local cur in vec) { /* if it's a multi-object announcement, check it */ if (cur.ofKind(MultiObjectAnnouncement)) { local idx; local cnt; local sep; /* * We have a multi-object announcement. If we find only * one other report within the group, and the report's * text is short, let this report run together with its * neighbors without any additional visual separation. * Otherwise, set this group apart from its neighbors by * adding a paragraph break before and after the group; * this will make the results easier to read by visually * separating each longish response as a separate * paragraph. * * First, find the current item's index. */ idx = vec.indexWhich({x: x == cur}); /* * now scan subsequent items in the same command * iteration, and check to see if (1) we have more than * one item, or (2) the item has a longish message */ for (cnt = 0, ++idx, sep = nil ; idx <= vec.length() && cnt < 2 ; ++idx, ++cnt) { local sub = vec[idx]; /* if we've reached the end of the group, stop scanning */ if (sub.iter_ != cur.iter_) break; /* * If it has long text, add visual separation. Note * that "long" is just a heuristic, because we can't * tell whether the text will wrap in any given * interpreter - that depends on the width of the * interpreter window and the font size, among other * things, and we have no way of knowing any of this * here. */ if (sub.ofKind(CommandReportMessage) && sub.messageText_ != nil && sub.messageText_.length() > 60) { /* it's long - add separation */ sep = true; break; } } /* if we need separation, add it now */ if (sep || cnt > 1) { /* * This is indeed a complex iterated item. Set it * off by paragraph breaks before and after the * iteration. * * First, find the first item in this iteration. If * it's not the first item in the whole transcript, * insert a separator before it. */ idx = vec.indexWhich({x: x.iter_ == cur.iter_}); if (idx != 1) vec.insertAt(idx, new GroupSeparatorMessage(cur)); /* * Next, find the last item in this iteration. If * it's no the last item in the entire transcript, * add a separator after it. */ idx = vec.lastIndexWhich({x: x.iter_ == cur.iter_}); if (idx != vec.length()) vec.insertAt(idx + 1, new GroupSeparatorMessage(cur)); } } /* * if it's a command result from an implied command, and we * have another command result following from the enclosing * command, add a separator between this result and the next * result */ if (cur.ofKind(CommandReportMessage) && cur.isActionImplicit) { local idx; /* get the index of this element */ idx = vec.indexOf(cur); /* * if there's another element following, check to see if * it's a command report for an enclosing action (i.e., * an action that initiated this implied action) */ if (idx < vec.length()) { local nxt; /* get the next element */ nxt = vec[idx + 1]; /* * if it's a command report for an action that * encloses this action, or it's another implicit * announcement, then put a separator before it */ if ((nxt.ofKind(CommandReportMessage) && nxt.getAction() != cur.getAction() && cur.isActionNestedIn(nxt)) || nxt.ofKind(ImplicitActionAnnouncement)) { /* add a separator */ vec.insertAt(idx + 1, new InternalSeparatorMessage(cur)); } } } } } ; /* ------------------------------------------------------------------------ */ /* * Invoke a callback function using a transcript of the given class. * Returns the return value of the callback function. */ withCommandTranscript(transcriptClass, func) { local transcript; local oldTranscript; /* * if we already have an active transcript, just invoke the * function, running everything through the existing active * transcript */ if (gTranscript != nil && gTranscript.isActive) { /* invoke the callback and return the result */ return (func)(); } /* * Create a transcript of the given class. Make the transcript * transient, since it's effectively part of the output stream state * and thus shouldn't be saved or undone. */ transcript = transcriptClass.createTransientInstance(); /* make this transcript the current global transcript */ oldTranscript = gTranscript; gTranscript = transcript; /* install the transcript as a filter on the main output stream */ mainOutputStream.addOutputFilter(transcript); /* make sure we undo our global changes before we leave */ try { /* invoke the callback and return the result */ return (func)(); } finally { /* uninstall the transcript output filter */ mainOutputStream.removeOutputFilter(transcript); /* restore the previous global transcript */ gTranscript = oldTranscript; /* show the transcript results */ transcript.showReports(true); } } frobtads-1.2.3/tads3/lib/adv3/settings.t0000644000175000001440000005050711502002533017150 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - settings file management * * This is a framework that the library uses to keep track of certain * preference settings - things like the NOTIFY, FOOTNOTES, and EXITS * settings. * * The point of this framework is "global" settings - settings that apply * not just to a particular game, but to all games that have a particular * feature. Things like NOTIFY, FOOTNOTES, and some other such features * are part of the standard library, so they tend to be available in most * games. Furthermore, they tend to work more or less the same way in * most games. As a result, a given player will probably prefer to set * the options a particular way for most or all games. If a player * doesn't like score notification, she'll probably dislike it across the * board, not just in certain games. * * This module provides the internal, programmatic core for managing * global preferences. There's no UI in this part of the implementation; * the adv3 library layers the UI on top via the settingsUI object, but * other alternative UIs could be built using the API provided here. * * The framework is extensible - there's an easy, structured way for * library extensions and games to add their own configuration variables * that will be automatically managed by the framework. All you have to * do to create a new configuration variable is to create a SettingsItem * object to represent it. Once you've created the object, the library * will automatically find it and manage it for you. * * This module is designed to be separable from the adv3 library, so that * alternative libraries or stand-alone (non-library-based) games can * reuse it. This file has no dependencies on anything in adv3 (at * least, it shouldn't). */ #include #include /* ------------------------------------------------------------------------ */ /* * A settings item. This encapsulates a single setting variable. When * we're saving or restoring default settings, we'll simply loop over all * objects of this class to get or set the current settings. * * Note that we don't make any assumptions in this base class about the * type of the value associated with this setting, how it's stored, or * how it's represented in the external configuration file. This means * that each subclass has to provide the property or properties that * store the item's value, and must also define the methods that operate * on the value. * * If you want to force a particular default setting for a particular * preference item, overriding the setting stored in the global * preferences file, you can override that SettingsItem's * settingFromText() method. This is the method that interprets the * information in the preferences file, so if you want to ignore the * preferences file setting, override this method to set the hard-coded * value of your choosing. */ class SettingsItem: object /* * The setting's identifier string. This is the ID of the setting as * it appears in the external configuration file. * * The ID should be chosen to ensure uniqueness. To reduce the * chances of name collisions, we suggest a convention of using a two * part name: a prefix identifying the source of the name (an * abbreviated version of the name of the library, library extension, * or game), followed by a period as a separator, followed by a short * descriptive name for the variable. The library follows this * convention by using names of the form "adv3.xxx" - the "adv3" * prefix indicates the standard library. * * The ID should contain only letters, numbers, and periods. Don't * use spaces or punctuation marks (other than periods). * * Note that the ID string is for the program's use, not the * player's, so this isn't something we translate to different * languages. Note, though, that the configuration file is a simple * text file, so it wouldn't hurt to use a reasonably meaningful * name, in case the user takes it upon herself to look at the * contents of the file. */ settingID = '' /* * Display a message fragment that shows the current setting value. * We use this to show the player exactly what we're saving or * restoring in response to a SAVE DEFAULTS or RESTORE DEFAULTS * command, so that there's no confusion about which settings are * included. In most cases, the best thing to show here is the * command that selects the current setting: "NOTIFY ON," for * example. This is for the UI's convenience; it's not used by the * settings manager itself. */ settingDesc = "" /* * Should this item be included in listings shown to the user? If * this is true, the UI will include this setting in a display list * of current settings shown to the user on request, by calling our * settingDesc method. */ includeInListing = true /* * Get the textual representation of the setting - returns a string * representing the setting as it should appear in the external * configuration file. We use this to write the setting to the file. * * Note that this is only needed if the default saveItem() method is * used. */ settingToText() { /* subclasses must override */ } /* * Set the current value to the contents of the given string. The * string contains a textual representation of a setting value, as * previously generated with settingToText(). * * This is only needed if the default restoreItem() method is used. */ settingFromText(str) { /* subclasses must override */ } /* * Load from a settings file. By default, this simply calls the * setting file object to load the data. * * This implementation is suitable for any scalar type, so this won't * need to be overwritten for subclasses that only need to load a * single string value from the file. Subclasses that implement * complex (non-scalar) datatypes can override this as needed to read * multiple line items from the file. */ restoreItem(s) { /* look up the file item by ID */ local fileItem = s.getItem(settingID); /* * if this item appears in the file, retrieve its value; if not, * restore my factory default setting */ settingFromText(fileItem != nil ? fileItem.val_ : factoryDefault); } /* * Save to a settings file. By default, this makes a string out of * our value and updates or adds our corresponding entry in the file. * * This implementation is suitable for any scalar type, so this won't * need to be overwritten for subclasses that only need to store a * single string value in the file. Subclasses that implement * complex (non-scalar) datatypes can override this as needed to * manipulate multiple line items in the file. */ saveItem(s) { /* get the string representation of my value */ local val = settingToText(); /* add or replace it in the file */ s.setItem(settingID, val); } /* * My "factory default" setting. At pre-init time, before we've * loaded the settings file for the first time, we'll run through all * SettingsItems and store their pre-defined source-code settings * here, as though we were saving the values to a file. Later, when * we load a file, if we find the file lacks an entry for this * setting item, we'll simply re-load the factory default from this * property. */ factoryDefault = nil ; /* * A binary settings item - this is for variables that have simple * true/nil values. */ class BinarySettingsItem: SettingsItem /* convert to text - use ON or OFF as the representation */ settingToText() { return isOn ? 'on' : 'off'; } /* parse text */ settingFromText(str) { /* convert to lower-case and strip off spaces */ if (rexMatch('*(+)', str.toLower()) != nil) str = rexGroup(1)[3]; /* get the new setting */ isOn = (str.toLower() == 'on'); } /* our value is true (on) or nil (off) */ isOn = nil ; /* * A string settings item. This is for variables that have scalar string * values. Value strings can contain anything except newlines. */ class StringSettingsItem: SettingsItem /* convert to text */ settingToText() { /* quote the value if necessary */ return quoteValue(val); } /* parse text */ settingFromText(str) { /* * If the value isn't quoted, use the value as-is, trimming off * leading and trailing spaces. If it at least starts with a * quote, remove the leading and trailing quote (if present) and * translate backslash sequences. */ if (rexMatch('^*"', str)) { /* it's quoted - remove the quotes and translate backslashes */ val = rexReplace( [leadTrailSpPat, '\\"', '\\\\', '\\n', '\\r'], str, ['', '"', '\\', '\n', '\r']); } else { /* no leading quote; just trim spaces */ rexMatch(trimSpPat, str); val = rexGroup(1)[3]; } } leadTrailSpPat = static new RexPattern('^+|+$') trimSpPat = static new RexPattern('^*(.*?)*$') /* * Class method: quote a string value for storing in the file. If * the string has any leading or trailing spaces, starts with a * double quote, or contains any newlines, we'll quote it; otherwise * we'll return it as-is. */ quoteValue(str) { /* * if the value needs quoting, quote it, otherwise just return it * unchanged */ if (rexSearch(needQuotePat, str)) { /* * add quotes around the string, and backslash-escape any * quotes, backslashes, or newlines within the string */ return '"' + val.findReplace( ['"', '\\', '\n', '\r'], ['\\"', '\\\\', '\\n', '\\r']) + '"'; } else { /* quotes aren't needed*/ return str; } } needQuotePat = static new RexPattern('^+|^"|[\r\n]') /* our current value string */ val = '' ; /* ------------------------------------------------------------------------ */ /* * The settings manager. This object gathers up some global methods for * managing the saved settings. This base class provides only a * programmatic interface - it doesn't have a user interface. */ settingsManager: object /* * Save the current settings. This writes out the current settings * to the global settings file. * * On any error, the method throws an exception. Possible errors * include: * * - FileCreationException indicates that the settings file couldn't * be opened for writing. */ saveSettings() { /* retrieve the current settings */ local s = retrieveSettings(); /* if that failed, there's nothing more we can do */ if (s == nil) return; /* * Update the file's contents with all of the current in-memory * settings objects (applying the filter condition, if provided). */ forEachInstance(SettingsItem, { item: item.saveItem(s) }); /* write out the settings */ storeSettings(s); } /* * Restore all of the settings. If an error occurs, we'll throw an * exception: * * - SettingsNotSupportedException - this is an older interpreter * that doesn't support the "special files" feature, so we can't save * or restore the default settings. */ restoreSettings() { /* retrieve the current settings */ local s = retrieveSettings(); /* * update all of the in-memory settings objects with the values * from the file */ forEachInstance(SettingsItem, {item: item.restoreItem(s)}); } /* * Retrieve the settings from the global settings file. This returns * a SettingsFileData object that describes the file's contents. * Note that if there simply isn't an existing settings file, we'll * successfully return a SettingsFileData object with no data - the * absence of a settings file isn't an error, but is merely * equivalent to an empty settings file. */ retrieveSettings() { local f; local s = new SettingsFileData(); local linePat = new RexPattern( '*(+)' + '*=*([^\n]*)\n?$'); /* * Try opening the settings file. Older interpreters don't * support the "special files" feature; if the interpreter * predates special file support, it'll throw a "string value * required," since it won't recognize the special file ID value * as a valid filename. */ try { /* open the "library defaults" special file */ f = File.openTextFile(LibraryDefaultsFile, FileAccessRead); } catch (FileNotFoundException fnf) { /* * The interpreter supports the special file, but the file * doesn't seem to exist. Simply return the empty file * contents object. */ return s; } catch (RuntimeError rte) { /* * if the error is "string value required," then we have an * older interpreter that doesn't support special files - * indicate this by returning nil */ if (rte.errno_ == 2019) { /* re-throw this as a SettingsNotSupportedException */ throw new SettingsNotSupportedException(); } /* other exceptions are unexpected, so re-throw them */ throw rte; } /* read the file */ for (;;) { local l; /* read the next line */ l = f.readFile(); /* stop if we've reached end of file */ if (l == nil) break; /* parse the line */ if (rexMatch(linePat, l) != nil) { /* * it parsed - add the variable and its value to the * contents object */ s.addItem(rexGroup(1)[3], rexGroup(2)[3]); } else { /* it doesn't parse, so just keep the line as a comment */ s.addComment(l); } } /* done with the file - close it */ f.closeFile(); /* return the populated file contents object */ return s; } /* store the given SettingsFileData to the global settings file */ storeSettings(s) { /* * Open the "library defaults" file. Note that we don't have to * worry here about the old-interpreter situation that we handle * in retrieveSettings() - if the interpreter doesn't support * special files, we won't ever get this far, because we always * have to retrieve the current file's contents before we can * store the new contents. */ local f = File.openTextFile(LibraryDefaultsFile, FileAccessWrite); /* write each line of the file's contents */ foreach (local item in s.lst_) item.writeToFile(f); /* done with the file - close it */ f.closeFile(); } ; /* ------------------------------------------------------------------------ */ /* * Exception: the settings file mechanism isn't supported on this * interpreter. This indicates that this is an older interpreter that * doesn't support the "special files" feature, so we can't save or load * the global settings file. */ class SettingsNotSupportedException: Exception ; /* ------------------------------------------------------------------------ */ /* * SettingsFileData - this is an object we use to represent the contents * of the configuration file. */ class SettingsFileData: object construct() { /* * We store the contents of the file in two ways: as a list, in * the same order in which the contents appear in the file; and * as a lookup table keyed by variable name. The list lets us * preserve the parts of the file's contents that we don't need * to change when we read it in and write it back out. The * lookup table makes it easy to look up particular variable * values. */ tab_ = new LookupTable(16, 32); lst_ = new Vector(16); } /* * find an item - returns a SettinsFileItem for the key, or nil if * there's no existing item */ getItem(id) { /* return the entry from our ID-keyed table */ return tab_[id]; } /* iterate over all data (non-comment) items in the file */ forEach(func) { /* scan the list */ foreach (local s in lst_) { /* if this is a data item, pass it to the callback */ if (s.ofKind(SettingsFileItem)) func(s.id_, s.val_); } } /* add a variable */ addItem(id, val) { local item; /* create the item descriptor object */ item = new SettingsFileItem(id, val); /* append it to our file-contents-ordered list */ lst_.append(item); /* add it to the lookup table, keyed by the variable ID */ tab_[id] = item; } /* set a variable, adding a new variable if it doesn't already exist */ setItem(id, val) { /* look for an existing SettingsFileItem entry for my ID */ local fileItem = getItem(id); /* update the item if it exists, otherwise add a new one */ if (fileItem != nil) fileItem.val_ = val; else addItem(id, val); } /* add a comment line */ addComment(str) { /* append a comment descriptor to the contents list */ lst_.append(new SettingsFileComment(str)); } /* delete an item */ delItem(id) { /* if it's in our table, delete it */ local item = tab_[id]; if (item != nil) { /* delete it from the lookup table */ tab_.removeElement(id); /* delete it from the file source list */ lst_.removeElement(item); } } /* lookup table of values, keyed by variable name */ tab_ = nil /* a list of SettingsFileItem objects giving the contents of the file */ lst_ = nil ; /* * SettingsFileItem - this object describes a single item within an * external settings file. */ class SettingsFileItem: object construct(id, val) { id_ = id; val_ = val; } /* write this value to a file */ writeToFile(f) { f.writeFile(id_ + ' = ' + val_ + '\n'); } /* the variable's ID */ id_ = nil /* the string representation of the value */ val_ = nil ; /* * SettingsFileComment - this object describes an unparsed line in the * settings file. We treat lines that don't match our parsing rules as * comments. We preserve the contents and order of these lines, but we * don't otherwise try to interpret them. */ class SettingsFileComment: object construct(str) { /* if it doesn't end in a newline, add a newline */ if (!str.endsWith('\n')) str += '\n'; /* remember the string */ str_ = str; } /* write the comment line to a file */ writeToFile(f) { f.writeFile(str_); } /* the text from the file */ str_ = nil ; frobtads-1.2.3/tads3/lib/adv3/resolver.t0000644000175000001440000012047411670446653017177 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: Resolvers. * * This module defines the Resolver classes. A Resolver is an abstract * object that the parser uses to control the resolution of noun phrases * to game objects. Specialized Resolver subclasses allow noun phrases * to be resolved differently according to their grammatical function in * a command. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Basic object resolver. An Action object creates an object resolver * to mediate the process of resolving noun phrases to objects. * * A resolver encapsulates a set of object resolution rules. In most * cases, an action that takes only a direct object can be its own * resolver, because it needs only one set of resolution rules; for this * reason, this basic Resolver implementation is designed to work with * the direct object. Actions with multiple objects will need separate * resolvers for each object, since they might want to use different * rules for the different objects. */ class Resolver: object construct(action, issuingActor, targetActor) { /* remember my action and actor objects */ action_ = action; issuer_ = issuingActor; actor_ = targetActor; /* cache the scope list */ cacheScopeList(); } /* * Are we a sub-phrase resolver? This should return true if we're * being used to resolve a sub-phrase of the main phrase. */ isSubResolver = nil /* * Reset the resolver - this can be called if we are to re-use the * same resolver to resolve a list of noun phrases again. */ resetResolver() { /* forget the equivalents we've resolved so far */ equivs_ = nil; } /* get the action we're resolving */ getAction() { return action_; } /* get the target actor */ getTargetActor() { return actor_; } /* * Match an object's name. By default, we'll call the object's own * matchName method with the given original and adjusted token * lists. Subclasses can override this to call different match * methods (such as matchNameDisambig). */ matchName(obj, origTokens, adjustedTokens) { return obj.matchName(origTokens, adjustedTokens); } /* * Get the resolver for qualifier phrases. By default, this simply * returns myself, since the resolver for qualifiers is in most * contexts the same as the main resolver. * * This can be overridden in contexts where the qualifier resolver * is different from the main resolver. In general, when a * sub-resolver narrows the scope for resolving a phrase, such as an * exclusion list or a disambiguation response, we will want to * resolve qualifiers in the context of the main resolution scope * rather than the narrowed scope. */ getQualifierResolver() { return self; } /* * Get the resolver for possessive phrases. By default, we return a * standard possessive resolver. This can be overridden in contexts * wher ethe possesive resolution context is special. */ getPossessiveResolver() { return new PossessiveResolver(self); } /* * Cache the scope list for this object. By default, we cache the * standard physical scope list for our target actor. * * Note that if a subclass uses completely different rules for * determining scope, it need not store a scope_ list at all. The * scope_ list is purely an implementation detail of the base * Resolver class. A subclass can use whatever internal * implementation it wants, as long as it overrides objInScope() and * getScopeList() to return consistent results. */ cacheScopeList() { /* cache our actor's default scope list */ scope_ = actor_.scopeList(); } /* * Determine if an object is in scope for the purposes of object * resolution. By default, we'll return true if the object is in our * cached scope list - this ensures that we produce results that are * consistent with getScopeList(). * * Some subclasses might want to override this method to decide on * scope without reference to a cached scope list, for efficiency * reasons. For example, if a command's scope is the set of all * objects, caching the full list would take a lot of memory; to save * the memory, you could override cacheScopeList() to do nothing at * all, and then override objInScope() to return true - this will * report that every object is in scope without bothering to store a * list of every object. * * Be aware that if you override objInScope(), you should ensure that * getScopeList() yields consistent results. In particular, * objInScope() should return true for every object in the list * returned by getScopeList() (although getScopeList() doesn't * necessarily have to return every object for which objInScope() is * true). */ objInScope(obj) { return scope_.indexOf(obj) != nil; } /* * Get the full list of objects in scope. By default, this simply * returns our cached scope list. * * For every object in the list that getScopeList() returns, * objInScope() must return true. However, getScopeList() need not * return *all* objects that are in scope as far as objInScope() is * concerned - it can, but a subset of in-scope objects is * sufficient. * * The default implementation returns the complete set of in-scope * objects by simply returning the cached scope list. This is the * same scope list that the default objInScope() checks, which * ensures that the two methods produce consistent results. * * The reason that it's okay for this method to return a subset of * in-scope objects is that the result is only used to resolve * "wildcard" phrases in input, and such phrases don't have to expand * to every possible object. Examples of wildcard phrases include * ALL, missing phrases that need default objects, and locational * phrases ("the vase on the table" - which isn't superficially a * wildcard, but implicitly contains one in the form of "considering * only everything on the table"). It's perfectly reasonable for the * parser to expand a wildcard based on what's actually in sight, in * mind, or whatever's appropriate. So, in cases where you define an * especially expansive objInScope() - for example, a universal scope * like the one TopicResolver uses - it's usually fine to use the * default definition of getScopeList(), which returns only the * objects that are in the smaller physical scope. */ getScopeList() { return scope_; } /* * Is this a "global" scope? By default, the scope is local: it's * limited to what the actor can see, hear, etc. In some cases, the * scope is broader, and extends beyond the senses; we call those * cases global scope. * * This is an advisory status only. The caller musn't take this to * mean that everything is in scope; objInScope() and getScopeList() * must still be used to make the exact determination of what objects * are in scope. However, some noun phrase productions might wish to * know generally whether we're in a local or global sort of scope, * so that they can adjust their zeal at reducing ambiguity. In * cases of global scope, we generally want to be more inclusive of * possible matches than in local scopes, because we have much less * of a basis to guess about what the player might mean. */ isGlobalScope = nil /* * Get the binding for a reflexive third-person pronoun (himself, * herself, itself, themselves). By default, the reflexive binding * is the anaphoric binding from the action - that is, it refers * back to the preceding noun phrase in a verb phrase with multiple * noun slots (as in ASK BOB ABOUT HIMSELF: 'himself' refers back to * 'bob', the previous noun phrase). */ getReflexiveBinding(typ) { return getAction().getAnaphoricBinding(typ); } /* * Resolve a pronoun antecedent, given a pronoun selector. This * returns a list of ResolveInfo objects, for use in object * resolution. 'poss' is true if this is a possessive pronoun (his, * her, its, etc), nil if it's an ordinary, non-possessive pronoun * (him, her, it, etc). */ resolvePronounAntecedent(typ, np, results, poss) { local lst; local scopeLst; /* check the Action for a special override for the pronoun */ lst = getAction().getPronounOverride(typ); /* if there's no override, get the standard raw antecedent list */ if (lst == nil) lst = getRawPronounAntecedent(typ); /* if there is no antecedent, return an empty list */ if (lst != nil && lst != []) { local cur; /* if it's a single object, turn it into a list */ if (dataType(lst) == TypeObject) lst = [lst]; /* add any extra objects for the pronoun binding */ foreach (cur in lst) lst = cur.expandPronounList(typ, lst); /* filter the list to keep only in-scope objects */ scopeLst = new Vector(lst.length()); foreach (cur in lst) { local facets; /* get the object's facets */ facets = cur.getFacets(); /* * If it has any, pick the best one that's in scope. If * not, keep the object only if it's in scope. */ if (facets.length() != 0) { local best; /* * This object has other facets, so we want to * consider the other in-scope facets in case any are * more suitable than the original one. For example, * we might have just referred to a door, and then * traveled through the door to an adjoining room. * We now want the antecedent to be the side (facet) * of the door that's in the new location. */ /* get the in-scope subset of the facets */ facets = (facets + cur).subset({x: objInScope(x)}); /* keep the best facet from the list */ best = findBestFacet(actor_, facets); /* * If we found a winner, use it instead of the * original. */ if (best != nil) cur = best; } /* if the object is in scope, include it in the results */ if (objInScope(cur)) scopeLst.append(cur); } /* create a list of ResolveInfo objects from the antecedents */ lst = scopeLst.toList().mapAll({x: new ResolveInfo(x, 0, np)}); } /* * If there's nothing matching in scope, try to find a default. * Look to see if there's a unique default object matching the * pronoun, and select it if so. */ if (lst == nil || lst == []) lst = getPronounDefault(typ, np); /* run the normal resolution list filtering on the list */ lst = action_.finishResolveList(lst, whichObject, np, nil); /* return the result */ return lst; } /* * Get the "raw" pronoun antecedent list for a given pronoun * selector. This returns a list of objects matching the pronoun. * The list is raw in that it is given as a list of game objects * (not ResolveInfo objects), and it isn't filtered for scope. */ getRawPronounAntecedent(typ) { /* check for pronouns that are relative to the issuer or target */ switch(typ) { case PronounMe: /* * It's a first-person construction. If the issuing actor is * the player character, and we don't treat you/me as * interchangeable, this refers to the player character only * if the game refers to the player character in the second * person (so, if the game calls the PC "you", the player * calls the PC "me"). If the issuing actor isn't the player * character, then a first-person pronoun refers to the * command's issuer. If we allow you/me mixing, then "me" * always means the PC in input, no matter how the game * refers to the PC in output. */ if (issuer_.isPlayerChar && issuer_.referralPerson != SecondPerson && !gameMain.allowYouMeMixing) { /* * the issuer is the player, but the game doesn't call * the PC "you", so "me" has no meaning */ return []; } else { /* "me" refers to the command's issuer */ return [issuer_]; } case PronounYou: /* * It's a second-person construction. If the target actor is * the player character, and we don't treat you/me as * interchangeable, this refers to the player character only * if the game refers to the player character in the first * person (so, if the game calls the PC "me", then the player * calls the PC "you"). If we allow you/me mixing, "you" is * always the PC in input, no matter how the game refers to * the PC in output. * * If the target actor isn't the player character, then a * second-person pronoun refers to either the target actor or * to the player character, depending on the referral person * of the current command that's targeting the actor. If the * command is in the second person, then a second-person * pronoun refers to the actor ("bob, hit you" means for Bob * to hit himself). If the command is in the third person, * then a second-person pronoun is a bit weird, but probably * refers to the player character ("tell bob to hit you" * means for Bob to hit the PC). */ if (actor_.isPlayerChar && actor_.referralPerson != FirstPerson && !gameMain.allowYouMeMixing) { /* * the target is the player character, but the game * doesn't call the PC "me", so "you" has no meaning in * this command */ return []; } else if (actor_.commandReferralPerson == ThirdPerson) { /* * we're addressing the actor in the third person, so YOU * probably doesn't refer to the target actor; the only * other real possibility is that it refers to the player * character */ return [gPlayerChar]; } else { /* in other cases, "you" refers to the command's target */ return [actor_]; } default: /* * it's not a relative pronoun, so ask the target actor for * the antecedent based on recent commands */ return actor_.getPronounAntecedent(typ); } } /* * Determine if "all" is allowed for the noun phrase we're resolving. * By default, we'll just ask the action. */ allowAll() { /* ask the action to determine whether or not "all" is allowed */ return action_.actionAllowsAll; } /* * Get the "all" list - this is the list of objects that we should * use when the object of the command is the special word "all". * We'll ask the action to resolve 'all' for the direct object, * since we are by default a direct object resolver. */ getAll(np) { /* * ask the action to resolve 'all' for the direct object, and * then filter the list and return the result */ return filterAll(action_.getAllDobj(actor_, getScopeList()), DirectObject, np); } /* * Filter an 'all' list to remove things that don't belong. We * always remove the actor executing the command, as well as any * objects explicitly marked as hidden from 'all' lists. * * Returns a ResolveInfo list, with each entry marked with the * MatchedAll flag. */ filterAll(lst, whichObj, np) { local result; /* set up a vector to hold the result */ result = new Vector(lst.length()); /* * run through the list and include elements that we don't want * to exclude */ foreach (local cur in lst) { /* * if this item isn't the actor, and isn't marked for * exclusion from 'all' lists in general, include it */ if (cur != actor_ && !cur.hideFromAll(getAction())) result.append(cur); } /* * create a ResolveInfo for each object, with the 'MatchedAll' * flag set for each object */ result.applyAll({x: new ResolveInfo(x, MatchedAll, np)}); /* run through the list and apply each object's own filtering */ result = getAction().finishResolveList(result, whichObject, np, nil); /* return the result as a list */ return result.toList(); } /* * Get the list of potential default objects. This is simply the * basic 'all' list, not filtered for exclusion with hideFromAll. */ getAllDefaults() { /* ask the action to resolve 'all' for the direct object */ local lst = action_.getAllDobj(actor_, getScopeList()); /* return the results as ResolveInfo objects */ return lst.mapAll({x: new ResolveInfo(x, 0, nil)}); } /* * Filter an ambiguous list of objects ('lst') resolving to a noun * phrase. If the objects in the list vary in the degree of * suitability for the command, returns a list consisting only of the * most suitable objects. If the objects are all equally suitable - * or equally unsuitable - the whole list should be returned * unchanged. * * 'requiredNum' is the number of objects required in the final list * by the caller; if the result list is larger than this, the caller * will consider the results ambiguous. * * 'np' is the noun phrase production that we're resolving. This is * usually a subclass of NounPhraseProd. * * This routine does NOT perform any interactive disambiguation, but * is merely a first attempt at reducing the number of matching * objects by removing the obviously unsuitable ones. * * For example, for an "open" command, if the list consists of one * object that's open and one object that's currently closed, the * result list should include only the closed one, since it is * obvious that the one that's already open does not need to be * opened again. On the other hand, if the list consists only of * open objects, they should all be returned, since they're all * equally unsuitable. * * It is not necessary to reduce the list to a single entry; it is * adequate merely to reduce the ambiguity by removing any items that * are clearly less suitable than the survivors. */ filterAmbiguousNounPhrase(lst, requiredNum, np) { return withGlobals( {:action_.filterAmbiguousDobj(lst, requiredNum, np)}); } /* * Filter an ambiguous noun phrase list using the strength of * possessive qualification, if any. If we have subsets at * different possessive strengths, choose the strongest subset that * has at least the required number of objects. */ filterPossRank(lst, num) { local sub1 = lst.subset({x: x.possRank_ >= 1}); local sub2 = lst.subset({x: x.possRank_ >= 2}); /* * sub2 is the subset with rank 2; if this meets our needs, * return it. If sub2 doesn't meet our needs, then check to see * if sub1 does; sub1 is the subset with rank 1 or higher. If * neither subset meets our needs, use the original list. */ if (sub2.length() >= num) return sub2; else if (sub1.length() >= num) return sub1; else return lst; } /* * Filter a list of ambiguous matches ('lst') for a noun phrase, to * reduce each set of equivalent items to a single such item, if * desired. If no equivalent reduction is desired for this type of * resolver, this can simply return the original list. * * 'np' is the noun phrase production that we're resolving. This is * usually a subclass of NounPhraseProd. */ filterAmbiguousEquivalents(lst, np) { /* if we have only one item, there's obviously nothing redundant */ if (lst.length() == 1) return lst; /* scan the list, looking for equivalents */ for (local i = 1, local len = lst.length() ; i <= len ; ++i) { /* * if this item is marked as equivalent, check for others * like it */ if (lst[i].obj_.isEquivalent) { /* * If this object is in our list of previously-used * equivalents, and we have more equivalents to this * object in our list, then omit this one, so that we * keep a different equivalent this time. This way, if * we have a noun list such as "take coin and coin", * we'll return different equivalent items for each * equivalent noun phrase. */ if (equivs_ != nil && equivs_.indexOf(lst[i].obj_) != nil && lst.lastIndexWhich( {x: x.obj_.isVocabEquivalent(lst[i].obj_)}) > i) { /* * we've already returned this one, and we have * another equivalent later in the list that we can * use instead this time - remove this one from the * list */ lst = lst.removeElementAt(i); /* adjust the our counters for the removal */ --len; --i; } else { /* * We've decided to keep this element, either * because we haven't already returned it as a match * for this noun phrase, or because it's the last * one of its kind. Add it to the list of * equivalents we've previously returned. */ if (equivs_ == nil) equivs_ = new Vector(10); equivs_.append(lst[i].obj_); /* * check each object at a higher index to see if * it's equivalent to this one */ for (local j = i + 1 ; j <= len ; ++j) { /* check this object */ if (lst[i].obj_.isVocabEquivalent(lst[j].obj_)) { /* they match - remove the other one */ lst = lst.removeElementAt(j); /* reduce the list length accordingly */ --len; /* back up our scanning index as well */ --j; } } } } } /* return the updated list */ return lst; } /* * Filter a plural phrase to reduce the set to the logical subset, if * possible. If there is no logical subset, simply return the * original set. * * 'np' is the noun phrase we're resolving; this is usually a * subclass of PluralProd. */ filterPluralPhrase(lst, np) { return withGlobals({:action_.filterPluralDobj(lst, np)}); } /* * Select a resolution for an indefinite noun phrase ("a coin"), * given a list of possible matches. The matches will be given to * us sorted from most likely to least likely, as done by * filterAmbiguousNounPhrase(). * * By default, we simply select the first 'n' items from the list * (which are the most likely matches), because in most contexts, an * indefinite noun phrase means that we should arbitrarily select * any matching object. This can be overridden for contexts in * which indefinite noun phrases must be handled differently. */ selectIndefinite(results, lst, requiredNumber) { /* * arbitrarily choose the first 'requiredNumber' item(s) from * the list */ return lst.sublist(1, requiredNumber); } /* * Get the default object or objects for this phrase. Returns a list * of ResolveInfo objects if a default is available, or nil if no * default is available. This routine does not interact with the * user; it should merely determine if the command implies a default * strongly enough to assume it without asking the user. * * By default, we ask the action for a default direct object. * Resolver subclasses should override this as appropriate for the * specific objects they're used to resolve. */ getDefaultObject(np) { /* ask the action to provide a default direct object */ return withGlobals({:action_.getDefaultDobj(np, self)}); } /* * Resolve a noun phrase involving unknown words, if possible. If * it is not possible to resolve such a phrase, return nil; * otherwise, return a list of resolved objects. This routine does * not interact with the user - "oops" prompting is handled * separately. * * 'tokList' is the token list for the phrase, in the canonical * format as returned from the tokenizer. Each element of 'tokList' * is a sublist representing one token. * * Note that this routine allows for specialized unknown word * resolution separately from the more general matchName mechanism. * The purpose of this method is to allow the specific type of * resolver to deal with unknown words specially, rather than using * the matchName mechanism. This routine is called as a last * resort, only after the matchName mechanism fails to find any * matches. */ resolveUnknownNounPhrase(tokList) { /* by default, we can't resolve an unknown noun phrase */ return nil; } /* * Execute a callback function in the global context of our actor * and action - we'll set gActor and gAction to our own stored actor * and action values, then call the callback, then restore the old * globals. */ withGlobals(func) { /* invoke the function with our action and actor in the globals */ return withParserGlobals(issuer_, actor_, action_, func); } /* the role played by this object, if any */ whichObject = DirectObject /* * Get an indication of which object we're resolving, for message * generation purposes. By default, we'll indicate direct object; * this should be overridden for resolvers of indirect and other * types of objects. */ whichMessageObject = DirectObject /* * The cached scope list, if we have one. Note that this is an * internal implementation detail of the base class; subclasses can * dispense with the cached scope list if they define their own * objInScope() and getScopeList() overrides. * * Note that any subclasses (including Actions) that make changes to * this list MUST ensure that the result only contains unique * entries. The library assumes in several places that there are no * duplicate entries in the list; subtle problems can occur if the * list contains any duplicates. */ scope_ = [] /* my action */ action_ = nil /* the issuing actor */ issuer_ = nil /* the target actor object */ actor_ = nil /* * List of equivalent objects we've resolved so far. We use this to * try to return different equivalent objects when multiple noun * phrases refer to the same set of equivalents. */ equivs_ = nil ; /* ------------------------------------------------------------------------ */ /* * Proxy Resolver - this is used to create resolvers that refer methods * not otherwise overridden back to an underlying resolver */ class ProxyResolver: object construct(origResolver) { /* remember my underlying resolver */ self.origResolver = origResolver; } /* delegate methods we don't override to the underlying resolver */ propNotDefined(prop, [args]) { /* delegate the call to the original resolver */ return origResolver.(prop)(args...); } /* base our possessive resolver on the proxy */ getPossessiveResolver() { return new PossessiveResolver(self); } ; /* ------------------------------------------------------------------------ */ /* * Basic resolver for indirect objects */ class IobjResolver: Resolver /* * we resolve indirect objects for message generation purposes */ whichObject = IndirectObject whichMessageObject = IndirectObject /* resolve 'all' for the indirect object */ getAll(np) { /* * ask the action to resolve 'all' for the indirect object, and * then filter the list and return the result */ return filterAll(action_.getAllIobj(actor_, getScopeList()), IndirectObject, np); } /* get all possible default objects */ getAllDefaults() { /* ask the action to resolve 'all' for the indirect object */ local lst = action_.getAllIobj(actor_, getScopeList()); /* return the results as ResolveInfo objects */ return lst.mapAll({x: new ResolveInfo(x, 0, nil)}); } /* filter an ambiguous noun phrase */ filterAmbiguousNounPhrase(lst, requiredNum, np) { return withGlobals( {:action_.filterAmbiguousIobj(lst, requiredNum, np)}); } /* * Filter a plural phrase to reduce the set to the logical subset, * if possible. If there is no logical subset, simply return the * original set. */ filterPluralPhrase(lst, np) { return withGlobals({:action_.filterPluralIobj(lst, np)}); } /* * Get the default object or objects for this phrase. Since we * resolve indirect objects, we'll ask the action for a default * indirect object. */ getDefaultObject(np) { /* ask the action to provide a default indirect object */ return withGlobals({:action_.getDefaultIobj(np, self)}); } ; /* ------------------------------------------------------------------------ */ /* * Basic topic qualifier resolver. This can be used to resolve qualifier * phrases (such as possessives or locationals) within topic phrases. */ class TopicQualifierResolver: Resolver getAll(np) { /* 'all' doesn't make sense as a qualifier; return an empty list */ return []; } getAllDefaults() { /* we don't need defaults for a qualifier */ return []; } filterAmbiguousNounPhrase(lst, requiredNum, np) { /* we have no basis for any filtering; return the list unchanged */ return lst; } filterPluralPhrase(lst, np) { /* we have no basis for any filtering */ return lst; } getDefaultObject(np) { /* have have no way to pick a default */ return nil; } ; /* ------------------------------------------------------------------------ */ /* * Actor Resolver. We use this to resolve the actor to whom a command * is directed: the actor must be in scope for the player character. */ class ActorResolver: Resolver construct(issuingActor) { /* remember the issuing actor */ actor_ = issuingActor; /* * Use our pseudo-action for "command actor" - this represents * the intermediate step where the issuing actor is doing * whatever physical activity is needed (such as talking) to give * the command to the target actor. This isn't a real action; * it's just an implied intermediate step in the overall action. * We need this mostly because there are assumptions elsewhere in * the resolution process that there's a valid Action object * available. */ action_ = CommandActorAction; /* ...and the action needs an actor */ action_.actor_ = actor_; /* cache the scope list for the actor who issued the command */ cacheScopeList(); } /* * Get the "all" list - this is the list of objects that we should * use when the object of the command is the special word "all". By * default, we'll return everything in scope. */ getAll(np) { /* we can't address 'all' */ throw new ParseFailureException(&cannotAddressMultiple); } /* get the default object list */ getAllDefaults() { /* there are no default actors */ return []; } /* * Filter an ambiguous list of objects. We will filter according to * which objects are most logical as targets of commands. */ filterAmbiguousNounPhrase(lst, requiredNum, np) { local likelyCnt; /* give each object in the list a chance to filter the list */ lst = getAction().finishResolveList(lst, ActorObject, np, requiredNum); /* * Run through the list and see how many objects are likely * command targets. */ likelyCnt = 0; foreach (local cur in lst) { /* if it's a likely command target, count it */ if (cur.obj_.isLikelyCommandTarget) ++likelyCnt; } /* * If some of the targets are likely and others aren't, and we * have at least the required number of likely targets, keep * only the likely ones. If they're all likely or all unlikely, * it doesn't help us because we still have no basis for * choosing some over others; if removing unlikely ones would * not give us enough to meet the minimum number required it * also doesn't help, because we don't have a basis for * selecting as many as are needed. */ if (likelyCnt != 0 && likelyCnt != lst.length() && likelyCnt >= requiredNum) { /* * we have a useful subset of likely ones - filter the list * down to the likely subset */ lst = lst.subset({cur: cur.obj_.isLikelyCommandTarget}); } /* return the result */ return lst; } /* * Filter a plural list */ filterPluralPhrase(lst, np) { /* * Use the same filtering that we use for ambiguous nouns. This * simply reduces the set to the likely command targets if any * are likely command targets. */ return filterAmbiguousNounPhrase(lst, 1, np); } /* get a default object */ getDefaultObject(np) { /* there is never a default for the target actor */ return nil; } /* resolve a noun phrase involving unknown words */ resolveUnknownNounPhrase(tokList) { /* we can't resolve an unknown noun phrase used as an actor target */ return nil; } /* * Get a raw pronoun antecedent list. Since we are resolving the * target actor, pronouns are relative to the issuing actor. */ getRawPronounAntecedent(typ) { /* check for pronouns that are relative to the issuer */ switch(typ) { case PronounMe: /* * It's a first-person construction. If the issuing actor * is the player character, and the PC is in the second * person, this refers to the player character (the game * calls the PC "you", so the player calls the PC "me"). If * the issuing actor is an NPC, this is unconditionally the * PC. */ if (actor_.isPlayerChar && actor_.referralPerson != SecondPerson) return []; else return [actor_]; case PronounYou: /* * It's a second-person construction. If the issuer is the * player character, and the player character is in the * first person, this refers to the player character (the * game calls the PC "me", so the player calls the PC * "you"). If the issuer isn't the player character, "you" * has no meaning. */ if (!actor_.isPlayerChar || actor_.referralPerson != FirstPerson) return []; else return [actor_]; default: /* * it's not a relative pronoun, so ask the issuing actor for * the antecedent based on recent commands */ return actor_.getPronounAntecedent(typ); } } /* we resolve target actors */ whichObject = ActorObject whichMessageObject = ActorObject ; /* * A pseudo-action for "command actor." This represents the act of one * actor (usually the PC) giving a command to another, as in "BOB, GO * NORTH". This isn't a real action that the player can type; it's just * an internal construct that we use to represent the partially resolved * action, when we know that we're addressing another actor but we're * still working on figuring out what we're saying. */ class CommandActorAction: Action ; /* ------------------------------------------------------------------------ */ /* * A possessive resolver is a proxy to a main resolver that considers an * object in scope if (a) it's in scope in the base resolver, or (b) the * object is known to the actor. */ class PossessiveResolver: ProxyResolver objInScope(obj) { /* * An object is in scope for the purposes of a possessive phrase * if it's in scope in the base resolver, or it's known to the * actor. An object is only in scope for a possessive qualifier * phrase if its canResolvePossessive property is true. */ return (obj.canResolvePossessive && (origResolver.objInScope(obj) || origResolver.getTargetActor().knowsAbout(obj))); } /* this is a sub-resolver */ isSubResolver = true ; frobtads-1.2.3/tads3/lib/adv3/actor.t0000644000175000001440000142674111746525765016463 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - actors * * This module provides definitions related to actors, which represent * characters in the game. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Implied command modes */ enum ModePlayer, ModeNPC; /* ------------------------------------------------------------------------ */ /* * A Topic is an object representing some piece of knowledge in the * story. Actors can use Topic objects in commands such as "ask" and * "tell". * * A physical simulation object can be a Topic through multiple * inheritance. In addition, a game can define Topic objects for * abstract conversation topics that don't correspond to simulation * objects; for example, a topic could be created for "the meaning of * life" to allow a command such as "ask guru about meaning of life." * * The key distinction between Topic objects and regular objects is that * a Topic can represent an abstract, non-physical concept that isn't * connected to any "physical" object in the simulation. */ class Topic: VocabObject /* * Is the topic known? If this is true, the topic is in scope for * actions that operate on topics, such as "ask about" and "tell * about." If this is nil, the topic isn't known. * * By default, we mark all topics as known to begin with, which * allows discussion of any topic at any time. Some authors prefer * to keep track of which topics the player character actually has * reason to know about within the context of the game, making topics * available for conversation only after they become known for some * good reason, such as another character mentioning them in * conversation. * * Note that, as with Thing.isKnown, this is only the DEFAULT 'known' * property. Each actor can have its own separate 'known' property * by defining the actor's 'knownProp' to a different property name. */ isKnown = true /* * Topics are abstract objects, so they can't be sensed with any of * the physical senses, even if they're ever included as part of a * containment hierarchy (which might be convenient in some cases * for purposes of associating a topic with a physical object, for * example). */ canBeSensed(sense, trans, ambient) { return nil; } /* a topic cannot by default be used to resolve a possessive phrase */ canResolvePossessive = nil ; /* ------------------------------------------------------------------------ */ /* * FollowInfo - this is an object that tracks an actor's knowledge of * the objects that the actor can follow, which are objects that actor * has witnessed leaving the current location. We keep track of each * followable object and the direction we saw it depart. */ class FollowInfo: object /* the object we can follow */ obj = nil /* the TravelConnector the object traversed to leave */ connector = nil /* * The source location - this is the location we saw the object * depart. We keep track of this because an actor can follow an * object only if the actor is starting from the same location where * the actor saw the object depart. */ sourceLocation = nil ; /* ------------------------------------------------------------------------ */ /* * Postures. A posture describes how an actor is internally positioned: * standing, lying, sitting. We represent postures with objects of * class Posture to make it easier to add new game-specific postures. */ class Posture: object /* * Try getting the current actor into this posture within the given * location, by running an appropriate implied command. */ tryMakingPosture(loc) { } /* put the actor into our posture via a nested action */ setActorToPosture(actor, loc) { } ; /* * Standing posture - this is the default posture, which an actor * normally uses for travel. Actors are generally in this posture any * time they are not sitting on something, lying on something, or * similar. */ standing: Posture tryMakingPosture(loc) { return tryImplicitAction(StandOn, loc); } setActorToPosture(actor, loc) { nestedActorAction(actor, StandOn, loc); } ; /* * Sitting posture. */ sitting: Posture tryMakingPosture(loc) { return tryImplicitAction(SitOn, loc); } setActorToPosture(actor, loc) { nestedActorAction(actor, SitOn, loc); } ; /* * Lying posture. */ lying: Posture tryMakingPosture(loc) { return tryImplicitAction(LieOn, loc); } setActorToPosture(actor, loc) { nestedActorAction(actor, LieOn, loc); } ; /* ------------------------------------------------------------------------ */ /* * Conversation manager output filter. We look for special tags in the * output stream: * * <.reveal key> - add 'key' to the knowledge token lookup table. The * 'key' is an arbitrary string, which we can look up in the table to * determine if the key has even been revealed. This can be used to make * a response conditional on another response having been displayed, * because the key will only be added to the table when the text * containing the <.reveal key> sequence is displayed. * * <.convnode name> - switch the current responding actor to conversation * node 'name'. * * <.convstay> - keep the responding actor in the same conversation node * as it was in at the start of the current response * * <.topics> - schedule a topic inventory for the end of the turn (just * before the next command prompt) */ conversationManager: OutputFilter, PreinitObject /* * Custom extended tags. Games and library extensions can add their * own tag processing as needed, by using 'modify' to extend this * object. There are two things you have to do to add your own tags: * * First, add a 'customTags' property that defines a regular * expression for your added tags. This will be incorporated into * the main pattern we use to look for tags. Simply specify a * string that lists your tags separated by "|" characters, like * this: * * customTags = 'foo|bar' * * Second, define a doCustomTag() method to process the tags. The * filter routine will call your doCustomTag() method whenever it * finds one of your custom tags in the output stream. */ customTags = nil doCustomTag(tag, arg) { /* do nothing by default */ } /* filter text written to the output stream */ filterText(ostr, txt) { local start; /* scan for our special tags */ for (start = 1 ; ; ) { local match; local arg; local actor; local sp; local tag; local nxtOfs; /* scan for the next tag */ match = rexSearch(tagPat, txt, start); /* if we didn't find it, we're done */ if (match == nil) break; /* note the next offset */ nxtOfs = match[1] + match[2]; /* get the argument (the third group from the match) */ arg = rexGroup(3); if (arg != nil) arg = arg[3]; /* pick out the tag */ tag = rexGroup(1)[3].toLower(); /* check which tag we have */ switch (tag) { case 'reveal': /* reveal the key by adding it to our database */ setRevealed(arg); break; case 'convbegin': /* * Internal tag - starting a conversational response for * an actor, identified by an index in our idToActor * vector. Get the actor. */ actor = idToActor[toInteger(arg)]; /* * since we're just starting a response, clear the flag * in the actor indicating that a ConvNode has been set * in the course of this response */ actor.responseSetConvNode = nil; /* remember the new responding actor */ respondingActor = actor; /* done */ break; case 'convend': /* * Ending a conversational response for a given actor, * identified by the first argument, which is an index in * our idToActor vector. */ sp = arg.find(' '); actor = idToActor[toInteger(arg.substr(1, sp - 1))]; /* the rest of the argument is the default new ConvNode */ arg = arg.substr(sp + 1); /* if the new ConvNode is empty, it means no ConvNode */ if (arg == '') arg = nil; /* * if we didn't explicitly set a new ConvNode in the * course of this response, apply the default */ if (!actor.responseSetConvNode) actor.setConvNodeReason(arg, 'convend'); /* * Since we've just finished showing a message that * specifically refers to this actor, the player should * be able to refer to this actor using a pronoun on the * next command. Set the responding actor as the * antecedent for the appropriate singular pronouns for * the player character. Note that we do this at the end * of the response, so that the antecedent is the last * one if we have more than one. */ gPlayerChar.setPronounObj(actor); /* done */ break; case 'convnode': /* * If there's a current responding actor, set its current * conversation node. */ if (respondingActor != nil) { /* * Set the new node. While we're working, capture * any output that occurs so that we can insert it * into the output stream just after the <.convnode> * tag, so that any text displayed within the * ConvNode's activation method (noteActive) is * displayed in the proper order. */ local ctxt = mainOutputStream.captureOutput( {: respondingActor.setConvNodeReason(arg, 'convnode') }); /* re-insert any text we captured */ txt = txt.substr(1, nxtOfs - 1) + ctxt + txt.substr(nxtOfs); } break; case 'convstay': /* * leave the responding actor in the old conversation * node - we don't need to change the ConvNode, but we do * need to note that we've explicitly set it */ if (respondingActor != nil) respondingActor.responseSetConvNode = true; break; case 'topics': /* schedule a topic inventory listing */ scheduleTopicInventory(); break; default: /* check for an extended tag */ doCustomTag(tag, arg); break; } /* continue the search after this match */ start = nxtOfs; } /* * remove the tags from the text by replacing every occurrence * with an empty string, and return the result */ return rexReplace(tagPat, txt, '', ReplaceAll); } /* regular expression pattern for our tags */ tagPat = static new RexPattern( '' + '(reveal|convbegin|convend|convnode|convstay|topics' + (customTags != nil ? '|' + customTags : '') + ')' + '(+(<^rangle>+))?' + '') /* * Schedule a topic inventory request. Game code can call this at * any time to request that the player character's topic inventory * be shown automatically just before the next command prompt. In * most cases, game code won't call this directly, but will request * the same effect using the <.topics> tag in topic response text. */ scheduleTopicInventory() { /* note that we have a request for a prompt-time topic inventory */ pendingTopicInventory = true; } /* * Show or schedule a topic inventory request. If the current * action has a non-default command report, schedule it; otherwise, * show it now. * * If there's a non-default report, don't suggest the topics now; * instead, schedule a topic inventory for the end of the turn. * When we have a non-default report, the report could change the * ConvNode for the actor, so we don't want to show the topic * inventory until we've had a chance to process all of the reports. */ showOrScheduleTopicInventory(actor, otherActor) { /* check for a non-default command report in the current action */ if (gTranscript.currentActionHasReport( {x: x.ofKind(MainCommandReport)})) { /* we have a non-default report - defer the topic inventory */ scheduleTopicInventory(); } else { /* we have only a default report, so show the inventory now */ actor.suggestTopicsFor(otherActor, nil); } } /* * Note that an actor is about to give a response through a * TopicEntry object. We'll remember the actor so that we'll know * which actor is involved in a <.convnode> operation. */ beginResponse(actor) { /* if the actor doesn't have an ID yet, assign one */ if (actor.convMgrID == nil) { /* add the actor to our vector of actors */ idToActor.append(actor); /* the ID is simply the index in this vector */ actor.convMgrID = idToActor.length(); } /* output a <.convbegin> for the actor */ gTranscript.addReport(new ConvBeginReport(actor.convMgrID)); } /* * Finish the response - call this after we finish handling the * response. There must be a subsequent matching call to this * routine whenever beginResponse() is called. * * 'node' is the default new ConvNode the actor for the responding * actor. If another ConvNode was explicitly set in the course of * handling the response, this is ignored, since the explicit * setting overrides this default. */ finishResponse(actor, node) { local prv; local oldNode; /* if the node is a ConvNode object, use its name */ if (node != nil && node.ofKind(ConvNode)) node = node.name; /* * if the previous report was our ConvBeginReport, the * conversation display was empty, so ignore the whole thing */ if ((prv = gTranscript.getLastReport()) != nil && prv.ofKind(ConvBeginReport) && prv.actorID == actor.convMgrID) { /* remove the <.convbegin> report - we're canceling it out */ gTranscript.deleteLastReport(); /* we're done - do not generate the <.convend> */ return; } /* * if the actor has a current ConvNode, and our default next * node is nil, and the current node is marked as "sticky," stay * in the current node rather than switching to a nil default */ if (node == nil && (oldNode = actor.curConvNode) != nil && oldNode.isSticky) { /* it's sticky, so stay at this node */ node = oldNode.name; } /* output a <.convend> for the actor */ gTranscript.addReport(new ConvEndReport(actor.convMgrID, node)); } /* * The current responding actor. Actors should set this when they're * about to show a response to an ASK, TELL, etc. */ respondingActor = nil /* * Mark a tag as revealed. This adds an entry for the tag to the * revealedNameTab table. We simply set the table entry to 'true'; * the presence of the tag in the table constitutes the indication * that the tag has been revealed. * * (Games and library extensions can use 'modify' to override this * and store more information in the table entry. For example, you * could store the time when the information was first revealed, or * the location where it was learned. If you do override this, just * be sure to set the revealedNameTab entry for the tag to a non-nil * and non-zero value, so that any code testing the presence of the * table entry will see that the slot is indeed set.) */ setRevealed(tag) { revealedNameTab[tag] = true; } /* * The global lookup table of all revealed keys. This table is keyed * by the string naming the revelation; the value associated with * each key is not used (we always just set it to true). */ revealedNameTab = static new LookupTable(32, 32) /* a vector of actors, indexed by their convMgrID values */ idToActor = static new Vector(32) /* preinitialize */ execute() { /* add every ConvNode object to our master table */ forEachInstance(ConvNode, { obj: obj.getActor().convNodeTab[obj.name] = obj }); /* * set up the prompt daemon that makes automatic topic inventory * suggestions when appropriate */ new PromptDaemon(self, &topicInventoryDaemon); } /* * Prompt daemon: show topic inventory when appropriate. When a * response explicitly asks us to show a topic inventory using the * <.topics> tag, or when other game code asks us to show topic * inventory by calling scheduleTopicInventory(), we'll show the * inventory just before the command input prompt. */ topicInventoryDaemon() { /* if we have a topic inventory scheduled, show it now */ if (pendingTopicInventory) { /* * Show the player character's topic inventory. This is not * an explicit inventory request, since the player didn't ask * for it. */ gPlayerChar.suggestTopics(nil); /* we no longer have a pending inventory request */ pendingTopicInventory = nil; } } /* flag: we have a pending prompt-time topic inventory request */ pendingTopicInventory = nil ; /* ------------------------------------------------------------------------ */ /* * A plug-in topic database. The topic database is a set of TopicEntry * objects that specify the responses to queries on particular topics. * The exact nature of the queries that a particular topic database * handles is up to the database subclass to define; we just provide the * abstract mechanism for finding and displaying responses. * * This is a "plug-in" database in that it's meant to be added into other * classes using multiple inheritance. This isn't meant to be used as a * stand-alone abstract topic entry container. */ class TopicDatabase: object /* * Is the topic group active? A TopicEntry always checks with its * container to see if the children of the container are active. By * default, everything in the database is active. */ topicGroupActive = true /* * Get the score adjustment for all topic entries contained within. * The default adjustment is zero; TopicGroup objects can use this to * adjust the score for their nested entries. */ topicGroupScoreAdjustment = 0 /* * Handle a topic. Look up the topic in our topic list for the * given conversational action type. If we find a match, we'll * invoke the matching topic list entry to handle it. We'll return * true if we find a match, nil if not. */ handleTopic(fromActor, topic, convType, path) { local resp; /* find the best response */ resp = findTopicResponse(fromActor, topic, convType, path); /* if we found a match, let it handle the topic */ if (resp != nil) { /* show the response */ showTopicResponse(fromActor, topic, resp); /* tell the caller we handled it */ return true; } else { /* tell the caller we didn't handle it */ return nil; } } /* show the response we found for a topic */ showTopicResponse(fromActor, topic, resp) { /* let the response object handle it */ resp.handleTopic(fromActor, topic); } /* * find the best response (a TopicEntry object) for the given topic * (a ResolvedTopic object) */ findTopicResponse(fromActor, topic, convType, path) { local topicList; local best, bestScore; /* * Get the list of possible topics for this conversation type. * The topic list is contained in one of our properties; exactly * which property is determined by the conversation type. */ topicList = self.(convType.topicListProp); /* if the topic list is nil, we obviously won't find the topic */ if (topicList == nil) return nil; /* scan our topic list for the best match(es) */ best = new Vector(); bestScore = nil; foreach (local cur in topicList) { /* get this item's score */ local score = cur.adjustScore(cur.matchTopic(fromActor, topic)); /* * If this item has a score at all, and the topic entry is * marked as active, and it's best (or only) score so far, * note it. Ignore topics marked as not active, since * they're in the topic database only provisionally. */ if (score != nil && cur.checkIsActive() && (bestScore == nil || score >= bestScore)) { /* clear the vector if we've found a better score */ if (bestScore != nil && score > bestScore) best = new Vector(); /* add this match to the list of ties for this score */ best.append(cur); /* note the new best score */ bestScore = score; } } /* * If the best-match list is empty, we have no matches. If * there's just one match, we have a winner. If we found more * than one match tied for first place, we need to pick one * winner. */ if (best.length() == 0) { /* no matches at all */ best = nil; } else if (best.length() == 1) { /* exactly one match - it's easy to pick the winner */ best = best[1]; } else { /* * We have multiple topics tied for first place. Run through * the topic list and ask each topic to propose the winner. */ local toks = topic.topicProd.getOrigTokenList().mapAll( {x: getTokVal(x)}); local winner = nil; foreach (local t in best) { /* ask this topic what it thinks the winner should be */ winner = t.breakTopicTie(best, topic, fromActor, toks); /* if the topic had an opinion, we can stop searching */ if (winner != nil) break; } /* * If no one had an opinion, run through the list again and * try to pick by vocabulary match strength. This is only * possible when all of the topics are associated with * simulation objects; if any topics have pattern matches, we * can't use this method. */ if (winner == nil) { local rWinner = nil; foreach (local t in best) { /* get this topic's match object(s) */ local m = t.matchObj; if (m == nil) { /* * there's no match object - it's not comparable * to others in terms of match strength, so we * can't use this method to break the tie */ winner = nil; break; } /* * If it's a list, search for an element with a * ResolveInfo entry in the topic match, using the * strongest match if we find more than one. * Otherwise, just use the strength of this match. */ local ri; if (m.ofKind(Collection)) { /* search for a ResolveInfo object */ foreach (local mm in m) { /* get this topic */ local riCur = topic.getResolveInfo(mm); /* if this is the best match so far, keep it */ if (compareVocabMatch(riCur, ri) > 0) ri = riCur; } } else { /* get the ResolveInfo object */ ri = topic.getResolveInfo(m); } /* * if we didn't find a match, we can't use this * method to break the tie */ if (ri == nil) { winner = nil; break; } /* * if this is the best match so far, elect it as the * tentative winner */ if (compareVocabMatch(ri, rWinner) > 0) { rWinner = ri; winner = t; } } } /* * if there's a tie-breaking winner, use it; otherwise just * arbitrarily pick the first item in the list of ties */ best = (winner != nil ? winner : best[1]); } /* * If there's a hierarchical search path, AND this topic entry * defines a deferToEntry() method, look for matches in the * inferior databases on the path and check to see if we want to * defer to one of them. */ if (best != nil && path != nil && best.propDefined(&deferToEntry)) { /* look for a match in each inferior database */ for (local i = 1, local len = path.length() ; i <= len ; ++i) { local inf; /* * Look up an entry in this inferior database. Pass in * the remainder of the path, so that the inferior * database can consider further deferral to its own * inferior databases. */ inf = path[i].findTopicResponse(fromActor, topic, convType, path.sublist(i + 1)); /* * if we found an entry in this inferior database, and * our entry defers to the inferior entry, then ignore * the match in our own database */ if (inf != nil && best.deferToEntry(inf)) return nil; } } /* return the best matching response object, if any */ return best; } /* * Compare the vocabulary match strengths of two ResolveInfo objects, * for the purposes of breaking ties in topic matching. Uses the * usual comparison/sorting return value conventions: -1 means that a * is weaker than b, 0 means they're equivalent, 1 means a is * stronger than b. */ compareVocabMatch(a, b) { /* * If both are nil, they're equivalent. If one or the other is * nil, the non-nil item is stronger. */ if (a == nil && b == nil) return 0; if (a == nil) return -1; if (b == nil) return 1; /* * Both are valid objects, so compare based on the vocabulary * match flags. */ local fa = a.flags_, fb = b.flags_; /* check plural truncations - no plural truncation is better */ if ((fa & PluralTruncated) && !(fb & PluralTruncated)) return -1; if (!(fa & PluralTruncated) && (fb & PluralTruncated)) return 1; /* check any truncation - no truncation is better */ if ((fa & VocabTruncated) && !(fb & VocabTruncated)) return -1; if (!(fa & VocabTruncated) && (fb & VocabTruncated)) return 1; /* we can't find any reason to prefer one over the other */ return 0; } /* show our suggested topic list */ showSuggestedTopicList(lst, asker, askee, explicit) { /* get the asking actor's scope list for use later */ scopeList = asker.scopeList(); /* remove items that have redundant list groups and full names */ for (local i = 1, local len = lst.length() ; i <= len ; ++i) { local a = lst[i]; /* check for redundant elements */ for (local j = i + 1 ; j <= len ; ++j) { local b = lst[j]; /* * If item 'a' matches item 'b', and both are active, * remove item 'b'. We only need to remove redundant items if they're both active, since inactive items */ if (a.suggestionGroup == b.suggestionGroup && a.fullName == b.fullName && a.isSuggestionActive(asker, scopeList) && b.isSuggestionActive(asker, scopeList)) { /* delete item 'b' from the list */ lst.removeElementAt(j); /* adjust our indices for the deletion */ --j; --len; } } } /* show our list */ new SuggestedTopicLister(asker, askee, explicit) .showList(asker, nil, lst, 0, 0, nil, nil); } /* * Flag: this database level should limit topic suggestions (for the * TOPICS and TALK TO commands) to its own topics, excluding any * topics inherited from the "broader" context. If this property is * set to true, then we won't include suggestions from any lower * level of the database hierarchy. If this property is nil, we'll * also include any topic suggestions from the broader context. * * Topic databases are arranged into a fixed hierarchy for an actor. * At the top level is the current ConvNode object; at the next level * is the ActorState; and at the bottom level is the Actor itself. * So, if the ConvNode's limitSuggestions property is set to true, * then the suggestions for the actor will include ONLY the ConvNode. * If the ConvNode has the property set to nil, but the ActorState * has it set to true, then we'll include the ConvNode and the * ActorState suggestions. * * By default, we set this to nil. This should usually be set to * true for any ConvNode or ActorState where the NPC won't allow the * player to stray from the subject. For example, if a ConvNode only * accepts a YES or NO response to a question, then this property * should probably be set to true in the ConvNode, since other * suggested topics won't be accepted as conversation topics as long * as the ConvNode is active. */ limitSuggestions = nil /* * Add a topic to our topic database. We'll add it to the * appropriate list or lists as indicated in the topic itself. * 'topic' is a TopicEntry object. */ addTopic(topic) { /* add the topic to each list indicated in the topic */ foreach (local cur in topic.includeInList) addTopicToList(topic, cur); } /* remove a topic from our topic database */ removeTopic(topic) { /* remove the topic from each of its lists */ foreach (local cur in topic.includeInList) removeTopicFromList(topic, cur); } /* add a suggested topic */ addSuggestedTopic(topic) { /* add the topic to our suggestion list */ addTopicToList(topic, &suggestedTopics); } /* remove a suggested topic */ removeSuggestedTopic(topic) { /* add the topic to our suggestion list */ removeTopicFromList(topic, &suggestedTopics); } /* * Add a topic to the given topic list. The topic list is given as a * property point; for example, we'd specify &askTopics to add the * topic to our ASK list. */ addTopicToList(topic, listProp) { /* if we haven't created this topic list vector yet, create it now */ if (self.(listProp) == nil) self.(listProp) = new Vector(8); /* add the topic */ self.(listProp).append(topic); } /* remove a topic from the given topic list */ removeTopicFromList(topic, listProp) { /* if the list exists, remove the topic from it */ if (self.(listProp) != nil) self.(listProp).removeElement(topic); } /* * Our list of suggested topics. These are SuggestedTopic objects * that describe things that another actor wants to ask or tell this * actor about. */ suggestedTopics = nil /* * Get the "owner" of the topics in this database. The meaning of * "owner" varies according to the topic database type; for actor * topic databases, for example, this is the actor. Generally, the * owner is the object being queried about the topic, from the * player's perspective. Each type of database should define this * method to return the appropriate object. */ getTopicOwner() { return nil; } ; /* * A TopicDatabase for an Actor. This is used not only directly for an * Actor but also for an actor's sub-databases, in ActorState and * ConvNode. * * Actor topic databases field queries for the various types of * topic-based interactions an actor can participate in: ASK, TELL, SHOW, * GIVE, and so on. * * Each actor has its own topic database, which means each actor can have * its own set of responses. Actor states can also have their own * separate topic databases; this makes it easy to make an actor's * response to a particular question vary according to the actor's state. * Conversation nodes can also have their own separate databases, which * allows for things like threaded conversations. */ class ActorTopicDatabase: TopicDatabase /* * Initiate conversation on the given simulation object. If we can * find an InitiateTopic matching the given object, we'll show its * topic response and return true; if we can't find a topic to * initiate, we'll simply return nil. */ initiateTopic(obj) { /* find an initiate topic for the given object */ if (handleTopic(gPlayerChar, obj, initiateConvType, nil)) { /* * we handled the topic, so note that we're in conversation * with the player character now */ getTopicOwner().noteConversation(gPlayerChar); /* indicate that we found a topic to initiate */ return true; } /* we didn't find a topic to initiate */ return nil; } /* show a topic response */ showTopicResponse(fromActor, topic, resp) { local actor = getTopicOwner(); local newNode; /* * note whether the response is conversational - we need to do * this ahead of time, since invoking the response can sometimes * have the side effect of changing the response's status */ local isConv = resp.isConversational; /* tell the conversation manager we're starting a response */ conversationManager.beginResponse(actor); /* let the response object handle it */ resp.handleTopic(fromActor, topic); /* * By default, after showing a response, we want to leave the * conversation node tree entirely if we didn't explicitly set * the next node in the course of the response. So, set the * default new node to 'nil'. However, if the topic is * non-conversational, it shouldn't affect the conversation * thread at all, so leave the current node unchanged. */ if (isConv) newNode = nil; else newNode = actor.curConvNode; /* tell the conversation manager we're done with the response */ conversationManager.finishResponse(actor, newNode); } /* * Our 'ask about', 'ask for', 'tell about', 'give', 'show', * miscellaneous, command, and self-initiated topic databases - these * are vectors we initialize as needed. Since every actor and every * actor state has its own separate topic database, it's likely that * the bulk of these databases will be empty, so we don't bother even * creating a vector for a topic list until the first topic is added. * This means we have to be able to cope with these being nil * anywhere we use them. */ askTopics = nil askForTopics = nil tellTopics = nil showTopics = nil giveTopics = nil miscTopics = nil commandTopics = nil initiateTopics = nil /* our special command database */ specialTopics = nil ; /* ------------------------------------------------------------------------ */ /* * A "suggested" topic. These provide suggestions for things the player * might want to ASK or TELL another actor about. At certain times * (specifically, when starting a conversation with HELLO or TALK TO, or * when the player enters a TOPICS command to explicitly ask for a list * of topic suggestions), we'll look for these objects in the actor or * actor state for the actor to whom we're talking. We'll show a list * of each currently active suggestion we find. This gives the player * some guidance of what to talk about. For example: * * >talk to bob *. "Excuse me," you say. * * Bob looks up from his newspaper. "Yes? Oh, you again." * * (You'd like to ask him about the black book, the candle, and the * bell, and tell him about the crypt.) * * Topic suggestions are entirely optional. Some authors don't like the * idea, since they think it's too much like a menu system, and just * gives away the solution to the game. If you don't want to have * anything to do with topic suggestions, we won't force you - simply * don't define any SuggestedTopic objects, and the library will never * offer suggestions and will even disable the TOPICS command. * * If you do want to use topic suggestions, the easiest way to use this * class is to combine it using multiple inheritance with a TopicEntry * object. You just have to add SuggestedTopic to the superclass list * for your topic entry object, and give the suggested topic a name * string (using a property and format defined by the language-specific * library) to display in suggestions lists. Doing this, the suggestion * will automatically be enabled whenever the topic entry is available, * and will automatically be removed from the suggestions when the topic * is invoked in conversation (in other words, we'll only suggest asking * about the topic until it's been asked about once). * * Topic suggestions can be associated with an actor or an actor state; * these are topics that a given character would like to talk to the * associated actor about. The association is a bit tricky: suggested * topic objects are stored with the actor being *talked to*. For * example, if we want to suggest topics that the player character might * want to ASK BILL ABOUT, we store these suggestions with *Bill*. We * do NOT store the suggestions with the player character. This might * seem backwards at first glance, since fundamentally the suggestions * belong in the player character's "brain" - they are, after all, * things the player character wants to talk about. In practice, * though, there are two things that make it easier to keep the * information with the character being asked. First, in most games, * there's just one player character, so one of the two actors in each * association will always be the player character; by storing the * objects with the NPC, we can just let the PC be assumed as the other * actor as a default, saving us some typing that would be necessary if * we had to specify each object in the other direction. Second, we * keep the *response* objects associated with the character being asked * - that association is intuitive, at least. The thing is, we can * usually combine the suggestion and response into a single object, * saving another bunch of typing; if we didn't keep the suggestion with * the character being asked, we couldn't combine the suggestions and * responses this way, since they'd have to be associated with different * actors. */ class SuggestedTopic: object /* * The name of the suggestion. The rules for setting this vary by * language; in the English version, we'll display the fullName when * we show a stand-alone item, and the groupName when we appear in a * list group (such as a group of ASK ABOUT or TELL ABOUT * suggestions). * * In English, the fullName should be suitable for use after * 'could': "You could , , or ". * * In English, the phrasing where the 'name' property is used * depends on the specific subclass, but it should usually be a * qualified noun phrase (that is, it should include a qualifier * such as "a" or "the" or a possessive). For ASK and TELL, for * example, the 'name' should be suitable for use after ABOUT: "You * could ask him about , , or ." * * By default, we'll walk up our 'location' tree looking for another * suggested topic; if we find one, we'll use its corresponding name * values. */ fullName = (fromEnclosingSuggestedTopic(&fullName, '')) name = (fromEnclosingSuggestedTopic(&name, '')) /* * Our associated topic. In most cases, this will be initialized * automatically: if this suggested topic object is also a * TopicEntry object (using multiple inheritance), we'll set this * during start-up to 'self', or if our location is a TopicEntry, * we'll set this to our location. This only needs to be * initialized manually if neither of those conditions is true. */ associatedTopic = nil /* * Set the location to the actor to ask or tell about this topic. * This is the target of the ASK ABOUT or TELL ABOUT command, NOT * the actor who's doing the asking. This can also be set to a * TopicEntry object, in which case we'll be associated with the * actor with which the topic entry is associated, and we'll also * automatically tie the topic entry to this suggestion. * * Because we're using the location property, you can use the '+' * notation to add a suggested topic to the target actor, state * objects, or topic entry. */ location = nil /* * The actor who *wants* to ask or tell about this topic. Our * location property gives the actor to be asked or told, because * we're associated with the target actor - the same actor who has * the TopicEntry information for the topic. This property, in * contrast, gives the actor who's doing the asking. * * By default, we return the player character; in most cases, you * won't have to override this. In most games, only the player * character uses the suggested topic mechanism, because there's no * reason to suggest topics for NPC's - they're just automata, after * all, so if we want them to ask something, we can just program * them to ask it directly. Also, most games have only one player * character. Games that meet these criteria won't ever have to * override this. If you do have multiple player characters, you'll * probably want to override this for each suggested topic to * indicate which character wants to ask about the topic, as the * different player characters might have different things they'd * want to talk about. */ suggestTo = (gPlayerChar) /* the ListGroup with which we're to list this suggestion */ suggestionGroup = [] /* find the nearest enclosing SuggestedTopic parent */ findEnclosingSuggestedTopic() { /* walk up our location list */ for (local loc = location ; loc != nil ; loc = loc.location) { /* if this is a suggested topic, it's what we're looking for */ if (loc.ofKind(SuggestedTopic)) return loc; } /* didn't find anything */ return nil; } /* find the outermost enclosing SuggestedTopic parent */ findOuterSuggestedTopic() { local outer; /* walk up our location list */ for (local loc = self, outer = nil ; loc != nil ; loc = loc.location) { /* if this is a suggested topic, it's the outermost so far */ if (loc.ofKind(SuggestedTopic)) outer = loc; } /* return the outermost suggested topic we found */ return outer; } /* * get a property from the nearest enclosing SuggestedTopic, or * return the given default value if there is no enclosing * SuggestedTopic */ fromEnclosingSuggestedTopic(prop, defaultVal) { /* look for the nearest enclosing suggested topic */ local enc = findEnclosingSuggestedTopic(); /* * return the desired property from the enclosing suggested * topic object if we found one, or the default if there is no * enclosing object */ return (enc != nil ? enc.(prop) : defaultVal); } /* * Should we suggest this topic to the given actor? We'll return * true if the actor is the same actor for which this suggestion is * intended, and the associated topic entry is currently active, and * we haven't already satisfied our curiosity about the topic. */ isSuggestionActive(actor, scopeList) { /* * Check to see if this is our target actor; that the associated * topic itself is active; that our curiosity hasn't already been * satisfied; and that it's at least possible to match the * associated topic right now. If all of these conditions are * met, we can make this suggestion. */ return (actor == suggestTo && associatedTopicIsActive() && associatedTopicCanMatch(actor, scopeList) && !curiositySatisfied); } /* * The number of times to suggest asking about our topic. When * we've asked about our associated topic this many times, we'll * have satisfied our curiosity. In most cases, we'll only want to * suggest a topic until it's asked about once, since most topics * only have a single meaningful response, so we'll use 1 as the * default. This should be overridden in cases where a topic will * reveal more information when asked several times. If this is * nil, it means that there's no limit to the number of times to * suggest asking about this. */ timesToSuggest = 1 /* * Have we satisfied our curiosity about this topic? Returns true * if so, nil if not. We'll never suggest a topic when this returns * true, because this means that the player no longer feels the need * to ask about the topic. */ curiositySatisfied = (timesToSuggest != nil && associatedTopicTalkCount() >= timesToSuggest) /* initialize - this is called automatically during pre-initialization */ initializeSuggestedTopic() { /* if we have a location, link up with our location */ if (location != nil) location.addSuggestedTopic(self); /* * if we're also a TopicEntry (using multiple inheritance), then * we are our own associated topic object */ if (ofKind(TopicEntry)) associatedTopic = self; } /* * Methods that rely on the associated topic. We isolate these in a * few methods here so that the rest of class doesn't depend on the * exact nature of our topic association. In particular, this allows * for subclasses that don't have an associated topic at all, or that * have multiple associated topics. Subclasses with specialized * topic relationships can simply override these methods to define * these methods appropriately. */ /* is the associated topic active? */ associatedTopicIsActive() { return associatedTopic.checkIsActive(); } /* get the number of previous invocations of the associated topic */ associatedTopicTalkCount() { return associatedTopic.talkCount; } /* is it possible to match the associated topic? */ associatedTopicCanMatch(actor, scopeList) { return associatedTopic.isMatchPossible(actor, scopeList); } /* * Note that we're being shown in a topic inventory listing. By * default, we don't do anything here, but subclasses can use this to * do any extra work they want to do on being listed. */ noteSuggestion() { } ; /* * A suggested topic that applies to an entire AltTopic group. * * Normally, a suggestion is tied to an individual TopicEntry. This * means that when a topic has several AltTopic alternatives, each * AltTopic can be its own separate, independent suggestion. A * particular alternative can be a suggestion or not, independently of * the other alternatives for the same TopicEntry. Since each AltTopic * is a separate suggestion, asking about one of the alternatives won't * have any effect on the "curiosity" about the other alternatives - in * other words, the other alternatives will be separately suggested when * they become active. * * In many cases, it's better for an entire set of alternatives to be * treated as a single suggested topic. That is, we want to suggest the * topic when ANY of the alternatives is active, and asking about any one * of the alternatives will satisfy the PC's curiosity for ALL of the * alternatives. This sort of arrangement is usually better for cases * where the conditions that trigger the different alternatives aren't * things that ought to make the PC think to ask the same question again. * * Use this class by associating it with the *root* TopicEntry of the * group of alternatives. You can do this most simply by mixing this * class into the superclass list of the root TopicEntry: * *. + AskTellTopic, SuggestedTopicTree, SuggestedAskTopic *. // ... *. ; * ++ AltTopic ... ; * ++ AltTopic ... ; * * This makes the entire group of AltTopics part of the same suggestion. * Note that you must *also* include SuggestedAsk, SuggestedTellTopic, or * one of the other specialized types among the superclass, to indicate * which kind of suggestion this is. */ class SuggestedTopicTree: SuggestedTopic /* is the associated topic active? */ associatedTopicIsActive() { /* the topic is active if anything in the AltTopic group is active */ return associatedTopic.anyAltIsActive; } /* get the number of previous invocations of the associated topic */ associatedTopicTalkCount() { /* return the number of invocations of any alternative */ return associatedTopic.altTalkCount; } ; /* * A suggested ASK ABOUT topic. We'll list ASK ABOUT topics together in * a subgroup ("you'd like to ask him about the book, the candle, and * the bell..."). */ class SuggestedAskTopic: SuggestedTopic suggestionGroup = [suggestionAskGroup] ; /* * A suggested TELL ABOUT topic. We'll list TELL ABOUT topics together * in a subgroup. */ class SuggestedTellTopic: SuggestedTopic suggestionGroup = [suggestionTellGroup] ; /* * A suggested ASK FOR topic. We'll list ASK FOR topics together as a * group. */ class SuggestedAskForTopic: SuggestedTopic suggestionGroup = [suggestionAskForGroup] ; /* * A suggested GIVE TO topic. */ class SuggestedGiveTopic: SuggestedTopic suggestionGroup = [suggestionGiveGroup] ; /* * A suggested SHOW TO topic. */ class SuggestedShowTopic: SuggestedTopic suggestionGroup = [suggestionShowGroup] ; /* * A suggested YES/NO topic */ class SuggestedYesTopic: SuggestedTopic suggestionGroup = [suggestionYesNoGroup] ; class SuggestedNoTopic: SuggestedTopic suggestionGroup = [suggestionYesNoGroup] ; /* ------------------------------------------------------------------------ */ /* * A conversation node. Conversation nodes are supplemental topic * databases that represent a point in time in a conversation - a * particular context that arises from what came immediately before in * the conversation. A conversation node is used to set up a group of * special responses that make sense only in a momentary context within a * conversation. * * A ConvNode object must be nested (via the 'location' property) within * an actor or an ActorState. This is how we associate the ConvNode with * its actor. Note that putting a ConvNode inside an ActorState doesn't * do anything different from putting the node directly inside the * ActorState's actor - we allow it only for convenience, to allow * greater flexibility arranging source code. */ class ConvNode: ActorTopicDatabase /* * Every ConvNode must have a name property. This is a string * identifying the object. Use this name string instead of a regular * object name (so ConvNode instances can essentially always be * anonymous, as far as the compiler is concerned). This string is * used to find the ConvNode in the master ConvNode database * maintained in the conversationManager object. * * A ConvNode name should be unique with respect to all other * ConvNode objects - no two ConvNode objects should have the same * name string. Other than this, the name strings are arbitrary. * (However, they shouldn't contain any '>' characters, because this * would prevent them from being used in <.convnode> tags, which is * the main place ConvNode's are usually used.) */ name = '' /* * Is this node "sticky"? If so, we'll stick to this node if we * show a response that doesn't set a new node. By default, we're * not sticky, so if we show a response that doesn't set a new node * and doesn't use a <.convstay> tag, we'll simply forget the node * and set the actor to no current ConvNode. * * Sticky nodes are useful when you want the actor to stay * on-subject even when the player digresses to talk about other * things. This is useful when the actor has a particular thread * they want to drive the conversation along. */ isSticky = nil /* * Show our NPC-initiated greeting. This is invoked when our actor's * initiateConversation() method is called to cause our actor to * initiate a conversation with the player character. This method * should show what our actor says to initiate the conversation. By * default, we'll invoke our npcGreetingList's script, if the * property is non-nil. * * A greeting should always be defined for any ConvNode that's used * in an initiateConversation() call. * * To define a greeting when defining a ConvNode, you can override * this method with a simple double-quoted string message, or you can * define an npcGreetingList property as an EventList of some kind. */ npcGreetingMsg() { /* if we have an npcGreetingList property, invoke the script */ if (npcGreetingList != nil) npcGreetingList.doScript(); } /* an optional EventList containing our NPC-initiated greetings */ npcGreetingList = nil /* * Our NPC-initiated conversation continuation message. This is * invoked on each turn (during the NPC's takeTurn() daemon * processing) that we're in this conversation node and the player * character doesn't do anything conversational. This allows the NPC * to carry on the conversation of its own volition. Define this as * a double-quoted string if you want the NPC to say something to * continue the conversation. */ npcContinueMsg = nil /* * An optional EventList containing NPC-initiated continuation * messages. You can define an EventList here instead of defining * npcContinueMsg, if you want more than one continuation message. */ npcContinueList = nil /* * Flag: automatically show a topic inventory on activating this * conversation node. Some conversation nodes have sufficiently * obscure entries that it's desirable to show a topic inventory * automatically when the node becomes active. * * By default, we automatically show a topic inventory if the node * contains an active SpecialTopic entry. Since special topics are * inherently obscure, in that they use non-standard commands, we * always want to show topics when one of these becomes active. */ autoShowTopics() { /* if we have an active special topic, show the topic inventory */ return (specialTopics != nil && specialTopics.indexWhich({x: x.checkIsActive()}) != nil); } /* our NPC is initiating a conversation starting with this node */ npcInitiateConversation() { local actor = getActor(); /* tell the conversation manager we're the actor who's talking */ conversationManager.beginResponse(actor); /* note that we're in conversation with the player character now */ getActor().noteConversation(gPlayerChar); /* show our NPC greeting */ npcGreetingMsg(); /* look for an ActorHelloTopic within the node */ handleTopic(gPlayerChar, actorHelloTopicObj, helloConvType, nil); /* end the response, staying in the current ConvNode by default */ conversationManager.finishResponse(actor, self); } /* * Continue the conversation of the NPC's own volition. Returns * true if we displayed anything, nil if not. */ npcContinueConversation() { local actor = getActor(); local disp; /* tell the conversation manager we're starting a response */ conversationManager.beginResponse(actor); /* show our text, watching to see if we generate any output */ disp = outputManager.curOutputStream.watchForOutput(function() { /* * if we have a continuation list, invoke it; otherwise if we * have a continuation message, show it; otherwise, just * return nil to let the caller know we have nothing to add */ if (npcContinueList != nil) npcContinueList.doScript(); else npcContinueMsg; }); /* end the response, staying in the current ConvNode by default */ conversationManager.finishResponse(actor, self); /* * if we actually said anything, note that we're in conversation * with the player character */ if (disp) getActor().noteConversation(gPlayerChar); /* return the display indication */ return disp; } /* our actor is our location, or our location's actor */ getActor() { /* if our location is an actor state, return the state's actor */ if (location.ofKind(ActorState)) return location.getActor(); /* otherwise, our location must be our actor */ return location; } /* our actor is the "owner" of our topics */ getTopicOwner() { return getActor(); } /* * Handle a conversation topic. The actor state object will call * this to give the ConvNode the first crack at handling a * conversation command. We'll return true if we handle the command, * nil if not. Our default handling is to look up the topic in the * given database list property, and handle it through the TopicEntry * we find there, if any. */ handleConversation(otherActor, topic, convType, path) { /* try handling it, returning the handled/not-handled result */ return handleTopic(otherActor, topic, convType, path); } /* * Can we end the conversation? If so, return true; our caller will * invoke our endConversation() to let us know that the conversation * is over. * * To prevent the conversation from ending, simply return nil. * * In most cases, you won't want to force the conversation to keep * going without any comment. Instead, you'll want to display some * message to let the player know what's going on - something like * "Hey! We're not through here!" If you do display a message, then * rather than returning nil, return the special value blockEndConv - * this tells the caller that the actor said something, so the caller * will call noteConvAction() to prevent further generated * conversation output on this same turn. * * 'reason' gives the reason the conversation is ending, as an * endConvXxx enum code. */ canEndConversation(actor, reason) { return true; } /* * Receive notification that our actor is ending a stateful * conversation. This is called before the normal * InConversationState disengagement operations. 'reason' is one of * the endConvXxx enums, indicating why the conversation is ending. * * Instances can override this for special behavior on terminating a * conversation. For example, an actor who just asked a question * could say something to indicate that the other actor is being * rude. By default, we do nothing. * * Note that there's no way to block the ending of the conversation * here. If you want to prevent the conversation from ending, use * canEndConversation() instead. */ endConversation(actor, reason) { } /* * Process a special command. Check the given command line string * against all of our topics, and see if we have a match to any topic * that takes a special command syntax. If we find a matching * special topic, we'll note the match, and turn the command into our * secret internal pseudo-command "XSPCLTOPIC". That command will * then go through the parser, which will recognize it and process it * using the normal conversational mechanisms, which will find the * SpecialTopic we noted earlier (in this method) and display its * response. * * 'str' is the original input string, exactly as entered by the * player, and 'procStr' is the "processed" version of the input * string. The nature of the processing varies by language, but * generally this involves things like removing punctuation marks and * any "noise words" that don't usually change the meaning of the * input, at least for the purposes of matching a special topic. */ processSpecialCmd(str, procStr) { local match; local cnt; /* we don't have an active special topic yet */ activeSpecialTopic = nil; /* * if we have no special topics, there's definitely no special * processing we need to do */ if (specialTopics == nil) return str; /* scan our special topics for a match */ cnt = 0; foreach (local cur in specialTopics) { /* if this one is active, and it matches the string, note it */ if (cur.checkIsActive() && cur.matchPreParse(str, procStr)) { /* remember it as the last match */ match = cur; /* count the match */ ++cnt; } } /* * If we found exactly one match, then activate it. If we found * zero or more than one, ignore any special topics and proceed * on the assumption that this is a normal command. (We ignore * ambiguous matches because this probably means that the entire * command is some very common word that happens to be acceptable * as a keyword in one or more of our matches. In these cases, * the common word was probably meant as an ordinary command, * since the player would likely have been more specific if a * special topic were really desired.) */ if (cnt == 1) { /* * remember the active SpecialTopic - we'll use this memory * to find it again when we get through the full command * processing */ activeSpecialTopic = match; /* * Change the command to our special internal pseudo-command * that triggers the active special topic. Include the * original string as a literal phrase, enclosed in double * quotes and specially coded to ensure that the tokenizer * doesn't become confused by any embedded quotes. */ return 'xspcltopic "' + SpecialTopicAction.encodeOrig(str) + '"'; } else { /* proceed, treating the original input as an ordinary command */ return str; } } patWhitespace = static new RexPattern('+') patDelim = static new RexPattern('') /* * Handle an XSPCLTOPIC command from the given actor. This is part * two of the two-phase processing of SpecialTopic matches. Our * pre-parser checks each SpecialTopic's custom syntax for a match * to the player's text input, and if it finds a match, it sets our * activeSpecialTopic property to the matching SpecialTopic, and * changes the user's command to XSPCLTOPIC for processing by the * regular parser. The regular parser sees the XSPCLTOPIC command, * which is a valid verb that calls the issuing actor's * saySpecialTopic() routine, which in turn forwards the request to * the issuing actor's interlocutor's current conversation node - * which is to say, 'self'. We complete the two-step procedure by * going back to the active special topic object that we previously * noted and showing its response. */ saySpecialTopic(fromActor) { /* make sure we have an active special topic object */ if (activeSpecialTopic != nil) { local actor = getTopicOwner(); /* tell the conversation manager we're starting a response */ conversationManager.beginResponse(actor); /* let the SpecialTopic handle the response */ activeSpecialTopic.handleTopic(fromActor, nil); /* * Tell the conversation manager we're done. By default, we * want to leave the conversation tree entirely, so set the * new default node to 'nil'. */ conversationManager.finishResponse(actor, nil); /* that wraps things up for the active special topic */ activeSpecialTopic = nil; } else { /* * There is no active special topic, so the player must have * typed in the XSPCLTOPIC command explicitly - if we got * here through the normal two-step procedure then this * property would not be nil. Politely decline the command, * since it's not for the player's direct use. */ gLibMessages.commandNotPresent; } } /* * The active special topic. This is the SpecialTopic object that * we matched during pre-parsing, so it's the one whose response we * wish to show while processing the command we pre-parsed. */ activeSpecialTopic = nil /* * Note that we're becoming active, with a reason code. Our actor * will call this method when we're becoming active, as long as we * weren't already active. * * 'reason' is a string giving a reason code for why we're being * called. For calls from the library, this will be one of these * codes: * * 'convnode' - processing a <.convnode> tag * * 'convend' - processing a <.convend> tag * * 'initiateConversation' - a call to Actor.initiateConversation() * * 'endConversation' - a call to Actor.endConversation() * * The reason code is provided so that the node can adapt its action * for different trigger conditions, if desired. By default, we * ignore the reason code and just call the basic noteActive() * method. */ noteActiveReason(reason) { noteActive(); } /* * Note that we're becoming active, with a reason code. Our actor * will call this method when we're becoming active, as long as we * weren't already active. * * Note that if you want to adapt the method's behavior according to * why the node was activated, you can override noteActiveReason() * instead of this method. */ noteActive() { /* if desired, schedule a topic inventory whenever we're activated */ if (autoShowTopics()) conversationManager.scheduleTopicInventory(); } /* * Note that we're leaving this conversation node. This doesn't do * anything by default, but individual instances might find the * notification useful for triggering side effects. */ noteLeaving() { } ; /* ------------------------------------------------------------------------ */ /* * Pre-parser for special ConvNode-specific commands. When the player * character is talking to another character, and the NPC's current * ConvNode includes topics with their own commands, we'll check the * player's input to see if it matches any of these topics. */ specialTopicPreParser: StringPreParser doParsing(str, which) { local actor; local node; /* * don't handle this on requests for missing literals - these * responses are always interpreted as literal text, so there's * no way this could be a special ConvNode command */ if (which == rmcAskLiteral) return str; /* * if the player character isn't currently in conversation, or * the actor with whom the player character is conversing doesn't * have a current conversation node, there's nothing to do */ if ((actor = gPlayerChar.getCurrentInterlocutor()) == nil || (node = actor.curConvNode) == nil) return str; /* ask the conversation node to process the string */ return node.processSpecialCmd(str, processInputStr(str)); } /* * Process the input string, as desired, for special-topic parsing. * This method is for the language module's use; by default, we do * nothing. * * Language modules should override this to remove punctuation marks * and to do any other language-dependent processing to make the * string parsable. */ processInputStr(str) { return str; } ; /* ------------------------------------------------------------------------ */ /* * A conversational action type descriptor. This descriptor is used in * handleConversation() in Actor and ActorState to describe the type of * conversational action we're performing. The type descriptor object * encapsulates a set of information that tells us how to handle the * action. */ class ConvType: object /* * The unknown interlocutor message property. This is used when we * try this conversational action without knowing whom we're talking * to. For example, if we just say HELLO, and there's no one around * to talk to, we'll use this as the default response. This can be a * library message property, or simply a single-quoted string to * display. */ unknownMsg = nil /* * The TopicDatabase topic-list property. This is the property of * the TopicDatabase object that we evaluate to get this list of * topic entries to search for a match to the topic. */ topicListProp = nil /* the default response property for this action */ defaultResponseProp = nil /* * Call the default response property on the given topic database. * This invokes the property given by defaultResponseProp(). We have * both the property and the method to call the property because this * allows us to test for the existence of the property and to call it * with the appropriate argument list. */ defaultResponse(db, otherActor, topic) { } /* * Perform any special follow-up action for this type of * conversational action. */ afterResponse(actor, otherActor) { } ; helloConvType: ConvType unknownMsg = &sayHelloMsg topicListProp = &miscTopics defaultResponseProp = &defaultGreetingResponse defaultResponse(db, other, topic) { db.defaultGreetingResponse(other); } /* after an explicit HELLO, show any suggested topics */ afterResponse(actor, otherActor) { /* show or schedule a topic inventory, as appropriate */ conversationManager.showOrScheduleTopicInventory(actor, otherActor); } ; byeConvType: ConvType unknownMsg = &sayGoodbyeMsg topicListProp = &miscTopics defaultResponseProp = &defaultGoodbyeResponse defaultResponse(db, other, topic) { db.defaultGoodbyeResponse(other); } ; yesConvType: ConvType unknownMsg = &sayYesMsg topicListProp = &miscTopics defaultResponseProp = &defaultYesResponse defaultResponse(db, other, topic) { db.defaultYesResponse(other); } ; noConvType: ConvType unknownMsg = &sayNoMsg topicListProp = &miscTopics defaultResponseProp = &defaultNoResponse defaultResponse(db, other, topic) { db.defaultNoResponse(other); } ; askAboutConvType: ConvType topicListProp = &askTopics defaultResponseProp = &defaultAskResponse defaultResponse(db, other, topic) { db.defaultAskResponse(other, topic); } ; askForConvType: ConvType topicListProp = &askForTopics defaultResponseProp = &defaultAskForResponse defaultResponse(db, other, topic) { db.defaultAskForResponse(other, topic); } ; tellAboutConvType: ConvType topicListProp = &tellTopics defaultResponseProp = &defaultTellResponse defaultResponse(db, other, topic) { db.defaultTellResponse(other, topic); } ; giveConvType: ConvType topicListProp = &giveTopics defaultResponseProp = &defaultGiveResponse defaultResponse(db, other, topic) { db.defaultGiveResponse(other, topic); } ; showConvType: ConvType topicListProp = &showTopics defaultResponseProp = &defaultShowResponse defaultResponse(db, other, topic) { db.defaultShowResponse(other, topic); } ; commandConvType: ConvType topicListProp = &commandTopics defaultResponseProp = &defaultCommandResponse defaultResponse(db, other, topic) { db.defaultCommandResponse(other, topic); } ; /* * This type is for NPC-initiated conversations. It's not a normal * conversational action, since it doesn't involve handling a player * command, but is usually instead triggered by an agenda item, * takeTurn(), or other background activity. */ initiateConvType: ConvType topicListProp = &initiateTopics ; /* * CONSULT ABOUT isn't a true conversational action, since it's applied * to inanimate objects (such as books); but it's handled through the * conversation system, so it needs a conversation type object */ consultConvType: ConvType topicListProp = &consultTopics ; /* ------------------------------------------------------------------------ */ /* * A topic database entry. Actors and actor state objects store topic * databases; a topic database is essentially a set of these entries. * * A TopicEntry can go directly inside an Actor, in which case it's part * of the actor's global set of topics; or, it can go inside an * ActorState, in which case it's part of the state's database and is * only active when the state is active; or, it can go inside a * TopicGroup, which is a set of topics with a common controlling * condition; or, it can go inside a ConvNode, in which case it's in * effect only when the conversation node is active. * * Each entry is a relationship between a topic, which is something that * can come up in an ASK or TELL action, and a handling for the topic. * In addition, each entry determines what kind or kinds of actions it * responds to. * * Note that TopicEntry objects are *not* simulation objects. Rather, * these are abstract objects; they can be associated with simulation * objects via the matching mechanism, but these are separate from the * actual simulation objects. The reason for this separation is that a * given simulation object might have many different response - the * response could vary according to who's being asked the question, who's * asking, and what else is happening in the game. * * An entry decides for itself if it matches a topic. By default, an * entry can match based on either a simulation object, which we'll match * to anything in the topic's "in scope" or "likely" match lists, or * based on a regular expression string, which we'll match to the actual * topic text entered in the player's command. * * An entry can decide how strongly it matches a topic. The database * will choose the strongest match when multiple entries match the same * topic. The strength of the match is given by a numeric score; the * higher the score, the stronger the match. The match strength makes it * easy to specify a hierarchy of topics from specific to general, so * that we provide general responses to general topic areas, but can * still respond to particular topics areas more specifically. For * example, we might want to provide a specific match to the FROBNOZ * SPELL object, talking about that particular magic spell, but provide a * generic '.* spell' pattern to response to questions about any old * spell. We'd give the generic pattern a lower score, so that the * specific FROBNOZ SPELL response would win when it matches, but we'd * fall back on the generic pattern in other cases. */ class TopicEntry: object /* * My matching simulation object or objects. This can be either a * single object or a list of objects. */ matchObj = nil /* * Is this topic active? This can be used to control how an actor * can respond without have to worry about adding and removing topics * manually at key events, or storing the topics in state objects. * Sometimes, it's easier to just put a topic entry in the actor's * database from the start, and test some condition dynamically when * the topic is actually queried. To do this, override this method * to test the condition that determines when the topic entry should * become active. We'll never show the topic's response when * isActive returns nil. By default, we simply return true to * indicate that the topic entry is active. */ isActive = true /* * Flag: we are a "conversational" topic. This is true by default. * When this is set to nil, a ConversationReadyState will NOT show * its greeting and will not enter its InConversationState to show * this topic entry's response. * * This should be set to nil when the topic entry's response is * non-conversational, in which case a greeting would be * undesirable. This is appropriate for responses like "You don't * think he'd want to talk about that", where the response indicates * that the player character didn't even ask a question (or * whatever). */ isConversational = true /* * Do we imply a greeting? By default, all conversational topics * imply a greeting. We separate this out so that the implied * greeting can be controlled independently of whether or not we're * actually conversational, if desired. */ impliesGreeting = (isConversational) /* * Get the actor associated with the topic, if any. By default, * we'll return our enclosing database's topic owner, if it's an * actor - in almost all cases, if there's any actor associated with * a topic, it's simply the owner of the database containing the * topic. */ getActor() { local owner; /* * if we have an owner, and it's an actor, then it's our * associated actor; otherwise, we don't have any associated * actor */ if ((owner = location.getTopicOwner()) != nil && owner.ofKind(Actor)) return owner; else return nil; } /* * Determine if this topic is active. This checks the isActive * property, and also takes into account our relationship to * alternative entries for the topic. Generally, you should *define* * (override) isActive, and *call* this method. */ checkIsActive() { /* * if our isActive property indicates we're not active, we're * definitely not active, so there's no need to check for an * overriding alternative */ if (!isActive) return nil; /* if we have an active nested alternative, it overrides us */ if (altTopicList.indexWhich({x: x.isActive}) != nil) return nil; /* ask our container if its topics are active */ return location.topicGroupActive(); } /* * Check to see if any alternative in the alternative group is * active. This returns true if we're active or if any of our nested * AltTopics is active. */ anyAltIsActive() { /* * if all topics within our container are inactive, then there's * definitely no active alternative */ if (!location.topicGroupActive()) return nil; /* * if we're active, or any of our nested AltTopics is active, our * alternative group is active */ if (isActive || altTopicList.indexWhich({x: x.isActive}) != nil) return true; /* we didn't find any active alternatives in the entire group */ return nil; } /* * Adjust my score value for any hierarchical adjustments. We'll add * the score adjustment for each enclosing object. */ adjustScore(score) { /* the score is nil, it means there's no match, so don't adjust it */ if (score == nil) return score; /* add in the cumulative adjustment from my containers */ return score + location.topicGroupScoreAdjustment; } /* * Check to see if we want to defer to the given topic from an * inferior topic database. By default, we never defer to a topic * from an inferior database: we choose a matching topic from the top * database in the hierarchy where we find a match. * * The database hierarchy, for most purposes, starts with the * ConvNode at the highest level, then the ActorState, then the * Actor. We search those databases, in that order, and we take the * first match we find. By default, if there's another match in a * lower-level database, it doesn't matter what its matchScore is: we * always pick the one from the highest-level database where we find * a match. You can override this method to change this behavior. * * We don't actually define this method here, because the presence of * the method is significant. If the method isn't defined at all, we * won't bother looking for a possible deferral, saving the trouble * of searching the other databases in the hierarchy. */ // deferToEntry(other) { return nil; } /* * Our match strength score. By default, we'll use a score of 100, * which is just an arbitrary base score. */ matchScore = 100 /* * The set of database lists we're part of. This is a list of * property pointers, giving the TopicDatabase properties of the * lists we participate in. */ includeInList = [] /* * Our response. This is displayed when we're the topic entry * selected to handle an ASK or TELL. Each topic entry must override * this to show our response text (or, alternatively, an entry can * override handleTopic so that it doesn't call this property). */ topicResponse = "" /* * The number of times this topic has invoked by the player. Each * time the player asks/tells/etc about this topic, we'll increment * this count. */ talkCount = 0 /* * The number of times this topic or any nested AltTopic has been * invoked by the player. Each time the player asks/tells/etc about * this topic OR any of its AltTopic children, we'll increment this * count. */ altTalkCount = 0 /* * the owner of any AltTopic nested within me is the same as my own * topic owner, which we take from our location */ getTopicOwner() { if (location != nil) return location.getTopicOwner(); else return nil; } /* * Initialize. If we have a location property, we'll assume that the * location is a topic database object, and we'll add ourselves to * that database. */ initializeTopicEntry() { /* if we have a location, add ourselves to its topic database */ if (location != nil) location.addTopic(self); /* sort our list of AltTopic children */ altTopicList = altTopicList.sort( SortAsc, {a, b: a.altTopicOrder - b.altTopicOrder}); } /* add a topic nested within us */ addTopic(entry) { /* if we have a location, add the entry to its topic database */ if (location != nil) location.addTopic(entry); } /* * Add an AltTopic entry. This is called by our AltTopic children * during initialization; we'll simply add the entry to our list of * AltTopic children. */ addAltTopic(entry) { /* add the entry to our list of alternatives */ altTopicList += entry; } /* get the topic group score adjustment (for AltTopics nested within) */ topicGroupScoreAdjustment = (location.topicGroupScoreAdjustment) /* check the group isActive status (for AltTopics nested within) */ topicGroupActive = (location.topicGroupActive) /* our list of AltTopic children */ altTopicList = [] /* * Match a topic. This is abstract in this base class; it must be * defined by each concrete subclass. This returns nil if there's no * match, or an integer value if there's a match. The higher the * number's value, the stronger the match. * * This is abstract in the base class because the meaning of 'topic' * varies by subclass, according to which type of command it's used * with. For example, in ASK and TELL commands, 'topic' is a * ResolvedTopic describing the topic in the player's command; for * GIVE and SHOW commands, it's the resolved simulation object. */ // matchTopic(fromActor, topic) { return nil; } /* * Check to see if a match to this topic entry is *possible* right * now for the given actor. For most subclasses, this is inherently * imprecise, because the 'match' function simply isn't reversible in * general: to know if we can be matched, we'd have to determine if * there's a non-empty set of possible inputs that can match us. * This method is complementary to matchTopic(), so subclasses must * override with a corresponding implementation. * * 'actor' is the actor to whom we're making the suggestion. * 'scopeList' is the list of objects that are in scope for the * actor. * * The library only uses this to determine if a suggestion should be * offered. So, specialized topic instances with non-standard match * rules don't have to worry about this unless they're used as * suggestions, or unless the game itself needs this information for * some other reason. */ // isMatchPossible(actor, scopeList) { return true; } /* * Break a tie among matching topics entries. The topic database * searcher calls this on each matching topic entry when it finds * multiple entries tied for first place, based on their match * scores. This gives the entries a chance to figure out which one * is actually the best match for the input, given the other entries * that also matched. * * This method returns a TopicEntry object - one of the objects from * the match list - if it has an opinion as to which one should take * precedence. It returns nil if it doesn't know or doesn't care. * Returning nil gives the other topics in the match list a chance to * make the selection. If all of the objects in the list return nil, * the topic database searcher simply picks one of the topic matches * arbitrarily. * * 'matchList' is the list of tied TopicEntry objects. 'topic' is * the ResolvedTopic object from the parser, representing the * player's input phrase that we're matching. 'fromActor' is the * actor performing the command. 'toks' is a list of strings giving * the word tokens of the noun phrase. * * The topic database searcher calls this method for each matching * topic entry in the case of a tie, and simply accepts the opinion * of the first one that expresses an opinion by returning a non-nil * value. There's no voting; whoever happens to get *and use* the * first say also gets the last word. We expect that this won't be a * problem in practice: when this comes up at all, it's because there * are a couple of closely related topic entries that are active in a * particular context, and you need a special bit of tweaking to pick * the right one for a given input phrase. Simply pick one of the * involved entries and define this method there. */ breakTopicTie(matchList, topic, fromActor, toks) { /* * we don't have an opinion - defer to the next object in the * list, or allow an arbitrary selection */ return nil; } /* * Set pronouns for the topic, if possible. If the topic corresponds * to a game-world object, then we should set the pronoun antecedent * to the game object. This must be handled per subclass because of * the range of possible meanings of 'topic'. */ setTopicPronouns(fromActor, topic) { } /* * Handle the topic. This is called when we find that this is the * best topic entry for the current topic. * * By default, we'll do one of two things: * * - If 'self' inherits from Script, then we'll simply invoke our * doScript() method. This makes it especially easy to set up a * topic entry that shows a series of responses: just add EventList * or one of its subclasses to the base class list when defining the * topic, and define the eventList property as a list of string * responses. For example: * *. + TopicEntry, StopEventList @blackBook *. ['What makes you think I know anything about it? *. he says, his voice shaking. ', *. 'No! You can\'t make me tell you! he wails. ', *. 'All right, fine! I\'ll tell you, but I warn you, *. this is knowledge mortal men were never meant to know. ', *. // and so on *. ] *. ; * * - Otherwise, we'll call our topicResponse property, which should * simply be a double-quoted string to display. This is the simplest * way to define a topic with just one response. * * Note that 'topic' will vary by subclass, depending on the type of * command used with the topic type. For example, for ASK and TELL * commands, 'topic' is a ResolvedTopic object; for GIVE and SHOW, * it's a simulation object (i.e., generally a Thing subclass). */ handleTopic(fromActor, topic) { /* note the invocation */ noteInvocation(fromActor); /* set pronoun antecedents if possible */ setTopicPronouns(fromActor, topic); /* check to see if we're a Script */ if (ofKind(Script)) { /* we're a Script - invoke our script */ doScript(); } else { /* show our simple response string */ topicResponse; } } /* note that we've been invoked */ noteInvocation(fromActor) { /* * we count as one of the alternatives in our alternative group, * so note the invocation of the group */ noteAltInvocation(fromActor, self); /* count the invocation */ ++talkCount; } /* * Note that something in our entire alternative group has been * invoked. We count as a member of our own group, so this is * invoked when we're invoked; this is also invoked when any AltTopic * child of ours is invoked. */ noteAltInvocation(fromActor, alt) { local owner; /* notify our owner of the topic invocation */ if ((owner = location.getTopicOwner()) != nil) owner.notifyTopicResponse(fromActor, alt); /* count the alternative invocation */ ++altTalkCount; } /* * Add a suggested topic. A suggested topic can be nested within a * topic entry; doing this associates the suggested topic with the * topic entry, and automatically associates the suggested topic * with the entry's actor or actor state. */ addSuggestedTopic(t) { /* * If the SuggestedTopic is *directly* within us, we're the * SuggestedTopic object's associated TopicEntry. The nesting * could be deeper, if we have alternative topics nested within * us; in these cases, we're not directly associated with the * suggested topic. */ if (t.location == self) t.associatedTopic = self; /* add the suggestion to our location's topic database */ if (location != nil) location.addSuggestedTopic(t); } ; /* * A TopicGroup is an abstract container for a set of TopicEntry objects. * The purpose of the group object is to apply a common "is active" * condition to all of the topics within the group. * * The isActive condition of the TopicGroup is effectively AND'ed with * any other conditions on the nested TopicEntry's. In other words, a * TopicEntry within the TopicGroup is active if the TopicEntry would * otherwise be acive AND the TopicGroup is active. * * TopicEntry objects are associated with the group via the 'location' * property - set the location of the TopicEntry to point to the * containing TopicGroup. * * You can put a TopicGroup anywhere a TopicEntry could go - directly * inside an Actor, inside an ActorState, or within another TopicGroup. * The topic entries within a topic group act as though they were * directly in the topic group's container. */ class TopicGroup: object /* * The group "active" condition - each instance should override this * to specify the condition that applies to all of the TopicEntry * objects within the group. */ isActive = true /* * The *adjustment* to the match score for topic entries contained * within this group. This is usually a positive number, so that it * boosts the match strength of the child topics. */ matchScoreAdjustment = 0 /* * the topic owner for any topic entries within the group is the * topic owner taken from the group's own location */ getTopicOwner() { return location.getTopicOwner(); } /* are TopicEntry objects within the group active? */ topicGroupActive() { /* * our TopicEntry objects are active if the group condition is * true and our container's contents are active */ return isActive && location.topicGroupActive(); } /* * Get my score adjustment. We'll return our own basic score * adjustment plus the cumulative adjustment for our containers. */ topicGroupScoreAdjustment = (matchScoreAdjustment + location.topicGroupScoreAdjustment) /* add a topic - we'll simply add the topic directly to our container */ addTopic(topic) { location.addTopic(topic); } /* add a suggested topic - we'll pass this up to our container */ addSuggestedTopic(topic) { location.addSuggestedTopic(topic); } ; /* * An alternative topic entry. This makes it easy to define different * responses to a topic according to the game state; for example, we * might want to provide a different response for a topic after some * event has occurred, so that we can reflect knowledge of the event in * the response. * * A set of alternative topics is sort of like an inverted if-then-else. * You start by defining a normal TopicEntry (an AskTopic, or an * AskTellTopic, or whatever) for the basic response. Then, you add a * nested AltTopic located within the base topic; you can add another * AltTopic nested within the base topic, and another after that, and so * on. When we need to choose one of the topics, we'll choose the last * one that indicates it's active. So, the order of appearance is * essentially an override order: the first AltTopic overrides its parent * TopicEntry, and each subsequent AltTopic overrides its previous * AltTopic. * * + AskTellTopic @lighthouse "It's very tall."; *. ++ AltTopic "Not really..." isActive=(...); *. ++ AltTopic "Well, maybe..." isActive=(...); *. ++ AltTopic "One more thing..." isActive=(...); * * In this example, the response we'll show for ASK ABOUT LIGHTHOUSE will * always be the LAST entry of the group that's active. For example, if * all of the responses are active except for the very last one, then * we'll show the "Well, maybe" response, because it's the last active * response. If the main AskTellTopic is active, but none of the * AltTopics are active, we'll show the "It's very tall" main response, * because it's the last element of the group that's active. * * Note that an AltTopic takes its matching information from its parent, * so you don't need to specify a matchObj or any other matching * information in an AltTopic. You merely need to provide the response * text and the isActive test. */ class AltTopic: TopicEntry /* we match if our parent matches, and with the same score */ matchTopic(fromActor, topic) { return location.matchTopic(fromActor, topic); } /* we can match if our parent can match */ isMatchPossible(actor, scopeList) { return location.isMatchPossible(actor, scopeList); } /* we can match a pre-parse string if our parent can */ matchPreParse(str, pstr) { return location.matchPreParse(str, pstr); } /* set pronouns for the topic */ setTopicPronouns(fromActor, topic) { location.setTopicPronouns(fromActor, topic); } /* include in the same lists as our parent */ includeInList = (location.includeInList) /* AltTopic initialization */ initializeAltTopic() { /* add myself to our parent's child list */ if (location != nil) location.addAltTopic(self); } /* * Determine if this topic is active. An AltTopic is active if its * own isActive indicates true, AND none of its subsequent siblings * are active. */ checkIsActive() { /* we can't be active if our own isActive says we're not */ if (!isActive) return nil; /* * Check for any active element after us in the parent's list. * To do this, scan from the end of the parent list backwards, * and look for an element that's active. If we reach our own * entry, then we'll know that there are no active entries * following us in the list. Note that we already know we're * active, or we wouldn't have gotten this far, so we can simply * look for the rightmost active element in the list. */ if (location != nil && location.altTopicList.lastValWhich({x: x.isActive}) != self) { /* * we found an active element after ourself, so it overrides * us - we're therefore not active */ return nil; } /* ask our container if its topics are active */ return location.topicGroupActive(); } /* take our implied-greeting status from our parent */ impliesGreeting = (location.impliesGreeting) /* take our conversational status from our parent */ isConversational = (location.isConversational) /* * Our relative order within our parent's list of alternatives. By * default, we simply return the source file ordering, which ensures * that static AltTopic objects (i.e., those defined directly in * source files, not dynamically created with 'new') will be ordered * just as they're laid out in the source file. */ altTopicOrder = (sourceTextOrder) /* note invocation */ noteInvocation(fromActor) { /* count our own invocation */ ++talkCount; /* let our container know its AltTopic child is being invoked */ if (location != nil) location.noteAltInvocation(fromActor, self); } /* our AltTopic counter is the AltTopic counter for the enclosing topic */ altTalkCount = (location != nil ? location.altTalkCount : talkCount) ; /* * A "topic match" topic entry. This is a topic entry that matches topic * phrases in the grammar. * * Handling topic phrases is a bit tricky, because they can't be resolved * to definitive game-world objects the way ordinary noun phrases can. * Topic phrases can refer to things that aren't physically present, but * which are known to the actor performing the command; they can refer to * abstract Topic objects, that have no physical existence in the game * world at all; and they can ever be arbitrary text that doesn't match * any vocabulary defined by the game. * * Our strategy in matching topics is to first narrow the list down to * the physical and abstract game objects that both match the vocabulary * used in the command and are part of the memory of the actor performing * the command. That much is handled by the normal topic phrase * resolution rules, and gives us a list of possible matches. Then, * given this narrowed list of possibilities, we look through the list of * objects that we're associated with; we effectively intersect the two * lists, and if the result is non-empty, we consider it a match. * Finally, we also consider any regular expression that we're associated * with; if we have one, and the topic phrase text in the command matches * the input, we'll consider it a match. */ class TopicMatchTopic: TopicEntry /* * A regular expression pattern that we'll match to the actual topic * text as entered in the command. If 'matchExactCase' is true, * we'll match the exact text in its original upper/lower case * rendering; otherwise, we'll convert the player input to lower-case * before matching it against the pattern. In most cases, we'll want * to match the input no matter what combination of upper and lower * case the player entered, so matchExactCase is nil by default. * * Note that both the object (or object list) and the regular * expression pattern can be included for a single topic entry * object. This allows a topic entry to match several different ways * of entering the topic name, or to match several different topics * with the same response. */ matchPattern = nil matchExactCase = nil /* * Match the topic. By default, we'll match to either the simulation * object or objects in matchObj, or the pattern in matchPattern. * Note that we always try both ways of matching, so a single * AskTellTopic can define both a pattern and an object list. * * 'topic' is a ResolvedTopic object describing the player's text * input and the list of objects that the parser matched to the text. * * Subclasses can override this as desired to use other ways of * matching. */ matchTopic(fromActor, topic) { /* * if we have one or more match objects, try matching to the * topic's best simulation object match */ if (matchObj != nil) { /* * we have a match object or match object list - if it's a * collection, check each element, otherwise just match the * single object */ if (matchObj.ofKind(Collection)) { /* try matching each object in the list */ if (matchObj.indexWhich({x: findMatchObj(x, topic)}) != nil) return matchScore; } else { /* match the single object */ if (findMatchObj(matchObj, topic)) return matchScore; } } /* * check for a match to the regular expression pattern, if we * have a pattern AND the resolved topic allows literal matches */ if (matchPattern != nil && topic.canMatchLiterally()) { local txt; /* * There's no match object; try matching our regular * expression to the actual topic text. Get the actual text. */ txt = topic.getTopicText(); /* * if they don't want an exact case match, convert the * original topic text to lower case */ if (!matchExactCase) txt = txt.toLower(); /* if the regular expression matches, we match */ if (rexMatch(matchPattern, txt) != nil) return matchScore; } /* we didn't find a match - indicate this with a nil score */ return nil; } /* * Match an individual item from our match list to the given * ResolvedTopic object. We'll check each object in the resolved * topic's "in scope" and "likely" lists. */ findMatchObj(obj, rt) { /* check the "in scope" list */ if (rt.inScopeList.indexOf(obj) != nil) return true; /* check the "likely" list */ return (rt.likelyList.indexOf(obj) != nil); } /* * It's possible for us to match if any of our matchObj objects are * known to the actor. If we have no matchObj objects, we must be * matching on a regular expression or on a custom condition, so we * can't speculate on matchability; we'll simply return true in those * cases. */ isMatchPossible(actor, scopeList) { /* check what we have in our matchObj */ if (matchObj == nil) { /* * we have no match object, so we must match on a regular * expression or a custom condition; we can't speculate on * our matchability, so just return true as a default */ return true; } else if (matchObj.ofKind(Collection)) { /* * we have a list of match objects - return true if any of * them are known or are currently in scope */ return (matchObj.indexWhich( {x: actor.knowsAbout(x) || scopeList.indexOf(x)}) != nil); } else { /* * we have a single match object - return true if it's known * or it's in scope */ return (actor.knowsAbout(matchObj) || scopeList.indexOf(matchObj) != nil); } } /* set the topic pronouns */ setTopicPronouns(fromActor, topic) { /* check to see what kind of match object we have */ if (matchObj == nil) { /* * no match object, so we must match a regular expression * pattern; this gives us no clue what game object we might * match, so there's nothing we can do here */ } else if (matchObj.ofKind(Collection)) { local lst; /* * We match a list of objects. Get the subset of the * in-scope list from the topic that we match. Consider only * the in-scope items for now, and consider only game-world * objects (Things). */ lst = matchObj.subset( {x: x.ofKind(Thing) && topic.inScopeList.indexOf(x) != nil}); /* if that didn't turn up anything, consider the likelies, too */ if (lst.length() == 0) lst = matchObj.subset( {x: (x.ofKind(Thing) && topic.likelyList.indexOf(x) != nil)}); /* * if that narrows it down to one match, make it the pronoun * antecedent */ if (lst.length() == 1) fromActor.setPronounObj(lst[1]); } else { /* * we match a single object; if it's a game-world object (a * Thing), use it as the pronoun antecedent */ if (matchObj.ofKind(Thing)) fromActor.setPronounObj(matchObj); } } ; /* * A dual ASK/TELL topic database entry. This type of topic is included * in both the ASK ABOUT and TELL ABOUT lists. * * Many authors have chosen to treat ASK and TELL as equivalent, or at * least, equivalent for most topics. Since these verbs only very weakly * suggest what the player character is actually saying, it's frequently * the case that a given topic response makes just as much sense coming * from TELL as from ASK, or vice versa. In these cases, it's best to * enter the topic under both ASK and TELL; which one the player tries * might simply depend on the player's frame of mind, and they might feel * cheated if one works and the other doesn't in cases where both are * equally valid. */ class AskTellTopic: TopicMatchTopic /* include me in both the ASK and TELL lists */ includeInList = [&askTopics, &tellTopics] ; /* * An ASK ABOUT topic database entry. This type of topic is included in * the ASK ABOUT list only. */ class AskTopic: AskTellTopic includeInList = [&askTopics] ; /* * A TELL ABOUT topic database entry. This type of topic entry is * included in the TELL ABOUT list only. */ class TellTopic: AskTellTopic includeInList = [&tellTopics] ; /* * An ASK FOR topic database entry. This type of topic entry is * included in the ASK FOR list only. */ class AskForTopic: AskTellTopic includeInList = [&askForTopics] ; /* * A combination ASK ABOUT and ASK FOR topic. */ class AskAboutForTopic: AskTellTopic includeInList = [&askTopics, &askForTopics] ; /* * A combination ASK ABOUT, TELL ABOUT, and ASK FOR topic. */ class AskTellAboutForTopic: AskTellTopic includeInList = [&askTopics, &tellTopics, &askForTopics] ; /* * A base class for topic entries that match simple simulation objects. */ class ThingMatchTopic: TopicEntry /* * Match the topic. We'll match the simulation object in 'obj' to * our matchObj object or list. */ matchTopic(fromActor, obj) { /* * if matchObj is a collection, check each element, otherwise * just match the single object */ if (matchObj.ofKind(Collection)) { /* try matching each object in the list */ if (matchObj.indexOf(obj) != nil) return matchScore; } else { /* match the single object */ if (matchObj == obj) return matchScore; } /* didn't find a match - indicate this by returning a nil score */ return nil; } /* * It's possible for us to match if any of our matchObj objects are * in scope. */ isMatchPossible(actor, scopeList) { /* check to see what kind of match object we have */ if (matchObj.ofKind(Collection)) { /* we can match if any of our match objects are in scope */ return (matchObj.indexWhich({x: scopeList.indexOf(x)}) != nil); } else { /* we can match if our single match object is in scope */ return scopeList.indexOf(matchObj); } } /* set the topic pronouns */ setTopicPronouns(fromActor, topic) { /* * the 'topic' is just an ordinary game object; as long as it's a * Thing, set it as the antecedent */ if (topic.ofKind(Thing)) fromActor.setPronounObj(topic); } ; /* * A GIVE/SHOW topic database entry. * * Note that this base class is usable for any command that refers to a * simulation object. It's NOT suitable for ASK/TELL lists, or for other * commands that refer to topics, since we expect our 'topic' to be a * resolved simulation object. */ class GiveShowTopic: ThingMatchTopic /* include me in both the GIVE and SHOW lists */ includeInList = [&giveTopics, &showTopics] ; /* * A GIVE TO topic database entry. This type of topic entry is included * in the GIVE TO list only. */ class GiveTopic: GiveShowTopic includeInList = [&giveTopics] ; /* * A SHOW TO topic database entry. This type of topic entry is included * in the SHOW TO list only. */ class ShowTopic: GiveShowTopic includeInList = [&showTopics] ; /* * A TopicEntry that can match a Thing or a Topic. This can be used to * combine ASK/TELL-type responses and GIVE/SHOW-type responses in a * single topic entry. * * When this kind of topic is used as a suggested topic, note that you * should name the suggestion according to the least restrictive verb. * This is important because the suggestion will be active if any of the * verbs would allow it; to ensure that we suggest a verb that will * actually work, we should thus use the least restrictive verb. In * practice, this means you should use ASK or TELL as the suggestion * name, because an object merely has to be known to be used as a topic; * it might be possible to ASK/TELL about an object but not GIVE/SHOW the * object, because the object is known but not currently in scope. */ class TopicOrThingMatchTopic: ThingMatchTopic, TopicMatchTopic matchTopic(fromActor, obj) { /* * if we're being asked to match a ResolvedTopic, use the * inherited TopicMatchTopic handling; otherwise, use the * inherited ThingMatchTopic handling */ if (obj.ofKind(ResolvedTopic)) return inherited TopicMatchTopic(fromActor, obj); else return inherited ThingMatchTopic(fromActor, obj); } isMatchPossible(actor, scopeList) { /* if a match is possible from either subclass, allow it */ return (inherited TopicMatchTopic(actor, scopeList) || inherited ThingMatchTopic(actor, scopeList)); } setTopicPronouns(fromActor, obj) { /* * if the object is a ResolvedTopic, use the inherited * TopicMatchTopic handling, otherwise use the ThingMatchTopic * handling */ if (obj.ofKind(ResolvedTopic)) return inherited TopicMatchTopic(fromActor, obj); else return inherited ThingMatchTopic(fromActor, obj); } ; /* * A combined ASK/TELL/SHOW topic. Players will sometimes want to point * something out when it's visible, rather than asking about it; this * allows SHOW TO to be used as a synonym for ASK ABOUT for these cases. */ class AskTellShowTopic: TopicOrThingMatchTopic includeInList = [&askTopics, &tellTopics, &showTopics] ; /* * A combined ASK/TELL/GIVE/SHOW topic. */ class AskTellGiveShowTopic: TopicOrThingMatchTopic includeInList = [&askTopics, &tellTopics, &giveTopics, &showTopics] ; /* * A command topic. This is used to respond to orders given to an NPC, * as in "BOB, GO EAST." The match object for this kind of topic entry * is an Action class; for example, to create a response to "BOB, LOOK", * we'd create a CommandTopic that matches LookAction. * * If you're designing a CommandTopic for a command can be accepted from * a remote location, such as by telephone, you should be aware that the * command will be running in the NPC's visual sense context. This means * that if the player character can't see the NPC, the topic result * message will be hidden - the NPC's visual sense context hides all * messages generated while it's in effect if the PC can't see the NPC. * This is usually desirable, since most messages relay visual * information that wouldn't be visible to the player character if the PC * can't see the subject of the message. However, if you've specifically * designed your CommandTopic to work remotely, this isn't at all what * you want, since you've already taken the remoteness into account in * the message and thus want the message to be displayed after all. The * way to handle this is to wrap the message in a callWithSenseContext() * with a nil sense context. For example: * * topicResponse() *. { callWithSenseContext(nil, nil, {: "Here's my message!" }); } */ class CommandTopic: TopicEntry /* we go in the command topics list */ includeInList = [&commandTopics] /* match the topic */ matchTopic(fromActor, obj) { /* * Check the collection or the single object, as needed. Note * that our match object is an Action base class, so we must * match if 'obj' is of the match object class. */ if (matchObj.ofKind(Collection)) { /* check each entry for a match */ if (matchObj.indexWhich({x: obj.ofKind(x)}) != nil) return matchScore; } else { /* check our single object */ if (obj.ofKind(matchObj)) return matchScore; } /* didn't find a match */ return nil; } /* * we can always match, since the player can always type in any * possible action */ isMatchPossible(actor, scopeList) { return true; } /* we have no pronouns to set */ setTopicPronouns(fromActor, topic) { } ; /* * A base class for simple miscellaneous topics. These handle things * like YES, NO, HELLO, and GOODBYE, where the topic is entirely * contained in the verb, and there's no separate noun phrase needed to * indicate the topic. */ class MiscTopic: TopicEntry matchTopic(fromActor, obj) { /* * if it's one of our matching topics, return our match score, * otherwise return a nil score to indicate failure */ return (matchList.indexOf(obj) != nil) ? matchScore : nil; } /* * a match is always possible for simple verb topics (since the * player could always type the verb) */ isMatchPossible(actor, scopeList) { return true; } ; /* * A greeting topic - this handles a HELLO or TALK TO command, as well * as implied greetings (the kind of greeting generated when we jump * directly into a conversation with an actor that uses stateful * conversations, by typing a command like ASK ABOUT or TELL ABOUT * without first saying HELLO explicitly). */ class HelloTopic: MiscTopic includeInList = [&miscTopics] matchList = [helloTopicObj, impHelloTopicObj] /* * this is an explicit greeting, so it obviously shouldn't trigger * an implied greeting, regardless of how conversational we are */ impliesGreeting = nil /* * if we use this as a greeting upon entering a ConvNode, we'll want * to stay in the node afterward */ noteInvocation(fromActor) { inherited(fromActor); "<.convstay>"; } ; /* * An implied greeting topic. This handles ONLY implied greetings. * * Note that we have a higher-than-normal score by default. This makes * it easy to program two common cases for conversational states. * First, the more common case, where you want a single message for both * implied and explicit greetings: just create a HelloTopic, since that * responds to both kinds. Second, the less common case, where we want * to differentiate, writing separate responses for implied and explicit * greetings: create a HelloTopic for the explicit kind, and ALSO create * an ImpHelloTopic for the implied kind. Since the ImpHelloTopic has a * higher score, it'll overshadow the HelloTopic object when it matches * an implied greeting; but since ImpHelloTopic doesn't match an * explicit greeting, we'll fall back on the HelloTopic for that. */ class ImpHelloTopic: MiscTopic includeInList = [&miscTopics] matchList = [impHelloTopicObj] matchScore = 200 /* * this is itself a greeting, so we obviously don't want to trigger * another greeting to greet the greeting */ impliesGreeting = nil /* * if we use this as a greeting upon entering a ConvNode, we'll want * to stay in the node afterward */ noteInvocation(fromActor) { inherited(fromActor); "<.convstay>"; } ; /* * Actor Hello topic - this handles greetings when an NPC initiates the * conversation. */ class ActorHelloTopic: MiscTopic includeInList = [&miscTopics] matchList = [actorHelloTopicObj] matchScore = 200 /* this is a greeting, so we don't want to trigger another greeting */ impliesGreeting = nil /* * if we use this as a greeting upon entering a ConvNode, we'll want * to stay in the node afterward */ noteInvocation(fromActor) { inherited(fromActor); "<.convstay>"; } ; /* * A goodbye topic - this handles both explicit GOODBYE commands and * implied goodbyes. Implied goodbyes happen when a conversation ends * without an explicit GOODBYE command, such as when the player character * walks away from the NPC, or the NPC gets bored and wanders off, or the * NPC terminates the conversation of its own volition. */ class ByeTopic: MiscTopic includeInList = [&miscTopics] matchList = [byeTopicObj, leaveByeTopicObj, boredByeTopicObj, actorByeTopicObj] /* * If we're not already in a conversation when we say GOODBYE, don't * bother saying HELLO implicitly - if the player is saying GOODBYE * explicitly, she probably has the impression that there's some kind * of interaction already going on with the NPC. If we didn't * override this, you'd get an automatic HELLO followed by the * explicit GOODBYE when not already in conversation, which is a * little weird. */ impliesGreeting = nil ; /* * An implied goodbye topic. This handles ONLY automatic (implied) * conversation endings, which happen when we walk away from an actor * we're talking to, or the other actor ends the conversation after being * ignored for too long, or the other actor ends the conversation of its * own volition via npc.endConversation(). * * We use a higher-than-default matchScore so that any time we have both * a ByeTopic and an ImpByeTopic that are both active, we'll choose the * more specific ImpByeTopic. */ class ImpByeTopic: MiscTopic includeInList = [&miscTopics] matchList = [leaveByeTopicObj, boredByeTopicObj, actorByeTopicObj] matchScore = 200 ; /* * A "bored" goodbye topic. This handles ONLY goodbyes that happen when * the actor we're talking terminates the conversation out of boredom * (i.e., after a period of inactivity in the conversation). * * Note that this is a subset of ImpByeTopic - ImpByeTopic handles * "bored" and "leaving" goodbyes, while this one handles only the * "bored" goodbyes. You can use this kind of topic if you want to * differentiate the responses to "bored" and "leaving" conversation * endings. */ class BoredByeTopic: MiscTopic includeInList = [&miscTopics] matchList = [boredByeTopicObj] matchScore = 300 ; /* * A "leaving" goodbye topic. This handles ONLY goodbyes that happen * when the PC walks away from the actor they're talking to. * * Note that this is a subset of ImpByeTopic - ImpByeTopic handles * "bored" and "leaving" goodbyes, while this one handles only the * "leaving" goodbyes. You can use this kind of topic if you want to * differentiate the responses to "bored" and "leaving" conversation * endings. */ class LeaveByeTopic: MiscTopic includeInList = [&miscTopics] matchList = [leaveByeTopicObj] matchScore = 300 ; /* * An "actor" goodbye topic. This handles ONLY goodbyes that happen when * the NPC terminates the conversation of its own volition via * npc.endConversation(). */ class ActorByeTopic: MiscTopic includeInList = [&miscTopics] matchList = [actorByeTopicObj] matchScore = 300 ; /* a topic for both HELLO and GOODBYE */ class HelloGoodbyeTopic: MiscTopic includeInList = [&miscTopics] matchList = [helloTopicObj, impHelloTopicObj, byeTopicObj, boredByeTopicObj, leaveByeTopicObj, actorByeTopicObj] /* * since we handle greetings, we don't want to trigger a separate * implied greeting */ impliesGreeting = nil ; /* * Topic singletons representing HELLO and GOODBYE topics. These are * used as the parameter to matchTopic() when we're looking for the * response to the corresponding verbs. */ helloTopicObj: object; byeTopicObj: object; /* * a topic singleton for implied greetings (the kind of greeting that * happens when we jump right into a conversation with a command like * ASK ABOUT or TELL ABOUT, rather than explicitly saying HELLO first) */ impHelloTopicObj: object; /* * a topic singleton for an NPC-initiated hello (this is the kind of * greeting that happens when the NPC is the one who initiates the * conversation, via actor.initiateConversation()) */ actorHelloTopicObj: object; /* * topic singletons for the two kinds of automatic goodbyes (the kind of * conversation ending that happens when we simply walk away from an * actor we're in conversation with, or when we ignore the other actor * for enough turns that the actor gets bored and ends the conversation * of its own volition) */ boredByeTopicObj: object; leaveByeTopicObj: object; /* * a topic singleton for an NPC-initiated goodbye (this is the kind of * goodbye that happens when the NPC is the one who breaks off the * conversation, via npc.endConversation()) */ actorByeTopicObj: object; /* * A YES/NO topic. These handle YES and/or NO, which are normally used * as responses to questions posed by the NPC. YesNoTopic is the base * class, and can be used to create a single response for both YES and * NO; YesTopic provides a response just for YES; and NoTopic provides a * response just for NO. The only thing an instance of these classes * should normally need to specify is the response text (or a list of * response strings, by multiply inheriting from an EventList subclass as * usual). */ class YesNoTopic: MiscTopic includeInList = [&miscTopics] /* * our list of matching topic objects - we'll only ever be asked to * match 'yesTopicObj' (for YES inputs) or 'noTopicObj' (for NO * inputs) */ matchList = [yesTopicObj, noTopicObj] ; class YesTopic: YesNoTopic matchList = [yesTopicObj] ; class NoTopic: YesNoTopic matchList = [noTopicObj] ; /* * Topic singletons representing the "topic" of YES and NO commands. We * use these as the parameter to matchTopic() in the TopicEntry objects * when we're looking for a response to a YES or NO command. */ yesTopicObj: object; noTopicObj: object; /* * A default topic entry. This is an easy way to create an entry that * will be used as a last resort, if no other entry is found. This kind * of entry will match *any* topic, but with the lowest possible score, * so it will only be used if there's no other match for the topic. * * It's a good idea to provide some variety in a character's default * responses, because it seems that in every real game session, the * player will at some point spend a while peppering an NPC with * questions on every topic that comes to mind. Usually, the player will * think of many things that the author didn't anticipate. The more * things the author covers, the better, but it's unrealistic to think * that an author can reasonably anticipate every topic, or even most * topics, that players will think of. So, we'll have a whole bunch of * ASK, ASK, ASK commands all at once, and much of the time we'll get a * bunch of default responses in a row. It gets tedious in these cases * when the NPC repeats the same default response over and over. * * A simple but effective trick is to provide three or four random * variations on "I don't know that," customized for the character. This * makes the NPC seem less like a totally predictable robot, and it can * also be a convenient place to flesh out the character a bit. An easy * way to do this is to add ShuffledEventList to the superclass list of * the default topic entry, and provide a eventList list with the various * random responses. For example: * * + DefaultAskTellTopic, ShuffledEventList *. ['Bob mutters something unintelligible and keeps fiddling with *. the radio. ', *. 'Bob looks up from the radio for a second, but then goes back *. to adjusting the knobs. ', *. 'Bob just keeps adjusting the radio, completely ignoring you. '] *. ; * * It's important to be rather generic in default responses; in * particular, it's a bad idea to suggest that the NPC doesn't know about * the topic. From the author's perspective, it's easy to make the * mistake of thinking "this is a default response, so it'll only be used * for topics that are completely off in left field." Wrong! Sometimes * the player will indeed ask about completely random stuff, but in * *most* cases, the player is only asking because they think it's a * reasonable thing to ask about. Defaults that say things like "I don't * know anything about that" or "What a crazy thing to ask about" or "You * must be stupid if you think I know about that!" can make a game look * poorly implemented, because these will inevitably be shown in response * to questions that the NPC really ought to know about: * *. >ask bob about his mother *. "I don't know anything about that!" *. *. >ask bob about his father *. "You'd have to be a moron to think I'd know about that!" * * It's better to use responses that suggest that the NPC is * uninterested, or is hostile, or is preoccupied with something else, or * doesn't understand the question, or something else appropriate to the * character. If you can manage to make the response about the * *character*, rather than the topic, it'll reduce the chances that the * response is jarringly illogical. */ class DefaultTopic: TopicEntry /* * A list of objects to exclude from the default match. This can be * used to create a default topic that matches everything EXCEPT a * few specific topics that are handled in enclosing topic databases. * For example, if you want to create a catch-all in a ConvNode's * list of topics, but you want a particular topic to escape the * catch-all and be sent instead to the Actor's topic database, you * can put that topic in the exclude list for the catch-all, making * it a catch-almost-all. */ excludeMatch = [] /* match anything except topics in our exclude list */ matchTopic(fromActor, topic) { /* * If the topic matches anything in the exclusion list, do NOT * match the topic. If 'topic' is a ResolvedTopic, search its * in-scope and 'likely' lists; otherwise search for 'topic' * directly in the exclusion list. */ if (topic.ofKind(ResolvedTopic)) { /* it's a resolved topic, so search the in-scope/likely lists */ if (topic.inScopeList.intersect(excludeMatch).length() != 0 || topic.likelyList.intersect(excludeMatch).length() != 0) return nil; } else if (excludeMatch.indexOf(topic) != nil) return nil; /* match anything else with our score */ return matchScore; } /* use a low default matching score */ matchScore = 1 /* a match is always possible for a default topic */ isMatchPossible(actor, scopeList) { return true; } /* set the topic pronoun */ setTopicPronouns(fromActor, topic) { /* * We're not matching anything, so we can get no guidance from * the match object. Instead, look at the topic itself. If it's * a Thing, set the Thing as the antecedent. If it's a * ResolvedTopic, and there's only one Thing match in scope, or * only one Thing match in the likely list, set that. Otherwise, * we have no grounds for guessing. */ if (topic != nil) { if (topic.ofKind(Thing)) { /* we have a Thing - use it as the antecedent */ fromActor.setPronounObj(topic); } else if (topic.ofKind(ResolvedTopic)) { local lst; /* * if there's only one Thing in scope, or only one Thing * in the 'likely' list, use it */ lst = topic.inScopeList.subset({x: x.ofKind(Thing)}); if (lst.length() == 0) lst = topic.likelyList.subset({x: x.ofKind(Thing)}); /* if we got exactly one object, it's the antecedent */ if (lst.length() == 1) fromActor.setPronounObj(lst[1]); } } } ; /* * Default topic entries for different uses. We'll use a hierarchy of * low match scores, in descending order of specificity: 3 for * single-type defaults (ASK only, for example), 2 for multi-type * defaults (ASK/TELL), and 1 for the ANY default. */ class DefaultCommandTopic: DefaultTopic includeInList = [&commandTopics] matchScore = 3 ; class DefaultAskTopic: DefaultTopic includeInList = [&askTopics] matchScore = 3 ; class DefaultTellTopic: DefaultTopic includeInList = [&tellTopics] matchScore = 3 ; class DefaultAskTellTopic: DefaultTopic includeInList = [&askTopics, &tellTopics] matchScore = 2 ; class DefaultGiveTopic: DefaultTopic includeInList = [&giveTopics] matchScore = 3 ; class DefaultShowTopic: DefaultTopic includeInList = [&showTopics] matchScore = 3 ; class DefaultGiveShowTopic: DefaultTopic includeInList = [&giveTopics, &showTopics] matchScore = 2 ; class DefaultAskForTopic: DefaultTopic includeInList = [&askForTopics] matchScore = 3 ; class DefaultAnyTopic: DefaultTopic includeInList = [&askTopics, &tellTopics, &showTopics, &giveTopics, &askForTopics, &miscTopics, &commandTopics] /* * exclude these from actor-initiated hellos & goodbyes - those * should only match topics explicitly */ excludeMatch = [actorHelloTopicObj, actorByeTopicObj] matchScore = 1 ; /* * A "special" topic. This is a topic that responds to its own unique, * custom command input. In other words, rather than responding to a * normal command like ASK ABOUT or SHOW TO, we'll respond to a command * for which we define our own syntax. Our special syntax doesn't have * to follow any of the ordinary parsing conventions, because whenever * our ConvNode is active, we get a shot at parsing player input before * the regular parser gets to see it. * * A special topic MUST be part of a ConvNode, because these are * inherently meaningful only in context. A special topic is active * only when its conversation node is active. * * Special topics are automatically Suggested Topics as well as Topic * Entries. Because special topics use their own custom grammar, it's * unreasonable to expect a player to guess at the custom grammar, so we * should always provide a topic inventory suggestion for every special * topic. */ class SpecialTopic: TopicEntry, SuggestedTopicTree /* * Our keyword list. Each special topic instance must define a list * of strings giving the keywords we match. The special topic will * match user input if the user input consists exclusively of words * from this keyword list. The user input doesn't have to include * all of the words defined here, but all of the words in the user's * input have to appear here to match. * * Alternatively, an instance can specifically define its own custom * regular expression pattern instead of using the keyword list; the * regular expression allows the instance to include punctuation in * the syntax, or apply more restrictive criteria than simply * matching the keywords. */ keywordList = [] /* * Initialize the special topic. This runs during * pre-initialization, to give us a chance to do pre-game set-up. * * This routine adds the topic's keywords to the global dictionary, * under the 'special' token type. Since a special topic's keywords * are accepted when the special topic is active, it would be wrong * for the parser to claim that the words are unknown when the * special topic isn't active. By adding the keywords to the * dictionary, we let the parser know that they're valid words, so * that it won't claim that they're unknown. */ initializeSpecialTopic() { /* add each keyword */ foreach (local cur in keywordList) { /* * Add the keyword. Since we don't actually need the * word-to-object association that the dictionary stores, * simply associate the word with the SpecialTopic class * rather than with this particular special topic instance. * The dictionary only stores a given word-obj-prop * association once, even if it's entered repeatedly, so * tying all of the special topic keywords to the * SpecialTopic class ensures that we won't store redundant * entries if the same keyword is used in multiple special * topics. */ cmdDict.addWord(SpecialTopic, cur, &specialTopicWord); } } /* * our regular expression pattern - we'll build this automatically * from the keyword list if this isn't otherwise defined */ matchPat = nil /* our suggestion (topic inventory) base name */ name = '' /* * our suggestion (topic inventory) full name is usually the same as * the base name; special topics usually aren't grouped in topic * suggestion listings, since each topic usually has its own unique, * custom syntax */ fullName = (name) /* on being suggested, update the special topic history */ noteSuggestion() { specialTopicHistory.noteListing(self); } /* include in the specialTopics list of our parent topic database */ includeInList = [&specialTopics] /* * By default, don't limit the number of times we'll suggest this * topic. Since a special topic is valid only in a particular * ConvNode context, we normally want all of the topics in that * context to be available, even if they've been used before. */ timesToSuggest = nil /* check for a match */ matchTopic(fromActor, topic) { /* * We match if and only if we're the current active topic for * our conversation node, as designated during our pre-parsing. * Because we're activated exclusively by our special syntax, * the only way we can ever match is by matching our special * syntax in pre-parsing; when that happens, the pre-parser * notes the matching SpecialTopic and sends a pseudo-command to * the parser to let it know to invoke the special topic's * response. We take this circuitous route to showing the * response because we do our actual matching in the pre-parse * step, but we want to do the actual command processing * normally; we can only accomplish both needs using this * two-step process, with the two steps tied together via our * memory of the topic selected in pre-parse. */ if (getConvNode().activeSpecialTopic == self) return matchScore; else return nil; } /* * a special topic is always matchable, since we match on literal * text */ isMatchPossible(actor, scopeList) { return true; } /* * Match a string during pre-parsing. By default, we'll match the * string if all of its words (as defined by the regular expression * parser) match our keywords. */ matchPreParse(str, procStr) { /* build the regular expression pattern if there isn't one */ if (matchPat == nil) { local pat; /* start with the base pattern string */ pat = '*(%<'; /* add the keywords */ for (local i = 1, local len = keywordList.length() ; i <= len ; ++i) { /* add this keyword to the pattern */ pat += keywordList[i]; /* add the separator or terminator, as appropriate */ if (i == len) pat += '%>*)+'; else pat += '%>*|%<'; } /* create the pattern object */ matchPat = new RexPattern(pat); } /* we have a match if the pattern matches the processed input */ return rexMatch(matchPat, procStr) == procStr.length(); } /* find our enclosing ConvNode object */ getConvNode() { /* scan up the containment tree for a ConvNode */ for (local loc = location ; loc != nil ; loc = loc.location) { /* if this is a ConvNode, it's what we're looking for */ if (loc.ofKind(ConvNode)) return loc; } /* not found */ return nil; } ; /* * A history of special topics listed in topic inventories. This keeps * track of special topics that we've recently offered, so that we can * provide better feedback if the player tries to use a recently-listed * special topic after it's gone out of context. * * When the player types a command that the parser doesn't recognize, the * parser will check the special topic history to see if the command * matches a special topic that was suggested recently. If so, we'll * explain that the command isn't usable right now, rather than claiming * that the command is completely invalid. A player might justifiably * find it confusing to have the game suggest a command one minute, and * then claim that the very same command is invalid a minute later. * * Ideally, we'd search *every* special topic for a match each time the * player enters an invalid command, but that could take a long time in a * conversation-heavy game with a large number of special topics. As a * compromise, we keep track of the last few special commands that were * actually suggested, so that we can scan those. The reasoning is that * a player is more likely to try a recently-offered special command; the * player will probably eventually forget older suggestions, and in any * case it's much more jarring to see a "command not understood" response * to a suggestion that's still fresh in the player's memory. * * This is a transient object because we're interested in the special * topics that have been offered in the current session, irrespective of * things like 'undo' and 'restore'. From the player's perspective, the * recency of a special topic suggestion is a function of the transcript, * not of the internal story timeline. For example, if the game suggests * a special topic, then the player types UNDO, the player might still * think to try the special topic on the next turn simply because it's * right there on the screen a few lines up. */ transient specialTopicHistory: object /* * Maximum number of topics to keep in our inventory. When the * history exceeds this number, we'll throw away the oldest entry * each time we need to add a new entry - thus, we'll always have the * N most recent suggestions. * * This can be configured as desired. The default setting tries to * strike a balance between speed and good feedback - we try to keep * track of enough entries that most players wouldn't think to try * anything that's aged out of the list, but not so many that it * takes a long time to scan them all. * * If you set this to nil, we won't keep a history at all, but * instead simply scan every special topic in the entire game when we * need to look for a match to an entered command - in a game with a * small number of special topics (on the order of, say, 30 or 40), * there should be no problem using this approach. Note that this * changes the behavior in one important way: when there's no history * limit, we can topics that *haven't even been offered yet*. In * some ways this is more desirable than only scanning past * suggestions, since it avoids weird situations where the game * claims that a command is unrecognized at one point, but later * suggests and then accepts the exact same command. It's * conceivably less desirable in that it could accidentally give away * information to the player, by letting them know that a randomly * typed command will be meaningful at some point in the game - but * the odds of this even happening seem minuscule, and the * possibility that it would give away meaningful information even if * it did happen seems very remote. */ maxEntries = 20 /* note that a special topic 't' is being listed in a topic inventory */ noteListing(t) { /* * If t's already in the list, delete it from its current * position, so that we can add it back at the end of the list, * reflecting its status as the most recent entry. */ historyList.removeElement(t); /* * if the list is already at capacity, remove the oldest entry, * which is the first entry in the list */ if (maxEntries != nil && historyList.length() >= maxEntries) historyList.removeElementAt(1); /* add the new entry at the end of the list */ historyList.append(t); } /* * Scan the history list (or, if there's no limit to the history, * scan all of the special topics in the entire game) for a match to * an unrecognized command. Returns true if we find a match, nil if * not. */ checkHistory(toks) { local str, procStr; /* get the original and processed version of the input string */ str = cmdTokenizer.buildOrigText(toks); procStr = specialTopicPreParser.processInputStr(str); /* * scan each special topic in the history - or, if the history is * unlimited, scan every special topic */ if (maxEntries != nil) { /* scan each entry in our history list */ for (local l = historyList, local i = 1, local len = l.length() ; i <= len ; ++i) { /* check this entry */ if (l[i].matchPreParse(str, procStr)) return true; } } else { /* no history limit - scan every special topic in the game */ for (local o = firstObj(SpecialTopic) ; o != nil ; o = nextObj(o, SpecialTopic)) { /* check this entry */ if (o.matchPreParse(str, procStr)) return true; } } /* we didn't find a match */ return nil; } /* * The list of entries. Create it when we first need it, which * perInstance does for us. */ historyList = perInstance(new transient Vector(maxEntries)) ; /* * An "initiate" topic entry. This is a rather different kind of topic * entry from the ones we've defined so far; an initiate topic is for * cases where the NPC itself wants to initiate a conversation in * response to something in the environment. * * One way to use initiate topics is to use the current location as the * topic key. This lets the NPC say something appropriate to the current * room, and can be coded simply as * *. actor.initiateTopic(location); */ class InitiateTopic: ThingMatchTopic /* include in the initiateTopics list */ includeInList = [&initiateTopics] /* * since this kind of topic is triggered by internal calculations in * the game, and not on anything the player is doing, there's no * reason that our match object should be a pronoun antecedent */ setTopicPronouns(fromActor, topic) { } ; /* a catch-all default initiate topic */ class DefaultInitiateTopic: DefaultTopic includeInList = [&initiateTopics] ; /* ------------------------------------------------------------------------ */ /* * An ActorState represents the current state of an Actor. * * The main thing that makes actors special is that they're supposed to * be living, breathing people or creatures. That substantially * complicates the programming of one of these objects, because in order * to create the appearance of animation, many things about an actor have * to change over time. * * The ActorState is designed to make it easier to program this * variability that's needed to make an actor seem life-like. The idea * is to separate the parts of an actor that tend to change according to * what the actor is doing, moving all of those out of the Actor object * and into an ActorState object instead. Each ActorState object * represents one state of an actor (i.e., one thing the actor can be * doing). The Actor object becomes easier to program, because we've * reduced the Actor object to the character's constant, unchanging * features. The stateful part is also easier to program, because we * don't have to make it conditional on anything; we simply define all of * the stateful parts in an ActorState, and we define separate ActorState * objects for the different states. * * For example, suppose we want a shopkeeper actor, whose activities * include waiting behind the counter, sweeping the floor, and stacking * cans. We'd define one ActorState object for each of these activities. * When the shopkeeper switches from standing behind the counter to * sweeping, for example, we simply set the "curState" property in the * shopkeeper object so that it points to the "sweeping" state object. * When it's time to stack cans, we change "curState" to it points to the * "stacking cans" state object. */ class ActorState: TravelMessageHandler, ActorTopicDatabase construct(actor) { location = actor; } /* * Activate the state - this is called when we're about to become * the active state for an actor. We do nothing by default. */ activateState(actor, oldState) { } /* * Deactivate the state - this is called when we're the active state * for an actor, and the actor is about to switch to a new state. * We do nothing by default. */ deactivateState(actor, newState) { } /* * Is this the actor's initial state? If so, we'll automatically * set the actor's curState to point to 'self' during * pre-initialization. For obvious reasons, this should be set to * true for only one state for each actor; if multiple states are * all flagged as initial for the same actor, we'll pick on * arbitrarily as the actual initial state. */ isInitState = nil /* * Should we automatically suggest topics when the player greets our * actor? By default, we show our "topic inventory" (the list of * currently active topics marked as "suggested"). This can be set * to nil to suppress this automatic suggestion list. * * Some authors might not like the idea of automatically suggesting * topics every time we greet a character, but nonetheless wish to * keep the TOPICS command as a sort of hint mechanism. This flag * can be used for this purpose. Authors who don't like suggested * topics at all can simply skip defining any SuggestedTopic entries, * in which case there will never be anything to suggest, rendering * this flag moot. */ autoSuggest = true /* * The 'location' is the actor that we're associated with. * * ActorState objects aren't actual simulation objects, so the * 'location' property isn't used for containment. For convenience, * though, use it to indicate which actor we're associated with; this * lets us use the '+' notation to define the state objects * associated with an actor. */ location = nil /* * Get the actor associated with the state - this is simply the * 'location' property. If we're nested inside another ActorState, * then our actor is our enclosing ActorState's actor. */ getActor() { if (location.ofKind(ActorState)) return location.getActor(); else return location; } /* the owner of any topic entries within the state is just my actor */ getTopicOwner() { return getActor(); } /* initialize the actor state */ initializeActorState() { /* * if we're the initial state for our actor, set the actor's * current state property to point to me */ if (isInitState) getActor().setCurState(self); } /* * Show the special description for the actor when the actor is * associated with this state. By default, we use the actor's * actorHereDesc message, which usually shows a generic message * (something like "Bob is here" or "Bob is sitting on the chair") to * indicate that the actor is present. * * States representing scripted activities should override these to * indicate what the actor is doing: "Bob is sweeping the floor," for * example. */ specialDesc() { getActor().actorHereDesc; } /* show the special description for the actor at a distance */ distantSpecialDesc() { getActor().actorThereDesc; } /* show the special description for the actor in a remote location */ remoteSpecialDesc(actor) { getActor().actorThereDesc; } /* * The list group(s) for the special description. By default, if * our specialDesc isn't overridden, we'll keep this in sync with * the specialDesc by returning our actor's actorListWith. And if * specialDesc *is* overridden, we'll just return an empty list to * indicate that we're not part of any list group. If you want to * provide your own listing group special to the state, simply * override this and speicfy the custom list group. */ specialDescListWith() { /* * if specialDesc is inherited from ActorState, then use the * default handling from the actor; otherwise, use no grouping at * all by default */ if (!overrides(self, ActorState, &specialDesc)) return getActor().actorListWith; else return []; } /* show the special description when we appear in a contents listing */ showSpecialDescInContents(actor, cont) { /* by default, just show our posture in our container */ getActor().listActorPosture(actor); } /* * Our "state" description. This shows information on what the actor * is *currently* doing; we display this after the static part of the * actor's description on EXAMINE . By default, we add * nothing here, but state objects that represent scripted activies * should override this to describe their scripted activities. */ stateDesc = "" /* * Should we obey an action? If so, returns true; if not, displays * an appropriate response and returns nil. This will only be * called when the issuing actor is different from our actor, since * a command to oneself is implicitly always obeyed. */ obeyCommand(issuingActor, action) { /* * By default, we ignore all orders. We do need to generate a * response, though, so for this purpose, treat the order as a * conversational action, with the 'action' object as the topic. */ handleConversation(issuingActor, action, commandConvType); /* indicate that the order is refused */ return nil; } /* * Suggest topics for the given actor to talk to us about. This is * called when the given actor enters a TOPICS command (in which * case 'explicit' will be true) or enters a conversation with us * via TALK TO or the like (in which case 'explicit' will be nil). */ suggestTopicsFor(actor, explicit) { /* * if this is not an explicit TOPICS request, and we're not in * "auto suggest" mode, don't show anything - we don't want any * automatic suggestions in this mode */ if (!explicit && !autoSuggest) return; /* * show a paragraph break, in case we're being tacked on to * another report; but make it cosmetic, so that this by itself * doesn't suppress a default report, in case we don't end up * displaying any topics */ cosmeticSpacingReport('<.p>'); /* show our suggestion list */ showSuggestedTopicList(getSuggestedTopicList(), actor, getActor(), explicit); } /* * Get our suggested topic list. The suggested topic list consists * of the union of the current ConvNode's suggestion list, the * ActorState list, and the Actor's suggestion list. In each case, * the suggestion list is the list of all SuggestedTopic objects at * each database level. * * The suggestions are arranged in a hierarchy, and each hierarchy * level can prevent suggestions from a lower level from being * included. The top level of the hierarchy is the ConvNode; the * next level is the ActorState; and the last level is the Actor. * Suggestions are limited at each level with the 'limitSuggestions' * property: if true, suggestions from lower levels are not included. */ getSuggestedTopicList() { local v = new Vector(16); local node; local lst; /* add the actor's current conversation node topics */ if ((node = getActor().curConvNode) != nil) { /* if there are any suggested topics in the node, include them */ if ((lst = node.suggestedTopics) != nil) v.appendAll(lst); /* * if this ConvNode is marked as limiting suggestions to * those defined within the node, return what we have * without adding anything from the broader context */ if (node.limitSuggestions) return v; } /* add our own topics */ if ((lst = stateSuggestedTopics) != nil) v.appendAll(lst); /* * if the ActorState is limiting suggestions, don't include any * suggestions from the broader context (i.e., from the Actor * itself) */ if (limitSuggestions) return v; /* if our actor has its own list, add those as well */ if ((lst = getActor().suggestedTopics) != nil) v.appendAll(lst); /* return the combined list */ return v; } /* * get the topic suggestions for this state - by default, we just * return our own suggestedTopics list */ stateSuggestedTopics = (suggestedTopics) /* * Get my implied in-conversation state. This is used when our actor * initiates a conversation without specifying a particular * conversation state to enter (i.e., actor.initiateConversation() is * called with 'state' set to nil). By default, we don't have an * implied conversation state, so we just return 'self' to indicate * that we want to stay in the current state. States that are * coupled with separate in-conversation states, such as * ConversationReadyState, should return their associated * conversation states here. */ getImpliedConvState = (self) /* * General conversation handler. This can be used to process most * conversational commands - ASK, TELL, GIVE, SHOW, etc. The * standard sequence of processing is as follows: * * - If our actor has a non-nil current conversation node (ConvNode) * object, and the ConvNode wants to handle the event, let the * ConvNode handle it. * * - Otherwise, check our own topic database to see if we can find a * TopicEntry that matches the topic; if we can find one, let the * TopicEntry handle it. * * - Otherwise, let the actor handle it. * * 'otherActor' is the actor who originated the conversation command * (usually the player character). 'topic' is the subject being * discussed (the indirect object of ASK ABOUT, for example). * convType' is a ConvType describing the type of conversational * action we're performing. */ handleConversation(otherActor, topic, convType) { local actor = getActor(); local hasDefault; local node; local path; /* determine if I have a default response handler */ hasDefault = propDefined(convType.defaultResponseProp); /* * Figure the database search path for looking up the topics. * We'll start in the ConvNode database, then continue to the * ActorState database, then finally to the Actor database. * However, we won't reach the Actor database if there's a * default response handler in the state, because if we fail to * find it at the state, we'll take the default. * * Since the path we need to provide at each point is the * *remaining* path, don't bother including the ConvNode, since * we'd just have to take it right back out to get the remaining * path after the ConvNode. */ path = [self]; if (!hasDefault) path += actor; /* * If our actor has a current conversation node, check to see if * the conversation node wants to handle it. If not, check our * own topic database, then the actor's. */ if ((node = actor.curConvNode) == nil || !node.handleConversation(otherActor, topic, convType, path)) { /* get the remaining database search path */ path = path.sublist(2); /* * Either we don't have a ConvNode, or the ConvNode isn't * interested in handling the operation. Check to see if we * can handle it through our own topic database. */ if (!handleTopic(otherActor, topic, convType, path)) { /* * We couldn't find anything in our topic database that's * interested in handling it. Check to see if the state * object defines the default response handler method, * and use that as the response if so. */ if (hasDefault) { /* * the state object (i.e., self) does define the * default response method, so invoke that */ convType.defaultResponse(self, otherActor, topic); } else { /* * We don't have a topic database entry and we don't * have our own definition of the default response * handler. All that remains is to let our actor * handle it. */ actor.handleConversation(otherActor, topic, convType); } } } /* whatever happened, run the appropriate after-response handling */ convType.afterResponse(actor, otherActor); } /* * Receive notification that a TopicEntry is being used (via its * handleTopic method) to respond to a command. The TopicEntry will * call this before it shows its message or takes any other action. * By default, we do nothing. */ notifyTopicResponse(fromActor, entry) { } /* * Handle a before-action notification for our actor. By default, * we do nothing. */ beforeAction() { /* do nothing by default */ } /* handle an after-action notification for our actor */ afterAction() { } /* handle a before-travel notification */ beforeTravel(traveler, connector) { local other = getActor().getCurrentInterlocutor(); /* * if our conversational partner is departing, break off the * conversation */ if (connector != nil && other != nil && traveler.isActorTraveling(other)) { /* end the conversation */ if (!endConversation(gActor, endConvTravel)) { /* * they don't want to allow the conversation to end, so * abort the travel action */ exit; } } } /* handle an after-travel notification */ afterTravel(traveler, connector) { } /* * End the current conversation. 'reason' indicates why we're * leaving the conversation - this is one of the endConvXxx enums * defined in adv3.h. beforeTravel() calls this automatically when * the other party is trying to depart, and they're talking to us. * * This returns true if we wish to allow the conversation to end, * nil if not. */ endConversation(actor, reason) { local ourActor = getActor(); local node; /* tell the current ConvNode about it */ if ((node = ourActor.curConvNode) != nil) { local ret; /* the can-end call might show a response, so set our actor */ conversationManager.beginResponse(ourActor); /* ask the node if it's okay to end the conversation */ ret = node.canEndConversation(actor, reason); /* * If the result is blockEndConv, it means that the actor * said something to force the conversation to keep going. * Make a note that the other actor already said something on * this turn so that we don't generate another scripted * message later, and flag this as preventing the * conversation ending. */ if (ret == blockEndConv) { /* flag that the other actor said something this turn */ ourActor.noteConvAction(actor); /* we're unable to end the conversation now */ ret = nil; } /* end the response, leaving the node unchanged by default */ conversationManager.finishResponse( ourActor, ourActor.curConvNode); /* * if the node said no, tell the caller we can't end the * conversation right now */ if (!ret) return nil; /* tell the node we are indeed ending the conversation */ node.endConversation(actor, reason); } /* forget any conversation tree position */ ourActor.setConvNodeReason(nil, 'endConversation'); /* indicate that we are allowing the conversation to end */ return true; } /* * Take a turn. This is called when it's the actor's turn and * there's not something else the actor needs to be doing (such as * following another actor, or carrying out a command in the actor's * pending command queue). * * By default, we perform several steps automatically. * * First, we check to see if the actor is in a ConvNode. If so, the * ConvNode takes precedence. If we haven't been addressed already * in conversation on this turn, we'll let the ConvNode perform its * "continuation," which lets the NPC advance the conversation of its * own volition. In any case, if we have a current ConvNode, we're * done with the turn, since we assume the actor will want to proceed * with the conversation before pursuing its agenda or performing a * background action. * * Second, assuming there's no active ConvNode, we check for an * "agenda" item that's ready to execute. If we find one, we execute * it, and we're done. The agenda item takes precedence over any * other scripting we might have. * * Finally, if we also inherit from Script, and we didn't find an * active ConvNode or an agenda item that was ready to execute, we * invoke our doScript() method. This makes it especially easy to * define random background messages for the actor - just add an * EventList class (ShuffledEventList is usually the right one) to * the state's superclass list, and define a list of background * message strings. */ takeTurn() { local actor = getActor(); /* * Check to see if we want to continue a conversation. If so, * and we haven't already conversed this turn, try the * continuing conversation. If that displays anything, consider * the turn done. * * Otherwise, try executing an agenda item. If we do, consider * the turn done. * * Otherwise, if we're of class Script, execute our scripted * action. */ if (actor.curConvNode != nil && !actor.conversedThisTurn() && actor.curConvNode.npcContinueConversation()) { /* * we displayed an NPC-motivated conversation continuation, * so we're done with this turn */ } else if (actor.executeAgenda()) { /* we executed an agenda item, so we need do nothing more */ } else if (ofKind(Script)) { /* we're a Script, so invoke our scripted action */ doScript(); } } /* * Receive notification that we just followed another actor as part * of our programmed following behavior (in other words, due to our * 'followingActor' property, not due to an explicit FOLLOW command * directed to us). 'success' is true if we ended up in the actor's * location, nil if not. * * This can be used to update the actor's state after a 'follow' * operation occurs; for example, if the actor's state depends on * the actor's location, this can update the state accordingly. We * don't do anything by default. */ justFollowed(success) { /* do nothing by default */ } /* * Our group-travel arrival description. By default, when we perform * an accompanying travel with another actor as the lead actor, the * accompanying travel state will display this message instead of our * specialDesc when the lead actor first arrives in the new location. * We'll just display our own specialDesc by default, but this should * usually be overridden to say something specific to the group * travel arrival. The actual message is entirely dependent on the * nature of the group travel, which is why we don't provide a * special message by default. * * For scripted behavior, it's sometimes better to use arrivingTurn() * rather than this method to describe the behavior. * arrivingWithDesc() is called as part of the room description, so * it's best for any message shown here to fit well into the usual * room description format. For more complex transitions into the * new room state, arrivingTurn() is sometimes more appropriate, * since it runs like a daemon, after the arrival (and thus the new * room description) is completed. */ arrivingWithDesc() { specialDesc(); } /* * Perform any special action on a group-travel arrival. When group * travel is performed using the AccompanyingInTravelState class, * this is essentially called in lieu of the regular takeTurn() * method on the state that is coming into effect after the group * travel. (Not really, but effectively: the accompanying travel * state will still be in effect, so its takeTurn() method is what's * really called, but that method will call this method explicitly.) * By default, we do nothing. Since this runs on our turn, it's a * good place to put any scripted behavior we perform on arriving at * our new destination after the group travel. */ arrivingTurn() { } /* * For our TravelMessageHandler implementation, the nominal traveler * is our actor. Note that this is all we need to implement for * travel message handling, since we simply inherit the default * handling for all of the arrival/departure messages. */ getNominalTraveler() { return getActor(); } ; /* * A "ready for conversation" state. This can be used as the base class * for actor states when the actor is receptive to conversation, and we * want to have the sense of a conversational context. The key feature * that this class provides is the ability to provide messages when * engaging and disengaging the conversation. * * Note that this state is NOT required for conversation, since the basic * ActorState object accepts conversational commands like ASK, TELL, * GIVE, and TAKE. The special feature of the "conversation ready" state * is that we explicitly move the actor to a separate state when * conversation begins. This is especially appropriate for states in * which the NPC is actively carrying on some other activity; the * conversation should interrupt those states, so that the actor stops * the other activity and gives us its full attention. * * This type of state can be associated with its in-conversation state * object in one of two ways. First, the inConvState property can be * explicitly set to point to the in-conversation state object. Second, * this object can be nested inside its in-conversation state object via * the 'location' property (so you can use the '+' syntax to put this * object inside its in-conversation state object). The 'ready' object * goes inside the 'conversing' object because a single 'conversing' * object can frequently be shared among several 'ready' states. */ class ConversationReadyState: ActorState /* * The associated in-conversation state. This should be set to an * InConversationState object that controls the actor's behavior * while carrying on a conversation. Note that the library will * automatically set this if the instance is nested (via its * 'location' property) inside an InConversationState object. */ inConvState = nil /* my implied conversational state is my in-conversation state */ getImpliedConvState = (inConvState) /* * Show our greeting message. If 'explicit' is true, it means that * the player character is greeting us through an explicit greeting * command, such as HELLO or TALK TO. Otherwise, the greeting is * implied by some other conversational action, such a ASK ABOUT or * SHOW TO. We do nothing by default; this should be overridden in * most cases to show some sort of exchange of pleasantries - * something like this: * *. >bob, hello *. "Hi, there," you say. * * Bob looks up over his newspaper. "Oh, hello," he says, putting * down the paper. "What can I do for you?" * * Note that games shouldn't usually override this method. Instead, * you should simply create a HelloTopic entry and put it inside the * state object; we'll find the HelloTopic and show its message as * our greeting. * * If you want to distinguish between explicit and implicit * greetings, you can create an ImpHelloTopic entry for implied * greetings (i.e., the kind of greeting that occurs automatically * when the player jumps right into a conversation with our actor * using ASK ABOUT or the like, without explicitly saying HELLO * first). The regular HelloTopic will handle explicit greetings, * and the ImpHelloTopic will handle the implied kind. */ showGreetingMsg(actor, explicit) { /* look for a HelloTopic in our topic database */ if (handleTopic(actor, explicit ? helloTopicObj : impHelloTopicObj, helloConvType, nil)) "<.p>"; } /* * Enter this state from a conversation. This should show any * message we want to display when we're ending a conversation and * switching from the conversation to this state. 'reason' is the * endConvXxx enum indicating what triggered the termination of the * conversation. 'oldNode' is the ConvNode we were in just before we * initiated the termination - we need this information because we * want to look in the ConvNode for a Bye topic message to display, * but we can't just look in the actor for the node because it will * already have been cleared out by the time we get here. * * Games shouldn't normally override this method. Instead, simply * create a ByeTopic entry and put it inside the state object; we'll * find the ByeTopic and show its message for the goodbye. * * If you want to distinguish between different types of goodbyes, * you can create an ImpByeTopic for any implied goodbye (i.e., the * kind where the other actor just walks away, or where we get bored * of the other actor ignoring us). You can also further * differentiate by creating BoredByeTopic and/or LeaveByeTopic * objects to handle just those cases. The regular ByeTopic will * handle explicit GOODBYE commands, and the others (ImpByeTopic, * BoredByeTopic, LeaveByeTopic) will handle the implied kinds. */ enterFromConversation(actor, reason, oldNode) { local topic; local reasonMap = [endConvBye, byeTopicObj, endConvTravel, leaveByeTopicObj, endConvBoredom, boredByeTopicObj, endConvActor, actorByeTopicObj]; /* figure out which topic object we need, based on the reason code */ topic = reasonMap[reasonMap.indexOf(reason) + 1]; /* * Look for a ByeTopic in the ConvNode; failing that, try our own * database. */ if (oldNode == nil || !oldNode.handleConversation(actor, topic, byeConvType, nil)) { /* there's no node handler; try our own database */ handleTopic(actor, topic, byeConvType, nil); } } /* handle a conversational action directed to our actor */ handleConversation(otherActor, topic, convType) { /* * If this is a greeting, handle it ourselves. Otherwise, pass * it along to our associated in-conversation state. */ if (convType == helloConvType) { /* * Switch to our associated in-conversation state and show a * greeting. Since we're explicitly entering the * conversation, we have no topic entry. */ enterConversation(otherActor, nil); /* show or schedule a topic inventory, as appropriate */ conversationManager.showOrScheduleTopicInventory( getActor(), otherActor); } else { /* * it's not a greeting, so pass it to our in-conversation * state for handling */ inConvState.handleConversation(otherActor, topic, convType); } } /* * Initiate conversation based on the given simulation object. This * is an internal method that isn't usually called directly from game * code; game code usually calls the Actor's initiateTopic(), which * calls this routine to check for a topic that's part of the state * object. */ initiateTopic(obj) { /* defer to our in-conversation state */ return inConvState.initiateTopic(obj); } /* * Receive notification that a TopicEntry is being used (via its * handleTopic method) to respond to a command. If the TopicEntry is * conversational, automatically enter our in-conversation state. */ notifyTopicResponse(fromActor, entry) { if (entry.isConversational) enterConversation(fromActor, entry); } /* * Enter a conversation with the given actor, either explicitly (via * HELLO or TALK TO) or implicitly (by directly asking a question, * etc). 'entry' gives the TopicEntry that's triggering the implicit * conversation entry; if this is nil, it means that we're being * triggered explicitly. */ enterConversation(actor, entry) { local myActor = getActor(); local explicit = (entry == nil); /* if the actor can't talk to us, we can't enter the conversation */ if (!actor.canTalkTo(myActor)) { /* tell them we can't talk now */ reportFailure(&objCannotHearActorMsg, myActor); /* terminate the command */ exit; } /* * Show our greeting, if desired. We show a greeting if we're * being invoked explicitly (that is, there's no TopicEntry), or * if we're being invoked explicitly and the TopicEntry implies a * greeting. */ if (explicit || entry.impliesGreeting) showGreetingMsg(actor, explicit); /* activate the in-conversation state */ myActor.setCurState(inConvState); } /* * Get this state's suggested topic list. ConversationReady states * shouldn't normally have topic entries of their own, since a * ConvversationReady state usually forwards conversation handling * to its corresponding in-conversation state. So, simply return * the suggestion list from our in-conversation state object. */ stateSuggestedTopics = (inConvState.suggestedTopics) /* initialize the actor state object */ initializeActorState() { /* inherit the default handling */ inherited(); /* * if we're nested inside an in-conversation state object, the * containing in-conversation state is the one we'll use for * conversations */ if (location.ofKind(InConversationState)) inConvState = location; } ; /* * The "in-conversation" state. This works with ConversationReadyState * to handle transitions in and out of conversations. In this state, we * are actively engaged in a conversation. * * Throughout this implementation, we assume that we only care about * conversations with a single character, specifically the player * character. There's generally no good reason to fully model * conversations between NPC's, since that kind of NPC activity is in * most cases purely pre-scripted and thus requires no special state * tracking. Since we generally only need to worry about tracking a * conversation with the player character, we don't bother with the * possibility that we're simultaneously in conversation with more than * one other character. */ class InConversationState: ActorState /* * Our attention span, in turns. This is the number of turns that * we'll be willing to stay in the conversation while the other * character is ignoring us. After the conversation has been idle * this long, we'll assume the other actor is no longer talking to * us, so we'll terminate the conversation ourselves. * * If the NPC's doesn't have a limited attention span, set this * property to nil. This will prevent the NPC from ever disengaging * of its own volition. */ attentionSpan = 4 /* * The state to switch to when the conversation ends. Instances can * override this to select the next state. By default, we'll return * to the state that we were in immediately before the conversation * started. */ nextState = (previousState) /* * End the current conversation. 'reason' indicates why we're * leaving the conversation - this is one of the endConvXxx enums * defined in adv3.h. * * This method is a convenience only; you aren't required to call * this method to end the conversation, since you can simply switch * to another actor state directly if you prefer. This method's * main purpose is to display an appropriate message terminating the * conversation while switching to the new state. If you want to * display your own message directly from the code that's changing * the state, there's no reason to call this. * * This returns true if we wish to allow the conversation to end, * nil if not. */ endConversation(actor, reason) { local nxt; local myActor = getActor(); /* * note the current ConvNode for our actor - when we check with * the ConvNode to see about ending the conversation, this will * automatically exit the ConvNode, so we need to save this first * so that we can refer to it later to check for a Bye topic */ local oldNode = myActor.curConvNode; /* * Inherit the base behavior first - if it disallows the action, * return failure. The inherited version will check with the * current ConvNode to see if has any objection. */ if (!inherited(actor, reason)) return nil; /* get the next state */ nxt = nextState; /* if there isn't one, stay in the actor's current state */ if (nxt == nil) nxt = myActor.curState; /* * If the next state is a 'conversation ready' state, tell it * we're entering from a conversation. We're ending the * conversation explicitly only if 'reason' is endConvBye. Pass * along the ConvNode we just exited (if any), so that we can * look for a response in the node. */ if (nxt.ofKind(ConversationReadyState)) nxt.enterFromConversation(actor, reason, oldNode); /* switch our actor to the next state */ myActor.setCurState(nxt); /* indicate that we are allowing the conversation to end */ return true; } /* handle a conversational command */ handleConversation(otherActor, topic, convType) { /* handle goodbyes specially */ if (convType == byeConvType) { /* * If this is an implicit goodbye, run the normal * conversation handling in order to display any implied * ByeTopic message - but capture the output in case we * decide not to end the conversation after all. Only do * this in the case of an implicit goodbye, though - for an * explicit goodbye, there's no need for this as the explicit * BYE will do the same thing on its own. */ local txt = nil; if (topic != byeTopicObj) { txt = mainOutputStream.captureOutput( {: inherited(otherActor, topic, convType) }); } /* * try to end the conversation; if we won't allow it, * terminate the action here */ if (!endConversation(otherActor, endConvBye)) exit; /* show the captured ByeTopic output */ if (txt != nil) say(txt); } else { /* use the inherited handling */ inherited(otherActor, topic, convType); } } /* * provide a default HELLO response, if we don't have a special * TopicEntry for it */ defaultGreetingResponse(actor) { /* * As our default response, point out that we're already at the * actor's service. (This isn't an error, because the other * actor might not have been talking to us, even though we * thought we were talking to them.) */ gLibMessages.alreadyTalkingTo(getActor(), actor); } takeTurn() { local actor = getActor(); /* if we didn't interact this turn, increment our boredom counter */ if (!actor.conversedThisTurn()) actor.boredomCount++; /* run the inherited handling */ inherited(); } /* activate this state */ activateState(actor, oldState) { /* * If the previous state was a ConversationReadyState, or we * have no other state remembered, remember the previous state - * this is the default we'll return to at the end of the * conversation, if the instance doesn't specify another state. * * We don't remember prior states that aren't conv-ready states * to make it easier to temporarily interrupt a conversation * with some other state, and later return to the conversation. * If we remembered every prior state, then we'd return to the * interrupting state when the conversation ended, which is * usually not what's wanted. Usually, we want to return to the * last conv-ready state when a conversation ends, ignoring any * other intermediate states that have been active since the * conv-ready state was last in effect. */ if (previousState == nil || oldState.ofKind(ConversationReadyState)) previousState = oldState; /* * reset the actor's boredom counter, since we're just starting a * new conversation, and add our boredom agenda item to the * active list to monitor our boredom level */ actor.boredomCount = 0; actor.addToAgenda(actor.boredomAgendaItem); /* remember the time of the last conversation command */ actor.lastConvTime = Schedulable.gameClockTime; } /* deactivate this state */ deactivateState(actor, newState) { /* * we're leaving the conversation state, so there's no need to * monitor our boredom level any longer */ actor.removeFromAgenda(actor.boredomAgendaItem); /* do the normal work */ inherited(actor, newState); } /* * The previous state - this is the state we were in before the * conversation began, and the one we'll return to by default when * the conversation ends. We'll set this automatically on * activation. */ previousState = nil ; /* * A special kind of agenda item for monitoring "boredom" during a * conversation. We check to see if our actor is in a conversation, and * the PC has been ignoring the conversation for too long; if so, our * actor initiates the end of the conversation, since the PC apparently * isn't paying any attention to us. */ class BoredomAgendaItem: AgendaItem /* we construct these dynamically during actor initialization */ construct(actor) { /* remember our actor as our location */ location = actor; } /* * we're ready to run if our actor is in an InConversationState and * its boredom count has reached the limit for the state */ isReady() { local actor = getActor(); local state = actor.curState; return (inherited() && state.ofKind(InConversationState) && state.attentionSpan != nil && actor.boredomCount >= state.attentionSpan); } /* on invocation, end the conversation */ invokeItem() { local actor = getActor(); local state = actor.curState; /* tell the state to end the conversation */ state.endConversation(actor.getCurrentInterlocutor(), endConvBoredom); } /* * by default, handle boredom before other agenda items - we do this * because an ongoing conversation will be the first thing on the * NPC's mind */ agendaOrder = 50 ; /* * A "hermit" actor state is a state where the actor is unresponsive to * conversational overtures (ASK ABOUT, TELL ABOUT, HELLO, GOODBYE, YES, * NO, SHOW TO, GIVE TO, and any orders directed to the actor). Any * attempt at conversation will be met with the 'noResponse' message. */ class HermitActorState: ActorState /* * Show our response to any conversational command. We'll simply * show the standard "there's no response" message by default, but * subclasses can (and usually should) override this to explain * what's really going on. Note that this routine will be invoked * for any sort of conversation command, so any override needs to be * generic enough that it's equally good for ASK, TELL, and * everything else. * * Note that it's fairly easy to create a shuffled list of random * messages, if you want to add some variety to the actor's * responses. To do this, use an embedded ShuffledEventList: * * myState: HermitActorState *. noResponse() { myList.doScript(); } *. myList: ShuffledEventList { *. ['message1', 'message2', 'message3'] } *. ; */ noResponse() { mainReport(&noResponseFromMsg, getActor()); } /* all conversation actions get the same default response */ handleConversation(otherActor, topic, convType) { /* just show our standard default response */ noResponse(); } /* * Since the hermit state blocks topics from outside the state, don't * offer suggestions for other topics while in this state. * * Note that you might sometimes want to override this to allow the * usual topic suggestions (by setting this to nil). In particular: * * - If it's not outwardly obvious that the actor is unresponsive, * you'll probably want to allow suggestions. Remember, TOPICS * suggests topics that the *PC* wants to talk about, not things the * NPC is interested in. If the PC doesn't necessarily know that the * NPC won't respond, the PC would still want to ask about those * topics. * * - If the hermit state is to be short-lived, you might want to show * the topic suggestions even in the hermit state, so that the player * is aware that there are still useful topics to explore with the * NPC. The player might otherwise assume that the NPC is out of * useful topics, and not bother trying again later when the NPC * becomes more responsive. */ limitSuggestions = true ; /* * The basic "accompanying" state. In this state, whenever the actor * we're accompanying travels to a location we want to follow, we'll * travel at the same time with the other actor. */ class AccompanyingState: ActorState /* * Check to see if we are to accompany the given traveler on the * given travel. 'traveler' is the Traveler performing the travel, * and 'conn' is the connector that the traveler is about to take. * * Note that 'traveler' is a Traveler object. This will simply be an * Actor (which is a kind of Traveler) when the actor is performing * the travel directly, but it could also be another kind of * Traveler, such as a Vehicle. This routine must determine whether * to accompany other kinds of actors. * * By default, we'll return true to indicate that we want to * accompany any traveler anywhere they go. This should almost * always be overridden in practice to be more specific. */ accompanyTravel(traveler, conn) { return true; } /* * Get our accompanying state object. We'll create a basic * accompanying in-travel state object, returning to the current * state when we're done. 'traveler' is the Traveler object that's * performing the travel; this might be an Actor, but could also be a * Vehicle or other Traveler subclass. */ getAccompanyingTravelState(traveler, connector) { /* * Create the default intermediate state for the travel. Note * that the lead actor is the actor performing the command - this * won't necessarily be the traveler, since the actor could be * steering a vehicle. */ return new AccompanyingInTravelState( getActor(), gActor, getActor().curState); } /* * handle a before-travel notification for my actor */ beforeTravel(traveler, connector) { /* * If we want to accompany the given traveler on this travel, add * ourselves to the initiating actor's list of accompanying * actors. Never set an actor to accompany itself, since doing * so would lead to infinite recursion. */ if (accompanyTravel(traveler, connector) && getActor() != gActor) { /* * Add me to the list of actors accompanying the actor * initiating the travel - that actor will run a nested * travel action on us before doing its own travel. Note * that the initiating actor is gActor, since that's the * actor performing the action that led to the travel. */ gActor.addAccompanyingActor(getActor()); /* put my actor into the appropriate new group travel state */ getActor().setCurState( getAccompanyingTravelState(traveler, connector)); } /* inherit the default handling */ inherited(traveler, connector); } ; /* * "Accompanying in-travel" state - this is an actor state used when an * actor is taking part in a group travel operation. This state lasts * only as long as the single turn - which belongs to the lead actor - * that it takes to carry out the group travel. Once our turn comes * around, we'll restore the actor to the previous state - or, we can set * the actor to a different state, if desired. Setting the actor to a * different state is useful when the group travel triggers a new * scripted activity in the new room. */ class AccompanyingInTravelState: ActorState construct(actor, lead, next) { /* do the normal initialization */ inherited(actor); /* remember the lead actor and the next state */ leadActor = lead; nextState = next; } /* the lead actor of the group travel */ leadActor = nil /* * the next state - we'll switch our actor to this state after the * travel has been completed */ nextState = nil /* * Show our "I am here" description. By default, we'll use the * arrivingWithDesc of the *next* state object. */ specialDesc() { nextState.arrivingWithDesc; } /* take our turn */ takeTurn() { /* * The group travel only takes the single turn in which the * travel is initiated, so by the time our turn comes around, the * group travel is done. Clear out the lead actor's linkage to * us as an accompanying actor. */ leadActor.accompanyingActors.removeElement(getActor()); /* switch our actor to the next state */ getActor().setCurState(nextState); /* * call our next state's on-arrival turn-taking method, so that * it can carry out any desired scripted behavior for our arrival */ nextState.arrivingTurn(); } /* initiate a topic - defer to the next state */ initiateTopic(obj) { return nextState.initiateTopic(obj); } /* * Override our departure messages. When we're accompanying another * actor on a group travel, the lead actor will, as part of its turn, * send each accompanying actor (including us) on ahead. This means * that the lead actor will see us departing from the starting * location, because we'll leave before the lead actor has itself * departed. Rather than using the normal "Bob leaves to the west" * departure report, customize the departure reports to indicate * specifically that we're going with the lead actor. (Note that we * only have to handle the departing messages, since group travel * always sends accompanying actors on ahead of the main actor, hence * the accompanying actors will always be seen departing, not * arriving.) * * Note that all of these call our generic sayDeparting() method by * default, so a subclass can catch all of the departure types at * once just by overriding sayDeparting(). Overriding the individual * methods is still desirable, of course, if you want separate * messages for the different departure types. */ sayDeparting(conn) { gLibMessages.sayDepartingWith(getActor(), leadActor); } sayDepartingDir(dir, conn) { sayDeparting(conn); } sayDepartingThroughPassage(conn) { sayDeparting(conn); } sayDepartingViaPath(conn) { sayDeparting(conn); } sayDepartingUpStairs(conn) { sayDeparting(conn); } sayDepartingDownStairs(conn) { sayDeparting(conn); } /* * Describe local travel using our standard departure message as * well. This is used to describe our travel when our origin and * destination locations are both visible to the PC; in these cases, * we don't describe the departure separately because the whole * process of travel from departure to arrival is visible to the PC * and thus is best handled with a single message, which we generate * here. In our case, since the "accompanying" state describes even * normal travel as though it were visible all along, we can use our * standard "departing" message to describe local travel as well. */ sayArrivingLocally(dest, conn) { sayDeparting(conn); } sayDepartingLocally(dest, conn) { sayDeparting(conn); } ; /* ------------------------------------------------------------------------ */ /* * A pending conversation information object. An Actor keeps a list of * these for pending conversations. */ class PendingConvInfo: object construct(state, node, turns) { /* remember how to start the conversation */ state_ = state; node_ = node; /* compute the game clock time when we can start the conversation */ time_ = Schedulable.gameClockTime + turns; } /* * our ActorState and ConvNode (or ConvNode name string), describing * how we're to start the conversation */ state_ = nil node_ = nil /* the minimum game clock time at which we can start the conversation */ time_ = nil ; /* ------------------------------------------------------------------------ */ /* * An "agenda item." Each actor can have its own "agenda," which is a * list of these items. Each item represents an action that the actor * wants to perform - this is usually a goal the actor wants to achieve, * or a conversational topic the actor wants to pursue. * * On any given turn, an actor can carry out only one agenda item. * * Agenda items are a convenient way of controlling complex behavior. * Each agenda item defines its own condition for when the actor can * pursue the item, and each item defines what the actor does when * pursuing the item. Agenda items can improve the code structure for an * NPC's behavior, since they nicely isolate a single background action * and group it with the conditions that trigger it. But the main * benefit of agenda items is the one-per-turn pacing - by executing at * most one agenda item per turn, we ensure that the NPC will carry out * its self-initiated actions at a measured pace, rather than as a jumble * of random actions on a single turn. * * Note that NPC-initiated conversation messages override agendas. If an * actor has an active ConvNode, AND the ConvNode displays a * "continuation message" on a given turn, then the actor will not pursue * its agenda on that turn. In this way, ConvNode continuation messages * act rather like high-priority agenda items. */ class AgendaItem: object /* * My actor - agenda items should be nested within the actor using * '+' so that we can find our actor. Note that this doesn't add the * item to the actor's agenda - that has to be done explicitly with * actor.addToAgenda(). */ getActor() { return location; } /* * Is this item active at the start of the game? Override this to * true to make the item initially active; we'll add it to the * actor's agenda during the game's initialization. */ initiallyActive = nil /* * Is this item ready to execute? The actor will only execute an * agenda item when this condition is met. By default, we're ready * to execute. Items can override this to provide a declarative * condition of readiness if desired. */ isReady = true /* * Is this item done? On each turn, we'll remove any items marked as * done from the actor's agenda list. We remove items marked as done * before executing any items, so done-ness overrides readiness; in * other words, if an item is both 'done' and 'ready', it'll simply * be removed from the list and will not be executed. * * By default, we simply return nil. Items can override this to * provide a declarative condition of done-ness, or they can simply * set the property to true when they finish their work. For * example, an item that only needs to execute once can simply set * isDone to true in its invokeItem() method; an item that's to be * repeated until some success condition obtains can override isDone * to return the success condition. */ isDone = nil /* * The ordering of the item relative to other agenda items. When we * choose an agenda item to execute, we always choose the lowest * numbered item that's ready to run. You can leave this with the * default value if you don't care about the order. */ agendaOrder = 100 /* * Execute this item. This is invoked during the actor's turn when * the item is the first item that's ready to execute in the actor's * agenda list. We do nothing by default. */ invokeItem() { } /* * Reset the item. This is invoked whenever the item is added to an * actor's agenda. By default, we'll set isDone to nil as long as * isDone isn't a method; this makes it easier to reuse agenda * items, since we don't have to worry about clearing out the isDone * flag when reusing an item. */ resetItem() { /* if isDone isn't a method, reset it to nil */ if (propType(&isDone) != TypeCode) isDone = nil; } ; /* * An AgendaItem initializer. For each agenda item that's initially * active, we'll add the item to its actor's agenda. */ PreinitObject execute() { forEachInstance(AgendaItem, function(item) { /* * If this item is initially active, add the item to its * actor's agenda. */ if (item.initiallyActive) item.getActor().addToAgenda(item); }); } ; /* * A "conversational" agenda item. This type of item is ready to execute * only when the actor hasn't engaged in conversation during the same * turn. This type of item is ideal for situations where we want the * actor to pursue a conversational topic, because we won't initiate the * action until we get a turn where the player didn't directly talk to * us. */ class ConvAgendaItem: AgendaItem isReady = (!getActor().conversedThisTurn() && getActor().canTalkTo(otherActor) && inherited()) /* * The actor we're planning to address - by default, this is the PC. * If the conversational overture will be directed to another NPC, * you can specify that other actor here. */ otherActor = (gPlayerChar) ; /* * A delayed agenda item. This type of item becomes ready to execute * when the game clock reaches a given turn counter. */ class DelayedAgendaItem: AgendaItem /* we're ready if the game clock time has reached our ready time */ isReady = (Schedulable.gameClockTime >= readyTime && inherited()) /* the turn counter on the game clock when we become ready */ readyTime = 0 /* * Set our ready time based on a delay from the current time. We'll * become ready after the given number of turns elapses. For * convenience, we return 'self', so a delayed agenda item can be * initialized and added to an actor's agenda in one simple * operation, like so: * * actor.addToAgenda(item.setDelay(1)); */ setDelay(turns) { /* * initialize our ready time as the given number of turns in the * future from the current game clock time */ readyTime = Schedulable.gameClockTime + turns; /* return 'self' for the caller's convenience */ return self; } ; /* ------------------------------------------------------------------------ */ /* * An Actor is a living person, animal, or other entity with a will of * its own. Actors can usually be addressed with targeted commands * ("bob, go north"), and with commands like ASK ABOUT, TELL ABOUT, GIVE * TO, and SHOW TO. * * Note that, by default, an Actor can be picked up and moved with * commands like TAKE, PUT IN, and so on. This is suitable for some * kinds of actors but not for others: it might make sense with a cat or * a small dog, but not with a bank guard or an orc. For an actor that * can't be taken, use the UntakeableActor or one of its subclasses. * * An actor's contents are the things the actor is carrying or wearing. */ class Actor: Thing, Schedulable, Traveler, ActorTopicDatabase /* flag: we're an actor */ isActor = true /* * Our current state. This is an ActorState object representing what * we're currently doing. Whenever the actor changes to a new state * (for example, because of a scripted activity), this can be changed * to reflect the actor's new state. The state object groups the * parts of the actor's description and other methods that tend to * vary according to what the actor's doing; it's easier to keep * everything related to scripted activities together in a state * object than it is to handle all of the variability with switch() * statements of the like in methods directly in the actor. * * It's not necessary to initialize this if the actor doesn't take * advantage of the ActorState mechanism. If this isn't initialized * for a particular actor, we'll automatically create a default * ActorState object during pre-initialization. */ curState = nil /* set the current state */ setCurState(state) { /* if this isn't a change of state, there's nothing to do */ if (state == curState) return; /* if we have a previous state, tell it it's becoming inactive */ if (curState != nil) curState.deactivateState(self, state); /* notify the new state it's becoming active */ if (state != nil) state.activateState(self, curState); /* remember the new state */ curState = state; } /* * Our current conversation node. This is a ConvNode object that * keeps track of the flow of the conversation. */ curConvNode = nil /* * Our table of conversation nodes. At initialization, the * conversation manager scans all ConvNode instances and adds each * one to its actor's table. This table is keyed by the name of * node, and the value for each entry is the ConvNode object - this * lets us look up the ConvNode object by name. Because each actor * has its own lookup table, ConvNode names only have to be unique * within the actor's set of ConvNodes. */ convNodeTab = perInstance(new LookupTable(32, 32)) /* set the current conversation node */ setConvNode(node) { setConvNodeReason(node, nil); } /* set the current conversation node, with a reason code */ setConvNodeReason(node, reason) { /* remember the old node */ local oldNode = curConvNode; /* if the node was specified by name, look up the object */ if (dataType(node) == TypeSString) node = convNodeTab[node]; /* remember the new node */ curConvNode = node; /* * If we're changing to a new node, notify the new and old * nodes. Note that these notifications occur after the new * node has been set, which ensures that any further node change * triggered by the node change won't redundantly issue the same * notifications: since the old node is no longer active, it * can't receive another departure notification, and since the * new node is already active, it can't receive another * activation. */ if (node != oldNode) { /* if there's an old node, note that we're leaving it */ if (oldNode != nil) oldNode.noteLeaving(); /* let the node know that it's becoming active */ if (node != nil) node.noteActiveReason(reason); } /* * note that we've explicitly set a ConvNode (even if it's not * actually changing), in case the conversation manager is * tracking what's happening during a response */ responseSetConvNode = true; } /* * conversation manager ID - this is assigned by the conversation * manager to map to and from output stream references to the actor; * this is only for internal use by the conversation manager */ convMgrID = nil /* * Flag indicating whether or not we've set a ConvNode in the course * of the current response. This is for use by the converstaion * manager. */ responseSetConvNode = nil /* * Initiate a conversation with the player character. This lets the * NPC initiate a conversation, in response to something the player * character does, or as part of the NPC's scripted activity. This * is only be used for situations where the NPC initiates the * conversation - if the player character initiates conversation with * TALK TO, ASK, TELL, etc., we handle the conversation through our * normal handlers for those commands. * * 'state' is the ActorState to switch to for the conversation. This * will normally be an InConversationState object, but doesn't have * to be. * * You can pass nil for 'state' to use the current state's implied * conversational state. The implied conversational state of a * ConversationReadyState is the associated InConversationState; the * implied conversation state of any other state is simply the same * state. * * 'node' is a ConvNode object, or a string naming a ConvNode object. * We'll make this our current conversation node. A valid * conversation node is required because we use this to generate the * initial NPC greeting of the conversation. In most cases, when the * NPC initiates a conversation, it's because the NPC wants to ask a * question or otherwise say something specific, so there should * always be a conversational context implied, thus the need for a * ConvNode. If there's no need for a conversational context, the * NPC script code might just as well display the conversational * exchange as a plain old message, and not bother going to all this * trouble. */ initiateConversation(state, node) { /* * if there's no state provided, use the current state's implied * conversation state */ if (state == nil) state = curState.getImpliedConvState; /* * if there's an ActorHelloTopic for the old state, invoke it to * show the greeting */ curState.handleTopic(self, actorHelloTopicObj, helloConvType, nil); /* switch to the new state, if it's not the current state */ if (state != nil && state != curState) setCurState(state); /* we're now talking to the player character */ noteConversation(gPlayerChar); /* switch to the conversation node */ setConvNodeReason(node, 'initiateConversation'); /* tell the conversation node that the NPC is initiating it */ if (node != nil) curConvNode.npcInitiateConversation(); } /* * Initiate a conversation based on the given simulation object. * We'll look for an InitiateTopic matching the given object, and if * we can find one, we'll show its topic response. */ initiateTopic(obj) { /* try our current state first */ if (curState.initiateTopic(obj)) return true; /* we didn't find a state object; use the default handling */ return inherited(obj); } /* * Schedule initiation of conversation. This allows the caller to * set up a conversation to start on a future turn. The * conversation will start after (1) the given number of turns has * elapsed, and (2) the player didn't target this actor with a * conversational command on the same turn. This allows us to set * the NPC so that it *wants* to start a conversation, and will do * so as soon as it has a chance to get a word in. * * If 'turns' is zero, the conversation can start the next time the * actor takes a turn; so, if this is called during the PC's action * processing, the conversation can start on the same turn. Note * that if this is called during the actor's takeTurn() processing, * it won't actually start the conversation until the next turn, * because that's the next time we'll check the queue. If 'turns' * is 1, then the player will get at least one more command before * the conversation will begin, and so on with higher numbers. */ scheduleInitiateConversation(state, node, turns) { /* add a new pending conversation to our list */ pendingConv.append(new PendingConvInfo(state, node, turns)); } /* * Break off our current conversation, of the NPC's own volition. * This is the opposite number of initiateConversation: this causes * the NPC to effectively say BYE on its own, rather than waiting * for the PC to decide to end the conversation. * * This call is mostly useful when the actor's current state is an * InConversationState, since the main function of this routine is * to switch to an out-of-conversation state. */ endConversation() { /* * tell the current state to end the conversation of the NPC's * own volition */ curState.endConversation(self, endConvActor); } /* * Our list of pending conversation initiators. In our takeTurn() * processing, we'll check this list for conversations that we can * initiate. */ pendingConv = nil /* * Hide actors from 'all' by default. The kinds of actions that * normally apply to 'all' and the kinds that normally apply to * actors have pretty low overlap. * * If a particular actor looks a lot like an inanimate object, it * might want to override this to participate in 'all' for most or * all actions. */ hideFromAll(action) { return true; } /* * don't hide actors from defaulting, though - it's frequently * convenient and appropriate to assume an actor by default, * especially for commands like GIVE TO and SHOW TO */ hideFromDefault(action) { return nil; } /* * We meet the objHeld precondition for ourself - that is, for any * verb that requires holding an object, we can be considered to be * holding ourself. */ meetsObjHeld(actor) { return actor == self || inherited(actor); } /* * Actors are not listed with the ordinary objects in a room's * description. However, an actor is listed as part of an inventory * description. */ isListed = nil isListedInContents = nil isListedInInventory = true /* the contents of an actor aren't listed in a room's description */ contentsListed = nil /* * Full description. By default, we'll show either the pcDesc or * npcDesc, depending on whether we're the current player character * or a non-player character. * * Generally, individual actors should NOT override this method. * Instead, customize pcDesc and/or npcDesc to describe the permanent * features of the actor. */ desc { /* * show the appropriate messages, depending on whether we're the * player character or a non-player character */ if (isPlayerChar()) { /* show our as-player-character description */ pcDesc; } else { /* show our as-non-player-character description */ npcDesc; } } /* show our status */ examineStatus() { /* * If I'm an NPC, show where I'm sitting/standing/etc. (If I'm * the PC, we don't usually want to show this explicitly to avoid * redundancy. The player is usually sufficiently aware of the * PC's posture by virtue of being in control of the actor, and * the information also tends to show up often enough in other * places, such as on the status line and in the room * description.) */ if (!isPlayerChar()) postureDesc; /* show the status from our state object */ curState.stateDesc; /* inherit the default handling to show our contents */ inherited(); } /* * Show my posture, as part of the full EXAMINE description of this * actor. We'll let our nominal actor container handle it. */ postureDesc() { descViaActorContainer(&roomActorPostureDesc, nil); } /* * The default description when we examine this actor and the actor * is serving as the player character. This should generally not * include any temporary status information; just show constant, * fixed features. */ pcDesc { gLibMessages.pcDesc(self); } /* * Show the description of this actor when this actor is a non-player * character. * * This description should include only the constant, fixed * description of the character. Do not include information on what * the actor is doing right now, because that belongs in the * ActorState object instead. When we display the actor's * description, we'll show this text, and then we'll show the * ActorState description as well; this combination approach makes it * easier to keep the description synchronized with any scripted * activities the actor is performing. * * By default, we'll show this as a "default descriptive report," * since it simply says that there's nothing special to say. * However, whenever this is overridden with an actual description, * you shouldn't bother to use defaultDescReport - simply display the * descriptive message directly: * * npcDesc = "He's wearing a gorilla costume. " */ npcDesc { defaultDescReport(&npcDescMsg, self); } /* examine my contents specially */ examineListContents() { /* if I'm not the player character, show my inventory */ if (!isPlayerChar()) holdingDesc; } /* * Always list actors specially, rather than as ordinary items in * contents listings. We'll send this to our current state object * for processing, since our "I am here" description tends to vary by * state. */ specialDesc() { curState.specialDesc(); } distantSpecialDesc() { curState.distantSpecialDesc(); } remoteSpecialDesc(actor) { curState.remoteSpecialDesc(actor); } specialDescListWith() { return curState.specialDescListWith(); } /* * By default, show the special description for an actor in the group * of special descriptions that come *after* the room's portable * contents listing. An actor's presence is usually a dynamic * feature of a room, and so we don't want to suggest that the actor * is a permanent feature of the room by describing the actor * directly with the room's main description. */ specialDescBeforeContents = nil /* * When we're asked to show a special description as part of the * description of a containing object (which will usually be a nested * room of some kind), just show our posture in our container, rather * than showing our full "I am here" description. */ showSpecialDescInContents(actor, cont) { /* show our posture to indicate our container */ listActorPosture(actor); } /* * By default, put all of the actor special descriptions after the * special descriptions of ordinary objects, by giving actors a * higher listing order value. */ specialDescOrder = 200 /* * Get my listing group for my special description as part of a room * description. By default, we'll let our immediate location decide * how we're grouped. */ actorListWith() { local group; /* * if our special desc is overridden, don't use any grouping by * default - this make a special description defined in the * actor override any grouping we'd otherwise do */ if (overrides(self, Actor, &specialDesc)) return []; /* get the group for the posture */ group = location.listWithActorIn(posture); /* * if we have a group, return a list containing the group; * otherwise return an empty list */ return (group == nil ? [] : [group]); } /* * Actor "I am here" description. This is displayed as part of the * description of a room - it describes the actor as being present in * the room. By default, we let the "nominal actor container" * provide the description. */ actorHereDesc { descViaActorContainer(&roomActorHereDesc, nil); } /* * Actor's "I am over there" description. This is displayed in the * room description when the actor is visible, but is either in a * separate top-level room or is at a distance. By default, we let * the "nominal actor container" provide the description. */ actorThereDesc { descViaActorContainer(&roomActorThereDesc, nil); } /* * Show our status, as an addendum to the given room's name (this is * the room title, shown at the start of a room description and on * the status line). By default, we'll let our nominal actor * container provide the status, to indicate when we're * standing/sitting/lying in a nested room. * * In concrete terms, this generally adds a message such as "(sitting * on the chair)" to the name of a room if we're in a nested room * within the room. When we're standing in the main room, this * generally adds nothing. * * Note that we pass the room we're describing as the "container to * ignore" parameter, because we don't want to say something like * "Phone Booth (standing in the phone booth)" - that is, we don't * want to mention the nominal container again if the nominal * container is what we're naming in the first place. */ actorRoomNameStatus(room) { descViaActorContainer(&roomActorStatus, room); } /* * Describe the actor via the "nominal actor container." The nominal * container is determined by our direct location. * * 'contToIgnore' is a container to ignore. If our nominal container * is the same as this object, we'll generate a description without a * mention of a container at all. * * The reason we have the 'contToIgnore' parameter is that the caller * might already have reported our general location, and now merely * wants to add that we're standing or standing or whatever. In * these cases, if we were to say that we're sitting on or standing * on that same object, it would be redundant information: "Bob is in * the garden, sitting in the garden." The 'contToIgnore' parameter * tells us the object that the caller has already mentioned as our * general location so that we don't re-report the same thing. We * need to know the actual object, rather than just the fact that the * caller mentioned a general location, because our general location * and the specific place we're standing or sitting or whatever might * not be the same: "Bob is in the garden, sitting in the lawn * chair." * */ descViaActorContainer(prop, contToIgnore) { local pov; local cont; /* get our nominal container for our current posture */ cont = location.getNominalActorContainer(posture); /* get the point of view, using the player character by default */ if ((pov = getPOV()) == nil) pov = gPlayerChar; /* * if we have a nominal container, and it's not the one to * ignore, and the player character can see it, generate the * description via the container; otherwise, use a generic * library message that doesn't mention the container */ if (cont not in (nil, contToIgnore) && pov.canSee(cont)) { /* describe via the container */ cont.(prop)(self); } else { /* use the generic library message */ gLibMessages.(prop)(self); } } /* * Describe my inventory as part of my description - this is only * called when we examine an NPC. If an NPC doesn't wish to have * its inventory listed as part of its description, it can simply * override this to do nothing. */ holdingDesc { /* * show our contents as for a normal "examine", but using the * special contents lister for what an actor is holding */ examineListContentsWith(holdingDescInventoryLister); } /* * refer to the player character with my player character referral * person, and refer to all other characters in the third person */ referralPerson { return isPlayerChar() ? pcReferralPerson : ThirdPerson; } /* by default, refer to the player character in the second person */ pcReferralPerson = SecondPerson /* * The referral person of the current command targeting the actor. * This is meaningful only when a command is being directed to this * actor, and this actor is an NPC. * * The referral person depends on the specifics of the language. In * English, a command like "bob, go north" is a second-person * command, while "tell bob to go north" is a third-person command. * The only reason this is important is in interpreting what "you" * means if it's used as an object in the command. "tell bob to hit * you" probably means that Bob should hit the player character, * while "bob, hit you" probably means that Bob should hit himself. */ commandReferralPerson = nil /* determine if I'm the player character */ isPlayerChar() { return libGlobal.playerChar == self; } /* * Implicit command handling style for this actor. There are two * styles for handling implied commands: "player" and "NPC", * indicated by the enum codes ModePlayer and ModeNPC, respectively. * * In "player" mode, each implied command is announced with a * description of the command to be performed; DEFAULT responses are * suppressed; and failures are shown. Furthermore, interactive * requests for more information from the parser are allowed. * Transcripts like this result: * * >open door *. (first opening the door) *. (first unlocking the door) *. What do you want to unlock it with? * * In "NPC" mode, implied commands are treated as complete and * separate commands. They are not announced; default responses are * shown; failures are NOT shown; and interactive requests for more * information are not allowed. When an implied command fails in NPC * mode, the parser acts as though the command had never been * attempted. * * By default, we return ModePlayer if we're the player character, * ModeNPC if not (thus the respective names of the modes). Some * authors might prefer to use "player mode" for NPC's as well as for * the player character, which is why the various parts of the parser * that care about this mode consult this method rather than simply * testing the PC/NPC status of the actor. */ impliedCommandMode() { return isPlayerChar() ? ModePlayer : ModeNPC; } /* * Try moving the given object into this object. For an actor, this * will do one of two things. If 'self' is the actor performing the * action that's triggering this implied command, then we can achieve * the goal simply by taking the object. Otherwise, the way to get * an object into my possession is to have the actor performing the * command give me the object. */ tryMovingObjInto(obj) { if (gActor == self) { /* * I'm performing the triggering action, so I merely need to * pick up the object */ return tryImplicitAction(Take, obj); } else { /* * another actor is performing the action; since that actor * is the one who must perform the implied action, the way to * get an object into my inventory is for that actor to give * it to me */ return tryImplicitAction(GiveTo, obj, self); } } /* desribe our containment of an object as carrying the object */ mustMoveObjInto(obj) { reportFailure(&mustBeCarryingMsg, obj, self); } /* * You can limit the cumulative amount of bulk an actor can hold, and * the maximum bulk of any one object the actor can hold, using * bulkCapacity and maxSingleBulk. These properties are analogous to * the same ones in Container. * * A word of caution on these is in order. Many authors worry that * it's unrealistic if the player character can carry too much at one * time, so they'll fiddle with these properties to impose a carrying * limit that seems realistic. Be advised that authors love this * sort of "realism" a whole lot more than players do. Players * almost universally don't care about it, and in fact tend to hate * the inventory juggling it inevitably leads to. Juggling inventory * isn't any fun for the player. Don't fool yourself about this - * the thoughts in the mind of a player who's tediously carting * objects back and forth three at a time will not include admiration * of your prowess at simulational realism. In contrast, if you set * the carrying limit to infinity, it's a rare player who will even * notice, and a much rarer player who'll complain about it. * * If you really must insist on inventory limits, refer to the * BagOfHolding class for a solution that can salvage most of the * "realism" that the accountancy-inclined author craves, without * creating undue inconvenience for the player. BagOfHolding makes * inventory limits palatable for the player by essentially * automating the required inventory juggling. In fact, for most * players, an inventory limit in conjunction with a bag of holding * is actually better than an unlimited inventory, since it improves * readability by keeping the direct inventory list to a manageable * size. */ bulkCapacity = 10000 maxSingleBulk = 10 /* * An actor can limit the cumulative amount of weight being held, * using weightCapacity. By default we make this so large that * there is effectively no limit to how much weight an actor can * carry. */ weightCapacity = 10000 /* * Can I own the given object? By default, an actor can own * anything. */ canOwn(obj) { return true; } /* * Get the preconditions for travel. By default, we'll add the * standard preconditions that the connector requires for actors. * * Note that these preconditions apply only when the actor is the * traveler. If the actor is in a vehicle, so that the vehicle is * the traveler in a given travel operation, the vehicle's * travelerPreCond conditions are used instead of ours. */ travelerPreCond(conn) { return conn.actorTravelPreCond(self); } /* by default, actors are listed when they arrive aboard a vehicle */ isListedAboardVehicle = true /* * Get the object that's actually going to move when this actor * travels via the given connector. In most cases this is simply the * actor; but when the actor is in a vehicle, travel commands move * the vehicle, not the actor: the actor stays in the vehicle while * the vehicle moves to a new location. We determine this by asking * our immediate location what it thinks about the situation. * * If we have a special traveler explicitly set, it overrides the * traveler indicated by the location. */ getTraveler(conn) { /* * Return our special traveler if we have one; otherwise, if we * have a location, return the traveler indicated by our * location; otherwise, we're the traveler. */ if (specialTraveler != nil) return specialTraveler; else if (location != nil) return location.getLocTraveler(self, conn); else return self; } /* * Get the "push traveler" for the actor. This is the nominal * traveler that we want to use when the actor enters a command like * PUSH BOX NORTH. 'obj' is the object we're trying to push. */ getPushTraveler(obj) { /* * If we already have a special traveler, just use the special * traveler. Otherwise, if we have a location, ask the location * what it thinks. Otherwise, we're the traveler. */ if (specialTraveler != nil) return specialTraveler; else if (location != nil) return location.getLocPushTraveler(self, obj); else return self; } /* is an actor traveling with us? */ isActorTraveling(actor) { /* we're the only actor traveling when we're the traveler */ return (actor == self); } /* invoke a callback on each actor traveling with the traveler */ forEachTravelingActor(func) { /* we're the only actor, so simply invoke the callback on myself */ (func)(self); } /* * Get the actors involved in travel, when we're acting in our role * as a Traveler. When the Traveler is simply the Actor, the only * actor involved in the travel is 'self'. */ getTravelerActors = [self] /* we're the self-motive actor doing the travel */ getTravelerMotiveActors = [self] /* * Set the "special traveler." When this is set, we explicitly * perform travel through this object rather than through the * traveler indicated by our location. Returns the old value, so * that the old value can be restored when the caller has finished * its need for the special traveler. */ setSpecialTraveler(traveler) { local oldVal; /* remember the old value so that we can return it */ oldVal = specialTraveler; /* remember the new value */ specialTraveler = traveler; /* return the old value */ return oldVal; } /* our special traveler */ specialTraveler = nil /* * Try moving the actor into the given room in preparation for * travel, using pre-condition rules. */ checkMovingTravelerInto(room, allowImplicit) { /* try moving the actor into the room */ return room.checkMovingActorInto(allowImplicit); } /* * Check to ensure the actor is ready to enter the given nested * room, using pre-condition rules. By default, we'll ask the given * nested room to handle it. */ checkReadyToEnterNestedRoom(dest, allowImplicit) { /* ask the destination to do the work */ return dest.checkActorReadyToEnterNestedRoom(allowImplicit); } /* * Travel within a location, as from a room to a contained nested * room. This should generally be used in lieu of travelTo when * traveling between locations that are related directly by * containment rather than with TravelConnector objects. * * Travel within a location is not restricted by darkness; we assume * that if the nested objects are in scope at all, travel among them * is allowed. * * This type of travel does not trigger calls to travelerLeaving() * or travelerArriving(). To mitigate this loss of notification, we * call actorTravelingWithin() on the source and destination * objects. */ travelWithin(dest) { /* if I'm not going anywhere, ignore the operation */ if (dest == location) return; /* * Notify the traveler. Note that since this is local travel * within a single top-level location, there's no connector. */ getTraveler(nil).travelerTravelWithin(self, dest); } /* * Traveler interface: perform local travel, between nested rooms * within a single top-level location. */ travelerTravelWithin(actor, dest) { local origin; /* remember my origin */ origin = location; /* notify the source that we're traveling within a room */ if (origin != nil) origin.actorTravelingWithin(origin, dest); /* * if our origin and destination have different effective follow * locations, track the follow */ if (origin != nil && dest != nil && origin.effectiveFollowLocation != dest.effectiveFollowLocation) { /* * notify observing objects of the travel; we're not moving * along a connector, so there is no connector associated * with the tracking information */ connectionTable().forEachAssoc( {obj, val: obj.beforeTravel(self, nil)}); } /* move me to the destination */ moveInto(dest); /* * recalculate the global sense context for message generation * purposes, since we've moved to a new location */ if (gAction != nil) gAction.recalcSenseContext(); /* notify the destination of the interior travel */ if (dest != nil) dest.actorTravelingWithin(origin, dest); } /* * Check for travel in the dark. If we're in a dark room, and our * destination is a dark room, ask the connector for guidance. * * Travel connectors normally call this before invoking our * travelTo() method to carry out the travel. The darkness check * usually must be made before any barrier checks. */ checkDarkTravel(dest, connector) { local origin; /* * If we're not in the dark in the current location, there's no * need to check for dark-to-dark travel; light-to-dark travel * is always allowed. */ if (isLocationLit()) return; /* get the origin - this is the traveler's location */ origin = getTraveler(connector).location; /* * Check to see if the connector itself is visible in the dark. * If it is, then allow the travel without restriction. */ if (connector.isConnectorVisibleInDark(origin, self)) return; /* * We are attempting dark-to-dark travel. We allow or disallow * this type of travel on a per-connector basis, so ask the * connector to handle it. If the connector wishes to disallow * the travel, it will display an appropriate failure report and * terminate the command with 'exit'. */ connector.darkTravel(self, dest); } /* * Travel to a new location. */ travelTo(dest, connector, backConnector) { /* send the request to the traveler */ getTraveler(connector) .travelerTravelTo(dest, connector, backConnector); } /* * Perform scripted travel to the given adjacent location. This * looks for a directional connector in our current location whose * destination is the given location, and for a corresponding * back-connector in the destination location. If we can find the * connectors, we'll perform the travel using travelTo(). * * The purpose of this routine is to simplify scripted travel for * simple cases where directional connectors are available for the * desired travel. This routine is NOT suitable for intelligent * goal-seeking NPC's who automatically try to find their own routes, * for two reasons. First, this routine only lets an NPC move to an * *adjacent* location; it won't try to find a path between arbitrary * locations. Second, this routine is "omniscient": it doesn't take * into account what the NPC knows about the connections between * locations, but simply finds a connector that actually provides the * desired travel. * * What this routine *is* suitable for are cases where we have a * pre-scripted series of NPC travel actions, where we have a list of * rooms we want the NPC to visit in order. This routine simplifies * this type of scripting by automatically finding the connectors; * the script only has to specify the next location for the NPC to * visit. */ scriptedTravelTo(dest) { local conn; /* find a connector from the current location to the new location */ conn = location.getConnectorTo(self, dest); /* if we found the connector, perform the travel */ if (conn != nil) nestedActorAction(self, TravelVia, conn); } /* * Remember the last door I traveled through. We use this * information for disambiguation, to boost the likelihood that an * actor that just traveled through a door is referring to the same * door in a subsequent "close" command. */ rememberLastDoor(obj) { lastDoorTraversed = obj; } /* * Remember our most recent travel. If we know the back connector * (i.e., the connector that reverses the travel we're performing), * then we'll be able to accept a GO BACK command to attempt to * return to the previous location. */ rememberTravel(origin, dest, backConnector) { /* remember the destination of the travel, and the connector back */ lastTravelDest = dest; lastTravelBack = backConnector; } /* * Reverse the most recent travel. If we're still within the same * destination we reached in the last travel, and we know the * connector we arrived through (i.e., the "back connector" for the * last travel, which reverses the connector we took to get here), * then try traveling via the connector. */ reverseLastTravel() { /* * If we don't know the connector back to our previous location, * we obviously can't reverse the travel. If we're not still in * the same location as the previous travel's destination, then * we can't reverse the travel either, because the back * connector isn't applicable to our current location. (This * latter condition could only happen if we've been moved * somewhere without ordinary travel occurring, but this is a * possibility.) */ if (lastTravelBack == nil || lastTravelDest == nil || !isIn(lastTravelDest)) { reportFailure(&cannotGoBackMsg); exit; } /* attempt travel via our back connector */ nestedAction(TravelVia, lastTravelBack); } /* the last door I traversed */ lastDoorTraversed = nil /* the destination and back connector for our last travel */ lastTravelDest = nil lastTravelBack = nil /* * use a custom message for cases where we're holding a destination * object for BOARD, ENTER, etc */ checkStagingLocation(dest) { /* * if the destination is within us, explain specifically that * this is the problem */ if (dest.isIn(self)) reportFailure(&invalidStagingContainerActorMsg, self, dest); else inherited(dest); /* terminate the command */ exit; } /* * Travel arrival/departure messages. Defer to the current state * object on all of these. */ sayArriving(conn) { curState.sayArriving(conn); } sayDeparting(conn) { curState.sayDeparting(conn); } sayArrivingLocally(dest, conn) { curState.sayArrivingLocally(dest, conn); } sayDepartingLocally(dest, conn) { curState.sayDepartingLocally(dest, conn); } sayTravelingRemotely(dest, conn) { curState.sayTravelingRemotely(dest, conn); } sayArrivingDir(dir, conn) { curState.sayArrivingDir(dir, conn); } sayDepartingDir(dir, conn) { curState.sayDepartingDir(dir, conn); } sayArrivingThroughPassage(conn) { curState.sayArrivingThroughPassage(conn); } sayDepartingThroughPassage(conn) { curState.sayDepartingThroughPassage(conn); } sayArrivingViaPath(conn) { curState.sayArrivingViaPath(conn); } sayDepartingViaPath(conn) { curState.sayDepartingViaPath(conn); } sayArrivingUpStairs(conn) { curState.sayArrivingUpStairs(conn); } sayArrivingDownStairs(conn) { curState.sayArrivingDownStairs(conn); } sayDepartingUpStairs(conn) { curState.sayDepartingUpStairs(conn); } sayDepartingDownStairs(conn) { curState.sayDepartingDownStairs(conn); } /* * Get the current interlocutor. By default, we'll address new * conversational commands (ASK ABOUT, TELL ABOUT, SHOW TO) to the * last conversational partner, if that actor is still within range. */ getCurrentInterlocutor() { /* * if we've talked to someone before, and we can still talk to * them now, return that actor; otherwise we have no default */ if (lastInterlocutor != nil && canTalkTo(lastInterlocutor)) return lastInterlocutor; else return nil; } /* * Get the default interlocutor. If there's a current interlocutor, * and we can still talk to that actor, then that's the default * interlocutor. If not, we'll return whatever actor is the default * for a TALK TO command. Note that TALK TO won't necessarily have a * default actor; if it doesn't, we'll simply return nil. */ getDefaultInterlocutor() { local actor; /* check for a current interlocutor */ actor = getCurrentInterlocutor(); /* * if we're not talking to anyone, or if the person we were * talking to can no longer hear us, look for a default object * for a TALK TO command and use it instead as the default */ if (actor == nil || !canTalkTo(actor)) { /* set up a TALK TO command and a resolver */ local tt = new TalkToAction(); local res = new Resolver(tt, gIssuingActor, gActor); /* get the default direct object */ actor = tt.getDefaultDobj(new EmptyNounPhraseProd(), res); /* if that worked, get the object from the resolve info */ if (actor != nil) actor = actor[1].obj_; } /* return what we found */ return actor; } /* * The most recent actor that we've interacted with through a * conversational command (ASK, TELL, GIVE, SHOW, etc). */ lastInterlocutor = nil /* * Our conversational "boredom" counter. While we're in a * conversation, this tracks the number of turns since the last * conversational command from the actor we're talking to. * * Note that this state is part of the actor, even though it's * usually managed by the InConversationState object. The state is * stored with the actor rather than with the state object because * it really describes the condition of the actor, not of the state * object. */ boredomCount = 0 /* * game-clock time (Schedulable.gameClockTime) of the last * conversational command addressed to us by the player character */ lastConvTime = -1 /* * Did we engage in any conversation on the current turn? This can * be used as a quick check in background activity scripts when we * want to run a step only in the absence of any conversation on the * same turn. */ conversedThisTurn() { return lastConvTime == Schedulable.gameClockTime; } /* * Note that we're performing a conversational command targeting the * given actor. We'll make the actors point at each other with their * 'lastInterlocutor' properties. This is called on the character * performing the conversation command: if the player types ASK BOB * ABOUT BOOK, this will be called on the player character actor, * with 'other' set to Bob. */ noteConversation(other) { /* note that we're part of a conversational action */ noteConvAction(other); /* let the other actor know we're conversing with them */ other.noteConversationFrom(self); } /* * Note that another actor is issuing a conversational command * targeting us. For example, if the player types ASK BOB ABOUT * BOOK, then this will be called on Bob, with the player character * actor as 'other'. */ noteConversationFrom(other) { /* note that we're part of a conversational action */ noteConvAction(other); } /* * Note that we're taking part in a conversational action with * another character. This is symmetrical - it could mean we're the * initiator of the conversation action or the target. We'll * remember the person we're talking to, and reset our conversation * time counters so we know we've conversed on this turn. */ noteConvAction(other) { /* note our last conversational partner */ lastInterlocutor = other; /* set the actor to be the pronoun antecedent */ setPronounObj(other); /* * reset our boredom counter, as the other actor has just spoken * to us */ boredomCount = 0; /* remember the time of our last conversation from the PC */ lastConvTime = Schedulable.gameClockTime; } /* note that we're consulting an item */ noteConsultation(obj) { lastConsulted = obj; } /* * Receive notification that a TopicEntry response in our database is * being invoked. We'll just pass this along to our current state. */ notifyTopicResponse(fromActor, entry) { /* let our current state handle it */ curState.notifyTopicResponse(fromActor, entry); } /* the object we most recently consulted */ lastConsulted = nil /* * The actor's "agenda." This is a list of AgendaItem objects that * describe things the actor wants to do of its own volition on its * own turn. */ agendaList = nil /* * our special "boredom" agenda item - this makes us initiate an end * to an active conversation when the PC has ignored us for a given * number of consecutive turns */ boredomAgendaItem = perInstance(new BoredomAgendaItem(self)) /* add an agenda item */ addToAgenda(item) { /* if we don't have an agenda list yet, create one */ if (agendaList == nil) agendaList = new Vector(10); /* add the item */ agendaList.append(item); /* * keep the list in ascending order of agendaOrder values - this * will ensure that we'll always choose the earliest item that's * ready to run */ agendaList.sort(SortAsc, {a, b: a.agendaOrder - b.agendaOrder}); /* reset the agenda item */ item.resetItem(); } /* remove an agenda item */ removeFromAgenda(item) { /* if we have an agenda list, remove the item */ if (agendaList != nil) agendaList.removeElement(item); } /* * Execute the next item in our agenda, if there are any items in the * agenda that are ready to execute. We'll return true if we found * an item to execute, nil if not. */ executeAgenda() { local item; /* if we don't have an agenda, there are obviously no items */ if (agendaList == nil) return nil; /* remove any items that are marked as done */ while ((item = agendaList.lastValWhich({x: x.isDone})) != nil) agendaList.removeElement(item); /* * Scan for an item that's ready to execute. Since we keep the * list sorted in ascending order of agendaOrder values, we can * just pick the earliest item in the list that's ready to run, * since that will be the ready-to-run item with the lowest * agendaOrder number. */ item = agendaList.valWhich({x: x.isReady}); /* if we found an item, execute it */ if (item != nil) { try { /* execute the item */ item.invokeItem(); } catch (RuntimeError err) { /* * If an error occurs while executing the item, mark the * item as done. This will ensure that we won't get * stuck in a loop trying to execute the same item over * and over, which will probably just run into the same * error on each attempt. */ item.isDone = true; /* re-throw the exception */ throw err; } /* tell the caller we found an item to execute */ return true; } else { /* tell the caller we found no agenda item */ return nil; } } /* * Calculate the amount of bulk I'm holding directly. By default, * we'll simply add up the "actor-encumbering bulk" of each of our * direct contents. * * Note that we don't differentiate here based on whether or not an * item is being worn, or anything else - we deliberately leave such * distinctions up to the getEncumberingBulk routine, so that only * the objects are in the business of deciding how bulky they are * under different circumstances. */ getBulkHeld() { local total; /* start with nothing */ total = 0; /* add the bulks of directly-contained items */ foreach (local cur in contents) total += cur.getEncumberingBulk(self); /* return the total */ return total; } /* * Calculate the total weight I'm holding. By default, we'll add up * the "actor-encumbering weight" of each of our direct contents. * * Note that we deliberately only consider our direct contents. If * any of the items we are directly holding contain further items, * getEncumberingWeight will take their weights into account; this * frees us from needing any special knowledge of the internal * structure of any items we're holding, and puts that knowledge in * the individual items where it belongs. */ getWeightHeld() { local total; /* start with nothing */ total = 0; /* add the weights of directly-contained items */ foreach (local cur in contents) total += cur.getEncumberingWeight(self); /* return the total */ return total; } /* * Try making room to hold the given object. This is called when * checking the "room to hold object" pre-condition, such as for the * "take" verb. * * If holding the new object would exceed the our maximum holding * capacity, we'll go through our inventory looking for objects that * can reduce our held bulk with implicit commands. Objects with * holding affinities - "bags of holding", keyrings, and the like - * can implicitly shuffle the actor's possessions in a manner that * is neutral as far as the actor is concerned, thereby reducing our * active holding load. * * Returns true if an implicit command was attempted, nil if not. */ tryMakingRoomToHold(obj, allowImplicit) { local objWeight; local objBulk; local aff; /* get the amount of weight this will add if taken */ objWeight = obj.getEncumberingWeight(self); /* * If this object alone is too heavy for us, give up. We * distinguish this case from the case where the total (of * everything held plus the new item) is too heavy: in the * latter case we can tell the actor that they can pick this up * by dropping something else first, whereas if this item alone * is too heavy, no such advice is warranted. */ if (objWeight > weightCapacity) { reportFailure(&tooHeavyForActorMsg, obj); exit; } /* * if taking the object would push our total carried weight over * our total carrying weight limit, give up */ if (obj.whatIfHeldBy({: getWeightHeld()}, self) > weightCapacity) { reportFailure(&totalTooHeavyForMsg, obj); exit; } /* get the amount of bulk the object will add */ objBulk = obj.getEncumberingBulk(self); /* * if the object is simply too big to start with, we can't make * room no matter what we do */ if (objBulk > maxSingleBulk || objBulk > bulkCapacity) { reportFailure(&tooLargeForActorMsg, obj); exit; } /* * Test what would happen to our bulk if we were to move the * object into our directly held inventory. Do this by running * a "what if" scenario to test moving the object into our * inventory, and check what effect it has on our held bulk. If * it fits, we can let the caller proceed without further work. */ if (obj.whatIfHeldBy({: getBulkHeld()}, self) <= bulkCapacity) return nil; /* * if we're not allowed to run implicit commands, we won't be * able to accomplish anything, so give up */ if (!allowImplicit) { reportFailure(&handsTooFullForMsg, obj); exit; } /* * Get "bag of holding" affinity information for my immediate * contents. Consider only objects with encumbering bulk, since * it will do us no good to move objects without any encumbering * bulk. Also ignore objects that aren't being held (some direct * contents aren't considered to be held, such as clothing being * worn). */ aff = getBagAffinities(contents.subset( {x: x.getEncumberingBulk(self) != 0 && x.isHeldBy(self)})); /* if there are no bag affinities, we can't move anything around */ if (aff.length() == 0) { reportFailure(&handsTooFullForMsg, obj); exit; } /* * If we have at least four items, find the two that were picked * up most recently (according to the "holding index" value) and * move them to the end of the list. In most cases, we'll only * have to dispose of one or two items to free up enough space * in our hands, so we'll probably never get to the last couple * of items in our list, so we're effectively ruling out moving * these two most recent items; but they'll be in the list if we * do find we need to move them after all. * * The point of this rearrangement is to avoid annoying cases of * moving something we just picked up, especially if we just * picked it up in order to carry out the command that's making * us free up more space now. This looks especially stupid when * we perform some command that requires picking up two items * automatically: we pick up the first, then we put it away in * order to pick up the second, but then we find that we need * the first again. */ if (aff.length() >= 4) { local a, b; /* remove the two most recent items from the vector */ a = BagAffinityInfo.removeMostRecent(aff); b = BagAffinityInfo.removeMostRecent(aff); /* re-insert them at the end of the vector */ aff.append(b); aff.append(a); } /* * Move each object in the list until we have reduced the bulk * sufficiently. */ foreach (local cur in aff) { /* * Try moving this object to its bag. If the bag is itself * inside this object, don't even try, since that would be an * attempt at circular containment. * * If the object we're trying to hold is inside this object, * don't move the object. That might put the object we're * trying to hold out of reach, since moving an object into a * bag could involve closing the object or making its * contents not directly accessible. */ if (!cur.bag_.isIn(cur.obj_) && !obj.isIn(cur.obj_) && cur.bag_.tryPuttingObjInBag(cur.obj_)) { /* * this routine tried tried to move the object into the * bag - check our held bulk to see if we're in good * enough shape yet */ if (obj.whatIfHeldBy({: getBulkHeld()}, self) <= bulkCapacity) { /* * We've met our condition - there's no need to look * any further. Return, telling the caller we've * performed an implicit command. */ return true; } } } /* * If we get this far, it means that we tried every child object * but failed to find anything that could help. Explain the * problem and abort the command. */ reportFailure(&handsTooFullForMsg, obj); exit; } /* * Check a bulk change of one of my direct contents. */ checkBulkChangeWithin(obj) { local objBulk; /* get the object's new bulk */ objBulk = obj.getEncumberingBulk(self); /* * if this change would cause the object to exceed our * single-item bulk limit, don't allow it */ if (objBulk > maxSingleBulk || objBulk > bulkCapacity) { reportFailure(&becomingTooLargeForActorMsg, obj); exit; } /* * If our total carrying capacity is exceeded with this change, * don't allow it. Note that 'obj' is already among our * contents when this routine is called, so we can simply check * our current total bulk within. */ if (getBulkHeld() > bulkCapacity) { reportFailure(&handsBecomingTooFullForMsg, obj); exit; } } /* * Next available "holding index" value. Each time we pick up an * item, we'll assign it our current holding index value and then * increment our value. This gives us a simple way to keep track of * the order in which we picked up items we're carrying. * * Note that we make the simplifying assumption that an object can * be held by only one actor at a time (multi-location items are * generally not portable), which means that we can use a simple * property in each object being held to store its holding index. */ nextHoldingIndex = 1 /* add an object to my contents */ addToContents(obj) { /* assign the new object our next holding index */ obj.holdingIndex = nextHoldingIndex++; /* inherit default handling */ inherited(obj); } /* * Go to sleep. This is used by the 'Sleep' action to carry out the * command. By default, we simply say that we're not sleepy; actors * can override this to cause other actions. */ goToSleep() { /* simply report that we can't sleep now */ mainReport(&cannotSleepMsg); } /* * My current "posture," which specifies how we're positioned with * respect to our container; this is one of the standard library * posture enum values (Standing, etc.) or another posture added by * the game. */ posture = standing /* * Get a default acknowledgment of a change to our posture. This * should acknowledge the posture so that it tells us the current * posture. This is used for a command such as "stand up" from a * chair, so that we can report the appropriate posture status in * our acknowledgment; we might end up being inside another nested * container after standing up from the chair, so we might not * simply be standing when we're done. */ okayPostureChange() { /* get our nominal container for our current posture */ local cont = location.getNominalActorContainer(posture); /* if the container is visible, let it handle it */ if (cont != nil && gPlayerChar.canSee(cont)) { /* describe via the container */ cont.roomOkayPostureChange(self); } else { /* use the generic library message */ defaultReport(&okayPostureChangeMsg, posture); } } /* * Describe the actor as part of the EXAMINE description of a nested * room containing the actor. 'povActor' is the actor doing the * looking. */ listActorPosture(povActor) { /* get our nominal container for our current posture */ local cont = location.getNominalActorContainer(posture); /* if the container is visible, let it handle it */ if (cont != nil && povActor.canSee(cont)) cont.roomListActorPosture(self); } /* * Stand up. This is used by the 'Stand' action to carry out the * command. */ standUp() { /* if we're already standing, say so */ if (posture == standing) { reportFailure(&alreadyStandingMsg); return; } /* ask the location to make us stand up */ location.makeStandingUp(); } /* * Disembark. This is used by the 'Get out' action to carry out the * command. By default, we'll let the room handle it. */ disembark() { /* let the room handle it */ location.disembarkRoom(); } /* * Set our posture to the given status. By default, we'll simply * set our posture property to the new status, but actors can * override this to handle side effects of the change. */ makePosture(newPosture) { /* remember our new posture */ posture = newPosture; } /* * Display a description of the actor's location from the actor's * point of view. * * If 'verbose' is true, then we'll show the full description in all * cases. Otherwise, we'll show the full description if the actor * hasn't seen the location before, or the terse description if the * actor has previously seen the location. */ lookAround(verbose) { /* turn on the sense cache while we're looking */ libGlobal.enableSenseCache(); /* show a description of my immediate location, if I have one */ if (location != nil) location.lookAroundPov(self, self, verbose); /* turn off the sense cache now that we're done */ libGlobal.disableSenseCache(); } /* * Get my "look around" location name as a string. This returns a * string containing the location name that we display in the status * line or at the start of a "look around" description of my * location. */ getLookAroundName() { return mainOutputStream.captureOutput( {: location.lookAroundWithinName(self, getVisualAmbient()) }) .specialsToText(); } /* * Adjust a table of visible objects for 'look around'. By default, * we remove any explicitly excluded objects. */ adjustLookAroundTable(tab, pov, actor) { /* remove any explicitly excluded objects */ foreach (local cur in excludeFromLookAroundList) tab.removeElement(cur); /* inherit the base handling */ inherited(tab, pov, actor); } /* * Add an object to the 'look around' exclusion list. Returns true * if the object was already in the list, nil if not. */ excludeFromLookAround(obj) { /* * if the object is already in the list, don't add it again - * just tell the caller it's already there */ if (excludeFromLookAroundList.indexOf(obj) != nil) return true; /* add it to the list and tell the caller it wasn't already there */ excludeFromLookAroundList.append(obj); return nil; } /* remove an object from the 'look around' exclusion list */ unexcludeFromLookAround(obj) { excludeFromLookAroundList.removeElement(obj); } /* * Our list of objects explicitly excluded from 'look around'. These * objects will be suppressed from any sort of listing (including in * the room's contents list and in special descriptions) in 'look * around' when this actor is doing the looking. */ excludeFromLookAroundList = perInstance(new Vector(5)) /* * Get the location into which objects should be moved when the * actor drops them with an explicit 'drop' command. By default, we * return the drop destination of our current container. */ getDropDestination(objToDrop, path) { return (location != nil ? location.getDropDestination(objToDrop, path) : nil); } /* * The senses that determine scope for this actor. An actor might * possess only a subset of the defined sense. * * By default, we give each actor all of the human senses that we * define, except touch. In general, merely being able to touch an * object doesn't put the object in scope, because if an object * isn't noticed through some other sense, touch would only make an * object accessible if it's within arm's reach, which for our * purposes means that the object is being held directly by the * actor. Imagine an actor in a dark room: lots of things might be * touchable in the sense that there's no physical barrier to * touching them, but without some other sense to locate the * objects, the actor wouldn't have any way of knowing where to * reach to touch things, so they're not in scope. So, touch isn't * a scope sense. */ scopeSenses = [sight, sound, smell] /* * "Sight-like" senses: these are the senses that operate like sight * for the actor, and which the actor can use to determine the names * of objects and the spatial relationships between objects. These * senses should operate passively, in the sense that they should * tend to collect sensory input continuously and without explicit * action by the actor, the way sight does and the way touch, for * example, does not. These senses should also operate instantly, * in the sense that the sense can reasonably take in most or all of * a location at one time. * * These senses are used to determine what objects should be listed * in room descriptions, for example. * * By default, the only sight-like sense is sight, since other human * senses don't normally provide a clear picture of the spatial * relationships among objects. (Touch could with some degree of * effort, but it can't operate passively or instantly, since * deliberate and time-consuming action would be necessary.) * * An actor can have more than one sight-like sense, in which case * the senses will act effectively as one sense that can reach the * union of objects reachable through the individual senses. */ sightlikeSenses = [sight] /* * Hearing-like senses. These are senses that the actor can use to * hear objects. */ hearinglikeSenses = [sound] /* * Smell-like senses. These are senses that the actor can use to * smell objects. */ smelllikeSenses = [smell] /* * Communication senses: these are the senses through which the * actor can communicate directly with other actors through commands * and messages. * * Conceptually, these senses are intended to be only those senses * that the actors would *naturally* use to communicate, because * senses in this list allow direct communications via the most * ordinary game commands, such as "bob, go east". * * If some form of indirect communication is possible via a sense, * but that form is not something the actor would think of as the * most natural, default form of communication, it should *not* be * in this list. For example, two sighted persons who can see one * another but cannot hear one another could still communicate by * writing messages on pieces of paper, but they would ordinarily * communicate by talking. In such a case, sound should be in the * list but sight should not be, because sight is not a natural, * default form of communications for the actors. */ communicationSenses = [sound] /* * Determine if I can communicate with the given character via a * natural, default form of communication that we share with the * other character. This determines if I can talk to the other * character. We'll return true if I can talk to the other actor, * nil if not. * * In order for the player character to issue a command to a * non-player character (as in "bob, go east"), the NPC must be able * to sense the PC via at least one communication sense that the two * actors have in common. * * Likewise, in order for a non-player character to say something to * the player, the player must be able to sense the NPC via at least * one communication sense that the two actors have in common. */ canTalkTo(actor) { local common; /* * first, get a list of the communications senses that we have * in common with the other actor - we must have a sense channel * via this sense */ common = communicationSenses.intersect(actor.communicationSenses); /* * if there are no common senses, we can't communicate, * regardless of our physical proximity */ if (common == []) return nil; /* * Determine how well the other actor can sense me in these * senses. Note that all that matters it that the actor can * hear me, because we're determine if I can talk to the other * actor - it doesn't matter if I can hear the other actor. */ foreach (local curSense in common) { local result; /* * determine how well the other actor can sense me in this * sense */ result = actor.senseObj(curSense, self); /* check whether or not this is good enough */ if (actor.canBeTalkedTo(self, curSense, result)) return true; } /* * if we get this far, we didn't find any senses with a clear * enough communications channel - we can't talk to the other * actor */ return nil; } /* * Determine whether or not I can understand an attempt by another * actor to talk to me. 'talker' is the actor doing the talking. * 'sense' is the sense we're testing; this will always be a sense * in our communicationSenses list, and will always be a * communications sense we have in common with the other actor. * 'info' is a SenseInfo object giving information on the clarity of * the sense path to the other actor. * * We return true if we can understand the communication, nil if * not. There is no middle ground where we can partially * understand; we can either understand or not. * * Note that this routine is concerned only with our ability to * sense the communication. The result here should NOT pay any * attention to whether or not we can actually communicate given a * clear sense path - for example, this routine should not reflect * whether or not we have a spoken language in common with the other * actor. * * This is a service method for canTalkTo. This is broken out as a * separate method so that individual actors can override the * necessary conditions for communications in particular senses. */ canBeTalkedTo(talker, sense, info) { /* * By default, we allow communication if the sense path is * transparent or distant. We don't care what the sense is, * since we know we'll never be asked about a sense that's not * in our communicationSenses list. */ return info.trans is in (transparent, distant); } /* * Flag: we wait for commands issued to other actors to complete * before we get another turn. If this is true, then whenever we * issue a command to another actor ("bob, go north"), we will not * get another turn until the other actor has finished executing the * full set of commands we issued. * * By default, this is true, which means that we wait for other * actors to finish all of the commands we issue before we take * another turn. * * If this is set to nil, we'll continue to take turns while the * other actor carries out our commands. In this case, the only * time cost to us of issuing a command is given by orderingTime(), * which normally takes one turn for issuing a command, regardless * of the command's complexity. Some games might wish to use this * mode for interesting effects with NPC's carrying out commands in * parallel with the player, but it's an unconventional style that * some players might find confusing, so we don't use this mode by * default. */ issueCommandsSynchronously = true /* * Flag: the "target actor" of the command line automatically reverts * to this actor at the end of a sentence, when this actor is the * issuer of a command. If this flag is nil, an explicit target * actor stays in effect until the next explicit target actor (or the * end of the entire command line, if no other explicit target actors * are named); if this flag is true, a target actor is in effect only * until the end of a sentence. * * Consider this command line: * * >Bob, go north and get fuel cell. Get log tape. * * If this flag is nil, then the second sentence ("get log tape") is * interpreted as a command to Bob, because Bob is explicitly * designated as the target of the command, and this remains in * effect until the end of the entire command line. * * If this flag is true, on the other hand, then the second sentence * is interpreted as a command to the player character, because the * target actor designation ("Bob,") lasts only until the end of the * sentence. Once a new sentence begins, we revert to the issuing * actor (the player character, since the command came from the * player via the keyboard). */ revertTargetActorAtEndOfSentence = nil /* * The amount of time, in game clock units, it takes me to issue an * order to another actor. By default, it takes one unit (which is * usually equal to one turn) to issue a command to another actor. * However, if we are configured to wait for our issued commands to * complete in full, the ordering time is zero; we don't need any * extra wait time in this case because we'll wait the full length * of the issued command to begin with. */ orderingTime(targetActor) { return issueCommandsSynchronously ? 0 : 1; } /* * Wait for completion of a command that we issued to another actor. * The parser calls this routine after each time we issue a command * to another actor. * * If we're configured to wait for completion of orders given to * other actors before we get another turn, we'll set ourselves up * in waiting mode. Otherwise, we'll do nothing. */ waitForIssuedCommand(targetActor) { /* if we can issue commands asynchronously, there's nothing to do */ if (!issueCommandsSynchronously) return; /* * Add an empty pending command at the end of the target actor's * queue. This command won't do anything when executed; its * purpose is to let us track whether or not the target is still * working on commands we have issued up to this point, which we * can tell by looking to see whether our empty command is still * in the actor's queue. * * Note that we can't simply wait until the actor's queue is * empty, because the actor could acquire new commands while * it's working on our pending commands, and we wouldn't want to * wait for those to finish. Adding a dummy pending command is * a reliable way of tracking the actor's queue, because any * changes to the target actor's command queue will leave our * dummy command in its proper place until the target actor gets * around to executing it, at which point it will be removed. * * Remember the dummy pending command in a property of self, so * that we can check later to determine when the command has * finished. */ waitingForActor = targetActor; waitingForInfo = new PendingCommandMarker(self); targetActor.pendingCommand.append(waitingForInfo); } /* * Synchronous command processing: the target actor and dummy * pending command we're waiting for. When these are non-nil, we * won't take another turn until the given PendingCommandInfo has * been removed from the given target actor's command queue. */ waitingForActor = nil waitingForInfo = nil /* * Add the given actor to the list of actors accompanying my travel * on the current turn. This does NOT set an actor in "follow mode" * or "accompany mode" or anything like that - don't use this to make * an actor follow me around. Instead, this makes the given actor go * with us for the CURRENT travel only - the travel we're already in * the process of performing to process the current TravelVia action. */ addAccompanyingActor(actor) { /* if we don't have the accompanying actor vector yet, create it */ if (accompanyingActors == nil) accompanyingActors = new Vector(8); /* add the actor to my list */ accompanyingActors.append(actor); } /* * My vector of actors who are accompanying me. * * This is for internal bookkeeping only, and it applies to the * current travel only. This is NOT a general "follow mode" setting, * and it shouldn't be used to get me to follow another actor or * another actor to follow me. To make me accompany another actor, * simply override accompanyTravel() so that it returns a suitable * ActorState object. */ accompanyingActors = nil /* * Get the list of objects I can follow. This is a list of all of * the objects which I have seen departing a location - these are * all in scope for 'follow' commands. */ getFollowables() { /* return the list of the objects we know about */ return followables_.mapAll({x: x.obj}); } /* * Do I track departing objects for following the given object? * * By default, the player character tracks everyone, and NPC's track * only the actor they're presently tasked to follow. Most NPC's * will never accept 'follow' commands, so there's no need to track * everyone all the time; for efficiency, we take advantage of this * assumption so that we can avoid storing a bunch of tracking * information that will never be used. */ wantsFollowInfo(obj) { /* * by default, the player character tracks everyone, and NPC's * track only the object (if any) they're currently tasked to * follow */ return isPlayerChar() || followingActor == obj; } /* * Receive notification that an object is leaving its current * location as a result of the action we're currently processing. * Actors (and possibly other objects) will broadcast this * notification to all Actor objects connected in any way by * containment when they move under their own power (such as with * Actor.travelTo) to a new location. We'll keep tracking * information if we are configured to keep tracking information for * the given object and we can see the given object. Note that this * is called when the object is still at the source end of the travel * - the important thing is that we see the object departing. * * 'obj' is the object that is seen to be leaving, and 'conn' is the * TravelConnector it is taking. * * 'conn' is the connector being traversed. If we're simply being * observed in this location (as in a call to setHasSeen), rather * than being observed to leave the location, the connector will be * nil. * * 'from' is the effective starting location of the travel. This * isn't necessarily the departing object's location, since the * departing object could be inside a vehicle or some other kind of * traveler object. * * Note that this notification is sent only to actors with some sort * of containment connection to the object that's moving, because a * containment connection is necessary for there to be a sense * connection. */ trackFollowInfo(obj, conn, from) { local info; /* * If we're not tracking the given object, or we can't see the * given object, ignore the notification. In addition, we * obviously have no need to track ourselves.x */ if (obj == self || !wantsFollowInfo(obj) || !canSee(obj)) return; /* * If we already have a FollowInfo for the given object, re-use * the existing one; otherwise, create a new one and add it to * our tracking list. */ info = followables_.valWhich({x: x.obj == obj}); if (info == nil) { /* we don't have an existing one - create a new one */ info = new FollowInfo(); info.obj = obj; /* add it to our list */ followables_ += info; } /* remember information about the travel */ info.connector = conn; info.sourceLocation = from; } /* * Get information on what to do to make this actor follow the given * object. This returns a FollowInfo object that reports our last * knowledge of the given object's location and departure, or nil if * we don't know anything about how to follow the actor. */ getFollowInfo(obj) { return followables_.valWhich({x: x.obj == obj}); } /* * By default, all actors are followable. */ verifyFollowable() { return true; } /* * Verify a "follow" command being performed by this actor. */ actorVerifyFollow(obj) { /* * check to see if we're in the same effective follow location * as the target; if we are, it makes no sense to follow the * target, since we're already effectively at the same place */ if (obj.location != nil && (location.effectiveFollowLocation == obj.location.effectiveFollowLocation)) { /* * We're in the same location as the target. If we're the * player character, this makes no sense, because the player * character can't go into follow mode (as that would take * away the player's ability to control the player * character). If we're an NPC, though, this simply tells * us to go into follow mode for the target, so there's * nothing wrong with it. */ if (isPlayerChar) { /* * The target is right here, but we're the player * character, so it makes no sense for us to go into * follow mode. If we can see the target, complain that * it's already here; if not, we can only assume it's * here, but we can't know for sure. */ if (canSee(obj)) illogicalNow(&followAlreadyHereMsg); else illogicalNow(&followAlreadyHereInDarkMsg); } } else if (!canSee(obj)) { /* * The target isn't here, and we can't see it from here, so * we must want to follow it to its current location. Get * information on how we will follow the target. If there's * no such information, we obviously can't do any following * because we never saw the target go anywhere in the first * place. */ if (getFollowInfo(obj) == nil) { /* we've never heard of the target */ illogicalNow(&followUnknownMsg); } } } /* * Carry out a "follow" command being performed by this actor. */ actorActionFollow(obj) { local canSeeObj; /* note whether or not we can see the target */ canSeeObj = canSee(obj); /* * If we're not the PC, check to see if this is a follow-mode * request; otherwise, try to go to the location of the target. */ if (!isPlayerChar && canSeeObj) { /* * If we're not already following this actor, acknowledge the * request and go into 'follow' mode. If we're already * following this actor, and we didn't issue the command to * ourself, let them know we're already in the requested * mode. Otherwise, ignore it silently - if we issued the * command to ourself, it's because we're just executing our * own 'follow' mode imperative. */ if (followingActor != obj) { /* let them know we're going to follow the actor now */ reportAfter(&okayFollowModeMsg); /* go into follow mode */ followingActor = obj; } else if (gIssuingActor != self) { /* let them know we're already in follow mode */ reportAfter(&alreadyFollowModeMsg); } /* * if we're already in the target's effective follow * location, that's all we need to do */ if (location.effectiveFollowLocation == obj.location.effectiveFollowLocation) return; } /* * If we can see the target, AND we're in the same top-level * location as the target, then simply use a "local travel" * operation to move into the same location. This only works * with targets that are within the same top-level location, * since that's the whole point of the local-travel routines. * For non-local travel, we need to perform a full-fledged travel * command instead. */ if (canSeeObj && isIn(obj.getOutermostRoom())) { /* * We have no information, so we will only have made it past * verification if we can see the other actor from our * current location. Try moving to the other actor's * effective follow location. */ obj.location.effectiveFollowLocation.checkMovingActorInto(true); /* * Since checkMovingActorInto will do its work through * implicit actions, if we're the player character, then the * entire action will have been performed implicitly, so we * won't have a real report for the series of generated * actions, just implied action announcements. If we're an * NPC, on the other hand, we'll generate the full reports, * since NPC implied actions simply show what the actor is * doing. So, if we're the PC, generate an additional * default acknowledgment of the 'follow' action. */ if (isPlayerChar) defaultReport(&okayFollowInSightMsg, location.effectiveFollowLocation); } else { local info; local srcLoc; /* get the information on how to follow the target */ info = getFollowInfo(obj); /* get the effective follow location we have to be in */ srcLoc = info.sourceLocation.effectiveFollowLocation; /* if there's no connector, we can't go anywhere */ if (info.connector == nil) { /* * We have no departure information, so we can't follow * the actor. If we're currently within sight of the * location where we last saw the actor, it means that we * saw the actor here, then went somewhere else, then * came back, and in our absence the actor itself * departed. In this case, report that we don't know * where the actor went. * * If we're not in sight of the location where we last * saw the actor, then instead remind the player of where * that was. */ if (canSee(srcLoc)) { /* * we're where we last saw the actor, but the actor * must have departed while we were away - so we * simply don't know where the actor went */ reportFailure(&followUnknownMsg); } else { /* * we've gone somewhere else since we last saw the * actor, so remind the player of where it was that * we saw the actor */ reportFailure(&cannotFollowFromHereMsg, srcLoc); } /* in any case, that's all we can do now */ return; } /* * Before we can follow the target, we must be in the same * effective location that the target was in when we * observed the target leaving. */ if (location.effectiveFollowLocation != srcLoc) { /* * If we can't even see the effective follow location, we * must have last observed the other actor traveling from * an unrelated location. In this case, simply say that * we don't know where the followee went. */ if (!canSee(srcLoc)) { reportFailure(&cannotFollowFromHereMsg, srcLoc); return; } /* * Try moving into the same location, by invoking the * pre-condition handler for moving me into the * effective follow location from our memory of the * actor's travel. We *could* run this as an actual * precondition, but it's easier to run it here now that * we've sorted out exactly what we want to do. */ srcLoc.checkMovingActorInto(true); } /* perform a TravelVia action on the connector */ nestedAction(TravelVia, info.connector); } } /* * Our list of followable information. Each entry in this list is a * FollowInfo object that tracks a particular followable. */ followables_ = [] /* determine if I've ever seen the given object */ hasSeen(obj) { return obj.(seenProp); } /* mark the object to remember that I've seen it */ setHasSeen(obj) { obj.noteSeenBy(self, seenProp); } /* receive notification that another actor is observing us */ noteSeenBy(actor, prop) { /* do the standard work to remember that we've been seen */ inherited(actor, prop); /* * Update the follow tracking information with the latest * observed location. We're merely observing the fact that the * actor is here, not that the actor is departing, so the * connector is nil. * * The point of noting the actor's presence in the "follow info" * is that we want to replace any previous memory we have of the * actor departing from another location. Now that we know where * the actor is, any old memory of the actor having left another * location is now irrelevant. We only keep track of one "follow * info" record per actor, so this new record will replace any * older record. */ actor.trackFollowInfo(self, nil, location); } /* * Determine if I know about the given object. I know about an * object if it's specifically marked as known to me; I also know * about the object if I can see it now, or if I've ever seen it in * the past. */ knowsAbout(obj) { return canSee(obj) || hasSeen(obj) || obj.(knownProp); } /* mark the object as known to me */ setKnowsAbout(obj) { obj.(knownProp) = true; } /* * My 'seen' property. By default, this is simply 'seen', which * means that we don't distinguish who's seen what - in other words, * there's a single, global 'seen' flag per object, and if anyone's * ever seen something, then we consider that to mean everyone has * seen it. * * Some games might want to track each NPC's sight memory * separately, or at least they might want to track it individually * for a few specific NPC's. You can do this by making up a new * property name for each NPC whose sight memory you want to keep * separate, and simply setting 'seenProp' to that property name for * each such NPC. For example, for Bob, you could make the property * bobHasSeen, so in Bob you'd define 'sightProp = &bobHasSeen'. */ seenProp = &seen /* * My 'known' property. By default, this is simply 'known', which * means that we don't distinguish who knows what. * * As with 'seenProp' above, if you want to keep track of each NPC's * knowledge separately, you must override this property for each * NPC who's to have its own knowledge base to use a separate * property name. For example, if you want to keep track of what * Bob knows individually, you could define 'knownProp = &bobKnows' * in Bob. */ knownProp = &isKnown /* * Determine if the actor recognizes the given object as a "topic," * which is an object that represents some knowledge the actor can * use in conversations, consultations, and the like. * * By default, we'll recognize any Topic object marked as known, and * we'll recognize any game object for which our knowsAbout(obj) * returns true. Games might wish to override this in some cases to * limit or expand an actor's knowledge according to what the actor * has experienced of the setting or story. Note that it's often * easier to control actor knowledge using the lower-level * knowsAbout() and setKnowsAbout() methods, though. */ knowsTopic(obj) { /* we know the object as a topic if we know about it at all */ return knowsAbout(obj); } /* * Determine if the given object is a likely topic for a * conversational action performed by this actor. By default, we'll * return true if the topic is known, nil if not. */ isLikelyTopic(obj) { /* if the object is known, it's a possible topic */ return knowsTopic(obj); } /* we are the owner of any TopicEntry objects contained within us */ getTopicOwner() { return self; } /* * Suggest topics of conversation. This is called by the TOPICS * command (in which case 'explicit' is true), and whenever we first * engage a character in a stateful conversation (in which case * 'explicit' is nil). * * We'll show the list of suggested topics associated with our * current conversational partner. If there are no topics, we'll say * nothing unless 'explicit' is true, in which case we'll simply say * that there are no topics that the player character is thinking * about. * * The purpose of this method is to let the game author keep an * "inventory" of topics with this actor for a given conversational * partner. This inventory is meant to represent the topics that on * the player character's mind - things the player character wants to * talk about with the other actor. Note that we're talking about * what the player *character* is thinking about - obviously we don't * know what's on the player's mind. * * When we enter conversation, or when the player asks for advice, * we'll show this inventory. The idea is to help guide the player * through a conversation without the more heavy-handed device of a * formal conversation menu system, so that conversations have a more * free-form feel without leaving the player hunting in the dark for * the magic ASK ABOUT topic. * * The TOPICS system is entirely optional. If a game doesn't specify * any SuggestedTopic objects, then this routine will simply never be * called, and the TOPICS command won't be allowed. Some authors * think it gives away too much to provide a list of topic * suggestions like this, and others don't like anything that smacks * of a menu system because they think it destroys the illusion * created by the text-input command line that the game is boundless. * Authors who feel this way can just ignore the TOPICS system. But * be aware that the illusion of boundlessness isn't always a good * thing for players; hunting around for ASK ABOUT topics can make * the game's limits just as obvious, if not more so, by exposing the * vast number of inputs for which the actor doesn't have a good * response. Players aren't stupid - a string of variations on "I * don't know about that" is just as obviously mechanistic as a * numbered list of menu choices. Using the TOPICS system might be a * good compromise for many authors, since the topic list can help * guide the player to the right questions without making the player * feel straitjacketed by a menu list. */ suggestTopics(explicit) { local actor; /* * if we're talking to someone, look up their suggested topics; * otherwise, we have nothing to suggest */ if ((actor = getCurrentInterlocutor()) != nil) { /* * we're talking to someone - suggest topics appropriate to * the person we're talking to */ actor.suggestTopicsFor(self, explicit); } else if (explicit) { /* we're not talking to anyone, so there's nothing to suggest */ gLibMessages.noTopicsNotTalking; } } /* * Suggest topics that the given actor might want to talk to us * about. The given actor is almost always the player character, * since generally NPC's don't talk to one another using * conversation commands (there'd be no point; they're simple * programmed automata, not full-blown AI's). */ suggestTopicsFor(actor, explicit) { /* by default, let our state suggest topics */ curState.suggestTopicsFor(actor, explicit); } /* * Receive notification that a command is being carried out in our * presence. */ beforeAction() { /* * If another actor is trying to take something in my inventory, * by default, do not allow it. */ if (gActor != self && (gActionIs(Take) || gActionIs(TakeFrom)) && gDobj.isIn(self)) { /* check to see if we want to allow this action */ checkTakeFromInventory(gActor, gDobj); } /* let our state object take a look at the action */ curState.beforeAction(); } /* * Perform any actor-specific processing for an action. The main * command processor invokes this on gActor after notifying nearby * objects via beforeAction(), but before carrying out the main * action of the command. */ actorAction() { /* do nothing by default */ } /* * Receive notification that a command has just been carried out in * our presence. */ afterAction() { /* let the state object handle it */ curState.afterAction(); } /* receive a notification that someone is about to travel */ beforeTravel(traveler, connector) { /* let the state object handle it */ curState.beforeTravel(traveler, connector); /* * If desired, track the departure so that we can follow the * traveler later. First, track the departure of each actor * traveling with the traveler. */ traveler.forEachTravelingActor( {actor: trackFollowInfo(actor, connector, traveler.location)}); /* * if the traveler is distinct from the actors traveling, track * it as well */ if (!traveler.isActorTraveling(traveler)) trackFollowInfo(traveler, connector, traveler.location); } /* receive a notification that someone has just traveled here */ afterTravel(traveler, connector) { /* let the state object handle it */ curState.afterTravel(traveler, connector); } /* * Receive notification that I'm initiating travel. This is called * on the actor performing the travel action before the travel is * actually carried out. */ actorTravel(traveler, connector) { /* * If other actors are accompanying me on this travel, run the * same travel action on the accompanying actors, using nested * actions. */ if (accompanyingActors != nil && accompanyingActors.length() != 0) { /* * Run the same travel action as a nested action on each * accompanying actor. Skip this for any accompanying actor * we're carrying, as they'll naturally go with us as a * result of being carried. */ foreach (local cur in accompanyingActors) { /* if the actor's not being carried, run the same action */ if (!cur.isIn(self)) nestedActorAction(cur, TravelVia, gDobj); } /* * The accompanying actor list applies for this single group * travel command, so now that we've moved everyone, we have * no further need for the list. Clear it out. */ accompanyingActors.removeRange(1, accompanyingActors.length()); } } /* * Check to see if we want to allow another actor to take something * from my inventory. By default, we won't allow it - we'll always * fail the command. */ checkTakeFromInventory(actor, obj) { /* don't allow it - show an error and terminate the command */ mainReport(&willNotLetGoMsg, self, obj); exit; } /* * Build a list of the objects that are explicitly registered to * receive notification when I'm the actor in a command. */ getActorNotifyList() { return actorNotifyList; } /* * Add an item to our registered notification items. These items * are to receive notifications when we're the actor performing a * command. * * Items can be added here if they must be notified of actions * performed by the actor even when the items aren't connected by * containment with the actor at the time of the action. All items * connected to the actor by containment are automatically notified * of each action; only items that must receive notification even * when not in scope need to be registered here. */ addActorNotifyItem(obj) { actorNotifyList += obj; } /* remove an item from the registered notification list */ removeActorNotifyItem(obj) { actorNotifyList -= obj; } /* our list of registered actor notification items */ actorNotifyList = [] /* * Get the ambient light level in the visual senses at this actor. * This is the ambient level at the actor. */ getVisualAmbient() { local ret; local cache; /* check for a cached value */ if ((cache = libGlobal.actorVisualAmbientCache) != nil && (ret = cache[self]) != nil) { /* found a cached entry - use it */ return ret; } /* get the maximum ambient level at self for my sight-like senses */ ret = senseAmbientMax(sightlikeSenses); /* if caching is active, cache our result for next time */ if (cache != nil) cache[self] = ret; /* return the result */ return ret; } /* * Determine if my location is lit for my sight-like senses. */ isLocationLit() { /* * Check for a simple, common case before doing the full * sense-path calculation: if our location is providing its own * light to its interior, then the location is lit. Most simple * rooms are always lit. */ if (sightlikeSenses.indexOf(sight) != nil && location != nil && location.brightness > 1 && location.transSensingOut(sight) == transparent) return true; /* * We don't have the simple case of light directly from our * location, so run the full sense path check and get our * maximum visual ambience level. If it's above the "self-lit" * level of 1, then we can see. */ return (getVisualAmbient() > 1); } /* * Get the best (most transparent) sense information for one of our * visual senses to the given object. */ bestVisualInfo(obj) { local best; /* we don't have a best value yet */ best = nil; /* check each sight-like sense */ foreach (local sense in sightlikeSenses) { /* * get the information for the object in this sense, and keep * the best (most transparent) info we've seen so far */ best = SenseInfo.selectMoreTrans(best, senseObj(sense, obj)); } /* return the best one we found */ return best; } /* * Build a list of all of the objects of which an actor is aware. * * An actor is aware of an object if the object is within reach of * the actor's senses, and has some sort of presence in that sense. * Note that both of these conditions must be true for at least one * sense possessed by the actor; an object that is within earshot, * but not within reach of any other sense, is in scope only if the * object is making some kind of noise. * * In addition, objects that the actor is holding (i.e., those * contained by the actor directly) are always in scope, regardless * of their reachability through any sense. */ scopeList() { local lst; /* we have nothing in our master list yet */ lst = new Vector(32); /* oneself is always in one's own scope list */ lst.append(self); /* iterate over each sense */ foreach (local sense in scopeSenses) { /* * get the list of objects with a presence in this sense * that can be sensed from our point of view, and and append * it to our master list */ lst.appendUnique(sensePresenceList(sense)); } /* add all of the items we are directly holding */ lst.appendUnique(contents); /* * ask each of our direct contents to add any contents of their * own that are in scope by virtue of their containers being in * scope */ foreach (local cur in contents) cur.appendHeldContents(lst); /* add any items that are specially in scope in the location */ if (location != nil) { /* get the extra scope items */ local extra = location.getExtraScopeItems(self); /* if this is a non-nil list, add it to our list */ if (extra.length() != 0) lst.appendUnique(extra); } /* * Finally, add anything extra each item already in scope wants * to add. Note that we keep going until we've visited each * element of the vector at its current length on each iteration, * so if we add any new items, we'll check them to see if they * want to add any new items, and so on. */ for (local i = 1 ; i <= lst.length() ; ++i) { local extra; /* get the extra scope items for this item */ extra = lst[i].getExtraScopeItems(self); /* if the extra item list is non-nil, add it to our list */ if (extra.length() != 0) lst.appendUnique(extra); } /* return the result */ return lst.toList(); } /* * Determine if I can see the given object. This returns true if * the object can be sensed at all in one of my sight-like senses, * nil if not. */ canSee(obj) { /* try each sight-like sense */ foreach (local sense in sightlikeSenses) { /* * if I can sense the object in this sense, I can sense the * object */ if (senseObj(sense, obj).trans != opaque) return true; } /* we didn't find any sight-like sense where we can see the object */ return nil; } /* * Determine if I can hear the given object. */ canHear(obj) { /* try each hearling-like sense */ foreach (local sense in hearinglikeSenses) { /* * if I can sense the object in this sense, I can sense the * object */ if (senseObj(sense, obj).trans != opaque) return true; } /* we found no hearing-like sense that lets us hear the object */ return nil; } /* * Determine if I can smell the given object. */ canSmell(obj) { /* try each hearling-like sense */ foreach (local sense in smelllikeSenses) { /* * if I can sense the object in this sense, I can sense the * object */ if (senseObj(sense, obj).trans != opaque) return true; } /* we found no smell-like sense that lets us hear the object */ return nil; } /* * Find the object that prevents us from seeing the given object. */ findVisualObstructor(obj) { /* try to find an opaque obstructor in one of our visual senses */ foreach (local sense in sightlikeSenses) { local obs; /* cache path information for this sense */ cacheSenseInfo(connectionTable(), sense); /* if we find an obstructor in this sense, return it */ if ((obs = findOpaqueObstructor(sense, obj)) != nil) return obs; } /* we didn't find any obstructor */ return nil; } /* * Build a table of full sensory information for all of the objects * visible to the actor through the actor's sight-like senses. * Returns a lookup table with the same set of information as * senseInfoTable(). */ visibleInfoTable() { /* return objects visible from my own point of view */ return visibleInfoTableFromPov(self); } /* * Build a table of full sensory information for all of the objects * visible to me from a particular point of view through my * sight-like senses. */ visibleInfoTableFromPov(pov) { local tab; /* we have no master table yet */ tab = nil; /* iterate over each sense */ foreach (local sense in sightlikeSenses) { local cur; /* get information for all objects for the current sense */ cur = pov.senseInfoTable(sense); /* merge the table so far with the new table */ tab = mergeSenseInfoTable(cur, tab); } /* return the result */ return tab; } /* * Build a lookup table of the objects that can be sensed for the * purposes of taking inventory. We'll include everything in the * normal visual sense table, plus everything directly held. */ inventorySenseInfoTable() { local visInfo; local cont; local ambient; local info; /* * Start with the objects visible to the actor through the * actor's sight-like senses. */ visInfo = visibleInfoTable(); /* get the ambient light level at the actor */ if ((info = visInfo[self]) != nil) ambient = info.ambient; else ambient = 0; /* * We'll assume that, for each item that the actor is directly * holding AND knows about, the actor can still identify the item * by touch, even if it's not visible. This way, when we're in a * dark room, we'll still be able to refer to the objects we're * directly holding, as long as we already know about them. * * Likewise, add items within our direct contents that are * considered equally held. */ cont = new Vector(32); foreach (local cur in contents) { /* add this item from our contents */ cont.append(cur); /* add its contents that are themselves equally as held */ cur.appendHeldContents(cont); } /* * Make a fully-sensible entry for each of our held items. We * can simply replace any existing entry in the table that we got * from the visual senses, since a fully transparent entry will * be at least as good as anything we got from the normal visual * list. Only include items that the actor knows about; we'll * assume that we can identify by touch anything we're holding if * we already know what it is, but not otherwise. */ foreach (local cur in cont) { /* if we know about the object, make it effectively visible */ if (knowsAbout(cur)) visInfo[cur] = new SenseInfo(cur, transparent, nil, ambient); } /* return the table */ return visInfo; } /* * Show what the actor is carrying. */ showInventory(tall) { /* * show our inventory with our default listers as given by our * inventory/wearing lister properties */ showInventoryWith(tall, inventoryLister); } /* * Show what the actor is carrying, using the given listers. * * Note that this method must be overridden if the actor does not * use a conventional 'contents' list property to store its full set * of contents. */ showInventoryWith(tall, inventoryLister) { local infoTab; /* get the table of objects sensible for inventory */ infoTab = inventorySenseInfoTable(); /* list in the appropriate mode ("wide" or "tall") */ inventoryLister.showList(self, self, contents, ListRecurse | (tall ? ListTall : 0), 0, infoTab, nil); /* mention sounds coming from inventory items */ inventorySense(sound, inventoryListenLister); /* mention odors coming from inventory items */ inventorySense(smell, inventorySmellLister); } /* * Add to an inventory description a list of things we notice * through a specific sense. */ inventorySense(sense, lister) { local infoTab; local presenceList; /* get the information table for the desired sense */ infoTab = senseInfoTable(sense); /* * get the list of everything with a presence in this sense that * I'm carrying */ presenceList = senseInfoTableSubset(infoTab, {obj, info: obj.isIn(self) && obj.(sense.presenceProp)}); /* add a paragraph break */ cosmeticSpacingReport('<.p>'); /* list the items */ lister.showList(self, nil, presenceList, 0, 0, infoTab, nil); } /* * The Lister object that we use for inventory listings. By * default, we use actorInventoryLister, but this can be overridden * if desired to use a different listing style. */ inventoryLister = actorInventoryLister /* * The Lister for inventory listings, for use in a full description * of the actor. By default, we use the "long form" inventory * lister, on the assumption that most actors have relatively lengthy * descriptive text. This can be overridden to use other formats; * the short-form lister, for example, is useful for actors with only * brief descriptions. */ holdingDescInventoryLister = actorHoldingDescInventoryListerLong /* * Perform library pre-initialization on the actor */ initializeActor() { /* set up an empty pending command list */ pendingCommand = new Vector(5); /* create a default inventory lister if we don't have one already */ if (inventoryLister == nil) inventoryLister = actorInventoryLister; /* create our antecedent tables */ antecedentTable = new LookupTable(8, 8); possAnaphorTable = new LookupTable(8, 8); /* if we don't have a state object, create a default */ if (curState == nil) setCurState(new ActorState(self)); /* create our pending-conversation list */ pendingConv = new Vector(5); } /* * Note conditions before an action or other event. By default, we * note our location and light/dark status, so that we comment on * any change in the light/dark status after the event if we're * still in the same location. */ noteConditionsBefore() { /* note our original location and light/dark status */ locationBefore = location; locationLitBefore = isLocationLit(); } /* * Note conditions after an action or other event. By default, if * we are still in the same location we were in when * noteConditionsBefore() was last called, and the light/dark status * has changed, we'll mention the change in light/dark status. */ noteConditionsAfter() { /* * If our location hasn't changed but our light/dark status has, * note the new status. We don't make any announcement if the * location has changed, since the travel routine will * presumably have shown us the new location's light/dark status * implicitly as part of the description of the new location * after travel. */ if (location == locationBefore && isLocationLit() != locationLitBefore) { /* consider this the start of a new turn */ "<.commandsep>"; /* note the change with a new 'NoteDarkness' action */ newActorAction(self, NoteDarkness); /* * start another turn, in case this occurred during an * implicit action or the like */ "<.commandsep>"; } } /* conditions we noted in noteConditionsBefore() */ locationBefore = nil locationLitBefore = nil /* let the actor have a turn as soon as the game starts */ nextRunTime = 0 /* * Scheduling order - this determines the order of execution when * several items are schedulable at the same game clock time. * * We choose a scheduling order that schedules actors in this * relative order: * * 100 player character, ready to execute *. 200 NPC, ready to execute *. 300 player character, idle *. 400 NPC, idle * * An "idle" actor is one that is waiting for another character to * complete a command, or an NPC with no pending commands to * perform. (For the player character, it doesn't matter whether or * not there's a pending command, because if the PC has no pending * command, we ask the player for one.) * * This ordering ensures that each actor gets a chance to run each * turn, but that actors with work to do go first, and other things * being equal, the player character goes ahead of NPC's. */ scheduleOrder = 100 /* calculate the scheduling order */ calcScheduleOrder() { /* determine if we're ready to run */ if (readyForTurn()) scheduleOrder = isPlayerChar() ? 100 : 200; else scheduleOrder = isPlayerChar() ? 300 : 400; /* return the scheduling order */ return scheduleOrder; } /* * Determine if we're ready to do something on our turn. We're * ready to do something if we're not waiting for another actor to * finish doing something and either we're the player character or * we already have a pending command in our command queue. */ readyForTurn() { /* * if we're waiting for another actor, we're not ready to do * anything */ if (checkWaitingForActor()) return nil; /* * if we're the player character, we're always ready to take a * turn as long as we're not waiting for another actor (which we * now know we're not), because we can either execute one of our * previously queued commands, or we can ask for a new command * to perform */ if (isPlayerChar()) return true; /* * if we have something other than placeholders in our command * queue, we're ready to take a turn, because we can execute the * next command in our queue */ if (pendingCommand.indexWhich({x: x.hasCommand}) != nil) return true; /* * we have no specific work to do, so we're not ready for our * next turn */ return nil; } /* * Check to see if we're waiting for another actor to do something. * Return true if so, nil if not. If we've been waiting for another * actor, and the actor has finished the task we've been waiting for * since the last time we checked, we'll clean up our internal state * relating to the wait and return nil. */ checkWaitingForActor() { local cmdIdx; local idx; /* if we're not waiting for an actor, simply return nil */ if (waitingForActor == nil) return nil; /* * We're waiting for an actor to complete a command. Check to * see if the completion marker is still in the actor's queue; if * it's not, then the other actor has already completed our task. * If the completion marker is in the other actor's queue, but * there are no command entries before it, then we're also done * waiting, because we're not actually waiting for the completion * marker but instead for the tasks that were ahead of it in the * main game execution loop. * * So, find the index of our marker in the queue, and find the * index of the first real command in the queue. If our marker * is still in the queue, and there's a command in the queue * before our marker, the actor we're waiting for still has * things to do before we're ready, so we're still waiting. */ idx = waitingForActor.pendingCommand.indexOf(waitingForInfo); cmdIdx = waitingForActor.pendingCommand.indexWhich({x: x.hasCommand}); if (idx != nil && cmdIdx != nil && idx > cmdIdx) { /* * The marker is still in the queue, and there's at least * one other command ahead of it, so the other actor hasn't * finished the task we've been waiting for. Tell the * caller that we are indeed still waiting for someone. */ return true; } /* * The other actor has disposed of our end-marker (or is about * to, because it's the next thing left in the actor's queue), * so it has finished with all of the commands we have been * waiting for. However, if I haven't caught up in game clock * time with the actor I've been waiting for, I'm still waiting. */ if (waitingForActor.nextRunTime > nextRunTime) return true; /* we're done waiting - forget our wait status information */ waitingForActor = nil; waitingForInfo = nil; /* tell the caller we're no longer waiting for anyone */ return nil; } /* the action the actor performed most recently */ mostRecentAction = nil /* * Add busy time. An action calls this when we are the actor * performing the action, and the action consumes game time. This * marks us as busy for the given time units. */ addBusyTime(action, units) { /* note the action being performed */ mostRecentAction = action; /* adjust the next run time by the busy time */ nextRunTime += units; } /* * When it's our turn and we don't have any command to perform, * we'll call this routine, which can perform a scripted operation * if desired. */ idleTurn() { local tCur = Schedulable.gameClockTime; local origNextRunTime = nextRunTime; /* * if we haven't been targeted for conversation on this turn, * see if we have a conversation we want to start */ if (lastConvTime < tCur) { /* check for a conversation that's ready to go */ local info = pendingConv.valWhich({x: tCur >= x.time_}); /* if we found one, kick it off */ if (info != nil) { /* remove it from the list */ pendingConv.removeElement(info); /* start the conversation */ initiateConversation(info.state_, info.node_); } } /* notify our state object that we're taking a turn */ curState.takeTurn(); /* * If we haven't already adjusted our next run time, consume a * turn, so we're not ready to run again until the next game time * increment. In some cases, we'll already have made this * adjustment; for example, we might have run a nested command * within our state object's takeTurn() method. */ if (nextRunTime == origNextRunTime) ++nextRunTime; } /* * Receive notification that this is a non-idle turn. This is * called whenever a command in our pending command queue is about * to be executed. * * This method need not do anything at all, since the caller will * take care of running the pending command. The purpose of this * method is to take care of any changes an actor wants to make when * it receives an explicit command, as opposed to running its own * autonomous activity. * * By default, we cancel follow mode if it's in effect. It usually * makes sense for an explicit command to interrupt follow mode; * follow mode is usually started by an explicit command in the * first place, so it is usually sensible for a new command to * replace the one that started follow mode. */ nonIdleTurn() { /* by default, cancel follow mode */ followingActor = nil; } /* * If we're following an actor, this keeps track of the actor we're * following. NPC's can use this to follow around another actor * whenever possible. */ followingActor = nil /* * Handle a situation where we're trying to follow an actor but * can't. By default, this simply cancels our follow mode. * * Actors might want to override this to be more tolerant. For * example, an actor might want to wait until five turns elapse to * give up on following, in case the target actor returns after a * brief digression; or an actor could stay in follow mode until it * received other instructions, or found something better to do. */ cannotFollow() { /* * by default, simply cancel follow mode by forgetting about the * actor we're following */ followingActor = nil; } /* * Execute one "turn" - this is a unit of time passing. The player * character generally is allowed to execute one command in the * course of a turn; a non-player character with a programmed task * can perform an increment of the task. * * We set up an ActorTurnAction environment and invoke our * executeActorTurn() method. In most cases, subclasses should * override executeActorTurn() rather than this method, since * overriding executeTurn() directly will lose the action * environment. */ executeTurn() { /* start a new command visually when a new actor is taking over */ "<.commandsep>"; /* * Execute the turn in a daemon action context, and in the sight * context of the actor. The sense context will ensure that we * report the results of the action only if the actor is visible * to the player character; in most cases, the actor's * visibility is equivalent to the visibility of the effects, so * this provides a simple way of ensuring that the results of * the action are reported if and only if they're visible to the * player character. * * Note that if we are the player character, don't use the sense * context filtering -- we normally want full reports for * everything the player character does. */ return withActionEnv(EventAction, self, {: callWithSenseContext(isPlayerChar() ? nil : self, sight, {: executeActorTurn() }) }); } /* * The main processing for an actor's turn. In most cases, * subclasses should override this method (rather than executeTurn) * to specialize an actor's turn processing. */ executeActorTurn() { /* * If we have a pending response, and we're in a position to * deliver it, our next work is to deliver the pending response. */ if (pendingResponse != nil && canTalkTo(pendingResponse.issuer_)) { /* * We have a pending response, and the command issuer from * the pending response can hear us now, so we can finally * deliver the response. * * If the issuer is the player character, send to the player * using our deferred message generator; otherwise, call the * issuer's notification routine, since it's an NPC-to-NPC * notification. */ if (pendingResponse.issuer_.isPlayerChar()) { /* * we're notifying the player - use the deferred message * generator */ getParserDeferredMessageObj().(pendingResponse.prop_)( self, pendingResponse.args_...); } else { /* it's an NPC-to-NPC notification - notify the issuer */ pendingResponse.issuer_.notifyIssuerParseFailure( self, pendingResponse.prop_, pendingResponse.args_); } /* * in either case, we've gotten this out of our system now, * so we can forget about the pending response */ pendingResponse = nil; } /* check to see if we're waiting for another actor */ if (checkWaitingForActor()) { /* * we're still waiting, so there's nothing for us to do; take * an idle turn and return */ idleTurn(); return true; } /* * if we're the player character, and we have no pending commands * to execute, our next task will be to read and execute a * command */ if (pendingCommand.length() == 0 && isPlayerChar()) { local toks; /* read a command line and get the resulting token list */ toks = readMainCommandTokens(rmcCommand); /* * re-activate the main transcript - reading the command * line will have deactivated the transcript, but we want it * active again now that we're about to start executing the * command */ gTranscript.activate(); /* * If it came back nil, it means that the input was fully * processed in pre-parsing; this means that we don't have * any more work to do on this turn, so we can simply end our * turn now. */ if (toks == nil) return true; /* retrieve the token list from the command line */ toks = toks[2]; /* * Add it to our pending command queue. Since we read the * command from the player, and we're the player character, * we treat the command as coming from myself. * * Since this is a newly-read command line, we're starting a * new sentence. */ addPendingCommand(true, self, toks); } /* * Check to see if we have any pending command to execute. If * so, our next task is to execute the pending command. */ if (pendingCommand.length() != 0) { local cmd; /* remove the first pending command from our queue */ cmd = pendingCommand[1]; pendingCommand.removeElementAt(1); /* if this is a real command, note the non-idle turn */ if (cmd.hasCommand) nonIdleTurn(); /* execute the first pending command */ cmd.executePending(self); /* * We're done with this turn. If we no longer have any * pending commands, tell the scheduler to refigure the * execution order, since another object might now be ready * to run ahead of our idle activity. */ if (pendingCommand.indexWhich({x: x.hasCommand}) == nil) return nil; else return true; } /* * If we're following an actor, and the actor isn't in sight, see * if we can catch up. */ if (followingActor != nil && location != nil && (followingActor.location.effectiveFollowLocation != location.effectiveFollowLocation)) { local info; /* see if we have enough information to follow */ info = getFollowInfo(followingActor); /* * Check to see if we have enough information to follow the * actor. We can only follow if we saw the actor depart at * some point, and we're in the same location where we last * saw the actor depart. (We have to be in the same * location, because we follow by performing the same command * we saw the actor perform when we last saw the actor * depart. Repeating the command will obviously be * ineffective unless we're in the same location as the actor * was.) */ if (info != nil) { local success; /* * we know how to follow the actor, so simply perform * the same command we saw the actor perform. */ newActorAction(self, Follow, followingActor); /* note whether or not we succeeded */ success = (location.effectiveFollowLocation == followingActor.location.effectiveFollowLocation); /* notify the state object of our attempt */ curState.justFollowed(success); /* * if we failed to track the actor, note that we are * unable to follow the actor */ if (!success) { /* note that we failed to follow the actor */ cannotFollow(); } /* we're done with this turn */ return true; } else { /* * we don't know how to follow this actor - call our * cannot-follow handler */ cannotFollow(); } } /* we have no pending work to perform, so take an idle turn */ idleTurn(); /* no change in scheduling priority */ return true; } /* * By default, all actors are likely command targets. This should * be overridden for actors who are obviously not likely to accept * commands of any kind. * * This is used to disambiguate target actors in commands, so this * should provide an indication of what should be obvious to a * player, because the purpose of this information is to guess what * the player is likely to take for granted in specifying a target * actor. */ isLikelyCommandTarget = true /* * Determine if we should accept a command. 'issuingActor' is the * actor who issued the command: if the player typed the command on * the command line, this will be the player character actor. * * This routine performs only the simplest check, since it doesn't * have access to the specific action being performed. This is * intended as a first check, to allow us to bypass noun resolution * if the actor simply won't accept any command from the issuer. * * Returns true to accept a command, nil to reject it. If this * routine returns nil, and the command came from the player * character, a suitable message should be displayed. * * Note that most actors should not override this routine simply to * express the will of the actor to accept a command, since this * routine performs a number of checks for the physical ability of * the actor to execute a command from the issuer. To determine * whether or not the actor should obey physically valid commands * from the issuer, override obeyCommand(). */ acceptCommand(issuingActor) { /* if we're the current player character, accept any command */ if (isPlayerChar()) return true; /* if we can't hear the issuer, we can't talk to it */ if (issuingActor != self && !issuingActor.canTalkTo(self)) { /* report that the target actor can't hear the issuer */ reportFailure(&objCannotHearActorMsg, self); /* tell the caller that the command cannot proceed */ return nil; } /* if I'm busy doing something else, say so */ if (nextRunTime > Schedulable.gameClockTime) { /* tell the issuing actor I'm busy */ notifyParseFailure(issuingActor, &refuseCommandBusy, [issuingActor]); /* tell the caller to abandon the command */ return nil; } /* check to see if I have other work to perform first */ if (!acceptCommandBusy(issuingActor)) return nil; /* we didn't find any reason to object, so allow the command */ return true; } /* * Check to see if I'm busy with pending commands, and if so, * whether or not I should accept a new command. Returns true if we * should accept a command, nil if not. If we return nil, we must * notify the issuer of the rejection. * * By default, we won't accept a command if we have any work * pending. */ acceptCommandBusy(issuingActor) { /* if we have any pending commands, don't accept a new command */ if (pendingCommand.length() != 0) { /* * if we have only commands from the same issuer pending, * cancel all of the pending commands and accept the new * command instead */ foreach (local info in pendingCommand) { /* * if this is from a different issuer, don't accept a * new command */ if (info.issuer_ != issuingActor) { /* tell the other actor that we're busy */ notifyParseFailure(issuingActor, &refuseCommandBusy, [issuingActor]); /* tell the caller to abandon the command */ return nil; } } /* * all of the pending commands were from the same issuer, so * presumably the issuer wants to override those commands; * remove the old ones from our pending queue */ pendingCommand.removeRange(1, pendingCommand.length()); } /* we didn't find any problems */ return true; } /* * Determine whether or not we want to obey a command from the given * actor to perform the given action. We only get this far when we * determine that it's possible for us to accept a command, given * the sense connections between us and the issuing actor, and given * our pending command queue. * * When this routine is called, the action has been determined, and * the noun phrases have been resolved. However, we haven't * actually started processing the action yet, so the globals for * the noun slots (gDobj, gIobj, etc) are NOT available. If the * routine needs to know which objects are involved, it must obtain * the full list of resolved objects from the action (using, for * example, getResolvedDobjList()). * * When there's a list of objects to be processed (as in GET ALL), * we haven't started working on any one of them yet - this check is * made once for the entire command, and applies to the entire list * of objects. If the actor wants to respond specially to * individual objects, you can do that by overriding actorAction() * instead of this routine. * * This routine should display an appropriate message and return nil * if the command is not to be accepted, and should simply return * true to accept the command. * * By default, we'll let our state object handle this. * * Note that actors that override this might also need to override * wantsFollowInfo(), since an actor that accepts "follow" commands * will need to keep track of the movements of other actors if it is * to carry out any following. */ obeyCommand(issuingActor, action) { /* note that the issuing actor is targeting me in conversation */ issuingActor.noteConversation(self); /* let the state object handle it */ return curState.obeyCommand(issuingActor, action); } /* * Say hello/goodbye/yes/no to the given actor. We'll greet the * target actor is the target actor was specified (i.e., actor != * self); otherwise, we'll greet our current default conversational * partner, if we have one. */ sayHello(actor) { sayToActor(actor, helloTopicObj, helloConvType); } sayGoodbye(actor) { sayToActor(actor, byeTopicObj, byeConvType); } sayYes(actor) { sayToActor(actor, yesTopicObj, yesConvType); } sayNo(actor) { sayToActor(actor, noTopicObj, noConvType); } /* handle one of the conversational addresses */ sayToActor(actor, topic, convType) { /* * If the target actor is the same as the issuing actor, then no * target actor was specified in the command, so direct the * address to our current conversational partner, if we have * one. */ if (actor == self) actor = getDefaultInterlocutor(); /* * if we found an actor, send the address to the actor's state * object; otherwise, handle it with the given default message */ if (actor != nil) { /* make sure we can talk to the other actor */ if (!canTalkTo(actor)) { /* can't talk to them - say so and give up */ reportFailure(&objCannotHearActorMsg, actor); exit; } /* remember our current conversational partner */ noteConversation(actor); /* handle it as a topic */ actor.curState.handleConversation(self, topic, convType); } else { /* * we don't know whom we're addressing; just show the default * message for an unknown interlocutor */ mainReport(convType.unknownMsg); } } /* * Handle the XSPCLTOPIC pseudo-command. This command is generated * by the SpecialTopic pre-parser when it recognizes the player's * input as matching an active SpecialTopic's custom syntax. Our * job is to route this back to our current interlocutor's active * ConvNode, so that it can find the SpecialTopic that it matched in * pre-parsing and show its response. */ saySpecialTopic() { local actor; /* send it to our interlocutor */ if ((actor = getCurrentInterlocutor()) == nil || actor.curConvNode == nil) { /* * We don't seem to have a current interlocutor, or the * interlocutor doesn't have a current conversation node. * This is inconsistent; there's no way we could have * generated XSPCLTOPIC from our pre-parser under these * conditions. The most likely thing is that the player * tried typing in XSPCLTOPIC manually. Politely ignore it. */ gLibMessages.commandNotPresent; } else { /* note the conversation directed to the other actor */ noteConversation(actor); /* send the request to the ConvNode for processing */ actor.curConvNode.saySpecialTopic(self); } } /* * Add a command to our pending command list. The new command is * specified as a list of tokens to be parsed, and it is added after * any commands already in our pending list. */ addPendingCommand(startOfSentence, issuer, toks) { /* add a descriptor to the pending command list */ pendingCommand.append( new PendingCommandToks(startOfSentence, issuer, toks)); } /* * Insert a command at the head of our pending command list. The * new command is specified as a list of tokens to parse, and it is * inserted into our pending command list before any commands * already in the list. */ addFirstPendingCommand(startOfSentence, issuer, toks) { /* add a descriptor to the start of our list */ pendingCommand.insertAt( 1, new PendingCommandToks(startOfSentence, issuer, toks)); } /* * Add a resolved action to our pending command list. The new * command is specified as a resolved Action object; it is added * after any commands already in our list. */ addPendingAction(startOfSentence, issuer, action, [objs]) { /* add a descriptor to the pending command list */ pendingCommand.append(new PendingCommandAction( startOfSentence, issuer, action, objs...)); } /* * Insert a resolved action at the start of our pending command * list. The new command is specified as a resolved Action object; * it is added before any commands already in our list. */ addFirstPendingAction(startOfSentence, issuer, action, [objs]) { /* add a descriptor to the pending command list */ pendingCommand.insertAt(1, new PendingCommandAction( startOfSentence, issuer, action, objs...)); } /* pending commands - this is a list of PendingCommandInfo objects */ pendingCommand = nil /* * pending response - this is a single PendingResponseInfo object, * which we'll deliver as soon as the issuing actor is in a position * to hear us */ pendingResponse = nil /* * get the library message object for a parser message addressed to * the player character */ getParserMessageObj() { /* * If I'm the player character, use the player character message * object; otherwise, use the default non-player character * message object. * * To customize parser messages from a particular actor, create * an object based on npcMessages, and override this routine in * the actor so that it returns the custom object rather than * the standard npcMessages object. To customize messages for * ALL of the NPC's in a game, simply modify npcMessages itself, * since it's the default for all non-player characters. */ return isPlayerChar() ? playerMessages : npcMessages; } /* * Get the deferred library message object for a parser message * addressed to the player character. We only use this to generate * messages deferred from non-player characters. */ getParserDeferredMessageObj() { return npcDeferredMessages; } /* * Get the library message object for action responses. This is * used to generate library responses to verbs. */ getActionMessageObj() { /* * return the default player character or NPC message object, * depending on whether I'm the player or not; individual actors * can override this to supply actor-specific messages for * library action responses */ return isPlayerChar() ? playerActionMessages: npcActionMessages; } /* * Notify an issuer that a command sent to us resulted in a parsing * failure. We are meant to reply to the issuer to let the issuer * know about the problem. messageProp is the libGlobal message * property describing the error, and args is a list with the * (varargs) arguments to the message property. */ notifyParseFailure(issuingActor, messageProp, args) { /* * In case the actor is in a remote location but in scope for the * purposes of the conversation only (such as over a phone or * radio), run this in a neutral sense context. Since we're * reporting a parser failure, we want the message to be * displayed no matter what the scope situation is. */ callWithSenseContext(nil, nil, function() { /* check who's talking to whom */ if (issuingActor.isPlayerChar()) { /* * The player issued the command. If the command was * directed to an NPC (i.e., we're not the player), check * to see if the player character is in scope from our * perspective. */ if (issuingActor != self && !canTalkTo(issuingActor)) { /* * The player issued the command to an NPC, but the * player is not capable of hearing the NPC's * response. */ cannotRespondToCommand(issuingActor, messageProp, args); } else { /* * generate a message using the appropriate message * generator object */ getParserMessageObj().(messageProp)(self, args...); } } else { /* * the command was issued from one NPC to another - * notify the issuer of the problem, but don't display * any messages, since this interaction is purely among * the NPC's */ issuingActor. notifyIssuerParseFailure(self, messageProp, args); } }); } /* * We have a parser error to report to the player, but we cannot * respond at the moment because the player is not capable of * hearing us (there is no sense path for our communications senses * from us to the player actor). Defer reporting the message until * later. */ cannotRespondToCommand(issuingActor, messageProp, args) { /* * Remember the problem for later deliver. If we already have a * deferred response, forget it - just report the latest * problem. */ pendingResponse = new PendingResponseInfo(issuingActor, messageProp, args); /* * Some actors might want to override this to start searching * for the player character. We don't have any generic * mechanism to conduct such a search, but a game that * implements one might want to make use of it here. */ } /* * Receive notification that a command we sent to another NPC * failed. This is only called when one NPC sends a command to * another NPC; this is called on the issuer to let the issuer know * that the target can't perform the command because of the given * resolution failure. * * By default, we don't do anything here, because we don't have any * default code to send a command from one NPC to another. Any * custom NPC actor that sends a command to another NPC actor might * want to use this to deal with problems in processing those * commands. */ notifyIssuerParseFailure(targetActor, messageProp, args) { /* by default, we do nothing */ } /* * Antecedent lookup table. Each actor keeps its own table of * antecedents indexed by pronoun type, so that we can * simultaneously have different antecedents for different pronouns. */ antecedentTable = nil /* * Possessive anaphor lookup table. In almost all cases, the * possessive anaphor for a given pronoun will be the same as the * corresponding regular pronoun: HIS indicates possession by HIM, * for example. In a few cases, though, the anaphoric quality of * possessives takes precedence, and these will differ. For * example, in TELL BOB TO DROP HIS BOOK, "his" refers back to Bob, * while in TELL BOB TO HIT HIM, "him" refers to whatever it * referred to before the command. */ possAnaphorTable = nil /* * set the antecedent for the neuter singular pronoun ("it" in * English) */ setIt(obj) { setPronounAntecedent(PronounIt, obj); } /* set the antecedent for the masculine singular ("him") */ setHim(obj) { setPronounAntecedent(PronounHim, obj); } /* set the antecedent for the feminine singular ("her") */ setHer(obj) { setPronounAntecedent(PronounHer, obj); } /* set the antecedent list for the ungendered plural pronoun ("them") */ setThem(lst) { setPronounAntecedent(PronounThem, lst); } /* look up a pronoun's value */ getPronounAntecedent(typ) { /* get the stored antecedent for this pronoun */ return antecedentTable[typ]; } /* set a pronoun's antecedent value */ setPronounAntecedent(typ, val) { /* remember the value in the antecedent table */ antecedentTable[typ] = val; /* set the same value for the possessive anaphor */ possAnaphorTable[typ] = val; } /* set a possessive anaphor value */ setPossAnaphor(typ, val) { /* set the value in the possessive anaphor table only */ possAnaphorTable[typ] = val; } /* get a possessive anaphor value */ getPossAnaphor(typ) { return possAnaphorTable[typ]; } /* forget the possessive anaphors */ forgetPossAnaphors() { /* copy all of the antecedents to the possessive anaphor table */ antecedentTable.forEachAssoc( {key, val: possAnaphorTable[key] = val}); } /* * Copy pronoun antecedents from the given actor. This should be * called whenever an actor issues a command to us, so that pronouns * in the command are properly resolved relative to the issuer. */ copyPronounAntecedentsFrom(issuer) { /* copy every element from the issuer's table */ issuer.antecedentTable.forEachAssoc( {key, val: setPronounAntecedent(key, val)}); } /* -------------------------------------------------------------------- */ /* * Verb processing */ /* show a "take from" message as indicating I don't have the dobj */ takeFromNotInMessage = &takeFromNotInActorMsg /* verify() handler to check against applying an action to 'self' */ verifyNotSelf(msg) { /* check to make sure we're not trying to do this to myself */ if (self == gActor) illogicalSelf(msg); } /* macro to verify we're not self, and inherit the default behavior */ #define verifyNotSelfInherit(msg) \ verify() \ { \ verifyNotSelf(msg); \ inherited(); \ } /* * For the basic physical manipulation verbs (TAKE, DROP, PUT ON, * etc), it's illogical to operate on myself, so check for this in * verify(). Otherwise, handle these as we would ordinary objects, * since we might be able to manipulate other actors in the normal * manner, especially actors small enough that we can pick them up. */ dobjFor(Take) { verifyNotSelfInherit(&takingSelfMsg) } dobjFor(Drop) { verifyNotSelfInherit(&droppingSelfMsg) } dobjFor(PutOn) { verifyNotSelfInherit(&puttingSelfMsg) } dobjFor(PutUnder) { verifyNotSelfInherit(&puttingSelfMsg) } dobjFor(Throw) { verifyNotSelfInherit(&throwingSelfMsg) } dobjFor(ThrowAt) { verifyNotSelfInherit(&throwingSelfMsg) } dobjFor(ThrowDir) { verifyNotSelfInherit(&throwingSelfMsg) } dobjFor(ThrowTo) { verifyNotSelfInherit(&throwingSelfMsg) } /* customize the message for THROW TO */ iobjFor(ThrowTo) { verify() { /* by default, we don't want to catch anything */ illogical(&willNotCatchMsg, self); } } /* treat PUT SELF IN FOO as GET IN FOO */ dobjFor(PutIn) { verify() { /* the target actor is always unsuitable as a default */ if (gActor == self) nonObvious; } check() { /* if I'm putting myself somewhere, treat it as GET IN */ if (gActor == self) replaceAction(Enter, gIobj); /* do the normal work */ inherited(); } } dobjFor(Kiss) { preCond = [touchObj] verify() { /* cannot kiss oneself */ verifyNotSelf(&cannotKissSelfMsg); } action() { mainReport(&cannotKissActorMsg); } } dobjFor(AskFor) { preCond = [canTalkToObj] verify() { /* it makes no sense to ask myself for something */ verifyNotSelf(&cannotAskSelfForMsg); } action() { /* note that the issuer is targeting us with conversation */ gActor.noteConversation(self); /* let the state object handle it */ curState.handleConversation(gActor, gTopic, askForConvType); } } dobjFor(TalkTo) { preCond = [canTalkToObj] verify() { /* it's generally illogical to talk to oneself */ verifyNotSelf(&cannotTalkToSelfMsg); } action() { /* note that the issuer is targeting us in conversation */ gActor.noteConversation(self); /* handle it as a 'hello' topic */ curState.handleConversation(gActor, helloTopicObj, helloConvType); } } iobjFor(GiveTo) { verify() { /* it makes no sense to give something to myself */ verifyNotSelf(&cannotGiveToSelfMsg); /* it also makes no sense to give something to itself */ if (gDobj == gIobj) illogicalSelf(&cannotGiveToItselfMsg); } action() { /* take note that I've seen the direct object */ noteObjectShown(gDobj); /* note that the issuer is targeting us with conversation */ gActor.noteConversation(self); /* let the state object handle it */ curState.handleConversation(gActor, gDobj, giveConvType); } } iobjFor(ShowTo) { verify() { /* it makes no sense to show something to myself */ verifyNotSelf(&cannotShowToSelfMsg); /* it also makes no sense to show something to itself */ if (gDobj == gIobj) illogicalSelf(&cannotShowToItselfMsg); } action() { /* take note that I've seen the direct object */ noteObjectShown(gDobj); /* note that the issuer is targeting us with conversation */ gActor.noteConversation(self); /* let the actor state object handle it */ curState.handleConversation(gActor, gDobj, showConvType); } } /* * Note that the given object has been explicitly shown to me. By * default, we'll mark the object and its visible contents as having * been seen by me. This is called whenever we're the target of a * SHOW TO or GIVE TO, since presumably such an explicit act of * calling our attention to an object would make us consider the * object as having been seen in the future. */ noteObjectShown(obj) { local info; /* get the table of things we can see */ info = visibleInfoTable(); /* if the object is in the table, mark it as seen */ if (info[obj] != nil) setHasSeen(obj); /* also mark the visible contents of the object as having been seen */ obj.setContentsSeenBy(info, self); } dobjFor(AskAbout) { preCond = [canTalkToObj] verify() { /* it makes no sense to ask oneself about something */ verifyNotSelf(&cannotAskSelfMsg); } action() { /* note that the issuer is targeting us with conversation */ gActor.noteConversation(self); /* let our state object handle it */ curState.handleConversation(gActor, gTopic, askAboutConvType); } } dobjFor(TellAbout) { preCond = [canTalkToObj] verify() { /* it makes no sense to tell oneself about something */ verifyNotSelf(&cannotTellSelfMsg); } check() { /* * If the direct object is the issuing actor, rephrase this * as "issuer, ask actor about iobj". * * Note that we do this in 'check' rather than 'action', * because this will ensure that we'll rephrase the command * properly even if the subclass overrides with its own * check, AS LONG AS the overriding method inherits this base * definition first. If we did the rephrasing in the * 'action', then an overriding 'check' might incorrectly * disqualify the operation on the assumption that it's an * ordinary TELL ABOUT rather than what it really is, which * is a rephrased ASK ABOUT. */ if (gDobj == gIssuingActor) replaceActorAction(gIssuingActor, AskAbout, gActor, gTopic); } action() { /* note that the issuer is targeting us with conversation */ gActor.noteConversation(self); /* let the state object handle it */ curState.handleConversation(gActor, gTopic, tellAboutConvType); } } /* * Handle a conversational command. All of the conversational * actions (HELLO, GOODBYE, YES, NO, ASK ABOUT, ASK FOR, TELL ABOUT, * SHOW TO, GIVE TO) are routed here when we're the target of the * action (for example, we're BOB in ASK BOB ABOUT TOPIC) AND the * ActorState doesn't want to handle the action. */ handleConversation(actor, topic, convType) { /* try handling the topic from our topic database */ if (!handleTopic(actor, topic, convType, nil)) { /* the topic database didn't handle it; use a default response */ defaultConvResponse(actor, topic, convType); } } /* * Show a default response to a conversational action. By default, * we'll show the default response for our conversation type. */ defaultConvResponse(actor, topic, convType) { /* call the appropriate default response for the ConvType */ convType.defaultResponse(self, actor, topic); } /* * Show our default greeting message - this is used when the given * another actor greets us with HELLO or TALK TO, and we don't * otherwise handle it (such as via a topic database entry). * * By default, we'll just show "there's no response" as a default * message. We'll show this in default mode, so that if the caller * is going to show a list of suggested conversation topics (which * the 'hello' and 'talk to' commands will normally try to do), the * topic list will override the "there's no response" default. In * other words, we'll have one of these two types of exchanges: * *. >talk to bob *. There's no response * *. >talk to bill *. You could ask him about the candle, the book, or the bell, or *. tell him about the crypt. */ defaultGreetingResponse(actor) { defaultReport(&noResponseFromMsg, self); } /* show our default goodbye message */ defaultGoodbyeResponse(actor) { mainReport(&noResponseFromMsg, self); } /* * Show the default answer to a question - this is called when we're * the actor in ASK ABOUT , and we can't find a more * specific response for the given topic. * * By default, we'll show the basic "there's no response" message. * This isn't a very good message in most cases, because it makes an * actor pretty frustratingly un-interactive, which gives the actor * the appearance of a cardboard cut-out. But there's not much * better that the library can do; the potential range of actors * makes a more specific default response impossible. If the default * response were "I don't know about that," it wouldn't work very * well if the actor is someone who only speaks Italian. So, the * best we can do is this generally rather poor default. But that * doesn't mean that authors should resign themselves to a poor * default answer; instead, it means that actors should take care to * override this when defining an actor, because it's usually * possible to find a much better default for a *specific* actor. * * The *usual* way of providing a default response is to define a * DefaultAskTopic (or a DefaultAskTellTopic) and put it in the * actor's topic database. */ defaultAskResponse(fromActor, topic) { mainReport(&noResponseFromMsg, self); } /* * Show the default response to being told of a topic - this is * called when we're the actor in TELL ABOUT , and we * can't find a more specific response for the topic. * * As with defaultAskResponse, this should almost always be * overridden by each actor, since the default response ("there's no * response") doesn't make the actor seem very dynamic. * * The usual way of providing a default response is to define a * DefaultTellTopic (or a DefaultAskTellTopic) and put it in the * actor's topic database. */ defaultTellResponse(fromActor, topic) { mainReport(&noResponseFromMsg, self); } /* the default response for SHOW TO */ defaultShowResponse(byActor, topic) { mainReport(¬InterestedMsg, self); } /* the default response for GIVE TO */ defaultGiveResponse(byActor, topic) { mainReport(¬InterestedMsg, self); } /* the default response for ASK FOR */ defaultAskForResponse(byActor, obj) { mainReport(&noResponseFromMsg, self); } /* default response to being told YES */ defaultYesResponse(fromActor) { mainReport(&noResponseFromMsg, self); } /* default response to being told NO */ defaultNoResponse(fromActor) { mainReport(&noResponseFromMsg, self); } /* default refusal of a command */ defaultCommandResponse(fromActor, topic) { mainReport(&refuseCommand, self, fromActor); } ; /* ------------------------------------------------------------------------ */ /* * An UntakeableActor is one that can't be picked up and moved. */ class UntakeableActor: Actor, Immovable /* use customized messages for some 'Immovable' methods */ cannotTakeMsg = &cannotTakeActorMsg cannotMoveMsg = &cannotMoveActorMsg cannotPutMsg = &cannotPutActorMsg /* TASTE tends to be a bit rude */ dobjFor(Taste) { action() { mainReport(&cannotTasteActorMsg); } } /* * even though we act like an Immovable, we don't count as an * Immovable for listing purposes */ contentsInFixedIn(loc) { return nil; } ; /* * A Person is an actor that represents a human character. This is just * an UntakeableActor with some custom versions of the messages for * taking and moving the actor. */ class Person: UntakeableActor /* customize the messages for trying to take or move me */ cannotTakeMsg = &cannotTakePersonMsg cannotMoveMsg = &cannotMovePersonMsg cannotPutMsg = &cannotPutPersonMsg cannotTasteActorMsg = &cannotTastePersonMsg /* * use a fairly large default bulk, since people are usually fairly * large compared with the sorts of items that one carries around */ bulk = 10 ; /* ------------------------------------------------------------------------ */ /* * Pending response information structure */ class PendingResponseInfo: object construct(issuer, prop, args) { issuer_ = issuer; prop_ = prop; args_ = args; } /* the issuer of the command (and target of the response) */ issuer_ = nil /* the message property and argument list for the message */ prop_ = nil args_ = [] ; /* * Pending Command Information structure. This is an abstract base class * that we subclass for particular ways of representing the command to be * executed. */ class PendingCommandInfo: object construct(issuer) { issuer_ = issuer; } /* * Check to see if this pending command item has a command to * perform. This returns true if we have a command, nil if we're * just a queue placeholder without any actual command to execute. */ hasCommand = true /* execute the command */ executePending(targetActor) { } /* the issuer of the command */ issuer_ = nil /* we're at the start of a "sentence" */ startOfSentence_ = nil ; /* a pending command based on a list of tokens from an input string */ class PendingCommandToks: PendingCommandInfo construct(startOfSentence, issuer, toks) { inherited(issuer); startOfSentence_ = startOfSentence; tokens_ = toks; } /* * Execute the command. We'll parse our tokens and execute the * parsed results. */ executePending(targetActor) { /* parse and execute the tokens */ executeCommand(targetActor, issuer_, tokens_, startOfSentence_); } /* the token list for the command */ tokens_ = nil ; /* a pending command based on a pre-resolved Action and its objects */ class PendingCommandAction: PendingCommandInfo construct(startOfSentence, issuer, action, [objs]) { inherited(issuer); startOfSentence_ = startOfSentence; action_ = action; objs_ = objs; } /* execute the pending command */ executePending(targetActor) { /* invoke the action's main execution method */ try { /* run the action */ newActionObj(CommandTranscript, issuer_, targetActor, action_, objs_...); } catch (TerminateCommandException tcExc) { /* * the command cannot proceed; simply abandon the command * action here */ } } /* the resolved Action to perform */ action_ = nil /* the resolved objects for the action */ objs_ = nil ; /* * A pending command marker. This is not an actual pending command; * rather, it's just a queue marker. We sometimes want to synchronize * some other activity with an actor's progress through its command * queue; for example, we might want one actor to wait until another * actor has executed a particular pending action. These markers can be * used for this kind of synchronization; they move through the queue * like ordinary pending commands, so we can tell if an actor has reached * a particular command by observing the marker's progress through the * queue. */ class PendingCommandMarker: PendingCommandInfo /* I have no command to execute */ hasCommand = nil ; /* ------------------------------------------------------------------------ */ /* * Set the current player character */ setPlayer(actor) { /* remember the new player character */ libGlobal.playerChar = actor; /* set the root global point of view to this actor */ setRootPOV(actor, actor); } frobtads-1.2.3/tads3/lib/adv3/pov.t0000644000175000001440000002504410472354744016135 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - point of view * * This module provides definitions related to point of view and sensory * context. When we generate output, we do so with respect to a * particular point of view; different points of view can result in * different output, because of the viewer's distance from an object, for * example, or because of the presence of obscuring materials between the * viewer and the viewed object. We also generate output in a particular * sensory context, which controls whether or not a message that * describes an object with respect to a particular sense should be * generated at all; for example, if the viewer can't see an object * because of darkness or an obscuring layer of material, messages about * the object's visual appearance should not be generated. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Call a function with a given sensory context. * * The sensory context specifies the source of any messages generated in * the course of the routine we invoke and the sense which those * messages use to convey information. If the player character cannot * sense the source object in the given sense, then we block all * messages generated while calling this function. * * If the source object is nil, this establishes a neutral sense context * in which all messages are visible. * * This can be used for processing events that are not directly * initiated by the player character, such as non-player character * activities or scheduled events (fuses and daemons). The idea is that * anything described in the course of calling our routine is physically * associated with the source object and relates to the given sense, so * if the player character cannot sense the source object, then the * player should not be aware of these happenings and thus should not * see the messages. * * Sense contexts are not nested in their effects - we will show or hide * the messages that our callback routine generates regardless of * whether or not messages are hidden by an enclosing sensory context. * So, this routine effectively switches to the new sense context for * the duration of the callback, eliminating the effect of any enclosing * context. However, we do restore the enclosing sense context before * returning, so there is no lasting net effect on the global sense * context. */ callWithSenseContext(source, sense, func) { return senseContext.withSenseContext(source, sense, func); } /* * Sense context output filter. When the sense context doesn't allow * the player character to sense whatever's going on, we'll block all * output; otherwise, we'll pass output through unchanged. */ senseContext: SwitchableCaptureFilter /* * Recalculate the current sense context. We will check to see if * the player character can sense the current sense context's source * object in the current sense context's sense, and show or hide * output from this point forward accordingly. This can be called * any time conditions change in such a way that the sense context * should be refigured. */ recalcSenseContext() { /* * simply invalidate the cached status; this will ensure that we * recalculate the status the next time we're called upon to * determine whether or not we need to block output */ cached_ = nil; } /* * Get our current blocking status. If we've already cached the * status, we'll return the cached status; otherwise, we'll compute * and cache the new blocking status, based on the current sensory * environment. */ isBlocking() { /* if we haven't cached the status, compute the new status */ if (!cached_) { /* calculate the new status based on the current environment */ isBlocking_ = shouldBlock(); /* we now have a valid cached status */ cached_ = true; } /* return the cached status */ return isBlocking_; } /* our current cached blocking status, and its validity */ isBlocking_ = nil cached_ = true /* * Calculate whether or not I should be blocking output according to * the current game state. Returns true if so, nil if not. */ shouldBlock() { /* * Determine if the new sense context allows messages to be * displayed. If there is no source object, we allow * everything; otherwise, we only allow messages if the player * character can sense the source object in the given sense. */ if (source_ == nil) { /* neutral sense context - allow messages */ return nil; } else { /* * Determine if the player character can sense the given * object. If the source can be sensed with any degree of * transparency other than 'opaque', allow the messages. */ return (libGlobal.playerChar.senseObj(sense_, source_) .trans == opaque); } } /* invoke a callback with a given sense context */ withSenseContext(source, sense, func) { local oldSource, oldSense; /* remember the old sense and source values */ oldSource = source_; oldSense = sense_; /* set up the new sense context */ setSenseContext(source, sense); /* make sure we restore the old status on the way out */ try { /* invoke the callback */ return (func)(); } finally { /* restore the old sense context */ setSenseContext(oldSource, oldSense); } } /* * Set a sense context. */ setSenseContext(source, sense) { /* remember the new setings */ source_ = source; sense_ = sense; /* calculate the new sensory status */ recalcSenseContext(); } /* the source object and sense of the sensory context */ sense_ = nil source_ = nil ; /* ------------------------------------------------------------------------ */ /* * Get the current point-of-view actor - this is the actor who's * performing the action (LOOK AROUND, EXAMINE, SMELL, etc) that's * generating the current description. */ getPOVActor() { return libGlobal.pointOfViewActor; } /* * Get the current point of view. In *most* cases, this is the same as * the point-of-view actor: the actor is looking around with its own * eyes, so it's the point of view. However, this can differ from the * actor when the actor is viewing the location being described through * an intermediary of some kind. For example, if an actor is observing a * remote room through a closed-circuit TV system, the point of view * would be the camera in the remote room (not the TV - the point of view * is intended to be the object that's physically absorbing the light * rays or other sensory equivalents). */ getPOV() { return libGlobal.pointOfView; } /* get the POV actor, returning the given default if there isn't one set */ getPOVActorDefault(dflt) { /* start with the global setting */ local val = libGlobal.pointOfViewActor; /* if that's not nil, return it; otherwise, return the default */ return (val != nil ? val : dflt); } /* get the POV, returning the given default if there isn't one set */ getPOVDefault(dflt) { /* start with the global setting */ local val = libGlobal.pointOfView; /* if that's not nil, return it; otherwise, return the default */ return (val != nil ? val : dflt); } /* * Change the point of view without altering the point-of-view stack */ setPOV(actor, pov) { /* set the new point of view */ libGlobal.pointOfViewActor = actor; libGlobal.pointOfView = pov; } /* * Set the root point of view. This doesn't affect the current point of * view unless there is no current point of view; this merely sets the * outermost default point of view. */ setRootPOV(actor, pov) { local stk = libGlobal.povStack; /* * if there's nothing in the stacked list, set the current point of * view; otherwise, just set the innermost stacked element */ if (stk.length() == 0) { /* there is no point of view, so set the current point of view */ libGlobal.pointOfViewActor = actor; libGlobal.pointOfView = pov; } else { /* set the innermost stacked point of view */ stk[1] = pov; stk[2] = actor; } } /* * Push the current point of view */ pushPOV(actor, pov) { /* stack the current one */ libGlobal.povStack.append(libGlobal.pointOfView); libGlobal.povStack.append(libGlobal.pointOfViewActor); /* set the new point of view */ setPOV(actor, pov); } /* * Pop the most recent point of view pushed */ popPOV() { local stk = libGlobal.povStack; local len; /* check if there's anything left on the stack */ len = stk.length(); if (len != 0) { /* take the most recent element off the stack */ libGlobal.pointOfViewActor = stk[len]; libGlobal.pointOfView = stk[len - 1]; /* take the actor and POV objects off the stack */ stk.removeRange(len - 1, len); } else { /* nothing on the stack - clear the point of view */ libGlobal.pointOfViewActor = nil; libGlobal.pointOfView = nil; } } /* * Clear the point of view and all stacked elements */ clearPOV() { local len; local stk = libGlobal.povStack; /* forget the current point of view */ setPOV(nil, nil); /* drop everything on the stack */ len = stk.length(); stk.removeRange(1, len); } /* * Call a function from a point of view. We'll set the new point of * view, call the function with the given arguments, then restore the * original point of view. */ callFromPOV(actor, pov, funcToCall, [args]) { /* push the new point of view */ pushPOV(actor, pov); /* make sure we pop the point of view no matter how we leave */ try { /* call the function */ (funcToCall)(args...); } finally { /* restore the enclosing point of view on the way out */ popPOV(); } } frobtads-1.2.3/tads3/lib/adv3/browser.t0000644000175000001440000006046111725774262017021 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - browser (Web UI) input/output manager * * This module defines the low-level functions for handling input and * output via the Web UI. * * The functions in this module are designed primarily for internal use * within the library itself. Games should use the higher level objects * and functions defined in input.t and output.t instead of directly * calling the functions defined here. The reason for separating these * functions is to make the UI selection pluggable, so that the same game * can be compiled for either the traditional UI or the Web UI simply by * plugging in the correct i/o module. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Browser globals */ transient browserGlobals: object /* the HTTPServer object for the browser UI session */ httpServer = nil /* * Log file handle. For a LogTypeTranscript file, this is a * LogConsole object; for other types, it's a regular file handle. */ logFile = nil /* logging type (LogTypeXxx from tadsio.h, or nil if not logging) */ logFileType = nil ; /* ------------------------------------------------------------------------ */ /* * Initialize the user interface. The library calls this once at the * start of the interpreter session to set up the UI. For the Web UI, we * create the HTTP server and send connection instructions to the client. */ initUI() { /* * Set up the HTTP server. Listen on the launch address, which is * the address that the client used to reach the external Web server * that launched the interpreter. For local stand-alone launches, * the launch address is nil, so the HTTP server will listen on * localhost, which is just what we need in order to connect to the * local UI. */ local srv = browserGlobals.httpServer = new HTTPServer( getLaunchHostAddr(), nil, 1024*1024); /* send connection instructions to the client */ webSession.connectUI(srv); } /* * Initialize the display. We call this when we first enter the * interpreter, and again at each RESTART, to set up the main game * window's initial layout. We set up the conventional IF screen layout, * with the status line across the top and the transcript/command window * filling the rest of the display. */ initDisplay() { /* set up the command window and status line */ webMainWin.createFrame(commandWin, 'command', '0, statusline.bottom, 100%, 100%'); webMainWin.createFrame(statuslineBanner, 'statusline', '0, 0, 100%, content.height'); /* capture the title string */ local title = mainOutputStream.captureOutput( {: gameMain.setGameTitle() }); /* parse out the contents of the tag */ if (rexSearch('<nocase>[<]title[>](.*)[<]/title[>]', title)) title = rexGroup(1)[3]; /* initialize the statusline window object */ statuslineBanner.init(); statusLine.statusDispMode = StatusModeBrowser; /* set the title */ webMainWin.setTitle(title); /* get the session parameters from the arguments */ local arg = libGlobal.getCommandSwitch('-gameid='); if (arg != nil && arg != '') webSession.launcherGameID = arg; arg = libGlobal.getCommandSwitch('-storagesid='); if (arg != nil && arg != '') webSession.storageSID = arg; arg = libGlobal.getCommandSwitch('-username='); if (arg != nil && arg != '') webSession.launcherUsername = arg; } /* * Shut down the user interface. The library calls this when the game is * about to terminate. */ terminateUI() { /* if we have an HTTP server, shut it down */ if (browserGlobals.httpServer != nil) { /* flush our windows */ webMainWin.flushWin(); commandWin.sendWinEvent('<scrollToBottom/>'); /* end any scripting */ aioSetLogFile(nil, LogTypeTranscript); aioSetLogFile(nil, LogTypeCommand); /* * keep running for a few more minutes, to give clients a chance * to perform final tasks like downloading log files */ ClientSession.shutdownWait(5*60*1000); /* send the shutdown message */ eventPage.sendEvent('<shutdown/>'); /* wait a short time for clients to process the shutdown event */ ClientSession.shutdownWait(5*1000); /* shut down the http server */ browserGlobals.httpServer.shutdown(true); } } /* ------------------------------------------------------------------------ */ /* * Check to see if we're in HTML mode */ checkHtmlMode() { /* * The web UI is always in HTML mode. This is regardless of the * interpreter class, because that only tells us about the * interpreter's own native UI. The actual user interface in Web UI * mode runs in a separate Web browser app, which is inherently HTML * capable. */ return true; } /* ------------------------------------------------------------------------ */ /* * Write text to the main game window */ aioSay(txt) { /* write the text to the main command window */ commandWin.write(txt); /* if we're logging a full transcript, write the text */ if (browserGlobals.logFileType == LogTypeTranscript) browserGlobals.logFile.writeToStream(txt); } /* ------------------------------------------------------------------------ */ /* * Is a script file active? */ readingScript() { return setScriptFile(ScriptReqGetStatus) != nil; } /* * Is an event script active? */ readingEventScript() { local s = setScriptFile(ScriptReqGetStatus); return (s != nil && (s & ScriptFileEvent) != 0); } /* ------------------------------------------------------------------------ */ /* * Get a line of input from the keyboard, with timeout */ aioInputLineTimeout(timeout) { /* check for script input */ local scriptMode = setScriptFile(ScriptReqGetStatus); if (scriptMode != nil) { /* we're in a script, so use the regular input line reader */ local e = inputLineTimeout(timeout); /* * If it's not an end-of-file indication, return the event. An * EOF means that there are no more events in the script, so * return to reading from the live client UI. */ if (e[1] != InEvtEof) { /* echo the input if we're not in quiet mode */ if (e[1] == InEvtLine && !(scriptMode & ScriptFileQuiet)) aioSay(e[2].htmlify() + '\n'); /* log and return the event */ return aioLogInputEvent(e); } } /* * read an input line event from the main command window, log it, and * return it */ return aioLogInputEvent(commandWin.getInputLine(timeout)); } /* * Cancel a suspended input line */ aioInputLineCancel(reset) { /* cancel the input line in the command window */ commandWin.cancelInputLine(reset); } /* ------------------------------------------------------------------------ */ /* * Read an input event */ aioInputEvent(timeout) { /* check for script input */ if (readingEventScript()) { /* we're in a script, so use the regular input line reader */ local e = inputEvent(timeout); /* * If it's not an end-of-file indication, return the event. An * EOF means that there are no more events in the script, so * return to reading from the live client UI. */ if (e[1] != InEvtEof) return aioLogInputEvent(e); } /* read an event from the main command window, log it, and return it */ return aioLogInputEvent(webMainWin.getInputEvent(timeout)); } /* ------------------------------------------------------------------------ */ /* * Show a "More" prompt */ aioMorePrompt() { /* show a More prompt in the main command window */ commandWin.showMorePrompt(); } /* ------------------------------------------------------------------------ */ /* * Clear the screen */ aioClearScreen() { /* clear the main transcript window */ commandWin.clearWindow(); } /* ------------------------------------------------------------------------ */ /* * Show a file selector dialog */ aioInputFile(prompt, dialogType, fileType, flags) { /* * First, try reading from the local console. Even though we're * using the Web UI, there are two special cases where the input will * come from the local (server-side) console instead of from the * browser UI: * * 1. We're reading from an event script. In this case, regardless * of the UI mode, the interpreter reads from a server-side file and * parses the results into an inputFile() result, bypassing any UI * interaction. * * 2. We're running in the Web UI's local stand-alone configuration, * where the browser is actually an integrated window within the * interpreter. This configuration simulates the traditional UI by * running everything locally - the client and server are running on * the same machine, so there's really no distinction between * client-side and server-side. Because everything's local, files * are local, so we want to display traditional local file selector * dialogs. The stand-alone interpreter does this for us via the * standard inputFile() function when it detects this configuration. * * If neither of these special cases apply, inputFile() will return * an error to let us know that it can't show a file dialog in the * current configuration, so we'll continue on to showing the dialog * on the client side via the Web UI. */ local f = inputFile(prompt, dialogType, fileType, flags); /* if that failed, forget the result */ if (f[1] == InFileFailure) f = nil; /* if we got a file, check for warnings */ if (f != nil && f.length() >= 4 && f[4] != nil) { /* keep going until we get a definitive answer */ for (local done = nil ; !done ; ) { /* show the warning dialog */ local d = webMainWin.getInputDialog( InDlgIconWarning, libMessages.inputFileScriptWarning(f[4], f[2]), libMessages.inputFileScriptWarningButtons, 1, 3); /* check the result */ switch (d) { case 0: case 3: /* dialog error or Cancel Script - stop the script */ setScriptFile(nil); /* return a Cancel result */ return [InFileCancel]; case 1: /* "Yes" - proceed */ done = true; break; case 2: /* Choose New File button - show a file dialog */ local fNew = webMainWin.getInputFile( prompt, dialogType, fileType, flags); switch (fNew[1]) { case InFileSuccess: /* success - use the new file, and we're done */ f = fNew; done = true; break; case InFileCancel: /* cancel - repeat the prompt */ break; case InFileFailure: /* dialog error - cancel the script */ setScriptFile(nil); return [InFileCancel]; } } } } /* * if we didn't get a result from a script or from the local console, * tell the client UI to display its file dialog */ if (f == nil) f = webMainWin.getInputFile(prompt, dialogType, fileType, flags); /* log a synthetic <file> event, if applicable */ aioLogInputEvent( ['<file>', f[1] != InFileSuccess ? '' : dataType(f[2]) == TypeObject && !f[2].ofKind(FileName) ? 't' : f[2]]); /* return the file information */ return f; } /* ------------------------------------------------------------------------ */ /* * Show an input dialog */ aioInputDialog(icon, prompt, buttons, defaultButton, cancelButton) { /* check for script input */ local d = nil; if (readingEventScript()) { /* we're in a script, so use the regular dialog event reader */ d = inputDialog(icon, prompt, buttons, defaultButton, cancelButton); /* if it failed, forget the result */ if (d == 0) d = nil; } /* if we didn't get script input, show the dialog via the client UI */ if (d == nil) d = webMainWin.getInputDialog(icon, prompt, buttons, defaultButton, cancelButton); /* log a synthetic <dialog> event, if applicable */ aioLogInputEvent(['<dialog>', d]); /* return the result */ return d; } /* ------------------------------------------------------------------------ */ /* * Set/remove the output logging file */ aioSetLogFile(fname, typ = LogTypeTranscript) { /* if there's currently a log file open, close it */ local log = browserGlobals.logFile; if (log != nil) { switch (browserGlobals.logFileType) { case LogTypeTranscript: /* for a transcript, we have a log console as the handle */ log.closeConsole(); break; default: /* for other types, we have a regular file handle */ try { log.closeFile(); } catch (Exception exc) { /* ignore errors, as we have no way to return them */ } break; } /* we've closed the handle, so forget it */ log = nil; } /* presume success */ local ok = true; /* if there's a filename, create a new console for this file */ if (fname != nil) { /* create the output handle according to the type */ switch (typ) { case LogTypeTranscript: /* * full transcript - create a log console, which will do the * standard output formatting for us */ log = new LogConsole(fname, nil, 80); break; case LogTypeCommand: case LogTypeScript: /* for other types, create an ordinary text file */ try { /* open the log file */ log = File.openTextFile(fname, FileAccessWrite, nil); /* for an event script, write the <eventscript> opener */ if (typ == LogTypeScript) log.writeFile('<eventscript>\n'); } catch (Exception exc) { /* if anything went wrong, we have no log file */ log = nil; } break; default: throw RuntimeError.newRuntimeError(2306, 'bad log file type'); } /* we failed if the log handle is nil */ if (log == nil) { ok = nil; typ = nil; } } else { /* no longer logging */ typ = nil; } /* remember the new handle and log type */ browserGlobals.logFile = log; browserGlobals.logFileType = typ; /* return the success/failure indicator */ return ok; } /* * Log an input event. We call this internally from each of the event * input routines to add the event to any event or command log we're * creating. */ aioLogInputEvent(evt) { /* if the system is maintaining its own input log, write it there */ logInputEvent(evt); /* get the script globals */ local ltyp = browserGlobals.logFileType; local log = browserGlobals.logFile; /* get the basic event parameters */ local evtType = evt[1]; local param = (evt.length() > 1 ? evt[2] : nil); /* format the event based on the event type */ switch (ltyp) { case LogTypeTranscript: /* transcript - echo command line input */ if (evt[1] == InEvtLine) log.writeToStream(evt[2].htmlify() + '\n'); break; case LogTypeCommand: /* command script - write command inputs only */ if (evt[1] == InEvtLine) log.writeFile('>' + evt[2] + '\n'); break; case LogTypeScript: /* event script - write all event types */ switch (evtType) { case InEvtKey: log.writeFile('<key>' + evtCharForScript(param)); break; case InEvtTimeout: log.writeFile('<timeout>' + param); break; case InEvtHref: log.writeFile('<href>' + param); break; case InEvtNoTimeout: log.writeFile('<notimeout>'); break; case InEvtEof: log.writeFile('<eof>'); break; case InEvtLine: log.writeFile('<line>' + param); break; case InEvtSysCommand: log.writeFile('<command>' + param); break; case InEvtEndQuietScript: log.writeFile('<endqs>'); break; default: /* if it's a string value, it's the literal event tag */ if (dataType(evtType) == TypeSString) log.writeFile(evtType + param); break; } /* add a newline at the end of the event line */ log.writeFile('\n'); break; } /* * return the event, so that the caller can conveniently return it * after logging it */ return evt; } /* * Get an InEvtKey event parameter in suitable format for script file * output. This returns the key as it appears in the event, except that * ASCII control characters are translated to '[ctrl-X]'. */ evtCharForScript(c) { if (c.toUnicode(1) < 32) { /* it's a control character - return the [ctrl-X] sequence */ return '[ctrl-<<makeString(c.toUnicode(1) + 64)>>]'; } else { /* return everything else as it appears in the event descriptor */ return c; } } /* ------------------------------------------------------------------------ */ /* * Generate a string to show hyperlinked text. The browser UI is always * in HTML mode, so we unconditionally generate the hyperlink. * * If the display text is included, we'll generate the entire link, * including the <A HREF> tag, the hyperlinked text contents, and the * </A> end tag. If the text is omitted, we'll simply generate the <A * HREF> tag itself, leaving it to the caller to display the text and the * </A>. * * The optional 'flags' is a combination of AHREF_xxx flags indicating * any special properties of the hyperlink. */ aHref(href, txt?, title?, flags = 0) { /* figure extra properties, based on the flags */ local props = ''; if (flags & AHREF_Plain) props += 'class="plain" '; /* generate the <A HREF>, text, and </A>, as applicable */ return '<a <<props>> href="<<href.findReplace('"', '%22')>>"<< (title != nil ? ' title="' + title.findReplace('"', '"') + '"' : '') >> onclick="javascript:return gamehref(event,\'<< href.findReplace(['\'', '"'], ['\\\'', '\'+String.fromCharCode(34)+\'']) >>\', \'main.command\', this);"><.a><< (txt != nil ? txt + '<./a></a>' : '')>>'; } /* ------------------------------------------------------------------------ */ /* * Generate a string to show hyperlinked text, with alternate text if * we're not in HTML mode. The browser UI is always in HTML mode, so we * unconditionally generate the hyperlink. */ aHrefAlt(href, linkedText, altText, title?) { return aHref(href, linkedText, title); } /* ------------------------------------------------------------------------ */ /* * The standard main command window. */ transient commandWin: WebCommandWin ; /* ------------------------------------------------------------------------ */ /* * Generate HTML to wrap the left/right portions of the status line. The * basic status line has three stages: stage 0 precedes the left portion, * stage 1 comes between the left and right portions, and stage 2 follows * the right portion. If we're listing exits, we get two more stages: * stage 3 precedes the exit listing, stage 4 follows it. */ statusHTML(stage) { switch(stage) { case 0: /* start the left-aligned portion */ return '<div class="statusleft">'; case 1: /* close the left portion, and start the right-aligned portion */ return '</div><div class="statusright">'; case 2: /* * Close the right portion, and break clear of the floating * sections. The break is necessary to make sure that the * contents of the two sections count in the window height; some * browsers don't include floating boxes in the content height, * so we need to manually extend the main vertical box's height * past the floating sections. */ return '</div><div class="statusStrut"></div>'; case 3: case 4: /* before/after exit listing - we have nothing to add here */ return ''; default: return ''; } } /* ------------------------------------------------------------------------ */ /* * Web Banner Window. This is designed as a *partial* drop-in * replacement for the BannerWindow class, using Web UI windows as * implemented in the core TADS javascript client. * * This class is designed to be mixed with a WebWindow subclass. * * This isn't a complete replacement for BannerWindow, because the layout * model for the Web UI is different from the banner window model (the * Web UI model is better and more flexible). This class implements the * parts of the BannerWindow API related to the stream-oriented output to * the window, so you shouldn't have to change anything that writes HTML * text to the window. However, you will have to rework code that sets * up the window's layout to use the Web UI model. */ class WebBannerWin: OutputStreamWindow /* * Initialize. Call this when first displaying the window in the UI. */ init() { /* set up our output stream */ createOutputStream(); } /* create our output stream subclass */ createOutputStreamObj() { return new transient WebWinOutputStream(self); } /* flush output */ flushBanner() { flushWin(); } /* write text */ writeToBanner(txt) { outputStream_.writeToStream(txt); } /* * Banner window size settings. We simply ignore these; callers must * rework their layout logic for the Web UI, since the javascript * layout system is so different. */ setSize(siz, units, advisory) { } sizeToContents() { } ; /* * Output stream for web banner windows */ class WebWinOutputStream: OutputStream /* construct */ construct(win) { /* do the base class construction */ inherited(); /* save our window */ win_ = win; } /* ignore preinit - we're always created dynamically */ execute() { } /* write to the underlying window */ writeFromStream(txt) { /* add the text to the window */ win_.write(txt); } /* our status line window */ win_ = nil ; /* ------------------------------------------------------------------------ */ /* * The basic status line window. The "banner" in the name is historical, * because the traditional console UI implements the status line as a * banner window. We don't actually have banner windows in the Web UI; * we use iframes instead. But we keep the name to make it easier to * port games written for the traditional UI to the Web UI. */ transient statuslineBanner: WebStatusWin, WebBannerWin ; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/events.t��������������������������������������������������������������0000644�0001750�0000144�00000130225�12014206026�016612� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: events * * This module defines the event framework. An event is a programmed * operation that occurs at a particular point in the game; an event can * be turn-based, in which case it occurs after a given number of turns * has elapsed, or it can occur in real time, which means that it occurs * after a particular interval of time has elapsed. */ #include "adv3.h" #include <dict.h> #include <gramprod.h> /* ------------------------------------------------------------------------ */ /* * Run the main scheduling loop. This continues until we encounter an * end-of-file error reading from the console, or a QuitException is * thrown to terminate the game. */ runScheduler() { /* keep going until we quit the game */ for (;;) { /* catch the exceptions that terminate the game */ try { /* start with an empty list of schedulable items */ local vec = new Vector(10); /* find the lowest time at which something is ready to run */ local minTime = nil; foreach (local cur in Schedulable.allSchedulables) { /* get this item's next eligible run time */ local curTime = cur.getNextRunTime(); /* * if it's not nil, and it's equal to or below the * lowest we've seen so far, note it */ if (curTime != nil && (minTime == nil || curTime <= minTime)) { /* * if this is different from the current minimum * schedulable time, clear out the list of * schedulables, because the list keeps track of the * items at the lowest time only */ if (minTime != nil && curTime < minTime) vec.removeRange(1, vec.length()); /* add this item to the list */ vec.append(cur); /* note the new lowest schedulable time */ minTime = curTime; } } /* * if nothing's ready to run, the game is over by default, * since we cannot escape this state - we can't ourselves * change anything's run time, so if nothing's ready to run * now, we won't be able to change that, and so nothing will * ever be ready to run */ if (minTime == nil) { "\b[Error: nothing is available for scheduling - terminating]\b"; return; } /* * Advance the global turn counter by the amount of game * clock time we're consuming now. */ libGlobal.totalTurns += minTime - Schedulable.gameClockTime; /* * advance the game clock to the minimum run time - nothing * interesting happens in game time until then, so we can * skip straight ahead to this time */ Schedulable.gameClockTime = minTime; /* calculate the schedule order for each item */ vec.forEach({x: x.calcScheduleOrder()}); /* * We have a list of everything schedulable at the current * game clock time. Sort the list in ascending scheduling * order, so that the higher priority items come first in * the list. */ vec = vec.sort( SortAsc, {a, b: a.scheduleOrder - b.scheduleOrder}); /* * Run through the list and run each item. Keep running * each item as long as it's ready to run - that is, as long * as its schedulable time equals the game clock time. */ vecLoop: foreach (local cur in vec) { /* run this item for as long as it's ready to run */ while (cur.getNextRunTime() == minTime) { try { /* * execute this item - if it doesn't want to be * called again without considering other * objects, stop looping and refigure the * scheduling order from scratch */ if (!cur.executeTurn()) break vecLoop; } catch (Exception exc) { /* * The scheduled operation threw an exception. * If the schedulable's next run time didn't get * updated, then the same schedulable will be * considered ready to run again immediately on * the next time through the loop. It's quite * possible in this case that we'll simply repeat * the operation that threw the exception and get * right back here again. If this happens, it * will effectively starve all of the other * schedulables. To ensure that other * schedulables get a chance to run before we try * this erroneous operation again, advance its * next run time by one unit if it hasn't already * been advanced. */ if (cur.getNextRunTime() == minTime) cur.incNextRunTime(1); /* re-throw the exception */ throw exc; } } } } catch (EndOfFileException eofExc) { /* end of file reading command input - we're done */ return; } catch (QuittingException quitExc) { /* explicitly quitting - we're done */ return; } catch (RestartSignal rsSig) { /* * Restarting - re-throw the signal for handling in the * system startup code. Note that we explicitly catch this * signal, only to rethrow it, because we'd otherwise flag it * as an unhandled error in the catch-all Exception handler. */ throw rsSig; } catch (RuntimeError rtErr) { /* if this is a debugger error of some kind, re-throw it */ if (rtErr.isDebuggerSignal) throw rtErr; /* display the error, but keep going */ "\b[<<rtErr.displayException()>>]\b"; } catch (TerminateCommandException tce) { /* * Aborted command - ignore it. This is most like to occur * when a fuse, daemon, or the like tries to terminate itself * with this exception, thinking it's operating in a normal * command execution environment. As a convenience, simply * ignore these exceptions so that any code can use them to * abort everything and return to the main scheduling loop. */ } catch (ExitSignal es) { /* ignore this, just as we ignore TerminateCommandException */ } catch (ExitActionSignal eas) { /* ignore this, just as we ignore TerminateCommandException */ } catch (Exception exc) { /* some other unhandled exception - display it and keep going */ "\b[Unhandled exception: <<exc.displayException()>>]\b"; } } } /* ------------------------------------------------------------------------ */ /* * An item that can be scheduled for time-based notifications. The main * scheduler loop in runScheduler() operates on objects of this class. * * Note that we build a list of all Schedulable instances during * pre-initialization. If any Schedulable objects are dynamically * created, they must be added to the list explicitly after creation in * order for the event manager to schedule them for execution. The * default constructor does this automatically, so subclasses can simply * inherit our constructor to be added to the master list. */ class Schedulable: object /* construction - add myself to the Schedulable list */ construct() { /* * Add myself to the master list of Schedulable instances. Note * that we must update the list in the Schedulable class itself. */ Schedulable.allSchedulables += self; } /* * Get the next time (on the game clock) at which I'm eligible for * execution. We won't receive any scheduling notifications until * this time. If this object doesn't want any scheduling * notifications, return nil. */ getNextRunTime() { return nextRunTime; } /* advance my next run time by the given number of clock units */ incNextRunTime(amt) { if (nextRunTime != nil) nextRunTime += amt; } /* * Notify this object that its scheduled run time has arrived. This * should perform the scheduled task. If the scheduled task takes * any game time, the object's internal next run time should be * updated accordingly. * * The scheduler will invoke this method of the same object * repeatedly for as long as its nextRunTime remains unchanged AND * this method returns true. If the object's scheduling priority * changes relative to other schedulable objects, it should return * nil here to tell the scheduler to recalculate scheduling * priorities. */ executeTurn() { return true; } /* * Scheduling order. This determines which item goes first when * multiple items are schedulable at the same time (i.e., they all * have the same getNextRunTime() values). The item with the lowest * number here goes first. * * This should never be evaluated except immediately after a call to * calcScheduleOrder. */ scheduleOrder = 100 /* * Calculate the scheduling order, returning the order value and * storing it in our property scheduleOrder. This is used to * calculate and cache the value prior to sorting a list of * schedulable items. We use this two-step approach (first * calculate, then sort) so that we avoid repeatedly evaluating a * complex calculation, if indeed there is a complex calculation to * perform. * * By default, we assume that the schedule order is static, so we * simply leave our scheduleOrder property unchanged and return its * present value. */ calcScheduleOrder() { return scheduleOrder; } /* my next running time, in game clock time */ nextRunTime = nil /* * A class variable giving the current game clock time. This is a * class variable because there's only one global game clock. The * game clock starts at zero and increments in game time units; a * game time unit is the arbitrary quantum of time for our event * scheduling system. */ gameClockTime = 0 /* * A list of all of the Schedulable objects in the game. We set this * up during pre-initialization; if any Schedulable instances are * created dynamically, they must be explicitly added to this list * after creation. */ allSchedulables = nil ; /* * Pre-initializer: build the master list of Schedulable instances */ PreinitObject /* * Execute preinitialization. Build a list of all of the schedulable * objects in the game, so that we can scan this list quickly during * play. */ execute() { local vec; /* set up an empty vector to hold the schedulable objects */ vec = new Vector(32); /* add all of the Schedulable instances to the vector */ forEachInstance(Schedulable, {s: vec.append(s)}); /* save the list of Schedulable instances as an ordinary list */ Schedulable.allSchedulables = vec.toList(); } ; /* ------------------------------------------------------------------------ */ /* * Basic Event Manager. This is a common base class for the game-time * and real-time event managers. This class handles the details of * managing the event queue; the subclasses must define the specifics of * event timing. */ class BasicEventManager: object /* add an event */ addEvent(event) { /* append the event to our list */ events_.append(event); } /* remove an event */ removeEvent(event) { /* remove the event from our list */ events_.removeElement(event); } /* * Remove events matching the given object and property combination. * We remove all events that match both the object and property * (events matching only the object or only the property are not * affected). * * This is provided mostly as a convenience for cases where an event * is known to be uniquely identifiable by its object and property * values; this saves the caller the trouble of keeping track of the * Event object created when the event was first registered. * * When a particular object/property combination might be used in * several different events, it's better to keep a reference to the * Event object representing each event, and use removeEvent() to * remove the specific Event object of interest. * * Returns true if we find any matching events, nil if not. */ removeMatchingEvents(obj, prop) { local found; /* * Scan our list, and remove each event matching the parameters. * Note that it's safe to remove things from a vector that we're * iterating with foreach(), since foreach() makes a safe copy * of the vector for the iteration. */ found = nil; foreach (local cur in events_) { /* if this one matches, remove it */ if (cur.eventMatches(obj, prop)) { /* remove the event */ removeEvent(cur); /* note that we found a match */ found = true; } } /* return our 'found' indication */ return found; } /* * Remove the current event - this is provided for convenience so * that an event can cancel itself in the course of its execution. * * Note that this has no effect on the current event execution - * this simply prevents the event from receiving additional * notifications in the future. */ removeCurrentEvent() { /* remove the currently active event from our list */ removeEvent(curEvent_); } /* event list - each instance must initialize this to a vector */ // events_ = nil ; /* * Event Manager. This is a schedulable object that keeps track of * fuses and daemons, and schedules their execution. */ eventManager: BasicEventManager, Schedulable /* * Use a scheduling order of 1000 to ensure we go after all actors. * By default, actors use scheduling orders in the range 100 to 400, * so our order of 1000 ensures that fuses and daemons run after all * characters on a given turn. */ scheduleOrder = 1000 /* * Get the next run time. We'll find the lowest run time of our * fuses and daemons and return that. */ getNextRunTime() { local minTime; /* * run through our list of events, and find the event that is * scheduled to run at the lowest game clock time */ minTime = nil; foreach (local cur in events_) { local curTime; /* get this item's scheduled run time */ curTime = cur.getNextRunTime(); /* if it's not nil and it's the lowest so far, remember it */ if (curTime != nil && (minTime == nil || curTime < minTime)) minTime = curTime; } /* return the minimum time we found */ return minTime; } /* * Execute a turn. We'll execute each fuse and each daemon that is * currently schedulable. */ executeTurn() { local lst; /* * build a list of all of our events with the current game clock * time - these are the events that are currently schedulable */ lst = events_.subset({x: x.getNextRunTime() == Schedulable.gameClockTime}); /* execute the items in this list */ executeList(lst); /* no change in scheduling priorities */ return true; } /* * Execute a command prompt turn. We'll execute each * per-command-prompt daemon. */ executePrompt() { /* execute all of the per-command-prompt daemons */ executeList(events_.subset({x: x.isPromptDaemon})); } /* * internal service routine - execute the fuses and daemons in the * given list, in eventOrder priority order */ executeList(lst) { /* sort the list in ascending event order */ lst = lst.toList() .sort(SortAsc, {a, b: a.eventOrder - b.eventOrder}); /* run through the list and execute each item ready to run */ foreach (local cur in lst) { /* remember our old active event, then establish the new one */ local oldEvent = curEvent_; curEvent_ = cur; /* make sure we restore things on the way out */ try { local pc; /* have the player character note the pre-event conditions */ pc = gPlayerChar; pc.noteConditionsBefore(); /* cancel any sense caching currently in effect */ libGlobal.disableSenseCache(); /* execute the event */ cur.executeEvent(); /* * if the player character is the same as it was, ask * the player character to note any change in conditions */ if (gPlayerChar == pc) pc.noteConditionsAfter(); } catch (Exception exc) { /* * If an event throws an exception out of its handler, * remove the event from the active list. If we were to * leave it active, we'd go back and execute the same * event again the next time we look for something to * schedule, and that would in turn probably just * encounter the same exception - so we'd be stuck in an * infinite loop executing this erroneous code. To * ensure that we don't get stuck, remove the event. */ removeCurrentEvent(); /* re-throw the exception */ throw exc; } finally { /* restore the enclosing current event */ curEvent_ = oldEvent; } } } /* our list of fuses and daemons */ events_ = static new Vector(20) /* the event currently being executed */ curEvent_ = nil ; /* * Pseudo-action subclass to represent the action environment while * processing a daemon, fuse, or other event. */ class EventAction: Action /* * event actions are internal system actions; they don't consume * additional turns themselves, since they run between player turns */ actionTime = 0; ; /* * A basic event, for game-time and real-time events. */ class BasicEvent: object /* construction */ construct(obj, prop) { /* remember the object and property to call at execution */ obj_ = obj; prop_ = prop; } /* * Execute the event. This must be overridden by the subclass to * perform the appropriate operation when executed. In particular, * the subclass must reschedule or unschedule the event, as * appropriate. */ executeEvent() { } /* does this event match the given object/property combination? */ eventMatches(obj, prop) { return obj == obj_ && prop == prop_; } /* * Call our underlying method. This is an internal routine intended * for use by the executeEvent() implementations. */ callMethod() { /* * invoke the method in our sensory context, and in a simulated * action environment */ withActionEnv(EventAction, gPlayerChar, {: callWithSenseContext(source_, sense_, {: obj_.(self.prop_)() }) }); } /* the object and property we invoke */ obj_ = nil prop_ = nil /* * The sensory context of the event. When the event fires, we'll * execute its method in this sensory context, so that any messages * generated will be displayed only if the player character can * sense the source object in the given sense. * * By default, these are nil, which means that the event's messages * will be displayed (or, at least, they won't be suppressed because * of the sensory context). */ source_ = nil sense_ = nil ; /* * Base class for fuses and daemons */ class Event: BasicEvent /* our next run time, in game clock time */ getNextRunTime() { return nextRunTime; } /* delay our scheduled run time by the given number of turns */ delayEvent(turns) { nextRunTime += turns; } /* remove this event from the event manager */ removeEvent() { eventManager.removeEvent(self); } /* * Event order - this establishes the order we run relative to other * events scheduled to run at the same game clock time. Lowest * number goes first. By default, we provide an event order of 100, * which should leave plenty of room for custom events before and * after default events. */ eventOrder = 100 /* creation */ construct(obj, prop) { /* inherit default handling */ inherited(obj, prop); /* add myself to the event manager's active event list */ eventManager.addEvent(self); } /* * our next execution time, expressed in game clock time; by * default, we'll set this to nil, which means that we are not * scheduled to execute at all */ nextRunTime = nil /* by default, we're not a per-command-prompt daemon */ isPromptDaemon = nil ; /* * Fuse. A fuse is an event that fires once at a given time in the * future. Once a fuse is executed, it is removed from further * scheduling. */ class Fuse: Event /* * Creation. 'turns' is the number of turns in the future at which * the fuse is executed; if turns is 0, the fuse will be executed on * the current turn. */ construct(obj, prop, turns) { /* inherit the base class constructor */ inherited(obj, prop); /* * set my scheduled time to the current game clock time plus the * number of turns into the future */ nextRunTime = Schedulable.gameClockTime + turns; } /* execute the fuse */ executeEvent() { /* call my method */ callMethod(); /* a fuse fires only once, so remove myself from further scheduling */ eventManager.removeEvent(self); } ; /* * Sensory-context-sensitive fuse - this is a fuse with an explicit * sensory context. We'll run the fuse in its sense context, so any * messages generated will be visible only if the given source object is * reachable by the player character in the given sense. * * Conceptually, the source object is considered the source of any * messages that the fuse generates, and the messages pertain to the * given sense; so if the player character cannot sense the source * object in the given sense, the messages should not be displayed. For * example, if the fuse will describe the noise made by an alarm clock * when the alarm goes off, the source object would be the alarm clock * and the sense would be sound; this way, if the player character isn't * in hearing range of the alarm clock when the alarm goes off, we won't * display messages about the alarm noise. */ class SenseFuse: Fuse construct(obj, prop, turns, source, sense) { /* inherit the base constructor */ inherited(obj, prop, turns); /* remember our sensory context */ source_ = source; sense_ = sense; } ; /* * Daemon. A daemon is an event that fires repeatedly at given * intervals. When a daemon is executed, it is scheduled again for * execution after its interval elapses again. */ class Daemon: Event /* * Creation. 'interval' is the number of turns between invocations * of the daemon; this should be at least 1, which causes the daemon * to be invoked on each turn. The first execution will be * (interval-1) turns in the future - so if interval is 1, the * daemon will first be executed on the current turn, and if * interval is 2, the daemon will be executed on the next turn. */ construct(obj, prop, interval) { /* inherit the base class constructor */ inherited(obj, prop); /* * an interval of less than 1 is meaningless, so make sure it's * at least 1 */ if (interval < 1) interval = 1; /* remember my interval */ interval_ = interval; /* * set my initial execution time, in game clock time - add one * less than the interval to the current game clock time, so * that we count the current turn as yet to elapse for the * purposes of the interval before the daemon's first execution */ nextRunTime = Schedulable.gameClockTime + interval - 1; } /* execute the daemon */ executeEvent() { /* call my method */ callMethod(); /* advance our next run time by our interval */ nextRunTime += interval_; } /* our execution interval, in turns */ interval_ = 1 ; /* * Sensory-context-sensitive daemon - this is a daemon with an explicit * sensory context. This is the daemon counterpart of SenseFuse. */ class SenseDaemon: Daemon construct(obj, prop, interval, source, sense) { /* inherit the base constructor */ inherited(obj, prop, interval); /* remember our sensory context */ source_ = source; sense_ = sense; } ; /* * Command Prompt Daemon. This is a special type of daemon that * executes not according to the game clock, but rather once per command * prompt. The system executes all of these daemons just before each * time it prompts for a command line. */ class PromptDaemon: Event /* execute the daemon */ executeEvent() { /* * call my method - there's nothing else to do for this type of * daemon, since our scheduling is not affected by the game * clock */ callMethod(); } /* flag: we are a special per-command-prompt daemon */ isPromptDaemon = true ; /* * A one-time-only prompt daemon is a regular command prompt daemon, * except that it fires only once. After it fires once, the daemon * automatically deactivates itself, so that it won't fire again. * * Prompt daemons are occasionally useful for non-recurring processing, * when you want to defer some bit of code until a "safe" time between * turns. In these cases, the regular PromptDaemon is inconvenient to * use because it automatically recurs. This subclass is handy for these * cases, since it lets you schedule some bit of processing for a single * deferred execution. * * One special situation where one-time prompt daemons can be handy is in * triggering conversational events - such as initiating a conversation - * at the very beginning of the game. Initiating a conversation can only * be done from within an action context, but no action context is in * effect during the game's initialization. An easy way to deal with * this is to create a one-time prompt daemon during initialization, and * then trigger the event from the daemon's callback method. The prompt * daemon will set up a daemon action environment just before the first * command prompt is displayed, at which point the callback will be able * to trigger the event as though it were in ordinary action handler * code. */ class OneTimePromptDaemon: PromptDaemon executeEvent() { /* execute as normal */ inherited(); /* remove myself from the event list, so that I don't fire again */ removeEvent(); } ; /* ------------------------------------------------------------------------ */ /* * Real-Time Event Manager. This object manages all of the game's * real-time events, which are events that occur according to elapsed * real-world time. */ realTimeManager: BasicEventManager, InitObject /* * Get the elapsed game time at which the next real-time event is * scheduled. This returns a value which can be compared to that * returned by getElapsedTime(): if this value is less than or equal * to the value from getElapsedTime(), then the next event is reay * for immediate execution; otherwise, the result of subtracting * getElapsedTime() from our return value gives the number of * milliseconds until the next event is schedulable. * * Note that we don't calculate the delta to the next event time, * but instead return the absolute time, because the caller might * need to perform extra processing before using our return value. * If we returned a delta, that extra processing time wouldn't be * figured into the caller's determination of event schedulability. * * If we return nil, it means that there are no scheduled real-time * events. */ getNextEventTime() { local tMin; /* * run through our event list and find the event with the lowest * scheduled run time */ tMin = nil; foreach (local cur in events_) { local tCur; /* get the current item's time */ tCur = cur.getEventTime(); /* * if this one has a valid time, and we don't have a valid * time yet or this one is sooner than the soonest one we've * seen so far, note this one as the soonest so far */ if (tMin == nil || (tCur != nil && tCur < tMin)) { /* this is the soonest so far */ tMin = tCur; } } /* return the soonest event so far */ return tMin; } /* * Run any real-time events that are ready to execute, then return * the next event time. The return value has the same meaning as * that of getNextEventTime(). */ executeEvents() { local tMin; /* * Keep checking as long as we find anything to execute. Each * time we execute an event, we might consume enough time that * an item earlier in our queue that we originally dismissed as * unready has become ready to run. */ for (;;) { local foundEvent; /* we haven't yet run anything on this pass */ foundEvent = nil; /* we haven't found anything schedulable on this pass yet */ tMin = nil; /* run each event whose time is already here */ foreach (local cur in events_) { local tCur; /* * If this event has a non-nil time, and its time is * less than or equal to the current system clock time, * run this event. All event times are in terms of the * game elapsed time. * * If this event isn't schedulable, at least check to * see if it's the soonest schedulable event so far. */ tCur = cur.getEventTime(); if (tCur != nil && tCur <= getElapsedTime()) { /* cancel any sense caching currently in effect */ libGlobal.disableSenseCache(); /* execute this event */ cur.executeEvent(); /* note that we executed something */ foundEvent = true; } else if (tMin == nil || (tCur != nil && tCur < tMin)) { /* it's the soonest event so far */ tMin = tCur; } } /* if we didn't execute anything on this pass, stop scanning */ if (!foundEvent) break; } /* return the time of the next event */ return tMin; } /* * Get the current game elapsed time. This is the number of * milliseconds that has elapsed since the game was started, * counting only the continuous execution time. When the game is * saved, we save the elapsed time at that point; when the game is * later restored, we project that saved time backwards from the * current real-world time at restoration to get the real-world time * where the game would have started if it had actually been played * continuously in one session. */ getElapsedTime() { /* * return the current system real-time counter minus the virtual * starting time */ return getTime(GetTimeTicks) - startingTime; } /* * Set the current game elapsed time. This can be used to freeze * the real-time clock - a caller can note the elapsed game time at * one point by calling getElapsedTime(), and then pass the same * value to this routine to ensure that no real time can effectively * pass between the two calls. */ setElapsedTime(t) { /* * set the virtual starting time to the current system real-time * counter minus the given game elapsed time */ startingTime = getTime(GetTimeTicks) - t; } /* * The imaginary real-world time of the starting point of the game, * treating the game as having been played from the start in one * continous session. Whenever we restore a saved game, we project * backwards from the current real-world time at restoration by the * amount of continuous elapsed time in the saved game to find the * point at which the game would have started if it had been played * continuously in one session up to the restored point. * * We set a static initial value for this, using the interpreter's * real-time clock value at compilation time. This ensures that * we'll have a meaningful time base if any real-time events are * created during pre-initialization. This static value will only be * in effect during preinit; we're an InitObject, so our execute() * method will be invoked at run-time start-up, and at that point * we'll reset the zero point to the actual run-time start time. */ startingTime = static getTime(GetTimeTicks) /* * Initialize at run-time startup. We want to set the zero point as * the time when the player actually started playing the game (any * time we spent in pre-initialization doesn't count on the real-time * clock, since it's not part of the game per se). */ execute() { /* * note the real-time starting point of the game, so we can * calculate the elapsed game time later */ startingTime = getTime(GetTimeTicks); } /* * save the elapsed time so far - this is called just before we save * a game so that we can pick up where we left off on the elapsed * time clock when we restore the saved game */ saveElapsedTime() { /* remember the elapsed time so far */ elapsedTimeAtSave = getElapsedTime(); } /* * Restore the elapsed time - this is called just after we restore a * game. We'll project the saved elapsed time backwards to figure * the imaginary starting time the game would have had if it had * been played in one continuous session rather than being saved and * restored. */ restoreElapsedTime() { /* * project backwards from the current time by the saved elapsed * time to get the virtual starting point that will give us the * same current elapsed time on the system real-time clock */ startingTime = getTime(GetTimeTicks) - elapsedTimeAtSave; } /* our event list */ events_ = static new Vector(20) /* the event currently being executed */ curEvent_ = nil /* * saved elapsed time - we use this to figure the virtual starting * time when we restore a saved game */ elapsedTimeAtSave = 0 ; /* * Real-time manager: pre-save notification receiver. When we're about * to save the game, we'll note the current elapsed game time, so that * when we later restore the game, we can figure the virtual starting * point that will give us the same effective elapsed time on the system * real-time clock. */ PreSaveObject execute() { /* * remember the elapsed time at the point we saved the game, so * that we can restore it later */ realTimeManager.saveElapsedTime(); } ; /* * Real-time manager: post-restore notification receiver. Immediately * after we restore a game, we'll tell the real-time manager to refigure * the virtual starting point of the game based on the saved elapsed * time. */ PostRestoreObject execute() { /* figure the new virtual starting time */ realTimeManager.restoreElapsedTime(); } ; /* * Real-Time Event. This is an event that occurs according to elapsed * wall-clock time in the real world. */ class RealTimeEvent: BasicEvent /* * Get the elapsed real time at which this event is triggered. This * is a time value in terms of realTimeManager.getElapsedTime(). */ getEventTime() { /* by default, simply return our eventTime value */ return eventTime; } /* construction */ construct(obj, prop) { /* inherit default handling */ inherited(obj, prop); /* add myself to the real-time event manager's active list */ realTimeManager.addEvent(self); } /* remove this event from the real-time event manager */ removeEvent() { realTimeManager.removeEvent(self); } /* our scheduled event time */ eventTime = 0 ; /* * Real-time fuse. This is an event that fires once at a specified * elapsed time into the game. */ class RealTimeFuse: RealTimeEvent /* * Creation. 'delta' is the amount of real time (in milliseconds) * that should elapse before the fuse is executed. If 'delta' is * zero or negative, the fuse will be schedulable immediately. */ construct(obj, prop, delta) { /* inherit default handling */ inherited(obj, prop); /* * set my scheduled time to the current game elapsed time plus * the delta - this will give us the time in terms of elapsed * game time at which we'll be executed */ eventTime = realTimeManager.getElapsedTime() + delta; } /* execute the fuse */ executeEvent() { /* call my method */ callMethod(); /* a fuse fires only once, so remove myself from further scheduling */ realTimeManager.removeEvent(self); } ; /* * Sensory-context-sensitive real-time fuse. This is a real-time fuse * with an explicit sensory context. */ class RealTimeSenseFuse: RealTimeFuse construct(obj, prop, delta, source, sense) { /* inherit the base constructor */ inherited(obj, prop, delta); /* remember our sensory context */ source_ = source; sense_ = sense; } ; /* * Real-time daemon. This is an event that occurs repeatedly at given * real-time intervals. When a daemon is executed, it is scheduled * again for execution after its real-time interval elapses again. The * daemon's first execution will occur one interval from the time at * which the daemon is created. * * If a daemon is executed late (because other, more pressing tasks had * to be completed first, or because the user was busy editing a command * line and the local platform doesn't support real-time command * interruptions), the interval is applied to the time the daemon * actually executed, not to the originally scheduled execution time. * For example, if the daemon is scheduled to run once every minute, but * can't run at all for five minutes because of command editing on a * non-interrupting platform, once it actually does run, it won't run * again for (at least) another minute after that. This means that the * daemon will not run five times all at once when it's finally allowed * to run - there's no making up for lost time. */ class RealTimeDaemon: RealTimeEvent /* * Creation. 'interval' is the number of milliseconds between * invocations. */ construct(obj, prop, interval) { /* inherit the base constructor */ inherited(obj, prop); /* remember my interval */ interval_ = interval; /* * figure my initial execution time - wait for one complete * interval from the current time */ eventTime = realTimeManager.getElapsedTime() + interval; } /* execute the daemon */ executeEvent() { /* call my method */ callMethod(); /* * Reschedule for next time. To ensure that we keep to our * long-term schedule, reschedule based on our original schedule * time rather than the current clock time; that way, if there * was a delay after our original scheduled time in firing us, * we'll make up for it by shortening the interval until the * next firing. If that would make us already schedulable, then * our interval must be so short we can't keep up with it; in * that case, add the interval to the current clock time. */ eventTime += interval_; if (realTimeManager.getElapsedTime() < eventTime) eventTime = realTimeManager.getElapsedTime() + interval_; } /* my execution interval, in milliseconds */ interval_ = 1 ; /* * Sensory-context-sensitive real-time daemon - this is a real-time * daemon with an explicit sensory context. This is the daemon * counterpart of RealTimeSenseFuse. */ class RealTimeSenseDaemon: RealTimeDaemon construct(obj, prop, interval, source, sense) { /* inherit the base constructor */ inherited(obj, prop, interval); /* remember our sensory context */ source_ = source; sense_ = sense; } ; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/travel.t��������������������������������������������������������������0000644�0001750�0000144�00001017452�12014206575�016623� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - travel * * This module defines the parts of the simulation model related to * travel: rooms and other locations, directions, passages. */ /* include the library header */ #include "adv3.h" infiniteLoop() { for (local i = 1 ; i < 100 ; ) ; } /* ------------------------------------------------------------------------ */ /* * Directions. Each direction is represented by an instance of * Direction. * * A Direction object encapsulates information about a travel direction. * By using an object to represent each possible travel direction, we * make it possible for a game or library extension to add new custom * directions without having to change the basic library. */ class Direction: object /* * The link property for the direction. This is a property pointer * that we use to ask an actor's location for a TravelConnector when * the actor attempts to travel in this direction. */ dirProp = nil /* * The default TravelConnector when an actor attempts travel in this * direction, but the actor's location does not define the dirProp * property for this direction. This is almost always a connector * that goes nowhere, such as noTravel, since this is used only when * a location doesn't have a link for travel in this direction. */ defaultConnector(loc) { return noTravel; } /* * Initialize. We'll use this routine to add each Direction * instance to the master direction list (Direction.allDirections) * during pre-initialization. */ initializeDirection() { /* add myself to the master direction list */ Direction.allDirections.append(self); } /* * Class initialization - this is called once on the class object. * We'll build our master list of all of the Direction objects in * the game, and then sort the list using the sorting order. */ initializeDirectionClass() { /* initialize each individual Direction object */ forEachInstance(Direction, { dir: dir.initializeDirection() }); /* * sort the direction list according to the individual Directin * objects' defined sorting orders */ allDirections.sort(SortAsc, {a, b: a.sortingOrder - b.sortingOrder}); } /* * Our sorting order in the master list. We use this to present * directions in a consistent, aesthetically pleasing order in * listings involving multiple directions. The sorting order is * simply an integer that gives the relative position in the list; * the list of directions is sorted from lowest sorting order to * highest. Sorting order numbers don't have to be contiguous, * since we simply put the directions in an order that makes the * sortingOrder values ascend through the list. */ sortingOrder = 1 /* * A master list of the defined directions. We build this * automatically during initialization to include each Direction * instance. Any operation that wants to iterate over all possible * directions can run through this list. * * By using this master list to enumerate all possible directions * rather than a hard-coded set of pre-defined directions, we can * ensure that any new directions that a game or library extension * adds will be incorporated automatically into the library simply * by virtue of the existence Direction instances to represent the * new directions. */ allDirections = static new Vector(10) ; /* * The base class for compass directions (north, south, etc). We don't * add anything to the basic Direction class, but we use a separate * class for compass directions to allow language-specific * customizations for all of the directions and to allow travel commands * to treat these specially when needed. */ class CompassDirection: Direction ; /* * The base class for shipboard directions (port, aft, etc). */ class ShipboardDirection: Direction defaultConnector(loc) { /* * If 'loc' is aboard a ship, use noTravel as the default * connector, since we simply can't go this direction. If we're * not aboard ship, use the special connector noShipTravel, * which is meant to convey that shipboard directions make no * sense when not aboard a ship. */ return (loc.isShipboard ? noTravel : noShipTravel); } ; /* * The base class for vertical directions (up, down) */ class VerticalDirection: Direction ; /* * The base class for "relative" directions (in, out) */ class RelativeDirection: Direction ; /* * Individual compass directions. * * Our macro defines a direction object with a name based on the * direction's room travel link property and the base class. So, * DefineDirection(north, Compass) defines a direction object called * 'northDirection' based on the CompassDirection class, with the link * property 'north' and default travel connector 'noTravel'. * * Note that we define a sorting order among the default directions as * follows. First, we define several groups of related directions, * which we put in a relative order: the compass directions, then the * vertical directions, then the "relative" (in/out) directions, and * finally the shipboard directions. Then, we order the directions * within these groups. For the sortingOrder values, we use arbitrary * integers with fairly wide separations, to leave plenty of room for * custom game-specific directions to be added before, between, after, * or within these pre-defined groups. */ #define DefineDirection(prop, base, order) \ prop##Direction: base##Direction \ dirProp = &prop \ sortingOrder = order DefineDirection(north, Compass, 1000); DefineDirection(south, Compass, 1100); DefineDirection(east, Compass, 1200); DefineDirection(west, Compass, 1300); DefineDirection(northeast, Compass, 1400); DefineDirection(northwest, Compass, 1500); DefineDirection(southeast, Compass, 1600); DefineDirection(southwest, Compass, 1700); DefineDirection(up, Vertical, 3000); DefineDirection(down, Vertical, 3100); DefineDirection(in, Relative, 5000) defaultConnector(loc) { return noTravelIn; } ; DefineDirection(out, Relative, 5100) defaultConnector(loc) { return noTravelOut; } ; DefineDirection(fore, Shipboard, 7000); DefineDirection(aft, Shipboard, 7100); DefineDirection(port, Shipboard, 7200); DefineDirection(starboard, Shipboard, 7300); /* ------------------------------------------------------------------------ */ /* * Travel Message Handler. This contains a set of messages that are * specific to different types of TravelConnector objects, to describe * NPC arrivals and departures via these connectors when the NPC's are in * view of the player character. * * This base class implements the methods simply by calling the * corresponding gLibMessages message methods. * * The purpose of providing this variety of connector-specific handlers * is to make it easy for individual travelers to customize the * arrival/departure messages for specific connector subclasses. These * messages sometimes benefit from customization for specific * traveler/connector combinations; the easiest way to enable such * granular customization is to make it possible to override a message * per connector type on the traveler object. */ class TravelMessageHandler: object /* * Get the traveler for the purposes of arrival/departure messages. * Implementations that aren't themselves the travelers should * override this to supply the correct nominal traveler. */ getNominalTraveler() { return self; } /* generic arrival/departure - for the base TravelConnector class */ sayArriving(conn) { gLibMessages.sayArriving(getNominalTraveler()); } sayDeparting(conn) { gLibMessages.sayDeparting(getNominalTraveler()); } /* generic local arrival and departure messages */ sayArrivingLocally(dest, conn) { gLibMessages.sayArrivingLocally(getNominalTraveler(), dest); } sayDepartingLocally(dest, conn) { gLibMessages.sayDepartingLocally(getNominalTraveler(), dest); } /* generic remote travel message */ sayTravelingRemotely(dest, conn) { gLibMessages.sayTravelingRemotely(getNominalTraveler(), dest); } /* directional arrival/departure - for RoomConnector */ sayArrivingDir(dir, conn) { dir.sayArriving(getNominalTraveler()); } sayDepartingDir(dir, conn) { dir.sayDeparting(getNominalTraveler()); } /* arrival/departure via a ThroughPassage */ sayArrivingThroughPassage(conn) { gLibMessages.sayArrivingThroughPassage(getNominalTraveler(), conn); } sayDepartingThroughPassage(conn) { gLibMessages.sayDepartingThroughPassage(getNominalTraveler(), conn); } /* arrival/departure via a PathPassage */ sayArrivingViaPath(conn) { gLibMessages.sayArrivingViaPath(getNominalTraveler(), conn); } sayDepartingViaPath(conn) { gLibMessages.sayDepartingViaPath(getNominalTraveler(), conn); } /* arrival/departure up/down stairs */ sayArrivingUpStairs(conn) { gLibMessages.sayArrivingUpStairs(getNominalTraveler(), conn); } sayArrivingDownStairs(conn) { gLibMessages.sayArrivingDownStairs(getNominalTraveler(), conn); } sayDepartingUpStairs(conn) { gLibMessages.sayDepartingUpStairs(getNominalTraveler(), conn); } sayDepartingDownStairs(conn) { gLibMessages.sayDepartingDownStairs(getNominalTraveler(), conn); } ; /* ------------------------------------------------------------------------ */ /* * A Traveler is an object that can travel. The two main kinds of * travelers are Actors and Vehicles. A vehicle can contain multiple * actors, so a single vehicle travel operation could move several * actors. * * This class is intended to be multiply inherited, since it is not based * on any simulation object class. */ class Traveler: TravelMessageHandler /* * Check, using pre-condition rules, that the traveler is directly * in the given room. We'll attempt to implicitly remove the actor * from any nested rooms within the given room. */ checkDirectlyInRoom(dest, allowImplicit) { /* ask the destination to ensure the traveler is in it directly */ return dest.checkTravelerDirectlyInRoom(self, allowImplicit); } /* * Check, using pre-condition rules, that the traveler is in the * given room, moving the traveler to the room if possible. */ checkMovingTravelerInto(room, allowImplicit) { /* subclasses must override */ } /* * Get the travel preconditions specific to the traveler, for the * given connector. By default, we return no extra conditions. */ travelerPreCond(conn) { return []; } /* * Can the traveler travel via the given connector to the given * destination? Returns true if the travel is permitted, nil if not. * * By default, this simply returns true to indicate that the travel * is allowed. Individual instances can override this to enforce * limitations on what kind of travel the traveler can perform. */ canTravelVia(connector, dest) { return true; } /* * Explain why the given travel is not possible. This is called when * canTravelVia() returns nil, to display a report to the player * explaining why the travel was disallowed. * * By default, we do nothing, since our default canTravelVia() never * disallows any travel. If canTravelVia() is overridden to disallow * travel under some conditions, this must be overridden to generate * an appropriate explanatory report. */ explainNoTravelVia(connector, dest) { } /* * Show my departure message - this is shown when I'm leaving a room * where the player character can see me for another room where the * player character cannot see me. */ describeDeparture(dest, connector) { /* * If we're visible to the player character, describe the * departure. Ask the connector to describe the travel, since * it knows the direction of travel and can describe special * things like climbing stairs. * * Don't bother to describe the departure if the traveler is the * player character, or the player character is inside the * traveler. The PC obviously can't leave the presence of * itself. */ if (!isActorTraveling(gPlayerChar)) { /* * invoke the departure with a visual sense context for the * traveler */ callWithSenseContext(self, sight, {: describeNpcDeparture(dest, connector)}); } } /* * Describe the departure of a non-player character, or any traveler * not involving the player character. */ describeNpcDeparture(dest, connector) { /* * If the PC can see our destination, we're doing "local travel" * - we're traveling from one top-level location to another, but * entirely in view of the PC. In this case, the PC was already * aware of our presence, so we don't want to describe the * departure as though we're actually leaving; we're just moving * around. If the destination isn't in sight, then we're truly * departing. */ if (gPlayerChar.canSee(dest)) { /* * We're moving from somewhere within the PC's sight to * another location within the PC's sight, so we're not * actually departing, from the PC's point of view: we're * just moving around a bit. * * - If we're moving further away from the PC - that is, if * we're moving out of the PC's own top-level location and * into a different top-level location, describe this as a * "local departure." * * - If we're moving closer to the PC - moving from a top * level location that doesn't contain the PC to the PC's * current top-level location - say nothing. We'll leave it * to the "local arrival" message to mention it, because * that's the more relevant aspect of the travel from the * PC's POV: we're approaching the PC. * * - If we're moving from one top-level location to another, * say nothing. We'll leave this to arrival time as well. * Whether we describe remote-to-remote travel as arrival or * departure is just arbitrary; we only want to generate one * message, but there's no general reason to prefer * generating the message at departure vs arrival. */ if (gPlayerChar.isIn(getOutermostRoom())) { /* * we're moving further away from the player - describe * this as a local departure */ connector.describeLocalDeparture(self, location, dest); } } else { /* we're really departing - let the connector describe it */ connector.describeDeparture(self, location, dest); } } /* * Show my arrival message - this is shown when I'm entering a room * in view of the player character from a location where the player * character could not see me. 'backConnector' is the connector in * the destination location from which we appear to be emerging. */ describeArrival(origin, backConnector) { /* * If the player character is participating in the travel, * describe the arrival from the point of view of the player * character by showing the "look around" description of the new * location. * * Otherwise, describe the NPC traveler's arrival. Only * describe the arrival if the PC can see the traveler, for * obvious reasons. */ if (isActorTraveling(gPlayerChar)) { /* * Add an intra-report separator, to visually separate the * description of the arrival from any result text that came * before. (For example, if we performed an implied command * before we could begin travel, this will visually separate * the results of the implied command from the arrival * message.) */ say(gLibMessages.intraCommandSeparator); /* * The player character is traveling - show the travel from * the PC's perspective. */ gPlayerChar.lookAround(gameMain.verboseMode.isOn); } else if (travelerSeenBy(gPlayerChar)) { /* * The player character isn't traveling, but the PC can see * me now that I'm in my new location, so describe the * arrival of the traveler. Do this within a visual sense * context for the traveler. */ callWithSenseContext( self, sight, {: describeNpcArrival(origin, backConnector)}); } } /* * Describe the arrival of a non-player character or any other * traveler that doesn't involve the player character. */ describeNpcArrival(origin, backConnector) { /* * If we know the connector back, use its arrival message; * otherwise, use a generic arrival message. */ if (backConnector != nil) { /* * If the PC can see the origin, we're not actually arriving * anew; instead, we're simply moving around within the PC's * field of view. In these cases, we don't want to use the * normal arrival message, because we're not truly arriving; * we could be moving closer to the player, further away from * the player, or moving laterally from one remote location * to another. If the PC can't see the origin, we're truly * arriving. */ if (gPlayerChar.canSee(origin)) { /* * We're not arriving anew, but just moving around within * the PC's field of view. * * - If we're moving closer to the PC - moving from a * top-level location that doesn't contain the PC to one * that does - describe this as a "local arrival." * * - If we're moving further away from the PC - from * within the PC's top-level location to a different * top-level location - don't say anything. We will have * already described this as a "local departure" during * the departure message phase, and from the PC's point * of view, that fully covers it. * * - If we're moving laterally - from one remote * top-level location to another ("remote" means "does * not contain the PC") - it's arbitrary whether this * should be described at arrival or departure time. We * arbitrarily pick arrival, so describe the lateral * local travel now. */ if (gPlayerChar.isIn(getOutermostRoom())) { /* * We're now in the same top-level location as the * PC, so we've moved closer to the PC, so describe * this as a "local arrival." */ backConnector.describeLocalArrival( self, origin, location); } else if (!gPlayerChar.isIn(origin)) { /* * We're *not* in the same top-level location as the * PC, and we weren't on departure either, so this is * a lateral move - a move from one remote location * to another. Describe it as such now. */ backConnector.describeRemoteTravel( self, origin, location); } } else { /* we're arriving anew - let the connector describe it */ backConnector.describeArrival(self, origin, location); } } else { /* there's no back-connector, so use a generic arrival message */ gLibMessages.sayArriving(self); } } /* * Travel to a new location. Moves the traveler to a new location * via the given connector, triggering any side effects of the * travel. * * Note that this routine is not normally called directly; in most * cases, the actor's travelTo is called, and it in turn invokes * this method in the appropriate traveler. * * 'dest' is the new location to which we're traveling. 'connector' * is the TravelConnector we're traversing from the source location * to reach the new destination; the connector is normally the * invoker of this routine. 'backConnector' is the connector in the * destination from which the actor will appear to emerge on the * other end of the travel. */ travelerTravelTo(dest, connector, backConnector) { local origin; local isDescribed; local actors; /* * Remember my departure original location - this is the * location where the traveler was before we moved. */ origin = location; /* * Determine if we're describing the travel. We'll describe it * if we're actually changing locations, or if the connector is * explicitly a "circular" passage, which means that it connects * back to the same location but involves a non-trivial passage, * as sometimes happens in settings like caves. */ isDescribed = (dest != origin || connector.isCircularPassage); /* * Send a before-departure notification to everyone connected to * by containment to the traveler. */ getNotifyTable().forEachAssoc( {obj, val: obj.beforeTravel(self, connector)}); /* notify the actor initiating the travel */ if (gActor != nil) gActor.actorTravel(self, connector); /* tell the old room we're leaving, if we changed locations */ if (origin != nil && isDescribed) origin.travelerLeaving(self, dest, connector); /* notify the connector that we're traversing it */ connector.noteTraversal(self); /* get the list of traveling actors */ actors = getTravelerActors(); /* move to the destination */ moveIntoForTravel(dest); /* * We've successfully completed the travel, so remember it for * each actor involved in the travel. Do this only if we * actually went somewhere, or if the connector is circular - if * we didn't end up going anywhere, and the connector isn't * circular, we must have turned back halfway without completing * the travel. */ if (dest != origin || connector.rememberCircularPassage) { foreach (local cur in actors) { /* ask the actor to remember the travel */ cur.rememberTravel(origin, dest, backConnector); /* remember the destination of the connector for this actor */ connector.rememberTravel(origin, cur, dest); /* * If there's a back-connector, also remember travel in * the other direction. The actor can reasonably be * expected to assume that the connector through which * it's arriving connects back to the source location, so * remember the association. */ if (backConnector != nil) backConnector.rememberTravel(dest, cur, origin); } } /* * Recalculate the global sense context for message generation * purposes, since we've moved to a new location. It's possible * that we've arrived at the player character's location, in * which case we might have been suppressing messages (due to * our being in a different room) but should now show messages * (because we're now near the player character). */ if (gAction != nil) gAction.recalcSenseContext(); /* tell the new room we're arriving, if we changed locations */ if (location != nil && isDescribed) location.travelerArriving(self, origin, connector, backConnector); /* notify objects now connected by containment of the arrival */ getNotifyTable().forEachAssoc( {obj, val: obj.afterTravel(self, connector)}); } /* * Perform "local" travel - that is, travel between nested rooms * within a single top-level location. By default, we simply defer * to the actor to let it perform the local travel. */ travelerTravelWithin(actor, dest) { actor.travelerTravelWithin(actor, dest); } /* * Get a lookup table giving the set of objects to be notified of a * beforeTravel/afterTravel event. By default, we return a table * including every object connected to the traveler by containment. */ getNotifyTable() { /* return the table of objects connected by containment */ return connectionTable(); } /* * Determine if the given actor can see this traveler. By default, * we'll simply check to see if the actor can see 'self'. */ travelerSeenBy(actor) { return actor.canSee(self); } /* * Is the given actor traveling with this traveler? Returns true if * the actor is in my getTravelerActors list. */ isActorTraveling(actor) { /* check to see if the given actor is in my actor list */ return getTravelerActors.indexOf(actor) != nil; } /* * Is the given object being carried by the traveler? Returns true * if the object is inside the traveler itself, or is inside any of * the actors traveling. */ isTravelerCarrying(obj) { /* if the object is inside the traveler, it's being carried */ if (obj.isIn(self)) return true; /* if the object is inside any traveling actor, it's being carried */ foreach (local cur in getTravelerActors) { if (obj.isIn(cur)) return true; } /* the object isn't being carried */ return nil; } /* invoke a callback function for each traveling actor */ forEachTravelingActor(func) { /* by default, get the list, and invoke the callback per item */ getTravelerActors.forEach(func); } /* * Get the list of actors taking part in the travel. When an actor * is the traveler, this list simply contains the actor itself; for * a vehicle or other composite traveler that moves more than one * actor at a time, this should return the list of all of the actors * involved in the travel. */ getTravelerActors = [] /* * Get the list of actors traveling undo their own power. In the * case of an actor traveling directly, this is just the actor; in * the case of an actor pushing something, this is likewise the * actor; in the case of a group of actors traveling together, this * is the list of traveling actors; in the case of a vehicle, this * is an empty list, since anyone traveling with the vehicle is * traveling under the vehicle's power. */ getTravelerMotiveActors = [] ; /* ------------------------------------------------------------------------ */ /* * A Travel Connector is a special connection interface that allows for * travel from one location to another. Most actor movement, except for * movement between locations related by containment (such as from a room * to sitting in a chair within the room) are handled through travel * connector objects. * * Travel connectors are used in the directional link properties in rooms * - north, south, east, west, in, out, etc. A room direction link * property is always set to a travel connector - but note that a room is * itself a travel connector, so a travel link in one room can simply be * set to point directly to another room. In many cases, rooms * themselves serve as travel connectors, so that one room can point a * direction link property directly to another room. * * Some travel connectors are physical objects in the simulation, such as * doors or stairways; other connectors are just abstract objects that * represent connections, but don't appear as manipulable objects in the * game. * * A travel connector provides several types of information about travel * through its connection: * * - For actors actually traveling, the connector provides a method that * moves an actor through the connector. This method can trigger any * side effects of the travel. * * - For automatic map builders, actor scripts, and other callers who * want to learn what can be known about the link without actually * traversing it, the connector provides an "apparent destination" * method. This method returns the destination of travel through the * connector that a given actor would expect just by looking at the * connector. The important thing about this routine is that it doesn't * trigger any side effects, but simply indicates whether travel is * apparently possible, and if so what the destination of the travel * would be. */ class TravelConnector: Thing /* * Get any connector-specific pre-conditions for travel via this * connector. */ connectorTravelPreCond() { local lst; /* start with no conditions */ lst = []; /* if we have a staging location, require that we're in it */ if (connectorStagingLocation != nil) lst = [new TravelerDirectlyInRoom(gActor, self, connectorStagingLocation)]; /* * If we're a physical Thing with a non-nil location, require * that we be touchable. Only physical objects need to be * touchable; connectors are sometimes abstract objects, which * obviously can't be touched. */ if (ofKind(Thing) && location != nil) { /* require that the traveler can touch the connector */ local cond = new TouchObjCondition(gActor.getTraveler(self)); lst += new ObjectPreCondition(self, cond); } /* return the result */ return lst; } /* * The "staging location" for travel through this connector. By * default, if we have a location, that's our staging location; if * we don't have a location (in which case we probably an outermost * room), we don't have a staging location. */ connectorStagingLocation = (location) /* * Get the travel preconditions that this connector requires for * travel by the given actor. In most cases, this won't depend on * the actor, but it's provided as a parameter anyway; in most cases, * this will just apply the conditions that are relevant to actors as * travelers. * * By default, we require actors to be "travel ready" before * traversing a connector. The exact meaning of "travel ready" is * provided by the actor's immediate location, but it usually simply * means that the actor is standing. This ensures that the actor * isn't sitting in a chair or lying down or something like that. * Some connectors might not require this, so this routine can be * overridden per connector. * * Note that this will only be called when an actor is the traveler. * When a vehicle or other kind of traveler is doing the travel, this * will not be invoked. */ actorTravelPreCond(actor) { /* * create an object precondition ensuring that the actor is * "travel ready"; the object of this precondition is the * connector itself */ return [new ObjectPreCondition(self, actorTravelReady)]; } /* * Barrier or barriers to travel. This property can be set to a * single TravelBarrier object or to a list of TravelBarrier * objects. checkTravelBarriers() checks each barrier specified * here. */ travelBarrier = [] /* * Check barriers. The TravelVia check() routine must call this to * enforce barriers. */ checkTravelBarriers(dest) { local traveler; local lst; /* get the traveler */ traveler = gActor.getTraveler(self); /* ask the traveler what it thinks of travel through this connector */ if (!traveler.canTravelVia(self, dest)) { /* explain why the traveler can't pass */ traveler.explainNoTravelVia(self, dest); /* terminate the command */ exit; } /* check any travel conditions we apply directly */ if (!canTravelerPass(traveler)) { /* explain why the traveler can't pass */ explainTravelBarrier(traveler); /* terminate the command */ exit; } /* get the barrier list */ lst = travelBarrier; /* if it's just a single object, make it a list of one element */ if (!lst.ofKind(Collection)) lst = [lst]; /* check each item in our barrier list */ foreach (local cur in lst) { /* if this barrier doesn't allow travel, we cannot travel */ if (!cur.canTravelerPass(traveler)) { /* ask the barrier to explain why travel isn't possible */ cur.explainTravelBarrier(traveler); /* terminate the command */ exit; } } } /* * Check to see if the Traveler object is allowed to travel through * this connector. Returns true if travel is allowed, nil if not. * * This is called from checkTravelBarriers() to check any conditions * coded directly into the TravelConnector. By default, we simply * return true; subclasses can override this to apply special * conditions. * * If an override wants to disallow travel, it should return nil * here, and then provide an override for explainTravelBarrier() to * provide a descriptive message explaining why the travel isn't * allowed. * * Conditions here serve essentially the same purpose as barrier * conditions. The purpose of providing this additional place for * the same type of conditions is simply to improve the convenience * of defining travel conditions for cases where barriers are * unnecessary. The main benefit of using a barrier is that the same * barrier object can be re-used with multiple connectors, so if the * same set of travel conditions apply to several different * connectors, barriers allow the logic to be defined once in a * single barrier object and then re-used easily in each place it's * needed. However, when a particular condition is needed in only * one place, creating a barrier to represent the condition is a bit * verbose; in such cases, the condition can be placed in this method * more conveniently. */ canTravelerPass(traveler) { return true; } /* * Explain why canTravelerPass() returned nil. This is called to * display an explanation of why travel is not allowed by * self.canTravelerPass(). * * Since the default canTravelerPass() always allows travel, the * default implementation of this method does nothing. Whenever * canTravelerPass() is overridden to return nil, this should also be * overridden to provide an appropriate explanation. */ explainTravelBarrier(traveler) { } /* * Is this connector listed? This indicates whether or not the exit * is allowed to be displayed in lists of exits, such as in the * status line or in "you can't go that way" messages. By default, * all exits are allowed to appear in listings. * * Note that this indicates if listing is ALLOWED - it doesn't * guarantee that listing actually occurs. A connector can be * listed only if this is true, AND the point-of-view actor for the * listing can perceive the exit (which means that * isConnectorApparent must return true, and there must be * sufficient light to see the exit). */ isConnectorListed = true /* * Get an unlisted proxy for this connector. This is normally * called from the asExit() macro to set up one room exit direction * as an unlisted synonym for another. */ createUnlistedProxy() { return new UnlistedProxyConnector(self); } /* * Determine if the travel connection is apparent - as a travel * connector - to the actor in the given origin location. This * doesn't indicate whether or not travel is possible, or where * travel goes, or that the actor can tell where the passage goes; * this merely indicates whether or not the actor should realize * that the passage exists at all. * * A closed door, for example, would return true, because even a * closed door makes it clear that travel is possible in the * direction, even if it's not possible currently. A secret door, * on the other hand, would return nil while closed, because it * would not be apparent to the actor that the object is a door at * all. */ isConnectorApparent(origin, actor) { /* by default, passages are apparent */ return true; } /* * Determine if the travel connection is passable by the given * traveler in the current state. For example, a door would return * true when open, nil when closed. * * This information is intended to help game code probing the * structure of the map. This information is NOT used in actor * travel; for actor travel, we rely on custom checks in the * connector's TravelVia handler to enforce the conditions of travel. * Actor travel uses TravelVia customizations rather than this method * because that allows better specificity in reporting failures. * This method lets game code get at the same information, but in a * more coarse-grained fashion. */ isConnectorPassable(origin, traveler) { /* by default, we're passable */ return true; } /* * Get the apparent destination of travel by the actor to the given * origin. This returns the location to which the connector * travels, AS FAR AS THE ACTOR KNOWS. If the actor does not know * and cannot tell where the connector leads, this should return nil. * * Note that this method does NOT necessarily return the actual * destination, because we obviously can't know the destination for * certain until we traverse the connection. Rather, the point of * this routine is to return as much information as the actor is * supposed to have. This can be used for purposes like * auto-mapping, where we'd want to show what the player character * knows of the map, and NPC goal-seeking, where an NPC tries to * figure out how to get from one point to another based on the * NPC's knowledge of the map. In these sorts of applications, it's * important to use only knowledge that the actor is supposed to * have within the parameters of the simulation. * * Callers should always test isConnectorApparent() before calling * this routine. This routine does not check to ensure that the * connector is apparent, so it could return misleading information * if used independently of isConnectorApparent(); for example, if * the connector *formerly* worked but has now disappeared, and the * actor has a memory of the former destination, we'll return the * remembered destination. * * The actor can know the destination by a number of means: * * 1. The location is familiar to the character. For example, if * the setting is the character's own house, the character would * obviously know the house well, so would know where you'd end up * going east from the living room or south from the kitchen. We * use the origin method actorKnowsDestination() to determine this. * * 2. The destination is readily visible from the origin location, * or is clearly marked. For example, in an outdoor setting, it * might be clear that going east from the field takes you to the * hilltop. In an indoor setting, an open passage might make it * clear that going east from the living room takes you to the * dining room. We use the origin method actorKnowsDestination() to * determine this. * * 3. The actor has been through the connector already in the course * of the game, and so remembers the connection by virtue of recent * experience. If our travelMemory class property is set to a * non-nil lookup table object, then we'll automatically use the * lookup table to remember the destination each time an actor * travels via a connector, and use this information by default to * provide apparent destination information. */ getApparentDestination(origin, actor) { local dest; /* * Ask the origin if the actor knows the destination for the * given connector. If so, and we can determine our * destination, then return the destination. */ if (origin.actorKnowsDestination(actor, self) && (dest = getDestination(origin, actor.getTraveler(self))) != nil) return dest; /* * If we have a travelMemory table, look to see if the traversal * of this actor via this connector from this origin is recorded * in the table, and if so, assume that the destination is the * same as it was last time. * * Note that we ignore our memory of travel if we never saw the * destination of the travel (which would be the case if the * destination was dark every time we've been there, so we've * never seen any details about the location). */ if (travelMemory != nil && (dest = travelMemory[[actor, origin, self]]) != nil && actor.hasSeen(dest)) { /* we know the destination from past experience */ return dest; } /* we don't know the destination */ return nil; } /* * Get our destination, given the traveler and the origin location. * * This method is required to return the current destination for the * travel. If the connector doesn't go anywhere, this should return * nil. The results of this method must be stable for the extent of * a turn, up until the time travel actually occurs; in other words, * it must be possible to call this routine simply for information * purposes, to determine where the travel will end up. * * This method should not trigger any side effects, since it's * necessary to be able to call this method more than once in the * course of a given travel command. If it's necessary to trigger * side effects when the connector is actually traversed, apply the * side effects in noteTraversal(). * * For auto-mapping and the like, note that getApparentDestination() * is a better choice, since this method has internal information * that might not be apparent to the characters in the game and thus * shouldn't be revealed through something like an auto-map. This * method is intended for internal use in the course of processing a * travel action, since it knows the true destination of the travel. */ getDestination(origin, traveler) { return nil; } /* * Get the travel connector leading to the given destination from the * given origin and for the given travel. Return nil if we don't * know a connector leading there. * * By default, we simply return 'self' if our destination is the * given destination, or nil if not. * * Some subclasses might encapsulate one or more "secondary" * connectors - that is, the main connector might choose among * multiple other connectors. In these cases, the secondary * connectors typically won't be linked to directions on their own, * so the room can't see them directly - it can only find them * through us, since we're effectively a wrapper for the secondary * connectors. In these cases, we won't have any single destination * ourself, so getDestination() will have to return nil. But we * *can* work backwards: given a destination, we can find the * secondary connector that points to that destination. That's what * this routine is for. */ connectorGetConnectorTo(origin, traveler, dest) { /* if we go there, return 'self', else return nil */ return (getDestination(origin, traveler) == dest ? self : nil); } /* * Note that the connector is being traversed. This is invoked just * before the traveler is moved; this notification is fired after the * other travel-related notifications (beforeTravel, actorTravel, * travelerLeaving). This is a good place to display any special * messages describing what happens during the travel, because any * messages displayed here will come after any messages related to * reactions from other objects. */ noteTraversal(traveler) { /* do nothing by default */ } /* * Service routine: add a memory of a successful traversal of a * travel connector. If we have a travel memory table, we'll add * the traversal to the table, so that we can find it later. * * This is called from Traveler.travelerTravelTo() on successful * travel. We're called for each actor participating in the travel. */ rememberTravel(origin, actor, dest) { /* * If we have a travelMemory table, add this traversal. Store * the destination, keyed by the combination of the actor, * origin, and connector object (i.e., self) - this will allow * us to remember the destination we reached last time if we * want to know where the same route goes in the future. */ if (TravelConnector.travelMemory != nil) TravelConnector.travelMemory[[actor, origin, self]] = dest; } /* * Our "travel memory" table. If this contains a non-nil lookup * table object, we'll store a record of each successful traversal * of a travel connector here - we'll record the destination keyed * by the combination of actor, origin, and connector, so that we * can later check to see if the actor has any memory of where a * given connector goes from a given origin. * * We keep this information by default, which is why we statically * create the table here. Keeping this information does involve * some overhead, so some authors might want to get rid of this * table (by setting the property to nil) if the game doesn't make * any use of the information. Note that this table is stored just * once, in the TravelConnector class itself - there's not a * separate table per connector. */ travelMemory = static new LookupTable(256, 512) /* * Is this a "circular" passage? A circular passage is one that * explicitly connects back to its origin, so that traveling through * the connector leaves us where we started. When a passage is * marked as circular, we'll describe travel through the passage * exactly as though we had actually gone somewhere. By default, if * traveling through a passage leaves us where we started, we assume * that nothing happened, so we don't describe any travel. * * Circular passages don't often occur in ordinary settings; these * are mostly useful in disorienting environments, such as twisty * cave networks, where a passage between locations can change * direction and even loop back on itself. */ isCircularPassage = nil /* * Should we remember a circular trip through this passage? By * default, we remember the destination of a passage that takes us * back to our origin only if we're explicitly marked as a circular * passage; in other cases, we assume that the travel was blocked * somehow instead. */ rememberCircularPassage = (isCircularPassage) /* * Describe an actor's departure through the connector from the * given origin to the given destination. This description is from * the point of view of another actor in the origin location. */ describeDeparture(traveler, origin, dest) { local dir; /* * See if we can find a direction linked to this connector from * the origin location. If so, describe the departure using the * direction; otherwise, describe it using the generic departure * message. * * Find the connector from the player character's perspective, * because the description we're generating is for the player's * benefit and thus should be from the PC's perspective. */ if ((dir = origin.directionForConnector(self, gPlayerChar)) != nil) { /* * We found a direction linked to the connector, so this * must have been the way they traveled. Describe the * departure as being in that direction. * * Note that it's possible that more than one direction is * linked to the same connector. In such cases, we'll just * take the first link we find, because it's equally * accurate to say that the actor went in any of the * directions linked to the same connector. */ traveler.sayDepartingDir(dir, self); } else { /* * We didn't find any direction out of the origin linking to * this connector, so we don't know how what direction they * went. Show the generic departure message. */ traveler.sayDeparting(self); } } /* * Describe an actor's arrival through the connector from the given * origin into the given destination. This description is from the * point of view of another actor in the destination. * * Note that this is called on the connector that reverses the * travel, NOT on the connector the actor is actually traversing - * that is, 'self' is the backwards connector, leading from the * destination back to the origin location. So, if we have two * sides to a door, and the actor traverses the first side, this * will be called on the second side - the one that links the * destination back to the origin. */ describeArrival(traveler, origin, dest) { local dir; /* * See if we can find a direction linked to this connector in * the destination location. If so, describe the arrival using * the direction; otherwise, describe it using a generic arrival * message. * * Find the connector from the player character's perspective, * because the description we're generating is for the player's * benefit and thus should be from the PC's perspective. */ if ((dir = dest.directionForConnector(self, gPlayerChar)) != nil) { /* * we found a direction linked to this back connector, so * describe the arrival as coming from the direction we * found */ traveler.sayArrivingDir(dir, self); } else { /* * we didn't find any direction links, so use a generic * arrival message */ traveler.sayArriving(self); } } /* * Describe a "local departure" via this connector. This is called * when a traveler moves around entirely within the field of view of * the player character, and move *further away* from the PC - that * is, the traveler's destination is visible to the PC when we're * leaving our origin, AND the origin's top-level location contains * the PC. We'll describe the travel not in terms of truly * departing, but simply in terms of moving away. */ describeLocalDeparture(traveler, origin, dest) { /* say that we're departing locally */ traveler.sayDepartingLocally(dest, self); } /* * Describe a "local arrival" via this connector. This is called * when the traveler moves around entirely within the field of view * of the player character, and comes *closer* to the PC - that is, * the traveler's origin is visible to the player character when we * arrive in our destination, AND the destination's top-level * location contains the PC. We'll describe the travel not in terms * of truly arriving, since the traveler was already here to start * with, but rather as entering the destination, but just in terms of * moving closer. */ describeLocalArrival(traveler, origin, dest) { /* say that we're arriving locally */ traveler.sayArrivingLocally(dest, self); } /* * Describe "remote travel" via this connector. This is called when * the traveler moves around entirely within the field of view of the * PC, but between two "remote" top-level locations - "remote" means * "does not contain the PC." In this case, the traveler isn't * arriving or departing, exactly; it's just moving laterally from * one top-level location to another. */ describeRemoteTravel(traveler, origin, dest) { /* say that we're traveling laterally */ traveler.sayTravelingRemotely(dest, self); } /* * Find a connector in the destination location that connects back as * the source of travel from the given connector when traversed from * the source location. Returns nil if there is no such connector. * This must be called while the traveler is still in the source * location; we'll attempt to find the connector back to the * traveler's current location. * * The purpose of this routine is to identify the connector by which * the traveler arrives in the new location. This can be used, for * example, to generate a connector-specific message describing the * traveler's emergence from the connector (so we can say one thing * if the traveler arrives via a door, and another if the traveler * arrives by climing up a ladder). * * By default, we'll try to find a travel link in the destination * that links us back to this same connector, in which case we'll * return 'self' as the connector from which the traveler emerges in * the new location. Failing that, we'll look for a travel link * whose apparent source is the origin location. * * This should be overridden for any connector with an explicit * complementary connector. For example, it is common to implement a * door using a pair of objects, one representing each side of the * door; in such cases, each door object would simply return its twin * here. Note that a complementary connector doesn't actually have * to go anywhere, since it's still useful to have a connector back * simply for describing travelers arriving on the connector. * * This *must* be overridden when the destination location doesn't * have a simple connector whose apparent source is this connector, * because in such cases we won't be able to find the reverse * connector with our direction search. */ connectorBack(traveler, dest) { local origin; /* if there's no destination, there's obviously no connector */ if (dest == nil) return nil; /* * get the origin location - this the traveler's current * immediate container */ origin = traveler.location; /* * First, try to find a link back to this same connector - this * will handle simple symmetrical links with the same connector * object shared between two rooms. * * We try to find the actual connector before looking for a * connector back to the origin - it's possible that there are * several ways back to the starting point, and we want to make * sure we pick the one that was actually traversed if possible. */ foreach (local dir in Direction.allDirections) { /* * If this direction link from the destination is linked back * to this same connector, we have a symmetrical connection, * so we're the connector back. Note that we're interested * only in map structure here, so we don't pass an actor; the * actor isn't actually in the new location, so what the * actor can see is irrelevant to us here. */ if (dest.getTravelConnector(dir, nil) == self) { /* the same connector goes in both directions */ return self; } } /* * we didn't find a link back to the same connector, so try to * find a link from the destination whose apparent source is the * origin */ foreach (local dir in Direction.allDirections) { local conn; /* * if this link from the destination has an apparent source * of our origin, the traveler appears to be arriving from * this link */ if ((conn = dest.getTravelConnector(dir, nil)) != nil && conn.fixedSource(dest, traveler) == origin) { /* * this direction has an apparent source of the origin * room - it's not necessarily the same link they * traversed, but at least it appears to come from the * same place they came from, so it'll have to do */ return conn; } } /* we couldn't find any link back to the origin */ return nil; } /* * Get the "fixed" source for travelers emerging from this connector, * if possible. This can return nil if the connector does not have a * fixed relationship with another connector. * * The purpose of this routine is to find complementary connectors * for simple static map connections. This is especially useful for * direct room-to-room connections. * * When a connector relationship other than a simple static mapping * exists, the connectors must generally override connectorBack(), in * which case this routine will not be needed (at least, this routine * won't be needed as long as the overridden connectorBack() doesn't * call it). Whenever it is not clear how to implement this routine, * don't - implement connectorBack() instead. */ fixedSource(dest, traveler) { /* by default, return nothing */ return nil; } /* * Can the given actor see this connector in the dark, looking from * the given origin? Returns true if so, nil if not. * * This is used to determine if the actor can travel from the given * origin via this connector when the actor (in the origin location) * is in darkness. * * By default, we implement the usual convention, which is that * travel from a dark room is possible only when the destination is * lit. If we can't determine our destination, we will assume that * the connector is not visible. */ isConnectorVisibleInDark(origin, actor) { local dest; /* * Get my destination - if we can't determine our destination, * then assume we're not visible. */ if ((dest = getDestination(origin, actor.getTraveler(self))) == nil) return nil; /* * Check the ambient illumination level in the destination. If * it's 2 or higher, then it's lit; otherwise, it's dark. If * the destination is lit, consider the connector to be visible, * on the theory that the connector lets a little bit of the * light from the destination leak into the origin room - just * enough to make the connection itself visible without making * anything else in the origin room visible. */ return (dest.wouldBeLitFor(actor)); } /* * Handle travel in the dark. Specifically, this is called when an * actor attempts travel from one dark location to another dark * location. (We don't invoke this in any other case: * light-to-light, light-to-dark, and dark-to-light travel are all * allowed without any special checks.) * * By default, we will prohibit dark-to-dark travel by calling the * location's darkTravel handler. Individual connectors can * override this to allow such travel or apply different handling. */ darkTravel(actor, dest) { /* * by default, simply call the actor's location's darkTravel * handler */ actor.location.roomDarkTravel(actor); } /* * Action handler for the internal "TravelVia" action. This is not a * real action, but is instead a pseudo-action that we implement * generically for travel via the connector. Subclasses that want to * handle real actions by traveling via the connector can use * remapTo(TravelVia) to implement the real action handlers. Note * that remapTo should be used (rather than, say, asDobjFor), since * this will ensure that every type of travel through the connector * actually looks like a TravelVia action, which is useful for * intercepting travel actions generically in other code. */ dobjFor(TravelVia) { preCond() { /* * For our preconditions, use the traveler's preconditions, * plus the location's preconditions, plus any special * connector-specific preconditions we supply. */ return gActor.getTraveler(self).travelerPreCond(self) + gActor.location.roomTravelPreCond() + connectorTravelPreCond(); } verify() { /* * Verify travel for the current command's actor through this * connector. This performs normal action verify processing. * * The main purpose of this routine is to allow the connector * to flag obviously dangerous travel to allow a caller to * avoid such travel as an implicit or scripted action. In * most cases, there's no need to make travel illogical * because there's generally no potential ambiguity involved * in analyzing a travel verb. * * Note that this routine must check with the actor to * determine if the actor or a vehicle will actually be * performing the travel, by calling gActor.getTraveler(), if * the routine cares about the difference. */ } check() { local t = gActor.getTraveler(self); local dest; /* * Check the travel. * * This routine should take into account the light levels at * the source and destination locations, if travel between * dark rooms is to be disallowed. */ /* get my destination */ dest = getDestination(t.location, t); /* check dark-to-dark travel */ gActor.checkDarkTravel(dest, self); /* enforce barriers */ checkTravelBarriers(dest); } action() { local t = gActor.getTraveler(self); local dest; /* * Execute the travel, moving the command's actor through the * travel connection: we carry out any side effects of the * travel and deliver the actor (if appropriate) to the * destination of the connector. * * Note that this routine must check with the actor to * determine if the actor or a vehicle will actually be * performing the travel, by calling gActor.getTraveler(), if * the routine cares about the difference. In most cases, * the routine won't care: most implementations of this * routine will (if they effect any travel at all) eventually * call gActor.travelTo() to carry out the travel, and that * routine will always route the actual movement to the * vehicle if necessary. */ /* get my destination */ dest = getDestination(t.location, t); /* travel to my destination */ gActor.travelTo(dest, self, connectorBack(t, dest)); } } ; /* * A "probe" object, for testing light levels in rooms. This is a dummy * object that we use for what-if testing - it's not actually part of * the simulation. */ lightProbe: Thing; /* * A TravelBarrier can be attached to a TravelConnector, via the * travelBarrier property, to form a conditional barrier to travel. */ class TravelBarrier: object /* * Determine if this barrier blocks the given traveler. By default, * we don't block anyone. This doesn't make us much of a barrier, so * subclasses should override this with a more specific condition. */ canTravelerPass(traveler) { return true; } /* * Explain why travel isn't allowed. This should generate an * appropriate failure report explaining the problem. This is * invoked when travel is attempted and canTravelerPass returns nil. * Subclasses must override this. */ explainTravelBarrier(traveler) { } ; /* * An "unlisted proxy" connector acts as a proxy for another connector. * We act exactly like the underlying connector, except that we suppress * the connector from automatic exit lists. This can be used for cases * where an otherwise normal connector is needed but the connector is * not to appear in automatic exit lists (such as the status line). * * The most common situation where this kind of connector is useful is * where multiple directions in a given room all go to the same * destination. In these cases, it's often desirable for some of the * directions to be unlisted alternatives. The asExit() macro can be * used for convenience to set up these direction synonyms. */ class UnlistedProxyConnector: object construct(pri) { /* remember my underlying primary connector */ primaryConn = pri; } /* * Our underlying connector. Start out with a default TadsObject * rather than nil in case anyone wants to call a property or test * inheritance before we're finished with our constructor - this will * produce reasonable default behavior without having to test for nil * everywhere. */ primaryConn = TadsObject /* we're not listed */ isConnectorListed = nil /* map any TravelVia action to our underlying connector */ dobjFor(TravelVia) remapTo(TravelVia, primaryConn) /* redirect everything we don't handle to the underlying connector */ propNotDefined(prop, [args]) { return primaryConn.(prop)(args...); } /* * As a proxy, we don't want to disguise the fact that we're a proxy, * if someone specifically asks, so admist to being of our own true * kind; but we also act mostly like our underlying connector, so if * someone wants to know if we're one of those, say yes to that as * well. So, return true if the inherited version returns true, and * also return true if our primary connector would return true. */ ofKind(cls) { return inherited(cls) || primaryConn.ofKind(cls); } ; /* * A travel connector that doesn't allow any travel - if travel is * attempted, we simply use the origin's cannotTravel method to display * an appropriate message. */ noTravel: TravelConnector /* it is obvious that is no passage this way */ isConnectorApparent(origin, actor) { return nil; } /* this is not a passable connector */ isConnectorPassable(origin, traveler) { return nil; } dobjFor(TravelVia) { /* * we know that no travel will occur, so we don't need to satisfy * any preconditions */ preCond = [] action() { /* we can't go this way - use the origin's default message */ gActor.location.cannotTravel(); } } ; /* * An "ask which" travel connector. Rather than just traversing a * connector, we ask for a direct object for a specified travel verb; if * the player supplies the missing indirect object (or if the parser can * automatically choose a default), we'll perform the travel verb using * that direct object. * * This type of connector has two uses. * * First, the library various instances, with appropriate specified * travel verbs, as the default connector for certain directions that * frequently end up mapping to in-scenario objects. Specifically, * noTravelIn, noTravelDown, and noTravelOut are used as the default in, * down, and out connectors. If the player types DOWN, for example, and * there's no override for 'down' in a given room, then we'll invoke * noTravelDown; this will in turn ask for a missing direct object for * the GetOffOf action, since DOWN can mean getting off of a platform or * other nested room when on such a thing. When there's an obvious * thing to get down from, the parser will provide the default * automatically, which will make DOWN into a simple synonym for GET OFF * OF <whatever>. * * Second, games can use this kind of connector for a given direction * when the direction is ambiguous. For example, you can use this as * the 'north' connector when there are two doors leading north from the * location. When the player types NORTH, the parser will ask which * door the player wants to go through. */ class AskConnector: TravelConnector, ResolveAsker /* * The specific travel action to attempt. This must be a TAction - * an action that takes a direct object (and only a direct object). * The default is TravelVia, but this should usually be customized * in each instance to the type of travel appropriate for the * possible connectors. */ travelAction = TravelViaAction /* * The list of possible direct objects for the travel action. If * this is nil, we'll simply treat the direct object of the * travelAction as completely missing, forcing the parser to either * find a default or ask the player for the missing object. If the * travel is limited to a specific set of objects (for example, if * there are two doors leading north, and we want to ask which one * to use), this should be set to the list of possible objects; the * parser will then use the ambiguous noun phrase rules instead of * the missing noun phrase rules to ask the player for more * information. */ travelObjs = nil /* * The phrase to use in the disambiguation question to ask which of * the travelObjs entries is to be used. The language-specific * module provides a suitable default, but this should usually be * overridden if travelObjs is overridden. */ travelObjsPhrase = nil /* * An extra prompt message to show before the normal parser prompt * for a missing or ambiguous object. We'll show this just before * the normal parser message, if it's specified. * * If you want to customize the messages more completely, you can * override askDisambig() or askMissingObject(). The parser will * invoke these to generate the prompt, so you can customize the * entire messages by overriding these. */ promptMessage = nil /* * For each of the ResolveAsker methods that might be invoked, add * the promptMessage text before the normal parser question. */ askDisambig(targetActor, promptTxt, curMatchList, fullMatchList, requiredNum, askingAgain, dist) { promptMessage; inherited(targetActor, promptTxt, curMatchList, fullMatchList, requiredNum, askingAgain, dist); } askMissingObject(targetActor, action, which) { promptMessage; inherited(targetActor, action, which); } /* handle travel via this connector */ dobjFor(TravelVia) { /* * No preconditions or checks are necessary, since we don't * actually perform any travel on our own; we simply recast the * command as a new action, hence we want to delegate the * preconditions and check() handling to the replacement action. * Note that this means that you can't put a travel barrier * directly on an AskConnector - you have to put any barriers on * the underlying real connectors instead. */ preCond = [] check() { } /* * Recast the travel into our specified action, asking for the * direct object we need for that action. */ action() { /* * if we have a set of possible direct objects, retry this * with the ambiguous object set; otherwise, retry with a * completely missing direct object */ if (travelObjs != nil) travelAction.retryWithAmbiguousDobj( gAction, travelObjs, self, travelObjsPhrase); else travelAction.retryWithMissingDobj(gAction, self); } } /* * Get a connector leading to the given destination. We'll scan our * travel objects; for each one that's a TravelConnector, we'll ask * it to find the connector, and return the result if we get one. */ connectorGetConnectorTo(origin, traveler, dest) { /* if we have no travel objects, there's nothing to check */ if (travelObjs == nil) return nil; /* scan our secondary connectors */ foreach (local cur in travelObjs) { /* if this is a travel connector, ask it what it thinks */ if (cur.ofKind(TravelConnector)) { local conn; /* * if this secondary connector can give us a connector to * the destination, use that connector */ conn = cur.connectorGetConnectorTo(origin, traveler, dest); if (conn != nil) return conn; } } /* didn't find a match */ return nil; } ; /* * A "default ask connector" is an AskConnector that we use for certain * directions (down, in, out) as the library default connector for the * directions. */ class DefaultAskConnector: AskConnector /* * since this is a default connector for all locations, indicate that * no travel is apparently possible in this direction */ isConnectorApparent(origin, actor) { return nil; } /* this is not a passable connector */ isConnectorPassable(origin, traveler) { return nil; } ; /* * A default travel connector for going in. When travel in the relative * direction "in" isn't allowed, we'll try recasting the command as an * "enter" command with an unknown direct object. */ noTravelIn: DefaultAskConnector /* when we go 'in', we'll try to ENTER something */ travelAction = EnterAction ; /* * A default travel connector for going out. When travel in the * relative direction "out" isn't allowed, we'll try recasting the * command as an "get out of" command with an unknown direct object. */ noTravelOut: DefaultAskConnector /* when we go 'out', we'll try to GET OUT OF something */ travelAction = GetOutOfAction ; /* * A default travel connector for going out from a nested room. This * works the same way as noTravelOut, except that we'll show OUT as a * listed exit. */ nestedRoomOut: noTravelOut isConnectorApparent(origin, actor) { return true; } ; /* * A special travel connector for 'down' that recasts the command as a * "get off of" command. This can be used for platforms and the like, * where a 'down' command should usually be taken to mean "get off * platform" rather than "down from enclosing room". */ noTravelDown: DefaultAskConnector /* when we go 'down', we'll try to GET OFF OF something */ travelAction = GetOffOfAction ; /* * A travel connector for going in that explicitly redirects the command * to "enter" and asks for the missing direct object. This behaves the * same way as noTravelIn, but explicitly makes the inward travel * apparent; this can be used to override the noTravelIn default for * locations where travel in is explicitly allowed. */ askTravelIn: AskConnector travelAction = EnterAction ; /* explicitly redirect travel out to "get out of" */ askTravelOut: AskConnector travelAction = GetOutOfAction ; /* explicitly redirect travel down to "get off of" */ askTravelDown: AskConnector travelAction = GetOffOfAction ; /* * "No Shipboard Directions" travel connector. This is used as the * default connector for the shipboard directions for the base Room * class. This connector displays a special message indicating that the * room is not a boat hence the shipboard directions don't work here. */ noShipTravel: noTravel dobjFor(TravelVia) { action() { /* simply indicate that this direction isn't applicable here */ gLibMessages.notOnboardShip(); } } ; /* ------------------------------------------------------------------------ */ /* * A mix-in class that can be added to objects that also inherit from * TravelConnector to add a message as the connector is traversed. * * Note that this isn't itself a travel connector; it's just a class * that should be combined with TravelConnector or one of its * subclasses. This class should be in the superclass list before the * TravelConnector-derived superclass. */ class TravelWithMessage: object /* * My message to display when the player character traverses the * connector. This should be overridden with the custom message for * the connector. By default, if we're a Script, we'll invoke the * script to show the next message. */ travelDesc() { if (ofKind(Script)) doScript(); } /* * My message to display when any non-player character traverses the * connector. If this is not overridden, no message will be * displayed when an NPC travels through the connector. */ npcTravelDesc = "" /* * Display my message. By default, we show one message for the * player character and another message for NPC's. */ showTravelDesc() { if (gActor.isPlayerChar()) travelDesc; else npcTravelDesc; } /* on traversing the connector, show our message */ noteTraversal(traveler) { /* display my message */ showTravelDesc(); /* inherit any other superclass handling as well */ inherited(traveler); } ; /* * A simple connector that displays a message when the connector is * traversed. */ class TravelMessage: TravelWithMessage, TravelConnector /* my destination location */ destination = nil /* get my destination */ getDestination(origin, traveler) { return destination; } /* our source is the same as our destination */ fixedSource(dest, traveler) { return destination; } ; /* * A travel connector that can't be traversed, and which shows a custom * failure message when traversal is attempted. Instances should define * travelDesc to the message to display when travel is attempted. * * Travel is not apparently possible in this direction, so this type of * connector will not show up in automatic exit lists or maps. This * class is designed for connections that are essentially the same as no * connection at all, but where it's desirable to use a special message * to describe why travel can't be accomplished. */ class NoTravelMessage: TravelMessage dobjFor(TravelVia) { /* as in noTravel, we need no preconditions or checks */ preCond = [] action() { /* simply show my message - that's all we do */ showTravelDesc(); } } /* * Because no travel is possible, we want a non-empty message for * NPC's as well as for the PC; by default, use the same message for * all actors by using travelDesc for NPC's. */ npcTravelDesc = (travelDesc) /* travel is not apparently possible in this direction */ isConnectorApparent(origin, actor) { return nil; } isConnectorPassable(origin, traveler) { return nil; } ; /* * A "fake" connector. This is a connector that doesn't actually allow * travel, but acts like it *could*. We simply show a special message * when travel is attempted, but we don't move the actor. * * This is a subclass of NoTravelMessage, so instances should customize * travelDes with the special failure message when travel is attempted. * * Note that this type of connector is by default *apparently* an exit, * even though it doesn't actually go anywhere. This is useful for "soft * boundaries," where the game's map is meant to appear to the player to * continue but beyond which no more locations actually exist in the map. * This is an oft-used device meant to create an illusion that the game's * map exists in a larger world even though the larger world is not * modeled. The message we display should in such cases attribute the * actor's inability to traverse the connector to a suitable constraint * within the context of the game world; for example, the actor could be * said to be unwilling to go beyond this point because the actor knows * or suspects there's nothing for a long way in this direction, or * because the actor's goals require staying within the modeled map, or * because the actor is afraid of what lies beyond. * * Note that FakeConnector should only be used when the reason for * blocking the travel is apparent before we even try travel. In * particular, FakeConnector is good for motivational reasons not to * travel, where the actor decides for reasons of its own not to even * attempt the travel. It's especially important not to use * FakeConnector in cases where the travel is described as attempted but * aborted halfway in - things like encountering a blocked tunnel. This * is important because FakeConnector aborts the travel immediately, * before sending out any of the notifications that would accompany * ordinary travel, which means that physical barriers (trolls blocking * the way, being tied to a chair) that would otherwise block ordinary * travel will be bypassed. For cases where travel is attempted, but * something turns you back halfway in, use DeadEndConnector. */ class FakeConnector: NoTravelMessage /* * travel is *apparently* possible in this direction (even though * it's not *actually* possible) */ isConnectorApparent(origin, actor) { return true; } isConnectorPassable(origin, traveler) { return nil; } ; /* ------------------------------------------------------------------------ */ /* * A Dead End Connector is a connector that appears to lead somewhere, * but which turns out to be impassable for reasons that aren't apparent * until we get some distance into the passage. * * The Dead End Connector might look a lot like the Fake Connector, but * there's an important difference. A Fake Connector is a connector that * can't even be physically attempted: the reason not to take a fake * connector is something that shows up before we even start moving, * usually a motivational reason ("You really can't leave town until you * find your missing brother"). A Dead End Connector, on the other hand, * is meant to model a physical attempt to travel that's blocked by some * problem halfway along, such as travel down a tunnel that turns out to * have caved in. */ class DeadEndConnector: TravelMessage /* * The apparent destination name. If the actor is meant to know the * apparent destination from the outset, or if traversing the * connector gives the actor an idea of where the connector * supposedly goes, this can be used to give the name of that * destination. This name will show up in exit listings, for * example, once the PC knows where the connector supposedly goes. * * Note that this isn't the actual destination of the connector, * since the actual destination is simply back to the origin (that's * the whole point of the dead end, after all). This is simply where * the connector *appears* to go. If an attempted traversal doesn't * even reveal that much, then you should just leave this nil, since * the destination will never become apparent to the PC. */ apparentDestName = nil /* * Our apparent destination. By default, we create a FakeDestination * object to represent our apparent destination if we have a non-nil * name for the apparent destination. * * If the supposed-but-unreachable destination of the connector is in * fact a real location in the game, you can override this to point * directly to that actual location. This default is for the typical * case where the supposed destination doesn't actually exist on the * game map as a real room. */ apparentDest() { /* * if we have an apparent destination name, create a * FakeDestination to represent the apparent destination, and * plug that in as our apparent destination for future reference */ if (apparentDestName != nil) { local fake; /* create a fake destination */ fake = new FakeDestination(self); /* plug it in as our new apparentDest for future calls */ apparentDest = fake; /* return the new object as the result */ return fake; } /* our apparent destination is unknown */ return nil; } /* get our apparent destination */ getApparentDestination(origin, actor) { /* * If the actor knows the destination for the given connector (as * determined by the origin room), or we have a memory of this * traversal, return our fake destination object to represent the * destination. We can only return our fake destination if we * have an explicit apparent destination, since it's only in this * case that our apparent destination ever actually becomes * known, even after an attempted traversal. * * Our actual destination is always just the origin, but we * *appear* to have some other destination. Even though we can * never actually reach that other, apparent destination, we at * least want to give the appearance of going there, which we * provide through our fake destination object. */ if (apparentDest != nil && (origin.actorKnowsDestination(actor, self) || travelMemory[[actor, origin, self]] != nil)) return apparentDest; /* our unreachable destination is not apparent */ return nil; } /* there's no corresponding connector back for a dead end */ connectorBack(traveler, dest) { return nil; } /* our actual destination is always our origin */ getDestination(origin, traveler) { return origin; } /* do remember circular trips, since that's the only kind we make */ rememberCircularPassage = true ; /* * A fake apparent destination, for dead-end connectors. The dead-end * connector will create an object of this class to represent its * apparent but actually unreachable destination, if it has an apparent * destination name. */ class FakeDestination: object /* construct - remember our associated connector */ construct(conn) { connector = conn; } /* get our destination name - this is the name from our connector */ getDestName(actor, origin) { return connector.apparentDestName; } /* our underlying connector (usually a DeadEndConnector) */ connector = nil ; /* ------------------------------------------------------------------------ */ /* * A direct room-to-room connector. In most cases, it is not necessary * to create one of these objects, because rooms can serve as their own * connectors. These objects are needed in certain cases, though, such * as when a room-to-room connection requires a travel barrier. */ class RoomConnector: TravelConnector /* the two rooms we link */ room1 = nil room2 = nil /* * get the destination, given the origin: this is the one of the two * rooms we link that we're not in now */ getDestination(origin, traveler) { if (origin == room1) return room2; else if (origin == room2) return room1; else return nil; } fixedSource(origin, traveler) { /* * we're a symmetrical two-way connector, so our source from the * perspective of one room is the same as the destination from * that same perspective */ return getDestination(origin, traveler); } /* * Get the precondition for this connector. The normal * TravelConnector rule that the traveler must be in the outbound * connector's location is not meaningful for an abstract room * connector, because this type of connection itself isn't * represented as a physical game object with a location; it's just * an abstract data structure. This means that we must have a * directional property in the *source* location that points directly * to the destination (i.e., self). * * So, the appropriate starting point for room connectors is the * object that contains the connection. In other words, we must * search for the nearest object enclosing the *traveler* that has a * direction property directly linked to 'self'; that enclosing * container is the required starting location for the travel. */ connectorTravelPreCond() { /* * Scan upwards from the traveler's location, looking for an * object that has a directional property linked to self. Only * scan into locations that the actor can see. */ for (local loc = gActor.getTraveler(self).location ; loc != nil && gActor.canSee(loc) ; loc = loc.location) { /* look for a directional connector directly from 'loc' to us */ if (loc.localDirectionLinkForConnector(self) != nil) { /* * we're linked from this enclosing location, so this is * where we have to be before travel */ return [new TravelerDirectlyInRoom(gActor, self, loc)]; } } /* we couldn't find a link, so apply no condition */ return []; } ; /* * A one-way room connector. This works like an ordinary room * connector, but connects only in one direction. To use this class, * simply define the 'destination' property to point to the room we * connect to. */ class OneWayRoomConnector: RoomConnector /* my destination - instances must define this */ destination = nil /* we always have a fixed destination */ getDestination(origin, traveler) { return destination; } ; /* * A room "auto-connector". This is a special subclass of RoomConnector * that can be mixed in to any BasicLocation subclass to make the room * usable as the direct target of a directional property in another room * - so you could say "east = myRoom", for example, without creating any * intermediate connector. */ class RoomAutoConnector: RoomConnector /* * Suppose that roomA.north = roomB. This means that if an actor is * in roomA, and executes a "north" command, we'll execute a * TravelVia action on room B, because the "connector" will be * roomB. In these cases, the destination of the travel and the * travel connector are one and the same. So, when the connector is * roomB, the destination of travel is also simply roomB. */ getDestination(origin, traveler) { /* we are our own destination */ return self; } ; /* ------------------------------------------------------------------------ */ /* * Base class for passages between rooms. This can be used for a * passage that not only connects the rooms but also exists as an object * in its own right within the rooms it connects. * * In most cases, two passage objects will exist - one on each side of * the passage, so one object in each room connected. One of the * objects should be designated as the "master"; the other is the * "slave." The master object is the one which should implement all * special behavior involving state changes (such as opening or closing * a door). * * This basic passage is not designed to be opened and closed; use Door * for a passage that can be opened and closed. */ class Passage: Linkable, Fixture, TravelConnector /* * Our destination - this is where the actor ends up when traveling * through the passage (assuming the passage is open). By default, * we return the "room location" of our other side's container; in * cases where our other side is not directly in our destination * room (for example, the other side is part of some larger object * structure), this property should be overridden to specify the * actual destination. * * If our otherSide is nil, the passage doesn't go anywhere. This * can be useful to create things that look and act like passages, * but which don't actually go anywhere - in other words, * passage-like decorations. */ destination = (otherSide != nil ? otherSide.location.roomLocation : nil) /* get my destination - just return my 'destination' property */ getDestination(origin, traveler) { return destination; } /* get our open/closed status */ isOpen() { /* * if we have a separate master object, defer to it; otherwise, * use our own status */ return (masterObject == self ? isOpen_ : masterObject.isOpen); } /* internal open/closed status - open by default */ isOpen_ = true /* * We're not visible in the dark if we're closed. If we're open, * the normal rules apply. * * Normally, a passage is visible in the dark if there's light in * the adjoining location: our model is that enough light is leaking * in through the passage to make the passage itself visible, but * not enough to light anything else in the current room. In the * case of a closed passage, though, we assume that it completely * blocks any light from the other room, eliminating any indication * of a passage. * * If you do want an openable passage to be visible in the dark even * when it's closed, it's probably better to make the passage * self-illuminating (i.e., with brightness 1), because this will * put the passage in scope and thus allow it to be manipulated. */ isConnectorVisibleInDark(origin, actor) { return isOpen() && inherited(origin, actor); } /* a passage is passable when it's open */ isConnectorPassable(origin, traveler) { return isOpen(); } /* * Initialize. If we're a slave, we'll set up the otherSide * relationship between this passage and our master passage. */ initializeThing() { /* inherit default handling */ inherited(); /* * if we have a master side, initialize our relationship with * the master side */ if (masterObject != self) { /* set our otherSide to point to the master */ otherSide = masterObject; /* set the master's otherSide to point to us */ masterObject.initMasterObject(self); } } /* * Initialize the master object. The other side of a two-sided door * will call this on the master object to let the master object know * about the other side. 'other' is the other-side object. By * default, we'll simply remember the other object in our own * 'otherSide' property. */ initMasterObject(other) { otherSide = other; } /* * Our corresponding passage object on the other side of the * passage. This will be set automatically during initialization * based on the masterObject property of the slave - it is not * generally necessary to set this manually. */ otherSide = nil /* our other side is the other facet of the passage */ getFacets() { return otherSide != nil ? [otherSide] : inherited(); } /* * our source is always our destination, since we have a one-to-one * relationship with our comlementary passage in the destination * location (we point to it, it points to us) */ fixedSource(origin, traveler) { return destination; } /* the connector back is our complementary side, if we have one */ connectorBack(traveler, dest) { /* if we have a complementary side, it's the connector back */ if (otherSide != nil) return otherSide; /* we don't have a complementary side, so use the default handling */ return inherited(traveler, dest); } /* can the given actor travel through this passage? */ canActorTravel(actor) { /* * by default, the actor can travel through the passage if and * only if the passage is open */ return isOpen; } /* * Display our message when we don't allow the actor to travel * through the passage because the passage is closed. By default, * we'll simply display the default cannotTravel message for the * actor's location, but this can be overridden to provide a more * specific report of the problem. */ cannotTravel() { /* use the actor's location's cannotTravel handling */ gActor.location.cannotTravel(); } /* carry out travel via this connector */ dobjFor(TravelVia) { /* check travel */ check() { /* * Move the actor only if we're open; if we're not, use the * standard no-travel handling for the actor's location. * Note that we don't try to implicitly open the passage, * because the basic passage is not openable; if we're * closed, it means we're impassable for some reason that * presumably cannot be remedied by a simple "open" command. */ if (!canActorTravel(gActor)) { /* we're closed, so use our no-travel handling */ cannotTravel(); exit; } else { /* inherit the default checks */ inherited(); } } } dobjFor(LookThrough) { action() { /* * if we're open, we simply "can't see much from here"; * otherwise, we can't see through it at all */ if (isOpen) mainReport(¬hingThroughPassageMsg); else inherited(); } } ; /* * A passage that an actor can travel through (with a "go through" or * "enter" command). A "go through" command applied to the passage * simply makes the actor travel through the passage as though using the * appropriate directional command. * * We describe actors arriving or departing via the passage using * "through" descriptions ("Bob arrives through the door," etc). */ class ThroughPassage: Passage describeDeparture(traveler, origin, dest) { /* describe the traveler departing through this passage */ traveler.sayDepartingThroughPassage(self); } describeArrival(traveler, origin, dest) { /* describe the traveler arriving through this passage */ traveler.sayArrivingThroughPassage(self); } /* treat "go through self" as travel through the passage */ dobjFor(GoThrough) remapTo(TravelVia, self) /* "enter" is the same as "go through" for this type of passage */ dobjFor(Enter) asDobjFor(GoThrough) /* * Explicitly map the indirect object 'verify' handlers for the * push-travel commands corresponding to the regular travel commands * we provide. This isn't strictly necessary except when we're mixed * in to something like a Decoration, in which case explicitly * defining the mapping here is important because it will give the * mapping higher precedence than a catch-all handlers overriding the * same mapping we inherit from Thing. We use the same mapping that * Thing provides by default. */ mapPushTravelIobj(PushTravelThrough, TravelVia) mapPushTravelIobj(PushTravelEnter, TravelVia) ; /* * A Path Passage is a specialization of through passage that's more * suitable for outdoor locations. This type of passage is good for * things like outdoor walkways, paths, and streets, where travelers walk * along the connector but aren't enclosed by it. The main practical * difference is how we describe departures and arrivals; in English, we * describe these as being "via" the path rather than "through" the path, * since there's no enclosure involved in these connections. */ class PathPassage: ThroughPassage describeDeparture(traveler, origin, dest) { traveler.sayDepartingViaPath(self); } describeArrival(traveler, origin, dest) { /* describe the traveler arriving through this passage */ traveler.sayArrivingViaPath(self); } /* putting something on a path is the same as dropping it */ iobjFor(PutOn) remapTo(Drop, DirectObject) /* use a special message for standing on a path */ cannotStandOnMsg = &cannotStandOnPathMsg /* FOLLOW PATH -> travel via the path */ dobjFor(Follow) remapTo(TravelVia, DirectObject) ; /* ------------------------------------------------------------------------ */ /* * The exit portal of a one-way passage. This isn't a fully functional * passage, but rather an object that acts as the receiving end of a * passage that can only be traversed in one direction. * * This can be used for various purposes: the underside of a trap door, * the bottom of a chute, the exit of a wormhole. */ class ExitOnlyPassage: ThroughPassage dobjFor(TravelVia) { action() { /* * Show our default failure message. This can be overridden * to provide a more customized description of why the * passage cannot be entered from this side. */ reportFailure(&cannotEnterExitOnlyMsg, self); } } ; /* ------------------------------------------------------------------------ */ /* * Stairway - this is a special kind of passage that is used for * vertical connections, such as stairways and ladders. This type of * passage doesn't allow "enter" or "go through," but does allow "climb". * * The basic Stairway should generally not be used, as it doesn't know * where it is relative to connected stairs. Instead, the more specific * StairwayUp and StairwayDown should be used in most cases. * * Note that at midpoints along long stairways (landings, for example), * separate stairway objects should normally be used: one going up and * one going down. */ class Stairway: Passage /* * Treat "climb self" as travel through the passage */ dobjFor(Climb) remapTo(TravelVia, self) ; /* * A stairway going up from here. */ class StairwayUp: Stairway describeArrival(traveler, origin, dest) { /* * describe the actor arriving by coming down these stairs (they * leave up, so they arrive down) */ traveler.sayArrivingDownStairs(self); } describeDeparture(traveler, origin, dest) { /* describe the actor leaving up these stairs */ traveler.sayDepartingUpStairs(self); } /* "climb up" is the specific direction for "climb" here */ dobjFor(ClimbUp) asDobjFor(Climb) /* cannot climb down from here */ dobjFor(ClimbDown) { verify() { illogical(&stairwayNotDownMsg); } } ; /* * A stairway going down from here. */ class StairwayDown: Stairway /* "climb down" is the specific direction for "climb" here */ dobjFor(ClimbDown) asDobjFor(Climb) describeArrival(traveler, origin, dest) { /* * describe the actor arriving by coming up these stairs (they * leave down, so they arrive up) */ traveler.sayArrivingUpStairs(self); } describeDeparture(traveler, origin, dest) { /* describe the actor traveling down these stairs */ traveler.sayDepartingDownStairs(self); } /* cannot climb up from here */ dobjFor(ClimbUp) { verify() { illogical(&stairwayNotUpMsg); } } ; /* ------------------------------------------------------------------------ */ /* * Basic Door class. This is the base class for door-like objects: a * travel connector with two sides, and which can be opened and closed. * Each side of the door is linked to the other, so that opening or * closing one side makes the same change to the other side. * * A basic door has the internal functionality of a door, but doesn't * provide handling for player commands that manipulate the open/closed * status. */ class BasicDoor: BasicOpenable, ThroughPassage /* * Open/close the door. If we have a complementary door object * representing the other side, we'll remark in the sensory context * of its location that it's also opening or closing. */ makeOpen(stat) { /* inherit the default behavior */ inherited(stat); /* * if our new status is in effect, notify the other side so that * it can generate a message in its location */ if (isOpen == stat && otherSide != nil) otherSide.noteRemoteOpen(stat); } /* * Note a "remote" change to our open/close status. This is an * open/close operation performed on our complementary object * representing the other side of the door. We'll remark on the * change in the sensory context of this side, but only if we're * suppressing output in the current context - if we're not, then * the player will see the message generated by the side that we * directly acted upon, so we don't need a separate report for the * other side. */ noteRemoteOpen(stat) { /* * If I'm not visible to the player character in the current * sense context, where the action is actually taking place, * switch to my own sensory context and display a report. This * way, if the player can see this door but not the other side, * and the action is taking place on the other side, we'll still * see a note about the change. We only need to do this if * we're not already visible to the player, because if we are, * we'll generate an ordinary report of the door opening in the * action's context. */ if (senseContext.isBlocking) { /* show a message in my own sensory context */ callWithSenseContext(self, sight, {: describeRemoteOpen(stat) }); } } /* * Describe the door being opened remotely (that is, by someone on * the other side). This is called from noteRemoteOpen to actually * display the message. */ describeRemoteOpen(stat) { /* show the default library message for opening the door remotely */ gLibMessages.sayOpenDoorRemotely(self, stat); } /* carry out travel through the door */ dobjFor(TravelVia) { action() { /* move the actor to our destination */ inherited(); /* remember that this is the last door the actor traversed */ gActor.rememberLastDoor(self); } } /* * Boost the likelihood that a command is referring to us if we just * traveled through the door; this is meant to be called from * verify() routines. For a few commands (close, lock), the most * likely door being referred to is the door just traversed. */ boostLikelihoodOnTravel() { /* * if this (or our other side) is the last door the actor * traversed, boost the likelihood that they're referring to us */ if (gActor.lastDoorTraversed == self || (otherSide != nil && gActor.lastDoorTraversed == otherSide)) logicalRank(120, 'last door traversed'); } ; /* ------------------------------------------------------------------------ */ /* * Door. This is a travel connector that can be opened and closed with * player commands. This is a simple subclass of BasicDoor that adds * support for player commands to manipulate the door. */ class Door: Openable, BasicDoor /* make us initially closed */ initiallyOpen = nil /* if we can't travel because the door is closed, say so */ cannotTravel() { if (gActor.canSee(self) && !isOpen) reportFailure(&cannotGoThroughClosedDoorMsg, self); else inherited(); } /* * get the 'door open' precondition - by default, we create a * standard doorOpen precondition for this object, but this can be * overridden if desired to create custom doorOpen variations */ getDoorOpenPreCond() { return new ObjectPreCondition(self, doorOpen); } /* the door must be open before we can travel this way */ connectorTravelPreCond() { local ret; local doorCond; /* start with the inherited conditions */ ret = inherited(); /* if there's a door-open condition, add it as well */ if ((doorCond = getDoorOpenPreCond()) != nil) ret += doorCond; /* return the result */ return ret; } dobjFor(Close) { verify() { /* inherit default handling */ inherited(); /* boost the likelihood if they just traveled through us */ boostLikelihoodOnTravel(); } } dobjFor(Lock) { verify() { /* inherit default handling */ inherited(); /* boost the likelihood if they just traveled through us */ boostLikelihoodOnTravel(); } } /* * looking behind a door implies opening it to see what's on the * other side */ dobjFor(LookBehind) { preCond = (inherited + objOpen) action() { /* * If the door is open, AND we implicitly opened the door to * carry out this action, use a special report that takes * into account that we were specifically looking to see what * was on the other side of the door; otherwise, use the * default behavior */ if (isOpen && gTranscript.currentActionHasReport( {x: (x.action_.ofKind(OpenAction) && x.isActionImplicit())})) mainReport(¬hingBeyondDoorMsg); else inherited(); } } /* looking through a door requires it to be open */ dobjFor(LookThrough) { preCond = (nilToList(inherited) + objOpen) } ; /* ------------------------------------------------------------------------ */ /* * Secret Door. This is a special kind of door that gives no hint of * its being a door when it's closed. This can be used for objects that * serve as a secret passage but are otherwise normal objects, such as a * bookcase that conceals a passage. */ class SecretDoor: BasicDoor /* a secret passage usually starts off secret (i.e., closed) */ initiallyOpen = nil isConnectorApparent(origin, actor) { /* * A secret passage is not apparent as a pasage unless it's * open. */ if (isOpen) { /* the passage is open - use the inherited handling */ return inherited(origin, actor); } else { /* * the passage is closed, so it's not apparently a passage * at all */ return nil; } } ; /* * Hidden Door. This is a secret door that is invisible when closed. * This can be used when the passage isn't even visible when it's * closed, such as a door that seamlessly melds into the wall. */ class HiddenDoor: SecretDoor /* * When we're closed, we're completely invisible, so we have no * sight presence. When we're open, we have our normal visual * presence. */ sightPresence = (isOpen) ; /* ------------------------------------------------------------------------ */ /* * Automatically closing door. This type of door closes behind an actor * as the actor traverses the connector. */ class AutoClosingDoor: Door dobjFor(TravelVia) { action() { /* inherit the default handling */ inherited(); /* * Only close the door if the actor performing the travel * isn't accompanying another actor. If the actor is * accompanying someone, we essentially want them to hold the * door for the other actor - at the very least, we don't * want to slam it in the other actor's face! */ if (!gActor.curState.ofKind(AccompanyingInTravelState)) { /* close the door */ makeOpen(nil); /* mention that the automatic closing */ reportAutoClose(); } } } /* * Report the automatic closure. The TravelVia action() calls this * after closing the door to generate a message mentioning that the * door was closed. By default, we just show the standard * doorClosesBehindMsg library message. */ reportAutoClose() { mainReport(&doorClosesBehindMsg, self); } ; /* ------------------------------------------------------------------------ */ /* * The base class for Enterables and Exitables. These are physical * objects associated with travel connectors. For example, the object * representing the exterior of a building in the location containing * the building could be an Enterable, so that typing ENTER BUILDING * takes us into the building via the travel connector that leads inside. * * Enterables and Exitables are physical covers for travel connectors. * These objects aren't travel connectors themselves, and they don't * specify the destination; instead, these just point to travel * connectors. */ class TravelConnectorLink: object /* the underlying travel connector */ connector = nil /* * The internal "TravelVia" action just maps to travel via the * underlying connector. However, we want to apply our own * preconditions, so we don't directly remap to the underlying * connector. Instead, we provide our own full TravelVia * implementation, and then we perform the travel on the underlying * connector via a replacement action in our own action() handler. */ dobjFor(TravelVia) { /* the physical link object has to be touchable */ preCond = [touchObj] verify() { } action() { /* carry out the action by traveling via our connector */ replaceAction(TravelVia, connector); } } /* * These objects are generally things like buildings (exterior or * interior), which tend to be large enough that their details can be * seen at a distance. */ sightSize = large ; /* * An Enterable is an object that exists in one location, and which can * be entered to take an actor to another location. Enterables are used * for things such as the outsides of buildings, so that the building * can have a presence on its outside and can be entered via a command * like "go into building". * * An Enterable isn't a connector, but points to a connector. This type * of object is most useful when there's already a connector that exists * as a separate object, such as the door to a house: the house object * can be made an Enterable that simply points to the door object. */ class Enterable: TravelConnectorLink, Fixture /* * "Enter" action this simply causes the actor to travel via the * connector. */ dobjFor(Enter) remapTo(TravelVia, self) /* explicitly define the push-travel indirect object mapping */ mapPushTravelIobj(PushTravelEnter, TravelVia) ; /* * An Exitable is like an Enterable, except that you exit it rather than * enter it. This can be used for objects representing the current * location as an enclosure (a jail cell), or an exit door. */ class Exitable: TravelConnectorLink, Fixture /* Get Out Of/Exit action - this simply maps to travel via the connector */ dobjFor(GetOutOf) remapTo(TravelVia, self) /* explicitly define the push-travel indirect object mapping */ mapPushTravelIobj(PushTravelGetOutOf, TravelVia) ; /* * An EntryPortal is just like an Enterable, except that "go through" * also works on it. Likewise, an ExitPortal is just like an Exitable * but accepts "go through" as well. */ class EntryPortal: Enterable dobjFor(GoThrough) remapTo(TravelVia, self) ; class ExitPortal: Exitable dobjFor(GoThrough) remapTo(TravelVia, self) ; /* ------------------------------------------------------------------------ */ /* * A "TravelPushable" is an object that can't be taken, but can be moved * from one location to another via commands of the form "push obj dir," * "push obj through passage," and the like. * * TravelPushables tend to be rather rare, and we expect that instances * will almost always be special cases that require additional * specialized code. This is therefore only a general framework for * pushability. */ class TravelPushable: Immovable cannotTakeMsg = &cannotTakePushableMsg cannotMoveMsg = &cannotMovePushableMsg cannotPutMsg = &cannotPutPushableMsg /* can we be pushed via the given travel connector? */ canPushTravelVia(connector, dest) { return true; } /* explain why canPushTravelVia said we can't be pushed this way */ explainNoPushTravelVia(connector, dest) { } /* * Receive notification that we're about to be pushed somewhere. * This is called just before the underlying traveler performs the * actual travel. (By default, we don't even define this method, to * ensure that if we're combined with another base class that * overrides the method, the overrider will be called.) */ // beforeMovePushable(traveler, connector, dest) { } /* * Move the object to a new location as part of a push-travel * operation. By default, this simply uses moveInto to move the * object, but subclasses can override this to apply conditions to * the pushing, put the object somewhere else in some cases, display * extra messages, or do anything else that's necessary. * * Our special PushTraveler calls this routine after the underlying * real traveler has finished its travel to the new location, so the * traveler's location will indicate the destination of the travel. * Note that this routine is never called if the traveler ends up in * its original location after the travel, so this routine isn't * called when travel isn't allowed for the underlying traveler. */ movePushable(traveler, connector) { /* move me to the traveler's new location */ moveIntoForTravel(traveler.location); /* describe what we're doing */ describeMovePushable(traveler, connector); } /* * Describe the actor pushing the object into the new location. * This is called from movePushable; we pull this out as a separate * routine so that the description of the pushing can be overridden * without having to override all of movePushable. */ describeMovePushable(traveler, connector) { /* * If the actor is the player character, mention that we're * pushing the object with us. For an NPC, show nothing, * because the normal travel message will mention that the NPC * is pushing the object as part of the normal travel report * (thanks to PushTraveler's name override). */ if (gActor.isPlayerChar) mainReport(&okayPushTravelMsg, self); } dobjFor(PushTravel) { verify() { } action() { local newTrav; local oldTrav; local wasExcluded; /* * Create a push traveler to coordinate the travel. The * push traveler performs the normal travel, and also moves * the object being pushed. */ newTrav = pushTravelerClass.createInstance( self, gActor.getPushTraveler(self)); /* set the actor's special traveler to our push traveler */ oldTrav = gActor.setSpecialTraveler(newTrav); /* * Add myself to the actor's look-around exclusion list - * this ensures that we won't be listed among the objects in * the new location when we show the description on arrival. * This is desirable because of how we describe the travel: * we first want to show the new location's description, then * describe pushing this object into the new location. But * we actually have to move the object first, to make sure * any side effects of its presence in the new location are * taken into account when we describe the location - because * by the time we're in the room enough to have a look * around, the pushable will be in the room with us. Even * so, we don't actually want to describe the pushable as in * the room at this stage, because from the player's * perspective we're really still in the process of pushing * it; describing it as already settled into the new location * sounds weird because it makes it seem like the object was * already in place when we arrived. To avoid this * weirdness, suppress it from the new location's initial * description on arrival. */ wasExcluded = gActor.excludeFromLookAround(self); /* make sure we undo our global changes when we're done */ try { /* * Now that we've activated our special push traveler * for the actor, simply perform the ordinary travel * command. The travel command we perform depends on * the kind of push-travel command we attempted, so * simply let the current action carry out the action. */ gAction.performTravel(); } finally { /* restore the actor's old special traveler on the way out */ gActor.setSpecialTraveler(oldTrav); /* * if we weren't in the actor's 'look around' exclusion * list before, remove us from the list */ if (!wasExcluded) gActor.unexcludeFromLookAround(self); } } } /* * The class we create for our special push traveler - by default, * this is PushTraveler, but we parameterize this via this property * to allow special PushTraveler subclasses to be created; this * could be useful, for example, to customize the traveler name * messages. */ pushTravelerClass = PushTraveler ; /* * A special Traveler class for travel involving pushing an object from * one room to another. This class encapsulates the object being pushed * and the actual Traveler performing the travel. * * For the most part, we refer Traveler methods to the underlying * Traveler. We override a few methods to provide special handling. */ class PushTraveler: object construct(obj, traveler) { /* remember the object being pushed and the real traveler */ obj_ = obj; traveler_ = traveler; } /* the object being pushed */ obj_ = nil /* * the underlying Traveler - this is the real Traveler that will * move to a new location */ traveler_ = nil /* * Travel to a new location. We'll run the normal travel routine * for the underlying real traveler; then, if we ended up in a new * location, we'll move the object being pushed to the traveler's * new location. */ travelerTravelTo(dest, connector, backConnector) { local oldLoc; local origin; /* remember the traveler's origin, so we can tell if we moved */ origin = traveler_.location; /* let the object being pushed describe the departure */ obj_.beforeMovePushable(traveler_, connector, dest); /* * *Tentatively* move the pushable to its new location. Just do * the basic move-into for this, since it's only tentative - this * is just so that we get any side effects of its presence in the * new location for the purposes of describing the new location. */ oldLoc = location; obj_.baseMoveInto(dest); /* * Call the traveler's normal travel routine, to perform the * actual movement of the underlying traveler. */ traveler_.travelerTravelTo(dest, connector, backConnector); /* undo the tentative move of the pushable */ obj_.baseMoveInto(oldLoc); /* * If we moved to a new location, we can now actually move the * pushable object to the new location. */ if (traveler_.location != origin) obj_.movePushable(traveler_, connector); } /* * Perform local travel, between nested rooms within a top-level * location. By default, we simply don't allow pushing objects * between nested rooms. * * To allow pushing an object between nested rooms, override this in * parallel with travelerTravelTo(). Note that you'll have to call * travelerTravelWithin() on the underlying traveler (which will * generally be the actor), and you'll probably want to set up a new * set of notifiers parallel to beforeMovePushable() and * movePushable(). You'll probably particularly need to customize * the report in your parallel for movePushable() - the default ("you * push x into the area") isn't very good when nested rooms are * involved, and you'll probably want something more specific. */ travelerTravelWithin(actor, dest) { reportFailure(&cannotPushObjectNestedMsg, obj_); exit; } /* * Can we travel via the given connector? We'll ask our underlying * traveler first, and if that succeeds, we'll ask the object we're * pushing. */ canTravelVia(connector, dest) { /* ask the underlying traveler first, then our pushed object */ return (traveler_.canTravelVia(connector, dest) && obj_.canPushTravelVia(connector, dest)); } /* * Explain why the given travel is not possible. If our underlying * traveler raised the objection, let it explain; otherwise, let our * pushed object explain. */ explainNoTravelVia(connector, dest) { if (!traveler_.canTravelVia(connector, dest)) traveler_.explainNoTravelVia(connector, dest); else obj_.explainNoPushTravelVia(connector, dest); } /* by default, send everything to the underlying Traveler */ propNotDefined(prop, [args]) { return traveler_.(prop)(args...); } ; /* * A PushTravelBarrier is a TravelConnector that allows regular travel, * but not travel that involves pushing something. By default, we block * all push travel, but subclasses can customize this so that we block * only specific objects. */ class PushTravelBarrier: TravelBarrier /* * Determine if the given pushed object is allowed to pass. Returns * true if so, nil if not. By default, we'll return nil for every * object; subclasses can override this to allow some objects to be * pushed through the barrier but not others. */ canPushedObjectPass(obj) { return nil; } /* explain why an object can't pass */ explainTravelBarrier(traveler) { reportFailure(&cannotPushObjectThatWayMsg, traveler.obj_); } /* * Determine if the given traveler can pass through this connector. * If the traveler isn't a push traveler, we'll allow the travel; * otherwise, we'll block the travel if our canPushedObjectPass * routine says the object being pushed can pass. */ canTravelerPass(traveler) { /* if it's not a push traveler, it can pass */ if (!traveler.ofKind(PushTraveler)) return true; /* it can pass if we can pass the object being pushed */ return canPushedObjectPass(traveler.obj_); } ; /* ------------------------------------------------------------------------ */ /* * A basic location - this is the base class for locations that can * contain actors. */ class BasicLocation: Thing /* * Get the nested room list grouper for an actor in the given * posture directly in this room. This is used when we're listing * the actors within the nested room as * * By default, we maintain a lookup table, and store one nested * actor grouper object for each posture. This makes it so that we * show one group per posture in this room; for example, if we * contain two sitting actors and three standing actors, we'll say * something like "bill and bob are sitting on the stage, and jill, * jane, and jack are standing on the stage." This can be * overridden if a different arrangement of groups is desired; for * example, an override could simply return a single grouper to list * everyone in the room together, regardless of posture. */ listWithActorIn(posture) { /* if we don't have a lookup table for this yet, create one */ if (listWithActorInTable == nil) listWithActorInTable = new LookupTable(5, 5); /* if this posture isn't in the table yet, create a grouper for it */ if (listWithActorInTable[posture] == nil) listWithActorInTable[posture] = new RoomActorGrouper(self, posture); /* return the grouper for this posture */ return listWithActorInTable[posture]; } /* * our listWithActorIn table - this gets initialized to a * LookupTable as soon as we need one (in listWithActorIn) */ listWithActorInTable = nil /* * Check the ambient illumination level in the room for the given * actor's senses to determine if the actor would be able to see if * the actor were in this room without any additional light sources. * Returns true if the room is lit for the purposes of the actor's * visual senses, nil if not. * * Note that if the actor is already in this location, there's no * need to run this test, since we could get the information from * the actor directly. The point of this test is to determine the * light level in this location without the actor having to be * present. */ wouldBeLitFor(actor) { /* * Check for a simple, common case before running the more * expensive full what-if test. Most rooms provide their own * illumination in the actor's 'sight' sense; if this room does * provide its own interior 'brightness' of at least 2, and the * actor has 'sight' among its visual senses, then the room is * lit. Note that we must use transSensingOut() to determine * how we transmit our own brightness to our interior, since * that gives the transparency for looking from within this room * at objects outside the room; our own intrinsic brightness is * defined as the brightness of our exterior surface. */ if (actor.sightlikeSenses.indexOf(sight) != nil && brightness > 1 && transSensingOut(sight) == transparent) return true; /* * We can't determine for sure that the location is lit with a * simple test, so run a full what-if test, checking what * ambient light level our "probe" object would see if it were * moved to this location. Return true if the ambient light * level is higher than the "self-lit" value of 1, nil if not. */ return (lightProbe.whatIf( {: senseAmbientMax(actor.sightlikeSenses) }, &moveInto, self) > 1); } /* * Show our room description: this is the interior description of * the room, for use when the room is viewed by an actor within the * room. By default, we show our ordinary 'desc'. */ roomDesc { desc; } /* as part of a room description, mention an actor in this room */ roomActorHereDesc(actor) { gLibMessages.actorInRoom(actor, self); } /* * Provide a default description for an actor in this location, as * seen from a remote location (i.e., from a separate top-level room * that's linked to our top-level room by a sense connector of some * kind). By default, we'll describe the actor as being in this * nested room. */ roomActorThereDesc(actor) { local pov = getPOV(); local outer; /* get the outermost visible enclosing room */ outer = getOutermostVisibleRoom(pov); /* * If we found a room, and it's not us (i.e., we found something * outside this room that can be seen from the current point of * view), use the three-part description: actor in nested room * (self) in outer room. If we didn't find a room, or it's the * same as us, all we can say is that the actor is in the nested * room. */ if (outer not in (nil, self)) { /* use the three-part description: actor in self in outer */ gLibMessages.actorInRemoteNestedRoom(actor, self, outer, pov); } else { /* we're as far out as we can see: just say actor is in self */ gLibMessages.actorInRemoteRoom(actor, self, pov); } } /* show the status addendum for an actor in this location */ roomActorStatus(actor) { gLibMessages.actorInRoomStatus(actor, self); } /* describe the actor's posture while in this location */ roomActorPostureDesc(actor) { gLibMessages.actorInRoomPosture(actor, self); } /* acknowledge a posture change while in this location */ roomOkayPostureChange(actor) { defaultReport(&roomOkayPostureChangeMsg, actor.posture, self); } /* * describe the actor's posture as part of the EXAMINE description of * the nested room */ roomListActorPosture(actor) { gLibMessages.actorInRoom(actor, self); } /* * Prefix and suffix messages for listing a group of actors * nominally in this location. 'posture' is the posture of the * actors. 'remote' is the outermost visible room containing the * actors, but only if that room is remote from the point-of-view * actor; if everything is local, this will be nil. 'lst' is the * list of actors being listed. By default, we'll just show the * standard library messages. */ actorInGroupPrefix(pov, posture, remote, lst) { if (remote == nil) gLibMessages.actorInGroupPrefix(posture, self, lst); else gLibMessages.actorInRemoteGroupPrefix( pov, posture, self, remote, lst); } actorInGroupSuffix(pov, posture, remote, lst) { if (remote == nil) gLibMessages.actorInGroupSuffix(posture, self, lst); else gLibMessages.actorInRemoteGroupSuffix( pov, posture, self, remote, lst); } /* * Show a list of exits from this room as part of failed travel * ("you can't go that way"). */ cannotGoShowExits(actor) { /* if we have an exit lister, ask it to show exits */ if (gExitLister != nil) gExitLister.cannotGoShowExits(actor, self); } /* show the exit list in the status line */ showStatuslineExits() { /* if we have a global exit lister, ask it to show the exits */ if (gExitLister != nil) gExitLister.showStatuslineExits(); } /* * Get the estimated height, in lines of text, of the exits display's * contribution to the status line. This is used to calculate the * extra height we need in the status line, if any, to display the * exit list. If we're not configured to display exits in the status * line, this should return zero. */ getStatuslineExitsHeight() { if (gExitLister != nil) return gExitLister.getStatuslineExitsHeight(); else return 0; } /* * Make the actor stand up from this location. By default, we'll * simply change the actor's posture to "standing," and show a * default success report. * * Subclasses might need to override this. For example, a chair * will set the actor's location to the room containing the chair * when the actor stands up from the chair. */ makeStandingUp() { /* simply set the actor's new posture */ gActor.makePosture(standing); /* issue a default report of the change */ defaultReport(&okayPostureChangeMsg, standing); } /* * Default posture for an actor in the location. This is the * posture assumed by an actor when moving out of a nested room * within this location. */ defaultPosture = standing /* failure report we issue when we can't return to default posture */ mustDefaultPostureProp = &mustBeStandingMsg /* run the appropriate implied command to achieve our default posture */ tryMakingDefaultPosture() { return defaultPosture.tryMakingPosture(self); } /* * Check this object as a staging location. We're a valid location, * so we allow this. */ checkStagingLocation(dest) { /* we've valid, so we don't need to do anything */ } /* * Try moving the actor into this location. */ checkMovingActorInto(allowImplicit) { /* * If the actor isn't somewhere within us, we can't move the * actor here implicitly - we have no generic way of causing * implicit travel between top-level locations. Note that some * rooms might want to override this when travel between * adjacent locations makes sense as an implicit action; we * expect that such cases will be rare, so we don't attempt to * generalize this possibility. */ if (!gActor.isIn(self)) { reportFailure(&cannotDoFromHereMsg); exit; } /* * if the actor is already directly in me, simply check to make * sure the actor is in the default posture for the room - if * not, try running an appropriate implied command to change the * posture */ if (gActor.isDirectlyIn(self)) { /* if the actor's already in the default posture, we're okay */ if (gActor.posture == defaultPosture) return nil; /* run the implied command to stand up (or whatever) */ if (allowImplicit && tryMakingDefaultPosture()) { /* make sure we're in the proper posture now */ if (gActor.posture != defaultPosture) exit; /* note that we ran an implied command */ return true; } /* we couldn't get into the default posture - give up */ reportFailure(mustDefaultPostureProp); exit; } /* * The actor is within a nested room within me. Find our * immediate child containing the actor, and remove the actor * from the child. */ foreach (local cur in contents) { /* if this is the one containing the actor, remove the actor */ if (gActor.isIn(cur)) return cur.checkActorOutOfNested(allowImplicit); } /* we didn't find the nested room with the actor, so give up */ reportFailure(&cannotDoFromHereMsg); exit; } /* * Check, using pre-condition rules, that the actor is ready to * enter this room as a nested location. By default, we do nothing, * since we're not designed as a nested location. */ checkActorReadyToEnterNestedRoom(allowImplicit) { return nil; } /* * Check that the traveler is directly in the given room, using * pre-condition rules. 'nested' is the nested location immediately * within this room that contains the actor (directly or * indirectly). */ checkTravelerDirectlyInRoom(traveler, allowImplicit) { /* if the actor is already directly in this room, we're done */ if (traveler.isDirectlyIn(self)) return nil; /* try moving the actor here */ return traveler.checkMovingTravelerInto(self, allowImplicit); } /* * Check, using pre-condition rules, that the actor is removed from * this nested location and moved to its exit destination. By * default, we're not a nested location, so there's nothing for us * to do. */ checkActorOutOfNested(allowImplicit) { /* we're not a nested location, so there's nothing for us to do */ return nil; } /* * Determine if the current gActor, who is directly in this location, * is "travel ready." This means that the actor is ready, as far as * this location is concerned, to traverse the given connector. By * default, we consider an actor to be travel-ready if the actor is * standing; this takes care of most nested room situations, such as * chairs and beds, automatically. */ isActorTravelReady(conn) { return gActor.posture == standing; } /* * Run an implicit action, if possible, to make the current actor * "travel ready." This will be called if the actor is directly in * this location and isActorTravelReady() returns nil. By default, * we try to make the actor stand up. This should always be paired * with isActorTravelReady - the condition that routine tests should * be the condition this routine tries to bring into effect. If no * implicit action is possible, simply return nil. */ tryMakingTravelReady(conn) { return tryImplicitAction(Stand); } /* the message explaining what we must do to be travel-ready */ notTravelReadyMsg = &mustBeStandingMsg /* * An actor is attempting to disembark this location. By default, * we'll simply turn this into an "exit" command. */ disembarkRoom() { /* treat this as an 'exit' command */ replaceAction(Out); } /* * The destination for objects explicitly dropped by an actor within * this room. By default, we'll return self, because items dropped * should simply go in the room itself. Some types of rooms will * want to override this; for example, a room that represents the * middle of a tightrope would probably want to set the drop * destination to the location below the tightrope. Likewise, * objects like chairs will usually prefer to have dropped items go * into the enclosing room. */ getDropDestination(objToDrop, path) { /* by default, objects dropped in this room end up in this room */ return self; } /* * The nominal drop destination - this is the location where objects * are *reported* to go when dropped by an actor in this location. * By default, we simply return 'self'. * * The difference between the actual drop location and the nominal * drop location is that the nominal drop location is used only for * reporting messages, while the actual drop location is the * location where objects are moved on 'drop' or equivalent actions. * Rooms, for example, want to report that a dropped object lands on * the floor (or the ground, or whatever), even though the room * itself is the location where the object actually ends up. We * distinguish between the nominal and actual drop location to allow * these distinctions in reported messages. */ getNominalDropDestination() { return self; } /* * The "nominal actor container" - this is the container which we'll * say actors are in when we describe actors who are actually in * this location. By default, this simply returns self, but it's * sometimes useful to describe actors as being in some object other * than self. The most common case is that normal top-level rooms * usually want to describe actors as being "on the floor" or * similar. */ getNominalActorContainer(posture) { return self; } /* * Get any extra items in scope for an actor in this location. * These are items that are to be in scope even if they're not * reachable through any of the normal sense paths (so they'll be in * scope even in the dark, for example). * * By default, this returns nothing. Subclasses can override as * necessary to include additional items in scope. For example, a * chair would probably want to include itself in scope, since the * actor presumably knows he or she is sitting in a chair even if * it's too dark to see the chair. */ getExtraScopeItems(actor) { return []; } /* * Receive notification that we're about to perform a command within * this location. This is called on the outermost room first, then * on the nested rooms, from the outside in, until reaching the room * directly containing the actor performing the command. */ roomBeforeAction() { } /* * Receive notification that we've just finished a command within * this location. This is called on the room immediately containing * the actor performing the command, then on the room containing * that room, and so on to the outermost room. */ roomAfterAction() { } /* * Get my notification list - this is a list of objects on which we * must call beforeAction and afterAction when an action is * performed within this room. * * We'll also include any registered notification items for all of * our containing rooms up to the outermost container. * * The general notification mechanism always includes in the * notification list all of the objects connected by containment to * the actor, so objects that are in this room need not register for * explicit notification. */ getRoomNotifyList() { local lst; /* start with our explicitly registered list */ lst = roomNotifyList; /* add notification items for our immediate locations */ forEachContainer( {cont: lst = lst.appendUnique(cont.getRoomNotifyList())}); /* return the result */ return lst; } /* * Add an item to our registered notification list for actions in * the room. * * Items can be added here if they must be notified of actions * performed by within the room even when the items aren't in the * room at the time of the action. All items connected by * containment with the actor performing an action are automatically * notified of the action; only items that must receive notification * even when not connected by containment need to be registered * here. */ addRoomNotifyItem(obj) { roomNotifyList += obj; } /* remove an item from the registered notification list */ removeRoomNotifyItem(obj) { roomNotifyList -= obj; } /* our list of registered notification items */ roomNotifyList = [] /* * Get the room location. Since we're capable of holding actors, we * are our own room location. */ roomLocation = (self) /* * Receive notification that a traveler is arriving. This is a * convenience method that rooms can override to carry out side * effects of arrival. This is called just before the room's * arrival message (usually the location description) is displayed, * so the method can make any adjustments to the room's status or * contents needed for the arrival. By default, we do nothing. */ enteringRoom(traveler) { } /* * Receive notification that a traveler is leaving. This is a * convenience method that rooms can override to carry out side * effects of departure. This is called just after any departure * message is displayed. By default, we do nothing. */ leavingRoom(traveler) { } /* * Receive notification that a traveler is about to leave the room. * 'traveler' is the object actually traveling. In most cases this * is simply the actor; but when the actor is in a vehicle, this is * the vehicle instead. * * By default, we describe the traveler's departure if the traveler's * destination is different from its present location. */ travelerLeaving(traveler, dest, connector) { /* describe the departure */ if (dest != traveler.location) traveler.describeDeparture(dest, connector); /* run the departure notification */ leavingRoom(traveler); } /* * Receive notification that a traveler is arriving in the room. * 'traveler' is the object actually traveling. In most cases this * is simply the actor; but when the actor is in a vehicle, this is * the vehicle instead. * * By default, we set each of the "motive" actors to its default * posture, then describe the arrival. */ travelerArriving(traveler, origin, connector, backConnector) { /* * Set the self-motive actors into the proper default posture * for the location. We only do this for actors moving under * their own power, since actors in vehicles will presumably * just stay in the posture that's appropriate for the vehicle. */ foreach (local actor in traveler.getTravelerMotiveActors) { /* * If the actor isn't in this posture already, set the actor * to the new posture. Note that we set the new posture * directly, rather than via a nested command; we don't want * the travel to consist of a NORTH plus a SIT, but simply of * a NORTH. Note that this could bypass side effects * normally associated with the SIT (or whatever), but we * assume that when a room with a specific posture is linked * directly from a separate location, the travel connector * linking up to the new room will take care of the necessary * side effects. */ if (actor.posture != defaultPosture) actor.makePosture(defaultPosture); } /* run the arrival notification */ enteringRoom(traveler); /* describe the arrival */ traveler.describeArrival(origin, backConnector); } /* * Receive notification of travel among nested rooms. When an actor * moves between two locations related directly by containing (such * as from a chair to the room containing the chair, or vice versa), * we first call this routine on the origin of the travel, then we * move the actor, then we call this same routine on the destination * of the travel. * * This routine is used any time an actor is moved with * travelWithin(). This is not used when an actor travels between * locations related by a TravelConnector object rather than by * direct containment. * * We do nothing by default. Locations can override this if they * wish to perform any special handling during this type of travel. */ actorTravelingWithin(origin, dest) { } /* * Determine if the given actor has "intrinsic" knowledge of the * destination of the given travel connector leading away from this * location. This knowledge is independent of any memory the actor * has of actual travel through the connector in the course of the * game, which we track separately via the TravelConnector's travel * memory mechanism. * * There are two main reasons an actor would have intrinsic * knowledge of a connector's destination: * * 1. The actor is supposed to be familiar with the location and its * surroundings, within the context of the game. For example, if * part of the game is the player character's own house, the PC * would probably know where all of the connections among rooms go. * * 2. The destination location is plainly visible from this location * or is clearly marked (such as with a sign). For example, if the * current location is an open field, a nearby hilltop to the east * might be visible from here, so we could see from here where we'll * end up by going east. Alternatively, if we're in a lobby, and * the passage to the west is marked with a sign reading "electrical * room," an actor would have good reason to think an electrical * room lies to the west. * * We handle case (1) automatically through our actorIsFamiliar() * method: if the actor is familiar with the location, we assume by * default that the actor knows where all of the connectors from * here go. We don't have any default handling for case (2), so * individual rooms (or subclasses) must override this method if * they want to specify intrinsic knowledge for any of their * outgoing connectors. */ actorKnowsDestination(actor, conn) { /* * if the actor is familiar with this location, then the actor * by default knows where all of the outgoing connections go */ if (actorIsFamiliar(actor)) return true; /* there's no other way the actor would know the destination */ return nil; } /* * Is the actor familiar with this location? In other words, is the * actor supposed to know the location well at the start of the game? * * This should return true if the actor is familiar with this * location, nil if not. By default, we return nil, since actors * are not by default familiar with any locations. * * The purpose of this routine is to determine if the actor is meant * to know the location well, within the context of the game, even * before the game starts. For example, if an area in the game is * an actor's own house, the actor would naturally be familiar, * within the context of the game, with the locations making up the * house. * * Note that this routine doesn't need to "learn" based on the * events of the game. The familiarity here is meant only to model * the actor's knowledge as of the start of the game. */ actorIsFamiliar(actor) { return nil; } /* * The default "you can't go that way" message for travel within this * location in directions that don't allow travel. This is shown * whenever an actor tries to travel in one of the directions we have * set to point to noTravel. A room can override this to produce a * different, customized message for unset travel directions - this * is an easy way to change the cannot-travel message for several * directions at once. * * The handling depends on whether or not it's dark. If it's dark, * we don't want to reveal whether or not it's actually possible to * perform the travel, since there's no light to see where the exits * are. */ cannotTravel() { /* check for darkness */ if (!gActor.isLocationLit()) { /* the actor is in the dark - use our dark travel message */ cannotGoThatWayInDark(); } else { /* use the standard "can't go that way" routine */ cannotGoThatWay(); } } /* * Receive notification of travel from one dark location to another. * This is called before the actor is moved from the source * location, and can cancel the travel if desired by using 'exit' to * terminate the command. * * By default, we'll simply display the same handler we do when the * player attempts travel in a direction with no travel possible in * the dark (cannotGoThatWayInDark), and then use 'exit' to cancel * the command. This default behavior provides the player with no * mapping information in the dark, since the same message is * generated whether or not travel would be possible in a given * direction were light present. */ roomDarkTravel(actor) { /* * show the same message we would show if we attempted travel in * the dark in a direction with no exit */ cannotGoThatWayInDark(); /* terminate the command */ exit; } /* * Show the default "you can't go that way" message for this * location. By default, we show a generic message, but individual * rooms might want to override this to provide a more specific * description of why travel isn't allowed. */ cannotGoThatWay() { /* "you can't go that way" */ reportFailure(cannotGoThatWayMsg); /* show a list of exits, if appropriate */ cannotGoShowExits(gActor); } /* * The message to display when it's not possible to travel in a given * direction from this room; this is either a single-quoted string or * an actor action messages property (by default, it's the latter, * giving a default library message). */ cannotGoThatWayMsg = &cannotGoThatWayMsg /* * Show a version of the "you can't go that way" message for travel * while in the dark. This is called when the actor is in the dark * (i.e., there's no ambient light at the actor) and attempts to * travel in a direction that doesn't allow travel. By default, we * show a generic "you can't see where you're going in the dark" * message. * * This routine is essentially a replacement for the * cannotGoThatWay() routine that we use when the actor is in the * dark. */ cannotGoThatWayInDark() { /* "it's too dark; you can't see where you're going */ reportFailure(&cannotGoThatWayInDarkMsg); } /* * Get preconditions for travel for an actor in this location. These * preconditions should be applied by any command that will involve * travel from this location. By default, we impose no additional * requirements. */ roomTravelPreCond = [] /* * Get the effective location of an actor directly within me, for * the purposes of a "follow" command. To follow someone, we must * have the same effective follow location that the target had when * we last observed the target leaving. * * For most rooms, this is simply the room itself. */ effectiveFollowLocation = (self) /* * Dispatch the room daemon. This is a daemon routine invoked once * per turn; we in turn invoke roomDaemon on the current player * character's current location. */ dispatchRoomDaemon() { /* call roomDaemon on the player character's location */ if (gPlayerChar.location != nil) gPlayerChar.location.roomDaemon(); } ; /* ------------------------------------------------------------------------ */ /* * Room: the basic class for top-level game locations (that is, game * locations that aren't inside any other simulation objects, but are at * the top level of the containment hierarchy). This is the smallest * unit of movement; we do not distinguish among locations within a * room, even if a Room represents a physically large location. If it * is necessary to distinguish among different locations in a large * physical room, simply divide the physical room into sections and * represent each section with a separate Room object. * * A Room is not necessarily indoors; it is simply a location where an * actor can be located. This peculiar usage of "room" to denote any * atomic location, even outdoors, was adopted by the authors of the * earliest adventure games, and has been the convention ever since. * * A room's contents are the objects contained directly within the room. * These include fixed features of the room as well as loose items in * the room, which are effectively "on the floor" in the room. * * The Room class implements the Travel Connector interface in such a * way that travel from one room to another can be established simply by * setting a direction property (north, south, etc) in the origin room * to point to the destination room. This type of travel link has no * side effects and is unconditional. * * A room is by default an indoor location; this means that it contains * walls, floor, and ceiling. An outdoor location should be based on * OutdoorRoom rather than Room. */ class Room: Fixture, BasicLocation, RoomAutoConnector /* * Initialize */ initializeThing() { /* inherit default handling */ inherited(); /* * Add my room parts to my contents list. Only include room * parts that don't have explicit locations. */ contents += roomParts.subset({x: x.location == nil}); } /* * we're a "top-level" location: we don't have any other object * containing us, but we're nonetheless part of the game world, so * we're at the top level of the containment tree */ isTopLevel = true /* * we generally do not want rooms to be included when a command * refers to 'all' */ hideFromAll(action) { return true; } /* don't consider myself a default for STAND ON, SIT ON, or LIE ON */ hideFromDefault(action) { /* don't hide from STAND ON, SIT ON, LIE ON */ if (action.ofKind(StandOnAction) || action.ofKind(SitOnAction) || action.ofKind(LieOnAction)) return nil; /* don't hide from defaults for other actions, though */ return inherited(action); } /* * Most rooms provide their own implicit lighting. We'll use * 'medium' lighting (level 3) by default, which provides enough * light for all activities, but is reduced to dim light (level 2) * when it goes through obscuring media or over distance. */ brightness = 3 /* * Get my "destination name," as seen by the given actor from the * given origin location. This gives the name we can use to * describe this location from the perspective of an actor in an * adjoining location looking at a travel connector from that * location to here. * * By default, we simply return our destName property. This default * behavior can be overridden if it's necessary for a location to * have different destination names in different adjoining * locations, or when seen by different actors. * * If this location's name cannot or should not be described from an * adjoining location, this should simply return nil. */ getDestName(actor, origin) { return destName; } /* * Our destination name, if we have one. By default, we make this * nil, which means the room cannot be described as a destination of * connectors from adjoining locations. */ destName = nil /* * My "atmosphere" list. This can be set to an EventList object to * provide atmosphere messages while the player character is within * this room. The default roomDaemon will show one message from this * EventList (by calling the EventList's doScript() method) on each * turn the player character is in this location. */ atmosphereList = nil /* * Room daemon - this is invoked on the player character's immediate * location once per turn in a daemon. */ roomDaemon() { /* * if we have an atmosphere message list, display the next * message */ if (atmosphereList != nil) { /* show visual separation, then the current atmosphere message */ "<.commandsep>"; atmosphereList.doScript(); } } /* * The nominal drop destination - this is the location where we * describe objects as being when they're actually directly within * the room. * * By default, we return the object representing the room's floor. * If there's no floor, we simply return 'self'. */ getNominalDropDestination() { local floor; /* * if there's a floor, it's the nominal drop destination; * otherwise, just indicate that our contents are in 'self' */ return ((floor = roomFloor) != nil ? floor : self); } /* * Since we could be our own nominal drop destination, we need a * message to describe things being put here. */ putDestMessage = &putDestRoom /* * The nominal actor container. By default, this is the room's * nominal drop destination, which is usually the floor or * equivalent. */ getNominalActorContainer(posture) { return getNominalDropDestination(); } /* * move something into a room is accomplished by putting the object * on the floor */ tryMovingObjInto(obj) { local floor; /* if we have a floor, put the object there */ if ((floor = roomFloor) != nil) return tryImplicitAction(PutOn, obj, floor); else return nil; } /* explain that something must be in the room first */ mustMoveObjInto(obj) { local floor; /* if we have a floor, say that the object has to go there */ if ((floor = roomFloor) != nil) reportFailure(&mustBeInMsg, obj, floor); else inherited(obj); } /* * Get the apparent location of one of our room parts. * * In most cases, we use generic objects (defaultFloor, * defaultNorthWall, etc.) for the room parts. There's only one * instance of each of these generic objects in the whole game - * there's only one floor, one north wall, and so on - so these * instances can't have specific locations the way normal objects do. * Thus the need for this method: this tells us the *apparent* * location of one of the room part objects as perceived from this * room. * * If the part isn't in this location, we'll return nil. */ getRoomPartLocation(part) { local loc; /* * if this part is in our part list, then its apparent location * is 'self' */ if (roomParts.indexOf(part) != nil) return self; /* * if the room part has an explicit location itself, and that * location is either 'self' or is in 'self', return the * location */ if (part != nil && (loc = part.location) != nil && (loc == self || loc.isIn(self))) return loc; /* we don't have the part */ return nil; } /* * Get the list of extra parts for the room. An indoor location has * walls, floor, and ceiling; these are all generic objects that are * included for completeness only. * * A room with special walls, floor, or ceiling should override this * to eliminate the default objects from appearing in the room. * Note that if the room has a floor, it should always be * represented by an object of class Floor, and should always be * part of this list. */ roomParts = [defaultFloor, defaultCeiling, defaultNorthWall, defaultSouthWall, defaultEastWall, defaultWestWall] /* * Get the room's floor. This looks for an object of class Floor in * the roomParts list; if there is no such object, we'll return nil, * indicating that the room has no floor at all. */ roomFloor = (roomParts.valWhich({x: x.ofKind(Floor)})) /* * Get any extra items in scope in this location. These are items * that are to be in scope even if they're not reachable through any * of the normal sense paths (so they'll be in scope even in the * dark, for example). * * By default, if we have a floor, and the actor is directly in this * room, we return our floor: this is because an actor is presumed to * be in physical contact with the floor whenever directly in the * room, and thus the actor would be aware that there's a floor * there, even if the actor can't see the floor. In some rooms, it's * desirable to have certain objects in scope because they're * essential features of the room; for example, a location that is * part of a stairway would probably have the stairs in scope by * virtue of the actor standing on them. */ getExtraScopeItems(actor) { local floor; /* * if we have a floor, and the actor is in this room, explicitly * make the floor in scope */ if ((floor = roomFloor) != nil && actor.isDirectlyIn(self)) return [floor]; else return []; } /* * When we're in the room, treat EXAMINE <ROOM> the same as LOOK * AROUND. (This will only work if the room is given vocabulary * like a normal object.) */ dobjFor(Examine) { verify() { /* * When we're in the room, downgrade the likelihood a bit, as * we'd rather inspect an object within the room if there's * something with the same name. When we're *not* in the * room - meaning this is a remote room that's visible from * the player's location - downgrade the likelihood even * more, since we're more likely to want to examine the room * we're actually in than a remote room with the same name. */ if (gActor.isIn(self)) logicalRank(80, 'x room'); else logicalRank(70, 'x room'); } action() { /* * if the looker is within the room, replace EXAMINE <self> * with LOOK AROUND; otherwise, use the normal description */ if (gActor.isIn(self) && gActor.canSee(self)) gActor.lookAround(LookRoomDesc | LookListSpecials | LookListPortables); else inherited(); } } /* treat LOOK IN <room> as EXAMINE <room> */ dobjFor(LookIn) remapTo(Examine, self) /* LOOK UNDER and BEHIND are illogical */ dobjFor(LookUnder) { verify { illogical(&cannotLookUnderMsg); } } dobjFor(LookBehind) { verify { illogical(&cannotLookBehindMsg); } } /* treat SMELL/LISTEN TO <room> as just SMELL/LISTEN */ dobjFor(Smell) remapTo(SmellImplicit) dobjFor(ListenTo) remapTo(ListenImplicit) /* map STAND/SIT/LIE ON <room> to my default floor */ dobjFor(StandOn) maybeRemapTo(roomFloor != nil, StandOn, roomFloor) dobjFor(SitOn) maybeRemapTo(roomFloor != nil, SitOn, roomFloor) dobjFor(LieOn) maybeRemapTo(roomFloor != nil, LieOn, roomFloor) /* * treat an explicit GET OUT OF <ROOM> as OUT if there's an apparent * destination for OUT; otherwise treat it as "vague travel," which * simply tells the player that they need to specify a direction */ dobjFor(GetOutOf) { remap() { /* remap only if this isn't an implied action */ if (gAction.parentAction == nil) { /* * if we have an apparent Out connection, go there; * otherwise it's not obvious where we're meant to go */ if (out != nil && out.isConnectorApparent(self, gActor)) return [OutAction]; else return [VagueTravelAction]; } /* don't remap here - use the standard handling */ return inherited(); } } /* * for BOARD and ENTER, there are three possibilities: * * - we're already directly in this room, in which case it's * illogical to travel here again * * - we're in a nested room within this room, in which case ENTER * <self> is the same as GET OUT OF <outermost nested room within * self> * * - we're in a separate top-level room that's connected by a sense * connector, in which case ENTER <self> should be handled as TRAVEL * VIA <connector from actor's current location to self> */ dobjFor(Board) asDobjFor(Enter) dobjFor(Enter) { verify() { /* * if we're already here, entering the same location is * redundant; if we're not in a nested room, we need a travel * connector to get there from here */ if (gActor.isDirectlyIn(self)) illogicalAlready(&alreadyInLocMsg); else if (!gActor.isIn(self) && gActor.location.getConnectorTo(gActor, self) == nil) illogicalNow(&whereToGoMsg); } preCond() { /* * if we're in a different top-level room, and there's a * travel connector, we'll simply travel via the connector, * so we don't need to impose any pre-conditions of our own */ if (!gActor.isIn(self) && gActor.location.getConnectorTo(gActor, self) != nil) return []; /* * if we're in a nested room within this object, we'll * replace the action with "get out of <outermost nested * room>", so there's no need for any extra preconditions * here */ if (gActor.isIn(self) && !gActor.isDirectlyIn(self)) return []; /* otherwise, use the default conditions */ return inherited(); } action() { /* * if we're in a nested room, get out; otherwise travel via a * suitable travel connector */ if (gActor.isIn(self) && !gActor.isDirectlyIn(self)) { /* * get out of the *outermost* nested room - that is, our * direct child that contains the actor */ local chi = contents.valWhich({x: gActor.isIn(x)}); /* if we found it, get out of it */ if (chi != nil) replaceAction(GetOutOf, chi); } else { /* get the connector from here to there */ local conn = gActor.location.getConnectorTo(gActor, self); /* if we found it, go that way */ if (conn != nil) replaceAction(TravelVia, conn); } /* * if we didn't replace the action yet, we can't figure out * how to get here from the actor's current location */ reportFailure(&whereToGoMsg); } } ; /* * A dark room, which provides no light of its own */ class DarkRoom: Room /* * turn off the lights */ brightness = 0 ; /* * An outdoor location. This differs from an indoor location in that it * has ground and sky rather than floor and ceiling, and has no walls. */ class OutdoorRoom: Room /* an outdoor room has ground and sky, but no walls */ roomParts = [defaultGround, defaultSky] ; /* * A shipboard room. This is a simple mix-in class: it can be used * along with any type of Room to indicate that this room is aboard a * ship. When a room is aboard a ship, the shipboard travel directions * (port, starboard, fore, aft) are allowed; these directions normally * make no sense. * * This is a mix-in class rather than a Room subclass to allow it to be * used in conjunction with any other Room subclass. To make a room * shipboard, simply declare your room like this: * * mainDeck: Shipboard, Room // etc */ class Shipboard: object /* mark the location as being aboard ship */ isShipboard = true ; /* * For convenience, we define ShipboardRoom as a shipboard version of the * basic Room type. */ class ShipboardRoom: Shipboard, Room ; /* ------------------------------------------------------------------------ */ /* * Make a room "floorless." This is a mix-in class that you can include * in a superclass list ahead of Room or any of its subclasses to create * a room where support is provided by some means other than standing on * a surface, or where there's simply no support. Examples: hanging on a * rope over a chasm; climbing a ladder; in free-fall after jumping out * of a plane; levitating in mid-air. * * There are two main special features of a floorless room. First, and * most obviously, there's no "floor" or "ground" object among the room * parts. We accomplish this by simply subtracting out any object of * class Floor from the room parts list inherited from the combined base * room class. * * Second, there's no place to put anything down, so objects dropped here * either disappear from the game or are transported to another location * (the room at the bottom of the chasm, for example). */ class Floorless: object /* * Omit the default floor/ground objects from the room parts list. * Room classes generally have static room parts lists, so calculate * this once per instance and store the results. * * NOTE - if you combine Floorless with a base Room class that has a * dynamic room parts list, you'll need to override this to calculate * the subset dynamically on each invocation. */ roomParts = perInstance(inherited().subset({x: !x.ofKind(Floor)})) /* * The room below, if any - this is where objects dropped here will * actually end up. By default, this is nil, which means that * objects dropped here simply disappear from the game. If there's a * "bottom of chasm" location where dropped objects should land, * provide it here. */ bottomRoom = nil /* receive a dropped object */ receiveDrop(obj, desc) { /* * move the dropped object to the room at the bottom of whatever * it is we're suspended over; if there is no bottom room, we'll * simply remove the dropped object from the game */ obj.moveInto(bottomRoom); /* * Say that the object drops out of sight below. Build this by * combining the generic report prefix from the drop descriptor * with our generic suffix, which describes the object as * vanishing below. */ mainReport(desc.getReportPrefix(obj, self) + gActor.getActionMessageObj().floorlessDropMsg(obj)); } ; /* * For convenience, provide a combination of Floorless with the ordinary * Room. */ class FloorlessRoom: Floorless, Room; /* ------------------------------------------------------------------------ */ /* * Room Part - base class for "parts" of rooms, such as floors and walls. * Room parts are unusual in a couple of ways. * * First, room parts are frequently re-used widely throughout a game. We * define a single instance of each of several parts that are found in * typical rooms, and then re-use those instances in all rooms with those * parts. For example, we define one "default floor" object, and then * use that object in most or all rooms that have floors. We do this for * efficiency, to avoid creating hundreds of essentially identical copies * of the common parts. * * Second, because room parts are designed to be re-used, things that are * in or on the parts are actually represented in the containment model * as being directly in their containing rooms. For example, an object * that is said to be "on the floor" actually has its 'location' property * set to its immediate room container, and the 'contents' list that * contains the object is that of the room, not of the floor object. We * must therefore override some of the normal handling for object * locations within room parts, in order to make it appear (for the * purposes of command input and descriptive messages) that the things * are in/on their room parts, even though they're not really represented * that way in the containment model. */ class RoomPart: Fixture /* * When we explicitly examine a RoomPart, list any object that's * nominally contained in the room part, as long it doesn't have a * special description for the purposes of the room part. (If it * does have a special description, then examining the room part will * automatically display that special desc, so we don't want to * include the object in a separate list of miscellaneous contents of * the room part.) */ isObjListedInRoomPart(obj) { /* * list the object *unless* it has a special description for the * purposes of examining this room part */ return !obj.useSpecialDescInRoomPart(self); } /* * Add this room part to the given room. * * Room parts don't have normal "location" properties. Instead, a * room part explicitly appears in the "roomParts" list of each room * that contains it. For the most part, room parts are static - * they're initialized in the room definitions and never changed. * However, if you need to dynamically add a room part to a room * during the game, you can do so using this method. */ moveIntoAdd(room) { /* add me to the room's 'roomParts' and 'contents' lists */ room.roomParts += self; room.contents += self; } /* * Remove this room part from the given room. This can be used if * it's necessary to remove the room part dynamically from a room. */ moveOutOf(room) { /* remove me from the room's 'roomParts' and 'contents' lists */ room.roomParts -= self; room.contents -= self; } /* * Don't include room parts in 'all'. Room parts are so ubiquitous * that we never want to assume that they're involved in a command * except when it is specifically so stated. */ hideFromAll(action) { return true; } /* do allow use as a default, though */ hideFromDefault(action) { return nil; } /* * When multiple room parts show up in a resolve list, and some of * the parts are local to the actor's immediate location and others * aren't, keep only the local ones. This helps avoid pointless * ambiguity in cases where two (or more) top-level locations are * linked with a sense connector, and one or the other location has * custom room part objects. */ filterResolveList(lst, action, whichObj, np, requiredNum) { /* if a definite number of objects is required, check ambiguity */ if (requiredNum != nil) { /* get the subset that's just RoomParts */ local partLst = lst.subset({x: x.obj_.ofKind(RoomPart)}); /* * get the *remote* subset - this is the subset that's not in * the outermost room of the target actor */ local outer = action.actor_.getOutermostRoom(); local remoteLst = partLst.subset({x: !x.obj_.isIn(outer)}); /* * If all of the objects are remote, or all of them are * local, we can't narrow things down on this basis; but if * we found some remote and some local, eliminate the remote * items, since we want to favor the local ones */ if (remoteLst.length() not in (0, partLst.length())) lst -= remoteLst; } /* now do any inherited work, and return the result */ return inherited(lst, action, whichObj, np, requiredNum); } /* * Since room parts are generally things like walls and floors that * enclose the entire room, they're typically visually large, and * tend to have fairly large-scale details (such as doors and * windows). So, by default we set the sightSize to 'large' so that * the details are visible at a distance. */ sightSize = large /* * as with decorations, downgrade the likelihood for Examine, as the * standard walls, floors, etc. are pretty much background noise * that are just here in case someone wants to refer to them * explicitly */ dobjFor(Examine) { verify() { inherited(); logicalRank(70, 'x decoration'); } } /* describe the status - shows the things that are in/on the part */ examineStatus() { /* show the contents of the room part */ examinePartContents(&descContentsLister); } /* show our contents */ examinePartContents(listerProp) { /* * Get my location, as perceived by the actor - this is the room * that contains this part. If I don't have a location as * perceived by the actor, then we can't show any contents. */ local loc = gActor.location.getRoomPartLocation(self); if (loc == nil) return; /* * create a copy of the lister customized for this part, if we * haven't already done so */ if (self.(listerProp).part_ == nil) { self.(listerProp) = self.(listerProp).createClone(); self.(listerProp).part_ = self; } /* show the contents of the containing location */ self.(listerProp).showList(gActor, self, loc.contents, 0, 0, gActor.visibleInfoTable(), nil); } /* * show our special contents - this shows objects with special * descriptions that are specifically in this room part */ examineSpecialContents() { local infoTab; local lst; /* get the actor's list of visible items */ infoTab = gActor.visibleInfoTable(); /* * get the list of special description items, using only the * subset that uses special descriptions in this room part */ lst = specialDescList(infoTab, {obj: obj.useSpecialDescInRoomPart(self)}); /* show the list */ specialContentsLister.showList(gActor, nil, lst, 0, 0, infoTab, nil); } /* * Get the destination for a thrown object that hits me. Since we * don't have a real location, we must ask the actor for our room * part location, and then use its hit-and-fall destination. */ getHitFallDestination(thrownObj, path) { local loc; local dest; /* * if we have an explicit location, start with it; otherwise, ask * the actor's location to find us; if we can't even find * ourselves there, just use the actor's current location */ if ((loc = location) == nil && (loc = gActor.location.getRoomPartLocation(self)) == nil) loc = gActor.location; /* use the location's drop destination for thrown objects */ dest = loc.getDropDestination(thrownObj, path); /* give the destination a chance to make adjustments */ return dest.adjustThrowDestination(thrownObj, path); } /* consider me to be in any room of which I'm a part */ isIn(loc) { local rpl; /* * get the room-part location of this room part, from the * perspective of the prospective location we're asking about */ if (loc != nil && (rpl = loc.getRoomPartLocation(self)) != nil) { /* * We indeed have a room part location in the given * location, so we're a part of the overall location. We * might be directly in the location (i.e., 'rpl' could * equal 'loc'), or we might be somewhere relative to 'loc' * (for example, we could be within a nested room within * 'loc', in which case 'rpl' is a nested room unequal to * 'loc' but is within 'loc'). So, if 'rpl' equals 'loc' or * is contained in 'loc', I'm within 'loc', because I'm * within 'rpl'. */ if (rpl == loc || rpl.isIn(loc)) return true; } /* * we don't appear to be in 'loc' on the basis of our special * room-part location; fall back on the inherited handling */ return inherited(loc); } /* our contents listers */ contentsLister = roomPartContentsLister descContentsLister = roomPartDescContentsLister lookInLister = roomPartLookInLister specialContentsLister = specialDescLister /* look in/on: show our contents */ dobjFor(LookIn) { verify() { } action() { /* show my contents */ examinePartContents(&lookInLister); /* show my special contents */ examineSpecialContents(); } } /* we can't look behind/through/under a room part by default */ nothingUnderMsg = &cannotLookUnderMsg nothingBehindMsg = &cannotLookBehindMsg nothingThroughMsg = &cannotLookThroughMsg /* * initialization - add myself to my location's roomPart list if I * have an explicit location */ initializeThing() { /* do the normal work first */ inherited(); /* * if I have an explicit location, and I'm not in my location's * roomPart list, add myself to the list */ if (location != nil && location.roomParts.indexOf(self) == nil) location.roomParts += self; } ; /* * A floor for a nested room. This should be placed directly within a * nested room object if the nested room is to be described as having a * floor separate from the nested room itself. We simply remap any * commands relating to using the floor as a surface (Put On, Throw At, * Sit On, Lie On, Stand On) to the enclosing nested room. */ class NestedRoomFloor: Fixture iobjFor(PutOn) remapTo(PutOn, DirectObject, location) iobjFor(ThrowAt) remapTo(ThrowAt, DirectObject, location) dobjFor(SitOn) remapTo(SitOn, location) dobjFor(LieOn) remapTo(LieOn, location) dobjFor(StandOn) remapTo(StandOn, location) ; /* * Base class for the default floor and the default ground of a top-level * room. The floor and ground are where things usually go when dropped, * and they're the locations where actors within a room are normally * standing. */ class Floor: RoomPart /* specifically allow me as a default for STAND ON, SIT ON, and LIE ON */ hideFromDefault(action) { /* don't hide from STAND ON, SIT ON, LIE ON */ if (action.ofKind(StandOnAction) || action.ofKind(SitOnAction) || action.ofKind(LieOnAction)) return nil; /* for other actions, use the standard handling */ return inherited(action); } /* * When explicitly examining a Floor object, list any objects that * are listed in the normal room description (as in LOOK AROUND). By * default, the floor is the nominal container for anything directly * in the room, so we'll normally want LOOK AROUND and LOOK AT FLOOR * to produce the same list of objects. */ isObjListedInRoomPart(obj) { /* list the object if it's listed in a normal LOOK AROUND */ return obj.isListed; } /* * 'put x on floor' equals 'drop x'. Add a precondition that the * drop destination is the main room, since otherwise we could have * strange results if we dropped something inside a nested room. */ iobjFor(PutOn) { preCond() { /* * require that this floor object itself is reachable, and * that the drop destination for the direct object is an * outermost room */ return [touchObj, new ObjectPreCondition( gDobj, dropDestinationIsOuterRoom)]; } verify() { } action() { replaceAction(Drop, gDobj); } } /* * The message we use to describe this object prepositionally, as the * destination of a throw or drop. This should be a gLibMessages * property with the appropriate prepositional phrase. We use a * custom message specific to floor-like objects. */ putDestMessage = &putDestFloor /* 'throw x at floor' */ iobjFor(ThrowAt) { check() { /* * If I'm reachable, suggest just putting it down instead. * We only make the suggestion, rather than automatically * treating the command as DROP, because a player explicitly * typing THROW <obj> AT FLOOR is probably attempting to * express something more violent than merely putting the * object down; the player probably is thinking in terms of * breaking the object (or the floor). */ if (canBeTouchedBy(gActor)) { mainReport(&shouldNotThrowAtFloorMsg); exit; } } } /* is the given actor already on the floor? */ isActorOnFloor(actor) { /* * the actor is on the floor if the actor is directly in the * floor's room-part-location for the actor's location */ return actor.isDirectlyIn(actor.location.getRoomPartLocation(self)); } /* verify sitting/standing/lying on the floor */ verifyEntry(newPosture, alreadyMsg) { /* * If we're already in my location, and we're in the desired * posture, this command is illogical because we're already * where they want us to end up. Otherwise, it's logical to * stand/sit/lie on the floor, but rank it low since we don't * want the floor to interfere with selecting a default if * there's anything around that's actually like a chair. * * If it's logical, note that we've verified okay for the * action. On a future pass, we might have enforced a * precondition that moved us here, at which point our work will * be done - but we don't want to complain in that case, since * it was logical from the player's perspective to carry out the * command even though we have nothing left to do. */ if (gActor.posture == newPosture && isActorOnFloor(gActor) && gAction.verifiedOkay.indexOf(self) == nil) { /* we're already on the floor in the desired posture */ illogicalNow(alreadyMsg); } else { /* * it's logical, but rank it low in case there's something * more special we can stand/sit/lie on */ logicalRank(50, 'on floor'); /* * note that we've verified okay, so we don't complain on a * future pass if we discover that a precondition has * brought us into compliance with the request prematurely */ gAction.verifiedOkay += self; } } /* perform sitting/standing/lying on the floor */ performEntry(newPosture) { /* * bring the new posture into effect; there's no need for * actually moving the actor, since the preconditions will have * moved us to our main enclosing room already */ gActor.makePosture(newPosture); /* report success */ defaultReport(&roomOkayPostureChangeMsg, newPosture, self); } /* 'stand on floor' causes actor to stand in the containing room */ dobjFor(StandOn) { preCond = [touchObj, new ObjectPreCondition(gActor.location.getOutermostRoom(), actorDirectlyInRoom)] verify() { verifyEntry(standing, &alreadyStandingOnMsg); } action() { performEntry(standing); } } /* 'sit on floor' causes the actor to sit in the containing room */ dobjFor(SitOn) { preCond = [touchObj, new ObjectPreCondition(gActor.location.getOutermostRoom(), actorDirectlyInRoom)] verify() { verifyEntry(sitting, &alreadySittingOnMsg); } action() { performEntry(sitting); } } /* 'lie on floor' causes the actor to lie down in the room */ dobjFor(LieOn) { preCond = [touchObj, new ObjectPreCondition(gActor.location.getOutermostRoom(), actorDirectlyInRoom)] verify() { verifyEntry(lying, &alreadyLyingOnMsg); } action() { performEntry(lying); } } /* * Mention that an actor is here, as part of a room description. * When the actor is standing, just say that the actor is here, since * it's overstating the obvious to say that the actor is standing on * the floor. For other postures, do mention the floor. */ roomActorHereDesc(actor) { /* * if we're standing, just say that the actor is "here"; * otherwise, say that the actor is sitting/lying/etc on self */ if (actor.posture == standing) gLibMessages.roomActorHereDesc(actor); else gLibMessages.actorInRoom(actor, self); } /* * Mention that an actor is here, as part of a room description. * Since a floor is a trivial part of its enclosing room, there's no * point in mentioning that we're on the floor, as that's stating the * obvious; instead, simply describe the actor as being in the * actor's actual enclosing room. */ roomActorThereDesc(actor) { actor.location.roomActorThereDesc(actor); } /* * Show our room name status for an actor on the floor. Since * standing on the floor is the trivial default for any room, we * won't bother mentioning it. Other postures we'll mention the same * way we would for any nested room. */ roomActorStatus(actor) { if (actor.posture != standing) gLibMessages.actorInRoomStatus(actor, self); } /* * Show the actor's posture here. When we're standing on the floor, * don't mention the posture, as this is too trivial a condition to * state. Otherwise, mention it as normal for a nested room. */ roomActorPostureDesc(actor) { if (actor.posture != standing) gLibMessages.actorInRoomPosture(actor, self); } /* * Generate an acknowledgment for a posture change here. If the * actor is standing, just say "okay, you're now standing" without * mentioning the floor, since standing on the floor is the trivial * default. For other postures, say that we're sitting/lying/etc on * the floor. */ roomOkayPostureChange(actor) { if (actor.posture == standing) defaultReport(&okayPostureChangeMsg, standing); else defaultReport(&roomOkayPostureChangeMsg, actor.posture, self); } /* * mention the actor as part of the EXAMINE description of a nested * room containing the actor */ roomListActorPosture(actor) { /* * Since standing is the default posture for an actor, and since * the floor (or equivalent) is the default place to be standing, * don't bother mentioning actors standing on a floor. * Otherwise, mention that the actor is sitting/lying/etc here. */ if (actor.posture != standing) gLibMessages.actorInRoom(actor, self); } /* * Prefix and suffix messages for listing a group of actors * nominally on the this floor. Actors are said to be on the floor * when they're really in the location containing the floor. * * If we're talking about a remote location, simply describe it as * the location rather than mentioning the floor, since the floor is * a trivial part of the remote location not worth mentioning. * * If we're local, and we're standing, we'll simply say that we're * "standing here"; again, saying that we're standing on the floor * is stating the obvious. If we're not standing, we will mention * that we're on the floor. */ actorInGroupPrefix(pov, posture, remote, lst) { if (remote != nil) gLibMessages.actorThereGroupPrefix(pov, posture, remote, lst); else if (posture == standing) gLibMessages.actorHereGroupPrefix(posture, lst); else gLibMessages.actorInGroupPrefix(posture, self, lst); } actorInGroupSuffix(pov, posture, remote, lst) { if (remote != nil) gLibMessages.actorThereGroupSuffix(pov, posture, remote, lst); else if (posture == standing) gLibMessages.actorHereGroupSuffix(posture, lst); else gLibMessages.actorInGroupSuffix(posture, self, lst); } ; /* ------------------------------------------------------------------------ */ /* * Define the default room parts. */ /* * the default floor, for indoor locations */ defaultFloor: Floor ; /* * the default ceiling, for indoor locations */ defaultCeiling: RoomPart ; /* * The default walls, for indoor locations. We provide a north, south, * east, and west wall in each indoor location by default. */ class DefaultWall: RoomPart ; defaultNorthWall: DefaultWall ; defaultSouthWall: DefaultWall ; defaultEastWall: DefaultWall ; defaultWestWall: DefaultWall ; /* * the default sky, for outdoor locations */ defaultSky: Distant, RoomPart ; /* * The default ground, for outdoor locations. */ defaultGround: Floor ; /* ------------------------------------------------------------------------ */ /* * A "room part item" is an object that's specially described as being * part of, or attached to, a RoomPart (a wall, ceiling, floor, or the * like). This is a mix-in class that can be combined with any ordinary * object class (but usually with something non-portable, such as a * Fixture or Immovable). The effect of adding RoomPartItem to an * object's superclasses is that a command like EXAMINE EAST WALL (or * whichever room part the object is associated with) will display the * object's specialDesc, but a simple LOOK will not. This class is * sometimes useful for things like doors, windows, ceiling fans, and * other things attached to the room. * * Note that this is a mix-in class, so you should always combine it with * a regular Thing-based class. * * When using this class, you should define two properties in the object: * specialNominalRoomPartLocation, which you should set to the RoomPart * (such as a wall) where the object should be described; and * specialDesc, which is the description to show when the room part is * examined. Alternatively (or in addition), you can define * initNominalRoomPartLocation and initSpecialDesc - these work the same * way, but will only be in effect until the object is moved. */ class RoomPartItem: object /* * show our special description when examining our associated room * part, as long as we actually define a special description */ useSpecialDescInRoomPart(part) { /* only show the special description in our associated room part */ if (!isNominallyInRoomPart(part)) return nil; /* * if we define an initial special description, and this is our * nominal room part for that description, use it */ if (isInInitState && propType(&initSpecialDesc) != TypeNil && initNominalRoomPartLocation == part) return true; /* likewise for our specialDesc */ if (propType(&specialDesc) != TypeNil && specialNominalRoomPartLocation == part) return true; /* otherwise, don't use the special description */ return nil; } /* * don't use the special description in room descriptions, or in * examining any other container */ useSpecialDescInRoom(room) { return nil; } useSpecialDescInContents(cont) { return nil; } ; /* ------------------------------------------------------------------------ */ /* * A Nested Room is any object that isn't a room but which can contain * an actor: chairs, beds, platforms, vehicles, and the like. * * An important property of nested rooms is that they're not * full-fledged rooms for the purposes of actor arrivals and departures. * Specifically, an actor moving from a room to a nested room within the * room does not trigger an actor.travelTo invocation, but simply moves * the actor from the containing room to the nested room. Moving from * the nested room to the containing room likewise triggers no * actor.travelTo invocation. The travelTo method is not applicable for * intra-room travel because no TravelConnector objects are traversed in * such travel; we simply move in and out of contained objects. To * mitigate this loss of notification, we instead call * actor.travelWithin() when moving among nested locations. * * By default, an actor attempting to travel from a nested location via * a directional command will simply attempt the travel as though the * actor were in the enclosing location. */ class NestedRoom: BasicLocation /* * Our interior room name. This is the status line name we display * when an actor is within this object and can't see out to the * enclosing room. Since we can't rely on the enclosing room's * status line name if we can't see the enclosing room, we must * provide one of our own. * * By default, we'll use our regular name. */ roomName = (name) /* * Show our interior room description. We use this to generate the * long "look" description for the room when an actor is within the * room and cannot see the enclosing room. * * Note that this is used ONLY when the actor cannot see the * enclosing room - when the enclosing room is visible (because the * nested room is something like a chair that doesn't enclose the * actor, or can enclose the actor but is open or transparent), then * we'll simply use the description of the enclosing room instead, * adding a note to the short name shown at the start of the room * description indicating that the actor is in the nested room. * * By default, we'll show the appropriate "actor here" description * for the posture, so we'll say something like "You are sitting on * the red chair" or "You are in the phone booth." Instances can * override this to customize the description with something more * detailed, if desired. */ roomDesc { local pov = getPOVActorDefault(gActor); pov.listActorPosture(pov); } /* * The maximum bulk the room can hold. We'll define this to a large * number by default so that bulk isn't a concern. * * Lower numbers here can be used, for example, to limit the seating * capacity of a chair. */ bulkCapacity = 10000 /* * Check for ownership. For a nested room, an actor can be taken to * own the nested room by virtue of being inside the room - the * chair Bob is sitting in can be called "bob's chair". * * If we don't have an explicit owner, and the potential owner 'obj' * is in me and can own me, we'll report that 'obj' does in fact own * me. Otherwise, we'll defer to the inherited implementation. */ isOwnedBy(obj) { /* * if we're not explicitly owned, and 'obj' can own me, and * 'obj' is inside me, consider us owned by 'obj' */ if (owner == nil && obj.isIn(self) && obj.canOwn(self)) return true; /* defer to the inherited definition of ownership */ return inherited(obj); } /* * Get the extra scope items within this room. Normally, the * immediately enclosing nested room is in scope for an actor in the * room. So, if the actor is directly in 'self', return 'self'. */ getExtraScopeItems(actor) { /* if the actor is directly in the nested room, return it */ if (actor.isDirectlyIn(self)) return [self]; else return []; } /* * By default, 'out' within a nested room should take us out of the * nested room itself. The easy way to accomplish this is to set up * a 'nestedRoomOut' connector for the nested room, which will * automatically try a GET OUT command. If we didn't do this, we'd * *usually* pick up the noTravelOut from our enclosing room, but * only when the enclosing room didn't override 'out' to point * somewhere else. Explicitly setting up a 'noTravelOut' here * ensures that we'll consistently GET OUT of the nested room even if * the enclosing room has its own 'out' destination. * * Note that nestedRoomOut shows as a listed exit in exit listings * (for the EXITS command and in the status line). If you don't want * OUT to be listed as an available exit for the nested room, you * should override this to use noTravelOut instead. */ out = nestedRoomOut /* * An actor is attempting to "get out" while in this location. By * default, we'll treat this as getting out of this object. This * can be overridden if "get out" should do something different. */ disembarkRoom() { /* run the appropriate command to get out of this nested room */ removeFromNested(); } /* * Make the actor stand up from this location. By default, we'll * cause the actor to travel (using travelWithin) to our container, * and assume the appropriate posture for the container. */ makeStandingUp() { /* remember the old posture, in case the travel fails */ local oldPosture = gActor.posture; /* get the exit destination; if there isn't one, we can't proceed */ local dest = exitDestination; if (dest == nil) { reportFailure(&cannotDoFromHereMsg); exit; } /* * Set the actor's posture to the default for the destination. * Do this before effecting the actual travel, so that the * destination can change this default if it wants. */ gActor.makePosture(dest.defaultPosture); /* protect against 'exit' and the like during the travel attempt */ try { /* * move the actor to our exit destination, traveling entirely * within nested locations */ gActor.travelWithin(dest); } finally { /* if we didn't end up traveling, restore the old posture */ if (gActor.isIn(self)) gActor.makePosture(oldPosture); } /* generate the appropriate default for the new location */ gActor.okayPostureChange(); } /* * Try an implied command to move the actor from outside of this * nested room into this nested room. This must be overridden in * subclasses to carry out the appropriate implied command. Returns * the result of tryImplicitAction(). * * This is called when we need to move an actor into this location * as part of an implied command. We use an overridable method * because different kinds of nested rooms have different commands * for entering: SIT ON CHAIR, LIE ON BED, GET IN CAR, RIDE BIKE, * and so on. This should be normally be overridden imply by * calling tryImplicitAction() with the appropriate command for the * specific type of nested room, and returning the result. */ tryMovingIntoNested() { /* do nothing by default - subclasses must override */ return nil; } /* * message property to use for reportFailure when * tryMovingIntoNested fails */ mustMoveIntoProp = nil /* * Try an implied command to remove an actor from this location and * place the actor in my immediate containing location. This must * be overridden in subclasses to carry out the appropriate implied * command. Returns the result of tryImplicitAction(). * * This is essentially the reverse of tryMovingIntoNested(), and * should in most cases be implemented by calling * tryImplicitAction() with the appropriate command to get out of * the room, and returning the result. */ tryRemovingFromNested() { /* do nothing by default - subclasses must override */ return nil; } /* * Replace the current action with one that removes the actor from * this nested room. This is used to implement the GET OUT command * when the actor is directly in this nested room. In most cases, * this should simply be implemented with a call to replaceAction() * with the appropriate command. */ removeFromNested() { /* subclasses must override */ } /* * Try moving the actor into this location. This is used to move * the actor into this location as part of meeting preconditions, * and we use the normal precondition check protocol: we return nil * if the condition (actor is in this room) is already met; we * return true if we successfully execute an implied command to meet * the condition; and we report a failure message and terminate the * command with 'exit' if we don't know how to meet the condition or * the implied command we try to execute fails or fails to satisfy * the condition. * * This does not normally need to be overridden in subclasses. */ checkMovingActorInto(allowImplicit) { /* if the actor is within me, use default handling */ if (gActor.isIn(self)) return inherited(allowImplicit); /* try an implied command to move the actor into this nested room */ if (allowImplicit && tryMovingIntoNested()) { /* if we didn't succeed, terminate the command */ if (!gActor.isDirectlyIn(self)) exit; /* tell the caller we executed an implied command */ return true; } /* * if we can be seen, report that the actor must travel here * first; if we can't be seen, simply say that this can't be done * from here */ if (gActor.canSee(self)) { /* report that we have to move into 'self' first */ reportFailure(mustMoveIntoProp, self); } else { /* * we can't be seen; simply say this we can't do this command * from the current location */ reportFailure(&cannotDoFromHereMsg); } /* terminate the action */ exit; } /* * Check, using pre-condition rules, that the actor is removed from * this nested location and moved to my immediate location. This is * used to enforce a precondition that the actor is in the enclosing * location. * * This isn't normally overridden in subclasses. */ checkActorOutOfNested(allowImplicit) { /* try removing the actor from this nested location */ if (allowImplicit && tryRemovingFromNested()) { /* * make sure we managed to move the actor to our exit * destination */ if (!gActor.isDirectlyIn(exitDestination)) exit; /* indicate that we carried out an implied command */ return true; } /* we can't carry out our implied departure plan - fail */ reportFailure(&cannotDoFromMsg, self); exit; } /* * Check, using pre-condition rules, that the actor is ready to * enter this room as a nested location. * * This isn't normally overridden in subclasses. */ checkActorReadyToEnterNestedRoom(allowImplicit) { /* * If the actor is directly in this room, we obviously need do * nothing, as the actor is already in this nested room. */ if (gActor.isDirectlyIn(self)) return nil; /* * If the actor isn't within us (directly or indirectly), we * must move the actor to a valid "staging location," so that * the actor can move from the staging location into us. (A * staging location is simply any location from which we can * move directly into this nested room without any intervening * travel in or out of other nested rooms.) */ if (!gActor.isIn(self)) return checkActorInStagingLocation(allowImplicit); /* * The actor is within us, but isn't directly within us, so * handle this with the normal routine to move the actor into * this room. */ return checkMovingActorInto(allowImplicit); } /* * Check, using precondition rules, that the actor is in a valid * "staging location" for entering this nested room. We'll ensure * that the actor is directly in one of the locations in our * stagingLocations list, running an appropriate implicit command to * move the actor to the first item in that list if the actor isn't * in any of them. * * This isn't normally overridden in subclasses. */ checkActorInStagingLocation(allowImplicit) { local lst; local target; /* get the list of staging locations */ lst = stagingLocations; /* if there are no valid staging locations, we can't move here */ if (lst.length() == 0) { cannotMoveActorToStagingLocation(); exit; } /* * Try each of the locations in our staging list, to see if the * actor is directly in any of them. */ foreach (local cur in lst) { /* * if the actor is directly in this staging location, then * the actor can reach the destination with no additional * intervening travel - simply return nil in this case to * indicate that no implicit commands are needed before the * proposed nested room entry */ if (gActor.isDirectlyIn(cur)) return nil; } /* * The actor isn't directly in any staging location, so we must * move the actor to an appropriate staging location before we * can proceed. Choose a staging location based on the actor's * current location. */ if ((target = chooseStagingLocation()) != nil) { /* * We've chosen a target staging location. First, check to * make sure the location we've chosen is valid - we might * have chosen a default (such as the nested room's * immediate container) that isn't usable as a staging * location, so we need to check with it first to make sure * it's willing to allow this. */ target.checkStagingLocation(self); /* * The check routine didn't abort the command, so try an * appropriate implicit command to move the actor into the * chosen staging location. */ return target.checkMovingActorInto(allowImplicit); } /* * There's no apparent intermediate staging location given the * actor's current location. We thus cannot proceed with the * command; simply report that we can't get there from here. */ cannotMoveActorToStagingLocation(); exit; } /* * Choose an intermediate staging location, given the actor's * current location. This routine is called when the actor is * attempting to move into 'self', but isn't in any of the allowed * staging locations for 'self'; this routine's purpose is to choose * the staging location that the actor should implicitly try to * reach on the way to 'self'. * * By default, we'll attempt to find the first of our staging * locations that indirectly contains the actor. (We know none of * the staging locations directly contains the actor, because if one * did, we wouldn't be called in the first place - we're only called * when the actor isn't already directly in one of our staging * locations.) This approach is appropriate when nested rooms are * related purely by containment: if an actor is in a nested room * within one of our staging locations, we can reach that staging * location by having the actor get out of the more deeply nested * room. * * However, this default approach is not appropriate when nested * rooms are related in some way other than simple containment. We * don't have any general framework for other types of nested room * relationships, so this routine must be overridden in such a case * with special-purpose code defining the special relationship. * * If we fail to find any staging location indirectly containing the * actor, we'll return the result of defaultStagingLocation(). */ chooseStagingLocation() { /* look for a staging location indirectly containing the actor */ foreach (local cur in stagingLocations) { /* * if the actor is indirectly in this staging location, * choose it as the target intermediate staging location */ if (gActor.isIn(cur)) return cur; } /* * We didn't find any locations in the staging list that * indirectly contain the actor, so use the default staging * location. */ return defaultStagingLocation(); } /* * The default staging location for this nested room. This is the * staging location we'll attempt to reach implicitly if the actor * isn't in any of the rooms in the stagingLocations list already. * We'll return the first element of our stagingLocations list for * which isStagingLocationKnown returns true. */ defaultStagingLocation() { local lst; /* get the list of valid staging locations */ lst = stagingLocations; /* find the first element which is known to the actor */ foreach (local cur in lst) { /* if this staging location is known, take it as the default */ if (isStagingLocationKnown(cur)) return cur; } /* we didn't find any known staging locations - there's no default */ return nil; } /* * Report that we are unable to move an actor to any staging * location for this nested room. By default, we'll generate the * message "you can't do that from here," but this can overridden to * provide a more specific if desired. */ cannotMoveActorToStagingLocation() { /* report the standard "you can't do that from here" message */ reportFailure(&cannotDoFromHereMsg); } /* * Report that we are unable to move an actor out of this nested * room, because there's no valid 'exit destination'. This is * called when we attempt to GET OUT OF the nested room, and the * 'exitDestination' property is nil. */ cannotMoveActorOutOf() { /* report the standard "you can't do that from here" message */ reportFailure(&cannotDoFromHereMsg); } /* * The valid "staging locations" for this nested room. This is a * list of the rooms from which an actor can DIRECTLY reach this * nested room; in other words, the actor will be allowed to enter * 'self', with no intervening travel, if the actor is directly in * any of these locations. * * If the list is empty, there are no valid staging locations. * * The point of listing staging locations is to make certain that * the actor has to go through one of these locations in order to * get into this nested room. This ensures that we enforce any * conditions or trigger any side effects of moving through the * staging locations, so that a player can't bypass a puzzle by * trying to move directly from one location to another without * going through the required intermediate steps. Since we always * require that an actor go through one of our staging locations in * order to enter this nested room, and since we carry out the * travel to the staging location using implied commands (which are * just ordinary commands, entered and executed automatically by the * parser), we can avoid having to code any checks redudantly in * both the staging locations and any other nearby locations. * * By default, an actor can only enter a nested room from the room's * direct container. For example, if a chair is on a stage, an * actor must be standing on the stage before the actor can sit on * the chair. */ stagingLocations = [location] /* * Our exit destination. This is where an actor ends up when the * actor is immediately inside this nested room and uses a "get out * of" or equivalent command to exit the nested room. * * By default, we'll use the default staging location as the exit * destination. */ exitDestination = (defaultStagingLocation()) /* * Is the given staging location "known"? This returns true if the * staging location is usable as a default, nil if not. If this * returns true, then the location can be used in an implied command * to move the actor to the staging location in order to move the * actor into self. * * If this returns nil, no implied command will be attempted for * this possible staging location. This doesn't mean that an actor * gets a free pass through the staging location; on the contrary, * it simply means that we won't try any automatic command to move * an actor to the staging location, hence travel from a non-staging * location to this nested room will simply fail. This can be used * when part of the puzzle is to figure out that moving to the * staging location is required in the first place: if we allowed an * implied command in such cases, we'd give away the puzzle by * solving it automatically. * * By default, we'll treat all of our staging locations as known. */ isStagingLocationKnown(loc) { return true; } /* * Get the travel preconditions for an actor in this location. By * default, if we have a container, and the actor can see the * container, we'll return its travel preconditions; otherwise, we'll * use our inherited preconditions. */ roomTravelPreCond() { local ret; /* * If we can see out to our location, use the location's * conditions, since by default we'll try traveling from the * location; if we can't see out to our location, we won't be * attempting travel through our location's connectors, so use * our own preconditions instead. */ if (location != nil && gActor.canSee(location)) ret = location.roomTravelPreCond(); else ret = inherited(); /* return the results */ return ret; } /* * We cannot take a nested room that the actor is occupying */ dobjFor(Take) { verify() { /* it's illogical to take something that contains the actor */ if (gActor.isIn(self)) illogicalNow(&cannotTakeLocationMsg); /* inherit the default handling */ inherited(); } } /* * "get out of" action - exit the nested room */ dobjFor(GetOutOf) { preCond() { return [new ObjectPreCondition(self, actorDirectlyInRoom)]; } verify() { /* * the actor must be located on the platform; but allow the * actor to be indirectly on the platform, since we'll use a * precondition to move the actor out of any more nested * rooms within us */ if (!gActor.isIn(self)) illogicalNow(¬OnPlatformMsg); } check() { /* * If we have no 'exit destination' - that is, we have * nowhere to go when we GET OUT OF the nested room - then * prohibit the operation. */ if (exitDestination == nil) { /* explain the problem and terminate the command */ cannotMoveActorOutOf(); exit; } } action() { /* travel to our get-out-of destination */ gActor.travelWithin(exitDestination); /* * set the actor's posture to the default posture for the * new location */ gActor.makePosture(gActor.location.defaultPosture); /* issue a default report of the change */ defaultReport(&okayNotStandingOnMsg); } } /* explicitly define the push-travel indirect object mappings */ mapPushTravelIobj(PushTravelGetOutOf, TravelVia) ; /* ------------------------------------------------------------------------ */ /* * A "high nested room" is a nested room that is elevated above the rest * of the room. This specializes the staging location handling so that * it generates more specific messages. */ class HighNestedRoom: NestedRoom /* report that we're unable to move to a staging location */ cannotMoveActorToStagingLocation() { reportFailure(&nestedRoomTooHighMsg, self); } /* if we can't get out, report that it's because we're too high up */ cannotMoveActorOutOf() { reportFailure(&nestedRoomTooHighToExitMsg, self); } /* * Staging locations. By default, we'll return an empty list, * because a high location is not usually reachable directly from its * containing location. * * Note that puzzles involving moving platforms will have to manage * this list dynamically, which could be done either by writing a * method here that returns a list of currently valid staging * locations, or by adding objects to this list as they become valid * staging locations and removing them when they cease to be. For * example, if we have an air vent in the ceiling that we can only * reach when a chair is placed under the vent, this property could * be implemented as a method that returns a list containing the * chair only when the chair is in the under-the-vent state. * * Note that this empty default setting will also give us no exit * destination, since the default exit location is the default * staging location. */ stagingLocations = [] ; /* ------------------------------------------------------------------------ */ /* * A chair is an item that an actor can sit on. When an actor is sitting * on a chair, the chair contains the actor. In addition to sitting, * chairs can optionally allow standing as well. * * We define the "BasicChair" as something that an actor can sit on, and * then subclass this with the standard "Chair", which adds surface * capabilities. */ class BasicChair: NestedRoom /* * A list of the allowed postures for this object. By default, we * can sit and stand on a chair, since most ordinary chairs are * suitable for both. */ allowedPostures = [sitting, standing] /* * A list of the obvious postures for this object. The only obvious, * default thing you do with most ordinary chairs is sit on them, * even they allow other postures. Something like a large sofa might * want to allow both sitting and lying. * * This list differs from the allowed postures list because some * postures might be possible but not probable. For most ordinary * chairs, standing is possible, but it's not the first thing you'd * think of doing with the chair. */ obviousPostures = [sitting] /* * A chair's effective follow location is usually its location's * effective follow location, because we don't usually want to treat * a chair as a separate location for the purposes of "follow." * That is, if A and B are in the same room, and A sits down on a * chair in the room, we don't want to count this as a move that B * could follow. */ effectiveFollowLocation = (location.effectiveFollowLocation) /* * Try an implied command to move the actor from outside of this * nested room into this nested room. By default, we'll call upon * our default posture object to activate its command to move the * actor into this object in the default posture. For a chair, the * default posture is typically sitting, so the 'sitting' posture * will perform a SIT ON <self> command. */ tryMovingIntoNested() { /* * ask our default posture object to carry out the appropriate * command to move the actor into 'self' in that posture */ return defaultPosture.tryMakingPosture(self); } /* tryMovingIntoNested failure message is "must sit on chair" */ mustMoveIntoProp = &mustSitOnMsg /* default posture in this nested room is sitting */ defaultPosture = sitting /* * by default, objects dropped while sitting in a chair go into the * enclosing location's drop destination */ getDropDestination(obj, path) { return location != nil ? location.getDropDestination(obj, path) : self; } /* * Remove an actor from the chair. By default, we'll simply stand * up, since this is the normal way out of a chair. */ tryRemovingFromNested() { /* try standing up */ if (gActor.posture == sitting) return tryImplicitAction(Stand); else return tryImplicitAction(GetOffOf, self); } /* * Run the appropriate command to remove us from this nested * container, as a replacement command. */ removeFromNested() { /* to get out of a chair, we simply stand up */ if (gActor.posture == sitting) replaceAction(Stand); else replaceAction(GetOutOf, self); } /* * "sit on" action */ dobjFor(SitOn) { preCond = (preCondForEntry(sitting)) verify() { /* verify entering the chair in a 'sitting' posture */ if (verifyEntry(sitting, &alreadySittingOnMsg, &noRoomToSitMsg)) inherited(); } action() { /* enter the chair in the 'sitting' posture */ performEntry(sitting); } } /* * "stand on" action */ dobjFor(StandOn) { /* if it's allowed, use the same preconditions as 'sit on' */ preCond = (preCondForEntry(standing)) verify() { /* verify entering in a 'standing' posture */ if (verifyEntry(standing, &alreadyStandingOnMsg, &noRoomToStandMsg)) inherited(); } action() { /* enter the chair in the 'standing' posture */ performEntry(standing); } } /* * "lie on" action */ dobjFor(LieOn) { preCond = (preCondForEntry(lying)) verify() { /* verify entering in a 'lying' posture */ if (verifyEntry(lying, &alreadyLyingOnMsg, &noRoomToLieMsg)) inherited(); } action() { /* enter in the 'lying' posture */ performEntry(lying); } } /* * For "get on/in" / "board", let our default posture object handle * it, by running the appropriate nested action that moves the actor * into self in the default posture. */ dobjFor(Board) { verify() { } action() { defaultPosture.setActorToPosture(gActor, self); } } /* "get off of" is the same as "get out of" */ dobjFor(GetOffOf) asDobjFor(GetOutOf) /* standard preconditions for sitting/lying/standing on the chair */ preCondForEntry(posture) { /* * if this is not among my allowed postures, we don't need any * special preconditions, since we'll fail in the verify */ if (allowedPostures.indexOf(posture) == nil) return inherited(); /* * in order to enter the chair, we have to be able to touch it * and we have to be able to enter it as a nested room */ return [touchObj, new ObjectPreCondition(self, actorReadyToEnterNestedRoom)]; } /* * Verify that we can enter the chair in the given posture. This * performs verification work common to SIT ON, LIE ON, and STAND ON. * If this returns true, the caller should inherit the base class * default handling, otherwise it shouldn't. */ verifyEntry(posture, alreadyMsg, noRoomMsg) { /* * if the given posture isn't allowed, tell the caller to use the * inherited default handling */ if (allowedPostures.indexOf(posture) == nil) return true; /* this posture is allowed, but it might not be obvious */ if (obviousPostures.indexOf(posture) == nil) nonObvious; /* * If the actor is already on this chair in the given posture, * this action is redundant. If we already verified okay on this * point for this same action, ignore the repeated command - it * must mean that we applied a precondition that did all of our * work for us (such as moving us out of a nested room * immediately within us). */ if (gActor.posture == posture && gActor.isDirectlyIn(self) && gAction.verifiedOkay.indexOf(self) == nil) illogicalNow(alreadyMsg); else gAction.verifiedOkay += self; /* * If there's not room for the actor's added bulk, don't allow * the actor to sit/lie/stand on the chair. If the actor is * already within the chair, there's no need to add the actor's * bulk for this change, since it's already counted as being * within us. */ if (!gActor.isIn(self) && getBulkWithin() + gActor.getBulk() > bulkCapacity) illogicalNow(noRoomMsg); /* we can't sit/stand/lie on something the actor is holding */ if (isIn(gActor)) illogicalNow(&cannotEnterHeldMsg); /* * if the actor is already in me, but in a different posture, * boost the likelihood slightly */ if (gActor.isDirectlyIn(self) && gActor.posture != posture) logicalRank(120, 'already in'); /* tell the caller we don't want to inherit the base class handling */ return nil; } /* * Perform entry in the given posture. This carries out the common * actions for SIT ON, LIE ON, and STAND ON. */ performEntry(posture) { /* * Move the actor into me - this counts as interior travel within * the enclosing room. Note that we move the actor before * changing the actor's posture in case the travel fails. */ gActor.travelWithin(self); /* set the actor to the desired posture */ gActor.makePosture(posture); /* report success */ defaultReport(&roomOkayPostureChangeMsg, posture, self); } ; /* * A Chair is a basic chair with the addition of being a Surface. */ class Chair: BasicChair, Surface /* * By default, a chair has a seating capacity of one person, so use * a maximum bulk that only allows one actor to occupy the chair at * a time. */ bulkCapacity = 10 ; /* * Bed. This is an extension of Chair that allows actors to lie on it * as well as sit on it. As with chairs, we have a basic bed, plus a * regular bed that serves as a surface as well. */ class BasicBed: BasicChair /* * we can sit, lie, and stand on a typical bed, but only sitting and * lying are obvious default actions */ allowedPostures = [sitting, lying, standing] obviousPostures = [sitting, lying] /* tryMovingIntoNested failure message is "must sit on chair" */ mustMoveIntoProp = &mustLieOnMsg /* default posture in this nested room is sitting */ defaultPosture = lying ; /* * A Bed is a basic bed with the addition of Surface capabilities. */ class Bed: BasicBed, Surface ; /* * A Platform is a nested room upon which an actor can stand. In * general, when you can stand on something, you can also sit and lie on * it as well (it might not be comfortable, but it is usually at least * possible), so we make this a subclass of Bed. * * The main difference between a platform and a chair that allows * standing is that a platform is more of a mini-room. In particular, * items an actor drops while standing on a platform land on the platform * itself, whereas items dropped while sitting (or standing) on a chair * land in the enclosing room. In addition, the obvious default action * for a chair is to sit on it, while the obvious default action for a * platform is to stand on it. */ class BasicPlatform: BasicBed /* * we can sit, lie, and stand on a typical platform, and all of * these could be reasonably expected to be done */ allowedPostures = [sitting, lying, standing] obviousPostures = [sitting, lying, standing] /* an actor can follow another actor onto or off of a platform */ effectiveFollowLocation = (self) /* tryMovingIntoNested failure message is "must get on platform" */ mustMoveIntoProp = &mustGetOnMsg /* default posture in this nested room is sitting */ defaultPosture = standing /* by default, objects dropped on a platform go onto the platform */ getDropDestination(obj, path) { return self; } /* * Remove an actor from the platform. "Get off" is the normal * command to leave a platform. */ tryRemovingFromNested() { /* try getting off of the platform */ return tryImplicitAction(GetOffOf, self); } /* * Replace the current action with one that removes the actor from * this nested room. */ removeFromNested() { /* get off of the platform */ replaceAction(GetOffOf, self); } /* * Make the actor stand up. On a platform, standing is normally * allowed, so STAND doesn't usually imply "get off platform" as it * does in the base class. */ makeStandingUp() { /* * If standing isn't among my allowed postures, inherit the * default behavior, which is to get out of the nested room. */ if (allowedPostures.indexOf(standing) == nil) { /* we can't stand on the platform, so use the default handling */ inherited(); } else { /* we can stand on the platform, so make the actor stand */ gActor.makePosture(standing); /* issue a default report of the change */ defaultReport(&roomOkayPostureChangeMsg, standing, self); } } /* * Traveling 'down' from a platform should generally be taken to * mean 'get off platform'. */ down = noTravelDown ; /* * A Platform is a basic platform with the addition of Surface behavior. */ class Platform: BasicPlatform, Surface ; /* * A "nominal platform" is a named place where NPC's can stand. This * class makes it easy to arrange for an NPC to be described as standing * in a particular location in the room: for example, we could have an * actor "standing in a doorway", or "leaning against the streetlamp." * * In most cases, a nominal platform is a "secret" object, in that it * won't be listed in a room's contents and it won't have any vocabulary * words. So, the player will never be able to refer to the object in a * command. * * To use this class, instantiate it with an object located in the room * containing the pseudo-platform. Don't give the object any vocabulary * words. Locate actors within the pseudo-platform to give them the * special description. * * For simple platform-like "standing on" descriptions, just define the * name. For descriptions like "standing in", "standing under", or * "standing near", where only the preposition needs to be customized, * define the name and define actorInPrep. For more elaborate * customizations, such as "leaning against the streetlamp", you'll need * to override roomActorHereDesc, roomActorStatus, roomActorPostureDesc, * roomListActorPosture, and actorInGroupPrefix/Suffix, */ class NominalPlatform: Fixture, Platform /* don't let anyone stand/sit/lie here via a command */ dobjFor(StandOn) { verify() { illogical(&cannotStandOnMsg); } } dobjFor(SitOn) { verify() { illogical(&cannotSitOnMsg); } } dobjFor(LieOn) { verify() { illogical(&cannotLieOnMsg); } } /* ignore me for 'all' and object defaulting */ hideFromAll(action) { return true; } hideFromDefault(action) { return true; } /* * nominal platforms are internal objects only, not part of the * visible game world structure, so treat them as equivalent to their * location for FOLLOW purposes */ effectiveFollowLocation = (location.effectiveFollowLocation) ; /* * A booth is a nested room that serves as a small enclosure within a * larger room. Booths can serve as regular containers as well as * nested rooms, and can be made openable by addition of the Openable * mix-in class. Note that booths don't have to be fully enclosed, nor * do they actually have to be closable. * * Examples of booths: a cardboard box large enough for an actor can * stand in; a closet; a shallow pit. */ class Booth: BasicPlatform, Container /* * Try an implied command to move the actor from outside of this * nested room into this nested room. */ tryMovingIntoNested() { /* try getting in me */ return tryImplicitAction(Board, self); } /* * Remove an actor from the booth. "Get out" is the normal command * to leave this type of room. */ tryRemovingFromNested() { /* try getting out of the object */ return tryImplicitAction(GetOutOf, self); } /* * Replace the current action with one that removes the actor from * this nested room. */ removeFromNested() { /* get out of the object */ replaceAction(GetOutOf, self); } /* * "Enter" is equivalent to "get in" (or "board") for a booth */ dobjFor(Enter) asDobjFor(Board) /* explicitly define the push-travel indirect object mapping */ mapPushTravelIobj(PushTravelEnter, Board) ; /* ------------------------------------------------------------------------ */ /* * A Vehicle is a special type of nested room that moves instead of the * actor in response to travel commands. When an actor in a vehicle * types, for example, "go north," the vehicle moves north, not the * actor. * * In most cases, a Vehicle should multiply inherit from one of the * other nested room subclasses to make it more specialized. For * example, a bicycle might inherit from Chair, so that actors can sit * on the bike. * * Note that because Vehicle inherits from NestedRoom, the OUT direction * in the vehicle by default means what it does in NestedRoom - * specifically, getting out of the vehicle. This is appropriate for * vehicles where we'd describe passengers as being inside the vehicle, * such as a car or a boat. However, if the vehicle is something you * ride on, like a horse or a bike, it's probably more appropriate for * OUT to mean "ride the vehicle out of the enclosing room." To get * this effect, simply override the "out" property and set it to nil; * this will prevent the NestedRoom definition from being inherited, * which will make us look for the OUT location of the enclosing room as * the travel destination. */ class Vehicle: NestedRoom, Traveler /* * When a traveler is in a vehicle, and the traveler performs a * travel command, the vehicle is what changes location; the * contained traveler simply stays put while the vehicle moves. */ getLocTraveler(trav, conn) { local stage; /* * If the connector is contained within the vehicle, take it as * leading out of the vehicle - this means we want to move * traveler within the vehicle rather than the vehicle. * Consider the connector be inside the vehicle if it's a * physical object (a Thing) that's inside the vehicle, or one * of the vehicle's own directional links points directly to the * connector. * * Likewise, if the connector is marked with the vehicle or any * object inside the vehicle as its staging location, the * vehicle obviously isn't involved in the travel. */ if ((conn != nil && conn.ofKind(Thing) && conn.isIn(self)) || Direction.allDirections.indexWhich( {dir: self.(dir.dirProp) == conn}) != nil || ((stage = conn.connectorStagingLocation) != nil && (stage == self || stage.isIn(self)))) { /* * this connector leads out from within the vehicle - move * the inner traveler rather than the vehicle itself */ return trav; } /* * If we have a location, ask it who travels when the VEHICLE is * the traveler within it; otherwise, the vehicle is the * traveler. * * We ask the location, because the location might itself be a * vehicle, in which case it might want us to be driving around * the enclosing vehicle. However, we pass ourselves (i.e., * this vehicle) as the inner traveler, rather than the traveler * we were passed, because a traveler within a vehicle moves the * vehicle when traveling. */ return (location != nil ? location.getLocTraveler(self, conn) : self); } /* * An OUT command while within a vehicle could mean one of two * things: either to GET OUT of the vehicle, or to ride/drive the * vehicle out of its enclosing location. * * There's no good way of guessing which meaning the player intends, * so we have to choose one or the other. We choose the ride/drive * interpretation as the default, for two reasons. First, it seems * to be what most players expect. Second, the other interpretation * leaves no standard way of expressing the ride/drive meaning. We * return nil here to indicate to the OUT action that we want the * enclosing location's 'out' connector to be used while an actor is * in the vehicle. * * For some vehicles, it might be more appropriate for OUT to mean * GET OUT. In these cases, simply override this so that it returns * nestedRoomOut. */ out = nil /* * Get the "location push traveler" - this is the traveler when a * push-travel command is performed by a traveler within this * location. If the object we're trying to push is within me, use * the contained traveler, since the contained traveler must be * trying to push the object around directly. If the object isn't * inside me, then we're presumably trying to use the vehicle to push * around the object, so the traveler is the vehicle or something * containing the vehicle. */ getLocPushTraveler(trav, obj) { /* * If the object is inside me, use the nested traveler; * otherwise, we're presumably trying to use the vehicle to move * the object. */ if (obj.isIn(self)) { /* * we're moving something around inside me; use the * contained traveler */ return trav; } else if (location != nil) { /* * we're pushing something around outside me, so we're * probably trying to use the vehicle to do so; we have a * location, so ask it what it thinks, passing myself as the * new suggested traveler */ return location.getLocPushTraveler(self, obj); } else { /* * we're pushing something around outside me, and I have no * location, so I must be the traveler */ return self; } } /* * Determine if an actor is traveling with me. The normal base * class implementation works, but it's more efficient just to check * to see if the actor is inside this object than to construct the * entire nested contents list just to check to see if the actor's * in that list. */ isActorTraveling(actor) { return actor.isIn(self); } /* invoke a callback for each actor traveling with us */ forEachTravelingActor(func) { /* invoke the callback on each actor in our contents */ allContents().forEach(function(obj) { if (obj.isActor) (func)(obj); }); } /* * Get the actors involved in the travel. This is a list consisting * of all of the actors contained within the vehicle. */ getTravelerActors = (allContents().subset({x: x.isActor})) /* * there are no self-motive actors in a vehicle - the vehicle is * doing the travel, and the actors within are just moving along * with it as cargo */ getTravelerMotiveActors = [] /* * Traveler preconditions for the vehicle. By default, we add no * preconditions of our own, but specific vehicles might want to * override this. For example, a car might want to require that the * doors are closed, the engine is running, and the seatbelts are * fastened before it can travel. */ travelerPreCond(conn) { return []; } /* * Check, using pre-condition rules, that the traveler is in the * given room, moving the traveler to the room if possible. */ checkMovingTravelerInto(room, allowImplicit) { /* if we're in the desired location, we're set */ if (isDirectlyIn(room)) return nil; /* * By default, we can't move a vehicle into a room implicitly. * Individual vehicles can override this when there's an obvious * way of moving the vehicle in and out of nested rooms. */ reportFailure(&vehicleCannotDoFromMsg, self); exit; } /* * the lister object we use to display the list of actors aboard, in * arrival and departure messages for the vehicle */ aboardVehicleListerObj = aboardVehicleLister ; /* * A VehicleBarrier is a TravelConnector that allows actors to travel, * but blocks vehicles. By default, we block all vehicles, but * subclasses can customize this so that we block only specific * vehicles. */ class VehicleBarrier: TravelBarrier /* * Determine if the given traveler can pass through this connector. * By default, we'll return nil for a Vehicle, true for anything * else. This can be overridden to allow specific vehicles to pass, * or to filter on any other criteria. */ canTravelerPass(traveler) { return !traveler.ofKind(Vehicle); } /* explain why we can't pass */ explainTravelBarrier(traveler) { reportFailure(&cannotGoThatWayInVehicleMsg, traveler); } ; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/adv3.h����������������������������������������������������������������0000644�0001750�0000144�00000215020�11746537323�016146� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - main header * * This file provides definitions of macros, properties, and other * identifiers used throughout the library and in game source. * * Each source code file in the library and in a game should generally * #include this header near the top of the source file. */ #ifndef ADV3_H #define ADV3_H /* ------------------------------------------------------------------------ */ /* * Include the system headers that we depend upon. We include these here * so that each game source file will pick up the same set of system * headers in the same order, which is important for intrinsic function * set definitions. */ #include <tads.h> #include <tok.h> #include <t3.h> #include <vector.h> #include <strbuf.h> #include <file.h> #include <dict.h> /* ------------------------------------------------------------------------ */ /* * Establish the default dictionary for the game's player command parser * vocabulary. */ dictionary cmdDict; /* ------------------------------------------------------------------------ */ /* * canInherit - determine if there's anything to inherit from the current * method. Returns true if there's a method to inherit, nil if * 'inherited' in the current context would not invoke any code. */ #define canInherit() \ propInherited(targetprop, targetobj, definingobj, PropDefAny) /* ------------------------------------------------------------------------ */ /* * Generic part of speech for "miscellaneous word." We use this to * classify words in unstructured phrases; it can apply to any token. * Note that dictionary entries are never made with this word type, so * it's not specific to any language; this is merely for flagging words in * unstructured phrases in grammar matches. */ dictionary property miscWord; /* * Generic part of speech for SpecialTopic words. We enter special topic * keywords into the parser dictionary so that they're not flagged as * unknown words if they're used out of context. */ dictionary property specialTopicWord; /* ------------------------------------------------------------------------ */ /* * If we're compiling for debugging, automatically include the parser * debug code, which allows certain information on the parsing process * (such as grammar match trees) to be displayed each time a command is * typed. * * Note that you can turn on parser debugging independently of full * compiler debug information simply by explicitly defining PARSER_DEBUG * (with the t3make -D option, for example). */ #ifdef __DEBUG #define PARSER_DEBUG #endif /* * Define some convenience macros for parser debug operations. When * PARSER_DEBUG isn't defined, these macros expand out to nothing. */ #ifdef PARSER_DEBUG #define dbgShowGrammarList(lst) showGrammarList(lst) #define dbgShowGrammarWithCaption(headline, match) \ showGrammarWithCaption(headline, match) #else /* PARSER_DEBUG */ #define dbgShowGrammarList(lst) #define dbgShowGrammarWithCaption(headline, match) #endif /* PARSER_DEBUG */ /* ------------------------------------------------------------------------ */ /* * Parser global variables giving information on the command currently * being performed. These are valid through doAction processing. These * should never be changed except by the parser. */ /* the actor performing the current command */ #define gActor (libGlobal.curActor) /* * For convenience, define some macros that return the current direct and * indirect objects from the current action. The library only uses direct * and indirect objects, so games that define additional command objects * will have to add their own similar macros for those. */ #define gDobj (gAction.getDobj()) #define gIobj (gAction.getIobj()) /* * Get the current ResolvedTopic, and the literal text of the topic phrase * as the user typed it (but converted to lower case). These are * applicable when the current action has a topic phrase. */ #define gTopic (gAction.getTopic()) #define gTopicText (gTopic.getTopicText.toLower()) /* get the current literal phrase text, when the command has one */ #define gLiteral (gAction.getLiteral()) /* * The tentative pre-resolution lists for the direct and indirect objects. * When we're resolving an object of a multi-object command, these * pre-resolution lists are available for the later-resolved objects. * * Note that these values are list of ResolveInfo objects. The obj_ * property of a list entry gives the entry's game-world object. * * These lists do not provide the final resolution lists for the objects; * rather, they provide a tentative set of possibilities based on the * information that's available without knowing the results of resolving * the earlier-resolved objects yet. * * These are not meaningful when resolving single-object actions. */ #define gTentativeDobj (gAction.getTentativeDobj()) #define gTentativeIobj (gAction.getTentativeIobj()) /* * the actor who *issued* the current command (this is usually the player * character, because most commands are initiated by the player on the * command line, but it is also possible for one actor to send a command * to another programmatically) */ #define gIssuingActor (libGlobal.curIssuingActor) /* the Action object of the command being executed */ #define gAction (libGlobal.curAction) /* * Determine if the current global action is the specified action. Only * the action prefix is needed - so use "Take" rather than "TakeAction" * here. * * This tests to see if the current global action is an instance of the * given action class - we test that it's an instance rather than the * action class itself because the parser creates an instance of the * action when it matches the action's syntax. */ #define gActionIs(action) \ (gAction != nil && gAction.actionOfKind(action##Action)) /* is the current global action ANY of the specified actions? */ #define gActionIn(action...) \ (gAction != nil \ && (action#foreach/gAction.actionOfKind(action##Action)/||/)) /* the verification results object - this is valid during verification */ #define gVerifyResults (libGlobal.curVerifyResults) /* the command transcript object - this is valid during command execution */ #define gTranscript (mainOutputStream.curTranscript) /* * Some message processors add their own special parameters to messages, * because they want to use expansion parameters (in the "{the dobj/him}" * format) outside of the set of objects directly involved in the command. * * The Action method setMessageParam() lets you define such a parameter, * but for convenience, we define this macro for setting one or more * parameters whose names exactly match their local variable names. In * other words, if you call this macro like this: * * gMessageParams(obj, cont) * * then you'll get one parameter with the text name 'obj' whose expansion * will be the value of the local variable obj, and another with text name * 'cont' whose expansion is the value of the local variable cont. */ #define gMessageParams(var...) \ (gAction.setMessageParams(var#foreach/#@var, var/,/)) /* * Synthesize a global message parameter name for the given object and * return the synthesized name. This is useful in cases where there might * be repeated instances of global message parameters in the same action, * so it's not safe to use a fixed name string for the object. We'll * create a unique global message parameter name, associate the object with * the name, and return the name string. */ #define gSynthMessageParam(var) (gAction.synthMessageParam(var)) /* ------------------------------------------------------------------------ */ /* * Miscellaneous macros */ /* get the current player character Actor object */ #define gPlayerChar (libGlobal.playerChar) /* * the exit lister object - if the exits module isn't included in the * game, this will be nil */ #define gExitLister (libGlobal.exitListerObj) /* * the hint manager object - if the hints module isn't included in the * game, this will be nil */ #define gHintManager (libGlobal.hintManagerObj) /* ------------------------------------------------------------------------ */ /* * The current library messages object. This is the source object for * messages that don't logically relate to the actor carrying out the * comamand. It's mostly used for meta-command replies, and for text * fragments that are used to construct descriptions. * * This message object isn't generally used for parser messages or action * replies - most of those come from the objects given by the current * actor's getParserMessageObj() or getActionMessageObj(), respectively. * * By default, this is set to libMessages. The library never changes this * itself, but a game can change this if it wants to switch to a new set of * messages during a game. (If you don't need to change messages during a * game, but simply want to customize some of the default messages, you * don't need to set this variable - you can simply use 'modify * libMessages' instead. This variable is designed for cases where you * want to *dynamically* change the standard messages during the game.) */ #define gLibMessages (libGlobal.libMessageObj) /* ------------------------------------------------------------------------ */ /* * readMainCommandTokens() phase identifiers. We define a separate code * for each kind of call to readMainCommandTokens() so that we can do any * special token processing that depends on the type of command we're * reading. * * The library doesn't use the phase information itself for anything. * These phase codes are purely for the author's use in writing * pre-parsing functions and for differentiating prompts for the different * types of input, as needed. * * Games that read additional response types of their own are free to add * their own enums to identify the additional phases. Since the library * doesn't need the phase information for anything internally, it won't * confuse the library at all to add new game-specific phase codes. */ /* reading a normal command line */ enum rmcCommand; /* reading an unknown word response, to check for an "oops" command */ enum rmcOops; /* reading a response to a prompt for a missing object phrase */ enum rmcAskObject; /* reading a response to a prompt for a missing literal phrase */ enum rmcAskLiteral; /* reading a response to an interactive disambiguation prompt */ enum rmcDisambig; /* ------------------------------------------------------------------------ */ /* * Property set definitions */ /* in debug mode, flag objFor definitions for non-existent actions */ #ifdef __DEBUG # define objForCheck(which, action) \ sentinel##which##action = __objref(action##Action, warn) #else # define objForCheck(which, action) #endif #define objFor(which, action) \ objForCheck(which, action) \ propertyset '*' ## #@which ## #@action #define dobjFor(action) objFor(Dobj, action) #define iobjFor(action) objFor(Iobj, action) /* * Treat an object definition as equivalent to another object definition. * These can be used immediately after a dobjFor() or iobjFor() to treat * the first action as though it were the second. So, if the player types * "search box", and we want to treat the direct object the same as for * "look in box", we could make this definition for the box: * * dobjFor(Search) asDobjFor(LookIn) * * Note that no semicolon is needed after this definition, and that this * definition is completely in lieu of a regular property set for the * object action. * * In general, a mapping should NOT change the role of an object: * dobjFor(X) should not usually be mapped using asIobjFor(Y), and * iobjFor(X) shouldn't be mapped using asDobjFor(Y). The problem with * changing the role is that the handler routines often assume that the * object is actually in the role for which the handler was written; a * verify handler might refer to '{dobj}' in generating a message, for * example, so reversing the roles would give the wrong object in the role. * * Role reversals should always be avoided, but can be used if necessary * under conditions where all of the code involved in the TARGET of the * mapping can be carefully controlled to ensure that it doesn't make * assumptions about object roles, but only references 'self'. Reversing * roles in a mapping should never be attempted in general-purpose library * code, because code based on the library could override the target of the * role-reversing mapping, and the override could fail to observe the * restrictions on object role references. * * Note that role reversals can almost always be handled with other * mechanisms that handle reversals cleanly. Always consider remapTo() * first when confronted with a situation that seems to call for a * role-reversing asObjFor() mapping, as remapTo() specifically allows for * object role changes. */ #define asObjFor(obj, Action) \ { \ preCond { return preCond##obj##Action; } \ verify() { verify##obj##Action; } \ remap() { return remap##obj##Action; } \ check() { check##obj##Action; } \ action() { action##obj##Action; } \ } #define asDobjFor(action) asObjFor(Dobj, action) #define asIobjFor(action) asObjFor(Iobj, action) /* * Define mappings of everything except the action. This can be used in * cases where we want to pick up the verification, preconditions, and * check routines from another handler, but not the action. This is often * useful for two-object verbs where the action processing is entirely * provided by one or the other object, so applying it to both would be * redundant. */ #define asObjWithoutActionFor(obj, Action) \ { \ preCond { return preCond##obj##Action; } \ verify() { verify##obj##Action; } \ remap() { return remap##obj##Action(); } \ check() { check##obj##Action; } \ action() { } \ } #define asDobjWithoutActionFor(action) asObjWithoutActionFor(Dobj, action) #define asIobjWithoutActionFor(action) asObjWithoutActionFor(Iobj, action) /* * "Remap" an action. This effectively rewrites the action in the given * form. Each of the object slots can be filled either with a specific * object, or with a noun phrase role name (DirectObject, IndirectObject); * in the latter case, the object or objects from the named noun phrase * role in the *current* action (i.e., before the rewrite) will be used. * * If the new action has two or more objects (for example, if it's a * TIAction), then EXACTLY ONE of the slots must be filled with a specific * object, and all of the other slots must be filled with role names. The * specific object is the one that corresponds to the original object * that's doing the remapping in the first place - this can simply be * 'self' if the new action will operate on the same object, or it can be * a different object. The important thing is that the 'verify' method * for the defining object will be forwarded to the corresponding 'verify' * method on the corresponding object for the new action. * * This macro must be used as the ENTIRE definition block for a dobjFor() * or iobjFor(). For example, to remap a "put in" command directed to a * desk so that the command is instead applied to a drawer in the desk, we * could define the following on the desk object: * * iobjFor(PutIn) remapTo(PutIn, DirectObject, deskDrawer) */ #define remapTo(action, objs...) { remap = [action##Action, ##objs] } /* * Conditionally remap an action. If 'cond' (a conditional expression) * evaluated to true, we'll remap the action as directed; otherwise, we'll * inherit the default handling */ #define maybeRemapTo(cond, action, objs...) \ { remap = ((cond) ? [action##Action, ##objs] : inherited()) } /* * For two-object push-travel actions, such as "push sled into cave", * define a special mapping for both the direct and indirect objects: * * - Map the direct object (the object being pushed) to a simple * PushTravel action. So, for "push sled into cave," map the direct * object handling to PushTravel for the sled. This makes the handling of * the command equivalent to "push sled north" and the like. * * - Map the indirect object (the travel connector) to use the PushTravel * action's verify remapper. This is handled specially by the PushTravel * action object to handle the verification as though it were verifying * the corresponding ordinary (non-push) travel action on the indirect * object. Beyond verification, we do nothing, since the direct object of * a pushable object will handle the whole action using a nested travel * action. * * This effectively decomposes the two-object action into two coupled * single-object actions: a regular PushTravel action on the object being * pushed, and a regular whatever-kind-of-travel on the connector being * traversed. This handling has the appeal that it means that we don't * need a separate PUSH version of every kind of allowed travel on a * connector, and we don't need a special handler version for each kind of * travel on a pushable object; instead, we just use the basic PushTravel * and kind-of-travel handlers to form the combined form. Note that this * still allows separate treatment of the combined form wherever desired, * just by overriding these default handlers for the two-object action. */ #define mapPushTravelHandlers(pushAction, travelAction) \ dobjFor(pushAction) asDobjFor(PushTravel) \ mapPushTravelIobj(pushAction, travelAction) #define mapPushTravelIobj(pushAction, travelAction) \ iobjFor(pushAction) \ { \ verify() \ { gAction.verifyPushTravelIobj(self, travelAction##Action); } \ } /* ------------------------------------------------------------------------ */ /* * For an Actor, delegate an action handler to the ActorState object for * processing. You can use this any time you want to write the handlers * for a particular action in the ActorState rather than in the Actor * itself. This would be desirable if the actor's response for a * particular action varies considerably according to the actor's state. * For example, if you want an actor's response to being attacked to be * handled in the actor's current ActorState object, you could put this * code in the Actor object: * * dobjFor(AttackWith) actorStateDobjFor(AttackWith) * * Once you've done this, you'd just write a normal dobjFor(AttackWith) * handler in each of the ActorState objects associated with the actor. */ #define actorStateObjFor(obj, Action) \ { \ preCond { return curState.preCond##obj##Action; } \ verify() { curState.verify##obj##Action; } \ remap() { return curState.remap##obj##Action; } \ check() { curState.check##obj##Action; } \ action() { curState.action##obj##Action; } \ } #define actorStateDobjFor(action) actorStateObjFor(Dobj, action) #define actorStateIobjFor(action) actorStateObjFor(Iobj, action) /* ------------------------------------------------------------------------ */ /* * Object role identifiers. These are used to identify the role of a noun * phrase in a command. * * The library provides base classes for actions of zero, one, and two noun * phrases in their grammars: "look", "take book", "put book on shelf". We * thus define role identifiers for direct and indirect objects. Note that * even though we stop there, this doesn't preclude games or library * extensions from adding actions that take more than two noun phrases * ("put coin in slot with tongs"); any such extensions must simply define * their own additional role identifiers for the third or fourth (etc) noun * phrase. */ enum ActorObject, DirectObject, IndirectObject; /* * A special role for the "other" object of a two-object command. This * can be used in certain contexts (such as remapTo) where a particular * object role is implied by the context, and where the action involved * has exactly two objects; OtherObject in such contexts means * DirectObject when the implied role is IndirectObject, and vice versa. */ enum OtherObject; /* ------------------------------------------------------------------------ */ /* * Pronoun types. These are used to identify pronoun antecedents when * resolving noun phrases involving pronouns. * * We define a basic set of pronouns here that are common to most * languages. Language-specific modules are free to add their own pronoun * types as needed. * * Our basic set is: * * 'it' - the neuter singular *. 'him' - the masculine singular *. 'her' - the feminine singular *. 'them' - the ungendered plural *. 'you' - second person singular *. 'me' - first person singular * * Note that the first-person and second-person pronouns are assigned * meanings that can vary by context. When a command is issued by the * player character to the player character (i.e., the command comes from * the player and no target actor is specified), these refer to the player * character when the PC is in the appropriate referral person - if the * game calls the PC "you", then the player calls the PC "me", and vice * versa. When a command is targeted to or issued by an actor other than * the PC, then "you" refers to the command's target and "me" refers to * the command's issuer. */ enum PronounIt, PronounThem, PronounHim, PronounHer, PronounYou, PronounMe; /* ------------------------------------------------------------------------ */ /* * set the location property */ + property location; /* ------------------------------------------------------------------------ */ /* * Alternative exit definition. This can be used to define a secondary * direction that links to the same destination, via the same connector, as * another direction. It's frequently desirable to link multiple * directions to the same exit; for example, a door leading north might * also lead out, or a stairway to the north could lead up as well. * * Use this as follows in a room's property list: * *. out asExit(north) * * (Note that there's no '=' sign.) * * It's not necessary to use this macro to declare an alternative exit, * since the alternatives can all point directly to the same connector as * the original. The only thing this macro does is to make the alternative * exit unlisted - it won't be shown in the list of exits in the status * line, and it won't be shown in "you can't go that way" messages. * * Note that there's one common case where you should be careful with * asExit(): if you have a room that has an exit in some compass direction, * you might be tempted to make OUT the primary "direction" for the exit, * and treat the equivalent compass as a synonym, with a line such as * "south asExit(out)". You should avoid doing this - do it the other way * instead, with the compass direction as the primary direction and OUT as * the synonym: "out asExit(south)". The reason this is important is that * if there's a nested room inside the location (such as a chair), OUT * while in the nested room will mean to get out of the nested room. If * you make the compass direction primary and make OUT the synonym, the * compass direction will be listed as an available exit both in the * location and in any nested rooms within it. */ #define asExit(dir) = static ((dir).createUnlistedProxy()) /* ------------------------------------------------------------------------ */ /* * "Person" indices. We define these as numbers rather than enums so that * we can easily use these as list indices. */ #define FirstPerson 1 #define SecondPerson 2 #define ThirdPerson 3 /* ------------------------------------------------------------------------ */ /* * Transparency levels */ /* the sense is passed without loss of detail */ enum transparent; /* the sense is passed, but with a loss of detail associated with distance */ enum distant; /* * The sense is passed, but with attenuation of energy level. No other * obscuration of detail occurs; this is something like tinted glass that * doesn't distort the transmitted sense but reduces the amount of energy. */ enum attenuated; /* * The sense is passed, but with a loss of detail due to an obscuring * layer of material. The energy level is also attenuated. This is * something like dirty or wavy glass that distorts an image transmitted * through it but doesn't completely block out light. */ enum obscured; /* the sense is not passed at all */ enum opaque; /* ------------------------------------------------------------------------ */ /* * Size classes. An object is large, medium, or small with respect to * each sense; the size is used to determine how well the object can be * sensed at a distance or when obscured. * * What "size" means depends on the sense. For sight, the size * indicates the visual size of the object. For hearing, the size * indicates the loudness of the object. */ /* * Large - the object is large enough that its details can be sensed * from a distance or through an obscuring medium. */ enum large; /* * Medium - the object can be sensed at a distance or when obscured, but * not in any detail. Most objects fall into this category. Note that * things that are parts of large objects should normally be medium. */ enum medium; /* * Small - the object cannot be sensed at a distance at all. This is * appropriate for detailed parts of medium-class objects. */ enum small; /* ------------------------------------------------------------------------ */ /* * Path traversal operations. */ /* traverse from the starting point of the path */ enum PathFrom; /* traverse into the contents */ enum PathIn; /* traverse out to the container */ enum PathOut; /* traverse from an object to a peer at the same containment level */ enum PathPeer; /* * traverse through an object with no common container on either side of * the traversal - this is used when we are traversing an object, such as a * SenseConnector, that connects unrelated locations */ enum PathThrough; /* traverse to the ending point of the path */ enum PathTo; /* ------------------------------------------------------------------------ */ /* * Listing Options */ /* * use "tall" notation, which lists objects in a single column, one item * per line (the default is "wide" notation, which creates a sentence * with the object listing) */ #define ListTall 0x0001 /* * Recursively list the contents of each item we list. * * For a 'tall' list, this indicates that we'll show the listed contents * of each item that we list, and the listed contents of those items, and * so on, indenting each level to indicate the containment relationship. * * For a 'wide' list, this indicates that we'll show the listed contents * of each item in-line in the listing, as a parenthetic note. * * For both types of listings, when this flag is set and the indent level * is zero (indicating a top-level listing), after the main list, we'll * show a separate list for the contents of each item in our list that * isn't itself listable but has listed contents, or has contents with * listed contents, and so on to any level. For example, if we're showing * a room description, and the room contains a desk that isn't listed * because it's a fixed part of the room, we'll show a separate list of * the desk's listed contents. */ #define ListRecurse 0x0002 /* * use "long list" notation - separates items that contain sublists with * special punctuation, to set off the individual items in the longer * listing from the items in the sublists (for example, separates items * with semicolons rather than commas) */ #define ListLong 0x0004 /* * This is a recursive listing of the contents of an item. This is set by * showList() in calls it makes to recursive listing levels. */ #define ListContents 0x0008 /* * Custom option bits. Flag bits with this value and higher are reserved * for use by individual lister subclasses. * * To ensure compatibility with any future changes that involve adding * more base lister flags, subclasses are encouraged to use the following * mechanism. DO NOT use #define to define your own custom subclass * flags. Instead, define a property of your lister subclass for each * flag you need as follows: * * myCustomFlag1 = ListerCustomFlag(1) // use 1 for the first flag *. myCustomFlag2 = ListerCustomFlag(2) // etc *. nextCustomFlag = ListerCustomFlag(3) * * You DO NOT have to use the name 'myCustomFlag1' - use whatever name you * like that describes the nature of the flag. However, the last item * MUST be called 'nextCustomFlag' - this ensures that any subclasses of * your class will allocate their own flags with new values that don't * conflict with any of yours. * * Then, when a client of your Lister subclass needs to pass one of your * flag to the Lister, it should simply evaluate your 'myCustomFlagN' * property of your lister. If you'd like, you can even #define a ListXxx * macro that retrieves the value, for the convenience of your callers: * * #define ListMyclassMyCustomFlag1 (Myclass.myCustomFlag1) */ #define ListCustomFlag 0x0100 #define ListerCustomFlag(n) static ((inherited.nextCustomFlag) << ((n) - 1)) /* ------------------------------------------------------------------------ */ /* * Spelled-out number options, for spellInt() and related functions. * * Note that the interfaces to the number-spelling functions can vary by * language, and that variation can include these flags. Some language * modules might ignore some of these generic flags or define additional * language-specific flags. */ /* * Use tens of hundreds rather than thousands if possible - 1950 is * 'nineteen hundred fifty' rather than 'one thousand nine hundred * fifty'. This only works if the number (not including the millions * and billions) is in the range 1,100 to 9,999, because we don't want * to say something like 'one hundred twenty hundred' for 12,000. */ #define SpellIntTeenHundreds 0x0001 /* * use 'and' before the tens - 125 is 'one hundred and twenty-five' * rather than 'one hundred twenty-five' */ #define SpellIntAndTens 0x0002 /* * put a comma after each power group - 123456 is 'one hundred * twenty-three thousand, four hundred fifty-six' */ #define SpellIntCommas 0x0004 /* ------------------------------------------------------------------------ */ /* * Decimal number format options. These are used with the number * formatting functions to control the formatting of numbers displayed in * decimal digit format. */ /* * Use a group separator character between digit groups, using the * default setting in languageGlobals. */ #define DigitFormatGroupSep 0x0001 /* * Explicitly use a comma/period to separate digit groups, overriding * the current languageGlobals setting. */ #define DigitFormatGroupComma 0x0002 #define DigitFormatGroupPeriod 0x0004 /* ------------------------------------------------------------------------ */ /* * aHref() flags */ #define AHREF_Plain 0x0001 /* plain text hyperlink (no underline/color) */ /* ------------------------------------------------------------------------ */ /* * ResolveInfo flags */ /* the noun phrase ends with an adjective */ #define EndsWithAdj 0x0001 /* * one of the words in the noun phrase was truncated from its full * dictionary spelling */ #define VocabTruncated 0x0002 /* * One or more plurals was truncated from its full dictionary spelling. * (We specially distinguish plurals that are truncated, because in * English a plural is usually formed by adding "s" or "es" to the end of * the singular form of a noun, meaning that a given singular form is * usually a leading substring of its plural. When a singular noun is * longer than the truncation limit, which is conventionally six * characters, the singular will always match as a truncated version of * the plural, so every time someone types in a singular it'll be treated * as ambiguous between the singular and plural form. So, in the English * parser, we have a preference to ignore a truncated plural any time the * word could also be interpreted as an untruncated singular, hence we * note when we have a truncated plural.) */ #define PluralTruncated 0x0004 /* * The object came from an 'all' phrase. Normally, the only time this * makes any difference is when deciding whether or not to mention which * object we're acting upon; an 'all' object should normally be mentioned * explicitly, as though the command had involved multiple objects, * because otherwise it might not be clear to the user what object had * actually matched 'all'. */ #define MatchedAll 0x0008 /* * Always announce the object before executing the command on it. This * flag can be set for objects that match phrases whose meaning isn't * necessarily known to the player, such as "all" (which selects objects * based on the simulation state, which might not exactly match what the * player had in mind) or "any book" (which might select arbitrarily from * several possibilities, so the player can't know which we'll choose). */ #define AlwaysAnnounce 0x0010 /* * The noun phrase describing this object was ambiguous, and the object * was selected by automatic disambiguation in a context where it was * clear which object was indicated. This is used in cases where the * objects not selected were all illogical for the action context. */ #define ClearDisambig 0x0020 /* * The noun phase describing this object was ambiguous, and the object was * selected by automatic disambiguation in a context where it was not * perfectly clear which object was indicated. This is used for cases * where the objects selected were more logical than the objects not * selected, but some of the unselected objects were still logical. * * This flag doesn't mean that we chose arbitrarily, but rather that we * chose the best object or objects from a field that included additional * objects that, though not quite as good, were still valid. We flag this * case because the user *could* have meant to use one of the other valid * objects, even though we consider it most likely that the user meant to * use the one(s) we selected; so, we want to flag this so we can call the * user's attention to our choice, to make it more likely that the user * will immediately notice if we made the wrong choice. * * Note that we can't have both ClearDisambig and UnclearDisambig at the * same time, but we *can* have neither of these. If neither flag is set * for an object, it simply means that the object wasn't ambiguous to * start with. When the user explicitly picks an object interactively, * the selected object is effectively unambiguous, so it won't have either * flag set; even though it started off ambiguous, the user did all of the * work of selecting the appropriate object, leaving things unambiguous in * the end. */ #define UnclearDisambig 0x0040 /* * The noun phrase was missing from the command and this object was * supplied as an implicit default. */ #define DefaultObject 0x0080 /* * We've announced this as a defaulted object. We use this to ensure that * we only make this type of announcement once, even if the opportunity to * make the announcement comes up more than once; this can happen when * we're asking for missing objects interactively in a multi-object * command, since we might want to announce a default before prompting as * well as before execution. */ #define AnnouncedDefaultObject 0x0100 /* ------------------------------------------------------------------------ */ /* * Announcement styles for disambiguated objects. These are used in the * gameMain object (see GameMainDef) to select which type of announcement * is used when the parser disambiguates a noun phrase using the * logicalness rules. */ /* * Announce unclear disambiguation results only. When this setting is * selected, the parser makes a parenthetical announcement (e.g., "(the red * door)") when it selects an object based on likelihood rankings from * among more than one logical match. The parser makes no announcement * when exactly one logical object is in scope, even if other objects match * the noun phrase by name. */ enum AnnounceUnclear; /* * Announce clear and unclear disambiguation results, both using * parenthetical announcement ("(the red door)"). When this setting is * selected, the parser makes these announcements every time it applies the * logicalness rules or likelihood rankings to disambiguate a noun phrase. * There's no announcement when no disambiguation is needed (because the * noun phrase matches only one in-scope object). */ enum AnnounceClear; /* * Describe clear disambiguation results, rather than announcing them. The * parser makes the parenthetical announcement, as usual, for unclear * disambiguation picks, but not for clear picks (a clear pick is one where * there's only one logical object, even though the noun phrase matches * more than one object). For clear picks, however, the parser uses a * verbose version of the action reply in lieu of one of the terse default * messages. For example, rather than saying just "Taken", the parser * would reply "You take the red book." The longer messages mention the * object by name, to make it clear exactly which one was chosen. */ enum DescribeClear; /* ------------------------------------------------------------------------ */ /* * Inventory modes. "Wide" mode displays the inventory in paragraph form; * "tall" mode displays as a list, with one item per line, indenting items * to reflect containment. */ enum InventoryWide, InventoryTall; /* ------------------------------------------------------------------------ */ /* * Define an action with the given base class. This adds the *Action * suffix to the given root name, and defines a class with the given base * class. We also define the baseActionClass property to refer to myself; * this is the canonical class representing the action for all subclasses. * This information is useful because a language module might define * several grammar rule subclasses for the given class; this lets us * relate any instances of those various subclasses back to this same * canonical class for the action if necessary. */ #define DefineAction(name, baseClass...) \ class name##Action: ##baseClass \ baseActionClass = name##Action /* * Define a "system" action. System actions are meta-game commands, such * as SAVE and QUIT, that generally operate the user interface and are not * part of the game world. */ #define DefineSystemAction(name) \ DefineAction(name, SystemAction) /* * Define a concrete IAction, given the root name for the action. We'll * automatically generate a class with name XxxAction. */ #define DefineIAction(name) \ DefineAction(name, IAction) /* define a conversational IAction, such as Hello, Goodbye, Yes, No */ #define DefineConvIAction(name) \ DefineAction(name, ConvIAction) /* * Define a concrete TAction, given the root name for the action. We'll * automatically generate a class with name XxxAction, a verProp with name * verXxx, a checkProp with name checkXxx, and an actionProp with name * actionDobjXxx. */ #define DefineTAction(name) \ DefineTActionSub(name, TAction) /* * Define a concrete TAction with a specific base class. */ #define DefineTActionSub(name, cls) \ DefineAction(name, cls) \ verDobjProp = &verifyDobj##name \ remapDobjProp = &remapDobj##name \ preCondDobjProp = &preCondDobj##name \ checkDobjProp = &checkDobj##name \ actionDobjProp = &actionDobj##name \ /* * Define a concrete TIAction, given the root name for the action. We'll * automatically generate a class with name XxxAction, a verDobjProp with * name verDobjXxx, a verIobjProp with name verIobjxxx, a checkDobjProp * with name checkDobjXxx, a checkIobjProp with name checkIobjXxx, an * actionDobjProp with name actionDobjXxx, and an actionIobjProp with name * actionIobjXxx. */ #define DefineTIAction(name) \ DefineTIActionSub(name, TIAction) /* * Define a concrete TIAction with a specific base class. */ #define DefineTIActionSub(name, cls) \ DefineAction(name, cls) \ verDobjProp = &verifyDobj##name \ verIobjProp = &verifyIobj##name \ remapDobjProp = &remapDobj##name \ remapIobjProp = &remapIobj##name \ preCondDobjProp = &preCondDobj##name \ preCondIobjProp = &preCondIobj##name \ checkDobjProp = &checkDobj##name \ checkIobjProp = &checkIobj##name \ actionDobjProp = &actionDobj##name \ actionIobjProp = &actionIobj##name /* * Define a concrete TopicAction, given the root name for the action. */ #define DefineTopicAction(name) \ DefineAction(name, TopicAction) /* * Define a concrete TopicTAction, given the root name for the action. * 'which' gives the role the topic serves, for message generation purposes * - this should be one of the object role enums (DirectObject, * IndirectObject, etc) indicating which role the topic plays in the * action's grammar. */ #define BaseDefineTopicTAction(name, which, cls) \ DefineAction(name, cls) \ verDobjProp = &verifyDobj##name \ remapDobjProp = &remapDobj##name \ preCondDobjProp = &preCondDobj##name \ checkDobjProp = &checkDobj##name \ actionDobjProp = &actionDobj##name \ whichMessageTopic = which #define DefineTopicTAction(name, which) \ BaseDefineTopicTAction(name, which, TopicTAction) /* * Define a concrete ConvTopicTAction. This is just like defining a * TopicTAction, but defines the action using the ConvTopicTAction * subclass. */ #define DefineConvTopicTAction(name, which) \ BaseDefineTopicTAction(name, which, ConvTopicTAction) /* * Define a concrete LiteralAction, given the root name for the action. */ #define DefineLiteralAction(name) \ DefineAction(name, LiteralAction) /* * Define a concrete LiteralTAction, given the root name for the action. * 'which' gives the role the literal phrase serves, for message generation * purposes - this should be one of the object role enums (DirectObject, * IndirectObject, etc) indicating which role the topic plays in the * action's grammar. */ #define DefineLiteralTAction(name, which) \ DefineAction(name, LiteralTAction) \ verDobjProp = &verifyDobj##name \ remapDobjProp = &remapDobj##name \ preCondDobjProp = &preCondDobj##name \ checkDobjProp = &checkDobj##name \ actionDobjProp = &actionDobj##name \ whichMessageLiteral = which /* ------------------------------------------------------------------------ */ /* * Convenience macros for setting verify results. * * A verify routine can use these macros to set any number of verify * results. The VerifyResultList will keep only the result that gives the * strongest disapproval of the action, since the verification process is * by its nature only interested in the most negative result. * * These macros take advantage of the fact that we have a global * VerifyResultList object, which gathers the results of the verification, * so they can be used only in verify routines. The global verification * results object is valid during each verification invocation. */ /* * Command is logical. There's generally no need to add a logical result * explicitly, since a command is logical unless disapproved, but we * include this for completeness. * * We use 100 as the default likelihood, to leave plenty of room for * specific likelihood rankings both above and below the default level. */ #define logical \ (gVerifyResults.addResult(new LogicalVerifyResult(100, '', 100))) /* * Command is logical, and is ranked as indicated among logical results. * The 'rank' value is the likelihood rank; the higher the rank, the more * logical the command is. The rank is only used to establish an ordering * of the logical results; if a command also has illogical results, all of * the illogical results rank as less logical than the logical result with * the lowest likelihood. * * The 'key' value is an arbitrary string value associated with the * ranking. When two result lists both have a logical result object, and * both logical result objects have the same likelihood level, we'll check * the keys; if the keys match, we'll treat the two results as equivalent * and thus not distinguishing for disambiguation. This is useful because * it creates a crude multivariate space for ranking items for * disambiguation. * * For example, suppose we have a "put in" command, and we have two * possibilities for the target container. Neither is being held by the * actor, so they both have a result with a logical rank of 70 with a key * value of 'not held'. In addition, both are openable, and one is open * and the other is closed; the closed one has an additional result with a * logical rank of 80 and a key of 'not open'. Which do we choose? If we * looked only at the logical rankings, both would be equivalent, since * both have 70's as their most disapproving results. However, we see * that the two 70's were applied for the same reason - because they share * a common key - so we know this information isn't helpful for * disambiguation and can be ignored. So, we find that the closed one has * an 80, and the other has no other results (hence is by default logical * with rank 100), thus we take the '80' as the better one. * * Throughout the library, we use the following conventions: * * 150 = especially good fit: a good candidate for the action that is * especially likely to be used with the command. For example, a book is * especially suitable for a "read" command. * * 140 = similar to 150, but slightly less ideal a fit. We use this for * objects that are excellent fits, but for which we know certain other * objects might be better fits. * * 100 = default: a perfectly good candidate for the action, with nothing * that would make it illogical, but nothing that makes it especially * likely, either * * 80 = slightly less than perfect: a good candidate, but with some * temporary and correctable attribute that may make it less likely than * others. This is used for attributes that can be corrected: a container * needs to be opened for the action to succeed, but isn't currently open, * or an article of clothing cannot be worn for the action to proceeds, * but is currently being worn. * * 60/70 = slightly less than perfect, but with some attributes that can't * be readily corrected and which make the candidate potentially less * likely. These are used to make guesses about which might object might * be intended when several are logical but some might be more readily * used than others; for example, if putting an object into a container, a * container being held might rank higher than one not being held, so the * one not being held might be ranked a "70" likelihood. * * 50 = logical but not especially likely: an acceptable candidate for the * action, but probably not the best choice for the action. This is used * when an object can be used for the action, but would not be expected to * do anything special with the action. */ #define logicalRank(rank, key) \ (gVerifyResults.addResult(new LogicalVerifyResult(rank, key, 100))) /* * Logical ranking with specific list ordering. This is the same as a * regular logicalRank, but uses the given list ordering rather than the * default list ordering (100). */ #define logicalRankOrd(rank, key, ord) \ (gVerifyResults.addResult(new LogicalVerifyResult(rank, key, ord))) /* command is logical but dangerous */ #define dangerous \ (gVerifyResults.addResult(new DangerousVerifyResult(''))) /* * command is logical but non-obvious: the object should never be taken as * a default */ #define nonObvious \ (gVerifyResults.addResult(new NonObviousVerifyResult(''))) /* command is currently (but not always) illogical, for the given reason */ #define illogicalNow(msg, params...) \ (gVerifyResults.addResult(new IllogicalNowVerifyResult(msg, ##params))) /* illogical because things are already as the command would make them */ #define illogicalAlready(msg, params...) \ (gVerifyResults.addResult( \ new IllogicalAlreadyVerifyResult(msg, ##params))) /* command is always illogical */ #define illogical(msg, params...) \ (gVerifyResults.addResult(new IllogicalVerifyResult(msg, ##params))) /* illogical since we're trying to use something on itself (eg, PUT X IN X) */ #define illogicalSelf(msg, params...) \ (gVerifyResults.addResult(new IllogicalSelfVerifyResult(msg, ##params))) /* command is being performed on an inaccessible object */ #define inaccessible(msg, params...) \ (gVerifyResults.addResult(new InaccessibleVerifyResult(msg, ##params))) /* ------------------------------------------------------------------------ */ /* * Convenience macros for setting command results. */ /* * Set a default report for the current command. This report will be * shown unless a non-default report is issued, or if the default report * is to be suppressed (for example, because the command is being * performed implicitly as part of another command). * * Default reports should be used only for simple acknowledgments of the * command's successful completion - things like "Taken" or "Dropped" or * "Done." * * Default responses are suppressed for implicit commands because they are * redundant. When a command is performed implicitly, it is conventional * to mention the command being performed with a parenthetical: "(First * taking the book)". In such cases, a simple acknowledgment that the * command was successfully performed would add nothing of use but would * merely make the output more verbose, so we omit it. */ #define defaultReport(msg, params...) \ (gTranscript.addReport(new DefaultCommandReport(msg, ##params))) /* * Set a default descriptive report for the current command. This report * will be shown unless any other report is shown for the same command. * This differs from defaultReport in that we don't suppress a default * description for an implied command: we only suppress a default * description when there are other reports for the same command. * * The purpose of the default descriptive report is to generate reports * that say things along the lines that there's nothing special to * describe. For example: * * >x desk *. You see nothing special about it. * * >look in alcove *. There's nothing in the alcove. * * When there's nothing else to report, these default descriptions are * suitable as the full response to the command. However, they become * undesirable when we have other "status" information or related special * descriptions to display; consider: * * >x desk *. You see nothing special about it. *. Someone has jabbed a dagger into the top of the desk. * * >look in alcove *. There's nothing in the alcove. *. A vase is displayed in the alcove. * * >x bag *. You see nothing special about it. It's open, and it contains *. a red book, an iron key, and a brass key. * * In the first two examples above, we have special descriptions for * objects contained in the objects being described. The special * descriptions essentially contradict the default descriptions' claims * that there's nothing special to mention, and also render the default * descriptions unnecessary, in that it would be enough to show just the * special descriptions. The third example above is similar, but the * extra information is status information for the object being described * rather than a special description of a contained item; as with the * other examples, the generic default description is both contradictory * and unnecessary. * * Default description reports should ONLY be used for messages that have * the character of the examples above: generic descriptions that indicate * explicitly that there's nothing special to report. Messages that offer * any sort of descriptive detail should NOT be generated as default * description reports, because it is suitable and desirable to retain an * actual descriptive message even when other status information or * related special descriptions are also shown. */ #define defaultDescReport(msg, params...) \ (gTranscript.addReport(new DefaultDescCommandReport(msg, ##params))) /* * Add an cosmetic internal spacing report. This type of report is used * to show spacing (usually a paragraph break) within command output. * * The important thing about this report is that it doesn't trigger * suppression of any default reports. This is useful when internal * separation is added on speculation that there might be some reports to * separate, but without certainty that there will actually be any reports * shown; for example, when preparing to show a list of special * descriptions, we might add some spacing just in case some special * descriptions will be shown, saving the trouble of checking to see if * anything actually needs to be shown. */ #define cosmeticSpacingReport(msg, params...) \ (gTranscript.addReport(new CosmeticSpacingCommandReport(msg, ##params))) /* * Add an "extra" report. This is an incidental message that doesn't * affect the display of a default report. */ #define extraReport(msg, params...) \ (gTranscript.addReport(new ExtraCommandReport(msg, ##params))) /* * Set a main report for the current command. This report will be shown * as the main report from the command, overriding any default report for * the command. */ #define mainReport(msg, params...) \ (gTranscript.addReport(new MainCommandReport(msg, ##params))) /* * Set a "before" report for the current command. This report will be * shown before any main report, but will override any default report for * the command. */ #define reportBefore(msg, params...) \ (gTranscript.addReport(new BeforeCommandReport(msg, ##params))) /* * Set an "after" report for the current command. This report will be * shown after any main report, but will override any default report for * the command. */ #define reportAfter(msg, params...) \ (gTranscript.addReport(new AfterCommandReport(msg, ##params))) /* * Report failure. This overrides any default report, and marks the * command as having failed. * * A failure report should NOT indicate any state change - this is * important because failure reports are suppressed under some conditions * (for example, when an NPC is performing an implied command, and the * implied command fails, we don't show the failure report). If a failure * is accompanied by a state change, then a mainReport() should be made in * addition to the failure report - the main report should indicate the * state change. */ #define reportFailure(msg, params...) \ (gTranscript.addReport(new FailCommandReport(msg, ##params))) /* * Report a question. This shows a report that's really an interactive * prompt for more information, such as a prompt for a missing object. */ #define reportQuestion(msg, params...) \ (gTranscript.addReport(new QuestionCommandReport(msg, ##params))) /* ------------------------------------------------------------------------ */ /* * Thing message property overrides sometimes need to be selective about * the role of the object. These macros let you specify that a Thing * message override is only in effect when the Thing is the direct or * indirect object. When the object isn't in the specified role, the * message override will be ignored. * * For example, suppose you want to override an object's response to PUT * IN, but *only* when it's the indirect object of PUT IN - *not* when the * object is itself being put somewhere. To do this, you could give the * object a property like this: * *. notAContainerMsg = iobjMsg('The vase\'s opening is too small. ') * * This specifies that when the object is involved in a PUT IN command that * fails with the 'notAContainerMsg' message, the given message should be * used - but *only* when the object is the indirect object. */ #define dobjMsg(msg) (gDobj == self ? msg : nil) #define iobjMsg(msg) (gIobj == self ? msg : nil) /* ------------------------------------------------------------------------ */ /* * Try performing a command implicitly. The action is the root name of * the action, without the 'Action' suffix - we'll automatically add the * suffix. 'objs' is a varying-length list of the resolved objects in the * new action - the direct object, indirect object, and any others needed * for the action. */ #define tryImplicitAction(action, objs...) \ _tryImplicitAction(gIssuingActor, gActor, &announceImplicitAction, \ action##Action, ##objs) /* * Try performing a command implicitly, with a special descriptive * message. 'msgProp' gives the libMessages method to invoke the announce * the action, if the action is performed. If 'msgProp' is nil, no * message is displayed at all. * * 'action' is the root name of the action, without the 'Action' suffix * (we'll automatically add the suffix). 'objs' is a varying-length list * of the resolved objects - direct object, indirect object, and any * others needed. */ #define tryImplicitActionMsg(msgProp, action, objs...) \ _tryImplicitAction(gIssuingActor, gActor, msgProp, \ action##Action, ##objs) /* * Replace the current action with a new action. The new action will be * performed, and the original action will be terminated with 'exit'. * * 'action' is the root name of the action, without the 'Action' suffix * (we'll add the suffix automatically). 'objs' is a varying-length list * of the resolved objects - direct object, indirect object, etc. */ #define replaceAction(action, objs...) \ _replaceAction(gActor, action##Action, ##objs) /* * Replace the current action with a new action directed to a different * actor (but from the same issuing actor). */ #define replaceActorAction(actor, action, objs...) \ _replaceAction(actor, action##Action, ##objs) /* * Run a nested action. */ #define nestedAction(action, objs...) \ _nestedAction(nil, gActor, action##Action, ##objs) /* * Run a nested action targeted to a given actor. */ #define nestedActorAction(actor, action, objs...) \ _nestedAction(nil, actor, action##Action, ##objs) /* * Run a new action. This is a brand new action run as a separate turn, * not as a nested action. This doesn't replace any current action, but is * simply a separate action. * * This is normally used only for internal actions that are run between * other actions. This should not normally be used while another action is * being processed - use nestedAction for that instead. This should also * not normally be used to replace the current action with another - use * replaceAction for that. * * Returns a CommandTranscript object, which provides information on the * results of the action. */ #define newAction(action, objs...) \ _newAction(CommandTranscript, nil, gActor, action##Action, ##objs) /* run a new action with a specific actor */ #define newActorAction(actor, action, objs...) \ _newAction(CommandTranscript, nil, actor, action##Action, ##objs) /* * Ask for a direct object and retry the command using the single-object * phrasing. This can be used in the action() routine for a no-object * command to ask for the missing direct object. * * In many cases, there is simply no grammar rule for a zero-object form * of a verb; in such cases, this macro is not needed, since the missing * object is handled via the grammar. However, for some actions, it is * desirable to allow the zero-object phrasing some of the time, but * require the direct-object phrasing other times. This macro exists for * these cases, because it allows the intransitive version of the action * to decide, on a case-by-case basis, whether to process the no-object * form of the command or to prompt for a direct object. * * newAction is the root name (without the Action suffix) of the * transitive action to execute. For example, if we're processing a plain * "in" command, we could use askForDobj(Enter) to ask for a direct object * for the transitive "enter" phrasing. */ #define askForDobj(newAction) \ (newAction##Action.retryWithMissingDobj(gAction, ResolveAsker)) /* * Ask for an indirect object and retry the command using the two-object * phrasing. This can be used in the action() routine of a single-object * command to ask for the missing indirect object. * * In many cases, there is simply no grammar rule for a single-object form * of a verb; in such cases, this macro is not needed, since the missing * object is handled via the grammar. However, for some actions, it is * desirable to allow the single-object phrasing some of the time, but * require the two-object phrasing other times. This macro exists for * these cases, because it allows the action() routine to decide, on a * case-by-case basis, whether to process the single-object form of the * command or to prompt for an indirect object. * * newAction is the root name (without the Action suffix) of the * two-object form of the action. For example, if we're processing a * single-object "unlock" command, we would use askForIobj(UnlockWith) to * ask for an indirect object for the "unlock with" two-object phrasing. */ #define askForIobj(newAction) \ (newAction##Action.retryWithMissingIobj(gAction, ResolveAsker)) /* * Ask for a literal phrase and retry the command using the two-object * phrasing. This is analogous to askForDobj() and askForIobj(), but for * literal phrases; we effectively convert a TAction into a * LiteralTAction. */ #define askForLiteral(newAction) \ (newAction##Action.retryWithMissingLiteral(gAction)) /* * Ask for a topic phrase and retry the command using the two-object * phrasing. */ #define askForTopic(newAction) \ (newAction##Action.retryWithMissingTopic(gAction)) /* ------------------------------------------------------------------------ */ /* * Command interruption signal macros. */ /* a concise macro to throw an ExitSignal */ #define exit throw new ExitSignal() /* a concise macro to throw an ExitActionSignal */ #define exitAction throw new ExitActionSignal() /* a concise macro to throw an AbortImplicitSignal */ #define abortImplicit throw new AbortImplicitSignal() /* ------------------------------------------------------------------------ */ /* * Flags for LOOK AROUND styles */ /* show the room name as part of the description */ #define LookRoomName 0x0001 /* show the room's long desription (the roomDesc) */ #define LookRoomDesc 0x0002 /* show the non-portable items (the specialDesc's) */ #define LookListSpecials 0x0004 /* show the portable items */ #define LookListPortables 0x0008 /* ------------------------------------------------------------------------ */ /* * Template for multi-location objects. To put a MultiLoc object in * several initial locations, simply use a template giving the list of * locations. */ MultiLoc template [locationList]; /* ------------------------------------------------------------------------ */ /* * Templates for style tags */ StyleTag template 'tagName' 'openText'? 'closeText'?; /* ------------------------------------------------------------------------ */ /* * A template for footnotes - all we usually need to define in a footnote * is its descriptive text, so this makes it easy to define one. */ Footnote template "desc"; /* footnote status levels */ enum FootnotesOff, FootnotesMedium, FootnotesFull; /* ------------------------------------------------------------------------ */ /* * An achievement defines its descriptive text. It can also optionally * define the number of points it awards. */ Achievement template +points? "desc"; /* ------------------------------------------------------------------------ */ /* * An event list takes a list of strings, objects, and/or functions. */ EventList template [eventList]; /* * A shuffled event list with two lists - the first list is the sequential * initial list, fired in the exact order specified; and the second is the * random list, with the events that occur in shuffled order after we * exhaust the initial list. */ ShuffledEventList template [firstEvents] [eventList]; /* a synchronized event list takes its state from another list */ SyncEventList template ->masterObject inherited; /* low-level shuffled list */ ShuffledList template [valueList]; /* ------------------------------------------------------------------------ */ /* * Define a template for the Tip class. */ Tip template "desc"; /* ------------------------------------------------------------------------ */ /* * Definitions for the menu system */ /* * The indices for the key values used to navigate menus, which are held * in the keyList array of MenuItems. */ #define M_QUIT 1 #define M_PREV 2 #define M_UP 3 #define M_DOWN 4 #define M_SEL 5 /* some templates for defining menu items */ MenuItem template 'title' 'heading'?; MenuTopicItem template 'title' 'heading'? [menuContents]; MenuLongTopicItem template 'title' 'heading'? 'menuContents'; /* templates for hint system objects */ Goal template ->closeWhenAchieved? 'title' 'heading'? [menuContents]; Hint template 'hintText' [referencedGoals]?; /* ------------------------------------------------------------------------ */ /* * Templates for topic database entries. */ /* * A TopicEntry can be defined with an optional score, followed by the * match criteria (which can be either a single matching object, a list of * matching objects, or a regular expression pattern string), followed by * the optional response text (which can be given either as a double-quoted * string or as a list of single-quoted strings to use as an EventList). */ TopicEntry template +matchScore? @matchObj | [matchObj] | 'matchPattern' "topicResponse" | [eventList] ?; /* a ShuffledEventList version of the above */ TopicEntry template +matchScore? @matchObj | [matchObj] | 'matchPattern' [firstEvents] [eventList]; /* we can also include *both* the match object/list *and* pattern */ TopicEntry template +matchScore? @matchObj | [matchObj] 'matchPattern' "topicResponse" | [eventList] ?; /* a ShuffledEventList version of the above */ TopicEntry template +matchScore? @matchObj | [matchObj] 'matchPattern' [firstEvents] [eventList]; /* miscellanous topics just specify the response text or list */ MiscTopic template "topicResponse" | [eventList]; MiscTopic template [firstEvents] [eventList]; /* * A SpecialTopic takes a keyword list or a regular expression instead of * the regular match criteria. It also takes a suggestion name string and * the normal response text. There's no need for a score in a special * topic, since these are unique. */ SpecialTopic template 'name' [keywordList] | 'matchPat' "topicResponse" | [eventList] ?; /* a ShuffledEventList version of the above */ SpecialTopic template 'name' [keywordList] | 'matchPat' [firstEvents] [eventList]; /* default topics just specify the response text */ DefaultTopic template "topicResponse" | [eventList]; DefaultTopic template [firstEvents] [eventList]; /* alternative topics just specify the response string or strings */ AltTopic template "topicResponse" | [eventList]; AltTopic template [firstEvents] [eventList]; /* a TopicGroup can specify its score adjustment */ TopicGroup template +matchScoreAdjustment; /* a conversation node need a name */ ConvNode template 'name'; /* * End-of-conversation reason codes */ enum endConvBye; /* player typed GOODBYE */ enum endConvTravel; /* the other character is trying to travel away */ enum endConvBoredom; /* our attentionSpan has been exhausted */ enum endConvActor; /* the NPC itself (not the player) is saying GOODBYE */ /* * Special result code for Actor.canEndConversation() - this indicates that * the other actor said something to force the conversation to keep going. */ enum blockEndConv; /* ------------------------------------------------------------------------ */ /* * Conversation manager macros */ /* has a topic key been revealed through <.reveal>? */ #define gRevealed(key) (conversationManager.revealedNameTab[key] != nil) /* reveal a topic key, as though through <.reveal> */ #define gReveal(key) (conversationManager.setRevealed(key)) /* mark a Topic/Thing as known/seen by the player character */ #define gSetKnown(obj) (gPlayerChar.setKnowsAbout(obj)) #define gSetSeen(obj) (gPlayerChar.setHasSeen(obj)) /* ------------------------------------------------------------------------ */ /* * For compatibility with versions before 3.1.1, define * openableContentsLister as a synonym for openableDescContentsLister. The * former was renamed to the latter in 3.1.1 because the original name was * inconsistent with the corresponding listers for other classes. In * principle, openableContentsLister is meant to be the 'contentsLister' * (for displaying the openable's contents in room descriptions, etc) for * an Openable, while openableDescContentsLister is its * 'descContentsLister' (for displaying the openable's contents in its own * EXAMINE description). Fortunately we don't have a need for a special * contentsLister for Openable, so we can avoid breaking existing code by * mapping the old name to the new name. */ #define openableContentsLister openableDescContentsLister #endif /* ADV3_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/numbers.t�������������������������������������������������������������0000644�0001750�0000144�00000006133�10472354603�016774� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright 2000, 2006 Michael J. Roberts * * TADS 3 library - number handling. This module provides utility * functions for converting numbers to strings in various formats. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Format a number as a binary string. */ intToBinary(val) { local result; local outpos; /* if the value is zero, the binary representation is simply '0' */ if (val == 0) return '0'; /* allocate a vector to store the characters of the result */ result = new Vector(33).fillValue(nil, 1, 33); /* * Fill up the vector with 1's and 0's, working from the lowest-order * bit to the highest-order bit. On each iteration, we'll pull out * the low-order bit, add it to the result string, and then shift the * value right one bit so that the next higher-order bit becomes the * low-order bit in the value. */ for (outpos = 34 ; val != 0 ; val >>= 1) { /* * Add the next bit. Add it at the end of the string so the * final result reads left-to-right, high-to-low. Note that * Unicode value 0x30 is the digit '0', and Unicode value 0x31 is * the digit '1'. We build the result as a vector of Unicode * values for efficiency, so that we don't have to repeatedly * allocate partial strings. */ result[--outpos] = ((val & 1) == 0 ? 0x30 : 0x31); } /* convert the vector of Unicode characters to a string */ return makeString(result.toList(outpos, 34 - outpos)); } /* ------------------------------------------------------------------------ */ /* * Convert an integer number to Roman numerals. Returns a string with * the Roman numeral format. This can only accept numbers in the range * 1 to 4999; returns nil for anything outside of this range. */ intToRoman(val) { local str; local info = [ /* numeral value / corresponding string */ 1000, 'M', 900, 'CM', 500, 'D', 400, 'CD', 100, 'C', 90, 'XC', 50, 'L', 40, 'XL', 10, 'X', 9, 'IX', 5, 'V', 4, 'IV', 1, 'I' ]; local i; /* if the value is outside of the legal range, fail immediately */ if (val < 1 || val > 4999) return nil; /* * iterate over the specifiers and apply each one as many times as * possible */ for (str = '', i = 1 ; val != 0 ; ) { /* * If we're greater than the current specifier, apply it; * otherwise, move on to the next specifier. */ if (val >= info[i]) { /* add this specifier's roman numeral into the result */ str += info[i+1]; /* subtract the corresponding value */ val -= info[i]; } else { /* move to the next specifier */ i += 2; } } /* return the result */ return str; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/modid.t���������������������������������������������������������������0000644�0001750�0000144�00000064604�12145504453�016423� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - module ID's * * This module defines a framework for "module ID's." The module ID * mechanism allows library modules to identify themselves. The module * ID framework is modular, in that several libraries can be linked * together without any prior knowledge of one another, and the module ID * framework will be able to find all of their ID's. */ /* include the library header */ #include "adv3.h" /* we also require file access */ #include <file.h> /* ------------------------------------------------------------------------ */ /* * Module ID. Each library add-in can define one of these, so that the * "credits" command and the like can automatically show the version of * each library module included in the finished game, without the game's * author having to compile a list of the module versions manually. * * An easy way to implement a CREDITS command is to define a ModuleID * object for the game itself, and override the showCredit() method to * display the text of the game's credits. The module object for the * game itself should usually have a listingOrder of 1, because the * author usually will want the game's information to be displayed first * in any listing that shows each included library module (such as the * VERSION command's output). */ class ModuleID: object /* my name */ name = '' /* the "byline" for the module, in plain text and HTML versions */ byline = '' htmlByline = '' /* my version number string */ version = '' /* * Show my library credit. By default won't show anything. * Libraries should generally not override this, because we want to * leave it up to the author to determine how the credits are * displayed. If a library overrides this, then the author won't be * able to control the formatting of the library credit, which is * undesirable. */ showCredit() { } /* * Show version information. By default, we show our name and * version number, then start a new line. The main game's module ID * should generally override this to show an appropriate version * message for the game, and any library add-ins that want to * display their version information can override this to do so. */ showVersion() { gLibMessages.showVersion(name, version); "\n"; } /* * Show the "about this game" information. By default, we show * nothing here. Typically, only the game's module ID object will * override this; in the game's module ID object, this method should * display any desired background information about the game that * the author wants the player to see on typing the ABOUT command. * * The ABOUT command conventionally displays information about the * game and its author - the kind of thing you'd find in an author's * notes section in a book - along with any special instructions to * the player, such as notes on unusual command syntax. Information * that players will find especially helpful include: * * - A list of any unusual command phrasings that the game uses. * Ideally, you will disclose here every verb that's required to * complete the game, beyond the basic set common to most games * (LOOK, INVENTORY, NORTH, SOUTH, TAKE, DROP, PUT IN, etc). By * disclosing every necessary verb and phrasing, you can be certain * to avoid "guess the verb" puzzles. (Note that it's possible to * disclose every *required* verb without disclosing every * *accepted* verb - some verbs might be so suggestive of a * particular puzzle solution that you wouldn't want to disclose * them, but as long as you disclose less suggestive alternatives * that can be used to solve the same puzzles, you have a valid * defense against accusations of using "guess the verb" puzzles.) * * - A quick overview of the NPC conversation system, if any. * Conversation systems have been slowly evolving as authors * experiment with different styles, and at least three or four * different conventions have emerged. The default that experienced * players will expect is the traditional ASK/TELL system, so it's * especially important to mention your system if you're using * something else. * * - An indication of the "cruelty" level of the game. In * particular, many experienced players find it helpful to know from * the outset how careful they have to be about saving positions * throughout play, so it's helpful to point out whether or not it's * possible for the player character to be killed; whether it's * possible to get into situations where the game becomes * "unwinnable"; and if the game can become unwinnable, whether or * not this will become immediately clear. The kindest games never * kill the PC and are always winnable, no matter what actions the * player takes; it's never necessary to save these games except to * suspend a session for later resumption. The cruelest games kill * the PC without warning (although if they offer an UNDO command * from a "death" prompt, then even this doesn't constitute true * cruelty), and can become unwinnable in ways that aren't readily * and immediately apparent to the player, which means that the * player could proceed for quite some time (and thus invest * substantial effort) after the game is already lost. * * - A description of any special status line displays or other * on-screen information whose meaning might not be immediately * apparent. */ showAbout() { } /* * My listing order. When we compile a list of modules, we'll sort * the modules first by ascending listing order; any modules with * the same listing order will be sorted alphabetically by name with * respect to the other modules with the same listing order. * * The value 1 is reserved for the game's own ID object. Note that * the TADS 3 library defines a module ID with listing order 50, * which is chosen so that the main library credit will appear after * the game credits but before any extension credits using the * default order value 100 that we define here. Extensions are * free, however, to use a number lower than 5 if they wish to * appear before the main library credit. */ listingOrder = 100 /* * get a list of all of the modules that are part of the game, * sorted in listing order */ getModuleList() { local lst; /* compile a list of all of the modules */ lst = new Vector(10); forEachInstance(ModuleID, { obj: lst.append(obj) }); lst = lst.toList(); /* * sort the list by listing order (and alphabetically by name * where listing orders are the same) */ lst = lst.sort(SortAsc, function(a, b) { /* if the listings order differ, sort by listing order */ if (a.listingOrder != b.listingOrder) return a.listingOrder - b.listingOrder; /* the listing orders are the same; sort by name */ if (a.name < b.name) return -1; else if (a.name > b.name) return 1; else return 0; }); /* return the sorted list */ return lst; } ; /* ------------------------------------------------------------------------ */ /* * A module ID with metadata. During pre-initialization, we'll * automatically write out a file with the metadata for each of these * objects. This is an abstract base class; a subclass must be created * for each specific metadata format. */ class MetadataModuleID: ModuleID, PreinitObject /* execute pre-initialization */ execute() { /* write out our metadata */ writeMetadataFile(); } /* * write our metadata file - this must be overridden by each * subclass to carry out the specific steps needed to create and * write the metadata file in the appropriate format for the * subclass */ writeMetadataFile() { } ; /* * A module ID with GameInfo metadata. The GameInfo metadata format is * the standard TADS format for descriptive data about the game. The * usual way to use GameInfo metadata is to create a file called * "gameinfo.txt" for a game, then embed this file directly in the * game's .t3 file using the TADS 3 resource bundler (t3res). Once the * gameinfo.txt is embedded in the .t3 file, tools will be able to read * the game's descriptive data directly from the .t3 file. For example, * HTML TADS on Windows can read the information into its Game Chest, * which allows the interpreter to show the full name of the game, the * author, and a blurb describing the game, among other things. */ class GameInfoModuleID: MetadataModuleID /* * The IFID - this is a UUID uniquely identifying the game, using the * standard UUID format (xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where * each 'x' is a hexadecimal digit). You should pick an IFID when * you start each game project, and then keep the same IFID * throughout the game's entire existence, *including* version * updates. Each new version release of the same game - even major * new versions - should use the same IFID, so that the versions can * all be related to one another as the same game. * * If the game has multiple IFIDs, list them here, separated by * commas. You should NOT *intentionally* create multiple IFIDs for * your game; once you've created an IFID, it should be the unique * and permanent identifier for the game. In particular, do NOT * create a new IFID for a new version: the whole series of releases * throughout a game's lifetime should be identified by a single * IFID, so that archivists will know that the versions are all * incarnations of the same work. * * The reason that multiple IFIDs are allowed at all is that many * older games were not assigned explicit UUID-style IFIDs when * released. In such cases, the game has an "implied" IFID based on * an MD5 hash of the compiled game file's contents. Every release * that doesn't contain an explicit IFID will therefore have a * different implied IFID. So, for example, if you've already * released versions 1, 2, and 3 of your game, and you didn't assign * explicit IFID values to those releases, each version will have a * different implied IFID. When you release version 4, you should * NOT assign a new UUID-style IFID. Instead, in the IFID string * here, list ALL THREE of the implied IFIDs from the past releases. * Each of the three IFIDs counts from now on as an IFID for the * work, for all versions collectively. (By placing the list of * IFIDs in version 4, you prevent version 4 from adding yet another * implied IFID of its own: the explicit IFID list supersedes the * implied IFID.) See the Babel spec for more information, and for * instructions on how to calculate the implied IFID for a TADS game * that was released without a UUID-style IFID. */ IFID = '' /* * The game's headline. It's become an IF tradition to use a * quasi-subtitle of the sort that Infocom used, of the form "An * Interactive Mystery." This can be used to define that subtitle. */ headline = '' /* * If this game is part of a series, such as a trilogy, these can be * used to identify the name of the series and the position in the * series. The series name should be something like "The Enchanter * Trilogy"; the series number, if provided, should be a simple * integer string ('1', '2', etc) giving the position in the series. * Note that the series number isn't required even if a series name * is specified, since some series are just groups of works without * any particular ordering. */ seriesName = '' seriesNumber = '' /* * The genre of the game. Some games don't fit any particular genre, * and some authors just don't like the idea of having to pigeonhole * their games, so feel free to leave it out. If there's a good fit * to a well-established genre, though, you can specify it here. We * recommend you keep this short - one word, maybe two - and use a * genre name that's generally recognized as such. You might want to * use Baf's Guide as a reference (http://www.wurb.com/if/genre). */ genreName = '' /* * The forgiveness level, according to the Zarfian scale propounded * by Andrew Plotkin on rec.arts.int-fiction. This must be one of * these terms, using the exact capitalization shown: Merciful, * Polite, Tough, Nasty, Cruel. */ forgivenessLevel = '' /* * The names and email addresses of the authors, in GameInfo format. * This list must use the following format: * * author one <email>; author two <email> <email>; ... * * In other words, list the first author's name, followed by one or * more email addresses, in angle brackets, for the first author. * If more than one author is to be listed, add a semicolon, * followed by the name of the second author, followed by the second * author's email address or addresses, enclosing each in angle * brackets. Repeat as needed for additional authors. The list * does not need to end with a semicolon; semicolons are merely used * to separate entries. */ authorEmail = '' /* * The game's web site, if any. If specified, this must be an * absolute URL with http protocol - that is, it must be of the form * "http://mydomain.com/...". */ gameUrl = '' /* * Descriptive text for the game, in plain text format. This is a * short description that can be used, for example, in a catalog of * games. This should be a couple of sentences or so. */ desc = '' /* * Descriptive text for the game, as an HTML fragment. This should * have the same information as the 'desc', but this version can use * HTML markups (including tags and character entities) to embellish * the display of the text. Any HTML markups should be "in-line" * body elements only, not "block" or head elements, so that this * text can be inserted into a larger HTML document. For example, * markups like <i> and <b> are fine, but <p> and <table> should not * be used. */ htmlDesc = '' /* * The release date. By default, we compute this statically to be * today's date. This means this will be set to the date of * compilation. If the game wishes to override this, note that the * GameInfo format requires this to be of the form YYYY-MM-DD. For * example, December 9, 2001 would be '2001-12-09'. */ releaseDate = static getGameInfoToday() /* * The date of first publication. This can be just a year in YYYY * format, or a full YYYY-MM-DD date. This is the original release * date of the original version of the game, which is often of * interest to archivists. This should *not* be updated when a new * release is made - it's always the date of *original* publication. */ firstPublished = '' /* * The language in which this game's text is written. This is the * RFC3066 language code for the main language of the work. For * example, games written in US English would use 'en-US', while * games written in British English would use 'en-GB'. Note that * each language-specific library module should use 'modify' to set * this to the default for the library. */ languageCode = '' /* * The license type for this game. Most text IF games these days * are released as freeware, so we use this as the default. The * GameInfo metadata format defines several other standard license * types, including Public Domain, Shareware, Commercial Demo, * Commercial, and Other. Authors should change this if they plan * to release under a licensing model other than freeware. * * Note that the GameInfo metadata format documentation explicitly * states that the license type indicated here is advisory only and * cannot be considered definitive. This means that this setting * does not take away any of the author's rights to set specific * license terms. Even so, we recommend that you pick an * appropriate value here to avoid any confusion. */ licenseType = 'Freeware' /* * The copying rules for this game. Most text games these days are * released as freeware with minimal restrictions on copying, so we * use a default of "nominal cost only." Other values defined in the * GameInfo format include Prohibited, No Restrictions, No-Cost Only, * At-Cost Only, and Other. A modifier indicates whether or not the * game may be included in compilations (such as those "10,001 great * games" CD-R's that people like to sell on auction sites); we * indicate that inclusion in compilations is allowed by default. * You can change this to "Compilations Prohibited" if you prefer not * to allow your game to be distributed in that fashion. * * Note that the restrictions specified here aren't enforced by any * sort of copy-protection or DRM (digital rights management) * technology. This information is entirely for the benefit of * conscientious users who want to abide by your wishes and thus need * to know what your wishes are. * * The GameInfo bundle is mostly for the benefit of software that can * extract the information from the compiled game. So, we recommend * that you also put a full notice and explanation of your license * restrictions somewhere that users can easily find it, such as in a * separate LICENSE.TXT file that you distribute with your game, or * in the text of the game itself (displayed by a LICENSE or * COPYRIGHT command, for example). */ copyingRules = 'Nominal cost only; compilations allowed' /* * The recommended "presentation profile" for the game. 'Default' * means that the interpreter's default profile should be used. * (Some interpreters let the user select which profile to use as the * default, in which case 'Default' means we'll use that profile.) */ presentationProfile = 'Default' /* write our metadata file */ writeMetadataFile() { local f; /* * open the file - note that the GameInfo.txt resource is * required to be encoded in UTF-8, so open the file with the * UTF-8 character set */ f = File.openTextFile(gameInfoFilename, FileAccessWrite, 'utf-8'); /* scan our list of metadata keys and write each one */ for (local i = 1, local len = metadataKeys.length() ; i + 1 <= len ; i += 2) { local key; local prop; local val; /* get the key name and value property for this entry */ key = metadataKeys[i]; prop = metadataKeys[i+1]; val = self.(prop); /* turn any '\ ' characters into ' ' characters */ val = val.findReplace('\ ', ' ', ReplaceAll); /* write out this key if there's a value defined for it */ if (val != nil && val != '') f.writeFile(key + ': ' + val + '\n'); } /* done with the file - close it */ f.closeFile(); /* remember the primary IFID in the globals */ libGlobal.IFID = rexReplace([',.*$', '<space>+'], IFID, ''); } /* * the GameInfo filename - by default, we write the standard * gameinfo.txt file */ gameInfoFilename = 'GameInfo.txt' /* * The metadata key mappings. This is a list of key/property pairs. * The key in each pair is a string giving a standard GameInfo key * name, and the property gives the property (of self) that we * evaluate to get the string value for that key. */ metadataKeys = [ 'IFID', &IFID, 'Name', &name, 'Headline', &headline, 'Byline', &byline, 'HtmlByline', &htmlByline, 'AuthorEmail', &authorEmail, 'Url', &gameUrl, 'Desc', &desc, 'HtmlDesc', &htmlDesc, 'Version', &version, 'ReleaseDate', &releaseDate, 'FirstPublished', &firstPublished, 'Series', &seriesName, 'SeriesNumber', &seriesNumber, 'Genre', &genreName, 'Forgiveness', &forgivenessLevel, 'Language', &languageCode, 'LicenseType', &licenseType, 'CopyingRules', ©ingRules, 'PresentationProfile', &presentationProfile ] /* * get today's date, using the GameInfo standard date format * (YYYY-MM-DD) */ getGameInfoToday() { local dt; local mm, dd; /* get the current date */ dt = getTime(GetTimeDateAndTime); /* get the month, and add a leading zero if it's only one digit */ mm = (dt[2] < 10 ? '0' : '') + dt[2]; /* get the day, and add a leading zero if it's only one digit */ dd = (dt[3] < 10 ? '0' : '') + dt[3]; /* build and return the full YYYY-MM-DD date string */ return toString(dt[1]) + '-' + mm + '-' + dd; } ; /* ------------------------------------------------------------------------ */ /* * Base class for the game's module ID. This merely sets the listing * order to 1 so that the game's credit is listed first. Normally, * exactly one GameID object, called 'versionInfo', is defined in a game, * to provide the game's identifying information. * * Note that this class is based on GameInfoModuleID, so the library will * automatically write out a gameinfo.txt file based on this object's * settings. For full GameInfo data, the game should minimally define the * following properties (see GameInfoModuleID and ModuleID for details on * these properties): * *. IFID - a random 32-digit hex number to uniquely identify the game; *. you can generate one at http://www.tads.org/ifidgen/ifidgen *. name - the name of the game *. byline - the main author credit: "by so and so" *. htmlByline - the main author credit as an HTML fragment *. authorEmail - the authors' names and email addresses (in GameInfo format) *. desc - a short blurb describing the game, in plain text format *. htmlDesc - the descriptive blurb as an HTML ragment *. version - the game's version string * * In addition, you can override the following settings if you don't like * the defaults inherited from GameInfoModuleID: * *. releaseDate - the release date string (YYYY-MM-DD) *. licenseType - freeware, shareware, etc. *. copyingRules - summary rules on copying *. presentationProfile - Multimedia, Plain Text */ class GameID: GameInfoModuleID /* always list the game's credits before any library credits */ listingOrder = 1 /* * Show the game's credits. By default, we'll just show our name and * by-line. * * Typically, authors will want to override this to display the full * credits for the game. Most authors like to show the author or * authors, along with notes of thanks to important contributors. * * Note that libraries generally will not show anything automatically * in the credits, to allow the author full control over the * formatting of the credits. Authors are encouraged to give credit * where it's due for any libraries they use. */ showCredit() { /* by default, just show the game's name and by-line */ gLibMessages.showCredit(name, htmlByline); } /* * show a blank line after the game's version information, to make * it stand apart from the list of library and VM version numbers */ showVersion() { inherited(); "\b"; } ; /* ------------------------------------------------------------------------ */ /* * The main TADS 3 library ID. */ moduleAdv3: ModuleID name = 'TADS 3 Library' byline = 'by Michael J.\ Roberts' htmlByline = 'by <a href="mailto:mjr_@hotmail.com">' + 'Michael J.\ Roberts</a>' version = '3.1.3' /* * We use a listing order of 50 so that, if all of the other credits * use the defaults, we appear after the game's own credits * (conventionally at listing order 1) and before any extension * credits (which inherit the default order 100), but so that * there's room for extensions that want to appear before us, or * after us but before any default-ordered extensions. */ listingOrder = 50 ; /* ------------------------------------------------------------------------ */ /* * An ID module not for the library but for the T3 VM itself. This * doesn't display any credit information, but displays version number * information for the VM so that the "version" command shows what * version of the interpreter is in use. */ ModuleID showVersion() { local vsn = t3GetVMVsn(); /* * show the version information - note that we must decompose * the version number into the standard 3-part dotted string */ gLibMessages.showVersion('T3 VM (' + t3GetVMID() + ')', '' + (vsn >> 16) + '.' + ((vsn >> 8) & 0xFF) + '.' + (vsn & 0xFF)); "\n"; } /* * Use a very high listing order so that we're the last thing shown. */ listingOrder = 10000 ; ����������������������������������������������������������������������������������������������������������������������������frobtads-1.2.3/tads3/lib/adv3/misc.t����������������������������������������������������������������0000644�0001750�0000144�00000266406�11562747175�016302� 0����������������������������������������������������������������������������������������������������ustar �realnc��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - miscellaneous definitions * * This module contains miscellaneous definitions that don't have a * natural grouping with any larger modules, and which aren't complex * enough to justify modules of their own. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * When a call is made to a property not defined or inherited by the * target object, the system will automatically invoke this method. The * method will be invoked with a property pointer as its first argument, * and the original arguments as the remaining arguments. The first * argument gives the property that was invoked and not defined by the * object. A typical definition in an object would look like this: * * propNotDefined(prop, [args]) { ... } * * If this method is not defined by the object, the system simply * returns nil as the value of the undefined property evaluation or * method invocation. */ property propNotDefined; export propNotDefined; /* ------------------------------------------------------------------------ */ /* * We refer to some properties defined primarily in score.t - that's an * optional module, though, so make sure the compiler has heard of these. */ property calcMaxScore, runScoreNotifier; /* ------------------------------------------------------------------------ */ /* * The library base class for the gameMain object. * * Each game MUST define an object called 'gameMain' to define how the * game starts up. You can use GameMainDef as the base class of your * 'gameMain' object, in which case the only thing you're required to * specify in your object is the 'initialPlayerChar' property - you can * inherit everything else from the GameMainDef class if you don't * require any further customizations. */ class GameMainDef: object /* * The initial player character. Each game's 'gameMain' object MUST * define this to refer to the Actor object that serves as the * initial player character. */ initialPlayerChar = nil /* * Show the game's introduction. This routine is called by the * default newGame() just before entering the main command loop. The * command loop starts off by showing the initial room description, * so there's no need to do that here. * * Most games will want to override this, to show a prologue message * setting up the game's initial situation for the player. We don't * show anything by default. */ showIntro() { } /* * Show the "goodbye" message. This is called after the main command * loop terminates. * * We don't show anything by default. If you want to show a "thanks * for playing" type of message as the game exits, override this * routine with the desired text. */ showGoodbye() { } /* * Begin a new game. This default implementation shows the * introductory message, calls the main command loop, and finally * shows the goodbye message. * * You can override this routine if you want to customize the startup * protocol. For example, if you want to create a pre-game options * menu, you could override this routine to show the list of options * and process the user's input. If you need only to customize the * introduction and goodbye messages, you can simply override * showIntro() and showGoodbye() instead. */ newGame() { /* * Show the statusline before we display our introductory. This * will help minimize redrawing - if we waited until after * displaying some text, we might have to redraw some of the * screen to rearrange things for the new screen area taken up by * the status line, which could be visible to the user. By * setting up the status line first, we'll probably have less to * redraw because we won't have anything on the screen yet when * figuring the layout. */ statusLine.showStatusLine(); /* show the introduction */ showIntro(); /* run the game, showing the initial location's full description */ runGame(true); /* show the end-of-game message */ showGoodbye(); } /* * Restore a game and start it running. This is invoked when the * user launches the interpreter using a saved game file; for * example, on a Macintosh, this happens when the user double-clicks * on a saved game file on the desktop. * * This default implementation bypasses any normal introduction * messages: we simply restore the game file if possible, and * immediately start the game's main command loop. Most games won't * need to override this, but you can if you need some special effect * in the restore-at-startup case. */ restoreAndRunGame(filename) { local succ; /* mention that we're about to restore the saved position */ gLibMessages.noteMainRestore(); /* try restoring it */ succ = RestoreAction.startupRestore(filename); /* show a blank line after the restore result message */ "<.p>"; /* if we were successful, run the game */ if (succ) { /* * Run the command loop. There's no need to show the room * description, since the RESTORE action will have already * done so. */ runGame(nil); /* show the end-of-game message */ showGoodbye(); } } /* * Set the interpreter window title, if applicable to the local * platform. This simply displays a <TITLE> tag to set the title to * the string found in the versionInfo object. */ setGameTitle() { /* write the <TITLE> tag with the game's name */ "<title><<versionInfo.name>>"; } /* * Set up the HTML-mode about-box. By default, this does nothing. * Games can use this routine to show an tag, if desired, * to set up the contents of an about-box for HTML TADS platforms. * * Note that an tag must be re-initialized each time the * main game window is cleared, so this routine should be called * again after any call to clearScreen(). */ setAboutBox() { /* we don't show any about-box by default */ } /* * Build a saved game metadata table. This returns a LookupTable * containing string key/value pairs that are stored in saved game * files, providing descriptive information that can be displayed to * the user when browsing a collection of save files. This is called * each time we execute a SAVE command, so that we store the current * context of the game. * * Some interpreters display information from this table when * presenting the user with a list of files for RESTORE. The * contents of the table are intentionally open-ended to allow for * future extensions, but at the moment, the following keys are * specifically defined (note that capitalization must be exact): * * UserDesc - descriptive text entered by the user (this should * simply be the contents of the 'userDesc' parameter). This is * treated as ordinary plain text (i.e., no HTML or other markups are * interpreted in this text). * * AutoDesc - descriptive text generated by the game to describe the * saved position. This text can contain the simple HTML markups * .., .., and
for formatting. * * Return nil if you don't want to save any metadata information. * * 'userDesc' is an optional string entered by the user via the Save * Game dialog. Some interpreters let the user enter a description * for a saved game via the file selector dialog; the descriptive * text is separate from the filename, and is intended to let the * user enter a more free-form description than would be allowed in a * filename. This text, if any, is passed to use via the 'userDesc' * parameter. */ getSaveDesc(userDesc) { /* create the lookup table */ local t = new LookupTable(); /* store the user description, if provided */ if (userDesc != nil) t['UserDesc'] = userDesc; /* start our auto description with the current room name */ desc = gPlayerChar.getLookAroundName() + '; '; /* if we're keeping score, include the score */ if (libGlobal.scoreObj != nil) desc += toString(libGlobal.scoreObj.totalScore) + ' points in '; /* add the number of turns so far */ desc += toString(libGlobal.totalTurns) + ' moves'; /* add the auto description */ t['AutoDesc'] = desc; /* return the table */ return t; } /* * The gameMain object also specifies some settings that control * optional library behavior. If you want the standard library * behavior, you can just inherit the default settings from this * class. Some games might want to select non-default variations, * though. */ /* * The maximum number of points possible in the game. If the game * includes the scoring module at all, and this is non-nil, the SCORE * and FULL SCORE commands will display this value to the player as a * rough indication of how much farther there is to go in the game. * * By default, we initialize this on demand, by calculating the sum * of the point values of the Achievement objects in the game. The * game can override this if needed to specify a specific maximum * possible score, rather than relying on the automatic calculation. */ maxScore() { local m; /* ask the score module (if any) to compute the maximum score */ m = (libGlobal.scoreObj != nil ? libGlobal.scoreObj.calcMaxScore : nil); /* supersede this initializer with the calculated value */ maxScore = m; /* return the result */ return m; } /* * The score ranking list - this provides a list of names for * various score levels. If the game provides a non-nil list here, * the SCORE and FULL SCORE commands will show the rank along with * the score ("This makes you a Master Adventurer"). * * This is a list of score entries. Each score entry is itself a * list of two elements: the first element is the minimum score for * the rank, and the second is a string describing the rank. The * ranks should be given in ascending order, since we simply search * the list for the first item whose minimum score is greater than * our score, and use the preceding item. The first entry in the * list would normally have a minimum of zero points, since it * should give the initial, lowest rank. * * If this is set to nil, which it is by default, we'll simply skip * score ranks entirely. */ scoreRankTable = nil /* * Verbose mode. If this is on, the full room description is * displayed each time the player enters a room, regardless of * whether or not the player has seen the room before; if this is * nil, the full description is only displayed on the player's first * entry to a room, and only the short description on re-entry. Note * that the library provides VERBOSE and TERSE commands that let the * player change this setting dynamically. * * We use a BinarySettingsItem to store the current mode, so that * this setting's default will be taken from the user's global * cross-game preferences. */ verboseMode = verboseModeSettingsItem /* * Option flag: allow the player to use "you" and "me" * interchangeably in referring to the player character. We set this * true by default, so that the player can refer to the player * character in either the first or second person, regardless of how * the game refers to the PC. * * If desired, the game can set this flag to nil to force the player * to use the correct pronoun to refer to the player character. We * define "correct" in the case of first or second person as the * complement of what the game uses: if the game calls the PC "me", * the player must say "you", and vice versa. In a third-person * game, the player must also refer to the PC in the third person. * * We set the default to allow using "you" and "me" interchangeably * because (a) this will create no confusion in most games, and (b) * many players would be annoyed otherwise. For one thing, most * experienced IF players will be rather set in their ways; they'll * be accustomed to using either "me" or "you" (but usually "me") to * refer to the PC, and will tend out of habit to do so even in games * that don't use the traditional second-person narration format. * For another thing, different players have different ideas about * whether the PC is "you" or "me" in input, even in a conventional * second-person game. Some players think in terms of a conversation * with the narrator, in which case the narrator's "you" is the * player's "me", and vice versa; other players are rather more * literal-minded, assuming that if the game talks about "you" then * so should the player. * * Even in games that use first-person or third-person narration, it * seems unlikely that there will be a separate second-person element * to the narration, and as long as that's true, it should cause no * confusion for the game to accept "you" and "me" as equivalent in * commands. However, the library provides this option in case such * as situation does arise. */ allowYouMeMixing = true /* * Option flag: filter plural phrase matches exclude the most obvious * illogicalities, such as trying to TAKE an object that's already * being held, or trying to OPEN an object that's already open. * * This is set to true by default, which means that we exclude an * object from matching a plural phrase when the object's "verify" * routine for the verb has an "illogical-already" or an * "illogical-self" result. * * If you would prefer that plural words are simply matched to * everything present that matches the vocabulary, without any * filtering at all, override this and set it to nil. */ filterPluralMatches = true /* * Option flag: allow ALL to be used for every verb. This is true by * default, which means that players will be allowed to use ALL with * any command - OPEN ALL, EXAMINE ALL, etc. * * Some authors don't like to allow players to use ALL with so many * verbs, because they think it's a sort of "cheating" when players * try things like OPEN ALL. This option lets you disable ALL for * most verbs; if you set this to nil, only the basic inventory * management verbs (TAKE, TAKE FROM, DROP, PUT IN, PUT ON) will * allow ALL, and other verbs will simply respond with an error * ("'All' isn't allowed with that verb"). * * If you're writing an especially puzzle-oriented game, you might * want to set this to nil. It's a trade-off though, as some people * will think your game is less player-friendly if you disable ALL. */ allVerbsAllowAll = true /* * When a command fails, should we continue processing any remaining * commands on the same command line, or simply ignore them? The * reason we might want to ignore additional commands is that they * might not do what the player was expecting if an earlier command * failed; this can sometimes create confusing situations, because * the player expected one effect but got something quite different. * On the other hand, *not* executing all the commands on the command * line could be confusing in its own way, since the game's * assessment of what constitutes "failure" might not be clear to the * player; from the player's perspective, the game might appear to be * inexplicably skipping commands. * * There's no perfect solution. As always, the ideal is to * understand the player's intentions and act accordingly. But when * a command fails, it's usually because the player's idea of what's * going on is out of sync with the game's - in other words, if we're * in this situation to start with, it's probably because our best * effort to understand the player's intentions has already failed. * This isn't always the case; sometimes we understand the player's * intentions perfectly well, but the command fails anyway because of * some surprising new development. In these cases, aborting the * rest of the command is arguably the right approach, because the * player will need a chance to reconsider the pre-typed commands in * light of the new information. In other cases, though, it's not so * clear. For many players, the prime virtue for the parser is to be * predictable, and the most predictable thing to do is to simply * plow through the rest of the command line in all cases. * * Our traditional approach (from the early adv3 versions, and even * in tads 2) has been the simple-minded approach - just keep going * in all cases. So, we make this the default. You can abort * remaining commands on a command failure by setting this to true. */ cancelCmdLineOnFailure = nil /* * Should we use distinguishers when generating action object * announcement messages? If this is set, announcement messages that * list objects by name will add distinguishing details to indicate * specifically which objects are being referred to. This applies to * messages announcing default objects, vaguely matched objects, and * multiple objects. * * A distinguisher is a parser object that tells two objects apart by * some feature that's different in the two objects. The key thing * is that the difference has some natural language phrasing * associated with it, both on the input side and the output side. * For example, the locational distinguisher can tell two objects * apart if they have different containers, since it can describe the * objects by adding containment phrases like "in the box" or "on the * table". * * When this flag is turned on, the parser will try to pick a * distinguisher that can tell apart the specific objects mentioned * in the announcement, from one another or from other objects in * scope, depending on the context. The point is to help make it * clearer to the player the exact objects being referred to. * * Even when this flag is set, the parser tries to minimize the use * of additional distinguishing detail. The goal is to be natural in * the usage, adding detail only when a human speaker would, which is * when the phrasing would otherwise be ambiguous. * * Setting this flag to nil makes the parser simply use the basic * name of each object in an announcement. You can use this setting * if you find that the distinguisher mode generates too much fussy * detail for your taste. */ useDistinguishersInAnnouncements = true /* * How should we handle object announcements when an object is * automatically disambiguated? This controls how an action is * described when the parser uses the logicalness rules to narrow * down the object for a noun phrase when the noun phrase could refer * to multiple in-scope objects. There are three options: * * AnnounceUnclear - Make a parenthetical announcement only when the * choice is *not* clear (as described below). This is the original * library behavior, from before this option was added. * * AnnounceClear - Make a parenthetical announcement (for example, * "(the red door)") for all disambiguated objects, whether clear or * unclear. We don't make an announcement when there's only one * in-scope object matching the noun phrase - the announcement is * only when multiple objects match the words. * * DescribeClear - For *unclear* disambiguation, make a parenthetical * announcement, to emphasize that the parser had to make a choice. * For *clear* disambiguation, skip the announcement, but *do* use a * verbose version of the library message in place of one of the * terse default replies. For example, for >TAKE BOX, instead of * "Taken", we would reply "You take the green box." The longer * reply in these cases always mentions the involved object by name, * to make it clear exactly which object we chose to use. * * The default setting is DescribeClear. * * This only applies when the disambiguation choice is clear - that * is, when there's exactly one in-scope object that passes the * logicalness tests. For example, if the current location contains * a red door that's open and a green door that's closed, CLOSE DOOR * clearly refers to the red door because the other one is already * closed - it's not logical. There are other cases where the * disambiguation is a best guess rather than a clear choice, such as * when there are multiple logical objects but there's one that's * more likely than the others due to the logicalRank results. In * those best-guess situations, the parser always announces its * decision, because it's entirely plausible that the player meant * one of the other logical, but less likely, choices. */ ambigAnnounceMode = DescribeClear /* * Should the "before" notifications (beforeAction, roomBeforeAction, * and actorAction) run before or after the "check" phase? * * The library traditionally ran the "before" notifiers first, so * this is the default. However, in many ways it's more logical and * useful to run "check" first. That way, you can consider the * action to be more or less committed by the time the "before" * notifiers are invoked. Of course, a command is never *truly* * committed until it's actually been executed, since a "before" * handler could always cancel it. But this is relatively rare - * "before" handlers usually carry out side effects, so it's very * useful to be able to know that the command has already passed all * of its own internal checks by the time "before" is invoked - that * way, you can invoke side effects without worrying that the command * will subsequently fail. */ beforeRunsBeforeCheck = true ; /* * The VERBOSE mode settings item. */ verboseModeSettingsItem: BinarySettingsItem /* VERBOSE mode is on by default */ isOn = true /* our configuration file variable ID */ settingID = 'adv3.verbose' /* show our description */ settingDesc = (gLibMessages.shortVerboseStatus(isOn)) ; /* ------------------------------------------------------------------------ */ /* * Clear the main game window. In most cases, you should call this * rather than calling the low-level clearScreen() function directly, * since this routine takes care of a couple of chores that should * usually be done at the same time. * * First, we flush the transcript to ensure that no left-over reports * that were displayed before we cleared the screen will show up on the * new screen. Second, we call the low-level clearScreen() function to * actually clear the display window. Finally, we re-display any * tag, to ensure that the about-box will still be around; * this is necessary because any existing tag is lost after * the screen is cleared. */ cls() { /* flush any captured transcript output */ if (gTranscript != nil) gTranscript.flushForInput(); /* clear the screen */ aioClearScreen(); } /* ------------------------------------------------------------------------ */ /* * Run the game. We start by showing the description of the initial * location, if desired, and then we read and interpret commands until * the game ends (via a "quit" command, winning, death of the player * character, or any other way of terminating the game). * * This routine doesn't return until the game ends. * * Before calling this routine, the caller should already have set the * global variable gPlayerChar to the player character actor. * * 'look' is a flag indicating whether or not to look around; if this is * true, we'll show a full description of the player character's initial * location, as though the player were to type "look around" as the first * command. */ runGame(look) { /* show the starting location */ if (look) { /* run the initial "look around" in a dummy command context */ withActionEnv(EventAction, gPlayerChar, {: gPlayerChar.lookAround(true) }); } /* run the scheduling loop until the game ends */ runScheduler(); } /* ------------------------------------------------------------------------ */ /* * Main program entrypoint. The core run-time start-up code calls this * after running pre-initialization and load-time initialization. This * entrypoint is called when we're starting the game normally; when the * game is launched through a saved-position file, mainRestore() will be * invoked instead. */ main(args) { libGlobal.commandLineArgs = args; mainCommon(&newGame); } /* * Main program entrypoint for restoring a saved-position file. This is * invoked from the core run-time start-up code when the game is launched * from the operating system via a saved-position file. For example, on * Windows, double-clicking on a saved-position file on the Windows * desktop launches the interpreter, which looks in the save file to find * the game executable to run, then starts the game and invokes this * entrypoint. */ mainRestore(args, restoreFile) { libGlobal.commandLineArgs = args; mainCommon(&restoreAndRunGame, restoreFile); } /* * Common main entrypoint - this handles starting a new game or restoring * an existing saved state. */ mainCommon(prop, [args]) { try { /* restore the global default settings */ settingsManager.restoreSettings(); } catch (Exception exc) { /* * ignore any errors restoring defaults - it's not critical that * we restore this file automatically */ } try { /* at the start of the session, set up the UI subsystem */ if (mainGlobal.restartID == 0) { /* initialize the UI */ initUI(); /* * tell the system library to call our UI shutdown function * at program exit */ mainAtExit.addHandler(terminateUI); } /* initialize the display */ initDisplay(); /* call the appropriate gameMain method */ gameMain.(prop)(args...); } catch (QuittingException q) { /* * This exception is a signal to quit the game, which we will now * proceed to do by returning from this function, which exits the * program. */ } } /* ------------------------------------------------------------------------ */ /* * Determine if the given object overrides the definition of the given * property inherited from the given base class. Returns true if the * object derives from the given base class, and the object's definition * of the property comes from a different place than the base class's * definition of the property. */ overrides(obj, base, prop) { return (obj.ofKind(base) && (obj.propDefined(prop, PropDefGetClass) != base.propDefined(prop, PropDefGetClass))); } /* ------------------------------------------------------------------------ */ /* * Library Pre-Initializer. This object performs the following * initialization operations immediately after compilation is completed: * * - adds each defined Thing to its container's contents list * * - adds each defined Sense to the global sense list * * This object is named so that other libraries and/or user code can * create initialization order dependencies upon it. */ adv3LibPreinit: PreinitObject execute() { /* save each SettingsItem's factory default settings */ forEachInstance(SettingsItem, {i: i.factoryDefault = i.settingToText()}); /* set the initial player character, as specified in gameMain */ gPlayerChar = gameMain.initialPlayerChar; /* * visit every VocabObject, and run its vocabulary initializer * (this routine will be defined in the language-specific part * of the library to enter each object's vocabulary words into * the dictionary) */ forEachInstance(VocabObject, { obj: obj.initializeVocab() }); /* visit every Thing, and run its general initializer */ forEachInstance(Thing, { obj: obj.initializeThing() }); /* initialize SpecialTopic objects */ forEachInstance(SpecialTopic, { obj: obj.initializeSpecialTopic() }); /* * Initialize each MultiInstance object. Do this after * initializing the Thing objects, because we'll be dynamically * constructing new Thing objects for the instances. Those new * Things will be initialized by their constructors, so we don't * want to initialize them redundantly with explicit * initializeThing calls. */ forEachInstance(MultiInstance, { obj: obj.initializeLocation() }); /* add every Sense to the global sense list */ forEachInstance(Sense, { obj: libGlobal.allSenses += obj }); /* * initialize each ActorState - do this before initializing * actors, since we want each actor's initial state to plug * itself into its actor before we initialize the actors */ forEachInstance(ActorState, { obj: obj.initializeActorState() }); /* initialize each Actor */ forEachInstance(Actor, { obj: obj.initializeActor() }); /* * initialize the AltTopics first, to set up their parents' lists * of their AltTopic children */ forEachInstance(AltTopic, { obj: obj.initializeAltTopic() }); /* initialize the topic database entries */ forEachInstance(TopicEntry, { obj: obj.initializeTopicEntry() }); /* initialize the suggested topics */ forEachInstance(SuggestedTopic, { obj: obj.initializeSuggestedTopic() }); /* initialize the master direction list */ Direction.initializeDirectionClass(); /* initialize the noise/odor notification daemon */ local d = new Daemon(SensoryEmanation, ¬eSenseChanges, 1); /* * give it a later-than-default event order, so that it runs * after most other daemons and fuses */ d.eventOrder = 500; /* set up a daemon for the current location */ new Daemon(BasicLocation, &dispatchRoomDaemon, 1); /* * Initialize the status line daemon. Set this daemon's event * order to a high value so that it runs last, after all other * daemons - we want this to be the last prompt daemon so that * the status line is updated after any other daemons have done * their jobs already, in case any of them move the player * character to a new location or affect the score, or make any * other changes that should be reflected on the status line. */ local sld = new PromptDaemon(statusLine, &showStatusLineDaemon); sld.eventOrder = 1000; /* * Attach the command sequencer output filter, the * language-specific message parameter substitution filter, the * style tag formatter filter, and the paragraph filter to the * main output stream. Stack them so that the paragraph manager * is at the bottom, since the library tag filter can produce * paragraph tags and thus needs to sit atop the paragraph * filter. Put the command sequencer above those, since it * might need to write style tags. Finally, put the sense * context filter on top of those. */ mainOutputStream.addOutputFilter(typographicalOutputFilter); mainOutputStream.addOutputFilter(mainParagraphManager); mainOutputStream.addOutputFilter(styleTagFilter); mainOutputStream.addOutputFilter(langMessageBuilder); mainOutputStream.addOutputFilter(commandSequencer); mainOutputStream.addOutputFilter(conversationManager); mainOutputStream.addOutputFilter(senseContext); /* * Attach our message parameter filter and style tag filter to * the status line streams. We don't need most of the main * window's filters in the status line. */ statusTagOutputStream.addOutputFilter(styleTagFilter); statusTagOutputStream.addOutputFilter(langMessageBuilder); statusLeftOutputStream.addOutputFilter(styleTagFilter); statusLeftOutputStream.addOutputFilter(langMessageBuilder); statusRightOutputStream.addOutputFilter(styleTagFilter); statusRightOutputStream.addOutputFilter(langMessageBuilder); } /* * Make sure the output streams we depend on are initialized before * me (so that they set up properly internally). Also, make sure * that the message builder object (langMessageBuilder) is set up * first, so that we can add entries to its parameter substitution * table. */ execBeforeMe = [mainOutputStream, statusTagOutputStream, statusLeftOutputStream, statusRightOutputStream, langMessageBuilder] ; /* ------------------------------------------------------------------------ */ /* * Library Initializer. This object performs the following * initialization operations each time the game is started: * * - sets up the library's default output function */ adv3LibInit: InitObject execute() { /* * Set up our default output function. Note that we must do * this during run-time initialization each time we start the * game, rather than during pre-initialization, because the * default output function state is not part of the load-image * configuration. */ t3SetSay(say); } ; /* ------------------------------------------------------------------------ */ /* * Generic script object. This class can be used to implement a simple * state machine. */ class Script: object /* * Get the current state. This returns a value that gives the * current state of the script, which is usually simply an integer. */ getScriptState() { /* by default, return our state property */ return curScriptState; } /* * Process the next step of the script. This routine must be * overridden to perform the action of the script. This routine's * action should call getScriptState() to get our current state, and * should update the internal state appropriately to take us to the * next step after the current one. * * By default, we don't do anything at all. */ doScript() { /* override to carry out the script */ } /* * Property giving our current state. This should never be used * directly; instead, getScriptState() should always be used, since * getScriptState() can be overridden so that the state depends on * something other than this internal state property. The meaning of * the state identifier is specific to each subclass. */ curScriptState = 0 ; /* * Random-Firing script add-in. This is a mix-in class that you can add * to the superclass list of any Script subclass to make the script * execute only a given percentage of the time it's invoked. Each time * doScript() is invoked on the script, we'll look at the probability * settings (see the properties below) to determine whether we really * want to execute the script this time; if so, we'll proceed with the * scripted event, otherwise we'll just return immediately, doing * nothing. * * Note that this must be used in the superclass list *before* the Script * subclass: * * myScript: RandomFiringScript, EventList *. // ...my definitions... *. ; * * This class is especially useful for random atmospheric events, because * it allows you to make the timing of scripted events random. Rather * than making a scripted event happen on every single turn, you can use * this to make events happen only sporadically. It can often feel too * predictable and repetitious when a random background event happens on * every single turn; firing events less frequently often makes them feel * more realistic. */ class RandomFiringScript: object /* * Percentage of the time an event occurs. By default, we execute an * event 100% of the time - meaning every time that doScript() is * invoked. If you set this to a lower percentage, then each time * doScript() is invoked, we'll randomly decide whether or not to * execute an event based on this percentage. For example, if you * want an event to execute on average about a third of the time, set * this to 33. * * Note that this is a probabilistic frequency. Setting this to 33 * does *not* mean that we'll execute exactly every third time. * Rather, it means that we'll randomly execute or not on each * invocation, and averaged over a large number of invocations, we'll * execute about a third of the time. */ eventPercent = 100 /* * Random atmospheric events can get repetitive after a while, so we * provide an easy way to reduce the frequency of our events after a * while. This way, we'll generate the events more frequently at * first, but once the player has seen them enough to get the idea, * we'll cut back. Sometimes, the player will spend a lot of time in * one place trying to solve a puzzle, so the same set of random * events can get stale. Set eventReduceAfter to the number of times * you want the events to be generated at full frequency; after we've * fired events that many times, we'll change eventPercent to * eventReduceTo. If eventReduceAfter is nil, we won't ever change * eventPercent. */ eventReduceAfter = nil eventReduceTo = nil /* * When doScript() is invoked, check the event probabilities before * proceeding. */ doScript() { /* process the script step only if the event odds allow it */ if (checkEventOdds()) inherited(); } /* * Check the event odds to see if we want to fire an event at all on * this invocation. */ checkEventOdds() { /* * check the event odds to see if we fire an event this time; if * not, we're done with the script invocation */ if (rand(100) >= eventPercent) return nil; /* * we're firing an event this time, so count this against the * reduction limit, if there is one */ if (eventReduceAfter != nil) { /* decrement the limit counter */ --eventReduceAfter; /* if it has reached zero, apply the reduced frequency */ if (eventReduceAfter == 0) { /* apply the reduced frequency */ eventPercent = eventReduceTo; /* we no longer have a limit to look for */ eventReduceAfter = nil; } } /* indicate that we do want to fire an event */ return true; } ; /* ------------------------------------------------------------------------ */ /* * An "event list." This is a general-purpose type of script that lets * you define the scripted events separately from the Script object. * * The script is driven by a list of values; each value represents one * step of the script. Each value can be a single-quoted string, in * which case the string is simply displayed; a function pointer, in * which case the function is invoked without arguments; another Script * object, in which case the object's doScript() method is invoked; a * property pointer, in which case the property of 'self' (the EventList * object) is invoked with no arguments; or nil, in which case nothing * happens. * * This base type of event list runs through the list once, in order, and * then simply stops doing anything once we pass the last event. */ class EventList: Script construct(lst) { eventList = lst; } /* the list of events */ eventList = [] /* cached length of the event list */ eventListLen = (eventList.length()) /* advance to the next state */ advanceState() { /* increment our state index */ ++curScriptState; } /* by default, start at the first list element */ curScriptState = 1 /* process the next step of the script */ doScript() { /* get our current event state */ local idx = getScriptState(); /* get the list (evaluate it once to avoid repeated side effects) */ local lst = eventList; /* cache the length */ eventListLen = lst.length(); /* if it's a valid index in our list, fire the event */ if (idx >= 1 && idx <= eventListLen) { /* carry out the event */ doScriptEvent(lst[idx]); } /* perform any end-of-script processing */ scriptDone(); } /* carry out one script event */ doScriptEvent(evt) { /* check what kind of event we have */ switch (dataTypeXlat(evt)) { case TypeSString: /* it's a string - display it */ say(evt); break; case TypeObject: /* it must be a Script object - invoke its doScript() method */ evt.doScript(); break; case TypeFuncPtr: /* it's a function pointer - invoke it */ (evt)(); break; case TypeProp: /* it's a property of self - invoke it */ self.(evt)(); break; default: /* do nothing in other cases */ break; } } /* * Perform any end-of-script processing. By default, we advance the * script to the next state. * * Some scripts might want to override this. For example, a script * could be driven entirely by some external timing; the state of a * script could vary once per turn, for example, or could change each * time an actor pushes a button. In these cases, invoking the * script wouldn't affect the state of the event list, so the * subclass would override scriptDone() so that it does nothing at * all. */ scriptDone() { /* advance to the next state */ advanceState(); } ; /* * An "external" event list is one whose state is driven externally to * the script. Specifically, the state is *not* advanced by invoking the * script; the state is advanced exclusively by some external process * (for example, by a daemon that invokes the event list's advanceState() * method). */ class ExternalEventList: EventList scriptDone() { } ; /* * A cyclical event list - this runs through the event list in order, * returning to the first element when we pass the last element. */ class CyclicEventList: EventList advanceState() { /* go to the next state */ ++curScriptState; /* if we've passed the end of the list, loop back to the start */ if (curScriptState > eventListLen) curScriptState = 1; } ; /* * A stopping event list - this runs through the event list in order, * then stops at the last item and repeats it each time the script is * subsequently invoked. * * This is often useful for things like ASK ABOUT topics, where we reveal * more information when asked repeatedly about a topic, but eventually * reach a point where we've said everything: * *. >ask bob about black book *. "What makes you think I know anything about it?" he says, his * voice shaking. * * >again *. "No! You can't make me tell you!" * * >again *. "All right, I'll tell you what you want to know! But I warn you, * these are things mortal men were never meant to know. Your life, your * very soul will be in danger from the moment you hear these dark secrets!" * * >again *. [scene missing] * * >again *. "I've already told you all I know." * * >again *. "I've already told you all I know." */ class StopEventList: EventList advanceState() { /* if we haven't yet reached the last state, go to the next one */ if (curScriptState < eventListLen) ++curScriptState; } ; /* * A synchronized event list. This is an event list that takes its * actions from a separate event list object. We get our current state * from the other list, and advancing our state advances the other list's * state in lock step. Set 'masterObject' to refer to the master list * whose state we synchronize with. * * This can be useful, for example, when we have messages that reflect * two different points of view on the same events: the messages for each * point of view can be kept in a separate list, but the one list can be * a slave of the other to ensure that the two lists are based on a * common state. */ class SyncEventList: EventList /* my master event list object */ masterObject = nil /* my state is simply the master list's state */ getScriptState() { return masterObject.getScriptState(); } /* to advance my state, advance the master list's state */ advanceState() { masterObject.advanceState(); } /* let the master list take care of finishing a script step */ scriptDone() { masterObject.scriptDone(); } ; /* * Randomized event list. This is similar to a regular event list, but * chooses an event at random each time it's invoked. */ class RandomEventList: RandomFiringScript, EventList /* process the next step of the script */ doScript() { /* check the odds to see if we want to fire an event at all */ if (!checkEventOdds()) return; /* get our next random number */ local idx = getNextRandom(); /* cache the list and its length, to avoid repeated side effects */ local lst = eventList; eventListLen = lst.length(); /* run the event, if the index is valid */ if (idx >= 1 && idx <= eventListLen) doScriptEvent(lst[idx]); } /* * Get the next random state. By default, we simply return a number * from 1 to the number of entries in our event list. This is a * separate method to allow subclasses to customize the way the * random number is selected. */ getNextRandom() { /* * Note that rand(n) returns a number from 0 to n-1 inclusive; * since list indices run from 1 to list.length, add one to the * result of rand(list.length) to get a value in the proper range * for a list index. */ return rand(eventListLen) + 1; } ; /* * Shuffled event list. This is similar to a random event list, except * that we fire our events in a "shuffled" order rather than an * independently random order. "Shuffled order" means that we fire the * events in random order, but we don't re-fire an event until we've run * through all of the other events. The effect is as though we were * dealing from a deck of cards. * * For the first time through the main list, we normally shuffle the * strings immediately at startup, but this is optional. If shuffleFirst * is set to nil, we will NOT shuffle the list the first time through - * we'll run through it once in the given order, then shuffle for the * next time through, then shuffle again for the next, and so on. So, if * you want a specific order for the first time through, just define the * list in the desired order and set shuffleFirst to nil. * * You can optionally specify a separate list of one-time-only sequential * strings in the property firstEvents. We'll run through these strings * once. When we've exhausted them, we'll switch to the main eventList * list, showing it one time through in its given order, then shuffling * it and running through it again, and so on. The firstEvents list is * never shuffled - it's always shown in exactly the order given. */ class ShuffledEventList: RandomFiringScript, EventList /* * a list of events to go through sequentially, in the exact order * specified, before firing any events from the main list */ firstEvents = [] /* * Flag: shuffle the eventList list before we show it for the first * time. By default, this is set to true, so that the behavior is * random on each independent run of the game. However, it might be * desirable in some cases to always use the original ordering of the * eventList list the first time through the list. If this is set to * nil, we won't shuffle the list the first time through. */ shuffleFirst = true /* * Flag: suppress repeats in the shuffle. If this is true, it * prevents a given event from showing up twice in a row, which could * otherwise happen right after a shuffle. This is ignored for lists * with one or two events: it's impossible to prevent repeats in a * one-element list, and doing so in a two-element list would produce * a predictable A-B-A-B... pattern. * * You might want to set this to nil for lists of three or four * elements, since such short lists can result in fairly * un-random-looking sequences when repeats are suppressed, because * the available number of permutations drops significantly. */ suppressRepeats = true /* process the next step of the script */ doScript() { /* cache the lists to avoid repeated side effects */ local firstLst = firstEvents; local firstLen = firstLst.length(); local lst = eventList; eventListLen = lst.length(); /* process the script step only if the event odds allow it */ if (!checkEventOdds()) return; /* * States 1..N, where N is the number of elements in the * firstEvents list, simply show the firstEvents elements in * order. * * If we're set to shuffle the main eventList list initially, all * states above N simply show elements from the eventList list in * shuffled order. * * If we're NOT set to shuffle the main eventList list initially, * the following apply: * * States N+1..N+M, where M is the number of elements in the * eventList list, show the eventList elements in order. * * States above N+M show elements from the eventList list in * shuffled order. */ local evt; if (curScriptState <= firstLen) { /* simply fetch the next string from firstEvents */ evt = firstEvents[curScriptState++]; } else if (!shuffleFirst && curScriptState <= firstLen + eventListLen) { /* fetch the next string from eventList */ evt = lst[curScriptState++ - firstLen]; } else { /* we're showing shuffled strings from the eventList list */ evt = lst[getNextRandom()]; } /* execute the event */ doScriptEvent(evt); } /* * Get the next random event. We'll pick an event from our list of * events using a ShuffledIntegerList to ensure we pick each value * once before re-using any values. */ getNextRandom() { /* if we haven't created our shuffled list yet, do so now */ if (shuffledList_ == nil) { /* * create a shuffled integer list - we'll use these shuffled * integers as indices into our event list */ shuffledList_ = new ShuffledIntegerList(1, eventListLen); /* apply our suppressRepeats option to the shuffled list */ shuffledList_.suppressRepeats = suppressRepeats; } /* ask the shuffled list to pick an element */ return shuffledList_.getNextValue(); } /* our ShuffledList - we'll initialize this on demand */ shuffledList_ = nil ; /* ------------------------------------------------------------------------ */ /* * Shuffled List - this class keeps a list of values that can be returned * in random order, but with the constraint that we never repeat a value * until we've handed out every value. Think of a shuffled deck of * cards: the order of the cards handed out is random, but once a card is * dealt, it can't be dealt again until we put everything back into the * deck and reshuffle. */ class ShuffledList: object /* * the list of values we want to shuffle - initialize this in each * instance to the set of values we want to return in random order */ valueList = [] /* * Flag: suppress repeated values. We mostly suppress repeats by our * very design, since we run through the entire list before repeating * anything in the list. However, there's one situation (in a list * with more than one element) where a repeat can occur: immediately * after a shuffle, we could select the last element from the * previous shuffle as the first element of the new shuffle. If this * flag is set, we'll suppress this type of repeat by choosing again * any time we're about to choose a repeat. * * Note that we ignore this for a list of one element, since it's * obviously impossible to avoid repeats in this case. We also * ignore it for a two-element list, since this would produce the * predictable pattern A-B-A-B..., defeating the purpose of the * shuffle. */ suppressRepeats = nil /* create from a given list */ construct(lst) { /* remember our list of values */ valueList = lst; } /* * Get a random value. This will return a randomly-selected element * from 'valueList', but we'll return every element of 'valueList' * once before repeating any element. * * If we've returned every value on the current round, we'll * automatically shuffle the values and start a new round. */ getNextValue() { local i; local ret; local justReshuffled = nil; /* if we haven't initialized our vector, do so now */ if (valuesVec == nil) { /* create the vector */ valuesVec = new Vector(valueList.length(), valueList); /* all values are initially available */ valuesAvail = valuesVec.length(); } /* if we've exhausted our values on this round, start over */ if (valuesAvail == 0) { /* shuffle the elements */ reshuffle(); /* note that we just did a shuffle */ justReshuffled = true; } /* pick a random element from the 'available' partition */ i = rand(valuesAvail) + 1; /* * If we just reshuffled, and we're configured to suppress a * repeat immediately after a reshuffle, and we chose the first * element of the vector, and we have at least three elements, * choose a different element. The first element in the vector is * always the last element we return from each run-through, since * the 'available' partition is at the start of the list and thus * shrinks down until it contains only the first element. * * If we have one element, there's obviously no point in trying to * suppress repeats. If we have two elements, we *still* don't * want to suppress repeats, because in this case we'd generate a * predicatable A-B-A-B pattern (because we could never have two * A's or two B's in a row). */ if (justReshuffled && suppressRepeats && valuesAvail > 2) { /* * we don't want repeats, so choose anything besides the * first element; keep choosing until we get another element */ while (i == 1) i = rand(valuesAvail) + 1; } /* remember the element we're returning */ ret = valuesVec[i]; /* * Move the value at the top of the 'available' partition down * into the hole we're creating at 'i', since we're about to * reduce the size of the 'available' partition to reflect the * use of one more value; that would leave the element at the top * of the partition homeless, so we need somewhere to put it. * Luckily, we also need to delete element 'i', since we're using * this element. Solve both problems at once by moving element * we're rendering homeless into the hole we're creating. */ valuesVec[i] = valuesVec[valuesAvail]; /* move the value we're returning into the top slot */ valuesVec[valuesAvail] = ret; /* reduce the 'available' partition by one */ --valuesAvail; /* return the result */ return ret; } /* * Shuffle the values. This puts all of the values back into the * deck (as it were) for a new round. It's never required to call * this, because getNextValue() automatically shuffles the deck and * starts over each time it runs through the entire deck. This is * provided in case the caller has a reason to want to put all the * values back into play immediately, before every value has been * dealt on the current round. */ reshuffle() { /* * Simply reset the counter of available values. Go with the * original source list's length, in case we haven't initialized * our internal vector yet. */ valuesAvail = valueList.length(); } /* * Internal vector of available/used values. Elements from 1 to * 'valuesAvail', inclusive, are still available for use on this * round. Elements above 'valuesAvail' have already been used. */ valuesVec = nil /* number of values still available on this round */ valuesAvail = 0 ; /* * A Shuffled Integer List is a special kind of Shuffled List that * returns integers in a given range. Like an ordinary Shuffled List, * we'll return integers in the given range in random order, but we'll * only return each integer once during a given round; when we exhaust * the supply, we'll reshuffle the set of integers and start over. */ class ShuffledIntegerList: ShuffledList /* * The minimum and maximum values for our range. Instances should * define these to the range desired. */ rangeMin = 1 rangeMax = 10 /* initialize the value list on demand */ valueList = nil /* construct with the given range */ construct(rmin, rmax) { rangeMin = rmin; rangeMax = rmax; } /* get the next value */ getNextValue() { /* * If we haven't set up our value list yet, do so now. This is * simply a list of integers from rangeMin to rangeMax. */ if (valueList == nil) { local ele = rangeMin; valueList = List.generate({i: ele++}, rangeMax - rangeMin + 1); } /* use the inherited handling to select from our value list */ return inherited(); } ; /* ------------------------------------------------------------------------ */ /* * Library global variables */ libGlobal: object /* * The current library messages object. This is the source object * for messages that don't logically relate to the actor carrying out * the comamand. It's mostly used for meta-command replies, and for * text fragments that are used to construct descriptions. * * This message object isn't generally used for parser messages or * action replies - most of those come from the objects given by the * current actor's getParserMessageObj() or getActionMessageObj(), * respectively. * * By default, this is set to libMessages. The library never changes * this itself, but a game can change this if it wants to switch to a * new set of messages during a game. (If you don't need to change * messages during a game, but simply want to customize some of the * default messages, you don't need to set this variable - you can * simply use 'modify libMessages' instead. This variable is * designed for cases where you want to *dynamically* change the * standard messages during the game.) */ libMessageObj = libMessages /* * Sense cache - we keep SenseInfo lists here, keyed by [pov,sense]; * we normally discard the cached information at the start of each * turn, and disable caching entirely at the start of the "action" * phase of each turn. We leave caching disabled during each turn's * action phase because this is the phase where simulation state * changes are typically made, and hence it would be difficult to * keep the cache coherent during this phase. * * When this is nil, it indicates that caching is disabled. We only * allow caching during certain phases of execution, when game state * is not conventionally altered, so that we don't have to do a lot * of work to keep the cache up to date. */ senseCache = nil /* * Can-Touch cache - we keep CanTouchInfo entries here, keyed by * [from,to]. This cache is the touch-path equivalent of the sense * cache, and is enabled and disabled */ canTouchCache = nil /* * Connection list cache - this is a cache of all of the objects * connected by containment to a given object. */ connectionCache = nil /* * Actor visual ambient cache - this keeps track of the ambient light * level at the given actor. */ actorVisualAmbientCache = nil /* enable the cache, clearing any old cached information */ enableSenseCache() { /* create a new, empty lookup table for the sense cache */ senseCache = new LookupTable(32, 64); /* create the can-touch cache */ canTouchCache = new LookupTable(32, 64); /* create the actor visual ambient cache */ actorVisualAmbientCache = new LookupTable(32, 64); /* create a connection list cache */ connectionCache = new LookupTable(32, 64); } /* disable the cache */ disableSenseCache() { /* forget the cache tables */ senseCache = nil; canTouchCache = nil; actorVisualAmbientCache = nil; connectionCache = nil; } /* * Invalidate the sense cache. This can be called if something * happens during noun resolution or verification that causes any * cached sense information to become out of date. For example, if * you have to create a new game-world object during noun-phrase * resolution, this should be called to ensure that the new object's * visibility is properly calculated and incorporated into the cached * information. */ invalSenseCache() { /* remember whether or not caching is currently enabled */ local wasEnabled = (senseCache != nil); /* clear the cache by disabling it */ disableSenseCache(); /* if the cache was previously enabled, re-enable it */ if (wasEnabled) enableSenseCache(); } /* * List of all of the senses. The library pre-initializer will load * this list with a reference to each instance of class Sense. */ allSenses = [] /* * The current player character */ playerChar = nil /* * The current perspective actor. This is the actor who's performing * the action (LOOK AROUND, EXAMINE, SMELL, etc) that's generating * the current description. */ pointOfViewActor = nil /* * The current perspective object. This is *usually* the actor * performing the current command, but can be a different object when * the actor is viewing the location being described via an * intermediary, such as through a closed-circuit TV camera. */ pointOfView = nil /* * The stack of point of view objects. The last element of the * vector is the most recent point of view after the current point * of view. */ povStack = static new Vector(32) /* * The global score object. We use a global for this, rather than * referencing libScore directly, to allow the score module to be * left out entirely if the game doesn't make use of scoring. The * score module should set this during pre-initialization. */ scoreObj = nil /* * The global Footnote class object. We use a global for this, * rather than referencing Footnote directly, to allow the footnote * module to be left out entirely if the game doesn't make use of * footnotes. The footnote class should set this during * pre-initialization. */ footnoteClass = nil /* the total number of turns so far */ totalTurns = 0 /* * flag: the parser is in 'debug' mode, in which it displays the * parse tree for each command entered */ parserDebugMode = nil /* * Most recent command, for 'undo' purposes. This is the last * command the player character performed, or the last initial * command a player directed to an NPC. * * Note that if the player directed a series of commands to an NPC * with a single command line, only the first command on such a * command line is retained here, because it is only the first such * command that counts as a player's turn in terms of the game * clock. Subsequent commands are executed by the NPC's on the * NPC's own time, and do not count against the PC's game clock * time. The first command counts against the PC's clock because of * the time it takes the PC to give the command to the NPC. */ lastCommandForUndo = '' /* * Most recent target actor phrase; this goes with * lastCommandForUndo. This is nil if the last command did not * specify an actor (i.e., was implicitly for the player character), * otherwise is the string the player typed specifying a target * actor. */ lastActorForUndo = '' /* * Current command information. We keep track of the current * command's actor and action here, as well as the verification * result list and the command report list. */ curActor = nil curIssuingActor = nil curAction = nil curVerifyResults = nil /* the exitLister object, if included in the build */ exitListerObj = nil /* the hint manager, if included in the build */ hintManagerObj = nil /* * The game's IFID, as defined in the game's main module ID object. * If the game has multiple IFIDs in the module list, this will store * only the first IFID in the list. NOTE: the library initializes * this automatically during preinit; don't set this manually. */ IFID = nil /* * Command line arguments. The library sets this to a list of * strings containing the arguments passed to the program on the * command line. This list contains the command line arguments * parsed according to the local conventions for the operating system * and C++ library. The standard parsing procedure used by most * systems is to break the line into tokens delimited by space * characters. Many systems also allow space characters to be * embedded in tokens by quoting the tokens. The first argument is * always the name of the .t3 file currently executing. */ commandLineArgs = [] /* * Retrieve a "switch" from the command line. Switches are options * specifies with the conventional Unix "-xxx" notation. This * searches for a command option that equals the given string or * starts with the given substring. If we find it, we return the * part of the option after the given substring - this is * conventionally the value of the switch. For example, the command * line might look like this: * *. t3run mygame.t3 -name=MyGame -user=Bob * * Searching for '-name=' would return 'MyGame', and searching for * '-user=' would return' Bob'. * * If the switch is found but has no value attached, the return value * is an empty string. If the switch isn't found at all, the return * value is nil. */ getCommandSwitch(s) { /* search from argument 2 to the last switch argument */ local args = commandLineArgs; for (local i in 2..args.length()) { /* * if this isn't a switch, or is the special "-" last switch * marker, we're done */ local a = args[i]; if (!a.startsWith('-') || a == '-') return nil; /* check for a match */ if (a.startsWith(s)) return a.substr(s.length() + 1); } /* didn't find it */ return nil; } ; /* ------------------------------------------------------------------------ */ /* * FinishType objects are used in finishGameMsg() to indicate what kind * of game-over message to display. We provide a couple of standard * objects for the most common cases. */ class FinishType: object /* the finishing message, as a string or library message property */ finishMsg = nil ; /* 'death' - the game has ended due to the player character's demise */ ftDeath: FinishType finishMsg = &finishDeathMsg; /* 'victory' - the player has won the game */ ftVictory: FinishType finishMsg = &finishVictoryMsg; /* 'failure' - the game has ended in failure (but not necessarily death) */ ftFailure: FinishType finishMsg = &finishFailureMsg; /* 'game over' - the game has simply ended */ ftGameOver: FinishType finishMsg = &finishGameOverMsg; /* * Finish the game, showing a message explaining why the game has ended. * This can be called when an event occurs that ends the game, such as * the player character's death, winning, or any other endpoint in the * story. * * We'll show a message defined by 'msg', using a standard format. The * format depends on the language, but in English, it's usually the * message surrounded by asterisks: "*** You have won! ***". 'msg' can * be: * *. - nil, in which case we display nothing *. - a string, which we'll display as the message *. - a FinishType object, from which we'll get the message * * After showing the message (if any), we'll prompt the user with * options for how to proceed. We'll always show the QUIT, RESTART, and * RESTORE options; other options can be offered by listing one or more * FinishOption objects in the 'extra' parameter, which is given as a * list of FinishOption objects. The library defines a few non-default * finish options, such as finishOptionUndo and finishOptionCredits; in * addition, the game can subclass FinishOption to create its own custom * options, as desired. */ finishGameMsg(msg, extra) { local lst; /* * Adjust the turn counter to take into account the action currently * in progress, if any, and to reflect any turns that the player * character has already completed and which aren't yet reflected in * the turn counter. If we're processing a daemon, the PC's next * schedulable run time will already reflect the last turn the PC * completed, but the global turn counter won't be there yet, since * we're still scheduling daemons that were ready to run on the same * turn as the player's last action. */ libGlobal.totalTurns = gPlayerChar.nextRunTime + gAction.actionTime; /* * Explicitly run any final score notification now. This will ensure * that any points awarded in the course of the final command that * brought us to this point will generate the usual notification, and * that the notification will appear at a reasonable place, just * before the termination message. */ if (libGlobal.scoreObj != nil) libGlobal.scoreObj.runScoreNotifier(); /* translate the message, if specified */ if (dataType(msg) == TypeObject) { /* it's a FinishType object - get its message property or string */ msg = msg.finishMsg; /* if it's a library message property, look it up */ if (dataType(msg) == TypeProp) msg = gLibMessages.(msg); } /* if we have a message, display it */ if (msg != nil) gLibMessages.showFinishMsg(msg); /* if the extra options include a scoring option, show the score */ if (extra != nil && extra.indexWhich({x: x.showScoreInFinish}) != nil) { "<.p>"; libGlobal.scoreObj.showScore(); "<.p>"; } /* * Since we need to interact directly with the player, any sense * context currently in effect is now irrelevant. Reset the sense * context by setting the 'source' object to nil to indicate that we * don't need any sense blocking at all. We can just set the context * directly, since this routine will never return into the * surrounding command processing - we always either terminate the * program or proceed to a different game context (via undo, restore, * restart, etc). By the same token, the actor we're talking to now * is the player character. */ senseContext.setSenseContext(nil, sight); gActor = gPlayerChar; /* start with the standard options */ lst = [finishOptionRestore, finishOptionRestart]; /* add any additional options in the 'extra' parameter */ if (extra != nil) lst += extra; /* always add 'quit' as the last option */ lst += finishOptionQuit; /* process the options */ processOptions(lst); } /* finish the game, offering the given extra options but no message */ finishGame(extra) { finishGameMsg(nil, extra); } /* * Show failed startup restore options. If a restore operation fails at * startup, we won't just proceed with the game, but ask the user what * they want to do; we'll offer the options of restoring another game, * quitting, or starting the game from the beginning. */ failedRestoreOptions() { /* process our set of options */ processOptions([restoreOptionRestoreAnother, restoreOptionStartOver, finishOptionQuit]); } /* * Process a list of finishing options. We'll loop, showing prompts and * reading responses, until we get a response that terminates the loop. */ processOptions(lst) { /* keep going until we get a valid response */ promptLoop: for (;;) { local resp; /* show the options */ finishOptionsLister.showListAll(lst, 0, 0); /* switch to before-command mode for reading the interactive input */ "<.commandbefore>"; /* * update the status line, in case the score or turn counter has * changed (this is especially likely when we first enter this * loop, since we might have just finished the game with our * previous action, and that action might well have awarded us * some points) */ statusLine.showStatusLine(); /* read a response */ resp = inputManager.getInputLine(nil, nil); /* switch to command-after mode */ "<.commandafter>"; /* check for a match to each of the options in our list */ foreach (local cur in lst) { /* if this one matches, process the option */ if (cur.responseMatches(resp)) { /* it matches - carry out the option */ if (cur.doOption()) { /* * they returned true - they want to continue asking * for more options */ continue promptLoop; } else { /* * they returned nil - they want us to stop asking * for options and return to our caller */ return; } } } /* * If we got this far, it means that we didn't get a valid * option. Display our "invalid option" message, and continue * looping so that we show the prompt again and read a new * option. */ gLibMessages.invalidFinishOption(resp); } } /* * Finish Option class. This is the base class for the abstract objects * representing options offered by finishGame. */ class FinishOption: object /* * The description, as displayed in the list of options. For the * default English messages, this is expected to be a verb phrase in * infinitive form, and should show the keyword accepted as a * response in all capitals: "RESTART", "see some AMUSING things to * do", "show CREDITS". */ desc = "" /* * By default, the item is listed. If you want to create an * invisible option that's accepted but which isn't listed in the * prompt, just set this to nil. Invisible options are sometimes * useful when the output of one option mentions another option; for * example, the CREDITS message might mention a LICENSE command for * displaying the license, so you want to make that command available * without cluttering the prompt with it. */ isListed = true /* our response keyword */ responseKeyword = '' /* * a single character we accept as an alternative to our full * response keyword, or nil if we don't accept a single-character * response */ responseChar = nil /* * Match a response string to this option. Returns true if the * string matches our response, nil otherwise. By default, we'll * return true if the string exactly matches responseKeyword or * exactly matches our responseChar (if that's non-nil), but this * can be overridden to match other strings if desired. By default, * we'll match the response without regard to case. */ responseMatches(response) { /* do all of our work in lower-case */ response = response.toLower(); /* * check for a match the full response keyword or to the single * response character */ return (response == responseKeyword.toLower() || (responseChar != nil && response == responseChar.toLower())); } /* * Carry out the option. This is called when the player enters a * response that matches this option. This routine must perform the * action of the option, then return true to indicate that we should * ask for another option, or nil to indicate that the finishGame() * routine should simply return. */ doOption() { /* tell finishGame() to ask for another option */ return true; } /* * Flag: show the score with the end-of-game announcement. If any * option in the list of finishing options has this flag set, we'll * show the score using the same message that the SCORE command * uses. */ showScoreInFinish = nil ; /* * QUIT option for finishGame. The language-specific code should modify * this to specify the description and response keywords. */ finishOptionQuit: FinishOption doOption() { /* * carry out the Quit action - this will signal a * QuittingException, so this call will never return */ QuitAction.terminateGame(); } ; /* * RESTORE option for finishGame. */ finishOptionRestore: FinishOption doOption() { /* * Try restoring. If this succeeds (i.e., it returns true), tell * the caller to stop looping and to proceed with the game by * returning nil. If this fails, tell the caller to keep looping * by returning true. */ if (RestoreAction.askAndRestore()) { /* * we succeeded, so we're now restored to some prior game * state - terminate any remaining processing in the command * that triggered the end-of-game options */ throw new TerminateCommandException(); } else { /* it failed - tell the caller to keep looping */ return true; } } ; /* * RESTART option for finishGame */ finishOptionRestart: FinishOption doOption() { /* * carry out the restart - this will not return, since we'll * reset the game state and re-enter the game at the restart * entrypoint */ RestartAction.doRestartGame(); } ; /* * START FROM BEGINNING option for failed startup restore. This is just * like finishOptionRestart, but shows a different option name. */ restoreOptionStartOver: finishOptionRestart ; /* * RESTORE ANOTHER GAME option for failed startup restore. This is just * like finishOptionRestore, but shows a different option name. */ restoreOptionRestoreAnother: finishOptionRestore ; /* * UNDO option for finishGame */ finishOptionUndo: FinishOption doOption() { /* try performing the undo */ if (UndoAction.performUndo(nil)) { /* act as though UNDO were the last actual command, for AGAIN */ AgainAction.saveForAgain(gPlayerChar, gPlayerChar, nil, UndoAction); /* * Success - terminate the current command with no further * processing. */ throw new TerminateCommandException(); } else { /* * failure - show a blank line and tell the caller to ask * for another option, since we couldn't carry out this * option */ "<.p>"; return true; } } ; /* * FULL SCORE option for finishGame */ finishOptionFullScore: FinishOption doOption() { /* show a blank line before the score display */ "\b"; /* run the Full Score action */ FullScoreAction.showFullScore(); /* show a paragraph break after the score display */ "<.p>"; /* * this option has now had its full effect, so tell the caller * to go back and ask for a new option */ return true; } /* * by default, show the score with the end-of-game announcement when * this option is included */ showScoreInFinish = true ; /* * Option to show the score in finishGame. This doesn't create a listed * option in the set of offered options, but rather is simply a flag to * finishGame() that the score should be announced along with the * end-of-game announcement message. */ finishOptionScore: FinishOption /* show the score in the end-of-game announcement */ showScoreInFinish = true /* this is not a listed option */ isListed = nil /* this option isn't selectable, so it has no effect */ doOption() { } ; /* * CREDITS option for finishGame */ finishOptionCredits: FinishOption doOption() { /* show a blank line before the credits */ "\b"; /* run the Credits action */ CreditsAction.execSystemAction(); /* show a paragraph break after the credits */ "<.p>"; /* * this option has now had its full effect, so tell the caller * to go back and ask for a new option */ return true; } ; /* * AMUSING option for finishGame */ finishOptionAmusing: FinishOption /* * The game must modify this object to define a doOption method. We * have no built-in way to show a list of amusing things to try, so * if a game wants to offer this option, it must provide a suitable * definition here. (We never offer this option by default, so a * game need not provide a definition if the game doesn't explicitly * offer this option via the 'extra' argument to finishGame()). */ ; /* ------------------------------------------------------------------------ */ /* * The settings user interface. This is a subclass of the Settings * Manager that adds a command-line user interface, particularly to allow * the user to view, save, and load the default settings. * * Our user interface consists mainly of a pair of special commands: SAVE * DEFAULTS and RESTORE DEFAULTS. The SAVE DEFAULTS command tells the * library to write out all of the current settings (at least, all of * those that participate in this framework) to a file. RESTORE DEFAULTS * explicitly reads that same file and puts the stored settings into * effect. Finally, we'll also read the file and activate its stored * settings when we start (or RESTART) the game. * */ settingsUI: settingsManager /* display all of the current settings */ showAll() { local first = true; /* loop over all SettingsItem instances */ forEachInstance(SettingsItem, function(item) { /* include only items that want to participate in the listing */ if (item.includeInListing) { /* add a separator if this isn't the first one */ if (!first) gLibMessages.settingsItemSeparator; /* show this item's description */ item.settingDesc; /* it's no longer the first */ first = nil; } }); } /* * Save settings, and display an acknowledgment message (or an error * message, if necessary) for the user's edification. */ saveSettingsMsg() { /* catch any errors */ try { /* save the settings */ saveSettings(); /* if we got this far, declare success */ gLibMessages.savedDefaults(); } catch (Exception exc) { /* we couldn't open the file */ gLibMessages.defaultsFileWriteError; } } /* * Restore settings, and display an acknowledgment or error message, * as appropriate. */ restoreSettingsMsg() { /* catch any errors */ try { /* restore the settings */ restoreSettings(); /* if we got this far, declare success */ gLibMessages.restoredDefaults(); } catch (SettingsNotSupportedException sns) { /* this interpreter doesn't support the settings file */ gLibMessages.defaultsFileNotSupported; } catch (Exception exc) { /* display other errors */ gLibMessages.defaultsFileReadError(exc); } } ; /* ------------------------------------------------------------------------ */ /* * Utility functions */ /* * nilToList - convert a 'nil' value to an empty list. This can be * useful for mix-in classes that will be used in different inheritance * contexts, since the classes might or might not inherit a base class * definition for list-valued methods such as preconditions. This * provides a usable default for list-valued methods that return nothing * from superclasses. */ nilToList(val) { return (val != nil ? val : []); } /* * partitionList - partition a list into a pair of two lists, the first * containing items that match the predicate 'fn', the second containing * items that don't match 'fn'. 'fn' is a function pointer (usually an * anonymous function) that takes a single argument - a list element - * and returns true or nil. * * The return value is a list with two elements. The first element is a * list giving the elements of the original list for which 'fn' returns * true, the second element is a list giving the elements for which 'fn' * returns nil. * * (Contributed by Tommy Nordgren.) */ partitionList(lst, fn) { local lst1 = lst.subset(fn); local lst2 = lst.subset({x : !fn(x)}); return [lst1, lst2]; } /* * Determine if list a is a subset of list b. a is a subset of b if * every element of a is in b. */ isListSubset(a, b) { /* a can't be a subset if it has more elements than b */ if (a.length() > b.length()) return nil; /* check each element of a to see if it's also in b */ foreach (local cur in a) { /* if this element of a is not in b, a is not a subset of b */ if (b.indexOf(cur) == nil) return nil; } /* * we didn't find any elements of a that are not also in b, so a is a * subset of b */ return true; } frobtads-1.2.3/tads3/lib/adv3/extras.t0000644000175000001440000042336711465246012016640 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - extras: special-purpose object classes * * This module defines classes for specialized simulation objects. * * Portions are based on original work by Eric Eve, incorporated by * permission. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * A "complex" container is an object that can have multiple kinds of * contents simultaneously. For example, a complex container could act * as both a surface, so that some objects are sitting on top of it, and * simultaneously as a container, with objects inside. * * The standard containment model only allows one kind of containment per * container, because the nature of the containment is a feature of the * container itself. The complex container handles multiple simultaneous * containment types by using one or more sub-containers: for example, if * we want to be able to act as both a surface and a regular container, * we use two sub-containers, one of class Surface and one of class * Container, to hold the different types of contents. When we need to * perform an operation specific to a certain containment type, we * delegate the operation to the sub-container of the appropriate type. * * Note that the complex container itself treats its direct contents as * components, so any component parts can be made direct contents of the * complex container object. * * If you want to include objects in your source code that are initially * located within the component sub-containers, define them as directly * within the ComplexContainer object, but give each one a 'subLocation' * property set to the property of the component sub-container that will * initially contain it. For example, here's how you'd place a blanket * inside a washing machine, and a laundry basket on top of it: * *. + washingMachine: ComplexContainer 'washing machine' 'washing machine' *. subContainer: ComplexComponent, Container { etc } *. subSurface: ComplexComponent, Surface { etc } *. ; *. *. ++ Thing 'big cotton blanket' 'blanket' *. subLocation = &subContainer *. ; *. *. ++ Container 'laundry basket' 'laundry basket' *. subLocation = &subSurface *. ; * * The subLocation setting is only used for initialization, and we * automatically set it to nil right after we use it to set up the * initial location. If you want to move something into one of the * sub-containers on the fly, simply refer to the desired component * directly: * * pants.moveInto(washingMachine.subContainer); */ class ComplexContainer: Thing /* * Our inner container, if any. This is a "secret" object (in other * words, it doesn't appear to players as a separate named object) * that we use to store the contents that are meant to be within the * complex container. If this is to be used, it should be set to a * Container object - the most convenient way to do this is by using * the nested object syntax to define a ComplexComponent Container * instance, like so: * * washingMachine: ComplexContainer *. subContainer: ComplexComponent, Container { etc } *. ; * * Note that we use the ComplexComponent class (as well as * Container) for the sub-container object. This makes the * sub-container automatically use the name of its enclosing object * in messages (in this case, the sub-container will use the same * name as the washing machine). * * Note that the sub-containers don't have to be of class * ComplexComponent, but using that class makes your job a little * easier because the class sets the location and naming * automatically. If you prefer to define your sub-containers as * separate objects, not nested in the ComplexContainer's * definition, there's no need to make them ComplexComponents; just * make them ordinary Component objects. * * If this property is left as nil, then we don't have an inner * container. */ subContainer = nil /* * Our inner surface, if any. This is a secret object like the * inner container; this object acts as our surface. */ subSurface = nil /* * Our underside, if any. This is a secret object like the inner * container; this object can act as the space underneath us, or as * our bottom surface. */ subUnderside = nil /* * Our rear surface or container, if any. This is a secret internal * object like the inner container; this object can act as our back * surface, or as the space just behind us. */ subRear = nil /* a list of all of our component objects */ allSubLocations = [subContainer, subSurface, subUnderside, subRear] /* * Show our status. We'll show the status for each of our * sub-objects, so that we list any contents of our sub-container or * sub-surface along with our description. */ examineStatus() { /* if we have a sub-container, show its status */ if (subContainer != nil) subContainer.examineStatus(); /* if we have a sub-surface, show its status */ if (subSurface != nil) subSurface.examineStatus(); /* if we have a sub-rear, show its status */ if (subRear != nil) subRear.examineStatus(); /* if we have a sub-underside, show its status */ if (subUnderside != nil) subUnderside.examineStatus(); } /* * In most cases, the open/closed and locked/unlocked status of a * complex container refer to the status of the sub-container. */ isOpen = (subContainer != nil ? subContainer.isOpen : inherited) isLocked = (subContainer != nil ? subContainer.isLocked : inherited) makeOpen(stat) { if (subContainer != nil) subContainer.makeOpen(stat); else inherited(stat); } makeLocked(stat) { if (subContainer != nil) subContainer.makeLocked(stat); else inherited(stat); } /* * route all commands that treat us as a container to our * sub-container object */ dobjFor(Open) maybeRemapTo(subContainer != nil, Open, subContainer) dobjFor(Close) maybeRemapTo(subContainer != nil, Close, subContainer) dobjFor(LookIn) maybeRemapTo(subContainer != nil, LookIn, subContainer) iobjFor(PutIn) maybeRemapTo(subContainer != nil, PutIn, DirectObject, subContainer) dobjFor(Lock) maybeRemapTo(subContainer != nil, Lock, subContainer) dobjFor(LockWith) maybeRemapTo(subContainer != nil, LockWith, subContainer, IndirectObject) dobjFor(Unlock) maybeRemapTo(subContainer != nil, Unlock, subContainer) dobjFor(UnlockWith) maybeRemapTo(subContainer != nil, UnlockWith, subContainer, IndirectObject) /* route commands that treat us as a surface to our sub-surface */ iobjFor(PutOn) maybeRemapTo(subSurface != nil, PutOn, DirectObject, subSurface) /* route commands that affect our underside to our sub-underside */ iobjFor(PutUnder) maybeRemapTo(subUnderside != nil, PutUnder, DirectObject, subUnderside) dobjFor(LookUnder) maybeRemapTo(subUnderside != nil, LookUnder, subUnderside) /* route commands that affect our rear to our sub-rear-side */ iobjFor(PutBehind) maybeRemapTo(subRear != nil, PutBehind, DirectObject, subRear) dobjFor(LookBehind) maybeRemapTo(subRear != nil, LookBehind, subRear) /* route commands relevant to nested rooms to our components */ dobjFor(StandOn) maybeRemapTo(getNestedRoomDest(StandOnAction) != nil, StandOn, getNestedRoomDest(StandOnAction)) dobjFor(SitOn) maybeRemapTo(getNestedRoomDest(SitOnAction) != nil, SitOn, getNestedRoomDest(SitOnAction)) dobjFor(LieOn) maybeRemapTo(getNestedRoomDest(LieOnAction) != nil, LieOn, getNestedRoomDest(LieOnAction)) dobjFor(Board) maybeRemapTo(getNestedRoomDest(BoardAction) != nil, Board, getNestedRoomDest(BoardAction)) dobjFor(Enter) maybeRemapTo(getNestedRoomDest(EnterAction) != nil, Enter, getNestedRoomDest(EnterAction)) /* map GET OUT/OFF to whichever complex component we're currently in */ dobjFor(GetOutOf) maybeRemapTo(getNestedRoomSource(gActor) != nil, GetOutOf, getNestedRoomSource(gActor)) dobjFor(GetOffOf) maybeRemapTo(getNestedRoomSource(gActor) != nil, GetOffOf, getNestedRoomSource(gActor)) /* * Get the destination for nested travel into this object. By * default, we'll look at the sub-container and sub-surface * components to see if either is a nested room, and if so, we'll * return that. The surface takes priority if both are nested rooms. * * You can override this to differentiate by verb, if desired; for * example, you could have SIT ON and LIE ON refer to the sub-surface * component, while ENTER and BOARD refer to the sub-container * component. * * Note that if you do need to override this method to distinguish * between a sub-container ("IN") and a sub-surface ("ON") for nested * room purposes, there's a subtlety to watch out for. The English * library maps "sit on" and "sit in" to the single Action SitOn; * likewise with "lie in/on" for LieOn and "stand in/on" for StandOn. * If you're distinguishing the sub-container from the sub-surface, * you'll probably want to distinguish SIT IN from SIT ON (and * likewise for LIE and STAND). Fortunately, even though the action * class is the same for both phrasings, you can still find out * exactly which preposition the player typed using * action.getEnteredVerbPhrase(). */ getNestedRoomDest(action) { /* * check the sub-surface first to see if it's a nested room; * failing that, check the sub-container; failing that, we don't * have a suitable component destination */ if (subSurface != nil && subSurface.ofKind(NestedRoom)) return subSurface; else if (subContainer != nil && subContainer.ofKind(NestedRoom)) return subContainer; else return nil; } /* * Get the source for nested travel out of this object. This is used * for GET OUT OF - we figure out which nested room component * the actor is in, so that we can remap the command to GET OUT OF * . */ getNestedRoomSource(actor) { /* figure out which child the actor is in */ foreach (local chi in allSubLocations) { if (chi != nil && actor.isIn(chi)) return chi; } /* the actor doesn't appear to be in one of our component locations */ return nil; } /* * Get a list of objects suitable for matching ALL in TAKE ALL FROM * . By default, if we have a sub-surface and/or * sub-container, we return everything in scope that's inside either * one of those. Otherwise, if we have a sub-rear-surface and/or an * underside, we'll return everything from those. */ getAllForTakeFrom(scopeList) { local containers; /* * Make a list of the containers in which we're going to look. * If we have a sub-container or sub-surface, look only in those. * Otherwise, if we have a rear surface or underside, look in * those. */ containers = []; if (subContainer != nil) containers += subContainer; if (subSurface != nil) containers += subSurface; if (containers == []) { if (subRear != nil) containers += subRear; if (subUnderside != nil) containers += subUnderside; } /* * return the list of everything in scope that's directly in one * of the selected containers, but isn't a component of its * direct container */ return scopeList.subset( {x: (x != self && containers.indexOf(x) == nil && containers.indexWhich( {c: x.isDirectlyIn(c) && !x.isComponentOf(c)}) != nil)}); } /* * Add an object to my contents. If the object has a subLocation * setting, take it as indicating which of my subcontainers is to * contain the object. */ addToContents(obj) { local sub; /* * if the object has a subLocation, add it to my appropriate * component object; if not, add to my own contents as usual */ if ((sub = obj.subLocation) != nil) { /* * It specifies a subLocation - add it to the corresponding * component's contents. Note that subLocation is a property * pointer - &subContainer for my container component, * &subSurface for my surface component, etc. */ self.(sub).addToContents(obj); /* * The object's present location is merely for set-up * purposes, so that the '+' object definition notation can * be used to give the object its initial location. The * object really wants to be in the sub-container, to whose * contents list we've just added it. Set its location to * the sub-container. */ obj.location = self.(sub); /* * Now that we've moved the object into its sub-location, * forget the subLocation setting, since this property is * only for initialization. */ obj.subLocation = nil; } else { /* there's no subLocation, so use the default handling */ inherited(obj); } } /* * If we have any SpaceOverlay children, abandon the contents of the * overlaid spaces as needed. */ mainMoveInto(newCont) { /* * If any of our components are SpaceOverlays, notify them. We * only worry about the rear and underside components, since it's * never appropriate for our container and surface components to * act as space overlays. */ notifyComponentOfMove(subRear); notifyComponentOfMove(subUnderside); /* do the normal work */ inherited(newCont); } /* * if we're being pushed into a new location (as a PushTraveler), * abandon the contents of any SpaceOverlay components */ beforeMovePushable(traveler, connector, dest) { /* * notify our SpaceOverlay components that we're being moved, if * we're going to end up in a new location */ if (dest != location) { /* notify our rear and underside components of the move */ notifyComponentOfMove(subRear); notifyComponentOfMove(subUnderside); } /* do the normal work */ inherited(traveler, connector, dest); } /* * if the given component is a SpaceOverlay, notify it that we're * moving, so that it can abandon its contents as needed */ notifyComponentOfMove(sub) { /* if it's a SpaceOverlay, abandon its contents if necessary */ if (sub != nil && sub.ofKind(SpaceOverlay)) sub.abandonContents(); } /* pass bag-of-holding operations to our sub-container */ tryPuttingObjInBag(target) { /* if we have a subcontainer, let it handle the operation */ return (subContainer != nil ? subContainer.tryPuttingObjInBag(target) : nil); } /* pass implicit PUT x IN self operations to our subcontainer */ tryMovingObjInto(obj) { /* if we have a subcontainer, let it handle the operation */ return (subContainer != nil ? subContainer.tryMovingObjInto(obj) : nil); } ; /* * we don't actually define any subLocation property values anywhere, so * declare it to make sure the compiler knows it's a property name */ property subLocation; /* * A component object of a complex container. This class can be used as * a mix-in for sub-objects of a complex container (the subContainer or * subSurface) defined as nested objects. * * This class is based on Component, which is suitable for complex * container sub-objects because it makes them inseparable from the * complex container. It's also based on NameAsParent, which makes the * object automatically use the same name (in messages) as the lexical * parent object. This is usually what one wants for a sub-object of a * complex container, because it makes the sub-object essentially * invisible to the user by referring to the sub-object in messages as * though it were the complex container itself: "The washing machine * contains...". * * This class also automatically initializes our location to our lexical * parent, during the pre-initialization process. Any of these that are * dynamically created at run-time (using 'new') must have their * locations set manually, because initializeLocation() won't be called * automatically in those cases. */ class ComplexComponent: Component, NameAsParent initializeLocation() { /* set our location to our lexical parent */ location = lexicalParent; /* inherit default so we initialize our container's 'contents' list */ inherited(); } /* * Get our "identity" object. We take our identity from our parent * object, if we have one. Note that our identity isn't simply our * parent, but rather is our parent's identity, recursively defined. */ getIdentityObject() { return (location != nil ? location.getIdentityObject() : self); } /* don't participate in 'all', since we're a secret internal object */ hideFromAll(action) { return true; } /* * In case this component is being used to implement a nested room of * some kind (a platform, booth, etc), use the complex container's * location as the staging location. Normally our staging location * would be our direct container, but as with other aspects of * complex containers, the container/component are meant to act as a * single combined object, so we'd want to bypass the complex * container and move directly between the enclosing location and * 'self'. */ stagingLocations = [lexicalParent.location] ; /* * A container door. This is useful for cases where you want to create * the door to a container as a separate object in its own right. */ class ContainerDoor: Component /* * In most cases, you should create a ContainerDoor as a component of * a ComplexContainer. It's usually necessary to use a * ComplexContainer in order to use a door, since the door has to go * somewhere, and it can't go inside the container it controls * (because if it were inside, it wouldn't be accessible when the * container is closed). * * By default, we assume that our immediate location is a complex * container, and its subContainer is the actual container for which * we're the door. You can override this property to create a * different relationship if necessary. */ subContainer = (location.subContainer) /* we're open if our associated sub-container is open */ isOpen = (subContainer.isOpen) /* our status description mentions our open status */ examineStatus() { /* add our open status */ say(isOpen ? gLibMessages.currentlyOpen : gLibMessages.currentlyClosed); /* add the base class behavior */ inherited(); } /* looking in or behind a door is like looking inside the container */ dobjFor(LookIn) remapTo(LookIn, subContainer) dobjFor(LookBehind) remapTo(LookIn, subContainer) /* door-like operations on the door map to the container */ dobjFor(Open) remapTo(Open, subContainer) dobjFor(Close) remapTo(Close, subContainer) dobjFor(Lock) remapTo(Lock, subContainer) dobjFor(LockWith) remapTo(LockWith, subContainer, IndirectObject) dobjFor(Unlock) remapTo(Unlock, subContainer) dobjFor(UnlockWith) remapTo(UnlockWith, subContainer, IndirectObject) ; /* ------------------------------------------------------------------------ */ /* * A "space overlay" is a special type of container whose contents are * supposed to be adjacent to the container object (i.e., self), but are * not truly contained in the usual sense. This is used to model spatial * relationships such as UNDER and BEHIND, which aren't directly * supported in the normal containment model. * * The special feature of a space overlay is that the contents aren't * truly attached to the container object, so they don't move with it the * way that the contents of an ordinary container do. For example, * suppose we have a space overlay representing a bookcase and the space * behind it, so that we can hide a painting behind the bookcase: in this * case, moving the bookcase should leave the painting where it was, * because it was just sitting there in that space. In the real world, * of course, the painting was sitting on the floor all along, so moving * the bookcase would have no effect on it; but our spatial relationship * model isn't quite as good as reality's, so we have to resort to an * extra fix-up step. Specifically, when we move a space overlay, we * always check to see if its contents need to be relocated to the place * where they were really supposed to be all along. */ class SpaceOverlay: BulkLimiter /* * If we move this object, the objects we contain might stay put * rather than moving along with the container. For example, if we * represent the space behind a bookcase, moving the bookcase would * leave objects that were formerly behind the bookcase just sitting * on the floor (or attached to the wall, or whatever). */ mainMoveInto(newContainer) { /* check to see if our objects need to be left behind */ abandonContents(); /* now do the normal work */ inherited(newContainer); } /* * when we're being pushed to a new location via push-travel, abandon * our contents before we're moved */ beforeMovePushable(traveler, connector, dest) { /* check to see if our objects need to be left behind */ if (dest != getIdentityObject().location) abandonContents(); /* do the normal work */ inherited(traveler, connector, dest); } /* * abandonLocation is where the things under me end up when I'm * moved. * * An Underside or RearContainer represents an object that has a * space underneath or behind it, respectively, but the space itself * isn't truly part of the container object (i.e., self). This * means that when the container moves, the objects under/behind it * shouldn't move. For example, if there's a box under a bed, * moving the bed out of the room should leave the box sitting on * the floor where the bed used to be. * * By default, our abandonLocation is simply the location of our * "identity object" - that is, the location of our nearest * enclosing object that isn't a component. * * This can be overridden if the actual abandonment location should * be somewhere other than our assembly location. In addition, you * can set this to nil to indicate that objects under/behind me will * NOT be abandoned when I move; instead, they'll simply stay with * me, as though they're attached to my underside/back surface. */ abandonLocation = (getIdentityObject().location) /* * By default we list our direct contents the first time we're * moved, and ONLY the first time. If alwaysListOnMove is * overridden to true, then we'll list our contents EVERY time we're * moved. If neverListOnMove is set to true, then we'll NEVER list * our contents automatically when moved; this can be used in cases * where the game wants to produce its own listing explicitly, * rather than using the default listing we generate. (Obviously, * setting both 'always' and 'never' is meaningless, but in case * you're wondering, 'never' overrides 'always' in this case.) * * Setting abandonLocation to nil overrules alwaysListOnMove: if * there's no abandonment, then we consider nothing to be revealed * when we're moved, since my contents move along with me. */ alwaysListOnMove = nil neverListOnMove = nil /* * The lister we use to describe the objects being revealed when we * move the SpaceOverlay object and abandon the contents. Each * concrete kind of SpaceOverlay must provide a lister that uses * appropriate language; the list should be roughly of the form * "Moving the armoire reveals a rusty can underneath." Individual * objects can override this to customize the message further. */ abandonContentsLister = nil /* * Abandon my contents when I'm moved. This is called whenever we're * moved to a new location, to take care of leaving behind the * objects that were formerly under me. * * We'll move my direct contents into abandonLocation, unless that's * set to nil. We don't move any Component objects within me, since * we assume those to be attached. */ abandonContents() { local dest; /* * if there's no abandonment location, our contents move with us, * so there's nothing to do */ if ((dest = abandonLocation) == nil) return; /* * If we've never been moved before, or we always reveal my * contents when moved, list our contents now. In any case, if * we *never* list on move, don't generate the listing. */ if ((alwaysListOnMove || !getIdentityObject().moved) && !neverListOnMove) { local marker1, marker2; /* * We want to generate a listing of what is revealed by * moving the object, which we can do by generating a * listing of what we would normally see by looking in the * overlay interior. We want the listing as it stands now, * but in most cases, we don't actually want to generate the * list quite yet, because we want the action that's moving * the object to complete and show all of its messages first. * * However, if the action leaves the actor in a new * location, do generate the listing before the rest of the * action output, since the listing won't make any sense * after we've moved to (and displayed the description of) * the new location. * * To accomplish all of this, generate the listing now, * before the rest of the action output, but insert special * report markers into the transcript before and after the * listing. Then, register to receive after-action * notification; at the end of the action, we'll go back and * move the range of transcript output between the markers * to the end of the command's transcript entries, if we * haven't moved to a new room. * * One final complication: we don't want our listing here to * hide any default report from the main command, so run it * as a sub-action. A sub-action doesn't override the * visibility of its parent's default report. */ /* first, add a marker to the transcript before the listing */ marker1 = gTranscript.addMarker(); /* generate the listing, using a generic sub-action context */ withActionEnv(Action, gActor, {: listContentsForMove() }); /* add another transcript marker after the listing */ marker2 = gTranscript.addMarker(); /* * create our special handler object to receive notification * at the end of the command - it'll move the reports to the * end of the command output if need be */ new SpaceOverlayAbandonFinisher(marker1, marker2); } /* now move my non-Component contents to the abandonment location */ foreach(obj in contents) { /* if it's not a component, move it */ if(!obj.ofKind(Component)) obj.moveInto(dest); } } /* * My weight does NOT include my "contents" if we abandon our * contents on being moved. Our contents are not attached to us as * they are in a normal sort of container; instead, they're merely * colocated, so when we're moved, that colocation relationship ends. */ getWeight() { /* * if we abandon our contents on being moved, our weight doesn't * include them, because they're not attached; otherwise, they do * act like they're attached, and hence must be included in our * weight as for any ordinary container */ if (abandonLocation != nil) { /* * our contents are not included in our weight, so our total * weight is simply our own intrinsic weight */ return weight; } else { /* our contents are attached, so include their weight as normal */ return inherited(); } } /* * List our contents for moving the object. By default, we examine * our interior using our abandonContentsLister. */ listContentsForMove() { /* examine our contents with the abandonContentsLister */ examineInteriorWithLister(abandonContentsLister); } ; /* * Space Overlay Abandon Finisher - this is an internal object that we * create in SpaceOverlay.abandonContents(). Its purpose is to receive * an afterAction notification and clean up the report order if * necessary. */ class SpaceOverlayAbandonFinisher: object construct(m1, m2) { /* remember the markers */ marker1 = m1; marker2 = m2; /* remember the actor's starting location */ origLocation = gActor.location; /* register for afterAction notification */ gAction.addBeforeAfterObj(self); } /* the transcript markers identifying the listing reports */ marker1 = nil marker2 = nil /* the actor's location at the time we generated the listing */ origLocation = nil /* receive our after-action notification */ afterAction() { /* * If the actor hasn't changed locations, move the reports we * generated for the listing to the end of the transcript. */ if (gActor.location == origLocation) gTranscript.moveRangeAppend(marker1, marker2); } ; /* * An "underside" is a special type of container that describes its * contents as being under the object. This is appropriate for objects * that have a space underneath, such as a bed or a table. */ class Underside: SpaceOverlay /* * Can actors put new objects under self, using the PUT UNDER * command? By default, we allow it. Override this property to nil * if new objects cannot be added by player commands. */ allowPutUnder = true /* we need to LOOK UNDER this object to see its contents */ nestedLookIn() { nestedAction(LookUnder, self); } /* use custom contents listers, for our special "under" wording */ contentsLister = undersideContentsLister descContentsLister = undersideDescContentsLister lookInLister = undersideLookUnderLister inlineContentsLister = undersideInlineContentsLister abandonContentsLister = undersideAbandonContentsLister /* customize the message for taking something from that's not under me */ takeFromNotInMessage = &takeFromNotUnderMsg /* customize the message indicating another object is already in me */ circularlyInMessage = &circularlyUnderMsg /* message phrase for objects put under me */ putDestMessage = &putDestUnder /* message when we don't have room to put another object under me */ tooFullMsg = &undersideTooFull /* message when an object is too large (all by itself) to fit under me */ tooLargeForContainerMsg = &tooLargeForUndersideMsg /* can't put self under self */ cannotPutInSelfMsg = &cannotPutUnderSelfMsg /* can't put something under me when it's already under me */ alreadyPutInMsg = &alreadyPutUnderMsg /* our implied containment verb is PUT UNDER */ tryMovingObjInto(obj) { return tryImplicitAction(PutUnder, obj, self); } /* -------------------------------------------------------------------- */ /* * Handle putting things under me */ iobjFor(PutUnder) { verify() { /* use the standard put-in-interior verification */ verifyPutInInterior(); } check() { /* only allow it if PUT UNDER commands are allowed */ if (!allowPutUnder) { reportFailure(&cannotPutUnderMsg); exit; } } action() { /* move the direct object onto me */ gDobj.moveInto(self); /* issue our default acknowledgment */ defaultReport(&okayPutUnderMsg); } } /* * Looking "under" a surface simply shows the surface's contents. */ dobjFor(LookUnder) { verify() { } action() { examineInterior(); } } ; /* * A special kind of Underside that only accepts specific contents. */ class RestrictedUnderside: RestrictedHolder, Underside /* * A message that explains why the direct object can't be put under * this object. In most cases, the rather generic default message * should be overridden to provide a specific reason that the dobj * can't be put under me. The rejected object is provided as a * parameter in case the message needs to vary by object, but we * ignore this and just use a single blanket failure message by * default. */ cannotPutUnderMsg(obj) { return &cannotPutUnderRestrictedMsg; } /* override PutUnder to enforce our contents restriction */ iobjFor(PutUnder) { check() { checkPutDobj(&cannotPutUnderMsg); } } ; /* * A "rear container" is similar to an underside: it models the space * behind an object. */ class RearContainer: SpaceOverlay /* * Can actors put new objects behind self, using the PUT BEHIND * command? By default, we allow it. Override this property to nil * if new objects cannot be added by player commands. */ allowPutBehind = true /* we need to LOOK BEHIND this object to see its contents */ nestedLookIn() { nestedAction(LookBehind, self); } /* use custom contents listers */ contentsLister = rearContentsLister descContentsLister = rearDescContentsLister lookInLister = rearLookBehindLister inlineContentsLister = rearInlineContentsLister abandonContentsLister = rearAbandonContentsLister /* the message for taking things from me that aren't behind me */ takeFromNotInMessage = &takeFromNotBehindMsg /* * my message indicating that another object x cannot be put into me * because I'm already in x */ circularlyInMessage = &circularlyBehindMsg /* message phrase for objects put under me */ putDestMessage = &putDestBehind /* message when we're too full for another object */ tooFullMsg = &rearTooFullMsg /* message when object is too large to fit behind me */ tooLargeForContainerMsg = &tooLargeForRearMsg /* customize the verification messages */ cannotPutInSelfMsg = &cannotPutBehindSelfMsg alreadyPutInMsg = &alreadyPutBehindMsg /* our implied containment verb is PUT BEHIND */ tryMovingObjInto(obj) { return tryImplicitAction(PutBehind, obj, self); } /* -------------------------------------------------------------------- */ /* * Handle the PUT UNDER command */ iobjFor(PutBehind) { verify() { verifyPutInInterior(); } check() { /* only allow it if PUT BEHIND commands are allowed */ if (!allowPutBehind) { reportFailure(&cannotPutBehindMsg); exit; } } action() { /* move the direct object behind me */ gDobj.moveInto(self); /* issue our default acknowledgment */ defaultReport(&okayPutBehindMsg); } } /* * Looking "behind" a surface simply shows the surface's contents. */ dobjFor(LookBehind) { verify() { } action() { examineInterior(); } } ; /* * A special kind of RearContainer that only accepts specific contents. */ class RestrictedRearContainer: RestrictedHolder, RearContainer /* * A message that explains why the direct object can't be put behind * this object. In most cases, the rather generic default message * should be overridden to provide a specific reason that the dobj * can't be put behind me. The rejected object is provided as a * parameter in case the message needs to vary by object, but we * ignore this and just use a single blanket failure message by * default. */ cannotPutBehindMsg(obj) { return &cannotPutBehindRestrictedMsg; } /* override PutBehind to enforce our contents restriction */ iobjFor(PutBehind) { check() { checkPutDobj(&cannotPutBehindMsg); } } ; /* * A "rear surface" is essentially the same as a "rear container," but * models the contents as being attached to the back of the object rather * than merely sitting behind it. * * The only practical difference between the "container" and the * "surface" is that moving a surface moves its contents along with it, * whereas moving a container abandons the contents, leaving them behind * where the container used to be. */ class RearSurface: RearContainer /* * We're a surface, not a space, so our contents stay attached when * we move. */ abandonLocation = nil ; /* * A restricted-contents RearSurface */ class RestrictedRearSurface: RestrictedHolder, RearSurface /* explain the problem */ cannotPutBehindMsg(obj) { return &cannotPutBehindRestrictedMsg; } /* override PutBehind to enforce our contents restriction */ iobjFor(PutBehind) { check() { checkPutDobj(&cannotPutBehindMsg); } } ; /* ------------------------------------------------------------------------ */ /* * A "stretchy container." This is a simple container subclass whose * external bulk changes according to the bulks of the contents. */ class StretchyContainer: Container /* * Our minimum bulk. This is the minimum bulk we'll report, even * when the aggregate bulks of our contents are below this limit. */ minBulk = 0 /* get my total external bulk */ getBulk() { local tot; /* start with my own intrinsic bulk */ tot = bulk; /* add the bulk contribution from my contents */ tot += getBulkForContents(); /* return the total, but never less than the minimum */ return tot >= minBulk ? tot : minBulk; } /* * Calculate the contribution to my external bulk of my contents. * The default for a stretchy container is to conform exactly to the * contents, as though the container weren't present at all, hence * we simply sum the bulks of our contents. Subclasses can override * this to define other aggregate bulk effects as needed. */ getBulkForContents() { local tot; /* sum the bulks of the items in our contents */ tot = 0; foreach (local cur in contents) tot += cur.getBulk(); /* return the total */ return tot; } /* * Check what happens when a new object is inserted into my * contents. This is called with the new object already tentatively * added to my contents, so we can examine our current status to see * if everything works. * * Since we can change our own size when a new item is added to our * contents, we'll trigger a full bulk change check. */ checkBulkInserted(insertedObj) { /* * inherit the normal handling to ensure that the new object * fits within this container */ inherited(insertedObj); /* * since we can change our own shape when items are added to our * contents, trigger a full bulk check on myself */ checkBulkChange(); } /* * Check a bulk change of one of my direct contents. Since my own * bulk changes whenever the bulk of one of my contents changes, we * must propagate the bulk change of our contents as a change in our * own bulk. */ checkBulkChangeWithin(changingObj) { /* * Do any inherited work, in case we have a limit on our own * internal bulk. */ inherited(changingObj); /* * This might cause a change in my own bulk, since my bulk * depends on the bulks of my contents. When this is called, * obj is already set to indicate its new bulk; since we * calculate our own bulk by looking at our contents' bulks, * this means that our own getBulk will now report the latest * value including obj's new bulk. */ checkBulkChange(); } ; /* ------------------------------------------------------------------------ */ /* * "Bag of Holding." This is a mix-in that actively moves items from the * holding actor's direct inventory into itself when the actor's hands * are too full. * * The bag of holding offers a solution to the conflict between "realism" * and playability. On the one hand, in real life, you can only hold so * many items at once, so at first glance it seems a simulation ought to * have such a limit in order to be more realistic. On the other hand, * most players justifiably hate having to deal with a carrying limit, * because it forces the player to spend a lot of time doing tedious * inventory management. * * The Bag of Holding is a compromise solution. The concept is borrowed * from live role-playing games, where it's usually a magical item that * can hold objects of unlimited size and weight, thereby allowing * characters to transport impossibly large objects. In text IF, a bag * of holding isn't usually magical - it's usually just something like a * large backpack, or a trenchcoat with lots of pockets. And it usually * isn't meant as a solution to an obvious puzzle; rather, it's meant to * invisibly prevent inventory management from becoming a puzzle in the * first place, by shuffling objects out of the PC's hands automatically * to free up space as needed. * * This Bag of Holding implementation works by automatically moving * objects from an actor's hands into the bag object, whenever the actor * needs space to pick up a new item. Whenever an action has a * "roomToHoldObj" precondition, the precondition will automatically look * for a BagOfHolding object within the actor's inventory, and then move * as many items as necessary from the actor's hands to the bag. */ class BagOfHolding: object /* * Get my bags of holding. Since we are a bag of holding, we'll add * ourselves to the vector, then we'll inherit the normal handling * to pick up our contents. */ getBagsOfHolding(vec) { /* we're a bag of holding */ vec.append(self); /* inherit the normal handling */ inherited(vec); } /* * Get my "affinity" for the given object. This is an indication of * how strongly this bag wants to contain the object. The affinity * is a number in arbitrary units; higher numbers indicate stronger * affinities. An affinity of zero means that the bag does not want * to contain the object at all. * * The purpose of the affinity is to support specialized holders * that are designed to hold only specific types of objects, and * allow these specialized holders to implicitly gather their * specific objects. For example, a key ring might only hold keys, * so it would have a high affinity for keys and a zero affinity for * everything else. A lunchbox might have a higher affinity for * things like sandwiches than for anything else, but might be * willing to serve as a general container for other small items as * well. * * The units of affinity are arbitrary, but the library uses the * following values for its own classes: * * 0 - no affinity at all; the bag cannot hold the object * * 50 - willing to hold the object, but not of the preferred type * * 100 - default affinity; willing and able to hold the object, but * just as willing to hold most other things * * 200 - special affinity; this object is of a type that we * especially want to hold * * We intentionally space these loosely so that games can use * intermediate levels if desired. * * When we are looking for bags of holding to consolidate an actor's * directly-held inventory, note that we always move the object with * the highest bag-to-object affinity out of all of the objects * under consideration. So, if you want to give a particular kind * of bag priority so that the library uses that bag before any * other bag, make this routine return a higher affinity for the * bag's objects than any other bags do. * * By default, we'll return the default affinity of 100. * Specialized bags that don't hold all types of objects must * override this to return zero for objects they can't hold. */ affinityFor(obj) { /* * my affinity for myself is zero, for obvious reasons; for * everything else, use the default affinity */ return (obj == self ? 0 : 100); } ; /* ------------------------------------------------------------------------ */ /* * Keyring - a place to stash keys * * Keyrings have some special properties: * * - A keyring is a bag of holding with special affinity for keys. * * - A keyring can only contain keys. * * - Keys are considered to be on the outside of the ring, so a key can * be used even if attached to the keyring (in other words, if the ring * itself is held, a key attached to the ring is also considered held). * * - If an actor in possession of a keyring executes an "unlock" command * without specifying what key to use, we will automatically test each * key on the ring to find the one that works. * * - When an actor takes one of our keys, and the actor is in possession * of this keyring, we'll automatically attach the key to the keyring * immediately. */ class Keyring: BagOfHolding, Thing /* lister for showing our contents in-line as part of a list entry */ inlineContentsLister = keyringInlineContentsLister /* lister for showing our contents as part of "examine" */ descContentsLister = keyringExamineContentsLister /* * Determine if a key fits our keyring. By default, we will accept * any object of class Key. However, subclasses might want to * override this to associate particular keys with particular * keyrings rather than having a single generic keyring. To allow * only particular keys onto this keyring, override this routine to * return true only for the desired keys. */ isMyKey(key) { /* accept any object of class Key */ return key.ofKind(Key); } /* we have high affinity for our keys */ affinityFor(obj) { /* * if the object is one of my keys, we have high affinity; * otherwise we don't accept it at all */ if (isMyKey(obj)) return 200; else return 0; } /* implicitly put a key on the keyring */ tryPuttingObjInBag(target) { /* we're a container, so use "put in" to get the object */ return tryImplicitActionMsg(&announceMoveToBag, PutOn, target, self); } /* on taking the keyring, attach any loose keys */ dobjFor(Take) { action() { /* do the normal work */ inherited(); /* get the list of loose keys */ local lst = getLooseKeys(gActor); /* consider only the subset that are my valid keys */ lst = lst.subset({x: isMyKey(x)}); /* if there are any, move them onto the keyring */ if (lst.length() != 0) { /* put each loose key on the keyring */ foreach (local cur in lst) cur.moveInto(self); /* announce what happened */ extraReport(&movedKeysToKeyringMsg, self, lst); } } } /* * Get the loose keys in the given actor's possession. On taking the * keyring, we'll attach these loose keys to the keyring * automatically. By default, we return any keys the actor is * directly holding. */ getLooseKeys(actor) { return actor.contents; } /* allow putting a key on the keyring */ iobjFor(PutOn) { /* we can only put keys on keyrings */ verify() { /* we'll only allow our own keys to be attached */ if (gDobj == nil) { /* * we don't know the actual direct object yet, but we * can at least check to see if any of the possible * dobj's is my kind of key */ if (gTentativeDobj.indexWhich({x: isMyKey(x.obj_)}) == nil) illogical(&objNotForKeyringMsg); } else if (!isMyKey(gDobj)) { /* the dobj isn't a valid key for this keyring */ illogical(&objNotForKeyringMsg); } } /* put a key on me */ action() { /* move the key into me */ gDobj.moveInto(self); /* show the default "put on" response */ defaultReport(&okayPutOnMsg); } } /* treat "attach x to keyring" as "put x on keyring" */ iobjFor(AttachTo) remapTo(PutOn, DirectObject, self) /* treat "detach x from keyring" as "take x from keyring" */ iobjFor(DetachFrom) remapTo(TakeFrom, DirectObject, self) /* receive notification before an action */ beforeAction() { /* * Note whether or not we want to consider moving the direct * object to the keyring after a "take" command. We will * consider doing so only if the direct object isn't already on * the keyring - if it is, we don't want to move it back right * after removing it, obviously. * * Skip the implicit keyring attachment if the current command * is implicit, because they must be doing something that * requires holding the object, in which case taking it is * incidental. It could be actively annoying to attach the * object to the keyring in such cases - for example, if the * command is "put key on keyring," attaching it as part of the * implicit action would render the explicit command redundant * and cause it to fail. */ moveAfterTake = (!gAction.isImplicit && gDobj != nil && !gDobj.isDirectlyIn(self)); } /* flag: consider moving to keyring after this "take" action */ moveAfterTake = nil /* receive notification after an action */ afterAction() { /* * If the command was "take", and the direct object was a key, * and the actor involved is holding the keyring and can touch * it, and the command succeeded in moving the key to the * actor's direct inventory, then move the key onto the keyring. * Only consider this if we decided to during the "before" * notification. */ if (moveAfterTake && gActionIs(Take) && isMyKey(gDobj) && isIn(gActor) && gActor.canTouch(self) && gDobj.isDirectlyIn(gActor)) { /* move the key to me */ gDobj.moveInto(self); /* * Mention what we did. If the only report for this action * so far is the default 'take' response, then use the * combined taken-and-attached message. Otherwise, append * our 'attached' message, which is suitable to use after * other messages. */ if (gTranscript.currentActionHasReport( {x: (x.ofKind(CommandReportMessage) && x.messageProp_ != &okayTakeMsg)})) { /* * we have a non-default message already, so add our * message indicating that we added the key to the * keyring */ reportAfter(&movedKeyToKeyringMsg, self); } else { /* use the combination taken-and-attached message */ mainReport(&takenAndMovedToKeyringMsg, self); } } } /* find among our keys a key that works the direct object */ findWorkingKey(lock) { /* try each key on the keyring */ foreach (local key in contents) { /* * if this is the key that unlocks the lock, replace the * command with 'unlock lock with key' */ if (lock.keyFitsLock(key)) { /* note that we tried keys and found the right one */ extraReport(&foundKeyOnKeyringMsg, self, key); /* return the key */ return key; } } /* we didn't find the right key - indicate failure */ reportFailure(&foundNoKeyOnKeyringMsg, self); return nil; } /* * Append my directly-held contents to a vector when I'm directly * held. We consider all of the keys on the keyring to be * effectively at the same containment level as the keyring, so if * the keyring is held, so are its attached keys. */ appendHeldContents(vec) { /* append all of our contents, since they're held when we are */ vec.appendUnique(contents); } /* * Announce myself as a default object for an action. * * Do not announce a keyring as a default for "lock with" or "unlock * with". Although we can use a keyring as the indirect object of a * lock/unlock command, we don't actually do the unlocking with the * keyring; so, when we're chosen as the default, suppress the * announcement, since it would imply that we're being used to lock * or unlock something. */ announceDefaultObject(whichObj, action, resolvedAllObjects) { /* if it's not a lock-with or unlock-with, use the default message */ if (!action.ofKind(LockWithAction) && !action.ofKind(UnlockWithAction)) { /* for anything but our special cases, use the default handling */ return inherited(whichObj, action, resolvedAllObjects); } /* use no announcement */ return ''; } /* * Allow locking or unlocking an object with a keyring. This will * automatically try each key on the keyring to see if it fits the * lock. */ iobjFor(LockWith) { verify() { /* if we don't have any keys, we're not locking anything */ if (contents.length() == 0) illogical(&cannotLockWithMsg); /* * if we know the direct object, and we don't have any keys * that are plausible for the direct object, we're an * unlikely match */ if (gDobj != nil) { local foundPlausibleKey; /* * try each of my keys to see if it's plausible for the * direct object */ foundPlausibleKey = nil; foreach (local cur in contents) { /* * if this is a plausible key, note that we have at * least one plausible key */ if (gDobj.keyIsPlausible(cur)) { /* note that we found a plausible key */ foundPlausibleKey = true; /* no need to look any further - one is good enough */ break; } } /* * If we didn't find a plausible key, we're an unlikely * match. * * If we did find a plausible key, increase the * likelihood that this is the indirect object so that * it's greater than the likelihood for any random key * that's plausible for the lock (which has the default * likelihood of 100), but less than the likelihood of * the known good key (which is 150). This will cause a * keyring to be taken as a default over any ordinary * key, but will cause the correct key to override the * keyring as the default if the correct key is known to * the player already. */ if (foundPlausibleKey) logicalRank(140, 'keyring with plausible key'); else logicalRank(50, 'no plausible key'); } } action() { local key; /* * Try finding a working key. If we find one, replace the * command with 'lock with , so that we have the * full effect of the 'lock with' command using the key * itself. */ if ((key = findWorkingKey(gDobj)) != nil) replaceAction(LockWith, gDobj, key); } } iobjFor(UnlockWith) { /* verify the same as for LockWith */ verify() { /* if we don't have any keys, we're not unlocking anything */ if (contents.length() == 0) illogical(&cannotUnlockWithMsg); else verifyIobjLockWith(); } action() { local key; /* * if we can find a working key, run an 'unlock with' action * using the key */ if ((key = findWorkingKey(gDobj)) != nil) replaceAction(UnlockWith, gDobj, key); } } ; /* * Key - this is an object that can be used to unlock things, and which * can be stored on a keyring. The key that unlocks a lock is * identified with a property on the lock, not on the key. */ class Key: Thing /* * A key on a keyring that is being held by an actor is considered * to be held by the actor, since the key does not have to be * removed from the keyring in order to be manipulated as though it * were directly held. */ isHeldBy(actor) { /* * if I'm on a keyring, I'm being held if the keyring is being * held; otherwise, use the default definition */ if (location != nil && location.ofKind(Keyring)) return location.isHeldBy(actor); else return inherited(actor); } /* * Try making the current command's actor hold me. If we're on a * keyring, we'll simply try to make the keyring itself held, rather * than taking the key off the keyring; otherwise, we'll inherit the * default behavior to make ourselves held. */ tryHolding() { if (location != nil && location.ofKind(Keyring)) return location.tryHolding(); else return inherited(); } /* -------------------------------------------------------------------- */ /* * Action processing */ /* treat "detach key" as "take key" if it's on a keyring */ dobjFor(Detach) { verify() { /* if I'm not on a keyring, there's nothing to detach from */ if (location == nil || !location.ofKind(Keyring)) illogical(&keyNotDetachableMsg); } remap() { /* if I'm on a keyring, remap to "take self" */ if (location != nil && location.ofKind(Keyring)) return [TakeAction, self]; else return inherited(); } } /* "lock with" */ iobjFor(LockWith) { verify() { /* * if we know the direct object is a LockableWithKey, we can * perform some additional checks on the likelihood of this * key being the intended key for the lock */ if (gDobj != nil && gDobj.ofKind(LockableWithKey)) { /* * If the player should know that we're the key for the * lock, boost our likelihood so that we'll be picked * out automatically from an ambiguous set of keys. */ if (gDobj.isKeyKnown(self)) logicalRank(150, 'known key'); /* * if this isn't a plausible key for the lockable, it's * unlikely that this is a match */ if (!gDobj.keyIsPlausible(self)) illogical(keyNotPlausibleMsg); } } } /* * the message to use when the key is obviously not plausible for a * given lock */ keyNotPlausibleMsg = &keyDoesNotFitLockMsg /* "unlock with" */ iobjFor(UnlockWith) { verify() { /* use the same key selection we use for "lock with" */ verifyIobjLockWith(); } } ; /* ------------------------------------------------------------------------ */ /* * A Dispenser is a container for a special type of item, such as a book * of matches or a box of candy. */ class Dispenser: Container /* * Can we return one of our items to the dispenser once the item is * dispensed? Books of matches wouldn't generally allow this, since * a match must be torn out to be removed, but simple box dispensers * probably would. By default, we won't allow returning an item * once dispensed. */ canReturnItem = nil /* * Is the item one of the types of items we dispense? Normally, we * dispense identical items, so our default implementation simply * determines if the item is an instance of our dispensable class. * If the dispenser can hand out items of multiple, unrelated * classes, this can be overridden to use a different means of * identifying the dispensed items. */ isMyItem(obj) { return obj.ofKind(myItemClass); } /* * The class of items we dispense. This is used by the default * implementation of isMyItem(), so subclasses that inherit that * implementation should provide the appropriate base class here. */ myItemClass = Dispensable /* "put in" indirect object handler */ iobjFor(PutIn) { verify() { /* if we know the direct object, consider it further */ if (gDobj != nil) { /* if we don't allow returning our items, don't allow it */ if (!canReturnItem && isMyItem(gDobj)) illogical(&cannotReturnToDispenserMsg); /* if it's not my dispensed item, it can't go in here */ if (!isMyItem(gDobj)) illogical(&cannotPutInDispenserMsg); } /* inherit default handling */ inherited(); } } ; /* * A Dispensable is an item that comes from a Dispenser. This is in * most respects an ordinary item; the only special thing about it is * that if we're still in our dispenser, we're an unlikely match for any * command except "take" and the like. */ class Dispensable: Thing /* * My dispenser. This is usually my initial location, so by default * we'll pre-initialize this to our location. */ myDispenser = nil /* pre-initialization */ initializeThing() { /* inherit the default initialization */ inherited(); /* * We're usually in our dispenser initially, so assume that our * dispenser is simply our initial location. If myDispenser is * overridden in a subclass, don't overwrite the inherited * value. */ if (propType(&myDispenser) == TypeNil) myDispenser = location; } dobjFor(All) { verify() { /* * If we're in our dispenser, and the command isn't "take" * or "take from", reduce our disambiguation likelihood - * it's more likely that the actor is referring to another * equivalent item that they've already removed from the * dispenser. */ if (isIn(myDispenser) && !gActionIs(Take) && !gActionIs(TakeFrom)) { /* we're in our dispenser - reduce the likelihood */ logicalRank(60, 'in dispenser'); } } } ; /* ------------------------------------------------------------------------ */ /* * A Matchbook is a special dispenser for matches. */ class Matchbook: Collective, Openable, Dispenser /* we cannot return a match to a matchbook */ canReturnItem = nil /* * we dispense matches (subclasses can override this if they want to * dispense a specialized match subclass) */ myItemClass = Matchstick /* * Act as a collective for any items within me. This will have no * effect unless we also have a plural name that matches that of the * contained items. * * It is usually desirable for a matchbook to act as a collective * for the contained items, so that a command like "take matches" * will be taken to apply to the matchbook rather than the * individual matches. */ isCollectiveFor(obj) { return obj.isIn(self); } /* * Append my directly-held contents to a vector when I'm directly * held. When the matchbook is open, append our matches, because we * consider the matches to be effectively attached to the matchbook * (rather than contained within it). */ appendHeldContents(vec) { /* if we're open, append our contents */ if (isOpen) vec.appendUnique(contents); } ; /* * A FireSource is an object that can set another object on fire. This * is a mix-in class that can be used with other classes. */ class FireSource: object /* * We can use a fire source to light another object, provided the * fire source is itself burning. We don't provide any action * handling - we leave that to the direct object. */ iobjFor(BurnWith) { preCond = [objHeld, objBurning] verify() { /* don't allow using me to light myself */ if (gDobj == self) illogicalNow(&cannotBurnDobjWithMsg); /* * If we're already lit, make this an especially good choice * for lighting other objects - this will ensure that we * choose this over a match that isn't already lit, which is * what you'd normally want to do to avoid wasting a match. * * Note that our ranking is specifically coordinated with * that used by Matchstick. We'll use a lit match over any * normal FireSource (rank 160); we'll use a lit FireSource * (rank 150) over an unlit match (rank 140). * * If we're not lit, make the action non-obvious so that * we're not taken as a default to light another object on * fire. We *could* light something once we're lit, but that * presumes there's a way to light me in the first place, * which might require yet another object (a match, for * example) - so ignore me as a default if we're not already * lit, and go directly to some other object. This should be * overridden for self-lighting objects such as matches. */ if (isLit) logicalRank(150, 'fire source'); else nonObvious; } } ; /* * A Matchstick is a self-igniting match from a matchbook. (We use this * lengthy name rather than simply "Match" because the latter is too * generic, and could be taken by a casual reader for an object * representing a successful search result or the like.) */ class Matchstick: FireSource, LightSource /* matches have fairly feeble light */ brightnessOn = 2 /* not lit initially */ isLit = nil /* amount of time we burn, in turns */ burnLength = 2 /* default long description describes burning status */ desc() { if (isLit) gLibMessages.litMatchDesc(self); else gLibMessages.unlitMatchDesc(self); } /* get our state */ getState = (isLit ? matchStateLit : matchStateUnlit) /* get a list of all states */ allStates = [matchStateLit, matchStateUnlit] /* "burn" action */ dobjFor(Burn) { preCond = [objHeld] verify() { /* can't light a match that's already burning */ if (isLit) illogicalAlready(&alreadyBurningMsg); } action() { local t; /* describe it */ defaultReport(&okayBurnMatchMsg); /* make myself lit */ makeLit(true); /* get our default burn length */ t = burnLength; /* * if this is an implicit command, reduce the burn length by * one turn - this ensures that the player can't * artificially extend the match's useful life by doing * something that implicitly lights the match */ if (gAction.isImplicit) --t; /* start our burn-out timer going */ new SenseFuse(self, &matchBurnedOut, t, self, sight); } } iobjFor(BurnWith) { verify() { /* * Whether or not a match is burning, it's an especially * good choice to light something else on fire. Make it * even more likely when it's burning already. * * Note that this is specifically coordinated with the base * FireSource ranking. We'll pick a lit match (160) over an * ordinary lit FireSource (150), but we'll pick a lit * FireSource (150) over an unlit match (140). This will * avoid consuming a match that's not already lit when * another fire source is already available. */ logicalRank(isLit ? 160 : 140, 'fire source'); } } /* "extinguish" */ dobjFor(Extinguish) { verify() { /* can't extinguish a match that isn't burning */ if (!isLit) illogicalAlready(&matchNotLitMsg); } action() { /* describe the match going out */ defaultReport(&okayExtinguishMatchMsg); /* no longer lit */ makeLit(nil); /* remove the match from the game */ moveInto(nil); } } /* fuse handler for burning out */ matchBurnedOut() { /* * if I'm not still burning, I must have been extinguished * explicitly already, so there's nothing to do */ if (!isLit) return; /* make sure we separate any output from other commands */ "<.p>"; /* report that we're done burning */ gLibMessages.matchBurnedOut(self); /* * remove myself from the game (for simplicity, a match simply * disappears when it's done burning) */ moveInto(nil); } /* matches usually come in bunches of equivalents */ isEquivalent = true ; /* * A light source that produces light using a fuel supply. This kind of * light source uses a daemon to consume fuel whenever it's lit. */ class FueledLightSource: LightSource /* provide a bright light by default */ brightnessOn = 3 /* not lit initially */ isLit = nil /* * Our fuel source object. If desired, this can be set to a * separate object to model the fuel supply separately from the * light source itself; for example, you could set this to point to * a battery, or to a vial of oil. By default, for simplicity, the * fuel supply and light source are the same object. * * The fuel supply object must expose two methods: getFuelLevel() * and consumeFuel(). */ fuelSource = (self) /* * Get my fuel level, and consume fuel. We use these methods only * when we're our own fuelSource (which we are by default). When * we're not our own fuel source, the fuel source object must * provide these methods instead of us. * * Our fuel level is the number of turns that we can continue to * burn. Each turn we're lit, we'll reduce the fuel level by one. * We'll automatically extinguish ourself when the fuel level * reaches zero. * * If the light source can burn forever, simply return nil as the * fuel level. */ getFuelLevel() { return fuelLevel; } consumeFuel(amount) { fuelLevel -= amount; } /* our fuel level - we use this when we're our own fuel source */ fuelLevel = 20 /* light or extinguish */ makeLit(lit) { /* if the current fuel level is zero, we can't be lit */ if (lit && fuelSource.getFuelLevel() == 0) return; /* inherit the default handling */ inherited(lit); /* if we're lit, activate our daemon; otherwise, stop our daemon */ if (isLit) { /* start our burn daemon going */ burnDaemonObj = new SenseDaemon(self, &burnDaemon, 1, self, sight); } else { /* stop our daemon */ eventManager.removeEvent(burnDaemonObj); /* forget out daemon */ burnDaemonObj = nil; } } /* burn daemon - this is called on each turn while we're burning */ burnDaemon() { local level = fuelSource.getFuelLevel(); /* if we use fuel, consume one increment of fuel for this turn */ if (level != nil) { /* * If our fuel level has reached zero, stop burning. Note * that the daemon is called on the first turn after we * start burning, so we must go through a turn with the fuel * level at zero before we stop burning. */ if (level == 0) { /* make sure we separate any output from other commands */ "<.p>"; /* mention that the candle goes out */ sayBurnedOut(); /* * Extinguish the candle. Note that we do this *after* * we've already displayed the message about the candle * burning out, because that message is displayed in our * own sight context. If we're the only light source * present, then we're invisible once we're not providing * light, so our message about burning out would be * suppressed if we displayed it after cutting off our * own light. To make sure we can see the message, wait * until after the message to cut off our light. */ makeLit(nil); } else { /* reduce our fuel level by one */ fuelSource.consumeFuel(1); } } } /* mention that we've just burned out */ sayBurnedOut() { gLibMessages.objBurnedOut(self); } /* our daemon object, valid while we're burning */ burnDaemonObj = nil ; /* * A candle is an item that can be set on fire for a controlled burn. * Although we call this a candle, this class can be used for other types * of fuel burners, such as torches and oil lanterns. * * Ordinary candles are usually fire sources as well, in that you can * light one candle with another once the first one is lit. To get this * effect, mix FireSource into the superclass list (but put it before * Candle, since FireSource is specifically designed as a mix-in class). */ class Candle: FueledLightSource /* * The message we display when we try to light the candle and we're * out of fuel. This message can be overridden by subclasses that * don't fit the default message. */ outOfFuelMsg = &candleOutOfFuelMsg /* the message we display when we successfully light the candle */ okayBurnMsg = &okayBurnCandleMsg /* show a message when the candle runs out fuel while burning */ sayBurnedOut() { /* by default, show our standard library message */ gLibMessages.candleBurnedOut(self); } /* * Determine if I can be lit with the specific indirect object. By * default, we'll allow any object to light us if the object passes * the normal checks applied by its own iobjFor(BurnWith) handlers. * This can be overridden if we can only be lit with specific * sources of fire; for example, a furnace with a deeply-recessed * burner could refuse to be lit by anything but particular long * matches, or a particular type of fuel could refuse to be lit * except by certain especially hot flames. */ canLightWith(obj) { return true; } /* * Default long description describes burning status. In most * cases, this should be overridden to provide more details, such as * information on our fuel level. */ desc() { if (isLit) gLibMessages.litCandleDesc(self); else inherited(); } /* "burn with" action */ dobjFor(BurnWith) { preCond = [touchObj] verify() { /* can't light it if it's already lit */ if (isLit) illogicalAlready(&alreadyBurningMsg); } check() { /* * make sure the object being used to light us is a valid * source of fire for us */ if (!canLightWith(obj)) { reportFailure(&cannotBurnDobjWithMsg); exit; } /* if the fuel level is zero, we can't be lit */ if (fuelSource.getFuelLevel() == 0) { reportFailure(outOfFuelMsg); exit; } } action() { /* make myself lit */ makeLit(true); /* describe it */ defaultReport(okayBurnMsg); } } /* "extinguish" */ dobjFor(Extinguish) { verify() { /* can't extinguish a match that isn't burning */ if (!isLit) illogicalAlready(&candleNotLitMsg); } action() { /* describe the match going out */ defaultReport(&okayExtinguishCandleMsg); /* no longer lit */ makeLit(nil); } } ; /* ------------------------------------------------------------------------ */ /* * "Tour Guide" is a mix-in class for Actors. This class can be * multiply inherited by objects along with Actor or a subclass of * Actor. This mix-in makes the Follow action, when applied to the tour * guide, initiate travel according to where the tour guide wants to go * next. So, if the tour guide is here and is waving us through the * door, FOLLOW GUIDE will initiate travel through the door. * * This class should appear in the superclass list ahead of Actor or the * Actor subclass. */ class TourGuide: object dobjFor(Follow) { verify() { /* * If the actor can see us, and we're in a "guided tour" * state, we can definitely perform the travel. Otherwise, * use the standard "follow" behavior. */ if (gActor.canSee(self) && getTourDest() != nil) { /* * we're waiting to show the actor to the next stop on * the tour, so we can definitely proceed with this * action */ } else { /* we're not in a tour state, so use the standard handling */ inherited(); } } action() { local dest; /* * if we're in a guided tour state, initiate travel to our * escort destination; otherwise, use the standard handling */ if (gActor.canSee(self) && (dest = getTourDest()) != nil) { /* initiate travel to our destination */ replaceAction(TravelVia, dest); return; } else { /* no tour state; use the standard handling */ inherited(); } } } /* * Get the travel connector that takes us to our next guided tour * destination. By default, this returns the escortDest from our * current actor state if our state is a guided tour state, or nil * if our state is any other kind of state. Subclasses must * override this if they use other kinds of states to represent * guided tours, since we'll only detect that we're in a guided tour * state if our current actor state object is of class * GuidedTourState (or any subclass). */ getTourDest() { return (curState.ofKind(GuidedTourState) ? curState.escortDest : nil); } ; /* * Guided Tour state. This provides a simple way of defining a "guided * tour," which is a series of locations to which we try to guide the * player character. We don't force the player character to travel as * specified; we merely try to lead the player. The actual travel is up * to the player. * * Here's how this works. For each location on the guided tour, create * one of these state objects. Set escortDest to the travel connector * to which we're attempting to guide the player character from the * current location. Set stateAfterEscort to the state object for the * next location on the tour. Set stateDesc to something indicating * that we're trying to show the player to the next stop - something * along the lines of "Bob waits for you by the door." Set * arrivingWithDesc to a message indicating that we just showed up in * the current location and are ready to show the player to the next - * "Bob goes to the door and waits for you to follow him." */ class GuidedTourState: AccompanyingState /* the travel connector we're trying to show the player into */ escortDest = nil /* * The next state for our actor to assume after the travel. This * should be overridden and set to the state object for the next * stop on the tour. */ stateAfterEscort = nil /* the actor we're escorting - this is usually the player character */ escortActor = (gPlayerChar) /* * The class we use for our actor state during the escort travel. * By default, we use the basic guided-tour accompanying travel * state class, but games will probably want to use a customized * subclass of this basic class in most cases. The main reason to * use a custom subclass is to provide customized messages to * describe the departure of the escorting actor. */ escortStateClass = GuidedInTravelState /* * we should accompany the travel if the actor we're guiding will be * traveling, and they're traveling to the next stop on our tour */ accompanyTravel(traveler, conn) { return (traveler.isActorTraveling(escortActor) && conn == escortDest); } /* * get our accompanying state object - we'll create an instance of * the class specified in our escortStateClass property */ getAccompanyingTravelState(traveler, conn) { return escortStateClass.createInstance( location, gActor, stateAfterEscort); } ; /* * A subclass of the basic accompanying travel state specifically * designed for guided tours. This is almost the same as the basic * accompanying travel state, but provides customized messages to * describe the departure of our associated actor, which is the actor * serving as the tour guide. */ class GuidedInTravelState: AccompanyingInTravelState sayDeparting(conn) { gLibMessages.sayDepartingWithGuide(location, leadActor); } ; /* ------------------------------------------------------------------------ */ /* * An Attachable is an object that can be attached to another, using an * ATTACH X TO Y command. This is a mix-in class that is meant to be * combined with a Thing-derived class to create an attachable object. * * Attachment is symmetrical: we can only attach to other Attachable * objects. As a result, the verb handling for ATTACH can be performed * symmetrically - ATTACH X TO Y is handled the same way as ATTACH Y TO * X. Sometimes reversing the roles makes the command nonsensical, but * when the reversal makes sense, it seems unlikely that it'll ever * change the meaning of the command. This makes it program the verb * handling, because it means that we can designate one of X or Y as the * handler for the verb, and just write the code once there. Refer to * the handleAttach() method to see how this works. * * There's an important detail that we leave to instances, because * there's no good general rule we can implement. Specifically, there's * the matter of imposing appropriate constraints on the relative * locations of objects once they're attached to one another. There are * numerous anomalies that become possible once two objects are attached. * Consider the example of a battery connected to a jumper cable that's * in turn connected to a lamp: * * - if we put the battery in a box but leave the lamp outside the box, * we shouldn't be able to close the lid of the box all the way without * breaking the cables * * - if we're carrying the battery but not the lamp, traveling to a new * room should drag the lamp along * * - if we drop the battery down a well, the lamp should be dragged down * with it * * Our world model isn't sophisticated enough to properly model an * attachment relationship, so it can't deal with these contingencies by * proper physical simulation. Which is why we have to leave these for * the game to handle. * * There are two main strategies you can apply to handle these problems. * * First, you can impose limits that prevent these sorts of situations * from coming up in the first place, either by carefully designing the * scenario so they simply don't come up, or by imposing more or less * artificial constraints. For example, you could solve all of the * problems above by eliminating the jumper cable and attaching the lamp * directly to the battery, or by making the jumper cable very short. * Anything attached to the battery would effectively become located "in" * the battery, so it would move everywhere along with the battery * automatically. Detaching the lamp would move the lamp back outside * the battery, and conversely, moving the lamp out of the battery would * detach the objects. * * Second, you can detect the anomalous cases and handle them explicitly * with special-purpose code. You could use beforeAction and afterAction * methods on one of the attached objects, for example, to detect the * various problematic actions, either blocking them or implementing * appropriate consequences. * * Given the number of difficult anomalies possible with rope-like * objects, the second approach is challenging on its own. However, it * often helps to combine it with the first approach, limiting the * scenario. In other words, you'd limit the scenario to some extent, * but not totally: rather than completely excising the difficult * behavior, you'd narrow it down to a manageable subset of the full * range of real-world possibilities; then, you'd deal with the remaining * anomalies on a case-by-case basis. For example, you could make the * battery too heavy to carry, which would guarantee that it would never * be put in a box, thrown down a well, or carried out of the room. That * would only leave a few issues: walking away while carrying the plugged * in lamp, which could be handled with an afterAction that severs the * attachment; putting the lamp in a box and closing the box, which could * be handled with a beforeAction by blocking Close actions whenever the * lamp is inside the object being closed. */ class Attachable: object /* * The list of objects I'm currently attached to. Note that each of * the objects in this list must usually be an Attachable, and we * must be included in the attachedObjects list in each of these * objects. */ attachedObjects = [] /* * Perform programmatic attachment, without any notifications. This * simply updates my attachedObjects list and the other object's list * to indicate that we're attached to the other object (and vice * versa). */ attachTo(obj) { attachedObjects += obj; obj.attachedObjects += self; } /* perform programmatic detachment, without any notifications */ detachFrom(obj) { attachedObjects -= obj; obj.attachedObjects -= self; } /* get the subset of my attachments that are non-permanent */ getNonPermanentAttachments() { /* return the subset of objects not permanently attached */ return attachedObjects.subset({x: !isPermanentlyAttachedTo(x)}); } /* am I attached to the given object? */ isAttachedTo(obj) { /* we are attached to the other object if it's in our list */ return (attachedObjects.indexOf(obj) != nil); } /* * Am I the "major" item in my attachment relationship to the given * object? This affects how our relationship is described in our * status message: in an asymmetrical relationship, where one object * is the "major" item, we will always describe the minor item as * being attached to the major item rather than vice versa. This * allows you to ensure that the message is always "the sign is * attached to the wall", and never "the wall is attached to the * sign": the wall is the major item in this relationship, so it's * always the sign that's attached to it. * * By default, we always return nil here, which means that * attachment relationships are symmetrical by default. In a * symmetrical relationship, we'll describe the other things as * attached to 'self' when describing self. */ isMajorItemFor(obj) { return nil; } /* * Am I *listed* as attached to the given object? If this is true, * then our examineStatus() will list 'obj' among the things I'm * attached to: "Self is attached to obj." If this is nil, I'm not * listed as attached. * * By default, we're listed if (1) we're not permanently attached to * 'obj', AND (2) we're not the "major" item in the attachment * relationship. The reason we're not listed if we're permanently * attached is that the attachment information is presumably better * handled via the fixed description of the object rather than in * the extra status message; this is analogous to the way immovable * items (such as Fixtures) aren't normally listed in the * description of a room. The reason we're not listed if we're the * "major" item in the relationship is that the "major" status * reverses the relationship: when we're the major item, the other * item is described as attached to *us*, rather than vice versa. */ isListedAsAttachedTo(obj) { /* * only list the item if it's not permanently attached, and * we're not the "major" item for the object */ return (!isPermanentlyAttachedTo(obj) && !isMajorItemFor(obj)); } /* * Is 'obj' listed as attached to me when I'm described? If this is * true, then our examineStatus() will list 'obj' among the things * attached to me: "Attached to self is obj." If this is nil, then * 'obj' is not listed among the things attached to me when I'm * described. * * This routine is simply the "major" list counterpart of * isListedAsAttachedTo(). * * By default, we list 'obj' among my attachments if (1) I'm the * "major" item for 'obj', AND (2) 'obj' is listed as attached to * me, as indicated by obj.isListedAsAttachedTo(self). We only list * our minor attachments here, because we list all of our other * listable attachments separately, as the things I'm attached to. * We also only list items that are themselves listable as * attachments, for obvious reasons. */ isListedAsMajorFor(obj) { /* * only list the item if we're the "major" item for the object, * and the object is itself listable as an attachment */ return (isMajorItemFor(obj) && obj.isListedAsAttachedTo(self)); } /* * Can I attach to the given object? This returns true if the other * object is allowable as an attachment, nil if not. * * By default, we look to see if the other side is an Attachable, and * if so, if it overrides canAttachTo(); if so, we'll call its * canAttachTo to ask whether it thinks it can attach to us. If the * other side doesn't override this, we'll simply return nil. This * arrangement is convenient because it means that only one side of * an attachable pair needs to implement this; the other side will * automatically figure it out by calling the first side and relying * on the symmetry of the relationship. */ canAttachTo(obj) { /* * if the other side's an Attachable, and it overrides this * method, call the override; if not, it's by default not one of * our valid attachments */ if (overrides(obj, Attachable, &canAttachTo)) { /* * the other side is an Attachable that defines a specific * attachment rule, so ask the other side if it thinks we're * one of its attachments; by the symmetry of the * relationship, if we're one of its attachments, then it's * one of ours */ return obj.canAttachTo(self); } else { /* * the other side doesn't want to tell us, so we're on our * own; we don't recognize any attachments on our own, so * it's not a valid attachment */ return nil; } } /* * Explain why we can't attach to the given object. This should * simply display an appropriate mesage. We use reportFailure to * flag it as a failure report, but that's not actually required, * since we call this from our 'check' routine, which will mark the * action as having failed even if we don't here. */ explainCannotAttachTo(obj) { reportFailure(&wrongAttachmentMsg); } /* * Is it possible for me to detach from the given object? This asks * whether a given attachment relationship can be dissolved with * DETACH FROM. * * By default, we'll use similar logic to canAttachTo: if the other * object overrides canDetachFrom(), we'll let it make the * determination. Otherwise, we'll return nil if one or the other * side is a PermanentAttachment, true if not. This lets you prevent * detachment by overriding canDetachFrom() on just one side of the * relationship. */ canDetachFrom(obj) { /* if the other object overrides canDetachFrom, defer to it */ if (overrides(obj, Attachable, &canDetachFrom)) { /* let the other side make the judgment */ return obj.canDetachFrom(self); } else { /* * the other side doesn't override it, so assume we can * detach unless one or the other side is a * PermanentAttachment */ return !isPermanentlyAttachedTo(obj); } } /* * Am I permanently attached to the other object? This returns true * if I'm a PermanentAttachment or the other object is. */ isPermanentlyAttachedTo(obj) { /* * if either one of us is a PermanentAttachment, we're * permanently attached to each other */ return ofKind(PermanentAttachment) || obj.ofKind(PermanentAttachment); } /* * A message explaining why we can't detach from the given object. * Note that 'obj' can be nil, because we could be attempting a * DETACH command with no indirect object. */ cannotDetachMsgFor(obj) { /* * if we have an object, it must be the wrong one; otherwise, we * simply can't detach generically, since the object to detach * from wasn't specified, and there's nothing obvious we can * detach from */ return obj != nil ? &wrongDetachmentMsg : &cannotDetachMsg; } /* * Process attachment to a new object. This routine is called on * BOTH the direct and indirect object during the attachment process * - that is, it's called on the direct object with the indirect * object as the argument, and then it's called on the indirect * object with the direct object as the argument. * * This symmetrical handling makes it easy to handle the frequent * cases where the player might say ATTACH X TO Y or ATTACH Y TO X * and mean the same thing either way. Because this method is called * for both X and Y in either phrasing, you can simply choose to * write the handler code in either X or Y - you only have to write * it once, because the handler will be called on each of the * objects, regardless of the phrasing. So, if you choose to * designate X as the official ATTACH handler, write a handleAttach() * method on X, and leave the one on Y doing nothing: during * execution, the X method will do its work, and the Y method will do * nothing, so regardless of phrasing order, the net result will be * the same. * * By default we do nothing. Each instance should override this to * display any extra message and take any extra action needed to * process the attachment status change. Note that the override * doesn't need to worry about managing the attachedObjects list, as * the main action handler does that automatically. * * Note that handleAttach() is always called after both objects have * updated their attachedObjects lists. This means that you can turn * right around and detach the objects here, if you don't want to * leave them attached. */ handleAttach(other) { /* do nothing by default */ } /* * Receive notification that this object or one of its attachments * is being moved to a new container. When an attached object is * moved, we'll call this on the object being moved AND on every * object attached to it. 'movedObj' is the object being moved, and * 'newCont' is the new container it's being moved into. * * By default we do nothing. Instances can override this as needed. * For example, if you wish to enforce a rule that this object and * all of its attached objects share a common direct container, you * could either block the move (by displaying an error and using * 'exit') or run a nested DetachFrom action to sever the attachment * with the object being moved. */ moveWhileAttached(movedObj, newCont) { /* do nothing by default */ } /* * Receive notification that this object or one of its attachments is * being moved in the course of an actor traveling to a new location. * Whenever anyone travels while carrying an attachable object * (directly or indirectly), we'll call this on the object being * moved AND on every object attached to it. 'movedObj' is the * object being carried by the traveling actor, 'traveler' is the * Traveler performing the travel, and 'connector' is the * TravelConnector that the traveler is traversing. * * By default, we do nothing. Instances can override this as needed. */ travelWhileAttached(movedObj, traveler, connector) { /* do nothing by default */ } /* * Handle detachment. This works like handleAttach(), in that this * routine is invoked symmetrically for both sides of a DETACH X FROM * Y commands. * * As with handleAttach(), we do nothing by default, so instances * should override as needed. Note that the override doesn't need to * worry about managing the attachedObjects list, as the main action * handler does that automatically. As with handleAttach(), this is * called after the attachedObjects lists for both objects are * updated. */ handleDetach(other) { /* do nothing by default */ } /* the Lister we use to show our list of attached objects */ attachmentLister = perInstance(new SimpleAttachmentLister(self)) /* * the Lister we use to list the items attached to us (i.e., the * items for which we're the "major" item in the attachment * relationship) */ majorAttachmentLister = perInstance(new MajorAttachmentLister(self)) /* add a list of our attachments to the desription */ examineStatus() { local tab; /* inherit the normal status description */ inherited(); /* get the actor's visual sense table */ tab = gActor.visibleInfoTable(); /* add our list of attachments */ attachmentLister.showList(gActor, self, attachedObjects, 0, 0, tab, nil); /* add our list of major attachments */ majorAttachmentLister.showList(gActor, self, attachedObjects, 0, 0, tab, nil); } /* * Move into a new container. If I'm attached to anything, we'll * notify ourself and our attachments. */ mainMoveInto(newCont) { /* if I'm attached to anything, notify everyone */ if (attachedObjects.length() != 0) { /* notify myself */ moveWhileAttached(self, newCont); /* notify my attachments */ attachedObjects.forEach({x: x.moveWhileAttached(self, newCont)}); } /* inherit the base handling */ inherited(newCont); } /* * Receive notification of travel. If I'm involved in the travel, * and I'm attached to anything, we'll notify ourself and our * attachments. */ beforeTravel(traveler, connector) { /* * If we're traveling with the traveler, and we're attached to * anything, notify everything that's attached. */ if (attachedObjects.length() != 0 && traveler.isTravelerCarrying(self)) { /* notify myself */ travelWhileAttached(self, traveler, connector); /* notify each of my attachments */ attachedObjects.forEach( {x: x.travelWhileAttached(self, traveler, connector)}); } } /* * during initialization, make sure the attachedObjects list is * symmetrical for both sides of the attachment relationship */ initializeThing() { /* do the normal work */ inherited(); /* * check to make sure that each of our attached objects points * back at us */ foreach (local cur in attachedObjects) { /* * if we're not in this one's attachedObjects list, add * ourselves to the list, so that everyone's consistent */ if (cur.attachedObjects.indexOf(self) == nil) cur.attachedObjects += self; } } /* handle attachment on the direct object side */ dobjFor(AttachTo) { /* require that the actor can touch the direct object */ preCond = [touchObj] verify() { /* * it makes sense to attach to anything but myself, or things * we're already attached to */ if (gIobj != nil) { if (isAttachedTo(gIobj)) illogicalAlready(&alreadyAttachedMsg); else if (gIobj == self) illogicalSelf(&cannotAttachToSelfMsg); } } check() { /* only allow it if we can attach to the other object */ if (!canAttachTo(gIobj)) { explainCannotAttachTo(gIobj); exit; } } action() { /* add the other object to our list of attached objects */ attachedObjects += gIobj; /* add our default acknowledgment */ defaultReport(&okayAttachToMsg); /* fire the handleAttach event if we're ready */ maybeHandleAttach(gIobj); } } /* handle attachment on the indirect object side */ iobjFor(AttachTo) { /* * Require that the direct object can touch the indirect object. * This ensures that the two objects to be attached can touch * one another. Note that we don't also require that the actor * be able to touch the indirect object directly, since it's * good enough that (1) the actor can touch the direct object * (which we enforce with the dobj precondition), and (2) the * direct object can touch the indirect object. This allows for * odd things like plugging something into a recessed outlet, * where the recessed bit can't be reached directly but can be * reached using the plug. */ preCond = [dobjTouchObj] verify() { /* * it makes sense to attach to anything but myself, or things * we're already attached to */ if (gDobj != nil) { if (isAttachedTo(gDobj)) illogicalAlready(&alreadyAttachedMsg); else if (gDobj == self) illogicalSelf(&cannotAttachToSelfMsg); } } check() { /* only allow it if we can attach to the other object */ if (!canAttachTo(gDobj)) { explainCannotAttachTo(gDobj); exit; } } action() { /* add the other object to our list of attached objects */ attachedObjects += gDobj; /* fire the handleAttach event if we're ready */ maybeHandleAttach(gIobj); } } /* * Fire the handleAttach event - we'll notify both sides as soon as * both sides are hooked up with each other. This ensures that both * lists are updated before we notify either side, so the ordering * doesn't depend on whether we handle the dobj or iobj first. */ maybeHandleAttach(other) { /* if both lists are hooked up, send the notifications */ if (attachedObjects.indexOf(other) != nil && other.attachedObjects.indexOf(self) != nil) { /* notify our side */ handleAttach(other); /* notify the other side */ other.handleAttach(self); } } /* handle simple, unspecified detachment (DETACH OBJECT) */ dobjFor(Detach) { verify() { /* if I'm not attached to anything, this is illogical */ if (attachedObjects.length() == 0) illogicalAlready(cannotDetachMsgFor(nil)); } action() { local lst; /* get the non-permanent attachment subset */ lst = getNonPermanentAttachments(); /* check what that leaves us */ if (lst.length() == 0) { /* * we're not attached to anything that we can detach * from, so simply report that we can't detach * generically */ reportFailure(cannotDetachMsgFor(nil)); } else if (lst.length() == 1) { /* * we have exactly one attached object from which we can * detach, so they must want to detach from that - * process this as DETACH FROM my one attached object */ replaceAction(DetachFrom, self, lst[1]); } else { /* * we have more than one detachable attachment, so ask * which one they mean */ askForIobj(DetachFrom); } } } /* handle detaching me from a specific other object */ dobjFor(DetachFrom) { verify() { /* it only makes sense to try detaching us from our attachments */ if (gIobj != nil && !isAttachedTo(gIobj)) illogicalAlready(¬AttachedToMsg); } check() { /* make sure I'm allowed to detach from the given object */ if (!canDetachFrom(gIobj)) { reportFailure(cannotDetachMsgFor(gIobj)); exit; } } action() { /* remove the other object from our list of attached objects */ attachedObjects -= gIobj; /* add our default acknowledgment */ defaultReport(&okayDetachFromMsg); /* fire the handleDetach event if appropriate */ maybeHandleDetach(gIobj); } } /* handle detachment on the indirect object side */ iobjFor(DetachFrom) { verify() { /* it only makes sense to try detaching my attachments */ if (gDobj == nil) { /* * we don't know the dobj yet, but we can check the * tentative list for the possible set */ if (gTentativeDobj .indexWhich({x: isAttachedTo(x.obj_)}) == nil) illogicalAlready(¬AttachedToMsg); } else if (gDobj != nil && !isAttachedTo(gDobj)) illogicalAlready(¬AttachedToMsg); } check() { /* make sure I'm allowed to detach from the given object */ if (!canDetachFrom(gDobj)) { reportFailure(cannotDetachMsgFor(gDobj)); exit; } } action() { /* remove the other object from our list of attached objects */ attachedObjects -= gDobj; /* fire the handleDetach event if appropriate */ maybeHandleDetach(gDobj); } } /* * Fire the handleDetach event - we'll notify both sides as soon as * both sides are un-hooked up. This ensures that both lists are * updated before we notify either side, so the ordering doesn't * depend on whether we handle the dobj or iobj first. */ maybeHandleDetach(other) { /* if both lists are un-hooked up, send the notifications */ if (attachedObjects.indexOf(other) == nil && other.attachedObjects.indexOf(self) == nil) { /* notify our side */ handleDetach(other); /* notify the other side */ other.handleDetach(self); } } /* * TAKE X FROM Y is the same as DETACH X FROM Y for things we're * attached to, but use the inherited handling otherwise */ dobjFor(TakeFrom) { verify() { /* * use the inherited handling only if we're not attached - * if we're attached, consider it logical, overriding any * containment relationship check we might otherwise make */ if (gIobj == nil || !isAttachedTo(gIobj)) inherited(); } check() { /* inherit the default check only if we're not attached */ if (!isAttachedTo(gIobj)) inherited(); } action() { /* * if we're attached, change this into a DETACH FROM action; * otherwise, use the inherited TAKE FROM handling */ if (isAttachedTo(gIobj)) replaceAction(DetachFrom, self, gIobj); else inherited(); } } iobjFor(TakeFrom) { verify() { /* use the inherited handling only if we're not attached */ if (gDobj == nil || !isAttachedTo(gDobj)) inherited(); } check() { /* inherit the default check only if we're not attached */ if (!isAttachedTo(gDobj)) inherited(); } action() { /* inherit the default action only if we're not attached */ if (!isAttachedTo(gDobj)) inherited(); } } ; /* * An Attachable-specific precondition: the Attachable isn't already * attached to something else. This can be added to the preCond list for * an Attachable (for iobjFor(AttachTo) and dobjFor(AttachTo)) to ensure * that any existing attachment is removed before a new attachment is * formed. This is useful when the Attachable can connect to only one * thing at a time. */ objNotAttached: PreCondition checkPreCondition(obj, allowImplicit) { /* * if we don't already have any non-permanent attachments, we're * fine (as we don't require removing permanent attachments) */ if (obj.attachedObjects.indexWhich( {x: !obj.isPermanentlyAttachedTo(x)}) == nil) return nil; /* * Try an implicit Detach command. It should be safe to use the * form that doesn't specify what we're detaching from, since the * whole point of this condition is that the object can have only * one non-permanent attachment, hence the vague Detach handler * should be able to figure out what we mean. */ if (allowImplicit && tryImplicitAction(Detach, obj)) { /* if we're still attached to anything, we failed, so abort */ if (obj.attachedObjects.indexWhich( {x: !obj.isPermanentlyAttachedTo(x)}) != nil) exit; /* tell the caller we executed an implied action */ return true; } /* we must detach first */ reportFailure(&mustDetachMsg, obj); exit; } ; /* * A "nearby" attachable is a subclass of Attachable that adds a * requirement that the attached objects be in a given location. By * default, we simply require that they have a common immediate * container, but this can be overridden so that each object's location * is negotiated separately. This is a simple and effective pattern that * avoids many of the potential anomalies with attachment (see the * Attachable comments for examples). * * In AttachTo actions, we enforce the nearby requirement with a * precondition requiring the direct object to be in the same immediate * container as the indirect object, and vice versa. In * moveWhileAttached(), we enforce the rule by detaching the objects if * one is being moved away from the other's immediate container. */ class NearbyAttachable: Attachable dobjFor(AttachTo) { /* require that the objects be in the negotiated locations */ preCond = (inherited() + nearbyAttachableCond) } iobjFor(AttachTo) { /* require that the objects be in the negotiated locations */ preCond = (inherited() + nearbyAttachableCond) } /* * Get the target locations for attaching to the given other object. * The "target locations" are the locations where the objects are * required to be in order to carry out the ATTACH command to attach * this object to the other object (or vice versa). * * This method returns a list with three elements. The first * element is the target location for 'self', and the second is the * target location for 'other', the object we're attaching to. The * third element is an integer giving the priority; a higher number * means higher priority. * * The priority is an arbitrary value that we use to determine which * of the two objects involved in the attach gets to decide on the * target locations. We call this method on both of the two objects * being attached to one another, then we use the target locations * returned by the object that claims the higher priority. If the * two priorities are equal, we pick one arbitrarily. * * The default implementation chooses my own immediate container as * the target location for both objects. However, if the other * object is non-portable, we'll choose its immediate location * instead, since we obviously can't move it to our container. */ getNearbyAttachmentLocs(other) { /* * If the other object is portable, use our immediate container * as the proposed location for both objects; otherwise, use the * other object's immediate container. In any case, use a low * priority, since we're just the default base class * implementation; any override will generally have higher * priority. */ if (other.ofKind(NonPortable)) { /* the other can't be moved, so use its location */ return [other.location, other.location, 0]; } else { /* * the other can be moved, so use our own location, in a * paraphrase of the realty agent's favorite mantra */ return [location, location, 0]; } } /* when an attached object is being moved, detach the objects */ moveWhileAttached(movedObj, newCont) { /* * If I'm the one being moved, detach me from all of my * non-permanent attachments; otherwise, just detach me from the * other object, since it's the only one of my attachments being * moved. */ if (movedObj == self) { /* I'm being moved - detach from everything */ foreach (local cur in attachedObjects) { /* * If we're not permanently attached to this one, and * it's not inside me, detach from it. We don't need to * detach from objects inside this one, because they'll * be moved along with us automatically. */ if (!cur.isIn(self) && !isPermanentlyAttachedTo(cur)) nestedDetachFrom(cur); } } else { /* just detach from the one object */ nestedDetachFrom(movedObj); } } /* perform a nested DetachFrom action on the given object */ nestedDetachFrom(obj) { /* run the nested DetachFrom as an implied action */ tryImplicitAction(DetachFrom, self, obj); /* * if we're still attached to this object, the implied command * must have failed, so abort the entire action */ if (attachedObjects.indexOf(obj) != nil) exit; } ; /* * Precondition for nearby-attachables. This ensures that the two * objects being attached are in their negotiated locations. */ nearbyAttachableCond: PreCondition /* carry out the precondition */ checkPreCondition(obj, allowImplicit) { local dobjProposal, iobjProposal; local dobjTargetLoc, iobjTargetLoc; local iobjRet, dobjRet; /* * Ask each of the NearbyAttachable objects (the direct and * indirect objects) what it thinks. If an object isn't a * NearbyAttachable, it won't have any opinion, so use a * placeholder result with an extremely negative priority * (ensuring that it won't be chosen). In order for this * precondition to have been triggered, one or the other of the * objects must have been a nearby-attachable. */ dobjProposal = (gDobj.ofKind(NearbyAttachable) ? gDobj.getNearbyAttachmentLocs(gIobj) : [nil, nil, -2147483648]); iobjProposal = (gIobj.ofKind(NearbyAttachable) ? gIobj.getNearbyAttachmentLocs(gDobj) : [nil, nil, -2147483648]); /* * If the direct object claims higher priority, use its * attachment locations; otherwise, use the direct object's * locations. (This means that we take the indirect object's * proposed locations if the priorites are equal.) */ if (dobjProposal[3] > iobjProposal[3]) { /* the direct object claims higher priority, so use its results */ dobjTargetLoc = dobjProposal[1]; iobjTargetLoc = dobjProposal[2]; } else { /* * The direct object doesn't have a higher priority, so use * the indirect object's results. Note that the iobj * results list has the iobj in the first position, since it * was the 'self' when we asked it for its proposal. */ dobjTargetLoc = iobjProposal[2]; iobjTargetLoc = iobjProposal[1]; } /* carry out the pair of moves as needed */ dobjRet = moveObject(gDobj, dobjTargetLoc, allowImplicit); iobjRet = moveObject(gIobj, iobjTargetLoc, allowImplicit); /* * Return the indication of whether or not we carried out an * implied command. (Note that we can't call moveObject in this * 'return' expression directly, because of the short-circuit * behavior of the '||' operator. We must call both, even if * both carry out an action.) */ return (dobjRet || iobjRet); } /* carry out an implied action to move an object to a location */ moveObject(obj, loc, allowImplicit) { /* if the object is already there, we have nothing to do */ if (obj.location == loc) return nil; /* try the implied move */ if (allowImplicit && loc.tryMovingObjInto(obj)) { /* make sure it worked */ if (obj.location != loc) exit; /* we performed an implied action */ return true; } /* we can't move it - report failure and abort */ loc.mustMoveObjInto(obj); exit; } ; /* * A PlugAttachable is a mix-in class that turns PLUG INTO into ATTACH TO * and UNPLUG FROM into DETACH FROM. This can be combined with * Attachable or an Attachable subclass for objects that can be attached * with PLUG INTO commands. */ class PlugAttachable: object /* PLUG IN - to what? */ dobjFor(PlugIn) { verify() { } action() { askForIobj(PlugInto); } } /* PLUG INTO is the same as ATTACH TO for us */ dobjFor(PlugInto) remapTo(AttachTo, self, IndirectObject) iobjFor(PlugInto) remapTo(AttachTo, DirectObject, self) /* UNPLUG FROM is the same as DETACH FROM */ dobjFor(Unplug) remapTo(Detach, self) dobjFor(UnplugFrom) remapTo(DetachFrom, self, IndirectObject) iobjFor(UnplugFrom) remapTo(DetachFrom, DirectObject, self) ; /* ------------------------------------------------------------------------ */ /* * Permanent attachments. This class is for things that are described * in the story text as attached to one another, but which can never be * separated. This is a mix-in class that can be combined with a Thing * subclass. * * Descriptions of attachment tend to invite the player to try detaching * the parts; the purpose of this class is to provide responses that are * better than the defaults. A good custom message for this class * should usually acknowledge the attachment relationship, and explain * why the parts can't be separated. * * There are two ways to express the attachment relationship. * * First, the more flexible way: in each PermanentAttachment object, * define the 'attachedObjects' property to contain a list of the * attached objects. All of those other attached objects should usually * be PermanentAttachment objects themselves, because the real-world * relationship we're modeling is obviously symmetrical. Because of the * symmetrical relationship, it's only necessary to include the list * entry on one side of a pair of attached objects - each side will * automatically link itself to the other at start-up if it appears in * the other's attachedObjects list. * * Second, the really easy way: if one of the attached objects is * directly inside the other (which often happens for permanent * attachments, because one is a component of the other), make the * parent a PermanentAttachment, make the inner one a * PermanentAttachmentChild, and you're done. The two will * automatically link up their attachment lists at start-up. * * Note that this is a subclass of Attachable. Note also that a * PermanentAttachment can be freely combined with a regular Attachable; * for example, you could create a rope with a hook permanently * attached, but stil allow the rope to be attached to other things as * well: you'd make the rope a regular Attachable, and make the hook a * PermanentAttachment. The hook would be unremovable because of its * permanent status, and this would symmetrical prevent the rope from * being removed from the hook. But the rope could still be attached to * and detached from other objects. */ class PermanentAttachment: Attachable /* * Get the message explaining why we can't detach from 'obj'. * * By default, if our container is also a PermanentAttachment, and * we're attached to it, we'll simply return its message. This * makes it really easy to define symmetrical permanent attachment * relationships using containment, since all you have to do is make * the container and the child both be PermanentAttachments, and * then just define the cannot-detach message in the container. If * the container isn't a PermanentAttachment, or we're not attached * to it, we'll return our default library message. */ cannotDetachMsgFor(obj) { if (location != nil && location.ofKind(PermanentAttachment) && isAttachedTo(location)) return location.cannotDetachMsgFor(obj); else return baseCannotDetachMsg; } /* basic message to use when we try to detach something from self */ baseCannotDetachMsg = &cannotDetachPermanentMsg ; /* * A permanent attachment "child" - this is an attachment that's * explicitly attached to its container object. This is a convenient * way of setting up an attachment relationship between container and * contents when the contents object isn't a Component. */ class PermanentAttachmentChild: PermanentAttachment /* we're attached directly to our container */ attachedObjects = perInstance([location]) ; /* ------------------------------------------------------------------------ */ /* * A mix-in class for objects that don't come into play until some * future event. This class lets us initialize these objects with their * *eventual* location, using the standard '+' syntax, but they won't * actually appear in the given location until later in the game. * During pre-initialization, we'll remember the starting location, then * set the actual location to nil; later, the object can be easily moved * to its eventual location by calling makePresent(). */ class PresentLater: object /* * My "key" - this is an optional property you can add to a * PresentLater object to associate it with a group of objects. You * can then use makePresentByKey() to move every object with a given * key into the game world at once. This is useful when an event * triggers a whole set of objects to come into the game world: * rather than having to write a method that calls makePresent() on * each of the related objects individually, you can simply give each * related object the same key value, then call makePresentByKey() on * that key. * * You don't need to define this for an object unless you want to use * makePresentByKey() with the object. */ plKey = nil /* * Flag: are we present initially? By default, we're only present * later, as that's the whole point. In some cases, though, we have * objects that come and go, but start out present. Setting this * property to true makes the object present initially, but still * allows it to come and go using the standard PresentLater * mechanisms. */ initiallyPresent = nil initializeLocation() { /* * Save the initial location for later, and then clear out the * current location. We want to start out being out of the game, * but remember where we'll appear when called upon. To * accommodate MultiLoc objects, check locationList first. */ if (locationList != nil) { /* save the location list */ eventualLocation = locationList; /* * clear my location list if I'm not initially present; if I * am initially present, inherit the normal initialization */ if (!initiallyPresent) locationList = []; else inherited(); } else { /* save my eventual location */ eventualLocation = location; /* * clear my location if I'm not initially present; if I am * present initially, inherit the normal set-up */ if (!initiallyPresent) location = nil; else inherited(); } } /* bring the object into the game world in its eventual location(s) */ makePresent() { local pc; /* * If we have a list, add ourself to each location in the list; * otherwise, simply move ourself to the single location. */ if (eventualLocation != nil && eventualLocation.ofKind(Collection)) eventualLocation.forEach({loc: moveIntoAdd(loc)}); else moveInto(eventualLocation); /* if the player character can now see me, mark me as seen */ pc = gPlayerChar; if (pc.canSee(self)) { /* mark me as seen */ pc.setHasSeen(self); /* mark my visible contents as seen */ setContentsSeenBy(pc.visibleInfoTable(), pc); } } /* * make myself present if the given condition is true; otherwise, * remove me from the game world (i.e. move me into nil) */ makePresentIf(cond) { if (cond) makePresent(); else moveInto(nil); } /* * Bring every PresentLater object with the given key into the game. * Note that this is a "class" method that you call on PresentLater * itself: * * PresentLater.makePresentByKey('foo'); */ makePresentByKey(key) { /* * scan every PresentLater object, and move each one with the * given key into the game */ forEachInstance(PresentLater, function(obj) { if (obj.plKey == key) obj.makePresent(); }); } /* * Bring every PresentLater object with the given key into the game, * or move every one out of the game, according to the condition * 'cond'. * * If 'cond' is a function pointer, we'll invoke it once per object * with the given key, passing the object as the parameter, and use * the return value as the in game/out of game setting. For example, * if you wanted to show every object with key 'foo' AND with the * property 'showObj' set to true, you could write this: * * PresentLater.makePresentByKeyIf('foo', {x: x.showObj}); * * Note that this is a "class" method that you call on PresentLater * itself. */ makePresentByKeyIf(key, cond) { /* * scan every PresentLater object, check each one's key, and make * each one with the given key present */ forEachInstance(PresentLater, function(obj) { /* consider this object if its key matches */ if (obj.plKey == key) { local flag = cond; /* * evaluate the condition - if it's a function pointer, * invoke it on the current object, otherwise just take * it as a pre-evaluated condition value */ if (dataTypeXlat(cond) == TypeFuncPtr) flag = (cond)(obj); /* show or hide the object according to the condition */ obj.makePresentIf(flag); } }); } /* our eventual location */ eventualLocation = nil ; frobtads-1.2.3/tads3/lib/adv3/changes.htm0000644000175000001440000346661512145504453017277 0ustar realncusers Recent Changes to the TADS 3 Library

Recent Changes to the TADS 3 Library

This is a history of changes to Adv3, the TADS 3 adventure game library. Changes are listed in reverse chronological order (the most recent changes are first).

3.1.3

Released May 16, 2013

A bug in the parser caused a run-time error when a player attempted to traverse an AskConnector. This is now fixed. (bugdb.tads.org #0000169)
For conversational commands such as HELLO, GOODBYE, YES, and NO, the parser now sets the sense context to the issuing actor (that is, the actor who issued a command, which is usually the player character) rather than the target actor (the target in BOB, GOODBYE is Bob). For most commands, the target actor is the one who's meant to carry out the command - BOB, GO NORTH asks Bob to go north - so it's appropriate to execute most actions in the sense context of the target actor. In the past, the parser did just this, unconditionally for all types of actions. However, this isn't appropriate for conversational commands, because the meaning of the target actor is different in these commands; the target specifies the person being addressed in the conversation rather than the actor carrying out the command. BOB, HELLO doesn't tell Bob to say hello, but rather tells the player character to say hello to Bob. In these types of commands, then, the actor actually carrying out the command is the issuer, not the target, so the sense context should remain with the issuer. The parser now makes this distinction when setting the sense context. (bugdb.tads.org #0000190)
A parser bug caused the same object to be matched multiple times in a disambiguation query under certain circumstances. If an object defined multiple 'noun' words that all had a common prefix up to the truncation limit, and there were two or more objects with the same list of similar nouns, and the player entered a noun phrase that matched the common leading substring, a normal disambiguation question would be triggered. If the player then answered this question with ALL, the parser matched each object multiple times, equal to the number of matching (truncated) nouns per object.

The key element to triggering the bug is multiple noun synonyms that share a common prefix that's longer than the truncation limit. It's rare to find examples of this in English games, but it's common in inflected languages where nouns have multiple forms with different endings (the bug was discovered in a Czech translation). Here's a somewhat contrived English example. The kind of compass you use to inscribe circles can be called a COMPASS or COMPASSES - it's one of those odd English words like "scissors" or "pants" where the plural noun form can be used for the singular object, but (unlike scissors or pants) the singular form can be used interchangeably. If you set the TADS truncation limit to 6 characters, and then create a red compass and a blue compass with COMPASS and COMPASSES as noun synonyms, the bug would have caused the following odd interaction:

>x compas
Which compas do you mean, the red compass or the blue compass?

>all
red compass: It's an ordinary red compass.
blue compass: It's an ordinary blue compass.
blue compass: It's an ordinary blue compass.
red compass: It's an ordinary red compass.

This is now fixed. (bugdb.tads.org #0000188)

The library showed the wrong default message for the THROW DOWN command (that is, the ThrowDir action, when the direction is Down). The message used to be the rather non sequitur "You're not carrying that"; the message now says "You should just put it down instead."
3.1.2

Released August 20, 2012

In the past, atmosphere messages (via the Room atmosphereList property) didn't display a paragraph separator before the message, so if whatever item happened to be displayed just before an atmosphere message didn't add a paragraph separator at the end, the atmosphere message was shown as a continuation of the preceding paragraph. Room.roomDaemon() now explicitly adds a <.commandsep> before each atmosphere message to ensure that a paragraph break is displayed. (bugdb.tads.org #0000161)
3.1.1

Released July 14, 2012

The "inventory tall" lister incorrectly listed the contents of contentsListedSeparately containers both in-line and separately. This has been fixed, so that only the separate contents list is shown. (bugdb.tads.org #0000116)
The Thing methods for LOOK and EXAMINE that generate listings of contents now pass an additional named parameter, "examinee:", to the list methods. This is occasionally useful when one of the listing-related methods on one of the contents objects needs to know exactly what the context of the listing is.

The specific situation that motivated this change was a problem involving MultiLoc items with special descriptions, as described in the next item.

In the past, a MultiLoc with a special description was incorrectly included in the miscellaneous object list portion of a LOOK description for its room, or an EXAMINE description of its container. A special description is supposed to supersede inclusion in the ordinary list, since it provides the same information. This now works correctly.

The reason for the double listing was that the method that decides whether or not to include an object in the ordinary list, isListedInContents, didn't deal properly with MultiLoc objects. In fact, it couldn't deal with them properly, because it didn't have enough information, namely the identity of the object being examined.

For an ordinary Thing, the examinee is implicit in the call to isListedInContents, because this method is called only on direct children of the examinee; a Thing has only one direct container, so the examinee is implicitly the Thing's location. This logic doesn't work for a MultiLoc, though, because a MultiLoc can have multiple direct containers. So, to work properly, this method needed to know the examinee. This information is now available to the method via the new "examinee:" named parameter that the various Thing methods for LOOK and EXAMINE now supply. A new BaseMultiLoc.isListedInContents method uses the "examinee:" parameter to get the correct listing information. (bugdb.tads.org #0000119)

openableContentsLister has been renamed to openableDescContentsLister, because the original name was inconsistent with the corresponding listers for other classes. xxxContentsLister is supposed to be the lister assigned to the 'contentsLister' property for class xxx, and xxxDescContentsLister is the one for 'descContentsLister'. ('contentsLister' is the lister used for descriptions of objects containing the xxx, such as the enclosing room description; 'descContentsLister' is for EXAMINE descriptions of the xxx itself.) There isn't (and never was) a separate 'contentsLister' for Openable; the former openableContentsLister was always its 'descContentsLister', but unfortunately wasn't named accordingly.

To minimize the impact on existing code, adv3.h provides a #define that maps the old name to the new name. In the case of Openable, there's no need for a specialized 'contentsLister' at all, so there's no practical need to distinguish the two names, allowing us to create the mapping for the sake of compatibility. Existing code shouldn't notice the difference unless it defined an openableDescContentsLister object of its own, which seems unlikely given that openableContentsLister already filled the role implied by that name.

The dobjFor() and iobjFor() macros in adv3.h now have some special code that catches handler definitions for non-existent Actions. If the Action named in the dobjFor() or iobjFor() doesn't exist, the compiler will report an "undefined symbol" error naming the Action object. This only happens in debug builds (e.g., using the "-d" option for the command-line compiler), since it generates some extra code to create a reference to the action object. The warning can be helpful for catching typos (misspelled action names) or references to actions you intended to define but never got around to. (bugdb.tads.org #0000135)
The English parser is a little more nuanced about the way it handles queries for missing direct objects for verbs with prepositional phrasing for the direct object. For example:

>DIG
What do you want to dig in?

>IN THE DIRT

In the past, the parser treated the reply "IN THE DIRT" as a new command meaning ENTER DIRT. The assumption was that if the reply matched the phrasing of a new command, it must in fact be a new command rather than a reply to the missing object query. In many cases where there's this kind of ambiguity, the parser would have to be able to read the user's mind to know what was intended, but in this particular case the parallel phrasing of the query ("...dig in?") and the reply ("in noun") suggests pretty strongly that the user did indeed intend the input as a reply to the query rather than as a new command.

To address this, the parser has a new method for production matches, isSpecialResponseMatch. This new method returns true if the production matches special phrasing that makes it more likely to be a query response than a new command, otherwise nil. The English parser uses this for the various prepositional response phrasings, such as inSingleNoun and forSingleNoun; these are all subclasses of PrepSingleNounProd. When one of these productions matches input that starts with its preposition, it will return true from isSpecialResponseMatch, causing the missing object query processor to treat the match as a reply to the query rather than as a new command, even when the phrasing could also be interpreted as a new command. (bugdb.tads.org #0000147)

A bug in the parser caused the wrong message to be displayed when a command was directed to an actor, and the predicate portion of the command contained an unknown word, and the noun phrase wording for the actor portion matched at least one grammatical structure phrasing that could only refer to an out-of-scope object. In such a case, the parser reported "You see no actor here" rather than "The word word is not necessary in this story". This was obviously the wrong message when an actor matching the noun phrase actually was in scope, but was also inconsistent with the "unknown word" message shown in other similar cases that didn't meet all of the bug conditions. This has now been fixed so that the "unknown word" message is used. (bugdb.tads.org #0000149)
In the past, the library incorrectly called breakTopicTie() on each matching topic with the topic object as the first argument, instead of passing the whole list of matching topics as advertised. This has been corrected. (bugdb.tads.org #0000138)
3.1.0

Released 12/21/2011

The on-again, off-again detailed-naming scheme is now on again, this time with enhancements that will (hopefully) make it here to stay. This feature was first introduced in version 3.0.10, then disabled by default in 3.0.11 due to rough edges in that first attempt. It's reinstated in this release, with improvements. This improved version has been separately available for some time in an extension called newNames.t, so it's seen some testing and we think it'll provide good results.

The problem with the 3.0.10 implementation was that it made the object-name announcements unnaturally verbose - it added an excess of qualifiers to object names compared to what a human speaker would use. The new version tries to be smarter about adding only useful detail, by using more contextual information. For a multi-object announcement, for example, the new version only adds enough detail to distinguish the objects being announced from one another (rather than from everything else in scope), and also considers the context at a fixed point in time. The new version also uses the ordinary name for an object whenever that's enough to distinguish it; it only uses the (usually more verbose) disambig name when it's needed.

If you find that the new version still produces awkward announcements, or if you just prefer the original style, you can disable this feature: just set gameMain.useDistinguishersInAnnouncements = nil. This makes the parser revert to the basic object names in announcements.

Note that this change makes the newNames.t extension superfluous, since the library now incorporates it in its entirety.

It's now possible for a game to provide custom code to break ties in topic matches, when more than one topic matches the player's input with the same score. In the past, the parser just chose a matching topic entry arbitrarily. In some cases, it's possible for the game to make a smarter choice based on the exact input text. In addition, the parser now automatically filters tie lists by selecting exact text matches over truncated text matches.

To add a custom tie-breaker, you add the method breakTopicTie to one or more of your TopicEntry objects. This method receives as arguments the list of tied TopicEntry objects, the original ResolvedTopic object from the parser, the atcor, and the original input token list. When a topic tie occurs, the parser calls this method on each tied TopicEntry object in turn, in in arbitrary order. This method can either select the tie-breaker or do nothing. If it selects the tie-breaker, the selection process ends immediately with the selected winner; otherwise, the parser continues calling breakTopicTie on the remaining TopicEntry objects in the list of ties.

This method was added to address a situation where two topics happened to match the same input due to vocabulary truncation. Suppose we have a game object that matches the vocabulary "elephant", and another that matches "elephants". If the player enters ASK ABOUT ELEPHANT, the word ELEPHANT in the input will match both game objects, since the parser allows the user to abbreviate words by truncating to the first six characters only. Now suppose we have separate topic entries for these two objects, and that both topics are active at the same time and have the same match score. This results in a topic tie, triggering the new tie-breaking code.

For the sake of this example, let's suppose that you want to distinguish between ASK ABOUT ELEPHANT and ASK ABOUT ELEPHANTS (which stands to reason, as you probably wouldn't have created both topic entries otherwise). In the past, since we have a tie between the two topic entries, the parser simply chose one arbitrarily. You can now specifically select one or the other for a given entry, by writing a breakTopicTie method. Since we want ASK ABOUT ELEPHANT to match the singular version of the topic, here's how the method would look:

    breakTopicTie(matches, topic, actor, toks)
    {
        if (toks[1].toLower() == 'elephant')
            return self;
        return nil;
    }

You can add this method to either TopicEntry object, but it really belongs conceptually with the singular version of the topic.

The default handling for breakTopicTie() is simply to return nil, which defers to other objects in the list to make the selection.

For the specific case of truncation matching, you actually don't have to write a breakTopicTie() method after all, since the parser now also uses vocabulary truncation to break ties on its own. It does this after applying any custom breakTopicTie() results. That is, if breakTopicTie() fails to break the tie, because none of the TopicEntry objects return a non-nil result for the method, the parser tries to break the tie on its own by comparing vocabulary truncation flags. If one topic entry matched with truncation, and another didn't, the parser will select the one that matched without truncation.

The parser selects by truncation because this is probably the most common basis for selection. But it's certainly not the only basis. Other selection criteria could include word order, and the presence or absence of certain words. Those sorts of criteria would be very difficult to encode into a general framework, thus breakTopicTie() leaves it to the game to make the selection.

This feature addresses bugdb.tads.org #0000078.

The scheme that selects which message to display for a reportFailure() or similar message call had a problem when a verb had both a direct and an indirect object, and both of those objects had overrides for the same message. The message generator always gave priority to the direct object when both objects had overrides, which was wrong if the message was due to a condition relating to the indirect object. For example, given an Underside and a Container, both with limited bulk, "PUT Underside IN Container" showed the Underside's bulk error message ("The Container won't fit under the Underside"), since the Underside is the direct object.

The message generator now gives priority to the calling object. The generator looks in the stack trace for a "self" object in the source list, and gives priority to the first source object it finds. This is the source object whose method is actually generating the message; this will usually be a verify(), check(), or action() method to process a verb, so presumably it's the object to which the error condition applies.

(It's certainly valid to generate messages from other places than the verify(), check(), and action() methods, or indeed from an object that's not one of the message sources. But the source priority shouldn't matter in those cases, because the message presumably has no particular object affiliation anyway if it's not being generated by code attached to one of the source objects.)

(bugdb.tads.org #0000061)

The new TAction methods getDobjTokens() and getDobjWords() make it easier to retrieve the original text entered by the player to refer to the current direct object. These work only when there's an active direct object (a non-nil object in gDobj).

The corresponding new TIAction methods getIobjTokens() and getIobjWords() retrieve the original text of the current indirect object.

The token lists returned generally contain the full phrase that the player entered, including qualifiers like articles ("the") and possessives. When the player enters a list ("take book, bell, and candle"), the token list will only include the phrase for the current object, not the whole list.

Original text isn't always available even when there's a valid direct/indirect object in play. For example, text isn't generally available when the action is a nested or implied action, since these actions are synthesized internally within the game rather than being parsed from user input. The new methods return nil when there's no original text available for the corresponding objects.

In the past, the getOrigTokenList() method on a noun phrase that had been manually disambiguated ("which book do you mean...") returned only the tokens of the user's last answer. For example, if the player typed "take book", and answered the "which book" question with "blue", then getOrigTokenList() on the resulting noun phrase returned just the "blue" token.

The parser now builds a token list that has the complete original noun phrase plus the disambiguation answer (or answers, if multiple questions were needed to narrow the list sufficiently). The naive way to do this (at least in English) might be to simply prepend the disambiguation answer to the original noun phrase, on the assumption that the answer is simply an adjective modifying the original phrase; this would work for the "blue book" example above. One problem with this is that some names include prepositional phrases, such as "stack of bills"; we'd want a "which one?" answer of "$20" to turn this into "stack of $20 bills" instead of "$20 stack of bills", since that would mean something rather different. The bigger problem is that the parser accepts a number of answer formats that aren't mere adjectives, such as "the blue one", "both", "any of them", and "2" or "second" or "the second one" to pick the second item listed in the parser's "which book" question. These obviously would produce nonsense using the naive algorithm.

The parser's solution is to append each new answer parenthetically to the end of the original phrase. This produces a result that's at least comprehensible (and usually pretty good) for any answer format, because it parallels the way the phrase was actually built, as a series of post hoc clarifications: "book (the blue one)" or "tea (Earl Gray) (hot)". Further, if there's a need to display the phrase to the player, it should make sense to the player because it parrots back what they just typed, retaining the same word order and grouping.

SenseConnector now overrides checkThrowViaPath(), to handle throwing objects across the connector independently of "move" and "reach" operations. In the past, throwing was handled as equivalent to moving, but this caused an unintended change of behavior when DistanceConnector was recently changed (in 3.0.16) to allow moving objects across a distance.

SenseConnector also defines a new method, checkThrowThrough(). This method is the "throw" equivalent of the existing methods checkMoveThrough() and checkTouchThrough(). The default definition of the new method returns a failure indication, parallel to the corresponding existing methods for "move" and "reach".

Finally, DistanceConnector overrides checkThrowThrough() in parallel to its corresponding overrides for "move" and "reach".

This change (a) restores the pre-3.0.16 behavior for throwing across a distance, by disallowing it, and (b) makes it possible to define independent move, reach, and throw handling for sense connectors.

The English parser now accepts "LOOK thing" and "L think" as synonyms for "LOOK AT thing", since it's been observed that many novice players use this syntax.

(bugdb.tads.org #0000091)

Thing.addToContents() now skips adding the child object to the 'contents' list if it's already there. In the past, it was possible to cause an item to added to its parent's contents list twice, by explicitly using moveInto() in an initializeThing() override; this caused strange behavior, such as listing the child item twice in contents list messages. The extra check avoids this unintended duplication.

(bugdb.tads.org #0000067)

Using a RoomPart as a regular contents item in a room (rather than as an element of a roomParts list) caused a run-time error on LOOK AROUND in some cases. This has been corrected.

(bugdb.tads.org #0000076)

In the past, if you put one StretchyContainer inside another, the library didn't properly enforce the bulkCapacity of the outer container when adding new contents to the inner container. (Since the inner container is itself a StretchyContainer, its bulk can increase when a new item is added, which can make it too large for the outer container.) This has been corrected.

(bugdb.tads.org #0000068)

ASK ABOUT and other topic verbs sometimes didn't work properly when plural words were used in the topic phrase. This has been corrected.

(bugdb.tads.org #0000069)

The English command grammar now allows "the" as an article in possessive phrases, as in "the orc's axe". In the past, possessive phrases didn't allow any article qualifier (the library took the overly narrow view that possessives are usually proper names, as in "Bob's book").

In addition, plural possessives, signified by an apostrophe at the end of a plural word, are now accepted. For example, "the Smiths' house" would be accepted if "Smiths" is defined as a plural word. (bugdb.tads.org #0000077)

The Lister had a bug involving list groups with minimum group sizes. If there weren't enough candidate items to list with a group, according to the group's minGroupSize property, but there were more than one item, the list builder incorrectly included only one of the items in the final list, omitting the others. This has been corrected - the list builder now includes moves all of the candidate items to the "singles" list, so they all show up in the final list. (bugdb.tads.org #0000083)
In the past, if a command contained a noun phrase of the form THE ONE IN CONTAINER, and the phrase required interactive disambiguation, the parser incorrectly acted as though the player answered the "which one do you mean?" prompt with ALL. This was due to a parser bug; it's now fixed. (bugdb.tads.org #0000095)
The parser now gives priority to a possessive-phrase interpretation of a noun phrase that doesn't match anything in scope, in cases where the same words could be interpreted as a plain noun phrase without any possessive qualifiers. For example, in the library sample game, we have an object defined with vocabulary words including "Bob's fish cleaner", which means that "Bob's" is defined as an adjective in the dictionary. "Bob" also is the name of an actor in the game, so the word "Bob's" in player input can be interpreted in two ways: as an adjective, or as a possessive qualifier. If the player enters a noun phrase such as "Bob's desk", the same phrase can be interpreted with two very different grammatical structures: the possessive qualifier "Bob's" modifying an underlying noun phrase "desk", or the noun phrase "Bob's desk", where the first word is a simple adjective from the dictionary. In the past, the parser considered the two phrasings be equally strong at the structural level. If one phrasing has a valid resolution (i.e., it could be mapped to in-scope objects) and the other doesn't, the structural equivalence doesn't matter, because the parser always picks the phrase that has the best resolution to game objects. However, in cases where neither phrasing has a resolution - because there's no desk in scope at all, say - the parser picks the interpretation based on structural strength. Since the two phrasings were equally strong, the parser picked one arbitrarily. If the winner happened to be the adjective interpretation, this produced the odd error message "You see no bob's desk here." Now, with this change, the parser gives priority to the possessive interpretation in cases where other things are equal, so the error will be "You see no desk here." This is a better message because the unmatched object really is simply "desk", regardless of whose desk it is; reporting that "bob's desk" is the missing object seems overly literal. (bugdb.tads.org #0000096)
Room now provides a suitable message for cases where objects are thrown within the room, but there's no floor object among the room part list for the room. In the past, this caused a run-time error, because there was no message defined for this situation.

When an object is thrown, the library's default handling is simply to drop the object into the room, as though the player had typed DROP PROJECTILE. The default message, though, describes the object as "falling to the floor", where to the floor is replaced by a message that depends on the "nominal drop destination" for the location - i.e., the object where things are described as landing when dropped in the room. The standard nominal drop is the room's floor, which is to say the Floor object in the room's roomParts list. If there's no floor object, though, the fallback is the Room itself. The run-time error occurred because Room didn't have the associated message property for this situation. The to the floor phrase in the message is obtained via the putDestMessage property, which Room didn't define. Room now defines this property as the message property pointer &putDestRoom. libMessages in the English library in turn defines putDestRoom to display "into the room". The result is that THROW PROJECTILE AT WALL in a room without a floor now properly replies "...and falls into the room", instead of causing a run-time error.

(bugdb.tads.org #0000078)

Using replaceAction with GiveTo sometimes caused a run-time error. (This was due a complex interaction with the giveMeToAskFor global remapping. AskFor is a "topic" action, but GiveTo isn't; the remapping tried to resolve the GiveTo direct object as a topic for AskFor, but within the context of the GiveTo verb, which didn't provide one of the required resolver methods, createTopicQualifierResolver. This method is now defined for the base Action class, since this situation creates a dependency on it even for ordinary non-topic action types.)

(bugdb.tads.org #0000103)

The English library message for an empty topic inventory has been reworded slightly for the past-tense version, replacing "right now" with "just then".
3.0.18.1

Released 5/5/2009

A typo in the English message for refuseCommand has been fixed.
3.0.18

Released 4/28/2009

More of the lister classes have been reorganized along the same lines as the BaseContentsLister refactoring in version 3.0.17. In particular:
  • surfaceLookInLister, undersideLookUnderLister, and rearLookBehindLister are now trivial classes based on the new class LookWhereContentsLister. For consistency, thingLookInLister is based on the new class as well, but is non-trivial due to its different wording.
  • surfaceInlineContentsLister, undersideInlineContentsLister, and rearInlineContentsLister are now trivial classes based on the new class BaseInlineContentsLister. For consistency, inlineContentsLister is also based on the new base class, but is non-trivial due to its different wording.
When an action remapping is inherited from a base class, a subclass or instance can now define a verify() method for the original action. In the past, only the new action's verify() was called - the remapping bypassed any dobjFor() or iobjFor() definitions for the old action. This made it impossible to tweak the subclass handling for verbs that were remapped in base classes.

With this change, the verify() for the new and old actions are both called, if there's a verify() for the old action defined on a subclass of the class where the remap() itself is defined. In other words, if the verify() "overrides" the remap(), the subclassed verify() is invoked.

Because the verify() methods for both the old and new actions are called, you can't actually override the new action's verify() call. But verify() results are cumulative, so you can use it to adjust the logicalness of the action, or to make the action illogical.

The parser can now has an option to generate more descriptive messages when noun phrases are disambiguated using the logicalness rules.

Disambiguation occurs when the player uses a noun phrase that matches more than one in-scope object. For example, if the current room contains a red door and a blue door, and the player types OPEN DOOR, the noun phrase DOOR could refer to either of the two door objects. The parser must decide which of the two objects the player is referring to; this process is called disambiguation. The parser does this by applying the various logical, logicalRank, illogicalNow, illogicalAlready, and related rules defined on the matching objects in the library and in your game code.

There are two kinds of disambiguation results: "clear" and "unclear". When exactly one of the objects involved is logical, it's a clear disambiguation result: we consider it safe to assume that the player was referring to that exact object, since the other choices simply don't make sense. When more than one object is logical, though, the parser can sometimes still pick one automatically (i.e., without generating an extra question asking the player to specify which one she intended) based on the "likelihood" rankings given by logicalRank rules. A pick based on likelihood rankings is an unclear result, because the player could plausibly have been referring to one of the other logical matches.

For unclear results, the parser has always generated a parenthetical announcement saying which object it chose. This is to alert the player to the parser's choice, so that any misunderstanding is immediately apparent.

However, in past versions, the parser didn't say anything to explicate clear disambiguation results. In practice, even clear results can sometimes disagree with the player's intentions, for the simple reason that the player sometimes misunderstands the current state of the objects. So some authors have found that it's better to make some kind of object announcement in every case, regardless of how clear the parser thinks the resolution is.

The parser now provides two new modes, in addition to the traditional mode, for announcing clear disambiguation results. These new modes are selected via the new gameMain property ambigAnnounceMode. You can set this in your gameMain object to select the behavior you prefer. The settings are:

  • AnnounceUnclear: The parser makes a parenthetical announcement (such as "(the red door)") only when a disambiguation result is unclear. It doesn't make any announcements for clear disambiguation results, or when only one in-scope object matches a noun phrase. This is the traditional behavior that the library used before the new options were added.
  • AnnounceClear: The parser makes a parenthetical announcement for any disambiguation result, whether clear or unclear. No announcement is generated when there's no disambiguation, though (that is, the noun phrase itself matches only one in-scope object).
  • DescribeClear: For unclear disambiguation, the parser makes the traditional parenthetical announcement. For clear disambiguation, the parser skips the parenthetical announcement, but it uses more detailed default reply messages in lieu of the ultra-terse defaults, such as "Taken" or "Dropped". For example, instead of just "Taken", the parser replies "You take the dusty old tome." The longer messages mention the objects involved by name, so they provide the same information as the parenthetical announcements, but in a somewhat less conspicuous and mechanistic way.

Note that the new default behavior differs from the old behavior from before this feature was added. If you prefer the old behavior, you must explicitly set gameMain.ambigAnnounceMode to AnnounceUnclear.

The short-vs-long message selection for the DescribeClear option is done through a couple of new MessageHelper methods, shortTMsg() and shortTIMsg(). If you're adding new messages of your own, you can use these methods to do the same type of selection.

The parser can now accept short-hand replies to disambiguation queries involving a locational qualifier, using nothing more than an adjective or noun from one of the location names. For example:

>take torch
Which torch do you mean, the torch in the left sconce, or the torch in
the right sconce?

>left
Taken.

In the past, it was necessary for the response to be explicit about the location qualification: THE TORCH IN THE LEFT SCONCE, THE ONE IN THE LEFT, etc. In particular, the response had to actually use the locational phrasing to refer to the distinguishing location. This is no longer necessary; a player can now simply name one of the locations, and can even abbreviate to a single distinguishing word from the location name.

To support this change, the DisambigResolver now keeps track of the Distinguisher that was used to generate the question (the prompting message - "Which torch do you mean...?"). This lets the resolver look for qualifying phrases relevant to the distinguishing characteristic called out in the prompt.

The Distinguisher, meanwhile, has a couple of new methods that let it provide the added information: objInScope() lets the distinguisher add qualifying objects to the scope list, and matchName() lets the distinguisher recognize qualifying objects in addition to the original ambiguous objects. The locational and ownership distinguishers defined in the library provide suitable definitions for these new methods. Games that define their own custom Distinguisher subclasses might want to add these methods if they also make use of ancillary qualifier objects.

In the English library, PathPassage adjusts the logicalness of the verbs Enter and Go Through downward (to logical rank 50). These verbs aren't usually used with path-like objects in English; this logicalness adjustment tells the parser to pick other objects first in cases of ambiguity.
The new conversationManager method setRevealed(key) adds the given key to the revealed topic table. The library now calls this method any time it needs to add a key to the table (whereas it used to manipulate the table directly).

These changes don't affect any existing behavior - existing code will continue to work unchanged. The purpose of the changes is to provide a single point for overriding the default library behavior via 'modify'. In particular, you can override setRevealed() to store additional information about the revealed topic in its table entry, beyond the mere fact of the revelation. You could, for example, store the location where the key was revealed, or a time stamp marking when it was revealed.

A new ConvNode method, noteActiveReason(reason), extends the older noteActive() method by providing information on why the node is being activated. The 'reason' parameter is a string indicating what triggered the node change:
  • 'convnode' - processing a <.convnode> tag
  • 'convend' - processing a <.convend> tag
  • 'initiateConversation' - a call to Actor.initiateConversation()
  • 'endConversation' - a call to Actor.endConversation()

By default, this new method simply ignores the reason code and calls the noteActive() method. This ensures that existing code will work unchanged.

If you need the reason information when activating the node, override noteActiveReason(). Otherwise, you can override noteActive() as in the past.

The new reason code information is supplied when the caller changes the node by calling the new Actor method setConvNodeReason(). This is essentially an extended version of the existing setConvNode() method. The new method takes an additional argument giving the reason code, which it passes to noteActiveReason().

Note that the reason code will be nil if the node change is initiated by calling Actor.setConvNode() directly. All of the library calls to setConvNode() have been replaced by calls to setConvNodeReason(), so unless your game code is calling setConvNode() directly, the reason code will always be set properly. For compatibility with existing code, setConvNode() can still be called; it works as before, simply passing a nil reason code.

In the past, when an actor used conversational states (ConversationReadyState, InConversationState), terminating the conversation didn't take into account any ByeTopic objects in the current ConvNode within the conversation. This was because the conversation states have some special transition handling that overrides the usual code where the ConvNode ByeTopic would be found. The conversation states now take care to check the ConvNode in effect at the end of the conversation, so ByeTopic objects within a ConvNode should now interact properly with conversation states.
The transformation from "X, GIVE ME Y" to "ASK X FOR Y" that was introduced in 3.0.17 caused a run-time error if "Y" was a valid word but didn't resolve to an object. This has been corrected. (bugdb.tads.org #0000021)
In the past, when EXAMINE was used with ItemizingCollectiveGroup, the output omitted any object that wasn't listable through the room contents lister. This was a problem for objects such as Fixtures, which normally aren't displayed in a room listing.

ItemizingCollectiveGroup now calls a new method on self, examineUnlisted(x), to list each object x that isn't listed via the room contents lister. By default, this new method performs a nested EXAMINE command on the unlisted object to show its full description.

In Settable, the message routing properties have been changed slightly. In the past, the message properties okaySetToMsg and setToInvalidMsg were doubly redirected: okaySetToMsg had the value &okaySetToMsg, and setToInvalidMsg had the value &setToInvalidMsg. The TurnTo action handler that used these messages evaluated the properties directly, to get the property pointers, which the library then processed in the usual way.

The point of this arrangement was that subclasses of Settable (Dial in particular) could easily redirect these messages to different library messages, just by overriding the properties. However, this was inconsistent with the message scheme most other objects use, in that you couldn't override okaySetToMsg(val) in the Settable itself to yield the message text, which is how the message scheme works for most other objects.

The new version keeps the redirection step, but renames it to allow the standard override scheme to be used. The new property okaySetToMsgProp evaluates to &okaySetToMsg, and setToInvalidMsgProp evaluates to &setToInvalidMsg. This means that you can override okaySetToMsg(val) and setToInvalidMsg(val) using the standard pattern.

(bugdb.tads.org #0000045)

Underside and RearContainer now provide tryMovingObjInto() method definitions. The new methods try the obvious implied commands suitable for these objects: Underside tries an implied PutUnder, and RearContainer tries an implicit PutBehind.
The BannerWindow method updateForRestore() is now called during a Restore or Undo operation for every banner, even when the banner is already displayed. In the past, this method wasn't called on banner windows that were already present; it was only called when the window had to be created in the course of the Restore/Undo. It's now called uniformly for all banners.
3.0.17.1

Released 8/12/2008

This version has no library changes; this release is to correct a TADS 3 compiler problem introduced in 3.0.17.
3.0.17

Released 8/9/2008

Risk of incompatibility: The standard handling for the TakeFrom action has been changed slightly. The changes generally make it easier to code objects by unifying the handling of Take and TakeFrom for most objects, but some slight adjustments to existing code might be required - see below.

First, Thing.dobjFor(TakeFrom)'s action() handler now actually replaces the TakeFrom with a Take action. In the past, the handler merely invoked the Take action() handler. For the most part, this had the same effect as replacing the action, but not always; some arrangements of subclasses and overrides caused subtle differences. Actually replacing the action ensures that a successful TakeFrom is handled exactly like a regular Take, which means that custom behavior for Take will automatically apply exactly the same way to TakeFrom without any explicit TakeFrom overrides. Of course, you can still override dobjFor(TakeFrom) separately if you do want it to behave differently from Take.

Second, Immovable.dobjFor(Take) now fails the action in the check() handler rather than the action handler, which allows us to remove Immovable.dobjFor(TakeFrom) entirely. In the past, TakeFrom was separately overridden to ensure that the indirect object wouldn't attempt to carry out the action, but this is no longer necessary, since the basic Take check() phase (which TakeFrom's check() phase invokes) will fail the command.

These two changes should essentially eliminate the need to override TakeFrom when all you want to do is make it behave the same as Take. TakeFrom should now always follow Take's lead, so if you simply want TakeFrom and Take to behave the same way, just override Take and you're set.

Some existing code might not work properly with the changes to Immovable. In particular, you'll need to look at any objects of class Immovable (or any subclass of Immovable) where you've overridden the dobjFor(Take) action() handler. In the past, overriding the action() handler for Take was enough to override Immovable's handling of the command, since Immovable failed the command in the action() handler and did nothing in the check() handler. With the change, the check() handler cancels the action. This means that your overridden action() handler might never be invoked. The solution is simply to insert an empty dobjFor(Take) check() handler in your object:

   boulder: Immovable 'boulder' 'boulder' "It's a big boulder. "
      dobjFor(Take)
      {
        verify()
           { /* my original verify routine... */ }
        check() { }   // THIS HAD TO BE ADDED DUE TO THE LIBRARY CHANGE !!!
        action()
           { /* my original action routine... */ }
      }
    ;

If you're not sure whether you have any objects needing this change, you can add this bit of code to your game, and then run the game and type IMMTEST to get a list of objects that you should inspect:

   VerbRule(immtest) 'immtest' : IAction
     execAction()
     {
       "Immovables with dobjFor(Take) action() overrides:\n";
       forEachInstance(Immovable, new function(x)
       {
         if (overrides(x, Immovable, &actionDobjTake))
           "\t!!! <>\n";
       });
     }
   ;
Krister Fundin's Tips extension has now been integrated into the library as a standard feature. This system makes it easy to create custom one-time tips of the sort that the library has traditionally used for things like the explanation of the NOTIFY command the first time the game's score changes. The library now uses the Tips system for its standard tips, and the system is readily extensible with custom, game-specific tips. A new chapter in the Technical Manual describes the system.
It's now possible to determine if a script file is currently being read, and if so, the modes being used to read it. To get this information, call setScriptFile(ScriptReqGetStatus). If input is currently being read from a script, the function returns an integer value giving a combination of ScriptFileXxx flags describing the reading mode; for example, the return value will be (ScriptFileQuiet | ScriptFileNonstop) for a script being read in quiet, non-stop mode. Note that a return value of 0 (zero) indicates that a script is being read, but that none of the mode flags are applicable - that is, the script is being read in the default modes, with output and "more" prompts displayed. If a script is not currently being read, the return value is nil.

The new flag ScriptFileEvent will be included in the return value if applicable. This lets you determine whether the script being read is an event script or a plain command-line script. Note that this new flag is "query only" - if you include it in the 'flags' argument in a call to setScriptFile() to start reading a script, the function ignores it, since the script reader determines the type of the script to be read automatically by examining the file's contents. The purpose of this flag is to let you find out how the script reader is treating the file, rather than letting you tell the script reader how to treat the file.

Note that this new form of setScriptFile() is only supported in VM versions 3.0.17 and higher. If you call setScriptFile(ScriptReqGetStatus) on an earlier VM, a run-time error result, because older implementations of the function accept only a filename string or nil as the first argument. You can use try-catch to handle this situation - a run-time error indicates that this form of the function isn't supported, in which case the script status information is not available.

Two new actions have been added: RecordEvents and RecordEventsString. These are variations on the Record and RecordString actions that record event scripts (rather than ordinary command scripts, as Record and RecordString do). The English library defines verb syntax for these new actions ("record events", "record events on", and "record events 'filename'").
A new parsing mechanism makes it possible to rewrite commands based on "global" conditions. Unlike remapTo, which lets you associate remapping rules with the objects involved in the command, the new mechanism lets you rewrite a command even before the objects are resolved. There are cases where it's important to be able to apply the rewrite before any object resolution has taken place, because the resolution process itself can vary substantially depending on the type of action being performed. remapTo has to wait until after objects are resolved, which is sometimes too late.

This new mechanism is implemented via a new class, GlobalRemapping. To create a rewrite rule, you create a GlobalRemapping object, and define its getRemapping() method. This method looks at the action to determine if it's of interest, using whatever criteria you want. In most cases, this means checking the action type to see if it's an action you want to rewrite, and possibly checking to see if certain objects are involved as well.

The library defines one GlobalRemapping object, giveMeToAskFor, which rewrites commands of the form "X, GIVE ME Y" to the alternative format "ME, ASK X FOR Y". The library formerly tried to perform this same rewriting via an iobjFor(GiveTo) check() method in Actor, but this approach didn't work in many cases due to interference from several other checks made earlier in the execution process. The old check() scheme has been deleted in favor of the new mechanism.

For full details, see the new chapter on Global Command Remapping in the Technical Manual.

A new set of Action methods makes it possible to temporarily override the meaning of a pronoun, for as long as the action is in effect. Action.setPronounOverride() creates an override, which associates an object with a particular pronoun type (PronounMe, PronounYou, etc). This new meaning overrides the usual meaning, but only for the duration of the action. Action.getPronounOverride() looks for an override defined with setPronounOverride() for this action.

This new mechanism is designed mainly to support GlobalRemapping. In particular, a remapping that changes the target actor usually needs to override the meaning of "you" in the rewritten action, so that "you", "your", and "yours" are interpreted as referring to the original target actor rather than the target actor of the rewritten format.

For details, see the new chapter "Global Command Remapping" in the Technical Manual.

The classes BaseSurfaceContentsLister, BaseRearContentsLister, and BaseUndersideContentsLister are now all based on a new class, BaseContentsLister. This new class unifies the code that was formerly defined separately in the three subclasses - BaseSurfaceContentsLister, BaseRearContentsLister, and BaseUndersideContentsLister are now trivial subclasses with no overrides of their own. (They're still present as separate classes, (1) for the sake of compatibility with existing code, and (2) because the distinct classes could still be useful for other types of container-type-specific overrides beyond what the library defines.)

The three subclasses exist to provide customized wording in the contents listings generated for the corresponding specialized container types. However, the variation in the wording provided by the library definitions was always very simple - the only thing that varies is the preposition used to describe the relationship between the container and its contents ("on" for a Surface, "under" for an Underside object, "behind" for a RearContainer or RearSurface). As it turns out, the needed preposition is available as a property of the container object, namely objInPrep. This makes it possible to unify the code for the three classes by referring to this property rather than hard-coding the preposition - by doing this, the code for the three subclasses becomes identical, which allows it to be consolidated in a common base class.

The advantage of this change is that it's no longer necessary to change a specialized container's contents-lister class simply because you want to change the preposition used to describe the relationship between contents and container. In the past, you had to change both objInPrep and the contents lister. Now, changing objInPrep is enough - since the default contents listers refer to objInPrep, changes to objInPrep will automatically flow through to the wording in contents listings.

Because the class structure is the same as before, existing code should work unchanged, and you can still use contents lister customizations to achieve effects beyond the simple wording changes that objInPrep provides, if needed.

The new class ActorHelloTopic makes it easier to create a greeting message for cases where the NPC initiates a conversation through a script. Place an ActorHelloTopic object anywhere a HelloTopic would go to create a greeting specifically for the case of an NPC-initiated conversation.

[Update for 3.0.18: disregard the following. These changes were not effected as described, and weren't quite what was intended. See this entry for an update.]

ByeTopic objects can now be used with actors who use conversational states (ConversationReadyState, InConversationState). In the past, the conversational states largely bypassed the topic-based Goodbye processing, so goodbyes had to be written with the specialized goodbye methods on the state objects. This made the conversation states harder to use, since it created special cases you had to be aware of. Now, you can set up a goodbye message by locating a ByeTopic within the InConversationState or within any ConvNode.

The class ComplexComponent now defines stagingLocations as the lexical parent's (which is normally the ComplexContainer's) location. This makes it easier to embed a nested room (a platform, booth, etc) as a component of a complex container object, by allowing inbound travel into the nested room to bypass the complex container and move directly between the enclosing location and the component/nested room. As with other aspects of complex containers, the container and component are meant to look and act like a single object, from the player's perspective, so it's not desirable to treat the container and component as separate objects for the purposes of nested room travel.

Along the same lines, ComplexContainer now tries to remap the actions StandOn, SitOn, LieOn, Board, and Enter to a suitable component. These actions are now automatically remapped to the object returned from the new method getNestedRoomDest(), if any. If that method returns nil, the actions are not remapped after all.

The new getNestedRoomDest() method of ComplexContainer looks for a suitable component to use as the destination of a nested room travel command. By default, it first looks to see if the sub-surface component is a NestedRoom object, and if so, returns that; otherwise, it checks the sub-container component, and returns that if that's a NestedRoom; otherwise it returns nil. This default behavior makes everything automatic for cases where you have exactly one component that's a nested room. If you want to define more than one nested room within a complex container (for example, a large crate that you can STAND ON and also GET IN), you will need to override getNestedRoomDest() to return the appropriate destination by verb, or you can simply override the dobjFor(action) remapTo() definition to point remap to the correct component.

A bug in the parser sometimes caused "truncated plurals" to be selected ahead of exact matches in a player's answer to a disambiguation question. By design, the parser prefers an exact match to a singular noun ahead of a partial match to a plural (for example, with the default six-letter truncation, the word "object" in command input would match either the singular "object" or the plural "objects", but the parser assume the singular word was intended because it's an exact match for the input). However, this principle was being ignored in this case due to the bug. This has now been corrected. (bugdb.tads.org #0000006)
In the past, if a ConvNode was activated via the <.convnode> tag, and the ConvNode's noteActive() method displayed any text, the text displayed within noteActive() appeared out of order in the final output relative to the text containing the <.convnode> tag. This happened because <.convnode> is processed within an output filter, so the entire string containing the <.convnode> had to be processed by the filter before any of it was actually displayed; if any text was displayed in the course of recursively called routines such as noteActive(), that text would as a result show up first in the final output.

The <.convnode> output filter now captures any text that's displayed in the course of activating a new ConvNode, and re-inserts the text into the stream at the proper point. This ensures that the text appears in the correct sequence, so it's now safe to display text from within a noteActive() routine. (bugdb.tads.org #0000003)

In the past, TIAction verbs (those taking a direct object and an indirect object, such as PUT IN or UNLOCK WITH) had the side effect of clearing the parser's memory of gender-specific pronoun antecedents. For example, after typing "LOOK AT BOB; UNLOCK THE DOOR WITH HIS KEY", the parser would forget that "him" refers to Bob, even though there wasn't anyone else mentioned in the second command to supersede the meaning of "him". This has been fixed. (bugdb.tads.org #0000020)
The Room class now applies a lower level of disambiguation "likelihood" for the Examine action to a remote room than for a room containing the actor. In the past, the class applied a lower-than-default likelihood for a room containing the actor, on the assumption that a player who types EXAMINE FOO probably means to examine a nearby object rather than the enclosing room, when both are called FOO. However, there's a third possibility, which is that there's a remote room (i.e., a separate top-level room that's connected to the current location by a sense connection) in scope that's also called FOO. In these cases, we'd still want to select the nearby object over either room; but in cases where we only have the two rooms to decide between, it seems most likely that the player would want to refer to the enclosing room rather than the remote one. This new distinction in the likelihood levels of enclosing and remote rooms accomplishes this: the most likely selection is still a nearby non-room object, the next most likely is the enclosing room, and the least likely is a remote room.
3.0.16

Released 4/10/2008

The new class SimpleLister provides simplified interfaces for creating formatted lists of items. This class is useful when you just want to create a textual listing, using the standard serial-comma generation and so on, without all the hassle of specifying a sense environment, point of view, etc. The class also has a method that creates a text string for a listing (rather than displaying the list directly, which is what the base Lister normally does).

Two concrete instances of SimpleLister are provided, to further simplify particular listings: objectLister, which can be used to format a list of simulation objects; and stringLister, which can be used to format a list of string values.

Commands of the form "X, Y", where both X and Y were words not in the game's dictionary, caused a run-time error ("nil object reference"). This was due to a library bug in the code that resolves the actor object when giving orders to an actor ("BOB, GO EAST"). This has been corrected.
The Goal object has a new property, goalFullyDisplayed, that the hint system automatically sets to true upon displaying the last hint in the Goal's menu list. This can be used, for example, to close a hint for a red herring after the player's seen the full list of hints; this might be desirable to remove clutter from the menu, since the player probably won't worry about an object again once it's been revealed that it's just a red herring.
The templates for TravelMessage, NoTravelMessage, and DeadEndConnector now accept an event list (given as a list in square brackets) in lieu of a double-quoted message string. These classes were already mixable (via multiple inheritance) with EventList, so this change simply makes it more convenient to define instances when using this capability.
Top-level rooms (i.e., Room objects) didn't properly handle ENTER commands (e.g., ENTER HALLWAY) when attempted from a separate top-level room connected via a sense connector. The ENTER handling for Room incorrectly assumed that only one top-level room was in scope at a time, which isn't true when rooms have sense connections. The handler also failed to handle the simpler situation where the actor was in a nested room within the target room.

The Room dobjFor(Enter) handler has been improved to handle such situations. The new handling is as follows:

  • If the actor is already directly in the target room (for example, the player types ENTER HALLWAY while already in the hall), the behavior is as before ("You're already in the hallway").
  • If the actor is indirectly in the target room (for example, the PC is sitting in a chair in the hall, and types ENTER HALLWAY), the command is replaced with GET OUT OF outermost nested room containing actor within target room.
  • If the actor is in a separate top-level room, and there's a travel connector from the actor's current location to the target room, the command is replaced with TRAVEL VIA connector.
  • Otherwise, the command fails with the message &whereToGoMsg ("You'll have to say which way to go").
In the past, when NestedRoom.checkMovingActorInto() decided it had to perform an implied action to move the actor into the nested room, the routine assumed that the action failed if the actor didn't end up in the nested room and in the room's default posture. This condition does hold for the library NestedRoom subclasses when the defaults are inherited, but overriding either the default posture or the implied command for a particular instance can make the condition fail. When the condition fails, the routine infers that the implied action failed, and terminates the whole command.

This condition - specifically, the test for posture - is overly strict. The routine isn't documented as guaranteeing the default posture, and in fact it never did guarantee the default posture anyway, as the implied action was always bypassed if the actor's location was initially correct, regardless of posture.

So, the condition has now been changed to remove the default posture check. The implied command (if any) is now considered successful if the actor is directly in the nested room afterward.

Two changes to BasicChair simplify the way the "get in" commands for the object and its subclasses are implemented.

First, the tryMovingIntoNested() routines in BasicChair and its subclasses have been unified into a single base-class method. In the past, each BasicChair subclass overrode tryMovingIntoNested() to perform the implied action matching the subclass type - SitOn for BasicChair, LieOn for BasicBed, StandOn for BasicPlatform.

Now, the overrides have been removed, and the functionality has been been unified into a single method on BasicChair that the subclasses all inherit. The BasicChair version of the method handles all of the subclasses by deferring to the default posture object (as returned from the defaultPosture property).

Second, the dobjFor(Board) definitions have been unified in a single BasicChair definition, rather than requiring overrides in each subclass as in the past. As with tryMovingIntoNested(), the dobjFor(Board) definition in BasicChair now calls upon the default posture object to carry out the appropriate command. This removes the need to override dobjFor(Board) in each subclass.

The benefit of these changes is that there's less to override when creating a custom type of BasicChair subclass. Setting the defaultPosture also automatically takes care of setting the implied action for "get in" and the mapped action for "board", since tryMovingIntoNested() and dobjFor(Board) now choose the sub-action that matches the default posture. Of course, if for some reason you want these actions to differ from what the default posture selects, you can still override tryMovingIntoNested() and/or dobjFor(Board) to achieve whatever special effects you want. For typical objects where the two match, though, these changes means less work is needed to keep everything in sync.

The "standing" posture object's setActorToPosture() method has been changed slightly. In the past, the routine performed an intransitive Stand action, which causes the actor to stand up in whatever location it's currently occupying. This was at odds with the specification of the method: the routine should not only make the actor stand up, but make the actor stand in a given location; the old version ignored the target location. The routine now does use the target location, and executes a transitive StandOn action with the target location as direct object.

The old implementation was the way it was because the transitive StandOn action didn't always work properly when applied to top-level rooms. However, the handling of StandOn for top-level rooms has since been improved, so this is now a viable implementation. This makes the method work consistently with its specification and with the corresponding methods in the other pre-defined posture objects.

The Decoration class now overrides the SEARCH action so that it uses the standard Thing handling, to be consistent with the way LOOK IN is handled. In the past, SEARCH instead was handled by the Decoration catch-all verb handler, which shows the generic "that's not important" message. This led to different default Decoration behavior for SEARCH vs LOOK IN, which was inconsistent with the library's usual default treatment of the two actions as synonymous.
The English library's typographical output filter object (typographicalOutputFilter) ordinarily adds an "en" space (a space that's slightly wider than a normal inter-word space) after any sentence-ending punctuation, such as a period or question mark. This produces looser spacing between sentences, which some people find easier to read. However, it had an undesirable side effect, which was to add the same spacing after honorific abbreviations like Mr. and Mrs. - these look exactly like sentence endings, as far as the simple substitution rule was concerned.

The filter now specifically looks for a pre-defined set of abbreviations, and suppresses the extra spacing when it finds one. The abbreviations are given by the property typographicalOutputFilter.abbreviations - to change or add to the set of abbreviations, modify typographicalOutputFilter to change this property. (Note that this property is only evaluated once, at program start-up, so changing it on the fly won't have any effect. If you do want to modify the set of abbreviations dynamically, you'll have to instead update typographicalOutputFilter.abbrevPat to use a new RexPattern object with the new set of abbreviations.)

DistanceConnector now allows moving objects through the connector via moveInto - in particular, DistanceConnector now overrides checkMoveThrough() to return a "success" indication. In the past, DistanceConnector inherited checkMoveThrough() from SenseConnector, which disallowed a move via moveInto() if the distance connector was involved.

In the abstract, there's usually no good reason that an object can't be moved through a distance connector. The only reason to disallow it might be that the distance involved is so great that it would take an impractical amount of time to complete the move.

More to the point, though, player commands will generally disallow this kind of operation for reasons of reachability - a touchObj precondition is involved for any basic command that moves an object from one container to another (TAKE, DROP, PUT IN, PUT ON, etc), and that precondition will fail separately because the distance object is unreachable. Because of this, a moveInto() involving a distance connector is almost certainly due to a programmatic (scripted) operation, rather than a basic player command. In the past, you could work around this by calling moveIntoForTravel() instead of moveInto(), but this was a needless complication. This change allows moveInto() to succeed even if a distance connector is involved.

The ShuffledEventList object has a new property, suppressRepeats, that lets you control whether or not a given event can occur twice in a row. This property corresponds to the suppressRepeats property of the ShuffledList object.

By default, this is set to true, which yields the former behavior. You can set this to nil if you don't want to suppress repeats. With very small lists (of three or four elements), the suppression of repeats sometimes makes the result sequence look fairly un-random, because the no-repeats constraint reduces the number of available permutations. If you see output for a small event list that doesn't look random enough, you might try setting suppressRepeats to nil to see if that produces more pleasing results.

ShuffledList now ignores suppressRepeats for a two-element list. If the list has only two elements, and repeats aren't allowed, the list will necessarily yield the predictable sequence A-B-A-B..., which defeats the purpose of the shuffled list.
The Lockable class's dobjFor(Open) check() method now inherits the default handling. Since Lockable is designed to be used as a mix-in class, this addition ensures that any additional Open checks inherited from other superclasses are properly invoked.
The comment pre-parser object (commentPreParser) now sets a runOrder value that's lower than the default, so that it runs before other pre-parsers that inherit the default runOrder. In most cases, it's unnecessary (and even potentially problematic) to run comments through other pre-parsers, since most are designed to parse the usual sorts of command syntax, not the free-form text allowed in comments. Running the comment pre-parser first effectively bypasses other pre-parsers when a comment is encountered, since the comment pre-parser halts further processing of the command when it detects that the command is actually a comment.
In the past, if an actor returned nil from obeyCommand(), the parser ignored the command for the purposes of the AGAIN command. A subsequent AGAIN command in this case simply repeated the previous command - the one just before the refused order. This has been corrected; the parser now remembers a command for AGAIN even if the actor to whom the command is directed refuses it.
Thing.setGlobalParamName() didn't correctly delete any previous name from the global table. (This would only have been a problem if you used the method more than once on a particular object, to change the object's global parameter name on the fly, which seems highly unlikely.) This has been corrected.
3.0.15.3

Released 11/2/2007

There are no library changes in this release.
3.0.15.2

Released 9/13/2007

NonObviousVerifyResult.resultRank is now defined as the same value as IllogicalVerifyResult.resultRank. This makes the parser treat illogical and non-obvious objects as equivalent in cases of ambiguity, so that the parser won't choose one over the other. In the past, non-obvious objects were ranked lower than illogical objects, which caused the parser to choose an illogical object over a non-obvious object in cases of ambiguity. This didn't quite make sense; for the purposes of choosing among ambiguous objects, non-obvious objects really should be treated as equivalent to ordinarily illogical objects, since the point of the non-obvious designation is that the object appears at first glance to be illogical for a given verb.
The parser now incorporates Michel Nizette's vocabLikelihood extension. This extension lets each object set an "intrinsic likelihood" value that affects how the parser chooses among ambiguous objects. When the parser can't tell the difference between two objects using the normal 'verify' procedure, it compares the vocabLikelihood properties of the two objects, and chooses the one with the higher value if they differ. This makes it easier to fine-tune the disambiguation process to reduce unnecessary prompting.
SensoryEmanation.endEmanation() now resets displayCount to nil. displayCount was documented as being automatically reset when a period of continuous presence ended, but in the past no actual resetting was done. We set the value to nil, rather than to 0 or 1, to make it more obvious (when debugging, for example) that the object is not currently in scope.
The English library now defines suitable custom objInPrep, actorInPrep, and actorOutOfPrep values for Surface, Underside, and RearContainer. This ensures that the default messages produce reasonable relational descriptions when referring to the contents of these specialized container types. (In the past, only Surface.objInPrep was suitably customized.)
BasicOpenable.tryImplicitRemoveObstructor() now only attempts an implied Open if the object is closed. When the object is already open, the routine now does nothing. This change is necessary because objects can conceivably create an obstruction even if they're already open: once the object's open, if it's still creating an obstruction, another implied Open would be pointless (and, in fact, could cause an infinite loop).
The new Thing method isOrIsIn(obj) returns true if 'self' is either inside 'obj' or equal to 'obj'. This is useful for cases where you want to check if a given object is anywhere within another object's containment tree, including the other object itself.
When a MultiLoc object is the point-of-view object in a sense calculation, it now connects all of its multiple containers to the scope in addDirectConnections(). In the past, a MultiLoc's containers were only considered part of the scope when the point of view was inside the MultiLoc, but it makes sense for the MultiLoc itself to be the same way, since it inherently "sees" all of its containers.
LocateInParent is now defined as a class. (It was always meant to be, but the definition lacked an explicit 'class' keyword in past version.)
3.0.15.1

Released 7/19/2007

Compatibility warning: The message property cannotTalkToSelf has been renamed to cannotTalkToSelfMsg, for consistency with other message property names. (You should search your existing game code and update any references to the old name to use the new name instead.)

The new gameMain property beforeRunsBeforeCheck lets you control the relative ordering of the "check" phase and "before" notifiers.

In the past, the library always ran the "before" notifiers - beforeAction and roomBeforeAction - before running the check() handlers for the objects involved in the action. To ensure compatibility, the default setting for the new property is beforeRunsBeforeCheck = true - this causes "before" to run before check(), as it always has.

However, in many ways, it's more logical and practical to run the check() phase first first, and then run the "before" notifiers. This ordering allows the "before" handlers to assume that the action is more or less committed to running to completion, since they know that the check() phase has already tested the action and allowed it to proceed. The most common use of "before" handlers is to carry out side effects of an action, so by running all of the check() tests first, the "before" handlers can more confidently invoke their side effects, without worrying that the action will later fail due to a check() condition.

Now, you can never truly call an action "committed" until it's been fully carried out, since even a "before" handler can conceivably cancel a command (with "exit", for instance). You could set up a situation where an action is affected by two "before" handlers, and the one that runs later cancels the command - thus invalidating any assumption in the first one that the command is committed. However, this is relatively unusual, and in any case it's under your control: if you don't use "before" handlers to cancel commands, it won't occur.

If you want to use the new alternative ordering, where "check" runs before the "before" notifications, simply set beforeRunsBeforeCheck to nil in your gameMain object. Some authors have tested this ordering with existing games and found it to work well with existing code, but be aware that you could trigger subtle changes - you might have unknowingly created dependencies on the relative ordering of check() and "before" that will only show up under certain conditions. We therefore recommend using the new alternative ordering only for new code or for projects that are relatively early in their development cycle, to ensure thorough testing of the new ordering.

ThingState now allows tokens to be shared among multiple states associated with the same object. In the past, state words had to be unique among an object's states: it wasn't possible to have a set of states such as "unpainted", "painted red", and "painted blue", because it was invalid for "painted" to appear in two states. This sort of sharing is now allowed.

The old rule was that a word could match an object only if the word didn't appear in any of the object's other (i.e., non-current) ThingState token lists. The new rule is that a word can match an object if (a) the word appears in the object's current ThingState token list, or (b) the word doesn't appear in any of the object's other ThingState token lists. Condition (a) is what's new; it allows words to be shared among states by allowing a word to match the current state as long as it's in the current state's list, regardless of whether it appears in other states' lists.

In the English library, when a command triggers a chain of several implied actions, the announcement for the implied actions now adds the word "then" in each conjunction: "(first standing up, then taking the box, then opening it)". In the past, "then" appeared only in the final conjunction; most people find it reads more naturally to use "then" in each conjunction.
The English library now uses the adjustDefaultObjectPrep() mechanism to generate implied action announcements. This means that verbs like SitOn and GetOutOf will use object-specific prepositions when they're announced as implied actions: "(first sitting on the bench)" vs "(first sitting in the car)".
The actions GetOutOf and GetOffOf now adjust the prepositions in their generated verb phrases according to the actorOutOfPrep definition for the direct object.
AccompanyingInTravelState.initiateTopic() now defers to the next state object. Since the in-travel state is designed to be ephemeral, it's unlikely that one of these would actually contain its own initiated topics, so in almost all cases the desired topic would come from the state that the actor will be in following the travel.
The library now defines a simple template for ShuffledList. The new template takes a single list parameter giving the value list.
ListGroupCustom now defines groupDisplaysSublist = nil by default. This type of group is intended mainly for cases where you want to display a collective message to describe the group, rather than listing its elements individually, so we usually don't want the inclusion of a ListGroupCustom message to trigger the "long list" format (i.e., with semicolons) in the overall list. This change makes this behvior the default.
Non-physical CollectiveGroup objects (i.e., those with 'nil' locations) didn't work when the associated individuals were MultiLoc or SenseConnector objects. This has been corrected.
Actor.knowAbout() now considers an object to be known if it's currently in sight. In the past, an object was known if it was specifically marked as known, or it was marked as having been previously seen. For the most part, an object that's currently visible will also be marked as having been previously seen, since this marking occurs whenever a Look command or travel to a new location lists an object. However, if an object comes into scope (via moveInto) while an actor is in a given location, the object won't be marked as having been seen until the next Look command or subsequent travel away from and then back to the location.

This change corrects a subtle problem that involved possessive phrases in commands. In the past, an object that just came into scope was excluded from matching a possessive noun phrase under certain circumstances, such as in the response to a disambiguation question. This should no longer occur.

In the past, the library seemed to allow commands like PUSH TV INTO BOX (i.e., commands to push a pushable object into a nested room), but on closer inspection it didn't really carry them out: instead, it carried out only the underlying basic travel command, without actually moving the object supposedly being pushed around. The library now rejects PUSH INTO nested room commands with a new message, &cannotPushObjectNested ("You can't push the TV there").

The library rejects these commands by default because we think it'll be relatively rare that games will need to allow them. Given that, it didn't seem worth complicating things by adding a bunch of new methods to provide generalized support, and we also didn't want to introduce that much new code at the current point in the release cycle.

However, the library does now provide a new hook that will make it easier for games to implement PUSH INTO nested commands if they need to. The new hook will also make it possible to implement riding vehicles into nested rooms, which wasn't previously possible. The new hook is a new Traveler method, travelerTravelWithin().

This new method is called on the Traveler object when a nested-room travel command (GET IN, STAND ON, SIT ON, LIE ON, PUSH X INTO, etc) is performed. In simple cases, the Traveler is simply the Actor performing command. However, if the actor is riding in a Vehicle, the Traveler is the Vehicle object; and if the command is a PUSH travel command, such as PUSH TV INTO BOX, the Traveler is a special ephemeral object, of class PushTraveler, created just for the command.

The library provides default implementations of the new hook method for Actor, Vehicles, and PushTraveler. For Actor, the method simply does the same thing that travelWithin() used to do, so existing code will work as it did before. For Vehicle, the method calls the same method on the Actor, so again it will work as it used to. For PushTraveler, the method simply terminates the command with the new message &cannotPushObjectNested.

If you want to allow pushing objects into nested rooms, you'll need to modify PushTraveler.travelerTravelWithin() to do basically the same thing that PushTraveler.travelerTravelTo() does. You'll probably want to set up a new set of notification methods parallel to beforeMovePushable() and movePushable() - you could just use those same routines, but you'll probably want to set up new custom nested-room versions instead, since the conditions and messages will probably need to be different for nested rooms from those used for room-to-room travel.

Note that you could also take advantage of the new method to allow an actor to ride a vehicle into a nested room. The default handling in Vehicle defers to the underlying Actor, and the actor performs the travel as though the Vehicle were any other nested room - which means that the actor gets out of the vehicle, then gets into the new nested room. If you instead wanted this type of command to mean "ride vehicle into nested room," you can override travelerTravelWithin() on the vehicle, so that the method moves the Vehicle itself into the new nested room. (You'll also have to set up preconditions that move the vehicle to the proper staging locations using the same rules that are used for actors, which could take a little work - that's the main reason the library doesn't support it out of the box yet.)

In the past, if an NPC was using AccompanyingState to follow around the player character, and the pair went through an AutoClosingDoor, the door was reported as having closed twice during the turn - once for each actor going through. AutoClosingDoor now skips the auto-closing step if the actor going through is in an accompanying state.
The parser's "OOPS" mechanism had a problematic interaction with literal phrases that showed up under certain conditions. In particular, if the player entered a command that caused some kind of follow-up question, such as a disambiguation query ("which book do you mean...") or missing-object prompt ("what do you want to unlock it with?"), and the player chose to ignore the follow-up question and instead just enter a new command, and the new command contained a literal phrase ("type asdf on keypad", say), the OOPS mechanism was overly aggressive in deciding the command contained a typo.

The root of the problem was that follow-up questions are parsed according to their own special grammars, which are much more limited than the full grammar used at the main command prompt. Normally, when the player responds with something that doesn't match one of these special grammars, the parser assumes the player intended to bypass the follow-up question and just enter a new command. However, if parser manages to find a non-dictionary word in the player's input, it invokes the OOPS mechanism to see if the player actually intended to answer the follow-up question but made a typographical error entering the response. The problem is that if the player entered a literal-phrase verb, the presence of a non-dictionary word doesn't necessarily imply a typographical error. For normal commands (as opposed to follow-up questions), this isn't an issue, because the parser finds the literal when matching against the full grammar. For follow-up questions, though, the parser never noticed the literal-phrase interpretation of the input, since it wasn't matching against the full grammar, so it prematurely decided that the input as mis-typed.

The OOPS mechanism now makes an extra check when presented with a non-dictionary word in the response to a follow-up question. Before deciding that the input really does contain a typo, the OOPS mechanism first checks the input to see if it matches anything in the full command-line grammar; if the parser finds a match, it assumes that the entry was meant as a brand new command, and so it bypasses the follow-up question and re-parses the input as a new command.

In the past, calling getOrigTokenList() on an EventAction object caused a run-time error. This was because EventActions don't have the token list data that the parser normally attaches when creating an Action to represent a parsed command line, which in turn is because these actions aren't created from command parsing but are instead "synthetic" actions, created internally by the library for bookkeeping purposes. (This same problem might have affected other synthetic actions as well, but it's only known to have shown up with EventActions.)

This has been corrected. Action.getOrigTokenList() now checks to make sure there's a parser token list attached to the action, and if not, the method return the parent action's token list; and if there's no parent action, the method simply returns an empty list.

HermitActorState now sets its property limitSuggestsion to true by default, to prevent the TOPICS command from displaying any topic suggestions from the actor or conversation node. This is desirable because the hermit state effectively blocks any topics defined outside the state: the whole point of the state is that it makes the actor essentially unresponsive to conversational overtures. So there's generally no point in offering topic suggestions while the actor is in the state.

In some cases, you might want to override this for a particular hermit state object. In particular, if it's not outwardly obvious that the actor will be unresponsive, you might still want to allow suggestions, since the player has no way of knowing that the actor won't respond to questions until she attempts asking them. Similarly, if the hermit state is expected to persist for a short time only, you might want to continue to allow suggestions so that the player knows there are useful topics to explore when the actor becomes responsive again.

ByeTopic now overrides impliesGreeting to suppress any implied HELLO greeting. This means that if the player explicitly says GOODBYE to an NPC, and no conversation is in progress, the goodbye response will be displayed by itself, without an implied greeting exchange. In the past, ByeTopic inherited the default 'true' value for impliesGreeting, so saying GOODBYE to an NPC outside of a conversation caused an implied greeting, followed immediately by the explicit goodbye response. In most cases, this ended up looking pretty strange - not only was the self-canceling HELLO-GOODBYE exchange odd on its face, but in most cases the implied greeting also looked redundant from the player's perspective, because a player tended to say GOODBYE explicitly only when she was under the impression that some kind of conversational interaction was already under way.
In the past, conversation-flow problems sometimes occurred if a topic changed its "isConversational" status as a side effect of showing the response for the topic. This was because ActorTopicDatabase.showTopicResponse() waited until after invoking the response to determine whether the response should affect the conversation flow, so the status change side effect caused showTopicResponse() to use the new setting - meant for the next response - rather than the setting for the response it actually showed. This has been corrected; showTopicResponse() now makes its determination based on the isConversational status as it stands just before invoking the response.
The finishGameMsg() function now explicitly runs the score notifier daemon before it displays the end-of-game message. This ensures that the usual score-change message is displayed if any points were awarded in the course of the action that triggered the end of the game. Without the explicit invocation, the notifier daemon wouldn't typically get a chance to show a notification for the final point award: the notification is normally shown just before the next command prompt after points are awarded, and at the end of the game there's not usually another command prompt forthcoming.
In the English library, inlineListingContentsLister didn't always generate correct verb agreement for its prefix message for a "wide" listing ("You are carrying tongs (which contains ...)"). This has been corrected.
In the past, searching a RoomPart nominally containing a RoomPartItem sometimes caused a "nil object reference" run-time error. This was due to a missing point-of-view object setting; the code where the error occurred now applies a default POV of the command actor when no explicit POV is set.
Container and Openable objects now apply a touchObj precondition to the Search action. This requires that the actor be able to reach the object in order to search it. This change is intended to better enforce the idea that searching a container involves some actual physical manipulation, such as shifting the contents around to look behind or under things, momentarily removing contents to see what's underneath, and so on. The actor at least has to be able to reach the object to perform this kind of manipulation.
In the past, AGAIN didn't work properly when used to repeat a FOLLOW command involving an NPC using the "guided tour" mechanism (the TourGuide and GuidedTourState classes). This was due to a subtle turn-timer problem with AGAIN that affected commands like FOLLOW that use nested or replacement actions in the course of their execution. This problem was observed in the FOLLOW/AGAIN situation, but could also have affected other commands. The root problem has been corrected.
3.0.15

Released 3/8/2007

The Follow action has always adjusted the scope list to include any NPCs that the target actor remembers having seen leave, whether or not they're still within sight. This allows you to FOLLOW a character who's no longer present - the whole point of FOLLOW would be defeated if this weren't possible, after all. However, in the past, this scope list expansion created duplicate entries in the scope list if an actor with a "follow memory" was also still physically present - the actor was added to the scope list once for ordinary visibility, then again for the "follow memory." This duplication caused problems in some situations, since other parts of the library expect the scope list to contain only unique entries. The Follow action now ensures that the list remains unique after its additions.
3.0.14

Released 2/9/2007

There are no library changes in TADS 3.0.14.

3.0.13

Released 1/19/2007

The adv3 English library has a new option setting for the parser's truncation length. The truncation length has always been customizable in the low-level system objects that the parser uses to look up and match vocabulary words, but the library didn't formerly provide a good way for games to override its truncation length setting. In effect, the truncation length was hard-coded into the English library, and the only way for authors to change it was to edit en_us.t; most authors would rather not do that because of the hassle of merging their own changes into future library updates.

The new setting is in gameMain.parserTruncLength. The default setting is 6 characters - this is the same as the English parser's old hard-wired setting, so existing code will behave the same as it did in the past.

As part of this change, the new method languageGlobals.setStringComparator() lets you change the active string comparator dynamically. You can use this method if you want to change the truncation length, or other comprator settings, on the fly during the game.

The English library now allows a TAction VerbRule to specify a custom preposition for a default object announcement, overriding the preposition coded in the verbPhrase. This is useful for a few verbs where the preposition tends to vary according to the direct object. For example, SIT might use ON in some cases but IN for other cases: "on the stool" vs "in the car."

To accomplish this, TAction.announceDefaultObject() first parses the verbPhrase to get the standard preposition, then calls a new method on self, adjustDefaultObjectPrep(prep, obj). 'prep' is the default preposition from the verbPhrase, and 'obj' is the direct object of the command. The routine returns the new preposition string. The default implementation simply returns 'prep', but the StandOn, SitOn, and LieOn actions override this to return the direct object's actorInPrep.

InitiateTopics can now be located within ActorState objects. In the past, InitiateTopics were only found when they were directly within an Actor or a TopicGroup within an actor. The library now looks for InitiateTopics in the current actor state object as well, which is more consistent with other topic types.
inputManager.pauseForMore() now re-activates the transcript if it was active before the pause. In the past, this function simply flushed output and left the transcript deactivated. This was problematic for certain library subsystems that depend upon the transcript being active throughout a command. Now, the function flushes the transcript's pending output up to the pause, then reactivates the transcript after the pause, so that transcript capture proceeds as normal throughout the rest of the command.
In the past, the library generally assumed that NestedRoom objects would only be located inside either Rooms or other NestedRooms, but never inside ordinary Things. However, in practice it's often useful to be able to put a nested room inside something that's not itself a nested room.

A number of small library changes should now make NestedRooms work properly when located inside ordinary Thing objects. The changes are mostly internal and should have no impact on existing code, but for reference, here's what's changed:

  • The methods getRoomPartLocation(), getLocTraveler(), atmosphereList(), roomDaemon(), getDropDestination(), and effectiveFollowLocation() have been moved to Thing, with essentially no changes. These are the methods the library calls on containers of nested rooms, so moving them to Thing allows any game object to contain a nested room.
  • The definitions of most of the above methods have been removed from BasicLocation, since BasicLocation now simply inherits the Thing definitions. The definitions of roomDaemon() and atmosphereList() have been moved to Room instead, since those definitions were intended for top-level rooms.
  • Room now defines checkActorOutOfNested() to find and defer to the child object containing the actor. This ensures that the check is forwarded to the nested room containing the actor.

In addition to the changes above, NestedRoom no longer assumes that its immediate location is the destination when an actor within the nested room leaves the nested room. Instead, NestedRoom now consistently uses the value given by exitDestination. Further, NestedRoom.exitDestination itself no longer uses the immediate container as its default setting, but instead uses the default staging location as given by the defaultStagingLocation method.

A change in 3.0.10 introduced a parsing problem with distinguishing nouns from plurals when the words were right at the parser's truncation length. To be more precise, if you defined a noun that was exactly the truncation length, and you also defined a plural for that noun as the noun plus "s" (or, actually, as the noun plus any other letters), then the parser would incorrectly interpret the noun word in player input as though it were plural.

For example, the default truncation length is 6 characters, so this problem occurred with "button" and "buttons" defined as a noun and plural, respectively. In this case, if you had two or more objects in scope with vocabulary like 'red button*buttons', and the player typed something like "x button", the parser incorrectly treated the "button" in the player's input as though it were plural, so it applied the Examine command iteratively to all of the buttons in scope, rather than asking the player which singular button they intended to examine.

This problem has now been corrected.

The message definition for cannotSetToMsg was missing from the English message list (in en_us/msg_neu.t). The message is now there.
A command of the form PUSH pushable INTO object, where the second object was some random, non-enterable thing, yielded an unformatted message ("{that/he dobj} {is} not something you can enter"). The underlying problem was that the action remapping invoked by PushTravelViaIobjAction didn't properly set up the object parameters for the remapped verb, leaving the message system unable to find the direct object. This has been corrected.
The Attachable class had a problem that caused a run-time error when the player entered a command of the form DETACH object, where object was an Attachable and the command had no indirect object. The problem came from a name conflict between an Attachable method called cannotDetachMsg(obj), which took one parameter, and a library messages property of the same name, which takes no parameters. When the message resolution system tried to retrieve the library message from the direct object, it invoked the zero-parameter version of the property, which caused the run-time error due to the parameter mismatch with the one-parameter version implemented in Attachable.

To fix this problem, the method in Attachable has been renamed to cannotDetachMsgFor(obj). The library message has the same name as before.

Any existing code that overrode the Attachable method will have to adjust for the name change. That said, it seems almost impossible for this change to affect existing code, since the very bug that we're talking about here would have prevented the override from working properly in the first place.

A library bug caused a run-time error ("stack overflow") on commands like REMOVE ME or REMOVE nested room containing me. This has been corrected.

(The specific problem is as follows. The library assumes that a REMOVE X command will result in the actor taking X, and so as a sanity check calculates how much weight the actor would be holding if that were allowed. When X is the actor or contains the actor, the hypothetical weight check created a circular containment situation; the stack overflow came from the library's attempt to recursively visit all of the hypothetical contents of the actor, which is of course an infinite loop in a circular containment situation. To avoid this problem, the library now ignores hypotheticals that would create circular containment. It's safe to ignore these hypothetical tests because commands that perform them should always be disallowed anyway, since actual circular containment is never allowed.)

A library bug caused a run-time error ("nil object reference") on entering a command of the form object, unknown word, where object was any in-scope non-Actor object. This has been corrected.

(The problem came about because the parser attempted to treat such a command as though it were directed to an NPC. This is some special handling that applies when the command has a syntax error; the point is to let the author customize the parsing error messages for orders given to particular actors. However, when object isn't an actor, this is problematic, because the library assumes that it can call certain Actor methods on the object in question. The fix is that the library will only apply this handling in cases where the object is actually an Actor; in other cases, it won't assume that the command was intended as an order to an NPC, so it will simply use the default parsing error messages.)

3.0.12

TADS 3.0 General Release version - Released 9/15/2006

The library didn't properly handle situations where an NPC order involved multiple objects and a failed implied sub-action. If an implied action failed for one of the objects involved, the implied action was assumed to have failed for all subsequent objects in the multiple object list as well, even if the subsequent implied actions actually succeeded. This resulted in self-contradictory transcripts, where an implied action was reported as successful, but then was followed by a message that the implied action had failed:

  Bob takes the coin.
  Bob must be holding the coin first.

The problem was that the implied action mechanism was incorrectly considering the failure status for the entire top-level command when determining if the subsequent implied actions failed. Instead, it should have been checking the status of the implied actions themselves. It now does this by looking for a failure report within the implied action's reports, rather than looking for a failure anywhere within the entire transcript.

SpaceOverlay.getWeight() now omits the object's "contents" from the weight calculation if the contents are to be abandoned when the object is moved. If the contents are to be abandoned, it means that they're not actually attached to or contained within the space overlay, but are simply colocated with it; they thus have no contribution to the overlay's total weight. If the contents are not to be abandoned on moving the overlay object, they're effectively attached to it, so they do contribute to its weight as normal.
CaptureFilter is now a subclass of OutputFilter (it was formerly just an 'object'); and SwitchableCaptureFilter is a subclass of CaptureFilter (it also was just an 'object'). This should make no difference functionally, as an output filter is only required to implement the filterText() method, but is desirable anyway in that it makes an ofKind(OutputFilter) test recognize these object types as output filter subclasses.
3.0.11

Released 9/8/2006

Very slight compatibility risk: NameAsOther (and thus NameAsParent) no longer maps the "in" names to its target object. The "in" names are the names generated when an object is described as contained within the NameAsOther. In the past, these were mapped to the target object along with all of the ordinary names for the object. However, this was the wrong behavior for ComplexComponent, which inherits from NameAsOther, because the containment relationship between a ComplexContainer and its contents is defined by the container subclass mixed with ComplexComponent in the object's superclass list, not by the target object, which in this case is the ComplexContainer of which the ComplexComponent is a part.

Although it's conceivable that some other applications of NameAsOther would actually want the old behavior, it seems highly unlikely, so we don't expect any practical compatibility list.

However, we have provided a new mix-in class, ChildNameAsOther, that adds mappings for all of the "in" names to the target object. So, if you have a NameAsOther that depends upon the old "in" name mapping, just add ChildNameAsOther to the object's superclass list (right after NameAsOther or NameAsParent), and you'll get the same behavior as before.

In 3.0.10, the ImpByeTopic was differentiated into a couple of subclasses to allow handling implicit goodbyes differently when desired (see below). However, this change didn't handle one type of goodbye correctly, namely the NPC-initiated goodbye, via npc.endConversation(). The change incorrectly made it so that those goodbyes were treated the same as explicit player-initiated goodbyes.

Prior to the 3.0.10 change, NPC-initiated goodbyes were subsumed into the undifferentiated "implicit goodbye" category, and they should clearly remain in that category; they simply need to be differentiated like the other implicit goodbyes were in the 3.0.10 change.

To this end, the new topic class ActorByeTopic has been added; this is analogous to BoredByeTopic and LeaveByeTopic, and is used when the NPC terminated the conversation via npc.endConversation(). In the absence of an active ActorByeTopic, the active ImpByeTopic will be used instead. This restores compatibility with pre-3.0.10 code (where there was no differentiation among "implied goodbye" types, and npc.endConversation() events were handled as implied goodbyes), while providing a specific topic type to handle this one type of goodbyes.

The English library's "instructions" module (instruct.t) now uses HTML markups to display typographical quotes and apostrophes ("curly quotes") throughout the text of the standard instructions. Thanks to Greg Boettcher for making this improvement.
The detailed-naming scheme for object announcements (see below) introduced in 3.0.10 has been made optional, and disabled by default. Testing reveals that the mechanism as currently designed is too twitchy for some people's taste, so we've disabled it by default for the time being; however, the code is all intact, for those who want to use it as-is or tweak it for their needs. To enable the detailed announcement naming, set gameMain.useDistinguishersInAnnouncements to true.
The BannerWindow system had a flaw in the way it re-initialized banners after a RESTART. The problem showed up in cases where there were dependency orderings among the windows, so that one window's initBannerWindow() had to call another's initBannerWindow() in order to create the windows in the correct order. The problem didn't always happen even in cases of ordering dependencies, since it also depended on the arbitrary ordering of the VM's internal object lists. When it happened, the problem manifested itself by creating extra copies of an affected banner window at each RESTART. This has now been corrected.
In banner.t, the formerly anonymous InitObject that handles banner initialization (and post-RESTART re-initialization) now has a name, bannerInit. This is so that games can use "modify" and "replace" with the object, and also so they can refer to it from the execBeforeMe properties of other InitObjects, for initialization dependency ordering purposes.
"Follow mode" for NPCs ("Bob, follow me") didn't work correctly when the actor being followed moved between two top-level rooms connected by a sight sense connector. This has been corrected. (The problem was that the code that carried out the "follow" attempted to move the follower using a "local travel" action - something like STAND ON STAGE or ENTER BOOTH - any time the target actor was still in sight. The follower now attempts local travel only if the target is still in sight and the target is within the same top-level room; otherwise, the follow uses a suitable full-fledged travel action.)
In Actor.actorActionFollow(), if the actor is already in "follow" mode for the requested other actor, a message is now displayed to this effect (alreadyFollowModeMsg: "I'm already following Bob"). This won't be a factor by default, since the library automatically cancels "follow" mode any time a new command is issued to an actor before attempting to enact the new command. However, games might want to override this auto-cancel behavior, in which case they might encounter this situation in actorActionFollow(). In the past, the routine did nothing at all - it didn't even show a message, so the generally undesirable "Nothing happens" was displayed by default.
In the past, an actor that started in an InConversationState triggered a run-time error at start-up, due to an initialization order problem in the library. This has been corrected. (In particular, an actor's boredAgendaItem property is now initialized via a perInstance() definition rather than in initializeActor(). This ensures that the property is initialized before it's needed. In the past, the order of initializations sometimes resulted in the library trying to use the actor's BoredAgendaItem object before it was initialized, leading to a "nil object reference" error.)
Room's condition for remapping the GetOutOf action to the Out action has changed slightly. In the past, this remapping was performed only if the 'out' direction had an apparent destination, which is only the case when the actor attempting the travel already knows the destination (such as from past experience or at-hand information). This condition wasn't quite right, though. Instead, the condition should have been simply that the 'out' direction has an apparent connector - that is, there's a visible way to travel in the 'out' direction. Room has been changed to use the new condition.
The template (in en_us/en_us.h) for DeadEndConnector now makes the apprentDestName entry optional. This allows using the template to define a dead-end connector that merely displays a message when traversed, without giving it a name.
In the English library, PathPassage now limits its remapping of the Take action to the TravelVia action to cases where the entered verb phrase was literally "take." This prevents other phrasings, such as "pick up path" or "get path," from being interpreted as attempts to travel along a path.
The message sayTravelingRemotely in the English library has been corrected to add the word "to" before the destination name.
3.0.10

Released 8/17/2006

Incompatibility warning: (This note concerns a change that was made in 3.0.9 but inadvertantly omitted from the 3.0.9 release notes. We're mentioning it now in case anyone was affected by it and needs help adjusting their code for the change.)

In version 3.0.9, gameMain.verboseMode was changed from a simple true/nil property to an object of class BinarySettingsItem. Any existing game code that attempted to turn verbose mode on or off by setting gameMain.verboseMode to true or nil will now encounter a run-time error the first time the player enters a travel command.

If you want to set the default verbosity mode explicitly in your game, you can't do it any more by setting gameMain.verboseMode to true or nil. Instead, you can add a line like this to a start-up routine, such as gameMain.newGame():

   gameMain.verboseMode.isOn = true; // turn on verbose mode

Note that the verbosity mode is now part of the "global preferences" mechanism, so in most cases it's best for games not to change it explicitly, instead leaving it up to the player to decide on the setting. In the past, some authors liked to set a verbosity mode that they felt was most suitable for the game. Now that the player can specify a set of default preferences that they wish to apply to all games, it's better for authors not to presume to change the player's default settings without a good reason. As with any other rule, there are bound to be exceptions, so if you have a really good reason to override the player's preferences then you should feel free to do so. But if you're tempted to override the player's preferences just because you like it a particular way, you might want to reconsider.

Minor incompatibility warning: The Lockable class now has initiallyLocked set to true. This means that all Lockable objects now start out locked by default (i.e., unless your game code specifically overrides initiallyLocked to set it to nil for a given Lockable). In the past, Lockable didn't define initiallyLocked at all (so it defaulted to nil), but Door and IndirectLockable defined it as true - so some Lockables formerly started out locked by default while others were unlocked by default. This change should be generally beneficial, since (1) it simplifies matters by making the initial lock status consistent across all Lockables, and (2) the vast majority of Lockables start out locked anyway, so "locked" is the better default.

If you have any Lockables in existing code that you specifically intended to start out unlocked, and you didn't explicitly set initiallyLocked to nil in those objects, you'll have to do so now.

Minor incompatibility warning: The method Action.callVerifyPrecond has been renamed to callVerifyPreCond - that is, the 'c' in 'precond' is now capitalized. This change is for better naming consistency, since all of the other symbol names in the library that include the substring "precond" capitalize the C. This routine is intended mostly for internal use within the library, so it should affect little or no existing game code.
Minor incompatibility warning: The NOTE command has been removed, and replaced by a new comment syntax that lets the player enter a comment by starting a command line with a punctuation-mark prefix.

The default comment prefix is now an asterisk ("*"). You can change this to any prefix string you'd like by modifying the commentPrefix property of the commentPreParser object. You can also control whether or not leading whitespace is allowed before the comment prefix (it is by default) by modifying commentPreParser's leadPat property.

Using NOTE as the comment prefix was problematic because NOTE is a common enough word that games often want to use it in an object's name, and this creates situations where a user might want to start an input line with NOTE with the intention of referring to an object, not of entering a comment. It was essentially impossible in some of these cases to reliably determine which the player meant. The new approach avoids these problems by using syntax that should be unambiguous in nearly all games.

In the past, there was a StringPreParser object in the English library that helped the NOTE command by quoting the note text in some cases. This preparser has been removed, and a new preparser has been added in its place, but this time in the general library, in misc.t. The new object is called commentPreParser, and it performs all of the comment handling itself, without the need for a separate NOTE action.

As part of this change, NoteAction and NoteIAction have been eliminated. In the unlikely event that you used 'modify' to change the behavior of these actions, you'll have to rework your code. Look at the commentPreParser object for details of the new arrangement.

Minor incompatibility warning: Each actor now has its own separate lookup table of ConvNode names. This means that you don't have to worry about making ConvNode names unique globally - you only have to make sure that names are unique within a single actor's set of nodes.

This shouldn't affect existing game code, except for cases where you refer directly to conversationManager.convNodeTab. You should scan your source for occurrences of "convNodeTab" and change any that you find to "actor.convNodeTab", where "actor" is the Actor who owns the table. The most likely place for game code to refer to convNodeTab is in a "modify conversationManager" definition.

Minor incompatibility warning: Thing.getExtraScopeItems() is now required to return a list - nil is no longer a valid return value for this method. Use an empty list instead of nil to indicate that there's nothing to add to the current scope. The old nil return code was inconsistent and unnecessary, as an empty list always functionally meant the same thing anyway.

It's very unlikely that this change will affect any existing game code, since any game code that overrides this method almost certainly did so to add something to the scope. However, you do a quick search through your game code for getExtraScopeItems(), and make sure that you don't have any nil returns; if you do, simply change them to empty lists ("return [];").

Minor incompatibility warning: Thing.getListedContentsInExamine() has been renamed to getContentsForExamine(), and its operation changed slightly. The old name was a bit misleading, in that the method actually needs to return all of the visible contents of the object, whether or not they're marked as listable. Unlistable contents need to be included in this list so that their listable contents can be included recursively. This is the operational change: in the past, only listable contents were included in the returned list; now, all visible contents are included.

This change corrects an inconsistency that occurred in cases where an object had fixed-in-place contents that themselves had contents. In the past, directly examining such an object didn't mention anything about the contents of the second-level containers, while a simple LOOK did include the inner contents. The change makes the two cases consistent.

This change should have little or no effect on existing code, since the former getListedContentsInExamine() method is an internal method that's unlikely to have been called or overridden in game code. If you overrode this routine, though, you'll need to apply the name change to your code, and make any adjustments for the slight change in the method's semantics.

Minor incompatibility warning: the "equivalent object" mechanism has changed substantially, although existing game code shouldn't be affected unless it was modifying the equivalence mechanism itself.

In the past, equivalence was based on the superclass of the object. Two objects were equivalent if (a) they both had isEquivalent set to true, and (b) they had identical superclass lists. This approach to equivalence was occasionally problematic, particularly when using "proxy" objects to modify the behavior of objects involved in equivalence groups. In addition, the superclass approach was somewhat counterintuitive: the whole point of the equivalence mechanism is to treat objects with identical names as interchangeable, but using superclasses as the basis of the equivalence had nothing to do with the naming.

The new scheme is based on a new property called equivalenceKey, which is defined in the language module. In the English library, the default setting of this new property is the disambigName of the object. So, equivalence groups are now simply based on the basic disambiguation name of the object. This is much more consistent with the disambiguation mechanism itself, because it means that the parser decides whether objects are interchangeable using essentially the same logic that the player uses intuitively: if the game always refers to two objects by the same name, they're interchangeable.

Another advantage of the new scheme is that it's much more customizable than the old scheme. Since the basis of the equivalence decision is now distinguished as a separate property (equivalenceKey), you can control equivalence groups simply by overriding the property. You could even effectively restore the old scheme by defining equivalenceKey as getSuperclassList() - this would make the immediate superclass list of an object the basis of its equivalence grouping, producing the same behavior as in the past.

Note that if you change an object's equivalenceKey dynamically during play - or if you change its underlying disambigName property - the object's listing group won't be automatically updated. If you do make such a change and you want to update the object's listing group, just call initializeEquivalent() on the object. (Under the old scheme, there was really no way to change an object's grouping - even if you changed its name, it was still grouped based on its class, which could have caused strange results in listings. The new scheme at least allows for this kind of change, although it requires this manual step to keep the list grouping settings in sync.)

In the past, when EXAMINE was applied to a Room, the Room simply turned the command into a nested LOOK AROUND command. This has changed slightly. Now, the Room directly does the work that LOOK AROUND would do, without the nested command, and with the difference that the initial line with the room name in boldface isn't included in the output. (This is accomplished by omitting the LookRoomName flag from the 'verbose' flags for the Actor.lookAround() call.)

There are two reasons for this change. First, when the player explicitly types EXAMINE room name, it's somewhat redundant to include the room name in the output. Second, when the player types EXAMINE ALL or EXAMINE list of things, the standard multi-object-command output format already prefaces the results for each item in the list with the name of the item ("kitchen:"), which made the room name line especially redundant in this case.

An object can now be associated with more than one CollectiveGroup. This allows an object to be a member of multiple disjoint groups. For example, you could create a group for "treasure," and a group for "jewelry," and put some objects in both groups, while leaving others in one group or the other.

To define multiple CollectiveGroups for a given object, use the new property 'collectiveGroups' on the objects that you want to associate with the groups. Set this property to a list with the CollectiveGroup objects to associate with the given object.

You can still use the single-valued 'collectiveGroup' property, so existing game code will continue to work unchanged. However, 'collectiveGroup' is now obsolescent, so you should not use it for new code - use 'collectiveGroups' instead. For compatibility with existing code, the library's default definition of Thing.collectiveGroups is a method that returns an empty list if collectiveGroup is nil, or a one-element list containing the collectiveGroup value if the value is not nil. Since this is slightly ineffecient, support for the old 'collectiveGroup' will probably eventually be removed, at which point the default for 'collectiveGroups' will simply be an empty list.

Several new classes bring functionality parallel to RestrictedContainer to surfaces, undersides, and rear containers and surfaces. The new classes are RestrictedSurface, RestrictedUnderside, RestrictedRearContainer, and RestrictedRearSurface. These new classes (along with RestrictedContainer itself) are all based on the new mix-in class RestrictedHolder, which defines generic containment restriction rules that can be applied to any container subtype.

The new classes work just like RestrictedContainer did, and RestrictedContainer itself hasn't changed its behavior (it's been refactored slightly for the new base class, but this is just an internal change that shouldn't affect any existing code). To accommodate the new types of restriction, suitable new library "player action" messages have been added (cannotPutOnRestrictedMsg, cannotPutUnderRestrictedMsg, cannotPutBehindRestrictedMsg).

In the past, the default Search action handling was the same as for LookIn. Now, the Search handling is slightly different for Container and Openable.

First, Openable adds an objOpen precondition for Search if the actor isn't inside the object; for LookIn, this is skipped if the object is transparent.

Second, in the Search check() method, Container requires that the object be openable or transparent to the "touch" sense, or that the actor is inside the container. LookIn is similar, but only requires that the container to be non-opaque in the "sight" sense.

The reason for these changes is that Search implies a more thorough, physical examination than LookIn does. To most people, an explicit search involves physically poking through an object's contents, while "look in" is more passive, implying just having a look inside.

In the past, the Thing handlers for PUT UNDER and PUT BEHIND included touchObj preconditions on the direct object instead of objHeld preconditions. These have been changed to objHeld conditions; since these verbs generally require physically moving the direct object, the correct precondition is that the direct object be held, just as for the other PUT verbs.
A new Action method, getEnteredVerbPhrase(), returns the action's original verb phrase text as typed by the player. The return value is a string giving the verb phrase in a canonical format: specifically, the result is all in lower-case, and each noun phrase in the player's input is replaced by a placeholder token: '(dobj)' for the direct object, '(iobj)' for the indirect object, '(topic)' for a topic phrase, and '(literal)' for a literal text phrase. Only the verb phrase is included in the result - there's no target actor phrase, and no sentence-ending punctuation included. The noun phrase replacements apply to the entire noun phrases, so the single placeholder '(dobj)' could represent an entire list of direct objects. For example, if the player types "BOB, PUT THE BOOK AND PENCIL IN THE BOX AND GIVE IT TO ME", calling getEnteredVerbPhrase() on the PutIn action would yield 'put (dobj) in (iobj)', and calling the method on the GiveTo would yield 'give (dobj) to (iobj)'.

There are two reasons why the method returns the canonical format rather than the full text of the entire command. First, the full text is already readily available, via gAction.getOrigText(), so there's no need for a new method to retrieve this information. Second, and more importantly, the canonical format isoaltes the verb phrase structure of the player's input, independently of any noun phrases, making it easy to determine exactly which verb phrasing the player actually used.

This method is most useful in cases where the library's verb rules define two or more different phrasings for the same Action, and you need to be able to distinguish exactly which variant the player entered. For the most part, this is unnecessary: when the library's verb rules include synonyms, it's because the different phrasings usually have exactly the same abstract meaning, hence it's enough to know which Action matched the grammar. In some cases, though, a particular verb applied to a particular object has an idiomatic meaning different from the usual meaning for other objects, and in those cases the generic synonyms often fail to be idiomatic synonyms.

For example, the library defines "get (dobj)" and "take (dobj)" as synonyms for the Take action, because GET and TAKE can almost always be used interchangeably. However, if you were defining an "aspirin" object, you might want to treat the command "take aspirin" as meaning "eat aspirin," but you would still want "get aspirin" to mean "pick up aspirin." You could handle this by overriding the dobjFor(Take) action() method on the aspirin object, comparing gAction.getEnteredVerbPhrase() to 'take (dobj)', and calling replaceAction(Eat, self) if it's a match.

The new Action method getPredicate() returns the verb phrase match tree object that the parser resolved to the Action. The English library uses Action objects as predicate match tree objects, so in the English version this simply yields the original Action. However, non-English libraries can use separate objects for verb phrase match tree objects, so if you need any information from the verb phrase's grammar tree (for example, if you need to call getOrigText() or getOrigTokenList()), you should use action.getPredicate() rather than using the action object directly. (This does not apply to the new method getEnteredVerbPhrase() above - that method is explicitly defined on the Action, not the grammar object. getEnteredVerbPhrase() itself calls getPredicate() to get the grammar information, so you can simply call getEnteredVerbPhrase() directly on the Action object.)
The library looks to a new global variable, gLibMessages, to determine which object to use as the "library message" source. (This is the object used for messages that don't logically relate to the current actor; generally, these messages are replies to meta-commands, or text fragments used to construct descriptions.)

In the past, the library simply used the libMessages object directly as the source of these messages. Now, the library instead uses the current value of gLibMessages as the message source. The default value of gLibMessages is libMessages, and the library never changes this value itself, so the default behavior is exactly the same as before.

The purpose of this change is to allow a game to switch to a new set of library messages dynamically during play. For example, if your game is structured into chapters with different points of view, you might want to use a distinctive writing styles for the different chapters, in which case you'd want to change all of the library messages at each chapter transition. To do this, simply set gLibMessages to refer to a new object at each point when you want to switch message sources; subsequent messages will come from your new source object. Note that it's not necessary to do this if you only want to customize messages statically (i.e., throughout the entire game); for that, you can just 'modify libMessages'.

In the past, the Floor class replied to THROW object AT FLOOR with the same default message ("you should just put it down instead") in all cases. This reply is no longer used in cases where the Floor is unreachable; instead, the standard THROW handling applies, since the player couldn't accomplish the same thing with DROP.
RoomPart.getHitFallDestination() will now take into account any explicit 'location' setting in the RoomPart when determining the destination. In the past, an explicit 'location' setting was ignored, and a valid destination could only be found if the RoomPart was in the same top-level room as the actor. Now, if the RoomPart has an explicit non-nil location setting, that takes precedence in determining the destination. This is useful when creating connected top-level locations with room parts that you want capable of serving as THROW targets.
When throwing an object at a target, and the target happens also to be the place where the object is described as landing when thrown at that target (i.e., the target is the nominal drop destination of its own the "hit-fall" destination), a new default message is used, of the form "The projectile lands on the target." In the past, the message was the awkward "The projectile hits the target without any obvious effect, and falls to the target." This was most likely to occur when the target was something like a floor or ground object, since that's the only case where the target is likely to also be the nominal drop destination. This change is in the routine DropTypeThrow.standardReport().
GIVE TO, SHOW TO, and THROW TO now use different messages depending on whether the indirect object is an actor or an inanimate object. For ordinary Things, the message is now of the form "You can't give anything to X"; for Actors, the message is "The X does not appear interested." In the past, the latter message was used for all objects, animate or not. Imputing "interest" to an inanimate object could be read as either intentionally snide (as though the library were sarcastically calling the player dense for trying such an obviously stupid thing) or simply wrong (as though the library didn't know the difference); since msg_neu is supposed to affect a neutral narrative tone, neither alternative is desirable.

In addition, showing or giving an Actor to itself is now handled with a separate message ("Showing X to itself would accomplishing nothing").

REMOVE dobj now uses a separate reply if the object is already being held: "There's nothing to remove the X from." In the past, the command was unconditionally turned into TAKE dobj, which showed "(from you)" as the implied indirect object part, which was a bit awkward.
When the THROW direction command is used with a nonportable object (as in THROW DESK EAST), the result is now the same as for commands like MOVE and PUSH: "The desk cannot be moved."
The default response for THROW DOWN has been changed to use the same message as for DROP when applied to an object that's not currently being held: "You're not carrying that."
In statusLine.beginStatusLine(), in the StatusModeApi case, there was some leftover old code ("<body bgcolor=statusbg text=statustext>") right after the call to setColorScheme() that effectively undid the color settings made in setColorScheme(). The old code has been deleted.
The library now uses the same default message in response to a command to throw the player character in a direction (as in THROW ME EAST) as it does as for THROW ME AT something.
A new Thing method, adjustThrowDestination(), gives a prospective landing site for a thrown object a chance to redirect the landing to another object. getHitFallDestination() now treats the result it calculates using getDropDestination() as tentative, and calls this new method on the tentative result object to determine the actual destination. The default implementation of the new method in Thing simply returns 'self', which confirms the tentative destination as the actual destination. BulkLimiter overrides the new method to redirect the landing site to the BulkLimiter's location's drop destination if the thrown object would overflow the BulkLimiter's capacity.

This change corrects a problem that occurred when a thrown object landed in a BulkLimiter that was already near capacity. In the past, the BulkLimiter applied its capacity control by effectively blocking the THROW before it happened. With the change, the THROW will be allowed to proceed, and the thrown object will land in the nearest suitable container of the original target.

Note that any game code that overrides getHitFallDestination() should be sure to call adjustThrowDestination() on its tentative return value. Games usually won't have any reason to override getHitFallDestination(), so this change shouldn't affect most existing code.

In ActorState, the beforeTravel() method no longer ends the current conversation if the actor is merely moving between nested locations within the same top-level location. These aren't usually true departures that should trigger conversation termination, so the method no longer treats them as such.
A new travel connector class, DeadEndConnector, can be used for situations where travel through the connector is impossible, but for reasons that can only be learned by attempting to go through the connector. For example, this can be used for a passage that turns out to be blocked by a cave-in that isn't visible from the passage entrance, or in a situation where you describe the actor as wandering around in a direction for a while but giving up and returning to the point of origin.

DeadEndConnector supplements FakeConnector, which is most useful for exits that look like connections but can't be traversed for reasons that are apparent before the travel is ever physically attempted - in particular, for motivational reasons ("You can't leave town without finding your missing brother"). DeadEndConnector differs from FakeConnector in that DeadEndConnector acts as though the physical travel were actually occurring. It fires all of the normal travel notifications, so any side effects that would occur on ordinary travel will also occur with a DeadEndConnector.

"Push travel" has been changed slightly to handle situations where the object being pushed has side effects that affect the description of the destination location. The most common situation where this arises is when the object being pushed is (or contains) a light source, but it could happen in many other custom-coded situations.

In the past, the object being pushed wasn't moved to its new location until after the new location had been described. The point of this sequencing was to exclude the pushed object from the new location's description, simply by keeping it out of the new location until after the description was displayed. This exclusion is desirable because it would otherwise look as though the pushed object were already in the new location on the player character's arrival, which would be confusing. However, it prevented any side effects of the pushed object's presence from being taken into account in the new location's description. If the pushed object was a light source, for example, and the new location was otherwise unlit, the new location was described as dark.

Now, the library tentatively moves the pushable object to the destination of the travel, and marks the object (via the new Actor.excludeFromLookAround mechanism) for exclusion from the location description. It then moves the actor and shows the description of the new location. Finally, it moves the pushable back to the origin location, and then moves a second time, this time for real, to the destination location.

(The reason for moving the pushable twice - first tentatively, before the travel, then again "for real," after the travel - is that the method that makes the final move is overridable and so might actually do something other than move the pushable to the travel destination. Even if it leaves the object unmoved, though, or moves it to a different final destination, the tentative first move is still a valid intermediate step. At the point we're generating the description of the new room, the player character is in the process of pushing the pushable into the new room - the PC is notionally walking along with the pushable at that stage. If the overridable final-move method wants to do something different, it can do so; it will simply have to describe the change, which it had to do in the past anyway. At that point, the PC will already be in the new location, and so will in fact have pushed the pushable this far; anything that happens in the overridable method happens after the intermediate stage where we generated the description, so any side effects of the pushable's tentative presence were valid at that point, no matter what happens afterwards.)

In the English library, the Push-Travel commands (PUSH X NORTH, PUSH X UP RAMP, etc) now only accept single direct objects. In the past, the grammar phrasings allowed direct object lists, but actually pushing multiple objects into a new room is a practical impossibility, so there's no point in accepting it in the grammar.
The mechanism for describing "local" NPC travel has been enhanced. Local NPC travel is travel where an NPC moves from one top-level location to another, and both top-level locations are in view of the PC. In these cases, special handling is needed because the NPC isn't truly arriving or departing in the usual sense; the NPC is instead moving closer to, further away from, or laterally with respect to, the PC.

In the past, the local NPC travel mechanism did everything via the "local arrival" message: the NPC generated only this one special message at the destination end of the travel. The new mechanism adds two messages analogous to the local arrival message: a "local departure" message and a "remote travel" message. Here's how they're used:

  • If the PC is moving closer to the PC, the local arrival message is displayed at the destination end of the travel. "Closer" means that the NPC is moving from a top-level location that doesn't contain the PC to a top-level location that does contain the PC.
  • If the PC is moving further away from the PC, the local departure message is displayed at the origin end of the travel. "Further away" means that the NPC is moving from the top-level location that contains the PC to a different top-level location that doesn't.
  • If the PC is moving laterally, the remote travel message is displayed at the destination end of the travel. "Laterally" means that the NPC is moving between two top-level locations, neither of which contain the PC. Note that there's no good basis for preferring to show the message on the origin or destination side of the travel in this case, since the generic situation is so symmetrical, but at the same time it's necessary to choose one or the other because we don't want two messages for this kind of travel. So, we have to make an arbitrary choice, and the library chooses to show the message on the destination side.

These new rules obviously require some new methods. Here's the new arrangement of methods:

  • The local arrival message is embodied in TravelConnector.describeLocalArrival(), which by default calls Traveler.sayArrivingLocally(), which by default calls libMessages.sayArrivingLocally(). Note that these aren't new - this part of the mechanism carries forward unchanged from past versions. However, their usage has changed, in that the library in the past called the local arrival messages in all cases of local travel, but now calls it only in certain cases, as described above, and calls the local-departure or remote-travel methods in other cases that formerly all folded into the local-arrival case.
  • The local departure message is embodied in TravelConnector.describeLocalDeparture(), which by default calls Traveler.sayDepartingLocally(), which by default calls libMessages.sayDepartingLocally(). These methods are new.
  • The remote travel message is embodied in TravelConnector.describeRemoteTravel(), which by default calls Traveler.sayTravelingRemotely(), which by default calls libMessages.sayTravelingRemotely(). These methods are new.
  • Existing code should continue to work correctly with the new framework, although with one caveat: if you customized a describeLocalArrival() or sayArrivingLocally() method, you might want to add corresponding customizations for the new local-departure and/or remote-travel methods. If you don't, your local-arrival customization will still work in the cases where it applies under the new rules, but it won't be invoked in all the cases it was in the past.
  • AccompanyingInTravelState now describes local travel using its standard departure message. This ensures that the correct messages are displayed when accompanying an actor in local travel or travel between two connected top-level locations (such as locations linked by distance).
    TravelAction.actionOfKind(cls) now properly handles the case where 'cls' is the base TravelAction class. (In the past, asking about TravelAction itself caused a run-time error.)
    The various SettingsItem objects defined in the library are now all named objects. In the past, some of these (such as gameMain.verboseMode) were defined as embedded objects; this made it more difficult to modify their behavior, since it wasn't possible to use 'modify' to alter the objects directly. The behavior should be exactly the same as in the past; the only difference is that the objects are now easier to customize.
    The putDestMessage that was defined in defaultFloor and defaultGround has been moved to the base Floor class instead - this message should be common to most floor/ground type objects, not just for the default ones in the library.
    The method standing.tryMakingPosture(loc) now generates the command STAND ON loc, rather than simply STAND as it did in the past. This makes the behavior consistent with the sitting and lying postures.
    Room can now be used as the direct object of STAND ON, SIT ON, and LIE ON. These are handled simply by remapping the commands to the room's "floor" object (specifically, the object returned from the room's roomFloor method). This is in part to accommodate the change above to standing.tryMakingPosture(), and in part to provide better automatic handling for player commands like SIT IN YARD.
    RoomPart now applies some additional disambiguation filtering in cases where two or more top-level locations are linked by a sense connector. When a resolve list contains multiple RoomPart objects, and some of those RoomParts are local and some remote, RoomPart will reduce the list to include only the local RoomParts. (A "local" object is one within the same top-level location as the actor; a "remote" object is one that's in a separate top-level location that's linked by a sense connector.)
    If a RoomPart has an explicit 'location' setting, that location will no longer add the RoomPart redundantly to the location's 'contents' list. In the past, the RoomPart would show up twice in the contents list, because it was added once by virtue of being in the RoomParts list, and again by virtue of its 'location' setting.

    In addition, when a RoomPart has an explicit 'location' setting, it will now automatically add itself to that location's 'roomParts' list. This means that you don't have to manually set both properties, which saves a little work and also makes your game easier to maintain, since you won't have to remember to make coordinated changes to both settings in your source code if you change the room part later.

    RoomPart and TravelConnectorLink now have a default sightSize of 'large'. This means that it's possible to examine these objects at a distance. Room parts are things like walls and ceilings that tend to be large and to contain large-scale details that would be realistically discernible at a distance - the details are typically things like doors and windows. Similarly, TravelConnectorLink (which you'd mainly use via its subclasses Enterable and Exitable) is for things like building exteriors, which likewise tend to have large-scale details.

    In cases where you create a RoomPart or an Enterable or Exitable that has fine-grained details that would be too small to see at a distance, and the object is visible from a separate top-level location linked by distance, you might want to override this to set the sightSize back to medium. When there's no distance-linked top-level location, this shouldn't be an issue, since there'd be no way to examine the object from a distance to begin with.

    RoomPartItem now overrides useSpecialDescInRoom() and useSpecialDescInContents() to return nil, and no longer overrides useSpecialDesc and useInitSpecialDesc.

    In the past, RoomPartItem overrode useSpecialDesc and useInitSpecialDesc (setting them to nil) in order to prevent room part items from being included in LOOK descriptions, but this had the bad side effect of preventing showSpecialDesc() from showing the initSpecialDesc. This change uses the more precise methods to select exactly where the special desc should be shown, without affecting the selection of which special desc to show.

    Thing.isListedInContents now calls useSpecialDescInContents(location) to make its determination. In the past, it called useSpecialDesc directly; this was incorrect because it didn't properly take into account the differentiation among contexts that the various useSpecialDescInXxx methods provide.
    The exit-list generator (exitLister.showExitsWithLister) now considers two destination locations to be equivalent based on the destination room object rather than the destination room name. This means that two distinct exit locations will now be listed separately even if they have the same name. For example, in the past, if the east and west exits led to separate rooms that both happened to be named "the hallway," the listing formerly read "east (or west), to the hallway", but will now read "east, to the hallway; west, to the hallway". The old approach of merging list entries based on name alone produced odd results in some cases, and didn't have any obvious benefits; the new approach should produce more predictable results.
    TIAction has a new method, retryWithAmbiguousIobj(), that lets an action handler specifically ask for disambiguation from a list of possibilities. This is the TIAction equivalent of the TAction method retryWithAmbiguousDobj(), and works the same way; the only difference is that the disambiguation list applies to the indirect object rather than to the direct object.
    The TAction and TIAction methods retryWithAmbiguousDobj() and retryWithAmbiguousIobj() can now be called before the "iteration" phase of the command execution, specifically during Action.beforeActionMain(), and they'll work properly: they'll ask their disambiguation question, then apply it to the entire iteration for the other object list. For example, if you call retryWithAmbiguousIobj() during beforeActionMain() to prompt for a new indirect object for a PUT IN command, the player's response will automatically be applied to the whole direct object list if the player specified multiple direct objects. In the past, it wasn't possible to call these "retry" methods during beforeActionMain(), so if they were used during commands with multiple objects in the other object slot, the result was that the "retry" - and its question to the user - was repeated for each object in the list.

    (This new flexibility involves two supporting changes. First, the various initForMissingXxx() methods in various Action subclasses now detect that the iteration over the object list hasn't begun yet, and they simply retain the entire object list (rather than the current iteration item, as they did in the past) for the retry. Second, to support the first change, the class PreResolvedAmbigProd now accepts an entire object list instead of just a single iteration element. These are internal methods that game code is unlikely to call directly, so the only visible effect of these changes should be the new flexibility in how the "retry" methods can be used.)

    In the past, initializeThing() was sometimes called multiple times in the course of dynamically creating an object (with 'new'). This happened when creating objects based on classes that inherited from Thing more than once (classes like this include Flashlight, Passage, Room, Chair, Bed, Platform, NominalPlatform, Booth, and UntakeableActor). This happened because the default constructor for any object based on multiple base classes simply inherits each of the base class constructors, one after the other; when more than one base class itself inherits from Thing, this results in multiple inherited invocations of the Thing constructor, which in the past resulted in multiple invocations of initializeThing() for the new object.

    Now, initializeThing() is only called once. The Thing constructor now tests a flag before calling initializeThing; it only calls the method if the flag isn't set, and it sets the flag after calling the method. This ensures that subsequent inherited constructor calls simply skip the call to initializeThing(). The constructor also skips the call to its own inherited base class constructor when the flag is set; this ensures that the vocabulary initializations in VocabObject are only invoked once per object.

    Room has a few command-handling enhancements, for rooms that have associated vocabulary. LOOK IN room is now treated the same as EXAMINE room; LOOK UNDER and LOOK BEHIND are refused ("You can't look under that"); SMELL and LISTEN TO room are now equivalent to simply SMELL and LISTEN; BOARD and ENTER are treated as redundant ("You're already in that"); and GET OUT OF is a little smarter about remapping, so that it only remaps to GO OUT if there actually is an OUT direction defined for the room, and fails with an error if not ("You'll have to say which way to go").
    NestedRoom.makeStandingUp() now leaves the actor's posture unchanged if the travel to the new location fails. It does this by noting the original posture before the travel attempt, and checking after the travel attempt to see if the actor is still in the starting nested room; if so, the routine restores the saved posture. This ensures that a failed travel attempt won't cause a posture change in the original nested location.
    NominalPlatform now overrides hideFromDefault() to always return true. Nominal platforms are meant to be used as internal objects and not to appear as simulation objects, so it's generally not desirable for them to be used as defaults.
    AccompanyingState had a problem that caused infinite recursion (leading to a stack overflow) when an actor in an AccompanyingState was explicitly moved via scriptedTravelTo(). The problem was simply that the actor attempted to accompany itself on its own travel, and that accompanying travel triggered a further accompanying travel, and so on. This has been corrected: actors in accompanying travel states now explicitly ignore their own travel for the purposes of accompanying travel.
    The library uses a separate default message in cases where an Actor is holding an object used in ENTER, BOARD, SIT ON, etc. In the past, the message was the rather awkward "You can't do that while the chair in in Bob." The new message is phrased "...while Bob is holding the chair" instead. The default message for non-Actors is unchanged.
    FOLLOW caused a run-time error if the follower was holding the NPC to be followed. This was because effectiveFollowLocation wasn't defined for ordinary objects. This has been corrected by defining Thing.effectiveFollowLocation to return the location's effectiveFollowLocation, or just 'self' if the object has no location.
    There are two new subclasses of ImpByeTopic, to allow you to differentiate between the two implicit ways of ending a conversation. BoredByeTopic handles cases where the NPC ends the conversation because of "boredom" (that is, inactivity in the conversation that exceeds the NPC's attentionSpan setting), while LeaveByeTopic handles cases where the PC simply walks away from the NPC in mid-conversation.

    BoredByeTopic and LeaveByeTopic extend the hierarchy that already existed with ByeTopic and ImpByeTopic. If there's an active ImpByeTopic and no active BoredByeTopic or LeaveByeTopic objects at the time of an implied "goodbye", the ImpByeTopic will be used for both cases (this also happens to be exactly the way the library worked in the past, before the two new subclasses were added, so this change won't disturb existing code). If there's an active BoredByeTopic as well as an active ImpByeTopic, the BoredByeTopic will be selected over the ImpByeTopic to handle "boredom" goodbyes; likewise, if there's an active LeaveByeTopic, it will be selected over an active ImpByeTopic to handle goodbyes triggered by the PC's departure.

    The "boredom" mechanism in InConversationState has been changed slightly. In the past, it was implemented directly in the takeTurn() method of InConversationState. Now, it's handled with an AgendaItem instead - specifically, a new subclass called BoredomAgendaItem, which the Actor and InConversationState classes manage automatically. This change should have no effect on existing code, since the new code implements the same behavior as the old version. The benefit of this change is that it makes it easier for a game to customize the boredom behavior, since it's now part of the more general "agenda" mechanism instead of being buried in ad hoc code inside the conversation state class.
    The suggested topic lister had a problem that showed up when several topic suggestions were present with the same listing name, and some of the topics were inactive while others were active. The lister automatically removes redundant entries from suggestion lists by removing multiple items with the same name, but in the past, the lister sometimes incorrectly removed the active elements instead of the inactive ones, effectively eliminating the suggestion entirely from the list. This has been corrected: the lister now only removes a redundant suggestion if there's another active suggestion with the same listing name.
    InitiateTopic no longer sets any pronoun antecedents when triggered. (It did set its match object as a pronoun antecedent in the past. This was undesirable because an InitiateTopic is triggered by internal calculations in the game, not by anything the player has done.)
    ConvAgendaItem now checks to make sure the player character is present before declaring itself ready. This ensures that an NPC won't attempt a conversational agenda item unless the PC is actually present to hear what the NPC has to say. (This change is in ConvAgendaItem.isReady.)
    Actor.executeAgenda() formerly marked an agenda item as done if invoking the item threw any sort of exception. Now, this is only done on a run-time error (a RuntimeError exception); in cases of other exceptions, the item's doneness isn't changed. The original purpose of the doneness change was to reduce debugging hassles in cases where an agenda item encountered an error; in such cases, if the item wasn't marked as done, the scheduler would end up invoking the item in an infinite loop, because it would look perpetually ready to run. However, doing this on all exceptions interfered with certain non-error cases where exceptions were used to jump out of the action handling. Limiting the caught exceptions to runtime errors should retain most of the intended benefits while avoiding the problems with a more general error catcher here.
    Actor.initiateConversation() now handles a nil 'state' argument slightly differently. In the past, if 'state' was nil, the actor's state was simply left unchanged. Now, the method will switch the actor to the state returned from the current state's getImpliedConvState() method. In most cases, the net effect is exactly as before - i.e., the actor's state is left unchanged - because the default getImpliedConvState() simply returns 'self'. However, ConversationReadyState overrides getImpliedConvState() to return the associated InConversationState. This change makes it easier to initiate a conversation when using an actor with conversational states, since it will generally pick the correct conversational state automatically.

    Note that the ActorState method getImpliedConvState() is new with this change.

    Due to a bug, <.convnode> tags displayed inside npcGreetingMsg methods weren't handled properly if the greeting was displayed due to NPC initiation of the conversation (via npcInitiateConversation()). This has been corrected.
    ConvNode.canEndConversation() can now return a special value, blockEndConv, that indicates that the actor said something to force the conversation to keep going. You should always use this if you display a message in the routine and you want to prevent the conversation from ending. Returning blockEndConv is almost the same as returning nil from the method, but has the additional side effect that the caller will call noteConvAction() on the other actor, to prevent this actor from generating any further scripted remarks on the same turn.
    When the library displays a parser error message for a command directed to an NPC, it now shows the error message in a neutral sense context. This ensures that the message is displayed even if the NPC is in a remote location (this could be the case if we're talking over the phone, for example). In the past, the parser message was generated in the NPC's sense context, so the NPC was in a remote location that wasn't in scope to the PC, the parser message was suppressed, resulting in a "Nothing happens" message or the like.
    When a command of the form "actor, xxx" is entered, and the portion after the "actor" phrase is unparseable, the parser now attempts to parse at least the "actor" part to determine if the command is being directed to an NPC. In the past, the parser didn't do this; the actor phrase is part of the basic sentence grammar, so if the parser failed to match the rest of the basic sentence grammar, it didn't bother trying to figure out if a target actor was included. To find the target actor, the parser now makes a second attempt at parsing a command to see if it matches a very basic sentence syntax that only includes the target actor specification; if it can match the syntax, and resolve the noun phrase to an in-scope actor, the parser takes the result to be the target actor.

    The main result of this change is that a few of the low-level parser message methods - askUnknownWord, specialTopicInactive, commandNotUnderstood - are now invoked on the target actor when one is present in the command. In the past, because the parser didn't even figure out that a target actor was present in these cases, these methods were always invoked on the player character actor. With this change, it's now possible to customize a NPC's responses for unknown words and command phrasings individually by NPC.

    The library now gives a replacement action zero time if it's replacing an action that itself has zero time. This corrects some timing anomalies (particularly with respect to NPCs) that showed up in certain unusual cases where replaceAction() was used in the course of an action that itself was being run as a nested action.
    A new parser option lets you cancel remaining commands on the command line when an action fails.

    To implement this, Action.afterActionMain() checks to see if the action failed, as indicated by a 'reportFailure()' message in the course of the action handling. If the action failed, and gameMain.cancelCmdLineOnFailure is set to true, afterActionMain() throws a CancelCommandLineException. This exception is in turn caught in executeCommand(), which cancels any remaining commands on the command line and simply proceeds to the next turn.

    In addition, the new playerMessages method explainCancelCommandLine() lets you display an explanation when a command line is canceled due to the failure of a command. executeCommand() invokes this new method when it handles a CancelCommandLineException if there are in fact any remaining tokens on the command line. (This check for remaining tokens skips the explanation when there's nothing left on the command line to cancel, as the cancellation obviously has no effect in such cases.) This new message method doesn't display anything by default; it's just a hook that you can use if you want to provide an explanation. Note that you'll probably want to show this explanation only once per game, rather than every time a command is canceled (you can use a flag property to do this - if the flag is nil, show the message and set the flag to true; otherwise skip the message).

    The default setting for gameMain.cancelCmdLineOnFailure is nil. This provides the traditional handling, which simply continues executing any remaining commands on the command line even when an action fails.

    The reason this new feature is an option rather than simply being the default new policy is that neither possibility is ideal in every case. On the one hand, continuing to execute commands after an action has failed can lead to confusing results: the failure of the earlier command can leave things in a state other than what the player was anticipating, which could change the behavior of subsequent commands. So the argument in favor of canceling remaining commands is that it avoids this possible source of player confusion by halting processing once it appears that the player's thinking is out of sync with the game's internal state. On the other hand, exactly what constitutes failure might not always be apparent to the player, so halting halfway through a command line might sometimes appear arbitrary to the player, or even buggy. The argument in favor of continuing to plow through the rest of the command line like nothing happened, then, is that it's simple and consistent. A prime virtue of any UI is predictability, so that the user has an easier time forming a working mental model of the software, and the most predictable behavior is to unconditionally execute everything on the command line. Further, given that multi-level UNDO is available by default in tads3 games, any unintended effects from extra commands can always be manually undone as soon as the player realizes what happened. The arguments on both sides are valid, so the library leaves it up to the author to set the game's policy. (It's even been suggested that this ought to be left up to the player, via a command that selects the mode, but I think this would be overkill, so for now this is just an author-controlled option. Games are free to add their own command to let the player control it, of course; it's just a matter of flipping the cancelCmdLineOnFailure setting at run-time.)

    The Action class has a new method, checkAction(), that is called just before the existing execAction() method. TAction and TIAction define the new method, and perform the calls to the direct and indirect object 'check' methods in this new method rather than in execAction(), as was done in the past.

    This change should have no effect on existing code, since it's a simple internal rearrangement. The benefit is that it's now possible for game code to call the 'check' phase of a verb explicitly, without also invoking the 'execute' phase.

    OopsIAction is now defined as an IAction. (In the past, it was based directly on Action, which was problematic if an OopsIAction grammar match was used in certain contexts, such as CommandRanking.sortByRanking.)
    ObjectPreCondition now uses the same ordering as the underlying condition.
    During calls to the "verify" methods for remapped actions (i.e., actions mapped to different actions using remapTo), the library now sets gAction to the remapped action. In the past, during the initial remap testing stage, the library left gAction with the original action that triggered the remapping. This gives game code more consistent information during the verify phase.
    VocabObject now defines the methods isOwnedBy(obj) and getNominalOwner(). This allows player input to refer to non-physical objects, such as topics, using possessive syntax.
    The INSTRUCTIONS command is now ignored for the purposes of UNDO and AGAIN.
    The parser no longer chooses Unthings as default objects in cases where noun phrases are missing in player input. (This is handled via Unthing.hideFromDefault().)
    The new Thing method setGlobalParamName() lets you add a global message parameter name dynamically. In the past, there was no straightforward way to do this; you had to manually update the message builder's internal table of names in order to add a new name. This method takes care of all of the necessary internal table updates.

    You only need to use this method if you want to add or change a parameter name dynamically at run-time. The library still automatically initializes the message builder's tables for globalParamName settings defined at compile-time.

    The parser now marks a plural-phrase match for a direct or indirect object as "unclearly disambiguated" if the phrase also matches a singular noun for an object that's also in scope. This flag makes the parser generate an extra message to notify the player that the parser chose an object from a potentially ambiguous set:

      >show bob the rags
      (the piece of cloth)
      Bob does not appear interested.
    

    If the parser's decision was wrong, the message will alert the player, so that the player will know to try rephrasing the command rather than being left with the impression that the intended command didn't work.

    The event manager (the part of the library that manages fuses and daemons) now automatically disables a daemon or fuse that throws an exception out of its main execution method. The reason this is important is that an infinite loop would otherwise occur in many cases: if the object remained active, the event manager would re-invoke it after the exception, and in most cases the re-invocation would simply encounter the same exception, at which point the event manager would invoke it once again, and so on ad infinitum.

    Note that this change only comes into play when a fuse/daemon throws an exception out of its main method. It won't affect a daemon/fuse that merely encounters an exception in the course of its processing, as long as the fuse/daemon catches and handles the exception.

    A new class, OneTimePromptDaemon, makes it easy to set up a prompt daemon that only fires once. It works like an ordinary prompt daemon, but removes itself from the active event list as soon as it fires. This can be handy for cases where you want to defer some non-recurring processing until just before the next command prompt.
    Thanks to Greg Boettcher, the English library's default messages now use typographical ("curly") quote marks and apostrophes. In cases of open/close quote marks, these are generated with <Q>...</Q> markups; in cases of apostrophes, &rsquo; entities are used.

    This change is purely cosmetic, so it should require no changes to existing game code. It shouldn't even affect test scripts (where you run an input script and "diff" the output against a reference log) - by default, the log file mechanism by default generates transcript output in plain ASCII, and the curly quotes are mapped to ordinary straight quotes when rendered in plain ASCII.

    The library now attempts to be as precise as possible when announcing objects chosen in vague disambiguations, as defaults for missing noun phrases, and for multiple objects to a verb. In the past, the library simply used the base name of the object being announced, but this didn't take into account the various things that the parser can use to distinguish objects on input, such as lit/unlit state, location, and owner. The library now generates these object announcements using the same Distinguisher mechanism it uses to resolve ambiguous noun phrases in input, ensuring that the generated names are as precise as possible in distinguishing announced objects from others in scope.

    To accomplish this, the announcements call upon a new Thing method, getInScopeDistinguisher(). This method looks at the Distinguisher objects associated with the object to be announced, and tries to find one that can distinguish the object to be announced from every other object in scope. If it finds one, it returns it. If it fails to find one, it returns the distinguisher that does the best partial job - that is, the one that distinguishes the object from the largest number of other in-scope objects. (The method never returns nil; in the worst case, it simply returns basicDistinguisher, which distinguishes objects based on their disambigName properties.) The announcements then use the returned Distinguisher to generate the announced name.

    Note that in the simplest case, this change results in the disambigName being used in object announcements; in the past, the base name was used. In most cases, this won't cause any change in game behavior, since the disambigName for most objects is just the base name anyway.

    The library now suppresses vague disambiguation announcements for objects specified with indefinite articles when the announcement would be redundant. That is, if there's no Distinguisher that can tell apart any of the possible matches for an object specified with an indefinite article, the library doesn't bother making the announcement, because it would add no useful information for the player. For example, in the past, you might have seen an exchange like this:

       >take a silver coin
       (the silver coin)
    

    The parser was making the announcement because it had chosen arbitrarily from one of several silver coins. But since all of the possible matches were indistinguishable anyway, the announcement doesn't help the player see which one in particular was chosen. The library now suppresses the message in this case. Note, however, that if it's possible to distinguish any of the possible matches from one another, you'll still see a message. For example, if there's a silver coin and a gold coin present, and the player types TAKE A COIN, the library will announce which one (gold or silver). Similarly, if there's a silver coin on the table and another on the floor, and the player types TAKE A SILVER COIN, the library will mention the location of the one chosen: "(the silver coin on the floor)", for example.

    The English library can now correctly generate a plural name for an object whose name includes an "of" phrase; for example, the library now correctly generates the plural for "piece of paper" as "pieces of paper". In the past, the library pluralized the last word of the entire phrase, so the result in this example would have been "piece of papers". The new algorithm when there's an "of" is simply to pluralize the last word before the first "of", using the same rules as for a phrase without "of".
    In the past, the English parser treated a command of the form GIVE adjective noun as though it meant GIVE noun TO adjective. This was almost never what the player actually meant; the player almost always really meant GIVE object TO the current interlocutor, where object is named adjective noun. The parser now applies that more likely interpretation when confronted with this syntax. The same applies to SHOW.

    (As part of this change, a new parser class was added: ImpliedActorNounPhraseProd. This is a subclass of EmptyNounPhraseProd that works almost the same way, but doesn't apply a grammar ranking penalty for the missing noun phrase if a default interlocutor can be identified. The single-noun-phrase grammars for GIVE and SHOW use this new class, ensuring that they're chosen over the non-prepositional two-noun-phrase phrasings whenever a default interlocutor is present.)

    In the past, the English parser treated the word "of" as a non-weak token when matching noun phrases of the form "x of y". This meant that if a noun phrase consisted only of weak tokens and "of", the parser matched it. This has changed; "of" is now ignored for the purposes of the weak-token test, so a phrase consisting only of weak tokens and "of" is now considered weak overall.
    A bug in the English library caused run-time errors in some cases involving TopicAction verbs. The problem was that some of the TopicAction message generators assumed that they were the current active verb, which isn't always the case. This has been fixed.
    The English library defines the new verb phrasings ASK actor topic and TELL actor topic; these are phrasings for the new actions AskVague and TellVague, respectively. These are defined entirely to provide more helpful error messages in cases where the player enters an ASK or TELL verb without an ABOUT phrase. The default handling for the new actions simply displays an explanation of the proper ASK ABOUT or TELL ABOUT phrasing. Some players have been seen to misinterpret custom phrasings that show up in SpecialTopic prompts, such as "ask bob why", as general-purpose command phrasing, and then attempt to use similar phrasing elsewhere in the game. This results in "invalid command" errors from the parser, of course, but the standard error messages were often unhelpful with this particular kind of phrasing error. The new handlers are meant to improve on the standard error messages for this common case.
    The English library now treats the command phrasing CLIMB IN/INTO/IN TO as a Board action, and CLIMB ON/ONTO/ON TO as a StandOn action. In the past, both forms of CLIMB were defined for both of these actions, which made the choice of which action matched the syntax arbitrary and unpredictable. This change ensures that each of these phrasings is unambiguously assigned to only one action.
    The English library now tries a little harder to figure out whether a command of the form ENTER text means ENTER text ON some implied keypad-type object, or GO IN object. In the past, the library always used the GO IN interpretation. Now, the library assumes the ENTER ON keypad interpretation if there's a suitable default object present; if there isn't, the library reverts to the GO IN interpretation.
    The English library formerly allowed any object to match the pronoun "them"; it now limits "them" matches to (a) objects used in lists in player input, as in TAKE IRON KEY AND BRASS KEY, and (b) objects marked as having plural usage via isPlural.
    The parser now lowers the grammar match ranking for any phrasing involving a plural in a noun slot requiring a single object. This makes the parser a bit smarter about picking the right vocabulary interpretation in cases of nouns that have both singular and plural usage, such as FISH and SHEEP.
    In the English library, the isHim and isHer properties are now taken into account for arbitrary Thing objects in parameter substitutions in messages. In the past, gender was only taken into account for Actor objects; non-Actor objects were always "it" or "them" in messages.
    In the English library, the Actor methods theName, theNameObj, theNamePossAdj, theNamePossNoun, aName, and aNameObj now properly handle the first-person plural case ("we", "us", "our", "ours", etc). In the past, a plural Actor with first-person referralPerson incorrectly used the singular first-person pronouns.
    The PourOnto verb in the English library now correctly uses onSingleNoun as its indirect object response phrase, matching the regular grammar for the verb. (In the past, it incorrectly used withSingleNoun as the response phrase.)
    The English library now tries harder to avoid duplicating vocabulary in the lists it constructs from vocabWords properties during initialization. First, VocabObject.inheritVocab() now avoids redundantly scanning vocabulary inherited from superclasses. Since this routine's whole point is to explicitly scan the class hierarchy for each object and add the vocabulary inherited from each superclass, it needs to bypass the normal automatic inheritance mechanism, which it now does. Second, initializeVocabWith() now explicitly removes duplicates from each word list it updates, to avoid duplication in cases where an object explicitly defines the same vocabulary word at multiple inheritance levels.
    The new function isListSubset() determines if one list is a subset of another list: that is, if every element of the first list also appears somewhere in the second list. (This function was formerly defined as a method in the Lister class, but it's generically useful and wasn't tied in any way to Lister, so it's been broken out as a function to make it more readily accessible to other code.)
    The library functions main() and mainRestore() have been enhanced to catch QuittingException signals. These functions, of course, are the main entrypoints for the entire program - the former is for normal start-up, and the latter is for the special kind of start-up that restores a saved state directly from the operating system shell, such as when the player launches the game by double-clicking a saved-state file on the Windows desktop. Catching QuittingException in these functions allows game code to throw QuittingException just about anywhere, without worrying about the context. In the past, this exception was only usable after the main command loop started; in particular, it wasn't usable from startup or initialization code (such as the code that displays the game's introductory messages), because there was no handler to catch the exception in effect while that code was running.
    3.0.9

    Released 8/29/2005

    Minor compatibility-breaking change: In the English library, the ThingState template now uses "+" rather than "@" to introduce the listingOrder property value. The choice of punctuation here is pretty arbitrary; the reason for the change is that a consensus emerged among users that "+" would be easier to remember in this template because the other library templates generally use "+" to mark integer slots.

    If you have existing game code that defines any ThingState objects using the old template, you'll need to change the definitions to use "+" instead of "@". Alternatively, you could put your own definition of the old ThingState template into a common header file that your game use, in which case you wouldn't have to change any of your existing object definitions; but you're probably better off just changing your code to use the new convention, since defining your own template could cause some slight confusion for other users if you should share your code in the future.

    Slight risk of incompatibility: Thing.getDropDestination() must now accept a nil value for the 'obj' argument. A nil value of 'obj' asks for the drop destination for a "typical" object, rather than for any specific object. In the past, it wasn't specified that 'obj' could be nil, and the library never directly called the method with a nil 'obj' value, so it's possible that some existing game code might assume that 'obj' is non-nil. You should do a quick scan of your game code for getDropDestination() definitions to ensure that they'll work properly when 'obj' is nil. In particular, make sure that you don't evaluate any properties of 'obj' without first checking that 'obj' is non-nil:

       getDropDestination(obj, path)
       {
          if (obj.isReallySmall) ...;   // Don't do this!!!
          if (obj != nil && obj.isReallySmall) ...; // Do this instead
       }
    
    Slight risk of incompatibility: The default value of Event.eventOrder is now 100 (it was formerly 50). This default is essentially arbitrary (since it's only important for relative ordering), but a value of 100 is more consistent with other similar ordering defaults elsewhere in the library.

    In addition, the eventOrder of the standard sense-change daemon is now 500, to ensure that the sense-change daemon runs after most other events. The library automatically creates the sense-change daemon at game start-up; this is the daemon that displays messages about new, ongoing, or ending sounds and odors. In the past, this daemon had default priority, so its order relative to other fuses and daemons was arbitrary. If another fuse or daemon ran after the sense-change daemon, and made some change that affected the sensory environment, a message about the change wasn't displayed until the following turn, since the chance had already passed to generate the message on the turn during which the change actually occurred. This change ensures that the sense-change daemon runs after most other events, so any changes that occur in daemons and fuses will be reflected in the sense-change update for the same turn.

    If you've explicitly set eventOrder for any fuses or daemons in your game, you might need to adjust the values you set. In particular, if you set an eventOrder value between 50 and 100 for an event object to ensure that the event is processed after events that inherit the default eventOrder, you'll need to raise that value above 100. You should also consider if it's desirable for those events to run after the sense-change daemon, as they would have in the past (since the sense-change daemon used to have default priority), and if so, raise their eventOrder values to above 500. Finally, if you have an existing eventOrder that's over 500, you might want to consider whether you still want the event to run after the sense-change daemon; if not, you should lower the value to under 500.

    Possibly incompatible change: The parameter lists of some of the internal library routines that handle point-of-view have changed. These aren't likely to affect any existing game code, since these are mostly used only inside the library itself, but you should check your code for any mentions of these names to be sure. You'll need to adjust any code in your game that either calls or overrides any of these methods or functions:
    • Thing.fromPOV() has a new parameter giving the actor who's doing the looking.
    • Function pushPOV() takes a new parameter giving the actor.
    • Function callFromPOV() takes a new parameter giving the actor.
    • Function setPOV() takes a new parameter giving the actor.
    • Function setRootPOV() takes a new parameter giving the actor.
    Very slight risk of incompatibility: In the following methods, the parameter now gives the point-of-view actor instead of the point-of-view object. The POV actor and object are usually the same anyway, so the effects on existing code should be minimal. If you actually need the POV object, you can get it with the getPOV() function.
    • Thing.showRemoteSpecialDesc
    • Thing.remoteInitSpecialDesc
    • Thing.remoteSpecialDesc
    • Thing.showSpecialDescInContents
    • Thing.showObscuredSpecialDescInContents
    • Thing.showDistantSpecialDescInContents
    • Thing.showRemoteSpecialDescInContents
    • Actor.listActorPosture
    The new function getPOVActor() returns the "point-of-view actor." This is the actor who's doing the looking that is, the actor who's performing the action (LOOK AROUND, EXAMINE, SMELL, etc) that's generating the current description. This is usually, but not always, the same as gActor, and it usually, but not always, returns the same object as getPOV().

    The point-of-view actor can differ from gActor in cases where game code explicitly generates a room description from the point of view of one actor in the course of processing a command given to a different actor. In these cases, gActor will be the actor who's performing the command, and getPOVActor() will return the actor who's generating the description.

    The point-of-view actor can differ from the point-of-view object (the object returned by the getPOV() function) when the actor is observing the location being described through an intermediary object. For example, if we're generating a description of a remote room because an actor is examining a closed-circuit TV monitor showing the remote location, the point-of-view actor will be the actor who's looking at the monitor, while the point-of-view object will be the camera in the remote location. Note that the POV is the camera, not the monitor: the POV is intended to be the object that's physically absorbing the light rays, or other sensory equivalents, from the surroundings being described.

    The library now selects remote descriptions for rooms and other objects a little differently than before. In the past, the remoteness of the point-of-view object was the deciding factor - that is, remote descriptions were used when the POV wasn't in the same room as the object being described. Now, the library bases the decision on the point-of-view actor, not the POV object: if the actor who's doing the looking isn't in the same room, the remote description is used. This means, for example, that the remote description will be generated when observing a room through a closed-circuit TV system: even though the POV (the camera) is in inside the room being described, the actor who's observing the TV monitor is in a different room, so the remote description is used.

    This change is implemented in Thing.showSpecialDescWithInfo() and Thing.showSpecialDescInContentsWithInfo().

    The method Thing.lookAroundWithinContents(), which is the part of the room describer that lists the room's contents, had a bug in past versions that caused a single object to be listed twice in some unusual cases. In particular, if the room itself had brightness 1, and it contained an object that also had brightness 1, the object was listed twice: once in a list starting "In the dark you see," then again in a list starting "The room contains." The same problem occurred in a dark room if an object with brightness 1 was inside a container with brightness 1. This has been fixed.
    A new mechanism in Actor allows objects to be explicitly excluded from the "look around" description of a room. To exclude an object, call the method excludeFromLookAround(obj) on the actor, passing in the object 'obj' to be excluded. The object will be suppressed from room descriptions until further notice - it won't show up in any of the visible-object lists, including special descriptions, room contents lists, or nested contents lists. To remove an object from the exclusion list, call unexcludeFromLookAround(obj).

    The exclusion that this method applies is over and above any other listing-selection mechanisms, such as obj.isListed. It's intended mostly for ephemeral exclusions that are applied other than by the object itself, since the object itself can usually more easily control its own listability via isListed and the like.

    In Thing.adjustLookAroundTable(), the point-of-view actor is no longer removed from the table; instead, just the point-of-view object is removed. In cases where the POV and actor differ and the actor is in sight of the POV, we usually want to include the actor in the generated description.

    In addition, the method now calls adjustLookAroundTable() on the actor and POV objects, giving them a chance to make further adjustments to the table.

    Suppose we have an NPC who's in scope but out of hearing range, such as when the NPC is on the other side of a transparent, soundproof window from the player character. If the player addresses a command to the NPC, the library now uses the same error message that it uses for ASK ABOUT and other conversation commands in the same situation.

    (This corrects a bug that used to occur. In the past, the library attempted to let the NPC respond, rather than generating a parser error. The problem with this approach was that the soundproof barrier stopped the PC from hearing the NPC's response, thus the library could only report that "Nothing obvious happens." Now that this is handled as a parsing error instead of a conversational response, the more helpful "Bob does not appear to hear you" is displayed.)

    Thing.isListedInRoomPart has changed slightly. In the past, an object was listed (i.e., shown in a miscellaneous list of portable contents) as part of an EXAMINE FLOOR (or EAST WALL or the like) if the object was nominally part of the room part (that is, nominally on the floor, or on the given wall, etc) and the object was normally listed the same way in a LOOK AROUND command. Now, the object is listed if it's nominally part of the room part and the new RoomPart method isObjListedInRoomPart() returns true.

    The new method RoomPart.isObjListedInRoomPart returns true by default if the object does not have a special description for the purposes of examining the room part - that is, if the object's useSpecialDescInRoomPart returns nil. We don't want to include the object in the miscellaneous list if it has a special description because the special description will be automatically shown - it would thus be mentioned twice if we included it in the miscellaneous list as well.

    Floor overrides isObjListedInRoomPart to list an object only if it's listed normally - that is, the object's isListed returns true. This is the old behavior that applied to all room parts, but now it only applies to Floor objects. The reason we treat Floor differently is that everything that's directly in a room is by default nominally on the floor in the room, so if we didn't limit the listings like this in the case of EXAMINE FLOOR, we'd include all of the internal fixtures and components of the room, which is generally undesirable.

    The main purpose of this change is that it makes it a little easier to set up Fixture-type objects that are described as being situated on walls, ceilings, and the like. Now, all that's necessary is to give the object a specialNominalRoomPartLocation - even if the object isn't normally listed in a room description, as is the case for a Fixture, we'll now list the object when we examine its room part as long as it doesn't have an explicit special description for the room part.

    The new mix-in class RoomPartItem can be used to create an object that shows its special description when an associated room part is described. This is useful for things like doors, windows, ceiling fans, and other objects attached to the room. The main thing this class does is to make the object show its special description when its associated room part is examined (as in LOOK AT EAST WALL), but not in the main room description (as in LOOK AROUND). This is desirable because this kind of object is usually not remarkable enough to call out with a special description in the overall room, but does warrant a mention when its associated room part is explicitly examined.
    SensoryEmanation has a new property, isEmanating, that lets you turn the object on and off. By default, is Emanating is true; if you set this to nil, it shuts off the emanation. This makes it easier to make sounds and odors start and stop, which is often useful when the emanations are associated with dynamic objects such as actors.
    Settable has a new method, canonicalizeSetting(), that converts a proposed new setting to a "canonical" format. Settable now uses this to process the player's input before passing the value to isValidSetting() or makeSetting(), ensuring that these routines don't need to perform any further format conversion of their own.

    This method makes it much easier to handle superficial formatting variations in the player's input by centralizing the conversions to this single routine. In the past, both isValidSetting() and makeSetting() had to handle formatting variations since they received the raw player input.

    Settable's default implementation of canonicalizeSetting() doesn't do anything - it just returns the original player input unchanged. NumberedDial overrides the method to convert spelled-out numbers to numeral format and to strip off any leading zeroes from an entered numeral format; LabeledDial overrides it to convert the input to the exact case of the matching entry in the list of valid labels.

    The new library routines parseInt() and parseIntTokens() parse a string or token list (respectively) that contains a spelled-out or digit-string numeric value. These routines parse the input and return the corresponding integer value, or nil if the input can't be parsed as a number. For example, parseInt('one thousand and twenty-four') returns 1024, parseInt('72') returns 72, and parseInt('dusty tome') returns nil.
    The English library now incorporates Michel Nizette's Past Tense extension. This enables the author to select the tense - present or past - used in library messages, and even allows you to change the tense dynamically during the game.

    The 'master switch" for the active tense is gameMain.usePastTense. Set this to true to activate past tense in library messages. The default is nil, which selects present tense.

    A number of new message parameters and verbEndingXxx properties help in writing messages that can change tense. The standard library messages defined in en_us/msg_neu.t use these new features to adapt automatically to the current tense.

    See the comments in en_us/en_us.t for full details.

    The library has a new framework for "global defaults," which lets the user save certain preference settings as default values for future sessions. These saved defaults are global: they apply to any new game subsequently loaded, provided that the new game supports the new global defaults system. Games compiled with library versions prior to 3.0.9 didn't support this new system, but games compiled with 3.0.9 or later will support it by default, as long as an author doesn't explicitly disable it for some reason. The settings are saved to a file whose name and location are determined by the interpreter (and which might vary by system, to conform to local conventions).

    Two new commands let the user control the global defaults: SAVE DEFAULTS saves the current settings to the configuration file, and RESTORE DEFAULTS explicitly loads the file and applies its settings. These commands both list the full set of saved settings, so that the user can easily tell exactly which settings are affected.

    By default, the library uses the new global settings framework for the VERBOSE, FOOTNOTES, NOTIFY, and EXITS settings.

    The new mechanism is implemented mostly in the new file settings.t. The main API is exposed through the new settingsManager object, and the command-line user interface is implemented in the new settingsUI object. The new SettingsItem class is used to encapsulate individual configuration variables. If you want to create your own extensions to the set of global default variables, simply create a SettingsItem object for each new variable you want to add. The settings framework will automatically find your new SettingsItem objects, and save and restore them as needed.

    The new macro gSetSeen(obj) marks an object as having been seen by the player character.
    The new macro gTopicText returns a string containing the literal text of the current topic phrase, when the current action contains a topic phrase. The text returned is as the user typed it, but converted to lower case.
    The LiteralAction and LiteralTAction class are now based on a new common mix-in base class, LiteralActionBase. This is parallel to the structure of TopicAction and TopicTAction, with their common mix-in base class TopicActionBase. This new arrangement shouldn't at all change the behavior of LiteralAction or LiteralTAction, and should be entirely transparent to existing code.

    The change has two main benefits. First, it eliminates some code duplication, by replacing a few bits of code that were effectively duplicated in LiteralAction and LiteralTAction with common versions that both classes now inherit from LiteralActionBase. Second, game code can now easily determine if an action involves any sort of literal phrase, by testing gAction.ofKind(LiteralActionBase).

    The new Thing method failCheck() makes it a little more convenient to write check() routines. check() routines frequently use this pattern:

       check()
       {
         if (condition)
         {
           reportFailure('You can\'t do that for some reason.');
           exit;
         }
       }
    

    The failCheck() routine encapsulates the display of the failure report and the termination of the action with 'exit'. Using failCheck(), you can rewrite the code above more concisely, like so:

       check()
       {
         if (condition)
           failCheck('You can\'t do that for some reason.');
       }
    
    Occluder.occludeObj() has a new default implementation. By default, the method now asks the object being tested for occlusion to make the decision, by calling the object's isOccludedBy() method.

    Thing provides a default implementation of the new isOccludedBy() method, which simply returns nil to indicate that the object is not occluded by the given Occluder.

    Existing code won't be affected by this change, since any existing Occluder code already overrides isOccludedBy() to make the per-object occlusion decision. The reason for this change is that it's sometimes easier to decide about occlusion in the individual objects rather than in the Occluder; the new default Occluder.occludeObj() makes it easer to write the code that way by providing a pre-defined framework that already works that way.

    In the past, if a Container was used as the indirect object of PUT IN or POUR INTO, the library applied an objOpen precondition to ensure the container was open. The library now applies the objOpen condition only if the actor performing the action isn't itself inside the container. The reason for the objOpen condition is that putting or pouring things into a container requires access to the container's interior, and a container's interior is only reachable from outside the container when the container is open. However, when an actor is inside a container, the actor can reach the inside of the container whether or not the container is open, so there's no need for the objOpen condition.
    Fixture, Component, and Immovable now define custom handlers for PUT BEHIND that are consistent with their handlers for PUT IN, PUT ON, and PUT UNDER.
    On an actor's attempt to take an item that was inside something the actor was already carrying, Actor.tryMakingRoomToHold() formerly double-counted the item's weight in enforcing the actor's carrying limit - it counted the item first for the weight of carrying the new item itself, then again for its contribution to the weight of its old container (because the old container was in the actor's inventory). The routine now correctly counts the weight only once. (The new algorithm uses the whatIfHeldBy() method, which actually tests the proposed new arrangement to calculate the new weight carried and thus yields reliable results no matter what configuration previously existed.)
    TAKE ALL now tries to take everything that's directly in the actor's current "drop destination", or that's directly in the actor's direct container, or that's in any intermediate container between the two. The former behavior was to take everything in the actor's direct location only. This change improves the behavior when the actor is in something like a chair or bed. In these sorts of nested rooms, items dropped usually land in the enclosing room; the new behavior ensures that a TAKE ALL command attempted while in such a location will include items that were just dropped while in the same location. With the former behavior in this situation, TAKE ALL missed the items in the drop location, which was likely to confuse players.
    NonPortable now defines a default weight and bulk of zero. Since these objects aren't meant to be carried or moved around by actors, their weight and bulk are essentially irrelevant; but the previous non-zero defaults occasionally had undesirable side effects in cases where a non-portable was a component of another object or among another object's contents.
    An attempt to give an object to an NPC when the NPC is already holding the object in question is now ruled out as illogical ("Bob already has that").
    The library now provides commands that let the player control the display of the exits listing in the status line. EXITS ON now turns on both the status line and room description exit listings, and EXITS OFF turns them both off. The new command EXITS STATUS (which can also be entered as EXITS STATUSLINE or EXITS STATUS LINE) turns on the status line exit listing only, and EXITS LOOK turns on the room description exit listing only.

    In addition, these new commands are now described in the standard instructions.

    The "probabilistic firing" feature of the RandomEventList class is now available in a separate mix-in class, RandomFiringScript. This new class can be mixed into the superclass list of any Script subclass to add the random firing feature.

    RandomEventList and ShuffledEventList are now based on the new RandomFiringScript. This doesn't change their behavior; existing code based on these classes should continue to work unchanged.

    The SyncEventList class now delegates calls to its scriptDone() method to its master list object. This ensures that any explicit calls to a SyncEventList's scriptDone() method will be handled via the master list, which is consistent with SyncEventList's handling for most of its other methods.
    The library now has a better response to the FOLLOW command if the target NPC departed the player character's present location while the PC was somewhere else. Suppose the PC sees Bob in the living room, then goes to the dining room; while the PC is in the dining room, Bob departs for a third room (due to some scripted activity). The player then returns to the living room. At this point, it's not possible to FOLLOW BOB, since the PC didn't see where Bob went. This much hasn't changed; what's changed is the library's default response if player types FOLLOW BOB in this situation. In the past, the response was "The last place him was the living room," which is true but not very helpful (and a little non sequiturish). Now, the response is "You're not sure where he went from here."
    NominalPlatform now returns its location's effectiveFollowLocation as its own effectiveFollowLocation. A nominal platform isn't meant to be part of the visible structure of the game world, so, for FOLLOW purposes, a character on a nominal platform is effectively in the enclosing room as far as the other characters in the room are concerned.
    A bug in the parser caused possessive pronouns to be treated differently in a TopicTAction when the direct object phrase was missing than when it was provided. For example, ASK BOB ABOUT HIS KEY was treated differently from the abbreviated equivalent A HIS KEY. This has been corrected.
    The parser now drops its preference for untruncated name matches when resolving an object in "global scope," and instead treats truncated and exact name matches on an equal footing in these cases. This change is analogous to the global scope changes for plurals and adjective-ending phrases introduced in 3.0.6q and 3.0.6p. See the adjective-ending notes from 3.0.6p for details on what global scope is and why its rules on matching are more lenient.

    The practical effect of this change is that topic words will now match properly even when there are longer and shorter versions of the same vocabulary defined for various objects. In the past, if one object was defined with a six-letter noun, and another object had a seven-letter noun with the same first six letters as the other object ("coffee" and "coffees", say), the parser filtered out the seven-letter match in favor of the six-letter match, even though both objects ought to be equally valid in global scope. With the change, both objects are now properly kept as possible resolutions when parsing a topic phrase.

    The parser now selects Collective objects instead of their corresponding individuals when matching "all" and "all except list" noun phrases.

    Whether it's better to select the Collective or the individuals probably varies according to context, since the "right" one is the one the player intends in a particular command, and that will likely vary from one command to the next (and from one player to the next). But the design of the parser requires that we choose one interpretation or the other across the board; context sensitivity isn't an option. Even if context sensitivity were an option, we might still not have enough information to correctly read the player's mind every time. This change - matching the Collective object instead of the individuals - seems more likely to yield intuitive results more of the time than the old behavior.

    The parser now applies the normal filterResolveList() filtering when resolving a target-actor phrase (such as the "bob" in "bob, go north"), the same as it does when resolving other kinds of noun phrases. In the past, the parser omitted this filtering step for target actors, which didn't give objects like Unthings and Collectives a chance to do their special filtering.
    When the player enters a command of the form TAKE ALL FROM iobj, the TakeFrom action now lets the indirect object determine what ALL means. It does this via a new method, getAllForTakeFrom(scopeList). Thing implements this method using almost the same behavior that TakeFrom used in the past: it returns a list of everything in scope that's directly inside 'self' and isn't a component of 'self'. The old behavior simply returned everything in scope that was directly inside the indirect object.

    ComplexContainer overrides getAllForTakeFrom() to return everything that's in scope and that's directly in (a) the sub-container or sub-surface, if the object has either, or (b) the sub-rear-surface or sub-underside, if the object doesn't have a sub-container or sub-surface; and as with Thing's version, this list is then limited to objects that aren't components of their direct containers. Note that if there's both a sub-surface and a sub-container, the method will return everything that's in either of these; and likewise with the sub-rear-surface and sub-underside.

    This change is designed primarily to improve the behavior for ComplexContainers, which wasn't ideal under the old mechanism, as it only returned the complex container's sub-components, which are internal implementation objects that are meant to be invisible to the player. Clearly, in the case a complex container, it's the contents of the inner containers that are of interest in TAKE ALL FROM. This change also makes it easier to customize the TAKE ALL FROM behavior for your own special containers, since it gives the container a chance to determine what ALL means in context.

    The grammar class VagueContainerNounPhraseProd, which matches locational phrases such as "all in box" and "the one in the box," also uses the new method to determine which objects are involved. This provides the same improvement for commands such as LOOK AT EVERYTHING IN THE BOX when a complex container or other special container is involved.

    The NounPhraseWithVocab method getVocabMatchList() now takes an extra parameter, specifying any extra flags that should be OR'd into the ResolveInfo flags for the items in the match list. In the past, the version of the method in the adjective-phrase subclasses took the extra flags parameter, but the base class method didn't, which made it difficult to create classes for production types that could define grammars with either the adjective or noun subproductions.
    In the past, if the player typed STAND while the PC was sitting or lying on an object derived from BasicPlatform, the library simply made the PC stand up, remaining on the platform. In particular, the library didn't bother checking the allowedPostures property to see if the platform actually allowed the 'standing' posture; it simply assumed that standing was allowed on any platform. This didn't easily allow for things like booths with limited headroom. The makeStandingUp method of BasicPlatform now checks allowedPostures property, to make sure that 'standing' is listed. If 'standing' isn't among the allowed postures, the makeStandingUp method now inherits the base class handling, which removes the actor from the nested room and sets the actor's posture to the default for the enclosing location. This will produce better results in most cases, since the natural implication of STAND UP when in a vertically confined space would be to remove oneself from the space.
    It's now possible to control the order in which preconditions are executed, at the level of the individual preconditions. The new PreCondition property preCondOrder gives the order of execution of a given precondition. The default for most types of preconditions is 100; for TouchObjCondition (and thus for touchObj), it's 200, which means that touchObj conditions will execute after other conditions by default.

    It's occasionally useful to be able to fine-tune precondition order because preconditions can sometimes interfere with one another. This is particularly a problem with implied actions that require the actor to be in a particular posture or location, because many of the standard preconditions can move the actor around as a side effect. For example, a doorOpen condition could execute an implied STAND as part of its implied OPEN DOOR; if another precondition requires the actor being to be sitting, its effect would be undone if the doorOpen were executed second. We can safely STAND and OPEN DOOR first, then SIT DOWN second, because we'll still satisfy the doorOpen condition even though we're no longer standing when we're done.

    In addition to the new preCondOrder ordering, the default ordering has changed for two-object verbs (those with direct and indirect objects). By default, the direct object preconditions are now executed first, followed by the indirect preconditions; in the past this order was reversed. This is only the default order - the preCondOrder takes precedence. That is, the action builds a list consisting of the direct object preconditions followed by the indirect object preconditions, then sorts that list in ascending order of preCondOrder, preserving the existing relative order of objects with the same preCondOrder.

    adv3.h now defines a couple of new templates for your convenience:

       SyncEventList template ->masterObject inherited;
       Hint template 'hintText' [referencedGoals]?
    
    When an Unthing is used as a possessive or locational qualifier ("the key in the unthing" or "the unthing's key," for example), the parser now shows the Unthing's "not here" message. In the past, the parser showed the same default messages for an Unthing as for any other object, using wording like "The unthing doesn't appear to have that" or "You see no key in the unthing." This wording was incongruous for Unthings because an Unthing represents the absence of an object, and hence an Unthing can't "appear" to have or contain - or even not have or not contain - anything. Showing the Unthing's "not here" message yields more sensible results.
    An Unthing now responds to LISTEN and SMELL commands with the same message it uses for EXAMINE (by default, "You don't see that here"). This applies even if the Unthing is distant or obscured. (In the past, the inherited Decoration messages for these verbs were used.)
    The English library provides a new template for Unthing:

    Unthing template 'vocabWords' 'name' @location? 'notHereMsg'?;
    
    The finishGame() function now updates the global turn counter to reflect any turn currently in progress, or, when called from a daemon or fuse, the turn the player has already completed. In the past, the turn counter displayed in finishGame() didn't reflect the turn in progress that triggered the call to finishGame(). Internally, a turn isn't counted in the global turn counter until it's finished; but from the player's perspective, a turn should count as soon as it starts. This change ensures that the count that finishGame() displays is consistent with the player's expectations.
    A Lockable that's also an Openable no longer reports its locked/unlocked status when the object is open. By default, an Openable must be closed in order to be locked, so if it's open, it's always unlocked. This means that the locked/unlocked status is effectively redundant information when the object is open.

    By way of implementation, the Lockable class has a new method, lockStatusReportable, that indicates whether or not the lock status is worth mentioning in the object's current state. (This doesn't supersede lockStatusObvious, but rather complements it. lockStatusObvious still indicates whether or not the lock status is visually apparent; lockStatusReportable indicates whether it's worth mentioning from an aesthetic point of view.) Lockable defers to any mix-in class that provides the method, and Openable takes advantage of this by providing an implementation of the method that returns true if the object is closed, nil if it's open.

    Door and SecretDoor are now based on a new common base class, BasicDoor. The new BasicDoor class defines the core internal behavior of a door: a travel connector object that can be open or closed, that allows travel only when open, and that can have two objects linked together to represent the two sides of the door.

    The relationship between BasicDoor and Door is analogous to that between BasicOpenable and Openable. BasicDoor defines the internal behavior, while Door adds handling for user commands to manipulate the door (in particular, OPEN, CLOSE, LOCK, UNLOCK).

    This change doesn't affect the functionality of Door; it merely rearranges the code for Door internally by moving a few methods to the new BasicDoor base class. SecretDoor isn't much affected, either, but does now add support for remembering a recent traversal for disambiguation purposes, as Door did in the past (and still does). No changes should be needed in existing game code.

    The main benefits of this change are that it makes SecretDoor behave more consistently with the Door class, and it provides a single base class for all door-like objects. In addition, the new BasicDoor class makes it easier to define custom doors with special controls, since it has all of the necessary functionality but doesn't make any assumptions about handling commands.

    BasicDoor.noteRemoteOpen() now calls a new method, describeRemoteOpen(), to generate the message describing the change. This makes it easier to override the message for a door, since you can simply display the custom message without having to worry about the sense context.
    The Enterable and Exitable classes no longer accept the GO THROUGH command. In the past, these classes did accept GO THROUGH as synonymous with travel, but this became undesirable when the EntryPortal and ExitPortal classes were added. Enterable and Exitable are meant for things like buildings, where ENTER or EXIT make sense but not GO THROUGH; EntryPortal and ExitPortal are for things like doors and passages, where you can equally well ENTER/EXIT them or GO THROUGH them.
    The AutoClosingDoor class has a new method, reportAutoClose(), that generates the message that mentions that the door closed behind whoever just went through it. The TravelVia action() handler calls reportAutoClose() after it automatically closes the door; by default, reportAutoClose() shows the standard library message &doorClosesBehindMsg. In the past, the action() handler simply showed the same library message itself, rather than calling out to a separate method.

    The purpose of this change is to make it easier to customize how the auto-closing is reported, or whether it's reported at all. If you want the door to close silently, without any mention that it closed, simply override reportAutoClose() to do nothing.

    By default, a FireSource object now removes itself from consideration as a default indirect object for the BurnWith action if it's not already lit. This ensures that the library won't (for example) attempt to use an unlit candle as a default source of fire to light another candle. In the past, under certain circumstances, the library could cause a stack overflow by trying to light one candle with another, which it tried to light with the first, and so on. This change ensures that a fire source will only be chosen as the default indirect object for BurnWith if it's already lit.

    (Matchstick objects, of course, aren't affected by this change, since they can light themselves on fire independently. Matchsticks thus remain valid as default choices for BurnWith even when they're unlit.)

    The Wearable class now sets an elevated likelihood (via logicalRank()) for a REMOVE (or TAKE OFF or DOFF) command when the object is being worn. This helps avoid disambiguation prompts for these commands.
    When a Hidden object is discovered (via its 'discover()' method), the object now marks itself and its contents as having been seen by the player character, assuming they're within sight at the time the object is discovered.
    The new RoomPart methods moveIntoAdd(room) and moveOutOf(room) make it easier to dynamically change a room's set of RoomPart objects. In the past, there was no straightforward way to change a room's RoomParts dynamically; these new methods make it easy by making the necessary updates to the affected room's properties.
    Room.getExtraScopeItems() now adds the room's floor to scope only if the actor is directly in the room. In the past, this routine added the floor to scope unconditionally. The rationale of adding the floor to scope is that an actor standing in a room is aware that there's a floor there simply by virtue of standing on it - but that rationale only holds when the actor is actually standing on the floor (or sitting on it, or whatever), and that's only the case when the actor is actually directly in the room. If the actor is in a connected room, or inside a nested room within the outer room, then in most cases the actor isn't actually in contact with the floor.

    Similarly, NestedRoom.getExtraScopeItems() now adds the nested room to scope only when the actor is directly in the nested room. The rationale is the same as for the floor in an outer room.

    The TALK TO command now treats talking to oneself as illogical; in the past, it was merely considered improbable.

    A side effect of this change is that the intransitive conversational commands (HELLO, GOODBYE, YES, NO) won't try to make the PC talk to itself by default. In version 3.0.8, these commands were enhanced to choose the same default interlocutor as TALK TO would when the most recent interlocutor is no longer present. This had the undesirable effect of choosing the PC as the default target when no other actors were present. Now that TALK TO won't choose the PC as the default actor, HELLO and the rest won't do so, either.

    In past versions, the InConversationState's "boredom counter" was handled inconsistently in certain cases. In particular, when an NPC initiated a conversation, it took one more turn for the NPC to become bored than it took when the PC initiated the conversation. This has been corrected.
    The library now uses the "identity" object when setting a pronoun antecedent in Actor.setPronounObj(). This ensures that when an internal program object is being manipulated, and that object generates a pronoun antecedent, any subsequent command reference to the antecedent will properly refer to the main simulation object associated with the internal implementation object. For example, this ensures that when a ComplexComponent is manipulated, subsequent references to "it" refer back to its ComplexContainer parent.
    A library bug introduced in version 3.0.8 caused a run-time error when the player attempted to address a command to another actor and used a pronoun to identify the target actor (as in HIM, GO NORTH or ASK HIM TO GO NORTH - the first phrasing is awkward enough that most players wouldn't think to use it, but the ASK phrasing is natural enough that players might try it). This has been corrected.
    A bug in ThingOrTopicMatchTopic.isMatchPossible caused a run-time error under certain conditions. This has been fixed.
    The <.roomname> style tag now adds a newline after the room name text; this is in lieu of the <.roomdesc> style tag showing a newline before the room description, as was done in the past. The newline is really part of the room name formatting, to ensure that the room name goes on a line by itself. This change won't affect most existing games, since the usual LOOK AROUND format shows the room name followed by the long description; but the change does ensure that a room description won't have an extra leading newline in cases where the room name isn't displayed.
    In Intangible, Fixture, Decoration, Room, and RoomPart, hideFromDefault() now simply returns nil. (In the past, these classes defined hideFromDefault() as returning the inherited value, which didn't have the desired effect of bypassing the inherited call (from Thing.hideFromDefault()) to self.hideFromAll(). These classes override hideFromAll(), but they want to use separate handling for hideFromDefault(). The old override didn't correctly accomplish this because of the way the base Thing implementation of hideFromDefault() is written.)
    The library now responds to commands like THROW ME, THROW ME AT something, and THROW ME TO something with an appropriate failure message. (In the past, these commands were actually allowed, and used the standard THROW handling, which obviously isn't desirable.)
    The library now accepts SIT and LIE (and, equivalently, SIT DOWN and LIE DOWN) as intransitive verbs. In the past, these were defined as missing-direct-object forms of the transitive commands SIT ON and LIE ON, which meant that the library tried to find a default direct object. This had the undesirable effect of making an actor who was already sitting/lying automatically stand up and sit/lie on some other suitable object in the room, which as a last resort was the default floor. The new intransitive verbs check to see if the actor is already sitting/lying, and if so, simply generate an error message to this effect. If the actor isn't already sitting/lying, the new commands proceed as before: they look for a suitable default object, and sit/lie on that.

    As part of this change, the intransitive SIT DOWN and LIE DOWN phrasings no longer have [badness], so the parser will match them as full-fledged intransitive phrasings.

    There are two small changes to the action message processor.

    First, it's now possible to define a message method on a Thing that takes no arguments, even if the message normally does require arguments. This makes it more syntactically convenient to define simple message string or property-redirect methods that require some small chunk of code, but which don't need any of the argument values that are normally sent to the message method. For example, we can now write this:

       notImportantMsg = (myGenericMsg)
    

    instead of this (which was formerly required):

       notImportantMsg(obj) { return myGenericMsg; }
    

    Second, these sorts of message methods can now return nil to indicate that they don't want to provide the message after all. This is useful when an object only wants to override a library message when it's in a certain state. It's also useful when an object wants to provide the message only when that object is in a particular role in a two-object command (i.e., a command with both a direct and indirect object). Recall that when a command involves both a direct and an indirect object, the message processor asks both of the objects for an override; this new capability lets the objects be selective about providing an override according to their actual roles in the command.

    For example, suppose you want your 'vase' object to override the "not a container" message for PUT IN, but only when the vase is the indirect object of the PUT IN, not when it's being put somewhere itself. To do this, you could write vase.notAContainer like so:

       notAContainerMsg = (gIobj == self
                           ? 'The vase\'s opening is too small.' : nil)
    

    The library provides a pair of new macros that simplifies the syntax for that kind of conditional message override: dobjMsg() and iobjMsg(). These macros simply test to see if gDobj or gIobj (respectively) equal 'self'; if so, they return the argument, and if not they return nil.

    The statusLine object has a new method, setColorScheme(), that sets up the status line's colors. This change won't affect any existing code, since it's just a minor internal reorganization, and the behavior is the same as it used to be. The point of the change is that it's now easier to customize the status line color scheme, since you only have to override this one method, and the only thing the override has to do is to write out a <BODY> tag with the desired color scheme.
    The English tokenizer now recognizes upper-case "'S" (apostrophe-S) suffixes as equivalent to the lower-case version. In the past, the tokenizer only recognized this token in lower-case.
    In the English version, the SpecialTopic matcher now specifically rejects matches for several "weak" topic command inputs: I, L, and LOOK. The matcher only rejects the matches when these are the only thing the player typed. If the player types just "I", for example, they probably intend to enter an INVENTORY command rather than to select a special topic that happens to have "I" among its tokens (as in "tell him I don't know").
    In the English grammar, plurals are now accepted as possessive qualifier phrases (as in "the women's books"). When a plural possessive is used to qualify a plural phrase ("the women's books"), the parser resolves the overall phrase to all of the in-scope objects matching the base noun phrase that are owned by anyone matching the qualifier phrase. When a plural possessive qualifies a singular noun ("the women's book"), the parser makes the same initial selection, but then prompts for more information if the selection includes more than one object.
    When a possessive phrase is used to qualify a noun phrase, and the result is ambiguous, the parser now filters the list of possible matches using the filterResolveList() mechanism. This ensures that special objects - especially Unthings and Collectives - are treated the same in this case as they are in other situations. In the past, for example, the parser included Unthings in a disambiguation list if the Unthings matched the vocabulary of a possessive qualifier; this was undesirable because Unthings aren't meant to be treated as actual, present objects from the player character's perspective.
    In the past, if the player responded to a disambiguation prompt with a possessive phrase, and the specified possessor didn't own any of the objects in the original disambiguation list, the parser responded with a message like "Bob does not appear to have any such thing." This was inconsistent with the way disambiguation responses are treated for other types of responses. Now, an inappropriate possessive response receives the same message as would a random adjective that doesn't apply to any of the original possibilities: "That was not one of the choices."
    The English version of the library now lets the Action determine which message to use when there's nothing in scope matching a noun phrase in a command. In the past, the parser always used the "noMatch" message, the default wording of which was "You see no noun phrase here." This was sometimes jarring for commands that generally refer to non-visual objects, such as LISTEN TO or SMELL. With this change, the library now calls the Action (via its new method noMatch()) to determine which message to show. Most actions simply invoke the new message method noMatchCannotSee(), which shows the same message as before. The LISTEN and SMELL actions override noMatch() to call the new message method noMatchNowAware(), which shows the message "You are not aware of any noun phrase here." The new wording should be more natural when the noun phrase is something inherently non-visual.

    (It would be even better if the library could decide based on the noun phrase itself whether the noun phrase refers to something visual or not, since the player could just as well try to EXAMINE THE SULFUROUS ODOR or TAKE THE BEEPING NOISE. Unfortunately, it's effectively impossible to do this, since the true meaning of the error message is that the parser can't determine what the noun phrase actually refers to. We don't want to say that outright, of course, since that would detract from the player's sense of immersion in the game world by calling attention to the parser and its limitations, so the best we can do is to find a heuristic that works most of the time. The addition of the per-action customization improves on the old scheme and should make incongruous cases relatively rare.)

    The parser's algorithm for choosing grammar matches has been fine-tuned to handle cases with truncated words and adjective-ending phrases a little better, particularly in commands involving multiple objects. In some cases, when presented with a two-object command (e.g., PUT X IN Y) in which one of the object phrases failed to match any in-scope objects, for the other phrase, the parser formerly chose a noun-ending phrase over an adjective-ending phrase, even if the adjective-ending version matched an object in scope and the noun-ending version didn't. This sometimes yielded odd "you don't see that" messages that named the object that actually was in scope, from the player's perspective. This change should improve the behavior in situations like this.
    In the English library, the code that tries to figure out whether to use "a" and "an" for an object's aName now ignores any leading HTML tags in the object's base name. This is useful for objects with aName strings such as "<i>Apple County Times</i>".

    In addition, the code now specially recognizes names that start with one-letter words (such as "X-ray" or "<q>L</q> button"), and assumes they're pronounced by saying the name of the letter. So, for example, "X-ray" turns into "an X-ray," since the "X" is pronounced as "ex."

    The English library now accepts SET as a synonym for PUT in the commands PUT DOWN, PUT ON, PUT IN, PUT UNDER, and PUT BEHIND.

    SET is also now accepted as a single-object verb. When applied to a Thing, the library assumes the intention is SET thing ON something, and prompts for the other object. When applied to a Settable, the library assumes the intention is SET settable TO setting, and prompts for the setting.

    Note that the single-object form (simply SET dobj) could be useful all by itself in some cases: SET ALARM, for example. For such cases, you can simply define a custom dobjFor(Set) for the object.

    In the English library, L is now accepted as a synonym for LOOK in the various forms of the LOOK UP and LOOK FOR commands: L UP topic IN object, L topic UP IN object, L FOR topic, etc.
    In the English library, THROW iobj dobj is now accepted as a synonym for THROW dobj TO iobj, allowing phrasing like THROW BOB THE BALL.

    In addition, THROW obj direction (as in THROW BOOK EAST) is now accepted, via the new action ThrowDir. The default handling for ThrowDir (in Thing) is simply to display a message essentially explaining that the proper command is THROW AT: "You need to be more specific about what you want to throw the book at." If the phrasing is THROW obj DOWN or THROW DOWN obj, though, we use the same response we use for THROW AT FLOOR ("You should just put it down instead").

    The parser's grammar ranking mechanism has a new ranking criterion: "phrasing weakness." This is an arbitrary numeric value that the language module can use to rank particular phrasings as relatively weak, which means that they're less likely to be valid matches.

    The English grammar uses this new criterion to mark phrasings as weak when they involve two object phrases with no prepositional marker and no qualifier word - things like GIVE BOB BOOK. These phrasings are weak because they could be interpreted as a single object with a missing phrase instead of two objects - if the wording were GIVE GREEN BOOK, for example, there's probably a GREEN BOOK and a missing object rather than two objects GREEN and BOOK. A phrasing such as GIVE BOOK TO BOB isn't weak because the prespositional marker makes the grammar fairly unambiguous; likewise, GIVE BOB THE BOOK is relatively clear because the article "the" can't normally go in the middle of a noun phrase, so most likely introduces a second noun phrase.

    In the English library, CLIMB UP and CLIMB DOWN without a direct object are now explicitly recognized in the grammar as missing-object versions of CLIMB UP dobj and CLIMB DOWN dobj. This prevents the CLIMB dobj grammar from treating the UP or DOWN as a direct object, which usually yielded an ungainly response along the lines of "you see no up here." (The missing-object CLIMB UP and CLIMB DOWN are marked with "badness" in the grammar, which means that if there actually is an in-scope object with vocabulary words "up" or "down", the CLIMB dobj form will still be matched - so CLIMB UP as short-hand for CLIMB THE UP STAIRWAY will continue to work as it did before.)
    In the English library, the message substitution parameter "{a/he}" was incorrectly not marked as a sentence subject. It's now so marked.
    In the English library, the methods askMissingObject(), missingObject(), askMissingLiteral(), and missingLiteral() have been moved from class messageHelper (in en_us.t) to playerMessages (in msg_neu.t). These methods are just message generators, so they belong in the message object with all of the other message generators.
    In the English library, the messages for noMatchForAll have been changed to variations of "You see nothing suitable." In the past, the message was "You see nothing to use for 'all' here," which was somewhat misleading: it suggested that there's nothing present at all, when the actual point is merely that there's nothing present that can be used in the manner specified.
    In 3.0.8, OOPS X (for any literal X) was changed so that it didn't consume a turn, but OOPS (without a literal) was inadvertantly left unchanged, so it still counted as a turn. This has been corrected: a bare OOPS command no longer consumes a turn.
    In the past, if the player typed NOTE without any text for the note, the parser prompted for the missing text ("What do you want to note?"). It no longer does this. Instead, the first time an empty NOTE is entered, the parser displays a message explaining that NOTE is usually used to enter a message, in case the player wasn't aware of that; and after that, the parser simply accepts an empty NOTE the same as any other.
    In the past, typing the secret command XSPCLTOPIC by itself on a command line caused a run-time error. (XSPCLTOPIC is a verb that the library uses internally to handle SpecialTopic entries. It's not something the player ever needs to type, or indeed is ever allowed to type, but at certain time, the library internally runs XSPCLTOPIC commands through the normal command processing as though the player had typed.) This has been fixed.
    In the past, if the parser asked for a missing literal phrase (such as for TYPE), and the player responded with input that included a character that the game's tokenizer didn't accept, the game displayed an "unknown exception" message. The problem was that the parser didn't properly catch the tokenizer's bad-token exception in this case. The parser now catches this exception and reports it as normal.
    In the English library, when the parser is announcing the name of each object in a multi-object command, paragraph breaks are now suppressed after the object name. This slightly improves formatting in cases where the action response for an object happens to start with a paragraph break.
    In the English library, the verb-phrase builder method (getVerbPhrase()) for TopicAction, TopicTAction, LiteralAction, and LiteralTAction didn't work correctly. This has been corrected.
    The new output control pseudo-tag <./P0> cancels the effect of an immediately preceding <.P0> tag. This is useful in certain cases where you want to ensure that a newline or paragraph break is displayed, even if a <.P0> was just displayed.
    All of the various spellInt-related functions are now implemented in en_us.t. Some of these functions were formerly in numbers.t, but that wasn't the right place for them because some language modules might need to change these functions' interfaces (for example, to specify the grammatical case or gender of the result). This change should have no effect on any existing game code; it's a purely internal change.
    The message property 'flashlightOnButDark' has been renamed to 'flashlightOnButDarkMsg' (that is, the suffix 'Msg' has been added) for consistency with the other message property names.
    The default library message for TASTE has been changed to "It tastes as you expected" (from "You taste nothing out of the ordinary"). This emphasizes that the physical action was completed but that nothing unusual was detected; the old message could have been read as suggesting that the object in question had no flavor at all, which obviously isn't true of most objects.

    In addition, UntakeableActor and Person now use messages similar to those used for TAKE ("The cat won't let you," "Bob probably wouldn't like that"), since these sorts of actors are meant to exhibit volition, and in the real world would generally not allow themselves to be arbitrarily tasted.

    A couple of the default library messages for POUR incorrectly referred to the direct object where they meant to use the indirect object. These have been fixed.
    3.0.8

    Released 9/12/2004

    Compatibility-breaking change: The class formerly named ShipboardRoom has been renamed to simply Shipboard. This name change is for consistency; most of the other similar mix-in classes are named as simple adjectives. You should search your game code for any mentions of ShipboardRoom and change each to Shipboard.

    Note that the new class ShipboardRoom is provided for convenience as a pre-defined combination of Shipboard and Room. This means that any rooms you previously defined with the superclass list ShipboardRoom, Room (and just changed, following the prescription above, to Shipboard, Room) can now be written simply as ShipboardRoom. This is purely for convenience; you can leave the class list as Shipboard, Room and it will behave the same way.

    Compatibility-breaking change: The class formerly named OccludingConnector is now called Occluder. This change emphasizes that the class is a mix-in that isn't limited to use with sense connectors.
    Possibly compatibility-breaking change: The parameters to the enterConversation() method of the ConversationReadyState class have changed. The old second parameter, 'explicit', has been deleted and replaced with a new parameter, 'entry', which gives the TopicEntry (if any) that triggered the conversation entry. Any game code that calls or overrides enterConversation() must be changed to accomodate the parameter change.

    The new parameter gives enterConversation() more information about what triggered the conversation. This extra information is necessary because, without it, the conversation system wasn't able to properly handle topic entries that were marked as conversational but non-greeting (that is, the isConversational property is true, but impliesGreeting is nil). In the past, such entries were incorrectly treated as though they were entirely non-conversational, so they didn't enter an in-conversation state when used in a response.

    Note that the same information conveyed by the old 'explicit' parameter can be inferred from the new 'entry' parameter. When 'entry' is nil, it means that the conversation entry is explicit, because it was triggered by a direct action (HELLO, etc.) rather than a TopicEntry. When 'entry' is non-nil, it means that the conversation entry is implied by the TopicEntry.

    Possibly compatibility-breaking change: The ConvNode method processSpecialCmd() has an additional parameter, as does the SpecialTopic method matchPreParse(). It's unlikely that any game code will be affected by this, because games don't normally override these methods - usually, games just define SpecialTopic keyword lists.

    The new parameter in both cases gives the "processed" version of the player's input. The default SpecialTopic implementation now matches the processed version of the input rather than the original text the player entered.

    In the English library, the processed input is the player's original input with any periods, commas, question marks, exclamation marks, colons, and semicolons removed, and with any leading "A" or "T" keyword removed.

    The purpose of the change to the parameters of these methods is that it makes it simpler and more efficient to do this filtering.

    Note that the punctuation filtering is new. We filter out the punctuation because it's not usually meaningful for matching special topics. For example, if the player's input contains the correct keywords to match a special topic, we usually want to match the special topic even if the player (for example) put a period or question mark at the end of the command.

    Possibly compatibility-breaking change: The format of the message generated by DropType.getReportPrefix() has been changed so that the message is expected to be a complete sentence. This affects the library messages floorlessDropMsg, droppingObjMsg, throwHitMsg, and throwShortMsg (this last one is new in this update). These methods and messages are mostly for internal use, but if you override or call DropType.getReportPrefix() in your code, or if you've customized any of these messages, you'll need to adjust your own messages for this change.

    The old sentence fragment format had a couple of problems. First, it was likely to be problematic for some non-English translations. Second, it was less flexible than the new full-sentence format, even in English, for the purposes of adding new "drop type" objects and of adding new messages composed using the prefix.

    The parser's pronoun antecedent rules have changed slightly. In the past, when the player entered a command with multiple noun slots (such as UNLOCK DOOR WITH KEY), the parser always chose the last-resolved object as the antecedent for any future pronoun. This meant that the parser remembered either the direct object or the indirect object, depending on the verb. This rule didn't always produce the results that you'd expect in natural English conversation, and had the further drawback that the remembered object varied from verb to verb.

    The parser now remembers both objects for two-object verbs, and waits to make a decision until the player actually enters a command with a pronoun. When the player types a singular pronoun (it, he, she), and the most recent command had multiple noun slots, the parser performs the standard "verify" evaluation on each potential antecedent and picks the most logical one. If more than one antecedent is equally logical, the parser simply picks one arbitrarily, and announces the choice in the usual manner. When the choice is completely arbitrary, it will always be the direct object of the two-noun command.

    Some examples:

       >LOCK DOOR WITH KEY
       Locked.
    
       >DROP IT
       Dropped.   (the key, because it's illogical to drop the door)
    
       >UNLOCK DOOR WITH KEY
       (first taking the key)
       Unlocked.
    
       >OPEN IT
       Opened.    (the door, because it's illogical to open the key)
    
       >LOCK DOOR WITH KEY
       (first closing the door)
       Locked.
    
       >EXAMINE IT
       (the door)
       It's a heavy wooden door.
    

    In the last command - EXAMINE IT - the choice of the two potential antecedents is arbitrary, because it's equally logical to examine the key or the door. The parser thus chooses the direct object of the prior command, and announces the choice to alert the player (in case she was thinking "it" would refer to the key).

    The new Thing method notifyMoveInto(newContainer) complements the existing moveInto notification routines notifyRemove() and notifyInsert(). The new notifyMoveInto() method is called last in the sequence, after notifyRemove() has been called on the old container tree and notifyInsert() on the new container tree. notifyInsert() gives the object being moved a chance to perform any special additional processing before the move, or to veto the move (which it can do by displaying an explanatory message and calling 'exit', just as notifyRemove() and notifyInsert() are allowed to do).

    The new routine has several benefits. First, it provides the object being moved with the same sort of notifications that the old and new containers already receive. Since it runs along with these other notifications, it allows the same distinction as those other notifiers between moves that trigger these side effects (via calls to moveInto) and those that don't (via direct calls to baseMoveInto) Second, since the new method runs last, after the notifyRemove() and notifyInsert() notifications, it has the last chance to veto the move; this means that if it doesn't cancel the move, then the move will definitely proceed (at least to the extent that baseMoveInto() will be called). Any side effects that must be carried out only when the move will succeed can thus be safely coded here.

    The parser now provides more helpful error feedback when the player tries to enter a SpecialTopic command that's not active.

    First, the library automatically enters all of the keywords defined by special topics into the game's dictionary. This ensures that the parser won't claim that special topic keywords are unknown if they're used out of context, as it did in the past. It was confusing when the parser claimed that special topic keywords were unknown, because the parser most certainly did know them some of the time.

    Second, when the player enters a command that looks syntactically invalid, but which doesn't contain any unknown words, the parser scans recent special topics for a match. If it finds a match, the parser will show a new message: "That command can't be used right now." This old response - "The story doesn't understand that command" - was potentially confusing, since the story can recognize the exact same command at other times.

    The parser scans the list of special topics most recently listed in topic inventories, using the new specialTopicHistory object. This object keeps track of a configurable number of the most recent SpecialTopic suggestions listed; set its maxEntries property, which defaults to 20, to the maximum number of recent entries to scan. The purpose of the limit is to ensure that a scan won't take too long, by limiting the scan to the specified maximum number of SpecialTopic items. The trade-off is that the parser will forget older suggestions, so they'll be met with the old "story doesn't understand" message if the player tries them. However, it seems safe to assume that the player will also forget about older suggestions, and so won't think to try them.

    If you set maxEntries to nil, it essentially removes the limit to the number of entries to scan. The library won't maintain the history list at all in this case. Instead, to check an unrecognized command, the parser will simply scan all SpecialTopic objects in the entire game, whether or not they've ever been suggested in topic inventory displays. This option might be preferable for a game where the total number of items is small enough that scanning them all doesn't take an appreciable amount of time. (Or not. Some authors might not want the parser to recognize previously-unseen special topics at all - even to the extent of saying "that command can't be used right now" - because doing so could conceivably give away upcoming plot developments. That is, if the player accidentally types a command that happens to match a special topic that hasn't ever been suggested, the player could infer from the special error message that the command will do something later in the game. The chances of actually giving away anything important this way seem rather remote, but this sort of thing rubs some authors the wrong way. If you don't like the idea of using the different error message for special topics until after they've been suggested at least once, but you also don't want a limit on the number of past topics to remember, just set maxEntries to a very high number.)

    The new property pluralOrder lets you control the order in which a set of objects is processed when the objects match a plural phrase in a command. This property applies to all VocabObjects (which is the base class for any object with vocabulary words); the arbitrary default value is 100.

    In most cases, the order of processing for the matches for a plural phrase is arbitrary, so you won't need to worry about this new property. Once in a while, though, it's convenient to be able to control the order of matching. This is especially useful when objects have ordinal names (first door, second door, etc), or when objects are named by relative spatial position (top drawer, middle drawer, bottom drawer).

    Note that the new property only controls the order of processing within an individual plural phrase. For example, in the command TAKE BOOKS AND MAGAZINES, the books will be sorted by their relative pluralOrder values, then the magazines will be sorted by theirs - but when the command is processed, all of the books will be taken before any of the magazines, regardless of the relative pluralOrder values. This is important because it ensures that commands are processed in the order specified by the player.

    Because pluralOrder and the existing disambigPromptOrder will probably be used in the same way most of the time, disambigPromptOrder now returns the pluralOrder value by default. So, for the common case that the two properties should have the same value, you can simply set pluralOrder.

    The new function cls() clears the screen, and takes care of a couple of chores that should always be done at the same time. First, the function clears any text captured in the transcript manager, ensuring that text that was meant to be displayed before the screen was cleared won't show up on the new, blank screen. Second, the function calls the new gameMain.setAboutBox() method after clearing the screen, ensuring that any <ABOUTBOX> tag is re-displayed; this is necessary because HTML TADS discards any old <ABOUTBOX> information whenever the screen is cleared.
    The new method setAboutBox() has been added to the gameMain() object. This new method is designed as the place to put your game's <ABOUTBOX> tag, if you provide one. The method is invoked during start-up, and is also called from the new cls() function, ensuring that the about-box information will be re-displayed each time the screen is cleared.

    The default implementation in GameMainDef does nothing. The new method is simply a placeholder for the game's (optional) use.

    This change is designed to make it easier to manage your about-box information. HTML TADS discards any previous <ABOUTBOX> tag when the screen is cleared, so it's necessary to re-display the tag whenever you clear the screen. If you write your <ABOUTBOX> tag in the new gameMain.setAboutBox() method, and you always use the new cls() function to clear the screen (rather than calling the low-level clearScreen() function directly), the library will automatically keep yout about-box active.

    You can now control whether or not a given verb accepts the word ALL in its noun phrases. By default, every verb accepts ALL. However, if you set the new gameMain property allVerbsAllowAll to nil, only the basic inventory-management verbs (TAKE, TAKE FROM, DROP, PUT IN, PUT ON) will accept ALL, and every other verb will simply show an error message ("'All' isn't allowed with that verb").

    Some authors like to limit usage of ALL to the basic inventory verbs, either because of tradition or because they think it's a sort of "cheating" when a player tries commands like OPEN ALL or EXAMINE ALL. This feature is probably of particular interest to writers of puzzle-oriented games.

    In addition to the all-or-(almost-)nothing option setting in gameMain, you can control ALL acceptance on a verb-by-verb basis. The new Action property actionAllowsAll controls whether a particular Action accepts ALL for its noun phrases. By default, the base definition in Action simply returns the value of gameMain.allVerbsAllowAll. In addition, the basic inventory verbs (Take, TakeFrom, Drop, PutIn, PutOn) override actionAllowsAll to return true. If you want to enable or disable ALL for an individual action, you can use "modify" to set the property for that action class.

    The library uses the new Style Tag <.announceObj> to set the display style for multi-object announcements. (These are the little announcements, of the form "blue book:", that precede the responses when a command is applied to multiple objects.)

    The default display style is to show object announcements in bold text. When a command operates on several objects, the response can get rather lengthy; the new bold-face style for the announcements is intended to make these responses easier to read by making the boundaries of the sub-parts more visually apparent.

    The new StyleTag object is called announceObjStyleTag. You can use "modify" with this object to customize the announcement style, if you want.

    Actor.lookAround(), Thing.lookAroundPov(), and Thing.lookAroundWithin() now accept a set of bit flags for the 'verbose' parameter. 'true' and 'nil' can still be passed, and have the same meanings as before, so existing code doesn't need to be changed. However, the new bit flags give you more control over the format of the listing. The flags, which can be combined with the "|" operator, are:
    • LookRoomName: include the room name, shown on a line by itself at the start of the description.
    • LookRoomDesc: show the full description of the room.
    • LookListSpecials: show the special descriptions (specialDesc) for visible objects that have them.
    • LookListPortables: list the portable items that are visible.

    Note that lookAroundWithin() automatically adds LookRoomDesc to the flags if the actor has never seen the room before.

    If you pass 'nil' for the 'verbose' parameter, the effect is the same as passing (LookRoomName | LookListSpecials | LookListPortables). If you pass 'true', the effect is the same as passing (LookRoomName | LookRoomDesc | LookListSpecials | LookListPortables).

    The main effect of this change is that it lets you generate a room description without including the room title, which wasn't possible in the past. This can be useful for cases where the standard LOOK AROUND format isn't desirable, such as when describing the view through a sense connector to another room.

    The new Thing method roomRemoteDesc(actor) lets you customize the way a room is described when it's viewed by an actor from another room. The library calls this from Thing.lookAroundWithin() when the actor who's viewing the room is not within the room being viewed.

    Note that none of the standard library commands ever call this new method, because the basic library doesn't provide any command to view a remote room. However, games might provide their own commands to do so, such as LOOK THROUGH KEYHOLE, and you might want to call lookAroundWithin() to generate the descriptions in these cases. Doing so will invoke roomRemoteDesc instead of roomDesc to generate the room description.

    The default Thing.roomRemoteDesc implementation simply calls roomDesc. You'll probably want to override this any time you actually use it, to show a suitable description from the remote actor's point of view.

    A new Thing method, adjustLookAroundTable(), lets you customize the set of objects that lookAroundWithin() can mention. By default, this routine removes the POV and actor objects (which are usually the same object) from the table, ensuring that these objects aren't mentioned.

    In the past, lookAroundWithin() simply removed the actor object directly, so the default behavior is the same as in the past. Separating this out into a new method lets games customize which objects are mentioned and which aren't for the particular room and point of view.

    The new Thing method isLookAroundCeiling() determines whether the LOOK AROUND description for a given object is generated by that object directly, or by its location. By default, this routine returns true if either the object is a top-level room (that is, it has a nil location), or the object's enclosing location isn't visible. In either of these cases, since it's not possible to see out to an enclosing location, the current object has to provide its own interior description.

    This method is called from lookAroundPov(), which formerly performed the same test directly, so the default behavior hasn't changed. This has been separated into a new method because the default condition isn't always ideal. For example, if the player's inside a closed wooden booth with a small window that looks out to the enclosing location, and the player types LOOK AROUND, we'd want to describe the interior of the booth, not the outside location. For this case, you could override isLookAroundCeiling() for the booth to return true even though the location is visible through the window.

    The new TAction method getDobjInfo() returns the full ResolveInfo object associated with the current direct object. Similarly, the new TIAction method getIobjInfo() returns the indirect object's ResolveInfo.
    The parser now keeps track of the noun phrase that was parsed for each object involved in the command. This information is kept in the ResolveInfo object, in the 'np_' property. This information can be used, for example, to obtain the original text of the noun phrase that was resolved to a given object. For example, to obtain the original text that resolved to the current direct object being processed in an action() method, you could write this:

       local txt = gAction.getDobjInfo().np_.getOrigText();
    
    Due to a bug introduced in the previous release, the method PresentLater.makePresentByKeyIf() didn't work correctly if the condition argument was a function pointer. In these cases, the routine called the function on the first object matching the key, and then used that same true/nil result for every matching object. (Thus, if the first matching object was to be revealed, every matching object was revealed; if the first was to be hidden, all were hidden.) This has been corrected; the method once again calls the given function individually for each object matching the key.
    Component objects are now hidden from EXAMINE ALL by default. Since components are simply detail features of larger objects, it's usually enough to examine the larger objects; it usually doesn't make sense to also include the components separately in EXAMINE ALL responses.
    By default, FOLLOW now reports a more helpful message when there's not enough information to carry out the command. In the past, the default message was simply "You don't know where (the actor) went." This was misleading when the real problem was that the actor's departure had been previously observed, but from some other location. The new message is of this form: "The last place you saw (the actor) was (the room where we last saw him)." Note that the library now keeps track of the last place we saw each actor, even when we didn't observe the actor leaving, to ensure that this new message is always accurate.
    The "badness" levels of some of the grammar rules in the English parser have been re-ranked, to put the various error conditions in a different order. In particular, the "miscellaneous noun phrase" rules now have a lower badness than any of the "missing noun phrase" rules. In the past, this relationship was reversed, which caused a subtle problem when a two-noun verb was used with a noun phrase containing a word not in the dictionary.

    Some background: In the English parser, certain grammar rules are defined specifically to match certain distinct patterns of erroneous user input. The point of matching invalid input is to recognize it and respond intelligently to it, rather than issue a blanket error message. Most of these invalid constructs are marked with "badness" levels, which tells the parser to match them only as a last resort, so that we match a valid grammar rule over one of these special error rules whenever possible.

    In the English grammar, each of the verbs with two noun slots (PUT X ON Y, ASK X ABOUT Y, etc.) has a corresponding special grammar rule that includes only the first noun slot (PUT X, ASK X, etc.), for cases where the player inadvertantly or intentionally types the command with only one noun. In the past, these rules weren't marked with "badness" levels. Now they are: these are treated as having "badness" equivalent to that of an empty noun phrase. This ensures that they'll be matched only as a last resort, when there isn't a more "correct" interpretation of the phrase.
    CollectiveGroup objects are now explicitly marked as unlisted in room or object contents or in inventories (that is, a CollectiveGroup's isListed, isListedInContents, and isListedInInventory properties now return nil). In the past, collective group objects incorrectly showed up in these listings as separate objects under certain circumstances, such as when their individuals were self-illuminating (brightness = 1) and the enclosing room was dark.
    In the past, the description of a dark room listed every visible self-illuminating object, even including those being carried by the actor doing the looking. Now, the description excludes objects in the actor's inventory, since these objects aren't part of the actor's surroundings.
    In the past, the INVENTORY command included in its listing any items that were being directly held, even if they weren't visible, on the assumption that the actor could identify these items by touch alone. This has now been refined slightly: instead of unconditionally including directly-held items in the listing, the library now includes directly-held items that the actor already knows about, as indicated by the actor's knowsAbout() method. The rationale is almost the same as before, but this change means that we no longer assume that the actor can identify an item by touch unless the actor already knows about the item. Of course, this change doesn't affect the listing of visible items - visible items are listed whether previously known or not.
    The "finish option" handler for RESTORE (finishOptionRestore) now terminates any remaining processing on the command line that triggered the finish options, when the RESTORE is successful. This ensures that when the finish options are triggered from somewhere in the middle of a command (such as in a beforeAction method), there will be no strange side effects from carrying out the rest of the triggering command.
    In the English grammar, LOOK OUT is now a synonym for LOOK THROUGH. This allows constructs like LOOK OUT WINDOW, which is more natural for some objects than LOOK THROUGH but has the same meaning in virtually all cases.
    In the English grammar, the acceptable phrasing for the SitOn command no longer includes "sit object" or "sit down object." These phrasings aren't grammatical, but they were included to match intransitive inputs (SIT and SIT DOWN) and then ask for the missing direct object phrase. Now, rather than matching the intransitive forms using the main SitOn rule, they're matched using the SitOnWhat rule. The same change applies to the LieOn rules.
    UnlistedProxyConnector.primaryConn is now initialized to TadsObject by default. In rare cases, game code might invoke a property of an UnlistedProxyConnector before the object is constructed; this new default (rather than nil) provides reasonable behavior in these cases, without requiring an added run-time test for a nil primaryConn.
    In the English messages, the AutoClosingDoor's announcement (of the door having automatically closed) now has a leading paragraph break, to set it off from the room description or other travel report that usually precedes the announcement.
    The English library's rules have changed slightly for substituting reflexive pronouns in message strings. In the past, the library always substituted a reflexive pronoun when an "objective-case" parameter matched the subject of the sentence; for example, in the message string "{You/he} can't eat {the dobj/him}," if the direct object was the same as the actor, the message would be "You can't eat yourself" (rather than simply "You can't eat you"). The new rule is almost the same, but has a new exception: if the parameter name of an objective-case parameter is the same as that of the subject of the sentence, the reflexive substitution is not used.

    For example, the library now displays "{You/he} close{s} the door behind {you/him}" as "You close the door behind you." The old rule would have used "behind yourself" instead, but the new exception kicks in because both the subject ("{You/he}") and the objective-case item ("{you/him}") refer to the 'actor' parameter object.

    The reason the automatic reflexive substitution exists in the first place is that it allows you to write a message string that refers to multiple parameters that might end up being the same object some of the time. In the first example ("{You/he} can't eat {the dobj/him}"), the direct object and the actor might sometimes be the same, but usually aren't; since the library automatically provides the reflexive pronoun when the direct object happens to be the same as the actor, the same message string works for any combination of objects ("You can't eat yourself" vs. "You can't eat the deck chair"). However, this rule is too aggressive without the exception. The point of the exception is to give authors exact control in cases where they know in advance that the subject and objective-case object will be the same, by virtue of the fact that the message is referring to the same parameter by name in both cases. In these cases, there's no need for the library to provide reflexive pronouns conditionally, because the author knows in advance that the objects will always be the same - so the author will know whether or not the reflexive pronoun is desired, and will write it that way, or not, as desired.

    If an exception occurs during an NPC's turn (in ActorState.takeTurn, for example), the scheduler will now count the NPC's turn as over, so that other NPC's get a chance to run before the same NPC takes another turn. In the past, an uncaught exception during an NPC's turn bypassed the turn-counting for the NPC, so the same NPC got another turn immediately; in some cases, this caused the NPC to try the same erroneous operation again, which set up an infinite loop as the erroneous NPC code was invoked over and over without giving anyone else a chance to run. The same change applies to fuses, daemons, and any other "schedulable" object.
    In the English library, SCRIPT ON is now accepted as a synonym for SCRIPT (for consistency with SCRIPT OFF), and RECORD ON as a synonym for RECORD. In addition, the RECORD ON/OFF messages have been changed to sound less geeky (they formerly referred to "RECORD mode," but now talk about "command recording" instead).
    In the English library, "inside" and "inside of" are now accepted in locational qualifier phrases (as in "read the coin inside the box").
    When the player looks behind or through a closed door, the library now assumes that the player's intention is to open the door to see what's on the other side. So, the library now applies an objOpen precondition to these commands, which will implicitly open the door if it's closed.

    In addition, in the case of LOOK BEHIND, the library uses a special message if the implicit Open was applied: "Opening the door reveals nothing unusual." This message emphasizes that we're looking through the passage, not at the space created between the door and the adjacent wall (if such a thing is applicable).

    When we look behind a door that was already open, the library assumes that the player's intention is simply to look at the back side of the door, or the space between the door and the adjacent wall. In this case, the inherited Thing message ("you see nothing unusual behind the door") is used.

    LOOK THROUGH PASSAGE now has a custom response when the passage is open: "You can't see much through (the passage) from here." In the real world, we can typically look through a passage into the next room, but the library world model doesn't actually allow us to see anything in the next room; so we don't want to say that we can't see through the passage at all, but at the same time we can't actually say we see anything. The new default suggests that we can in principle see some distance through the passage, but not far enough to see anything in the connected location.

    When a passage is closed, the inherited Thing message ("you can't see anything through (the passage)") is used instead, since a closed passage is assumed to be something opaque, such as a closed door.

    Looking through, under, or behind a room part now yields a custom response that says that you simply can't do that, rather than using the inherited Thing defaults (which say that you can't see anything there). For room parts, it doesn't generally make sense to perform any of these actions, which the new custom messages try to convey.
    If a player types simply GO or WALK, the library now displays a more helpful message: "You'll have to say which way to go." In the past, the parser didn't recognize that phrasing at all, so responded with the standard unknown-verb message.
    PathPassage now uses a custom message for STAND ON: "If you want to follow the path, just say so." The inherited default ("That isn't something you can stand on") doesn't make a lot of sense for paths, but at the same time, we don't want to treat every path passage as a separate nested room, so this message seeks to convey that the action might be logical in the real-world but isn't required in the game.
    Actor now handles a command of the form PUT ME IN container by replacing it with ENTER container. This is done any time the direct object is the target actor.

    In addition, the actor is now excluded in all cases from being the default direct object for PUT IN. In the past, the actor was selected as the default if there was nothing better, which occasionally led to odd results.

    Actor now treats the commands PUT ON and PUT UNDER as illogical when applied to oneself (as in PUT ME ON THE TABLE).
    Thing now calls verifyMoveTo() to verify a DROP command only when the object is being held by the actor performing the command. When the object isn't being held, the DROP will be illogical on that basis alone - the object definitely won't be moved, so there's no reason to verify the move. This is important because some subclasses use verifyMoveTo() to report other catch-all conditions ("that's too heavy to move," etc), which in the past sometimes were reported instead of the more basic problem ("you're not holding that"). By avoiding the call to verifyMoveTo() when the object isn't being held, we ensure that the more basic problem is always reported instead of any applicable catch-all condition.
    The Immovable class incorrectly handled certain two-object actions (such as PUT IN, PUT ON, MOVE WITH) by failing in the action() routine. This still allowed the other object involved in the command to handle the action successfully. For these two-object actions, the class needs to block the action in the check() routine instead, which it now does.
    When an AgendaItem throws an exception (including things like 'exit' signals) out of its invokeItem() method, the library now automatically marks the agenda item as finished (by setting its isDone property to true). In most cases, if an agenda item throws an exception, it's because something went wrong; since the same thing would probably go wrong again if the item were tried again, allowing the agenda item to remain in its "ready" state typically resulted in an infinite loop as the actor kept trying the same item over and over. This change avoids infinite loops in these cases by ensuring that an item that throws an error isn't retried automatically.
    When an object provides its own custom definition for a library message (via one of the xxxMsg properties), it can now be defined as a simple single-quoted string, even if the library method for the message normally takes one or more arguments. This makes it more convenient to define these custom messages. Of course, you can still provide a full method definition that takes the same arguments as the corresponding library message method.
    Settable now applies a touchObj precondition to the SetTo action. This ensures, for example, that a dial is reachable before it can be turned to a new setting.
    The Floor class now includes a touchObj precondition for StandOn, SitOn, LieOn, and (as an indirect object for) PutOn. This ensures that a floor in a connected but unreachable room (such as a room connected by distance) will be ruled out as a potential target object for these verbs, avoiding poor defaults and unnecessary disambiguation prompts.
    The touchObj precondition now considers a distant object illogical during its 'verify' phase.

    In the past, this precondition treated an object that was visible but not reachable as still potentially logical, but at reduced likelihood. The reasoning was that the obstruction might be easily removable via an implied action. For example, an object inside an openable glass case could be made touchable by opening the glass case, which could be performed as an implied action. However, in the case of distance, there's not usually any way to remove the obstruction automatically, so there's no point in considering the object to be logical, even at reduced likelihood. The precondition still does exactly this for objects that aren't at a distance, but now assumes that distance can't be resolved automatically.

    In the past, if the player used a SEARCH command on an Openable, and the object was closed, the implied OPEN action showed the newly-revealed contents of the object. The main SEARCH command that triggered the OPEN also showed the Openable's contents, so the contents were listed twice. Openable now omits the redundant contents listing for the implied OPEN when the main command is SEARCH, just as it's long done when the main command is LOOK IN.
    A "conversational" command (HELLO, GOODBYE, YES, NO) now consistently counts as a turn for the issuing actor, even when a target actor is specified (as in "BOB, HELLO"). This corrects a subtle inconsistency that occurred when a conversational command was directed to an NPC that had a pending agenda item. In the past, explicitly directing a conversational command to such an actor ("BOB, HELLO") prevented the agenda item from executing on the same turn, because specifying the target actor caused the turn to count against the NPC's turn counter rather than the PC's; but leaving out the target actor (as in saying simply "HELLO") counted the action as a PC turn, allowing the NPC to carry out its agenda item. Now, both forms of the command ("HELLO" and "BOB, HELLO") have the same turn-counting treatment, so a pending agenda item will be allowed to proceed in either case.
    The HELLO, GOODBYE, YES, and NO commands now find a suitable default actor to talk to if the target actor isn't specified (that is, if the command entered is simply something like "HELLO" rather than "BOB, HELLO"). If there's a current interlocutor, and it's still within earshot, then it's the default. Otherwise, we look for a suitable default direct object for a TALK TO command. This makes things more convenient for the player, since it allows the player to omit the target actor any time only one suitable actor is present.
    In the past, saying BYE to an actor in a conversation-ready state (that is, an actor ready for a stateful conversation, but not currently in conversation) caused a run-time error. This has been corrected.
    Non-conversational topic entries (those with the isConversational property set to nil) now leave the actor's ConvNode unchanged. Conceptually, a response that doesn't involve any conversation shouldn't affect the thread of an ongoing conversation, so it makes more sense for such topics to leave the ConvNode unchanged.
    In the past, Thing.lookAroundWithin() generated a slightly strange description when the point of view was a remote room, and the room containing the actor doing the looking was visible from the remote room. In these cases, the actors were described as being "here" ( as in "Bob is standing here"), even though the "here" in this kind of description is more properly the remote point-of-view room. The actors are now described as though they're in a remote room ("Bob is standing in the cave"), which is consistent with the perspective of the rest of the description.
    In the past, no turn was counted for giving an actor an order (BOB, GO NORTH) that was refused in the actor's obeyCommand() method. This didn't entirely make sense, since merely attempting to give an order to an actor is an in-game action, from the player's perspective. This has been changed so that giving an order that's refused counts as one turn.
    In the past, if an actor was in an accompanying-travel state, carrying the actor and then traveling to a new room caused a run-time error. The run-time error has been fixed.

    In addition, the accompanying-travel state is now ignored when the actor is being carried. When an NPC is being carried, it will naturally move along with the actor it's accompanying, just as all of the actor's other inventory items do by virtue of being carried. We therefore don't want the NPC to separately travel to the new location, because doing so makes the actor drop the NPC.

    Thing now defines the methods canSee(obj), canHear(obj), and canSmell(obj). In the past, these were defined only by Actor, not by Thing, since only actors have senses that allow perception of the physical surroundings. In some cases, though, an actor uses an inanimate device as a point of view (a television monitor hooked up to a remote camera, for example), and in these cases it's necessary to check visibility (or other senses) from one inanimate object to another. The new methods provide this capability. They can be overridden as needed; for example, a camera that operates in the infrared might want to override canSee() to calculate visibility in a custom "infraredSight" sense rather than in the basic "sight" sense.
    The NOTE command is now ignored for the purposes of the AGAIN command. For example, if you type INVENTORY, then type NOTE SOMETHING, then type AGAIN, the game will repeat the INVENTORY command. There's no point in repeating a NOTE command, and NOTE is a sort of side conversation between player and author anyway, so it makes sense for AGAIN to act like the NOTE never happened.
    The SAVE command is now ignored for the purposes of AGAIN. There's no point in repeating a SAVE command immediately, as nothing will have changed in the game state between two consecutive SAVE commands, and hence no re-saving is warranted.
    After a game is successfully restored, the memory of the previous command is forgotten, so the AGAIN command cannot be used. This ensures that there's no confusion about whether AGAIN refers back to the RESTORE command (which would be pointless, as it would just re-restore the same state), the previous command in the same session, or the last command before SAVE from the restored game.
    When a "finish options" menu is presented (with finishGame() or finishGameMsg()), and the player selects the UNDO option, the library now treats UNDO as the last command for the purposes of AGAIN. That is, if the player answers a finish-options prompt with UNDO, and then types AGAIN, the library performs another UNDO. (In the past, the game repeated the command that led to the finish-options prompt, since that was the last actual command. However, most players wouldn't think to make this distinction, and wouldn't want to repeat whatever command led to the game-ending prompt anyway, so the most reasonable interpretation of an immediate AGAIN is to perform another UNDO.)
    The RESTORE command is now ignored for the purposes of the UNDO command. When a RESTORE succeeds, there's no way to undo anything: the restore itself can't be undone, and the incoming restored state initially doesn't have any undo history. When the RESTORE fails, it won't make any changes to the game state, so there's nothing about the RESTORE itself to undo. The main effect of this change is that typing UNDO after a failed RESTORE undoes the last undoable command immediately before the RESTORE.
    In the past, if the player typed an OOPS command at the wrong time (when there wasn't any spelling error to correct), it was counted as a turn. This was undesirable because OOPS is a meta command, not an in-game action. The command no longer counts as a turn.
    In the English library, a string pre-parser now wraps the comment text of a NOTE command in quotation marks, if the text contains any punctuation marks or the word THEN. This allows players to enter arbitrary free-form text in NOTE remarks without any danger of confusing the parser.
    The formatting in the English instructions has been tweaked slightly to make it more consistent. In addition, the parsing of the responses to the INSTRUCTIONS prompt had a couple of small problems that have now been corrected.
    A bug in the English library caused message substitution strings of form "{it's dobj/he's}" (that is, with an apostrophe-S on each example word, and the parameter name written in the middle of the two example words) to be expanded incorrectly. This has been corrected.
    In the English library, the methods (theNamePossNoun, theNamePossAdj) that build the default possessive name based on the base name of an object now add an apostrophe-S to a plural name that doesn't end in "s". In the past, a bare apostrophe was always added to a plural name, which produced the wrong results for certain words (the men', the children', the fish').
    The English library's method that builds the default plural name of an object (pluralNameFrom) now handles abbreviations more in keeping with conventional English usage:
    • For single letters, the method now adds an apostrophe-S to small letters (a's, b's, c's) and to the capital letters A, E, I, M, U, and V (A's, E's, etc). For other capital letters, and for non-letters, the method simply adds an "s" (Bs, Cs, Ds). (The apostrophe-S capitals are special because they could be confused with words or common abbreviations if we just added an "s".)
    • For words that end in a capital letters or a digit, the method now just adds "s" (CPAs, PCs, MPs, 1970s, 20s).
    • For words that end in a period, the method adds apostrophe-S (Ph.D.'s, M.A.'s).
    The new Thing property objInPrep can be used to specify the preposition to use when describing another object as being within a given object. In the English library, the default for Thing is 'in'. Note that the existing property actorInPrep is now defined by default as returning the same value as the new objInPrep, since for most purposes they have the same meaning.
    A new message is displayed when a Throw command fails because a DistanceConnector is in the way. In the past, the library used the default Thing message, which describes the projectile as bouncing off the object that's in the way. In the case of a distance connector, this doesn't make any sense. The library now uses a custom message that describes the projectile as falling short of the target.
    In the past, if an object was thrown through a MultiLoc connector to a separate top-level room, the object sometimes incorrectly landed on the originating side of the connector rather than on the target side. This only happened when the MultiLoc was a peer of the target. The thrown object now correctly lands within the target's top-level location rather than within the origin's top-level room.
    The ComplexContainer class now refers the methods isOpen, isLocked, makeOpen, and makeLocked to the subContainer, if it's non-nil. A complex container appears in the game world as effectively enclosing the contents of its sub-container, because the sub-container doesn't usually appear as a distinct object in the game world, so the apparent open and locked status of the complex container are almost always the same as for its sub-container. These new referrals make it a little easier to write code by removing the need to refer explicitly to the sub-container when checking or changing the open or locked status of a complex container.
    An error in BasicOpenable.makeOpen() caused a run-time error ("wrong number of arguments") in certain cases when a BasicOpenable was used with multiple inheritance. In particular, the error ocurred when an object derived from BasicOpenable also had at least one additional superclass which itself defined makeOpen(), and the additional superclass was placed after the BasicOpenable subclass in the object's superclass list. This has been fixed.
    3.0.7

    Released 6/12/2004

    The default rules in Thing and NonPortable for contentsListed and contentsListedInExamine weren't quite right, so they've been changed slightly. The new default rules are that an object's contents are always listed when the object is directly examined, and they're listed in "indirect" examinations if (1) the object is itself mentioned somehow in the indirect description, and (2) its contents would be listed in a direct examination.

    (A couple of definitions might help. A "direct examination" is what happens when the player explicitly EXAMINEs the object; an "indirect examination" occurs when looking at the room, showing an inventory listing, or directly examining an enclosing object. An object is "mentioned somehow" in an indirect description if either (1) it's included in the basic generated list of portable items within the enclosing object that's being examined, as determined by the isListed property, or (2) it shows as special description, as determined by the useSpecialDesc method.)

    The reasoning behind the new rules is as follows. First, on directly examining an object, its contents should almost always be mentioned; we'd want to conceal the contents only in unusual cases, so the default for contentsListedInExamine is simply 'true'. Second, when indirectly examining an object, we'd normally want its contents to be included in the description, since they're also, indirectly, the contents of the enclosing object being examined; however, when the object itself isn't mentioned in the indirect examination, then we probably don't want to mention its contents, either. Furthermore, if for some reason we've chosen to conceal the contents even when the object is directly examined, then we almost certainly want to conceal the contents in indirect examinations as well.

    The rule is slightly different, and simpler, in NonPortable: contentsListed simply returns the value of contentsListedInExamine. The reason for this different default is that a NonPortable is almost always a fixed feature of its enclosing objects, and as such is almost always mentioned, to the extent it merits mentioning, as part of the custom description of the room or containing object. This is sometimes accomplished with a specialDesc, but not always, so the absence of a specialDesc isn't sufficient reason to think that the object isn't mentioned. Since a NonPortable almost always gets some kind of mention in indirect examinations, then, the default is that its contents are listed in these cases, too, as long as they'd be listed in a direct examination.

    The Consultable class now requires that the object be both touchable and visible for the Consult action (in the past, it only had to be touchable). Consultables are typically things like books or maps whose contents are read visually, so in most cases it only makes sense to consult a consultable when it's visible. This change prevents lookup up information in a consultable while in the dark, for example.
    The new property disambigPromptOrder can be given to any VocabObject (any simulation object with vocabulary words) to control its ordering in an interactive disambiguation prompt ("Which book do you mean, the red book, or the blue book?"). When a disambiguation prompt is shown, the objects in the prompt are listed in ascending order of this property value. By default, disambigPromptOrder is 100; since all objects have this same default, if you don't override it for any objects, the ordering of disambiguation lists will be arbitrary.

    Arbitrary ordering is usually fine. However, it's nice to be able to control the order when objects have ordinals in their names ("first book," "third button," etc), to make the list order match the nominal order. For example, if you have a group of books named "first book," "second book," and "third book," you could give these objects disambigPromptOrder values of 101, 102, and 103, respectively.

    When the parser processes a response to an interactive disambiguation question ("Which book do you mean..."), it now takes a vocabulary match to an ordinal name as superseding the alternative interpretation as a list position. For example:

       Which book do you mean, the red book, the first black book, or the
       second black book?
    

    >second

    The parser will now interpret "second" as referring to "second black book." In the past, it took this to mean the second item in the list, which in this case is "first black book"; this is hardly ever what the player means in these cases, though.

    (This change is actually a bug fix. The parser already had logic to apply this precedence order, but it didn't work properly. In particular, the "adjective ending" preference - the preference for avoiding a phrase that ends in an adjective - superseded the ordinal preference. Adjective endings are obviously not a problem for disambiguation responses, though, so this has been changed.)

    When the parser asks the player for disambiguation help to choose among several items that can be distinguished only by their location, and some of the items are in different top-level locations from the player character, the parser now describes the remote locations using the remote room names (rather than with the default "floor" or "ground" container, as was formerly the case). This helps ensure that objects are distinguished properly when several top-level rooms are connected by sense connectors.

       Which coin do you mean, the one on the floor, or the one on the
       balcony?
    

    In a related change, the parser now chooses a "local" interpretation of a locational qualifier in cases of ambiguity. For example, if the player were to answer the question above with "the one on the floor," the parser would now assume that the player is talking about the coin in the local location (i.e., within the player character's top-level enclosing room). In the past, if the balcony also had a floor, the locational qualifier would have been ambiguous.

    The new function dataTypeXlat(val), defined in the system library file _main.t, returns the "translated" datatype for the given value. This is almost identical to dataType(val), except that when 'val' is an anonymous function, the new dataTypeXlat() returns TypeFuncPtr rather than TypeObject. In most cases, it's convenient to treat an anonymous function pointer value as though it were an ordinary static function pointer, because the two types are almost interchangeable in their usage; this function makes it easier to code such cases, because you don't have to worry about these two different ways that a function-pointer-like value might appear.
    The new class AskTellShowTopic lets you combine responses for an object for ASK ABOUT, TELL ABOUT, and SHOW TO in a single topic response object. Players sometimes find SHOW TO to be more natural than ASK ABOUT when an object is present and visible; this new class makes it convenient when you want to handle either command the same way.
    A bug in Thing.lookAroundWithinContents caused an infinite loop when attempting to describe a location in which three or more top-level rooms were linked by sense connectors. This is now fixed.
    In 3.0.6q, BulkLimiter defined lookInDesc as doing nothing. This was problematic because BulkLimiter is inherited by some special types of containers, such as Underside and RearSurface, that don't have interiors for the purposes of LOOK IN. The problem showed up in Underside and RearSurface objects: attempting to LOOK IN these objects showed no reply at all, hence yielded the library's default "Nothing obvious happens" message.

    BulkLimiter no longer defines this property at all. Instead, this functionality has been moved to Container and Surface. Furthermore, BulkLimiter.examineInterior no longer shows the lookInDesc; instead Container and Surface now show the lookInDesc as part of their action() handler for the LOOK IN command.

    The new Thing method hideFromDefault() is similar to hideFromAll(), but indicates that the object should be hidden from being used as a default for the given action (that is, it won't be considered as a possible default when the player types a command that omits one or more required noun phrases). By default, this simply returns what hideFromAll() returns for the same action, since in general an object that's hidden from ALL should also be ignored for defaulting.

    This is a coarser-grained mechanism than using verify() routines for individual actions, but it's more convenient when you want to exclude an object from being used as a default for a large number of actions.

    gActionIs(a) now returns true if 'a' is one of the specific-direction travel subclasses (NorthAction, SouthAction, etc), and the current action is a base Travel action that's going the same direction. This makes it possible to test for a NORTH command simply by testing gActionIs(North), which is what you would have expected even in past versions if you hadn't known better.

    (In the past, it was necessary to write a test like this instead: (gActionIs(Travel) && gAction.getDirection == northDirection). This two-part test was required because the grammar rules for NORTH, SOUTH, etc. generate the base Travel action instead of the specific-direction subclasses. The grammar rules still do this, but gActionIs() now calls a new Action method, actionOfKind(), which TravelAction overrides to encapsulate the two-part test; since TravelAction does the test for you, there's no longer any need to write it out by hand. You can still write the two-part test if you prefer, and it still works the same as it did before, but it's easier and clearer to just write gActionIs(North) and the like.)

    The algorithm in Actor.tryMakingRoomToHold has been improved slightly. (This is the routine that automatically frees up space in an actor's hands, when the space is needed for the current explicit action, by moving things being held into "bags of holding.") The change is a bit heuristic, but it should work well in most cases. The new rule: when there are at least four items that can be moved into bags of holding, the routine will now find the two that were picked up most recently and move them to the end of the list of possible items. In practice, it's rare to have to put away more than one or two items for a single action, so this change effectively ensures that the items picked up most recently won't be put away automatically.

    The point of this change is to avoid annoying situations where the game automatically puts away an object that you just picked up in order to do something with it. These situations were especially annoying when they were entirely automatic (for example, the game picked up an object automatically, then put it away automatically in order to make room to pick up another object automatically, then had to go get the first object out again in order to use it). It was rare for this to happen, but it looked pretty silly when it did.

    In a related change, the library now arranges the order of putting things away to ensure that if object A goes inside bag of holding B, and B goes inside another bag of holding C, then A will be put in B before B is put in C. This was almost always the result anyway due to affinity ordering, but proper nesting order is now explicitly assured.

    There are two new "illogical" status types that "verify" routines can use to indicate the logicalness of an action with greater precision. The library uses these new types when appropriate in its action handlers. The new types are:
    • illogicalAlready() - this indicates that the action is illogical because the condition the command seeks to create is already in effect. For example, we're trying to TAKE an object that the actor is already holding, or we're trying to OPEN an object that's already open. This new type is almost the same as illogicalNow(), but provides more detail about the condition.
    • illogicalSelf() - this indicates that the action is illogical because it's attempting to use an object on itself in some invalid way. For example, we're trying to PUT something inside itself. This type is almost the same as illogical(), with the added detail that it's the self-application that makes the command impossible to carry out.
    The Passage class now considers a closed passage to be invisible in a dark room. The reasoning is that, when a passage is open, enough light from the adjoining room (if the adjoining room is lit) comes through the passage to make the passage itself visible, although not enough comes through to light the current room; but when a passage is closed, not even this small amount of light comes through the passage.
    In the past, the parser matched a plural phrase (such as EXAMINE BOXES) to the most likely subset of the matching in-scope objects, as determined by the "verify" logicalness ranking. This was too restrictive; it wasn't at all the common-sense interpretation that most users would expect from a plural usage, which is simply to match everything that matches the vocabulary.

    This has been changed. There are two possible behaviors, which you can control via a global configuration property, gameMain.filterPluralMatches:

    • If gameMain.filterPluralMatches is true, the parser excludes objects from matching the plural phrase if their "verify" routines include an "illogical-already" or an "illogical-self" result. This means, for example, that TAKE BOOKS will exclude any books that are already being held, DROP BOOKS will exclude any books that aren't in the player's inventory. This is the default.
    • If gameMain.filterPluralMatches is nil, the parser simply uses all vocabulary matches for a plural.
    The English parser now handles pronouns better in the recently-added command phrasing TELL actor TO do something. In particular:
    • A reflexive pronoun that agrees in number and gender with the actor will be treated as referring to the actor, as long as there's not a valid interpretation internal to the predicate. For example, TELL BOB TO HIT HIMSELF will treat HIMSELF as referring to Bob. However, TELL BOB TO ASK BILL ABOUT HIMSELF will treat HIMSELF as referring to Bill, since that interpretation is entirely self-contained within the predicate.
    • A regular pronoun that agrees in number and gender with the actor will be treated as referring to the actor. This is especially important with possessives: TELL BOB TO DROP HIS BOOK will treat HIS as referring to Bob.
    • A second-person pronoun is now considered to refer to the player character, rather than to the target actor. For example, TELL BOB TO HIT YOU is taken to mean that Bob should hit the player character.
    • The target actor is set as a pronoun antecedent for subsequent commands from the issuing actor. For example, after TELL BOB TO GO NORTH, the command FOLLOW HIM will mean to follow Bob.
    The English parser now rejects the phrasing TELL first actor TO TELL second actor TO do something. (It formerly accepted this, but it didn't handle it as stated: in effect, the first TELL TO was ignored, so the command was treated as simply TELL second actor TO do something.)
    The English parser now accepts the commands SIT DOWN and LIE DOWN. The parser accepts these stated intransitively (i.e., without a direct object), but treats them as synonyms for SIT ON (something unspecified) and LIE ON (something unspecified), respectively. That is, the parser tries to find a suitable default object to sit on, and prompts the player for the missing object it no default can be assumed.
    In the English parser, when the command phrasing involves what looks like an indirect object phrase that doesn't match anything in the grammar, a "miscellaneous preposition phrase" production is matched. This type of production is now more consistent about reporting that "the story doesn't understand that command," to indicate that the structure of the verb phrase was unrecognized. In the past, if the the direct or indirect object phrases were themselves unresolvable, the parser showed that error instead; this was often less useful, because the root problem in these cases is really that the overall verb phrasing can't be parsed.
    The NOTE command now shows a warning if the transcript isn't currently being saved (with the SCRIPT command). This command's main purpose is to let the player embed a comment in the session transcript, as a means of sending feedback to the author. There's not a lot of point in using the command when the transcript isn't being saved, so there's a good chance that a player using the command thinks that a SCRIPT command is already in effect. The warning calls the player's attention to this. The warning is only issued the first time that NOTE is used without logging in effect, but this is reset each time logging is started and then stopped.
    In the past, real-time events created during pre-initialization weren't set up properly, because the real-time manager's internal clock wasn't initialized until run-time start-up. The clock now has a static initializer, ensuring that events created during preinit will have a valid time base.
    3.0.6q

    Released 5/9/2004

    Possible compatibility-breaking change: The standard parser dictionary has been renamed from G_dict to cmdDict (for "command dictionary"). The dictionary is simply an object, and the new name is more consistent with other parser object names (in particular, cmdTokenizer, the standard parser tokenizer).

    It's relatively uncommon for game code to refer to the main dictionary directly, but you should search your code for references to G_dict and change the name to cmdDict.

    Possible compatibility-breaking change: The methods that describe an actor's location have been changed, in order to improve consistency and to simplify the code structure. These changes are mostly internal to the library, so they're unlikely to affect existing code unless you've made some rather low-level customizations.

    The changes involve a lot of renaming and rerouting of internal method calls. The list of changes below is divided into groups of related changes.

    Group 1: The "nominal actor container" methods have been consolidated into a single method.

    • The methods getNominalStandingContainer, getNominalSittingContainer, and getNominalLyingContainer have been deleted. In their place is a single new method, getNominalActorContainer, which takes the posture as a parameter. Since it's almost never necessary to differentiate the nominal actor container by posture, the single method approach is more convenient, and it's also more readily extensible to new postures.

    Group 2: The Posture object has been taken out of the loop, as it were, for the methods that describe actors. The Posture object's role in the past was to break out these various methods into a separate version for each posture, and then further dispatch to the actor's nominal location: for example, the okayPostureChange method was split up into okayStand, okaySit, and okayLie.

    The original idea was that locations might want to customize extensively by posture, but in practice the multiplicity of methods turned out to be more trouble than it was worth. With the present change, each set of Stand/Sit/Lie variations is now consolidated back into a single method, which makes the Posture object's role unnecessary, allowing it to be removed from the dispatch chain. It's still used in generating the final message, but in most cases this is now a simple matter of getting the posture's participle ("sitting", etc) from the Posture object and using it to build the message string.

    The following list of changes will look daunting, but it's unlikely that existing game code will be much affected, as these methods were always meant mostly for internal use in the library.

    • Actor.actorHereDesc changes:
      • Posture.actorHereDesc and libMessages.actorHereDesc have been deleted. Instead, the actor calls directly to roomActorHereDesc on the nominal actor container.
      • The BasicLocation, Floor, and libMessages methods actorStandingHere, actorSittingHere, and actorLyingHere have been deleted, and replaced by the new method roomActorHereDesc.
      • The libMessages methods actorHereStandingOn, actorHereSittingOn, and actorHereLyingOn have been deleted, and replaced by the new method actorInRoom.
    • Actor.actorThereDesc changes:
      • BasicLocation, Floor, and libMessages provide definitions of the new method roomActorThereDesc.
      • NestedRoom.roomRemoteActorDesc has been renamed to roomActorThereDesc, and the implementation has been moved from NestedRoom to BasicLocation.
      • libMessages.nestedRoomRemoteActorDesc has been renamed to actorInRemoteNestedRoom, and libMessages.roomRemoteActorDesc has been renamed to actorInRemoteRoom.
    • Room status name changes - these affect the mechanism that generates room title addenda such as "(sitting in the chair)":
      • Posture.statusPosture has been deleted. Instead, the (newly renamed) Actor.actorRoomNameStatus now handles this directly.
      • Actor.statusPosture has been renamed to actorRoomNameStatus, to clarify and reflect the broader potential of its purpose.
      • The BasicLocation, Floor, and libMessages methods statusStanding, statusSitting, and statusLying have been deleted, and replaced by the new method roomActorStatus.
      • The libMessages methods statusStandingOn, statusSittingOn, and statusLyingOn have been deleted, and replaced by the new method actorInRoomStatus.
    • Actor posture description changes:
      • Posture.actorPostureDesc has been deleted. Actor now handles this directly.
      • The BasicLocation, Floor, and libMessages methods actorStandingDesc, actorSittingDesc, and actorLyingDesc have been deleted, and replaced with the new method roomActorPostureDesc.
      • The libMessages methods actorStandingOnDesc, actorSittingOnDesc, and actorLyingOnDesc have been deleted, and replaced by the new method roomActorPostureDesc.
    • Posture change acknowledgment changes - these affect the mechanism that generates the default responses to commands like SIT and STAND:
      • Posture.okayPostureChange has been deleted. Actor now handles this directly.
      • The BasicLocation and Floor methods okayStand, okaySit, and okayLie have been deleted, and replaced by the new method roomOkayPostureChange.
      • The libMessages methods okayStandMsg, okaySitMsg, and okayLieMsg have been replaced by the new method okayPostureChangeMsg.
      • The playerActionMessages and npcActionMessages methods okayStandOnObjMsg, okaySitOnObjMsg, and okayLieOnObjMsg have been deleted, and replaced by the new method roomOkayPostureChange.
    • Changes to the mechanism that mentions that an actor is sitting/lying/standing in a nested room, as part of the EXAMINE description of the nested room:
      • Posture.listActorPosture has been deleted. Actor.listActorPosture now calls the nominal actor container directly instead.
      • The BasicLocation and Floor methods listActorStanding, listActorSitting, and listActorLying have been deleted, and replaced by the new method roomListActorPosture.
      • The libMessages methods listActorStandingOn, listActorSittingOn, and listActorLyingOn have been deleted. Their role has been subsumed by actorInRoom.

    Group 3: Actor "grouping" is now handled in BasicLocation, rather than only in NestedRoom.

    • The implementation of listWithActorIn() that was formerly in BasicChair has been moved to NestedRoom. This means that actors without special descriptions are now grouped by default with other actors without special descriptions who are present in the same location and posture.
    • NestedActorGrouper has been renamed to RoomActorGrouper, since it's now used for any room with actors to be grouped, not just nested rooms.
    • In order to accommodate the wider range of cases where actor group lists are now generated by default, RoomActorGrouper now determines two additional bits of information: it gets the nominal actor container for the location containing the actors being listed, and it checks to see if the point-of-view actor is viewing the listing from a "remote" location (a separate top-level room connected by a sense connector). It uses this information to generate the group prefix and suffix messages differently than before:
      • If the nominal actor container is visible, the grouper calls the new methods actorInGroupPrefix() and actorInGroupSuffix() on the nominal actor container to generate the prefix and suffix messages. BasicLocation and Floor provide suitable default definitions of these new methods.
      • If the nominal actor container isn't visible, but the actors are in a remote location, the new libMessages methods actorThereGroupPrefix() and actorThereGroupSuffix() are used.
      • Otherwise, the new libMessages methods actorHereGroupPrefix() and actorHereGroupSuffix() are used.
    • Actor.actorListWith now returns an empty list if 'self' overrides specialDesc. This ensures that a specialDesc defined on the actor will take precedence over any list group applied by the location. (This is more important now that BasicLocation applies actor grouping by default.)
    • Actor.actorHereListWith has been renamed to actorListWith. This method is used whether the actor is local, distant, or remote, but the old name incorrectly suggested the existence of a separate actorThereListWith in parallel with the actorHereDesc/actorThereDesc pair.

    Group 4: Miscellaneous minor changes to the generation of remote room actor descriptions.

    • NestedRoom.roomRemoteActorDesc now generates a more elaborate message by default, describing not only the actor's immediate location, but its outermost visible location as well. If the actor's outermost visible location isn't the same as 'self', the method now invokes libMessages.nestedRoomRemoteActorDesc, which generates a message of this form: "Bob is in the dining room, sitting in the chair."
    • The method inRoomName that was formerly defined in Room is now defined in Thing instead. This ensures that a suitable default remote room name will be generated for nested rooms as well as top-level rooms. In addition, the method now simply returns actorInName by default, which ensures that the preposition used in the default remote room name will match the one customized via actorInPrep, if necessary.
    The new TravelConnector method connectorGetConnectorTo() gives a travel connector a chance to scan any "secondary" connectors it knows about for a connection to a given destination. In some cases, a travel connector encapsulates other travel connectors; these are secondary connectors in the sense that they're linked to the room only from the first connector, and thus the room can't find them on its own. This new method provides a way for a room to query a connector for any secondary connectors it knows about, in order to find a connector to a given destination. The base TravelConnector class provides a default implementation suitable for most subclasses, and AskConnector overrides the method to search its list of secondary connectors. Thing.getConnectorTo calls the new method when searching for a connector to a given destination.

    The immediate practical benefit of the new method is that it makes it possible for scripted NPC travel to traverse AskConnectors. In the past, an NPC encountering an AskConnector was typically unable to proceed, since the AskConnector didn't directly link to the destinations of its underlying connectors, and the underlying connectors typically weren't linked directly as directions to the enclosing room. This made it impossible for the scripted NPC to find a suitable connector, and thus blocked the scripted travel.

    The properties for choosing whether or not to list the contents of an object to list in EXAMINE and LOOK AROUND descriptions have been expanded slightly to provide more control and better consistency. These changes are all in the Thing class.

    The isListed property now returns, by default, the value of isListedInContents. The meanings of these properties haven't changed: isListedInContents controls whether or not the object is listed when examining the object's direct container, whereas isListedInContents controls listability for examining indirect containers, including the enclosing room. The reason for this change is that it's rare that we'd want an object to be unlisted when its direct container is examined, but still want it to be listed when enclosing containers are examined (whereas the reverse isn't entirely uncommon, since we sometimes want to mention an object only as a detail of its direct parent, but not of the broader room context).

    The new property contentsListedInExamine lets you control whether or not an object's direct contents are listed when the object is examined directly. In the past, you could do this only by overriding examineListContents, but the new property makes it easier to control this behavior. The new property returns the same thing that contentsListed used to return.

    The contentsListed property now returns the value of contentsListedInExamine by default. This is parallel to the change to isListed.

    The new method getListedContentsInExamine() returns the list of direct contents that should be listed when the object is examined. This new method is the direct-examination equivalent of the existing getListedContents() method, and returns the same value that getListedContents() formerly returned: the subset of direct contents that appear in the sense info table passed to the method.

    The getListedContents() method now simply returns the result of calling getListedContentsInExamine().

    The method examineListContentsWith() now calls getListedContentsInExamine() to obtain the list of direct contents to list when examining an object. In the past, this used the 'contents' property. The method also checks the value of contentsListedInExamine, and skips the entire listing if this property is nil.

    These changes are all designed to be compatible with existing code. They should simply add more flexibility and make certain behaviors easier to customize, but existing code shouldn't be affected.

    In 3.0.6p, the library started listing the visible, portable contents of "remote" rooms as part of each room description. The implementation had a problem when MultiLoc objects were involved, though: if a MultiLoc object appeared in more than one of the connected top-level rooms and had portable contents, those contents were listed several times - once for each connected top-level room in which the MultiLoc appeared.

    This problem has been corrected: the room description generator now ensures that a MultiLoc's contents appear only once. To do this, the room description code now excludes each object that is also located in the "local" top-level room or in any connected remote room whose contents listing has already been generated.

    NestedRoom now overrides roomRemoteActorDesc() to describe the actor as being in the nested room. In the past, if the nested room's location was visible, the nested room deferred to the location to provide the description. This produced misleading messages, because it described the actor as being in the outermost visible room containing the actor - which was technically true, but left out the important information about the intermediate nested room.
    Noise and Odor objects no longer include themselves in LISTEN TO ALL and SMELL ALL, respectively. These objects formerly did include themselves in ALL for these commands, since the commands are obviously highly applicable. However, because these objects are intangible, it usually doesn't make sense from the player's perspective to consider them in ALL, which a player usually thinks of as applying to the set of discrete, visible objects. In practice, including these in ALL was often problematic because these intangible objects are usually not given names or vocabulary; they usually exist only to model a feature of their source object, and aren't meant to look like separate objects to the player.

    Along the same lines, Vaporous objects are now included in EXAMINE ALL, SMELL ALL, and LISTEN TO ALL. These objects do have a visible presence, so it makes sense to include them in commands that examine everything in sight with any sense.

    For particular Noise and Odor objects that you do want to participate in LISTEN TO ALL and SMELL ALL, just override hideFromAll() to return true for the desired action or actions.

    NonPortable now overrides the inherited Thing verify() handling for EXAMINE to make an un-held NonPortable as likely as a held Thing for disambiguation purposes.

    Recall that, by default, Thing downgrades the disambiguation likelihood that EXAMINE refers to an object not being held. The reasoning is that an object being held is physically closer to the actor, so in a real-world situation, it would be more convenient to closely inspect an object being held than one sitting nearby. Since NonPortable objects can't be held, though, this reasoning breaks down somewhat. The change removes the disambiguation disadvantage for NonPortable objects.

    The objHeld precondition uses a new Thing method, meetsObjHeld(actor), to determine if the object meets the condition. This new method is called instead of isHeldBy(actor) on the given object. This change allows an object to indicate that it's not actually being held by an actor, but that it's as good as held for the purposes of the objHeld condition. It's rare for meetsObjHeld() to return anything other than isHeldBy(), but does happen occasionally. The most obvious case is body parts: these can't be considered to be held in the actor's hands (unless the actor is specifically doing so for some reason), but at the same time, there's no reason the actor should have to hold a body part in order to carry out actions with objHeld conditions.
    The Wearable class now handles SHOW TO specially: if the object is being worn by the actor doing the showing, it doesn't require that it be held. In the past, the objHeld condition was applied, as it is by default to any portable object, which meant that the character had to take off a wearable in order to show it to someone; this doesn't usually make a lot of sense, thus the change.
    In Thing, remoteSpecialDesc(pov) now calls distantSpecialDesc by default; distantSpecialDesc in turn calls specialDesc by default. In the past, remoteSpecialDesc called specialDesc directly by default. This change leaves the default behavior the same as before, but it simplifies the common case where the "distant" and "remote" descriptions are the same by letting a single override - distantSpecialDesc - handle both descriptions.

    Similarly, remoteInitSpecialDesc(pov) now calls distantInitSpecialDesc by default (rather than initSpecialDesc, as it did in the past).

    Room now treats the GetOutOf action as equivalent to Out. (This means that if the room has vocabulary, the player can now say "get out of (room)" as a synonym for "out".)
    The English verb grammar now accepts LEAVE as a synonym for EXIT.
    The English parser now accepts "A" and "T" as abbreviations for ASK and TELL, respectively, in the full phrasing with ABOUT. That is, you can now say, for example, A BOB ABOUT BOOK. In the past, the A and T abbreviations could only be used in the super-short forms of the commands, where only the topic could be specified (as in T BOOK).
    The English parser now accepts the formats ASK actor TO command and TELL actor TO command as equivalent to the traditional actor, command format for telling an actor to do something. The new ASK TO and TELL TO formats are simply syntactic variations; they're otherwise handled exactly the same way as the traditional actor, command format.
    In the English library, SpecialTopic now allows a variation on the input format to accommodate players who are thinking in terms of ASK/TELL. Now, when a SpecialTopic is active, and the player's input doesn't match the special topic's input pattern, and the user's input starts with "A" or "T" as a separate word, the SpecialTopic checks for a match to the rest of the player's input (i.e., the part following the "A" or "T"). For example, if the special topic is "apologize for forgetting the anniversary," and the player types T ANNIVERSARY, the special topic will try dropping the "T" and matching just the "ANNIVERSARY" part.

    This change is designed to accommodate players who are accustomed to the standard ASK/TELL format, and who might not realize that the special topic list shows commands meant to be entered literally, or who are just so in the habit of using ASK/TELL commands that it doesn't occur to them to leave out the "A" or "T" part. Since an "A" or "T" command pretty clearly indicates that the player's intent is to communicate with the NPC, it makes sense to check the topic the player is trying against any active special topics.

    Note that the special topic parser only accepts the super-abbreviated formats - the "A" and "T" commands. This is because it seems much less likely that it would even occur to a player to try a special command with the full ASK ABOUT or TELL ABOUT phrasing. Once the command is expanded to the full phrasing, the detailed syntax of the special command suggestions should make it fairly obvious to the player that the ASK ABOUT or TELL ABOUT part isn't needed. In contrast, since the abbreviated "A" and "T" formats are ungrammatical to start with, it would seem perfectly natural to use them with arbitrary special command suggestions.

    The RoomPart class now downgrades its logicalness for Examine commands, in the same way that Decoration does. Room parts are generally so much background noise, included only in case a player explicitly refers to them. Downgrading the logicalness helps prevent the room parts from causing nuisance disambiguation questions.
    Due to a parser bug, the phrase ALL IN container (which can also be written ALL FROM, ALL THAT'S IN, etc) incorrectly ignored the hideFromAll() status of the objects in the container when determining which objects to match to the phrase. This has been corrected; the phrase now respects the hideFromAll() settings of the objects involved.
    The English parser now matches words it has parsed as "literal adjectives" to ordinary adjectives in the dictionary when resolving a phrase in "global scope." This change is the literal-adjective equivalent of the similar changes involving noun phrases in global scope introduced in 3.0.6p.
    Noun phrases that consist entirely of a "literal adjective" word are now marked as "adjective-ending" phrases for the purposes of ranking grammatical pattern matches during parsing. (The omission of this flagging in the past was an oversight.)
    Due to a bug, the removeEvent() method of the RealTimeEvent class (the common base class for real-time fuses and daemons) didn't work properly. This has been corrected.
    A bug in the Lister class caused in-line contents listings to use the "long list" format (i.e., with semicolons as the separator between items) if the enclosing list did. An in-line contents list is set off by parentheses, so it stands as an independent list and thus should a long-list format only if it would need to on its own, irrespective of the needs of the enclosing main list. (At least, the parentheses are used in the English library; other languages might use different notation, but in any case should use a notation that makes the contents list similarly independent of the enclosing list.)
    A couple of minor changes to the default LOOK IN handling make it a little easier to customize the behavior, and reduce the need to do so.

    First, Thing has a new property, lookInDesc, that gives a message that's displayed by default in response to LOOK IN and SEARCH. This shows the standard default ("there's nothing unusual in it") if you don't provide a custom message. It happens frequently that a game wants to depict an object as having some interior features, but doesn't want to actually model those interior features as separate objects. The new lookInDesc makes this more convenient, since you only need to provide the custom message in this property, rather than overriding the entire dobjFor(LookIn) handling, as was necessary in the past.

    Second, Decoration now treats LOOK IN as logical by default (in the past, this was handled by the catch-all routine that makes most actions respond that the object is "not important"), and applies the same logical-rank downgrade applied to EXAMINE. Correspondingly, the default lookInDesc for Decoration displays the object's "not important" message. This means that Decoration objects act by default as they did before, displaying "that isn't important" in response to LOOK IN, but can be given a custom LOOK IN response simply by setting lookInDesc, just as with any Thing.

    Third, the English phrasing for finding nothing in an object specifically searched with LOOK IN or SEARCH has changed slightly, to report that there's nothing "unusual" in the object. In many cases, an object is described as having what would in the real world be interior features, but those interior features aren't modeled as actual game objects and hence don't show up by default in response to LOOK IN or SEARCH. The slight change in phrasing helps make the response at least technically defensible. It's probably still better in most cases to customize the LOOK IN response anyway, but at least the default response is a little less jarring in these cases.

    Decoration now treats READ as logical by default, the same as it does EXAMINE. (As mentioned above, LOOK IN now has the same treatment as well.)
    In the Thing class, PUT X IN X and PUT X ON X now respond with custom messages ("you can't put the X in/on itself"). In the past, the catch-all message indicating that X isn't a container/surface was used, which is a less conspicuous problem than attempting to put something in or on itself.
    In the English library, the plural-to-noun match expansion introduced in version 3.0.6p is now limited to "global scope," just as the noun-ending-to-adjective-ending match expansion already was. English plurals are usually formed by adding "s" or "es" to the end of a word, so when a noun is six letters or longer, it usually looks like a truncated form of its plural; this interacted with the plural-to-noun match expansion to be overly aggressive in upgrading phrases to a plural interpretation. The match expansion is only really important in global scope anyway (for the reasons why, see the explanation of the noun-to-adjective expansion), so this change maintains the primary benefits of the match expansion while avoiding the weird cases that can come up in local scope.
    The parser now considers an indefinite noun phrase to be less desirable, for grammar selection purposes, than a definite interpretation of the same phrase. Object names can occasionally look like indefinite phrases: an elevator button labeled "1" could be called a "one button," for example, and a subway might have an "A train." When there's an interpretation that exactly matches the phrase to the name of an in-scope object, the parser will now prefer that interpretation over one that treats a word like "a" or "one" as signifying that an arbitrary match to the other words should be chosen.
    A library bug that caused a run-time error in Actor.findVisualObstructor() has been corrected.
    The library now executes "prompt daemons" (the special type of daemon that runs before each command prompt, rather than when the turn counter changes) before every prompt, rather than just before a main command prompt. This ensures that various miscellaneous tasks are performed whenever the game pauses for user input, even when input is requested in the course of processing a command.
    3.0.6p

    Released 4/25/2004

    Compatibility-breaking change: The property initDesc, and the related properties, have been renamed. Games that define or use these properties will have to change to use the new names. You should search all of your existing source for the old names, and replace each one with the corresponding new name.

    Important: you must search for "initDesc" and replace it with "initSpecialDesc" first. This is required because the former "initExamineDesc" has been renamed to "initDesc" - so you have to make sure you change all the old "initDesc" instances before you rename "initSpecialDesc" to create new "initDesc" instances. Please do your search-and-replace operations in the order shown below:

    • change "initDesc" to "initSpecialDesc"
    • change "useInitDesc" to "useInitSpecialDesc"
    • change "obscuredInitDesc" to "obscuredInitSpecialDesc"
    • change "distantInitDesc" to "distantInitSpecialDesc"
    • change "initExamineDesc" to "initDesc"
    • change "useInitExamineDesc" to "useInitDesc"

    The purpose of this change is to make the naming more consistent. The old usage - "initDesc" for a special description and "initExamineDesc" for the EXAMINE description - was inconsistent with the respective non-initial properties, "specialDesc" and "desc". The qualification was reversed: "specialDesc" was qualified as special, leaving the EXAMINE description unqualified as simply "desc", while the initial descriptions qualified the EXAMINE desription rather than the special one.

    Minor compatibility-breaking change: The Attachable class's scheme for explaining why the object can't be attached to a given other object has been changed. In the past, the method cannotAttachMsg(obj) returned a message string or actor-action-messages property, but this created a conflict with another message property of the same name, which is used by Thing. To resolve the conflict, the Attachable method has now been renamed to explainCannotAttachTo(obj), and rather than returning a message to display, the new method simply displays the message itself.

    If you have any Attachable objects that define cannotAttachMsg, you must make two changes to each. First, rename cannotAttachMsg to explainCannotAttachTo. Second, rather than returning a string, display the string directly.

    Possible compatibility-breaking change: The conversation mechanism has some internal changes to support deferral across hierarchy levels (see below).
    • In TopicDatabase.findTopicResponse(), the former first parameter, 'topicList', has been dropped, and two new parameters have been added: 'convType', giving the conversation action type; and 'path', giving a list of the inferior topic databases that will be searched for the topic if it's not found in the current database (i.e., 'self').
    • In TopicDatabase.handleTopic(), the former first parameter, 'topicList', has been dropped; and two new parameters have been added: 'convType' and 'path'. These have the same meanings as above.
    • In ConvNode.handleConversation(), the new parameter 'path' has been added. This only affects the ConvNode instance of this method - the method of the same name in Actor and ActorState is unchanged.

    Note that the TopicDatabase methods findTopicResponse() and handleTopic() no longer need the topic list parameter because this information can be obtained from the new convType parameter.

    These methods are mostly for internal use by the library, so the likelihood that your existing game code will be affected is small. Even so, you should scan through your code for these methods and make the appropriate adjustments.

    Important: note that there are two related but independent methods called handleTopic(). There's the TopicDatabase method, which is affected by this change, and there's the separate TopicEntry method, which is not affected. When you're scanning your code, you only need to change the TopicDatabase version. It's easy to tell which is which at a glance: the TopicDatabase version takes three parameters, while the TopicEntry version only takes two. When you're scanning your code, only change the three-parameter instances.

    Additional technical notes: you'll only need to read this part if you're making some rather advanced overrides to the library. Please skip this part unless you find something that you're not sure how to handle in the search-and-replace step above.

    If your code contains calls to any of the affected methods, and you're wondering what to pass for the convType parameter, you should first check to see if you're receiving that value as a parameter to the calling code; if so, just pass that parameter down. This is usually easy to spot at a glance. If you have code like this:

       handleTopic(self.(convType.topicListProp), actor, topic);
    

    then you can simply change it to this:

       handleTopic(actor, topic, convType, nil);
    

    The important thing to note here is that you're already using the convType parameter to look up the topic list in the old-style call; you can simply drop the topic list lookup and pass the convType parameter itself instead (but note that it moves to the last position).

    If you don't have a convType parameter passed in from your caller, you'll need to choose which one to use. Look at which topic list you're passing, and think about what conversational action is triggering your code. Then scan through the list of xxxConvType objects (askAboutConvType, tellAboutConvType, yesConvType, noConvType, etc) defined in actor.t, and choose one that (1) has the same topic list property that you're already using, defined in its topicListProp property, and (2) conceptually matches the action you're performing. If you can't find anything appropriate, you might simply need to define your own ConvType object for whatever extended action you're defining; just create one following the same pattern as the existing ones in actor.t.

    You can usually just pass nil for the new fourth parameter ('path') to handleTopic(), findTopicResponse(), and ConvNode.handleConversation(). You only have to pass a non-nil value when you're implementing a search through a hierarchy of topic databases, in which case you'll have to pass a list of the inferior databases in the hierarchy. The purpose of the new parameter is to allow a higher-level database to defer to a match in a lower-level database, if one exists, so at each level you must pass the remainder of the "search path" of databases.

    The above changes to the conversation methods are designed to facilitate a new mechanism that allows a conversation TopicEntry in one topic database to defer to a TopicEntry from an inferior database. This is done through the new TopicEntry method deferToEntry(entry): this method returns true if 'self' should defer to 'entry', nil if not. 'entry' is always a non-nil TopicEntry object from an inferior topic database.

    Recall that topic entry databases for conversation are arranged into a three-level hierarchy for each actor: at the top is the ConvNode's list of topics, at the middle level is the ActorState list, and at the bottom level is the Actor list. We find a matching topic entry by scanning each level of this hierarchy in turn, and taking the first match we find. So, if there's a ConvNode match for a topic we're looking for, we use it, even if there's a match for the same topic in the ActorState or Actor. The matchScore is irrelevant: the three-level hierarchy always trumps the matchScore ranking. The matchScore ranking is only used to choose among different matching entries at the same hierarchy level.

    The new deferToEntry() mechanism allows topic entries to defer handling across hierarchy levels. Here's how the new scheme works: before we start searching the hierarchy, we start by noting the matching entry at each level. Then, we search each level from the top down as before. At each level, though, we look at the winning match, and ask it via deferToEntry() if it wants to defer to the winning match from the next level down the hierarchy. If so, then we ignore the match at that level. This means that we skip the higher-level match and go straight to the lower-level handling.

    The main purpose of this addition is to allow a DefaultTopic entry to be a catch-all only for topics that aren't handled explicitly at a lower hierarchy level. For example, to create a DefaultTopic that defers to any non-default match at a lower hierarchy level, add this to the DefaultTopic object:

       deferToEntry(other) { return !other.ofKind(DefaultTopic); }
    

    This tells the topic finder that if there's any match that's not itself a DefaultTopic at a lower hierarchy level, then this default should be ignored.

    The English library template for Room has been extended, and the default relationships that derive one type of Room naming property from another have changed slightly. These changes should be fully compatible with existing game code, and should make it easier to customize the various kinds of room names.

    The template for Room now takes the first string to be the 'roomName' property, the second to be 'destName', and the third to be the 'name' property. The 'desc' property is still in the last position.

      Room template 'roomName' 'destName'? 'name'? "desc"?;
    

    In the past, the 'name' was the first entry, and 'destName' was the second. The library took the 'roomName' to be the same as the 'name'. However, it makes more sense to do this the other way around: the 'roomName' is now the one that's explicitly defined by the game, and the 'name' setting is optional. If the 'name' setting is omitted, it's derived by default by converting the 'roomName' to lower case.

    The 'roomName' is what's shown as the title of the room, in the room description and on the status line. This is usually given in "title case," meaning that each word has an initial capital letter, with the exception of minor function words: "Hall of the Ancient Kings". This is the format that games have defined all along, but in the past, they defined it using the 'name' property. The change improves matters, because it allows the room's ordinary 'name' property to be entered separately, in a format suitable for use when the name is needed in library messages: "You can't have the ice cave."

    The default 'name' derivation - converting the 'roomName' string to lower case - gives decent results a lot of the time. However, in many cases, you'll want to customize the ordinary name separately. The template makes this easy by adding a slot for the 'name' property separately from the 'roomName'.

    There's one additional change to the default derivations. In the past, the 'destName' didn't have a default at all. Now, the default is the room's 'theName', which is itself derived, by default, by prepending the ordinary name with 'the'. As before, the template lets you supply a separate, custom 'destName' value.

    The MultiLoc object now transmits sense information to and from its contents in a more consistent fashion. In the past, putting things inside a MultiLoc had somewhat inconsistent results; the behavior should be more predictable now.

    The primary design principle of a MultiLoc hasn't changed: a MultiLoc is a single object that appears in multiple locations, but which doesn't provide any sense connection among the multiple containers. A MultiLoc is a single object in the sense that the same object appears, in its physical entirety, in each of its containers.

    In the past, a MultiLoc was essentially a "sense ceiling": sense information propagated from outside the MultiLoc to its interior, but didn't propagate from within the MultiLoc to its containers. This was intended to prevent the MultiLoc from propagating sense information among its containers, but it was too restrictive an implementation. For example, if a MultiLoc contained a flashlight, the flashlight's light didn't shine into the MultiLoc's locations. Similarly, if an NPC was inside a MultiLoc, the NPC couldn't see or hear anything from any of the MultiLoc's locations, and hence couldn't take part in a conversation with the PC while the PC was outside the MultiLoc.

    The change is that a MultiLoc is no longer a sense ceiling; it's now more like a wall. Now, from a point of view inside the MultiLoc, all of the MultiLoc's locations (and their contents, to the extent that's appropriate) are visible. From the point of view of any of the MultiLoc's containers, the MultiLoc and its contents (as appropriate) are visible, but none of the MultiLoc's other containers are visible. Light is similarly transmitted from inside the MultiLoc to all of its containers, but not from one container to another. So, for example, if a flashlight is inside a MultiLoc, it provides light to every container of the MultiLoc; but if a flashlight is in one of a MultiLoc's containers, the light doesn't make it through the MultiLoc to its other containers.

    The main effect of this change is that things located inside a MultiLoc now behave the way one would expect them to. In the past, the asymmetrical "sense ceiling" design led to some odd behavior when putting objects inside a MultiLoc: an actor within a MultiLoc couldn't carry on a conversation with someone outside, for example, because the actor inside couldn't hear out beyond the MultiLoc. With the change, MultiLoc should behave much more intuitively.

    The ContainerDoor class now defines the isOpen method to return the same thing as its associated container's isOpen method; and it now defines examineStatus to display its associated container's open/closed status.
    A ComplexContainer can now be used as a bag of holding. Operations involving the bag-of-holding features of a complex container are simply passed redirected to the complex container's subContainer object.
    Certain object contents listings were grammatically incorrect when showing a single item that had a plural name. For example, if an item had the name "some coins," and it was the only item on a table, the table description said "On the table is some coins."

    The listers now take into account the "cardinality" of each item listed, to ensure grammatical agreement in these cases. For English, there are only two degrees of cardinality that matter: one, and many. The English module therefore assigns a grammatical cardinality of 2 to items whose isPlural property is set to true, 1 for all others; this is done with the new Thing method listCardinality(lister). A lister calls this new method to determine the cardinality of the list, so that it can provide the correct information to the methods that generate the messages.

    The LISTEN TO command now lists not only the sound the object being examined is making, but the audible sounds of its contents as well. This ensures that sounds made by components of an object are properly displayed when the enclosing object is examined, and also ensures that a container that has a sound source inside will have its sound listed.

    The SMELL command has been similarly enhanced.

    As part of this change, the thingListenDesc and thingSmellDesc messages have been moved from libMessages to playerActionMessages, and renamed thingListenDescMsg and thingSmellDescMsg, respectively. These messages are now shown as default description messages, necessitating the move to playerActionMessages.

    The Distant class now explicitly allows LISTEN TO, the same way it allows EXAMINE.
    It's now possible to create a SenseConnector that occludes some objects from view, depending on the point of view. This is useful for situations where a sense connector provides only a partial view of another room. For example, consider two rooms with a window between them, providing a view of one room from the other. Now, suppose a bookcase is positioned with its back to the window. When viewing the room with the bookcase from the other side of the window, it's not possible to see the contents of the bookcase.

    The new class, OccludingConnector, is a mix-in that you can combine with SenseConnector to create a partially occluded sense path. Put OccludingConnector before SenseConnector in the class list. You specify which objects to occlude from view by defining the method occludeObj(obj, sense, pov). 'obj' is an object to test for occlusion; 'sense' is the sense being calculated; and 'pov' is the point of view, which is usually the actor performing the command. Your method must return true if the connector occludes the object, nil if not.

    To implement the window in our example above, we'd do something like this:

      OccludingConnector, SenseConnector, Fixture 'window' 'window'
        "It's a small window. "
        connectorMaterial = glass
        locationList = [roomA, roomB]
        occludeObj(obj, sense, pov)
        {
          /* from roomA, we can't see what's in the bookcase in roomB */
          return (pov.isIn(roomA) && obj.isIn(bookcase));
        }
      ;
    

    OccludingConnector uses a new mechanism that can be used for more extensive customization of the sense path calculation than OccludingConnector itself enables. During each sense path calculation, the library first builds a table of all of the objects connected by containment to the point of view. (This table contains all of the objects that can possibly be sensed, because sense paths are based entirely on containment relationships.) The library then calls clearSenseInfo() on each item to initialize it for the path calculation.

    In clearSenseInfo(), an object can register itself for an additional notification at the end of the sense calculation. To register for the notification, override clearSenseInfo(), inherit the default code, and append 'self' to the notification vector:

      clearSenseInfo()
      {
        inherited();
        senseTmp.notifyList.append(self);
      }
    

    Now that the object is registered, it will be notified at the end of the calculation, after the sense path to each object is fully calculated, but before the final table of sense paths is constructed. The notification method is called finishSensePath(objs, sense). 'objs' is a LookupTable whose keys are the objects connected by containment to the point of view; 'sense' is the Sense object of the calculation.

    You can do anything you want to the sense table in finishSensePath(). The sense path information for each object is stored in the object's tmpXxx_ properties - tmpTrans_, tmpAmbient_, tmpObstructor_, etc. To change an object's sense path, simply change those properties. (OccludingConnector simply sets each occluded object's path transparency, in tmpTrans_ and tmpTransWithin_, to nil to indicate that the object cannot be seen.) You can make additional objects visible by adding them to the lookup table and setting their tmpXxx_ properties to the settings you want them to have.

    Note that the notification step is required for performance reasons. The library could call the notification method on every object, which would simplify things by eliminating the need for the extra line of code to register. However, very few objects are likely to require this extra notification, and since sense path calculations are performed very frequently, the library doesn't want to call the notification method blindly on all objects. So, to avoid adding the notification overhead to every object, the library requires the rare objects that require the notification to register themselves.

    The logic that decides on the descriptions generated for distant and obscured objects has changed slightly. The purpose of these changes is to make it easier to customize the various ways an object is described, and to make the selection of description more intuitive.

    First, a new type of description has been added: the "remote" description. There are three new remote description methods: remoteDesc, for the EXAMINE description; remoteSpecialDesc, for the special description; and remoteInitSpecialDesc, for the remote initial special description. These new methods are used to provide the examination and special descriptions when the object is viewed from a separate top-level location. For example, if two top-level rooms are connected by a window, so that an actor in one room can see objects in the other room through the window, the remote description is used to describe objects in the other room from the actor's perspective.

    Second, when choosing a special description (as part of a room or contents listing), the logic is now as follows:

    • If the object is in a separate top-level location from the actor doing the looking, the remoteSpecialDesc is shown;
    • otherwise, if the object is obscured, the obscuredSpecialDesc is shown;
    • otherwise, if the object is distant, the distantSpecialDesc is shown;
    • otherwise, the specialDesc is shown.

    In the past, the sightSize was taken into account in selecting the type of special description to show: the specialDesc was shown if the object had a large visual size, regardless of the transparency of the sense path. Now, the sightSize isn't considered at all. The reason for the change is that the distantDesc and obscuredDesc are almost always custom messages per object, so they will naturally take into account the visual size of the object; considering the visual size added an unnecessary extra dimension.

    Note how the new remoteSpecialDesc fits into the selection rules: the remote special description takes precedence over the obscured and distant descriptions. That is, if the object being described is in a separate top-level room from the actor doing the looking, the remoteSpecialDesc is used, even if the object is also visually obscured or at a distance.

    Third, when choosing an examine description (when the object is explicitly examined), the logic is now as follows:

    • If the object is in a separate top-level room from the actor doing the looking, and a remoteDesc method is defined for the object, the remoteDesc method is used;
    • otherwise, if the object is obscured, and an obscuredDesc method is defined for the object, the obscuredDesc method is used;
    • otherwise, if the object is distant, and a distantDesc method is defined for the object, the distantDesc method is used;
    • otherwise, if the object's details are visible (which will be true even for a distant or obscured object with sightSize set to large), the ordinary desc is used;
    • otherwise, if the object is obscured, the defaultObscuredDesc method will be used;
    • otherwise, if the object is distant, the defaultDistantDesc method will be used.

      In the past, the 'desc' was used any time the visual details were visible, even if the object was distant or obscured and even if the object had a custom distantDesc or obscuredDesc. The change is that the distantDesc or obscuredDesc will essentially "override" the ordinary 'desc' when custom definitions are provided, regardless of the visual size of the object. The reason for this change is to keep the EXAMINE description selection rules consistent with the special description rules; the rules are essentially parallel in the two cases. The EXAMINE selection rules are slightly more complicated because of the need to generate suitable defaults when a custom obscured/distant description is not defined: by considering first whether or not a custom description is available, we can decide to show the custom distant/obscured description (if one is available), the default ordinary description (if no custom description is available and the details are visible), or a default distant/obscured description (in any other case).

      Note that the defaultObscuredDesc and defaultDistantDesc methods are new. The default implementations simply display the same default library messages that the default obscuredDesc and distantDesc implementations displayed in past versions.

    The logic for deciding whether or not to use the initial special description and initial examine description for an object has been refactored slightly. The refactoring yields the same default effects as before, so existing game code won't be affected, but the changes provide better control for overriding the default behavior.

    First, the new Thing method isInInitState determines whether or not to use the initial special and examine descriptions. By default, this method returns true if the object has never been moved, nil if the object has been moved. That is, the object is considered to be in its initial state, for the purposes of descriptions, until the first time the object is moved by an actor in the game.

    Second, the new Thing method useInitExamineDesc() indicates whether or not the initExamineDesc should be used when examining the object. By default, this returns true if the object is in its initial state, as indicated by isInInitState, and the object defines a non-nil initExamineDesc. Since isInInitState by default returns true if the object has never been moved, and nil otherwise, the default behavior is the same as before, so existing games will not be affected by this change.

    Third, the existing Thing method useInitDesc() looks at isInInitState to make its determination. Since isInInitState by default returns true if the object has never been moved, this has the same effect as in the past.

    These changes make it easier to change the "never moved" logic that determines whether or not to use initial descriptions, and also allows you to control the use of the initial special and examine descriptions independently of one another. For example, if you want the initial examine description to be used one time only, rather than to be based on whether the object has been moved, you could override useInitExamineDesc() to base its determination on 'described' rather than on 'moved'.

    In the English library, Room has a new method that provides a prepositional phrase that can be used to describe objects as being in the room. The new method is inRoomName(pov), and it returns a string that can be used to construct a sentence describing objects as being in the room. 'pov' is the point of view; this might not be in the same top-level room, as we could be viewing objects in this room from the point of view of a separate room that's connected to our room by a window or another sense connector.
    Room descriptions now include listings of the portable objects visible in separate top-level locations. In the past, only the contents of the point-of-view actor's own enclosing room were described; now, the contents of each connected top-level room are described as well.

    To generate the new remote room contents listings, the room describer first determines the set of top-level rooms with items that are visible from the point of view of the actor doing the looking. The room describer shows the contents of the local room (the room containing the point of view) first, using the same procedure it always has. Next, the describer runs through the set of other top-level rooms with visible contents; for each one, it generates a separate remote contents listing.

    Each remote room's contents listing is displayed using essentially the same procedure used to generate the local room's contents listing. However, instead of using the lister defined by the local room's roomContentsLister property (which is set to the roomLister object by default), each remote listing is generated using a lister returned by the new method remoteRoomContentsLister(other). This method is called on the point-of-view room, and 'other' is the remote room whose contents are to be listed. By default, this returns a lister that uses the remote room's inRoomName to generate the description.

    You can customize the listing of portable items visible in remote locations by returning a custom lister from remoteRoomContentsLister(). Note that the new class CustomRoomLister makes this easy, if all you want to do is to customize the prefix and suffix messages displayed in the listing:

      remoteRoomContentsLister(other)
      {
        return new CustomRoomLister('Through the window, {you/he} see{s}', '.');
      }
    

    Note that remoteRoomContentsLister(other) is called on the local room - the room containing the actor doing the looking - and 'other' is the remote room. This arrangement lets you customize the listing in the point-of-view room, which makes sense in that the overall description is of the point-of-view room.

    When a room is described (as when entering the room, or examining it with a LOOK AROUND command), everything visible from the room is marked as having been seen. In the past, only visible objects that were located within the actor's top-level enclosing location were marked as visible; now, everything that's visible is marked as seen, including objects in other top-level rooms that are visible from the actor's location.
    The library has an enhanced way of listing an actor's presence in a room (as part of the room description) when the actor is visible in a remote location (i.e., a separate top-level room) or at a distance. In the past, there was no distinction between the normal case and the distant and remote cases. Now, when the actor is in a remote room, or at a distance, a different path is used to generate the message.

    To accomplish this change, Actor now overrides remoteSpecialDesc and distantSpecialDesc. On LOOK AROUND, the room calls the appropriate one of these to describe the actor when the actor is at a distance. (The normal selection procedure is used: the remoteSpecialDesc is used if the NPC is in a separate top-level location from the point-of-view actor, otherwise distantSpecialDesc is used if the NPC is at a distance.) The new Actor implementations of remoteSpecialDesc and distantSpecialDesc invoke the corresponding new ActorState methods of the same names. These new ActorState methods in turn call back to the new Actor method actorThereDesc (both the distant and remote methods call this one new method). The new actorThereDesc calls the new method roomRemoteActorDesc on the actor's location. Thing provides a default implementation of this new methods, which simply calls the same thing on its the location if there's a location that's visible from the point of view, or shows a default library message if not. The default library message method is also called roomRemoteActorDesc. This uses the location's inRoomName to generate the description.

    This sequence of calls lets you hook in and customize the actor's distant special description at whatever level is most convenient - at the actor itself, at the ActorState, or at the containing room. In most cases, it will probably be most convenient to customize this in the room, to display a message customized to the appearance of the room from a distance. To do this, you'd put a method like this on the room:

      roomRemoteActorDesc(actor)
      {
        "\^<<actor.nameIs>> over at the north end of the courtyard. ";
      }
    

    Note that the actor to describe is provided as a parameter, and that the method is responsible for displaying the complete message describing the actor's presence.

    In the past, an EXAMINE command applied to a room was always treated as a LOOK AROUND command; this didn't produce good results when the room being examined was a separate room that the actor doing the looking wasn't in (i.e., a room connected to the actor's location via a sense connector of some kind). This no longer happens; when the actor doing the looking isn't inside the room, the standard EXAMINE handling is used, as though the room were any ordinary object.
    Thing.lookAroundPov() now actually sets a global point-of-view object. This ensures that the POV information can be obtained (via the getPOV() function) when room descriptions are being shown.
    Thing.addToSenseInfoTable() no longer includes the 'src' parameter. Instead, the same information can be obtained from senseTmp.pointOfView. (The parameter has been removed for effiency: the information is rarely needed, and is available from this other source when it is needed.)
    The Thing method canDetailsBeSensed() now takes an addition parameter giving the point of view. This is usually the actor performing a LOOK AROUND, EXAMINE, or other command that's causing a description of the object to be generated.
    Thing.lookAroundWithinSense now marks each item with a "presence" in the sense as known to the actor who's doing the looking. This ensures that any noises and odors that are actively listed become known to the actor as soon as they're listed. Objects are marked as known rather than seen, because it might be possible to hear or smell objects that can't be seen. Note that only objects with a "presence" in the sense are marked as known; an object with a presence in a sense is one that is actively calling attention to itself in the sense, via a noise or odor message in the room description. Thus, an object that isn't actually making any noise worth listing doesn't become known just because an actor could hear it if it were making noise; it has to actually be making some noise (and say so by setting its soundPresence to true) to become known in this manner. Note also that a noise and its source are frequently separate objects (likewise with odors), and it's the noise that becomes known in this arrangement, not the source. This is important because it's often possible to hear the sound a thing makes without knowing what's making the noise: we can hear a beeping coming from inside a closed box, so we now know about the beeping, but we can't tell that it's coming from an alarm clock, so the alarm clock remains unknown to us.
    By default, Thing now applies the objHeld precondition to a Show action; this means that SHOW X TO Y requires X to be held if X is an ordinary Thing. In addition, NonPortable only requires that the object be visible. This change assumes that portable objects need to be held up for someone else to look at, as though offering the object to the other person, while non-portable objects can be merely pointed out but left in place.
    The Vehicle class now treats an OUT command while an actor is inside the vehicle as meaning to drive or ride the vehicle out of the vehicle's enclosing location. In the past, OUT in a vehicle meant the same thing as in a nested room: GET OUT of the vehicle. The old behavior didn't seem to match most players' expectations, and it had the drawback that it left no standard way of expressing the intention of driving/riding the vehicle out of its location.

    For certain vehicles, it might still be desirable for OUT to mean GET OUT. Fortunately, it's easy to override the default behavior on a case-by-case basis - just override a vehicle's 'out' property to return nestedRoomOut:

       car: Vehicle
         out = nestedRoomOut
    
         // ...other properties...
       ;
    
    In the past, RoomConnector and OneWayRoomConnector didn't apply the proper preconditions to travel; in particular, they didn't properly determine the required initial location for the travel, so they didn't move an actor out of a nested room before attempting the travel. This has been corrected; RoomConnector now uses the travel preconditions formerly defined in RoomAutoConnector, so all RoomConnector subclasses (including OneWayRoomConnector and RoomAutoConnector) now use the correct preconditions.
    Enterable and Exitable objects now add a "touchable" pre-condition to travel through the objects. In the past, only the pre-conditions for the underlying connector were applied; since some types of travel connectors are not themselves physical objects, this occasionally led to odd results. In particular, if an Enterable or Exitable was visible but not touchable (for example, if the object was at a distance, or separated from the actor by a window), it was possible to enter it despite its being unreachable. This type of anomaly should no longer occur.
    The departure and arrival messages for NPC's now yield more pleasing results in cases where an NPC arrives or departs from a visible "remote" location - that is, a location not part of the PC's outermost room, but connected to the PC's location by a sense connector (such as a window between two rooms, or a DistanceConnector). There are two cases where the departure and arrival messages are enhanced.

    First, when an NPC departs from or arrives at a remote location, and the other end of the NPC's travel is out of sight, the library adds the name of the remote location to the arrival or departure message. For example: "Bob leaves to the east from the alley," or "Bob comes down the stairs to the north end of the courtyard." The name of the location is taken from the destName property of the NPC's location. The location name is only shown when the location is remote; when an NPC enters or leaves the PC's outermost room, the traditional messages ("Bob leaves to the east," etc.) are shown. The addition of the location name to the message helps clarify that the NPC is moving around nearby and within sight, but isn't directly at the PC's location.

    Second, when an NPC starts and finishes its journey within sight of the PC, the library omits the departure message entirely, and displays a new type of arrival message. When the entire journey is within the PC's field of view, we don't wish to describe the departure and arrival as two separate steps, since the whole journey can be described as a single operation; this is what the new type of arrival message does. The new message is given by the new TravelConnector method describeLocalArrival(), which the traveler calls from its describeNpcArrival method when it determines that the origin is visible to the PC. TravelConnector.describeLocalArrival() in turn calls the new Traveler method sayArrivingLocally(), and the default implementation of this new method simply displays the library message given by libMessages.sayArrivingLocally(). The library message displays a message of the form "Bob enters the alley." Note that it might sometimes be desirable to customize the travel connector's describeLocalArrival() method: you could say things like "Bob climbs the stairs up to the loft," or "Bob squeezes through the window into the kitchen," for example.

    BasicLocation.cannotGoThatWay now obtains the message to display from a new property of 'self', cannotGoThatWayMsg. By default, this returns &cannotGoThatWayMsg, which is the library message property for the default message for this case. You can easily override a room's message when travel is not possible in a given direction by overriding cannotGoThatWayMsg to return a single-quoted string with your custom message.
    The class GuidedInTravelState formerly defined each of the sayDepartingXxx() methods separately to show the departing-with-guide library message. Now, the class instead defines only sayDeparting() to display this message. This creates the identical effect by default, since all of the sayDepartingXxx() methods inherited from GuidedInTravelState's base class (AccompanyingInTravelState) simply call sayDeparting(). The advantage of this change is that it allows GuidedInTravelState's travel message for all of these different variations to be customized by overriding sayDeparting(), rather than overriding each sayDepartingXxx() individually.
    SpaceOverlay now checks the "moved" status of its "identity" object, not its own "moved" status, to determine if it should abandon its contents on being moved for the first time. This makes the operation work properly when the overlay is a component of a ComplexContainer, which will usually be the case; since only the parent complex container is actually marked as moved, checking the status of the child overlay object wasn't enough. Checking the identity object ensures that the status of the larger object of which the overlay is a part is properly taken into account.
    The TravelPushable method beforeMovePushable() now takes a new third parameter, 'dest', giving the destination of the travel.
    SpaceOverlay now overrides beforeMovePushable() to call abandonContents(). This ensures that if the object is a TravelPushable, and it's pushed to an new location, the contents are abandoned before the actor moves to the destination location. In the past, the object waited for the normal moveInto() notification to abandon its contents; but this notification didn't arrive until after the actor had already moved to the destination location, so the actor wasn't able to see the contents being abandoned, thus we weren't able to list the contents properly.

    ComplexContainer also overrides beforeMovePushable(), passing the notification down to any SpaceOverlay components it has. This corrects the same problem for a ComplexContainer that's also a TravelPushable, and which has SpaceOverlay components.

    SpaceOverlay is now more careful about the way it reveals the contents of the object when it's moved. In the past, it simply did an appropriate kind of nested command (LOOK UNDER for an Underside, for example) to generate the listing. This generally showed up before the main report for the action, so when the main report was a simple default report (such as "Taken"), it created confusion by suggesting that the default report somehow applied to the last object listed in the revelation:

       >take box
       Under the box is a rusty can. Taken.
    

    There are two changes to the way this listing is generated.

    First, the listing is no longer just a generic LOOK UNDER (or equivalent). Instead, it uses a new lister, given by the abandonContentsLister property of the SpaceOverlay object. Underside uses undersideAbandonContentsLister by default, and RearSurface and RearContainer use rearAbandonContentsLister. These new listers use more specific language to describe the revelation, of the form "Moving the box reveals a rusty can underneath" (or "behind").

    Second, the SpaceOverlay takes care to move the listing so that it follows the main report from the command that triggered the listing. This ensures that the main report remains close to the command line, which is required if the terse phrasing of the default reports is to make sense.

    There's one exception, though: if the actor performing the command ends up in a different location at the end of the command, the listing is not moved to the end of the report. Instead, the listing simply stays at the beginning of the command. This exception is important because travel usually triggers a description of the new location, and once we're in the new location, the revelation of the SpaceOverlay's contents will seem out of place. It's better in these cases to leave the contents listing at the start of the reports.

    With these two changes, the old listing above is transformed into this:

       >take box
       Taken. Moving the box reveals a rusty can underneath.
    
    If all of the changes described above to SpaceOverlay's revelation listing are still not adequate for some special case in your game, you can now easily suppress the default listing and generate your own explicit listing instead, thanks to a couple of new features.

    First, the new SpaceOverlay property 'neverListOnMove' lets you suppress the default listing; just set this property to true, and no listing will ever be generated when the overlay object is moved. If the contents are to be abandoned, the abanonment will still happen, but no automatic mention will be made of it. Your code is responsible in these cases for generating any desired listing.

    Second, the new method listContentsForMove() generates the revelation of the contents in the default format. SpaceOverlay calls this method itself to generate the automatic message when appropriate. If you set neverListOnMove to true in order to suppress the default revelation listing, you can call listContentsForMove() at the appropriate juncture in your own code to generate a substitute listing. Of course, you could instead completely customize the listing with your own method; but if you only want to change where the listing appears, without changing its contents, listContentsForMove() makes that easy.

    Note also that you can customize the standard listing format used by providing your own lister, and setting the SpaceOverlay object's abandonContentsLister to refer to your custom lister.

    In the default Floor class, there were a couple of problems with the commands SIT ON FLOOR, STAND ON FLOOR, and LIE ON FLOOR that have now been corrected. First, when standing in a nested room, STAND ON FLOOR incorrectly failed with the message "You're already standing." Second, when already sitting, lying, or standing on the floor, another command to do the same thing was incorrectly accepted; it's now rejected with an appropriate message (such as "You're already sitting on the floor").
    If the player character was in a nested room, and typed a command to leave the nested room, and immediately followed with an AGAIN command, the AGAIN incorrectly succeeded, making it look like the character was getting out of the nested room repeatedly without getting back in. This has been corrected; an appropriate message ("you're not in the booth") is now displayed on the errant repetition.
    In ActorTopicDatabase.initiateTopic(), the conversation is "noted" only if an InitiateTopic is found to handle the request. Noting the conversation means that we set the actor's last interlocutor to the player character, so this change means that initiateTopic() won't affect the actor's interlocutor unless a conversation is actually initiated. (In the past, the routine always noted the conversation. This caused odd side effects when initiateTopic() was called speculatively, without knowing for sure that an InitiateTopic was actually available, since the interlocutor changed even though no conversational action was visible to the player. This change ensures that the interlocutor will only change when a conversation actually takes place.)
    When an InConversationState object becomes the active state for an actor, it remembers the previously active state so that it can, by default, transition back to that previous state when the conversation ends. In the past, the InConversationState class remembered any previous state and used it as the new state at the end of the conversation. This has been changed. Now, the class remembers the previous state only if the previous state is a ConversationReadyState, or there's no other previous state already remembered. This change makes it easier for an actor to interrupt a conversation with another activity, and later resume the conversation, because the interrupting state will no longer "take over" at the end of the conversation. You can still explicitly set the end-of-conversation state explicitly to anything you want; this change only affects the default state transitions that occur in the absence of any explicit settings.
    The HELLO and TALK TO commands now defer the implied topic inventory until the end of the turn, if the response to the HELLO or TALK TO contains any non-default reports. This is important because a non-default report could set the ConvNode for the responding actor, but such a change doesn't take effect until the transcript is fully processed at the end of the turn. In the past, HELLO and TALK TO always showed the topic inventory right away, so the inventory didn't necessarily match the new ConvNode's active topics. With this change, a ConvNode change within a HELLO or TALK TO will be properly reflected in the topic inventory.
    The new ConvNode property 'isSticky' lets you create a "sticky" conversation node. By default, nodes are non-stick: this yields the traditional behavior, where the NPC leaves the node if a response doesn't explicitly stay there. (If the node is active, and the NPC shows a response that doesn't set any new node, the actor's current node is set to nil.)

    If you set 'isSticky' to true for a node, the node will stay active if the NPC shows a response that doesn't set a new node. In other words, the "default next node" for any response while the sticky node is active is the sticky node itself, rather than nil.

    Sticky nodes are useful in cases where you want an actor to drive a conversation along a particular thread, but you still want to allow the player to digress by talking about other topics. As long as a digression doesn't explicitly set a new node, the sticky node will remain active.

    DefaultCommandTopic now has a matchScore of 3, which makes the library choose it over a DefaultAnyTopic if both kinds of default topics are active for a given response. (In the past, DefaultCommandTopic had the same matchScore as a DefaultAnyTopic, so in cases where both kinds were active the library chose one arbitrarily. The DefaultCommandTopic should always take precedence, as it now does, because it's the more specific kind of response.)
    AltTopic now takes its setting for the 'impliesGreeting' property from its enclosing topic. This ensures that an AltTopic used within a HelloTopic or the like will behave the same as its enclosing main topic by default.
    ByeTopic and ImpByeTopic now work together the same way that HelloTopic and ImpHelloTopic work together. Specifically, ByeTopic is now a catch-all that matches both explicit GOODBYE commands and implied conversation endings. (Implied goodbyes happen when the player character just walks away from a conversation, or the NPC gets "bored" from lack of attention). ImpByeTopic still matches only implied goodbyes; since it's the more specific of the two, it now uses an elevated matchScore. This means that if both a ByeTopic and an ImpByeTopic are active at the same time, the ImpByeTopic will be chosen for an implied goodbye, and the ByeTopic will be chosen for an explicit GOODBYE command.

    This change makes it easier to program catch-all goodbye messages, while still allowing differentiation when desired. To create a catch-all message that handles both explicit and implicit goodbyes, just create a ByeTopic. If you want to differentiate, create one of each: the ByeTopic will be chosen for the explicit GOODBYE commands, since the ImpByeTopic won't match those; and the ImpByeTopic will be chosen for implied goodbye, because both will match, but the ImpByeTopic's higher matchScore will ensure that it's selected over the ByeTopic.

    Along the same lines, HelloGoodbyeTopicObj now matches implied as well as explicit goodbyes. (In the past, it handled implied and explicit greetings, but only explicit goodbyes.)

    Some of the behavior of InConversationState.endConversation has been moved to the base ActorState class. This ensures that the active ConvNode will be notified if the player walks away from a conversation.
    The library messages for scoring (showScoreMessage, showScoreNoMaxMessage, showScoreRankMessage, showFullScorePrefix) now refer explicitly to "you", not to the player character. Since the score is a meta-game feature, it should be attributed directly to the player, not to the player character. This is especially important in games that refer to the player character in the first or third person, since the distinction between player and player character is more evident in these cases than it is in second-person games.
    The library message for trying to put an object inside an another object that already contains the first object (i.e., the "circular containment" message) now reports the objects as specified, rather than possibly showing an intermediate container. In the past, an intermediate container was shown in some circumstances, especially when a ComplexContainer was involved; this produced the wrong messages in some cases.
    In the past, the multiple object announcement ("red book: Taken") was missing in in some cases where the action was remapped (via remapTo). This has been corrected.
    The implied action message generator now allows for "silent" implied actions - implied actions that behave as usual but generate no announcement text (the normal announcement is the message of the form "(first opening the door)" shown at the start of the response to the command).

    First, the new libMessages method silentImplicitAction() can be used in place of the standard announceImplicitAction() to generate the announcement. silentImplicitAction() simply shows no message. To use it, perform the implied action with a call like this:

       tryImplicitActionMsg(&silentImplicitAction, Open, self);
    

    Second, the lister that aggregates a set of implicit actions into a single announcement message now accepts the empty messages generated by silentImplicitAction(). This ensures that silent implied actions can be mixed with announced implied actions in a single command, and a sensible aggregate announcement will be displayed.

    Thing.setContentsSeenBy() incorrectly marked the object's contents as having been seen by 'gActor' rather than by the actor passed as a parameter to the method. This has been corrected.
    In PresentLater, when the object is made present (via the makePresent method, or any of the related methods), the object and its contents are specifically marked as seen by the player character, to the extent the player character can actually see them at the moment they become present in the location. In most cases, when an object comes into play dynamically via the PresentLater mechanism, the sudden appearance is the result of some specific event that the game will describe through custom handling - that is, the game will specifically mention the new object to the player, so the player character should remember having seen the object.
    In a two-object command (such as PUT VASE ON TABLE), the parser now retains the meaning of a pronoun used in the command, regardless of which object slot the pronoun is used in. For example, after the command PUT VASE ON IT, the pronoun "it" is set to refer to the indirect object, even though "it" would normally refer to the direct object after a PUT X ON Y command.

    In the past, the parser unconditionally set the pronoun antecedent after a two-object command to the second-resolved object, which in many such verbs is the direct object. So, after PUT VASE ON IT, the pronoun "it" previously referred to the vase. Due to this change, though, the explicit use of the word "it" directly within the command causes meaning of "it" to be left unchanged by the command. This change doesn't affect the behavior when no pronoun is used in the command, so PUT VASE ON TABLE still causes "it" to refer to the vase on the subsequent command.

    ConsultTopic and DefaultConsultTopic now refrain from setting any pronoun antecedents for the topic. This means that after any consultation command (LOOK UP, FIND IN, etc.), the pronoun antecedent will be the consultable object, not the topic last sought.
    TopicTAction (used for commands such as ASK ABOUT and CONSULT ABOUT) didn't work properly if a reflexive pronoun was used as the direct object, referring back to the topic phrase: LOOK UP BOOK IN ITSELF, for example. This showed a couple of symptoms, including "Nothing obvious happens" responses and run-time errors. The problem has been corrected; the action now looks to the topic phrase to resolve the direct object, producing consistent and appropriate results.
    When a command is directed to an NPC (as in BOB, OPEN DOOR), and the parser has to prompt interactively for disambiguation or for a missing noun phrase, the parser now treats gender-matched third-person pronouns in the reponse as referring to the target actor. For example:

       >look at bob
       He's wearing a red sweater.
    
       >bill, examine
       What do you want Bill to look at?
    
       >his book
       The book looks old and dusty; the title on the cover is faded
       to the point of being unreadable.
    

    In the past, the parser would have taken the "his" in the interactive response to refer to Bob, since Bob would have been the pronoun antecedent up until that point (by virtue of having been mentioned in the previous command). With this change, the parser takes "his" to refer to Bill. This fits with the phrasing of the prompt text, and also with the fact that Bill was mentioned more recently in a command (specifically, in the incomplete "examine" command).

    Similarly, reflexive third-person pronouns (HIMSELF, HERSELF, ITSELF, THEMSELVES) in interactive responses are taken as referring to the target actor, when the gender matches.

    The parser is now more consistent in its treatment of truncated vocabulary matches. (Truncated matches are words in the player's command that match longer object names defined in the game, by truncating the longer object names to the dictionary's minimum length. For example, the default English dictionary truncation length is 6 characters, meaning that the player can shorten the word FLASHLIGHT to FLASHL, FLASHLI, etc., when entering commands.)

    In the past, the parser treated exact matches as stronger than truncated matches for singular definite noun phrases (the most common kind of noun phrase: TAKE BOOK, OPEN THE BOX). Any time the parser matched some names exactly and other names only after truncation for the same user input, the parser kept only the exact matches, filtering out the truncated matches. This rule hasn't been changed; what's changed is that it has now been extended to every kind of noun phrase, including indefinite phrases (TAKE A BOOK) and plural phrases (TAKE BOOKS).

    In the English parser, when the parser matches a plural phrase (i.e., a phrase with a vocabulary word defined under the 'plural' property), the parser now includes both 'plural' and 'noun' matches for the word. In the past, only the 'plural' matches were included.

    In some cases, it's convenient to define a word that's grammatically a plural under the 'noun' property, because a single game object encompasses what looks to the user like a set of something: a bookcase might define "shelves" as a noun, for example, since the individual shelves aren't modeled as separate objects. This change ensures that if other objects are in scope that define the same word under the 'plural' property, the bookcase will still match the word "shelves" when the noun phrase is resolved.

    This change doesn't actually make much difference most of the time, but it is frequently significant in "topic" phrases (as in ASK ABOUT). Topic phrases have very wide scope, so a particular word can match numerous objects from all over the game. In these cases, the old scheme that included only 'plural' matches was overly exclusive, causing the parser to omit objects from the topic resolution that the author would at first glance have expected to be included. The new more inclusive matching should reduce the potential for confusing match exclusions in these cases.

    The English parser is now more inclusive in matching nouns and adjectives at the ends of "topic" phrases. (Topic phrases are used in commands like ASK ABOUT and TELL ABOUT, where the player can refer to physical objects as well as to abstract Topic objects, and can even use random text that doesn't correspond to any game objects.)

    Normally, when the last word of a noun phrase can match one or more objects in scope that define the word under the 'noun' property, the parser will ignore any additional in-scope objects that match the same word under the 'adjective' property. For example, if there's a desk in scope that defines 'desk' as a noun, and there's also a desk drawer that defines 'desk' as an adjective, the parser will interpreter LOOK AT DESK as referring to the desk, not to the drawer. However, if there's a red book present, but there's no object in scope that defines 'red' as a noun, the parser takes READ RED as referring to the red book, even though BOOK was left unstated. This combination of rules gives the player the convenience of abbreviating the name of an object to a unique adjective, while avoiding spurious ambiguity in cases like the desk-and-drawer exmaple.

    In cases of "global" scope, though, this strategy isn't as beneficial. When resolving a topic phrase, objects from all over the game have to be considered "in scope" even though they're not physically present, as it's perfectly reasonable for the player to ASK ABOUT something that was seen earlier in the game but isn't physically present at the time of the question. As a result, the elimination of adjective bindings for a word can cause subtle problems that are very difficult to track down, and which sometimes can't be easily fixed without creating other problems. Two objects could happen to share the same vocabulary words, in one case as an adjective and in the other case as a noun, even though the objects are completely unrelated. Thus, the existence of one object can change the way another object's vocabulary words are treated when matching a topic phrase.

    The English parser now treats topic resolution differently. When resolving a topic phrase, the parser no longer makes the strict noun/adjective distinction. Instead, the parser considers both kinds of vocabulary to match. So, if the player types ASK BOB ABOUT DESK in our example with the drawer, both the desk and the drawer are now considered possible topic matches.

    This change has the potential to create a different sort of problem, in that it could include too many possible matches for a topic phrase. However, this is a much easier problem to deal with than the old one, and in most cases won't even be noticeable as a problem. In the first place, topic phrases don't require disambiguation in the normal sense; the parser never asks the player which object was intended, so the more inclusive matching won't degrade the user interface by generating more disambiguation queries. Second, it's much easier to understand why an extra object is matching a given vocabulary word than to understand why a given object is failing to match. A failure to match indicates that some other object is defining the same word under another part of speech, but it doesn't tell you which object or where it's defined; when too many objects match, in contrast, you can easily see (using the debugger, for example) which extra objects are matching, and make any desired vocabulary adjustments. Third, in the vast majority of cases, the extra matches are completely harmless and not even noticeable. Topics are generally handled in conversations, and conversations have to explicitly include responses for all of the topics they want to match. If an extra object or two show up in vocabulary matching for a given conversational command, it won't usually matter, because those unexpected objects won't have topic matches in the conversation anyway and will thus simply be ignored. In cases where you want a conversation to distinguish among objects with similar vocabulary, you can add the necessary distinguishing vocabulary to the objects just as you would in cases of ordinary ambiguity, and you can tweak the topic list (using match scores, for example) to control which response is used for which vocabulary. In the rare cases where it's not straightforward to fine-tune the matching using object vocabulary, you can always resort to using regular expressions as the topic match criteria.

    Consider a situation where a given object is entered in the dictionary with two words for a given part of speech, and one of the words happens to be a leading substring of the other word, and both words are long enough to trigger "truncated" matching (that is, each word is at least six characters long, in the default dictionary truncation configuration). For example, suppose a given object is entered in the dictionary with 'noun' words for WINDOW and WINDOWSILL. Now, if the player enters a command using the shorter word (WINDOW), the raw dictionary match list will contain two matches for this object: one for the exact match to the shorter word, and one for the truncated match to the longer word.

    In the past, this situation sometimes had undesirable results. In most cases, the two matches to the same object were collapsed into a single match, but the "match flags" from the two matches were combined - so both matches were marked as truncated. Depending on what other objects were in scope at the time, this had the effect of eliminating the object from consideration as a resolution of the noun phrase: even though it had an exact match in the dictionary, the resolver saw it as a truncated match because of the combination of the two sets of match flags. This was especially likely to affect topic phrases (ASK ABOUT, for example), because the global scope made it much more likely that other objects would have exact matches for the same vocabulary.

    The parser is now smarter about the way it combines these sorts of redundant matches. Instead of simply OR'ing together the match flags, the parser now specifically keeps only the "strongest" match in these cases. In our example, the parser sees that there's a truncated dictionary match (WINDOWSILL) and an exact match (WINDOW) for the same object, so it now decides to consider the match to be exact.

    The English parser now accepts IN object, INTO object, and IN TO object as verb synonyms for ENTER object. These aren't exactly valid English grammar, but they have a certain linguistic consistency with other telegraphic IF-ese constructs, and they seem to naturally occur to some players. These variations all have an obvious meaning to an English speaker, and they're not ambiguous with any other commands, so there should be no harm in adding them as convenient abbreviations.
    After a misspelling, if the player typed OOPS but left off the corrected spelling (i.e., just typed OOPS by itself on the command line), the parser responded with a message saying that OOPS isn't allowed right now. This has been corrected; the parser now responds with a new message explaining the correct syntax, and gives the user another chance to enter the OOPS command.
    When a single player command triggers more than one implied action (for example, GO NORTH might trigger OPEN DOOR, which in turn might trigger UNLOCK DOOR), the reporting mechanism had a couple of problems when the doubly-nested action (UNLOCK DOOR in our example) itself triggered a nested action, such as by remapTo or nestedAction. First, the reported order of the implied actions was reversed ("first opening the door, then unlocking it"); second, the doubly-nested one reported the nested action rather than the original implied action. Both of these have now been corrected.
    When you synthesize an action programmatically (via a function such as replaceAction or nestedAction), the library has to choose a language-specific final subclass of the generic action you specify. It does this by choosing a VerbRule that's subclassed from the base action. In the past, the choice was entirely arbitrary. Now, the library is a little pickier: it now chooses only a final subclass, never an intermediate class. That is, it always chooses a VerbRule which hasn't been further subclassed (or modified with 'modify'). The old, less selective algorithm occasionally caused oddities, such as choosing a base VerbRule that was subclassed with 'modify', thus picking up pre-'modify' values of properties such as the verb's descriptive text.
    The conversationManager object now provides a simple extensibility mechanism that lets you add your own custom conversation tags. To add custom tags, use 'modify' to extend the conversationManager object, then add two things. First, define the customTags property, giving a string with the new tag names you want to add. The tag names must be in lower-case letters; do not include the angle brackets or the leading period in the tag name. Separate multiple tag names with "|" symbols. Second, define a doCustomTag(tag,arg) method that processes your custom tag or tags. The 'tag' argument is a string giving the custom tag that was matched in the output stream; only the tag name will be included, not including the angle brackets or leading period. The 'arg' method is a string giving the argument to the tag, if any; this is simply the part inside the angle brackets following any whitespace after the tag.
    SensoryEmanation and its subclasses Noise and Odor will now let you use Script objects for the hereWithSource and hereWithoutSource properties. This is handy in cases where you want to vary the object's description over time. If you define these properties as Script objects, the SensoryEmanation object will invoke their doScript() methods when an emanation message is to be displayed.
    The Keyring class has a new method, getLooseKeys(actor), that identifies the loose keys in an actor's possession that are eligible for automatic attachment when the keyring is taken. By default, this returns the actor's direct contents (i.e., the objects the actor is directly holding).

    This can be overridden to consider, for example, loose keys held in the actor's pocket. To prevent the keyring from automatically grabbing any keys when it's taken, simply override this method to return an empty list. Note that you don't need to bother limiting the returned list to include only valid keys, since the caller does this by considering only objects for which isMyKey() returns true.

    The function withParserGlobals() now takes an additional parameter giving the actor who issued the command. (This is an internal function that's probably not used at all by existing game code, so it should have no impact on games. If you are calling this function, though, note that you'll need to supply the extra parameter.)
    The menu system now sets the main content banner window to use scrollbars if possible, and automatically scroll to show new text in any case. This ensures that hint lists that are too long to fit in the window will scroll as needed to keep the last hint in view.
    3.0.6o

    Released 3/14/2004

    Possible compatibility-breaking change: The property formerly named 'vocabWords_' has been renamed to 'vocabWords' (the change is that the underscore formerly at the end of the name has been removed). The underscore suffix is conventionally used in the library to indicate an internal library property that isn't normally for use by game code, which obviously isn't appropriate in this case, as this is the property that games usually initialize to set an object's vocabulary words.

    You should search for instances of 'vocabWords_' in your existing code, and replace them with 'vocabWords'. Most existing game code will probably have few, if any, instances of this property name, since most object definitions in existing game code probably use the standard library templates to set this property's value without giving its name. Even so, you should search through your code and update any mentions to use the new name.

    The method getOutermostRoom(), which was previously defined in BasicLocation and NestedRoom, has been moved instead to Thing. This ensures that the method will work properly even when a nested room is located within a non-room object. The Thing definition simply returns the enclosing location if there is one, or 'self' if not.
    The new TopicEntry subclass AskTellGiveShowTopic makes it easy to define a response that covers ASK, TELL, GIVE, and SHOW for a given object or set of objects. It's sometimes convenient to be able to treat all of these commands as equivalent in handling a response.

    AskTellGiveShowTopic is based on the new class TopicOrThingMatchTopic, which can be subclassed to create other combinations of other subsets of these four verbs (or to add topic entries for new custom game-defined verbs). This new class combines the features of the "Thing" and "Topic" base classes for topic entries.

    ConvNode has a new method, autoShowTopics(), that returns true if the node is to show a topic inventory automatically on activation. By default, this method returns true if the node contains any active SpecialTopic entries. You can override this if desired to generate a topic inventory automatically under other conditions, or to suppress the automatic inventory when an active special topic is present (although the latter probably isn't a good idea).

    The main reason you might want to generate a topic inventory automatically on activating a node is that the node contains one or more obscure topics that players will be unlikely to guess on their own. If it's unlikely that the player would think to ask just the right thing at just the right time, a topic inventory can help guide them through the conversation by showing the possibilities. As always, only the topics you specifically mark as suggested (with SuggestedAskTopic, etc) will be listed in the inventory. When possible, it's probably better from a game-design perspective to lead players into asking the right questions more subtly, by putting the right cues in the conversation itself; when this is impractical, the topic inventory is a usable substitute.

    The pronoun expansion for CollectiveGroup objects that was introduced in 3.0.6n has been partially removed. In the past, when an individual object was associated with a CollectiveGroup object, the parser automatically added the group object into consideration for resolving a subsequent pronoun referring to the individual. This was meant to provide better fidelity for resolving pronouns in these cases, but it led to new problems when the original noun phrase could only have referred to the individual. To correct the problems, the parser no longer puts the collective group into consideration when a pronoun refers to one of the group's individuals.

    The other direction still applies, though: when a pronoun refers to a CollectiveGroup object, the parser puts the group's associated individuals into consideration for resolving the pronoun.

    A bug in the CommandTopic class (introduced in 3.0.6n) prevented it from matching a single Action class as the matchObj correctly. (It did work properly when a list of Action classes was given, but not when a single object was given.) This has been corrected.
    The HelloGoodbyeTopic class now has its impliesGreeting property set to true, ensuring that this topic type won't trigger an implied greeting. Since a HelloGoodbyeTopic is itself a greeting, an implied greeting would be undesirable, not to mention that it caused a run-time error when it occurred.
    A bug in ComplexContainer caused a problem when an object was initially located in one of the the complex container's sub-containers via the 'subLocation' property. In the past, the ComplexContainer didn't properly re-initialize the object's 'location' property to reflect that it was actually in the sub-container. This has been fixed.

    The problem manifested as strange behavior when the contained object was removed from the sub-container (via TAKE or the like). In particular, because the object's 'location' was still set to the ComplexContainer, the object wasn't properly removed from the sub-container's contents list, so the object appeared to still be in the sub-container as well as in its new location.

    A couple of problems involving formatting and real-time events have been fixed. In 3.0.6i, fuses and daemons - including the real-time versions - started running with a transcript context, which means that their text output is captured. This caused some interactions with the input manager that weren't taken into account properly in the library.

    First, if a real-time event itself stopped and asked for input from the user (by reading a new command line, for example), the transcript capturing prevented the text displayed by the event from showing up at all until after the user entered something on the new command line. It was possible to work around this by deactivating the transcript prior to showing any output in the real-time event, but this is no longer necessary.

    Second, even if a real-time event didn't display anything, its mere silent firing caused an extra blank line to show up immediately after the user next pressed the Enter key to finish a command line.

    Both of these problems are now corrected, so there's no need for real-time events to do anything special with respect to output formatting or command-line input.

    The new pre-defined message object parameter 'pc' can be used to refer directly to the player character. This is similar to the 'actor' parameter, but refers to the player character actor even if an NPC is the current active character. An expression such as "{you pc/he}" thus refers to the current gPlayerChar, not to the current gActor.
    The library messages for scoring (showScoreMessage, showScoreNoMaxMessage, showScoreRankMessage, showFullScorePrefix) incorrectly referred to the current actor rather than the current player character, which resulted in attributing the score to an NPC rather than to the player if a score message was generated on another actor's turn. These messages now explicitly refer to the player character.
    3.0.6n

    Released 3/6/2004

    Major compatibility-breaking change: The library's main entrypoints to the game have changed. Existing games must be modified to accommodate this change.

    In the past, the game code was responsible for defining two entrypoints: main() and mainRestore(). The template games generated by Workbench's "New Project" command defined these two functions as one-liners that called mainCommon(), where the real action took place.

    In the new arrangement, the library now invokes methods of a new object, gameMain, which each game must define. For your convenience, the library defines a class, GameMainDef, that you can use as the base class of your gameMain object. This base class defines suitable defaults for the required methods, so you only have to override what you need to customize. Overall, this change should result in much less required startup code for each game.

    If you use GameMainDef as the base class of your gameMain object, you're only required to define one property in it:

    • initialPlayerChar - set this property to the Actor object that serves as the initial player character in the game. You must set this; GameMainDef can't provide a default for you here.

    In addition, there are a couple of optional optional methods that you can use for customization:

    • showIntro() - show the game's introduction. Most games will want to override this to show the prologue text for the game.
    • showGoodbye() - show the game's "goodbye" message. Override this if you want to add a parting message as the game terminates.

    Most games will find it sufficient to define only initialPlayerChar and showIntro(), and simply inherit the default handling for everything else from GameMainDef. However, there are several additional methods that you can override if you want to do something special.

    • newGame() - the library invokes this method to start a new game. The default version calls showIntro() to show the introductory text, then runs the main command loop until the player quits, then calls showGoodbye() to show the goodbye message. You can override this if you want to do something extra, such as showing a pre-game options screen.
    • restoreAndRunGame(filename) - the library invokes this method to restore and start running a saved game that the user selected when launching the interpreter. It should rarely be necessary to override this.
    • setGameTitle() - the library invokes this method at start-up to set the interpreter's window title to show the game's title, as defined in versionInfo.name. It should rarely be necessary to override this.

    If your existing game code uses the Workbench-generated template, or you follow the same pattern, then rewriting your code to fit the new design is relatively straightforward. Just follow these steps.

    • Save a copy of your existing code before you do anything else. If things don't seem to work properly after you finish applying this update, you can compare the modified version with your backup copy to help track down the problem.
    • Delete your main() and mainRestore() functions. Your game code doesn't need to define these routines at all any more, since the library now defines them.
    • Create a new object definition for gameMain:

         gameMain: GameMainDef
           initialPlayerChar = me
           showIntro()
           {
             // TO DO!
           }
         ;
      
    • Find your introductory text, and anything else you customized in your old mainCommon() routine, and move it into the showIntro() method (replacing the "TO DO!" comment in the example above).
    • Delete your mainCommon() function. It has now been subsumed into your new gameMain object.

    That's it - if you were using the standard Workbench template for mainCommon(), you're now finished.

    If you made certain kinds of changes to the template main(), mainRestore(), or mainCommon() that Workbench produced when you originally created your game, you might have to do some additional work. These extra changes are uncommon, but check this list to make sure they don't apply to you:

    • Do you have anything in main() or mainRestore() beyond a simple call to mainCommon()? If so, you'll probably have to move that code into the showIntro() method, or you might even have to override newGame() (and possibly even restoreAndRunGame()). This is beyond the scope of this simple recipe, so please refer to the definition of GameMainDef in misc.t for full information on the new setup.
    • If you customized the code in mainCommon() that restores the file specified by 'restoreFile', you'll need to override restoreAndRunGame(). You can probably just move your existing code from your old mainCommon() into a new mainGame.restoreAndRunGame() method. Refer to the definition of GameMainDef in misc.t.
    • If you player character isn't named 'me', you'll need to change the initialPlayerChar definition accordingly.
    Minor Compatibility-breaking change: A few properties that were previously in libGlobal and other library objects have been moved into the new gameMain object instead. The relocated properties all select optional library behavior; they've been moved to gameMain to make it easier for game authors to select non-default option settings. In the past, you had to use 'modify' to change these settings, or explicitly assign them from your startup code; now, you simply have to include any non-default option settings as property values in your gameMain object definition.

    The properties moved are:

    • The former libGlobal.verboseMode is now gameMain.verboseMode.
    • The former exitLister.enableStatusline is now gameMain.showExitsInStatusline.
    • The former libGlobal.allowYouMeMixing is now gameMain.allowYouMeMixing.
    • The former libMessages.scoreRankTable is now gameMain.scoreRankTable.
    • The former libScore.maxScore is now gameMain.maxScore. (This value is also now nil by default, since there's no reason for the library to assume a particular maximum score.)
    Compatibility-breaking change: The format of the verbPhrase string has changed slightly for the English version of the library. Game-defined verbs might need to be adjusted to the new format.

    The change is that prepositions in a verb phrase are now grouped inside the parentheses of the "(what)" placeholders for the objects, if the prepositions go with the noun phrases. For example, the LookIn action's verbPhrase string has changed from 'look/looking in (what)' to 'look/looking (in what)'. The difference is subtle: by moving the preposition 'in' so that it's inside the parentheses with the direct object's "(what)" placeholder, we're telling the library that the 'in' is part of the direct object noun phrase. Contrast this with the verbPhrase for Doff, which is now 'take/taking off (what)': this tells us that the preposition 'off' is part of the verb structure, not part of the direct object.

    How can you tell which way it goes? There are two rules you should use to determine this.

    First, if the preposition separates the direct and indirect objects, such as the IN in PUT X IN Y, then it always goes inside the indirect object phrase, hence 'put (what) (in what)'.

    Second, for any other preposition, use the "it test": try writing the verb using 'it' in place of 'what', and ask whether it sounds right for the preposition to go before or after the 'it'. For example, for the Doff action, TAKE IT OFF sounds right, and TAKE OFF IT is obviously wrong. This means that the preposition 'off' belongs in the verb phrase, hence 'take off (what)'. In contrast, for the LookIn action, LOOK IN IT sounds right, not LOOK IT IN, so the preposition goes with the direct object, hence 'look (in what)'. If the "it test" indicates that the preposition goes before the 'it', then the preposition is part of the direct object and thus goes inside the parentheses of the "(what)"; if the preposition goes after the 'it', then the preposition is part of the verb and goes outside the parentheses.

    Prepositions that go with the verb are uncommon in two-object verbs, but they do occur. An example from the library is ConsultAbout, one form of which uses the verbPhrase 'look/looking up (what) (in what)'. We'd write LOOK IT UP IN IT, so the preposition 'up' goes after the direct object pronoun and is thus part of the verb, while the preposition 'in' goes before the indirect object pronoun and is thus part of the indirect object. In case there's any ambiguity about whether the 'up' goes with the verb or with the indirect object, try the un-it test: we'd write LOOK UP TOPIC IN BOOK, so clearly the 'up' is not part of the indirect object, but is just part of the verb.

    Compatibility-breaking change: The TextList classes have been removed, leaving only the more general EventList classes. The TextList classes have been gradually converging with the EventList classes anyway, to the point where the TextList classes have become nothing more than renamed versions of the corresponding EventList classes. Retaining the parallel set of names for otherwise equivalent classes is bad because it steepens the learning curve for new users and makes for more to remember for experienced users; so, the redundant names have now been dropped.

    Existing code will need to be scanned for occurrences of TextList, StopTextList, RandomTextList, ShuffledTextList, and SyncTextList. Replace these names with CyclicEventList, StopEventList, RandomEventList, ShuffledEventList, and SyncEventList, respectively. (Note that you can simply do a global search-and-replace to change to suffix TextList to EventList, except that you must change occurrences the TextList when it occurs as an entire word to CyclicEventList.)

    In addition, the following property names must be changed:

    • textStrings to eventList
    • firstStrings to firstEvents
    • messagePercent to eventPercent
    • messageReduceAfter to eventReduceAfter
    • messageReduceTo to eventReduceTo

    If you really don't want to change your existing code, you can always use #define to create macros with the substitutions. You really should change your code if possible, though, since using the old names is likely to create confusion as the old names recede from your memory and from the documentation.

    Compatibility-breaking change: In ConversationReadyState, the greeting and goodbye messages have been changed.

    First, the greetingList property is no longer used; instead, the state now looks for a HelloTopic entry and uses its response. In addition, you can differentiate between explicit HELLO commands and implied greetings generated by other conversational commands (ASK TO, for example) by creating separate HelloTopic and ImpHelloTopic objects. In most cases, it's not necessary to distinguish the two cases, so you can simply use a single HelloTopic to cover both cases.

    Second, the enterFromByeMsg and enterFromConvMsg methods have been removed, along with the enterFromByeList and enterFromConvList properties. Instead, the state now looks for a ByeTopic or ImpByeTopic entry, depending on whether the conversation is ending due to an explicit GOODBYE command or due to an automatic ending (due to the other actor walking away, or due to an inactivity timeout) and uses its response.

    You should scan your existing game code for any greetingList, enterFromByeMsg, enterFromByeList, enterFromConvMsg, and enterFromConvList properties, and change them to HelloTopic, ByeTopic, and ImpByeTopic as appropriate.

    Note that both the HELLO and GOODBYE topic entries go in the ConversationReadyState, not in the InConversationState. This is because these handlers typically describe the transition to and from the "ready" state. A single in-conversation state could work with several "ready" states, so putting the hello/goodbye topic entries in the in-conversation state wouldn't allow any differentiation among the several "ready" state transitions. Keeping the hello/goodbye entries in the "ready" states themselves makes this differentiation easy. For example, this allows a "goodbye" message to say something like "Bob goes back to sweeping the porch."

    Possibly compatibility-breaking change: The internal methods that handle conversational commands in Actor have changed. The changes are isolated to the internal methods that route the commands within and between the Actor and ActorState objects, not to the main public interfaces that game code usually uses. Even so, since it's occasionally useful to override these internal methods, some existing game code might be affected.

    In general terms, these changes are designed to concentrate the handling of all conversational actions along a single path. In the past, each type of conversational command was handled by its own cluster of methods, so the Actor and ActorState had a set of parallel methods for HELLO, GOODBYE, YES, NO, ASK ABOUT, ASK FOR, TELL ABOUT, SHOW TO, and GIVE TO. This old design was intended to make it easy to override these handlers in isolation, but experience has since shown that it's much more useful to be able to override all of the handlers in concert instead. The new design therefore consolidates all of these different conversational actions into a single method that's parameterized with the type of action. There are still several methods because of the sequence of processing points through the Actor and ActorState, but there are no longer several clusters of methods: each of the old clusters is now a single method.

    An additional benefit of these changes is that HELLO and GOODBYE are now handled through the topic database like everything else. This has two useful effects. First, it means that a DefaultAnyTopic entry will now respond to a HELLO or GOODBYE along with everything else. This is highly desirable in most cases, because these defaults are usually meant to convey a blanket response to all conversational overtures. Second, it means that it's now a bit easier to customize HELLO and GOODBYE responses: just use HelloTopic, ByeTopic, and HelloByeTopic objects as desired.

    Advice: Because there are a lot of details to these changes, we suggest you scan your code for mentions of the affected methods, to see if your code is affected at all. If your code doesn't define or call any of these methods, you shouldn't be affected. The methods are:

    • sayToActor
    • handleConversation
    • yesNoFrom
    • answerQuestion
    • hearAbout
    • beShown
    • beGiven
    • answerRequestFor

    Now to the specific changes.

    Actor.sayToActor() has some changes to its parameters. The 'prop', 'propArgs', and 'topicListProp' parameters have been dropped, and the new 'topic' and 'convType' parameters have been added. 'topic' is a special object representing the topic; the library defines the singletons helloTopicObj, byeTopicObj, yesTopicObj, and noTopicObj for the corresponding library actions. 'convType' is an object of type ConvType describing the type of action being performed.

    The new helloTopicObj and byeTopicObj singletons have been added, as mentioned above. These are used as special topic placeholders for the HELLO and GOODBYE commands, respectively.

    The new ConvType class has been added, and singleton instances for all of the standard library conversation actions (HELLO, GOODBYE, YES, NO, ASK ABOUT, ASK FOR, etc.) have been defined.

    The 'topicListProp' and 'handler' parameters of ActorState.handleConversation() has been dropped, and the new 'convType' parameter has been added. 'convType' is a ConvType object representing the conversation type.

    The 'topicListProp' parameter of ConvNode.handleConversation() has been replaced with a new 'convType' parameter, which is a ConvType object describing the conversation type.

    Actor now provides a handleConversation() method. The ActorState will invoke this method by default when the ActorState's own handleConversation() method doesn't handle the action. This new Actor method is in lieu of the former cluster of per-command handlers in Actor: yesnoFrom, answerQuestion, hearAbout, beShown, beGiven, answerRequestFor.

    All of the per-command handlers in Actor and ActorState have been removed: yesNoFrom, answerQuestion, hearAbout, beShown, beGiven, answerRequestFor. These are replaced by the handleConversation() method in Actor and ActorState, as mentioned above.

    The new Actor method defaultConvResponse() is a general handler for showing the default response when a conversational action isn't otherwise handled (that is, it's not handled by any topic database entry, and the ActorState doesn't want to handle it specially). This new method provides an easy way to show the same response for every conversational action - just override this one method, and you can show a common response for all conversation commands. By default, this method routes looks at the convType parameter to determine the conversation type, and invokes the Actor method that provides the default response for that particular conversation type, using the same default response methods used in the past (defaultGreetingResponse, defaultGoodbyeResponse, etc).

    The ActorState object can now optionally define any of the default response handlers that Actor can define (defaultGreetingResponse, defaultAskResponse, etc). When these are defined, they'll be called when the state object doesn't provide a suitable TopicEntry in its topic database. Note that these effectively override the Actor's topic database and default response handlers for a given type of action. The order of handling for a conversational action is now ConvNode, ActorState TopicEntry list, ActorState default response method, Actor TopicEntry list, and finally Actor default response method.

    Possibly compatibility-breaking change: A change to the library's message system makes it easier to customize the default response messages for individual objects. In the past, if you wanted to customize the message for OPEN COCONUT, say, you'd have to override the 'verify' method for the 'coconut' object's dobjFor(Open). With this change, you can create a custom per-object message more easily: you simply define coconut.cannotOpenMsg with the custom message string.

    You can use the new message customization scheme any time the library generates a standard response message using code like this:

      dobjFor(Open)
      {
        verify { illogical(&cannotOpenMsg); }
      }
    

    Any time you see library code like that, you can override the default response message for that action on an individual object simply by defining the message property in your object:

      coconut: Thing
        cannotOpenMsg = '{You/he} would need something sharp to do that. '
      ;
    

    In addition to illogical(), this also works with illogicalNow(), inaccessible(), defaultReport(), defaultDescReport(), extraReport(), mainReport() reportBefore(), reportAfter(), reportFailure(), and any other verify or action reporting routines.

    Here's how this works. In the past, when a verify, check, or action routine generated a message using a message property, the library looked up the message in the current actor's "action message object," which is usually playerActionMessages when the actor is the player character, and npcActionMessages otherwise. Now, the library still looks there, but only after checking the individual objects involved in the command to see if any of them define the message property.

    The library looks first at the direct object, then at the indirect object. If you were to create your own custom verbs with three or more object slots, such as PUT COIN IN SLOT WITH TWEEZERS, the library would automatically continue on to those extra objects as well. If the library finds the message property in any of these objects, it stops and uses that object as the source of the message; if it can't find the message property among these objects, the library simply falls back on the standard message object (playerActionMessage or whatever).

    Important: As part of this change, all of the library message properties have been renamed to end in "Msg". This affects every message property, so if you've created your own "action message object," or you've used 'modify' to change playerActionMessages and/or npcActionMessages, you'll have to do some extensive searching and replacing to add the "Msg" suffix to every message property name. Sorry about this; the proposed change was put to the TADS 3 mailing list, and no one objected. The naming change isn't gratuitous. The reason for the name change is that it should greatly reduce the chances of collisions between message properties and properties defined for internal use by an object. The library itself formerly had a number of these collisions, so it was necessary to rename at least those properties; using the naming convention consistently for all of the message properties will help ensure that games don't inadvertantly introduce their own name collisions.

    There's one last detail to mention. An object can override a message property with another message property. For example, the Vaporous object in the library uses this feature:

      notWithIntangibleMsg = &notWithVaporousMsg
    

    When a message property in an object points directly to another property, the library takes this as an indirection to another library message from the action message object. This feature is mostly for the library's benefit, since library objects are required to get all of their messages from the action message object (to ensure that the library can be translated without rewriting entire object definitions).

    Minor compatibility-breaking change: The Script method getState() has been renamed to getScriptState(), and the Script property curState has been renamed to curScriptState. This change allows Script and its subclasses to be combined (with multiple inheritance) with Thing (which defines its own meaning for getState) and with Actor (which defines its own curState). It's sometimes desirable to combine Thing or Actor with a Script subclass, because that's an easy way to attach simple scripting behavior to these objects.

    Existing game code that overrides or calls getState or curState for a Script object (including any EventList subclass) will need to be changed to use the new name. You should scan your source code for occurrences of these names, and rename them as needed. Note that you should not rename curState properties that pertain to Actor objects, since Actor.curState has not been renamed.

    In most cases, game code won't have any reason to override or access these script properties at all, so most game code should be unaffected by this change. Game code usually just defines instances of the library EventList subclasses using templates, and such code won't be affected by this change.

    Minor compatibility-breaking change: The library objects redirectTravelIn, redirectTravelOut, and redirectTravelDown have been renamed to askTravelIn, askTravelOut, and askTravelDown, respectively. The names changes are for consistency with the naming of the new class AskConnector.
    The PresentLater class has a new property, initiallyPresent, that lets you override the standard behavior of the class, which is to make the object initially absent from the game map. This new property is set to nil by default; if you override it to true, the object will be initially present in the game, like any ordinary object. This new property would seem to defeat the purpose of the class, but it actually extends the class to situations where an object comes and goes during the game, but starts out present. Using PresentLater with initiallyPresent set to true, you can still use all of the PresentLater showing and hiding mechanisms, such as the key-based and conditional methods; this is often more convenient than moving objects in and out of the game map individually and manually.
    The Attachable class now automatically notifies itself and each of its attachments whenever the Attachable is carried by a traveler, by calling the new method travelWhileAttached(). The method does nothing by default, but games can override it as needed to enforce conditions or carry out side effects when an attached object is moved indirectly via travel.
    The Attachable class now has a way of specifying the "direction" of an attachment relationship, for the purposes of descriptive messages. Formerly, attachments were always described symmetrically: if A was attached to B, then examining A generated a status message along the lines of "A is attached to B," while examining B generated a message like "B is attached to A." This didn't always work; "the note is attached to wall" is fine, but "the wall is attached to the note" isn't quite right. The new feature lets you specify that the note is always said to be attached to the wall, never vice versa.

    The direction of a relationship is specified by the new Attachable method isMajorItemFor(obj). By default, this method always simply returns nil, which means that there are no "major" items by default, which makes all attachment relationships symmetrical by default. If you wish, you can override isMajorItemFor() so that it returns true in some cases. When A.isMajorItemFor(B) returns true, the relationship will always be described such that B is said to be attached to A: examining A will yield "a B is attached to the A," while examining B will show "the B is attached to an A."

    A few new methods have been added to support the new feature; these are mostly for internal use, but could potentially be used by a game to fine-tune the way attachments are listed. The new method isListedAsAttachedTo(obj) lets the object indicate whether or not 'obj' is listed among the things 'self' is attached to; by default, this returns true if 'obj' isn't permanently attached and 'self' isn't the "major" item for 'obj'. The new method isListedAsMajorFor(obj) is essentially the major-list counterpart: it indicates whether or not 'obj' is listed among the things attached to 'self' when 'self' is described. By default, this method returns true if 'self' is the "major" item for 'obj', and obj.isListedAsAttachedTo(self) returns true (that is, 'obj' thinks it should be listed as attached to 'self'). Finally, majorAttachmentLister returns the lister to use for the items attached to 'self' for which 'self' is the "major" item in the relationship; by default, this uses a MajorAttachmentLister instance.

    The new classes Underside, RearContainer, and RearSurface make it easier to model situations where one object is behind or under another. Underside can be used to model the space under an object, or for the bottom surface of an object. RearContainer models the space behind an object, and RearSurface models its back surface.

    In addition to letting you set up "under" and "behind" relationships among objects initially, these new classes support the PUT UNDER and PUT BEHIND commands to let actors add new contents under and behind the objects. (The PUT BEHIND command is also new in this release.) An Underside can have new objects added under it with PUT UNDER, and a RearContainer or RearSurface can have new contents added with PUT BEHIND. These commands can optionally be disallowed for a given Underside or RearContainer/RearSurface: override the properties allowPutUnder and allowPutBehind, respectively. The new classes derive from BulkLimiter, so you can use the usual BulkLimiter properties to control the individual and total bulk allowed under and behind the objects.

    ComplexContainer has been extended to support these new classes. The new ComplexContainer property subUnderside can be set to an Underside object representing the space under or bottom surface of the complex container; the new property subRear can be set to a RearContainer or RearSurface representing the space behind or back surface of the complex container. PUT BEHIND and LOOK BEHIND commands on the complex container are routed to the subUnderside; PUT UNDER and LOOK UNDER are routed to the subRear; and both subcomponents are included in the regular LOOK AT display.

    These additions were adapted from work originally done by Eric Eve.

    It's now easier to set up objects so that they're initially inside the component sub-containers of a ComplexContainer. In the past, if you wanted to create an object in your source code so that it was initially inside a ComplexContainer's internal container, for example, you had to explicitly set the object's 'location' property, rather than using the "+" syntax. Now, you can use the "+" syntax as long as you add a little extra information: give each contained object a new property, 'subLocation', and set it to the property of the component sub-container you want to use as the initial location. For example, here's how you'd create a washing machine as a complex container, with a blanket inside and a laundry basket on top:

      + washingMachine: ComplexContainer 'washing machine' 'washing machine'
        subContainer: ComplexComponent, Container { /* etc */ }
        subSurface: ComplexComponent, Surface { /* etc */ }
      ;
    
      ++ Thing 'blanket' 'blanket'
        subLocation = &subContainer
      ;
    
      ++ Container 'laundry basket' 'laundry basket'
        subLocation = &subSurface
      ;
    

    The blanket and the laundry basket are nominally directly inside the washing machine itself, according to the "+" syntax, but their 'subLocation' settings ensure that they end up in the desired component sub-containers during initialization.

    Note that 'subLocation' is only intended for initialization, so the library automatically sets subLocation to nil for each object right after the initial setting is used. This helps avoid any unpleasant surprises should the object be moved into a different ComplexContainer later on. When you're moving objects around on the fly in your program code, there's no reason to use subLocation at all; instead, just specify the appropriate component as the explicit destination of the moveInto: towel.moveInto(washingMachine.subSurface), for example.

    The Openable class has a new property, openingLister, that specifies the lister to use to display the list of items revealed when the object is opened. By default, this is set to the openableOpeningLister object. Individual objects can override this if they want to customize the message listing the revealed items.

    In addition, the object formerly called openingLister has been renamed to openableOpeningLister.

    The new CommandTopic makes it easier to generate a response when an NPC receives a particular command. Just create a CommandTopic, with a list of the Action classes you wish to match. Actions are matched on class alone; if you want to match something more specific, such as matching a particular direct object of a particular action, you'll have to create a custom matchTopic() method for your CommandTopic object. If you want to create a default response that matches any action, use a DefaultCommandTopic.

    CommandTopic works like any other TopicEntry object, so you can put these in ConvNode, ActorState, and Actor topic databases to provide responses under the specific conditions you need to match.

    As part of this change, ActorState.obeyCommand() now invokes handleConversation() to look for a response to the command, specifying the Action object as the topic and 'commandConvType' as the conversation type object. This looks as usual in the ConvNode, ActorState, and Actor conversation topic databases for a CommandTopic to handle the response, or for a DefaultAnyTopic if there's no CommandTopic.

    The parser now gives more weight to explicit owners when resolving possessives. If a noun phrase is qualified by a possessive ("my book"), and the noun phrase is ambiguous even with the possessive qualifier (because the possessor is carrying more than one matching object), the parser will now choose an object with an explicit "owner" property over one without an explicit owner. When the parser resolves this kind of ambiguity by choosing an explicitly owned object over one that's merely being carried, it will mark the chosen object with the UnclearDisambig flag to indicate that it's a best guess.
    The library no longer treats "brightness" (the sensory intensity of light, sound, etc.) as diminishing over a "distant" containment boundary. In the past, distance diminished brightness by one level. While this was arguably physically realistic over large distances, in practice it proved to be undesirable for the typical scales in IF settings, where "distant" is usually used to indicate that something's tens or hundreds of feet away, not miles.
    The sense mechanism has a new feature that allows an object to take on its own special sensory status. An object can use this feature to override the normal sense path mechanism and decide for itself how it appears to the senses. The change is comprised of two new methods.

    First, when building the sense information table, the library now calls a new method, addToSenseInfoTable, on each object connected by containment to the source object. This method by default does what the library has always done: it evaluates the temporary sense properties set up by the containment path traversal, and adds a SenseInfo object to the table based on the sense properties. An objects can now override this method to add a table entry based on a different sensory status.

    Second, when finding the containment paths from one object to another, the library calls the new method specialPathFrom on the target object if (and only if) no ordinary containment path can be found. This method can supply its own custom containment path. This allows an object to exist outside of the normal containment hierarchy but still get a chance to represent its connection to the normal containment hierarchy as it sees fit.

    When a CollectiveGroup object has no location, it now takes on the sensory status of its individuals. It will take on the "best" sense status of any individual in scope, which is the one that's most transparent and (transparencies being equal) has the highest ambient level.

    This change makes it much easier to work with CollectiveGroup objects, because it makes a CollectiveGroup as visible, audible, etc. as any of its individuals.

    Note that this change doesn't affect CollectiveGroup objects that have a normal location. These objects already participated in the sensory model in the normal manner, and will continue to do so. This change only affects group objects with no location.

    The handling of the "catch-all" Default handlers has changed slightly. The sequence of processing is now as follows:

    • The iobjFor(All) and dobjFor(All) handlers run first, if defined.
    • The iobjFor(Default) and dobjFor(Default) handlers run next, if they "override" the verb-specific handlers.
    • The iobjFor() and dobjFor() handlers for the specific verb run next, unless the corresponding Default handlers override them

    In the past, the 'verify', 'check', and 'action' methods for the Default handlers were called in addition to any verb-specific versions of the methods. For example, if an object defined a dobjFor(Default) with an action() method, and the player entered an OPEN command on the object, the Default action handler ran, and then the dobjFor(Open) action() method also ran. In most cases, this made no difference, because the base Thing class doesn't even define action() or check() handlers for most verbs, since it disallows most verbs in the verify() stage.

    With this change, the Default handlers now run instead of the verb-specific handlers, when the Default handlers run at all. The rules about when Default handlers override verb-specific handlers, and vice versa, haven't changed. A Default handler still overrides any verb-specific handler inherited from a base class, and any verb-specific handler defined in the same class as the Default handler or in any subclass still overrides that Default handler.

    Note that the relative order of the All and Default handlers has been reversed: the All handler now always runs first. This makes the sequence of methods proceed from most general to most specific.

    If an 'exit' occurs within a PreCondition object's checkPreCondition() method, it's now treated as a failure of the enclosing action that invoked the precondition. The normal pattern that most preconditions use is as follows: first, evaluate if the condition is met; second, if the condition isn't met, try an appropriate implied action to try to bring it into effect; third, re-test the condition to see if the implied action did what it was meant to; and fourth, if the condition still doesn't apply, use 'exit' to terminate the triggering action. It's this use of 'exit' that's now interpreted as a failure of the enclosing action.

    Note that this doesn't apply if 'exit' is used within a nested implied action; this only applies if 'exit' is used within the precondition's checkPreCondition() method itself.

    The library now defines a template for the Achievement class, taking a double-quoted string for the achievment's description text.
    The new function finishGameMsg() makes it easier to show one of the conventional end-of-game messages, such as "*** YOU HAVE DIED ***" or "*** YOU HAVE WON ***". This function takes two parameters: a message specifier, and a list of extra finishing options. The extra options are the same as for the existing function finishGame().

    The message specifier can be one of several standard, pre-defined objects, or it can simply be a string to display. The pre-defined object ftDeath, ftVictory, ftFailure, and ftGameOver display messages for death of the player character ("YOU HAVE DIED"), victory ("YOU HAVE WON"), failure ("YOU HAVE FAILED"), and simply "game over", respectively. Alternatively, you can make up your own messages for unconventional cases ('YOU HAVE SUFFERED A FATE WORSE THAN DEATH', say) simply by specifying a string. Your string will be displayed surrounded by "***" sequences to yield the conventional formatting, or with other sequences that might vary by language. You can also pass nil as the message specifier, in which case no message at all is displayed.

    The value of gameMain.maxScore (formerly libScore.maxScore) is now allowed to be nil. In addition, the default value in GameMainDef is now nil. When this value is nil, the SCORE and FULL SCORE commands simply won't mention a maximum score for the game.

    If your game has complex scoring that makes it difficult or impossible to state a maximum possible score, or if you simply don't want to give this information to the player, simply set maxScore to nil in your gameMain object. The library uses nil as the default because there's no reason for the library to assume that a game will have a particular maximum score (the library formerly used a default value of 100, but this was overly presumptuous).

    The library's scoring system has a new, optional usage style that provides two benefits. First, it lets you define the number of points an Achievement is worth as part of the Achievement object, better encapsulating the information about the Achievement. Second, the library can use this new information to automatically compute the maximum possible score in the game, saving you the trouble of figuring this out manually (and of keeping the stated maximum in sync with the game as you make changes).

    If you explicitly set the maxScore property in your gameMain object, the library will not automatically compute the maximum score. Your explicit maxScore setting always overrides the computed value.

    To take advantage of the new capabilities, simply define the new 'points' property for each Achievement object you create. Then, rather than calling addToScore() or addToScoreOnce(), both of which take a parameter specifying the number of points to award, call the new Achievement methods awardPoints() or awardPointsOnce() instead. For example, suppose you have some existing code that looks like this:

      vase: Thing
        handleBreakage()
        {
          // ... do the real work here...
    
          scoreMarker.addToScoreOnce(10);
        }
    
        scoreMarker: Achievement { "breaking the base" }
      ;
    

    This code defines a nested object called 'scoreMarker' to describe the scoring item, and the 'handleBreakage' method awards the Achievement object, assigning it 10 points in the score. Using the new style, you'd change the code above to look like this instead:

      vase: Thing
        handleBreakage()
        {
          // ... do the real work here...
    
          scoreMarker.awardPoints();
        }
    
        scoreMarker: Achievement { +10 "breaking the base" }
      ;
    

    The "+10" in the nested Achievement definition assigns the object a value of 10 in its 'points' property (using a template defined in adv3.h), indicating that the item is worth 10 points in the score. Rather than specifying this in the code that awards the Achievement, we define it as part of the Achievement object itself. The code that awards the points doesn't need to specify the number of points; it just calls the awardPoints() method of the Achievement object, which awards the points defined in the object. This makes the code easier to read, since you can see the description of the scoring item and the number of points it's worth in one place.

    To take advantage of the library's automatic computation of the maximum possible score, you have to follow a few rules:

    • Use only Achievement objects to award points. Never call addToScore() with a string value to award an ad hoc scoring item.
    • Set the 'points' property of each of your Achievement objects to the number of points the item is worth.
    • If an Achievement can be scored more than once, also set the 'maxPoints' property to the maximum number of points the item will contribute to the score. This is usually just 'points' times the number of times the item can be awarded.
    • Only define Achievement objects statically. Never use 'new Achievement' to create an Achievement dynamically. (If you create new Achievements dynamically, the library won't know about them at start-up time, obviously, so it can't count their contributions to the maximum score.)
    • Always award Achievements through their awardPoints() or awardPointsOnce() methods. (This ensures that each Achievement is scored with the number of points specified in its 'points' property.)
    • Achievements can't be mutually exclusive. That is, there must exist at least one solution of the game in which every Achievement object is awarded. (If you have Achievement objects that represent different solutions to the same puzzle, so that only one of these achievements can actually be awarded in any given traversal of the game, the library would incorrectly count the score contributions of all of the alternatives in the maximum score. The correct maximum would count only the single highest score among the alternatives. The library has no way of expressing mutual exclusion among Achievements, so if your game has such a situation, you'll need to set libScore.maxScore manually.)

    If you follow these rules, the library will accurately compute the maximum score value, so you don't need to bother figuring out how many points are in the game yourself, and you don't need to worry about initializing libScore.maxScore in your game's start-up code.

    If your game can't follow the rules above (because you have alternative solutions to puzzles that assign different scores, for example, or because your game has alternative paths with different maximum scores), you'll need to figure the maximum score manually, and set the property 'maxScore' to this value in your 'gameMain' object. Explicitly setting gameMain.maxScore in your code will override the library's automatic computation.

    The finishGame() and finishGameMsg() functions now display the current score (using the same message that the SCORE command normally displays) if the finishOptionFullScore option is in the extra options list. In fact, these functions will announce the score if any extra option has the property showScoreInFinish set to true; finishOptionFullScore has this option set to true, so including it in the extra options list causes the score to be announced. If you want the end-of-game announcement to include the score, but you don't want to offer the FULL SCORE option, you can include finishOptionScore in the extra options list; this is an "unlisted" option, so it doesn't add anything to the list of offered option list, but it does cause the end-of-game announcement to include the score.
    Actor.holdingDesc (which provides the default description of what an actor is holding as part of processing an Examine action on the actor) now uses pretty much the same handling that Thing uses when it lists the contents of an object. The only difference is that Actor.holdingDesc still uses the special holdingDescInventoryLister as the contents lister. One important consequence of this change is that examining an actor marks the actor's contents as having been seen by the player character, just as examining an ordinary object marks the object's contents as seen. Another is that the scope of objects included in the listing is limited to objects the point-of-view actor can actually see. In the past, the examined actor listed everything in "inventory scope," which includes objects that are directly held even if they're not currently visible; this is correct for an inventory listing from the point of the view of the actor taking the inventory (because the actor can presumably identify directly held items by feel, even in the dark), but the new sight-only behavior is more appropriate for examining another actor.

    This change also adds a new Thing method examineListContentsWith(). This is simply a service method that performs the bulk of what Thing.examineListContents() did before, but is parameterized by a lister to use to generate the listing.

    The new class AskConnector makes it easy to define a directional connection when the direction is ambiguous. For example, suppose that you have two doors leading north, and you don't want to use the traditional trick of distinguishing the doors' respective directions as northeast and northwest. (If you had ten doors leading north instead of two, the traditional northeast/northwest trick wouldn't help anyway.) If the player types NORTH, you would like the parser to ask which door to use.

    AskConnector makes this easy. Simply define the room's 'north' as an AskConnector object, and set the AskConnector instance's property 'travelAction' to 'GoThroughAction'. The connector, when invoked for travel, will try running the GoThroughAction, but with a missing direct object; this will make the parser ask the player which door to use with the usual prompt for a missing object: "What do you want to go through?"

    If the direction has a specific set of possible objects, as in our example of two doors leading north, you can further refine the parser's question by specifying the 'travelObjs' property. Set this to a list of the possible direct objects for the action. If you set this property, you should also set 'travelObjsPhrase' to a string giving the noun phrase to use in disambiguous questions: in our example, we might set this to 'door', so that the question becomes "Which door do you mean...?".

    The English library now provides a default verbPhrase for the TravelVia action ('use/using (what)', which is pretty vague, but TravelVia is fairly vague itself). This lets the parser generate usable messages in cases where a TravelVia action elicits a parser prompt, such as when using TravelVia in askForDobj().
    TravelPushable has several small changes.

    First, TravelPushable.movePushable() now uses moveIntoForTravel() to move the object, rather than moveInto(). This corrects a problem that occurred when the starting and destination locations were connected by a sense connection (such as a distance connector).

    Second, movePushable() and describeMovePushable() each now take an additional argument giving the connector being traversed.

    Third, the new method beforeMovePushable() gives the pushable object a chance to do any necessary work just before the travel takes place. This is especially useful for adding a message describing anything special that happens when pushing the object out of its current location. By default, this routine does nothing.

    The new Traveler methods canTravelVia() and explainNoTravelVia() allow a traveler to disallow travel via a particular connector and/or to a particular destination. These new methods essentially provide a complement of the "travel barrier" mechanism, which allows a connector to disallow travel by a particular traveler. The new methods are useful becuase it's often more convenient to tie a travel condition to the traveler than to the connector. For example, you might want to implement a condition that prohibits a particular actor from going outside; you could do this by defining a canTravelVia() method on the actor that returns nil if the destination is an OutdoorRoom.
    The new BasicLocation methods enteringRoom(traveler) and leavingRoom(traveler) make it more convenient to write code that responds to an actor's arrival into or departure from a room. These methods are called from travelerArriving() and travelerLeaving(), respectively; they differ only in that (1) they have simpler parameter lists, leaving out information that frequently isn't needed to write an arrival/departure event handler, and (2) the base class implementations don't do anything, so there's no need to use 'inherited' to inherit up the base class behavior. These new methods are purely for convenience, to make the very common task of writing arrival/departure event handlers a little less work.
    The new class ContainerDoor makes it easy to create a door for a container, if you want the door to act like a separate object in its own right. This works with the ComplexContainer class: create a parent as a ComplexContainer, and inside it create a ContainerDoor and the normal secret actual Container. The door will redirect commands like open, close, lock, and unlock to the secret inner container, but will still appear as a separate object.
    In the past, the base TravelConnector methods describeArrival() and describeDeparture() simply showed the most generic travel messages ("Bob is leaving the area"). This meant that replacing a simple room connection with a TravelMessage connector meant giving up the directional message that a normal room connection generated ("Bob is leaving to the east").

    Now, the directional message is the default in the base TravelConnector class. If there's a direction property linked to the connector from the origin (for a departure) or destination (for an arrival), the base TravelConnector messages will now show the directional version of the message. The generic, non-directional message will only be shown when no directional link can be found. Note that this doesn't affect the more specific types of connectors and their custom messages, so stairs will still say "Bob goes up the stairs," doors will still say "Bob leaves through the wooden door," and so on.

    The new TravelConnector method isConnectorPassable() indicates whether or not a traveler can pass through the connector. This can be used in game code that probes the map to determine if a connector can be traversed in its current state.
    In the past, the noTravel object, and the NoTravelMessage and FakeConnector classes, incorrectly displayed their special messages on attempted travel even when it was dark in the room. In the dark, the "dark travel" message should take precedence. This has been corrected. (The problem was that these classes overrode the dobjFor(TravelVia) check() method to bypass the normal darkness check. The unnecessary override has been removed, so the standard darkness check is now made, so the dark-travel message is now shown instead of any special message.)
    The BasicLocation methods getTraveler() and getPushTraveler() have been renamed and moved into Thing. These methods are now called getLocTraveler() ("get location traveler") and getLocPushTraveler() ("get location push traveler"), respectively.

    This change is necessary to allow actors to hold other actors, and to allow intermediate containers within rooms (that is, to allow a Room to hold an arbitrary Thing subclass, which in turn holds a NestedRoom). Actor has methods with the names getTraveler() and getPushTraveler(), but these methods have a different purpose than the old BasicLocation methods: the Actor methods get the traveler when the actor is initiating the travel, and the old BasicLocation methods get the actual traveler when a traveler within the location initiates travel. The use of the same name created a conflict between these different purposes, so one or the other set had to be renamed. In addition, limiting these methods to BasicLocation prevented ordinary Things from holding actors who could travel; adding them to Thing corrects this.

    The logic in Vehicle.getTraveler() that determines whether to move the vehicle or the traveler within the vehicle has changed slightly. Rather than basing the decision on travel barriers, the routine now moves the traveler rather than the vehicle only if the connector is contained within the vehicle - that is, the connector leads somewhere from inside the vehicle. The connector is considered to be inside the vehicle if (1) it's a physical object (a Thing) that's inside the vehicle, or (2) one of the direction properties of the vehicle is set to the connector. (The old logic had the undesirable side effect of implicitly removing the traveler from the vehicle if the vehicle couldn't cross a barrier, which was really too automatic.)
    In the past, the way push-travel actions was handled caused a problem in certain cases involving remapping of actions. The problem showed up most readily with StairwayUp and StairwayDown objects; specifically, PUSH x DOWN acted differently than PUSH x DOWN y, where y was a StairwayUp or StairwayDown and the room's 'up' or 'down' pointed to y. These commands obviously should have been equivalent.

    Stairways were the only library objects that exposed the problem, but it was possible to define your own objects with the same bad behavior. In general, the problem occurred whenever a two-object push-travel action (PUSH x UP y, PUSH x THROUGH y, PUSH x INTO y, etc.) was attempted, and the indirect object's handler for the corresponding travel action (ClimbUp, GoThrough, Enter, etc.) was defined using asDobjFor(), and the handler for the target action of the asDobjFor() was itself defined using a remapTo().

    The problem has been fixed, so any two-object push-travel action should now be handled equivalently to the corresponding one-object (directional) push-travel action on the same travel connector.

    The Chair, Bed, and Platform classes can now all allow standing, sitting, and lying on the object. In the past, Chair only allowed sitting, Bed allowed lying and sitting, and Platform allowed all three. The set of postures allowed can now be customized for each individual object.

    The new property allowedPostures contains a list of the postures that the object allows. By default, Chair allows sitting and standing, and Bed and Platform allow sitting, standing, and lying. You can override allowedPostures for an individual Chair, Bed, or Platform object to add or remove allowed postures. When a posture isn't in the allowed list, it will be ruled as "illogical" in the verification stage.

    In addition, the new property obviousPostures contains a list of the postures that are "obvious" for the object; these are the postures that are most natural for the object. The obvious postures are enumerated separately from the allowed postures to control defaulting: if a posture is allowed but not obvious for an object, then the corresponding command will be ruled as "nonObvious" in the verify stage, ensuring that the command will only be accepted if stated explicitly (that is, the player will be able to perform the command, but the parser won't automatically guess the command based on incomplete information from the player). By default, it's obvious to sit on a chair, to sit or lie on a bed, and to sit, lie, or stand on a platform.

    The three separate classes are still three separate classes for a couple of reasons. First, each one has a primary, "natural" posture associated with it: sitting on a chair, lying on a bed, standing on a platform. This primary posture is the one that the library will assume for implied actions and incomplete commands. Second, Platform differs from the other two in that objects dropped while standing on a platform land on the platform, whereas objects dropped while on a chair or bed land in the enclosing room.

    Note that a posture that isn't allowed by allowedPostures will be treated as illogical. If you want to create an object for which a given posture makes physical sense, but isn't allowed for some other reason (character motivation constraints, for example), you should include the posture in allowedPostures and then disallow the corresponding command using the check() routine.

    NestedRoom now defines 'out' to use the special 'noTravelOut' connector, which will automatically treat an OUT command as a GET OUT OF command while in the nested room. Without this, the 'noTravelOut' would typically be "inherited" from the enclosing room anyway, but not always: if the enclosing room defined its own OUT, the nested room would pick up that one instead, which isn't usually appropriate when within a nested room. This change ensures that nested rooms behave consistently even when nested within rooms with their own explicit 'out' connections.
    The HighNestedRoom class now prohibits exiting the room, by default. It does this by setting the exitDestination property to nil; this indicates that the room has no natural exit location, so it's not possible to leave it. HighNestedRoom generates an appropriate message ("It's too long a drop to do that from here") when an attempt to exit the location fails.

    The base NestedRoom now checks, in the GetOutOf action's check() handler, that there is a non-nil exitDestination. If there's not a valid exit destination, the check() handler calls the new method cannotMoveActorOutOf() to display an appropriate message, then terminates the command with 'exit'.

    By default, NestedRoom now uses the new travel connector nestedRoomOut for its "out" link, rather than noTravelOut as it did in the past. The nestedRoomOut connector works just like noTravelOut, except that nestedRoomOut is "apparent" to characters, so it shows up in the EXITS list. Since OUT is usually a valid travel command while in a nested room (the command exits the nested room), it makes sense to list OUT explicitly as a possible direction while in a nested room. To override this for a particular nested room, just set 'out' to noTravelOut; to change this behavior for all nested rooms, do the same thing with 'modify NestedRoom'.
    The new Floorless class is a mix-in that can be combined with any other type of Room class to create a floorless version of the room. Mix Floorless in ahead of the Room base class in the superclass list of the room you're creating; Floorless will, among other things, subtract any default floor/ground object from the base room's roomParts list.

    The existing FloorlessRoom class has been retained, but is now simply a convenience class that combines Floorless and Room. This provides the same behavior as the old FloorlessRoom class, so existing code should not be affected.

    Some fine-tuning has been applied to announcements for cascading implied actions in certain cases. When a series of nested implied actions fails, the parser now announces only the first of the series. For example, in the past, if a travel action initiated an OPEN DOOR which initiated an UNLOCK DOOR which failed, the parser showed something like this: (first trying to unlock the door and then open it). The parser now shows only this: (first trying to unlock the door).

    The reason for dropping the extra announcements is that they don't really tell the player anything useful, so they're just unnecessary verbosity. Because of the recursive relationship of the implied action in these cases, the first action is nested in the second, which is nested in the third, and so on; so the second and subsequent actions all failed as a direct result of the first one failing (in our example, the OPEN fails because it requires the UNLOCK to succeed, so when the UNLOCK fails the OPEN must also fail). This means that we never actually get around to trying the second and subsequent actions; when the first action fails, we give up on trying any of the others. The only thing they tell the player is why the failed action was attempted at all (in our example, the implied OPEN explains why we're attempting the implied UNLOCK). But the rationale in these cases is almost always obvious to the player, so even this isn't a very good reason to keep the extra announcements.

    This behavior can be controlled via implicitAnnouncementGrouper.keepAllFailures. The property is nil by default; to list the entire stack of failures for these cases, change it to true.

    The extra "testing" step for opening Lockable objects has been removed. This extra step was introduced in 3.0.6m to make the cascade of implied actions more plausible for these cases, but the extra verbosity was really too much, so it's been removed in the interest of simplicity.

    Instead, a Lockable now tests a new method, autoUnlockOnOpen(); if this returns true, then OPEN implies UNLOCK, otherwise it does not. This means that, if autoUnlockOnOpen returns nil, and the object is locked, then an OPEN command will simply fail with an error message ("the door seems to be locked"), and the player will have to enter an explicit UNLOCK command.

    The new autoUnlockOnOpen() method is defined as follows:

    • For Lockable, the method returns the value of the lockStatusObvious property. This means that OPEN implies UNLOCK if the lock status is readily observable, and requires a separate, explicit UNLOCK if not.
    • For KeyedLockable, the method returns true if the inherited value is true, OR if the actor is carrying a known key for the object. By default, a KeyedLockable automatically remembers keys as known once they're successfully used, so once an object is successfully unlocked with a key, an OPEN command will automatically imply UNLOCK on subsequent attempts, as long as the actor is carrying the same key. This is a concession to playability, in that it eliminates the need to spell out every step of operating a door or other keyed lockable after the first time through, but it's also arguably more realistic: the actor knows from experience how to operate the door, so we can see the automatic UNLOCK as merely leaving some boring details out of the narration.

    Some authors might always prefer the automatic UNLOCK to forcing a player to type a separate UNLOCK command for objects with non-obvious locking status. Which way you prefer is a matter of taste. On the one hand, the extra UNLOCK command is a little annoying to players, and is exactly the sort of thing the precondition mechanism was created to avoid. On the other hand, the automatic UNLOCK is weird when the lock status isn't apparent, because until the OPEN fails, we have no way of knowing that the object was locked in the first place and thus no reason to try the implied UNLOCK; it's an instance of the parser revealing game-state information that the player character isn't supposed to know. It also could be seen as actually detracting from playability by making the game do too much automatically, taking away a certain amount of control from the player (the player could think: "I didn't even know that door was locked; if I had, I wouldn't have tried to unlock it right now.")

    By default, the library implements the more realistic but also more tedious behavior, requiring a separate UNLOCK to OPEN a locked object whose lock status isn't obvious. If you prefer to make UNLOCK automatic on OPEN for a given object, simply override autoOpenOnUnlock() to return true unconditionally for that object; if you prefer to make UNLOCK automatic for all objects, modify Lockable to make autoOpenOnUnlock() return true in all cases.

    The implied command behavior of keyed-lockable objects has been changed slightly. In the past, attempting to OPEN a keyed-lockable triggered an implied UNLOCK, which asked for a key. Thus, a command like OPEN DOOR, or even GO NORTH, could be answered with a question ("What do you want to unlock it with?"). This was a little awkward because the player never actually said in so many words that they wanted to unlock the door. Now, keyed-lockable objects simply point out that a key is required to open the lock:

      >go north
      (first trying to unlock the iron door)
      You need a key to unlock the door.
    

    Note, however, that if the actor knows which key to use, and one of the known keys is in sight, then the command will be allowed to proceed implicitly. In these cases, it won't be necessary to ask for the key, since the actor's knowledge of the key allows it to be supplied automatically as a default.

    The Keyring class now follows the normal Thing conventions for mentioning the contents of the keyring. Formerly, Keyring.desc generated the list of contents, using a lister defined in the property 'examineLister'. Now, Keyring has no separate 'desc' method; instead, the listing is generated by the standard Examine code inherited from Thing, and the 'examineLister' property has been renamed to 'descContentsLister' to accommodate the inherited Thing methods.
    The new class CustomImmovable makes it easy to create an Immovable that uses a single customized message to respond to all attempts to move the object. Just override cannotTakeMsg, and the "move" and "put in" messages will use the same message. The new class CustomFixture does the same thing for Fixture.
    The new class FueledLightSource abstracts some of the functionality that was formerly in Candle, to create a base class for objects that provide light using a limited fuel supply. The new base class doesn't assume that it's something that burns, as Candle does, so you can use the new class to create things like limited-use flashlights. Candle is now based on the new class. This change shouldn't affect any existing game code; the implementation of Candle has merely been rearranged internally to the library, so no changes should be required to existing code.

    An additional improvement in the new FueledLightSource class is that the fuel source is no longer assumed to be the object itself. This lets you model the fuel source as a separate object; for example, you could use a separate battery object as the fuel source for a flashlight, allowing a character in the game to swap out a dead battery for a new one. Simply override the 'fuelSource' property of your FueledLightSource object to specify the object that provides the fuel. By default, the fuel source is still 'self', so Candle objects in existing source code won't need any changes.

    The tryMakingRoomToHold method in Actor now checks each object it's about to move into a "bag of holding" to make sure the object that's to be held isn't inside the object to be moved. This ensures that the attempt to make more room to hold things doesn't inadvertantly move the object to be held out of reach, defeating the purpose of making room to hold it. This change ensures that the object to be held stays where it is, since all of its containers stay where they are.
    UnlistedProxyConnector.ofKind now returns true if the caller is asking about UnlistedProxyConnector, and also returns true if the caller is asking about the class of the underlying connector. This makes a proxy look like its underlying connector and also like a proxy, which is consistent with the rest of its behavior.
    The new DefaultTopic property 'excludeMatch' can be set to a list of topics (Thing or Topic objects) to exclude from the catch-all match. By default, this is just an empty list, so nothing is excluded. Individual DefaultTopic objects can provide a list of objects to exclude from the catch-all. This is useful when you're creating a DefaultTopic for a ConvNode or ActorState, but you want one or more specific topics to be referred to the enclosing topic database for handling. By including the topics in the excludeMatch list, you ensure that they won't be caught in the catch-all DefaultTopic, which will let them propagate to the enclosing topic database.
    ConvNode.npcContinueConversation() now returns true if anything was displayed in the course of the method, nil if not. This lets a caller determine if the NPC actually had anything to say.

    ActorState.takeTurn() now uses this information to consider the NPC to have finished its turn if the ConvNode added a conversational message. In the past, merely having a non-nil ConvNode was enough to make takeTurn() consider the turn to be taken entirely by the ConvNode; now, the turn is only considered finished if the ConvNode actually wanted to say something. This allows ordinary actor-state background scripts to continue running even when a ConvNode is active, as long as the ConvNode doesn't generate any NPC conversation messages.

    The nestedActorAction() function now sets a visual sense context for the new actor. (In the past, the nested action incorrectly executed within the same sense context as the enclosing action, which allowed actions that shouldn't have been visible to be reported, and vice versa.)
    If an implied action announcement comes immediately after other report messages for an action in which the implied action is nested, the command transcript now automatically inserts a paragraph start just before the implied action announcement. It's often desirable to invoke a nested action in the course of handling a main action, and in some cases the nested action can trigger implied actions of its own. This new transcript feature makes the spacing look better in these cases when the main action reports its own results before invoking the nested action. Similarly, if an implied action announcement occurs after a result message from a previous implied action, a paragraph break is inserted before the second implied announcement. This improves readability in these cases.
    The CommandTranscript method summarizeAction() now interacts properly with conversational responses. A change in 3.0.6m caused a problem when attempting to summarize a series of conversational messages (generated by TopicEntry activation); specifically, the summarizer didn't recognize the new "in-band" conversational boundary markers that the conversation manager started generating in 3.0.6m. The summarizer now handles the conversation markers properly.
    The AgendaItem class has a new property, initiallyActive, that indicates that an agenda item is active at the start of the game. During initialization, the library will automatically add each initially active agenda item to its actor's agenda (by calling addToAgenda() on the actor).
    The new AgendaItem property agendaOrder makes it easy to control the priority order of agenda items. The agenda list is now sorted in ascending order of the agendaOrder values; this means that the lower the number, the earlier the item will appear in the agenda list. Actors choose the first item in the list that's ready to run, so ready-to-run agenda items will always be executed in order of their agendaOrder values. By default, agendaOrder is set to 100; set a lower value if you want the item to go earlier than other items, a higher number if you want it to go later. If you don't care about the ordering, you can leave the default value unchanged.

    Note that readiness is more important than agendaOrder. Suppose that AgendaItem A has a lower agendaItem value, and thus goes earlier in the list, than AgendaItem B. Despite this list order, if B is ready to run and A is not, B will be executed.

    The new class SuggestedAskForTopic can be mixed into an AskForTopic object's superclass list to add the "ask for" to the topic inventory. This works in parallel with the other SuggestedTopic subclasses.
    In the past, AltTopic didn't work properly with SpecialTopic. This has been corrected; you can now create AltTopic children of SpecialTopic entries, and they'll work as you'd expect.

    In a related change, SpecialTopic now inherits from SuggestedTopicTree, rather than SuggestedTopic as it did in the past. This ensures that a special topic will be suggested in the topic inventory when any of its AltTopic children are active.

    The library now defines template variations that allow defining firstEvents lists with various TopicEntry/ShuffledTextList combinations: TopicEntry, MiscTopic, SpecialTopic, DefaultTopic, AltTopic. Now, if you define an instance of one of these classes, your definition can include two lists: the first will be the firstEvents list, and the second will be the eventList list.
    The new DefaultAskForTopic class lets you define a default response to an ASK FOR command. This new class is parallel to the other DefaultTopic subclasses.
    The various DefaultTopic subclasses are now arranged in a hierarchy of low "match score" values. In the past, all DefaultTopics used a match score of 1, which generally made them rank lower than any non-default topic, but didn't differentiate among different default topics that matched the same input topic. Now, the single-type defaults (DefaultAskTopic, DefaultTellTopic, etc.) have a match score of 3, the multi-type defaults (DefaultAskTellTopic, etc.) have a match score of 2, and DefaultAnyTopic has a score of 1. This means that a single-type default will take precedence over a multi-type default, which will in turn take precedence over an ANY default. This lets you define a DefaultAskTopic that will handle any ASK command, for example, and a DefaultAnyTopic to handle everything else.
    The initiateTopic() method (in Actor, ActorState, and ConvNode) now remembers that the player character is talking to the NPC. This ensures that commands that check the current interlocutor, such as TOPICS, properly recognize that the characters are talking to one another.
    A problem in the library generated a spurious "you're not talking to anyone" message under certain obscure circumstances involving an automatic "topic inventory" scheduled at the start of a conversation. This is now fixed.
    In some cases, if an actor was in a ConvNode but didn't say anything on a given turn, the actor was incorrectly set as the most recent pronoun. The library was being overly aggressive about setting the pronoun as though the actor had said something, as a convenience to the player to respond to the actor. The library no longer sets the pronoun unless the actor actually says something (i.e., some text is generated in the course of the actor's turn while in the ConvNode).
    In the English language module, the vocabulary initializer now ignores extra spaces after delimiters (such as '/' or '*'). This makes it easier to break up long vocabulary initializers across multiple lines of code. For example:

      + Thing 'big large huge gigantic giant book/tome/volume/
              text/manuscript' 'huge tome'
        // etc
      ;
    

    Note the line break in the middle of the list of nouns. This introduces an extra space into the string, which is now perfectly okay.

    In the English parser, the literal adjective wildcard string has been changed. In the past, "*" (an asterisk within double quotes) was the wildcard character. This made it difficult to match literally an asterisk in the input, so the wildcard is now the character \u0001 (i.e., Unicode code point 1), which is highly unlikely to be used in input.
    In the English parser, 'miscVocab' was used as a token property instead of 'miscWord' in a few places. The correct token type should have been 'miscWord'; this has been corrected.
    The menu system now "wraps" up-arrow and down-arrow keys at the top and bottom of a list of menu items; that is, if the first menu item is selected and user presses the up-arrow, the selection moves to the last menu item, and a down-arrow at the last item selects the first item.
    The library now provides a template for MenuTopicItem, with slots for the title string, an optional heading string, and the menu contents list.
    When a locked door with lockStatusObvious was examined, a message indicating its status (locked or unlocked) was meant to be displayed, but wasn't. This has been corrected.
    A recent change to OOPS processing (in 3.0.6j) introduced a bug: typing OOPS after a misspelling in a response to an interactive parser query didn't work. For example, if your answer to a disambiguation question ("which book do you mean...") contained a typo, you couldn't use OOPS to correct that typo. This now works properly again.
    Typing AGAIN after a SpecialTopic command didn't work in the past. This has been corrected. AGAIN can now be used to repeat a special topic, as long as the same comamnd is still valid as a special topic; if the command isn't a valid special topic any longer, the parser will simply say that the command cannot be repeated.
    The new Thing method expandPronounList() allows an object to effectively reverse the effects of filterResolveList() when a pronoun refers to the object in a subsequent command. The parser calls this new method on each of the objects in the "raw" binding for a pronoun (that is, the set of program objects that were resolved in the previous command that established the antecedent of the pronoun).

    The Thing and CollectiveGroup objects use this new method to ensure that a collective group's filtering is applied consistently for each command, whether the command uses a full noun phrase or a pronoun to refer to the groupable objects.

    When a noun phrase in a player command refers to an object that has "facets" (as indicated by the object's getFacets() method), and more than one facet of the object is in scope, the parser will now automatically choose only one facet of the object. The parser chooses the facet that has the best transparency in the actor's sight-like senses; if the visibilities are equivalent, then the parser chooses the facet with the best touch transparency; if visibilities and touchabilities are equivalent, the parser chooses one arbitrarily. This change ensures that different facets of the same object will never create ambiguity in the parser, which is important because a set of facets is always meant to implement what the player sees as a single game world object.

    In a related change, the parser's method of choosing a facet as the antecedent of a pronoun has changed slightly. First, the parser now considers all in-scope facets when an antecedent has facets, even if the base antecedent object is itself in scope. Second, the parser chooses which in-scope facet to use based on the same criteria as above: visibility, then touchability. This ensures that when multiple facets are in scope (because of sense connectors, for example), the most readily visible and reachable one will be selected.

    The parser now selects a default antecedant for a pronoun whenever possible, based on the objects in scope. For example, if you type LOOK AT HIM, and you haven't established a prior meaning for HIM (by referring to a male actor with a previous command), or the prior meaning of HIM is no longer valid (because that actor is no longer present), then the parser will assume that HIM refers to a male actor who is present, assuming there's exactly one. If there are no in-scope objects matching the gender of the pronoun, or multiple matching objects are in scope, the parser has no way of guessing what the pronoun means. When a pronoun could only refer to one object, though, the parser will assume that that's the object you meant.
    In 3.0.6l, the parser started accepting HIM and HER to select a unique gendered object from a disambiguation query ("which one do you mean..."). This handling has been improved slightly: the parser now treats HIM and HER as narrowing the choices, rather than requiring a unique match. If more than one of the choices matches the pronoun, the parser narrows the choices to the ones that match and asks for disambiguation help again, this time offering the reduced list of choices.
    A bug in turn timing showed up when an implied command used replaceAction(); due to the bug, such a command didn't consume a turn. This showed up, for example, when a travel command (GO NORTH) triggered an OPEN DOOR command, which in turn triggered an UNLOCK DOOR command, which replaced itself via askForIobj() with an UNLOCK DOOR WITH WHAT? command. This has now been corrected.
    In the various places where nested actions are created (nestedAction, nestedActorAction, remapTo, etc), it's now easier to specify literal and topic objects. In the past, it was necessary for the caller to wrap these types of objects in the appropriate internal parser structures, such as a ResolvedTopic. This is no longer necessary: actions that use special wrappers internally will now automatically create the appropriate wrappers for you.

    In practice, this means that you can simply use a string in any nested action context where a literal is required, and you can use a simple Thing or Topic object in any context where a topic is required. Examples that formerly required special coding, but now work in the obvious fashion:

    • remapTo(TypeLiteralOn, typewriter, 'hello')
    • nestedAction(AskFor, bob, goldKey);
    A remapTo() object argument can now be a string, when the new verb accepts a literal in that position. For example, you can use remapTo(TypeLiteralOn, typewriter, 'hello') to remap to the command TYPE "HELLO" ON TYPEWRITER.
    The parser failed to announce a defaulted object under certain obscure circumstances. In particular, if a remapTo() on a two-object action remapped a command, and the source of the remapping was itself chosen as a defaulted object, the announcement was missing. The announcement will now be generated.
    In libMessages, obscuredReadDesc() and dimReadDesc() incorrectly returned a string rather than displaying a message, resulting in no message being displayed when reading a Readable under dim or obscured visual conditions. This has been corrected; these libMessages methods now display their messages directly.
    In the default English messages, the various messages indicating that an actor is in the room and situated in or on a nested room (such as sitting in a chair, or standing on a platform) have been changed slightly. In the past, these said something like "Bob is here, sitting on the chair." Now, the essentially pointless "here" part has been dropped, so the messages read more simply: "Bob is sitting on the chair."
    The English library message for attempting to take an UntakeableActor referred to the wrong object (the actor instead of the direct object) in one of its clauses. The message has been corrected.
    The English library message listing the newly revealed objects after opening an Openable now uses specialized phrasing when an NPC performs the action: "Bob opens the box, revealing..." The PC version hasn't changed: "Opening the box reveals..."
    The English library's default message for attempting to ask oneself for something (ASK ME FOR...) incorrectly referred to the substitution parameter "{iobj}" (which isn't available with an ASK FOR command, because the second noun phrase is actually a topic phrase). This has been corrected.
    The English version of the INSTRUCTIONS command now automatically senses the presence of certain optional conversation features and mentions the corresponding commands only if they're used in the game. In particular, the TOPICS command will be mentioned only if there are SuggestedTopic instances in the game; the existence of special topics will be mentioned on if there are SpecialTopic instances; and the TALK TO command will be mentioned only if there are InConversationState instances. This reduces the manual work needed to customize the conversation system instructions; if your game uses these features, they'll be mentioned, otherwise they won't.
    A style tag name can now contain digits, as long it starts with an alphabetic character. (In the past, style tag names were only allowed to contain alphabetic characters. This makes style tag naming more consistent with the HTML tag naming rules.)
    The new function overrides(obj, base, prop) makes it easy to test whether or not the given object 'obj' overrides the property 'prop' that 'obj' inherits from base class 'base'. This function returns true if 'obj' derives from 'base' (that is, obj.ofKind(base) returns true), and 'obj' and 'base' get their definitions of 'prop' from different places.

    In the past, the library tested in a few places to see if a given property or method was inherited from a given library base class. This was almost the same test that overrides() performs, but didn't work properly in cases where a library base class had been modified by a game or library extension; when 'modify' is used to extend a class, the defining class for methods defined in the original (pre-modification) version of the class is the original version, not the new version created by 'modify', which takes on the original name. To allow for 'modify' usage, all of the tests the library used to make along these lines have now been replaced with calls to overrides() instead.

    The SENSE_CACHE conditional compilation has been removed, so the sense caching code is now permanently enabled. This shouldn't affect any existing code, since I'm not aware of anyone who's wanted to use the option to disable the sense cache.

    (This is simply an internal change to eliminate some unnecessary extra source code that's been disabled for a long time anyway. When the sense-cache mechanism was first introduced, it was made optional, so that any games that had problems with the caching could turn it off. The code has been active by default for a long time now, and I'm not aware of anyone who has found it necessary or desirable to disable it, so there no longer seems to be any value in the added complexity of having both options. Removing the conditional option simplifies the source code by removing the effectively dead code for the option of disabling the sense cache.)

    Due to a bug in the parser, a run-time error occurred in certain cases when the player entered a two-object command that was missing its indirect object, and then answered the parser's prompt for the missing object. This has been corrected.
    The standard library tokenizer now accepts function pointers and anonymous function pointers in the fourth slot (the converter callback) in a token rule. The function is invoked using the same arguments that are used when calling a method of 'self' to convert the token value.
    3.0.6m

    Released November 15, 2003

    Important compatibility-breaking change #1: The 'explicit' parameter has been removed from Thing.lookAround() and Actor.lookAround(), as well as from the internal Thing service methods lookAroundPov(), lookAroundWithin(), and lookAroundWithinSense(). This extra parameter was an historical relic that's no longer used, so it's being removed to simplify the interfaces to these routines. Game code is likely to call lookAround() in a few places, and might even override it, so authors should check their existing game code and remove this parameter from any calls or overrides.
    Important compatibility-breaking change #2: The AltTopic mechanism has been changed slightly. In the past, AltTopics were nested one within another to form a group of alternatives. Now, AltTopics are still nested within their parent TopicEntry, but they're no longer nested within one another; instead, they're simply siblings. So, for old game code like this:

      // OLD WAY!!!
      + TopicEntry @lighthouse "It's very tall...";
      ++ AltTopic "Not really..." isActive=(...);
      +++ AltTopic "Well, maybe..." isActive=(...);
      ++++ AltTopic "Okay, it is..." isActive=(...);
    

    ...you'd now instead write it like this:

      // the new way
      + TopicEntry @lighthouse "It's very tall...";
      ++ AltTopic "Not really..." isActive=(...);
      ++ AltTopic "Well, maybe..." isActive=(...);
      ++ AltTopic "Okay, it is..." isActive=(...);
    

    The only change you need to make is to remove the additional nesting from the second and subsequent AltTopic in each group - simply put all of the AltTopics in a group at the same '+' nesting level.

    The old nesting scheme had a tendency to get unwieldy whenever a single topic had more than one or two alternatives. The new scheme makes game code a little cleaner when defining large AltTopic lists.

    Possible compatibility-breaking change #3: The getTraveler() methods, in both Actor and BasicLocation, now have an additional parameter giving the connector being traversed. This allows the routine to take into account both the actor and the connector involved in the travel, which can be useful in some cases.

    For example, you might want to set up a travel connector that leads out of a vehicle, in which case you'd want the actor within the vehicle rather than the vehicle to be the traveler when the connector is traversed. In these cases, you'd override the vehicle's getTraveler() method to check the connector, returning the actor rather than the vehicle when the outbound connector is the one being traversed.

    In addition, for greater generality, the "actor" argument to BasicLocation.getTraveler() (and subclass overrides) has been renamed to indicate that it now takes a traveler rather than merely an actor. Vehicle.getTraveler() takes advantage of this: it now recursively asks its container for the traveler, passing itself (rather than the traveler within itself) as the proposed traveler. These changes allows for situations where an actor is inside a vehicle that's inside another vehicle, for example.

    Finally, Vehicle.getTraveler() now makes an additional check before returning itself as the traveler: if the connector allows the traveler within the vehicle to pass (as indicated by the connector's canTravelerPass() method), but the connector doesn't allow the vehicle itself to pass, then getTraveler() simply returns the original traveler rather than trying to make the vehicle travel. This makes it easy to set up an outbound connector from within a vehicle - simply set up the connector's canTravelerPass() to exclude the vehicle itself, and a traveler within the vehicle will automatically leave the vehicle behind on traversing the connector.

    There are a few minor interface changes in the travel mechanism, all of which should be transparent to existing game code.

    The TravelConnector method connectorBack() now takes a Traveler object rather than an Actor as its first argument.

    The TravelConnector method fixedSource() now takes a Traveler instead of an Actor as its second argument.

    The Thing method getTravelConnector() now accepts a nil 'actor' argument. When the actor is nil, it means that the caller is interested in the structure of the map independently of any actor's present ability to perceive that structure. This is useful in some cases, such as auto-mapping, where we want to know what the map is actually like rather than merely what it looks like to a given actor at a given time.

    The internal handling of the various PushTravel action subclasses now uses only the 'verify' stage of the action handling for the indirect object, when there's an indirect object at all. For example, PUSH BOX THROUGH DOOR only calls the door's dobjFor(GoThrough) 'verify' handler, not any of its other handlers. This change reflects the fact that the action of a push-travel is always carried out by a nested TravelVia with the special PushTraveler object, hence the only need we have for the indirect object handlers at all during the initial PushTravel handling is for disambiguation via the 'verify' routine. The old implementation was problematic when the indirect object remapped decomposed verb, because it incorrectly remapped the entire action before the proper nested action could be invoked. This change corrects the problem, allowing the PushTravel varieties to work correctly for any combination of objects.

    In the Traveler class, describeDeparture() and describeArrival() now show their messages in a visual sense context for the traveler, if the player character isn't involved in the travel. This ensures that the travel is properly described (or properly not described) in situations where the traveler is visible to the player character but the motivating NPC isn't, or vice versa, such as when an NPC is inside an opaque vehicle.

    The Fixture and Immovable classes are now based on a new common base class, NonPortable. This change is entirely a matter of the library's internal organization, and is entirely transparent to game code - Fixture and Immovable behave exactly like they did before.

    The new class was introduced for two reasons. First, it consolidates the behavior common to all non-portable objects, eliminating a small amount of redundancy in the former arrangement. Second, and much more importantly, it roots all of library's non-portable object classes in a single base class. This means that code can reliably test an object for unportability by checking obj.ofKind(NonPortable). In the past, it was necessary to write (obj.ofKind(Immovable) || obj.ofKind(Fixture)); not only is that test more cumbersome, but its very form is suggestive of the potential for additional "||" clauses in the future. The new arrangement formalizes in the library a single root class for all unportable objects, ensuring a clean way for the library and library extensions to add any future specialized unportables without breaking existing game code.

    Note that this change does not break existing game code that uses the "||" test; other than the new common base class, the inheritance structure for Fixture and Immovable and their subclasses has not changed. Even so, it wouldn't be a bad idea to replace any "||" tests in your existing code with the simpler ofKind(NonPortable) test, because this will ensure that your code takes advantage of the insulation from future changes that the new class affords.

    The Fixture class (and by inheritance, its subclass Component) now considers an instance to be owned by a given object if the object is the Fixture's location, or the location is owned by the object. Since a Fixture/Component is a permanent part of its location, it usually is appropriate to consider the component to be owned by anything that owns the location, as well as to consider the component owned by the location itself.
    The new mix-in class Attachable (in extras.t) makes it easier to define objects that can be attached to one another, such as a hose and a faucet, a plug and an electrical outlet, or a rope and a railing. Attachable is meant to be combined (using multiple inheritance) with Thing or a Thing subclass. The class provides a number of methods that you can override to control which objects can be attached to one another, and the special effect of enacting out those attachments.

    The Attachable class provides default handlers for AttachTo, Detach, DetachFrom, and TakeFrom, but it's relatively easy to define additional, customized action handlers in terms of these default actions using the standard 'remapTo' mechanism. For example, if you were defining a rope, you could define a TieTo action, and then map TieTo to AttachTo in your rope object.

    The new classes PermanentAttachment and PermanentAttachmentChild are subclasses of Attachable for cases where you describe objects as attached in the story text, but you don't want to allow the objects to be detached from one another. Describing objects as attached tends to invite a player to try to detach them; these classes lets the objects provide customized responses to the player's attempts, without actually allowing the objects to come apart. (These classes aren't intended for mere components; the Component class is adequate for that. PermanentAttachment is for situations such as a ball attached with a string to a paddle, where essentially separate objects are conspicuously attached to one another.)

    The new mix-in class PresentLater makes it easy to set up objects that aren't in the game world initially, but will appear later in response to some event. For example, you might want to create a hidden door that isn't part of the game world until the player casts the DETECT HIDDEN DOORS spell, or you might want to create a pile-of-leaves object that shows up at the foot of a tree after the player shakes the tree.

    The traditional way of programming these sorts of objects was to create the object with a 'nil' initial location, then use moveInto() to move the object into the game upon the triggering event. PresentLater provides an alternative. To use PresentLater, you set up the object in its eventual location, as though it were going to be there from the start, but you add PresentLater at the start of the object's superclass list. During pre-initialization, the library will remember the object's starting location, and then move the object to 'nil', effectively removing it from the game world. Later, when you want to the object to appear, simply call makePresent() on the object, and it will automatically move to the eventual location that was noted during start-up.

    The advantage of using PresentLater is that you define the object's eventual location as part of the object itself, as though it were an ordinary object; this means that you don't have to specify its eventual location as part of some method (in a moveInto call) elsewhere in the source code. Another advantage is that you can use the makePresentByKey() method to bring a whole set of related objects into the game world at once: you can give each PresentLater object a "key" property, which is just an arbitrary value you choose to identify a group of objects, and then bring all objects that have that key into the game world at once.

    The new class Unthing represents the absence of an object. This class is useful for the occasional situation where a player is likely to assume that an object is present even though it's not. For example, sometimes an object becomes hidden, but a player might not notice that; rather than letting the regular parser error handle it, we can create an Unthing that explains that the object can no longer be seen. Unthing is a simple subclass of Decoration, with a customized default handling message.
    The travelerDirectlyInRoom precondition object has been replaced with a new TravelerDirectlyInRoom class. This change allows the caller to create the precondition object with the full set of information it needs via the class's constructor. This change is probably transparent to existing game code, as game could would have little reason to use this precondition.
    The class Thing now defines the method checkTravelerDirectlyInRoom(); the default implementation simply defers to the container. This ensures that connectors that are defined inside ordinary objects (for example, a hole in a bookcase) will properly ensure that a traveler is in the Thing's containing Room or NestedRoom before travel. In the past, the absence of this method meant that there were no conditions at all on a traveler's initial location in such cases.
    The dropDestinationIsOutRoom precondition now moves the actor, rather than the traveler (to the extent that they differ), to the outer room when needed. (Since the actor is attempting to reach the outer room in these cases, it makes more sense to move the actor, rather than any vehicle the actor is inside.)

    Similarly, the Floor object's SitOn and LieOn handlers now apply a precondition requiring the actor, rather than the traveler, to be in the room directly containing the Floor object.

    The new VocabObject method getFacets() returns a list of "facets" of an object. These are other objects that share the same parsing identity; for example, the two sides of a door are facets of the same physical object, from a character's perspective, so each side would return a list containing the other side from getFacets(). In fact, the Door class overrides getFacets() to do just this.

    The parser uses getFacets() to resolve an out-of-scope pronoun antecedent, if possible. If the player uses a pronoun, and the antecedent is out of scope, the parser calls the antecedent's getFacets() method, and looks for a facet that's in scope; if it finds an in-scope facet, the parser uses that facet as the replacement antecedent. For example, if you type OPEN DOOR, GO THROUGH IT, THEN CLOSE IT, the parser will now correctly resolve the second "it" to the door. In the past, the second "it" wasn't resolvable, because it referred to the side of the door that's in the other room and thus out of scope after the travel. Using the new facet list capability, the parser can now resolve the second "it" to the side of the door that is in scope.

    (If more than one new facet of the object is in scope in these cases, the pronoun resolver will take the one that's most readily visible - that is, the one that has the most "transparent" sense path in the actor's visual senses. If all of the in-scope facets are equally visible, the resolver just picks one arbitrarily.)

    By default, VocabObject.getFacets() simply returns an empty list. The Door and Passage classes override the method to return a list containing the linked object representing the other side of the door or passage, if there is one. The new MultiFaceted object returns the list of facet objects that it synthesizes; each facet instance of a MultiFaceted does the same thing.

    You can customize getFacets() whenever you use multiple objects with a shared parser identity. One common case where you might want to do this is "object transmutation," where you substitute one object for another in order to effect a radical change in the setting. For example, you might have one object representing a wooden dining table, and another representing the pile of splintered wood that an enraged chef turns it into after the guests insult his cooking. In this case, you could override getFacets() in the table so that it returns the pile-of-splintered-wood object.

    The class AutoMultiLoc has been removed, and its capabilities have been rolled directly into the MultiLoc class itself. All of the initialization properties and methods (initialLocationClass, isInitiallyIn, and buildLocationList) have been moved into MultiLoc, and they work the same as they used to. There really wasn't any need for a separate AutoMultiLoc class, since the two styles of initialization (enumerated and rule-based) can coexist quite easily in the single class.

    Along the same lines, the DynamicMultiLoc class has been removed, and its functionality has been rolled into MultiLoc. In particular, the reInitializeLocation() method is now part of the base MultiLoc class.

    If you have any AutoMultiLoc or DynamicMultiLoc objects in your existing game code, you should simply change the AutoMultiLoc or DynamicMultiLoc superclass to MultiLoc. The objects should then behave the same as they did before.

    MultiLoc.moveOutOf() caused a run-time error due to a mismatched call to notifyRemove(). This is now fixed.
    The new classes MultiInstance and MultiFaceted are useful for cases where you want to create an object in multiple locations, but MultiLoc isn't suitable. A MultiLoc is suitable when a single object is contained entirely and simultaneously in more than one location; when a single large object spans several locations, or when you simply want to duplicate a ubiquitous background object in several places, the new classes are better.

    MultiInstance is good when you want to duplicate a ubiquitous background object in multiple locations: trees in a forest, the sun in outdoor locations, crowds in the street. MultiFaceted is for large objects that span many locations, such as rivers or long ropes. MultiLoc isn't ideal for these sorts of cases because the sense system treats a MultiLoc as a single object that's entirely in all of its locations at once; this means, for example, that if it's lit in one place, it's lit everyplace.

    MultiInstance and MultiFaceted work mostly like MultiLoc from the programming perspective. Internally, though, rather than appearing itself in each of its locations, a MultiInstance or MultiFaceted creates a separate "instance" object for each of its locations. Each instance is an ordinary singly-located object, so the library creates one per location. The instance objects are instances of a template that you define as part of the MultiInstance or MultiFaceted object.

    The new classes present the MultiLoc multi-location interface, so you can add and subtract locations as needed, using the usual routines: moveInto(), moveIntoAdd(), moveOutOf(). You can also set the initial set of locations as you would for a MultiLoc: you can set locationList to the list of initial locations, or you can define initialLocationClass, isInitiallyIn, and/or buildLocationList.

    The instances of a MultiInstance or MultiFaceted are all essentially identical, so these classes are only good for relatively homogeneous objects. These classes aren't appropriate for cases where you want to present distinctive aspects of an object, such as the different sides of a large building, or distinctive individual objects, such as different people in a crowd. When the individual parts are distinct, you're best off just creating separate objects for the different instances. The new classes are mainly for convenience in cases where you'd otherwise have to repeat an identical object definition in several locations.

    The new pre-conditions dobjTouchObj and iobjTouchObj can be applied to the indirect or direct object (respectively) of a two-object action, to require that the one object can touch the other. This is useful for actions where the actor manipulates one of the objects directly, but manipulates the other object only indirectly using the first object.

    For example, PUSH COIN WITH STICK would be a good place to use iobjTouchObj as a direct object precondition: the actor is manipulating the stick directly, so the actor has to be able to touch the stick, but the coin only needs to be reachable indirectly with the stick. Another example: PLUG CORD INTO OUTLET requires direct manipulation of the cord, but only the cord needs to touch the outlet, so this would be a good case for dobjTouchObj as a precondition on the indirect object.

    In the class Thing, the default direct object handlers for the actions MoveWith, TurnWith, ScrewWith, and UnscrewWith now use the iobjTouchObj condition rather than touchObj condition. These verbs all generally model interactions where the direct object only needs to be reachable through the indirect object. The new class Attachable uses dobjTouchObj as a precondition on the indirect object.

    To enable the new iobjTouchObj condition, the TouchObjCondition class can now handle a yet-unresolved source object during the verification stage. When the source object is nil, the pre-condition simply skips its verification stage. This allows a TouchObjCondition to be applied across objects for a two-object action: for example, it allows you to apply a condition to the direct object that the indirect object can touch it.

    The new preconditions sameLocationAsIobj and sameLocationAsDobj let you require that a given object is in the same immediate location as the other object of a two-object command. You use sameLocationAsIobj as a precondition on a direct object, and sameLocationAsDobj as a precondition on an indirect object. Both of these condition objects are based on the class SameLocationCondition, which you can construct dynamically to require the target object to be in the same location as an arbitrary second object, which need not be directly involved in the command.

    These conditions use the new Thing method tryMovingObjInto(obj), which tries to move the given object 'obj' into 'self'. This method is customized in Room, Container, Surface, and Actor to generate appropriate implied commands for those classes, and you can override it in your own objects as needed.

    The new Actor method endConversation() effectively lets an NPC say GOODBYE of its own volition, ending a conversation without waiting for the player to leave or say GOODBYE. This is the complement of Actor.initiateConversation: it lets an NPC initiate the end of a conversation.

    The new conversation-ending code endConvActor indicates that we're ending the conversation of the actor's volition.

    The new ConvNode method noteLeaving() is called when the conversation node is about to become inactive. This is the complement of noteActive(). This method doesn't do anything by default, but instances can use it to trigger side effects when leaving the node.
    The conversation manager's mechanism that keeps track of conversation responses has been changed to make it better able to handle multiple conversations on a single turn. In particular, the conversation manager now flags the start and end of each response text in the text stream. Because the "transcript" subsystem captures displayed text and defers its actual output until later, difficult synchronization problems arose if a game tried to trigger multiple responses from different actors on a single turn. This change should be transparent to existing game code.
    TopicEntry has a new method, setTopicPronoun(), that tries to set a pronoun antecedent when the topic is matched. The default handleTopic() method automatically calls the new method.

    It's not always possible for setTopicPronoun() to guess about an antecedent for a topic phrase match, because TopicEntry instances can match more than one game object, and topic phrases by their nature can refer to more objects than are present visually. The new method will set a pronoun antecedent for the topic if the topic phrase in the player's input yields one object when "intersected" with the TopicEntry's 'matchObj' list. The method first looks only at in-scope objects, then looks to the "likely" list, but only if there are no in-scope matches. If the intersection yields more than one object, the library doesn't set a pronoun antecedent at all, since the match is ambiguous.

    Since this heuristic procedure can't always decide on an antecedent, you might occasionally want to set the antecedent explicitly in a particular TopicEntry. You can do this by overriding the TopicEntry instance's setTopicPronoun(fromActor,topic), in which you'd call fromActor.setPronounObj(obj), where 'obj' is the game object you want to set as the antecedent.

    In the past, when an "again" command was used to repeat a command directed to a non-player character ("bob, go east"), the turn counter incorrectly advanced by two turns. This was due to an error in the mechanism that allows one actor to wait for another to finish a command. This is now fixed.

    A separate problem prevented an undo savepoint from being created for an "again" command repeated a command directed to another character. Since no savepoint was created, typing "undo" after such an "again" command took back not only the "again" turn but the turn before it as well. The savepoint is now created properly.

    Due to a bug, the conversation mechanism didn't enter a conversation (i.e., didn't show the "greeting protocol") if a DefaultTopicEntry was used for a response. This has been fixed.

    As part of this change, the ActorTopicEntry class, which was introduced in the refactoring of the TopicEntry class introduced in 3.0.6l, has been eliminated. On further consideration, the bifurcation of TopicEntry into actor-associated and non-actor-associated subtypes wasn't very clean, since it implied a similar split for some of the other classes that work with TopicEntry, notably AltTopic and DefaultTopic. This was too unwieldy.

    So, ActorTopicEntry has been removed. In its place, the "topic owner" abstraction, which was introduced in 3.0.6l, has been used throughout the base TopicEntry class to replace the need for an associated actor. This means that the base TopicEntry can do everything that it did before (and everything that ActorTopicEntry did before), but without any assumption that there's an associated actor. The getActor() method is still present, but it's now strictly for the convenience of game code; the library no longer assumes that topic entries to be associated with actors. The TopicEntry.getActor() method simply returns the topic owner if it's of class Actor, otherwise nil.

    These changes should have no impact on existing game code, since the ActorTopicEntry was intended as an internal class structure only.

    A bug in the TopicResolver class caused a run-time error for a command like ASK someone ABOUT ALL. This is now fixed.
    When the ConsultAbout action picks a default consultable object based on the actor's last ConsultAbout command, the action now marks the defaulted object as such, which triggers the usual announcement of the default object.
    The typographicalOutputFilter object (in en_us.t) is now a little smarter about what it considers sentence-ending punctuation. If a lower-case letter or a hyphen of some kind follows what the filter would otherwise take for sentence-ending punctuation, it doesn't treat it as a sentence ending. This gives better results in the common case of an exclamation point or question mark within a quoted passage: "'Who are you?' he asked." It also helps in some less common cases, such as when an exclamation point is embedded in a sentence, such as in an interjection set off by dashes ("And then -- oh no! -- I dropped it").
    The PendingCommandInfo class has been refactored into a couple of subclasses for a cleaner class structure. PendingCommandInfo is now an abstract base class; the new concrete subclasses PendingCommandToks, PendingCommandAction, and PendingCommandMarker now implement the specialized behavior that was formerly embedded in the single class and handled conditionally. These classes are mostly for internal use in the library, so this should have no impact on any existing game code.
    Some messages in BasicContainer have been modified slightly to remove the assumption from the base class that it's an object that can be opened. First, the new properties cannotTouchThroughMsg and cannotMoveThroughMsg give property pointers, referring to playerActionMessages properties, that specify the messages to use when an object cannot be reached or moved through the container's containment boundary. By default, these now refer to the new messages cannotTouchThroughContainer and cannotMoveThroughContainer, which don't say anything about the container being closed. Second, BasicOpenable class overrides these two new properties to refer to the original cannotTouchThroughClosed and cannotMoveThroughClosed messages. Third, the tryImplicitRemoveObstructor() method that was formerly in BasicContainer has been moved to BasicOpenable instead, eliminating the assumption in BasicContainer that the obstruction can be removed by opening the container.

    Note that these changes are designed in such a way that objects based on both Openable and Container (either by mixing the two superclasses, or by using the library class OpenableContainer) should be unaffected. Objects based on Container or BasicContainer, without any Openable mix-in, should now behave more logically, in that they won't assume anything about being openable.

    The touchObj precondition incorrectly reported two failure messages if an implied command failed trying to remove an obstruction to touch. The failure message of the implied command itself was shown, and a separate failure message explaining that the target object isn't reachable was also shown; in such cases, only the implied command failure should have been reported. The precondition now omits the redundant second message.
    KeyedLockable is now more flexible about the "known key list" and who owns it. In the past, the "master object" of a linked lockable (the master object of a two-sided door, for example) always owned the known key list; this made it impossible to have separate keys operate the two sides of a door, and also created a dependency on which side of the door was the master. Now, KeyedLockable instead uses the local side's known key list if it has one, and only uses the master side's list if either the local side has an empty list or the master side has no list; only if the local side has an empty list, and the master side has a list, is the master side used. This makes initialization of two-sided doors much more flexible, because it no longer matters which side is the master for the purposes of the known key list.
    In 3.0.6j, we introduced the notion of whether or not the status of a Lockable is known. The idea was that actors shouldn't just automatically unlock a door on trying to open it unless they have some reason to know the door is locked in the first place. Unfortunately, this mechanism took away the important playability convenience feature that automatically unlocks locked doors when it's obvious how to do so.

    This mechanism is now replaced with something a little more subtle, which solves the same problem without any loss of convenience for the player. Instead of letting the original OPEN command fail on encountering a door that's locked but not previously known to be locked, the library now inserts a "testing" action into the sequence ahead of the implied UNLOCK. The "testing" action represents the actor's first attempt to open the lockable, as in attempting to turn a locked doorknob. This first phase fails, but instead of letting the rest of the command fail, we consider the actor to have immediately learned from the first phase that the object is locked, so we proceed directly to an implied UNLOCK action. The result looks like this:

    >go north
    (first trying the door and finding it locked, unlocking it, then 
    opening it)
    

    Note that the lockStatusObvious property of the former scheme still applies. If lockStatusObvious is true, then the extra "testing" phase is omitted, since we can tell just looking at the object that it's locked. Note also that the "testing" phase is only used when the object is actually locked; when it's not locked, the OPEN command succeeds, so there's no need to split the OPEN action into the two phases.

    The Door class now uses a custom cannotTravel() message, rather than using the default message for the location. The message explains that the travel is not possible because the door is closed; the generic cannot-travel message for a room simply claims that the travel is impossible, which isn't as specific as we can be about it.
    The Openable class has a new method, openStatus, that makes it easier to override the open/closed status addendum that's shown as part of the object's description. The openableContentsLister now calls this method to show the status; the default implementation just says "it's open" (or equivalent, adjusted for gender and number). An Openable can override this to customize the message as needed.
    The Passage class's initialization of a two-sided relationship has been refined slightly. In the past, the non-master side of a two-sided passage explicitly set its master object's otherSide property to point to the non-master side. Now, instead of setting the property directly, the non-master side calls the master object's initMasterObject() method with the non-master side as the parameter; by default, initMasterObject() simply sets 'otherSide' to the other object. This makes it easier to customize the initialization for cases where more than two objects are involved in a passage relationship, or where the passage relationship is dynamic, by allowing the master object to directly control its own initialization.
    In the Candle class, when the object runs out of fuel, it now displays its "burned out" message before actually cutting off its light source (by marking itself as unlit). In the past, the order of these operations was reversed, which caused the library to suppress the "burned out" message when the candle was the only light source. The message was suppressed because these operations are performed in a daemon that runs in the "sight" sense context of the candle itself; once the candle is marked as unlit, it becomes invisible when there's no other light source, and so any message displayed in its sight context is suppressed. By showing the message while the candle is still marked as lit, we ensure that the message will be seen as long as the candle is in view of the player character.
    The Readable class now generates its "obscured" and "dim" default descriptions properly. A bug formerly caused a run-time error when attempting to read a Readable under these sense conditions.
    The menu system now includes an automatic sorting mechanism, which sorts a menu's entries into a game-defined order each time the menu is displayed. The default initializeContents() method in the MenuObject class does the sorting, based on the new 'menuOrder' properties of the items in the menu. The 'menuOrder' property is an integer value giving the relative order of an item among its siblings. By default, 'menuOrder' returns the 'sourceTextOrder' value for the menu item, so the default ordering is simply the original ordering of the items in the source file.
    In the English module, the new property isQualifiedName generalizes the function of the existing isProperName property. isProperName indicates that a 'name' property is "proper," meaning it's the name of a person or place - the kind of noun that's usually capitalized in English. Proper names don't require "qualification" when they appear in sentences, which basically means that they don't need articles like "the" or "a", because they're inherently definite. The new isQualifiedName property can be set to true for an object when the name is fully qualified, but isn't proper. By default, a proper name is considered qualified, but the two properties can be set independently as needed.

    An example of a non-proper but qualified name is "your book." This isn't the proper name of the book, but the possessive pronoun makes it fully qualified, making it incorrect to add an article when using the name in a sentence.

    In practice, at the moment, "qualified" and "definite" have the same effect, which is to omit any articles when the name is used in a sentence. The new property is intended to separate the two concepts in case there's a need to distinguish them in the game or in future library changes.

    The English parser has a new bit of fine-tuning in its verb phrase chooser for a particular type of ambiguous phrasing. In certain sentences in English, a preposition can be interpreted either as part of a noun phrase or as a structural part of the verb; for example, DETACH STRING FROM BOX could be interpreted either as a verb with two objects (STRING as the direct object, BOX as the indirect object), or as a verb with a single object (STRING FROM BOX as the direct object). English speakers will almost always assume the two-object interpretation automatically in cases like this; English is such a strongly positional language that the verb phrase structure tends to dominate everything else in a sentence. In the past, the parser didn't care one way or the other, so when both interpretations were valid, the parser sometimes arbitrarily chose the much less probable single-object version. The parser now takes the verb structure dominance into account by preferring the intepretation with more "noun slots" in the verb phrase, whenever there's ambiguity.
    The English parser's tokenizer now treats ampersands essentially as though they were alphabetic characters for the various kinds of "word" tokens. Ampersands have no other meaning to the standard parser, and show up every so often in names of things, so it makes sense for the parser to accept them as parts of words.

    Note that an ampersand is treated as alphabetic, not as a punctuation mark. This has two implications that might be counterintuitive, at least to the extent that ampersands look to most people like punctuation marks. (In fact, they're not punctuation marks at all; they're actually ligatures of "et," Latin for "and," although the glyph has become so stylized in modern typefaces that it's hard to see that unless you know what to look for.) First, an ampersand is not a token separator, so a sequence like "T&V" will be read as a single token. Second, when an ampersand is surrounded by whitespace (or other token separators), so that it does count as a separate token, it'll be parsed as a "word" token, not as punctuation. For example, "Bob & Sons" is read as three "word" tokens. This means that you can use a lone "&" as an adjective or noun in defining an object's vocabulary.

    In the English module, Thing.conjugateRegularVerb() incorrectly applied an "-ies" ending to verbs ending in a consonant plus a "y", such as "say". This affected routines like itVerb() and nameVerb(). The routine now uses a regular "-s" ending when a vowel precedes a terminal "y".
    The parser now allows possessive pronoun adjectives ("her book") to refer back to the previous noun phrase in the same action. For example, ASK BOB ABOUT HIS HAT will treat "his" as referring to Bob. This is accomplished by first trying to find an earlier noun phrase in the same action that matches the pronoun in number and gender. If we find a match, we use it, otherwise we use the ordinary pronoun antecedent from a previous command. Note that the number/gender match ensures that we don't get overzealous in applying this: if we type ASK BOB ABOUT HER BOOK, we find that BOB doesn't match HER, so we look to the meaning of HER from a previous command.

    As part of this change, the Action routine that was formerly called getReflexiveBinding has been renamed to getAnaphoricBinding. The new name better reflects the more generalized function, since it can be used for other kinds of anaphoric bindings besides reflexives. This is an internal method that is highly unlikely to be used in any game code, so this change should be transparent.

    ("Anaphor" is the linguistic term for any sort of reference to an earlier noun phrase in a sentence; a reflexive is a particular kind of anaphor that re-uses a noun phrase from an earlier "slot" in a predicate to fill another slot in the same predicate, such as HIMSELF in ASK BOB ABOUT HIMSELF. The reason reflexives exist in English and other languages is that they're unambiguously anaphoric: in ASK BOB ABOUT HIMSELF, HIMSELF can only refer to Bob, whereas the HIM in ASK BOB ABOUT HIM almost certainly refers to someone other than Bob from a previous sentence or clause. The name change in the method reflects the fact that there are other kinds of anaphoric constructs besides reflexives; in ASK BOB ABOUT HIS BOOK, the HIS refers anaphorically to BOB, but it's not reflexive.)

    In Actor.executeActorTurn(), when a pending response is delivered, the actor now clears its memory of the pending response. (In the past, the pending response wasn't cleared, so an NPC with a pending response would keep delivering the pending response over and over.)
    When AskFor changed to a TopicTAction in 3.0.6l, the Actor handler for GiveTo was affected but wasn't updated properly. The GiveTo handler changes a GiveTo command directed to another actor into an AskFor command (BOB, GIVE ME THE CROWBAR becomes ASK BOB FOR CROWBAR), so the AskFor change required this rephrasing to be cast in the new TopicTAction terms. This change wasn't made, so the GiveTo handler didn't work properly. This has now been fixed.
    Actor.desc no longer shows the postureDesc as part of the description of an NPC. Instead, this is shown in examineStatus(). This change shouldn't affect any existing code; it merely moves around where the calls are made, but still makes all the same calls in the same sequence. The change makes it easier to override an Actor's 'desc' without changing the overall display format.
    A bug in the sense system prevented a self-illuminating object (an object with a brightness of 1) from applying its self-illumination to its own interior surface; the illumination was considered external only. This was inconsistent with the normal sense-transmission rules, and it's now been corrected. A self-illuminating object is now considered to have an ambience level of 1 on both its inner and outer surface.
    A subtle bug in the sense system caused ambient sense energy levels to be transmitted incorrectly into the interior of objects with non-transparent fill media. In particular, the sense system incorrectly applied the fill medium to the ambience level at the inner surface of an object as transmitted from the outside of the object. This was incorrect because it meant that a viewer on the inside of an object saw the ambient level to the object adjusted twice for the fill medium: once for the incorrect adjustment at the inner surface, and once more for the path from the viewer, through the fill medium, to the inner surface. This has been corrected: the ambient level arriving at the inner surface of an object from outside the object is now calculated without any adjustment for the fill medium, and any further transmission inward is then adjusted for the fill medium.

    This problem showed up by making an object invisible from its own interior when the object had a non-transparent fill medium, and the light coming in from outside wasn't bright enough to penetrate the fill medium twice. For example, a transparent booth filled with attenuating fog, with external normal illumination (brightness 3), was not visible from its interior because of the double traversal of the fog. Such a booth is now visible from its interior.

    When looking for an object's default associated Noise or Odor, the Thing methods getNoise() and getOdor() now only consider objects with an active "presence" in their sense. That is, getNoise() will only return a Noise object with a non-nil soundPresence property value, and getOdor() will only return an Odor with a non-nil smellPresence. This makes it easier to turn an object's associated sense data on and off, since you can simply use the presence properties of the Noise and Odor objects.
    In 3.0.6j, CollectiveGroup stopped matching singular phrases and phrases with quantities specified (as in "five coins"). This behavior hasn't changed, but it's now easier to control, with the new method isCollectiveQuant(). This method returns true if the given "required quantity" allows using the collective, nil if not. By default, this returns true if and only if there's no specific quantity needed (in which case the quantity parameter is nil).
    RandomTextList and ShuffledTextList can now handle arbitrary event entries in their lists, using the same rules as the base EventList class. (In the past, only strings and nil values were allowed in these specialized subclasses, but they now use the base class handling to carry out each event, so they can now handle any sort of event the base class can.)
    3.0.6l

    Released October 4, 2003

    The actor knowledge and "sight memory" systems have been revamped to make it easier to keep track of individual knowledge for different actors. A few key interfaces have changed, so existing games will have to make corresponding changes.

    First, the main interface changes:

    • obj.seenBy(actor) is now actor.hasSeen(obj)
    • obj.setSeenBy(actor, stat) is now actor.setHasSeen(obj)
    • obj.isKnownBy(actor) is now actor.knowsAbout(obj)
    • obj.setKnownBy(actor, stat) is now actor.setKnowsAbout(obj)

    Note that the "stat" parameter has been removed from the two "set" methods. This parameter was essentially superfluous, since it's rare to the point of non-existence for a game to want to mark something as un-seen or unknown after it's been previously seen or known; the new methods elide this parameter and act like the old methods acted with the parameter set to true.

    You have a choice of how to update your existing game code to the new interfaces. The first option is to do the search-and-replace operations in the list above throughout your game's source code; this is the recommended option, because it'll make your code consistent with future documentation. The second option is to modify the VocabObject class to reinstate the old methods, defining them in terms of the new ones:

    modify VocabObject
      seenBy(actor) { return actor.hasSeen(self); }
      setSeenBy(actor, flag) { actor.setHasSeen(self); }
      isKnownBy(actor) { return actor.knowsAbout(self); }
      setKnownBy(actor, flag) { actor.setKnowsAbout(self); }
    ;
    

    Second, how do the changes facilitate separate NPC knowledge tracking? The change here is that the Thing and Topic 'seen' and 'isKnown' properties that underlay the old system are still used, but are now only defaults for tracking the seen/known information. The actual properties used to track the information are determined on an actor-by-actor basis, using the new Actor.seenProp and Actor.knownProp properties.

    By default, Actor defines seenProp as &seen, and knownProp as &isKnown. This means that, by default, there's only one "global" set of knowledge. To track an NPC's knowledge individually, simply set seenProp and/or knownProp to new properties unique to that actor. For example:

    bob: Person
      // ... other definitions...
      seenProp = &bobSeen
      knownProp = &bobKnown
    ;
    

    This specifies that 'bob' will individually track its knowledge, separately from any other actors. When you call bob.setKnowsAbout(obj), the library will examine obj.bobKnown and obj.bobSeen, because those are the properties that track bob's knowledge. Note that this individual tracking is completely automatic once you define seenProp and knownProp, since the Actor methods hasSeen, setHasSeen, knowsAbout, and setKnowsAbout all use seenProp and/or knownProp to do their work.

    Note that because the default values in Actor for seenProp and knownProp are (respectively) &seen and &isKnown, everything works roughly the same way it did before if you don't override these properties. In particular, if you don't care about tracking individual NPC knowledge, which typical games probably won't, you only have to worry about 'seen' and 'isKnown' as before. So, if you want to initialize an object as pre-known, before the start of the game, simply set isKnown=true for that object as in the past.

    Third, a minor enhancement: the GIVE and SHOW iobjFor() handlers in Actor now automatically mark the object being shown, and its visible contents, as having been seen by the actor being shown the object. For games that track NPC knowledge separately, this will ensure that the target of a GIVE or SHOW takes note of having seen the object in question.

    A new class, Consultable, can be used to implement things like books and file cabinets: inanimate objects in which an actor can look up a topic, to find some information. This class works almost exactly like the Actor conversation system. Another new class, ConsultTopic, serves as the TopicEntry subclass for Consultables. Simply create one or more ConsultTopic objects, and locate them within the Consultable, to populate the consultable object's database of responses.

    In support of the new Consultable class, the TopicDatabase and TopicEntry classes have been refactored a bit. In particular, these two classes no longer assume that they're associated with an actor, and no longer assume that they're involved in conversation. The parts of the former implementations that made assumptions about actor or conversation associations have been moved into a pair of new subclasses, ActorTopicDatabase and ActorTopicEntry. The existing classes that were based directly on TopicDatabase and TopicEntry are now based on the new ActorXxx subclasses instead. For almost all existing game code, this change should be entirely transparent, because the final subclasses that most games use still have the same names and work the same way they did before; the only changes are some internal reshuffling to abstract out most of the functionality into the new base classes.

    In addition, the TopicResolver class has a new subclass, ConvTopicResolver, that's used for the conversational actions (ASK ABOUT, TELL ABOUT). The base TopicResolver doesn't differentiate at all among topics; it simply considers everything to be an equally good match. The ConvTopicResolver differentiates topics into three sublists, as it did in the past: objects that are in physical scope or are known to the actor performing the command; objects that are "likely" topics, as indicated by the performing actor's isLikelyTopic(); and everything else.

    The ConsultAbout action in the English module now adds grammar for some incomplete forms, such as "look up (topic)". If the player enters one of these incomplete forms, the parser will prompt for the missing object in the usual fashion.

    Similarly, when the direct object is missing from a CONSULT ABOUT command, the default is the last object consulted by the same actor, as long as that object is still visible. The new Actor property lastConsulted keeps track of the last object consulted; the new Actor method noteConsultation() sets this property. The Consultable class calls gActor.noteConsultation(self) in its ConsultAbout action() handler. This saves the player some typing, since they can leave out the object to be consulted when they're looking up a series of topics in the same consultable (they can type simply LOOK UP BOB, for example).

    The TopicEntry scoring system now uses nil to represent a non-match. This means the valid scoring range now includes zero and negative values, which in turn means that it's no longer a problem if matchScoreAdjustment values reducing scores to zero or below. This makes it easier to use score adjustments, since there's no longer any need to worry about interactions between score values and adjustment values taking a final score out of range.
    The AskFor action is now a TopicTAction instead of a TIAction. That is, the indirect object is now a topic rather than an ordinary resolved object. The TIAction implementation was never really appropriate, since ASK FOR conceptually needs topic-oriented rules for the indirect object: one needs to be able to ask for things that aren't in scope, as well as for abstract topics (ASK FOR HELP, ASK FOR DIRECTIONS).

    This change should have little or no impact on existing games.

    The new TopicEntry subclass AskAboutForTopic can be used to make a single topic entry respond to either ASK ABOUT or ASK FOR for a given game object or objects (read the name as "ask about/for topic"). Similarly, AskTellAboutForTopic can respond to ASK ABOUT, TELL ABOUT, or ASK FOR (read the name as "ask/tell about/for topic," although the TELL FOR combination implied in that reading doesn't make any sense, obviously).
    The limitSuggestions property now applies to ActorState objects as well as to ConvNode objects. Suggestions are effectively arranged into a hierarchy: the top level of the hierarchy is the ConvNode, the next level is the ActorState, and the bottom level is the Actor. The full list of suggestions consists of the ConvNode's suggestions, plus the ActorState's suggestions, plus the Actor's suggestions. However, if limitSuggestions is true at any level of the hierarchy, then suggestions below that point are not included in the full list. So, if the ConvNode's limitSuggestions property is true, only the ConvNode suggestions are included. If the ConvNode's limitSuggestions property is nil, but the ActorState's limitSuggestions property is true, then the ConvNode and ActorState suggestions are both included. If limitSuggestions is nil for both the ConvNode and ActorState, then the suggestions at all three levels (ConvNode, ActorState, and Actor) are included in the full list.
    The new class SuggestedTopicTree makes it easy to create a tree of AltTopic alternatives that acts as a single suggested topic.

    Normally, when you create a tree of AltTopic alternatives, you can make each AltTopic a separate suggestion. Each alternative is effectively an independent topic for suggestion purposes, so asking about one doesn't satisfy the PC's curiosity about any of the other alternatives. This means that the game might make the same suggestion several times, as the different alternatives become active.

    In many cases, it's better to treat the whole group of alternatives as a single suggestion, so that the suggestion is only made once (or only as many times as desired) for the whole set. This is what SuggestedTopicTree is for. To use this class, simply add SuggestedTopicTree to the superclass list for the main TopicEntry (that is, the TopicEntry at the root of the tree: the one containing all of the AltTopic alternatives).

    Note that the new TopicEntry property altTalkCount keeps track of the number of invocations for an entire alternative group. This property is kept in the outermost TopicEntry for an AltTopic group, and is incremented each time the outermost TopicEntry or any of its AltTopic objects is invoked (via ASK ABOUT, TELL ABOUT, etc). SuggestedTopicTree uses this new counter to determine if the PC's curiosity has been satisfied for the set of alternatives as a group.

    The former ConversationManager method setRespondingActor() has been renamed to beginResponse(), and a new method finishResponse() has been added. Each call to beginResponse() should have a corresponding call to finishResponse().

    This change allows the conversation manager to defer setting the new default ConvNode in the responding actor until after the response has been finished. This ensures that a ConvNode's noteActive() method will not be invoked in response to a <.convstay> tag. In the past, the conversation manager immediately transitioned the actor to the default next ConvNode at the start of showing the response, so if the response contained a <.convstay> tag, this caused a transition back to the starting ConvNode, and thus a call to noteActive() on the ConvNode object. The new scheme ensures that the actor's ConvNode won't change at all when processing a <.convstay> tag.

    The AgendaItem class has a new method, resetItem(), that is invoked each time the item is added to an actor's agenda (via Actor.addToAgenda()). By default, this method sets the item's isDone property to nil, as long as the property isn't a method. This makes it easier to re-use agenda items, since you don't have to worry about resetting isDone explicitly.
    The Surface class now uses a custom Lister, surfaceInlineContentsLister, to show its in-line contents listing. The new lister shows the in-line contents using the format "(on which is...)". In the past, Surface simply inherited Thing's in-line contents lister, which phrases things in terms of containment ("(which contains...)"), which isn't quite right for surfaces.

    For naming consistency, the Lister formerly named keyringListingContentsLister has been renamed to keyringInlineContentsLister. The old name was a bit odd and didn't properly call attention to the "in-line" aspect.

    Whenever an object's contents are listed via EXAMINE, LOOK IN, or OPEN, the library now automatically marks all of the object's visible contents as having been seen. Only contents that are actually visible to the character performing the command will be marked as seen, but it doesn't matter whether or not the contents are mentioned at all in the description of the object.
    The new Thing method useSpecialDescInRoom(room) lets an object specify whether or not its special description should be shown in a LOOK AROUND description. By default, this simply returns useSpecialDesc(). In some cases, it might be desirable for an object to show its special description only when a container is examined, but not in a room description; this methods lets the object control this.

    The new Thing property suppressAutoSeen can be used to suppress the library's automatic "seen" marking. By default, the library automatically marks every visible object as having been seen whenever a LOOK AROUND command is performed. The library also automatically marks as seen every visible object within a container when the container is explicitly examined (with EXAMINE, LOOK IN, or OPEN, for example).

    suppressAutoSeen is nil by default. If you set it to true for an object, then the library will not mark the object as having been seen in any of the standard cases, even when the object is visible. The game must mark the object as having been seen by explicitly calling setSeenBy(), if and when desired.

    DistanceConnector now derives from Intangible as well as SenseConnector. This makes it easier to use DistanceConnector, since it's no longer necessary to mix it with a Thing subclass. Each distance-connector object needs to derive from Thing so that it fits into the normal sense model, but these objects will almost never have any physical presence in the game; Intangible fills both of these needs.
    The DistanceConnector template in adv3.h, which sets the locationList property, has been changed into a generic MultiLoc template. This lets you initialize any MultiLoc object's location list by specifying the list of locations after the class name when defining the object.
    The Room template in en_us/en_us.h now accepts a room that defines only a 'name' property; the 'desc' property is now optional. This lets you use the template to define the room name even if you want to define a complex method for the 'desc'.
    A bug in the Sense class incorrectly considered objects to be out of range of the 'smell' and 'sound' senses when the objects had distant, obscured, or attenuated sense paths. This has been corrected.
    A Door object's getDoorOpenPreCond() can now return nil, if it's not desirable to use a precondition for opening the door.
    The BasicChair, BasicBed, and BasicPlatform classes now include a touchObj precondition for the SIT ON, LIE ON, and STAND ON commands.
    Where it makes sense, the preconditions that report failures with messages along the lines of "You must open (something) first" now set the pronoun antecedent to the object mentioned. This makes exchanges like this work as one would expect:

      >north
      You must open the door first.
    
      >open it
    
    In the hint system's Goal class, the new properties openWhenKnown and closeWhenKnown let you tie a hint topic to the player character's knowledge of a Topic or Thing. If you set a Goal object's openWhenKnown to refer a Topic or Thing, then the Goal will become "open" as soon as the Topic or Thing becomes known to the PC. Likewise, if you set closeWhenKnown to a Topic or Thing, the goal will become "closed" when the Topic or Thing becomes known.

    The new Goal properites openWhenRevealed and closeWhenRevealed let you tie a hint to a <.reveal> tag. You can set these properties to (single-quoted) strings giving <.reveal> tags to test.

    The Goal class has two new methods, openWhen and closeWhen, that compute the conditions for opening and closing the hint topic, respectively. These methods isolate the conditions that were formerly included in-line in the updateContents() method.

    Isolating the conditions in separate methods makes it easier to use 'modify Goal' to add new custom open/close sub-conditions. For example, suppose you wanted to add a pair of new custom Goal properties that open and close hint topics based on a Thing being moved for the first time. You could do this like so:

      modify Goal
        openWhenMoved = nil
        closeWhenMoved = nil
        openWhen = (inherited || (openWhenMoved != nil && openWhenMoved.moved))
        closeWhen = (inherited || (closeWhenMoved != nil && closeWhenMoved.moved))
      ;
    
    The new library function intOrdinal(n) returns a "numeric ordinal" representation of the number: '1st', '2nd', '3rd', and so on. (Thanks to Søren J. Løvborg for suggesting this and providing a sample implementation.)

    Along the same lines, the new library functions spellIntOrdinal(n) and spellIntOrdinalExt(n, flags) return fully spelled-out ordinals ('first', 'second', 'third', etc). The 'Ext' version takes the same bit-flag values as spellIntExt().

    The library no longer considers the player character to be an antecedent to a third-person pronoun when the game itself refers to the PC in the first or second person (as determined by the PC Actor's referralPerson property). In the past, typing X ME and then X IT examine the player character twice. This will no longer happen, except in third-person games, since the first X ME won't set ME as an antecedent for HIM, HER, IT, or THEM. This isn't especially important for IT and THEM, but it can be for HIM and HER if the PC has a specified gender.
    The library now treats HIM and HER a little differently in disambiguation responses. If HIM or HER is given as the answer to a disambiguation question ("which one do you mean..."), the parser first looks to see if there's a prior meaning of the pronoun that applies; it always did this much. This is the new part: if there is no prior meaning for the pronoun, or the prior meaning isn't one of the choices in the query, then the parser looks to see if any of the objects in the query match the pronoun. If there's exactly one match, the parser takes that as the result.
    The English library module defines a few new methods related to pronouns and gender. The new Thing methods canMatchHim, canMatchHer, canMatchIt, and canMatchThem determine if an object can match these individual pronouns. By default, these simply return true if the corresponding isHim/isHer/isIt flags are true. Actor overrides these so that they return true if the inherited version returns true and the actor can be referred to in the third person, which is determined by testing the additional new Actor method canMatch3rdPerson. The new canMatch3rdPerson method returns true if the actor isn't the player character, or the game refers to the PC in the third person.
    The former Actor methods canTouch(obj) and findTouchObstructor(obj) have been moved to Thing. There wasn't any need for these methods to be special to Actor, and it's sometimes useful to establish reachability between arbitrary non-Actor objects, so these are more appropriate for Thing than Actor.
    The new class TouchObjCondition implements a generic object-to-object reachability condition. This is similar to the existing touchObj pre-condition, but allows for arbitrary source objects, whereas touchObj can only test to see if the current gActor can touch an object. touchObj is now a subclass of TouchObjCondition.
    In the past, throwing an object at a MultiLoc object, or at a component within a MultiLoc object, didn't work properly. This has been corrected.

    In order to allow throwing at MultiLoc objects, the interface to a Thing method, getDropDestination, has been altered slightly. This method now takes an additional parameter giving the "sense path" that was used in the operation that's seeking the drop destination. The new sense path parameter is a list in the same format returned by Thing.selectPathTo. This path information allows getDropDestination to determine whence the MultiLoc object was approached - that is, it allows a MutliLoc object to find out which of its containers or containment peers was last traversed to reach the object. This path can be nil, but when it's supplied, it tells us how we're approaching the drop destination.

    Note that this change slightly affects the interfaces to several Thing methods: getHitFallDestination, throwTargetHitWith, stopThrowViaPath, throwViaPath, and throwTargetCatch. In all of these routines, the former 'prvCont' parameter is now replaced with the 'path' parameter. Similarly, the processThrow method passes the path rather than the previous container.

    In the English grammar, abbreviated words that include periods in the abbreviation (as in "Main St." or "J. Pretensions") are now treated a little differently, to ensure that abbreviated words don't have any bad interactions with unabbreviated equivalents. This change should be completely transparent to existing game code.

    First, the tokenizer no longer treats an abbreviation as a single token that includes the period, but rather as two separate tokens: one for the word itself, and a separate token for the period. The period is entered not with the ordinary punctuation token class, but with the new 'tokAbbrPeriod' class. Second, wherever ordinary noun tokens were allowed in the grammar, the grammar now instead accepts the new subproduction 'nounWord'. This new production matches an ordinary noun token, or a noun token followed by a tokAbbrPeriod token. Third, and similarly, the existing 'adjWord' production now accepts an adjective token followed by a tokAbbrPeriod token. Fourth, the vocabulary initialization (VocabObject.initializeVocabWith, which parses the vocabWords_ string) now enters each token that ends in a period with the period, as it did, but also without the period. So, for example, the vocabWords_ string 'main st.' will create an adjective 'main', a noun 'st.', and a second noun 'st' (in other words, 'st' is entered in the dictionary both with and without a trailing period).

    This corrects a problem that could have occurred with the old scheme if the same word was used abbreviated and unabbreviated. For example, if 's.' was used as an abbreviated word (for a string like 's. main st.', for example), and 's' was also entered in the dictionary as a verb (which it is by default, for the South action) the command "s." was not properly handled. The problem was that the tokenizer would see that "s." was entered in the dictionary with the period, so it tokenized it with the period. This precluded interpreting it as two tokens, 's' and '.', so the 's' was not matched to the verb, hence the command was rejected as not understood.

    A useful side effect of this change is that each abbreviation is now automatically entered in the dictionary without its period. This should be helpful to players who choose to avoid the extra typing of entering the periods in abbreviations.

    The new Hidden class can be used to implement objects that are present but not seen. This is useful for objects that are too inconspicuous to be noticed, or are positioned in such a way that they can't be readily seen (but in such a way that the ordinary sense mechanism would think the object was visible).
    The BulkLimiter class now parameterizes all of the messages that were previously coded directly as failure reports. The new BulkLimiter properties tooLargeMsg, becomingTooLargeMsg, and becomingTooFullMsg join the existing tooFullMsg in specifying the various failure reports; these properties in turn contain property pointers that refer to messages defined in the playerActionMessages object. This makes it easier to create "cosmetic" subclasses of BulkLimiter that define customized types of containment relationships.
    The new macro askForTopic(action) allows a single-object TAction to ask for a topic phrase and retry the command as a TopicTAction. This allows, for example, a CONSULT action with no topic specified to ask for a topic and turn itself into a CONSULT ABOUT action.

    To enable this change, the parser includes a new grammar production, EmptyTopicPhrase. This new production represents a topic phrase that requires an interactive response from the player.

    In the past, it wasn't possible to use askForDobj() to convert a LiteralAction into a LiteralTAction, or a TopicAction into a TopicTAction, by requesting a missing direct object from the player. This was due to an oversight in the library, which has now been corrected.

    Another similar, though more subtle, oversight made it impossible to use askForDobj() to convert a TAction into a TIAction. This is an unusual scenario, because the usual way to perform this conversion would be through askForIobj(): it's almost always the indirect object that's missing in these cases. However, there are cases where a missing direct object is possible; for example, we might want a full-fledged SPRAY verb that takes only one object, as in SPRAY AIR FRESHENER, but also have a two-object form for things like SPRAY SINK WITH DISINFECTANT, in which it's the direct object that's missing from the two-object form when we type SPRAY DISINFECTANT. (This particular example is a bit contrived, since we could just as well have defined the verb as SPRAY DISINFECTANT ON SINK, but even so, we don't want to be forced to find such a rephrasing.) In these cases, askForDobj() can now be used. When a TAction is retried as a TIAction with askForDobj(), the direct object of the TAction now becomes the indirect object of the TIAction; this is plainly the only way it can be, since we know the direct object is missing by virtue of the fact that we're asking for it.

    The interface of the method Actor.trackFollowInfo() has been changed slightly to improve its flexibility. In the past, this method assumed that the current action was actually performing the travel; now, the method instead takes an additional argument to eliminate this dependency. The new third argument gives the starting location of the travel; this is normally simply the location of the actor traveling, but could be something else; for example, when the actor is inside a vehicle, then the starting location is the vehicle's location.
    The new class NestedRoomFloor can be used for convenience in creating an object that serves as the floor of a nested room.
    The Floor class now accepts STAND ON commands. STAND ON FLOOR is simply remapped to STAND UP.
    The BurnWith action now resolves its direct object first. This is the better resolution order for this action, since it allows the direct object to set the scope for choosing the indirect object, which is likely to be omitted and thus require a suitable default to be chosen.

    In addition, the FireSource class now declares it illogical to burn an object using itself as the fire source. It's common for a Candle to also be a FireSource; this change prevents such an object from being chosen as its own default fire source in a command like LIGHT CANDLE, which would cause an infinite loop as we tried to light the candle with itself, which would require first lighting the candle, which would default to lighting it with itself again, and so on.

    The FireSource and Matchstick classes have also been fine-tuned to work together a little better. The FireSource class now specifies a logicalRank of 150 when used as the indirect object of BurnWith. The Matchstick class uses a logical rank of 160 when lit and 140 when not lit. This makes a matchstick an especially logical choice for starting a fire under all conditions, but makes a non-matchstick an even better choice when it's already burning. A lit match trumps everything (160), next best is an already-lit non-matchstick FireSource (150), and when there's nothing else around that's burning, a matchstick will still be a good choice (140). This ensures that we don't light a new match when there's another fire source readily at hand, but at the same time ensures that we'll default to using a match when no better choice is available.

    When an implicit action is interrupted with an interactive prompt, the announcement of the implicit action is now phrased as "trying" the action. This is along the same lines as the change in 3.0.6k that phrased failed implicit actions as "trying"; since an interactive prompt interrupts an implied action, it makes more sense to refer to the implied action as being merely attempted at this stage.

    Internally, the announcement generator distinguishes between failed actions and actions interrupted for interactive input, so it's a fairly easy matter to customize the types of messages separately. To customize the "asking" case separately from the "trying" case, customize the askingImpCtx object; in particular, override its buildImplicitAnnouncement() method and its useInfPhrase property as needed. The default in the library is to use the same message style for both cases.

    Due to a bug in the library, using "exit" to abort an action from within a check() handler did not properly mark the action as failed within the transcript, which meant that the "trying" form of an implicit action announcement was not generated. This has been corrected.
    In the past, when an implied action was remapped to another action (via remapTo), the normal announcement of the implied action wasn't generated. This has been corrected: the announcement will now be shown as usual.
    The parser is now a little smarter about excluding items with ALL EXCEPT in cases of simple remappings. In many cases, remapTo is used to create simple object-to-object synonyms for certain verbs; for example, OPEN DESK might be remapped to OPEN DRAWER when it's obvious that the two commands mean the same thing. In these cases, when an object is excluded from the ALL list with EXCEPT, the parser will now look for these simple synonym remappings, and it will treat them as equivalent. So, if OPEN DESK is remapped to OPEN DRAWER, and the player types OPEN ALL BUT DESK or OPEN ALL BUT DRAWER, then the desk and drawer will be excluded from the ALL list.

    Note that the parser can only exclude synonym remappings when they're remapped using the remapTo (or maybeRemapTo) mechanism. Remappings that are done using replaceAction() or the like can't be detected as synonyms, so they won't be excluded. Remappings that aren't simple synonyms won't be excluded, since remapping to different verbs or word orders changes the meaning of the command enough that the EXCEPT list shouldn't exclude the new objects. For example, if OPEN DOOR remaps to PUSH BUTTON, because a button is used to operate a mechanical door, OPEN ALL BUT BUTTON would not exclude the remapping to PUSH BUTTON, since we didn't say not to PUSH the button, only not to OPEN the button.

    The standard "weak vocabulary" checking in Thing now takes into account truncatable vocabulary words, as well as any other special dictionary comparison rules, by using the main dictionary's string comparator to check for weak tokens. Note that this routine uses the global property languageGlobals.dictComparator, so if the game ever changes the main dictionary's string comparator object, it should also change this global property at the same time.
    The Topic class now specifically disallows sensing a topic object with any physical senses. This ensures that a topic object will never be in any physical scope, even if the object is part of a containment hierarchy. (It's sometimes convenient to include a topic as part of a containment hierarchy in order to associate the topic with a physical game world object.)
    The possessive qualifier resolver (PossessiveResolver) now considers an object to be in scope in a possessive phrase only if the object has the property canResolvePossessive set to true. This property is true by default for all Things and nil by default for all Topics. In other words, by default, possessive phrase qualifiers can only be resolved to physical game world objects, never to abstract topics.
    3.0.6k

    Released August 17, 2003

    The new TopicEntry method isMatchPossible() tries to guess whether or not the TopicEntry can be matched by any input currently. This question is unanswerable in general, because it's asking if there exists any input, out of the infinite set of possible inputs, that will match the topic entry. However, the TopicEntry subclasses use heuristics to provide an answer that's right in most cases. In particular, the ASK and TELL entries consider themselves matchable if any of their matchObj objects are either known to the player character or are simply in scope; the GIVE and SHOW entries are considered matchable if any of their matchObj objects are in scope; and YES, NO, and special topic entries are always considered matchable. If a game creates a custom matchTopic() method, it might want to customize isMatchPossible() to match the custom match condition.

    The SuggestedTopic method isSuggestionActive() uses this new method to hide suggestions that aren't currently matchable. This provides an important benefit for the most common cases: topics won't be suggested until the player character knows about them. This avoids showing suggestions for things the player shouldn't even have heard of yet.

    To facilitate this change, isSuggestionActive() now takes an additional argument, giving the list of in-scope objects. This avoids the need to repeatedly compute the scope list when scanning a large list of suggested topic objects, which is important because the scope calculation is complex and can be time-consuming.

    Some important terminology for TopicEntry objects has been changed: where we formerly referred to a TopicEntry as "known," we now refer to it as "active" instead. The old terminology implied that TopicEntry objects model NPC knowledge; this was misleading, though, because the availability of a TopicEntry often depends on PC knowledge as well as NPC knowledge, or what the NPC wishes to reveal rather than what the NPC actually knows, or various other things. The new terminology removes the implication that NPC knowledge in particular is being modeled.

    This change requires new names for several TopicEntry properties: isKnown is now isActive; checkIsKnown is now checkIsActive; and topicGroupKnown is now topicGroupActive.

    Game code that uses TopicEntry objects will have to rename isKnown to isActive in the object definitions. The other renamed properties might have to be changed as well, of course, but these are less likely to appear in game code.

    The travel precondition mechanism has been tweaked a little to make it more flexible for handling nested-room situations. In the past, each travel connector was responsible for providing a precondition applied whenever an actor traversed the connector, via the travel connector's actorTravelPreCond(actor) method. This formerly returned the actorStanding precondition. While this was appropriate in most cases, since it ensured that the actor wasn't sitting in a chair or anything like that, it didn't make for easy customization for specialized nested rooms or other unusual situations.

    The change is that TravelConnector.actorTravelPreCond(actor) now returns a new precondition, actorTravelReady, instead of actorStanding. The actorTravelReady precondition abstracts the travel condition by letting the actor's immediate location handle it. In particular, it calls three new BasicLocation methods to carry out the precondition: isActorTravelReady(conn), tryMakingTravelReady(conn), and notTravelReadyMsg. By default, these methods do exactly what actorStanding does: they require the actor to be standing up before travel. However, a location can override any or all of these. For example, a particular nested room might want to use a command other than "stand up" to get the actor out of the nested room in preparation for travel; the nested room could override tryMakingActorTravelReady() in this case to carry out the desired implied command.

    Note that the travel connector to be traversed is an argument to the first two of the new methods. This allows the actor's current location to impose different requirements on travel via different connectors. For example, it might be necessary to climb down from a ladder before leaving via any connector except for a window high up on the wall, which can only be reached when on the ladder.

    The new Action method cancelIteration() lets you tell cancel further iteration of the current action, if multiple objects are involved in the action. You can call this method on the current gAction object during the 'check' or 'action' phases of execution. Once the method is called, the action will complete the execution cycle for the current object as normal, but it will then act as though the current object were the last object of the command.

    Note that this new method does not have any effect on the execution of the command for the current object; in particular, it doesn't interrupt the execution flow the way 'exit' and the like do. Instead, this method simply sets a flag in the current Action object telling it to ignore any additional objects it would otherwise iterate over. If you want to jump out of the execution cycle in addition to canceling further iteration, simply use 'exit' (or one of the similar signals) after calling this method. Note also that this method obviously can't cancel the iteration for any prior objects in the iteration.

    In the status-line and menu modules, there were some unnecessary "flush" operations (on banners and on the main window). Most of these were placed before "sizeToContents" calls on banners, which is unnecessary because that operation automatically flushes the underlying output stream anyway. These extra calls were not only unnecessary but were also undesirable, because the explicit flush operations always update the display immediately in addition to flushing internal text buffers. This made the display flicker annoyingly at times. The extra flushes have been removed, which makes the display updating faster and eliminates the flicker in the common cases covered by the changes.
    When a MenuLongTopicItem is part of a series of "chapters" (as indicated by the isChapterMenu property), and the user navigates directly from one chapter to the next, the menu system keeps the banner configuration for the chapter display throughout the change to the new chapter. In the past, the menu system removed the banner and then immediately restored it, which caused some visual flicker. This change eliminates the flicker during the chapter change.
    The English implicitAnnouncementGrouper implementation now uses pronouns, where appropriate, to refer to repeated objects when it groups a series of announcements. It frequently happens that the same object appears more than once when a series of implied actions is performed; this new phrasing makes the announcement text shorter and makes it read more naturally. For example, rather than saying "first unlocking the door, then opening the door", we'll now say "first unlocking the door, then opening it".
    The implicit action announcement mechanism now phrases the announcement for a failed action a little differently than for a successful action. When an action fails, the message now reads "(first trying to ...)". The old phrasing was a little awkward at times, because the announcement describes the action as being performed, and then the (usually) very next message says just the opposite, that the action wasn't performed at all because it failed. Depending on the wording of the failure message, the combination could even be misleading on occasion, since the suggestion in the announcement that the action was performed could lead to a player assuming that the failure message must refer to something else.

    To enable this change, the interface to the libMessages method announceImplicitAction() has changed slightly. The method now takes a "context" parameter, which is an object of class ImplicitAnnouncementContext The context contains information on the format of the message to be generated.

    Also, the implicitGroupTransform internally scans for failure messages that apply to implied action announcements, and rewrites the implied action announcements into the new format when failures are noted.

    Finally, the implicitAnnouncementGrouper object implementation in the English module does some additional grouping, to render a series of "trying" messages as readably as possible. When a series of consecutive "trying" messages appears, the grouper combines them under a single "trying" phrase, as in "first trying to unlock the door and then open it."

    Several small internal changes have been made in the English library module.

    First, the new method getInfPhrase() returns a string giving the full action description in infinitive form. This returns a phrase such as "open the box" or "unlock the door with the key". The verb is always in the infinitive form, but note that the English infinitive complementizer, "to", is not part of the returned phrase; this is because an infinitive is used without a complementizer in certain contexts in English, such as with auxiliary verbs (such as "might" or "should").

    Second, the new method getVerbPhrase(inf, ctx) returns the full verb phrase in either the infinitive form ("open the box") or the present participle form ("opening the box"). In English, the two forms are equivalent except for the verb ending, so the code to generate both forms can be easily consolidated into a single routine. The subclasses of Action override getVerbPhrase() as needed to provide appropriate phrasings. The context object 'ctx' is optional; if it's not nil, it should be an instance of the new class GetVerbPhraseContext. If the context object is provided, the routine uses it to keep track of pronoun antecedents for the verb phrase; the returned verb phrase will use a pronoun if one of its objects matches the current antecedent, and the routine will set the antecedent in the context for the next verb phrase that uses the same context. The context is useful if you're generating a series of verb phrases that you're going to string together into a single sentence; by using pronouns to refer to repeated objects, the sentence will be shorter and will read more naturally.

    Third, the implementation of getParticiplePhrase() now simply calls getVerbPhrase() in all cases.

    Fourth, the routine formerly called verbInf() has been renamed to getQuestionInf(), to better describe its purpose. This routine doesn't return the full infinitive form of the verb, but rather returns an infinitive form specifically for an interrogative in which one of the verb's objects is the interrogative's unknown. The old name was always misleading, but the addition of getInfPhrase() would have made the name even more confusing because of the similarity of the names.

    In the routine that tries to free up space in an actor's hands by moving objects into a "bag of holding," objects with no encumbering bulk are now never moved. In the past, objects were moved in descending order of encumbering bulk, so objects with zero bulk were typically ignored; however, if there weren't enough bulky objects present, it was possible for the routine to try moving bulkless items. Since worn items have no encumbering bulk by default, this meant the routine sometimes removed worn items and moved them to the bag, which was pointless.
    The new class FloorlessRoom, a subclass of Room, makes it easier to build rooms that represent locations without conventional floors. Anything dropped in the room is described as falling and vanishing below, since there's no floor for it to land on. You can control where dropped objects land using the bottomRoom property; by default, this is nil, which means that dropped objects simply disappear from the game.
    The Room class no longer requires the room's floor to be represented by the first element of the roomParts list for the room. Instead, the new method roomFloor returns the room's floor object; this method finds an object of class Floor in the roomParts list and returns it, or nil if there no object of class Floor in the list. This change eliminates the fragility caused by the former convention.
    A new mechanism provides more consistent handling when objects are dropped into a room. The new mechanism is used in the library to handle the DROP and THROW commands, both of which can result in an object being dropped into the enclosing room. The mechanism is extensible, so library extensions and games can add their own actions that drop objects into the room.

    Whenever an object is to be dropped into the enclosing room, the DROP and THROW commands first find the drop destination using the existing getDropDestination() method. Then, they invoke the new receiveDrop(obj, desc) method on the drop destination object. In most cases, the drop location is the enclosing room, so the enclosing room's receiveDrop() method is usually the one that will be invoked.

    The receiveDrop() method is responsible for carrying out the effects of dropping the object, including any game-state changes (such as moving the dropped to its new location) and reporting the results of the action.

    In most cases, the effects won't vary according to which command was used to discard the object. This is the reason that there's only one receiveDrop() method, rather than separate methods for DROP, THROW, and any library extension or game-specific verbs. By using a common routine to handle all means of discarding an object, the game only has to implement any special handling once.

    However, the message will almost always vary according to the command, for the simple reason that the message needs to acknowledge what the actor did to discard the item in addition to the effects of dropping it. This is the main reason the 'desc' argument is provided: it's a special "drop descriptor" object that lets you find out what command was used. You could also look at gAction to find the Action being performed, but the descriptor object makes your job a lot easier by providing some convenience methods for building report messages.

    The descriptor object is of class DropType. This class has two important methods. First, standardReport() displays the "normal" report for the action. For DROP, this is simply "Dropped." For THROW, this is a message of the form "The ball hits the desk, and falls to the floor." You can use the standard report when your receiveDrop() method doesn't do anything out of the ordinary and thus doesn't require a special report. Second, getReportPrefix() returns a string that gives the start of a sentence describing the action. The returned string is a complete main clause for a sentence -- but it has no ending punctuation, so you can add more to the sentence if you like. The main clause will always be written so that the object being dropped will be the logical antecedent for any pronouns in subsequent text, which lets you tack on a string like this: ", and it falls to the floor."

    Rather than building a message from the report prefix provided by the descriptor, you are free to build a completely custom message, if you prefer. To do this, you could use 'modify' to add your own method to each DropType subclass in the library, and then call this method from your receiveDrop(). Alternatively, you could use ofKind() to check what kind of DropType descriptor you got, and show custom messages for the ones you recognize.

    Thing.stopThrowViaPath() now simply calls throwTargetHitWith() by default. When an object interrupts a projectile's course, the result is exactly as though the projectile had been successfully thrown at the interrupting object, so the separate implementations were redundant.
    finishGame() now resets the sense context. This ensures that if the event that triggered the end of the game happened while a blocking sense context was in effect, the finish options are still listed. (Since the finish options involve direct interaction with the player, they obviously shouldn't be displayed in any NPC sense context.)
    Thing now has a constructor, which calls initializeThing(). This ensures that any Thing objects that are created dynamically (with 'new') are initialized in the same manner as objects defined statically.
    The library's handling of RESTART has been changed slightly to correct a problem that cropped up under certain unusual circumstances. The old handling reset the VM within the Restart action execution method, and then threw a RestartSignal to tell the main loop to re-enter the game from the beginning. This approach had a subtle flaw: if any dynamically-created objects were referenced from an intermediate stack frame in the code that initiated the restart, those objects were retained through the VM reset because they were still accessible via the stack. These objects then had a chance to remain visible to an object loop, such as those that run during the library pre-initialization. The Restart action handler now simply throws the RestartSignal to the main loop, and the main loop now performs the VM reset. This ensures that the stack is reset to initial conditions before the VM reset occurs, which in turn ensures that only those dynamic objects that should survive the reset do survive the reset.
    3.0.6j

    Released August 2, 2003

    The new class DistanceConnector is a specialized subclass of SenseConnector that allows two (or more) locations to be connected, but at a distance. This is handy for situations such as divided rooms (where we use two or more Room objects to model a single large physical location), and for cases where two rooms are open to one another (such as a balcony overlooking a courtyard).
    The new class Vaporous is a subclass of Intangible designed for insubstantial but visible objects, such as fire, smoke, and fog. This class works a lot like Intangible, but it has a visual presence, and it specifically allows the commands Examine, Smell, and Listen To; in addition, it allows the commands Look In, Look Under, Look Behind, Look Through, and Search, and responds to these with "You just see (the object)."
    The Lister class no longer sets the 'seen' attribute of objects it lists, because this attribute is now more generally handled in the main room description routines (per the change in meaning of 'seen' in 3.0.6i). As a result, the Lister.markAsSeen method has been removed.
    A new macro, gSetKnown(obj), marks an object (usually a Thing or a Topic) as known by the player character. This is simply short-hand for obj.setKnownBy(gPlayerChar, true), for convenience.
    The EventList object has been enhanced slightly to accept property pointer entries. A property pointer entry is interpreted as a property to invoke on the EventList itself, with no argument. This allows writing complex steps as separate properties of the EventList object, and then referring to them from the script by property pointer.

    In addition, the ScriptEvent class has been eliminated. Instead, simply use Script objects in an event list. So, when an object appears in an event list, the EventList class now invokes the object's doScript() method. This makes it easy to build event lists recursively.

    The new EventList subclasses CyclicEventList and StopEventList provide new pre-defined options for the behavior of an event list after visiting all elements. The new subclass SyncEventList provides a list that synchronizes its state with another list.

    The TextList, StopTextList, and SyncTextList classes are now simple subclasses of CyclicEventList, StopEventList, and SyncEventList, respectively.

    A new EventList method, scriptDone(), is invoked by doScript() after it processes the script's current step. By default, this method simply invokes advanceState() to advance the event list to its next state. Subclasses can override scriptDone() so that it does not advance the script's state, or does something else in addition.

    The new EventList subclass ExternalEventList overrides scriptDone() so that the method does nothing. This makes it easy to create an event list that is driven externally; that is, the event list doesn't advance its state when doScript() is invoked, but only advances its state in response to some external processing that calls advanceState() directly on the event list object.

    The Room class now provides a dobjFor(Examine) that replaces the Examine action with a Look action. This means that if a Room object has ordinary vocabulary words defined, and the player types EXAMINE (room), the command will automatically be treated as though the player had typed LOOK AROUND.
    The new class IndirectLockable is a subclass of Lockable that can be used for situations where a lockable object cannot have its locked status directly manipulated by LOCK and UNLOCK commands. The reply to LOCK and UNLOCK can be customized via the object's cannotLockMsg and cannotUnlockMsg properties.
    The Lockable class now handles the 'objUnlocked' precondition a little more subtly. The class now keeps track of whether or not the player knows the object to be locked; the status is assumed to be unknown initially. When the player doesn't know the status, the Open action does not apply an objUnlocked precondition, but instead disallows the action in the check() routine. When the status is known, the objUnlocked precondition is applied as before. Whenever the Open action fails in the check() due to the object being locked, the status is taken to be known for subsequent attempts.

    This change makes the precondition handling a little more intelligent. When the player doesn't know the status beforehand, an Open command will simply fail with the discovery that the object is locked. This usually makes more sense than attempting to unlock the object automatically with an implied Unlock command, because if the object isn't already known to be locked, there's no reason to try to unlock it. When the status is known, however, it makes sense to perform the implied Unlock.

    Objects whose lock status is visibly apparent can be marked as such with the lockStatusObvious property. This property is nil by default; an object whose lock status can be visibly observed should set this to true. When this property is set, the lock status is always known, since we'll assume that actors will simply look at the lock and observe the status. In addition, when this property is set, we'll add the locked/unlocked status at the end of the object's 'Examine' description (we'll add "It's currently locked" or "It's currently unlocked").

    A new class, TravelWithMessage, can be mixed (using multiple inheritance) with TravelConnector or a subclass of TravelConnector to create a connector that shows a message as it's traversed. This is slightly more flexible than using TravelMessage, since TravelMessage can't usually be mixed with other TravelConnector-derived classes.
    A new TravelConnector property, stagingLocation, gives the location in which a traveler must be located prior to traversing the connector. In the past, the connector simply assumed the traveler needed to be in the connector's direct location; this new property allows this condition to be customized. At times, the connector might be inside an intermediate container, for example, in which case the staging location might be a container of the container. By default, the staging location is the connector's location, so connectors that don't override stagingLocation will have the same behavior as before.
    A new Thing method, roomLocation, returns the 'self' if the object is capable of serving as a location for actors, or the nearest container that is a roomLocation otherwise.

    The Passage travel connector class now uses the other side's container's roomLocation as the default destination of the passage, rather than the other side's immediate container, as it did in the past. This makes it much easier to create passages that model non-trivial containment relationships, such as holes in walls, since an intermediate container (between the Passage and the enclosing Room or NestedRoom) will no longer create any confusion in performing the travel.

    The NoTravelMessage class can now multiply inherit from Script, for convenience in defining message lists. If you want to provide several different messages to choose from when the connector is traversed, you can make your connector object inherit from TextList as well as NoTravelMessage, and the travelDesc() will automatically invoke the script to show a message.

    If the travel connector has a non-nil location, the TravelConnector class's connectorTravelPreCond method adds a precondition that the connector must be touchable. This ensures that characters won't be allowed to traverse connectors that are visible but on the other side of a window, for example, or connectors that are out of reach.

    The travel notification protocol has been changed very slightly. The travel connector's noteTraversal() method is now invoked from within the Traveler.travelerTravelTo() method rather than the TravelVia handler in the connector. This change means that noteTraversal() is called just before the traveler is actually moved, and in particular that it occurs after all of the other travel notifications (beforeTravel, actorTravel, and travelerLeaving). This is generally the more desirable ordering for the notifications, because it puts the connector's reaction closest to the actual traveler movement. If you want the connector to do something special before any of the other notifications, you can override the action() handler in the connector's dobjFor(TravelVia) so that it performs the special code and then inherits the base class handling.
    When an actor travels through a connector that leads to a location with a posture other than the actor's original posture, the new location now sets the actor's posture directly, rather than via a nested action. For example, if a travel connector leads to a chair, then traversing the connector will move the actor into the chair and set the actor's posture to 'sitting', without activating a nested Sit action. In the past, the nested Sit action would have been used instead. This change is in BasicLocation.travelerArriving(), which sets the new actor's posture via a call to Actor.makePosture().

    This change is generally desirable because we usually want arrival by travel connector to be self-contained; we don't want it to appear as though the character walked to the new location and only then performed a SIT ON THE CHAIR command (say). It's worth noting, however, that any side effects of the usual way of getting into the new location's posture will be skipped. Therefore, when linking a travel connector into a nested room with a non-standing posture, you'll need to consider any side effects you'd want to trigger, and trigger them explicitly on the travel connector itself.

    The new Actor method scriptedTravelTo() simplifies coding of scripted travel for an actor. This routine is suitable for cases where an NPC is to perform a series of scripted travel actions, where each room along the way is scripted in advance. This routine is not suitable for goal-seeking NPC's, because it can only perform travel to a location adjacent to the actor's current location, and because it's "omniscient" (it doesn't take into account the actor's knowledge of the the map, but simply considers the actual map). For cases where an NPC is to visit a scripted series of locations, this is a convenient way to accomplish the travel, since it requires specifying only the destination of each step of the travel.
    The class SecretDoor is now based on BasicOpenable as well as ThroughPassage. The class previously didn't have BasicOpenable anywhere among its superclasses, which prevented its open/closed mechanisms (initiallyOpen, isOpen, makeOpen) from working consistently with other types of doors, which are all based (indirectly) on BasicOpenable.
    The new ThroughPassage subclass PathPassage is a specialization for cases such as outdoor walkways, paths, and streets. The key difference between the standard ThroughPassage and the more specialized PathPassage is that path passages are not considered enclosed, so the descriptions for arrival and departure via a path take this into account. In the English messages, we describe travel on a path as being "via" the path rather than "through" it. In addition, the English parser accepts "take" as a synonym for "enter" or "go through."

    To handle this new class, TravelMessageHandler and its subclasses have a new pair of methods, sayArrivingViaPath() and sayDepartingViaPath(), for generating travel messages related to paths.

    The new method Actor.actorTravel(traveler, connector) complements beforeTravel() and afterTravel(). This new method is called on the actor who initiated the travel (that is, the actor performing the command, if any); it's called after the beforeTravel() notifications are sent to nearby objects. This method is provided for symmetry with the beforeAction/actorAction/afterAction set.
    The AccompanyingState.accompanyTravel() method has been changed slightly. The first parameter is now a Traveler object, which might be an Actor but could also be another kind of Traveler, such as a Vehicle. This allows the game to determine whether the NPC is to accompany actors when they travel on vehicles or in other indirect ways.

    In addition, the class's beforeAction() method, which responded to the initiating actor's travel, has been changed to a beforeTravel() method. This takes advantage of the more precise kind of notification offered by beforeTravel(). In particular, using this method avoids unnecessary attempts to accompany an actor who attempts a travel command that doesn't actually result in travel (because the connector doesn't go anywhere, for example).

    If a travel connector is located inside a nested room, the preconditions for traversing the connector now correctly move the character to the nested room prior to the travel. In the previous library version, the enclosing room tried to add its own precondition requiring the traveler to be in the outer room, which was incorrect.
    The Follow command now provides better feedback to the player in certain cases. When the player has previously observed the target actor departing, but from a different location than the player's current location, the Follow command will now respond with the message "You didn't see which we he went" (rather than "You can't do that from here" as it did in the past).
    An Actor's executeTurn() method, and all of the methods it invokes (the Actor's idleTurn() and the ActorState's takeTurn(), for example), now run within a valid "action environment," just like fuses and daemons started doing in 3.0.6i. In addition, the executeTurn() method sets up a visual sense context for the actor, ensuring that any output displayed in the course of the actor's turn is displayed only when the actor is visible to the player character. (It is presumed that the effects of any action that an actor performs are visible if and only if the actor is visible. This is just the default case, though: the game is always free to set up a separate sense context, using callWithSenseContext(), whenever necessary.)

    To facilitate this change and make it easier to override an actor's per-turn processing, the main handling for an actor's turn is now in a new method, executeActorTurn(). The executeTurn() method simply sets up the action environment and then invokes executeActorTurn(). In most cases, subclasses that need to specialize the per-turn processing should override executeActorTurn() rather than executeTurn(), since this will let the override run in the action environment.

    The optional initial "look around" that the runGame() function performs is now executed within an "action environment," just like fuses and daemons started doing in 3.0.6i. This allows the initial room description to refer to gActor and gAction, which some library methods do implicitly.
    callWithSenseContext() now uses "lazy evaluation": rather than immediately calculating the effect of the new sense context, the routine now simply notes that it has a new context, and defers calculating the effects of the new context until the effects actually need to be known. This makes it very inexpensive to set up a new sense context in cases where the context won't actually be needed for anything, which is frequently the case in daemons, fuses, and the standard per-turn processing for NPC's. This can save a substantial amount of time when a game has numerous background events if, as is typical, most of the background events don't generate any output on a given turn.
    The new macros actorStateDobjFor(action) and actorStateIobjFor(action) make it convenient to delegate an actor's processing for an action to the actor's current ActorState object. These are defined in adv3.h, and work analogously to asDobjFor(action) and asIobjFor(action). Using these macros involves two steps. First, in the Actor, write a short handler for the action that uses the macro to delegate to the state object:

      bob: Person
        // ... other definitions ...
        dobjFor(AttackWith) actorStateDobjFor(AttackWith)
      ;
    

    This sets up the "bob" Actor object so that the handling for AttackWith, with bob as the direct object, will be delegated to bob's current ActorState object. Second, just write a normal dobjFor() handler in each of the ActorState objects associated with bob:

      + bobFighting: ActorState
        dobjFor(AttackWith)
        {
          action() { "Bob puts up a fight..."; }
        }
      ;
      + bobCowering: ActorState
        dobjFor(AttackWith)
        {
          action() { "Bob just hides in the corner..."; }
        }
      ;
    
    The withActionEnv() function now takes an addition parameter giving the Actor object to use as the gActor value while running the callback. In the past, this function always used the player character Actor (given by gPlayerChar); the new parameter allows other actors to be used instead.
    The withCommandTranscript() function now returns the result of the callback function, rather than the transcript object created to run the callback. Callers who still need the transcript object as the result can simply return gTranscript from the callback function itself, as this value will then be passed up as the return value from withCommandTranscript().
    The attentionSpan property of an InConversationState object can now be set to nil to indicate that there is no attention span limit for the NPC. Setting attentionSpan to nil will prevent the NPC from ever ending the conversation by virtue of being ignored for too long.
    The standard SpecialTopic template (in adv3.h) now allows defining a list of strings (for the textStrings property) in place of a single response. The standard TopicEntry, SpecialTopic, and YesNoTopic templates now make the response/response list entry optional, allowing the rest of the templates to be used even if a custom method is needed to handle the response.
    A new DefaultTopic subclass, DefaultAnyTopic, matches all of the different sorts of topic interactions: ASK, TELL, GIVE, and SHOW. This is especially useful for default handling in conversation nodes (ConvNodes) where you want catch all topics not otherwise handled.
    The new TopicEntry property isConversational lets a TopicEntry specify whether or not the response is "conversational." A conversational response is one that involves some kind of interaction with the NPC. This property is true by default, but some response objects will want to set it to nil to indicate that the response doesn't involve any conversation with the NPC. For example, a response like "You don't think he wants to discuss that right now" is non-conversational, because it doesn't involve any exchange with the NPC.

    When isConversational is nil for the response object that's found for a conversation command, a ConversationReadyState will not enter its in-conversation, but will simply show the response and remain in the conversation-ready state. This means that there will be no "greeting" exchange for these responses.

    The new ConvNode method canEndConversation(actor, reason) lets a node prevent a conversation from ending. 'reason' is an enum indicating what is triggering the attempted termination: endConvBye if the player typed BYE; endConvTravel if the other actor is leaving (i.e., the player typed a travel command); or endConvBoredom if we're terminating the conversation on our own because our attentionSpan has been exceeded. This method can cancel the operation that attempted the termination simply by returning nil; the method should display an explanation when returning nil, to let the player know why the command is being canceled.

    In related changes, ConvNode.endConversation() and ActorState.endConversation() now take 'reason' codes rather than 'explicit' flags. This gives the methods full details on why the termination is occurring.

    The new AgendaItem class provides a simple new mechanism for handling cases where you want to model an NPC in terms of motivation. Each actor has an "agenda," which is a list of AgendaItem objects. On each turn, the actor's ActorState object (in the takeTurn method) will look at the agenda list, to see if any items are ready to execute. If there's an item that's ready to execute, the ActorState will execute the first one that's ready.

    Each AgendaItem object has a method called isReady, which indicates whether or not the item is ready to execute. An actor will only execute an agenda item once it's ready. The invokeItem method contains the code that actually carries out the agenda item's action.

    By default, an agenda item is removed from the actor's agenda list when it's executed. However, if the item's stayInList property is true, then the item will not be removed on execution. This property lets you handle agenda items which the actor will try repeatedly, until some other code determines that the goal has been achieved and removes the item from the actor's agenda.

    You must nest AgendaItem objects within their actor, using the "+" notation. This associates the AgendaItem objects with the actor, but it doesn't add them to the actor's agenda list. You must explicitly add agenda items to the actor's agenda list by calling the actor's addToAgenda() method. Agenda items must be explicitly added because an actor's motivation typically will change dynamically as the game's events unfold.

    Autonomous NPC conversation continuation processing has been moved from InConversationState to the base ActorState. This allows actors that don't use greeting protocols to nonetheless use stateful conversations.

    To accommodate this change, the default takeTurn() method in ActorState now preforms more processing. If the actor has an active ConvNode, then this takes precedence over any other default processing for the actor's takeTurn() method. If the actor hasn't engaged in conversation on the same turn, we invoke the ConvNode to continue the conversation under NPC control; otherwise, we do nothing more. If there's no ConvNode, we process the actor's "agenda." If there's no ConvNode, and the actor has no agenda items that are ready to execute, and the ActorState inherits from Script, then we invoke the script. This structure means that a stateful conversation takes precedence over everything, and an agenda item takes precedence over state-level background activity.

    In the Actor class, the TellAbout and AskAbout handlers now include the "canTalkToObj" precondition by default. This ensures that the NPC being addressed can actually hear the other actor, to disallow conversations in cases such as when the two actors are too far apart to hear one another. The HELLO, GOODBYE, YES, and NO commands now all make the same check as well, in the sayToActor() method.

    Similarly, ConversationReadyState now makes the same check before going through a greeting. If the initating actor can't talk to the target actor, the enterConversation() method says so and uses 'exit' to terminate the command. This prevents strange situations from arising commands that don't require the canTalkToObj precondition. (SHOW TO works this way, because the actors don't necessarily have to be able to talk to each other to use SHOW TO; we could show a note to an NPC on the other side of a sound-proof window, for example.)

    The new TopicEntry subclass InitiateTopic provides an easy way of making an NPC initiate conversation based on simulation objects in the environment. To make an NPC trigger a conversation, call the actor's initiateTopic(obj) method, where 'obj' is the simulation object you want to use to key the conversation. This will find an InitiateTopic object in the actor's topic database matching the given object ('obj'), and show its response text. One easy way to use this is to initiate conversation based on the NPC's current location: just create InitiateTopic objects keyed to those locations where you want the NPC to say something special, and add a line like this to the ActorState's takeTurn() method:

      getActor().initiateTopic(getActor().location);
    
    The "topic inventory" list shown by the TOPICS command is no longer enclosed in parentheses by default. The parentheses are still included when the topic inventory is shown implicitly (by a TALK TO command or a <.topics> tag, for example).
    The new ConvNode property limitSuggestions allows a ConvNode to indicate that suggested topics from the broader conversation context should not be included in a topic inventory listing. This is useful for times when the ConvNode won't allow the player to stray from the subject, such a a ConvNode that only allows a YES or NO answer to a question. In these cases, set limitSuggestions to true. The property is nil by default, which causes topic inventory listings to include not only any suggestions defined within the ConvNode, but also any defined in the active ActorState or in the Actor itself.
    The new Actor.scheduleInitiateConversation(state, node, turns) method lets the game set up an NPC to start a converation at the next opportunity after the given number of turns has elapsed. If 'turns' is zero, then the conversation can start on the next turn on which the NPC hasn't been targeted for conversation by the player; if 'turns' is one, the player will get at least one more turn before the conversation can start, and so on for higher 'turns' values. The important thing is that the conversation can only start on a turn on which the NPC isn't targeted with a conversational command (ASK, TELL, etc). The Actor checks its list of pending conversations on each turn in its takeTurn() method, before invoking the current state's takeTurn() method.
    In the previous version, if Actor.initiateConversation() was called from within the player character's turn (in an NPC's beforeAction() handler responding to a player character action, for example), and the ConvNode had a "continuation" message (defined in npcContinuationMsg or npcContinuationList), the first continuation message was incorrectly shown on the same turn. This no longer occurs; the continuation message won't be shown until the next turn at the earliest. ("At the earliest," because a continuation message won't be shown on a given turn if the player character addresses a conversational command to the NPC on that turn.)
    When an actor continues a conversation of its own volition (using npcContinueMsg or npcContinueList) in a ConvNode, if the continuation routine actually displays any text, then the ConvNode will automatically set the player character and the initiating NPC to be in conversation with one another. This ensures that any subsequent conversational command from the player will have the initiating NPC as the default interlocutor.
    In the past, the REPLAY QUIET command didn't display any acknowledgment, and improperly left all subsequent text in bold-face. This has been corrected.
    ActorState.greetingsFrom now delegates to a method of the same name in Actor; this makes it easier to change the default greeting for a simple actor without a state object.

    Along the same lines, ActorState.showGreetingMsg has been removed, and replaced with Actor.defaultGreetingResponse. The method has been moved to Actor because of the change to greetingsFrom, and the method has been renamed for consistency with the similar pattern for the other conversation command default message methods (for ASK, TELL, etc) in Actor.

    Along the same lines, ActorState.goodbyeFrom now delegates to a method of the same name in Actor by default. A new Actor method, defaultGoodbyeResponse, displays the default message, for consistency with the naming of the similar methods for other conversation commands.

    Two new ConversationReadyState methods have been added: enterFromByeMsg and enterFromConvMsg. These are provided for convenience as single-message complements for enterFromByeList and enterFromConvList, respectively. By default, these simply invoke their respective list scripts.
    VocabObject has a new feature that allows an object's vocabulary to be divided into "strong" and "weak" tokens. Weak and strong tokens are the same as far as the parser is concerned, and are entered into the dictionary as usual. The difference is that a weak token can only be used to refer to an object in combination with one or more strong tokens. That is, if the player enters a noun phrase, and the noun phrase matches all of the vocabulary for a given in-scope object, the object will match the noun phrase only if one or more of the words in the noun phrase is a strong token for the object.

    The purpose of this new feature is to make it easy to define extra vocabulary for an object that the parser accepts for an object without creating any new ambiguity. This comes up in situations where you simply want to create additional synonyms for an object, as well as in cases where an object is most strongly identified in terms of its relationship to something else. For example, you might have a location that contains a couple of doors, one to a house and the other to a shed. You could use adjectives ("house door" and "shed door") to differentiate the doors, but it might be more natural to use a prepositional phrasing, such as "the door of the house" and "the door of the shed." If you also have a house and a shed object, though, this phrasing would create unwanted ambiguity with those objects. This is where weak tokens come in. If you define "house" and "shed" as weak tokens for the respective door objects, the parser will never take those words alone to refer to the doors, so there will be no ambiguity with the house and shed objects themselves; but the parser will still allow "house" and "shed" to refer to the doors, as long as they're used in combination with the strong token "door".

    Weak tokens are defined per-object (they're not global). Each object that has weak tokens keeps a list of its weak tokens in its 'weakTokens' property. In the basic VocabObject.matchName() implementation, the object checks the noun phrase to see if it consists entirely of weak tokens, and rejects the match if so.

    When you define an object, you can define weak tokens in one of two ways. If you define 'noun' and 'adjective' (etc.) properties directly, simply define a 'weakTokens' property containing a list of strings giving the object's weak tokens. If you use the vocabulary initialization string, simply enclose each weak token in parentheses. For example:

    + Door 'front door/(house)' 'front door of house' 
      "The door is badly weathered. "
    ;
    
    VocabObject.matchName() and matchNameDisambig() now invoke a new method, matchNameCommon(), to carry out their common handling. This change facilitates the weak-token feature mentioned above. In most cases, when a game needs to override an object's name matching test, it should override matchNameCommon(), since this method provides the common handling for normal matching and disambiguation matching. Games can still override matchName() and/or matchNameDisambig() individually, but it's only necessary to do so when you want to use different rules for normal matching and disambiguation matching.
    The English parser's handling of quoted string literals as adjectives has been improved slightly, but the changes will require some small adjustments to any games currently using literal adjectives.

    Instead of allowing any adjective to be quoted, the parser now only matches quoted strings that are specifically designated as "literal adjectives" in an object's vocabulary. To designate an adjective as a literal adjective, you can either enclose the adjective in double-quotes in the vocabulary initializer string, or you can explicitly define the word using the 'literalAdjective' part-of-speech property instead of the normal 'adjective' part-of-speech. For example, you could define an elevator button for the lobby level like so:

    + Button '"L" button' 'L button' "It's a button labeled <q>L.</q> ";
    

    This change makes the parser stricter about which words it accepts as quoted adjectives, but it also allows the parser to be more flexible about requiring the quotes. In particular, the parser now accepts all of the following forms for the button defined above: "L" button, button "L", L button, and button L. In the past, the parser was unable to accept the last of these, because without the quotes, it wasn't able to tell that the unquoted "L" was meant as a literal adjective. Now that the "L" is defined in the dictionary as a literal adjective, it's no longer necessary for the player to quote the word for the parser to recognize it as a literal adjective, so the parser is able to accept the form with the literal adjective following the noun.

    Note that this change also involves a subtle change to the special "*" vocabulary wildcard token. In the past, if the string '"*"' (that is, an asterisk enclosed in double-quotes) appeared as an 'adjective' dictionary entry for an object, then that object matched any quoted string used as an adjective in player input. Now, this effect is obtained by using the string '*' as a 'literalAdjective' dictionary entry for an object. This change has no effect at all on the way you write vocabulary initializer strings, since you still enclose the asterisk in quotes in that string; however, if you're defining a 'literalAdjective' list directly for an object, you now must drop the quotes and add a word entry consisting of simply the asterisk.

    The English parser now accepts pronouns as responses to disambiguation questions. (The disambigListItem(noun) rule now matches completeNounPhraseWithoutAll rather than qualifiedNounPhrase, as it did in the past; the new sub-production matches pronouns, which qualifiedNounPhrase does not.)
    In the English parser rules, the command "T topic" is now accepted as a short-cut for "TELL ABOUT topic." Together with the "A topic" shortcut for ASK ABOUT, this can greatly reduce the amount of typing a player has to do in a game that has a lot of ASK/TELL character interaction.
    In the English parser rules, the Hello action now accepts "hallo" as equivalent to "hello" or "hi", and also accepts "say" with any of these variations.
    A new Action method, callAfterActionMain(obj), allows a game to register an object for invocation at the end of the current action. This is only meanginful when used on the current gAction object, since it registers for notification of completion of the current action. When the current action is finished - including the iteration over all of the objects involved in the action - the action invokes the afterActionMain() method of each registered object.

    This new method is especially useful for adding a "summary" of an action that involves multiple objects. Each individual object's action handler (an action() method in a dobjFor() block, for example) can register a handler object to receive notification when the overall command is finished. A given object can be registered only once - redundant registrations are simply ignored - so each individual iterated object doesn't have to worry about whether or not other iterated objects will register the same handler. Then, at the end of the action, the handler will be invoked; it can determine what happened in the action, and do something based on the end result. For example, the handler could scan the transcript (gTranscript) for certain types of reports, and add a summary message based on the reports, or could even replace some of the individual reports with a summary. The handler could also take addition action based on the overall end results; for example, in a GIVE TO command, a handler could look at the full set of objects successfully given, and decide that the combination is sufficient to allow the recipient to give the donor something in return.

    A new CommandTranscript method, summarizeAction(), can be used with the new callAfterActionMain() system to generate a summary of an action that involves multiple objects. summarizeAction() scans through the transcript, looking for runs of two or more reports for the current action that match criteria specified by the caller (via a callback function). For each run of two or more consecutive qualifying reports, the method removes those reports, along with their corresponding multi-object announcements, and replaces the last of the reports with a single "summary" report generated by the caller (via another callback function). This method lets you turn a transcript like this:

       gold coin: Bob accepts the gold coin.
       gold coin: Bob accepts the gold coin.
       gold coin: Bob accepts the gold coin.
    

    into something like this:

       Bob accepts the three gold coins.
    

    This sort of summary isn't straightforward to generate in general, and the library makes no attempt to apply it generically. In specific cases where you can control the range of possible results, though, this can be a powerful way to improve the game's output by describing an iterated action as though it were a single logical unit.

    Phrases involving quantities ("five coins") and "all" with a plural ("all coins") are now resolved a little more consistently and intuitively, from a player's perspective. First, phrases involving quantities are now always disambiguated in the same manner as singular phrases, and then the required number of objects is chosen; in the past, disambiguation filtering was not as consistent. This change ensures in particular that "collectives" are properly filtered in or out; the old mechanism sometimes missed collectives because it sometimes skipped the normal disambiguation filtering. Second, "all"-plus-plural phrases are now resolved to all matching objects, rather than to only the most logical subset of matching objects, as was the case in the past. This means that a command applied to "all coins" really is applied to all coins that are present, even those for which the action isn't currently valid.

    To implement these changes, NounPhraseProd has a new method, getVerifyKeepers(), that the disambiguation filtering methods call to reduce the list. The default NounPhraseProd definition of the method does the traditional filtering that the disambiguation filter did, which keeps just the most logical subset of the results. AllPluralProd overrides this method to keep everything in the list, and QuantifiedPluralProd overrides the method to select a subset with the desired number of entries.

    The CollectiveGroup object by default no longer matches a noun phrase that specifies a quantity ("take five coins" or "take both coins"; or any singular phrase, since a singular phrase specifies a quantity of one). Collective groups are designed to stand in for a completely collection of in-scope individuals, not for arbitrary subsets of the individuals, so when a quantity is specified we must fall back on iterating over a selected subset of the individuals.

    To implement this change, the filterResolveList() method now takes two additional parameters: one giving the quantity specified in the noun phrase, if any, and another giving the role played by the object (DirectObject, IndirectObject, etc). When the noun phrase specifies a quantity, the new parameter will be set to an integer giving the quantity specified. If the noun phrase is unspecific about the quantity (as in "take coins" or "get all coins"), then the quantity parameter will be nil.

    Note that the isCollectiveAction() method now takes an additional parameter as well, giving the role in the action played by the object being resolved (DirectObject, IndirectObject, etc). This allows differentiating the handling based on both the action and the role played in the action.

    Custom CollectiveGroup objects can represent specific quantities of objects, from the player's perspective, if desired. For example, a game might want to create a CollectiveGroup that represents a quantity of money, rather than dealing with the individual coins making up the quantity. To do this, the CollectiveGroup object must override the filterResolveList() method, and must set the "quant_" element of its own ResolveInfo entry to an integer giving the number of grammatical objects it represents. The CollectiveGroup must keep track of what it's representing somehow; the best way to do this is to create a new instance of the CollectiveGroup itself, and store a property in the new instance giving the quantity represented by the collective. Note that using CollectiveGroup objects in this manner is tricky; you'll need to code action handlers for the custom CollectiveGroup object so that they correctly take into the account the quantity represented by the group object.

    The ownershipAndLocationDistinguisher has been refactored into two separate distinguishers: ownershipDistinguisher and locationDistinguisher. Each of these new distinguishers is very similar to the old combined distinguisher, but the ownershipDistinguisher gives priority to ownership, regardless of the containment relationship. The library classes that define distinguishers now use both of these, with the ownership distinguisher applied first.

    The purpose of this change is to ensure that ownership will be used as a distinguishing feature whenever possible, before the parser falls back on location. The old combined distinguisher could only use ownership in the limited case where the owner and immediate location were the same, because it couldn't otherwise be sure that it would be able to distinguish multiple objects with the same owner but different locations. By separating the ownership and location distinguishers, we first try to identify objects purely by ownership; when this fails, we fall back on the old approach of identifying by immediate location in preference to owner.

    To support this change, the English methods for owner-or-location names (aNameOwnerLoc, theNameOwnerLoc, countNameOwnerLoc) now take a parameter indicating whether to use ownership or location as the first priority in generating the name. When ownership takes priority, these methods will show the name with a possessive form of the owner regardless of the containment relationship between the object and its owner.

    Possessive qualifiers (such as "bob's" in "bob's chair") are now resolved in a special resolution context that allows referring to owners that aren't in scope for the purposes of the phrase being qualified (such as the "chair" in "bob's chair"). A possessive phrase is now considered in scope if it's in scope for the phrase being qualified or it's known to the actor performing the command. This allows possessive phrases to be used to refer to objects that are present, and which are known to belong to an actor, even if the actor itself isn't present.
    When the parser generates a disambiguation prompt, and the prompt distinguishes objects by possessives ("which match: bob's match or your match?"), the parser automatically sets the pronoun antecedents for "him" and/or "her" to the people mentioned in the prompt. This allows the player to answer with "his" or "hers" (or "his match"), referring back to the person or people mentioned in the message.
    The parser error messages that display literal text entered by the player (such as the message for unrecognized punctuation marks, and the message for unknown words) now HTML-ify the user's text. This ensures that any markup-significant characters (such as "<" and "&") are converted into HTML sequences that display the corresponding characters.
    A bug in the English parser's rule for phrases like "anything in the box" caused run-time errors when the word "anything" alone was used as a noun phrase. This has been fixed.
    In the hint system, the new Goal methods openWhenTrue and closeWhenTrue can be used to define arbitrary conditions that open and close the goal. These new general-purpose conditions supplement the more specific conditions (openWhenSeen, openWhenAchieved, etc.); the goal will be opened if any of the openWhenXxx conditions are met, and will be closed when any of the closeWhenXxx conditions are met.

    The new Goal property openWhenDescribed lets you specify that the goal should be opened when the referenced object is described (usually with EXAMINE). This is good for goals where the existence of a puzzle isn't made apparent until the full description of an object is viewed. The new property closeWhenDescribed correspondingly closes the goal when the referenced object is described.

    The RESTART command no longer resets the commandSequencer to the "no-command" state, as it did in the past. This change corresponds to the change in commandSequencer start-up state effected in 3.0.6i.
    The English grammar now accepts '1' as a synonym for 'one' or 'any' in singular indefinite noun phrases, such as "take 1 coin." (In the past, the grammar omitted this alternative.)
    The new class SecretFixture is designed for objects that are needed in the internal implementation but are not meant to be manipulated directly by characters. This is a simple subclass of Fixture; the main difference is that a SecretFixture is hidden from "all," to help prevent direct references to it in commands.
    In the English grammar, a new production named singleNounOnly can be used where a single noun (rather than a list of nouns) is structurally required. The singleNoun production itself will structurally match a list of nouns, but considers such matches semantically invalid. In contrast, singleNounOnly won't even structurally match a noun list.

    The firstCommandPhrase(withActor) rule now uses the new singleNounOnly production to match the actor phrase. This eliminates the structural ambiguity of certain types of invalid input that were able to cause unbounded memory consumption with the old rule.

    The menu system no longer shows a border in full-screen menu windows. (When a menu takes up the whole game window, a border is superfluous, since there's no other window requiring separation. Removing the border slightly but noticeably improves the appearance of a full-screen menu.)
    The Decoration class now gives Examine commands a reduced "logical rank" (of 70) for disambiguation purposes. This means that if the player enters an Examine command, and the vocabulary for the direct object matches a Decoration object and a non-Decoration object, the non-Decoration object will be chosen ahead of the Decoration. It's usually desirable to treat Decoration objects as second-class citizens for disambiguation purposes, because they're usually meant to stay in the background as much as possible.
    The standard Actor handlers for the Give and Show actions now produce more sensible messages for giving or showing something to oneself. (The old message was the generic "you do not respond"; the new message is "Giving [or showing] that to yourself won't accomplish anything.")
    The base definition of the method filterResolveList() has been moved from Thing to VocabObject. The parser calls this method on objects that match vocabulary in player input, so it more properly pertains to VocabObject than to Thing. (In practice, this is important in some cases when Topic objects match player input, because Topic objects descend from VocabObject but not from Thing.)
    The cached scope list in Resolver and its subclasses is now better encapsulated, to make it easier to subclass Resolver. In particular, the "scope_" member is referenced only in the method cacheScopeList(), objInScope(), and the new method getScopeList(). This means that a subclass can dispense entirely with the cached scope list (and the "scope_" member), as long as the subclass implements objInScope() and getScopeList() to return mutually consistent results. A subclass can alternatively override cacheScopeList() to use a different set of rules to obtain and cache the scope list.

    Along similar lines, TAction.initResolver() now calls cacheScopeList() to initialize its cached scope list, rather than doing so directly. This allows TAction subclasses to customize the scope rules in exactly the same way that subclasses of Resolver can customize scope. Likewise, TopicResolver.filterAmbiguousNounPhrase() now calls objInScope() rather than accessing the "scope_" list directly.

    The new Achievement method addToScoreOnce() makes it a little easier to do score book-keeping in the common case of achievements that are scorable only once. This method adds the achievement to the score, with a given number of points, but only if the achievement has never been scored before; if the achievement has been scored before, the method does nothing at all. This makes it unnecessary to keep track of any separate status to avoid scoring the same action more than once. (Some achievements are meant to be repeatable; this method wouldn't be useful in such cases, obviously.)
    The complexMultiTransform report transformer is now a little more liberal in determining when to add visual separation between reports when an action is iterated over multiple objects. In the past, visual separation (i.e., a paragraph break) was added only when implicit command announcements were present. Now, visual separation will be added any time the report group for an individual object in the iteration consists of more than one report, or a report has a single message with text over 60 characters in length. This new, more liberal policy is designed to add visual separation essentially any time an individual object's result message is non-trivial, since multi-object messages become hard to read when they're all jammed together, unless each individual message is very short. The new policy errs on the side of adding too much visual separation, which on balance seems to do considerably less harm to readability than too little separation.
    The handling of OOPS responses has been improved slightly. In particular, when the player uses an OOPS command to correct an error, the new text that results from applying the OOPS substitution is now run through the normal pre-parsing sequence.

    In the past, an OOPS command itself was run through the normal pre-parsing steps, but the new command resulting from applying the OOPS replacement text was not pre-parsed. This resulted in incorrect behavior in certain certain rare cases. One noticeable example was when a SpecialTopic was active. Because SpecialTopics rely on pre-parsing to match their special command templates, and because the corrected text after the OOPS wasn't pre-parsed, it was effectively impossible to use OOPS to correct a typo in a command intended to match a SpecialTopic. The new OOPS handling ensures that pre-parsing is run as normal on the new command resulting from applying an OOPS correction.

    When the player enters a command that matches an active SpecialTopic, the library now treats this as a conversational action directed to the interlocutor, as though the player had typed an ASK or TELL command. This is important in cases where the actor has background actions that it performs based on the absence of an explicit conversational interaction from the player on any given turn, such as showing ConvNode continuation messages.
    ASK FOR commands are now handled uniformly with other conversational commands (ASK ABOUT, TELL ABOUT, GIVE TO, etc). In particular, these commands are now routed to the ActorState for handling, and can be handled using the new AskForTopic class, which works along the same lines as GiveToTopic and ShowToTopic.
    The library now automatically sets the antecedent for the appropriate pronouns to refer to a responding actor, any time a TopicEntry is used to generate response text. This ensures that the player can use a pronoun on the next command to refer to the last actor who generated a conversational message.
    The new method Actor.setPronounObj(obj) makes it easier to set a simulation object as the pronoun antecedent for commands targeting the given actor. (In the past, only the setPronoun() method was available, which took a list of ResolveInfo objects. When the game or the library needs to set the pronoun directly, it's often easier to set it directly in terms of simulation objects, without creating a ResolveInfo list.)
    The new classes SimpleNoise and SimpleOdor make it easier to define Noise and Odor objects for the common case that a noise/odor is just a background part of the room description that (1) doesn't require ongoing "daemon" announcements, and (2) doesn't need any differentiation among the different types of descriptive messages. These classes simply use the "desc" property as the default for all of the descriptive messages, and are marked as "ambient," to avoid automatic inclusion in the room description.
    The Throw command now works properly when throwing something at oneself. (In the past, throwing something at oneself incorrectly tried to treat the actor as the drop location of the throw; it now correctly treats the actor's location as the drop location.)
    The new NOTE verb accepts an arbitrary literal as its object; the command doesn't do anything except acknowledge the input. The purpose of the command is to allow the player to enter arbitrary notes into the session transcript as she plays the game. Players might want to make such notes for their own later reference, but this command is especially useful for play-testing, because it gives play-testers a very easy way of pointing out bugs or making comments directly in the session transcript where they apply, and at the moment they occur to the player.
    A library bug caused a run-time error if an attempt was made to remap from a direct object handler (a dobjFor) to an intransitive action (an action with no objects). Remapping to intransitive actions will now work properly.
    The RealTimeDaemon class had a couple of problems that prevented it from working properly. For one thing, it wasn't based on RealTimeEvent as it should have been; for another, it didn't reset its next firing time when fired. These problems have been corrected.
    3.0.6i

    Released June 15, 2003

    The commandSequencer now starts in "before-command" mode, rather than in "no-command" mode. This allows any introductory text displayed before the first command to use the normal command sequencing tags.
    The "seen" property of Thing has been changed slightly in meaning. In the past, this property was set to true only when an object was specifically listed as a portable item in a room description. Now, the property is set to true whenever a room description is displayed from the player character's perspective, and the object is visible to the player character. The important difference is that unlisted items, such as 'Fixture' and 'Heavy' items, or items with special descriptions, will be marked as seen as soon as their containing rooms are described.
    Due to a bug in the previous version, the REPLAY command only worked when one of its qualifiers (REPLAY QUIET or REPLAY NONSTOP) was used; just plain REPLAY didn't work. The plain REPLAY command now works correctly.
    The gameinfo.t module has been removed from the base system library. In its place, the GameID class has been extended to write the GameInfo data file automatically during pre-initialization. If the game defines a GameID object, then the library automatically writes a gameinfo.txt file, using values from the GameID object. Refer to the comments in modid.t for the GameID class.
    Actor.setConvNode() now accepts a string giving the name of a ConvNode, as an alternative to a reference to a ConvNode object. In the past, only the object reference was accepted. Passing in a string naming a ConvNode now has the same effect as passing a reference to the ConvNode itself.
    The new Actor method initiateConversation() makes it easier to code an NPC initiating a conversation. This method takes an ActorState object to use as the NPC's new state, and a ConvNode (which can be specified by the string name of the ConvNode) to use as the initial conversation node.

    In addition, the new ConvNode method npcGreetingMsg is defined to display the initial conversational exchange in an NPC-initiated conversation. Any ConvNode used in an initiateConversation() call must either override npcGreetingMsg to define a greeting message, or define an npcGreetingList property as a TextList containing a list of greeting messages.

    The new ConvNode method npcContinueMsg is defined to display a conversational "continuation" message from the actor. This method lets the NPC continue a conversation of its own volition if the player character doesn't do so. This method is invoked on each turn when the ConvNode is active, and the player didn't enter a conversational command on that turn. The method is invoked during the actor's takeTurn() daemon processing. To define a continuation message, you can either define npcContinueMsg as a double-quoted string with the message, or you can define npcContinueList to a TextList subclass containing a list of continuation messages.

    If you don't override npcContinueMsg or npcContinueList, there will be no NPC-initiated continuation message. If you do provide a continuation message, then the ConvNode stays active by default. You can use the <.convnode> tag within the continuation message's text to switch to a new ConvNode, just as in a topic response message.

    The commandSequencer now starts in "before-command" mode, rather than in "no-command" mode. This allows any introductory text displayed before the first command to use the normal command sequencing tags.
    The Yes and No topic classes (YesTopic, NoTopic, YesNoTopic) had a number of bugs that prevented them from working properly. These classes should now work correctly.
    A new template for DefaultTopic makes it easier to define these objects. The DefaultTopic template accepts simply a response string (double-quoted), or a list of response strings (single-quoted).
    A bug in the AltTopic mechanism prevented intermediate AltTopics in nestings more than one level deep from being properly selected. This is now fixed.
    In the English-language module, the miscWord production now accepts '#'-prefaced numbers and quoted strings. This provides more flexibility for inputs in things like topic phrases.
    The AccompanyingState class didn't use the correct test for a travel action in its beforeAction() method, which prevented the class from carrying out its accompanying travel role at all. This has been corrected.
    ActorState.obeyCommand() generated the wrong default message. This is now fixed.
    ConvNode had a couple of problems that caused run-time errors or other strange behavior when activating a ConvNode that had no associated SpecialTopics. These have been fixed.
    Fuses and daemons (including real-time events and prompt daemons) now run automatically in a standard Action environment. This means that you can call essentially any library code without having to worry about whether or not it will need to access gAction or gTranscript. In the past, fuse and daemon code was called with no Action environment in effect, which made it necessary to either limit the library calls made within the fuse or daemon, or to explicitly set up an action environment (using withActionEnv(), for example) before invoking library code. This added a lot of unnecessary complexity to fuse/daemon code. Now that these routines are always invoked with a valid Action environment, these restrictions have been lifted.
    The withActionEnv() method now takes an additional argument, giving the specific Action class to use for the dummy action object in effect during the callback.
    The senseContext object's pushSenseContext() and popSenseContext() have been removed; they've been replaced with a new withSenseContext() method.

    In addition, the callWithSenseContext() function has been changed to eliminate the argument list parameter; instead, callers should use anonymous functions when the code to be invoked requires arguments. (This is the way the rest of the library already used the function in most cases anyway, so this change simply cleans up some old code that's no longer needed. Most games won't need to call this function directly, so this is unlikely to affect any existing game code.)

    The INSTRUCTIONS command now operates either in menu or in-line mode, not both. In the past, the menu format supplemented the in-line format, but this was redundant and complicated the user interface.
    3.0.6h

    Released June 7, 2003

    Several major enhancements to the Actor class have been added. These changes are described in more detail in the comments in the library sources files, and in a new series of technical articles on the tads.org web site, Creating Dynamic Characters in TADS 3. We'll summarize the changes here, but you should refer to the articles for full details.

    WARNING! Due to the extensive scope of these new features, there's a good chance that they'll undergo some changes as we gain experience with them. Be aware that game code that uses these features might have to be changed to accommodate library changes in future updates.

    A new class, ActorState, makes it easier to create characters that exhibit multiple behaviors. The Actor class has been changed to work with ActorState; most of the methods involved in describing an actor's physical state and in interacting with an actor are now delegated from the Actor class to the ActorState object. The idea is that most of the parts of an actor that need to vary according to the actor's behavior have been moved to the new ActorState object; this means that adding a new behavior to an actor is a matter of defining a new ActorState object for the actor.

    Several subclasses of ActorState have also been created. These include ConversationReadyState and InConversationState, which are used to make an actor explicitly enter, maintain, and exit a conversation; HermitActorState, for times when an actor is unresponsive; and AccompanyingState and AccompanyingInTravelState, which allow an NPC to travel with another character as part of the character's own turns.

    A new "topic database" system makes it easier to manage conversational interactions, by creating individual objects that represent responses to ASK, TELL, GIVE, SHOW, and other interactions. The TopicDatabase class is used to store an actor's responses; Actor and ActorState inherit from TopicDatabase, so you don't normally create instances of this class directly. The TopicEntry class represents a response; several subclasses, including AltTopic, AskTopic, TellTopic, AskTellTopic, GiveTopic, ShowTopic, GiveShowTopic, YesTopic, NoTopic, DefaultTopic, and SpecialTopic are used to create the individual responses.

    The conversationManager object and the ConvNode class are used to manage "threaded" conversations. A threaded conversation is one where an actor keeps track of what has been said before, and responds according to the context of the conversation.

    The SuggestedTopic class and its subclasses let you maintain a "topic inventory," which is a mechanism that automatically suggests topics to the player. This is an optional mechanism that you can use in various ways; you can use it as a sort of hint system, and you can use it to guide threaded conversations.

    Two library modules have been renamed: 'hints.t' is now 'hintsys.t', and 'menus.t' is now 'menusys.t'. Since these modules are included in most projects using the library file (adv3.tl), this shouldn't require any changes to any game code or project makefiles. The new names better reflect that these modules define the hint and menu systems, respectively, rather than actual hints or menus.
    A new function, withActionEnv(func), makes it possible for daemon code to create a simulated Action environment. Some code relies upon there being a current Action object and an active command transcript object; these objects are normally only available while executing a command, which made it difficult to call code that depends on this command environment from within a daemon. This new function makes it easy to call such code from within daemons and other non-command contexts. The function sets up a dummy Action object and a default command transcript, then invokes the given function; the function can do anything that can be done during normal Action processing.
    Openable now shows any special descriptions of the contents of an object that are revealed when the object is opened (via an OPEN command).
    The English module now includes some needed customizations to the TopicAction class. These were missing in the past, which caused incorrect messages to be generated in some cases involving this type of action.
    The new class RoomAutoConnector is a subclass of RoomConnector that can be mixed in to any BasicLocation subclass to make the room usable directly as a connector to itself. Room inherits from this class (it formerly inherited from RoomConnector and overrode part of the interface; the overrides are no longer necessary because they're contained in RoomAutoConnector itself now). In particular, RoomAutoConnector can be mixed in to any NestedRoom object's superclasses to make the nested room usable as a connector to itself.
    The new notification methods beforeTravel(traveler, connector) and afterTravel(traveler, connector) are invoked just before and after a traveler travels via travelerTravelTo(). These notifications are sent to each object connected by containment to the traveler; beforeTravel() is called on each object connected by containment to the traveler in its old location, and afterTravel() is called on each object connected by containment in the new location.

    These notifications are more precise than using beforeAction() and afterAction() with the TravelVia pseudo-action, because these actions are only called when travel is actually occurring. TravelVia will fire notifications even when travel isn't actually possible.

    A beforeTravel() method can veto the travel action using "exit". The notification is invoked before the travel is actually performed, and even before a description of the departure is produced.

    The method Actor.trackFollowInfo() is now invoked from Actor.beforeTravel() rather than directly from traveler.travelerTravelTo(), as it was in the past. This change makes the follow-tracking a lot less "special," in that it uses the general before/after travel notification system rather than a separate, purpose-built notification system. Note that this change means that any actor object that overrides beforeTravel() must inherit the base class implementation if the actor wants to be able to follow other actors.

    The default travel precondition calculations have been changed slightly to allow connections between top-level rooms to be placed inside nested rooms. This allows, for example, a window that can only be reached when standing on a desk, or a secret passage from inside a wardrobe. The main changes are that TravelVia doesn't automatically require the traveler to be in the outermost room, but in the room containing the outbound connector; and travel between top-level rooms that leaves the traveler inside a nested room (as would happen when climbing through the window from outside and ending up on the shelf) now provides a normal arrival message.

    First, BasicLocation.roomTravelPreCond() no longer does anything; the method is still present so that rooms can override it as desired, but it doesn't do anything by default. In the past, this method unconditionally added a precondition requiring that the traveler be in the outermost room; we don't want to apply that condition, since the required starting location of the traveler depends on the outbound connector, not the room.

    Second, TravelConnector.connectorTravelPreCond() now adds a requirement that the traveler be in the connector's location, if the connector has a location. This ends up having the same effect as BasicLocation.roomTravelPreCond() in cases where explicit connectors, such as doors and passages, are situated in the top-level room.

    Third, RoomAutoConnector.connectorTravelPreCond() overrides the inherited version, and instead requires that the traveler be in the "appropriate" starting location. This provides the same effect as the old BasicLocation.roomTravelPreCond(), but with an enhancement. The "appropriate" location is defined as the nearest enclosing location (enclosing the traveler) that has a directional connection ("north = xxx", etc) leading to the room. In most cases, rooms are connected directly to one another only at the top level; in such cases, the directional connection will be found in the traveler's outermost room, so the effect will be exactly as it was with the old system. But here's the enhancement: in cases where the directional connector is defined leading out of a nested room, this will correctly find the nested room as the starting location.

    Note that NestedRoom objects cannot by default be used as direct targets of directional connectors, because they're not based on RoomConnector. It seems highly unlikely that it would ever be useful to connect a nested room directly to a directional connector (i.e., "east = wardrobe") - some kind of explicit connector (a door, or a passage, or something) seems desirable in almost every case, just from the perspective of game geography. In the event that a direct directional connector is required, though, simply include RoomAutoConnector in the superclass list for your specific nested room.

    Fourth, the methods travelerArriving() and travelerLeaving() have been moved from Room to BasicLocation. This produces the proper travel messages for any travel between top-level locations, even when the travel starts or ends in a nested location.

    BasicLocation.travelerArriving() now enforces the defaultPosture setting for the location. The default posture is enforced via a nested command before the arrival message is displayed. This makes it easier to create locations with restrictions on posture, such as a low crawl where you can only sit or lie down.

    Note that if all goes well, there won't be any mention of the action for the player character (because the successful nested action will result in a default report, which will be suppressed due to the non-default message for the room description); so the new posture will simply be reflected in the description of the new location. Generally, all should always go well in these cases, since the actor will already be located within the location and merely has to change posture. If you want to explain why the posture change is necessary, you can override travelerArriving() for the location, display an explanation, then inherit the default. An appropriate message would be something like "The ceiling is so low you have to lie down."

    The semantics of TravelConnector.getDestination() have been changed slightly. First, the method now takes a traveler rather than an actor, since connectors are always traversed by travelers. (A traveler might in fact turn out to be an actor, of course, but it could also be something else, such as a vehicle.) Second, the method is now required to return the destination, and the destination must be stable for the duration of the turn up to the point where the travel actually occurs.

    To allow for connectors that are affected by traversal (for example, a connector that randomizes its destination each time it's used), the new method noteTraversal() must be called whenever the connector is actually traversed. This method can change the connector state as needed for the traversal. Note that game code should generally never need to call noteTraversal(), as the library dobjFor(TravelVia) action implementations do this automatically as needed. This method is provided for games to override as needed.

    The base TAction and TIAction classes no longer require that any of the target objects of the command define an action() method for the verb. Instead, the classes now merely require that at least one of the target objects defines any of action(), check(), or verify(). If none of the target objects defines any of these methods for the verb, then the verb will automatically add an "illogical" status during the verify phase.

    The purpose of this check is to ensure that the command has some sort of handling. If the game adds a new verb, but never defines any handling at all for the new verb for some object, then without this check, there would be no handling at all if the player attempted to apply the verb to that object. The old check tested for the existence of an action() handler, but this was overly conservative, since an object could have fully handled the verb in its verify() or check() handler. The new test allows for this by allowing the action to proceed if any sort of handler (check, verify, or action) is defined for an object involved in the command.

    A new property, globalParamName, has been added to Thing. This lets you define a substitution parameter string (for "{xxx}" sequences) that can be used at any time to refer to the object in message text. Once you define an object as having a global parameter name, you can use that name to refer to the object in {xxx} sequences, even when no command is active, and even when the object isn't involved in any command. This can be especially useful for writing messages that refer to objects whose names change in the course of the game, such as actors who are known by one name until introduced, then are known by another name ("the white-haired man" might become "Bob" after he tells us his name, for example).
    A new Script subclass, EventList, provides a convenient way of defining scripts using procedural code in steps without writing a big 'switch' statement in the doScript() method. The eventList property contains a list of script step elements. Each element gives one step of the script. An element can be a single-quoted string, in which case the string is simply displayed; a function pointer, in which case the function is invoked with no arguments; a ScriptEvent object, in which case the object's doEvent() method is invoked; or nil, in which case nothing happens on the step.
    The Actor method getDefaultInterlocutor() has been renamed to getCurrentInterlocutor(), to better reflect its purpose. The conversation model keeps track of the actor we're currently talking to; conversational commands that aren't directed to a specific actor (such as ASK ABOUT BOOK) are assumed to be directed to the current conversational partner.
    A couple of the abstract base classes in the area of Container have been renamed to better reflect their purposes. The class formerly known as BasicContainer is now called BulkLimiter, and the class formerly known as Encloser is now called BasicContainer. So, Container is a BasicContainer, which is a BulkLimiter; Surface is also a BulkLimiter. The new name BulkLimiter is more consistent with its main purpose, which is to constrain the aggregate bulk of its contents. The old name "Encloser" was confusingly similar to the real word "Enclosure," and was vague in conveying how the class differs from Container; the new name BasicContainer makes it clearer that this class contains some of the basic abstract functionality of Container but
    The actor inventory listing mechanism has been reworked to make it a smarter about the listing format. When the inventory list is short, the listing will appear in a single sentence showing both the items being carried and the items being worn. When the listing is long, it's broken up into two sentences, as in the past. The threshhold for "long" is set with a property of the new DividedInventoryLister class, singleSentenceMaxNouns; the default is 7, which means that the listing will be shown in one sentence if it involves seven items of fewer, two sentences otherwise. For example:

      >inventory
      You are carrying a music box, a gold coin, and thirty silver coins,
      and you're wearing a watch and a helmet.
    
      >inventory
      You are carrying a cardboard box (which contains several LP's, a
      bunch of photographs, and some letters), a rubber chicken, two
      pieces of the Magic Goblet of Forblotsk, and a bowl of chowder.
      You're wearing a pair of flippers, a wool jacket, and a red wig.
    

    The list length is determined by capturing the output and counting the phrase separators. The English library counts commas, semicolons, the word "and", and right parentheses to determine the phrase count.

    This change slightly affects the Actor class (showInventoryWith now takes only one lister object), and extensively affects the InventoryLister class and the WearingLister class and their subclasses (the English-specific implementations in msg_neu.t). Games shouldn't be affected unless they modified these classes.

    If you want the old behavior, where the items being worn and the items being carried were always shown as separate sentences, simply set DividedInventoryLister.singleSentenceMaxNouns to zero. If you want to always use a single sentence no matter what, set the property to some absurdly high number - 30000, say.

    If you'd prefer the more traditional behavior that shows the items being worn marked "(being worn)" and mixed in with the rest of the inventory in a single listing, simply change Actor.inventoryLister to refer to actorSingleInventoryLister.

    The match list for an ALL phrase in player input is now filtered through the filterResolveList() method of each object in the list, just as ambiguous noun phrases are. This allows objects that substitute for others in resolution lists (such as Collective and CollectiveGroup objects) to perform the same substitutions in ALL lists that they would in normal matches.
    The miscWord grammar rules are now defined in en_us.t, rather than in the language-independent parser.t as they were previously. This change is necessary because different languages might have different token types that are valid in miscellaneous word lists. (In point of fact, the English parser defines a special language-specific token, tokApostropheS, that is now allowed in miscellaenous word lists.)
    The menu system now uses the regular game color scheme as the defaults; the top "instructions" bar is shown using the status line color scheme, and the main menu area uses the normal game window color scheme. This scheme is the safest default, since it uses only colors the user has selected (on interpreters that allow the user to select the color scheme); this ensures in particular that things like hyperlink colors work well with the menu colors, since presumably the user will have chosen settings that work well together. (If not, at least it's not the game's fault.) This change means that menus won't stand out as well from the main game window, since they use the same color scheme, so the default mode for menus has been changed to the "full screen" mode. This new default appearance - full screen, using normal game and status-line colors - makes menus very unobtrusive, since they just look like an ordinary game screen.
    The menu system takes advantage of the new MORE-mode banner style option, by using a separate banner to show "long topic" items. Since long text can now safely be displayed in a banner, with the interpreter providing pagination via MORE prompts as needed, long topics can avoid taking over the main game window. This is nice because it leaves the original game window intact (without any need to clear the screen) after the user exits from the menu system.
    The MenuItem class has a new property, heading, that specifies the text to display as the heading of the menu while the menu is active. By default, the heading is the same as the title, which is the string displayed in the parent menu's list of items. The separate property allows the caption shown while the menu is active to differ from its title, if this is desired.
    A couple of the English messages for the npcMessagesDirect group have been recast as quoted statements from the NPC, for consistency with other messages in the group. The affected messages are noMatchDisambig and disambigOrdinalOutOfRange. In addition, askDisambig (in the same group) has been tweaked slightly to accommodate this change.
    The CollectiveGroup class has been split into two classes. The full behavior of the old class is now contained in the class ItemizingCollectiveGroup, which is a subclass of CollectiveGroup. On Examine, the CollectiveGroup class no longer shows the itemized list of collected items, but instead simply shows its own description using the normal Examine handling. The itemizing behavior is desirable in some cases of collective groups, but certainly not all; this change makes it easy for the game to select whether or not the itemizing behavior is used.
    If a check() routine (in a dobjFor() or iobjFor() group) terminates the command with 'exit', the parser now automatically marks the action in the transcript as having failed. This is the same thing that the reportFailure() macro does, so this means that it's never necessary to use reportFailure() from within a check() handler. Since the purpose of check() is to enforce conditions on the action, exiting from the check() routine necessarily means that the command has failed; this change takes advantage of that to save games a bit of work.
    A new set of grammar productions provide for third-person reflexive pronouns in verbs with two noun-phrase slots: "put klein bottle in itself," for example. These are mostly intended for conversation, specifically things like "ask bob about himself," but will work in general for the rare cases where they might be useful.

    The parser resolves a third-person reflexive pronoun by referring back to the resolved object list for the other noun phrase in the action. Verbs taking only one noun phrase don't accept these, as they make no sense: "open itself" isn't meaningful. In the basic TIAction, the order of noun phrase resolution can vary by verb, and the order of resolution isn't required to match the order of the nouns in the phrase; in English, at least, a reflexive pronoun in this type of construction is always anaphoric (i.e., it always refers to a phrase earlier in the sentence). This means that the TIAction resolver could find itself trying to resolve the reflexive phrase first, before it knows the resolution of the phrase to which the reflexive refers. To cope with this situation, the resolver notes when this occurs, provides an empty list for the initial resolution of the reflexive, and then goes back and re-resolves the reflexive after resolving the other noun phrase.

    The new Thing property isKnown lets you specify that an object is known in advance to the actors in the game. The method isKnownBy(actor) can be used to test actor knowledge of a Thing: isKnownBy(actor) returns true if seenBy(actor) returns true, or the isKnown property is set to true for the object. The Actor methods knowsTopic() and isLikelyTopic() now use isKnownBy() rather than seenBy() to determine actor knowledge.
    A few new conversation-related verbs have been added: Goodbye, Yes, and No. These are handled similarly to Hello.
    A new Action method, isConversational(issuingActor), determines if an action is "conversational." A conversational action is one that involves the issuing actor saying something, within the game context, to the target actor, as opposed to an order to the target actor to do something. Of the system-defined verbs, Hello, Goodbye, Yes, No, and TellAbout (with the issuing actor as the direct object) are defined as conversational.
    The interface of the Actor.obeyCommand() has changed, as has its meaning. The method now takes two parameters: the actor who issued the command, and the Action object. In the past, this method was called with only the issuing actor as a parameter, because the action was unresolved when the method was called. Now, the parser calls this method after resolving the action and its objects. This change gives the method full access to the details of the action, so it can decide to accept or reject commands based on the actual action being performed.

    The parser does not call this method when the action is "conversational," as indicated by the isConversational() method no the action. Conversational methods are not considered to involve an order to the target actor. The actual physical action of a conversational action simply consists of the issuing actor saying something to the target actor, so even though these actions are phrased as though they're orders to the target actor, they're really carried out by the issuing actor, and thus don't require acceptance by the target actor.

    The default implementation of this method on Actor calls the corresponding method on the atcor's current state object. The basic state object implementation simply refuses the command, so the default behavior is the same as it was in the past.

    The TextList subclass formerly known as SlaveTextList has been renamed to SyncTextList, to better reflect that this list is always kept synchronized with its associated master list. The old name suggested that the connection was one-way, that state changes flowed only from the master to the slave, when in fact the master and slave are fully synchronized. The new name is more suggestive of this two-way connection.
    A new TextList subclass, StopTextList, provides a minor variation on the standard text list script. Once StopTextList reaches its last message, it will simply stay at the last message forever, repeating the last message each time the script is invoked. This is useful for cases such as a conversation topic where an actor has several things to say, but once the actor has said each bit, the actor will from that point on just repeat the last message; the last message would usually be something like "I've already told you all I know about it," or could be a summary of what the actor revealed.
    ShuffledTextList features a few enhancements.

    First, a new property, firstStrings, can be set to a list of strings to show sequentially before starting the shuffled strings. This can be useful in cases where you have some meaningful information to convey initially, but once those messages have been displayed, you want to fall back on randomized messages for atmosphere and variety. The firstStrings list is shown only once, in sequential order. Once the firstStrings list is exhausted, these strings are never shown again.

    Second, the main shuffled list in textStrings can now be shown sequentially the first time through, if desired. Set the property shuffleFirst to nil (it's true by default) if you don't want the list shuffled the first time through. Since the strings in the textStrings list are intended to be shown in random order, in most cases it won't matter to the author what order is used, and in this sense the order in which the strings are actually defined is as random as any other order. In some cases, it might actually be desirable to have the strings come out in a certain order the first time through; this lets you refer back to an earlier message in a later message, for example, with assurance that the player will have always seen the earlier message first. After the first time through the list, the list is always shuffled and shown again in random order.

    Third, the class now takes care to ensure that a message is never repeated consecutively. Repeats are normally avoided naturally by the shuffling: every item is shown once before anything is repeated. But a consecutive repeat was still possible in one special case, which is immediately after a shuffle. Because the order of one shuffle is independent of the order of the next shuffle, it was possible for the last element of the previous shuffle to be the same as the first element of the next shuffle. The class now suppresses this case by checking each new shuffle to make sure its first element doesn't match the last element of the previous shuffle, and choosing again when necessary. This change is intended to increase the apparent randomness by ensuring that the same string is never shown twice in a row.

    The special description list order has been modified slightly. If two objects have the same specialDescOrder value, but one of the two objects is inside the other, we'll list the outer object first. So, specialDescOrder still dominates, but when there's no specialDescOrder preference, we'll list containers before their children. In almost all cases, this produces a more pleasing list order; objects within other objects will frequently mention their placement in their special description text, so it's usually better to have seen the containing item's own special description before we see the containing item mentioned in a child item's special description.
    A set of new commands makes it easier to record and play back command scripts, which can be especially useful while writing and testing a game. The command RECORD (RecordAction) starts recording a command script, which saves command input text to a file. RECORD OFF (RecordOffAction) turns off the recording starting with RECORD (it has no effect if no recording is in progress). REPLAY (ReplayAction) plays back a command script previously recorded. RECORD and REPLAY both accept a filename in quotes on the command line, but this is optional; if you just type RECORD or REPLAY, the commands will ask you to select a file using a standard file dialog.

    REPLAY has two mutually exclusive options. REPLAY QUIET plays back the script without showing any output while the script is running. REPLAY NONSTOP plays back the script without pausing for MORE prompts.

    Because REPLAY is fully redundant with the old "@" syntax, but much friendlier, the "@" syntax has been removed.

    The new class LocateInParent makes it easy to define a nested object that's to be located within the enclosing object. This is a mix-in superclass, so simply add it to the object's superclass list; LocateInParent should go ahead of Thing or any Thing subclass in the superclass list.
    3.0.6g

    Released April 12, 2003

    The default object picker has been improved to better handle cases where commands are remapped. Remapping from one object to another is often used as a convenience to the player, so that the same command can be applied to any of several related objects with the same effect. For example, you might want to set up a jar with a lid so that OPEN JAR and OPEN LID have the same effect; this could be done by remapping OPEN LID to OPEN JAR. Similarly, a house with a door might remap ENTER HOUSE so that it's handled as ENTER DOOR. In the past, cases like these prevented the parser from choosing a default object, because the parser can only apply a default when there's only a single object that could make sense - the remappings made several different objects look equally good superficially, even though the apparent different possibilities were all going to turn into the same thing in the end thanks to the remapping.

    The parser now keeps track, during the verification process, of any remapped objects. The default picker looks at this remapping information before deciding on a default. If there are any objects among the possible defaults that are to be remapped, the default picker will discard any that are redundant due to the remappings. For the OPEN JAR/OPEN LID example, the parser would see that OPEN LID turns into OPEN JAR, and it would thus discard the lid from the list of possible defaults, since the jar is already in the list. This would leave us with just one possibility, so the parser would be able to apply use it as the default.

    The parser will only eliminate a remapped object as redundant when the remapping matches the object, action, and role of another object in the list of possible defaults. If a remapping changes the verb, the remapped object will only match another object if it's also remapped to that same new verb. The verb has to match because the command could otherwise have a different effect, and thus the two actions on the same object wouldn't be redundant with one another.

    The asExit() macro has been changed slightly. You should no longer uses the ":" syntax to define the direction; instead, simply put the asExit macro directly after the direction name:

      north asExit(down)
    

    This change is intended to make asExit more consistent with the similarly-named asDobjFor() and related macros.

    The menu system has a few minor changes to make the user interface more easily customizable.
    • In HTML interpreters, the top title/instructions bar now shows a hyperlink that returns to the parent menu. The text in this hyperlink is controlled by the 'prevMenuLink' property, which by default uses the text 'Previous'.
    • In HTML interpreters, a hyperlink is shown in topic lists to advance to the next topic item. The text of the hyperlink is given by the 'nextMenuTopicLink' property.
    • The 'fgcolor' and 'bgcolor' properties now look to the parent menu, if there is a parent menu, for their default values. For the top-level menu, the defaults are still the status line colors. In most cases, you'll want all of the menus in an entire menu tree to have the same appearance, and this change makes it easy to accomplish this: simply specify the appearance in the top-level menu, and all of the child menus will use the parent menu settings. Of course, individual menus can sever this parental dependence simply by overriding these properties.
    • Two new properties, 'topbarfg' and 'topbarbg', allow each menu to specify the foreground (text) and background colors of the top title and instructions bar. By default, these look to the parent menu, if there is one; the top-level menu defaults to using the inverse of the color scheme of the menu itself.
    • The 'indent' property uses the parent's value by default.
    • A new property, 'fullScreenMode', lets you indicate that you want the menu to take over the entire interpreter window. By default, menus are given just enough space at the top of the interpreter window to display their contents. If 'fullScreenMode' is true, though, menus will cover the entire interpreter window. Full-screen mode has the advantage that it's less jumpy than the default partial-screen mode, since the partial mode resizes the menu windows on each navigation operation to accommodate the new contents.
    A new hint menu type has been added: HintLongTopicItem. This is simply a MenuLongTopicItem subclass designed for use in hint menus. (Regular HintLongTopicItem objects don't have the necessary logic for calculating visibility in a hint menu, so the specialized subclass should be used for long-topic menus within hint menu trees rather than the base MenuLongTopicItem type.)
    The library's turn-counter incorrectly counted a command as two turns if the command's action() handler invoked a nested action which was then remapped (via remapTo). Nested actions aren't supposed to count as separate turns; it was erroneous for the remapping to affect this one way or the other. This has been corrected.
    moveInto(nil) now works properly for a MultiLoc object. (In the past, this incorrectly caused a run-time error.)
    SensoryEmanation has a new property, isAmbient. This is nil by default; if set to true, it indicates that the noise/odor/etc is purely in the background, so it's not especially noticeable. Ambient emanations won't be mentioned when they first become sensed; normally, an emanation is automatically mentioned whenever conditions change so that it was not sensed previously but is now. Ambient emanations will still be mentioned in explicit intransitive LISTEN/SMELL commands; they simply won't be mentioned on their own.
    Part of the Container class has been separated into a new lower-level base class, Encloser. An Encloser is an object that can enclose its contents, so that all senses must pass through the encloser's material when passing in and out of the object. Encloser has all of the basic handling for enclosing contents, but not the action handling. Encloser is meant for cases where the object's contents are not to be directly manipulable by the player, via "put in" commands and the like. Container is now a subclass of Encloser; Container defines the suitable action handlers to allow a player to manipulate the container's contents.

    A new mechanism in the Actor class makes it relatively easy to set it up so one or more non-player characters accompany the player character on travel. This kind of group travel is especially good for things like a sidekick character who goes everywhere the player does, and for tour guides or other escorts who are showing the player character where to go.

    This mechanism is similar to the "follow" mechanism, which makes one actor attempt to follow another on each turn, but it improves considerably on regular following by customizing the messages. Normal following is a little awkward for explicit group travel of the sort that one wants with sidekicks and escorts, because the messages are so generic; the NPC almost seems to be wandering around on its own and just coincidentally showing up where the PC is. This new "accompanying" mechanism accomplishes much the same thing, but smooths out the messages a bit. First, rather than having the NPC trailing along after the fact, the new system sends accompanying NPC's on ahead; this means that the NPC's don't just wander in later as they do with normal following. Second, the message for the NPC's pre-departure is customized to make it clear that the NPC isn't departing, but is coming along with you. Third, on arriving in the new location, there's a customization hook for describing the NPC's presence in the new location specially for the group travel; this is important because it gives us a place to mention that the actor starts doing whatever it is the actor normally does in the new location. The overall effect is that each group travel action is made to look like a single, integrated action, rather than two generic and unrelated actor travel actions.

    NPC's can also be set to accompany NPC's with the same mechanism, but it's much less interesting for that, since the main value of the new mechanism is that it improves the messages for PC-plus-NPC group travel. For NPC-plus-NPC group travel, this new mechanism has no particular advantage over the the simpler "follow" mechanism.

    Setting up accompanying travel is relatively straightforward. You have to specify the conditions under which the group travel occurs, and you should customize two messages related to the travel. You set this all up by overriding methods on the NPC who's going to follow the lead actor (usually the PC). The methods to override are as follows.

    First, override the accompanyTravel() method so that it returns true under the conditions where you want the accompanying travel to occur. This method is called each time your NPC is present and sees another actor attempt a travel action (it's called during your NPC's beforeAction when the action is a TravelVia). If you want your actor to accompany the PC everywhere, simply return true when the action actor is the PC. The method also has access to the TravelConnector involved in the travel, so you selectively accompany an actor for some destinations but not others, if you wish.

    Warning: the next two parts are likely to be modified soon. I'd recommend against writing any code that makes customizations using these features right now.

    Second, define an accompanyDesc method for your actor. This method is used instead of the usual actorHereDesc method to describe your actor in the new location immediately after the group travel is finished. It's usually desirable to use a special message to describe the actor right after the accompanying travel, because you usually want to convey that the actor is just arriving - not that the actor is walking in separately from the lead actor, but that the actor is arriving at the same time as the lead actor. If you do override accompanyingDesc, you should override accompanyingListWith to return an empty list ([]).

    Third, you can optionally provide a special TravelMessageHandler for your actor by overriding getAccompanyingTravelMessageHandler. By default, this routine provides a message handler that uses messages like "Bob goes with you" instead of the usual "Bob leaves to the east" to describe the departure of your actor. The normal departure message isn't appropriate, because the actor isn't just leaving, it's leaving *with* the lead actor. The default "Bob goes with you" is better, but you might still want to override this handler to provide even more customized messages: "Bob escorts you to the east," or "You drag Bob with you," or whatever makes sense for the specific situation.

    Finally, note that you can use accompanying travel and the regular following mechanism together. Regular following can be a useful fallback for cases you don't want to customize. Regular following occurs after the fact, because it occurs on an NPC's turn when the NPC sees that the actor it's tasked to follow is no longer present. Accompanying travel, in contrast, happens on the same turn as the lead actor's travel. This means that regular following is essentially overridden by accompanying travel, since it happens first.

    The English parser now accepts quoted strings as adjectives. Quoted strings can be used as adjectives in exactly the same way that numbers can be used, so phrases such as "QU" TILE and BUTTON "G" are accepted.

    For the purposes of matching vocabulary in the dictionary, quoted adjectives are simply treated as though the quotes weren't present. So, "QU" TILE is treated exactly the same as QU TILE. This means you don't have to worry about the quotes when defining your vocabulary words.

    There is an additional bit of special treatment for quoted strings. The special vocabulary word '"*"' (that is, an asterisk within double-quote characters) serves as a wildcard for any quoted string. If an object defines this special wildcard as an adjective among its vocabulary words, then that object will match any quoted string as an adjective. This is the equivalent of the '#' wildcard string for numeric adjectives.

    String-as-adjective phrasing is implemented using the new production literalAdjPhrase. This new production is now used wherever numberPhrase was formerly used in the role of an adjective in noun phrase rules. literalAdjPhrase matches numbers, '#' number phrases, and quoted strings.

    In travel.t, there were formerly a couple of TravelConnector subclasses that handled action synonyms for TravelVia using asDobjFor(TravelVia). These have been changed to use remapTo(TravelVia) instead.

    The asDobjFor() approach had the drawback that the action synonyms didn't ever generate TravelVia actions proper, but simply called the TravelVia handlers internally; so, for example, beforeAction() routines wouldn't ever see a TravelVia action being performed. The new remapping approach is better because it means that every possible action on these objects that involves travel will go through an actual TravelVia action. This allows beforeAction() and similar routines to be assured that they can catch all travel actions by looking for TravelVia alone, eliminating the need to worry about synonym actions that could cause travel.

    A couple of new classes simplify a few tasks involving entry and exit portals. Exitable is a new class that's similar to Enterable, but provides Exit instead of Enter as its main action. EntryPortal and ExitPortal are just like Enterable and Exitable, respectively, but add support for GoThrough actions.
    Added finishOptionFullScore, to make it easy for the game to offer the player the option of seeing the full score at completion.
    3.0.6f

    Released March 23, 2003

    It's now less dire for gPlayer.location to be set to nil. In the past, setting gPlayer.location to nil caused an infinite error loop, because the status line code dereferenced gPlayer.location without checking for nil, which caused an error, which aborted the current turn, which started a new turn, which tried to update the status line, and around we went. It's still not completely valid for gPlayer.location to be nil; this change just removes the infinite error loop, which makes it a little easier to deal with this problem arising during programming and testing.
    Changed Thing.dobjFor(Remove) to remap to RemoveFrom (asking for an iobj). Wearable now overrides Remove to map it to Doff. It makes more sense in general for "remove foo" to mean "remove foo (from something)" rather than "doff foo"; only when "foo" is an article of clothing does the "doff" interpretation usually apply.
    Added the new TravelConnector property isConnectorListed. If this property is nil, the connector is suppressed from generated exit lists (such as the status line exit list, and "you can't go that way" messages).
    Added a new class, UnlistedProxyConnector, that acts as a proxy for an underlying connector, but is unlisted. The idea is to make it easy to add an exit that acts exactly like another connector, but is suppressed from exit lists.
    Added a macro, asExit(), that can be used to define an UnlistedProxyConnector that links to another direction exit.
    Integrated Stephen Granade's menu system (menus.t) with the main library.
    Added an on-line adaptive hint system (hints.t).
    Added the new class RestrictedContainer. This is a Container specialization that makes it easy to define containers that only accept certain objects as contents. In the basic implementation, the allowable objects are simply enumerated as a list of objects; but this can be easily overridden for other methods of determining which objects are allowed.
    Added the new class SingleContainer, and the associated new precondition objEmpty. SingleContainer is a specialization of Container that only allows a single object to be in the container at a time; this is suitable for things like sockets and receptacles. SingleContainer places the objEmpty precondition on the PutIn action; the objEmpty precondition simply requires that the subject object is empty, and tries implicit TakeFrom actions on the contents to accomplish this.
    In en_us.t, changed typographicalOutputFilter.eosPattern to ignore any run of HTML tags between sentence-ending punctuation and a following space.
    The main implicit action processor now uses the more abstract "allowed in implicit" test on the verify results, rather than merely checking for "dangerous" results. This will allow results flagged as "non-obvious" to cancel implied actions, as they should. (A non-obvious command should never be done implicitly, because implicit commands are specifically for actions that are obvious intermediate steps toward the explicitly stated command.)
    Openable and Lockable have been cleaned up slightly. A new class, Linkable, has been added for objects that can be paired in master/slave relationships; Openable and Lockable are now based on Linkable. This removes some duplicated code for managing the masterObject relationship. In initialization, Linkable checks for a master object loop (where each object points to the other as the master) and break such loops by arbitrarily selecting one as the master. Also, we have reduced the number of times we have to refer to masterObject by relying on isOpen/isLocked/makeOpen/makeLocked to defer to the master. Also, the initiallyOpen/initiallyLocked initializations are now handled in initializeThing() rather than in isOpen/isLocked.

    Another new class, BasicOpenable, provides the basic state management for openable objects (which can optionally be linked in pairs to maintain the same status across the pair) but doesn't provide any of the verb handling of Openable. This can be used for objects that want to maintain open/closed state using the usual method names, but which don't respond to direct player open/close commands.

    IMPORTANT: Objects based on Openable and Lockable must not initialize their open status with isOpen or their locked status with isLocked. Instead, initialize the status with initiallyOpen and initiallyLocked, respectively. Also, be sure you never set isOpen or isLocked directly; instead, call makeOpen() and makeLocked() to effect these status changes.

    Changed Passage slightly to rely on isOpen/etc more to manage the masterObject relationship.
    Added a parameter to Direction.defaultConnector() giving the location from which travel is being attempted. This allows the direction to customize the default connector according to the type of location.
    Added a new property, isShipboard, to Thing. By default, if we have a location, we use the location's isShipboard setting, otherwise we return nil. Rooms aboard ships can set this to true to indicate that shipboard directions make sense here.

    In ShipboardDirection.defaultConnector, if the source location or any container has 'isShipboard' set to true, then we now use noTravel as the default connector rather than noShipTravel, as the latter is meant to convey that shipboard directions make no sense in non-shipboard locations.

    Added a ShipboardRoom mix-in class that defines isShipboard to true.

    Changed the way that VerifyResultList determines if an action is allowed at all or allowed as an implicit command. In the past, this calculation did what the "most disapproving" result in the list said to do; this wasn't quite right, because approval is essentially a separate axis from the "disapproval" order, despite the name. In reality, the "disapproval" order is the message priority order, and the actual determination of approval depends on there being no disapprovers. So, to determine approval, we now scan the full result list and require that every result approves; a single disapproval, no matter where it appears in message priority order, constitutes disapproval.
    In the English module, added a new token type for numbers specified with a leading pound sign (as in "#2 pencil" or "room #101"). These are treated essentially the same as numeric adjectives. These match vocabulary for just the underlying numbers, not including the pound signs, so vocabulary should be specified simply as '2 pencil' and '101 room'. (In other words, do not include a pound sign in vocabulary words; just use the number as with ordinary numeric adjectives, and the parser will match the number with or without the pound sign).
    The TravelVia check() condition in Passage no longer uses isOpen as its test. Instead, it calls the new abstract condition method, canActorTravel(), passing the current actor as a parameter. This makes it easy to allow some actors to pass and not others, and to test for conditions other than isOpen. By default, canActorTravel() simply returns isOpen, so this doesn't change the default behavior.

    Along the same lines, Door.connectorTravelPreCond() now calls a separate method, getDoorOpenPreCond(), to obtain the door-is-open precondition object. By default, this returns a doorOpen precondition (wrapped with an ObjectPreCondition for 'self'), so the default behavior hasn't changed.

    In the past, when an NPC was following another actor, and the actor being followed moved into a nested room within the current room, we generated a redundant message:

    >stand on platform
    Okay, you are now standing on the platform.
    Bill stands on the platform.
    Bill follows you onto the platform.
    

    The redundant "Bill follows you" has been eliminated.

    The reporting mechanism now processes sets of implicit action announcements to make them more readable and more easily understood. In the past, each implicit action was shown separately; when one implicit action triggered another, this could make for somewhat confusing displays. For example, in the Platform Room in sample.t, if the PC is sitting on the blue chair and wants to get up and sit on the red chair, we formerly generated a transcript like this:

    >sit on red chair
    (first standing on the red platform)
    (first getting off of the blue platform)
    (first standing)
    Okay, you're now sitting on the red chair.
    

    To the uninitiated, this looks backwards: shouldn't we stand up, then get off the blue platform, then get on the red platform, and then sit on the red chair? Of course, that's what's actually happening, and in its own way the transcript above reflects this: the second "first" applies to the first "first": it's saying "before you can stand on the red platform, you first have to get off the blue platform." Likewise, the third "first" applies to the second "first," to say "before you can stand on the red platform, you have to stand up first." In other words, the implied reports are nested recursively. The recursive structure isn't represented visually, though, so it's not evident whether one of the later "firsts" refers to the preceding "first" or back to the original command line. It would have been possible to represent the recursive structure visually, using indentation or something like that, but this probably wouldn't have made most people very happy; most non-programmers aren't accustomed to thinking in terms of recursion and stacks and tree views, and even programmers might well have found this kind of presentation to be too obviously mechanistic.

    To improve the situation, the transcript processor now features a new transform, called implicitGroupTransform, that rearranges these recursive listings into an order that should be entirely straightforward to anyone, programmer or not. The new transform also consolidates these lists into a much more concise and readable format. The transformer does two things. First, it "unstacks" recursive lists of implied action announcements to put them into the chronological order in which the commands were actually performed; the transcript has always kept track internally of which announcement is tied to which action, so the new transformer can merely inspect these records to determine the action relationships and use this information to unwind the stack. Second, the transformer combines each run of adjacent implied action announcements into a single announcement, showing a list of the actions performed. The result is that the example above now looks like this:

    >sit on red chair
    (first standing, getting off of the blue platform, then standing on
    the red platform)
    Okay, you're now sitting on the red chair.
    

    This new format should be easier for players to understand, since it shows implied actions in the order in which they're actually carried out, eliminating any need to be aware of the recursive goal-seeking work that the system is doing internally. Hopefully, players will also find it more readable and less obviously mechanistic than the old format.

    3.0.6a-e

    3.0.6e was released March 16, 2003

    For a restore-on-startup operation, if the restore fails, don't simply start the game from the beginning. Instead, offer options to START from the beginning, RESTORE another game, or QUIT, using the same type of prompt that we use for presenting "finishing" options.
    Add a verb for OOPS typed at arbitrary times, which simply explains that OOPS can only be used after the parser has pointed out an unknown word.
    Fix problem using possessive qualifiers in topic phrases. (The problem is that we're using the topic resolver to resolve qualifiers in the topic phrase; we should be using an ordinary object resolver. Change the topic resolver's qualifier resolver to use the topic action's direct object resolver by default.)
    Move the pronoun-setting routines (setPronoun, setIt, setHim, setHer, setThem) into Actor - these shouldn't be global functions since they need to operate on the current actor.
    In TopicAction, set the pronoun antecedent based on the direct object phrase immediately during resolution, to allow things like "ask bob about his book."
    Add a DefineIAction macro, for defining IAction classes.
    Base LiteralAction on IAction, not directly on Action. Because of this change, rewrite OopsAction to implement its processing in execAction() rather than doActionMain().
    Refactor the Instructions action into an InstructionsAction base class and a separate grammar rule, to allow the base class to be referenced by name from other code.
    Add a banner manager, along the lines of the input and output managers. A new BannerWindow class would represent a banner; this would encapsulate the system banner handle and provide methods that operate on the banner. The game should use the BannerWindow methods to perform all operations on banners, rather than calling the system-level banner API directly. The banner manager should provide persistence support, so that the on-screen banner layout is restored on RESTORE, UNDO, or RESTART. To this end, BannerWindow should be an ordinary persistent object, and a separate set of transient objects should track the current UI state. On RESTORE, UNDO, or RESTART, the transient tracking list and the persistent BannerWindow states should be compared, and the actual on-screen UI state, as represented in the transient objects, should be brought into line with the saved state.
    Wait to mark an object as 'seen' in Thing.lookAroundWithin() until just before the method returns, so that any routines called from the method can check the old 'seen' status.
    Fix gAction reference in Actor.travelWithin() so that it's conditional on gAction being non-nil.
    Fix takeFromNotInActor message (iobj/dobj are reversed).
    Fix TAction.retryWithMissingDobj() and TIAction.retryWithMissingIobj() so that they cancel the game time contribution of their enclosing (replaced) actions.
    Fix touchObj precondition logic so that it realizes when an attempt to remove an obstruction with a precondition fails. To do this, keep track of which obstructions we've tried to remove with implicit commands, and give up if we encounter the same obstruction more than once.
    Add <.parser> to msg_neu.t responses for some system verbs (NOTIFY ON/OFF, EXITS ON/OFF, a few others).
    Assign the IndirectObject role to the literal in a LiteralTAction by overriding getRoleFromIndex, getObjectForRole, and getMatchForRole. These are needed to allow remapTo to be used with a LiteralTAction.

    Note that the literal is always in the IndirectObject role, regardless of whichLiteral, which only specifies the grammatical role for message generation purposes.

    Likewise, assign the IndirectObject role to the topic in TopicAction. This will allow remapTo to be used with a TopicAction.

    Rename TopicAction to TopicTAction, parallel to LiteralTAction.
    Add a new TopicAction that takes only a topic as its object, parallel to LiteralAction.
    Rename whichObject, whichLiteral, and whichTopic to whichMessageObject, whichMessageLiteral, and whichMessageTopic, respectively, to make it clearer that these apply only to the roles played in generated messages, and not to any other roles.

    In particular, the message role only affects the way the object is used in generating messages based on the verb, such as "what do you want to open it with?".

    It would be better to interpret "type on typewriter" as having a missing literal than as having a missing ON phrase (in other words, we want to interpreter this as TYPE <empty literal> ON TYPEWRITER rather than as TYPE "ON TYPEWRITER" <on missing object>).

    To do this, add a verb rule for TYPE ON <object>, without any literal phrase. For most objects, fail in verification; but when verification passes, in the action, ask for a missing literal phrase. (This has the additional benefit that it makes it easy to create objects that allow generic typing on them, as in TYPE ON COMPUTER, for situations where the game doesn't want to make the player type anything specific but still wants to allow the generic act of typing on the object.)

    >ASK BOB ABOUT
    what do you want to ask him about?
    
    >BILL
    "Ah, yes, , very interesting..."
    

    (The problem is that we're not propagating getOrigText() from the empty topic phrase match down to the underlying replacement match.)

    >ASK BOB
    what do you want to ask him about?
    
    >ABOUT BILL
    "Ah, yes, about bill, very interesting..."
    

    (The problem is that we need an aboutTopicPhrase production to parse responses to ABOUT WHAT questions.)

    Add a generic Settable class for things (such as dials) that can be set to various settings. Base Dial on Settable.
    Do not override lookAroundWithinDesc() in BasicLocation; instead, provide an implementation of BasicLocation.roomDesc that simply uses the 'desc' property to display the room's interior description. (This allows nested rooms to differentiate more easily between interior and exterior descriptions, if they want to.)
    Add a new method to Thing, filterResolveList(), to allow "global" filtering of a noun phrase resolution list. Call this method from the parser where we use 'verify' to filter a resolution list. Unlike 'verify', this new method has access to the entire resolution list; this allows the method to take action based on which other objects are in the resolve list.
    Add a new mix-in class, Collective, to serve the purpose of the former isCollectiveFor(obj) method in Thing. To create an object like the matchbook in the sample game, use Collective as an additional base class.

    Remove the special-cased plural filtering in AllPluralProd and DefinitePluralProd. Move this logic instead into Collective's filterResolveList() implementation.

    Add a new class, CollectiveGroup, to allow creating "abstract" collective objects. These differ from regular Collective objects in that a Collective is actually a simulation object (such as a matchbook that can hold several matches), whereas a CollectiveGroup is not a separate object from the player's perspective (so it's never listed as a separate object in a room's contents listing, for example).

    Add a new Thing property, collectiveGroup, that allows an ordinary object to be associated with a CollectiveGroup object. In CollectiveGroup's filterResolveList() method, choose to keep either the individuals (the ordinary Thing objects associated via their collectiveGroup properties with a CollectiveGroup object) or the CollectiveGroup in the resolution list, but not both. By default, make the selection based on action; subclasses can override as desired to use other criteria.

    CollectiveGroup can be used to create things like a "money" object to represent a set of coins and bills, so that a command like "look at money" can respond with a single description of all of the money present, rather than iterating over all of the coins and bills individually.

    Eliminate the actorHereLister. Instead, handle actor descriptions using the special description mechanism. By default, give actors a higher specialDescOrder, to keep actor special descriptions listed after other special descriptions.
    Add a new special description control property to Thing, specialDescBeforeContents; set it to true by default in Thing, but override it to nil in Actor. In verbose room descriptions, show special descriptions in two phases: where we currently show special descriptions, just before the room's list of portable contents, show special descriptions only for items with specialDescBeforeContents set to true. Then, after we've shown the portable contents list and any other sensory messages (listen/smell), show the special descriptions for objects with specialDescBeforeContents set to nil.

    This will allow the traditional ordering, with actor "I am here" messages placed after the rest of the room description, while keeping the rest of the special descriptions grouped with the room's main description. Most special descriptions belong with the main room description because they're meant to be extensions of the room description. Some special descriptions are not; they're meant to describe more ephemeral status information, so work better when grouped with the other status-like messages. Actors in particular fall into the latter category, since actors are meant to seem autonomous, not parts of the rooms they occupy.

    When showing special descriptions of the contents of an object as part of the object's description, use a new form of the special description method, showSpecialDescInContentsWithInfo(), and corresponding specific methods for viewing conditions (showSpecialDescInContents, showObscuredSpecialDescInContents, showDistantSpecialDescInContents).

    Override showSpecialDescInContents in Actor, so that we can show the type of description we previously showed using nestedActorLister.

    Remove nestedActorLister and NestedRoom.examineNestedRoomActors().

    Add a default output filter, typographicalOutputFilter, to the main output stream. In this filter, convert '--' and '---' sequences to typographical dashes, and insert an en space at the end of each sentence.

    The conversion of sentence-ending punctuation compensates for the elimination of the double-space insertion in the VM-level formatter. It also improves matters over the old way by making the treatment of sentence-ending punctuation customizable: the game can replace the filter method with its own custom conversions. This allows customization not only for stylistic variation but for language-specific conventions as well.

    Add the special {subj} messageBuilder token. This token simply notes its target object as the subject of the sentence, for internal book-keeping purposes, but doesn't actually display anything.
    Add a "log file console" type. This would act like the main console or a banner console, in that it would apply the full set of VM-level output formatting (including text-only HTML interpretation) to the text sent through the stream, but it would write the output only to a file, rather than displaying it anywhere. This would be useful for capturing output to a file without showing the output at all on the display; for example, this would make it easy to generate an About.txt file by capturing the output of the ABOUT command to a file during preinit, without having the ABOUT output also show up on the screen.

    This would require a new set of VM-level functions in the tads-io set:

    • logConsoleCreate(filename, charmap, width): returns a handle to the new console
    • logConsoleSay(handle, ...): writes the arguments to the log console; works like say() and bannerSay()
    • logConsoleClose(handle): closes the console

    In addition, add a LogConsole library class to simplify operations with the log console. This class can be quite simple; it's just an OutputStream subclass that implements the writeFromStream method to call logConsoleSay().

    Add a TopicQualifierResolver subclass of Resolver, specifically for resolving qualifier phrases in topics. Use a new instance of this class instead of the direct object resolver in TopicActionBase. (The direct object resolver isn't really appropriate, and doesn't work at all with plain TopicActions, because they don't have direct object resolvers at all.)
    Add a new mapping macro, iobjAsDobjFor(), that makes it safe to map an indirect object handler to a direct object handler in the same object. Use this for the mapPushTravelHandlers() macro.

    (The mapPushTravelHandlers() macro essentially wants to decompose the two-object action into two single-object actions, which isn't safe with the regular asDobjFor() mapping. The difference with iobjAsDobjFor() is that this new routine temporarily makes the indirect object take on the direct object role, so that the target dobj handler sees the proper object in the direct object slot.)

    Add a LabeledDial subclass of Dial. This class accepts arbitrary text labels as dial stops; the property validSettings contains a list of strings giving the valid dial stop labels.
    Remove any .t and .tl extensions from adv3.tl and en_us.tl. (The extensions are implied by the file types; it makes the library file more portable to omit the extensions, since the compiler will automatically apply the default extensions using the appropriate local conventions when the extensions aren't explicitly included in the names as they appear in the library files.)
    Get rid of Thing.articleIndef; games should simply override aName instead when they want to override the default indefinite article determination.
    Change the way theDisambigName, aDisambigName, and countDisambigName work. If name == disambigName, then return the corresponding xxxName from xxxDisambigName; otherwise, apply the same algorithm to disambigName that the corresponding xxxName applies to name. For example, in aDisambigName, if name == disambigName, simply return aName; otherwise, apply the indefinite article algorithm to disambigName and return the result.

    This change will allow disambigName to be overridden without requiring all of the xxxDisambigName's to be overridden at the same time, because the xxxDisambigName's will by default apply the standard algorithms to the modified disambigName. At the same time, though, this has the virtue of the original implementation that, in the common case where disambigName is not overridden, any overrides to theName, aName, and countName will be used for the corresponding xxxDisambigName's. Furthermore, since these are all still separate methods, objects can still separately override each xxxDisambigName as needed for cases where the disambigName is customized and the customized name requires overriding the normal article algorithms.

    While we're at it, add pluralDisambigName, using the same logic.

    Remove the dictionary properties possessiveAdj, possessiveNoun, and their corresponding grammar. (These are no longer needed, since we now have explicit grammar for all of the possessive pronouns in adjective and noun usages. At one time, these were used for adding vocabulary for the player character, but it worked better to use the grammar and resolver mechanism more explicitly.)
    Rework "'s" handling for literal vocabulary:

    First, remove the special-case tokenizer handling for "'s" words that appear in the dictionary, so that we handle all "'s" words uniformly in the tokenizer: all "'s" suffixes are treated as separate tokens.

    Second, never add "'s" words to the dictionary in initializeVocab(). Instead, define a new dictionary property, adjApostS; when we see a "'s" word in a vocabulary list, remove the "'s" suffix and add only the prefix to the dictionary, but add it as an adjApostS instead of as an adjective. (For simplicity, don't bother with nounApostS at all; allow only adjectives as apostrophe-s words.)

    Third, add a new grammar production, adjWord, that accepts either a single adjective or an adjApostS followed by an apostrophe-s token.

    Fourth, where appropriate, change 'adjective->' grammar rules to use 'adjWord->' instead.

    These changes correct the problem that adding a literal "'s" word to an object's vocabulary prevented that word from being used as a true possessive phrase in the grammar. The old tokenizer rule was that a word that appeared with an explicit "'s" suffix in the dictionary was kept intact, rather than split into separate tokens for the root word and the "'s" suffix. Since the word was kept intact as a single token, it couldn't match the grammar rules for possessive phrases, which only match the separated form. These changes allow the same word to be used in either context, since everything is treated the same way in the tokenizer; even if a word is used as a literal vocabulary word with a "'s", it'll still be split up during tokenization. Even with this change, we can still match explicit "'s" vocabulary words, thanks to the adjWord grammar.

    Note this has one small potential impact on existing games: if an object explicitly defines its own 'adjective' property (rather than using the automatic library initialization), and a word defined in the adjective list ends in apostrophe-s, then that word must be stripped of the apostrophe-s suffix, removed from the 'adjective' list, and added to a new 'adjApostS' list instead. Similarly, if any vocabulary words that end in apostrophe-s are dynamically added with G_dict.addWord(), the apostrophe-s words should be stripped of the suffix and added under the '&adjApostS' property instead of the '&adjective' property.

    Add noteLiteral(lst_.getOrigText()) to getVocabMatchList() in simpleNounPhrase(misc)? This would make the length of text in a miscellaneous word list a match selection criterion (less important than the presence of a misc word list). This won't change anything in cases where we want to choose between a match with a misc word list and one without, as the presence or absence of a misc word list is more important than literal length, but it will help distinguish between two matches that both have misc word lists. Since we prefer a shorter literal match, this will have the effect of preferring to match the more structured interpretation, since a shorter misc word match means a longer grammar match.
    Fix run-time error with "a " (the 'inherited' argument list is wrong in AskAboutAction.getDefaultDobj; the same problem is in TellAboutAction).
    Rename class Fixed to Fixture.
    Add new class Immovable: this is for objects that aren't fixed in place obviously or by their very nature (as are Fixtures), but rather are fixed for some other reason: great weight, hidden fasteners, etc. This class differs from Fixture in that it disallows actions like take, move, push, and pull not in the verify() but in the action().

    Add a subclass, Heavy, for objects that are immovable because they're very heavy. This is suitable for things like large furniture and big boulders.

    Remove moveInto() override on Immovable. (This attempted to be a last resort to disallow actions that involved trying to move the object, but it prevented programmatic relocation as well, creating more problems than it solved.)
    Fix problem with applying AGAIN to a command issued to another actor: the target actor becomes "busy" and won't accept another command until a turn passes. The problem is that we're not accounting for synchronous actors in the AGAIN processing: we need to tell the issuer to waitForIssuedCommand on the target actor.
    Fix problem in GiveTo and ShowTo actions: getDefaultIobj calls inherited() with wrong argument list.
    Fix the parameter list for npcActionMessages.okayTurnTo.
    Make RandomTextList customizable with the percentage of the time a message is generated at all. Random message lists are often used for atmospheric messages, and it's usually better if these messages don't appear on every single turn. Add a 'messagePercent' property that determines the percentage of turns where a message is generated; the default is 100, which leaves the default behavior as it was (i.e., a message is generated on every turn).

    In addition, especially with random atmospheric messages, it can get tedious to see the same messages over and over if you spend a lot of time in the same area. It's therefore often the case that we want frequent atmospheric messages when the player first gets to a location, but then we want the frequency to drop dramatically after the player has spent more than a few turns there. To make this easy to handle, add a couple of new properties: 'messageReduceAfter' is the number of times that we want to see messages at the initial frequency, and 'messageReduceTo' is a new value for 'messagePercent' that we'll apply after we've generated the messages 'messageReduceAfter' times. Make these nil by default, which means that there is never a reduction in the frequency. Authors can use these properties to generate random atmosphere messages at high frequency at first, but then drop the frequency to just an occasional message after the player has been in the location long enough to get the idea.

    Add a new class, NominalPlatform. This is a "secret" object: it's not visible to the player as a separate simulation object, and won't normally have any vocabulary or appear in any room listings. The purpose of this object is to make it easier to describe NPC's as standing somewhere specific within a room; the author locates an NPC within the NominalPlatform, and the library will automatically describe the NPC as standing on the platform: "Bob is here, standing in the corner." The author can also customize the display methods for the nominal platform so that the description is something like "leaning against the lamppost."
    Add a new property to Thing, specialContentsLister, that lets individual objects and rooms customize the lister they use to generate special descriptions. Set the property by default to specialDescLister.
    Add a new topicPhrase grammar rule that matches a miscWordList without badness. (The normal topicPhrase grammar matches a singleNoun, which matches a miscWordList, but it does so with badness; we want to add an explicit rule that matches a miscWordList without badness.) It's in the nature of topic phrases to go outside of what's implemented in the simulation model; we don't want to treat resolvable and irresolvable topic phrases any differently at the grammatical level, because doing so can lead to inconsistencies when parsing a verb containing both a topic and another kind of noun phrase.
    Add gLiteral to adv3.h, analogous to gTopic.
    Change Thing.dobjFor(Attack) so that the default Attack handling is simply to display the message "it's useless to attack that." Remove the askIobjFor(AttackWith). Specifying a weapon shouldn't be required; especially for attacks like "hit" and "kick," asking for an indirect object will seem wrong to a player. This makes a tiny bit of extra work for authors when AttackWith is overridden, since Attack will have to be overridden as well in such cases, but this is probably worthwhile anyway because it'll encourage authors to make the same distinction between weapon and weaponless attacks that players are likely to make in their own minds.
    Move the code from Contents.examineContainerContents into Thing.examineStatus (actually, into a new method called from Thing.examineStatus, call it examineListContents). Get rid of Surface.examineSurfaceContents. Ordinary Thing objects should traverse into their contents during Examine; this shouldn't be limited to Container and Surface objects. We need to do this listing in Thing, because a Thing could contain a component that's a container, and we want to list its contents when examining the Thing.
    Fix bug in Action.getObjPreconditions: if pre is nil before appending the catch-all list, we'll get an error. Check that pre is nil before appending, and just use the catch-all list by itself if so.
    Change the TravelConnector.describeDeparture() and describeArrival() interfaces to add a new parameter for the traveler. Use the traveler parameter instead of the global gActor. This makes the travel routines more flexible, and in particular allows them to be called from outside of commands (in daemons, for example). It also will improve the handling for non-actor travelers (vehicles in particular).
    In RoomConnector.describeDeparture/Arrival, use gPlayerChar rather than gActor as the point-of-view object in directionForConnector(). The description is being generated for the benefit of the player, so it should be from the PC's perspective.

    Split off a new method from each of Traveler.describeDeparture and describeArrival: describeNpcDeparture/Arrival, which is called when an NPC (or a traveler not involving the PC) is doing the travel. This will simplify overriding the messages for vehicles and other special travelers when desired, since the overriding method won't have to make all of the checks to see if it's the PC doing the travel.

    Run all of the libMessages.sayDeparting/Arriving connector-specific messages through an intermediate method call in the Traveler; these new Traveler methods by default are simply be covers for calls to the libMessages methods. The purpose of the extra layer of calls through Traveler is to make it easier for individual Traveler subclasses to customize the messages on a per-connector-subclass basis.
    '@script.txt' at the command prompt should start reading from the named script file. (This should work essentially the same way it did in tads 2, except that we want to handle this in the library instead of in the interpreter.) Also, '@@script.txt' should run the script in "quiet mode," which is to say that input and output generated while reading the script are to be suppressed.
    Add some new classes to misc.t for "shuffled" random selections. A shuffled selection is a random selection taken from a set of values that doesn't repeat a selection until we've gone through all of the values once, like dealing from a shuffled deck of cards.

    ShuffledList: the basic shuffled selection class. Keeps a list of values, and returns a randomly selected element on demand.

    ShuffledIntegerList: a specialization of ShuffledList that returns a randomly selected integer from a given range.

    ShuffledTextList: a subclass of RandomTextList that makes its selection using shuffling rather than independent random selection.

    Add a point-of-view parameter to Thing.showSpecialDescWithInfo().
    Add a parameter to the following library messages to indicate which object is being described (the object is needed for things like gender and number agreement in some languages):

    openMsg, closedMsg, lockedMsg, unlockedMsg, onMsg, offMsg

    Add a parameter to the following library messages to indicate which actor is being described (the actor is needed for things like gender and number agreement in some languages):

    statusStanding, statusSitting, statusLying, statusStandingOn, statusSittingOn, statusLyingOn

    Add a parameter to the various name methods in ThingState (listName, inventoryName, wornName) providing a list of the objects being shown in this state. This is needed for some languages so that the state description can agree (in gender and number, for example) with the objects being desribed as being in the state.
    Change the count parameter to the full list in actorStandingGroupPrefix and the related methods in libMessages. This will allow the prefix/suffix messages to check any attributes of the individual objects in the list, such as gender mix, that might affect the message.
    The INSTRUCTIONS command should not take any turns (make it a "system" command). Also, it should not consume any game time on the real-time clock (which, given the size of the instructions, could be noticeable).
    Add an extended form of inputManager.getInputLine(), which takes a new InputDef object to define the input parameters. Use this new parameter definitions object to determine what to display to set and remove the input text style; do this instead of unconditionally using the <.inputline> style tag. The InputDef class should use <.inputline> as the default, of course. Keep the existing getInputLine() method, but make it a cover for a call to the new extended version, setting up an InputDef object representing the parameters.
    Make "remove" (with a direct object but no indirect object) a fully separate verb from "doff". In English, "remove foo" usually means "doff foo", but it can also have the substantially separate sense of removing a component of an object. In the English library module, provide a default mapping of "remove" to "doff" in Thing, to keep the default meaning the same. Making a separate Remove action will allow this action to be independently overridden per object when it's desirable to do so.
    By default, don't list anything in the credits for a library module. We should leave the formatting of the credits fully up to the author.
    3.0.5
    Add an optional mainRestore() function, to be provided by the game and called from the run-time startup code when a saved game is explicitly selected for restoration at startup (such as by double-clicking on a saved position file from a GUI desktop, or using the "-r" option with the command-line interpreter).
    Change runGame() so that it no longer takes the initial player character parameter. Instead, callers should always set gPlayerChar explicitly prior to calling runGame(). (This change makes runGame() more sensible for cases where a game is restored: since the restore operation will restore gPlayerChar as part of restoring the rest of the game's state, it would be redundant to pass gPlayerChar as a parameter to runGame() only to have runGame() set gPlayerChar to that value.)
    Fix the bug in EventManager.removeMatchingEvents ('eventMatches' should be 'cur.eventMatches')
    Add a new output stream method, addOutputFilterBelow(), which adds an output filter at a given point in the filter stack.
    Remove the "\( \)" escape sequences (for highlighted text). Authors should use HTML markups and instead.
    Combine open/closed and contents status listings into a single message, to make the openable container default description less choppy.
    Rename maxBulk to bulkCapacity, to make the meaning clearer. Similarly, rename maxWeight to weightCapacity.
    >put trike in bag. drop bag. ride trike. (produces a run-time error) The problem is that we're failing to treat the bag as an invalid "staging location" on the way to boarding the trike. Add a routine, checkStagingLocation, to Thing, and call when moving an actor into a chosen staging location; this routine should generate an error explaining why the travel is impossible and terminate the command with 'exit'. BasicLocation should override this to allow being used as a staging location.
    add default Thing handling for 'feel' and 'taste' commands
    >close bag; x all in bag
    red ball: You cannot see that. [etc]
    

    We're incorrectly resolving the contents of an object for an "all in x" phrase, even when the contents can't be seen. We must filter the resolved objects to include only the visible contents.

    "all in x" doesn't filter for objects hidden from "all" phrases; it should.
    Standing should be a precondition of "jump".
    The noMatchDisambig and disambigOrdinalOutOfRange messages should use <.parser> open tags, but not close tags: instead, the <.parser> mode should be left open, to be closed by the disambiguation re-prompt that always follows.

    askDisambig should provide an open <.parser> tag if and only if askAgain is false: this will let the mode flow in from the preceding re-prompt message from noMatchDisambig or disambigOrdinalOutOfRange. askDisambig should always close the <.parser> tag.

    Get rid of parserMessage(), and just use <.parser> tags instead.
    We shouldn't be able to read at brightness 2. For Readable, differentiate readability according to brightness and transparency, as long as a readDesc is defined. (When no readDesc is defined for the object, use the default "examine" behavior as usual.)
    Disambiguation: when indistinguishables are involved, answering with an ordinal applies the ordinal to the full list, rather than the reduced list offered:

    >take coin
    Which coin do you mean, a gold coin, a silver coin, or a copper coin?
    
    >second
    Taken. [actually takes a gold coin, since more than one was present]
    
    Add a new option flag, libGlobal.allowYouMeMixing, that controls whether or not the parser accepts "you" and "me" in commands as equivalent. Set this to true by default, since most games will not have both first- and second-person narrative characters at the same time, and hence it seems unlikely that it will ever create confusion if the player can use "you" and "me" interchangeably. (Having the option to make the parser strict about this will take care of any cases where a game actually does have separate first- and second-person characters present simultaneously.)
    The forwarding scheme isn't quite right. Replace it.

    First, get rid of dobjForwardTo, iobjForwardTo, remapTIAction, dobjRemapTI, iobjRemapTI, dobjRemapTIReverse, and iobjRemapTIReverse.

    Second, add a new mechanism that combines the old forwarding and remapping schemes into a single new system. Use syntax like so:

    desk: Fixed
      dobjFor(Open) remapTo(Open, drawer)
      dobjFor(Close) remapTo(Close, drawer)
    ;
    

    The first definition above maps the Open action, when applied to the desk, to a replacement action of Open applied to the drawer instead. The second does the same thing for Close. This replaces the old dobjForwardTo/iobjForwardTo scheme, so we no longer need forwarding at all. The difference with the new remapTo scheme is that we use a replacement action for the remapping - the remap for Open above is essentially like using replaceAction(Open, drawer) for the action definition of Open on the desk.

    Further, consider this syntax:

    lid: Fixed
      dobjFor(Push) remapTo(Open, jar)
      dobjFor(Pull) remapTo(Close, jar)
    ;
    

    Here we've remapped not only the object (as we formerly did with forwarding), but also the action. The first definition above says to replace "push lid" with the new action "open jar."

    Next, consider this:

    ninjaThrowingStar: Weapon
      iobjFor(AttackWith) remapTo(ThrowAt, self, DirectObject)
    ;
    

    In this case, we're doing what the old dobjRemapTIReverse did, but with considerably clearer syntax. We're saying that when we resolve the indirect object of "attack with" and find that it's the throwing star, we should remap the action to "throw at ", where is the original direct object of the AttackWith action. The important thing about using the "DirectObject" argument is that it tells us to use the pre-resolved match tree for the direct object if we haven't yet resolved the direct object noun phrase - this means that we'll be able to resolve that noun phrase in the context of the new action (ThrowAt) rather than in the original action (AttackWith) context.

    Compiler: for each nested object definition, set a property of the nested object, 'lexicalParent', to point to the lexically enclosing object.
    Change the default message for talking/asking/etc a random object to indicate more specifically that it's illogical to talk to the object.
    Add a class for a complex container, for objects that contain objects in multiple ways. Examples: a crate that can contain things within and also have objects placed on top of it; a container with components.

    The class should treat all of its immediate contents as components, so it should be based on Thing rather than Container or Surface. It should have two additional properties: one for a secret object that serves as the internal container, and another for a secret object that serves as the internal surface. Commands like "put in" and "look in" should be redirected to the internal container, if one is defined; "put on" and the like to the internal surface, if present. The contents lister must specifically show the contents of the secret internal container and surface as though they were contained directly by the outer object.

    Change default Thing handling for Push, Pull, Move, MoveWith (dobj), and MoveTo (dobj), so that verification succeeds with a logicalRank of 50, and report that there is no effect in the action. (These actions all involve physical manipulations that a player might want to try with any object, so it's better to allow these to pass verification, so that preconditions can be allowed to proceed rather than halting before even getting to preconditions.)
    OutOfReach seems to put the object itself (not just its contents) out of reach. Add a separate test for reaching 'self'.
    In Actor.canReach, don't make touching our immediate location a special case - just traverse the sense path to the container as normal.
    >look in stove
    (First opening the stove)
    Opening the stove reveals a loaf of bread.  The stove contains a loaf of bread.
    

    It would be good to remove this redundancy: if we're opening something implicitly for a look-in command, we shouldn't bother showing what the opening reveals. (We can handle this in Openable's Open action handler: if the action is implied, and the parent action is LookIn with the same direct object, we can suppress the revelation message.)

    Suppress default "you see nothing special about it" or "it's an ordinary x" descriptions when there's a special status message, such as contents to be listed.
    >look in small alcove
    You see nothing in it.  A trophy is visible...
    

    (Perhaps if we special description items, and nothing listed, we should display no message at all.)

    Use tags for line-input modes in commandSequencer, eliminating method calls. This should remove the fragile order-of-call issues that we currently have.
    Should we reformat some messages involving implied commands? For example:

    >put coin in bag
    (first opening the bag)
    Opening the bag reveals a keyring. Done.
    

    Perhaps we should put that "done" on a new line - no paragraph separator, just a new line. (In general, perhaps when there's a non-default response from an implied command, we should add an "implied command terminator separator," which by default would simply be a newline.)

    Rename the English-specific part of the library to make it easier to add new languages in the future. In particular:
    • Create a new US English subdirectory of the adv3 library directory; use ISO 639/3166 codes for the naming convention, so the US English directory is called en_us.
    • Move us_eng.t to en_us/en_us.t
    • Move us_eng.h to en_us/en_us.h
    • Move msg_neu.t to en_us/msg_neu.t
    • Add a new library, en_us/en_us.tl. Move the English-specific source file inclusions out of adv3.tl and into en_us.tl.
    • Include the new en_us.tl library in the auto-generated makefile for the new-project wizard in Workbench. (We'll have to add a user preference at some point that lets the user select the language library to use, rather than using en_us.tl unconditionally.)
    Add a general IF instructions module (like instruct.t from tads 2)
    Fix nil dereference in transcript processing for ambiguous noun phrase errors.
    Fix command state management for suffixes so that suffixes are always generated before the prompt. (We simply need to display <.commandbegin> in intput.t before displaying the prompt.)
    Add a new command state, stateNoCommand, for interactive input. In this state, don't add any prefixes/separators/suffixes when reading input. This should be used for yes/no answers, answers to end-of-game prompts, and so forth - for anything where we need to read input interactively in the course of a command.
    Move en_us.t Thing vocabulary initialization code to VocabObject.
    For commands like "put coin with jar," we're asking "what do you want to put," which is a weird question. (This happens because we interpret this as "put coin with jar in missing-np" and then resolve the iobj first; this asks the usual question, which comes out the way it does because the dobj tentatively resolves to an empty list since it is a non-matching phrase. To solve this, we could use a dummy entry in the tentative resolution list, to signal that we actually have a dobj phrase, even if it's not resolvable to anything.)
    It might be nice to catch constructs like this:

    >open door with key
    You see no door with key here.
    

    In particular, it might be nice to recognize "<nounPhrase> <prep> <nounPhrase>" constructs specifically, and flag them as "command not understood" errors instead of "object not seen" errors. This could be handled by adding a "badness" production for np-prep-np structures for the prepositions commonly used in command phrases; by making this a badness match, we'll match it as a last resort when we can't find a valid verb phrase structure.

    Add an extra category of reports, "extraReport", that's used to add extra information that doesn't replace any default report. Use this for the key-found-on-keyring report, since this report is supplemental to any default Locked/Unlocked report and shouldn't suppress it.
    Move the call to initializeVocab out of Thing.initializeThing, and instead call initializeVocab directly from adv3LibPreinit.execute() for each VocabObject. (This will initialize vocabulary for non-Thing VocabObjects, such as Topic objects.)
    Add a new inputManager method, promptForMore(), that shows the MORE prompt, taking care of flushing the transcript and dealing with the real-time clock. The method should take an argument specifying whether or not to freeze the real-time clock; but even if the clock keeps running, real-time events shouldn't be allowed to occur until after the user acknowledges the MORE prompt, because otherwise we'd defeat the purpose of the MORE pause, which is to wait for user acknowledgment before showing any new output.
    Change the Decoration class so that its generic not-important message can be overridden simply by changing a property, rather than having to override all of the Default handlers individually.

    Likewise for Distant and Intangible.

    Add a new 'isTopLevel' property, which determines if an object is a top-level container within the game world. Set this to nil by default for everything except Room objects, where we set it to true. Use this property in evaluating isIn(nil): for an object with a nil location, isIn(nil) returns true if and only if the object is NOT top-level. This allows isIn(nil) to be used to sense whether or not the object is part of the game world, recursively considering the status of the object's containers.
    Remove selfInReach from OutOfReach.
    Fix problem with TravelConnector.verifyTravel(): when this routine is called, gVerifyResults is not reliably set to a valid list. In particular, gVerifyResults must be set to a valid result list from TravelAction.verifyAction().
    Turn on the sense cache while generating the status line. Displaying the status line can involve some substantial sense calculations, so enabling caching while generating it can improve overall response time noticeably.
    Fix 'follow wall' (generates nil pointer error)
    Fix 'throw x at wall' (or floor or other room parts - nil pointer error)
    Fix nil pointer error when addressing an ambiguous target actor.
    >sit on darker chair
    >sit on chair
    Which one?
    >darker
    

    This is allowed, but shouldn't be - the reply should be "you're already sitting on the darker chair." (The problem is that we verify the lighter chair as okay, and we remember that we verified something as okay but don't bother remembering what; we need to track which objects we verify as okay individually.)

    Make searching a surface (or looking in a surface) show the surface's contents.
    Check bulk when adding a new item to a surface. (This will fix the weird inconsistency with chairs that allows you to put an object on a chair you're sitting on, even if there's not room for you to sit on the chair when the object is already on it.)
    Reduce the verification logical rank of non-Food objects for >TASTE.
    >sit on chair
    >look
    ... You contain a keyring ...
    

    (We shouldn't mention your contents in that manner. The problem is that Actor inherits contentsListed from Fixed; Actor should override contentsListed and set it to nil, since an actor's contents shouldn't be listed in the ordinary fashion in a room description.)

    Show object contents for EXAMINE and LOOK IN to full depth.
    Searching (or looking in) a room part shows contents as being "in" it, which isn't appropriate for walls, ceilings, or floors.
    For THROW AT FLOOR, perhaps we should have a message other than "Dropped" - something indicating the action is more violent than just setting the object down.
    Make contents listings go to arbitrary depth. Rather than showing a separate list for the contents of top-level list items, show the contents of each item parenthetically, and then recursively add these parenthetical lists to show all contents. (Only do this when the ListRecurse flag is set.)

    Add a new Thing property, contentsListedSeparately, that allows an object to control whether its contents are listed in the new in-line style, or as the traditional separate list. Make this nil by default.

    For "tall" listings, show contents of unlisted top-level items in separate lists after the main list, just as we do for "wide" listings.
    Remove the need to call showContentsList() separately after showList(); instead, call this automatically from showList() if the ListRecurse flag is set.
    Throwing destinations aren't working quite right. Refactor things a bit: rename getFallDestination() to getHitFallDestination(), and give it the previous container in the path as a parameter, so that it can decide whether the object is being thrown from inside or outside (or, in the case of a multi-location item, the source location). Make things drop to the floor in most cases; games can override to make things fall into intermediate containers when desired.
    Change Actor.standUp to use reportFailure() when already standing.

    >out
    Out of what?
    >e
    You can't get out of that (the east wall).
    

    It would be nice to treat such responses as new commands. We could probably adopt the heuristic that we give the new command interpretation priority over the noun phrase interpretation of a reply to this prompt. Anything that looks like both a syntactically valid noun phrase and a syntactically valid command probably matches the noun phrase syntax only because it's extremely abbreviated; the chances of the verb phrase match being an accident are somewhat less. In addition, it should be less confusing to the user to treat the response as a new command when a noun phrase was intended than vice versa, and it should be fairly obvious how to force a noun phrase interpretation (adding "the" would do the trick, for example).

    Can we go to arbitrary depth for contentsListedSeparately? In particular:

      >i
      You are carrying a duffel bag (which contains a pen cup).  The 
      pen cup contains four pens.
    

    Right now, we're not adding that bit about the pens, because we think we're done with the top-level list, as we've already listed everything: we scan the duffel, and decide that we don't need to recurse into it, since it's listed and has in-line listed contents. What we'd need to do is add another recursive descent to look for everything that we listed at the second (or deeper) level that has contentsListedSeparately.

    Tweak the parser's command match-tree ranking mechanism a little, so that differences in the number of occurrences of a problem are counted in a second pass, after we've exhausted the possibility of any differences in the presence or absence of problems. This will make slightly better choices in certain cases, such as when we have two separate noun phrases in the action, and one interpretation treats only one as ending in an adjective while the other has an adjective ending in both phrases.
    Add a profiling facility, to gather statistics for performance optimizations in the library.
    Rename senseInfoList to senseInfoTable, and make it return a LookupTable rather than a list. Sense information lists are almost always used as random-access tables, so we can speed up some operations by representing these as lookup tables keyed on object.
    Rename connectionList to connectionTable, and make it return a LookupTable rather than a list. Since we want only one copy of each object in the result, it's faster to build this as a lookup table, where we can determine whether or not an object is already present much more quickly than we can with a vector.
    >e
    >put cup in desk
    >undo
    

    (Takes back turns through "e". The problem is that we're marking a remapped action as a nested action; it's not really nested, because the original action is completely replaced and will never reach the execution phase. Do not mark remapped actions as nested.)

    Add Thing.specialDescOrder to control specialDesc the relative order of specialDesc listings.
    Change the format of the first-score-notification supplemental message, which explains how to turn off score notifications: rather than showing it as part of the same paragraph with the score, start a new line and show it as a separate notification message.
    Rename UnqualifiedPluralProd to DefinitePluralProd. In the English parser, base implicitDetPluralOnlyNounPhrase(main) and explicitDetPluralNounPhrase(definite) on DefinitePluralProd, since "books" and "the books" should act the same way in English.
    Simplify the Key and Keyring Attach and Detach handling by using remapTo() in the keyring: remap "attach x to keyring" to "put x on keyring" in Keyring.iobjFor(AttachTo), and remap "detach x from keyring" to "take x from keyring" in Keyring.iobjFor(DetachFrom). This removes a bunch of code from Key to handle the remapping at its end - that code all predated the remap mechanism, and can now be greatly simplified by using the new mechanism.
    Fix a problem in vocabulary initialization: if a hyphen is used as a placeholder in a vocabWords_ setting, the hyphen is added to the object's part-of-speech property (it's not added to the dictionary, but it does show up in the list for the part-of-speech property). It should be omitted from the list, since it's not really a vocabulary word.
    Fix finishOptionUndo so that it can be used in daemons. This requires a couple of changes. First, remove the transcript flush - this is a relic of the pre-'transient' transcript mechanism and is no longer needed. Second, daemons and fuses need to be able to use TerminateCommandException and the like to exit, so we should catch command-ending exceptions in the event manager.
    Fix examining sightSize=large objects at a distance. (The problem is that Thing.canDetailsBeSensed() is attenuating the ambient light level for the sense path, which is unnecessary as the SenseInfo already does this. canDetailsBeSensed doesn't need to go to so much trouble - any SenseInfo with trans != opaque indicates the object can be sensed, since trans will always be set to opaque for objects that cannot be sensed.)
    Add tok.t to system.tl. (This file isn't actually necessary to compile a simple program, which is why it hasn't been in system.tl all along; but it is necessary for any game based on the library, so on balance it's probably better to include it in the default library.
    Separate some action-time checks into check() routines: Container.dobjFor(LookIn); Dial.dobjFor(TurnTo) (this could perhaps benefit from some 'verify' checking of the literal value as well).
    Add a specialDescLister, and use it for listing special descriptions. Add a new property of Thing, specialDescListWith, which returns a list group to use when showing special descriptions; use this new list group generator in specialDescLister. This will allow special descriptions to be grouped when desired, simply by using the standard list grouping mechanism.
    Add an "isCircularPassage" property to TravelConnector: when this property of the connector is true, fully describe travel that winds up in the origin. (We normally don't bother describing such circular trips, but sometimes it's desirable to be able to do so.)
    In en_us.t, add askDobjResponseProd=singleNoun for each action that takes a singleDobj as its direct object.
    In en_us.t, remove the redundant syntax for "walk in/into" and "go in/into" from VerbRule(GoThrough), as these are already handled by VerbRule(Enter).
    In en_us.t, use tags wherever literal quotes are used, rather than ASCII-34 quotes.
    Convert the parser to use the new Dictionary API. In the English parser, install a StringComparator to perform case folding and truncation matching. Remove case conversions from the English tokenizer, as they're not needed with the StringComparator.
    Refactor the travel connector operations so that everything turns into a TravelVia on its connector. Handle direction commands ("go north") by using replaceAction(TravelVia, connector). This simplifies the travel verbs, and makes the task of defining travel connectors more consistent with defining other action handlers.
    Move the travel connector checks for dark-to-dark travel and for travel barriers into the TravelVia check() handler.
    Rearrange the room description code (lookAroundPov, etc) so that lookAround can be called on any object. To do this, move all of the room description mechanism from BasicLocation into Thing, and work the NestedRoom code into the Thing code for descriptions.
    Add a return value to basicEventManager.removeMatchingEvents, indicating whether or not a matching event was actually found.
    Fix getTenativeLiteralText argument mismatches in parser.t when calling getLiteralText.
    In MessageBuilder.generateMessage(), for the case where we're substituting '{actor}' but there's no current action, use the current gActor value if it's not nil, or gPlayerChar if gActor is nil (rather than always using gPlayerChar - using gActor instead allows a daemon or other non-action message producer to set gActor to indicate the actor doing whatever it is that's generating the message).
    In Actor.addPendingAction() and addFirstPendingAction(), take a resolved object list as the last varargs-list argument, and pass it to the PendingCommandInfo constructor.
    Hide Fixed objects from "drop all".
    Add some more default verbs: CutWith, Kiss.
    Add RoomPart.isIn(). Consider a RoomPart to be within a given location if its room part location is equal to or within the given location, or the inherited isIn returns true.
    Add some more string-quoting styles to the English tokenizer: `like this', and with typographical ("curly") single and double quotes (\u2018 and \u2019 for single quotes, \u201c and \u201d for double).
    Add a quotedStringPhrase production type, for rules that explicitly call for quoted strings (but not unquoted literal text). Use this production to build the quoted string rule for literalPhrase.
    Add SAVE, RESTORE, and SCRIPT command variations that take quoted strings giving the filenames to use.
    Traveler.travelTo in a daemon dereferences gAction - check to see if gAction is nil, and skip that step if so.
    Change all uses of 'seen' to use the seenBy() and setSeenBy() methods. Move these from BasicLocation into Thing so that all objects uses them uniformly.
    Bug: TERSE mode doesn't show full descriptions on first entry.
    Some of the objects shown in the introductory room description aren't getting marked as seen. (The problem is that Thing.lookAroundWithin is using gActor in a few places where it should be using the 'actor' parameter.)
    Bug: NoTravelMessage is commented as being an apparent connector, but is implemented as a non-apparent connector. Leave the implementation the way it is, but fix the comments, and add a new class that acts the same as NoTravelMessage but has an apparent connector (call it FakeConnector, perhaps).
    Rename LiteralAction to LiteralTAction. Add a new LiteralAction class that takes only a literal phrase as its object (i.e., no other direct object), for commands like "say random stuff". Note that DefineLiteralAction is likewise renamed to DefineLiteralTAction, and we add a new DefineLiteralAction for defining an action with only a literal phrase for its object.
    Add a PostUndoObject class, analogous to PostRestoreObject.
    Reduce parse rankings for pronoun phrases vs noun phrases: if a word is explicitly defined as noun, it probably should be a stronger match than a pronoun interpretation of the same word.
    3.0.4 and earlier
    Consider this command:

    >put ball in asdf
    Which ball do you mean, the red ball, or the green ball?
    
    >red
    The story doesn't know the word 'asdf'.
    
    >oops box
    Which ball do you mean, the red ball, or the green ball?
    

    The issue is that OOPS retries the entire command with an updated token list, and as a result, the disambiguation work from the first attempt at resolving the objects is lost for the second iteration. The reparsing is necessary, since the corrected typo could change the entire meaning of the command.

    One possible solution: add a resolution pass specifically to catch unknown words. Don't do any other resolution on this pass - just deal with unknown words. This would ensure that we process OOPS before we ask any disambiguation questions.

    Another possibility: keep a set of answers to questions, and the conditions under which the questions were asked. Clear the list each time we start a new parsing from scratch, but let the list survive if we reparse a token list. Whenever we're about to ask a question, look to see if the same question has been asked with the same conditions, and if so, reuse the same response.

    All
    Missing noun phrases: prompt for input when PC gave command
    Missing noun phrases: use a default when possible
    Object announcements: include a preposition when appropriate (announcing indirect object, announcing direct object with verb taking a preposition: ">dig \n (in the dirt)")
    Pronouns
    Again
    Again: should we use disambiguation information from past commands? For example:

    >take book Which book do you mean, the red one, the blue one, or the green one? >blue Taken. >g

    Should this 'take blue book' or should it ask... Which book do you mean, the blue one, or the green one?

    Right now we use the literal tokens from the original command, so we ask the disambiguation question again. This is exactly what we want in cases with indistinguishables:

    You see five silver coins here.
    >take coin
    Taken.
    >g
    Taken.
    

    But in cases where we have distinguishable objects, we probably want the resolved objects, not the words.

    We probably want to distinguish this way: if we had indefinites or indistinguishables, resolve again from the original text. Otherwise, use the original resolution.

    disambiguation: we're not keeping the full list properly in cases of indistinguishables:

    >take coin
    Which coin, a gold coin, or a silver coin?
    
    >coin
    Which coin, the silver coin, or the gold coin?
    

    The second time around, the cardinality of the full list should not change, so we should be asking the same question as the first time.

    Preconditions
    We need 'touchDobj' and 'touchIobj' preconditions - use these for all objects that require that the object be physically manipulated by the actor but not necessarily held. These are required to deal with things like the contents of a closed transparent container, where an object can be in scope but cannot be manipulated.

    As part of this, we need to find a way to say what object blocks a sense path opaquely, so that we can say why we can't touch something that we can see. There are two main cases: containers are in the way, and connectors are in the way.

    Bag of holding: when an actor's hands are full and the actor is trying to take something, pick a held object and try to find a bag of holding for it. First, check the object's preferredHolder property - this gives an object or class that the object prefers as its bag of holding. If the actor is holding such a holder, move the object to the holder. If we can't find any affinity, look for any object the actor is holding of the generic BagOfHolding class.

    Examples of specialized bags of holding: wallet, key ring, purse, duffel bag.

    Formatting: where does the blank line come from before an implicit message for a single command?
    use disambiguation filter even for indefinites, so we pick the best possibility if there are objects of different likelihoods
    "The red one" should work for exclusion lists. It should probably also work in general - although 'one' should bind more tightly to an actual name, in the absence of a name it should serve as a generic noun.
    "drop coins" - should we consider only the ones we're carrying? In other words, should we run verification and include only the logical ones? Probably - if there are some logical and some not logical, apply the command only to the logical ones; otherwise, apply the command to all of them.
    "take both coins" - really wacky
    "take two gold" should work (i.e., quantified ending in adjective)
    consider:

    >take both coins
    Which two (of five gold coins or three silver coins) do you mean?
    
    >two gold
    You don't see that many coins here.
    

    The problem is that we disambiguate with the reduced match list.

    >take coin
    Which coin, gold, silver, or copper?
    
    >all but copper
    

    no worky - might even be a gram prod bug, since we seem to have a weird firstToken/lastToken value in these cases

    "take books except red" - disambiguation of the exclusion list should be limited in scope to the resolved plural list.
    "take books except silver" - "You see no silver here" - this should ignore the missing object.
    "take coins except copper" - the 'copper' should implicitly be plural rather than indefinite so that we skip all of the copper coins if there are several
    >take gold coin, gold coin
    gold coin: Taken.
    gold coin: You're already carrying the gold coin.
    

    When we have multiple equivalent items in scope, and we mention several items in a list, we should pick separate equivalent instances for each mention.

    This should apply to disambiguation responses, too:

    >take coin
    Which coin do you mean, a gold coin or a silver coin?
    
    >gold, gold
    
    "take any ball except green" - takes the green if there's nothing else left
    add verification for pre-conditions - objHeld, for example, would boost the likelihood for a held object
    add the new pre-execution check as a separate phase (checkXxx?)
    output capturers - one at a time
    add object affinities for bag of holding
    key ring - not just a bag of holding, but automatically adds items as they're picked up when the key ring is in inventory
    Key ring messages: we should check for a non-default report, and if there is no non-default report we should include more details: "You take the key and put it on the keyring."
    Probably should have an objDirectlyHeld precondition. This one would be used for commands that physically move an object; objHeld would continue to be used for commands that merely need to do something with an object. So, "put x in y" would require that x is directly held, while "unlock x with y" would only require that y be nominally held. For objDirectlyHeld, we'll make the implicit command silent if the object is being indirectly carried, so that we don't get weird things like this:

    >i
    You are carrying a paper bag.  The paper bag contains a key.
    
    >drop key
    (First taking the key)
    Dropped.
    
    If there's no doXxx, fail verification with a generic Illogical('you can\'t do that').
    For travel: shouldn't we have a verify phase for moving the actor?
    need to export propNotDefined in a system file
    take key: if it started on the keyring, we should take it off the keyring
    Travel connectors
    Fix 'i tall' listings to show contents with a better format
    Move senses to a separate module.
    >get coin
    Which coin, a gold coin, a copper coin, or a silver coin?
    
    >coin
    Which coin, a copper coin, a silver coin, or a gold coin?
    

    The re-ordering on the second round is weird - we should keep the order stable if we can.

    >get ball
    Which ball, red or green?
    
    >blue
    You don't see that here.
    

    The error shouldn't be "you don't see that here" - it should be more like "that's not one of the choices you were offered." However, it would be better to widen the scope again to the real scope, rather than failing.

    Add maxWeight and weight enforcement
    Move pre-conditions to the objects. Keep the verb preconditions as well, but verb-specific pre-conditions should never mention the objects.
    Mark objects for which we've displayed descriptions, for topic scoping
    Add "module ID" objects, for library extensions. The "credits" command should run through the module ID's and show a credit for each one.
    Make styles into objects
    Fuses and daemons
    Need to apply logical tests again after running preconditions. (Try this: "put rusty key on keyring" - it'll pick up the key for the 'held' precondition, which will put the key on the keyring, but then the explicit put-on-keyring command will succeed, which it shouldn't.)
    Generalize the showList mechanism to allow for other types of hierarchies besides contents to be shown. Use the lister interface to virtualize the hierarchy traversal.
    Use the generic showList mechanism for the full score lister, rather than using a specialized lister that does essentially the same thing.
    Score change notifications: add daemon that senses score changes and notifies between turns.
    Do not count the time taken for implicit commands.
    Add a "message generation context" that determines what is generating messages. This object must be sensible to the player character for messages to be generated. For convenience, define a callWithMessageContext function that establishes the context, invokes a callback, then restores the enclosing context.

    When establishing a new context, we check to see if the generator is in scope in the given sense. If not, we hide messages; if so, we show messages.

    A nested context should actually be considered a context switch, because we don't want an enclosing hiding context to hide an enclosed showing context. For example, if we are generating messages with sight scope for Lloyd, and we have a nested context where Lloyd is saying something and so we now are in hearing scope for Lloyd, we want the nested context to show its messages if in fact Lloyd is in hearing scope even if Lloyd is not in sight scope (maybe the player character and Lloyd are in contact over a walkie-talkie).

    NPC messages: only show if NPC is in sight of PC (using message generation context)
    Use message generation context for fuses and daemons. Allow events to be created with a context object and sense object; whenever an event is executed, establish the context for the duration of the event's execution.
    try this: bob, n. z. s. / n. z

    We should see bob leaving on his third queued move, but we don't for some reason.

    Do we really want to remove the objects directly involved in a command from the beforeAction and afterAction processing? It seems like it's an unnecessary inconsistency.
    Should consider directing report messages to the pc/npc message objects. This would more readily allow separate messages for reporting the results of pc and npc commands (for example, "Taken" vs "Bob takes the book"). This is probably trivial, since we already have the pc/npc message generator division anyway; we probably just need to use it rather than verbMessages for generating report messages. (If there's really a good reason to have a separate verbMessage object, maybe we should have a per-actor verbMessages object, the way we do with the default library messages generator.)
    Missing blank line between commands on parser failures:

    >bob, n. get silver coin.
    >n
    
    In room descriptions, list actors separately.
    Don't let the room lister include actor inventory listings with the main room contents listing.
    Footnote system
    Add an initExamineDesc, to parallel initDesc when an object is in its initial position and is explicitly examined.
    Check changes in object bulks for effects on containers. If a container doesn't allow a change in child bulk, fail the command that attempted the change. (For example, in a stretchy bag, putting a new object into the bag should be disallowed if doing so would make expand the bag so much that the bag would be too large for its own container.)
    Stretchy bags, that conform to their contents (in the sense that the bulk changes according to its contents)
    Use normal command result reports (especially ReportFailure) in precondition checks.
    For target of "put in", elevate likelihood for an item being held.
    implement save/restore
    implement restart
    implement undo
    For undoing commands to NPC's, show the NPC as part of the command being undone: "undoing one command: bob, go north".
    Add '\{' and '\}' (push/pop formatter state) output sequences.
    Implicit commands should not set antecedents.
    "Take All" should include the non-fixed contents of fixed containers and surfaces within the room.
    Status line
    Footnotes: add FOOTNOTE MEDIUM mode, which shows new footnote references but hides references to footnotes that have already been read. This helps cut down on unnecessary clutter, since a footnote reference is unlikely to be of much interest once it's been read, assuming that footnotes are always extra information that isn't required to finish the game.
    Weird:

    >get large red ball, small red ball and drop them
    [works]
    
    >get asdf red ball, small red ball and drop them
    Don't know the word 'asdf'
    
    >oops large
    ...you see no drop them here
    

    Why does it pick out the right interpretation normally but can't on a token reparse?

    Add an "unobvious" verification mode that allows the command but will not be accepted as a default.
    Decorations
    Add dobjCheck, iobjCheck, dobjGen, iobjGen equivalents
    Add a default mechanism for listing the inventory of an actor as part of the actor's description. ("Bob is carrying...wearing...").
    Add a sorter method to the list group object, for simple control over the sorting order
    Add input font switch when reading a command
    When defaulting a direct object of a two-object command, include the verb's participle form in the default message:

    >ask about watch
    (asking Bob)
    

    Most default messages are meant to be tagged onto the end of the command line, but when we're adding a direct object to a two-object verb, the added object goes in the middle of the command and hence the default phrase doesn't sound right when tagged onto the end of the command line. By adding the participle form of the verb, we make it clear that the default message stands alone as a new sentence (or sentence fragment, anyway) and is not meant as a missing continuation of the original command text.

    "show iron key" - since the missing phrase form of this command has badness, this ends up being interpreted as "show key to iron", which is silly. For command forms like this with two objects and no preposition, we should probably add a non-badness single-object grammar that asks for the missing prepositional phrase. How we select the misisng-object phrasing over the two-object interpretation is unclear, though.
    Parameterize the amount of time it takes for an actor to give an order to another actor.
    try 'inflate raft and put it in bottle' - we don't get a blank line before the 'put it in bottle' response, presumably because it has an implicit command first
    Floor/ceiling/walls for indoor rooms, ground/sky for outdoor rooms
    Traverse the sense path for throwing an object at another object, and make the projectile land in the appropriate place when an intervening object prevents the throw from finishing.
    Keep track of multiple logical results, and make comparisons based on the whole list. Distinguish different reasons (using 'key' values) for different logical results with the same ranking.

    The idea is that if we have two objects that are both ranked the same way on one axis but are distinguishable on another axis, we want to consider the axis on which they're distinguishable. For example, if we type "put silver coin in jar," and one jar is open and the other is closed, we want to pick the one that's open. At the same time, they'll both be ranked as equivalently logical on the basis that neither is held by the actor. We don't want the being-held status to override the open status, but we likewise don't want the open status to override the being-held status if both are open but only one is being held. The solution is to ignore the equivalent being-held status when both objects have this same value, and to ignore the equivalent open status when both have the same value, but still consider these attributes when they differ.

    Provide suitable default implementations for about 1.0e+6 verbs
    "re-route" syntax - see, for example, keyring.iobjFor(AttachTo). Use asDobjFor and asIobjFor:

       iobjFor(AttachTo) asIobjFor(PutOn)
    
    For travel: actorStanding pre-condition that automatically extricates the actor from chairs and the like. We must catch dangerous conditions in the verifier.
    For missing iobj's, allow preposition in response:

    >dig in dirt
    What do you want to dig in it with?
    
    >with shovel
    
    Type/enter string/number on object
    Type/turn to unquoted string?
    In a two-object action, when assuming one object or the other, and then asking for the other, we should show the defaulted one before prompting:

    >ask
    (Bob)
    What do you want to ask him about?
    
    For disambiguation, never pick a 'not obvious' over an illogical, because a 'not obvious' essentially counts as an illogical for the purposes of picking objects.
    It might be good to eliminate the redundant default announcement in cases like this:

    >ask
    (asking Bob)
    What do you want to ask him about?
    
    >watch
    (asking Bob)
    
    It might be good to eliminate the participle when asking for a direct object of a two-object verb and the indirect object is not yet known:

    >ask
    (asking Bob)
    

    It would be better as simply

    >ask
    (Bob)
    
    Turn x to string/number
    Dials (turn, turn to)
    Buttons (push)
    Switches (turn on, turn off)
    Levers (pull, push, move)
    Doors
    In the dark: should be able to list items you're holding by touch.
    In the dark, 'look at' should indicate that it's the darkness that prevents an in-scope item from being inspected.
    In the dark, inventory should do something better than say "you're empty-handed".
    Climbables (such as stairs). These should probably just be travel connectors that go up and down.
    Check scope for preconditions - make sure we don't apply a command to an object not in scope by way of a precondition implicit command. For example, if you type "go north" in the dark, and there's a closed door that you can't see, you shouldn't be able to open the door by way of the travel precondition.
    >go through passage
    >again
    

    This somehow keeps the old passage in scope. Must need to check scope again for 'again'.

    (The problem is that the last action is hanging on to its cached resolver scope lists. We simply need to drop the old resolvers so that we start the new command with new resolvers.)

    climb up/climb down verbs
    make sure we have makeOpen, makeClosed, makeLocked, makeUnlocked, etc. - in other words, encapsulate these types of state changes the same way moveInto encapsulates containment changes
    Add an "inaccessible" verification failure - this would be more disapproving than any "illogical" level and would be used for "it's too dark" and "you can't reach that" types of failures.
    The pre-condition execution sequence should probably be like this:

      for (pass = 1 ; pass <= 2 ; ++pass)
        run all verifiers
        for each precondition
          run this precondition, allowing implicit commands on pass 1 ONLY
        if no implicit commands were executed
          break
    

    This would ensure that side effects of an implicit command are re-tested through previous preconditions. Each precondition would only be allowed to run its implicit command on pass 1 because this would prevent infinite loops from conflicting preconditions - on pass 2, if the condition isn't met by now, we'll assume it's not going to be, so we'll simply fail the command.

    "actor, give x to me" should be handled as though it were "ask actor for x", via a replacement command.
    actors: shouldn't allow taking possessions in general
    Dark rooms: limit travel to adjacent lit locations?
    Explicitly inherit base class vocabWords_ in initializeVocab() in us_eng.t.
    touch/feel verbs
    Add verb "listen" (both transitive and intransitive)
    Add verb "smell" (transitive and intransitive)
    Enterables
    keys and doors: add options for boosting likelihood for keys:
    • on the first successful use of a key, automatically boost the likelihood for future disambiguation using the key if an option is set (this might be the default option)
    • if the author wants the player character to know from the outset which key goes with a door, the author can add the key to the lockable's knownKeyList - this can be useful for games with obvious key associations or key associations known from the outset to the player character (for example, if the PC has a key to their own house, the ought to know what the key opens)
    locks and keys
    lockable doors
    Automatic key rings for unlocking doors - an "unlock" command with no indirect object automatically searches for a keyring in the actor's inventory, and automatically searches for a key on the keyring.
    keyring: need to implicitly take keyring if not held when trying it
    keyrings: message on success should be something like:

    (trying each key on the keyring, you find that the iron key unlocks the door)

    keyring search: when we already know the correct key, we should just default it rather than search the keyring for it
    implicit commands that use 'replace' should inherit original implicit status (try 's' from the stair bottom - we get an "unlocked" report for the replaced 'unlock door' command)
    For Fixed, indirect the illogical messages through properties, to allow easier customization.
    Whenever any message appears before a room description in a command sequence, show a blank line before the room description. This applies especially to travel, but is generally desirable for any command that shows a room description.
    Put iron key in duffel; unlock iron door - won't default the iron key, because the keyring takes precedence by virtue of being held.

    The problem is that the objHeld precondition is applying a likelihood of 80 because the iron key isn't held, and the held keys are getting 100's. Maybe we need to reduce the likelihood of any other keys or keyrings when the known key is present, but this seems like a poor special-case hack for a problem that is likely to arise in other contexts.

    To solve this, add a 'priority' to logical results. Use low priority for precondition rankings, because these are inherently of lower importance than unique attributes of the objects themselves.

    Add an 'exit only passage' to be used as the receiving end of things like trap doors, chutes, and so on.
    Message travel connectors: simple connector that shows a message as the connector is traversed.
    preserve the case of tokens in literal phrases
    Light/dark changes should be noted at the end of each turn.
    Light/dark changes should be noted before/after each daemon is dispatched.
    >move me to
    What do you want to move it to?
    

    Could we substitute "yourself" for "it" somehow?

    Trap doors - in particular, the arrival message should indicate that the trap door automatically slams shut after the actor arrives.
    Possessives - "take his box", "take bob's box"
    possessives - problem with disambig:

    >get key's jar
    Which key's do you mean...
    

    That should just be 'which key do you mean', not 'which KEY'S'

    possessives - plurals
    possessives - five of bob's keys
    possessives - bob's keys except the green one
    get all of bob's keys except the rusty and iron ones - even when this is just one key, we should announce it as though it were a multiple object (so we need to set some flag)
    possessives:

    >bob's
    unknown word ''s'
    
    possessives - problem with disambig:

    >get key
    Which key do you mean...
    
    >get bob's key
    The word ''s' is not necessary...
    

    The problem would seem to be that we don't handle 's in disambig responses and treat it as an unknown word.

    >look under me
    You see nothing unusual under you.
    

    We should make that second 'you' into 'yourself' instead. In general, can we change an objective case use of an object already used in the nominative should to a reflexive instead?

    possessives - disambig response should allow 'bob's' as a choice, even if it's not offered
    possessives - "get keys except bob's" - need special grammar for just a possessive in this context
    >bob, x key
    Which key?
    
    >quit
    This command cannot be directed to another character...
    
    Distant items
    Finish topic-verb implementation
    Consider:

    >lock door
    (first closing the iron door)
    (with the iron key)
    Locked.
    

    The defaulted indirect object is misleading. This should instead be:

    >lock door
    (with the iron key)
    (first closing the iron door)
    Locked.
    

    The problem comes from the fact that "Lock" has an objClosed precondition for keyed lockable; "Lock" doesn't need this precondition, because "LockWith" has it. Removing the precondition will get the ordering right.

    truncated adjectives seem worse than unknowns in ranking:

    >type hello on typewri
    
    do not filter the 'all' list with hideFromAll when looking for default objects
    >get iron key
    You take the key and attach it to the keyring
    
    >get keys
    brass key: You take and attach...
    rusty key: You take and attach...
    iron key: Taken.
    

    It's irritating that the last one is taken off the keyring. Should we leave items out of plurals when we have multiple logical items of different logicalness? (This isn't a completely academic problem, since you could in practice have a couple of new keys in a room that you want to pick up, without detaching keys you already have on the keyring. One easy way to solve this would be to use a different command for detaching from the keyring - require 'take key off keyring', and say Illogical('if you want to take the key off the keyring, say so'); but this could be irritating when you just want to take the key off the keyring.

    follow: should replace command with actual command used for last observed travel; alternatively, reproduce the preconditions and verification for the travel via the connector
    Player character vocabulary - either add switchPlayer to fiddle with the dictionary, or have some other way to route 'me' and 'my' to the current player character (using matchName, for example).
    Move travel preconditions into the connectors after all. Just create an openDoor precondition type that is instantiated with a specific door to open, rather than attaching it to a command object.
    send bob into dark. follow bob into dark, then:

    >follow bob
    Bob is right here.
    
    >bob, u
    You see no bob here.
    
    >bob, n
    >g
    

    We're not checking to see if the actor is still in scope.

    >n. open door.
    >close door and sit down
    
    --> endless loop - weird problem with token indices for 'sit down' part of grammar tree. The token indices for the second predicate are for the entire command, strangely, so we think we need to parse the entire phrase again and again.

    This has something to do with the empty dobj noun phrase. Same thing happens with 'close door and open' (which also has an empty dobj), but not with 'close door and look' (which takes no dobj) or 'close door and sit down on floor' (which has a non-empty dobj).

    when changing sensory contexts, don't flush output; instead, consider sensory context when queueing reports, so that we simply don't queue a report unless it's visible in the sense context
    when a door closes from the other side, show a message about it
    Status line messages for nested rooms, sitting, lying
    Standing needs to be able to specify what you're standing on in some cases, such as platforms. Is it only the floor that won't?
    when standing, make sure we move actor to room
    when sitting/standing, make sure we do all the necessary work of travelTo
    Chairs, beds (make Room not Fixed? or make a deeper base class for BaseRoom, with Room: Fixed, Room?)
    sit/lie on floor: actor doesn't actually need to stand first, if the actor is already sitting/lying in the room. In other words, if we're just changing from sitting to lying or vice versa, and not changing containers, we don't need an intermediate standing state. We need something like standing-or-in-obj instead.
    sit/lie/stand on - when checking that we can enter a nested room, make sure we're in the immediate container of the nested room
    sit/lie: need to configure prep for 'on' vs 'in' (since we 'sit on' some things but 'sit in' other things, and likewise for 'lie on' vs 'lie in')
    sit on floor, then sit on chair -> no intermediate 'stand'. This might not be important, since it won't involve actual movement, but it's a little weird, and it would be nice to make it consistent with the real nested rooms.
    'sit on floor' doesn't work if run from a doubly-nested room (or deeper)
    sit on armchair, then stand on dais: "already standing on the dais". Need to deal with an implied command that effects the change required of the main command.
    Might want a precondition for travel more general than 'standing', to allow for standing in vehicles and on platforms
    list actors in a nested room when describing the room
    maximum seating capacity for a chair
    in box: try 'e': "you can't do that from the main platform".
    in box: in the dark, should probably have the enclosing room in scope
    in box: when closed and we have light, definitely should have the enclosing room in scope
    in box: should not be able to try to get out of box when we can't sense beyond the box - it should serve as top-level location for the purposes of the implied actions in this case
    When in box, and box is closed, list contents of box as top-level contents of room.
    Listing room contents should show contents of fixed items to any depth. The first-level contents of anything not listed should by default be listed.
    Vehicles
    traveling - don't use actorStanding, but use travelerReady instead. If the traveler is an actor this turns into actorStanding (or whatever is appropriate for the actor); for a vehicle it's probably nothing, but could be used to check for things like the doors being closed on a car or seatbelts being fastened.
    Vehicles: when PC is riding, we get no description of the new room
    Vehicles: put tricycle on platform; get on tricycle; ride tricycle: we don't seem to move the tricycle off the platform first.
    Vehicles: departing should use definite article
    Vehicles: departing doesn't list riders (presumably they're out of scope by the time the departure message is generated; need to save sense info or something)
    sample game: implement 'ride tricycle'
    sample game: when getting on tricycle, mention how to ride it
    Put x on floor: if we're in a nested room with a drop location that isn't the main room, we'll perform a "drop" in the nested room. Should actually move to outermost room if possible. (Maybe we need to leave the nested room as a precondition.)
    Vehicles: need a convenient way to put up vehicle barriers, so that you can walk through a door but not ride through, and vice versa
    'follow me' mode for an actor
    recognize (in input) reflexive pronouns referring to target actor (bob, examine yourself)
    Allow things to be placed out of reach from within nested rooms (canReachFromRoom).
    change from LangThing to 'modify Thing', etc.
    change "bob, tell me about x" into "me, ask bob about x"
    'push/move dobj to iobj' - for things like building staircases ('push crate to window'). Synonyms might include 'push/move/put dobj under iobj'.
    Fix follow problem: "bill cannot stand in closed box" is reported even though we're out of range of the sense.
    Allow some types of nested rooms (platforms, booths) to be their own follow locations, so we can follow an actor into such a room
    Might want to keep pronoun antecedents per actor. When an issuer gives a command to a target, the target should copy all of the antecedents from the issuer.
    for 'in' and 'out', by default ask for a direct object if there's no evident travel connection for the direction
    remove all preconditions from noTravel, as we know travel will not occur and hence don't need to meet any conditions
    'push obj north' (and other directions)
    for "follow", track actors in vehicles as well as the vehicles themselves
    'draggable' class, implementing a general framework for pushing objects from one room to another
    add a push-travel barrier that works like the vehicle barrier, but for traveling while pushing objects
    Change the barrier mechanism so that barriers are listed on the connector, rather than being part of the connector. This is needed because the connector is in many cases directly addressable - putting the barrier in front of the connector as a proxy connector doesn't work when you can reach the connector directly, making an end run around the barrier.
    Change the listing mechanism so that showList is a method of the ShowListInterface class, to simplify argument lists.
    Handle nested grouping. Use this for equivalents in groups.
    For group listings, don't add a group when there's only one group due to equivalence. We currently say this, which is awkward: "the jar contains two coins (two copper coins)."
    Equivalent listings - eliminate as a separate special case and simply list equivalents using a listing group. Add an Equivalent Group which implements a simple listing group for equivalents; by default, the group simply shows the count.
    For our silver/gold/copper coins in the sample game, make sure the grouping mechanism can list like this: "eight coins (three silver and five gold)". Note that the names in the parens leave off "coins" and just list the number and adjective.
    Move all of the lister objects into the messages module, and eliminate the secondary indirection through the libMessages object. Simply make the lister itself part of the messages module, and put the messages directly in the lister.
    Get rid of the unnecessary list flags LIST_WORN and LIST_INVENTORY, replacing them with parameterization at the listing method level instead.
    Use the generic list mechanism to list actors in a room. By default, each actor simply lists separately, but it should be possible to group actors using the normal listing group mechanism ("Bob and Bill are at their desks", for example).
    Can we make the disambig list use the normal listing mechanism?
    Add a "specialDesc", similar to initDesc but for more general purposes. specialDesc should turn into initDesc when initDesc is applicable.
    add "the former" and "the latter" as disambiguation responses
    For nested rooms, break out the "staging location" test from checkActorReadyToEnterNestedRoom into a separate routine. Use a list of possible staging areas given by a separate property for easier overriding.
    For nested rooms, use a separate "exit destination" property for the destination of a GetOutOf action, rather than assuming that we always move to the enclosing location.
    Add a "high nested room" that can't be entered except from particular staging areas due to height. These is a fairly trivial specialization: first, don't solve the problem using implicit commands unless the actor has already solved it before; second, the default failure message is something like "you can't enter that from here; it's too high up."
    Rename the '*predicate*' production classes to '*command*'
    Base all command (formerly predicate) classes on a common CommandProd base class
    Add a resolveAction method to the command classes. This method retrieves the action from the command. In English, this simply retrieves the 'predicate' match tree object, which is always based on action; other languages, especially those that use case markers rather than word order to encode phrase-role information, can customize this behavior appropriately for their grammatical systems.
    When scanning command parse matches, we can immediately eliminate any match for which resolveAction fails to find a valid action.
    Use separate listers for room/object contents lists and inventory lists. Use separate show-list-item methods for inventory items.
    For keyrings, show contents of keyring in room/object/inventory contents as a sublist: a keyring (attached to which are an iron key and a bronze key)...
    Allow queuing pending actions for an actor, not just token lists. Make it possible to insert a pending action at the beginning of the queue (for continuation actions).
    Use a separate listing flag (isListedInInventory) for inventory list inclusion. For actors, make this true by default, even though actors are not listed in room descriptions.
    Dispenser and Dispensable, for matchbooks and the like
    Collective objects for cases like "book of matches" and "matches" - when we type "take matches," we should interpret this as the book of matches (i.e., "matches" == singular) rather than as the individual matches ("matches" == plural).
    Matches - flammable objects that are self-igniting
    Matchbooks
    Include a checksum in saved state files, and check at start of load. This will help avoid attempting to restore a file that was corrupted.
    Add a tentative pre-resolution pass, for the later-resolved object of a two-object action, that runs before the resolution of the earlier-resolved object. Don't perform full resolution on this pass; specifically, don't count any problems against the ranking results, and don't ask for help interactively.

    Make use of this information in 'verify' routines for the earlier-resolved objects when applicable. For example, for "take from ", we resolve the indirect object first, so we don't know the final dobj resolution when resolving the iobj; so, use the tentative information so that we can rule an iobj illogical if there's no tentative dobj inside the proposed iobj.

    Might want to clean up the other-object business in askMissingObject and related routines. This scheme doesn't feel properly generalized. For that matter, we might want to rethink the whole scheme so that it's subclassed by and handled in the action classes, rather than parameterized, since the parameterization scheme seems hokey and brittle.
    Remove the otherObjectList and otherObjectWhich stuff when we've cleaned up the corresponding code. This information should now be available via the tentative resolution lists instead.
    When lighting a match as part of an implied command, reduce the burn time by one turn - this ensures that lighting the match as part of another action won't artificially extend the match's burn time.
    candles, torches, oil lanterns - flammable objects that must be lit with another item, such as a match
    defaults: when there are two or more equivalent items that could be used as defaults, pick one arbitrarily
    for implied transfers into a bag of holding, put the indirect object of "take from" last in the affinity list
    for implied transfers into a bag of holding, other things being equal, transfer the least recently acquired object first
    food items
    Change the logic of checkActorInStagingLocation so that it doesn't assume that we can reach a staging location just because we're indirectly in the location. Instead, add a "choose staging location" method so that rooms can override the staging location chooser with appropriate special-case code when needed.
    Add issuing actor and target actor parameters to resolveAction(), so that the resolver can tell what reflexive pronoun phrases mean. (In many languages, reflexive constructions are structurally part of some predicates, and affect the meaning of the predicate, so the identity of the speaker and of the subject must sometimes be known to correctly determine the interpretation of the predicate structure.)
    sample game: reject just plain "match" in the matchbook's matchName
    touch scope: include contents of matchbooks and keyrings, so that these can be used even in the dark
    Add an associated odor and sound property to each Thing. This points to an object that encapsulates the object's sensory emanations. Always add these objects to scope when the associated objects are in scope, with the same properties.

    Make "listen to x" and "smell x" defer to the associated objects.

    For scoping, a sound or smell doesn't have to place the main object in scope, but rather can just put the intangible sensory emanation in scope. For example, if an alarm clock is buzzing, the 'buzzing sound' object would be in scope but not the alarm clock itself. (On the other hand, some sounds, like a ringing phone, might be sufficiently distinctive as to place the main object in scope. But for those cases we can simply associate the sound/smell with the main object.)

    We need different odor/sound descriptions for different situations:

    >listen to phone
    It's ringing.
    ==> phone.soundDesc
    
    >listen to ring [phone visible]
    It's coming from the phone.
    ==> sound.soundWithSource
    
    >listen to ring [phone not visible]
    It sounds like a phone.
    ==> sound.soundWithoutSource
    
    >look [phone visible]
    ...
    The phone is ringing.
    ==> sound.soundHereWithSource
    
    >look [phone not visible]
    ...
    You can hear what sounds like a phone ringing.
    ==> sound.soundHereWithoutSource
    
    Don't open a footnote when the PC isn't seeing the text with the footnote reference.
    Add sense emanations to inventory displays.
    For sound/smell objects, add options to control ongoing announcements of sensory emanations:
    • always show on room description, or only show on explicit "look" descriptions (and corresponding sense commands - "smell", "listen")
    • show every n clock ticks
    • show every n clock ticks for m iterations, then go to a secondary message every p clock ticks (for something like "the phone is still ringing").
    Room descriptions should be differentiated according to whether we're explicitly looking or merely entering a new room.
    When a noise's or odor's source is not visible, and the object is examined (via "examine", or via "listen to" or "smell" or whatever), it might be desirable in many cases to show the apparent source, which is to say the visually opaque obstructor, and describe the sound/odor as coming from inside/outside/behind/whatever the obstructor.

    The obstructor can be easily found with gActor.findOpaqueObstructor(sight, obj) (where 'obj' is the noise or odor object). Once the obstructor is found, we'll have to generate an appropriate message, which will require a new method parallel to obs.cannotReachObject(obj) - perhaps we could call it obs.describeSoundObstructor(obj) etc - which would display "The ringing seems to be coming from inside the box" and the like.

    This entire mechanism would not be used when the obstructor itself cannot be seen, such as in the dark.

    Consider:

    >look
    ...
    You can hear what sounds like a phone ringing.
    
    >listen to ring
    It sounds like a phone.
    
    >x phone
    You don't see any phone.
    

    It would be better if that last line were:

    >x phone
    You can hear a phone ringing, but you can't see it.
    

    To deal with this, adjust the touchObj and objVisible preconditions so that it provides better feedback for an object that can be heard but not seen.

    Write a common main() that most games will use, with appropriate hooks for showing the introductory text and so on. Or, perhaps better, write a standard initialization routine that the game's main() can call.
    Provide an author-configurable option to process multiple orders to NPC's synchronously - so, if you say "bob, go north, get all, go south", the PC doesn't get a command line prompt again until Bob finishes the whole set of commands.
    It might be worth considering making the disambiguation responses for &noMatchDisambig and &disambigOrdinalOutOfRange more interactive. Currently, if you give an invalid response to a disambig question, you get an error and no chance to retry. Something like this might be more intuitive:

    >take ball
    Which ball, the red ball, or the green ball?
    
    >third
    There weren't that many choices - did you mean the red ball, or the
    green ball?
    
    >orange
    That wasn't one of the choices - did you mean the red ball, or the
    green ball?
    

    However, if the response looks like a valid response but doesn't give us a match, and it also looks syntactically like a valid new command, treat it as a new command.

    Generic script objects: an object for which we call a method automatically each turn it's active. We'd keep a counter that we'd automatically advance on each call. We should probably use this generic object to implement text lists.
    Generic text lists: an object that encapsulates a list of messages to display, one per turn.

    Text lists should be linkable to a common counter object, so that the lists are synchronized on the same counter. This lets you have separate lists for each room, but keep the timeline in each list the same.

    It should be possible to associate a text list object with a room instead of coding the room's text list in-line in the room.

    Room text lists: a list of atmosphere messages we cycle through while the player is in the room. This should be totally automatic, so you just program the list and the library automatically sets up a daemon to run through the messages.
    When describing an object, show the special descriptions for any contents of the object, just as we would list them in room descriptions. This lets us see the special descriptions for contents, without having to write any additional descriptive code in the container.
    consider:

    >take coin
    Which coin, silver or gold?
    

    We should probably keep the copper one in the list. In particular, we probably shouldn't remove an item from consideration for disambiguation just because it's less likely - once we've determined that the noun phrase is ambiguous, we should offer all logical matches, even when they're less likely.

    consider:

    >take coinc
    Which coin...?
    
    >take tricyc
    The word 'tricyc' is not necessary in this story.
    
    >take tricyc
    Taken.
    

    For some reason, truncated words are treated as misspellings when they appear in disambiguation responses.

    Can we cache sense information to speed up resolution a bit? In particular, we should be able to cache sense information during the noun phrase resolution and verification steps, since these do not normally involve any game state changes; once execution begins, though, caching should be turned off. (This is probably the optimal set of trade-offs: on the one hand, the game and library should never have to worry about cache invalidation - the library only needs to turn caching on before starting the parsing phase and then turn caching off before beginning the execution phase of a command; on the other hand, we should derive substantial efficiency from caching during the parsing phase, because this phase in particular tends to evaluate a lot of sense information.

    (Experimental code has been added, conditionally compiled based on the SENSE_CACHE macro. We'll leave it on for the time being to get some experience with it to see how well it works.)

    Preparsing
    Just-out-of-reach containers. Create a container type that puts its contents in the distance for 'touch' purposes. This can be used for things like items on the ceiling. We'll need a way to make this vary depending on the source object, so that, for example, something could be out of reach for an actor standing in the room but in reach if the actor stands on a desk.
    When using 'again' with a command that had an interactive disambiguation response, the response 'that wasn't one of the choices' is not appropriate since the player doesn't get to respond.
    cache touch paths (and canTouch information) the same way we cache sense paths
    For OutOfReach containers, we shouldn't be able to touch the container itself, since not only the contents but the container itself is meant to be out of reach. To do this, add a PathTo traversal for the last item in a path (and while we're at it, add the symmetrical PathFrom for the first item in the path), and in OutOfReach's checkTouchViaPath, treat PathTo the same as PathIn.
    'down' from a platform should be 'get off platform' by default
    add 'debug' verb (to break into the debugger)
    Real-time events: on save/restore, adjust system clock basis to keep everything in sync
    Real-time events: design a class for the events
    Real-time events: integrate with the scheduler and command reader
    Real-time events: when an interruption occurs, catch any output and automatically cancel the interrupted input
    Fix problem with quoted strings in literal phrases (as in 'type "hello" on typewriter')
    Fix problem with 'throw': it's illogical to throw something at an object within the object being thrown
    Change all of the exclusion list phrases to terminalNounPhrase rules - otherwise, when they exclude everything, they get misinterpreted as ((all but x and y) and z) which turns into (z and z). Everything with a 'but' should turn into a terminalNounPhrase, just like the plain 'all but <list>' rule.
    Make 'me' symmetric with 'yourself' in the grammar. In particular, 'me' should refer to the issuing actor, which is not necessarily the player character.
    fix problem with "yourself, jump"
    For chairs and the like, we might want to consider an object owned by an actor if the actor is occupying the object (so, "bob's chair" is the chair bob is sitting on).
    When taking an item, reduce the likelihood that an item being carried by another actor is the one being taken.
    Provide a way of distinguishing lit and unlit equivalents in a list. Likewise for worn and unworn objects. Should probably add another list group object after the equivalent grouper, and in this object we should provide one group for lit objects and another for unlit. The desired output is something like this:

      You are carrying three matches (one lit), two flashlights (one
      providing light).
    
    Move the inheritNext into the behavior as the default native 'inherit' instruction. Remove inheritNext from library source files.
    the dagger shouldn't show up in the 'x desk' description when the dagger's initial message is still being shown
    Add macros to make grammar rules for predicates easier to read.
    Improve the module names (adv3g -> parser.t, adv3v -> verbs.t, etc)
    provide .t3m, .tdc for sample game with proper directory set-up
    accept 'all <adj>' and the like
    build the #include file list in Workbench automatically when creating a project, and when explicitly asked via a new command "scan source files for #include"
    compiler: warn on finding a backslash outside quoted text in front of anything but a newline
    Add a simpler, more structured way to add a single paragraph break. Use a new pseudo-tag, "<.p>", to indicate a paragraph break; process this tag just before writing output to the console.
    All library input functions should coordinate with command reports to turn off output capturing.
    Add a finishGame() function offering restart/restore/undo/quit options
    Build a proper set of Workbench sample games
    Freeze the real-time clock while we're waiting for inputFile results, while we're saving/restoring a file, and while we're waiting for an inputLine when real-time events are not allowed. All of these should be considered to be outside the normal real-time flow of the game, so none of them should consume any game real-time.
    In showList: mix groups and singles in the original order of lst, rather than displaying groups and then singles separately. This is important because we're not preserving the original ordering when the list is sorted and it has subgroups. We can't guarantee that we'll preserve the sorting order, of course, since grouping could put together items that weren't consecutive in the sorting order, so let grouping prevail when there's a conflict; but when possible, keep the order the same. To do this, we should have a single loop that traverses the original lst; for each item, if it's a single, show the single, otherwise show its group. Only show the group for the first item in the group; once we match a group, mark the group as used so we don't show it again.
    '+' should consistently concatenate individual items from a collection on the right-hand side, whether the right-hand side is a list, vector, or array
    '+' and '-' should work with an Array as the left-hand side
    Delete class Array. We'll have to change the function object stuff to use Vector instead of Array.
    Make Vector+val and Vector-val return new objects - do not modify the original vector in place
    add Vector.appendAll() - appends list elements in-place; maybe Vector.removeValue() and Vector.removeAllValues() to do '-' in-place
    LookupTable.forEach: remove the 'key' argument to make its interface the same as everything else's.
    Everything with a forEach: add a forEachAssoc(key, val) method alongside forEach.
    for Openable, if the object isn't transparent looking in, implicitly open the object for "look in"
    Iterators: add getCurVal and getCurKey methods.
    Fix bug: 'listen' or 'smell' shows no response when there are things that nominally have Noise/Odor associations, but none of the Noise/Odor objects actually has anything to say at the moment.
    Dictionary: add a way to iterate over the entries in the dictionary.
    For inputManager.inputLineBegin and inputLineEnd, use a new style tag "<.inputline>" rather than coding "" directly. This makes it easier for a game to customize the input font setting.
    Add access to the "restore code" (the second argument passed to restoreGame()) for PostRestoreObject instances by adding the value as a class property of PostRestoreObject.
    Change the Thing template that ends with "desc" @location to end instead with @location "desc" (i.e., reverse the desc and location property order). Since "desc" can be lengthy, putting the location first will make the template more readable.
    Add before/after command separation messages in libMessages, to allow for finer-grained control over the formatting of command responses.
    Check dark travel before checking any barriers - move the call to gActor.checkDarkTravel into the TravelConnector methods that call gActor.travelTo, since this must be called as a separate phase before the travel connectors do any of their other work.
    Make Illogical() the most illogical ranking, and make IllogicalNow() slightly less illogical (i.e., invert the old ordering of Illogical and IllogicalNow).
    Add Action.setMessageParam(), to allow message processors to set their own special message parameters that they can use in expansion text. This would be better than constructing messages partially with the "{it obj/him}" mechanism and brute-force string concatenation, because it would allow the concatenated bits to participate in the same processing steps, such as reflexive pronoun conversions.

    Add a macro (gMessageParams) to simplify the syntax for adding new message parameters.

    "x me" doesn't work in dark (target actor of a command should always be in scope)
    Remove 'actorResolved' check from execCommandTokens - this check is vestigial (it stopped being necessary when we started pulling out the actor phrase after resolution and re-parsing the rest of the command), and makes it impossible to have multiple levels of indirection, as in "tell bob to tell bill to go north".
    Refactor FirstCommandProdWithActor (parser.t) to move the actor resolution stuff into a separate CommandProdWithActor that FCPWA inherits from.
    >bob, get ball
    Which ball?
    
    >z
    Bob waits...
    

    The "z" should have a default target of the player character, not Bob.

    Allow things like "tell bill to tell bob to go north". In particular, fix execCommandTokens so that it doesn't assume that only one actor can be resolved per command.
    Keep first-on-line information in the PendingCommandInfo. (This requires changing the interfaces to all of the Actor routines that create pending command info objects: addPendingCommand, addFirstPendingCommand, addPendingAction, addFirstPendingAction.)
    Add a noun-list grammar rule for singleNoun, so that if we match a noun list in a place where a single noun is grammatically required, we can generate an appropriate message rather than failing to match grammar.

    Symptoms fixed: "ask bob and bill about box" -> "you see no bob and bill here"; "give coins to bob and bill" -> "you see no coins to here".

    Add a point-of-view argument to BasicLocation.lookAround, so that a room can be described from a point of view other than the actor doing the looking.
    Fix: paragraph break in response to getting out of white box while the box is closed (in the platform room). (The last report - "Okay..." - is displayed on the same line as the beeper noise.)
    Can we make every kind of noun phrase production derive from a common NounPhraseProd base class?
    Allow multiple actors per command line, tads2-style, as in "bob, go north. look. bill, go south." (In tads 2, a new actor can be specified after a period.) However, only allow this in "synchronous" mode, where the issuing actor waits for all commands to NPC's to complete before taking another turn; in asynchronous mode, do not allow mid-command target actor changes, because it's too confusing.
    Fix problem in touchObj precondition: we assume that we can find a valid touch path, so we traverse the path without checking to see if we got a nil path. In some cases (such as when the target object is in a location not sharing any common container with the actor), there is no touch path at all, so we need to deal properly with this possibility.
    Fix visual command separation for 'undo' after a multi-command line, and for a multi-undo command line.
    Modify/replace/delete grammar rules. Add new syntax:

      modify grammar production(tag): : 
         
      ;
      replace grammar production(tag): : 
        
      ;
    

    To make this workable, the "production(tag)" names of grammar match objects must be unique; change the compiler to enforce uniqueness of these names.

    Also, change VerbRule to include a tag: VerbRule(tag). This will allow modify/replace to be used with VerbRule(tag) definitions to replace library verb grammar rules.

    Add a mechanism for remapping a two-noun-phrase action to a different action after resolving first object. For example, allow mapping ATTACK x WITH THROWING STAR to THROW THROWING STAR AT x.

    To do this, add a new pair of properties to TIAction: remapDobjProp and remapIobjProp; define these properties using the same template style as the rest of the properties in DefineTIAction. Add a new remapTIAction() macro that can be used from within a remap() method to remap a command.

    Note that only the first-resolved object can be remapped, because the whole point is to effect the remapping before resolving the second-resolved object, so that the second-resolved object can be resolved using the rules of the new action rather than of the original action.

    In BasicLocation.lookAround, remove the point-of-view object from the list of objects to be described, rather than removing the actor.
    Move the grammar convenience macros (VerbRule, SingleDobj, etc) out of adv3.h and into us_eng.h - these are all specific to the English verb grammar, so they should be in the English-specific definitions rather than the language-independent definitions.
    If a Daemon is created with an interval of zero or less, it'll put the scheduler into an infinite loop invoking the daemon, because the daemon will be permanently schedulable. Force the interval to at least 1 on creation.
    Add a lookAround() method to Thing, to generate a description from the point of view of the Thing. This is necessary to allow things like remote cameras. This method should call the container's lookAroundPov() method (which must also be added to Thing) with the point of view set self; this algorithm will eventually reach the innermost room-like object, which will display the appropriate room-like description.
    Add a PAUSE command, to stop the real-time clock for games that use real-time events.
    Add a grammar notation that specifies a match for one of several vocabulary properties. With the '<prod>' grammar that was being kicked around for a while, the notation '' was appealing, but without the angle brackets, it's not obvious what the equivalent syntax would be.

    Use angle-bracket notation, even though we don't use it for individual tokens: <nounM nounN nounF>

    Add an explicit 'grammar' declaration with no matchable rules, so that production names can be declared without actually creating rules for them.
    make 'inherited' after 'delegated' more consistent with the regular inheritance behavior (in particular, the relatively new dynamic inheritance search algorithm will fail to find a class related to 'self' in the delegatee's superclass tree, so there will be nothing to inherit; ideally, we'd have an 'inheritance target' in the stack frame in addition to 'self')
    NestedRoom.getTravelConnector refers to gActor. The reason the method looks at gActor is that it wants to check if the actor can see the containing room, so that it can get the connector from the containing room. Add an 'actor' parameter to getTravelConnector(), so that we can specify an actor explicitly and thus ask for travel connectors outside of turn execution contexts.
    SouthwestAction is missing from action.t
    noTravelIn, noTravelOut, noTravelDown - these need to override isConnectorApparent to return nil.

    Add redirectTravelIn, etc, which do show isConnectorApparent = true.

    Add newActionObj alongside _newAction: the new function takes a pre-created action instance, rather than creating a new instance itself.
    Add a getBestMatch() method to ResolvedTopic to retrieve the top-ranking object. By default, provide an implementation that simply returns an arbitrary object from the strongest match list. Games and library extensions could customize this to use a different topic picker mechanism as desired, but providing a decent default implementation would help authors get started without having to worry about coding their own topic framework if they didn't want anything specific here.
    Add a PreRestartObject, analogous to PreSaveObject, to allow any special actions to be performed before a restart.
    execCommandTokens should probably switch the sense context upon discovering a target actor different from the issuing actor, before actually starting to execute the command.
    Move all of the output management into discrete OutputStream objects. Associate filters, notifiers, capturers with individual streams. By default, create one stream for the main text and another for the status line.

    NOTE: This change affects all of the notifier, filter, monitor, and capturer function interfaces. All of these formerly global functions are now methods of the OutputStream class.

    Regarding touch path calculation (see e.g. touchObj precondition): should we be finding a touch path to an object in a separate location not connected by containment, but connected by a SenseConnector?
    Fix basicScoreChange reporting for changes of 1 (or -1) points: use singular "point".
    change the T3 Workbench .tdc format to use .t3m files
    vmrun: respond to Ctrl+Break to break into debugger
    "parse-debug" command: accept the command without the "on" or "off" specifier, and simply invert the current mode.
    Add a nounMultiList production alongside nounList. Use this production in matching singleNoun, rather than using the regular nounList, so that we don't create spurious additional matches for the degenerate single noun phrase form of nounList.
    For bags of holding, check (in Container.tryPuttingObjInBag) to make sure that the object being moved into the bag actually fits, and don't even try if it won't. This will avoid spurious attempts and failures to move things in to a bag of holding implicitly after the bag becomes full.
    Refactor the CommandProd-based productions in parser.t to move the methods into new classes, and base the defined grammars on the classes. This will facilitate adding new commandPhrase grammar rules by allowing the existing behavior to be re-used via inheritance.
    Add a new Actor property (revertTargetActorAtEndOfSentence) that, when set, tells the parser to consider target actor designations ("bob, go north...") in effect only until the end of a sentence. When the property is set to true, switch the target actor back to the original issuing actor at the end of each sentence.
    As a convenience, add a new StyleTag subclass, HtmlStyleTag, for tags with different renderings in HTML and plain text modes.
    Add some new style tags:
    • <.a> - for <a> text (and use it for all links we generate)
    • <.statusroom> - for the room name portion of the status line
    • <.statusscore> - for the score/turn count portion of the status line
    Add inputManager methods getKey() and getEvent() that provide covers for inputKey() and inputEvent(), respectively, the integrate with the real-time manager and the reports gatherer in the same manner as inputLine.
    Don't use [badness] to reduce the priority of ordinal lists in disambiguation responses; instead, use the ranking mechanism to give ordinals a lower ranking than noun/adjective interpretations.
    Fix looping problem with answering 'g' to disambiguation queries. (Don't store the response to a disambiguation query until after we actually know that the input is an answer to the query, as opposed to a new command.)
    Do not notify sense path in moveInto() when traveling. (Travel uses the separate TravelConnector mechanism, which doesn't necessarily map directly onto sense connections; we don't want to attempt to notify sense connections of travel because we don't actually use them for travel.)
    When disambiguating interactively, if the response isn't in the full list, check to see if it's in the full scope list. This allows for cases where the player really wants to try a command on something that we decide isn't logical - it's better to let the player try than to deny that the object is among the possible choices.

    We should test the scope list as a second pass rather than allowing the full scope on the first pass. So, we should try first as we do now, limiting scope to the narrowed ("logical") list; then, if that fails, we should try again with the full scope list.

    Add an equivalent of the tads 2 "->" syntax: this syntax allowed routing messages for one object to another object. For example, if we wanted to route "open desk" to "open drawer", we would put "doOpen -> drawer" in the desk. Define a new macro:

      dobjForwardTo(Open, drawer)
    

    This is used in place of a dobjFor().

    (This was from Phil Lewis, who suggested "reroute" as the name and also suggested a slightly more general format: dobjReroute(drawer, Open). It might also be useful to be able to specify the verb to forward separately, so that you could handle a verb with both a different verb and different target object, but the action is probably the same in most cases, so it seems more convenient to be able to omit it.)

    In roomSmellLister (and roomListenLister), add a custom isListed method that checks a new property of the target object, isSmellListedInRoom (isSoundListedInRoom in the case of Noise objects). Set these properties to true by default. Add an isListed override to smellActionLister (and listenActionLister) that simply returns true. This change allows an Odor/Noise to list in response to a >SMELL or >LISTEN without also showing in the normal room description, as it would by default.
    Propagate the results of an action up to callers by returning the CommandReportList. Actually, we'll have to return a list of CommandReportList objects, since one CommandReportList is generated per iteration for an iterated action. The aggregate list could be stored in a CommandResults object, which could provide high-level analysis methods (e.g., isFailure()) to interpret the action results.

    To do this, return a CommandResults object describing the command reports list from newAction(), nestedAction(), and so on.

    Provide a way of specifying a subclass of CommandReportList to use when executing a new or nested command. To do this, callers use a new function that takes a particular CommandReportList subclass to use while calling a callback:

      result = withCommandReportsClass(
          MyCommandReportList, {: nestedActorAction(bob, SitOn, chair) });
    
    In the tokenizer, if a word with a "'s" suffix explicitly appears in the dictionary with the "'s", do NOT make the "'s" into a separate token - keep the whole string with the "'s" as a single token. This allows for cases where the grammar has a literal token with a "'s" suffix, and where vocabulary words (such as adjectives) are explicitly defined with "'s" suffixes.
    In TravelConnector, allow for a single barrier rather than a list of barriers. If the travelBarrier property is not a Collection of some kind, treat it as a single TravelBarrier.
    Add a OneWayRoomConnector to make it easier to define a connector that connects one room to another but not vice versa.f
    Add checkTravelConditions() to TravelConnector. Call this from checkTravelBarriers(). By default, this does nothing; instances can override it to apply special conditions to the travel in lieu of creating a separate TravelBarrier object when the conditions don't need to be re-used in other connectors.
    Fix indentation problem for grouped items in INVENTORY TALL lists.
    Hide intangibles from 'all' by default. Noises and odors should probably be included in 'all' for LISTEN/SMELL.
    When trying to put objects into a bag of holding to make room to take a new object, don't even try moving an object that contains the bag of holding (because doing so will just fail with a message "the is already in that").
    Need a better way to say "the one in the room" than showing the room name, because that's not useful in input. Should say something like "the match on the floor".
    Once everything in a disambiguation list is a basic equivalent, we could use the 'name' of the objects to display the prompt, rather than using the input text.
    In Thing.initializeEquivalent, deal with the possibility that we have a base class with a separate equivalent grouper. Create a separate grouper for the subclass by testing to see if equivalentGrouper is defined directly by the class object. If there is an inherited grouper, we must create our own separate grouper for the subclass, AND we must take the superclass grouper out of the listWith for the subclass (since it will inherit the base class listWith by default).
    In action.t around line 1532, when we're adding the UnclearDisambig flag, we might want to suppress the flag if the object we selected and the objects we rejected are basic equivalents of one another. This avoids weird situations such as

      >light match
      (the match)
    

    when we have one match we're holding and another in the matchbook: we choose the one we're holding over the one in the matchbook because of the must-be-holding precondition to lighting a match, but mentioning which one we're choosing is weird because it doesn't tell us anything.

    >drop match
    Which match, one of your matches, or the match in the matchbook?
    
    >my match
    Which match, one of your matches, or the match in the matchbook?
    

    ...etc. The problem is that we're taking "my match" to mean any match in my possession for parsing purposes, whereas we differentiate holding from indirectly owned. Ideally, we'd prefer to treat "my match" as "the match I'm holding directly" when it's ambiguous.

    Add a disambigName property, and use it instead of the ordinary name when generating disambiguation prompts. (We'll need aDisambigName, theDisambigName, and countDisambigName as well to round out the set.)
    Add "x in y" grammar.
    Add a mechanism for distinguishing equivalents with different states:

      Which candle to you mean, the lit candle or the unlit candle?
    
      -but not-
      Which wax do you mean, the unlit candle or the seal?
      -which should just be
      Which wax do you mean, the candle or the seal?
    

    Use this mechanism to create a base class for light sources. We can use this for things like matches and candles. The disambig name for an object is "lit x" or "unlit x", so we add the adjective "lit" or "unlit" according to state. Check the adjective in parseName.

    Use this same mechanism to specify by owner when owner is a distinguishing factor - "which gas mask do you mean, yours, or teeterwaller's?"

    To do this, associate with each object a list of "distinguisher" objects. A candle might include the "lit" distinguisher object, and probably all objects would include the "owner" distinguisher. We'd make a list of all of the distinguishables in common to the set of equivalents, then run through the common set. For each one, we'd ask the distinguisher if the list of equivalents we have is distinguishable via this distinguisher. The "lit" distinguisher would return true if the objects were all in different "lit" states, and the "owner" distinguisher would return true if all had different owners. We'd stop at the first distinguisher that can tell all of the objects apart. The distinguisher would give us the method to call in each of the objects to list its distinguishing name - the "lit" distinguisher would call the litName property, for example, which would display "the lit candle" or "the unlit candle"; the "owner" distinguisher would display "yours" or "teeterwaller's".

    The response would have to be handled by the normal disambiguation response mechanism, so the objects would have to conspire with the distinguisher to have the proper adjectives. This is easy, though, because the objects are the ones displaying the adjectives in the first place when they show the disambiguation list.

    If we find no distinguisher that can tell all of the items apart, we'd look for one that can tell at least some of the items apart, and phrase it like this: "the lit candle, or one of the unlit candles". If they select one of the ones indistinguishable with this distinguisher, we'd iterate, and maybe pick it up with a separate distinguisher next time.

    If none of the distinguishers can tell any of the objects apart, we'd simply choose one arbitrarily like we do now.

    >take candle
    Which candle do you mean, the lit candle, or one of the unlit candles?
    
    >unlit
    Which do you mean, your candle, or one of Bob's?
    
    >bob's
    (arbitrarily picking one of bob's several unlit candles)
    Taken.
    

    Note that for ownership distinctions, need to offer a way to refer to unowned objects:

    >get candle
    Which candle do you mean, Bob's, or the other one?
    -or- Which candle do you mean, Bob's, or another one?
    
    [responses would include...]
    >another / another one / another gold coin / one of the others 
     / one of the other gold coins / other / the other / the other one
     / the other gold coin / other gold coin
    
    Hide actors from 'all' for all verbs by default.
    For "put in" and "put on", use roughly the same strategy as "take" for generating the "all" list: include only objects that are directly in the actor's location, or in fixed objects in the actor's location, as "take" does, but also include objects that the actor is directly holding. Keep the same iobj/in-iobj exclusions.
    Break up TravelConnector.checkTravelConditions() into two separate methods (canTravelerPass(traveler) and explainTravelBarrier(traveler)), just like TravelBarrier.
    Fix Thing.normalizePath() so that it makes two separate passes over the list. It should apply the PathThrough tranformation first, then the PathPeer transformation as a second pass. (Performing both transformations both on a single pass can cause PathThrough transformations to be missed, because the PathPeer changes the list in such a way that the PathThrough transformation doesn't recognize a needed change. The PathThrough condition could alternatively be modified to recognize PathPeer operations, but it's easier to do things in two passes. Note that doing PathThrough first is safe, because it only adds to the list - it never drops any operations that the PathPeer transformation looks at.)
    Add distance differentiation for 'examine' descriptions (and for 'listen') and 'smell' descriptions as well). Specifically, add distantDesc and obscuredDesc, and call them from basicExamine according to the current point of view. Add corresponding methods for 'listen' and 'smell' descriptions.
    Add automatic state listing to the equivalent grouper. Use a new Thing method, getState, that returns a ThingState object. Use this object to group equivalents by state and to show the state of each item.
    Use the ThingState object to provide the ordinary status description of an object.
    Use ThingState to provide automatic matchName() filtering by state. Add a property to ThingState, stateTokens, that lists tokens that can only be used in noun phrases referring to an object in the state.
    Integrate Steve's exit lister package into the library.
    It would be nice to have a sorting order for the directions, so we list in a more canonical order: north, south, east, west, northeast, northwest, southeast, southwest, up, down, in, out.
    When two exits have the same destination, list them together: "south (or out) to the front yard".
    Don't show exits to dark rooms in the dark.
    In listing exits from a dark room, maybe we should show exits leading to rooms with light, since travel would be allowed to those locations.
    Change the way we figure out if travel from a dark room to an adjoining lit room is possible. Add a separate TravelConnector method that specifically determines if the connector itself is visible to a given actor in a given origin location when the origin location is dark. By default, make a connector visible from a dark location when the destination room is lit. This will provide the customary behavior (i.e., dark-to-dark travel is prohibited, but dark-to-light is allowed on the theory that some of the light from the destination leaks through to the origin, making the connection itself self-illuminating and thus visible even in the dark origin room), while more clearly articulating the rule by making the visibility of the connector the explicit factor in determining whether or not the travel is allowed, and also providing a clean way to override the default heuristic for visibility of the connector for particular connections that should use a different rule.
    Add an option to show available exits in the status line (using a terse display that just lists the direction names).
    Make Surface.PutOn behave like Container.PutIn with respect resolving the indirect object using the tentative direct object list: if everything in the tentative direct object match list is already on the indirect object, flag the indirect object as illogical.
    Sense Event Model: Create a new class, SensoryEvent, that can be used to trigger transient sensory events that actively notify interested observers of their occurrence.
    Add convenience macros for remapTIAction that define the full remapping for a dobj or iobj to another action, with the same or reversed noun phrase order:

      dobjRemapTI(OpenWith, UnlockWith)
      dobjRemapTIReverse(FillWith, PutIn)
      dobjRemapTIReverse(ThrowAt, AttackWith)
    
    examineSpecialContents should probably differentiate according to POV and sight viewing conditions.
    Change CommandReportList to a SimpleOutputCapturer. We don't actually want to process any notifiers or monitors when capturing report results, because we turn them into a report list that can be reordered. We want to wait until we actually display the reports to notify anything of the output.
    >i
    You are carrying three matches (one lit), and a matchbook (which
    contains two matches).
    
    >x match
    Which match do you mean, an unlit match, or the lit match?
    
    >an unlit match
    (the match)
    The match is an ordinary match.
    

    It would be better not to make an arbitrary choice here, unless they explicitly said "any unlit match". We should have another go at asking for detail in this case.

    Add TITLEs to <A HREF>'s in system messages.
    Exit lister: if we have multiple exits from a location with nil destination names, we are incorrectly grouping them as though they had the same destination name. The result is that we only see one such exit in an EXITS list. Destinations with nil destination names should be treated as unknown destinations.
    Sort adv3.tl alphabetically (except for 'modify' order dependencies).
    Add Dan Schmidt's hyphen-converting output filter to output.c
    Add Dan's Thing.canBeSeenBy, Thing.canBeSeen, etc. (These are convenience methods, especially for canBeSeen and its like, which operate on the player character, saving a little typing by making 'gPlayerChar' implicit.)
    Add Dan's Event.delayEvent, removeEvent methods
    Add an event manager method that cancels an event based on the obj/prop combination, to save work for cases where this uniquely identifies the event (this way, the author doesn't have to go to the extra trouble of saving the Event object reference if the event can be uniquely identified without it).
    Add a "mass" or "collective" noun property, for objects that refer to collections of large numbers of smaller parts, or continuously-measured quantities: "some popcorn", "some water". (Is there anything that this changes besides changing aName to "some "? Should think about it.)
    Add a lower-level base class for Switch, OnOffControl, that keeps track of the on/off state and accepts "turn on" and "turn off". Define the additional switch-specific verbs ("switch" and "flip") only in the Switch subclass - this allows for adding on/off behavior without making something look exactly like a light switch.
    Add a GO BACK command to return to the most recent location (if the connector in was reversible).
    Keep track of the last "interlocutor" for each actor; this is actor last addressed in a targeted command ("bob, go north"), or the last actor in a conversational command - ASK ABOUT, TELL ABOUT, SHOW TO, GIVE TO. For the conversational commands, if the actor is left out (ASK ABOUT GOLD, TELL ABOUT STORM), use the performing actor's most recent interlocutor as the default, if the interlocutor is still within talking range.
    Create a subclass of Actor, UntakeableActor, for an actor that can't be taken or moved. Define an additional subclass, Person, to represent human characters, and provide some custom "fixed" messages for it.
    Ensure that the SCORE and FOOTNOTE modules are completely modular, so that they can simply be omitted from the build if not required.
    Get rid of libMessages.openBracket and closeBracket; instead, add a style tag, <.parser>...<./parser>. Also, add <.notification>...<./notification> for notifications (score changes, FOOTNOTE instructions, etc.).
    Change withBrackets() to parserMessage(), for better documentary effect.
    Simplify the menagerie of output filters, notifiers, monitors, and capturers, reducing everything to a single type (filter). Enforce stacking of filters.
    Get rid of the paragraph suppression immediately following input. Instead, use the command sequencer to display the appropriate kind of separation.
    Rename CommandReportList to CommandTranscript, and rename gCommandReports to gTranscript.
    Add resolved object arguments to Actor.addPendingAction, Actor.addFirstPendingAction.
    Rearrange arguments to Actor methods addPendingCommand, addPendingAction, addFirstPendingCommand, addFirstPendingAction to move the firstInSentence flag first. This will keep everything consistent with the rearrangement necessitated by adding the varargs resolved object list to addPendingAction and addFirstPendingAction.
    When a command is applied to several objects, and processing the command for some of the objects involves implied subcommands, set off the results for the objects with implied subcommands visually, by putting an extra paragraph break before and after each result for an object with implied subcommands:

    PO Box: Taken.
    red ball: Taken.
    
    iron key:
    (First putting the red ball in the duffel bag to make room)
    Taken.
    
    blue test booklet: Taken.
    
    brass key:
    (First putting the red book in the duffel bag to make room)
    Taken.
    
    gold coin: Taken.
    gold coin: Taken.
    
    Use a new style tag, <.commandsep>, for command separation, eliminating the method call commandSequencer.startNewCommand(). Using a tag instead of a method call will work better with text that doesn't reach the output stream immediately (for example, text that is queued into a command transcript during command results display).
    In Thing.construct(), initialize the new object's vocabulary words and add them to the global dictionary.
    Add information on the connector back, if known, to the EXITS display:

      Obvious exits lead north, back to the east, and south to the den.
      ...north; east, back to the living room; and south, to the den.
    
    For NPC's, the announcement/result format for implied commands should probably be changed. In particular, we should probably just treat NPC implied actions as full separate commands:

    >bob, north
    Bob opens the door.
    
    Bob departs through the door.
    
    For NPC's, don't allow interactive defaulting for an implied command.
    For NPC's, if an implied command fails, we should fail with an indication that the NPC needs to perform the implied command:

    >bob s
    Bob must open the door before he can do that.
    
    >bob, open door
    Bob must unlock the door before he can do that.
    
    Get rid of gCommandReportClass and withCommandReportsClass. Instead, add a parameter to _newAction() to specify the transcript class to use.
    Add a new BasicLocation method, getRoomPartLocation(part), which returns the immediate container of the given room part, as perceived in that location. Top-level rooms would generally just return 'self' if they have the room part, nil if not. Nested rooms would generally pass this up to their containers, because they don't have room parts themselves in most cases. Nested rooms with their own room parts would handle it themselves.
    "x floor" - should list objects that are on the floor; likewise for other room parts (walls, ceiling). Use a separate listing test (isListedInRoomPart(part)) to allow objects to opt out of being listed specifically in such cases.

    For special descriptions, add a "room part location" property - this is a purely advisory property used only for finding contents of the floor and so on. When we "x floor", show special descriptions only for objects whose roomPartLocation is the floor of interest.

    Re-enable the transcript after prompting for interactive resolution (reading an OOPS response, a disambiguation response, etc).
    Add a method to Action to swap the roles of the tentative objects, for use with the 'verify' forwarding for TIAction remapping. Add a call to this method to the dobjRemapTIReverse/iobjRemapTIReverse macros before they forward their 'verify' calls.
    When an action is remapped, and we need to announce an object (such as a defaulted object), announce the entire action, not just the object. For example:

    >fill bucket
    (dipping the bucket into the river)
    
    >fill bucket
    (pouring the coffee into the bucket)
    

    This is important because the normal announcements are worded with the intention of being tacked onto the end of what the player typed; when the action is remapped, the action we're actually executing is different from what the player typed, so phrase fragments that are intended to be added to what the player typed are no long applicable.

    frobtads-1.2.3/tads3/lib/adv3/console.t0000644000175000001440000002231611476177143016773 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - console input/output manager * * This module defines the low-level functions for handling input and * output via the traditional interpreter's user interface, using the * local keyboard and console via the "tads-io" function set. * * The functions in this module are designed primarily for internal use * within the library itself. Games should use the higher level objects * and functions defined in input.t and output.t instead of directly * calling the functions defined here. The reason for separating these * functions is so that we can substitute the Web UI versions for games * that wish to use the Web UI insetad of the traditional console UI. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Initialize the user interface. The library calls this once at the * start of the interpreter session to set up the UI. For the console * interpreter, we don't need to do anything here; the interpreter takes * care of setting up the display window for us. */ initUI() { } /* * Initialize the display. The library calls this at the start of the * game, and after each RESTART, to set up the layout of the game window. */ initDisplay() { /* set the interpreter window title */ gameMain.setGameTitle(); /* set up the ABOUT box */ gameMain.setAboutBox(); } /* * Shut down the user interface. The library calls this once just before * the game is about to terminate. */ terminateUI() { /* we don't need to do any work to close the display */ } /* ------------------------------------------------------------------------ */ /* * Check to see if we're in HTML mode */ checkHtmlMode() { /* * The HTML mode depends on the interpreter's capabilities. If this * is a full multimedia interpreter - i.e., HTML-class - then we * operate in HTML mode. Otherwise we operate in plain text mode. * * Note that the text-mode interpreters actually do interpret HTML in * the output stream, but most markups don't do anything in text * mode. Knowing the mode is thus useful in a couple of ways. In * some cases we simply avoid the overhead of generating a bunch of * HTML that will just be ignored. In other cases, we display * something a little differently in text mode, to compensate for the * lack of the effect we use in HTML mode. */ return systemInfo(SysInfoInterpClass) == SysInfoIClassHTML; } /* ------------------------------------------------------------------------ */ /* * Write text to the main game window */ aioSay(txt) { /* call the interpreter's console output writer */ tadsSay(txt); } /* ------------------------------------------------------------------------ */ /* * Get a line of input from the keyboard, with timeout */ aioInputLineTimeout(timeout) { /* call the interpreter's console input reader */ return inputLineTimeout(timeout); } /* * Cancel a suspended input line */ aioInputLineCancel(reset) { /* call the interpreter's console input line handler */ inputLineCancel(reset); } /* ------------------------------------------------------------------------ */ /* * Read an input event */ aioInputEvent(timeout) { /* call the interpreter's console event reader */ return inputEvent(timeout); } /* ------------------------------------------------------------------------ */ /* * Show a "More" prompt */ aioMorePrompt() { /* call the interpreter's console More prompt generator */ morePrompt(); } /* ------------------------------------------------------------------------ */ /* * Show a file selector dialog */ aioInputFile(prompt, dialogType, fileType, flags) { /* call the interpreter's console file dialog handler */ return inputFile(prompt, dialogType, fileType, flags); } /* ------------------------------------------------------------------------ */ /* * Show an input dialog */ aioInputDialog(icon, prompt, buttons, defaultButton, cancelButton) { /* call the interpreter's native input dialog handler */ return inputDialog(icon, prompt, buttons, defaultButton, cancelButton); } /* ------------------------------------------------------------------------ */ /* * Set/remove the output logging file */ aioSetLogFile(fname, logType?) { /* set the log file in the interpreter console */ return setLogFile(fname, logType); } /* ------------------------------------------------------------------------ */ /* * Clear the screen */ aioClearScreen() { /* clear the local interpreter console screen */ clearScreen(); /* re-initialize any tag */ gameMain.setAboutBox(); } /* ------------------------------------------------------------------------ */ /* * Generate a string to show hyperlinked text. If we're not in HTML * mode, we'll simply return the text without the hyperlink; otherwise, * we'll return the text with a hyperlink to the given HREF. * * If the display text is included, we'll generate the entire link, * including the tag, the hyperlinked text contents, and the * end tag. If the text is omitted, we'll simply generate the tag itself, leaving it to the caller to display the text and the * . * * The optional 'flags' is a combination of AHREF_xxx flags indicating * any special properties of the hyperlink. */ aHref(href, txt?, title?, flags = 0) { /* check for HTML mode */ if (outputManager.htmlMode) { /* figure the properties based on the flags */ local props = ''; if ((flags & AHREF_Plain) != 0) props += 'plain '; /* * We're in HTML mode - generate a tag enclosing the text. * If there's text, include the text and end tag, otherwise * just show the tag itself. */ return '> href="<>"<< (title != nil ? ' title="' + title + '"' : '') >>><.a><< (txt != nil ? txt + '<./a>' : '')>>'; } else { /* plain text mode - just return the text unchanged */ return txt; } } /* ------------------------------------------------------------------------ */ /* * Generate a string to show hyperlinked text, with alternate text if * we're not in HTML mode. If we're in HTML mode, we'll return * linkedTxt linked to the given HREF; if we're in plain text mode, * we'll return the alternate text as-is. */ aHrefAlt(href, linkedText, altText, title?) { /* check HTML mode */ if (outputManager.htmlMode) { /* we're in HTML mode - generate an tag for the linked text */ return '>><.a><><./a>'; } else { /* plain text mode - just return the alternate text */ return altText; } } /* ------------------------------------------------------------------------ */ /* * Generate HTML to wrap the left/right portions of the status line. The * basic status line has three stages: stage 0 precedes the left portion, * stage 1 comes between the left and right portions, and stage 2 follows * the right portion. If we're listing exits, we get two more stages: * stage 3 precedes the exit listing, stage 4 follows it. */ statusHTML(stage) { switch (stage) { case 1: /* return the right-alignment tab between the two sections */ return ''; case 3: case 4: /* show a line break before and after the exit listing */ return '
    '; default: /* other stages don't require any special text */ return ''; } } /* ------------------------------------------------------------------------ */ /* * The banner window for the status line. */ statuslineBanner: BannerWindow /* close the window */ removeBanner() { /* remove the banner */ inherited(); /* tell the statusLine object to refigure the display mode */ statusLine.statusDispMode = nil; } /* initialize */ initBannerWindow() { /* if we're already initialized, do nothing */ if (inited_) return; /* inherit the default handling (to set our 'inited_' flag) */ inherited(); /* tell the status line to initialize its banner window */ statusLine.initBannerWindow(self); } /* * Set the color scheme. We simply show a tag that selects * the parameterized colors STATUSBG and STATUSTEXT. (These are * called "parameterized" colors because they don't select specific * colors, but rather select whatever colors the interpreter wishes * to use for the status line. In many cases, the interpreter lets * the user select these colors via a Preferences dialog.) */ setColorScheme() { /* set up the interpreter's standard status line colors */ ""; } ; frobtads-1.2.3/tads3/lib/adv3/actions.t0000644000175000001440000026106111726375033016767 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: Actions. * * This module defines the set of built-in library actions. */ #include "adv3.h" #include "tok.h" /* ------------------------------------------------------------------------ */ /* * Special "debug" action - this simply breaks into the debugger, if the * debugger is present. */ DefineIAction(Debug) execAction() { /* if the debugger is present, break into it */ if (t3DebugTrace(T3DebugCheck)) t3DebugTrace(T3DebugBreak); else "Debugger not present. "; } ; /* ------------------------------------------------------------------------ */ /* * Special internal action to note a change to the darkness level. This * command is invoked internally when a change to the darkness level * occurs. */ DefineIAction(NoteDarkness) execAction() { /* * if we're in the dark, note that darkness has fallen; * otherwise, show the player character's room description as * though the player had typed "look" */ if (gActor.isLocationLit()) { /* look around */ gActor.lookAround(true); } else { /* it is now dark */ mainReport(&newlyDarkMsg); } } /* this is an internal command that takes no time */ actionTime = 0 /* this isn't a real action, so it's not repeatable */ isRepeatable = nil /* this action doesn't do anything; don't include it in undo */ includeInUndo = nil ; /* ------------------------------------------------------------------------ */ /* * Special "again" action. This command repeats the previous command. */ DefineIAction(Again) /* for obvious reasons, 'again' is not itself repeatable with 'again' */ isRepeatable = nil /* * the undo command itself is not undoable (but the underlying * command that we repeat might be) */ includeInUndo = nil /* information on the most recent command */ lastIssuingActor = nil lastTargetActor = nil lastTargetActorPhrase = nil lastAction = nil /* save the most recent command so that it can be repeated if desired */ saveForAgain(issuingActor, targetActor, targetActorPhrase, action) { /* save the information */ lastIssuingActor = issuingActor; lastTargetActor = targetActor; lastTargetActorPhrase = targetActorPhrase; lastAction = action.createClone(); } /* forget the last command, so that AGAIN cannot be used */ clearForAgain() { lastAction = nil; } /* * Execute the 'again' command. This action is special enough that * we override its entire action processing sequence - this is * necessary in case we're repeating another special command, such * as 'again', and in any case is desirable because we don't want * 'again' to count as a command in its own right; it's essentially * just a macro that we replace with the original command. */ doAction(issuingActor, targetActor, targetActorPhrase, countsAsIssuerTurn) { /* if there's nothing to repeat, show an error and give up */ if (lastAction == nil) { gLibMessages.noCommandForAgain(); return; } /* * 'again' cannot be executed with a target actor - the target * actor must be the player character */ if (!targetActor.isPlayerChar) { gLibMessages.againCannotChangeActor(); return; } /* * if the issuing actor isn't the same as the target actor, make * sure the issuer can still talk to the target */ if (lastIssuingActor != lastTargetActor && !lastIssuingActor.canTalkTo(lastTargetActor)) { /* complain that we can no longer talk to the target */ gLibMessages.againCannotTalkToTarget( lastIssuingActor, lastTargetActor); return; } /* * If the last issuing actor is different from the last target * actor, then the command counts as an issuer turn, because * we're effectively repeating the entire last command, including * the target actor specification. That is, after a command like * "bob, go east", saying "again" is just like saying "bob, go * east" again, which counts as an issuer turn. */ if (lastTargetActor != lastIssuingActor) countsAsIssuerTurn = true; /* reset any cached information for the new command context */ lastAction.resetAction(); /* repeat the action */ lastAction.repeatAction(lastTargetActor, lastTargetActorPhrase, lastIssuingActor, countsAsIssuerTurn); /* * If the command was directed from the issuer to a different * target actor, and the issuer wants to wait for the full set of * issued commands to complete before getting another turn, tell * the issuer to begin waiting. */ if (lastTargetActor != lastIssuingActor) lastIssuingActor.waitForIssuedCommand(lastTargetActor); } /* * this command itself consumes no time on the game clock (although * the action we perform might) */ actionTime = 0 ; /* ------------------------------------------------------------------------ */ /* * PreSaveObject - every instance of this class is notified, via its * execute() method, just before we save the game. This uses the * ModuleExecObject framework, so the sequencing lists (execBeforeMe, * execAfterMe) can be used to control relative ordering of execution * among instances. */ class PreSaveObject: ModuleExecObject /* * Each instance must override execute() with its specific pre-save * code. */ ; /* * PostRestoreObject - every instance of this class is notified, via its * execute() method, immediately after we restore the game. */ class PostRestoreObject: ModuleExecObject /* * note: each instance must override execute() with its post-restore * code */ /* * The "restore code," which is the (normally integer) value passed * as the second argument to restoreGame(). The restore code gives * us some idea of what triggered the restoration. By default, we * define the following restore codes: * * 1 - the system is restoring a game as part of interpreter * startup, usually because the user explicitly specified a game to * restore on the interpreter command line or via a GUI shell * mechanism, such as double-clicking on a saved game file from the * desktop. * * 2 - the user is explicitly restoring a game via a RESTORE command. * * Games and library extensions can use their own additional restore * codes in their calls to restoreGame(). */ restoreCode = nil ; /* * PreRestartObject - every instance of this class is notified, via its * execute() method, just before we restart the game (with a RESTART * command, for example). */ class PreRestartObject: ModuleExecObject /* * Each instance must override execute() with its specific * pre-restart code. */ ; /* * PostUndoObject - every instance of this class is notified, via its * execute() method, immediately after we perform an 'undo' command. */ class PostUndoObject: ModuleExecObject /* * Each instance must override execute() with its specific post-undo * code. */ ; /* ------------------------------------------------------------------------ */ /* * Special "save" action. This command saves the current game state to * an external file for later restoration. */ DefineAction(Save, FileOpAction) /* the file dialog prompt */ filePromptMsg = (gLibMessages.getSavePrompt()) /* we're asking for a file to save, or type t3-save */ fileDisposition = InFileSave fileTypeID = FileTypeT3Save /* cancel message */ showCancelMsg() { gLibMessages.saveCanceled(); } /* perform a save */ performFileOp(fname, ack, desc:?) { /* before saving the game, notify all PreSaveObject instances */ PreSaveObject.classExec(); /* * Save the game to the given file. If an error occurs, the * save routine will throw a runtime error. */ try { /* try saving the game */ saveGame(fname, gameMain.getSaveDesc(desc)); } catch (StorageServerError sse) { /* the save failed due to a storage server problem - explain */ gLibMessages.saveFailedOnServer(sse); /* done */ return; } catch (RuntimeError err) { /* the save failed - mention the problem */ gLibMessages.saveFailed(err); /* done */ return; } /* note the successful save */ gLibMessages.saveOkay(); } /* * Saving has no effect on game state, so it's irrelevant whether or * not it's undoable; but it might be confusing to say we undid a * "save" command, because the player might think we deleted the * saved file. To avoid such confusion, do not include "save" * commands in the undo log. */ includeInUndo = nil /* * Don't allow this to be repeated with AGAIN. There's no point in * repeating a SAVE immediately, as nothing will have changed in the * game state to warrant saving again. */ isRepeatable = nil ; /* * Subclass of Save action that takes a literal string as part of the * command. The filename must be a literal enclosed in quotes, and the * string (with the quotes) must be stored in our fname_ property by * assignment of a quotedStringPhrase production in the grammar rule. */ DefineAction(SaveString, SaveAction) execSystemAction() { /* * Perform the save, using the filename given in our fname_ * parameter, trimmed of quotes. */ performFileOp(fname_.getStringText(), true); } ; /* ------------------------------------------------------------------------ */ /* * Special "restore" action. This action restores game state previously * saved with the "save" action. */ DefineSystemAction(Restore) execSystemAction() { /* ask for a file and restore it */ askAndRestore(); /* * regardless of what happened, abandon any additional commands * on the same command line */ throw new TerminateCommandException(); } /* * Ask for a file and try to restore it. Returns true on success, * nil on failure. (Failure could indicate that the user chose to * cancel out of the file selector, that we couldn't find the file to * restore, or that the file isn't a valid saved state file. In any * case, we show an appropriate message on failure.) */ askAndRestore() { local succ; local result; local origElapsedTime; /* presume failure */ succ = nil; /* note the current elapsed game time */ origElapsedTime = realTimeManager.getElapsedTime(); /* ask for a file */ result = getInputFile(gLibMessages.getRestorePrompt(), InFileOpen, FileTypeT3Save, 0); /* * restore the real-time clock, so that the time spent in the * file selector dialog doesn't count against the game time */ realTimeManager.setElapsedTime(origElapsedTime); /* check the inputFile response */ switch(result[1]) { case InFileSuccess: /* * try restoring the file; use code 2 to indicate that the * restoration was performed by an explicit RESTORE command */ if (performRestore(result[2], 2)) { /* note that we succeeded */ succ = true; } else { /* * failed - in case the failed restore took some time, * restore the real-time clock, so that the file-reading * time doesn't count against the game time */ realTimeManager.setElapsedTime(origElapsedTime); } /* done */ break; case InFileFailure: /* advise of the failure of the prompt */ if (result.length() > 1) gLibMessages.filePromptFailedMsg(result[2]); else gLibMessages.filePromptFailed(); break; case InFileCancel: /* acknowledge the cancellation */ gLibMessages.restoreCanceled(); break; } /* * If we were successful, clear out the AGAIN memory. This * avoids any confusion about whether we're repeating the RESTORE * command itself, the command just before RESTORE from the * current session, or the last command before SAVE from the * restored game. */ if (succ) AgainAction.clearForAgain(); /* return the success/failure indication */ return succ; } /* * Restore a game on startup. This can be called from mainRestore() * to restore a saved game directly as part of loading the game. * (Most interpreters provide a way of starting the interpreter * directly with a saved game to be restored, skipping the * intermediate step of running the game and using a RESTORE * command.) * * Returns true on success, nil on failure. On failure, the caller * should simply exit the program. On success, the caller should * start the game running, usually using runGame(), after showing any * desired introductory messages. */ startupRestore(fname) { /* * try restoring the game, using code 1 to indicate that this is * a direct startup restore */ if (performRestore(fname, 1)) { /* success - tell the caller to proceed with the restored game */ return true; } else { /* * Failure. We've described the problem, so ask the user * what they want to do about it. */ try { /* show options and read the response */ failedRestoreOptions(); /* if we get here, proceed with the game */ return true; } catch (QuittingException qe) { /* quitting - tell the caller to terminate */ return nil; } } } /* * Restore a file. 'code' is the restoreCode value for the * PostRestoreObject notifications. Returns true on success, nil on * failure. */ performRestore(fname, code) { try { /* restore the file */ restoreGame(fname); } catch (StorageServerError sse) { /* failed due to a storage server error - explain the problem */ gLibMessages.restoreFailedOnServer(sse); /* indicate failure */ return nil; } catch (RuntimeError err) { /* failed - check the error to see what went wrong */ switch(err.errno_) { case 1201: /* not a saved state file */ gLibMessages.restoreInvalidFile(); break; case 1202: /* saved by different game or different version */ gLibMessages.restoreInvalidMatch(); break; case 1207: /* corrupted saved state file */ gLibMessages.restoreCorruptedFile(); break; default: /* some other failure */ gLibMessages.restoreFailed(err); break; } /* indicate failure */ return nil; } /* note that we've successfully restored the game */ gLibMessages.restoreOkay(); /* set the appropriate restore-action code */ PostRestoreObject.restoreCode = code; /* notify all PostRestoreObject instances */ PostRestoreObject.classExec(); /* * look around, to refresh the player's memory of the state the * game was in when saved */ "\b"; libGlobal.playerChar.lookAround(true); /* indicate success */ return true; } /* * There's no point in including this in undo. If the command * succeeds, it's not undoable itself, and there won't be any undo * information in the newly restored state. If the command fails, it * won't make any changes to the game state, so there won't be * anything to undo. */ includeInUndo = nil ; /* * Subclass of Restore action that takes a literal string as part of the * command. The filename must be a literal enclosed in quotes, and the * string (with the quotes) must be stored in our fname_ property by * assignment of a quotedStringPhrase production in the grammar rule. */ DefineAction(RestoreString, RestoreAction) execSystemAction() { /* * Perform the restore, using the filename given in our fname_ * parameter, trimmed of quotes. Use code 2, the same as any * other explicit RESTORE command. */ performRestore(fname_.getStringText(), 2); /* abandon any additional commands on the same command line */ throw new TerminateCommandException(); } ; /* ------------------------------------------------------------------------ */ /* * Restart the game from the beginning. */ DefineSystemAction(Restart) execSystemAction() { /* confirm that they really want to restart */ gLibMessages.confirmRestart(); if (yesOrNo()) { /* * The confirmation input will have put us into * start-of-command mode for sequencing purposes; force the * sequencer back to mid-command mode, so we can show * inter-command separation before the restart. */ /* restart the game */ doRestartGame(); } else { /* confirm that we're not really restarting */ gLibMessages.notRestarting(); } } /* carry out the restart action */ doRestartGame() { /* * Show a command separator, to provide separation from any * introductory text that we'll show on restarting. Note that * we probably just asked for confirmation, which means that the * command sequencer will be in start-of-command mode; force it * back to mid-command mode so we show inter-command separation. */ commandSequencer.setCommandMode(); "<.commandsep>"; /* before restarting, notify anyone interested of our intentions */ PreRestartObject.classExec(); /* * Throw a 'restart' signal; the main entrypoint loop will catch * this and actually perform the restart. * * Note that we *could* do the VM reset (via restartGame()) here, * but there's an advantage to doing it in the main loop: we * won't be in the stack context of whatever command we're * performing. If we did the restart here, it's possible that * some useless objects would survive the VM reset just because * they're referenced from within a caller's stack frame. Those * objects would immediately go out of scope when we get back to * the main loop, but they might survive long enough to create * apparent inconsistencies. In particular, if we did a * firstObj/nextObj loop, we could discover those objects and * re-establish more lasting references to them, which we * certainly don't want to do. By deferring the VM reset until * we get back to the main loop, we'll ensure that objects won't * survive the reset just because they're on the stack * momentarily here. */ throw new RestartSignal(); } /* there's no point in including this in undo */ includeInUndo = nil ; /* ------------------------------------------------------------------------ */ /* * Undo one turn. */ DefineSystemAction(Undo) /* * "Undo" is so special that we must override the entire action * processing sequence. We do this because undoing will restore the * game state as of the previous savepoint, which would leave all * sorts of things unsynchronized in the normal action sequence. To * avoid problems, we simply leave out any other action processing * and perform the 'undo' directly. */ doAction(issuingActor, targetActor, targetActorPhrase, countsAsIssuerTurn) { /* * the player obviously knows about UNDO, so there's no need for * a tip about it */ undoTip.makeShown(); /* * don't allow this unless the player character is performing * the command directly */ if (!targetActor.isPlayerChar) { /* * tell them this command cannot be directed to another * actor, and give up */ gLibMessages.systemActionToNPC(); return; } /* perform the undo */ performUndo(true); } /* * Perform undo. Returns true if we were successful, nil if not. * * 'asCommand' indicates whether or not the undo is being performed * as an explicit command: if so, we'll save the UNDO command for use * in AGAIN. */ performUndo(asCommand) { /* try undoing to the previous savepoint */ if (undo()) { local oldActor; local oldIssuer; local oldAction; /* notify all PostUndoObject instances */ PostUndoObject.classExec(); /* set up the globals for the command */ oldActor = gActor; oldIssuer = gIssuingActor; oldAction = gAction; /* set the new globals */ gActor = gPlayerChar; gIssuingActor = gPlayerChar; gAction = self; /* make sure we reset globals on the way out */ try { /* success - mention what we did */ gLibMessages.undoOkay(libGlobal.lastActorForUndo, libGlobal.lastCommandForUndo); /* look around, to refresh the player's memory */ libGlobal.playerChar.lookAround(true); } finally { /* restore the parser globals to how we found them */ gActor = oldActor; gIssuingActor = oldIssuer; gAction = oldAction; } /* * if this was an explicit 'undo' command, save the command * to allow repeating it with 'again' */ if (asCommand) AgainAction.saveForAgain(gPlayerChar, gPlayerChar, nil, self); /* indicate success */ return true; } else { /* no more undo information available */ gLibMessages.undoFailed(); /* indicate failure */ return nil; } } /* * "undo" is not undoable - if we undo again after an undo, we undo * the next most recent command */ includeInUndo = nil ; /* ------------------------------------------------------------------------ */ /* * Save the defaults */ DefineSystemAction(SaveDefaults) execSystemAction() { /* tell SettingsItem to save all settings */ settingsUI.saveSettingsMsg(); } /* there's no point in including this in undo */ includeInUndo = nil ; /* * Restore defaults */ DefineSystemAction(RestoreDefaults) execSystemAction() { /* * Tell SettingsItem to restore all settings. This is an * explicit request, so we want SettingsItem to describe what * happened. */ settingsUI.restoreSettingsMsg(); } /* there's no point in including this in undo */ includeInUndo = nil ; /* ------------------------------------------------------------------------ */ /* * Quit the game. */ DefineSystemAction(Quit) execSystemAction() { /* confirm that they really want to quit */ gLibMessages.confirmQuit(); if (yesOrNo()) { /* carry out the termination */ terminateGame(); } else { /* show the confirmation that we're not quitting */ gLibMessages.notTerminating(); } } /* * Carry out game termination. This can be called when we wish to * end the game without asking for any additional player * confirmation. */ terminateGame() { /* acknowledge that we're quitting */ gLibMessages.okayQuitting(); /* throw a 'quitting' signal to end the game */ throw new QuittingException; } /* there's no point in including this in undo */ includeInUndo = nil ; /* * Pause the game. This stops the real-time clock until the user * presses a key. Games that don't use the real-time clock will have no * use for this. */ DefineSystemAction(Pause) execSystemAction() { local elapsed; /* * remember the current elapsed game real time - when we are * released from the pause, we'll restore this time */ elapsed = realTimeManager.getElapsedTime(); /* show our prompt */ gLibMessages.pausePrompt(); /* keep going until we're released */ waitLoop: for (;;) { /* * Wait for a key, and see what we have. Note that we * explicitly do not want to allow any real-time events to * occur, so we simply wait forever without timeout. */ switch(inputKey()) { case ' ': /* space key - end the wait */ break waitLoop; case 's': case 'S': /* mention that we're saving */ gLibMessages.pauseSaving(); /* * set the elapsed time to the time when we started, so * that the saved position reflects the time at the * start of the pause */ realTimeManager.setElapsedTime(elapsed); /* save the game - go run the normal SAVE command */ SaveAction.execSystemAction(); /* show our prompt again */ "<.p>"; gLibMessages.pausePrompt(); /* go back to wait for another key */ break; case '[eof]': /* end-of-file on keyboard input - throw an error */ "\b"; throw new EndOfFileException(); default: /* ignore other keys; just go back to wait again */ break; } } /* show the released-from-pause message */ gLibMessages.pauseEnded(); /* * set the real-time clock to the same elapsed game time * that we had when we started the pause, so that the * elapsed real time of the pause itself doesn't count * against the game elapsed time */ realTimeManager.setElapsedTime(elapsed); } ; /* * Change to VERBOSE mode. */ DefineSystemAction(Verbose) execSystemAction() { /* set the global 'verbose' mode */ gameMain.verboseMode.isOn = true; /* acknowledge it */ gLibMessages.acknowledgeVerboseMode(true); } ; /* * Change to TERSE mode. */ DefineSystemAction(Terse) execSystemAction() { /* set the global 'verbose' mode */ gameMain.verboseMode.isOn = nil; /* acknowledge it */ gLibMessages.acknowledgeVerboseMode(nil); } ; /* in case the score module isn't present */ property showScore; property showFullScore; property scoreNotify; /* * Show the current score. */ DefineSystemAction(Score) execSystemAction() { /* show the simple score */ if (libGlobal.scoreObj != nil) { /* show the score */ libGlobal.scoreObj.showScore(); /* * Mention the FULL SCORE command to the player if we haven't * already. Note that we only want to mention */ if (!mentionedFullScore) { /* explain about it */ gLibMessages.mentionFullScore; /* don't mention it again */ ScoreAction.mentionedFullScore = true; } } else gLibMessages.scoreNotPresent; } /* there's no point in including this in undo */ includeInUndo = nil /* have we mentioned the FULL SCORE command yet? */ mentionedFullScore = nil ; /* * Show the full score. */ DefineSystemAction(FullScore) execSystemAction() { /* show the full score in response to an explicit player request */ showFullScore(); /* this counts as a mention of the FULL SCORE command */ ScoreAction.mentionedFullScore = true; } /* show the full score */ showFullScore() { /* show the full score */ if (libGlobal.scoreObj != nil) libGlobal.scoreObj.showFullScore(); else gLibMessages.scoreNotPresent; } /* there's no point in including this in undo */ includeInUndo = nil ; /* * Show the NOTIFY status. */ DefineSystemAction(Notify) execSystemAction() { /* show the current notification status */ if (libGlobal.scoreObj != nil) gLibMessages.showNotifyStatus( libGlobal.scoreObj.scoreNotify.isOn); else gLibMessages.commandNotPresent; } ; /* * Turn score change notifications on. */ DefineSystemAction(NotifyOn) execSystemAction() { /* turn notifications on, and acknowledge the status */ if (libGlobal.scoreObj != nil) { libGlobal.scoreObj.scoreNotify.isOn = true; gLibMessages.acknowledgeNotifyStatus(true); } else gLibMessages.commandNotPresent; } ; /* * Turn score change notifications off. */ DefineSystemAction(NotifyOff) execSystemAction() { /* turn notifications off, and acknowledge the status */ if (libGlobal.scoreObj != nil) { libGlobal.scoreObj.scoreNotify.isOn = nil; gLibMessages.acknowledgeNotifyStatus(nil); } else gLibMessages.commandNotPresent; } ; /* * Show version information for the game and the library modules the * game is using. */ DefineSystemAction(Version) execSystemAction() { /* show the version information for each library */ foreach (local cur in ModuleID.getModuleList()) cur.showVersion(); } /* there's no point in including this in undo */ includeInUndo = nil ; /* * Show the credits for the game and the library modules the game * includes. */ DefineSystemAction(Credits) execSystemAction() { /* show the credits for each library */ foreach (local cur in ModuleID.getModuleList()) cur.showCredit(); } /* there's no point in including this in undo */ includeInUndo = nil ; /* * Show the "about" information for the game and library modules. */ DefineSystemAction(About) execSystemAction() { local anyOutput; /* watch for any output while showing module information */ anyOutput = outputManager.curOutputStream .watchForOutput(function() { /* show information for each module */ foreach (local cur in ModuleID.getModuleList()) cur.showAbout(); }); /* * if we didn't have any ABOUT information to show, display a * message to this effect */ if (!anyOutput) gLibMessages.noAboutInfo; } /* there's no point in including this in undo */ includeInUndo = nil ; /* * A state object that keeps track of our logging (scripting) status. * This is transient, because logging is controlled through the output * layer in the interpreter, which does not participate in any of the * persistence mechanisms. */ transient scriptStatus: object /* * Script file name. This is nil when logging is not in effect, and * is set to the name of the scripting file when a log file is * active. */ scriptFile = nil /* RECORD file name */ recordFile = nil /* have we warned about using NOTE without logging in effect? */ noteWithoutScriptWarning = nil ; /* * Property: object is a web temp file. The Web UI uses this to flag * that a file we're saving to is actually a temp file that will be * offered as a downloadable file to the client after the file is written * and closed. */ property isWebTempFile; /* * A base class for file-oriented actions, such as SCRIPT, RECORD, and * REPLAY. We provide common handling that prompts interactively for a * filename; subclasses must override a few methods and properties to * carry out the specific subclassed operation on the file. */ DefineSystemAction(FileOp) /* our file dialog prompt message */ filePromptMsg = '' /* the file dialog open/save type */ fileDisposition = InFileSave /* the file dialog type ID */ fileTypeID = FileTypeLog /* show our cancellation mesage */ showCancelMsg = "" /* * Carry out our file operation. * * 'desc' is an optional named argument giving a description string * entered by the user via the Save Game dialog. Some versions of * the Save Game dialog let the user enter this additional * information, which can be stored as part of the saved game * metadata. */ performFileOp(fname, ack, desc:?) { /* * Each concrete action subclass must override this to carry out * our operation. This is called when the user has successfully * selected a filename for the operation. */ } execSystemAction() { /* * ask for a file and carry out our action; since the command is * being performed directly from the command line, we want an * acknowledgment message on success */ setUpFileOp(true); } /* ask for a file, and carry out our operation is we get one */ setUpFileOp(ack) { local result; local origElapsedTime; /* note the current game time */ origElapsedTime = realTimeManager.getElapsedTime(); /* ask for a file */ result = getInputFile(filePromptMsg, fileDisposition, fileTypeID, 0); /* check the inputFile result */ switch(result[1]) { case InFileSuccess: /* carry out our file operation */ if (result.length >= 3) performFileOp(result[2], ack, desc:result[3]); else performFileOp(result[2], ack); break; case InFileFailure: /* advise of the failure of the prompt */ if (result.length() > 1) gLibMessages.filePromptFailedMsg(result[2]); else gLibMessages.filePromptFailed(); break; case InFileCancel: /* acknowledge the cancellation */ showCancelMsg(); break; } /* * restore the original elapsed game time, so that the time spent * in the file selector dialog doesn't count against the game * time */ realTimeManager.setElapsedTime(origElapsedTime); } /* we can't include this in undo, as it affects external files */ includeInUndo = nil /* don't allow repeating with AGAIN */ isRepeatable = nil ; /* * Turn scripting on. This creates a text file that contains a * transcript of all commands and responses from this point forward. */ DefineAction(Script, FileOpAction) /* our file dialog parameters - ask for a log file to save */ filePromptMsg = (gLibMessages.getScriptingPrompt()) fileTypeID = FileTypeLog fileDisposition = InFileSave /* show our cancellation mesasge */ showCancelMsg() { gLibMessages.scriptingCanceled(); } /* * set up scripting - this can be used to set up scripting * programmatically, in the course of carrying out another action */ setUpScripting(ack) { setUpFileOp(ack); } /* turn on scripting to the given file */ performFileOp(fname, ack) { /* turn on logging */ local ok = nil, exc = nil; try { ok = aioSetLogFile(fname, LogTypeTranscript); } catch (Exception e) { exc = e; } if (ok) { /* remember that scripting is in effect */ scriptStatus.scriptFile = fname; /* * forget any past warning that we've issued about NOTE * without a script in effect; the next time scripting isn't * active, we'll want to issue a new warning, since they * might not be aware at that point that the scripting we're * starting now has ended */ scriptStatus.noteWithoutScriptWarning = nil; /* note that logging is active, if acknowledgment is desired */ if (ack) { if (fname.isWebTempFile) gLibMessages.scriptingOkayWebTemp(); else gLibMessages.scriptingOkay(); } } else { /* scripting is no longer in effect */ scriptStatus.scriptFile = nil; /* show an error, if acknowledgment is desired */ if (ack) { if (exc != nil) gLibMessages.scriptingFailedException(exc); else gLibMessages.scriptingFailed; } } } ; /* * Subclass of Script action taking a quoted string as part of the * command syntax. The grammar rule must set our fname_ property to a * quotedStringPhrase subproduction. */ DefineAction(ScriptString, ScriptAction) execSystemAction() { /* if there's a filename, we don't need to prompt */ if (fname_ != nil) { /* set up scripting to the filename specified in the command */ performFileOp(fname_.getStringText(), true); } else { /* there's no filename, so prompt as usual */ inherited(); } } ; /* * Turn scripting off. This stops recording the game transcript started * with the most recent SCRIPT command. */ DefineSystemAction(ScriptOff) execSystemAction() { /* turn off scripting */ turnOffScripting(true); } /* turn off scripting */ turnOffScripting(ack) { /* if we're not in a script file, ignore it */ if (scriptStatus.scriptFile == nil) { gLibMessages.scriptOffIgnored(); return; } /* cancel scripting in the interpreter's output layer */ aioSetLogFile(nil, LogTypeTranscript); /* remember that scripting is no longer in effect */ scriptStatus.scriptFile = nil; /* acknowledge the change, if desired */ if (ack) gLibMessages.scriptOffOkay(); } /* we can't include this in undo, as it affects external files */ includeInUndo = nil ; /* * RECORD - this is similar to SCRIPT, but stores a file containing only * the command input, not the output. */ DefineAction(Record, FileOpAction) /* our file dialog parameters - ask for a log file to save */ filePromptMsg = (gLibMessages.getRecordingPrompt()) fileTypeID = FileTypeCmd fileDisposition = InFileSave /* show our cancellation mesasge */ showCancelMsg() { gLibMessages.recordingCanceled(); } /* * set up recording - this can be used to set up scripting * programmatically, in the course of carrying out another action */ setUpRecording(ack) { setUpFileOp(ack); } /* turn on recording to the given file */ performFileOp(fname, ack) { /* turn on command logging */ local ok = nil, exc = nil; try { ok = aioSetLogFile(fname, logFileType); } catch (Exception e) { exc = e; } if (ok) { /* remember that recording is in effect */ scriptStatus.recordFile = fname; /* note that logging is active, if acknowledgment is desired */ if (ack) gLibMessages.recordingOkay(); } else { /* recording failed */ scriptStatus.recordFile = nil; /* show an error if acknowledgment is desired */ if (ack) { if (exc != nil) gLibMessages.recordingFailedException(exc); else gLibMessages.recordingFailed(); } } } /* the log file type - by default, we open a regular command log */ logFileType = LogTypeCommand ; /* subclass of Record action that sets up an event script recording */ DefineAction(RecordEvents, RecordAction) logFileType = LogTypeScript ; /* subclass of Record action taking a quoted string for the filename */ DefineAction(RecordString, RecordAction) execSystemAction() { /* set up scripting to the filename specified in the command */ performFileOp(fname_.getStringText(), true); } ; /* subclass of RecordString action that sets up an event script recording */ DefineAction(RecordEventsString, RecordStringAction) logFileType = LogTypeScript ; /* * Turn command recording off. This stops recording the command log * started with the most recent RECORD command. */ DefineSystemAction(RecordOff) execSystemAction() { /* turn off recording */ turnOffRecording(true); } /* turn off recording */ turnOffRecording(ack) { /* if we're not recording anything, ignore it */ if (scriptStatus.recordFile == nil) { gLibMessages.recordOffIgnored(); return; } /* cancel recording in the interpreter's output layer */ aioSetLogFile(nil, LogTypeCommand); /* remember that recording is no longer in effect */ scriptStatus.recordFile = nil; /* acknowledge the change, if desired */ if (ack) gLibMessages.recordOffOkay(); } /* we can't include this in undo, as it affects external files */ includeInUndo = nil ; /* * REPLAY - play back a command log previously recorded. */ DefineAction(Replay, FileOpAction) /* our file dialog parameters - ask for a log file to save */ filePromptMsg = (gLibMessages.getReplayPrompt()) fileTypeID = FileTypeCmd fileDisposition = InFileOpen /* show our cancellation mesasge */ showCancelMsg() { gLibMessages.replayCanceled(); } /* script flags passed to setScriptFile */ scriptOptionFlags = 0 /* replay the given file */ performFileOp(fname, ack) { /* * Note that we're reading from the script file if desired. Do * this before opening the script, so that we display the * acknowledgment even if we're in 'quiet' mode. */ if (ack) gLibMessages.inputScriptOkay( fname.ofKind(TemporaryFile) ? fname.getFilename() : fname); /* activate the script file */ local ok = nil, exc = nil; try { ok = setScriptFile(fname, scriptOptionFlags); } catch (Exception e) { exc = e; } if (!ok) { if (exc != nil) gLibMessages.inputScriptFailed(exc); else gLibMessages.inputScriptFailed(); } } ; /* subclass of Replay action taking a quoted string for the filename */ DefineAction(ReplayString, ReplayAction) execSystemAction() { /* * if there's a string, use the string as the filename; * otherwise, inherit the default handling to ask for a filename */ if (fname_ != nil) { /* set up scripting to the filename specified in the command */ performFileOp(fname_.getStringText(), true); } else { /* inherit the default handling to ask for a filename */ inherited(); } } ; /* in case the footnote module is not present */ property showFootnote; /* * Footnote - this requires a numeric argument parsed via the * numberPhrase production and assigned to the numMatch property. */ DefineSystemAction(Footnote) execSystemAction() { /* ask the Footnote class to do the work */ if (libGlobal.footnoteClass != nil) libGlobal.footnoteClass.showFootnote(numMatch.getval()); else gLibMessages.commandNotPresent; } /* there's no point in including this in undo */ includeInUndo = nil ; property footnoteSettings; /* base class for FOOTNOTES xxx commands */ DefineSystemAction(Footnotes) execSystemAction() { if (libGlobal.footnoteClass != nil) { /* set my footnote status in the global setting */ libGlobal.footnoteClass.footnoteSettings.showFootnotes = showFootnotes; /* acknowledge it */ gLibMessages.acknowledgeFootnoteStatus(showFootnotes); } else gLibMessages.commandNotPresent; } /* * the footnote status I set when this command is activated - this * must be overridden by each subclass */ showFootnotes = nil ; DefineAction(FootnotesFull, FootnotesAction) showFootnotes = FootnotesFull ; DefineAction(FootnotesMedium, FootnotesAction) showFootnotes = FootnotesMedium ; DefineAction(FootnotesOff, FootnotesAction) showFootnotes = FootnotesOff ; DefineSystemAction(FootnotesStatus) execSystemAction() { /* show the current status */ if (libGlobal.footnoteClass != nil) gLibMessages.showFootnoteStatus(libGlobal.footnoteClass. footnoteSettings.showFootnotes); else gLibMessages.commandNotPresent; } /* there's no point in including this in undo */ includeInUndo = nil ; DefineIAction(Inventory) execAction() { /* show the actor's inventory in the current mode */ gActor.showInventory(inventoryMode == InventoryTall); } /* current inventory mode - start in 'wide' mode by default */ inventoryMode = InventoryWide; ; DefineIAction(InventoryTall) execAction() { /* set inventory mode to 'tall' */ InventoryAction.inventoryMode = InventoryTall; /* run the inventory action */ InventoryAction.checkAction(); InventoryAction.execAction(); } ; DefineIAction(InventoryWide) execAction() { /* set inventory mode to 'wide' */ InventoryAction.inventoryMode = InventoryWide; /* run the inventory action */ InventoryAction.checkAction(); InventoryAction.execAction(); } ; DefineIAction(Wait) execAction() { /* just show the "time passes" message */ defaultReport(&timePassesMsg); } ; DefineIAction(Look) execAction() { /* show the actor's current location in verbose mode */ gActor.lookAround(true); } ; DefineIAction(Sleep) execAction() { /* let the actor handle it */ gActor.goToSleep(); } ; DefineTAction(Take) /* this is a basic inventory-management verb, so allow ALL with it */ actionAllowsAll = true /* get the ALL list for the direct object */ getAllDobj(actor, scopeList) { local locList; local dropLoc; local actorLoc; /* * Include all of the objects that are directly in the actor's * immediate container, the container's container, and so on out * to the "drop destination" location (which is where things go * when we DROP them, and is meant to represent the nearest * platform-like or floor-like container). Also include anything * that's directly in anything fixed in place within one of these * containers. Don't include anything that actually contains the * actor, since we normally can't pick up something we're inside. * * Start by getting the actor's immediate location and drop * destination location. */ actorLoc = actor.location; dropLoc = actor.getDropDestination(nil, nil); /* * create a vector to hold the location list, and start it off * with the drop location */ locList = new Vector(10); locList.append(dropLoc); /* now work up the location list until we hit the drop location */ for (local cur = actorLoc ; cur != nil && cur != dropLoc ; cur = cur.location) { /* add this container to the list */ locList.append(cur); } /* * now generate the subset of in-scope objects that are directly * in any of these locations (or directly in items fixed in place * within any of these locations), and return the result */ return scopeList.subset( {x: (locList.indexWhich( {loc: x.isDirectlyIn(loc) || x.isInFixedIn(loc)}) != nil && !actor.isIn(x)) }); } ; DefineTIAction(TakeFrom) /* this is a basic inventory-management verb, so allow ALL with it */ actionAllowsAll = true /* get the ALL list for the direct object */ getAllDobj(actor, scopeList) { /* ask the indirect object for the list */ return getIobj() == nil ? [] : getIobj().getAllForTakeFrom(scopeList); } ; DefineTAction(Remove) ; DefineTAction(Drop) /* this is a basic inventory-management verb, so allow ALL with it */ actionAllowsAll = true /* get the ALL list for the direct object */ getAllDobj(actor, scopeList) { /* include only objects directly held by the actor */ return scopeList.subset({x: x.isDirectlyIn(actor)}); } ; DefineTAction(Examine) ; DefineTAction(Read) ; DefineTAction(LookIn) ; DefineTAction(Search) ; DefineTAction(LookUnder) ; DefineTAction(LookBehind) ; DefineTAction(LookThrough) ; DefineTAction(Feel) ; DefineTAction(Taste) ; DefineTAction(Smell) ; DefineTAction(ListenTo) ; /* * Base class for undirected sensing, such as "listen" or "smell" with no * object. We'll scan for things that have a presence in the * corresponding sense and describe each one. */ DefineIAction(SenseImplicit) /* the sense in which I operate */ mySense = nil /* the object property to display this sense's description */ descProp = nil /* the default message to display if we find nothing specific to sense */ defaultMsgProp = nil /* the Lister we use to show the items */ resultLister = nil /* execute the action */ execAction() { local senseTab; local presenceList; /* get a list of everything in range of this sense for the actor */ senseTab = gActor.senseInfoTable(mySense); /* get a list of everything with a presence in this sense */ presenceList = senseInfoTableSubset(senseTab, {obj, info: obj.(mySense.presenceProp)}); /* * if there's anything in the list, show it; otherwise, show a * default report */ if (presenceList.length() != 0) { /* show the list using our lister */ resultLister.showList(gActor, nil, presenceList, 0, 0, senseTab, nil); } else { /* there's nothing to show - say so */ defaultReport(defaultMsgProp); } } ; DefineAction(SmellImplicit, SenseImplicitAction) mySense = smell descProp = &smellDesc defaultMsgProp = ¬hingToSmellMsg resultLister = smellActionLister ; DefineAction(ListenImplicit, SenseImplicitAction) mySense = sound descProp = &soundDesc defaultMsgProp = ¬hingToHearMsg resultLister = listenActionLister ; DefineTIAction(PutIn) /* this is a basic inventory-management verb, so allow ALL with it */ actionAllowsAll = true /* get the ALL list for the direct object */ getAllDobj(actor, scopeList) { local loc; local iobj = nil; local iobjIdent = nil; /* get the actor's location */ loc = actor.location; /* if we have an iobj list, retrieve its first element */ if (iobjList_ != nil && iobjList_.length() > 0) { iobj = iobjList_[1].obj_; iobjIdent = iobj.getIdentityObject(); } /* * Include objects that are directly in the actor's location, or * within fixed items in the actor's location, or directly in the * actor's inventory. * * Exclude the indirect object and its "identity" object (since * we obviously can't put the indirect object in itself), and * exclude everything already directly in the indirect object. */ return scopeList.subset({x: (x.isDirectlyIn(loc) || x.isInFixedIn(loc) || x.isDirectlyIn(actor)) && x != iobj && x != iobjIdent && !x.isDirectlyIn(iobj)}); } ; DefineTIAction(PutOn) /* this is a basic inventory-management verb, so allow ALL with it */ actionAllowsAll = true /* get the ALL list for the direct object */ getAllDobj(actor, scopeList) { /* use the same strategy that we do in PutIn */ local loc = actor.location; return scopeList.subset({x: (x.isDirectlyIn(loc) || x.isInFixedIn(loc) || x.isDirectlyIn(actor)) && x != getIobj() && !x.isDirectlyIn(getIobj())}); } ; DefineTIAction(PutUnder) ; DefineTIAction(PutBehind) ; DefineTAction(Wear) ; DefineTAction(Doff) ; DefineConvTopicTAction(AskFor, IndirectObject) ; DefineConvTopicTAction(AskAbout, IndirectObject) ; DefineConvTopicTAction(TellAbout, IndirectObject) /* * TELL ABOUT is a conversational address, as opposed to an order, * if the direct object of the action is the same as the issuer: in * this case, the command has the form , TELL ME ABOUT * , which has exactly the same meaning as ASK ABOUT * . */ isConversational(issuer) { local dobj; /* * if the resolved direct object matches the issuer, it's * conversational */ dobj = getResolvedDobjList(); return (dobj.length() == 1 && dobj[1] == issuer); } ; /* * AskVague and TellVague are for syntactically incorrect phrasings that * a player might accidentally type, especially in conjunction with a * past SpecialTopic prompt; in English, for example, we define these as * ASK and TELL . These are used only to * provide more helpful error messages. */ DefineTopicTAction(AskVague, IndirectObject) ; DefineTopicTAction(TellVague, IndirectObject) ; DefineConvIAction(Hello) execAction() { /* the issuing actor is saying hello to the target actor */ gIssuingActor.sayHello(gActor); } ; DefineConvIAction(Goodbye) execAction() { /* the issuing actor is saying goodbye to the target actor */ gIssuingActor.sayGoodbye(gActor); } ; DefineConvIAction(Yes) execAction() { /* the issuing actor is saying yes to the target actor */ gIssuingActor.sayYes(gActor); } ; DefineConvIAction(No) execAction() { /* the issuing actor is saying no to the target actor */ gIssuingActor.sayNo(gActor); } ; /* * Invoke the active SpecialTopic. This isn't a real command - the * player will never actually type this; rather, it's a pseudo-command * that we send to ourselves from a string pre-parser when we recognize * input that matches a SpecialTopic's custom command syntax. * * Note that we actually define the syntax for this command right here * in the language-independent library, because this isn't a real * command. The user never needs to type this command, since it's * something we generate internally. The only important language issue * is that we use a command keyword that no language will ever want to * use for a real command, so we intentionally use some near-English * gibberish. */ DefineLiteralAction(SpecialTopic) execAction() { /* * the issuing actor is saying the current special topic to the * actor's current interlocutor */ gIssuingActor.saySpecialTopic(); } /* * Repeat the action, for an AGAIN command. We need to make sure * the special text interpretation we gave to the command still * holds; if not, reparse the original text and try that. */ repeatAction(lastTargetActor, lastTargetActorPhrase, lastIssuingActor, countsAsIssuerTurn) { local cmd; /* get the original text the player entered */ cmd = getEnteredText(); /* * try running this through the special topic pre-parser again, * to see if it still has the special meaning */ if (specialTopicPreParser.doParsing(cmd, rmcCommand) .startsWith('xspcltopic ')) { /* * it still has the special meaning, so simply execute as we * normally would, by inheriting the standard Action * handling */ inherited(lastTargetActor, lastTargetActorPhrase, lastIssuingActor, countsAsIssuerTurn); } else { /* * The command no longer has the special meaning it did on * the last command, so we can't repeat this command. */ gLibMessages.againNotPossible(lastIssuingActor); } } /* * Get the original player-entered text. This is our literal * phrase, with the embedded-quote encoding decoded. */ getEnteredText() { return decodeOrig(getLiteral()); } /* * encode the original text for our literal phrase: turn double * quotes into '%q' sequences, and turn percent signs into '%%' * sequences */ encodeOrig(txt) { /* replace '%' with '%%', and double quotes with '%q' */ return txt.findReplace(['%', '"'], ['%%', '%q']); } /* decode our encoding */ decodeOrig(txt) { /* replace '%%' with '%', and '%q' with '"' */ return txt.findReplace(['%%', '%q'], ['%', '"']); } ; grammar predicate(SpecialTopic): 'xspcltopic' literalPhrase->literalMatch : SpecialTopicAction /* * Use the text of the command as originally typed by the player as * our apparent original text. */ getOrigText() { return getEnteredText(); } ; /* in case they try typing just 'xspcltopic' */ grammar predicate(EmptySpecialTopic): 'xspcltopic' : IAction /* just act like we don't understand this command */ execAction() { gLibMessages.commandNotPresent; } ; DefineTAction(Kiss) ; DefineIAction(Yell) execAction() { /* yelling generally has no effect; issue a default response */ mainReport(&okayYellMsg); } ; DefineTAction(TalkTo) ; DefineSystemAction(Topics) execSystemAction() { /* check to see if any suggestions are defined in the entire game */ if (firstObj(SuggestedTopic, ObjInstances) != nil) { /* we have topics - let the actor handle it */ gActor.suggestTopics(true); } else { /* there are no topics at all, so this command isn't used */ gLibMessages.commandNotPresent; } } /* don't include this in undo */ includeInUndo = nil ; DefineTIAction(GiveTo) getDefaultIobj(np, resolver) { /* check the actor for a current interlocutor */ local obj = resolver.getTargetActor().getCurrentInterlocutor(); if (obj != nil) return [new ResolveInfo(obj, 0, np)]; else return inherited(np, resolver); } ; /* * Define a global remapping to transform commands of the form "X, GIVE * ME Y" to the format "ME, ASK X FOR Y". This makes it easier to write * the code to handle these sorts of exchanges, since it means you only * have to write it in the ASK FOR handler. */ giveMeToAskFor: GlobalRemapping /* * Remap a command, if applicable. We look for commands of the form * "X, GIVE ME Y": we look for a GiveTo action whose indirect object * is the same as the issuing actor. When we find this form of * command, we rewrite it to "ME, ASK X FOR Y". */ getRemapping(issuingActor, targetActor, action) { /* * if it's of the form "X, GIVE Y TO Z", where Z is the issuing * actor (generally ME, but it could conceivably be someone * else), transform it into "Z, ASK X FOR Y". */ if (action.ofKind(GiveToAction) && action.canIobjResolveTo(issuingActor)) { /* create the ASK FOR action */ local newAction = AskForAction.createActionInstance(); /* remember the original version of the action */ newAction.setOriginalAction(action); /* * Changing the phrasing from "X, GIVE Y TO Z" to "Z, ASK X * FOR Y" will change the target actor from X in the old * version to Z in the new version. In the original format, * the pronouns "you", "your", and "yours" implicitly refers * to Z ("Bob, give me your book" implies "bob's book"). The * rewrite will change that, though - assuming that Z is a * second-person actor, "you" will by default refer to Z in * the rewrite. In order to preserve the original meaning, * we have to override "you" in the rewrite so that it * continues to refer to "X", which we can do using a pronoun * override in the new action. */ newAction.setPronounOverride(PronounYou, targetActor); /* * The direct object - the person we're asking - is the * original target actor ("bob" in "bob, give me x"). Since * this is a specific object, we need to wrap it in a * PreResolvedProd. */ local dobj = new PreResolvedProd(targetActor); /* * The thing we're asking for is the original direct object. * ASK FOR takes a topic phrase for its indirect object, * whereas GIVE TO takes a regular noun phrase. The two * aren't quite identical syntactically, so we'll do better * if we re-parse the original dobj noun phrase as a topic * phrase. Fortunately, this is easy... */ local iobj = newAction.reparseMatchAsTopic( action.dobjMatch, issuingActor, issuingActor); /* set the object match trees */ newAction.setObjectMatches(dobj, iobj); /* * Return the new command, addressing the *issuing* actor * this time around. */ return [issuingActor, newAction]; } /* it's not ours */ return nil; } ; DefineTIAction(ShowTo) getDefaultIobj(np, resolver) { /* check the actor for a current interlocutor */ local obj = resolver.getTargetActor().getCurrentInterlocutor(); if (obj != nil) return [new ResolveInfo(obj, 0, np)]; else return inherited(np, resolver); } ; DefineTAction(Follow) /* * For resolving our direct object, we want to include in the scope * any item that isn't present but which the actor saw departing the * present location. */ initResolver(issuingActor, targetActor) { /* inherit the base resolver initialization */ inherited(issuingActor, targetActor); /* * add to the scope all of the actor's followable objects - * these are the objects which the actor has witnessed leaving * the actor's present location */ scope_ = scope_.appendUnique(targetActor.getFollowables()); } ; DefineTAction(Attack) ; DefineTIAction(AttackWith) /* * for the indirect object, limit 'all' and defaults to the items in * inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineTAction(Throw) ; DefineTAction(ThrowDir) /* get the direction of the throwing (as a Direction object) */ getDirection() { return dirMatch.dir; } ; DefineTIAction(ThrowAt) ; DefineTIAction(ThrowTo) ; DefineTAction(Dig) ; DefineTIAction(DigWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineIAction(Jump) preCond = [actorStanding] execAction() { /* show the default report for jumping in place */ mainReport(&okayJumpMsg); } ; DefineTAction(JumpOver) ; DefineTAction(JumpOff) ; DefineIAction(JumpOffI) execAction() { mainReport(&cannotJumpOffHereMsg); } ; DefineTAction(Push) ; DefineTAction(Pull) ; DefineTAction(Move) ; DefineTIAction(MoveWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineTIAction(MoveTo) ; DefineTAction(Turn) ; DefineTIAction(TurnWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineLiteralTAction(TurnTo, IndirectObject) ; DefineTAction(Set) ; DefineLiteralTAction(SetTo, IndirectObject) ; DefineTAction(TypeOn) ; DefineLiteralTAction(TypeLiteralOn, DirectObject) ; DefineLiteralTAction(EnterOn, DirectObject) ; DefineTAction(Consult) ; DefineTopicTAction(ConsultAbout, IndirectObject) getDefaultDobj(np, resolver) { /* * if the actor has consulted something before, and that object * is still visible, use it as the default for this consultation */ local actor = resolver.getTargetActor(); local obj = actor.lastConsulted; if (obj != nil && actor.canSee(obj)) return [new ResolveInfo(obj, DefaultObject, np)]; else return inherited(np, resolver); } /* * Filter the topic phrase resolution. If we know our direct object, * and it's a Consultable, refer the resolution to the Consultable. */ filterTopic(lst, np, resolver) { local dobj; /* check the direct object */ if (dobjList_ != nil && dobjList_.length() == 1 && (dobj = dobjList_[1].obj_).ofKind(Consultable)) { /* * we have a Consultable direct object - let it handle the * topic phrase resolution */ return dobj.resolveConsultTopic(lst, topicMatch, resolver); } else { /* otherwise, use the default handling */ return inherited(lst, np, resolver); } } ; DefineTAction(Switch) ; DefineTAction(Flip) ; DefineTAction(TurnOn) ; DefineTAction(TurnOff) ; DefineTAction(Light) ; DefineTAction(Burn) ; DefineTIAction(BurnWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } /* resolve the direct object first */ resolveFirst = DirectObject ; DefineTAction(Extinguish) ; DefineTIAction(AttachTo) ; DefineTIAction(DetachFrom) ; DefineTAction(Detach) ; DefineTAction(Break) ; DefineTAction(Cut) ; DefineTIAction(CutWith) ; DefineTAction(Climb) ; DefineTAction(ClimbUp) ; DefineTAction(ClimbDown) ; DefineTAction(Open) ; DefineTAction(Close) ; DefineTAction(Lock) ; DefineTAction(Unlock) ; DefineTIAction(LockWith) /* * Resolve the direct object (the lock) first, so that we know what * we're trying to unlock when we're verifying the key. This allows * us to (optionally) boost the likelihood of a known good key for * disambiguation. */ resolveFirst = DirectObject /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineTIAction(UnlockWith) /* resolve the direct object first, for the same reason as in LockWith */ resolveFirst = DirectObject /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineTAction(Eat) ; DefineTAction(Drink) ; DefineTAction(Pour) ; DefineTIAction(PourInto) ; DefineTIAction(PourOnto) ; DefineTAction(Clean) ; DefineTIAction(CleanWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineIAction(Sit) execAction() { /* * if the actor is already sitting, just say so; otherwise, ask * what they want to sit on */ if (gActor.posture == sitting) reportFailure(&alreadySittingMsg); else askForDobj(SitOn); } ; DefineTAction(SitOn) ; DefineIAction(Lie) execAction() { /* * if the actor is already lying down, just say so; otherwise, * ask what they want to lie on */ if (gActor.posture == lying) reportFailure(&alreadyLyingMsg); else askForDobj(LieOn); } ; DefineTAction(LieOn) ; DefineTAction(StandOn) ; DefineIAction(Stand) execAction() { /* let the actor handle it */ gActor.standUp(); } ; DefineTAction(Board) ; DefineTAction(GetOutOf) getAllDobj(actor, scopeList) { /* 'all' for 'get out of' is the actor's immediate container */ return scopeList.subset({x: actor.isDirectlyIn(x)}); } ; DefineTAction(GetOffOf) getAllDobj(actor, scopeList) { /* 'all' for 'get off of' is the actor's immediate container */ return scopeList.subset({x: actor.isDirectlyIn(x)}); } ; DefineIAction(GetOut) execAction() { /* let the actor handle it */ gActor.disembark(); } ; DefineTAction(Fasten) ; DefineTIAction(FastenTo) ; DefineTAction(Unfasten) ; DefineTIAction(UnfastenFrom) ; DefineTAction(PlugIn) ; DefineTIAction(PlugInto) ; DefineTAction(Unplug) ; DefineTIAction(UnplugFrom) ; DefineTAction(Screw) ; DefineTIAction(ScrewWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; DefineTAction(Unscrew) ; DefineTIAction(UnscrewWith) /* limit 'all' for the indirect object to items in inventory */ getAllIobj(actor, scopeList) { return scopeList.subset({x: x.isIn(actor)}); } ; /* ------------------------------------------------------------------------ */ /* * Travel Action - this is the base class for verbs that attempt to move * an actor to a new location via one of the directional connections * from the current location. * * Each grammar rule for this action must set the 'dirMatch' property to * a DirectionProd match object that gives the direction. */ DefineIAction(Travel) execAction() { local conn; /* * Perform the travel via the connector, if we have one. If * there's no connector defined for this direction, show a * default "you can't go that way" message. */ if ((conn = getConnector()) != nil) { /* * we have a connector - use the pseudo-action TravelVia with * the connector to carry out the travel */ replaceAction(TravelVia, conn); } else { /* no connector - show a default "can't go that way" error */ mainReport(&cannotGoThatWayMsg); } } /* get the direction object for the travel */ getDirection() { return dirMatch != nil ? dirMatch.dir : nil; } /* * Get my travel connector. My connector is given by the travel * link property for this action as defined in the actor's current * location. */ getConnector() { /* ask the location for the connector in my direction */ return gActor.location == nil ? nil : gActor.location.getTravelConnector(getDirection(), gActor); } /* * The grammar rules for the individual directions will usually just * create a base TravelAction object, rather than one of the * direction-specific subclasses (NorthAction, etc). For * convenience in testing the action, though, treat ourself as * matching the subclass with the same direction. */ actionOfKind(cls) { /* * If they're asking about a specific-direction TravelAction * subclass, then we match it if our own direction matches that * of the given subclass, and we fail to match if our direction * doesn't match the given direction. */ if (cls.ofKind(TravelAction) && cls.getDirection() != nil) { /* we match if and only if the direction matches */ return (getDirection() == cls.getDirection()); } /* otherwise, inherit the default handling */ return inherited(cls); } ; /* for a vague command such as GO, which doesn't say where to go */ DefineIAction(VagueTravel) execAction() { /* simply ask for a direction */ reportFailure(&whereToGoMsg); } ; /* * This class makes it convenient to synthesize a TravelAction given a * Direction object. To create a travel action for a direction, use * *. new TravelDirAction(direction) * * where 'direction' is the direction object (northDirection, etc) for * the desired direction of travel. Note that if you want to use the * resulting object in replaceAction() or one of the similar macros, * you'll need to go directly to the underlying function rather than * using the standard macro, since the macros expect a literal action * name rather than an object. For example: * *. _replaceAction(gActor, new TravelDirAction(getDirection)); */ DefineAction(TravelDir, TravelAction) construct(dir) { /* remember my direction */ dir_ = dir; } /* get my direction */ getDirection() { return dir_; } /* my direction, normally specified during construction */ dir_ = nil ; /* * To make it more convenient to use directional travel actions as * synthesized commands, define a set of action classes for the specific * directions. */ DefineAction(North, TravelAction) getDirection = northDirection ; DefineAction(South, TravelAction) getDirection = southDirection ; DefineAction(East, TravelAction) getDirection = eastDirection ; DefineAction(West, TravelAction) getDirection = westDirection ; DefineAction(Northeast, TravelAction) getDirection = northeastDirection ; DefineAction(Northwest, TravelAction) getDirection = northwestDirection ; DefineAction(Southeast, TravelAction) getDirection = southeastDirection ; DefineAction(Southwest, TravelAction) getDirection = southwestDirection ; DefineAction(In, TravelAction) getDirection = inDirection ; DefineAction(Out, TravelAction) getDirection = outDirection ; DefineAction(Up, TravelAction) getDirection = upDirection ; DefineAction(Down, TravelAction) getDirection = downDirection ; DefineAction(Fore, TravelAction) getDirection = foreDirection ; DefineAction(Aft, TravelAction) getDirection = aftDirection ; DefineAction(Port, TravelAction) getDirection = portDirection ; DefineAction(Starboard, TravelAction) getDirection = starboardDirection ; /* * Non-directional travel actions */ DefineTAction(GoThrough) ; DefineTAction(Enter) ; /* * An internal action for traveling via a connector. This isn't a real * action, and shouldn't have a grammar defined for it. The purpose of * this action is to allow real actions that cause travel via a * connector to be implemented by mapping to this internal action, which * we implement on the base travel connector class. */ DefineTAction(TravelVia) /* * The direct object of this synthetic action isn't necessarily an * ordinary simulation object: it could be a TravelConnector instead. * Since callers asking for a direct object almost always expect a * simulation object, returning a non-simulation object here can be * problematic. To avoid this, we return an empty object list by * default - this ensures that no one who asks for the direct object * of the verb will get back a non-simulation travel connector. */ getCurrentObjects = [] ; /* "go back" */ DefineIAction(GoBack) execAction() { /* ask the actor to handle it */ gActor.reverseLastTravel(); } ; /* ------------------------------------------------------------------------ */ /* * Combined pushing-and-traveling action ("push crate north", "drag sled * into cave"). All of these are based on a base action class, which * defines the methods invoked on the object being pushed; the * subclasses provide a definition of the connector that determines * where the travel takes us. */ DefineTAction(PushTravel) /* * Carry out the nested travel action for the special combination * push-traveler. This should carry out the same action we would * have performed for the underlying basic travel. * * This method is invoked by the TravelPushable to carry out a * push-travel action. The TravelPushable object will first set up * a PushTraveler as the actor's global traveler, and it will then * invoke this method to carry out the actual travel with that * special traveler in effect. Our job is to provide the mapping to * the correct underlying simple travel action; since we'll be * moving the PushTraveler object, we can move it using the ordinary * non-push travel action as though it were any other traveler. * * This method is abstract - each subclass must define it * appropriately. */ // performTravel() { } ; /* * For directional push-and-travel commands, we define a common base * class that does the work to find the connector based on the room's * directional connector. * * Subclasses for grammar rules must define the 'dirMatch' property to * be a DirectionProd object for the associated direction. */ DefineAction(PushTravelDir, PushTravelAction) /* * Get the direction we're going. By default, we return the * direction associated with the dirMatch match object from our * grammar match. */ getDirection() { return dirMatch.dir; } /* carry out the nested travel action for a PushTravel */ performTravel() { local conn; /* ask the actor's location for the connector in our direction */ conn = gActor.location.getTravelConnector(getDirection(), gActor); /* perform a nested TravelVia on the connector */ nestedAction(TravelVia, conn); } ; /* * To make it easy to synthesize actions for pushing objects, define * individual subclasses for the various directions. */ DefineAction(PushNorth, PushTravelDirAction) getDirection = northDirection ; DefineAction(PushSouth, PushTravelDirAction) getDirection = southDirection ; DefineAction(PushEast, PushTravelDirAction) getDirection = eastDirection ; DefineAction(PushWest, PushTravelDirAction) getDirection = westDirection ; DefineAction(PushNorthwest, PushTravelDirAction) getDirection = northwestDirection ; DefineAction(PushNortheast, PushTravelDirAction) getDirection = northeastDirection ; DefineAction(PushSouthwest, PushTravelDirAction) getDirection = southwestDirection ; DefineAction(PushSoutheast, PushTravelDirAction) getDirection = southeastDirection ; DefineAction(PushUp, PushTravelDirAction) getDirection = upDirection ; DefineAction(PushDown, PushTravelDirAction) getDirection = downDirection ; DefineAction(PushIn, PushTravelDirAction) getDirection = inDirection ; DefineAction(PushOut, PushTravelDirAction) getDirection = outDirection ; DefineAction(PushFore, PushTravelDirAction) getDirection = foreDirection ; DefineAction(PushAft, PushTravelDirAction) getDirection = aftDirection ; DefineAction(PushPort, PushTravelDirAction) getDirection = portDirection ; DefineAction(PushStarboard, PushTravelDirAction) getDirection = starboardDirection ; /* * Base class for two-object push-travel commands, such as "push boulder * out of cave" or "drag sled up hill". For all of these, the connector * is given by the indirect object. */ DefineAction(PushTravelViaIobj, TIAction, PushTravelAction) /* * Verify the indirect object of the push-travel action. We'll * remap this to given corresponding simple travel action, and call * that action's verifier. */ verifyPushTravelIobj(obj, action) { /* handle this by remapping it to the underlying simple action */ remapVerify(IndirectObject, gVerifyResults, [action, obj]); } ; DefineTIActionSub(PushTravelThrough, PushTravelViaIobjAction) /* * Carry out the underlying simple travel action. This simply * performs a GoThrough on my indirect object, as though we had * typed simply GO THROUGH iobj. The PushTraveler will already be * set up as the actor's special traveler, so the ordinary GO * THROUGH command will move the special PushTraveler object as * though it were the original actor. */ performTravel() { nestedAction(GoThrough, getIobj()); } ; DefineTIActionSub(PushTravelEnter, PushTravelViaIobjAction) /* carry out the underlying simple travel as an ENTER action */ performTravel() { nestedAction(Enter, getIobj()); } ; DefineTIActionSub(PushTravelGetOutOf, PushTravelViaIobjAction) /* carry out the underlying simple travel as a GET OUT OF action */ performTravel() { nestedAction(GetOutOf, getIobj()); } ; DefineTIActionSub(PushTravelClimbUp, PushTravelViaIobjAction) /* carry out the underlying simple travel as a CLIMB UP action */ performTravel() { nestedAction(ClimbUp, getIobj()); } ; DefineTIActionSub(PushTravelClimbDown, PushTravelViaIobjAction) /* carry out the underlying simple travel as an CLIMB DOWN action */ performTravel() { nestedAction(ClimbDown, getIobj()); } ; /* * The "exits" verb. This verb explicitly shows all of the exits from * the current location. */ DefineIAction(Exits) execAction() { /* * if we have an exit lister object, invoke it; otherwise, * explain that this command isn't supported in this game */ if (gExitLister != nil) gExitLister.showExitsCommand(); else gLibMessages.commandNotPresent; } ; /* in case the exits module isn't included */ property showExitsCommand, exitsOnOffCommand; /* * Change the exit display mode. The grammar must set one of the mode * token properties to a non-nil value, according to which mode the * player selected: on_ for turning on statusline and description lists; * stat_ for turning on only the statusline list; look_ for turning on * only the room description list; and off_ for turning off everything. */ DefineSystemAction(ExitsMode) execSystemAction() { local stat, look; /* * If it's EXITS ON, turn on both statusline and room description * lists. If it's EXITS LOOK or EXITS STATUS, just turn on one * or the other. Otherwise, turn both off. */ stat = (stat_ != nil || on_ != nil); look = (look_ != nil || on_ != nil); /* update the exit display */ if (gExitLister != nil) gExitLister.exitsOnOffCommand(stat, look); else gLibMessages.commandNotPresent; } ; /* * Dummy OOPS action for times when OOPS isn't in context. We'll simply * explain how OOPS works, and that you can't use it right now. */ DefineLiteralAction(Oops) execAction() { /* simply explain how this command works */ gLibMessages.oopsOutOfContext; } /* this is a meta-command, so don't consume any time */ actionTime = 0 ; /* intransitive form of "oops" */ DefineIAction(OopsI) doActionMain() { /* as with OOPS with a literal, simply explain the problem */ gLibMessages.oopsOutOfContext; } /* this is a meta-command, so don't consume any time */ actionTime = 0 ; property disableHints, showHints; /* hint system - disable hints for this session */ DefineSystemAction(HintsOff) execSystemAction() { if (gHintManager != nil) gHintManager.disableHints(); else mainReport(gLibMessages.hintsNotPresent); } ; /* invoke hint system */ DefineSystemAction(Hint) execSystemAction() { if (gHintManager != nil) gHintManager.showHints(); else mainReport(gLibMessages.hintsNotPresent); } ; /* ------------------------------------------------------------------------ */ /* * Parser debugging verbs */ #ifdef PARSER_DEBUG DefineIAction(ParseDebug) execAction() { local newMode; /* * get the mode - if the mode is explicitly stated in the * command, use the stated new mode, otherwise invert the current * mode */ newMode = (onOrOff_ == 'on' ? true : onOrOff_ == 'off' ? nil : !libGlobal.parserDebugMode); /* set the new mode */ libGlobal.parserDebugMode = newMode; /* mention the change */ "Parser debugging is now <>.\n"; } ; grammar predicate(ParseDebug): 'parse-debug' 'on'->onOrOff_ | 'parse-debug' 'off'->onOrOff_ | 'parse-debug' : ParseDebugAction ; #endif frobtads-1.2.3/tads3/lib/adv3/menuweb.t0000644000175000001440000002436711674642661017005 0ustar realncusers#charset "us-ascii" /* * TADS 3 Library - Menu System, console edition * * This implements the menusys user interface for the traditional * console-mode interpreters. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Menu Item - user interface implementation for the console */ modify MenuItem /* * Call menu.display when you're ready to show the menu. This * should be called on the top-level menu; we run the entire menu * display process, and return when the user exits from the menu * tree. */ display() { /* save the top-level key list */ MenuItem.curKeyList = keyList; /* set myself as the current menu and the top-level menu */ MenuItem.topMenu = self; MenuItem.curMenu = self; /* display the menu in the javascript client */ showMenu(nil); /* process network events until the user closes the menu */ MenuItem.isOpen = true; processNetRequests({: !MenuItem.isOpen }); } /* current menu, and current top-level menu */ curMenu = nil topMenu = nil /* is the menu open? */ isOpen = nil /* show this menu as a submenu */ showMenu(from) { /* get the XML representation of the menu item list */ local xml = '<>'; /* tell the javascript client to display the menu */ webMainWin.sendWinEvent(xml); /* save this in the main window as the current menu state */ webMainWin.menuSysState = xml; } /* navigate into a submenu */ enterSubMenu(idx) { /* validate the index and select the new menu */ if (idx >= 1 && idx <= contents.length()) { /* get the new menu */ local m = contents[idx]; /* note the new menu location */ MenuItem.curMenu = m; /* show the new menu */ m.showMenu(self); } } /* * Package my menu items as XML, to send to the javascript API. * 'from' is the menu we just navigated from, if any. This is nil * when we enter the top level menu, since we're not navigating from * another menu; when we navigate from a parent to a child, this is * the parent; when we return from a child to a parent, this is the * child; and when we move directly from sibling to sibling (via a * next/previous chapter command), this is the sibling. When we * display a new topic in a topic list menu, this is simply 'self'. */ getXML(from) { /* set up a string buffer for the xml */ local s = new StringBuffer(); /* update the menu contents */ updateContents(); /* start with the menu title */ s.append('<<title.htmlify()>>'); /* note if we're the top-level menu */ if (location == nil || MenuItem.topMenu == self) s.append(''); /* run through the contents */ for (local item in contents) s.append('<>'); /* if the 'from' menu is a child, initially select it */ local idx = contents.indexOf(from); if (idx != nil) s.append('<>'); /* add the keys */ getKeysXML(s); /* return the string */ return toString(s); } /* get the XML description of the top-level key list */ getKeysXML(buf) { buf.append(''); for (local kl in curKeyList) { buf.append(''); for (local k in kl) { if (k == ' ') k = 'U+0020'; buf.append('<>'); } buf.append(''); } buf.append(''); } /* * Prepare a title or content string for our XML output. If 'val' is * a string, we'll run it through the output formatter to expand any * special <.xxx> sequences. If 'val' is a property, we'll evaluate * the property of self, capturing the output if it generates any or * capturing the string if it returns one. In all cases, we take the * result string and convert TADS special characters to HTML, and * finally html-escape the result for inclusion in XML output, and * return the resulting string. */ formatXML(func) { /* call the function and process it through the menu stream filters */ local txt = menuOutputStream.captureOutput(func); /* convert special characters and html-escape the result */ return txt.specialsToHtml().htmlify(); } ; /* * Menu system UI request processor. This receives requests from the * javascript client in response to user actions: selecting a menu item, * navigating to the parent menu, closing the menu. */ menuSysEventPage: WebResource vpath = '/webui/menusys' processRequest(req, query) { /* check the action type */ if (query.isKeyPresent('close')) { /* close the menu */ MenuItem.isOpen = nil; } else if (query.isKeyPresent('prev')) { /* go to the parent menu, or close the menu if at the top */ local cur = MenuItem.curMenu; if (cur != nil && cur != MenuItem.topMenu && cur.location != nil) { /* go to the parent */ local par = cur.location; MenuItem.curMenu = par; par.showMenu(cur); } else { /* there's no parent - close the menu */ MenuItem.isOpen = nil; /* tell the UI to close its menu dialog */ webMainWin.sendWinEvent(''); webMainWin.menuSysState = ''; } } else if (query.isKeyPresent('select')) { /* * Select a child menu. The 'select=n' parameter is the * index in the current menu's child list of the new item to * select. Retrieve the index. */ MenuItem.curMenu.enterSubMenu(toInteger(query['select'])); } else if (query.isKeyPresent('nextTopic')) { /* get the next topic in the current topic menu */ sendAck(req, MenuItem.curMenu.getNextTopicXML()); /* rebuild the menu state for the change */ webMainWin.menuSysState = '<>'; /* we've sent our reply, so we're done */ return; } else if (query.isKeyPresent('chapter')) { /* get the next or previous chapter, as applicable */ local dir = query['chapter']; local m = MenuItem.curMenu; local par = m.location; local nxt = (dir == 'next' ? par.getNextMenu(m) : par.getPrevMenu(m)); /* enter this chapter */ if (nxt != nil) { MenuItem.curMenu = nxt; nxt.showMenu(m); } } /* acknowledge the request */ sendAck(req); } ; /* ------------------------------------------------------------------------ */ /* * Menu topic item - console UI implementation */ modify MenuTopicItem /* get the XML description of my menu list */ getXML(from) { /* start with an empty result buffer */ local s = new StringBuffer(); /* update our contents, as needed */ updateContents(); /* add the title and total number of items in the menu */ s.append('<<title.htmlify()>>' + '<>'); /* note if we're the top-level menu */ if (location == nil || MenuItem.topMenu == self) s.append(''); /* add each item in our list */ for (local i in 1..lastDisplayed) s.append(getTopicXML(i)); /* add the keys */ getKeysXML(s); /* return the XML string */ return toString(s); } /* get the next topic, in XML format */ getNextTopicXML() { /* if we're not already at the last item, advance the counter */ if (lastDisplayed < menuContents.length()) ++lastDisplayed; /* format the last item */ return getTopicXML(lastDisplayed); } /* get the XML formatted description of the item at the given index */ getTopicXML(i) { /* get the item */ local item = menuContents[i]; /* get the item's text, and format as XML */ item = formatXML(dataType(item) == TypeObject ? {: item.getItemText() } : item); /* format the item text */ return '<>'; } ; /* ------------------------------------------------------------------------ */ /* * Long topic item */ modify MenuLongTopicItem /* get my XML description */ getXML(from) { /* start with an empty result buffer */ local s = new StringBuffer(); /* update our contents, as needed */ updateContents(); /* add my title (heading) */ local t = heading != nil && heading != '' ? heading : title; s.append('<<t.htmlify()>>'); /* add our contents */ s.append('<>'); /* * if this is a chapter menu, note if we have links for the next * and previous chapters */ if (isChapterMenu) { local m; if ((m = location.getNextMenu(self)) != nil) s.append('<>'); if ((m = location.getPrevMenu(self)) != nil) s.append('<>'); } /* add the keys */ getKeysXML(s); /* return the XML string */ return toString(s); } ; frobtads-1.2.3/tads3/lib/adv3/score.t0000644000175000001440000004274211465246012016437 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 by Michael J. Roberts. All Rights Reserved. * * TADS 3 Library - scoring * * This module defines objects related to keeping track of the player's * score, which indicates the player's progress through the game. */ /* include the library header */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * An Achievement is an object used to award points in the score. For * most purposes, an achievement can be described simply by a string, * but the Achievement object provides more flexibility in describing * combined scores when a set of similar achievements are to be grouped. * * There are two ways to use the scoring system. * * 1. You can use a mix of string names and Achievement objects for * scoring items; each time you award a scoring item, you call the * function addToScore() to specify the achievement (by name or by * Achievement object) and the number of points to award. You can also * call the method addToScoreOnce() on an Achievement object to award * the scoring item, ensuring that the item is only awarded once in the * entire game (saving you the trouble of checking to see if the event * that triggered the scoring item has happened before already in the * same game). If you do this, you MUST set the property * gameMain.maxScore to reflect the maximum score possible in the game. * * 2. You can use EXCLUSIVELY Achievement objects to represents scoring * items, and give each Achievement object a 'points' property * indicating the number of points it's worth. To award a scoring item, * you call the method awardPoints() on an Achievement object. If you * use this style of scoring, the library AUTOMATICALLY computes the * gameMain.maxScore value, by adding up the 'points' values of all of * the Achievement objects in the game. For this to work properly, you * have to obey the following rules: * *. - use ONLY Achievement objects (never strings) to award points; *. - set the 'points' property of each Achievement to its score; *. - define Achievement objects statically only (never use 'new' to *. create an Achievement dynamically) *. - if an Achievement can be awarded more than once, you must override *. its 'maxPoints' property to reflect the total number of points it *. will be worth when it is awarded the maximum number of times; *. - always award an Achievement through its awardPoints() or *. awardPointsOnce() method; *. - there exists at least one solution of the game in which every *. Achievement object is awarded */ class Achievement: object /* * The number of points this Achievement scores individually. By * default, we set this to nil. If you use the awardPoints() or * awardPointsOnce() methods, you MUST set this to a non-nil value. * * If you set this to a non-nil value, the library will use it * pre-compute the maximum possible score in the game, saving you the * trouble of figuring out the maximum score by hand. */ points = nil /* * The MAXIMUM number of points this Achievement can award. This is * by default just our 'points' value, on the assumption that the * achievement is scored only once. The library uses this value * during pre-initialization to compute the maximum possible score in * the game. */ maxPoints = (points) /* * Describe the achievement - this must display a string explaining * the reason the points associated with this achievement were * awarded. * * Note that this description can make use of the scoreCount * information to show different descriptions depending on how many * times the item has scored. For example, an achievement for * finding various treasure items might want to display "finding a * treasure" if only one treasure was found and "finding five * treasures" if five were found. * * In some cases, it might be desirable to keep track of additional * custom information, and use that information in generating the * description. For example, the game might keep a list of * treasures found with the achievement, adding to the list each * time the achievement is scored, and displaying the contents of * the list when the description is shown. */ desc = "" /* show myself in a full-score listing */ listFullScoreItem() { /* show the number of points I'm worth */ gLibMessages.fullScoreItemPoints(totalPoints); /* show my description */ desc; } /* * The number of times the achievement has been awarded. Each time * the achievement is passed to addToScore(), this is incremented. * Note that this is distinct from the number of points. */ scoreCount = 0 /* * the number of points awarded for the achievement; if this * achievement has been accomplished multiple times, this reflects * the aggregate number of points awarded for all of the times it * has been accomplished */ totalPoints = 0 /* * Add this achievement to the score one time only, awarding the * given number of points. This can be used to score an achievement * without separately tracking whether or not the achievement has * been accomplished previously. If the achievement has already been * scored before, this will do nothing at all; otherwise, it'll score * the achievement with the given number of points. Returns true if * we do award the points, nil if not (because we've awarded them * before). */ addToScoreOnce(points) { /* if I've never been scored before, score me now */ if (scoreCount == 0) { /* add the points to the score */ addToScore(points, self); /* tell the caller we awarded the points as requested */ return true; } else { /* tell the caller we didn't do anything */ return nil; } } /* * Award this Achievement's score, using the score value specified in * my 'points' property. */ awardPoints() { /* add me to the score, using my 'points' property */ addToScore(points, self); } /* * Award this Achievement's score, but ensure that we're never * awarded more than one time. If this Achievement has already been * awarded, this does nothing at all. Returns true if we do award * the points, nil if not (because we've awarded them before). */ awardPointsOnce() { /* award my 'points' value only if we haven't score before */ return addToScoreOnce(points); } ; /* * Generic text achievement. When we add an achievement to the full * score list and the achievement is a simple string description, we'll * create one of these to encapsulate the achievement. */ class SimpleAchievement: Achievement /* create dynamically with a given string as our description */ construct(str) { desc_ = str; } /* show my description */ desc { say(desc_); } /* my description string */ desc_ = '' ; /* * List interface for showing the full score list */ fullScoreLister: Lister showListPrefixTall(itemCount, pov, parent) { /* showt he full score list intro message */ gLibMessages.showFullScorePrefix; } /* every achievement is listed */ isListed(obj) { return true; } /* each item counts as a singular object grammatically */ listCardinality(obj) { return 1; } /* achievements have no containment hierarchy */ getContents(obj) { return []; } getListedContents(obj, infoTab) { return []; } showContentsList(pov, obj, options, indent, infoTab) { } showInlineContentsList(pov, obj, options, indent, infoTab) { } /* show an item */ showListItem(obj, options, pov, infoTab) { obj.listFullScoreItem(); } ; /* * Score notification daemon handler. We'll receive a * checkNotification() call each turn; we'll display a notification * message each time the score has changed since the last time we ran. */ scoreNotifier: PreinitObject /* the score as it was the last time we displayed a notification */ lastScore = static (libScore.totalScore) /* we've never generated a notification about the score before */ everNotified = nil /* daemon entrypoint */ checkNotification() { /* * if the score has changed since the last time we checked, * possibly generate a notification */ if (libScore.totalScore != lastScore) { /* only show a message if we're allowed to */ if (libScore.scoreNotify.isOn) { local delta; /* calculate the change since the last notification */ delta = libScore.totalScore - lastScore; /* * generate the first or non-first notification, as * appropriate */ if (everNotified) gLibMessages.scoreChange(delta); else gLibMessages.firstScoreChange(delta); /* * note that we've ever generated a score change * notification, so that we don't generate the more * verbose first-time message on subsequent * notifications */ everNotified = true; } /* * Remember the current score, so that we don't generate * another notification until the score has changed again. * Note that we note the new score even if we aren't * displaying a message this time, because we don't want to * generate a message upon re-enabling notifications. */ lastScore = libScore.totalScore; } } /* execute pre-initialization */ execute() { /* initialize the score change notification daemon */ new PromptDaemon(self, &checkNotification); } ; /* * Add points to the total score. This is a convenience function that * simply calls libScore.addToScore_(). */ addToScore(points, desc) { /* simply call the libScore method to handle it */ libScore.addToScore_(points, desc); } /* * The main game score object. */ libScore: PreinitObject /* * Add to the score. 'points' is the number of points to add to the * score, and 'desc' is a string describing the reason the points * are being awarded, or an Achievement object describing the points. * * We keep a list of each unique achievement. If 'desc' is already * in this list, we'll simply add the given number of points to the * existing entry for the same description. * * Note that, if 'desc' is an Achievement object, it will match a * previous item only if it's exactly the same Achievement instance. */ addToScore_(points, desc) { local idx; /* * if the description is a string, encapsulate it in a * SimpleAchievement object */ if (dataType(desc) == TypeSString) { local newDesc; /* * look for an existing SimpleAchievement in our list with * the same descriptive text - if we find one, reuse it, * since this is another instance of the same group of * achievements and thus can be combined into the same * achievement object */ newDesc = fullScoreList.valWhich( { x: x.ofKind(SimpleAchievement) && x.desc_ == desc }); /* * if we didn't find it, create a new simple achievement to * wrap the descriptive text */ if (newDesc == nil) newDesc = new SimpleAchievement(desc); /* * for the rest of our processing, use the wrapper simple * achievement object instead of the original text string */ desc = newDesc; } /* increase the use count for the achievement */ desc.scoreCount++; /* add the points to the total */ totalScore += points; /* try to find a match in our list of past achievements */ idx = fullScoreList.indexOf(desc); /* if we didn't find it, add it to the list */ if (idx == nil) fullScoreList.append(desc); /* * combine the points awarded this time into the total for this * achievement */ desc.totalPoints += points; } /* * Explicitly run the score notification daemon. */ runScoreNotifier() { /* explicitly run the notification */ scoreNotifier.checkNotification(); } /* * Show the simple score */ showScore() { /* * Show the basic score statistics. Use the appropriate form of * the message, depending on whether or not there's a maximum * score value. */ if (gameMain.maxScore != nil) gLibMessages.showScoreMessage(totalScore, gameMain.maxScore, libGlobal.totalTurns); else gLibMessages.showScoreNoMaxMessage(totalScore, libGlobal.totalTurns); /* show the score ranking */ showScoreRank(totalScore); } /* * show the score rank message */ showScoreRank(points) { local idx; local tab = gameMain.scoreRankTable; /* if there's no rank table, skip the ranking */ if (tab == nil) return; /* * find the last item for which our score is at least the * minimum - the table is in ascending order of minimum score, * so we want the last item for which our score is sufficient */ idx = tab.lastIndexWhich({x: points >= x[1]}); /* if we didn't find an item, use the first by default */ if (idx == nil) idx = 1; /* show the description from the item we found */ gLibMessages.showScoreRankMessage(tab[idx][2]); } /* * Display the full score. 'explicit' is true if the player asked * for the full score explicitly, as with a FULL SCORE command; if * we're showing the full score automatically in the course of some * other action, 'explicit' should be nil. */ showFullScore() { /* show the basic score statistics */ showScore(); /* list the achievements in 'tall' mode */ fullScoreLister.showListAll(fullScoreList.toList(), ListTall, 0); } /* * Vector for the full score achievement list. This is a list of * all of the Achievement objects awarded for accomplishments so * far. */ fullScoreList = static new Vector(32) /* the total number of points scored so far */ totalScore = 0 /* * current score notification status - if on, we'll show a message at * the end of each turn where the score changes, otherwise we won't * mention anything */ scoreNotify = scoreNotifySettingsItem /* * Compute the sum of the maximum point values of the Achievement * objects in the game. Point values are optional in Achievement * objects; if there are no Achievement objects with non-nil point * values, this will simply return nil. */ calcMaxScore() { local sum; local found; /* start with a running total of zero */ sum = 0; /* we haven't found any non-nil point values yet */ found = nil; /* * Run through all of the Achievement objects to see if we can * derive a maximum score for the game. */ forEachInstance(Achievement, function(obj) { local m; /* * If this object has a non-nil maxPoints value, add it to * the running total. */ if ((m = obj.maxPoints) != nil) { /* add this one to the sum */ sum += m; /* note that we found one with a non-nil point value */ found = true; } }); /* * If we found any Achievements with point values, return the sum * of those point values; otherwise, return nil. */ return (found ? sum : nil); } /* execute pre-initialization */ execute() { /* register as the global score handler */ libGlobal.scoreObj = self; } ; /* settings item for score notification mode */ scoreNotifySettingsItem: BinarySettingsItem /* the "factory setting" for NOTIFY is ON */ isOn = true /* our configuration file variable ID */ settingID = 'adv3.notify' /* show our description */ settingDesc = (gLibMessages.shortNotifyStatus(isOn)) ; frobtads-1.2.3/tads3/lib/adv3/menusys.t0000644000175000001440000004372511635651235017037 0ustar realncusers#charset "us-ascii" /* * TADS 3 Library - Menu System * * Copyright 2003 by Stephen Granade *. Modifications copyright 2003, 2010 Michael J. Roberts * * This module is designed to make it easy to add on-screen menu trees to * a game. Note that we're not using the term "menu" in its modern GUI * sense of a compact, mouse-driven pop-up list. The style of menu we * implement is more like the kind you'd find in old character-mode * terminal programs, where a list of text items takes over the main * window contents. * * Note that in plain-text mode (for interpreters without banner * capabilities), a menu won't be fully usable if it exceeds nine * subitems: each item in a menu is numbered, and the user selects an * item by entering its number; but we only accept a single digit as * input, so only items 1 through 9 can be selected on any given menu. * In practice you probably wouldn't want to create larger menus anyway, * for usability reasons, but this is something to keep in mind. If you * need more items, you can group some of them into a submenu. * * The user interface for the menu system is implemented in menucon.t for * traditional console interpreter, and in menuweb.t for the Web UI. * * Stephen Granade adapted this module from his TADS 2 menu system, and * Mike Roberts made some minor cosmetic changes to integrate it with the * main TADS 3 library. */ #include "adv3.h" /* * General instructions: * * Menus consist of MenuItems, MenuTopicItems, and MenuLongTopicItems. * * - MenuItems are the menu (and sub-menu) items that the player will * select. Their "title" attribute is what will be shown in the menu, * and the "heading" attribute is shown as the heading while the menu * itself is active; by default, the heading simply uses the title. * * - MenuTopicItems are for lists of topic strings that the player will * be shown, like hints. "title" is what will be shown in the menu; * "menuContents" is a list of either strings to be displayed, one at a * time, or objects which each must return a string via a "menuContents" * method. * * - MenuLongTopicItems are for longer discourses. "title" is what will * be shown in the menu; "menuContents" is either a string to be printed * or a routine to be called. * * adv3.h contains templates for MenuItems, for your convenience. * * A simple example menu: * * FirstMenu: MenuItem 'Test menu'; *. + MenuItem 'Pets'; *. ++ MenuItem 'Chinchillas'; *. +++ MenuTopicItem 'About them' *. menuContents = ['Furry', 'South American', 'Curious', * 'Note: Not a coat']; *. +++ MenuTopicItem 'Benefits' *. menuContents = ['Non-allergenic', 'Cute', 'Require little space']; *. +++ MenuTopicItem 'Downsides' *. menuContents = ['Require dust baths', 'Startle easily']; *. ++ MenuItem 'Cats'; *. +++ MenuLongTopicItem 'Pure evil' *. menuContents = 'Cats are, quite simply, pure evil. I would provide *. ample evidence were there room for it in this *. simple example.'; *. +++ MenuTopicItem 'Benefits' *. menuContents = ['They, uh, well...', 'Okay, I can\'t think of any.']; */ /* ------------------------------------------------------------------------ */ /* * Menu output stream. We run topic contents through this output stream * to allow topics to use the special paragraph and style tag markups. */ transient menuOutputStream: OutputStream /* * Process a function call through the stream. If the function * generates any output, we capture it. If the function simply * returns text, we run it through the filters. */ captureOutput(val) { /* reset our buffer */ buf_.deleteChars(1); /* call the function while capturing its output */ outputManager.withOutputStream(menuOutputStream, function() { /* if it's a function, invoke it */ if (dataType(val) != TypeSString) val = val(); /* if we have a string, run it through my filters */ if (dataType(val) == TypeSString) writeToStream(val); }); /* return my captured output */ return toString(buf_); } /* we capture our output to a string buffer */ writeFromStream(txt) { buf_.append(txt); } /* initialize */ execute() { inherited(); buf_ = new StringBuffer(); addOutputFilter(typographicalOutputFilter); addOutputFilter(menuParagraphManager); addOutputFilter(styleTagFilter); } /* our capture buffer (a StringBuffer object) */ buf_ = nil ; /* * Paragraph manager for the menu output stream. */ transient menuParagraphManager: ParagraphManager ; /* ------------------------------------------------------------------------ */ /* * A basic menu object. This is an abstract base class that * encapsulates some behavior common to different menu classes, and * allows the use of the + syntax (like "+ MenuItem") to define * containment. */ class MenuObject: object /* our contents list */ contents = [] /* * Since we're inheriting from object, but need to use the "+" * syntax, we need to set up the contents appropriately */ initializeLocation() { if (location != nil) location.addToContents(self); } /* add a menu item */ addToContents(obj) { /* * If the menu has a nil menuOrder, and it inherits menuOrder * from us, then it must be a dynamically-created object that * doesn't provide a custom menuOrder. Provide a suitable * default of a value one higher than the highest menuOrder * currently in our list, to ensure that the item always sorts * after any items currently in the list. */ if (obj.menuOrder == nil && !overrides(obj, MenuObject, &menuOrder)) { local maxVal; /* find the maximum current menuOrder value */ maxVal = nil; foreach (local cur in contents) { /* * if this one has a value, and it's the highest so far * (or the only one with a value we've found so far), * take it as the maximum so far */ if (cur.menuOrder != nil && (maxVal == nil || cur.menuOrder > maxVal)) maxVal = cur.menuOrder; } /* if we didn't find any values, use 0 as the arbitrary default */ if (maxVal == nil) maxVal = 0; /* go one higher than the maximum of the existing items */ obj.menuOrder = maxVal; } /* add the item to our contents list */ contents += obj; } /* * The menu order. When we're about to show a list of menu items, * we'll sort the list in ascending order of this property, then in * ascending order of title. By default, we set this order value to * be equal to the menu item's sourceTextOrder. This makes the menu * order default to the order of objects as defined in the source. If * some other basis is desired, override topicOrder. */ menuOrder = (sourceTextOrder) /* * Compare this menu object to another, for the purposes of sorting a * list of menu items. Returns a positive number if this menu item * sorts after the other one, a negative number if this menu item * sorts before the other one, 0 if the relative order is arbitrary. * * By default, we'll sort by menuOrder if the menuOrder values are * different, otherwise arbitrarily. */ compareForMenuSort(other) { /* * if one menuOrder value is nil, sort it earlier than the other; * if they're both nil, they sort as equivalent */ if (menuOrder == nil && other.menuOrder == nil) return 0; else if (menuOrder == nil) return -1; else if (other.menuOrder == nil) return 1; /* return the difference of the sort order values */ return menuOrder - other.menuOrder; } /* * Finish initializing our contents list. This will be called on * each MenuObject *after* we've called initializeLocation() on every * object. In other words, every menu will already have been added * to its parent's contents; this can do anything else that's needed * to initialize the contents list. For example, some subclasses * might want to sort their contents here, so that they list their * menus in a defined order. By default, we sort the menu items by * menuOrder; subclasses can override this as needed. */ initializeContents() { /* sort our contents list in the object-defined sorting order */ contents = contents.sort( SortAsc, {a, b: a.compareForMenuSort(b)}); } ; /* * This preinit object makes sure the MenuObjects all have their * contents initialized properly. */ PreinitObject execute() { /* initialize each menu's location */ forEachInstance(MenuObject, { menu: menu.initializeLocation() }); /* do any extra work to initialize each menu's contents list */ forEachInstance(MenuObject, { menu: menu.initializeContents() }); } ; /* ------------------------------------------------------------------------ */ /* * A MenuItem is a given item in the menu tree. In general all you need * to do to use menus is create a tree of MenuItems with titles. * * To display a menu tree, call displayMenu() on the top menu in the * tree. That routine displays the menu and processes user input until * the user dismisses the menu, automatically displaying submenus as * necessary. */ class MenuItem: MenuObject /* the name of the menu; this is listed in the parent menu */ title = '' /* * the heading - this is shown when this menu is active; by default, * we simply use the title */ heading = (title) /* * Display properties. These properties control the way the menu * appears on the screen. By default, a menu looks to its parent * menu for display properties; this makes it easy to customize an * entire menu tree, since changes in the top-level menu will cascade * to all children that don't override these settings. However, each * menu can customize its own appearance by overriding these * properties itself. * * 'fgcolor' and 'bgcolor' are the foreground (text) and background * colors, expressed as HTML color names (so '#nnnnnn' values can be * used to specify RGB colors). * * 'indent' is the number of pixels to indent the menu's contents * from the left margin. This is used only in HTML mode. * * 'fullScreenMode' indicates whether the menu should take over the * entire screen, or limit itself to the space it actually requires. * Full screen mode makes the menu block out any game window text. * Limited mode leaves the game window partially uncovered, but can * be a bit jumpy, since the window changes size as the user * navigates through different menus. */ /* foreground (text) and background colors, as HTML color names */ fgcolor = (location != nil ? location.fgcolor : 'text') bgcolor = (location != nil ? location.bgcolor : 'bgcolor') /* * Foreground and background colors for the top instructions bar. * By default, we use the color scheme of the parent menu, or the * inverse of our main menu color scheme if we're the top menu. */ topbarfg = (location != nil ? location.topbarfg : 'statustext') topbarbg = (location != nil ? location.topbarbg : 'statusbg') /* number of spaces to indent the menu's contents */ indent = (location != nil ? location.indent : '10') /* * full-screen mode: make our menu take up the whole screen (apart * from the instructions bar, of course) */ fullScreenMode = (location != nil ? location.fullScreenMode : true) /* * The keys used to navigate the menus, in order: * * [quit, previous, up, down, select] * * Since multiple keys can be used for the same navigation, the list * is implemented as a List of Lists. Keys must be given as * lower-case in order to match input, since we convert all input * keys to lower-case before matching them. * * In the sublist for each key, we use the first element as the key * name we show in the instruction bar at the top of the screen. * * By default, we use our parent menu's key list, if we have a * parent; if we have no parent, we use the standard keys from the * library messages. */ keyList = (location != nil ? location.keyList : gLibMessages.menuKeyList) /* * the current key list - we'll set this on entry to the start of * each showMenuXxx method, so that we keep track of the actual key * list in use, as inherited from the top-level menu */ curKeyList = nil /* * Title for the link to the previous menu, if any. If the menu has * a parent menu, we'll display this link next to the menu title in * the top instructions/title bar. If this is nil, we won't display * a link at all. Note that this can contain an HTML fragment; for * example, you could use an tag to display an icon here. */ prevMenuLink = (location != nil ? gLibMessages.prevMenuLink : nil) /* * Update our contents. By default, we'll do nothing; subclasses * can override this to manage dynamic menus if desired. This is * called just before the menu is displayed, each time it's * displayed. */ updateContents() { } /* * Get the next menu in our list following the given menu. Returns * nil if we don't find the given menu, or the given menu is the last * menu. */ getNextMenu(menu) { /* find the menu in our contents list */ local idx = contents.indexOf(menu); /* * if we found it, and it's not the last, return the menu at the * next index; otherwise return nil */ return (idx != nil && idx < contents.length() ? contents[idx + 1] : nil); } /* * Get the menu previous tot he given menu. Returns nil if we don't * find the given menu or the given menu is the first one. */ getPrevMenu(menu) { /* find the menu in our contents list */ local idx = contents.indexOf(menu); /* * if we found it, and it's not the first, return the menu at the * prior index; otherwise return nil */ return (idx != nil && idx > 1 ? contents[idx - 1] : nil); } /* get the index in the parent of the given child menu */ getChildIndex(child) { return contents.indexOf(child); } ; /* ------------------------------------------------------------------------ */ /* * MenuTopicItem displays a series of entries successively. This is * intended to be used for displaying something like a list of hints for * a topic. Set menuContents to be a list of strings to be displayed. */ class MenuTopicItem: MenuItem /* the name of this topic, as it appears in our parent menu */ title = '' /* heading, displayed while we're showing this topic list */ heading = (title) /* hyperlink text for showing the next menu */ nextMenuTopicLink = (gLibMessages.nextMenuTopicLink) /* * A list of strings and/or MenuTopicSubItem items. Each one of * these is meant to be something like a single hint on our topic. * We display these items one at a time when our menu item is * selected. */ menuContents = [] /* the index of the last item we displayed from our menuContents list */ lastDisplayed = 1 /* * The maximum number of our sub-items that we'll display at once. * This is only used on interpreters with banner capabilities, and is * ignored in full-screen mode. */ chunkSize = 6 /* we'll display this after we've shown all of our items */ menuTopicListEnd = (gLibMessages.menuTopicListEnd) ; /* ------------------------------------------------------------------------ */ /* * A menu topic sub-item can be used to represent an item in a * MenuTopicItem's list of display items. This can be useful when * displaying a topic must trigger a side-effect. */ class MenuTopicSubItem: object /* * Get the item's text. By default, we just return an empty string. * This should be overridden to return the appropriate text, and can * also trigger any desired side-effects. */ getItemText() { return ''; } ; /* ------------------------------------------------------------------------ */ /* * Long Topic Items are used to print out big long gobs of text on a * subject. Use it for printing long treatises on your design * philosophy and the like. */ class MenuLongTopicItem: MenuItem /* the title of the menu, shown in parent menus */ title = '' /* the heading, shown while we're displaying our contents */ heading = (title) /* either a string to be displayed, or a method returning a string */ menuContents = '' /* * Flag - this is a "chapter" in a list of chapters. If this is set * to true, then we'll offer the options to proceed directly to the * next and previous chapters. If this is nil, we'll simply wait for * acknowledgment and return to the parent menu. */ isChapterMenu = nil /* the message we display at the end of our text */ menuLongTopicEnd = (gLibMessages.menuLongTopicEnd) ; frobtads-1.2.3/tads3/lib/adv3/input.t0000644000175000001440000011550511553035246016464 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * TADS 3 Library: input * * This modules defines functions and objects related to reading input * from the player. */ #include "adv3.h" /* ------------------------------------------------------------------------ */ /* * Keyboard input parameter definition. */ class InputDef: object /* * The prompt function. This is a function pointer (which is * frequently given as an anonymous function) or nil; if it's nil, * we won't show any prompt at all, otherwise we'll call the * function pointer to display a prompt as needed. */ promptFunc = nil /* * Allow real-time events. If this is true, we'll allow real-time * events to interrupt the input; if it's nil, we'll freeze the * real-time clock while reading input. */ allowRealTime = nil /* * Begin the input style. This should do anything required to set * the font to the desired attributes for the input text. By * default, we'll simply display <.inputline> to set up the default * input style. */ beginInputFont() { "<.inputline>"; } /* * End the input style. By default, we'll close the <.inputline> * that we opened in beginInputFont(). */ endInputFont() { "<./inputline>"; } ; /* * Basic keyboard input parameter definition. This class defines * keyboard input parameters with the real-time status and prompt * function specified via the constructor. */ class BasicInputDef: InputDef construct(allowRealTime, promptFunc) { self.allowRealTime = allowRealTime; self.promptFunc = promptFunc; } ; /* ------------------------------------------------------------------------ */ /* * Keyboard input manager. */ inputManager: PostRestoreObject /* * Read a line of input from the keyboard. * * If allowRealTime is true, we'll execute any real-time events that * are already due to run, and then we'll allow the input to be * interrupted by real-time events, if interrupted input is * supported on the local platform. Otherwise, we will not process * any real-time events. * * promptFunc is a callback function to invoke to display the * prompt. This is provided as a callback so that we can re-display * the prompt as necessary after real-time event interruptions. * Note that if real-time interruption is not to be allowed, the * caller can simply display the prompt before calling this routine * rather than passing in a prompt callback, if desired. * * If we're in HTML mode, this will switch into the 'tads-input' * font while reading the line, so this routine should be used * wherever possible rather than calling inputLine() or * inputLineTimeout() directly. */ getInputLine(allowRealTime, promptFunc) { /* read input using a basic InputDef for the given parameters */ return getInputLineExt(new BasicInputDef(allowRealTime, promptFunc)); } /* * Read a line of input from the keyboard - extended interface, * using the InputDef object to define the input parameters. * 'defObj' is an instance of class InputDef, defining how we're to * handle the input. */ getInputLineExt(defObj) { /* make sure the command transcript is flushed */ if (gTranscript != nil) gTranscript.flushForInput(); /* * If a previous input was in progress, cancel it - this must be * a recursive entry from a real-time event that's interrupting * the enclosing input attempt. Simply cancel out the enclosing * read attempt entirely in this case; if and when we return to * the enclosing reader, that reader will start over with a * fresh read attempt at that point. */ cancelInputInProgress(true); /* * Keep going until we finish reading the command. We might * have to try several times, because our attempts might be * interrupted by real-time events. */ for (;;) { local result; local timeout; local t0; /* note the starting time, in case we want to freeze the clock */ t0 = realTimeManager.getElapsedTime(); /* process real-time events, if possible */ timeout = processRealTimeEvents(defObj.allowRealTime); /* show the prompt and any pre-input codes */ inputLineBegin(defObj); getInput: /* * Read the input. (Note that if our timeout is nil, this * will simply act like the ordinary untimed inputLine.) */ result = aioInputLineTimeout(timeout); /* * If we're not allowing real-time event processing, freeze * the clock during the read - set the elapsed game * real-time clock back to the value it had on entry, so * that the input effectively consumes no real time. */ if (!defObj.allowRealTime) realTimeManager.setElapsedTime(t0); /* check the event code from the result list */ switch(result[1]) { case InEvtNoTimeout: /* * the platform doesn't support timeouts - note it for * future reference so that we don't ask for input with * timeout again, then go back to try the input again * without a timeout */ noInputTimeout = true; timeout = nil; goto getInput; case InEvtLine: /* we've finished the current line - end input mode */ inputLineEnd(); /* return the line of text we got */ return result[2]; case InEvtTimeout: /* * We got a timeout without finishing the input line. * This means that we've reached the time when the next * real-time event is ready to execute. Simply continue * looping; we'll process all real-time events that are * ready to go, then we'll resume reading the command. * * Before we proceed, though, notify the command * sequencer (via the command-interrupt pseudo-tag) that * we're at the start of output text after an interrupted * command line input */ "<.commandint>"; break; case InEvtEof: /* * End of file - this indicates that the user has closed * down the application, or that the keyboard has become * unreadable due to a hardware or OS error. * * Write a blank line to the display in an attempt to * flush any partially-entered command line text, then * throw an error to signal the EOF condition. */ "\b"; throw new EndOfFileException(); case InEvtEndQuietScript: /* * End of "quiet" script - this indicates that we've * been reading input from a script file, but we've now * reached the end of that file and are about to return * to reading from the keyboard. * * "Quiet script" mode causes all output to be hidden * while the script is being processed. This means that * we won't have displayed a prompt for the current * line, or updated the status line. We'll * automatically display a new prompt when we loop back * for another line of input, but we have to mark the * current input line as actually ended now for that to * happen. */ inputLineInProgress = nil; inProgressDefObj = nil; /* * update the status line, since the quiet script mode * will have suppressed all status line updates while we * were reading the script, and thus the last update * before this prompt won't have been shown */ statusLine.showStatusLine(); /* back for more */ break; case 'newGuest': /* * Synthetic "new guest" event from the Web UI. This * indicates that a new user has joined the session. The * parameter is the new user's screen name. Announce the * new user's arrival as a real-time event, and go back * to reading input. */ "<.commandint>"; libMessages.webNewUser(result[2]); break; case 'logError': /* * Synthetic "log error" event from the Web UI. The UI * posts this type of an event when an error occurs in an * asynchronous task, where it's not possible to display * an error message directly. */ "<.commandint>\b<>\b"; break; } } } /* * Pause for a MORE prompt. If freezeRealTime is true, we'll stop * the real-time clock; otherwise we'll let it keep running. Even if * we don't freeze the clock, we won't actually process any real-time * events while waiting: the point of the MORE prompt is to allow the * player to read and acknowledge the on-screen display before * showing anything new, so we don't want to allow any output to * result from real-time events that occur while waiting for user * input. If any real-time events come due while we're waiting, * we'll process them when we're done. * * In order to ensure that the display makes sense to the user, we * flush any captured input in the transcript before pausing. We * re-activate transcript capture after the pause if it was active * before. Note that in some cases, this could affect the overall * output for the command, since some commands wait until the very * end of the command to go back and process the entire transcript * for the command. Since we interrupt the transcript, flushing any * output that occurred before the pause, a command that goes back * over its entire output stream at the end of the turn won't be able * to see or modify any of the output that occurred prior to the * pause, since we will have flushed the output to that point. */ pauseForMore(freezeRealTime) { local t0; local wasTranscriptActive = nil; /* * flush any command transcript and turn off transcript capture, * so that we show any pent-up reports before pausing for the * MORE prompt */ if (gTranscript != nil) wasTranscriptActive = gTranscript.flushForInput(); /* * cancel any pending input - we must be interrupting the * pending input with a real-time event */ cancelInputInProgress(true); /* note the starting time, in case we want to freeze the clock */ t0 = realTimeManager.getElapsedTime(); /* run the MORE prompt */ aioMorePrompt(); /* if the transcript was previously active, re-activate it */ if (wasTranscriptActive) gTranscript.activate(); /* * if the caller wanted us to freeze the clock, restore the * elapsed game real time to what it was when we started, so * that the time the player took to acknowledge the MORE prompt * won't count against the elapsed game time; otherwise, process * any real-time events that came due while we were waiting */ if (freezeRealTime) { /* time was frozen - restore the original elapsed time */ realTimeManager.setElapsedTime(t0); } else { /* * time wasn't frozen - check for any events that have come * due since we started waiting, and process them * immediately */ processRealTimeEvents(true); } } /* * Ask for an input file. Freezes the real-time event clock for the * duration of reading the event. */ getInputFile(prompt, dialogType, fileType, flags) { /* * note the game elapsed time before we start - we want to * freeze the real-time clock while we're waiting for the user * to respond, since this system verb exists outside of the * usual time flow of the game */ local origElapsedTime = realTimeManager.getElapsedTime(); /* ask for a file */ local result = aioInputFile(prompt, dialogType, fileType, flags); /* * restore the game real-time counter to what it was before we * started the interactive response */ realTimeManager.setElapsedTime(origElapsedTime); /* return the result from inputFile */ return result; } /* * Ask for input through a dialog. Freezes the real-time clock for * the duration of the dialog display. The arguments are the same as * for the built-in inputDialog() function. */ getInputDialog(icon, prompt, buttons, defaultButton, cancelButton) { /* * note the current elapsed game real time, so we can restore it * after the dialog is done */ local origElapsedTime = realTimeManager.getElapsedTime(); /* show the dialog */ local result = aioInputDialog(icon, prompt, buttons, defaultButton, cancelButton); /* * restore the real-time counter, so that the time spent in the * dialog doesn't count */ realTimeManager.setElapsedTime(origElapsedTime); /* return the dialog result */ return result; } /* * Read a keystroke, processing real-time events while waiting, if * desired. 'allowRealTime' and 'promptFunc' work the same way they * do with getInputLine(). */ getKey(allowRealTime, promptFunc) { local evt; /* get an event */ evt = getEventOrKey(allowRealTime, promptFunc, true); /* * the only event that getEventOrKey will return is a keystroke, * so return the keystroke from the event record */ return evt[2]; } /* * Read an event, processing real-time events while waiting, if * desired. 'allowRealTime' and 'promptFunc' work the same way they * do with getInputLine(). */ getEvent(allowRealTime, promptFunc) { /* read and return an event */ return getEventOrKey(allowRealTime, promptFunc, nil); } /* * Read an event or keystroke. 'allowRealTime' and 'promptFunc' work * the same way they do in getInputLine(). If 'keyOnly' is true, * then we're only interested in keystroke events, and we'll ignore * any other events entered. * * Note that this routine is not generally called directly; callers * should usually call the convenience routines getKey() or * getEvent(), as needed. */ getEventOrKey(allowRealTime, promptFunc, keyOnly) { /* make sure the command transcript is flushed */ if (gTranscript != nil) gTranscript.flushForInput(); /* * Cancel any in-progress input. If there's an in-progress * input, a real-time event must be interrupting the input, * which is recursively invoking us to start a new input. */ cancelInputInProgress(true); /* keep going until we get a keystroke or other event */ for (;;) { local result; local timeout; local t0; /* note the starting time, in case we want to freeze the clock */ t0 = realTimeManager.getElapsedTime(); /* process real-time events, if possible */ timeout = processRealTimeEvents(allowRealTime); /* show the prompt and any pre-input codes */ inputEventBegin(promptFunc); getInput: /* * Read the input. (Note that if our timeout is nil, this * will simply act like the ordinary untimed inputLine.) */ result = aioInputEvent(timeout); /* * If we're not allowing real-time event processing, freeze * the clock during the read - set the elapsed game * real-time clock back to the value it had on entry, so * that the input effectively consumes no real time. */ if (!allowRealTime) realTimeManager.setElapsedTime(t0); /* check the event code from the result list */ switch(result[1]) { case InEvtNoTimeout: /* * the platform doesn't support timeouts - note it for * future reference so that we don't ask for input with * timeout again, then go back to try the input again * without a timeout */ noInputTimeout = true; timeout = nil; goto getInput; case InEvtTimeout: /* * We got a timeout without finishing the input line. * This means that we've reached the time when the next * real-time event is ready to execute. Simply continue * looping; we'll process all real-time events that are * ready to go, then we'll restart the event wait. */ break; case InEvtEof: /* * End of file - this indicates that the user has closed * down the application, or that the keyboard has become * unreadable due to a hardware or OS error. * * Write a blank line to the display in an attempt to * flush any partially-entered command line text, then * throw an error to signal the EOF condition. */ "\b"; throw new EndOfFileException(); case InEvtKey: /* keystroke - finish the input and return the event */ inputEventEnd(); return result; case InEvtHref: /* * Hyperlink activation - if we're allowed to return * events other than keystrokes, finish the input and * return the event; otherwise, ignore the event and keep * looping. */ if (!keyOnly) { inputEventEnd(); return result; } break; default: /* ignore other events */ break; } } } /* * Cancel input in progress. * * If 'reset' is true, we'll clear any input state saved from the * interrupted in-progress editing session; otherwise, we'll retain * the saved editing state for restoration on the next input. * * This MUST be called before calling tadsSay(). Games should * generally never call tadsSay() directly (call the library * function say() instead), so in most cases authors will not need * to worry about calling this on output. * * This MUST ALSO be called before performing any keyboard input. * Callers using inputManager methods for keyboard operations won't * have to worry about this, because the inputManager methods call * this routine when necessary. */ cancelInputInProgress(reset) { /* cancel the interpreter's internal input state */ aioInputLineCancel(reset); /* if we were editing a command line, terminate the editing session */ if (inputLineInProgress) { /* do our normal after-input work */ inputLineEnd(); } /* if we were waiting for event input, note that we are no longer */ if (inputEventInProgress) { /* do our normal after-input work */ inputEventEnd(); } } /* * Process any real-time events that are ready to run, and return the * timeout until the next real-time event. * * If allowRealTime is nil, we won't process real-time events at all; * we'll merely return nil for the timeout to indicate to the caller * that any user input interaction about to be attempted should wait * indefinitely. */ processRealTimeEvents(allowRealTime) { local timeout; /* presume we will not use a timeout */ timeout = nil; /* process real-time events, if allowed */ if (allowRealTime) { local tNext; /* * Process any real-time events that are currently ready to * execute, and note the amount of time until the next * real-time event is ready. */ tNext = realTimeManager.executeEvents(); /* * If there's an event pending, note the interval between the * current time and the event's scheduled time - this will * give us the maximum amount of time we want to wait for the * user to edit the command line before interrupting to * execute the pending event. Ignore this if the platform * doesn't support timeouts to begin with. */ if (tNext != nil && !noInputTimeout) timeout = tNext - realTimeManager.getElapsedTime(); } /* return the timeout until the next real-time event */ return timeout; } /* * Begin reading key/event input. We'll cancel any report gatherer * so that prompt text shows immediately, and show the prompt if * desired. */ inputEventBegin(promptFunc) { /* if we're not continuing previous input, show the prompt */ if (!inputEventInProgress) { inputBegin(promptFunc); /* note that we're in input mode */ inputEventInProgress = true; } } /* * End keystroke/event input. */ inputEventEnd() { /* if input is in progress, terminate it */ if (inputEventInProgress) { /* note that we're no longer reading an event */ inputEventInProgress = nil; } } /* * Begin command line editing. If we're in HTML mode, we'll show * the appropriate codes to establish the input font. */ inputLineBegin(defObj) { /* notify the command sequencer that we're reading a command */ "<.commandbefore>"; /* if we're not resuming a session, set up a new session */ if (!inputLineInProgress) { /* begin input */ inputBegin(defObj.promptFunc); /* switch to input font */ defObj.beginInputFont(); /* note that we're in input mode */ inputLineInProgress = true; /* remember the parameter object for this input */ inProgressDefObj = defObj; } } /* * End command line editing. If we're in HTML mode, we'll show the * appropriate codes to close the input font. */ inputLineEnd() { /* if input is in progress, terminate it */ if (inputLineInProgress) { /* note that we're no longer reading a line of input */ inputLineInProgress = nil; /* end input font mode */ inProgressDefObj.endInputFont(); /* notify the command sequencer that we're done reading */ "<.commandafter>"; /* * tell the main text area's output stream that we just * ended an input line */ mainOutputStream.inputLineEnd(); /* forget the parameter object for the input */ inProgressDefObj = nil; } } /* * Begin generic input. Cancels command report list capture, and * shows the prompt if given. */ inputBegin(promptFunc) { /* * Turn off command transcript capture, if it's active. Once * we're soliciting input interactively, we can no longer * usefully capture the text output of commands, but this is fine * because we must be doing something for which capture isn't * important anyway. Reporting capture is used for things like * selecting the kind of result to show, which clearly isn't a * factor for actions involving interactive input. */ if (gTranscript != nil) gTranscript.flushForInput(); /* if we have a prompt, display it */ if (promptFunc != nil) (promptFunc)(); } /* receive post-restore notification */ execute() { /* * Reset the inputLine state. If we had any previously * interrupted input from the current interpreter session, forget * it by canceling and resetting the input line. If we had an * interrupted line in the session being restored, forget about * that, too. */ aioInputLineCancel(true); inputLineInProgress = nil; inputEventInProgress = nil; /* * Clear the inputLineTimeout disabling flag - we might be * restoring the game on a different platform from the one where * the game started, so we might be able to use timed command * line input even if we didn't when we started the game. By * clearing this flag, we'll check again to see if we can * perform timed input; if we can't, we'll just set the flag * again, so there will be no harm done. */ noInputTimeout = nil; } /* * Flag: command line input is in progress. If this is set, it means * that we interrupted command-line editing by a timeout, so we * should not show a prompt the next time we go back to the keyboard * for input. */ inputLineInProgress = nil /* the InputDef object for the input in progress */ inProgressDefObj = nil /* flag: keystroke/event input is in progress */ inputEventInProgress = nil /* * Flag: inputLine does not support timeouts on the current platform. * We set this when we get an InEvtNoTimeout return code from * inputLineTimeout, so that we'll know not to try calling again with * a timeout. This applies to the current interpreter only, so we * must ignore any value restored from a previously saved game, since * the game might have been saved on a different platform. * * Note that if this value is nil, it means only that we've never * seen an InEvtNoTimeout return code from inputLineEvent - it does * NOT mean that timeouts are supported locally. * * We assume that the input functions are uniform in their treatment * of timeouts; that is, we assume that if inputLineTimeout supports * timeout, then so does inputEvent, and that if one doesn't support * timeout, the other won't either. */ noInputTimeout = nil ; /* ------------------------------------------------------------------------ */ /* * Read a command line from the player. Displays the main command * prompt and returns a line of input. * * We process any pending real-time events before reading the command. * If the local platform supports real-time command-line interruptions, * we'll continue processing real-time events as they occur in the * course of command editing. */ readMainCommand(which) { local str; /* execute any pre-command-prompt daemons */ eventManager.executePrompt(); /* * Read a line of input, allowing real-time event processing, and * return the line of text we read. Use the appropriate main * command prompt for the given prompt mode. */ str = inputManager.getInputLine( true, {: gLibMessages.mainCommandPrompt(which) }); /* return the string we read */ return str; } /* ------------------------------------------------------------------------ */ /* * End-of-file exception - this is thrown when readMainCommand() * encounters end of file reading the console input. */ class EndOfFileException: Exception ; /* ------------------------------------------------------------------------ */ /* * 'Quitting' exception. This isn't an error - it merely indicates that * the user has explicitly asked to quit the game. */ class QuittingException: Exception ; /* ------------------------------------------------------------------------ */ /* * Base class for command input string preparsers. * * Preparsers must be registered in order to run. During * preinitialization, we will automatically register any existing * preparser objects; preparsers that are created dynamically during * execution must be registered explicitly, which can be accomplished by * inheriting the default constructor from this class. */ class StringPreParser: PreinitObject /* * My execution order number. When multiple preparsers are * registered, we'll run the preparsers in ascending order of this * value (i.e., smallest runOrder goes first). */ runOrder = 100 /* * Do our parsing. Each instance should override this method to * define the parsing that it does. * * 'str' is the string to parse, and 'which' is the rmcXxx enum * giving the type of command we're working with. * * This method returns a string or nil. If the method returns a * string, the caller will forget the original string and work from * here on out with the new version returned; this allows the method * to rewrite the original input as desired. If the method returns * nil, it means that the string has been fully handled and that * further parsing of the same string is not desired. */ doParsing(str, which) { /* return the original string unchanged */ return str; } /* * construction - when we dynamically create a preparser, register * it by default */ construct() { /* register the preparser */ StringPreParser.registerPreParser(self); } /* run pre-initialization */ execute() { /* register the preparser if it's not already registered */ StringPreParser.registerPreParser(self); } /* register a preparser */ registerPreParser(pp) { /* if the preparser isn't already in our list, add it */ if (regList.indexOf(pp) == nil) { /* append this new item to the list */ regList.append(pp); /* the list is no longer sorted */ regListSorted = nil; } } /* * Class method - Run all preparsers. Returns the result of * successively calling each preparser on the given string. */ runAll(str, which) { /* * if the list of preparsers isn't sorted, sort it in ascending * order of execution order number */ if (!regListSorted) { /* sort the list */ regList.sort(SortAsc, {x, y: x.runOrder - y.runOrder}); /* the list is now sorted */ regListSorted = true; } /* run each preparser */ foreach (local cur in regList) { /* run this preparser */ str = cur.doParsing(str, which); /* * if the result is nil, it means that the string has been * fully handled, so we need not run any further preparsing */ if (str == nil) return nil; } /* return the result of the series of preparsing steps */ return str; } /* class property containing the list of registered parsers */ regList = static new Vector(10) /* class property - the registration list has been sorted */ regListSorted = nil ; /* ------------------------------------------------------------------------ */ /* * The "comment" pre-parser. If the command line starts with a special * prefix string (by default, "*", but this can be changed via our * commentPrefix property), this pre-parser intercepts the command, * treating it as a comment from the player and otherwise ignoring the * entire input line. The main purpose is to give players a way to put * comments into recorded transcripts, as notes to themselves when later * reviewing the transcripts or as notes to the author when submitting * play-testing feedback. */ commentPreParser: StringPreParser doParsing(str, which) { /* get the amount of leading whitespace, so we can ignore it */ local sp = rexMatch(leadPat, str); /* * if the command line starts with the comment prefix, treat it * as a comment */ if (str.substr(sp + 1, commentPrefix.length()) == commentPrefix) { /* * It's a comment. * * If a transcript is being recorded, simply acknowledge the * comment; if not, acknowledge it, but with a warning that * the comment isn't being saved anywhere */ if (scriptStatus.scriptFile != nil) gLibMessages.noteWithScript; else if (warningCount++ == 0) gLibMessages.noteWithoutScriptWarning; else gLibMessages.noteWithoutScript; /* * Otherwise completely ignore the command line. To do this, * simply return nil: this tells the parser that the command * has been fully handled by the preparser. */ return nil; } else { /* it's not a command - return the string unchanged */ return str; } } /* * The comment prefix. You can change this to any character, or to * any sequence of characters (longer sequences, such as '//', will * work fine). If a command line starts with this exact string (or * starts with whitespace followed by this string), we'll consider * the line to be a comment. */ commentPrefix = '*' /* * The leading-whitespace pattern. We skip any text that matches * this pattern at the start of a command line before looking for the * comment prefix. * * If you don't want to allow leading whitespace before the comment * prefix, you can simply change this to '' - a pattern consisting of * an empty string always matches zero characters, so it will prevent * us from skipping any leading charactres in the player's input. */ leadPat = static new RexPattern('*') /* warning count for entering comments without SCRIPT in effect */ warningCount = 0 /* * Use a lower execution order than the default, so that we run * before most other pre-parsers. Most other pre-parsers are written * to handle actual commands, so it's usually just a waste of time to * have them look at comments at all - and can occasionally be * problematic, since the free-form text of a comment could confuse a * pre-parser that's expecting a more conventional command format. * When the comment pre-parser detects a comment, it halts any * further processing of the command - so by running ahead of other * pre-parsers, we'll effectively bypass other pre-parsers when we * detect a comment. */ runOrder = 50 ; /* ------------------------------------------------------------------------ */ /* * Read a line of text and return the token list and the original text. * We keep going until a non-empty line of text is read. * * 'which' is one of the rmcXxx enum values specifying what kind of * command line we're reading. * * The return value is a list of two elements. The first element is the * string entered, and the second element is the token list. */ readMainCommandTokens(which) { local str; local toks; /* keep going until we get a non-empty command line */ for (;;) { /* read a command line */ str = readMainCommand(which); /* run any preparsing desired on the string */ str = StringPreParser.runAll(str, which); /* * if preparsing returned nil, it means that the preparser fully * handled the string - simply return nil to tell the caller * that its work is done */ if (str == nil) return nil; try { /* tokenize the command string */ toks = cmdTokenizer.tokenize(str); } catch (TokErrorNoMatch tokExc) { /* * Invalid tokens in the response - complain about it. Flag * the error as being in the first character of the * remaining string, since that's the character for which we * could find no match. */ gLibMessages.invalidCommandToken(tokExc.curChar_.htmlify()); /* go back for another input line */ continue; } /* if we got a non-empty token list, return it */ if (toks.length() != 0) return [str, toks]; /* show the empty-command reply */ gLibMessages.emptyCommandResponse(); } } frobtads-1.2.3/tads3/lib/file.t0000644000175000001440000001435412006317055015402 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2001, 2006 Michael J. Roberts * * This file is part of TADS 3. * * This module defines classes and constants related to the File * intrinsic class. In particular, this module defines the Exception * subclasses thrown by File methods. */ #include #include /* ------------------------------------------------------------------------ */ /* * File status information. This is returned from file.getFileInfo(). */ class FileInfo: object construct(typ, siz, ctime, mtime, atime, target, attrs, ...) { fileType = typ; fileSize = siz; fileCreateTime = ctime; fileModifyTime = mtime; fileAccessTime = atime; fileLinkTarget = target; fileAttrs = attrs; /* for convenience, note if it's a directory and/or special link */ isDir = (typ & FileTypeDir) != 0; specialLink = (typ & (FileTypeSelfLink | FileTypeParentLink)); } /* is this file a directory? */ isDir = nil /* * Is this a special link directory? This is FileTypeSelfLink for a * directory link to itself; it's FileTypeParentLink for a directory * link to the parent; it's zero for all other files. On Windows and * Unix, these flags will be set for the special "." and ".." * directories, respectively. These flags only apply to the * *system-defined* special links; they aren't set for user-created * links that happen to point to self or parent. This is zero for * all other files. */ specialLink = 0 /* * Link target. If the file is a symbolic link, this contains a * string giving the target file's path. This is the direct target * of this link, which might itself be another link. */ fileLinkTarget = nil /* * type of the file, as a combination of FileTypeXxx bit flags (see * filename.h) */ fileType = 0 /* * file attributes, as a combination of FileAttrXxx bit flags (see * filename.h) */ fileAttrs = 0 /* size of the file in bytes */ fileSize = 0 /* * The file's time of creation, last modification, and last access, * as Date objects. On some systems, these timestamps might not all * be available; an item that's not available is set to nil. */ fileCreateTime = nil fileModifyTime = nil fileAccessTime = nil ; export FileInfo 'File.FileInfo'; /* ------------------------------------------------------------------------ */ /* * File Exception classes. All File exceptions derive from FileException, * to allow for generic 'catch' clauses which catch any file-related * error. */ class FileException: Exception displayException() { "file error"; } ; /* * File not found - this is thrown when attempting to open a file for * reading and the file doesn't exist or can't be opened (because the user * doesn't have privileges to read the file, or the file is already being * used by another user, for example). */ class FileNotFoundException: FileException displayException() { "file not found"; } ; /* * File creation error - this is thrown when attempting to open a file for * writing and the file can't be created; this can happen because the disk * or the directory is full, due to privilege failures, or due to sharing * violations, among other reasons. */ class FileCreationException: FileException displayException() { "cannot create file"; } ; /* * File cannot be opened - this is thrown when attempting to open a file * for reading and writing but the file can't be opened. This can happen * for numerous reasons: sharing violations, privilege failures, lack of * space on the disk or in the directory. */ class FileOpenException: FileException displayException() { "cannot open file"; } ; /* * File synchronization exception. This is thrown when an operation * (such as a read or write) is attempted during normal execution on a * file object that was originally opened during pre-initialization. A * file object created during pre-initialization can't be used to access * the file during ordinary execution, since the state of the external * file might have changed since the pre-init session ended. In such * cases, a new file object must be created instead. */ class FileSyncException: FileException displayException() { "file synchronization error"; } ; /* * File closed - this is thrown when an operation is attempted on a file * that has already been explicitly closed. */ class FileClosedException: FileException displayException() { "operation attempted on closed file"; } ; /* * File I/O exception - this is thrown when a read or write operation on a * file fails. This can indicate, for example, that the device containing * the file is full, or that a physical media error occurred. */ class FileIOException: FileException displayException() { "file I/O error"; } ; /* * File mode error - this is thrown when an attempted operation is * incompatible with the file's mode. This is thrown under these * conditions: * * - writing to a file opened for read-only access *. - reading from a file opened for write-only access *. - calling readFile or writeFile on a raw-mode file *. - calling readBytes or writeBytes on a non-raw-mode file */ class FileModeException: FileException displayException() { "invalid file mode"; } ; /* * File safety error - this is thrown when an attempted "open" operation * is prohibited by the current file safety level set by the user. */ class FileSafetyException: FileException displayException() { "access to file blocked by user-specified file safety level"; } ; /* export the file exceptions for use by the intrinsic class */ export FileNotFoundException 'File.FileNotFoundException'; export FileCreationException 'File.FileCreationException'; export FileOpenException 'File.FileOpenException'; export FileIOException 'File.FileIOException'; export FileSyncException 'File.FileSyncException'; export FileClosedException 'File.FileClosedException'; export FileModeException 'File.FileModeException'; export FileSafetyException 'File.FileSafetyException'; frobtads-1.2.3/tads3/lib/webui.t0000644000175000001440000036136312136227662015613 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2010 Michael J. Roberts. All Rights Reserved. * * This module defines some useful helper functions for implementing a * TADS game with a Web-based user interface. */ #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ /* * write a message to the system debug log */ #define DbgMsg(msg) t3DebugTrace(T3DebugLog, msg) /* ------------------------------------------------------------------------ */ /* * Session timeout settings. Times are in milliseconds. */ /* * Housekeeping interval. We'll wait at least this long between * housekeeping passes. */ #define HousekeepingInterval 15000 /* * Session startup timeout. When we first start up, we'll give our first * client this long to establish a connection. If we don't hear anything * from a client within this interval, we'll assume that the client * exited before it had a chance to set up its first connection, so we'll * terminate the server. */ #define SessionStartupTimeout 90000 /* * Ongoing session timeout. After we've received our first connection, * we'll terminate the server if we go this long without any active * connections. */ #define SessionTimeout 120000 /* * Event request timeout. After an event request from a client has been * sitting in the queue for longer than this limit, we'll send a "keep * alive" reply to let the client know that we're still here. The client * will just turn around and send a new request. This periodic handshake * lets the client know we're still alive, and vice versa. */ #define EventRequestTimeout 90000 /* * Client session timeout. If we haven't seen a request from a given * client within this interval, we'll assume that the client has * disconnected, and we'll drop the client session state on this end. */ #define ClientSessionTimeout 60000 /* ------------------------------------------------------------------------ */ /* * Some handy Vector extensions */ modify Vector push(ele) { append(ele); } pop() { return getAndRemove(length()); } shift() { return getAndRemove(1); } getAndRemove(idx) { /* get the element */ local ret = self[idx]; /* remove it */ removeElementAt(idx); /* return the popped element */ return ret; } clear() { if (length() > 0) removeRange(1, length()); } ; /* ------------------------------------------------------------------------ */ /* * HTTPRequest extensions */ modify HTTPRequest /* * Send a reply, catching "socket disconnect" exceptions. In most * cases, server objects will want to use this method rather than the * native sendReply() so that they won't have to handle disconnect * exceptions manually. * * Disconnect exceptions are common with HTTP: the protocol is * stateless by design, so clients can close their sockets at any * time. Most modern browsers use HTTP 1.1, which allows the client * to maintain an open socket indefinitely and reuse it serially for * multiple requests, but browsers tend to close these reusable * sockets after a few minutes of inactivity. * * When this routine encounters a disconnected socket, it deletes the * client session record for the request, and then otherwise ignores * the error. This generally makes the client's socket management * transparent to the server, since if the client is still running * they'll just connect again with a new socket and retry any lost * requests. */ sendReplyCatch(reply, contType?, stat?, headers?) { /* catch any errors */ try { /* try sending the reply */ sendReply(reply, contType, stat, headers); } catch (SocketDisconnectException sde) { /* notify the associated client session of the disconnect */ local client = ClientSession.find(self); if (client != nil) client.checkDisconnect(); } } ; /* ------------------------------------------------------------------------ */ /* * Web UI Session object. This keeps track of miscellaneous items * associated with the game session. */ transient webSession: object /* * Get the full URL to the given resource. */ getFullUrl(resname) { return 'http://<>:<< server.getPortNum()>><>'; } /* * Connect to the UI. By default, we ask the webMainWin object to * establish a connection, and we save the server object internally * for future reference. */ connectUI(srv) { /* connect the UI to the main window object */ webMainWin.connectUI(srv); /* save a reference to the server object */ server = srv; /* * set the zero point for the connection time - for timeout * purposes, the countdown starts now, since we obviously can't * have a connection before this point */ lastClientTime = getTime(GetTimeTicks); } /* * The session key. This identifies the server as a whole, and is * essentially an authentication mechanism that lets clients prove * they got our address from an authorized source (rather than just * stumbling across it via a port scan, say). Clients must hand this * to us on each request, either via a URL query parameter or via a * cookie. The normal setup (via WebResourceInit) is for the client * to send us the key as a URL parameter on the initial request, at * which point we'll pass it back as a set-cookie, removing the need * for the client to include the key in subsequent URLs. * * The key is just a random number that's long enough that an * interloper couldn't hope to guess it. We generate this on the * first evaluation, and it remains fixed at that point for as long * as we're running. */ sessionKey = (sessionKey = generateRandomKey()) /* * The collaborative session key. This is a secondary session key * that allows additional users to connect to the session for * collaborative play. */ collabKey = (collabKey = generateRandomKey()) /* * Validate a session key sent from the client */ validateKey(req, query) { /* get the key, either from the query string or from a cookie */ local rkey = query['TADS_session']; if (rkey == nil) rkey = req.getCookie('TADS_session'); /* if there's no key, it's an error */ if (rkey == nil) { /* no key -> bad request */ req.sendReply(400); return nil; } /* the key has to match either the main key or the collab key */ if (rkey != sessionKey && rkey != collabKey) { /* invalid key -> forbidden */ req.sendReply(403); return nil; } /* the session key is valid */ return true; } /* * The launcher's game ID. This is the ID passed from the web server * that launched the game, to let us know how the game is identified * in the launcher database. This is typically an IFDB TUID string. */ launcherGameID = nil /* * The launcher's user name. This is passed from the web server that * launched the game, to let us know the host user's screen name. We * use this as the user's default screen name in multi-user games. */ launcherUsername = 'Host' /* * The primary storage server session ID, for the user who launched * the server. If the user who launched the game logged in to a * cloud storage server, this is the session ID that we use to * transact business with the server on behalf of this logged-in * user. This token identifies and authenticates the user, but it's * ephemeral and it's only valid for the current game server session, * so it's not quite like a password. This is the session for the * launch user only; if other collaborative users join, they can get * their own session IDs that will allow them to store files under * their own private user folders on the server. */ storageSID = nil /* * Get the collaborative player launch URL. This is a URL that the * host can send to other players who wish to join the session as * collaborative users. */ getCollabUrl() { /* return the main window URL */ return webSession.getFullUrl( '<>?TADS_session=<>'); } /* list of active client sessions (ClientSession objects) */ clientSessions = static new transient Vector() /* add a client session */ addClient(s) { clientSessions.append(s); lastClientTime = getTime(GetTimeTicks); everHadClient = true; } /* remove a client session */ removeClient(s) { clientSessions.removeElement(s); lastClientTime = getTime(GetTimeTicks); } /* the HTTPServer object running our web session */ server = nil /* * Run housekeeping tasks. The network event processor calls this * periodically to let us perform background cleanup tasks. Returns * the system tick time of the next housekeeping run. */ housekeeping() { /* * if it hasn't been long enough yet since the last housekeeping * run, skip this */ if (getTime(GetTimeTicks) < hkTime) return hkTime; /* send keep-alives and check for dead client sessions */ clientSessions.forEach(function(s) { /* send keep-alives to aged-out event requests */ s.sendKeepAlive(); /* check to see if the client has disconnected */ s.checkDisconnect(); }); /* if there are no client sessions, check for server termination */ if (clientSessions.length() == 0) { /* * There are no clients connected. If we've exceeded the * maximum interval for running without a client, shut down * the server. Use the initial connection time limit if * we've never had a client, otherwise use the ongoing idle * time limit. */ local limit = everHadClient ? SessionTimeout : SessionStartupTimeout; if (getTime(GetTimeTicks) > lastClientTime + limit) { /* no clients - shut down */ throw new QuittingException(); } } else { /* we have a session as of right now */ lastClientTime = getTime(GetTimeTicks); } /* update the housekeeping timer */ return hkTime = getTime(GetTimeTicks) + HousekeepingInterval; } /* system time (ms ticks) of next scheduled housekeeping pass */ hkTime = 0 /* the last time we noticed that we had a client connected */ lastClientTime = 0 /* have we ever had a client connection? */ everHadClient = nil ; /* * Generate a random key. This returns a 128-bit random number as a hex * string. This is designed for ephemeral identifiers, such as session * keys. */ generateRandomKey() { /* generate a long random hex string */ return rand('xxxxxxxx%-xxxx%-xxxx%-xxxxxxxxxxxx%-xxxx'); } /* ------------------------------------------------------------------------ */ /* * Client session. This represents a connection to one browser (or other * client application). Each browser client is a separate session, so we * create one instance of this class per connected browser. Note that * browser instances don't necessarily represent different users - a * single user could open multiple browser windows on the same server. * * We identify each browser instance via a session cookie, which we * establish when the client connects. The browser sends the cookie with * each subsequent request, allowing us to tie the request to the browser * session we previously set up. */ class ClientSession: object construct(skey, ssid) { /* remember my session key and storage server SID */ storageSID = ssid; /* note if we're a secondary (collaborative play) user */ isPrimary = (skey == webSession.sessionKey); /* add me to the master list of sessions */ webSession.addClient(self); /* note our last activity time */ updateEventTime(); /* create a UI preferences object */ uiPrefs = new WebUIPrefs(self); } /* the UI preferences object for this session */ uiPrefs = nil /* * The client's "screen name" - this is the user-visible name that * we'll show other users to identify commands and chat messages * entered by this client. */ screenName = '' /* set the default screen name for a client */ setDefaultScreenName() { /* * If this is the primary, the name is the launching user's * screen name. If not, it's 'Guest N', where N is the number of * guest connections we have. */ screenName = (isPrimary ? webSession.launcherUsername : 'Guest <>'); } /* the client's IFDB user ID (a "TUID"), if logged in to IFDB */ ifdbTuid = nil /* the list of pending event requests from this client */ pendingReqs = perInstance(new Vector()) /* * The client's event queue. When a server-to-client event occurs, * we post it to each current client's queue. When the client sends * a get-event request, we satisfy it out of this queue. */ pendingEvts = perInstance(new Vector()) /* * The client session key. This identifies the client across * requests. We send this to the client as a cookie when they * connect, so we get it back on each request. */ clientKey = perInstance(generateRandomKey()) /* * The storage server session key for the user connected to this * session, if any. We can have multiple users logged in to the game * in collaborative play mode, each with their own separate storage * server session. This allows each user to have their own private * preference settings, saved games, etc. */ storageSID = nil /* * Am I the primary player? This is true if the player connected * using the primary session key. Collaborative players join through * the separate collaborative session key. */ isPrimary = nil /* * Is this session alive? When we detect that the client has * disconnected, we'll set this to nil. When waiting for a client in * a modal event loop, this can be used to terminate the wait if the * client disconnects. */ isAlive = true /* * Last request time, in system ticks (ms). We use this to determine * how long it's been since we've heard from the client, for timeout * purposes. This is updated any time we receive a command or event * request from the client, and each time we successfully send an * event reply. */ lastEventTime = 0 /* update the last event time for this client */ updateEventTime() { lastEventTime = getTime(GetTimeTicks); } /* class method: broadcast an event message to all connected clients */ broadcastEvent(msg) { /* send the event to each client in our active list */ webSession.clientSessions.forEach({ c: c.sendEvent(msg) }); } /* send an event to this client */ sendEvent(msg) { /* enqueue the event, then match it to a request if possible */ pendingEvts.push(msg); processQueues(); } /* receive an event request from the client */ requestEvent(req) { /* enqueue the request, then match it to an event if possible */ pendingReqs.push(new ClientEventRequest(req)); processQueues(); } /* flush outstanding events for this client */ flushEvents() { /* discard any queued events */ pendingEvts.clear(); /* send no-op replies to any pending event requests */ while (pendingReqs.length() > 0) { /* pull out the first request */ local req = pendingReqs.shift(); /* * Send a reply. Ignore any errors: the client probably * canceled their side of the socket already, so we'll * probably get a socket error sending the reply. Since * we're just canceling the request anyway, this is fine. */ try { /* send a no-op reply */ req.req.sendReply('', 'text/xml', 200); /* update the successful communications time */ updateEventTime(); } catch (Exception exc) { /* * ignore errors - the client probably canceled their * side of the socket already, so we'll probably fail * sending the reply */ } } } /* * Send a keep-alive reply to each pending request from this client * that's been waiting for longer than the timeout interval. * * Javascript clients in principle will wait indefinitely for an * XmlHttpRequest to complete, but in practice browsers tend to set * fairly long but finite time limits. If the time limit is exceeded * for a request, the client will fail the request with an error. To * prevent this, our main event loop (processNetRequests) * periodically calls this routine if no other events have occurred * recently. We'll clear out the pending event request queue for * each client by sending a no-op reply to each event. This tells * the client that the server is still alive and connected but has * nothing new to report. */ sendKeepAlive() { /* * Send no-op replies to any requests that have been in the queue * for longer than the maximum event wait interval. New requests * are added to the end of the queue, so the first item in the * queue is the oldest. */ local t = getTime(GetTimeTicks); while (pendingReqs.length() > 0 && t >= pendingReqs[1].reqTimeout) { /* pull out the oldest request */ local req = pendingReqs.shift(); try { /* send a no-op reply */ req.req.sendReply( '', 'text/xml', 200, ['Cache-control: no-store, no-cache, ' + 'must-revalidate, post-check=0, pre-check=0', 'Pragma: no-cache', 'Expires: Mon, 26 Jul 1997 05:00:00 GMT']); /* successful communications, so note the last up time */ updateEventTime(); } catch (Exception exc) { /* couldn't send the reply - consider disconnecting */ checkDisconnect(); } } } /* broadcast a downloadable file to all clients */ broadcastDownload(desc) { /* add the download to all clients */ webSession.clientSessions.forEach({c: c.addDownload(desc)}); } /* add a download to this client */ addDownload(desc) { /* add the download to the table */ downloads[desc.resName] = desc; } /* * Cancel a downloadable file. Removes the file from the download * list and notifies the client that the file is no longer available. */ cancelDownload(desc) { /* remove the file from our table */ downloads.removeElement(desc.resName); /* tell the client that the file is no longer available */ webMainWin.sendWinEventTo( '<>', self); } /* this client's list of downloadable temporary files */ downloads = perInstance(new LookupTable()) /* get a list of all of my downloadable files */ allDownloads() { return downloads.valsToList(); } /* process the request and response queues */ processQueues() { /* keep going as long as we have requests and responses to pair up */ while (pendingReqs.length() > 0 && pendingEvts.length() > 0) { /* pull the first element out of each list */ local req = pendingReqs.shift(); local evt = pendingEvts[1]; /* * Answer the request with the event. Since this is an API * request masquerading as a page view, we need to generate a * live reply to each new request to the same virtual * resource path. So, include some headers to tell the * browser and any proxies not to cache the reply. */ try { /* send the reply */ req.req.sendReply( evt, 'text/xml', 200, ['Cache-control: no-store, no-cache, ' + 'must-revalidate, post-check=0, pre-check=0', 'Pragma: no-cache', 'Expires: Mon, 26 Jul 1997 05:00:00 GMT']); /* we've successfully sent the event - discard it */ pendingEvts.shift(); /* update the successful communications time */ updateEventTime(); } catch (SocketDisconnectException sde) { /* the socket has been disconnected - consider disconnecting */ checkDisconnect(); } catch (Exception exc) { /* * Ignore other errors, since we can just turn around and * send the event again in response to the next event * request from the client - no information will be lost, * since we still have the event in the queue. */ } } } /* wait for the queues to empty in preparation for shutting down */ shutdownWait(timeout) { /* process network requests until all clients disconnect */ processNetRequests({: webSession.clientSessions.length() == 0 }, timeout); } /* * Check to see if the client is still alive. If the client has no * pending event requests, and we haven't heard from the client in * more than the client session timeout interval, assume the client * is no longer connected and kill the session object. * * This should be called whenever a sending a reply to a request * fails with a Socket Disconnect exception. We also run this * periodically during routine housekeeping to check for clients that * haven't even bothered to send a request. */ checkDisconnect() { /* if we don't have any pending requests, kill the session */ if (pendingReqs.length() == 0 && getTime(GetTimeTicks) >= lastEventTime + ClientSessionTimeout) { /* remove the client session from our master list */ webSession.removeClient(self); /* the session is now dead */ isAlive = nil; } } /* * Class method: forcibly disconnect all clients. This simply * deletes the list of active clients and deletes any pending events * in their queues. This doesn't actually terminate their network * connections, but simply clears out any pending work for each * client that we've initiated on the server side. */ disconnectAll() { /* delete all pending events for each client */ webSession.clientSessions.forEach({ s: s.pendingReqs.clear() }); /* delete the client list */ webSession.clientSessions.clear(); } /* * Class method: Find a client session, given the session key or an * HTTPRequest object. */ find(key) { /* * if the key is given as an HTTPRequest, find the key by URL * parameter or cookie */ if (dataType(key) == TypeObject && key.ofKind(HTTPRequest)) { /* get the request */ local req = key; /* try the URL parameters and cookies */ if ((key = req.getQueryParam('TADS_client')) == nil && (key = req.getCookie('TADS_client')) == nil) { /* there's no client key, so there's no client */ return nil; } } /* find the client that matches the key string */ return webSession.clientSessions.valWhich({ x: x.clientKey == key }); } /* * Get the primary session. This is the session for the original * initiating user (the "host" in a multi-user game). */ getPrimary() { /* scan for a session with the host session key */ return webSession.clientSessions.valWhich({ x: x.isPrimary }); } ; /* ------------------------------------------------------------------------ */ /* * Client event request. Each client session object keeps a queue of * pending event requests, representing incoming "GET /webui/getEvent" * requests that have yet to be answered. */ class ClientEventRequest: object construct(req) { self.req = req; reqTimeout = getTime(GetTimeTicks) + EventRequestTimeout; } /* the underlying HTTPRequest object */ req = nil /* * The system time (ms ticks) when the request times out. If we * don't have an actual event to send in response by this time, the * housekeeper will generate a no-op reply just to let the client * know that we're still here. */ reqTimeout = 0 ; /* ------------------------------------------------------------------------ */ /* * A WebResource is a virtual file accessible via the HTTP server. Each * resource object has a path, which can be given as a simple string that * must be matched exactly, or as a RexPattern object with a regular * expression to be matched. Each object also has a "processRequest" * method, which the server invokes to answer the request when the path * is matched. */ class WebResource: object /* * The virtual path to the resource. This is the apparent URL path * to this resource, as seen by the client. * * URL paths follow the Unix file system conventions in terms of * format, but don't confuse the virtual path with an actual file * system path. The vpath doesn't have anything to do with the disk * file system on the server machine or anywhere else. That's why we * call it "virtual" - it's merely the apparent location, from the * client's perspective. * * When the server receives a request from the client, it looks at * the URL sent by the client to determine which WebResource object * should handle the request. The server does this by matching the * resource path portion of the URL to the virtual path of each * WebResource, until it finds a WebResource that matches. The * resource path in the URL is the part of the URL following the * domain, and continuing up to but not including any "?" query * parameters. The resource path always starts with a slash "/". * For example, for the URL "http://192.168.1.15/test/path?param=1", * the resource path would be "/test/path". * * The virtual path can be given as a string or as a RexPattern. If * it's a string, a URL resource path must match the virtual path * exactly, including upper/lower case. If the virtual path is given * as a RexPattern, the URL resource path will be matched to the * pattern with the usual regular expression rules. */ vpath = '' /* * Process the request. This is invoked when we determine that this * is the highest priority resource object matching the request. * 'req' is the HTTPRequest object; 'query' is the parsed query data * as returned by req.parseQuery(). The query information is * provided for convenience, in case the result depends on the query * parameters. */ processRequest(req, query) { /* by default, just send an empty HTML page */ req.sendReply('TADS', 'text/html', 200); } /* * The priority of this resource. If the path is given as a regular * expression, a given request might match more than one resource. * In such cases, the matching resource with the highest priority is * the one that's actually used to process the request. */ priority = 100 /* * The group this resource is part of. This is the object that * "contains" the resource, via its 'contents' property; any object * will work here, since it's just a place to put the contents list * for the resource group. * * By default, we put all resources into the mainWebGroup object. * * The point of the group is to allow different servers to use * different sets of resources, or to allow one server to use * different resource sets under different circumstances. When a * server processes a request, it does so by looking through the * 'contents' list for a group of its choice. */ group = mainWebGroup /* * Determine if this resource matches the given request. 'query' is * the parsed query from the request, as returned by * req.parseQuery(). 'req' is the HTTPRequest object representing * the request; you can use this to extract more information from the * request, such as cookies or the client's network address. * * This method returns true if the request matches this resource, nil * if not. * * You can override this to specify more complex matching rules than * you could achieve just by specifying the path string or * RexPattern. For example, you could make the request conditional * on the time of day, past request history, cookies in the request, * parameters, etc. */ matchRequest(query, req) { /* get the query path */ local qpath = query[1]; /* by default, we match GET */ local verb = req.getVerb().toUpper(); if (verb != 'GET' && verb != 'POST') return nil; /* if the virtual path a string, simply match the string exactly */ if (dataType(vpath) == TypeSString) return vpath == qpath; /* if it's a regular expression, match the pattern */ if (dataType(vpath) == TypeObject && vpath.ofKind(RexPattern)) return rexMatch(vpath, qpath) != nil; /* we can't match other path types */ return nil; } /* * Send a generic request acknowledgment or reply. This wraps the * given XML fragment in an XML document with the root type given by * the last element in our path name. If the 'xml' value is omitted, * we send "" by default. */ sendAck(req, xml = '') { /* * Figure the XML document root element. If we have a non-empty * path, use the last element of the path (as delimited by '/' * characters). Otherwise, use a default root of . */ local root = 'reply'; if (dataType(vpath) == TypeSString && vpath.length() > 0 && rexSearch(vpath, '/([^/]+)$') != nil) root = rexGroup(1)[3]; /* send the reply, wrapping the fragment in a proper XML document */ sendXML(req, root, xml); } /* * Send an XML reply. This wraps the given XML fragment in an XML * document with the given root element. */ sendXML(req, root, xml) { req.sendReply('\<<>><>>>', 'text/xml', 200); } ; /* ------------------------------------------------------------------------ */ /* * A resource file request handler. This handles a request by sending * the contents of the resource file matching the given name. * * To expose a bundled game resource as a Web object that the client can * access and download via HTTP, simply create an instance of this class, * and set the virtual path (the vpath property) to the resource name. * See coverArtResource below for an example - that object creates a URL * for the Cover Art image so that the browser can download and display * it. * * You can expose *all* bundled resources in the entire game simply by * creating an object like this: * *. WebResourceResFile *. vpath = static new RexPattern('/') *. ; * * That creates a URL mapping that matches *any* URL path that * corresponds to a bundled resource name. The library intentionally * doesn't provide an object like this by default, as a security measure; * the default configuration as a rule tries to err on the side of * caution, and in this case the cautious thing to do is to hide * everything by default. There's really no system-level security risk * in exposing all resources, since the only files available as resources * are files you explicitly bundle into the build anyway; but even so, * some resources might be for internal use within the game, so we don't * want to just assume that everything should be downloadable. * * You can also expose resources on a directory-by-directory basis, * simply by specifying a longer path prefix: * *. WebResourceResFile *. vpath = static new RexPattern('/graphics/') *. ; * * Again, the library doesn't define anything like this by default, since * we don't want to impose any assumptions about how your resources are * organized. */ class WebResourceResFile: WebResource /* * Match a request. A resource file resource matches if we match the * virtual path setting for the resource, and the requested resource * file exists. */ matchRequest(query, req) { return inherited(query, req) && resExists(processName(query[1])); } /* process the request: send the resource file's contents */ processRequest(req, query) { /* get the local resource name */ local name = processName(query[1]); /* get the filename suffix (extension) */ local ext = nil; if (rexSearch('%.([^.]+)$', name) != nil) ext = rexGroup(1)[3]; local fp = nil; try { /* open the file in the appropriate mode */ if (isTextFile(name)) fp = File.openTextResource(name); else fp = File.openRawResource(name); } catch (FileException exc) { /* send a 404 error */ req.sendReply(404); return; } /* * If the file suffix implies a particular mime type, set it. * There are some media types that are significant to browsers, * but which the HTTPRequest object can't infer based on the * contents, so as a fallback infer the media type from the * filename suffix if possible. */ local mimeType = browserExtToMime[ext]; /* * Send the file's contents. Since resource files can be large * (e.g., images or audio files), send the reply asynchronously * so that we don't block other requests while the file is being * downloaded. Browsers typically download multimedia resources * in background threads so that the UI remains responsive during * large downloads, so we have to be prepared to handle these * overlapped requests while a download proceeds. */ req.sendReplyAsync(fp, mimeType); /* done with the file */ fp.closeFile(); } /* extension to MIME type map for important browser file types */ browserExtToMime = static [ 'css' -> 'text/css', 'js' -> 'text/javascript' ] /* * Process the name. This takes the path string from the query, and * returns the resource file name to look for. By default, we simply * return the same name specified by the client, minus the leading * '/' (since resource paths are always relative). */ processName(n) { return n.substr(2); } /* * Determine if the given file is a text file or a binary file. By * default, we base the determination solely on the filename suffix, * checking the extension against a list of common file types. */ isTextFile(fname) { /* get the extension */ if (rexMatch('.*%.([^.]+)$', fname) != nil) { /* pull out the extension */ local ext = rexGroup(1)[3].toLower(); /* * check against common binary types - if it's not there, * assume it's text */ return (binaryExts.indexOf(ext) == nil); } else { /* no extension - assume binary */ return nil; } } /* table of common binary file extensions */ binaryExts = ['jpg', 'jpeg', 'png', 'mng', 'bmp', 'gif', 'mpg', 'mp3', 'mid', 'ogg', 'wav', 'pdf', 'doc', 'docx', 'swf', 'dat', 'sav', 'bin', 'gam', 't3', 't3v']; ; /* * The resource handler for our standard library resources. All of the * library resources are in the /webuires resource folder. This exposes * everything in that folder as a downloadable Web object. */ webuiResources: WebResourceResFile vpath = static new RexPattern('/webuires/') ; /* the special cover art resource */ coverArtResource: WebResourceResFile vpath = static new RexPattern('/%.system/CoverArt%.(jpg|png)$') ; /* * Session initializer resource. This is a mix-in class designed to be * used for a special resource that initializes the session. Mix this * with a WebResource class to set up the initializer. When you connect * to the client via connectWebUI(), point the client to this resource. * * There are two elements to setting up the session. First, we need to * set the program session key as a cookie. The client obtains this from * the registration mechanism, whose purpose is to launch the game * program and send the connection information back to the client. The * client sends this to us in the form of a URL parameter, TADS_session. * This key is essentially for authentication, to make sure that the * client that we're talking to is actually the client that launched the * program: only that client would be able to get the key, because we * invent it and send it to the registrar, and the registrar only sends * it back to the client session it's already established on its end. * This prevents port scanners from finding our open port and trying to * crawl our "site" or otherwise access our services. * * The second setup element is to create a client session key. Whereas * the program session key is for our entire service, the client session * key is specific to this one connection. If the user opens two browser * windows on this server, each browser needs its own separate client * session so that we can tell the traffic apart. The client session key * is simply another random key we generate, and again we pass it back to * the client in a cookie. * * The reason we set cookies for both of these session keys is that it * lets the client pass the information back to us on subsequent requests * without having to encode another parameter in every URL. We set * session cookies in both cases; the program session is for * authentication purposes and so we don't want it to be stored or * shared, and the client session key is explicitly to identify this one * browser session, so it obviously shouldn't be shared across browser * instances or sessions. * * Note that instances should always provide a string (as opposed to a * regular expression) for the virtual path (the 'vpath' property). We * have to send the path to the browser UI as part of the connection * information, so we need a string we can send rather than a pattern to * match. */ class WebResourceInit: object /* * Connect to the client. The program should call this after * creating its HTTPServer object, which you pass here as 'srv'. * This establishes the client UI connection, generating the path to * the start page. */ connectUI(srv) { /* * point the client to our start page, adding the session key as * a query parameter */ connectWebUI(srv, '<>?TADS_session=<>'); } /* * Process the request. This sets up the program and client session * keys as cookies. */ processRequest(req, query) { /* get the session parameter from the query */ local skey = query['TADS_session']; /* set the session cookie in the reply */ if (skey) req.setCookie('TADS_session', '<>; path=/;'); /* check for a client session */ local ckey = req.getCookie('TADS_client'); if (ckey == nil || ClientSession.find(ckey) == nil) { /* * There's no client session ID cookie, or the ID is invalid * or has expired. Create a new client session. */ /* get the storage server session ID key from the request */ local ssid = query['storagesid']; /* get the user name */ local uname = query['username']; /* * if there's no storage server session, and they're * connecting under the primary server session key, use the * primary user's storage server session */ if (ssid == nil && skey == webSession.sessionKey) ssid = webSession.storageSID; /* create the new session object */ local client = new transient ClientSession(skey, ssid); /* if there's a user name, set it in the session */ if (uname != nil) client.screenName = uname; else client.setDefaultScreenName(); /* send the client session ID to the browser as a cookie */ req.setCookie('TADS_client', '<>; path=/;'); /* if this is a guest, alert everyone to the new connection */ if (!client.isPrimary) webMainWin.postSyntheticEvent('newGuest', client.screenName); } /* go return the underlying resource */ inherited(req, query); } /* the HTPTServer for communicating with the client */ server = nil ; /* ------------------------------------------------------------------------ */ /* * A WebResourceGroup is a container for WebResource objects. When a * server receives a request, it looks in its group list to find the * resource object that will handle the request. */ class WebResourceGroup: object /* * Should this group handle the given request? By default, we say * yes if the server that received the request is associated with * this group via the group's 'server' property. */ isGroupFor(req) { /* get the request's server object */ local srv = req.getServer(); /* * if this server matches our 'server' property, or is in the * list of servers if 'server' is a list, we're the group for the * request */ return (srv == server || (dataType(server) == TypeList && server.indexOf(srv) != nil)); } /* * The priority of the group, relative to other groups. If the same * server matches multiple groups, this allows you to designate which * group has precedence. A higher value means higher priority. */ priority = 100 /* * The HTTPServer object or objects this group is associated with. * The general event processor uses this to route a request to the * appropriate resource group, by finding the group that's associated * with the server that received the request. * * To associate a group with multiple servers, make this a list. */ server = nil /* the WebResource objects in the group */ contents = [] /* * Process a request. This looks for the highest priority matching * resource in the group, then hands the request to that resource for * processing. */ processRequest(req) { /* parse the query */ local query = req.parseQuery(); /* * Check for the session ID. The session ID is required either * in a URL parameter or in a cookie. If it's not present, * reject the request with a "403 Forbidden" error, since the * session is essentially an authentication token to tell us that * the client is in fact the same user that launched the game. */ if (!webSession.validateKey(req, query)) return; /* * Search our list for the first resource that matches this * request. The list is initialized in descending priority * order, so the first match we find will be the one with the * highest priority. */ local match = contents.valWhich({res: res.matchRequest(query, req)}); /* if we found a match, process it; otherwise return a 404 error */ if (match != nil) match.processRequest(req, query); else req.sendReply(404); } /* class property: list of all WebResourceGroup objects */ all = [] ; /* ------------------------------------------------------------------------ */ /* * The default web resource group. This is the default container for * WebResource objects. */ mainWebGroup: WebResourceGroup /* the default group matches any server, but with low priority */ isGroupFor(req) { return true; } priority = 1 ; /* ------------------------------------------------------------------------ */ /* * At startup, put each WebResource object into the contents list for its * group. */ PreinitObject execute() { /* build the contents list for each resource group */ forEachInstance(WebResource, function(obj) { /* get the group object for the resource */ local g = obj.group; /* if the group doesn't have a contents list yet, create one */ if (g.contents == nil) g.contents = []; /* add this resource to the contents list */ g.contents += obj; }); /* sort each group's contents list in priority order */ forEachInstance(WebResourceGroup, function(obj) { /* sort the group's contents list */ obj.contents = obj.contents.sort( SortDesc, { a, b: a.priority - b.priority }); /* add this group to the master list of groups */ WebResourceGroup.all += obj; }); /* sort the groups in descending order of priority */ WebResourceGroup.all = WebResourceGroup.all.sort( SortDesc, { a, b: a.priority - b.priority }); } ; /* ------------------------------------------------------------------------ */ /* * Guest connection request. This enables "switchboard" applications on * remote servers that keep track of multi-user game sessions, to show * users available sessions and connect new users to those sessions. * * The first step in setting up a switchboard is for the game server to * register itself with the switchboard by sending a request on startup. * That part is external to us - that's not handled within the game * program but rather within the web server script that launches the * game. Here, then, we simply assume that this work is already done. * * The second step is that the switchboard needs to check back with the * game server from time to time to see if it's still alive - essentially * a "ping" operation. We handle that here: if we respond to the * request, we're obviously still alive. * * The third step is that we need to send the switchboard a URL that lets * secondary users ("guests") connect to the game session. We handle * that here as well: our reply body is the client connection URL. */ guestConnectPage: WebResource vpath = '/webui/guestConnect' processRequest(req, query) { /* send the collaborative connection URL */ req.sendReply(webSession.getCollabUrl(), 'text/plain', 200); } ; /* ------------------------------------------------------------------------ */ /* * getEvent request. This is the mechanism we use to "send" events to * the client. The client sends a getEvent request to us, and we simply * put it in a queue - we don't send back any response immediately. As * soon as we want to send an event to the client, we go through the * queue of pending getEvent requests, and reply to each one with the * event we want to send. */ eventPage: WebResource vpath = '/webui/getEvent' processRequest(req, query) { /* find the client */ local c = ClientSession.find(req); /* if we found the client session object, send it the request */ if (c != nil) c.requestEvent(req); else req.sendReply(400); } /* broadcast an event message to each client */ sendEvent(msg) { /* build the full XML message */ msg = '<>'; /* send it to each client */ ClientSession.broadcastEvent(msg); } /* send an event to a particular client */ sendEventTo(msg, client) { /* build the full XML message */ msg = '<>'; /* send it to the given client */ client.sendEvent(msg); } ; /* * Flush events. This cancels any pending event requests for the client. * The client can use this after being reloaded to flush any outstanding * event requests from a past incarnation of the page. */ flushEventsPage: WebResource vpath = '/webui/flushEvents' processRequest(req, query) { /* find the client */ local c = ClientSession.find(req); /* if we found the client, send it the flush request */ if (c != nil) c.flushEvents(); /* * acknowledge the request; it's okay if we didn't find the * client session, since this just means there are no events to * flush for this client */ sendAck(req); } ; /* * getState request. The web page can send this to get a full accounting * of the current state of the UI. It does this automatically when first * loaded, and again when the user manually refreshes the page. * * We handle this by asking the main window to generate its state. */ uiStatePage: WebResource vpath = '/webui/getState' processRequest(req, query) { /* get the window making the request */ local w = webMainWin.winFromPath(query['window']); local client = ClientSession.find(req); /* if we found the window, send the reply */ if (w) { /* send the uiState reply for the window */ sendXML(req, 'uiState', w.getState(client)); } else { /* no window - send an error reply */ req.sendReply(406); } } ; /* ------------------------------------------------------------------------ */ /* * Process network requests. Continues until doneFunc() returns true, or * a timeout or error occurs. If we return because doneFunc() returned * true, we'll return nil. Otherwise, we'll return the NetEvent that * terminated the wait. */ processNetRequests(doneFunc, timeout?) { /* if there's a timeout, figure the ending time */ local endTime = (timeout != nil ? getTime(GetTimeTicks) + timeout : nil); /* keep going until the 'done' function returns true */ while (doneFunc == nil || !doneFunc()) { try { /* get the next housekeeping time */ local hkTime = webSession.hkTime; /* * figure the time to the next timeout - stop at the caller's * ending time, or the next housekeeping time, whichever is * sooner */ local tf = (endTime != nil && endTime < hkTime ? endTime : hkTime); /* figure the time remaining to the next timeout */ local dt = max(0, tf - getTime(GetTimeTicks)); /* * Flush any pending output. If we're waiting for user * input, this ensures that the last output is visible in the * UI while we await the next user action. */ flushOutput(); /* get the next network event */ local evt = getNetEvent(dt); /* see what we have */ switch (evt.evType) { case NetEvRequest: /* * This is an incoming network request from a client. * Check the protocol type of the request object. */ if (evt.evRequest.ofKind(HTTPRequest)) { /* * HTTP request - process it through the appropriate * web resource group. First, find the group that * wants to handle the request. */ local req = evt.evRequest; local group = WebResourceGroup.all.valWhich( { g: g.isGroupFor(req) }); /* * if there's a client associated with the request, * update its last activity time */ local client = ClientSession.find(req); if (client != nil) client.updateEventTime(); /* if we found a group, let it handle the request */ if (group != nil) { try { /* send the request to the group for processing */ group.processRequest(req); } catch (Exception exc) { /* * Unhandled exception - something went wrong * in the server, so the appropriate reply is * 500 Internal Server Error. Send the * exception message with the reply. */ local msg = 'Unhandled exception processing request: ' + exc.getExceptionMessage().specialsToText(); req.sendReply(msg, 'text/plain', 500); } } else { /* no group - send back a 404 error */ req.sendReply(404); } } break; case NetEvTimeout: /* * Timeout. Always try running housekeeping on a * timeout; the housekeeper will ignore this unless the * time has actually come. */ hkTime = webSession.housekeeping(); /* * Check for a caller timeout. If the caller's end time * has arrived, pass the timeout back to the caller as a * timeout event. */ if (endTime != nil && getTime(GetTimeTicks) >= endTime) return evt; /* * it wasn't a caller timeout, so it must have been an * internal housekeeping timeout, which we just handled; * simply continue looping */ break; case NetEvUIClose: /* * UI Closed. This tells us that the user has manually * closed the UI window. This only happens in the local * stand-alone configuration, where the "browser" is an * integrated part of the interpreter application, * simulating the traditional TADS interpreter setup * where the whole program is a single application * running on one machine. In this setup, closing the UI * window should dismiss the whole application, since * that's the convention on virtually all GUIs. */ /* disconnect all clients */ ClientSession.disconnectAll(); /* quit by signaling a "quit game" event */ throw new QuittingException(); } } catch (SocketDisconnectException sdx) { /* the client has closed its connection; ignore this */ } } /* indicate that the 'done' condition was triggered */ return nil; } /* ------------------------------------------------------------------------ */ /* * Web Window tracker. This is a game object that controls and remembers * the state of a "window" in the browser user interface. By "window", * we basically mean an HTML page, which might reside at the top level of * the browser itself, or inside an IFRAME element within an enclosing * page. * * Each WebWindow class corresponds to a particular HTML page that we * serve the client. The HTML page is the expression of the window in * the browser, and the WebWindow object is the expression of the same * information in the game program. The two are different facets of the * same conceptual UI object. The reason we need the two separate * expressions is that the server controls everything, but the client has * to do the actual display work, and the two parts of the program speak * different languages - the server is TADS, and the client is HTML. * * The WebWindow object on the server lets us easily reconstruct the UI * state in a newly opened browser window, or when the user performs a * page refresh. This object's job is to send information to the client * on demand that allows the client to display the page in its current * state. * * Note that a given WebWindow/HTML page combination can be used more * than once within the same UI. The pages defined in the library are * designed to be generic and reusable, so you might use the same window * class more than once for different purposes within the UI. The * library pages can also be subclassed, by subclassing the WebWindow * object and creating a customized copy of the corresponding HTML page * resource. */ class WebWindow: WebResourceResFile /* * The URL path to the window's HTML definition file, as seen by the * browser. For the pre-defined library window types, we expose the * HTML file in the root of the URL namespace - e.g., "/main.htm". * The files are actually stored in the /webuires folder, but we * expose them to the browser as though they were in the root folder * to make embedded object references on the pages simpler. The * browser figures the path to an embedded object relative to the * containing page, so by placing the containing page in the root * folder, embedded object paths don't have to worry about * referencing parent folders. */ vpath = nil /* * The window's actual source location, as a resource path. A given * WebWindow subclass corresponds to a particular HMTL page, since * the class and the page are facets of the same conceptual object * (one facet is the browser expression, the other is the game * program expression). */ src = nil /* process a request path referencing me into my actual resource path */ processName(n) { return src; } /* * Resolve a window path name. For container windows, this should * search the sub-windows for the given path. By default, we match * simply if the path matches our name. */ winFromPath(path) { return path == name ? self : nil; } /* * Flush the window. This sends any buffered text to the UI. */ flushWin() { } /* * Write text to the window. Subclasses with stream-oriented APIs * must override this. */ write(txt) { } /* * Clear the window. Subclasses must override this. */ clearWindow() { } /* * Get the window's current state. This returns a string containing * an XML fragment that describes the state of the window. This * information is sent to the HTML page when the browser asks for the * current layout state when first loaded or when the page is * refreshed. The XML format for each subclass is specific to the * Javascript on the class's HTML page. */ getState(client) { return ''; } /* send an event related to this window to all clients */ sendWinEvent(evt) { /* * send the event message, adding a parameter to * identify the source */ eventPage.sendEvent('<><>'); } /* send a window event to a specific client */ sendWinEventTo(evt, client) { eventPage.sendEventTo('<><>', client); } /* specialsToHtml context */ sthCtx = perInstance(new SpecialsToHtmlState()) /* the name of this window */ name = nil /* the full path name of this window, in "win.sub.sub" format */ pathName = nil ; /* ------------------------------------------------------------------------ */ /* * Layout Window. This is a specialized Web Window tracker for our * layout page type, which is displayed using the resource file * webuires/layout.htm. This page is designed as a container of more * specialized sub-window pages; its job is to divide up the window space * into IFRAME elements that display the sub-windows, and to manage the * geometry of the IFRAMEs. * * The layout page is primarily designed to be the top-level page of the * web UI. The idea is to set up a layout page as the navigation URL for * the browser, so the layout page fills the browser window. You then * arrange your functional windows within the layout page - a command * window, a status line window, etc. This arrangement is similar to * banner window in HTML TADS, but IFRAMEs are considerably more * flexible; for example, they don't have to tile the main window, and * you can size them in the full range of units CSS provides. * * Layout windows aren't limited to the top level, though. Since you can * put any HTML page within an IFRAME, you can put another layout window * within an IFRAME, to further subdivide the space inside the IFRAME. */ class WebLayoutWindow: WebWindow /* * Resolve a window path name */ winFromPath(path) { /* get the first element and the rest of the path */ local idx = path.find('.'); if (idx == nil) idx = path.length() + 1; /* pull out the first element and the rest */ local head = path.substr(1, idx - 1); local tail = path.substr(idx + 1); /* if the first element doesn't match our name, it's not a match */ if (head != name) return nil; /* if that's the end of the path, we have our match */ if (tail == '') return self; /* match the rest of the path against our children */ foreach (local w in frames) { local match = w[1].winFromPath(tail); if (match != nil) return match; } /* no match */ return nil; } /* * Create a new window within the layout. This creates an IFRAME in * the browser, laid out according to the 'pos' argument, and * displays the given window object within the frame. * * If the window already exists, this updates the window with the new * layout settings. * * 'win' is a WebWindow object that will be displayed within the * IFRAME. This method automatically loads the HTML resource from * the WebWindow into the new IFRAME. * * 'name' is the name of the window. Each window within a layout * must have a distinct name. This allows you to refer to the * dimensions of other windows in 'pos' parameters. The name should * be alphanumeric. * * 'pos' is the layout position for the new frame. This is a string * in this format: 'left, top, width, height', where 'left' is the * horizontal position of the top left corner, 'top' is the vertical * position of the top left corner, 'width' is the width of the * window, and 'height' is the height. Each element can be specified * as a Javascript-style arithmetic expression. Within the * expression, you can use a mix of any of the following: * *. 123 - a number, representing a number of pixels on the display *. 5em - 5 'em' units, relative to the main BODY font in the window *. 5en - 5 'en' units in the main BODY font *. 5ex - 5 'ex' units in the main BODY font *. window.width - the width in pixels of the enclosing window *. window.height - the height in pixels of the enclosing window *. 50% - percentage of the width or height of the enclosing window *. content.width - the width in pixels of the contents of the frame *. content.height - the height in pixels of the contents of the frame *. x.left - horizontal coordinate of leftmost edge of window 'x' *. x.right - horizontal coordinate of rightmost edge of window 'x' *. x.top - vertical coordinate of top edge of window 'x' *. x.bottom - vertical coordinate of bottom edge of window 'x' *. x.width - width in pixels of window 'x' *. x.height - height in pixels of window 'x' * * The "window" dimensions refer to the *enclosing* window. If this * layout window is the main page of the UI, this is simply the * browser window itself. For a layout window nested within another * frame, this is the enclosing frame. * * Percentage units apply to the enclosing window. When a percentage * is used in the 'left' or 'width' slot, it applies to the width of * the enclosing window; in the 'top' or 'height' slot, it applies to * the height. * * The "content" dimensions refer to the contents of the frame we're * creating. This is the size of the contents as actually laid out * in the browser. * * "x.left" and so on refer to the dimensions of other frames *within * this same layout window*. 'x' is the name of another window * within the same layout, as specified by the 'name' argument given * when the window was created. */ createFrame(win, name, pos) { /* set the window's internal name to its full path name */ win.name = name; win.pathName = self.name + '.' + name; /* add the window to our list */ frames[name] = [win, pos]; /* notify the UI */ sendWinEvent('' + '<>' + '<>' + '<>' + ''); } /* * Flush this window. For a layout window, we simply flush each * child window. */ flushWin() { /* flush each child window */ frames.forEach({w: w[1].flushWin()}); } /* * Get the state. */ getState(client) { /* build an XML fragment describing the list of frames */ local s = ''; frames.forEachAssoc(function(name, info) { s += '<>' + '<>' + '<>' + ''; }); /* return the completed state object */ return s; } /* * The table of active frames within this layout. This table is * keyed by window name; each entry is a list of [win, pos], where * 'win' is the WebWindow object for the window, and 'pos' is its * position parameter. */ frames = perInstance(new LookupTable(16, 32)) /* my virtual path and the actual resource file location */ vpath = '/layoutwin.htm' src = 'webuires/layoutwin.htm' ; /* ------------------------------------------------------------------------ */ /* * Command Window. This object keeps track of the state of command * window within the web UI. */ class WebCommandWin: WebWindow /* * Write to the window */ write(txt) { /* add the text to the output buffer */ outbuf.append(txt); } /* * Flush the buffers */ flushWin() { /* get the current output buffer */ local txt = toString(outbuf).specialsToHtml(sthCtx); /* add it to the state buffer (text since last input) */ textbuf.append(txt); /* send the text to the client via an event */ sendWinEvent('<>'); /* we've now processed the pending output buffer */ outbuf.deleteChars(1); } /* * Read a line of input in this window. Blocks until the reply is * received. Returns nil on timeout. */ getInputLine(timeout?) { /* flush buffered output */ flushWin(); /* clear out the last input */ lastInput = nil; lastInputClient = nil; lastInputReady = nil; /* set the UI state */ mode = 'inputLine'; isInputOpen = true; /* send the inputLine event to the client */ sendWinEvent(''); /* process network events until we get our input or we time out */ processNetRequests( {: lastInputReady || webMainWin.syntheticEventReady() }, timeout); /* move the current textbuf contents to the scrollback list */ textbufToScrollback(lastInput); /* back to 'working' mode */ mode = 'working'; /* check the result */ if (lastInput != nil) { /* we got a reply - mark the input line as closed in the UI */ isInputOpen = nil; /* reset to the start of the line in the output context */ sthCtx.resetLine(); /* remember the source of the command */ webMainWin.curCmdClient = lastInputClient; /* return the Line Input event */ return [InEvtLine, lastInput]; } else if (webMainWin.syntheticEventReady()) { /* there's a synthetic event available - return it */ return webMainWin.getSyntheticEvent(); } else { /* we didn't get a reply, so we timed out */ return [InEvtTimeout, '']; } } /* * Cancel an input line that was interrupted by a timeout */ cancelInputLine(reset) { /* if the input line is open, send the cancel event */ if (isInputOpen) { /* send the cancel event to the client */ sendWinEvent(''); /* the input line is closed */ isInputOpen = nil; /* reset to the start of the line in the output context */ sthCtx.resetLine(); } } /* * Get the state of this command window */ getState(client) { return '<>' + scrollback.join() + '<>' + ''; } /* * Receive input from the client */ receiveInput(req, query) { /* remember the text */ lastInput = query['txt']; /* remember the source of the input */ lastInputClient = ClientSession.find(req); /* set the input-ready flag so we exit the modal input loop */ lastInputReady = true; /* get the user who entered the command */ local user = (lastInputClient != nil ? lastInputClient.screenName : ''); /* tell any other windows listening in about the new input */ sendWinEvent('' + (lastInput != nil ? '<>' : '') + '<>' + ''); /* reset to the start of the line in the output context */ sthCtx.resetLine(); } /* * Clear the window */ clearWindow() { /* flush the output buffer */ flushWin(); /* * clear the transcript - if the user refreshes the browser * window, we want to show a blank window */ textbuf.deleteChars(1); scrollback.clear(); /* send a clear window event */ sendWinEvent(''); /* reset to the start of the line in the output context */ sthCtx.resetLine(); } /* * Move the current text buffer contents to the scrollback list. If * this would make the scrollback list exceed the limit, we'll drop * the oldest item. * * 'cmd' is the command line text of the last input. We include this * in the srollback list with special tagging so that the UI can * display it in a custom style, if it wants. */ textbufToScrollback(cmd) { /* get the current buffer, tagged as a entry in the list */ local t = '<>'; /* add the command line tagged as */ if (cmd != nil) t += '<>'; /* add the buffer to the scrollback list */ scrollback.append('<>'); /* if this pushes us past the limit, drop the oldest item */ if (scrollback.length() > scrollbackLimit) scrollback.shift(); /* clear text buffer */ textbuf.deleteChars(1); } /* * Show a "More" prompt */ showMorePrompt() { /* flush the output buffer */ flushWin(); /* send a "More" prompt event to the UI */ sendWinEvent(''); mode = 'morePrompt'; moreMode = true; /* process events until More mode is done */ processNetRequests({: !moreMode }); /* return the default mode */ mode = 'working'; } /* * receive notification from the client that the user has responded * to the More prompt, ending the pause */ endMoreMode() { /* no longer in More mode */ moreMode = nil; } /* main window text buffer since last input read */ textbuf = perInstance(new StringBuffer(4096)) /* * Scrollback list. After each input, we add the contents of * 'textbuf' to this list. If this pushes the list past the limit, * we drop the oldest item. This is used to reconstruct a reasonable * amount of scrollback history when a new client connects, or when * an existing client refreshes the page. */ scrollback = perInstance(new Vector()) /* * The scrollback limit, as a number of command inputs. Each input * interaction adds one item to the scrollback list. When the number * of items in the list exceeds the limit set here, we drop the * oldest item. */ scrollbackLimit = 10 /* pending output buffer, since last flush */ outbuf = perInstance(new StringBuffer(4096)) /* the text of the last input line we received from the client */ lastInput = nil /* is input ready? */ lastInputReady = nil /* client session who sent the last input line */ lastInputClient = nil /* * Is an input line open? This is true between sending an * event and either getting a reply, or explicitly * sending a close or cancel event. */ isInputOpen = nil /* flag: we're in More mode */ moreMode = nil /* * Current UI mode. This is 'working' if the program is running and * in the process of computing and/or generating output; 'inputLine' * if we're waiting for the user to enter a line of input; * 'morePrompt' if we're showing a "More" prompt. */ mode = 'working' /* my virtual path, and the actual resource file location */ vpath = '/cmdwin.htm' src = 'webuires/cmdwin.htm' ; /* * input-line event page */ inputLinePage: WebResource vpath = '/webui/inputLine' processRequest(req, query) { /* find the window */ local w = webMainWin.winFromPath(query['window']); /* dispatch to the window if we found it */ if (w != nil) { /* send the input to the window */ w.receiveInput(req, query); /* acknowledge the request */ sendAck(req); } else req.sendReply(406); } ; /* * "More" prompt done event page */ morePromptDonePage: WebResource vpath = '/webui/morePromptDone' processRequest(req, query) { /* find the target winodw */ local w = webMainWin.winFromPath(query['window']); if (w != nil) { /* release More mode in the server window */ w.endMoreMode(); /* acknowledge the request */ sendAck(req); } else req.sendReply(406); } ; /* ------------------------------------------------------------------------ */ /* * Set Preferences command */ setPrefsPage: WebResource vpath = '/webui/setPrefs' processRequest(req, query) { /* get the request body */ local f = req.getBody(); if (f == nil) { errorReply(req, 'Error saving preferences: no data received'); return; } else if (f == 'overflow') { errorReply(req, 'Error saving preferences: message too large'); return; } /* get the client session for the request */ local cli = ClientSession.find(req); if (cli == nil) { errorReply(req, 'Error saving preferences: missing session'); return; } /* process the file through the profile reader */ cli.uiPrefs.readSettings(f); /* save the updated settings */ cli.uiPrefs.saveSettings(); /* done with the file */ f.closeFile(); /* done - acknowledge the request */ sendAck(req); } /* send an error as the reply to a request, formatted into XML */ errorReply(req, msg) { sendXML(req, 'reply', '<>'); } ; /* * UI Settings list. This represents a named UI settings profile in the * Web UI. A profile is a list of name/value pairs. * * Most of the name keys are style IDs defined in the javascript for on * the UI side - see main.js. These style IDs are arbitrary keys we * define to identify UI elements - "mainFont" for the main font name, * "statusBkg" for the status-line window's background color, etc. Each * style ID generally corresponds to a dialog control widget in the * preferences dialog in the javascript UI, and also corresponds to one * or more CSS style selectors. The mapping from style ID to CSS is * defined in the UI javascript (see prefsMapper in main.js). * * The non-style key "profileName" is the user-visible name of this * profile. Internally, we refer to profiles using ID values, which are * arbitrary identifiers generated by the UI when it creates a new * profile (it currently uses integer keys). */ class WebUIProfile: object construct(id) { self.profileID = id; self.settings = new LookupTable(); } /* set a preference item in the profile */ setItem(id, val) { settings[id] = val; } /* call a callback for each style: func(id, val) */ forEach(func) { settings.forEachAssoc(func); } /* internal ID of the profile */ profileID = '' /* table of style value strings, keyed by style ID */ settings = nil ; /* * Web UI preferences. This object contains the in-memory version of the * display style preferences file. * * Each client session has its own copy of this object, because each * client can be associated with a different user, and each user has * their own preferences file. */ class WebUIPrefs: object construct(c) { /* remember our client session object */ clientSession = c; /* load the initial settings from the user's config file */ loadSettings(); } /* read the settings file */ loadSettings() { /* open the preferences file; do nothing if that fails */ local f = openSettingsFile(FileAccessRead); if (f == nil) return; /* read the settings from the file */ readSettings(f); /* done with the file */ f.closeFile(); } /* read settings from a file */ readSettings(f) { /* set up our table of profiles */ local pros = profileTab = new LookupTable(); /* we don't have a current profile selection yet */ curProfile = nil; /* read the file */ for (;;) { /* read the next line */ local l = f.readFile(); if (l == nil) break; /* if it's a valid line, process it */ if (rexMatch(curProPat, l) != nil) { /* current profile setting */ curProfile = rexGroup(1)[3]; } else if (rexMatch(proItemPat, l) != nil) { /* style item definition - pull out the parts */ local proid = rexGroup(1)[3]; local key = rexGroup(2)[3]; local val = rexGroup(3)[3]; /* if the profile isn't in the table yet, add it */ local pro = pros[proid]; if (pro == nil) pros[proid] = pro = new WebUIProfile(proid); /* add this item to the table */ pro.setItem(key, val); } } } /* current profile ID pattern - current-profile:xxx */ curProPat = static new RexPattern('current-profile=([^\n]*)\n?$') /* setting ID pattern for profile items - nnn.xxx=yyy */ proItemPat = static new RexPattern('([^.]+)%.([^=]+)=([^\n]*)\n?$') /* save the current settings to the user's config file */ saveSettings() { /* if there's no profile table, there's nothing to write */ if (profileTab == nil) return; try { /* open the preferences file; do nothing if that fails */ local f = openSettingsFile(FileAccessWrite); if (f == nil) return; /* write the current profile ID */ f.writeFile('current-profile=<>\n'); /* write the profiles */ profileTab.forEachAssoc(function(id, pro) { /* write each element of this profile */ pro.forEach(function(key, val) { /* write this profile item */ f.writeFile('<>.<>=<>\n'); }); }); /* done with the file */ f.closeFile(); } catch (Exception e) { /* * couldn't save the file; this isn't fatal, so just log an * error event */ webMainWin.postSyntheticEvent( 'logError', 'An error occurred saving your setting changes. (Details: <>)'); } } /* open the settings file */ openSettingsFile(access) { /* get the filename; abort if we don't have a file */ local name = getSettingsFile(); if (name == nil) return nil; /* open the file */ try { /* open the file and return the handle */ return File.openTextFile(name, access, 'ascii'); } catch (Exception exc) { /* failed to open the file */ return nil; } } /* get the settings file path */ getSettingsFile() { /* if we're in local stand-alone mode, use the local Web UI file */ if (getLaunchHostAddr() == nil) return WebUIPrefsFile; /* if there's a storage server session, the file is on the server */ if (clientSession.storageSID != nil) return '~<>/special/2'; /* * We're in client/server mode, but there's no storage server. * In this mode, we don't have any server-side location to store * files, so we can't save or restore the configuration. */ return nil; } /* get the current settings as XML, to send to the web UI */ getXML() { /* if there's no profile table, there are no settings to return */ if (profileTab == nil) return ''; /* create a buffer for the results */ local s = new StringBuffer(); /* add the current profile */ s.append('<>'); /* add each profile's contents */ profileTab.forEachAssoc(function(id, pro) { /* open this profile section */ s.append('<>'); /* add each style element */ pro.forEach(function(id, val) { s.append('' + '<>' + '<>' + ''); }); /* close this profile's section */ s.append(''); }); /* return the result as an XML string */ return toString(s); } /* the client session for this preference list */ clientSession = nil /* * profile table - this is a LookupTable of WebUIProfile objects * keyed by profile name */ profileTab = nil /* current active profile selected by the user */ curProfile = nil ; /* ------------------------------------------------------------------------ */ /* * Status line window */ class WebStatusWin: WebWindow /* my request path and actual resource path */ vpath = '/statwin.htm' src = 'webuires/statwin.htm' /* * Set the room and score/turns portions of the status line. This * sets the left side of the status line to the 'room' text (which * can contain HTML markups), and the right side to the the * score/turns values, if present. If the turn counter is omitted * but the score value is present, we'll just show the score value; * otherwise we'll format these as "score/turns". If no score value * is present, we'll leave the right side blank. */ setStatus(room, score?, turns?) { /* set up the room text in the left portion */ local msg = '
    <>
    '; /* * format the right side: 'score/turns', 'score', or empty, * depending on what the caller specified */ local rt = (score != nil ? toString(score) : '') + (turns != nil ? '/' + turns : ''); /* if there's a right side, wrap it with right alignment */ if (rt != '') msg += '
    <>
    '; /* * our left/right divisions are floats, which some browsers don't * count against the container size; so add a clear:all division * to make sure we count the height properly */ msg += '
    '; /* set the text */ setStatusText(msg); } /* * Set the text of the status line. This sets the entire status * window to the given HTML text, without any additional formatting. */ setStatusText(msg) { /* setting new text, so reset the stream context */ sthCtx.resetState(); /* set the new text */ txt_.deleteChars(1); txt_.append(msg.specialsToHtml(sthCtx)); /* note that we have new text to send to the UI */ deltas_ = true; } /* add text to the status line */ write(msg) { /* add the text */ txt_.append(msg.specialsToHtml(sthCtx)); /* note that we have new text to send to the UI */ deltas_ = true; } /* clear the window */ clearWindow() { setStatusText(''); } /* flush pending text to the window */ flushWin() { /* if we have any deltas since the last flush, send changes */ if (deltas_) { /* * Send a set-text event, along with a resize. The resize * ensures that the status line adjusts to the current * content size, assuming the window is using automatic * content-height sizing. */ sendWinEvent( '<>'); /* reset the deltas counter */ deltas_ = nil; } } /* * Refigure the window size. The status line is generally set up to * be automatically sized to its contents, which requires that we * tell the UI when it's time to recalculate the layout to reflect * the current contents after a change. */ resize() { sendWinEvent(''); } /* get the current state to send to the browser */ getState(client) { return '<>'; } /* do we have any deltas since the last flush? */ deltas_ = nil /* the current status message */ txt_ = perInstance(new StringBuffer(512)) ; /* ------------------------------------------------------------------------ */ /* * The standard "main window" of our user interface. This is the game * object that represents the default initial HTML page that the player's * web browser connects to. We build this out of three base classes: * * - WebResourceInit, because this is the starting page that the browser * initially connects to. This class does the initial handshaking to set * up the session. * * - WebResourceResFile, because we store the HTML for the page in a * resource file. This class does the work of sending the resource * file's contents to the browser. * * - WebLayoutWindow, because the default main page is also a layout * window, which is basically a container for IFRAME elements where we * plug in the sub-windows that make up the game's user interface. * * Games can customize the front page in any way they like. If you want * to customize the HTML of the main page, you can substitute a different * HTML (.htm) file, and change the processName() method to return the * name of that file. If you want to use something other than a layout * window as the front page, you can simply replace this whole class. */ transient webMainWin: WebResourceInit, WebLayoutWindow, WebResourceResFile /* * match the webuires directory path as the URL path, but map this to * main.htm as the underlying resource name */ vpath = '/' processName(n) { return 'webuires/main.htm'; } /* the top window is always called "main" */ name = 'main' pathName = 'main' /* the window title */ title = 'TADS' /* set the window title */ setTitle(title) { /* remember the title */ self.title = title; /* update the browser */ sendWinEvent('<>'); } /* * Client session for current command line input. Certain modal * interactions, such as file dialogs, are directed only to the * client that initiated the current command. */ curCmdClient = nil /* get the state */ getState(client) { /* get the inherited layout state */ local s = inherited(client); /* add the preference settings */ s += '<>'; /* add the window title */ s += '<<title.htmlify()>>'; /* add the input event, input file, and input dialog states */ s += inputEventState; s += inputDialogState; s += menuSysState; /* if this is the command client, include the file dialog state */ if (client == curCmdClient) s += fileDialogState; /* add the downloadable file list */ s += client.allDownloads().mapAll( { x: x.isReady ? '<>' : '' } ).join(); /* return the result */ return s; } /* wait for an input event */ getInputEvent(timeout) { /* flush buffered output */ flushWin(); /* send the get-event request to the client */ inputEventState = ''; inputEventResult = nil; sendWinEvent(inputEventState); /* process network events until we get an input event */ processNetRequests({: inputEventResult != nil }, timeout); /* check what happened */ if (inputEventResult) { /* we got an input event - return it */ return inputEventResult; } else { /* * There's no result, so we timed out. Tell the UI to cancel * the input event mode. */ inputEventState = ''; sendWinEvent(''); /* return a timeout event */ return [InEvtTimeout]; } } /* receive an input event */ receiveInputEvent(req, query) { local typ = query['type']; local param = query['param']; switch (typ) { case 'key': inputEventResult = [InEvtKey, param]; break; case 'href': inputEventResult = [InEvtHref, param]; break; default: inputEventResult = [InEvtEof]; break; } } /* show the file selector dialog */ getInputFile(prompt, dialogType, fileType, flags) { /* mappings from FileTypeXxx to storage manager type codes */ local typeMap = [ FileTypeLog -> 'log', FileTypeData -> 'dat', FileTypeCmd -> 'cmd', FileTypeText -> 'txt', FileTypeBin -> 'bin', FileTypeT3Image -> 't3', FileTypeT3Save -> 't3v', * -> '*']; /* get the storage server session for the command client */ local sid = curCmdClient.storageSID; /* * If we have a session, get the URL to the storage server file * selection dialog. Note that we don't specify the session in * the URL, since the storage server separately maintains the * session with the client via a cookie. */ local url = nil; if (sid != nil) url = getNetStorageURL( 'fileDialog?filetype=<>' + '&dlgtype=<>' + '&prompt=<>' + '&ret=<>'); /* * if there's no storage server, try using a client PC upload or * download */ if (url == nil) return getInputFileFromClient(prompt, dialogType, fileType, flags); /* * set up the file dialog state description (store it in a * property, so that we can send it again as part of a state * request if the client window is reloaded from scratch) */ fileDialogState = '' + '<>' + '<>' + '<>' + '<>' + ''; /* * Presume that the dialog will be canceled. If the user clicks * the close box on the dialog, the dialog will be dismissed * without ever sending us a result. This counts as a * cancellation, so it's the default result. */ fileDialogResult = [InFileCancel]; /* send the request to the current command client */ sendWinEventTo(fileDialogState, curCmdClient); /* process network events until the dialog is dismissed */ processNetRequests( {: fileDialogState == '' || !curCmdClient.isAlive }); /* return the dialog result */ return fileDialogResult; } /* * Get an input file from the client PC. We'll attempt to upload or * download a file from/to the client PC, using a local temporary * file for the actual file operations. This is a special form of * the input file dialog that we use when we're not connected to a * storage server. */ getInputFileFromClient(prompt, dialogType, fileType, flags) { /* use the appropriate procedure for Open vs Save */ if (dialogType == InFileOpen) { /* * Opening an existing file. Show a dialog asking the user * to select a file to upload. If the user uploads a file, * we'll use the local temp file created by the upload as the * result of the input dialog. */ fileDialogState = '' + '<>' + ''; /* presume the user will cancel the dialog */ fileDialogResult = [InFileCancel]; /* send the request to the UI */ sendWinEventTo(fileDialogState, curCmdClient); /* process network events until the dialog is dismissed */ processNetRequests( {: fileDialogState == '' || !curCmdClient.isAlive }); /* return the file dialog result */ return fileDialogResult; } else if (dialogType == InFileSave) { /* * Saving to a new file. The caller is asking the user to * choose a name for a file that the caller *intends* to * create, but hasn't yet created. HTTP can only do a * download as a transaction, though - we can offer an * existing file to the user to download, and the user will * choose the location for the download as part of that * transaction. We don't have that file yet because the * standard protocol is to ask the user for the name first, * then write the selected file. * * So, we have to play a little game. We don't ask the user * to save anything right now, because there's no file to * offer for download yet. Instead, we silently generate a * local temporary file name and return that to our caller. * Our caller will create the temp file and write its data. * * The trick is that we wrap this temp file name in a * DownloadTempFile object. When the caller finishes * writing, it will call closeFile() on the temp file. The * system will pass that along to use by calling closeFile() * on our DownloadTempFile object. At that point, we'll have * a completed temporary file, so we'll pop up a download box * at that point offering the file for download to the user. * The user can then select a local file on the client side, * and we'll send the temporary file's contents as the * download to store in the client file. */ return [InFileSuccess, tempFileDownloadPage.addFile(fileType, curCmdClient)]; } else { /* unknown dialog type */ return [InFileFailure]; } } /* * Offer a file for download to the client. 'file' is a * DownloadTempFile object previously created by a call to * inputFile(). */ offerDownload(file) { /* * send the "offer download" event to the client - the client * will display an iframe with the file as an "attachment", which * will trigger the browser to offer it as a download */ sendWinEventTo( '<>', curCmdClient); } /* receive a file selection from the file selector dialog */ receiveFileSelection(req, query) { /* get the filename from the query */ local f = query['file']; local desc = query['desc']; local cancel = (query['cancel'] != nil); local err = query['error']; /* * Set the file dialog result list. This uses the same format as * the native inputFile() routine: the first element is the * status code (Success, Failure, Cancel); on success, the second * element is the filename string. We add two bits of */ if (err != nil) fileDialogResult = [InFileFailure, err]; else if (cancel || f == nil || f == '') fileDialogResult = [InFileCancel]; else { /* we got a filename - add the SID prefix if necessary */ if (rexMatch('~[^/]+/', f) == nil) f = '~<>/<>'; /* success - return the filename */ fileDialogResult = [InFileSuccess, f]; /* if there's a description string, return that as well */ if (desc != nil) fileDialogResult += desc; } } /* receive notification that the file dialog has been closed */ inputFileDismissed() { /* clear the file dialog state */ fileDialogState = ''; } /* receive a file upload from the file upload dialog */ receiveFileUpload(req, query) { /* get the file from the request */ local fields = req.getFormFields(), file; if (fields == 'overflow') { /* error */ fileDialogResult = [InFileFailure, libMessages.webUploadTooBig]; } else if (fields != nil && (file = fields['file']) != nil) { /* got it - save the contents to a local temporary file */ local tmpfile = new TemporaryFile(); local fpTmp = nil; try { /* open the temp file */ fpTmp = File.openRawFile(tmpfile, FileAccessWrite); /* make sure the input file is in raw binary mode */ local fpIn = file.file; fpIn.setFileMode(FileModeRaw); /* copy the contents */ fpTmp.writeBytes(fpIn); /* close the temp file */ fpTmp.closeFile(); /* * Set the dialog result to the temp file. The system * will automatically delete the underlying file system * object when the garbage collector deletes the * TemporaryFile. */ fileDialogResult = [InFileSuccess, tmpfile]; } catch (Exception exc) { /* error - the dialog failed */ fileDialogResult = [InFileFailure, exc.getExceptionMessage()]; /* close and delete the temporary file */ if (fpTmp != nil) fpTmp.closeFile(); if (tmpfile != nil) tmpfile.deleteFile(); } } else { /* no file - consider it a cancellation */ fileDialogResult = [InFileCancel]; } /* the dialog is dismissed */ fileDialogState = ''; } /* show a generic inputDialog dialog */ getInputDialog(icon, prompt, buttons, defaultButton, cancelButton) { /* * if one of the standard button sets was selected, turn it into * a list of localized button names */ switch (buttons) { case InDlgOk: buttons = [libMessages.dlgButtonOk]; break; case InDlgOkCancel: buttons = [libMessages.dlgButtonOk, libMessages.dlgButtonCancel]; break; case InDlgYesNo: buttons = [libMessages.dlgButtonYes, libMessages.dlgButtonNo]; break; case InDlgYesNoCancel: buttons = [libMessages.dlgButtonYes, libMessages.dlgButtonNo, libMessages.dlgButtonCancel]; break; } /* get a suitable localized title corresponding to the icon tyep */ local title = [InDlgIconNone -> libMessages.dlgTitleNone, InDlgIconWarning -> libMessages.dlgTitleWarning, InDlgIconInfo -> libMessages.dlgTitleInfo, InDlgIconQuestion -> libMessages.dlgTitleQuestion, InDlgIconError -> libMessages.dlgTitleError][icon]; /* build the dialog xml description */ inputDialogState = '' + '<>' + '<<title>>' + '<>' + '' + buttons.mapAll({b: ''}) .join('') + '' + '<>' + '<>' + ''; /* send the request to the client */ inputDialogResult = nil; sendWinEvent(inputDialogState); /* process network events until we get an answer */ processNetRequests({: inputDialogResult != nil }); /* return the dialog result */ return inputDialogResult; } /* receive a selection from the input dialog */ receiveInputDialog(req, query) { /* note the result button */ inputDialogResult = toInteger(query['button']); /* if we didn't get a valid button index, select button 1 */ if (inputDialogResult == 0) inputDialogResult = 1; /* the dialog is no longer open */ inputDialogState = ''; } /* * Post a synthetic event. A synthetic event looks like a regular UI * or network event, but is generated internally instead of being * delivered from the underlying browser or network subsystems. * * 'id' is a string giving the event type. The remaining parameters * are up to each event type to define. */ postSyntheticEvent(id, [params]) { /* add it to the synthetic event queue */ synthEventQueue.append([id] + params); } /* is a synthetic event ready? */ syntheticEventReady() { return synthEventQueue.length() > 0; } /* pull the next synthetic event from the queue */ getSyntheticEvent() { return synthEventQueue.shift(); } /* * file dialog state - this is the XML describing the currently open * file dialog; if the dialog isn't open, this is an empty string */ fileDialogState = '' /* * file dialog result - this is a result list using the same format * as the native inputFile() function */ fileDialogResult = nil /* * input dialog state - this is the XML describing an input dialog * while a dialog is running, or an empty string if not */ inputDialogState = '' /* input dialog result - this is the button number the user selected */ inputDialogResult = nil /* input event state */ inputEventState = '' /* input event result */ inputEventResult = nil /* menuSys state - menu system state (maintained by the menu module) */ menuSysState = '' /* * Synthetic event queue. This is a vector of synthetic events, set * up in the [type, params...] format that the system inputEvent() * function and related functions use. The 'type' code for a * synthetic evente is a string instead of the numeric identifier * that the system functions use. */ synthEventQueue = static new transient Vector() ; /* ------------------------------------------------------------------------ */ /* * Temporary file download page. This page serves temporary files * created via inputFile() as HTTP downloads to the client. */ transient tempFileDownloadPage: WebResource vpath = static new RexPattern('/clienttmp/') processRequest(req, query) { /* * look up the file - the key in our table is the ID string after * the /clienttmp/ path prefix */ local id = query[1].substr(12); local client = ClientSession.find(req); local desc = client.downloads[id]; /* check for cancellation */ if (query['cancel'] != nil) { /* * acknowledge the request - do this before we cancel the * file, since removing the file will remove the link that * fired this request (the order probably isn't a big deal * one way or the other, and we probably can't really control * it anyway as the browser might process the ack and the * event out of order, but just in case) */ sendAck(req); /* if we found the descriptor, cancel it */ if (desc != nil) client.cancelDownload(desc); /* done */ return; } /* if we didn't find it, return failure */ if (desc == nil) { req.sendReply(404); return; } /* open the file */ local fp = nil; try { fp = File.openRawFile(desc.tempFileName, FileAccessRead); } catch (Exception exc) { /* couldn't send the file - send a 404 */ req.sendReply(404); return; } /* set up the download file headers */ local headers = [ 'Content-Disposition: attachment; filename=<>' ]; try { /* send the file's contents */ req.sendReply(fp, desc.mimeType, 200, headers); } catch (Exception exc) { /* ignore errors */ } /* done with the file */ fp.closeFile(); /* * We've at least tried sending the file, so remove it from the * download list. This was just a temp file, so there's no need * to keep it around even if we ran into an error sending it; if * the send failed, the user can just repeat the operation that * generated the file. */ client.cancelDownload(desc); } /* add a file to our list of downloadable files */ addFile(fileType, client) { /* * Generate a server-side name template based on the file type. * The name doesn't matter to us, but browsers will display it to * the user, and many browsers use the server-side name as the * default name for the newly downloaded file in the "Save File" * dialog. Many browsers also use the suffix to determine the * file type, ignoring any Content-Type headers. */ local tpl = [FileTypeLog -> 'Script#.txt', FileTypeData -> 'File#.dat', FileTypeCmd -> 'Command#.txt', FileTypeText -> 'File#.txt', FileTypeBin -> 'File#.bin', FileTypeT3Image -> 'Story#.t3', FileTypeT3Save -> 'Save#.t3v', * -> 'File#'][fileType]; /* figure the mime type based on the file type */ local mimeType = [FileTypeLog -> 'text/plain', FileTypeCmd -> 'text/plain', FileTypeT3Image -> 'application/x-t3vm-image', FileTypeT3Save -> 'application/x-t3vm-state', * -> 'application/octet-stream'][fileType]; /* replace the '#' in the template with the next ID value */ tpl = tpl.findReplace('#', toString(nextID++)); /* create a new table entry */ local desc = new DownloadTempFile(tpl, mimeType); /* add this download to each client */ client.addDownload(desc); /* return the descriptor */ return desc; } /* next available ID */ nextID = 1 ; /* * Downloadable temporary file descriptor. We create this object when * the program calls inputFile() to ask for a writable file. This lets * the caller create and write a temporary file on the server side; when * the caller is done with the file, we'll offer the file for download to * the client through the UI. */ class DownloadTempFile: object construct(res, mimeType) { tempFileName = new TemporaryFile(); resName = res; resPath = '/clienttmp/<>'; timeCreated = getTime(GetTimeTicks); self.mimeType = mimeType; } /* * File spec interface. This allows the DownloadTempFile to be used as * though it were a filename string. * * When the object is passed to one of the File.open methods, or to * saveGame(), setScriptFile(), etc., the system will call our * getFilename() method to determine the actual underlying file. * We'll return our temporary file object. * * When the underlying file is closed, the system calls our * closeFile() method to notify us. */ getFilename() { return tempFileName; } closeFile() { /* mark the file as ready for download */ isReady = true; /* offering the file to the client as an HTTP download */ webMainWin.offerDownload(self); } /* TemporaryFile object for the local temp file */ tempFileName = nil /* root resource name, and full resource path */ resName = nil resPath = nil /* MIME type */ mimeType = nil /* creation timestamp, as a system tick count value */ timeCreated = 0 /* is the file ready for download? */ isReady = nil /* this is a web temp file */ isWebTempFile = true ; /* ------------------------------------------------------------------------ */ /* * Input event page. The client javascript does a GET on this resource * to send us an input event. */ inputEventPage: WebResource vpath = '/webui/inputEvent' processRequest(req, query) { /* send the event to the main window object */ webMainWin.receiveInputEvent(req, query); /* acknowledge the request */ sendAck(req); } ; /* ------------------------------------------------------------------------ */ /* * Input dialog event page. The web UI sends a GET to this page when the * user selects a button in an input dialog. */ inputDialogPage: WebResource vpath = '/webui/inputDialog' processRequest(req, query) { /* send the event to the main window */ webMainWin.receiveInputDialog(req, query); /* acknowledge the request */ sendAck(req); } ; /* ------------------------------------------------------------------------ */ /* * File dialog event page. This page is used by the IFDB Storage Server * file dialog to return information to the game UI. The IFDB dialog * page can't itself perform scripting actions on the enclosing dialog * frame, since it's being served from a different domain - browsers * prohibit cross-domain scripting for security reasons. The IFDB dialog * must therefore navigate back to a page within the game server domain * in order to return information through scripting. This is that page: * when the IFDB page is ready to return information, it navigates its * frame to this page, passing the return values in the request * parameters. Since this page is served by the game server, within the * game server domain, the browser allows it to use scripting actions on * its enclosing frame. We finish the job by dismissing the dialog in * the UI. */ inputFilePage: WebResource vpath = '/webui/inputFile' processRequest(req, query) { /* set the file selection in the main window */ webMainWin.receiveFileSelection(req, query); /* * We have our response, so dismiss the file dialog. Do this by * sending back a page with script instructions to close the * containing dialog window. */ req.sendReply( '' + '' + ''); } ; /* * Cancel the input dialog. This is called from the UI directly to * cancel the file selection, when the user closes the dialog through the * enclosing main page UI rather than from within the dialog. This is * useful if the dialog page fails to load, for example. * * Note: the upload file dialog also uses this. The upload dialog is * basically a variation on the regular input file dialog. */ inputFileCancel: WebResource vpath = '/webui/inputFileDismissed' processRequest(req, query) { /* note that the dialog has been dismissed */ webMainWin.inputFileDismissed(); /* acknowledge the request */ sendAck(req); } ; /* * Receive results from the input file dialog */ uploadFilePage: WebResource vpath = '/webui/uploadFileDialog' processRequest(req, query) { /* send the request to the main window */ webMainWin.receiveFileUpload(req, query); /* send back a script to dismiss the dialog */ req.sendReply( '' + '' + ''); } ; /* ------------------------------------------------------------------------ */ /* * Receive the client's screen name setting */ setScreenNamePage: WebResource vpath = '/webui/setScreenName' processRequest(req, query) { /* set the name in the client session */ local cli = ClientSession.find(req); if (cli != nil) cli.screenName = query['name']; /* acknowledge the request */ sendAck(req); } ; frobtads-1.2.3/tads3/lib/tok.t0000644000175000001440000003442512145504453015265 0ustar realncusers#charset "us-ascii" /* * Tokenizer - customizable tokenizer class for use with the intrinsic * class 'grammar-production' parser. * * This tokenizer implementation is parameterized with a set of rules * (see below); a basic set of rules is provided, but users can * customize the tokenizer quite extensively simply by subclassing the * Tokenizer class and overriding the 'rules_' property with a new set * of rules declarations. */ #include "tads.h" #include "t3.h" #include "dict.h" #include "tok.h" #include "vector.h" /* ------------------------------------------------------------------------ */ /* * Tokenizer exceptions */ /* * base class for all tokenizer errors (to allow blanket 'catch') */ class TokenizerError: Exception displayException() { "Tokenizer exception"; } ; /* * no match for token */ class TokErrorNoMatch: TokenizerError construct(str) { /* remember the full remaining text */ remainingStr_ = str; /* * for convenience, separately remember the single character * that we don't recognize - this is simply the first character * of the rest of the line */ curChar_ = str.substr(1, 1); } displayException() { "Tokenizer error: unexpected character '<>'"; } /* * The remainder of the string. This is the part that couldn't be * matched; we were successful in matching up to this point. */ remainingStr_ = nil /* current character (first character of remainingStr_) */ curChar_ = nil ; /* ------------------------------------------------------------------------ */ /* * Basic token types */ /* word */ enum token tokWord; /* quoted string */ enum token tokString; /* punctuation */ enum token tokPunct; /* integer number */ enum token tokInt; /* ------------------------------------------------------------------------ */ /* * Tokenizer base class */ class Tokenizer: object /* * Tokenizing rules. The subclass can override this to specify a * list that defines different tokenization rules. Each entry in the * master rules_ list is one rule. Each rule is a list consisting of * the name of the rule; the pattern to match for the rule; the token * type (an 'enum token') to use when the rule is matched; the value * computation rule; and the value test rule. * * The name of a rule is just an arbitrary string to identify the * rule. This can be used to insert new rules in order relative to * known existing rules, or to delete known existing rules. * * If the value computation rule is nil, we'll just use the matching * text as the token value. If the value rule is a string, we'll use * the string as a replacement pattern (with rexReplace). If it's a * property ID, we'll invoke the property of self with the following * arguments: * * txt, typ, toks * * 'txt' is the matched text; 'typ' is the token type from the rule; * and 'toks' is a vector to which the new token or tokens are to be * added. The routine is responsible for adding the appropriate * values to the result list. Note that the routine can add more * than one token to the results if desired. * * If the value test rule is non-nil, it must be either a method or a * function; we'll call the method or function to test to see if the * matched value is valid. We'll call the method (on self) with the * matching text as the argument; if the method returns true, the * rule matches, otherwise the rule fails, and we'll continue looking * for another rule as though we hadn't matched the rule's regular * expression in the first place. This can be used for rules that * require more than a simple regular expression match; for example, * the value test can be used to look up the match in a dictionary, * so that the rule only matches tokens that are defined in the * dictionary. */ rules_ = static [ /* skip whitespace */ ['whitespace', R'+', nil, &tokCvtSkip, nil], /* certain punctuation marks */ ['punctuation', R'[.,;:?!]', tokPunct, nil, nil], /* * Words - note that we convert everything to lower-case. A * word must start with an alphabetic character, but can contain * alphabetics, digits, hyphens, and apostrophes after that. */ ['word', R'(|[-\'])*', tokWord, &tokCvtLower, nil], /* strings */ ['string single-quote', R'\'(.*)\'', tokString, nil, nil], ['string double-quote', R'"(.*)"', tokString, nil, nil], /* integer numbers */ ['integer', R'[0-9]+', tokInt, nil, nil] ] /* * Insert a new rule before or after the existing rule with the name * 'curName'. If 'curName' is nil, or rule is found with the given * name, we'll insert the new rule at the end of the list. 'rule' * must be a list with the standard elements for a tokenizer rule. * 'after' is nil to insert the new rule before the given existing * rule, true to insert after it. */ insertRule(rule, curName, after) { local idx; /* * if the name of an existing rule was supplied, find the * existing rule with the given name */ idx = nil; if (curName != nil) idx = rules_.indexWhich({x: tokRuleName(x) == curName}); /* if we didn't find curName, insert at the end of the list */ if (idx == nil) idx = rules_.length(); /* if we're inserting after the given element, adjust the index */ if (after) ++idx; /* insert the new rule */ insertRuleAt(rule, idx); } /* * Insert a rule at the given index in our rules list. 'rule' must * be a list with the standard elements for a tokenizer rule. 'idx' * is the index of the new rule; we'll insert before the existing * element at this index; so if 'idx' is 1, we'll insert before the * first existing rule. */ insertRuleAt(rule, idx) { /* insert the rule */ rules_ = rules_.insertAt(idx, rule); } /* * Delete a rule by name. This finds the rule with the given name * and removes it from the list. */ deleteRule(name) { local idx; /* find the rule with the given name */ idx = rules_.indexWhich({x: tokRuleName(x) == name}); /* if we found the named element, remove it from the list */ if (idx != nil) deleteRuleAt(idx); } /* delete the rule at the given index */ deleteRuleAt(idx) { /* delete the rule */ rules_ = rules_.removeElementAt(idx); } /* convert a string to lower-case (for value computation rules) */ tokCvtLower(txt, typ, toks) { /* add the lower-cased version of the string to the result list */ toks.append([txt.toLower(), typ, txt]); } /* * processing routine to skip a match - this is used for whitespace * and other text that does not result in any tokens in the result * list */ tokCvtSkip(txt, typ, toks) { /* simply skip the text without generating any new tokens */ } /* * Tokenize a string. If we find text that we can't match to any of * the rules, we'll throw an exception (TokErrorNoMatch). If we * succeed in tokenizing the entire string, we'll return a list with * one element per token. Each element of the main list is a * sublist with the following elements describing a token: * * - The first element gives the token's value. * * - The second element the token type (given as a token type enum * value). * * - The third element the original token strings, before any * conversions or evaluations were performed. For example, this * maintains the original case of strings that are lower-cased for * the corresponding token values. */ tokenize(str) { local toks = new Vector(32); local startIdx = 1; local len = str.length(); /* keep going until we run out of string */ mainLoop: while (startIdx <= len) { /* run through the rules in sequence until we match one */ ruleLoop: for (local i = 1, local cnt = rules_.length() ; i <= cnt ; ++i) { local cur; local match; local val; /* get the current rule */ cur = rules_[i]; /* check for a match to the rule's pattern */ match = rexMatch(tokRulePat(cur), str, startIdx); if (match != nil && match > 0) { local test; local txt; local typ; /* get the matching text */ txt = str.substr(startIdx, match); /* * if there's a value test, invoke it to determine * if the token really matches */ if ((test = tokRuleTest(cur)) != nil) { local accept; /* check what kind of test function we have */ switch (dataType(test)) { case TypeFuncPtr: case TypeObject: /* it's a function or anonymous function */ accept = (test)(txt); break; case TypeProp: /* it's a method */ accept = self.(test)(txt); break; default: /* consider anything else to be accepted */ accept = true; break; } /* * if the value test failed, it means that the * token doesn't match this rule after all - * ignore the regex match and keep searching for * another rule */ if (!accept) continue ruleLoop; } /* get the type of the token from the rule */ typ = tokRuleType(cur); /* get this value processing rule */ val = tokRuleVal(cur); /* determine what value to use */ switch(dataTypeXlat(val)) { case TypeNil: /* use the matching text verbatim */ toks.append([txt, typ, txt]); break; case TypeProp: /* * invoke the property - it's responsible for * adding the token or tokens to the results * lists */ self.(val)(txt, typ, toks); break; case TypeSString: /* it's a regular expression replacement */ toks.append( [rexReplace(tokRulePat(cur), txt, val, ReplaceOnce), typ, txt]); break; case TypeFuncPtr: /* invoke the function */ (val)(txt, typ, toks); break; default: /* * use any other value exactly as given in * the rule */ toks.append([val, typ, txt]); break; } /* * continue the search at the next character after * the end of this token */ startIdx += match; /* start over with the rest of the string */ continue mainLoop; } } /* * We failed to find a match for this part of the string. * Throw an exception and let the caller figure out what to * do. The exception parameter gives the rest of the * string, so the caller can display a suitable error * message if desired. */ throw new TokErrorNoMatch(str.substr(startIdx)); } /* we're done with the string - return out value and type lists */ return toks.toList(); } ; /* ------------------------------------------------------------------------ */ /* * Test Section */ #ifdef TOK_TEST main(args) { "Enter text to tokenize. Type Q or QUIT when done. "; for (;;) { local str, toks; /* read a string */ "\b>"; str = inputLine(); /* catch tokenization errors */ try { /* tokenize the string */ toks = Tokenizer.tokenize(str); /* if the first token is 'quit', we're done */ if (toks.length() > 0 && getTokType(toks[1]) == tokWord && (getTokVal(toks[1])== 'quit' || getTokVal(toks[1]) == 'q')) { /* they want to stop - exit the command loop */ break; } /* display the tokens */ for (local i = 1, local cnt = toks.length() ; i <= cnt ; ++i) "(<>) "; } catch (TokErrorNoMatch err) { "Unrecognized punctuation: <>"; } } } #endif /* TOK_TEST */ frobtads-1.2.3/tads3/lib/extensions/0000755000175000001440000000000012145614112016464 5ustar realncusersfrobtads-1.2.3/tads3/lib/extensions/TCommand/0000755000175000001440000000000012145614112020166 5ustar realncusersfrobtads-1.2.3/tads3/lib/extensions/TCommand/doc/0000755000175000001440000000000012145614112020733 5ustar realncusersfrobtads-1.2.3/tads3/lib/extensions/TCommand/doc/tcommand.htm0000644000175000001440000005177111051054617023265 0ustar realncusersTCommand
    TCommand

    The TCommand module is basically designed to extend the functionality of the CommandTopic class. It does this in a number of ways:

    ·By modifying CommandTopic so that it can optionally match the direct and/or indirect object of the command, as well as the action commanded.  
    ·By adding two new methods to CommandTopic to facilitate the handling of commands.  
    ·By adding the obeyCommand property to CommandTopic to indicate whether or not the NPC should obey the command.  
    ·By modifying ActorState.obeyCommand to work better with CommandTopic and multiple direct objects.  
    ·By adding a new CommandHelper convenience mix-in class, and defining TCommandTopic, DefaultTCommandTopic and AltTCommandTopic to make use of it.  
    ·By modifying DefaultCommandTopic to work the same way as CommandTopic  
    ·By adding an AltCommandTopic class  
    ·By adding a SuggestedCommandTopic class  

    The modied CommandTopic can match one or more direct objects and/or indirect objects as well as a specific action. While the library version of CommandTopic can only match specific actions such as TakeAction or DropAction, the modified CommandTopic can additionally match one or more direct objects as well, the object or objects specified in its matchDobj property. This may be specified as either a single object to match, a list of objects to match, a class of objects to match, or a list of classes of objects to match. It should also work perfectly well as a mixed list of classes and objects. In exactly the same way the matchIObj property can be used to match the indirect object of any command issued, CommandTopic also defines some additional methods primarily for convenience of access in the topicResponse method (to avoid the need to have to override handleTopic)

    The following properties and methods have been added to CommandTopic:

    ·handleAction(fromActor, action) - This method is called before topicResponse, and contains any handling of the action you wish. Its main purpose is to provide a hook for CommandHelper to use, but it can also be used for your own handling.  
    ·actionResponse(fromActor, action) - this is called after topicResponse, and is basically intended as an alternative to topicResponse when the response needs access to the action that's being commanded.  
    ·obeyCommand - if this is set to true, then ActorState.obeyCommand will return true to allow the target actor to obey the command just as it was given.  
    ·matchDobj - the direct object, class of direct object (e.g. Food, Wearable), or list of direct objects/classes that you want this CommandTopic to match. If this is left at nil it will simply be ignored.  
    ·matchIobj - the indirect object, class of indirect object (e.g. Container, Surface), or list of indirect objects/classes that you want this CommandTopic to match. If this is left at nil it will simply be ignored.  

    The new CommandHelper mix-in class overrides handleAction and provides the following additional properties:

    ·cmdDobj - the current direct object of the command that the CommandTopic matched on this occasion. For example,. if matchDobj is the list [brassCoin, goldCoin, silverCoin], and matchObj (which matches the action on a CommandTopic or TCommandTopic) is TakeAction, then if the TCommandTopic is responding to "bob, take silver coin", cmdDobj will be silverCoin.  
    ·cmdIobj - the current indirect object, if there is one. E.g. if the CommandTopic matched the command "Bob, put the coin in the slot", this will contain the slot object.  
    ·cmdAction - the current action that's being command. This simply makes it available to methods like topicResponse which otherwise wouldn't have access to it.  
    ·cmdTopic - if the action commanded is a TopicActionBase or subclass thereof (e.g. Bob, ask Fred about boots) then this holds the topic associated with the command (in this example, the 'boots' topic).  
    ·cmdPhrase - this is simply cmdAction.getInfinitivePhrase, e.g. if the command was "Bob, put the red book on the shelf" it will contain something like "put the red book on the shelf." This can be useful when the CommandTopic may have matched a number of different commands and we want to construct a sentence containing the command in topicResponse, e.g. "<q>Bob, <<cmdPhrase>>, will you?</q>". Note you might sometimes want the participle phrase, which you can get, for example, with "<q>Bob, would you mind terribly <<cmdAction.getParticiplePhrase>>?</q>", which might come out as "Bob, you mind terribly putting the red book on the shelf."  
    ·topicCmdPhrase() - this is a method that returns the command phrase (e.g. "ask Bob about boots") associated with a command to perform a TopicAction (e.g. "fred, ask bob about boots."). It it called by cmdPhrase when necessary, and was added to circumvent a run-time error that would otherwise occur. You probably won't need to use this method - you can just use cmdPhrase.  

    In addition, two properties have been added to ActorState to enable trapping of commands that normally don't make much sense when directed to an NPC, along with two methods to explain why these commands are blocked:

    ·autoBlockSystemCommands - if true (the default) then any attempt to direct a system command to this actor when he's in this ActorState (e.g. Bob, Restore) will automatically be blocked, and the explainBlockSystemCommand() method called to explain why.  
    ·autoBlockTopicCommands - if true (the default) then any attempt to ask the actor who's in this ActorState to perform a conversational command (e.g. Bob, Ask Sally about lighthouse) will automatically be blocked, and the explainBlockTopicCommand() method called to explain why.  

    For your convenience, TCommandTopic is now defined simply as

    class TCommandTopic : CommandHelper, CommandTopic
    ;

    For an example of a TCommandTopic, if you wanted Bob to respond to the commands TAKE THE DIAMOND or TAKE THE GOLD COIN by obeying these (but no other) take commands, you could code a TCommandTopic thus:

    + TCommandTopic @TakeAction
       matchDobj = [diamond, goldCoin]
       topicResponse
      {
         "<q>Bob, would you <<cmdPhrase>>, please?</q> you ask.\b
          <q>Certainly,</q> he replies. ";
          nestedActorAction(getActor, Take, cmdDobj);
      }
    ;

    Note that you cannot necessarily assume that the command will succeed, however; in the above example, for instance, you'd probably want to test that Bob wasn't already holding the object in question before displaying an interchange that implies his willingness to take it. Again, you'd want to check that the nestedActorAction had succeeded (in this case, by testing that the currentDobj had changed location as a result of it) before displaying a message that implies that it has.

    You can also use TCommandTopic with a command using two objects (direct and indirect). It's then up to you whether it matches on the action only, or also on either the direct object or the indirect object or both. You may, for example, want it to match only on the direct object: if Bob is willing to put the red book in the black bag but nowhere else, you'd want to give the player an indication that Bob might be willing to put the red book somewhere, rather than a generic message suggesting he's unwilling to do anything with it at all, so you might write:

    + TCommandTopic @PutInAction
       matchDobj = redBook
       topicResponse
       {
          "<q>Bob, be a good fellow and <<actionPhrase>>, would you?</q> you ask.\b";
          if(cmdIobj == blackBag)
          {
             "<q>Sure.</q> Bob agrees readily. ";
             nestedActorAction(getActor, PutIn, cmdDobj, blackBag);
          }
          else
             "<q>I don't think I should put it there.</q> he replies dubiously. ";
       }
    ;

    Once again, it should be emphasized that the nestedActorAction may not succeed, so you may want to provide more sophisticated handling to allow for this possibility.

    Commands with Multiple Direct Objects

    It is possible that a player might enter a command such as:

    >bob, take the brass coin, the small magnet, the large rifle, and the damp cucumber

    Potentially, this could become quite difficult to handle in a CommandTopic. If you wanted Bob to accept commands to take the coin or the rifle, but not the magnet or the cucumber, simply matching this input in a CommandTopic would become problematic; proceeding to sort out Bob's response to being told to take all four objects could become even more so. It would be much easier to have one CommandTopic to handle the combination of action and direct object that Bob is willing to act upon, and another to handle those he isn't (this could either be a simple CommandTopic with a lower matchScore to pick up all other Take commands, or a DefaultCommandTopic).

    To make this situation easier to handle, therefore, the TCommand module modifies ActorState.obeyCommand so that it splits any action on multiple direct objects into a sequence of actions on single direct objects. That way, any TCommandTopic (or CommandTopic or DefaultCommandTopic) can be certain that it only has to handle one direct object at a time.

    At the same time, the overridden ActorState.obeyCommand sets each direct object in turn as the current direct object of the action object it passes to the CommandTopic.

    Changes to DefaultCommandTopic

    TCommand modified DefaultCommandTopic by adding the same handleAction(fromActor, action), actionResponse(fromActor, action) and obeyCommand methods/properties that have been added to CommandTopic. For your convenience the module also defines:

    class DefaultTCommandTopic : CommandHelper, DefaultCommandTopic
    ;


    This makes it easier to define a DefaultCommandTopic that displays a meaningful exchange between the player character and the target actor, such as:

    + DefaultTCommandTopic
       "<q>Would you <<cmdPhrase>> please, Bob?</q> you ask.\b
        <q>Actually, no, I won't.</q> he refuses. "
    ;

    The purpose of this is mainly to gain a more elegant transcript where a conversation is started by addressing a command to an NPC, and the greeting protocols intervene between the command typed by the player and the message displayed bu the DefaultCommandTopic. Rather than have :

    >bob, follow me
    "Hello Bob, nice to see you again. How are you?" you greet him.

    "I'm fine. It's good to see you too." he replies warmly.

    "Actually, no, I won't." he refuses.

    This allows us to have a transcript that reads:

    >bob, follow me
    "Hello Bob, nice to see you again. How are you?" you greet him.

    "I'm fine. It's good to see you too." he replies warmly.

    "Would you follow me please, Bob?" you ask.

    "Actually, no, I won't." he refuses.


    Which is an obvious improvement.

    SuggestedCommandTopic
    The new SuggestedCommandTopic is a SuggestedTopic class for use with CommandTopic or TCommandTopic. The name property should be set to something suitable to follow "You could tell her to ".



    Finally, AltCommandTopic is simply a version of AltTopic for use with CommandTopic. It adds the same handleAction(fromActor, action), actionResponse(fromActor, action) and obeyCommand methods/properties that have been added to CommandTopic. Predictably, there is also an AltTCommandTopic defined as:

    AltTCommandTopic : CommandHelper, AltCommandTopic
    ;




    frobtads-1.2.3/tads3/lib/extensions/TCommand/TCommand.t0000644000175000001440000003365411235302453022071 0ustar realncusers#charset "us-ascii" #include #include ModuleID { name = 'TCommandTopic Library Extension' byline = 'by Eric Eve' htmlByline = 'by Eric Eve' version = '3.2' listingOrder = 70 } /* * VERSION HISTORY * * v3.2 21-Jul-09 - adds cmdDir to CommandHelper so that the direction of a * TravelAction can be inspected. * * v3.1 01-Mar-08 - Added handling on Actor to trap System Actions issued * as commands to NPCs. * * v3.0 08-Jul-07 - Added handling to ActorState to allow optional blocking * of SystemActions and TopicActions directed at the Actor without needing * to provide custom CommandTopics to trap them. * Also enhanced CommandHelper.proper() to deal more intelligently with * possessives. * * v2.2 11-Dec-05 - Added handling to optionally give or restore initial * capitals to proper names used in cmdPhrase. * * v2.1 10-Dec-05 - Added handling to CommandHelper to prevent run-time * error when constructing cmdPhrase in the case of a TopicAction type * command (e.g. fred, ask me about john). * * v2.0 27-Mar-04 - Substantial changes: *.- Move the matchTopic handling into CommandTopic *.- Allow CommandTopic to match the indirect object as well as the direct * object of commands. *.- Define a new CommandHelper * mix-in class to make it more convenient to get at the current command * action and its objects *.- Define two new methods on CommandTopic, * handleAction(fromActor, action) and actionResponse(fromActor, action) * for convenience of command handling. *.- Define new AltCommandTopic and * AltTCommandTopic classes * * v1.1 13-Mar-04 *.- Move handleTopic, actionPhrase and currentAction into * CommandTopic *.- Add an obeyCommand property to CommandTopic & * DefaultCommandTopic *.- Add a currentIObj property to TCommandTopic *.- Add the ExecCommandTopic class */ /* modify DefaultCommandTopic to make two new properties available */ modify DefaultCommandTopic handleTopic(fromActor, action) { getActor.lastCommandTopic_ = self; handleAction(fromActor, action); inherited(fromActor, action); actionResponse(fromActor, action); } /* This method performs any handling we want before * topicResponse; it's mainly here * to be overridden by CommandHelper, but can be used * for any purposes authors want. */ handleAction(fromActor, action) { } /* This method can be overridden to provide responses * that need to know the issuing actor or the action * commanded. It is performed after topicResponse. */ actionResponse(fromActor, action) { /* by default - do nothing */ } matchScore = 3 obeyCommand = nil ; modify CommandTopic /* * The direct object, or a list of direct objects, that will be matched * by this topic. If this is left at nil, any direct object or none * will be matched. */ matchDobj = nil /* * The indirect object, or a list of indirect objects, that will be matched * by this topic. If this is left at nil, any indirect object or none * will be matched. */ matchIobj = nil matchTopic(fromActor, action) { /* First check whether we match the action of the command */ if(!inherited(fromActor, action)) return nil; /* Then check whether we match the direct object of the command action*/ if(matchDobj != nil && !matchObjs(action.getDobj, matchDobj)) return nil; /* Finally, check whether we match its indirect object */ if(matchIobj != nil && !matchObjs(action.getIobj, matchIobj)) return nil; /* We've matched everything we should, so return matchScore */ return matchScore; } matchObjs(obj, objToMatch) { if(objToMatch.ofKind(Collection)) { return objToMatch.indexWhich({x: obj.ofKind(x)}); } else { return obj.ofKind(objToMatch); } } handleTopic(fromActor, action) { /* Tell our actor that this is the commandTopic that has * been matched and is responding. */ getActor.lastCommandTopic_ = self; handleAction(fromActor, action); inherited(fromActor, action); actionResponse(fromActor, action); } /* This method performs any handling we want before * topicResponse; it's mainly here * to be overridden by CommandHelper, but can be used * for any purposes authors want. */ handleAction(fromActor, action) { } /* * An additional method for providing customised responses * that provides ready access to the fromActor and action parameters */ actionResponse(fromActor, action) { /* by default - do nothing */ } /* Set this to true if you want the actor to obey the command just as * it was issued */ obeyCommand = nil ; /* Mix-in Class for use with CommandTopic and DefaultCommandTopic * Principally designed to cache the currently commanded action * and its most commonly-useful properties. This is mainly for * convenience, and allows the additional properties to be accessed * from topicResponse. */ CommandHelper : object handleAction(fromActor, action) { cmdDobj = action.getDobj; cmdIobj = action.getIobj; cmdTopic = action.getTopic; cmdAction = action; cmdDir = action.getDirection; cmdPhrase = proper(action.ofKind(TopicActionBase) ? topicCmdPhrase : youToMe(action.getInfPhrase)); } cmdDobj = nil cmdIobj = nil cmdAction = nil cmdPhrase = nil cmdTopic = nil cmdDir = nil topicCmdPhrase { /* * This is a hack to get round the fact that * TopicTAction.getVerbPhrase() references gTopicText, which in turn * references gAction (which is the wrong action at this point), so * we temporarily need gAction to refer to the action commanded, not * the EventAction which is the real gAction at this point. */ local oldAction; try { oldAction = gAction; gAction = cmdAction; return youToMe(cmdAction.getInfPhrase); } finally { gAction = oldAction; } } /* Takes a string and replaces any occurrence of a word that exists in * the list of properNames with the same word with an initial capital. */ proper(str) { foreach(local name in nilToList(properNames)) { local name1 = ' ' + name; local name2 = ' ' + name.substr(1,1).toUpper + name.substr(2); str = str.findReplace(name1, name2, ReplaceAll); } /* * If the current npc is mentioned in the possessive, change this * to 'your'; also replace the npc's name with 'yourself'. */ str = langMessageBuilder.generateMessage(str); str = rexReplace('%<' + getActor.theName + '<`|squote>s%>', str, 'your', ReplaceAll); str = rexReplace('%<' + getActor.theName + '%>', str, 'yourself', ReplaceAll); return str; } /* * Modify this to contain a list of proper names that you want to * appear with an initial capital if they appear in a cmdPhrase */ properNames = [] ; function youToMe(str) { if(str.ofKind(String)) { str = str.findReplace(' you ', ' me ', ReplaceAll); if(str.endsWith(' you')) str = str.findReplace(' you', ' me', ReplaceOnce, str.length-5); } return str.toLower(); } /* * TCommandTopic is a CommandTopic that can match a particular direct * object or set of direct objects as well as a particular action or * set of actions. This allows the TCommandTopic's topicResponse() * method either to display a message specific to a particular action * and direct object, but also, optionally, to define the actions * the target actor takes in response. * * For example, to define a TCommandTopic that matches the command * >BOB, TAKE THE GOLD COIN * or * >BOB, TAKE THE DIAMOND * * and then displays a suitable conversational interchange and * had Bob take the gold coin you might define: * * + TCommandTopic @TakeAction * matchDobj = [goldCoin, diamond] * topicResponse() * { * "Bob, <>, would you?, you ask.\b * Sure, he agrees readily. "; * nestedActorAction(getActor, Take, currentDobj); * } * ; */ class TCommandTopic : CommandHelper, CommandTopic ; class DefaultTCommandTopic : CommandHelper, DefaultCommandTopic ; modify Actor /* Keep track of the last CommandTopic triggered on this actor */ lastCommandTopic_ = nil /* By default, trap system commands targeted to NPCs */ obeyCommand(issuingActor, action) { if(action.ofKind(SystemAction) && obeySystemCommands == nil) { systemActionToNPC(); return nil; } return inherited(issuingActor, action); } systemActionToNPC() { libMessages.systemActionToNPC(); } obeySystemCommands = nil ; /* * Modification to ActorState.obeyCommand() to make it work with * TCommandTopic. This modification affects only actions that * have one or more direct objects, and does the following: * * 1) If the action has more than one direct object, it is * split into a series of actions each having only one * direct object, taking each direct object in turn. * This allows the game author to assume that any * TCommandTopic will only have one direct object to * deal with at a time, facilitating the handling when * the author may wish to have the target actor make * different responses where the direct objects are different. * * 2) For each action with a direct object passed to handleConversation * (and then to a CommandTopic, TCommandTopic or DefaultCommandTopic) * the current direct object is marked as such on the action object * passed; this allows CommandTopic, TCommandTopic and DefaultCommandTopic * to use methods such as getDobj and getInfPhrase that assume that * the direct object is defined. */ modify ActorState obeyCommand(issuingActor, action) { /* * Reset the lastCommandTopic to nil in case the later * handling bypasses setting it, and we're left with * the spurious residue of a previous command. */ getActor.lastCommandTopic_ = nil; /* * If we direct a SystemAction or a TopicAction at the actor, it * should normally be blocked. Commands like BOB, SAVE or BOB, ASK * SALLY ABOUT FRED are unlikely to have satisfactory handling, * and will typically produce suboptimal responses in * DefaultCommandTopics, so it's easiest to block them globally * here. We also provide a couple of flags to allow this to be * easily overridden on a per-state basis if desired. */ if(autoBlockSystemCommands && action.ofKind(SystemAction)) { explainBlockSystemCommand(); return nil; } if(autoBlockTopicCommands && action.ofKind(TopicActionBase)) { explainBlockTopicCommand(); return nil; } /* * If the action takes a direct object (and so could have a list * of direct objects), split it into a series of actions * with a single direct object */ local dobjLst = action.getResolvedDobjList; if(dobjLst != nil) { local singleAction = action; local iobjLst = action.getResolvedIobjList; if(iobjLst != nil) singleAction.iobjCur_ = iobjLst[1]; foreach(local obj in dobjLst) { singleAction.dobjCur_ = obj; handleConversation(issuingActor, singleAction, commandConvType); } } /* Otherwise, just treat it as a single command */ else handleConversation(issuingActor, action, commandConvType); /* * check whether to accept or refuse the command, based on * the decision of the CommandTopic matched */ return getActor.lastCommandTopic_ != nil && getActor.lastCommandTopic_.obeyCommand; } autoBlockSystemCommands = true explainBlockSystemCommand() { playerMessages.systemActionToNPC(); } autoBlockTopicCommands = true explainBlockTopicCommand() { "It d{oes|id}n't make much sense to ask <> to do that. "; } ; /* AltCommandTopic is an AltTopic with the a special handleTopic * method to facilitate the handling of commands. */ AltCommandTopic : AltTopic handleTopic(fromActor, action) { getActor.lastCommandTopic_ = self; handleAction(fromActor, action); inherited(fromActor, action); actionResponse(fromActor, action); } handleAction(fromActor, action) { } /* * An additional method for providing customised responses * that provides ready access to the fromActor and action parameters */ actionResponse(fromActor, action) { /* by default - do nothing */ } /* Set this to true if you want the actor to obey the command just as * it was issued */ obeyCommand = nil ; AltTCommandTopic : CommandHelper, AltCommandTopic ; class SuggestedCommandTopic : SuggestedTopic suggestionGroup = [suggestionCommandGroup] fullName = ('tell {it targetActor/him} to ' + name) ; suggestionCommandGroup : SuggestionListGroup groupPrefix = "tell {it targetActor/him} to " ; frobtads-1.2.3/tads3/lib/extensions/smartAccompany.t0000644000175000001440000001471711210750426021645 0ustar realncusers#charset "us-ascii" #include #include /* * smartAccompany.t * * by Eric Eve * * A small extension that improves the ordering and grouping of reports * from an NPC in an AccompanyingState following the player character. * For a full explanation of what this extension does, and how it works, * see Example 3 in the Tech Man article on Manipulating the Transcript. * * Version 1.3; 27-May-09 * * Version 1.3 fixes a bug which causes the name of the following actor * (and nothing else) to be displayed in a separate sentence when the * actor follows the player character from a dark room. * * version 1.2; 04-May-09 * * Version 1.2 fixes a bug that can cause a run-time error when a report * in the transcript doesn't have a messageText_ property. * * Version 1.1 fixes a bug that could cause a run-time error when travel * between rooms is disallowed. * * */ ModuleID name = 'smartAccompany' byline = 'by Eric Eve' htmlByline = 'by Eric Eve' version = '1.3' listingOrder = 75 ; /* * Combine reports of the NPC's actions in the old location into a single * sentence and move it to just before the new room description. */ modify AccompanyingState beforeTravel(traveler, connector) { gAction.callAfterActionMain(self); inherited(traveler, connector); } afterActionMain() { /* * Find the insertion point, which is just before the description * of the new room. */ local idx = gTranscript.reports_.indexWhich({ x: x.messageText_ == '<.roomname>' }); /* * If there is no insertion point, travel failed for some reason, * in which case we don't want to change the transcript at all. */ if(idx == nil) return; local actor = getActor; local str = ''; local vec = new Vector(4); local pat = new RexPattern('^' + actor.theName + '+'); /* * Look through all the reports in the transcript for those whose * message text begins with the name of our actor, followed by at * least one space (so that if the actor's name is Rob we don't * pick up any messages relating to Roberta, for example), * regardless of case (so we pick up messages about 'the tall man' * and 'The tall man'). * * Once we get to the description of a new room, stop looking (we * don't want to include the description of the actor arriving in * the new location, just the actor departing the old one). * * Store the relevant message strings (those before the new room * description, but not any after it) in the vector vec; at the * same time remove these reports from the transcript (since we'll * be replacing them with a single report below). */ while((idx = gTranscript.reports_.indexWhich({ x: x.messageText_ != nil && rexMatch(pat, x.messageText_) })) != nil) { if(idx > gTranscript.reports_.indexWhich({ x: x.messageText_ == '<.roomname>' })) break; vec.append(gTranscript.reports_[idx].messageText_); gTranscript.reports_.removeElementAt(idx); } local len = actor.theName.length() + 1; /* * Now go through each of the strings in vec in turn, stripping * off the actor's name at the start and the period/full-stop at * the end (so that, for example 'Bob stands up. ' becomes 'stands * up'. In searching for the position of the terminating full * stop (period) we start beyond the end of the actor's name in * case the actor's name contains a full stop (e.g. Prof. Smith). */ vec.applyAll( { cur: cur.substr(len, cur.find('.', len) - len)}); /* * Concatanate these truncated action reports into a single string * listing the actor's actions (e.g. 'stands up and comes with * you'), separating the final pair of actions with 'and' and any * previous actions with a comma. We can use stringLister (defined * in the previous example) to do this for us. */ str = stringLister.makeSimpleList(vec.toList); /* * If we're left with an empty string, there's nothing left to do, * so we should stop here rather than go on to display a * fragmentary sentence. */ if(str == '') return; /* * Put the actor's name back at the start of the string, and * conclude the string with a full-stop and a paragraph break. */ str = '\^' + actor.theName + ' ' + str + '.<.p>'; /* * Find the insertion point, which is just before the description * of the new room. This may have changed as a result of our * manipulations above. */ idx = gTranscript.reports_.indexWhich({ x: x.messageText_ == '<.roomname>' }); /* * Insert the message we just created as a new report at the * appropriate place, i.e. just before the new room description. */ gTranscript.reports_.insertAt(idx, new MessageResult(str)); } ; /* * The modifications on AccompanyingState probably aren't so suitable for a * GuidedTourState, so we make afterActionMain() do nothing by default. This * can be overridden by game authors if desired. */ modify GuidedTourState afterActionMain() { } ; /* * Ensure that the default sayDeparting() message from * AccompanyingInTravelState is added to the transcript in a single report, * starting with the NPC's name. */ modify AccompanyingInTravelState sayDeparting(conn) { local msg = mainOutputStream.captureOutput( {: inherited(conn) });; if(msg.startsWith('\^')) msg = msg.substr(2); mainReport(msg); } ; frobtads-1.2.3/tads3/lib/extensions/SimpleAttachable.t0000644000175000001440000002471610661551344022075 0ustar realncusers#charset "us-ascii" #include #include /* * SIMPLE ATTACHABLE * * SimpleAttachable version 1.0 by Eric Eve * * This file defines the SimpleAttachable class together with some * supporting objects. Feel free to use this in your own games if you find * it useful. * * Attachables in general are complicated to handle, because they can * behave in so many different ways. The SimpleAttachable class is meant * to make handling one common case easier, in particular the case where a * smaller object is attached to a larger object and then moves round with * it. * * More formally, a SimpleAttachable enforces the following rules: * * (1) In any attachment relationship between SimpleAttachables, one * object must be the major attachment, and all the others will be that * object's minor attachments (if there's a fridge with a red magnet and a * blue magnet attached, the fridge is the major attachement and the * magnets are its minor attachments). * * (2) A major attachment can have many minor attachments attached to * it at once, but a minor attachment can only be attached to one major * attachment at a time (this is a consequence of (3) below). * * (3) When a minor attachment is attached to a major attachment, the * minor attachment is moved into the major attachment. This automatically * enforces (4) below. * * (4) When a major attachment is moved (e.g. by being taken or pushed * around), its minor attachments automatically move with it. * * (5) When a minor attachment is taken, it is automatically detached * from its major attachment (if I take a magnet, I leave the fridge * behind). * * (6) When a minor attachment is detached from a major attachment it * is moved into the major attachment's location. * * (7) The same SimpleAttachable can be simultaneously a minor item * for one object and a major item for one or more other objects (we could * attach a metal paper clip to the magnet while the magnet is attached to * the fridge; if we take the magnet the paper clip comes with it while the * fridge is left behind). * * (8) If a SimpleAttachable is attached to a major attachment while * it's already attached to another major attachment, it will first be * detached from its existing major attachment before being attached to * the new one (ATTACH MAGNET TO OVEN will trigger an implicit DETACH * MAGNET FROM FRIDGE if the magnet was attached to the fridge). * * (9) Normally, both the major and the minor attachments should be of * class SimpleAttachable. * * * Setting up a SimpleAttachable is then straightforward, since all the * complications are handled on the class. In the simplest case all the * game author needs to do is to define the minorAttachmentItems property * on the major SimpleAttachable to hold a list of items that can be * attached to it, e.g.: * * minorAttachmentItems = [redMagnet, blueMagnet] * * If a more complex way of deciding what can be attached to a major * SimpleAttachable is required, override its isMajorItemFor() method * instead, so that it returns true for any obj that can be attached, e.g.: * * isMajorItemFor(obj) { return obj.ofKind(Magnet); } * * One further point to note: if you want a Container-type object to act * as a major SimpleAttachment, you'll need to make it a ComplexContainer. * */ ModuleID name = 'SimpleAttachable' byLine = 'by Eric Eve' htmlByLine = 'by Eric Eve' version = '1.0' ; class SimpleAttachable: Attachable /* Move the minor attachment into the major attachment. */ handleAttach(other) { if(other.isMajorItemFor(self)) moveInto(other); } /* * When we're detached, if we were in the other object move us into the * other object's location. */ handleDetach(other) { if(isIn(other)) moveInto(other.location); } /* * If a minor attachment is taken, first detach it from its major * attachment. */ dobjFor(Take) { preCond = (nilToList(inherited) + objNotAttachedToMajor) } /* * If we're attached to a major attachment, treat TAKE US FROM MAJOR as * equivalent to DETACH US FROM MAJOR. */ dobjFor(TakeFrom) maybeRemapTo(isAttachedToMajor, DetachFrom, self, location) /* * If we're already attached to a major attachment, detach us from it * before attaching us to a different major atttachment. */ dobjFor(AttachTo) { preCond = (nilToList(inherited) + objDetachedFromLocation) } iobjFor(AttachTo) { preCond = (nilToList(inherited) + objDetachedFromLocation) } /* We're a major item for any item in our minorAttachmentItems list. */ isMajorItemFor(obj) { return nilToList(minorAttachmentItems).indexOf(obj) != nil; } /* * The list of items that can be attached to us for which we would be * the major attachment item. */ minorAttachmentItems = [] /* * A pair of convenience methods to determined if we're attached to any * items that are major or minor attachments relative to us. */ isAttachedToMajor = (location && location.isMajorItemFor(self)) isAttachedToMinor = (contents.indexWhich({x: self.isMajorItemFor(x)}) != nil) /* * Define if this item be listed when it's a minor item attached to * another item. */ isListedWhenAttached = true isListed = (isAttachedToMajor ? isListedWhenAttached : inherited ) isListedInContents = (isAttachedToMajor ? nil : inherited ) /* * Customise the listers so that if we contain minor items as * attachments they're shows as being attached to us, not as being in * us. */ contentsLister = (isAttachedToMinor ? majorAttachmentLister : inherited) inlineContentsLister = (isAttachedToMinor ? inlineListingAttachmentsLister : inherited ) /* * A SimpleAttachment can be attached to another SimpleAttachment if * one of the SimpleAttachments is a major item for the other. */ canAttachTo(obj) { return isMajorItemFor(obj) || obj.isMajorItemFor(self); } /* * If I start the game located in an object that's a major item for me, * presumbably we're meant to start off attached. */ initializeThing() { inherited; if(location && location.isMajorItemFor(self)) attachTo(location); } ; /* * Custom lister to show the contents of a major attachment as being * attached to it. */ inlineListingAttachmentsLister: ContentsLister showListEmpty(pov, parent) { } showListPrefixWide(cnt, pov, parent) { " (to which < 1 ? '{are|were}' : '{is|was}'>> attached "; } showListSuffixWide(itemCount, pov, parent) { ")"; } ; /* Special precondition for use when taking a minor attachment. */ objNotAttachedToMajor: PreCondition /* * Other things being equal, prefer to take an item that's not a minor * attachment (if the blue magnet is attached to the fridge and the red * magnet is lying on the floor, then make TAKE MAGNET take the red * one). */ verifyPreCondition(obj) { if(obj.location.isMajorItemFor(obj)) logicalRank(90, 'attached'); } checkPreCondition(obj, allowImplicit) { /* * if we don't already have any non-permanent attachments that * are the major attachments for us, we're fine (as we don't * require removing permanent attachments); nothing more needs to * be done. */ if (obj.attachedObjects.indexWhich( {x: x.isMajorItemFor(obj) && !obj.isPermanentlyAttachedTo(x) }) == nil) return nil; local major = obj.attachedObjects.valWhich({x: x.isMajorItemFor(obj)}); /* * Try implicitly detaching us from our major attachment. */ if (allowImplicit && tryImplicitAction(DetachFrom, obj, major)) { /* * if we're still attached to a major attachment, we failed, * so abort */ if (obj.attachedObjects.indexWhich( {x: !obj.isPermanentlyAttachedTo(x) && x.isMajorItemFor(obj)}) != nil) exit; /* tell the caller we executed an implied action */ return true; } /* we must detach first */ reportFailure(&mustDetachMsg, obj); exit; } ; /* * Special precondition to detach us from an existing major attachment * before attaching us to another one. This needs to be different from * objNotAttachedToMajor so that we don't perform any unnecessary * detachments. */ objDetachedFromLocation: PreCondition checkPreCondition(obj, allowImplicit) { /* * If the other object involved in the command is not a majorItem * for us, or we're not already in an object that we're attached * to which is our major item, then there's nothing to do. */ local other = (obj == gDobj ? gIobj : gDobj); local loc = obj.location; if(!other.isMajorItemFor(obj) || !(loc.isMajorItemFor(obj) && obj.isAttachedTo(loc))) return nil; /* * if we don't already have any non-permanent attachments that * are the major attachments for us, we're fine (as we don't * require removing permanent attachments); nothing more needs to * be done. */ if (allowImplicit && tryImplicitAction(DetachFrom, obj, loc)) { /* if we're still attached to anything, we failed, so abort */ if (loc.isMajorItemFor(obj) && obj.isAttachedTo(loc)) exit; /* tell the caller we executed an implied action */ return true; } /* we must detach first */ reportFailure(&mustDetachMsg, obj); exit; } ; frobtads-1.2.3/tads3/lib/extensions/customBanner.t0000644000175000001440000005455510501754006021330 0ustar realncusers#charset "us-ascii" #include #include /* * Custom Banner version 1.2 * by Eric Eve * * Version date: 13-Sep-06 * * This file implements a CustomBannerWindow class that vastly eases * the process of setting up banners and displaying material in them. * e.g. to set up a graphics banner to display pictures, starting with * pic1.jpg at startup, but not appearing at all on an interpreter that * can't display JPEGs you could define: * * pictureWindow: CustomBanner * canDisplay = (systemInfo(SysInfoJpeg)) * bannerArgs = [nil, BannerAfter, statuslineBanner, BannerTypeText, * BannerAlignTop, 10, BannerSizeAbsolute, BannerStyleBorder] * currentContents = '' * ; * * Then to change the picture dislayed at a later point, call: * * pictureWindow.updateContents(''); * * And everything else, including getting everything right on RESTART, UNDO * and RESTORE should be taken care of. */ /* ------------------------------------------------------------------------ */ /* * A CustomBannerWindow, like a BannerWindow, corrsponds to an on-screen * banner. The purpose of CustomBannerWindow is to eliminate most of the * busy-work that a game author would otherwise have to take care of in * displaying and manipulating banners. * * As with BannerWinnow, merely creating a CustomBannerWindow does not * display the banner. However, any CustomBannerWindows in existence at * the start of the game will be added to the screen display, unless the * condition specified in their shouldDisplay() method prevents initialization. * * The one property that must be defined on each instance of a CustomBannerWindow * is bannerArgs, which takes the form: * * bannerArgs = [parent, where, other, windowType, align, * size, sizeUnits, styleFlags] * * where each list element has the same meaning at the corresponding argument * to BannerWindow.showBanner() * * This merely ensures that the CustomBannerWindow is added to the screen's * banner window layout. To have the CustomBannerWindow display some content * when first added to the screen layout, override its current contents property: * * currentContents = 'My initial contents' * * To change what's displayed in a CustomBannerWindow from game code, call its * updateContents() method, e.g.: * * pictureWindow.updateContents(''); * * To redisplay the current contents, call the showCurrentContents() method. * By default a call to updateContents() or showCurrentContents() clears the * window before displaying the new content. To have the additional content * added to the existing content, change the clearBeforeUpdate property to nil. * * You can control whether the game uses this banner at all by overriding * the canDisplay property. The main purpose of this property is to easily allow * a game to run on different types of interpreter. For example, if your banner is * meant to display pictures in the JPEG format, there's no point having it included * in the screen layout of an interpreter that can't display JPEGs, and attempts to * update its contents with a new picture should do nothing. In which case we could * define: * * canDisplay = (systemInfo(SysInfoJpeg)) * * Calls to CustomBannerWindow methods like updateContents() and clearWindow() * should be safe on an interpreter for which shouldDisplay returns nil, since * by default these will do nothing beyond updating the banner's currentContents * property. This makes it easier to write game code that is suitable for all * classes of interpreter * * To have a CustomBannerWindow resize to contents each time its contents are * displayed, set its autoSize property to true. * * If you do not want a CustomBannerWindow you have defined not to be dispayed * at game startup, set its isActive property to nil. Call the activate() * method to add it to the screen layout and the deactivate() method to remove * it, or any other CustomBannerWindow, from the screen display. * * Obviously, it is then the game author's responsibility to ensure that no * other banner window that's meant to persist after pictureWindow is deactivated * depends on pictureWindow for its existence; i.e. that we're not deactivating * the parent of some other banner window(s) we want to keep or subsequently * activate, or the sibling of any banner window that's subsequently going to * defined in relation to us. */ class CustomBannerWindow: BannerWindow /* * The list of any banner windows that must be set up before me, * either one of them is my parent, or because I'm going * to be placed before or after them with BannerBefore or BannerAfter. * * If bannerArgs has been set up with the list of showBanner arguments, * then we can derive this information automatically */ initBeforeMe() { /* * If our bannerArgs property contains a list of the right length, i.e. 8 * elements, then the first and third elements of the list (our parent, and * the sibling we're to be placed before or after) must be initialized before * we are. If either of these is nil, no harm is done, since initBannerWindow() * will simply skip the nil value. * * Moreover, if our sibling is in the list, we don't need our parent as well, * since either our sibling or one of its siblings will initialize our parent. */ local lst = []; if (propType(&bannerArgs) == TypeList && bannerArgs.length() == 8) lst = bannerArgs[3] ? [bannerArgs[3]] : [bannerArgs[1]]; initBeforeMe = lst; return lst; } /* * A condition to test whether this banner window should actually display. * Normally this would test for the interpreter class if this would * affect whether we wanted this banner to be created. For example, if * we were going to use this banner window to display a JPEG picture, we * might not this window to display at all if the interpreter we're running * on can't display JPEGS, so we might write: * * canDisplay = (systemInfo(SysInfoJpeg)) * * If your complete system of CustomBanners depends on the same condition * (e.g. you don't want any CustomBanners if the interpreter we're running * on can't display JPEGs, then it's probably easiest to modify CustomBanner * and override scanDisplay on the modified class. * * By default, we simply check that the interpreter we're running on * can display banners. */ canDisplay = (systemInfo(SysInfoBanners)) shouldDisplay = (canDisplay && isActive) /* * The standard use of initBannerWindow is first to ensure that any * banner windows whose existence we presuppose have themselves been * initialized, and then to set up our own window on screen. * This function should be used for initializing banner window *layout*, * not content. */ initBannerWindow() { /* * If we shouldn't display on this class of interpreter, don't * initialize us. */ if(!shouldDisplay) return nil; /* * If we've already been initialized, there's nothing left to do. */ if(inited_) return true; /* * Initialize all the bannner windows on whose existence our own * depends. If one of them can't be initialized, neither can we, * in which case return nil to show that our initialization failed. * If, however, the parent or sibling banner window we want initialized * before us is not a CustomBannerWindow, then its initBannerWindow() * won't have a return value, in which case we ignore the fact that * it returns nil */ foreach(local ban in initBeforeMe) if(ban && !ban.initBannerWindow() && ban.ofKind(CustomBannerWindow)) return nil; /* * Create my banner window on screen; if this fails return nil * to indicate that the window could not be created */ return (inited_ = initBannerLayout()); } /* * Initialize my onscreen layout, normally through a call to showBanner(), * whose return value this method should return, e.g.: * * initBannerLayout() * { * return showBanner(nil, BannerAfter, statuslineBanner, * BannerTypeText, BannerAlignTop, 1, BannerSizeAbsolute, * BannerStyleBorder); * } * * By default we simply call initBannerLayout() using our bannerArgs. */ initBannerLayout() { return showBanner(bannerArgs...); } /* * The list of args used to define our screen layout, as they would be passed * to showBanner. This is used both by initBannerLayout and initBeforeMe. * * The args should be listed in the form * * bannerArgs = [parent, where, other, windowType, align, size, sizeUnits, styleFlags] * * e.g. * bannerArgs = [nil, BannerAfter, statuslineBanner, * BannerTypeText, BannerAlignTop, 1, BannerSizeAbsolute, * BannerStyleBorder] * */ bannerArgs = nil /* * The current contents to be displayed in this window, which could be * a string of text, or the HTML string to display a picture. * * currentContents can be overridden to hold the initial contents * we want this banner to display, but it should not otherwise be * directly written to in game code. To display new contents in the * banner, use updateContents() instead. */ currentContents = '' /* * Is this banner currently active? Set to nil if you don't want to this * CustomBannerWindow to be active at startup; thereafter use the deactivate() * and activate() methods */ isActive = true /* * deactivate a currently active banner; this removes it from the screen * and prevents writing anything further to it. Be careful to respect the * dependency order of banner windows when activating and deactivating * * The argument is optional. If it is the constant true then the currentContents * will be set to an empty string (''). If it is a string, then the currentContents * will be set to that string (ready to be displayed when the banner is reactivated). */ deactivate([args]) { removeBanner(); isActive = nil; if(args.length > 0) { local arg = args[1]; switch(dataType(arg)) { case TypeTrue: currentContents = ''; break; case TypeSString: currentContents = arg; break; } } } /* * Activate a currently inactive banner; this restores it to the screen. * The argument is optional; if present and true then activate(true) * displays the current contents of the banner window after activating it. * If the first argument is a string then the string is displayed in the banner. */ activate([args]) { if(isActive) return; isActive = true; initBannerWindow(); if(args.length() > 0 && args[1] != nil) { if(dataType(args[1]) == TypeSString) updateContents(args...); else showCurrentContents(); } } removeBanner() { /* * If I'm removed I can't be inited_ any more, and I'll need to be regarded * as not inited_ in the event of being redisplayed in the future. */ inited_ = nil; inherited; } /* * Set this flag to true to clear the contents of the window before displaying * the new contents, e.g. to display a new picture that replaces the old one. */ clearBeforeUpdate = true /* * Set this to true to have this banner size to contents each time its * contents are displayed. Note that not all interpreters support the size to * contents so you should still set an appropriate initial size, and, where * appropriate, call setSize() with the isAdvisory flag set. */ autoSize = nil /* * Update the contents of this banner window. This is the method to * call to change what a banner displays. * * The second argument is optional. If present it overrides the * setting of clearBeforeUpdate: updateContents(cont, true) will * clear the banner before the update, whereas updateContents(cont, nil) * will not, whatever the value of clearBeforeUpdate. */ updateContents(cont, [args]) { /* * Update our current contents. Note that this takes place even if * shouldDisplay is nil, so that if, for example, we are updated on * a text-only interpreter on which this banner is not displayed, * and the game is saved there and subsequently restored on a full HTML * interpreter in which we are displayed, the HTML interpreter will know * what contents it needs to display in us. */ currentContents = cont; showCurrentContents(args...); } /* Show the current contents of this banner window */ showCurrentContents([args]) { local clr; if(args.length > 0) clr = (args[1] != nil); else clr = clearBeforeUpdate; if(clr) clearWindow(); writeToBanner(currentContents); if(autoSize) sizeToContents(); } /* This is called on each CustomBannerWindow after a Restore. */ restoreBannerDisplay() { /* * It's possible a game was saved in a text-mode terp and * restored in an HTML one. In which case we need to initialize * this banner before attempting to display anything */ if(shouldDisplay && handle_ == nil) { if(!initBannerWindow()) return; } /* redisplay my contents after a restore */ showCurrentContents(); } /* * Alternatively we might have been saved in a terp that does * use this banner and restored in one that doesn't, in which * case we should remove ourselves. This is called on each BannerWindow * after a restore, but before bannerTracker.restoreDisplayState(). */ restoreRemove() { if(!shouldDisplay) removeBanner(); } /* show my initial contents on startup */ initBannerDisplay() { showCurrentContents(); } /* * We provide overrides for all the various banner manipulation methods * that game code might call, in order to make it safe to call them even * our shouldDisplay method returns nil and we don't - or shouldn't - exist. * For each of these xxxYyy methods we provide an altXxxyyy method that is * called when shouldDisplay is nil (e.g. because we're using a window to * display graphics on an interpreter that doesn't have graphics capablities). * By default these altXxxYyy methods do nothing, which in many cases will * be fine, but if you do want something else to happen you can override * the appropriate altXxxYyy method accordingly (e.g. to show a message in * the main game window instead of this banner). This should make it easier * to structure the rest of your game code without needing to worry about * what happens on interpreters which don't display your banners. */ clearWindow() { if(shouldDisplay) inherited(); else altClearWindow(); } altClearWindow() { } /* write to me, but only if I should display */ writeToBanner(txt) { if(shouldDisplay) inherited(txt); else altWriteToBanner(txt); } /* * altWriteToBanner(txt) is called when our game code tries to display * something in this banner, but our shouldDisplay method has ruled out * displaying this banner. In this case we might want to write something * to the main display instead. By default we do nothing here, but * individual instances and/or subclasses can override this method as * required. */ altWriteToBanner(txt) { } /* * We don't provide alternative methods for the setSize and sizeToContents * methods, since there would almost certainly be nothing for them to do. * We simply do nothing if shouldDisplay is nil. */ setSize(size, sizeUnits, isAdvisory) { if(shouldDisplay) inherited(size, sizeUnits, isAdvisory); } sizeToContents() { /* size our system-level window to our contents */ if(shouldDisplay) bannerSizeToContents(handle_); } captureOutput(func) { if(shouldDisplay) inherited(func); else altCaptureOutput(func); } /* Simply execute the callback without changing the output stream */ altCaptureOutput(func) { (func)(); } setOutputStream() { if(shouldDisplay) /* set my stream as the default */ return outputManager.setOutputStream(outputStream_); else return altSetOutputStream(); } /* * Our caller, or rather our caller's caller, will expect us to return * the current output stream, which means we must be sure to do this * whatever else we do. */ altSetOutputStream() { return outputManager.curOutputStream; } flushBanner() { if(shouldDisplay) inherited(); else altFlushBanner(); } altFlushBanner() { } setTextColor(fg, bg) { if(shouldDisplay) inherited(fg, bg); else altSetTextColor(fg, bg); } altSetTextColor(fg, bg) { } setScreenColor(color) { if(shouldDisplay) inherited(color); else altSetScreenColor(color); } altSetScreenColor(color) { } cursorTo(row, col) { if(shouldDisplay) inherited(row, col); else altCursorTo(row, col); } /* * If this banner isn't displaying we can't do anything directly comparable * to setting the cursot to a particular column and row in it, but we might * want to do something else instead, like inserting so many blank lines in * the main window. */ altCursorTo(row, col) { } ; /* * Initialize or reinitialize what all CustomBanners display at startup or * after an UNDO */ customBannerInit: InitObject, PostUndoObject execBeforeMe = [bannerInit] execute() { /* first ensure that all banner windows that need to exist do exist */ // forEachInstance(CustomBannerWindow, new function(win) { // if(win.shouldDisplay && win.handle_ == nil) // win.initBannerWindow(); // } ); /* then show the current contents of every active banner */ forEachInstance(CustomBannerWindow, {win: win.showCurrentContents() } ); } ; /* * Reinitialize what all the CustomBanners display on restoring. This requires * a different procedure since we can't be sure that we're being restored on * the same class of interpreter as we were saved on. */ customBannerRestore: PostRestoreObject execBeforeMe = [bannerTracker] execute() { /* * If we save in one terp, restore in the second terp, save in the second * terp, then restore in the first terp, when different rules apply about * displaying banners in the two terps, then windows removed in the second * terp could still be marked as inited_ in the restore file that comes * back to the first terp. To get round this, on restoration we ensure * that each CustomBanner's inited_ property in fact corresponds to whether * it has an active handle_, otherwise the attempt to reinitialize missing * banners might fail. */ forEachInstance(CustomBannerWindow, {win: win.inited_ = (win.handle_ != nil) } ); forEachInstance(CustomBannerWindow, {win: win.restoreBannerDisplay() } ); } ; customBannerRestoreRemove: PostRestoreObject execAfterMe = [bannerTracker] execute() { forEachInstance(CustomBannerWindow, {win: win.restoreRemove() } ); } ; /* * If we display a menu then we need to remove any active banners from the * screen before the menu displays and restore them to the screen on exiting * from the menu */ modify MenuItem display() { /* * First we store a list of all the banners that are currently * displaying */ local vec = new Vector(10); forEachInstance(CustomBannerWindow, new function(ban) { if(ban.shouldDisplay) vec.append(ban); } ); /* deactive all active banners */ foreach(local ban in vec) ban.deactivate(); try { /* carry out the inherited menu display */ inherited(); } /* * Restore all the banners in our list of banners that were previously * displayed. To ensure that they are activated in the right order * we make what may be several passes through the list. On each pass * we activate only those banners that don't depend on any inactive * banners for their activation. Each time we activate a banner, we * remove it from the list. On the next pass through the list any * banners that depended on banners we have just activated may now themselves * be activated, so we can carry on until every banner has been activated * and removed from the list. */ finally { while(vec.length()) { local bannerRemoved = nil; foreach(local ban in vec) { if(ban.bannerArgs[1] != nil && ban.bannerArgs[1].handle_ == nil) continue; if(ban.bannerArgs[3] != nil && ban.bannerArgs[3].handle_ == nil) continue; ban.activate(true); vec.removeElement(ban); bannerRemoved = true; } /* * If we didn't remove any banners on this pass through, we're * potentially in an infinite loop, so we'd better break out * of it. */ if(!bannerRemoved) break; } } } ; frobtads-1.2.3/tads3/lib/extensions/newNames.t0000644000175000001440000006500611204226057020437 0ustar realncusers/* * Copyright (c) 2000, 2007 by Michael J. Roberts. All Rights Reserved. * * NOTE: THIS FILE IS OBSOLETE! As of TADS 3.0.19, everything in this * extension is incorporated directly into the standard adv3 library, so * there's no need to add it separately to your project. You should * remove it from your project's list of source files. This file is now * included in the distribution for historical reference only. * * * TADS 3 Library Extension - New Names * * This is an experimental extension that enhances the new * detailed-naming feature that was first introduced in 3.0.10. This * feature has been turned off by default, because it's somewhat * incomplete and quirky as presently implemented. This extension is * essentially a second version of the feature that attempts to reduce * the quirks and improve the feature's performance. * * The point of the detailed-naming feature is to generate differentiated * names in object announcements: default object messages, which tell you * which object the parser has selected by default when you leave a * needed object out of a command ("UNLOCK DOOR / (with the gold key)"); * multi-object prefixes, which tell you which of the several objects in * your command the parser is working on now ("TAKE GOLD KEY AND SILVER * KEY / gold key: Taken / silver key: Taken"); and ambiguous selection * messages, which ("TAKE COIN / (the silver coin)"). By * "differentiated" names, we mean names that distinguish the object in * question from other similarly named objects that are part of the same * command or are also in scope. For example, if there are several * objects present that are all called "gold coin," the detailed naming * system might call one of them "your gold coin" and the other "the gold * coin on the floor," so that you can tell specifically which one the * parser is talking about at any given time. * * The original scheme basically accomplished this, but it had some * problems: it was a little *too* specific at times, and it wasn't as * consistent as one would like. The problem with being too specific is * that it's unnatural; in natural speech, people are really good at * using just enough specificity for the context. When science fiction * writers want to emphasize the alienness of an android or a Vulcan, * they have the character refer to everyday objects in comically * detailed, technical language; the original detailed-naming scheme had * this sort of effect. So, the main improvement this extension tries to * make is to find a better balance, to use enough specificity to * distinguish nearby objects, but not so much that the parser sounds * like an android caricature. * * The specific changes: * * 1. For multi-object announcements, the naming now only attempts to * differentiate the listed objects from *each other*, NOT from * everything else in scope. * * 2. For multi-object announcements, the basis for the differentiation * (location, ownership, etc) is determined *in advance* for all of the * objects, before the command is executed on any of the objects. In the * original implementation, the differentiation was figured for each * object on the fly, so if the command being executed affected the * object state, the objects involved could become distinguishable during * the command even though they weren't initially. From the player's * perspective, they're entering the command with respect to the state of * the world at the start of the command, so it's more consistent with * the player's expectations to determine the naming at that point in * time. This approach also results in more consistent naming overall, * since the game-state basis for the naming is the same for the whole * list. * * 3. When objects are distinguishable by their base names, the base * names are used. The original implementation favored the disambigName * even if the base names were distinguishable. This was undesirable * because the disambigName is essentially designed to globally * distinguish an object, so it tends to be very specific; when we only * need to distinguish an object from other things in scope, we usually * don't need such a specific name. */ #include #include modify ResolveInfo /* * The pre-calculated multi-object announcement text for this object. * When we iterate over the object list in a command with multiple * direct or indirect objects (TAKE THE BOOK, BELL, AND CANDLE), we * calculate the little announcement messages ("book:") for the * objects BEFORE we execute the actual commands. We then use the * pre-calculated announcements during our iteration. This ensures * consistency in the basis for choosing the names, which is * important in cases where the names include state-dependent * information for the purposes of distinguishing one object from * another. The relevant state can change over the course of * executing the command on the objects in the iteration, so if we * calculated the names on the fly we could end up with inconsistent * naming. The user thinks of the objects in terms of their state at * the start of the command, so the pre-calculation approach is not * only more internally consistent, but is also more consistent with * the user's perspective. */ multiAnnounce = nil ; modify Thing /* * Get the distinguisher to use for printing this object's name in an * action announcement (such as a multi-object, default object, or * vague-match announcement). We check the global option setting to * see if we should actually use distinguishers for this; if so, we * call getInScopeDistinguisher() to find the correct distinguisher, * otherwise we use the "null" distinguisher, which simply lists * objects by their base names. * * 'lst' is the list of other objects from which we're trying to * differentiate 'self'. The reason 'lst' is given is that it lets * us choose the simplest name for each object that usefully * distinguishes it; to do this, we need to know exactly what we're * distinguishing it from. */ replace getAnnouncementDistinguisher(lst) { return (gameMain.useDistinguishersInAnnouncements ? getBestDistinguisher(lst) : nullDistinguisher); } /* * Get a distinguisher that differentiates me from all of the other * objects in scope, if possible, or at least from some of the other * objects in scope. */ replace getInScopeDistinguisher() { /* return the best distinguisher for the objects in scope */ return getBestDistinguisher(gActor.scopeList()); } /* * Get a distinguisher that differentiates me from all of the other * objects in the given list, if possible, or from as many of the * other objects as possible. */ getBestDistinguisher(lst) { local bestDist, bestCnt; /* remove 'self' from the list */ lst -= self; /* * Try the "null" distinguisher, which distinguishes objects * simply by their base names - if that can tell us apart from * the other objects, there's no need for anything more * elaborate. So, calculate the list of indistinguishable * objects using the null distinguisher, and if it's empty, we * can just use the null distinguisher as our result. */ if (lst.subset({obj: !nullDistinguisher.canDistinguish(self, obj)}) .length() == 0) return nullDistinguisher; /* * Reduce the list to the set of objects that are basic * equivalents of mine - these are the only ones we care about * telling apart from us, since all of the other objects can be * distinguished by their disambig name. */ lst = lst.subset( {obj: !basicDistinguisher.canDistinguish(self, obj)}); /* if the basic distinguisher can tell us apart, just use it */ if (lst.length() == 0) return basicDistinguisher; /* as a last resort, fall back on the basic distinguisher */ bestDist = basicDistinguisher; bestCnt = lst.countWhich({obj: bestDist.canDistinguish(self, obj)}); /* * run through my distinguisher list looking for the * distinguisher that can tell me apart from the largest number * of my vocab equivalents */ foreach (local dist in distinguishers) { /* if this is the default, skip it */ if (dist == bestDist) continue; /* check to see how many objects 'dist' can distinguish me from */ local cnt = lst.countWhich({obj: dist.canDistinguish(self, obj)}); /* * if it can distinguish me from every other object, use this * one - it uniquely identifies me */ if (cnt == lst.length()) return dist; /* * that can't distinguish us from everything else here, but * if it's the best so far, remember it; we'll fall back on * the best that we find if we fail to find a perfect * distinguisher */ if (cnt > bestCnt) { bestDist = dist; bestCnt = cnt; } } /* * We didn't find any distinguishers that can tell me apart from * every other object, so choose the one that can tell me apart * from the most other objects. */ return bestDist; } ; modify Action /* announce a multi-action object, if appropriate */ maybeAnnounceMultiObject(info, numberInList, whichObj) { /* * announce if we have more than one object, or we're set to * announce this object in any case */ if (numberInList > 1 || (info.flags_ & AlwaysAnnounce) != 0) { /* show the current object of a multi-object action */ gTranscript.announceMultiActionObject( info.multiAnnounce, info.obj_, whichObj); /* tell the caller we made an announcement */ return true; } /* tell the caller we didn't make an announcement */ return nil; } /* * Pre-calculate the multi-object announcement text for each object. * This is important because these announcements might choose a form * for the name that distinguishes it from the other objects in the * iteration, and the basis for distinction might be state-dependent * (such as the object's current owner or location), and the relevant * state might change as we iterate over the objects. From the * user's perspective, they're referring to the objects based on the * state at the start of the command, so the user will expect to see * names based on the that state. */ cacheMultiObjectAnnouncements(lst, whichObj) { /* run through the list and cache each object's announcement */ foreach (local cur in lst) { /* calculate and cache this object's multi-object announcement */ cur.multiAnnounce = libMessages.announceMultiActionObject( cur.obj_, whichObj, self); } } /* get the list of resolved objects in the given role */ getResolvedObjList(which) { /* * the base action doesn't have any objects in any roles, so just * return nil; subclasses need to override this */ return nil; } ; modify TAction /* get the list of resolved objects in the given role */ getResolvedObjList(which) { return (which == DirectObject ? getResolvedDobjList() : inherited(which)); } /* * Execute the action. We'll run through the execution sequence * once for each resolved direct object. */ replace doActionMain() { /* * Set the direct object list as the antecedent, using the * language-specific pronoun setter. Don't set pronouns for a * nested command, because the player didn't necessarily refer to * the objects in a nested command. */ if (parentAction == nil) gActor.setPronoun(dobjList_); /* we haven't yet canceled the iteration */ iterationCanceled = nil; /* pre-calculate the multi-object announcement text for each dobj */ cacheMultiObjectAnnouncements(dobjList_, DirectObject); /* run through the sequence once for each direct object */ for (local i = 1, local len = dobjList_.length() ; i <= len && !iterationCanceled ; ++i) { /* make this object our current direct object */ dobjCur_ = dobjList_[i].obj_; dobjInfoCur_ = dobjList_[i]; /* announce the object if appropriate */ announceActionObject(dobjList_[i], len, whichMessageObject); /* run the execution sequence for the current direct object */ doActionOnce(); /* if we're top-level, count the iteration in the transcript */ if (parentAction == nil) gTranscript.newIter(); } } /* announce a default object used with this action */ announceDefaultObject(obj, whichObj, resolvedAllObjects) { local prep; local nm; /* * Get the name to display. Since we're selecting an object * automatically from everything in scope, we need to * differentiate the object from the other objects in scope. */ nm = obj.getAnnouncementDistinguisher(gActor.scopeList()) .theName(obj); /* * get any direct object preposition - this is the part inside * the "(what)" specifier parens, excluding the last word */ rexSearch('(.*+)?+', verbPhrase); prep = (rexGroup(1) == nil ? '' : rexGroup(1)[3]); /* do any verb-specific adjustment of the preposition */ if (prep != nil) prep = adjustDefaultObjectPrep(prep, obj); /* show the preposition (if any) and the object */ return (prep == '' ? nm : prep + nm); } ; modify TIAction /* get the list of resolved objects in the given role */ getResolvedObjList(which) { return (which == IndirectObject ? getResolvedIobjList() : inherited(which)); } /* * Execute the action. We'll run through the execution sequence * once for each resolved object in our direct or indirect object * list, depending on which one is the list and which one is the * singleton. */ doActionMain() { local lst; local preAnnouncedDobj; local preAnnouncedIobj; /* * Get the list of resolved objects for the multiple object. If * neither has multiple objects, it doesn't matter which is * iterated, since we'll just do the command once anyway. */ lst = (iobjList_.length() > 1 ? iobjList_ : dobjList_); /* * Set the pronoun antecedents, using the game-specific pronoun * setter. Don't set an antecedent for a nested command. */ if (parentAction == nil) { /* * Set both direct and indirect objects as potential * antecedents. Rather than trying to figure out right now * which one we might want to refer to in the future, remember * both - we'll decide which one is the logical antecedent * when we find a pronoun to resolve in a future command. */ gActor.setPronounMulti(dobjList_, iobjList_); /* * If one or the other object phrase was specified in the * input as a pronoun, keep the meaning of that pronoun the * same, overriding whatever we just did. Note that the * order we use here doesn't matter: if a given pronoun * appears in only one of the two lists, then the list where * it's not set has no effect on the pronoun, hence it * doesn't matter which comes first; if a pronoun appears in * both lists, it will have the same value in both lists, so * we'll just do the same thing twice, so, again, order * doesn't matter. */ setPronounByInput(dobjList_); setPronounByInput(iobjList_); } /* * pre-announce the non-list object if appropriate - this will * provide a common pre-announcement if we iterate through * several announcements of the main list objects */ if (lst == dobjList_) { /* pre-announce the single indirect object if needed */ preAnnouncedIobj = preAnnounceActionObject( iobjList_[1], dobjList_, IndirectObject); /* we haven't announced the direct object yet */ preAnnouncedDobj = nil; /* pre-calculate the multi-object announcements */ cacheMultiObjectAnnouncements(dobjList_, DirectObject); } else { /* pre-announce the single direct object if needed */ preAnnouncedDobj = preAnnounceActionObject( dobjList_[1], iobjList_, DirectObject); /* we haven't announced the indirect object yet */ preAnnouncedIobj = nil; /* pre-calculate the multi-object announcements */ cacheMultiObjectAnnouncements(iobjList_, IndirectObject); } /* we haven't yet canceled the iteration */ iterationCanceled = nil; /* iterate over the resolved list for the multiple object */ for (local i = 1, local len = lst.length() ; i <= len && !iterationCanceled ; ++i) { local dobjInfo; local iobjInfo; /* * make the current list item the direct or indirect object, * as appropriate */ if (lst == dobjList_) { /* the direct object is the multiple object */ dobjInfo = dobjInfoCur_ = lst[i]; iobjInfo = iobjInfoCur_ = iobjList_[1]; } else { /* the indirect object is the multiple object */ dobjInfo = dobjInfoCur_ = dobjList_[1]; iobjInfo = iobjInfoCur_ = lst[i]; } /* get the current dobj and iobj from the resolve info */ dobjCur_ = dobjInfo.obj_; iobjCur_ = iobjInfo.obj_; /* * if the action was remapped, and we need to announce * anything, announce the entire action */ if (isRemapped()) { /* * We were remapped. The entire phrasing of the new * action might have changed from what the player typed, * so it might be nonsensical to show the objects as we * usually would, as sentence fragments that are meant * to combine with what the player actually typed. So, * instead of showing the usual sentence fragments, show * the entire phrasing of the command. * * Only show the announcement if we have a reason to: we * have unclear disambiguation in one of the objects, or * one of the objects is defaulted. * * If we don't want to announce the remapped action, * still consider showing a multi-object announcement, * if we would normally need to do so. */ if (needRemappedAnnouncement(dobjInfo) || needRemappedAnnouncement(iobjInfo)) { /* show the remapped announcement */ gTranscript.announceRemappedAction(); } else { /* announce the multiple dobj if necessary */ if (!preAnnouncedDobj) maybeAnnounceMultiObject( dobjInfo, dobjList_.length(), DirectObject); /* announce the multiple iobj if necessary */ if (!preAnnouncedIobj) maybeAnnounceMultiObject( iobjInfo, iobjList_.length(), IndirectObject); } } else { /* announce the direct object if appropriate */ if (!preAnnouncedDobj) announceActionObject(dobjInfo, dobjList_.length(), DirectObject); /* announce the indirect object if appropriate */ if (!preAnnouncedIobj) announceActionObject(iobjInfo, iobjList_.length(), IndirectObject); } /* run the execution sequence for the current direct object */ doActionOnce(); /* if we're top-level, count the iteration in the transcript */ if (parentAction == nil) gTranscript.newIter(); } } /* announce a default object used with this action */ announceDefaultObject(obj, whichObj, resolvedAllObjects) { local verb; local prep; local nm; /* presume we won't have a verb or preposition */ verb = ''; prep = ''; /* * Check the full phrasing - if we're showing the direct object, * but an indirect object was supplied, use the verb's * participle form ("asking bob") in the default string, since * we must clarify that we're not tagging the default string on * to the command line. Don't include the participle form if we * don't know all the objects yet, since in this case we are in * fact tagging the default string onto the command so far, as * there's nothing else in the command to get in the way. */ if (whichObj == DirectObject && resolvedAllObjects) { /* * extract the verb's participle form (including any * complementizer phrase) */ rexSearch('/(<^lparen>+) ', verbPhrase); verb = rexGroup(1)[3] + ' '; } /* get the preposition to use, if any */ switch(whichObj) { case DirectObject: /* use the preposition in the first "(what)" phrase */ rexSearch('(.*?)*+', verbPhrase); prep = rexGroup(1)[3]; break; case IndirectObject: /* use the preposition in the second "(what)" phrase */ rexSearch('.*(.*?)*+', verbPhrase); prep = rexGroup(1)[3]; break; } /* * get the name to display - since we selected this object * automatically from among the objects in scope, we need to * differentiate it from the other objects in scope */ nm = obj.getAnnouncementDistinguisher(gActor.scopeList()) .theName(obj); /* build and return the complete phrase */ return spSuffix(verb) + spSuffix(prep) + nm; } ; modify MultiObjectAnnouncement construct(preCalcMsg, obj, whichObj, action) { /* do the inherited work */ inherited(obj, whichObj, action); /* * if we have a pre-calculated message, use it instead of the * message we just generated - this lets the caller explicitly * set the message as desired */ if (preCalcMsg != nil) messageText_ = preCalcMsg; } ; modify CommandTranscript /* * Announce one of a set of objects to a multi-object action. We'll * record this announcement for display with our report list. */ replace announceMultiActionObject(preCalcMsg, obj, whichObj) { /* save a multi-action object announcement */ addReport(new MultiObjectAnnouncement( preCalcMsg, obj, whichObj, gAction)); } ; modify libMessages /* * Announce the current object of a set of multiple objects on which * we're performing an action. This is used to tell the player * which object we're acting upon when we're iterating through a set * of objects specified in a command targeting multiple objects. */ announceMultiActionObject(obj, whichObj, action) { /* * get the display name - we only need to differentiate this * object from the other objects in the iteration */ local nm = obj.getAnnouncementDistinguisher( action.getResolvedObjList(whichObj)).name(obj); /* build the announcement */ return '<./p0>\n<.announceObj>' + nm + ':<./announceObj> <.p0>'; } /* * Announce a singleton object that we selected from a set of * ambiguous objects. This is used when we disambiguate a command * and choose an object over other objects that are also logical but * are less likely. In such cases, it's courteous to tell the * player what we chose, because it's possible that the user meant * one of the other logical objects - announcing this type of choice * helps reduce confusion by making it immediately plain to the * player when we make a choice other than what they were thinking. */ announceAmbigActionObject(obj, whichObj, action) { /* * get the display name - distinguish the object from everything * else in scope, since we chose from a set of ambiguous options */ local nm = obj.getAnnouncementDistinguisher(gActor.scopeList()) .theName(obj); /* announce the object in "assume" style, ending with a newline */ return '<.assume>' + nm + '<./assume>\n'; } ; frobtads-1.2.3/tads3/lib/extensions/combineReports.t0000644000175000001440000003635411541145535021666 0ustar realncusers#charset "us-ascii" #include #include /* * Combine Reports * * by Eric Eve * * Version 1.04 (27-Feb-11) * * This extension is a further development of Example 2 in the Technical * Manual article on Manipulating the Transcript. If this extension is * included in your game, reports of taking, dropping, or putting * (in/on/under/behind) a series of reports will be combined into a * single report listing the objects taken, dropped, or put. Reports of * actions that failed will appear in the normal way, but will be moved * to the end so as not to interrupt the combined report of successful * actions. * * With this extension installed, output like: * *. blue ball: Taken *. red pen: Taken *. gold coin: Taken *. gold coin: Taken * * Is grouped into a single sentence like: * *. You take the blue ball, the red pen, and the two gold coins. * * For PUT actions the extension also groups any implicit take action * reports in the same way. e.g. instead of: * * (first taking the red book, then taking the green ball, then taking * the pen, then taking the gold coin, then taking the gold coin) * * We get: * * (first taking the red book, the green ball, the pen and two gold coins) * * Version History: * * Version 1.03 tidies up the output from a multiple TAKE FROM command * * Version 1.02 makes use of the new standard Library class SimpleLister * * Version 1.01 makes a couple of things more customizable, and corrects * a typo in the report produced for dropping multiple objects. * */ ModuleID name = 'combineReports' byline = 'by Eric Eve' htmlByline = 'by Eric Eve' version = '1.03' listingOrder = 75 ; /* first define a useful lister */ definiteObjectLister: SimpleLister /* * Use the definite article form of the object ('the ball') unless its * one of several identical objects, in which case the indefinite form * ('a ball') is more appropriate. */ showListItem(obj, options, pov, infoTab) { say(obj.isEquivalent ? obj.aName : obj.theName); } ; /* modify CommandReport to keep a note of the direct object it refers to */ modify CommandReport construct() { inherited(); dobj_ = gDobj; } dobj_ = nil ; /* Make TAKE, DROP and PUT combine reports by using the actionReportManager */ modify TakeAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{take[s]|took}'; } ; modify TakeFromAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{take[s]|took}'; } ; modify DropAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return 'drop{s/ped}'; } ; modify PutOnAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{put[s]|put}'; } ; modify PutInAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{put[s]|put}'; } ; modify PutUnderAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{put[s]|put}'; } ; modify PutBehindAction afterActionMain() { inherited; if(parentAction == nil) actionReportManager.afterActionMain(); } vCorrect(str) { return '{put[s]|put}'; } ; /* * The actionReport Manager does most of the work. It is hopefully * sufficiently general that it could be made to work with other actions * besides TAKE, DROP and PUT IN/ON/UNDER/BEHIND. Experiment at your own * risk! */ actionReportManager: object /* * The minimum number of objects that must be in a list before we * attempt to summarize it. Normally this will be 2, but it may be * that some games will want to change it to 1, for example, so that * the same reporting style is used for singleton objects as for lists. */ minLengthToSummarize = 2 afterActionMain() { /* * If the action isn't iterating over at least * minLengthToSummarize direct objects we have nothing to do, so * we'll stop before doing any messing with the transcript */ if(gAction.dobjList_.length() < minLengthToSummarize) return; /* * First move any reports of failed attempts to the end, so they * can be fully reported after the summary of the actions that * succeeded. */ local len = gTranscript.reports_.length; for(local i = gTranscript.reports_.indexWhich({x: x.isFailure}); i != nil && i <= len; i = gTranscript.reports_.indexWhich({x: x.isFailure})) { /* * first find the MultiObjectAnnouncement relating to this * failure */ local idx1 = i; while(idx1 > 1 && !gTranscript.reports_[idx1].ofKind(MultiObjectAnnouncement)) idx1--; /* then find the next MultiObjectAnnouncement */ local idx2 = i; while(idx2 <= len && !gTranscript.reports_[idx2].ofKind(MultiObjectAnnouncement)) idx2++; /* Extract a list of all reports between these markers */ local objVec = new Vector(20).copyFrom(gTranscript.reports_, idx1, 1, idx2 - idx1); /* * Ensure that all reports about this object are marked as * failures */ objVec.forEach({x: x.isFailure = true }); /* Move this list to the end of the transcript */ gTranscript.reports_.removeRange(idx1, idx2-1); gTranscript.reports_.appendAll(objVec); /* * We don't want to check these reports again, so reduce len by * the number of reports we just moved. */ len -= objVec.length(); } /* * Give the game author the opportunity of further processing the * failure reports, if desired. We also check the summarizeFailures * flag (nil by default) so that we don't carry out any pointless * processing if we don't need it here. It also leaves open the * possibility of some future version of this extension defining * its own version of processFailures(), which game authors can * then opt in to using. */ if(summarizeFailures && gTranscript.reports_.length > len) { local failVec = processFailures(new Vector(20).copyFrom(gTranscript.reports_, len + 1, 1, gTranscript.reports_.length() - len)); gTranscript.reports_ = gTranscript.reports_.setLength(len) + failVec; } /* * Define this function separately as we'll use it more than once; * the function identifies implicit action reports relating to * taking things. */ local impFunc = {x: x.ofKind(ImplicitActionAnnouncement) && x.action_.ofKind(TakeAction) && !x.isFailure }; /* * Count how many implicit action reports there are relating to * taking things. */ local impActions = gTranscript.reports_.countWhich(impFunc); /* We only need to do anything if there's more than one. */ if(impActions > 1) { /* Note the location of the first relevant implicit action report */ local firstImp = gTranscript.reports_.indexWhich(impFunc); /* Store a copy of this report */ local rep = gTranscript.reports_[firstImp]; /* Get a list of all the implicit take reports */ local impVec = gTranscript.reports_.subset(impFunc); local impTxt = definiteObjectLister.makeSimpleList( impVec.mapAll({x: x.dobj_}).getUnique() ); /* * Change the text of this implicit action report to account * for all the objects implicitly taken */ local otherIdx = gTranscript.reports_.indexWhich ({x: x.ofKind(ImplicitActionAnnouncement) && !x.action_.ofKind(TakeAction) && !x.isFailure }); if(otherIdx) { /* * if we're going to show some other implicit reports, it's * probably best to do so first */ firstImp = otherIdx + 1; rep.messageText_ = 'taking ' + impTxt; } else rep.messageText_ = '<./p0>\n<.assume>first taking ' + impTxt + '<./assume>\n'; /* * Prevent the implicitAnnouncementGrouper from overwriting the * text we've just stored. */ rep.messageProp_ = nil; /* * Remove all the individual implicit take action * reports from the transcript. */ gTranscript.reports_ = gTranscript.reports_.subset({x: !impFunc(x) }); /* * Add back our summary implicit action report at the location * of the first individual report we removed. */ gTranscript.reports_.insertAt(firstImp, rep); /* * Remove all the CommandReports relating to taking things, * since they would otherwise show up now we've removed * most of the implicit action reports. */ gTranscript.reports_ = gTranscript.reports_.subset({ x: !((x.ofKind(DefaultCommandReport) || x.ofKind(MainCommandReport)) && x.action_.ofKind(TakeAction)) } ); } /* * After all the preliminary work we finally summarize the * successful default reports relating to the main action into a * single report. There's a complication with TAKE FROM since a * TAKE FROM action is eventually turned into a TAKE action, so we * need to handle this as a special case. */ gTranscript.summarizeAction( { x: ((x.action_ == gAction) || (gActionIs(TakeFrom) && x.action_.ofKind (TakeAction))) && !x.isFailure }, new function (vec) { /* * Construct a string reporting the objects we did take, * ensuring that each one is counted only once. */ local dobjText = definiteObjectLister.makeSimpleList ( vec.applyAll({x: x.dobj_}).getUnique()); /* Then use this to construct a description of the action */ return gAction.getActionDescWith(dobjText); }); } /* * The processFailures method is provided as a hook for game authors * who want to process the failure messages (e.g, to summarize them * some way) further than this extension does (it just moves them all * to the end). * * The vec parameter contains the vector of failure messages for * further processing. Note that this method won't be called at all * unless there are some failure messages in vec to process. * * This method also won't be called unless summarizeFailures is true * (by default, it's nil). * * This method should return a vector of CommandReports resulting from * whatever we wanted to do to the failure reports generated by the * library. By default we just return the vector that's passed to us. * * The caller will automatically append the vector returned by this * method to the vector of successul reports. */ processFailures(vec) { return vec; } /* * By default we don't run the processFailures routine at all. This * avoids pointless work when processFailures doesn't do anything, and * also allows authors to opt in to any future version of * processFailures that does do something. */ summarizeFailures = nil ; /* * This modification enables us to prevent the implicitAnnouncementGrouper * from overwriting a customized messageText_ */ modify CommandAnnouncement getMessageText([params]) { if(messageProp_) return gLibMessages.(messageProp_)(params...); else return messageText_; } ; /* * Service routines for describing an action given a string (dobjText) * containing a list of direct objects. * * The complication is that the verb getVerbPhrase uses the verb in the * present imperative form, e.g. 'TAKE'. This is wrong if the game is in the * past tense or the actor is third person singular. We therefore need to * correct for that. */ modify TAction getActionDescWith(dobjText) { return '{You/he} ' + vCheck(gAction.getVerbPhrase1(true, gAction.verbPhrase, dobjText, nil)) + '.<.p>'; } verbName() { rexMatch(pat, verbPhrase); return rexGroup(1)[3]; } vCheck(str) { local vName = verbName(); local correctedVName = vCorrect(vName); if(vName != correctedVName) str = rexReplace('%<'+vName+'%>', str, correctedVName, ReplaceAll); return str; } /* * Put the verb into the correct tense and ensure that it agrees with * its subject. Subclasses may need to override depending on the verb, * to add the appropriate verbEndingXXX property. */ vCorrect(str) { return gActor.conjugateRegularVerb(str); } pat = static new RexPattern('(.*)(?=/)') ; modify TIAction getActionDescWith(dobjText) { return '{You/he} '+ vCheck(gAction.getVerbPhrase2(true, gAction.verbPhrase, dobjText, nil, gIobj.theName)) + '.<.p>'; } ; frobtads-1.2.3/tads3/lib/extensions/pathfind.t0000644000175000001440000003241710461501706020460 0ustar realncusers/* * Copyright 2003, 2006 Michael J. Roberts * * Path Finder. This module provides a class that implements Dijkstra's * Algorithm for finding the shortest path between two nodes in a graph. * The basic path finder class makes no assumptions about the nature of * the underlying graph; subclasses must provide the concrete details * about the graph being traversed. We provide a subclass that finds * paths in a game-world location map; this can be used for things like * making an NPC find its own way to a destination. * * Everyone is granted permission to use and modify this file for any * purpose, provided that the original author is credited. */ #include /* ------------------------------------------------------------------------ */ /* * The basic path finder class. This implements Dijkstra's algorithm to * find the shortest path from one node to another in a graph. This base * implementation doesn't make any assumptions about the nature of the * underlying graph; each subclass must override one method to provide * the concrete graph implementation. */ class BasePathFinder: object /* * Find the best path from 'fromNode' to 'toNode', which are nodes in * the underlying graph. We'll return a vector consisting of graph * nodes, in order, giving the shortest path between the nodes. Note * that 'fromNode' and 'toNode' are included in the returned vector. * * If the two nodes 'fromNode' and 'toNode' aren't connected in the * graph, we'll simply return nil. */ findPath(fromNode, toNode) { local i; local len; local cur; local workingList; local doneList; local toEntry; local ret; /* start with set containing the initial node */ workingList = new Vector(32); workingList.append(new PathFinderNode(fromNode)); /* * Work through the list. For each item in the list, add all of * the items adjacent to that item to the end of the list. Keep * visiting new elements until we've visited everything in the * list once. * * We'll only add an element to the list if it's not already in * the list. This guarantees that the loop will converge (since * the number of items in the graph must be finite). */ for (i = 1 ; i <= workingList.length() ; ++i) { /* add each adjacent item to the working list */ forEachAdjacent(workingList[i].graphNode, new function(adj, dist) { /* * add the adjacent node only if it's not already in the * working list */ if (workingList.indexWhich({x: x.graphNode == adj}) == nil) workingList.append(new PathFinderNode(adj)); }); } /* * if the destination isn't in the working list, then there's no * hope of finding a path to it */ if (workingList.indexWhich({x: x.graphNode == toNode}) == nil) return nil; /* start with an empty "done" list */ doneList = new Vector(32); /* we know the distance from the starting element to itself is zero */ cur = workingList[1]; cur.bestDist = 0; cur.predNode = nil; /* keep going while we have unresolved nodes */ while (workingList.length() != 0) { local minIdx; local minDist; /* find the working list element with the shortest distance */ minDist = nil; minIdx = nil; for (i = 1, len = workingList.length() ; i <= len ; ++i) { /* if this is the best so far, remember it */ cur = workingList[i]; if (cur.bestDist != nil && (minDist == nil || cur.bestDist < minDist)) { /* this is the best so far */ minDist = cur.bestDist; minIdx = i; } } /* move the best one to the 'done' list */ cur = workingList[minIdx]; doneList.append(cur = workingList[minIdx]); workingList.removeElementAt(minIdx); /* * update the best distance for everything adjacent to the * one we just finished */ forEachAdjacent(cur.graphNode, new function(adj, dist) { local newDist; local entry; /* * Find the working list entry from the adjacent room. * If there's no working list entry, there's nothing we * need to do here, since we must already be finished * with it. */ entry = workingList.valWhich({x: x.graphNode == adj}); if (entry == nil) return; /* * calculate the new distance to the adjacent room, if * we were to take a path from the room we just finished * - this is simply the path distance to the * just-finished room plus the distance from that room * to the adjacent room (i.e., 'dist') */ newDist = cur.bestDist + dist; /* * If this is better than the best estimate for the * adjacent room so far, assume we'll use this path. * Note that if the best estimate so far is nil, it * means we haven't found any path to the adjacent node * yet, so this new distance is definitely the best so * far. */ if (entry.bestDist == nil || newDist < entry.bestDist) { /* it's the best so far - remember it */ entry.bestDist = newDist; entry.predNode = cur; } }); } /* * We've exhausted the working list, so we know the best path to * every node. Now all that's left is to generate the list of * nodes that takes us from here to there. * * The information we have in the 'done' list is in reverse * order, because it tells us the predecessor for each node. * So, first find out how long the path is by traversing the * predecessor list from the ending point to the starting point. * Note that the predecessor of the starting element is nil, so * we can simply keep going until we reach a nil predecessor. */ toEntry = doneList.valWhich({x: x.graphNode == toNode}); for (cur = toEntry, len = 0 ; cur != nil ; cur = cur.predNode, ++len) ; /* create the vector that represents the path */ ret = new Vector(len); /* * Traverse the predecessor list again, filling in the vector. * Since the predecessor list gives us the path in the reverse * of the order we want, fill in the vector from the last * element backwards, so that the vector ends up in the order we * want. In the return vector, store the nodes from the * underlying graph (rather than our internal tracking entries). */ for (cur = toEntry, i = len ; cur != nil ; cur = cur.predNode, --i) ret[i] = cur.graphNode; /* that's it - return the path */ return ret; } /* * Iterate over everything adjacent to the given node in the * underlying graph. This routine must simply invoke the given * callback function once for each graph node adjacent to the given * graph node. * * The callback takes two arguments: the adjacent node, and the * distance from 'node' to the adjacent node. Note that the distance * must be a positive number, as Dijkstra's algorithm depends on * positive distances. If the graph isn't weighted by distance, * simply use 1 for all distances. * * This method isn't implemented in the base class, since we don't * make any assumptions about the underlying graph. Subclasses must * provide concrete implementations of this routine to define the * underlying graph. */ forEachAdjacent(node, func) { /* subclasses must override */ } ; /* * A node entry for the path finder - this encapsulates the node in the * underlying graph, along with the "label" information in the algorithm. * Note that this is NOT a node in the underlying graph; rather, this is * an internal data structure that we use in the path finder to keep * track of the underlying nodes and their status in the work queue. */ class PathFinderNode: object construct(node) { /* remember the underlying node */ graphNode = node; } /* the underlying node in the graph */ graphNode = nil /* * The best estimate of the shortest distance from the starting * point. We use nil to represent infinity here. */ bestDist = nil /* the best-path predecessor for this path element */ predNode = nil ; /* * Room path finder. This is a concrete implementation of the path * finder that finds a path from one Room to another in the game-world * map. * * This implementation traverses rooms based on the actual connections in * the game map. Note that this isn't appropriate for all purposes, * since it doesn't take into account what the actor knows about the game * map. This "omniscient" implementation is suitable for situations * where the actor's knowledge isn't relevant and we just want the actual * best path between the locations. */ roomPathFinder: BasePathFinder /* find the path for a given actor from one room to another */ findPath(actor, fromLoc, toLoc) { /* remember the actor */ actor_ = actor; /* run the normal algorithm */ return inherited(fromLoc, toLoc); } /* * iterate over the nodes adjacent in the underlying graph to the * given node */ forEachAdjacent(loc, func) { /* * run through the directions, and add the apparent destination * for each one */ foreach (local dir in Direction.allDirections) { local conn; local dest; /* * if there's a connector, and it has an apparent * destination, then the apparent destination is the * adjacent node */ if ((conn = loc.getTravelConnector(dir, actor_)) != nil && (dest = getDestination(loc, dir, conn)) != nil && includeRoom(dest)) { /* * This one seems to go somewhere - process the * destination. The standard room map has no concept of * distance, so use equal weightings of 1 for all * inter-room distances. */ (func)(dest, 1); } } } /* * Get the location adjacent to the given location, for the purposes * of finding the path. By default, we return the actual * destination, but subclasses might want to use other algorithms. * For example, if a subclass's goal is to make an NPC find its own * way from one location to another, then it should use the APPARENT * destination, from the NPC's perspective, rather than the actual * destination, since we'd want to construct the path based on the * NPC's knowledge of the map. */ getDestination(loc, dir, conn) { /* return the actual destination for the connector */ return conn.getDestination(loc, actor_); } /* * For easier customization, this method allows the map that we * search to be filtered so that it only includes a particular * subset of the map. This returns true if a given room is to be * included in the search, nil if not. By default, we include all * rooms. Note that this is only called to begin with for rooms * that have apparent connections to the starting room, so there's * no need to filter out areas of the map that aren't connected at * all to the starting search location. */ includeRoom(loc) { return true; } /* the actor who's finding the path */ actor_ = nil ; /* * An NPC goal-seeking path finder. This is a subclass of the basic room * path finder that */ npcRoomPathFinder: roomPathFinder /* * Get the destination. Unlike the base class implementation, we * take into the NPC's knowledge of the map by only using connector * destinations that are APPARENT to the NPC. */ getDestination(loc, dir, conn) { /* return the apparent destination of the connector */ return conn.getApparentDestination(loc, actor_); } ; frobtads-1.2.3/tads3/lib/extensions/cquotes.t0000644000175000001440000000704210501772061020341 0ustar realncusers#charset "us-ascii" /* ** cquotes: a TADS 3 output filter for making single curly quotes ** ** To use, just add to your project. The PreinitObject at the end ** of this file automatically registers the curly quote output filter. ** ** You may use this module in your own game. You may distribute ** modified versions of this file, though I would prefer you contact ** me first at stephen@granades.com and see about having me add your ** changes to my source. ** ** Version: 0.2 (2 Feb 2004) ** Added in fixes for patIsHTMLTag and patIsFormatTag from ** Matt McGlone ** 0.1 (27 Aug 2002) ** Original release ** ** Copyright (c) 2002, 2004 by Stephen Granade. All Rights Reserved. */ #include #include // A filter to change single quotes "'" to curly ones. Comes in two // flavors: // // non-aggressive: will only change the single quotes that are part // of English contractions (like "won't") into ‘ // aggressive: will change every single left quote it can find. Any // single quote that is preceeded by a letter or punctuation is // turned into ‘. // // To choose between them, set cquoteOutputFilter.aggressive to // true (for aggressive changing) or nil (for non-aggressive matching). // // No translation is done to single quotes which a) fall within HTML // tags (i.e. ), or b) fall within // formatting tags (i.e. {It's obj}) cquoteOutputFilter: OutputFilter aggressive = nil // Patterns for our searches patIsHTMLTag = static new RexPattern('<^rangle>+<^rangle>*') patIsFormatTag = static new RexPattern('{[^}]+[^}]*}') patAggressive = static new RexPattern('()') patIsCont1Tag = static new RexPattern('()(s|m|d|ve|re|ll)') patIsCont2Tag = static new RexPattern('()nt') patIsPossTag = static new RexPattern('()s') filterText(ostr, val) { local ret; // Look for an HTML tag. We only need to find the first one, // because we'll be recursing through the string ret = rexSearch(patIsHTMLTag, val); if (ret == nil) { // Look for a formatting tag ret = rexSearch(patIsFormatTag, val); } // If we got a match either from the HTML or the formatting // tag, ignore that match recursively; that is, run the output // filter on the text before and after the match. This is // assuming that the whole start wasn't prefixed by a backslash // (since e.g. "\ #include /* * showTranscript * * by Eric Eve * * version 1.0 * * This extension provides a utility routine, showTranscript, to help game * authors inspect the current contents of the transcript (as an alternative * to examining it in the Workbench debugger). It is only intended for * help in writing and debugging code, and will not be included in code * compiled for release. * * The typical use of showTranscript() would be to call it from an * afterActionMain() routine that is meant to be combining reports or * otherwise manipulating the transcript. * * Note that to use this extension your project MUST also include reflect.t */ #ifdef __DEBUG showTranscript() { local wasActive = gTranscript.isActive; local len = gTranscript.reports_.length(); gTranscript.deactivate(); local lf = new RexPattern(''); for(local i=1; i<=len; i++) { local cur = gTranscript.reports_[i]; "[<>]: [<>]: action_ = <>; messageText_ = '<>'; dobj_ = <> \n"; } if(wasActive) gTranscript.activate(); } showClassName(obj) { if(obj == nil) return 'nil'; return reflectionServices.valToSymbol(obj.getSuperclassList[1]); } #endiffrobtads-1.2.3/tads3/lib/extensions/CustomStatus.t0000644000175000001440000001360711704654365021354 0ustar realncusers#charset "us-ascii" #include #include /* * CustomStatus - by Mark Engelberg (mark.engelberg@gmail.com) * * CustomStatus is a mix-in class for situations where you want to have * more control over the various status messages that are printed by * default when an object is examined. * * By mixing in this class, default reporting of status is disabled, and * it becomes an "opt-in" system. This leaves you free to describe the * status of the object as part of its prose description. * * For convenience, the class contains several status reporting functions * that you can use in your description if a default message will do. * Even if you find yourself using default messages, you may prefer doing * it this way, because you then have full control over where in your * description the status message occurs, and you have the ability to * choose between a few variations (see below). */ CustomStatus : Thing // Most status reports occur in examineStatus. So the first step is to // override examineStatus. This gets rid of open and locked status statements // and automatic listing of contents when you examine an object. // It doesn't do anything about the way contents are described when they are // inside of other objects, or in an inventory. examineStatus{} // Next, we provide various messages you can mix in to your descriptions to // report simple status messages. It's up to you to make sure you're // reporting something meaningful for that kind of object. // For example, reportLockedStatus only makes sense on something lockable. // Reporters for openable objects. // "It's open. " reportOpenStatus { say ('\^'+openStatus+'. '); } // "It's currently open. " reportCurrentlyOpenStatus { say(isOpen ? gLibMessages.currentlyOpen : gLibMessages.currentlyClosed); } // "The object is open. " reportDobjOpenStatus { "{The dobj/he} is <>. "; } // Reporters for lockable objects. // Default reporting of lock status respects the lockStatusObvious and // lockStatusReportable flags. reportLockedStatus { if (lockStatusObvious && lockStatusReportable) say ( '\^' + itIsContraction + ' ' + lockedDesc + '. '); } reportCurrentlyLockedStatus { if (lockStatusObvious && lockStatusReportable) say(isLocked ? gLibMessages.currentlyLocked : gLibMessages.currentlyUnlocked); } reportDobjLockedStatus { if (lockStatusObvious && lockStatusReportable) "{The dobj/he} is <>. "; } // Reporters for containers /* * If you want to report the contents of something (a container, an * actor, etc.), you can use this reportContents, which is usually * just a synonym for examineListContents. */ reportContents { if (ofKind(RoomPart)) // Listing the contents of a room part is handled a // bit differently examinePartContents(&descContentsLister); else examineListContents; } /* * If you want specialized lister behavior when reporting the contents, you * can call this reporter with a specific lister as an argument. */ reportContentsWith(lister) { examineListContentsWith(lister); } /* * The openable class is a major thorn in my side for the purposes of * this opt-in strategy. The problem is that the openable class * incorporates the open status as part of its container lister. It * does this by setting the descContentsLister to * openableDescContentsLister. We really don't want the status to be * displayed unless we specify, so we have to set the * descContentsLister back to the basic thingDescContentsLister in * the case that this is an openable object. The flaw with this * approach is that if you have a class between CustomStatus and * Openable in your class list which defines a different * descContentsLister, this will break it. Unfortunately, I can't * think of a better strategy right now. */ descContentsLister { if (ofKind(Openable)) return(thingDescContentsLister); else return inherited; } // Often, in containers, it is useful to report a container's open status and // its contents in the same sentence. reportOpenStatusAndContents { examineListContentsWith(openableDescContentsLister); } // Reporters for actors // If you have a specific lister you'd like to use, you can just // use examineListContentsWith, but we give it a reportContentsWith name // for consistency with the other reporters. // Now let's turn our attention to actors that use this mix-in // The default examineStatus provides a posture description, and a state // description. Let's provide some default reporters. reportPosture { postureDesc; } reportState { curState.stateDesc; } // Combine all the actor status pieces into one default report, if desired. reportActorStatus { reportPosture; reportState; reportContents; } // Reporters for attachable objects reportAttachments { local tab; /* get the actor's visual sense table */ tab = gActor.visibleInfoTable(); /* add our list of attachments */ attachmentLister.showList(gActor, self, attachedObjects, 0, 0, tab, nil); /* add our list of major attachments */ majorAttachmentLister.showList(gActor, self, attachedObjects, 0, 0, tab, nil); } ; frobtads-1.2.3/tads3/lib/extensions/OpenState.t0000644000175000001440000001012711667522342020567 0ustar realncusers/* Copyright (c) 2006 by Michael J. Roberts. All Rights Reserved. */ /* * It's often handy for the player to be able to refer to objects by * their current open/closed states ("go through the open door" or "look * in the closed jar"). This is a small extension that adds "open" and * "closed" vocabulary automatically to Openable objects, according to * their current states. It also lets the disambiguation system * distinguish objects according to their open/closed state, so that the * parser can ask for help accordingly ("Which do you mean, the open jar, * or one of the closed jars?"). * * The author hereby grants permission to anyone to use this software for * any purpose and without fee, under the condition that anyone using * this software assumes all liability for his or her use of the * software. NO WARRANTY: The author specifically disclaims all * warranties with respect to this software, including all implied * warranties of merchantability and fitness. */ #include #include /* * State objects for 'open' and 'closed' states */ openState: ThingState stateTokens = ['open'] ; closedState: ThingState stateTokens = ['closed'] ; /* * A disambiguation distinguisher for open/closed state. This can tell * apart two objects if they're both openable, and their open/closed * states differ. * * This allows the parser to ask disambiguation questions based on the * open/closed state when it's the only thing that distinguishes the * relevant objects. "Which do you mean, the open jar, or one of the * closed jars?" */ openClosedDistinguisher: Distinguisher canDistinguish(a, b) { return a.ofKind(Openable) && b.ofKind(Openable) && a.isOpen != b.isOpen; } name(obj) { return obj.nameOpenClosed; } aName(obj) { return obj.aNameOpenClosed; } theName(obj) { return obj.theNameOpenClosed; } countName(obj, cnt) { return obj.pluralNameOpenClosed; } ; /* * In the basic openable class, add handling for state-dependent 'open' * and 'closed' adjectives, and use the current state as a basis for * disambiguation (via a Distinguisher). * * Note that we take advantage of the ThingState mechanism to help with * the name matching, but we don't use the mechanism fully. In * particular, we never report openState or closedState in getState() or * allStates(). The reason we don't use the full mechanism is that adv3 * currently only allows an object to have a single active state, and we * don't want to rule out other states by using up the whole single * active state slot for the open/closed status. Hopefully this * restriction will be lifted in the future, in which case we'll be able * to simplify this by simply adding openState or closedState to the * current state list, and then fall back on the standard * matchNameCommon() handling. */ modify BasicOpenable /* add the adjectives */ adjective = 'open' 'closed' /* ...but only accept the one that applies to our current state */ matchNameCommon(origTokens, adjustedTokens) { /* * check with our appropriate state object - if that fails, the * whole match fails */ local st = (isOpen ? openState : closedState); if (st.matchName(self, origTokens, adjustedTokens, [openState, closedState]) == nil) return nil; /* inherit the base handling */ return inherited(origTokens, adjustedTokens); } /* use the open/closed state as a basis for disambiguation */ distinguishers = (nilToList(inherited) + openClosedDistinguisher) /* open/closed names */ nameOpenClosed = ((isOpen ? 'open ' : 'closed ') + name) theNameOpenClosed = ((isOpen ? 'the open ' : 'the closed ') + name) pluralNameOpenClosed = ((isOpen ? 'open ' : 'closed ') + name) aNameOpenClosed() { if (isPlural || isMassNoun) return (isOpen ? 'open ' : 'closed ') + name; else return (isOpen ? 'an open ' : 'a closed ') + name; } ; frobtads-1.2.3/tads3/lib/extensions/custmsg.t0000644000175000001440000002433211173427665020361 0ustar realncusers#charset "us-ascii" #include #include /* * custmsg.t *. by Eric Eve (eric.eve@hmc.ox.ac.uk) *. Version 1.1 - 28-Mar-08 * * A small extension that makes it easier to customize or suppress certain * library-generated pieces of descriptive text. * * (1) If postureReportable is set to nil on an ActorState, the * library-generated postureDesc() (e.g. "Mavis is sitting on the wooden * chair. ") is no longer incorporated into the description in response to * an EXAMINE command. * * (2) Openable now has an openStatusReportable property (analogous to * lockStatusReportable on Lockable) that makes it easy to suppress the * "it's open" and "it's closed" messages that get tagged onto the end of * the descriptions of Openable objects. * * (3) It's now easier to customize or suppress the "(providing light)" * and "(lit)" messages that appear after the names of LightSources in * inventory listings and the like; just override the providingLightMsg on * the object in question. The mechanism can be extended to use other * properties on other classes of objects using author-defined ThingStates. * * (4) The new Thing property specialContentsListedInExamine can be set * to nil to suppress the listing of any contents that uses a specialDesc. */ /* Version 1.1 adds the openStatusReportable property to ContainerDoor */ /* * The modifications to Actor and ActorState allow authors to opt in to * including a postureDesc (e.g, "Mavis is sitting on the wooden chair. ") * when a stateDesc is already provided, instead of having to opt out. */ modify Actor replace examineStatus() { /* * If I'm an NPC, show where I'm sitting/standing/etc. (If I'm * the PC, we don't usually want to show this explicitly to avoid * redundancy. The player is usually sufficiently aware of the * PC's posture by virtue of being in control of the actor, and * the information also tends to show up often enough in other * places, such as on the status line and in the room * description.) * * Also, we only show this if the curState's postureReportable * property is true. This allows redundant posture descriptions * to be suppressed if the current ActorState's stateDesc already * mentions them. */ if (!isPlayerChar() && curState.postureReportable) postureDesc; /* show the status from our state object */ curState.stateDesc; /* inherit the default handling to show our contents */ inherited(); } ; modify ActorState /* * By default the actor's postureDesc() is displayed between its desc * and the stateDesc on the current ActorState. If the stateDesc * already mentions the actor's posture this may be redundant; changing * postureReportable to nil suppresses the display of the * postureDesc(). */ postureReportable = true ; //------------------------------------------------------------------------------ /* * The following modification to ThingState is designed to make it easier to * customize state-dependent messages such as '(providing light)' or * '(being worn)'. The mechanism should be readily extensible to * author-defined ThingStates. */ modify LightSource /* * The message that's appended to our name in inventory listings and the * like when we're lit; the default in the English language library is * '(providing light)'. To suppress the message altogether, set this * property to nil. To replace it with another message, omit the * parentheses, e.g. on a flashlight you might define: * * providingLightMsg = 'currently switched on' */ providingLightMsg = lightSourceStateOn.listName_ notProvidingLightMsg = lightSourceStateOff.listName_ ; modify Matchstick /* * The message that's appended to our name in inventory listings and the * like when we're lit; the default in the English language library is * '(lit)'. To suppress the message altogether, set this * property to nil. To replace it with another message, omit the * parentheses, e.g. : * * providingLightMsg = 'now ablaze' */ providingLightMsg = matchStateLit.listName_ notProvidingLightMsg = matchStateUnlit.listName_ ; modify Wearable /* * The messages that's appended to our name in listing when we're worn * or unworn. By default this will be '(worn)' or nothing; but the * wornMsg won't be used if we're appearing in a list of items * introduced as being worn (e.g. "You are wearing..."). */ wornMsg = wornState.listName_ unwornMsg = unwornState.listName_ ; modify ThingState listName(lst) { /* * if we have a msgProp and there's one item in our list, use the * msgProp of that item to provide our state-dependent text. */ if(msgProp != nil && lst.length() == 1) return (lst[1]).(msgProp); /* Otherwise, use our listName_ (as in the standard library) */ return listName_; } /* * The property on the object being described that will provide the * text for any state-dependent extra description, such as 'providing * light'. If this is defined, it should be set to a property pointer, * e.g. &providingLightMsg. If it is left at nil ThingState will behave * just as it does in the standard library. */ msgProp = nil ; modify lightSourceStateOn msgProp = &providingLightMsg ; modify matchStateLit msgProp = &providingLightMsg ; modify wornState msgProp = &wornMsg ; /* * Although the following three ThingStates don't display any state-related * text in the standard library, equivalent modifications are added here * to make it easy to add object-specific state information (e.g. * 'currently switched off' on a flashlight) if so desired). This does not * affect the standard library behaviour unless a game author chooses to * override the notProvidingLightMsg_ property anywhere. These two * ThingStates can also be given a listName_ property if a globally * applicable message is required (e.g. 'unlit' for every unlit matchstick) */ modify lightSourceStateOff msgProp = ¬ProvidingLightMsg ; modify matchStateUnlit msgProp = ¬ProvidingLightMsg ; modify unwornState msgProp = &unwornMsg ; /* * Note that the same coding pattern can be used if desired on custom * ThingStates. E.g. * *. class MyCustomClass: Thing *. allStates = [sillyState, sensibleState] *. getState = sillyState *. sillyMsg = sillyState.listName_ *. sensibleMsg = sensibleState.listName_ *. ; *. *. sillyState: ThingState 'silly' *. stateTokens = ['silly'] *. msgProp = &sillyMsg *. ; *. *. sensibleState: ThingState 'sensible' *. stateTokens = ['sensible'] *. msgProp = &sensibleMsg *. ; * * Note, however, that there is no need to define the msgProps if the * ThingStates are being created for a single object, or if all the * objects they will apply to are to use the same state-specific messages; * in these cases ThingStates may be defined and used just as they are in * the standard library. For example, if we never wanted to vary the * 'silly' and 'sensible' messages on different objects, there would be no * need (and not point) to define sillyMsg and sensibleMsg on * MyCustomClass, and no need to define msgProp on sillySrate and * sensibleState. * */ //------------------------------------------------------------------------------ modify Openable /* * openStatusReportable works on analogy with lockStatusReportable on * Lockable. It allows us to suppress the 'it's open' and 'it's closed' * messages that follow the description of an Openable object (by * setting this property to nil). By default this property is true, * giving the standard library behaviour. * * It may also be useful to set this property to the value of an * expression, e.g.: * * openStatusReportable = (isOpen) * * would result in the 'it's open' message being appended when we're * open, but nothing being appended when we're closed. */ openStatusReportable = (canInherit() ? inherited() : true) ; modify openableContentsLister showListEmpty(pov, parent) { if(parent.openStatusReportable) "\^<>. "; } showListPrefixWide(itemCount, pov, parent) { if(parent.openStatusReportable) "\^<>, and"; else "\^<>"; " contain<> "; } ; /* * Make openStatusReportable work on ContainerDoor in the same way as it * works on Openable. */ modify ContainerDoor openStatusReportable = (canInherit() ? inherited() : true) /* * If we set OpenStatusReportable is set to nil, we may want to * incorporate the open/closed state into our owm description. We * borrow openDesc from Openable to facilitate this. */ openDesc = (delegated Openable) replace examineStatus() { /* add our open status, if it is reportable */ if(openStatusReportable) say(isOpen ? gLibMessages.currentlyOpen : gLibMessages.currentlyClosed); /* add the base class behavior */ inherited(); } ; //------------------------------------------------------------------------------ modify Thing examineSpecialContents() { if(specialContentsListedInExamine) inherited; } /* * specialContentsListedInExamine works analogously to * contentsListedInExamine, except that if it is nil it is the listing * of this object's special contents (i.e. any contained object that * uses a specialDesc or initSpecialDesc, including actors) that will * be suppressed. */ specialContentsListedInExamine = true ; frobtads-1.2.3/tads3/lib/extensions/subtime.t0000644000175000001440000012632410461501706020334 0ustar realncusers/* * Copyright 2003, 2006 Michael J. Roberts * * "Subjective Time" module. This implements a form of in-game * time-keeping that attempts to mimic the player's subjective * experience of time passing in the scenario while still allowing for * occasional, reasonably precise time readings, such as from a * wristwatch in the game world. * * It's always been difficult to handle the passage of time in * interactive fiction. It's such a vexing problem that most IF avoids * the problem entirely by setting the game action in a world that * forgot time, where there are no working clocks and where night never * falls. Like so many of the hard parts of interactive fiction, the * difficulty here comes from the uneasy union of simulation and * narrative that's at the heart of all IF. * * In ordinary static narratives, the passage of time can swing between * extremes: a scene that only takes moments of real-time within the * story, such as an intense action scene or an extended inner * monologue, can go on for many pages; and elsewhere in the same story, * days and years can pass in the space of a sentence or two. Narrative * time has no relationship to the length of a passage of text; * narrative time is related only to the density of story-significant * action. * * It might seem at first glance that this break between in-story time * and chapter length is a special privilege of the staticness of a * book: the text just sits there on a page, unchanging in time, so of * course there's no real-time anchor for the story's internal clock. * But that can't be it, because we see exactly the same narrative * manipulation of time in film, which is a fundamentally time-based * medium. Films don't present their action in real-time any more than * books do. What's more, film follows the same rules as text-based * fiction in compressing and expanding story-time: many minutes of film * can spool by as the last seven seconds tick off a bomb's count-down * timer, while the seasons can change in the course of a five-second * dissolve. Even Fox's "24," a television series whose entire conceit * is that the action is presented in real-time, can be seen on closer * inspection to hew to its supposed real-time anchors only every ten * minutes or so, where the ads are inserted; in between the ads, the * normal bag of tricks is liberally employed to compress and expand * time. * * In simulations, we have a different situation, because the pacing of * the story is under the indirect control of the player. (I call the * control indirect because it's not presented to the player as a simple * control knob with settings for "slower" and "faster." The player can * directly control how quickly she enters commands, but this has only a * very minor effect on the pace of the story; the major component of * the story's pacing is the player's rate of absorbing information from * the story and synthesizing solutions to its obstacles. In most * cases, this is the gating factor: the player is motivated, by a * desire to win the game, to go as fast as possible, but is unable to * go any faster because of the need to peform time-consuming mental * computation. Thus the player's control is indirect; there's the * illusion of a control knob for "faster" and "slower," but in practice * the knob's setting is forced to a fixed value, determined by the * player's ability to solve puzzles: no player ever wants to choose a * "slower" setting than they have to, and the "faster" settings are * unavailable because of the gating mental computation factor.) * * But even so, the player in a simulation does have complete control * over the specific actions that the player character performs. The * player makes the character walk around, examine things, try a few * random verbs on an object, walk around some more, and so on. All of * these things clearly ought to take time in the simulation. The * question is: how much time? * * The most obvious approach is to tie the passage of game-time to the * actions the player character performs, by making each turn take some * amount of time. At the simplest, each turn could take a fixed amount * of time, say 1 minute. A more sophisticated approach might be for * some kinds of actions to take more time than others; walking to a new * room might take 2 minutes, say, while examining a wall might take 30 * seconds. Some early IF games took this approach. Infocom's * Planetfall, for example, has a cycle of day and night that's tied to * the number of turns taken by the player. * * Unfortunately, such a direct mapping of turns to game-time hasn't * proved to be very satisfactory in practice. This approach doesn't * seem to make the treatment of time more realistic -- in fact, it's * just the opposite. For example, in Planetfall, as objective * game-time passes, it's necessary to eat and sleep; the game has a * limited supply of food, so if you spend too much time exploring, you * can lock yourself out of victory simply by exhausting all available * food and starving to death. The workaround, of course, is to restart * the game after you've explored enough to know your way around, at * which point you can cruise through the parts you've been through * before. The end result is that we get two opposite but equally * unrealistic treatments of time: on the first pass, far too much * objective time passes as we wander around exploring, given how little * we accomplish; and on the second pass, far too little objective time * elapses, given how rapidly we progress through the story. * * One problem would seem to be that verbs are far too coarse a means to * assign times to actions: picking up five small items off a table * might take almost no time at all, while picking up the table itself * could require a minute or two; walking to the next room might take * ten seconds, while walking across an airport terminal takes ten * minutes. An author could in principle assign timings to actions at * the level of individual verb-object combinations, but this would * obviously be unmanageable in anything but the tiniest game. * * A second problem is that it might be better to see the player's * actual sequence of command input as somewhat distinct from the player * character's subjective storyline. In the turns-equals-time approach, * we're essentially asserting a literal equivalence between the * player's commands and the player character's story. In typical IF * game play, the player spends quite a lot of time exploring, * examining, and experimenting. If we were to imagine ourselves as an * observer inside the game world, what we'd see would be highly * unnatural and unrealistic: no one actually behaves like an IF player * character. If we wanted to achieve more realism, we might consider * that the medium imposes a certain amount of this exploration activity * artificially, as a means for the player to gather information that * would, in reality, be immediately and passively accessible to the * player character - just by being there, the player character would * instantly absorb a lot of information that the player can only learn * by examination and experimentation. Therefore, we could say that, * realistically, a lot of the exploration activity that a player * performs isn't really part of the story, but is just an artifact of * the medium, and thus shouldn't count against the story world's * internal clock. * * One ray of hope in all of this is that the human sense of time is * neither precise nor objective. People do have an innate sense of the * passage of time, but it doesn't work anything like the * turns-equals-time approach would have it. People don't internally * track time by counting up the actions they perform. In fact, people * generally don't even have a very precise intuitive sense for how long * a particular action takes; in most cases, people are actually pretty * bad at estimating the time it will take to perform ordinary tasks. * (Unless you're asking about areas where they have a lot of hands-on * experience actually timing things on a clock, such as you might find * in certain professions. And a lot of people aren't even good at that * - just try getting a decent schedule estimate out of a programmer, * for example.) * * The human sense of time is not, then, an objective accounting of * recent activity. Rather, it's a highly subjective sense, influenced * by things like emotion and focus of attention. A couple of familiar * examples illustrate how a person's subjective experience of time can * wildly depart from time's objective passage. First, think about how * time seems to stretch out when you have nothing to do but wait for * something, like when you're stuck in an airport waiting for a late * flight. Second, think about how time flies by when you're doing * something that intensely focuses your attention, like when you're * playing a really good computer game or you're deeply engaged in a * favorite hobby: hours can seem to disappear into a black hole. Being * bored tends to expand time, while being intensely occupied tends to * compress it. There are other factors that affect the subjective time * sense, but occupation of attention seems to be one of the most * important. * * So where does this lead us? If it's not clear which "turns" are part * of the story and which are merely artifacts of the medium, the turn * counter can't be a reliable source of game-time. But if the human * sense of time is inherently subjective, then maybe we're better off * tying the passage of time to something other than the raw turn * counter anyway. * * The key to our solution is to treat the turn counter as correlated to * the player's subjective time, rather than the story's objective time. * In other words, rather than saying that a LOOK command actually takes * 30 seconds in the game world, we say that a LOOK command might *feel* * like it takes some amount of time to the player, but that the actual * time it takes depends on what it accomplishes in the story. How much * time really passes for a single LOOK command? It all depends on how * many LOOK commands it takes to accomplish something. * * So, if the player spends long periods of time exploring and frobbing * around with objects, but not making any progress, we've had a lot of * subjective time pass (many turns), but very little story time (few * story-significant events). This is exactly what people are used to * in real life: when you're bored and not accomplishing anything, time * seems to drag. If the player starts knocking down puzzles left and * right, making great progress through the story, we suddenly have a * feeling of little subjective time passing (few turns) while story * time is flying past (many story-significant events). Again, just * what we're used to in real life when deeply engaged in some activity. * * This basic approach isn't new. Gareth Rees's Christminster includes * plot elements that occur at particular clock-times within the story, * and a working clock-tower to let the player character tell the time. * Time advances in the game strictly according to the player's progress * through the plot (i.e., solving puzzles). At key plot events, the * clock advances; at all other times, it just stays fixed in place. * The game works around the "precision problem" (which we'll come to * shortly) with the somewhat obvious contrivance of omitting the * minute-hand from the clock, so the time can only be read to the * nearest hour. Michael Gentry's Anchorhead also ties events to the * story's clock-time, and provides a story clock of sorts that advances * by the player's progress in solving puzzles. Anchorhead handles the * precision problem by using an even coarser clock than Christminster, * specifically the position of the sun in the sky, which I think can * only be read to a precision of "day" or "night." The * precision-avoiding contrivance here is much less obvious than in * Christminster, and it especially helps that the setting and * atmosphere of the game practically demand a continuous dark overcast. * * But what if we wanted a normal clock in the game - one with a minute * hand? This is where the precision problem comes in. The problem * with subjective time is that, in the real world, a tool-using human * in possession of a wristwatch can observe the objective time whenever * she wants. In games like Anchorhead and Christminster, the clock * only advances at key plot points, so if we permitted the player * character a timepiece with a minute hand, we'd get unrealistic * exchanges like this: * * >x watch *. It's 3:05pm. * * >east. get bucket. west. fill bucket with water. drink water. east. *. ... done ... * * >etc, etc, etc... *. ... done ... * * >x watch *. It's 3:05pm. * * That's why Christminster doesn't have a minute hand, and why * Anchorhead doesn't have clocks at all. When the time is only * reported vaguely, the player won't notice that the clock is frozen * between plot events: it's not a problem when we're told that it's * still "some time after three" for dozens of turns in a row. * * This is where this implementation tries to add something new. We try * to achieve a compromise between an exclusively narrative advancement * of time, and an exclusively turn-based clock. The goal is to have * the story drive the actual advancement of the clock, while still * giving plausible time readings whenever the player wants them. * * The compromise hinges on the way people use clocks in the real world: * a person will typically glance at the clock occasionally, but not * watch it continuously. Our approach also depends upon the * observation that people are generally bad at estimating how long a * particular activity takes. If people are bad at judging the time for * one activity, then they're even worse at judging it for a series of * activities. This is an advantage for us, because it means that the * "error bars" around a player's estimate of how long something ought * to be taking are inherently quite large, which in turn means that we * have a large range of plausible results - as long as we don't say * it's 3:05pm every time we're asked. * * Here's the scheme. We start by having the game assign the times of * important events. The game doesn't have to tell us in advance about * everything; it only has to tell us the starting time, and the game * clock time of the next important event in the plot. For example, the * game could tell us that it's now noon, and the next important plot * point will be when the player character manages to get into the * castle, which happens at 6pm, just as the sun is starting to set. * Note that the story asserts that this event happens at 6pm; it * doesn't matter how many turns it takes the player to solve the * puzzles and get into the castle, since *in the story*, we always get * in at 6pm. * * If the player looks at a clock at the start of the game, they'll see * that it's noon. The clock module then starts keeping track of the * number of turns the player takes. The next time the player looks at * a clock, we'll check to see how many turns it's been since the last * look at the clock. (We'll also consider plot points where the story * actually sets the clock to be the same as the player looking at the * clock, since these have the same effect of making us commit to a * particular time.) We'll scale this linearly to a number of minutes * passing. Then, we'll crank down this linear scaling factor according * to how close we're getting to the next event time - this ensures that * we'll leave ourselves room to keep the clock advancing without * bumping up against the next event time. So, we have a sort of Zeno's * paradox factor: the closer we get to the next event time, the slower * we'll approach it. * * The point is to create a plausible illusion of precision. A player * who checks infrequently, as should be typical, should see a plausible * series of intermediate times between major plot points. * * One final note: if you want to display a clock in the status line, or * show the current time with every prompt, this is the wrong module to * use. A key assumption of the scheme implemented here is that the * time will be checked only occasionally, when it occurs to the player * to look. If the game is constantly checking the time * programmatically, it'll defeat the whole purpose, since we won't be * able to exploit the player's presumed uncertainty about exactly how * much time should have elapsed between checks. */ #include /* ------------------------------------------------------------------------ */ /* * The Clock Manager is the object that keeps track of the game-world * wall-clock time. Timepieces in the game can consult this for the * current official time. * * To use the clock manager's services, you should decide on a set of * plot events in your game that occur at particular times. This * requires you to "linearize" your game's plot to the extent that these * events must occur in a particular order and at particular times within * the game world. This might sound at odds with the idea that the * player is in control, but most IF stories are actually structured this * way anyway, with at least a few plot points that always happen in a * particular order, no matter what the player does. Puzzles tend to * introduce a locally linear structure by their nature, in that a player * can't reach a plot point that depends on the solution to a puzzle * until solving that puzzle. * * Once you've decided on your important plot points, create a ClockEvent * object to represent each one. Each ClockEvent object specifies the * game-clock time at which the event occurs. Once you've defined these * objects, add code to your game to call the eventReached() method of * the appropriate ClockEvent object when each plot event occurs. This * anchors the game-world clock to the player's progress through the * special set of plot points. * * Note that you must always create a ClockEvent to represent the very * beginning of the game - this establishes the time at the moment the * game opens. At start-up, we'll initialize the game clock to the time * of the earliest event we find, so it's never necessary to call * eventReached() on this special first event - its function is to tell * the clock manager the game's initial wall-clock time. * * We keep track of the "day" in game time, but we don't have a calendar * feature. We simply keep track of the day relative to the moment the * game begins; it's up to the game to make this into a calendar date, if * desired. For example, if the game is designed to begin at noon on * March 21, 1882, the game could figure out the current calendar day by * adding the current day to March 20 (since the first day is day 1). * The game would have to figure out when month and year boundaries are * crossed, of course, to show the resulting calendar date. If you don't * care about tying your story to a particular calendar date, but you do * want to nail it down to particular days of the week, this is a lot * easier, since you can use "mod-7" arithmetic to compute the weekday - * just use "(day % 7) + 1" as an index into a seven-element list of * weekday names. * * A recommendation on usage: if you're using the clock for scheduling * appointments that the player character is responsible for keeping, * it's a good idea to structure the game so that there's a plot event a * little before an appointment. For example, if you've told the player * that they have a noon lunch planned with a friend, you should set up * the plot sequence so that there's some important event that happens at * 11:45am, or thereabouts. The event doesn't need to be related to the * lunch in any visible way, as far as the player is concerned - the * point of the event is to gate the lunch appointment. The event should * usually be something of the nature of solving a puzzle. This way, the * player works on the puzzle, since it's not yet time for the lunch * appointment; suddenly, when they solve the puzzle, the game can let * them know that it's almost lunch time, and that they should go meet * their friend. This plays well into the whole subjective-time design * of the clock manager, because the player will presumably have her * attention occupied solving the puzzle, and so it will seem perfectly * natural for the game clock to have changed substantially when she * comes up for air after having been working on the puzzle. You can * then set up another plot event to represent the player character's * arrival at the restaurant, which might occur in the story at 11:55am * (or maybe 12:20pm, if the PC is someone who tends to annoy his friends * by always running late). * * When the player character isn't supposed to be waiting for a * particular plot event, there's no need to gate it like this. If you * have a plot event that coincides with night falling, * * Note that you can use the clock manager even if your game has no * pre-defined plot points. Just create one event to represent the start * of the game, to give the clock its initial setting. The clock manager * will use the same subjective time scheme it would if there were more * plot points, but with no ending boundary to worry about. The clock * manager probably isn't as interesting in a game without significant * time-anchored plot events, since the passage of time doesn't have any * real relation to the plot in such a game;s but it might be desirable * to have a working clock anyway, if only for the added sense of detail. */ clockManager: PreinitObject /* * Get the current game-clock time. This returns a list in the same * format as ClockEvent.eventTime: [day,hour,minute]. * * Remember that our time-keeping scheme is a sort of "Schrodinger's * clock" [see footnote 1]. Between time checks, the game time * clock is in a vague, fuzzy state, drifting along at an * indeterminate pace from the most recent check. When this method * is called, though, the clock manager is forced to commit to a * particular time, because we have to give a specific answer to the * question we're being asked ("what time is it?"). As in quantum * mechanics, then, the act of observation affects the quantity * being observed. Therefore, you should avoid calling this routine * unnecessarily; call it only when you actually have to tell the * player what time it is - and don't tell the player what time it * is unless they ask, or there's some other good reason. * * If you want a string-formatted version of the time (as in * '9:05pm'), you can call checkTimeFmt(). */ checkTime() { local turns; local mm; /* * Determine how many turns it's been since we last committed to * a specific wall-clock time. This will give us the * psychological "scale" of the amount of elapsed wall-clock the * user might expect. */ turns = Schedulable.gameClockTime - turnLastCommitted; /* * start with the base scaling factor - this is the number of * minutes of game time we impute to a hundred turns, in the * absence of the constraint of running up against the next event */ mm = (turns * baseScaleFactor) / 100; /* * If the base scaled time would take us within two hours of the * next event time, slow the clock down from our base scaling * factor so that we always leave ourselves room to advance the * clock further on the next check. Reduce the passage of time * in proportion to our reduced window - so if we have only 60 * minutes left, advance time at half the normal pace. */ if (nextTime != nil) { /* get the minutes between now and the next scheduled event */ local delta = diffMinutes(nextTime, curTime); /* check to see if the raw increment would leave under 2 hours */ if (delta - mm < 120) { /* * The raw time increment would leave us under two hours * away. If we have under two hours to go before the * next event, scale down the rate of time in proportion * to our share under two hours. (Note that we might * have more than two hours to go and still be here, * because the raw adjusted time leaves under two * hours.) */ if (delta < 120) mm = (mm * delta) / 120; /* * In any case, cap it at half the remaining time, to * ensure that we won't ever make it to the next event * time until the next event occurs. */ if (mm > delta / 2) mm = delta / 2; } } /* * If our calculation has left us with no passage of time, simply * return the current time unchanged, and do not treat this as a * commit point. We don't consider this a commit point because * we treat it as not even checking again - it's effectively just * a repeat of the last check, since it's still the same time. * This ensures that we won't freeze the clock for good due to * rounding - enough additional turns will eventually accumulate * to nudge the clock forward. */ if (mm == 0) return curTime; /* add the minutes to the current time */ curTime = addMinutes(curTime, mm); /* the current turn is now the last commit point */ turnLastCommitted = Schedulable.gameClockTime; /* return the new time */ return curTime; } /* * The base scaling factor: this is the number of minutes per hundred * turns when we have unlimited time until the next event. This * number is pretty arbitrary, since we're depending so much on the * player's uncertainty about just how long things take, and also * because we'll adjust it anyway when we're running out of time * before the next event. Even so, you might want to adjust this * value up or down according to your sense of the pacing of your * game. */ baseScaleFactor = 60 /* * Get the current game-clock time, formatted into a string with the * given format mask - see formatTime() for details on how to write a * mask string. * * Note that the same cautions for checkTime() apply here - calling * this routine commits us to a particular time, so you should call * this routine only when you're actually ready to display a time to * the player. */ checkTimeFmt(fmt) { return formatTime(checkTime(), fmt); } /* * Get a formatted version of the given wall-clock time. The time is * expressed as a list, in the same format as ClockEvent.eventTime: * [day,hour,minute], where 'day' is 1 for the first day of the game, * 2 for the second, and so on. * * The format string consists of one or more prefixes, followed by a * format mask. The prefixes are flags that control the formatting, * but don't directly insert any text into the result string: * * 24 -> use 24-hour time; if this isn't specified, a 12-hour clock * is used instead. On the 24-hour clock, midnight is hour zero, so * 12:10 AM is represented as 00:10. * * [am][pm] -> use 'am' as the AM string, and 'pm' as the PM string, * for the 'a' format mask character. This lets you specify an * arbitrary formatting for the am/pm marker, overriding the default * of 'am' or 'pm'. For example, if you want to use 'A.M.' and * 'P.M.' as the markers, you'd write a prefix of [A.M.][P.M.]. If * you want to use ']' within the marker string itself, quote it with * a '%': '[[AM%]][PM%]]' indicates markers of '[AM]' and '[PM]'. * * Following the prefix flags, you specify the format mask. This is * a set of special characters that specify parts of the time to * insert. Each special character is replaced with the corresponding * formatted time information in the result string. Any character * that isn't special is just copied to the result string as is. The * special character are: * * h -> hour, no leading zero for single digits (hence 9:05, for * example) * * hh -> hour, leading zero (09:05) * * m -> minutes, no leading zero (9:5) * * mm -> minutes with a leading zero (9:05) * * a -> AM/PM marker. If an [am][pm] prefix was specified, the 'am' * or 'pm' string from the prefix is used. Otherwise, 'am' or 'pm' * is literally inserted. * * % -> quote next character (so %% -> a single %) * * other -> literal * * Examples: * * 'hh:mma' produces '09:05am' *. '[A.M][P.M]h:mma' produces '9:05 P.M.' *. '24hhmm' produces '2105'. */ formatTime(t, fmt) { local hh = t[2]; local mm = t[3]; local pm = (hh >= 12); local use24 = nil; local amStr = nil; local pmStr = nil; local ret; local match; /* check flags */ for (;;) { local fl; /* check for a flag string */ match = rexMatch( '24|(<^rsquare>|%%)+', fmt, 1); /* if we didn't find another flag, we're done */ if (match == nil) break; /* pull out the flag text */ fl = fmt.substr(1, match); fmt = fmt.substr(match + 1); /* check the match */ if (fl == '24') { /* note 24-hour time */ use24 = true; } else { /* it's an am/pm marker - strip the brackets */ fl = fl.substr(2, fl.length() - 2); /* change any '%]' sequences into just ']' */ fl = fl.findReplace('%]', ']', ReplaceAll, 1); /* set AM if we haven't set it already, else set PM */ if (amStr == nil) amStr = fl; else pmStr = fl; } } /* if we didn't select an AM/PM, use the default */ amStr = (amStr == nil ? 'am' : amStr); pmStr = (pmStr == nil ? 'pm' : pmStr); /* adjust for a 12-hour clock if we're using one */ if (!use24) { /* subtract 12 from PM times */ if (pm) hh -= 12; /* hour 0 on a 12-hour clock is written as 12 */ if (hh == 0) hh = 12; } /* run through the format and build the result string */ for (ret = '', local i = 1, local len = fmt.length() ; i <= len ; ++i) { /* check what we have */ match = rexMatch( 'h|hh|m|mm|a|A|am|AM|a%.m%.|A.%M%.|24|%%', fmt, i); if (match == nil) { /* no match - copy this character literally */ ret += fmt.substr(i, 1); } else { /* we have a match - check what we have */ switch (fmt.substr(i, match)) { case 'h': /* add the hour, with no leading zero */ ret += toString(hh); break; case 'hh': /* add the hour, with a leading zero if needed */ if (hh < 10) ret += '0'; ret += toString(hh); break; case 'm': /* add the minute, with no leading zero */ ret += toString(mm); break; case 'mm': /* add the minute, with a leading zero if needed */ if (mm < 10) ret += '0'; ret += toString(mm); break; case 'a': /* add the am/pm indicator */ ret += (pm ? pmStr : amStr); break; case '%': /* add the next character literally */ ++i; ret += fmt.substr(i, 1); break; } /* skip any extra characters in the field */ i += match - 1; } } /* return the result string */ return ret; } /* pre-initialize */ execute() { local vec; /* build a list of all of the ClockEvent objects in the game */ vec = new Vector(10); forEachInstance(ClockEvent, {x: vec.append(x)}); /* sort the list by time */ vec.sort(SortAsc, {a, b: a.compareTime(b)}); /* store it */ eventList = vec.toList(); /* * The earliest event is always the marker for the beginning of * the game. Since it's now the start of the game, mark the * first event in our list as reached. (The first event is * always the earliest we find, by virtue of the sort we just * did.) */ vec[1].eventReached(); } /* * Receive notification from a clock event that an event has just * occurred. (This isn't normally called directly from game code; * instead, game code should usually call the ClockEvent object's * eventReached() method.) */ eventReached(evt) { local idx; /* find the event in our list */ idx = eventList.indexOf(evt); /* * Never go backwards - if events fire out of order, keep only * the later event. (Games should generally be constructed in * such a way that events can only fire in order to start with, * but in case a weird case slips through, we make this extra * test to ensure that the player doesn't see any strange * retrograde motion on the clock.) */ if (lastEvent != nil && lastEvent.compareTime(evt) > 0) return; /* note the current time */ curTime = evt.eventTime; /* if there's another event following, note the next time */ if (idx < eventList.length()) nextTime = eventList[idx + 1].eventTime; else nextTime = nil; /* * we're committing to an exact wall-clock time, so remember the * current turn counter as the last commit point */ turnLastCommitted = Schedulable.gameClockTime; } /* add minutes to a [dd,hh,mm] value, returning a new [dd,hh,mm] value */ addMinutes(t, mm) { /* add the minutes; if that takes us over 60, carry to hours */ if ((t[3] += mm) >= 60) { local hh; /* we've passed 60 minutes - figure how many hours that is */ hh = t[3] / 60; /* keep only the excess-60 minutes in the minutes slot */ t[3] %= 60; /* add the hours; if that takes us over 24, carry to days */ if ((t[2] += hh) >= 24) { local dd; /* we've passed 24 hours - figure how many days that is */ dd = t[2] / 24; /* keep only the excess-24 hours in the hours slot */ t[2] %= 24; /* add the days */ t[1] += dd; } } /* return the adjusted time */ return t; } /* get the difference in minutes between two [dd,hh,mm] values */ diffMinutes(t1, t2) { local mm; local hh; local dd; local bhh = 0; local bdd = 0; /* get the difference in minutes; if negative, note the borrow */ mm = t1[3] - t2[3]; if (mm < 0) { mm += 60; bhh = 1; } /* get the difference in hours; if negative, note the borrow */ hh = t1[2] - t2[2] - bhh; if (hh < 0) { hh += 24; bdd = 1; } /* get the difference in days */ dd = t1[1] - t2[1] - bdd; /* add them all together to get the total minutes */ return mm + 60*hh + 60*24*dd; } /* * our list of clock events (we build this automatically during * pre-initialization) */ eventList = nil /* the current game clock time */ curTime = nil /* the most recent event that we reached */ lastEvent = nil /* the next event's game clock time */ nextTime = nil /* * The turn counter (Schedulable.gameClockTime) on the last turn * where committed to a specific time. Each time we check the time, * we look here to see how many turns have elapsed since the last * time check, and we use this to choose a plausible scale for the * wall-clock time change. */ turnLastCommitted = 0 ; /* * Clock-setting plot event. This object represents a plot point that * occurs at a particular time in the story world. Create one of these * for each of your plot events. The Clock Manager automatically builds * a list of all of these objects during pre-initialization, so you don't * have to explicitly tell the clock manager about these. * * Whenever the story reaches one of these events, you should call the * eventReached() method of the event object. This will set the clock * time to the event's current time, and take note of how long we have * until the next plot event. */ class ClockEvent: object /* * The time at which this event occurs. This is expressed as a list * with three elements: the day number, the hour (on a 24-hour * clock), and the minute. The day number is relative to the start * of the game - day 1 is the first day of the game. So, for * example, to express 2:40pm on the second day of the game, you'd * write [2,14,40]. Note that 12 AM is written as 0 (zero) on a * 24-hour clock, so 12:05am on day 1 would be [1,0,5]. */ eventTime = [1,0,0] /* get a formatted version of the event time */ formatTime(fmt) { return clockManager.formatTime(eventTime, fmt); } /* * Compare our event time to another event's time. Returns -1 if our * time is earlier than other's, 0 if we're equal, and 1 if we're * after other. */ compareTime(other) { local a = eventTime; local b = other.eventTime; /* compare based on the most significant element that differs */ if (a[1] != b[1]) return a[1] - b[1]; else if (a[2] != b[2]) return a[2] - b[2]; else return a[3] - b[3]; } /* * Notify the clock manager that this event has just occurred. This * sets the game clock to the event's time. The game code must call * this method when our point in the plot is reached. */ eventReached() { /* notify the clock manager */ clockManager.eventReached(self); } ; /* ------------------------------------------------------------------------ */ /* * [Footnote 1] * * "Schrodinger's cat" is a famous thought experiment in quantum * physics, concerning how a quantum mechanical system exists in * multiple, mutually exclusive quantum states simultaneously until an * observer forces the system to assume only one of the states by the * act of observation. The thought experiment has been popularized as * an illustration of how weird and wacky QM is, but it's interesting to * note that Schrodinger actually devised it to expose what he saw as an * unacceptable paradox in quantum theory. * * The thought experiment goes like this: a cat is locked inside a * special box that's impervious to light, X-rays, etc., so that no one * on the outside can see what's going on inside. The box contains, * apart from the cat, a little radiation source and a radiation * counter. When the counter detects a certain radioactive emission, it * releases some poison gas, killing the cat. The radioactive emission * is an inherently quantum mechanical, unpredictable process, and as * such can (and must) be in a superposition of "emitted" and "not * emitted" states until observed. Because the whole system is * unobservable from the outside, the supposition is that everything * inside is "entangled" with the quantum state of the radioactive * emission, hence the cat is simultaneously living and dead until * someone opens the box and checks. It's not just that no one knows; * rather, the cat is actually and literally alive and dead at the same * time. * * Schrodinger's point was that this superposition of the cat's states * is a necessary consequence of the way QM was interpreted at the time * he devised the experiment, but that it's manifestly untrue, since we * know that cats are macroscopic objects that behave according to * classical, deterministic physics. Hence a paradox, hence the * interpretation of the theory must be wrong. The predominant * interpretation of QM has since shifted a bit so that the cat would * now count as an observer - not because it's alive or conscious or * anything metaphysical, but simply because it's macroscopic - so the * cat's fate is never actually entangled with the radioactive source's * quantum state. Popular science writers have continued to talk about * Schrodinger's cat as though it's for real, maybe to make QM seem more * exotic to laypersons, but most physicists today wouldn't consider the * experiment to be possible as literally stated. Physicists today * might think of it as a valid metaphor to decribe systems where all of * the components are on an atomic or subatomic scale, but no one today * seriously thinks you can create an actual cat that's simultaneously * alive and dead. */ frobtads-1.2.3/tads3/lib/samples/0000755000175000001440000000000012145614112015731 5ustar realncusersfrobtads-1.2.3/tads3/lib/samples/startB3.t0000644000175000001440000000240512145513566017454 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 1999, 2002 by Michael J. Roberts. Permission is * granted to anyone to copy and use this file for any purpose. * * This is a starter T3 source file. This is designed for a project * that doesn't require any of the standard TADS 3 adventure game * libraries. * * To compile this game in TADS Workbench, open the "Build" menu and * select "Compile for Debugging." To run the game, after compiling it, * open the "Debug" menu and select "Go." * * This starter file is intended for people who want to use T3 to create * projects that don't fall into the usual TADS 3 adventure game * patterns, so it doesn't include any of the standard libraries. If * you want to create a more typical Interactive Fiction project, you * might want to create a new project, and select the "introductory" or * "advanced" option when the New Project Wizard asks you what kind of * starter game you'd like to create. */ #include /* * The main entrypoint - the T3 virtual machine calls this function to * start the program running. 'args' is a list of strings giving the * command-line arguments that the user specified, if any. */ main(args) { // put your program code here } frobtads-1.2.3/tads3/lib/webui.tl0000644000175000001440000000006311374242715015751 0ustar realncusersname: TADS Web UI source: webui resource: webuires frobtads-1.2.3/tads3/lib/reflect.t0000644000175000001440000002721011773577500016117 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts * * This file is part of TADS 3. */ #include #include #include "reflect.h" /* ------------------------------------------------------------------------ */ /* * Main reflection services object. * * During pre-initialization, we'll plug this into the _main module's * globals so that the _main module will know it can use reflection * services. */ reflectionServices: PreinitObject /* execute preinitialization */ execute() { /* plug ourselves into the main globals */ mainGlobal.reflectionObj = self; /* store the main symbol table */ symtab_ = t3GetGlobalSymbols(); /* create a reverse lookup table from the main symbol table */ if (symtab_ != nil) { /* * create a lookup table for the reverse table - it'll be * the same size as the original table, so create it using * the same statistics */ reverseSymtab_ = new LookupTable(symtab_.getBucketCount(), symtab_.getEntryCount()); /* * for each entry in the main table, create an entry in the * reverse table with the role of key and value reversed - * this will allow us to look up any value and find its * global symbol, if it has one */ symtab_.forEachAssoc({key, val: reverseSymtab_[val] = key}); } } /* * Convert a value to a symbol, or to a string representation if * it's not of a symbolic type. */ valToSymbol(val) { local sym; /* the representation depends on the type */ switch(dataType(val)) { case TypeNil: return 'nil'; case TypeTrue: return 'true'; case TypeInt: return toString(val); case TypeSString: case TypeList: case TypeObject: /* * If we're asking about 'self', inherit the handling. Note * that, for any object type, x.ofKind(x) is always true, so * there's no need for a separate test to see if val equals * self. */ if (val.ofKind(self)) return inherited(); /* check for intrinsic classes */ if (IntrinsicClass.isIntrinsicClass(val)) { /* intrinsic classes should always be in the symbol table */ sym = reverseSymtab_[val]; return (sym != nil ? sym : '{intrinsicClass}'); } /* use our special value-to-symbol method on the object itself */ return val.valToSymbol(); case TypeProp: /* * this should usually convert to a symbol, but might have * been allocated dynamically */ sym = reverseSymtab_[val]; return (sym != nil ? sym : '{prop}'); case TypeFuncPtr: /* * look for a name; if it doesn't have one, it must be an * anonymous function */ sym = reverseSymtab_[val]; return (sym != nil ? sym : '{anonFunc}'); case TypeEnum: /* these should always convert directly to symbols */ sym = reverseSymtab_[val]; return (sym != nil ? sym : '{enum}'); case TypeBifPtr: /* these should always convert directly to symbols */ sym = reverseSymtab_[val]; return (sym != nil ? sym : '{intrinsicFunc}'); case TypeNativeCode: return '{native code}'; default: return '???'; } } /* * Format a stack frame object (of class T3StackInfo). */ formatStackFrame(fr, includeSourcePos) { local ret = new StringBuffer(); /* see what kind of frame we have */ if (fr.func_ != nil) { /* it's a function */ ret.append(valToSymbol(fr.func_)); } else if (fr.obj_ != nil) { /* * It's an object.property. Check for one special case we * want to show specially: if the object is an AnonFuncPtr * object, ignore the property and just show it as an * anonymous function. */ if (fr.obj_.ofKind(AnonFuncPtr)) ret.append('{anonFunc}'); else { ret.append(valToSymbol(fr.self_)); ret.append('.'); ret.append(valToSymbol(fr.prop_)); } } else { /* no function or object - must be a system routine */ ret.append('(System)'); } /* if it's not a system routine, add the argument list */ if (fr.argList_ != nil) { /* add the open paren */ ret.append('('); /* add the arguments */ local i, len = fr.argList_.length(); for (i = 1 ; i <= len ; ++i) { /* if it's not the first one, add a comma */ if (i != 1) ret.append(', '); /* add this value */ ret.append(valToSymbol(fr.argList_[i]).htmlify()); } /* add any named arguments */ if (fr.namedArgs_ != nil) { /* add each key from the named argument table */ fr.namedArgs_.forEachAssoc(function(key, val) { /* add a separator if this isn't the first item */ if (i++ != 1) ret.append(', '); /* add this "name: value" */ ret.append(key); ret.append(':'); ret.append(valToSymbol(val)); }); } /* add the close paren */ ret.append(')'); /* if desired, add the source location */ if (includeSourcePos && fr.srcInfo_ != nil) { ret.append(' '); ret.append(fr.srcInfo_[1]); ret.append(', line '); ret.append(fr.srcInfo_[2]); } } /* return the result */ return toString(ret); } /* the global symbol table */ symtab_ = nil /* the global reverse-lookup symbol table */ reverseSymtab_ = nil ; /* ------------------------------------------------------------------------ */ /* * Export the reflection services interfaces used by the VM */ export reflectionServices 'reflection.reflectionServices'; export valToSymbol 'reflection.valToSymbol'; /* ------------------------------------------------------------------------ */ /* * Modify the basic Object class to provide a to-symbol mapping */ modify Object valToSymbol() { /* get my symbol from the global reflection table */ local sym = reflectionServices.reverseSymtab_[self]; /* if we got a symbol, return it */ if (sym != nil) return sym; /* * We didn't get a symbol, so there's no source file name. See * if we can find source-file names for the superclasses, though. */ sym = '{obj:'; local found = nil; foreach (local sc in getSuperclassList()) { local scSym; /* add a comma to the list if this isn't the first element */ if (sym != '{obj:') sym += ','; /* if we have a name here, add it to the list */ if ((scSym = reflectionServices.reverseSymtab_[sc]) != nil) { /* note that we found a named superclass */ found = true; /* add the superclass name to the list */ sym += scSym; } else { /* we don't have a name for this superclass; say so */ sym += '{anonymous}'; } } /* * if we found any named superclasses, return the list of names; * otherwise, just say (obj) */ return (found ? sym + '}' : '{obj}'); } ; /* ------------------------------------------------------------------------ */ /* * Modify the String intrinsic class to provide a to-symbol mapping */ modify String valToSymbol() { local ret; local i; local start; /* start with an open quote */ ret = '\''; /* loop through the string to find each special character */ for (i = 1, local len = length(), start = 1 ; i <= len ; ++i) { local qu; /* presume we won't add a quoted character on this round */ qu = nil; /* see what we have here */ switch(substr(i, 1)) { case '\\': qu = '\\\\'; break; case '\'': qu = '\\\''; break; case '\n': qu = '\\n'; break; case '\t': qu = '\\t'; break; case '\b': qu = '\\b'; break; case '\ ': qu = '\\ '; break; case '\^': qu = '\\^'; break; case '\v': qu = '\\v'; break; } /* * if we have a quoted character, add the part up to the * quoted character plus the quoted character */ if (qu != nil) { /* add the part up to here but not including this char */ if (i != start) ret += substr(start, i - start); /* add the quoted form of the character */ ret += qu; /* start again after this character */ start = i + 1; } } /* add the trailing unquoted part if we haven't already */ if (i != start) ret += substr(start, i - start); /* add a close quote and return the result */ return ret + '\''; } ; /* ------------------------------------------------------------------------ */ /* * Modify the List intrinsic class to provide a to-symbol mapping */ modify List valToSymbol() { local ret; /* start off with an open bracket */ ret = '['; /* convert each element to symbolic form */ for (local i = 1, local len = length() ; i <= len ; ++i) { /* add a comma if this isn't the first element */ if (i != 1) ret += ', '; /* add this element converted to symbolic form */ ret += reflectionServices.valToSymbol(self[i]); } /* add the close bracket and return the result */ return ret + ']'; } ; /* ------------------------------------------------------------------------ */ /* * If desired, modify the BigNumber intrinsic class to provide a * to-symbol mapping. We only include this modification if the program * is compiled with REFLECT_BIGNUM defined. */ #ifdef REFLECT_BIGNUM #include "bignum.h" modify BigNumber valToSymbol() { /* use the default formatting */ return formatString(12); } ; #endif /* REFLECT_BIGNUM */ frobtads-1.2.3/tads3/lib/tadsnet.t0000644000175000001440000003635011736431752016137 0ustar realncusers#charset "us-ascii" #include #include /* * Copyright (c) 1999, 2006 Michael J. Roberts * * This file is part of TADS 3 * * This file defines classes and properties used with the tads-net * intrinsic function set. If you're using this function set, you should * include this source file in your build by adding it to your project * makefile. */ /* include the tads-net intrinsic function set interface definition */ #include /* ------------------------------------------------------------------------ */ /* * A NetEvent instance describes an event read via the getNetEvent() * function. * * In most cases, this base class will not be instantiated directly. * getNetEvent() will always construct the appropriate subclass for the * specific type of event being generated, if that subclass is defined in * the game program. However, it's possible that the game won't define * all necessary subclasses. For example, a game written for version 1 * of the networking package wouldn't include new subclasses added in * version 2, because those subclasses weren't defined at the time the * game was written. When getNetEvent() needs to instantiate a subclass * that isn't defined in the game program, it will instead create a base * NetEvent object, which will simply store the subclass-specific * arguments as a list. This could be useful for debugging purposes, * because it will at least let the programmer inspect the event details * with the interactive debugger. */ class NetEvent: object /* * The event type. This is a NetEvXxx value (see tadsnet.h) * indicating which type of event this is. */ evType = nil /* * Construction. getNetEvent() only constructs this object directly * when the subclass it's looking for isn't defined in the game * program. */ construct(t, [args]) { evType = t; evArgs = args; } /* * Extra event-specific arguments. This is primarily for debugging * purposes, since it's only used when getNetEvent() needs to * construct a NetEvent subclass that isn't defined in the game. In * this case, the absence of a subclass definition in the game * presumably means that the game isn't written to handle the type of * event generated (for example, because it was written for an older * interpreter version that didn't have the event type). */ evArgs = nil ; /* * Network Request Event. This type of event occurs when a server (such * as an HTTPServer object) receives a request from a network client. * * The evRequest member contains a request object describing the network * request. The class of this object depends on the type of server that * received the request. For example, for an HTTP server, this will be * an HTTPRequest object. To reply to the request, use the appropriate * method(s) in the request object - for details, see the specific * request classes for the server types you create in your program. */ class NetRequestEvent: NetEvent /* construction */ construct(t, req) { inherited(t, req); evRequest = req; } evType = NetEvRequest /* * The request object. When the event type is NetEvRequest, this * contains a request object describing the request. The class of * the request object varies according to the server type; you can * use ofKind() to check which type of request it is. For example, * for an HTTP request, this will be an object of class HTTPRequest. */ evRequest = nil ; /* * Network Timeout Event. getNetEvent() returns this type of event when * the timeout interval expires before any actual event occurs. */ class NetTimeoutEvent: NetEvent evType = NetEvTimeout ; /* * Network Reply event. This type of event occurs when we receive a * reply to a network request made with sendNetRequest(). */ class NetReplyEvent: NetEvent /* construction */ construct(t, id, status, body, headers, loc) { inherited(t, id, body, headers, loc); statusCode = status; requestID = id; replyBody = body; replyHeadersRaw = headers; redirectLoc = loc; /* parse the headers into a lookup table keyed by header name */ if (headers != nil) { /* create the lookup table */ local ht = replyHeaders = new LookupTable(); /* split the headers at the CR-LF separators */ headers = headers.split('\r\n'); /* the first line of the headers is actually the HTTP status */ if (headers.length() > 1) { /* save the status line */ httpStatusLine = headers[1]; headers = headers.sublist(2); } /* process the rest of the headers */ for (local h in headers) { /* split the header at the ":", and trim spaces */ h = h.split(':', 2).mapAll( { s: rexReplace('^+|+$', s, '') }); /* * If it looks like a header, add it to the table. If * the header is repeated, append it to the previous * value with a comma delimiter. */ if (h.length() == 2) { local name = h[1].toLower(), val = h[2]; if (ht.isKeyPresent(name)) val = '<>, <>'; ht[name] = val; } } } } /* our default event type is NetEvReply */ evType = NetEvReply /* * The request identifier. This is the ID value provided by the * caller in the call to sendNetRequest(), so that the caller can * relate the reply back to the corresponding request. */ requestID = nil /* * The network status code. This is an integer value indicating * whether the request was successful or failed with an error. A * negative value is a low-level TADS error indicating that the * request couldn't be sent to the server, or that a network error * occurred receiving the reply: * *. -1 - out of memory *. -2 - couldn't connect to host *. -3 - other network/socket error *. -4 - invalid parameters *. -5 - error reading the content data to send to the server *. -6 - error saving the reply data received from the server *. -7 - error retrieving reply headers *. -8 - error starting background thread *. -100 - other TADS/network error * * A positive value means that the network transaction itself was * successful, and reflects the status information returned by the * network server that handled the request. This must be interpreted * according to the protocol used to send the request: * * - For HTTP requests, the value is an HTTP status code. A code in * the 200 range generally indicates success, while other ranges * generally indicate errors. */ statusCode = nil /* the content body from the reply */ replyBody = nil /* * the HTTP headers from the reply, as a lookup table indexed by * header name */ replyHeaders = nil /* the HTTP status string (the first line of the headers) */ httpStatusLine = nil /* * the HTTP headers from the reply, in the raw text format - this is * simply a string of all the headers, separated by CR-LF (\r\n) * sequences */ replyHeadersRaw = nil /* * Redirect location, if applicable. By default, this will be nil * whether or not a redirection took place, because sendNetRequest() * normally follows redirection links transparently, returning only * the final result from the final server we're redirected to. * However, you can override automatic redirection with an option * flag (NetReqNoRedirect) when calling sendNetRequest(). When that * option is selected, the function won't follow redirection links at * all, but will instead simply return the redirect information as * the result from the request. When that happens, this property is * set to a string giving the target of the redirect. You can then * follow the redirect manually, if desired, by sending a new request * to the target given here. */ redirectLoc = nil ; /* * Network Reply Done event. This type of event occurs when an * asynchronous network reply (such as HTTPRequest.sendReplyAsync()) * completes. */ class NetReplyDoneEvent: NetEvent /* construction */ construct(t, req, err, msg) { inherited(t, req, err, msg); requestObj = req; socketErr = err; errMsg = msg; } /* our default event type is NetEvReplyDone */ evType = NetEvReplyDone /* * The object representing the request we replied to. For HTTP * requests, this is an HTTPRequest object. */ requestObj = nil /* was the reply successfully sent? */ isSuccessful() { return errMsg == nil; } /* * The socket error, if any. If the reply failed due to a network * error, this contains the error number. If no network error * occurred, this is zero. */ socketErr = 0 /* * Error message, if any. If the reply failed, this contains a * string with a description of the error that occurred. If the * reply was sent successfully, this is nil. */ errMsg = nil ; /* ------------------------------------------------------------------------ */ /* * A FileUpload represents a file uploaded by a network client via a * protocol server, such as an HTTPServer. * * When your program is acting as a network server, a FileUpload object * represents a file received from the client. For example, * HTTPRequest.getFormFields() returns a FileUpload object to represent * each field in the posted form. * * When your program acts as a network client (via sendNetRequest), you * can create use FileUpload to post file attachments to posted forms. */ class FileUpload: object construct(file, contentType, filename) { self.file = file; self.contentType = contentType; self.filename = filename; } /* * The file data. * * When you create the FileUpload object for use with * sendNetRequest() to post form data, you must use a string or * ByteArray value for this property. * * When the FileUpload is created by HTTPRequest.getFormFields(), * this property contains a File object with the uploaded content. * This is open for read-only access. If the contentType parameter * is a text type ("text/html", "text/plain", etc), and the * interpreter recognizes the character set parameter in the * contentType, the file is in Text mode (FileModeText) with the * appropriate character mapper in effect. Otherwise, the file is in * raw binary mode (FileModeRaw). If you need the file to be opened * in a different mode, you can use setFileMode() on the file to * change the mode. */ file = nil /* * The content type. This a string giving the MIME type specified by * the client with the upload. This is the full content-type string, * including any attributes, such "charset" for a text type. This * can be nil if the client doesn't specify a content-type at all. * * It's important to recognize that this information is supplied by * the client, and is NOT validated by the protocol server. At best * you should consider it a suggestion, and at worst a malicious lie. * The client could be innocently mistaken about the type, or could * even be intentionally misrepresenting it. You should always * validate the actual contents, rather than relying on the client's * description of the format; in particular, be careful not to assume * that expected data fields are present, in the valid range, etc. */ contentType = nil /* * The client-side filename, if specified. This is a string giving * the name of the file on the client machine. This generally has no * particular meaning to the server, since we can't infer anything * about the directory structure or naming conventions on an * arbitrary client. However, this might be useful for reference, * such as showing information about the upload in a user interface. * It's sometimes also marginally useful to know the suffix * (extension) for making further guesses about the content type - * although as with the content-type, you can't rely upon this, but * can only use it as a suggestion from the client. * * The client won't necessarily specify a filename at all, in which * case this will be nil. */ filename = nil ; /* ------------------------------------------------------------------------ */ /* * A NetException is the base class for network errors. */ class NetException: Exception construct(msg?, errno?) { if (errMsg != nil) errMsg = 'Network error: <>'; if (errno != nil) errMsg += ' (system error code <>)'; } displayException() { "<>"; } /* a descriptive error message provided by the system */ errMsg = 'Network error' ; /* * A NetSafetyException is thrown when the program attempts to perform a * network operation that isn't allowed by the current network safety * level settings. The user controls the safety level; the program can't * override this. */ class NetSafetyException: NetException errMsg = 'Network operation prohibited by user-specified ' + 'network safety level' ; /* * A SocketDisconnectException is thrown when attempting to read or write * a network socket that's been closed, either by us or by the peer (the * computer on the other end of the network connection). If we didn't * close the socket on this side, this error usually means simply that * the peer program has terminated or otherwise disconnected, so we * should consider the conversation terminated. */ class SocketDisconnectException: NetException errMsg = 'Network socket disconnected by peer or closed' ; /* export the objects and properties used in the tads-net function set */ export NetEvent 'TadsNet.NetEvent'; export NetRequestEvent 'TadsNet.NetRequestEvent'; export NetTimeoutEvent 'TadsNet.NetTimeoutEvent'; export NetReplyEvent 'TadsNet.NetReplyEvent'; export NetReplyDoneEvent 'TadsNet.NetReplyDoneEvent'; export NetException 'TadsNet.NetException'; export SocketDisconnectException 'TadsNet.SocketDisconnectException'; export NetSafetyException 'TadsNet.NetSafetyException'; export FileUpload 'TadsNet.FileUpload'; export file 'TadsNet.FileUpload.file'; export contentType 'TadsNet.FileUpload.contentType'; export filename 'TadsNet.FileUpload.filename'; frobtads-1.2.3/tads3/lib/system.tl0000644000175000001440000000032511044374034016155 0ustar realncusersname: TADS System Files source: _main source: file source: tok source: gramprod source: multmeth # we explicitly include the main startup module, so we don't want the # compiler to include it automatically nodef frobtads-1.2.3/tads3/lib/_main.t0000644000175000001440000015070711705536143015557 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved. * * This module defines a number of low-level functions and classes that * most TADS 3 programs will need, whether based on the adv3 library or * not. This module includes the main program entrypoint, the basic * Exception classes, and the modular initialization framework. * * The compiler automatically links this module into every program by * default, but you can override this by specifying the "-nodef" option * to t3make. If you remove this module, you'll have to provide your own * implementations for many of the functions and classes defined here. */ #include "tads.h" #include "reflect.h" #include "strbuf.h" /* ------------------------------------------------------------------------ */ /* * Main program entrypoint. The VM invokes this function at program * startup. */ _main(args) { /* call the common main entrypoint, with no startup file specified */ _mainCommon(args, nil); } /* declare 'main' as a function, in case it's not otherwise defined */ extern function main; /* * Main program entrypoint for restoring a saved state. The VM invokes * this function at startup instead of _main() when the user explicitly * specifies a saved state file to restore when starting the program. * (On a command-line interpreter, this would involve using a special * option on the T3 interpreter command line; for a GUI shell, this * might simply involve double-clicking on the desktop icon for a saved * state file.) * * Note that we must export this as the 'mainRestore' symbol so that the * interpreter knows how to find it. */ export _mainRestore 'mainRestore'; _mainRestore(args, restoreFile) { /* call the common main entrypoint */ _mainCommon(args, restoreFile); } /* * Common main entrypoint. This function can be called with or without * a saved state file to restore. */ _mainCommon(args, restoreFile) { try { /* * Keep going as long as we keep restarting. Note that the * restoreFile only counts on the first iteration, so we clear it * out after each iteration; if we RESTART after that, we'll want * to just start the game from the beginning. */ for ( ; ; restoreFile = nil) { /* perform load-time initialization */ initAfterLoad(); /* if we're in preinit-only mode, we're done */ if (t3GetVMPreinitMode()) return; /* catch any RESTART signals thrown out of the main entrypoint */ try { /* * If there's a saved state file to restore, call our * mainRestore() function instead of main(). If * mainRestore() isn't defined, show a message to this * effect but keep going. */ if (restoreFile != nil && dataType(mainGlobal.mainRestoreFunc) == TypeFuncPtr) { /* * Call the user's main startup-and-restore * entrypoint function. Note that we call indirectly * through our function pointer so that we don't * force a function called mainRestore() to be linked * with the program. */ (mainGlobal.mainRestoreFunc)(args, restoreFile); } else { /* * if we have a restore file but no mainRestore * routine, explain to the user that they'll have to * restore manually */ if (restoreFile != nil) { "\n[This program cannot restore the saved position file automatically. Please try restoring the saved position file again using a command within the program.]\b"; } /* call the selected main entrypoint */ flexcall(&main, args); } /* * we made it through the main entrypoint without a * restart, so we're done */ break; } catch (RestartSignal rsig) { /* * call the intrinsic restartGame function to reset all * of the static objects to their initial state */ restartGame(); /* * Now that we've reset the VM, update the restart ID in * the main globals. Note that we waited until now to do * this, because this change would have been lost in the * reset if we'd made the change before the reset. Note * also that the 'rsig' object itself will survive the * reset because the thrower presumably allocated it * dynamically, hence it's not a static object subject to * reset. */ mainGlobal.restartID = rsig.restartID; /* * Now we can just continue on to the next iteration of * the restart loop. This will take us back to the * initialization and enter the game as though we'd just * started the program again. */ } } } catch (ProgramException exc) { /* * just re-throw these out to the VM, so that the VM exits to * the operating system with an error indication */ throw exc; } catch (Exception exc) { /* write output to the main console */ t3SetSay(_default_display_fn); /* display the unhandled exception */ "\n<>\n"; } finally { /* before exiting, call registered exit handlers */ mainAtExit.callHandlers(); } } /* ------------------------------------------------------------------------ */ /* * Flexible function call. This calls the given function, passing as * many arguments from the given argument list as the function actually * wants. If the list doesn't have enough arguments to satisfy the * function's minimum requirements, we add 'nil' arguments to pad out the * minimum. If the list exceeds the function's maximum, we drop * arguments past the maximum. */ flexcall(func, [args]) { /* get the function's desired argument list */ local paramDesc = getFuncParams(func); /* add or remove arguments as needed to fit the limits */ local n = args.length(); if (n < paramDesc[1]) { /* we need more to satisfy the minimum - add nil values */ args += makeList(nil, paramDesc[1] - n); } else if (n > paramDesc[1] + paramDesc[2] && !paramDesc[3]) { /* * the function doesn't take infinite arguments, and we have more * than it allows in the fixed plus optional parts, so drop the * extra elements */ args = args.sublist(1, paramDesc[1] + paramDesc[2]); } /* call the function */ func(args...); } /* ------------------------------------------------------------------------ */ /* * Restart signal. This can be used to restart from the main * entrypoint. The caller should create one of these objects, then use * restartGame() (or an equivalent from a different function set, if * appropriate) to reset static object state to the initial program load * conditions, then throw the signal object. */ class RestartSignal: Exception construct() { /* * use the next restart ID, so we can tell that we're on a fresh * run on this session */ restartID = mainGlobal.restartID + 1; } ; /* ------------------------------------------------------------------------ */ /* * General post-load initialization. The main program entrypoint * _main() calls this routine to set up the default display function, * run pre-initialization if necessary, and run initialization. This * routine is also useful for the target of a restartGame() routine, to * perform all of the basic load-time initialization again after a * restart. */ initAfterLoad() { /* establish the default display function */ t3SetSay(_default_display_fn); /* if we haven't run preinit, do so now */ if (!mainGlobal.preinited_) { /* * Explicitly run garbage collection prior to preinit. * * In most cases, this is unnecessary, but in some cases it's * important. In particular, object loops (over all objects, or * over all instances of a given class) can still see otherwise * unreachable objects. It's common to do object loops in * preinit to set up static data caches and tables and so on. If * there *were* any garbage objects lying around, preinit could * find them via object loops, and might register them into * tables or what not. * * Even considering the preinit object loop, doing a garbage * collection sweep would *still* be redundant in most cases, * since preinit is normally done right after compilation, when * the program wouldn't yet have had a chance to create any * garbage objects to be worried about in object loops. However, * there's still one more case to consider, and that's RESTART: * in a debug build, or even in some release builds, we'd have to * re-run preinit after a RESTART, and there certainly could be * garbage objects left around from before the RESTART. * * To ensure that we deal gracefully with this combination of * conditions - garbage objects, RESTART, and object loops in * preinit - simply do an explicit garbage collection run before * invoking preinit. */ t3RunGC(); /* run our internal preinit */ _preinit(); /* remember that we've run preinit */ mainGlobal.preinited_ = true; } /* if we're not in preinit-only mode, run internal initialization */ if (!t3GetVMPreinitMode()) _init(); } /* ------------------------------------------------------------------------ */ /* * Module Execution Object. This is an abstract base class for various * classes that provide modular execution hooks. This class and its * subclasses are mix-in classes - they can be multiply inherited by any * object (as long as it's not already some other kind of module * execution object). * * The point of the Module Execution Object and its subclasses is to * allow libraries and user code to define execution hooks, without * having to worry about what other libraries and user code bits are * defining the same hook. When we need to execute a hook defined via * this object, we iterate over all of the instances of the appropriate * subclass and invoke its execute() method. * * By default, the order of execution is arbitrary. In some cases, * though, dependencies will exist, so that one object cannot be invoked * until another object has already been invoked. In these cases, you * must set the execBeforeMe property to contain a list of the objects * whose execute() methods must be invoked before this object's * execute() method is invoked. The library will check this list before * calling execute() on this object, and ensure that each object in the * list has been invoked before calling this object's execute(). */ class ModuleExecObject: object /* * List of objects that must be executed before me - by default, the * order doesn't matter, so we'll set this to an empty list. * Instances can override this if it is necessary to execute other * objects before this object can be executed. */ execBeforeMe = [] /* * List of objects that must be executed after me - this is * analogous to execBeforeMe, but we make sure we run before these. */ execAfterMe = [] /* * Subclass-specific execution method. Each subclass should * override this method to provide its execution code. */ execute() { } /* * PRIVATE METHODS AND PROPERTIES. Subclasses and instances should * not need to override or invoke these. */ /* flag - true if we've been executed on this round */ isExecuted_ = nil /* flag - true if we're in the process of executing */ isDoingExec_ = nil /* execute - internal method: checks dependency order */ _execute() { /* * If I've already been executed, there's nothing more that I * need to do. We might be called by the arbitrarily-ordered * iteration over all objects after we've already been executed, * because we might be executed explicitly by an object that * depends upon us if it's reached before we are. */ if (isExecuted_) return; /* * If we're in the process of executing any of the objects we * depend upon, and a dependent calls us, we have a circular * dependency. */ if (isDoingExec_) throw new CircularExecException(self); /* * Mark ourselves as being in the process of executing. If * there are any circular dependencies (i.e., if we depend on an * object, which in turn depends on us), it's clearly an error, * in that both objects can't be executed before the other. * This flag allows us to detect circular dependencies by * noticing if we're called by a dependent while we're in the * process of calling the things we depend upon. */ isDoingExec_ = true; /* * Check each entry in my 'before' list to ensure that they've * all been executed already. Invoke execute() now for any that * haven't. */ for (local i = 1, local cnt = execBeforeMe.length() ; i <= cnt ; ++i) { local cur; /* get this object */ cur = execBeforeMe[i]; /* if this one hasn't been executed yet, do so now */ if (!cur.isExecuted_) { /* * This one hasn't been executed yet - explicitly * execute it now. Note that we do this recursively * through the internal execution method, so that 'cur' * has a chance to execute any objects that it depends * upon. */ cur._execute(); } } /* * we've resolved all of our dependencies, so we're good to go - * run the user's execution code */ execute(); /* * mark ourselves as having been executed, so we don't run the * user's code again should we be called again by a dependent or * by the global iteration loop later in the scan */ isExecuted_ = true; isDoingExec_ = nil; } /* flag to indicate that this is the first time running classExec */ hasInitialized_ = nil /* * Class execution. Call this method on the particular class of * modules to execute. We'll iterate over all instances of that * class and invoke each instance's _execute() method. */ classExec() { /* * If this is the first time running this classExec, turn * execAfterMe dependencies into appropriate execBeforeMe * dependencies. */ if (!hasInitialized_) { /* * Go through all instances of this type of initializer, and * re-cast the execAfterMe lists as execBeforeMe lists. */ forEachInstance(self, function(obj) { foreach(local dependent in obj.execAfterMe) dependent.execBeforeMe += obj; }); /* remember that we're now initialized */ hasInitialized_ = true; } /* * since we're starting a new round, clear all of the 'executed' * flags in all of the objects, to ensure that we execute all * objects on this round (this cleans up the flag settings from * any previous rounds) */ forEachInstance(self, { obj: obj.isExecuted_ = obj.isDoingExec_ = nil }); /* execute all objects */ forEachInstance(self, { obj: obj._execute() }); } ; /* * Pre-Initialization object. During pre-initialization, we'll invoke * the execute() method on each instance of this class. */ class PreinitObject: ModuleExecObject /* * Each instance of this object MUST override execute() with the * specific pre-initialization code that the instance wants to * perform. * * In addition, each instance can optionally set the property * execBeforeMe to a list of the other PreinitObject's that must be * invoked before this object is. If this property is not set, this * object's place in the preinit execution order will be arbitrary. */ ; /* * Initialization object. During initialization, just before calling * the user's main(args) function, we'll invoke the execute() method on * each instance of this class. */ class InitObject: ModuleExecObject /* * Each instance of this object MUST override execute() with the * specific initialization code that the instance wants to perform. * * In addition, each instance can optionally set the property * execBeforeMe to a list of the other InitObject's that must be * invoked before this object is. If this property is not set, this * object's place in the initialization execution order will be * arbitrary. */ ; /* * Exception: circular execution dependency in ModuleExecObject */ class CircularExecException: Exception construct(obj) { obj_ = obj; } displayException() { "circular module dependency detected (refer to ModuleExecObject._execute() in _main.t)"; } /* * The object that detected the circular dependency. We can't use * this for much ourselves, but it might be useful to store this * information so that it's available to the programmer from within * the debugger. */ obj_ = nil ; /* * Library pre-initialization. This is called immediately after * compilation to pre-initialize the program. Any changes made here to * object states become part of the initial state stored in the image * file, so this establishes the static initial state of the program. * * The advantage of doing work during pre-initialization is that this * work is done once, during compilation, and is thus not repeated each * time a user starts the program. Time-consuming initialization work * can thus be made invisible to the user. * * Note that the pre-initialization code should never do anything that * involves the user interface, since this code runs during compilation * and does not run again when users start the program. So, anything * that you want a user to see must be done during normal initialization * (such as in the main() routine), not here. */ _preinit() { local symtab; /* try getting the mainRestore() function from the global symbol table */ if ((symtab = t3GetGlobalSymbols()) != nil) mainGlobal.mainRestoreFunc = symtab['mainRestore']; /* execute all preinit objects */ PreinitObject.classExec(); } /* * Library initialization. This is called during each program start-up * to initialize the program. Since this is run each time the user * starts the program, this can display any introductory messages, set * up the user interface, and so on. */ _init() { /* execute all init objects */ InitObject.classExec(); } /* ------------------------------------------------------------------------ */ /* * For convenience, a simple object iterator function. This function * invokes a callback function for each instance of the given class, in * arbitrary order. * * The callback is invoked with one argument, which gives the current * instance. The callback can "break" out of the loop by throwing a * BreakLoopSignal, which can be done conveniently using the breakLoop * macro. */ forEachInstance(cls, func) { try { /* loop over all objects of the given class */ for (local obj = firstObj(cls) ; obj != nil ; obj = nextObj(obj, cls)) func(obj); } catch (BreakLoopSignal sig) { /* * ignore the signal - it simply means we want to terminate the * loop and return to the caller */ } } /* * Find an instance of the given class for which the given function * returns true. We iterate over objects of the given class in * arbitrary order, and return the first instance for which the function * returns true. Retursn nil if there is no such instance. */ instanceWhich(cls, func) { /* loop over all objects of the given class */ for (local obj = firstObj(cls) ; obj != nil ; obj = nextObj(obj, cls)) { /* if the callback returns true for this object, return the object */ if (func(obj)) return obj; } /* * we didn't find any instances for which the callback returns true; * indicate this by returning nil */ return nil; } /* * An exception object for breaking out of a callback loop, such as * forEachInstance. */ class BreakLoopSignal: Exception displayException() { "loop break signal"; } ; /* ------------------------------------------------------------------------ */ /* * Get the "translated" datatype of a value. This is essentially the * same as dataType(), except that anonymous function objects and dynamic * function objects are treated as being "function pointer" types * (TypeFuncPtr). */ dataTypeXlat(val) { local t; /* get the base type */ t = dataType(val); /* if it's an anonymous function, return TypeFuncPtr */ if (t == TypeObject && (val.ofKind(AnonFuncPtr) || (defined(DynamicFunc) && val.ofKind(DynamicFunc)))) return TypeFuncPtr; /* otherwise, just return the base type */ return t; } /* ------------------------------------------------------------------------ */ /* * Base class for all exception objects. We derive all exceptions from * this base class so that we can write 'catch' blocks that catch all * exceptions by catching 'Exception'. * * The displayException() method displays a message describing the * exception. Subclasses should override this method. */ class Exception: object /* construct, with an optional message describing the error */ construct(msg?, ...) { /* if there's a message, save it, otherwise keep the default */ if (msg != nil) errmsg_ = msg; } /* display the exception - should always be overridden */ displayException() { "<>"; } /* * Get the exception message as a string. This captures the output * of displayException() and returns it a string. Use this instead * of accessing errmsg_, since that member is private and might not * reflect the actual displayed message. */ getExceptionMessage() { /* capture and return the displayed exception message as a string */ return _outputCapture({: displayException() }); } /* * Private member: The error message passed to the constructor, if * any. Note that this doesn't necessarily contain the actual * displayed exception message, since displayException() can be * overridden in subclasses to display additional parameters or other * text entirely. The definitive message is the one that * displayException() generates. If you want the displayed message * as a string, use getExceptionMessage(). */ errmsg_ = 'Unknown exception' /* * Display a stack trace, given a list of T3StackInfo objects. Note * that, for efficiency, we do not by default cache a stack trace * when an exception occurs; individual subclasses can obtain a * stack trace if desired at construction and use the information to * show a stack trace for the exception. */ showStackTrace(stackList) { local haveSrc; /* check to see if there's any source info in the stack trace */ haveSrc = nil; foreach (local cur in stackList) { /* note if we have source info here */ if (cur.srcInfo_ != nil) { /* * we have source information - note it and stop * searching, since even one bit of source info is * enough to show the stack */ haveSrc = true; break; } } /* * if we have any source information at all, or we have * reflection services available to decode the stack trace * symbolically, show the stack */ if (haveSrc || mainGlobal.reflectionObj != nil) { for (local i = 1, local cnt = stackList.length() ; i <= cnt ; ++i) { local cur = stackList[i]; /* show a mark next to level 1, spaces elsewhere */ if (i == 1) "-->"; else "\ \ \ "; /* * if there's a system reflection object, show symbolic * information on the current function call; otherwise, * simply show the source location */ if (mainGlobal.reflectionObj != nil) { /* reflection is available - show full symbolic info */ "<>"; } else { /* no reflection information - show source only */ if (cur.srcInfo_ != nil) "<>, line <>"; else if (cur.isSystem()) "<System>"; else "???"; } /* end the line */ "\n"; } } } ; /* ------------------------------------------------------------------------ */ /* * RuntimeError exception class. The VM creates and throws an instance * of this class when any run-time error occurs. The VM explicitly sets * the exceptionMessage property to a string giving the VM error message * for the run-time error that occurred. */ class RuntimeError: Exception construct(errno, ...) { /* remember the VM error number */ errno_ = errno; /* * Store a stack trace for the current location. Always discard * the first element of the result, since this will reflect * RuntimeError.construct, which is obviously not interesting. */ stack_ = t3GetStackTrace().sublist(2); /* * The next element of the stack trace is usually a native code * frame, because the VM itself invokes our constructor in * response to a runtime exception; this is not an interesting * frame, so if it's present, remove it. */ if (stack_.length() > 0 && stack_[1].isSystem()) stack_ = stack_.sublist(2); } /* create a runtime error with a given error message */ newRuntimeError(errno, msg) { local e = new RuntimeError(errno); e.exceptionMessage = msg; return e; } /* display the exception */ displayException() { /* show the exception message */ "Runtime error: <>\n"; /* show a stack trace if possible */ showStackTrace(stack_); } /* check to see if it's a debugger signal of some kind */ isDebuggerSignal() { return errno_ is in ( 2391, /* debugger 'abort command' signal */ 2392 /* debugger 'restart' signal */ ); } /* the VM error number of the exception */ errno_ = 0 /* the exception message, provided to us by the VM after creation */ exceptionMessage = '' /* the stack trace, which we store at the time we're created */ stack_ = nil ; /* * Export our RuntimeError class so that the VM knows about it and can * create instances of it. Also export our exceptionMessage property, * so the VM can store its explanatory text there. */ export RuntimeError; export exceptionMessage; /* * Unknown character set exception - this is thrown from any routine that * needs a local character set mapping when no mapping exists on the local * platform. */ class UnknownCharSetException: Exception displayException = "Unknown character set" ; /* * this exception object must be exported for use by the CharacterSet * intrinsic class */ export UnknownCharSetException 'CharacterSet.UnknownCharSetException'; /* * A Program Exception terminates the entire program, passing an error * indication to the operating system. The VM doesn't provide a way to * specify the *particular* error code to return to the OS, as there's no * portable set of error codes; rather, the VM simply returns a code to * the OS that means generically that an error occurred, if there's any * such concept on the local operating system. The VM will normally * display this message just before it terminates the program, possibly * with some additional text mentioning that a program error occurred * (such as "unhandled exception: "). */ class ProgramException: Exception construct(msg) { exceptionMessage = msg.htmlify(); } displayException() { "<> "; } ; /* * A StorageServerError is thrown when a file operation on a remote * storage server fails. The storage server is used when the game runs * on a Web game server in client/server mode. In Web mode, files are * stored on a separate storage server rather than on the Web server * itself, so that the files can be transparently accessed if the game is * continued from another Web server. This exception is used when a * request to the storage server fails, which could be due to an error on * the storage server, a network error communicating between the game * server and the storage server, or an invalid request (e.g., incorrect * user credentials). */ class StorageServerError: RuntimeError construct(errno, msg) { /* * Do the base class construction. Note that errno is the * VM-level error number, which is usually just the generic * "storage server error" code. The storage server provides a * separate, more specific error code of its own as the first * token of the message string. */ inherited(errno); /* * storage server error messages are formatted with an error code * as the first space-delimited token, and a human-readable * message following */ local sp = msg.find(' '); errCode = msg.substr(1, sp - 1); msg = msg.substr(sp + 1); /* * If the error code is a negative integer, it's an error on the * client side sending the HTTP request to the storage server. * If it's a positive integer, it's an HTTP error code from the * storage server. Otherwise it's an error abbreviation token * from the server. */ local errNum = toInteger(errCode); if (errNum < 0) { local reqErrs = [ -1 -> 'out of memory', -2 -> 'unable to connect', -3 -> 'network error', -4 -> 'invalid parameters', -5 -> 'error reading temporary file', -6 -> 'error writing temporary file', * -> 'error code <>' ]; errMsg = reqErrs[errNum]; } else if (errNum > 0) errMsg = 'HTTP error (status code <>)'; else errMsg = msg; } /* the storage server error code */ errCode = nil /* * error message - this is the message text we get back from the * storage server for a request that's successful at the HTTP level * but fails on the storage server, OR a message describing the HTTP * error or network error that caused the request to fail */ errMsg = 'no details available' /* display the exception */ displayException() { /* show the exception message */ "Storage server error: <>\n"; /* show a stack trace if possible */ showStackTrace(stack_); } ; /* export this for use by the interpreter networking package */ export StorageServerError 'StorageServerError'; /* ------------------------------------------------------------------------ */ /* * Default string display function. Our main entrypoint code * establishes this function as the default output function. */ _default_display_fn(str) { _tads_io_say(str); } /* * Raw output capture. This bypasses any filtering and directly captures * any output generated by the callback. */ _outputCapture(func) { /* temporarily set the low-level string output to capture output */ local buf = new StringBuffer(); local oldSay = t3SetSay({str: buf.append(str)}); /* make sure we restore the "say" function on the way out */ try { /* call the callback in the new capture context */ func(); /* return the captured data */ return toString(buf); } finally { /* restore the old "say" function */ t3SetSay(oldSay); } } /* ------------------------------------------------------------------------ */ /* * The stack information object. The intrinsic function * t3GetStackTrace() in the 't3vm' function set returns a list of these * objects; each object represents a level in the stack trace. */ class T3StackInfo: object /* * Construct a stack level object. The system invokes this * constructor with information on the stack level. */ construct(func, obj, prop, selfObj, argList, srcInfo, locals, namedArgs, frameDesc) { /* remember the values */ func_ = func; obj_ = obj; prop_ = prop; self_ = selfObj; argList_ = argList; srcInfo_ = srcInfo; locals_ = locals; namedArgs_ = namedArgs; frameDesc_ = frameDesc; } /* * Is this a system routine? This returns true if an intrinsic * function or an intrinsic class method is running at this level. */ isSystem() { /* * It's a system function if: * * - we have NEITHER a function nor a method *. - the function is a built-in function pointer *. - the defining object is an intrinsic class * * The first case applies to pre-3.0.19 VMs, where no information * was available for native callers. Starting in 3.0.19, full * information is available. */ return ((func_ == nil && obj_ == nil) || dataType(func_) == TypeBifPtr || (obj_ != nil && IntrinsicClass.isIntrinsicClass(obj_))); } /* * the function running at this stack level - this is nil if an * object property is running instead of a function */ func_ = nil /* * The object and property running at this stack level - these are * nil if a function is running instead of an object method. The * object is the object where the method is actually defined - this * might not be the same as self, because the object might have * inherited the method from a base class. */ obj_ = nil prop_ = nil /* * the 'self' object at this level - this is nil if a function is * running at this level instead of an object method */ self_ = nil /* * The list of positional arguments to the function or method. Each * element is the value of an argument; the list is arranged in the * same order as the arguments. */ argList_ = [] /* * Local variables. This is a LookupTable containing the local * variables currently in scope at this stack level. Each element in * the table has a string key (index) giving the name of the local * variable, and each corresponding value is the local's current * value. The table is only included when the stack listing was * produced by a call to t3GetStackTrace() with the T3GetStackLocals * flag set; otherwise it's nil. If the locals were requested, and * the stack level has no local variables, this will be an empty * lookup table. */ locals_ = nil /* * Named arguments. This is a LookupTable containing the named * arguments passed in from this stack level. Each element in the * table has a string key (index) giving the name of the argument, * and each corresponding value is the value of that argument. If * there are no named arguments, this value is nil. */ namedArgs_ = nil /* * The source location of the next code to be executed in the * function or method in this frame. If source-level debugging * information is available for the current execution point in this * frame, this will contain a list of two values: * * srcInfo_[1] = string giving the name of the source file *. srcInfo_[2] = integer giving the line number in the source file * * If the program wasn't compiled with source-level debugging * information, or the current code location in the frame doesn't * have any source information, this will be set to nil. * * Note that the location reflected here is the *return address* in * this frame - that is, the code location that will be executed when * control returns to the frame. This means that the source location * will frequently appear as the next executable line after the one * that called the next inner frame, because this is where execution * will resume when control returns to the frame. */ srcInfo_ = nil /* * A StackFrameDesc object that can be used to get information from * the frame and change local variables in the frame. */ frameDesc_ = nil ; /* export T3StackInfo for use by the system */ export T3StackInfo; /* ------------------------------------------------------------------------ */ /* * Stream state object for String.specialsToHtml(). */ class SpecialsToHtmlState: object /* * Reset the state. This should be used when the output stream * context is reset, such as when clearing the window. */ resetState() { flags_ = 0; tag_ = ''; } /* * Explicitly reset to the start of a line. This can be called after * a non-output operation that resets the line position, such as * reading an input line. */ resetLine() { /* reset the in-line flag, space flag, qspace flag, and tab column */ flags_ &= ~(0x0001 | 0x0040 | 0x0080 | 0x0300); } /* * Internal output state flags at end of last string parsed. This is * a combination of bit flags: * * 0x0001 - last string ended within a line of text *. 0x0002 - caps flag '\^' pending *. 0x0004 - lowercase flag '\v' pending *. 0x0008 - last string ended within an HTML tag *. 0x0010 - last string ended in double-quoted HTML tag attribute text *. 0x0020 - last string ended in single-quoted HTML tag attribute text *. 0x0040 - last string ended with an ordinary space *. 0x0080 - last string ended with a quoted space '\ ' *. 0x0100 - parity level: 0=double quotes, 1=single quotes *. 0x0300 - distance from last '\t' tab column (0..3) */ flags_ = 0 /* tag in progress at end of last string parsed */ tag_ = '' ; /* ------------------------------------------------------------------------ */ /* * global data object for this module */ mainGlobal: object /* flag: we've run pre-initialization */ preinited_ = nil /* * The global reflection object - if the program is compiled with * the standard reflection module, that module will set this * property to point to the reflection object. * * We use this so that we don't require the reflection module to be * included. If the module isn't included, this will be nil, so * we'll know not to use reflection. If this is not nil, we'll know * we can use reflection services. */ reflectionObj = nil /* * Restart ID. This is an integer that indicates how the main * entrypoint was last reached. This is initially zero; each time * we restart the game, this is incremented. * * The restart ID is the only information that survives across a * restart boundary. Other than this, entering via a restart is * exactly like loading the program from scratch; all other * information about the program state before the restart is lost in * the restart operation. */ restartID = 0 /* pointer to mainRestore function, if defined */ mainRestoreFunc = nil ; /* ------------------------------------------------------------------------ */ /* * At-exit handlers. This is a registry for custom handlers that are to * be invoked just before the program terminates. */ transient mainAtExit: object /* * Add an at-exit handler. User code can call this to register a * handler that will be invoked just before the program exits. */ addHandler(func) { handlers.append(func); } /* call our exit handlers */ callHandlers() { /* call each handler in the list */ foreach (local f in handlers) { try { /* call this handler */ f(); } catch (Exception exc) { /* display and ignore any exceptions it throws */ "\nError in exit handler: <>\n"; } } } /* list of exit handlers */ handlers = static new Vector() ; /* ------------------------------------------------------------------------ */ /* * <> index generator. The compiler generates an anonymous * instance of this class for each <> list in string, setting the * property 'numItems' to the number of items in the list, and * 'listAttrs' property to a string giving the sequence type. The * compiler generates a call to getNextIndex() within the string to get * the next index value, which is an integer from 1 to numItems. */ class OneOfIndexGen: object /* number of list items */ numItems = 1 /* * List attributes. This is a string with a comma-delimited list of * tokens describing the treatment on the list for each fetch. The * first call to getNextIndex() takes the first token off the list * and generates an appropriate return value, possibly queuing up a * list of future values. The next call to getNextIndex() reads from * the queue. Once the queue is exhausted, the next call takes the * second token off the attribute list and repeats the process. Once * the attribute list is down to one token, we don't remove the * token, but simply repeat it forever. * * For example, 'seq,rand' runs through the entire list in sequence * once, then generates independently random values from then on. * 'shuffle,stop' runs through the list once in shuffled order, then * repeats the last pick from the shuffled list forever. * * The individual attribute values are: * * rand - pick an item at random, independently of past selections. * * rand-norpt - pick an item at random, but don't pick the single * most recent item chosen, to avoid repeating the same thing twice * in a row. * * rand-wt - pick an item by random, weighting the items with * decreasing probabilities. The last item is given relative weight * 1, the second to last weight 2, the third to last weight 3, etc. * In other words, the nth item from the end of the list is n times * as likely to be picked as the last item. The picks are * independent. * * seq - run through the items in sequence (1, 2, ... numItems). * * shuffle - run through the list in a shuffled order. * * shuffle2 - shuffle the list into a random order, but only run * through half before reshuffling * * stop - repeat the previous selection forever. (This should only * be used as the second or later attribute in the list, since it * depends on a prior selection being made.) */ listAttrs = '' /* * Get the next index value. Returns an integer from 1 to numItems. * The algorithm for choosing the index depends on the list type, as * defined by listAttrs. */ getNextIndex() { /* if we're out of items, generate the next iteration of the list */ if (idx_ > lst_.length()) { /* pull out the first item from the attributes */ local alst = listAttrs.split(',', 2); /* * If we have more than one attribute, keep only the * remaining attributes, since the first attribute is for the * first list iteration only. */ if (alst.length() > 1) listAttrs = alst[2]; /* some of the attributes are interested in the last item */ local last = lst_.length() >= 1 ? lst_[lst_.length()] : nil; /* build the list for the head attribute */ local pick; switch (alst[1]) { case 'stop': /* reuse the last item every time from now on */ lst_ = [last ?? 1]; break; case 'seq': /* run through the list in sequence */ lst_ = List.generate({i: i}, numItems); break; case 'rand': /* * pick an item at random, independent of other picks - * just generate a single item list, since we'll want to * pick at random again next time */ lst_ = [rand(numItems) + 1]; break; case 'rand-norpt': /* * Pick an item at random, but don't repeat the last item * chosen. Pick repeatedly until we select an item that * doesn't match the last selection, if there is one. * Exception: if the list has only one item, it's * impossible to choose a different item, so don't try. */ do { pick = rand(numItems) + 1; } while (numItems > 1 && pick == last); lst_ = [pick]; break; case 'rand-wt': /* * Pick an item at random, independently of other trials, * weighting the list in decreasing order of probability. * The relative weights are 1 for the last item, 2 for * the second to last item, 3 for the third to last, etc. * This gives us a total weight of n(n+1)/2, so start by * picking a number in that range. 1 represents the last * item, 2..3 the second to last, 3..5 the third, etc. */ pick = rand(numItems*(numItems+1)/2) + 1; for (local i in 1..numItems ; ; pick -= i) { /* if pick is <= i, it's this item */ if (pick <= i) { lst_ = [numItems - i + 1]; break; } } break; case 'shuffle': case 'shuffle2': /* * Shuffle the list. Populate the list with the integer * values from 1 to numItems, then shuffle the list into * a random order. * * The shuffling algorithm, as a physical analogy: Start * with a deck of cards labeled 1 to numItems. Put all * of the cards in a hat. Pick a card at random from the * hat and set it aside - call this the "pile". Pick * another random card from the hat and put it on the top * of the pile. Repeat until the hat is empty. We now * have a pile of the cards arranged in random order. * * To implement this, we create a vector and fill it with * integers from 1 to numItems. Partition the vector * with an index 'i': items from 1..i are the hat, items * from i+1..numItems are the deck. To draw a card at * random, we pick a random number from 1..i. To remove * it from the hat, we swap it into position 'i', because * that will effectively shrink the hat by one slot and * grow the pile by one slot. Decrement i and repeat. */ lst_ = Vector.generate({i: i}, numItems); for (local i in numItems..2 step -1) { /* pick a random element from the remaining set */ pick = rand(i) + 1; /* swap the chosen item and the last element */ local tmp = lst_[i]; lst_[i] = lst_[pick]; lst_[pick] = tmp; } /* * The whole point of shuffling is to avoid obvious * repetition, so make sure we don't repeat any previous * item from a previous run through the list as the first * item of the new list. If the first item matches the * previous item, reshuffle it into a random spot in the * list. (If the list only has one item, of course, this * is pointless, so don't bother in that case.) */ if (lst_[1] == last && numItems > 1) { pick = rand(numItems - 1) + 2; lst_[1] = lst_[pick]; lst_[pick] = last; } /* * in half-shuffle mode, only keep the first half of the * list, so that we re-shuffle halfway through */ if (alst[1] == 'shuffle2' && numItems > 2) lst_ = lst_.toList(1, numItems/2); break; } /* start from the first item in the list */ idx_ = 1; } /* return and consume the next item */ return lst_[idx_++]; } /* generated list */ lst_ = [] /* current position in the list */ idx_ = 1 ; frobtads-1.2.3/tads3/lib/gameinfo.t0000644000175000001440000001016610472355543016256 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2001, 2006 Michael J. Roberts. * * Permission is granted to anyone to use this file, including modified * and derived versions, without charge, provided this original copyright * notice is retained. * * This is an add-in utility for tads 3 games to make it simpler to add * game information to a compiled game. * * The game information mechanism lets you bind certain documentary * information about the game directly into the compiled (.t3) file. The * information can then be extracted by automated tools; for example, * archive maintainers can extract the game information and use it to * describe the archive entry. */ /* * To use this module, a game must simply call our function * writeGameInfo(), passing in a lookup table and a destination filename. * * The lookup table consists of one entry per game information item. For * each entry, the key is the name of the item, and the associated value * is a string giving the text of the item. * * The destination filename should usually be "GameInfo.txt", since * that's the name of the game information resource that must be bound * into the .t3 file. However, the name isn't actually important until * the resource compiler (t3res) is invoked, at which point an arbitrary * filename can be mapped to the required resource name if desired. Note * that we'll overwrite any existing file with the given name. * * writeGameInfo() will throw a FileException if an error occurs writing * the data to the file. * * writeGameInfo() should be called during pre-initialization so that the * game information is generated immediately after compilation is * finished. Here's an example of how you might do this: * * PreinitObject *. execute() *. { *. local tab = new LookupTable(); *. *. tab['Name'] = 'My Test Game'; *. tab['Author'] = 'Bob I. Fiction'; *. tab['Desc'] = 'My simple test game, just to demonstrate how *. to write game information.'; *. *. writeGameInfo(tab, 'GameInfo.txt'); *. } *. ; * * After pre-initialization finishes, you must finish the job by running * the resource compiler, with a command line something like this: * * t3res mygame.t3 -add GameInfo.txt * * If you didn't call the output file GameInfo.txt, you can map the * filename to the proper resource name with a command like this: * * t3res mygame.t3 -add outfile.xyz=GameInfo.txt */ #include #include /* TADS GameInfo writer */ gameInfoWriter: object /* * Write the game information from the given LookupTable to the given * file. Each key/value pair in the LookupTable gives the GameInfo * key and the corresponding value string for that key. */ writeGameInfo(tab, fname) { local f; /* * open the file - note that the GameInfo.txt resource is * required to be encoded in UTF-8, so open the file with the * UTF-8 character set */ f = File.openTextFile(fname, FileAccessWrite, 'utf-8'); /* write each entry in the table */ tab.forEachAssoc({key, val: f.writeFile(key + ': ' + val + '\n')}); /* done with the file - close it */ f.closeFile(); } /* * Get today's date as a string in the format YYYY-MM-DD. This can * be used as a simple way of keeping the release date in the game * information up to date with the latest compilation. */ getGameInfoToday() { local dt; local mm, dd; /* get the current date */ dt = getTime(GetTimeDateAndTime); /* get the month, and add a leading zero if it's only one digit */ mm = (dt[2] < 10 ? '0' : '') + dt[2]; /* get the day, and add a leading zero if it's only one digit */ dd = (dt[3] < 10 ? '0' : '') + dt[3]; /* build and return the full YYYY-MM-DD date string */ return toString(dt[1]) + '-' + mm + '-' + dd; } ; frobtads-1.2.3/tads3/lib/gramprod.t0000644000175000001440000001446311464244765016315 0ustar realncusers#charset "us-ascii" /* * Copyright (c) 2005, 2006 Michael J. Roberts * * This file is part of TADS 3. * * This module defines some classes used by the GrammarProd intrinsic * class. */ #include #include /* ------------------------------------------------------------------------ */ /* * GrammarProd descriptor classes. The GrammarProd intrinsic class's * getGrammarInfo() method uses these classes to build its description of * a grammar production. */ /* export the classes so the intrinsic class can find them */ export GrammarAltInfo 'GrammarProd.GrammarAltInfo'; export GrammarAltTokInfo 'GrammarProd.GrammarAltTokInfo'; /* * Rule alternative descriptor. This describes one alternative in a * grammar production. An alternative is one complete list of matchable * tokens. * * In a 'grammar' statement, alternatives are delimited by '|' symbols at * the top level. Each group of tokens set off by '|' symbols is one * alternative. * * When '|' symbols are grouped with parentheses in a 'grammar' * statement, the compiler "flattens" the grouping by expanding out the * parenthesized groups until it has entirely top-level alternatives. * So, at the level of a GrammarProd object, there's no such thing as * parentheses or nested '|' symbols. */ class GrammarAltInfo: object /* * Constructor. GrammarProd.getGrammarInfo() calls this once for * each alternative making up the production, passing in the values * that define the alternative. Note that we have a '...' in our * argument list so that we'll be compatible with any future * GrammarProd versions that add additional arguments - we won't do * anything with the extra arguments, but we'll harmlessly ignore * them, so code compiled with this library version will continue to * work correctly. */ construct(score, badness, matchObj, toks, ...) { /* stash away the information */ gramBadness = badness; gramMatchObj = matchObj; gramTokens = toks; } /* * The 'badness' value associated with the alternative. A value of * zero means that there's no badness. */ gramBadness = 0 /* * the "match object" class - this is the class that * GrammarProd.parseTokens() instantiates to represent a match to * this alternative in the match list that the method returns */ gramMatchObj = nil /* * The token descriptor list. This is a list of zero or more * GrammarAltTokInfo objects describing the tokens making up this * rule. */ gramTokens = [] ; /* * Grammar rule token descriptor. GrammarProd.getGrammarInfo() * instantiates one of these objects to represent each token slot in an * alternative; a GrammarAltInfo object's gramTokens property has a list * of these objects. */ class GrammarAltTokInfo: object /* * Constructor. GrammarProd.getGrammarInfo() calls this once for * each token in each alternative in the production, passing in * values to fully describe the token slot: the target property (in a * 'grammar' statement, this is the property after a '->' symbol); * the token type; and extra information that depends on the token * type. Note that we use '...' at the end of the argument list so * that we'll be compatible with any future changes to GrammarProd * that add more arguments to this method. */ construct(prop, typ, info, ...) { /* remember the information */ gramTargetProp = prop; gramTokenType = typ; gramTokenInfo = info; } /* * The target property - this is the property of the *match object* * that will store the match information for the token. In a * 'grammar' statement, this is the property after the '->' symbol * for this token. */ gramTargetProp = nil /* * The token type. This is one of the GramTokTypeXxx values (see * gramprod.h) indicating what kind of token slot this is. */ gramTokenType = nil /* * Detailed information for the token slot, which depends on the * token type: * * GramTokTypeProd - this gives the GrammarProd object defining the * sub-production that this token slot matches * * GramTokTypeSpeech - this is the property ID giving the * part-of-speech property that this token slot matches * * GramTokTypeNSpeech - this is a list of property IDs giving the * part-of-speech properties that this token slot matches * * GramTokTypeLiteral - this is a string giving the literal that this * slot matches * * GramTokTypeTokEnum - this is the enum value giving the token type * that this slot matches * * GramTokTypeStar - no extra information (the value will be nil) */ gramTokenInfo = nil ; /* * Dynamic match object interface. This is a mix-in class that should be * used as a superclass for any class used as the match object when * creating new alternatives dynamically with GrammarProd.addAlt(). * * This class provides an implementation of grammarInfo() that works like * the version the compiler generates for static match objects. In this * case, we use the grammarAltProps information that addAlt() stores in * the match object. */ class DynamicProd: object /* * Generate match information. This returns the same information * that grammarInfo() returns for match objects that the compiler * generates for static 'grammar' statements. */ grammarInfo() { return [grammarTag] + grammarAltProps.mapAll({ p: self.(p) }); } /* * grammarTag - the name for the collection of alternatives * associated with the match object. This name is primarily for * debugging purposes; it appears as the first element of the * grammarInfo() result list. */ grammarTag = 'new-alt' /* * grammarAltProps - the list of "->" properties used in all of the * alternatives associated with this match object. addAlts() stores * this list automatically - there's no need to create it manually. */ grammarAltProps = [] ; frobtads-1.2.3/tads3/lib/webuires/0000755000175000001440000000000012145614112016112 5ustar realncusersfrobtads-1.2.3/tads3/lib/webuires/debuglog.js0000644000175000001440000000130711426534526020254 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Debug log window scripts for TADS 3 Web UI. */ function debugLogInit() { // initialize the utilities package utilInit(); // tell our parent window that we're done loading if (window.opener) window.opener.debugLogLoaded.fire(); } function debugLogPrint(msg) { // add the message to the debug log division var d = $("debugLogDiv"); var s = document.createElement("SPAN"); s.innerHTML = msg; d.appendChild(s); // scroll the window to the bottom var ht = d.offsetHeight; var wrc = getWindowRect(); var y = ht - wrc.height; if (y > 0) window.scrollTo(0, y); } frobtads-1.2.3/tads3/lib/webuires/statwin.js0000644000175000001440000001014011500434216020134 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Status line scripts for TADS 3 Web UI. */ function statLineInit() { // initialize the utility package utilInit(); // ask our parent to recalculate the window layout now that we're here parent.calcLayout(); // set up the default document-level key handler - this will send focus // back to the main command window if the user presses a key while // focus is in our window (and not in a key-consuming control) addKeyHandler(document, genericDocKeyFocus); // get the initial state getInitState(); // load preference styles $win().prefsToCSS(null, window); } function onGameState(req, resp) { // set the text from the status setStatusText(xmlChildText(resp, "text")); // recalculate the parent window layout, in case the layout is sensitive // to our content size parent.calcLayout(); } function onGameEvent(req, resp) { if (xmlHasChild(resp, "text")) setStatusText(xmlChildText(resp, "text")); if (xmlHasChild(resp, "appendText")) appendStatusText(xmlChildText(resp, "appendText")); if (xmlHasChild(resp, "resize")) parent.calcLayout(); if (xmlHasChild(resp, "setsize")) parent.setWinLayout(this, xmlChildText(resp, "setsize")); } var statText = ""; function setStatusText(msg) { msg = BrowserInfo.adjustText(msg); statText = msg; $("statusMain").innerHTML = msg; } function appendStatusText(msg) { msg = BrowserInfo.adjustText(msg); statText += msg; $("statusMain").innerHTML = statText; } /* ------------------------------------------------------------------------ */ /* * Network waiting indicator ("throbber"). We show this as part of the * status line because the throbber is basically a status display, so (a) * it's logical to group with the status line in the abstract, and (b) in * concrete terms, the status line is usually a good visual location for * this because it's prominent but out of the way of the input area of the * window. */ var NetThrobber = { // number of open network requests netRequestCount: 0, // timeout for starting the visual indicator, if one is pending starter: null, // start a network request onStartRequest: function() { // Count the request; if it's the first one, set a timer to // start the throbber. Note that we don't simply start it // immediately: if the request completes quickly, it's better // not to show the throbber at all, since the quick on-off // flash would be annoying. We only need the throbber when // the request takes a noticeable amount of time to complete. // So, wait until a few moments before starting it; if the // request completes before the wait expires, we'll skip the // whole thing and avoid any flashing. If the request turns // out to take longer than the timeout, it's a long enough wait // that we want visual feedback that something's going on. if (this.netRequestCount++ == 0 && !this.starter) { this.starter = setTimeout(function() { NetThrobber.starter = null; NetThrobber.show(); }, 500); } }, // end a network request onEndRequest: function() { // Decrement the open request count. If this was the last open // request, turn off the wait indicator. if (--this.netRequestCount == 0) this.hide(); }, // show the throbber show: function() { $("netThrobberDiv").style.display = "block"; $win().status = "Waiting for the game to respond..."; }, // hide the throbber hide: function() { // if we have a timer waiting to show the throbber, simply kill // the timer; otherwise hide the display if (this.starter) { clearTimeout(this.starter); this.starter = null; } else { $("netThrobberDiv").style.display = "none"; $win().status = ""; } } }; frobtads-1.2.3/tads3/lib/webuires/dlg-title-corners.gif0000644000175000001440000000212311422027676022147 0ustar realncusersGIF89a°‡@@ÿ!ù,°ÿ80€Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI3!Á›8ÔÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´iÅœ7JJµªÕ«X³jÝʵ«×¯;¡‚K¶¬Ù³hÓª]˶­Û9ßÊK·®Ý»xóêÝK5*ß¿€ L¸°áÃR "^̸±ãÇ#Kž+p²å˘3kÞ̹óÅÊžC‹Mº´éÓ]u¢^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀMªN¼¸ñãÈ“+­¼¹óçУ‡f.½ºõëØ³ÓU¬½»÷ïàÃ#0õ+¾¼ùóèÓwÄ©¾½û÷ðËC¿¾ýûøyϧž¿¿ÿÿn¶{hàæµ_@;frobtads-1.2.3/tads3/lib/webuires/statwin.css0000644000175000001440000000371311502453117020323 0ustar realncusers/* * TADS Web UI - style sheet for statusline window. */ html { margin: 0px; border: 0px; padding: 0px; overflow: hidden; } body.statusline { margin: 0px; padding: 0px; overflow: hidden; border: 0px; background: #C0C0C0; /* preference setting */ color: #000000; /* preference setting */ } /* Because we override links in this division, we need to re-override "plain" style links as well - otherwise the "body.statusline a:link" selector would override the base "a.plain:link" selector due to the greater specificity of the former. We thus need to provide at least an equally specific a.plain rule in this context. */ body.statusline a.plain:link { color: inherit; color: expression(this.parentNode.currentStyle ? this.parentNode.currentStyle.color : "normal"); } body.statusline a:link { color: #0000ff; /* preference setting */ } body.statusline a:hover, body.statusline a.plain:hover { color: #ff00ff; /* preference setting */ } body.statusline a:active, body.statusline a.plain:active, body.statusline a:focus, body.statusline a.plain:focus { color: #ff0000; /* preference setting */ } div.statusline { width: 100%; padding: 0px; margin: 0px; position: relative; } div#statusMain { padding: 0px 8px; margin: 0; border-bottom: 1px solid black; } div.statusleft { display: inline; float: left; padding: 0.5ex 0px; } div.statusright { display: inline; padding: 0.5ex 0px; float: right; } div.statusStrut { clear: both; height: 0px; } div#statusMenu { display: inline; float: right; height: 100%; } div#statusMenu img { border: none; padding: 0.5ex 8px; vertical-align: middle; } div#netThrobberDiv { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; background: url("netspinner.gif") transparent no-repeat center right; display: none; } frobtads-1.2.3/tads3/lib/webuires/errorPopupBkg.gif0000644000175000001440000000213011424355210021376 0ustar realncusersGIF89ad‡“l’jžt”r–x%»]£v¼f¼f¹cº`ºb»d»d»f¾a¼d¼eÿÿÿ þýýÿú&û&ü+ô0ø5ÿ ö?"ú>(ÿ<<ÇRÌ]%Åg*Ær"Ät*Æt)Òc)Ðl.Ýi>Út?ÿLLÿTTàKëpSèUõcU›‚@žŠIª’N®™]܀݄؉Ù—Ú™àîâí”ï—í˜íœïžòŸðŸá¢â£î£ï®ò§ ð¦ ó¨ ñª òª ò­ ñ£ò£ò¤ñ²ó´/ô»!Ì‘GÔ–_Ô¡dݯ}í€fô‰zá¤Fé²Pé½iæ¾zõÅ6úÌOòËdõÓpÞ¯Œâ¼˜îĉÿÿÿ!ùh,dÿшæŒÁƒ,x¦E… #Jœ@ðŒˆ‰[dL„8ðL†‰ÊÁ°Qà™‡µ˜ù£ä™/d#æÊ†‰g\lœË\VàÜÉK3¨œÐ ñÌÎ]À  $GЦCX©2eÀ%E"xˆèT">ž$9bÉ$(@,„‰&N˜@¤GæNTÁC ”(;tòãÆÁQ<@@B ,0PòÎÏžAï¤+z#éÒXQ›V½šujס]Ÿf=[umÔ·Kç½tïÏ¿GÃ&;<6íâ‚·®|hñæ¯aC'þùtã¶­k¯ÎùvïÝ¥5^öxóå§Ï¾÷yõàã“—ž>üùøë翯¿?ÿÿìÙà~úW €îµ§Û{6ˆZ@;frobtads-1.2.3/tads3/lib/webuires/debuglog.css0000644000175000001440000000024611422367335020427 0ustar realncusersbody { font: 9pt/12pt Verdana, Arial, Helvetica, Sans-serif; background: #0000ff; color: #ffffff; } div#debugLogDiv { } a:link { color: #ffa0ff; } frobtads-1.2.3/tads3/lib/webuires/debuglog.htm0000644000175000001440000000071211502452611020414 0ustar realncusers TADS Debug Log
    frobtads-1.2.3/tads3/lib/webuires/netspinner.gif0000644000175000001440000000577311420145344021003 0ustar realncusersGIF89a÷Ã{)Œ{BŒ{ZŒ„Œ„”„)”„1”„9Œ„9”„B”„R”„ZŒ„Z”„c”ŒœŒ!”Œ)”Œ)œŒ1”Œ1œŒ9”Œ9œŒB”ŒBœŒJ”ŒJœŒR”ŒRœŒZ”ŒZœŒc”ŒcœŒkœ”””!œ”1œ”9œ”9¥”Bœ”B¥”Jœ”J¥”Rœ”R¥”Zœ”Z¥”cœ”c¥”kœ”k¥”s¥œ¥œ!¥œ)¥œ1¥œ9¥œB¥œJ¥œJ­œR¥œZ¥œZ­œc¥œc­œk¥œk­œs¥œs­œ{¥œ{­œ„¥¥­¥!­¥)­¥1¥¥1­¥9­¥B­¥J¥¥J­¥R­¥Z­¥c¥¥c­¥k¥¥k­¥s­¥sµ¥{­¥„­¥„µ¥Œ­¥”µ­­­!µ­J­­Rµ­Zµ­cµ­kµ­s­­sµ­{­­{µ­„µ­„½­Œµ­”µ­”½­œµ­¥µµ!µµ9µµB½µJ½µRµµk½µs½µ{µµ{½µ„½µŒµµŒ½µ”½µœ½µ¥½µ­½½½½„½½„ƽŒ½½ŒÆ½”½½”ƽœ½½œÆ½œÎ½¥½½¥Æ½­½½­Æ½µÆÆkÆÆsÎÆ„ÆÆŒÆÆŒÎÆ”ÆÆ”ÎÆœÆÆœÎÆ¥ÆÆ¥ÎÆ­ÆÆ­ÎÆµÆÆµÎÆ½ÆÆ½ÎΜÎΜÖÎ¥ÎÎ¥ÖέÎέÖεÎεÖνÎνÖÎÆÎÎÆÖÎÎÖÖ¥ÖÖ­ÖÖµÖÖ½ÖÖ½ÞÖÆÖÖÆÞÖÎÖÖÎÞÞ­ÞÞµÞÞ½ÞÞÆÞÞÎÞÞÖÞÞÖçÞÞÞÞÞççÆÞçÎÞçÖççÞçïççèæçèæç!ÿ NETSCAPE2.0!ù Ã,¨‡ 8ÌJ‚‡¥ª‘j؈ ΚñÄᄈ g ;ÓEÑCŒL¼ØÑE£†ˆU…ù³¤…,†0 ÌÔ„/™zĈ™"fÌa{Êœ±3†A`ƒ ôtÏÍaÁpéB:0Ö¡[—Ô0ʤé•(\ÁrQUJ‘.Oœ0…z%+j.\Uiܤ+¦Vªt½6• .Uœ†i 5Œ­®„±<ÅLX—©\?K™Š!ù Ã,°‡ 8슂‡©Ú‘jØ Ã<‘hˆP×’(I Cá`AU±†éylj>3€ <#®&8n˜€@€ÂTgM‰Q"‹šl dTŠÍaÁ*Òsˆž>B*ª4èÐ!CQžÚt(©@¯geÒµi&M¯rKÈ(U¦^šB™²…ëWÂXº†aÊÕê•­\»ì&Ôe*Ô0W¶†ávà¬R²†½ª5L×⬒a !ù Ã,¬‡ 8L‚‡Í*sjX(ÃR™ð”Vƒ`x ûAcAU×ÃRe9•P—&C¢¡‹`.ZÃ.ÑÚ©O¦@hRf‰Ô¥]™âcfJ®5MxÄe ¦;|Ôð"G*ž¶²T)˜À`(z@øJÖ+W°ZÜ’#ljEnµ²eË•-‚jR¨È±#Ë@`¸~ v€ÂÔ§¸œ"äS€‹¤ÃrÓ‹–$žN8,`Æ#Kž,0 !ù Ã,§‡ 8,“!‚‡å:k§EÃb©I•0©BÃ.1Æ'“*‚ºt Ô‹Ó¥EœLí6ëã°V¸ZõÂTé’>zr©!D ®Zº\IRTˆOV ñD—-RÁòòa¥ÏÀ\¿°âºEPÍŽ9td–pàh©d)+0XVÙú €‚ Ù; A× äëCƒËN‚‡q€à2q* ˆ— !ù Ã,£‡ 8l&‚‡éb•k(MÃbJ•.VÃBAA‡âÔL„Áb…ÑÎ> ¶¬A $,ª R¨‚Ç(³¸Ô±‚pK7+v,š³fX,Ogè0,î0+OL"ô„aSßÃN½à”4 Š!ù Ã,§‡ 8,X.‚6L«ƒ âæê–®ˆq#ÕFƒ¶\éÂDªÒÇ[£$YÒu(FÁ.)Òô’à¡J…&UR„pÁ=‰ùaÄg` ‚ÁÌÂfÁTn X`K›2ÖŒÚãrX‡Df©È°c‡+ºöR#°Qªa[šôá±¢O ¶³<œe‚Å0<†)ã!'zyìv ˆÞ—[°D !ù Ã,¥‡ H° AƒÁ‚<Lá q Óõë¡À\Álájeqã­\¤$> Öª•«Y:pàСÁPŸ,…¢„‰H6d!ˆI‘&Lš. ì£"€C^y•"˜*F ¨Qc%H¢X‡b ü@"‹.<~XÙ³GמL{nR5lK˜DV¬$2ÅgجSgA2¬ –ajÍ2ÈÉ…§aq‡2³ô¡5!ù Ã,§‡ H° Áƒô4Ka°*áeða0];yh’ÀMÁ`Áp=ØFCŠáÂ(0K‡,V!h —M\®Œ PÁ3 ÁV¤Z±jåj` FdÉÁT@ÂäcšŽ bƒ=¨±£ ‘) j¨0 à*nèÂ#Š2L &Í ZÀ¸1#Æ (HxºÆL„ ,PA ”Qˆ°x;frobtads-1.2.3/tads3/lib/webuires/tads.css0000644000175000001440000000514711424614623017574 0ustar realncusers/* * TADS Web UI - global style sheet. */ body { line-height: 140%; /* the following can be changed by the player via the preference settings mechanism */ font-family: Georgia, Times New Roman, Times, New York, serif; font-size: 10pt; color: #000000; background-color: #ffffff; } /* generic font styles for the preferences mechanism */ .serifFont { font-family: Georgia, Times New Roman, Times, New York, serif; } .sansFont { font-family: Verdana, Arial, Helvetica, sans-serif; } .scriptFont { font-family: Comic Sans MS, Zapf Chancery, ZapfChancery, cursive; } .ttFont { font-family: Courier New, Courier, monospace; } tt { font-family: Courier New, Courier, monospace; } /* note - the extra .plain selectors are to increase the specificity of this item, so that it overrides other regular A items we might define later */ a.plain:link { text-decoration: none; color: inherit; /* IE hack - works around lack of support for 'inherit' */ color: expression(this.parentNode.currentStyle ? this.parentNode.currentStyle.color : "normal"); } a:link { /* color: preference setting */ color: #0000ff; text-decoration: underline; } a:hover, a.plain:hover { /* color: preference setting */ color: #ff00ff; text-decoration: underline; } a:active, a.plain:active, a:focus, a.plain:focus { /* color: preference setting */ color: #ff0000; text-decoration: underline; } div.combobox { display: inline-block; zoom: 1; * display: inline; border: 1px solid #0000ff; background: #c0e0e0; margin: 0px; vertical-align: middle; } div.combobox input { margin: 1px; padding: 2px; vertical-align: middle; border: none; background: #ffffff; color: #000000; } div.combobox img { vertical-align: middle; margin: 0px; border: none; } select.comboPopup { position: absolute; z-index: 19900; display: none; padding: 0px; margin: 0px; } /* * replacement for
    - this looks like a horizontal rule, but works * an IE bug. IE uses the wrong containing box to calculate the auto * width of a real
    when used in an inline-block box. IE goes up * to the parent block box, bypassing the inline-block box. This makes * an embedded HR expand the inline-block box to the full width of the * parent, which is often not what we want. Using the
    avoids * the bug and looks basically the same. To use, simply write *
    in place of a regular
    element. */ div.hr { border-top: 1px solid #808080; margin: 1ex 0px; } frobtads-1.2.3/tads3/lib/webuires/dlgIconWarning.gif0000644000175000001440000000277211445204665021530 0ustar realncusersGIF89a((‡  !0.3062!!!!!"""#$$ %%%%%&&&&%&(&&(((****,,),,-...0.$0/*30"10'11-75-96&002222335431763555668987888;;:;;<<<ëØ4íØ0ìÙ3íÚ3îÚ1íÙ5ìÙ7êØ9êØ>ÚÎ\ÞÐTÞÑ^ØÍkÛÏhÚÏoÖÌqÒÊzÒÌ{×ÍxÕÌÙÎqÝÐfÞÑdåÖCäÔEçÖEãÔKæÕKæÖIè×BèÖEãÓPãÔTàÒ]¢¢¢ªªª¯¯¯°°°²²²´´´µµ¶¶¶¶¿¾¶¸¸¸¸¸¹¹¹¹¹¹ºººººº»»»»¹¹¼º»¼¾½¸¿¿»¼¼¼½½¾¾¾½¾¾¾¼½À½¾À¾¾À¾¿Â¿Àþ¦Ä½¢Ä¾¢Â¾­À¿¸À¿¼ÊÂËÆŽÎÈ„ÇØÆÁœÈÖÉŘÍǘÌÇœÎÈ“ÔËÐÉŠÑÊŠÅÀ£ÇàÃÁ®Ã®Å¨ÆÃ¬ÆÄ¬ÈÅ¥ÃÁ°ÂÁ¶Ã·Ä³ÁÀ¹ÁÁ»ÂÁºÀÀ¼ÀÀ¾ÂÁ½Â¾ÄÿÀÀÀÀÀÁÁÁÀÁÁÁÀÀÃÃÃÁÂÂÂÃÃÃÂÃÄÃÃÆÄÄÃÄÄÄÅÅÆÅÅÇÆÆÄÆÆÆÈÈÈÉÉÉÊÊÊÌÌÌÍÍÍÎÎÎÐÐÐÒÒÒÔÔÔÿÿÿÿÿÿ!ùÿ,((ÿÿ H°à@våʱ3Ȱ¡Ã‚öÐ%Œ§ê¡Å‹ÿ&TÇÆù­+—-¡/µH;frobtads-1.2.3/tads3/lib/webuires/main.css0000644000175000001440000002261611753303600017560 0ustar realncusers/* * TADS Web UI - style sheet for the main, top-level window. The main * window is mostly a frame container for subwindow widgets and doesn't * show much content of its own, except that the main window handles * our in-window dialog framework and has a special widget for error * notification. */ body { width: 100%; height: 100%; margin: 0px; padding: 0px; overflow: hidden; } html { overflow: hidden; height: 100%; } iframe { margin: 0px; padding: 0px; height: 100%; width: 100%; position: absolute; border: 0px; } div#errorPopupDiv { position: absolute; top: -1000px; /* to initially hide it */ z-index: 10000; height: 12pt; width: 45ex; color: #ffffff; text-align: center; margin: 0; padding: 0; font: bold 8pt/12pt Verdana, Helvetica, Sans-Serif; } div#errorPopupDiv div.icon { background: url("errorPopupBkg.gif"); position: absolute; left: 0px; top: 0px; width: 20px; height: 100%; margin: 0px; } div#errorPopupDiv div.content { background: #ff0000; color: #ffffff; margin: 0px 0px 0px 20px; height: 100%; font: bold 8pt/12pt Verdana, Helvetica, Sans-Serif; border-top: 1px solid #ffffff; } div#errorPopupDiv a { color: #ffffff; } div#downloadPopupDiv { position: absolute; top: -1000px; /* to initially hide it */ z-index: 10000; width: 45ex; color: #ffffff; text-align: left; margin: 0; padding: 0; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; } div#downloadPopupDiv div.icon { background: url("downloadPopupBkg.gif"); position: absolute; left: 0px; top: 0px; width: 10px; margin: 0px; height: 100%; } div#downloadPopupDiv div.content { background: #6000e0; color: #ffffff; margin: 0px 0px 0px 10px; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; border-top: 1px solid #ffffff; } div#downloadPopupDiv div.dlfile { padding-left: 2ex; } div#downloadPopupDiv div.dlfile a:link { color: #ffffff; } div#downloadPopupDiv div.dlfile a:visited { color: #ffffff; } div.dialogCoverDiv { position: fixed; z-index: 10100; top: 0px; left: 0px; width: 100%; height: 100%; background: url("modal-cover.png"); opacity: 0.70; filter: alpha(opacity=70); } div.dialogCanvas { position: fixed; z-index: 10101; top: 0px; left: 0px; width: 100%; height: 100%; overflow: auto; opacity: 0.00; } div.dialogDiv { position: relative; z-index: 10101; background: transparent; width: 70%; } div.dialogTitleBar { position: absolute; top: -12pt; left: 0px; width: 100%; height: 12pt; background: #4040ff; border-bottom: 1px solid #9090ff; cursor: move; } div.dialogTitle { position: absolute; left: 0px; top: -5px; width: 100%; height: 12pt; text-align: center; color: #ffffff; font: bold 9pt/12pt Verdana, Helvetica, Sans-Serif; cursor: move; } div.dialogCloseBox { position: absolute; right: 10px; top: -5px; width: 16px; height: 16px; cursor: pointer; background: url("dlg-closebox.gif") no-repeat top right; } div.dialogCloseBoxH { background: url("dlg-closeboxH.gif") no-repeat top right; } div.dialogCloseBoxA { background: url("dlg-closeboxA.gif") no-repeat top right; } div.dialogMoveCover { display: none; position: absolute; z-index: 12000; } div.dialogDiv-tl { position: absolute; background: url("dlg-title-corners.gif") no-repeat top left; left: 0px; top: -10px; width: 52%; height: 10px; } div.dialogDiv-tr { position: absolute; background: url("dlg-title-corners.gif") no-repeat top right; right: 0px; top: -10px; width: 52%; height: 10px; } div.dialogDiv-bl { position: absolute; background: url("dlg-corners.gif") no-repeat bottom left; left: 0px; bottom: -10px; width: 52%; height: 10px; } div.dialogDiv-br { position: absolute; background: url("dlg-corners.gif") no-repeat bottom right; right: 0px; bottom: -10px; width: 52%; height: 10px; } div.dialogWrapper { text-align: center; /* IE hack - center the dialogContent block */ background: #ffffff; overflow: auto; } div.dialogContent { color: #000000; font: 9pt/11pt Verdana, Helvetica, Sans-Serif; /* the real CSS way to center the dialog content */ margin: 0px auto; /* automatic left/right margins */ display: inline-block; /* use the content width as the DIV width */ /* ...but, of course, IE has to do things its own bizarre way */ zoom: 1; /* force the block to have an IE "layout" object */ * display: inline; /* "layout" makes this work like inline-block */ text-align: left; /* uninherit the IE centering hack in the wrapper */ } div.dialogContent a { color: #0000ff; } div.dialogContent a:hover { color: #ff00ff; } div.dialogContent a:active { color: #ff0000; } div.dialogMarginDiv { margin: 1em 2em 1ex 2em; } div.dialogZeroMarginDiv { margin: 0px; } div.dialogButtons { text-align: center; margin: 1.5em 1em 1ex 1em; clear: both; } div#debugLogDiv { position: absolute; right: 0px; top: 50px; z-index: 11000; width: 35%; height: 75%; overflow: auto; background: #ff8000; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; color: #ffffff; display: none; } div.errorListDiv { margin: 1em; overflow: auto; border: 1px inset #c0c0ff; background: #f0f0f0; padding: 1em; max-height: 20em; } div.dialogContent span.details { font-size: 90%; } div.errorFooter { font-size: 85%; color: #303030; margin-top: 1ex; } div.errorDetail { font-size: 85%; color: #303030; display: none; } div.errorDetail div.errorSubDetail { padding: 0 1em 0 2em; } div.dialogContent a.colorbutton { height: 1.5em; width: 3em; background: #ff00ff; border: 1px solid #0000ff; display: table-cell; display: inline-block; margin: 0.75em 0.75ex 0.75em 0.75ex; vertical-align: middle; color: #ffffff; text-decoration: none; text-align: center; } div.prefsSection { margin: 1ex 0 1ex 0; } div.prefsSection select { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } div.prefsSection input { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } div.prefsSection div.combobox { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } span.prefsLabel { width: 30ex; display: table-cell; display: inline-block; } a.dlgButton { margin: 0 1ex 0 1ex; } div.colorPicker { position: absolute; z-index: 19900; display: none; background: #000080; color: #ffffff; font: 8pt/10pt Verdana, Helvetica, Sans-Serif; border: 1px solid #808080; } div.colorPicker input { border: 1px solid #303030; font: 8pt/10pt Verdana, Helvetica, Sans-Serif; color: #000000; background: #c0c0ff; } a.colorPickerButton { display: table-cell; display: inline-block; width: 2ex; height: 2ex; margin: 2px; border: 2px inset gray; cursor: pointer; } div.colorPickerDefault { text-align: center; width: 100%; } div.colorPickerDefault div.off { padding: 0px 1ex; display: inline-block; zoom: 1; * display: inline; } div.colorPickerDefault div.on { padding: 0px 1ex; display: inline-block; zoom: 1; * display: inline; background: #0080ff; } div.colorPickerDefault a { color: #ffffff; text-decoration: none; border: none; } div.colorPickerDefault img { border: none; vertical-align: middle; } div.inputDialog { text-align: center; margin: 1em; padding: 1em; } table.inputDialogTab { width: 100%; border: none; margin: 0px; } table.inputDialogTab tr { vertical-align: top; height: 40px; } td.inputDialogPrompt { vertical-align: middle; text-align: center; padding: 0px 60px; } img.inputDialogIcon { position: absolute; } img.inputDialogStrut { height: 40px; width: 0px; } iframe.downloadFile { position: absolute; width: 1px; height: 1px; left: -100px; top: -100px; } div.menuSys { padding: 3em 0px; text-align: center; } div.menuSysItem { padding: 1ex 0px; } div.menuSysTopicFooter { padding: 1em 0px 0px 0px; text-align: center; } div.menuSysItem a:link { text-decoration: none; } div.menuSysItem a:active { text-decoration: underline; } div.menuSysItem a:hover { text-decoration: underline; } div.menuSysLongTopic { overflow-y: auto; overflow-x: hidden; margin: 2em 0px 1em 0px; text-align: left; padding-right: 24px; /* hack for IE scrollbar overlap */ } div.menuSysChapterButtons { } div.menuSysChapterButtons a { padding: 0px 1.5em; } div#pageMenuDiv { position: absolute; z-index: 20000; display: none; background: #ffffff; padding: 0px; margin: 0px; border: 0px; } div.pageMenuWrapper { color: #00000; font: bold 8pt/16pt Verdana, Helvetica, Sans-Serif; border: 1px solid #c0c0c0; padding: 6px 1.5em; } div#pageMenuDiv a { text-decoration: none; color: #0000ff; } div#pageMenuDiv a:hover { color: #ff0000; } div.menuShadow { position: absolute; z-index: 19999; background: #b0b0b0; opacity: 0.60; filter: alpha(opacity=60); } div#__TADS_swf_div { position: absolute; left: -10000px; top: -10000px; } frobtads-1.2.3/tads3/lib/webuires/dlg-closeboxH.gif0000644000175000001440000000200411440012233021260 0ustar realncusersGIF89a‡ƒ¯†²ˆ´‰¶ЏŒº¸À‘Á’ÔÀ”ŕǘ˚͛Ϭߥã ë¡í¢ï©á­ç¥ò¦ô©øªú¬þ¶ôºò¸ö¹ø¼ü®ÿ°ÿ¸ÿ¹ÿºÿ»ÿ#³ÿ(µÿÀÿÀÿÄÿÅÿÀÿÁÿÁÿÂÿÇÿ!Âÿ#Ãÿ$Ãÿ&Äÿ!Èÿ#Èÿ*Àÿ*Åÿ,Æÿ.Æÿ,Ëÿ.Ëÿ2Âÿ2Çÿ7Äÿ0Ìÿ3Íÿ7Éÿ5Íÿ9Îÿ;Ïÿ=Ïÿ?ÐÿAÐÿDÐÿFÑÿHÑÿJÒÿLÒÿNÓÿL×ÿRÔÿUÕÿWÕÿYÖÿ]×ÿ_×ÿbØÿhÖÿjÖÿlÚÿpØÿrÙÿ}Üÿßÿ„ÞÿˆßÿŒâÿàÿ“áÿ¿ïÿÂïÿÆïÿÙõÿÿþóÿÿÿ!ùk,á׬I£¦ Áƒj®A“eJ%C„ô¨qcEgÖ¨ÙFŒ-V¨HqrÈŒVCæ ,U¦@IRĤ‚‚O¾€)XåÉ’‚;ŠØhP —.“¤±ǃ‚F¬\©‚ÐÅ 1žªñEŠ”(_˜Pñâ…ƒ‚84q‚PD *TáãˆÁ 9”  à; ù‘¢ ô â†! is dragged and dropped, it gets into this weird half-focus // state where IE thinks it has focus, but Windows *doesn't*. Windows // leaves focus wherever it was before the drag. When a keyboard event // comes in, IE does the JS event bubble according to its internal notion // that the has focus, but Windows actually sends the keystroke to // the old focus control. So what you get is a keystroke going to a // control without any JS events being generated. This is a problem // when the keystroke is Enter and the control is our command line field: // IE simply inserts the newline into the field without telling us about // it. I can't find any good workaround other than a timer, to check // after the fact for this weird situation. Checking a few times a // second shouldn't bog things down too much but should correct the // problem before it's noticeable in the UI. setInterval(function() { var fld = $("cmdline"); if (fld && fld.nodeName == "SPAN") { var txt = fld.innerHTML; if (txt && txt.search(/
    |[\r\n]/) >= 0) { setCommandText(fld, getCommandText(fld).replace(/[\r\n]/g, "")); handleEnterKey(fld); } } }, 250); } /* ------------------------------------------------------------------------ */ /* * Process a game event from the server */ function onGameEvent(req, resp) { // check what we have if (xmlHasChild(resp, "say")) { window.closeCommandLine(null, null, false); window.writeText(xmlChildText(resp, "say")); } if (xmlHasChild(resp, "closeInputLine")) { var c = xmlChild(resp, "closeInputLine")[0]; var txt = xmlChildText(c, "text"); var user = xmlChildText(c, "user"); window.closeCommandLine(txt, user, false, null); } if (xmlHasChild(resp, "cancelInputLine")) { window.cancelCommandLine( xpath(resp, "cancelInputLine.reset") == "yes"); } if (xmlHasChild(resp, "inputLine")) { window.openCommandLine(); } if (xmlHasChild(resp, "scrollToBottom")) { scrollToAnchor(); } if (xmlHasChild(resp, "clearWindow")) { window.clearWindow(); } if (xmlHasChild(resp, "morePrompt")) { window.showMorePrompt(); } if (xmlHasChild(resp, "setsize")) { // update our window layout parent.setWinLayout(this, xmlChildText(resp, "setsize")); } if (xmlHasChild(resp, "resize")) { // recalculate our window layout parent.calcLayout(); } } function onGameState(req, resp) { // read the scrollback items and write them as text var hist = []; xpath(resp, "scrollback/sbitem[*]").forEach(function(s) { // write this output text passage window.writeText(xpath(s, "text#text")); // if there's an input line for this block, add it var i = xpath(s, "input"); if (i) { // get the appropriate style for the old command line text var style = xmlHasChild(i, "interrupted") ? "cmdlineInterrupted" : "cmdlineOld"; // add the command line text var t = xmlNodeText(i); window.writeText("" + t.htmlify() + "
    "); // save it in our new history list hist.push(t); } }); // if there's no global history list (probably because we're refreshing // the page), use the new history list that we built from the scrollback // information as the active history list if (cmdLineHistory.length == 0) { // trim it to the maximum retention count while (hist.length > cmdLineHistoryLimit) hist.shift(); // install it as the active command history cmdLineHistory = hist; // select the last item cmdLineHistoryIdx = hist.length; } switch (xmlChildText(resp, "mode")) { case "working": break; case "inputLine": window.openCommandLine(); break; case "moreMode": window.showMorePrompt(); break; } } /* ------------------------------------------------------------------------ */ /* * Document event handlers */ function onDocKey(desc) { // if there's an explicit More prompt showing, release it if (morePromptDiv && desc && !desc.modifier) { // set the new scroll anchor scrollAnchor = getObjectRect(morePromptDiv).y - 5; // remove the division $("maindiv").removeChild(morePromptDiv); morePromptDiv = null; // let the server know that the More prompt is done serverRequest("/webui/morePromptDone?window=" + pageName, ServerRequest.Command); // we're done with the key - don't process it further return false; } // do the generic focus processing genericDocKeyFocus(desc); } function onDocMouseDown(ev) { // apply the default processing return true; } function onDocMouseUp(ev) { // apply the default processing return true; } var docFocusThread = null; function onFieldFocus() { if (docFocusThread) { clearTimeout(docFocusThread); docFocusThread = null; } } /* * Process a selection change in the field. We call this from the * 'onselect' event in the field, which fires whenever the selection * changes to a new non-empty selection; and also on mouseup and mousedown * messages (since these can move the cursor, but don't generate 'onselect' * events if the selection range is empty); and also on key events (since * these can also change the selection, and again don't generate 'onselect' * events for empty selections). */ var lastFldSel = null; function onFieldSelect() { // remember the new selection range in the field, if we have one var sel = getSelRangeInEle("cmdline"); if (sel) lastFldSel = sel; } /* * Process a mouse event (mousedown or mouseup) in the field. Mouse clicks * can cause selection changes that don't generate 'onselect' events, so * note the selection after processing each click. */ function onFieldMouse() { // note the new selection after the click has been fully processed setTimeout(onFieldSelect, 0); } /* * Set the default focus for a keyboard event. This moves focus to the * command line if there's an active command line, and focus isn't already * in the command line or some other focus-taking object. */ function setDefaultFocusChild(desc, fromWin) { // if the command line doesn't exist, do nothing var fld = $("cmdline"); if (!fld) return; // if the command line already has focus, do nothing var ae = document.activeElement; if (ae == fld && document.hasFocus && document.hasFocus()) return; // if some other input control has focus, do nothing if (ae && ae != fld && eleKeepsFocus(ae, desc)) return; // remember current selection, since focusing will set a new default // selection in the field var sel = lastFldSel; // note the current text in the field var oldTxt = getCommandText(fld); // set focus after finishing with this key event setTimeout(function() { // move focus to the field try { fld.focus(); } catch (e) { // couldn't set focus - the field must have been deleted return; } // Note if the field's contents have changed since we read them. // IE has some buggy behavior in certain situations where the // field *kind of* loses focus, but not quite: for JS purposes, // IE reports that the field doesn't have focus, but internally // the underlying control actually does have focus. The result // is that the field turns out to process the key even though // IE assures us that it won't. We can detect many of these // cases by checking the before and after field contents; if // the field changed between the key event and the timeout, // the event must have made some change to the field after // all, so we don't want to insert buffered characters // after all. var newTxt = getCommandText(fld); var changed = newTxt != oldTxt; // re-select the original range as of when we last lost focus if (sel && !changed) setSelRangeInEle(fld, sel); // make sure the field is fully in view var rcFld = getObjectRect(fld); var rcDiv = getObjectRect(fld.parentNode); var s = rcDiv.y + rcDiv.height - getWindowRect().height + rcFld.height/2; var scur = getScrollPos(); if (s > scur.y) window.scrollTo(scur.x, s); // process buffered keys readKeyBuf(fld, changed); }, 0); // If it's an Up or Down key, cancel the default behavior, so that // the browser doesn't scroll the window. These keys step through // the command history when a command line is active, so we don't // want to also scroll the window. if (desc && (desc.keyName == "Up" || desc.keyName == "Down")) preventDefault(desc.event); } // Process keys in the top window's keyboard buffer. The buffer captures // keystrokes when there's no focus, so that we can deliver them to the // field when it initially gains focus or regains focus. function readKeyBuf(fld, changed) { // get the key buffer var buf = $win().keybuf; // read each key for (var done = false ; buf.length && !done ; ) { // get the next key - take the oldest key first so that we process // keys in the same order as they were typed var desc = buf.shift(); // if the field changed since we buffered the key, ignore everything // except certain command keys if (changed && desc.keyName != "Enter") continue; // note the current selection in the field var r = getSelRangeInEle(fld) || { start: 0, end: 0 }; // if it's a character key, replace the selection with the // character; otherwise process the special key if (desc.ch && desc.ch.charCodeAt(0) > 27) { // pretend we typed the character replaceSelRange(fld, desc.ch, false); } else switch (desc.keyName) { case "U+0008": // Backspace - delete the selection, or the character to the left. // If nothing's selected, select the character to the left. if (r.start == r.end && r.start > 0) setSelRangeInEle(fld, {start: r.start - 1, end: r.start}); // delete the selection replaceSelRange(fld, "", false); break; case "U+007F": // Delete - delete the selection, or the character to the right. // If nothing's selected, select the character to the right. if (r.start == r.end) setSelRangeInEle(fld, {start: r.start, end: r.start + 1}); // delete the selection replaceSelRange(fld, "", false); break; case "Enter": // process the enter key handleEnterKey(fld); // stop processing buffered keys, since we're no longer active done = true; break; case "Left": // move to the start of the selection, or left one character if (r.start == 0) r = {start: 0, end: 0}; else if (r.start == r.end) r = {start: r.start - 1, end: r.start - 1}; else r = {start: r.start, end: r.start}; setSelRangeInEle(fld, r); break; case "Right": // move to the end of the selection, or right one character if (r.start == r.end) r = {start: r.start + 1, end: r.start + 1}; else r = {start: r.end, end: r.end}; setSelRangeInEle(fld, r); break; default: // try running others through the normal key handler cmdlineKey(desc, fld); break; } } } /* * IE-specific Paste filtering for the command line. We make sure that the * pasted data doesn't contain any newlines or formatted text. */ function onFieldPaste() { // get the clipboard contents var txt = window.clipboardData.getData("Text"); if (txt != null) { // remove any newlines (replace each run of newlines with one space) txt = txt.replace(/[\r\n]+/g, " "); // Manually do the past into the field's current selection. Do // this rather than allowing the native paste to proceed, because // the native paste will insert formatted HTML text. There's not // a way to tell IE to paste the plain text version that it shows // us; it always just pastes the HTML. So we have to simulate the // effect by doing the replacement manually. replaceSelRange("cmdline", txt, true); // stop the rest of the paste return false; } else { // no text available - do the native paste operation instead return true; } } function onWinScroll(ev) { // the user has presumably scrolled the window manually, so set // the new anchor position to the current scroll position (assuming // it's below the old scroll anchor) var y = getScrollPos().y; if (y > scrollAnchor) scrollAnchor = y; // if there's a pending command line, try opening it if (cmdLinePending) maybeOpenCommandLine(); // continue to the default browser processing return true; } function onWinResize(event) { // if there's a pending command line, try opening it - the resize might // have brought the new command line real estate into view maybeOpenCommandLine(); // adjust the command line width adjustCmdlineWidth(); // apply the default processing return true; } /* ------------------------------------------------------------------------ */ /* * Clear the window */ function clearWindow() { // end the current output division outputDiv = null; // If the active element is the body, explicitly move focus there if // possible. This avoids a weird glitch on IE. I think what's going // on is that IE has some residual memory of the focus being in the // division that contained the most recent command line, and removing // that division below causes IE to send the focus off into some // off-screen limbo to follow the removed element. Once focus goes // to limbo, we can't seem to get it back programmatically; the user // has to manually click on the window, which is irritating for the // user. Explicitly establishing focus in the body element before we // remove anything seems to keep focus in the window after the clear. var ae = document.activeElement; if (ae == document.body && ae.focus) ae.focus(); // remove all of the "outputdiv" children of the "maindiv" division var main = $("maindiv"); for (var chi = main.firstChild, nxt = null ; chi ; chi = nxt) { // note the next child, in case we delete this one nxt = chi.nextSibling; // if this is an "outputdiv" division, delete it if (chi.className == "outputdiv") main.removeChild(chi); } } /* ------------------------------------------------------------------------ */ /* * Window text writer */ // current output division, and current source text for the division var outputDiv = null; var outputDivTxt = null; /* * Write text to the window. This adds the text at the end of the current * output division in the document. */ function writeText(txt) { // open an output division if necessary openOutputDiv(); // add the text to the running source for the division outputDivTxt += BrowserInfo.adjustText(txt); // set the text in the division outputDiv.innerHTML = outputDivTxt; } /* * Open an output division */ function openOutputDiv() { // if there's no output division open, create one if (!outputDiv) { // create a new division outputDiv = document.createElement("div"); outputDiv.className = "outputdiv"; // add it at the end of the main division var md = $("maindiv"); md.appendChild(outputDiv); // start a new running division text section outputDivTxt = ''; } } /* ------------------------------------------------------------------------ */ /* * "More" prompt */ function showMorePrompt() { // if there's already a More prompt division, do nothing if (morePromptDiv) return; // create the "More" prompt division var mp = morePromptDiv = document.createElement("div"); mp.className = "morePrompt"; // add it at the end of the main division var main = $("maindiv"); main.appendChild(mp); // fill it in mp.innerHTML = "Press a key to continue..."; // scroll it into view, if possible scrollToAnchor(); } /* ------------------------------------------------------------------------ */ /* * Command line processing */ /* command-line globals */ var scrollAnchor = 0; var cmdLinePending = null; var cmdLineHistory = []; var cmdLineHistoryIdx = 0; var cmdLineHistoryLimit = 20; var cmdLineNum = 1; /* * Get the contents of an open text field */ function getCommandText(fld) { // we use a on some browsers, and an // field on others var txt = fld.nodeName == "SPAN" ? fld.innerText : fld.value; // if it's null, make it an empty string if (txt == null) txt = ""; // return the text return txt; } /* * Set the contents of an open text field */ function setCommandText(fld, txt) { // before setting the text, make sure the field is big enough for it var probe = $("cmdline_probe."); probe.innerHTML = (txt.htmlify() + 'MMMM').replace(/ /g, ' '); if (probe.offsetWidth > fld.offsetWidth) fld.style.width = probe.offsetWidth + "px"; // set the contents, according to the input element type if (fld.nodeName == "SPAN") fld.innerText = txt; else fld.value = txt; // move the cursor to the end of the new text setSelRangeInEle(fld, {start: txt.length, end: txt.length}); } /* * Open a command line. This closes the current output division and adds a * new input editor after it. */ function openCommandLine() { // add an empty span as a wrapper for the command line var id = "cmdline_" + cmdLineNum; writeText(""); cmdLinePending = $(id); // Try opening the new command line immediately, if possible. (Whether // we can depends on the scrolling situation.) maybeOpenCommandLine(); // scroll down as far as possible scrollToAnchor(); } /* * Scroll down as far as possible to bring new text into view. This * scrolls down to the bottom of the document, or to the point where the * "scroll anchor" just goes out of view, whichever distance is shorter. * The scroll anchor is the last vertical position that we can safely * assume the user has seen: it's the bottom of the document at the time of * the last pause for user interaction, such as a command line input or key * input wait. */ var inMoreMode = false, morePromptDiv = null; function scrollToAnchor() { // get the main division, and figure its bottom position in doc coords var main = $("maindiv"); var rcMain = getObjectRect(main); // get the window size and current scroll position var rcWin = getWindowRect(); var curScroll = getScrollPos(); // the most we'd want to scroll would be far enough to bring the bottom // of the main division into view, so start with that var s = rcMain.y + rcMain.height - rcWin.height; // if that's beyond the anchor position, only scroll as far as the anchor if (s > scrollAnchor) s = scrollAnchor; // if we're not already scrolled past the target, scroll there now if (s > curScroll.y) window.scrollTo(curScroll.x, s); } /* * Try opening the command line for editing. This creates the actual * text field that we use to present the input editor in the UI. */ function maybeOpenCommandLine() { // IE seems to trigger some events recursively at surprising times. // In particular, IE seems to do queued processing on virtually any // system call, even seemingly non-blocking things like object size // retrievals, and this can trigger event calls. Since this routine // is called from a number of events, that means that we can be // invoked recursively seemingly at random. To avoid reentrancy // problems, privatize our globals while we're working. var cl = cmdLinePending; cmdLinePending = null; // if there's no command line pending, there's nothing to do if (!cl) return; // Check to see if the new command line is in view. We've set up // the wrapper division for the new command line at the bottom of // the stream division, so we simply have to see if the bottom of // division is in view. var rcMain = getObjectRect($("maindiv")); // figure the distance from the scroll anchor to the bottom of the div var dist = rcMain.y + rcMain.height - scrollAnchor; // if that doesn't fit in the window, don't open the command line yet var rcWin = getWindowRect(); if (dist > rcWin.height) { // set a More prompt in the status line (this doesn't work on most // newer browsers, and there's no workaround, since most browsers // now prohibit javascript from tampering with the status line for // security reasons; but just in case) window.status = "*** More ***"; // note that More mode was activated inMoreMode = true; // restore the pending command line and return cmdLinePending = cl; return; } // if we activated More mode in the past, turn it off if (inMoreMode) { // remove any More prompt window.status = ""; // Consider any keys that were typed up to this point to have been // consumed by the More mode, so clear the keystroke buffer. $win().keybuf = []; // no longer in More mode inMoreMode = false; } // hide the bottom spacer while the command line is open, so that // the command line is at the very bottom of the window // $("bottomSpacerDiv").style.display = "none"; // figure the font - use the computed font of the enclosing span var font = "font: " + getStyle(cl, "font").replace(/'/g, '') + ";"; // set up the event handlers for the input field var events = "onkeypress=\"javascript:return $kp(event, cmdlineKey, this);\" " + "onkeydown=\"javascript:return $kd(event, cmdlineKey, this);\" " + "onmousedown=\"javascript:onFieldMouse();return true;\" " + "onmouseup=\"javascript:adjustCmdlineWidth();onFieldMouse();" + "return true;\" " + "ondrop=\"javascript:adjustCmdlineWidth();return true;\" " + "onpaste=\"javascript:adjustCmdlineWidth();return true;\" " + "onfocus=\"javascript:onFieldFocus();\" "; // fill in the command line wrapper with the input field if (BrowserInfo.ie) { // IE seems impossibly fiddly with text field alignment; as far as // I can tell it's simply not possible to get a field to line up // seamlessly with the surrounding text for arbitrary font size. // We could always make manual adjustments, but I can't even figure // out a way to predict what the adjustment needs to be - it seems // to vary across different fonts and sizes. Fortunately, there's // the contentEditable property, which lets us make an ordinary SPAN // into an editable field. This actually works on most of the other // browsers as well, but it has some other quirks of its own, so // we'll only use it where we absolutely have to (so far just IE). // Add the IE-specific "before deactivate" event, to capture the // selection range before we lose focus. Also intercept Paste // events, so that we can limit pasting to single-line unformatted // text. events += "onbeforedeactivate=\"" + "javascript:onFieldSelect();return true;\" " + "onpaste=\"javascript:return onFieldPaste();\" "; // create the content-editable span cl.innerHTML = ""; } else { // some browsers need some slight tweaks to the positioning // to get everything aligned properly var hacks = ""; if (BrowserInfo.safari || BrowserInfo.opera) hacks = "margin: -1px 0 0 -1px;"; else if (BrowserInfo.firefox && BrowserInfo.firefox >= 3.05) hacks = "position: relative; left: -1px;"; // We're using an , so monitor selection changes // directly on the input field. Also, adjust the field width as // we edit it to keep it large enough for the text. events += "onselect=\"javascript:onFieldSelect();\" " + "oninput=\"javascript:adjustCmdlineWidth();" + "return true;\" "; // all of the other browsers seem to get the alignment fine with // a regular text field, so use a regular text field cl.innerHTML = ""; } // set focus on the new input field - do this in a timeout to give // the browser a chance to process events related to creating the // new objects, to make sure the object exists in the UI before we // try to give it the focus docFocusThread = setTimeout(function() { // if we have an interrupted command line (such as from a timeout // interruption), restore the saved state if (interruptedCommandLine) { // get the field var fld = $("cmdline"); // restore the editing state setCommandText(fld, interruptedCommandLine.text); setSelRangeInEle(fld, interruptedCommandLine.sel); // done with the interrupted editing state interruptedCommandLine = null; } // set the default focus $win().setDefaultFocus(null, null); // We only open the command line when it's in view, so it must // be in view. Reset the scroll anchor to the new command line // on the assumption that the user has had a chance to view // everything up to this point. scrollAnchor = getObjectRect("cmdline").y - 5; }, 0); // set the initial field width adjustCmdlineWidth(); // add a history item for the active command line cmdLineHistory.push(""); } /* * Explicitly cancel the command line. This is used to interrupt a command * line in progress, usually due to a timeout. If 'reset' is true, clear * the input line state, so that the next new command line starts out * empty. Otherwise, save the state from the current command line editing * session, so that we can restore it on the next open. */ var interruptedCommandLine = null; function cancelCommandLine(reset) { // get the active command field; if there isn't an open command line, // there's nothing we need to do var fld = $("cmdline"); if (!fld) return; // close the command line, using the interrupted style closeCommandLine(null, null, false, "cmdlineInterrupted"); // remove the placeholder top history item cmdLineHistory.pop(); } /* * Close the command line. If 'style' is specified, it's the CSS class * name to use to show the static text of the now-closed command line. */ function closeCommandLine(txt, user, reset, style) { // if the style is null, use the default if (!style) style = "cmdlineOld"; // get the text input field var fld = $("cmdline"); var div = null; var height = ""; if (fld) { // if we're not resetting, save away the current editing info var s; if (reset) s = null; else { s = { text: getCommandText(fld), sel: getSelRangeInEle(fld), lastSel: lastFldSel }; } // store the saved command line info interruptedCommandLine = s; // get its contents, if the caller didn't specify overriding text if (txt == null) txt = getCommandText(fld); // Note the actual height of the input field. This allows us to // set a matching height for the static text span that will replace // the field now that we're closing the editor. An input field // doesn't usually have the exactly same native height as a regular // text span with the same contents, so this explicit setting is // needed to keep the line spacing from visibly changing when we // change the command line from an input field to a . height = "height:" + getObjectRect(fld).height + "px"; // remove focus from the field fld.blur(); // forget the last selection lastFldSel = null; // the command line division is the field's parent div = fld.parentNode; } else if (cmdLinePending) { // We have a pending command line that we never activated. This // happens when there's unviewed text past the bottom of the // window; we wait to activate the command line until the user // scrolls its location into view. We do have a placeholder // division in this case, though, so we can just plug in the // entered command there - that's where it would ultimately have // gone anyway, it's just that we skip the entire creation and // deletion of the field within the division. div = cmdLinePending; // there's no longer a pending command line cmdLinePending = null; } else { // no command line is open or pending - there's nothing to close return; } // IE doesn't respect the "white-space: pre" style on inline elements, // so to work around this we change spaces into  's. var spanTxt = txt.htmlify(); if (BrowserInfo.ie) spanTxt = spanTxt.replace(/ /g, ' '); // if a user name was provided add it var userPrefix = ""; if (user) userPrefix = "[" + user + "] "; // replace the field with the text that was entered var lastCmdLineID = "closedCmdLine" + (cmdLineNum++); div.innerHTML = "" + userPrefix + spanTxt + ""; // remember the last command line object var lastCmdLine = $(lastCmdLineID); // start a new output division outputDiv = null; // return the command line text return txt; } /* * Set the command line font */ function setCommandLineFont() { // get the active input field var fld = $("cmdline"); // if we have a field, set its font if (fld) fld.style.font = getStyle(fld.parentNode, "font"); } /* * Adjust the command-line width. We call this automatically as the user * types, to keep the field as least as wide as the text it contains. This * ensures that we don't scroll the contents of the input field, but rather * scroll the whole window. Scrolling the input field's contents is * confusing because we try to make the field fade into the background as * much as possible: we don't show a border or separate background color. * We want it to look like an input line in a regular terminal window, * which doesn't look any different from the surrounding output text. */ function adjustCmdlineWidth() { // give the browser a few moments to digest events, then adjust the width // of the field so that it's large enough for the field's contents setTimeout(function() { // get the field var fld = $("cmdline"); if (!fld) return; // get the "probe" element, for measuring the font size var probe = $("cmdline_probe."); // set the probe's font and contents to match the real input's, // with a little extra space to spare for the cursor at the end var txt = getCommandText(fld); probe.innerHTML = (txt.htmlify() + 'MMMM').replace(/ /g, ' '); // set the new width fld.style.width = probe.offsetWidth + "px"; }, 10); } // Common keypress/keydown handler. function cmdlineKey(desc, fld) { // Set the scroll anchor to the command line field. Since the user // is typing into the field, it's safe to assume they've had a chance // to read everything above this point in the document. (Take off // a few pixels for a little margin of visual context.) scrollAnchor = getObjectRect(fld).y - 5; // Check for a selection change after processing the key through // the default handling. setTimeout(onFieldSelect, 0); // check for a change in the field width adjustCmdlineWidth(); // see what we have switch (desc.keyName) { case "Up": // up arrow // recall the previous history item if (cmdLineHistoryIdx > 0) { // if we're at the top of the list, save the current text if (cmdLineHistoryIdx + 1 == cmdLineHistory.length) cmdLineHistory[cmdLineHistoryIdx] = getCommandText(fld); // recall the previous item setCommandText(fld, cmdLineHistory[--cmdLineHistoryIdx]); } // we've fully handled the event return false; case "Down": // down arrow // recall the next history item if (cmdLineHistoryIdx + 1 < cmdLineHistory.length) setCommandText(fld, cmdLineHistory[++cmdLineHistoryIdx]); // we've fully handled the event return false; case "PageUp": // Page Up case "PageDown": // Page Down // Input fields sometimes capture the page up/down keys for // cursor navigation. For our single-line inputs, that's // not desirable; instead, treat it as a document scroll event. // get the direction var dir = (desc.keyName == "PageUp" ? -1 : 1); // get the window height, for the scroll distance var wrc = getWindowRect(); // scroll by a little less than a page var dist = wrc.height; if (dist > 20) dist -= 20; // do the scrolling window.scrollBy(0, dist * dir); // handled return false; case "U+001B": // Escape // Clear the command line. Do this after finishing with the // keystroke: in Firefox, at least, Escape seems to be sort of // a super-Undo that restores the last thing that was in the // field, so the browser undoes our work here if we do it in-line. // This seems to happen even with the preventDefault() below, so // I'm not sure where the value reset is coming from, but it's // someplace we can't seem to override. Clearing the field in a // timeout lets us take care of it after the browser has finished // the un-overridable super-Undo. setTimeout(function() { setCommandText(fld, ""); }, 0); // prevent the default handling for Escape - this is a command key // in many browsers meaning "cancel page load", which can also // cancel in-progress ajax requests preventDefault(desc.event); // handled return false; case "U+0055": // 'U' // check for Ctrl+U if (desc.ctrlKey) { // clear the line setCommandText(fld, ""); // on IE, block the Ctrl+U underline handling if (BrowserInfo.ie) { preventDefault(desc.event); return false; } } // it's not Ctrl+U, so we don't handle it here return true; case "U+0042": // 'B' case "U+0049": // 'I' case "U+004B": // 'K' // When editing a 'contentEditable' span in IE, IE enables the // traditional rich-text formatting keys: Ctrl+B for Bold, // Ctrl+I for italic, Ctrl+U for underline, Ctrl+K for Hyperlink. // We don't want to allow formatted text, so disable these for IE. if (BrowserInfo.ie && desc.ctrlKey && document.activeElement && document.activeElement.nodeName == "SPAN" && document.activeElement.isContentEditable) { // in a content-editable span - suppress formatting command keys preventDefault(desc.event); return false; } // otherwise, apply the default handling return true; case "Enter": handleEnterKey(fld); // no need for further handling return false; default: // we don't handle anything else, so use the default behavior return true; } } // handle (or simulate) an enter key function handleEnterKey() { // immediately scroll down by the height of the field, and to the left // edge var rc = getObjectRect("cmdline"); var curScroll = getScrollPos(); window.scrollBy(-curScroll.x, rc.height); // close the current command line var txt = closeCommandLine(null, null, true); // If they selected and entered a past history item without changing // anything, stay at the current position in the history. Otherwise, // add the new item to the end of the history and reset the index // to the latest item. if (cmdLineHistoryIdx + 1 < cmdLineHistory.length && cmdLineHistory[cmdLineHistoryIdx] == txt) { // We've selected and entered a past item without making any // changes. The temporary last history item we added for the // new command line was never committed, so delete it. cmdLineHistory.pop(); } else if (txt == "") { // don't add empty lines to the history cmdLineHistory.pop(); } else { // We've entered a new item. Save it as the last history item. // There's already an array item for it, since we added a temporary // item for the new command line when we opened it - so we just // need to set the value in the last item. cmdLineHistory[cmdLineHistory.length-1] = txt; // trim the history if it's getting too long if (cmdLineHistory.length > cmdLineHistoryLimit) cmdLineHistory.shift(); } // put the history cursor at the end of the list cmdLineHistoryIdx = cmdLineHistory.length; // process the command through the registered callback if (window.onCloseCommandLine) window.onCloseCommandLine(txt); // send the text to the server serverRequest("/webui/inputLine?txt=" + encodeURIComponent(txt) + "&window=" + pageName, ServerRequest.Command); } // handle a command hyperlink function gamehref(ev, href, win, ele) { // if we have a window path, ask our parent to find the window if (win) { // send it to the given window, if we can find it if (win = $win(win)) return win.gamehref(ev, href, null, ele); else return false; } // there's no path, so it's directed to us - put it in our command buffer var fld = $("cmdline"); if (fld) { // set the text in the field setCommandText(fld, href); // enter the command handleEnterKey(); // we've handled the event, so don't bubble it up return false; } else { // there's no active command line, so send the click to the main // window, in case the server wants it as an href event $win().clickToServer(ev, href, ele); } // bubble the event, but prevent the default browser handling preventDefault(getEvent(ev)); return true; } frobtads-1.2.3/tads3/lib/webuires/dlgIconInfo.gif0000644000175000001440000000276711445204512021011 0ustar realncusersGIF89a((‡J©JªKªMªM«O« O«!Pª"Pª#Q«'X«(Uª(U¬(U­+V¬-Y«-X­.Y¬0Z­3\­6^­8`®:b®—Â#@²IÕA Q"MPÝ ˆð‘È"YT‡L…lôBu!ØqzHQ 2näDu Ät@±kK`÷Va‚TGBr|‰Duì HÙÂÀs'ìqs<ñ01Ž?%íB…ð¡‡Qœ /9â 2S ɬuJò†l"‰I¢ðÌ¿!DÌ(¨ˆrG¼èâˆ#¢ CNÌI£L"ŒlŠ(ŠÜI4 -7ÑÕ $¢S4·;frobtads-1.2.3/tads3/lib/webuires/util.js0000644000175000001440000051061712145504453017445 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Utility package for TADS 3 Web UI. This is a javascript file that's * designed to be included from the main UI page of a Web-UI game. This * provides a set of useful functions for implementing an IF-style user * interface in HTML/Javascript. */ /* ------------------------------------------------------------------------ */ /* * Miscellaneous globals */ // my page name, as I'm known to the server var pageName = ""; // the generic noun we use to refer to this program in error messages // (game, story, program, ...) var selfNoun = "story"; // default action suggestion var errorRecoveryAction = " If the " + selfNoun + " otherwise seems to be running " + "properly, you can ignore this error. If the " + selfNoun + " isn't responding or is acting strangely, it might help to " + "
    reload this page."; /* ------------------------------------------------------------------------ */ /* * Define a property on an object. If the browser supports the * defineProperty method, we'll use that; otherwise we'll just set the * property directly. * * This differs from direct property setting in that we mark the property * as non-enumerable. This is particularly useful for our Object.prototype * extensions when jQuery is also being used. jQuery uses Object property * enumerations internally and incorrectly assumes that all results are * browser-defined properties, so our custom extensions can create problems * if jQuery is also used. Explicitly hiding those from enumerations lets * us define our own custom Object.prototype extensions without confusing * jQuery. (The webui library itself doesn't use jQuery, so this isn't an * issue out of the box, but it is an issue for individual game authors who * want to use jQuery with the webui code.) */ function defineProperty(target, name, method) { if (Object.defineProperty && Object.defineProperties) Object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true }); else target[name] = method; } /* ------------------------------------------------------------------------ */ /* * Initialize. Call this once at page load to set up this package. */ function utilInit() { // if we have a parent, ask the parent for my name if (window.parent && window.parent != window) pageName = window.parent.pathFromWindow(window); // detect the browser version BrowserInfo.init(); // copy an object defineProperty(Object.prototype, "copy", function(deep) { // create a blank object with the same prototype as me var newObj = CopyProto.create(this); // copy each property for (var prop in this) { // include only directly defined properties if (this.hasOwnProperty(prop)) { // get this value var val = this[prop]; // recursively copy objects if this is a "deep" copy if (deep && typeof(val) == "object") val = val.copy(true); // save this value newObj[prop] = val; } } // return the new object return newObj; }); // get the number of enumerable keys defineProperty(Object.prototype, "propCount", function() { var cnt = 0; for (var prop in this) { if (this.hasOwnProperty(prop)) ++cnt; } return cnt; }); // enumerate keys set in an object, excluding prototype keys defineProperty(Object.prototype, "forEach", function(func) { for (var prop in this) { // call the callback only for props directly defined in 'this' if (this.hasOwnProperty(prop)) func(this[prop], prop); } }); // find the property for which a callback returns true defineProperty(Object.prototype, "propWhich", function(func) { for (var prop in this) { if (this.hasOwnProperty(prop) && func(this[prop], prop)) return prop; } return null; }); // find the property value for which a callback returns true defineProperty(Object.prototype, "valWhich", function(func) { for (var prop in this) { var val; if (this.hasOwnProperty(prop) && func(val = this[prop], prop)) return val; } return null; }); // htmlify a string - convert markup-significant characters to & entities String.prototype.htmlify = function() { return this.replace(/[<>&"']/g, function(m) { return { '<': "<", '>': ">", '&': "&", '"': """, '\'': "'" }[m]; }); }; // remove html quoting from a string String.prototype.unhtmlify = function() { return this.replace(/&(gt|lt|amp|quot|nbsp|#[0-9]+);/ig, function(m, i) { var r = { 'gt': '>', 'lt': '<', 'amp': '&', 'quot': '"', 'nbsp': ' ' }[i.toLowerCase()]; if (r) return r; else return String.fromCharCode(parseInt(i)); }); } // trim leading and trailing spaces from a string String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); }; // convenience method: find an item in an array if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(val) { for (var i = 0 ; i < this.length ; ++i) { if (val == this[i]) return i; } return -1; }; } // convenience method: find the index for a value matching a callback if (!Array.prototype.indexWhich) { Array.prototype.indexWhich = function(test) { for (var i = 0 ; i < this.length ; ++i) { if (test(this[i], i)) return i; } return -1; }; } // convenience method: find a value matching a callback if (!Array.prototype.valWhich) { Array.prototype.valWhich = function(test) { for (var i = 0 ; i < this.length ; ++i) { var v = this[i]; if (test(v, i)) return v; } return -1; }; } // convenience method: map items in an array if (!Array.prototype.map) { Array.prototype.map = function(mapping) { var ret = []; for (var i = 0 ; i < this.length ; ++i) ret.push(mapping(this[i], i)); return ret; }; } // convenience method: filter an array if (!Array.prototype.filter) { Array.prototype.filter = function(filterFunc) { var ret = []; for (var i = 0 ; i < this.length ; ++i) { var ele = this[i]; if (filterFunc(ele, i)) ret.push(ele); } return ret; }; } // convenience method: get the top element of a stack-like array Array.prototype.top = function() { if (this.length) return this[this.length - 1]; else return null; }; // override our copy method for arrays Array.prototype.copy = function(deep) { // create the new array var newArray = []; // copy each element for (var i = 0 ; i < this.length ; ++i) { // get this value var val = this[i]; // make a deep copy of object values, if desired if (deep && typeof(val) == "object") val = val.copy(true); // set the new value newArray[i] = val; } // return the new array return newArray; }; // override the for-each enumerator for arrays - just step through // the index values from 0 to length Array.prototype.forEach = function(func) { for (var i = 0, len = this.length ; i < len ; ++i) func(this[i], i); }; // add a listener for mouse down events on the main document, // for closing spring-loaded pop-up objects addEventHandler(document, "mousedown", popupCloser); // set our custom javascript error handler window.onerror = function(msg, url, lineNum) { // generate a stack trace var s = getStackTrace(); // the top element is the onerror handler we're currently in, // which isn't part of the error, so clip it out of the list s.shift(); // create a human-readable trace listing s = s.map(function(ele) { return ele.desc.htmlify(); }).join("
    "); // log the error logError("An error occurred in the " + selfNoun + "'s user interface programming. This is probably a " + "bug that should be reported to the " + selfNoun + "'s author." + errorRecoveryAction, "Javascript error: " + msg.htmlify() + "
    Window: " + pageName + " (" + url.htmlify() + ")" + "
    Line: " + lineNum + (s ? "
    Call stack:
    " + s + "
    ": ""), { failed: true, succeeded: false }); return true; }; // set up an unload handler addEventHandler(window, "beforeunload", unloadCleanup); } // window unload handler function unloadCleanup() { // cancel outstanding ajax requests cancelServerRequests(); } /* * Create an object with the same prototype as an existing object. To * invoke, call SameProto.create(obj). */ function CopyProto() { } CopyProto.create = function(obj) { /* * This is a bit tricky, but here's how this works. The ECMA spec says * that a new object's prototype is set to its constructor's prototype * *at the moment of creation*. So step 1 is to prepare the * constructor's prototype so that it matches the source object's; then * step 2 is to instantiate the object with 'new'. */ CopyProto.prototype = obj.constructor.prototype; return new CopyProto(); } /* * Get a stack trace. This returns a list of function call descriptors. */ function getStackTrace() { // start with an empty list var s = []; // walk up the call stack starting with our caller for (var c = arguments.callee.caller ; c ; c = c.caller) { // Make sure we haven't encountered this caller before. // Javascript uses the function object to represent a call // level, which makes it impossible to walk a recursive // stack because each recursive call overwrites the caller // information in the function object. JS of course has // the actual caller information internally, but it's // not exposed through the standard APIs. for (var i = 0 ; i < s.length ; ++i) { if (s[i].fn == c) { s.push({ desc: "(recursive)" }); return s; } } // set up the level descriptor var ele = { fn: c, args: c.arguments }; // get a formatted version of the argument list var a = []; if (c.arguments) { for (var i = 0 ; i < c.arguments.length ; ++i) a.push(valToDesc(c.arguments[i])); } // build the human-readable function call description ele.desc = funcToDesc(c) + "(" + a.join(", ") + ")"; // add it to the list s.push(ele); } // return the trace return s; } function valToDesc(v) { // convert to string switch (typeof(v)) { case "function": return funcToDesc(v); break; case "string": // shorten it if it's long if (v.length > 60) v = v.substr(0, 30) + "..." + v.substr(v.length - 20); // escape special characters return "\"" + v.replace(/["\\\n\r\v\f\t]/g, function(m) { return { '"': '\\"', '\n': '\\n', '\r': '\\r', '\\': '\\\\', '\v': '\\v', '\f': '\\f', '\t': '\\t' }[m]; }) + "\""; case null: return "null"; case "undefined": return "undefined"; default: if (v == null) { return "null"; } else if (v instanceof Array) { var a = [], amax = (v.length > 8 ? 5 : 8); for (var i = 0 ; i < v.length && i < amax ; ++i) a.push(valToDesc(v[i])); if (v.length > amax) a.push("... (" + (v.length - amax) + " more)"); return "[" + a.join(", ") + "]"; } else { try { v = v.toString(); } catch (e) { v = typeof(v); } if (v.match(/\[object\s+([a-z0-9_$]+)\]/i)) v = "object<" + RegExp.$1 + ">"; return v; } return typeof(v); } } // format a function reference to a human-readable form (primarily for // stack trace generation) function funcToDesc(f) { // get the function in string form, and convert all whitespace // (including newlines) to ordinary spaces fn = f.toString().replace(/[ \n\r\v\f\t]+/g, " "); // For a named function, the usual toString form is like this: // function name(args) { body } // Some browsers return this same form for anonymous functions, // supplying 'anonymous' as the name. if (fn.match( /^function\s+([a-z0-9_$]+)\s*\([a-z0-9_$,\s]*\)\s*(\{.*)$/i)) { // we have the basic syntax - if the name is 'anonymous', show // using our anonymous function formatter; otherwise just use // the function name if (RegExp.$1 == "anonymous") { // The browser is using literally "anonymous" as the name. // Pull out the body to show for context. Use a clipped // version to keep the size reasonable. var body = anonFuncToDesc(RegExp.$2); // add the event handler name, if applicable return funcToEventDesc(f) + body; } else { // Named function. var body = RegExp.$2; fn = RegExp.$1; // Some browsers return a synthesized name based on the event // attribute for an in-line event handler. If we have an // event name, and the function has the same name, assume // this is the situation, so include the function body for // context as though it were an anonymous function. var ev = funcToEventDesc(f); if (ev && ev.substr(ev.indexOf(".") + 1) == fn) return ev + anonFuncToDesc(body); // it's just a regular named function return fn; } } else { // it's not in the standard format, so it's probably some other // anonymous function formatting return funcToEventDesc(f) + anonFuncToDesc(fn); } } // Get the event object for a function call, if applicable function eventFromFunc(f) { // Check for an event object in the function arguments. If we have // at least one argument, and it's an object, and it has a "type" // property, and the type property looks like "onxxx", assume it's // an event object. if (f.toString().match(/^function\s+[a-z]+\s*\(event\)/) && f.arguments && f.arguments.length >= 1 && f.arguments[0] != null && typeof(f.arguments[0]) == "object" && typeof(f.arguments[0].type) == "string" && f.arguments[0].type.match(/^[a-z]+/)) return f.arguments[0]; // if this is IE, use the window.event object if (window.event) return window.event; // no event object return null; } // Check for an event handler. If this is a top-level function, // and the target of the event has an attribute for the event // method, and the attribute points to this function, we must be // the handler for the event. In this case we can supply the // name of the event and the ID or node type of the target object // as additional context. function funcToEventDesc(f) { // only top-level functions can be event handlers if (f.caller != null) return ""; // we obviously need to have an event for this to be an event handler var ev = eventFromFunc(f); if (!ev) return ""; // figure the event attribute, based on the event type name var attr = "on" + ev.type; // The event handler could be on the target, or on a parent object // by event bubbling. Scan up the parent tree for an object with // a handler pointing to our function. for (var targ = getEventTarget(ev) ; targ ; targ = targ.parentNode) { // check for a handler in this object pointing to our function if (targ[attr] == f) { // Found it - this must be the handler we're running. Return // the ID of the element (or just the tag name if there's no ID) // and the event attribute name as the event description. return (targ.id ? targ.id : targ.nodeName) + "." + attr; } } // didn't find a handler return ""; } // Format an anonymous function body to human-readable form. We'll // show a fragment of the source code body of the function, if available. function anonFuncToDesc(f) { // some browsers give us the format "{ javascript:source }" for // in-line event handlers (onclick, etc) - remove the "javascript:". if (f.match(/^\{\s*javascript:\s*(.*)$/i)) f = "{ " + RegExp.$1; // If the body is short, return it unchanged. If it's long, clip // out the middle to keep the display reasonably concise - keep // the head and tail for context, and keep a little more of the // head than the tail, since that's usually the most easily // recognizable portion (for the human reader). if (f.length > 60) f = f.substr(0, 30) + "..." + f.substr(f.length - 20); // return what we found return f; } // request our initial state function getInitState() { // send the initial status request for this window serverRequest("/webui/getState?window=" + pageName, ServerRequest.Command, function(req, resp) { window.onGameState(req, resp); }); } /* ------------------------------------------------------------------------ */ /* * Generic document-level key handler, with focus setting. This does all * the work of genericDocKey(), plus we set the default focus control if * focus isn't already in a key-handling control. * * Any document objects that don't have their own special handling for * keystrokes at the document level can install this default handler at * load time. This handler looks for a default focus destination, and * establishes focus there. The default location is normally the main * command line in the main command transcript window. This handler makes * the standard terminal-style UI a little smoother by always sending focus * back to the command line when the user types something with the focus * set somewhere that doesn't accept input. * * Some windows (such as the top-level window) have their own focus * management rules that differ a little from ours, so they shouldn't use * this. Instead, they should do their focus management, then call * genericDocKey(), which does the rest of the generic key handling besides * the focus setting. */ function genericDocKeyFocus(desc) { // if it's a text editing key, try setting the default focus if (isFieldKey(desc)) setDefaultFocus(desc, window); // handle a generic document key genericDocKey(desc); // bubble the event return true; } /* * Handle a generic document-level keystroke. This should be called from * each window's document key handler. * * First, we check to see if the key is going to an input control that has * focus. If so, we let the key pass through without further attention, so * that the control gets the key. * * If there's no input control with focus: * * - We try sending the key to the main window for server event handling. * If the server has asked for an input event, we send the key as the * requested event. * * - For certain keys, we suppress the default browser handling. Since our * UI is primarily oriented around the command line, we want certain keys * to keep their command-line meanings even between inputs. This is in * case the the user is typing faster than we can keep up with; we don't * want the the key to suddenly change meaning when the user thinks it's an * editing key. In particular, the Backspace and Escape keys are common * editing keys that would be disruptive to the UI if the browser applied * their usual accelerator meanings of "go to the previous page" and "stop * downloading stuff on this page". */ function genericDocKey(desc) { // if the current focus processes the key, allow the default handling var ae = document.activeElement; if (eleKeepsFocus(ae, desc)) return; // check to see if the server wants keystroke events $win().keyToServer(desc); // this key will be processed at the document level, so filter it switch (desc.keyName) { case "U+0008": case "U+001B": // block the default handling for these keys preventDefault(desc.event); break; } } /* * Determine if the given keyboard event is a "field" key. Returns true if * this is a key that's meaningful to an input field: a regular printable * character, or a cursor movement key (arrow, Home, End, etc). */ function isFieldKey(desc) { // get the key var k = desc.keyName; switch (k) { case "Shift": case "Control": case "Alt": case "CapsLock": case "PageUp": case "PageDown": case "NumLock": case "ScrollLock": case "Win": // Windows/Start shift keys case "Pause": case "F1": case "F2": case "F3": case "F4": case "F5": case "F6": case "F7": case "F8": case "F9": case "F10": case "F11": case "F12": case "U+0009": // tab // none of these keys directly affect the field, so ignore them return false; case "U+0008": case "U+001B": // these are explicitly field keys return true; default: // if the Ctrl or Alt keys are down, don't consider this a field // key, since it's probably an accelerator or shortcut instead if (desc.ctrlKey || desc.altKey || desc.metaKey) return false; // looks like a regular field key return true; } } /* * Set the default focus. If the focus isn't currently in a control that * accepts keyboard input, we'll move the focus to the default command * input line, if there is one. */ function setDefaultFocus(desc, fromWin) { // check for retained focus if (evtKeepsFocus(desc)) return; // if we have a parent window, ask it to look for a default var par = window.parent; if (par && par != window) { // there's a parent window - pass the request up to it par.setDefaultFocus(desc, fromWin); } else { // there's no parent; try setting the default focus to a child window setDefaultFocusChild(desc, fromWin); } } /* * Determine if focus should say put for the current keyboard event. */ function evtKeepsFocus(desc) { // if there's no keyboard event, focus can move if (!desc || !desc.event) return false; // There's a weird bug in IE where a control has focus for event // purposes but doesn't actually have keyboard focus. When this // happens, the actual Windows event goes to the actual focus control, // which isn't part of the JS event bubble. var ae = getEventTarget(desc.event); // if the event is directed to an input control, leave it where it is for ( ; ae ; ae = ae.parentNode) { // if focus is already in an input object of some kind, leave it there if (eleKeepsFocus(ae, desc)) return true; } // didn't find a focus keeper return false; } /* * Determine if the given element should retain focus for the given * keyboard event. We'll return true if the element is a focusable input * control (an INPUT, TEXTAREA, or SELECT), or it's an and the * event is the Return key (a return key in a hyperlink selects the * hyperlink). */ function eleKeepsFocus(ele, desc) { // if there's no element, it doesn't keep focus if (!ele) return false; // Keep focus for all events if this is an input control of some // kind (INPUT, TEXTAREA, or SELECT). if (ele.nodeName == "INPUT" || ele.nodeName == "TEXTAREA" || ele.nodeName == "SELECT") return true; // For IE, also treat any element with contentEditable=true as an // input control. if (BrowserInfo.ie && ele.isContentEditable) return true; // If this is an element, and the event is the Return key, // keep focus in the hyperlink. Pressing Return while a hyperlink // has focus is usually equivalent to a click in the hyperlink, so // this event is meaningful with focus left as it is. if (ele.nodeName == "A" && ele.href && desc && desc.keyName == "Enter") return true; // the event isn't a meaningful input keystroke for this control return false; } /* * Move focus to a suitable child window. When we reach the top-level * container window, it will call this to start working back down the * window tree to find a suitable child to set focus in. */ function setDefaultFocusChild(desc, fromWin) { // by default, do nothing; other window types can override it (notably // the layout window) } /* ------------------------------------------------------------------------ */ /* * Default document mouse-click handler: close any spring-loaded popups * that are currently open. */ function popupCloser(ev) { // get the event var ev = getEvent(ev); // if there's a spring-loaded popup active, and the event isn't within // the top popup, close the top popup if (springPopups.length > 0 && !isEventInObject(ev, springPopups.top().ele) && !isEventInObject(ev, springPopups.top().openerEle)) closePopup(); // if we have a parent window, propagate the click if (window.parent && window.parent != window) window.parent.popupCloser(ev); } /* * Close the topmost active popup */ function closePopup(ele) { // if there's nothing to close, don't go on if (springPopups.length == 0) return; // if they want to close a specific popup, and it's not the top // one, ignore the request - this could be a queued event related // to the event that already closed the indicated popup if (ele && springPopups.top().ele != ele) return; // remove the top popup from the list var s = springPopups.pop(); // hide the popup s.ele.style.display = "none"; // call its 'close' method, if it has one if (s.close) s.close(); // if there's a default focus element, restore focus there if (s.focusEle) { var fe = $(s.focusEle); fe.focus(); if (fe.nodeName == "INPUT" && fe.type == "text") setSelRangeInEle(fe); } } /* * Open a popup. 'ele' is the outermost html element of the popup, and * 'close' is an optional function to call to close the popup. If * 'closeFocusEle' is specified, it's the element where we'll set focus * when the popup closes. */ function openPopup(ev, ele, close, closeFocusEle, openerEle) { // first make sure we don't have another popup to close popupCloser(ev); // add it to the popup list springPopups.push({ ele: ele, close: close, focusEle: closeFocusEle, openerEle: openerEle }); // move the focus to the popup object if (ele.focus) ele.focus(); // if we haven't added our keyboard handlers, do so now if (!ele.popupKeyHandlers) { // add a key handler to catch return and escape while we have focus var k = function(desc) { if (desc.keyName == "Enter" || desc.keyName == "U+001B") { closePopup(ele); cancelBubble(desc.event); return false; } return true; }; // Also set up a focus-loss handler: if focus leaves the popup // (and its children), and focus isn't going to the opener element, // close the popup. Not all elements can acquire focus in the // first place on all browsers, so it's basically a no-op on some // browsers, but it addresses some edge cases on IE in particular. var f = function(ev) { var ae = document.activeElement; if (!(ae && (isChildElement(ele, ae) || ae == openerEle))) closePopup(ele); }; addEventHandler(ele, "keydown", function(ev) { return $kd(ev, k); }); addEventHandler(ele, "keypress", function(ev) { return $kp(ev, k); }); addEventHandler(ele, "blur", f); ele.popupKeyHandlers = true; } // don't propagate this event any further cancelBubble(ev); } // active popup list var springPopups = []; /* ------------------------------------------------------------------------ */ /* * Shorthand to retrieve an object by name. Call like this: * *. $(obj, [sub [, sub2 [...]]]) * * If 'obj' is a string, we treat it as a path of elements separated by * periods. Each element can be: * * - an object ID *. - a class name, given in square brackets *. - a node name (tag), given in angle brackets * * For example, "mainDiv.[details]" returns the first child with * class="details" of the element with ID "mainDiv". "form3." * returns the first INPUT element of form3. * * If 'obj' is any other type, we return it as-is. This allows passing in * an element reference without first checking its type, since it will * simply be returned unchanged. This makes it easy to write routines that * accept path names or references as arguments: if the argument is a path * name, it will be translated to an element; if it's already an element, * it will be returned as given. * * If any 'sub' arguments are given, they must be strings giving the same * path notation above. We'll search for each string in turn as a child * path of the resulting object for the previous argument. This allows * efficient searching within a part of the DOM tree for which you already * have the root object. */ function $() { // scan each argument for (var obj = document.body, ac = 0 ; ac < arguments.length ; ++ac) { // get this argument var arg = arguments[ac]; // if it's a string, look it up by name if (typeof(arg) == "string") { // break it up into dot-delimited path elements var path = arg.split("."); // find each item in the path for (var i = 0 ; i < path.length ; ++i) { // check the syntax for this element var e = path[i]; if (e.match(/^\[.*\]$/)) { // search by class name e = e.substr(1, e.length - 2); obj = breadthSearch( obj, function(x) { return x.className == e; }); } else if (e.match(/^\<.*\>$/)) { // search by tag name e = e.substr(1, e.length - 2); obj = breadthSearch( obj, function(x) { return x.nodeName == e; }); } else { // search by ID if (obj == document.body) obj = document.getElementById(e); else obj = breadthSearch( obj, function(x) { return x.id == e; }); } // if we didn't find a match at this point, give up if (!obj) return null; } } else if (typeof(arg) == "object") { // the argument is the pre-resolved object obj = arg; } else { // invalid type return null; } } // return what we ended with return obj; } /* * Do a breadth-first search for a child matching the given condition */ function breadthSearch(obj, cond) { // first, search all direct children of 'obj' var chi; for (chi = obj.firstChild ; chi ; chi = chi.nextSibling) { if (cond(chi)) return chi; } // didn't find it among direct children, so search grandchildren for (chi = obj.firstChild ; chi ; chi = chi.nextSibling) { // skip certain types if (chi.nodeName == "SELECT") continue; // search this child's children var gc = breadthSearch(chi, cond); if (gc) return gc; } // didn't find it return null; } /* ------------------------------------------------------------------------ */ /* * Event handler manipulation. These routines let you attach and remove * event handler functions. An element can have any number of attached * handlers; these coexist with one another and with any in-line "onxxx" * handler defined in the element's HTML description. */ function addEventHandler(obj, eventName, func) { obj = $(obj); if (obj.addEventListener) obj.addEventListener(eventName, func, false); else if (obj.attachEvent) { obj.attachEvent("on" + eventName, func); // save it in our private list as well if (!obj.$eventList) obj.$eventList = []; obj.$eventList.push([eventName, func]); } } function removeEventHandler(obj, eventName, func) { obj = $(obj); if (obj.removeEventListener) obj.removeEventListener(eventName, func, false); else if (obj.detachEvent) { obj.detachEvent("on" + eventName, func); // remove it from our private list var l = obj.$eventList; if (l) { for (var i = 0 ; i < l.length ; ++i) { if (l[i][0] == eventName && l[i][1] == func) { l.splice(i, 1); break; } } } } } /* ------------------------------------------------------------------------ */ /* * Synthetic events. To fire an event, first create the event object with * createEvent(), then send it with sendEvent(). */ /* * Create an event. 'type' is the class of the event object, taken from * the list below. 'name' is the name of the event, which is the root name * without the "on" prefix. * * 'params' is an object giving extra parameters for the event creation. * These vary by event class: * * - bubble: event bubbles up the hierarchy (default=true) *. - cancel: event can be canceled (default=true) *. - view: the window in which the event occurs (default=window) *. - detail: mouse click count, wheel count, etc (default=1) *. - screenX: screen x coordinate (default=0) *. - screenY: screen y coordinate (default=0) *. - clientX: client x coordinate (default=0) *. - clientY: client y coordinate (default=0) *. - ctrlKey: control key setting (default=false) *. - altKey: alt key setting (default=false) *. - shiftKey: shift key setting (default=false) *. - metaKey: meta key setting (default=false) *. - button: mouse button number (default=1) *. - relatedTarget: DOM element related to the mouse event (default=null) * * The event name can be one of the standard browser events, or can be a * custom invented name. For reference, we list the most common standard * names for the supported event classes below. * * The event types (classes) that we handle are: * * "Event": base event (blur, change, copy, cut, error, focus, input, load, * paste, reset, resize, scroll, select, submit, onload) * * "MouseEvent": mouse event (click, contextmenu, dblclick, drag, drop, * mousedown, mousemove, mouseout, mouseover, mouseup, mousewheel) * * For the above event types, we'll call the class-specific initializer for * the event, using the parameters specified in 'params'. These event * types are sufficiently cross-browser that we can support them uniformly. * Other classes are not universally supported, so we don't provide * specific handling for them. We'll return the event object that the * browser gave us, but we won't initialize it. */ function createEvent(type, name, params) { // get a parameter with a default var p = function(prop, dflt) { return params && (prop in params) ? params[prop] : dflt; }; // create the event var e; if (document.createEvent) { // firefox, opera, safari... basically everyone but IE e = document.createEvent(type); switch (type) { case "Event": e.initEvent(name, p("bubble", true), p("cancel", true)); break; case "MouseEvent": e.initMouseEvent(name, p("bubble", true), p("cancel", true), p("view", window), p("detail", 1), p("screenX", 0), p("screenY", 0), p("clientX", 0), p("clientY", 0), p("ctrlKey", false), p("altKey", false), p("shiftKey", false), p("metaKey", false), p("button", 1), p("relatedTarget", null)); break; default: break; } } else if (document.createEventObject) { // IE has to do everything its own way, of course... e = document.createEventObject(); e.type = name; switch (type) { case "MouseEvent": e.altKey = p("altKey", false); e.ctrlKey = p("ctrlKey", false); e.shiftKey = p("shiftKey", false); e.metaKey = p("metaKey", false); e.button = p("button", 1); e.clientX = p("clientX", 0); e.clientY = p("clientY", 0); e.fromElement = p("relatedTarget", null); e.toElement = p("relatedTarget", null); e.offsetX = 0; // $$$ e.offsetY = 0; // $$$ break; } } else { // no event creation function availble e = null; } return e; } /* * Send an event */ function sendEvent(ele, ev) { if (ele.fireEvent) { // the IE way - set the target ev.srcElement = ele; // for mouseover and mouseout, set the to/from element properly if (ev.type == "mouseover") ev.toElement = ele; else if (ev.type == "mouseout") ev.fromElement = ele; // fire the event try { return ele.fireEvent("on" + ev.type, ev); } catch (e) { // IE doesn't allow synthesized events; it throws an "invalid // argument" error if we attempt it. Fall back on manually // calling the event handlers. for (ev.cancelBubble = false ; ele && !ev.cancelBubble ; ele = ele.parentNode) { // dispatch to our custom handlers in this element var l = ele.$eventList; if (l) { l.forEach(function(item) { if (item[0] == ev.type) item[1](ev); }); } // dispatch to the in-line handler for this element if (("on" + ev.type) in ele) ele["on" + ev.type](ev); } } } else if (ele.dispatchEvent) { // firefox, safari, opera... IE-bar return ele.dispatchEvent(ev); } else { return false; } } /* ------------------------------------------------------------------------ */ /* * Add a keyboard event handler to an object. This sets up an event * handler based on our cross-browser keyboard package, so you only need to * write a single event handler for the combined keypress and keydown * events. */ function addKeyHandler(obj, func, ctx) { // look up the element, if given by name obj = $(obj); // set up the keydown and keypress delegation functions var kd = function(ev) { return $kd(ev, func, ctx); }; var kp = function(ev) { return $kp(ev, func, ctx); }; // set up a record of the function, for later removal if desired if (!obj.keyHandlers) obj.keyHandlers = []; obj.keyHandlers.push({ func: func, kd: kd, kp: kp }); // add the delegation handlers addEventHandler(obj, "keydown", kd); addEventHandler(obj, "keypress", kp); } /* * Remove a keyboard event handler */ function removeKeyHandler(obj, func, ctx) { // look up the element, if given by name obj = $(obj); // find the list of handlers we've defined for this element var kh = obj.keyHandlers; if (kh) { // search for the user function for (var i = 0 ; i < kh ; ++i) { // check for a match to the user function if (kh[i].func == func && kh[i].ctx == ctx) { // found it - remove the delegation handlers removeEventHandler(obj, "keydown", kh[i].kd); removeEventHandler(obj, "keypress", kh[i].kp); // remove this element from the handler list kh.splice(i, 1); // no need to keep looking break; } } } } /* ------------------------------------------------------------------------ */ /* * Abstract event handler object. This simplifies handling for code that's * conditional on some external event or process completing. Use the * whenDone() method to invoke a callback if and when the event has * occurred: if the event has already occurred, we'll invoke the callback * immediately, otherwise we'll queue it up to be invoked when the event * occurs. * * The 'ele' and 'eventName' arguments are optional. If you provide them, * we'll automatically link the AbsEvent to the given native browser event * on the given element. To do this, we'll set up an event listener for * the given event on the given element that invokes the AbsEvent's fire() * method when the native event occurs. * * If you don't provide 'ele' and 'eventName', you must manually call * fire() to trigger the AbsEvent when appropriate. * * There are two main uses for AbsEvent: * * - External events, such as browser events or Flash movie events. For * this type of use, you can supply the 'ele' and 'eventName' arguments to * set up a handler automatically, OR you can write your own event handler, * which calls the AbsEvent object's fire() method when the native event * occurs. (It might seem redundant to have AbsEvent at all for native * events. The value of AbsEvent is that it easily lets you attach * multiple handlers to the event, and you don't have to worry about * whether or not the event has happened yet when you install a handler: if * the event has already happened, the handler will be invoked * immediately.) * * - Pseudo-threads. This is a long-running task that you break up into a * series of steps, and then carry out the steps gradually using timeouts. * The idea is to allow the UI to remain responsive while the long task is * being executed. For this, use the startThread() method to initiate the * task. */ function AbsEvent(ele, eventName) { // we're not done yet, and we have no callbacks queued yet this.isDone = false; this.cb = []; // set up our event listener, if applicable if (ele && eventName) { var ae = this; addEventHandler(ele, eventName, function() { ae.fire(); }); } // return the new object return this; } /* * Invoke a callback if and when the event has occurred. If the event has * already occurred, we'll invoke the callback immediately; otherwise we'll * enqueue it to be invoked when the event fires. * * If 'hurry' is true, and this is a pseudo-thread, we'll set the thread's * step interval to zero so the thread runs to completion as soon as * possible. This won't affect external events for obvious reasons. */ AbsEvent.prototype.whenDone = function(cb, hurry) { // check to see if the event has already fired if (this.isDone) { // the event has fired already - invoke the callback immediately cb(); } else { // the event hasn't fired yet - queue the callback for later this.cb.push(cb); // if the caller wants the thread to finish as soon as possible, // set the step delay interval to zero if (hurry) this.interval = 0; } }; /* * Completion event. The caller must invoke this when our external event * occurs. */ AbsEvent.prototype.fire = function() { // note that the event has fired this.isDone = true; // invoke each queued callback for (var i = 0 ; i < this.cb.length ; ++i) this.cb[i](); // clear the callback list this.cb = []; }; /* * Start a pseudo-thread. This sets up a long-running task to be performed * in little chunks that run intermittently while the browser UI runs. We * accomplish this by executing the chunks with timeouts. * * thread(step) is the thread function to call. We will invoke this * periodically on a timer. 'step' starts at 0 and is incremented on each * call. The function should perform one chunk of work and then return * true if we should continue calling it, false if the whole task is * finished. * * interval is the optional timer interval in milliseconds. If not * supplied, we'll use a default of 25 sms. */ AbsEvent.prototype.startThread = function(thread, interval) { // reset the event this.isDone = false; // set the time-slice interval this.interval = (interval ? interval : 25); // for statistics, note the starting time this.startTime = (new Date()).getTime(); // make the first call this.stepThread(thread, 0); } AbsEvent.prototype.stepThread = function(thread, step) { // If the interval is zero, run the thread to completion without delay. // Otherwise do just one step, then schedule the next step after the // delay interval. if (this.interval == 0) { // no scheduling delay - run the thread to completion while (thread(step++)) ; // signal the completion event this.fire(); } else if (thread(step++)) { // the thread wants to continue running - schedule the next step var e = this; setTimeout(function() { e.stepThread(thread, step); }, this.interval); } else { // the thread is done - signal the completion event this.fire(); } } /* ------------------------------------------------------------------------ */ /* * Utility functions */ /* * Get the current text selection on the page. This returns a string * containing the text of the highlighted selection range, if any. */ function getSelText() { // try it the Netscape way var sel = window.getSelection && window.getSelection(); if (sel) return sel.toString(); var sel = document.getSelection && document.getSelection(); if (sel) return sel; // try it the IE way sel = document.selection; sel = sel && sel.createRange(); sel = sel && sel.text; // return whatever we found return sel; } /* * Get the current selection range in a given element. */ function getSelRangeInEle(ele) { // if they gave us the element name, look it up ele = $(ele); if (!ele) return null; // for IE, use the selection range object for the document if (document.selection) { // IE - use a TextRange object, adjusted to be element-relative var r, r2, r3; try { if (ele.nodeName == "INPUT" && ele.type.toLowerCase() == "text" || ele.nodeName == "TEXTAREA") { ele.focus(); r = document.selection.createRange(); r2 = ele.createTextRange(); r3 = ele.createTextRange(); } else { r = document.selection.createRange(); r2 = r.duplicate(); r3 = r.duplicate(); try { r2.moveToElementText(ele); } catch (exc) { } } r2.setEndPoint('EndToEnd', r); try { r3.moveToElementText(ele); } catch (exc) { } // make sure it actually overlaps 'ele' if (r.compareEndPoints('StartToEnd', r3) <= 0 && r.compareEndPoints('EndToStart', r3) >= 0) { // figure the relative start and end points var s = r2.text.length - r.text.length; var e = s + r.text.length; // return the range, limiting to the ele's range return { start: Math.max(s, 0), end: Math.min(e, r3.text.length) }; } else return null; } catch (exc) { } } // for other browsers, INPUT and TEXTAREA elements have a special // way of handling this if (ele.selectionStart || ele.selectionStart == '0') return { start: ele.selectionStart, end: ele.selectionEnd }; // for other browsers, try the window selection range var r = window.getSelection(); if (r) { // scan the subranges for (var i = 0 ; i < r.rangeCount ; ++i) { // check this subrange var sr = r.getRangeAt(i); if (rangeIntersectsEle(sr, ele)) { var start, end; // set up a range encompassing the target element var sr2 = sr.cloneRange(); sr2.setStart(ele, 0); sr2.setEndAfter(ele); // figure the starting point if (sr.compareBoundaryPoints(Range.START_TO_START, sr2) <= 0) start = 0; else start = sr.startOffset; // figure the ending point if (sr.compareBoundaryPoints(Range.END_TO_END, sr2) >= 0) end = sr2.toString().length; else end = sr.endOffset; // return the result return { start: start, end: end }; } } } // we didn't find a selection return null; } function rangeIntersectsEle(range, ele) { // if they gave us the element name, look it up ele = $(ele); var r = ele.ownerDocument.createRange(); try { r.selectNode(ele); } catch (e) { r.selectNodeContents(ele); } return range.compareBoundaryPoints(Range.END_TO_START, r) < 0 && range.compareBoundaryPoints(Range.START_TO_END, r) > 0; } // Set the selection range in a given element. If the range is omitted, // we'll select the whole contents of the element, if we can figure // the range. function setSelRangeInEle(ele, range) { // if they gave us the element name, look it up ele = $(ele); // if the range isn't supplied, synthesize the range to cover // the whole element if (!range) { // if it's an INPUT or TEXTAREA, use its value if ((ele.nodeName == "INPUT" && ele.type == "text") || ele.nodeName == "TEXTAREA") { // it's a text control - use its value string as the range range = { start: 0, end: ele.value.length }; } else if (typeof(ele.innerText) == "string") { // for anything else, use its inner text length range = { start: 0, end: ele.innerText.length }; } else { // can't figure a range range = { start: 0, end: 0 }; } } // check for browser variations if (ele.createTextRange || document.body.createTextRange) { // IE - we have to do this indirectly through a TextRange object var r; if (ele.createTextRange) r = ele.createTextRange(); else { r = document.body.createTextRange(); r.moveToElementText(ele); } r.collapse(true); r.moveEnd('character', range.end); r.moveStart('character', range.start); r.select(); } else if (ele.setSelectionRange) { // non-IE - there's a method that does exactly what we want ele.setSelectionRange(range.start, range.end); } } // Replace the selection in the given control with the given text function replaceSelRange(ele, txt, selectNewText) { // if they gave us the element name, look it up ele = $(ele); // get the current selection range var r = getSelRangeInEle(ele); if (r) { // make sure we're replacing ONLY the text in this element, by // explicitly selecting this range (the range within 'ele' might // only have been a subset of a larger selection) setSelRangeInEle(ele, r); // replace the selection range with the new text if (ele.nodeName == "INPUT" || ele.nodeName == "TEXTAREA") { // replace the text in the element value ele.value = ele.value.substr(0, r.start) + txt + ele.value.substr(r.end); } else { // get the selection range object if (window.getSelection) { // non-IE - get the selectionRange for the selection var rr = window.getSelection(); if (rr) { // create the selectionRange rr = rr.createRange(); // delete its contents rr.deleteFromDocument(); // make sure that actually happened (Opera is buggy) if (!window.getSelection().isCollapsed) document.execCommand("Delete"); // insert the new text if (rr.rangeCount > 0) rr.getRangeAt(0).insertNode( document.createTextNode(txt)); } } else if (document.selection) { // IE - replace the selection text selection var rr = document.selection.createRange(); rr.text = txt; } else return; } // select the new text if desired, or move the selection to the // end of the new text if not var range = { start: selectNewText ? r.start : r.start + txt.length, end: r.start + txt.length }; setSelRangeInEle(ele, range); } } /* ------------------------------------------------------------------------ */ /* * Add a new option, sorting it alphabetically into the existing * list. */ function addSelectOptionSort(sel, label, val) { // search the existing Rename Delete Reset Create New
    * indicates a default setting
    Main game text: $[fontpicker id="mainFont"] $[colorpicker id="mainColor"]
    Link colors: $[colorpicker id="mainLinkColor"]   Hover: $[colorpicker id="mainLinkHover"]   Active: $[colorpicker id="mainLinkActive"]
    Main background: $[colorpicker id="mainBkg"]
    Command-line text: $[fontpicker id="cmdFont"] $[colorpicker id="cmdColor"]
    $[checkbox id="cmdBold" label="Bold"] $[checkbox id="cmdItalic" label="Italic"]
    Status line text: $[fontpicker id="statusFont"] $[colorpicker id="statusColor"]
    Link colors: $[colorpicker id="statusLinkColor"]   Hover: $[colorpicker id="statusLinkHover"]   Active: $[colorpicker id="statusLinkActive"]
    Status line background: $[colorpicker id="statusBkg"]
    Serifed font: $[fontpicker id="serifFont"]
    Sans-serif font: $[fontpicker id="sansFont"]
    Cursive/script font: $[fontpicker id="scriptFont"]
    Typewriter font: $[fontpicker id="ttFont"]
     

    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
    frobtads-1.2.3/tads3/lib/webuires/defaultPrefs.js0000644000175000001440000000325411442254641021106 0ustar realncusers/* * Default display preference settings for this game. Edit this file if * you want to customize the default appearance. */ var defaultPrefs = { // Main font. This is the font used throughout the transcript // window and status line. If you list multiple fonts with commas, // the browser will go through the list looking for one that's // installed on the run-time system. mainFont: "Georgia, Times New Roman, Times, New York, serif", mainFontSize: "10pt", mainColor: "#000000", mainLinkColor: "#0000ff", mainLinkHover: "#ff00ff", mainLinkActive: "#ff0000", // main window background color mainBkg: "#ffffff", // Command-line font. Leave these null to inherit the main font // attributes for the command line. cmdFont: null, cmdFontSize: null, cmdColor: null, cmdBold: "bold", cmdItalic: "normal", // Status line font and background colors. Leave these null to inherit // the main font attributes. statusFont: null, statusFontSize: null, statusColor: "#000000", statusBkg: "#c0c0c0", statusLinkColor: "#0000ff", statusLinkHover: "#ff00ff", statusLinkActive: "#ff0000", // Generic fonts. These correspond to the HTML TADS "parameterized" // fonts - TADS-serif, TADS-sans, etc. Null inherits the main font // settings. serifFont: "Georgia, Times New Roman, Times, New York, serif", serifFontSize: null, sansFont: "Verdana, Arial, Helvetica, sans-serif", sansFontSize: null, scriptFont: "Comic Sans MS, Zapf Chancery, ZapfChancery, cursive", scriptFontSize: null, ttFont: "Courier New, Courier, monospace", ttFontSize: null }; frobtads-1.2.3/tads3/lib/webuires/dlg-corners.gif0000644000175000001440000000212311422301521021010 0ustar realncusersGIF89a°‡ÿÿÿ!ù,°ÿ80€Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI3!Á›8ÔÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´iÅœ7JJµªÕ«X³jÝʵ«×¯;¡‚K¶¬Ù³hÓª]˶­Û9ßÊK·®Ý»xóêÝK5*ß¿€ L¸°áÃR "^̸±ãÇ#Kž+p²å˘3kÞ̹óÅÊžC‹Mº´éÓ]u¢^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀMªN¼¸ñãÈ“+­¼¹óçУ‡f.½ºõëØ³ÓU¬½»÷ïàÃ#0õ+¾¼ùóèÓwÄ©¾½û÷ðËC¿¾ýûøyϧž¿¿ÿÿn¶{hàæµ_@;frobtads-1.2.3/tads3/lib/webuires/main.js0000644000175000001440000034615212145504453017415 0ustar realncusers/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Main window scripts for TADS 3 Web UI. These scripts are for the * top-level layout window, which usually contains one or more "widget" * windows, such as the main game transcript window and the status line. */ /* ------------------------------------------------------------------------ */ /* * main window initialization */ function mainInit() { // the top window is always called "main" pageName = "main"; // check for the Flash plug-in if (getFlashPlayerVersion()) { // Flash is available, so load our SWF object. We don't embed the // object statically, because doing so provokes some browsers to // display a UI prompt about installing Flash every single time the // page is loaded, which is annoying to users who intentionally run // sans Flash. But now that we know Flash is present, we can safely // load our SWF, so dynamically insert the tag for it. $("__TADS_swf_div.
    ").innerHTML = "" + "" + "" + "" + "" + "" + "" + ""; } else { // The Flash plug-in isn't available, so we won't be able to run // our SWF object. Fire our load-completion event to signal that // there's nothing to wait for. TADS_swf.loaded.fire(); } // initialize the utilities and layout window packages utilInit(); layoutInit(); // initialize the XML request frame initXmlFrame(); // override some handlers window.onresize = mainResize; window.onGameEvent = mainGameEvent; window.onGameState = mainGameState; window.onUload = mainUnload; // Cancel any pending events from past incarnations, in case we just // refreshed the page. Lingering events from past loads refer to old // objects from the previous DOM tree, so they won't work with the new // incarnation of the page. serverRequest("/webui/flushEvents"); // get the initial state getInitState(); // cache some images that we might need if we lose our connection cacheImage("/webuires/dlg-corners.gif"); cacheImage("/webuires/dlg-title-corners.gif"); cacheImage("/webuires/modal-cover.png"); cacheImage("/webuires/errorPopupBkg.gif"); cacheImage("/webuires/warningPopupBkg.gif"); cacheImage("/webuires/modal-cover.png"); } /* ------------------------------------------------------------------------ */ /* * Main game window events and state */ // are we in an inputEvent() wait? var wantInputEvent = false; // event handler function mainGameEvent(req, resp) { // check for title changes if (xmlHasChild(resp, "setTitle")) document.title = xmlChildText(resp, "setTitle"); // check for preference settings changes if (xmlHasChild(resp, "setPrefs")) xmlToPrefs(xmlChild(resp, "setPrefs")[0]); // check for preference dialog invocations if (xmlHasChild(resp, "showPrefsDialog")) showPrefsDialog(); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var file = xmlChildText(resp, "offerDownload"); addDownloadFile(file); addDownloadFrame(file); } if (xmlHasChild(resp, "cancelDownload")) { var file = xmlChildText(resp, "cancelDownload"); removeDownloadFile(file); removeDownloadFrame(file); } // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for event input waits and cancellations if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; else if (xmlHasChild(resp, "cancelInputEvent")) wantInputEvent = false; // check for menu system events if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // "inherit" the layout window handling layoutGameEvent(req, resp); } // state handler function mainGameState(req, resp) { // check for title settings if (xmlHasChild(resp, "title")) document.title = xmlChildText(resp, "title"); // check for preference settings if (xmlHasChild(resp, "prefs")) xmlToPrefs(xmlChild(resp, "prefs")[0]); // "inherit" the layout window handling layoutGameState(req, resp); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for input event waits if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var lst = xmlChild(resp, "offerDownload"); for (var i = 0 ; i < lst.length ; ++i) addDownloadFile(xmlNodeText(lst[i])); } // check for menu system dialogs if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // This is the main, top-level window, so once we get the initial // state set up, start the "event thread". This is only conceptually // a thread, since javascript is single-threaded, but it acts roughly // like a thread in that the browser sends the request asynchronously // and "resumes" our processing when the request completes, by // invoking our event callback. // Ask for our first event. Do this in a timeout rather than directly, // to allow the browser to process any outstanding events in its own // queue; this avoids a problem that shows up in Mobile Safari and IE9 // that seems to be related to the timing of receiving the IFRAME // contents on the initial page setup. A zero timeout is fine; we // don't need a delay per se, we just need to order things so that we // don't generate this request until the browser has finished processing // UI events already in its internal queue. setTimeout(function() { serverRequest( "/webui/getEvent", ServerRequest.Subscription, cbGetEvent); }, 0); } // getEvent request callback. This handles a response from the server to // a "get event" request we sent earlier. This allows the server to send // us events: we ask the server for an event via a request, and as soon as // the server has an event available, it replies to the request. function cbGetEvent(req, resp) { // get the target window var w = getEventWin(req, resp); // If we got found a window, forward the event. If we didn't find // the window, or it's not loaded yet, drop the event. This should // be okay, since the window will ask for a full status refresh when // it first loads - this will compensate for any missed events that // came in before it loaded. if (w && w.onGameEvent) w.onGameEvent(req, resp); // if we're shutting down, skip asking for another event if (xmlHasChild(resp, "shutdown")) { // display the disconnecting dialog showDialog({ title: "TADS", id: "shutdown", contents: "The game server has terminated the connection." }); // done return; } // go back for the next request serverRequest("/webui/getEvent", ServerRequest.Subscription, cbGetEvent); } // Get the window for a state/event reply function getEventWin(req, resp) { // if there's a window spec, get it; assume it's directed to the // main frame window otherwise if (xmlHasChild(resp, "window")) { // get the window from the path return windowFromAbsPath(xmlChildText(resp, "window")); } else { // no window spec, so use the main window return window; } } // get a window from the absolute path function windowFromAbsPath(path) { // turn the path string into a list if (typeof(path) == "string") path = path.split("."); // make sure it starts with our page name if (path.shift() != pageName) return null; // if it's just the one element, it's the main window if (path.length == 0) return window; // resolve the rest as a relative path return windowFromPath(path); } /* ------------------------------------------------------------------------ */ /* * Key event handler for inputEvent() requests from the server. When the * server program wants to solicit an event from the client, it sends us a * status message. We set the global variable * wantInputEvent to true when we receive this message; this tells us that * when we get a keystroke or hyperlink click event that isn't handled in * the UI itself, we should send it back to the server as an input event * message. * * All subwindows should forward their unhandled key events here so that we * can transmit the appropriate events to the server. Only unhandled * events should be sent here; if the event is handled within the client * UI, it doesn't have to be sent to the server. */ function keyToServer(desc) { // if the server wants an input event, send it if (desc && wantInputEvent) { // translate the key from DOM 3 naming to TADS naming var key = DOM3toTADSKey[desc.keyName] || desc.ch; // if we don't have a TADS translation for the key, don't send an // event for this key after all if (!key) return; // translate control keys to [ctrl-x] notation var ch = key.charCodeAt(0); if (ch == 10 || ch == 13) ch = '\n'; else if (ch < 32) key = "[ctrl-" + String.fromCharCode(ch + 64) + "]"; // send the event serverRequest("/webui/inputEvent?type=key¶m=" + encodeURIComponent(key)); // we're no longer waiting for an input event wantInputEvent = false; // consider this key handled - don't do the default browser action preventDefault(desc.event); } } /* event map from DOM 3 key names to TADS key names */ var DOM3toTADSKey = { "U+001B": "[esc]", "Up": "[up]", "Down": "[down]", "Left": "[left]", "Right": "[right]", "Home": "[home]", "End": "[end]", "U+007F": "[del]", "Insert": "[insert]", "ScrollLock": "[scroll]", "PageUp": "[page up]", "PageDown": "[page down]", "F1": "[f1]", "F2": "[f2]", "F3": "[f3]", "F4": "[f4]", "F5": "[f5]", "F6": "[f6]", "F7": "[f7]", "F8": "[f8]", "F9": "[f9]", "F10": "[f10]", "F11": "[f11]", "F12": "[f12]", "U+0008": "[bksp]" }; /* * Document-level click handler for inputEvent() requests from the server. * Unhandled hyperlink clicks should be sent here, so we can send them on * to the server as inputEvent() results. */ function clickToServer(ev, href,ele) { // if the server wants an input event, send it if (wantInputEvent) { // send the event to the server serverRequest("/webui/inputEvent?type=href¶m=" + encodeURIComponent(href)); // we're no longer waiting for an input event wantInputEvent = false; } } /* ------------------------------------------------------------------------ */ /* * resize the main window */ function mainResize() { // move the errorPopupDiv to the bottom right, if it's visible if (errorPopupVis) { var rc = getWindowRect(); var ediv = $("errorPopupDiv"); ediv.style.left = (rc.width - ediv.offsetWidth) + "px"; ediv.style.top = (rc.height - ediv.offsetHeight) + "px"; } if (downloadFiles.length) { var rc = getWindowRect(); var ddiv = $("downloadPopupDiv"); ddiv.style.left = (rc.width - ddiv.offsetWidth) + "px"; var top = rc.height - ddiv.offsetHeight; if (errorPopupVis) top -= ediv.offsetHeight; ddiv.style.top = top + "px"; } // reposition any dialogs for (var i = 0 ; i < dialogStack.length ; ++i) { if (!dialogStack[i].manuallyPositioned) positionDialog(dialogStack[i].ele, false); } // inherit the layout window handling calcLayout(); } /* ------------------------------------------------------------------------ */ /* * Close the window */ function mainUnload() { if (debugLogWin) { debugLogWin.close(); debugLogWin = null; } } /* ------------------------------------------------------------------------ */ /* * Error logging. This is used mainly for network connection errors. * Network errors are often due to temporary conditions - network * congestion, router latency, server hiccups. Many times, an XML request * will fail due to a network hiccup, but retrying the same request will * succeed. We therefore don't want to be too strident about network * errors, but at the same time we don't want to completely ignore them. * Our compromise is a small message panel that appears at the bottom * corner of the main window when an error occurs. This alerts the user to * the error, and offers an option to see more details. This doesn't * intrude too much into the UI visually, and doesn't intrude at all * modally, since there's no need to acknowledge or dismiss the popup. */ // error log var errorLog = []; // Is the error popup visible? This is the little button that we show // in the bottom right corner when a new error is available to be viewed. var errorPopupVis = false; // Add an error to the error log. 'msg' is the explanatory error message // text, and 'detail' is addition technical detail that might be helpful // for a programmer debugging the error (javascript stack trace, bad XML // text, HTTP result code, etc, as appropriate to the specific error // condition). // // 'id' is an optional object that tells us the status of an error that // might be possible to resolve automatically with retries. First, this // serves to tie together a series of errors that results from a series // of retries for the same operation. For example, for XML requests, // the 'id' is the request descriptor object. Tying the retry errors // to the original error is useful because (a) the error log can show // the relationship, so that the user doesn't think five separate things // went wrong when they were really five repeated failed attempts at // the same thing, and (b) if a retry eventually succeeds, the error log // can show that the earlier attempts were eventually resolved and thus // can be ignored. Second, the 'id' object has two fields that tell us // whether the operation is in fact being retried: // // id.failed - true if the operation has permanently failed and will not // be retried, false if the operation is being retried // id.succeeded - true if the operation has succeeded on a retry // // If 'id' is null, we'll supply a default. function logError(msg, detail, id) { // for testing purposes, it's sometimes useful to pop this up // in the separate debug log window, but we don't want to do // this in release builds // debugLog("Error: " + msg + "
    " + detail + "

    "); // if there's no 'id' object, supply a default if (!id) id = new LogErrorID(); // add the message at the start of the error list errorLog.unshift({msg: msg, detail: detail, id: id, viewed: false, timestamp: new Date()}); // if the error frame isn't visible, show it if (!errorPopupVis) { // mark it as visible, but make it completely transparent errorPopupVis = true; var d = $("errorPopupDiv"); setAlpha(d, 0); // do an explicit size recalc to position the popup mainResize(); // start fading it in startFade(d, 1, 700); } // If all unviewed errors are being retried automatically, set the // error popup background to orange. Otherwise set it to red. var allRetry = true; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if it's unviewed, and it's marked as failed, we have an error // that won't be retried if (!e.viewed && e.id.failed) { allRetry = false; break; } } // set the appropriate background color in the popup $("errorPopupDiv.[content]").style.background = (allRetry ? "#ff8000" : "#ff0000"); $("errorPopupDiv.[icon]").style.backgroundImage = (allRetry ? "url(/webuires/warningPopupBkg.gif)" : "url(/webuires/errorPopupBkg.gif)"); } function LogErrorID() { this.succeeded = false; this.failed = true; } function openErrorDetail(i) { $("errorDetailDiv-" + i).style.display = "block"; $("openErrorDetailLink-" + i).style.display = "none"; } // Un-log a network error. Call this when a request that failed with an // error subsequently succeeds on a retry. function unlogError(id) { // find the error in the error log var openCnt = 0; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if this error isn't resolved and hasn't been viewed, // count it as open if (!e.id.succeeded && !e.viewed) ++openCnt; } // if there are now no open errors, the error popup is no longer needed if (openCnt == 0) hideErrorPopup(); } // display the error log var errorLogViewCount = 0; function showErrorLog() { // build the error list var lst = []; for (var i = 0 ; i < errorLog.length ; ++i) { var msg = errorLog[i].msg; var id = errorLog[i].id; var detail = errorLog[i].detail; var ts = errorLog[i].timestamp; if (detail) { msg += "
    " + ts.toLocaleTimeString() + (errorLogViewCount == 0 ? "" : errorLog[i].viewed ? " | Previously viewed" : " | New") + (id.succeeded ? " | Error was resolved automatically" : !id.failed ? " | Request is being retried automatically" : "") + " | " + "View Details" + "
    " + "
    " + detail + "
    "; } lst.push(msg); } // count this view of the error log errorLogViewCount++; // mark all messages as viewed for (var i = 0 ; i < errorLog.length ; ++i) errorLog[i].viewed = true; // If there's a single error, show it as the whole dialog contents. // Otherwise show a scrolling list of the errors. var cont; if (errorLog.length == 0) { cont = "This window shows any errors encountered in the browser " + "since you loaded the page, including network problems " + "and javascript errors. So far, no errors have occurred " + "since you loaded or refreshed the page."; } else if (errorLog.length == 1) { // one error - just show the error cont = lst[0]; } else { // Multiple errors - show a scrolling list cont = "The game encountered the following errors (the most " + "recent is listed first):" + "
    " + lst.join("
    ") + "
    "; } // hide the error popup hideErrorPopup(); // show the dialog showDialog({ title: "Error Log", id: 'errorLog', contents: cont }); } // hide the error log popup function hideErrorPopup() { // move the popup division off-screen errorPopupVis = false; var d = $("errorPopupDiv"); startFade(d, -1, 250, function() { d.style.top = "-1000px"; mainResize(); }); } /* ------------------------------------------------------------------------ */ /* * Download popup. This appears at the lower right corner of the window, * just above the error popup if there is one. This shows the list of * outstanding downloadable files sent by the game server. */ // list of downloadable files var downloadFiles = []; // add a downloadable file function addDownloadFile(fname) { // add the file to the list downloadFiles.push(fname); // adjust the height var d = $("downloadPopupDiv"); // rebuild the download list rebuildDownloadDiv(); // if this is the first file, show the popup if (downloadFiles.length == 1) { // make it completely transparent, and start fading it in setAlpha(d, 0); startFade(d, 1, 700); } // do an explicit size recalc to position and resize the popup mainResize(); } // remove a downloadable file function removeDownloadFile(fname) { // find the file var idx = downloadFiles.indexOf(fname); if (idx >= 0) { // remove it from the list downloadFiles.splice(idx, 1); // rebuild the download list rebuildDownloadDiv(); // if the list is empty, hide the popup, otherwise resize it var cnt = downloadFiles.length; var d = $("downloadPopupDiv"); if (cnt == 0) { // empty - fade it out startFade(d, -1, 250, function() { d.style.top = "-1000px"; }); } else { // do an explicit resize to position and size the popup mainResize(); } } } // rebuild the download file division contents function rebuildDownloadDiv() { var s = ["Download Files"]; downloadFiles.forEach(function(fname) { var shortName = fname.replace(/^.*\//, "").htmlify(); var href = encodeURI(fname); s.push("
    • " + "" + shortName + "" + " | Cancel" + "
    "); }); $("downloadPopupDiv.[content]").innerHTML = s.join(""); } function cancelDownloadFile(fname) { serverRequest(fname + "?cancel"); } /* ------------------------------------------------------------------------ */ /* * Debugging messages. During development, it's often useful to instrument * the code with message displays; this provides a place to put them that's * less intrusive than 'alert()'. Messages written here are displayed in a * separate popup window that we open the first time this is called. */ var debugLogWin = null, debugLogLoaded = new AbsEvent(); function debugLog(msg) { // if the log window isn't open yet, or it's been closed, open it if (!debugLogWin || debugLogWin.closed) { debugLogLoaded.isDone = false; debugLogWin = window.open( "/webuires/debuglog.htm", "tadsDebugLog", "location=no, resizable=yes, scrollbars=yes, status=yes, " + "width=550, height=400"); } // when the log window is loaded, add the message debugLogLoaded.whenDone(function() { debugLogWin.debugLogPrint(msg); }); } /* ------------------------------------------------------------------------ */ /* * Show an HTML dialog. This shows the dialog as a foreground DIV within * the main window, not as an 'alert()' or new window popup. * * 'id' is an optional identification string for identifying the dialog * within the program. This is useful if you want to be sure that the * active dialog is a particular dialog, rather than a nested dialog it * opens. We don't make any of this here; it's purely for the dialog's own * use in other scripts. * * 'params' is an object with the properties listed below. All properties * are optional except the ones marked required. * * title: a string to display in the dialog's title bar. Required. * * contents: a string giving the HTML contents of the dialog, OR a DOM * element whose contents will be duplicated to populate the dialog. * Required. * * buttons: an array of button objects. Each object has these members: *. { *. name: string with the display name of the button *. isDefault: true if pressing the Return key activates this button *. isCancel: true if pressing the Escape key activates this button *. onclick: function to call when clicking the button; if not set, *. clicking the button simply dismisses the dialog *. key: the key that can be used to select this button; applies only *. when focus isn't in an input control; you can specify either an *. ordinary character (using upper-case for letters), or a key *. name (e.g., 'U+0008' for backspace) *. } * * If you omit the name of the button, the button won't be displayed, but * will still be available as a keystroke handler. * * init: a function to call to initialize the dialog. This is called after * the new dialog has been set up, but before it's been displayed. The * dialog's root element is passed as the argument - init(dialogElement). * * initFocus: the control that should initially receive focus. If this * isn't set, we'll search for a focusable control (INPUT, SELECT, ) and * set focus there. * * initAfterVisible: a function to call to initialize after the dialog has * been displayed and focus has been set. As with init(), this is called * with the dialog's root element as the argument. * * dismiss: a function to call when dismissing the dialog. This will be * called as dismiss(dialogElement, button), where 'button' is the button * object from the 'buttons' list corresponding to the button the user * clicked to close the dialog. Return true to allow the dialog to close, * false to prevent it from closing. You can use a false return to perform * validation. * * dimensions: an object { width:, height: } with style strings to use for * the dimensions of the dialog. If not provided, we'll let the browser * set dimensions based on the content size. * * fillWidth: true if the dialog contents should be stretched to fill the * whole width of the containing dialog. By default, we'll show the * contents at their natural width, centered in the dialog container. In * some cases we instead want the contents to scale according to the * container, though, such as when the dialog contains an iframe. Set this * to true to stretch the contents to the container width. */ var dialogStack = []; var fontSelectorOptions = null; function showDialog(params) { // get the contents, retrieving HTML source if it's a DOM object var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // assume all we'll need to do is show the dialog var go = function() { showDialogMain(params); }; // check for $[fontpicker] macros if (contents.match(/\$\[fontpicker([^\]]*)\]/)) { // we have one or more font pickers, so we need to build the // list of installed fonts before we can show the dialog var go1 = go; go = function() { TADS_swf.waitForFontList(go1); }; } // execute the dialog display procedure we've built up go(); } // main dialog builder function showDialogMain(params) { // figure the z-index for the new dialog var z = 10500 + dialogStack.length*10; var dlgID = "dialogDiv" + (dialogStack.length + 1); // create the global descriptor for the dialog var dlgDesc = { title: params.title, dd: dlgID, manuallyPositioned: false, dismiss: params.dismiss, initFocus: params.initFocus, id: params.id }; // push it onto the active dialog stack dialogStack.push(dlgDesc); // create the background cover for the new dialog var bkg = dlgDesc.bkg = document.createElement("div"); bkg.className = "dialogCoverDiv"; bkg.style.zIndex = z; document.body.appendChild(bkg); // create the canvas var canvas = dlgDesc.canvas = document.createElement("div"); canvas.className = "dialogCanvas"; canvas.style.zIndex = "" + (z + 1); document.body.appendChild(canvas); // create the new dialog division var dlg = dlgDesc.ele = document.createElement("div"); dlg.id = dlgID; dlg.innerHTML = $("dialogTemplate").innerHTML; dlg.className = "dialogDiv"; dlg.style.zIndex = "" + (z + 1); dlg.onkeydown = function(ev) { return $kd(ev, dlgKey, dlgDesc); }; dlg.onkeypress = function(ev) { return $kp(ev, dlgKey, dlgDesc); }; canvas.appendChild(dlg); // if dimensions were given, set them in the dialog element if (params.dimensions && params.dimensions.height) dlg.style.height = params.dimensions.height; if (params.dimensions && params.dimensions.width) dlg.style.width = params.dimensions.width; // get a couple of elements for further manipulation var ttl = $(dlg, "[dialogTitle]"); var cont = $(dlg, "[dialogContent]"); // if desired, stretch the content division to fill the dialog if (params.fillWidth) cont.style.width = "100%"; // fill in the title ttl.innerHTML = params.title; // set default buttons if none were given var buttons = params.buttons; if (!buttons) buttons = [{name: "OK", isDefault: true, isCancel: true}]; // save the buttons in the dialog descriptor dlgDesc.buttons = buttons || []; // if it's a pre-defined division, fetch its contents var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // expand $[fontpicker] macros var fontSizeOptions = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 48, 72] .map(function(x) { return ""; }).join(""); fontSizeOptions = "" + fontSizeOptions; var fontPickers = []; var preFontCont = contents; contents = contents.replace( /\$\[fontpicker([^\]]*)\]/gm, function(match, attrs) { // parse the attributes var pattrs = parseTagAttrs(attrs); // add this to the font select pending list fontPickers.push({ attrs: attrs, pattrs: pattrs }); // Build the font picker: a combo for the font name, another // for the font size, and a color picker for the text color. // Set the font picker select list to initially empty, since // we might have to wait for the font lister thread to finish. return createCombo(pattrs.id, 40, "") + createCombo(pattrs.id + "Size", 5, fontSizeOptions); }); // note if we actually needed any font pickers - we do if we found // any $[fontpicker] macros to expand var needFonts = (contents != preFontCont); // expand $[colorpicker] macros var colorPickers = []; contents = contents.replace( /\$\[colorpicker([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); var btnID = dlgID + "." + pattrs.id; colorPickers[pattrs.id] = pattrs; return "" + ""; }); // expand $[checkbox] contents = contents.replace( /\$\[checkbox([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); return ""; }); // add the buttons to the contents if (buttons.length) { contents += "
    "; for (var i = 0 ; i < buttons.length ; ++i) { var b = buttons[i]; if (b.name) { contents += "" + "" + b.name + ""; } } contents += "
    "; } // fill in the contents cont.innerHTML = "
    " + contents + "
    "; // call the initializer function if (params.init) params.init(dlg, params); // initialize the contents and adjust the size immediately positionDialog(dlg, true); // just in case the browser needs a moment to finish processing the // innerHTML changes, resize it again in a few moments setTimeout(function() { // set the size again positionDialog(dlg, true); // set it opaque setAlpha(canvas, 100); // set the initial focus initDialogFocus(); // run the after-visible function if (params.initAfterVisible) setTimeout( function() { params.initAfterVisible(dlg, params); }, 10); }, 1); // If we needed any font pickers, load the system's font list, if // we haven't already. When that's done, populate the font pickers. if (needFonts) { // get the cached font list var fonts = TADS_swf.fontList; // build the font select list fontSelectorOptions = fonts.map(function(ele) { ele = ele.htmlify(); return "" }).join(""); // add "Default" as the first item fontSelectorOptions = "" + fontSelectorOptions; // populate each font picker for (var i = 0 ; i < fontPickers.length ; ++i) { // get this font picker's field, and from that get the sel.outerHTML = sel.outerHTML.replace( /<\/select>/im, fontSelectorOptions + "") .replace(/size=("[^"]+"|'[^']'|[^'"][^\s>]*)/im, "size=\"20\""); } else { // no outerHTML - replace just the option list sel.innerHTML = fontSelectorOptions; sel.size = "20"; } } } } /* * Initialize focus in the currently active dialog */ function initDialogFocus() { // ignore this if there's no dialog if (dialogStack.length == 0) return; // get the dialog description var desc = dialogStack.top(); var dlg = desc.ele; // get the initial focus control specified in the parameters var f; if (desc.initFocus) f = $(dlg, desc.initFocus); // if that wasn't specified, search for the first focusable object // within the dialog if (!f) { f = breadthSearch(dlg, function(ele) { return (ele.focus && (ele.nodeName == "INPUT" || ele.nodeName == "TEXTAREA" || ele.nodeName == "SELECT" || ele.nodeName == "A")); }); } // Move focus to the control, if we found one, or to the dialog // itself if not. If the dialog doesn't accept focus, just // remove focus from wherever it is now. if (f) { // move focus to the control f.focus(); // select its initial text if it's an INPUT or TEXTAREA if (f.nodeName == "INPUT" || f.nodeName == "TEXTAREA") setSelRangeInEle(f); } else if (dlg.focus) dlg.focus(); else if (document.activeElement) document.activeElement.blur(); } /* * Reposition a dialog for the current window size */ function positionDialog(dlg, init) { // get the dialog and window areas var drc = getObjectRect(dlg); var wrc = getWindowRect(); // center the dialog in the window var x = (wrc.width - drc.width)/2, y = (wrc.height - drc.height)/2; // if this is a sub-dialog, show it relative to the parent dialog // instead of centered if (init && dialogStack.length > 1) { // get the preceding dialog position var par = dialogStack[dialogStack.length - 2]; var prc = getObjectRect(par.ele); // move the new dialog below and right of the parent x = prc.x + 25; y = prc.y + 25; // mark this dialog and the parent as manually positioned par.manuallyPositioned = true; dialogStack.top().manuallyPositioned = true; } // set the position dlg.style.left = (x > 0 ? x : 0) + "px"; dlg.style.top = (y > 0 ? y : 0) + "px"; } /* * handle mouse tracking in a dialog title bar */ var dlgTrackInfo = null; function dlgTitleDown(ev) { // if there's no dialog, ignore it if (dialogStack.length == 0) return; // get the dialog and its bounding box var dlg = dialogStack.top().ele; var rc = getObjectRect(dlg); // get the event ev = getEvent(ev); // note the start offset of the tracking dlgTrackInfo = { x: ev.clientX - rc.x, y: ev.clientY - rc.y, dlg: dlg }; // track the mouse throughout the document addEventHandler(document, "mousemove", dlgTitleMove); addEventHandler(document, "mouseup", dlgTitleUp); addEventHandler(document, "selectstart", dlgTitleSelect); // Show the dialog's "move cover" division. This is a transparent // division that covers the whole dialog at a higher z-index than // any of its contents. The point is to block out any IFRAME within // the dialog for the duration of the move, so that events are directed // to our document rather than to the IFRAME's document. If we didn't // have this cover, mouse events would disappear into the IFRAME any // time the mouse moves quickly enough to stray into the IFRAME before // it generates a mousemove event within our title bar. Cross-domain // scripting limits prevent those events from bubbling up to our // document handlers if the IFRAME is showing an outside page (such // as from the storage server). The dialog cover blocks the IFRAME // and keeps all the events within our document. var cover = $(dlg, "[dialogMoveCover]"); cover.style.display = "block"; cover.style.width = rc.width + "px"; cover.style.height = rc.height + "px"; // skip the default handling return false; } // prevent text selection while tracking the mouse in IE function dlgTitleSelect(ev) { return false; } function dlgTitleMove(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // get the event ev = getEvent(ev); // move the dialog var dlg = dlgTrackInfo.dlg; dlg.style.left = (ev.clientX - dlgTrackInfo.x) + "px"; dlg.style.top = (ev.clientY - dlgTrackInfo.y) + "px"; // mark the dialog as manually moved dialogStack.top().manuallyPositioned = true; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } function dlgTitleUp(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // remove the event handlers that we installed just for the move removeEventHandler(document, "mousemove", dlgTitleMove); removeEventHandler(document, "mouseup", dlgTitleUp); removeEventHandler(document, "selectstart", dlgTitleSelect); // remove the dialog cover division $(dlgTrackInfo.dlg, "[dialogMoveCover]").style.display = "none"; // done tracking dlgTrackInfo = null; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } /* * Handle a keystroke in a color button in the main dialog. We'll let * Enter keystrokes get handled in the , but we'll block these from * bubbling up to the dialog. This lets us use the keyboard interface to * select a color button without dismissing the dialog. */ function colorbuttonKey(desc, ctx) { var ele = ctx[0]; var hasDefault = ctx[1]; if (desc.keyName == "Enter" || desc.keyName == "U+0020") { openColorPicker(desc.event, ele, hasDefault); cancelBubble(desc.event); preventDefault(desc.event); return false; } return true; } /* * Open a color picker */ var colorPickerBuilt = false; var colorPickerTarget = null; function openColorPicker(ev, ele, hasDefault) { // get the button and the color picker popup ele = $(ele); var cp = $("colorPicker"); // if it's already open, close it if (cp.style.display == "block") { closePopup(cp); return; } // build out the buttons if we haven't already done so if (!colorPickerBuilt) { var colors = ["ff8080", "ffff80", "80ff80", "ff80ff", "80ffff", "0080ff", "ff80c0", "ff80ff", "ff0000", "ffff00", "80ff00", "00ff40", "00ffff", "0080c0", "8080c0", "ff00ff", "804040", "ff8040", "00ff00", "008080", "004080", "8080ff", "800040", "ff0080", "800000", "ff8000", "008000", "008040", "0000ff", "0000A0", "800080", "8000ff", "400000", "804000", "004000", "004040", "000080", "000040", "400040", "400080", "000000", "808000", "808040", "808080", "408080", "C0C0C0", "400000", "ffffff"]; // set up the template for each button var keyTpl = "\"javascript:return @FUNC@" + "(event,colorBtnKey,['#@COLOR@',this]);\""; var btnTpl = ""; // build the button array - eight buttons per line for (var i = 0, s = [] ; i < colors.length ; ) { // build this line for (var j = 0, sl = [] ; j < 8 ; ++j, ++i) { // build this button and add it to the line list sl.push(btnTpl .replace(/@COLOR@/gm, colors[i]) .replace(/@I@/gm, i)); } // combine the line into a and add it to the master list s.push("" + sl.join("") + "
    "); } // add the custom color entry field s.push("
    Custom (rrggbb): " + "
    "); // add the Use Default button s.push("
    " + "
    "); // combine the element list into a string and plug it into the // popup division cp.innerHTML = s.join(""); // we've now built the color picker colorPickerBuilt = true; } // remember the opener button as the value destination on close colorPickerTarget = ele; // move the popup under the opener buton var rc = getObjectRect(ele); cp.style.left = rc.x + "px"; cp.style.top = (rc.y + rc.height) + "px"; cp.style.display = "block"; openPopup(ev, cp, null, ele, ele); // Force the browser to recalculate the layout after digesting its // internal event queue. IE for one has problems figuring containing // box sizes on the first pass, but gets it right if we explicitly // resize the box (even setting it to its current size) later. setTimeout(function() { cp.style.width = getObjectRect(cp).width + "px"; }, 1); // get the current color var curColor = canonicalizeColor(getStyle(ele, "background-color")); // set the custom field to the current color var customFld = $("colorPickerCustom"); customFld.value = curColor.substr(1); // if there's a button showing the current selected color, focus it var sel = breadthSearch(cp, function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && ele.style.backgroundColor == curColor); }); // set up the Default button var dflt = $("colorPicker.colorPickerDefault"); if (hasDefault) { dflt.style.display = "block"; checkColorPickerDefault(ele.useDefaultColor); } else dflt.style.display = "none"; // if we have a selection, set focus there; otherwise set focus on // the custom field if (sel) sel.focus(); else { customFld.focus(); setSelRangeInEle(customFld); } } /* * handle a key in the color picker popup background */ function colorPopupKey(desc) { switch (desc.keyName) { case "Up": case "Down": case "Left": case "Right": // if I have focus directly (i.e., it's not in a child), // move focus to the first button on any arrow key if (document.activeElement == $("colorPicker")) $("colorPicker.").focus(); break; } // skip default actions on all keys return false; } /* * Click on a color picker opener button */ function colorBtnClick(ev, color) { // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup closePopup(); // don't process the event further return false; } /* * apply a change to a color picker button */ function colorPickerApply(color, useDefault) { // set the color of the color element setColorButton(colorPickerTarget, color, useDefault); // generate an onchange event sendEvent(colorPickerTarget, createEvent("Event", "change")); } /* * click on the Use Default button in a color picker */ function colorDefaultClick(ev, ele) { // set Use Default and close the popop colorPickerApply(null, true); closePopup(); return false; } function checkColorPickerDefault(checked) { var span = $("colorPicker.colorPickerDefault.
    "); var img = $("colorPicker.colorPickerDefault."); if (checked) { span.className = "on"; img.src = "/webuires/ckbox8pt-on.gif"; } else { span.className = "off"; img.src = "/webuires/ckbox8pt-off.gif"; } } /* * handle a key in a color picker button */ function colorBtnKey(desc, ctx) { var color = ctx[0], ele = ctx[1]; var b = null; var nextButton = function(b, dir) { var idx = parseInt(b.id.substr(18)); return $("colorPicker.colorPickerButton#" + (idx + dir)); }; switch (desc.keyName) { case "Up": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, -1); break; case "Down": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, 1); break; case "Left": b = nextButton(ele, -1); break; case "Right": b = nextButton(ele, 1); break; case "U+0020": case "Enter": // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup and swallow the event closePopup(); cancelBubble(desc.event); preventDefault(desc.event); break; case "U+0009": // we're on a button, so tab into the field var fld = $("colorPickerCustom"); fld.focus(); setSelRangeInEle(fld); // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; } // move the focus to the new target, if we found one if (b) { // move focus to the new button, and select its color b.focus(); $("colorPickerCustom").value = b.style.backgroundColor.substr(1); // uncheck "Use default" checkColorPickerDefault(false); } // don't bother bubbling the event return false; } /* * Keystroke in color picker custom color field */ function colorPickFldKey(desc) { switch (desc.keyName) { case "Enter": var v = $("colorPickerCustom").value; if (v.charAt(0) != '#') v = '#' + v; if (v.match(/^#[0-9a-f]{6}$/i)) { // valid - select the color and de-select "Use Default" colorPickerApply(v, false); // close the popup closePopup(); } else { cancelBubble(desc.event); alert("Please use the HTML-style hexadecimal RRGGBB format. " + "For example, for bright red, enter FF0000."); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // if the custom color corresponds to a button, tab to the button var fld = $("colorPickerCustom"); var val = parseColor(fld.value); var b = breadthSearch($("colorPicker"), function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && parseColor(ele.style.backgroundColor) == val); }); if (b) { b.focus(); fld.value = b.style.backgroundColor.substr(1); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; default: // do the default processing for other keys return true; } } /* * Click a dialog button */ function clickDialogButton(n) { // get the button info var b = dialogStack.top().buttons[n]; // call the button function, or just dismiss the dialog if there isn't one if (b.onclick) b.onclick(); else dismissDialog(b); } /* * Dismiss the dialog */ function dismissDialog(btn) { // get the dialog info var dinfo = dialogStack.top(); var dlg = dinfo.ele; var bkg = dinfo.bkg; // if there's not button, it's the close box in the title bar - create // a pseudo button structure for it if (!btn) btn = { name: "(Close Box)", isCancel: true }; // call the dismiss callback, if present if (dinfo.dismiss && !dinfo.dismiss(dlg, btn)) return; // remove the dialog from the stack dialogStack.pop(); // remove focus from any active dialog element if (document.activeElement) document.activeElement.blur(); // remove them from the document document.body.removeChild(dinfo.canvas); document.body.removeChild(bkg); // move focus to the default input field in the document setDefaultFocus(); } /* * Dismiss a dialog by ID. If the given dialog is open, we'll cancel it * and any active child dialogs. */ function dismissDialogById(id, btn) { // scan to see if this dialog is open in the first place for (var i = 0 ; i < dialogStack.length ; ++i) { if (dialogStack[i].id == id) { // this is the one - dismiss all nested child dialogs while (dialogStack.length > i) dismissDialog(btn); // no need to look any further break; } } } /* * Set the default focus. If a dialog is active, set focus to the dialog. * Otherwise set the focus to the default input field in the default child * window. */ function setDefaultFocus(desc, fromWin) { // if the event is directed to a focus-keeping control, leave focus // where it is if (evtKeepsFocus(desc)) return; // if a dialog is active, set focus there; otherwise refer this to // the default focusable child window if (dialogStack.length) initDialogFocus(); else setDefaultFocusChild(desc, fromWin); // If the original event target isn't a control that reads keystrokes, // buffer the keystroke for the next time we want to read input. This // ensures that we won't drop keystrokes between command input field // activations. bufferKey(desc); } // Type-ahead buffer. When we receive keystrokes at the document level, // and focus isn't in a control that reads keystrokes, we'll buffer the // keys for future use when a focus-taking control is activated. // // The idea is that the IF user interface is "conversational": the UI // interaction consists primarily of alternating prompts and user // keyboard inputs. Users become accustomed to this and will anticipate // that a new command line (or other input reader) will be forthcoming // shortly after each input is completed, so they'll sometimes start // typing even before the new command line is visually activated. In // traditional terminal interfaces, these in-between keystrokes are // nearly always buffered. Our key buffering scheme here is designed to // simulate that behavior. var keybuf = []; function bufferKey(desc) { // don't buffer keystrokes if the server is waiting for an input // event - we'll instead send the key to the server (later) if (wantInputEvent) return; // if there's no descriptor, there's nothing to save if (!desc) return; // if the key has already been buffered, don't buffer it again if (desc.keyBufferedInMain) return; // discard CRs - IE9 sends CR LF for a newline, but we only want the LF if (desc.keyCode == 13) return; // note that the key has been buffered desc.keyBufferedInMain = true; // If the buffer's full, drop the oldest key. The limit is arbitrary; // the point isn't to conserve memory or anything like that, but simply // to limit the UI weirdness that can happen if we buffer too many // keystrokes. The problem is that if there's an extended period where // the UI is buffering keystrokes rather than reading them, then when we // do finally get around to activating an input control, the UI can seem // to be possessed by demons as the huge buffer plays back. Limiting // the buffer size avoids this - if the buffer's small enough, playback // will be virtually instantaneous, so no demons. There's really not // much of a tradeoff here, either, because most users will naturally // stop typing ahead anyway if the UI is unresponsive for any extended // period. if (keybuf.length >= 16) keybuf.shift(); // make a copy of the key event for buffering, but remove the event // object, as it might not be valid after the handler returns var d = desc.copy(false); d.event = { }; // buffer the event copy keybuf.push(d); } /* * If the main body should get focus at any point, and gets a keystroke, * send focus back to the default focus element. This is mostly for the * sake of dialog focus: we try to keep focus within the dialog by handling * keyboard events in the dialog itself, but there are odd edge cases where * key events bypass the dialog handlers and go to the browser's default * handler, which can set focus into the main body. This catch-all ensures * that if focus is outside the dialog while the dialog is active, any * keystrokes will trigger refocusing. */ function mainBodyKey(desc) { // if focus is in the main document body, move it back to the default // location (usually the command line, but sometimes a dialog object) if (isFieldKey(desc) && document.activeElement == document.body) setDefaultFocus(desc, window); // handle a generic document key genericDocKey(desc); // bubble the event return true; } /* * Handle a key within one of our popup DIVs */ function popupDivKey(desc) { // move focus back to the default location if (isFieldKey(desc)) setDefaultFocus(desc, window); // bubble the event return true; } /* * Handle a key in a dialog */ function dlgKey(desc, dlg) { // Check for explicit shortcut keys defined in the button list - these // override the default handling. Shortcut keys apply only if the // current focus element doesn't use the keystroke directly (e.g., if // focus is in a text field, regular characters go to the field as // text editing entries.) var idx, ch = desc.ch ? desc.ch.toUpperCase() : ""; if (!eleKeepsFocus(document.activeElement, desc) && (idx = dlg.buttons.indexWhich(function(b) { return b.key && (b.key == ch || b.key == desc.keyName); })) >= 0) { // click this button clickDialogButton(idx); // don't process the event any further cancelBubble(desc.event); preventDefault(desc.event); return false; } // check the key switch (desc.keyName) { case "Enter": // ignore this if the focus is in a button or link var e = document.activeElement; if ((e.nodeName == "INPUT" && e.type == "submit") || e.nodeName == "A") { // cancel the bubble, but let the default action occur cancelBubble(desc.event); return true; } // look for a default button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the default button, click it if (dlg.buttons[i].isDefault) { // click the button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+001B": // look for a cancel button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the cancel button, click it if (dlg.buttons[i].isCancel) { // click the cancel button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // check immediately after the browser processes the key to see // if focus has left the dialog, and move it back if so setTimeout(function() { // get the current focus and the active dialog var f = document.activeElement; // walk up the parent list to see if f is within the dialog for ( ; f && f != dlg.ele ; f = f.parentNode) ; // if not, move focus back to the dialog if (dlg.ele && !f) dlg.ele.focus(); }, 0); // allow the default processing to proceed return true; default: // do the normal thing for other keys return true; } } /* ------------------------------------------------------------------------ */ /* * Support functions for the TADS.swf Flash object. This is an invisible * Flash object that we use for certain functionality that we can't get * directly from the standard browser/javascript APIs. We use this for * programmatic access only; it doesn't have any visual or UI presence. * * Flash is ubiquitous, but it's not universal: we can't count on it being * available everywhere. Our Flash object is invisible, so there's nothing * visually missing when Flash isn't available, but we obviously won't have * access to the API extensions we use it for. We thus have to be careful * to recover gracefully when the Flash object isn't present: we can use * defaults, omit the desired functionality, or find some other approach to * get similar functionality (e.g., proprietary browser features). * * We use the Flash object for the following features: * * - Sound playback. Flash provides a good set of audio tools, including * concurrent playback of multiple sounds, support for common audio * formats, event notifications, volume control, and fades. * * - Font enumeration. Flash can give us a list of the fonts installed on * the system. */ var TADS_swf = { // Event coordinator for TADS.swf load completion. The swf object calls // loaded.fire() from its constructor via the Flash external interface // (the Flash mechanism that allows Flash scripts to call Javascript on // the embedding page). This is specified via the "onload" parameter to // the Flash object loader in the and tags that embed the // TADS.swf object, so be sure to specify the "onload" in those tags. loaded: new AbsEvent(), // cached font list fontList: null, // Wait for the font list to load, then proceed with the callback. // If the font list is already loaded, this simply invokes the callback // immediately. If the font list isn't loaded, we'll populate it. // // The reason that we have to structure this as a wait-for routine // with a callback is that we rely on a Flash object to enumerate // installed fonts, and Flash objects can load asynchronously. We // can't just assume that our Flash object is ready; we have to await // a go-ahead signal that it generates when it finished loading. // The wait-for structure lets us ensure that the Flash object is // ready before we try to invoke it. waitForFontList: function(doneFunc) { // First, we have to make sure we've loaded the Flash object. // Flash objects can be loaded asynchronously, so we have to // await the ready event from Flash before we can proceed. TADS_swf.loaded.whenDone(function() { // if we haven't already done so, build the font list if (!TADS_swf.fontList) { // Build the candidate font list. First try the TADS.swf // embedded Flash object. Flash isn't everywhere (especially // on mobile devices), so if we don't find it, fall back on // our canned list of candidate fonts. var fonts; try { // get the flash object var ie = navigator.userAgent.indexOf("Microsoft") != -1; var mobj = (ie ? window["__TADS_swf"] : document["__TADS_swf"]); // ask the Flash object for the list of installed fonts var sfonts = mobj.getFonts(); // Filter the list to keep only the first font of // each family. Style variations (bold, italic, etc) // are represented internally on most systems as whole // separate font objects, but for UI purposes we only // want to show one list entry per font family. // Keep only the font names in the final list. fonts = []; for (var i = 0 ; i < sfonts.length ; ++i) { // if this is a new name, add it to the list if (sfonts[i].fontStyle == "regular") fonts.push(sfonts[i].fontName); } } catch (e) { // An error occurred; we'll assume that the problem is // that Flash isn't available. Default to a canned list // of common fonts. We'll check each entry in a moment // to see if it's actually installed, so the final list // will only include fonts that are locally available. // Of course, it won't necessarily include *all* of the // available fonts, since we obviously can't include all // possible fonts in our prefab list. This list is only // meant to capture the basic set of fonts that most // operating systems include in their base installs. // Note that this list is actually a union of common // fonts from various operating systems, so it's unlikely // that all of them will be installed on any one machine. fonts = [ "Arial", "Arial Black", "Book Antiqua", "Charcoal", "Comic Sans MS", "Courier", "Courier New", "Gadget", "Geneva", "Georgia", "Helvetica", "Impact", "Lucida Console", "Lucida Grande", "Lucida Sans Unicode", "Monaco", "MS Sans Serif", "MS Serif", "New York", "Palatino", "Palatino Linotype", "Symbol", "Tahoma", "Times", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings", "Wingdings", "Zapf Chancery", "ZapfChancery", "Zapf Dingbats" ]; } // sort the font list by name (ignoring case) fonts.sort(function(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); // save the list TADS_swf.fontList = fonts; } // the font list is now ready - fire the callback doneFunc(); }); } }; /* ------------------------------------------------------------------------ */ /* * Preferences dialog */ function showPrefsDialog() { showDialog({ id: 'prefs', title: "Customize Display", contents: $("prefsDialog"), buttons: [ { name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true } ], init: initPrefsDialog, dismiss: onClosePrefsDialog }); } // List of preference profiles. This is a table of preference objects, // keyed by profile ID. var prefProfiles = { }; // Currently selected profile, by ID. Start with a default profile // that doesn't exist, which will trigger automatic profile creation // when needed. var curPrefProfile = 0; // Next profile ID. This is a high water mark (plus one) of all of the // profiles we've seen since loading; when we create a new profile, we'll // assign it this ID and increment this value. var nextPrefProfile = 1; // Mapping between dialog elements and preference settings. Each key is // the ID of a dialog element; the corresponding value explains how to // map between the dialog element and the style setting. // // type: specifies the control type: // // "color" specifies a color picker control. The value is in the // picker's style.backgroundColor property. // // "font" specifies a font list var proNames = []; dlgProfiles.forEach(function(pro, id) { var name = pro.profileName || "Untitled"; proNames.push({ id: id, name: name, sortName: name.toLowerCase() }); }); // sort it by name proNames.sort(function(a, b) { return a.sortName.localeCompare(b.sortName); }); // populate the profile list with the profile names var sel = $(dlg, "curProfile"); var idx = 0; proNames.forEach(function(pro) { var o = new Option(pro.name, pro.id); try { sel.add(o); } catch (e) { sel.add(o, null); } }); // select the current item setSelectByValue(sel, dlgCurProfile); // set the preferences settings in the dialog prefsToDlg(dlg, dlgProfiles[dlgCurProfile], true); // set up onchange event handlers for all controls breadthSearch(dlg, function(ele) { // if this is a control with a value, set an onchange handler if (prefsMapper[ele.id]) ele.onchange = function(ev) { onPrefItemChange(ev, dlg, ele); }; // continue the traversal return false; }); } // dialog copy of the profiles var dlgProfiles = null; var dlgCurProfile = null; // translate profile descriptions from XML sent by the server to our // in-memory settings list function xmlToPrefs(prefs) { // replace the profile table with the new one var newPrefs = { }; // run through the list of children if (xmlHasChild(prefs, "profile")) { var pros = prefs.getElementsByTagName("profile"); for (var i = 0 ; i < pros.length ; ++i) { // create a new empty profile, and add it to the master list // under the given ID var pro = pros[i]; var tab = { }; var pid = xmlChildText(pro, "id"); newPrefs[pid] = tab; // if this is a new high-water mark, note it var pidn = parseInt(pid); if (pidn && pidn >= nextPrefProfile) nextPrefProfile = pidn + 1; // read the elements var items = pro.getElementsByTagName("item"); for (var j = 0 ; items && j < items.length ; ++j) { // retrieve the item ID and value var item = items[j]; var id = xmlChildText(item, "id"); var val = xmlChildText(item, "value"); // if it's an empty string value, use null if (val == "") val = null; // set the item tab[id] = val; } // if this entry doesn't have a name, give it a default if (!("profileName" in tab)) tab.profileName = "Untitled"; } } // set the current profile ID var newCur = null; if (xmlHasChild(prefs, "currentProfile")) newCur = xmlChildText(prefs, "currentProfile"); // if we have any profiles, and the current profile is defined, // commit the new settings; otherwise ignore them if (newPrefs.propCount() > 0 && newCur && newCur in newPrefs) { // replace the memory profile set with the newly loaded set prefProfiles = newPrefs; curPrefProfile = newCur; // update the display prefsToCSS(); } } // load a preference set into the live CSS function prefsToCSS(prefs, win) { // create a default profile, if necessary initDefProfile(); // if the preference set wasn't specified, use the current setting if (!prefs) prefs = prefProfiles[curPrefProfile]; // if no window was specified, apply to myself and all children if (!win) win = window; // Build a table of styles to set. Each entry is keyed by the // selector, converted to all upper-case for matching; each value // is a list of [property, value] pairs for the selector. var tab = { }; prefsMapper.forEach(function(m, id) { // get the selector var sels = m.selector.toUpperCase(); // break up the list of selectors in case there are multiples sels.split(",").forEach(function(sel) { // get the table entry for the selector, or add a new one if // it's not already there sel = sel.trim(); var e; if (sel in tab) e = tab[sel]; else tab[sel] = e = []; // add this style and its computed value to the list e.push([m.style, getPrefVal(prefs, id)]); }); }); // apply the styles to this window and its children win.applyCSS(tab); // recalc the window layout calcLayout(); } // load a preference set into the preferences dialog function prefsToDlg(dlg, prefs, init) { // visit all controls in the dialog breadthSearch(dlg, function(ele) { // if this control is in the mapper, set its value if (ele.id && prefsMapper[ele.id]) prefToControl(ele, prefs, init); // continue the tree traversal return false; }); } // Set the display value of a control in the preferences dialog based // on the current value function prefToControl(ele, prefs, init) { // get the element ID and preferences mapper for the item var id = ele.id, m = prefsMapper[id]; // get the value from the current preference set or defaults var val = prefs[id]; var defVal = getPrefVal(prefs, id); if (val == undefined) val = null; // note if it's a default, and if so, apply the default var isDef = (val == null); if (isDef) val = defVal; // set the element's value from 'prefs' switch (m.type) { case "color": // set the control display if it's changes or we're initialing if (init || ele.useDefaultColor != isDef || ele.style.backgroundColor != val) setColorButton(ele, val, isDef); break; case "font": case "fontsize": // set the value in the field if (isDef) val = "*" + val; // set it if it's changed or we're initializing if (init || ele.value != val) ele.value = val; break; default: if (m.checkbox) { // checkbox - check it if the 'on' value is active ele.checked = (val == m.checkbox[1]); } else if (ele.type == "select-one") { // select list - find the value in the option list var opts = ele.options; for (var i = 0 ; i < opts.length ; ++i) { // if this value matches, select it if (opts[i].value == val) { ele.selectedIndex = i; break; } } } else { // for anything else, just set the value directly if (isDef) val = "*" + val; // set it if we're initializing or the value changed if (init || ele.value != val) ele.value = val; } break; } } function setColorButton(ele, color, isDef) { ele.useDefaultColor = isDef; if (color) { ele.style.backgroundColor = color; ele.style.color = contrastColor(color); } ele.innerHTML = (isDef ? "*" : ""); } // Calculate a constrasting color. For most colors we'll just calculate // the RGB complement, but for middle grays that gives us roughly the // same color, so we'll simply use black. function contrastColor(color) { // parse the color to an int color = parseColor(color); // calculate the inverse of each component var r = 255 - ((color >> 16) & 0xff); var g = 255 - ((color >> 8) & 0xff); var b = 255 - (color & 0xff); // if the color is near a middle gray, the RGB complement is just // another middle gray, which isn't very contrasting; black is a // safe choice in these cases if (r >= 0x70 && r <= 0x90 && g >= 0x70 && g <= 0x90 && b >= 0x70 && b <= 0x90) r = g = b = 0; // generate the result color in #rrggbb format return unparseCColor(r, g, b); } // save the preferences dialog's current settings into the in-memory // preferences table function dlgToPrefs(dlg, prefs) { // Traverse the dialog DOM tree and save each control value. Note // that we use the "search" function for the traversal, but we're not // really doing a search - we just want to traverse the tree for the // side effects. We can coopt the search function for this purpose // simply by always returning false from the test callback: the // search will never find what it's looking for, so it'll traverse // the entire tree before giving up and returning null. breadthSearch(dlg, function(ele) { // if this item has a mapping, save its value if (prefsMapper[ele.id]) controlToPref(ele, prefs); // return false so that we continue the "search" return false; }); } // set the preferences array value for a given control in the dialog function controlToPref(ele, prefs) { // get the mapper var id = ele.id; var m = prefsMapper[ele.id]; // figure the value based on the type var val; switch (m.type) { case "color": val = ele.useDefaultColor ? null : ele.style.backgroundColor; break; case "font": case "fontsize": // if it starts with "*", it's a default val = ele.value; if (val.charAt(0) == '*') val = null; break; default: if (m.checkbox) val = m.checkbox[ele.checked ? 1 : 0]; else val = ele.value; break; } // save the value prefs[id] = val; } function getPrefVal(prefs, id) { // first look at the given preference set, then at the defaults for (var i = 0 ; i < 2 ; ++i) { // get the current set var s = [prefs, defaultPrefs][i]; // scan up the inheritance tree for this ID for (var cid = id ; cid ; ) { // get the mapper entry var m = prefsMapper[cid]; // if this set has a non-null value for this item, it's the result var val = s[cid]; if (val != null && val != undefined) return val; // get the parent ID cid = m ? m.parent : null; } } // didn't find it return null; } function onPrefItemChange(ev, dlg, ele) { // get the active profile var profile = dlgProfiles[dlgCurProfile]; // save this control's value in the current preference set controlToPref(ele, profile); // refresh all controls to show new inherited defaults as needed prefsToDlg(dlg, profile, false); } function newPrefProfile() { // Create a temporary name for the profile of the form "Profile N+1", // where N is either the maximum of any existing profiles of the form // "Profile n", otherwise simply the number of existing profiles. var n = null; dlgProfiles.forEach(function (pro) { if (pro.profileName.match(/^\s*profile\s+(\d+)\s*$/i)) { var m = parseInt(RegExp.$1); if (n == null || m > n) n = m; } }); // if we didn't find any matching profiles, use the number of profiles if (n == null) n = dlgProfiles.propCount(); // generate the name var initName = "Profile " + (n+1); // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // show the create/rename dialog createOrRenamePrefProfile("Create Profile", initName, function(newName) { // copy the current profile var newPro = dlgProfiles[dlgCurProfile].copy(false); // set the name newPro.profileName = newName; // make sure the next profile ID is above any existing IDs dlgProfiles.forEach(function(id) { id = parseInt(id); if (nextPrefProfile <= id) nextPrefProfile = id + 1; }); // generate a new profile ID and store it in the dialog table var newID = (nextPrefProfile++).toString(); dlgProfiles[newID] = newPro; // add it to the profile selector control var sel = $(prefsDlg, "curProfile"); addSelectOptionSort(sel, newName, newID); // switch to the new profile dlgCurProfile = newID; setSelectByValue(sel, newID); }); } function renamePrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // run the rename dialog createOrRenamePrefProfile( "Rename Profile", dlgProfiles[dlgCurProfile].profileName, function(newName) { if (newName != dlgCurProfile) { // update the name in the profile table var pro = dlgProfiles[dlgCurProfile]; pro.profileName = newName; // update the ", buttons: [{ name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true }], dimensions: { width: "70ex" }, // on close, add the new profile if they clicked OK dismiss: function(dlg, btn) { if (btn && btn.save) { // get the new name and trim leading and trailing spaces var newName = $(dlg, "").value.trim(); // make sure the name is non-empty and unique var err = null; if (newName.match(/^\s+$/)) err = "Please enter a name for the new profile."; else if (newName != initName && dlgProfiles.propWhich(function(ele, prop) { return prop.trim().toLowerCase() == newName.toLowerCase(); })) err = "There's already a profile with this name. " + "Please choose a different name."; // if there's an error, show it and return failure if (err) { showDialog({ title: "Profile Name", contents: err }); return false; } // the name is valid - tell the caller to save it setName(newName); } // success - close the dialog return true; } }); } function delPrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // don't allow deleting the last profile if (dlgProfiles.propCount() == 1) { $alert("This is the only profile. If you wish to delete this " + "profile, please create a replacement first.", "Error"); return; } // run the delete confirmation dialog $confirm("Do you really want to delete the profile \"" + dlgProfiles[dlgCurProfile].profileName + "\"?", function(del) { // proceed with the deletion if desired if (del) { // remove this profile from the field, which allows the user to * select a local file to upload to the server. * * TADS uses file uploads when running in client/server mode without a * network storage server. In this configuration, the game server doesn't * have anywhere else to put saved games and other files, so it uses * uploads and downloads to the client PC. This dialog handles the upload * side. */ function showUploadDialog(desc) { // get the dialog description var prompt = xmlChildText(desc, "prompt"); // initialization - fill in the iframe var init = function(dlg, params) { var doc = getIFrameDoc($(dlg, "